diff --git a/.github/workflows/codeql-analysis.yml b/.github/workflows/codeql-analysis.yml index 7874bf40a3b..44fed4bfde0 100644 --- a/.github/workflows/codeql-analysis.yml +++ b/.github/workflows/codeql-analysis.yml @@ -25,7 +25,7 @@ jobs: if: ${{ github.event_name == 'pull_request' }} - name: Setup java - uses: actions/setup-java@v2 + uses: actions/setup-java@v3 with: distribution: zulu java-version: 17 diff --git a/.mvn/wrapper/MavenWrapperDownloader.java b/.mvn/wrapper/MavenWrapperDownloader.java index b901097f2db..7881deef30b 100644 --- a/.mvn/wrapper/MavenWrapperDownloader.java +++ b/.mvn/wrapper/MavenWrapperDownloader.java @@ -13,9 +13,16 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -import java.net.*; -import java.io.*; -import java.nio.channels.*; + +import java.io.File; +import java.io.FileInputStream; +import java.io.FileOutputStream; +import java.io.IOException; +import java.net.Authenticator; +import java.net.PasswordAuthentication; +import java.net.URL; +import java.nio.channels.Channels; +import java.nio.channels.ReadableByteChannel; import java.util.Properties; public class MavenWrapperDownloader { diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 4823ea9b222..8f0a48f0f3a 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -4,7 +4,7 @@ repos: - repo: https://github.com/ejba/pre-commit-maven - rev: v0.3.3 + rev: v0.3.4 hooks: - id: maven-spotless-apply stages: [pre-push] diff --git a/hapi-deployable-pom/pom.xml b/hapi-deployable-pom/pom.xml index 5e53eca724d..e0fdc9defbd 100644 --- a/hapi-deployable-pom/pom.xml +++ b/hapi-deployable-pom/pom.xml @@ -5,7 +5,7 @@ ca.uhn.hapi.fhir hapi-fhir - 6.9.7-SNAPSHOT + 6.11.7-SNAPSHOT ../pom.xml @@ -45,7 +45,6 @@ false false false - false true true 2 @@ -103,14 +102,6 @@ net.bytebuddy byte-buddy - - javax.xml.bind - jaxb-api - - - com.sun.xml.bind - jaxb-impl - .*\.txt$ diff --git a/hapi-fhir-android/pom.xml b/hapi-fhir-android/pom.xml index ece0037127c..ee1c47560a0 100644 --- a/hapi-fhir-android/pom.xml +++ b/hapi-fhir-android/pom.xml @@ -5,7 +5,7 @@ ca.uhn.hapi.fhir hapi-deployable-pom - 6.9.7-SNAPSHOT + 6.11.7-SNAPSHOT ../hapi-deployable-pom/pom.xml @@ -131,12 +131,6 @@ org.jacoco jacoco-maven-plugin - - ${basedir}/target/classes - - - ${basedir}/src/main/java - true diff --git a/hapi-fhir-base/pom.xml b/hapi-fhir-base/pom.xml index 8230b4bef07..0b400e33354 100644 --- a/hapi-fhir-base/pom.xml +++ b/hapi-fhir-base/pom.xml @@ -5,7 +5,7 @@ ca.uhn.hapi.fhir hapi-deployable-pom - 6.9.7-SNAPSHOT + 6.11.7-SNAPSHOT ../hapi-deployable-pom/pom.xml @@ -52,19 +52,13 @@ - com.helger - ph-schematron + com.helger.schematron + ph-schematron-api true - - - org.glassfish.jaxb - jaxb-core - - - com.helger - ph-commons + com.helger.schematron + ph-schematron-xslt true @@ -122,10 +116,16 @@ + + com.google.code.findbugs jsr305 + + jakarta.annotation + jakarta.annotation-api + org.awaitility @@ -221,7 +221,6 @@ org.apache.maven.plugins maven-checkstyle-plugin - ${maven_checkstyle_version} ${maven.multiModuleProjectDirectory}/hapi-fhir-checkstyle/src/checkstyle/hapi-base-checkstyle.xml diff --git a/hapi-fhir-base/src/main/java/ca/uhn/fhir/context/BaseRuntimeChildDefinition.java b/hapi-fhir-base/src/main/java/ca/uhn/fhir/context/BaseRuntimeChildDefinition.java index bafc0cce4ce..e55d2fa2b25 100644 --- a/hapi-fhir-base/src/main/java/ca/uhn/fhir/context/BaseRuntimeChildDefinition.java +++ b/hapi-fhir-base/src/main/java/ca/uhn/fhir/context/BaseRuntimeChildDefinition.java @@ -20,6 +20,7 @@ package ca.uhn.fhir.context; import ca.uhn.fhir.i18n.Msg; +import ca.uhn.fhir.model.api.annotation.Child; import org.hl7.fhir.instance.model.api.IBase; import org.hl7.fhir.instance.model.api.IBaseReference; @@ -78,6 +79,10 @@ public abstract class BaseRuntimeChildDefinition { this.myReplacedParentDefinition = myReplacedParentDefinition; } + public boolean isMultipleCardinality() { + return this.getMax() > 1 || this.getMax() == Child.MAX_UNLIMITED; + } + public interface IAccessor { List getValues(IBase theTarget); diff --git a/hapi-fhir-base/src/main/java/ca/uhn/fhir/context/BaseRuntimeElementDefinition.java b/hapi-fhir-base/src/main/java/ca/uhn/fhir/context/BaseRuntimeElementDefinition.java index 76ece1131bf..6cbb0693f57 100644 --- a/hapi-fhir-base/src/main/java/ca/uhn/fhir/context/BaseRuntimeElementDefinition.java +++ b/hapi-fhir-base/src/main/java/ca/uhn/fhir/context/BaseRuntimeElementDefinition.java @@ -21,6 +21,8 @@ package ca.uhn.fhir.context; import ca.uhn.fhir.i18n.Msg; import ca.uhn.fhir.util.UrlUtil; +import jakarta.annotation.Nonnull; +import jakarta.annotation.Nullable; import org.apache.commons.lang3.StringUtils; import org.apache.commons.lang3.Validate; import org.hl7.fhir.instance.model.api.IBase; @@ -31,8 +33,6 @@ import java.util.Collections; import java.util.HashMap; import java.util.List; import java.util.Map; -import javax.annotation.Nonnull; -import javax.annotation.Nullable; public abstract class BaseRuntimeElementDefinition { diff --git a/hapi-fhir-base/src/main/java/ca/uhn/fhir/context/FhirContext.java b/hapi-fhir-base/src/main/java/ca/uhn/fhir/context/FhirContext.java index f62b3fdfc49..44859cf8bc8 100644 --- a/hapi-fhir-base/src/main/java/ca/uhn/fhir/context/FhirContext.java +++ b/hapi-fhir-base/src/main/java/ca/uhn/fhir/context/FhirContext.java @@ -49,6 +49,8 @@ import ca.uhn.fhir.util.FhirTerser; import ca.uhn.fhir.util.ReflectionUtil; import ca.uhn.fhir.util.VersionUtil; import ca.uhn.fhir.validation.FhirValidator; +import jakarta.annotation.Nonnull; +import jakarta.annotation.Nullable; import org.apache.commons.lang3.Validate; import org.apache.commons.lang3.exception.ExceptionUtils; import org.apache.jena.riot.Lang; @@ -77,8 +79,6 @@ import java.util.Optional; import java.util.Properties; import java.util.Set; import java.util.stream.Collectors; -import javax.annotation.Nonnull; -import javax.annotation.Nullable; /** * The FHIR context is the central starting point for the use of the HAPI FHIR API. It should be created once, and then diff --git a/hapi-fhir-base/src/main/java/ca/uhn/fhir/context/ModelScanner.java b/hapi-fhir-base/src/main/java/ca/uhn/fhir/context/ModelScanner.java index 002c34edb7e..7913bc10fa9 100644 --- a/hapi-fhir-base/src/main/java/ca/uhn/fhir/context/ModelScanner.java +++ b/hapi-fhir-base/src/main/java/ca/uhn/fhir/context/ModelScanner.java @@ -38,6 +38,7 @@ import ca.uhn.fhir.model.primitive.BoundCodeDt; import ca.uhn.fhir.model.primitive.XhtmlDt; import ca.uhn.fhir.rest.api.RestSearchParameterTypeEnum; import ca.uhn.fhir.util.ReflectionUtil; +import jakarta.annotation.Nonnull; import org.hl7.fhir.instance.model.api.IBase; import org.hl7.fhir.instance.model.api.IBaseBackboneElement; import org.hl7.fhir.instance.model.api.IBaseDatatype; @@ -67,7 +68,6 @@ import java.util.Map; import java.util.Map.Entry; import java.util.Properties; import java.util.Set; -import javax.annotation.Nonnull; import static org.apache.commons.lang3.StringUtils.isBlank; @@ -498,7 +498,7 @@ class ModelScanner { nextElementType = ReflectionUtil.getGenericCollectionTypeOfField(next); } else if (Collection.class.isAssignableFrom(nextElementType)) { throw new ConfigurationException(Msg.code(1722) + "Field '" + next.getName() + "' in type '" - + next.getClass().getCanonicalName() + + nextElementType.getCanonicalName() + "' is a Collection - Only java.util.List curently supported"); } return nextElementType; diff --git a/hapi-fhir-base/src/main/java/ca/uhn/fhir/context/RuntimeSearchParam.java b/hapi-fhir-base/src/main/java/ca/uhn/fhir/context/RuntimeSearchParam.java index cba53cb118c..6cbc5532614 100644 --- a/hapi-fhir-base/src/main/java/ca/uhn/fhir/context/RuntimeSearchParam.java +++ b/hapi-fhir-base/src/main/java/ca/uhn/fhir/context/RuntimeSearchParam.java @@ -21,6 +21,8 @@ package ca.uhn.fhir.context; import ca.uhn.fhir.context.phonetic.IPhoneticEncoder; import ca.uhn.fhir.rest.api.RestSearchParameterTypeEnum; +import jakarta.annotation.Nonnull; +import jakarta.annotation.Nullable; import org.apache.commons.lang3.builder.EqualsBuilder; import org.apache.commons.lang3.builder.HashCodeBuilder; import org.apache.commons.lang3.builder.ToStringBuilder; @@ -37,8 +39,6 @@ import java.util.List; import java.util.Map; import java.util.Set; import java.util.StringTokenizer; -import javax.annotation.Nonnull; -import javax.annotation.Nullable; import static org.apache.commons.lang3.StringUtils.isNotBlank; import static org.apache.commons.lang3.StringUtils.trim; diff --git a/hapi-fhir-base/src/main/java/ca/uhn/fhir/context/support/DefaultProfileValidationSupport.java b/hapi-fhir-base/src/main/java/ca/uhn/fhir/context/support/DefaultProfileValidationSupport.java index 4a103500ebf..87bb3d000eb 100644 --- a/hapi-fhir-base/src/main/java/ca/uhn/fhir/context/support/DefaultProfileValidationSupport.java +++ b/hapi-fhir-base/src/main/java/ca/uhn/fhir/context/support/DefaultProfileValidationSupport.java @@ -23,6 +23,7 @@ import ca.uhn.fhir.context.FhirContext; import ca.uhn.fhir.context.FhirVersionEnum; import ca.uhn.fhir.util.ILockable; import ca.uhn.fhir.util.ReflectionUtil; +import jakarta.annotation.Nullable; import org.hl7.fhir.instance.model.api.IBase; import org.hl7.fhir.instance.model.api.IBaseResource; import org.hl7.fhir.instance.model.api.IPrimitiveType; @@ -32,7 +33,6 @@ import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.Optional; -import javax.annotation.Nullable; /** * This class returns the vocabulary that is shipped with the base FHIR @@ -99,6 +99,11 @@ public class DefaultProfileValidationSupport implements IValidationSupport { } } + @Override + public String getName() { + return myCtx.getVersion().getVersion() + " FHIR Standard Profile Validation Support"; + } + @Override public List fetchAllConformanceResources() { return myDelegate.fetchAllConformanceResources(); diff --git a/hapi-fhir-base/src/main/java/ca/uhn/fhir/context/support/DefaultProfileValidationSupportBundleStrategy.java b/hapi-fhir-base/src/main/java/ca/uhn/fhir/context/support/DefaultProfileValidationSupportBundleStrategy.java index 22a130a98fe..79d9ba03855 100644 --- a/hapi-fhir-base/src/main/java/ca/uhn/fhir/context/support/DefaultProfileValidationSupportBundleStrategy.java +++ b/hapi-fhir-base/src/main/java/ca/uhn/fhir/context/support/DefaultProfileValidationSupportBundleStrategy.java @@ -28,6 +28,7 @@ import ca.uhn.fhir.parser.LenientErrorHandler; import ca.uhn.fhir.rest.api.Constants; import ca.uhn.fhir.util.BundleUtil; import ca.uhn.fhir.util.ClasspathUtil; +import jakarta.annotation.Nullable; import org.apache.commons.lang3.StringUtils; import org.hl7.fhir.instance.model.api.IBaseBundle; import org.hl7.fhir.instance.model.api.IBaseResource; @@ -43,7 +44,6 @@ import java.util.List; import java.util.Locale; import java.util.Map; import java.util.Properties; -import javax.annotation.Nullable; import static org.apache.commons.lang3.StringUtils.isNotBlank; diff --git a/hapi-fhir-base/src/main/java/ca/uhn/fhir/context/support/IValidationSupport.java b/hapi-fhir-base/src/main/java/ca/uhn/fhir/context/support/IValidationSupport.java index b08d10cadc5..967aeb3a549 100644 --- a/hapi-fhir-base/src/main/java/ca/uhn/fhir/context/support/IValidationSupport.java +++ b/hapi-fhir-base/src/main/java/ca/uhn/fhir/context/support/IValidationSupport.java @@ -24,9 +24,12 @@ import ca.uhn.fhir.i18n.Msg; import ca.uhn.fhir.rest.server.exceptions.ResourceNotFoundException; import ca.uhn.fhir.util.ParametersUtil; import ca.uhn.fhir.util.UrlUtil; +import jakarta.annotation.Nonnull; +import jakarta.annotation.Nullable; import org.apache.commons.lang3.Validate; import org.apache.commons.lang3.builder.EqualsBuilder; import org.apache.commons.lang3.builder.HashCodeBuilder; +import org.apache.commons.lang3.builder.ToStringBuilder; import org.hl7.fhir.instance.model.api.IBase; import org.hl7.fhir.instance.model.api.IBaseCoding; import org.hl7.fhir.instance.model.api.IBaseParameters; @@ -41,8 +44,6 @@ import java.util.List; import java.util.Set; import java.util.function.Supplier; import java.util.stream.Collectors; -import javax.annotation.Nonnull; -import javax.annotation.Nullable; import static org.apache.commons.lang3.StringUtils.defaultString; import static org.apache.commons.lang3.StringUtils.isNotBlank; @@ -294,8 +295,8 @@ public interface IValidationSupport { */ @Nullable default CodeValidationResult validateCode( - @Nonnull ValidationSupportContext theValidationSupportContext, - @Nonnull ConceptValidationOptions theOptions, + ValidationSupportContext theValidationSupportContext, + ConceptValidationOptions theOptions, String theCodeSystem, String theCode, String theDisplay, @@ -328,14 +329,16 @@ public interface IValidationSupport { } /** - * Look up a code using the system and code value + * Look up a code using the system and code value. + * @deprecated This method has been deprecated in HAPI FHIR 7.0.0. Use {@link IValidationSupport#lookupCode(ValidationSupportContext, LookupCodeRequest)} instead. * * @param theValidationSupportContext The validation support module will be passed in to this method. This is convenient in cases where the operation needs to make calls to * other method in the support chain, so that they can be passed through the entire chain. Implementations of this interface may always safely ignore this parameter. * @param theSystem The CodeSystem URL * @param theCode The code - * @param theDisplayLanguage to filter out the designation by the display language. To return all designation, set this value to null. + * @param theDisplayLanguage Used to filter out the designation by the display language. To return all designation, set this value to null. */ + @Deprecated @Nullable default LookupCodeResult lookupCode( ValidationSupportContext theValidationSupportContext, @@ -347,12 +350,14 @@ public interface IValidationSupport { /** * Look up a code using the system and code value + * @deprecated This method has been deprecated in HAPI FHIR 7.0.0. Use {@link IValidationSupport#lookupCode(ValidationSupportContext, LookupCodeRequest)} instead. * * @param theValidationSupportContext The validation support module will be passed in to this method. This is convenient in cases where the operation needs to make calls to * other method in the support chain, so that they can be passed through the entire chain. Implementations of this interface may always safely ignore this parameter. * @param theSystem The CodeSystem URL * @param theCode The code */ + @Deprecated @Nullable default LookupCodeResult lookupCode( ValidationSupportContext theValidationSupportContext, String theSystem, String theCode) { @@ -360,7 +365,26 @@ public interface IValidationSupport { } /** - * Returns true if the given valueset can be validated by the given + * Look up a code using the system, code and other parameters captured in {@link LookupCodeRequest}. + * @since 7.0.0 + * + * @param theValidationSupportContext The validation support module will be passed in to this method. This is convenient in cases where the operation needs to make calls to + * other method in the support chain, so that they can be passed through the entire chain. Implementations of this interface may always safely ignore this parameter. + * @param theLookupCodeRequest The parameters used to perform the lookup, including system and code. + */ + @Nullable + default LookupCodeResult lookupCode( + ValidationSupportContext theValidationSupportContext, @Nonnull LookupCodeRequest theLookupCodeRequest) { + // TODO: can change to return null once the deprecated methods are removed + return lookupCode( + theValidationSupportContext, + theLookupCodeRequest.getSystem(), + theLookupCodeRequest.getCode(), + theLookupCodeRequest.getDisplayLanguage()); + } + + /** + * Returns true if the given ValueSet can be validated by the given * validation support module * * @param theValidationSupportContext The validation support module will be passed in to this method. This is convenient in cases where the operation needs to make calls to @@ -409,6 +433,13 @@ public interface IValidationSupport { return null; } + /** + * This field is used by the Terminology Troubleshooting Log to log which validation support module was used for the operation being logged. + */ + default String getName() { + return "Unknown " + getFhirContext().getVersion().getVersion() + " Validation Support"; + } + enum IssueSeverity { /** * The issue caused the action to fail, and no further checking could be performed. @@ -495,8 +526,13 @@ public interface IValidationSupport { public String getPropertyName() { return myPropertyName; } + + public abstract String getType(); } + String TYPE_STRING = "string"; + String TYPE_CODING = "Coding"; + class StringConceptProperty extends BaseConceptProperty { private final String myValue; @@ -513,6 +549,10 @@ public interface IValidationSupport { public String getValue() { return myValue; } + + public String getType() { + return TYPE_STRING; + } } class CodingConceptProperty extends BaseConceptProperty { @@ -543,9 +583,18 @@ public interface IValidationSupport { public String getDisplay() { return myDisplay; } + + public String getType() { + return TYPE_CODING; + } } class CodeValidationResult { + public static final String SOURCE_DETAILS = "sourceDetails"; + public static final String RESULT = "result"; + public static final String MESSAGE = "message"; + public static final String DISPLAY = "display"; + private String myCode; private String myMessage; private IssueSeverity mySeverity; @@ -674,6 +723,23 @@ public interface IValidationSupport { setSeverity(IssueSeverity.valueOf(theIssueSeverity.toUpperCase())); return this; } + + public IBaseParameters toParameters(FhirContext theContext) { + IBaseParameters retVal = ParametersUtil.newInstance(theContext); + + ParametersUtil.addParameterToParametersBoolean(theContext, retVal, RESULT, isOk()); + if (isNotBlank(getMessage())) { + ParametersUtil.addParameterToParametersString(theContext, retVal, MESSAGE, getMessage()); + } + if (isNotBlank(getDisplay())) { + ParametersUtil.addParameterToParametersString(theContext, retVal, DISPLAY, getDisplay()); + } + if (isNotBlank(getSourceDetails())) { + ParametersUtil.addParameterToParametersString(theContext, retVal, SOURCE_DETAILS, getSourceDetails()); + } + + return retVal; + } } class ValueSetExpansionOutcome { @@ -806,7 +872,7 @@ public interface IValidationSupport { } public IBaseParameters toParameters( - FhirContext theContext, List> theProperties) { + FhirContext theContext, List> thePropertyNames) { IBaseParameters retVal = ParametersUtil.newInstance(theContext); if (isNotBlank(getCodeSystemDisplayName())) { @@ -821,32 +887,42 @@ public interface IValidationSupport { if (myProperties != null) { Set properties = Collections.emptySet(); - if (theProperties != null) { - properties = theProperties.stream() + if (thePropertyNames != null) { + properties = thePropertyNames.stream() .map(IPrimitiveType::getValueAsString) .collect(Collectors.toSet()); } for (BaseConceptProperty next : myProperties) { + String propertyName = next.getPropertyName(); - if (!properties.isEmpty()) { - if (!properties.contains(next.getPropertyName())) { - continue; - } + if (!properties.isEmpty() && !properties.contains(propertyName)) { + continue; } IBase property = ParametersUtil.addParameterToParameters(theContext, retVal, "property"); - ParametersUtil.addPartCode(theContext, property, "code", next.getPropertyName()); + ParametersUtil.addPartCode(theContext, property, "code", propertyName); - if (next instanceof StringConceptProperty) { - StringConceptProperty prop = (StringConceptProperty) next; - ParametersUtil.addPartString(theContext, property, "value", prop.getValue()); - } else if (next instanceof CodingConceptProperty) { - CodingConceptProperty prop = (CodingConceptProperty) next; - ParametersUtil.addPartCoding( - theContext, property, "value", prop.getCodeSystem(), prop.getCode(), prop.getDisplay()); - } else { - throw new IllegalStateException(Msg.code(1739) + "Don't know how to handle " + next.getClass()); + String propertyType = next.getType(); + switch (propertyType) { + case TYPE_STRING: + StringConceptProperty stringConceptProperty = (StringConceptProperty) next; + ParametersUtil.addPartString( + theContext, property, "value", stringConceptProperty.getValue()); + break; + case TYPE_CODING: + CodingConceptProperty codingConceptProperty = (CodingConceptProperty) next; + ParametersUtil.addPartCoding( + theContext, + property, + "value", + codingConceptProperty.getCodeSystem(), + codingConceptProperty.getCode(), + codingConceptProperty.getDisplay()); + break; + default: + throw new IllegalStateException( + Msg.code(1739) + "Don't know how to handle " + next.getClass()); } } } @@ -990,6 +1066,16 @@ public interface IValidationSupport { public boolean isReverse() { return myReverse; } + + @Override + public String toString() { + return new ToStringBuilder(this) + .append("sourceValueSetUrl", mySourceValueSetUrl) + .append("targetSystemUrl", myTargetSystemUrl) + .append("targetValueSetUrl", myTargetValueSetUrl) + .append("reverse", myReverse) + .toString(); + } } /** diff --git a/hapi-fhir-base/src/main/java/ca/uhn/fhir/context/support/LookupCodeRequest.java b/hapi-fhir-base/src/main/java/ca/uhn/fhir/context/support/LookupCodeRequest.java new file mode 100644 index 00000000000..8c3bdcaa640 --- /dev/null +++ b/hapi-fhir-base/src/main/java/ca/uhn/fhir/context/support/LookupCodeRequest.java @@ -0,0 +1,75 @@ +/*- + * #%L + * HAPI FHIR - Core Library + * %% + * Copyright (C) 2014 - 2023 Smile CDR, Inc. + * %% + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * #L% + */ +package ca.uhn.fhir.context.support; + +import java.util.Collection; +import java.util.Collections; + +/** + * Represents parameters which can be passed to the $lookup operation for codes. + * @since 7.0.0 + */ +public class LookupCodeRequest { + private final String mySystem; + private final String myCode; + private String myDisplayLanguage; + private Collection myPropertyNames; + + /** + * @param theSystem The CodeSystem URL + * @param theCode The code + */ + public LookupCodeRequest(String theSystem, String theCode) { + mySystem = theSystem; + myCode = theCode; + } + + /** + * @param theSystem The CodeSystem URL + * @param theCode The code + * @param theDisplayLanguage Used to filter out the designation by the display language. To return all designation, set this value to null. + * @param thePropertyNames The collection of properties to be returned in the output. If no properties are specified, the implementor chooses what to return. + */ + public LookupCodeRequest( + String theSystem, String theCode, String theDisplayLanguage, Collection thePropertyNames) { + this(theSystem, theCode); + myDisplayLanguage = theDisplayLanguage; + myPropertyNames = thePropertyNames; + } + + public String getSystem() { + return mySystem; + } + + public String getCode() { + return myCode; + } + + public String getDisplayLanguage() { + return myDisplayLanguage; + } + + public Collection getPropertyNames() { + if (myPropertyNames == null) { + return Collections.emptyList(); + } + return myPropertyNames; + } +} diff --git a/hapi-fhir-base/src/main/java/ca/uhn/fhir/fhirpath/IFhirPath.java b/hapi-fhir-base/src/main/java/ca/uhn/fhir/fhirpath/IFhirPath.java index 67fc8ce5ff3..194607b8873 100644 --- a/hapi-fhir-base/src/main/java/ca/uhn/fhir/fhirpath/IFhirPath.java +++ b/hapi-fhir-base/src/main/java/ca/uhn/fhir/fhirpath/IFhirPath.java @@ -19,11 +19,11 @@ */ package ca.uhn.fhir.fhirpath; +import jakarta.annotation.Nonnull; import org.hl7.fhir.instance.model.api.IBase; import java.util.List; import java.util.Optional; -import javax.annotation.Nonnull; public interface IFhirPath { diff --git a/hapi-fhir-base/src/main/java/ca/uhn/fhir/fhirpath/IFhirPathEvaluationContext.java b/hapi-fhir-base/src/main/java/ca/uhn/fhir/fhirpath/IFhirPathEvaluationContext.java index 00b6abeab61..ad99df8b7bb 100644 --- a/hapi-fhir-base/src/main/java/ca/uhn/fhir/fhirpath/IFhirPathEvaluationContext.java +++ b/hapi-fhir-base/src/main/java/ca/uhn/fhir/fhirpath/IFhirPathEvaluationContext.java @@ -19,12 +19,11 @@ */ package ca.uhn.fhir.fhirpath; +import jakarta.annotation.Nonnull; +import jakarta.annotation.Nullable; import org.hl7.fhir.instance.model.api.IBase; import org.hl7.fhir.instance.model.api.IIdType; -import javax.annotation.Nonnull; -import javax.annotation.Nullable; - public interface IFhirPathEvaluationContext { /** diff --git a/hapi-fhir-base/src/main/java/ca/uhn/fhir/interceptor/api/HookParams.java b/hapi-fhir-base/src/main/java/ca/uhn/fhir/interceptor/api/HookParams.java index f9f96ccb4c1..9784541da0b 100644 --- a/hapi-fhir-base/src/main/java/ca/uhn/fhir/interceptor/api/HookParams.java +++ b/hapi-fhir-base/src/main/java/ca/uhn/fhir/interceptor/api/HookParams.java @@ -22,6 +22,7 @@ package ca.uhn.fhir.interceptor.api; import com.google.common.collect.ArrayListMultimap; import com.google.common.collect.ListMultimap; import com.google.common.collect.Multimaps; +import jakarta.annotation.Nonnull; import org.apache.commons.lang3.Validate; import java.util.Collection; @@ -29,7 +30,6 @@ import java.util.Collections; import java.util.List; import java.util.function.Supplier; import java.util.stream.Collectors; -import javax.annotation.Nonnull; public class HookParams { diff --git a/hapi-fhir-base/src/main/java/ca/uhn/fhir/interceptor/api/IBaseInterceptorService.java b/hapi-fhir-base/src/main/java/ca/uhn/fhir/interceptor/api/IBaseInterceptorService.java index d0fdcb61c94..0f4b14a6ff1 100644 --- a/hapi-fhir-base/src/main/java/ca/uhn/fhir/interceptor/api/IBaseInterceptorService.java +++ b/hapi-fhir-base/src/main/java/ca/uhn/fhir/interceptor/api/IBaseInterceptorService.java @@ -19,10 +19,11 @@ */ package ca.uhn.fhir.interceptor.api; +import jakarta.annotation.Nullable; + import java.util.Collection; import java.util.List; import java.util.function.Predicate; -import javax.annotation.Nullable; public interface IBaseInterceptorService extends IBaseInterceptorBroadcaster { diff --git a/hapi-fhir-base/src/main/java/ca/uhn/fhir/interceptor/api/IPointcut.java b/hapi-fhir-base/src/main/java/ca/uhn/fhir/interceptor/api/IPointcut.java index 8ebd00bee4c..4c389567ecf 100644 --- a/hapi-fhir-base/src/main/java/ca/uhn/fhir/interceptor/api/IPointcut.java +++ b/hapi-fhir-base/src/main/java/ca/uhn/fhir/interceptor/api/IPointcut.java @@ -19,8 +19,9 @@ */ package ca.uhn.fhir.interceptor.api; +import jakarta.annotation.Nonnull; + import java.util.List; -import javax.annotation.Nonnull; public interface IPointcut { @Nonnull diff --git a/hapi-fhir-base/src/main/java/ca/uhn/fhir/interceptor/api/Pointcut.java b/hapi-fhir-base/src/main/java/ca/uhn/fhir/interceptor/api/Pointcut.java index 994ea9cd1cf..828a060a113 100644 --- a/hapi-fhir-base/src/main/java/ca/uhn/fhir/interceptor/api/Pointcut.java +++ b/hapi-fhir-base/src/main/java/ca/uhn/fhir/interceptor/api/Pointcut.java @@ -25,6 +25,7 @@ import ca.uhn.fhir.rest.annotation.Search; import ca.uhn.fhir.rest.server.exceptions.AuthenticationException; import ca.uhn.fhir.rest.server.exceptions.BaseServerResponseException; import ca.uhn.fhir.validation.ValidationResult; +import jakarta.annotation.Nonnull; import org.hl7.fhir.instance.model.api.IBaseConformance; import java.io.Writer; @@ -33,7 +34,6 @@ import java.util.Collections; import java.util.HashSet; import java.util.List; import java.util.Set; -import javax.annotation.Nonnull; /** * Value for {@link Hook#value()} @@ -93,6 +93,10 @@ public enum Pointcut implements IPointcut { *
  • * ca.uhn.fhir.rest.client.api.IRestfulClient - The client object making the request *
  • + *
  • + * ca.uhn.fhir.rest.client.api.ClientResponseContext - Contains an IHttpRequest, an IHttpResponse, and an IRestfulClient + * and also allows the client to mutate the contained IHttpResponse + *
  • * *

    * Hook methods must return void. @@ -101,7 +105,8 @@ public enum Pointcut implements IPointcut { void.class, "ca.uhn.fhir.rest.client.api.IHttpRequest", "ca.uhn.fhir.rest.client.api.IHttpResponse", - "ca.uhn.fhir.rest.client.api.IRestfulClient"), + "ca.uhn.fhir.rest.client.api.IRestfulClient", + "ca.uhn.fhir.rest.client.api.ClientResponseContext"), /** * Server Hook: @@ -153,10 +158,10 @@ public enum Pointcut implements IPointcut { * Hooks may accept the following parameters: *
      *
    • - * javax.servlet.http.HttpServletRequest - The servlet request, when running in a servlet environment + * jakarta.servlet.http.HttpServletRequest - The servlet request, when running in a servlet environment *
    • *
    • - * javax.servlet.http.HttpServletResponse - The servlet response, when running in a servlet environment + * jakarta.servlet.http.HttpServletResponse - The servlet response, when running in a servlet environment *
    • *
    *

    @@ -166,7 +171,7 @@ public enum Pointcut implements IPointcut { * no further processing will occur and no further interceptors will be called. */ SERVER_INCOMING_REQUEST_PRE_PROCESSED( - boolean.class, "javax.servlet.http.HttpServletRequest", "javax.servlet.http.HttpServletResponse"), + boolean.class, "jakarta.servlet.http.HttpServletRequest", "jakarta.servlet.http.HttpServletResponse"), /** * Server Hook: @@ -193,10 +198,10 @@ public enum Pointcut implements IPointcut { * only be populated when operating in a RestfulServer implementation. It is provided as a convenience. * *
  • - * javax.servlet.http.HttpServletRequest - The servlet request, when running in a servlet environment + * jakarta.servlet.http.HttpServletRequest - The servlet request, when running in a servlet environment *
  • *
  • - * javax.servlet.http.HttpServletResponse - The servlet response, when running in a servlet environment + * jakarta.servlet.http.HttpServletResponse - The servlet response, when running in a servlet environment *
  • *
  • * ca.uhn.fhir.rest.server.exceptions.BaseServerResponseException - The exception that was thrown @@ -215,8 +220,8 @@ public enum Pointcut implements IPointcut { boolean.class, "ca.uhn.fhir.rest.api.server.RequestDetails", "ca.uhn.fhir.rest.server.servlet.ServletRequestDetails", - "javax.servlet.http.HttpServletRequest", - "javax.servlet.http.HttpServletResponse", + "jakarta.servlet.http.HttpServletRequest", + "jakarta.servlet.http.HttpServletResponse", "ca.uhn.fhir.rest.server.exceptions.BaseServerResponseException"), /** @@ -239,10 +244,10 @@ public enum Pointcut implements IPointcut { * only be populated when operating in a RestfulServer implementation. It is provided as a convenience. *
  • *
  • - * javax.servlet.http.HttpServletRequest - The servlet request, when running in a servlet environment + * jakarta.servlet.http.HttpServletRequest - The servlet request, when running in a servlet environment *
  • *
  • - * javax.servlet.http.HttpServletResponse - The servlet response, when running in a servlet environment + * jakarta.servlet.http.HttpServletResponse - The servlet response, when running in a servlet environment *
  • * *

    @@ -263,8 +268,8 @@ public enum Pointcut implements IPointcut { boolean.class, "ca.uhn.fhir.rest.api.server.RequestDetails", "ca.uhn.fhir.rest.server.servlet.ServletRequestDetails", - "javax.servlet.http.HttpServletRequest", - "javax.servlet.http.HttpServletResponse"), + "jakarta.servlet.http.HttpServletRequest", + "jakarta.servlet.http.HttpServletResponse"), /** * Server Hook: @@ -286,10 +291,10 @@ public enum Pointcut implements IPointcut { * only be populated when operating in a RestfulServer implementation. It is provided as a convenience. * *

  • - * javax.servlet.http.HttpServletRequest - The servlet request, when running in a servlet environment + * jakarta.servlet.http.HttpServletRequest - The servlet request, when running in a servlet environment *
  • *
  • - * javax.servlet.http.HttpServletResponse - The servlet response, when running in a servlet environment + * jakarta.servlet.http.HttpServletResponse - The servlet response, when running in a servlet environment *
  • * *

    @@ -308,8 +313,8 @@ public enum Pointcut implements IPointcut { boolean.class, "ca.uhn.fhir.rest.api.server.RequestDetails", "ca.uhn.fhir.rest.server.servlet.ServletRequestDetails", - "javax.servlet.http.HttpServletRequest", - "javax.servlet.http.HttpServletResponse"), + "jakarta.servlet.http.HttpServletRequest", + "jakarta.servlet.http.HttpServletResponse"), /** * Server Hook: @@ -411,10 +416,10 @@ public enum Pointcut implements IPointcut { * {@link NullPointerException} in the case of a bug being triggered. * *

  • - * javax.servlet.http.HttpServletRequest - The servlet request, when running in a servlet environment + * jakarta.servlet.http.HttpServletRequest - The servlet request, when running in a servlet environment *
  • *
  • - * javax.servlet.http.HttpServletResponse - The servlet response, when running in a servlet environment + * jakarta.servlet.http.HttpServletResponse - The servlet response, when running in a servlet environment *
  • * *

    @@ -429,8 +434,8 @@ public enum Pointcut implements IPointcut { "ca.uhn.fhir.rest.api.server.RequestDetails", "ca.uhn.fhir.rest.server.servlet.ServletRequestDetails", "java.lang.Throwable", - "javax.servlet.http.HttpServletRequest", - "javax.servlet.http.HttpServletResponse"), + "jakarta.servlet.http.HttpServletRequest", + "jakarta.servlet.http.HttpServletResponse"), /** * Server Hook: @@ -458,10 +463,10 @@ public enum Pointcut implements IPointcut { * ca.uhn.fhir.rest.api.server.ResponseDetails - This object contains details about the response, including the contents. Hook methods may modify this object to change or replace the response. * *

  • - * javax.servlet.http.HttpServletRequest - The servlet request, when running in a servlet environment + * jakarta.servlet.http.HttpServletRequest - The servlet request, when running in a servlet environment *
  • *
  • - * javax.servlet.http.HttpServletResponse - The servlet response, when running in a servlet environment + * jakarta.servlet.http.HttpServletResponse - The servlet response, when running in a servlet environment *
  • * *

    @@ -482,8 +487,8 @@ public enum Pointcut implements IPointcut { "ca.uhn.fhir.rest.server.servlet.ServletRequestDetails", "org.hl7.fhir.instance.model.api.IBaseResource", "ca.uhn.fhir.rest.api.server.ResponseDetails", - "javax.servlet.http.HttpServletRequest", - "javax.servlet.http.HttpServletResponse"), + "jakarta.servlet.http.HttpServletRequest", + "jakarta.servlet.http.HttpServletResponse"), /** * Server Hook: @@ -549,10 +554,10 @@ public enum Pointcut implements IPointcut { * java.lang.String - The GraphQL response * *
  • - * javax.servlet.http.HttpServletRequest - The servlet request, when running in a servlet environment + * jakarta.servlet.http.HttpServletRequest - The servlet request, when running in a servlet environment *
  • *
  • - * javax.servlet.http.HttpServletResponse - The servlet response, when running in a servlet environment + * jakarta.servlet.http.HttpServletResponse - The servlet response, when running in a servlet environment *
  • * *

    @@ -573,8 +578,8 @@ public enum Pointcut implements IPointcut { "ca.uhn.fhir.rest.server.servlet.ServletRequestDetails", "java.lang.String", "java.lang.String", - "javax.servlet.http.HttpServletRequest", - "javax.servlet.http.HttpServletResponse"), + "jakarta.servlet.http.HttpServletRequest", + "jakarta.servlet.http.HttpServletResponse"), /** * Server Hook: @@ -2930,6 +2935,31 @@ public enum Pointcut implements IPointcut { "ca.uhn.fhir.rest.api.server.RequestDetails", "org.hl7.fhir.instance.model.api.IBaseResource"), + /** + * Storage Hook: + * Invoked before a batch job is persisted to the database. + *

    + * Hooks will have access to the content of the job being created + * and may choose to make modifications to it. These changes will be + * reflected in permanent storage. + *

    + * Hooks may accept the following parameters: + *
      + *
    • + * ca.uhn.fhir.batch2.model.JobInstance + *
    • + *
    • + * ca.uhn.fhir.rest.api.server.RequestDetails - A bean containing details about the request that lead to the creation + * of the jobInstance. + *
    • + *
    + *

    + * Hooks should return void. + *

    + */ + STORAGE_PRESTORAGE_BATCH_JOB_CREATE( + void.class, "ca.uhn.fhir.batch2.model.JobInstance", "ca.uhn.fhir.rest.api.server.RequestDetails"), + /** * This pointcut is used only for unit tests. Do not use in production code as it may be changed or * removed at any time. diff --git a/hapi-fhir-base/src/main/java/ca/uhn/fhir/interceptor/executor/BaseInterceptorService.java b/hapi-fhir-base/src/main/java/ca/uhn/fhir/interceptor/executor/BaseInterceptorService.java index 07ac7df658b..5d1f409a3eb 100644 --- a/hapi-fhir-base/src/main/java/ca/uhn/fhir/interceptor/executor/BaseInterceptorService.java +++ b/hapi-fhir-base/src/main/java/ca/uhn/fhir/interceptor/executor/BaseInterceptorService.java @@ -31,6 +31,8 @@ import ca.uhn.fhir.util.ReflectionUtil; import com.google.common.annotations.VisibleForTesting; import com.google.common.collect.ArrayListMultimap; import com.google.common.collect.ListMultimap; +import jakarta.annotation.Nonnull; +import jakarta.annotation.Nullable; import org.apache.commons.lang3.Validate; import org.apache.commons.lang3.builder.ToStringBuilder; import org.apache.commons.lang3.builder.ToStringStyle; @@ -57,8 +59,6 @@ import java.util.Optional; import java.util.concurrent.atomic.AtomicInteger; import java.util.function.Predicate; import java.util.stream.Collectors; -import javax.annotation.Nonnull; -import javax.annotation.Nullable; public abstract class BaseInterceptorService & IPointcut> implements IBaseInterceptorService, IBaseInterceptorBroadcaster { @@ -263,10 +263,14 @@ public abstract class BaseInterceptorService & I return myRegisteredPointcuts.contains(thePointcut); } + protected Class getBooleanReturnType() { + return boolean.class; + } + @Override public boolean callHooks(POINTCUT thePointcut, HookParams theParams) { assert haveAppropriateParams(thePointcut, theParams); - assert thePointcut.getReturnType() == void.class || thePointcut.getReturnType() == boolean.class; + assert thePointcut.getReturnType() == void.class || thePointcut.getReturnType() == getBooleanReturnType(); Object retValObj = doCallHooks(thePointcut, theParams, true); return (Boolean) retValObj; @@ -282,14 +286,16 @@ public abstract class BaseInterceptorService & I for (BaseInvoker nextInvoker : invokers) { Object nextOutcome = nextInvoker.invoke(theParams); Class pointcutReturnType = thePointcut.getReturnType(); - if (pointcutReturnType.equals(boolean.class)) { + if (pointcutReturnType.equals(getBooleanReturnType())) { Boolean nextOutcomeAsBoolean = (Boolean) nextOutcome; if (Boolean.FALSE.equals(nextOutcomeAsBoolean)) { ourLog.trace("callHooks({}) for invoker({}) returned false", thePointcut, nextInvoker); theRetVal = false; break; + } else { + theRetVal = true; } - } else if (pointcutReturnType.equals(void.class) == false) { + } else if (!pointcutReturnType.equals(void.class)) { if (nextOutcome != null) { theRetVal = nextOutcome; break; @@ -349,7 +355,7 @@ public abstract class BaseInterceptorService & I List retVal; - if (haveMultiple == false) { + if (!haveMultiple) { // The global list doesn't need to be sorted every time since it's sorted on // insertion each time. Doing so is a waste of cycles.. @@ -485,9 +491,9 @@ public abstract class BaseInterceptorService & I myMethod = theHookMethod; Class returnType = theHookMethod.getReturnType(); - if (myPointcut.getReturnType().equals(boolean.class)) { + if (myPointcut.getReturnType().equals(getBooleanReturnType())) { Validate.isTrue( - boolean.class.equals(returnType) || void.class.equals(returnType), + getBooleanReturnType().equals(returnType) || void.class.equals(returnType), "Method does not return boolean or void: %s", theHookMethod); } else if (myPointcut.getReturnType().equals(void.class)) { diff --git a/hapi-fhir-base/src/main/java/ca/uhn/fhir/interceptor/model/RequestPartitionId.java b/hapi-fhir-base/src/main/java/ca/uhn/fhir/interceptor/model/RequestPartitionId.java index 2486f655e0d..8f78cb4f1fd 100644 --- a/hapi-fhir-base/src/main/java/ca/uhn/fhir/interceptor/model/RequestPartitionId.java +++ b/hapi-fhir-base/src/main/java/ca/uhn/fhir/interceptor/model/RequestPartitionId.java @@ -24,6 +24,8 @@ import ca.uhn.fhir.util.JsonUtil; import com.fasterxml.jackson.annotation.JsonProperty; import com.fasterxml.jackson.core.JsonProcessingException; import com.fasterxml.jackson.databind.ObjectMapper; +import jakarta.annotation.Nonnull; +import jakarta.annotation.Nullable; import org.apache.commons.lang3.Validate; import org.apache.commons.lang3.builder.EqualsBuilder; import org.apache.commons.lang3.builder.HashCodeBuilder; @@ -37,8 +39,6 @@ import java.util.Collection; import java.util.Collections; import java.util.List; import java.util.stream.Collectors; -import javax.annotation.Nonnull; -import javax.annotation.Nullable; import static org.apache.commons.lang3.ObjectUtils.defaultIfNull; diff --git a/hapi-fhir-base/src/main/java/ca/uhn/fhir/model/api/BaseElement.java b/hapi-fhir-base/src/main/java/ca/uhn/fhir/model/api/BaseElement.java index 0b877f2c434..6e675c3350f 100644 --- a/hapi-fhir-base/src/main/java/ca/uhn/fhir/model/api/BaseElement.java +++ b/hapi-fhir-base/src/main/java/ca/uhn/fhir/model/api/BaseElement.java @@ -24,7 +24,11 @@ import ca.uhn.fhir.model.api.annotation.Description; import org.apache.commons.lang3.Validate; import org.hl7.fhir.instance.model.api.IBaseDatatype; -import java.util.*; +import java.util.ArrayList; +import java.util.Collections; +import java.util.HashMap; +import java.util.List; +import java.util.Map; public abstract class BaseElement implements /*IElement, */ ISupportsUndeclaredExtensions { diff --git a/hapi-fhir-base/src/main/java/ca/uhn/fhir/model/api/IFhirVersion.java b/hapi-fhir-base/src/main/java/ca/uhn/fhir/model/api/IFhirVersion.java index f506a1f57aa..0532423b8ec 100644 --- a/hapi-fhir-base/src/main/java/ca/uhn/fhir/model/api/IFhirVersion.java +++ b/hapi-fhir-base/src/main/java/ca/uhn/fhir/model/api/IFhirVersion.java @@ -19,10 +19,15 @@ */ package ca.uhn.fhir.model.api; -import ca.uhn.fhir.context.*; +import ca.uhn.fhir.context.FhirContext; +import ca.uhn.fhir.context.FhirVersionEnum; +import ca.uhn.fhir.context.RuntimeResourceDefinition; import ca.uhn.fhir.fhirpath.IFhirPath; import ca.uhn.fhir.rest.api.IVersionSpecificBundleFactory; -import org.hl7.fhir.instance.model.api.*; +import org.hl7.fhir.instance.model.api.IBase; +import org.hl7.fhir.instance.model.api.IBaseResource; +import org.hl7.fhir.instance.model.api.IIdType; +import org.hl7.fhir.instance.model.api.IPrimitiveType; import java.io.InputStream; import java.util.Date; diff --git a/hapi-fhir-base/src/main/java/ca/uhn/fhir/narrative2/BaseNarrativeGenerator.java b/hapi-fhir-base/src/main/java/ca/uhn/fhir/narrative2/BaseNarrativeGenerator.java index 1917356c6cd..fbc1f298143 100644 --- a/hapi-fhir-base/src/main/java/ca/uhn/fhir/narrative2/BaseNarrativeGenerator.java +++ b/hapi-fhir-base/src/main/java/ca/uhn/fhir/narrative2/BaseNarrativeGenerator.java @@ -28,6 +28,7 @@ import ca.uhn.fhir.i18n.Msg; import ca.uhn.fhir.narrative.INarrativeGenerator; import ca.uhn.fhir.rest.server.exceptions.InternalErrorException; import ca.uhn.fhir.util.Logs; +import jakarta.annotation.Nullable; import org.hl7.fhir.instance.model.api.IBase; import org.hl7.fhir.instance.model.api.IBaseResource; import org.hl7.fhir.instance.model.api.INarrative; @@ -37,7 +38,6 @@ import java.util.EnumSet; import java.util.List; import java.util.Set; import java.util.stream.Collectors; -import javax.annotation.Nullable; import static org.apache.commons.lang3.StringUtils.defaultIfEmpty; import static org.apache.commons.lang3.StringUtils.isNotBlank; diff --git a/hapi-fhir-base/src/main/java/ca/uhn/fhir/narrative2/INarrativeTemplateManifest.java b/hapi-fhir-base/src/main/java/ca/uhn/fhir/narrative2/INarrativeTemplateManifest.java index e12362bd861..db479e5b1b8 100644 --- a/hapi-fhir-base/src/main/java/ca/uhn/fhir/narrative2/INarrativeTemplateManifest.java +++ b/hapi-fhir-base/src/main/java/ca/uhn/fhir/narrative2/INarrativeTemplateManifest.java @@ -20,12 +20,12 @@ package ca.uhn.fhir.narrative2; import ca.uhn.fhir.context.FhirContext; +import jakarta.annotation.Nonnull; import org.hl7.fhir.instance.model.api.IBase; import java.util.Collection; import java.util.EnumSet; import java.util.List; -import javax.annotation.Nonnull; public interface INarrativeTemplateManifest { List getTemplateByResourceName( diff --git a/hapi-fhir-base/src/main/java/ca/uhn/fhir/narrative2/NarrativeTemplateManifest.java b/hapi-fhir-base/src/main/java/ca/uhn/fhir/narrative2/NarrativeTemplateManifest.java index 8152d66f19c..15af18e5c56 100644 --- a/hapi-fhir-base/src/main/java/ca/uhn/fhir/narrative2/NarrativeTemplateManifest.java +++ b/hapi-fhir-base/src/main/java/ca/uhn/fhir/narrative2/NarrativeTemplateManifest.java @@ -28,6 +28,7 @@ import com.google.common.base.Charsets; import com.google.common.collect.ArrayListMultimap; import com.google.common.collect.ListMultimap; import com.google.common.collect.Multimaps; +import jakarta.annotation.Nonnull; import org.apache.commons.io.IOUtils; import org.apache.commons.lang3.StringUtils; import org.apache.commons.lang3.Validate; @@ -41,10 +42,18 @@ import java.io.File; import java.io.FileInputStream; import java.io.IOException; import java.io.StringReader; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collection; +import java.util.Collections; +import java.util.EnumSet; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Objects; +import java.util.Properties; import java.util.function.Consumer; import java.util.stream.Collectors; -import java.util.*; -import javax.annotation.Nonnull; import static org.apache.commons.lang3.StringUtils.isNotBlank; diff --git a/hapi-fhir-base/src/main/java/ca/uhn/fhir/parser/BaseParser.java b/hapi-fhir-base/src/main/java/ca/uhn/fhir/parser/BaseParser.java index 831e5789257..93a8875e10d 100644 --- a/hapi-fhir-base/src/main/java/ca/uhn/fhir/parser/BaseParser.java +++ b/hapi-fhir-base/src/main/java/ca/uhn/fhir/parser/BaseParser.java @@ -45,6 +45,7 @@ import ca.uhn.fhir.util.BundleUtil; import ca.uhn.fhir.util.FhirTerser; import ca.uhn.fhir.util.UrlUtil; import com.google.common.base.Charsets; +import jakarta.annotation.Nullable; import org.apache.commons.io.output.StringBuilderWriter; import org.apache.commons.lang3.StringUtils; import org.apache.commons.lang3.Validate; @@ -78,7 +79,6 @@ import java.util.Map; import java.util.Objects; import java.util.Set; import java.util.stream.Collectors; -import javax.annotation.Nullable; import static org.apache.commons.lang3.StringUtils.isBlank; import static org.apache.commons.lang3.StringUtils.isNotBlank; diff --git a/hapi-fhir-base/src/main/java/ca/uhn/fhir/parser/JsonParser.java b/hapi-fhir-base/src/main/java/ca/uhn/fhir/parser/JsonParser.java index 846d5dab271..f82222b4ddb 100644 --- a/hapi-fhir-base/src/main/java/ca/uhn/fhir/parser/JsonParser.java +++ b/hapi-fhir-base/src/main/java/ca/uhn/fhir/parser/JsonParser.java @@ -39,7 +39,6 @@ import ca.uhn.fhir.model.api.ISupportsUndeclaredExtensions; import ca.uhn.fhir.model.api.ResourceMetadataKeyEnum; import ca.uhn.fhir.model.api.Tag; import ca.uhn.fhir.model.api.TagList; -import ca.uhn.fhir.model.api.annotation.Child; import ca.uhn.fhir.model.base.composite.BaseCodingDt; import ca.uhn.fhir.model.base.composite.BaseContainedDt; import ca.uhn.fhir.model.primitive.IdDt; @@ -659,9 +658,8 @@ public class JsonParser extends BaseParser implements IJsonLikeParser { theEventWriter.endArray(); } BaseRuntimeChildDefinition replacedParentDefinition = nextChild.getReplacedParentDefinition(); - if (isMultipleCardinality(nextChild.getMax()) - || (replacedParentDefinition != null - && isMultipleCardinality(replacedParentDefinition.getMax()))) { + if (nextChild.isMultipleCardinality() + || (replacedParentDefinition != null && replacedParentDefinition.isMultipleCardinality())) { beginArray(theEventWriter, nextChildSpecificName); inArray = true; encodeChildElementToStreamWriter( @@ -728,14 +726,14 @@ public class JsonParser extends BaseParser implements IJsonLikeParser { List heldModExts = Collections.emptyList(); if (extensions.size() > i && extensions.get(i) != null - && extensions.get(i).isEmpty() == false) { + && !extensions.get(i).isEmpty()) { haveContent = true; heldExts = extensions.get(i); } if (modifierExtensions.size() > i && modifierExtensions.get(i) != null - && modifierExtensions.get(i).isEmpty() == false) { + && !modifierExtensions.get(i).isEmpty()) { haveContent = true; heldModExts = modifierExtensions.get(i); } @@ -746,7 +744,7 @@ public class JsonParser extends BaseParser implements IJsonLikeParser { } else { nextComments = null; } - if (nextComments != null && nextComments.isEmpty() == false) { + if (nextComments != null && !nextComments.isEmpty()) { haveContent = true; } @@ -804,10 +802,6 @@ public class JsonParser extends BaseParser implements IJsonLikeParser { return myIsSupportsFhirComment; } - private boolean isMultipleCardinality(int maxCardinality) { - return maxCardinality > 1 || maxCardinality == Child.MAX_UNLIMITED; - } - private void encodeCompositeElementToStreamWriter( RuntimeResourceDefinition theResDef, IBaseResource theResource, @@ -917,10 +911,7 @@ public class JsonParser extends BaseParser implements IJsonLikeParser { // Undeclared extensions extractUndeclaredExtensions( theResourceId, extensions, modifierExtensions, null, null, theEncodeContext, theContainedResource); - boolean haveExtension = false; - if (!extensions.isEmpty()) { - haveExtension = true; - } + boolean haveExtension = !extensions.isEmpty(); if (theResourceId.hasFormatComment() || haveExtension) { beginObject(theEventWriter, "_id"); diff --git a/hapi-fhir-base/src/main/java/ca/uhn/fhir/parser/json/jackson/JacksonStructure.java b/hapi-fhir-base/src/main/java/ca/uhn/fhir/parser/json/jackson/JacksonStructure.java index 27d93057d0c..14b0831c301 100644 --- a/hapi-fhir-base/src/main/java/ca/uhn/fhir/parser/json/jackson/JacksonStructure.java +++ b/hapi-fhir-base/src/main/java/ca/uhn/fhir/parser/json/jackson/JacksonStructure.java @@ -28,6 +28,7 @@ import ca.uhn.fhir.parser.json.BaseJsonLikeWriter; import ca.uhn.fhir.parser.json.JsonLikeStructure; import com.fasterxml.jackson.core.JsonGenerator; import com.fasterxml.jackson.core.JsonParser; +import com.fasterxml.jackson.core.JsonProcessingException; import com.fasterxml.jackson.core.StreamReadConstraints; import com.fasterxml.jackson.databind.DeserializationFeature; import com.fasterxml.jackson.databind.JsonNode; @@ -114,14 +115,38 @@ public class JacksonStructure implements JsonLikeStructure { setNativeArray((ArrayNode) OBJECT_MAPPER.readTree(pbr)); } } catch (Exception e) { - if (e.getMessage().startsWith("Unexpected char 39")) { + String message; + if (e instanceof JsonProcessingException) { + /* + * Currently there is no way of preventing Jackson from adding this + * annoying REDACTED message from certain messages we get back from + * the parser, so we just manually strip them. Hopefully Jackson + * will accept this request at some point: + * https://github.com/FasterXML/jackson-core/issues/1158 + */ + JsonProcessingException jpe = (JsonProcessingException) e; + StringBuilder messageBuilder = new StringBuilder(); + String originalMessage = jpe.getOriginalMessage(); + originalMessage = originalMessage.replace( + "Source: REDACTED (`StreamReadFeature.INCLUDE_SOURCE_IN_LOCATION` disabled); ", ""); + messageBuilder.append(originalMessage); + if (jpe.getLocation() != null) { + messageBuilder.append("\n at ["); + jpe.getLocation().appendOffsetDescription(messageBuilder); + messageBuilder.append("]"); + } + message = messageBuilder.toString(); + } else { + message = e.getMessage(); + } + + if (message.startsWith("Unexpected char 39")) { throw new DataFormatException( - Msg.code(1860) + "Failed to parse JSON encoded FHIR content: " + e.getMessage() + " - " + Msg.code(1860) + "Failed to parse JSON encoded FHIR content: " + message + " - " + "This may indicate that single quotes are being used as JSON escapes where double quotes are required", e); } - throw new DataFormatException( - Msg.code(1861) + "Failed to parse JSON encoded FHIR content: " + e.getMessage(), e); + throw new DataFormatException(Msg.code(1861) + "Failed to parse JSON encoded FHIR content: " + message, e); } } diff --git a/hapi-fhir-base/src/main/java/ca/uhn/fhir/parser/json/jackson/JacksonWriter.java b/hapi-fhir-base/src/main/java/ca/uhn/fhir/parser/json/jackson/JacksonWriter.java index d0101d0af7c..1e4ba3af13c 100644 --- a/hapi-fhir-base/src/main/java/ca/uhn/fhir/parser/json/jackson/JacksonWriter.java +++ b/hapi-fhir-base/src/main/java/ca/uhn/fhir/parser/json/jackson/JacksonWriter.java @@ -45,29 +45,15 @@ public class JacksonWriter extends BaseJsonLikeWriter { @Override public BaseJsonLikeWriter init() { if (isPrettyPrint()) { - DefaultPrettyPrinter prettyPrinter = new DefaultPrettyPrinter() { - - /** - * Objects should serialize as - *
    -				 * {
    -				 *    "key": "value"
    -				 * }
    -				 * 
    - * in order to be consistent with Gson behaviour, instead of the jackson default - *
    -				 * {
    -				 *    "key" : "value"
    -				 * }
    -				 * 
    - */ - @Override - public DefaultPrettyPrinter withSeparators(Separators separators) { - _separators = separators; - _objectFieldValueSeparatorWithSpaces = separators.getObjectFieldValueSeparator() + " "; - return this; - } - }; + DefaultPrettyPrinter prettyPrinter = new DefaultPrettyPrinter() + .withSeparators(new Separators( + Separators.DEFAULT_ROOT_VALUE_SEPARATOR, + ':', + Separators.Spacing.AFTER, + ',', + Separators.Spacing.NONE, + ',', + Separators.Spacing.NONE)); prettyPrinter = prettyPrinter.withObjectIndenter(new DefaultIndenter(" ", "\n")); myJsonGenerator.setPrettyPrinter(prettyPrinter); diff --git a/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/annotation/Operation.java b/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/annotation/Operation.java index 83907544fc5..69a521a77ec 100644 --- a/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/annotation/Operation.java +++ b/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/annotation/Operation.java @@ -118,7 +118,7 @@ public @interface Operation { * always the right choice), the framework will not attempt to generate a response to * this method. *

    - * This is useful if you want to include an {@link javax.servlet.http.HttpServletResponse} + * This is useful if you want to include an {@link jakarta.servlet.http.HttpServletResponse} * in your method parameters and create a response yourself directly from your * @Operation method. *

    @@ -134,7 +134,7 @@ public @interface Operation { * always the right choice), the framework will not attempt to parse the request body, * but will instead delegate it to the @Operation method. *

    - * This is useful if you want to include an {@link javax.servlet.http.HttpServletRequest} + * This is useful if you want to include an {@link jakarta.servlet.http.HttpServletRequest} * in your method parameters and parse the request yourself. *

    */ diff --git a/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/annotation/Read.java b/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/annotation/Read.java index 4276229f630..aedd0656b36 100644 --- a/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/annotation/Read.java +++ b/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/annotation/Read.java @@ -23,7 +23,10 @@ import ca.uhn.fhir.rest.client.api.IBasicClient; import ca.uhn.fhir.rest.client.api.IRestfulClient; import org.hl7.fhir.instance.model.api.IBaseResource; -import java.lang.annotation.*; +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; /** * RESTful method annotation to be used for the FHIR read and transaction method. diff --git a/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/annotation/TransactionParam.java b/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/annotation/TransactionParam.java index 8e50b13c9bb..51539b5d384 100644 --- a/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/annotation/TransactionParam.java +++ b/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/annotation/TransactionParam.java @@ -19,7 +19,10 @@ */ package ca.uhn.fhir.rest.annotation; -import java.lang.annotation.*; +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; import java.util.List; /** diff --git a/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/annotation/Validate.java b/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/annotation/Validate.java index 7826df8daee..2ece6309e97 100644 --- a/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/annotation/Validate.java +++ b/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/annotation/Validate.java @@ -22,7 +22,10 @@ package ca.uhn.fhir.rest.annotation; import ca.uhn.fhir.rest.api.ValidationModeEnum; import org.hl7.fhir.instance.model.api.IBaseResource; -import java.lang.annotation.*; +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; /** * RESTful method annotation to be used for the FHIR diff --git a/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/api/Constants.java b/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/api/Constants.java index ab3faa6862d..00d0b90799d 100644 --- a/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/api/Constants.java +++ b/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/api/Constants.java @@ -199,6 +199,8 @@ public class Constants { public static final String PARAM_PRETTY_VALUE_FALSE = "false"; public static final String PARAM_PRETTY_VALUE_TRUE = "true"; public static final String PARAM_PROFILE = "_profile"; + public static final String PARAM_PID = "_pid"; + public static final String PARAM_QUERY = "_query"; public static final String PARAM_RESPONSE_URL = "response-url"; // Used in messaging public static final String PARAM_REVINCLUDE = "_revinclude"; @@ -218,21 +220,6 @@ public class Constants { public static final String PARAM_TEXT = "_text"; public static final String PARAM_VALIDATE = "_validate"; - /** - * $member-match operation - */ - public static final String PARAM_MEMBER_PATIENT = "MemberPatient"; - - public static final String PARAM_MEMBER_IDENTIFIER = "MemberIdentifier"; - - public static final String PARAM_OLD_COVERAGE = "OldCoverage"; - public static final String PARAM_NEW_COVERAGE = "NewCoverage"; - public static final String PARAM_CONSENT = "Consent"; - public static final String PARAM_MEMBER_PATIENT_NAME = PARAM_MEMBER_PATIENT + " Name"; - public static final String PARAM_MEMBER_PATIENT_BIRTHDATE = PARAM_MEMBER_PATIENT + " Birthdate"; - public static final String PARAM_CONSENT_PATIENT_REFERENCE = PARAM_CONSENT + "'s Patient Reference"; - public static final String PARAM_CONSENT_PERFORMER_REFERENCE = PARAM_CONSENT + "'s Performer Reference"; - public static final String PARAMQUALIFIER_MISSING = ":missing"; public static final String PARAMQUALIFIER_MISSING_FALSE = "false"; public static final String PARAMQUALIFIER_MISSING_TRUE = "true"; @@ -331,6 +318,9 @@ public class Constants { */ public static final int UUID_LENGTH = 36; + public static final String BULK_DATA_ACCESS_IG_URL = + "http://hl7.org/fhir/uv/bulkdata/CapabilityStatement/bulk-data"; + /** * Application configuration key used to enable or disable Hibernate Envers. */ diff --git a/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/api/IVersionSpecificBundleFactory.java b/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/api/IVersionSpecificBundleFactory.java index 946c8943fbf..8027b1e11bc 100644 --- a/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/api/IVersionSpecificBundleFactory.java +++ b/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/api/IVersionSpecificBundleFactory.java @@ -22,6 +22,8 @@ package ca.uhn.fhir.rest.api; import ca.uhn.fhir.context.api.BundleInclusionRule; import ca.uhn.fhir.model.api.Include; import ca.uhn.fhir.model.valueset.BundleTypeEnum; +import jakarta.annotation.Nonnull; +import jakarta.annotation.Nullable; import org.hl7.fhir.instance.model.api.IBaseResource; import org.hl7.fhir.instance.model.api.IPrimitiveType; @@ -29,8 +31,6 @@ import java.util.ArrayList; import java.util.Date; import java.util.List; import java.util.Set; -import javax.annotation.Nonnull; -import javax.annotation.Nullable; /** * This interface should be considered experimental and will likely change in future releases of HAPI. Use with caution! diff --git a/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/api/PatchTypeEnum.java b/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/api/PatchTypeEnum.java index ea4c764e044..b8383671d80 100644 --- a/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/api/PatchTypeEnum.java +++ b/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/api/PatchTypeEnum.java @@ -23,10 +23,10 @@ import ca.uhn.fhir.context.FhirContext; import ca.uhn.fhir.i18n.Msg; import ca.uhn.fhir.rest.annotation.Patch; import ca.uhn.fhir.rest.server.exceptions.InvalidRequestException; +import jakarta.annotation.Nonnull; import java.util.HashMap; import java.util.Map; -import javax.annotation.Nonnull; import static org.apache.commons.lang3.StringUtils.defaultString; import static org.apache.commons.lang3.StringUtils.isBlank; diff --git a/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/api/PreferHeader.java b/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/api/PreferHeader.java index 09d42fdce25..03f9a115215 100644 --- a/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/api/PreferHeader.java +++ b/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/api/PreferHeader.java @@ -19,7 +19,7 @@ */ package ca.uhn.fhir.rest.api; -import javax.annotation.Nullable; +import jakarta.annotation.Nullable; public class PreferHeader { diff --git a/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/api/PreferReturnEnum.java b/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/api/PreferReturnEnum.java index 07ab10f62c0..09a945ca651 100644 --- a/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/api/PreferReturnEnum.java +++ b/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/api/PreferReturnEnum.java @@ -19,8 +19,9 @@ */ package ca.uhn.fhir.rest.api; +import jakarta.annotation.Nullable; + import java.util.HashMap; -import javax.annotation.Nullable; /** * Represents values for "return" value as provided in the the HTTP Prefer header. diff --git a/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/api/RestOperationTypeEnum.java b/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/api/RestOperationTypeEnum.java index 9a37ca86aa2..cd864314b82 100644 --- a/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/api/RestOperationTypeEnum.java +++ b/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/api/RestOperationTypeEnum.java @@ -20,11 +20,11 @@ package ca.uhn.fhir.rest.api; import ca.uhn.fhir.util.CoverageIgnore; +import jakarta.annotation.Nonnull; import org.apache.commons.lang3.Validate; import java.util.HashMap; import java.util.Map; -import javax.annotation.Nonnull; @CoverageIgnore public enum RestOperationTypeEnum { diff --git a/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/client/api/ClientResponseContext.java b/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/client/api/ClientResponseContext.java new file mode 100644 index 00000000000..b0748bf2ab7 --- /dev/null +++ b/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/client/api/ClientResponseContext.java @@ -0,0 +1,103 @@ +/*- + * #%L + * HAPI FHIR - Core Library + * %% + * Copyright (C) 2014 - 2023 Smile CDR, Inc. + * %% + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * #L% + */ +package ca.uhn.fhir.rest.client.api; + +import ca.uhn.fhir.context.FhirContext; +import ca.uhn.fhir.interceptor.api.Pointcut; +import org.hl7.fhir.instance.model.api.IBaseResource; + +import java.util.Objects; +import java.util.StringJoiner; + +/** + * Used to pass context to {@link Pointcut#CLIENT_RESPONSE}, including a mutable {@link IHttpResponse} + */ +public class ClientResponseContext { + private final IHttpRequest myHttpRequest; + private IHttpResponse myHttpResponse; + private final IRestfulClient myRestfulClient; + private final FhirContext myFhirContext; + private final Class myReturnType; + + public ClientResponseContext( + IHttpRequest myHttpRequest, + IHttpResponse theHttpResponse, + IRestfulClient myRestfulClient, + FhirContext theFhirContext, + Class theReturnType) { + this.myHttpRequest = myHttpRequest; + this.myHttpResponse = theHttpResponse; + this.myRestfulClient = myRestfulClient; + this.myFhirContext = theFhirContext; + this.myReturnType = theReturnType; + } + + public IHttpRequest getHttpRequest() { + return myHttpRequest; + } + + public IHttpResponse getHttpResponse() { + return myHttpResponse; + } + + public IRestfulClient getRestfulClient() { + return myRestfulClient; + } + + public FhirContext getFhirContext() { + return myFhirContext; + } + + public Class getReturnType() { + return myReturnType; + } + + public void setHttpResponse(IHttpResponse theHttpResponse) { + this.myHttpResponse = theHttpResponse; + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + ClientResponseContext that = (ClientResponseContext) o; + return Objects.equals(myHttpRequest, that.myHttpRequest) + && Objects.equals(myHttpResponse, that.myHttpResponse) + && Objects.equals(myRestfulClient, that.myRestfulClient) + && Objects.equals(myFhirContext, that.myFhirContext) + && Objects.equals(myReturnType, that.myReturnType); + } + + @Override + public int hashCode() { + return Objects.hash(myHttpRequest, myHttpResponse, myRestfulClient, myFhirContext, myReturnType); + } + + @Override + public String toString() { + return new StringJoiner(", ", ClientResponseContext.class.getSimpleName() + "[", "]") + .add("myHttpRequest=" + myHttpRequest) + .add("myHttpResponse=" + myHttpResponse) + .add("myRestfulClient=" + myRestfulClient) + .add("myFhirContext=" + myFhirContext) + .add("myReturnType=" + myReturnType) + .toString(); + } +} diff --git a/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/client/api/IRestfulClient.java b/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/client/api/IRestfulClient.java index daec727f9f8..2f49f863fda 100644 --- a/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/client/api/IRestfulClient.java +++ b/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/client/api/IRestfulClient.java @@ -24,10 +24,9 @@ import ca.uhn.fhir.interceptor.api.IInterceptorService; import ca.uhn.fhir.rest.api.EncodingEnum; import ca.uhn.fhir.rest.api.RequestFormatParamStyleEnum; import ca.uhn.fhir.rest.api.SummaryEnum; +import jakarta.annotation.Nonnull; import org.hl7.fhir.instance.model.api.IBaseResource; -import javax.annotation.Nonnull; - public interface IRestfulClient { /** diff --git a/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/gclient/TokenClientParam.java b/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/gclient/TokenClientParam.java index 2f8a91f22f4..d51b427f271 100644 --- a/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/gclient/TokenClientParam.java +++ b/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/gclient/TokenClientParam.java @@ -23,7 +23,9 @@ import ca.uhn.fhir.model.base.composite.BaseIdentifierDt; import org.apache.commons.lang3.ObjectUtils; import org.hl7.fhir.instance.model.api.IBaseCoding; -import java.util.*; +import java.util.Arrays; +import java.util.Collection; +import java.util.List; import static org.apache.commons.lang3.StringUtils.defaultString; diff --git a/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/param/DateRangeParam.java b/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/param/DateRangeParam.java index c1192fe5f70..ce6c7c51776 100644 --- a/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/param/DateRangeParam.java +++ b/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/param/DateRangeParam.java @@ -27,6 +27,7 @@ import ca.uhn.fhir.parser.DataFormatException; import ca.uhn.fhir.rest.api.QualifiedParamList; import ca.uhn.fhir.rest.server.exceptions.InvalidRequestException; import ca.uhn.fhir.util.DateUtils; +import jakarta.annotation.Nonnull; import org.apache.commons.lang3.Validate; import org.hl7.fhir.instance.model.api.IPrimitiveType; @@ -34,7 +35,6 @@ import java.util.ArrayList; import java.util.Date; import java.util.List; import java.util.Objects; -import javax.annotation.Nonnull; import static ca.uhn.fhir.rest.param.ParamPrefixEnum.EQUAL; import static ca.uhn.fhir.rest.param.ParamPrefixEnum.GREATERTHAN_OR_EQUALS; diff --git a/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/param/ParamPrefixEnum.java b/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/param/ParamPrefixEnum.java index 3763c5292ae..56910ea563b 100644 --- a/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/param/ParamPrefixEnum.java +++ b/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/param/ParamPrefixEnum.java @@ -19,7 +19,9 @@ */ package ca.uhn.fhir.rest.param; -import java.util.*; +import java.util.Collections; +import java.util.HashMap; +import java.util.Map; /** * Comparator/qualifier for values used in REST params, such as {@link DateParam}, {@link NumberParam}, and diff --git a/hapi-fhir-base/src/main/java/ca/uhn/fhir/util/AttachmentUtil.java b/hapi-fhir-base/src/main/java/ca/uhn/fhir/util/AttachmentUtil.java index 9e7d82dd51d..cbb1adceb58 100644 --- a/hapi-fhir-base/src/main/java/ca/uhn/fhir/util/AttachmentUtil.java +++ b/hapi-fhir-base/src/main/java/ca/uhn/fhir/util/AttachmentUtil.java @@ -19,7 +19,11 @@ */ package ca.uhn.fhir.util; -import ca.uhn.fhir.context.*; +import ca.uhn.fhir.context.BaseRuntimeChildDefinition; +import ca.uhn.fhir.context.BaseRuntimeElementCompositeDefinition; +import ca.uhn.fhir.context.BaseRuntimeElementDefinition; +import ca.uhn.fhir.context.FhirContext; +import ca.uhn.fhir.context.FhirVersionEnum; import org.apache.commons.lang3.Validate; import org.hl7.fhir.instance.model.api.IBase; import org.hl7.fhir.instance.model.api.ICompositeType; diff --git a/hapi-fhir-base/src/main/java/ca/uhn/fhir/util/BundleBuilder.java b/hapi-fhir-base/src/main/java/ca/uhn/fhir/util/BundleBuilder.java index 4c2bd44fb72..a073f2a0dcf 100644 --- a/hapi-fhir-base/src/main/java/ca/uhn/fhir/util/BundleBuilder.java +++ b/hapi-fhir-base/src/main/java/ca/uhn/fhir/util/BundleBuilder.java @@ -25,6 +25,8 @@ import ca.uhn.fhir.context.FhirContext; import ca.uhn.fhir.context.FhirVersionEnum; import ca.uhn.fhir.context.RuntimeResourceDefinition; import ca.uhn.fhir.model.primitive.IdDt; +import jakarta.annotation.Nonnull; +import jakarta.annotation.Nullable; import org.apache.commons.lang3.Validate; import org.hl7.fhir.instance.model.api.IBase; import org.hl7.fhir.instance.model.api.IBaseBackboneElement; @@ -36,8 +38,6 @@ import org.hl7.fhir.instance.model.api.IPrimitiveType; import java.util.Date; import java.util.Objects; -import javax.annotation.Nonnull; -import javax.annotation.Nullable; /** * This class can be used to build a Bundle resource to be used as a FHIR transaction. Convenience methods provide @@ -196,11 +196,7 @@ public class BundleBuilder { public UpdateBuilder addTransactionUpdateEntry(IBaseResource theResource) { Validate.notNull(theResource, "theResource must not be null"); - IIdType id = theResource.getIdElement(); - if (id.hasIdPart() && !id.hasResourceType()) { - String resourceType = myContext.getResourceType(theResource); - id = id.withResourceType(resourceType); - } + IIdType id = getIdTypeForUpdate(theResource); String requestUrl = id.toUnqualifiedVersionless().getValue(); String fullUrl = id.getValue(); @@ -225,13 +221,29 @@ public class BundleBuilder { myEntryRequestUrlChild.getMutator().setValue(request, url); // Bundle.entry.request.method - IPrimitiveType method = (IPrimitiveType) - myEntryRequestMethodDef.newInstance(myEntryRequestMethodChild.getInstanceConstructorArguments()); - method.setValueAsString(theHttpVerb); - myEntryRequestMethodChild.getMutator().setValue(request, method); + addRequestMethod(request, theHttpVerb); return url; } + /** + * Adds an entry containing an update (UPDATE) request without the body of the resource. + * Also sets the Bundle.type value to "transaction" if it is not already set. + * + * @param theResource The resource to update. + */ + public void addTransactionUpdateIdOnlyEntry(IBaseResource theResource) { + setBundleField("type", "transaction"); + + Validate.notNull(theResource, "theResource must not be null"); + + IIdType id = getIdTypeForUpdate(theResource); + String requestUrl = id.toUnqualifiedVersionless().getValue(); + String fullUrl = id.getValue(); + String httpMethod = "PUT"; + + addIdOnlyEntry(requestUrl, httpMethod, fullUrl); + } + /** * Adds an entry containing an create (POST) request. * Also sets the Bundle.type value to "transaction" if it is not already set. @@ -247,20 +259,47 @@ public class BundleBuilder { String resourceType = myContext.getResourceType(theResource); // Bundle.entry.request.url - IPrimitiveType url = - (IPrimitiveType) myContext.getElementDefinition("uri").newInstance(); - url.setValueAsString(resourceType); - myEntryRequestUrlChild.getMutator().setValue(request, url); + addRequestUrl(request, resourceType); - // Bundle.entry.request.url - IPrimitiveType method = (IPrimitiveType) - myEntryRequestMethodDef.newInstance(myEntryRequestMethodChild.getInstanceConstructorArguments()); - method.setValueAsString("POST"); - myEntryRequestMethodChild.getMutator().setValue(request, method); + // Bundle.entry.request.method + addRequestMethod(request, "POST"); return new CreateBuilder(request); } + /** + * Adds an entry containing a create (POST) request without the body of the resource. + * Also sets the Bundle.type value to "transaction" if it is not already set. + * + * @param theResource The resource to create + */ + public void addTransactionCreateEntryIdOnly(IBaseResource theResource) { + setBundleField("type", "transaction"); + + String requestUrl = myContext.getResourceType(theResource); + String fullUrl = theResource.getIdElement().getValue(); + String httpMethod = "POST"; + + addIdOnlyEntry(requestUrl, httpMethod, fullUrl); + } + + private void addIdOnlyEntry(String theRequestUrl, String theHttpMethod, String theFullUrl) { + IBase entry = addEntry(); + + // Bundle.entry.request + IBase request = myEntryRequestDef.newInstance(); + myEntryRequestChild.getMutator().setValue(entry, request); + + // Bundle.entry.request.url + addRequestUrl(request, theRequestUrl); + + // Bundle.entry.request.method + addRequestMethod(request, theHttpMethod); + + // Bundle.entry.fullUrl + addFullUrl(entry, theFullUrl); + } + /** * Adds an entry containing a delete (DELETE) request. * Also sets the Bundle.type value to "transaction" if it is not already set. @@ -341,20 +380,44 @@ public class BundleBuilder { IBase request = addEntryAndReturnRequest(); // Bundle.entry.request.url - IPrimitiveType url = - (IPrimitiveType) myContext.getElementDefinition("uri").newInstance(); - url.setValueAsString(theDeleteUrl); - myEntryRequestUrlChild.getMutator().setValue(request, url); + addRequestUrl(request, theDeleteUrl); // Bundle.entry.request.method - IPrimitiveType method = (IPrimitiveType) - myEntryRequestMethodDef.newInstance(myEntryRequestMethodChild.getInstanceConstructorArguments()); - method.setValueAsString("DELETE"); - myEntryRequestMethodChild.getMutator().setValue(request, method); + addRequestMethod(request, "DELETE"); return new DeleteBuilder(); } + private IIdType getIdTypeForUpdate(IBaseResource theResource) { + IIdType id = theResource.getIdElement(); + if (id.hasIdPart() && !id.hasResourceType()) { + String resourceType = myContext.getResourceType(theResource); + id = id.withResourceType(resourceType); + } + return id; + } + + private void addFullUrl(IBase theEntry, String theFullUrl) { + IPrimitiveType fullUrl = + (IPrimitiveType) myContext.getElementDefinition("uri").newInstance(); + fullUrl.setValueAsString(theFullUrl); + myEntryFullUrlChild.getMutator().setValue(theEntry, fullUrl); + } + + private void addRequestUrl(IBase request, String theRequestUrl) { + IPrimitiveType url = + (IPrimitiveType) myContext.getElementDefinition("uri").newInstance(); + url.setValueAsString(theRequestUrl); + myEntryRequestUrlChild.getMutator().setValue(request, url); + } + + private void addRequestMethod(IBase theRequest, String theMethod) { + IPrimitiveType method = (IPrimitiveType) + myEntryRequestMethodDef.newInstance(myEntryRequestMethodChild.getInstanceConstructorArguments()); + method.setValueAsString(theMethod); + myEntryRequestMethodChild.getMutator().setValue(theRequest, method); + } + /** * Adds an entry for a Collection bundle type */ @@ -406,10 +469,7 @@ public class BundleBuilder { IBase entry = addEntry(); // Bundle.entry.fullUrl - IPrimitiveType fullUrl = - (IPrimitiveType) myContext.getElementDefinition("uri").newInstance(); - fullUrl.setValueAsString(theFullUrl); - myEntryFullUrlChild.getMutator().setValue(entry, fullUrl); + addFullUrl(entry, theFullUrl); // Bundle.entry.resource myEntryResourceChild.getMutator().setValue(entry, theResource); diff --git a/hapi-fhir-base/src/main/java/ca/uhn/fhir/util/BundleUtil.java b/hapi-fhir-base/src/main/java/ca/uhn/fhir/util/BundleUtil.java index 2ed1a3b47af..98fc0bfac63 100644 --- a/hapi-fhir-base/src/main/java/ca/uhn/fhir/util/BundleUtil.java +++ b/hapi-fhir-base/src/main/java/ca/uhn/fhir/util/BundleUtil.java @@ -35,10 +35,12 @@ import ca.uhn.fhir.util.bundle.EntryListAccumulator; import ca.uhn.fhir.util.bundle.ModifiableBundleEntry; import ca.uhn.fhir.util.bundle.SearchBundleEntryParts; import com.google.common.collect.Sets; +import jakarta.annotation.Nonnull; import org.apache.commons.lang3.tuple.Pair; import org.hl7.fhir.instance.model.api.IBase; import org.hl7.fhir.instance.model.api.IBaseBinary; import org.hl7.fhir.instance.model.api.IBaseBundle; +import org.hl7.fhir.instance.model.api.IBaseReference; import org.hl7.fhir.instance.model.api.IBaseResource; import org.hl7.fhir.instance.model.api.IPrimitiveType; import org.slf4j.Logger; @@ -693,6 +695,25 @@ public class BundleUtil { return bundleEntry; } + /** + * Get resource from bundle by resource type and reference + * @param theContext FhirContext + * @param theBundle IBaseBundle + * @param theReference IBaseReference + * @return IBaseResource if found and null if not found. + */ + @Nonnull + public static IBaseResource getResourceByReferenceAndResourceType( + @Nonnull FhirContext theContext, @Nonnull IBaseBundle theBundle, @Nonnull IBaseReference theReference) { + return toListOfResources(theContext, theBundle).stream() + .filter(theResource -> theReference + .getReferenceElement() + .getIdPart() + .equals(theResource.getIdElement().getIdPart())) + .findFirst() + .orElse(null); + } + private static class SortLegality { private boolean myIsLegal; diff --git a/hapi-fhir-base/src/main/java/ca/uhn/fhir/util/ClasspathUtil.java b/hapi-fhir-base/src/main/java/ca/uhn/fhir/util/ClasspathUtil.java index f22e114484e..03a81448288 100644 --- a/hapi-fhir-base/src/main/java/ca/uhn/fhir/util/ClasspathUtil.java +++ b/hapi-fhir-base/src/main/java/ca/uhn/fhir/util/ClasspathUtil.java @@ -24,6 +24,7 @@ import ca.uhn.fhir.i18n.Msg; import ca.uhn.fhir.rest.api.EncodingEnum; import ca.uhn.fhir.rest.server.exceptions.InternalErrorException; import com.google.common.base.Charsets; +import jakarta.annotation.Nonnull; import org.apache.commons.io.IOUtils; import org.apache.commons.io.input.BOMInputStream; import org.hl7.fhir.instance.model.api.IBaseResource; @@ -37,7 +38,6 @@ import java.io.Reader; import java.nio.charset.StandardCharsets; import java.util.function.Function; import java.util.zip.GZIPInputStream; -import javax.annotation.Nonnull; /** * Use this API with caution, it may change! diff --git a/hapi-fhir-base/src/main/java/ca/uhn/fhir/util/CompositionBuilder.java b/hapi-fhir-base/src/main/java/ca/uhn/fhir/util/CompositionBuilder.java index 2cd02c96e09..a20c383a2be 100644 --- a/hapi-fhir-base/src/main/java/ca/uhn/fhir/util/CompositionBuilder.java +++ b/hapi-fhir-base/src/main/java/ca/uhn/fhir/util/CompositionBuilder.java @@ -21,6 +21,7 @@ package ca.uhn.fhir.util; import ca.uhn.fhir.context.FhirContext; import ca.uhn.fhir.context.RuntimeResourceDefinition; +import jakarta.annotation.Nonnull; import org.hl7.fhir.instance.model.api.IBase; import org.hl7.fhir.instance.model.api.IBaseCoding; import org.hl7.fhir.instance.model.api.IBaseReference; @@ -29,7 +30,6 @@ import org.hl7.fhir.instance.model.api.IIdType; import org.hl7.fhir.instance.model.api.IPrimitiveType; import java.util.Date; -import javax.annotation.Nonnull; /** * This class can be used to generate Composition resources in diff --git a/hapi-fhir-base/src/main/java/ca/uhn/fhir/util/DateRangeUtil.java b/hapi-fhir-base/src/main/java/ca/uhn/fhir/util/DateRangeUtil.java index 6e488c39a2d..6204c5d1897 100644 --- a/hapi-fhir-base/src/main/java/ca/uhn/fhir/util/DateRangeUtil.java +++ b/hapi-fhir-base/src/main/java/ca/uhn/fhir/util/DateRangeUtil.java @@ -20,10 +20,10 @@ package ca.uhn.fhir.util; import ca.uhn.fhir.rest.param.DateRangeParam; +import jakarta.annotation.Nonnull; +import jakarta.annotation.Nullable; import java.util.Date; -import javax.annotation.Nonnull; -import javax.annotation.Nullable; public class DateRangeUtil { diff --git a/hapi-fhir-base/src/main/java/ca/uhn/fhir/util/ExtensionUtil.java b/hapi-fhir-base/src/main/java/ca/uhn/fhir/util/ExtensionUtil.java index 074fbac22f3..9b100ec1309 100644 --- a/hapi-fhir-base/src/main/java/ca/uhn/fhir/util/ExtensionUtil.java +++ b/hapi-fhir-base/src/main/java/ca/uhn/fhir/util/ExtensionUtil.java @@ -21,6 +21,7 @@ package ca.uhn.fhir.util; import ca.uhn.fhir.context.FhirContext; import ca.uhn.fhir.i18n.Msg; +import jakarta.annotation.Nonnull; import org.apache.commons.lang3.StringUtils; import org.apache.commons.lang3.Validate; import org.hl7.fhir.instance.model.api.IBase; @@ -33,7 +34,6 @@ import java.util.List; import java.util.Optional; import java.util.function.Predicate; import java.util.stream.Collectors; -import javax.annotation.Nonnull; /** * Utility for modifying with extensions in a FHIR version-independent approach. diff --git a/hapi-fhir-base/src/main/java/ca/uhn/fhir/util/FhirTerser.java b/hapi-fhir-base/src/main/java/ca/uhn/fhir/util/FhirTerser.java index 4e4319076c3..9b812de7198 100644 --- a/hapi-fhir-base/src/main/java/ca/uhn/fhir/util/FhirTerser.java +++ b/hapi-fhir-base/src/main/java/ca/uhn/fhir/util/FhirTerser.java @@ -42,6 +42,8 @@ import ca.uhn.fhir.model.primitive.IdDt; import ca.uhn.fhir.model.primitive.StringDt; import ca.uhn.fhir.parser.DataFormatException; import com.google.common.collect.Lists; +import jakarta.annotation.Nonnull; +import jakarta.annotation.Nullable; import org.apache.commons.lang3.StringUtils; import org.apache.commons.lang3.Validate; import org.hl7.fhir.instance.model.api.IBase; @@ -71,8 +73,6 @@ import java.util.Set; import java.util.regex.Matcher; import java.util.regex.Pattern; import java.util.stream.Collectors; -import javax.annotation.Nonnull; -import javax.annotation.Nullable; import static org.apache.commons.lang3.StringUtils.defaultString; import static org.apache.commons.lang3.StringUtils.isBlank; @@ -287,9 +287,33 @@ public class FhirTerser { return retVal; } + /** + * Extracts all outbound references from a resource + * + * @param theResource the resource to be analyzed + * @return a list of references to other resources + */ public List getAllResourceReferences(final IBaseResource theResource) { + return getAllResourceReferencesExcluding(theResource, Lists.newArrayList()); + } + + /** + * Extracts all outbound references from a resource, excluding any that are located on black-listed parts of the + * resource + * + * @param theResource the resource to be analyzed + * @param thePathsToExclude a list of dot-delimited paths not to include in the result + * @return a list of references to other resources + */ + public List getAllResourceReferencesExcluding( + final IBaseResource theResource, List thePathsToExclude) { final ArrayList retVal = new ArrayList<>(); BaseRuntimeElementCompositeDefinition def = myContext.getResourceDefinition(theResource); + List> tokenizedPathsToExclude = thePathsToExclude.stream() + .map(path -> StringUtils.split(path, ".")) + .map(Lists::newArrayList) + .collect(Collectors.toList()); + visit(newMap(), theResource, theResource, null, null, def, new IModelVisitor() { @Override public void acceptElement( @@ -301,6 +325,10 @@ public class FhirTerser { if (theElement == null || theElement.isEmpty()) { return; } + + if (thePathToElement != null && pathShouldBeExcluded(tokenizedPathsToExclude, thePathToElement)) { + return; + } if (IBaseReference.class.isAssignableFrom(theElement.getClass())) { retVal.add(new ResourceReferenceInfo( myContext, theOuterResource, thePathToElement, (IBaseReference) theElement)); @@ -310,6 +338,19 @@ public class FhirTerser { return retVal; } + private boolean pathShouldBeExcluded(List> theTokenizedPathsToExclude, List thePathToElement) { + return theTokenizedPathsToExclude.stream().anyMatch(p -> { + // Check whether the path to the element starts with the path to be excluded + if (p.size() > thePathToElement.size()) { + return false; + } + + List prefix = thePathToElement.subList(0, p.size()); + + return Objects.equals(p, prefix); + }); + } + private BaseRuntimeChildDefinition getDefinition( BaseRuntimeElementCompositeDefinition theCurrentDef, List theSubList) { BaseRuntimeChildDefinition nextDef = theCurrentDef.getChildByNameOrThrowDataFormatException(theSubList.get(0)); diff --git a/hapi-fhir-base/src/main/java/ca/uhn/fhir/util/JsonUtil.java b/hapi-fhir-base/src/main/java/ca/uhn/fhir/util/JsonUtil.java index f02d60eecf4..0110dbba6f1 100644 --- a/hapi-fhir-base/src/main/java/ca/uhn/fhir/util/JsonUtil.java +++ b/hapi-fhir-base/src/main/java/ca/uhn/fhir/util/JsonUtil.java @@ -27,12 +27,12 @@ import com.fasterxml.jackson.annotation.JsonInclude; import com.fasterxml.jackson.core.JsonProcessingException; import com.fasterxml.jackson.databind.ObjectMapper; import com.fasterxml.jackson.databind.SerializationFeature; +import jakarta.annotation.Nonnull; import java.io.IOException; import java.io.StringWriter; import java.io.Writer; import java.util.List; -import javax.annotation.Nonnull; public class JsonUtil { diff --git a/hapi-fhir-base/src/main/java/ca/uhn/fhir/util/Logs.java b/hapi-fhir-base/src/main/java/ca/uhn/fhir/util/Logs.java index 99a9cf6dee2..2eb220ab03f 100644 --- a/hapi-fhir-base/src/main/java/ca/uhn/fhir/util/Logs.java +++ b/hapi-fhir-base/src/main/java/ca/uhn/fhir/util/Logs.java @@ -28,6 +28,9 @@ public class Logs { private static final Logger ourNarrativeGenerationTroubleshootingLog = LoggerFactory.getLogger("ca.uhn.fhir.log.narrative_generation_troubleshooting"); + private static final Logger ourTerminologyTroubleshootingLog = + LoggerFactory.getLogger("ca.uhn.fhir.log.terminology_troubleshooting"); + private static final Logger ourSubscriptionTroubleshootingLog = LoggerFactory.getLogger("ca.cdr.log.subscription_troubleshooting"); @@ -42,6 +45,10 @@ public class Logs { return ourBatchTroubleshootingLog; } + public static Logger getTerminologyTroubleshootingLog() { + return ourTerminologyTroubleshootingLog; + } + public static Logger getSubscriptionTroubleshootingLog() { return ourSubscriptionTroubleshootingLog; } diff --git a/hapi-fhir-base/src/main/java/ca/uhn/fhir/util/OperationOutcomeUtil.java b/hapi-fhir-base/src/main/java/ca/uhn/fhir/util/OperationOutcomeUtil.java index eadf61d44f2..ddcdf5ff20c 100644 --- a/hapi-fhir-base/src/main/java/ca/uhn/fhir/util/OperationOutcomeUtil.java +++ b/hapi-fhir-base/src/main/java/ca/uhn/fhir/util/OperationOutcomeUtil.java @@ -28,6 +28,7 @@ import ca.uhn.fhir.context.RuntimeResourceDefinition; import ca.uhn.fhir.i18n.Msg; import ca.uhn.fhir.rest.api.Constants; import ca.uhn.fhir.rest.server.exceptions.InternalErrorException; +import jakarta.annotation.Nullable; import org.hl7.fhir.instance.model.api.IBase; import org.hl7.fhir.instance.model.api.IBaseCoding; import org.hl7.fhir.instance.model.api.IBaseOperationOutcome; @@ -36,7 +37,6 @@ import org.hl7.fhir.instance.model.api.ICompositeType; import org.hl7.fhir.instance.model.api.IPrimitiveType; import java.util.List; -import javax.annotation.Nullable; import static org.apache.commons.lang3.StringUtils.isNotBlank; diff --git a/hapi-fhir-base/src/main/java/ca/uhn/fhir/util/ParametersUtil.java b/hapi-fhir-base/src/main/java/ca/uhn/fhir/util/ParametersUtil.java index 44d26280759..da99474dbe5 100644 --- a/hapi-fhir-base/src/main/java/ca/uhn/fhir/util/ParametersUtil.java +++ b/hapi-fhir-base/src/main/java/ca/uhn/fhir/util/ParametersUtil.java @@ -27,6 +27,7 @@ import ca.uhn.fhir.context.RuntimeResourceDefinition; import ca.uhn.fhir.i18n.Msg; import ca.uhn.fhir.model.api.annotation.Description; import ca.uhn.fhir.model.primitive.StringDt; +import jakarta.annotation.Nullable; import org.apache.commons.lang3.Validate; import org.hl7.fhir.instance.model.api.IBase; import org.hl7.fhir.instance.model.api.IBaseDatatype; @@ -45,7 +46,6 @@ import java.util.List; import java.util.Optional; import java.util.function.Function; import java.util.stream.Collectors; -import javax.annotation.Nullable; import static org.apache.commons.lang3.StringUtils.defaultIfBlank; import static org.apache.commons.lang3.StringUtils.isBlank; @@ -403,8 +403,15 @@ public class ParametersUtil { public static void addPartDecimal(FhirContext theContext, IBase theParameter, String theName, Double theValue) { IPrimitiveType value = (IPrimitiveType) theContext.getElementDefinition("decimal").newInstance(); - value.setValue(theValue == null ? null : BigDecimal.valueOf(theValue)); - + if (theValue == null) { + value.setValue(null); + } else { + BigDecimal decimalValue = BigDecimal.valueOf(theValue); + if (decimalValue.scale() < 0) { + decimalValue = decimalValue.setScale(0); + } + value.setValue(decimalValue); + } addPart(theContext, theParameter, theName, value); } diff --git a/hapi-fhir-base/src/main/java/ca/uhn/fhir/util/ReflectionUtil.java b/hapi-fhir-base/src/main/java/ca/uhn/fhir/util/ReflectionUtil.java index 0da15e2c571..ea691ac14e7 100644 --- a/hapi-fhir-base/src/main/java/ca/uhn/fhir/util/ReflectionUtil.java +++ b/hapi-fhir-base/src/main/java/ca/uhn/fhir/util/ReflectionUtil.java @@ -22,6 +22,7 @@ package ca.uhn.fhir.util; import ca.uhn.fhir.context.ConfigurationException; import ca.uhn.fhir.i18n.Msg; import ca.uhn.fhir.rest.server.exceptions.InternalErrorException; +import jakarta.annotation.Nonnull; import org.apache.commons.lang3.Validate; import java.lang.reflect.Constructor; @@ -39,7 +40,6 @@ import java.util.Comparator; import java.util.HashMap; import java.util.List; import java.util.concurrent.ConcurrentHashMap; -import javax.annotation.Nonnull; public class ReflectionUtil { diff --git a/hapi-fhir-base/src/main/java/ca/uhn/fhir/util/SearchParameterUtil.java b/hapi-fhir-base/src/main/java/ca/uhn/fhir/util/SearchParameterUtil.java index 9f5f9c0c32e..8ff4e8596dc 100644 --- a/hapi-fhir-base/src/main/java/ca/uhn/fhir/util/SearchParameterUtil.java +++ b/hapi-fhir-base/src/main/java/ca/uhn/fhir/util/SearchParameterUtil.java @@ -24,6 +24,7 @@ import ca.uhn.fhir.context.FhirContext; import ca.uhn.fhir.context.RuntimeResourceDefinition; import ca.uhn.fhir.context.RuntimeSearchParam; import ca.uhn.fhir.i18n.Msg; +import jakarta.annotation.Nullable; import org.apache.commons.lang3.Validate; import org.hl7.fhir.instance.model.api.IBase; import org.hl7.fhir.instance.model.api.IBaseResource; @@ -34,7 +35,6 @@ import java.util.List; import java.util.Optional; import java.util.Set; import java.util.stream.Collectors; -import javax.annotation.Nullable; public class SearchParameterUtil { diff --git a/hapi-fhir-base/src/main/java/ca/uhn/fhir/util/StreamUtil.java b/hapi-fhir-base/src/main/java/ca/uhn/fhir/util/StreamUtil.java new file mode 100644 index 00000000000..97a8ff4ed43 --- /dev/null +++ b/hapi-fhir-base/src/main/java/ca/uhn/fhir/util/StreamUtil.java @@ -0,0 +1,57 @@ +/*- + * #%L + * HAPI FHIR - Core Library + * %% + * Copyright (C) 2014 - 2023 Smile CDR, Inc. + * %% + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * #L% + */ +package ca.uhn.fhir.util; + +import com.google.common.collect.Iterators; +import com.google.common.collect.UnmodifiableIterator; + +import java.util.Iterator; +import java.util.List; +import java.util.Spliterator; +import java.util.Spliterators; +import java.util.stream.Stream; +import java.util.stream.StreamSupport; + +public class StreamUtil { + /** Static util class */ + private StreamUtil() {} + + /** + * Chunk the stream into Lists of size theChunkSize. + * The last chunk will be smaller unless the stream size is evenly divisible. + * Closes the underlying stream when done. + * + * @param theStream the input stream + * @param theChunkSize the chunk size. + * @return a stream of chunks + */ + public static Stream> partition(Stream theStream, int theChunkSize) { + Spliterator spliterator = theStream.spliterator(); + Iterator iterator = Spliterators.iterator(spliterator); + UnmodifiableIterator> partition = Iterators.partition(iterator, theChunkSize); + + // we could be fancier here and support parallel, and sizes; but serial-only is fine for now. + Spliterator> partitionedSpliterator = Spliterators.spliteratorUnknownSize(partition, 0); + Stream> result = StreamSupport.stream(partitionedSpliterator, false); + + // we lose close() via the Iterator. Add it back. + return result.onClose(theStream::close); + } +} diff --git a/hapi-fhir-base/src/main/java/ca/uhn/fhir/util/StringUtil.java b/hapi-fhir-base/src/main/java/ca/uhn/fhir/util/StringUtil.java index 34b092ced88..d3ad98daed7 100644 --- a/hapi-fhir-base/src/main/java/ca/uhn/fhir/util/StringUtil.java +++ b/hapi-fhir-base/src/main/java/ca/uhn/fhir/util/StringUtil.java @@ -19,11 +19,12 @@ */ package ca.uhn.fhir.util; +import jakarta.annotation.Nonnull; + import java.io.CharArrayWriter; import java.nio.charset.StandardCharsets; import java.text.Normalizer; import java.util.Arrays; -import javax.annotation.Nonnull; public class StringUtil { diff --git a/hapi-fhir-base/src/main/java/ca/uhn/fhir/util/TerserUtil.java b/hapi-fhir-base/src/main/java/ca/uhn/fhir/util/TerserUtil.java index 11b416fdbbf..434e085fb95 100644 --- a/hapi-fhir-base/src/main/java/ca/uhn/fhir/util/TerserUtil.java +++ b/hapi-fhir-base/src/main/java/ca/uhn/fhir/util/TerserUtil.java @@ -348,7 +348,30 @@ public final class TerserUtil { public static void clearField(FhirContext theFhirContext, IBaseResource theResource, String theFieldName) { BaseRuntimeChildDefinition childDefinition = getBaseRuntimeChildDefinition(theFhirContext, theFieldName, theResource); - clear(childDefinition.getAccessor().getValues(theResource)); + childDefinition.getMutator().setValue(theResource, null); + } + + /** + * Clears the specified field on the resource provided by the FHIRPath. If more than one value matches + * the FHIRPath, all values will be cleared. + * + * @param theFhirContext + * @param theResource + * @param theFhirPath + */ + public static void clearFieldByFhirPath(FhirContext theFhirContext, IBaseResource theResource, String theFhirPath) { + + if (theFhirPath.contains(".")) { + String parentPath = theFhirPath.substring(0, theFhirPath.lastIndexOf(".")); + String fieldName = theFhirPath.substring(theFhirPath.lastIndexOf(".") + 1); + FhirTerser terser = theFhirContext.newTerser(); + List parents = terser.getValues(theResource, parentPath); + for (IBase parent : parents) { + clearField(theFhirContext, fieldName, parent); + } + } else { + clearField(theFhirContext, theResource, theFhirPath); + } } /** @@ -362,7 +385,16 @@ public final class TerserUtil { BaseRuntimeElementDefinition definition = theFhirContext.getElementDefinition(theBase.getClass()); BaseRuntimeChildDefinition childDefinition = definition.getChildByName(theFieldName); Validate.notNull(childDefinition); - clear(childDefinition.getAccessor().getValues(theBase)); + BaseRuntimeChildDefinition.IAccessor accessor = childDefinition.getAccessor(); + clear(accessor.getValues(theBase)); + List newValue = accessor.getValues(theBase); + + if (newValue != null && !newValue.isEmpty()) { + // Our clear failed, probably because it was an immutable SingletonList returned by a FieldPlainAccessor + // that cannot be cleared. + // Let's just null it out instead. + childDefinition.getMutator().setValue(theBase, null); + } } /** diff --git a/hapi-fhir-base/src/main/java/ca/uhn/fhir/util/UrlUtil.java b/hapi-fhir-base/src/main/java/ca/uhn/fhir/util/UrlUtil.java index bca08865ebc..34d4650582e 100644 --- a/hapi-fhir-base/src/main/java/ca/uhn/fhir/util/UrlUtil.java +++ b/hapi-fhir-base/src/main/java/ca/uhn/fhir/util/UrlUtil.java @@ -28,6 +28,8 @@ import ca.uhn.fhir.rest.api.Constants; import ca.uhn.fhir.rest.server.exceptions.InvalidRequestException; import com.google.common.escape.Escaper; import com.google.common.net.PercentEscaper; +import jakarta.annotation.Nonnull; +import jakarta.annotation.Nullable; import org.apache.commons.lang3.StringUtils; import org.apache.http.NameValuePair; import org.apache.http.client.utils.URLEncodedUtils; @@ -50,8 +52,6 @@ import java.util.Map; import java.util.Map.Entry; import java.util.StringTokenizer; import java.util.stream.Collectors; -import javax.annotation.Nonnull; -import javax.annotation.Nullable; import static org.apache.commons.lang3.StringUtils.defaultIfBlank; import static org.apache.commons.lang3.StringUtils.defaultString; diff --git a/hapi-fhir-base/src/main/java/ca/uhn/fhir/util/VersionEnum.java b/hapi-fhir-base/src/main/java/ca/uhn/fhir/util/VersionEnum.java index 0cf304f0c56..a923b31f050 100644 --- a/hapi-fhir-base/src/main/java/ca/uhn/fhir/util/VersionEnum.java +++ b/hapi-fhir-base/src/main/java/ca/uhn/fhir/util/VersionEnum.java @@ -123,8 +123,15 @@ public enum VersionEnum { V6_8_0, V6_8_1, V6_8_2, + V6_8_3, + V6_8_4, + V6_8_5, V6_9_0, + V6_10_0, + V6_10_1, + V6_11_0, + V7_0_0; public static VersionEnum latestVersion() { diff --git a/hapi-fhir-base/src/main/java/ca/uhn/fhir/util/rdf/RDFUtil.java b/hapi-fhir-base/src/main/java/ca/uhn/fhir/util/rdf/RDFUtil.java index c1678236b7d..3f4cdc49df6 100644 --- a/hapi-fhir-base/src/main/java/ca/uhn/fhir/util/rdf/RDFUtil.java +++ b/hapi-fhir-base/src/main/java/ca/uhn/fhir/util/rdf/RDFUtil.java @@ -24,7 +24,8 @@ import org.apache.jena.rdf.model.ModelFactory; import org.apache.jena.riot.Lang; import org.apache.jena.riot.RDFDataMgr; -import java.io.*; +import java.io.Reader; +import java.io.Writer; public class RDFUtil { diff --git a/hapi-fhir-base/src/main/java/ca/uhn/fhir/validation/IValidationContext.java b/hapi-fhir-base/src/main/java/ca/uhn/fhir/validation/IValidationContext.java index e24720f8ede..4e0347f11c6 100644 --- a/hapi-fhir-base/src/main/java/ca/uhn/fhir/validation/IValidationContext.java +++ b/hapi-fhir-base/src/main/java/ca/uhn/fhir/validation/IValidationContext.java @@ -21,9 +21,9 @@ package ca.uhn.fhir.validation; import ca.uhn.fhir.context.FhirContext; import ca.uhn.fhir.rest.api.EncodingEnum; +import jakarta.annotation.Nonnull; import java.util.List; -import javax.annotation.Nonnull; public interface IValidationContext { diff --git a/hapi-fhir-base/src/main/java/ca/uhn/fhir/validation/ValidationContext.java b/hapi-fhir-base/src/main/java/ca/uhn/fhir/validation/ValidationContext.java index 1756e7fd124..ac7f35e844d 100644 --- a/hapi-fhir-base/src/main/java/ca/uhn/fhir/validation/ValidationContext.java +++ b/hapi-fhir-base/src/main/java/ca/uhn/fhir/validation/ValidationContext.java @@ -26,11 +26,11 @@ import ca.uhn.fhir.parser.LenientErrorHandler; import ca.uhn.fhir.rest.api.EncodingEnum; import ca.uhn.fhir.rest.server.exceptions.InvalidRequestException; import ca.uhn.fhir.util.ObjectUtil; +import jakarta.annotation.Nonnull; import org.hl7.fhir.instance.model.api.IBaseResource; import java.util.ArrayList; import java.util.List; -import javax.annotation.Nonnull; import static org.apache.commons.lang3.ObjectUtils.defaultIfNull; diff --git a/hapi-fhir-base/src/main/java/ca/uhn/fhir/validation/schematron/SchematronBaseValidator.java b/hapi-fhir-base/src/main/java/ca/uhn/fhir/validation/schematron/SchematronBaseValidator.java index 9355952873b..bd90fb8ca6a 100644 --- a/hapi-fhir-base/src/main/java/ca/uhn/fhir/validation/schematron/SchematronBaseValidator.java +++ b/hapi-fhir-base/src/main/java/ca/uhn/fhir/validation/schematron/SchematronBaseValidator.java @@ -37,8 +37,8 @@ import com.helger.commons.io.resource.ClassPathResource; import com.helger.commons.io.resource.IReadableResource; import com.helger.schematron.ISchematronResource; import com.helger.schematron.SchematronHelper; +import com.helger.schematron.sch.SchematronResourceSCH; import com.helger.schematron.svrl.jaxb.SchematronOutputType; -import com.helger.schematron.xslt.SchematronResourceSCH; import org.hl7.fhir.instance.model.api.IBaseBundle; import org.hl7.fhir.instance.model.api.IBaseResource; import org.slf4j.Logger; @@ -90,7 +90,12 @@ public class SchematronBaseValidator implements IValidatorModule { } StreamSource source = new StreamSource(new StringReader(resourceAsString)); - SchematronOutputType results = SchematronHelper.applySchematron(sch, source); + SchematronOutputType results; + try { + results = sch.applySchematronValidationToSVRL(source); + } catch (Exception e) { + throw new InternalErrorException(Msg.code(2433) + e.getMessage(), e); + } if (results == null) { return; } diff --git a/hapi-fhir-base/src/main/java/org/hl7/fhir/instance/model/api/IPrimitiveType.java b/hapi-fhir-base/src/main/java/org/hl7/fhir/instance/model/api/IPrimitiveType.java index 902db207996..ce3ae543536 100644 --- a/hapi-fhir-base/src/main/java/org/hl7/fhir/instance/model/api/IPrimitiveType.java +++ b/hapi-fhir-base/src/main/java/org/hl7/fhir/instance/model/api/IPrimitiveType.java @@ -19,7 +19,7 @@ */ package org.hl7.fhir.instance.model.api; -import javax.annotation.Nullable; +import jakarta.annotation.Nullable; public interface IPrimitiveType extends IBaseDatatype { diff --git a/hapi-fhir-base/src/main/resources/ca/uhn/fhir/i18n/hapi-messages.properties b/hapi-fhir-base/src/main/resources/ca/uhn/fhir/i18n/hapi-messages.properties index f0375733e44..fa53cd9effe 100644 --- a/hapi-fhir-base/src/main/resources/ca/uhn/fhir/i18n/hapi-messages.properties +++ b/hapi-fhir-base/src/main/resources/ca/uhn/fhir/i18n/hapi-messages.properties @@ -206,10 +206,3 @@ ca.uhn.fhir.jpa.provider.DiffProvider.cantDiffDifferentTypes=Unable to diff two ca.uhn.fhir.jpa.interceptor.validation.RuleRequireProfileDeclaration.noMatchingProfile=Resource of type "{0}" does not declare conformance to profile from: {1} ca.uhn.fhir.jpa.interceptor.validation.RuleRequireProfileDeclaration.illegalProfile=Resource of type "{0}" must not declare conformance to profile: {1} - -operation.member.match.error.coverage.not.found=Could not find coverage for member based on coverage id or coverage identifier. -operation.member.match.error.beneficiary.not.found=Could not find beneficiary for coverage. -operation.member.match.error.missing.parameter=Parameter "{0}" is required. -operation.member.match.error.beneficiary.without.identifier=Coverage beneficiary does not have an identifier. -operation.member.match.error.patient.not.found=Could not find matching patient for coverage. -operation.member.match.error.consent.release.data.mismatch=Consent policy does not match the data release segmentation capabilities. diff --git a/hapi-fhir-base/src/test/java/ca/uhn/fhir/i18n/HapiLocalizerTest.java b/hapi-fhir-base/src/test/java/ca/uhn/fhir/i18n/HapiLocalizerTest.java index 29857f00522..654d40aa675 100644 --- a/hapi-fhir-base/src/test/java/ca/uhn/fhir/i18n/HapiLocalizerTest.java +++ b/hapi-fhir-base/src/test/java/ca/uhn/fhir/i18n/HapiLocalizerTest.java @@ -1,12 +1,14 @@ package ca.uhn.fhir.i18n; -import static org.hamcrest.Matchers.*; -import static org.junit.jupiter.api.Assertions.assertEquals; -import static org.hamcrest.MatcherAssert.assertThat; +import org.junit.jupiter.api.Test; import java.util.Set; -import org.junit.jupiter.api.Test; +import static org.hamcrest.MatcherAssert.assertThat; +import static org.hamcrest.Matchers.empty; +import static org.hamcrest.Matchers.matchesPattern; +import static org.hamcrest.Matchers.not; +import static org.junit.jupiter.api.Assertions.assertEquals; public class HapiLocalizerTest { diff --git a/hapi-fhir-base/src/test/java/ca/uhn/fhir/model/api/ResourceMetadataKeyEnumTest.java b/hapi-fhir-base/src/test/java/ca/uhn/fhir/model/api/ResourceMetadataKeyEnumTest.java index b0ec6d38352..70f56111cf9 100644 --- a/hapi-fhir-base/src/test/java/ca/uhn/fhir/model/api/ResourceMetadataKeyEnumTest.java +++ b/hapi-fhir-base/src/test/java/ca/uhn/fhir/model/api/ResourceMetadataKeyEnumTest.java @@ -2,7 +2,8 @@ package ca.uhn.fhir.model.api; import org.junit.jupiter.api.Test; -import static org.junit.jupiter.api.Assertions.*; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNotEquals; public class ResourceMetadataKeyEnumTest { diff --git a/hapi-fhir-base/src/test/java/ca/uhn/fhir/model/api/TagTest.java b/hapi-fhir-base/src/test/java/ca/uhn/fhir/model/api/TagTest.java index ab747cf2923..2bfe43c07eb 100644 --- a/hapi-fhir-base/src/test/java/ca/uhn/fhir/model/api/TagTest.java +++ b/hapi-fhir-base/src/test/java/ca/uhn/fhir/model/api/TagTest.java @@ -5,7 +5,10 @@ import org.junit.jupiter.api.Test; import java.net.URI; import java.net.URISyntaxException; -import static org.junit.jupiter.api.Assertions.*; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertNotEquals; +import static org.junit.jupiter.api.Assertions.assertTrue; public class TagTest { diff --git a/hapi-fhir-base/src/test/java/ca/uhn/fhir/rest/api/CacheControlDirectiveTest.java b/hapi-fhir-base/src/test/java/ca/uhn/fhir/rest/api/CacheControlDirectiveTest.java index 744e03b17bd..0d7991d85d8 100644 --- a/hapi-fhir-base/src/test/java/ca/uhn/fhir/rest/api/CacheControlDirectiveTest.java +++ b/hapi-fhir-base/src/test/java/ca/uhn/fhir/rest/api/CacheControlDirectiveTest.java @@ -5,7 +5,9 @@ import org.junit.jupiter.api.Test; import java.util.Arrays; import java.util.List; -import static org.junit.jupiter.api.Assertions.*; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertTrue; public class CacheControlDirectiveTest { diff --git a/hapi-fhir-base/src/test/java/ca/uhn/fhir/rest/api/EncodingEnumTest.java b/hapi-fhir-base/src/test/java/ca/uhn/fhir/rest/api/EncodingEnumTest.java index b510b658f69..2e2a27e7cbe 100644 --- a/hapi-fhir-base/src/test/java/ca/uhn/fhir/rest/api/EncodingEnumTest.java +++ b/hapi-fhir-base/src/test/java/ca/uhn/fhir/rest/api/EncodingEnumTest.java @@ -2,7 +2,7 @@ package ca.uhn.fhir.rest.api; import org.junit.jupiter.api.Test; -import static org.junit.jupiter.api.Assertions.*; +import static org.junit.jupiter.api.Assertions.assertEquals; public class EncodingEnumTest { diff --git a/hapi-fhir-base/src/test/java/ca/uhn/fhir/rest/api/MethodOutcomeTest.java b/hapi-fhir-base/src/test/java/ca/uhn/fhir/rest/api/MethodOutcomeTest.java index 89a29b045da..5049c53ebc0 100644 --- a/hapi-fhir-base/src/test/java/ca/uhn/fhir/rest/api/MethodOutcomeTest.java +++ b/hapi-fhir-base/src/test/java/ca/uhn/fhir/rest/api/MethodOutcomeTest.java @@ -7,7 +7,8 @@ import java.util.Arrays; import java.util.HashMap; import java.util.Optional; -import static org.junit.jupiter.api.Assertions.*; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertTrue; class MethodOutcomeTest { diff --git a/hapi-fhir-base/src/test/java/ca/uhn/fhir/rest/param/DateParamTest.java b/hapi-fhir-base/src/test/java/ca/uhn/fhir/rest/param/DateParamTest.java index e1b13e4f6c2..1e89acc623c 100644 --- a/hapi-fhir-base/src/test/java/ca/uhn/fhir/rest/param/DateParamTest.java +++ b/hapi-fhir-base/src/test/java/ca/uhn/fhir/rest/param/DateParamTest.java @@ -17,7 +17,9 @@ import static java.util.concurrent.TimeUnit.SECONDS; import static org.hamcrest.MatcherAssert.assertThat; import static org.hamcrest.Matchers.endsWith; import static org.hamcrest.Matchers.startsWith; -import static org.junit.jupiter.api.Assertions.*; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNull; +import static org.junit.jupiter.api.Assertions.fail; public class DateParamTest { private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(DateParamTest.class); diff --git a/hapi-fhir-base/src/test/java/ca/uhn/fhir/rest/param/QualifierDetailsTest.java b/hapi-fhir-base/src/test/java/ca/uhn/fhir/rest/param/QualifierDetailsTest.java index 33eb930e129..f844bdb9dca 100644 --- a/hapi-fhir-base/src/test/java/ca/uhn/fhir/rest/param/QualifierDetailsTest.java +++ b/hapi-fhir-base/src/test/java/ca/uhn/fhir/rest/param/QualifierDetailsTest.java @@ -3,7 +3,8 @@ package ca.uhn.fhir.rest.param; import com.google.common.collect.Sets; import org.junit.jupiter.api.Test; -import static org.junit.jupiter.api.Assertions.*; +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertTrue; public class QualifierDetailsTest { diff --git a/hapi-fhir-base/src/test/java/ca/uhn/fhir/rest/param/StringParamTest.java b/hapi-fhir-base/src/test/java/ca/uhn/fhir/rest/param/StringParamTest.java index 064443fcf5e..3b6f7609537 100644 --- a/hapi-fhir-base/src/test/java/ca/uhn/fhir/rest/param/StringParamTest.java +++ b/hapi-fhir-base/src/test/java/ca/uhn/fhir/rest/param/StringParamTest.java @@ -1,14 +1,11 @@ package ca.uhn.fhir.rest.param; -import static ca.uhn.fhir.rest.api.Constants.PARAMQUALIFIER_STRING_TEXT; -import static org.junit.jupiter.api.Assertions.*; - import ca.uhn.fhir.context.FhirContext; import ca.uhn.fhir.model.api.IQueryParameterType; import ch.qos.logback.classic.Level; +import ch.qos.logback.classic.Logger; import ch.qos.logback.classic.spi.ILoggingEvent; import ch.qos.logback.core.read.ListAppender; -import org.apache.commons.lang3.StringUtils; import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Nested; @@ -18,12 +15,17 @@ import org.junit.jupiter.params.ParameterizedTest; import org.junit.jupiter.params.provider.ValueSource; import org.mockito.Mock; import org.mockito.junit.jupiter.MockitoExtension; -import ch.qos.logback.classic.Logger; import org.slf4j.LoggerFactory; import java.util.List; import java.util.stream.Collectors; +import static ca.uhn.fhir.rest.api.Constants.PARAMQUALIFIER_STRING_TEXT; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertNull; +import static org.junit.jupiter.api.Assertions.assertTrue; + @ExtendWith(MockitoExtension.class) public class StringParamTest { diff --git a/hapi-fhir-base/src/test/java/ca/uhn/fhir/rest/server/exceptions/BaseServerResponseExceptionTest.java b/hapi-fhir-base/src/test/java/ca/uhn/fhir/rest/server/exceptions/BaseServerResponseExceptionTest.java index 78d38818858..ee0167d79ab 100644 --- a/hapi-fhir-base/src/test/java/ca/uhn/fhir/rest/server/exceptions/BaseServerResponseExceptionTest.java +++ b/hapi-fhir-base/src/test/java/ca/uhn/fhir/rest/server/exceptions/BaseServerResponseExceptionTest.java @@ -2,7 +2,7 @@ package ca.uhn.fhir.rest.server.exceptions; import org.junit.jupiter.api.Test; -import static org.junit.jupiter.api.Assertions.*; +import static org.junit.jupiter.api.Assertions.assertTrue; public class BaseServerResponseExceptionTest { diff --git a/hapi-fhir-base/src/test/java/ca/uhn/fhir/util/DateRangeUtilTest.java b/hapi-fhir-base/src/test/java/ca/uhn/fhir/util/DateRangeUtilTest.java index cce99a49e8b..fc75a7dff89 100644 --- a/hapi-fhir-base/src/test/java/ca/uhn/fhir/util/DateRangeUtilTest.java +++ b/hapi-fhir-base/src/test/java/ca/uhn/fhir/util/DateRangeUtilTest.java @@ -5,7 +5,6 @@ import ca.uhn.fhir.rest.param.DateRangeParam; import ca.uhn.fhir.rest.param.ParamPrefixEnum; import org.apache.commons.lang3.builder.ToStringBuilder; import org.apache.commons.lang3.builder.ToStringStyle; -import org.junit.jupiter.api.Test; import org.junit.jupiter.params.ParameterizedTest; import org.junit.jupiter.params.provider.MethodSource; @@ -19,7 +18,9 @@ import static ca.uhn.fhir.rest.param.ParamPrefixEnum.GREATERTHAN_OR_EQUALS; import static ca.uhn.fhir.rest.param.ParamPrefixEnum.LESSTHAN; import static ca.uhn.fhir.rest.param.ParamPrefixEnum.LESSTHAN_OR_EQUALS; import static org.hamcrest.MatcherAssert.assertThat; -import static org.hamcrest.Matchers.*; +import static org.hamcrest.Matchers.equalTo; +import static org.hamcrest.Matchers.notNullValue; +import static org.hamcrest.Matchers.nullValue; class DateRangeUtilTest { diff --git a/hapi-fhir-base/src/test/java/ca/uhn/fhir/util/LogUtilTest.java b/hapi-fhir-base/src/test/java/ca/uhn/fhir/util/LogUtilTest.java index 1f8fa0c6578..a1f566f436c 100644 --- a/hapi-fhir-base/src/test/java/ca/uhn/fhir/util/LogUtilTest.java +++ b/hapi-fhir-base/src/test/java/ca/uhn/fhir/util/LogUtilTest.java @@ -5,7 +5,11 @@ import org.slf4j.Logger; import org.slf4j.event.Level; import static org.mockito.ArgumentMatchers.anyString; -import static org.mockito.Mockito.*; +import static org.mockito.Mockito.any; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.times; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.verifyNoMoreInteractions; public class LogUtilTest { diff --git a/hapi-fhir-base/src/test/java/ca/uhn/fhir/util/ReflectionUtilTest.java b/hapi-fhir-base/src/test/java/ca/uhn/fhir/util/ReflectionUtilTest.java index 776f24109c8..9f5dbd46357 100644 --- a/hapi-fhir-base/src/test/java/ca/uhn/fhir/util/ReflectionUtilTest.java +++ b/hapi-fhir-base/src/test/java/ca/uhn/fhir/util/ReflectionUtilTest.java @@ -1,15 +1,17 @@ package ca.uhn.fhir.util; -import static org.junit.jupiter.api.Assertions.*; +import ca.uhn.fhir.context.ConfigurationException; +import ca.uhn.fhir.i18n.Msg; +import org.junit.jupiter.api.Test; import java.lang.reflect.Method; import java.util.ArrayList; import java.util.List; -import ca.uhn.fhir.i18n.Msg; -import org.junit.jupiter.api.Test; - -import ca.uhn.fhir.context.ConfigurationException; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertTrue; +import static org.junit.jupiter.api.Assertions.fail; public class ReflectionUtilTest { diff --git a/hapi-fhir-base/src/test/java/ca/uhn/fhir/util/StreamUtilTest.java b/hapi-fhir-base/src/test/java/ca/uhn/fhir/util/StreamUtilTest.java new file mode 100644 index 00000000000..0a805af02d2 --- /dev/null +++ b/hapi-fhir-base/src/test/java/ca/uhn/fhir/util/StreamUtilTest.java @@ -0,0 +1,65 @@ +package ca.uhn.fhir.util; + +import org.junit.jupiter.api.Test; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.MethodSource; + +import java.util.List; +import java.util.concurrent.atomic.AtomicBoolean; +import java.util.stream.Collectors; +import java.util.stream.Stream; + +import static org.hamcrest.MatcherAssert.assertThat; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.fail; + +class StreamUtilTest { + + @ParameterizedTest + @MethodSource("streamPartitionTestCases") + void testStreamPartitionBy4(String theCase, List theInput, List> theOutput) { + List> result = StreamUtil.partition(theInput.stream(), 4).toList(); + + assertEquals(theOutput, result, theCase); + } + + static Object[][] streamPartitionTestCases() { + return new Object[][]{ + { + "empty list produces empty stream", + List.of(), + List.of() + }, + { + "short list produces single chunk", + List.of(1, 2, 3), + List.of(List.of(1, 2, 3)) + }, + { + "longer list produces several chunks", + List.of(1, 2, 3, 1, 2, 3, 1, 2, 3), + List.of(List.of(1, 2, 3, 1), List.of(2, 3, 1, 2), List.of(3)) + }, + { + "even size produces even chunks", + List.of(1, 2, 3,4,5,6,7,8), + List.of(List.of(1, 2, 3,4), List.of(5,6,7,8)) + }, + }; + } + + @Test + void testStreamPartitionClosesOriginalStream() { + // given + AtomicBoolean closed = new AtomicBoolean(false); + Stream baseStream = Stream.of(1, 2, 3).onClose(()->closed.set(true)); + + // when + StreamUtil.partition(baseStream, 2).close(); + + // then + assertThat("partition closed underlying stream", closed.get()); + } + + +} diff --git a/hapi-fhir-base/src/test/java/ca/uhn/fhir/util/TaskChunkerTest.java b/hapi-fhir-base/src/test/java/ca/uhn/fhir/util/TaskChunkerTest.java index 811608c0b8a..3b4133323f7 100644 --- a/hapi-fhir-base/src/test/java/ca/uhn/fhir/util/TaskChunkerTest.java +++ b/hapi-fhir-base/src/test/java/ca/uhn/fhir/util/TaskChunkerTest.java @@ -8,7 +8,7 @@ import org.mockito.Captor; import org.mockito.Mock; import org.mockito.junit.jupiter.MockitoExtension; -import javax.annotation.Nonnull; +import jakarta.annotation.Nonnull; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.mockito.Mockito.verify; diff --git a/hapi-fhir-base/src/test/java/ca/uhn/fhir/validation/ResultSeverityEnumTest.java b/hapi-fhir-base/src/test/java/ca/uhn/fhir/validation/ResultSeverityEnumTest.java index 860c6367a6a..bc689c55259 100644 --- a/hapi-fhir-base/src/test/java/ca/uhn/fhir/validation/ResultSeverityEnumTest.java +++ b/hapi-fhir-base/src/test/java/ca/uhn/fhir/validation/ResultSeverityEnumTest.java @@ -1,9 +1,9 @@ package ca.uhn.fhir.validation; -import static org.junit.jupiter.api.Assertions.*; - import org.junit.jupiter.api.Test; +import static org.junit.jupiter.api.Assertions.assertEquals; + public class ResultSeverityEnumTest { @Test diff --git a/hapi-fhir-bom/pom.xml b/hapi-fhir-bom/pom.xml index d5a6c2c70d6..39b230f2ea5 100644 --- a/hapi-fhir-bom/pom.xml +++ b/hapi-fhir-bom/pom.xml @@ -4,7 +4,7 @@ 4.0.0 ca.uhn.hapi.fhir hapi-fhir-bom - 6.9.7-SNAPSHOT + 6.11.7-SNAPSHOT pom HAPI FHIR BOM @@ -12,7 +12,7 @@ ca.uhn.hapi.fhir hapi-deployable-pom - 6.9.7-SNAPSHOT + 6.11.7-SNAPSHOT ../hapi-deployable-pom/pom.xml diff --git a/hapi-fhir-checkstyle/pom.xml b/hapi-fhir-checkstyle/pom.xml index 718fa973873..22eba74f131 100644 --- a/hapi-fhir-checkstyle/pom.xml +++ b/hapi-fhir-checkstyle/pom.xml @@ -5,7 +5,7 @@ ca.uhn.hapi.fhir hapi-fhir - 6.9.7-SNAPSHOT + 6.11.7-SNAPSHOT ../pom.xml @@ -42,7 +42,6 @@ org.apache.maven.plugins maven-checkstyle-plugin - 3.2.0 com.puppycrawl.tools @@ -109,7 +108,6 @@ org.apache.maven.plugins maven-checkstyle-plugin - ${maven_checkstyle_version} **/osgi/**/*, **/.mvn/**/*, **/.mvn_/**/* diff --git a/hapi-fhir-checkstyle/src/checkstyle/hapi-base-checkstyle.xml b/hapi-fhir-checkstyle/src/checkstyle/hapi-base-checkstyle.xml index 5ff02a4549d..3e2a2c34f5c 100644 --- a/hapi-fhir-checkstyle/src/checkstyle/hapi-base-checkstyle.xml +++ b/hapi-fhir-checkstyle/src/checkstyle/hapi-base-checkstyle.xml @@ -18,15 +18,32 @@ + + + + + + + + + + + + + diff --git a/hapi-fhir-checkstyle/src/main/java/ca/uhn/fhir/checks/HapiErrorCodeCheck.java b/hapi-fhir-checkstyle/src/main/java/ca/uhn/fhir/checks/HapiErrorCodeCheck.java index 90ab3b487a8..8a20127c15b 100644 --- a/hapi-fhir-checkstyle/src/main/java/ca/uhn/fhir/checks/HapiErrorCodeCheck.java +++ b/hapi-fhir-checkstyle/src/main/java/ca/uhn/fhir/checks/HapiErrorCodeCheck.java @@ -73,7 +73,11 @@ public final class HapiErrorCodeCheck extends AbstractCheck { } else { String location = getFilePath() + ":" + instantiation.getLineNo() + ":" + instantiation.getColumnNo() + "(" + code + ")"; - ourCache.put(code, location); + // Ignore errors thrown in test for duplicates, as some fake implementations are throwing the same + // codes for test purpsoes. + if (!location.contains("/test/")) { + ourCache.put(code, location); + } } } else { log(theAst.getLineNo(), "Called Msg.code() with a non-integer argument"); diff --git a/hapi-fhir-cli/hapi-fhir-cli-api/pom.xml b/hapi-fhir-cli/hapi-fhir-cli-api/pom.xml index a4701a94456..aa68ece2aeb 100644 --- a/hapi-fhir-cli/hapi-fhir-cli-api/pom.xml +++ b/hapi-fhir-cli/hapi-fhir-cli-api/pom.xml @@ -4,7 +4,7 @@ ca.uhn.hapi.fhir hapi-deployable-pom - 6.9.7-SNAPSHOT + 6.11.7-SNAPSHOT ../../hapi-deployable-pom/pom.xml @@ -152,38 +152,25 @@ spring-web + - org.eclipse.jetty - jetty-servlets + org.eclipse.jetty.ee10.websocket + jetty-ee10-websocket-jakarta-client - org.eclipse.jetty - jetty-servlet + jakarta.websocket + jakarta.websocket-client-api org.eclipse.jetty jetty-server - org.eclipse.jetty - jetty-util - - - org.eclipse.jetty - jetty-webapp - - - org.eclipse.jetty.websocket - websocket-jetty-api - - - org.eclipse.jetty.websocket - websocket-core-client - - - org.eclipse.jetty.websocket - websocket-jetty-client + org.eclipse.jetty.ee10 + jetty-ee10-servlet + + org.slf4j jcl-over-slf4j @@ -195,39 +182,17 @@ org.thymeleaf - thymeleaf-spring5 + thymeleaf-spring6 - com.helger - ph-schematron - - - org.glassfish.jaxb - jaxb-runtime - - - org.glassfish.jaxb - jaxb-core - - - com.sun.istack - istack-commons-runtime - - + com.helger.schematron + ph-schematron-api - com.helger - ph-commons - - - javax.xml.bind - jaxb-api - - - org.glassfish.jaxb - jaxb-runtime + com.helger.schematron + ph-schematron-xslt @@ -257,11 +222,30 @@ spring-test test
    - + + org.simplejavamail + simple-java-mail + + + + com.sun.activation + jakarta.activation + + + + + org.apache.maven.plugins + maven-surefire-plugin + + none + false + 1 + + diff --git a/hapi-fhir-cli/hapi-fhir-cli-api/src/main/java/ca/uhn/fhir/cli/BaseCommand.java b/hapi-fhir-cli/hapi-fhir-cli-api/src/main/java/ca/uhn/fhir/cli/BaseCommand.java index 7e9da8b5f25..2d15d1733ec 100644 --- a/hapi-fhir-cli/hapi-fhir-cli-api/src/main/java/ca/uhn/fhir/cli/BaseCommand.java +++ b/hapi-fhir-cli/hapi-fhir-cli-api/src/main/java/ca/uhn/fhir/cli/BaseCommand.java @@ -34,6 +34,7 @@ import com.google.common.base.Charsets; import com.google.common.collect.Sets; import com.google.gson.JsonObject; import com.google.gson.JsonParser; +import jakarta.annotation.Nullable; import org.apache.commons.cli.CommandLine; import org.apache.commons.cli.Option; import org.apache.commons.cli.OptionGroup; @@ -75,7 +76,6 @@ import java.util.Optional; import java.util.concurrent.ExecutionException; import java.util.stream.Collectors; import java.util.zip.GZIPInputStream; -import javax.annotation.Nullable; import static org.apache.commons.lang3.StringUtils.isBlank; import static org.apache.commons.lang3.StringUtils.isNotBlank; diff --git a/hapi-fhir-cli/hapi-fhir-cli-api/src/main/java/ca/uhn/fhir/cli/BulkImportCommand.java b/hapi-fhir-cli/hapi-fhir-cli-api/src/main/java/ca/uhn/fhir/cli/BulkImportCommand.java index 8794891e7f3..d6b242b5ffb 100644 --- a/hapi-fhir-cli/hapi-fhir-cli-api/src/main/java/ca/uhn/fhir/cli/BulkImportCommand.java +++ b/hapi-fhir-cli/hapi-fhir-cli-api/src/main/java/ca/uhn/fhir/cli/BulkImportCommand.java @@ -31,6 +31,7 @@ import ca.uhn.fhir.rest.client.interceptor.LoggingInterceptor; import ca.uhn.fhir.rest.param.StringParam; import ca.uhn.fhir.rest.server.exceptions.InternalErrorException; import ca.uhn.fhir.util.ParametersUtil; +import jakarta.annotation.Nonnull; import org.apache.commons.cli.CommandLine; import org.apache.commons.cli.Options; import org.apache.commons.cli.ParseException; @@ -40,11 +41,12 @@ import org.apache.commons.io.file.PathUtils; import org.apache.commons.io.filefilter.FileFileFilter; import org.apache.commons.io.filefilter.IOFileFilter; import org.apache.commons.io.filefilter.SuffixFileFilter; +import org.apache.commons.lang3.time.DateUtils; +import org.eclipse.jetty.ee10.servlet.ServletContextHandler; +import org.eclipse.jetty.ee10.servlet.ServletHolder; import org.eclipse.jetty.server.Connector; import org.eclipse.jetty.server.Server; import org.eclipse.jetty.server.ServerConnector; -import org.eclipse.jetty.servlet.ServletContextHandler; -import org.eclipse.jetty.servlet.ServletHolder; import org.hl7.fhir.instance.model.api.IBase; import org.hl7.fhir.instance.model.api.IBaseParameters; import org.hl7.fhir.instance.model.api.IBaseResource; @@ -67,7 +69,6 @@ import java.util.List; import java.util.Locale; import java.util.concurrent.ExecutionException; import java.util.zip.GZIPInputStream; -import javax.annotation.Nonnull; public class BulkImportCommand extends BaseCommand { @@ -234,7 +235,12 @@ public class BulkImportCommand extends BaseCommand { private List startServer(int thePort, List files) { List indexes = new ArrayList<>(); - myServer = new Server(thePort); + + myServer = new Server(); + ServerConnector connector = new ServerConnector(myServer); + connector.setIdleTimeout(DateUtils.MILLIS_PER_MINUTE); + connector.setPort(myPort); + myServer.setConnectors(new Connector[] {connector}); myServlet = new BulkImportFileServlet(); for (File t : files) { diff --git a/hapi-fhir-cli/hapi-fhir-cli-api/src/main/java/ca/uhn/fhir/cli/LoadingValidationSupportDstu2.java b/hapi-fhir-cli/hapi-fhir-cli-api/src/main/java/ca/uhn/fhir/cli/LoadingValidationSupportDstu2.java index 17c8ff14947..30247c93663 100644 --- a/hapi-fhir-cli/hapi-fhir-cli-api/src/main/java/ca/uhn/fhir/cli/LoadingValidationSupportDstu2.java +++ b/hapi-fhir-cli/hapi-fhir-cli-api/src/main/java/ca/uhn/fhir/cli/LoadingValidationSupportDstu2.java @@ -36,6 +36,11 @@ public class LoadingValidationSupportDstu2 implements IValidationSupport { private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(LoadingValidationSupportDstu2.class); + @Override + public String getName() { + return "Dstu2 CLI Loading Validation Support"; + } + @Override public T fetchResource(Class theClass, String theUri) { String resName = myCtx.getResourceType(theClass); diff --git a/hapi-fhir-cli/hapi-fhir-cli-api/src/main/java/ca/uhn/fhir/cli/LoadingValidationSupportDstu3.java b/hapi-fhir-cli/hapi-fhir-cli-api/src/main/java/ca/uhn/fhir/cli/LoadingValidationSupportDstu3.java index b142733790b..bd308f8766a 100644 --- a/hapi-fhir-cli/hapi-fhir-cli-api/src/main/java/ca/uhn/fhir/cli/LoadingValidationSupportDstu3.java +++ b/hapi-fhir-cli/hapi-fhir-cli-api/src/main/java/ca/uhn/fhir/cli/LoadingValidationSupportDstu3.java @@ -35,6 +35,11 @@ public class LoadingValidationSupportDstu3 implements IValidationSupport { private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(LoadingValidationSupportDstu3.class); + @Override + public String getName() { + return "Dstu3 CLI Loading Validation Support"; + } + @Override public T fetchResource(Class theClass, String theUri) { String resName = myCtx.getResourceType(theClass); diff --git a/hapi-fhir-cli/hapi-fhir-cli-api/src/main/java/ca/uhn/fhir/cli/ReindexTerminologyCommand.java b/hapi-fhir-cli/hapi-fhir-cli-api/src/main/java/ca/uhn/fhir/cli/ReindexTerminologyCommand.java index 36524c393da..8d202922236 100644 --- a/hapi-fhir-cli/hapi-fhir-cli-api/src/main/java/ca/uhn/fhir/cli/ReindexTerminologyCommand.java +++ b/hapi-fhir-cli/hapi-fhir-cli-api/src/main/java/ca/uhn/fhir/cli/ReindexTerminologyCommand.java @@ -24,7 +24,7 @@ import ca.uhn.fhir.rest.client.api.IGenericClient; import ca.uhn.fhir.rest.client.interceptor.LoggingInterceptor; import ca.uhn.fhir.rest.server.exceptions.BaseServerResponseException; import ca.uhn.fhir.util.ParametersUtil; -import joptsimple.internal.Strings; +import jakarta.annotation.Nonnull; import org.apache.commons.cli.CommandLine; import org.apache.commons.cli.ParseException; import org.hl7.fhir.instance.model.api.IBaseParameters; @@ -32,7 +32,6 @@ import org.hl7.fhir.r4.model.Parameters; import java.util.List; import java.util.Optional; -import javax.annotation.Nonnull; import static ca.uhn.fhir.jpa.provider.BaseJpaSystemProvider.RESP_PARAM_SUCCESS; @@ -111,7 +110,7 @@ public class ReindexTerminologyCommand extends BaseRequestGeneratingCommand { @Nonnull private String getResponseMessage(IBaseParameters response) { List message = ParametersUtil.getNamedParameterValuesAsString(myFhirCtx, response, "message"); - return Strings.join(message, NL); + return String.join(NL, message); } public static final String NL = System.getProperty("line.separator"); diff --git a/hapi-fhir-cli/hapi-fhir-cli-api/src/main/java/ca/uhn/fhir/cli/WebsocketSubscribeCommand.java b/hapi-fhir-cli/hapi-fhir-cli-api/src/main/java/ca/uhn/fhir/cli/WebsocketSubscribeCommand.java index 8ec137c69b7..1a13173f9f3 100644 --- a/hapi-fhir-cli/hapi-fhir-cli-api/src/main/java/ca/uhn/fhir/cli/WebsocketSubscribeCommand.java +++ b/hapi-fhir-cli/hapi-fhir-cli-api/src/main/java/ca/uhn/fhir/cli/WebsocketSubscribeCommand.java @@ -21,19 +21,17 @@ package ca.uhn.fhir.cli; import ca.uhn.fhir.i18n.Msg; import ca.uhn.fhir.model.primitive.IdDt; +import jakarta.websocket.ClientEndpoint; +import jakarta.websocket.ContainerProvider; +import jakarta.websocket.OnClose; +import jakarta.websocket.OnError; +import jakarta.websocket.OnMessage; +import jakarta.websocket.OnOpen; +import jakarta.websocket.Session; +import jakarta.websocket.WebSocketContainer; import org.apache.commons.cli.CommandLine; import org.apache.commons.cli.Options; import org.apache.commons.cli.ParseException; -import org.eclipse.jetty.websocket.api.Frame; -import org.eclipse.jetty.websocket.api.Session; -import org.eclipse.jetty.websocket.api.annotations.OnWebSocketClose; -import org.eclipse.jetty.websocket.api.annotations.OnWebSocketConnect; -import org.eclipse.jetty.websocket.api.annotations.OnWebSocketError; -import org.eclipse.jetty.websocket.api.annotations.OnWebSocketFrame; -import org.eclipse.jetty.websocket.api.annotations.OnWebSocketMessage; -import org.eclipse.jetty.websocket.api.annotations.WebSocket; -import org.eclipse.jetty.websocket.client.ClientUpgradeRequest; -import org.eclipse.jetty.websocket.client.WebSocketClient; import java.net.URI; @@ -46,6 +44,7 @@ public class WebsocketSubscribeCommand extends BaseCommand { private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(WebsocketSubscribeCommand.class); private boolean myQuit; + private Session mySession; @Override public String getCommandDescription() { @@ -75,14 +74,13 @@ public class WebsocketSubscribeCommand extends BaseCommand { IdDt subsId = new IdDt(theCommandLine.getOptionValue("i")); - WebSocketClient client = new WebSocketClient(); SimpleEchoSocket socket = new SimpleEchoSocket(subsId.getIdPart()); try { - client.start(); URI echoUri = new URI(target); - ClientUpgradeRequest request = new ClientUpgradeRequest(); ourLog.info("Connecting to : {}", echoUri); - client.connect(socket, echoUri, request); + + WebSocketContainer container = ContainerProvider.getWebSocketContainer(); + mySession = container.connectToServer(socket, echoUri); while (!myQuit) { Thread.sleep(500L); @@ -93,7 +91,9 @@ public class WebsocketSubscribeCommand extends BaseCommand { throw new CommandFailureException(Msg.code(1537) + e); } finally { try { - client.stop(); + if (mySession != null) { + mySession.close(); + } } catch (Exception e) { ourLog.error("Failure", e); } @@ -103,7 +103,7 @@ public class WebsocketSubscribeCommand extends BaseCommand { /** * Basic Echo Client Socket */ - @WebSocket(maxTextMessageSize = 64 * 1024) + @ClientEndpoint public class SimpleEchoSocket { private String mySubsId; @@ -115,37 +115,32 @@ public class WebsocketSubscribeCommand extends BaseCommand { mySubsId = theSubsId; } - @OnWebSocketClose + @OnClose public void onClose(int statusCode, String reason) { ourLog.info("Received CLOSE status={} reason={}", statusCode, reason); } - @OnWebSocketConnect + @OnOpen public void onConnect(Session theSession) { ourLog.info("Successfully connected"); this.session = theSession; try { String sending = "bind " + mySubsId; LOG_SEND.info("{}", sending); - theSession.getRemote().sendString(sending); + theSession.getBasicRemote().sendText(sending); } catch (Throwable t) { ourLog.error("Failure", t); myQuit = true; } } - @OnWebSocketError + @OnError public void onError(Throwable theError) { ourLog.error("Websocket error: ", theError); myQuit = true; } - @OnWebSocketFrame - public void onFrame(Frame theFrame) { - ourLog.debug("Websocket frame: {}", theFrame); - } - - @OnWebSocketMessage + @OnMessage public void onMessage(String theMsg) { LOG_RECV.info("{}", theMsg); } diff --git a/hapi-fhir-cli/hapi-fhir-cli-api/src/test/java/ca/uhn/fhir/cli/BulkImportCommandIT.java b/hapi-fhir-cli/hapi-fhir-cli-api/src/test/java/ca/uhn/fhir/cli/BulkImportCommandTest.java similarity index 92% rename from hapi-fhir-cli/hapi-fhir-cli-api/src/test/java/ca/uhn/fhir/cli/BulkImportCommandIT.java rename to hapi-fhir-cli/hapi-fhir-cli-api/src/test/java/ca/uhn/fhir/cli/BulkImportCommandTest.java index 02d3e24325b..db1ea7036ab 100644 --- a/hapi-fhir-cli/hapi-fhir-cli-api/src/test/java/ca/uhn/fhir/cli/BulkImportCommandIT.java +++ b/hapi-fhir-cli/hapi-fhir-cli-api/src/test/java/ca/uhn/fhir/cli/BulkImportCommandTest.java @@ -8,6 +8,8 @@ import ca.uhn.fhir.batch2.model.JobInstanceStartRequest; import ca.uhn.fhir.batch2.model.StatusEnum; import ca.uhn.fhir.context.FhirContext; import ca.uhn.fhir.jpa.batch.models.Batch2JobStartResponse; +import ca.uhn.fhir.jpa.partition.IRequestPartitionHelperSvc; +import ca.uhn.fhir.rest.api.server.RequestDetails; import ca.uhn.fhir.rest.server.exceptions.InternalErrorException; import ca.uhn.fhir.rest.server.interceptor.LoggingInterceptor; import ca.uhn.fhir.system.HapiSystemProperties; @@ -52,9 +54,9 @@ import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; @ExtendWith(MockitoExtension.class) -public class BulkImportCommandIT { +public class BulkImportCommandTest { - private static final Logger ourLog = LoggerFactory.getLogger(BulkImportCommandIT.class); + private static final Logger ourLog = LoggerFactory.getLogger(BulkImportCommandTest.class); static { HapiSystemProperties.enableTestMode(); @@ -62,10 +64,12 @@ public class BulkImportCommandIT { @RegisterExtension public HttpClientExtension myHttpClientExtension = new HttpClientExtension(); - @Mock + @Mock(strictness = Mock.Strictness.LENIENT) private IJobCoordinator myJobCoordinator; private final BulkDataImportProvider myProvider = new BulkDataImportProvider(); private final FhirContext myCtx = FhirContext.forR4Cached(); + @Mock + private IRequestPartitionHelperSvc myRequestPartitionHelperSvc; @RegisterExtension public RestfulServerExtension myRestfulServerExtension = new RestfulServerExtension(myCtx, myProvider) .registerInterceptor(new LoggingInterceptor()); @@ -77,6 +81,7 @@ public class BulkImportCommandIT { public void beforeEach() throws IOException { myProvider.setFhirContext(myCtx); myProvider.setJobCoordinator(myJobCoordinator); + myProvider.setRequestPartitionHelperService(myRequestPartitionHelperSvc); myTempDir = Files.createTempDirectory("hapifhir"); ourLog.info("Created temp directory: {}", myTempDir); } @@ -96,19 +101,12 @@ public class BulkImportCommandIT { @Test public void testBulkImport() throws IOException { - JobInstance jobInfo = new JobInstance() - .setStatus(StatusEnum.COMPLETED) - .setCreateTime(parseDate("2022-01-01T12:00:00-04:00")) - .setStartTime(parseDate("2022-01-01T12:10:00-04:00")); - - when(myJobCoordinator.getInstance(eq("THE-JOB-ID"))).thenReturn(jobInfo); - String fileContents1 = "{\"resourceType\":\"Observation\"}\n{\"resourceType\":\"Observation\"}"; String fileContents2 = "{\"resourceType\":\"Patient\"}\n{\"resourceType\":\"Patient\"}"; writeNdJsonFileToTempDirectory(fileContents1, "file1.json"); writeNdJsonFileToTempDirectory(fileContents2, "file2.json"); - when(myJobCoordinator.startInstance(any())).thenReturn(createJobStartResponse("THE-JOB-ID")); + when(myJobCoordinator.startInstance(any(), any())).thenReturn(createJobStartResponse("THE-JOB-ID")); // Start the command in a separate thread new Thread(() -> App.main(new String[]{ @@ -123,7 +121,7 @@ public class BulkImportCommandIT { await().until(() -> myRestfulServerExtension.getRequestContentTypes().size(), equalTo(2)); ourLog.info("Initiation requests complete"); - verify(myJobCoordinator, timeout(10000).times(1)).startInstance(myStartCaptor.capture()); + verify(myJobCoordinator, timeout(10000).times(1)).startInstance(any(RequestDetails.class), myStartCaptor.capture()); JobInstanceStartRequest startRequest = myStartCaptor.getValue(); BulkImportJobParameters jobParameters = startRequest.getParameters(BulkImportJobParameters.class); @@ -149,7 +147,7 @@ public class BulkImportCommandIT { writeNdJsonFileToTempDirectory(fileContents1, "file1.json.gz"); writeNdJsonFileToTempDirectory(fileContents2, "file2.json.gz"); - when(myJobCoordinator.startInstance(any())) + when(myJobCoordinator.startInstance(any(), any())) .thenReturn(createJobStartResponse("THE-JOB-ID")); // Start the command in a separate thread @@ -165,7 +163,7 @@ public class BulkImportCommandIT { await().until(() -> myRestfulServerExtension.getRequestContentTypes().size(), equalTo(2)); ourLog.info("Initiation requests complete"); - verify(myJobCoordinator, timeout(10000).times(1)).startInstance(myStartCaptor.capture()); + verify(myJobCoordinator, timeout(10000).times(1)).startInstance(any(RequestDetails.class), myStartCaptor.capture()); JobInstanceStartRequest startRequest = myStartCaptor.getValue(); BulkImportJobParameters jobParameters = startRequest.getParameters(BulkImportJobParameters.class); @@ -191,7 +189,7 @@ public class BulkImportCommandIT { writeNdJsonFileToTempDirectory(fileContents1, "file1.json"); writeNdJsonFileToTempDirectory(fileContents2, "file2.json"); - when(myJobCoordinator.startInstance(any())).thenReturn(createJobStartResponse("THE-JOB-ID")); + when(myJobCoordinator.startInstance(any(), any())).thenReturn(createJobStartResponse("THE-JOB-ID")); // Start the command in a separate thread new Thread(() -> App.main(new String[]{ @@ -206,7 +204,7 @@ public class BulkImportCommandIT { await().until(() -> myRestfulServerExtension.getRequestContentTypes().size(), equalTo(2)); ourLog.info("Initiation requests complete"); - verify(myJobCoordinator, timeout(10000).times(1)).startInstance(myStartCaptor.capture()); + verify(myJobCoordinator, timeout(10000).times(1)).startInstance(any(RequestDetails.class), myStartCaptor.capture()); try{ JobInstanceStartRequest startRequest = myStartCaptor.getValue(); diff --git a/hapi-fhir-cli/hapi-fhir-cli-api/src/test/java/ca/uhn/fhir/cli/HapiClearMigrationLockCommandTest.java b/hapi-fhir-cli/hapi-fhir-cli-api/src/test/java/ca/uhn/fhir/cli/HapiClearMigrationLockCommandTest.java index 635ce997c65..e25841cacb3 100644 --- a/hapi-fhir-cli/hapi-fhir-cli-api/src/test/java/ca/uhn/fhir/cli/HapiClearMigrationLockCommandTest.java +++ b/hapi-fhir-cli/hapi-fhir-cli-api/src/test/java/ca/uhn/fhir/cli/HapiClearMigrationLockCommandTest.java @@ -14,21 +14,26 @@ import org.springframework.jdbc.core.support.AbstractLobCreatingPreparedStatemen import org.springframework.jdbc.support.lob.DefaultLobHandler; import org.springframework.jdbc.support.lob.LobCreator; -import javax.annotation.Nonnull; +import jakarta.annotation.Nonnull; import java.io.File; import java.io.IOException; import java.sql.PreparedStatement; import java.sql.SQLException; import java.sql.Timestamp; import java.sql.Types; -import java.util.*; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Date; +import java.util.List; +import java.util.UUID; import static ca.uhn.fhir.jpa.migrate.HapiMigrationLock.LOCK_PID; import static ca.uhn.fhir.jpa.migrate.HapiMigrationStorageSvc.LOCK_TYPE; import static org.apache.commons.lang3.StringUtils.isBlank; import static org.hamcrest.CoreMatchers.containsString; import static org.hamcrest.MatcherAssert.assertThat; -import static org.junit.jupiter.api.Assertions.*; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.fail; import static org.slf4j.LoggerFactory.getLogger; public class HapiClearMigrationLockCommandTest extends ConsoleOutputCapturingBaseTest { diff --git a/hapi-fhir-cli/hapi-fhir-cli-api/src/test/java/ca/uhn/fhir/cli/HapiFlywayMigrateDatabaseCommandTest.java b/hapi-fhir-cli/hapi-fhir-cli-api/src/test/java/ca/uhn/fhir/cli/HapiFlywayMigrateDatabaseCommandTest.java index a24150e9079..d83a1ce05d4 100644 --- a/hapi-fhir-cli/hapi-fhir-cli-api/src/test/java/ca/uhn/fhir/cli/HapiFlywayMigrateDatabaseCommandTest.java +++ b/hapi-fhir-cli/hapi-fhir-cli-api/src/test/java/ca/uhn/fhir/cli/HapiFlywayMigrateDatabaseCommandTest.java @@ -2,11 +2,17 @@ package ca.uhn.fhir.cli; import ca.uhn.fhir.jpa.migrate.DriverTypeEnum; import ca.uhn.fhir.jpa.migrate.JdbcUtils; +import ca.uhn.fhir.jpa.migrate.SchemaMigrator; +import ca.uhn.fhir.jpa.migrate.dao.HapiMigrationDao; +import ca.uhn.fhir.jpa.migrate.entity.HapiMigrationEntity; +import ca.uhn.fhir.jpa.util.RandomTextUtils; import ca.uhn.fhir.system.HapiSystemProperties; import com.google.common.base.Charsets; import org.apache.commons.io.FileUtils; import org.apache.commons.io.IOUtils; +import org.junit.jupiter.api.MethodOrderer; import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.TestMethodOrder; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.jdbc.core.JdbcTemplate; @@ -14,7 +20,7 @@ import org.springframework.jdbc.core.support.AbstractLobCreatingPreparedStatemen import org.springframework.jdbc.support.lob.DefaultLobHandler; import org.springframework.jdbc.support.lob.LobCreator; -import javax.annotation.Nonnull; +import jakarta.annotation.Nonnull; import java.io.File; import java.io.IOException; import java.sql.PreparedStatement; @@ -32,10 +38,11 @@ import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertFalse; import static org.junit.jupiter.api.Assertions.assertTrue; +@TestMethodOrder(MethodOrderer.MethodName.class) public class HapiFlywayMigrateDatabaseCommandTest { private static final Logger ourLog = LoggerFactory.getLogger(HapiFlywayMigrateDatabaseCommandTest.class); - public static final String DB_DIRECTORY = "target/h2_test"; + private final String myDbDirectory = "target/h2_test/" + RandomTextUtils.newSecureRandomAlphaNumericString(5); static { HapiSystemProperties.enableTestMode(); @@ -123,11 +130,13 @@ public class HapiFlywayMigrateDatabaseCommandTest { String url = "jdbc:h2:" + location.getAbsolutePath(); DriverTypeEnum.ConnectionProperties connectionProperties = DriverTypeEnum.H2_EMBEDDED.newConnectionProperties(url, "", ""); + HapiMigrationDao hapiMigrationDao = new HapiMigrationDao(connectionProperties.getDataSource(), connectionProperties.getDriverType(), SchemaMigrator.HAPI_FHIR_MIGRATION_TABLENAME); String initSql = "/persistence_create_h2_340.sql"; executeSqlStatements(connectionProperties, initSql); seedDatabase340(connectionProperties); + seedDatabaseMigration340(hapiMigrationDao); ourLog.info("**********************************************"); ourLog.info("Done Setup, Starting Migration..."); @@ -160,6 +169,7 @@ public class HapiFlywayMigrateDatabaseCommandTest { // Verify that foreign key FK_SEARCHRES_RES on HFJ_SEARCH_RESULT exists foreignKeys = JdbcUtils.getForeignKeys(connectionProperties, "HFJ_RESOURCE", "HFJ_SEARCH_RESULT"); assertTrue(foreignKeys.contains("FK_SEARCHRES_RES")); + int expectedMigrationEntities = hapiMigrationDao.findAll().size(); App.main(args); @@ -181,6 +191,8 @@ public class HapiFlywayMigrateDatabaseCommandTest { // Verify that foreign key FK_SEARCHRES_RES on HFJ_SEARCH_RESULT still exists foreignKeys = JdbcUtils.getForeignKeys(connectionProperties, "HFJ_RESOURCE", "HFJ_SEARCH_RESULT"); assertTrue(foreignKeys.contains("FK_SEARCHRES_RES")); + assertTrue(expectedMigrationEntities == hapiMigrationDao.findAll().size()); + } @Test @@ -210,14 +222,47 @@ public class HapiFlywayMigrateDatabaseCommandTest { assertTrue(JdbcUtils.getTableNames(connectionProperties).contains("HFJ_BLK_EXPORT_JOB")); // Late table } + @Test + public void testMigrateFrom340_dryRun_whenNoMigrationTableExists() throws IOException, SQLException { + + File location = getLocation("migrator_h2_test_340_dryrun"); + + String url = "jdbc:h2:" + location.getAbsolutePath(); + DriverTypeEnum.ConnectionProperties connectionProperties = DriverTypeEnum.H2_EMBEDDED.newConnectionProperties(url, "", ""); + HapiMigrationDao hapiMigrationDao = new HapiMigrationDao(connectionProperties.getDataSource(), connectionProperties.getDriverType(), SchemaMigrator.HAPI_FHIR_MIGRATION_TABLENAME); + + String initSql = "/persistence_create_h2_340.sql"; + executeSqlStatements(connectionProperties, initSql); + + seedDatabase340(connectionProperties); + + ourLog.info("**********************************************"); + ourLog.info("Done Setup, Starting Migration..."); + ourLog.info("**********************************************"); + + String[] args = new String[]{ + BaseFlywayMigrateDatabaseCommand.MIGRATE_DATABASE, + "-d", "H2_EMBEDDED", + "-u", url, + "-n", "", + "-p", "", + "-r" + }; + + App.main(args); + + assertFalse(JdbcUtils.getTableNames(connectionProperties).contains("FLY_HFJ_MIGRATION")); + } + @Nonnull private File getLocation(String theDatabaseName) throws IOException { - File directory = new File(DB_DIRECTORY); + File directory = new File(myDbDirectory); if (directory.exists()) { - FileUtils.deleteDirectory(directory); + FileUtils.forceDelete(directory); } + assertFalse(directory.exists()); - return new File(DB_DIRECTORY + "/" + theDatabaseName); + return new File(myDbDirectory + "/" + theDatabaseName); } private void seedDatabase340(DriverTypeEnum.ConnectionProperties theConnectionProperties) { @@ -360,4 +405,16 @@ public class HapiFlywayMigrateDatabaseCommandTest { } + private void seedDatabaseMigration340(HapiMigrationDao theHapiMigrationDao) { + theHapiMigrationDao.createMigrationTableIfRequired(); + HapiMigrationEntity hapiMigrationEntity = new HapiMigrationEntity(); + hapiMigrationEntity.setPid(1); + hapiMigrationEntity.setVersion("3.4.0.20180401.1"); + hapiMigrationEntity.setDescription("some sql statement"); + hapiMigrationEntity.setExecutionTime(25); + hapiMigrationEntity.setSuccess(true); + + theHapiMigrationDao.save(hapiMigrationEntity); + } + } diff --git a/hapi-fhir-cli/hapi-fhir-cli-api/src/test/java/ca/uhn/fhir/cli/HeaderPassthroughOptionTest.java b/hapi-fhir-cli/hapi-fhir-cli-api/src/test/java/ca/uhn/fhir/cli/HeaderPassthroughOptionTest.java index 38cbd22c317..cc79a07c4a0 100644 --- a/hapi-fhir-cli/hapi-fhir-cli-api/src/test/java/ca/uhn/fhir/cli/HeaderPassthroughOptionTest.java +++ b/hapi-fhir-cli/hapi-fhir-cli-api/src/test/java/ca/uhn/fhir/cli/HeaderPassthroughOptionTest.java @@ -8,16 +8,19 @@ import ca.uhn.fhir.rest.client.api.IGenericClient; import ca.uhn.fhir.rest.client.interceptor.CapturingInterceptor; import ca.uhn.fhir.rest.server.RestfulServer; import ca.uhn.fhir.test.utilities.JettyUtil; +import ca.uhn.fhir.test.utilities.server.HttpServletExtension; +import ca.uhn.fhir.test.utilities.server.RestfulServerExtension; import org.apache.commons.cli.CommandLine; import org.apache.commons.cli.DefaultParser; import org.apache.commons.cli.ParseException; import org.eclipse.jetty.server.Server; -import org.eclipse.jetty.servlet.ServletHandler; -import org.eclipse.jetty.servlet.ServletHolder; +import org.eclipse.jetty.ee10.servlet.ServletHandler; +import org.eclipse.jetty.ee10.servlet.ServletHolder; import org.hl7.fhir.r4.model.IdType; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.ExtendWith; +import org.junit.jupiter.api.extension.RegisterExtension; import org.mockito.Mock; import org.mockito.junit.jupiter.MockitoExtension; import org.slf4j.Logger; @@ -46,33 +49,29 @@ public class HeaderPassthroughOptionTest { private static final Logger ourLog = LoggerFactory.getLogger(HeaderPassthroughOptionTest.class); final String FHIR_VERSION = "r4"; - private FhirContext myCtx = FhirContext.forR4(); - private Server myServer; - private int myPort; + private final FhirContext myCtx = FhirContext.forR4Cached(); private final String headerKey1 = "test-header-key-1"; private final String headerValue1 = "test header value-1"; private static final String ourConceptsFileName = "target/concepts.csv"; private static final String ourHierarchyFileName = "target/hierarchy.csv"; + private static final AtomicInteger ourFilenameCounter = new AtomicInteger(); private final CapturingInterceptor myCapturingInterceptor = new CapturingInterceptor(); private final UploadTerminologyCommand testedCommand = new RequestCapturingUploadTerminologyCommand(myCapturingInterceptor); + private final TerminologyUploaderProvider myProvider = new TerminologyUploaderProvider(); @Mock protected ITermLoaderSvc myTermLoaderSvc; - private static final AtomicInteger ourFilenameCounter = new AtomicInteger(); + + @RegisterExtension + public RestfulServerExtension myServer = new RestfulServerExtension(myCtx) + .registerProvider(myProvider); @BeforeEach - public void beforeEach() throws Exception { - myServer = new Server(0); - TerminologyUploaderProvider provider = new TerminologyUploaderProvider(myCtx, myTermLoaderSvc); - ServletHandler proxyHandler = new ServletHandler(); - RestfulServer servlet = new RestfulServer(myCtx); - servlet.registerProvider(provider); - ServletHolder servletHolder = new ServletHolder(servlet); - proxyHandler.addServletWithMapping(servletHolder, "/*"); - myServer.setHandler(proxyHandler); - JettyUtil.startServer(myServer); - myPort = JettyUtil.getPortForStartedServer(myServer); + public void beforeEach() { + myProvider.setContext(myCtx); + myProvider.setTerminologyLoaderSvc(myTermLoaderSvc); + when(myTermLoaderSvc.loadCustom(eq("http://foo"), anyList(), any())) .thenReturn(new UploadStatistics(100, new IdType("CodeSystem/101"))); } @@ -85,7 +84,7 @@ public class HeaderPassthroughOptionTest { String[] args = new String[]{ "-v", FHIR_VERSION, "-m", "SNAPSHOT", - "-t", "http://localhost:" + myPort, + "-t", myServer.getBaseUrl(), "-u", "http://foo", "-d", getConceptFilename(filenameCounter), "-d", getHierarchyFilename(filenameCounter), @@ -115,7 +114,7 @@ public class HeaderPassthroughOptionTest { String[] args = new String[]{ "-v", FHIR_VERSION, "-m", "SNAPSHOT", - "-t", "http://localhost:" + myPort, + "-t", myServer.getBaseUrl(), "-u", "http://foo", "-d", getConceptFilename(filenameCounter), "-d", getHierarchyFilename(filenameCounter), @@ -149,7 +148,7 @@ public class HeaderPassthroughOptionTest { String[] args = new String[]{ "-v", FHIR_VERSION, "-m", "SNAPSHOT", - "-t", "http://localhost:" + myPort, + "-t", myServer.getBaseUrl(), "-u", "http://foo", "-d", getConceptFilename(filenameCounter), "-d", getHierarchyFilename(filenameCounter), diff --git a/hapi-fhir-cli/hapi-fhir-cli-app/pom.xml b/hapi-fhir-cli/hapi-fhir-cli-app/pom.xml index 9f3a84bf061..a1d5071ed88 100644 --- a/hapi-fhir-cli/hapi-fhir-cli-app/pom.xml +++ b/hapi-fhir-cli/hapi-fhir-cli-app/pom.xml @@ -6,7 +6,7 @@ ca.uhn.hapi.fhir hapi-fhir-cli - 6.9.7-SNAPSHOT + 6.11.7-SNAPSHOT ../pom.xml diff --git a/hapi-fhir-cli/pom.xml b/hapi-fhir-cli/pom.xml index 2c6dd8583ab..dbcfdedd285 100644 --- a/hapi-fhir-cli/pom.xml +++ b/hapi-fhir-cli/pom.xml @@ -5,7 +5,7 @@ ca.uhn.hapi.fhir hapi-fhir - 6.9.7-SNAPSHOT + 6.11.7-SNAPSHOT ../pom.xml diff --git a/hapi-fhir-client-okhttp/pom.xml b/hapi-fhir-client-okhttp/pom.xml index 28b5ede3287..78bda8b4a32 100644 --- a/hapi-fhir-client-okhttp/pom.xml +++ b/hapi-fhir-client-okhttp/pom.xml @@ -4,7 +4,7 @@ ca.uhn.hapi.fhir hapi-deployable-pom - 6.9.7-SNAPSHOT + 6.11.7-SNAPSHOT ../hapi-deployable-pom/pom.xml @@ -81,11 +81,6 @@ jetty-server test - - org.eclipse.jetty - jetty-servlet - test - org.jboss.resteasy resteasy-client diff --git a/hapi-fhir-client-okhttp/src/test/java/ca/uhn/fhir/okhttp/GenericOkHttpClientDstu2Test.java b/hapi-fhir-client-okhttp/src/test/java/ca/uhn/fhir/okhttp/GenericOkHttpClientDstu2Test.java index 1969b94293d..0c38e94d735 100644 --- a/hapi-fhir-client-okhttp/src/test/java/ca/uhn/fhir/okhttp/GenericOkHttpClientDstu2Test.java +++ b/hapi-fhir-client-okhttp/src/test/java/ca/uhn/fhir/okhttp/GenericOkHttpClientDstu2Test.java @@ -28,39 +28,27 @@ import ca.uhn.fhir.rest.api.MethodOutcome; import ca.uhn.fhir.rest.api.PreferReturnEnum; import ca.uhn.fhir.rest.api.SearchStyleEnum; import ca.uhn.fhir.rest.api.SummaryEnum; -import ca.uhn.fhir.rest.client.api.Header; import ca.uhn.fhir.rest.client.api.IGenericClient; import ca.uhn.fhir.rest.client.api.ServerValidationModeEnum; import ca.uhn.fhir.rest.client.exceptions.InvalidResponseException; import ca.uhn.fhir.rest.client.impl.RestfulClientFactory; import ca.uhn.fhir.rest.client.interceptor.LoggingInterceptor; import ca.uhn.fhir.rest.param.DateRangeParam; -import ca.uhn.fhir.test.utilities.JettyUtil; -import com.google.common.collect.ArrayListMultimap; -import com.google.common.collect.Lists; -import com.google.common.collect.Maps; -import org.apache.commons.io.Charsets; +import ca.uhn.fhir.test.utilities.server.HttpServletExtension; +import ca.uhn.fhir.test.utilities.server.RequestCaptureServlet; import org.apache.commons.io.IOUtils; -import org.eclipse.jetty.server.Request; -import org.eclipse.jetty.server.Server; -import org.eclipse.jetty.server.handler.AbstractHandler; import org.hl7.fhir.instance.model.api.IBase; import org.hl7.fhir.instance.model.api.IBaseBundle; import org.hl7.fhir.instance.model.api.IBaseOperationOutcome; import org.hl7.fhir.instance.model.api.IBaseResource; -import org.junit.jupiter.api.AfterAll; -import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; -import javax.servlet.ServletException; -import javax.servlet.http.HttpServletRequest; -import javax.servlet.http.HttpServletResponse; +import org.junit.jupiter.api.extension.RegisterExtension; + import java.io.IOException; import java.util.Date; -import java.util.Enumeration; import java.util.List; -import java.util.Map; import static org.hamcrest.MatcherAssert.assertThat; import static org.hamcrest.Matchers.containsString; @@ -76,23 +64,14 @@ import static org.junit.jupiter.api.Assertions.fail; public class GenericOkHttpClientDstu2Test { private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(GenericOkHttpClientDstu2Test.class); - private static FhirContext ourCtx; - private static int ourPort; - private static Server ourServer; - private static int ourResponseCount = 0; - private static String[] ourResponseBodies; - private static String ourResponseBody; - private static String ourResponseContentType; - private static int ourResponseStatus; - private static String ourRequestUri; - private static List ourRequestUriAll; - private static String ourRequestMethod; - private static String ourRequestContentType; - private static byte[] ourRequestBodyBytes; - private static String ourRequestBodyString; - private static ArrayListMultimap ourRequestHeaders; - private static List> ourRequestHeadersAll; - private static Map ourRequestFirstHeaders; + private static final FhirContext ourCtx = FhirContext.forDstu2Cached(); + + private static final RequestCaptureServlet MY_SERVLET = new RequestCaptureServlet(); + + @RegisterExtension + public static final HttpServletExtension ourServer = new HttpServletExtension() + .withServlet(MY_SERVLET) + .keepAliveBetweenTests(); /** * This suite of tests can be reconfigured to test a different RestfulClientFactory implementation by @@ -115,9 +94,10 @@ public class GenericOkHttpClientDstu2Test { clientFactory.setServerValidationMode(ServerValidationModeEnum.NEVER); ourCtx.setRestfulClientFactory(clientFactory); - ourResponseCount = 0; + MY_SERVLET.reset(); } + @Test public void testProviderWhereWeForgotToSetTheContext() throws Exception { RestfulClientFactory clientFactory = createNewClientFactoryForTesting(null); @@ -126,7 +106,7 @@ public class GenericOkHttpClientDstu2Test { ourCtx.setRestfulClientFactory(clientFactory); try { - ourCtx.newRestfulGenericClient("http://localhost:" + ourPort + "/fhir"); + ourCtx.newRestfulGenericClient(ourServer.getBaseUrl() + "/fhir"); fail(); } catch (IllegalStateException e) { String factoryClassName = clientFactory.getClass().getSimpleName(); @@ -163,25 +143,25 @@ public class GenericOkHttpClientDstu2Test { conf.setCopyright("COPY"); final String respString = p.encodeResourceToString(conf); - ourResponseContentType = Constants.CT_FHIR_XML + "; charset=UTF-8"; - ourResponseBody = respString; + MY_SERVLET.ourResponseContentType = Constants.CT_FHIR_XML + "; charset=UTF-8"; + MY_SERVLET.ourResponseBody = respString; - IGenericClient client = ourCtx.newRestfulGenericClient("http://localhost:" + ourPort + "/fhir"); + IGenericClient client = ourCtx.newRestfulGenericClient(ourServer.getBaseUrl() + "/fhir"); client.fetchConformance().ofType(Conformance.class).execute(); - assertEquals("http://localhost:" + ourPort + "/fhir/metadata", ourRequestUri); - assertEquals(1, ourRequestHeaders.get("Accept").size()); - assertThat(ourRequestHeaders.get("Accept").get(0).getValue(), containsString(Constants.HEADER_ACCEPT_VALUE_XML_OR_JSON_LEGACY)); + assertEquals(ourServer.getBaseUrl() + "/fhir/metadata", MY_SERVLET.ourRequestUri); + assertEquals(1, MY_SERVLET.ourRequestHeaders.get("Accept").size()); + assertThat(MY_SERVLET.ourRequestHeaders.get("Accept").get(0).getValue(), containsString(Constants.HEADER_ACCEPT_VALUE_XML_OR_JSON_LEGACY)); client.fetchConformance().ofType(Conformance.class).encodedJson().execute(); - assertEquals("http://localhost:" + ourPort + "/fhir/metadata?_format=json", ourRequestUri); - assertEquals(1, ourRequestHeaders.get("Accept").size()); - assertThat(ourRequestHeaders.get("Accept").get(0).getValue(), containsString(Constants.CT_FHIR_JSON)); + assertEquals(ourServer.getBaseUrl() + "/fhir/metadata?_format=json", MY_SERVLET.ourRequestUri); + assertEquals(1, MY_SERVLET.ourRequestHeaders.get("Accept").size()); + assertThat(MY_SERVLET.ourRequestHeaders.get("Accept").get(0).getValue(), containsString(Constants.CT_FHIR_JSON)); client.fetchConformance().ofType(Conformance.class).encodedXml().execute(); - assertEquals("http://localhost:" + ourPort + "/fhir/metadata?_format=xml", ourRequestUri); - assertEquals(1, ourRequestHeaders.get("Accept").size()); - assertThat(ourRequestHeaders.get("Accept").get(0).getValue(), containsString(Constants.CT_FHIR_XML)); + assertEquals(ourServer.getBaseUrl() + "/fhir/metadata?_format=xml", MY_SERVLET.ourRequestUri); + assertEquals(1, MY_SERVLET.ourRequestHeaders.get("Accept").size()); + assertThat(MY_SERVLET.ourRequestHeaders.get("Accept").get(0).getValue(), containsString(Constants.CT_FHIR_XML)); } @Test @@ -194,24 +174,24 @@ public class GenericOkHttpClientDstu2Test { final Patient patient = new Patient(); patient.addName().addFamily("FAMILY"); - ourResponseContentType = Constants.CT_FHIR_XML + "; charset=UTF-8"; - ourResponseBodies = new String[]{p.encodeResourceToString(conf), p.encodeResourceToString(patient)}; + MY_SERVLET.ourResponseContentType = Constants.CT_FHIR_XML + "; charset=UTF-8"; + MY_SERVLET.ourResponseBodies = new String[]{p.encodeResourceToString(conf), p.encodeResourceToString(patient)}; ourCtx.getRestfulClientFactory().setServerValidationMode(ServerValidationModeEnum.ONCE); - IGenericClient client = ourCtx.newRestfulGenericClient("http://localhost:" + ourPort + "/fhir"); + IGenericClient client = ourCtx.newRestfulGenericClient(ourServer.getBaseUrl() + "/fhir"); Patient resp = client.read(Patient.class, new IdDt("123")); assertEquals("FAMILY", resp.getName().get(0).getFamily().get(0).getValue()); - assertEquals("http://localhost:" + ourPort + "/fhir/metadata", ourRequestUriAll.get(0)); - assertEquals(1, ourRequestHeadersAll.get(0).get("Accept").size()); - assertThat(ourRequestHeadersAll.get(0).get("Accept").get(0).getValue(), containsString(Constants.HEADER_ACCEPT_VALUE_XML_OR_JSON_LEGACY)); - assertThat(ourRequestHeadersAll.get(0).get("Accept").get(0).getValue(), containsString(Constants.CT_FHIR_XML)); - assertThat(ourRequestHeadersAll.get(0).get("Accept").get(0).getValue(), containsString(Constants.CT_FHIR_JSON)); - assertEquals("http://localhost:" + ourPort + "/fhir/Patient/123", ourRequestUriAll.get(1)); - assertEquals(1, ourRequestHeadersAll.get(1).get("Accept").size()); - assertThat(ourRequestHeadersAll.get(1).get("Accept").get(0).getValue(), containsString(Constants.HEADER_ACCEPT_VALUE_XML_OR_JSON_LEGACY)); - assertThat(ourRequestHeadersAll.get(1).get("Accept").get(0).getValue(), containsString(Constants.CT_FHIR_XML)); - assertThat(ourRequestHeadersAll.get(1).get("Accept").get(0).getValue(), containsString(Constants.CT_FHIR_JSON)); + assertEquals(ourServer.getBaseUrl() + "/fhir/metadata", MY_SERVLET.ourRequestUriAll.get(0)); + assertEquals(1, MY_SERVLET.ourRequestHeadersAll.get(0).get("Accept").size()); + assertThat(MY_SERVLET.ourRequestHeadersAll.get(0).get("Accept").get(0).getValue(), containsString(Constants.HEADER_ACCEPT_VALUE_XML_OR_JSON_LEGACY)); + assertThat(MY_SERVLET.ourRequestHeadersAll.get(0).get("Accept").get(0).getValue(), containsString(Constants.CT_FHIR_XML)); + assertThat(MY_SERVLET.ourRequestHeadersAll.get(0).get("Accept").get(0).getValue(), containsString(Constants.CT_FHIR_JSON)); + assertEquals(ourServer.getBaseUrl() + "/fhir/Patient/123", MY_SERVLET.ourRequestUriAll.get(1)); + assertEquals(1, MY_SERVLET.ourRequestHeadersAll.get(1).get("Accept").size()); + assertThat(MY_SERVLET.ourRequestHeadersAll.get(1).get("Accept").get(0).getValue(), containsString(Constants.HEADER_ACCEPT_VALUE_XML_OR_JSON_LEGACY)); + assertThat(MY_SERVLET.ourRequestHeadersAll.get(1).get("Accept").get(0).getValue(), containsString(Constants.CT_FHIR_XML)); + assertThat(MY_SERVLET.ourRequestHeadersAll.get(1).get("Accept").get(0).getValue(), containsString(Constants.CT_FHIR_JSON)); } @Test @@ -224,23 +204,23 @@ public class GenericOkHttpClientDstu2Test { final Patient patient = new Patient(); patient.addName().addFamily("FAMILY"); - ourResponseContentType = Constants.CT_FHIR_XML + "; charset=UTF-8"; - ourResponseBodies = new String[]{p.encodeResourceToString(conf), p.encodeResourceToString(patient)}; + MY_SERVLET.ourResponseContentType = Constants.CT_FHIR_XML + "; charset=UTF-8"; + MY_SERVLET.ourResponseBodies = new String[]{p.encodeResourceToString(conf), p.encodeResourceToString(patient)}; ourCtx.getRestfulClientFactory().setServerValidationMode(ServerValidationModeEnum.ONCE); - IGenericClient client = ourCtx.newRestfulGenericClient("http://localhost:" + ourPort + "/fhir"); + IGenericClient client = ourCtx.newRestfulGenericClient(ourServer.getBaseUrl() + "/fhir"); client.setEncoding(EncodingEnum.JSON); Patient resp = client.read(Patient.class, new IdDt("123")); assertEquals("FAMILY", resp.getName().get(0).getFamily().get(0).getValue()); - assertEquals("http://localhost:" + ourPort + "/fhir/metadata?_format=json", ourRequestUriAll.get(0)); - assertEquals(1, ourRequestHeadersAll.get(0).get("Accept").size()); - assertThat(ourRequestHeadersAll.get(0).get("Accept").get(0).getValue(), containsString(Constants.CT_FHIR_JSON)); - assertThat(ourRequestHeadersAll.get(0).get("Accept").get(0).getValue(), not(containsString(Constants.CT_FHIR_XML))); - assertEquals("http://localhost:" + ourPort + "/fhir/Patient/123?_format=json", ourRequestUriAll.get(1)); - assertEquals(1, ourRequestHeadersAll.get(1).get("Accept").size()); - assertThat(ourRequestHeadersAll.get(1).get("Accept").get(0).getValue(), containsString(Constants.CT_FHIR_JSON)); - assertThat(ourRequestHeadersAll.get(1).get("Accept").get(0).getValue(), not(containsString(Constants.CT_FHIR_XML))); + assertEquals(ourServer.getBaseUrl() + "/fhir/metadata?_format=json", MY_SERVLET.ourRequestUriAll.get(0)); + assertEquals(1, MY_SERVLET.ourRequestHeadersAll.get(0).get("Accept").size()); + assertThat(MY_SERVLET.ourRequestHeadersAll.get(0).get("Accept").get(0).getValue(), containsString(Constants.CT_FHIR_JSON)); + assertThat(MY_SERVLET.ourRequestHeadersAll.get(0).get("Accept").get(0).getValue(), not(containsString(Constants.CT_FHIR_XML))); + assertEquals(ourServer.getBaseUrl() + "/fhir/Patient/123?_format=json", MY_SERVLET.ourRequestUriAll.get(1)); + assertEquals(1, MY_SERVLET.ourRequestHeadersAll.get(1).get("Accept").size()); + assertThat(MY_SERVLET.ourRequestHeadersAll.get(1).get("Accept").get(0).getValue(), containsString(Constants.CT_FHIR_JSON)); + assertThat(MY_SERVLET.ourRequestHeadersAll.get(1).get("Accept").get(0).getValue(), not(containsString(Constants.CT_FHIR_XML))); } @Test @@ -253,78 +233,78 @@ public class GenericOkHttpClientDstu2Test { final String respString = p.encodeResourceToString(conf); - ourResponseContentType = Constants.CT_FHIR_XML + "; charset=UTF-8"; - ourResponseBody = respString; + MY_SERVLET.ourResponseContentType = Constants.CT_FHIR_XML + "; charset=UTF-8"; + MY_SERVLET.ourResponseBody = respString; - IGenericClient client = ourCtx.newRestfulGenericClient("http://localhost:" + ourPort + "/fhir"); + IGenericClient client = ourCtx.newRestfulGenericClient(ourServer.getBaseUrl() + "/fhir"); Conformance resp = (Conformance) client.capabilities().ofType(Conformance.class).execute(); - assertEquals("http://localhost:" + ourPort + "/fhir/metadata", ourRequestUri); + assertEquals(ourServer.getBaseUrl() + "/fhir/metadata", MY_SERVLET.ourRequestUri); assertEquals("COPY", resp.getCopyright()); - assertEquals("GET", ourRequestMethod); + assertEquals("GET", MY_SERVLET.ourRequestMethod); } @Test public void testCreate() throws Exception { - IGenericClient client = ourCtx.newRestfulGenericClient("http://localhost:" + ourPort + "/fhir"); + IGenericClient client = ourCtx.newRestfulGenericClient(ourServer.getBaseUrl() + "/fhir"); Patient p = new Patient(); p.addName().addFamily("FOOFAMILY"); client.create().resource(p).encodedXml().execute(); - assertEquals(1, ourRequestHeaders.get(Constants.HEADER_CONTENT_TYPE).size()); + assertEquals(1, MY_SERVLET.ourRequestHeaders.get(Constants.HEADER_CONTENT_TYPE).size()); assertContentTypeEquals(EncodingEnum.XML.getResourceContentType() + Constants.HEADER_SUFFIX_CT_UTF_8); - assertThat(ourRequestBodyString, containsString("")); - assertEquals("http://localhost:" + ourPort + "/fhir/Patient", ourRequestUri); - assertEquals("POST", ourRequestMethod); + assertThat(MY_SERVLET.ourRequestBodyString, containsString("")); + assertEquals(ourServer.getBaseUrl() + "/fhir/Patient", MY_SERVLET.ourRequestUri); + assertEquals("POST", MY_SERVLET.ourRequestMethod); p.setId("123"); client.create().resource(p).encodedXml().execute(); - assertEquals(1, ourRequestHeaders.get(Constants.HEADER_CONTENT_TYPE).size()); + assertEquals(1, MY_SERVLET.ourRequestHeaders.get(Constants.HEADER_CONTENT_TYPE).size()); assertContentTypeEquals(EncodingEnum.XML.getResourceContentType() + Constants.HEADER_SUFFIX_CT_UTF_8); - String body = ourRequestBodyString; + String body = MY_SERVLET.ourRequestBodyString; assertThat(body, containsString("")); assertThat(body, not(containsString("123"))); - assertEquals("http://localhost:" + ourPort + "/fhir/Patient", ourRequestUri); - assertEquals("POST", ourRequestMethod); + assertEquals(ourServer.getBaseUrl() + "/fhir/Patient", MY_SERVLET.ourRequestUri); + assertEquals("POST", MY_SERVLET.ourRequestMethod); } @Test public void testCreateConditional() throws Exception { - ourResponseStatus = Constants.STATUS_HTTP_204_NO_CONTENT; + MY_SERVLET.ourResponseStatus = Constants.STATUS_HTTP_204_NO_CONTENT; - IGenericClient client = ourCtx.newRestfulGenericClient("http://localhost:" + ourPort + "/fhir"); + IGenericClient client = ourCtx.newRestfulGenericClient(ourServer.getBaseUrl() + "/fhir"); Patient p = new Patient(); p.addName().addFamily("FOOFAMILY"); client.create().resource(p).conditionalByUrl("Patient?name=foo").encodedXml().execute(); - assertEquals(1, ourRequestHeaders.get(Constants.HEADER_CONTENT_TYPE).size()); + assertEquals(1, MY_SERVLET.ourRequestHeaders.get(Constants.HEADER_CONTENT_TYPE).size()); String expectedContentTypeHeader = EncodingEnum.XML.getResourceContentType() + Constants.HEADER_SUFFIX_CT_UTF_8; assertContentTypeEquals(expectedContentTypeHeader); - assertThat(ourRequestBodyString, containsString("")); - assertEquals("http://localhost:" + ourPort + "/fhir/Patient", ourRequestUri); - assertEquals("http://localhost:" + ourPort + "/fhir/Patient?name=foo", ourRequestFirstHeaders.get(Constants.HEADER_IF_NONE_EXIST).getValue()); - assertEquals("POST", ourRequestMethod); + assertThat(MY_SERVLET.ourRequestBodyString, containsString("")); + assertEquals(ourServer.getBaseUrl() + "/fhir/Patient", MY_SERVLET.ourRequestUri); + assertEquals(ourServer.getBaseUrl() + "/fhir/Patient?name=foo", MY_SERVLET.ourRequestFirstHeaders.get(Constants.HEADER_IF_NONE_EXIST).getValue()); + assertEquals("POST", MY_SERVLET.ourRequestMethod); client.create().resource(p).conditionalByUrl("Patient?name=http://foo|bar").encodedXml().execute(); - assertEquals(1, ourRequestHeaders.get(Constants.HEADER_CONTENT_TYPE).size()); + assertEquals(1, MY_SERVLET.ourRequestHeaders.get(Constants.HEADER_CONTENT_TYPE).size()); assertContentTypeEquals(expectedContentTypeHeader); - assertThat(ourRequestBodyString, containsString("")); - assertEquals("http://localhost:" + ourPort + "/fhir/Patient", ourRequestUri); - assertEquals("http://localhost:" + ourPort + "/fhir/Patient?name=http%3A//foo%7Cbar", ourRequestFirstHeaders.get(Constants.HEADER_IF_NONE_EXIST).getValue()); - assertEquals("POST", ourRequestMethod); + assertThat(MY_SERVLET.ourRequestBodyString, containsString("")); + assertEquals(ourServer.getBaseUrl() + "/fhir/Patient", MY_SERVLET.ourRequestUri); + assertEquals(ourServer.getBaseUrl() + "/fhir/Patient?name=http%3A//foo%7Cbar", MY_SERVLET.ourRequestFirstHeaders.get(Constants.HEADER_IF_NONE_EXIST).getValue()); + assertEquals("POST", MY_SERVLET.ourRequestMethod); client.create().resource(p).conditional().where(Patient.NAME.matches().value("foo")).encodedXml().execute(); - assertEquals(1, ourRequestHeaders.get(Constants.HEADER_CONTENT_TYPE).size()); + assertEquals(1, MY_SERVLET.ourRequestHeaders.get(Constants.HEADER_CONTENT_TYPE).size()); assertContentTypeEquals(expectedContentTypeHeader); - assertThat(ourRequestBodyString, containsString("")); - assertEquals("http://localhost:" + ourPort + "/fhir/Patient", ourRequestUri); - assertEquals("http://localhost:" + ourPort + "/fhir/Patient?name=foo", ourRequestFirstHeaders.get(Constants.HEADER_IF_NONE_EXIST).getValue()); - assertEquals("POST", ourRequestMethod); + assertThat(MY_SERVLET.ourRequestBodyString, containsString("")); + assertEquals(ourServer.getBaseUrl() + "/fhir/Patient", MY_SERVLET.ourRequestUri); + assertEquals(ourServer.getBaseUrl() + "/fhir/Patient?name=foo", MY_SERVLET.ourRequestFirstHeaders.get(Constants.HEADER_IF_NONE_EXIST).getValue()); + assertEquals("POST", MY_SERVLET.ourRequestMethod); } private void assertContentTypeEquals(String expectedContentTypeHeader) { @@ -335,20 +315,20 @@ public class GenericOkHttpClientDstu2Test { @Test public void testCreatePrefer() throws Exception { - ourResponseStatus = Constants.STATUS_HTTP_204_NO_CONTENT; + MY_SERVLET.ourResponseStatus = Constants.STATUS_HTTP_204_NO_CONTENT; - IGenericClient client = ourCtx.newRestfulGenericClient("http://localhost:" + ourPort + "/fhir"); + IGenericClient client = ourCtx.newRestfulGenericClient(ourServer.getBaseUrl() + "/fhir"); Patient p = new Patient(); p.addName().addFamily("FOOFAMILY"); client.create().resource(p).prefer(PreferReturnEnum.MINIMAL).execute(); - assertEquals(1, ourRequestHeaders.get(Constants.HEADER_PREFER).size()); - assertEquals(Constants.HEADER_PREFER_RETURN + '=' + Constants.HEADER_PREFER_RETURN_MINIMAL, ourRequestHeaders.get(Constants.HEADER_PREFER).get(0).getValue()); + assertEquals(1, MY_SERVLET.ourRequestHeaders.get(Constants.HEADER_PREFER).size()); + assertEquals(Constants.HEADER_PREFER_RETURN + '=' + Constants.HEADER_PREFER_RETURN_MINIMAL, MY_SERVLET.ourRequestHeaders.get(Constants.HEADER_PREFER).get(0).getValue()); client.create().resource(p).prefer(PreferReturnEnum.REPRESENTATION).execute(); - assertEquals(1, ourRequestHeaders.get(Constants.HEADER_PREFER).size()); - assertEquals(Constants.HEADER_PREFER_RETURN + '=' + Constants.HEADER_PREFER_RETURN_REPRESENTATION, ourRequestHeaders.get(Constants.HEADER_PREFER).get(0).getValue()); + assertEquals(1, MY_SERVLET.ourRequestHeaders.get(Constants.HEADER_PREFER).size()); + assertEquals(Constants.HEADER_PREFER_RETURN + '=' + Constants.HEADER_PREFER_RETURN_REPRESENTATION, MY_SERVLET.ourRequestHeaders.get(Constants.HEADER_PREFER).get(0).getValue()); } @Test @@ -357,11 +337,11 @@ public class GenericOkHttpClientDstu2Test { p.setId("123"); final String formatted = ourCtx.newXmlParser().encodeResourceToString(p); - ourResponseStatus = Constants.STATUS_HTTP_200_OK; - ourResponseContentType = Constants.CT_FHIR_XML + "; charset=UTF-8"; - ourResponseBody = formatted; + MY_SERVLET.ourResponseStatus = Constants.STATUS_HTTP_200_OK; + MY_SERVLET.ourResponseContentType = Constants.CT_FHIR_XML + "; charset=UTF-8"; + MY_SERVLET.ourResponseBody = formatted; - IGenericClient client = ourCtx.newRestfulGenericClient("http://localhost:" + ourPort + "/fhir"); + IGenericClient client = ourCtx.newRestfulGenericClient(ourServer.getBaseUrl() + "/fhir"); p = new Patient(); p.setId(new IdDt("1")); @@ -374,33 +354,33 @@ public class GenericOkHttpClientDstu2Test { @Test public void testDeleteConditional() throws Exception { - ourResponseStatus = Constants.STATUS_HTTP_204_NO_CONTENT; + MY_SERVLET.ourResponseStatus = Constants.STATUS_HTTP_204_NO_CONTENT; - IGenericClient client = ourCtx.newRestfulGenericClient("http://localhost:" + ourPort + "/fhir"); + IGenericClient client = ourCtx.newRestfulGenericClient(ourServer.getBaseUrl() + "/fhir"); client.delete().resourceById(new IdDt("Patient/123")).execute(); - assertEquals("DELETE", ourRequestMethod); - assertEquals("http://localhost:" + ourPort + "/fhir/Patient/123", ourRequestUri); + assertEquals("DELETE", MY_SERVLET.ourRequestMethod); + assertEquals(ourServer.getBaseUrl() + "/fhir/Patient/123", MY_SERVLET.ourRequestUri); client.delete().resourceConditionalByUrl("Patient?name=foo").execute(); - assertEquals("DELETE", ourRequestMethod); - assertEquals("http://localhost:" + ourPort + "/fhir/Patient?name=foo", ourRequestUri); + assertEquals("DELETE", MY_SERVLET.ourRequestMethod); + assertEquals(ourServer.getBaseUrl() + "/fhir/Patient?name=foo", MY_SERVLET.ourRequestUri); client.delete().resourceConditionalByType("Patient").where(Patient.NAME.matches().value("foo")).execute(); - assertEquals("DELETE", ourRequestMethod); - assertEquals("http://localhost:" + ourPort + "/fhir/Patient?name=foo", ourRequestUri); + assertEquals("DELETE", MY_SERVLET.ourRequestMethod); + assertEquals(ourServer.getBaseUrl() + "/fhir/Patient?name=foo", MY_SERVLET.ourRequestUri); } @SuppressWarnings("deprecation") @Test public void testDeleteNonFluent() throws Exception { - ourResponseStatus = Constants.STATUS_HTTP_204_NO_CONTENT; + MY_SERVLET.ourResponseStatus = Constants.STATUS_HTTP_204_NO_CONTENT; - IGenericClient client = ourCtx.newRestfulGenericClient("http://localhost:" + ourPort + "/fhir"); + IGenericClient client = ourCtx.newRestfulGenericClient(ourServer.getBaseUrl() + "/fhir"); client.delete().resourceById(new IdDt("Patient/123")).execute(); - assertEquals("DELETE", ourRequestMethod); - assertEquals("http://localhost:" + ourPort + "/fhir/Patient/123", ourRequestUri); + assertEquals("DELETE", MY_SERVLET.ourRequestMethod); + assertEquals(ourServer.getBaseUrl() + "/fhir/Patient/123", MY_SERVLET.ourRequestUri); } @@ -408,10 +388,10 @@ public class GenericOkHttpClientDstu2Test { public void testHistory() throws Exception { final String msg = getPatientFeedWithOneResult(); - ourResponseContentType = Constants.CT_FHIR_XML + "; charset=UTF-8"; - ourResponseBody = msg; + MY_SERVLET.ourResponseContentType = Constants.CT_FHIR_XML + "; charset=UTF-8"; + MY_SERVLET.ourResponseBody = msg; - IGenericClient client = ourCtx.newRestfulGenericClient("http://localhost:" + ourPort + "/fhir"); + IGenericClient client = ourCtx.newRestfulGenericClient(ourServer.getBaseUrl() + "/fhir"); ca.uhn.fhir.model.dstu2.resource.Bundle response; @@ -421,7 +401,7 @@ public class GenericOkHttpClientDstu2Test { .andReturnBundle(ca.uhn.fhir.model.dstu2.resource.Bundle.class) .execute(); - assertEquals("http://localhost:" + ourPort + "/fhir/_history", ourRequestUri); + assertEquals(ourServer.getBaseUrl() + "/fhir/_history", MY_SERVLET.ourRequestUri); assertEquals(1, response.getEntry().size()); response = client @@ -432,7 +412,7 @@ public class GenericOkHttpClientDstu2Test { .count(null) .execute(); - assertEquals("http://localhost:" + ourPort + "/fhir/_history", ourRequestUri); + assertEquals(ourServer.getBaseUrl() + "/fhir/_history", MY_SERVLET.ourRequestUri); assertEquals(1, response.getEntry().size()); response = client @@ -442,7 +422,7 @@ public class GenericOkHttpClientDstu2Test { .since(new InstantDt()) .execute(); - assertEquals("http://localhost:" + ourPort + "/fhir/_history", ourRequestUri); + assertEquals(ourServer.getBaseUrl() + "/fhir/_history", MY_SERVLET.ourRequestUri); assertEquals(1, response.getEntry().size()); response = client @@ -451,7 +431,7 @@ public class GenericOkHttpClientDstu2Test { .andReturnBundle(ca.uhn.fhir.model.dstu2.resource.Bundle.class) .execute(); - assertEquals("http://localhost:" + ourPort + "/fhir/Patient/_history", ourRequestUri); + assertEquals(ourServer.getBaseUrl() + "/fhir/Patient/_history", MY_SERVLET.ourRequestUri); assertEquals(1, response.getEntry().size()); response = client @@ -460,7 +440,7 @@ public class GenericOkHttpClientDstu2Test { .andReturnBundle(ca.uhn.fhir.model.dstu2.resource.Bundle.class) .execute(); - assertEquals("http://localhost:" + ourPort + "/fhir/Patient/123/_history", ourRequestUri); + assertEquals(ourServer.getBaseUrl() + "/fhir/Patient/123/_history", MY_SERVLET.ourRequestUri); assertEquals(1, response.getEntry().size()); response = client @@ -471,8 +451,8 @@ public class GenericOkHttpClientDstu2Test { .since(new InstantDt("2001-01-02T11:22:33Z")) .execute(); - assertThat(ourRequestUri, either(equalTo("http://localhost:" + ourPort + "/fhir/Patient/123/_history?_since=2001-01-02T11:22:33Z&_count=123")) - .or(equalTo("http://localhost:" + ourPort + "/fhir/Patient/123/_history?_count=123&_since=2001-01-02T11:22:33Z"))); + assertThat(MY_SERVLET.ourRequestUri, either(equalTo(ourServer.getBaseUrl() + "/fhir/Patient/123/_history?_since=2001-01-02T11:22:33Z&_count=123")) + .or(equalTo(ourServer.getBaseUrl() + "/fhir/Patient/123/_history?_count=123&_since=2001-01-02T11:22:33Z"))); assertEquals(1, response.getEntry().size()); response = client @@ -482,7 +462,7 @@ public class GenericOkHttpClientDstu2Test { .since(new InstantDt("2001-01-02T11:22:33Z").getValue()) .execute(); - assertThat(ourRequestUri, containsString("_since=2001-01")); + assertThat(MY_SERVLET.ourRequestUri, containsString("_since=2001-01")); assertEquals(1, response.getEntry().size()); } @@ -496,10 +476,10 @@ public class GenericOkHttpClientDstu2Test { outParams.addParameter().setName("meta").setValue(new MetaDt().addProfile("urn:profile:out")); final String respString = p.encodeResourceToString(outParams); - ourResponseContentType = Constants.CT_FHIR_XML + "; charset=UTF-8"; - ourResponseBody = respString; + MY_SERVLET.ourResponseContentType = Constants.CT_FHIR_XML + "; charset=UTF-8"; + MY_SERVLET.ourResponseBody = respString; - IGenericClient client = ourCtx.newRestfulGenericClient("http://localhost:" + ourPort + "/fhir"); + IGenericClient client = ourCtx.newRestfulGenericClient(ourServer.getBaseUrl() + "/fhir"); MetaDt resp = client .meta() @@ -509,11 +489,11 @@ public class GenericOkHttpClientDstu2Test { .encodedXml() .execute(); - assertEquals("http://localhost:" + ourPort + "/fhir/Patient/123/$meta-add", ourRequestUri); + assertEquals(ourServer.getBaseUrl() + "/fhir/Patient/123/$meta-add", MY_SERVLET.ourRequestUri); assertEquals("urn:profile:out", resp.getProfile().get(0).getValue()); - assertEquals("POST", ourRequestMethod); + assertEquals("POST", MY_SERVLET.ourRequestMethod); assertEquals("", - ourRequestBodyString); + MY_SERVLET.ourRequestBodyString); } @Test @@ -527,10 +507,10 @@ public class GenericOkHttpClientDstu2Test { outParams.addParameter().setName("meta").setValue(new MetaDt().addProfile("urn:profile:out")); final String respString = p.encodeResourceToString(outParams); - ourResponseContentType = Constants.CT_FHIR_XML + "; charset=UTF-8"; - ourResponseBody = respString; + MY_SERVLET.ourResponseContentType = Constants.CT_FHIR_XML + "; charset=UTF-8"; + MY_SERVLET.ourResponseBody = respString; - IGenericClient client = ourCtx.newRestfulGenericClient("http://localhost:" + ourPort + "/fhir"); + IGenericClient client = ourCtx.newRestfulGenericClient(ourServer.getBaseUrl() + "/fhir"); MetaDt resp = client .meta() @@ -538,9 +518,9 @@ public class GenericOkHttpClientDstu2Test { .fromServer() .execute(); - assertEquals("http://localhost:" + ourPort + "/fhir/$meta", ourRequestUri); + assertEquals(ourServer.getBaseUrl() + "/fhir/$meta", MY_SERVLET.ourRequestUri); assertEquals("urn:profile:out", resp.getProfile().get(0).getValue()); - assertEquals("GET", ourRequestMethod); + assertEquals("GET", MY_SERVLET.ourRequestMethod); resp = client .meta() @@ -548,9 +528,9 @@ public class GenericOkHttpClientDstu2Test { .fromType("Patient") .execute(); - assertEquals("http://localhost:" + ourPort + "/fhir/Patient/$meta", ourRequestUri); + assertEquals(ourServer.getBaseUrl() + "/fhir/Patient/$meta", MY_SERVLET.ourRequestUri); assertEquals("urn:profile:out", resp.getProfile().get(0).getValue()); - assertEquals("GET", ourRequestMethod); + assertEquals("GET", MY_SERVLET.ourRequestMethod); resp = client .meta() @@ -558,9 +538,9 @@ public class GenericOkHttpClientDstu2Test { .fromResource(new IdDt("Patient/123")) .execute(); - assertEquals("http://localhost:" + ourPort + "/fhir/Patient/123/$meta", ourRequestUri); + assertEquals(ourServer.getBaseUrl() + "/fhir/Patient/123/$meta", MY_SERVLET.ourRequestUri); assertEquals("urn:profile:out", resp.getProfile().get(0).getValue()); - assertEquals("GET", ourRequestMethod); + assertEquals("GET", MY_SERVLET.ourRequestMethod); } @Test @@ -577,10 +557,10 @@ public class GenericOkHttpClientDstu2Test { outParams.addParameter().setValue(new StringDt("STRINGVALOUT2")); final String respString = p.encodeResourceToString(outParams); - ourResponseContentType = Constants.CT_FHIR_XML + "; charset=UTF-8"; - ourResponseBody = respString; + MY_SERVLET.ourResponseContentType = Constants.CT_FHIR_XML + "; charset=UTF-8"; + MY_SERVLET.ourResponseBody = respString; - IGenericClient client = ourCtx.newRestfulGenericClient("http://localhost:" + ourPort + "/fhir"); + IGenericClient client = ourCtx.newRestfulGenericClient(ourServer.getBaseUrl() + "/fhir"); Parameters resp = client .operation() @@ -590,9 +570,9 @@ public class GenericOkHttpClientDstu2Test { .useHttpGet() .execute(); - assertEquals("http://localhost:" + ourPort + "/fhir/$SOMEOPERATION?param1=STRINGVALIN1¶m1=STRINGVALIN1b¶m2=STRINGVALIN2", ourRequestUri); + assertEquals(ourServer.getBaseUrl() + "/fhir/$SOMEOPERATION?param1=STRINGVALIN1¶m1=STRINGVALIN1b¶m2=STRINGVALIN2", MY_SERVLET.ourRequestUri); assertEquals(respString, p.encodeResourceToString(resp)); - assertEquals("GET", ourRequestMethod); + assertEquals("GET", MY_SERVLET.ourRequestMethod); resp = client .operation() @@ -602,9 +582,9 @@ public class GenericOkHttpClientDstu2Test { .useHttpGet() .execute(); - assertEquals("http://localhost:" + ourPort + "/fhir/Patient/$SOMEOPERATION?param1=STRINGVALIN1¶m1=STRINGVALIN1b¶m2=STRINGVALIN2", ourRequestUri); + assertEquals(ourServer.getBaseUrl() + "/fhir/Patient/$SOMEOPERATION?param1=STRINGVALIN1¶m1=STRINGVALIN1b¶m2=STRINGVALIN2", MY_SERVLET.ourRequestUri); assertEquals(respString, p.encodeResourceToString(resp)); - assertEquals("GET", ourRequestMethod); + assertEquals("GET", MY_SERVLET.ourRequestMethod); resp = client .operation() @@ -614,9 +594,9 @@ public class GenericOkHttpClientDstu2Test { .useHttpGet() .execute(); - assertEquals("http://localhost:" + ourPort + "/fhir/Patient/123/$SOMEOPERATION?param1=STRINGVALIN1¶m1=STRINGVALIN1b¶m2=STRINGVALIN2", ourRequestUri); + assertEquals(ourServer.getBaseUrl() + "/fhir/Patient/123/$SOMEOPERATION?param1=STRINGVALIN1¶m1=STRINGVALIN1b¶m2=STRINGVALIN2", MY_SERVLET.ourRequestUri); assertEquals(respString, p.encodeResourceToString(resp)); - assertEquals("GET", ourRequestMethod); + assertEquals("GET", MY_SERVLET.ourRequestMethod); resp = client .operation() @@ -625,7 +605,7 @@ public class GenericOkHttpClientDstu2Test { .withParameters(inParams) .useHttpGet() .execute(); - assertEquals("http://localhost:" + ourPort + "/fhir/Patient/123/$SOMEOPERATION?param1=STRINGVALIN1¶m1=STRINGVALIN1b¶m2=STRINGVALIN2", ourRequestUri); + assertEquals(ourServer.getBaseUrl() + "/fhir/Patient/123/$SOMEOPERATION?param1=STRINGVALIN1¶m1=STRINGVALIN1b¶m2=STRINGVALIN2", MY_SERVLET.ourRequestUri); } @Test @@ -637,10 +617,10 @@ public class GenericOkHttpClientDstu2Test { outParams.addParameter().setValue(new StringDt("STRINGVALOUT2")); final String respString = p.encodeResourceToString(outParams); - ourResponseContentType = Constants.CT_FHIR_XML + "; charset=UTF-8"; - ourResponseBody = respString; + MY_SERVLET.ourResponseContentType = Constants.CT_FHIR_XML + "; charset=UTF-8"; + MY_SERVLET.ourResponseBody = respString; - IGenericClient client = ourCtx.newRestfulGenericClient("http://localhost:" + ourPort + "/fhir"); + IGenericClient client = ourCtx.newRestfulGenericClient(ourServer.getBaseUrl() + "/fhir"); Parameters resp = client .operation() @@ -650,9 +630,9 @@ public class GenericOkHttpClientDstu2Test { .useHttpGet() .execute(); - assertEquals("http://localhost:" + ourPort + "/fhir/$SOMEOPERATION", ourRequestUri); + assertEquals(ourServer.getBaseUrl() + "/fhir/$SOMEOPERATION", MY_SERVLET.ourRequestUri); assertEquals(respString, p.encodeResourceToString(resp)); - assertEquals("GET", ourRequestMethod); + assertEquals("GET", MY_SERVLET.ourRequestMethod); resp = client .operation() @@ -662,9 +642,9 @@ public class GenericOkHttpClientDstu2Test { .useHttpGet() .execute(); - assertEquals("http://localhost:" + ourPort + "/fhir/Patient/$SOMEOPERATION", ourRequestUri); + assertEquals(ourServer.getBaseUrl() + "/fhir/Patient/$SOMEOPERATION", MY_SERVLET.ourRequestUri); assertEquals(respString, p.encodeResourceToString(resp)); - assertEquals("GET", ourRequestMethod); + assertEquals("GET", MY_SERVLET.ourRequestMethod); resp = client .operation() @@ -674,9 +654,9 @@ public class GenericOkHttpClientDstu2Test { .useHttpGet() .execute(); - assertEquals("http://localhost:" + ourPort + "/fhir/Patient/123/$SOMEOPERATION", ourRequestUri); + assertEquals(ourServer.getBaseUrl() + "/fhir/Patient/123/$SOMEOPERATION", MY_SERVLET.ourRequestUri); assertEquals(respString, p.encodeResourceToString(resp)); - assertEquals("GET", ourRequestMethod); + assertEquals("GET", MY_SERVLET.ourRequestMethod); resp = client .operation() @@ -685,17 +665,17 @@ public class GenericOkHttpClientDstu2Test { .withNoParameters(Parameters.class) .useHttpGet() .execute(); - assertEquals("http://localhost:" + ourPort + "/fhir/Patient/123/$SOMEOPERATION", ourRequestUri); + assertEquals(ourServer.getBaseUrl() + "/fhir/Patient/123/$SOMEOPERATION", MY_SERVLET.ourRequestUri); } @Test public void testOperationWithBundleResponseJson() throws Exception { - ourResponseContentType = Constants.CT_FHIR_JSON; - final String respString = "{\n" + " \"resourceType\":\"Bundle\",\n" + " \"id\":\"8cef5f2a-0ba9-43a5-be26-c8dde9ff0e19\",\n" + " \"base\":\"http://localhost:" + ourPort + "/fhir\"\n" + MY_SERVLET.ourResponseContentType = Constants.CT_FHIR_JSON; + final String respString = "{\n" + " \"resourceType\":\"Bundle\",\n" + " \"id\":\"8cef5f2a-0ba9-43a5-be26-c8dde9ff0e19\",\n" + " \"base\":\"" + ourServer.getBaseUrl() + "/fhir\"\n" + "}"; - ourResponseBody = respString; + MY_SERVLET.ourResponseBody = respString; - IGenericClient client = ourCtx.newRestfulGenericClient("http://localhost:" + ourPort + "/fhir"); + IGenericClient client = ourCtx.newRestfulGenericClient(ourServer.getBaseUrl() + "/fhir"); client.registerInterceptor(new LoggingInterceptor(true)); @@ -731,10 +711,10 @@ public class GenericOkHttpClientDstu2Test { outParams.setTotal(123); final String respString = p.encodeResourceToString(outParams); - ourResponseContentType = Constants.CT_FHIR_XML + "; charset=UTF-8"; - ourResponseBody = respString; + MY_SERVLET.ourResponseContentType = Constants.CT_FHIR_XML + "; charset=UTF-8"; + MY_SERVLET.ourResponseBody = respString; - IGenericClient client = ourCtx.newRestfulGenericClient("http://localhost:" + ourPort + "/fhir"); + IGenericClient client = ourCtx.newRestfulGenericClient(ourServer.getBaseUrl() + "/fhir"); Parameters resp = client .operation() @@ -742,11 +722,11 @@ public class GenericOkHttpClientDstu2Test { .named("$SOMEOPERATION") .withParameters(inParams).encodedXml().execute(); - assertEquals("http://localhost:" + ourPort + "/fhir/$SOMEOPERATION", ourRequestUri); - assertEquals(1, ourRequestHeaders.get(Constants.HEADER_CONTENT_TYPE).size()); + assertEquals(ourServer.getBaseUrl() + "/fhir/$SOMEOPERATION", MY_SERVLET.ourRequestUri); + assertEquals(1, MY_SERVLET.ourRequestHeaders.get(Constants.HEADER_CONTENT_TYPE).size()); assertContentTypeEquals(EncodingEnum.XML.getResourceContentType() + Constants.HEADER_SUFFIX_CT_UTF_8); - assertEquals(ourRequestBodyString, reqString); - assertEquals("POST", ourRequestMethod); + assertEquals(MY_SERVLET.ourRequestBodyString, reqString); + assertEquals("POST", MY_SERVLET.ourRequestMethod); assertEquals(1, resp.getParameter().size()); assertEquals(ca.uhn.fhir.model.dstu2.resource.Bundle.class, resp.getParameter().get(0).getResource().getClass()); } @@ -760,10 +740,10 @@ public class GenericOkHttpClientDstu2Test { outParams.addParameter().setValue(new StringDt("STRINGVALOUT2")); final String respString = p.encodeResourceToString(outParams); - ourResponseContentType = Constants.CT_FHIR_XML + "; charset=UTF-8"; - ourResponseBody = respString; + MY_SERVLET.ourResponseContentType = Constants.CT_FHIR_XML + "; charset=UTF-8"; + MY_SERVLET.ourResponseBody = respString; - IGenericClient client = ourCtx.newRestfulGenericClient("http://localhost:" + ourPort + "/fhir"); + IGenericClient client = ourCtx.newRestfulGenericClient(ourServer.getBaseUrl() + "/fhir"); Parameters resp = client .operation() @@ -774,14 +754,14 @@ public class GenericOkHttpClientDstu2Test { .encodedXml() .execute(); - assertEquals("http://localhost:" + ourPort + "/fhir/$SOMEOPERATION", ourRequestUri); + assertEquals(ourServer.getBaseUrl() + "/fhir/$SOMEOPERATION", MY_SERVLET.ourRequestUri); assertEquals(respString, p.encodeResourceToString(resp)); - assertEquals(1, ourRequestHeaders.get(Constants.HEADER_CONTENT_TYPE).size()); + assertEquals(1, MY_SERVLET.ourRequestHeaders.get(Constants.HEADER_CONTENT_TYPE).size()); assertContentTypeEquals(EncodingEnum.XML.getResourceContentType() + Constants.HEADER_SUFFIX_CT_UTF_8); - assertEquals("POST", ourRequestMethod); + assertEquals("POST", MY_SERVLET.ourRequestMethod); assertEquals( "", - (ourRequestBodyString)); + (MY_SERVLET.ourRequestBodyString)); /* * Composite type @@ -796,14 +776,14 @@ public class GenericOkHttpClientDstu2Test { .encodedXml() .execute(); - assertEquals("http://localhost:" + ourPort + "/fhir/$SOMEOPERATION", ourRequestUri); + assertEquals(ourServer.getBaseUrl() + "/fhir/$SOMEOPERATION", MY_SERVLET.ourRequestUri); assertEquals(respString, p.encodeResourceToString(resp)); - assertEquals(1, ourRequestHeaders.get(Constants.HEADER_CONTENT_TYPE).size()); + assertEquals(1, MY_SERVLET.ourRequestHeaders.get(Constants.HEADER_CONTENT_TYPE).size()); assertContentTypeEquals(EncodingEnum.XML.getResourceContentType() + Constants.HEADER_SUFFIX_CT_UTF_8); - assertEquals("POST", ourRequestMethod); + assertEquals("POST", MY_SERVLET.ourRequestMethod); assertEquals( "", - (ourRequestBodyString)); + (MY_SERVLET.ourRequestBodyString)); /* * Resource @@ -818,19 +798,19 @@ public class GenericOkHttpClientDstu2Test { .encodedXml() .execute(); - assertEquals("http://localhost:" + ourPort + "/fhir/$SOMEOPERATION", ourRequestUri); + assertEquals(ourServer.getBaseUrl() + "/fhir/$SOMEOPERATION", MY_SERVLET.ourRequestUri); assertEquals(respString, p.encodeResourceToString(resp)); - assertEquals(1, ourRequestHeaders.get(Constants.HEADER_CONTENT_TYPE).size()); + assertEquals(1, MY_SERVLET.ourRequestHeaders.get(Constants.HEADER_CONTENT_TYPE).size()); assertContentTypeEquals(EncodingEnum.XML.getResourceContentType() + Constants.HEADER_SUFFIX_CT_UTF_8); - assertEquals("POST", ourRequestMethod); + assertEquals("POST", MY_SERVLET.ourRequestMethod); assertEquals( "", - (ourRequestBodyString)); + (MY_SERVLET.ourRequestBodyString)); } @Test public void testOperationWithInvalidParam() { - IGenericClient client = ourCtx.newRestfulGenericClient("http://localhost:" + ourPort + "/fhir"); + IGenericClient client = ourCtx.newRestfulGenericClient(ourServer.getBaseUrl() + "/fhir"); // Who knows what the heck this is! IBase weirdBase = new IBase() { @@ -887,10 +867,10 @@ public class GenericOkHttpClientDstu2Test { outParams.addParameter().setValue(new StringDt("STRINGVALOUT2")); final String respString = p.encodeResourceToString(outParams); - ourResponseContentType = Constants.CT_FHIR_XML + "; charset=UTF-8"; - ourResponseBody = respString; + MY_SERVLET.ourResponseContentType = Constants.CT_FHIR_XML + "; charset=UTF-8"; + MY_SERVLET.ourResponseBody = respString; - IGenericClient client = ourCtx.newRestfulGenericClient("http://localhost:" + ourPort + "/fhir"); + IGenericClient client = ourCtx.newRestfulGenericClient(ourServer.getBaseUrl() + "/fhir"); client .operation() @@ -901,7 +881,7 @@ public class GenericOkHttpClientDstu2Test { .useHttpGet() .execute(); - assertEquals("http://localhost:" + ourPort + "/fhir/Patient/1/$validate-code?code=8495-4&system=http%3A%2F%2Floinc.org", ourRequestUri); + assertEquals(ourServer.getBaseUrl() + "/fhir/Patient/1/$validate-code?code=8495-4&system=http%3A%2F%2Floinc.org", MY_SERVLET.ourRequestUri); client .operation() @@ -912,11 +892,11 @@ public class GenericOkHttpClientDstu2Test { .encodedXml() .execute(); - assertEquals("http://localhost:" + ourPort + "/fhir/Patient/1/$validate-code", ourRequestUri); - ourLog.info(ourRequestBodyString); + assertEquals(ourServer.getBaseUrl() + "/fhir/Patient/1/$validate-code", MY_SERVLET.ourRequestUri); + ourLog.info(MY_SERVLET.ourRequestBodyString); assertEquals( "", - ourRequestBodyString); + MY_SERVLET.ourRequestBodyString); } @Test @@ -933,10 +913,10 @@ public class GenericOkHttpClientDstu2Test { outParams.addParameter().setValue(new StringDt("STRINGVALOUT2")); final String respString = p.encodeResourceToString(outParams); - ourResponseContentType = Constants.CT_FHIR_XML + "; charset=UTF-8"; - ourResponseBody = respString; + MY_SERVLET.ourResponseContentType = Constants.CT_FHIR_XML + "; charset=UTF-8"; + MY_SERVLET.ourResponseBody = respString; - IGenericClient client = ourCtx.newRestfulGenericClient("http://localhost:" + ourPort + "/fhir"); + IGenericClient client = ourCtx.newRestfulGenericClient(ourServer.getBaseUrl() + "/fhir"); Parameters resp = client .operation() @@ -944,12 +924,12 @@ public class GenericOkHttpClientDstu2Test { .named("$SOMEOPERATION") .withParameters(inParams).encodedXml().execute(); - assertEquals("http://localhost:" + ourPort + "/fhir/$SOMEOPERATION", ourRequestUri); + assertEquals(ourServer.getBaseUrl() + "/fhir/$SOMEOPERATION", MY_SERVLET.ourRequestUri); assertEquals(respString, p.encodeResourceToString(resp)); - assertEquals(1, ourRequestHeaders.get(Constants.HEADER_CONTENT_TYPE).size()); + assertEquals(1, MY_SERVLET.ourRequestHeaders.get(Constants.HEADER_CONTENT_TYPE).size()); assertContentTypeEquals(EncodingEnum.XML.getResourceContentType() + Constants.HEADER_SUFFIX_CT_UTF_8); - assertEquals(ourRequestBodyString, reqString); - assertEquals("POST", ourRequestMethod); + assertEquals(MY_SERVLET.ourRequestBodyString, reqString); + assertEquals("POST", MY_SERVLET.ourRequestMethod); resp = client .operation() @@ -957,12 +937,12 @@ public class GenericOkHttpClientDstu2Test { .named("$SOMEOPERATION") .withParameters(inParams).encodedXml().execute(); - assertEquals("http://localhost:" + ourPort + "/fhir/Patient/$SOMEOPERATION", ourRequestUri); + assertEquals(ourServer.getBaseUrl() + "/fhir/Patient/$SOMEOPERATION", MY_SERVLET.ourRequestUri); assertEquals(respString, p.encodeResourceToString(resp)); - assertEquals(1, ourRequestHeaders.get(Constants.HEADER_CONTENT_TYPE).size()); + assertEquals(1, MY_SERVLET.ourRequestHeaders.get(Constants.HEADER_CONTENT_TYPE).size()); assertContentTypeEquals(EncodingEnum.XML.getResourceContentType() + Constants.HEADER_SUFFIX_CT_UTF_8); - assertEquals(ourRequestBodyString, reqString); - assertEquals("POST", ourRequestMethod); + assertEquals(MY_SERVLET.ourRequestBodyString, reqString); + assertEquals("POST", MY_SERVLET.ourRequestMethod); resp = client .operation() @@ -970,16 +950,16 @@ public class GenericOkHttpClientDstu2Test { .named("$SOMEOPERATION") .withParameters(inParams).encodedXml().execute(); - assertEquals("http://localhost:" + ourPort + "/fhir/Patient/123/$SOMEOPERATION", ourRequestUri); + assertEquals(ourServer.getBaseUrl() + "/fhir/Patient/123/$SOMEOPERATION", MY_SERVLET.ourRequestUri); assertEquals(respString, p.encodeResourceToString(resp)); - assertEquals(1, ourRequestHeaders.get(Constants.HEADER_CONTENT_TYPE).size()); + assertEquals(1, MY_SERVLET.ourRequestHeaders.get(Constants.HEADER_CONTENT_TYPE).size()); assertContentTypeEquals(EncodingEnum.XML.getResourceContentType() + Constants.HEADER_SUFFIX_CT_UTF_8); - assertEquals(ourRequestBodyString, reqString); - assertEquals("POST", ourRequestMethod); + assertEquals(MY_SERVLET.ourRequestBodyString, reqString); + assertEquals("POST", MY_SERVLET.ourRequestMethod); resp = client.operation().onInstance(new IdDt("http://foo.com/bar/baz/Patient/123/_history/22")).named("$SOMEOPERATION").withParameters(inParams).execute(); - assertEquals("http://localhost:" + ourPort + "/fhir/Patient/123/$SOMEOPERATION", ourRequestUri); + assertEquals(ourServer.getBaseUrl() + "/fhir/Patient/123/$SOMEOPERATION", MY_SERVLET.ourRequestUri); } @Test @@ -994,10 +974,10 @@ public class GenericOkHttpClientDstu2Test { outParams.addParameter().setValue(new StringDt("STRINGVALOUT2")); final String respString = p.encodeResourceToString(outParams); - ourResponseContentType = Constants.CT_FHIR_XML + "; charset=UTF-8"; - ourResponseBody = respString; + MY_SERVLET.ourResponseContentType = Constants.CT_FHIR_XML + "; charset=UTF-8"; + MY_SERVLET.ourResponseBody = respString; - IGenericClient client = ourCtx.newRestfulGenericClient("http://localhost:" + ourPort + "/fhir"); + IGenericClient client = ourCtx.newRestfulGenericClient(ourServer.getBaseUrl() + "/fhir"); Parameters resp = client .operation() @@ -1005,12 +985,12 @@ public class GenericOkHttpClientDstu2Test { .named("$SOMEOPERATION") .withNoParameters(Parameters.class).encodedXml().execute(); - assertEquals("http://localhost:" + ourPort + "/fhir/$SOMEOPERATION", ourRequestUri); + assertEquals(ourServer.getBaseUrl() + "/fhir/$SOMEOPERATION", MY_SERVLET.ourRequestUri); assertEquals(respString, p.encodeResourceToString(resp)); - assertEquals(1, ourRequestHeaders.get(Constants.HEADER_CONTENT_TYPE).size()); + assertEquals(1, MY_SERVLET.ourRequestHeaders.get(Constants.HEADER_CONTENT_TYPE).size()); assertContentTypeEquals(EncodingEnum.XML.getResourceContentType() + Constants.HEADER_SUFFIX_CT_UTF_8); - assertEquals(ourRequestBodyString, reqString); - assertEquals("POST", ourRequestMethod); + assertEquals(MY_SERVLET.ourRequestBodyString, reqString); + assertEquals("POST", MY_SERVLET.ourRequestMethod); resp = client .operation() @@ -1018,12 +998,12 @@ public class GenericOkHttpClientDstu2Test { .named("$SOMEOPERATION") .withNoParameters(Parameters.class).encodedXml().execute(); - assertEquals("http://localhost:" + ourPort + "/fhir/Patient/$SOMEOPERATION", ourRequestUri); + assertEquals(ourServer.getBaseUrl() + "/fhir/Patient/$SOMEOPERATION", MY_SERVLET.ourRequestUri); assertEquals(respString, p.encodeResourceToString(resp)); - assertEquals(1, ourRequestHeaders.get(Constants.HEADER_CONTENT_TYPE).size()); + assertEquals(1, MY_SERVLET.ourRequestHeaders.get(Constants.HEADER_CONTENT_TYPE).size()); assertContentTypeEquals(EncodingEnum.XML.getResourceContentType() + Constants.HEADER_SUFFIX_CT_UTF_8); - assertEquals(ourRequestBodyString, reqString); - assertEquals("POST", ourRequestMethod); + assertEquals(MY_SERVLET.ourRequestBodyString, reqString); + assertEquals("POST", MY_SERVLET.ourRequestMethod); resp = client .operation() @@ -1031,12 +1011,12 @@ public class GenericOkHttpClientDstu2Test { .named("$SOMEOPERATION") .withNoParameters(Parameters.class).encodedXml().execute(); - assertEquals("http://localhost:" + ourPort + "/fhir/Patient/123/$SOMEOPERATION", ourRequestUri); + assertEquals(ourServer.getBaseUrl() + "/fhir/Patient/123/$SOMEOPERATION", MY_SERVLET.ourRequestUri); assertEquals(respString, p.encodeResourceToString(resp)); - assertEquals(1, ourRequestHeaders.get(Constants.HEADER_CONTENT_TYPE).size()); + assertEquals(1, MY_SERVLET.ourRequestHeaders.get(Constants.HEADER_CONTENT_TYPE).size()); assertContentTypeEquals(EncodingEnum.XML.getResourceContentType() + Constants.HEADER_SUFFIX_CT_UTF_8); - assertEquals(ourRequestBodyString, reqString); - assertEquals("POST", ourRequestMethod); + assertEquals(MY_SERVLET.ourRequestBodyString, reqString); + assertEquals("POST", MY_SERVLET.ourRequestMethod); resp = client .operation() @@ -1045,19 +1025,19 @@ public class GenericOkHttpClientDstu2Test { .withNoParameters(Parameters.class) .execute(); - assertEquals("http://localhost:" + ourPort + "/fhir/Patient/123/$SOMEOPERATION", ourRequestUri); + assertEquals(ourServer.getBaseUrl() + "/fhir/Patient/123/$SOMEOPERATION", MY_SERVLET.ourRequestUri); } @Test public void testPageNext() throws Exception { - ourResponseContentType = Constants.CT_FHIR_XML + "; charset=UTF-8"; - ourResponseBody = getPatientFeedWithOneResult(); + MY_SERVLET.ourResponseContentType = Constants.CT_FHIR_XML + "; charset=UTF-8"; + MY_SERVLET.ourResponseBody = getPatientFeedWithOneResult(); - IGenericClient client = ourCtx.newRestfulGenericClient("http://localhost:" + ourPort + "/fhir"); + IGenericClient client = ourCtx.newRestfulGenericClient(ourServer.getBaseUrl() + "/fhir"); ca.uhn.fhir.model.dstu2.resource.Bundle sourceBundle = new ca.uhn.fhir.model.dstu2.resource.Bundle(); - sourceBundle.getLinkOrCreate(IBaseBundle.LINK_PREV).setUrl("http://localhost:" + ourPort + "/fhir/prev"); - sourceBundle.getLinkOrCreate(IBaseBundle.LINK_NEXT).setUrl("http://localhost:" + ourPort + "/fhir/next"); + sourceBundle.getLinkOrCreate(IBaseBundle.LINK_PREV).setUrl(ourServer.getBaseUrl() + "/fhir/prev"); + sourceBundle.getLinkOrCreate(IBaseBundle.LINK_NEXT).setUrl(ourServer.getBaseUrl() + "/fhir/next"); ca.uhn.fhir.model.dstu2.resource.Bundle resp = client .loadPage() @@ -1065,12 +1045,12 @@ public class GenericOkHttpClientDstu2Test { .execute(); assertEquals(1, resp.getEntry().size()); - assertEquals("http://localhost:" + ourPort + "/fhir/next", ourRequestUri); + assertEquals(ourServer.getBaseUrl() + "/fhir/next", MY_SERVLET.ourRequestUri); } @Test public void testPageNextNoLink() throws Exception { - IGenericClient client = ourCtx.newRestfulGenericClient("http://localhost:" + ourPort + "/fhir"); + IGenericClient client = ourCtx.newRestfulGenericClient(ourServer.getBaseUrl() + "/fhir"); ca.uhn.fhir.model.dstu2.resource.Bundle sourceBundle = new ca.uhn.fhir.model.dstu2.resource.Bundle(); try { @@ -1082,13 +1062,13 @@ public class GenericOkHttpClientDstu2Test { @Test public void testPagePrev() throws Exception { - ourResponseContentType = Constants.CT_FHIR_XML + "; charset=UTF-8"; - ourResponseBody = getPatientFeedWithOneResult(); + MY_SERVLET.ourResponseContentType = Constants.CT_FHIR_XML + "; charset=UTF-8"; + MY_SERVLET.ourResponseBody = getPatientFeedWithOneResult(); - IGenericClient client = ourCtx.newRestfulGenericClient("http://localhost:" + ourPort + "/fhir"); + IGenericClient client = ourCtx.newRestfulGenericClient(ourServer.getBaseUrl() + "/fhir"); ca.uhn.fhir.model.dstu2.resource.Bundle sourceBundle = new ca.uhn.fhir.model.dstu2.resource.Bundle(); - sourceBundle.getLinkOrCreate("previous").setUrl("http://localhost:" + ourPort + "/fhir/prev"); + sourceBundle.getLinkOrCreate("previous").setUrl(ourServer.getBaseUrl() + "/fhir/prev"); ca.uhn.fhir.model.dstu2.resource.Bundle resp = client .loadPage() @@ -1096,14 +1076,14 @@ public class GenericOkHttpClientDstu2Test { .execute(); assertEquals(1, resp.getEntry().size()); - assertEquals("http://localhost:" + ourPort + "/fhir/prev", ourRequestUri); + assertEquals(ourServer.getBaseUrl() + "/fhir/prev", MY_SERVLET.ourRequestUri); /* * Try with "prev" instead of "previous" */ sourceBundle = new ca.uhn.fhir.model.dstu2.resource.Bundle(); - sourceBundle.getLinkOrCreate("prev").setUrl("http://localhost:" + ourPort + "/fhir/prev"); + sourceBundle.getLinkOrCreate("prev").setUrl(ourServer.getBaseUrl() + "/fhir/prev"); resp = client .loadPage() @@ -1111,7 +1091,7 @@ public class GenericOkHttpClientDstu2Test { .execute(); assertEquals(1, resp.getEntry().size()); - assertEquals("http://localhost:" + ourPort + "/fhir/prev", ourRequestUri); + assertEquals(ourServer.getBaseUrl() + "/fhir/prev", MY_SERVLET.ourRequestUri); } @Test @@ -1120,15 +1100,15 @@ public class GenericOkHttpClientDstu2Test { patient.addName().addFamily("FAM"); final String respString = ourCtx.newXmlParser().encodeResourceToString(patient); - ourResponseContentType = Constants.CT_FHIR_XML + "; charset=UTF-8"; - ourResponseBody = respString; + MY_SERVLET.ourResponseContentType = Constants.CT_FHIR_XML + "; charset=UTF-8"; + MY_SERVLET.ourResponseBody = respString; - IGenericClient client = ourCtx.newRestfulGenericClient("http://localhost:" + ourPort + "/fhir"); + IGenericClient client = ourCtx.newRestfulGenericClient(ourServer.getBaseUrl() + "/fhir"); Patient response; - response = (Patient) client.read(new UriDt("http://localhost:" + ourPort + "/fhir/Patient/123")); - assertEquals("http://localhost:" + ourPort + "/fhir/Patient/123", ourRequestUri); + response = (Patient) client.read(new UriDt(ourServer.getBaseUrl() + "/fhir/Patient/123")); + assertEquals(ourServer.getBaseUrl() + "/fhir/Patient/123", MY_SERVLET.ourRequestUri); assertEquals("FAM", response.getName().get(0).getFamily().get(0).getValue()); } @@ -1138,15 +1118,15 @@ public class GenericOkHttpClientDstu2Test { patient.addName().addFamily("FAM"); final String respString = ourCtx.newXmlParser().encodeResourceToString(patient); - ourResponseContentType = Constants.CT_FHIR_XML + "; charset=UTF-8"; - ourResponseBody = respString; + MY_SERVLET.ourResponseContentType = Constants.CT_FHIR_XML + "; charset=UTF-8"; + MY_SERVLET.ourResponseBody = respString; - IGenericClient client = ourCtx.newRestfulGenericClient("http://localhost:" + ourPort + "/fhir"); + IGenericClient client = ourCtx.newRestfulGenericClient(ourServer.getBaseUrl() + "/fhir"); Patient response; - response = (Patient) client.read().resource(Patient.class).withUrl(new IdDt("http://localhost:" + ourPort + "/AAA/Patient/123")).execute(); - assertEquals("http://localhost:" + ourPort + "/AAA/Patient/123", ourRequestUri); + response = (Patient) client.read().resource(Patient.class).withUrl(new IdDt(ourServer.getBaseUrl() + "/AAA/Patient/123")).execute(); + assertEquals(ourServer.getBaseUrl() + "/AAA/Patient/123", MY_SERVLET.ourRequestUri); assertEquals("FAM", response.getName().get(0).getFamily().get(0).getValue()); } @@ -1167,10 +1147,10 @@ public class GenericOkHttpClientDstu2Test { " \n" + ""; - ourResponseContentType = Constants.CT_FHIR_XML + "; charset=UTF-8"; - ourResponseBody = input; + MY_SERVLET.ourResponseContentType = Constants.CT_FHIR_XML + "; charset=UTF-8"; + MY_SERVLET.ourResponseBody = input; - IGenericClient client = ourCtx.newRestfulGenericClient("http://localhost:" + ourPort + "/fhir"); + IGenericClient client = ourCtx.newRestfulGenericClient(ourServer.getBaseUrl() + "/fhir"); ca.uhn.fhir.model.dstu2.resource.Bundle response; @@ -1187,10 +1167,10 @@ public class GenericOkHttpClientDstu2Test { public void testReadWithElementsParam() throws Exception { String msg = "{\"resourceType\":\"Patient\",\"id\":\"1\",\"meta\":{\"versionId\":\"1\",\"lastUpdated\":\"2014-12-20T18:41:29.706-05:00\"},\"identifier\":[{\"system\":\"urn:MultiFhirVersionTest\",\"value\":\"testSubmitPatient01\"}]}"; - ourResponseContentType = Constants.CT_FHIR_JSON + "; charset=UTF-8"; - ourResponseBody = msg; + MY_SERVLET.ourResponseContentType = Constants.CT_FHIR_JSON + "; charset=UTF-8"; + MY_SERVLET.ourResponseBody = msg; - IGenericClient client = ourCtx.newRestfulGenericClient("http://localhost:" + ourPort + "/fhir"); + IGenericClient client = ourCtx.newRestfulGenericClient(ourServer.getBaseUrl() + "/fhir"); IBaseResource response = client.read() .resource("Patient") @@ -1198,8 +1178,8 @@ public class GenericOkHttpClientDstu2Test { .elementsSubset("name", "identifier") .execute(); - assertThat(ourRequestUri, either(equalTo("http://localhost:" + ourPort + "/fhir/Patient/123?_elements=name%2Cidentifier")) - .or(equalTo("http://localhost:" + ourPort + "/fhir/Patient/123?_elements=identifier%2Cname"))); + assertThat(MY_SERVLET.ourRequestUri, either(equalTo(ourServer.getBaseUrl() + "/fhir/Patient/123?_elements=name%2Cidentifier")) + .or(equalTo(ourServer.getBaseUrl() + "/fhir/Patient/123?_elements=identifier%2Cname"))); assertEquals(Patient.class, response.getClass()); } @@ -1207,10 +1187,10 @@ public class GenericOkHttpClientDstu2Test { public void testReadWithSummaryInvalid() throws Exception { String msg = "<>>>><<<<>"; - ourResponseContentType = Constants.CT_HTML + "; charset=UTF-8"; - ourResponseBody = msg; + MY_SERVLET.ourResponseContentType = Constants.CT_HTML + "; charset=UTF-8"; + MY_SERVLET.ourResponseBody = msg; - IGenericClient client = ourCtx.newRestfulGenericClient("http://localhost:" + ourPort + "/fhir"); + IGenericClient client = ourCtx.newRestfulGenericClient(ourServer.getBaseUrl() + "/fhir"); try { client.read() @@ -1229,10 +1209,10 @@ public class GenericOkHttpClientDstu2Test { public void testReadWithSummaryParamHtml() throws Exception { String msg = "
    HELP IM A DIV
    "; - ourResponseContentType = Constants.CT_HTML + "; charset=UTF-8"; - ourResponseBody = msg; + MY_SERVLET.ourResponseContentType = Constants.CT_HTML + "; charset=UTF-8"; + MY_SERVLET.ourResponseBody = msg; - IGenericClient client = ourCtx.newRestfulGenericClient("http://localhost:" + ourPort + "/fhir"); + IGenericClient client = ourCtx.newRestfulGenericClient(ourServer.getBaseUrl() + "/fhir"); Patient response = client.read() .resource(Patient.class) @@ -1240,7 +1220,7 @@ public class GenericOkHttpClientDstu2Test { .summaryMode(SummaryEnum.TEXT) .execute(); - assertEquals("http://localhost:" + ourPort + "/fhir/Patient/123?_summary=text", ourRequestUri); + assertEquals(ourServer.getBaseUrl() + "/fhir/Patient/123?_summary=text", MY_SERVLET.ourRequestUri); assertEquals(Patient.class, response.getClass()); assertEquals("
    HELP IM A DIV
    ", response.getText().getDiv().getValueAsString()); } @@ -1249,10 +1229,10 @@ public class GenericOkHttpClientDstu2Test { public void testSearchByString() throws Exception { String msg = "{\"resourceType\":\"Bundle\",\"id\":null,\"base\":\"http://localhost:57931/fhir/contextDev\",\"total\":1,\"link\":[{\"relation\":\"self\",\"url\":\"http://localhost:57931/fhir/contextDev/Patient?identifier=urn%3AMultiFhirVersionTest%7CtestSubmitPatient01&_format=json\"}],\"entry\":[{\"resource\":{\"resourceType\":\"Patient\",\"id\":\"1\",\"meta\":{\"versionId\":\"1\",\"lastUpdated\":\"2014-12-20T18:41:29.706-05:00\"},\"identifier\":[{\"system\":\"urn:MultiFhirVersionTest\",\"value\":\"testSubmitPatient01\"}]}}]}"; - ourResponseContentType = Constants.CT_FHIR_JSON + "; charset=UTF-8"; - ourResponseBody = msg; + MY_SERVLET.ourResponseContentType = Constants.CT_FHIR_JSON + "; charset=UTF-8"; + MY_SERVLET.ourResponseBody = msg; - IGenericClient client = ourCtx.newRestfulGenericClient("http://localhost:" + ourPort + "/fhir"); + IGenericClient client = ourCtx.newRestfulGenericClient(ourServer.getBaseUrl() + "/fhir"); Bundle response = client.search() .forResource("Patient") @@ -1260,7 +1240,7 @@ public class GenericOkHttpClientDstu2Test { .returnBundle(Bundle.class) .execute(); - assertEquals("http://localhost:" + ourPort + "/fhir/Patient?name=james", ourRequestUri); + assertEquals(ourServer.getBaseUrl() + "/fhir/Patient?name=james", MY_SERVLET.ourRequestUri); assertEquals(Patient.class, response.getEntry().get(0).getResource().getClass()); } @@ -1268,18 +1248,18 @@ public class GenericOkHttpClientDstu2Test { public void testSearchByUrl() throws Exception { final String msg = getPatientFeedWithOneResult(); - ourResponseContentType = Constants.CT_FHIR_XML + "; charset=UTF-8"; - ourResponseBody = msg; + MY_SERVLET.ourResponseContentType = Constants.CT_FHIR_XML + "; charset=UTF-8"; + MY_SERVLET.ourResponseBody = msg; - IGenericClient client = ourCtx.newRestfulGenericClient("http://localhost:" + ourPort + "/fhir"); + IGenericClient client = ourCtx.newRestfulGenericClient(ourServer.getBaseUrl() + "/fhir"); ca.uhn.fhir.model.dstu2.resource.Bundle response = client.search() - .byUrl("http://localhost:" + ourPort + "/AAA?name=http://foo|bar") + .byUrl(ourServer.getBaseUrl() + "/AAA?name=http://foo|bar") .encodedJson() .returnBundle(ca.uhn.fhir.model.dstu2.resource.Bundle.class) .execute(); - assertEquals("http://localhost:" + ourPort + "/AAA?name=http%3A//foo%7Cbar&_format=json", ourRequestUri); + assertEquals(ourServer.getBaseUrl() + "/AAA?name=http%3A//foo%7Cbar&_format=json", MY_SERVLET.ourRequestUri); assertNotNull(response); response = client.search() @@ -1288,7 +1268,7 @@ public class GenericOkHttpClientDstu2Test { .returnBundle(ca.uhn.fhir.model.dstu2.resource.Bundle.class) .execute(); - assertEquals("http://localhost:" + ourPort + "/fhir/Patient?name=http%3A//foo%7Cbar&_format=json", ourRequestUri); + assertEquals(ourServer.getBaseUrl() + "/fhir/Patient?name=http%3A//foo%7Cbar&_format=json", MY_SERVLET.ourRequestUri); assertNotNull(response); response = client.search() @@ -1297,7 +1277,7 @@ public class GenericOkHttpClientDstu2Test { .returnBundle(ca.uhn.fhir.model.dstu2.resource.Bundle.class) .execute(); - assertEquals("http://localhost:" + ourPort + "/fhir/Patient?name=http%3A//foo%7Cbar&_format=json", ourRequestUri); + assertEquals(ourServer.getBaseUrl() + "/fhir/Patient?name=http%3A//foo%7Cbar&_format=json", MY_SERVLET.ourRequestUri); assertNotNull(response); response = client.search() @@ -1305,7 +1285,7 @@ public class GenericOkHttpClientDstu2Test { .returnBundle(ca.uhn.fhir.model.dstu2.resource.Bundle.class) .execute(); - assertEquals("http://localhost:" + ourPort + "/fhir/Patient", ourRequestUri); + assertEquals(ourServer.getBaseUrl() + "/fhir/Patient", MY_SERVLET.ourRequestUri); assertNotNull(response); response = client.search() @@ -1313,7 +1293,7 @@ public class GenericOkHttpClientDstu2Test { .returnBundle(ca.uhn.fhir.model.dstu2.resource.Bundle.class) .execute(); - assertEquals("http://localhost:" + ourPort + "/fhir/Patient", ourRequestUri); + assertEquals(ourServer.getBaseUrl() + "/fhir/Patient", MY_SERVLET.ourRequestUri); assertNotNull(response); try { @@ -1330,10 +1310,10 @@ public class GenericOkHttpClientDstu2Test { public void testSearchReturningDstu2Bundle() throws Exception { String msg = IOUtils.toString(GenericOkHttpClientDstu2Test.class.getResourceAsStream("/bundle_orion.xml")); - ourResponseContentType = Constants.CT_FHIR_XML + "; charset=UTF-8"; - ourResponseBody = msg; + MY_SERVLET.ourResponseContentType = Constants.CT_FHIR_XML + "; charset=UTF-8"; + MY_SERVLET.ourResponseBody = msg; - IGenericClient client = ourCtx.newRestfulGenericClient("http://localhost:" + ourPort + "/fhir"); + IGenericClient client = ourCtx.newRestfulGenericClient(ourServer.getBaseUrl() + "/fhir"); ca.uhn.fhir.model.dstu2.resource.Bundle response = client.search() .forResource("Observation") @@ -1355,10 +1335,10 @@ public class GenericOkHttpClientDstu2Test { public void testSearchWithElementsParam() throws Exception { String msg = "{\"resourceType\":\"Bundle\",\"id\":null,\"base\":\"http://localhost:57931/fhir/contextDev\",\"total\":1,\"link\":[{\"relation\":\"self\",\"url\":\"http://localhost:57931/fhir/contextDev/Patient?identifier=urn%3AMultiFhirVersionTest%7CtestSubmitPatient01&_format=json\"}],\"entry\":[{\"resource\":{\"resourceType\":\"Patient\",\"id\":\"1\",\"meta\":{\"versionId\":\"1\",\"lastUpdated\":\"2014-12-20T18:41:29.706-05:00\"},\"identifier\":[{\"system\":\"urn:MultiFhirVersionTest\",\"value\":\"testSubmitPatient01\"}]}}]}"; - ourResponseContentType = Constants.CT_FHIR_JSON + "; charset=UTF-8"; - ourResponseBody = msg; + MY_SERVLET.ourResponseContentType = Constants.CT_FHIR_JSON + "; charset=UTF-8"; + MY_SERVLET.ourResponseBody = msg; - IGenericClient client = ourCtx.newRestfulGenericClient("http://localhost:" + ourPort + "/fhir"); + IGenericClient client = ourCtx.newRestfulGenericClient(ourServer.getBaseUrl() + "/fhir"); Bundle response = client.search() .forResource("Patient") @@ -1367,8 +1347,8 @@ public class GenericOkHttpClientDstu2Test { .returnBundle(Bundle.class) .execute(); - assertThat(ourRequestUri, either(equalTo("http://localhost:" + ourPort + "/fhir/Patient?name=james&_elements=name%2Cidentifier")) - .or(equalTo("http://localhost:" + ourPort + "/fhir/Patient?name=james&_elements=identifier%2Cname"))); + assertThat(MY_SERVLET.ourRequestUri, either(equalTo(ourServer.getBaseUrl() + "/fhir/Patient?name=james&_elements=name%2Cidentifier")) + .or(equalTo(ourServer.getBaseUrl() + "/fhir/Patient?name=james&_elements=identifier%2Cname"))); assertEquals(Patient.class, response.getEntry().get(0).getResource().getClass()); } @@ -1376,10 +1356,10 @@ public class GenericOkHttpClientDstu2Test { public void testSearchByPost() throws Exception { String msg = "{\"resourceType\":\"Bundle\",\"id\":null,\"base\":\"http://localhost:57931/fhir/contextDev\",\"total\":1,\"link\":[{\"relation\":\"self\",\"url\":\"http://localhost:57931/fhir/contextDev/Patient?identifier=urn%3AMultiFhirVersionTest%7CtestSubmitPatient01&_format=json\"}],\"entry\":[{\"resource\":{\"resourceType\":\"Patient\",\"id\":\"1\",\"meta\":{\"versionId\":\"1\",\"lastUpdated\":\"2014-12-20T18:41:29.706-05:00\"},\"identifier\":[{\"system\":\"urn:MultiFhirVersionTest\",\"value\":\"testSubmitPatient01\"}]}}]}"; - ourResponseContentType = Constants.CT_FHIR_JSON + "; charset=UTF-8"; - ourResponseBody = msg; + MY_SERVLET.ourResponseContentType = Constants.CT_FHIR_JSON + "; charset=UTF-8"; + MY_SERVLET.ourResponseBody = msg; - IGenericClient client = ourCtx.newRestfulGenericClient("http://localhost:" + ourPort + "/fhir"); + IGenericClient client = ourCtx.newRestfulGenericClient(ourServer.getBaseUrl() + "/fhir"); Bundle response = client.search() .forResource("Patient") @@ -1389,29 +1369,29 @@ public class GenericOkHttpClientDstu2Test { .returnBundle(Bundle.class) .execute(); - assertEquals("http://localhost:" + ourPort + "/fhir/Patient/_search?_elements=identifier%2Cname", ourRequestUri); + assertEquals(ourServer.getBaseUrl() + "/fhir/Patient/_search?_elements=identifier%2Cname", MY_SERVLET.ourRequestUri); - // assertThat(ourRequestUri, - // either(equalTo("http://localhost:" + ourPort + "/fhir/Patient?name=james&_elements=name%2Cidentifier")).or(equalTo("http://localhost:" + ourPort + + // assertThat(MY_SERVLET.ourRequestUri, + // either(equalTo(ourServer.getBaseUrl() + "/fhir/Patient?name=james&_elements=name%2Cidentifier")).or(equalTo(ourServer.getBaseUrl() + // "/fhir/Patient?name=james&_elements=identifier%2Cname"))); assertEquals(Patient.class, response.getEntry().get(0).getResource().getClass()); - assertEquals("name=james", ourRequestBodyString); + assertEquals("name=james", MY_SERVLET.ourRequestBodyString); - assertEquals("application/x-www-form-urlencoded", ourRequestContentType.replace(";char", "; char").toLowerCase()); - assertEquals(Constants.HEADER_ACCEPT_VALUE_XML_OR_JSON_LEGACY, ourRequestFirstHeaders.get("Accept").getValue()); - assertThat(ourRequestFirstHeaders.get("User-Agent").getValue(), not(emptyString())); + assertEquals("application/x-www-form-urlencoded", MY_SERVLET.ourRequestContentType.replace(";char", "; char").toLowerCase()); + assertEquals(Constants.HEADER_ACCEPT_VALUE_XML_OR_JSON_LEGACY, MY_SERVLET.ourRequestFirstHeaders.get("Accept").getValue()); + assertThat(MY_SERVLET.ourRequestFirstHeaders.get("User-Agent").getValue(), not(emptyString())); } @Test public void testSearchByPostUseJson() throws Exception { String msg = "{\"resourceType\":\"Bundle\",\"id\":null,\"base\":\"http://localhost:57931/fhir/contextDev\",\"total\":1,\"link\":[{\"relation\":\"self\",\"url\":\"http://localhost:57931/fhir/contextDev/Patient?identifier=urn%3AMultiFhirVersionTest%7CtestSubmitPatient01&_format=json\"}],\"entry\":[{\"resource\":{\"resourceType\":\"Patient\",\"id\":\"1\",\"meta\":{\"versionId\":\"1\",\"lastUpdated\":\"2014-12-20T18:41:29.706-05:00\"},\"identifier\":[{\"system\":\"urn:MultiFhirVersionTest\",\"value\":\"testSubmitPatient01\"}]}}]}"; - ourResponseContentType = Constants.CT_FHIR_JSON + "; charset=UTF-8"; - ourResponseBody = msg; + MY_SERVLET.ourResponseContentType = Constants.CT_FHIR_JSON + "; charset=UTF-8"; + MY_SERVLET.ourResponseBody = msg; - IGenericClient client = ourCtx.newRestfulGenericClient("http://localhost:" + ourPort + "/fhir"); + IGenericClient client = ourCtx.newRestfulGenericClient(ourServer.getBaseUrl() + "/fhir"); Bundle response = client.search() .forResource("Patient") @@ -1422,30 +1402,30 @@ public class GenericOkHttpClientDstu2Test { .returnBundle(Bundle.class) .execute(); - assertThat(ourRequestUri, containsString("http://localhost:" + ourPort + "/fhir/Patient/_search?")); - assertThat(ourRequestUri, containsString("_elements=identifier%2Cname")); - assertThat(ourRequestUri, not(containsString("_format=json"))); + assertThat(MY_SERVLET.ourRequestUri, containsString(ourServer.getBaseUrl() + "/fhir/Patient/_search?")); + assertThat(MY_SERVLET.ourRequestUri, containsString("_elements=identifier%2Cname")); + assertThat(MY_SERVLET.ourRequestUri, not(containsString("_format=json"))); - // assertThat(ourRequestUri, - // either(equalTo("http://localhost:" + ourPort + "/fhir/Patient?name=james&_elements=name%2Cidentifier")).or(equalTo("http://localhost:" + ourPort + + // assertThat(MY_SERVLET.ourRequestUri, + // either(equalTo(ourServer.getBaseUrl() + "/fhir/Patient?name=james&_elements=name%2Cidentifier")).or(equalTo(ourServer.getBaseUrl() + // "/fhir/Patient?name=james&_elements=identifier%2Cname"))); assertEquals(Patient.class, response.getEntry().get(0).getResource().getClass()); - assertEquals("name=james", ourRequestBodyString); + assertEquals("name=james", MY_SERVLET.ourRequestBodyString); - assertEquals("application/x-www-form-urlencoded", ourRequestContentType); - assertEquals(Constants.CT_FHIR_JSON, ourRequestFirstHeaders.get("Accept").getValue()); + assertEquals("application/x-www-form-urlencoded", MY_SERVLET.ourRequestContentType); + assertEquals(Constants.CT_FHIR_JSON, MY_SERVLET.ourRequestFirstHeaders.get("Accept").getValue()); } @Test - public void testSearchWithLastUpdated() throws Exception { + public void testSearchWithLastUpdated() { String msg = "{\"resourceType\":\"Bundle\",\"id\":null,\"base\":\"http://localhost:57931/fhir/contextDev\",\"total\":1,\"link\":[{\"relation\":\"self\",\"url\":\"http://localhost:57931/fhir/contextDev/Patient?identifier=urn%3AMultiFhirVersionTest%7CtestSubmitPatient01&_format=json\"}],\"entry\":[{\"resource\":{\"resourceType\":\"Patient\",\"id\":\"1\",\"meta\":{\"versionId\":\"1\",\"lastUpdated\":\"2014-12-20T18:41:29.706-05:00\"},\"identifier\":[{\"system\":\"urn:MultiFhirVersionTest\",\"value\":\"testSubmitPatient01\"}]}}]}"; - ourResponseContentType = Constants.CT_FHIR_JSON + "; charset=UTF-8"; - ourResponseBody = msg; + MY_SERVLET.ourResponseContentType = Constants.CT_FHIR_JSON + "; charset=UTF-8"; + MY_SERVLET.ourResponseBody = msg; - IGenericClient client = ourCtx.newRestfulGenericClient("http://localhost:" + ourPort + "/fhir"); + IGenericClient client = ourCtx.newRestfulGenericClient(ourServer.getBaseUrl() + "/fhir"); Bundle response = client.search() .forResource("Patient") @@ -1454,18 +1434,18 @@ public class GenericOkHttpClientDstu2Test { .returnBundle(Bundle.class) .execute(); - assertEquals("http://localhost:" + ourPort + "/fhir/Patient?name=james&_lastUpdated=ge2011-01-01&_lastUpdated=le2012-01-01", ourRequestUri); + assertEquals(ourServer.getBaseUrl() + "/fhir/Patient?name=james&_lastUpdated=ge2011-01-01&_lastUpdated=le2012-01-01", MY_SERVLET.ourRequestUri); assertEquals(Patient.class, response.getEntry().get(0).getResource().getClass()); } @Test - public void testSearchWithProfileAndSecurity() throws Exception { + public void testSearchWithProfileAndSecurity() { String msg = "{\"resourceType\":\"Bundle\",\"id\":null,\"base\":\"http://localhost:57931/fhir/contextDev\",\"total\":1,\"link\":[{\"relation\":\"self\",\"url\":\"http://localhost:57931/fhir/contextDev/Patient?identifier=urn%3AMultiFhirVersionTest%7CtestSubmitPatient01&_format=json\"}],\"entry\":[{\"resource\":{\"resourceType\":\"Patient\",\"id\":\"1\",\"meta\":{\"versionId\":\"1\",\"lastUpdated\":\"2014-12-20T18:41:29.706-05:00\"},\"identifier\":[{\"system\":\"urn:MultiFhirVersionTest\",\"value\":\"testSubmitPatient01\"}]}}]}"; - ourResponseContentType = Constants.CT_FHIR_JSON + "; charset=UTF-8"; - ourResponseBody = msg; + MY_SERVLET.ourResponseContentType = Constants.CT_FHIR_JSON + "; charset=UTF-8"; + MY_SERVLET.ourResponseBody = msg; - IGenericClient client = ourCtx.newRestfulGenericClient("http://localhost:" + ourPort + "/fhir"); + IGenericClient client = ourCtx.newRestfulGenericClient(ourServer.getBaseUrl() + "/fhir"); Bundle response = client.search() .forResource("Patient") @@ -1476,7 +1456,7 @@ public class GenericOkHttpClientDstu2Test { .returnBundle(Bundle.class) .execute(); - assertEquals("http://localhost:" + ourPort + "/fhir/Patient?_security=system1%7Ccode1&_security=system2%7Ccode2&_profile=http%3A%2F%2Ffoo1&_profile=http%3A%2F%2Ffoo2", ourRequestUri); + assertEquals(ourServer.getBaseUrl() + "/fhir/Patient?_security=system1%7Ccode1&_security=system2%7Ccode2&_profile=http%3A%2F%2Ffoo1&_profile=http%3A%2F%2Ffoo2", MY_SERVLET.ourRequestUri); assertEquals(Patient.class, response.getEntry().get(0).getResource().getClass()); } @@ -1485,10 +1465,10 @@ public class GenericOkHttpClientDstu2Test { public void testSearchWithReverseInclude() throws Exception { String msg = getPatientFeedWithOneResult(); - ourResponseContentType = Constants.CT_FHIR_XML + "; charset=UTF-8"; - ourResponseBody = msg; + MY_SERVLET.ourResponseContentType = Constants.CT_FHIR_XML + "; charset=UTF-8"; + MY_SERVLET.ourResponseBody = msg; - IGenericClient client = ourCtx.newRestfulGenericClient("http://localhost:" + ourPort + "/fhir"); + IGenericClient client = ourCtx.newRestfulGenericClient(ourServer.getBaseUrl() + "/fhir"); Bundle response = client.search() .forResource(Patient.class) @@ -1497,17 +1477,17 @@ public class GenericOkHttpClientDstu2Test { .returnBundle(Bundle.class) .execute(); - assertEquals("http://localhost:" + ourPort + "/fhir/Patient?_revinclude=Provenance%3Atarget&_format=json", ourRequestUri); + assertEquals(ourServer.getBaseUrl() + "/fhir/Patient?_revinclude=Provenance%3Atarget&_format=json", MY_SERVLET.ourRequestUri); } @Test public void testSearchWithSummaryParam() throws Exception { String msg = "{\"resourceType\":\"Bundle\",\"id\":null,\"base\":\"http://localhost:57931/fhir/contextDev\",\"total\":1,\"link\":[{\"relation\":\"self\",\"url\":\"http://localhost:57931/fhir/contextDev/Patient?identifier=urn%3AMultiFhirVersionTest%7CtestSubmitPatient01&_format=json\"}],\"entry\":[{\"resource\":{\"resourceType\":\"Patient\",\"id\":\"1\",\"meta\":{\"versionId\":\"1\",\"lastUpdated\":\"2014-12-20T18:41:29.706-05:00\"},\"identifier\":[{\"system\":\"urn:MultiFhirVersionTest\",\"value\":\"testSubmitPatient01\"}]}}]}"; - ourResponseContentType = Constants.CT_FHIR_JSON + "; charset=UTF-8"; - ourResponseBody = msg; + MY_SERVLET.ourResponseContentType = Constants.CT_FHIR_JSON + "; charset=UTF-8"; + MY_SERVLET.ourResponseBody = msg; - IGenericClient client = ourCtx.newRestfulGenericClient("http://localhost:" + ourPort + "/fhir"); + IGenericClient client = ourCtx.newRestfulGenericClient(ourServer.getBaseUrl() + "/fhir"); Bundle response = client.search() .forResource("Patient") @@ -1516,7 +1496,7 @@ public class GenericOkHttpClientDstu2Test { .returnBundle(Bundle.class) .execute(); - assertEquals("http://localhost:" + ourPort + "/fhir/Patient?name=james&_summary=false", ourRequestUri); + assertEquals(ourServer.getBaseUrl() + "/fhir/Patient?name=james&_summary=false", MY_SERVLET.ourRequestUri); assertEquals(Patient.class, response.getEntry().get(0).getResource().getClass()); } @@ -1535,16 +1515,16 @@ public class GenericOkHttpClientDstu2Test { resp.addEntry().getResponse().setLocation("Patient/1/_history/1"); resp.addEntry().getResponse().setLocation("Patient/2/_history/2"); - ourResponseContentType = Constants.CT_FHIR_JSON + "; charset=UTF-8"; - ourResponseBody = reqString; + MY_SERVLET.ourResponseContentType = Constants.CT_FHIR_JSON + "; charset=UTF-8"; + MY_SERVLET.ourResponseBody = reqString; - IGenericClient client = ourCtx.newRestfulGenericClient("http://localhost:" + ourPort + "/fhir"); + IGenericClient client = ourCtx.newRestfulGenericClient(ourServer.getBaseUrl() + "/fhir"); String response = client.transaction() .withBundle(reqString) .execute(); - assertEquals("http://localhost:" + ourPort + "/fhir/", ourRequestUri); + assertEquals(ourServer.getBaseUrl() + "/fhir/", MY_SERVLET.ourRequestUri); assertThat(response, containsString("\"Bundle\"")); assertContentTypeEquals("application/json+fhir; charset=UTF-8"); @@ -1553,7 +1533,7 @@ public class GenericOkHttpClientDstu2Test { .encodedXml() .execute(); - assertEquals("http://localhost:" + ourPort + "/fhir/", ourRequestUri); + assertEquals(ourServer.getBaseUrl() + "/fhir/", MY_SERVLET.ourRequestUri); assertContentTypeEquals("application/xml+fhir; charset=UTF-8"); } @@ -1564,10 +1544,10 @@ public class GenericOkHttpClientDstu2Test { resp.addEntry().getResponse().setLocation("Patient/2/_history/2"); String respString = ourCtx.newJsonParser().encodeResourceToString(resp); - ourResponseContentType = Constants.CT_FHIR_JSON + "; charset=UTF-8"; - ourResponseBody = respString; + MY_SERVLET.ourResponseContentType = Constants.CT_FHIR_JSON + "; charset=UTF-8"; + MY_SERVLET.ourResponseBody = respString; - IGenericClient client = ourCtx.newRestfulGenericClient("http://localhost:" + ourPort + "/fhir"); + IGenericClient client = ourCtx.newRestfulGenericClient(ourServer.getBaseUrl() + "/fhir"); ca.uhn.fhir.model.dstu2.resource.Bundle input = new ca.uhn.fhir.model.dstu2.resource.Bundle(); @@ -1585,7 +1565,7 @@ public class GenericOkHttpClientDstu2Test { .encodedJson() .execute(); - assertEquals("http://localhost:" + ourPort + "/fhir", ourRequestUri); + assertEquals(ourServer.getBaseUrl() + "/fhir", MY_SERVLET.ourRequestUri); assertEquals(2, response.getEntry().size()); assertEquals("Patient/1/_history/1", response.getEntry().get(0).getResponse().getLocation()); @@ -1594,97 +1574,97 @@ public class GenericOkHttpClientDstu2Test { @Test public void testUpdateConditional() throws Exception { - ourResponseStatus = Constants.STATUS_HTTP_204_NO_CONTENT; + MY_SERVLET.ourResponseStatus = Constants.STATUS_HTTP_204_NO_CONTENT; - IGenericClient client = ourCtx.newRestfulGenericClient("http://localhost:" + ourPort + "/fhir"); + IGenericClient client = ourCtx.newRestfulGenericClient(ourServer.getBaseUrl() + "/fhir"); Patient p = new Patient(); p.addName().addFamily("FOOFAMILY"); client.update().resource(p).conditionalByUrl("Patient?name=foo").encodedXml().execute(); - assertEquals(1, ourRequestHeaders.get(Constants.HEADER_CONTENT_TYPE).size()); + assertEquals(1, MY_SERVLET.ourRequestHeaders.get(Constants.HEADER_CONTENT_TYPE).size()); assertContentTypeEquals(EncodingEnum.XML.getResourceContentType() + Constants.HEADER_SUFFIX_CT_UTF_8); - assertThat(ourRequestBodyString, containsString("")); - assertEquals("PUT", ourRequestMethod); - assertEquals("http://localhost:" + ourPort + "/fhir/Patient?name=foo", ourRequestUri); + assertThat(MY_SERVLET.ourRequestBodyString, containsString("")); + assertEquals("PUT", MY_SERVLET.ourRequestMethod); + assertEquals(ourServer.getBaseUrl() + "/fhir/Patient?name=foo", MY_SERVLET.ourRequestUri); client.update().resource(p).conditionalByUrl("Patient?name=http://foo|bar").encodedXml().execute(); - assertEquals(1, ourRequestHeaders.get(Constants.HEADER_CONTENT_TYPE).size()); + assertEquals(1, MY_SERVLET.ourRequestHeaders.get(Constants.HEADER_CONTENT_TYPE).size()); assertContentTypeEquals(EncodingEnum.XML.getResourceContentType() + Constants.HEADER_SUFFIX_CT_UTF_8); - assertThat(ourRequestBodyString, containsString("")); - assertEquals("PUT", ourRequestMethod); - assertEquals("http://localhost:" + ourPort + "/fhir/Patient?name=http%3A//foo%7Cbar", ourRequestUri); + assertThat(MY_SERVLET.ourRequestBodyString, containsString("")); + assertEquals("PUT", MY_SERVLET.ourRequestMethod); + assertEquals(ourServer.getBaseUrl() + "/fhir/Patient?name=http%3A//foo%7Cbar", MY_SERVLET.ourRequestUri); client.update().resource(ourCtx.newXmlParser().encodeResourceToString(p)).conditionalByUrl("Patient?name=foo").encodedXml().execute(); - assertEquals(1, ourRequestHeaders.get(Constants.HEADER_CONTENT_TYPE).size()); + assertEquals(1, MY_SERVLET.ourRequestHeaders.get(Constants.HEADER_CONTENT_TYPE).size()); assertContentTypeEquals(EncodingEnum.XML.getResourceContentType() + Constants.HEADER_SUFFIX_CT_UTF_8); - assertThat(ourRequestBodyString, containsString("")); - assertEquals("PUT", ourRequestMethod); - assertEquals("http://localhost:" + ourPort + "/fhir/Patient?name=foo", ourRequestUri); + assertThat(MY_SERVLET.ourRequestBodyString, containsString("")); + assertEquals("PUT", MY_SERVLET.ourRequestMethod); + assertEquals(ourServer.getBaseUrl() + "/fhir/Patient?name=foo", MY_SERVLET.ourRequestUri); client.update().resource(p).conditional().where(Patient.NAME.matches().value("foo")).and(Patient.ADDRESS.matches().value("AAA|BBB")).encodedXml().execute(); - assertEquals(1, ourRequestHeaders.get(Constants.HEADER_CONTENT_TYPE).size()); + assertEquals(1, MY_SERVLET.ourRequestHeaders.get(Constants.HEADER_CONTENT_TYPE).size()); assertContentTypeEquals(EncodingEnum.XML.getResourceContentType() + Constants.HEADER_SUFFIX_CT_UTF_8); - assertThat(ourRequestBodyString, containsString("")); - assertEquals("PUT", ourRequestMethod); - assertEquals("http://localhost:" + ourPort + "/fhir/Patient?name=foo&address=AAA%5C%7CBBB", ourRequestUri); + assertThat(MY_SERVLET.ourRequestBodyString, containsString("")); + assertEquals("PUT", MY_SERVLET.ourRequestMethod); + assertEquals(ourServer.getBaseUrl() + "/fhir/Patient?name=foo&address=AAA%5C%7CBBB", MY_SERVLET.ourRequestUri); client.update().resource(ourCtx.newXmlParser().encodeResourceToString(p)).conditional().where(Patient.NAME.matches().value("foo")).and(Patient.ADDRESS.matches().value("AAA|BBB")).encodedXml().execute(); - assertEquals(1, ourRequestHeaders.get(Constants.HEADER_CONTENT_TYPE).size()); + assertEquals(1, MY_SERVLET.ourRequestHeaders.get(Constants.HEADER_CONTENT_TYPE).size()); assertContentTypeEquals(EncodingEnum.XML.getResourceContentType() + Constants.HEADER_SUFFIX_CT_UTF_8); - assertThat(ourRequestBodyString, containsString("")); - assertEquals("PUT", ourRequestMethod); - assertEquals("http://localhost:" + ourPort + "/fhir/Patient?name=foo&address=AAA%5C%7CBBB", ourRequestUri); + assertThat(MY_SERVLET.ourRequestBodyString, containsString("")); + assertEquals("PUT", MY_SERVLET.ourRequestMethod); + assertEquals(ourServer.getBaseUrl() + "/fhir/Patient?name=foo&address=AAA%5C%7CBBB", MY_SERVLET.ourRequestUri); } private String getActualContentTypeHeader() { - return ourRequestFirstHeaders.get(Constants.HEADER_CONTENT_TYPE).getValue().replace(";char", "; char"); + return MY_SERVLET.ourRequestFirstHeaders.get(Constants.HEADER_CONTENT_TYPE).getValue().replace(";char", "; char"); } @Test public void testUpdateNonFluent() throws Exception { - ourResponseStatus = Constants.STATUS_HTTP_204_NO_CONTENT; + MY_SERVLET.ourResponseStatus = Constants.STATUS_HTTP_204_NO_CONTENT; - IGenericClient client = ourCtx.newRestfulGenericClient("http://localhost:" + ourPort + "/fhir"); + IGenericClient client = ourCtx.newRestfulGenericClient(ourServer.getBaseUrl() + "/fhir"); client.setEncoding(EncodingEnum.XML); Patient p = new Patient(); p.addName().addFamily("FOOFAMILY"); client.update(new IdDt("Patient/123"), p); - assertEquals(1, ourRequestHeaders.get(Constants.HEADER_CONTENT_TYPE).size()); + assertEquals(1, MY_SERVLET.ourRequestHeaders.get(Constants.HEADER_CONTENT_TYPE).size()); String expectedContentTypeHeader = EncodingEnum.XML.getResourceContentType() + Constants.HEADER_SUFFIX_CT_UTF_8; assertThat(getActualContentTypeHeader(), equalToIgnoringCase(expectedContentTypeHeader)); - assertThat(ourRequestBodyString, containsString("")); - assertEquals("http://localhost:" + ourPort + "/fhir/Patient/123?_format=xml", ourRequestUri); - assertEquals("PUT", ourRequestMethod); + assertThat(MY_SERVLET.ourRequestBodyString, containsString("")); + assertEquals(ourServer.getBaseUrl() + "/fhir/Patient/123?_format=xml", MY_SERVLET.ourRequestUri); + assertEquals("PUT", MY_SERVLET.ourRequestMethod); client.update("123", p); - assertEquals(1, ourRequestHeaders.get(Constants.HEADER_CONTENT_TYPE).size()); + assertEquals(1, MY_SERVLET.ourRequestHeaders.get(Constants.HEADER_CONTENT_TYPE).size()); assertThat(getActualContentTypeHeader(), equalToIgnoringCase(expectedContentTypeHeader)); - assertThat(ourRequestBodyString, containsString("")); - assertEquals("http://localhost:" + ourPort + "/fhir/Patient/123?_format=xml", ourRequestUri); - assertEquals("PUT", ourRequestMethod); + assertThat(MY_SERVLET.ourRequestBodyString, containsString("")); + assertEquals(ourServer.getBaseUrl() + "/fhir/Patient/123?_format=xml", MY_SERVLET.ourRequestUri); + assertEquals("PUT", MY_SERVLET.ourRequestMethod); } @Test public void testUpdatePrefer() throws Exception { - ourResponseStatus = Constants.STATUS_HTTP_204_NO_CONTENT; + MY_SERVLET.ourResponseStatus = Constants.STATUS_HTTP_204_NO_CONTENT; - IGenericClient client = ourCtx.newRestfulGenericClient("http://localhost:" + ourPort + "/fhir"); + IGenericClient client = ourCtx.newRestfulGenericClient(ourServer.getBaseUrl() + "/fhir"); Patient p = new Patient(); p.setId(new IdDt("1")); p.addName().addFamily("FOOFAMILY"); client.update().resource(p).prefer(PreferReturnEnum.MINIMAL).execute(); - assertEquals(1, ourRequestHeaders.get(Constants.HEADER_PREFER).size()); - assertEquals(Constants.HEADER_PREFER_RETURN + '=' + Constants.HEADER_PREFER_RETURN_MINIMAL, ourRequestHeaders.get(Constants.HEADER_PREFER).get(0).getValue()); + assertEquals(1, MY_SERVLET.ourRequestHeaders.get(Constants.HEADER_PREFER).size()); + assertEquals(Constants.HEADER_PREFER_RETURN + '=' + Constants.HEADER_PREFER_RETURN_MINIMAL, MY_SERVLET.ourRequestHeaders.get(Constants.HEADER_PREFER).get(0).getValue()); client.update().resource(p).prefer(PreferReturnEnum.REPRESENTATION).execute(); - assertEquals(1, ourRequestHeaders.get(Constants.HEADER_PREFER).size()); - assertEquals(Constants.HEADER_PREFER_RETURN + '=' + Constants.HEADER_PREFER_RETURN_REPRESENTATION, ourRequestHeaders.get(Constants.HEADER_PREFER).get(0).getValue()); + assertEquals(1, MY_SERVLET.ourRequestHeaders.get(Constants.HEADER_PREFER).size()); + assertEquals(Constants.HEADER_PREFER_RETURN + '=' + Constants.HEADER_PREFER_RETURN_REPRESENTATION, MY_SERVLET.ourRequestHeaders.get(Constants.HEADER_PREFER).get(0).getValue()); } @Test @@ -1693,11 +1673,11 @@ public class GenericOkHttpClientDstu2Test { p.setId("123"); final String formatted = ourCtx.newXmlParser().encodeResourceToString(p); - ourResponseStatus = Constants.STATUS_HTTP_200_OK; - ourResponseContentType = Constants.CT_FHIR_XML + "; charset=UTF-8"; - ourResponseBody = formatted; + MY_SERVLET.ourResponseStatus = Constants.STATUS_HTTP_200_OK; + MY_SERVLET.ourResponseContentType = Constants.CT_FHIR_XML + "; charset=UTF-8"; + MY_SERVLET.ourResponseBody = formatted; - IGenericClient client = ourCtx.newRestfulGenericClient("http://localhost:" + ourPort + "/fhir"); + IGenericClient client = ourCtx.newRestfulGenericClient(ourServer.getBaseUrl() + "/fhir"); p = new Patient(); p.setId(new IdDt("1")); @@ -1714,10 +1694,10 @@ public class GenericOkHttpClientDstu2Test { oo.addIssue().setDiagnostics("FOOBAR"); final String msg = ourCtx.newXmlParser().encodeResourceToString(oo); - ourResponseContentType = Constants.CT_FHIR_XML + "; charset=UTF-8"; - ourResponseBody = msg; + MY_SERVLET.ourResponseContentType = Constants.CT_FHIR_XML + "; charset=UTF-8"; + MY_SERVLET.ourResponseBody = msg; - IGenericClient client = ourCtx.newRestfulGenericClient("http://localhost:" + ourPort + "/fhir"); + IGenericClient client = ourCtx.newRestfulGenericClient(ourServer.getBaseUrl() + "/fhir"); Patient p = new Patient(); p.addName().addGiven("GIVEN"); @@ -1725,34 +1705,34 @@ public class GenericOkHttpClientDstu2Test { MethodOutcome response; response = client.validate().resource(p).encodedXml().execute(); - assertEquals("http://localhost:" + ourPort + "/fhir/Patient/$validate", ourRequestUri); - assertEquals("POST", ourRequestMethod); + assertEquals(ourServer.getBaseUrl() + "/fhir/Patient/$validate", MY_SERVLET.ourRequestUri); + assertEquals("POST", MY_SERVLET.ourRequestMethod); assertEquals( "", - ourRequestBodyString); + MY_SERVLET.ourRequestBodyString); assertNotNull(response.getOperationOutcome()); assertEquals("FOOBAR", toOo(response.getOperationOutcome()).getIssueFirstRep().getDiagnosticsElement().getValue()); response = client.validate().resource(ourCtx.newXmlParser().encodeResourceToString(p)).execute(); - assertEquals("http://localhost:" + ourPort + "/fhir/Patient/$validate", ourRequestUri); - assertEquals("POST", ourRequestMethod); + assertEquals(ourServer.getBaseUrl() + "/fhir/Patient/$validate", MY_SERVLET.ourRequestUri); + assertEquals("POST", MY_SERVLET.ourRequestMethod); assertEquals( "", - ourRequestBodyString); + MY_SERVLET.ourRequestBodyString); assertNotNull(response.getOperationOutcome()); assertEquals("FOOBAR", toOo(response.getOperationOutcome()).getIssueFirstRep().getDiagnosticsElement().getValue()); response = client.validate().resource(ourCtx.newJsonParser().encodeResourceToString(p)).execute(); - assertEquals("http://localhost:" + ourPort + "/fhir/Patient/$validate", ourRequestUri); - assertEquals("POST", ourRequestMethod); - assertEquals("{\"resourceType\":\"Parameters\",\"parameter\":[{\"name\":\"resource\",\"resource\":{\"resourceType\":\"Patient\",\"name\":[{\"given\":[\"GIVEN\"]}]}}]}", ourRequestBodyString); + assertEquals(ourServer.getBaseUrl() + "/fhir/Patient/$validate", MY_SERVLET.ourRequestUri); + assertEquals("POST", MY_SERVLET.ourRequestMethod); + assertEquals("{\"resourceType\":\"Parameters\",\"parameter\":[{\"name\":\"resource\",\"resource\":{\"resourceType\":\"Patient\",\"name\":[{\"given\":[\"GIVEN\"]}]}}]}", MY_SERVLET.ourRequestBodyString); assertNotNull(response.getOperationOutcome()); assertEquals("FOOBAR", toOo(response.getOperationOutcome()).getIssueFirstRep().getDiagnosticsElement().getValue()); response = client.validate().resource(ourCtx.newJsonParser().encodeResourceToString(p)).prettyPrint().execute(); - assertEquals("http://localhost:" + ourPort + "/fhir/Patient/$validate?_pretty=true", ourRequestUri); - assertEquals("POST", ourRequestMethod); - assertThat(ourRequestBodyString, containsString("\"resourceType\": \"Parameters\",\n")); + assertEquals(ourServer.getBaseUrl() + "/fhir/Patient/$validate?_pretty=true", MY_SERVLET.ourRequestUri); + assertEquals("POST", MY_SERVLET.ourRequestMethod); + assertThat(MY_SERVLET.ourRequestBodyString, containsString("\"resourceType\": \"Parameters\",\n")); assertNotNull(response.getOperationOutcome()); assertEquals("FOOBAR", toOo(response.getOperationOutcome()).getIssueFirstRep().getDiagnosticsElement().getValue()); } @@ -1763,10 +1743,10 @@ public class GenericOkHttpClientDstu2Test { oo.addIssue().setDiagnostics("FOOBAR"); final String msg = ourCtx.newXmlParser().encodeResourceToString(oo); - ourResponseContentType = Constants.CT_FHIR_XML + "; charset=UTF-8"; - ourResponseBody = msg; + MY_SERVLET.ourResponseContentType = Constants.CT_FHIR_XML + "; charset=UTF-8"; + MY_SERVLET.ourResponseBody = msg; - IGenericClient client = ourCtx.newRestfulGenericClient("http://localhost:" + ourPort + "/fhir"); + IGenericClient client = ourCtx.newRestfulGenericClient(ourServer.getBaseUrl() + "/fhir"); client.setEncoding(EncodingEnum.XML); Patient p = new Patient(); @@ -1776,11 +1756,11 @@ public class GenericOkHttpClientDstu2Test { response = client.validate(p); - assertEquals("http://localhost:" + ourPort + "/fhir/Patient/$validate?_format=xml", ourRequestUri); - assertEquals("POST", ourRequestMethod); + assertEquals(ourServer.getBaseUrl() + "/fhir/Patient/$validate?_format=xml", MY_SERVLET.ourRequestUri); + assertEquals("POST", MY_SERVLET.ourRequestMethod); assertEquals( "", - ourRequestBodyString); + MY_SERVLET.ourRequestBodyString); assertNotNull(response.getOperationOutcome()); assertEquals("FOOBAR", toOo(response.getOperationOutcome()).getIssueFirstRep().getDiagnosticsElement().getValue()); } @@ -1789,77 +1769,4 @@ public class GenericOkHttpClientDstu2Test { return (OperationOutcome) theOperationOutcome; } - @BeforeEach - public void beforeReset() { - ourRequestUri = null; - ourRequestUriAll = Lists.newArrayList(); - ourResponseStatus = 200; - ourResponseBody = null; - ourResponseBodies = null; - ourResponseCount = 0; - - ourResponseContentType = null; - ourRequestContentType = null; - ourRequestBodyBytes = null; - ourRequestBodyString = null; - ourRequestHeaders = null; - ourRequestFirstHeaders = null; - ourRequestMethod = null; - ourRequestHeadersAll = Lists.newArrayList(); - } - - @BeforeAll - public static void beforeClass() throws Exception { - ourCtx = FhirContext.forDstu2(); - - ourServer = new Server(0); - ourServer.setHandler(new AbstractHandler() { - - @Override - public void handle(String theArg0, Request theRequest, HttpServletRequest theServletRequest, HttpServletResponse theResp) throws IOException, ServletException { - theRequest.setHandled(true); - ourRequestUri = theRequest.getHttpURI().toString(); - ourRequestUriAll.add(ourRequestUri); - ourRequestMethod = theRequest.getMethod(); - ourRequestContentType = theServletRequest.getContentType(); - ourRequestBodyBytes = IOUtils.toByteArray(theServletRequest.getInputStream()); - ourRequestBodyString = new String(ourRequestBodyBytes, Charsets.UTF_8); - - ourRequestHeaders = ArrayListMultimap.create(); - ourRequestHeadersAll.add(ourRequestHeaders); - ourRequestFirstHeaders = Maps.newHashMap(); - - for (Enumeration headerNameEnum = theRequest.getHeaderNames(); headerNameEnum.hasMoreElements(); ) { - String nextName = headerNameEnum.nextElement(); - for (Enumeration headerValueEnum = theRequest.getHeaders(nextName); headerValueEnum.hasMoreElements(); ) { - String nextValue = headerValueEnum.nextElement(); - if (ourRequestFirstHeaders.containsKey(nextName) == false) { - ourRequestFirstHeaders.put(nextName, new Header(nextName, nextValue)); - } - ourRequestHeaders.put(nextName, new Header(nextName, nextValue)); - } - } - - theResp.setStatus(ourResponseStatus); - - if (ourResponseBody != null) { - theResp.setContentType(ourResponseContentType); - theResp.getWriter().write(ourResponseBody); - } else if (ourResponseBodies != null) { - theResp.setContentType(ourResponseContentType); - theResp.getWriter().write(ourResponseBodies[ourResponseCount]); - } - - ourResponseCount++; - } - }); - - JettyUtil.startServer(ourServer); - ourPort = JettyUtil.getPortForStartedServer(ourServer); - } - - @AfterAll - public static void afterClass() throws Exception { - JettyUtil.closeServer(ourServer); - } } diff --git a/hapi-fhir-client/pom.xml b/hapi-fhir-client/pom.xml index 0e8df018a33..8dfd0a83790 100644 --- a/hapi-fhir-client/pom.xml +++ b/hapi-fhir-client/pom.xml @@ -4,7 +4,7 @@ ca.uhn.hapi.fhir hapi-deployable-pom - 6.9.7-SNAPSHOT + 6.11.7-SNAPSHOT ../hapi-deployable-pom/pom.xml diff --git a/hapi-fhir-client/src/main/java/ca/uhn/fhir/rest/client/apache/ApacheHttpClient.java b/hapi-fhir-client/src/main/java/ca/uhn/fhir/rest/client/apache/ApacheHttpClient.java index 4235ef0de4a..4269b074d22 100644 --- a/hapi-fhir-client/src/main/java/ca/uhn/fhir/rest/client/apache/ApacheHttpClient.java +++ b/hapi-fhir-client/src/main/java/ca/uhn/fhir/rest/client/apache/ApacheHttpClient.java @@ -22,19 +22,29 @@ package ca.uhn.fhir.rest.client.apache; import ca.uhn.fhir.i18n.Msg; import ca.uhn.fhir.rest.api.Constants; import ca.uhn.fhir.rest.api.RequestTypeEnum; -import ca.uhn.fhir.rest.client.api.*; +import ca.uhn.fhir.rest.client.api.Header; +import ca.uhn.fhir.rest.client.api.IHttpClient; +import ca.uhn.fhir.rest.client.api.IHttpRequest; import ca.uhn.fhir.rest.server.exceptions.InternalErrorException; import org.apache.http.HttpEntity; import org.apache.http.NameValuePair; import org.apache.http.client.HttpClient; import org.apache.http.client.entity.UrlEncodedFormEntity; -import org.apache.http.client.methods.*; +import org.apache.http.client.methods.HttpDelete; +import org.apache.http.client.methods.HttpGet; +import org.apache.http.client.methods.HttpOptions; +import org.apache.http.client.methods.HttpPatch; +import org.apache.http.client.methods.HttpPost; +import org.apache.http.client.methods.HttpPut; +import org.apache.http.client.methods.HttpRequestBase; import org.apache.http.entity.ByteArrayEntity; import org.apache.http.message.BasicNameValuePair; import java.io.UnsupportedEncodingException; +import java.util.ArrayList; +import java.util.List; +import java.util.Map; import java.util.Map.Entry; -import java.util.*; /** * A Http Client based on Apache. This is an adapter around the class diff --git a/hapi-fhir-client/src/main/java/ca/uhn/fhir/rest/client/apache/ApacheHttpResponse.java b/hapi-fhir-client/src/main/java/ca/uhn/fhir/rest/client/apache/ApacheHttpResponse.java index 33c573d7054..2fb0546f33e 100644 --- a/hapi-fhir-client/src/main/java/ca/uhn/fhir/rest/client/apache/ApacheHttpResponse.java +++ b/hapi-fhir-client/src/main/java/ca/uhn/fhir/rest/client/apache/ApacheHttpResponse.java @@ -26,14 +26,24 @@ import ca.uhn.fhir.rest.client.impl.BaseHttpResponse; import ca.uhn.fhir.rest.server.exceptions.InternalErrorException; import ca.uhn.fhir.util.StopWatch; import org.apache.commons.io.IOUtils; +import org.apache.http.Header; +import org.apache.http.HttpEntity; +import org.apache.http.HttpResponse; import org.apache.http.client.methods.CloseableHttpResponse; import org.apache.http.entity.ContentType; -import org.apache.http.*; -import java.io.*; +import java.io.ByteArrayInputStream; +import java.io.IOException; +import java.io.InputStream; +import java.io.InputStreamReader; +import java.io.Reader; +import java.io.StringReader; import java.nio.charset.Charset; import java.nio.charset.StandardCharsets; -import java.util.*; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; /** * A Http Response based on Apache. This is an adapter around the class diff --git a/hapi-fhir-client/src/main/java/ca/uhn/fhir/rest/client/apache/BaseHttpClient.java b/hapi-fhir-client/src/main/java/ca/uhn/fhir/rest/client/apache/BaseHttpClient.java index 79fc6d71461..e6c0cdea08c 100644 --- a/hapi-fhir-client/src/main/java/ca/uhn/fhir/rest/client/apache/BaseHttpClient.java +++ b/hapi-fhir-client/src/main/java/ca/uhn/fhir/rest/client/apache/BaseHttpClient.java @@ -20,7 +20,9 @@ package ca.uhn.fhir.rest.client.apache; import ca.uhn.fhir.context.FhirContext; -import ca.uhn.fhir.rest.api.*; +import ca.uhn.fhir.rest.api.Constants; +import ca.uhn.fhir.rest.api.EncodingEnum; +import ca.uhn.fhir.rest.api.RequestTypeEnum; import ca.uhn.fhir.rest.client.api.Header; import ca.uhn.fhir.rest.client.api.HttpClientUtil; import ca.uhn.fhir.rest.client.api.IHttpClient; diff --git a/hapi-fhir-client/src/main/java/ca/uhn/fhir/rest/client/apache/GZipContentInterceptor.java b/hapi-fhir-client/src/main/java/ca/uhn/fhir/rest/client/apache/GZipContentInterceptor.java index ce6869ec4fd..06e974f6c41 100644 --- a/hapi-fhir-client/src/main/java/ca/uhn/fhir/rest/client/apache/GZipContentInterceptor.java +++ b/hapi-fhir-client/src/main/java/ca/uhn/fhir/rest/client/apache/GZipContentInterceptor.java @@ -20,7 +20,9 @@ package ca.uhn.fhir.rest.client.apache; import ca.uhn.fhir.rest.api.Constants; -import ca.uhn.fhir.rest.client.api.*; +import ca.uhn.fhir.rest.client.api.IClientInterceptor; +import ca.uhn.fhir.rest.client.api.IHttpRequest; +import ca.uhn.fhir.rest.client.api.IHttpResponse; import org.apache.http.Header; import org.apache.http.HttpEntityEnclosingRequest; import org.apache.http.client.methods.HttpRequestBase; diff --git a/hapi-fhir-client/src/main/java/ca/uhn/fhir/rest/client/apache/ModifiedStringApacheHttpResponse.java b/hapi-fhir-client/src/main/java/ca/uhn/fhir/rest/client/apache/ModifiedStringApacheHttpResponse.java new file mode 100644 index 00000000000..28ea8163668 --- /dev/null +++ b/hapi-fhir-client/src/main/java/ca/uhn/fhir/rest/client/apache/ModifiedStringApacheHttpResponse.java @@ -0,0 +1,135 @@ +/* + * #%L + * HAPI FHIR - Client Framework + * %% + * Copyright (C) 2014 - 2023 Smile CDR, Inc. + * %% + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * #L% + */ +package ca.uhn.fhir.rest.client.apache; + +import ca.uhn.fhir.i18n.Msg; +import ca.uhn.fhir.rest.client.api.IHttpResponse; +import ca.uhn.fhir.rest.client.impl.BaseHttpResponse; +import ca.uhn.fhir.rest.server.exceptions.InternalErrorException; +import ca.uhn.fhir.util.StopWatch; +import org.apache.commons.io.IOUtils; +import org.apache.http.client.methods.CloseableHttpResponse; + +import java.io.ByteArrayInputStream; +import java.io.IOException; +import java.io.InputStream; +import java.io.InputStreamReader; +import java.io.Reader; +import java.nio.charset.StandardCharsets; +import java.util.List; +import java.util.Map; + +/** + * Process a modified copy of an existing {@link IHttpResponse} with a String containing new content. + *

    + * Meant to be used with custom interceptors that need to hijack an existing IHttpResponse with new content. + */ +public class ModifiedStringApacheHttpResponse extends BaseHttpResponse implements IHttpResponse { + private static final org.slf4j.Logger ourLog = + org.slf4j.LoggerFactory.getLogger(ModifiedStringApacheHttpResponse.class); + private boolean myEntityBuffered = false; + private final String myNewContent; + private final IHttpResponse myOrigHttpResponse; + private byte[] myEntityBytes = null; + + public ModifiedStringApacheHttpResponse( + IHttpResponse theOrigHttpResponse, String theNewContent, StopWatch theResponseStopWatch) { + super(theResponseStopWatch); + myOrigHttpResponse = theOrigHttpResponse; + myNewContent = theNewContent; + } + + @Override + public void bufferEntity() throws IOException { + if (myEntityBuffered) { + return; + } + try (InputStream respEntity = readEntity()) { + if (respEntity != null) { + try { + myEntityBytes = IOUtils.toByteArray(respEntity); + } catch (IllegalStateException exception) { + throw new InternalErrorException(Msg.code(2447) + exception); + } + myEntityBuffered = true; + } + } + } + + @Override + public void close() { + if (myOrigHttpResponse instanceof CloseableHttpResponse) { + try { + ((CloseableHttpResponse) myOrigHttpResponse).close(); + } catch (IOException exception) { + ourLog.debug("Failed to close response", exception); + } + } + } + + @Override + public Reader createReader() throws IOException { + return new InputStreamReader(readEntity(), StandardCharsets.UTF_8); + } + + @Override + public Map> getAllHeaders() { + return myOrigHttpResponse.getAllHeaders(); + } + + @Override + public List getHeaders(String theName) { + return myOrigHttpResponse.getHeaders(theName); + } + + @Override + public String getMimeType() { + return myOrigHttpResponse.getMimeType(); + } + + @Override + public StopWatch getRequestStopWatch() { + return myOrigHttpResponse.getRequestStopWatch(); + } + + @Override + public Object getResponse() { + return null; + } + + @Override + public int getStatus() { + return myOrigHttpResponse.getStatus(); + } + + @Override + public String getStatusInfo() { + return myOrigHttpResponse.getStatusInfo(); + } + + @Override + public InputStream readEntity() { + if (myEntityBuffered) { + return new ByteArrayInputStream(myEntityBytes); + } else { + return new ByteArrayInputStream(myNewContent.getBytes()); + } + } +} diff --git a/hapi-fhir-client/src/main/java/ca/uhn/fhir/rest/client/impl/BaseClient.java b/hapi-fhir-client/src/main/java/ca/uhn/fhir/rest/client/impl/BaseClient.java index 857fc4181b5..62ce2db85a3 100644 --- a/hapi-fhir-client/src/main/java/ca/uhn/fhir/rest/client/impl/BaseClient.java +++ b/hapi-fhir-client/src/main/java/ca/uhn/fhir/rest/client/impl/BaseClient.java @@ -36,6 +36,7 @@ import ca.uhn.fhir.rest.api.Constants; import ca.uhn.fhir.rest.api.EncodingEnum; import ca.uhn.fhir.rest.api.RequestFormatParamStyleEnum; import ca.uhn.fhir.rest.api.SummaryEnum; +import ca.uhn.fhir.rest.client.api.ClientResponseContext; import ca.uhn.fhir.rest.client.api.IHttpClient; import ca.uhn.fhir.rest.client.api.IHttpRequest; import ca.uhn.fhir.rest.client.api.IHttpResponse; @@ -57,6 +58,7 @@ import ca.uhn.fhir.util.BinaryUtil; import ca.uhn.fhir.util.OperationOutcomeUtil; import ca.uhn.fhir.util.XmlDetectionUtil; import com.google.common.base.Charsets; +import jakarta.annotation.Nonnull; import org.apache.commons.io.IOUtils; import org.apache.commons.lang3.StringUtils; import org.apache.commons.lang3.Validate; @@ -78,7 +80,6 @@ import java.util.LinkedHashMap; import java.util.List; import java.util.Map; import java.util.Set; -import javax.annotation.Nonnull; import static org.apache.commons.lang3.StringUtils.isBlank; import static org.apache.commons.lang3.StringUtils.isNotBlank; @@ -352,12 +353,24 @@ public abstract class BaseClient implements IRestfulClient { response = httpRequest.execute(); + final Class returnType = (binding instanceof ResourceResponseHandler) + ? ((ResourceResponseHandler) binding).getReturnType() + : null; + + final ClientResponseContext clientResponseContext = + new ClientResponseContext(httpRequest, response, this, getFhirContext(), returnType); HookParams responseParams = new HookParams(); responseParams.add(IHttpRequest.class, httpRequest); responseParams.add(IHttpResponse.class, response); responseParams.add(IRestfulClient.class, this); + responseParams.add(ClientResponseContext.class, clientResponseContext); + getInterceptorService().callHooks(Pointcut.CLIENT_RESPONSE, responseParams); + // Replace the contents of the response with whatever the hook returned, or the same response as before if + // it no-op'd + response = clientResponseContext.getHttpResponse(); + String mimeType; if (Constants.STATUS_HTTP_204_NO_CONTENT == response.getStatus()) { mimeType = null; @@ -645,6 +658,10 @@ public abstract class BaseClient implements IRestfulClient { myAllowHtmlResponse = theAllowHtmlResponse; } + public Class getReturnType() { + return myReturnType; + } + @Override public T invokeClient( String theResponseMimeType, diff --git a/hapi-fhir-client/src/main/java/ca/uhn/fhir/rest/client/interceptor/BasicAuthInterceptor.java b/hapi-fhir-client/src/main/java/ca/uhn/fhir/rest/client/interceptor/BasicAuthInterceptor.java index 6cb8d3c1dfa..2e86a893fdb 100644 --- a/hapi-fhir-client/src/main/java/ca/uhn/fhir/rest/client/interceptor/BasicAuthInterceptor.java +++ b/hapi-fhir-client/src/main/java/ca/uhn/fhir/rest/client/interceptor/BasicAuthInterceptor.java @@ -20,7 +20,9 @@ package ca.uhn.fhir.rest.client.interceptor; import ca.uhn.fhir.rest.api.Constants; -import ca.uhn.fhir.rest.client.api.*; +import ca.uhn.fhir.rest.client.api.IClientInterceptor; +import ca.uhn.fhir.rest.client.api.IHttpRequest; +import ca.uhn.fhir.rest.client.api.IHttpResponse; import org.apache.commons.codec.binary.Base64; import org.apache.commons.lang3.StringUtils; import org.apache.commons.lang3.Validate; diff --git a/hapi-fhir-client/src/main/java/ca/uhn/fhir/rest/client/interceptor/CookieInterceptor.java b/hapi-fhir-client/src/main/java/ca/uhn/fhir/rest/client/interceptor/CookieInterceptor.java index 923680eaabc..4f22f76470d 100644 --- a/hapi-fhir-client/src/main/java/ca/uhn/fhir/rest/client/interceptor/CookieInterceptor.java +++ b/hapi-fhir-client/src/main/java/ca/uhn/fhir/rest/client/interceptor/CookieInterceptor.java @@ -20,7 +20,9 @@ package ca.uhn.fhir.rest.client.interceptor; import ca.uhn.fhir.rest.api.Constants; -import ca.uhn.fhir.rest.client.api.*; +import ca.uhn.fhir.rest.client.api.IClientInterceptor; +import ca.uhn.fhir.rest.client.api.IHttpRequest; +import ca.uhn.fhir.rest.client.api.IHttpResponse; /** * HTTP interceptor to be used for adding Cookie to requests. diff --git a/hapi-fhir-client/src/main/java/ca/uhn/fhir/rest/client/method/BaseMethodBinding.java b/hapi-fhir-client/src/main/java/ca/uhn/fhir/rest/client/method/BaseMethodBinding.java index 0e8e1c33a6b..11c50a18655 100644 --- a/hapi-fhir-client/src/main/java/ca/uhn/fhir/rest/client/method/BaseMethodBinding.java +++ b/hapi-fhir-client/src/main/java/ca/uhn/fhir/rest/client/method/BaseMethodBinding.java @@ -19,16 +19,41 @@ */ package ca.uhn.fhir.rest.client.method; -import ca.uhn.fhir.context.*; +import ca.uhn.fhir.context.ConfigurationException; +import ca.uhn.fhir.context.FhirContext; import ca.uhn.fhir.i18n.Msg; -import ca.uhn.fhir.model.api.*; +import ca.uhn.fhir.model.api.IResource; import ca.uhn.fhir.model.base.resource.BaseOperationOutcome; import ca.uhn.fhir.parser.IParser; -import ca.uhn.fhir.rest.annotation.*; -import ca.uhn.fhir.rest.api.*; +import ca.uhn.fhir.rest.annotation.AddTags; +import ca.uhn.fhir.rest.annotation.Create; +import ca.uhn.fhir.rest.annotation.Delete; +import ca.uhn.fhir.rest.annotation.DeleteTags; +import ca.uhn.fhir.rest.annotation.GetPage; +import ca.uhn.fhir.rest.annotation.History; +import ca.uhn.fhir.rest.annotation.Metadata; +import ca.uhn.fhir.rest.annotation.Operation; +import ca.uhn.fhir.rest.annotation.Patch; +import ca.uhn.fhir.rest.annotation.Read; +import ca.uhn.fhir.rest.annotation.Search; +import ca.uhn.fhir.rest.annotation.Transaction; +import ca.uhn.fhir.rest.annotation.Update; +import ca.uhn.fhir.rest.annotation.Validate; +import ca.uhn.fhir.rest.api.Constants; +import ca.uhn.fhir.rest.api.EncodingEnum; +import ca.uhn.fhir.rest.api.MethodOutcome; +import ca.uhn.fhir.rest.api.RestOperationTypeEnum; import ca.uhn.fhir.rest.client.exceptions.NonFhirResponseException; import ca.uhn.fhir.rest.client.impl.BaseHttpClientInvocation; -import ca.uhn.fhir.rest.server.exceptions.*; +import ca.uhn.fhir.rest.server.exceptions.BaseServerResponseException; +import ca.uhn.fhir.rest.server.exceptions.InternalErrorException; +import ca.uhn.fhir.rest.server.exceptions.InvalidRequestException; +import ca.uhn.fhir.rest.server.exceptions.MethodNotAllowedException; +import ca.uhn.fhir.rest.server.exceptions.PreconditionFailedException; +import ca.uhn.fhir.rest.server.exceptions.ResourceNotFoundException; +import ca.uhn.fhir.rest.server.exceptions.ResourceVersionConflictException; +import ca.uhn.fhir.rest.server.exceptions.UnclassifiedServerFailureException; +import ca.uhn.fhir.rest.server.exceptions.UnprocessableEntityException; import ca.uhn.fhir.util.ReflectionUtil; import org.apache.commons.io.IOUtils; import org.hl7.fhir.instance.model.api.IAnyResource; @@ -37,7 +62,10 @@ import org.hl7.fhir.instance.model.api.IBaseResource; import java.io.IOException; import java.io.InputStream; import java.lang.reflect.Method; -import java.util.*; +import java.util.Collection; +import java.util.List; +import java.util.Set; +import java.util.TreeSet; public abstract class BaseMethodBinding implements IClientResponseHandler { diff --git a/hapi-fhir-client/src/main/java/ca/uhn/fhir/rest/client/method/BaseOutcomeReturningMethodBinding.java b/hapi-fhir-client/src/main/java/ca/uhn/fhir/rest/client/method/BaseOutcomeReturningMethodBinding.java index 91db00c0dbd..c2043d7e9dd 100644 --- a/hapi-fhir-client/src/main/java/ca/uhn/fhir/rest/client/method/BaseOutcomeReturningMethodBinding.java +++ b/hapi-fhir-client/src/main/java/ca/uhn/fhir/rest/client/method/BaseOutcomeReturningMethodBinding.java @@ -32,7 +32,9 @@ import org.hl7.fhir.instance.model.api.IIdType; import java.io.InputStream; import java.lang.reflect.Method; -import java.util.*; +import java.util.List; +import java.util.Map; +import java.util.Set; abstract class BaseOutcomeReturningMethodBinding extends BaseMethodBinding { static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(BaseOutcomeReturningMethodBinding.class); diff --git a/hapi-fhir-client/src/main/java/ca/uhn/fhir/rest/client/method/BaseOutcomeReturningMethodBindingWithResourceIdButNoResourceBody.java b/hapi-fhir-client/src/main/java/ca/uhn/fhir/rest/client/method/BaseOutcomeReturningMethodBindingWithResourceIdButNoResourceBody.java index a31703842ab..af1b00f22b9 100644 --- a/hapi-fhir-client/src/main/java/ca/uhn/fhir/rest/client/method/BaseOutcomeReturningMethodBindingWithResourceIdButNoResourceBody.java +++ b/hapi-fhir-client/src/main/java/ca/uhn/fhir/rest/client/method/BaseOutcomeReturningMethodBindingWithResourceIdButNoResourceBody.java @@ -19,9 +19,12 @@ */ package ca.uhn.fhir.rest.client.method; -import ca.uhn.fhir.context.*; +import ca.uhn.fhir.context.ConfigurationException; +import ca.uhn.fhir.context.FhirContext; +import ca.uhn.fhir.context.RuntimeResourceDefinition; import ca.uhn.fhir.i18n.Msg; -import ca.uhn.fhir.rest.annotation.*; +import ca.uhn.fhir.rest.annotation.Delete; +import ca.uhn.fhir.rest.annotation.IdParam; import ca.uhn.fhir.rest.param.ParameterUtil; import org.hl7.fhir.instance.model.api.IBaseResource; diff --git a/hapi-fhir-client/src/main/java/ca/uhn/fhir/rest/client/method/ConditionalParamBinder.java b/hapi-fhir-client/src/main/java/ca/uhn/fhir/rest/client/method/ConditionalParamBinder.java index 37ce4bdf3eb..12a479b9e72 100644 --- a/hapi-fhir-client/src/main/java/ca/uhn/fhir/rest/client/method/ConditionalParamBinder.java +++ b/hapi-fhir-client/src/main/java/ca/uhn/fhir/rest/client/method/ConditionalParamBinder.java @@ -29,7 +29,9 @@ import org.apache.commons.lang3.Validate; import org.hl7.fhir.instance.model.api.IBaseResource; import java.lang.reflect.Method; -import java.util.*; +import java.util.Collection; +import java.util.List; +import java.util.Map; class ConditionalParamBinder implements IParameter { diff --git a/hapi-fhir-client/src/main/java/ca/uhn/fhir/rest/client/method/DeleteMethodBinding.java b/hapi-fhir-client/src/main/java/ca/uhn/fhir/rest/client/method/DeleteMethodBinding.java index a69d57f5c62..6e63d2a368e 100644 --- a/hapi-fhir-client/src/main/java/ca/uhn/fhir/rest/client/method/DeleteMethodBinding.java +++ b/hapi-fhir-client/src/main/java/ca/uhn/fhir/rest/client/method/DeleteMethodBinding.java @@ -31,7 +31,10 @@ import org.hl7.fhir.instance.model.api.IBaseResource; import org.hl7.fhir.instance.model.api.IIdType; import java.lang.reflect.Method; -import java.util.*; +import java.util.Collections; +import java.util.List; +import java.util.Map; +import java.util.Set; public class DeleteMethodBinding extends BaseOutcomeReturningMethodBindingWithResourceIdButNoResourceBody { diff --git a/hapi-fhir-client/src/main/java/ca/uhn/fhir/rest/client/method/HistoryMethodBinding.java b/hapi-fhir-client/src/main/java/ca/uhn/fhir/rest/client/method/HistoryMethodBinding.java index 86997ad19e1..38580994c73 100644 --- a/hapi-fhir-client/src/main/java/ca/uhn/fhir/rest/client/method/HistoryMethodBinding.java +++ b/hapi-fhir-client/src/main/java/ca/uhn/fhir/rest/client/method/HistoryMethodBinding.java @@ -31,7 +31,9 @@ import ca.uhn.fhir.rest.param.DateParam; import ca.uhn.fhir.rest.param.DateRangeParam; import ca.uhn.fhir.rest.param.ParameterUtil; import ca.uhn.fhir.rest.server.exceptions.InternalErrorException; -import org.hl7.fhir.instance.model.api.*; +import org.hl7.fhir.instance.model.api.IBaseResource; +import org.hl7.fhir.instance.model.api.IIdType; +import org.hl7.fhir.instance.model.api.IPrimitiveType; import java.lang.reflect.Method; import java.lang.reflect.Modifier; diff --git a/hapi-fhir-client/src/main/java/ca/uhn/fhir/rest/client/method/NullParameter.java b/hapi-fhir-client/src/main/java/ca/uhn/fhir/rest/client/method/NullParameter.java index d80cc262a19..2fff425f0ac 100644 --- a/hapi-fhir-client/src/main/java/ca/uhn/fhir/rest/client/method/NullParameter.java +++ b/hapi-fhir-client/src/main/java/ca/uhn/fhir/rest/client/method/NullParameter.java @@ -24,7 +24,9 @@ import ca.uhn.fhir.rest.server.exceptions.InternalErrorException; import org.hl7.fhir.instance.model.api.IBaseResource; import java.lang.reflect.Method; -import java.util.*; +import java.util.Collection; +import java.util.List; +import java.util.Map; class NullParameter implements IParameter { diff --git a/hapi-fhir-client/src/main/java/ca/uhn/fhir/rest/client/method/OperationMethodBinding.java b/hapi-fhir-client/src/main/java/ca/uhn/fhir/rest/client/method/OperationMethodBinding.java index b852238c9bc..fb723b1849d 100644 --- a/hapi-fhir-client/src/main/java/ca/uhn/fhir/rest/client/method/OperationMethodBinding.java +++ b/hapi-fhir-client/src/main/java/ca/uhn/fhir/rest/client/method/OperationMethodBinding.java @@ -31,7 +31,13 @@ import ca.uhn.fhir.rest.param.ParameterUtil; import ca.uhn.fhir.rest.server.exceptions.InternalErrorException; import ca.uhn.fhir.util.FhirTerser; import ca.uhn.fhir.util.ParametersUtil; -import org.hl7.fhir.instance.model.api.*; +import org.hl7.fhir.instance.model.api.IBase; +import org.hl7.fhir.instance.model.api.IBaseBundle; +import org.hl7.fhir.instance.model.api.IBaseDatatype; +import org.hl7.fhir.instance.model.api.IBaseParameters; +import org.hl7.fhir.instance.model.api.IBaseResource; +import org.hl7.fhir.instance.model.api.IIdType; +import org.hl7.fhir.instance.model.api.IPrimitiveType; import java.lang.reflect.Method; import java.lang.reflect.Modifier; diff --git a/hapi-fhir-client/src/main/java/ca/uhn/fhir/rest/client/method/PatchMethodBinding.java b/hapi-fhir-client/src/main/java/ca/uhn/fhir/rest/client/method/PatchMethodBinding.java index fd53b07ae4f..cee15f46595 100644 --- a/hapi-fhir-client/src/main/java/ca/uhn/fhir/rest/client/method/PatchMethodBinding.java +++ b/hapi-fhir-client/src/main/java/ca/uhn/fhir/rest/client/method/PatchMethodBinding.java @@ -24,7 +24,9 @@ import ca.uhn.fhir.context.FhirContext; import ca.uhn.fhir.i18n.Msg; import ca.uhn.fhir.rest.annotation.Patch; import ca.uhn.fhir.rest.annotation.ResourceParam; -import ca.uhn.fhir.rest.api.*; +import ca.uhn.fhir.rest.api.PatchTypeEnum; +import ca.uhn.fhir.rest.api.RequestTypeEnum; +import ca.uhn.fhir.rest.api.RestOperationTypeEnum; import ca.uhn.fhir.rest.client.impl.BaseHttpClientInvocation; import ca.uhn.fhir.rest.server.exceptions.InternalErrorException; import ca.uhn.fhir.rest.server.exceptions.InvalidRequestException; @@ -33,7 +35,12 @@ import org.hl7.fhir.instance.model.api.IIdType; import java.lang.annotation.Annotation; import java.lang.reflect.Method; -import java.util.*; +import java.util.Arrays; +import java.util.Collections; +import java.util.List; +import java.util.ListIterator; +import java.util.Map; +import java.util.Set; /** * Base class for an operation that has a resource type but not a resource body in the diff --git a/hapi-fhir-client/src/main/java/ca/uhn/fhir/rest/client/method/RawParamsParmeter.java b/hapi-fhir-client/src/main/java/ca/uhn/fhir/rest/client/method/RawParamsParmeter.java index eb147489828..8b919104632 100644 --- a/hapi-fhir-client/src/main/java/ca/uhn/fhir/rest/client/method/RawParamsParmeter.java +++ b/hapi-fhir-client/src/main/java/ca/uhn/fhir/rest/client/method/RawParamsParmeter.java @@ -26,7 +26,9 @@ import org.apache.commons.lang3.Validate; import org.hl7.fhir.instance.model.api.IBaseResource; import java.lang.reflect.Method; -import java.util.*; +import java.util.Collection; +import java.util.List; +import java.util.Map; public class RawParamsParmeter implements IParameter { diff --git a/hapi-fhir-client/src/main/java/ca/uhn/fhir/rest/client/method/ReadMethodBinding.java b/hapi-fhir-client/src/main/java/ca/uhn/fhir/rest/client/method/ReadMethodBinding.java index d2710f86fe4..014cce3e569 100644 --- a/hapi-fhir-client/src/main/java/ca/uhn/fhir/rest/client/method/ReadMethodBinding.java +++ b/hapi-fhir-client/src/main/java/ca/uhn/fhir/rest/client/method/ReadMethodBinding.java @@ -24,18 +24,25 @@ import ca.uhn.fhir.context.FhirContext; import ca.uhn.fhir.i18n.Msg; import ca.uhn.fhir.model.primitive.IdDt; import ca.uhn.fhir.model.valueset.BundleTypeEnum; -import ca.uhn.fhir.rest.annotation.*; +import ca.uhn.fhir.rest.annotation.Elements; +import ca.uhn.fhir.rest.annotation.IdParam; +import ca.uhn.fhir.rest.annotation.Read; import ca.uhn.fhir.rest.api.RestOperationTypeEnum; import ca.uhn.fhir.rest.param.ParameterUtil; import ca.uhn.fhir.rest.server.exceptions.BaseServerResponseException; import org.apache.commons.io.IOUtils; import org.apache.commons.lang3.Validate; -import org.hl7.fhir.instance.model.api.*; +import org.hl7.fhir.instance.model.api.IBaseBinary; +import org.hl7.fhir.instance.model.api.IBaseResource; +import org.hl7.fhir.instance.model.api.IIdType; import java.io.IOException; import java.io.InputStream; import java.lang.reflect.Method; -import java.util.*; +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; +import java.util.Map; public class ReadMethodBinding extends BaseResourceReturningMethodBinding implements IClientResponseHandlerHandlesBinary { diff --git a/hapi-fhir-client/src/main/java/ca/uhn/fhir/rest/client/method/SearchParameter.java b/hapi-fhir-client/src/main/java/ca/uhn/fhir/rest/client/method/SearchParameter.java index 058eda8f287..4fcecab24d8 100644 --- a/hapi-fhir-client/src/main/java/ca/uhn/fhir/rest/client/method/SearchParameter.java +++ b/hapi-fhir-client/src/main/java/ca/uhn/fhir/rest/client/method/SearchParameter.java @@ -19,16 +19,56 @@ */ package ca.uhn.fhir.rest.client.method; -import ca.uhn.fhir.context.*; +import ca.uhn.fhir.context.ConfigurationException; +import ca.uhn.fhir.context.FhirContext; +import ca.uhn.fhir.context.RuntimePrimitiveDatatypeDefinition; import ca.uhn.fhir.i18n.Msg; -import ca.uhn.fhir.model.api.*; +import ca.uhn.fhir.model.api.IQueryParameterAnd; +import ca.uhn.fhir.model.api.IQueryParameterOr; +import ca.uhn.fhir.model.api.IQueryParameterType; import ca.uhn.fhir.model.base.composite.BaseIdentifierDt; import ca.uhn.fhir.model.base.composite.BaseQuantityDt; import ca.uhn.fhir.model.primitive.StringDt; import ca.uhn.fhir.rest.annotation.OptionalParam; -import ca.uhn.fhir.rest.api.*; -import ca.uhn.fhir.rest.param.*; -import ca.uhn.fhir.rest.param.binder.*; +import ca.uhn.fhir.rest.api.Constants; +import ca.uhn.fhir.rest.api.QualifiedParamList; +import ca.uhn.fhir.rest.api.RestSearchParameterTypeEnum; +import ca.uhn.fhir.rest.param.CompositeAndListParam; +import ca.uhn.fhir.rest.param.CompositeOrListParam; +import ca.uhn.fhir.rest.param.CompositeParam; +import ca.uhn.fhir.rest.param.DateAndListParam; +import ca.uhn.fhir.rest.param.DateOrListParam; +import ca.uhn.fhir.rest.param.DateParam; +import ca.uhn.fhir.rest.param.DateRangeParam; +import ca.uhn.fhir.rest.param.HasAndListParam; +import ca.uhn.fhir.rest.param.HasOrListParam; +import ca.uhn.fhir.rest.param.HasParam; +import ca.uhn.fhir.rest.param.NumberAndListParam; +import ca.uhn.fhir.rest.param.NumberOrListParam; +import ca.uhn.fhir.rest.param.NumberParam; +import ca.uhn.fhir.rest.param.QuantityAndListParam; +import ca.uhn.fhir.rest.param.QuantityOrListParam; +import ca.uhn.fhir.rest.param.QuantityParam; +import ca.uhn.fhir.rest.param.ReferenceAndListParam; +import ca.uhn.fhir.rest.param.ReferenceOrListParam; +import ca.uhn.fhir.rest.param.ReferenceParam; +import ca.uhn.fhir.rest.param.StringAndListParam; +import ca.uhn.fhir.rest.param.StringOrListParam; +import ca.uhn.fhir.rest.param.StringParam; +import ca.uhn.fhir.rest.param.TokenAndListParam; +import ca.uhn.fhir.rest.param.TokenOrListParam; +import ca.uhn.fhir.rest.param.TokenParam; +import ca.uhn.fhir.rest.param.UriAndListParam; +import ca.uhn.fhir.rest.param.UriOrListParam; +import ca.uhn.fhir.rest.param.UriParam; +import ca.uhn.fhir.rest.param.binder.CalendarBinder; +import ca.uhn.fhir.rest.param.binder.DateBinder; +import ca.uhn.fhir.rest.param.binder.FhirPrimitiveBinder; +import ca.uhn.fhir.rest.param.binder.IParamBinder; +import ca.uhn.fhir.rest.param.binder.QueryParameterAndBinder; +import ca.uhn.fhir.rest.param.binder.QueryParameterOrBinder; +import ca.uhn.fhir.rest.param.binder.QueryParameterTypeBinder; +import ca.uhn.fhir.rest.param.binder.StringBinder; import ca.uhn.fhir.rest.server.exceptions.InternalErrorException; import ca.uhn.fhir.util.CollectionUtil; import ca.uhn.fhir.util.ReflectionUtil; @@ -36,7 +76,16 @@ import org.apache.commons.lang3.builder.ToStringBuilder; import org.hl7.fhir.instance.model.api.IBaseResource; import org.hl7.fhir.instance.model.api.IPrimitiveType; -import java.util.*; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Calendar; +import java.util.Collection; +import java.util.Collections; +import java.util.Date; +import java.util.HashMap; +import java.util.HashSet; +import java.util.List; +import java.util.Set; public class SearchParameter extends BaseQueryParameter { diff --git a/hapi-fhir-client/src/main/java/ca/uhn/fhir/rest/client/method/TransactionParameter.java b/hapi-fhir-client/src/main/java/ca/uhn/fhir/rest/client/method/TransactionParameter.java index 546fa2bdff2..e793c0361a6 100644 --- a/hapi-fhir-client/src/main/java/ca/uhn/fhir/rest/client/method/TransactionParameter.java +++ b/hapi-fhir-client/src/main/java/ca/uhn/fhir/rest/client/method/TransactionParameter.java @@ -19,7 +19,9 @@ */ package ca.uhn.fhir.rest.client.method; -import ca.uhn.fhir.context.*; +import ca.uhn.fhir.context.ConfigurationException; +import ca.uhn.fhir.context.FhirContext; +import ca.uhn.fhir.context.RuntimeResourceDefinition; import ca.uhn.fhir.i18n.Msg; import ca.uhn.fhir.model.api.IResource; import ca.uhn.fhir.rest.annotation.TransactionParam; @@ -28,7 +30,9 @@ import org.hl7.fhir.instance.model.api.IBaseResource; import java.lang.reflect.Method; import java.lang.reflect.Modifier; -import java.util.*; +import java.util.Collection; +import java.util.List; +import java.util.Map; public class TransactionParameter implements IParameter { diff --git a/hapi-fhir-client/src/main/java/ca/uhn/fhir/rest/client/tls/TlsAuthenticationSvc.java b/hapi-fhir-client/src/main/java/ca/uhn/fhir/rest/client/tls/TlsAuthenticationSvc.java index a829b16f4c5..472e7174c43 100644 --- a/hapi-fhir-client/src/main/java/ca/uhn/fhir/rest/client/tls/TlsAuthenticationSvc.java +++ b/hapi-fhir-client/src/main/java/ca/uhn/fhir/rest/client/tls/TlsAuthenticationSvc.java @@ -25,6 +25,7 @@ import ca.uhn.fhir.tls.KeyStoreInfo; import ca.uhn.fhir.tls.PathType; import ca.uhn.fhir.tls.TlsAuthentication; import ca.uhn.fhir.tls.TrustStoreInfo; +import jakarta.annotation.Nonnull; import org.apache.commons.lang3.Validate; import org.apache.http.conn.ssl.DefaultHostnameVerifier; import org.apache.http.conn.ssl.NoopHostnameVerifier; @@ -37,7 +38,6 @@ import java.io.FileInputStream; import java.io.InputStream; import java.security.KeyStore; import java.util.Optional; -import javax.annotation.Nonnull; import javax.net.ssl.HostnameVerifier; import javax.net.ssl.SSLContext; import javax.net.ssl.TrustManager; diff --git a/hapi-fhir-converter/pom.xml b/hapi-fhir-converter/pom.xml index cfa706d8794..9589ca5d79c 100644 --- a/hapi-fhir-converter/pom.xml +++ b/hapi-fhir-converter/pom.xml @@ -5,7 +5,7 @@ ca.uhn.hapi.fhir hapi-deployable-pom - 6.9.7-SNAPSHOT + 6.11.7-SNAPSHOT ../hapi-deployable-pom/pom.xml @@ -33,8 +33,8 @@ true - javax.servlet - javax.servlet-api + jakarta.servlet + jakarta.servlet-api provided @@ -126,36 +126,6 @@ xmlunit-core test - - org.eclipse.jetty - jetty-servlets - test - - - org.eclipse.jetty - jetty-servlet - test - - - org.eclipse.jetty - jetty-server - test - - - org.eclipse.jetty - jetty-util - test - - - org.eclipse.jetty - jetty-webapp - test - - - org.eclipse.jetty - jetty-http - test - ca.uhn.hapi.fhir hapi-fhir-test-utilities @@ -183,7 +153,7 @@ ca.uhn.fhir.rest.server*;resolution:=optional, - javax.servlet.http;resolution:=optional, + jakarta.servlet.http;resolution:=optional, * diff --git a/hapi-fhir-converter/src/main/java/ca/uhn/hapi/converters/canonical/VersionCanonicalizer.java b/hapi-fhir-converter/src/main/java/ca/uhn/hapi/converters/canonical/VersionCanonicalizer.java index 16c0af9310e..83941fa05d2 100644 --- a/hapi-fhir-converter/src/main/java/ca/uhn/hapi/converters/canonical/VersionCanonicalizer.java +++ b/hapi-fhir-converter/src/main/java/ca/uhn/hapi/converters/canonical/VersionCanonicalizer.java @@ -26,6 +26,7 @@ import ca.uhn.fhir.i18n.Msg; import ca.uhn.fhir.model.dstu2.composite.CodeableConceptDt; import ca.uhn.fhir.model.dstu2.composite.CodingDt; import ca.uhn.fhir.util.HapiExtensions; +import jakarta.annotation.Nonnull; import org.apache.commons.lang3.StringUtils; import org.hl7.fhir.convertors.advisors.impl.BaseAdvisor_10_40; import org.hl7.fhir.convertors.advisors.impl.BaseAdvisor_10_50; @@ -69,7 +70,6 @@ import java.util.Date; import java.util.List; import java.util.Objects; import java.util.stream.Collectors; -import javax.annotation.Nonnull; import static org.apache.commons.lang3.StringUtils.isBlank; @@ -458,7 +458,7 @@ public class VersionCanonicalizer { @Override public IBaseParameters parametersFromCanonical(Parameters theParameters) { Resource converted = VersionConvertorFactory_10_40.convertResource(theParameters, ADVISOR_10_40); - return (IBaseParameters) reencodeToHl7Org(converted); + return (IBaseParameters) reencodeFromHl7Org(converted); } @Override @@ -470,7 +470,7 @@ public class VersionCanonicalizer { @Override public IBaseResource structureDefinitionFromCanonical(StructureDefinition theResource) { Resource converted = VersionConvertorFactory_10_50.convertResource(theResource, ADVISOR_10_50); - return reencodeToHl7Org(converted); + return reencodeFromHl7Org(converted); } @Override @@ -514,7 +514,7 @@ public class VersionCanonicalizer { @Override public IBaseConformance capabilityStatementFromCanonical(CapabilityStatement theResource) { Resource converted = VersionConvertorFactory_10_50.convertResource(theResource, ADVISOR_10_50); - return (IBaseConformance) reencodeToHl7Org(converted); + return (IBaseConformance) reencodeFromHl7Org(converted); } private Resource reencodeToHl7Org(IBaseResource theInput) { diff --git a/hapi-fhir-converter/src/main/java/ca/uhn/hapi/converters/server/VersionedApiConverterInterceptor.java b/hapi-fhir-converter/src/main/java/ca/uhn/hapi/converters/server/VersionedApiConverterInterceptor.java index c009350abc5..6de828b406f 100644 --- a/hapi-fhir-converter/src/main/java/ca/uhn/hapi/converters/server/VersionedApiConverterInterceptor.java +++ b/hapi-fhir-converter/src/main/java/ca/uhn/hapi/converters/server/VersionedApiConverterInterceptor.java @@ -31,6 +31,8 @@ import ca.uhn.fhir.rest.server.exceptions.AuthenticationException; import ca.uhn.fhir.rest.server.exceptions.InternalErrorException; import ca.uhn.fhir.rest.server.interceptor.InterceptorAdapter; import ca.uhn.fhir.rest.server.interceptor.auth.AuthorizationConstants; +import jakarta.servlet.http.HttpServletRequest; +import jakarta.servlet.http.HttpServletResponse; import org.hl7.fhir.converter.NullVersionConverterAdvisor10_30; import org.hl7.fhir.converter.NullVersionConverterAdvisor10_40; import org.hl7.fhir.convertors.factory.VersionConvertorFactory_10_30; @@ -41,8 +43,6 @@ import org.hl7.fhir.exceptions.FHIRException; import org.hl7.fhir.instance.model.api.IBaseResource; import java.util.StringTokenizer; -import javax.servlet.http.HttpServletRequest; -import javax.servlet.http.HttpServletResponse; import static org.apache.commons.lang3.StringUtils.defaultString; import static org.apache.commons.lang3.StringUtils.isBlank; diff --git a/hapi-fhir-converter/src/main/java/org/hl7/fhir/converter/NullVersionConverterAdvisor10_30.java b/hapi-fhir-converter/src/main/java/org/hl7/fhir/converter/NullVersionConverterAdvisor10_30.java index e890353c895..89ee7307da7 100644 --- a/hapi-fhir-converter/src/main/java/org/hl7/fhir/converter/NullVersionConverterAdvisor10_30.java +++ b/hapi-fhir-converter/src/main/java/org/hl7/fhir/converter/NullVersionConverterAdvisor10_30.java @@ -19,13 +19,12 @@ */ package org.hl7.fhir.converter; +import jakarta.annotation.Nullable; import org.hl7.fhir.convertors.advisors.impl.BaseAdvisor_10_30; import org.hl7.fhir.dstu3.model.CodeSystem; import org.hl7.fhir.dstu3.model.ValueSet; import org.hl7.fhir.exceptions.FHIRException; -import javax.annotation.Nullable; - public class NullVersionConverterAdvisor10_30 extends BaseAdvisor_10_30 { @Nullable diff --git a/hapi-fhir-converter/src/main/java/org/hl7/fhir/converter/NullVersionConverterAdvisor10_40.java b/hapi-fhir-converter/src/main/java/org/hl7/fhir/converter/NullVersionConverterAdvisor10_40.java index edeb30824c3..31e005d9c4e 100644 --- a/hapi-fhir-converter/src/main/java/org/hl7/fhir/converter/NullVersionConverterAdvisor10_40.java +++ b/hapi-fhir-converter/src/main/java/org/hl7/fhir/converter/NullVersionConverterAdvisor10_40.java @@ -19,13 +19,12 @@ */ package org.hl7.fhir.converter; +import jakarta.annotation.Nullable; import org.hl7.fhir.convertors.advisors.impl.BaseAdvisor_10_40; import org.hl7.fhir.exceptions.FHIRException; import org.hl7.fhir.r4.model.CodeSystem; import org.hl7.fhir.r4.model.ValueSet; -import javax.annotation.Nullable; - public class NullVersionConverterAdvisor10_40 extends BaseAdvisor_10_40 { @Nullable diff --git a/hapi-fhir-converter/src/test/java/ca/uhn/hapi/converters/canonical/VersionCanonicalizerTest.java b/hapi-fhir-converter/src/test/java/ca/uhn/hapi/converters/canonical/VersionCanonicalizerTest.java index bd1e53cba51..c9ff28b1c64 100644 --- a/hapi-fhir-converter/src/test/java/ca/uhn/hapi/converters/canonical/VersionCanonicalizerTest.java +++ b/hapi-fhir-converter/src/test/java/ca/uhn/hapi/converters/canonical/VersionCanonicalizerTest.java @@ -2,22 +2,19 @@ package ca.uhn.hapi.converters.canonical; import ca.uhn.fhir.context.FhirVersionEnum; import ca.uhn.fhir.model.dstu2.composite.CodingDt; +import ca.uhn.fhir.model.dstu2.resource.Conformance; import ca.uhn.fhir.util.HapiExtensions; -import org.apache.commons.lang3.StringUtils; -import org.hl7.fhir.convertors.factory.VersionConvertorFactory_40_50; import org.hl7.fhir.instance.model.api.IBaseCoding; -import org.hl7.fhir.instance.model.api.IBaseHasExtensions; -import org.hl7.fhir.instance.model.api.IBaseResource; -import org.hl7.fhir.instance.model.api.IPrimitiveType; import org.hl7.fhir.r4.model.CodeType; import org.hl7.fhir.r4.model.Coding; -import org.hl7.fhir.r5.model.Base; +import org.hl7.fhir.r4.model.Parameters; +import org.hl7.fhir.r5.model.CapabilityStatement; import org.hl7.fhir.r5.model.Enumeration; import org.hl7.fhir.r5.model.SearchParameter; +import org.hl7.fhir.r5.model.StructureDefinition; +import org.junit.jupiter.api.Nested; import org.junit.jupiter.api.Test; -import javax.annotation.Nonnull; -import java.util.List; import java.util.stream.Collectors; import static ca.uhn.fhir.util.ExtensionUtil.getExtensionPrimitiveValues; @@ -25,73 +22,100 @@ import static org.hamcrest.MatcherAssert.assertThat; import static org.hamcrest.Matchers.contains; import static org.hamcrest.Matchers.empty; import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNotNull; class VersionCanonicalizerTest { + @Nested + class VersionCanonicalizerR4 { - @Test - public void testToCanonicalCoding() { - VersionCanonicalizer canonicalizer = new VersionCanonicalizer(FhirVersionEnum.DSTU2); - IBaseCoding coding = new CodingDt("dstuSystem", "dstuCode"); - Coding convertedCoding = canonicalizer.codingToCanonical(coding); - assertEquals("dstuCode", convertedCoding.getCode()); - assertEquals("dstuSystem", convertedCoding.getSystem()); + private static final FhirVersionEnum FHIR_VERSION = FhirVersionEnum.R4; + private static final VersionCanonicalizer ourCanonicalizer = new VersionCanonicalizer(FHIR_VERSION); + @Test + public void testToCanonical_SearchParameterNoCustomResourceType_ConvertedCorrectly() { + org.hl7.fhir.r4.model.SearchParameter input = new org.hl7.fhir.r4.model.SearchParameter(); + input.addBase("Patient"); + input.addBase("Observation"); + input.addTarget("Organization"); + + // Test + org.hl7.fhir.r5.model.SearchParameter actual = ourCanonicalizer.searchParameterToCanonical(input); + + // Verify + assertThat(actual.getBase().stream().map(Enumeration::getCode).collect(Collectors.toList()), contains("Patient", "Observation")); + assertThat(actual.getTarget().stream().map(Enumeration::getCode).collect(Collectors.toList()), contains("Organization")); + assertThat(getExtensionPrimitiveValues(actual, HapiExtensions.EXTENSION_SEARCHPARAM_CUSTOM_BASE_RESOURCE), empty()); + assertThat(getExtensionPrimitiveValues(actual, HapiExtensions.EXTENSION_SEARCHPARAM_CUSTOM_TARGET_RESOURCE), empty()); + + } + + @Test + public void testToCanonical_SearchParameterWithCustomResourceType__ConvertedCorrectly() { + // Setup + org.hl7.fhir.r4.model.SearchParameter input = new org.hl7.fhir.r4.model.SearchParameter(); + input.addBase("Base1"); + input.addBase("Base2"); + input.addTarget("Target1"); + input.addTarget("Target2"); + + // Test + org.hl7.fhir.r5.model.SearchParameter actual = ourCanonicalizer.searchParameterToCanonical(input); + + // Verify + assertThat(actual.getBase().stream().map(Enumeration::getCode).collect(Collectors.toList()), empty()); + assertThat(actual.getTarget().stream().map(Enumeration::getCode).collect(Collectors.toList()), empty()); + assertThat(getExtensionPrimitiveValues(actual, HapiExtensions.EXTENSION_SEARCHPARAM_CUSTOM_BASE_RESOURCE), contains("Base1", "Base2")); + assertThat(getExtensionPrimitiveValues(actual, HapiExtensions.EXTENSION_SEARCHPARAM_CUSTOM_TARGET_RESOURCE), contains("Target1", "Target2")); + // Original shouldn't be modified + assertThat(input.getBase().stream().map(CodeType::getCode).toList(), contains("Base1", "Base2")); + assertThat(input.getTarget().stream().map(CodeType::getCode).toList(), contains("Target1", "Target2")); + + } } - @Test - public void testFromCanonicalSearchParameter() { - VersionCanonicalizer canonicalizer = new VersionCanonicalizer(FhirVersionEnum.DSTU2); + @Nested + class VersionCanonicalizerDstu2 { + private static final FhirVersionEnum FHIR_VERSION = FhirVersionEnum.DSTU2; + private static final VersionCanonicalizer ourCanonicalizer = new VersionCanonicalizer(FHIR_VERSION); - SearchParameter inputR5 = new SearchParameter(); - inputR5.setUrl("http://foo"); - ca.uhn.fhir.model.dstu2.resource.SearchParameter outputDstu2 = (ca.uhn.fhir.model.dstu2.resource.SearchParameter) canonicalizer.searchParameterFromCanonical(inputR5); - assertEquals("http://foo", outputDstu2.getUrl()); + @Test + public void testToCanonical_Coding_ConvertSuccessful() { + IBaseCoding coding = new CodingDt("dstuSystem", "dstuCode"); + Coding convertedCoding = ourCanonicalizer.codingToCanonical(coding); + assertEquals("dstuCode", convertedCoding.getCode()); + assertEquals("dstuSystem", convertedCoding.getSystem()); + } + + @Test + public void testFromCanonical_SearchParameter_ConvertSuccessful() { + SearchParameter inputR5 = new SearchParameter(); + inputR5.setUrl("http://foo"); + ca.uhn.fhir.model.dstu2.resource.SearchParameter outputDstu2 = (ca.uhn.fhir.model.dstu2.resource.SearchParameter) ourCanonicalizer.searchParameterFromCanonical(inputR5); + assertEquals("http://foo", outputDstu2.getUrl()); + } + + @Test + public void testFromCanonical_CapabilityStatement_ConvertSuccessful() { + CapabilityStatement inputR5 = new CapabilityStatement(); + inputR5.setUrl("http://foo"); + Conformance conformance = (Conformance) ourCanonicalizer.capabilityStatementFromCanonical(inputR5); + assertEquals("http://foo", conformance.getUrl()); + } + + @Test + public void testFromCanonical_StructureDefinition_ConvertSuccessful() { + StructureDefinition inputR5 = new StructureDefinition(); + inputR5.setId("123"); + ca.uhn.fhir.model.dstu2.resource.StructureDefinition structureDefinition = (ca.uhn.fhir.model.dstu2.resource.StructureDefinition) ourCanonicalizer.structureDefinitionFromCanonical(inputR5); + assertEquals("StructureDefinition/123", structureDefinition.getId().getValue()); + } + + @Test + public void testFromCanonical_Parameters_ConvertSuccessful() { + org.hl7.fhir.r4.model.Parameters inputR4 = new Parameters(); + inputR4.setParameter("paramA", "1"); + ca.uhn.fhir.model.dstu2.resource.Parameters parameters = (ca.uhn.fhir.model.dstu2.resource.Parameters) ourCanonicalizer.parametersFromCanonical(inputR4); + assertNotNull(parameters.getParameter()); + assertEquals("paramA", parameters.getParameter().get(0).getName()); + } } - - @Test - public void testToCanonicalSearchParameter_NoCustomResourceType() { - // Setup - VersionCanonicalizer canonicalizer = new VersionCanonicalizer(FhirVersionEnum.R4); - - org.hl7.fhir.r4.model.SearchParameter input = new org.hl7.fhir.r4.model.SearchParameter(); - input.addBase("Patient"); - input.addBase("Observation"); - input.addTarget("Organization"); - - // Test - org.hl7.fhir.r5.model.SearchParameter actual = canonicalizer.searchParameterToCanonical(input); - - // Verify - assertThat(actual.getBase().stream().map(Enumeration::getCode).collect(Collectors.toList()), contains("Patient", "Observation")); - assertThat(actual.getTarget().stream().map(Enumeration::getCode).collect(Collectors.toList()), contains("Organization")); - assertThat(getExtensionPrimitiveValues(actual, HapiExtensions.EXTENSION_SEARCHPARAM_CUSTOM_BASE_RESOURCE), empty()); - assertThat(getExtensionPrimitiveValues(actual, HapiExtensions.EXTENSION_SEARCHPARAM_CUSTOM_TARGET_RESOURCE), empty()); - - } - - @Test - public void testToCanonicalSearchParameter_WithCustomResourceType() { - // Setup - VersionCanonicalizer canonicalizer = new VersionCanonicalizer(FhirVersionEnum.R4); - - org.hl7.fhir.r4.model.SearchParameter input = new org.hl7.fhir.r4.model.SearchParameter(); - input.addBase("Base1"); - input.addBase("Base2"); - input.addTarget("Target1"); - input.addTarget("Target2"); - - // Test - org.hl7.fhir.r5.model.SearchParameter actual = canonicalizer.searchParameterToCanonical(input); - - // Verify - assertThat(actual.getBase().stream().map(Enumeration::getCode).collect(Collectors.toList()), empty()); - assertThat(actual.getTarget().stream().map(Enumeration::getCode).collect(Collectors.toList()), empty()); - assertThat(getExtensionPrimitiveValues(actual, HapiExtensions.EXTENSION_SEARCHPARAM_CUSTOM_BASE_RESOURCE), contains("Base1", "Base2")); - assertThat(getExtensionPrimitiveValues(actual, HapiExtensions.EXTENSION_SEARCHPARAM_CUSTOM_TARGET_RESOURCE), contains("Target1", "Target2")); - // Original shouldn't be modified - assertThat(input.getBase().stream().map(CodeType::getCode).toList(), contains("Base1", "Base2")); - assertThat(input.getTarget().stream().map(CodeType::getCode).toList(), contains("Target1", "Target2")); - - } - - } diff --git a/hapi-fhir-converter/src/test/java/ca/uhn/hapi/converters/server/VersionedApiConverterInterceptorR4Test.java b/hapi-fhir-converter/src/test/java/ca/uhn/hapi/converters/server/VersionedApiConverterInterceptorR4Test.java index 08e6f17231c..7d83cabfe68 100644 --- a/hapi-fhir-converter/src/test/java/ca/uhn/hapi/converters/server/VersionedApiConverterInterceptorR4Test.java +++ b/hapi-fhir-converter/src/test/java/ca/uhn/hapi/converters/server/VersionedApiConverterInterceptorR4Test.java @@ -5,7 +5,9 @@ import ca.uhn.fhir.rest.annotation.Search; import ca.uhn.fhir.rest.api.EncodingEnum; import ca.uhn.fhir.rest.server.IResourceProvider; import ca.uhn.fhir.rest.server.RestfulServer; +import ca.uhn.fhir.test.utilities.HttpClientExtension; import ca.uhn.fhir.test.utilities.JettyUtil; +import ca.uhn.fhir.test.utilities.server.RestfulServerExtension; import ca.uhn.fhir.util.TestUtil; import ca.uhn.fhir.util.UrlUtil; import org.apache.commons.io.IOUtils; @@ -15,14 +17,15 @@ import org.apache.http.impl.client.CloseableHttpClient; import org.apache.http.impl.client.HttpClientBuilder; import org.apache.http.impl.conn.PoolingHttpClientConnectionManager; import org.eclipse.jetty.server.Server; -import org.eclipse.jetty.servlet.ServletHandler; -import org.eclipse.jetty.servlet.ServletHolder; +import org.eclipse.jetty.ee10.servlet.ServletHandler; +import org.eclipse.jetty.ee10.servlet.ServletHolder; import org.hl7.fhir.dstu3.model.HumanName; import org.hl7.fhir.dstu3.model.Patient; import org.hl7.fhir.instance.model.api.IBaseResource; import org.junit.jupiter.api.AfterAll; import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.RegisterExtension; import java.nio.charset.StandardCharsets; import java.util.ArrayList; @@ -35,16 +38,21 @@ import static org.hamcrest.MatcherAssert.assertThat; public class VersionedApiConverterInterceptorR4Test { private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(VersionedApiConverterInterceptorR4Test.class); - private static CloseableHttpClient ourClient; - private static FhirContext ourCtx = FhirContext.forDstu3(); - private static int ourPort; + private static final FhirContext ourCtx = FhirContext.forDstu3Cached(); - private static Server ourServer; + @RegisterExtension + public static final RestfulServerExtension ourServer = new RestfulServerExtension(ourCtx) + .registerProvider(new DummyPatientResourceProvider()) + .registerInterceptor(new VersionedApiConverterInterceptor()) + .setDefaultResponseEncoding(EncodingEnum.JSON); + + @RegisterExtension + public static final HttpClientExtension ourClient = new HttpClientExtension(); @Test public void testSearchNormal() throws Exception { - HttpGet httpGet = new HttpGet("http://localhost:" + ourPort + "/Patient"); + HttpGet httpGet = new HttpGet(ourServer.getBaseUrl() + "/Patient"); try (CloseableHttpResponse status = ourClient.execute(httpGet)) { String responseContent = IOUtils.toString(status.getEntity().getContent(), StandardCharsets.UTF_8); ourLog.info(responseContent); @@ -54,7 +62,7 @@ public class VersionedApiConverterInterceptorR4Test { @Test public void testSearchConvertToR2() throws Exception { - HttpGet httpGet = new HttpGet("http://localhost:" + ourPort + "/Patient"); + HttpGet httpGet = new HttpGet(ourServer.getBaseUrl() + "/Patient"); httpGet.addHeader("Accept", "application/fhir+json; fhirVersion=1.0"); try (CloseableHttpResponse status = ourClient.execute(httpGet)) { String responseContent = IOUtils.toString(status.getEntity().getContent(), StandardCharsets.UTF_8); @@ -65,7 +73,7 @@ public class VersionedApiConverterInterceptorR4Test { @Test public void testSearchConvertToR2ByFormatParam() throws Exception { - HttpGet httpGet = new HttpGet("http://localhost:" + ourPort + "/Patient?_format=" + UrlUtil.escapeUrlParam("application/fhir+json; fhirVersion=1.0")); + HttpGet httpGet = new HttpGet(ourServer.getBaseUrl() + "/Patient?_format=" + UrlUtil.escapeUrlParam("application/fhir+json; fhirVersion=1.0")); try (CloseableHttpResponse status = ourClient.execute(httpGet)) { String responseContent = IOUtils.toString(status.getEntity().getContent(), StandardCharsets.UTF_8); ourLog.info(responseContent); @@ -75,36 +83,9 @@ public class VersionedApiConverterInterceptorR4Test { @AfterAll public static void afterClassClearContext() throws Exception { - JettyUtil.closeServer(ourServer); TestUtil.randomizeLocaleAndTimezone(); } - @BeforeAll - public static void beforeClass() throws Exception { - ourServer = new Server(0); - - DummyPatientResourceProvider patientProvider = new DummyPatientResourceProvider(); - - ServletHandler proxyHandler = new ServletHandler(); - RestfulServer servlet = new RestfulServer(ourCtx); - servlet.setDefaultResponseEncoding(EncodingEnum.JSON); - servlet.setDefaultPrettyPrint(true); - servlet.registerInterceptor(new VersionedApiConverterInterceptor()); - - servlet.setResourceProviders(patientProvider); - ServletHolder servletHolder = new ServletHolder(servlet); - proxyHandler.addServletWithMapping(servletHolder, "/*"); - ourServer.setHandler(proxyHandler); - JettyUtil.startServer(ourServer); - ourPort = JettyUtil.getPortForStartedServer(ourServer); - - PoolingHttpClientConnectionManager connectionManager = new PoolingHttpClientConnectionManager(5000, TimeUnit.MILLISECONDS); - HttpClientBuilder builder = HttpClientBuilder.create(); - builder.setConnectionManager(connectionManager); - ourClient = builder.build(); - - } - public static class DummyPatientResourceProvider implements IResourceProvider { @Override diff --git a/hapi-fhir-dist/pom.xml b/hapi-fhir-dist/pom.xml index f5976ab3234..e896e8c4bf0 100644 --- a/hapi-fhir-dist/pom.xml +++ b/hapi-fhir-dist/pom.xml @@ -5,7 +5,7 @@ ca.uhn.hapi.fhir hapi-fhir - 6.9.7-SNAPSHOT + 6.11.7-SNAPSHOT ../pom.xml @@ -249,18 +249,12 @@ thymeleaf - com.helger - ph-schematron - - - Saxon-HE - net.sf.saxon - - + com.helger.schematron + ph-schematron-api - com.helger - ph-commons + com.helger.schematron + ph-schematron-xslt org.springframework diff --git a/hapi-fhir-docs/pom.xml b/hapi-fhir-docs/pom.xml index 331d4b20add..53b477e73d8 100644 --- a/hapi-fhir-docs/pom.xml +++ b/hapi-fhir-docs/pom.xml @@ -5,7 +5,7 @@ ca.uhn.hapi.fhir hapi-deployable-pom - 6.9.7-SNAPSHOT + 6.11.7-SNAPSHOT ../hapi-deployable-pom/pom.xml @@ -72,10 +72,6 @@ jackson-dataformat-yaml - - javax.ejb - ejb-api - javax.ws.rs jsr311-api @@ -104,8 +100,8 @@ - javax.servlet - javax.servlet-api + jakarta.servlet + jakarta.servlet-api provided diff --git a/hapi-fhir-docs/src/main/java/ca/uhn/hapi/fhir/docs/AuthorizationInterceptors.java b/hapi-fhir-docs/src/main/java/ca/uhn/hapi/fhir/docs/AuthorizationInterceptors.java index 94a2becaeac..ae017dc82d4 100644 --- a/hapi-fhir-docs/src/main/java/ca/uhn/hapi/fhir/docs/AuthorizationInterceptors.java +++ b/hapi-fhir-docs/src/main/java/ca/uhn/hapi/fhir/docs/AuthorizationInterceptors.java @@ -35,7 +35,14 @@ import ca.uhn.fhir.rest.api.server.storage.TransactionDetails; import ca.uhn.fhir.rest.server.IResourceProvider; import ca.uhn.fhir.rest.server.RestfulServer; import ca.uhn.fhir.rest.server.exceptions.AuthenticationException; -import ca.uhn.fhir.rest.server.interceptor.auth.*; +import ca.uhn.fhir.rest.server.interceptor.auth.AdditionalCompartmentSearchParameters; +import ca.uhn.fhir.rest.server.interceptor.auth.AuthorizationInterceptor; +import ca.uhn.fhir.rest.server.interceptor.auth.AuthorizedList; +import ca.uhn.fhir.rest.server.interceptor.auth.IAuthRule; +import ca.uhn.fhir.rest.server.interceptor.auth.PolicyEnum; +import ca.uhn.fhir.rest.server.interceptor.auth.RuleBuilder; +import ca.uhn.fhir.rest.server.interceptor.auth.SearchNarrowingConsentService; +import ca.uhn.fhir.rest.server.interceptor.auth.SearchNarrowingInterceptor; import ca.uhn.fhir.rest.server.interceptor.consent.ConsentInterceptor; import ca.uhn.fhir.rest.server.interceptor.consent.RuleFilteringConsentService; import ca.uhn.fhir.rest.server.servlet.ServletRequestDetails; diff --git a/hapi-fhir-docs/src/main/java/ca/uhn/hapi/fhir/docs/AuthorizingTesterUiClientFactory.java b/hapi-fhir-docs/src/main/java/ca/uhn/hapi/fhir/docs/AuthorizingTesterUiClientFactory.java index f40e1a0ef6e..4a284f8f03e 100644 --- a/hapi-fhir-docs/src/main/java/ca/uhn/hapi/fhir/docs/AuthorizingTesterUiClientFactory.java +++ b/hapi-fhir-docs/src/main/java/ca/uhn/hapi/fhir/docs/AuthorizingTesterUiClientFactory.java @@ -23,8 +23,7 @@ import ca.uhn.fhir.context.FhirContext; import ca.uhn.fhir.rest.client.api.IGenericClient; import ca.uhn.fhir.rest.client.interceptor.BasicAuthInterceptor; import ca.uhn.fhir.rest.server.util.ITestingUiClientFactory; - -import javax.servlet.http.HttpServletRequest; +import jakarta.servlet.http.HttpServletRequest; public class AuthorizingTesterUiClientFactory implements ITestingUiClientFactory { diff --git a/hapi-fhir-docs/src/main/java/ca/uhn/hapi/fhir/docs/BalpExample.java b/hapi-fhir-docs/src/main/java/ca/uhn/hapi/fhir/docs/BalpExample.java index f04b586d38f..8ebd8519160 100644 --- a/hapi-fhir-docs/src/main/java/ca/uhn/hapi/fhir/docs/BalpExample.java +++ b/hapi-fhir-docs/src/main/java/ca/uhn/hapi/fhir/docs/BalpExample.java @@ -26,11 +26,11 @@ import ca.uhn.fhir.rest.server.RestfulServer; import ca.uhn.fhir.storage.interceptor.balp.AsyncMemoryQueueBackedFhirClientBalpSink; import ca.uhn.fhir.storage.interceptor.balp.IBalpAuditContextServices; import ca.uhn.fhir.storage.interceptor.balp.IBalpAuditEventSink; +import jakarta.annotation.Nonnull; +import jakarta.servlet.ServletException; import org.hl7.fhir.r4.model.Reference; import java.util.List; -import javax.annotation.Nonnull; -import javax.servlet.ServletException; public class BalpExample { diff --git a/hapi-fhir-docs/src/main/java/ca/uhn/hapi/fhir/docs/ClientTransactionExamples.java b/hapi-fhir-docs/src/main/java/ca/uhn/hapi/fhir/docs/ClientTransactionExamples.java index cbd954ce231..de96c72ec61 100644 --- a/hapi-fhir-docs/src/main/java/ca/uhn/hapi/fhir/docs/ClientTransactionExamples.java +++ b/hapi-fhir-docs/src/main/java/ca/uhn/hapi/fhir/docs/ClientTransactionExamples.java @@ -21,7 +21,13 @@ package ca.uhn.hapi.fhir.docs; import ca.uhn.fhir.context.FhirContext; import ca.uhn.fhir.rest.client.api.IGenericClient; -import org.hl7.fhir.r4.model.*; +import org.hl7.fhir.r4.model.Bundle; +import org.hl7.fhir.r4.model.Enumerations; +import org.hl7.fhir.r4.model.IdType; +import org.hl7.fhir.r4.model.Observation; +import org.hl7.fhir.r4.model.Patient; +import org.hl7.fhir.r4.model.Quantity; +import org.hl7.fhir.r4.model.Reference; public class ClientTransactionExamples { diff --git a/hapi-fhir-docs/src/main/java/ca/uhn/hapi/fhir/docs/Copier.java b/hapi-fhir-docs/src/main/java/ca/uhn/hapi/fhir/docs/Copier.java index 35594752f37..c16556aedb4 100644 --- a/hapi-fhir-docs/src/main/java/ca/uhn/hapi/fhir/docs/Copier.java +++ b/hapi-fhir-docs/src/main/java/ca/uhn/hapi/fhir/docs/Copier.java @@ -29,7 +29,11 @@ import org.hl7.fhir.instance.model.api.IIdType; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import java.util.*; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.HashSet; +import java.util.List; +import java.util.Set; import static org.apache.commons.lang3.StringUtils.isNotBlank; diff --git a/hapi-fhir-docs/src/main/java/ca/uhn/hapi/fhir/docs/Dstu2Examples.java b/hapi-fhir-docs/src/main/java/ca/uhn/hapi/fhir/docs/Dstu2Examples.java index 3ec9e5275b6..7de0d0ff5f1 100644 --- a/hapi-fhir-docs/src/main/java/ca/uhn/hapi/fhir/docs/Dstu2Examples.java +++ b/hapi-fhir-docs/src/main/java/ca/uhn/hapi/fhir/docs/Dstu2Examples.java @@ -24,9 +24,9 @@ import ca.uhn.fhir.parser.IParser; import ca.uhn.fhir.rest.client.api.IGenericClient; import ca.uhn.fhir.rest.server.IResourceProvider; import ca.uhn.fhir.rest.server.RestfulServer; +import jakarta.servlet.ServletException; import java.util.Collection; -import javax.servlet.ServletException; @SuppressWarnings("serial") public class Dstu2Examples { diff --git a/hapi-fhir-docs/src/main/java/ca/uhn/hapi/fhir/docs/ExampleRestfulClient.java b/hapi-fhir-docs/src/main/java/ca/uhn/hapi/fhir/docs/ExampleRestfulClient.java index d2dcb9a74f8..703dabe9c65 100644 --- a/hapi-fhir-docs/src/main/java/ca/uhn/hapi/fhir/docs/ExampleRestfulClient.java +++ b/hapi-fhir-docs/src/main/java/ca/uhn/hapi/fhir/docs/ExampleRestfulClient.java @@ -20,7 +20,8 @@ package ca.uhn.hapi.fhir.docs; import ca.uhn.fhir.context.FhirContext; -import org.hl7.fhir.r4.model.*; +import org.hl7.fhir.r4.model.Patient; +import org.hl7.fhir.r4.model.StringType; import java.util.List; diff --git a/hapi-fhir-docs/src/main/java/ca/uhn/hapi/fhir/docs/ExampleRestfulServlet.java b/hapi-fhir-docs/src/main/java/ca/uhn/hapi/fhir/docs/ExampleRestfulServlet.java index 601a94cb353..9d7fa02fea7 100644 --- a/hapi-fhir-docs/src/main/java/ca/uhn/hapi/fhir/docs/ExampleRestfulServlet.java +++ b/hapi-fhir-docs/src/main/java/ca/uhn/hapi/fhir/docs/ExampleRestfulServlet.java @@ -21,11 +21,11 @@ package ca.uhn.hapi.fhir.docs; import ca.uhn.fhir.rest.server.IResourceProvider; import ca.uhn.fhir.rest.server.RestfulServer; +import jakarta.servlet.ServletException; +import jakarta.servlet.annotation.WebServlet; import java.util.ArrayList; import java.util.List; -import javax.servlet.ServletException; -import javax.servlet.annotation.WebServlet; // START SNIPPET: servlet diff --git a/hapi-fhir-docs/src/main/java/ca/uhn/hapi/fhir/docs/ExtensionsDstu3.java b/hapi-fhir-docs/src/main/java/ca/uhn/hapi/fhir/docs/ExtensionsDstu3.java index a433b2f27d6..267ea3c7249 100644 --- a/hapi-fhir-docs/src/main/java/ca/uhn/hapi/fhir/docs/ExtensionsDstu3.java +++ b/hapi-fhir-docs/src/main/java/ca/uhn/hapi/fhir/docs/ExtensionsDstu3.java @@ -22,7 +22,14 @@ package ca.uhn.hapi.fhir.docs; import ca.uhn.fhir.context.FhirContext; import ca.uhn.fhir.parser.DataFormatException; import ca.uhn.fhir.rest.client.api.IGenericClient; -import org.hl7.fhir.r4.model.*; +import org.hl7.fhir.r4.model.Bundle; +import org.hl7.fhir.r4.model.DateTimeType; +import org.hl7.fhir.r4.model.Extension; +import org.hl7.fhir.r4.model.HumanName; +import org.hl7.fhir.r4.model.IdType; +import org.hl7.fhir.r4.model.Identifier; +import org.hl7.fhir.r4.model.Patient; +import org.hl7.fhir.r4.model.StringType; import java.io.IOException; import java.util.List; diff --git a/hapi-fhir-docs/src/main/java/ca/uhn/hapi/fhir/docs/FhirContextIntro.java b/hapi-fhir-docs/src/main/java/ca/uhn/hapi/fhir/docs/FhirContextIntro.java index 786e1ffed14..58daa9517e3 100644 --- a/hapi-fhir-docs/src/main/java/ca/uhn/hapi/fhir/docs/FhirContextIntro.java +++ b/hapi-fhir-docs/src/main/java/ca/uhn/hapi/fhir/docs/FhirContextIntro.java @@ -22,7 +22,11 @@ package ca.uhn.hapi.fhir.docs; import ca.uhn.fhir.context.FhirContext; import ca.uhn.fhir.parser.DataFormatException; import ca.uhn.fhir.parser.IParser; -import org.hl7.fhir.r4.model.*; +import org.hl7.fhir.r4.model.Enumerations; +import org.hl7.fhir.r4.model.HumanName; +import org.hl7.fhir.r4.model.Identifier; +import org.hl7.fhir.r4.model.Observation; +import org.hl7.fhir.r4.model.Patient; public class FhirContextIntro { diff --git a/hapi-fhir-docs/src/main/java/ca/uhn/hapi/fhir/docs/FhirDataModel.java b/hapi-fhir-docs/src/main/java/ca/uhn/hapi/fhir/docs/FhirDataModel.java index 4a365cd0597..37ebe099e66 100644 --- a/hapi-fhir-docs/src/main/java/ca/uhn/hapi/fhir/docs/FhirDataModel.java +++ b/hapi-fhir-docs/src/main/java/ca/uhn/hapi/fhir/docs/FhirDataModel.java @@ -22,7 +22,17 @@ package ca.uhn.hapi.fhir.docs; import ca.uhn.fhir.context.FhirContext; import ca.uhn.fhir.model.api.TemporalPrecisionEnum; import ca.uhn.fhir.rest.client.api.IGenericClient; -import org.hl7.fhir.r4.model.*; +import org.hl7.fhir.r4.model.CodeableConcept; +import org.hl7.fhir.r4.model.Coding; +import org.hl7.fhir.r4.model.Enumerations; +import org.hl7.fhir.r4.model.HumanName; +import org.hl7.fhir.r4.model.Identifier; +import org.hl7.fhir.r4.model.InstantType; +import org.hl7.fhir.r4.model.Observation; +import org.hl7.fhir.r4.model.Patient; +import org.hl7.fhir.r4.model.Quantity; +import org.hl7.fhir.r4.model.SimpleQuantity; +import org.hl7.fhir.r4.model.StringType; import java.util.Date; import java.util.List; diff --git a/hapi-fhir-docs/src/main/java/ca/uhn/hapi/fhir/docs/JaxRsConformanceProvider.java b/hapi-fhir-docs/src/main/java/ca/uhn/hapi/fhir/docs/JaxRsConformanceProvider.java index 8827962da1e..3312ba27702 100644 --- a/hapi-fhir-docs/src/main/java/ca/uhn/hapi/fhir/docs/JaxRsConformanceProvider.java +++ b/hapi-fhir-docs/src/main/java/ca/uhn/hapi/fhir/docs/JaxRsConformanceProvider.java @@ -22,13 +22,13 @@ package ca.uhn.hapi.fhir.docs; import ca.uhn.fhir.jaxrs.server.AbstractJaxRsConformanceProvider; import ca.uhn.fhir.rest.api.Constants; import ca.uhn.fhir.rest.server.IResourceProvider; +import jakarta.ejb.EJB; +import jakarta.ejb.Stateless; +import jakarta.ws.rs.Path; +import jakarta.ws.rs.Produces; +import jakarta.ws.rs.core.MediaType; import java.util.concurrent.ConcurrentHashMap; -import javax.ejb.EJB; -import javax.ejb.Stateless; -import javax.ws.rs.Path; -import javax.ws.rs.Produces; -import javax.ws.rs.core.MediaType; /** * Conformance Rest Service diff --git a/hapi-fhir-docs/src/main/java/ca/uhn/hapi/fhir/docs/JaxRsPatientRestProvider.java b/hapi-fhir-docs/src/main/java/ca/uhn/hapi/fhir/docs/JaxRsPatientRestProvider.java index 90981c02318..3130b526342 100644 --- a/hapi-fhir-docs/src/main/java/ca/uhn/hapi/fhir/docs/JaxRsPatientRestProvider.java +++ b/hapi-fhir-docs/src/main/java/ca/uhn/hapi/fhir/docs/JaxRsPatientRestProvider.java @@ -21,22 +21,30 @@ package ca.uhn.hapi.fhir.docs; import ca.uhn.fhir.jaxrs.server.AbstractJaxRsResourceProvider; import ca.uhn.fhir.model.primitive.StringDt; -import ca.uhn.fhir.rest.annotation.*; +import ca.uhn.fhir.rest.annotation.ConditionalUrlParam; +import ca.uhn.fhir.rest.annotation.Create; +import ca.uhn.fhir.rest.annotation.IdParam; +import ca.uhn.fhir.rest.annotation.Operation; +import ca.uhn.fhir.rest.annotation.OperationParam; +import ca.uhn.fhir.rest.annotation.ResourceParam; import ca.uhn.fhir.rest.api.Constants; import ca.uhn.fhir.rest.api.MethodOutcome; import ca.uhn.fhir.rest.api.RequestTypeEnum; import ca.uhn.fhir.rest.api.RestOperationTypeEnum; +import jakarta.ejb.Local; +import jakarta.ejb.Stateless; +import jakarta.ws.rs.GET; +import jakarta.ws.rs.POST; +import jakarta.ws.rs.Path; +import jakarta.ws.rs.PathParam; +import jakarta.ws.rs.Produces; +import jakarta.ws.rs.core.MediaType; +import jakarta.ws.rs.core.Response; import org.hl7.fhir.r4.model.IdType; import org.hl7.fhir.r4.model.Parameters; import org.hl7.fhir.r4.model.Patient; import org.hl7.fhir.r4.model.StringType; -import javax.ejb.Local; -import javax.ejb.Stateless; -import javax.ws.rs.core.MediaType; -import javax.ws.rs.core.Response; -import javax.ws.rs.*; - /** * A demo JaxRs Patient Rest Provider */ diff --git a/hapi-fhir-docs/src/main/java/ca/uhn/hapi/fhir/docs/PagingPatientProvider.java b/hapi-fhir-docs/src/main/java/ca/uhn/hapi/fhir/docs/PagingPatientProvider.java index 7712ad307d2..ffeff021c9d 100644 --- a/hapi-fhir-docs/src/main/java/ca/uhn/hapi/fhir/docs/PagingPatientProvider.java +++ b/hapi-fhir-docs/src/main/java/ca/uhn/hapi/fhir/docs/PagingPatientProvider.java @@ -25,12 +25,12 @@ import ca.uhn.fhir.rest.api.server.IBundleProvider; import ca.uhn.fhir.rest.param.StringParam; import ca.uhn.fhir.rest.server.IResourceProvider; import ca.uhn.fhir.rest.server.method.ResponsePage; +import jakarta.annotation.Nonnull; import org.hl7.fhir.instance.model.api.IBaseResource; import org.hl7.fhir.r4.model.InstantType; import org.hl7.fhir.r4.model.Patient; import java.util.List; -import javax.annotation.Nonnull; @SuppressWarnings("null") // START SNIPPET: provider diff --git a/hapi-fhir-docs/src/main/java/ca/uhn/hapi/fhir/docs/PartitionExamples.java b/hapi-fhir-docs/src/main/java/ca/uhn/hapi/fhir/docs/PartitionExamples.java index dbe13655830..d17a23f2afd 100644 --- a/hapi-fhir-docs/src/main/java/ca/uhn/hapi/fhir/docs/PartitionExamples.java +++ b/hapi-fhir-docs/src/main/java/ca/uhn/hapi/fhir/docs/PartitionExamples.java @@ -28,13 +28,13 @@ import ca.uhn.fhir.rest.server.RestfulServer; import ca.uhn.fhir.rest.server.exceptions.InvalidRequestException; import ca.uhn.fhir.rest.server.servlet.ServletRequestDetails; import ca.uhn.fhir.rest.server.tenant.UrlBaseTenantIdentificationStrategy; +import jakarta.servlet.http.HttpServletRequest; import org.hl7.fhir.instance.model.api.IBaseResource; import org.hl7.fhir.r4.model.Observation; import org.hl7.fhir.r4.model.Patient; import org.springframework.beans.factory.annotation.Autowired; import java.util.Set; -import javax.servlet.http.HttpServletRequest; @SuppressWarnings("InnerClassMayBeStatic") public class PartitionExamples { diff --git a/hapi-fhir-docs/src/main/java/ca/uhn/hapi/fhir/docs/RequestCounterInterceptor.java b/hapi-fhir-docs/src/main/java/ca/uhn/hapi/fhir/docs/RequestCounterInterceptor.java index be64bc050ba..ca352849127 100644 --- a/hapi-fhir-docs/src/main/java/ca/uhn/hapi/fhir/docs/RequestCounterInterceptor.java +++ b/hapi-fhir-docs/src/main/java/ca/uhn/hapi/fhir/docs/RequestCounterInterceptor.java @@ -22,9 +22,8 @@ package ca.uhn.hapi.fhir.docs; import ca.uhn.fhir.interceptor.api.Hook; import ca.uhn.fhir.interceptor.api.Interceptor; import ca.uhn.fhir.interceptor.api.Pointcut; - -import javax.servlet.http.HttpServletRequest; -import javax.servlet.http.HttpServletResponse; +import jakarta.servlet.http.HttpServletRequest; +import jakarta.servlet.http.HttpServletResponse; // START SNIPPET: interceptor @Interceptor diff --git a/hapi-fhir-docs/src/main/java/ca/uhn/hapi/fhir/docs/RequestExceptionInterceptor.java b/hapi-fhir-docs/src/main/java/ca/uhn/hapi/fhir/docs/RequestExceptionInterceptor.java index 4e89f7eb9b9..c9cffa52427 100644 --- a/hapi-fhir-docs/src/main/java/ca/uhn/hapi/fhir/docs/RequestExceptionInterceptor.java +++ b/hapi-fhir-docs/src/main/java/ca/uhn/hapi/fhir/docs/RequestExceptionInterceptor.java @@ -23,10 +23,10 @@ import ca.uhn.fhir.interceptor.api.Hook; import ca.uhn.fhir.interceptor.api.Pointcut; import ca.uhn.fhir.rest.api.server.RequestDetails; import ca.uhn.fhir.rest.server.exceptions.BaseServerResponseException; +import jakarta.servlet.http.HttpServletRequest; +import jakarta.servlet.http.HttpServletResponse; import java.io.IOException; -import javax.servlet.http.HttpServletRequest; -import javax.servlet.http.HttpServletResponse; // START SNIPPET: interceptor public class RequestExceptionInterceptor { diff --git a/hapi-fhir-docs/src/main/java/ca/uhn/hapi/fhir/docs/RestfulPatientResourceProviderMore.java b/hapi-fhir-docs/src/main/java/ca/uhn/hapi/fhir/docs/RestfulPatientResourceProviderMore.java index a1c27f0d831..2d30a3ec2d4 100644 --- a/hapi-fhir-docs/src/main/java/ca/uhn/hapi/fhir/docs/RestfulPatientResourceProviderMore.java +++ b/hapi-fhir-docs/src/main/java/ca/uhn/hapi/fhir/docs/RestfulPatientResourceProviderMore.java @@ -27,19 +27,70 @@ import ca.uhn.fhir.model.api.TemporalPrecisionEnum; import ca.uhn.fhir.model.api.annotation.Description; import ca.uhn.fhir.model.valueset.BundleEntryTransactionMethodEnum; import ca.uhn.fhir.parser.DataFormatException; +import ca.uhn.fhir.rest.annotation.At; +import ca.uhn.fhir.rest.annotation.ConditionalUrlParam; import ca.uhn.fhir.rest.annotation.Count; -import ca.uhn.fhir.rest.annotation.*; -import ca.uhn.fhir.rest.api.*; +import ca.uhn.fhir.rest.annotation.Create; +import ca.uhn.fhir.rest.annotation.Delete; +import ca.uhn.fhir.rest.annotation.Elements; +import ca.uhn.fhir.rest.annotation.History; +import ca.uhn.fhir.rest.annotation.IdParam; +import ca.uhn.fhir.rest.annotation.IncludeParam; +import ca.uhn.fhir.rest.annotation.Metadata; +import ca.uhn.fhir.rest.annotation.Offset; +import ca.uhn.fhir.rest.annotation.OptionalParam; +import ca.uhn.fhir.rest.annotation.Read; +import ca.uhn.fhir.rest.annotation.RequiredParam; +import ca.uhn.fhir.rest.annotation.ResourceParam; +import ca.uhn.fhir.rest.annotation.Search; +import ca.uhn.fhir.rest.annotation.Since; +import ca.uhn.fhir.rest.annotation.Sort; +import ca.uhn.fhir.rest.annotation.Transaction; +import ca.uhn.fhir.rest.annotation.TransactionParam; +import ca.uhn.fhir.rest.annotation.Update; +import ca.uhn.fhir.rest.annotation.Validate; +import ca.uhn.fhir.rest.api.EncodingEnum; +import ca.uhn.fhir.rest.api.MethodOutcome; +import ca.uhn.fhir.rest.api.SortOrderEnum; +import ca.uhn.fhir.rest.api.SortSpec; +import ca.uhn.fhir.rest.api.SummaryEnum; +import ca.uhn.fhir.rest.api.ValidationModeEnum; import ca.uhn.fhir.rest.client.api.IBasicClient; -import ca.uhn.fhir.rest.param.*; +import ca.uhn.fhir.rest.param.CompositeParam; +import ca.uhn.fhir.rest.param.DateParam; +import ca.uhn.fhir.rest.param.DateRangeParam; +import ca.uhn.fhir.rest.param.ParamPrefixEnum; +import ca.uhn.fhir.rest.param.QuantityParam; +import ca.uhn.fhir.rest.param.ReferenceParam; +import ca.uhn.fhir.rest.param.StringAndListParam; +import ca.uhn.fhir.rest.param.StringOrListParam; +import ca.uhn.fhir.rest.param.StringParam; +import ca.uhn.fhir.rest.param.TokenOrListParam; +import ca.uhn.fhir.rest.param.TokenParam; import ca.uhn.fhir.rest.server.IResourceProvider; -import ca.uhn.fhir.rest.server.exceptions.*; +import ca.uhn.fhir.rest.server.exceptions.InvalidRequestException; +import ca.uhn.fhir.rest.server.exceptions.PreconditionFailedException; +import ca.uhn.fhir.rest.server.exceptions.ResourceNotFoundException; +import ca.uhn.fhir.rest.server.exceptions.ResourceVersionConflictException; +import ca.uhn.fhir.rest.server.exceptions.UnprocessableEntityException; +import jakarta.servlet.http.HttpServletRequest; +import jakarta.servlet.http.HttpServletResponse; import org.hl7.fhir.instance.model.api.IBaseResource; import org.hl7.fhir.instance.model.api.IIdType; +import org.hl7.fhir.r4.model.Bundle; import org.hl7.fhir.r4.model.Bundle.BundleEntryComponent; +import org.hl7.fhir.r4.model.CapabilityStatement; +import org.hl7.fhir.r4.model.Coding; +import org.hl7.fhir.r4.model.DateTimeType; +import org.hl7.fhir.r4.model.DiagnosticReport; +import org.hl7.fhir.r4.model.IdType; import org.hl7.fhir.r4.model.Identifier.IdentifierUse; +import org.hl7.fhir.r4.model.InstantType; +import org.hl7.fhir.r4.model.Observation; +import org.hl7.fhir.r4.model.OperationOutcome; import org.hl7.fhir.r4.model.OperationOutcome.IssueSeverity; -import org.hl7.fhir.r4.model.*; +import org.hl7.fhir.r4.model.Organization; +import org.hl7.fhir.r4.model.Patient; import java.io.IOException; import java.math.BigDecimal; @@ -47,8 +98,6 @@ import java.util.ArrayList; import java.util.Date; import java.util.List; import java.util.Set; -import javax.servlet.http.HttpServletRequest; -import javax.servlet.http.HttpServletResponse; @SuppressWarnings("unused") public abstract class RestfulPatientResourceProviderMore implements IResourceProvider { diff --git a/hapi-fhir-docs/src/main/java/ca/uhn/hapi/fhir/docs/SecurityInterceptors.java b/hapi-fhir-docs/src/main/java/ca/uhn/hapi/fhir/docs/SecurityInterceptors.java index f44f6004ecf..ac81b7322af 100644 --- a/hapi-fhir-docs/src/main/java/ca/uhn/hapi/fhir/docs/SecurityInterceptors.java +++ b/hapi-fhir-docs/src/main/java/ca/uhn/hapi/fhir/docs/SecurityInterceptors.java @@ -25,11 +25,10 @@ import ca.uhn.fhir.interceptor.api.Interceptor; import ca.uhn.fhir.interceptor.api.Pointcut; import ca.uhn.fhir.rest.api.server.RequestDetails; import ca.uhn.fhir.rest.server.exceptions.AuthenticationException; +import jakarta.servlet.http.HttpServletRequest; +import jakarta.servlet.http.HttpServletResponse; import org.apache.commons.codec.binary.Base64; -import javax.servlet.http.HttpServletRequest; -import javax.servlet.http.HttpServletResponse; - public class SecurityInterceptors { public void basicAuthInterceptorRealm() { diff --git a/hapi-fhir-docs/src/main/java/ca/uhn/hapi/fhir/docs/ServerETagExamples.java b/hapi-fhir-docs/src/main/java/ca/uhn/hapi/fhir/docs/ServerETagExamples.java index 6c78c25e8f9..0e8977d4346 100644 --- a/hapi-fhir-docs/src/main/java/ca/uhn/hapi/fhir/docs/ServerETagExamples.java +++ b/hapi-fhir-docs/src/main/java/ca/uhn/hapi/fhir/docs/ServerETagExamples.java @@ -21,9 +21,8 @@ package ca.uhn.hapi.fhir.docs; import ca.uhn.fhir.rest.server.ETagSupportEnum; import ca.uhn.fhir.rest.server.RestfulServer; - -import javax.servlet.ServletException; -import javax.servlet.annotation.WebServlet; +import jakarta.servlet.ServletException; +import jakarta.servlet.annotation.WebServlet; @SuppressWarnings("serial") public class ServerETagExamples { diff --git a/hapi-fhir-docs/src/main/java/ca/uhn/hapi/fhir/docs/ServerOperations.java b/hapi-fhir-docs/src/main/java/ca/uhn/hapi/fhir/docs/ServerOperations.java index 1365d2d61ec..173887ef082 100644 --- a/hapi-fhir-docs/src/main/java/ca/uhn/hapi/fhir/docs/ServerOperations.java +++ b/hapi-fhir-docs/src/main/java/ca/uhn/hapi/fhir/docs/ServerOperations.java @@ -24,7 +24,13 @@ import ca.uhn.fhir.model.primitive.StringDt; import ca.uhn.fhir.rest.annotation.IdParam; import ca.uhn.fhir.rest.annotation.Operation; import ca.uhn.fhir.rest.annotation.OperationParam; -import ca.uhn.fhir.rest.param.*; +import ca.uhn.fhir.rest.param.DateParam; +import ca.uhn.fhir.rest.param.DateRangeParam; +import ca.uhn.fhir.rest.param.StringParam; +import ca.uhn.fhir.rest.param.TokenAndListParam; +import ca.uhn.fhir.rest.param.TokenParam; +import jakarta.servlet.http.HttpServletRequest; +import jakarta.servlet.http.HttpServletResponse; import org.apache.commons.io.IOUtils; import org.hl7.fhir.dstu3.model.Parameters; import org.hl7.fhir.r4.model.Bundle; @@ -36,8 +42,6 @@ import org.slf4j.LoggerFactory; import java.io.IOException; import java.util.List; -import javax.servlet.http.HttpServletRequest; -import javax.servlet.http.HttpServletResponse; public class ServerOperations { private static final Logger ourLog = LoggerFactory.getLogger(ServerOperations.class); diff --git a/hapi-fhir-docs/src/main/java/ca/uhn/hapi/fhir/docs/ServletExamples.java b/hapi-fhir-docs/src/main/java/ca/uhn/hapi/fhir/docs/ServletExamples.java index a6b336f3cfa..7eb61d0711b 100644 --- a/hapi-fhir-docs/src/main/java/ca/uhn/hapi/fhir/docs/ServletExamples.java +++ b/hapi-fhir-docs/src/main/java/ca/uhn/hapi/fhir/docs/ServletExamples.java @@ -37,14 +37,14 @@ import ca.uhn.fhir.rest.server.interceptor.ResponseValidatingInterceptor; import ca.uhn.fhir.rest.server.interceptor.SearchPreferHandlingInterceptor; import ca.uhn.fhir.rest.server.interceptor.StaticCapabilityStatementInterceptor; import ca.uhn.fhir.validation.ResultSeverityEnum; +import jakarta.servlet.ServletException; +import jakarta.servlet.annotation.WebServlet; import org.hl7.fhir.common.hapi.validation.validator.FhirInstanceValidator; import org.hl7.fhir.r4.model.CapabilityStatement; import org.hl7.fhir.r4.model.Enumerations; import org.springframework.web.cors.CorsConfiguration; import java.util.Arrays; -import javax.servlet.ServletException; -import javax.servlet.annotation.WebServlet; @SuppressWarnings({"serial", "RedundantThrows", "InnerClassMayBeStatic"}) public class ServletExamples { diff --git a/hapi-fhir-docs/src/main/java/ca/uhn/hapi/fhir/docs/ValidatorExamples.java b/hapi-fhir-docs/src/main/java/ca/uhn/hapi/fhir/docs/ValidatorExamples.java index ed304aa3b48..dce56e686df 100644 --- a/hapi-fhir-docs/src/main/java/ca/uhn/hapi/fhir/docs/ValidatorExamples.java +++ b/hapi-fhir-docs/src/main/java/ca/uhn/hapi/fhir/docs/ValidatorExamples.java @@ -23,6 +23,7 @@ import ca.uhn.fhir.context.FhirContext; import ca.uhn.fhir.context.support.ConceptValidationOptions; import ca.uhn.fhir.context.support.DefaultProfileValidationSupport; import ca.uhn.fhir.context.support.IValidationSupport; +import ca.uhn.fhir.context.support.LookupCodeRequest; import ca.uhn.fhir.context.support.ValidationSupportContext; import ca.uhn.fhir.i18n.Msg; import ca.uhn.fhir.parser.IParser; @@ -35,6 +36,8 @@ import ca.uhn.fhir.validation.SchemaBaseValidator; import ca.uhn.fhir.validation.SingleValidationMessage; import ca.uhn.fhir.validation.ValidationResult; import ca.uhn.fhir.validation.schematron.SchematronBaseValidator; +import jakarta.annotation.Nonnull; +import jakarta.servlet.ServletException; import org.apache.commons.io.IOUtils; import org.apache.commons.io.filefilter.WildcardFileFilter; import org.hl7.fhir.common.hapi.validation.support.CachingValidationSupport; @@ -58,8 +61,6 @@ import org.hl7.fhir.r4.model.ValueSet; import java.io.File; import java.io.FileReader; import java.util.List; -import javax.annotation.Nonnull; -import javax.servlet.ServletException; @SuppressWarnings({"serial", "unused"}) public class ValidatorExamples { @@ -308,9 +309,7 @@ public class ValidatorExamples { @Override public LookupCodeResult lookupCode( ValidationSupportContext theValidationSupportContext, - String theSystem, - String theCode, - String theDisplayLanguage) { + @Nonnull LookupCodeRequest validationSupportParameterObject) { // TODO: implement (or return null if your implementation does not support this function) return null; } diff --git a/hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/7_0_0/3786-mdm-npe.yaml b/hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/6_10_0/3786-mdm-npe.yaml similarity index 100% rename from hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/7_0_0/3786-mdm-npe.yaml rename to hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/6_10_0/3786-mdm-npe.yaml diff --git a/hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/6_10_0/4803-forced-id-step-2.yaml b/hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/6_10_0/4803-forced-id-step-2.yaml new file mode 100644 index 00000000000..c52021bb166 --- /dev/null +++ b/hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/6_10_0/4803-forced-id-step-2.yaml @@ -0,0 +1,4 @@ +--- +type: change +issue: 4803 +title: "Internal client-assigned ids are now resolved within the HFJ_RESOURCE table, avoiding a join to HFJ_FORCED_ID" diff --git a/hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/7_0_0/4834-ips-dont-include-empty-section.yaml b/hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/6_10_0/4834-ips-dont-include-empty-section.yaml similarity index 100% rename from hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/7_0_0/4834-ips-dont-include-empty-section.yaml rename to hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/6_10_0/4834-ips-dont-include-empty-section.yaml diff --git a/hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/7_0_0/5089-clinical-reasoning-measurerefactor.yaml b/hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/6_10_0/5089-clinical-reasoning-measurerefactor.yaml similarity index 100% rename from hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/7_0_0/5089-clinical-reasoning-measurerefactor.yaml rename to hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/6_10_0/5089-clinical-reasoning-measurerefactor.yaml diff --git a/hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/7_0_0/5090-adding-mdm-operation-pointcuts.yaml b/hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/6_10_0/5090-adding-mdm-operation-pointcuts.yaml similarity index 100% rename from hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/7_0_0/5090-adding-mdm-operation-pointcuts.yaml rename to hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/6_10_0/5090-adding-mdm-operation-pointcuts.yaml diff --git a/hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/7_0_0/5129-add-total-results-to-mdm-possible-duplicate-operation.yaml b/hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/6_10_0/5129-add-total-results-to-mdm-possible-duplicate-operation.yaml similarity index 100% rename from hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/7_0_0/5129-add-total-results-to-mdm-possible-duplicate-operation.yaml rename to hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/6_10_0/5129-add-total-results-to-mdm-possible-duplicate-operation.yaml diff --git a/hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/7_0_0/5141-updating-resources-will-link-to-existing-resources.yaml b/hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/6_10_0/5141-updating-resources-will-link-to-existing-resources.yaml similarity index 100% rename from hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/7_0_0/5141-updating-resources-will-link-to-existing-resources.yaml rename to hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/6_10_0/5141-updating-resources-will-link-to-existing-resources.yaml diff --git a/hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/6_10_0/5176-supporting-lastUpdated-sp-with-reverse-chaining.yaml b/hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/6_10_0/5176-supporting-lastUpdated-sp-with-reverse-chaining.yaml new file mode 100644 index 00000000000..3c050843501 --- /dev/null +++ b/hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/6_10_0/5176-supporting-lastUpdated-sp-with-reverse-chaining.yaml @@ -0,0 +1,6 @@ +--- +type: fix +issue: 5176 +jira: SMILE-6333 +title: "Previously, the use of search parameter _lastUpdated as part of a reverse chaining search would return an error + message to the client. This issue has been fixed" diff --git a/hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/6_10_0/5192-include-in-search-paging-fix.yaml b/hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/6_10_0/5192-include-in-search-paging-fix.yaml new file mode 100644 index 00000000000..7a7acb851a1 --- /dev/null +++ b/hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/6_10_0/5192-include-in-search-paging-fix.yaml @@ -0,0 +1,9 @@ +--- +type: fix +issue: 5192 +title: "Fixed a bug where search Bundles with `include` entries from an _include query parameter might + trigger a 'next' link to blank pages. + Specifically, if _include'd resources + requested resources were greater than (or equal to) + requested page size, a 'next' link would be generated, even though no additional + resources are available. +" diff --git a/hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/7_0_0/5196-type-level-expunge-operation-is-accepted-even-when-expunge_operation_enabled-is-set-to-false.yaml b/hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/6_10_0/5196-type-level-expunge-operation-is-accepted-even-when-expunge_operation_enabled-is-set-to-false.yaml similarity index 100% rename from hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/7_0_0/5196-type-level-expunge-operation-is-accepted-even-when-expunge_operation_enabled-is-set-to-false.yaml rename to hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/6_10_0/5196-type-level-expunge-operation-is-accepted-even-when-expunge_operation_enabled-is-set-to-false.yaml diff --git a/hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/7_0_0/5198-fixing-everything-search-paging-issues.yaml b/hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/6_10_0/5198-fixing-everything-search-paging-issues.yaml similarity index 100% rename from hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/7_0_0/5198-fixing-everything-search-paging-issues.yaml rename to hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/6_10_0/5198-fixing-everything-search-paging-issues.yaml diff --git a/hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/7_0_0/5212-cds-hooks-request-throwing-500-internal-server-when-context-is-missing.yaml b/hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/6_10_0/5212-cds-hooks-request-throwing-500-internal-server-when-context-is-missing.yaml similarity index 100% rename from hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/7_0_0/5212-cds-hooks-request-throwing-500-internal-server-when-context-is-missing.yaml rename to hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/6_10_0/5212-cds-hooks-request-throwing-500-internal-server-when-context-is-missing.yaml diff --git a/hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/7_0_0/5215-improve-hfql-id-filtering.yaml b/hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/6_10_0/5215-improve-hfql-id-filtering.yaml similarity index 100% rename from hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/7_0_0/5215-improve-hfql-id-filtering.yaml rename to hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/6_10_0/5215-improve-hfql-id-filtering.yaml diff --git a/hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/7_0_0/5219-batch-npe.yaml b/hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/6_10_0/5219-batch-npe.yaml similarity index 100% rename from hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/7_0_0/5219-batch-npe.yaml rename to hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/6_10_0/5219-batch-npe.yaml diff --git a/hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/7_0_0/5220-race-conditions.yaml b/hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/6_10_0/5220-race-conditions.yaml similarity index 100% rename from hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/7_0_0/5220-race-conditions.yaml rename to hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/6_10_0/5220-race-conditions.yaml diff --git a/hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/7_0_0/5230-batch2-job-cannot-transition-to-failed-status-on-mssql.yaml b/hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/6_10_0/5230-batch2-job-cannot-transition-to-failed-status-on-mssql.yaml similarity index 100% rename from hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/7_0_0/5230-batch2-job-cannot-transition-to-failed-status-on-mssql.yaml rename to hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/6_10_0/5230-batch2-job-cannot-transition-to-failed-status-on-mssql.yaml diff --git a/hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/7_0_0/5231-fix-mdm-link-history-gives-404-after-mdm-clear.yaml b/hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/6_10_0/5231-fix-mdm-link-history-gives-404-after-mdm-clear.yaml similarity index 100% rename from hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/7_0_0/5231-fix-mdm-link-history-gives-404-after-mdm-clear.yaml rename to hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/6_10_0/5231-fix-mdm-link-history-gives-404-after-mdm-clear.yaml diff --git a/hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/7_0_0/5235-allow-search-on-multiple-patient-ids-in-partitioned-environment.yaml b/hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/6_10_0/5235-allow-search-on-multiple-patient-ids-in-partitioned-environment.yaml similarity index 100% rename from hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/7_0_0/5235-allow-search-on-multiple-patient-ids-in-partitioned-environment.yaml rename to hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/6_10_0/5235-allow-search-on-multiple-patient-ids-in-partitioned-environment.yaml diff --git a/hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/7_0_0/5236-rebuild-golden-resource-after-changing-mdm-link.yaml b/hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/6_10_0/5236-rebuild-golden-resource-after-changing-mdm-link.yaml similarity index 100% rename from hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/7_0_0/5236-rebuild-golden-resource-after-changing-mdm-link.yaml rename to hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/6_10_0/5236-rebuild-golden-resource-after-changing-mdm-link.yaml diff --git a/hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/6_10_0/5238-add-cds-on-fhir.yaml b/hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/6_10_0/5238-add-cds-on-fhir.yaml new file mode 100644 index 00000000000..38bddd5b239 --- /dev/null +++ b/hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/6_10_0/5238-add-cds-on-fhir.yaml @@ -0,0 +1,6 @@ +--- +type: add +issue: 5238 +title: "Added an implementation of Clinical Reasoning CDS on FHIR to the CDS Hooks module that allows PlanDefinition +worfklows to be processed as CDS Services using the $apply operation. +" diff --git a/hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/7_0_0/5242-mdm-clear-should-expunge-redirected-golden-resources.yaml b/hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/6_10_0/5242-mdm-clear-should-expunge-redirected-golden-resources.yaml similarity index 100% rename from hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/7_0_0/5242-mdm-clear-should-expunge-redirected-golden-resources.yaml rename to hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/6_10_0/5242-mdm-clear-should-expunge-redirected-golden-resources.yaml diff --git a/hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/7_0_0/5246-bulk-export-merge-steps.yaml b/hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/6_10_0/5246-bulk-export-merge-steps.yaml similarity index 100% rename from hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/7_0_0/5246-bulk-export-merge-steps.yaml rename to hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/6_10_0/5246-bulk-export-merge-steps.yaml diff --git a/hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/7_0_0/5254-bulk-export-enabled-disrupts-custom-resource-addition.yaml b/hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/6_10_0/5254-bulk-export-enabled-disrupts-custom-resource-addition.yaml similarity index 100% rename from hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/7_0_0/5254-bulk-export-enabled-disrupts-custom-resource-addition.yaml rename to hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/6_10_0/5254-bulk-export-enabled-disrupts-custom-resource-addition.yaml diff --git a/hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/7_0_0/5256-made-requestdetails-part-of-the-parameter-when-doing-cleanuppossiblematches.yaml b/hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/6_10_0/5256-made-requestdetails-part-of-the-parameter-when-doing-cleanuppossiblematches.yaml similarity index 100% rename from hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/7_0_0/5256-made-requestdetails-part-of-the-parameter-when-doing-cleanuppossiblematches.yaml rename to hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/6_10_0/5256-made-requestdetails-part-of-the-parameter-when-doing-cleanuppossiblematches.yaml diff --git a/hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/7_0_0/5259-sort-meta-collection-properties.yaml b/hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/6_10_0/5259-sort-meta-collection-properties.yaml similarity index 100% rename from hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/7_0_0/5259-sort-meta-collection-properties.yaml rename to hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/6_10_0/5259-sort-meta-collection-properties.yaml diff --git a/hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/7_0_0/5262-adding-new-parameter-strictmatch-to-the-mdm-history-link-operation.yaml b/hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/6_10_0/5262-adding-new-parameter-strictmatch-to-the-mdm-history-link-operation.yaml similarity index 100% rename from hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/7_0_0/5262-adding-new-parameter-strictmatch-to-the-mdm-history-link-operation.yaml rename to hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/6_10_0/5262-adding-new-parameter-strictmatch-to-the-mdm-history-link-operation.yaml diff --git a/hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/7_0_0/5265-group-bulk-export-forward-refs.yaml b/hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/6_10_0/5265-group-bulk-export-forward-refs.yaml similarity index 100% rename from hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/7_0_0/5265-group-bulk-export-forward-refs.yaml rename to hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/6_10_0/5265-group-bulk-export-forward-refs.yaml diff --git a/hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/7_0_0/5268-respect-response-status-code-of-method-outcome-from-resource-provider.yaml b/hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/6_10_0/5268-respect-response-status-code-of-method-outcome-from-resource-provider.yaml similarity index 100% rename from hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/7_0_0/5268-respect-response-status-code-of-method-outcome-from-resource-provider.yaml rename to hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/6_10_0/5268-respect-response-status-code-of-method-outcome-from-resource-provider.yaml diff --git a/hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/6_10_0/5271-improve-code-validation-error-messages.yaml b/hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/6_10_0/5271-improve-code-validation-error-messages.yaml new file mode 100644 index 00000000000..9d194939a3c --- /dev/null +++ b/hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/6_10_0/5271-improve-code-validation-error-messages.yaml @@ -0,0 +1,8 @@ +--- +type: add +issue: 5271 +title: "The error messages returned in an OperationOutcome when validating terminology codes + as a part of resource profile validation have been improved. Machine processable location + (line/col) information is now available through a pair of dedicated extensions, and + error messages such as UCUM parsing issues are now returned to the client (previously + they were swallowed and a generic error message was returned)." diff --git a/hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/6_10_0/5274-adding-metric-svc-to-mdm.yaml b/hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/6_10_0/5274-adding-metric-svc-to-mdm.yaml new file mode 100644 index 00000000000..6d9b3be278f --- /dev/null +++ b/hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/6_10_0/5274-adding-metric-svc-to-mdm.yaml @@ -0,0 +1,6 @@ +--- +type: add +issue: 5274 +title: "Added a service for generating metrics on mdm links and resources. + This includes JPA queries and updated indices. + " diff --git a/hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/7_0_0/5275-allow-to-configure-permission-rules-for-operation-with-access-to-all-resources.yaml b/hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/6_10_0/5275-allow-to-configure-permission-rules-for-operation-with-access-to-all-resources.yaml similarity index 100% rename from hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/7_0_0/5275-allow-to-configure-permission-rules-for-operation-with-access-to-all-resources.yaml rename to hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/6_10_0/5275-allow-to-configure-permission-rules-for-operation-with-access-to-all-resources.yaml diff --git a/hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/7_0_0/5275-searchbuilder-should-not-allow-everything-operation-to-follow-links-to-group-or-list-resources.yaml b/hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/6_10_0/5275-searchbuilder-should-not-allow-everything-operation-to-follow-links-to-group-or-list-resources.yaml similarity index 100% rename from hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/7_0_0/5275-searchbuilder-should-not-allow-everything-operation-to-follow-links-to-group-or-list-resources.yaml rename to hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/6_10_0/5275-searchbuilder-should-not-allow-everything-operation-to-follow-links-to-group-or-list-resources.yaml diff --git a/hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/7_0_0/5276-the-graphql-request-results-in-an-http-400-error.yaml b/hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/6_10_0/5276-the-graphql-request-results-in-an-http-400-error.yaml similarity index 100% rename from hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/7_0_0/5276-the-graphql-request-results-in-an-http-400-error.yaml rename to hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/6_10_0/5276-the-graphql-request-results-in-an-http-400-error.yaml diff --git a/hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/7_0_0/5290-allow-preventing-conditional-updates-to-invalidate-match.yaml b/hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/6_10_0/5290-allow-preventing-conditional-updates-to-invalidate-match.yaml similarity index 100% rename from hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/7_0_0/5290-allow-preventing-conditional-updates-to-invalidate-match.yaml rename to hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/6_10_0/5290-allow-preventing-conditional-updates-to-invalidate-match.yaml diff --git a/hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/7_0_0/5295-fix-absolute-refs-with-identifier.yaml b/hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/6_10_0/5295-fix-absolute-refs-with-identifier.yaml similarity index 100% rename from hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/7_0_0/5295-fix-absolute-refs-with-identifier.yaml rename to hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/6_10_0/5295-fix-absolute-refs-with-identifier.yaml diff --git a/hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/7_0_0/5297-ips-fixes.yaml b/hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/6_10_0/5297-ips-fixes.yaml similarity index 100% rename from hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/7_0_0/5297-ips-fixes.yaml rename to hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/6_10_0/5297-ips-fixes.yaml diff --git a/hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/7_0_0/5298-fix-everything-timeout.yaml b/hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/6_10_0/5298-fix-everything-timeout.yaml similarity index 100% rename from hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/7_0_0/5298-fix-everything-timeout.yaml rename to hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/6_10_0/5298-fix-everything-timeout.yaml diff --git a/hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/7_0_0/5300-remove-duplicates-from-r5-defaultprofilevalidationsupport.yaml b/hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/6_10_0/5300-remove-duplicates-from-r5-defaultprofilevalidationsupport.yaml similarity index 100% rename from hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/7_0_0/5300-remove-duplicates-from-r5-defaultprofilevalidationsupport.yaml rename to hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/6_10_0/5300-remove-duplicates-from-r5-defaultprofilevalidationsupport.yaml diff --git a/hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/7_0_0/5300-support-language-sp.yaml b/hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/6_10_0/5300-support-language-sp.yaml similarity index 100% rename from hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/7_0_0/5300-support-language-sp.yaml rename to hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/6_10_0/5300-support-language-sp.yaml diff --git a/hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/7_0_0/5306-add-no-history-mode.yaml b/hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/6_10_0/5306-add-no-history-mode.yaml similarity index 100% rename from hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/7_0_0/5306-add-no-history-mode.yaml rename to hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/6_10_0/5306-add-no-history-mode.yaml diff --git a/hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/7_0_0/5307-search-for-medicationrequests-with-medication-contained-does-not-return-correct-results.yaml b/hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/6_10_0/5307-search-for-medicationrequests-with-medication-contained-does-not-return-correct-results.yaml similarity index 100% rename from hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/7_0_0/5307-search-for-medicationrequests-with-medication-contained-does-not-return-correct-results.yaml rename to hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/6_10_0/5307-search-for-medicationrequests-with-medication-contained-does-not-return-correct-results.yaml diff --git a/hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/7_0_0/5310-broken-tag-parse.yaml b/hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/6_10_0/5310-broken-tag-parse.yaml similarity index 100% rename from hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/7_0_0/5310-broken-tag-parse.yaml rename to hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/6_10_0/5310-broken-tag-parse.yaml diff --git a/hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/7_0_0/5312-reindexstep-failing-when-executing-on-partition.yaml b/hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/6_10_0/5312-reindexstep-failing-when-executing-on-partition.yaml similarity index 100% rename from hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/7_0_0/5312-reindexstep-failing-when-executing-on-partition.yaml rename to hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/6_10_0/5312-reindexstep-failing-when-executing-on-partition.yaml diff --git a/hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/7_0_0/5316-transaction-bundle-with-conditional-delete-and-update-on-same-resource-should-not-fail.yaml b/hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/6_10_0/5316-transaction-bundle-with-conditional-delete-and-update-on-same-resource-should-not-fail.yaml similarity index 100% rename from hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/7_0_0/5316-transaction-bundle-with-conditional-delete-and-update-on-same-resource-should-not-fail.yaml rename to hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/6_10_0/5316-transaction-bundle-with-conditional-delete-and-update-on-same-resource-should-not-fail.yaml diff --git a/hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/6_10_0/5321-code-display-validation-now-configurable.yaml b/hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/6_10_0/5321-code-display-validation-now-configurable.yaml new file mode 100644 index 00000000000..1e5b06ad12e --- /dev/null +++ b/hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/6_10_0/5321-code-display-validation-now-configurable.yaml @@ -0,0 +1,8 @@ +--- +type: add +issue: 5321 +title: "It is now possible to configure the strictness of concept display name validation + using a new flag on the InMemoryTerminologyServerValidationSupport (for non-JPA validation) + and JpaStorageSettings (for JPA validation). In addition, the error messages emitted by + the validator when a concept display doesn't match have been improved to be much + more useful." diff --git a/hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/7_0_0/5322-dstu3-validation-resources-are-out-of-date.yaml b/hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/6_10_0/5322-dstu3-validation-resources-are-out-of-date.yaml similarity index 100% rename from hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/7_0_0/5322-dstu3-validation-resources-are-out-of-date.yaml rename to hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/6_10_0/5322-dstu3-validation-resources-are-out-of-date.yaml diff --git a/hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/6_10_0/5330-capability-statement-needs-to-declare-conformance-to-ig.yaml b/hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/6_10_0/5330-capability-statement-needs-to-declare-conformance-to-ig.yaml new file mode 100644 index 00000000000..7cdc6abc6ed --- /dev/null +++ b/hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/6_10_0/5330-capability-statement-needs-to-declare-conformance-to-ig.yaml @@ -0,0 +1,6 @@ +--- +type: fix +issue: 5330 +jira: SMILE-7372 +title: "Previously, the capability statement returned by the server would not declare conformance to IG when a bulk data +export provider is registered with the server. This issue has been fixed." diff --git a/hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/7_0_0/5331-mdm-query-links-operation-score-field-imprecise-value.yaml b/hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/6_10_0/5331-mdm-query-links-operation-score-field-imprecise-value.yaml similarity index 100% rename from hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/7_0_0/5331-mdm-query-links-operation-score-field-imprecise-value.yaml rename to hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/6_10_0/5331-mdm-query-links-operation-score-field-imprecise-value.yaml diff --git a/hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/6_10_0/5333-prefix-regression-export.yaml b/hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/6_10_0/5333-prefix-regression-export.yaml new file mode 100644 index 00000000000..ccd72cf2982 --- /dev/null +++ b/hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/6_10_0/5333-prefix-regression-export.yaml @@ -0,0 +1,5 @@ +--- +type: fix +issue: 5333 +jira: SMILE-7403 +title: "A regression was introduced in 2023.08.R01 which caused binary storage prefixes to not be applied to exported binary blobs. This has been fixed." diff --git a/hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/6_10_0/5336-mdm-link-history-would-fail-if-provided-with-unknown-ids-on-postgres.yaml b/hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/6_10_0/5336-mdm-link-history-would-fail-if-provided-with-unknown-ids-on-postgres.yaml new file mode 100644 index 00000000000..17aefdceb19 --- /dev/null +++ b/hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/6_10_0/5336-mdm-link-history-would-fail-if-provided-with-unknown-ids-on-postgres.yaml @@ -0,0 +1,6 @@ +--- +type: fix +issue: 5336 +jira: SMILE-7406 +title: "Previously, on PostgreSQL, the $mdm-link-history operation would fail if all ids provided to a parameter are +unknown, and the error will persist for all subsequent requests. This is now fixed." diff --git a/hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/6_10_0/5339-bulk-export-errors-when-patient-compartment-searchparameter-of-the-resource-is-not-present.yaml b/hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/6_10_0/5339-bulk-export-errors-when-patient-compartment-searchparameter-of-the-resource-is-not-present.yaml new file mode 100644 index 00000000000..d0e0461aeb4 --- /dev/null +++ b/hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/6_10_0/5339-bulk-export-errors-when-patient-compartment-searchparameter-of-the-resource-is-not-present.yaml @@ -0,0 +1,6 @@ +--- +type: fix +issue: 5339 +jira: SMILE-7236 +title: "Previously, Bulk Export will error when processing a resource if the patient compartment SearchParameter of that resource is not present. +This has been fixed, the new behaviour is to ignore such resources." diff --git a/hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/6_10_0/5341-add-cds-cr-registries.yaml b/hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/6_10_0/5341-add-cds-cr-registries.yaml new file mode 100644 index 00000000000..bda264324e3 --- /dev/null +++ b/hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/6_10_0/5341-add-cds-cr-registries.yaml @@ -0,0 +1,4 @@ +--- +type: add +issue: 5341 +title: "Added registries for CdsCrServices and CrDiscoveryServices in CDS Hooks to allow registration of custom services." diff --git a/hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/6_10_0/5344-expunge-failing-when-executing-on-partition.yaml b/hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/6_10_0/5344-expunge-failing-when-executing-on-partition.yaml new file mode 100644 index 00000000000..61ffdfbdaf6 --- /dev/null +++ b/hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/6_10_0/5344-expunge-failing-when-executing-on-partition.yaml @@ -0,0 +1,5 @@ +--- +type: fix +issue: 5344 +jira: SMILE-7324 +title: "Previously, issuing an expunge operation for resources on a specific partition would fail. This problem has been fixed." diff --git a/hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/6_10_0/5349-jpa-config-bean-override-exception.yaml b/hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/6_10_0/5349-jpa-config-bean-override-exception.yaml new file mode 100644 index 00000000000..1f2504c4602 --- /dev/null +++ b/hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/6_10_0/5349-jpa-config-bean-override-exception.yaml @@ -0,0 +1,4 @@ +--- +type: fix +issue: 5349 +title: "Removed duplicate helperSvc bean in JpaConfig (also defined in imported MdmJpaConfig) to resolve BeanDefinitionOverrideException" diff --git a/hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/6_10_0/5353-iterate-on-revincludes-and-includes-does-not-return-correct-resources-when-used-with-non-iterate-revincludes.yaml b/hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/6_10_0/5353-iterate-on-revincludes-and-includes-does-not-return-correct-resources-when-used-with-non-iterate-revincludes.yaml new file mode 100644 index 00000000000..fe14f3cd2b8 --- /dev/null +++ b/hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/6_10_0/5353-iterate-on-revincludes-and-includes-does-not-return-correct-resources-when-used-with-non-iterate-revincludes.yaml @@ -0,0 +1,6 @@ +--- +type: fix +issue: 5353 +jira: SMILE-7451 +title: "Previously, when using revincludes and includes with iterate, while also using revincludes without iterate, +the result omitted some resources that should have been included. This issue has now been fixed." diff --git a/hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/6_10_0/5355-add-openapi-resource-details.yaml b/hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/6_10_0/5355-add-openapi-resource-details.yaml new file mode 100644 index 00000000000..3e784c3b80f --- /dev/null +++ b/hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/6_10_0/5355-add-openapi-resource-details.yaml @@ -0,0 +1,8 @@ +--- +type: add +issue: 5355 +title: "The generated OpenAPI documentation produced by OpenApiInterceptor will now include + additional details in the individual resource type documentation, including the values of + *CapabilityStatement.rest.resource.documentation*, + *CapabilityStatement.rest.resource.profile*, and + *CapabilityStatement.rest.resource.supportedProfile*." diff --git a/hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/6_10_0/5356-cr-codecache-invalidation-bug.yaml b/hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/6_10_0/5356-cr-codecache-invalidation-bug.yaml new file mode 100644 index 00000000000..b3a9c984458 --- /dev/null +++ b/hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/6_10_0/5356-cr-codecache-invalidation-bug.yaml @@ -0,0 +1,4 @@ +--- +type: fix +issue: 5356 +title: "Clinical reasoning bug that did not invalidate resources in repository api global caches for terminology and libraries when updates/deletes were made." diff --git a/hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/6_10_0/5366-package-installer-overrides-build-in-search-parameters-with-multiple-base-resources.yaml b/hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/6_10_0/5366-package-installer-overrides-build-in-search-parameters-with-multiple-base-resources.yaml new file mode 100644 index 00000000000..ba042540a99 --- /dev/null +++ b/hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/6_10_0/5366-package-installer-overrides-build-in-search-parameters-with-multiple-base-resources.yaml @@ -0,0 +1,7 @@ +--- +type: add +issue: 5366 +jira: SMILE-5184 +title: "The package installer overrides existing (built-in) SearchParameter with multiple base resources. + This is happening when installing US Core package for Practitioner.given as an example. + This change allows the existing SearchParameter to continue to exist with the remaining base resources." diff --git a/hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/6_10_0/5371-force-summary-false-remote-validation.yaml b/hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/6_10_0/5371-force-summary-false-remote-validation.yaml new file mode 100644 index 00000000000..d969e560b11 --- /dev/null +++ b/hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/6_10_0/5371-force-summary-false-remote-validation.yaml @@ -0,0 +1,4 @@ +--- +type: change +issue: 5371 +title: "Remote terminology operations that fetch ValueSets or CodeSystems now force _summary=false to allow local validation." diff --git a/hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/6_10_0/5375-add-cr-settings-for-cds-hooks.yaml b/hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/6_10_0/5375-add-cr-settings-for-cds-hooks.yaml new file mode 100644 index 00000000000..5ca9d560973 --- /dev/null +++ b/hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/6_10_0/5375-add-cr-settings-for-cds-hooks.yaml @@ -0,0 +1,4 @@ +--- +type: add +issue: 5375 +title: "Add settings for CDS Services using CDS on FHIR. Also removed the dependency on Spring Boot from the CR configs used by CDS Hooks." diff --git a/hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/6_10_0/5377-improve-subscription-triggering-speed.yaml b/hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/6_10_0/5377-improve-subscription-triggering-speed.yaml new file mode 100644 index 00000000000..1ecda6db9a0 --- /dev/null +++ b/hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/6_10_0/5377-improve-subscription-triggering-speed.yaml @@ -0,0 +1,5 @@ +--- +type: perf +issue: 5377 +jira: SMILE-7545 +title: "Subscription triggering via the `$trigger-subscription` operation is now multi-threaded, which significantly improves performance for large data sets." diff --git a/hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/6_10_0/5387-allow-cached-search-with-consent.yaml b/hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/6_10_0/5387-allow-cached-search-with-consent.yaml new file mode 100644 index 00000000000..543467f2dc4 --- /dev/null +++ b/hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/6_10_0/5387-allow-cached-search-with-consent.yaml @@ -0,0 +1,6 @@ +--- +type: perf +issue: 5387 +title: "Enable the search cache for some requests even when a consent interceptor is active. + If no consent service uses canSeeResource (i.e. shouldProcessCanSeeResource() returns false); + or startOperation() returns AUTHORIZED; then the search cache is enabled." diff --git a/hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/6_10_0/5388-fhir-transaction-fails-if-searchnarrowinginterceptor-is-registered-and-partitioning-enabled.yaml b/hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/6_10_0/5388-fhir-transaction-fails-if-searchnarrowinginterceptor-is-registered-and-partitioning-enabled.yaml new file mode 100644 index 00000000000..dcfc89bcc74 --- /dev/null +++ b/hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/6_10_0/5388-fhir-transaction-fails-if-searchnarrowinginterceptor-is-registered-and-partitioning-enabled.yaml @@ -0,0 +1,6 @@ +--- +type: fix +issue: 5388 +title: "Previously, with partitioning enabled and `UrlBaseTenantIdentificationStrategy` used, registering +`SearchNarrowingInterceptor` would cause to incorrect resolution of `entry.request.url` parameter during +transaction bundle processing. This has been fixed." diff --git a/hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/6_10_0/5395-search-cleaner-faster.yaml b/hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/6_10_0/5395-search-cleaner-faster.yaml new file mode 100644 index 00000000000..871a0b64218 --- /dev/null +++ b/hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/6_10_0/5395-search-cleaner-faster.yaml @@ -0,0 +1,5 @@ +--- +type: perf +issue: 5395 +title: "The background activity that clears stale search results now has higher throughput. + Busy servers should no longer accumulate dead stale search results." diff --git a/hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/6_10_0/5404-cql-translator-fhirhelpers-bug.yaml b/hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/6_10_0/5404-cql-translator-fhirhelpers-bug.yaml new file mode 100644 index 00000000000..a9ee0f693cc --- /dev/null +++ b/hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/6_10_0/5404-cql-translator-fhirhelpers-bug.yaml @@ -0,0 +1,4 @@ +--- +type: fix +issue: 5404 +title: "Cql translating bug where FHIRHelpers library function was erroring and blocking clinical reasoning content functionality" diff --git a/hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/6_10_0/5405-use-new-fhir-id-for-sort.yaml b/hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/6_10_0/5405-use-new-fhir-id-for-sort.yaml new file mode 100644 index 00000000000..85c322066d1 --- /dev/null +++ b/hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/6_10_0/5405-use-new-fhir-id-for-sort.yaml @@ -0,0 +1,4 @@ +--- +type: perf +issue: 5405 +title: "Sorting by _id now uses the FHIR_ID column on HFJ_RESOURCE and avoid joins." diff --git a/hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/6_10_0/5407-we-dont-have-guaranteed-subscription-delivery-if-a-resource-is-too-large.yaml b/hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/6_10_0/5407-we-dont-have-guaranteed-subscription-delivery-if-a-resource-is-too-large.yaml new file mode 100644 index 00000000000..e7f8d7ef0be --- /dev/null +++ b/hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/6_10_0/5407-we-dont-have-guaranteed-subscription-delivery-if-a-resource-is-too-large.yaml @@ -0,0 +1,7 @@ +--- +type: add +issue: 5407 +title: "Previously, when the payload of a subscription message exceeds the broker maximum message size, exception would +be thrown and retry will be performed indefinitely until the maximum message size is adjusted. Now, the message will be +successfully delivered for rest-hook and email subscriptions, while message subscriptions remains the same behavior as +before." diff --git a/hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/6_10_0/5412-during-partition-response-link-is-incorrect.yaml b/hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/6_10_0/5412-during-partition-response-link-is-incorrect.yaml new file mode 100644 index 00000000000..03f4b2c8443 --- /dev/null +++ b/hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/6_10_0/5412-during-partition-response-link-is-incorrect.yaml @@ -0,0 +1,4 @@ +--- +type: fix +issue: 5412 +title: "Previously, with Partitioning enabled, submitting a bundle request would return a response with the partition name displayed twice in `response.link` property. This has been fixed." diff --git a/hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/6_10_0/5415-mdm-clear-fails-on-mssql.yaml b/hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/6_10_0/5415-mdm-clear-fails-on-mssql.yaml new file mode 100644 index 00000000000..d6cfc8cc9da --- /dev/null +++ b/hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/6_10_0/5415-mdm-clear-fails-on-mssql.yaml @@ -0,0 +1,4 @@ +--- +type: fix +issue: 5415 +title: "Previously, `$mdm-clear` jobs would fail on MSSQL. This is now fixed." diff --git a/hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/6_10_0/5419-binaries-created-only-for-the-first-resource-entry-of-the-bundle.yaml b/hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/6_10_0/5419-binaries-created-only-for-the-first-resource-entry-of-the-bundle.yaml new file mode 100644 index 00000000000..e1394edddcc --- /dev/null +++ b/hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/6_10_0/5419-binaries-created-only-for-the-first-resource-entry-of-the-bundle.yaml @@ -0,0 +1,6 @@ +--- +type: fix +issue: 5419 +title: "Previously, when `AllowAutoInflateBinaries` was enabled in `JpaStorageSettings` and bundles with multiple +resources were submitted, binaries were created on the disk only for the first resource entry of the bundle. +This has been fixed." diff --git a/hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/6_10_0/5428-pid-sp.yaml b/hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/6_10_0/5428-pid-sp.yaml new file mode 100644 index 00000000000..2c91eef4d4e --- /dev/null +++ b/hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/6_10_0/5428-pid-sp.yaml @@ -0,0 +1,5 @@ +--- +type: add +issue: 5428 +title: "Add support for non-standard _pid SearchParameter to the the JPA engine. + This new SP provides an efficient tie-breaking sort key." diff --git a/hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/6_10_0/5431-version_canonicalizer_fails_capabilitystatement_dstu2.yaml b/hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/6_10_0/5431-version_canonicalizer_fails_capabilitystatement_dstu2.yaml new file mode 100644 index 00000000000..2847a698b16 --- /dev/null +++ b/hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/6_10_0/5431-version_canonicalizer_fails_capabilitystatement_dstu2.yaml @@ -0,0 +1,5 @@ +--- +type: fix +issue: 5431 +jira: SMILE-7589 +title: "Previously, using VersionCanonicalizer to convert a CapabilityStatement from R5 to DSTU2 would fail. This is now fixed." diff --git a/hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/6_10_0/changes.yaml b/hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/6_10_0/changes.yaml new file mode 100644 index 00000000000..b6ce96f25a1 --- /dev/null +++ b/hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/6_10_0/changes.yaml @@ -0,0 +1,15 @@ +--- +- item: + type: "add" + title: "The version of a few dependencies have been bumped to the latest versions + (dependent HAPI modules listed in brackets): +
      +
    • MSSQL JDBC (JPA): 9.4.1.jre8 -> 12.2.0.jre11
    • +
    • Flexmark (All): 0.50.40 -> 0.64.8
    • +
    • Logback (All): 1.4.4 -> 1.4.7
    • +
    • H2 Database (JPA): 2.1.214 -> 2.2.220
    • +
    • Thymeleaf (Testpage Overlay): 3.0.14.RELEASE -> 3.1.2.RELEASE
    • +
    • xpp3 (All): 1.1.4c.0 -> 1.1.6
    • +
    • HtmlUnit (All): 2.67.0 -> 2.70.0
    • +
    • org.hl7.fhir.core (All): 6.0.22.2 -> 6.1.2.2
    • +
    " diff --git a/hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/6_10_0/upgrade.md b/hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/6_10_0/upgrade.md new file mode 100644 index 00000000000..16559ea2e17 --- /dev/null +++ b/hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/6_10_0/upgrade.md @@ -0,0 +1,33 @@ +### Major Database Change + +This release makes performance changes to the database definition in a way that is incompatible with releases before 6.4. +Attempting to run version 6.2 or older simultaneously with this release may experience errors when saving new resources. + +### Change Tracking and Subscriptions +This release introduces significant a change to the mechanism performing submission of resource modification events +to the message broker. Previously, an event would be submitted as part of the synchronous transaction +modifying a resource. Synchronous submission yielded responsive publishing with the caveat that events would be dropped +upon submission failure. + +We have replaced the synchronous mechanism with a two stage process. Events are initially stored in +database upon completion of the transaction and subsequently submitted to the broker by a scheduled task. +This new asynchronous submission mechanism will introduce a slight delay in event publishing. It is our view that such +delay is largely compensated by the capability to retry submission upon failure which will eliminate event losses. + +### Tag, Security Label, and Profile changes + +There are some potentially breaking changes: +* On resource retrieval and before storage, tags, security label and profile collections in resource meta will be +sorted in lexicographical order. The order of the elements for Coding types (i.e. tags and security labels) is defined +by the (security, code) pair of each element. This normally should not break any clients because these properties are +sets according to the FHIR specification, and hence the order of the elements in these collections should not matter. +Also with this change the following side effects can be observed: + - If using INLINE tag storage mode, the first update request to a resource which has tags, security + labels or profiles could create a superfluous resource version if the update request does not really introduce any + change to the resource. This is because the persisted tags, security labels, and profile may not be sorted in + lexicographical order, and this would be interpreted as a new resource version since the tags would be sorted + before storage after this change. If the update request actually changes the resource, there is no concern here. + Also, subsequent updates will not create an additional version because of ordering of the meta properties anymore. + - These meta collections are sorted in place by the storage layer before persisting the resource, so any piece of + code that is calling storage layer directly should not be passing in unmodifiable collections, as it would + result in an error. diff --git a/hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/6_10_0/version.yaml b/hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/6_10_0/version.yaml new file mode 100644 index 00000000000..f8715a7ba72 --- /dev/null +++ b/hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/6_10_0/version.yaml @@ -0,0 +1,3 @@ +--- +release-date: "2023-11-18" +codename: "Zed" diff --git a/hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/6_8_3/upgrade.md b/hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/6_8_3/upgrade.md new file mode 100644 index 00000000000..42b7c037d9b --- /dev/null +++ b/hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/6_8_3/upgrade.md @@ -0,0 +1 @@ +This release permits you to change the severity of a coding display mismatch error during validation. This also fixes a regression which was causing Bulk Export `_exportId` to not be respected during blob prefixing. diff --git a/hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/6_8_3/version.yaml b/hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/6_8_3/version.yaml new file mode 100644 index 00000000000..a8d336beddb --- /dev/null +++ b/hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/6_8_3/version.yaml @@ -0,0 +1,3 @@ +--- +release-date: "2023-09-22" +codename: "Yucatán" diff --git a/hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/6_8_4/upgrade.md b/hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/6_8_4/upgrade.md new file mode 100644 index 00000000000..e69de29bb2d diff --git a/hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/6_8_4/version.yaml b/hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/6_8_4/version.yaml new file mode 100644 index 00000000000..ebcd1754c79 --- /dev/null +++ b/hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/6_8_4/version.yaml @@ -0,0 +1,3 @@ +--- +release-date: "2023-10-18" +codename: "Yucatán" diff --git a/hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/6_8_5/upgrade.md b/hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/6_8_5/upgrade.md new file mode 100644 index 00000000000..e69de29bb2d diff --git a/hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/6_8_5/version.yaml b/hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/6_8_5/version.yaml new file mode 100644 index 00000000000..50b571c2a96 --- /dev/null +++ b/hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/6_8_5/version.yaml @@ -0,0 +1,3 @@ +--- +release-date: "2023-10-20" +codename: "Yucatán" diff --git a/hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/7_0_0/2082-jakarta-api-changes.yaml b/hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/7_0_0/2082-jakarta-api-changes.yaml new file mode 100644 index 00000000000..1ea5c3ecbba --- /dev/null +++ b/hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/7_0_0/2082-jakarta-api-changes.yaml @@ -0,0 +1,4 @@ +--- +type: fix +issue: 5452 +title: "Swapped from using `javax.*` to `jakarta.*` packages. This is a breaking change for a large majority of people who write custom code against HAPI-FHIR. Please see [the migration guide](/hapi-fhir/docs/interceptors/jakarta_upgrade.html) for more information." diff --git a/hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/7_0_0/5081-add-mdm-support-for-r5.yaml b/hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/7_0_0/5081-add-mdm-support-for-r5.yaml new file mode 100644 index 00000000000..c0e5e6a1b7f --- /dev/null +++ b/hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/7_0_0/5081-add-mdm-support-for-r5.yaml @@ -0,0 +1,4 @@ +--- +type: add +issue: 5081 +title: "Added MDM support for FHIR R5." diff --git a/hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/7_0_0/5192-include-in-search-paging-fix.yaml b/hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/7_0_0/5192-include-in-search-paging-fix.yaml index 7a7acb851a1..a63588d0a9f 100644 --- a/hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/7_0_0/5192-include-in-search-paging-fix.yaml +++ b/hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/7_0_0/5192-include-in-search-paging-fix.yaml @@ -2,8 +2,6 @@ type: fix issue: 5192 title: "Fixed a bug where search Bundles with `include` entries from an _include query parameter might - trigger a 'next' link to blank pages. - Specifically, if _include'd resources + requested resources were greater than (or equal to) - requested page size, a 'next' link would be generated, even though no additional - resources are available. + trigger a 'next' link to blank pages when + no more results `match` results are available. " diff --git a/hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/7_0_0/5271-improve-code-validation-error-messages.yaml b/hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/7_0_0/5271-improve-code-validation-error-messages.yaml index 9d194939a3c..13bb268ebca 100644 --- a/hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/7_0_0/5271-improve-code-validation-error-messages.yaml +++ b/hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/7_0_0/5271-improve-code-validation-error-messages.yaml @@ -1,6 +1,7 @@ --- type: add issue: 5271 +backport: 6.8.3 title: "The error messages returned in an OperationOutcome when validating terminology codes as a part of resource profile validation have been improved. Machine processable location (line/col) information is now available through a pair of dedicated extensions, and diff --git a/hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/7_0_0/5321-code-display-validation-now-configurable.yaml b/hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/7_0_0/5321-code-display-validation-now-configurable.yaml index 1e5b06ad12e..e5268283d39 100644 --- a/hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/7_0_0/5321-code-display-validation-now-configurable.yaml +++ b/hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/7_0_0/5321-code-display-validation-now-configurable.yaml @@ -1,6 +1,7 @@ --- type: add issue: 5321 +backport: 6.8.3 title: "It is now possible to configure the strictness of concept display name validation using a new flag on the InMemoryTerminologyServerValidationSupport (for non-JPA validation) and JpaStorageSettings (for JPA validation). In addition, the error messages emitted by diff --git a/hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/7_0_0/5333-prefix-regression-export.yaml b/hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/7_0_0/5333-prefix-regression-export.yaml index ccd72cf2982..c7907622074 100644 --- a/hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/7_0_0/5333-prefix-regression-export.yaml +++ b/hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/7_0_0/5333-prefix-regression-export.yaml @@ -1,5 +1,6 @@ --- type: fix issue: 5333 +backport: 6.8.3 jira: SMILE-7403 title: "A regression was introduced in 2023.08.R01 which caused binary storage prefixes to not be applied to exported binary blobs. This has been fixed." diff --git a/hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/7_0_0/5340-update-hapifhir-document.yaml b/hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/7_0_0/5340-update-hapifhir-document.yaml new file mode 100644 index 00000000000..99a64ec7af0 --- /dev/null +++ b/hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/7_0_0/5340-update-hapifhir-document.yaml @@ -0,0 +1,4 @@ +--- +type: fix +issue: 5340 +title: "Updated documentation specifying the correct status (CANCELLED) to set the job status to if a job is cancelled." diff --git a/hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/7_0_0/5353-iterate-on-revincludes-and-includes-does-not-return-correct-resources-when-used-with-non-iterate-revincludes.yaml b/hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/7_0_0/5353-iterate-on-revincludes-and-includes-does-not-return-correct-resources-when-used-with-non-iterate-revincludes.yaml new file mode 100644 index 00000000000..d4f8667914d --- /dev/null +++ b/hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/7_0_0/5353-iterate-on-revincludes-and-includes-does-not-return-correct-resources-when-used-with-non-iterate-revincludes.yaml @@ -0,0 +1,7 @@ +--- +type: fix +issue: 5353 +jira: SMILE-7451 +backport: 6.8.4 +title: "Previously, when using revincludes and includes with iterate, while also using revincludes without iterate, +the result omitted some resources that should have been included. This issue has now been fixed." diff --git a/hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/7_0_0/5377-improve-subscription-triggering-speed.yaml b/hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/7_0_0/5377-improve-subscription-triggering-speed.yaml new file mode 100644 index 00000000000..6577dc1f14a --- /dev/null +++ b/hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/7_0_0/5377-improve-subscription-triggering-speed.yaml @@ -0,0 +1,6 @@ +--- +type: perf +issue: 5377 +jira: SMILE-7545 +backport: 6.8.5 +title: "Subscription triggering via the `$trigger-subscription` operation is now multi-threaded, which significantly improves performance for large data sets." diff --git a/hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/7_0_0/5401-terserutil-clear.yaml b/hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/7_0_0/5401-terserutil-clear.yaml new file mode 100644 index 00000000000..733f62c5bc7 --- /dev/null +++ b/hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/7_0_0/5401-terserutil-clear.yaml @@ -0,0 +1,5 @@ +--- +type: add +issue: 5401 +title: "Previously, it was only possible to clear a top-level field on a resource using TerserUtil. A new method has been added +to TerserUtil to support clearing a value by FhirPath." diff --git a/hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/7_0_0/5426-enhance-bundleutil.yaml b/hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/7_0_0/5426-enhance-bundleutil.yaml new file mode 100644 index 00000000000..918a8283ddb --- /dev/null +++ b/hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/7_0_0/5426-enhance-bundleutil.yaml @@ -0,0 +1,4 @@ +--- +type: add +issue: 5426 +title: "Added a new method `getResourceByReferenceAndResourceType()` in `BundleUtil.java` to find a specific `Resource` from `Bundle` using `Reference`." diff --git a/hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/7_0_0/5436-swap-to-non-javax-clinical-reasoning.yaml b/hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/7_0_0/5436-swap-to-non-javax-clinical-reasoning.yaml new file mode 100644 index 00000000000..e479130e80a --- /dev/null +++ b/hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/7_0_0/5436-swap-to-non-javax-clinical-reasoning.yaml @@ -0,0 +1,4 @@ +--- +type: add +issue: 5436 +title: "Moving clinical reasoning to non-javax dependency for alignment with incoming hapi-fhir changes, bumping CR version and updating impacted classes and services" diff --git a/hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/7_0_0/5439-terminology-troubleshooting-log.yaml b/hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/7_0_0/5439-terminology-troubleshooting-log.yaml new file mode 100644 index 00000000000..79446e247b9 --- /dev/null +++ b/hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/7_0_0/5439-terminology-troubleshooting-log.yaml @@ -0,0 +1,4 @@ +--- +type: add +issue: 5439 +title: "Added Terminology Troubleshooting Log to support troubleshooting terminology issues." diff --git a/hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/7_0_0/5442-validation-fetcher-fetch-by-canonical.yaml b/hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/7_0_0/5442-validation-fetcher-fetch-by-canonical.yaml new file mode 100644 index 00000000000..226bb7cd153 --- /dev/null +++ b/hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/7_0_0/5442-validation-fetcher-fetch-by-canonical.yaml @@ -0,0 +1,4 @@ +--- +type: add +issue: 5442 +title: "The ValidatorResourceFetcher will now resolve canonical URL references as well as simple local references." diff --git a/hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/7_0_0/5444-batch-chunking.yaml b/hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/7_0_0/5444-batch-chunking.yaml new file mode 100644 index 00000000000..8e188e5785d --- /dev/null +++ b/hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/7_0_0/5444-batch-chunking.yaml @@ -0,0 +1,4 @@ +--- +type: change +issue: 5444 +title: "The reindexing and mdm-clear batch jobs now stream results internally for more reliable operation." diff --git a/hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/7_0_0/5445-adding-extra-logs-to-mdm-candidate-search-parameter.yaml b/hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/7_0_0/5445-adding-extra-logs-to-mdm-candidate-search-parameter.yaml new file mode 100644 index 00000000000..8c5542712aa --- /dev/null +++ b/hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/7_0_0/5445-adding-extra-logs-to-mdm-candidate-search-parameter.yaml @@ -0,0 +1,4 @@ +--- +type: fix +issue: 5445 +title: "Added warnings when MDM candidate search parameters are not defined and candidate search limit is exceeded." diff --git a/hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/7_0_0/5452-mdm-query-link-returns-scientific-notation-in-linkCreated-and-linkUpdated.yaml b/hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/7_0_0/5452-mdm-query-link-returns-scientific-notation-in-linkCreated-and-linkUpdated.yaml new file mode 100644 index 00000000000..9221fe51456 --- /dev/null +++ b/hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/7_0_0/5452-mdm-query-link-returns-scientific-notation-in-linkCreated-and-linkUpdated.yaml @@ -0,0 +1,5 @@ +--- +type: fix +issue: 5452 +title: "Previously, the $mdm-query-link operation would return values of field linkCreated and linkUpdated in scientific +notation when the last digits are 0. This is now fixed and always returns in standard notation." diff --git a/hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/7_0_0/5454-search-with-total-interfers-with-bundle-pagination-.yaml b/hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/7_0_0/5454-search-with-total-interfers-with-bundle-pagination-.yaml new file mode 100644 index 00000000000..50ae0efceae --- /dev/null +++ b/hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/7_0_0/5454-search-with-total-interfers-with-bundle-pagination-.yaml @@ -0,0 +1,5 @@ +--- +type: fix +issue: 5454 +jira: SMILE-7295 +title: "Previously, searching with parameter '_total' could influence chunked query resultsets and subsequently, paged results. This is now fixed." diff --git a/hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/7_0_0/5460-failed-to-expand-valueset-with-hierarchical-codesystem.yaml b/hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/7_0_0/5460-failed-to-expand-valueset-with-hierarchical-codesystem.yaml new file mode 100644 index 00000000000..704cadfb039 --- /dev/null +++ b/hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/7_0_0/5460-failed-to-expand-valueset-with-hierarchical-codesystem.yaml @@ -0,0 +1,6 @@ +--- +type: fix +issue: 5460 +jira: SMILE-7594 +title: "Previously, expanding a ValueSet using hierarchical CodeSystem would fail in different scenarios +with a constraint violation exception when codes (term concepts) were being persisted. This is now fixed." diff --git a/hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/7_0_0/5469-package-installer-property-filter-resource-on-status.yaml b/hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/7_0_0/5469-package-installer-property-filter-resource-on-status.yaml new file mode 100644 index 00000000000..1e998f245db --- /dev/null +++ b/hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/7_0_0/5469-package-installer-property-filter-resource-on-status.yaml @@ -0,0 +1,6 @@ +--- +type: add +issue: 5469 +jira: SMILE-7594 +title: "There is a programmatic filter enabled which skips installation for certain resources based on their status +in PackageInstallerSvcImpl. The filter can now be controlled via StorageSettings." diff --git a/hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/7_0_0/5472-remove-mandatory-caffeine-dependency-in-validator-module.yaml b/hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/7_0_0/5472-remove-mandatory-caffeine-dependency-in-validator-module.yaml new file mode 100644 index 00000000000..2680269879b --- /dev/null +++ b/hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/7_0_0/5472-remove-mandatory-caffeine-dependency-in-validator-module.yaml @@ -0,0 +1,5 @@ +--- +type: fix +title: "The hapi-fhir-validation module inadvertently included a mandatory dependency on the Caffeine + caching library instead of leaving it to implementors to choose which module to pick. This has been + corrected." diff --git a/hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/7_0_0/5475-batch-default-partition.yaml b/hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/7_0_0/5475-batch-default-partition.yaml new file mode 100644 index 00000000000..4cb01887bb8 --- /dev/null +++ b/hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/7_0_0/5475-batch-default-partition.yaml @@ -0,0 +1,4 @@ +--- +type: fix +issue: 5475 +title: "Ensure batch2 jpa persistence always targets the default partition." diff --git a/hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/7_0_0/5476-pass-properties-through-remote-terminology-service-for-code-system.yaml b/hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/7_0_0/5476-pass-properties-through-remote-terminology-service-for-code-system.yaml new file mode 100644 index 00000000000..f4b02e23951 --- /dev/null +++ b/hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/7_0_0/5476-pass-properties-through-remote-terminology-service-for-code-system.yaml @@ -0,0 +1,5 @@ +--- +type: add +issue: 5476 +title: "A new method on the IValidationSupport interface called lookupCode(LookupCodeRequest) has been added. +This method will replace the existing lookupCode methods, which are now deprecated." diff --git a/hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/7_0_0/5480-update-member-match-signature-to-match-the-spec.yaml b/hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/7_0_0/5480-update-member-match-signature-to-match-the-spec.yaml new file mode 100644 index 00000000000..7f19110cde1 --- /dev/null +++ b/hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/7_0_0/5480-update-member-match-signature-to-match-the-spec.yaml @@ -0,0 +1,4 @@ +--- +type: add +issue: 5480 +title: "Updated $member-match operation signature to match the latest specification." diff --git a/hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/7_0_0/5483-update-consent-storage-to-latest-spec.yaml b/hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/7_0_0/5483-update-consent-storage-to-latest-spec.yaml new file mode 100644 index 00000000000..18a71501d4b --- /dev/null +++ b/hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/7_0_0/5483-update-consent-storage-to-latest-spec.yaml @@ -0,0 +1,4 @@ +--- +type: add +issue: 5480 +title: "Updated Consent storage in $member-match operation." diff --git a/hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/7_0_0/5486-cli-migrate-database-in-dry-run-modifies-db.yaml b/hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/7_0_0/5486-cli-migrate-database-in-dry-run-modifies-db.yaml new file mode 100644 index 00000000000..f46b55475bd --- /dev/null +++ b/hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/7_0_0/5486-cli-migrate-database-in-dry-run-modifies-db.yaml @@ -0,0 +1,6 @@ +--- +type: fix +issue: 5486 +jira: SMILE-7457 +title: "Previously, testing database migration with cli migrate-database command in dry-run mode would insert in the +migration task table. The issue has been fixed." diff --git a/hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/7_0_0/5493-linked-resources-for-several-partitions-are-not-returned-in-the-everything-operation.yaml b/hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/7_0_0/5493-linked-resources-for-several-partitions-are-not-returned-in-the-everything-operation.yaml new file mode 100644 index 00000000000..dfe811b38c3 --- /dev/null +++ b/hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/7_0_0/5493-linked-resources-for-several-partitions-are-not-returned-in-the-everything-operation.yaml @@ -0,0 +1,6 @@ +--- +type: fix +jira: SMILE-7624 +title: "Previously, it was impossible to find all resources from different partitions for $everything operation + with partitioning.cross_partition_reference_mode=ALLOWED_UNQUALIFIED and dao_config.client_id_mode=ANY. + It's fixed now" \ No newline at end of file diff --git a/hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/7_0_0/5496-batch-default-partition-non-gated.yaml b/hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/7_0_0/5496-batch-default-partition-non-gated.yaml new file mode 100644 index 00000000000..0b8253d22a0 --- /dev/null +++ b/hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/7_0_0/5496-batch-default-partition-non-gated.yaml @@ -0,0 +1,4 @@ +--- +type: fix +issue: 5496 +title: "Ensure batch jobs target the default partition for non-gated steps." diff --git a/hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/7_0_0/5498-add-empty-and-id-only-payload-content-support-for-topic-subscriptions.yaml b/hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/7_0_0/5498-add-empty-and-id-only-payload-content-support-for-topic-subscriptions.yaml new file mode 100644 index 00000000000..55ed0de5ec6 --- /dev/null +++ b/hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/7_0_0/5498-add-empty-and-id-only-payload-content-support-for-topic-subscriptions.yaml @@ -0,0 +1,5 @@ +--- +type: add +issue: 5498 +title: "Added support for `id-only` and `empty` payload content types for notifications +triggered by R5, R4B, and R4 back-ported topic subscriptions." diff --git a/hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/7_0_0/5502-base-client-client-response-pointcut-mutate-response.yaml b/hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/7_0_0/5502-base-client-client-response-pointcut-mutate-response.yaml new file mode 100644 index 00000000000..31114533369 --- /dev/null +++ b/hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/7_0_0/5502-base-client-client-response-pointcut-mutate-response.yaml @@ -0,0 +1,6 @@ +--- +type: add +issue: 5502 +jira: SMILE-7262 +title: "It is now possible to mutate an HTTP response from the CLIENT_RESPONSE Pointcut, and pass this mutated response + to downstream processing." diff --git a/hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/7_0_0/5505-remove-broken-member-match-from-hapi.yaml b/hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/7_0_0/5505-remove-broken-member-match-from-hapi.yaml new file mode 100644 index 00000000000..ae082e5f83a --- /dev/null +++ b/hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/7_0_0/5505-remove-broken-member-match-from-hapi.yaml @@ -0,0 +1,4 @@ +--- +type: remove +issue: 5505 +title: "Removed an incorrect implementation of $member-match." diff --git a/hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/7_0_0/5510-allow-ge-le-comparators-in-hfql.yaml b/hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/7_0_0/5510-allow-ge-le-comparators-in-hfql.yaml new file mode 100644 index 00000000000..4d79490f720 --- /dev/null +++ b/hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/7_0_0/5510-allow-ge-le-comparators-in-hfql.yaml @@ -0,0 +1,7 @@ +--- +type: fix +jira: SMILE-7664 +title: "The HFQL/SQL engine incorrectly parsed expressions containing a `>=` or + `<=` comparator in a WHERE clause. This has been corrected. Additionally, the + execution engine has been optimized to apply clauses against the `meta.lastUpdated` + path more efficiently by using the equivalent search parameter automatically." diff --git a/hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/7_0_0/5511-oracle-migration-create-index.yaml b/hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/7_0_0/5511-oracle-migration-create-index.yaml new file mode 100644 index 00000000000..b9eda74e6d7 --- /dev/null +++ b/hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/7_0_0/5511-oracle-migration-create-index.yaml @@ -0,0 +1,6 @@ +--- +type: fix +issue: 5511 +title: "Previously, when creating an index as a part of a migration, if the index already existed with a different name +on Oracle, the migration would fail. This has been fixed so that the create index migration task now recovers with + a warning message if the index already exists with a different name." diff --git a/hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/7_0_0/5512-codesystem-lookup-with-designation-no-language-nullpointerexception.yaml b/hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/7_0_0/5512-codesystem-lookup-with-designation-no-language-nullpointerexception.yaml new file mode 100644 index 00000000000..b55b4044b51 --- /dev/null +++ b/hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/7_0_0/5512-codesystem-lookup-with-designation-no-language-nullpointerexception.yaml @@ -0,0 +1,6 @@ +--- +type: fix +issue: 5511 +title: 'Previously, CodeSystem `$lookup` with Remote Terminology Service enabled would throw NullPointerException +when the CodeSystem included designations with no language value. Also, there was an inconsistency between +input and output type `string` vs. `code` for property parameters. These issues have been fixed.' diff --git a/hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/7_0_0/5523-case-sensitive-package-ids.yaml b/hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/7_0_0/5523-case-sensitive-package-ids.yaml new file mode 100644 index 00000000000..a2b9b590398 --- /dev/null +++ b/hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/7_0_0/5523-case-sensitive-package-ids.yaml @@ -0,0 +1,5 @@ +--- +type: fix +issue: 5523 +jira: SMILE-7729 +title: "Previously, it was possible to store NPM Packages where the package name's case did not match the package ID's case, e.g. `my-package` was different than `MY-PACKAGE`. Names are now normalized to lower case before queries occur." diff --git a/hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/7_0_0/5529-allow-chained-search-in-bundle-where-fullurl-is-qualified.yaml b/hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/7_0_0/5529-allow-chained-search-in-bundle-where-fullurl-is-qualified.yaml new file mode 100644 index 00000000000..2fdbb4cb53d --- /dev/null +++ b/hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/7_0_0/5529-allow-chained-search-in-bundle-where-fullurl-is-qualified.yaml @@ -0,0 +1,4 @@ +--- +type: fix +issue: 5529 +title: "When using a chained SearchParameter to search within a Bundle as [described here](https://smilecdr.com/docs/fhir_storage_relational/chained_searches_and_sorts.html#document-and-message-search-parameters), if the `Bundle.entry.fullUrl` was fully qualified but the reference was not, the search did not work. This has been corrected." diff --git a/hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/7_0_0/5536-sql-migration-lowercase-and-custom-column-type-sql.yaml b/hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/7_0_0/5536-sql-migration-lowercase-and-custom-column-type-sql.yaml new file mode 100644 index 00000000000..c98850e5161 --- /dev/null +++ b/hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/7_0_0/5536-sql-migration-lowercase-and-custom-column-type-sql.yaml @@ -0,0 +1,4 @@ +--- +type: add +issue: 5536 +title: "In code: Support lowercase for SQL columns and overridden column type/driver type SQL type string rules" diff --git a/hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/7_0_0/5547-postgres-collation-utf8-migration-detect-add-index.yaml b/hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/7_0_0/5547-postgres-collation-utf8-migration-detect-add-index.yaml new file mode 100644 index 00000000000..23d3fbf2829 --- /dev/null +++ b/hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/7_0_0/5547-postgres-collation-utf8-migration-detect-add-index.yaml @@ -0,0 +1,7 @@ +--- +type: fix +issue: 5547 +title: "Previously LIKE queries against resources would perform poorly on PostgreSQL if the database locale/collation was not 'C'. + This has been resolved by checking hfj_spidx_string.sp_value_normalized and hfj_spidx_uri.sp_uri column + collations during migration and if either or both are non C, create a new btree varchar_pattern_ops on the + hash values. If both column collations are 'C', do not create any new indexes." diff --git a/hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/7_0_0/5555-avoid-resource-lob-column.yaml b/hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/7_0_0/5555-avoid-resource-lob-column.yaml new file mode 100644 index 00000000000..8ae0b2fab63 --- /dev/null +++ b/hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/7_0_0/5555-avoid-resource-lob-column.yaml @@ -0,0 +1,10 @@ +--- +type: perf +issue: 5555 +title: "Previously, resource body content went into one of 2 columns on the HFJ_RES_VER table: + RES_TEXT if the size was above a configurable threshold, or RES_TEXT_VC if it was below that + threshold. Performance testing has shown that the latter is always faster, and that on + Postgres the use of the latter is particularly problematic since it maps to the + largeobject table which isn't the recommended way of storing high frequency objects. + The configurable threshold is now ignored, and the latter column is always used. Any legacy + data in the former column will still be read however." diff --git a/hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/7_0_0/changes.yaml b/hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/7_0_0/changes.yaml index 86eb2ac6c22..dae1d7e2967 100644 --- a/hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/7_0_0/changes.yaml +++ b/hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/7_0_0/changes.yaml @@ -4,12 +4,29 @@ title: "The version of a few dependencies have been bumped to the latest versions (dependent HAPI modules listed in brackets):
      -
    • MSSQL JDBC (JPA): 9.4.1.jre8 -> 12.2.0.jre11
    • -
    • Flexmark (All): 0.50.40 -> 0.64.8
    • -
    • Logback (All): 1.4.4 -> 1.4.7
    • -
    • H2 Database (JPA): 2.1.214 -> 2.2.220
    • -
    • Thymeleaf (Testpage Overlay): 3.0.14.RELEASE -> 3.1.2.RELEASE
    • -
    • xpp3 (All): 1.1.4c.0 -> 1.1.6
    • -
    • HtmlUnit (All): 2.67.0 -> 2.70.0
    • -
    • org.hl7.fhir.core (All): 6.0.22.2 -> 6.1.2
    • +
    • Jackson (Base): 2.15.3 -> 2.16.0
    • +
    • SLF4j (Base): 2.0.3 -> 2.0.9
    • +
    • Logback (Base): 1.4.7 -> 1.4.14
    • +
    • Caffeine (Base): 3.1.1 -> 3.1.8
    • +
    • Spring Framework (JPA): 5.3.27 -> 6.1.1
    • +
    • Spring Boot (JPA-Starter): 5.3.27 -> 6.2.0
    • +
    • Spring Data BOM (JPA): 2021.2.2 -> 2023.1.0
    • +
    • Hibernate (JPA): 5.6.15.Final -> 6.4.0.Final
    • +
    • Hibernate Validator (JPA): 6.1.5.Final -> 8.0.0.Final
    • +
    • Hibernate Search (JPA): 6.1.6.Final -> 6.2.2.Final
    • +
    • Commons-DBCP2 (JPA): 2.9.0 -> 2.11.0
    • +
    • Spring Boot (Boot+Starter): 2.7.12 -> 3.1.4
    • +
    • Jetty (CLI): 10.0.14 -> 12.0.3
    • +
    • Jansi (CLI): 2.4.0 -> 2.4.1
    • +
    • Phloc Schematron (Schematron Validator): 5.6.5 -> 7.1.2
    • +
    • RestEasy (JAX-RS Server): 5.0.2.Final -> 6.2.5.Final
    " +- item: + type: "change" + title: "HAPI FHIR JPA now requires PostgreSQL 10+. Previously Postgres 9.4 + was supported but this version has been dropped." +- item: + type: "remove" + title: "The legacy `$lastn` support has been removed, and only the redesigned 'advanced' version is now + supported. The advanced version requires Elasticsearch." + diff --git a/hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/7_0_0/upgrade.md b/hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/7_0_0/upgrade.md index 258d2440c2f..be3d4628fa1 100644 --- a/hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/7_0_0/upgrade.md +++ b/hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/7_0_0/upgrade.md @@ -1,26 +1,18 @@ -This release introduces significant a change to the mechanism performing submission of resource modification events -to the message broker. Previously, an event would be submitted as part of the synchronous transaction -modifying a resource. Synchronous submission yielded responsive publishing with the caveat that events would be dropped -upon submission failure. +This release contains a large breaking change for authors of interceptors. Internally, HAPI-FHIR has swapped from using `javax.*` to `jakarta.*` packages. Please see [the migration guide](/hapi-fhir/docs/interceptors/jakarta_upgrade.html) for more information. Without manual intervention, the majority of interceptors will fail at runtime unless they are upgraded. -We have replaced the synchronous mechanism with a two stage process. Events are initially stored in -database upon completion of the transaction and subsequently submitted to the broker by a scheduled task. -This new asynchronous submission mechanism will introduce a slight delay in event publishing. It is our view that such -delay is largely compensated by the capability to retry submission upon failure which will eliminate event losses. +## Possible New Indexes on PostgresSQL +* This affects only clients running PostgreSQL who have a locale/collation that is NOT 'C' +* For those clients, the migration will detect this condition and add new indexes to: + * hfj_spidx_string + * hfj_spidx_uri +* This is meant to address performance issues for these clients on GET queries whose resulting SQL uses "LIKE" clauses -There are some potentially breaking changes: -* On resource retrieval and before storage, tags, security label and profile collections in resource meta will be -sorted in lexicographical order. The order of the elements for Coding types (i.e. tags and security labels) is defined -by the (security, code) pair of each element. This normally should not break any clients because these properties are -sets according to the FHIR specification, and hence the order of the elements in these collections should not matter. -Also with this change the following side effects can be observed: - - If using INLINE tag storage mode, the first update request to a resource which has tags, security - labels or profiles could create a superfluous resource version if the update request does not really introduce any - change to the resource. This is because the persisted tags, security labels, and profile may not be sorted in - lexicographical order, and this would be interpreted as a new resource version since the tags would be sorted - before storage after this change. If the update request actually changes the resource, there is no concern here. - Also, subsequent updates will not create an additional version because of ordering of the meta properties anymore. - - These meta collections are sorted in place by the storage layer before persisting the resource, so any piece of - code that is calling storage layer directly should not be passing in unmodifiable collections, as it would - result in an error. +These are the new indexes that will be created: + +```sql +CREATE INDEX idx_sp_string_hash_nrm_pattern_ops ON public.hfj_spidx_string USING btree (hash_norm_prefix, sp_value_normalized varchar_pattern_ops, res_id, partition_id); +``` +```sql +CREATE UNIQUE INDEX idx_sp_uri_hash_identity_pattern_ops ON public.hfj_spidx_uri USING btree (hash_identity, sp_uri varchar_pattern_ops, res_id, partition_id); +``` diff --git a/hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/7_0_0/version.yaml b/hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/7_0_0/version.yaml index d7d82eaec5e..08d30a1acef 100644 --- a/hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/7_0_0/version.yaml +++ b/hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/7_0_0/version.yaml @@ -1,3 +1,3 @@ --- -release-date: "2023-11-18" +release-date: "2023-02-18" codename: "TBD" diff --git a/hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/docs/files.properties b/hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/docs/files.properties index fa474411fe3..d4f2eb07628 100644 --- a/hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/docs/files.properties +++ b/hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/docs/files.properties @@ -105,6 +105,7 @@ page.interceptors.built_in_client_interceptors=Built-In Client Interceptors page.interceptors.server_interceptors=Server Interceptors page.interceptors.server_pointcuts=Server Pointcuts page.interceptors.built_in_server_interceptors=Built-In Server Interceptors +page.interceptors.jakarta_upgrade=7.0.0 Migration Guide section.security.title=Security page.security.introduction=Introduction diff --git a/hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/docs/interceptors/jakarta_upgrade.md b/hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/docs/interceptors/jakarta_upgrade.md new file mode 100644 index 00000000000..7c7612ee3db --- /dev/null +++ b/hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/docs/interceptors/jakarta_upgrade.md @@ -0,0 +1,78 @@ +# 7.0.0 Interceptor Upgrade Guide + +As of HAPI-FHIR 7.0.0, dependency on the `javax.*` packages has now changed to instead use the `jakarta.*` packages. This is a breaking change for any users who have written their own interceptors, as the package names of the interfaces have changed. + +In order to upgrade your interceptors, you will need to change, at a minimum, the imports in your affected interceptor implementations. For example, if you have an interceptor that uses imports such as `javax.servlet.http.HttpServletRequest`, you will need to change these to `jakarta.servlet.http.HttpServletRequest`. The following is an example of a migration of an interceptor. + +## Example + +### Old Server Interceptor + +```java +package com.example; + +import ca.uhn.fhir.interceptor.api.Hook; +import ca.uhn.fhir.interceptor.api.Pointcut; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.annotation.Autowired; + +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; +import java.util.Iterator; +import java.util.function.Supplier; + +public class SampleInteceptor{ + + private static final Logger ourLog = LoggerFactory.getLogger(SampleInteceptor.class); + + @Hook(Pointcut.SERVER_INCOMING_REQUEST_PRE_PROCESSED) + public boolean serverIncomingRequestPreProcessed(HttpServletRequest theHttpServletRequest, HttpServletResponse theHttpServletResponse) { + ourLog.info("I'm an interceptor!"); + return true; + } +} +``` + +## New Server Interceptor + +```java +package com.example; + +import ca.uhn.fhir.interceptor.api.Hook; +import ca.uhn.fhir.interceptor.api.Pointcut; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.annotation.Autowired; + +import jakarta.servlet.http.HttpServletRequest; +import jakarta.servlet.http.HttpServletResponse; +import java.util.Iterator; +import java.util.function.Supplier; + +public class SampleInteceptor{ + + private static final Logger ourLog = LoggerFactory.getLogger(SampleInteceptor.class); + + @Hook(Pointcut.SERVER_INCOMING_REQUEST_PRE_PROCESSED) + public boolean serverIncomingRequestPreProcessed(HttpServletRequest theHttpServletRequest, HttpServletResponse theHttpServletResponse) { + ourLog.info("I'm an interceptor!"); + return true; + } +} +``` + +You'll note that there is only one very subtle difference between these two versions, and that is the change from: + +```java +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; +``` + +to: + +```java +import jakarta.servlet.http.HttpServletRequest; +import jakarta.servlet.http.HttpServletResponse; +``` + diff --git a/hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/docs/security/consent_interceptor.md b/hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/docs/security/consent_interceptor.md index 96b2dc481d2..900baa3566d 100644 --- a/hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/docs/security/consent_interceptor.md +++ b/hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/docs/security/consent_interceptor.md @@ -24,3 +24,13 @@ The ConsentInterceptor requires a user-supplied instance of the [IConsentService ```java {{snippet:classpath:/ca/uhn/hapi/fhir/docs/ConsentInterceptors.java|service}} ``` + +## Performance and Privacy + +Filtering search results in `canSeeResource()` requires inspecting every resource during a search and editing the results. +This is slower than the normal path, and will prevent the reuse of the results from the search cache. +The `willSeeResource()` operation supports reusing cached search results, but removed resources may be 'visible' as holes in returned bundles. +Disabling `canSeeResource()` by returning `false` from `processCanSeeResource()` will enable the search cache. + + + diff --git a/hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/docs/server_jpa/database_support.md b/hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/docs/server_jpa/database_support.md index 5b5a473f744..763682c55ff 100644 --- a/hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/docs/server_jpa/database_support.md +++ b/hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/docs/server_jpa/database_support.md @@ -1,19 +1,20 @@ # Database Support -HAPI FHIR JPA Server maintains active support for several databases: +HAPI FHIR JPA Server maintains active support for several databases. -- [MS SQL Server](https://www.microsoft.com/en-us/sql-server/sql-server-2019) -- [PostgreSQL](https://www.postgresql.org/) -- [Oracle](https://www.oracle.com/ca-en/database/12c-database/) +The supported databases are regularly tested for ongoing compliance and performance, and HAPI FHIR has specific performance optimizations for each platform. Make sure to use the HAPI FHIR dialect class as opposed to the default hibernate dialect class. -Use of any of the above databases is fully supported by HAPI-FHIR, and code is actively written to work with them. +| Database | Status | Hibernate Dialect Class | Notes | +|-----------------------------------------------------------------------------|---------------|----------------------------------------------------------|-----------------------------------------------------------------------------------------------------------------------------------| +| [MS SQL Server](https://www.microsoft.com/en-us/sql-server/sql-server-2019) | **Supported** | `ca.uhn.fhir.jpa.model.dialect.HapiFhirH2Dialect` | | +| [PostgreSQL](https://www.postgresql.org/) | **Supported** | `ca.uhn.fhir.jpa.model.dialect.HapiFhirPostgresDialect` | | +| [Oracle](https://www.oracle.com/ca-en/database/12c-database/) | **Supported** | `ca.uhn.fhir.jpa.model.dialect.HapiFhirOracleDialect` | | +| [Cockroach DB](https://www.cockroachlabs.com/) | Experimental | `ca.uhn.fhir.jpa.model.dialect.HapiFhirCockroachDialect` | A CockroachDB dialect was contributed by a HAPI FHIR community member. This dialect is not regularly tested, use with caution. | +| MySQL | Deprecated | `ca.uhn.fhir.jpa.model.dialect.HapiFhirMySQLDialect` | MySQL and MariaDB exhibit poor performance with HAPI FHIR and have therefore been deprecated. These databases should not be used. | +| MariaDB | Deprecated | `ca.uhn.fhir.jpa.model.dialect.HapiFhirMariaDBDialect` | MySQL and MariaDB exhibit poor performance with HAPI FHIR and have therefore been deprecated. These databases should not be used. | # Experimental Support -HAPI FHIR currently provides experimental for the following databases, but does not actively support them, or write code specifically to work with them: - -- [Cockroach DB](https://www.cockroachlabs.com/) - HAPI FHIR uses the Hibernate ORM to provide database abstraction. This means that HAPI FHIR could theoretically also work on other databases supported by Hibernate. For example, although we do not regularly test or validate on other platforms, community members have reported successfully running HAPI FHIR on: @@ -21,9 +22,3 @@ For example, although we do not regularly test or validate on other platforms, c - Cache - Firebird -# Deprecated Support - -These databases were previously supported by HAPI FHIR JPA Server, but have since been deprecated, and should not be used. - -- [MySQL](https://www.mysql.com/) - diff --git a/hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/docs/server_jpa/schema.md b/hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/docs/server_jpa/schema.md index 46310f9c51f..f4fbcbce057 100644 --- a/hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/docs/server_jpa/schema.md +++ b/hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/docs/server_jpa/schema.md @@ -262,6 +262,11 @@ a [Resource Client ID Strategy](/apidocs/hapi-fhir-storage/undefined/ca/uhn/fhir of [ANY](/apidocs/hapi-fhir-storage/undefined/ca/uhn/fhir/jpa/api/config/JpaStorageSettings.ClientIdStrategyEnum.html#ANY) the server will create a Forced ID for all resources (not only resources having textual IDs). +The **HFJ_RESOURCE** table now has a FHIR_ID column. +This column is always populated; both for server-assigned ids and for client-assigned ids. +As of Hapi release 6.10, this column is used in place of the **HFJ_FORCED_ID** table for id search and sort. +A future version of Hapi will stop populating the **HFJ_FORCED_ID** table. + ## Columns @@ -526,6 +531,15 @@ The following columns are common to **all HFJ_SPIDX_xxx tables**. This is the name of the resource being indexed. + + + + + + + @@ -550,12 +564,13 @@ The following columns are common to **all HFJ_SPIDX_xxx tables**. # HFJ_SPIDX_DATE: Date Search Parameters -For any FHIR Search Parameter of type *date* that generates a database index, a row in the *HFJ_SPIDX_DATE* table will be created. +For any FHIR Search Parameter of type [*date*](https://www.hl7.org/fhir/search.html#date) that generates a database index, a row in the `HFJ_SPIDX_DATE` table will be created. +Range queries with Date parameters (e.g. `Observation?date=ge2020-01-01`) will query the HASH_IDENTITY, SP_VALUE_LOW_DATE_ORDINAL and/or SP_VALUE_HIGH_DATE_ORDINAL columns. +Range queries with DateTime parameters (e.g. `Observation?date=ge2021-01-01T10:30:00`) will query the HASH_IDENTITY, SP_VALUE_LOW and/or SP_VALUE_HIGH columns. +Sorting is done by the SP_VALUE_LOW column. ## Columns -The following columns are common to **all HFJ_SPIDX_xxx tables**. -
    HASH_IDENTITYLong + A hash of SP_NAME and RES_TYPE. Used to narrow the table to a specific SearchParameter during sorting, and some queries. +
    SP_UPDATED
    @@ -617,3 +632,294 @@ The following columns are common to **all HFJ_SPIDX_xxx tables**.
    + +# HFJ_SPIDX_NUMBER: Number Search Parameters + +FHIR Search Parameters of type [*number*](https://www.hl7.org/fhir/search.html#number) produce rows in the `HFJ_SPIDX_NUMBER` table. +Range queries and sorting use the HASH_IDENTITY and SP_VALUE columns. + +## Columns + + + + + + + + + + + + + + + + + + + + +
    NameRelationshipsDatatypeNullableDescription
    SP_VALUEDoubleNot nullable + This is the value extracted by the SearchParameter expression. +
    + + + +# HFJ_SPIDX_QUANTITY: Quantity Search Parameters + +FHIR Search Parameters of type [*quantity*](https://www.hl7.org/fhir/search.html#quantity) produce rows in the `HFJ_SPIDX_QUANTITY` table. +Range queries (e.g. `Observation?valueQuantity=gt100`) with no units provided will query the HASH_IDENTITY and SP_VALUE columns. +Range queries (e.g. `Observation?valueQuantity=gt100||mmHg`) with a unit but not unit-sytem provided will use the HASH_IDENTITY_AND_UNITS and SP_VALUE columns. +Range queries (e.g. `Observation?valueQuantity=gt100|http://unitsofmeasure.org|mmHg`) with a full system and unit will use the HASH_IDENTITY_SYS_UNITS and SP_VALUE columns. +Sorting is done via the HASH_IDENTITY and SP_VALUE columns. + +## Columns + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    NameRelationshipsDatatypeNullableDescription
    HASH_IDENTITY_AND_UNITSLong + A hash like HASH_IDENTITY that also includes the SP_UNITS column. +
    HASH_IDENTITY_SYS_UNITSLong + A hash like HASH_IDENTITY that also includes the SP_SYSTEM and SP_UNITS columns. +
    SP_SYSTEMString + The system of the quantity units. e.g. "http://unitsofmeasure.org". +
    SP_UNITSString + The units of the quantity. E.g. "mg". +
    SP_VALUEDouble + This is the value extracted by the SearchParameter expression. +
    + +# HFJ_SPIDX_QUANTITY_NRML: Normalized Quantity Search Parameters + +Hapi Fhir supports searching by normalized units when enabled (see https://hapifhir.io/hapi-fhir/apidocs/hapi-fhir-jpaserver-model/ca/uhn/fhir/jpa/model/entity/StorageSettings.html#getNormalizedQuantitySearchLevel()). +When this feature is enabled, each row stored in HFJ_SPIDX_QUANTITY to also store a row in HFJ_SPIDX_QUANTITY_NRML in canonical UCUM units. +E.g. a weight recorded in an Observation as +``` +"valueQuantity" : { + "value" : 172, + "unit" : "lb_av", + "system" : "http://unitsofmeasure.org", + "code" : "[lb_av]" + }, +``` +would match the search `Observation?valueQuantity=172`, +but would also match the search `Observation?valueQuantity=78|http://unitsofmeasure.org|kg`. +The row in HFJ_SPIDX_QUANTITY would contain the value 172 pounds, while the HFJ_SPIDX_QUANTITY_NRML table would hold the equivalent 78 kg value. +Only value searches that provide fully qualified units are eligible for normalized searches. +Sorting only uses the HFJ_SPIDX_QUANTITY table. + +## Columns + +Same as HFJ_SPIDX_QUANTITY above, except the SP_VALUE, SP_SYSTEM, and SP_UNITS columns hold the converted value in canonical units instead of the value extracted by the SearchParameter. +This table is only used for range queries with a unit which can be converted to canonical units. + + +# HFJ_SPIDX_STRING: String Search Parameters + +FHIR Search Parameters of type [*string*](https://www.hl7.org/fhir/search.html#string) produce rows in the `HFJ_SPIDX_STRING` table. +The default string search matches by prefix, ignoring case or accents. This uses the HASH_IDENTITY column and a LIKE prefix clause on the SP_VALUE_NORMALIZED columns. +The `:exact` string search matches exactly. This uses only the HASH_EXACT column. +Sorting is done via the HASH_IDENTITY and SP_VALUE_NORMALIZED columns. + +## Columns + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    NameRelationshipsDatatypeNullableDescription
    HASH_EXACTLong + A hash like HASH_IDENTITY that also includes the SP_VALUE_EXACT column. +
    SP_VALUE_NORMALIZEDString + An UPPERCASE string with accents removed. +
    SP_VALUE_EXACTString + The extracted string unchanged. +
    + +# HFJ_SPIDX_TOKEN: Token Search Parameters + +FHIR Search Parameters of type [*token*](https://www.hl7.org/fhir/search.html#token) extract values of type Coding, code, and others. +These produce rows in the `HFJ_SPIDX_TOKEN` table. +The default token search accepts three parameter formats: matching the code (e.g. `Observation?code=15074-8`), +matching both system and code (e.g. `Observation?code=http://loinc.org|15074-8`), +or matching a system with any code (e.g. `Observation?http://loinc.org|`). +All three are exact searches and use the hashes: HASH_VALUE, HASH_SYS_AND_VALUE, and HASH_SYS respectively. +Sorting is done via the HASH_IDENTITY and SP_VALUE columns. + +## Columns + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    NameRelationshipsDatatypeNullableDescription
    HASH_VALUELong + A hash like HASH_IDENTITY that also includes the SP_VALUE column. +
    HASH_SYS_AND_VALUELong + A hash like HASH_IDENTITY that also includes the SP_SYSTEM and SP_VALUE columns. +
    HASH_SYSLong + A hash like HASH_IDENTITY that also includes the SP_SYSTEM column. +
    SP_SYSTEMString + The system of the code. +
    SP_VALUEString + This is the bare code value. +
    + + +# HFJ_SPIDX_URI: URI Search Parameters + +FHIR Search Parameters of type [*uri*](https://www.hl7.org/fhir/search.html#uri) produce rows in the `HFJ_SPIDX_URI` table. +The default uri search matches the complete uri. This uses the HASH_URI column for an exact match. +A uri search with the `:above` modifier will match any prefix. This also uses the HASH_URI column, but also tests hashes of every prefix of the query value. +A uri search with the `:below` modifier will match any extension. This query uses the HASH_IDENTITY and a LIKE prefix match of the SP_URI column. +Sorting is done via the HASH_IDENTITY and SP_URI columns. + +## Columns + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    NameRelationshipsDatatypeNullableDescription
    HASH_URILong + A hash like HASH_IDENTITY that also includes the SP_URI column. +
    SP_URIString + The uri string extracted by the SearchParameter. +
    + diff --git a/hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/docs/server_jpa/search.md b/hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/docs/server_jpa/search.md index 8699d76d5bf..e760b4a3dab 100644 --- a/hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/docs/server_jpa/search.md +++ b/hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/docs/server_jpa/search.md @@ -22,6 +22,12 @@ Searching on Location.Position using `near` currently uses a box search, not a r The special `_filter` is only partially implemented. +### _pid + +The JPA server implements a non-standard special `_pid` which matches/sorts on the raw internal database id. +This sort is useful for imposing tie-breaking sort order in an efficient way. + +Note that this is an internal feature that may change or be removed in the future. Use with caution. diff --git a/hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/docs/server_jpa_batch/introduction.md b/hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/docs/server_jpa_batch/introduction.md index 74551d291a0..ce8dbc4a1f0 100644 --- a/hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/docs/server_jpa_batch/introduction.md +++ b/hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/docs/server_jpa_batch/introduction.md @@ -31,7 +31,7 @@ HAPI-FHIR Batch Jobs run based on job notification messages. The process is kic The handler then does the following: 1. Change the work chunk status from QUEUED to IN_PROGRESS 2. Change the Job Instance status from QUEUED to IN_PROGRESS -3. If the Job Instance is cancelled, change the status to COMPLETED and abort processing. +3. If the Job Instance is cancelled, change the status to CANCELLED and abort processing. 4. The first step of the job definition is executed with the job parameters 5. This step creates new work chunks. For each work chunk it creates, it json serializes the work chunk data, stores it in the database, and publishes a new message to the Batch Notification Message Channel to notify worker threads that there are new work chunks waiting to be processed. 6. If the step succeeded, the work chunk status is changed from IN_PROGRESS to COMPLETED, and the data it contained is deleted. diff --git a/hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/docs/server_jpa_mdm/mdm_operations.md b/hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/docs/server_jpa_mdm/mdm_operations.md index 4fcecc9dc4e..eec3d90a613 100644 --- a/hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/docs/server_jpa_mdm/mdm_operations.md +++ b/hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/docs/server_jpa_mdm/mdm_operations.md @@ -816,13 +816,13 @@ Content-Type: application/fhir+json; charset=UTF-8 { "name":"resource", "resource": { - "resourceType":"Orgaization", + "resourceType":"Organization", "name": "McMaster Family Practice" } }, { "name":"resourceType", - "valueString": "Orgaization" + "valueString": "Organization" } ] } diff --git a/hapi-fhir-jacoco/pom.xml b/hapi-fhir-jacoco/pom.xml index 7e8139bd23c..96c394d0795 100644 --- a/hapi-fhir-jacoco/pom.xml +++ b/hapi-fhir-jacoco/pom.xml @@ -11,7 +11,7 @@ ca.uhn.hapi.fhir hapi-deployable-pom - 6.9.7-SNAPSHOT + 6.11.7-SNAPSHOT ../hapi-deployable-pom/pom.xml diff --git a/hapi-fhir-jaxrsserver-base/pom.xml b/hapi-fhir-jaxrsserver-base/pom.xml index d39e783edf1..ce646c625d0 100644 --- a/hapi-fhir-jaxrsserver-base/pom.xml +++ b/hapi-fhir-jaxrsserver-base/pom.xml @@ -4,7 +4,7 @@ ca.uhn.hapi.fhir hapi-deployable-pom - 6.9.7-SNAPSHOT + 6.11.7-SNAPSHOT ../hapi-deployable-pom/pom.xml @@ -74,20 +74,22 @@
    - javax.ejb - ejb-api - provided + jakarta.ejb + jakarta.ejb-api - javax.servlet - javax.servlet-api - provided + jakarta.servlet + jakarta.servlet-api - org.jboss.spec.javax.ws.rs - jboss-jaxrs-api_2.1_spec - provided + jakarta.ws.rs + jakarta.ws.rs-api + + jakarta.interceptor + jakarta.interceptor-api + + org.springframework spring-context @@ -109,11 +111,6 @@ jetty-server test - - org.eclipse.jetty - jetty-servlet - test - ca.uhn.hapi.fhir hapi-fhir-test-utilities diff --git a/hapi-fhir-jaxrsserver-base/src/main/java/ca/uhn/fhir/jaxrs/client/JaxRsHttpClient.java b/hapi-fhir-jaxrsserver-base/src/main/java/ca/uhn/fhir/jaxrs/client/JaxRsHttpClient.java index 8ea09b92709..5e1e2569665 100644 --- a/hapi-fhir-jaxrsserver-base/src/main/java/ca/uhn/fhir/jaxrs/client/JaxRsHttpClient.java +++ b/hapi-fhir-jaxrsserver-base/src/main/java/ca/uhn/fhir/jaxrs/client/JaxRsHttpClient.java @@ -29,20 +29,20 @@ import ca.uhn.fhir.rest.client.api.IHttpClient; import ca.uhn.fhir.rest.client.api.IHttpRequest; import ca.uhn.fhir.rest.client.impl.BaseHttpClientInvocation; import ca.uhn.fhir.rest.client.method.MethodUtil; +import jakarta.ws.rs.client.Client; +import jakarta.ws.rs.client.Entity; +import jakarta.ws.rs.client.Invocation.Builder; +import jakarta.ws.rs.core.Form; +import jakarta.ws.rs.core.MultivaluedHashMap; +import jakarta.ws.rs.core.MultivaluedMap; import org.hl7.fhir.instance.model.api.IBaseBinary; import java.util.List; import java.util.Map; -import javax.ws.rs.client.Client; -import javax.ws.rs.client.Entity; -import javax.ws.rs.client.Invocation.Builder; -import javax.ws.rs.core.Form; -import javax.ws.rs.core.MultivaluedHashMap; -import javax.ws.rs.core.MultivaluedMap; /** * A Http Request based on JaxRs. This is an adapter around the class - * {@link javax.ws.rs.client.Client Client} + * {@link jakarta.ws.rs.client.Client Client} * * @author Peter Van Houte | peter.vanhoute@agfa.com | Agfa Healthcare */ diff --git a/hapi-fhir-jaxrsserver-base/src/main/java/ca/uhn/fhir/jaxrs/client/JaxRsHttpRequest.java b/hapi-fhir-jaxrsserver-base/src/main/java/ca/uhn/fhir/jaxrs/client/JaxRsHttpRequest.java index 94f03508eed..844e46b81cf 100644 --- a/hapi-fhir-jaxrsserver-base/src/main/java/ca/uhn/fhir/jaxrs/client/JaxRsHttpRequest.java +++ b/hapi-fhir-jaxrsserver-base/src/main/java/ca/uhn/fhir/jaxrs/client/JaxRsHttpRequest.java @@ -25,19 +25,19 @@ import ca.uhn.fhir.rest.client.api.BaseHttpRequest; import ca.uhn.fhir.rest.client.api.IHttpRequest; import ca.uhn.fhir.rest.client.api.IHttpResponse; import ca.uhn.fhir.util.StopWatch; +import jakarta.ws.rs.client.Entity; +import jakarta.ws.rs.client.Invocation; +import jakarta.ws.rs.core.Response; import java.util.Collections; import java.util.HashMap; import java.util.LinkedList; import java.util.List; import java.util.Map; -import javax.ws.rs.client.Entity; -import javax.ws.rs.client.Invocation; -import javax.ws.rs.core.Response; /** * A Http Request based on JaxRs. This is an adapter around the class - * {@link javax.ws.rs.client.Invocation Invocation} + * {@link jakarta.ws.rs.client.Invocation Invocation} * * @author Peter Van Houte | peter.vanhoute@agfa.com | Agfa Healthcare */ diff --git a/hapi-fhir-jaxrsserver-base/src/main/java/ca/uhn/fhir/jaxrs/client/JaxRsHttpResponse.java b/hapi-fhir-jaxrsserver-base/src/main/java/ca/uhn/fhir/jaxrs/client/JaxRsHttpResponse.java index 1522d5007d3..4599ee9e1b3 100644 --- a/hapi-fhir-jaxrsserver-base/src/main/java/ca/uhn/fhir/jaxrs/client/JaxRsHttpResponse.java +++ b/hapi-fhir-jaxrsserver-base/src/main/java/ca/uhn/fhir/jaxrs/client/JaxRsHttpResponse.java @@ -22,17 +22,17 @@ package ca.uhn.fhir.jaxrs.client; import ca.uhn.fhir.rest.client.api.IHttpResponse; import ca.uhn.fhir.rest.client.impl.BaseHttpResponse; import ca.uhn.fhir.util.StopWatch; +import jakarta.ws.rs.core.MediaType; +import jakarta.ws.rs.core.Response; import java.io.*; import java.util.List; import java.util.Map; import java.util.Map.Entry; import java.util.concurrent.ConcurrentHashMap; -import javax.ws.rs.core.MediaType; -import javax.ws.rs.core.Response; /** - * A Http Response based on JaxRs. This is an adapter around the class {@link javax.ws.rs.core.Response Response} + * A Http Response based on JaxRs. This is an adapter around the class {@link jakarta.ws.rs.core.Response Response} * @author Peter Van Houte | peter.vanhoute@agfa.com | Agfa Healthcare */ public class JaxRsHttpResponse extends BaseHttpResponse implements IHttpResponse { diff --git a/hapi-fhir-jaxrsserver-base/src/main/java/ca/uhn/fhir/jaxrs/client/JaxRsRestfulClientFactory.java b/hapi-fhir-jaxrsserver-base/src/main/java/ca/uhn/fhir/jaxrs/client/JaxRsRestfulClientFactory.java index 68084615474..fc5788fa903 100644 --- a/hapi-fhir-jaxrsserver-base/src/main/java/ca/uhn/fhir/jaxrs/client/JaxRsRestfulClientFactory.java +++ b/hapi-fhir-jaxrsserver-base/src/main/java/ca/uhn/fhir/jaxrs/client/JaxRsRestfulClientFactory.java @@ -25,11 +25,11 @@ import ca.uhn.fhir.rest.api.RequestTypeEnum; import ca.uhn.fhir.rest.client.api.Header; import ca.uhn.fhir.rest.client.api.IHttpClient; import ca.uhn.fhir.rest.client.impl.RestfulClientFactory; +import jakarta.ws.rs.client.Client; +import jakarta.ws.rs.client.ClientBuilder; import java.util.List; import java.util.Map; -import javax.ws.rs.client.Client; -import javax.ws.rs.client.ClientBuilder; /** * A Restful Client Factory, based on Jax Rs @@ -97,7 +97,7 @@ public class JaxRsRestfulClientFactory extends RestfulClientFactory { } /** - * Only accept clients of type javax.ws.rs.client.Client + * Only accept clients of type jakarta.ws.rs.client.Client * Can be used to set a specific Client implementation * @param theHttpClient */ diff --git a/hapi-fhir-jaxrsserver-base/src/main/java/ca/uhn/fhir/jaxrs/server/AbstractJaxRsBundleProvider.java b/hapi-fhir-jaxrsserver-base/src/main/java/ca/uhn/fhir/jaxrs/server/AbstractJaxRsBundleProvider.java index fc00b175fd6..27fc15d6f9f 100644 --- a/hapi-fhir-jaxrsserver-base/src/main/java/ca/uhn/fhir/jaxrs/server/AbstractJaxRsBundleProvider.java +++ b/hapi-fhir-jaxrsserver-base/src/main/java/ca/uhn/fhir/jaxrs/server/AbstractJaxRsBundleProvider.java @@ -25,21 +25,25 @@ import ca.uhn.fhir.jaxrs.server.interceptor.JaxRsExceptionInterceptor; import ca.uhn.fhir.jaxrs.server.util.JaxRsMethodBindings; import ca.uhn.fhir.jaxrs.server.util.JaxRsRequest; import ca.uhn.fhir.jaxrs.server.util.JaxRsRequest.Builder; +import ca.uhn.fhir.rest.api.Constants; +import ca.uhn.fhir.rest.api.RequestTypeEnum; +import ca.uhn.fhir.rest.api.RestOperationTypeEnum; import ca.uhn.fhir.rest.api.server.IBundleProvider; import ca.uhn.fhir.rest.api.server.IRestfulServer; -import ca.uhn.fhir.rest.api.*; import ca.uhn.fhir.rest.server.IPagingProvider; import ca.uhn.fhir.rest.server.interceptor.IServerInterceptor; import ca.uhn.fhir.rest.server.method.BaseMethodBinding; +import jakarta.interceptor.Interceptors; +import jakarta.ws.rs.Consumes; +import jakarta.ws.rs.GET; +import jakarta.ws.rs.POST; +import jakarta.ws.rs.Produces; +import jakarta.ws.rs.core.MediaType; +import jakarta.ws.rs.core.Response; import java.io.IOException; import java.util.Collections; import java.util.List; -import javax.interceptor.Interceptors; -import javax.ws.rs.Produces; -import javax.ws.rs.core.MediaType; -import javax.ws.rs.core.Response; -import javax.ws.rs.*; /** * This server is the abstract superclass for all bundle providers. It exposes diff --git a/hapi-fhir-jaxrsserver-base/src/main/java/ca/uhn/fhir/jaxrs/server/AbstractJaxRsConformanceProvider.java b/hapi-fhir-jaxrsserver-base/src/main/java/ca/uhn/fhir/jaxrs/server/AbstractJaxRsConformanceProvider.java index 5208fe2e03f..926794f41cf 100644 --- a/hapi-fhir-jaxrsserver-base/src/main/java/ca/uhn/fhir/jaxrs/server/AbstractJaxRsConformanceProvider.java +++ b/hapi-fhir-jaxrsserver-base/src/main/java/ca/uhn/fhir/jaxrs/server/AbstractJaxRsConformanceProvider.java @@ -40,6 +40,12 @@ import ca.uhn.fhir.rest.server.RestfulServerUtils; import ca.uhn.fhir.rest.server.method.BaseMethodBinding; import ca.uhn.fhir.rest.server.provider.ServerCapabilityStatementProvider; import ca.uhn.fhir.util.ReflectionUtil; +import jakarta.ws.rs.GET; +import jakarta.ws.rs.OPTIONS; +import jakarta.ws.rs.Path; +import jakarta.ws.rs.Produces; +import jakarta.ws.rs.core.MediaType; +import jakarta.ws.rs.core.Response; import org.apache.commons.lang3.StringUtils; import org.hl7.fhir.dstu2.hapi.rest.server.ServerConformanceProvider; import org.hl7.fhir.instance.model.api.IBaseResource; @@ -59,12 +65,6 @@ import java.util.List; import java.util.Map.Entry; import java.util.Set; import java.util.concurrent.ConcurrentHashMap; -import javax.ws.rs.GET; -import javax.ws.rs.OPTIONS; -import javax.ws.rs.Path; -import javax.ws.rs.Produces; -import javax.ws.rs.core.MediaType; -import javax.ws.rs.core.Response; /** * This is the conformance provider for the jax rs servers. It requires all providers to be registered during startup because the conformance profile is generated during the postconstruct phase. diff --git a/hapi-fhir-jaxrsserver-base/src/main/java/ca/uhn/fhir/jaxrs/server/AbstractJaxRsPageProvider.java b/hapi-fhir-jaxrsserver-base/src/main/java/ca/uhn/fhir/jaxrs/server/AbstractJaxRsPageProvider.java index 86626914d35..133791e8c63 100644 --- a/hapi-fhir-jaxrsserver-base/src/main/java/ca/uhn/fhir/jaxrs/server/AbstractJaxRsPageProvider.java +++ b/hapi-fhir-jaxrsserver-base/src/main/java/ca/uhn/fhir/jaxrs/server/AbstractJaxRsPageProvider.java @@ -30,14 +30,14 @@ import ca.uhn.fhir.rest.api.*; import ca.uhn.fhir.rest.server.IPagingProvider; import ca.uhn.fhir.rest.server.PageProvider; import ca.uhn.fhir.rest.server.method.PageMethodBinding; +import jakarta.interceptor.Interceptors; +import jakarta.ws.rs.GET; +import jakarta.ws.rs.Produces; +import jakarta.ws.rs.QueryParam; +import jakarta.ws.rs.core.MediaType; +import jakarta.ws.rs.core.Response; import java.io.IOException; -import javax.interceptor.Interceptors; -import javax.ws.rs.GET; -import javax.ws.rs.Produces; -import javax.ws.rs.QueryParam; -import javax.ws.rs.core.MediaType; -import javax.ws.rs.core.Response; /** * Base class for a provider to provide the [baseUrl]?_getpages=foo request, which is a request to the diff --git a/hapi-fhir-jaxrsserver-base/src/main/java/ca/uhn/fhir/jaxrs/server/AbstractJaxRsProvider.java b/hapi-fhir-jaxrsserver-base/src/main/java/ca/uhn/fhir/jaxrs/server/AbstractJaxRsProvider.java index 6d3e3b17cd2..b297ee90daa 100644 --- a/hapi-fhir-jaxrsserver-base/src/main/java/ca/uhn/fhir/jaxrs/server/AbstractJaxRsProvider.java +++ b/hapi-fhir-jaxrsserver-base/src/main/java/ca/uhn/fhir/jaxrs/server/AbstractJaxRsProvider.java @@ -29,16 +29,16 @@ import ca.uhn.fhir.jaxrs.server.util.JaxRsRequest.Builder; import ca.uhn.fhir.rest.api.*; import ca.uhn.fhir.rest.server.interceptor.IServerInterceptor; import ca.uhn.fhir.rest.server.*; +import jakarta.ws.rs.core.Context; +import jakarta.ws.rs.core.HttpHeaders; +import jakarta.ws.rs.core.MultivaluedMap; +import jakarta.ws.rs.core.Response; +import jakarta.ws.rs.core.UriInfo; import org.apache.commons.lang3.StringUtils; import java.io.IOException; import java.util.Map.Entry; import java.util.*; -import javax.ws.rs.core.Context; -import javax.ws.rs.core.HttpHeaders; -import javax.ws.rs.core.MultivaluedMap; -import javax.ws.rs.core.Response; -import javax.ws.rs.core.UriInfo; /** * This is the abstract superclass for all jaxrs providers. It contains some defaults implementing @@ -103,7 +103,7 @@ public abstract class AbstractJaxRsProvider implements IRestfulServerDefaults { /** * This method returns the server base, independent of the request or resource. * - * @see javax.ws.rs.core.UriInfo#getBaseUri() + * @see jakarta.ws.rs.core.UriInfo#getBaseUri() * @return the ascii string for the server base */ public String getBaseForServer() { diff --git a/hapi-fhir-jaxrsserver-base/src/main/java/ca/uhn/fhir/jaxrs/server/AbstractJaxRsResourceProvider.java b/hapi-fhir-jaxrsserver-base/src/main/java/ca/uhn/fhir/jaxrs/server/AbstractJaxRsResourceProvider.java index ebb634294af..5feb325154e 100644 --- a/hapi-fhir-jaxrsserver-base/src/main/java/ca/uhn/fhir/jaxrs/server/AbstractJaxRsResourceProvider.java +++ b/hapi-fhir-jaxrsserver-base/src/main/java/ca/uhn/fhir/jaxrs/server/AbstractJaxRsResourceProvider.java @@ -30,14 +30,21 @@ import ca.uhn.fhir.rest.api.*; import ca.uhn.fhir.rest.server.IPagingProvider; import ca.uhn.fhir.rest.server.IResourceProvider; import ca.uhn.fhir.rest.server.method.BaseMethodBinding; +import jakarta.interceptor.Interceptors; +import jakarta.ws.rs.Consumes; +import jakarta.ws.rs.DELETE; +import jakarta.ws.rs.GET; +import jakarta.ws.rs.POST; +import jakarta.ws.rs.PUT; +import jakarta.ws.rs.Path; +import jakarta.ws.rs.PathParam; +import jakarta.ws.rs.Produces; +import jakarta.ws.rs.core.MediaType; +import jakarta.ws.rs.core.Response; import org.hl7.fhir.instance.model.api.IBaseResource; import java.io.IOException; import java.net.URL; -import javax.interceptor.Interceptors; -import javax.ws.rs.core.MediaType; -import javax.ws.rs.core.Response; -import javax.ws.rs.*; /** * This server is the abstract superclass for all resource providers. It exposes diff --git a/hapi-fhir-jaxrsserver-base/src/main/java/ca/uhn/fhir/jaxrs/server/interceptor/JaxRsExceptionInterceptor.java b/hapi-fhir-jaxrsserver-base/src/main/java/ca/uhn/fhir/jaxrs/server/interceptor/JaxRsExceptionInterceptor.java index 19b046cf2d9..f58be619bec 100644 --- a/hapi-fhir-jaxrsserver-base/src/main/java/ca/uhn/fhir/jaxrs/server/interceptor/JaxRsExceptionInterceptor.java +++ b/hapi-fhir-jaxrsserver-base/src/main/java/ca/uhn/fhir/jaxrs/server/interceptor/JaxRsExceptionInterceptor.java @@ -24,12 +24,12 @@ import ca.uhn.fhir.jaxrs.server.util.JaxRsRequest; import ca.uhn.fhir.rest.server.exceptions.BaseServerResponseException; import ca.uhn.fhir.rest.server.exceptions.InternalErrorException; import ca.uhn.fhir.rest.server.interceptor.ExceptionHandlingInterceptor; +import jakarta.interceptor.AroundInvoke; +import jakarta.interceptor.InvocationContext; +import jakarta.servlet.ServletException; +import jakarta.ws.rs.core.Response; import java.io.IOException; -import javax.interceptor.AroundInvoke; -import javax.interceptor.InvocationContext; -import javax.servlet.ServletException; -import javax.ws.rs.core.Response; /** * An interceptor that catches the jax-rs exceptions diff --git a/hapi-fhir-jaxrsserver-base/src/main/java/ca/uhn/fhir/jaxrs/server/interceptor/JaxRsResponseException.java b/hapi-fhir-jaxrsserver-base/src/main/java/ca/uhn/fhir/jaxrs/server/interceptor/JaxRsResponseException.java index 8b14284e530..14100369b86 100644 --- a/hapi-fhir-jaxrsserver-base/src/main/java/ca/uhn/fhir/jaxrs/server/interceptor/JaxRsResponseException.java +++ b/hapi-fhir-jaxrsserver-base/src/main/java/ca/uhn/fhir/jaxrs/server/interceptor/JaxRsResponseException.java @@ -20,8 +20,7 @@ package ca.uhn.fhir.jaxrs.server.interceptor; import ca.uhn.fhir.rest.server.exceptions.BaseServerResponseException; - -import javax.ejb.ApplicationException; +import jakarta.ejb.ApplicationException; /** * A JEE wrapper exception that will not force a rollback. diff --git a/hapi-fhir-jaxrsserver-base/src/main/java/ca/uhn/fhir/jaxrs/server/util/JaxRsRequest.java b/hapi-fhir-jaxrsserver-base/src/main/java/ca/uhn/fhir/jaxrs/server/util/JaxRsRequest.java index 3fd75c52e6f..52a9177ffd5 100644 --- a/hapi-fhir-jaxrsserver-base/src/main/java/ca/uhn/fhir/jaxrs/server/util/JaxRsRequest.java +++ b/hapi-fhir-jaxrsserver-base/src/main/java/ca/uhn/fhir/jaxrs/server/util/JaxRsRequest.java @@ -32,6 +32,8 @@ import ca.uhn.fhir.rest.api.server.RequestDetails; import ca.uhn.fhir.rest.server.exceptions.InvalidRequestException; import ca.uhn.fhir.rest.server.method.ResourceParameter; import ca.uhn.fhir.util.UrlUtil; +import jakarta.ws.rs.core.HttpHeaders; +import jakarta.ws.rs.core.MediaType; import org.apache.commons.lang3.StringUtils; import java.io.IOException; @@ -42,8 +44,6 @@ import java.util.Collections; import java.util.HashMap; import java.util.List; import java.util.Map; -import javax.ws.rs.core.HttpHeaders; -import javax.ws.rs.core.MediaType; /** * The JaxRsRequest is a jax-rs specific implementation of the RequestDetails. diff --git a/hapi-fhir-jaxrsserver-base/src/main/java/ca/uhn/fhir/jaxrs/server/util/JaxRsResponse.java b/hapi-fhir-jaxrsserver-base/src/main/java/ca/uhn/fhir/jaxrs/server/util/JaxRsResponse.java index 5d08b17f87e..1de1682f63d 100644 --- a/hapi-fhir-jaxrsserver-base/src/main/java/ca/uhn/fhir/jaxrs/server/util/JaxRsResponse.java +++ b/hapi-fhir-jaxrsserver-base/src/main/java/ca/uhn/fhir/jaxrs/server/util/JaxRsResponse.java @@ -22,6 +22,9 @@ package ca.uhn.fhir.jaxrs.server.util; import ca.uhn.fhir.rest.api.Constants; import ca.uhn.fhir.rest.server.BaseRestfulResponse; import ca.uhn.fhir.util.IoUtil; +import jakarta.annotation.Nonnull; +import jakarta.ws.rs.core.Response; +import jakarta.ws.rs.core.Response.ResponseBuilder; import org.apache.commons.lang3.StringUtils; import org.apache.commons.lang3.Validate; @@ -32,9 +35,6 @@ import java.io.StringWriter; import java.io.Writer; import java.util.List; import java.util.Map.Entry; -import javax.annotation.Nonnull; -import javax.ws.rs.core.Response; -import javax.ws.rs.core.Response.ResponseBuilder; import static org.apache.commons.lang3.StringUtils.isNotBlank; diff --git a/hapi-fhir-jaxrsserver-base/src/test/java/ca/uhn/fhir/jaxrs/client/GenericJaxRsClientDstu2Test.java b/hapi-fhir-jaxrsserver-base/src/test/java/ca/uhn/fhir/jaxrs/client/GenericJaxRsClientDstu2Test.java index 4392426bc20..725c8268525 100644 --- a/hapi-fhir-jaxrsserver-base/src/test/java/ca/uhn/fhir/jaxrs/client/GenericJaxRsClientDstu2Test.java +++ b/hapi-fhir-jaxrsserver-base/src/test/java/ca/uhn/fhir/jaxrs/client/GenericJaxRsClientDstu2Test.java @@ -27,38 +27,26 @@ import ca.uhn.fhir.rest.api.MethodOutcome; import ca.uhn.fhir.rest.api.PreferReturnEnum; import ca.uhn.fhir.rest.api.SearchStyleEnum; import ca.uhn.fhir.rest.api.SummaryEnum; -import ca.uhn.fhir.rest.client.api.Header; import ca.uhn.fhir.rest.client.api.IGenericClient; import ca.uhn.fhir.rest.client.api.ServerValidationModeEnum; import ca.uhn.fhir.rest.client.exceptions.InvalidResponseException; import ca.uhn.fhir.rest.client.interceptor.LoggingInterceptor; import ca.uhn.fhir.rest.param.DateRangeParam; -import ca.uhn.fhir.test.utilities.JettyUtil; -import com.google.common.collect.ArrayListMultimap; -import com.google.common.collect.Lists; -import com.google.common.collect.Maps; -import org.apache.commons.io.Charsets; +import ca.uhn.fhir.test.utilities.server.HttpServletExtension; +import ca.uhn.fhir.test.utilities.server.RequestCaptureServlet; import org.apache.commons.io.IOUtils; -import org.eclipse.jetty.server.Request; -import org.eclipse.jetty.server.Server; -import org.eclipse.jetty.server.handler.AbstractHandler; import org.hl7.fhir.instance.model.api.IBase; import org.hl7.fhir.instance.model.api.IBaseBundle; import org.hl7.fhir.instance.model.api.IBaseOperationOutcome; import org.hl7.fhir.instance.model.api.IBaseResource; -import org.junit.jupiter.api.AfterAll; -import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; -import javax.servlet.http.HttpServletRequest; -import javax.servlet.http.HttpServletResponse; -import java.io.IOException; +import org.junit.jupiter.api.extension.RegisterExtension; + import java.util.ArrayList; import java.util.Date; -import java.util.Enumeration; import java.util.List; -import java.util.Map; import static org.hamcrest.MatcherAssert.assertThat; import static org.hamcrest.Matchers.containsString; @@ -73,23 +61,15 @@ import static org.junit.jupiter.api.Assertions.fail; public class GenericJaxRsClientDstu2Test { private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(GenericJaxRsClientDstu2Test.class); - private static FhirContext ourCtx; - private static int ourPort; - private static Server ourServer; - private static int ourResponseCount = 0; - private static String[] ourResponseBodies; - private static String ourResponseBody; - private static String ourResponseContentType; - private static int ourResponseStatus; - private static String ourRequestUri; - private static List ourRequestUriAll; - private static String ourRequestMethod; - private static String ourRequestContentType; - private static byte[] ourRequestBodyBytes; - private static String ourRequestBodyString; - private static ArrayListMultimap ourRequestHeaders; - private static List> ourRequestHeadersAll; - private static Map ourRequestFirstHeaders; + + private static final FhirContext ourCtx = FhirContext.forDstu2(); + + private static final RequestCaptureServlet CAPTURE_SERVLET = new RequestCaptureServlet(); + + @RegisterExtension + public static final HttpServletExtension ourServer = new HttpServletExtension() + .withServlet(CAPTURE_SERVLET) + .keepAliveBetweenTests(); @BeforeEach public void before() { @@ -97,7 +77,7 @@ public class GenericJaxRsClientDstu2Test { clientFactory.setServerValidationMode(ServerValidationModeEnum.NEVER); ourCtx.setRestfulClientFactory(clientFactory); - ourResponseCount = 0; + CAPTURE_SERVLET.reset(); } private String getPatientFeedWithOneResult() { @@ -129,28 +109,28 @@ public class GenericJaxRsClientDstu2Test { conf.setCopyright("COPY"); final String respString = p.encodeResourceToString(conf); - ourResponseContentType = Constants.CT_FHIR_XML + "; charset=UTF-8"; - ourResponseBody = respString; + CAPTURE_SERVLET.ourResponseContentType = Constants.CT_FHIR_XML + "; charset=UTF-8"; + CAPTURE_SERVLET.ourResponseBody = respString; - IGenericClient client = ourCtx.newRestfulGenericClient("http://localhost:" + ourPort + "/fhir"); + IGenericClient client = ourCtx.newRestfulGenericClient(ourServer.getBaseUrl() + "/fhir"); client.fetchConformance().ofType(Conformance.class).execute(); - assertEquals("http://localhost:" + ourPort + "/fhir/metadata", ourRequestUri); - assertEquals(1, ourRequestHeaders.get("Accept").size()); - assertThat(ourRequestHeaders.get("Accept").get(0).getValue(), containsString(Constants.HEADER_ACCEPT_VALUE_XML_OR_JSON_LEGACY)); + assertEquals(ourServer.getBaseUrl() + "/fhir/metadata", CAPTURE_SERVLET.ourRequestUri); + assertEquals(1, CAPTURE_SERVLET.ourRequestHeaders.get("Accept").size()); + assertThat(CAPTURE_SERVLET.ourRequestHeaders.get("Accept").get(0).getValue(), containsString(Constants.HEADER_ACCEPT_VALUE_XML_OR_JSON_LEGACY)); client.fetchConformance().ofType(Conformance.class).encodedJson().execute(); - assertEquals("http://localhost:" + ourPort + "/fhir/metadata?_format=json", ourRequestUri); - assertEquals(1, ourRequestHeaders.get("Accept").size()); - assertThat(ourRequestHeaders.get("Accept").get(0).getValue(), containsString(Constants.CT_FHIR_JSON)); + assertEquals(ourServer.getBaseUrl() + "/fhir/metadata?_format=json", CAPTURE_SERVLET.ourRequestUri); + assertEquals(1, CAPTURE_SERVLET.ourRequestHeaders.get("Accept").size()); + assertThat(CAPTURE_SERVLET.ourRequestHeaders.get("Accept").get(0).getValue(), containsString(Constants.CT_FHIR_JSON)); client.fetchConformance().ofType(Conformance.class).encodedXml().execute(); - assertEquals("http://localhost:" + ourPort + "/fhir/metadata?_format=xml", ourRequestUri); - assertEquals(1, ourRequestHeaders.get("Accept").size()); - assertThat(ourRequestHeaders.get("Accept").get(0).getValue(), containsString(Constants.CT_FHIR_XML)); + assertEquals(ourServer.getBaseUrl() + "/fhir/metadata?_format=xml", CAPTURE_SERVLET.ourRequestUri); + assertEquals(1, CAPTURE_SERVLET.ourRequestHeaders.get("Accept").size()); + assertThat(CAPTURE_SERVLET.ourRequestHeaders.get("Accept").get(0).getValue(), containsString(Constants.CT_FHIR_XML)); } @@ -164,24 +144,24 @@ public class GenericJaxRsClientDstu2Test { final Patient patient = new Patient(); patient.addName().addFamily("FAMILY"); - ourResponseContentType = Constants.CT_FHIR_XML + "; charset=UTF-8"; - ourResponseBodies = new String[]{p.encodeResourceToString(conf), p.encodeResourceToString(patient)}; + CAPTURE_SERVLET.ourResponseContentType = Constants.CT_FHIR_XML + "; charset=UTF-8"; + CAPTURE_SERVLET.ourResponseBodies = new String[]{p.encodeResourceToString(conf), p.encodeResourceToString(patient)}; ourCtx.getRestfulClientFactory().setServerValidationMode(ServerValidationModeEnum.ONCE); - IGenericClient client = ourCtx.newRestfulGenericClient("http://localhost:" + ourPort + "/fhir"); + IGenericClient client = ourCtx.newRestfulGenericClient(ourServer.getBaseUrl() + "/fhir"); Patient resp = client.read(Patient.class, new IdDt("123")); assertEquals("FAMILY", resp.getName().get(0).getFamily().get(0).getValue()); - assertEquals("http://localhost:" + ourPort + "/fhir/metadata", ourRequestUriAll.get(0)); - assertEquals(1, ourRequestHeadersAll.get(0).get("Accept").size()); - assertThat(ourRequestHeadersAll.get(0).get("Accept").get(0).getValue(), containsString(Constants.HEADER_ACCEPT_VALUE_XML_OR_JSON_LEGACY)); - assertThat(ourRequestHeadersAll.get(0).get("Accept").get(0).getValue(), containsString(Constants.CT_FHIR_XML)); - assertThat(ourRequestHeadersAll.get(0).get("Accept").get(0).getValue(), containsString(Constants.CT_FHIR_JSON)); - assertEquals("http://localhost:" + ourPort + "/fhir/Patient/123", ourRequestUriAll.get(1)); - assertEquals(1, ourRequestHeadersAll.get(1).get("Accept").size()); - assertThat(ourRequestHeadersAll.get(1).get("Accept").get(0).getValue(), containsString(Constants.HEADER_ACCEPT_VALUE_XML_OR_JSON_LEGACY)); - assertThat(ourRequestHeadersAll.get(1).get("Accept").get(0).getValue(), containsString(Constants.CT_FHIR_XML)); - assertThat(ourRequestHeadersAll.get(1).get("Accept").get(0).getValue(), containsString(Constants.CT_FHIR_JSON)); + assertEquals(ourServer.getBaseUrl() + "/fhir/metadata", CAPTURE_SERVLET.ourRequestUriAll.get(0)); + assertEquals(1, CAPTURE_SERVLET.ourRequestHeadersAll.get(0).get("Accept").size()); + assertThat(CAPTURE_SERVLET.ourRequestHeadersAll.get(0).get("Accept").get(0).getValue(), containsString(Constants.HEADER_ACCEPT_VALUE_XML_OR_JSON_LEGACY)); + assertThat(CAPTURE_SERVLET.ourRequestHeadersAll.get(0).get("Accept").get(0).getValue(), containsString(Constants.CT_FHIR_XML)); + assertThat(CAPTURE_SERVLET.ourRequestHeadersAll.get(0).get("Accept").get(0).getValue(), containsString(Constants.CT_FHIR_JSON)); + assertEquals(ourServer.getBaseUrl() + "/fhir/Patient/123", CAPTURE_SERVLET.ourRequestUriAll.get(1)); + assertEquals(1, CAPTURE_SERVLET.ourRequestHeadersAll.get(1).get("Accept").size()); + assertThat(CAPTURE_SERVLET.ourRequestHeadersAll.get(1).get("Accept").get(0).getValue(), containsString(Constants.HEADER_ACCEPT_VALUE_XML_OR_JSON_LEGACY)); + assertThat(CAPTURE_SERVLET.ourRequestHeadersAll.get(1).get("Accept").get(0).getValue(), containsString(Constants.CT_FHIR_XML)); + assertThat(CAPTURE_SERVLET.ourRequestHeadersAll.get(1).get("Accept").get(0).getValue(), containsString(Constants.CT_FHIR_JSON)); } @Test @@ -194,23 +174,23 @@ public class GenericJaxRsClientDstu2Test { final Patient patient = new Patient(); patient.addName().addFamily("FAMILY"); - ourResponseContentType = Constants.CT_FHIR_XML + "; charset=UTF-8"; - ourResponseBodies = new String[]{p.encodeResourceToString(conf), p.encodeResourceToString(patient)}; + CAPTURE_SERVLET.ourResponseContentType = Constants.CT_FHIR_XML + "; charset=UTF-8"; + CAPTURE_SERVLET.ourResponseBodies = new String[]{p.encodeResourceToString(conf), p.encodeResourceToString(patient)}; ourCtx.getRestfulClientFactory().setServerValidationMode(ServerValidationModeEnum.ONCE); - IGenericClient client = ourCtx.newRestfulGenericClient("http://localhost:" + ourPort + "/fhir"); + IGenericClient client = ourCtx.newRestfulGenericClient(ourServer.getBaseUrl() + "/fhir"); client.setEncoding(EncodingEnum.JSON); Patient resp = client.read(Patient.class, new IdDt("123")); assertEquals("FAMILY", resp.getName().get(0).getFamily().get(0).getValue()); - assertEquals("http://localhost:" + ourPort + "/fhir/metadata?_format=json", ourRequestUriAll.get(0)); - assertEquals(1, ourRequestHeadersAll.get(0).get("Accept").size()); - assertThat(ourRequestHeadersAll.get(0).get("Accept").get(0).getValue(), containsString(Constants.CT_FHIR_JSON)); - assertThat(ourRequestHeadersAll.get(0).get("Accept").get(0).getValue(), not(containsString(Constants.CT_FHIR_XML))); - assertEquals("http://localhost:" + ourPort + "/fhir/Patient/123?_format=json", ourRequestUriAll.get(1)); - assertEquals(1, ourRequestHeadersAll.get(1).get("Accept").size()); - assertThat(ourRequestHeadersAll.get(1).get("Accept").get(0).getValue(), containsString(Constants.CT_FHIR_JSON)); - assertThat(ourRequestHeadersAll.get(1).get("Accept").get(0).getValue(), not(containsString(Constants.CT_FHIR_XML))); + assertEquals(ourServer.getBaseUrl() + "/fhir/metadata?_format=json", CAPTURE_SERVLET.ourRequestUriAll.get(0)); + assertEquals(1, CAPTURE_SERVLET.ourRequestHeadersAll.get(0).get("Accept").size()); + assertThat(CAPTURE_SERVLET.ourRequestHeadersAll.get(0).get("Accept").get(0).getValue(), containsString(Constants.CT_FHIR_JSON)); + assertThat(CAPTURE_SERVLET.ourRequestHeadersAll.get(0).get("Accept").get(0).getValue(), not(containsString(Constants.CT_FHIR_XML))); + assertEquals(ourServer.getBaseUrl() + "/fhir/Patient/123?_format=json", CAPTURE_SERVLET.ourRequestUriAll.get(1)); + assertEquals(1, CAPTURE_SERVLET.ourRequestHeadersAll.get(1).get("Accept").size()); + assertThat(CAPTURE_SERVLET.ourRequestHeadersAll.get(1).get("Accept").get(0).getValue(), containsString(Constants.CT_FHIR_JSON)); + assertThat(CAPTURE_SERVLET.ourRequestHeadersAll.get(1).get("Accept").get(0).getValue(), not(containsString(Constants.CT_FHIR_XML))); } @Test @@ -223,16 +203,16 @@ public class GenericJaxRsClientDstu2Test { final String respString = p.encodeResourceToString(conf); - ourResponseContentType = Constants.CT_FHIR_XML + "; charset=UTF-8"; - ourResponseBody = respString; + CAPTURE_SERVLET.ourResponseContentType = Constants.CT_FHIR_XML + "; charset=UTF-8"; + CAPTURE_SERVLET.ourResponseBody = respString; - IGenericClient client = ourCtx.newRestfulGenericClient("http://localhost:" + ourPort + "/fhir"); + IGenericClient client = ourCtx.newRestfulGenericClient(ourServer.getBaseUrl() + "/fhir"); Conformance resp = client.capabilities().ofType(Conformance.class).execute(); - assertEquals("http://localhost:" + ourPort + "/fhir/metadata", ourRequestUri); + assertEquals(ourServer.getBaseUrl() + "/fhir/metadata", CAPTURE_SERVLET.ourRequestUri); assertEquals("COPY", resp.getCopyright()); - assertEquals("GET", ourRequestMethod); + assertEquals("GET", CAPTURE_SERVLET.ourRequestMethod); } @@ -245,7 +225,7 @@ public class GenericJaxRsClientDstu2Test { ourCtx.setRestfulClientFactory(clientFactory); try { - ourCtx.newRestfulGenericClient("http://localhost:" + ourPort + "/fhir"); + ourCtx.newRestfulGenericClient(ourServer.getBaseUrl() + "/fhir"); fail(); } catch (IllegalStateException e) { assertEquals(Msg.code(1355) + "JaxRsRestfulClientFactory does not have FhirContext defined. This must be set via JaxRsRestfulClientFactory#setFhirContext(FhirContext)", e.getMessage()); @@ -254,7 +234,7 @@ public class GenericJaxRsClientDstu2Test { @Test public void testCreate() { - IGenericClient client = ourCtx.newRestfulGenericClient("http://localhost:" + ourPort + "/fhir"); + IGenericClient client = ourCtx.newRestfulGenericClient(ourServer.getBaseUrl() + "/fhir"); Patient p = new Patient(); @@ -262,23 +242,23 @@ public class GenericJaxRsClientDstu2Test { client.create().resource(p).encodedXml().execute(); - assertEquals(1, ourRequestHeaders.get(Constants.HEADER_CONTENT_TYPE).size()); - assertEquals(EncodingEnum.XML.getResourceContentType() + Constants.HEADER_SUFFIX_CT_UTF_8, ourRequestFirstHeaders.get(Constants.HEADER_CONTENT_TYPE).getValue().replace(";char", "; char")); - assertThat(ourRequestBodyString, containsString("")); - assertEquals("http://localhost:" + ourPort + "/fhir/Patient", ourRequestUri); - assertEquals("POST", ourRequestMethod); + assertEquals(1, CAPTURE_SERVLET.ourRequestHeaders.get(Constants.HEADER_CONTENT_TYPE).size()); + assertEquals(EncodingEnum.XML.getResourceContentType() + Constants.HEADER_SUFFIX_CT_UTF_8, CAPTURE_SERVLET.ourRequestFirstHeaders.get(Constants.HEADER_CONTENT_TYPE).getValue().replace(";char", "; char")); + assertThat(CAPTURE_SERVLET.ourRequestBodyString, containsString("")); + assertEquals(ourServer.getBaseUrl() + "/fhir/Patient", CAPTURE_SERVLET.ourRequestUri); + assertEquals("POST", CAPTURE_SERVLET.ourRequestMethod); p.setId("123"); client.create().resource(p).encodedXml().execute(); - assertEquals(1, ourRequestHeaders.get(Constants.HEADER_CONTENT_TYPE).size()); - assertEquals(EncodingEnum.XML.getResourceContentType() + Constants.HEADER_SUFFIX_CT_UTF_8, ourRequestFirstHeaders.get(Constants.HEADER_CONTENT_TYPE).getValue().replace(";char", "; char")); - String body = ourRequestBodyString; + assertEquals(1, CAPTURE_SERVLET.ourRequestHeaders.get(Constants.HEADER_CONTENT_TYPE).size()); + assertEquals(EncodingEnum.XML.getResourceContentType() + Constants.HEADER_SUFFIX_CT_UTF_8, CAPTURE_SERVLET.ourRequestFirstHeaders.get(Constants.HEADER_CONTENT_TYPE).getValue().replace(";char", "; char")); + String body = CAPTURE_SERVLET.ourRequestBodyString; assertThat(body, containsString("")); assertThat(body, not(containsString("123"))); - assertEquals("http://localhost:" + ourPort + "/fhir/Patient", ourRequestUri); - assertEquals("POST", ourRequestMethod); + assertEquals(ourServer.getBaseUrl() + "/fhir/Patient", CAPTURE_SERVLET.ourRequestUri); + assertEquals("POST", CAPTURE_SERVLET.ourRequestMethod); } @@ -287,39 +267,39 @@ public class GenericJaxRsClientDstu2Test { public void testCreateConditional() { - ourResponseStatus = Constants.STATUS_HTTP_204_NO_CONTENT; + CAPTURE_SERVLET.ourResponseStatus = Constants.STATUS_HTTP_204_NO_CONTENT; - IGenericClient client = ourCtx.newRestfulGenericClient("http://localhost:" + ourPort + "/fhir"); + IGenericClient client = ourCtx.newRestfulGenericClient(ourServer.getBaseUrl() + "/fhir"); Patient p = new Patient(); p.addName().addFamily("FOOFAMILY"); client.create().resource(p).conditionalByUrl("Patient?name=foo").encodedXml().execute(); - assertEquals(1, ourRequestHeaders.get(Constants.HEADER_CONTENT_TYPE).size()); - assertEquals(EncodingEnum.XML.getResourceContentType() + Constants.HEADER_SUFFIX_CT_UTF_8, ourRequestFirstHeaders.get(Constants.HEADER_CONTENT_TYPE).getValue().replace(";char", "; char")); - assertThat(ourRequestBodyString, containsString("")); - assertEquals("http://localhost:" + ourPort + "/fhir/Patient", ourRequestUri); - assertEquals("http://localhost:" + ourPort + "/fhir/Patient?name=foo", ourRequestFirstHeaders.get(Constants.HEADER_IF_NONE_EXIST).getValue()); - assertEquals("POST", ourRequestMethod); + assertEquals(1, CAPTURE_SERVLET.ourRequestHeaders.get(Constants.HEADER_CONTENT_TYPE).size()); + assertEquals(EncodingEnum.XML.getResourceContentType() + Constants.HEADER_SUFFIX_CT_UTF_8, CAPTURE_SERVLET.ourRequestFirstHeaders.get(Constants.HEADER_CONTENT_TYPE).getValue().replace(";char", "; char")); + assertThat(CAPTURE_SERVLET.ourRequestBodyString, containsString("")); + assertEquals(ourServer.getBaseUrl() + "/fhir/Patient", CAPTURE_SERVLET.ourRequestUri); + assertEquals(ourServer.getBaseUrl() + "/fhir/Patient?name=foo", CAPTURE_SERVLET.ourRequestFirstHeaders.get(Constants.HEADER_IF_NONE_EXIST).getValue()); + assertEquals("POST", CAPTURE_SERVLET.ourRequestMethod); client.create().resource(p).conditionalByUrl("Patient?name=http://foo|bar").encodedXml().execute(); - assertEquals(1, ourRequestHeaders.get(Constants.HEADER_CONTENT_TYPE).size()); - assertEquals(EncodingEnum.XML.getResourceContentType() + Constants.HEADER_SUFFIX_CT_UTF_8, ourRequestFirstHeaders.get(Constants.HEADER_CONTENT_TYPE).getValue().replace(";char", "; char")); - assertThat(ourRequestBodyString, containsString("")); - assertEquals("http://localhost:" + ourPort + "/fhir/Patient", ourRequestUri); - assertEquals("http://localhost:" + ourPort + "/fhir/Patient?name=http%3A//foo%7Cbar", ourRequestFirstHeaders.get(Constants.HEADER_IF_NONE_EXIST).getValue()); - assertEquals("POST", ourRequestMethod); + assertEquals(1, CAPTURE_SERVLET.ourRequestHeaders.get(Constants.HEADER_CONTENT_TYPE).size()); + assertEquals(EncodingEnum.XML.getResourceContentType() + Constants.HEADER_SUFFIX_CT_UTF_8, CAPTURE_SERVLET.ourRequestFirstHeaders.get(Constants.HEADER_CONTENT_TYPE).getValue().replace(";char", "; char")); + assertThat(CAPTURE_SERVLET.ourRequestBodyString, containsString("")); + assertEquals(ourServer.getBaseUrl() + "/fhir/Patient", CAPTURE_SERVLET.ourRequestUri); + assertEquals(ourServer.getBaseUrl() + "/fhir/Patient?name=http%3A//foo%7Cbar", CAPTURE_SERVLET.ourRequestFirstHeaders.get(Constants.HEADER_IF_NONE_EXIST).getValue()); + assertEquals("POST", CAPTURE_SERVLET.ourRequestMethod); client.create().resource(p).conditional().where(Patient.NAME.matches().value("foo")).encodedXml().execute(); - assertEquals(1, ourRequestHeaders.get(Constants.HEADER_CONTENT_TYPE).size()); - assertEquals(EncodingEnum.XML.getResourceContentType() + Constants.HEADER_SUFFIX_CT_UTF_8, ourRequestFirstHeaders.get(Constants.HEADER_CONTENT_TYPE).getValue().replace(";char", "; char")); - assertThat(ourRequestBodyString, containsString("")); - assertEquals("http://localhost:" + ourPort + "/fhir/Patient", ourRequestUri); - assertEquals("http://localhost:" + ourPort + "/fhir/Patient?name=foo", ourRequestFirstHeaders.get(Constants.HEADER_IF_NONE_EXIST).getValue()); - assertEquals("POST", ourRequestMethod); + assertEquals(1, CAPTURE_SERVLET.ourRequestHeaders.get(Constants.HEADER_CONTENT_TYPE).size()); + assertEquals(EncodingEnum.XML.getResourceContentType() + Constants.HEADER_SUFFIX_CT_UTF_8, CAPTURE_SERVLET.ourRequestFirstHeaders.get(Constants.HEADER_CONTENT_TYPE).getValue().replace(";char", "; char")); + assertThat(CAPTURE_SERVLET.ourRequestBodyString, containsString("")); + assertEquals(ourServer.getBaseUrl() + "/fhir/Patient", CAPTURE_SERVLET.ourRequestUri); + assertEquals(ourServer.getBaseUrl() + "/fhir/Patient?name=foo", CAPTURE_SERVLET.ourRequestFirstHeaders.get(Constants.HEADER_IF_NONE_EXIST).getValue()); + assertEquals("POST", CAPTURE_SERVLET.ourRequestMethod); } @@ -328,22 +308,22 @@ public class GenericJaxRsClientDstu2Test { public void testCreatePrefer() { - ourResponseStatus = Constants.STATUS_HTTP_204_NO_CONTENT; + CAPTURE_SERVLET.ourResponseStatus = Constants.STATUS_HTTP_204_NO_CONTENT; - IGenericClient client = ourCtx.newRestfulGenericClient("http://localhost:" + ourPort + "/fhir"); + IGenericClient client = ourCtx.newRestfulGenericClient(ourServer.getBaseUrl() + "/fhir"); Patient p = new Patient(); p.addName().addFamily("FOOFAMILY"); client.create().resource(p).prefer(PreferReturnEnum.MINIMAL).execute(); - assertEquals(1, ourRequestHeaders.get(Constants.HEADER_PREFER).size()); - assertEquals(Constants.HEADER_PREFER_RETURN + '=' + Constants.HEADER_PREFER_RETURN_MINIMAL, ourRequestHeaders.get(Constants.HEADER_PREFER).get(0).getValue()); + assertEquals(1, CAPTURE_SERVLET.ourRequestHeaders.get(Constants.HEADER_PREFER).size()); + assertEquals(Constants.HEADER_PREFER_RETURN + '=' + Constants.HEADER_PREFER_RETURN_MINIMAL, CAPTURE_SERVLET.ourRequestHeaders.get(Constants.HEADER_PREFER).get(0).getValue()); client.create().resource(p).prefer(PreferReturnEnum.REPRESENTATION).execute(); - assertEquals(1, ourRequestHeaders.get(Constants.HEADER_PREFER).size()); - assertEquals(Constants.HEADER_PREFER_RETURN + '=' + Constants.HEADER_PREFER_RETURN_REPRESENTATION, ourRequestHeaders.get(Constants.HEADER_PREFER).get(0).getValue()); + assertEquals(1, CAPTURE_SERVLET.ourRequestHeaders.get(Constants.HEADER_PREFER).size()); + assertEquals(Constants.HEADER_PREFER_RETURN + '=' + Constants.HEADER_PREFER_RETURN_REPRESENTATION, CAPTURE_SERVLET.ourRequestHeaders.get(Constants.HEADER_PREFER).get(0).getValue()); } @@ -355,11 +335,11 @@ public class GenericJaxRsClientDstu2Test { final String formatted = ourCtx.newXmlParser().encodeResourceToString(p); - ourResponseStatus = Constants.STATUS_HTTP_200_OK; - ourResponseContentType = Constants.CT_FHIR_XML + "; charset=UTF-8"; - ourResponseBody = formatted; + CAPTURE_SERVLET.ourResponseStatus = Constants.STATUS_HTTP_200_OK; + CAPTURE_SERVLET.ourResponseContentType = Constants.CT_FHIR_XML + "; charset=UTF-8"; + CAPTURE_SERVLET.ourResponseBody = formatted; - IGenericClient client = ourCtx.newRestfulGenericClient("http://localhost:" + ourPort + "/fhir"); + IGenericClient client = ourCtx.newRestfulGenericClient(ourServer.getBaseUrl() + "/fhir"); p = new Patient(); p.setId(new IdDt("1")); @@ -372,38 +352,38 @@ public class GenericJaxRsClientDstu2Test { @Test public void testDeleteConditional() { - ourResponseStatus = Constants.STATUS_HTTP_204_NO_CONTENT; + CAPTURE_SERVLET.ourResponseStatus = Constants.STATUS_HTTP_204_NO_CONTENT; - IGenericClient client = ourCtx.newRestfulGenericClient("http://localhost:" + ourPort + "/fhir"); + IGenericClient client = ourCtx.newRestfulGenericClient(ourServer.getBaseUrl() + "/fhir"); client.delete().resourceById(new IdDt("Patient/123")).execute(); - assertEquals("DELETE", ourRequestMethod); - assertEquals("http://localhost:" + ourPort + "/fhir/Patient/123", ourRequestUri); + assertEquals("DELETE", CAPTURE_SERVLET.ourRequestMethod); + assertEquals(ourServer.getBaseUrl() + "/fhir/Patient/123", CAPTURE_SERVLET.ourRequestUri); client.delete().resourceConditionalByUrl("Patient?name=foo").execute(); - assertEquals("DELETE", ourRequestMethod); - assertEquals("http://localhost:" + ourPort + "/fhir/Patient?name=foo", ourRequestUri); + assertEquals("DELETE", CAPTURE_SERVLET.ourRequestMethod); + assertEquals(ourServer.getBaseUrl() + "/fhir/Patient?name=foo", CAPTURE_SERVLET.ourRequestUri); client.delete().resourceConditionalByType("Patient").where(Patient.NAME.matches().value("foo")).execute(); - assertEquals("DELETE", ourRequestMethod); - assertEquals("http://localhost:" + ourPort + "/fhir/Patient?name=foo", ourRequestUri); + assertEquals("DELETE", CAPTURE_SERVLET.ourRequestMethod); + assertEquals(ourServer.getBaseUrl() + "/fhir/Patient?name=foo", CAPTURE_SERVLET.ourRequestUri); } @Test public void testDelete() { - ourResponseStatus = Constants.STATUS_HTTP_204_NO_CONTENT; + CAPTURE_SERVLET.ourResponseStatus = Constants.STATUS_HTTP_204_NO_CONTENT; - IGenericClient client = ourCtx.newRestfulGenericClient("http://localhost:" + ourPort + "/fhir"); + IGenericClient client = ourCtx.newRestfulGenericClient(ourServer.getBaseUrl() + "/fhir"); client.delete().resourceById(new IdDt("Patient/123")).execute(); - assertEquals("DELETE", ourRequestMethod); - assertEquals("http://localhost:" + ourPort + "/fhir/Patient/123", ourRequestUri); + assertEquals("DELETE", CAPTURE_SERVLET.ourRequestMethod); + assertEquals(ourServer.getBaseUrl() + "/fhir/Patient/123", CAPTURE_SERVLET.ourRequestUri); } @@ -412,10 +392,10 @@ public class GenericJaxRsClientDstu2Test { final String msg = getPatientFeedWithOneResult(); - ourResponseContentType = Constants.CT_FHIR_XML + "; charset=UTF-8"; - ourResponseBody = msg; + CAPTURE_SERVLET.ourResponseContentType = Constants.CT_FHIR_XML + "; charset=UTF-8"; + CAPTURE_SERVLET.ourResponseBody = msg; - IGenericClient client = ourCtx.newRestfulGenericClient("http://localhost:" + ourPort + "/fhir"); + IGenericClient client = ourCtx.newRestfulGenericClient(ourServer.getBaseUrl() + "/fhir"); ca.uhn.fhir.model.dstu2.resource.Bundle response; @@ -427,7 +407,7 @@ public class GenericJaxRsClientDstu2Test { .andReturnBundle(ca.uhn.fhir.model.dstu2.resource.Bundle.class) .execute(); - assertEquals("http://localhost:" + ourPort + "/fhir/_history", ourRequestUri); + assertEquals(ourServer.getBaseUrl() + "/fhir/_history", CAPTURE_SERVLET.ourRequestUri); assertEquals(1, response.getEntry().size()); @@ -439,7 +419,7 @@ public class GenericJaxRsClientDstu2Test { .count(null) .execute(); - assertEquals("http://localhost:" + ourPort + "/fhir/_history", ourRequestUri); + assertEquals(ourServer.getBaseUrl() + "/fhir/_history", CAPTURE_SERVLET.ourRequestUri); assertEquals(1, response.getEntry().size()); @@ -450,7 +430,7 @@ public class GenericJaxRsClientDstu2Test { .since(new InstantDt()) .execute(); - assertEquals("http://localhost:" + ourPort + "/fhir/_history", ourRequestUri); + assertEquals(ourServer.getBaseUrl() + "/fhir/_history", CAPTURE_SERVLET.ourRequestUri); assertEquals(1, response.getEntry().size()); @@ -460,7 +440,7 @@ public class GenericJaxRsClientDstu2Test { .andReturnBundle(ca.uhn.fhir.model.dstu2.resource.Bundle.class) .execute(); - assertEquals("http://localhost:" + ourPort + "/fhir/Patient/_history", ourRequestUri); + assertEquals(ourServer.getBaseUrl() + "/fhir/Patient/_history", CAPTURE_SERVLET.ourRequestUri); assertEquals(1, response.getEntry().size()); @@ -470,7 +450,7 @@ public class GenericJaxRsClientDstu2Test { .andReturnBundle(ca.uhn.fhir.model.dstu2.resource.Bundle.class) .execute(); - assertEquals("http://localhost:" + ourPort + "/fhir/Patient/123/_history", ourRequestUri); + assertEquals(ourServer.getBaseUrl() + "/fhir/Patient/123/_history", CAPTURE_SERVLET.ourRequestUri); assertEquals(1, response.getEntry().size()); @@ -482,7 +462,7 @@ public class GenericJaxRsClientDstu2Test { .since(new InstantDt("2001-01-02T11:22:33Z")) .execute(); - assertThat(ourRequestUri, either(equalTo("http://localhost:" + ourPort + "/fhir/Patient/123/_history?_since=2001-01-02T11:22:33Z&_count=123")).or(equalTo("http://localhost:" + ourPort + "/fhir/Patient/123/_history?_count=123&_since=2001-01-02T11:22:33Z"))); + assertThat(CAPTURE_SERVLET.ourRequestUri, either(equalTo(ourServer.getBaseUrl() + "/fhir/Patient/123/_history?_since=2001-01-02T11:22:33Z&_count=123")).or(equalTo(ourServer.getBaseUrl() + "/fhir/Patient/123/_history?_count=123&_since=2001-01-02T11:22:33Z"))); assertEquals(1, response.getEntry().size()); @@ -493,7 +473,7 @@ public class GenericJaxRsClientDstu2Test { .since(new InstantDt("2001-01-02T11:22:33Z").getValue()) .execute(); - assertThat(ourRequestUri, containsString("_since=2001-01")); + assertThat(CAPTURE_SERVLET.ourRequestUri, containsString("_since=2001-01")); assertEquals(1, response.getEntry().size()); } @@ -508,10 +488,10 @@ public class GenericJaxRsClientDstu2Test { outParams.addParameter().setName("meta").setValue(new MetaDt().addProfile("urn:profile:out")); final String respString = p.encodeResourceToString(outParams); - ourResponseContentType = Constants.CT_FHIR_XML + "; charset=UTF-8"; - ourResponseBody = respString; + CAPTURE_SERVLET.ourResponseContentType = Constants.CT_FHIR_XML + "; charset=UTF-8"; + CAPTURE_SERVLET.ourResponseBody = respString; - IGenericClient client = ourCtx.newRestfulGenericClient("http://localhost:" + ourPort + "/fhir"); + IGenericClient client = ourCtx.newRestfulGenericClient(ourServer.getBaseUrl() + "/fhir"); MetaDt resp = client @@ -522,10 +502,10 @@ public class GenericJaxRsClientDstu2Test { .encodedXml() .execute(); - assertEquals("http://localhost:" + ourPort + "/fhir/Patient/123/$meta-add", ourRequestUri); + assertEquals(ourServer.getBaseUrl() + "/fhir/Patient/123/$meta-add", CAPTURE_SERVLET.ourRequestUri); assertEquals("urn:profile:out", resp.getProfile().get(0).getValue()); - assertEquals("POST", ourRequestMethod); - assertEquals("", ourRequestBodyString); + assertEquals("POST", CAPTURE_SERVLET.ourRequestMethod); + assertEquals("", CAPTURE_SERVLET.ourRequestBodyString); } @@ -541,10 +521,10 @@ public class GenericJaxRsClientDstu2Test { outParams.addParameter().setName("meta").setValue(new MetaDt().addProfile("urn:profile:out")); final String respString = p.encodeResourceToString(outParams); - ourResponseContentType = Constants.CT_FHIR_XML + "; charset=UTF-8"; - ourResponseBody = respString; + CAPTURE_SERVLET.ourResponseContentType = Constants.CT_FHIR_XML + "; charset=UTF-8"; + CAPTURE_SERVLET.ourResponseBody = respString; - IGenericClient client = ourCtx.newRestfulGenericClient("http://localhost:" + ourPort + "/fhir"); + IGenericClient client = ourCtx.newRestfulGenericClient(ourServer.getBaseUrl() + "/fhir"); MetaDt resp = client @@ -553,9 +533,9 @@ public class GenericJaxRsClientDstu2Test { .fromServer() .execute(); - assertEquals("http://localhost:" + ourPort + "/fhir/$meta", ourRequestUri); + assertEquals(ourServer.getBaseUrl() + "/fhir/$meta", CAPTURE_SERVLET.ourRequestUri); assertEquals("urn:profile:out", resp.getProfile().get(0).getValue()); - assertEquals("GET", ourRequestMethod); + assertEquals("GET", CAPTURE_SERVLET.ourRequestMethod); resp = client @@ -564,9 +544,9 @@ public class GenericJaxRsClientDstu2Test { .fromType("Patient") .execute(); - assertEquals("http://localhost:" + ourPort + "/fhir/Patient/$meta", ourRequestUri); + assertEquals(ourServer.getBaseUrl() + "/fhir/Patient/$meta", CAPTURE_SERVLET.ourRequestUri); assertEquals("urn:profile:out", resp.getProfile().get(0).getValue()); - assertEquals("GET", ourRequestMethod); + assertEquals("GET", CAPTURE_SERVLET.ourRequestMethod); resp = client @@ -575,9 +555,9 @@ public class GenericJaxRsClientDstu2Test { .fromResource(new IdDt("Patient/123")) .execute(); - assertEquals("http://localhost:" + ourPort + "/fhir/Patient/123/$meta", ourRequestUri); + assertEquals(ourServer.getBaseUrl() + "/fhir/Patient/123/$meta", CAPTURE_SERVLET.ourRequestUri); assertEquals("urn:profile:out", resp.getProfile().get(0).getValue()); - assertEquals("GET", ourRequestMethod); + assertEquals("GET", CAPTURE_SERVLET.ourRequestMethod); } @@ -597,10 +577,10 @@ public class GenericJaxRsClientDstu2Test { final String respString = p.encodeResourceToString(outParams); - ourResponseContentType = Constants.CT_FHIR_XML + "; charset=UTF-8"; - ourResponseBody = respString; + CAPTURE_SERVLET.ourResponseContentType = Constants.CT_FHIR_XML + "; charset=UTF-8"; + CAPTURE_SERVLET.ourResponseBody = respString; - IGenericClient client = ourCtx.newRestfulGenericClient("http://localhost:" + ourPort + "/fhir"); + IGenericClient client = ourCtx.newRestfulGenericClient(ourServer.getBaseUrl() + "/fhir"); Parameters resp = client @@ -611,9 +591,9 @@ public class GenericJaxRsClientDstu2Test { .useHttpGet() .execute(); - assertEquals("http://localhost:" + ourPort + "/fhir/$SOMEOPERATION?param1=STRINGVALIN1¶m1=STRINGVALIN1b¶m2=STRINGVALIN2", ourRequestUri); + assertEquals(ourServer.getBaseUrl() + "/fhir/$SOMEOPERATION?param1=STRINGVALIN1¶m1=STRINGVALIN1b¶m2=STRINGVALIN2", CAPTURE_SERVLET.ourRequestUri); assertEquals(respString, p.encodeResourceToString(resp)); - assertEquals("GET", ourRequestMethod); + assertEquals("GET", CAPTURE_SERVLET.ourRequestMethod); resp = client @@ -624,9 +604,9 @@ public class GenericJaxRsClientDstu2Test { .useHttpGet() .execute(); - assertEquals("http://localhost:" + ourPort + "/fhir/Patient/$SOMEOPERATION?param1=STRINGVALIN1¶m1=STRINGVALIN1b¶m2=STRINGVALIN2", ourRequestUri); + assertEquals(ourServer.getBaseUrl() + "/fhir/Patient/$SOMEOPERATION?param1=STRINGVALIN1¶m1=STRINGVALIN1b¶m2=STRINGVALIN2", CAPTURE_SERVLET.ourRequestUri); assertEquals(respString, p.encodeResourceToString(resp)); - assertEquals("GET", ourRequestMethod); + assertEquals("GET", CAPTURE_SERVLET.ourRequestMethod); resp = client @@ -637,9 +617,9 @@ public class GenericJaxRsClientDstu2Test { .useHttpGet() .execute(); - assertEquals("http://localhost:" + ourPort + "/fhir/Patient/123/$SOMEOPERATION?param1=STRINGVALIN1¶m1=STRINGVALIN1b¶m2=STRINGVALIN2", ourRequestUri); + assertEquals(ourServer.getBaseUrl() + "/fhir/Patient/123/$SOMEOPERATION?param1=STRINGVALIN1¶m1=STRINGVALIN1b¶m2=STRINGVALIN2", CAPTURE_SERVLET.ourRequestUri); assertEquals(respString, p.encodeResourceToString(resp)); - assertEquals("GET", ourRequestMethod); + assertEquals("GET", CAPTURE_SERVLET.ourRequestMethod); // @formatter:off @@ -651,7 +631,7 @@ public class GenericJaxRsClientDstu2Test { .useHttpGet() .execute(); // @formatter:on - assertEquals("http://localhost:" + ourPort + "/fhir/Patient/123/$SOMEOPERATION?param1=STRINGVALIN1¶m1=STRINGVALIN1b¶m2=STRINGVALIN2", ourRequestUri); + assertEquals(ourServer.getBaseUrl() + "/fhir/Patient/123/$SOMEOPERATION?param1=STRINGVALIN1¶m1=STRINGVALIN1b¶m2=STRINGVALIN2", CAPTURE_SERVLET.ourRequestUri); } @@ -664,10 +644,10 @@ public class GenericJaxRsClientDstu2Test { outParams.addParameter().setValue(new StringDt("STRINGVALOUT2")); final String respString = p.encodeResourceToString(outParams); - ourResponseContentType = Constants.CT_FHIR_XML + "; charset=UTF-8"; - ourResponseBody = respString; + CAPTURE_SERVLET.ourResponseContentType = Constants.CT_FHIR_XML + "; charset=UTF-8"; + CAPTURE_SERVLET.ourResponseBody = respString; - IGenericClient client = ourCtx.newRestfulGenericClient("http://localhost:" + ourPort + "/fhir"); + IGenericClient client = ourCtx.newRestfulGenericClient(ourServer.getBaseUrl() + "/fhir"); Parameters resp = client @@ -678,9 +658,9 @@ public class GenericJaxRsClientDstu2Test { .useHttpGet() .execute(); - assertEquals("http://localhost:" + ourPort + "/fhir/$SOMEOPERATION", ourRequestUri); + assertEquals(ourServer.getBaseUrl() + "/fhir/$SOMEOPERATION", CAPTURE_SERVLET.ourRequestUri); assertEquals(respString, p.encodeResourceToString(resp)); - assertEquals("GET", ourRequestMethod); + assertEquals("GET", CAPTURE_SERVLET.ourRequestMethod); resp = client @@ -691,9 +671,9 @@ public class GenericJaxRsClientDstu2Test { .useHttpGet() .execute(); - assertEquals("http://localhost:" + ourPort + "/fhir/Patient/$SOMEOPERATION", ourRequestUri); + assertEquals(ourServer.getBaseUrl() + "/fhir/Patient/$SOMEOPERATION", CAPTURE_SERVLET.ourRequestUri); assertEquals(respString, p.encodeResourceToString(resp)); - assertEquals("GET", ourRequestMethod); + assertEquals("GET", CAPTURE_SERVLET.ourRequestMethod); resp = client @@ -704,9 +684,9 @@ public class GenericJaxRsClientDstu2Test { .useHttpGet() .execute(); - assertEquals("http://localhost:" + ourPort + "/fhir/Patient/123/$SOMEOPERATION", ourRequestUri); + assertEquals(ourServer.getBaseUrl() + "/fhir/Patient/123/$SOMEOPERATION", CAPTURE_SERVLET.ourRequestUri); assertEquals(respString, p.encodeResourceToString(resp)); - assertEquals("GET", ourRequestMethod); + assertEquals("GET", CAPTURE_SERVLET.ourRequestMethod); // @formatter:off @@ -718,18 +698,18 @@ public class GenericJaxRsClientDstu2Test { .useHttpGet() .execute(); // @formatter:on - assertEquals("http://localhost:" + ourPort + "/fhir/Patient/123/$SOMEOPERATION", ourRequestUri); + assertEquals(ourServer.getBaseUrl() + "/fhir/Patient/123/$SOMEOPERATION", CAPTURE_SERVLET.ourRequestUri); } @Test public void testOperationWithBundleResponseJson() { - ourResponseContentType = Constants.CT_FHIR_JSON; - final String respString = "{\n" + " \"resourceType\":\"Bundle\",\n" + " \"id\":\"8cef5f2a-0ba9-43a5-be26-c8dde9ff0e19\",\n" + " \"base\":\"http://localhost:" + ourPort + "/fhir\"\n" + "}"; - ourResponseBody = respString; + CAPTURE_SERVLET.ourResponseContentType = Constants.CT_FHIR_JSON; + final String respString = "{\n" + " \"resourceType\":\"Bundle\",\n" + " \"id\":\"8cef5f2a-0ba9-43a5-be26-c8dde9ff0e19\",\n" + " \"base\":\"" + ourServer.getBaseUrl() + "/fhir\"\n" + "}"; + CAPTURE_SERVLET.ourResponseBody = respString; - IGenericClient client = ourCtx.newRestfulGenericClient("http://localhost:" + ourPort + "/fhir"); + IGenericClient client = ourCtx.newRestfulGenericClient(ourServer.getBaseUrl() + "/fhir"); client.registerInterceptor(new LoggingInterceptor(true)); @@ -765,10 +745,10 @@ public class GenericJaxRsClientDstu2Test { outParams.setTotal(123); final String respString = p.encodeResourceToString(outParams); - ourResponseContentType = Constants.CT_FHIR_XML + "; charset=UTF-8"; - ourResponseBody = respString; + CAPTURE_SERVLET.ourResponseContentType = Constants.CT_FHIR_XML + "; charset=UTF-8"; + CAPTURE_SERVLET.ourResponseBody = respString; - IGenericClient client = ourCtx.newRestfulGenericClient("http://localhost:" + ourPort + "/fhir"); + IGenericClient client = ourCtx.newRestfulGenericClient(ourServer.getBaseUrl() + "/fhir"); Parameters resp = client @@ -777,11 +757,11 @@ public class GenericJaxRsClientDstu2Test { .named("$SOMEOPERATION") .withParameters(inParams).encodedXml().execute(); - assertEquals("http://localhost:" + ourPort + "/fhir/$SOMEOPERATION", ourRequestUri); - assertEquals(1, ourRequestHeaders.get(Constants.HEADER_CONTENT_TYPE).size()); - assertEquals(EncodingEnum.XML.getResourceContentType() + Constants.HEADER_SUFFIX_CT_UTF_8, ourRequestFirstHeaders.get(Constants.HEADER_CONTENT_TYPE).getValue().replace(";char", "; char")); - assertEquals(ourRequestBodyString, reqString); - assertEquals("POST", ourRequestMethod); + assertEquals(ourServer.getBaseUrl() + "/fhir/$SOMEOPERATION", CAPTURE_SERVLET.ourRequestUri); + assertEquals(1, CAPTURE_SERVLET.ourRequestHeaders.get(Constants.HEADER_CONTENT_TYPE).size()); + assertEquals(EncodingEnum.XML.getResourceContentType() + Constants.HEADER_SUFFIX_CT_UTF_8, CAPTURE_SERVLET.ourRequestFirstHeaders.get(Constants.HEADER_CONTENT_TYPE).getValue().replace(";char", "; char")); + assertEquals(CAPTURE_SERVLET.ourRequestBodyString, reqString); + assertEquals("POST", CAPTURE_SERVLET.ourRequestMethod); assertEquals(1, resp.getParameter().size()); assertEquals(ca.uhn.fhir.model.dstu2.resource.Bundle.class, resp.getParameter().get(0).getResource().getClass()); @@ -796,10 +776,10 @@ public class GenericJaxRsClientDstu2Test { outParams.addParameter().setValue(new StringDt("STRINGVALOUT2")); final String respString = p.encodeResourceToString(outParams); - ourResponseContentType = Constants.CT_FHIR_XML + "; charset=UTF-8"; - ourResponseBody = respString; + CAPTURE_SERVLET.ourResponseContentType = Constants.CT_FHIR_XML + "; charset=UTF-8"; + CAPTURE_SERVLET.ourResponseBody = respString; - IGenericClient client = ourCtx.newRestfulGenericClient("http://localhost:" + ourPort + "/fhir"); + IGenericClient client = ourCtx.newRestfulGenericClient(ourServer.getBaseUrl() + "/fhir"); Parameters resp = client @@ -811,12 +791,12 @@ public class GenericJaxRsClientDstu2Test { .encodedXml() .execute(); - assertEquals("http://localhost:" + ourPort + "/fhir/$SOMEOPERATION", ourRequestUri); + assertEquals(ourServer.getBaseUrl() + "/fhir/$SOMEOPERATION", CAPTURE_SERVLET.ourRequestUri); assertEquals(respString, p.encodeResourceToString(resp)); - assertEquals(1, ourRequestHeaders.get(Constants.HEADER_CONTENT_TYPE).size()); - assertEquals(EncodingEnum.XML.getResourceContentType() + Constants.HEADER_SUFFIX_CT_UTF_8, ourRequestFirstHeaders.get(Constants.HEADER_CONTENT_TYPE).getValue().replace(";char", "; char")); - assertEquals("POST", ourRequestMethod); - assertEquals("", (ourRequestBodyString)); + assertEquals(1, CAPTURE_SERVLET.ourRequestHeaders.get(Constants.HEADER_CONTENT_TYPE).size()); + assertEquals(EncodingEnum.XML.getResourceContentType() + Constants.HEADER_SUFFIX_CT_UTF_8, CAPTURE_SERVLET.ourRequestFirstHeaders.get(Constants.HEADER_CONTENT_TYPE).getValue().replace(";char", "; char")); + assertEquals("POST", CAPTURE_SERVLET.ourRequestMethod); + assertEquals("", (CAPTURE_SERVLET.ourRequestBodyString)); /* @@ -833,13 +813,13 @@ public class GenericJaxRsClientDstu2Test { .encodedXml() .execute(); - assertEquals("http://localhost:" + ourPort + "/fhir/$SOMEOPERATION", ourRequestUri); + assertEquals(ourServer.getBaseUrl() + "/fhir/$SOMEOPERATION", CAPTURE_SERVLET.ourRequestUri); assertEquals(respString, p.encodeResourceToString(resp)); - assertEquals(1, ourRequestHeaders.get(Constants.HEADER_CONTENT_TYPE).size()); - assertEquals(EncodingEnum.XML.getResourceContentType() + Constants.HEADER_SUFFIX_CT_UTF_8, ourRequestFirstHeaders.get(Constants.HEADER_CONTENT_TYPE).getValue().replace(";char", "; char")); - assertEquals("POST", ourRequestMethod); + assertEquals(1, CAPTURE_SERVLET.ourRequestHeaders.get(Constants.HEADER_CONTENT_TYPE).size()); + assertEquals(EncodingEnum.XML.getResourceContentType() + Constants.HEADER_SUFFIX_CT_UTF_8, CAPTURE_SERVLET.ourRequestFirstHeaders.get(Constants.HEADER_CONTENT_TYPE).getValue().replace(";char", "; char")); + assertEquals("POST", CAPTURE_SERVLET.ourRequestMethod); assertEquals("", - (ourRequestBodyString)); + (CAPTURE_SERVLET.ourRequestBodyString)); /* @@ -856,21 +836,21 @@ public class GenericJaxRsClientDstu2Test { .encodedXml() .execute(); - assertEquals("http://localhost:" + ourPort + "/fhir/$SOMEOPERATION", ourRequestUri); + assertEquals(ourServer.getBaseUrl() + "/fhir/$SOMEOPERATION", CAPTURE_SERVLET.ourRequestUri); assertEquals(respString, p.encodeResourceToString(resp)); - assertEquals(1, ourRequestHeaders.get(Constants.HEADER_CONTENT_TYPE).size()); - assertEquals(EncodingEnum.XML.getResourceContentType() + Constants.HEADER_SUFFIX_CT_UTF_8, ourRequestFirstHeaders.get(Constants.HEADER_CONTENT_TYPE).getValue().replace(";char", "; char")); - assertEquals("POST", ourRequestMethod); + assertEquals(1, CAPTURE_SERVLET.ourRequestHeaders.get(Constants.HEADER_CONTENT_TYPE).size()); + assertEquals(EncodingEnum.XML.getResourceContentType() + Constants.HEADER_SUFFIX_CT_UTF_8, CAPTURE_SERVLET.ourRequestFirstHeaders.get(Constants.HEADER_CONTENT_TYPE).getValue().replace(";char", "; char")); + assertEquals("POST", CAPTURE_SERVLET.ourRequestMethod); assertEquals( "", - (ourRequestBodyString)); + (CAPTURE_SERVLET.ourRequestBodyString)); } @Test public void testOperationWithInvalidParam() { - IGenericClient client = ourCtx.newRestfulGenericClient("http://localhost:" + ourPort + "/fhir"); + IGenericClient client = ourCtx.newRestfulGenericClient(ourServer.getBaseUrl() + "/fhir"); // Who knows what the heck this is! IBase weirdBase = new IBase() { @@ -928,10 +908,10 @@ public class GenericJaxRsClientDstu2Test { final String respString = p.encodeResourceToString(outParams); - ourResponseContentType = Constants.CT_FHIR_XML + "; charset=UTF-8"; - ourResponseBody = respString; + CAPTURE_SERVLET.ourResponseContentType = Constants.CT_FHIR_XML + "; charset=UTF-8"; + CAPTURE_SERVLET.ourResponseBody = respString; - IGenericClient client = ourCtx.newRestfulGenericClient("http://localhost:" + ourPort + "/fhir"); + IGenericClient client = ourCtx.newRestfulGenericClient(ourServer.getBaseUrl() + "/fhir"); client @@ -944,7 +924,7 @@ public class GenericJaxRsClientDstu2Test { .execute(); - assertEquals("http://localhost:" + ourPort + "/fhir/Patient/1/$validate-code?code=8495-4&system=http%3A%2F%2Floinc.org", ourRequestUri); + assertEquals(ourServer.getBaseUrl() + "/fhir/Patient/1/$validate-code?code=8495-4&system=http%3A%2F%2Floinc.org", CAPTURE_SERVLET.ourRequestUri); client @@ -958,9 +938,9 @@ public class GenericJaxRsClientDstu2Test { .execute(); - assertEquals("http://localhost:" + ourPort + "/fhir/Patient/1/$validate-code", ourRequestUri); - ourLog.info(ourRequestBodyString); - assertEquals("", ourRequestBodyString); + assertEquals(ourServer.getBaseUrl() + "/fhir/Patient/1/$validate-code", CAPTURE_SERVLET.ourRequestUri); + ourLog.info(CAPTURE_SERVLET.ourRequestBodyString); + assertEquals("", CAPTURE_SERVLET.ourRequestBodyString); } @@ -979,10 +959,10 @@ public class GenericJaxRsClientDstu2Test { final String respString = p.encodeResourceToString(outParams); - ourResponseContentType = Constants.CT_FHIR_XML + "; charset=UTF-8"; - ourResponseBody = respString; + CAPTURE_SERVLET.ourResponseContentType = Constants.CT_FHIR_XML + "; charset=UTF-8"; + CAPTURE_SERVLET.ourResponseBody = respString; - IGenericClient client = ourCtx.newRestfulGenericClient("http://localhost:" + ourPort + "/fhir"); + IGenericClient client = ourCtx.newRestfulGenericClient(ourServer.getBaseUrl() + "/fhir"); Parameters resp = client @@ -991,12 +971,12 @@ public class GenericJaxRsClientDstu2Test { .named("$SOMEOPERATION") .withParameters(inParams).encodedXml().execute(); - assertEquals("http://localhost:" + ourPort + "/fhir/$SOMEOPERATION", ourRequestUri); + assertEquals(ourServer.getBaseUrl() + "/fhir/$SOMEOPERATION", CAPTURE_SERVLET.ourRequestUri); assertEquals(respString, p.encodeResourceToString(resp)); - assertEquals(1, ourRequestHeaders.get(Constants.HEADER_CONTENT_TYPE).size()); - assertEquals(EncodingEnum.XML.getResourceContentType() + Constants.HEADER_SUFFIX_CT_UTF_8, ourRequestFirstHeaders.get(Constants.HEADER_CONTENT_TYPE).getValue().replace(";char", "; char")); - assertEquals(ourRequestBodyString, reqString); - assertEquals("POST", ourRequestMethod); + assertEquals(1, CAPTURE_SERVLET.ourRequestHeaders.get(Constants.HEADER_CONTENT_TYPE).size()); + assertEquals(EncodingEnum.XML.getResourceContentType() + Constants.HEADER_SUFFIX_CT_UTF_8, CAPTURE_SERVLET.ourRequestFirstHeaders.get(Constants.HEADER_CONTENT_TYPE).getValue().replace(";char", "; char")); + assertEquals(CAPTURE_SERVLET.ourRequestBodyString, reqString); + assertEquals("POST", CAPTURE_SERVLET.ourRequestMethod); resp = client @@ -1005,12 +985,12 @@ public class GenericJaxRsClientDstu2Test { .named("$SOMEOPERATION") .withParameters(inParams).encodedXml().execute(); - assertEquals("http://localhost:" + ourPort + "/fhir/Patient/$SOMEOPERATION", ourRequestUri); + assertEquals(ourServer.getBaseUrl() + "/fhir/Patient/$SOMEOPERATION", CAPTURE_SERVLET.ourRequestUri); assertEquals(respString, p.encodeResourceToString(resp)); - assertEquals(1, ourRequestHeaders.get(Constants.HEADER_CONTENT_TYPE).size()); - assertEquals(EncodingEnum.XML.getResourceContentType() + Constants.HEADER_SUFFIX_CT_UTF_8, ourRequestFirstHeaders.get(Constants.HEADER_CONTENT_TYPE).getValue().replace(";char", "; char")); - assertEquals(ourRequestBodyString, reqString); - assertEquals("POST", ourRequestMethod); + assertEquals(1, CAPTURE_SERVLET.ourRequestHeaders.get(Constants.HEADER_CONTENT_TYPE).size()); + assertEquals(EncodingEnum.XML.getResourceContentType() + Constants.HEADER_SUFFIX_CT_UTF_8, CAPTURE_SERVLET.ourRequestFirstHeaders.get(Constants.HEADER_CONTENT_TYPE).getValue().replace(";char", "; char")); + assertEquals(CAPTURE_SERVLET.ourRequestBodyString, reqString); + assertEquals("POST", CAPTURE_SERVLET.ourRequestMethod); resp = client @@ -1021,17 +1001,17 @@ public class GenericJaxRsClientDstu2Test { .encodedXml() .execute(); - assertEquals("http://localhost:" + ourPort + "/fhir/Patient/123/$SOMEOPERATION", ourRequestUri); + assertEquals(ourServer.getBaseUrl() + "/fhir/Patient/123/$SOMEOPERATION", CAPTURE_SERVLET.ourRequestUri); assertEquals(respString, p.encodeResourceToString(resp)); - assertEquals(1, ourRequestHeaders.get(Constants.HEADER_CONTENT_TYPE).size()); - assertEquals(EncodingEnum.XML.getResourceContentType() + Constants.HEADER_SUFFIX_CT_UTF_8, ourRequestFirstHeaders.get(Constants.HEADER_CONTENT_TYPE).getValue().replace(";char", "; char")); - assertEquals(ourRequestBodyString, reqString); - assertEquals("POST", ourRequestMethod); + assertEquals(1, CAPTURE_SERVLET.ourRequestHeaders.get(Constants.HEADER_CONTENT_TYPE).size()); + assertEquals(EncodingEnum.XML.getResourceContentType() + Constants.HEADER_SUFFIX_CT_UTF_8, CAPTURE_SERVLET.ourRequestFirstHeaders.get(Constants.HEADER_CONTENT_TYPE).getValue().replace(";char", "; char")); + assertEquals(CAPTURE_SERVLET.ourRequestBodyString, reqString); + assertEquals("POST", CAPTURE_SERVLET.ourRequestMethod); resp = client.operation().onInstance(new IdDt("http://foo.com/bar/baz/Patient/123/_history/22")).named("$SOMEOPERATION").withParameters(inParams).execute(); // @formatter:on - assertEquals("http://localhost:" + ourPort + "/fhir/Patient/123/$SOMEOPERATION", ourRequestUri); + assertEquals(ourServer.getBaseUrl() + "/fhir/Patient/123/$SOMEOPERATION", CAPTURE_SERVLET.ourRequestUri); } @@ -1048,10 +1028,10 @@ public class GenericJaxRsClientDstu2Test { final String respString = p.encodeResourceToString(outParams); - ourResponseContentType = Constants.CT_FHIR_XML + "; charset=UTF-8"; - ourResponseBody = respString; + CAPTURE_SERVLET.ourResponseContentType = Constants.CT_FHIR_XML + "; charset=UTF-8"; + CAPTURE_SERVLET.ourResponseBody = respString; - IGenericClient client = ourCtx.newRestfulGenericClient("http://localhost:" + ourPort + "/fhir"); + IGenericClient client = ourCtx.newRestfulGenericClient(ourServer.getBaseUrl() + "/fhir"); Parameters resp = client @@ -1060,12 +1040,12 @@ public class GenericJaxRsClientDstu2Test { .named("$SOMEOPERATION") .withNoParameters(Parameters.class).encodedXml().execute(); - assertEquals("http://localhost:" + ourPort + "/fhir/$SOMEOPERATION", ourRequestUri); + assertEquals(ourServer.getBaseUrl() + "/fhir/$SOMEOPERATION", CAPTURE_SERVLET.ourRequestUri); assertEquals(respString, p.encodeResourceToString(resp)); - assertEquals(1, ourRequestHeaders.get(Constants.HEADER_CONTENT_TYPE).size()); - assertEquals(EncodingEnum.XML.getResourceContentType() + Constants.HEADER_SUFFIX_CT_UTF_8, ourRequestFirstHeaders.get(Constants.HEADER_CONTENT_TYPE).getValue().replace(";char", "; char")); - assertEquals(ourRequestBodyString, reqString); - assertEquals("POST", ourRequestMethod); + assertEquals(1, CAPTURE_SERVLET.ourRequestHeaders.get(Constants.HEADER_CONTENT_TYPE).size()); + assertEquals(EncodingEnum.XML.getResourceContentType() + Constants.HEADER_SUFFIX_CT_UTF_8, CAPTURE_SERVLET.ourRequestFirstHeaders.get(Constants.HEADER_CONTENT_TYPE).getValue().replace(";char", "; char")); + assertEquals(CAPTURE_SERVLET.ourRequestBodyString, reqString); + assertEquals("POST", CAPTURE_SERVLET.ourRequestMethod); resp = client @@ -1074,12 +1054,12 @@ public class GenericJaxRsClientDstu2Test { .named("$SOMEOPERATION") .withNoParameters(Parameters.class).encodedXml().execute(); - assertEquals("http://localhost:" + ourPort + "/fhir/Patient/$SOMEOPERATION", ourRequestUri); + assertEquals(ourServer.getBaseUrl() + "/fhir/Patient/$SOMEOPERATION", CAPTURE_SERVLET.ourRequestUri); assertEquals(respString, p.encodeResourceToString(resp)); - assertEquals(1, ourRequestHeaders.get(Constants.HEADER_CONTENT_TYPE).size()); - assertEquals(EncodingEnum.XML.getResourceContentType() + Constants.HEADER_SUFFIX_CT_UTF_8, ourRequestFirstHeaders.get(Constants.HEADER_CONTENT_TYPE).getValue().replace(";char", "; char")); - assertEquals(ourRequestBodyString, reqString); - assertEquals("POST", ourRequestMethod); + assertEquals(1, CAPTURE_SERVLET.ourRequestHeaders.get(Constants.HEADER_CONTENT_TYPE).size()); + assertEquals(EncodingEnum.XML.getResourceContentType() + Constants.HEADER_SUFFIX_CT_UTF_8, CAPTURE_SERVLET.ourRequestFirstHeaders.get(Constants.HEADER_CONTENT_TYPE).getValue().replace(";char", "; char")); + assertEquals(CAPTURE_SERVLET.ourRequestBodyString, reqString); + assertEquals("POST", CAPTURE_SERVLET.ourRequestMethod); resp = client @@ -1088,12 +1068,12 @@ public class GenericJaxRsClientDstu2Test { .named("$SOMEOPERATION") .withNoParameters(Parameters.class).encodedXml().execute(); - assertEquals("http://localhost:" + ourPort + "/fhir/Patient/123/$SOMEOPERATION", ourRequestUri); + assertEquals(ourServer.getBaseUrl() + "/fhir/Patient/123/$SOMEOPERATION", CAPTURE_SERVLET.ourRequestUri); assertEquals(respString, p.encodeResourceToString(resp)); - assertEquals(1, ourRequestHeaders.get(Constants.HEADER_CONTENT_TYPE).size()); - assertEquals(EncodingEnum.XML.getResourceContentType() + Constants.HEADER_SUFFIX_CT_UTF_8, ourRequestFirstHeaders.get(Constants.HEADER_CONTENT_TYPE).getValue().replace(";char", "; char")); - assertEquals(ourRequestBodyString, reqString); - assertEquals("POST", ourRequestMethod); + assertEquals(1, CAPTURE_SERVLET.ourRequestHeaders.get(Constants.HEADER_CONTENT_TYPE).size()); + assertEquals(EncodingEnum.XML.getResourceContentType() + Constants.HEADER_SUFFIX_CT_UTF_8, CAPTURE_SERVLET.ourRequestFirstHeaders.get(Constants.HEADER_CONTENT_TYPE).getValue().replace(";char", "; char")); + assertEquals(CAPTURE_SERVLET.ourRequestBodyString, reqString); + assertEquals("POST", CAPTURE_SERVLET.ourRequestMethod); // @formatter:off @@ -1104,22 +1084,22 @@ public class GenericJaxRsClientDstu2Test { .withNoParameters(Parameters.class) .execute(); // @formatter:on - assertEquals("http://localhost:" + ourPort + "/fhir/Patient/123/$SOMEOPERATION", ourRequestUri); + assertEquals(ourServer.getBaseUrl() + "/fhir/Patient/123/$SOMEOPERATION", CAPTURE_SERVLET.ourRequestUri); } @Test public void testPageNext() { - ourResponseContentType = Constants.CT_FHIR_XML + "; charset=UTF-8"; - ourResponseBody = getPatientFeedWithOneResult(); + CAPTURE_SERVLET.ourResponseContentType = Constants.CT_FHIR_XML + "; charset=UTF-8"; + CAPTURE_SERVLET.ourResponseBody = getPatientFeedWithOneResult(); - IGenericClient client = ourCtx.newRestfulGenericClient("http://localhost:" + ourPort + "/fhir"); + IGenericClient client = ourCtx.newRestfulGenericClient(ourServer.getBaseUrl() + "/fhir"); ca.uhn.fhir.model.dstu2.resource.Bundle sourceBundle = new ca.uhn.fhir.model.dstu2.resource.Bundle(); - sourceBundle.getLinkOrCreate(IBaseBundle.LINK_PREV).setUrl("http://localhost:" + ourPort + "/fhir/prev"); - sourceBundle.getLinkOrCreate(IBaseBundle.LINK_NEXT).setUrl("http://localhost:" + ourPort + "/fhir/next"); + sourceBundle.getLinkOrCreate(IBaseBundle.LINK_PREV).setUrl(ourServer.getBaseUrl() + "/fhir/prev"); + sourceBundle.getLinkOrCreate(IBaseBundle.LINK_NEXT).setUrl(ourServer.getBaseUrl() + "/fhir/next"); ca.uhn.fhir.model.dstu2.resource.Bundle resp = client @@ -1129,14 +1109,14 @@ public class GenericJaxRsClientDstu2Test { assertEquals(1, resp.getEntry().size()); - assertEquals("http://localhost:" + ourPort + "/fhir/next", ourRequestUri); + assertEquals(ourServer.getBaseUrl() + "/fhir/next", CAPTURE_SERVLET.ourRequestUri); } @Test public void testPageNextNoLink() { - IGenericClient client = ourCtx.newRestfulGenericClient("http://localhost:" + ourPort + "/fhir"); + IGenericClient client = ourCtx.newRestfulGenericClient(ourServer.getBaseUrl() + "/fhir"); ca.uhn.fhir.model.dstu2.resource.Bundle sourceBundle = new ca.uhn.fhir.model.dstu2.resource.Bundle(); try { @@ -1150,14 +1130,14 @@ public class GenericJaxRsClientDstu2Test { public void testPagePrev() { - ourResponseContentType = Constants.CT_FHIR_XML + "; charset=UTF-8"; - ourResponseBody = getPatientFeedWithOneResult(); + CAPTURE_SERVLET.ourResponseContentType = Constants.CT_FHIR_XML + "; charset=UTF-8"; + CAPTURE_SERVLET.ourResponseBody = getPatientFeedWithOneResult(); - IGenericClient client = ourCtx.newRestfulGenericClient("http://localhost:" + ourPort + "/fhir"); + IGenericClient client = ourCtx.newRestfulGenericClient(ourServer.getBaseUrl() + "/fhir"); ca.uhn.fhir.model.dstu2.resource.Bundle sourceBundle = new ca.uhn.fhir.model.dstu2.resource.Bundle(); - sourceBundle.getLinkOrCreate("previous").setUrl("http://localhost:" + ourPort + "/fhir/prev"); + sourceBundle.getLinkOrCreate("previous").setUrl(ourServer.getBaseUrl() + "/fhir/prev"); ca.uhn.fhir.model.dstu2.resource.Bundle resp = client @@ -1167,7 +1147,7 @@ public class GenericJaxRsClientDstu2Test { assertEquals(1, resp.getEntry().size()); - assertEquals("http://localhost:" + ourPort + "/fhir/prev", ourRequestUri); + assertEquals(ourServer.getBaseUrl() + "/fhir/prev", CAPTURE_SERVLET.ourRequestUri); /* @@ -1175,7 +1155,7 @@ public class GenericJaxRsClientDstu2Test { */ sourceBundle = new ca.uhn.fhir.model.dstu2.resource.Bundle(); - sourceBundle.getLinkOrCreate("prev").setUrl("http://localhost:" + ourPort + "/fhir/prev"); + sourceBundle.getLinkOrCreate("prev").setUrl(ourServer.getBaseUrl() + "/fhir/prev"); resp = client @@ -1185,7 +1165,7 @@ public class GenericJaxRsClientDstu2Test { assertEquals(1, resp.getEntry().size()); - assertEquals("http://localhost:" + ourPort + "/fhir/prev", ourRequestUri); + assertEquals(ourServer.getBaseUrl() + "/fhir/prev", CAPTURE_SERVLET.ourRequestUri); } @@ -1197,16 +1177,16 @@ public class GenericJaxRsClientDstu2Test { patient.addName().addFamily("FAM"); final String respString = ourCtx.newXmlParser().encodeResourceToString(patient); - ourResponseContentType = Constants.CT_FHIR_XML + "; charset=UTF-8"; - ourResponseBody = respString; + CAPTURE_SERVLET.ourResponseContentType = Constants.CT_FHIR_XML + "; charset=UTF-8"; + CAPTURE_SERVLET.ourResponseBody = respString; - IGenericClient client = ourCtx.newRestfulGenericClient("http://localhost:" + ourPort + "/fhir"); + IGenericClient client = ourCtx.newRestfulGenericClient(ourServer.getBaseUrl() + "/fhir"); Patient response; - response = (Patient) client.read(new UriDt("http://localhost:" + ourPort + "/fhir/Patient/123")); - assertEquals("http://localhost:" + ourPort + "/fhir/Patient/123", ourRequestUri); + response = (Patient) client.read(new UriDt(ourServer.getBaseUrl() + "/fhir/Patient/123")); + assertEquals(ourServer.getBaseUrl() + "/fhir/Patient/123", CAPTURE_SERVLET.ourRequestUri); assertEquals("FAM", response.getName().get(0).getFamily().get(0).getValue()); } @@ -1217,15 +1197,15 @@ public class GenericJaxRsClientDstu2Test { patient.addName().addFamily("FAM"); final String respString = ourCtx.newXmlParser().encodeResourceToString(patient); - ourResponseContentType = Constants.CT_FHIR_XML + "; charset=UTF-8"; - ourResponseBody = respString; + CAPTURE_SERVLET.ourResponseContentType = Constants.CT_FHIR_XML + "; charset=UTF-8"; + CAPTURE_SERVLET.ourResponseBody = respString; - IGenericClient client = ourCtx.newRestfulGenericClient("http://localhost:" + ourPort + "/fhir"); + IGenericClient client = ourCtx.newRestfulGenericClient(ourServer.getBaseUrl() + "/fhir"); Patient response; - response = client.read().resource(Patient.class).withUrl(new IdDt("http://localhost:" + ourPort + "/AAA/Patient/123")).execute(); - assertEquals("http://localhost:" + ourPort + "/AAA/Patient/123", ourRequestUri); + response = client.read().resource(Patient.class).withUrl(new IdDt(ourServer.getBaseUrl() + "/AAA/Patient/123")).execute(); + assertEquals(ourServer.getBaseUrl() + "/AAA/Patient/123", CAPTURE_SERVLET.ourRequestUri); assertEquals("FAM", response.getName().get(0).getFamily().get(0).getValue()); } @@ -1248,10 +1228,10 @@ public class GenericJaxRsClientDstu2Test { ""; - ourResponseContentType = Constants.CT_FHIR_XML + "; charset=UTF-8"; - ourResponseBody = input; + CAPTURE_SERVLET.ourResponseContentType = Constants.CT_FHIR_XML + "; charset=UTF-8"; + CAPTURE_SERVLET.ourResponseBody = input; - IGenericClient client = ourCtx.newRestfulGenericClient("http://localhost:" + ourPort + "/fhir"); + IGenericClient client = ourCtx.newRestfulGenericClient(ourServer.getBaseUrl() + "/fhir"); ca.uhn.fhir.model.dstu2.resource.Bundle response; @@ -1269,10 +1249,10 @@ public class GenericJaxRsClientDstu2Test { String msg = "{\"resourceType\":\"Patient\",\"id\":\"1\",\"meta\":{\"versionId\":\"1\",\"lastUpdated\":\"2014-12-20T18:41:29.706-05:00\"},\"identifier\":[{\"system\":\"urn:MultiFhirVersionTest\",\"value\":\"testSubmitPatient01\"}]}"; - ourResponseContentType = Constants.CT_FHIR_JSON + "; charset=UTF-8"; - ourResponseBody = msg; + CAPTURE_SERVLET.ourResponseContentType = Constants.CT_FHIR_JSON + "; charset=UTF-8"; + CAPTURE_SERVLET.ourResponseBody = msg; - IGenericClient client = ourCtx.newRestfulGenericClient("http://localhost:" + ourPort + "/fhir"); + IGenericClient client = ourCtx.newRestfulGenericClient(ourServer.getBaseUrl() + "/fhir"); IBaseResource response = client.read() .resource("Patient") @@ -1280,7 +1260,7 @@ public class GenericJaxRsClientDstu2Test { .elementsSubset("name", "identifier") .execute(); - assertThat(ourRequestUri, either(equalTo("http://localhost:" + ourPort + "/fhir/Patient/123?_elements=name%2Cidentifier")).or(equalTo("http://localhost:" + ourPort + "/fhir/Patient/123?_elements=identifier%2Cname"))); + assertThat(CAPTURE_SERVLET.ourRequestUri, either(equalTo(ourServer.getBaseUrl() + "/fhir/Patient/123?_elements=name%2Cidentifier")).or(equalTo(ourServer.getBaseUrl() + "/fhir/Patient/123?_elements=identifier%2Cname"))); assertEquals(Patient.class, response.getClass()); } @@ -1290,11 +1270,11 @@ public class GenericJaxRsClientDstu2Test { String msg = "<>>>><<<<>"; - ourResponseContentType = Constants.CT_HTML + "; charset=UTF-8"; - ourResponseBody = msg; + CAPTURE_SERVLET.ourResponseContentType = Constants.CT_HTML + "; charset=UTF-8"; + CAPTURE_SERVLET.ourResponseBody = msg; - IGenericClient client = ourCtx.newRestfulGenericClient("http://localhost:" + ourPort + "/fhir"); + IGenericClient client = ourCtx.newRestfulGenericClient(ourServer.getBaseUrl() + "/fhir"); try { @@ -1314,11 +1294,11 @@ public class GenericJaxRsClientDstu2Test { String msg = "
    HELP IM A DIV
    "; - ourResponseContentType = Constants.CT_HTML + "; charset=UTF-8"; - ourResponseBody = msg; + CAPTURE_SERVLET.ourResponseContentType = Constants.CT_HTML + "; charset=UTF-8"; + CAPTURE_SERVLET.ourResponseBody = msg; - IGenericClient client = ourCtx.newRestfulGenericClient("http://localhost:" + ourPort + "/fhir"); + IGenericClient client = ourCtx.newRestfulGenericClient(ourServer.getBaseUrl() + "/fhir"); Patient response = client.read() .resource(Patient.class) @@ -1326,7 +1306,7 @@ public class GenericJaxRsClientDstu2Test { .summaryMode(SummaryEnum.TEXT) .execute(); - assertEquals("http://localhost:" + ourPort + "/fhir/Patient/123?_summary=text", ourRequestUri); + assertEquals(ourServer.getBaseUrl() + "/fhir/Patient/123?_summary=text", CAPTURE_SERVLET.ourRequestUri); assertEquals(Patient.class, response.getClass()); assertEquals("
    HELP IM A DIV
    ", response.getText().getDiv().getValueAsString()); @@ -1337,11 +1317,11 @@ public class GenericJaxRsClientDstu2Test { String msg = "{\"resourceType\":\"Bundle\",\"id\":null,\"base\":\"http://localhost:57931/fhir/contextDev\",\"total\":1,\"link\":[{\"relation\":\"self\",\"url\":\"http://localhost:57931/fhir/contextDev/Patient?identifier=urn%3AMultiFhirVersionTest%7CtestSubmitPatient01&_format=json\"}],\"entry\":[{\"resource\":{\"resourceType\":\"Patient\",\"id\":\"1\",\"meta\":{\"versionId\":\"1\",\"lastUpdated\":\"2014-12-20T18:41:29.706-05:00\"},\"identifier\":[{\"system\":\"urn:MultiFhirVersionTest\",\"value\":\"testSubmitPatient01\"}]}}]}"; - ourResponseContentType = Constants.CT_FHIR_JSON + "; charset=UTF-8"; - ourResponseBody = msg; + CAPTURE_SERVLET.ourResponseContentType = Constants.CT_FHIR_JSON + "; charset=UTF-8"; + CAPTURE_SERVLET.ourResponseBody = msg; - IGenericClient client = ourCtx.newRestfulGenericClient("http://localhost:" + ourPort + "/fhir"); + IGenericClient client = ourCtx.newRestfulGenericClient(ourServer.getBaseUrl() + "/fhir"); Bundle response = client.search() .forResource("Patient") @@ -1349,7 +1329,7 @@ public class GenericJaxRsClientDstu2Test { .returnBundle(Bundle.class) .execute(); - assertEquals("http://localhost:" + ourPort + "/fhir/Patient?name=james", ourRequestUri); + assertEquals(ourServer.getBaseUrl() + "/fhir/Patient?name=james", CAPTURE_SERVLET.ourRequestUri); assertEquals(Patient.class, response.getEntry().get(0).getResource().getClass()); } @@ -1360,19 +1340,19 @@ public class GenericJaxRsClientDstu2Test { final String msg = getPatientFeedWithOneResult(); - ourResponseContentType = Constants.CT_FHIR_XML + "; charset=UTF-8"; - ourResponseBody = msg; + CAPTURE_SERVLET.ourResponseContentType = Constants.CT_FHIR_XML + "; charset=UTF-8"; + CAPTURE_SERVLET.ourResponseBody = msg; - IGenericClient client = ourCtx.newRestfulGenericClient("http://localhost:" + ourPort + "/fhir"); + IGenericClient client = ourCtx.newRestfulGenericClient(ourServer.getBaseUrl() + "/fhir"); ca.uhn.fhir.model.dstu2.resource.Bundle response = client.search() - .byUrl("http://localhost:" + ourPort + "/AAA?name=http://foo|bar") + .byUrl(ourServer.getBaseUrl() + "/AAA?name=http://foo|bar") .encodedJson() .returnBundle(ca.uhn.fhir.model.dstu2.resource.Bundle.class) .execute(); - assertEquals("http://localhost:" + ourPort + "/AAA?name=http%3A//foo%7Cbar&_format=json", ourRequestUri); + assertEquals(ourServer.getBaseUrl() + "/AAA?name=http%3A//foo%7Cbar&_format=json", CAPTURE_SERVLET.ourRequestUri); assertNotNull(response); @@ -1382,7 +1362,7 @@ public class GenericJaxRsClientDstu2Test { .returnBundle(ca.uhn.fhir.model.dstu2.resource.Bundle.class) .execute(); - assertEquals("http://localhost:" + ourPort + "/fhir/Patient?name=http%3A//foo%7Cbar&_format=json", ourRequestUri); + assertEquals(ourServer.getBaseUrl() + "/fhir/Patient?name=http%3A//foo%7Cbar&_format=json", CAPTURE_SERVLET.ourRequestUri); assertNotNull(response); @@ -1392,7 +1372,7 @@ public class GenericJaxRsClientDstu2Test { .returnBundle(ca.uhn.fhir.model.dstu2.resource.Bundle.class) .execute(); - assertEquals("http://localhost:" + ourPort + "/fhir/Patient?name=http%3A//foo%7Cbar&_format=json", ourRequestUri); + assertEquals(ourServer.getBaseUrl() + "/fhir/Patient?name=http%3A//foo%7Cbar&_format=json", CAPTURE_SERVLET.ourRequestUri); assertNotNull(response); @@ -1401,7 +1381,7 @@ public class GenericJaxRsClientDstu2Test { .returnBundle(ca.uhn.fhir.model.dstu2.resource.Bundle.class) .execute(); - assertEquals("http://localhost:" + ourPort + "/fhir/Patient", ourRequestUri); + assertEquals(ourServer.getBaseUrl() + "/fhir/Patient", CAPTURE_SERVLET.ourRequestUri); assertNotNull(response); @@ -1410,7 +1390,7 @@ public class GenericJaxRsClientDstu2Test { .returnBundle(ca.uhn.fhir.model.dstu2.resource.Bundle.class) .execute(); - assertEquals("http://localhost:" + ourPort + "/fhir/Patient", ourRequestUri); + assertEquals(ourServer.getBaseUrl() + "/fhir/Patient", CAPTURE_SERVLET.ourRequestUri); assertNotNull(response); @@ -1429,11 +1409,11 @@ public class GenericJaxRsClientDstu2Test { String msg = IOUtils.toString(GenericJaxRsClientDstu2Test.class.getResourceAsStream("/bundle_orion.xml")); - ourResponseContentType = Constants.CT_FHIR_XML + "; charset=UTF-8"; - ourResponseBody = msg; + CAPTURE_SERVLET.ourResponseContentType = Constants.CT_FHIR_XML + "; charset=UTF-8"; + CAPTURE_SERVLET.ourResponseBody = msg; - IGenericClient client = ourCtx.newRestfulGenericClient("http://localhost:" + ourPort + "/fhir"); + IGenericClient client = ourCtx.newRestfulGenericClient(ourServer.getBaseUrl() + "/fhir"); ca.uhn.fhir.model.dstu2.resource.Bundle response = client.search() @@ -1458,11 +1438,11 @@ public class GenericJaxRsClientDstu2Test { String msg = "{\"resourceType\":\"Bundle\",\"id\":null,\"base\":\"http://localhost:57931/fhir/contextDev\",\"total\":1,\"link\":[{\"relation\":\"self\",\"url\":\"http://localhost:57931/fhir/contextDev/Patient?identifier=urn%3AMultiFhirVersionTest%7CtestSubmitPatient01&_format=json\"}],\"entry\":[{\"resource\":{\"resourceType\":\"Patient\",\"id\":\"1\",\"meta\":{\"versionId\":\"1\",\"lastUpdated\":\"2014-12-20T18:41:29.706-05:00\"},\"identifier\":[{\"system\":\"urn:MultiFhirVersionTest\",\"value\":\"testSubmitPatient01\"}]}}]}"; - ourResponseContentType = Constants.CT_FHIR_JSON + "; charset=UTF-8"; - ourResponseBody = msg; + CAPTURE_SERVLET.ourResponseContentType = Constants.CT_FHIR_JSON + "; charset=UTF-8"; + CAPTURE_SERVLET.ourResponseBody = msg; - IGenericClient client = ourCtx.newRestfulGenericClient("http://localhost:" + ourPort + "/fhir"); + IGenericClient client = ourCtx.newRestfulGenericClient(ourServer.getBaseUrl() + "/fhir"); Bundle response = client.search() @@ -1473,7 +1453,7 @@ public class GenericJaxRsClientDstu2Test { .execute(); - assertThat(ourRequestUri, either(equalTo("http://localhost:" + ourPort + "/fhir/Patient?name=james&_elements=name%2Cidentifier")).or(equalTo("http://localhost:" + ourPort + "/fhir/Patient?name=james&_elements=identifier%2Cname"))); + assertThat(CAPTURE_SERVLET.ourRequestUri, either(equalTo(ourServer.getBaseUrl() + "/fhir/Patient?name=james&_elements=name%2Cidentifier")).or(equalTo(ourServer.getBaseUrl() + "/fhir/Patient?name=james&_elements=identifier%2Cname"))); assertEquals(Patient.class, response.getEntry().get(0).getResource().getClass()); } @@ -1483,11 +1463,11 @@ public class GenericJaxRsClientDstu2Test { String msg = "{\"resourceType\":\"Bundle\",\"id\":null,\"base\":\"http://localhost:57931/fhir/contextDev\",\"total\":1,\"link\":[{\"relation\":\"self\",\"url\":\"http://localhost:57931/fhir/contextDev/Patient?identifier=urn%3AMultiFhirVersionTest%7CtestSubmitPatient01&_format=json\"}],\"entry\":[{\"resource\":{\"resourceType\":\"Patient\",\"id\":\"1\",\"meta\":{\"versionId\":\"1\",\"lastUpdated\":\"2014-12-20T18:41:29.706-05:00\"},\"identifier\":[{\"system\":\"urn:MultiFhirVersionTest\",\"value\":\"testSubmitPatient01\"}]}}]}"; - ourResponseContentType = Constants.CT_FHIR_JSON + "; charset=UTF-8"; - ourResponseBody = msg; + CAPTURE_SERVLET.ourResponseContentType = Constants.CT_FHIR_JSON + "; charset=UTF-8"; + CAPTURE_SERVLET.ourResponseBody = msg; - IGenericClient client = ourCtx.newRestfulGenericClient("http://localhost:" + ourPort + "/fhir"); + IGenericClient client = ourCtx.newRestfulGenericClient(ourServer.getBaseUrl() + "/fhir"); Bundle response = client.search() @@ -1499,18 +1479,18 @@ public class GenericJaxRsClientDstu2Test { .execute(); - assertEquals("http://localhost:" + ourPort + "/fhir/Patient/_search?_elements=identifier%2Cname", ourRequestUri); + assertEquals(ourServer.getBaseUrl() + "/fhir/Patient/_search?_elements=identifier%2Cname", CAPTURE_SERVLET.ourRequestUri); - // assertThat(ourRequestUri, - // either(equalTo("http://localhost:" + ourPort + "/fhir/Patient?name=james&_elements=name%2Cidentifier")).or(equalTo("http://localhost:" + ourPort + "/fhir/Patient?name=james&_elements=identifier%2Cname"))); + // assertThat(MY_SERVLET.ourRequestUri, + // either(equalTo(ourServer.getBaseUrl() + "/fhir/Patient?name=james&_elements=name%2Cidentifier")).or(equalTo(ourServer.getBaseUrl() + "/fhir/Patient?name=james&_elements=identifier%2Cname"))); assertEquals(Patient.class, response.getEntry().get(0).getResource().getClass()); - assertEquals("name=james", ourRequestBodyString); + assertEquals("name=james", CAPTURE_SERVLET.ourRequestBodyString); - assertEquals("application/x-www-form-urlencoded", ourRequestContentType.replace(";char", "; char").toLowerCase()); - assertEquals(Constants.HEADER_ACCEPT_VALUE_XML_OR_JSON_LEGACY, ourRequestFirstHeaders.get("Accept").getValue()); - assertThat(ourRequestFirstHeaders.get("User-Agent").getValue(), not(emptyString())); + assertEquals("application/x-www-form-urlencoded", CAPTURE_SERVLET.ourRequestContentType.replace(";char", "; char").toLowerCase()); + assertEquals(Constants.HEADER_ACCEPT_VALUE_XML_OR_JSON_LEGACY, CAPTURE_SERVLET.ourRequestFirstHeaders.get("Accept").getValue()); + assertThat(CAPTURE_SERVLET.ourRequestFirstHeaders.get("User-Agent").getValue(), not(emptyString())); } @Test @@ -1518,11 +1498,11 @@ public class GenericJaxRsClientDstu2Test { String msg = "{\"resourceType\":\"Bundle\",\"id\":null,\"base\":\"http://localhost:57931/fhir/contextDev\",\"total\":1,\"link\":[{\"relation\":\"self\",\"url\":\"http://localhost:57931/fhir/contextDev/Patient?identifier=urn%3AMultiFhirVersionTest%7CtestSubmitPatient01&_format=json\"}],\"entry\":[{\"resource\":{\"resourceType\":\"Patient\",\"id\":\"1\",\"meta\":{\"versionId\":\"1\",\"lastUpdated\":\"2014-12-20T18:41:29.706-05:00\"},\"identifier\":[{\"system\":\"urn:MultiFhirVersionTest\",\"value\":\"testSubmitPatient01\"}]}}]}"; - ourResponseContentType = Constants.CT_FHIR_JSON + "; charset=UTF-8"; - ourResponseBody = msg; + CAPTURE_SERVLET.ourResponseContentType = Constants.CT_FHIR_JSON + "; charset=UTF-8"; + CAPTURE_SERVLET.ourResponseBody = msg; - IGenericClient client = ourCtx.newRestfulGenericClient("http://localhost:" + ourPort + "/fhir"); + IGenericClient client = ourCtx.newRestfulGenericClient(ourServer.getBaseUrl() + "/fhir"); Bundle response = client.search() @@ -1535,18 +1515,18 @@ public class GenericJaxRsClientDstu2Test { .execute(); - assertThat(ourRequestUri, containsString("http://localhost:" + ourPort + "/fhir/Patient/_search?")); - assertThat(ourRequestUri, containsString("_elements=identifier%2Cname")); + assertThat(CAPTURE_SERVLET.ourRequestUri, containsString(ourServer.getBaseUrl() + "/fhir/Patient/_search?")); + assertThat(CAPTURE_SERVLET.ourRequestUri, containsString("_elements=identifier%2Cname")); - // assertThat(ourRequestUri, - // either(equalTo("http://localhost:" + ourPort + "/fhir/Patient?name=james&_elements=name%2Cidentifier")).or(equalTo("http://localhost:" + ourPort + "/fhir/Patient?name=james&_elements=identifier%2Cname"))); + // assertThat(MY_SERVLET.ourRequestUri, + // either(equalTo(ourServer.getBaseUrl() + "/fhir/Patient?name=james&_elements=name%2Cidentifier")).or(equalTo(ourServer.getBaseUrl() + "/fhir/Patient?name=james&_elements=identifier%2Cname"))); assertEquals(Patient.class, response.getEntry().get(0).getResource().getClass()); - assertEquals("name=james", ourRequestBodyString); + assertEquals("name=james", CAPTURE_SERVLET.ourRequestBodyString); - assertEquals("application/x-www-form-urlencoded", ourRequestContentType); - assertEquals(Constants.CT_FHIR_JSON, ourRequestFirstHeaders.get("Accept").getValue()); + assertEquals("application/x-www-form-urlencoded", CAPTURE_SERVLET.ourRequestContentType); + assertEquals(Constants.CT_FHIR_JSON, CAPTURE_SERVLET.ourRequestFirstHeaders.get("Accept").getValue()); } @Test @@ -1554,11 +1534,11 @@ public class GenericJaxRsClientDstu2Test { String msg = "{\"resourceType\":\"Bundle\",\"id\":null,\"base\":\"http://localhost:57931/fhir/contextDev\",\"total\":1,\"link\":[{\"relation\":\"self\",\"url\":\"http://localhost:57931/fhir/contextDev/Patient?identifier=urn%3AMultiFhirVersionTest%7CtestSubmitPatient01&_format=json\"}],\"entry\":[{\"resource\":{\"resourceType\":\"Patient\",\"id\":\"1\",\"meta\":{\"versionId\":\"1\",\"lastUpdated\":\"2014-12-20T18:41:29.706-05:00\"},\"identifier\":[{\"system\":\"urn:MultiFhirVersionTest\",\"value\":\"testSubmitPatient01\"}]}}]}"; - ourResponseContentType = Constants.CT_FHIR_JSON + "; charset=UTF-8"; - ourResponseBody = msg; + CAPTURE_SERVLET.ourResponseContentType = Constants.CT_FHIR_JSON + "; charset=UTF-8"; + CAPTURE_SERVLET.ourResponseBody = msg; - IGenericClient client = ourCtx.newRestfulGenericClient("http://localhost:" + ourPort + "/fhir"); + IGenericClient client = ourCtx.newRestfulGenericClient(ourServer.getBaseUrl() + "/fhir"); Bundle response = client.search() @@ -1569,7 +1549,7 @@ public class GenericJaxRsClientDstu2Test { .execute(); - assertEquals("http://localhost:" + ourPort + "/fhir/Patient?name=james&_lastUpdated=ge2011-01-01&_lastUpdated=le2012-01-01", ourRequestUri); + assertEquals(ourServer.getBaseUrl() + "/fhir/Patient?name=james&_lastUpdated=ge2011-01-01&_lastUpdated=le2012-01-01", CAPTURE_SERVLET.ourRequestUri); assertEquals(Patient.class, response.getEntry().get(0).getResource().getClass()); } @@ -1579,11 +1559,11 @@ public class GenericJaxRsClientDstu2Test { String msg = "{\"resourceType\":\"Bundle\",\"id\":null,\"base\":\"http://localhost:57931/fhir/contextDev\",\"total\":1,\"link\":[{\"relation\":\"self\",\"url\":\"http://localhost:57931/fhir/contextDev/Patient?identifier=urn%3AMultiFhirVersionTest%7CtestSubmitPatient01&_format=json\"}],\"entry\":[{\"resource\":{\"resourceType\":\"Patient\",\"id\":\"1\",\"meta\":{\"versionId\":\"1\",\"lastUpdated\":\"2014-12-20T18:41:29.706-05:00\"},\"identifier\":[{\"system\":\"urn:MultiFhirVersionTest\",\"value\":\"testSubmitPatient01\"}]}}]}"; - ourResponseContentType = Constants.CT_FHIR_JSON + "; charset=UTF-8"; - ourResponseBody = msg; + CAPTURE_SERVLET.ourResponseContentType = Constants.CT_FHIR_JSON + "; charset=UTF-8"; + CAPTURE_SERVLET.ourResponseBody = msg; - IGenericClient client = ourCtx.newRestfulGenericClient("http://localhost:" + ourPort + "/fhir"); + IGenericClient client = ourCtx.newRestfulGenericClient(ourServer.getBaseUrl() + "/fhir"); Bundle response = client.search() @@ -1596,7 +1576,7 @@ public class GenericJaxRsClientDstu2Test { .execute(); - assertEquals("http://localhost:" + ourPort + "/fhir/Patient?_security=system1%7Ccode1&_security=system2%7Ccode2&_profile=http%3A%2F%2Ffoo1&_profile=http%3A%2F%2Ffoo2", ourRequestUri); + assertEquals(ourServer.getBaseUrl() + "/fhir/Patient?_security=system1%7Ccode1&_security=system2%7Ccode2&_profile=http%3A%2F%2Ffoo1&_profile=http%3A%2F%2Ffoo2", CAPTURE_SERVLET.ourRequestUri); assertEquals(Patient.class, response.getEntry().get(0).getResource().getClass()); } @@ -1608,11 +1588,11 @@ public class GenericJaxRsClientDstu2Test { String msg = getPatientFeedWithOneResult(); - ourResponseContentType = Constants.CT_FHIR_XML + "; charset=UTF-8"; - ourResponseBody = msg; + CAPTURE_SERVLET.ourResponseContentType = Constants.CT_FHIR_XML + "; charset=UTF-8"; + CAPTURE_SERVLET.ourResponseBody = msg; - IGenericClient client = ourCtx.newRestfulGenericClient("http://localhost:" + ourPort + "/fhir"); + IGenericClient client = ourCtx.newRestfulGenericClient(ourServer.getBaseUrl() + "/fhir"); Bundle response = client.search() @@ -1623,7 +1603,7 @@ public class GenericJaxRsClientDstu2Test { .execute(); - assertEquals("http://localhost:" + ourPort + "/fhir/Patient?_revinclude=Provenance%3Atarget&_format=json", ourRequestUri); + assertEquals(ourServer.getBaseUrl() + "/fhir/Patient?_revinclude=Provenance%3Atarget&_format=json", CAPTURE_SERVLET.ourRequestUri); } @@ -1632,11 +1612,11 @@ public class GenericJaxRsClientDstu2Test { String msg = "{\"resourceType\":\"Bundle\",\"id\":null,\"base\":\"http://localhost:57931/fhir/contextDev\",\"total\":1,\"link\":[{\"relation\":\"self\",\"url\":\"http://localhost:57931/fhir/contextDev/Patient?identifier=urn%3AMultiFhirVersionTest%7CtestSubmitPatient01&_format=json\"}],\"entry\":[{\"resource\":{\"resourceType\":\"Patient\",\"id\":\"1\",\"meta\":{\"versionId\":\"1\",\"lastUpdated\":\"2014-12-20T18:41:29.706-05:00\"},\"identifier\":[{\"system\":\"urn:MultiFhirVersionTest\",\"value\":\"testSubmitPatient01\"}]}}]}"; - ourResponseContentType = Constants.CT_FHIR_JSON + "; charset=UTF-8"; - ourResponseBody = msg; + CAPTURE_SERVLET.ourResponseContentType = Constants.CT_FHIR_JSON + "; charset=UTF-8"; + CAPTURE_SERVLET.ourResponseBody = msg; - IGenericClient client = ourCtx.newRestfulGenericClient("http://localhost:" + ourPort + "/fhir"); + IGenericClient client = ourCtx.newRestfulGenericClient(ourServer.getBaseUrl() + "/fhir"); Bundle response = client.search() @@ -1647,7 +1627,7 @@ public class GenericJaxRsClientDstu2Test { .execute(); - assertEquals("http://localhost:" + ourPort + "/fhir/Patient?name=james&_summary=false", ourRequestUri); + assertEquals(ourServer.getBaseUrl() + "/fhir/Patient?name=james&_summary=false", CAPTURE_SERVLET.ourRequestUri); assertEquals(Patient.class, response.getEntry().get(0).getResource().getClass()); } @@ -1661,12 +1641,12 @@ public class GenericJaxRsClientDstu2Test { String respString = ourCtx.newJsonParser().encodeResourceToString(resp); - ourResponseContentType = Constants.CT_FHIR_JSON + "; charset=UTF-8"; - ourResponseBody = respString; + CAPTURE_SERVLET.ourResponseContentType = Constants.CT_FHIR_JSON + "; charset=UTF-8"; + CAPTURE_SERVLET.ourResponseBody = respString; - IGenericClient client = ourCtx.newRestfulGenericClient("http://localhost:" + ourPort + "/fhir"); + IGenericClient client = ourCtx.newRestfulGenericClient(ourServer.getBaseUrl() + "/fhir"); - List input = new ArrayList(); + List input = new ArrayList<>(); Patient p1 = new Patient(); // No ID p1.addName().addFamily("PATIENT1"); @@ -1684,10 +1664,10 @@ public class GenericJaxRsClientDstu2Test { .execute(); - assertEquals("http://localhost:" + ourPort + "/fhir", ourRequestUri); + assertEquals(ourServer.getBaseUrl() + "/fhir", CAPTURE_SERVLET.ourRequestUri); assertEquals(2, response.size()); - String requestString = ourRequestBodyString; + String requestString = CAPTURE_SERVLET.ourRequestBodyString; ca.uhn.fhir.model.dstu2.resource.Bundle requestBundle = ourCtx.newJsonParser().parseResource(ca.uhn.fhir.model.dstu2.resource.Bundle.class, requestString); assertEquals(2, requestBundle.getEntry().size()); assertEquals("POST", requestBundle.getEntry().get(0).getRequest().getMethod()); @@ -1720,10 +1700,10 @@ public class GenericJaxRsClientDstu2Test { resp.addEntry().getResponse().setLocation("Patient/2/_history/2"); - ourResponseContentType = Constants.CT_FHIR_JSON + "; charset=UTF-8"; - ourResponseBody = reqString; + CAPTURE_SERVLET.ourResponseContentType = Constants.CT_FHIR_JSON + "; charset=UTF-8"; + CAPTURE_SERVLET.ourResponseBody = reqString; - IGenericClient client = ourCtx.newRestfulGenericClient("http://localhost:" + ourPort + "/fhir"); + IGenericClient client = ourCtx.newRestfulGenericClient(ourServer.getBaseUrl() + "/fhir"); String response = client.transaction() @@ -1731,9 +1711,9 @@ public class GenericJaxRsClientDstu2Test { .execute(); - assertEquals("http://localhost:" + ourPort + "/fhir/", ourRequestUri); + assertEquals(ourServer.getBaseUrl() + "/fhir/", CAPTURE_SERVLET.ourRequestUri); assertThat(response, containsString("\"Bundle\"")); - assertEquals("application/json+fhir;charset=UTF-8", ourRequestFirstHeaders.get("Content-Type").getValue()); + assertEquals("application/json+fhir;charset=UTF-8", CAPTURE_SERVLET.ourRequestFirstHeaders.get("Content-Type").getValue()); response = client.transaction() @@ -1742,8 +1722,8 @@ public class GenericJaxRsClientDstu2Test { .execute(); - assertEquals("http://localhost:" + ourPort + "/fhir/", ourRequestUri); - assertEquals("application/xml+fhir;charset=UTF-8", ourRequestFirstHeaders.get("Content-Type").getValue()); + assertEquals(ourServer.getBaseUrl() + "/fhir/", CAPTURE_SERVLET.ourRequestUri); + assertEquals("application/xml+fhir;charset=UTF-8", CAPTURE_SERVLET.ourRequestFirstHeaders.get("Content-Type").getValue()); } @@ -1756,10 +1736,10 @@ public class GenericJaxRsClientDstu2Test { String respString = ourCtx.newJsonParser().encodeResourceToString(resp); - ourResponseContentType = Constants.CT_FHIR_JSON + "; charset=UTF-8"; - ourResponseBody = respString; + CAPTURE_SERVLET.ourResponseContentType = Constants.CT_FHIR_JSON + "; charset=UTF-8"; + CAPTURE_SERVLET.ourResponseBody = respString; - IGenericClient client = ourCtx.newRestfulGenericClient("http://localhost:" + ourPort + "/fhir"); + IGenericClient client = ourCtx.newRestfulGenericClient(ourServer.getBaseUrl() + "/fhir"); ca.uhn.fhir.model.dstu2.resource.Bundle input = new ca.uhn.fhir.model.dstu2.resource.Bundle(); @@ -1779,7 +1759,7 @@ public class GenericJaxRsClientDstu2Test { .execute(); - assertEquals("http://localhost:" + ourPort + "/fhir", ourRequestUri); + assertEquals(ourServer.getBaseUrl() + "/fhir", CAPTURE_SERVLET.ourRequestUri); assertEquals(2, response.getEntry().size()); assertEquals("Patient/1/_history/1", response.getEntry().get(0).getResponse().getLocation()); @@ -1790,52 +1770,52 @@ public class GenericJaxRsClientDstu2Test { public void testUpdateConditional() { - ourResponseStatus = Constants.STATUS_HTTP_204_NO_CONTENT; + CAPTURE_SERVLET.ourResponseStatus = Constants.STATUS_HTTP_204_NO_CONTENT; - IGenericClient client = ourCtx.newRestfulGenericClient("http://localhost:" + ourPort + "/fhir"); + IGenericClient client = ourCtx.newRestfulGenericClient(ourServer.getBaseUrl() + "/fhir"); Patient p = new Patient(); p.addName().addFamily("FOOFAMILY"); client.update().resource(p).conditionalByUrl("Patient?name=foo").encodedXml().execute(); - assertEquals(1, ourRequestHeaders.get(Constants.HEADER_CONTENT_TYPE).size()); - assertEquals(EncodingEnum.XML.getResourceContentType() + Constants.HEADER_SUFFIX_CT_UTF_8, ourRequestFirstHeaders.get(Constants.HEADER_CONTENT_TYPE).getValue().replace(";char", "; char")); - assertThat(ourRequestBodyString, containsString("")); - assertEquals("PUT", ourRequestMethod); - assertEquals("http://localhost:" + ourPort + "/fhir/Patient?name=foo", ourRequestUri); + assertEquals(1, CAPTURE_SERVLET.ourRequestHeaders.get(Constants.HEADER_CONTENT_TYPE).size()); + assertEquals(EncodingEnum.XML.getResourceContentType() + Constants.HEADER_SUFFIX_CT_UTF_8, CAPTURE_SERVLET.ourRequestFirstHeaders.get(Constants.HEADER_CONTENT_TYPE).getValue().replace(";char", "; char")); + assertThat(CAPTURE_SERVLET.ourRequestBodyString, containsString("")); + assertEquals("PUT", CAPTURE_SERVLET.ourRequestMethod); + assertEquals(ourServer.getBaseUrl() + "/fhir/Patient?name=foo", CAPTURE_SERVLET.ourRequestUri); client.update().resource(p).conditionalByUrl("Patient?name=http://foo|bar").encodedXml().execute(); - assertEquals(1, ourRequestHeaders.get(Constants.HEADER_CONTENT_TYPE).size()); - assertEquals(EncodingEnum.XML.getResourceContentType() + Constants.HEADER_SUFFIX_CT_UTF_8, ourRequestFirstHeaders.get(Constants.HEADER_CONTENT_TYPE).getValue().replace(";char", "; char")); - assertThat(ourRequestBodyString, containsString("")); - assertEquals("PUT", ourRequestMethod); - assertEquals("http://localhost:" + ourPort + "/fhir/Patient?name=http%3A//foo%7Cbar", ourRequestUri); + assertEquals(1, CAPTURE_SERVLET.ourRequestHeaders.get(Constants.HEADER_CONTENT_TYPE).size()); + assertEquals(EncodingEnum.XML.getResourceContentType() + Constants.HEADER_SUFFIX_CT_UTF_8, CAPTURE_SERVLET.ourRequestFirstHeaders.get(Constants.HEADER_CONTENT_TYPE).getValue().replace(";char", "; char")); + assertThat(CAPTURE_SERVLET.ourRequestBodyString, containsString("")); + assertEquals("PUT", CAPTURE_SERVLET.ourRequestMethod); + assertEquals(ourServer.getBaseUrl() + "/fhir/Patient?name=http%3A//foo%7Cbar", CAPTURE_SERVLET.ourRequestUri); client.update().resource(ourCtx.newXmlParser().encodeResourceToString(p)).conditionalByUrl("Patient?name=foo").encodedXml().execute(); - assertEquals(1, ourRequestHeaders.get(Constants.HEADER_CONTENT_TYPE).size()); - assertEquals(EncodingEnum.XML.getResourceContentType() + Constants.HEADER_SUFFIX_CT_UTF_8, ourRequestFirstHeaders.get(Constants.HEADER_CONTENT_TYPE).getValue().replace(";char", "; char")); - assertThat(ourRequestBodyString, containsString("")); - assertEquals("PUT", ourRequestMethod); - assertEquals("http://localhost:" + ourPort + "/fhir/Patient?name=foo", ourRequestUri); + assertEquals(1, CAPTURE_SERVLET.ourRequestHeaders.get(Constants.HEADER_CONTENT_TYPE).size()); + assertEquals(EncodingEnum.XML.getResourceContentType() + Constants.HEADER_SUFFIX_CT_UTF_8, CAPTURE_SERVLET.ourRequestFirstHeaders.get(Constants.HEADER_CONTENT_TYPE).getValue().replace(";char", "; char")); + assertThat(CAPTURE_SERVLET.ourRequestBodyString, containsString("")); + assertEquals("PUT", CAPTURE_SERVLET.ourRequestMethod); + assertEquals(ourServer.getBaseUrl() + "/fhir/Patient?name=foo", CAPTURE_SERVLET.ourRequestUri); client.update().resource(p).conditional().where(Patient.NAME.matches().value("foo")).and(Patient.ADDRESS.matches().value("AAA|BBB")).encodedXml().execute(); - assertEquals(1, ourRequestHeaders.get(Constants.HEADER_CONTENT_TYPE).size()); - assertEquals(EncodingEnum.XML.getResourceContentType() + Constants.HEADER_SUFFIX_CT_UTF_8, ourRequestFirstHeaders.get(Constants.HEADER_CONTENT_TYPE).getValue().replace(";char", "; char")); - assertThat(ourRequestBodyString, containsString("")); - assertEquals("PUT", ourRequestMethod); - assertEquals("http://localhost:" + ourPort + "/fhir/Patient?name=foo&address=AAA%5C%7CBBB", ourRequestUri); + assertEquals(1, CAPTURE_SERVLET.ourRequestHeaders.get(Constants.HEADER_CONTENT_TYPE).size()); + assertEquals(EncodingEnum.XML.getResourceContentType() + Constants.HEADER_SUFFIX_CT_UTF_8, CAPTURE_SERVLET.ourRequestFirstHeaders.get(Constants.HEADER_CONTENT_TYPE).getValue().replace(";char", "; char")); + assertThat(CAPTURE_SERVLET.ourRequestBodyString, containsString("")); + assertEquals("PUT", CAPTURE_SERVLET.ourRequestMethod); + assertEquals(ourServer.getBaseUrl() + "/fhir/Patient?name=foo&address=AAA%5C%7CBBB", CAPTURE_SERVLET.ourRequestUri); client.update().resource(ourCtx.newXmlParser().encodeResourceToString(p)).conditional().where(Patient.NAME.matches().value("foo")).and(Patient.ADDRESS.matches().value("AAA|BBB")).encodedXml().execute(); - assertEquals(1, ourRequestHeaders.get(Constants.HEADER_CONTENT_TYPE).size()); - assertEquals(EncodingEnum.XML.getResourceContentType() + Constants.HEADER_SUFFIX_CT_UTF_8, ourRequestFirstHeaders.get(Constants.HEADER_CONTENT_TYPE).getValue().replace(";char", "; char")); - assertThat(ourRequestBodyString, containsString("")); - assertEquals("PUT", ourRequestMethod); - assertEquals("http://localhost:" + ourPort + "/fhir/Patient?name=foo&address=AAA%5C%7CBBB", ourRequestUri); + assertEquals(1, CAPTURE_SERVLET.ourRequestHeaders.get(Constants.HEADER_CONTENT_TYPE).size()); + assertEquals(EncodingEnum.XML.getResourceContentType() + Constants.HEADER_SUFFIX_CT_UTF_8, CAPTURE_SERVLET.ourRequestFirstHeaders.get(Constants.HEADER_CONTENT_TYPE).getValue().replace(";char", "; char")); + assertThat(CAPTURE_SERVLET.ourRequestBodyString, containsString("")); + assertEquals("PUT", CAPTURE_SERVLET.ourRequestMethod); + assertEquals(ourServer.getBaseUrl() + "/fhir/Patient?name=foo&address=AAA%5C%7CBBB", CAPTURE_SERVLET.ourRequestUri); } @@ -1844,9 +1824,9 @@ public class GenericJaxRsClientDstu2Test { public void testUpdateNonFluent() { - ourResponseStatus = Constants.STATUS_HTTP_204_NO_CONTENT; + CAPTURE_SERVLET.ourResponseStatus = Constants.STATUS_HTTP_204_NO_CONTENT; - IGenericClient client = ourCtx.newRestfulGenericClient("http://localhost:" + ourPort + "/fhir"); + IGenericClient client = ourCtx.newRestfulGenericClient(ourServer.getBaseUrl() + "/fhir"); client.setEncoding(EncodingEnum.XML); @@ -1854,19 +1834,19 @@ public class GenericJaxRsClientDstu2Test { p.addName().addFamily("FOOFAMILY"); client.update(new IdDt("Patient/123"), p); - assertEquals(1, ourRequestHeaders.get(Constants.HEADER_CONTENT_TYPE).size()); - assertEquals(EncodingEnum.XML.getResourceContentType() + Constants.HEADER_SUFFIX_CT_UTF_8, ourRequestFirstHeaders.get(Constants.HEADER_CONTENT_TYPE).getValue().replace(";char", "; char")); - assertThat(ourRequestBodyString, containsString("")); - assertEquals("http://localhost:" + ourPort + "/fhir/Patient/123?_format=xml", ourRequestUri); - assertEquals("PUT", ourRequestMethod); + assertEquals(1, CAPTURE_SERVLET.ourRequestHeaders.get(Constants.HEADER_CONTENT_TYPE).size()); + assertEquals(EncodingEnum.XML.getResourceContentType() + Constants.HEADER_SUFFIX_CT_UTF_8, CAPTURE_SERVLET.ourRequestFirstHeaders.get(Constants.HEADER_CONTENT_TYPE).getValue().replace(";char", "; char")); + assertThat(CAPTURE_SERVLET.ourRequestBodyString, containsString("")); + assertEquals(ourServer.getBaseUrl() + "/fhir/Patient/123?_format=xml", CAPTURE_SERVLET.ourRequestUri); + assertEquals("PUT", CAPTURE_SERVLET.ourRequestMethod); client.update("123", p); - assertEquals(1, ourRequestHeaders.get(Constants.HEADER_CONTENT_TYPE).size()); - assertEquals(EncodingEnum.XML.getResourceContentType() + Constants.HEADER_SUFFIX_CT_UTF_8, ourRequestFirstHeaders.get(Constants.HEADER_CONTENT_TYPE).getValue().replace(";char", "; char")); - assertThat(ourRequestBodyString, containsString("")); - assertEquals("http://localhost:" + ourPort + "/fhir/Patient/123?_format=xml", ourRequestUri); - assertEquals("PUT", ourRequestMethod); + assertEquals(1, CAPTURE_SERVLET.ourRequestHeaders.get(Constants.HEADER_CONTENT_TYPE).size()); + assertEquals(EncodingEnum.XML.getResourceContentType() + Constants.HEADER_SUFFIX_CT_UTF_8, CAPTURE_SERVLET.ourRequestFirstHeaders.get(Constants.HEADER_CONTENT_TYPE).getValue().replace(";char", "; char")); + assertThat(CAPTURE_SERVLET.ourRequestBodyString, containsString("")); + assertEquals(ourServer.getBaseUrl() + "/fhir/Patient/123?_format=xml", CAPTURE_SERVLET.ourRequestUri); + assertEquals("PUT", CAPTURE_SERVLET.ourRequestMethod); } @@ -1874,9 +1854,9 @@ public class GenericJaxRsClientDstu2Test { public void testUpdatePrefer() { - ourResponseStatus = Constants.STATUS_HTTP_204_NO_CONTENT; + CAPTURE_SERVLET.ourResponseStatus = Constants.STATUS_HTTP_204_NO_CONTENT; - IGenericClient client = ourCtx.newRestfulGenericClient("http://localhost:" + ourPort + "/fhir"); + IGenericClient client = ourCtx.newRestfulGenericClient(ourServer.getBaseUrl() + "/fhir"); Patient p = new Patient(); @@ -1884,13 +1864,13 @@ public class GenericJaxRsClientDstu2Test { p.addName().addFamily("FOOFAMILY"); client.update().resource(p).prefer(PreferReturnEnum.MINIMAL).execute(); - assertEquals(1, ourRequestHeaders.get(Constants.HEADER_PREFER).size()); - assertEquals(Constants.HEADER_PREFER_RETURN + '=' + Constants.HEADER_PREFER_RETURN_MINIMAL, ourRequestHeaders.get(Constants.HEADER_PREFER).get(0).getValue()); + assertEquals(1, CAPTURE_SERVLET.ourRequestHeaders.get(Constants.HEADER_PREFER).size()); + assertEquals(Constants.HEADER_PREFER_RETURN + '=' + Constants.HEADER_PREFER_RETURN_MINIMAL, CAPTURE_SERVLET.ourRequestHeaders.get(Constants.HEADER_PREFER).get(0).getValue()); client.update().resource(p).prefer(PreferReturnEnum.REPRESENTATION).execute(); - assertEquals(1, ourRequestHeaders.get(Constants.HEADER_PREFER).size()); - assertEquals(Constants.HEADER_PREFER_RETURN + '=' + Constants.HEADER_PREFER_RETURN_REPRESENTATION, ourRequestHeaders.get(Constants.HEADER_PREFER).get(0).getValue()); + assertEquals(1, CAPTURE_SERVLET.ourRequestHeaders.get(Constants.HEADER_PREFER).size()); + assertEquals(Constants.HEADER_PREFER_RETURN + '=' + Constants.HEADER_PREFER_RETURN_REPRESENTATION, CAPTURE_SERVLET.ourRequestHeaders.get(Constants.HEADER_PREFER).get(0).getValue()); } @@ -1902,11 +1882,11 @@ public class GenericJaxRsClientDstu2Test { final String formatted = ourCtx.newXmlParser().encodeResourceToString(p); - ourResponseStatus = Constants.STATUS_HTTP_200_OK; - ourResponseContentType = Constants.CT_FHIR_XML + "; charset=UTF-8"; - ourResponseBody = formatted; + CAPTURE_SERVLET.ourResponseStatus = Constants.STATUS_HTTP_200_OK; + CAPTURE_SERVLET.ourResponseContentType = Constants.CT_FHIR_XML + "; charset=UTF-8"; + CAPTURE_SERVLET.ourResponseBody = formatted; - IGenericClient client = ourCtx.newRestfulGenericClient("http://localhost:" + ourPort + "/fhir"); + IGenericClient client = ourCtx.newRestfulGenericClient(ourServer.getBaseUrl() + "/fhir"); p = new Patient(); p.setId(new IdDt("1")); @@ -1925,10 +1905,10 @@ public class GenericJaxRsClientDstu2Test { final String msg = ourCtx.newXmlParser().encodeResourceToString(oo); - ourResponseContentType = Constants.CT_FHIR_XML + "; charset=UTF-8"; - ourResponseBody = msg; + CAPTURE_SERVLET.ourResponseContentType = Constants.CT_FHIR_XML + "; charset=UTF-8"; + CAPTURE_SERVLET.ourResponseBody = msg; - IGenericClient client = ourCtx.newRestfulGenericClient("http://localhost:" + ourPort + "/fhir"); + IGenericClient client = ourCtx.newRestfulGenericClient(ourServer.getBaseUrl() + "/fhir"); Patient p = new Patient(); p.addName().addGiven("GIVEN"); @@ -1937,33 +1917,33 @@ public class GenericJaxRsClientDstu2Test { MethodOutcome response; response = client.validate().resource(p).encodedXml().execute(); - assertEquals("http://localhost:" + ourPort + "/fhir/Patient/$validate", ourRequestUri); - assertEquals("POST", ourRequestMethod); - assertEquals("", ourRequestBodyString); + assertEquals(ourServer.getBaseUrl() + "/fhir/Patient/$validate", CAPTURE_SERVLET.ourRequestUri); + assertEquals("POST", CAPTURE_SERVLET.ourRequestMethod); + assertEquals("", CAPTURE_SERVLET.ourRequestBodyString); assertNotNull(response.getOperationOutcome()); assertEquals("FOOBAR", toOo(response.getOperationOutcome()).getIssueFirstRep().getDiagnosticsElement().getValue()); response = client.validate().resource(ourCtx.newXmlParser().encodeResourceToString(p)).encodedXml().execute(); - assertEquals("http://localhost:" + ourPort + "/fhir/Patient/$validate", ourRequestUri); - assertEquals("POST", ourRequestMethod); - assertEquals("", ourRequestBodyString); + assertEquals(ourServer.getBaseUrl() + "/fhir/Patient/$validate", CAPTURE_SERVLET.ourRequestUri); + assertEquals("POST", CAPTURE_SERVLET.ourRequestMethod); + assertEquals("", CAPTURE_SERVLET.ourRequestBodyString); assertNotNull(response.getOperationOutcome()); assertEquals("FOOBAR", toOo(response.getOperationOutcome()).getIssueFirstRep().getDiagnosticsElement().getValue()); response = client.validate().resource(ourCtx.newJsonParser().encodeResourceToString(p)).execute(); - assertEquals("http://localhost:" + ourPort + "/fhir/Patient/$validate", ourRequestUri); - assertEquals("POST", ourRequestMethod); - assertEquals("{\"resourceType\":\"Parameters\",\"parameter\":[{\"name\":\"resource\",\"resource\":{\"resourceType\":\"Patient\",\"name\":[{\"given\":[\"GIVEN\"]}]}}]}", ourRequestBodyString); + assertEquals(ourServer.getBaseUrl() + "/fhir/Patient/$validate", CAPTURE_SERVLET.ourRequestUri); + assertEquals("POST", CAPTURE_SERVLET.ourRequestMethod); + assertEquals("{\"resourceType\":\"Parameters\",\"parameter\":[{\"name\":\"resource\",\"resource\":{\"resourceType\":\"Patient\",\"name\":[{\"given\":[\"GIVEN\"]}]}}]}", CAPTURE_SERVLET.ourRequestBodyString); assertNotNull(response.getOperationOutcome()); assertEquals("FOOBAR", toOo(response.getOperationOutcome()).getIssueFirstRep().getDiagnosticsElement().getValue()); response = client.validate().resource(ourCtx.newJsonParser().encodeResourceToString(p)).prettyPrint().execute(); - assertEquals("http://localhost:" + ourPort + "/fhir/Patient/$validate?_pretty=true", ourRequestUri); - assertEquals("POST", ourRequestMethod); - assertThat(ourRequestBodyString, containsString("\"resourceType\": \"Parameters\",\n")); + assertEquals(ourServer.getBaseUrl() + "/fhir/Patient/$validate?_pretty=true", CAPTURE_SERVLET.ourRequestUri); + assertEquals("POST", CAPTURE_SERVLET.ourRequestMethod); + assertThat(CAPTURE_SERVLET.ourRequestBodyString, containsString("\"resourceType\": \"Parameters\",\n")); assertNotNull(response.getOperationOutcome()); assertEquals("FOOBAR", toOo(response.getOperationOutcome()).getIssueFirstRep().getDiagnosticsElement().getValue()); @@ -1975,10 +1955,10 @@ public class GenericJaxRsClientDstu2Test { oo.addIssue().setDiagnostics("FOOBAR"); final String msg = ourCtx.newXmlParser().encodeResourceToString(oo); - ourResponseContentType = Constants.CT_FHIR_XML + "; charset=UTF-8"; - ourResponseBody = msg; + CAPTURE_SERVLET.ourResponseContentType = Constants.CT_FHIR_XML + "; charset=UTF-8"; + CAPTURE_SERVLET.ourResponseBody = msg; - IGenericClient client = ourCtx.newRestfulGenericClient("http://localhost:" + ourPort + "/fhir"); + IGenericClient client = ourCtx.newRestfulGenericClient(ourServer.getBaseUrl() + "/fhir"); client.setEncoding(EncodingEnum.XML); Patient p = new Patient(); @@ -1991,9 +1971,9 @@ public class GenericJaxRsClientDstu2Test { response = client.validate(p); - assertEquals("http://localhost:" + ourPort + "/fhir/Patient/$validate?_format=xml", ourRequestUri); - assertEquals("POST", ourRequestMethod); - assertEquals("", ourRequestBodyString); + assertEquals(ourServer.getBaseUrl() + "/fhir/Patient/$validate?_format=xml", CAPTURE_SERVLET.ourRequestUri); + assertEquals("POST", CAPTURE_SERVLET.ourRequestMethod); + assertEquals("", CAPTURE_SERVLET.ourRequestBodyString); assertNotNull(response.getOperationOutcome()); assertEquals("FOOBAR", toOo(response.getOperationOutcome()).getIssueFirstRep().getDiagnosticsElement().getValue()); @@ -2003,77 +1983,4 @@ public class GenericJaxRsClientDstu2Test { return (OperationOutcome) theOperationOutcome; } - @BeforeEach - public void beforeReset() { - ourRequestUri = null; - ourRequestUriAll = Lists.newArrayList(); - ourResponseStatus = 200; - ourResponseBody = null; - ourResponseBodies = null; - ourResponseCount = 0; - - ourResponseContentType = null; - ourRequestContentType = null; - ourRequestBodyBytes = null; - ourRequestBodyString = null; - ourRequestHeaders = null; - ourRequestFirstHeaders = null; - ourRequestMethod = null; - ourRequestHeadersAll = Lists.newArrayList(); - } - - @BeforeAll - public static void beforeClass() throws Exception { - ourCtx = FhirContext.forDstu2(); - - ourServer = new Server(0); - ourServer.setHandler(new AbstractHandler() { - - @Override - public void handle(String theArg0, Request theRequest, HttpServletRequest theServletRequest, HttpServletResponse theResp) throws IOException { - theRequest.setHandled(true); - ourRequestUri = theRequest.getHttpURI().toString(); - ourRequestUriAll.add(ourRequestUri); - ourRequestMethod = theRequest.getMethod(); - ourRequestContentType = theServletRequest.getContentType(); - ourRequestBodyBytes = IOUtils.toByteArray(theServletRequest.getInputStream()); - ourRequestBodyString = new String(ourRequestBodyBytes, Charsets.UTF_8); - - ourRequestHeaders = ArrayListMultimap.create(); - ourRequestHeadersAll.add(ourRequestHeaders); - ourRequestFirstHeaders = Maps.newHashMap(); - - for (Enumeration headerNameEnum = theRequest.getHeaderNames(); headerNameEnum.hasMoreElements(); ) { - String nextName = headerNameEnum.nextElement(); - for (Enumeration headerValueEnum = theRequest.getHeaders(nextName); headerValueEnum.hasMoreElements(); ) { - String nextValue = headerValueEnum.nextElement(); - if (ourRequestFirstHeaders.containsKey(nextName) == false) { - ourRequestFirstHeaders.put(nextName, new Header(nextName, nextValue)); - } - ourRequestHeaders.put(nextName, new Header(nextName, nextValue)); - } - } - - theResp.setStatus(ourResponseStatus); - - if (ourResponseBody != null) { - theResp.setContentType(ourResponseContentType); - theResp.getWriter().write(ourResponseBody); - } else if (ourResponseBodies != null) { - theResp.setContentType(ourResponseContentType); - theResp.getWriter().write(ourResponseBodies[ourResponseCount]); - } - - ourResponseCount++; - } - }); - - JettyUtil.startServer(ourServer); - ourPort = JettyUtil.getPortForStartedServer(ourServer); - } - - @AfterAll - public static void afterClass() throws Exception { - JettyUtil.closeServer(ourServer); - } } diff --git a/hapi-fhir-jaxrsserver-base/src/test/java/ca/uhn/fhir/jaxrs/client/GenericJaxRsClientDstu3Test.java b/hapi-fhir-jaxrsserver-base/src/test/java/ca/uhn/fhir/jaxrs/client/GenericJaxRsClientDstu3Test.java index 40d7a62d37b..6826becadcd 100644 --- a/hapi-fhir-jaxrsserver-base/src/test/java/ca/uhn/fhir/jaxrs/client/GenericJaxRsClientDstu3Test.java +++ b/hapi-fhir-jaxrsserver-base/src/test/java/ca/uhn/fhir/jaxrs/client/GenericJaxRsClientDstu3Test.java @@ -19,6 +19,8 @@ import ca.uhn.fhir.rest.client.interceptor.LoggingInterceptor; import ca.uhn.fhir.rest.param.DateRangeParam; import ca.uhn.fhir.system.HapiSystemProperties; import ca.uhn.fhir.test.utilities.JettyUtil; +import ca.uhn.fhir.test.utilities.server.HttpServletExtension; +import ca.uhn.fhir.test.utilities.server.RequestCaptureServlet; import com.google.common.collect.ArrayListMultimap; import com.google.common.collect.Lists; import com.google.common.collect.Maps; @@ -55,8 +57,10 @@ import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; -import javax.servlet.http.HttpServletRequest; -import javax.servlet.http.HttpServletResponse; +import jakarta.servlet.http.HttpServletRequest; +import jakarta.servlet.http.HttpServletResponse; +import org.junit.jupiter.api.extension.RegisterExtension; + import java.io.IOException; import java.util.ArrayList; import java.util.Date; @@ -77,23 +81,14 @@ import static org.junit.jupiter.api.Assertions.fail; public class GenericJaxRsClientDstu3Test { private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(GenericJaxRsClientDstu3Test.class); - private static FhirContext ourCtx; - private static int ourPort; - private static Server ourServer; - private static int ourResponseCount = 0; - private static String[] ourResponseBodies; - private static String ourResponseBody; - private static String ourResponseContentType; - private static int ourResponseStatus; - private static String ourRequestUri; - private static List ourRequestUriAll; - private static String ourRequestMethod; - private static String ourRequestContentType; - private static byte[] ourRequestBodyBytes; - private static String ourRequestBodyString; - private static ArrayListMultimap ourRequestHeaders; - private static List> ourRequestHeadersAll; - private static Map ourRequestFirstHeaders; + private static final FhirContext ourCtx = FhirContext.forDstu3(); + + private static final RequestCaptureServlet MY_SERVLET = new RequestCaptureServlet(); + + @RegisterExtension + public static final HttpServletExtension ourServer = new HttpServletExtension() + .withServlet(MY_SERVLET) + .keepAliveBetweenTests(); @BeforeEach public void before() { @@ -101,7 +96,7 @@ public class GenericJaxRsClientDstu3Test { clientFactory.setServerValidationMode(ServerValidationModeEnum.NEVER); ourCtx.setRestfulClientFactory(clientFactory); - ourResponseCount = 0; + MY_SERVLET.reset(); HapiSystemProperties.enableHapiClientKeepResponses(); } @@ -134,28 +129,28 @@ public class GenericJaxRsClientDstu3Test { conf.setCopyright("COPY"); final String respString = p.encodeResourceToString(conf); - ourResponseContentType = Constants.CT_FHIR_XML + "; charset=UTF-8"; - ourResponseBody = respString; + MY_SERVLET.ourResponseContentType = Constants.CT_FHIR_XML + "; charset=UTF-8"; + MY_SERVLET.ourResponseBody = respString; - IGenericClient client = ourCtx.newRestfulGenericClient("http://localhost:" + ourPort + "/fhir"); + IGenericClient client = ourCtx.newRestfulGenericClient(ourServer.getBaseUrl() + "/fhir"); client.fetchConformance().ofType(CapabilityStatement.class).execute(); - assertEquals("http://localhost:" + ourPort + "/fhir/metadata", ourRequestUri); - assertEquals(1, ourRequestHeaders.get("Accept").size()); - assertThat(ourRequestHeaders.get("Accept").get(0).getValue(), containsString(Constants.HEADER_ACCEPT_VALUE_XML_OR_JSON_NON_LEGACY)); + assertEquals(ourServer.getBaseUrl() + "/fhir/metadata", MY_SERVLET.ourRequestUri); + assertEquals(1, MY_SERVLET.ourRequestHeaders.get("Accept").size()); + assertThat(MY_SERVLET.ourRequestHeaders.get("Accept").get(0).getValue(), containsString(Constants.HEADER_ACCEPT_VALUE_XML_OR_JSON_NON_LEGACY)); client.fetchConformance().ofType(CapabilityStatement.class).encodedJson().execute(); - assertEquals("http://localhost:" + ourPort + "/fhir/metadata?_format=json", ourRequestUri); - assertEquals(1, ourRequestHeaders.get("Accept").size()); - assertThat(ourRequestHeaders.get("Accept").get(0).getValue(), containsString(Constants.CT_FHIR_JSON)); + assertEquals(ourServer.getBaseUrl() + "/fhir/metadata?_format=json", MY_SERVLET.ourRequestUri); + assertEquals(1, MY_SERVLET.ourRequestHeaders.get("Accept").size()); + assertThat(MY_SERVLET.ourRequestHeaders.get("Accept").get(0).getValue(), containsString(Constants.CT_FHIR_JSON)); client.fetchConformance().ofType(CapabilityStatement.class).encodedXml().execute(); - assertEquals("http://localhost:" + ourPort + "/fhir/metadata?_format=xml", ourRequestUri); - assertEquals(1, ourRequestHeaders.get("Accept").size()); - assertThat(ourRequestHeaders.get("Accept").get(0).getValue(), containsString(Constants.CT_FHIR_XML)); + assertEquals(ourServer.getBaseUrl() + "/fhir/metadata?_format=xml", MY_SERVLET.ourRequestUri); + assertEquals(1, MY_SERVLET.ourRequestHeaders.get("Accept").size()); + assertThat(MY_SERVLET.ourRequestHeaders.get("Accept").get(0).getValue(), containsString(Constants.CT_FHIR_XML)); } @@ -169,24 +164,24 @@ public class GenericJaxRsClientDstu3Test { final Patient patient = new Patient(); patient.addName().setFamily("FAMILY"); - ourResponseContentType = Constants.CT_FHIR_XML + "; charset=UTF-8"; - ourResponseBodies = new String[]{p.encodeResourceToString(conf), p.encodeResourceToString(patient)}; + MY_SERVLET.ourResponseContentType = Constants.CT_FHIR_XML + "; charset=UTF-8"; + MY_SERVLET.ourResponseBodies = new String[]{p.encodeResourceToString(conf), p.encodeResourceToString(patient)}; ourCtx.getRestfulClientFactory().setServerValidationMode(ServerValidationModeEnum.ONCE); - IGenericClient client = ourCtx.newRestfulGenericClient("http://localhost:" + ourPort + "/fhir"); + IGenericClient client = ourCtx.newRestfulGenericClient(ourServer.getBaseUrl() + "/fhir"); Patient resp = client.read(Patient.class, new IdType("123").getValue()); assertEquals("FAMILY", resp.getName().get(0).getFamily()); - assertEquals("http://localhost:" + ourPort + "/fhir/metadata", ourRequestUriAll.get(0)); - assertEquals(1, ourRequestHeadersAll.get(0).get("Accept").size()); - assertThat(ourRequestHeadersAll.get(0).get("Accept").get(0).getValue(), containsString(Constants.HEADER_ACCEPT_VALUE_XML_OR_JSON_NON_LEGACY)); - assertThat(ourRequestHeadersAll.get(0).get("Accept").get(0).getValue(), containsString(Constants.CT_FHIR_XML)); - assertThat(ourRequestHeadersAll.get(0).get("Accept").get(0).getValue(), containsString(Constants.CT_FHIR_JSON)); - assertEquals("http://localhost:" + ourPort + "/fhir/Patient/123", ourRequestUriAll.get(1)); - assertEquals(1, ourRequestHeadersAll.get(1).get("Accept").size()); - assertThat(ourRequestHeadersAll.get(1).get("Accept").get(0).getValue(), containsString(Constants.HEADER_ACCEPT_VALUE_XML_OR_JSON_NON_LEGACY)); - assertThat(ourRequestHeadersAll.get(1).get("Accept").get(0).getValue(), containsString(Constants.CT_FHIR_XML)); - assertThat(ourRequestHeadersAll.get(1).get("Accept").get(0).getValue(), containsString(Constants.CT_FHIR_JSON)); + assertEquals(ourServer.getBaseUrl() + "/fhir/metadata", MY_SERVLET.ourRequestUriAll.get(0)); + assertEquals(1, MY_SERVLET.ourRequestHeadersAll.get(0).get("Accept").size()); + assertThat(MY_SERVLET.ourRequestHeadersAll.get(0).get("Accept").get(0).getValue(), containsString(Constants.HEADER_ACCEPT_VALUE_XML_OR_JSON_NON_LEGACY)); + assertThat(MY_SERVLET.ourRequestHeadersAll.get(0).get("Accept").get(0).getValue(), containsString(Constants.CT_FHIR_XML)); + assertThat(MY_SERVLET.ourRequestHeadersAll.get(0).get("Accept").get(0).getValue(), containsString(Constants.CT_FHIR_JSON)); + assertEquals(ourServer.getBaseUrl() + "/fhir/Patient/123", MY_SERVLET.ourRequestUriAll.get(1)); + assertEquals(1, MY_SERVLET.ourRequestHeadersAll.get(1).get("Accept").size()); + assertThat(MY_SERVLET.ourRequestHeadersAll.get(1).get("Accept").get(0).getValue(), containsString(Constants.HEADER_ACCEPT_VALUE_XML_OR_JSON_NON_LEGACY)); + assertThat(MY_SERVLET.ourRequestHeadersAll.get(1).get("Accept").get(0).getValue(), containsString(Constants.CT_FHIR_XML)); + assertThat(MY_SERVLET.ourRequestHeadersAll.get(1).get("Accept").get(0).getValue(), containsString(Constants.CT_FHIR_JSON)); } @Test @@ -199,23 +194,23 @@ public class GenericJaxRsClientDstu3Test { final Patient patient = new Patient(); patient.addName().setFamily("FAMILY"); - ourResponseContentType = Constants.CT_FHIR_XML + "; charset=UTF-8"; - ourResponseBodies = new String[]{p.encodeResourceToString(conf), p.encodeResourceToString(patient)}; + MY_SERVLET.ourResponseContentType = Constants.CT_FHIR_XML + "; charset=UTF-8"; + MY_SERVLET.ourResponseBodies = new String[]{p.encodeResourceToString(conf), p.encodeResourceToString(patient)}; ourCtx.getRestfulClientFactory().setServerValidationMode(ServerValidationModeEnum.ONCE); - IGenericClient client = ourCtx.newRestfulGenericClient("http://localhost:" + ourPort + "/fhir"); + IGenericClient client = ourCtx.newRestfulGenericClient(ourServer.getBaseUrl() + "/fhir"); client.setEncoding(EncodingEnum.JSON); Patient resp = client.read(Patient.class, new IdType("123").getValue()); assertEquals("FAMILY", resp.getName().get(0).getFamily()); - assertEquals("http://localhost:" + ourPort + "/fhir/metadata?_format=json", ourRequestUriAll.get(0)); - assertEquals(1, ourRequestHeadersAll.get(0).get("Accept").size()); - assertThat(ourRequestHeadersAll.get(0).get("Accept").get(0).getValue(), containsString(Constants.CT_FHIR_JSON)); - assertThat(ourRequestHeadersAll.get(0).get("Accept").get(0).getValue(), not(containsString(Constants.CT_FHIR_XML))); - assertEquals("http://localhost:" + ourPort + "/fhir/Patient/123?_format=json", ourRequestUriAll.get(1)); - assertEquals(1, ourRequestHeadersAll.get(1).get("Accept").size()); - assertThat(ourRequestHeadersAll.get(1).get("Accept").get(0).getValue(), containsString(Constants.CT_FHIR_JSON)); - assertThat(ourRequestHeadersAll.get(1).get("Accept").get(0).getValue(), not(containsString(Constants.CT_FHIR_XML))); + assertEquals(ourServer.getBaseUrl() + "/fhir/metadata?_format=json", MY_SERVLET.ourRequestUriAll.get(0)); + assertEquals(1, MY_SERVLET.ourRequestHeadersAll.get(0).get("Accept").size()); + assertThat(MY_SERVLET.ourRequestHeadersAll.get(0).get("Accept").get(0).getValue(), containsString(Constants.CT_FHIR_JSON)); + assertThat(MY_SERVLET.ourRequestHeadersAll.get(0).get("Accept").get(0).getValue(), not(containsString(Constants.CT_FHIR_XML))); + assertEquals(ourServer.getBaseUrl() + "/fhir/Patient/123?_format=json", MY_SERVLET.ourRequestUriAll.get(1)); + assertEquals(1, MY_SERVLET.ourRequestHeadersAll.get(1).get("Accept").size()); + assertThat(MY_SERVLET.ourRequestHeadersAll.get(1).get("Accept").get(0).getValue(), containsString(Constants.CT_FHIR_JSON)); + assertThat(MY_SERVLET.ourRequestHeadersAll.get(1).get("Accept").get(0).getValue(), not(containsString(Constants.CT_FHIR_XML))); } @Test @@ -227,19 +222,19 @@ public class GenericJaxRsClientDstu3Test { final String respString = p.encodeResourceToString(conf); - ourResponseContentType = Constants.CT_FHIR_XML + "; charset=UTF-8"; - ourResponseBody = respString; + MY_SERVLET.ourResponseContentType = Constants.CT_FHIR_XML + "; charset=UTF-8"; + MY_SERVLET.ourResponseBody = respString; - IGenericClient client = ourCtx.newRestfulGenericClient("http://localhost:" + ourPort + "/fhir"); + IGenericClient client = ourCtx.newRestfulGenericClient(ourServer.getBaseUrl() + "/fhir"); //@formatter:off CapabilityStatement resp = client.fetchConformance().ofType(CapabilityStatement.class).execute(); //@formatter:on - assertEquals("http://localhost:" + ourPort + "/fhir/metadata", ourRequestUri); + assertEquals(ourServer.getBaseUrl() + "/fhir/metadata", MY_SERVLET.ourRequestUri); assertEquals("COPY", resp.getCopyright()); - assertEquals("GET", ourRequestMethod); + assertEquals("GET", MY_SERVLET.ourRequestMethod); } @@ -252,7 +247,7 @@ public class GenericJaxRsClientDstu3Test { ourCtx.setRestfulClientFactory(clientFactory); try { - ourCtx.newRestfulGenericClient("http://localhost:" + ourPort + "/fhir"); + ourCtx.newRestfulGenericClient(ourServer.getBaseUrl() + "/fhir"); fail(); } catch (IllegalStateException e) { assertEquals(Msg.code(1355) + "JaxRsRestfulClientFactory does not have FhirContext defined. This must be set via JaxRsRestfulClientFactory#setFhirContext(FhirContext)", e.getMessage()); @@ -261,7 +256,7 @@ public class GenericJaxRsClientDstu3Test { @Test public void testCreate() { - IGenericClient client = ourCtx.newRestfulGenericClient("http://localhost:" + ourPort + "/fhir"); + IGenericClient client = ourCtx.newRestfulGenericClient(ourServer.getBaseUrl() + "/fhir"); Patient p = new Patient(); @@ -269,23 +264,23 @@ public class GenericJaxRsClientDstu3Test { client.create().resource(p).encodedXml().execute(); - assertEquals(1, ourRequestHeaders.get(Constants.HEADER_CONTENT_TYPE).size()); - assertEquals(EncodingEnum.XML.getResourceContentTypeNonLegacy() + Constants.HEADER_SUFFIX_CT_UTF_8, ourRequestFirstHeaders.get(Constants.HEADER_CONTENT_TYPE).getValue().replace(";char", "; char")); - assertThat(ourRequestBodyString, containsString("")); - assertEquals("http://localhost:" + ourPort + "/fhir/Patient", ourRequestUri); - assertEquals("POST", ourRequestMethod); + assertEquals(1, MY_SERVLET.ourRequestHeaders.get(Constants.HEADER_CONTENT_TYPE).size()); + assertEquals(EncodingEnum.XML.getResourceContentTypeNonLegacy() + Constants.HEADER_SUFFIX_CT_UTF_8, MY_SERVLET.ourRequestFirstHeaders.get(Constants.HEADER_CONTENT_TYPE).getValue().replace(";char", "; char")); + assertThat(MY_SERVLET.ourRequestBodyString, containsString("")); + assertEquals(ourServer.getBaseUrl() + "/fhir/Patient", MY_SERVLET.ourRequestUri); + assertEquals("POST", MY_SERVLET.ourRequestMethod); p.setId("123"); client.create().resource(p).encodedXml().execute(); - assertEquals(1, ourRequestHeaders.get(Constants.HEADER_CONTENT_TYPE).size()); - assertEquals(EncodingEnum.XML.getResourceContentTypeNonLegacy() + Constants.HEADER_SUFFIX_CT_UTF_8, ourRequestFirstHeaders.get(Constants.HEADER_CONTENT_TYPE).getValue().replace(";char", "; char")); - String body = ourRequestBodyString; + assertEquals(1, MY_SERVLET.ourRequestHeaders.get(Constants.HEADER_CONTENT_TYPE).size()); + assertEquals(EncodingEnum.XML.getResourceContentTypeNonLegacy() + Constants.HEADER_SUFFIX_CT_UTF_8, MY_SERVLET.ourRequestFirstHeaders.get(Constants.HEADER_CONTENT_TYPE).getValue().replace(";char", "; char")); + String body = MY_SERVLET.ourRequestBodyString; assertThat(body, containsString("")); assertThat(body, not(containsString("123"))); - assertEquals("http://localhost:" + ourPort + "/fhir/Patient", ourRequestUri); - assertEquals("POST", ourRequestMethod); + assertEquals(ourServer.getBaseUrl() + "/fhir/Patient", MY_SERVLET.ourRequestUri); + assertEquals("POST", MY_SERVLET.ourRequestMethod); } @@ -294,48 +289,48 @@ public class GenericJaxRsClientDstu3Test { public void testCreateConditional() { - ourResponseStatus = Constants.STATUS_HTTP_204_NO_CONTENT; + MY_SERVLET.ourResponseStatus = Constants.STATUS_HTTP_204_NO_CONTENT; - IGenericClient client = ourCtx.newRestfulGenericClient("http://localhost:" + ourPort + "/fhir"); + IGenericClient client = ourCtx.newRestfulGenericClient(ourServer.getBaseUrl() + "/fhir"); Patient p = new Patient(); p.addName().setFamily("FOOFAMILY"); client.create().resource(p).conditionalByUrl("Patient?name=foo").encodedXml().execute(); - assertEquals(1, ourRequestHeaders.get(Constants.HEADER_CONTENT_TYPE).size()); - assertEquals(EncodingEnum.XML.getResourceContentTypeNonLegacy() + Constants.HEADER_SUFFIX_CT_UTF_8, ourRequestFirstHeaders.get(Constants.HEADER_CONTENT_TYPE).getValue().replace(";char", "; char")); - assertThat(ourRequestBodyString, containsString("")); - assertEquals("http://localhost:" + ourPort + "/fhir/Patient", ourRequestUri); - assertEquals("http://localhost:" + ourPort + "/fhir/Patient?name=foo", ourRequestFirstHeaders.get(Constants.HEADER_IF_NONE_EXIST).getValue()); - assertEquals("POST", ourRequestMethod); + assertEquals(1, MY_SERVLET.ourRequestHeaders.get(Constants.HEADER_CONTENT_TYPE).size()); + assertEquals(EncodingEnum.XML.getResourceContentTypeNonLegacy() + Constants.HEADER_SUFFIX_CT_UTF_8, MY_SERVLET.ourRequestFirstHeaders.get(Constants.HEADER_CONTENT_TYPE).getValue().replace(";char", "; char")); + assertThat(MY_SERVLET.ourRequestBodyString, containsString("")); + assertEquals(ourServer.getBaseUrl() + "/fhir/Patient", MY_SERVLET.ourRequestUri); + assertEquals(ourServer.getBaseUrl() + "/fhir/Patient?name=foo", MY_SERVLET.ourRequestFirstHeaders.get(Constants.HEADER_IF_NONE_EXIST).getValue()); + assertEquals("POST", MY_SERVLET.ourRequestMethod); client.create().resource(p).conditionalByUrl("Patient?name=http://foo|bar").encodedXml().execute(); - assertEquals(1, ourRequestHeaders.get(Constants.HEADER_CONTENT_TYPE).size()); - assertEquals(EncodingEnum.XML.getResourceContentTypeNonLegacy() + Constants.HEADER_SUFFIX_CT_UTF_8, ourRequestFirstHeaders.get(Constants.HEADER_CONTENT_TYPE).getValue().replace(";char", "; char")); - assertThat(ourRequestBodyString, containsString("")); - assertEquals("http://localhost:" + ourPort + "/fhir/Patient", ourRequestUri); - assertEquals("http://localhost:" + ourPort + "/fhir/Patient?name=http%3A//foo%7Cbar", ourRequestFirstHeaders.get(Constants.HEADER_IF_NONE_EXIST).getValue()); - assertEquals("POST", ourRequestMethod); + assertEquals(1, MY_SERVLET.ourRequestHeaders.get(Constants.HEADER_CONTENT_TYPE).size()); + assertEquals(EncodingEnum.XML.getResourceContentTypeNonLegacy() + Constants.HEADER_SUFFIX_CT_UTF_8, MY_SERVLET.ourRequestFirstHeaders.get(Constants.HEADER_CONTENT_TYPE).getValue().replace(";char", "; char")); + assertThat(MY_SERVLET.ourRequestBodyString, containsString("")); + assertEquals(ourServer.getBaseUrl() + "/fhir/Patient", MY_SERVLET.ourRequestUri); + assertEquals(ourServer.getBaseUrl() + "/fhir/Patient?name=http%3A//foo%7Cbar", MY_SERVLET.ourRequestFirstHeaders.get(Constants.HEADER_IF_NONE_EXIST).getValue()); + assertEquals("POST", MY_SERVLET.ourRequestMethod); client.create().resource(p).conditional().where(Patient.NAME.matches().value("foo")).encodedXml().execute(); - assertEquals(1, ourRequestHeaders.get(Constants.HEADER_CONTENT_TYPE).size()); - assertEquals(EncodingEnum.XML.getResourceContentTypeNonLegacy() + Constants.HEADER_SUFFIX_CT_UTF_8, ourRequestFirstHeaders.get(Constants.HEADER_CONTENT_TYPE).getValue().replace(";char", "; char")); - assertThat(ourRequestBodyString, containsString("")); - assertEquals("http://localhost:" + ourPort + "/fhir/Patient", ourRequestUri); - assertEquals("http://localhost:" + ourPort + "/fhir/Patient?name=foo", ourRequestFirstHeaders.get(Constants.HEADER_IF_NONE_EXIST).getValue()); - assertEquals("POST", ourRequestMethod); + assertEquals(1, MY_SERVLET.ourRequestHeaders.get(Constants.HEADER_CONTENT_TYPE).size()); + assertEquals(EncodingEnum.XML.getResourceContentTypeNonLegacy() + Constants.HEADER_SUFFIX_CT_UTF_8, MY_SERVLET.ourRequestFirstHeaders.get(Constants.HEADER_CONTENT_TYPE).getValue().replace(";char", "; char")); + assertThat(MY_SERVLET.ourRequestBodyString, containsString("")); + assertEquals(ourServer.getBaseUrl() + "/fhir/Patient", MY_SERVLET.ourRequestUri); + assertEquals(ourServer.getBaseUrl() + "/fhir/Patient?name=foo", MY_SERVLET.ourRequestFirstHeaders.get(Constants.HEADER_IF_NONE_EXIST).getValue()); + assertEquals("POST", MY_SERVLET.ourRequestMethod); } @Test public void testCreate2() { - ourResponseStatus = Constants.STATUS_HTTP_204_NO_CONTENT; + MY_SERVLET.ourResponseStatus = Constants.STATUS_HTTP_204_NO_CONTENT; - IGenericClient client = ourCtx.newRestfulGenericClient("http://localhost:" + ourPort + "/fhir"); + IGenericClient client = ourCtx.newRestfulGenericClient(ourServer.getBaseUrl() + "/fhir"); Patient p = new Patient(); @@ -343,11 +338,11 @@ public class GenericJaxRsClientDstu3Test { client.create().resource(p).encodedXml().execute(); - assertEquals(1, ourRequestHeaders.get(Constants.HEADER_CONTENT_TYPE).size()); - assertEquals(EncodingEnum.XML.getResourceContentTypeNonLegacy() + Constants.HEADER_SUFFIX_CT_UTF_8, ourRequestFirstHeaders.get(Constants.HEADER_CONTENT_TYPE).getValue().replace(";char", "; char")); - assertThat(ourRequestBodyString, containsString("")); - assertEquals("http://localhost:" + ourPort + "/fhir/Patient", ourRequestUri); - assertEquals("POST", ourRequestMethod); + assertEquals(1, MY_SERVLET.ourRequestHeaders.get(Constants.HEADER_CONTENT_TYPE).size()); + assertEquals(EncodingEnum.XML.getResourceContentTypeNonLegacy() + Constants.HEADER_SUFFIX_CT_UTF_8, MY_SERVLET.ourRequestFirstHeaders.get(Constants.HEADER_CONTENT_TYPE).getValue().replace(";char", "; char")); + assertThat(MY_SERVLET.ourRequestBodyString, containsString("")); + assertEquals(ourServer.getBaseUrl() + "/fhir/Patient", MY_SERVLET.ourRequestUri); + assertEquals("POST", MY_SERVLET.ourRequestMethod); } @@ -355,22 +350,22 @@ public class GenericJaxRsClientDstu3Test { public void testCreatePrefer() { - ourResponseStatus = Constants.STATUS_HTTP_204_NO_CONTENT; + MY_SERVLET.ourResponseStatus = Constants.STATUS_HTTP_204_NO_CONTENT; - IGenericClient client = ourCtx.newRestfulGenericClient("http://localhost:" + ourPort + "/fhir"); + IGenericClient client = ourCtx.newRestfulGenericClient(ourServer.getBaseUrl() + "/fhir"); Patient p = new Patient(); p.addName().setFamily("FOOFAMILY"); client.create().resource(p).prefer(PreferReturnEnum.MINIMAL).execute(); - assertEquals(1, ourRequestHeaders.get(Constants.HEADER_PREFER).size()); - assertEquals(Constants.HEADER_PREFER_RETURN + '=' + Constants.HEADER_PREFER_RETURN_MINIMAL, ourRequestHeaders.get(Constants.HEADER_PREFER).get(0).getValue()); + assertEquals(1, MY_SERVLET.ourRequestHeaders.get(Constants.HEADER_PREFER).size()); + assertEquals(Constants.HEADER_PREFER_RETURN + '=' + Constants.HEADER_PREFER_RETURN_MINIMAL, MY_SERVLET.ourRequestHeaders.get(Constants.HEADER_PREFER).get(0).getValue()); client.create().resource(p).prefer(PreferReturnEnum.REPRESENTATION).execute(); - assertEquals(1, ourRequestHeaders.get(Constants.HEADER_PREFER).size()); - assertEquals(Constants.HEADER_PREFER_RETURN + '=' + Constants.HEADER_PREFER_RETURN_REPRESENTATION, ourRequestHeaders.get(Constants.HEADER_PREFER).get(0).getValue()); + assertEquals(1, MY_SERVLET.ourRequestHeaders.get(Constants.HEADER_PREFER).size()); + assertEquals(Constants.HEADER_PREFER_RETURN + '=' + Constants.HEADER_PREFER_RETURN_REPRESENTATION, MY_SERVLET.ourRequestHeaders.get(Constants.HEADER_PREFER).get(0).getValue()); } @@ -382,11 +377,11 @@ public class GenericJaxRsClientDstu3Test { final String formatted = ourCtx.newXmlParser().encodeResourceToString(p); - ourResponseStatus = Constants.STATUS_HTTP_200_OK; - ourResponseContentType = Constants.CT_FHIR_XML + "; charset=UTF-8"; - ourResponseBody = formatted; + MY_SERVLET.ourResponseStatus = Constants.STATUS_HTTP_200_OK; + MY_SERVLET.ourResponseContentType = Constants.CT_FHIR_XML + "; charset=UTF-8"; + MY_SERVLET.ourResponseBody = formatted; - IGenericClient client = ourCtx.newRestfulGenericClient("http://localhost:" + ourPort + "/fhir"); + IGenericClient client = ourCtx.newRestfulGenericClient(ourServer.getBaseUrl() + "/fhir"); p = new Patient(); p.setId(new IdType("1")); @@ -399,24 +394,24 @@ public class GenericJaxRsClientDstu3Test { @Test public void testDeleteConditional() { - ourResponseStatus = Constants.STATUS_HTTP_204_NO_CONTENT; + MY_SERVLET.ourResponseStatus = Constants.STATUS_HTTP_204_NO_CONTENT; - IGenericClient client = ourCtx.newRestfulGenericClient("http://localhost:" + ourPort + "/fhir"); + IGenericClient client = ourCtx.newRestfulGenericClient(ourServer.getBaseUrl() + "/fhir"); client.delete().resourceById(new IdType("Patient/123")).execute(); - assertEquals("DELETE", ourRequestMethod); - assertEquals("http://localhost:" + ourPort + "/fhir/Patient/123", ourRequestUri); + assertEquals("DELETE", MY_SERVLET.ourRequestMethod); + assertEquals(ourServer.getBaseUrl() + "/fhir/Patient/123", MY_SERVLET.ourRequestUri); client.delete().resourceConditionalByUrl("Patient?name=foo").execute(); - assertEquals("DELETE", ourRequestMethod); - assertEquals("http://localhost:" + ourPort + "/fhir/Patient?name=foo", ourRequestUri); + assertEquals("DELETE", MY_SERVLET.ourRequestMethod); + assertEquals(ourServer.getBaseUrl() + "/fhir/Patient?name=foo", MY_SERVLET.ourRequestUri); client.delete().resourceConditionalByType("Patient").where(Patient.NAME.matches().value("foo")).execute(); - assertEquals("DELETE", ourRequestMethod); - assertEquals("http://localhost:" + ourPort + "/fhir/Patient?name=foo", ourRequestUri); + assertEquals("DELETE", MY_SERVLET.ourRequestMethod); + assertEquals(ourServer.getBaseUrl() + "/fhir/Patient?name=foo", MY_SERVLET.ourRequestUri); } @@ -424,14 +419,14 @@ public class GenericJaxRsClientDstu3Test { @SuppressWarnings("deprecation") @Test public void testDeleteNonFluent() { - ourResponseStatus = Constants.STATUS_HTTP_204_NO_CONTENT; + MY_SERVLET.ourResponseStatus = Constants.STATUS_HTTP_204_NO_CONTENT; - IGenericClient client = ourCtx.newRestfulGenericClient("http://localhost:" + ourPort + "/fhir"); + IGenericClient client = ourCtx.newRestfulGenericClient(ourServer.getBaseUrl() + "/fhir"); client.delete().resourceById(new IdType("Patient/123")).execute(); - assertEquals("DELETE", ourRequestMethod); - assertEquals("http://localhost:" + ourPort + "/fhir/Patient/123", ourRequestUri); + assertEquals("DELETE", MY_SERVLET.ourRequestMethod); + assertEquals(ourServer.getBaseUrl() + "/fhir/Patient/123", MY_SERVLET.ourRequestUri); } @@ -440,10 +435,10 @@ public class GenericJaxRsClientDstu3Test { final String msg = getPatientFeedWithOneResult(); - ourResponseContentType = Constants.CT_FHIR_XML + "; charset=UTF-8"; - ourResponseBody = msg; + MY_SERVLET.ourResponseContentType = Constants.CT_FHIR_XML + "; charset=UTF-8"; + MY_SERVLET.ourResponseBody = msg; - IGenericClient client = ourCtx.newRestfulGenericClient("http://localhost:" + ourPort + "/fhir"); + IGenericClient client = ourCtx.newRestfulGenericClient(ourServer.getBaseUrl() + "/fhir"); org.hl7.fhir.dstu3.model.Bundle response; @@ -454,7 +449,7 @@ public class GenericJaxRsClientDstu3Test { .andReturnBundle(org.hl7.fhir.dstu3.model.Bundle.class) .execute(); //@formatter:on - assertEquals("http://localhost:" + ourPort + "/fhir/_history", ourRequestUri); + assertEquals(ourServer.getBaseUrl() + "/fhir/_history", MY_SERVLET.ourRequestUri); assertEquals(1, response.getEntry().size()); @@ -467,7 +462,7 @@ public class GenericJaxRsClientDstu3Test { .count(null) .execute(); //@formatter:on - assertEquals("http://localhost:" + ourPort + "/fhir/_history", ourRequestUri); + assertEquals(ourServer.getBaseUrl() + "/fhir/_history", MY_SERVLET.ourRequestUri); assertEquals(1, response.getEntry().size()); @@ -479,7 +474,7 @@ public class GenericJaxRsClientDstu3Test { .since(new InstantType()) .execute(); //@formatter:on - assertEquals("http://localhost:" + ourPort + "/fhir/_history", ourRequestUri); + assertEquals(ourServer.getBaseUrl() + "/fhir/_history", MY_SERVLET.ourRequestUri); assertEquals(1, response.getEntry().size()); @@ -490,7 +485,7 @@ public class GenericJaxRsClientDstu3Test { .andReturnBundle(org.hl7.fhir.dstu3.model.Bundle.class) .execute(); //@formatter:on - assertEquals("http://localhost:" + ourPort + "/fhir/Patient/_history", ourRequestUri); + assertEquals(ourServer.getBaseUrl() + "/fhir/Patient/_history", MY_SERVLET.ourRequestUri); assertEquals(1, response.getEntry().size()); @@ -501,7 +496,7 @@ public class GenericJaxRsClientDstu3Test { .andReturnBundle(org.hl7.fhir.dstu3.model.Bundle.class) .execute(); //@formatter:on - assertEquals("http://localhost:" + ourPort + "/fhir/Patient/123/_history", ourRequestUri); + assertEquals(ourServer.getBaseUrl() + "/fhir/Patient/123/_history", MY_SERVLET.ourRequestUri); assertEquals(1, response.getEntry().size()); @@ -514,7 +509,7 @@ public class GenericJaxRsClientDstu3Test { .since(new InstantType("2001-01-02T11:22:33Z")) .execute(); //@formatter:on - assertThat(ourRequestUri, either(equalTo("http://localhost:" + ourPort + "/fhir/Patient/123/_history?_since=2001-01-02T11:22:33Z&_count=123")).or(equalTo("http://localhost:" + ourPort + "/fhir/Patient/123/_history?_count=123&_since=2001-01-02T11:22:33Z"))); + assertThat(MY_SERVLET.ourRequestUri, either(equalTo(ourServer.getBaseUrl() + "/fhir/Patient/123/_history?_since=2001-01-02T11:22:33Z&_count=123")).or(equalTo(ourServer.getBaseUrl() + "/fhir/Patient/123/_history?_count=123&_since=2001-01-02T11:22:33Z"))); assertEquals(1, response.getEntry().size()); @@ -526,7 +521,7 @@ public class GenericJaxRsClientDstu3Test { .since(new InstantType("2001-01-02T11:22:33Z").getValue()) .execute(); //@formatter:on - assertThat(ourRequestUri, containsString("_since=2001-01")); + assertThat(MY_SERVLET.ourRequestUri, containsString("_since=2001-01")); assertEquals(1, response.getEntry().size()); } @@ -541,10 +536,10 @@ public class GenericJaxRsClientDstu3Test { outParams.addParameter().setName("meta").setValue(new Meta().addProfile("urn:profile:out")); final String respString = p.encodeResourceToString(outParams); - ourResponseContentType = Constants.CT_FHIR_XML + "; charset=UTF-8"; - ourResponseBody = respString; + MY_SERVLET.ourResponseContentType = Constants.CT_FHIR_XML + "; charset=UTF-8"; + MY_SERVLET.ourResponseBody = respString; - IGenericClient client = ourCtx.newRestfulGenericClient("http://localhost:" + ourPort + "/fhir"); + IGenericClient client = ourCtx.newRestfulGenericClient(ourServer.getBaseUrl() + "/fhir"); //@formatter:off @@ -556,10 +551,10 @@ public class GenericJaxRsClientDstu3Test { .encodedXml() .execute(); //@formatter:on - assertEquals("http://localhost:" + ourPort + "/fhir/Patient/123/$meta-add", ourRequestUri); + assertEquals(ourServer.getBaseUrl() + "/fhir/Patient/123/$meta-add", MY_SERVLET.ourRequestUri); assertEquals("urn:profile:out", resp.getProfile().get(0).getValue()); - assertEquals("POST", ourRequestMethod); - assertEquals("", ourRequestBodyString); + assertEquals("POST", MY_SERVLET.ourRequestMethod); + assertEquals("", MY_SERVLET.ourRequestBodyString); } @@ -575,10 +570,10 @@ public class GenericJaxRsClientDstu3Test { outParams.addParameter().setName("meta").setValue(new Meta().addProfile("urn:profile:out")); final String respString = p.encodeResourceToString(outParams); - ourResponseContentType = Constants.CT_FHIR_XML + "; charset=UTF-8"; - ourResponseBody = respString; + MY_SERVLET.ourResponseContentType = Constants.CT_FHIR_XML + "; charset=UTF-8"; + MY_SERVLET.ourResponseBody = respString; - IGenericClient client = ourCtx.newRestfulGenericClient("http://localhost:" + ourPort + "/fhir"); + IGenericClient client = ourCtx.newRestfulGenericClient(ourServer.getBaseUrl() + "/fhir"); //@formatter:off @@ -588,9 +583,9 @@ public class GenericJaxRsClientDstu3Test { .fromServer() .execute(); //@formatter:on - assertEquals("http://localhost:" + ourPort + "/fhir/$meta", ourRequestUri); + assertEquals(ourServer.getBaseUrl() + "/fhir/$meta", MY_SERVLET.ourRequestUri); assertEquals("urn:profile:out", resp.getProfile().get(0).getValue()); - assertEquals("GET", ourRequestMethod); + assertEquals("GET", MY_SERVLET.ourRequestMethod); //@formatter:off @@ -600,9 +595,9 @@ public class GenericJaxRsClientDstu3Test { .fromType("Patient") .execute(); //@formatter:on - assertEquals("http://localhost:" + ourPort + "/fhir/Patient/$meta", ourRequestUri); + assertEquals(ourServer.getBaseUrl() + "/fhir/Patient/$meta", MY_SERVLET.ourRequestUri); assertEquals("urn:profile:out", resp.getProfile().get(0).getValue()); - assertEquals("GET", ourRequestMethod); + assertEquals("GET", MY_SERVLET.ourRequestMethod); //@formatter:off @@ -612,9 +607,9 @@ public class GenericJaxRsClientDstu3Test { .fromResource(new IdType("Patient/123")) .execute(); //@formatter:on - assertEquals("http://localhost:" + ourPort + "/fhir/Patient/123/$meta", ourRequestUri); + assertEquals(ourServer.getBaseUrl() + "/fhir/Patient/123/$meta", MY_SERVLET.ourRequestUri); assertEquals("urn:profile:out", resp.getProfile().get(0).getValue()); - assertEquals("GET", ourRequestMethod); + assertEquals("GET", MY_SERVLET.ourRequestMethod); } @@ -634,10 +629,10 @@ public class GenericJaxRsClientDstu3Test { final String respString = p.encodeResourceToString(outParams); - ourResponseContentType = Constants.CT_FHIR_XML + "; charset=UTF-8"; - ourResponseBody = respString; + MY_SERVLET.ourResponseContentType = Constants.CT_FHIR_XML + "; charset=UTF-8"; + MY_SERVLET.ourResponseBody = respString; - IGenericClient client = ourCtx.newRestfulGenericClient("http://localhost:" + ourPort + "/fhir"); + IGenericClient client = ourCtx.newRestfulGenericClient(ourServer.getBaseUrl() + "/fhir"); //@formatter:off @@ -649,9 +644,9 @@ public class GenericJaxRsClientDstu3Test { .useHttpGet() .execute(); //@formatter:on - assertEquals("http://localhost:" + ourPort + "/fhir/$SOMEOPERATION?param1=STRINGVALIN1¶m1=STRINGVALIN1b¶m2=STRINGVALIN2", ourRequestUri); + assertEquals(ourServer.getBaseUrl() + "/fhir/$SOMEOPERATION?param1=STRINGVALIN1¶m1=STRINGVALIN1b¶m2=STRINGVALIN2", MY_SERVLET.ourRequestUri); assertEquals(respString, p.encodeResourceToString(resp)); - assertEquals("GET", ourRequestMethod); + assertEquals("GET", MY_SERVLET.ourRequestMethod); //@formatter:off @@ -663,9 +658,9 @@ public class GenericJaxRsClientDstu3Test { .useHttpGet() .execute(); //@formatter:on - assertEquals("http://localhost:" + ourPort + "/fhir/Patient/$SOMEOPERATION?param1=STRINGVALIN1¶m1=STRINGVALIN1b¶m2=STRINGVALIN2", ourRequestUri); + assertEquals(ourServer.getBaseUrl() + "/fhir/Patient/$SOMEOPERATION?param1=STRINGVALIN1¶m1=STRINGVALIN1b¶m2=STRINGVALIN2", MY_SERVLET.ourRequestUri); assertEquals(respString, p.encodeResourceToString(resp)); - assertEquals("GET", ourRequestMethod); + assertEquals("GET", MY_SERVLET.ourRequestMethod); //@formatter:off @@ -677,9 +672,9 @@ public class GenericJaxRsClientDstu3Test { .useHttpGet() .execute(); //@formatter:on - assertEquals("http://localhost:" + ourPort + "/fhir/Patient/123/$SOMEOPERATION?param1=STRINGVALIN1¶m1=STRINGVALIN1b¶m2=STRINGVALIN2", ourRequestUri); + assertEquals(ourServer.getBaseUrl() + "/fhir/Patient/123/$SOMEOPERATION?param1=STRINGVALIN1¶m1=STRINGVALIN1b¶m2=STRINGVALIN2", MY_SERVLET.ourRequestUri); assertEquals(respString, p.encodeResourceToString(resp)); - assertEquals("GET", ourRequestMethod); + assertEquals("GET", MY_SERVLET.ourRequestMethod); // @formatter:off @@ -691,7 +686,7 @@ public class GenericJaxRsClientDstu3Test { .useHttpGet() .execute(); // @formatter:on - assertEquals("http://localhost:" + ourPort + "/fhir/Patient/123/$SOMEOPERATION?param1=STRINGVALIN1¶m1=STRINGVALIN1b¶m2=STRINGVALIN2", ourRequestUri); + assertEquals(ourServer.getBaseUrl() + "/fhir/Patient/123/$SOMEOPERATION?param1=STRINGVALIN1¶m1=STRINGVALIN1b¶m2=STRINGVALIN2", MY_SERVLET.ourRequestUri); } @@ -704,10 +699,10 @@ public class GenericJaxRsClientDstu3Test { outParams.addParameter().setValue(new StringType("STRINGVALOUT2")); final String respString = p.encodeResourceToString(outParams); - ourResponseContentType = Constants.CT_FHIR_XML + "; charset=UTF-8"; - ourResponseBody = respString; + MY_SERVLET.ourResponseContentType = Constants.CT_FHIR_XML + "; charset=UTF-8"; + MY_SERVLET.ourResponseBody = respString; - IGenericClient client = ourCtx.newRestfulGenericClient("http://localhost:" + ourPort + "/fhir"); + IGenericClient client = ourCtx.newRestfulGenericClient(ourServer.getBaseUrl() + "/fhir"); //@formatter:off @@ -719,9 +714,9 @@ public class GenericJaxRsClientDstu3Test { .useHttpGet() .execute(); //@formatter:on - assertEquals("http://localhost:" + ourPort + "/fhir/$SOMEOPERATION", ourRequestUri); + assertEquals(ourServer.getBaseUrl() + "/fhir/$SOMEOPERATION", MY_SERVLET.ourRequestUri); assertEquals(respString, p.encodeResourceToString(resp)); - assertEquals("GET", ourRequestMethod); + assertEquals("GET", MY_SERVLET.ourRequestMethod); //@formatter:off @@ -733,9 +728,9 @@ public class GenericJaxRsClientDstu3Test { .useHttpGet() .execute(); //@formatter:on - assertEquals("http://localhost:" + ourPort + "/fhir/Patient/$SOMEOPERATION", ourRequestUri); + assertEquals(ourServer.getBaseUrl() + "/fhir/Patient/$SOMEOPERATION", MY_SERVLET.ourRequestUri); assertEquals(respString, p.encodeResourceToString(resp)); - assertEquals("GET", ourRequestMethod); + assertEquals("GET", MY_SERVLET.ourRequestMethod); //@formatter:off @@ -747,9 +742,9 @@ public class GenericJaxRsClientDstu3Test { .useHttpGet() .execute(); //@formatter:on - assertEquals("http://localhost:" + ourPort + "/fhir/Patient/123/$SOMEOPERATION", ourRequestUri); + assertEquals(ourServer.getBaseUrl() + "/fhir/Patient/123/$SOMEOPERATION", MY_SERVLET.ourRequestUri); assertEquals(respString, p.encodeResourceToString(resp)); - assertEquals("GET", ourRequestMethod); + assertEquals("GET", MY_SERVLET.ourRequestMethod); // @formatter:off @@ -761,18 +756,18 @@ public class GenericJaxRsClientDstu3Test { .useHttpGet() .execute(); // @formatter:on - assertEquals("http://localhost:" + ourPort + "/fhir/Patient/123/$SOMEOPERATION", ourRequestUri); + assertEquals(ourServer.getBaseUrl() + "/fhir/Patient/123/$SOMEOPERATION", MY_SERVLET.ourRequestUri); } @Test public void testOperationWithBundleResponseJson() { - ourResponseContentType = Constants.CT_FHIR_JSON; - final String respString = "{\n" + " \"resourceType\":\"Bundle\",\n" + " \"id\":\"8cef5f2a-0ba9-43a5-be26-c8dde9ff0e19\",\n" + " \"base\":\"http://localhost:" + ourPort + "/fhir\"\n" + "}"; - ourResponseBody = respString; + MY_SERVLET.ourResponseContentType = Constants.CT_FHIR_JSON; + final String respString = "{\n" + " \"resourceType\":\"Bundle\",\n" + " \"id\":\"8cef5f2a-0ba9-43a5-be26-c8dde9ff0e19\",\n" + " \"base\":\"" + ourServer.getBaseUrl() + "/fhir\"\n" + "}"; + MY_SERVLET.ourResponseBody = respString; - IGenericClient client = ourCtx.newRestfulGenericClient("http://localhost:" + ourPort + "/fhir"); + IGenericClient client = ourCtx.newRestfulGenericClient(ourServer.getBaseUrl() + "/fhir"); client.registerInterceptor(new LoggingInterceptor(true)); @@ -808,10 +803,10 @@ public class GenericJaxRsClientDstu3Test { outParams.setTotal(123); final String respString = p.encodeResourceToString(outParams); - ourResponseContentType = Constants.CT_FHIR_XML + "; charset=UTF-8"; - ourResponseBody = respString; + MY_SERVLET.ourResponseContentType = Constants.CT_FHIR_XML + "; charset=UTF-8"; + MY_SERVLET.ourResponseBody = respString; - IGenericClient client = ourCtx.newRestfulGenericClient("http://localhost:" + ourPort + "/fhir"); + IGenericClient client = ourCtx.newRestfulGenericClient(ourServer.getBaseUrl() + "/fhir"); //@formatter:off @@ -823,11 +818,11 @@ public class GenericJaxRsClientDstu3Test { .encodedXml() .execute(); //@formatter:on - assertEquals("http://localhost:" + ourPort + "/fhir/$SOMEOPERATION", ourRequestUri); - assertEquals(1, ourRequestHeaders.get(Constants.HEADER_CONTENT_TYPE).size()); - assertEquals(EncodingEnum.XML.getResourceContentTypeNonLegacy() + Constants.HEADER_SUFFIX_CT_UTF_8, ourRequestFirstHeaders.get(Constants.HEADER_CONTENT_TYPE).getValue().replace(";char", "; char")); - assertEquals(ourRequestBodyString, reqString); - assertEquals("POST", ourRequestMethod); + assertEquals(ourServer.getBaseUrl() + "/fhir/$SOMEOPERATION", MY_SERVLET.ourRequestUri); + assertEquals(1, MY_SERVLET.ourRequestHeaders.get(Constants.HEADER_CONTENT_TYPE).size()); + assertEquals(EncodingEnum.XML.getResourceContentTypeNonLegacy() + Constants.HEADER_SUFFIX_CT_UTF_8, MY_SERVLET.ourRequestFirstHeaders.get(Constants.HEADER_CONTENT_TYPE).getValue().replace(";char", "; char")); + assertEquals(MY_SERVLET.ourRequestBodyString, reqString); + assertEquals("POST", MY_SERVLET.ourRequestMethod); assertEquals(1, resp.getParameter().size()); assertEquals(org.hl7.fhir.dstu3.model.Bundle.class, resp.getParameter().get(0).getResource().getClass()); @@ -842,10 +837,10 @@ public class GenericJaxRsClientDstu3Test { outParams.addParameter().setValue(new StringType("STRINGVALOUT2")); final String respString = p.encodeResourceToString(outParams); - ourResponseContentType = Constants.CT_FHIR_XML + "; charset=UTF-8"; - ourResponseBody = respString; + MY_SERVLET.ourResponseContentType = Constants.CT_FHIR_XML + "; charset=UTF-8"; + MY_SERVLET.ourResponseBody = respString; - IGenericClient client = ourCtx.newRestfulGenericClient("http://localhost:" + ourPort + "/fhir"); + IGenericClient client = ourCtx.newRestfulGenericClient(ourServer.getBaseUrl() + "/fhir"); //@formatter:off @@ -858,12 +853,12 @@ public class GenericJaxRsClientDstu3Test { .encodedXml() .execute(); //@formatter:on - assertEquals("http://localhost:" + ourPort + "/fhir/$SOMEOPERATION", ourRequestUri); + assertEquals(ourServer.getBaseUrl() + "/fhir/$SOMEOPERATION", MY_SERVLET.ourRequestUri); assertEquals(respString, p.encodeResourceToString(resp)); - assertEquals(1, ourRequestHeaders.get(Constants.HEADER_CONTENT_TYPE).size()); - assertEquals(EncodingEnum.XML.getResourceContentTypeNonLegacy() + Constants.HEADER_SUFFIX_CT_UTF_8, ourRequestFirstHeaders.get(Constants.HEADER_CONTENT_TYPE).getValue().replace(";char", "; char")); - assertEquals("POST", ourRequestMethod); - assertEquals("", (ourRequestBodyString)); + assertEquals(1, MY_SERVLET.ourRequestHeaders.get(Constants.HEADER_CONTENT_TYPE).size()); + assertEquals(EncodingEnum.XML.getResourceContentTypeNonLegacy() + Constants.HEADER_SUFFIX_CT_UTF_8, MY_SERVLET.ourRequestFirstHeaders.get(Constants.HEADER_CONTENT_TYPE).getValue().replace(";char", "; char")); + assertEquals("POST", MY_SERVLET.ourRequestMethod); + assertEquals("", (MY_SERVLET.ourRequestBodyString)); /* @@ -880,13 +875,13 @@ public class GenericJaxRsClientDstu3Test { .encodedXml() .execute(); //@formatter:on - assertEquals("http://localhost:" + ourPort + "/fhir/$SOMEOPERATION", ourRequestUri); + assertEquals(ourServer.getBaseUrl() + "/fhir/$SOMEOPERATION", MY_SERVLET.ourRequestUri); assertEquals(respString, p.encodeResourceToString(resp)); - assertEquals(1, ourRequestHeaders.get(Constants.HEADER_CONTENT_TYPE).size()); - assertEquals(EncodingEnum.XML.getResourceContentTypeNonLegacy() + Constants.HEADER_SUFFIX_CT_UTF_8, ourRequestFirstHeaders.get(Constants.HEADER_CONTENT_TYPE).getValue().replace(";char", "; char")); - assertEquals("POST", ourRequestMethod); + assertEquals(1, MY_SERVLET.ourRequestHeaders.get(Constants.HEADER_CONTENT_TYPE).size()); + assertEquals(EncodingEnum.XML.getResourceContentTypeNonLegacy() + Constants.HEADER_SUFFIX_CT_UTF_8, MY_SERVLET.ourRequestFirstHeaders.get(Constants.HEADER_CONTENT_TYPE).getValue().replace(";char", "; char")); + assertEquals("POST", MY_SERVLET.ourRequestMethod); assertEquals("", - (ourRequestBodyString)); + (MY_SERVLET.ourRequestBodyString)); /* @@ -903,21 +898,21 @@ public class GenericJaxRsClientDstu3Test { .encodedXml() .execute(); //@formatter:on - assertEquals("http://localhost:" + ourPort + "/fhir/$SOMEOPERATION", ourRequestUri); + assertEquals(ourServer.getBaseUrl() + "/fhir/$SOMEOPERATION", MY_SERVLET.ourRequestUri); assertEquals(respString, p.encodeResourceToString(resp)); - assertEquals(1, ourRequestHeaders.get(Constants.HEADER_CONTENT_TYPE).size()); - assertEquals(EncodingEnum.XML.getResourceContentTypeNonLegacy() + Constants.HEADER_SUFFIX_CT_UTF_8, ourRequestFirstHeaders.get(Constants.HEADER_CONTENT_TYPE).getValue().replace(";char", "; char")); - assertEquals("POST", ourRequestMethod); + assertEquals(1, MY_SERVLET.ourRequestHeaders.get(Constants.HEADER_CONTENT_TYPE).size()); + assertEquals(EncodingEnum.XML.getResourceContentTypeNonLegacy() + Constants.HEADER_SUFFIX_CT_UTF_8, MY_SERVLET.ourRequestFirstHeaders.get(Constants.HEADER_CONTENT_TYPE).getValue().replace(";char", "; char")); + assertEquals("POST", MY_SERVLET.ourRequestMethod); assertEquals( "", - (ourRequestBodyString)); + (MY_SERVLET.ourRequestBodyString)); } @Test public void testOperationWithInvalidParam() { - IGenericClient client = ourCtx.newRestfulGenericClient("http://localhost:" + ourPort + "/fhir"); + IGenericClient client = ourCtx.newRestfulGenericClient(ourServer.getBaseUrl() + "/fhir"); // Who knows what the heck this is! IBase weirdBase = new IBase() { @@ -974,10 +969,10 @@ public class GenericJaxRsClientDstu3Test { final String respString = p.encodeResourceToString(outParams); - ourResponseContentType = Constants.CT_FHIR_XML + "; charset=UTF-8"; - ourResponseBody = respString; + MY_SERVLET.ourResponseContentType = Constants.CT_FHIR_XML + "; charset=UTF-8"; + MY_SERVLET.ourResponseBody = respString; - IGenericClient client = ourCtx.newRestfulGenericClient("http://localhost:" + ourPort + "/fhir"); + IGenericClient client = ourCtx.newRestfulGenericClient(ourServer.getBaseUrl() + "/fhir"); //@formatter:off @@ -991,7 +986,7 @@ public class GenericJaxRsClientDstu3Test { .execute(); //@formatter:off - assertEquals("http://localhost:" + ourPort + "/fhir/Patient/1/$validate-code?code=8495-4&system=http%3A%2F%2Floinc.org", ourRequestUri); + assertEquals(ourServer.getBaseUrl() + "/fhir/Patient/1/$validate-code?code=8495-4&system=http%3A%2F%2Floinc.org", MY_SERVLET.ourRequestUri); //@formatter:off @@ -1005,9 +1000,9 @@ public class GenericJaxRsClientDstu3Test { .execute(); //@formatter:off - assertEquals("http://localhost:" + ourPort + "/fhir/Patient/1/$validate-code", ourRequestUri); - ourLog.info(ourRequestBodyString); - assertEquals("", ourRequestBodyString); + assertEquals(ourServer.getBaseUrl() + "/fhir/Patient/1/$validate-code", MY_SERVLET.ourRequestUri); + ourLog.info(MY_SERVLET.ourRequestBodyString); + assertEquals("", MY_SERVLET.ourRequestBodyString); } @@ -1026,10 +1021,10 @@ public class GenericJaxRsClientDstu3Test { final String respString = p.encodeResourceToString(outParams); - ourResponseContentType = Constants.CT_FHIR_XML + "; charset=UTF-8"; - ourResponseBody = respString; + MY_SERVLET.ourResponseContentType = Constants.CT_FHIR_XML + "; charset=UTF-8"; + MY_SERVLET.ourResponseBody = respString; - IGenericClient client = ourCtx.newRestfulGenericClient("http://localhost:" + ourPort + "/fhir"); + IGenericClient client = ourCtx.newRestfulGenericClient(ourServer.getBaseUrl() + "/fhir"); //@formatter:off @@ -1041,12 +1036,12 @@ public class GenericJaxRsClientDstu3Test { .encodedXml() .execute(); //@formatter:on - assertEquals("http://localhost:" + ourPort + "/fhir/$SOMEOPERATION", ourRequestUri); + assertEquals(ourServer.getBaseUrl() + "/fhir/$SOMEOPERATION", MY_SERVLET.ourRequestUri); assertEquals(respString, p.encodeResourceToString(resp)); - assertEquals(1, ourRequestHeaders.get(Constants.HEADER_CONTENT_TYPE).size()); - assertEquals(EncodingEnum.XML.getResourceContentTypeNonLegacy() + Constants.HEADER_SUFFIX_CT_UTF_8, ourRequestFirstHeaders.get(Constants.HEADER_CONTENT_TYPE).getValue().replace(";char", "; char")); - assertEquals(ourRequestBodyString, reqString); - assertEquals("POST", ourRequestMethod); + assertEquals(1, MY_SERVLET.ourRequestHeaders.get(Constants.HEADER_CONTENT_TYPE).size()); + assertEquals(EncodingEnum.XML.getResourceContentTypeNonLegacy() + Constants.HEADER_SUFFIX_CT_UTF_8, MY_SERVLET.ourRequestFirstHeaders.get(Constants.HEADER_CONTENT_TYPE).getValue().replace(";char", "; char")); + assertEquals(MY_SERVLET.ourRequestBodyString, reqString); + assertEquals("POST", MY_SERVLET.ourRequestMethod); //@formatter:off @@ -1058,12 +1053,12 @@ public class GenericJaxRsClientDstu3Test { .encodedXml() .execute(); //@formatter:on - assertEquals("http://localhost:" + ourPort + "/fhir/Patient/$SOMEOPERATION", ourRequestUri); + assertEquals(ourServer.getBaseUrl() + "/fhir/Patient/$SOMEOPERATION", MY_SERVLET.ourRequestUri); assertEquals(respString, p.encodeResourceToString(resp)); - assertEquals(1, ourRequestHeaders.get(Constants.HEADER_CONTENT_TYPE).size()); - assertEquals(EncodingEnum.XML.getResourceContentTypeNonLegacy() + Constants.HEADER_SUFFIX_CT_UTF_8, ourRequestFirstHeaders.get(Constants.HEADER_CONTENT_TYPE).getValue().replace(";char", "; char")); - assertEquals(ourRequestBodyString, reqString); - assertEquals("POST", ourRequestMethod); + assertEquals(1, MY_SERVLET.ourRequestHeaders.get(Constants.HEADER_CONTENT_TYPE).size()); + assertEquals(EncodingEnum.XML.getResourceContentTypeNonLegacy() + Constants.HEADER_SUFFIX_CT_UTF_8, MY_SERVLET.ourRequestFirstHeaders.get(Constants.HEADER_CONTENT_TYPE).getValue().replace(";char", "; char")); + assertEquals(MY_SERVLET.ourRequestBodyString, reqString); + assertEquals("POST", MY_SERVLET.ourRequestMethod); //@formatter:off @@ -1075,17 +1070,17 @@ public class GenericJaxRsClientDstu3Test { .encodedXml() .execute(); //@formatter:on - assertEquals("http://localhost:" + ourPort + "/fhir/Patient/123/$SOMEOPERATION", ourRequestUri); + assertEquals(ourServer.getBaseUrl() + "/fhir/Patient/123/$SOMEOPERATION", MY_SERVLET.ourRequestUri); assertEquals(respString, p.encodeResourceToString(resp)); - assertEquals(1, ourRequestHeaders.get(Constants.HEADER_CONTENT_TYPE).size()); - assertEquals(EncodingEnum.XML.getResourceContentTypeNonLegacy() + Constants.HEADER_SUFFIX_CT_UTF_8, ourRequestFirstHeaders.get(Constants.HEADER_CONTENT_TYPE).getValue().replace(";char", "; char")); - assertEquals(ourRequestBodyString, reqString); - assertEquals("POST", ourRequestMethod); + assertEquals(1, MY_SERVLET.ourRequestHeaders.get(Constants.HEADER_CONTENT_TYPE).size()); + assertEquals(EncodingEnum.XML.getResourceContentTypeNonLegacy() + Constants.HEADER_SUFFIX_CT_UTF_8, MY_SERVLET.ourRequestFirstHeaders.get(Constants.HEADER_CONTENT_TYPE).getValue().replace(";char", "; char")); + assertEquals(MY_SERVLET.ourRequestBodyString, reqString); + assertEquals("POST", MY_SERVLET.ourRequestMethod); resp = client.operation().onInstance(new IdType("http://foo.com/bar/baz/Patient/123/_history/22")).named("$SOMEOPERATION").withParameters(inParams).execute(); // @formatter:on - assertEquals("http://localhost:" + ourPort + "/fhir/Patient/123/$SOMEOPERATION", ourRequestUri); + assertEquals(ourServer.getBaseUrl() + "/fhir/Patient/123/$SOMEOPERATION", MY_SERVLET.ourRequestUri); } @@ -1102,10 +1097,10 @@ public class GenericJaxRsClientDstu3Test { final String respString = p.encodeResourceToString(outParams); - ourResponseContentType = Constants.CT_FHIR_XML + "; charset=UTF-8"; - ourResponseBody = respString; + MY_SERVLET.ourResponseContentType = Constants.CT_FHIR_XML + "; charset=UTF-8"; + MY_SERVLET.ourResponseBody = respString; - IGenericClient client = ourCtx.newRestfulGenericClient("http://localhost:" + ourPort + "/fhir"); + IGenericClient client = ourCtx.newRestfulGenericClient(ourServer.getBaseUrl() + "/fhir"); //@formatter:off @@ -1115,12 +1110,12 @@ public class GenericJaxRsClientDstu3Test { .named("$SOMEOPERATION") .withNoParameters(Parameters.class).encodedXml().execute(); //@formatter:on - assertEquals("http://localhost:" + ourPort + "/fhir/$SOMEOPERATION", ourRequestUri); + assertEquals(ourServer.getBaseUrl() + "/fhir/$SOMEOPERATION", MY_SERVLET.ourRequestUri); assertEquals(respString, p.encodeResourceToString(resp)); - assertEquals(1, ourRequestHeaders.get(Constants.HEADER_CONTENT_TYPE).size()); - assertEquals(EncodingEnum.XML.getResourceContentTypeNonLegacy() + Constants.HEADER_SUFFIX_CT_UTF_8, ourRequestFirstHeaders.get(Constants.HEADER_CONTENT_TYPE).getValue().replace(";char", "; char")); - assertEquals(ourRequestBodyString, reqString); - assertEquals("POST", ourRequestMethod); + assertEquals(1, MY_SERVLET.ourRequestHeaders.get(Constants.HEADER_CONTENT_TYPE).size()); + assertEquals(EncodingEnum.XML.getResourceContentTypeNonLegacy() + Constants.HEADER_SUFFIX_CT_UTF_8, MY_SERVLET.ourRequestFirstHeaders.get(Constants.HEADER_CONTENT_TYPE).getValue().replace(";char", "; char")); + assertEquals(MY_SERVLET.ourRequestBodyString, reqString); + assertEquals("POST", MY_SERVLET.ourRequestMethod); //@formatter:off @@ -1130,12 +1125,12 @@ public class GenericJaxRsClientDstu3Test { .named("$SOMEOPERATION") .withNoParameters(Parameters.class).encodedXml().execute(); //@formatter:on - assertEquals("http://localhost:" + ourPort + "/fhir/Patient/$SOMEOPERATION", ourRequestUri); + assertEquals(ourServer.getBaseUrl() + "/fhir/Patient/$SOMEOPERATION", MY_SERVLET.ourRequestUri); assertEquals(respString, p.encodeResourceToString(resp)); - assertEquals(1, ourRequestHeaders.get(Constants.HEADER_CONTENT_TYPE).size()); - assertEquals(EncodingEnum.XML.getResourceContentTypeNonLegacy() + Constants.HEADER_SUFFIX_CT_UTF_8, ourRequestFirstHeaders.get(Constants.HEADER_CONTENT_TYPE).getValue().replace(";char", "; char")); - assertEquals(ourRequestBodyString, reqString); - assertEquals("POST", ourRequestMethod); + assertEquals(1, MY_SERVLET.ourRequestHeaders.get(Constants.HEADER_CONTENT_TYPE).size()); + assertEquals(EncodingEnum.XML.getResourceContentTypeNonLegacy() + Constants.HEADER_SUFFIX_CT_UTF_8, MY_SERVLET.ourRequestFirstHeaders.get(Constants.HEADER_CONTENT_TYPE).getValue().replace(";char", "; char")); + assertEquals(MY_SERVLET.ourRequestBodyString, reqString); + assertEquals("POST", MY_SERVLET.ourRequestMethod); //@formatter:off @@ -1147,12 +1142,12 @@ public class GenericJaxRsClientDstu3Test { .encodedXml() .execute(); //@formatter:on - assertEquals("http://localhost:" + ourPort + "/fhir/Patient/123/$SOMEOPERATION", ourRequestUri); + assertEquals(ourServer.getBaseUrl() + "/fhir/Patient/123/$SOMEOPERATION", MY_SERVLET.ourRequestUri); assertEquals(respString, p.encodeResourceToString(resp)); - assertEquals(1, ourRequestHeaders.get(Constants.HEADER_CONTENT_TYPE).size()); - assertEquals(EncodingEnum.XML.getResourceContentTypeNonLegacy() + Constants.HEADER_SUFFIX_CT_UTF_8, ourRequestFirstHeaders.get(Constants.HEADER_CONTENT_TYPE).getValue().replace(";char", "; char")); - assertEquals(ourRequestBodyString, reqString); - assertEquals("POST", ourRequestMethod); + assertEquals(1, MY_SERVLET.ourRequestHeaders.get(Constants.HEADER_CONTENT_TYPE).size()); + assertEquals(EncodingEnum.XML.getResourceContentTypeNonLegacy() + Constants.HEADER_SUFFIX_CT_UTF_8, MY_SERVLET.ourRequestFirstHeaders.get(Constants.HEADER_CONTENT_TYPE).getValue().replace(";char", "; char")); + assertEquals(MY_SERVLET.ourRequestBodyString, reqString); + assertEquals("POST", MY_SERVLET.ourRequestMethod); // @formatter:off @@ -1163,22 +1158,22 @@ public class GenericJaxRsClientDstu3Test { .withNoParameters(Parameters.class) .execute(); // @formatter:on - assertEquals("http://localhost:" + ourPort + "/fhir/Patient/123/$SOMEOPERATION", ourRequestUri); + assertEquals(ourServer.getBaseUrl() + "/fhir/Patient/123/$SOMEOPERATION", MY_SERVLET.ourRequestUri); } @Test public void testPageNext() { - ourResponseContentType = Constants.CT_FHIR_XML + "; charset=UTF-8"; - ourResponseBody = getPatientFeedWithOneResult(); + MY_SERVLET.ourResponseContentType = Constants.CT_FHIR_XML + "; charset=UTF-8"; + MY_SERVLET.ourResponseBody = getPatientFeedWithOneResult(); - IGenericClient client = ourCtx.newRestfulGenericClient("http://localhost:" + ourPort + "/fhir"); + IGenericClient client = ourCtx.newRestfulGenericClient(ourServer.getBaseUrl() + "/fhir"); org.hl7.fhir.dstu3.model.Bundle sourceBundle = new org.hl7.fhir.dstu3.model.Bundle(); - sourceBundle.getLinkOrCreate(IBaseBundle.LINK_PREV).setUrl("http://localhost:" + ourPort + "/fhir/prev"); - sourceBundle.getLinkOrCreate(IBaseBundle.LINK_NEXT).setUrl("http://localhost:" + ourPort + "/fhir/next"); + sourceBundle.getLinkOrCreate(IBaseBundle.LINK_PREV).setUrl(ourServer.getBaseUrl() + "/fhir/prev"); + sourceBundle.getLinkOrCreate(IBaseBundle.LINK_NEXT).setUrl(ourServer.getBaseUrl() + "/fhir/next"); //@formatter:off org.hl7.fhir.dstu3.model.Bundle resp = client @@ -1188,14 +1183,14 @@ public class GenericJaxRsClientDstu3Test { //@formatter:on assertEquals(1, resp.getEntry().size()); - assertEquals("http://localhost:" + ourPort + "/fhir/next", ourRequestUri); + assertEquals(ourServer.getBaseUrl() + "/fhir/next", MY_SERVLET.ourRequestUri); } @Test public void testPageNextNoLink() { - IGenericClient client = ourCtx.newRestfulGenericClient("http://localhost:" + ourPort + "/fhir"); + IGenericClient client = ourCtx.newRestfulGenericClient(ourServer.getBaseUrl() + "/fhir"); org.hl7.fhir.dstu3.model.Bundle sourceBundle = new org.hl7.fhir.dstu3.model.Bundle(); try { @@ -1209,14 +1204,14 @@ public class GenericJaxRsClientDstu3Test { public void testPagePrev() { - ourResponseContentType = Constants.CT_FHIR_XML + "; charset=UTF-8"; - ourResponseBody = getPatientFeedWithOneResult(); + MY_SERVLET.ourResponseContentType = Constants.CT_FHIR_XML + "; charset=UTF-8"; + MY_SERVLET.ourResponseBody = getPatientFeedWithOneResult(); - IGenericClient client = ourCtx.newRestfulGenericClient("http://localhost:" + ourPort + "/fhir"); + IGenericClient client = ourCtx.newRestfulGenericClient(ourServer.getBaseUrl() + "/fhir"); org.hl7.fhir.dstu3.model.Bundle sourceBundle = new org.hl7.fhir.dstu3.model.Bundle(); - sourceBundle.getLinkOrCreate("previous").setUrl("http://localhost:" + ourPort + "/fhir/prev"); + sourceBundle.getLinkOrCreate("previous").setUrl(ourServer.getBaseUrl() + "/fhir/prev"); //@formatter:off org.hl7.fhir.dstu3.model.Bundle resp = client @@ -1226,7 +1221,7 @@ public class GenericJaxRsClientDstu3Test { //@formatter:on assertEquals(1, resp.getEntry().size()); - assertEquals("http://localhost:" + ourPort + "/fhir/prev", ourRequestUri); + assertEquals(ourServer.getBaseUrl() + "/fhir/prev", MY_SERVLET.ourRequestUri); /* @@ -1234,7 +1229,7 @@ public class GenericJaxRsClientDstu3Test { */ sourceBundle = new org.hl7.fhir.dstu3.model.Bundle(); - sourceBundle.getLinkOrCreate("prev").setUrl("http://localhost:" + ourPort + "/fhir/prev"); + sourceBundle.getLinkOrCreate("prev").setUrl(ourServer.getBaseUrl() + "/fhir/prev"); //@formatter:off resp = client @@ -1244,7 +1239,7 @@ public class GenericJaxRsClientDstu3Test { //@formatter:on assertEquals(1, resp.getEntry().size()); - assertEquals("http://localhost:" + ourPort + "/fhir/prev", ourRequestUri); + assertEquals(ourServer.getBaseUrl() + "/fhir/prev", MY_SERVLET.ourRequestUri); } @@ -1256,16 +1251,16 @@ public class GenericJaxRsClientDstu3Test { patient.addName().setFamily("FAM"); final String respString = ourCtx.newXmlParser().encodeResourceToString(patient); - ourResponseContentType = Constants.CT_FHIR_XML + "; charset=UTF-8"; - ourResponseBody = respString; + MY_SERVLET.ourResponseContentType = Constants.CT_FHIR_XML + "; charset=UTF-8"; + MY_SERVLET.ourResponseBody = respString; - IGenericClient client = ourCtx.newRestfulGenericClient("http://localhost:" + ourPort + "/fhir"); + IGenericClient client = ourCtx.newRestfulGenericClient(ourServer.getBaseUrl() + "/fhir"); Patient response; - response = (Patient) client.read(new UriDt("http://localhost:" + ourPort + "/fhir/Patient/123")); - assertEquals("http://localhost:" + ourPort + "/fhir/Patient/123", ourRequestUri); + response = (Patient) client.read(new UriDt(ourServer.getBaseUrl() + "/fhir/Patient/123")); + assertEquals(ourServer.getBaseUrl() + "/fhir/Patient/123", MY_SERVLET.ourRequestUri); assertEquals("FAM", response.getName().get(0).getFamily()); } @@ -1276,15 +1271,15 @@ public class GenericJaxRsClientDstu3Test { patient.addName().setFamily("FAM"); final String respString = ourCtx.newXmlParser().encodeResourceToString(patient); - ourResponseContentType = Constants.CT_FHIR_XML + "; charset=UTF-8"; - ourResponseBody = respString; + MY_SERVLET.ourResponseContentType = Constants.CT_FHIR_XML + "; charset=UTF-8"; + MY_SERVLET.ourResponseBody = respString; - IGenericClient client = ourCtx.newRestfulGenericClient("http://localhost:" + ourPort + "/fhir"); + IGenericClient client = ourCtx.newRestfulGenericClient(ourServer.getBaseUrl() + "/fhir"); Patient response; - response = client.read().resource(Patient.class).withUrl(new IdType("http://localhost:" + ourPort + "/AAA/Patient/123")).execute(); - assertEquals("http://localhost:" + ourPort + "/AAA/Patient/123", ourRequestUri); + response = client.read().resource(Patient.class).withUrl(new IdType(ourServer.getBaseUrl() + "/AAA/Patient/123")).execute(); + assertEquals(ourServer.getBaseUrl() + "/AAA/Patient/123", MY_SERVLET.ourRequestUri); assertEquals("FAM", response.getName().get(0).getFamily()); } @@ -1308,10 +1303,10 @@ public class GenericJaxRsClientDstu3Test { //@formatter:on - ourResponseContentType = Constants.CT_FHIR_XML + "; charset=UTF-8"; - ourResponseBody = input; + MY_SERVLET.ourResponseContentType = Constants.CT_FHIR_XML + "; charset=UTF-8"; + MY_SERVLET.ourResponseBody = input; - IGenericClient client = ourCtx.newRestfulGenericClient("http://localhost:" + ourPort + "/fhir"); + IGenericClient client = ourCtx.newRestfulGenericClient(ourServer.getBaseUrl() + "/fhir"); org.hl7.fhir.dstu3.model.Bundle response; @@ -1331,10 +1326,10 @@ public class GenericJaxRsClientDstu3Test { String msg = "{\"resourceType\":\"Patient\",\"id\":\"1\",\"meta\":{\"versionId\":\"1\",\"lastUpdated\":\"2014-12-20T18:41:29.706-05:00\"},\"identifier\":[{\"system\":\"urn:MultiFhirVersionTest\",\"value\":\"testSubmitPatient01\"}]}"; - ourResponseContentType = Constants.CT_FHIR_JSON + "; charset=UTF-8"; - ourResponseBody = msg; + MY_SERVLET.ourResponseContentType = Constants.CT_FHIR_JSON + "; charset=UTF-8"; + MY_SERVLET.ourResponseBody = msg; - IGenericClient client = ourCtx.newRestfulGenericClient("http://localhost:" + ourPort + "/fhir"); + IGenericClient client = ourCtx.newRestfulGenericClient(ourServer.getBaseUrl() + "/fhir"); //@formatter:off IBaseResource response = client.read() @@ -1344,7 +1339,7 @@ public class GenericJaxRsClientDstu3Test { .execute(); //@formatter:on - assertThat(ourRequestUri, either(equalTo("http://localhost:" + ourPort + "/fhir/Patient/123?_elements=name%2Cidentifier")).or(equalTo("http://localhost:" + ourPort + "/fhir/Patient/123?_elements=identifier%2Cname"))); + assertThat(MY_SERVLET.ourRequestUri, either(equalTo(ourServer.getBaseUrl() + "/fhir/Patient/123?_elements=name%2Cidentifier")).or(equalTo(ourServer.getBaseUrl() + "/fhir/Patient/123?_elements=identifier%2Cname"))); assertEquals(Patient.class, response.getClass()); } @@ -1354,11 +1349,11 @@ public class GenericJaxRsClientDstu3Test { String msg = "<>>>><<<<>"; - ourResponseContentType = Constants.CT_HTML + "; charset=UTF-8"; - ourResponseBody = msg; + MY_SERVLET.ourResponseContentType = Constants.CT_HTML + "; charset=UTF-8"; + MY_SERVLET.ourResponseBody = msg; - IGenericClient client = ourCtx.newRestfulGenericClient("http://localhost:" + ourPort + "/fhir"); + IGenericClient client = ourCtx.newRestfulGenericClient(ourServer.getBaseUrl() + "/fhir"); //@formatter:off try { @@ -1379,11 +1374,11 @@ public class GenericJaxRsClientDstu3Test { String msg = "
    HELP IM A DIV
    "; - ourResponseContentType = Constants.CT_HTML + "; charset=UTF-8"; - ourResponseBody = msg; + MY_SERVLET.ourResponseContentType = Constants.CT_HTML + "; charset=UTF-8"; + MY_SERVLET.ourResponseBody = msg; - IGenericClient client = ourCtx.newRestfulGenericClient("http://localhost:" + ourPort + "/fhir"); + IGenericClient client = ourCtx.newRestfulGenericClient(ourServer.getBaseUrl() + "/fhir"); //@formatter:off Patient response = client.read() @@ -1393,7 +1388,7 @@ public class GenericJaxRsClientDstu3Test { .execute(); //@formatter:on - assertEquals("http://localhost:" + ourPort + "/fhir/Patient/123?_summary=text", ourRequestUri); + assertEquals(ourServer.getBaseUrl() + "/fhir/Patient/123?_summary=text", MY_SERVLET.ourRequestUri); assertEquals(Patient.class, response.getClass()); assertEquals("
    HELP IM A DIV
    ", response.getText().getDiv().getValueAsString()); @@ -1404,11 +1399,11 @@ public class GenericJaxRsClientDstu3Test { String msg = "{\"resourceType\":\"Bundle\",\"id\":null,\"base\":\"http://localhost:57931/fhir/contextDev\",\"total\":1,\"link\":[{\"relation\":\"self\",\"url\":\"http://localhost:57931/fhir/contextDev/Patient?identifier=urn%3AMultiFhirVersionTest%7CtestSubmitPatient01&_format=json\"}],\"entry\":[{\"resource\":{\"resourceType\":\"Patient\",\"id\":\"1\",\"meta\":{\"versionId\":\"1\",\"lastUpdated\":\"2014-12-20T18:41:29.706-05:00\"},\"identifier\":[{\"system\":\"urn:MultiFhirVersionTest\",\"value\":\"testSubmitPatient01\"}]}}]}"; - ourResponseContentType = Constants.CT_FHIR_JSON + "; charset=UTF-8"; - ourResponseBody = msg; + MY_SERVLET.ourResponseContentType = Constants.CT_FHIR_JSON + "; charset=UTF-8"; + MY_SERVLET.ourResponseBody = msg; - IGenericClient client = ourCtx.newRestfulGenericClient("http://localhost:" + ourPort + "/fhir"); + IGenericClient client = ourCtx.newRestfulGenericClient(ourServer.getBaseUrl() + "/fhir"); //@formatter:off Bundle response = client.search() @@ -1418,7 +1413,7 @@ public class GenericJaxRsClientDstu3Test { .execute(); //@formatter:on - assertEquals("http://localhost:" + ourPort + "/fhir/Patient?name=james", ourRequestUri); + assertEquals(ourServer.getBaseUrl() + "/fhir/Patient?name=james", MY_SERVLET.ourRequestUri); assertEquals(Patient.class, response.getEntry().get(0).getResource().getClass()); } @@ -1429,20 +1424,20 @@ public class GenericJaxRsClientDstu3Test { final String msg = getPatientFeedWithOneResult(); - ourResponseContentType = Constants.CT_FHIR_XML + "; charset=UTF-8"; - ourResponseBody = msg; + MY_SERVLET.ourResponseContentType = Constants.CT_FHIR_XML + "; charset=UTF-8"; + MY_SERVLET.ourResponseBody = msg; - IGenericClient client = ourCtx.newRestfulGenericClient("http://localhost:" + ourPort + "/fhir"); + IGenericClient client = ourCtx.newRestfulGenericClient(ourServer.getBaseUrl() + "/fhir"); //@formatter:off org.hl7.fhir.dstu3.model.Bundle response = client.search() - .byUrl("http://localhost:" + ourPort + "/AAA?name=http://foo|bar") + .byUrl(ourServer.getBaseUrl() + "/AAA?name=http://foo|bar") .encodedJson() .returnBundle(org.hl7.fhir.dstu3.model.Bundle.class) .execute(); //@formatter:on - assertEquals("http://localhost:" + ourPort + "/AAA?name=http%3A//foo%7Cbar&_format=json", ourRequestUri); + assertEquals(ourServer.getBaseUrl() + "/AAA?name=http%3A//foo%7Cbar&_format=json", MY_SERVLET.ourRequestUri); assertNotNull(response); @@ -1453,7 +1448,7 @@ public class GenericJaxRsClientDstu3Test { .returnBundle(org.hl7.fhir.dstu3.model.Bundle.class) .execute(); //@formatter:on - assertEquals("http://localhost:" + ourPort + "/fhir/Patient?name=http%3A//foo%7Cbar&_format=json", ourRequestUri); + assertEquals(ourServer.getBaseUrl() + "/fhir/Patient?name=http%3A//foo%7Cbar&_format=json", MY_SERVLET.ourRequestUri); assertNotNull(response); @@ -1464,7 +1459,7 @@ public class GenericJaxRsClientDstu3Test { .returnBundle(org.hl7.fhir.dstu3.model.Bundle.class) .execute(); //@formatter:on - assertEquals("http://localhost:" + ourPort + "/fhir/Patient?name=http%3A//foo%7Cbar&_format=json", ourRequestUri); + assertEquals(ourServer.getBaseUrl() + "/fhir/Patient?name=http%3A//foo%7Cbar&_format=json", MY_SERVLET.ourRequestUri); assertNotNull(response); @@ -1474,7 +1469,7 @@ public class GenericJaxRsClientDstu3Test { .returnBundle(org.hl7.fhir.dstu3.model.Bundle.class) .execute(); //@formatter:on - assertEquals("http://localhost:" + ourPort + "/fhir/Patient", ourRequestUri); + assertEquals(ourServer.getBaseUrl() + "/fhir/Patient", MY_SERVLET.ourRequestUri); assertNotNull(response); @@ -1484,7 +1479,7 @@ public class GenericJaxRsClientDstu3Test { .returnBundle(org.hl7.fhir.dstu3.model.Bundle.class) .execute(); //@formatter:on - assertEquals("http://localhost:" + ourPort + "/fhir/Patient", ourRequestUri); + assertEquals(ourServer.getBaseUrl() + "/fhir/Patient", MY_SERVLET.ourRequestUri); assertNotNull(response); @@ -1503,11 +1498,11 @@ public class GenericJaxRsClientDstu3Test { String msg = IOUtils.toString(GenericJaxRsClientDstu3Test.class.getResourceAsStream("/bundle_orion.xml")); - ourResponseContentType = Constants.CT_FHIR_XML + "; charset=UTF-8"; - ourResponseBody = msg; + MY_SERVLET.ourResponseContentType = Constants.CT_FHIR_XML + "; charset=UTF-8"; + MY_SERVLET.ourResponseBody = msg; - IGenericClient client = ourCtx.newRestfulGenericClient("http://localhost:" + ourPort + "/fhir"); + IGenericClient client = ourCtx.newRestfulGenericClient(ourServer.getBaseUrl() + "/fhir"); //@formatter:off org.hl7.fhir.dstu3.model.Bundle response = client.search() @@ -1532,11 +1527,11 @@ public class GenericJaxRsClientDstu3Test { String msg = "{\"resourceType\":\"Bundle\",\"id\":null,\"base\":\"http://localhost:57931/fhir/contextDev\",\"total\":1,\"link\":[{\"relation\":\"self\",\"url\":\"http://localhost:57931/fhir/contextDev/Patient?identifier=urn%3AMultiFhirVersionTest%7CtestSubmitPatient01&_format=json\"}],\"entry\":[{\"resource\":{\"resourceType\":\"Patient\",\"id\":\"1\",\"meta\":{\"versionId\":\"1\",\"lastUpdated\":\"2014-12-20T18:41:29.706-05:00\"},\"identifier\":[{\"system\":\"urn:MultiFhirVersionTest\",\"value\":\"testSubmitPatient01\"}]}}]}"; - ourResponseContentType = Constants.CT_FHIR_JSON + "; charset=UTF-8"; - ourResponseBody = msg; + MY_SERVLET.ourResponseContentType = Constants.CT_FHIR_JSON + "; charset=UTF-8"; + MY_SERVLET.ourResponseBody = msg; - IGenericClient client = ourCtx.newRestfulGenericClient("http://localhost:" + ourPort + "/fhir"); + IGenericClient client = ourCtx.newRestfulGenericClient(ourServer.getBaseUrl() + "/fhir"); //@formatter:off Bundle response = client.search() @@ -1547,7 +1542,7 @@ public class GenericJaxRsClientDstu3Test { .execute(); //@formatter:on - assertThat(ourRequestUri, either(equalTo("http://localhost:" + ourPort + "/fhir/Patient?name=james&_elements=name%2Cidentifier")).or(equalTo("http://localhost:" + ourPort + "/fhir/Patient?name=james&_elements=identifier%2Cname"))); + assertThat(MY_SERVLET.ourRequestUri, either(equalTo(ourServer.getBaseUrl() + "/fhir/Patient?name=james&_elements=name%2Cidentifier")).or(equalTo(ourServer.getBaseUrl() + "/fhir/Patient?name=james&_elements=identifier%2Cname"))); assertEquals(Patient.class, response.getEntry().get(0).getResource().getClass()); } @@ -1557,11 +1552,11 @@ public class GenericJaxRsClientDstu3Test { String msg = "{\"resourceType\":\"Bundle\",\"id\":null,\"base\":\"http://localhost:57931/fhir/contextDev\",\"total\":1,\"link\":[{\"relation\":\"self\",\"url\":\"http://localhost:57931/fhir/contextDev/Patient?identifier=urn%3AMultiFhirVersionTest%7CtestSubmitPatient01&_format=json\"}],\"entry\":[{\"resource\":{\"resourceType\":\"Patient\",\"id\":\"1\",\"meta\":{\"versionId\":\"1\",\"lastUpdated\":\"2014-12-20T18:41:29.706-05:00\"},\"identifier\":[{\"system\":\"urn:MultiFhirVersionTest\",\"value\":\"testSubmitPatient01\"}]}}]}"; - ourResponseContentType = Constants.CT_FHIR_JSON + "; charset=UTF-8"; - ourResponseBody = msg; + MY_SERVLET.ourResponseContentType = Constants.CT_FHIR_JSON + "; charset=UTF-8"; + MY_SERVLET.ourResponseBody = msg; - IGenericClient client = ourCtx.newRestfulGenericClient("http://localhost:" + ourPort + "/fhir"); + IGenericClient client = ourCtx.newRestfulGenericClient(ourServer.getBaseUrl() + "/fhir"); //@formatter:off Bundle response = client.search() @@ -1573,18 +1568,18 @@ public class GenericJaxRsClientDstu3Test { .execute(); //@formatter:on - assertEquals("http://localhost:" + ourPort + "/fhir/Patient/_search?_elements=identifier%2Cname", ourRequestUri); + assertEquals(ourServer.getBaseUrl() + "/fhir/Patient/_search?_elements=identifier%2Cname", MY_SERVLET.ourRequestUri); - // assertThat(ourRequestUri, - // either(equalTo("http://localhost:" + ourPort + "/fhir/Patient?name=james&_elements=name%2Cidentifier")).or(equalTo("http://localhost:" + ourPort + "/fhir/Patient?name=james&_elements=identifier%2Cname"))); + // assertThat(MY_SERVLET.ourRequestUri, + // either(equalTo(ourServer.getBaseUrl() + "/fhir/Patient?name=james&_elements=name%2Cidentifier")).or(equalTo(ourServer.getBaseUrl() + "/fhir/Patient?name=james&_elements=identifier%2Cname"))); assertEquals(Patient.class, response.getEntry().get(0).getResource().getClass()); - assertEquals("name=james", ourRequestBodyString); + assertEquals("name=james", MY_SERVLET.ourRequestBodyString); - assertEquals("application/x-www-form-urlencoded", ourRequestContentType.replace(";char", "; char").toLowerCase()); - assertEquals(Constants.HEADER_ACCEPT_VALUE_XML_OR_JSON_NON_LEGACY, ourRequestFirstHeaders.get("Accept").getValue()); - assertThat(ourRequestFirstHeaders.get("User-Agent").getValue(), not(emptyString())); + assertEquals("application/x-www-form-urlencoded", MY_SERVLET.ourRequestContentType.replace(";char", "; char").toLowerCase()); + assertEquals(Constants.HEADER_ACCEPT_VALUE_XML_OR_JSON_NON_LEGACY, MY_SERVLET.ourRequestFirstHeaders.get("Accept").getValue()); + assertThat(MY_SERVLET.ourRequestFirstHeaders.get("User-Agent").getValue(), not(emptyString())); } @Test @@ -1592,11 +1587,11 @@ public class GenericJaxRsClientDstu3Test { String msg = "{\"resourceType\":\"Bundle\",\"id\":null,\"base\":\"http://localhost:57931/fhir/contextDev\",\"total\":1,\"link\":[{\"relation\":\"self\",\"url\":\"http://localhost:57931/fhir/contextDev/Patient?identifier=urn%3AMultiFhirVersionTest%7CtestSubmitPatient01&_format=json\"}],\"entry\":[{\"resource\":{\"resourceType\":\"Patient\",\"id\":\"1\",\"meta\":{\"versionId\":\"1\",\"lastUpdated\":\"2014-12-20T18:41:29.706-05:00\"},\"identifier\":[{\"system\":\"urn:MultiFhirVersionTest\",\"value\":\"testSubmitPatient01\"}]}}]}"; - ourResponseContentType = Constants.CT_FHIR_JSON + "; charset=UTF-8"; - ourResponseBody = msg; + MY_SERVLET.ourResponseContentType = Constants.CT_FHIR_JSON + "; charset=UTF-8"; + MY_SERVLET.ourResponseBody = msg; - IGenericClient client = ourCtx.newRestfulGenericClient("http://localhost:" + ourPort + "/fhir"); + IGenericClient client = ourCtx.newRestfulGenericClient(ourServer.getBaseUrl() + "/fhir"); //@formatter:off Bundle response = client.search() @@ -1609,18 +1604,18 @@ public class GenericJaxRsClientDstu3Test { .execute(); //@formatter:on - assertThat(ourRequestUri, containsString("http://localhost:" + ourPort + "/fhir/Patient/_search?")); - assertThat(ourRequestUri, containsString("_elements=identifier%2Cname")); + assertThat(MY_SERVLET.ourRequestUri, containsString(ourServer.getBaseUrl() + "/fhir/Patient/_search?")); + assertThat(MY_SERVLET.ourRequestUri, containsString("_elements=identifier%2Cname")); - // assertThat(ourRequestUri, - // either(equalTo("http://localhost:" + ourPort + "/fhir/Patient?name=james&_elements=name%2Cidentifier")).or(equalTo("http://localhost:" + ourPort + "/fhir/Patient?name=james&_elements=identifier%2Cname"))); + // assertThat(MY_SERVLET.ourRequestUri, + // either(equalTo(ourServer.getBaseUrl() + "/fhir/Patient?name=james&_elements=name%2Cidentifier")).or(equalTo(ourServer.getBaseUrl() + "/fhir/Patient?name=james&_elements=identifier%2Cname"))); assertEquals(Patient.class, response.getEntry().get(0).getResource().getClass()); - assertEquals("name=james", ourRequestBodyString); + assertEquals("name=james", MY_SERVLET.ourRequestBodyString); - assertEquals("application/x-www-form-urlencoded", ourRequestContentType); - assertEquals(Constants.HEADER_ACCEPT_VALUE_JSON_NON_LEGACY, ourRequestFirstHeaders.get("Accept").getValue()); + assertEquals("application/x-www-form-urlencoded", MY_SERVLET.ourRequestContentType); + assertEquals(Constants.HEADER_ACCEPT_VALUE_JSON_NON_LEGACY, MY_SERVLET.ourRequestFirstHeaders.get("Accept").getValue()); } @Test @@ -1628,11 +1623,11 @@ public class GenericJaxRsClientDstu3Test { String msg = "{\"resourceType\":\"Bundle\",\"id\":null,\"base\":\"http://localhost:57931/fhir/contextDev\",\"total\":1,\"link\":[{\"relation\":\"self\",\"url\":\"http://localhost:57931/fhir/contextDev/Patient?identifier=urn%3AMultiFhirVersionTest%7CtestSubmitPatient01&_format=json\"}],\"entry\":[{\"resource\":{\"resourceType\":\"Patient\",\"id\":\"1\",\"meta\":{\"versionId\":\"1\",\"lastUpdated\":\"2014-12-20T18:41:29.706-05:00\"},\"identifier\":[{\"system\":\"urn:MultiFhirVersionTest\",\"value\":\"testSubmitPatient01\"}]}}]}"; - ourResponseContentType = Constants.CT_FHIR_JSON + "; charset=UTF-8"; - ourResponseBody = msg; + MY_SERVLET.ourResponseContentType = Constants.CT_FHIR_JSON + "; charset=UTF-8"; + MY_SERVLET.ourResponseBody = msg; - IGenericClient client = ourCtx.newRestfulGenericClient("http://localhost:" + ourPort + "/fhir"); + IGenericClient client = ourCtx.newRestfulGenericClient(ourServer.getBaseUrl() + "/fhir"); //@formatter:off Bundle response = client.search() @@ -1643,7 +1638,7 @@ public class GenericJaxRsClientDstu3Test { .execute(); //@formatter:on - assertEquals("http://localhost:" + ourPort + "/fhir/Patient?name=james&_lastUpdated=ge2011-01-01&_lastUpdated=le2012-01-01", ourRequestUri); + assertEquals(ourServer.getBaseUrl() + "/fhir/Patient?name=james&_lastUpdated=ge2011-01-01&_lastUpdated=le2012-01-01", MY_SERVLET.ourRequestUri); assertEquals(Patient.class, response.getEntry().get(0).getResource().getClass()); } @@ -1653,11 +1648,11 @@ public class GenericJaxRsClientDstu3Test { String msg = "{\"resourceType\":\"Bundle\",\"id\":null,\"base\":\"http://localhost:57931/fhir/contextDev\",\"total\":1,\"link\":[{\"relation\":\"self\",\"url\":\"http://localhost:57931/fhir/contextDev/Patient?identifier=urn%3AMultiFhirVersionTest%7CtestSubmitPatient01&_format=json\"}],\"entry\":[{\"resource\":{\"resourceType\":\"Patient\",\"id\":\"1\",\"meta\":{\"versionId\":\"1\",\"lastUpdated\":\"2014-12-20T18:41:29.706-05:00\"},\"identifier\":[{\"system\":\"urn:MultiFhirVersionTest\",\"value\":\"testSubmitPatient01\"}]}}]}"; - ourResponseContentType = Constants.CT_FHIR_JSON + "; charset=UTF-8"; - ourResponseBody = msg; + MY_SERVLET.ourResponseContentType = Constants.CT_FHIR_JSON + "; charset=UTF-8"; + MY_SERVLET.ourResponseBody = msg; - IGenericClient client = ourCtx.newRestfulGenericClient("http://localhost:" + ourPort + "/fhir"); + IGenericClient client = ourCtx.newRestfulGenericClient(ourServer.getBaseUrl() + "/fhir"); //@formatter:off Bundle response = client.search() @@ -1670,7 +1665,7 @@ public class GenericJaxRsClientDstu3Test { .execute(); //@formatter:on - assertEquals("http://localhost:" + ourPort + "/fhir/Patient?_security=system1%7Ccode1&_security=system2%7Ccode2&_profile=http%3A%2F%2Ffoo1&_profile=http%3A%2F%2Ffoo2", ourRequestUri); + assertEquals(ourServer.getBaseUrl() + "/fhir/Patient?_security=system1%7Ccode1&_security=system2%7Ccode2&_profile=http%3A%2F%2Ffoo1&_profile=http%3A%2F%2Ffoo2", MY_SERVLET.ourRequestUri); assertEquals(Patient.class, response.getEntry().get(0).getResource().getClass()); } @@ -1682,11 +1677,11 @@ public class GenericJaxRsClientDstu3Test { String msg = getPatientFeedWithOneResult(); - ourResponseContentType = Constants.CT_FHIR_XML + "; charset=UTF-8"; - ourResponseBody = msg; + MY_SERVLET.ourResponseContentType = Constants.CT_FHIR_XML + "; charset=UTF-8"; + MY_SERVLET.ourResponseBody = msg; - IGenericClient client = ourCtx.newRestfulGenericClient("http://localhost:" + ourPort + "/fhir"); + IGenericClient client = ourCtx.newRestfulGenericClient(ourServer.getBaseUrl() + "/fhir"); //@formatter:off Bundle response = client.search() @@ -1697,7 +1692,7 @@ public class GenericJaxRsClientDstu3Test { .execute(); //@formatter:on - assertEquals("http://localhost:" + ourPort + "/fhir/Patient?_revinclude=Provenance%3Atarget&_format=json", ourRequestUri); + assertEquals(ourServer.getBaseUrl() + "/fhir/Patient?_revinclude=Provenance%3Atarget&_format=json", MY_SERVLET.ourRequestUri); } @@ -1706,11 +1701,11 @@ public class GenericJaxRsClientDstu3Test { String msg = "{\"resourceType\":\"Bundle\",\"id\":null,\"base\":\"http://localhost:57931/fhir/contextDev\",\"total\":1,\"link\":[{\"relation\":\"self\",\"url\":\"http://localhost:57931/fhir/contextDev/Patient?identifier=urn%3AMultiFhirVersionTest%7CtestSubmitPatient01&_format=json\"}],\"entry\":[{\"resource\":{\"resourceType\":\"Patient\",\"id\":\"1\",\"meta\":{\"versionId\":\"1\",\"lastUpdated\":\"2014-12-20T18:41:29.706-05:00\"},\"identifier\":[{\"system\":\"urn:MultiFhirVersionTest\",\"value\":\"testSubmitPatient01\"}]}}]}"; - ourResponseContentType = Constants.CT_FHIR_JSON + "; charset=UTF-8"; - ourResponseBody = msg; + MY_SERVLET.ourResponseContentType = Constants.CT_FHIR_JSON + "; charset=UTF-8"; + MY_SERVLET.ourResponseBody = msg; - IGenericClient client = ourCtx.newRestfulGenericClient("http://localhost:" + ourPort + "/fhir"); + IGenericClient client = ourCtx.newRestfulGenericClient(ourServer.getBaseUrl() + "/fhir"); //@formatter:off Bundle response = client.search() @@ -1721,7 +1716,7 @@ public class GenericJaxRsClientDstu3Test { .execute(); //@formatter:on - assertEquals("http://localhost:" + ourPort + "/fhir/Patient?name=james&_summary=false", ourRequestUri); + assertEquals(ourServer.getBaseUrl() + "/fhir/Patient?name=james&_summary=false", MY_SERVLET.ourRequestUri); assertEquals(Patient.class, response.getEntry().get(0).getResource().getClass()); } @@ -1735,10 +1730,10 @@ public class GenericJaxRsClientDstu3Test { String respString = ourCtx.newJsonParser().encodeResourceToString(resp); - ourResponseContentType = Constants.CT_FHIR_JSON + "; charset=UTF-8"; - ourResponseBody = respString; + MY_SERVLET.ourResponseContentType = Constants.CT_FHIR_JSON + "; charset=UTF-8"; + MY_SERVLET.ourResponseBody = respString; - IGenericClient client = ourCtx.newRestfulGenericClient("http://localhost:" + ourPort + "/fhir"); + IGenericClient client = ourCtx.newRestfulGenericClient(ourServer.getBaseUrl() + "/fhir"); List input = new ArrayList(); @@ -1758,10 +1753,10 @@ public class GenericJaxRsClientDstu3Test { .execute(); //@formatter:on - assertEquals("http://localhost:" + ourPort + "/fhir", ourRequestUri); + assertEquals(ourServer.getBaseUrl() + "/fhir", MY_SERVLET.ourRequestUri); assertEquals(2, response.size()); - String requestString = ourRequestBodyString; + String requestString = MY_SERVLET.ourRequestBodyString; org.hl7.fhir.dstu3.model.Bundle requestBundle = ourCtx.newJsonParser().parseResource(org.hl7.fhir.dstu3.model.Bundle.class, requestString); assertEquals(2, requestBundle.getEntry().size()); assertEquals(HTTPVerb.POST, requestBundle.getEntry().get(0).getRequest().getMethod()); @@ -1810,10 +1805,10 @@ public class GenericJaxRsClientDstu3Test { resp.addEntry().getResponse().setLocation("Patient/2/_history/2"); - ourResponseContentType = Constants.CT_FHIR_JSON + "; charset=UTF-8"; - ourResponseBody = reqString; + MY_SERVLET.ourResponseContentType = Constants.CT_FHIR_JSON + "; charset=UTF-8"; + MY_SERVLET.ourResponseBody = reqString; - IGenericClient client = ourCtx.newRestfulGenericClient("http://localhost:" + ourPort + "/fhir"); + IGenericClient client = ourCtx.newRestfulGenericClient(ourServer.getBaseUrl() + "/fhir"); //@formatter:off String response = client.transaction() @@ -1821,9 +1816,9 @@ public class GenericJaxRsClientDstu3Test { .execute(); //@formatter:on - assertEquals("http://localhost:" + ourPort + "/fhir/", ourRequestUri); + assertEquals(ourServer.getBaseUrl() + "/fhir/", MY_SERVLET.ourRequestUri); assertThat(response, containsString("\"Bundle\"")); - assertEquals("application/fhir+json;charset=UTF-8", ourRequestFirstHeaders.get("Content-Type").getValue()); + assertEquals("application/fhir+json;charset=UTF-8", MY_SERVLET.ourRequestFirstHeaders.get("Content-Type").getValue()); //@formatter:off response = client.transaction() @@ -1832,8 +1827,8 @@ public class GenericJaxRsClientDstu3Test { .execute(); //@formatter:on - assertEquals("http://localhost:" + ourPort + "/fhir/", ourRequestUri); - assertEquals("application/fhir+xml;charset=UTF-8", ourRequestFirstHeaders.get("Content-Type").getValue()); + assertEquals(ourServer.getBaseUrl() + "/fhir/", MY_SERVLET.ourRequestUri); + assertEquals("application/fhir+xml;charset=UTF-8", MY_SERVLET.ourRequestFirstHeaders.get("Content-Type").getValue()); } @@ -1846,10 +1841,10 @@ public class GenericJaxRsClientDstu3Test { String respString = ourCtx.newJsonParser().encodeResourceToString(resp); - ourResponseContentType = Constants.CT_FHIR_JSON + "; charset=UTF-8"; - ourResponseBody = respString; + MY_SERVLET.ourResponseContentType = Constants.CT_FHIR_JSON + "; charset=UTF-8"; + MY_SERVLET.ourResponseBody = respString; - IGenericClient client = ourCtx.newRestfulGenericClient("http://localhost:" + ourPort + "/fhir"); + IGenericClient client = ourCtx.newRestfulGenericClient(ourServer.getBaseUrl() + "/fhir"); org.hl7.fhir.dstu3.model.Bundle input = new org.hl7.fhir.dstu3.model.Bundle(); @@ -1869,7 +1864,7 @@ public class GenericJaxRsClientDstu3Test { .execute(); //@formatter:on - assertEquals("http://localhost:" + ourPort + "/fhir", ourRequestUri); + assertEquals(ourServer.getBaseUrl() + "/fhir", MY_SERVLET.ourRequestUri); assertEquals(2, response.getEntry().size()); assertEquals("Patient/1/_history/1", response.getEntry().get(0).getResponse().getLocation()); @@ -1880,52 +1875,52 @@ public class GenericJaxRsClientDstu3Test { public void testUpdateConditional() { - ourResponseStatus = Constants.STATUS_HTTP_204_NO_CONTENT; + MY_SERVLET.ourResponseStatus = Constants.STATUS_HTTP_204_NO_CONTENT; - IGenericClient client = ourCtx.newRestfulGenericClient("http://localhost:" + ourPort + "/fhir"); + IGenericClient client = ourCtx.newRestfulGenericClient(ourServer.getBaseUrl() + "/fhir"); Patient p = new Patient(); p.addName().setFamily("FOOFAMILY"); client.update().resource(p).conditionalByUrl("Patient?name=foo").encodedXml().execute(); - assertEquals(1, ourRequestHeaders.get(Constants.HEADER_CONTENT_TYPE).size()); - assertEquals(EncodingEnum.XML.getResourceContentTypeNonLegacy() + Constants.HEADER_SUFFIX_CT_UTF_8, ourRequestFirstHeaders.get(Constants.HEADER_CONTENT_TYPE).getValue().replace(";char", "; char")); - assertThat(ourRequestBodyString, containsString("")); - assertEquals("PUT", ourRequestMethod); - assertEquals("http://localhost:" + ourPort + "/fhir/Patient?name=foo", ourRequestUri); + assertEquals(1, MY_SERVLET.ourRequestHeaders.get(Constants.HEADER_CONTENT_TYPE).size()); + assertEquals(EncodingEnum.XML.getResourceContentTypeNonLegacy() + Constants.HEADER_SUFFIX_CT_UTF_8, MY_SERVLET.ourRequestFirstHeaders.get(Constants.HEADER_CONTENT_TYPE).getValue().replace(";char", "; char")); + assertThat(MY_SERVLET.ourRequestBodyString, containsString("")); + assertEquals("PUT", MY_SERVLET.ourRequestMethod); + assertEquals(ourServer.getBaseUrl() + "/fhir/Patient?name=foo", MY_SERVLET.ourRequestUri); client.update().resource(p).conditionalByUrl("Patient?name=http://foo|bar").encodedXml().execute(); - assertEquals(1, ourRequestHeaders.get(Constants.HEADER_CONTENT_TYPE).size()); - assertEquals(EncodingEnum.XML.getResourceContentTypeNonLegacy() + Constants.HEADER_SUFFIX_CT_UTF_8, ourRequestFirstHeaders.get(Constants.HEADER_CONTENT_TYPE).getValue().replace(";char", "; char")); - assertThat(ourRequestBodyString, containsString("")); - assertEquals("PUT", ourRequestMethod); - assertEquals("http://localhost:" + ourPort + "/fhir/Patient?name=http%3A//foo%7Cbar", ourRequestUri); + assertEquals(1, MY_SERVLET.ourRequestHeaders.get(Constants.HEADER_CONTENT_TYPE).size()); + assertEquals(EncodingEnum.XML.getResourceContentTypeNonLegacy() + Constants.HEADER_SUFFIX_CT_UTF_8, MY_SERVLET.ourRequestFirstHeaders.get(Constants.HEADER_CONTENT_TYPE).getValue().replace(";char", "; char")); + assertThat(MY_SERVLET.ourRequestBodyString, containsString("")); + assertEquals("PUT", MY_SERVLET.ourRequestMethod); + assertEquals(ourServer.getBaseUrl() + "/fhir/Patient?name=http%3A//foo%7Cbar", MY_SERVLET.ourRequestUri); client.update().resource(ourCtx.newXmlParser().encodeResourceToString(p)).conditionalByUrl("Patient?name=foo").execute(); - assertEquals(1, ourRequestHeaders.get(Constants.HEADER_CONTENT_TYPE).size()); - assertEquals(EncodingEnum.XML.getResourceContentTypeNonLegacy() + Constants.HEADER_SUFFIX_CT_UTF_8, ourRequestFirstHeaders.get(Constants.HEADER_CONTENT_TYPE).getValue().replace(";char", "; char")); - assertThat(ourRequestBodyString, containsString("")); - assertEquals("PUT", ourRequestMethod); - assertEquals("http://localhost:" + ourPort + "/fhir/Patient?name=foo", ourRequestUri); + assertEquals(1, MY_SERVLET.ourRequestHeaders.get(Constants.HEADER_CONTENT_TYPE).size()); + assertEquals(EncodingEnum.XML.getResourceContentTypeNonLegacy() + Constants.HEADER_SUFFIX_CT_UTF_8, MY_SERVLET.ourRequestFirstHeaders.get(Constants.HEADER_CONTENT_TYPE).getValue().replace(";char", "; char")); + assertThat(MY_SERVLET.ourRequestBodyString, containsString("")); + assertEquals("PUT", MY_SERVLET.ourRequestMethod); + assertEquals(ourServer.getBaseUrl() + "/fhir/Patient?name=foo", MY_SERVLET.ourRequestUri); client.update().resource(p).conditional().where(Patient.NAME.matches().value("foo")).and(Patient.ADDRESS.matches().value("AAA|BBB")).encodedXml().execute(); - assertEquals(1, ourRequestHeaders.get(Constants.HEADER_CONTENT_TYPE).size()); - assertEquals(EncodingEnum.XML.getResourceContentTypeNonLegacy() + Constants.HEADER_SUFFIX_CT_UTF_8, ourRequestFirstHeaders.get(Constants.HEADER_CONTENT_TYPE).getValue().replace(";char", "; char")); - assertThat(ourRequestBodyString, containsString("")); - assertEquals("PUT", ourRequestMethod); - assertEquals("http://localhost:" + ourPort + "/fhir/Patient?name=foo&address=AAA%5C%7CBBB", ourRequestUri); + assertEquals(1, MY_SERVLET.ourRequestHeaders.get(Constants.HEADER_CONTENT_TYPE).size()); + assertEquals(EncodingEnum.XML.getResourceContentTypeNonLegacy() + Constants.HEADER_SUFFIX_CT_UTF_8, MY_SERVLET.ourRequestFirstHeaders.get(Constants.HEADER_CONTENT_TYPE).getValue().replace(";char", "; char")); + assertThat(MY_SERVLET.ourRequestBodyString, containsString("")); + assertEquals("PUT", MY_SERVLET.ourRequestMethod); + assertEquals(ourServer.getBaseUrl() + "/fhir/Patient?name=foo&address=AAA%5C%7CBBB", MY_SERVLET.ourRequestUri); client.update().resource(ourCtx.newXmlParser().encodeResourceToString(p)).conditional().where(Patient.NAME.matches().value("foo")).and(Patient.ADDRESS.matches().value("AAA|BBB")).encodedXml().execute(); - assertEquals(1, ourRequestHeaders.get(Constants.HEADER_CONTENT_TYPE).size()); - assertEquals(EncodingEnum.XML.getResourceContentTypeNonLegacy() + Constants.HEADER_SUFFIX_CT_UTF_8, ourRequestFirstHeaders.get(Constants.HEADER_CONTENT_TYPE).getValue().replace(";char", "; char")); - assertThat(ourRequestBodyString, containsString("")); - assertEquals("PUT", ourRequestMethod); - assertEquals("http://localhost:" + ourPort + "/fhir/Patient?name=foo&address=AAA%5C%7CBBB", ourRequestUri); + assertEquals(1, MY_SERVLET.ourRequestHeaders.get(Constants.HEADER_CONTENT_TYPE).size()); + assertEquals(EncodingEnum.XML.getResourceContentTypeNonLegacy() + Constants.HEADER_SUFFIX_CT_UTF_8, MY_SERVLET.ourRequestFirstHeaders.get(Constants.HEADER_CONTENT_TYPE).getValue().replace(";char", "; char")); + assertThat(MY_SERVLET.ourRequestBodyString, containsString("")); + assertEquals("PUT", MY_SERVLET.ourRequestMethod); + assertEquals(ourServer.getBaseUrl() + "/fhir/Patient?name=foo&address=AAA%5C%7CBBB", MY_SERVLET.ourRequestUri); } @@ -1934,9 +1929,9 @@ public class GenericJaxRsClientDstu3Test { public void testUpdateNonFluent() { - ourResponseStatus = Constants.STATUS_HTTP_204_NO_CONTENT; + MY_SERVLET.ourResponseStatus = Constants.STATUS_HTTP_204_NO_CONTENT; - IGenericClient client = ourCtx.newRestfulGenericClient("http://localhost:" + ourPort + "/fhir"); + IGenericClient client = ourCtx.newRestfulGenericClient(ourServer.getBaseUrl() + "/fhir"); client.setEncoding(EncodingEnum.XML); @@ -1944,29 +1939,27 @@ public class GenericJaxRsClientDstu3Test { p.addName().setFamily("FOOFAMILY"); client.update(new IdType("Patient/123").getValue(), p); - assertEquals(1, ourRequestHeaders.get(Constants.HEADER_CONTENT_TYPE).size()); - assertEquals(EncodingEnum.XML.getResourceContentTypeNonLegacy() + Constants.HEADER_SUFFIX_CT_UTF_8, ourRequestFirstHeaders.get(Constants.HEADER_CONTENT_TYPE).getValue().replace(";char", "; char")); - assertThat(ourRequestBodyString, containsString("")); - assertEquals("http://localhost:" + ourPort + "/fhir/Patient/123?_format=xml", ourRequestUri); - assertEquals("PUT", ourRequestMethod); + assertEquals(1, MY_SERVLET.ourRequestHeaders.get(Constants.HEADER_CONTENT_TYPE).size()); + assertEquals(EncodingEnum.XML.getResourceContentTypeNonLegacy() + Constants.HEADER_SUFFIX_CT_UTF_8, MY_SERVLET.ourRequestFirstHeaders.get(Constants.HEADER_CONTENT_TYPE).getValue().replace(";char", "; char")); + assertThat(MY_SERVLET.ourRequestBodyString, containsString("")); + assertEquals(ourServer.getBaseUrl() + "/fhir/Patient/123?_format=xml", MY_SERVLET.ourRequestUri); + assertEquals("PUT", MY_SERVLET.ourRequestMethod); client.update("123", p); - assertEquals(1, ourRequestHeaders.get(Constants.HEADER_CONTENT_TYPE).size()); - assertEquals(EncodingEnum.XML.getResourceContentTypeNonLegacy() + Constants.HEADER_SUFFIX_CT_UTF_8, ourRequestFirstHeaders.get(Constants.HEADER_CONTENT_TYPE).getValue().replace(";char", "; char")); - assertThat(ourRequestBodyString, containsString("")); - assertEquals("http://localhost:" + ourPort + "/fhir/Patient/123?_format=xml", ourRequestUri); - assertEquals("PUT", ourRequestMethod); + assertEquals(1, MY_SERVLET.ourRequestHeaders.get(Constants.HEADER_CONTENT_TYPE).size()); + assertEquals(EncodingEnum.XML.getResourceContentTypeNonLegacy() + Constants.HEADER_SUFFIX_CT_UTF_8, MY_SERVLET.ourRequestFirstHeaders.get(Constants.HEADER_CONTENT_TYPE).getValue().replace(";char", "; char")); + assertThat(MY_SERVLET.ourRequestBodyString, containsString("")); + assertEquals(ourServer.getBaseUrl() + "/fhir/Patient/123?_format=xml", MY_SERVLET.ourRequestUri); + assertEquals("PUT", MY_SERVLET.ourRequestMethod); } @Test public void testUpdatePrefer() { + MY_SERVLET.ourResponseStatus = Constants.STATUS_HTTP_204_NO_CONTENT; - - ourResponseStatus = Constants.STATUS_HTTP_204_NO_CONTENT; - - IGenericClient client = ourCtx.newRestfulGenericClient("http://localhost:" + ourPort + "/fhir"); + IGenericClient client = ourCtx.newRestfulGenericClient(ourServer.getBaseUrl() + "/fhir"); Patient p = new Patient(); @@ -1974,13 +1967,13 @@ public class GenericJaxRsClientDstu3Test { p.addName().setFamily("FOOFAMILY"); client.update().resource(p).prefer(PreferReturnEnum.MINIMAL).execute(); - assertEquals(1, ourRequestHeaders.get(Constants.HEADER_PREFER).size()); - assertEquals(Constants.HEADER_PREFER_RETURN + '=' + Constants.HEADER_PREFER_RETURN_MINIMAL, ourRequestHeaders.get(Constants.HEADER_PREFER).get(0).getValue()); + assertEquals(1, MY_SERVLET.ourRequestHeaders.get(Constants.HEADER_PREFER).size()); + assertEquals(Constants.HEADER_PREFER_RETURN + '=' + Constants.HEADER_PREFER_RETURN_MINIMAL, MY_SERVLET.ourRequestHeaders.get(Constants.HEADER_PREFER).get(0).getValue()); client.update().resource(p).prefer(PreferReturnEnum.REPRESENTATION).execute(); - assertEquals(1, ourRequestHeaders.get(Constants.HEADER_PREFER).size()); - assertEquals(Constants.HEADER_PREFER_RETURN + '=' + Constants.HEADER_PREFER_RETURN_REPRESENTATION, ourRequestHeaders.get(Constants.HEADER_PREFER).get(0).getValue()); + assertEquals(1, MY_SERVLET.ourRequestHeaders.get(Constants.HEADER_PREFER).size()); + assertEquals(Constants.HEADER_PREFER_RETURN + '=' + Constants.HEADER_PREFER_RETURN_REPRESENTATION, MY_SERVLET.ourRequestHeaders.get(Constants.HEADER_PREFER).get(0).getValue()); } @@ -1992,11 +1985,11 @@ public class GenericJaxRsClientDstu3Test { final String formatted = ourCtx.newXmlParser().encodeResourceToString(p); - ourResponseStatus = Constants.STATUS_HTTP_200_OK; - ourResponseContentType = Constants.CT_FHIR_XML + "; charset=UTF-8"; - ourResponseBody = formatted; + MY_SERVLET.ourResponseStatus = Constants.STATUS_HTTP_200_OK; + MY_SERVLET.ourResponseContentType = Constants.CT_FHIR_XML + "; charset=UTF-8"; + MY_SERVLET.ourResponseBody = formatted; - IGenericClient client = ourCtx.newRestfulGenericClient("http://localhost:" + ourPort + "/fhir"); + IGenericClient client = ourCtx.newRestfulGenericClient(ourServer.getBaseUrl() + "/fhir"); p = new Patient(); p.setId(new IdType("1")); @@ -2015,10 +2008,10 @@ public class GenericJaxRsClientDstu3Test { final String msg = ourCtx.newXmlParser().encodeResourceToString(oo); - ourResponseContentType = Constants.CT_FHIR_XML + "; charset=UTF-8"; - ourResponseBody = msg; + MY_SERVLET.ourResponseContentType = Constants.CT_FHIR_XML + "; charset=UTF-8"; + MY_SERVLET.ourResponseBody = msg; - IGenericClient client = ourCtx.newRestfulGenericClient("http://localhost:" + ourPort + "/fhir"); + IGenericClient client = ourCtx.newRestfulGenericClient(ourServer.getBaseUrl() + "/fhir"); Patient p = new Patient(); p.addName().addGiven("GIVEN"); @@ -2027,33 +2020,33 @@ public class GenericJaxRsClientDstu3Test { MethodOutcome response; response = client.validate().resource(p).encodedXml().execute(); - assertEquals("http://localhost:" + ourPort + "/fhir/Patient/$validate", ourRequestUri); - assertEquals("POST", ourRequestMethod); - assertEquals("", ourRequestBodyString); + assertEquals(ourServer.getBaseUrl() + "/fhir/Patient/$validate", MY_SERVLET.ourRequestUri); + assertEquals("POST", MY_SERVLET.ourRequestMethod); + assertEquals("", MY_SERVLET.ourRequestBodyString); assertNotNull(response.getOperationOutcome()); assertEquals("FOOBAR", toOo(response.getOperationOutcome()).getIssue().get(0).getDiagnosticsElement().getValue()); response = client.validate().resource(ourCtx.newXmlParser().encodeResourceToString(p)).execute(); - assertEquals("http://localhost:" + ourPort + "/fhir/Patient/$validate", ourRequestUri); - assertEquals("POST", ourRequestMethod); - assertEquals("", ourRequestBodyString); + assertEquals(ourServer.getBaseUrl() + "/fhir/Patient/$validate", MY_SERVLET.ourRequestUri); + assertEquals("POST", MY_SERVLET.ourRequestMethod); + assertEquals("", MY_SERVLET.ourRequestBodyString); assertNotNull(response.getOperationOutcome()); assertEquals("FOOBAR", toOo(response.getOperationOutcome()).getIssue().get(0).getDiagnosticsElement().getValue()); response = client.validate().resource(ourCtx.newJsonParser().encodeResourceToString(p)).execute(); - assertEquals("http://localhost:" + ourPort + "/fhir/Patient/$validate", ourRequestUri); - assertEquals("POST", ourRequestMethod); - assertEquals("{\"resourceType\":\"Parameters\",\"parameter\":[{\"name\":\"resource\",\"resource\":{\"resourceType\":\"Patient\",\"name\":[{\"given\":[\"GIVEN\"]}]}}]}", ourRequestBodyString); + assertEquals(ourServer.getBaseUrl() + "/fhir/Patient/$validate", MY_SERVLET.ourRequestUri); + assertEquals("POST", MY_SERVLET.ourRequestMethod); + assertEquals("{\"resourceType\":\"Parameters\",\"parameter\":[{\"name\":\"resource\",\"resource\":{\"resourceType\":\"Patient\",\"name\":[{\"given\":[\"GIVEN\"]}]}}]}", MY_SERVLET.ourRequestBodyString); assertNotNull(response.getOperationOutcome()); assertEquals("FOOBAR", toOo(response.getOperationOutcome()).getIssue().get(0).getDiagnosticsElement().getValue()); response = client.validate().resource(ourCtx.newJsonParser().encodeResourceToString(p)).prettyPrint().execute(); - assertEquals("http://localhost:" + ourPort + "/fhir/Patient/$validate?_pretty=true", ourRequestUri); - assertEquals("POST", ourRequestMethod); - assertThat(ourRequestBodyString, containsString("\"resourceType\": \"Parameters\",\n")); + assertEquals(ourServer.getBaseUrl() + "/fhir/Patient/$validate?_pretty=true", MY_SERVLET.ourRequestUri); + assertEquals("POST", MY_SERVLET.ourRequestMethod); + assertThat(MY_SERVLET.ourRequestBodyString, containsString("\"resourceType\": \"Parameters\",\n")); assertNotNull(response.getOperationOutcome()); assertEquals("FOOBAR", toOo(response.getOperationOutcome()).getIssue().get(0).getDiagnosticsElement().getValue()); @@ -2065,10 +2058,10 @@ public class GenericJaxRsClientDstu3Test { oo.addIssue().setDiagnostics("FOOBAR"); final String msg = ourCtx.newXmlParser().encodeResourceToString(oo); - ourResponseContentType = Constants.CT_FHIR_XML + "; charset=UTF-8"; - ourResponseBody = msg; + MY_SERVLET.ourResponseContentType = Constants.CT_FHIR_XML + "; charset=UTF-8"; + MY_SERVLET.ourResponseBody = msg; - IGenericClient client = ourCtx.newRestfulGenericClient("http://localhost:" + ourPort + "/fhir"); + IGenericClient client = ourCtx.newRestfulGenericClient(ourServer.getBaseUrl() + "/fhir"); client.setEncoding(EncodingEnum.XML); Patient p = new Patient(); @@ -2081,9 +2074,9 @@ public class GenericJaxRsClientDstu3Test { response = client.validate(p); //@formatter:on - assertEquals("http://localhost:" + ourPort + "/fhir/Patient/$validate?_format=xml", ourRequestUri); - assertEquals("POST", ourRequestMethod); - assertEquals("", ourRequestBodyString); + assertEquals(ourServer.getBaseUrl() + "/fhir/Patient/$validate?_format=xml", MY_SERVLET.ourRequestUri); + assertEquals("POST", MY_SERVLET.ourRequestMethod); + assertEquals("", MY_SERVLET.ourRequestBodyString); assertNotNull(response.getOperationOutcome()); assertEquals("FOOBAR", ((OperationOutcome) response.getOperationOutcome()).getIssue().get(0).getDiagnosticsElement().getValue()); @@ -2093,77 +2086,4 @@ public class GenericJaxRsClientDstu3Test { return (OperationOutcome) theOperationOutcome; } - @BeforeEach - public void beforeReset() { - ourRequestUri = null; - ourRequestUriAll = Lists.newArrayList(); - ourResponseStatus = 200; - ourResponseBody = null; - ourResponseBodies = null; - ourResponseCount = 0; - - ourResponseContentType = null; - ourRequestContentType = null; - ourRequestBodyBytes = null; - ourRequestBodyString = null; - ourRequestHeaders = null; - ourRequestFirstHeaders = null; - ourRequestMethod = null; - ourRequestHeadersAll = Lists.newArrayList(); - } - - @BeforeAll - public static void beforeClass() throws Exception { - ourCtx = FhirContext.forDstu3(); - - ourServer = new Server(0); - ourServer.setHandler(new AbstractHandler() { - - @Override - public void handle(String theArg0, Request theRequest, HttpServletRequest theServletRequest, HttpServletResponse theResp) throws IOException { - theRequest.setHandled(true); - ourRequestUri = theRequest.getHttpURI().toString(); - ourRequestUriAll.add(ourRequestUri); - ourRequestMethod = theRequest.getMethod(); - ourRequestContentType = theServletRequest.getContentType(); - ourRequestBodyBytes = IOUtils.toByteArray(theServletRequest.getInputStream()); - ourRequestBodyString = new String(ourRequestBodyBytes, Charsets.UTF_8); - - ourRequestHeaders = ArrayListMultimap.create(); - ourRequestHeadersAll.add(ourRequestHeaders); - ourRequestFirstHeaders = Maps.newHashMap(); - - for (Enumeration headerNameEnum = theRequest.getHeaderNames(); headerNameEnum.hasMoreElements(); ) { - String nextName = headerNameEnum.nextElement(); - for (Enumeration headerValueEnum = theRequest.getHeaders(nextName); headerValueEnum.hasMoreElements(); ) { - String nextValue = headerValueEnum.nextElement(); - if (ourRequestFirstHeaders.containsKey(nextName) == false) { - ourRequestFirstHeaders.put(nextName, new Header(nextName, nextValue)); - } - ourRequestHeaders.put(nextName, new Header(nextName, nextValue)); - } - } - - theResp.setStatus(ourResponseStatus); - - if (ourResponseBody != null) { - theResp.setContentType(ourResponseContentType); - theResp.getWriter().write(ourResponseBody); - } else if (ourResponseBodies != null) { - theResp.setContentType(ourResponseContentType); - theResp.getWriter().write(ourResponseBodies[ourResponseCount]); - } - - ourResponseCount++; - } - }); - - JettyUtil.startServer(ourServer); - ourPort = JettyUtil.getPortForStartedServer(ourServer); - } - - @AfterAll - public static void afterClass() throws Exception { - JettyUtil.closeServer(ourServer); - } } diff --git a/hapi-fhir-jaxrsserver-base/src/test/java/ca/uhn/fhir/jaxrs/client/JaxRsRestfulClientFactoryTest.java b/hapi-fhir-jaxrsserver-base/src/test/java/ca/uhn/fhir/jaxrs/client/JaxRsRestfulClientFactoryTest.java index 7a6a8df614a..6e701a28a24 100644 --- a/hapi-fhir-jaxrsserver-base/src/test/java/ca/uhn/fhir/jaxrs/client/JaxRsRestfulClientFactoryTest.java +++ b/hapi-fhir-jaxrsserver-base/src/test/java/ca/uhn/fhir/jaxrs/client/JaxRsRestfulClientFactoryTest.java @@ -14,8 +14,8 @@ import org.slf4j.LoggerFactory; import javax.net.ssl.SSLException; import javax.net.ssl.SSLHandshakeException; -import javax.ws.rs.client.Client; -import javax.ws.rs.core.Response; +import jakarta.ws.rs.client.Client; +import jakarta.ws.rs.core.Response; import java.util.ArrayList; import java.util.Arrays; diff --git a/hapi-fhir-jaxrsserver-base/src/test/java/ca/uhn/fhir/jaxrs/client/MyFilter.java b/hapi-fhir-jaxrsserver-base/src/test/java/ca/uhn/fhir/jaxrs/client/MyFilter.java index 99bbe79ec96..afb4ea4b538 100644 --- a/hapi-fhir-jaxrsserver-base/src/test/java/ca/uhn/fhir/jaxrs/client/MyFilter.java +++ b/hapi-fhir-jaxrsserver-base/src/test/java/ca/uhn/fhir/jaxrs/client/MyFilter.java @@ -1,9 +1,9 @@ package ca.uhn.fhir.jaxrs.client; -import javax.ws.rs.client.ClientRequestContext; -import javax.ws.rs.client.ClientResponseContext; -import javax.ws.rs.client.ClientResponseFilter; -import javax.ws.rs.ext.Provider; +import jakarta.ws.rs.client.ClientRequestContext; +import jakarta.ws.rs.client.ClientResponseContext; +import jakarta.ws.rs.client.ClientResponseFilter; +import jakarta.ws.rs.ext.Provider; import java.io.IOException; /** diff --git a/hapi-fhir-jaxrsserver-base/src/test/java/ca/uhn/fhir/jaxrs/server/AbstractJaxRsConformanceProviderDstu2Hl7OrgTest.java b/hapi-fhir-jaxrsserver-base/src/test/java/ca/uhn/fhir/jaxrs/server/AbstractJaxRsConformanceProviderDstu2Hl7OrgTest.java index 5354a5664b0..c7296101b31 100644 --- a/hapi-fhir-jaxrsserver-base/src/test/java/ca/uhn/fhir/jaxrs/server/AbstractJaxRsConformanceProviderDstu2Hl7OrgTest.java +++ b/hapi-fhir-jaxrsserver-base/src/test/java/ca/uhn/fhir/jaxrs/server/AbstractJaxRsConformanceProviderDstu2Hl7OrgTest.java @@ -9,9 +9,9 @@ import org.jboss.resteasy.specimpl.ResteasyHttpHeaders; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; -import javax.ws.rs.core.MultivaluedHashMap; -import javax.ws.rs.core.Response; -import javax.ws.rs.core.UriInfo; +import jakarta.ws.rs.core.MultivaluedHashMap; +import jakarta.ws.rs.core.Response; +import jakarta.ws.rs.core.UriInfo; import java.net.URI; import java.util.Arrays; import java.util.concurrent.ConcurrentHashMap; diff --git a/hapi-fhir-jaxrsserver-base/src/test/java/ca/uhn/fhir/jaxrs/server/AbstractJaxRsConformanceProviderDstu2_1Test.java b/hapi-fhir-jaxrsserver-base/src/test/java/ca/uhn/fhir/jaxrs/server/AbstractJaxRsConformanceProviderDstu2_1Test.java index eaf56d26e28..ec39710d611 100644 --- a/hapi-fhir-jaxrsserver-base/src/test/java/ca/uhn/fhir/jaxrs/server/AbstractJaxRsConformanceProviderDstu2_1Test.java +++ b/hapi-fhir-jaxrsserver-base/src/test/java/ca/uhn/fhir/jaxrs/server/AbstractJaxRsConformanceProviderDstu2_1Test.java @@ -9,10 +9,10 @@ import org.jboss.resteasy.specimpl.ResteasyHttpHeaders; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; -import javax.ws.rs.core.MultivaluedHashMap; -import javax.ws.rs.core.MultivaluedMap; -import javax.ws.rs.core.Response; -import javax.ws.rs.core.UriInfo; +import jakarta.ws.rs.core.MultivaluedHashMap; +import jakarta.ws.rs.core.MultivaluedMap; +import jakarta.ws.rs.core.Response; +import jakarta.ws.rs.core.UriInfo; import java.net.URI; import java.util.Arrays; import java.util.concurrent.ConcurrentHashMap; diff --git a/hapi-fhir-jaxrsserver-base/src/test/java/ca/uhn/fhir/jaxrs/server/AbstractJaxRsConformanceProviderDstu3Test.java b/hapi-fhir-jaxrsserver-base/src/test/java/ca/uhn/fhir/jaxrs/server/AbstractJaxRsConformanceProviderDstu3Test.java index 6c8ebb7190b..4b026a3f51f 100644 --- a/hapi-fhir-jaxrsserver-base/src/test/java/ca/uhn/fhir/jaxrs/server/AbstractJaxRsConformanceProviderDstu3Test.java +++ b/hapi-fhir-jaxrsserver-base/src/test/java/ca/uhn/fhir/jaxrs/server/AbstractJaxRsConformanceProviderDstu3Test.java @@ -9,9 +9,9 @@ import org.jboss.resteasy.specimpl.ResteasyHttpHeaders; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; -import javax.ws.rs.core.MultivaluedHashMap; -import javax.ws.rs.core.Response; -import javax.ws.rs.core.UriInfo; +import jakarta.ws.rs.core.MultivaluedHashMap; +import jakarta.ws.rs.core.Response; +import jakarta.ws.rs.core.UriInfo; import java.net.URI; import java.util.Arrays; import java.util.concurrent.ConcurrentHashMap; diff --git a/hapi-fhir-jaxrsserver-base/src/test/java/ca/uhn/fhir/jaxrs/server/AbstractJaxRsConformanceProviderR4Test.java b/hapi-fhir-jaxrsserver-base/src/test/java/ca/uhn/fhir/jaxrs/server/AbstractJaxRsConformanceProviderR4Test.java index 78725548b92..5492f0ec91d 100644 --- a/hapi-fhir-jaxrsserver-base/src/test/java/ca/uhn/fhir/jaxrs/server/AbstractJaxRsConformanceProviderR4Test.java +++ b/hapi-fhir-jaxrsserver-base/src/test/java/ca/uhn/fhir/jaxrs/server/AbstractJaxRsConformanceProviderR4Test.java @@ -9,9 +9,9 @@ import org.jboss.resteasy.specimpl.ResteasyHttpHeaders; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; -import javax.ws.rs.core.MultivaluedHashMap; -import javax.ws.rs.core.Response; -import javax.ws.rs.core.UriInfo; +import jakarta.ws.rs.core.MultivaluedHashMap; +import jakarta.ws.rs.core.Response; +import jakarta.ws.rs.core.UriInfo; import java.net.URI; import java.util.Arrays; import java.util.concurrent.ConcurrentHashMap; diff --git a/hapi-fhir-jaxrsserver-base/src/test/java/ca/uhn/fhir/jaxrs/server/AbstractJaxRsConformanceProviderTest.java b/hapi-fhir-jaxrsserver-base/src/test/java/ca/uhn/fhir/jaxrs/server/AbstractJaxRsConformanceProviderTest.java index f64dbe68f4d..e1f4825f0fe 100644 --- a/hapi-fhir-jaxrsserver-base/src/test/java/ca/uhn/fhir/jaxrs/server/AbstractJaxRsConformanceProviderTest.java +++ b/hapi-fhir-jaxrsserver-base/src/test/java/ca/uhn/fhir/jaxrs/server/AbstractJaxRsConformanceProviderTest.java @@ -8,9 +8,9 @@ import org.jboss.resteasy.specimpl.ResteasyHttpHeaders; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; -import javax.ws.rs.core.MultivaluedHashMap; -import javax.ws.rs.core.Response; -import javax.ws.rs.core.UriInfo; +import jakarta.ws.rs.core.MultivaluedHashMap; +import jakarta.ws.rs.core.Response; +import jakarta.ws.rs.core.UriInfo; import java.net.URI; import java.util.Arrays; import java.util.concurrent.ConcurrentHashMap; diff --git a/hapi-fhir-jaxrsserver-base/src/test/java/ca/uhn/fhir/jaxrs/server/AbstractJaxRsProviderTest.java b/hapi-fhir-jaxrsserver-base/src/test/java/ca/uhn/fhir/jaxrs/server/AbstractJaxRsProviderTest.java index 6abf8f9b82d..d3ff832da32 100644 --- a/hapi-fhir-jaxrsserver-base/src/test/java/ca/uhn/fhir/jaxrs/server/AbstractJaxRsProviderTest.java +++ b/hapi-fhir-jaxrsserver-base/src/test/java/ca/uhn/fhir/jaxrs/server/AbstractJaxRsProviderTest.java @@ -14,10 +14,10 @@ import org.junit.jupiter.api.extension.ExtendWith; import org.mockito.Mock; import org.mockito.junit.jupiter.MockitoExtension; -import javax.ws.rs.core.MultivaluedHashMap; -import javax.ws.rs.core.MultivaluedMap; -import javax.ws.rs.core.Response; -import javax.ws.rs.core.UriInfo; +import jakarta.ws.rs.core.MultivaluedHashMap; +import jakarta.ws.rs.core.MultivaluedMap; +import jakarta.ws.rs.core.Response; +import jakarta.ws.rs.core.UriInfo; import java.io.IOException; import java.net.URI; import java.net.URISyntaxException; diff --git a/hapi-fhir-jaxrsserver-base/src/test/java/ca/uhn/fhir/jaxrs/server/AbstractJaxRsResourceProviderDstu3Test.java b/hapi-fhir-jaxrsserver-base/src/test/java/ca/uhn/fhir/jaxrs/server/AbstractJaxRsResourceProviderDstu3Test.java index 620a8b3dc1c..2b136dd03c0 100644 --- a/hapi-fhir-jaxrsserver-base/src/test/java/ca/uhn/fhir/jaxrs/server/AbstractJaxRsResourceProviderDstu3Test.java +++ b/hapi-fhir-jaxrsserver-base/src/test/java/ca/uhn/fhir/jaxrs/server/AbstractJaxRsResourceProviderDstu3Test.java @@ -20,8 +20,8 @@ import ca.uhn.fhir.test.utilities.JettyUtil; import ca.uhn.fhir.util.TestUtil; import org.apache.commons.lang3.StringUtils; import org.eclipse.jetty.server.Server; -import org.eclipse.jetty.servlet.ServletContextHandler; -import org.eclipse.jetty.servlet.ServletHolder; +import org.eclipse.jetty.ee10.servlet.ServletContextHandler; +import org.eclipse.jetty.ee10.servlet.ServletHolder; import org.hl7.fhir.dstu3.model.Bundle; import org.hl7.fhir.dstu3.model.Bundle.BundleEntryComponent; import org.hl7.fhir.dstu3.model.CapabilityStatement; diff --git a/hapi-fhir-jaxrsserver-base/src/test/java/ca/uhn/fhir/jaxrs/server/AbstractJaxRsResourceProviderTest.java b/hapi-fhir-jaxrsserver-base/src/test/java/ca/uhn/fhir/jaxrs/server/AbstractJaxRsResourceProviderTest.java index 67b5fff1d67..a0c1c6ecd82 100644 --- a/hapi-fhir-jaxrsserver-base/src/test/java/ca/uhn/fhir/jaxrs/server/AbstractJaxRsResourceProviderTest.java +++ b/hapi-fhir-jaxrsserver-base/src/test/java/ca/uhn/fhir/jaxrs/server/AbstractJaxRsResourceProviderTest.java @@ -32,8 +32,8 @@ import ca.uhn.fhir.test.utilities.JettyUtil; import ca.uhn.fhir.util.TestUtil; import org.apache.commons.lang3.StringUtils; import org.eclipse.jetty.server.Server; -import org.eclipse.jetty.servlet.ServletContextHandler; -import org.eclipse.jetty.servlet.ServletHolder; +import org.eclipse.jetty.ee10.servlet.ServletContextHandler; +import org.eclipse.jetty.ee10.servlet.ServletHolder; import org.hl7.fhir.instance.model.api.IBaseOperationOutcome; import org.junit.jupiter.api.AfterAll; import org.junit.jupiter.api.BeforeAll; diff --git a/hapi-fhir-jaxrsserver-base/src/test/java/ca/uhn/fhir/jaxrs/server/interceptor/JaxRsExceptionInterceptorTest.java b/hapi-fhir-jaxrsserver-base/src/test/java/ca/uhn/fhir/jaxrs/server/interceptor/JaxRsExceptionInterceptorTest.java index 044d00b10b7..c0d1de719a2 100644 --- a/hapi-fhir-jaxrsserver-base/src/test/java/ca/uhn/fhir/jaxrs/server/interceptor/JaxRsExceptionInterceptorTest.java +++ b/hapi-fhir-jaxrsserver-base/src/test/java/ca/uhn/fhir/jaxrs/server/interceptor/JaxRsExceptionInterceptorTest.java @@ -1,26 +1,35 @@ package ca.uhn.fhir.jaxrs.server.interceptor; -import static org.junit.jupiter.api.Assertions.*; -import static org.mockito.ArgumentMatchers.any; -import static org.mockito.ArgumentMatchers.isNull; -import static org.mockito.Mockito.*; - -import java.net.URI; -import java.util.HashMap; - -import javax.interceptor.InvocationContext; -import javax.servlet.ServletException; -import javax.ws.rs.core.*; - -import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.Test; - import ca.uhn.fhir.jaxrs.server.AbstractJaxRsProvider; import ca.uhn.fhir.jaxrs.server.test.TestJaxRsDummyPatientProvider; import ca.uhn.fhir.jaxrs.server.util.JaxRsRequest; import ca.uhn.fhir.rest.api.server.RequestDetails; -import ca.uhn.fhir.rest.server.exceptions.*; +import ca.uhn.fhir.rest.server.exceptions.BaseServerResponseException; +import ca.uhn.fhir.rest.server.exceptions.InternalErrorException; +import ca.uhn.fhir.rest.server.exceptions.NotImplementedOperationException; import ca.uhn.fhir.rest.server.interceptor.ExceptionHandlingInterceptor; +import jakarta.interceptor.InvocationContext; +import jakarta.servlet.ServletException; +import jakarta.ws.rs.core.HttpHeaders; +import jakarta.ws.rs.core.Response; +import jakarta.ws.rs.core.UriInfo; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; + +import java.net.URI; +import java.util.HashMap; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertSame; +import static org.junit.jupiter.api.Assertions.assertTrue; +import static org.junit.jupiter.api.Assertions.fail; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.isNull; +import static org.mockito.Mockito.doReturn; +import static org.mockito.Mockito.doThrow; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.spy; +import static org.mockito.Mockito.when; public class JaxRsExceptionInterceptorTest { @@ -89,7 +98,7 @@ public class JaxRsExceptionInterceptorTest { when(context.proceed()).thenThrow(new ServletException()); JaxRsResponseException thrownException = new JaxRsResponseException(new NotImplementedOperationException("not implemented")); - doThrow(new javax.servlet.ServletException("someMessage")).when(exceptionHandler).handleException(request, thrownException); + doThrow(new jakarta.servlet.ServletException("someMessage")).when(exceptionHandler).handleException(request, thrownException); Response result = interceptor.convertExceptionIntoResponse(request, thrownException); assertEquals(InternalErrorException.STATUS_CODE, result.getStatus()); } diff --git a/hapi-fhir-jaxrsserver-base/src/test/java/ca/uhn/fhir/jaxrs/server/interceptor/JaxRsResponseExceptionTest.java b/hapi-fhir-jaxrsserver-base/src/test/java/ca/uhn/fhir/jaxrs/server/interceptor/JaxRsResponseExceptionTest.java index bfa5d6b8b8f..58a7a911f7a 100644 --- a/hapi-fhir-jaxrsserver-base/src/test/java/ca/uhn/fhir/jaxrs/server/interceptor/JaxRsResponseExceptionTest.java +++ b/hapi-fhir-jaxrsserver-base/src/test/java/ca/uhn/fhir/jaxrs/server/interceptor/JaxRsResponseExceptionTest.java @@ -1,14 +1,12 @@ package ca.uhn.fhir.jaxrs.server.interceptor; +import ca.uhn.fhir.rest.server.exceptions.ForbiddenOperationException; +import jakarta.ejb.ApplicationException; +import org.junit.jupiter.api.Test; + import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertNotNull; -import javax.ejb.ApplicationException; - -import org.junit.jupiter.api.Test; - -import ca.uhn.fhir.rest.server.exceptions.ForbiddenOperationException; - public class JaxRsResponseExceptionTest { @Test diff --git a/hapi-fhir-jaxrsserver-base/src/test/java/ca/uhn/fhir/jaxrs/server/test/TestJaxRsConformanceRestProvider.java b/hapi-fhir-jaxrsserver-base/src/test/java/ca/uhn/fhir/jaxrs/server/test/TestJaxRsConformanceRestProvider.java index 7d9231b0930..30c3ddba7c8 100644 --- a/hapi-fhir-jaxrsserver-base/src/test/java/ca/uhn/fhir/jaxrs/server/test/TestJaxRsConformanceRestProvider.java +++ b/hapi-fhir-jaxrsserver-base/src/test/java/ca/uhn/fhir/jaxrs/server/test/TestJaxRsConformanceRestProvider.java @@ -3,11 +3,11 @@ package ca.uhn.fhir.jaxrs.server.test; import ca.uhn.fhir.jaxrs.server.AbstractJaxRsConformanceProvider; import ca.uhn.fhir.rest.api.Constants; import ca.uhn.fhir.rest.server.IResourceProvider; -import javax.ws.rs.Path; -import javax.ws.rs.Produces; -import javax.ws.rs.core.MediaType; +import jakarta.ejb.Stateless; +import jakarta.ws.rs.Path; +import jakarta.ws.rs.Produces; +import jakarta.ws.rs.core.MediaType; -import javax.ejb.Stateless; import java.util.concurrent.ConcurrentHashMap; /** diff --git a/hapi-fhir-jaxrsserver-base/src/test/java/ca/uhn/fhir/jaxrs/server/test/TestJaxRsConformanceRestProviderDstu3.java b/hapi-fhir-jaxrsserver-base/src/test/java/ca/uhn/fhir/jaxrs/server/test/TestJaxRsConformanceRestProviderDstu3.java index 335272b506d..d9b1b675eae 100644 --- a/hapi-fhir-jaxrsserver-base/src/test/java/ca/uhn/fhir/jaxrs/server/test/TestJaxRsConformanceRestProviderDstu3.java +++ b/hapi-fhir-jaxrsserver-base/src/test/java/ca/uhn/fhir/jaxrs/server/test/TestJaxRsConformanceRestProviderDstu3.java @@ -1,17 +1,15 @@ package ca.uhn.fhir.jaxrs.server.test; -import java.util.concurrent.ConcurrentHashMap; - -import javax.ejb.Stateless; - -import javax.ws.rs.Path; -import javax.ws.rs.Produces; -import javax.ws.rs.core.MediaType; - import ca.uhn.fhir.context.FhirContext; import ca.uhn.fhir.jaxrs.server.AbstractJaxRsConformanceProvider; import ca.uhn.fhir.rest.api.Constants; import ca.uhn.fhir.rest.server.IResourceProvider; +import jakarta.ejb.Stateless; +import jakarta.ws.rs.Path; +import jakarta.ws.rs.Produces; +import jakarta.ws.rs.core.MediaType; + +import java.util.concurrent.ConcurrentHashMap; /** * A conformance provider exposes the mock patient and this provider diff --git a/hapi-fhir-jaxrsserver-base/src/test/java/ca/uhn/fhir/jaxrs/server/test/TestJaxRsMockPageProvider.java b/hapi-fhir-jaxrsserver-base/src/test/java/ca/uhn/fhir/jaxrs/server/test/TestJaxRsMockPageProvider.java index f489a7290f4..cff9cf63005 100644 --- a/hapi-fhir-jaxrsserver-base/src/test/java/ca/uhn/fhir/jaxrs/server/test/TestJaxRsMockPageProvider.java +++ b/hapi-fhir-jaxrsserver-base/src/test/java/ca/uhn/fhir/jaxrs/server/test/TestJaxRsMockPageProvider.java @@ -1,13 +1,12 @@ package ca.uhn.fhir.jaxrs.server.test; -import javax.ejb.Stateless; -import javax.ws.rs.Path; -import javax.ws.rs.Produces; -import javax.ws.rs.core.MediaType; - import ca.uhn.fhir.jaxrs.server.AbstractJaxRsPageProvider; import ca.uhn.fhir.rest.api.Constants; import ca.uhn.fhir.rest.server.IPagingProvider; +import jakarta.ejb.Stateless; +import jakarta.ws.rs.Path; +import jakarta.ws.rs.Produces; +import jakarta.ws.rs.core.MediaType; @Path("/") @Stateless diff --git a/hapi-fhir-jaxrsserver-base/src/test/java/ca/uhn/fhir/jaxrs/server/test/TestJaxRsMockPageProviderDstu3.java b/hapi-fhir-jaxrsserver-base/src/test/java/ca/uhn/fhir/jaxrs/server/test/TestJaxRsMockPageProviderDstu3.java index aecb104681f..25a140ec821 100644 --- a/hapi-fhir-jaxrsserver-base/src/test/java/ca/uhn/fhir/jaxrs/server/test/TestJaxRsMockPageProviderDstu3.java +++ b/hapi-fhir-jaxrsserver-base/src/test/java/ca/uhn/fhir/jaxrs/server/test/TestJaxRsMockPageProviderDstu3.java @@ -1,9 +1,9 @@ package ca.uhn.fhir.jaxrs.server.test; -import javax.ejb.Stateless; -import javax.ws.rs.Path; -import javax.ws.rs.Produces; -import javax.ws.rs.core.MediaType; +import jakarta.ejb.Stateless; +import jakarta.ws.rs.Path; +import jakarta.ws.rs.Produces; +import jakarta.ws.rs.core.MediaType; import ca.uhn.fhir.context.FhirContext; import ca.uhn.fhir.jaxrs.server.AbstractJaxRsPageProvider; diff --git a/hapi-fhir-jaxrsserver-base/src/test/java/ca/uhn/fhir/jaxrs/server/test/TestJaxRsMockPatientRestProvider.java b/hapi-fhir-jaxrsserver-base/src/test/java/ca/uhn/fhir/jaxrs/server/test/TestJaxRsMockPatientRestProvider.java index 0f3d26d6fec..c765cecd6ea 100644 --- a/hapi-fhir-jaxrsserver-base/src/test/java/ca/uhn/fhir/jaxrs/server/test/TestJaxRsMockPatientRestProvider.java +++ b/hapi-fhir-jaxrsserver-base/src/test/java/ca/uhn/fhir/jaxrs/server/test/TestJaxRsMockPatientRestProvider.java @@ -1,34 +1,48 @@ package ca.uhn.fhir.jaxrs.server.test; -import java.util.Collections; -import java.util.Date; -import java.util.List; - -import javax.ejb.Stateless; -import javax.interceptor.Interceptors; -import javax.ws.rs.*; -import javax.ws.rs.core.MediaType; -import javax.ws.rs.core.Response; - -import ca.uhn.fhir.rest.api.server.IBundleProvider; -import ca.uhn.fhir.rest.api.server.RequestDetails; -import ca.uhn.fhir.rest.param.DateRangeParam; -import ca.uhn.fhir.rest.server.SimpleBundleProvider; -import org.hl7.fhir.instance.model.api.IIdType; -import org.mockito.Mockito; - import ca.uhn.fhir.jaxrs.server.AbstractJaxRsResourceProvider; import ca.uhn.fhir.jaxrs.server.interceptor.JaxRsExceptionInterceptor; import ca.uhn.fhir.model.api.IResource; -import ca.uhn.fhir.model.dstu2.resource.*; +import ca.uhn.fhir.model.dstu2.resource.OperationOutcome; +import ca.uhn.fhir.model.dstu2.resource.Parameters; +import ca.uhn.fhir.model.dstu2.resource.Patient; import ca.uhn.fhir.model.primitive.IdDt; import ca.uhn.fhir.model.primitive.StringDt; -import ca.uhn.fhir.rest.annotation.*; -import ca.uhn.fhir.rest.api.*; +import ca.uhn.fhir.rest.annotation.ConditionalUrlParam; +import ca.uhn.fhir.rest.annotation.Create; +import ca.uhn.fhir.rest.annotation.Delete; +import ca.uhn.fhir.rest.annotation.History; +import ca.uhn.fhir.rest.annotation.IdParam; +import ca.uhn.fhir.rest.annotation.Operation; +import ca.uhn.fhir.rest.annotation.OperationParam; +import ca.uhn.fhir.rest.annotation.Read; +import ca.uhn.fhir.rest.annotation.RequiredParam; +import ca.uhn.fhir.rest.annotation.ResourceParam; +import ca.uhn.fhir.rest.annotation.Search; +import ca.uhn.fhir.rest.annotation.Update; +import ca.uhn.fhir.rest.annotation.Validate; +import ca.uhn.fhir.rest.api.Constants; +import ca.uhn.fhir.rest.api.MethodOutcome; +import ca.uhn.fhir.rest.api.RequestTypeEnum; +import ca.uhn.fhir.rest.api.RestOperationTypeEnum; +import ca.uhn.fhir.rest.api.server.IBundleProvider; import ca.uhn.fhir.rest.param.StringAndListParam; import ca.uhn.fhir.rest.param.StringParam; import ca.uhn.fhir.rest.server.FifoMemoryPagingProvider; import ca.uhn.fhir.rest.server.IPagingProvider; +import jakarta.ejb.Stateless; +import jakarta.interceptor.Interceptors; +import jakarta.ws.rs.GET; +import jakarta.ws.rs.POST; +import jakarta.ws.rs.Path; +import jakarta.ws.rs.PathParam; +import jakarta.ws.rs.Produces; +import jakarta.ws.rs.core.MediaType; +import jakarta.ws.rs.core.Response; +import org.hl7.fhir.instance.model.api.IIdType; +import org.mockito.Mockito; + +import java.util.List; /** * A test server delegating each call to a mock diff --git a/hapi-fhir-jaxrsserver-base/src/test/java/ca/uhn/fhir/jaxrs/server/test/TestJaxRsMockPatientRestProviderDstu2Hl7Org.java b/hapi-fhir-jaxrsserver-base/src/test/java/ca/uhn/fhir/jaxrs/server/test/TestJaxRsMockPatientRestProviderDstu2Hl7Org.java index 99687058ced..963fe0b1779 100644 --- a/hapi-fhir-jaxrsserver-base/src/test/java/ca/uhn/fhir/jaxrs/server/test/TestJaxRsMockPatientRestProviderDstu2Hl7Org.java +++ b/hapi-fhir-jaxrsserver-base/src/test/java/ca/uhn/fhir/jaxrs/server/test/TestJaxRsMockPatientRestProviderDstu2Hl7Org.java @@ -2,11 +2,11 @@ package ca.uhn.fhir.jaxrs.server.test; import java.util.List; -import javax.ejb.Stateless; -import javax.interceptor.Interceptors; -import javax.ws.rs.*; -import javax.ws.rs.core.MediaType; -import javax.ws.rs.core.Response; +import jakarta.ejb.Stateless; +import jakarta.interceptor.Interceptors; +import jakarta.ws.rs.*; +import jakarta.ws.rs.core.MediaType; +import jakarta.ws.rs.core.Response; import org.hl7.fhir.dstu2.model.*; import org.hl7.fhir.instance.model.api.IBaseResource; diff --git a/hapi-fhir-jaxrsserver-base/src/test/java/ca/uhn/fhir/jaxrs/server/test/TestJaxRsMockPatientRestProviderDstu2_1.java b/hapi-fhir-jaxrsserver-base/src/test/java/ca/uhn/fhir/jaxrs/server/test/TestJaxRsMockPatientRestProviderDstu2_1.java index 08aab47036e..973fc38d8da 100644 --- a/hapi-fhir-jaxrsserver-base/src/test/java/ca/uhn/fhir/jaxrs/server/test/TestJaxRsMockPatientRestProviderDstu2_1.java +++ b/hapi-fhir-jaxrsserver-base/src/test/java/ca/uhn/fhir/jaxrs/server/test/TestJaxRsMockPatientRestProviderDstu2_1.java @@ -1,27 +1,46 @@ package ca.uhn.fhir.jaxrs.server.test; -import java.util.List; - -import javax.ejb.Stateless; -import javax.interceptor.Interceptors; -import javax.ws.rs.*; -import javax.ws.rs.core.MediaType; -import javax.ws.rs.core.Response; - -import org.hl7.fhir.dstu2016may.model.*; -import org.hl7.fhir.instance.model.api.IBaseResource; -import org.mockito.Mockito; - import ca.uhn.fhir.context.FhirContext; import ca.uhn.fhir.jaxrs.server.AbstractJaxRsResourceProvider; import ca.uhn.fhir.jaxrs.server.interceptor.JaxRsExceptionInterceptor; -import ca.uhn.fhir.rest.annotation.*; -import ca.uhn.fhir.rest.api.*; +import ca.uhn.fhir.rest.annotation.ConditionalUrlParam; +import ca.uhn.fhir.rest.annotation.Create; +import ca.uhn.fhir.rest.annotation.Delete; +import ca.uhn.fhir.rest.annotation.IdParam; +import ca.uhn.fhir.rest.annotation.Operation; +import ca.uhn.fhir.rest.annotation.OperationParam; +import ca.uhn.fhir.rest.annotation.Read; +import ca.uhn.fhir.rest.annotation.RequiredParam; +import ca.uhn.fhir.rest.annotation.ResourceParam; +import ca.uhn.fhir.rest.annotation.Search; +import ca.uhn.fhir.rest.annotation.Update; +import ca.uhn.fhir.rest.annotation.Validate; import ca.uhn.fhir.rest.api.Constants; +import ca.uhn.fhir.rest.api.MethodOutcome; +import ca.uhn.fhir.rest.api.RequestTypeEnum; +import ca.uhn.fhir.rest.api.RestOperationTypeEnum; import ca.uhn.fhir.rest.param.StringAndListParam; import ca.uhn.fhir.rest.param.StringParam; import ca.uhn.fhir.rest.server.FifoMemoryPagingProvider; import ca.uhn.fhir.rest.server.IPagingProvider; +import jakarta.ejb.Stateless; +import jakarta.interceptor.Interceptors; +import jakarta.ws.rs.GET; +import jakarta.ws.rs.POST; +import jakarta.ws.rs.Path; +import jakarta.ws.rs.PathParam; +import jakarta.ws.rs.Produces; +import jakarta.ws.rs.core.MediaType; +import jakarta.ws.rs.core.Response; +import org.hl7.fhir.dstu2016may.model.IdType; +import org.hl7.fhir.dstu2016may.model.OperationOutcome; +import org.hl7.fhir.dstu2016may.model.Parameters; +import org.hl7.fhir.dstu2016may.model.Patient; +import org.hl7.fhir.dstu2016may.model.StringType; +import org.hl7.fhir.instance.model.api.IBaseResource; +import org.mockito.Mockito; + +import java.util.List; /** * A test server delegating each call to a mock diff --git a/hapi-fhir-jaxrsserver-base/src/test/java/ca/uhn/fhir/jaxrs/server/test/TestJaxRsMockPatientRestProviderDstu3.java b/hapi-fhir-jaxrsserver-base/src/test/java/ca/uhn/fhir/jaxrs/server/test/TestJaxRsMockPatientRestProviderDstu3.java index 398734ea1e3..03a1d81de9b 100644 --- a/hapi-fhir-jaxrsserver-base/src/test/java/ca/uhn/fhir/jaxrs/server/test/TestJaxRsMockPatientRestProviderDstu3.java +++ b/hapi-fhir-jaxrsserver-base/src/test/java/ca/uhn/fhir/jaxrs/server/test/TestJaxRsMockPatientRestProviderDstu3.java @@ -2,11 +2,11 @@ package ca.uhn.fhir.jaxrs.server.test; import java.util.List; -import javax.ejb.Stateless; -import javax.interceptor.Interceptors; -import javax.ws.rs.*; -import javax.ws.rs.core.MediaType; -import javax.ws.rs.core.Response; +import jakarta.ejb.Stateless; +import jakarta.interceptor.Interceptors; +import jakarta.ws.rs.*; +import jakarta.ws.rs.core.MediaType; +import jakarta.ws.rs.core.Response; import org.hl7.fhir.dstu3.model.*; import org.hl7.fhir.instance.model.api.IBaseResource; diff --git a/hapi-fhir-jaxrsserver-base/src/test/java/ca/uhn/fhir/jaxrs/server/test/TestJaxRsMockPatientRestProviderR4.java b/hapi-fhir-jaxrsserver-base/src/test/java/ca/uhn/fhir/jaxrs/server/test/TestJaxRsMockPatientRestProviderR4.java index 0b34916b778..ccaaa2664df 100644 --- a/hapi-fhir-jaxrsserver-base/src/test/java/ca/uhn/fhir/jaxrs/server/test/TestJaxRsMockPatientRestProviderR4.java +++ b/hapi-fhir-jaxrsserver-base/src/test/java/ca/uhn/fhir/jaxrs/server/test/TestJaxRsMockPatientRestProviderR4.java @@ -1,27 +1,46 @@ package ca.uhn.fhir.jaxrs.server.test; -import java.util.List; - -import javax.ejb.Stateless; -import javax.interceptor.Interceptors; -import javax.ws.rs.*; -import javax.ws.rs.core.MediaType; -import javax.ws.rs.core.Response; - -import org.hl7.fhir.r4.model.*; -import org.hl7.fhir.instance.model.api.IBaseResource; -import org.mockito.Mockito; - import ca.uhn.fhir.context.FhirContext; import ca.uhn.fhir.jaxrs.server.AbstractJaxRsResourceProvider; import ca.uhn.fhir.jaxrs.server.interceptor.JaxRsExceptionInterceptor; -import ca.uhn.fhir.rest.annotation.*; -import ca.uhn.fhir.rest.api.*; +import ca.uhn.fhir.rest.annotation.ConditionalUrlParam; +import ca.uhn.fhir.rest.annotation.Create; +import ca.uhn.fhir.rest.annotation.Delete; +import ca.uhn.fhir.rest.annotation.IdParam; +import ca.uhn.fhir.rest.annotation.Operation; +import ca.uhn.fhir.rest.annotation.OperationParam; +import ca.uhn.fhir.rest.annotation.Read; +import ca.uhn.fhir.rest.annotation.RequiredParam; +import ca.uhn.fhir.rest.annotation.ResourceParam; +import ca.uhn.fhir.rest.annotation.Search; +import ca.uhn.fhir.rest.annotation.Update; +import ca.uhn.fhir.rest.annotation.Validate; import ca.uhn.fhir.rest.api.Constants; +import ca.uhn.fhir.rest.api.MethodOutcome; +import ca.uhn.fhir.rest.api.RequestTypeEnum; +import ca.uhn.fhir.rest.api.RestOperationTypeEnum; import ca.uhn.fhir.rest.param.StringAndListParam; import ca.uhn.fhir.rest.param.StringParam; import ca.uhn.fhir.rest.server.FifoMemoryPagingProvider; import ca.uhn.fhir.rest.server.IPagingProvider; +import jakarta.ejb.Stateless; +import jakarta.interceptor.Interceptors; +import jakarta.ws.rs.GET; +import jakarta.ws.rs.POST; +import jakarta.ws.rs.Path; +import jakarta.ws.rs.PathParam; +import jakarta.ws.rs.Produces; +import jakarta.ws.rs.core.MediaType; +import jakarta.ws.rs.core.Response; +import org.hl7.fhir.instance.model.api.IBaseResource; +import org.hl7.fhir.r4.model.IdType; +import org.hl7.fhir.r4.model.OperationOutcome; +import org.hl7.fhir.r4.model.Parameters; +import org.hl7.fhir.r4.model.Patient; +import org.hl7.fhir.r4.model.StringType; +import org.mockito.Mockito; + +import java.util.List; /** * A test server delegating each call to a mock diff --git a/hapi-fhir-jaxrsserver-base/src/test/java/ca/uhn/fhir/jaxrs/server/util/JaxRsRequestDstu3Test.java b/hapi-fhir-jaxrsserver-base/src/test/java/ca/uhn/fhir/jaxrs/server/util/JaxRsRequestDstu3Test.java index a9d22cac006..3fba820df81 100644 --- a/hapi-fhir-jaxrsserver-base/src/test/java/ca/uhn/fhir/jaxrs/server/util/JaxRsRequestDstu3Test.java +++ b/hapi-fhir-jaxrsserver-base/src/test/java/ca/uhn/fhir/jaxrs/server/util/JaxRsRequestDstu3Test.java @@ -9,9 +9,9 @@ import org.jboss.resteasy.specimpl.ResteasyHttpHeaders; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; -import javax.ws.rs.core.MultivaluedHashMap; -import javax.ws.rs.core.MultivaluedMap; -import javax.ws.rs.core.UriInfo; +import jakarta.ws.rs.core.MultivaluedHashMap; +import jakarta.ws.rs.core.MultivaluedMap; +import jakarta.ws.rs.core.UriInfo; import java.io.IOException; import java.net.URISyntaxException; import java.util.Arrays; diff --git a/hapi-fhir-jaxrsserver-base/src/test/java/ca/uhn/fhir/jaxrs/server/util/JaxRsRequestTest.java b/hapi-fhir-jaxrsserver-base/src/test/java/ca/uhn/fhir/jaxrs/server/util/JaxRsRequestTest.java index f4badf06e66..5502f8acdc9 100644 --- a/hapi-fhir-jaxrsserver-base/src/test/java/ca/uhn/fhir/jaxrs/server/util/JaxRsRequestTest.java +++ b/hapi-fhir-jaxrsserver-base/src/test/java/ca/uhn/fhir/jaxrs/server/util/JaxRsRequestTest.java @@ -3,15 +3,14 @@ package ca.uhn.fhir.jaxrs.server.util; import ca.uhn.fhir.jaxrs.server.test.TestJaxRsDummyPatientProvider; import ca.uhn.fhir.rest.api.RequestTypeEnum; import ca.uhn.fhir.rest.api.RestOperationTypeEnum; -import ca.uhn.fhir.rest.server.exceptions.NotImplementedOperationException; import org.apache.commons.lang3.StringUtils; import org.jboss.resteasy.specimpl.ResteasyHttpHeaders; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; -import javax.ws.rs.core.MultivaluedHashMap; -import javax.ws.rs.core.MultivaluedMap; -import javax.ws.rs.core.UriInfo; +import jakarta.ws.rs.core.MultivaluedHashMap; +import jakarta.ws.rs.core.MultivaluedMap; +import jakarta.ws.rs.core.UriInfo; import java.io.IOException; import java.net.URISyntaxException; import java.util.Arrays; diff --git a/hapi-fhir-jaxrsserver-base/src/test/java/ca/uhn/fhir/jaxrs/server/util/JaxRsResponseDstu3Test.java b/hapi-fhir-jaxrsserver-base/src/test/java/ca/uhn/fhir/jaxrs/server/util/JaxRsResponseDstu3Test.java index 9963c14f8b3..17842eeb0e1 100644 --- a/hapi-fhir-jaxrsserver-base/src/test/java/ca/uhn/fhir/jaxrs/server/util/JaxRsResponseDstu3Test.java +++ b/hapi-fhir-jaxrsserver-base/src/test/java/ca/uhn/fhir/jaxrs/server/util/JaxRsResponseDstu3Test.java @@ -9,7 +9,7 @@ import java.net.URISyntaxException; import java.util.Collections; import java.util.Set; -import javax.ws.rs.core.Response; +import jakarta.ws.rs.core.Response; import org.hl7.fhir.dstu3.model.*; import org.hl7.fhir.instance.model.api.IBaseBinary; diff --git a/hapi-fhir-jaxrsserver-base/src/test/java/ca/uhn/fhir/jaxrs/server/util/JaxRsResponseTest.java b/hapi-fhir-jaxrsserver-base/src/test/java/ca/uhn/fhir/jaxrs/server/util/JaxRsResponseTest.java index 3d296572414..c743127d1e5 100644 --- a/hapi-fhir-jaxrsserver-base/src/test/java/ca/uhn/fhir/jaxrs/server/util/JaxRsResponseTest.java +++ b/hapi-fhir-jaxrsserver-base/src/test/java/ca/uhn/fhir/jaxrs/server/util/JaxRsResponseTest.java @@ -10,7 +10,7 @@ import java.net.URISyntaxException; import java.util.Collections; import java.util.Set; -import javax.ws.rs.core.Response; +import jakarta.ws.rs.core.Response; import org.hamcrest.Matchers; import org.hl7.fhir.instance.model.api.IBaseBinary; diff --git a/hapi-fhir-jpa/pom.xml b/hapi-fhir-jpa/pom.xml index 8c6b15cd275..dc3ad059655 100644 --- a/hapi-fhir-jpa/pom.xml +++ b/hapi-fhir-jpa/pom.xml @@ -5,7 +5,7 @@ ca.uhn.hapi.fhir hapi-deployable-pom - 6.9.7-SNAPSHOT + 6.11.7-SNAPSHOT ../hapi-deployable-pom/pom.xml @@ -35,14 +35,6 @@
    - - org.hibernate - hibernate-entitymanager - - - org.hibernate - hibernate-java8 - org.hibernate.validator hibernate-validator @@ -59,7 +51,7 @@ org.hibernate.search - hibernate-search-mapper-orm + hibernate-search-mapper-orm-orm6 org.apache.logging.log4j diff --git a/hapi-fhir-jpa/src/main/java/ca/uhn/fhir/jpa/config/HapiFhirLocalContainerEntityManagerFactoryBean.java b/hapi-fhir-jpa/src/main/java/ca/uhn/fhir/jpa/config/HapiFhirLocalContainerEntityManagerFactoryBean.java index e00f858b49f..6dd0a286b55 100644 --- a/hapi-fhir-jpa/src/main/java/ca/uhn/fhir/jpa/config/HapiFhirLocalContainerEntityManagerFactoryBean.java +++ b/hapi-fhir-jpa/src/main/java/ca/uhn/fhir/jpa/config/HapiFhirLocalContainerEntityManagerFactoryBean.java @@ -21,7 +21,7 @@ package ca.uhn.fhir.jpa.config; import com.google.common.base.Strings; import org.hibernate.cfg.AvailableSettings; -import org.hibernate.query.criteria.LiteralHandlingMode; +import org.hibernate.query.criteria.ValueHandlingMode; import org.hibernate.resource.jdbc.spi.PhysicalConnectionHandlingMode; import org.springframework.beans.factory.config.ConfigurableListableBeanFactory; import org.springframework.orm.hibernate5.SpringBeanContainer; @@ -51,13 +51,14 @@ public class HapiFhirLocalContainerEntityManagerFactoryBean extends LocalContain Map retVal = super.getJpaPropertyMap(); // SOMEDAY these defaults can be set in the constructor. setJpaProperties does a merge. - if (!retVal.containsKey(AvailableSettings.CRITERIA_LITERAL_HANDLING_MODE)) { - retVal.put(AvailableSettings.CRITERIA_LITERAL_HANDLING_MODE, LiteralHandlingMode.BIND); + if (!retVal.containsKey(AvailableSettings.CRITERIA_VALUE_HANDLING_MODE)) { + retVal.put(AvailableSettings.CRITERIA_VALUE_HANDLING_MODE, ValueHandlingMode.BIND); } if (!retVal.containsKey(AvailableSettings.CONNECTION_HANDLING)) { retVal.put( - AvailableSettings.CONNECTION_HANDLING, PhysicalConnectionHandlingMode.DELAYED_ACQUISITION_AND_HOLD); + AvailableSettings.CONNECTION_HANDLING, + PhysicalConnectionHandlingMode.DELAYED_ACQUISITION_AND_RELEASE_AFTER_TRANSACTION); } /* diff --git a/hapi-fhir-jpa/src/main/java/ca/uhn/fhir/jpa/model/dialect/HapiFhirCockroachDialect.java b/hapi-fhir-jpa/src/main/java/ca/uhn/fhir/jpa/model/dialect/HapiFhirCockroachDialect.java new file mode 100644 index 00000000000..e38ff5d7878 --- /dev/null +++ b/hapi-fhir-jpa/src/main/java/ca/uhn/fhir/jpa/model/dialect/HapiFhirCockroachDialect.java @@ -0,0 +1,42 @@ +/*- + * #%L + * HAPI FHIR JPA Model + * %% + * Copyright (C) 2014 - 2023 Smile CDR, Inc. + * %% + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * #L% + */ +package ca.uhn.fhir.jpa.model.dialect; + +import org.hibernate.dialect.CockroachDialect; +import org.hibernate.dialect.DatabaseVersion; + +/** + * Dialect for CockroachDB database. + * Minimum version: 21.1 + */ +public class HapiFhirCockroachDialect extends CockroachDialect { + + public HapiFhirCockroachDialect() { + super(DatabaseVersion.make(21, 1)); + } + + /** + * @see HapiFhirH2Dialect#supportsColumnCheck() for an explanation of why we disable this + */ + @Override + public boolean supportsColumnCheck() { + return false; + } +} diff --git a/hapi-fhir-jpa/src/main/java/ca/uhn/fhir/jpa/model/dialect/HapiFhirDerbyDialect.java b/hapi-fhir-jpa/src/main/java/ca/uhn/fhir/jpa/model/dialect/HapiFhirDerbyDialect.java new file mode 100644 index 00000000000..b4e8f589f30 --- /dev/null +++ b/hapi-fhir-jpa/src/main/java/ca/uhn/fhir/jpa/model/dialect/HapiFhirDerbyDialect.java @@ -0,0 +1,42 @@ +/*- + * #%L + * HAPI FHIR JPA Model + * %% + * Copyright (C) 2014 - 2023 Smile CDR, Inc. + * %% + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * #L% + */ +package ca.uhn.fhir.jpa.model.dialect; + +import org.hibernate.dialect.DatabaseVersion; +import org.hibernate.dialect.DerbyDialect; + +/** + * Dialect for Oracle database. + * Minimum version: 10.14.2 + */ +public class HapiFhirDerbyDialect extends DerbyDialect { + + public HapiFhirDerbyDialect() { + super(DatabaseVersion.make(10, 14, 2)); + } + + /** + * @see HapiFhirH2Dialect#supportsColumnCheck() for an explanation of why we disable this + */ + @Override + public boolean supportsColumnCheck() { + return false; + } +} diff --git a/hapi-fhir-jpa/src/main/java/ca/uhn/fhir/jpa/model/dialect/HapiFhirH2Dialect.java b/hapi-fhir-jpa/src/main/java/ca/uhn/fhir/jpa/model/dialect/HapiFhirH2Dialect.java new file mode 100644 index 00000000000..12e2f9af6be --- /dev/null +++ b/hapi-fhir-jpa/src/main/java/ca/uhn/fhir/jpa/model/dialect/HapiFhirH2Dialect.java @@ -0,0 +1,64 @@ +/*- + * #%L + * HAPI FHIR JPA Model + * %% + * Copyright (C) 2014 - 2023 Smile CDR, Inc. + * %% + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * #L% + */ +package ca.uhn.fhir.jpa.model.dialect; + +import org.hibernate.dialect.DatabaseVersion; +import org.hibernate.dialect.H2Dialect; + +/** + * Dialect for H2 database. + * Minimum version: 2.2.220 + */ +public class HapiFhirH2Dialect extends H2Dialect { + + /** + * Constructor + */ + public HapiFhirH2Dialect() { + super(DatabaseVersion.make(2, 2, 220)); + } + + /** + * As of Hibernate 6, generated schemas include a column level check constraint that enforces valid values + * for columns that back an Enum type. For example, the column definition for ResourceTable#getFhirVersion() + * would look like: + *
    +	 *  RES_VERSION varchar(7) check (RES_VERSION in ('DSTU2','DSTU2_HL7ORG','DSTU2_1','DSTU3','R4','R4B','R5')),
    +	 * 
    + *

    + * This is a nice addition since it enforces the values that the Enum allows, but it's problematic for us because these + * constraints are invisible to the JDBC metadata API on most databases, which means that our schema migration + * checker isn't able to catch problems if we add a value to an Enum and don't add a corresponding database + * migration. Rather than risk having inconsistent behaviour between annotated and migrated schemas, we just + * disable these checks on all of our dialects. + *

    + * See this discussion from the author of SchemaCrawler discussing this limitation: + * https://stackoverflow.com/questions/63346650/schemacrawler-java-api-retrieve-check-column-constraints. + * With this change in place, the definition above becomes simply: + *

    +	 *  RES_VERSION varchar(7),
    +	 * 
    + *

    + */ + @Override + public boolean supportsColumnCheck() { + return false; + } +} diff --git a/hapi-fhir-jpa/src/main/java/ca/uhn/fhir/jpa/model/dialect/HapiFhirMariaDBDialect.java b/hapi-fhir-jpa/src/main/java/ca/uhn/fhir/jpa/model/dialect/HapiFhirMariaDBDialect.java new file mode 100644 index 00000000000..0a121b8f3f5 --- /dev/null +++ b/hapi-fhir-jpa/src/main/java/ca/uhn/fhir/jpa/model/dialect/HapiFhirMariaDBDialect.java @@ -0,0 +1,42 @@ +/*- + * #%L + * HAPI FHIR JPA Model + * %% + * Copyright (C) 2014 - 2023 Smile CDR, Inc. + * %% + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * #L% + */ +package ca.uhn.fhir.jpa.model.dialect; + +import org.hibernate.dialect.DatabaseVersion; +import org.hibernate.dialect.MariaDBDialect; + +/** + * Dialect for MySQL database. + * Minimum version: 10.11.5 + */ +public class HapiFhirMariaDBDialect extends MariaDBDialect { + + public HapiFhirMariaDBDialect() { + super(DatabaseVersion.make(10, 11, 5)); + } + + /** + * @see HapiFhirH2Dialect#supportsColumnCheck() for an explanation of why we disable this + */ + @Override + public boolean supportsColumnCheck() { + return false; + } +} diff --git a/hapi-fhir-jpa/src/main/java/ca/uhn/fhir/jpa/model/dialect/HapiFhirMySQLDialect.java b/hapi-fhir-jpa/src/main/java/ca/uhn/fhir/jpa/model/dialect/HapiFhirMySQLDialect.java new file mode 100644 index 00000000000..8bdbf26ba4c --- /dev/null +++ b/hapi-fhir-jpa/src/main/java/ca/uhn/fhir/jpa/model/dialect/HapiFhirMySQLDialect.java @@ -0,0 +1,42 @@ +/*- + * #%L + * HAPI FHIR JPA Model + * %% + * Copyright (C) 2014 - 2023 Smile CDR, Inc. + * %% + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * #L% + */ +package ca.uhn.fhir.jpa.model.dialect; + +import org.hibernate.dialect.DatabaseVersion; +import org.hibernate.dialect.MySQLDialect; + +/** + * Dialect for MySQL database. + * Minimum version: 5.7 + */ +public class HapiFhirMySQLDialect extends MySQLDialect { + + public HapiFhirMySQLDialect() { + super(DatabaseVersion.make(5, 7)); + } + + /** + * @see HapiFhirH2Dialect#supportsColumnCheck() for an explanation of why we disable this + */ + @Override + public boolean supportsColumnCheck() { + return false; + } +} diff --git a/hapi-fhir-jpa/src/main/java/ca/uhn/fhir/jpa/model/dialect/HapiFhirOracleDialect.java b/hapi-fhir-jpa/src/main/java/ca/uhn/fhir/jpa/model/dialect/HapiFhirOracleDialect.java new file mode 100644 index 00000000000..ec1095e3765 --- /dev/null +++ b/hapi-fhir-jpa/src/main/java/ca/uhn/fhir/jpa/model/dialect/HapiFhirOracleDialect.java @@ -0,0 +1,42 @@ +/*- + * #%L + * HAPI FHIR JPA Model + * %% + * Copyright (C) 2014 - 2023 Smile CDR, Inc. + * %% + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * #L% + */ +package ca.uhn.fhir.jpa.model.dialect; + +import org.hibernate.dialect.DatabaseVersion; +import org.hibernate.dialect.OracleDialect; + +/** + * Dialect for Oracle database. + * Minimum version: 12.2 (Oracle 12c R2) + */ +public class HapiFhirOracleDialect extends OracleDialect { + + public HapiFhirOracleDialect() { + super(DatabaseVersion.make(12, 2)); + } + + /** + * @see HapiFhirH2Dialect#supportsColumnCheck() for an explanation of why we disable this + */ + @Override + public boolean supportsColumnCheck() { + return false; + } +} diff --git a/hapi-fhir-jpa/src/main/java/ca/uhn/fhir/jpa/model/dialect/HapiFhirPostgres94Dialect.java b/hapi-fhir-jpa/src/main/java/ca/uhn/fhir/jpa/model/dialect/HapiFhirPostgres94Dialect.java new file mode 100644 index 00000000000..da3a48025ad --- /dev/null +++ b/hapi-fhir-jpa/src/main/java/ca/uhn/fhir/jpa/model/dialect/HapiFhirPostgres94Dialect.java @@ -0,0 +1,39 @@ +/*- + * #%L + * HAPI FHIR JPA Model + * %% + * Copyright (C) 2014 - 2023 Smile CDR, Inc. + * %% + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * #L% + */ +package ca.uhn.fhir.jpa.model.dialect; + +import org.hibernate.dialect.PostgreSQLDialect; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * This dialect is recommended when using HAPI FHIR JPA on Postgresql database. + * + * @deprecated Use {@link HapiFhirPostgresDialect} instead + */ +public class HapiFhirPostgres94Dialect extends PostgreSQLDialect { + private static final Logger ourLog = LoggerFactory.getLogger(HapiFhirPostgres94Dialect.class); + + public HapiFhirPostgres94Dialect() { + super(); + ourLog.warn("The " + getClass() + " dialect is deprecated and will be removed in a future release. Use " + + HapiFhirPostgresDialect.class.getName() + " instead"); + } +} diff --git a/hapi-fhir-jpaserver-model/src/main/java/ca/uhn/fhir/jpa/model/dialect/HapiFhirPostgres94Dialect.java b/hapi-fhir-jpa/src/main/java/ca/uhn/fhir/jpa/model/dialect/HapiFhirPostgresDialect.java similarity index 63% rename from hapi-fhir-jpaserver-model/src/main/java/ca/uhn/fhir/jpa/model/dialect/HapiFhirPostgres94Dialect.java rename to hapi-fhir-jpa/src/main/java/ca/uhn/fhir/jpa/model/dialect/HapiFhirPostgresDialect.java index 57f42b5322a..c0a3435b7a1 100644 --- a/hapi-fhir-jpaserver-model/src/main/java/ca/uhn/fhir/jpa/model/dialect/HapiFhirPostgres94Dialect.java +++ b/hapi-fhir-jpa/src/main/java/ca/uhn/fhir/jpa/model/dialect/HapiFhirPostgresDialect.java @@ -19,17 +19,20 @@ */ package ca.uhn.fhir.jpa.model.dialect; -import org.hibernate.dialect.PostgreSQL94Dialect; +import org.hibernate.dialect.DatabaseVersion; +import org.hibernate.dialect.PostgreSQLDialect; -import java.sql.Types; +public class HapiFhirPostgresDialect extends PostgreSQLDialect { -/** - * This dialect is recommended when using HAPI FHIR JPA on Postgresql database. - */ -public class HapiFhirPostgres94Dialect extends PostgreSQL94Dialect { + public HapiFhirPostgresDialect() { + super(DatabaseVersion.make(10, 0, 0)); + } - public HapiFhirPostgres94Dialect() { - super(); - registerColumnType(Types.CLOB, "oid"); + /** + * @see HapiFhirH2Dialect#supportsColumnCheck() for an explanation of why we disable this + */ + @Override + public boolean supportsColumnCheck() { + return false; } } diff --git a/hapi-fhir-jpa/src/main/java/ca/uhn/fhir/jpa/model/dialect/HapiFhirSQLServerDialect.java b/hapi-fhir-jpa/src/main/java/ca/uhn/fhir/jpa/model/dialect/HapiFhirSQLServerDialect.java new file mode 100644 index 00000000000..606d91b4dea --- /dev/null +++ b/hapi-fhir-jpa/src/main/java/ca/uhn/fhir/jpa/model/dialect/HapiFhirSQLServerDialect.java @@ -0,0 +1,42 @@ +/*- + * #%L + * HAPI FHIR JPA Model + * %% + * Copyright (C) 2014 - 2023 Smile CDR, Inc. + * %% + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * #L% + */ +package ca.uhn.fhir.jpa.model.dialect; + +import org.hibernate.dialect.DatabaseVersion; +import org.hibernate.dialect.SQLServerDialect; + +/** + * Dialect for MS SQL Server database. + * Minimum version: 12.0 (SQL Server 2014 and Azure SQL Database) + */ +public class HapiFhirSQLServerDialect extends SQLServerDialect { + + public HapiFhirSQLServerDialect() { + super(DatabaseVersion.make(11)); + } + + /** + * @see HapiFhirH2Dialect#supportsColumnCheck() for an explanation of why we disable this + */ + @Override + public boolean supportsColumnCheck() { + return false; + } +} diff --git a/hapi-fhir-jpa/src/main/java/ca/uhn/fhir/jpa/nickname/NicknameMap.java b/hapi-fhir-jpa/src/main/java/ca/uhn/fhir/jpa/nickname/NicknameMap.java index 5399355c6f6..7920c60b997 100644 --- a/hapi-fhir-jpa/src/main/java/ca/uhn/fhir/jpa/nickname/NicknameMap.java +++ b/hapi-fhir-jpa/src/main/java/ca/uhn/fhir/jpa/nickname/NicknameMap.java @@ -19,6 +19,8 @@ */ package ca.uhn.fhir.jpa.nickname; +import jakarta.annotation.Nonnull; + import java.io.BufferedReader; import java.io.IOException; import java.io.Reader; @@ -27,7 +29,6 @@ import java.util.Arrays; import java.util.HashMap; import java.util.List; import java.util.Map; -import javax.annotation.Nonnull; class NicknameMap { private final Map> myFormalToNick = new HashMap<>(); diff --git a/hapi-fhir-jpa/src/main/java/ca/uhn/fhir/jpa/nickname/NicknameSvc.java b/hapi-fhir-jpa/src/main/java/ca/uhn/fhir/jpa/nickname/NicknameSvc.java index cc4c2d37df9..816d84685d4 100644 --- a/hapi-fhir-jpa/src/main/java/ca/uhn/fhir/jpa/nickname/NicknameSvc.java +++ b/hapi-fhir-jpa/src/main/java/ca/uhn/fhir/jpa/nickname/NicknameSvc.java @@ -21,6 +21,7 @@ package ca.uhn.fhir.jpa.nickname; import ca.uhn.fhir.context.ConfigurationException; import ca.uhn.fhir.i18n.Msg; +import jakarta.annotation.Nonnull; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.core.io.ClassPathResource; @@ -34,7 +35,6 @@ import java.util.Collection; import java.util.HashSet; import java.util.List; import java.util.Set; -import javax.annotation.Nonnull; /** * Nickname service is used to load nicknames diff --git a/hapi-fhir-jpa/src/main/java/ca/uhn/fhir/jpa/sched/BaseHapiScheduler.java b/hapi-fhir-jpa/src/main/java/ca/uhn/fhir/jpa/sched/BaseHapiScheduler.java index 6a514116e9c..33bb4be6992 100644 --- a/hapi-fhir-jpa/src/main/java/ca/uhn/fhir/jpa/sched/BaseHapiScheduler.java +++ b/hapi-fhir-jpa/src/main/java/ca/uhn/fhir/jpa/sched/BaseHapiScheduler.java @@ -26,6 +26,7 @@ import ca.uhn.fhir.jpa.model.sched.ScheduledJobDefinition; import ca.uhn.fhir.rest.server.exceptions.InternalErrorException; import com.google.common.annotations.VisibleForTesting; import com.google.common.collect.Sets; +import jakarta.annotation.Nonnull; import org.apache.commons.lang3.Validate; import org.quartz.JobDataMap; import org.quartz.JobKey; @@ -47,7 +48,6 @@ import java.util.Properties; import java.util.Set; import java.util.concurrent.atomic.AtomicInteger; import java.util.stream.Collectors; -import javax.annotation.Nonnull; public abstract class BaseHapiScheduler implements IHapiScheduler { private static final Logger ourLog = LoggerFactory.getLogger(BaseHapiScheduler.class); diff --git a/hapi-fhir-jpa/src/main/java/ca/uhn/fhir/jpa/sched/BaseSchedulerServiceImpl.java b/hapi-fhir-jpa/src/main/java/ca/uhn/fhir/jpa/sched/BaseSchedulerServiceImpl.java index 58672a138a2..2bd359b21dd 100644 --- a/hapi-fhir-jpa/src/main/java/ca/uhn/fhir/jpa/sched/BaseSchedulerServiceImpl.java +++ b/hapi-fhir-jpa/src/main/java/ca/uhn/fhir/jpa/sched/BaseSchedulerServiceImpl.java @@ -27,6 +27,7 @@ import ca.uhn.fhir.jpa.model.sched.ISchedulerService; import ca.uhn.fhir.jpa.model.sched.ScheduledJobDefinition; import ca.uhn.fhir.util.StopWatch; import com.google.common.annotations.VisibleForTesting; +import jakarta.annotation.PostConstruct; import org.quartz.JobKey; import org.quartz.SchedulerException; import org.slf4j.Logger; @@ -41,7 +42,6 @@ import org.springframework.core.env.Environment; import java.util.Collection; import java.util.Set; import java.util.concurrent.atomic.AtomicBoolean; -import javax.annotation.PostConstruct; /** * This class provides task scheduling for the entire module using the Quartz library. diff --git a/hapi-fhir-jpa/src/main/java/ca/uhn/fhir/jpa/util/DerbyTenSevenHapiFhirDialect.java b/hapi-fhir-jpa/src/main/java/ca/uhn/fhir/jpa/util/DerbyTenSevenHapiFhirDialect.java index b83b99a13d9..7216e9fef42 100644 --- a/hapi-fhir-jpa/src/main/java/ca/uhn/fhir/jpa/util/DerbyTenSevenHapiFhirDialect.java +++ b/hapi-fhir-jpa/src/main/java/ca/uhn/fhir/jpa/util/DerbyTenSevenHapiFhirDialect.java @@ -19,33 +19,32 @@ */ package ca.uhn.fhir.jpa.util; -import org.hibernate.dialect.DerbyTenSevenDialect; -import org.hibernate.exception.spi.TemplatedViolatedConstraintNameExtracter; -import org.hibernate.exception.spi.ViolatedConstraintNameExtracter; +import org.hibernate.dialect.DerbyDialect; +import org.hibernate.exception.spi.TemplatedViolatedConstraintNameExtractor; +import org.hibernate.exception.spi.ViolatedConstraintNameExtractor; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import java.sql.SQLException; +import java.util.function.Function; -public class DerbyTenSevenHapiFhirDialect extends DerbyTenSevenDialect { +import static org.hibernate.exception.spi.TemplatedViolatedConstraintNameExtractor.extractUsingTemplate; + +public class DerbyTenSevenHapiFhirDialect extends DerbyDialect { private static final Logger ourLog = LoggerFactory.getLogger(DerbyTenSevenHapiFhirDialect.class); @Override - public ViolatedConstraintNameExtracter getViolatedConstraintNameExtracter() { - return new TemplatedViolatedConstraintNameExtracter() { - @Override - protected String doExtractConstraintName(SQLException theSqlException) throws NumberFormatException { - switch (theSqlException.getSQLState()) { - case "23505": - return this.extractUsingTemplate( - "unique or primary key constraint or unique index identified by '", - "'", - theSqlException.getMessage()); - default: - return null; - } + public ViolatedConstraintNameExtractor getViolatedConstraintNameExtractor() { + Function extractor = e -> { + switch (e.getSQLState()) { + case "23505": + return extractUsingTemplate( + "unique or primary key constraint or unique index identified by '", "'", e.getMessage()); + default: + return null; } }; + return new TemplatedViolatedConstraintNameExtractor(extractor); } } diff --git a/hapi-fhir-jpaserver-model/src/main/java/ca/uhn/fhir/jpa/model/dialect/ISequenceValueMassager.java b/hapi-fhir-jpa/src/main/java/ca/uhn/fhir/jpa/util/ISequenceValueMassager.java similarity index 84% rename from hapi-fhir-jpaserver-model/src/main/java/ca/uhn/fhir/jpa/model/dialect/ISequenceValueMassager.java rename to hapi-fhir-jpa/src/main/java/ca/uhn/fhir/jpa/util/ISequenceValueMassager.java index df4c3a7ba52..26b38682fba 100644 --- a/hapi-fhir-jpaserver-model/src/main/java/ca/uhn/fhir/jpa/model/dialect/ISequenceValueMassager.java +++ b/hapi-fhir-jpa/src/main/java/ca/uhn/fhir/jpa/util/ISequenceValueMassager.java @@ -1,6 +1,6 @@ /*- * #%L - * HAPI FHIR JPA Model + * hapi-fhir-jpa * %% * Copyright (C) 2014 - 2023 Smile CDR, Inc. * %% @@ -17,16 +17,17 @@ * limitations under the License. * #L% */ -package ca.uhn.fhir.jpa.model.dialect; +package ca.uhn.fhir.jpa.util; -import javax.annotation.Nullable; +import jakarta.annotation.Nullable; +import org.hibernate.service.Service; /** * This is an internal API and may change or disappear without notice * - * Implementations of this interface can modify the automatically generated sequence values created by hibernate seuqnece generator + * Implementations of this interface can modify the automatically generated sequence values created by hibernate sequence generator */ -public interface ISequenceValueMassager { +public interface ISequenceValueMassager extends Service { Long massage(String theGeneratorName, Long theId); diff --git a/hapi-fhir-jpaserver-base/pom.xml b/hapi-fhir-jpaserver-base/pom.xml index f0789864dd9..f8b5642bab5 100644 --- a/hapi-fhir-jpaserver-base/pom.xml +++ b/hapi-fhir-jpaserver-base/pom.xml @@ -5,7 +5,7 @@ ca.uhn.hapi.fhir hapi-deployable-pom - 6.9.7-SNAPSHOT + 6.11.7-SNAPSHOT ../hapi-deployable-pom/pom.xml @@ -208,7 +208,7 @@ org.springdoc - springdoc-openapi-ui + springdoc-openapi-starter-webmvc-ui @@ -217,7 +217,7 @@ org.thymeleaf - thymeleaf-spring5 + thymeleaf-spring6 @@ -243,23 +243,12 @@ xml-patch - - - - - javax.interceptor - javax.interceptor-api - provided - - - javax.servlet - javax.servlet-api + jakarta.servlet + jakarta.servlet-api provided @@ -308,11 +297,6 @@ org.springframework spring-websocket - - javax.el - javax.el-api - provided - org.glassfish jakarta.el @@ -325,12 +309,12 @@ - org.elasticsearch.client - elasticsearch-rest-high-level-client + co.elastic.clients + elasticsearch-java - org.apache.logging.log4j - log4j-api + jakarta.json + jakarta.json-api @@ -347,7 +331,7 @@ hibernate-search-backend-elasticsearch-aws - org.hibernate + org.hibernate.orm hibernate-envers @@ -435,98 +419,7 @@ - - de.jpdigital - hibernate56-ddl-maven-plugin - - - derby_10_7 - mysql57 - mariadb - oracle12c - sqlserver2012 - - - ca.uhn.fhir.jpa.model.dialect.HapiFhirH2Dialect - ca.uhn.fhir.jpa.model.dialect.HapiFhirPostgres94Dialect - org.hibernate.dialect.CockroachDB201Dialect - - ${project.build.directory}/classes/ca/uhn/hapi/fhir/jpa/docs/database - - ca.uhn.fhir.jpa.entity - ca.uhn.fhir.jpa.model.entity - - - - - process-classes - - gen-ddl - - - - - - - org.glassfish.jaxb - jaxb-runtime - ${jaxb_runtime_version} - - - ca.uhn.hapi.fhir - hapi-fhir-jpaserver-model - ${project.version} - - - org.hibernate - hibernate-core - ${hibernate_version} - - - org.hibernate - hibernate-envers - - ${hibernate_version} - - - javax.xml.bind - jaxb-api - ${jaxb_api_version} - - - com.sun.xml.bind - jaxb-impl - ${jaxb_api_version} - - - - - org.apache.maven.plugins - maven-antrun-plugin - - - process-classes - - run - - - - - - - - - - - - - - - - + ca.uhn.hapi.fhir hapi-tinder-plugin @@ -637,6 +530,68 @@ + + + ca.uhn.hapi.fhir + hapi-tinder-plugin + ${project.version} + + + + generate-ddl + + + + + + ca.uhn.fhir.jpa.entity + ca.uhn.fhir.jpa.model.entity + + + + ca.uhn.fhir.jpa.model.dialect.HapiFhirDerbyDialect + derby.sql + + + ca.uhn.fhir.jpa.model.dialect.HapiFhirMySQLDialect + mysql.sql + + + ca.uhn.fhir.jpa.model.dialect.HapiFhirMariaDBDialect + mariadb.sql + + + ca.uhn.fhir.jpa.model.dialect.HapiFhirOracleDialect + oracle.sql + + + ca.uhn.fhir.jpa.model.dialect.HapiFhirSQLServerDialect + sqlserver.sql + + + ca.uhn.fhir.jpa.model.dialect.HapiFhirCockroachDialect + cockroachdb.sql + + + ca.uhn.fhir.jpa.model.dialect.HapiFhirH2Dialect + h2.sql + + + ca.uhn.fhir.jpa.model.dialect.HapiFhirPostgresDialect + postgres.sql + classpath:ca/uhn/fhir/jpa/docs/database/hapifhirpostgres94-init01.sql + + + ${project.build.directory}/classes/ca/uhn/hapi/fhir/jpa/docs/database + + + + ca.uhn.hapi.fhir + hapi-fhir-jpaserver-model + ${project.version} + + + org.codehaus.mojo build-helper-maven-plugin diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/batch2/JobInstanceUtil.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/batch2/JobInstanceUtil.java index 156eb22028d..2cf68eab8ed 100644 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/batch2/JobInstanceUtil.java +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/batch2/JobInstanceUtil.java @@ -23,8 +23,7 @@ import ca.uhn.fhir.batch2.model.JobInstance; import ca.uhn.fhir.batch2.model.WorkChunk; import ca.uhn.fhir.jpa.entity.Batch2JobInstanceEntity; import ca.uhn.fhir.jpa.entity.Batch2WorkChunkEntity; - -import javax.annotation.Nonnull; +import jakarta.annotation.Nonnull; class JobInstanceUtil { @@ -61,6 +60,8 @@ class JobInstanceUtil { retVal.setReport(theEntity.getReport()); retVal.setEstimatedTimeRemaining(theEntity.getEstimatedTimeRemaining()); retVal.setWarningMessages(theEntity.getWarningMessages()); + retVal.setTriggeringUsername(theEntity.getTriggeringUsername()); + retVal.setTriggeringClientId(theEntity.getTriggeringClientId()); return retVal; } @@ -95,6 +96,8 @@ class JobInstanceUtil { theJobInstanceEntity.setReport(theJobInstance.getReport()); theJobInstanceEntity.setEstimatedTimeRemaining(theJobInstance.getEstimatedTimeRemaining()); theJobInstanceEntity.setWarningMessages(theJobInstance.getWarningMessages()); + theJobInstanceEntity.setTriggeringUsername(theJobInstance.getTriggeringUsername()); + theJobInstanceEntity.setTriggeringClientId(theJobInstance.getTriggeringClientId()); } /** diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/batch2/JpaBatch2Config.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/batch2/JpaBatch2Config.java index c4803afcf86..b0cb5810b84 100644 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/batch2/JpaBatch2Config.java +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/batch2/JpaBatch2Config.java @@ -21,16 +21,15 @@ package ca.uhn.fhir.jpa.batch2; import ca.uhn.fhir.batch2.api.IJobPersistence; import ca.uhn.fhir.batch2.config.BaseBatch2Config; +import ca.uhn.fhir.interceptor.api.IInterceptorBroadcaster; import ca.uhn.fhir.jpa.bulk.export.job.BulkExportJobConfig; import ca.uhn.fhir.jpa.dao.data.IBatch2JobInstanceRepository; import ca.uhn.fhir.jpa.dao.data.IBatch2WorkChunkRepository; import ca.uhn.fhir.jpa.dao.tx.IHapiTransactionService; +import jakarta.persistence.EntityManager; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.Import; -import org.springframework.context.annotation.Primary; - -import javax.persistence.EntityManager; @Configuration @Import({BulkExportJobConfig.class}) @@ -41,28 +40,13 @@ public class JpaBatch2Config extends BaseBatch2Config { IBatch2JobInstanceRepository theJobInstanceRepository, IBatch2WorkChunkRepository theWorkChunkRepository, IHapiTransactionService theTransactionService, - EntityManager theEntityManager) { + EntityManager theEntityManager, + IInterceptorBroadcaster theInterceptorBroadcaster) { return new JpaJobPersistenceImpl( - theJobInstanceRepository, theWorkChunkRepository, theTransactionService, theEntityManager); - } - - @Primary - @Bean - public IJobPersistence batch2JobInstancePersisterWrapper( - IBatch2JobInstanceRepository theJobInstanceRepository, - IBatch2WorkChunkRepository theWorkChunkRepository, - IHapiTransactionService theTransactionService, - EntityManager theEntityManager) { - IJobPersistence retVal = batch2JobInstancePersister( - theJobInstanceRepository, theWorkChunkRepository, theTransactionService, theEntityManager); - // Avoid H2 synchronization issues caused by - // https://github.com/h2database/h2database/issues/1808 - // TODO: Update 2023-03-14 - The bug above appears to be fixed. I'm going to try - // disabing this and see if we can get away without it. If so, we can delete - // this entirely - // if (HapiSystemProperties.isUnitTestModeEnabled()) { - // retVal = ProxyUtil.synchronizedProxy(IJobPersistence.class, retVal); - // } - return retVal; + theJobInstanceRepository, + theWorkChunkRepository, + theTransactionService, + theEntityManager, + theInterceptorBroadcaster); } } diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/batch2/JpaJobPersistenceImpl.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/batch2/JpaJobPersistenceImpl.java index 01fe091dbbe..d9d411de2c3 100644 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/batch2/JpaJobPersistenceImpl.java +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/batch2/JpaJobPersistenceImpl.java @@ -30,18 +30,28 @@ import ca.uhn.fhir.batch2.model.WorkChunkCreateEvent; import ca.uhn.fhir.batch2.model.WorkChunkErrorEvent; import ca.uhn.fhir.batch2.model.WorkChunkStatusEnum; import ca.uhn.fhir.batch2.models.JobInstanceFetchRequest; +import ca.uhn.fhir.interceptor.api.HookParams; +import ca.uhn.fhir.interceptor.api.IInterceptorBroadcaster; +import ca.uhn.fhir.interceptor.api.Pointcut; import ca.uhn.fhir.jpa.dao.data.IBatch2JobInstanceRepository; import ca.uhn.fhir.jpa.dao.data.IBatch2WorkChunkRepository; import ca.uhn.fhir.jpa.dao.tx.IHapiTransactionService; import ca.uhn.fhir.jpa.entity.Batch2JobInstanceEntity; import ca.uhn.fhir.jpa.entity.Batch2WorkChunkEntity; import ca.uhn.fhir.model.api.PagingIterator; +import ca.uhn.fhir.rest.api.server.RequestDetails; +import ca.uhn.fhir.rest.api.server.SystemRequestDetails; import ca.uhn.fhir.util.Batch2JobDefinitionConstants; import ca.uhn.fhir.util.Logs; import com.fasterxml.jackson.core.JsonParser; import com.fasterxml.jackson.databind.JsonNode; import com.fasterxml.jackson.databind.ObjectMapper; import com.fasterxml.jackson.databind.node.ObjectNode; +import jakarta.annotation.Nonnull; +import jakarta.annotation.Nullable; +import jakarta.persistence.EntityManager; +import jakarta.persistence.LockModeType; +import jakarta.persistence.Query; import org.apache.commons.collections4.ListUtils; import org.apache.commons.lang3.Validate; import org.slf4j.Logger; @@ -64,11 +74,6 @@ import java.util.UUID; import java.util.function.Consumer; import java.util.stream.Collectors; import java.util.stream.Stream; -import javax.annotation.Nonnull; -import javax.annotation.Nullable; -import javax.persistence.EntityManager; -import javax.persistence.LockModeType; -import javax.persistence.Query; import static ca.uhn.fhir.batch2.coordinator.WorkChunkProcessor.MAX_CHUNK_ERROR_COUNT; import static ca.uhn.fhir.jpa.entity.Batch2WorkChunkEntity.ERROR_MSG_MAX_LENGTH; @@ -82,6 +87,7 @@ public class JpaJobPersistenceImpl implements IJobPersistence { private final IBatch2WorkChunkRepository myWorkChunkRepository; private final EntityManager myEntityManager; private final IHapiTransactionService myTransactionService; + private final IInterceptorBroadcaster myInterceptorBroadcaster; /** * Constructor @@ -90,16 +96,19 @@ public class JpaJobPersistenceImpl implements IJobPersistence { IBatch2JobInstanceRepository theJobInstanceRepository, IBatch2WorkChunkRepository theWorkChunkRepository, IHapiTransactionService theTransactionService, - EntityManager theEntityManager) { + EntityManager theEntityManager, + IInterceptorBroadcaster theInterceptorBroadcaster) { Validate.notNull(theJobInstanceRepository); Validate.notNull(theWorkChunkRepository); myJobInstanceRepository = theJobInstanceRepository; myWorkChunkRepository = theWorkChunkRepository; myTransactionService = theTransactionService; myEntityManager = theEntityManager; + myInterceptorBroadcaster = theInterceptorBroadcaster; } @Override + @Transactional(propagation = Propagation.REQUIRED) public String onWorkChunkCreate(WorkChunkCreateEvent theBatchWorkChunk) { Batch2WorkChunkEntity entity = new Batch2WorkChunkEntity(); entity.setId(UUID.randomUUID().toString()); @@ -115,7 +124,7 @@ public class JpaJobPersistenceImpl implements IJobPersistence { ourLog.debug("Create work chunk {}/{}/{}", entity.getInstanceId(), entity.getId(), entity.getTargetStepId()); ourLog.trace( "Create work chunk data {}/{}: {}", entity.getInstanceId(), entity.getId(), entity.getSerializedData()); - myWorkChunkRepository.save(entity); + myTransactionService.withSystemRequestOnDefaultPartition().execute(() -> myWorkChunkRepository.save(entity)); return entity.getId(); } @@ -142,6 +151,8 @@ public class JpaJobPersistenceImpl implements IJobPersistence { public String storeNewInstance(JobInstance theInstance) { Validate.isTrue(isBlank(theInstance.getInstanceId())); + invokePreStorageBatchHooks(theInstance); + Batch2JobInstanceEntity entity = new Batch2JobInstanceEntity(); entity.setId(UUID.randomUUID().toString()); entity.setDefinitionId(theInstance.getJobDefinitionId()); @@ -153,6 +164,8 @@ public class JpaJobPersistenceImpl implements IJobPersistence { entity.setCreateTime(new Date()); entity.setStartTime(new Date()); entity.setReport(theInstance.getReport()); + entity.setTriggeringUsername(theInstance.getTriggeringUsername()); + entity.setTriggeringClientId(theInstance.getTriggeringClientId()); entity = myJobInstanceRepository.save(entity); return entity.getId(); @@ -209,7 +222,7 @@ public class JpaJobPersistenceImpl implements IJobPersistence { @Nonnull public Optional fetchInstance(String theInstanceId) { return myTransactionService - .withSystemRequest() + .withSystemRequestOnDefaultPartition() .execute(() -> myJobInstanceRepository.findById(theInstanceId).map(this::toInstance)); } @@ -225,7 +238,7 @@ public class JpaJobPersistenceImpl implements IJobPersistence { List instanceEntities; if (statuses != null && !statuses.isEmpty()) { - if (definitionId.equals(Batch2JobDefinitionConstants.BULK_EXPORT)) { + if (Batch2JobDefinitionConstants.BULK_EXPORT.equals(definitionId)) { if (originalRequestUrlTruncation(params) != null) { params = originalRequestUrlTruncation(params); } @@ -268,18 +281,22 @@ public class JpaJobPersistenceImpl implements IJobPersistence { public List fetchInstances(int thePageSize, int thePageIndex) { // default sort is myCreateTime Asc PageRequest pageRequest = PageRequest.of(thePageIndex, thePageSize, Sort.Direction.ASC, CREATE_TIME); - return myJobInstanceRepository.findAll(pageRequest).stream() - .map(this::toInstance) - .collect(Collectors.toList()); + return myTransactionService + .withSystemRequestOnDefaultPartition() + .execute(() -> myJobInstanceRepository.findAll(pageRequest).stream() + .map(this::toInstance) + .collect(Collectors.toList())); } @Override @Transactional(propagation = Propagation.REQUIRES_NEW) public List fetchRecentInstances(int thePageSize, int thePageIndex) { PageRequest pageRequest = PageRequest.of(thePageIndex, thePageSize, Sort.Direction.DESC, CREATE_TIME); - return myJobInstanceRepository.findAll(pageRequest).stream() - .map(this::toInstance) - .collect(Collectors.toList()); + return myTransactionService + .withSystemRequestOnDefaultPartition() + .execute(() -> myJobInstanceRepository.findAll(pageRequest).stream() + .map(this::toInstance) + .collect(Collectors.toList())); } private WorkChunk toChunk(Batch2WorkChunkEntity theEntity) { @@ -291,48 +308,52 @@ public class JpaJobPersistenceImpl implements IJobPersistence { } @Override - @Transactional(propagation = Propagation.REQUIRES_NEW) public WorkChunkStatusEnum onWorkChunkError(WorkChunkErrorEvent theParameters) { String chunkId = theParameters.getChunkId(); String errorMessage = truncateErrorMessage(theParameters.getErrorMsg()); - int changeCount = myWorkChunkRepository.updateChunkStatusAndIncrementErrorCountForEndError( - chunkId, new Date(), errorMessage, WorkChunkStatusEnum.ERRORED); - Validate.isTrue(changeCount > 0, "changed chunk matching %s", chunkId); - Query query = myEntityManager.createQuery("update Batch2WorkChunkEntity " + "set myStatus = :failed " - + ",myErrorMessage = CONCAT('Too many errors: ', CAST(myErrorCount as string), '. Last error msg was ', myErrorMessage) " - + "where myId = :chunkId and myErrorCount > :maxCount"); - query.setParameter("chunkId", chunkId); - query.setParameter("failed", WorkChunkStatusEnum.FAILED); - query.setParameter("maxCount", MAX_CHUNK_ERROR_COUNT); - int failChangeCount = query.executeUpdate(); + return myTransactionService.withSystemRequestOnDefaultPartition().execute(() -> { + int changeCount = myWorkChunkRepository.updateChunkStatusAndIncrementErrorCountForEndError( + chunkId, new Date(), errorMessage, WorkChunkStatusEnum.ERRORED); + Validate.isTrue(changeCount > 0, "changed chunk matching %s", chunkId); - if (failChangeCount > 0) { - return WorkChunkStatusEnum.FAILED; - } else { - return WorkChunkStatusEnum.ERRORED; - } + Query query = myEntityManager.createQuery("update Batch2WorkChunkEntity " + "set myStatus = :failed " + + ",myErrorMessage = CONCAT('Too many errors: ', CAST(myErrorCount as string), '. Last error msg was ', myErrorMessage) " + + "where myId = :chunkId and myErrorCount > :maxCount"); + query.setParameter("chunkId", chunkId); + query.setParameter("failed", WorkChunkStatusEnum.FAILED); + query.setParameter("maxCount", MAX_CHUNK_ERROR_COUNT); + int failChangeCount = query.executeUpdate(); + + if (failChangeCount > 0) { + return WorkChunkStatusEnum.FAILED; + } else { + return WorkChunkStatusEnum.ERRORED; + } + }); } @Override - @Transactional public void onWorkChunkFailed(String theChunkId, String theErrorMessage) { ourLog.info("Marking chunk {} as failed with message: {}", theChunkId, theErrorMessage); String errorMessage = truncateErrorMessage(theErrorMessage); - myWorkChunkRepository.updateChunkStatusAndIncrementErrorCountForEndError( - theChunkId, new Date(), errorMessage, WorkChunkStatusEnum.FAILED); + myTransactionService + .withSystemRequestOnDefaultPartition() + .execute(() -> myWorkChunkRepository.updateChunkStatusAndIncrementErrorCountForEndError( + theChunkId, new Date(), errorMessage, WorkChunkStatusEnum.FAILED)); } @Override - @Transactional public void onWorkChunkCompletion(WorkChunkCompletionEvent theEvent) { - myWorkChunkRepository.updateChunkStatusAndClearDataForEndSuccess( - theEvent.getChunkId(), - new Date(), - theEvent.getRecordsProcessed(), - theEvent.getRecoveredErrorCount(), - WorkChunkStatusEnum.COMPLETED, - theEvent.getRecoveredWarningMessage()); + myTransactionService + .withSystemRequestOnDefaultPartition() + .execute(() -> myWorkChunkRepository.updateChunkStatusAndClearDataForEndSuccess( + theEvent.getChunkId(), + new Date(), + theEvent.getRecordsProcessed(), + theEvent.getRecoveredErrorCount(), + WorkChunkStatusEnum.COMPLETED, + theEvent.getRecoveredWarningMessage())); } @Nullable @@ -389,7 +410,7 @@ public class JpaJobPersistenceImpl implements IJobPersistence { int thePageIndex, Consumer theConsumer) { myTransactionService - .withSystemRequest() + .withSystemRequestOnDefaultPartition() .withPropagation(Propagation.REQUIRES_NEW) .execute(() -> { List chunks; @@ -511,4 +532,14 @@ public class JpaJobPersistenceImpl implements IJobPersistence { } } } + + private void invokePreStorageBatchHooks(JobInstance theJobInstance) { + if (myInterceptorBroadcaster.hasHooks(Pointcut.STORAGE_PRESTORAGE_BATCH_JOB_CREATE)) { + HookParams params = new HookParams() + .add(JobInstance.class, theJobInstance) + .add(RequestDetails.class, new SystemRequestDetails()); + + myInterceptorBroadcaster.callHooks(Pointcut.STORAGE_PRESTORAGE_BATCH_JOB_CREATE, params); + } + } } diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/binstore/DatabaseBlobBinaryStorageSvcImpl.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/binstore/DatabaseBlobBinaryStorageSvcImpl.java index 58936143305..da13a94c46d 100644 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/binstore/DatabaseBlobBinaryStorageSvcImpl.java +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/binstore/DatabaseBlobBinaryStorageSvcImpl.java @@ -28,6 +28,10 @@ import ca.uhn.fhir.rest.api.server.RequestDetails; import ca.uhn.fhir.rest.server.exceptions.ResourceNotFoundException; import com.google.common.hash.HashingInputStream; import com.google.common.io.ByteStreams; +import jakarta.annotation.Nonnull; +import jakarta.persistence.EntityManager; +import jakarta.persistence.PersistenceContext; +import jakarta.persistence.PersistenceContextType; import org.apache.commons.io.IOUtils; import org.apache.commons.io.input.CountingInputStream; import org.hibernate.LobHelper; @@ -44,10 +48,6 @@ import java.sql.Blob; import java.sql.SQLException; import java.util.Date; import java.util.Optional; -import javax.annotation.Nonnull; -import javax.persistence.EntityManager; -import javax.persistence.PersistenceContext; -import javax.persistence.PersistenceContextType; @Transactional public class DatabaseBlobBinaryStorageSvcImpl extends BaseBinaryStorageSvcImpl { diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/bulk/export/svc/BulkDataExportJobSchedulingHelperImpl.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/bulk/export/svc/BulkDataExportJobSchedulingHelperImpl.java index 5d352926733..db0e0d22709 100644 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/bulk/export/svc/BulkDataExportJobSchedulingHelperImpl.java +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/bulk/export/svc/BulkDataExportJobSchedulingHelperImpl.java @@ -34,6 +34,8 @@ import ca.uhn.fhir.jpa.model.sched.ScheduledJobDefinition; import ca.uhn.fhir.rest.api.server.SystemRequestDetails; import ca.uhn.fhir.util.Batch2JobDefinitionConstants; import ca.uhn.fhir.util.JsonUtil; +import jakarta.annotation.Nonnull; +import jakarta.annotation.PostConstruct; import org.apache.commons.lang3.StringUtils; import org.apache.commons.lang3.time.DateUtils; import org.hl7.fhir.instance.model.api.IBaseBinary; @@ -54,8 +56,6 @@ import java.util.Date; import java.util.List; import java.util.Map; import java.util.Optional; -import javax.annotation.Nonnull; -import javax.annotation.PostConstruct; import static org.slf4j.LoggerFactory.getLogger; diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/bulk/export/svc/JpaBulkExportProcessor.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/bulk/export/svc/JpaBulkExportProcessor.java index ae0fb1115f1..eab1633b605 100644 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/bulk/export/svc/JpaBulkExportProcessor.java +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/bulk/export/svc/JpaBulkExportProcessor.java @@ -53,10 +53,13 @@ import ca.uhn.fhir.rest.param.HasOrListParam; import ca.uhn.fhir.rest.param.HasParam; import ca.uhn.fhir.rest.param.ReferenceOrListParam; import ca.uhn.fhir.rest.param.ReferenceParam; +import ca.uhn.fhir.rest.server.util.ISearchParamRegistry; import ca.uhn.fhir.util.ExtensionUtil; import ca.uhn.fhir.util.HapiExtensions; import ca.uhn.fhir.util.Logs; import ca.uhn.fhir.util.SearchParameterUtil; +import jakarta.annotation.Nonnull; +import jakarta.persistence.EntityManager; import org.apache.commons.lang3.StringUtils; import org.hl7.fhir.instance.model.api.IBaseExtension; import org.hl7.fhir.instance.model.api.IBaseReference; @@ -77,8 +80,6 @@ import java.util.Map; import java.util.Optional; import java.util.Set; import java.util.stream.Collectors; -import javax.annotation.Nonnull; -import javax.persistence.EntityManager; import static ca.uhn.fhir.rest.api.Constants.PARAM_HAS; import static ca.uhn.fhir.rest.api.Constants.PARAM_ID; @@ -121,6 +122,9 @@ public class JpaBulkExportProcessor implements IBulkExportProcessor { @Autowired private IHapiTransactionService myHapiTransactionService; + @Autowired + private ISearchParamRegistry mySearchParamRegistry; + private IFhirPath myFhirPath; @Override @@ -298,28 +302,36 @@ public class JpaBulkExportProcessor implements IBulkExportProcessor { private LinkedHashSet getRelatedResourceTypePids( ExportPIDIteratorParameters theParams, RuntimeResourceDefinition theDef) throws IOException { LinkedHashSet pids = new LinkedHashSet<>(); - // expand the group pid -> list of patients in that group (list of patient pids) - Set expandedMemberResourceIds = expandAllPatientPidsFromGroup(theParams); - assert !expandedMemberResourceIds.isEmpty(); - Logs.getBatchTroubleshootingLog() - .debug("{} has been expanded to members:[{}]", theParams.getGroupId(), expandedMemberResourceIds); + // Check if the patient compartment search parameter is active to enable export of this resource + RuntimeSearchParam activeSearchParam = + getActivePatientSearchParamForCurrentResourceType(theParams.getResourceType()); + if (activeSearchParam != null) { + // expand the group pid -> list of patients in that group (list of patient pids) + Set expandedMemberResourceIds = expandAllPatientPidsFromGroup(theParams); + assert !expandedMemberResourceIds.isEmpty(); + Logs.getBatchTroubleshootingLog() + .debug("{} has been expanded to members:[{}]", theParams.getGroupId(), expandedMemberResourceIds); - // for each patient pid -> - // search for the target resources, with their correct patient references, chunked. - // The results will be jammed into myReadPids - QueryChunker queryChunker = new QueryChunker<>(); - queryChunker.chunk(expandedMemberResourceIds, QUERY_CHUNK_SIZE, (idChunk) -> { - try { - queryResourceTypeWithReferencesToPatients(pids, idChunk, theParams, theDef); - } catch (IOException ex) { - // we will never see this; - // SearchBuilder#QueryIterator does not (nor can ever) throw - // an IOException... but Java requires the check, - // so we'll put a log here (just in the off chance) - ourLog.error("Couldn't close query iterator ", ex); - throw new RuntimeException(Msg.code(2346) + "Couldn't close query iterator", ex); - } - }); + // for each patient pid -> + // search for the target resources, with their correct patient references, chunked. + // The results will be jammed into myReadPids + QueryChunker queryChunker = new QueryChunker<>(); + queryChunker.chunk(expandedMemberResourceIds, QUERY_CHUNK_SIZE, (idChunk) -> { + try { + queryResourceTypeWithReferencesToPatients(pids, idChunk, theParams, theDef); + } catch (IOException ex) { + // we will never see this; + // SearchBuilder#QueryIterator does not (nor can ever) throw + // an IOException... but Java requires the check, + // so we'll put a log here (just in the off chance) + ourLog.error("Couldn't close query iterator ", ex); + throw new RuntimeException(Msg.code(2346) + "Couldn't close query iterator", ex); + } + }); + } else { + ourLog.warn("No active patient compartment search parameter(s) for resource type " + + theParams.getResourceType()); + } return pids; } @@ -600,6 +612,22 @@ public class JpaBulkExportProcessor implements IBulkExportProcessor { } } + private RuntimeSearchParam getActivePatientSearchParamForCurrentResourceType(String theResourceType) { + String activeSearchParamName = ""; + String resourceToCheck = theResourceType; + if (!PATIENT_BULK_EXPORT_FORWARD_REFERENCE_RESOURCE_TYPES.contains(theResourceType)) { + activeSearchParamName = + getPatientSearchParamForCurrentResourceType(theResourceType).getName(); + } else if ("Practitioner".equalsIgnoreCase(theResourceType)) { + resourceToCheck = "Patient"; + activeSearchParamName = "general-practitioner"; + } else if ("Organization".equalsIgnoreCase(theResourceType)) { + resourceToCheck = "Patient"; + activeSearchParamName = "organization"; + } + return mySearchParamRegistry.getActiveSearchParam(resourceToCheck, activeSearchParamName); + } + /** * Must not be called for resources types listed in PATIENT_BULK_EXPORT_FORWARD_REFERENCE_RESOURCE_TYPES * diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/bulk/imprt/svc/BulkDataImportSvcImpl.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/bulk/imprt/svc/BulkDataImportSvcImpl.java index 301d7739466..8d4097f0aa9 100644 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/bulk/imprt/svc/BulkDataImportSvcImpl.java +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/bulk/imprt/svc/BulkDataImportSvcImpl.java @@ -40,6 +40,8 @@ import ca.uhn.fhir.rest.server.exceptions.InvalidRequestException; import ca.uhn.fhir.util.Logs; import ca.uhn.fhir.util.ValidateUtil; import com.apicatalog.jsonld.StringUtils; +import jakarta.annotation.Nonnull; +import jakarta.annotation.PostConstruct; import org.apache.commons.lang3.time.DateUtils; import org.quartz.JobExecutionContext; import org.slf4j.Logger; @@ -58,8 +60,6 @@ import java.util.Objects; import java.util.Optional; import java.util.UUID; import java.util.concurrent.Semaphore; -import javax.annotation.Nonnull; -import javax.annotation.PostConstruct; import static ca.uhn.fhir.batch2.jobs.importpull.BulkImportPullConfig.BULK_IMPORT_JOB_NAME; diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/cache/ResourceVersionSvcDaoImpl.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/cache/ResourceVersionSvcDaoImpl.java index cdd9e944eef..f3fa8c0016b 100644 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/cache/ResourceVersionSvcDaoImpl.java +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/cache/ResourceVersionSvcDaoImpl.java @@ -29,6 +29,7 @@ import ca.uhn.fhir.jpa.model.entity.ResourceTable; import ca.uhn.fhir.jpa.searchparam.SearchParameterMap; import ca.uhn.fhir.jpa.util.QueryChunker; import ca.uhn.fhir.rest.api.server.SystemRequestDetails; +import jakarta.annotation.Nonnull; import org.hl7.fhir.instance.model.api.IIdType; import org.slf4j.Logger; import org.springframework.beans.factory.annotation.Autowired; @@ -41,7 +42,6 @@ import java.util.HashMap; import java.util.List; import java.util.Optional; import java.util.stream.Collectors; -import javax.annotation.Nonnull; import static org.slf4j.LoggerFactory.getLogger; diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/config/Batch2SupportConfig.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/config/Batch2SupportConfig.java index 0ea5cae5175..3c3f228adff 100644 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/config/Batch2SupportConfig.java +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/config/Batch2SupportConfig.java @@ -34,11 +34,10 @@ import ca.uhn.fhir.jpa.delete.batch2.DeleteExpungeSqlBuilder; import ca.uhn.fhir.jpa.delete.batch2.DeleteExpungeSvcImpl; import ca.uhn.fhir.jpa.reindex.Batch2DaoSvcImpl; import ca.uhn.fhir.jpa.searchparam.MatchUrlService; +import jakarta.persistence.EntityManager; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.context.annotation.Bean; -import javax.persistence.EntityManager; - public class Batch2SupportConfig { @Bean @@ -47,15 +46,9 @@ public class Batch2SupportConfig { MatchUrlService theMatchUrlService, DaoRegistry theDaoRegistry, FhirContext theFhirContext, - IHapiTransactionService theTransactionService, - JpaStorageSettings theJpaStorageSettings) { + IHapiTransactionService theTransactionService) { return new Batch2DaoSvcImpl( - theResourceTableDao, - theMatchUrlService, - theDaoRegistry, - theFhirContext, - theTransactionService, - theJpaStorageSettings); + theResourceTableDao, theMatchUrlService, theDaoRegistry, theFhirContext, theTransactionService); } @Bean diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/config/EnversAuditConfig.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/config/EnversAuditConfig.java index 18fd08f7a28..be5702b2e89 100644 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/config/EnversAuditConfig.java +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/config/EnversAuditConfig.java @@ -20,13 +20,12 @@ package ca.uhn.fhir.jpa.config; * #L% */ +import jakarta.persistence.EntityManagerFactory; import org.hibernate.envers.AuditReader; import org.hibernate.envers.AuditReaderFactory; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; -import javax.persistence.EntityManagerFactory; - @Configuration public class EnversAuditConfig { private final EntityManagerFactory myEntityManagerFactory; diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/config/HapiFhirHibernateJpaDialect.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/config/HapiFhirHibernateJpaDialect.java index a926dc35c85..2869152a254 100644 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/config/HapiFhirHibernateJpaDialect.java +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/config/HapiFhirHibernateJpaDialect.java @@ -21,12 +21,14 @@ package ca.uhn.fhir.jpa.config; import ca.uhn.fhir.i18n.HapiLocalizer; import ca.uhn.fhir.i18n.Msg; -import ca.uhn.fhir.jpa.model.entity.ForcedId; import ca.uhn.fhir.jpa.model.entity.ResourceHistoryTable; import ca.uhn.fhir.jpa.model.entity.ResourceIndexedComboStringUnique; import ca.uhn.fhir.jpa.model.entity.ResourceSearchUrlEntity; +import ca.uhn.fhir.jpa.model.entity.ResourceTable; import ca.uhn.fhir.rest.server.exceptions.ResourceVersionConflictException; import ca.uhn.fhir.system.HapiSystemProperties; +import jakarta.annotation.Nonnull; +import jakarta.persistence.PersistenceException; import org.hibernate.HibernateException; import org.hibernate.PessimisticLockException; import org.hibernate.exception.ConstraintViolationException; @@ -35,15 +37,14 @@ import org.slf4j.LoggerFactory; import org.springframework.dao.DataAccessException; import org.springframework.orm.jpa.vendor.HibernateJpaDialect; -import javax.persistence.PersistenceException; - import static org.apache.commons.lang3.StringUtils.defaultString; import static org.apache.commons.lang3.StringUtils.isNotBlank; public class HapiFhirHibernateJpaDialect extends HibernateJpaDialect { private static final Logger ourLog = LoggerFactory.getLogger(HapiFhirHibernateJpaDialect.class); - private HapiLocalizer myLocalizer; + static final String RESOURCE_VERSION_CONSTRAINT_FAILURE = "resourceVersionConstraintFailure"; + private final HapiLocalizer myLocalizer; /** * Constructor @@ -61,7 +62,7 @@ public class HapiFhirHibernateJpaDialect extends HibernateJpaDialect { } @Override - protected DataAccessException convertHibernateAccessException(HibernateException theException) { + protected DataAccessException convertHibernateAccessException(@Nonnull HibernateException theException) { return convertHibernateAccessException(theException, null); } @@ -86,22 +87,17 @@ public class HapiFhirHibernateJpaDialect extends HibernateJpaDialect { if (isNotBlank(constraintName)) { constraintName = constraintName.toUpperCase(); if (constraintName.contains(ResourceHistoryTable.IDX_RESVER_ID_VER)) { - throw new ResourceVersionConflictException(Msg.code(823) - + messageToPrepend - + myLocalizer.getMessage( - HapiFhirHibernateJpaDialect.class, "resourceVersionConstraintFailure")); + throw new ResourceVersionConflictException( + Msg.code(823) + makeErrorMessage(messageToPrepend, RESOURCE_VERSION_CONSTRAINT_FAILURE)); } if (constraintName.contains(ResourceIndexedComboStringUnique.IDX_IDXCMPSTRUNIQ_STRING)) { throw new ResourceVersionConflictException(Msg.code(824) - + messageToPrepend - + myLocalizer.getMessage( - HapiFhirHibernateJpaDialect.class, - "resourceIndexedCompositeStringUniqueConstraintFailure")); + + makeErrorMessage( + messageToPrepend, "resourceIndexedCompositeStringUniqueConstraintFailure")); } - if (constraintName.contains(ForcedId.IDX_FORCEDID_TYPE_FID)) { - throw new ResourceVersionConflictException(Msg.code(825) - + messageToPrepend - + myLocalizer.getMessage(HapiFhirHibernateJpaDialect.class, "forcedIdConstraintFailure")); + if (constraintName.contains(ResourceTable.IDX_RES_TYPE_FHIR_ID)) { + throw new ResourceVersionConflictException( + Msg.code(825) + makeErrorMessage(messageToPrepend, "forcedIdConstraintFailure")); } if (constraintName.contains(ResourceSearchUrlEntity.RES_SEARCH_URL_COLUMN_NAME)) { throw super.convertHibernateAccessException(theException); @@ -124,21 +120,24 @@ public class HapiFhirHibernateJpaDialect extends HibernateJpaDialect { * StressTestR4Test method testMultiThreadedUpdateSameResourceInTransaction() */ if (theException instanceof org.hibernate.StaleStateException) { - String msg = messageToPrepend - + myLocalizer.getMessage(HapiFhirHibernateJpaDialect.class, "resourceVersionConstraintFailure"); - throw new ResourceVersionConflictException(Msg.code(826) + msg); + throw new ResourceVersionConflictException( + Msg.code(826) + makeErrorMessage(messageToPrepend, RESOURCE_VERSION_CONSTRAINT_FAILURE)); } if (theException instanceof org.hibernate.PessimisticLockException) { PessimisticLockException ex = (PessimisticLockException) theException; String sql = defaultString(ex.getSQL()).toUpperCase(); if (sql.contains(ResourceHistoryTable.HFJ_RES_VER)) { - String msg = messageToPrepend - + myLocalizer.getMessage(HapiFhirHibernateJpaDialect.class, "resourceVersionConstraintFailure"); - throw new ResourceVersionConflictException(Msg.code(827) + msg); + throw new ResourceVersionConflictException( + Msg.code(827) + makeErrorMessage(messageToPrepend, RESOURCE_VERSION_CONSTRAINT_FAILURE)); } } DataAccessException retVal = super.convertHibernateAccessException(theException); return retVal; } + + @Nonnull + private String makeErrorMessage(String thePrefix, String theMessageKey) { + return thePrefix + myLocalizer.getMessage(HapiFhirHibernateJpaDialect.class, theMessageKey); + } } diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/config/JpaConfig.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/config/JpaConfig.java index 2fc257245cd..e61559c1392 100644 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/config/JpaConfig.java +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/config/JpaConfig.java @@ -32,9 +32,7 @@ import ca.uhn.fhir.interceptor.model.RequestPartitionId; import ca.uhn.fhir.jpa.api.config.JpaStorageSettings; import ca.uhn.fhir.jpa.api.dao.DaoRegistry; import ca.uhn.fhir.jpa.api.model.ExpungeOptions; -import ca.uhn.fhir.jpa.api.svc.IDeleteExpungeSvc; import ca.uhn.fhir.jpa.api.svc.IIdHelperService; -import ca.uhn.fhir.jpa.api.svc.IMdmClearHelperSvc; import ca.uhn.fhir.jpa.api.svc.ISearchUrlJobMaintenanceSvc; import ca.uhn.fhir.jpa.binary.interceptor.BinaryStorageInterceptor; import ca.uhn.fhir.jpa.binary.provider.BinaryAccessProvider; @@ -43,7 +41,6 @@ import ca.uhn.fhir.jpa.bulk.export.svc.BulkDataExportJobSchedulingHelperImpl; import ca.uhn.fhir.jpa.bulk.export.svc.BulkExportHelperService; import ca.uhn.fhir.jpa.bulk.imprt.api.IBulkDataImportSvc; import ca.uhn.fhir.jpa.bulk.imprt.svc.BulkDataImportSvcImpl; -import ca.uhn.fhir.jpa.bulk.mdm.MdmClearHelperSvcImpl; import ca.uhn.fhir.jpa.cache.IResourceVersionSvc; import ca.uhn.fhir.jpa.cache.ResourceVersionSvcDaoImpl; import ca.uhn.fhir.jpa.dao.DaoSearchParamProvider; @@ -54,7 +51,6 @@ import ca.uhn.fhir.jpa.dao.IJpaStorageResourceParser; import ca.uhn.fhir.jpa.dao.ISearchBuilder; import ca.uhn.fhir.jpa.dao.JpaStorageResourceParser; import ca.uhn.fhir.jpa.dao.MatchResourceUrlService; -import ca.uhn.fhir.jpa.dao.ObservationLastNIndexPersistSvc; import ca.uhn.fhir.jpa.dao.SearchBuilderFactory; import ca.uhn.fhir.jpa.dao.TransactionProcessor; import ca.uhn.fhir.jpa.dao.data.IResourceModifiedDao; @@ -117,7 +113,6 @@ import ca.uhn.fhir.jpa.search.builder.predicate.ComboNonUniqueSearchParameterPre import ca.uhn.fhir.jpa.search.builder.predicate.ComboUniqueSearchParameterPredicateBuilder; import ca.uhn.fhir.jpa.search.builder.predicate.CoordsPredicateBuilder; import ca.uhn.fhir.jpa.search.builder.predicate.DatePredicateBuilder; -import ca.uhn.fhir.jpa.search.builder.predicate.ForcedIdPredicateBuilder; import ca.uhn.fhir.jpa.search.builder.predicate.NumberPredicateBuilder; import ca.uhn.fhir.jpa.search.builder.predicate.QuantityNormalizedPredicateBuilder; import ca.uhn.fhir.jpa.search.builder.predicate.QuantityPredicateBuilder; @@ -181,6 +176,7 @@ import ca.uhn.fhir.subscription.api.IResourceModifiedMessagePersistenceSvc; import ca.uhn.fhir.util.IMetaTagSorter; import ca.uhn.fhir.util.MetaTagSorterAlphabetical; import ca.uhn.hapi.converters.canonical.VersionCanonicalizer; +import jakarta.annotation.Nullable; import org.hl7.fhir.common.hapi.validation.support.UnknownCodeSystemWarningValidationSupport; import org.hl7.fhir.utilities.graphql.IGraphQLStorageServices; import org.springframework.beans.factory.annotation.Autowired; @@ -199,7 +195,6 @@ import org.springframework.scheduling.concurrent.ScheduledExecutorFactoryBean; import org.springframework.transaction.PlatformTransactionManager; import java.util.Date; -import javax.annotation.Nullable; @Configuration // repositoryFactoryBeanClass: EnversRevisionRepositoryFactoryBean is needed primarily for unit testing @@ -227,7 +222,6 @@ public class JpaConfig { public static final String PERSISTED_JPA_BUNDLE_PROVIDER_BY_SEARCH = "PersistedJpaBundleProvider_BySearch"; public static final String PERSISTED_JPA_SEARCH_FIRST_PAGE_BUNDLE_PROVIDER = "PersistedJpaSearchFirstPageBundleProvider"; - public static final String SEARCH_BUILDER = "SearchBuilder"; public static final String HISTORY_BUILDER = "HistoryBuilder"; private static final String HAPI_DEFAULT_SCHEDULER_GROUP = "HAPI"; @@ -568,7 +562,8 @@ public class JpaConfig { @Bean(name = PERSISTED_JPA_BUNDLE_PROVIDER_BY_SEARCH) @Scope("prototype") - public PersistedJpaBundleProvider newPersistedJpaBundleProvider(RequestDetails theRequest, Search theSearch) { + public PersistedJpaBundleProvider newPersistedJpaBundleProviderBySearch( + RequestDetails theRequest, Search theSearch) { return new PersistedJpaBundleProvider(theRequest, theSearch); } @@ -576,12 +571,11 @@ public class JpaConfig { @Scope("prototype") public PersistedJpaSearchFirstPageBundleProvider newPersistedJpaSearchFirstPageBundleProvider( RequestDetails theRequest, - Search theSearch, SearchTask theSearchTask, ISearchBuilder theSearchBuilder, RequestPartitionId theRequestPartitionId) { return new PersistedJpaSearchFirstPageBundleProvider( - theSearch, theSearchTask, theSearchBuilder, theRequest, theRequestPartitionId); + theSearchTask, theSearchBuilder, theRequest, theRequestPartitionId); } @Bean(name = RepositoryValidatingRuleBuilder.REPOSITORY_VALIDATING_RULE_BUILDER) @@ -616,12 +610,6 @@ public class JpaConfig { return new DatePredicateBuilder(theSearchBuilder); } - @Bean - @Scope("prototype") - public ForcedIdPredicateBuilder newForcedIdPredicateBuilder(SearchQueryBuilder theSearchBuilder) { - return new ForcedIdPredicateBuilder(theSearchBuilder); - } - @Bean @Scope("prototype") public NumberPredicateBuilder newNumberPredicateBuilder(SearchQueryBuilder theSearchBuilder) { @@ -705,7 +693,7 @@ public class JpaConfig { @Bean(name = HISTORY_BUILDER) @Scope("prototype") - public HistoryBuilder newPersistedJpaSearchFirstPageBundleProvider( + public HistoryBuilder newHistoryBuilder( @Nullable String theResourceType, @Nullable Long theResourceId, @Nullable Date theRangeStartInclusive, @@ -843,11 +831,6 @@ public class JpaConfig { return new TermReindexingSvcImpl(); } - @Bean - public ObservationLastNIndexPersistSvc baseObservationLastNIndexpersistSvc() { - return new ObservationLastNIndexPersistSvc(); - } - @Bean @Scope("prototype") public PersistenceContextProvider persistenceContextProvider() { @@ -872,11 +855,6 @@ public class JpaConfig { return new SearchUrlJobMaintenanceSvcImpl(theResourceSearchUrlSvc); } - @Bean - public IMdmClearHelperSvc helperSvc(IDeleteExpungeSvc theDeleteExpungeSvc) { - return new MdmClearHelperSvcImpl(theDeleteExpungeSvc); - } - @Bean public IResourceModifiedMessagePersistenceSvc subscriptionMessagePersistence( FhirContext theFhirContext, diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/config/r4/JpaR4Config.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/config/r4/JpaR4Config.java index 281abe9a277..2175aaf4c8c 100644 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/config/r4/JpaR4Config.java +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/config/r4/JpaR4Config.java @@ -22,7 +22,6 @@ package ca.uhn.fhir.jpa.config.r4; import ca.uhn.fhir.context.FhirContext; import ca.uhn.fhir.context.support.IValidationSupport; import ca.uhn.fhir.jpa.api.IDaoRegistry; -import ca.uhn.fhir.jpa.api.dao.IFhirResourceDao; import ca.uhn.fhir.jpa.api.dao.IFhirSystemDao; import ca.uhn.fhir.jpa.config.GeneratedDaoAndResourceProviderConfigR4; import ca.uhn.fhir.jpa.config.JpaConfig; @@ -31,9 +30,6 @@ import ca.uhn.fhir.jpa.dao.r4.TransactionProcessorVersionAdapterR4; import ca.uhn.fhir.jpa.graphql.GraphQLProvider; import ca.uhn.fhir.jpa.graphql.GraphQLProviderWithIntrospection; import ca.uhn.fhir.jpa.provider.JpaSystemProvider; -import ca.uhn.fhir.jpa.provider.r4.IMemberMatchConsentHook; -import ca.uhn.fhir.jpa.provider.r4.MemberMatchR4ResourceProvider; -import ca.uhn.fhir.jpa.provider.r4.MemberMatcherR4Helper; import ca.uhn.fhir.jpa.term.TermLoaderSvcImpl; import ca.uhn.fhir.jpa.term.TermVersionAdapterSvcR4; import ca.uhn.fhir.jpa.term.api.ITermCodeSystemStorageSvc; @@ -42,12 +38,8 @@ import ca.uhn.fhir.jpa.term.api.ITermLoaderSvc; import ca.uhn.fhir.jpa.term.api.ITermVersionAdapterSvc; import ca.uhn.fhir.rest.server.util.ISearchParamRegistry; import org.hl7.fhir.r4.model.Bundle; -import org.hl7.fhir.r4.model.Consent; -import org.hl7.fhir.r4.model.Coverage; import org.hl7.fhir.r4.model.Meta; -import org.hl7.fhir.r4.model.Patient; import org.hl7.fhir.utilities.graphql.IGraphQLStorageServices; -import org.springframework.beans.factory.annotation.Autowired; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.Import; @@ -104,21 +96,4 @@ public class JpaR4Config { ITermDeferredStorageSvc theDeferredStorageSvc, ITermCodeSystemStorageSvc theCodeSystemStorageSvc) { return new TermLoaderSvcImpl(theDeferredStorageSvc, theCodeSystemStorageSvc); } - - @Bean - public MemberMatcherR4Helper memberMatcherR4Helper( - @Autowired FhirContext theContext, - @Autowired IFhirResourceDao theCoverageDao, - @Autowired IFhirResourceDao thePatientDao, - @Autowired IFhirResourceDao theConsentDao, - @Autowired(required = false) IMemberMatchConsentHook theExtensionProvider) { - return new MemberMatcherR4Helper( - theContext, theCoverageDao, thePatientDao, theConsentDao, theExtensionProvider); - } - - @Bean - public MemberMatchR4ResourceProvider memberMatchR4ResourceProvider( - FhirContext theFhirContext, MemberMatcherR4Helper theMemberMatchR4Helper) { - return new MemberMatchR4ResourceProvider(theFhirContext, theMemberMatchR4Helper); - } } diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/config/util/HapiEntityManagerFactoryUtil.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/config/util/HapiEntityManagerFactoryUtil.java index f3c0a72e75f..fe328bb5e1e 100644 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/config/util/HapiEntityManagerFactoryUtil.java +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/config/util/HapiEntityManagerFactoryUtil.java @@ -20,30 +20,90 @@ package ca.uhn.fhir.jpa.config.util; import ca.uhn.fhir.context.FhirContext; +import ca.uhn.fhir.jpa.api.config.JpaStorageSettings; import ca.uhn.fhir.jpa.config.HapiFhirHibernateJpaDialect; import ca.uhn.fhir.jpa.config.HapiFhirLocalContainerEntityManagerFactoryBean; +import ca.uhn.fhir.jpa.util.ISequenceValueMassager; +import ca.uhn.fhir.util.ReflectionUtil; +import jakarta.persistence.spi.PersistenceUnitInfo; +import org.hibernate.boot.registry.BootstrapServiceRegistry; +import org.hibernate.boot.registry.StandardServiceRegistryBuilder; import org.hibernate.jpa.HibernatePersistenceProvider; +import org.hibernate.jpa.boot.internal.EntityManagerFactoryBuilderImpl; +import org.hibernate.jpa.boot.internal.PersistenceUnitInfoDescriptor; +import org.hibernate.jpa.boot.spi.EntityManagerFactoryBuilder; import org.springframework.beans.factory.config.ConfigurableListableBeanFactory; import org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean; +import java.util.Map; + public final class HapiEntityManagerFactoryUtil { private HapiEntityManagerFactoryUtil() {} + /** * This method provides a partially completed entity manager * factory with HAPI FHIR customizations */ public static LocalContainerEntityManagerFactoryBean newEntityManagerFactory( - ConfigurableListableBeanFactory myConfigurableListableBeanFactory, FhirContext theFhirContext) { + ConfigurableListableBeanFactory myConfigurableListableBeanFactory, + FhirContext theFhirContext, + JpaStorageSettings theStorageSettings) { + LocalContainerEntityManagerFactoryBean retVal = new HapiFhirLocalContainerEntityManagerFactoryBean(myConfigurableListableBeanFactory); - configureEntityManagerFactory(retVal, theFhirContext); + + configureEntityManagerFactory(retVal, theFhirContext, theStorageSettings); return retVal; } public static void configureEntityManagerFactory( - LocalContainerEntityManagerFactoryBean theFactory, FhirContext theFhirContext) { + LocalContainerEntityManagerFactoryBean theFactory, + FhirContext theFhirContext, + JpaStorageSettings theStorageSettings) { theFactory.setJpaDialect(new HapiFhirHibernateJpaDialect(theFhirContext.getLocalizer())); theFactory.setPackagesToScan("ca.uhn.fhir.jpa.model.entity", "ca.uhn.fhir.jpa.entity"); - theFactory.setPersistenceProvider(new HibernatePersistenceProvider()); + theFactory.setPersistenceProvider(new MyHibernatePersistenceProvider(theStorageSettings)); + } + + private static class MyHibernatePersistenceProvider extends HibernatePersistenceProvider { + + private final JpaStorageSettings myStorageSettings; + + public MyHibernatePersistenceProvider(JpaStorageSettings theStorageSettings) { + myStorageSettings = theStorageSettings; + } + + /** + * @see MyEntityManagerFactoryBuilderImpl for an explanation of why we do this + */ + @Override + protected EntityManagerFactoryBuilder getEntityManagerFactoryBuilder( + PersistenceUnitInfo info, Map integration) { + return new MyEntityManagerFactoryBuilderImpl(info, integration); + } + + /** + * This class extends the default hibernate EntityManagerFactoryBuilder in order to + * register a custom service (the {@link ISequenceValueMassager}, which is used in + * {@link ca.uhn.fhir.jpa.model.dialect.HapiSequenceStyleGenerator}. + *

    + * In Hibernate 5 we didn't need to do this, since we could just register + * the service with Spring and Hibernate would ask Spring for it. This no longer + * seems to work in Hibernate 6, so we now have to manually register it. + */ + private class MyEntityManagerFactoryBuilderImpl extends EntityManagerFactoryBuilderImpl { + public MyEntityManagerFactoryBuilderImpl(PersistenceUnitInfo theInfo, Map theIntegration) { + super(new PersistenceUnitInfoDescriptor(theInfo), (Map) theIntegration); + } + + @Override + protected StandardServiceRegistryBuilder getStandardServiceRegistryBuilder(BootstrapServiceRegistry bsr) { + StandardServiceRegistryBuilder retVal = super.getStandardServiceRegistryBuilder(bsr); + ISequenceValueMassager sequenceValueMassager = + ReflectionUtil.newInstance(myStorageSettings.getSequenceValueMassagerClass()); + retVal.addService(ISequenceValueMassager.class, sequenceValueMassager); + return retVal; + } + } } } diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/BaseHapiFhirDao.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/BaseHapiFhirDao.java index 13dfc7b25f0..90ac47f982d 100644 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/BaseHapiFhirDao.java +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/BaseHapiFhirDao.java @@ -107,6 +107,18 @@ import com.google.common.collect.Sets; import com.google.common.hash.HashCode; import com.google.common.hash.HashFunction; import com.google.common.hash.Hashing; +import jakarta.annotation.Nonnull; +import jakarta.annotation.Nullable; +import jakarta.annotation.PostConstruct; +import jakarta.persistence.EntityManager; +import jakarta.persistence.NoResultException; +import jakarta.persistence.PersistenceContext; +import jakarta.persistence.PersistenceContextType; +import jakarta.persistence.TypedQuery; +import jakarta.persistence.criteria.CriteriaBuilder; +import jakarta.persistence.criteria.CriteriaQuery; +import jakarta.persistence.criteria.Predicate; +import jakarta.persistence.criteria.Root; import org.apache.commons.lang3.NotImplementedException; import org.apache.commons.lang3.StringUtils; import org.apache.commons.lang3.Validate; @@ -136,9 +148,7 @@ import org.springframework.transaction.support.TransactionSynchronization; import org.springframework.transaction.support.TransactionSynchronizationManager; import org.springframework.transaction.support.TransactionTemplate; -import java.nio.charset.StandardCharsets; import java.util.ArrayList; -import java.util.Arrays; import java.util.Collection; import java.util.Collections; import java.util.Date; @@ -149,18 +159,6 @@ import java.util.List; import java.util.Set; import java.util.StringTokenizer; import java.util.stream.Collectors; -import javax.annotation.Nonnull; -import javax.annotation.Nullable; -import javax.annotation.PostConstruct; -import javax.persistence.EntityManager; -import javax.persistence.NoResultException; -import javax.persistence.PersistenceContext; -import javax.persistence.PersistenceContextType; -import javax.persistence.TypedQuery; -import javax.persistence.criteria.CriteriaBuilder; -import javax.persistence.criteria.CriteriaQuery; -import javax.persistence.criteria.Predicate; -import javax.persistence.criteria.Root; import javax.xml.stream.events.Characters; import javax.xml.stream.events.XMLEvent; @@ -645,7 +643,6 @@ public abstract class BaseHapiFhirDao extends BaseStora theEntity.setResourceType(toResourceName(theResource)); } - byte[] resourceBinary; String resourceText; ResourceEncodingEnum encoding; boolean changed = false; @@ -662,7 +659,6 @@ public abstract class BaseHapiFhirDao extends BaseStora if (address != null) { encoding = ResourceEncodingEnum.ESR; - resourceBinary = null; resourceText = address.getProviderId() + ":" + address.getLocation(); changed = true; @@ -680,19 +676,9 @@ public abstract class BaseHapiFhirDao extends BaseStora theEntity.setFhirVersion(myContext.getVersion().getVersion()); HashFunction sha256 = Hashing.sha256(); - HashCode hashCode; - String encodedResource = encodeResource(theResource, encoding, excludeElements, myContext); - if (myStorageSettings.getInlineResourceTextBelowSize() > 0 - && encodedResource.length() < myStorageSettings.getInlineResourceTextBelowSize()) { - resourceText = encodedResource; - resourceBinary = null; - encoding = ResourceEncodingEnum.JSON; - hashCode = sha256.hashUnencodedChars(encodedResource); - } else { - resourceText = null; - resourceBinary = getResourceBinary(encoding, encodedResource); - hashCode = sha256.hashBytes(resourceBinary); - } + resourceText = encodeResource(theResource, encoding, excludeElements, myContext); + encoding = ResourceEncodingEnum.JSON; + HashCode hashCode = sha256.hashUnencodedChars(resourceText); String hashSha256 = hashCode.toString(); if (!hashSha256.equals(theEntity.getHashSha256())) { @@ -710,7 +696,6 @@ public abstract class BaseHapiFhirDao extends BaseStora } else { encoding = null; - resourceBinary = null; resourceText = null; } @@ -728,7 +713,6 @@ public abstract class BaseHapiFhirDao extends BaseStora changed = true; } - resourceBinary = null; resourceText = null; encoding = ResourceEncodingEnum.DEL; } @@ -753,46 +737,19 @@ public abstract class BaseHapiFhirDao extends BaseStora if (currentHistoryVersion == null || !currentHistoryVersion.hasResource()) { changed = true; } else { - changed = !Arrays.equals(currentHistoryVersion.getResource(), resourceBinary); + changed = !StringUtils.equals(currentHistoryVersion.getResourceTextVc(), resourceText); } } } EncodedResource retVal = new EncodedResource(); retVal.setEncoding(encoding); - retVal.setResourceBinary(resourceBinary); retVal.setResourceText(resourceText); retVal.setChanged(changed); return retVal; } - /** - * helper for returning the encoded byte array of the input resource string based on the encoding. - * - * @param encoding the encoding to used - * @param encodedResource the resource to encode - * @return byte array of the resource - */ - @Nonnull - private byte[] getResourceBinary(ResourceEncodingEnum encoding, String encodedResource) { - byte[] resourceBinary; - switch (encoding) { - case JSON: - resourceBinary = encodedResource.getBytes(StandardCharsets.UTF_8); - break; - case JSONC: - resourceBinary = GZipUtil.compress(encodedResource); - break; - default: - case DEL: - case ESR: - resourceBinary = new byte[0]; - break; - } - return resourceBinary; - } - /** * helper to format the meta element for serialization of the resource. * @@ -1437,8 +1394,7 @@ public abstract class BaseHapiFhirDao extends BaseStora List excludeElements = new ArrayList<>(8); getExcludedElements(historyEntity.getResourceType(), excludeElements, theResource.getMeta()); String encodedResourceString = encodeResource(theResource, encoding, excludeElements, myContext); - byte[] resourceBinary = getResourceBinary(encoding, encodedResourceString); - boolean changed = !Arrays.equals(historyEntity.getResource(), resourceBinary); + boolean changed = !StringUtils.equals(historyEntity.getResourceTextVc(), encodedResourceString); historyEntity.setUpdated(theTransactionDetails.getTransactionDate()); @@ -1450,19 +1406,14 @@ public abstract class BaseHapiFhirDao extends BaseStora return historyEntity; } - if (getStorageSettings().getInlineResourceTextBelowSize() > 0 - && encodedResourceString.length() < getStorageSettings().getInlineResourceTextBelowSize()) { - populateEncodedResource(encodedResource, encodedResourceString, null, ResourceEncodingEnum.JSON); - } else { - populateEncodedResource(encodedResource, null, resourceBinary, encoding); - } + populateEncodedResource(encodedResource, encodedResourceString, ResourceEncodingEnum.JSON); } + /* * Save the resource itself to the resourceHistoryTable */ historyEntity = myEntityManager.merge(historyEntity); historyEntity.setEncoding(encodedResource.getEncoding()); - historyEntity.setResource(encodedResource.getResourceBinary()); historyEntity.setResourceTextVc(encodedResource.getResourceText()); myResourceHistoryTableDao.save(historyEntity); @@ -1472,12 +1423,8 @@ public abstract class BaseHapiFhirDao extends BaseStora } private void populateEncodedResource( - EncodedResource encodedResource, - String encodedResourceString, - byte[] theResourceBinary, - ResourceEncodingEnum theEncoding) { + EncodedResource encodedResource, String encodedResourceString, ResourceEncodingEnum theEncoding) { encodedResource.setResourceText(encodedResourceString); - encodedResource.setResourceBinary(theResourceBinary); encodedResource.setEncoding(theEncoding); } @@ -1542,7 +1489,6 @@ public abstract class BaseHapiFhirDao extends BaseStora } historyEntry.setEncoding(theChanged.getEncoding()); - historyEntry.setResource(theChanged.getResourceBinary()); historyEntry.setResourceTextVc(theChanged.getResourceText()); ourLog.debug("Saving history entry ID[{}] for RES_ID[{}]", historyEntry.getId(), historyEntry.getResourceId()); diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/BaseHapiFhirResourceDao.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/BaseHapiFhirResourceDao.java index af0cefa9854..7120599b146 100644 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/BaseHapiFhirResourceDao.java +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/BaseHapiFhirResourceDao.java @@ -117,6 +117,15 @@ import ca.uhn.fhir.validation.IValidatorModule; import ca.uhn.fhir.validation.ValidationOptions; import ca.uhn.fhir.validation.ValidationResult; import com.google.common.annotations.VisibleForTesting; +import com.google.common.collect.Streams; +import jakarta.annotation.Nonnull; +import jakarta.annotation.Nullable; +import jakarta.annotation.PostConstruct; +import jakarta.persistence.LockModeType; +import jakarta.persistence.NoResultException; +import jakarta.persistence.TypedQuery; +import jakarta.servlet.http.HttpServletResponse; +import org.apache.commons.io.IOUtils; import org.apache.commons.lang3.Validate; import org.hl7.fhir.instance.model.api.IBaseCoding; import org.hl7.fhir.instance.model.api.IBaseMetaType; @@ -127,7 +136,6 @@ import org.hl7.fhir.instance.model.api.IPrimitiveType; import org.hl7.fhir.r4.model.Parameters; import org.hl7.fhir.r4.model.Parameters.ParametersParameterComponent; import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.beans.factory.annotation.Required; import org.springframework.data.domain.PageRequest; import org.springframework.data.domain.Slice; import org.springframework.transaction.PlatformTransactionManager; @@ -150,13 +158,7 @@ import java.util.UUID; import java.util.concurrent.Callable; import java.util.function.Supplier; import java.util.stream.Collectors; -import javax.annotation.Nonnull; -import javax.annotation.Nullable; -import javax.annotation.PostConstruct; -import javax.persistence.LockModeType; -import javax.persistence.NoResultException; -import javax.persistence.TypedQuery; -import javax.servlet.http.HttpServletResponse; +import java.util.stream.Stream; import static java.util.Objects.isNull; import static org.apache.commons.lang3.StringUtils.isBlank; @@ -644,6 +646,7 @@ public abstract class BaseHapiFhirResourceDao extends B private void createForcedIdIfNeeded( ResourceTable theEntity, String theResourceId, boolean theCreateForPureNumericIds) { + // TODO MB delete this in step 3 if (isNotBlank(theResourceId) && theEntity.getForcedId() == null) { if (theCreateForPureNumericIds || !IdHelperService.isValidPid(theResourceId)) { ForcedId forcedId = new ForcedId(); @@ -1220,7 +1223,6 @@ public abstract class BaseHapiFhirResourceDao extends B } @SuppressWarnings("unchecked") - @Required public void setResourceType(Class theTableType) { myResourceType = (Class) theTableType; } @@ -1653,8 +1655,11 @@ public abstract class BaseHapiFhirResourceDao extends B CURRENTLY_REINDEXING.put(theResource, Boolean.TRUE); } + SystemRequestDetails request = new SystemRequestDetails(); + request.getUserData().put(JpaConstants.SKIP_REINDEX_ON_UPDATE, Boolean.TRUE); + updateEntity( - null, theResource, theEntity, theEntity.getDeleted(), true, false, transactionDetails, true, false); + request, theResource, theEntity, theEntity.getDeleted(), true, false, transactionDetails, true, false); if (theResource != null) { CURRENTLY_REINDEXING.put(theResource, null); } @@ -1684,19 +1689,17 @@ public abstract class BaseHapiFhirResourceDao extends B if (historyEntity.getEncoding() == ResourceEncodingEnum.JSONC || historyEntity.getEncoding() == ResourceEncodingEnum.JSON) { byte[] resourceBytes = historyEntity.getResource(); + + // Always migrate data out of the bytes column if (resourceBytes != null) { String resourceText = decodeResource(resourceBytes, historyEntity.getEncoding()); - if (myStorageSettings.getInlineResourceTextBelowSize() > 0 - && resourceText.length() < myStorageSettings.getInlineResourceTextBelowSize()) { - ourLog.debug( - "Storing text of resource {} version {} as inline VARCHAR", - entity.getResourceId(), - historyEntity.getVersion()); - historyEntity.setResourceTextVc(resourceText); - historyEntity.setResource(null); - historyEntity.setEncoding(ResourceEncodingEnum.JSON); - changed = true; - } + ourLog.debug( + "Storing text of resource {} version {} as inline VARCHAR", + entity.getResourceId(), + historyEntity.getVersion()); + historyEntity.setResourceTextVc(resourceText); + historyEntity.setEncoding(ResourceEncodingEnum.JSON); + changed = true; } } if (isBlank(historyEntity.getSourceUri()) && isBlank(historyEntity.getRequestId())) { @@ -2066,6 +2069,29 @@ public abstract class BaseHapiFhirResourceDao extends B }); } + @Override + public > Stream searchForIdStream( + SearchParameterMap theParams, + RequestDetails theRequest, + @Nullable IBaseResource theConditionalOperationTargetOrNull) { + // the Stream is useless outside the bound connection time, so require our caller to have a session. + HapiTransactionService.requireTransaction(); + + RequestPartitionId requestPartitionId = + myRequestPartitionHelperService.determineReadPartitionForRequestForSearchType( + theRequest, myResourceName, theParams, theConditionalOperationTargetOrNull); + + ISearchBuilder builder = mySearchBuilderFactory.newSearchBuilder(this, getResourceName(), getResourceType()); + + String uuid = UUID.randomUUID().toString(); + + SearchRuntimeDetails searchRuntimeDetails = new SearchRuntimeDetails(theRequest, uuid); + IResultIterator iter = + builder.createQuery(theParams, searchRuntimeDetails, theRequest, requestPartitionId); + // Adapt IResultIterator to stream, and connect the close handler. + return Streams.stream(iter).onClose(() -> IOUtils.closeQuietly(iter)); + } + protected MT toMetaDt(Class theType, Collection tagDefinitions) { MT retVal = ReflectionUtil.newInstance(theType); for (TagDefinition next : tagDefinitions) { diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/BaseHapiFhirSystemDao.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/BaseHapiFhirSystemDao.java index 6696a3b0e52..242669a58a9 100644 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/BaseHapiFhirSystemDao.java +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/BaseHapiFhirSystemDao.java @@ -48,6 +48,16 @@ import ca.uhn.fhir.rest.api.server.storage.IResourcePersistentId; import ca.uhn.fhir.rest.server.exceptions.MethodNotAllowedException; import ca.uhn.fhir.util.StopWatch; import com.google.common.annotations.VisibleForTesting; +import jakarta.annotation.Nullable; +import jakarta.persistence.EntityManager; +import jakarta.persistence.PersistenceContext; +import jakarta.persistence.PersistenceContextType; +import jakarta.persistence.TypedQuery; +import jakarta.persistence.criteria.CriteriaBuilder; +import jakarta.persistence.criteria.CriteriaQuery; +import jakarta.persistence.criteria.JoinType; +import jakarta.persistence.criteria.Predicate; +import jakarta.persistence.criteria.Root; import org.hl7.fhir.instance.model.api.IBaseBundle; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.context.ApplicationContext; @@ -60,16 +70,6 @@ import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.stream.Collectors; -import javax.annotation.Nullable; -import javax.persistence.EntityManager; -import javax.persistence.PersistenceContext; -import javax.persistence.PersistenceContextType; -import javax.persistence.TypedQuery; -import javax.persistence.criteria.CriteriaBuilder; -import javax.persistence.criteria.CriteriaQuery; -import javax.persistence.criteria.JoinType; -import javax.persistence.criteria.Predicate; -import javax.persistence.criteria.Root; public abstract class BaseHapiFhirSystemDao extends BaseStorageDao implements IFhirSystemDao { diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/EncodedResource.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/EncodedResource.java index 7695b44ca13..503a85b15d1 100644 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/EncodedResource.java +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/EncodedResource.java @@ -24,7 +24,6 @@ import ca.uhn.fhir.jpa.model.entity.ResourceEncodingEnum; class EncodedResource { private boolean myChanged; - private byte[] myResource; private ResourceEncodingEnum myEncoding; private String myResourceText; @@ -36,14 +35,6 @@ class EncodedResource { myEncoding = theEncoding; } - public byte[] getResourceBinary() { - return myResource; - } - - public void setResourceBinary(byte[] theResource) { - myResource = theResource; - } - public boolean isChanged() { return myChanged; } diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/FhirSystemDaoDstu2.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/FhirSystemDaoDstu2.java index b9ab01c5c6a..76e6279ddd2 100644 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/FhirSystemDaoDstu2.java +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/FhirSystemDaoDstu2.java @@ -23,11 +23,11 @@ import ca.uhn.fhir.jpa.model.entity.TagDefinition; import ca.uhn.fhir.model.dstu2.composite.MetaDt; import ca.uhn.fhir.model.dstu2.resource.Bundle; import ca.uhn.fhir.rest.api.server.RequestDetails; +import jakarta.persistence.TypedQuery; import org.hl7.fhir.instance.model.api.IBaseBundle; import java.util.Collection; import java.util.List; -import javax.persistence.TypedQuery; public class FhirSystemDaoDstu2 extends BaseHapiFhirSystemDao { diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/FulltextSearchSvcImpl.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/FulltextSearchSvcImpl.java index 346b1063e3d..f6b1791f57b 100644 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/FulltextSearchSvcImpl.java +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/FulltextSearchSvcImpl.java @@ -53,6 +53,10 @@ import ca.uhn.fhir.rest.server.util.CompositeInterceptorBroadcaster; import ca.uhn.fhir.rest.server.util.ISearchParamRegistry; import ca.uhn.fhir.rest.server.util.ResourceSearchParams; import com.google.common.collect.Ordering; +import jakarta.annotation.Nonnull; +import jakarta.persistence.EntityManager; +import jakarta.persistence.PersistenceContext; +import jakarta.persistence.PersistenceContextType; import org.hibernate.search.backend.elasticsearch.ElasticsearchExtension; import org.hibernate.search.engine.search.predicate.dsl.PredicateFinalStep; import org.hibernate.search.engine.search.predicate.dsl.SearchPredicateFactory; @@ -78,10 +82,6 @@ import java.util.List; import java.util.Spliterators; import java.util.stream.Collectors; import java.util.stream.StreamSupport; -import javax.annotation.Nonnull; -import javax.persistence.EntityManager; -import javax.persistence.PersistenceContext; -import javax.persistence.PersistenceContextType; import static ca.uhn.fhir.rest.server.BasePagingProvider.DEFAULT_MAX_PAGE_SIZE; import static org.apache.commons.lang3.StringUtils.isNotBlank; diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/HistoryBuilder.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/HistoryBuilder.java index 445e9d8860b..0b3ce422e5e 100644 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/HistoryBuilder.java +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/HistoryBuilder.java @@ -32,6 +32,18 @@ import ca.uhn.fhir.rest.param.HistorySearchStyleEnum; import ca.uhn.fhir.rest.server.exceptions.InvalidRequestException; import com.google.common.collect.ImmutableListMultimap; import com.google.common.collect.Multimaps; +import jakarta.annotation.Nullable; +import jakarta.persistence.EntityManager; +import jakarta.persistence.PersistenceContext; +import jakarta.persistence.PersistenceContextType; +import jakarta.persistence.TypedQuery; +import jakarta.persistence.criteria.CriteriaBuilder; +import jakarta.persistence.criteria.CriteriaQuery; +import jakarta.persistence.criteria.Expression; +import jakarta.persistence.criteria.JoinType; +import jakarta.persistence.criteria.Predicate; +import jakarta.persistence.criteria.Root; +import jakarta.persistence.criteria.Subquery; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; @@ -42,18 +54,6 @@ import java.util.List; import java.util.Optional; import java.util.Set; import java.util.stream.Collectors; -import javax.annotation.Nullable; -import javax.persistence.EntityManager; -import javax.persistence.PersistenceContext; -import javax.persistence.PersistenceContextType; -import javax.persistence.TypedQuery; -import javax.persistence.criteria.CriteriaBuilder; -import javax.persistence.criteria.CriteriaQuery; -import javax.persistence.criteria.Expression; -import javax.persistence.criteria.JoinType; -import javax.persistence.criteria.Predicate; -import javax.persistence.criteria.Root; -import javax.persistence.criteria.Subquery; import static ca.uhn.fhir.jpa.util.QueryParameterUtils.toPredicateArray; @@ -177,18 +177,13 @@ public class HistoryBuilder { if (!thePartitionId.isAllPartitions()) { if (thePartitionId.isDefaultPartition()) { - predicates.add(theCriteriaBuilder.isNull( - theFrom.get("myPartitionIdValue").as(Integer.class))); + predicates.add(theCriteriaBuilder.isNull(theFrom.get("myPartitionIdValue"))); } else if (thePartitionId.hasDefaultPartitionId()) { predicates.add(theCriteriaBuilder.or( - theCriteriaBuilder.isNull( - theFrom.get("myPartitionIdValue").as(Integer.class)), - theFrom.get("myPartitionIdValue") - .as(Integer.class) - .in(thePartitionId.getPartitionIdsWithoutDefault()))); + theCriteriaBuilder.isNull(theFrom.get("myPartitionIdValue")), + theFrom.get("myPartitionIdValue").in(thePartitionId.getPartitionIdsWithoutDefault()))); } else { - predicates.add( - theFrom.get("myPartitionIdValue").as(Integer.class).in(thePartitionId.getPartitionIds())); + predicates.add(theFrom.get("myPartitionIdValue").in(thePartitionId.getPartitionIds())); } } @@ -205,13 +200,12 @@ public class HistoryBuilder { if (HistorySearchStyleEnum.AT == theHistorySearchStyle && myResourceId != null) { addPredicateForAtQueryParameter(theCriteriaBuilder, theQuery, theFrom, predicates); } else { - predicates.add(theCriteriaBuilder.greaterThanOrEqualTo( - theFrom.get("myUpdated").as(Date.class), myRangeStartInclusive)); + predicates.add( + theCriteriaBuilder.greaterThanOrEqualTo(theFrom.get("myUpdated"), myRangeStartInclusive)); } } if (myRangeEndInclusive != null) { - predicates.add(theCriteriaBuilder.lessThanOrEqualTo( - theFrom.get("myUpdated").as(Date.class), myRangeEndInclusive)); + predicates.add(theCriteriaBuilder.lessThanOrEqualTo(theFrom.get("myUpdated"), myRangeEndInclusive)); } if (predicates.size() > 0) { @@ -226,20 +220,19 @@ public class HistoryBuilder { List thePredicates) { Subquery pastDateSubQuery = theQuery.subquery(Date.class); Root subQueryResourceHistory = pastDateSubQuery.from(ResourceHistoryTable.class); - Expression myUpdatedMostRecent = - theCriteriaBuilder.max(subQueryResourceHistory.get("myUpdated")).as(Date.class); - Expression myUpdatedMostRecentOrDefault = + Expression myUpdatedMostRecent = theCriteriaBuilder.max(subQueryResourceHistory.get("myUpdated")); + Expression myUpdatedMostRecentOrDefault = theCriteriaBuilder.coalesce(myUpdatedMostRecent, theCriteriaBuilder.literal(myRangeStartInclusive)); pastDateSubQuery .select(myUpdatedMostRecentOrDefault) .where( theCriteriaBuilder.lessThanOrEqualTo( - subQueryResourceHistory.get("myUpdated").as(Date.class), myRangeStartInclusive), + subQueryResourceHistory.get("myUpdated"), myRangeStartInclusive), theCriteriaBuilder.equal(subQueryResourceHistory.get("myResourceId"), myResourceId)); Predicate updatedDatePredicate = - theCriteriaBuilder.greaterThanOrEqualTo(theFrom.get("myUpdated").as(Date.class), pastDateSubQuery); + theCriteriaBuilder.greaterThanOrEqualTo(theFrom.get("myUpdated"), pastDateSubQuery); thePredicates.add(updatedDatePredicate); } diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/HistoryBuilderFactory.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/HistoryBuilderFactory.java index 31205fdba27..0d016d43cf6 100644 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/HistoryBuilderFactory.java +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/HistoryBuilderFactory.java @@ -20,11 +20,11 @@ package ca.uhn.fhir.jpa.dao; import ca.uhn.fhir.jpa.config.JpaConfig; +import jakarta.annotation.Nullable; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.context.ApplicationContext; import java.util.Date; -import javax.annotation.Nullable; public class HistoryBuilderFactory { diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/IJpaStorageResourceParser.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/IJpaStorageResourceParser.java index e706c3d6e02..5e171d96666 100644 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/IJpaStorageResourceParser.java +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/IJpaStorageResourceParser.java @@ -22,10 +22,10 @@ package ca.uhn.fhir.jpa.dao; import ca.uhn.fhir.jpa.model.entity.BaseTag; import ca.uhn.fhir.jpa.model.entity.IBaseResourceEntity; import ca.uhn.fhir.jpa.model.entity.ResourceTag; +import jakarta.annotation.Nullable; import org.hl7.fhir.instance.model.api.IBaseResource; import java.util.Collection; -import javax.annotation.Nullable; public interface IJpaStorageResourceParser extends IStorageResourceParser { diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/JpaPersistedResourceValidationSupport.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/JpaPersistedResourceValidationSupport.java index 7935d32684d..9bb9b9f9f08 100644 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/JpaPersistedResourceValidationSupport.java +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/JpaPersistedResourceValidationSupport.java @@ -36,6 +36,8 @@ import ca.uhn.fhir.rest.param.TokenParam; import ca.uhn.fhir.rest.param.UriParam; import ca.uhn.fhir.sl.cache.Cache; import ca.uhn.fhir.sl.cache.CacheFactory; +import jakarta.annotation.Nullable; +import jakarta.annotation.PostConstruct; import org.apache.commons.lang3.Validate; import org.hl7.fhir.instance.model.api.IAnyResource; import org.hl7.fhir.instance.model.api.IBaseResource; @@ -57,8 +59,6 @@ import java.util.List; import java.util.Optional; import java.util.concurrent.TimeUnit; import java.util.function.Supplier; -import javax.annotation.Nullable; -import javax.annotation.PostConstruct; import static org.apache.commons.lang3.StringUtils.isBlank; import static org.hl7.fhir.common.hapi.validation.support.ValidationConstants.LOINC_LOW; @@ -103,6 +103,11 @@ public class JpaPersistedResourceValidationSupport implements IValidationSupport myNoMatch = myFhirContext.getResourceDefinition("Basic").newInstance(); } + @Override + public String getName() { + return myFhirContext.getVersion().getVersion() + " JPA Validation Support"; + } + @Override public IBaseResource fetchCodeSystem(String theSystem) { if (TermReadSvcUtil.isLoincUnversionedCodeSystem(theSystem)) { diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/JpaResourceDaoCodeSystem.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/JpaResourceDaoCodeSystem.java index e4b128f608e..2bc82648bc2 100644 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/JpaResourceDaoCodeSystem.java +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/JpaResourceDaoCodeSystem.java @@ -24,6 +24,7 @@ import ca.uhn.fhir.context.FhirVersionEnum; import ca.uhn.fhir.context.support.ConceptValidationOptions; import ca.uhn.fhir.context.support.IValidationSupport; import ca.uhn.fhir.context.support.IValidationSupport.CodeValidationResult; +import ca.uhn.fhir.context.support.LookupCodeRequest; import ca.uhn.fhir.context.support.ValidationSupportContext; import ca.uhn.fhir.i18n.Msg; import ca.uhn.fhir.jpa.api.dao.IFhirResourceDaoCodeSystem; @@ -41,6 +42,9 @@ import ca.uhn.fhir.rest.param.TokenParam; import ca.uhn.fhir.rest.server.exceptions.InvalidRequestException; import ca.uhn.fhir.util.FhirTerser; import ca.uhn.hapi.converters.canonical.VersionCanonicalizer; +import jakarta.annotation.Nonnull; +import jakarta.annotation.PostConstruct; +import org.apache.commons.collections4.CollectionUtils; import org.hl7.fhir.common.hapi.validation.support.CommonCodeSystemsTerminologyService; import org.hl7.fhir.instance.model.api.IBaseCoding; import org.hl7.fhir.instance.model.api.IBaseDatatype; @@ -52,10 +56,10 @@ import org.hl7.fhir.r4.model.Coding; import org.springframework.beans.factory.annotation.Autowired; import java.util.ArrayList; +import java.util.Collection; import java.util.Date; import java.util.List; -import javax.annotation.Nonnull; -import javax.annotation.PostConstruct; +import java.util.stream.Collectors; import static ca.uhn.fhir.util.DatatypeUtil.toStringValue; import static org.apache.commons.lang3.StringUtils.isNotBlank; @@ -125,8 +129,33 @@ public class JpaResourceDaoCodeSystem extends BaseHapiF IBaseCoding theCoding, IPrimitiveType theDisplayLanguage, RequestDetails theRequestDetails) { + return lookupCode( + theCode, + theSystem, + theCoding, + theDisplayLanguage, + CollectionUtils.emptyCollection(), + theRequestDetails); + } + + @Nonnull + @Override + public IValidationSupport.LookupCodeResult lookupCode( + IPrimitiveType theCode, + IPrimitiveType theSystem, + IBaseCoding theCoding, + IPrimitiveType theDisplayLanguage, + Collection> thePropertyNames, + RequestDetails theRequestDetails) { return doLookupCode( - myFhirContext, myTerser, myValidationSupport, theCode, theSystem, theCoding, theDisplayLanguage); + myFhirContext, + myTerser, + myValidationSupport, + theCode, + theSystem, + theCoding, + theDisplayLanguage, + thePropertyNames); } @Override @@ -285,7 +314,8 @@ public class JpaResourceDaoCodeSystem extends BaseHapiF IPrimitiveType theCode, IPrimitiveType theSystem, IBaseCoding theCoding, - IPrimitiveType theDisplayLanguage) { + IPrimitiveType theDisplayLanguage, + Collection> thePropertyNames) { boolean haveCoding = theCoding != null && isNotBlank(extractCodingSystem(theCoding)) && isNotBlank(extractCodingCode(theCoding)); @@ -323,11 +353,16 @@ public class JpaResourceDaoCodeSystem extends BaseHapiF ourLog.info("Looking up {} / {}", system, code); + Collection propertyNames = CollectionUtils.emptyIfNull(thePropertyNames).stream() + .map(IPrimitiveType::getValueAsString) + .collect(Collectors.toSet()); + if (theValidationSupport.isCodeSystemSupported(new ValidationSupportContext(theValidationSupport), system)) { ourLog.info("Code system {} is supported", system); IValidationSupport.LookupCodeResult retVal = theValidationSupport.lookupCode( - new ValidationSupportContext(theValidationSupport), system, code, displayLanguage); + new ValidationSupportContext(theValidationSupport), + new LookupCodeRequest(system, code, displayLanguage, propertyNames)); if (retVal != null) { return retVal; } diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/JpaResourceDaoComposition.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/JpaResourceDaoComposition.java index 1ff023172a6..22b7da2f025 100644 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/JpaResourceDaoComposition.java +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/JpaResourceDaoComposition.java @@ -27,12 +27,12 @@ import ca.uhn.fhir.rest.api.server.IBundleProvider; import ca.uhn.fhir.rest.api.server.RequestDetails; import ca.uhn.fhir.rest.param.DateRangeParam; import ca.uhn.fhir.rest.param.StringParam; +import jakarta.servlet.http.HttpServletRequest; import org.hl7.fhir.instance.model.api.IBaseResource; import org.hl7.fhir.instance.model.api.IIdType; import org.hl7.fhir.instance.model.api.IPrimitiveType; import java.util.Collections; -import javax.servlet.http.HttpServletRequest; public class JpaResourceDaoComposition extends BaseHapiFhirResourceDao implements IFhirResourceDaoComposition { diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/JpaResourceDaoEncounter.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/JpaResourceDaoEncounter.java index 9c365a43b7d..6e35f2e0341 100644 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/JpaResourceDaoEncounter.java +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/JpaResourceDaoEncounter.java @@ -27,12 +27,12 @@ import ca.uhn.fhir.rest.api.SortSpec; import ca.uhn.fhir.rest.api.server.IBundleProvider; import ca.uhn.fhir.rest.param.DateRangeParam; import ca.uhn.fhir.rest.param.StringParam; +import jakarta.servlet.http.HttpServletRequest; import org.hl7.fhir.instance.model.api.IBaseResource; import org.hl7.fhir.instance.model.api.IIdType; import org.hl7.fhir.instance.model.api.IPrimitiveType; import java.util.Collections; -import javax.servlet.http.HttpServletRequest; public class JpaResourceDaoEncounter extends BaseHapiFhirResourceDao implements IFhirResourceDaoEncounter { diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/JpaResourceDaoObservation.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/JpaResourceDaoObservation.java index 6181c2dedfc..b81db1c490c 100644 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/JpaResourceDaoObservation.java +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/JpaResourceDaoObservation.java @@ -22,9 +22,7 @@ package ca.uhn.fhir.jpa.dao; import ca.uhn.fhir.i18n.Msg; import ca.uhn.fhir.interceptor.model.RequestPartitionId; import ca.uhn.fhir.jpa.api.dao.IFhirResourceDaoObservation; -import ca.uhn.fhir.jpa.model.cross.IBasePersistedResource; import ca.uhn.fhir.jpa.model.dao.JpaPid; -import ca.uhn.fhir.jpa.model.entity.ResourceTable; import ca.uhn.fhir.jpa.partition.IRequestPartitionHelperSvc; import ca.uhn.fhir.jpa.searchparam.SearchParameterMap; import ca.uhn.fhir.model.api.IQueryParameterType; @@ -34,22 +32,20 @@ import ca.uhn.fhir.rest.api.SortOrderEnum; import ca.uhn.fhir.rest.api.SortSpec; import ca.uhn.fhir.rest.api.server.IBundleProvider; import ca.uhn.fhir.rest.api.server.RequestDetails; -import ca.uhn.fhir.rest.api.server.storage.TransactionDetails; import ca.uhn.fhir.rest.param.ReferenceOrListParam; import ca.uhn.fhir.rest.param.ReferenceParam; +import jakarta.persistence.EntityManager; +import jakarta.persistence.PersistenceContext; +import jakarta.persistence.PersistenceContextType; +import jakarta.servlet.http.HttpServletResponse; import org.hl7.fhir.instance.model.api.IBaseResource; import org.hl7.fhir.r4.model.Observation; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.transaction.support.TransactionTemplate; import java.util.ArrayList; -import java.util.Date; import java.util.List; import java.util.TreeMap; -import javax.persistence.EntityManager; -import javax.persistence.PersistenceContext; -import javax.persistence.PersistenceContextType; -import javax.servlet.http.HttpServletResponse; public class JpaResourceDaoObservation extends BaseHapiFhirResourceDao implements IFhirResourceDaoObservation { @@ -57,9 +53,6 @@ public class JpaResourceDaoObservation extends BaseHapi @PersistenceContext(type = PersistenceContextType.TRANSACTION) protected EntityManager myEntityManager; - @Autowired - ObservationLastNIndexPersistSvc myObservationLastNIndexPersistSvc; - @Autowired private IRequestPartitionHelperSvc myRequestPartitionHelperService; @@ -98,64 +91,6 @@ public class JpaResourceDaoObservation extends BaseHapi return Observation.SP_PATIENT; } - @Override - public ResourceTable updateEntity( - RequestDetails theRequest, - IBaseResource theResource, - IBasePersistedResource theEntity, - Date theDeletedTimestampOrNull, - boolean thePerformIndexing, - boolean theUpdateVersion, - TransactionDetails theTransactionDetails, - boolean theForceUpdate, - boolean theCreateNewHistoryEntry) { - return updateObservationEntity( - theRequest, - theResource, - theEntity, - theDeletedTimestampOrNull, - thePerformIndexing, - theUpdateVersion, - theTransactionDetails, - theForceUpdate, - theCreateNewHistoryEntry); - } - - protected ResourceTable updateObservationEntity( - RequestDetails theRequest, - IBaseResource theResource, - IBasePersistedResource theEntity, - Date theDeletedTimestampOrNull, - boolean thePerformIndexing, - boolean theUpdateVersion, - TransactionDetails theTransactionDetails, - boolean theForceUpdate, - boolean theCreateNewHistoryEntry) { - ResourceTable retVal = super.updateEntity( - theRequest, - theResource, - theEntity, - theDeletedTimestampOrNull, - thePerformIndexing, - theUpdateVersion, - theTransactionDetails, - theForceUpdate, - theCreateNewHistoryEntry); - - if (getStorageSettings().isLastNEnabled()) { - if (!retVal.isUnchangedInCurrentOperation()) { - if (retVal.getDeleted() == null) { - // Update indexes here for LastN operation. - myObservationLastNIndexPersistSvc.indexObservation(theResource); - } else { - myObservationLastNIndexPersistSvc.deleteObservationIndex(theEntity); - } - } - } - - return retVal; - } - protected void updateSearchParamsForLastn( SearchParameterMap theSearchParameterMap, RequestDetails theRequestDetails) { if (!isPagingProviderDatabaseBacked(theRequestDetails)) { diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/JpaResourceDaoPatient.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/JpaResourceDaoPatient.java index 0a1a4540614..e94702fad0b 100644 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/JpaResourceDaoPatient.java +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/JpaResourceDaoPatient.java @@ -36,6 +36,7 @@ import ca.uhn.fhir.rest.param.DateRangeParam; import ca.uhn.fhir.rest.param.StringAndListParam; import ca.uhn.fhir.rest.param.TokenOrListParam; import ca.uhn.fhir.rest.param.TokenParam; +import jakarta.servlet.http.HttpServletRequest; import org.hl7.fhir.instance.model.api.IBaseResource; import org.hl7.fhir.instance.model.api.IIdType; import org.hl7.fhir.instance.model.api.IPrimitiveType; @@ -47,7 +48,6 @@ import org.springframework.transaction.annotation.Transactional; import java.util.Arrays; import java.util.Collections; -import javax.servlet.http.HttpServletRequest; public class JpaResourceDaoPatient extends BaseHapiFhirResourceDao implements IFhirResourceDaoPatient { diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/JpaStorageResourceParser.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/JpaStorageResourceParser.java index 74ed3f1b492..54d4f74ef4d 100644 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/JpaStorageResourceParser.java +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/JpaStorageResourceParser.java @@ -56,6 +56,7 @@ import ca.uhn.fhir.parser.LenientErrorHandler; import ca.uhn.fhir.rest.api.Constants; import ca.uhn.fhir.util.IMetaTagSorter; import ca.uhn.fhir.util.MetaUtil; +import jakarta.annotation.Nullable; import org.apache.commons.lang3.Validate; import org.hl7.fhir.instance.model.api.IAnyResource; import org.hl7.fhir.instance.model.api.IBaseCoding; @@ -71,7 +72,6 @@ import java.util.Collection; import java.util.Collections; import java.util.Date; import java.util.List; -import javax.annotation.Nullable; import static ca.uhn.fhir.jpa.dao.BaseHapiFhirDao.decodeResource; import static java.util.Objects.nonNull; diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/ObservationLastNIndexPersistSvc.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/ObservationLastNIndexPersistSvc.java deleted file mode 100644 index fa8b6fb3274..00000000000 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/ObservationLastNIndexPersistSvc.java +++ /dev/null @@ -1,260 +0,0 @@ -/*- - * #%L - * HAPI FHIR JPA Server - * %% - * Copyright (C) 2014 - 2023 Smile CDR, Inc. - * %% - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * #L% - */ -package ca.uhn.fhir.jpa.dao; - -import ca.uhn.fhir.context.FhirContext; -import ca.uhn.fhir.context.RuntimeSearchParam; -import ca.uhn.fhir.jpa.api.config.JpaStorageSettings; -import ca.uhn.fhir.jpa.model.cross.IBasePersistedResource; -import ca.uhn.fhir.jpa.model.entity.ResourceIndexedSearchParamToken; -import ca.uhn.fhir.jpa.model.util.CodeSystemHash; -import ca.uhn.fhir.jpa.search.lastn.IElasticsearchSvc; -import ca.uhn.fhir.jpa.search.lastn.json.CodeJson; -import ca.uhn.fhir.jpa.search.lastn.json.ObservationJson; -import ca.uhn.fhir.jpa.searchparam.extractor.ISearchParamExtractor; -import ca.uhn.fhir.jpa.searchparam.extractor.PathAndRef; -import ca.uhn.fhir.parser.IParser; -import org.hl7.fhir.instance.model.api.IBase; -import org.hl7.fhir.instance.model.api.IBaseResource; -import org.springframework.beans.factory.annotation.Autowired; - -import java.util.ArrayList; -import java.util.Date; -import java.util.List; -import java.util.Objects; -import java.util.Optional; -import java.util.UUID; - -public class ObservationLastNIndexPersistSvc { - - @Autowired - private ISearchParamExtractor mySearchParameterExtractor; - - @Autowired(required = false) - private IElasticsearchSvc myElasticsearchSvc; - - @Autowired - private JpaStorageSettings myConfig; - - @Autowired - private FhirContext myContext; - - public void indexObservation(IBaseResource theResource) { - - if (myElasticsearchSvc == null) { - // Elasticsearch is not enabled and therefore no index needs to be updated. - return; - } - - List subjectReferenceElement = - mySearchParameterExtractor.extractValues("Observation.subject", theResource); - String subjectId = subjectReferenceElement.stream() - .map(refElement -> - mySearchParameterExtractor.extractReferenceLinkFromResource(refElement, "Observation.subject")) - .filter(Objects::nonNull) - .map(PathAndRef::getRef) - .filter(Objects::nonNull) - .map(subjectRef -> subjectRef.getReferenceElement().getValue()) - .filter(Objects::nonNull) - .findFirst() - .orElse(null); - - Date effectiveDtm = null; - List effectiveDateElement = - mySearchParameterExtractor.extractValues("Observation.effective", theResource); - if (effectiveDateElement.size() > 0) { - effectiveDtm = mySearchParameterExtractor.extractDateFromResource( - effectiveDateElement.get(0), "Observation.effective"); - } - - List observationCodeCodeableConcepts = - mySearchParameterExtractor.extractValues("Observation.code", theResource); - - // Only index for lastn if Observation has a code - if (observationCodeCodeableConcepts.size() == 0) { - return; - } - - List observationCategoryCodeableConcepts = - mySearchParameterExtractor.extractValues("Observation.category", theResource); - - createOrUpdateIndexedObservation( - theResource, - effectiveDtm, - subjectId, - observationCodeCodeableConcepts, - observationCategoryCodeableConcepts); - } - - private void createOrUpdateIndexedObservation( - IBaseResource theResource, - Date theEffectiveDtm, - String theSubjectId, - List theObservationCodeCodeableConcepts, - List theObservationCategoryCodeableConcepts) { - String resourcePID = theResource.getIdElement().getIdPart(); - - // Determine if an index already exists for Observation: - ObservationJson indexedObservation = null; - if (resourcePID != null) { - indexedObservation = myElasticsearchSvc.getObservationDocument(resourcePID); - } - if (indexedObservation == null) { - indexedObservation = new ObservationJson(); - } - - indexedObservation.setEffectiveDtm(theEffectiveDtm); - indexedObservation.setIdentifier(resourcePID); - if (myConfig.isStoreResourceInHSearchIndex()) { - indexedObservation.setResource(encodeResource(theResource)); - } - indexedObservation.setSubject(theSubjectId); - - addCodeToObservationIndex(theObservationCodeCodeableConcepts, indexedObservation); - - addCategoriesToObservationIndex(theObservationCategoryCodeableConcepts, indexedObservation); - - myElasticsearchSvc.createOrUpdateObservationIndex(resourcePID, indexedObservation); - } - - private String encodeResource(IBaseResource theResource) { - IParser parser = myContext.newJsonParser(); - return parser.encodeResourceToString(theResource); - } - - private void addCodeToObservationIndex( - List theObservationCodeCodeableConcepts, ObservationJson theIndexedObservation) { - // Determine if a Normalized ID was created previously for Observation Code - String existingObservationCodeNormalizedId = - getCodeCodeableConceptId(theObservationCodeCodeableConcepts.get(0)); - - // Create/update normalized Observation Code index record - CodeJson codeableConceptField = - getCodeCodeableConcept(theObservationCodeCodeableConcepts.get(0), existingObservationCodeNormalizedId); - - myElasticsearchSvc.createOrUpdateObservationCodeIndex( - codeableConceptField.getCodeableConceptId(), codeableConceptField); - - theIndexedObservation.setCode(codeableConceptField); - } - - private void addCategoriesToObservationIndex( - List observationCategoryCodeableConcepts, ObservationJson indexedObservation) { - // Build CodeableConcept entities for Observation.Category - List categoryCodeableConceptEntities = new ArrayList<>(); - for (IBase categoryCodeableConcept : observationCategoryCodeableConcepts) { - // Build CodeableConcept entities for each category CodeableConcept - categoryCodeableConceptEntities.add(getCategoryCodeableConceptEntities(categoryCodeableConcept)); - } - indexedObservation.setCategories(categoryCodeableConceptEntities); - } - - private CodeJson getCategoryCodeableConceptEntities(IBase theValue) { - String text = mySearchParameterExtractor.getDisplayTextFromCodeableConcept(theValue); - CodeJson categoryCodeableConcept = new CodeJson(); - categoryCodeableConcept.setCodeableConceptText(text); - - List codings = mySearchParameterExtractor.getCodingsFromCodeableConcept(theValue); - for (IBase nextCoding : codings) { - addCategoryCoding(nextCoding, categoryCodeableConcept); - } - return categoryCodeableConcept; - } - - private CodeJson getCodeCodeableConcept(IBase theValue, String observationCodeNormalizedId) { - String text = mySearchParameterExtractor.getDisplayTextFromCodeableConcept(theValue); - CodeJson codeCodeableConcept = new CodeJson(); - codeCodeableConcept.setCodeableConceptText(text); - codeCodeableConcept.setCodeableConceptId(observationCodeNormalizedId); - - List codings = mySearchParameterExtractor.getCodingsFromCodeableConcept(theValue); - for (IBase nextCoding : codings) { - addCodeCoding(nextCoding, codeCodeableConcept); - } - - return codeCodeableConcept; - } - - private String getCodeCodeableConceptId(IBase theValue) { - List codings = mySearchParameterExtractor.getCodingsFromCodeableConcept(theValue); - Optional codeCodeableConceptIdOptional = Optional.empty(); - - for (IBase nextCoding : codings) { - ResourceIndexedSearchParamToken param = mySearchParameterExtractor.createSearchParamForCoding( - "Observation", - new RuntimeSearchParam(null, null, "code", null, null, null, null, null, null, null), - nextCoding); - if (param != null) { - String system = param.getSystem(); - String code = param.getValue(); - String text = mySearchParameterExtractor.getDisplayTextForCoding(nextCoding); - - String codeSystemHash = String.valueOf(CodeSystemHash.hashCodeSystem(system, code)); - CodeJson codeCodeableConceptDocument = - myElasticsearchSvc.getObservationCodeDocument(codeSystemHash, text); - if (codeCodeableConceptDocument != null) { - codeCodeableConceptIdOptional = Optional.of(codeCodeableConceptDocument.getCodeableConceptId()); - break; - } - } - } - - return codeCodeableConceptIdOptional.orElse(UUID.randomUUID().toString()); - } - - private void addCategoryCoding(IBase theValue, CodeJson theCategoryCodeableConcept) { - ResourceIndexedSearchParamToken param = mySearchParameterExtractor.createSearchParamForCoding( - "Observation", - new RuntimeSearchParam(null, null, "category", null, null, null, null, null, null, null), - theValue); - if (param != null) { - String system = param.getSystem(); - String code = param.getValue(); - String text = mySearchParameterExtractor.getDisplayTextForCoding(theValue); - theCategoryCodeableConcept.addCoding(system, code, text); - } - } - - private void addCodeCoding(IBase theValue, CodeJson theObservationCode) { - ResourceIndexedSearchParamToken param = mySearchParameterExtractor.createSearchParamForCoding( - "Observation", - new RuntimeSearchParam(null, null, "code", null, null, null, null, null, null, null), - theValue); - if (param != null) { - String system = param.getSystem(); - String code = param.getValue(); - String text = mySearchParameterExtractor.getDisplayTextForCoding(theValue); - theObservationCode.addCoding(system, code, text); - } - } - - public void deleteObservationIndex(IBasePersistedResource theEntity) { - if (myElasticsearchSvc == null) { - // Elasticsearch is not enabled and therefore no index needs to be updated. - return; - } - - ObservationJson deletedObservationLastNEntity = - myElasticsearchSvc.getObservationDocument(theEntity.getIdDt().getIdPart()); - if (deletedObservationLastNEntity != null) { - myElasticsearchSvc.deleteObservationDocument(deletedObservationLastNEntity.getIdentifier()); - } - } -} diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/TolerantJsonParser.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/TolerantJsonParser.java index 1ae25cae3fa..9be401ba68b 100644 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/TolerantJsonParser.java +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/TolerantJsonParser.java @@ -27,6 +27,7 @@ import ca.uhn.fhir.parser.JsonParser; import ca.uhn.fhir.parser.LenientErrorHandler; import com.google.gson.Gson; import com.google.gson.JsonObject; +import jakarta.annotation.Nullable; import org.hl7.fhir.instance.model.api.IBaseResource; import org.hl7.fhir.instance.model.api.IPrimitiveType; import org.slf4j.Logger; @@ -34,7 +35,6 @@ import org.slf4j.LoggerFactory; import java.math.BigDecimal; import java.util.Objects; -import javax.annotation.Nullable; import static org.apache.commons.lang3.StringUtils.defaultString; diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/TransactionProcessor.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/TransactionProcessor.java index 16d58ec0be6..1a25d6dae25 100644 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/TransactionProcessor.java +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/TransactionProcessor.java @@ -44,6 +44,17 @@ import ca.uhn.fhir.util.StopWatch; import com.google.common.annotations.VisibleForTesting; import com.google.common.collect.ArrayListMultimap; import com.google.common.collect.ListMultimap; +import jakarta.annotation.Nullable; +import jakarta.persistence.EntityManager; +import jakarta.persistence.PersistenceContext; +import jakarta.persistence.PersistenceContextType; +import jakarta.persistence.PersistenceException; +import jakarta.persistence.Tuple; +import jakarta.persistence.TypedQuery; +import jakarta.persistence.criteria.CriteriaBuilder; +import jakarta.persistence.criteria.CriteriaQuery; +import jakarta.persistence.criteria.Predicate; +import jakarta.persistence.criteria.Root; import org.apache.commons.lang3.Validate; import org.hibernate.internal.SessionImpl; import org.hl7.fhir.instance.model.api.IBase; @@ -64,17 +75,6 @@ import java.util.Map; import java.util.Set; import java.util.regex.Pattern; import java.util.stream.Collectors; -import javax.annotation.Nullable; -import javax.persistence.EntityManager; -import javax.persistence.PersistenceContext; -import javax.persistence.PersistenceContextType; -import javax.persistence.PersistenceException; -import javax.persistence.Tuple; -import javax.persistence.TypedQuery; -import javax.persistence.criteria.CriteriaBuilder; -import javax.persistence.criteria.CriteriaQuery; -import javax.persistence.criteria.Predicate; -import javax.persistence.criteria.Root; import static ca.uhn.fhir.util.UrlUtil.determineResourceTypeInResourceUrl; import static org.apache.commons.lang3.StringUtils.countMatches; @@ -367,29 +367,25 @@ public class TransactionProcessor extends BaseTransactionProcessor { CriteriaBuilder cb = myEntityManager.getCriteriaBuilder(); CriteriaQuery cq = cb.createTupleQuery(); Root from = cq.from(ResourceIndexedSearchParamToken.class); - cq.multiselect( - from.get("myResourcePid").as(Long.class), - from.get(theIndexColumnName).as(Long.class)); + cq.multiselect(from.get("myResourcePid"), from.get(theIndexColumnName)); Predicate masterPredicate; if (theHashesForIndexColumn.size() == 1) { masterPredicate = cb.equal( - from.get(theIndexColumnName).as(Long.class), + from.get(theIndexColumnName), theHashesForIndexColumn.iterator().next()); } else { - masterPredicate = from.get(theIndexColumnName).as(Long.class).in(theHashesForIndexColumn); + masterPredicate = from.get(theIndexColumnName).in(theHashesForIndexColumn); } if (myPartitionSettings.isPartitioningEnabled() && !myPartitionSettings.isIncludePartitionInSearchHashes()) { if (theRequestPartitionId.isDefaultPartition()) { - Predicate partitionIdCriteria = - cb.isNull(from.get("myPartitionIdValue").as(Integer.class)); + Predicate partitionIdCriteria = cb.isNull(from.get("myPartitionIdValue")); masterPredicate = cb.and(partitionIdCriteria, masterPredicate); } else if (!theRequestPartitionId.isAllPartitions()) { - Predicate partitionIdCriteria = from.get("myPartitionIdValue") - .as(Integer.class) - .in(theRequestPartitionId.getPartitionIds()); + Predicate partitionIdCriteria = + from.get("myPartitionIdValue").in(theRequestPartitionId.getPartitionIds()); masterPredicate = cb.and(partitionIdCriteria, masterPredicate); } } diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/data/IForcedIdDao.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/data/IForcedIdDao.java index bd4a80f64ed..3042da6b639 100644 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/data/IForcedIdDao.java +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/data/IForcedIdDao.java @@ -19,7 +19,6 @@ */ package ca.uhn.fhir.jpa.dao.data; -import ca.uhn.fhir.jpa.dao.data.custom.IForcedIdQueries; import ca.uhn.fhir.jpa.model.entity.ForcedId; import org.springframework.data.jpa.repository.JpaRepository; import org.springframework.data.jpa.repository.Modifying; @@ -27,17 +26,16 @@ import org.springframework.data.jpa.repository.Query; import org.springframework.data.repository.query.Param; import org.springframework.stereotype.Repository; -import java.util.List; -import java.util.Optional; - +/** + * Legacy forced_id implementation. + * + * @deprecated we now have a fhir_id column directly on HFJ_RESOURCE. + * No runtime code should query this table except for deletions by PK. + * To be deleted in 2024 (zero-downtime). + */ +@Deprecated(since = "6.10") @Repository -public interface IForcedIdDao extends JpaRepository, IHapiFhirJpaRepository, IForcedIdQueries { - - @Query("SELECT f FROM ForcedId f WHERE f.myResourcePid IN (:resource_pids)") - List findAllByResourcePid(@Param("resource_pids") List theResourcePids); - - @Query("SELECT f FROM ForcedId f WHERE f.myResourcePid = :resource_pid") - Optional findByResourcePid(@Param("resource_pid") Long theResourcePid); +public interface IForcedIdDao extends JpaRepository, IHapiFhirJpaRepository { @Modifying @Query("DELETE FROM ForcedId t WHERE t.myId = :pid") diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/data/IMdmLinkJpaMetricsRepository.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/data/IMdmLinkJpaMetricsRepository.java new file mode 100644 index 00000000000..95d8e9bff82 --- /dev/null +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/data/IMdmLinkJpaMetricsRepository.java @@ -0,0 +1,46 @@ +/*- + * #%L + * HAPI FHIR JPA Server + * %% + * Copyright (C) 2014 - 2023 Smile CDR, Inc. + * %% + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * #L% + */ +package ca.uhn.fhir.jpa.dao.data; + +import ca.uhn.fhir.jpa.entity.MdmLink; +import ca.uhn.fhir.mdm.api.MdmLinkSourceEnum; +import ca.uhn.fhir.mdm.api.MdmMatchResultEnum; +import org.springframework.data.jpa.repository.JpaRepository; +import org.springframework.data.jpa.repository.Query; +import org.springframework.data.repository.query.Param; +import org.springframework.stereotype.Repository; + +import java.util.List; + +@Repository("metricsRepository") +public interface IMdmLinkJpaMetricsRepository extends JpaRepository, IHapiFhirJpaRepository { + + @Query("SELECT ml.myMatchResult AS match_result, ml.myLinkSource AS link_source, count(*) AS c " + + "FROM MdmLink ml " + + "WHERE ml.myMdmSourceType = :resourceName " + + "AND ml.myLinkSource in (:linkSource) " + + "AND ml.myMatchResult in (:matchResult) " + + "GROUP BY match_result, link_source " + + "ORDER BY match_result") + Object[][] generateMetrics( + @Param("resourceName") String theResourceType, + @Param("linkSource") List theLinkSources, + @Param("matchResult") List theMatchTypes); +} diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/data/IMdmLinkJpaRepository.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/data/IMdmLinkJpaRepository.java index 363f2ae9b28..1962fa63176 100644 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/data/IMdmLinkJpaRepository.java +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/data/IMdmLinkJpaRepository.java @@ -53,7 +53,7 @@ public interface IMdmLinkJpaRepository @Modifying @Query( value = - "DELETE FROM MPI_LINK_AUD f WHERE GOLDEN_RESOURCE_PID IN (:goldenPids) OR TARGET_PID IN (:goldenPids)", + "DELETE FROM MPI_LINK_AUD WHERE GOLDEN_RESOURCE_PID IN (:goldenPids) OR TARGET_PID IN (:goldenPids)", nativeQuery = true) void deleteLinksHistoryWithAnyReferenceToPids(@Param("goldenPids") List theResourcePids); diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/data/INpmPackageDao.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/data/INpmPackageDao.java index 6df63c7d233..d9d9716161f 100644 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/data/INpmPackageDao.java +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/data/INpmPackageDao.java @@ -28,6 +28,6 @@ import java.util.Optional; public interface INpmPackageDao extends JpaRepository, IHapiFhirJpaRepository { - @Query("SELECT p FROM NpmPackageEntity p WHERE p.myPackageId = :id") + @Query("SELECT p FROM NpmPackageEntity p WHERE lower(p.myPackageId) = lower(:id)") Optional findByPackageId(@Param("id") String thePackageId); } diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/data/INpmPackageVersionDao.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/data/INpmPackageVersionDao.java index 26d4076401e..3a27873e811 100644 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/data/INpmPackageVersionDao.java +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/data/INpmPackageVersionDao.java @@ -30,10 +30,11 @@ import java.util.Optional; public interface INpmPackageVersionDao extends JpaRepository, IHapiFhirJpaRepository { - @Query("SELECT p FROM NpmPackageVersionEntity p WHERE p.myPackageId = :id") + @Query("SELECT p FROM NpmPackageVersionEntity p WHERE lower(p.myPackageId) = lower(:id)") Collection findByPackageId(@Param("id") String thePackageId); - @Query("SELECT p FROM NpmPackageVersionEntity p WHERE p.myPackageId = :id AND p.myVersionId = :version") + @Query( + "SELECT p FROM NpmPackageVersionEntity p WHERE lower(p.myPackageId) = lower(:id) AND p.myVersionId = :version") Optional findByPackageIdAndVersion( @Param("id") String thePackageId, @Param("version") String thePackageVersion); @@ -41,7 +42,7 @@ public interface INpmPackageVersionDao extends JpaRepository findVersionIdsByPackageIdAndLikeVersion( @Param("id") String theId, @Param("version") String thePartialVersionString); } diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/data/IResourceHistoryTableDao.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/data/IResourceHistoryTableDao.java index bd65315bf30..00eeccb7345 100644 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/data/IResourceHistoryTableDao.java +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/data/IResourceHistoryTableDao.java @@ -44,30 +44,30 @@ public interface IResourceHistoryTableDao extends JpaRepository :dontWantVersion") Slice findForResourceId( Pageable thePage, @Param("resId") Long theId, @Param("dontWantVersion") Long theDontWantVersion); @Query( - "SELECT t FROM ResourceHistoryTable t LEFT OUTER JOIN FETCH t.myProvenance WHERE t.myResourceId = :resId AND t.myResourceVersion != :dontWantVersion") + "SELECT t FROM ResourceHistoryTable t LEFT OUTER JOIN FETCH t.myProvenance WHERE t.myResourceId = :resId AND t.myResourceVersion <> :dontWantVersion") Slice findForResourceIdAndReturnEntitiesAndFetchProvenance( Pageable thePage, @Param("resId") Long theId, @Param("dontWantVersion") Long theDontWantVersion); @Query("" + "SELECT v.myId FROM ResourceHistoryTable v " + "LEFT OUTER JOIN ResourceTable t ON (v.myResourceId = t.myId) " - + "WHERE v.myResourceVersion != t.myVersion AND " + + "WHERE v.myResourceVersion <> t.myVersion AND " + "t.myId = :resId") Slice findIdsOfPreviousVersionsOfResourceId(Pageable thePage, @Param("resId") Long theResourceId); @Query("" + "SELECT v.myId FROM ResourceHistoryTable v " + "LEFT OUTER JOIN ResourceTable t ON (v.myResourceId = t.myId) " - + "WHERE v.myResourceVersion != t.myVersion AND " + + "WHERE v.myResourceVersion <> t.myVersion AND " + "t.myResourceType = :restype") Slice findIdsOfPreviousVersionsOfResources(Pageable thePage, @Param("restype") String theResourceName); @Query("" + "SELECT v.myId FROM ResourceHistoryTable v " + "LEFT OUTER JOIN ResourceTable t ON (v.myResourceId = t.myId) " - + "WHERE v.myResourceVersion != t.myVersion") + + "WHERE v.myResourceVersion <> t.myVersion") Slice findIdsOfPreviousVersionsOfResources(Pageable thePage); @Modifying @@ -79,4 +79,16 @@ public interface IResourceHistoryTableDao extends JpaRepositoryRES_TEXT_VC column to the legacy RES_TEXT column, which is where data may have + * been stored by versions of HAPI FHIR prior to 7.0.0 + * + * @since 7.0.0 + */ + @Modifying + @Query( + "UPDATE ResourceHistoryTable r SET r.myResourceTextVc = null, r.myResource = :text, r.myEncoding = 'JSONC' WHERE r.myId = :pid") + void updateNonInlinedContents(@Param("text") byte[] theText, @Param("pid") long thePid); } diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/data/IResourceTableDao.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/data/IResourceTableDao.java index 3696314341e..c1263047940 100644 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/data/IResourceTableDao.java +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/data/IResourceTableDao.java @@ -19,6 +19,7 @@ */ package ca.uhn.fhir.jpa.dao.data; +import ca.uhn.fhir.jpa.dao.data.custom.IForcedIdQueries; import ca.uhn.fhir.jpa.model.entity.ResourceTable; import org.springframework.data.domain.Pageable; import org.springframework.data.domain.Slice; @@ -34,9 +35,11 @@ import java.util.Date; import java.util.List; import java.util.Map; import java.util.Optional; +import java.util.stream.Stream; @Transactional(propagation = Propagation.MANDATORY) -public interface IResourceTableDao extends JpaRepository, IHapiFhirJpaRepository { +public interface IResourceTableDao + extends JpaRepository, IHapiFhirJpaRepository, IForcedIdQueries { @Query("SELECT t.myId FROM ResourceTable t WHERE t.myDeleted IS NOT NULL") Slice findIdsOfDeletedResources(Pageable thePageable); @@ -63,13 +66,10 @@ public interface IResourceTableDao extends JpaRepository, I Slice findIdsOfResourcesWithinUpdatedRangeOrderedFromOldest( Pageable thePage, @Param("low") Date theLow, @Param("high") Date theHigh); - /** - * @return List of arrays containing [PID, resourceType, lastUpdated] - */ @Query( "SELECT t.myId, t.myResourceType, t.myUpdated FROM ResourceTable t WHERE t.myUpdated >= :low AND t.myUpdated <= :high ORDER BY t.myUpdated ASC") - Slice findIdsTypesAndUpdateTimesOfResourcesWithinUpdatedRangeOrderedFromOldest( - Pageable thePage, @Param("low") Date theLow, @Param("high") Date theHigh); + Stream streamIdsTypesAndUpdateTimesOfResourcesWithinUpdatedRangeOrderedFromOldest( + @Param("low") Date theLow, @Param("high") Date theHigh); /** * @return List of arrays containing [PID, resourceType, lastUpdated] @@ -82,6 +82,13 @@ public interface IResourceTableDao extends JpaRepository, I @Param("high") Date theHigh, @Param("partition_ids") List theRequestPartitionIds); + @Query( + "SELECT t.myId, t.myResourceType, t.myUpdated FROM ResourceTable t WHERE t.myUpdated >= :low AND t.myUpdated <= :high AND t.myPartitionIdValue IN (:partition_ids) ORDER BY t.myUpdated ASC") + Stream streamIdsTypesAndUpdateTimesOfResourcesWithinUpdatedRangeOrderedFromOldestForPartitionIds( + @Param("low") Date theLow, + @Param("high") Date theHigh, + @Param("partition_ids") List theRequestPartitionIds); + /** * @return List of arrays containing [PID, resourceType, lastUpdated] */ @@ -90,6 +97,11 @@ public interface IResourceTableDao extends JpaRepository, I Slice findIdsTypesAndUpdateTimesOfResourcesWithinUpdatedRangeOrderedFromOldestForDefaultPartition( Pageable thePage, @Param("low") Date theLow, @Param("high") Date theHigh); + @Query( + "SELECT t.myId, t.myResourceType, t.myUpdated FROM ResourceTable t WHERE t.myUpdated >= :low AND t.myUpdated <= :high ORDER BY t.myUpdated ASC") + Stream streamIdsTypesAndUpdateTimesOfResourcesWithinUpdatedRangeOrderedFromOldestForDefaultPartition( + @Param("low") Date theLow, @Param("high") Date theHigh); + // TODO in the future, consider sorting by pid as well so batch jobs process in the same order across restarts @Query( "SELECT t.myId FROM ResourceTable t WHERE t.myUpdated >= :low AND t.myUpdated <= :high AND t.myPartitionIdValue = :partition_id ORDER BY t.myUpdated ASC") diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/data/ISearchDao.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/data/ISearchDao.java index 6a9fa7fd5ab..35bb509b69a 100644 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/data/ISearchDao.java +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/data/ISearchDao.java @@ -20,8 +20,7 @@ package ca.uhn.fhir.jpa.dao.data; import ca.uhn.fhir.jpa.entity.Search; -import org.springframework.data.domain.Pageable; -import org.springframework.data.domain.Slice; +import com.google.errorprone.annotations.CanIgnoreReturnValue; import org.springframework.data.jpa.repository.JpaRepository; import org.springframework.data.jpa.repository.Modifying; import org.springframework.data.jpa.repository.Query; @@ -30,6 +29,8 @@ import org.springframework.data.repository.query.Param; import java.util.Collection; import java.util.Date; import java.util.Optional; +import java.util.Set; +import java.util.stream.Stream; public interface ISearchDao extends JpaRepository, IHapiFhirJpaRepository { @@ -38,10 +39,12 @@ public interface ISearchDao extends JpaRepository, IHapiFhirJpaRep @Query( "SELECT s.myId FROM Search s WHERE (s.myCreated < :cutoff) AND (s.myExpiryOrNull IS NULL OR s.myExpiryOrNull < :now) AND (s.myDeleted IS NULL OR s.myDeleted = FALSE)") - Slice findWhereCreatedBefore(@Param("cutoff") Date theCutoff, @Param("now") Date theNow, Pageable thePage); + Stream findWhereCreatedBefore(@Param("cutoff") Date theCutoff, @Param("now") Date theNow); - @Query("SELECT s.myId FROM Search s WHERE s.myDeleted = TRUE") - Slice findDeleted(Pageable thePage); + @Query("SELECT new ca.uhn.fhir.jpa.dao.data.SearchIdAndResultSize(" + "s.myId, " + + "(select max(sr.myOrder) as maxOrder from SearchResult sr where sr.mySearchPid = s.myId)) " + + "FROM Search s WHERE s.myDeleted = TRUE") + Stream findDeleted(); @Query( "SELECT s FROM Search s WHERE s.myResourceType = :type AND s.mySearchQueryStringHash = :hash AND (s.myCreated > :cutoff) AND s.myDeleted = FALSE AND s.myStatus <> 'FAILED'") @@ -54,10 +57,15 @@ public interface ISearchDao extends JpaRepository, IHapiFhirJpaRep int countDeleted(); @Modifying - @Query("UPDATE Search s SET s.myDeleted = :deleted WHERE s.myId = :pid") - void updateDeleted(@Param("pid") Long thePid, @Param("deleted") boolean theDeleted); + @Query("UPDATE Search s SET s.myDeleted = :deleted WHERE s.myId in (:pids)") + @CanIgnoreReturnValue + int updateDeleted(@Param("pids") Set thePid, @Param("deleted") boolean theDeleted); @Modifying @Query("DELETE FROM Search s WHERE s.myId = :pid") void deleteByPid(@Param("pid") Long theId); + + @Modifying + @Query("DELETE FROM Search s WHERE s.myId in (:pids)") + void deleteByPids(@Param("pids") Collection theSearchToDelete); } diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/data/ISearchIncludeDao.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/data/ISearchIncludeDao.java index 9312d300f0a..776b8a94faf 100644 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/data/ISearchIncludeDao.java +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/data/ISearchIncludeDao.java @@ -20,14 +20,18 @@ package ca.uhn.fhir.jpa.dao.data; import ca.uhn.fhir.jpa.entity.SearchInclude; +import com.google.errorprone.annotations.CanIgnoreReturnValue; import org.springframework.data.jpa.repository.JpaRepository; import org.springframework.data.jpa.repository.Modifying; import org.springframework.data.jpa.repository.Query; import org.springframework.data.repository.query.Param; +import java.util.Collection; + public interface ISearchIncludeDao extends JpaRepository, IHapiFhirJpaRepository { @Modifying - @Query(value = "DELETE FROM SearchInclude r WHERE r.mySearchPid = :search") - void deleteForSearch(@Param("search") Long theSearchPid); + @Query(value = "DELETE FROM SearchInclude r WHERE r.mySearchPid in (:search)") + @CanIgnoreReturnValue + int deleteForSearch(@Param("search") Collection theSearchPid); } diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/data/ISearchResultDao.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/data/ISearchResultDao.java index b16a1d99dbf..eb6a4f89474 100644 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/data/ISearchResultDao.java +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/data/ISearchResultDao.java @@ -20,6 +20,7 @@ package ca.uhn.fhir.jpa.dao.data; import ca.uhn.fhir.jpa.entity.SearchResult; +import com.google.errorprone.annotations.CanIgnoreReturnValue; import org.springframework.data.domain.Pageable; import org.springframework.data.domain.Slice; import org.springframework.data.jpa.repository.JpaRepository; @@ -27,6 +28,7 @@ import org.springframework.data.jpa.repository.Modifying; import org.springframework.data.jpa.repository.Query; import org.springframework.data.repository.query.Param; +import java.util.Collection; import java.util.List; public interface ISearchResultDao extends JpaRepository, IHapiFhirJpaRepository { @@ -37,12 +39,19 @@ public interface ISearchResultDao extends JpaRepository, IHa @Query(value = "SELECT r.myResourcePid FROM SearchResult r WHERE r.mySearchPid = :search") List findWithSearchPidOrderIndependent(@Param("search") Long theSearchPid); - @Query(value = "SELECT r.myId FROM SearchResult r WHERE r.mySearchPid = :search") - Slice findForSearch(Pageable thePage, @Param("search") Long theSearchPid); + @Modifying + @Query("DELETE FROM SearchResult s WHERE s.mySearchPid IN :searchIds") + @CanIgnoreReturnValue + int deleteBySearchIds(@Param("searchIds") Collection theSearchIds); @Modifying - @Query("DELETE FROM SearchResult s WHERE s.myId IN :ids") - void deleteByIds(@Param("ids") List theContent); + @Query( + "DELETE FROM SearchResult s WHERE s.mySearchPid = :searchId and s.myOrder >= :rangeStart and s.myOrder <= :rangeEnd") + @CanIgnoreReturnValue + int deleteBySearchIdInRange( + @Param("searchId") Long theSearchId, + @Param("rangeStart") int theRangeStart, + @Param("rangeEnd") int theRangeEnd); @Query("SELECT count(r) FROM SearchResult r WHERE r.mySearchPid = :search") int countForSearch(@Param("search") Long theSearchPid); diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/data/SearchIdAndResultSize.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/data/SearchIdAndResultSize.java new file mode 100644 index 00000000000..8c6d822de7f --- /dev/null +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/data/SearchIdAndResultSize.java @@ -0,0 +1,37 @@ +/*- + * #%L + * HAPI FHIR JPA Server + * %% + * Copyright (C) 2014 - 2023 Smile CDR, Inc. + * %% + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * #L% + */ +package ca.uhn.fhir.jpa.dao.data; + +import java.util.Objects; + +/** + * Record for search result returning the PK of a Search, and the number of associated SearchResults + */ +public class SearchIdAndResultSize { + /** Search PK */ + public final long searchId; + /** Number of SearchResults attached */ + public final int size; + + public SearchIdAndResultSize(long theSearchId, Integer theSize) { + searchId = theSearchId; + size = Objects.requireNonNullElse(theSize, 0); + } +} diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/data/custom/IForcedIdDaoImpl.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/data/custom/IResourceTableDaoImpl.java similarity index 74% rename from hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/data/custom/IForcedIdDaoImpl.java rename to hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/data/custom/IResourceTableDaoImpl.java index 9f271a15df2..208e8c78d1e 100644 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/data/custom/IForcedIdDaoImpl.java +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/data/custom/IResourceTableDaoImpl.java @@ -19,17 +19,20 @@ */ package ca.uhn.fhir.jpa.dao.data.custom; +import jakarta.persistence.EntityManager; +import jakarta.persistence.PersistenceContext; import org.springframework.stereotype.Component; import java.util.Collection; import java.util.List; -import javax.persistence.EntityManager; -import javax.persistence.PersistenceContext; @Component -// Don't change the name of this class. Spring Data requires the name to match. -// See https://stackoverflow.com/questions/11880924/how-to-add-custom-method-to-spring-data-jpa -public class IForcedIdDaoImpl implements IForcedIdQueries { +/** + * Custom query implementations. + * Don't change the name of this class. Spring Data requires the name to match. + * https://stackoverflow.com/questions/11880924/how-to-add-custom-method-to-spring-data-jpa + */ +public class IResourceTableDaoImpl implements IForcedIdQueries { @PersistenceContext private EntityManager myEntityManager; @@ -51,11 +54,9 @@ public class IForcedIdDaoImpl implements IForcedIdQueries { */ public Collection findAndResolveByForcedIdWithNoType( String theResourceType, Collection theForcedIds, boolean theExcludeDeleted) { - String query = "" + "SELECT " - + " f.myResourceType, f.myResourcePid, f.myForcedId, t.myDeleted " - + "FROM ForcedId f " - + "JOIN ResourceTable t ON t.myId = f.myResourcePid " - + "WHERE f.myResourceType = :resource_type AND f.myForcedId IN ( :forced_id )"; + String query = "SELECT t.myResourceType, t.myId, t.myFhirId, t.myDeleted " + + "FROM ResourceTable t " + + "WHERE t.myResourceType = :resource_type AND t.myFhirId IN ( :forced_id )"; if (theExcludeDeleted) { query += " AND t.myDeleted IS NULL"; @@ -78,11 +79,9 @@ public class IForcedIdDaoImpl implements IForcedIdQueries { Collection theForcedIds, Collection thePartitionId, boolean theExcludeDeleted) { - String query = "" + "SELECT " - + " f.myResourceType, f.myResourcePid, f.myForcedId, t.myDeleted " - + "FROM ForcedId f " - + "JOIN ResourceTable t ON t.myId = f.myResourcePid " - + "WHERE f.myResourceType = :resource_type AND f.myForcedId IN ( :forced_id ) AND f.myPartitionIdValue IN ( :partition_id )"; + String query = "SELECT t.myResourceType, t.myId, t.myFhirId, t.myDeleted " + + "FROM ResourceTable t " + + "WHERE t.myResourceType = :resource_type AND t.myFhirId IN ( :forced_id ) AND t.myPartitionIdValue IN ( :partition_id )"; if (theExcludeDeleted) { query += " AND t.myDeleted IS NULL"; @@ -103,11 +102,9 @@ public class IForcedIdDaoImpl implements IForcedIdQueries { */ public Collection findAndResolveByForcedIdWithNoTypeInPartitionNull( String theResourceType, Collection theForcedIds, boolean theExcludeDeleted) { - String query = "" + "SELECT " - + " f.myResourceType, f.myResourcePid, f.myForcedId, t.myDeleted " - + "FROM ForcedId f " - + "JOIN ResourceTable t ON t.myId = f.myResourcePid " - + "WHERE f.myResourceType = :resource_type AND f.myForcedId IN ( :forced_id ) AND f.myPartitionIdValue IS NULL"; + String query = "SELECT t.myResourceType, t.myId, t.myFhirId, t.myDeleted " + + "FROM ResourceTable t " + + "WHERE t.myResourceType = :resource_type AND t.myFhirId IN ( :forced_id ) AND t.myPartitionIdValue IS NULL"; if (theExcludeDeleted) { query += " AND t.myDeleted IS NULL"; @@ -130,11 +127,9 @@ public class IForcedIdDaoImpl implements IForcedIdQueries { Collection theForcedIds, List thePartitionIdsWithoutDefault, boolean theExcludeDeleted) { - String query = "" + "SELECT " - + " f.myResourceType, f.myResourcePid, f.myForcedId, t.myDeleted " - + "FROM ForcedId f " - + "JOIN ResourceTable t ON t.myId = f.myResourcePid " - + "WHERE f.myResourceType = :resource_type AND f.myForcedId IN ( :forced_id ) AND (f.myPartitionIdValue IS NULL OR f.myPartitionIdValue IN ( :partition_id ))"; + String query = "SELECT t.myResourceType, t.myId, t.myFhirId, t.myDeleted " + + "FROM ResourceTable t " + + "WHERE t.myResourceType = :resource_type AND t.myFhirId IN ( :forced_id ) AND (t.myPartitionIdValue IS NULL OR t.myPartitionIdValue IN ( :partition_id ))"; if (theExcludeDeleted) { query += " AND t.myDeleted IS NULL"; diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/dstu3/FhirSystemDaoDstu3.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/dstu3/FhirSystemDaoDstu3.java index ced37678fdf..2865edc0c9b 100644 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/dstu3/FhirSystemDaoDstu3.java +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/dstu3/FhirSystemDaoDstu3.java @@ -23,13 +23,13 @@ import ca.uhn.fhir.jpa.dao.BaseHapiFhirSystemDao; import ca.uhn.fhir.jpa.dao.JpaResourceDao; import ca.uhn.fhir.jpa.model.entity.TagDefinition; import ca.uhn.fhir.rest.api.server.RequestDetails; +import jakarta.persistence.TypedQuery; import org.hl7.fhir.dstu3.model.Bundle; import org.hl7.fhir.dstu3.model.Meta; import org.hl7.fhir.instance.model.api.IBaseBundle; import java.util.Collection; import java.util.List; -import javax.persistence.TypedQuery; public class FhirSystemDaoDstu3 extends BaseHapiFhirSystemDao { diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/expunge/ExpungeEverythingService.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/expunge/ExpungeEverythingService.java index 34f247edce7..9962eed3ef3 100644 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/expunge/ExpungeEverythingService.java +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/expunge/ExpungeEverythingService.java @@ -78,6 +78,17 @@ import ca.uhn.fhir.rest.server.provider.ProviderConstants; import ca.uhn.fhir.rest.server.servlet.ServletRequestDetails; import ca.uhn.fhir.rest.server.util.CompositeInterceptorBroadcaster; import ca.uhn.fhir.util.StopWatch; +import jakarta.annotation.Nullable; +import jakarta.persistence.EntityManager; +import jakarta.persistence.PersistenceContext; +import jakarta.persistence.PersistenceContextType; +import jakarta.persistence.Query; +import jakarta.persistence.TypedQuery; +import jakarta.persistence.criteria.CriteriaBuilder; +import jakarta.persistence.criteria.CriteriaQuery; +import jakarta.persistence.metamodel.EntityType; +import jakarta.persistence.metamodel.Metamodel; +import jakarta.persistence.metamodel.SingularAttribute; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; @@ -85,14 +96,8 @@ import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Propagation; import java.util.List; +import java.util.Set; import java.util.concurrent.atomic.AtomicInteger; -import javax.annotation.Nullable; -import javax.persistence.EntityManager; -import javax.persistence.PersistenceContext; -import javax.persistence.PersistenceContextType; -import javax.persistence.TypedQuery; -import javax.persistence.criteria.CriteriaBuilder; -import javax.persistence.criteria.CriteriaQuery; @Service public class ExpungeEverythingService implements IExpungeEverythingService { @@ -135,6 +140,18 @@ public class ExpungeEverythingService implements IExpungeEverythingService { RequestPartitionId requestPartitionId = myRequestPartitionHelperSvc.determineReadPartitionForRequest(theRequest, details); + deleteAll(theRequest, propagation, requestPartitionId, counter); + + purgeAllCaches(); + + ourLog.info("COMPLETED GLOBAL $expunge - Deleted {} rows", counter.get()); + } + + protected void deleteAll( + @Nullable RequestDetails theRequest, + Propagation propagation, + RequestPartitionId requestPartitionId, + AtomicInteger counter) { myTxService .withRequest(theRequest) .withPropagation(propagation) @@ -229,6 +246,7 @@ public class ExpungeEverythingService implements IExpungeEverythingService { expungeEverythingByTypeWithoutPurging(theRequest, ResourceHistoryTable.class, requestPartitionId)); counter.addAndGet( expungeEverythingByTypeWithoutPurging(theRequest, ResourceSearchUrlEntity.class, requestPartitionId)); + int counterBefore = counter.get(); counter.addAndGet(expungeEverythingByTypeWithoutPurging(theRequest, ResourceTable.class, requestPartitionId)); counter.addAndGet(expungeEverythingByTypeWithoutPurging(theRequest, PartitionEntity.class, requestPartitionId)); @@ -242,10 +260,6 @@ public class ExpungeEverythingService implements IExpungeEverythingService { .execute(() -> { counter.addAndGet(doExpungeEverythingQuery("DELETE from " + Search.class.getSimpleName() + " d")); }); - - purgeAllCaches(); - - ourLog.info("COMPLETED GLOBAL $expunge - Deleted {} rows", counter.get()); } @Override @@ -257,8 +271,10 @@ public class ExpungeEverythingService implements IExpungeEverythingService { myMemoryCacheService.invalidateAllCaches(); } - private int expungeEverythingByTypeWithoutPurging( - RequestDetails theRequest, Class theEntityType, RequestPartitionId theRequestPartitionId) { + protected int expungeEverythingByTypeWithoutPurging( + RequestDetails theRequest, Class theEntityType, RequestPartitionId theRequestPartitionId) { + HapiTransactionService.noTransactionAllowed(); + int outcome = 0; while (true) { StopWatch sw = new StopWatch(); @@ -268,16 +284,49 @@ public class ExpungeEverythingService implements IExpungeEverythingService { .withPropagation(Propagation.REQUIRES_NEW) .withRequestPartitionId(theRequestPartitionId) .execute(() -> { - CriteriaBuilder cb = myEntityManager.getCriteriaBuilder(); - CriteriaQuery cq = cb.createQuery(theEntityType); - cq.from(theEntityType); - TypedQuery query = myEntityManager.createQuery(cq); - query.setMaxResults(1000); - List results = query.getResultList(); - for (Object result : results) { - myEntityManager.remove(result); + + /* + * This method uses a nice efficient mechanism where we figure out the PID datatype + * and load only the PIDs and delete by PID for all resource types except ResourceTable. + * We delete ResourceTable using the entitymanager so that Hibernate Search knows to + * delete the corresponding records it manages in ElasticSearch. See + * FhirResourceDaoR4SearchWithElasticSearchIT for a test that fails without the + * block below. + */ + if (ResourceTable.class.equals(theEntityType)) { + CriteriaBuilder cb = myEntityManager.getCriteriaBuilder(); + CriteriaQuery cq = cb.createQuery(theEntityType); + cq.from(theEntityType); + TypedQuery query = myEntityManager.createQuery(cq); + query.setMaxResults(800); + List results = query.getResultList(); + for (Object result : results) { + myEntityManager.remove(result); + } + return results.size(); } - return results.size(); + + Metamodel metamodel = myEntityManager.getMetamodel(); + EntityType entity = metamodel.entity(theEntityType); + Set> singularAttributes = entity.getSingularAttributes(); + String idProperty = null; + for (SingularAttribute singularAttribute : singularAttributes) { + if (singularAttribute.isId()) { + idProperty = singularAttribute.getName(); + break; + } + } + + Query nativeQuery = myEntityManager.createQuery( + "SELECT " + idProperty + " FROM " + theEntityType.getSimpleName()); + nativeQuery.setMaxResults(800); + List pids = nativeQuery.getResultList(); + + nativeQuery = myEntityManager.createQuery("DELETE FROM " + theEntityType.getSimpleName() + + " WHERE " + idProperty + " IN (:pids)"); + nativeQuery.setParameter("pids", pids); + nativeQuery.executeUpdate(); + return pids.size(); }); outcome += count; diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/expunge/ResourceTableFKProvider.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/expunge/ResourceTableFKProvider.java index fe5c2d80804..22551b6e9a1 100644 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/expunge/ResourceTableFKProvider.java +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/expunge/ResourceTableFKProvider.java @@ -20,12 +20,12 @@ package ca.uhn.fhir.jpa.dao.expunge; import ca.uhn.fhir.mdm.api.IMdmSettings; +import jakarta.annotation.Nonnull; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; import java.util.ArrayList; import java.util.List; -import javax.annotation.Nonnull; @Service public class ResourceTableFKProvider { diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/index/DaoSearchParamSynchronizer.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/index/DaoSearchParamSynchronizer.java index 458f5a428d5..fb6dda60d86 100644 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/index/DaoSearchParamSynchronizer.java +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/index/DaoSearchParamSynchronizer.java @@ -24,18 +24,22 @@ import ca.uhn.fhir.jpa.model.entity.ResourceTable; import ca.uhn.fhir.jpa.searchparam.extractor.ResourceIndexedSearchParams; import ca.uhn.fhir.jpa.util.AddRemoveCount; import com.google.common.annotations.VisibleForTesting; +import jakarta.persistence.EntityManager; +import jakarta.persistence.PersistenceContext; +import jakarta.persistence.PersistenceContextType; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; import org.springframework.stereotype.Service; import java.util.ArrayList; import java.util.Collection; import java.util.HashSet; import java.util.List; -import javax.persistence.EntityManager; -import javax.persistence.PersistenceContext; -import javax.persistence.PersistenceContextType; @Service public class DaoSearchParamSynchronizer { + private static final Logger ourLog = LoggerFactory.getLogger(DaoSearchParamSynchronizer.class); + @PersistenceContext(type = PersistenceContextType.TRANSACTION) protected EntityManager myEntityManager; @@ -89,9 +93,13 @@ public class DaoSearchParamSynchronizer { tryToReuseIndexEntities(paramsToRemove, paramsToAdd); for (T next : paramsToRemove) { + if (!myEntityManager.contains(next)) { + // If a resource is created and deleted in the same transaction, we can end up + // in a state where we're deleting entities that don't actually exist. Hibernate + // 6 is stricter about this, so we skip here. + continue; + } myEntityManager.remove(next); - theEntity.getParamsQuantity().remove(next); - theEntity.getParamsQuantityNormalized().remove(next); } for (T next : paramsToAdd) { myEntityManager.merge(next); diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/index/IdHelperService.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/index/IdHelperService.java index 3199edcdbaa..2978764db68 100644 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/index/IdHelperService.java +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/index/IdHelperService.java @@ -25,26 +25,34 @@ import ca.uhn.fhir.interceptor.model.RequestPartitionId; import ca.uhn.fhir.jpa.api.config.JpaStorageSettings; import ca.uhn.fhir.jpa.api.model.PersistentIdToForcedIdMap; import ca.uhn.fhir.jpa.api.svc.IIdHelperService; -import ca.uhn.fhir.jpa.dao.data.IForcedIdDao; import ca.uhn.fhir.jpa.dao.data.IResourceTableDao; import ca.uhn.fhir.jpa.model.config.PartitionSettings; import ca.uhn.fhir.jpa.model.cross.IResourceLookup; import ca.uhn.fhir.jpa.model.cross.JpaResourceLookup; import ca.uhn.fhir.jpa.model.dao.JpaPid; -import ca.uhn.fhir.jpa.model.entity.ForcedId; import ca.uhn.fhir.jpa.model.entity.ResourceTable; import ca.uhn.fhir.jpa.search.builder.SearchBuilder; import ca.uhn.fhir.jpa.util.MemoryCacheService; import ca.uhn.fhir.jpa.util.QueryChunker; import ca.uhn.fhir.model.primitive.IdDt; import ca.uhn.fhir.rest.api.server.storage.BaseResourcePersistentId; -import ca.uhn.fhir.rest.api.server.storage.IResourcePersistentId; import ca.uhn.fhir.rest.server.exceptions.InvalidRequestException; import ca.uhn.fhir.rest.server.exceptions.PreconditionFailedException; import ca.uhn.fhir.rest.server.exceptions.ResourceNotFoundException; import com.google.common.annotations.VisibleForTesting; import com.google.common.collect.ListMultimap; import com.google.common.collect.MultimapBuilder; +import jakarta.annotation.Nonnull; +import jakarta.annotation.Nullable; +import jakarta.persistence.EntityManager; +import jakarta.persistence.PersistenceContext; +import jakarta.persistence.PersistenceContextType; +import jakarta.persistence.Tuple; +import jakarta.persistence.TypedQuery; +import jakarta.persistence.criteria.CriteriaBuilder; +import jakarta.persistence.criteria.CriteriaQuery; +import jakarta.persistence.criteria.Predicate; +import jakarta.persistence.criteria.Root; import org.apache.commons.lang3.StringUtils; import org.apache.commons.lang3.Validate; import org.hl7.fhir.instance.model.api.IAnyResource; @@ -67,17 +75,6 @@ import java.util.Map; import java.util.Optional; import java.util.Set; import java.util.stream.Collectors; -import javax.annotation.Nonnull; -import javax.annotation.Nullable; -import javax.persistence.EntityManager; -import javax.persistence.PersistenceContext; -import javax.persistence.PersistenceContextType; -import javax.persistence.Tuple; -import javax.persistence.TypedQuery; -import javax.persistence.criteria.CriteriaBuilder; -import javax.persistence.criteria.CriteriaQuery; -import javax.persistence.criteria.Predicate; -import javax.persistence.criteria.Root; import static ca.uhn.fhir.jpa.search.builder.predicate.BaseJoiningPredicateBuilder.replaceDefaultPartitionIdIfNonNull; import static org.apache.commons.lang3.StringUtils.isNotBlank; @@ -104,9 +101,6 @@ public class IdHelperService implements IIdHelperService { public static final Predicate[] EMPTY_PREDICATE_ARRAY = new Predicate[0]; public static final String RESOURCE_PID = "RESOURCE_PID"; - @Autowired - protected IForcedIdDao myForcedIdDao; - @Autowired protected IResourceTableDao myResourceTableDao; @@ -216,7 +210,9 @@ public class IdHelperService implements IIdHelperService { Validate.isTrue(!theIds.isEmpty(), "theIds must not be empty"); Map retVals = new HashMap<>(); - + RequestPartitionId partitionId = myPartitionSettings.isAllowUnqualifiedCrossPartitionReference() + ? RequestPartitionId.allPartitions() + : theRequestPartitionId; for (String id : theIds) { JpaPid retVal; if (!idRequiresForcedId(id)) { @@ -227,18 +223,17 @@ public class IdHelperService implements IIdHelperService { // is a forced id // we must resolve! if (myStorageSettings.isDeleteEnabled()) { - retVal = resolveResourceIdentity(theRequestPartitionId, theResourceType, id, theExcludeDeleted) + retVal = resolveResourceIdentity(partitionId, theResourceType, id, theExcludeDeleted) .getPersistentId(); retVals.put(id, retVal); } else { // fetch from cache... adding to cache if not available - String key = toForcedIdToPidKey(theRequestPartitionId, theResourceType, id); + String key = toForcedIdToPidKey(partitionId, theResourceType, id); retVal = myMemoryCacheService.getThenPutAfterCommit( MemoryCacheService.CacheEnum.FORCED_ID_TO_PID, key, t -> { List ids = Collections.singletonList(new IdType(theResourceType, id)); // fetches from cache using a function that checks cache first... - List resolvedIds = - resolveResourcePersistentIdsWithCache(theRequestPartitionId, ids); + List resolvedIds = resolveResourcePersistentIdsWithCache(partitionId, ids); if (resolvedIds.isEmpty()) { throw new ResourceNotFoundException(Msg.code(1100) + ids.get(0)); } @@ -270,6 +265,7 @@ public class IdHelperService implements IIdHelperService { * * @throws ResourceNotFoundException If the ID can not be found */ + @Nonnull @Override public JpaPid resolveResourcePersistentIds( @Nonnull RequestPartitionId theRequestPartitionId, @@ -327,7 +323,7 @@ public class IdHelperService implements IIdHelperService { @Override @Nonnull public List resolveResourcePersistentIdsWithCache( - RequestPartitionId theRequestPartitionId, List theIds, boolean theOnlyForcedIds) { + @Nonnull RequestPartitionId theRequestPartitionId, List theIds, boolean theOnlyForcedIds) { assert myDontCheckActiveTransactionForUnitTest || TransactionSynchronizationManager.isSynchronizationActive(); List retVal = new ArrayList<>(theIds.size()); @@ -375,36 +371,33 @@ public class IdHelperService implements IIdHelperService { RequestPartitionId theRequestPartitionId, List theIds, List theOutputListToPopulate) { CriteriaBuilder cb = myEntityManager.getCriteriaBuilder(); CriteriaQuery criteriaQuery = cb.createTupleQuery(); - Root from = criteriaQuery.from(ForcedId.class); + Root from = criteriaQuery.from(ResourceTable.class); /* - * We don't currently have an index that satisfies these three columns, but the - * index IDX_FORCEDID_TYPE_FID does include myResourceType and myForcedId - * so we're at least minimizing the amount of data we fetch. A largescale test - * on Postgres does confirm that this lookup does use the index and is pretty - * performant. + * IDX_RES_FHIR_ID covers these columns, but RES_ID is only INCLUDEd. + * Only PG, and MSSql support INCLUDE COLUMNS. + * @see AddIndexTask.generateSql */ - criteriaQuery.multiselect( - from.get("myResourcePid").as(Long.class), - from.get("myResourceType").as(String.class), - from.get("myForcedId").as(String.class)); + criteriaQuery.multiselect(from.get("myId"), from.get("myResourceType"), from.get("myFhirId")); + // one create one clause per id. List predicates = new ArrayList<>(theIds.size()); for (IIdType next : theIds) { List andPredicates = new ArrayList<>(3); if (isNotBlank(next.getResourceType())) { - Predicate typeCriteria = cb.equal(from.get("myResourceType").as(String.class), next.getResourceType()); + Predicate typeCriteria = cb.equal(from.get("myResourceType"), next.getResourceType()); andPredicates.add(typeCriteria); } - Predicate idCriteria = cb.equal(from.get("myForcedId").as(String.class), next.getIdPart()); + Predicate idCriteria = cb.equal(from.get("myFhirId"), next.getIdPart()); andPredicates.add(idCriteria); getOptionalPartitionPredicate(theRequestPartitionId, cb, from).ifPresent(andPredicates::add); predicates.add(cb.and(andPredicates.toArray(EMPTY_PREDICATE_ARRAY))); } + // join all the clauses as OR criteriaQuery.where(cb.or(predicates.toArray(EMPTY_PREDICATE_ARRAY))); TypedQuery query = myEntityManager.createQuery(criteriaQuery); @@ -432,23 +425,20 @@ public class IdHelperService implements IIdHelperService { * 3. If the requested partition search is not all partition, return the request partition as predicate. */ private Optional getOptionalPartitionPredicate( - RequestPartitionId theRequestPartitionId, CriteriaBuilder cb, Root from) { + RequestPartitionId theRequestPartitionId, CriteriaBuilder cb, Root from) { if (myPartitionSettings.isAllowUnqualifiedCrossPartitionReference()) { return Optional.empty(); } else if (theRequestPartitionId.isDefaultPartition() && myPartitionSettings.getDefaultPartitionId() == null) { - Predicate partitionIdCriteria = - cb.isNull(from.get("myPartitionIdValue").as(Integer.class)); + Predicate partitionIdCriteria = cb.isNull(from.get("myPartitionIdValue")); return Optional.of(partitionIdCriteria); } else if (!theRequestPartitionId.isAllPartitions()) { List partitionIds = theRequestPartitionId.getPartitionIds(); partitionIds = replaceDefaultPartitionIdIfNonNull(myPartitionSettings, partitionIds); if (partitionIds.size() > 1) { - Predicate partitionIdCriteria = - from.get("myPartitionIdValue").as(Integer.class).in(partitionIds); + Predicate partitionIdCriteria = from.get("myPartitionIdValue").in(partitionIds); return Optional.of(partitionIdCriteria); } else if (partitionIds.size() == 1) { - Predicate partitionIdCriteria = - cb.equal(from.get("myPartitionIdValue").as(Integer.class), partitionIds.get(0)); + Predicate partitionIdCriteria = cb.equal(from.get("myPartitionIdValue"), partitionIds.get(0)); return Optional.of(partitionIdCriteria); } } @@ -488,7 +478,7 @@ public class IdHelperService implements IIdHelperService { return myMemoryCacheService.get( MemoryCacheService.CacheEnum.PID_TO_FORCED_ID, theId.getId(), - pid -> myForcedIdDao.findByResourcePid(pid).map(ForcedId::asTypedFhirResourceId)); + pid -> myResourceTableDao.findById(pid).map(ResourceTable::asTypedFhirResourceId)); } private ListMultimap organizeIdsByResourceType(Collection theIds) { @@ -538,37 +528,35 @@ public class IdHelperService implements IIdHelperService { for (Iterator forcedIdIterator = nextIds.iterator(); forcedIdIterator.hasNext(); ) { String nextForcedId = forcedIdIterator.next(); String nextKey = nextResourceType + "/" + nextForcedId; - IResourceLookup cachedLookup = + IResourceLookup cachedLookup = myMemoryCacheService.getIfPresent(MemoryCacheService.CacheEnum.RESOURCE_LOOKUP, nextKey); if (cachedLookup != null) { forcedIdIterator.remove(); - if (!retVal.containsKey(nextForcedId)) { - retVal.put(nextForcedId, new ArrayList<>()); - } - retVal.get(nextForcedId).add(cachedLookup); + retVal.computeIfAbsent(nextForcedId, id -> new ArrayList<>()) + .add(cachedLookup); } } } - if (nextIds.size() > 0) { + if (!nextIds.isEmpty()) { Collection views; assert isNotBlank(nextResourceType); if (requestPartitionId.isAllPartitions()) { - views = myForcedIdDao.findAndResolveByForcedIdWithNoType( + views = myResourceTableDao.findAndResolveByForcedIdWithNoType( nextResourceType, nextIds, theExcludeDeleted); } else { if (requestPartitionId.isDefaultPartition()) { - views = myForcedIdDao.findAndResolveByForcedIdWithNoTypeInPartitionNull( + views = myResourceTableDao.findAndResolveByForcedIdWithNoTypeInPartitionNull( nextResourceType, nextIds, theExcludeDeleted); } else if (requestPartitionId.hasDefaultPartitionId()) { - views = myForcedIdDao.findAndResolveByForcedIdWithNoTypeInPartitionIdOrNullPartitionId( + views = myResourceTableDao.findAndResolveByForcedIdWithNoTypeInPartitionIdOrNullPartitionId( nextResourceType, nextIds, requestPartitionId.getPartitionIdsWithoutDefault(), theExcludeDeleted); } else { - views = myForcedIdDao.findAndResolveByForcedIdWithNoTypeInPartition( + views = myResourceTableDao.findAndResolveByForcedIdWithNoTypeInPartition( nextResourceType, nextIds, requestPartitionId.getPartitionIds(), theExcludeDeleted); } } @@ -580,10 +568,7 @@ public class IdHelperService implements IIdHelperService { Date deletedAt = (Date) next[3]; JpaResourceLookup lookup = new JpaResourceLookup(resourceType, resourcePid, deletedAt); - if (!retVal.containsKey(forcedId)) { - retVal.put(forcedId, new ArrayList<>()); - } - retVal.get(forcedId).add(lookup); + retVal.computeIfAbsent(forcedId, id -> new ArrayList<>()).add(lookup); if (!myStorageSettings.isDeleteEnabled()) { String key = resourceType + "/" + forcedId; @@ -616,19 +601,16 @@ public class IdHelperService implements IIdHelperService { for (Iterator forcedIdIterator = thePidsToResolve.iterator(); forcedIdIterator.hasNext(); ) { Long nextPid = forcedIdIterator.next(); String nextKey = Long.toString(nextPid); - IResourceLookup cachedLookup = + IResourceLookup cachedLookup = myMemoryCacheService.getIfPresent(MemoryCacheService.CacheEnum.RESOURCE_LOOKUP, nextKey); if (cachedLookup != null) { forcedIdIterator.remove(); - if (!theTargets.containsKey(nextKey)) { - theTargets.put(nextKey, new ArrayList<>()); - } - theTargets.get(nextKey).add(cachedLookup); + theTargets.computeIfAbsent(nextKey, id -> new ArrayList<>()).add(cachedLookup); } } } - if (thePidsToResolve.size() > 0) { + if (!thePidsToResolve.isEmpty()) { Collection lookup; if (theRequestPartitionId.isAllPartitions()) { lookup = myResourceTableDao.findLookupFieldsByResourcePid(thePidsToResolve); @@ -661,7 +643,7 @@ public class IdHelperService implements IIdHelperService { } @Override - public PersistentIdToForcedIdMap translatePidsToForcedIds(Set theResourceIds) { + public PersistentIdToForcedIdMap translatePidsToForcedIds(Set theResourceIds) { assert myDontCheckActiveTransactionForUnitTest || TransactionSynchronizationManager.isSynchronizationActive(); Set thePids = theResourceIds.stream().map(JpaPid::getId).collect(Collectors.toSet()); Map> retVal = new HashMap<>( @@ -671,11 +653,11 @@ public class IdHelperService implements IIdHelperService { thePids.stream().filter(t -> !retVal.containsKey(t)).collect(Collectors.toList()); new QueryChunker().chunk(remainingPids, t -> { - List forcedIds = myForcedIdDao.findAllByResourcePid(t); + List resourceEntities = myResourceTableDao.findAllById(t); - for (ForcedId forcedId : forcedIds) { - Long nextResourcePid = forcedId.getResourceId(); - Optional nextForcedId = Optional.of(forcedId.asTypedFhirResourceId()); + for (ResourceTable nextResourceEntity : resourceEntities) { + Long nextResourcePid = nextResourceEntity.getId(); + Optional nextForcedId = Optional.of(nextResourceEntity.asTypedFhirResourceId()); retVal.put(nextResourcePid, nextForcedId); myMemoryCacheService.putAfterCommit( MemoryCacheService.CacheEnum.PID_TO_FORCED_ID, nextResourcePid, nextForcedId); @@ -688,11 +670,11 @@ public class IdHelperService implements IIdHelperService { myMemoryCacheService.putAfterCommit( MemoryCacheService.CacheEnum.PID_TO_FORCED_ID, nextResourcePid, Optional.empty()); } - Map> convertRetVal = new HashMap<>(); + Map> convertRetVal = new HashMap<>(); retVal.forEach((k, v) -> { convertRetVal.put(JpaPid.fromId(k), v); }); - return new PersistentIdToForcedIdMap(convertRetVal); + return new PersistentIdToForcedIdMap<>(convertRetVal); } /** @@ -799,7 +781,7 @@ public class IdHelperService implements IIdHelperService { @Override public IIdType resourceIdFromPidOrThrowException(JpaPid thePid, String theResourceType) { Optional optionalResource = myResourceTableDao.findById(thePid.getId()); - if (!optionalResource.isPresent()) { + if (optionalResource.isEmpty()) { throw new ResourceNotFoundException(Msg.code(2124) + "Requested resource not found"); } return optionalResource.get().getIdDt().toVersionless(); @@ -820,7 +802,7 @@ public class IdHelperService implements IIdHelperService { public Set translatePidsToFhirResourceIds(Set thePids) { assert TransactionSynchronizationManager.isSynchronizationActive(); - PersistentIdToForcedIdMap pidToForcedIdMap = translatePidsToForcedIds(thePids); + PersistentIdToForcedIdMap pidToForcedIdMap = translatePidsToForcedIds(thePids); return pidToForcedIdMap.getResolvedResourceIds(); } diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/index/SearchParamWithInlineReferencesExtractor.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/index/SearchParamWithInlineReferencesExtractor.java index c77d2e206ce..7934a7c64db 100644 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/index/SearchParamWithInlineReferencesExtractor.java +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/index/SearchParamWithInlineReferencesExtractor.java @@ -40,6 +40,10 @@ import ca.uhn.fhir.rest.api.server.storage.TransactionDetails; import ca.uhn.fhir.rest.server.exceptions.ResourceVersionConflictException; import ca.uhn.fhir.rest.server.util.ISearchParamRegistry; import com.google.common.annotations.VisibleForTesting; +import jakarta.annotation.Nullable; +import jakarta.persistence.EntityManager; +import jakarta.persistence.PersistenceContext; +import jakarta.persistence.PersistenceContextType; import org.hl7.fhir.instance.model.api.IBaseResource; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.context.annotation.Lazy; @@ -48,10 +52,6 @@ import org.springframework.stereotype.Service; import java.util.Collection; import java.util.Iterator; import java.util.stream.Collectors; -import javax.annotation.Nullable; -import javax.persistence.EntityManager; -import javax.persistence.PersistenceContext; -import javax.persistence.PersistenceContextType; @Service @Lazy diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/mdm/MdmLinkDaoJpaImpl.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/mdm/MdmLinkDaoJpaImpl.java index 6e87d443f1b..f8a3765754f 100644 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/mdm/MdmLinkDaoJpaImpl.java +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/mdm/MdmLinkDaoJpaImpl.java @@ -38,8 +38,20 @@ import ca.uhn.fhir.mdm.dao.IMdmLinkDao; import ca.uhn.fhir.mdm.model.MdmPidTuple; import ca.uhn.fhir.rest.api.SortOrderEnum; import ca.uhn.fhir.rest.server.exceptions.UnprocessableEntityException; +import jakarta.annotation.Nonnull; +import jakarta.persistence.EntityManager; +import jakarta.persistence.TypedQuery; +import jakarta.persistence.criteria.CriteriaBuilder; +import jakarta.persistence.criteria.CriteriaQuery; +import jakarta.persistence.criteria.Expression; +import jakarta.persistence.criteria.Order; +import jakarta.persistence.criteria.Path; +import jakarta.persistence.criteria.Predicate; +import jakarta.persistence.criteria.Root; +import jakarta.validation.constraints.NotNull; import org.apache.commons.collections4.CollectionUtils; import org.apache.commons.collections4.ListUtils; +import org.apache.commons.lang3.ObjectUtils; import org.apache.commons.lang3.Validate; import org.hibernate.envers.AuditReader; import org.hibernate.envers.RevisionType; @@ -63,17 +75,6 @@ import java.util.Date; import java.util.List; import java.util.Optional; import java.util.stream.Collectors; -import javax.annotation.Nonnull; -import javax.persistence.EntityManager; -import javax.persistence.TypedQuery; -import javax.persistence.criteria.CriteriaBuilder; -import javax.persistence.criteria.CriteriaQuery; -import javax.persistence.criteria.Expression; -import javax.persistence.criteria.Order; -import javax.persistence.criteria.Path; -import javax.persistence.criteria.Predicate; -import javax.persistence.criteria.Root; -import javax.validation.constraints.NotNull; import static ca.uhn.fhir.mdm.api.params.MdmQuerySearchParameters.GOLDEN_RESOURCE_NAME; import static ca.uhn.fhir.mdm.api.params.MdmQuerySearchParameters.GOLDEN_RESOURCE_PID_NAME; @@ -87,10 +88,10 @@ public class MdmLinkDaoJpaImpl implements IMdmLinkDao { private static final Logger ourLog = LoggerFactory.getLogger(MdmLinkDaoJpaImpl.class); @Autowired - IMdmLinkJpaRepository myMdmLinkDao; + protected EntityManager myEntityManager; @Autowired - protected EntityManager myEntityManager; + IMdmLinkJpaRepository myMdmLinkDao; @Autowired private IIdHelperService myIdHelperService; @@ -246,6 +247,8 @@ public class MdmLinkDaoJpaImpl implements IMdmLinkDao { @Override public Page search(MdmQuerySearchParameters theParams) { + Long totalResults = countTotalResults(theParams); + CriteriaBuilder criteriaBuilder = myEntityManager.getCriteriaBuilder(); CriteriaQuery criteriaQuery = criteriaBuilder.createQuery(MdmLink.class); Root from = criteriaQuery.from(MdmLink.class); @@ -257,14 +260,9 @@ public class MdmLinkDaoJpaImpl implements IMdmLinkDao { if (!orderList.isEmpty()) { criteriaQuery.orderBy(orderList); } - TypedQuery typedQuery = myEntityManager.createQuery(criteriaQuery.where(finalQuery)); - CriteriaQuery countQuery = criteriaBuilder.createQuery(Long.class); - countQuery.select(criteriaBuilder.count(countQuery.from(MdmLink.class))).where(finalQuery); - - Long totalResults = myEntityManager.createQuery(countQuery).getSingleResult(); MdmPageRequest pageRequest = theParams.getPageRequest(); - + TypedQuery typedQuery = myEntityManager.createQuery(criteriaQuery.where(finalQuery)); List result = typedQuery .setFirstResult(pageRequest.getOffset()) .setMaxResults(pageRequest.getCount()) @@ -273,13 +271,26 @@ public class MdmLinkDaoJpaImpl implements IMdmLinkDao { return new PageImpl<>(result, PageRequest.of(pageRequest.getPage(), pageRequest.getCount()), totalResults); } + private Long countTotalResults(MdmQuerySearchParameters theParams) { + CriteriaBuilder criteriaBuilder = myEntityManager.getCriteriaBuilder(); + CriteriaQuery countQuery = criteriaBuilder.createQuery(Long.class); + Root from = countQuery.from(MdmLink.class); + + List andPredicates = buildPredicates(theParams, criteriaBuilder, from); + Predicate finalQuery = criteriaBuilder.and(andPredicates.toArray(new Predicate[0])); + + countQuery.select(criteriaBuilder.count(from)).where(finalQuery); + + return myEntityManager.createQuery(countQuery).getSingleResult(); + } + @NotNull private List buildPredicates( MdmQuerySearchParameters theParams, CriteriaBuilder criteriaBuilder, Root from) { List andPredicates = new ArrayList<>(); if (theParams.getGoldenResourceId() != null) { Predicate goldenResourcePredicate = criteriaBuilder.equal( - from.get(GOLDEN_RESOURCE_PID_NAME).as(Long.class), + from.get(GOLDEN_RESOURCE_PID_NAME), (myIdHelperService.getPidOrThrowException( RequestPartitionId.allPartitions(), theParams.getGoldenResourceId())) .getId()); @@ -287,33 +298,31 @@ public class MdmLinkDaoJpaImpl implements IMdmLinkDao { } if (theParams.getSourceId() != null) { Predicate sourceIdPredicate = criteriaBuilder.equal( - from.get(SOURCE_PID_NAME).as(Long.class), + from.get(SOURCE_PID_NAME), (myIdHelperService.getPidOrThrowException( RequestPartitionId.allPartitions(), theParams.getSourceId())) .getId()); andPredicates.add(sourceIdPredicate); } if (theParams.getMatchResult() != null) { - Predicate matchResultPredicate = criteriaBuilder.equal( - from.get(MATCH_RESULT_NAME).as(MdmMatchResultEnum.class), theParams.getMatchResult()); + Predicate matchResultPredicate = + criteriaBuilder.equal(from.get(MATCH_RESULT_NAME), theParams.getMatchResult()); andPredicates.add(matchResultPredicate); } if (theParams.getLinkSource() != null) { - Predicate linkSourcePredicate = criteriaBuilder.equal( - from.get(LINK_SOURCE_NAME).as(MdmLinkSourceEnum.class), theParams.getLinkSource()); + Predicate linkSourcePredicate = + criteriaBuilder.equal(from.get(LINK_SOURCE_NAME), theParams.getLinkSource()); andPredicates.add(linkSourcePredicate); } if (!CollectionUtils.isEmpty(theParams.getPartitionIds())) { - Expression exp = - from.get(PARTITION_ID_NAME).get(PARTITION_ID_NAME).as(Integer.class); + Expression exp = from.get(PARTITION_ID_NAME).get(PARTITION_ID_NAME); Predicate linkSourcePredicate = exp.in(theParams.getPartitionIds()); andPredicates.add(linkSourcePredicate); } if (theParams.getResourceType() != null) { Predicate resourceTypePredicate = criteriaBuilder.equal( - from.get(GOLDEN_RESOURCE_NAME).get(RESOURCE_TYPE_NAME).as(String.class), - theParams.getResourceType()); + from.get(GOLDEN_RESOURCE_NAME).get(RESOURCE_TYPE_NAME), theParams.getResourceType()); andPredicates.add(resourceTypePredicate); } @@ -371,21 +380,39 @@ public class MdmLinkDaoJpaImpl implements IMdmLinkDao { final AuditQueryCreator auditQueryCreator = myAuditReader.createQuery(); try { - final AuditCriterion goldenResourceIdCriterion = AuditEntity.property(GOLDEN_RESOURCE_PID_NAME) - .in(convertToLongIds(theMdmHistorySearchParameters.getGoldenResourceIds())); - final AuditCriterion resourceIdCriterion = AuditEntity.property(SOURCE_PID_NAME) - .in(convertToLongIds(theMdmHistorySearchParameters.getSourceIds())); + final AuditCriterion goldenResourceIdCriterion = buildAuditCriterionOrNull( + theMdmHistorySearchParameters.getGoldenResourceIds(), GOLDEN_RESOURCE_PID_NAME); + + final AuditCriterion resourceIdCriterion = + buildAuditCriterionOrNull(theMdmHistorySearchParameters.getSourceIds(), SOURCE_PID_NAME); final AuditCriterion goldenResourceAndOrResourceIdCriterion; if (!theMdmHistorySearchParameters.getGoldenResourceIds().isEmpty() && !theMdmHistorySearchParameters.getSourceIds().isEmpty()) { + + // Make sure the criterion does not contain empty IN clause, e.g. id IN (), which postgres (likely other + // sql servers) do not like. Directly return empty result instead. + if (ObjectUtils.anyNull(goldenResourceIdCriterion, resourceIdCriterion)) { + return new ArrayList<>(); + } goldenResourceAndOrResourceIdCriterion = AuditEntity.and(goldenResourceIdCriterion, resourceIdCriterion); + } else if (!theMdmHistorySearchParameters.getGoldenResourceIds().isEmpty()) { + + if (ObjectUtils.anyNull(goldenResourceIdCriterion)) { + return new ArrayList<>(); + } goldenResourceAndOrResourceIdCriterion = goldenResourceIdCriterion; + } else if (!theMdmHistorySearchParameters.getSourceIds().isEmpty()) { + + if (ObjectUtils.anyNull(resourceIdCriterion)) { + return new ArrayList<>(); + } goldenResourceAndOrResourceIdCriterion = resourceIdCriterion; + } else { throw new IllegalArgumentException(Msg.code(2298) + "$mdm-link-history Golden resource and source query IDs cannot both be empty."); @@ -420,6 +447,12 @@ public class MdmLinkDaoJpaImpl implements IMdmLinkDao { .collect(Collectors.toUnmodifiableList()); } + private AuditCriterion buildAuditCriterionOrNull( + List theMdmHistorySearchParameterIds, String theProperty) { + List longIds = convertToLongIds(theMdmHistorySearchParameterIds); + return longIds.isEmpty() ? null : AuditEntity.property(theProperty).in(longIds); + } + private MdmLinkWithRevision buildRevisionFromObjectArray(Object[] theArray) { final Object mdmLinkUncast = theArray[0]; final Object revisionUncast = theArray[1]; diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/mdm/MdmMetricSvcJpaImpl.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/mdm/MdmMetricSvcJpaImpl.java new file mode 100644 index 00000000000..3bc7bd5ecfb --- /dev/null +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/mdm/MdmMetricSvcJpaImpl.java @@ -0,0 +1,155 @@ +/*- + * #%L + * HAPI FHIR JPA Server + * %% + * Copyright (C) 2014 - 2023 Smile CDR, Inc. + * %% + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * #L% + */ +package ca.uhn.fhir.jpa.dao.mdm; + +import ca.uhn.fhir.jpa.api.dao.DaoRegistry; +import ca.uhn.fhir.jpa.dao.data.IMdmLinkJpaMetricsRepository; +import ca.uhn.fhir.mdm.api.BaseMdmMetricSvc; +import ca.uhn.fhir.mdm.api.MdmLinkSourceEnum; +import ca.uhn.fhir.mdm.api.MdmMatchResultEnum; +import ca.uhn.fhir.mdm.api.params.GenerateMdmMetricsParameters; +import ca.uhn.fhir.mdm.model.MdmLinkMetrics; +import ca.uhn.fhir.mdm.model.MdmLinkScoreMetrics; +import ca.uhn.fhir.mdm.model.MdmMetrics; +import ca.uhn.fhir.mdm.model.MdmResourceMetrics; +import jakarta.persistence.EntityManager; +import jakarta.persistence.EntityManagerFactory; +import jakarta.persistence.Query; +import org.springframework.transaction.annotation.Transactional; + +import java.math.BigInteger; +import java.util.Arrays; +import java.util.List; +import java.util.stream.Collectors; + +public class MdmMetricSvcJpaImpl extends BaseMdmMetricSvc { + + private final IMdmLinkJpaMetricsRepository myJpaRepository; + + private final EntityManagerFactory myEntityManagerFactory; + + public MdmMetricSvcJpaImpl( + IMdmLinkJpaMetricsRepository theRepository, + DaoRegistry theDaoRegistry, + EntityManagerFactory theEntityManagerFactory) { + super(theDaoRegistry); + myJpaRepository = theRepository; + myEntityManagerFactory = theEntityManagerFactory; + } + + protected MdmLinkMetrics generateLinkMetrics(GenerateMdmMetricsParameters theParameters) { + List linkSources = theParameters.getLinkSourceFilters(); + List matchResults = theParameters.getMatchResultFilters(); + + if (linkSources.isEmpty()) { + linkSources = Arrays.asList(MdmLinkSourceEnum.values()); + } + if (matchResults.isEmpty()) { + matchResults = Arrays.asList(MdmMatchResultEnum.values()); + } + + Object[][] data = myJpaRepository.generateMetrics(theParameters.getResourceType(), linkSources, matchResults); + MdmLinkMetrics metrics = new MdmLinkMetrics(); + metrics.setResourceType(theParameters.getResourceType()); + for (Object[] row : data) { + MdmMatchResultEnum matchResult = (MdmMatchResultEnum) row[0]; + MdmLinkSourceEnum source = (MdmLinkSourceEnum) row[1]; + long count = (Long) row[2]; + metrics.addMetric(matchResult, source, count); + } + return metrics; + } + + protected MdmLinkScoreMetrics generateLinkScoreMetrics(GenerateMdmMetricsParameters theParameters) { + String resourceType = theParameters.getResourceType(); + + List matchResultTypes = theParameters.getMatchResultFilters(); + + // if no result type filter, add all result types + if (matchResultTypes.isEmpty()) { + matchResultTypes = Arrays.asList(MdmMatchResultEnum.values()); + } + + String sql = "SELECT %s FROM MPI_LINK ml WHERE ml.TARGET_TYPE = :resourceType " + + "AND ml.MATCH_RESULT in (:matchResult)"; + + StringBuilder sb = new StringBuilder(); + sb.append("sum(case when ml.SCORE is null then 1 else 0 end) as B_" + NULL_VALUE); + + for (int i = 0; i < BUCKETS; i++) { + double bucket = getBucket(i + 1); + sb.append(",\n"); + if (i == 0) { + // score <= .01 + sb.append(String.format("sum(case when ml.SCORE <= %.2f then 1 else 0 end) as B%d", bucket, i)); + } else { + // score > i/100 && score <= i/100 + sb.append(String.format( + "sum(case when ml.score > %.2f and ml.SCORE <= %.2f then 1 else 0 end) as B%d", + getBucket(i), bucket, i)); + } + } + + EntityManager em = myEntityManagerFactory.createEntityManager(); + + Query nativeQuery = em.createNativeQuery(String.format(sql, sb.toString())); + + org.hibernate.query.Query hibernateQuery = (org.hibernate.query.Query) nativeQuery; + + hibernateQuery.setParameter("resourceType", resourceType); + hibernateQuery.setParameter( + "matchResult", matchResultTypes.stream().map(Enum::ordinal).collect(Collectors.toList())); + + List results = hibernateQuery.getResultList(); + + em.close(); + + MdmLinkScoreMetrics metrics = new MdmLinkScoreMetrics(); + + // we only get one row back + Object[] row = (Object[]) results.get(0); + int length = row.length; + for (int i = 0; i < length; i++) { + // if there's nothing in the db, these values will all be null + Number bi = row[i] != null ? (Number) row[i] : BigInteger.valueOf(0); + double bucket = getBucket(i); + if (i == 0) { + metrics.addScore(NULL_VALUE, bi.longValue()); + } else if (i == 1) { + metrics.addScore(String.format(FIRST_BUCKET, bucket), bi.longValue()); + } else { + metrics.addScore(String.format(NTH_BUCKET, getBucket(i - 1), bucket), bi.longValue()); + } + } + + return metrics; + } + + @Transactional + @Override + public MdmMetrics generateMdmMetrics(GenerateMdmMetricsParameters theParameters) { + MdmResourceMetrics resourceMetrics = generateResourceMetrics(theParameters); + MdmLinkMetrics linkMetrics = generateLinkMetrics(theParameters); + MdmLinkScoreMetrics scoreMetrics = generateLinkScoreMetrics(theParameters); + + MdmMetrics metrics = MdmMetrics.fromSeperableMetrics(resourceMetrics, linkMetrics, scoreMetrics); + return metrics; + } +} diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/r4/FhirSystemDaoR4.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/r4/FhirSystemDaoR4.java index 74bbcf58162..2ed5f0c2cca 100644 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/r4/FhirSystemDaoR4.java +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/r4/FhirSystemDaoR4.java @@ -23,13 +23,13 @@ import ca.uhn.fhir.jpa.dao.BaseHapiFhirSystemDao; import ca.uhn.fhir.jpa.dao.JpaResourceDao; import ca.uhn.fhir.jpa.model.entity.TagDefinition; import ca.uhn.fhir.rest.api.server.RequestDetails; +import jakarta.persistence.TypedQuery; import org.hl7.fhir.instance.model.api.IBaseBundle; import org.hl7.fhir.r4.model.Bundle; import org.hl7.fhir.r4.model.Meta; import java.util.Collection; import java.util.List; -import javax.persistence.TypedQuery; public class FhirSystemDaoR4 extends BaseHapiFhirSystemDao { diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/r4b/FhirSystemDaoR4B.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/r4b/FhirSystemDaoR4B.java index 8867aa89d13..b950fe13981 100644 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/r4b/FhirSystemDaoR4B.java +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/r4b/FhirSystemDaoR4B.java @@ -23,13 +23,13 @@ import ca.uhn.fhir.jpa.dao.BaseHapiFhirSystemDao; import ca.uhn.fhir.jpa.dao.JpaResourceDao; import ca.uhn.fhir.jpa.model.entity.TagDefinition; import ca.uhn.fhir.rest.api.server.RequestDetails; +import jakarta.persistence.TypedQuery; import org.hl7.fhir.instance.model.api.IBaseBundle; import org.hl7.fhir.r4b.model.Bundle; import org.hl7.fhir.r4b.model.Meta; import java.util.Collection; import java.util.List; -import javax.persistence.TypedQuery; public class FhirSystemDaoR4B extends BaseHapiFhirSystemDao { diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/r5/FhirSystemDaoR5.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/r5/FhirSystemDaoR5.java index 5925f9ee531..b1af9191e57 100644 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/r5/FhirSystemDaoR5.java +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/r5/FhirSystemDaoR5.java @@ -23,13 +23,13 @@ import ca.uhn.fhir.jpa.dao.BaseHapiFhirSystemDao; import ca.uhn.fhir.jpa.dao.JpaResourceDao; import ca.uhn.fhir.jpa.model.entity.TagDefinition; import ca.uhn.fhir.rest.api.server.RequestDetails; +import jakarta.persistence.TypedQuery; import org.hl7.fhir.instance.model.api.IBaseBundle; import org.hl7.fhir.r5.model.Bundle; import org.hl7.fhir.r5.model.Meta; import java.util.Collection; import java.util.List; -import javax.persistence.TypedQuery; public class FhirSystemDaoR5 extends BaseHapiFhirSystemDao { diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/search/ExtendedHSearchClauseBuilder.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/search/ExtendedHSearchClauseBuilder.java index 8dde509dbb6..e72cba34462 100644 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/search/ExtendedHSearchClauseBuilder.java +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/search/ExtendedHSearchClauseBuilder.java @@ -42,6 +42,7 @@ import ca.uhn.fhir.rest.param.UriParam; import ca.uhn.fhir.util.DateUtils; import ca.uhn.fhir.util.NumericParamRangeUtil; import ca.uhn.fhir.util.StringUtil; +import jakarta.annotation.Nonnull; import org.apache.commons.collections4.CollectionUtils; import org.apache.commons.lang3.ObjectUtils; import org.apache.commons.lang3.StringUtils; @@ -66,7 +67,6 @@ import java.util.Objects; import java.util.Optional; import java.util.Set; import java.util.stream.Collectors; -import javax.annotation.Nonnull; import static ca.uhn.fhir.jpa.dao.search.PathContext.joinPath; import static ca.uhn.fhir.jpa.model.search.HSearchIndexWriter.IDX_STRING_EXACT; diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/search/ExtendedHSearchIndexExtractor.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/search/ExtendedHSearchIndexExtractor.java index 8cb8ed8d9b0..d5bcf5e3312 100644 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/search/ExtendedHSearchIndexExtractor.java +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/search/ExtendedHSearchIndexExtractor.java @@ -36,6 +36,7 @@ import ca.uhn.fhir.rest.api.RestSearchParameterTypeEnum; import ca.uhn.fhir.rest.server.util.ResourceSearchParams; import ca.uhn.fhir.util.MetaUtil; import com.google.common.base.Strings; +import jakarta.annotation.Nonnull; import org.hl7.fhir.instance.model.api.IBase; import org.hl7.fhir.instance.model.api.IBaseCoding; import org.hl7.fhir.instance.model.api.IBaseResource; @@ -46,7 +47,6 @@ import java.util.HashMap; import java.util.List; import java.util.Locale; import java.util.Map; -import javax.annotation.Nonnull; import static org.apache.commons.lang3.StringUtils.isNotBlank; diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/search/LastNAggregation.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/search/LastNAggregation.java index 8b2e463f980..7b7857843b1 100644 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/search/LastNAggregation.java +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/search/LastNAggregation.java @@ -22,6 +22,7 @@ package ca.uhn.fhir.jpa.dao.search; import com.google.gson.Gson; import com.google.gson.JsonArray; import com.google.gson.JsonObject; +import jakarta.annotation.Nonnull; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -29,7 +30,6 @@ import java.util.List; import java.util.stream.Collectors; import java.util.stream.Stream; import java.util.stream.StreamSupport; -import javax.annotation.Nonnull; import static ca.uhn.fhir.jpa.model.search.HSearchIndexWriter.SEARCH_PARAM_ROOT; diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/search/PathContext.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/search/PathContext.java index eb87d87dcfd..614c04cec82 100644 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/search/PathContext.java +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/search/PathContext.java @@ -19,13 +19,14 @@ */ package ca.uhn.fhir.jpa.dao.search; +import jakarta.annotation.Nonnull; +import org.hibernate.search.engine.search.predicate.SearchPredicate; import org.hibernate.search.engine.search.predicate.dsl.*; import org.hibernate.search.util.common.annotation.Incubating; import java.util.List; import java.util.function.Consumer; import java.util.function.Function; -import javax.annotation.Nonnull; import static ca.uhn.fhir.jpa.dao.search.ExtendedHSearchClauseBuilder.PATH_JOINER; import static ca.uhn.fhir.jpa.model.search.HSearchIndexWriter.NESTED_SEARCH_PARAM_ROOT; @@ -108,80 +109,153 @@ class PathContext implements SearchPredicateFactory { // implement SearchPredicateFactory + @Override public MatchAllPredicateOptionsStep matchAll() { return myPredicateFactory.matchAll(); } + @Override + public MatchNonePredicateFinalStep matchNone() { + return myPredicateFactory.matchNone(); + } + + @Override public MatchIdPredicateMatchingStep id() { return myPredicateFactory.id(); } + @Override public BooleanPredicateClausesStep bool() { return myPredicateFactory.bool(); } + @Override public PredicateFinalStep bool(Consumer> clauseContributor) { return myPredicateFactory.bool(clauseContributor); } + @Override + public SimpleBooleanPredicateClausesStep and() { + return myPredicateFactory.and(); + } + + @Override + public SimpleBooleanPredicateOptionsStep and( + SearchPredicate theSearchPredicate, SearchPredicate... theSearchPredicates) { + return myPredicateFactory.and(theSearchPredicate, theSearchPredicates); + } + + @Override + public SimpleBooleanPredicateOptionsStep and( + PredicateFinalStep thePredicateFinalStep, PredicateFinalStep... thePredicateFinalSteps) { + return myPredicateFactory.and(thePredicateFinalStep, thePredicateFinalSteps); + } + + @Override + public SimpleBooleanPredicateClausesStep or() { + return myPredicateFactory.or(); + } + + @Override + public SimpleBooleanPredicateOptionsStep or( + SearchPredicate theSearchPredicate, SearchPredicate... theSearchPredicates) { + return myPredicateFactory.or(theSearchPredicate, theSearchPredicates); + } + + @Override + public SimpleBooleanPredicateOptionsStep or( + PredicateFinalStep thePredicateFinalStep, PredicateFinalStep... thePredicateFinalSteps) { + return myPredicateFactory.or(thePredicateFinalStep, thePredicateFinalSteps); + } + + @Override + public NotPredicateFinalStep not(SearchPredicate theSearchPredicate) { + return myPredicateFactory.not(theSearchPredicate); + } + + @Override + public NotPredicateFinalStep not(PredicateFinalStep thePredicateFinalStep) { + return myPredicateFactory.not(thePredicateFinalStep); + } + + @Override public MatchPredicateFieldStep match() { return myPredicateFactory.match(); } + @Override public RangePredicateFieldStep range() { return myPredicateFactory.range(); } + @Override public PhrasePredicateFieldStep phrase() { return myPredicateFactory.phrase(); } + @Override public WildcardPredicateFieldStep wildcard() { return myPredicateFactory.wildcard(); } + @Override public RegexpPredicateFieldStep regexp() { return myPredicateFactory.regexp(); } + @Override public TermsPredicateFieldStep terms() { return myPredicateFactory.terms(); } + @Override public NestedPredicateFieldStep nested() { return myPredicateFactory.nested(); } + @Override + public NestedPredicateClausesStep nested(String theObjectFieldPath) { + return myPredicateFactory.nested(theObjectFieldPath); + } + + @Override public SimpleQueryStringPredicateFieldStep simpleQueryString() { return myPredicateFactory.simpleQueryString(); } + @Override public ExistsPredicateFieldStep exists() { return myPredicateFactory.exists(); } + @Override public SpatialPredicateInitialStep spatial() { return myPredicateFactory.spatial(); } + @Override @Incubating public NamedPredicateOptionsStep named(String path) { return myPredicateFactory.named(path); } + @Override public T extension(SearchPredicateFactoryExtension extension) { return myPredicateFactory.extension(extension); } + @Override public SearchPredicateFactoryExtensionIfSupportedStep extension() { return myPredicateFactory.extension(); } + @Override @Incubating public SearchPredicateFactory withRoot(String objectFieldPath) { return myPredicateFactory.withRoot(objectFieldPath); } + @Override @Incubating public String toAbsolutePath(String relativeFieldPath) { return myPredicateFactory.toAbsolutePath(relativeFieldPath); diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/delete/DeleteConflictFinderService.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/delete/DeleteConflictFinderService.java index 1e52953446b..33a1932a48c 100644 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/delete/DeleteConflictFinderService.java +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/delete/DeleteConflictFinderService.java @@ -21,13 +21,13 @@ package ca.uhn.fhir.jpa.delete; import ca.uhn.fhir.jpa.model.entity.ResourceLink; import ca.uhn.fhir.jpa.model.entity.ResourceTable; +import jakarta.persistence.EntityManager; +import jakarta.persistence.PersistenceContext; +import jakarta.persistence.PersistenceContextType; +import jakarta.persistence.TypedQuery; import org.springframework.stereotype.Service; import java.util.List; -import javax.persistence.EntityManager; -import javax.persistence.PersistenceContext; -import javax.persistence.PersistenceContextType; -import javax.persistence.TypedQuery; @Service public class DeleteConflictFinderService { diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/delete/batch2/DeleteExpungeSqlBuilder.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/delete/batch2/DeleteExpungeSqlBuilder.java index 0bc2a272449..f368b52a161 100644 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/delete/batch2/DeleteExpungeSqlBuilder.java +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/delete/batch2/DeleteExpungeSqlBuilder.java @@ -28,6 +28,7 @@ import ca.uhn.fhir.jpa.dao.expunge.ResourceTableFKProvider; import ca.uhn.fhir.jpa.model.dao.JpaPid; import ca.uhn.fhir.jpa.model.entity.ResourceLink; import ca.uhn.fhir.rest.server.exceptions.InvalidRequestException; +import jakarta.annotation.Nonnull; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -36,7 +37,6 @@ import java.util.Collections; import java.util.List; import java.util.Set; import java.util.stream.Collectors; -import javax.annotation.Nonnull; public class DeleteExpungeSqlBuilder { private static final Logger ourLog = LoggerFactory.getLogger(DeleteExpungeSqlBuilder.class); diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/delete/batch2/DeleteExpungeSvcImpl.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/delete/batch2/DeleteExpungeSvcImpl.java index 9e7e7ff4474..9aee0268557 100644 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/delete/batch2/DeleteExpungeSvcImpl.java +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/delete/batch2/DeleteExpungeSvcImpl.java @@ -23,13 +23,13 @@ import ca.uhn.fhir.jpa.api.svc.IDeleteExpungeSvc; import ca.uhn.fhir.jpa.dao.IFulltextSearchSvc; import ca.uhn.fhir.jpa.model.dao.JpaPid; import ca.uhn.fhir.jpa.model.entity.ResourceTable; +import jakarta.persistence.EntityManager; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; import java.util.List; import java.util.stream.Collectors; -import javax.persistence.EntityManager; public class DeleteExpungeSvcImpl implements IDeleteExpungeSvc { private static final Logger ourLog = LoggerFactory.getLogger(DeleteExpungeSvcImpl.class); diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/entity/Batch2JobInstanceEntity.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/entity/Batch2JobInstanceEntity.java index dd9205a8f30..3b4ee07098d 100644 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/entity/Batch2JobInstanceEntity.java +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/entity/Batch2JobInstanceEntity.java @@ -21,24 +21,24 @@ package ca.uhn.fhir.jpa.entity; import ca.uhn.fhir.batch2.model.JobDefinition; import ca.uhn.fhir.batch2.model.StatusEnum; +import jakarta.persistence.Basic; +import jakarta.persistence.Column; +import jakarta.persistence.Entity; +import jakarta.persistence.EnumType; +import jakarta.persistence.Enumerated; +import jakarta.persistence.FetchType; +import jakarta.persistence.Id; +import jakarta.persistence.Index; +import jakarta.persistence.Lob; +import jakarta.persistence.Table; +import jakarta.persistence.Temporal; +import jakarta.persistence.TemporalType; +import jakarta.persistence.Version; import org.apache.commons.lang3.builder.ToStringBuilder; import org.apache.commons.lang3.builder.ToStringStyle; import java.io.Serializable; import java.util.Date; -import javax.persistence.Basic; -import javax.persistence.Column; -import javax.persistence.Entity; -import javax.persistence.EnumType; -import javax.persistence.Enumerated; -import javax.persistence.FetchType; -import javax.persistence.Id; -import javax.persistence.Index; -import javax.persistence.Lob; -import javax.persistence.Table; -import javax.persistence.Temporal; -import javax.persistence.TemporalType; -import javax.persistence.Version; import static ca.uhn.fhir.batch2.model.JobDefinition.ID_MAX_LENGTH; import static ca.uhn.fhir.jpa.entity.Batch2WorkChunkEntity.ERROR_MSG_MAX_LENGTH; @@ -55,6 +55,8 @@ public class Batch2JobInstanceEntity implements Serializable { public static final int TIME_REMAINING_LENGTH = 100; public static final int PARAMS_JSON_MAX_LENGTH = 2000; private static final long serialVersionUID = 8187134261799095422L; + public static final int INITIATING_USER_NAME_MAX_LENGTH = 200; + public static final int INITIATING_CLIENT_ID_MAX_LENGTH = 200; @Id @Column(name = "ID", length = JobDefinition.ID_MAX_LENGTH, nullable = false) @@ -130,6 +132,12 @@ public class Batch2JobInstanceEntity implements Serializable { @Column(name = "WARNING_MSG", length = WARNING_MSG_MAX_LENGTH, nullable = true) private String myWarningMessages; + @Column(name = "USER_NAME", length = INITIATING_USER_NAME_MAX_LENGTH, nullable = true) + private String myTriggeringUsername; + + @Column(name = "CLIENT_ID", length = INITIATING_CLIENT_ID_MAX_LENGTH, nullable = true) + private String myTriggeringClientId; + /** * Any output from the job can be held in this column * Even serialized json @@ -316,6 +324,24 @@ public class Batch2JobInstanceEntity implements Serializable { myWarningMessages = theWarningMessages; } + public String getTriggeringUsername() { + return myTriggeringUsername; + } + + public Batch2JobInstanceEntity setTriggeringUsername(String theTriggeringUsername) { + myTriggeringUsername = theTriggeringUsername; + return this; + } + + public String getTriggeringClientId() { + return myTriggeringClientId; + } + + public Batch2JobInstanceEntity setTriggeringClientId(String theTriggeringClientId) { + myTriggeringClientId = theTriggeringClientId; + return this; + } + @Override public String toString() { return new ToStringBuilder(this, ToStringStyle.SHORT_PREFIX_STYLE) @@ -338,6 +364,8 @@ public class Batch2JobInstanceEntity implements Serializable { .append("estimatedTimeRemaining", myEstimatedTimeRemaining) .append("report", myReport) .append("warningMessages", myWarningMessages) + .append("initiatingUsername", myTriggeringUsername) + .append("initiatingclientId", myTriggeringClientId) .toString(); } diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/entity/Batch2WorkChunkEntity.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/entity/Batch2WorkChunkEntity.java index e65c8814faa..159be9b503f 100644 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/entity/Batch2WorkChunkEntity.java +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/entity/Batch2WorkChunkEntity.java @@ -20,27 +20,27 @@ package ca.uhn.fhir.jpa.entity; import ca.uhn.fhir.batch2.model.WorkChunkStatusEnum; +import jakarta.persistence.Basic; +import jakarta.persistence.Column; +import jakarta.persistence.Entity; +import jakarta.persistence.EnumType; +import jakarta.persistence.Enumerated; +import jakarta.persistence.FetchType; +import jakarta.persistence.ForeignKey; +import jakarta.persistence.Id; +import jakarta.persistence.Index; +import jakarta.persistence.JoinColumn; +import jakarta.persistence.Lob; +import jakarta.persistence.ManyToOne; +import jakarta.persistence.Table; +import jakarta.persistence.Temporal; +import jakarta.persistence.TemporalType; +import jakarta.persistence.Version; import org.apache.commons.lang3.builder.ToStringBuilder; import org.apache.commons.lang3.builder.ToStringStyle; import java.io.Serializable; import java.util.Date; -import javax.persistence.Basic; -import javax.persistence.Column; -import javax.persistence.Entity; -import javax.persistence.EnumType; -import javax.persistence.Enumerated; -import javax.persistence.FetchType; -import javax.persistence.ForeignKey; -import javax.persistence.Id; -import javax.persistence.Index; -import javax.persistence.JoinColumn; -import javax.persistence.Lob; -import javax.persistence.ManyToOne; -import javax.persistence.Table; -import javax.persistence.Temporal; -import javax.persistence.TemporalType; -import javax.persistence.Version; import static ca.uhn.fhir.batch2.model.JobDefinition.ID_MAX_LENGTH; import static ca.uhn.fhir.jpa.entity.Batch2JobInstanceEntity.STATUS_MAX_LENGTH; diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/entity/BulkExportCollectionEntity.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/entity/BulkExportCollectionEntity.java index 7903eab9004..f564688c247 100644 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/entity/BulkExportCollectionEntity.java +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/entity/BulkExportCollectionEntity.java @@ -20,23 +20,23 @@ package ca.uhn.fhir.jpa.entity; import ca.uhn.fhir.jpa.model.entity.ResourceTable; +import jakarta.persistence.Column; +import jakarta.persistence.Entity; +import jakarta.persistence.FetchType; +import jakarta.persistence.ForeignKey; +import jakarta.persistence.GeneratedValue; +import jakarta.persistence.GenerationType; +import jakarta.persistence.Id; +import jakarta.persistence.JoinColumn; +import jakarta.persistence.ManyToOne; +import jakarta.persistence.OneToMany; +import jakarta.persistence.SequenceGenerator; +import jakarta.persistence.Table; +import jakarta.persistence.Version; import java.io.Serializable; import java.util.ArrayList; import java.util.Collection; -import javax.persistence.Column; -import javax.persistence.Entity; -import javax.persistence.FetchType; -import javax.persistence.ForeignKey; -import javax.persistence.GeneratedValue; -import javax.persistence.GenerationType; -import javax.persistence.Id; -import javax.persistence.JoinColumn; -import javax.persistence.ManyToOne; -import javax.persistence.OneToMany; -import javax.persistence.SequenceGenerator; -import javax.persistence.Table; -import javax.persistence.Version; /* * These classes are no longer needed. diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/entity/BulkExportCollectionFileEntity.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/entity/BulkExportCollectionFileEntity.java index a257166230e..0dcf6851d52 100644 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/entity/BulkExportCollectionFileEntity.java +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/entity/BulkExportCollectionFileEntity.java @@ -19,20 +19,20 @@ */ package ca.uhn.fhir.jpa.entity; -import ca.uhn.fhir.jpa.model.entity.ForcedId; +import ca.uhn.fhir.jpa.model.entity.ResourceTable; +import jakarta.persistence.Column; +import jakarta.persistence.Entity; +import jakarta.persistence.FetchType; +import jakarta.persistence.ForeignKey; +import jakarta.persistence.GeneratedValue; +import jakarta.persistence.GenerationType; +import jakarta.persistence.Id; +import jakarta.persistence.JoinColumn; +import jakarta.persistence.ManyToOne; +import jakarta.persistence.SequenceGenerator; +import jakarta.persistence.Table; import java.io.Serializable; -import javax.persistence.Column; -import javax.persistence.Entity; -import javax.persistence.FetchType; -import javax.persistence.ForeignKey; -import javax.persistence.GeneratedValue; -import javax.persistence.GenerationType; -import javax.persistence.Id; -import javax.persistence.JoinColumn; -import javax.persistence.ManyToOne; -import javax.persistence.SequenceGenerator; -import javax.persistence.Table; /* * These classes are no longer needed. @@ -60,7 +60,7 @@ public class BulkExportCollectionFileEntity implements Serializable { foreignKey = @ForeignKey(name = "FK_BLKEXCOLFILE_COLLECT")) private BulkExportCollectionEntity myCollection; - @Column(name = "RES_ID", length = ForcedId.MAX_FORCED_ID_LENGTH, nullable = false) + @Column(name = "RES_ID", length = ResourceTable.MAX_FORCED_ID_LENGTH, nullable = false) private String myResourceId; public void setCollection(BulkExportCollectionEntity theCollection) { diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/entity/BulkExportJobEntity.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/entity/BulkExportJobEntity.java index a9bf341724a..8d70aeecbcd 100644 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/entity/BulkExportJobEntity.java +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/entity/BulkExportJobEntity.java @@ -19,6 +19,20 @@ */ package ca.uhn.fhir.jpa.entity; +import jakarta.persistence.Column; +import jakarta.persistence.Entity; +import jakarta.persistence.FetchType; +import jakarta.persistence.GeneratedValue; +import jakarta.persistence.GenerationType; +import jakarta.persistence.Id; +import jakarta.persistence.Index; +import jakarta.persistence.OneToMany; +import jakarta.persistence.SequenceGenerator; +import jakarta.persistence.Table; +import jakarta.persistence.Temporal; +import jakarta.persistence.TemporalType; +import jakarta.persistence.UniqueConstraint; +import jakarta.persistence.Version; import org.apache.commons.lang3.builder.ToStringBuilder; import org.apache.commons.lang3.builder.ToStringStyle; import org.hl7.fhir.r5.model.InstantType; @@ -27,20 +41,6 @@ import java.io.Serializable; import java.util.ArrayList; import java.util.Collection; import java.util.Date; -import javax.persistence.Column; -import javax.persistence.Entity; -import javax.persistence.FetchType; -import javax.persistence.GeneratedValue; -import javax.persistence.GenerationType; -import javax.persistence.Id; -import javax.persistence.Index; -import javax.persistence.OneToMany; -import javax.persistence.SequenceGenerator; -import javax.persistence.Table; -import javax.persistence.Temporal; -import javax.persistence.TemporalType; -import javax.persistence.UniqueConstraint; -import javax.persistence.Version; import static ca.uhn.fhir.rest.api.Constants.UUID_LENGTH; import static org.apache.commons.lang3.StringUtils.isNotBlank; diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/entity/BulkImportJobEntity.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/entity/BulkImportJobEntity.java index f3151ea2ba4..7fff537777f 100644 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/entity/BulkImportJobEntity.java +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/entity/BulkImportJobEntity.java @@ -22,22 +22,22 @@ package ca.uhn.fhir.jpa.entity; import ca.uhn.fhir.jpa.bulk.imprt.model.BulkImportJobJson; import ca.uhn.fhir.jpa.bulk.imprt.model.BulkImportJobStatusEnum; import ca.uhn.fhir.jpa.bulk.imprt.model.JobFileRowProcessingModeEnum; +import jakarta.persistence.Column; +import jakarta.persistence.Entity; +import jakarta.persistence.EnumType; +import jakarta.persistence.Enumerated; +import jakarta.persistence.GeneratedValue; +import jakarta.persistence.GenerationType; +import jakarta.persistence.Id; +import jakarta.persistence.SequenceGenerator; +import jakarta.persistence.Table; +import jakarta.persistence.Temporal; +import jakarta.persistence.TemporalType; +import jakarta.persistence.UniqueConstraint; +import jakarta.persistence.Version; import java.io.Serializable; import java.util.Date; -import javax.persistence.Column; -import javax.persistence.Entity; -import javax.persistence.EnumType; -import javax.persistence.Enumerated; -import javax.persistence.GeneratedValue; -import javax.persistence.GenerationType; -import javax.persistence.Id; -import javax.persistence.SequenceGenerator; -import javax.persistence.Table; -import javax.persistence.Temporal; -import javax.persistence.TemporalType; -import javax.persistence.UniqueConstraint; -import javax.persistence.Version; import static ca.uhn.fhir.rest.api.Constants.UUID_LENGTH; import static org.apache.commons.lang3.StringUtils.left; diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/entity/BulkImportJobFileEntity.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/entity/BulkImportJobFileEntity.java index 11a2998989f..00ee7169465 100644 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/entity/BulkImportJobFileEntity.java +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/entity/BulkImportJobFileEntity.java @@ -20,21 +20,21 @@ package ca.uhn.fhir.jpa.entity; import ca.uhn.fhir.jpa.bulk.imprt.model.BulkImportJobFileJson; +import jakarta.persistence.Column; +import jakarta.persistence.Entity; +import jakarta.persistence.ForeignKey; +import jakarta.persistence.GeneratedValue; +import jakarta.persistence.GenerationType; +import jakarta.persistence.Id; +import jakarta.persistence.Index; +import jakarta.persistence.JoinColumn; +import jakarta.persistence.Lob; +import jakarta.persistence.ManyToOne; +import jakarta.persistence.SequenceGenerator; +import jakarta.persistence.Table; import java.io.Serializable; import java.nio.charset.StandardCharsets; -import javax.persistence.Column; -import javax.persistence.Entity; -import javax.persistence.ForeignKey; -import javax.persistence.GeneratedValue; -import javax.persistence.GenerationType; -import javax.persistence.Id; -import javax.persistence.Index; -import javax.persistence.JoinColumn; -import javax.persistence.Lob; -import javax.persistence.ManyToOne; -import javax.persistence.SequenceGenerator; -import javax.persistence.Table; import static org.apache.commons.lang3.StringUtils.left; diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/entity/HapiFhirEnversRevision.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/entity/HapiFhirEnversRevision.java index 9daa452e844..03417a1c66a 100644 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/entity/HapiFhirEnversRevision.java +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/entity/HapiFhirEnversRevision.java @@ -19,6 +19,13 @@ */ package ca.uhn.fhir.jpa.entity; +import jakarta.persistence.Column; +import jakarta.persistence.Entity; +import jakarta.persistence.GeneratedValue; +import jakarta.persistence.GenerationType; +import jakarta.persistence.Id; +import jakarta.persistence.SequenceGenerator; +import jakarta.persistence.Table; import org.apache.commons.lang3.builder.ToStringBuilder; import org.hibernate.envers.RevisionEntity; import org.hibernate.envers.RevisionNumber; @@ -26,13 +33,6 @@ import org.hibernate.envers.RevisionTimestamp; import java.io.Serializable; import java.util.Date; -import javax.persistence.Column; -import javax.persistence.Entity; -import javax.persistence.GeneratedValue; -import javax.persistence.GenerationType; -import javax.persistence.Id; -import javax.persistence.SequenceGenerator; -import javax.persistence.Table; /** * This class exists strictly to override the default names used to generate Hibernate Envers revision table. diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/entity/MdmLink.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/entity/MdmLink.java index 24e5063ab0c..3294240982c 100644 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/entity/MdmLink.java +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/entity/MdmLink.java @@ -26,29 +26,31 @@ import ca.uhn.fhir.mdm.api.IMdmLink; import ca.uhn.fhir.mdm.api.MdmLinkSourceEnum; import ca.uhn.fhir.mdm.api.MdmMatchResultEnum; import com.fasterxml.jackson.annotation.JsonIgnore; +import jakarta.persistence.Column; +import jakarta.persistence.Entity; +import jakarta.persistence.EnumType; +import jakarta.persistence.Enumerated; +import jakarta.persistence.FetchType; +import jakarta.persistence.ForeignKey; +import jakarta.persistence.GeneratedValue; +import jakarta.persistence.GenerationType; +import jakarta.persistence.Id; +import jakarta.persistence.Index; +import jakarta.persistence.JoinColumn; +import jakarta.persistence.ManyToOne; +import jakarta.persistence.SequenceGenerator; +import jakarta.persistence.Table; +import jakarta.persistence.Temporal; +import jakarta.persistence.TemporalType; +import jakarta.persistence.UniqueConstraint; import org.apache.commons.lang3.builder.ToStringBuilder; +import org.hibernate.annotations.JdbcTypeCode; import org.hibernate.envers.AuditTable; import org.hibernate.envers.Audited; import org.hibernate.envers.NotAudited; +import org.hibernate.type.SqlTypes; import java.util.Date; -import javax.persistence.Column; -import javax.persistence.Entity; -import javax.persistence.EnumType; -import javax.persistence.Enumerated; -import javax.persistence.FetchType; -import javax.persistence.ForeignKey; -import javax.persistence.GeneratedValue; -import javax.persistence.GenerationType; -import javax.persistence.Id; -import javax.persistence.Index; -import javax.persistence.JoinColumn; -import javax.persistence.ManyToOne; -import javax.persistence.SequenceGenerator; -import javax.persistence.Table; -import javax.persistence.Temporal; -import javax.persistence.TemporalType; -import javax.persistence.UniqueConstraint; @Entity @Table( @@ -64,7 +66,10 @@ import javax.persistence.UniqueConstraint; @Index(name = "IDX_EMPI_MATCH_TGT_VER", columnList = "MATCH_RESULT, TARGET_PID, VERSION"), // v---- this one @Index(name = "IDX_EMPI_GR_TGT", columnList = "GOLDEN_RESOURCE_PID, TARGET_PID"), - @Index(name = "FK_EMPI_LINK_TARGET", columnList = "TARGET_PID") + @Index(name = "FK_EMPI_LINK_TARGET", columnList = "TARGET_PID"), + // indexes for metrics + @Index(name = "IDX_EMPI_TGT_MR_LS", columnList = "TARGET_TYPE, MATCH_RESULT, LINK_SOURCE"), + @Index(name = "IDX_EMPI_TGT_MR_SCORE", columnList = "TARGET_TYPE, MATCH_RESULT, SCORE") }) @Audited // This is the table name generated by default by envers, but we set it explicitly for clarity @@ -136,10 +141,12 @@ public class MdmLink extends AuditableBasePartitionable implements IMdmLink toIncList(boolean theWantReverse) { + private Set toIncList(boolean theWantReverse, boolean theIncludeAll, boolean theWantIterate) { HashSet retVal = new HashSet<>(); for (SearchInclude next : getIncludes()) { if (theWantReverse == next.isReverse()) { - retVal.add(new Include(next.getInclude(), next.isRecurse())); + if (theIncludeAll) { + retVal.add(new Include(next.getInclude(), next.isRecurse())); + } else { + if (theWantIterate == next.isRecurse()) { + retVal.add(new Include(next.getInclude(), next.isRecurse())); + } + } } } return Collections.unmodifiableSet(retVal); } + private Set toIncList(boolean theWantReverse) { + return toIncList(theWantReverse, true, true); + } + public Set toIncludesList() { return toIncList(false); } @@ -426,6 +439,14 @@ public class Search implements ICachedSearchDetails, Serializable { return toIncList(true); } + public Set toIncludesList(boolean iterate) { + return toIncList(false, false, iterate); + } + + public Set toRevIncludesList(boolean iterate) { + return toIncList(true, false, iterate); + } + public void addInclude(SearchInclude theInclude) { getIncludes().add(theInclude); } diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/entity/SearchInclude.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/entity/SearchInclude.java index 8cf79736d7e..523a2961d5a 100644 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/entity/SearchInclude.java +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/entity/SearchInclude.java @@ -19,18 +19,19 @@ */ package ca.uhn.fhir.jpa.entity; +import jakarta.persistence.Column; +import jakarta.persistence.Entity; +import jakarta.persistence.ForeignKey; +import jakarta.persistence.GeneratedValue; +import jakarta.persistence.GenerationType; +import jakarta.persistence.Id; +import jakarta.persistence.Index; +import jakarta.persistence.JoinColumn; +import jakarta.persistence.ManyToOne; +import jakarta.persistence.SequenceGenerator; +import jakarta.persistence.Table; + import java.io.Serializable; -import javax.persistence.Column; -import javax.persistence.Entity; -import javax.persistence.ForeignKey; -import javax.persistence.GeneratedValue; -import javax.persistence.GenerationType; -import javax.persistence.Id; -import javax.persistence.Index; -import javax.persistence.JoinColumn; -import javax.persistence.ManyToOne; -import javax.persistence.SequenceGenerator; -import javax.persistence.Table; // @formatter:off @Entity diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/entity/SearchResult.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/entity/SearchResult.java index 5dc807554eb..a2deca13b38 100644 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/entity/SearchResult.java +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/entity/SearchResult.java @@ -19,11 +19,11 @@ */ package ca.uhn.fhir.jpa.entity; +import jakarta.persistence.*; import org.apache.commons.lang3.Validate; import org.apache.commons.lang3.builder.ToStringBuilder; import java.io.Serializable; -import javax.persistence.*; @Entity @Table( @@ -37,21 +37,22 @@ public class SearchResult implements Serializable { private static final long serialVersionUID = 1L; + @Deprecated(since = "6.10", forRemoval = true) // migrating to composite PK on searchPid,Order @GeneratedValue(strategy = GenerationType.AUTO, generator = "SEQ_SEARCH_RES") @SequenceGenerator(name = "SEQ_SEARCH_RES", sequenceName = "SEQ_SEARCH_RES") @Id @Column(name = "PID") private Long myId; - @Column(name = "SEARCH_ORDER", nullable = false, insertable = true, updatable = false) + @Column(name = "SEARCH_PID", insertable = true, updatable = false, nullable = false) + private Long mySearchPid; + + @Column(name = "SEARCH_ORDER", insertable = true, updatable = false, nullable = false) private int myOrder; @Column(name = "RESOURCE_PID", insertable = true, updatable = false, nullable = false) private Long myResourcePid; - @Column(name = "SEARCH_PID", insertable = true, updatable = false, nullable = false) - private Long mySearchPid; - /** * Constructor */ diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/entity/SubscriptionTable.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/entity/SubscriptionTable.java index 63b8f6a6588..a3a7b449952 100644 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/entity/SubscriptionTable.java +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/entity/SubscriptionTable.java @@ -20,9 +20,9 @@ package ca.uhn.fhir.jpa.entity; import ca.uhn.fhir.jpa.model.entity.ResourceTable; +import jakarta.persistence.*; import java.util.Date; -import javax.persistence.*; @Entity @Table( diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/entity/TermCodeSystem.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/entity/TermCodeSystem.java index 8c4a26b821a..847b5ddfb04 100644 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/entity/TermCodeSystem.java +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/entity/TermCodeSystem.java @@ -21,14 +21,14 @@ package ca.uhn.fhir.jpa.entity; import ca.uhn.fhir.jpa.model.entity.ResourceTable; import ca.uhn.fhir.util.ValidateUtil; +import jakarta.annotation.Nonnull; +import jakarta.persistence.*; import org.apache.commons.lang3.builder.EqualsBuilder; import org.apache.commons.lang3.builder.HashCodeBuilder; import org.apache.commons.lang3.builder.ToStringBuilder; import org.apache.commons.lang3.builder.ToStringStyle; import java.io.Serializable; -import javax.annotation.Nonnull; -import javax.persistence.*; import static org.apache.commons.lang3.StringUtils.left; import static org.apache.commons.lang3.StringUtils.length; diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/entity/TermCodeSystemVersion.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/entity/TermCodeSystemVersion.java index cfb5bb36e10..b3a61543eae 100644 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/entity/TermCodeSystemVersion.java +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/entity/TermCodeSystemVersion.java @@ -21,6 +21,21 @@ package ca.uhn.fhir.jpa.entity; import ca.uhn.fhir.jpa.model.entity.ResourceTable; import ca.uhn.fhir.util.ValidateUtil; +import jakarta.persistence.Column; +import jakarta.persistence.Entity; +import jakarta.persistence.FetchType; +import jakarta.persistence.ForeignKey; +import jakarta.persistence.GeneratedValue; +import jakarta.persistence.GenerationType; +import jakarta.persistence.Id; +import jakarta.persistence.Index; +import jakarta.persistence.JoinColumn; +import jakarta.persistence.ManyToOne; +import jakarta.persistence.OneToMany; +import jakarta.persistence.OneToOne; +import jakarta.persistence.SequenceGenerator; +import jakarta.persistence.Table; +import jakarta.persistence.UniqueConstraint; import org.apache.commons.lang3.builder.EqualsBuilder; import org.apache.commons.lang3.builder.HashCodeBuilder; import org.apache.commons.lang3.builder.ToStringBuilder; @@ -29,21 +44,6 @@ import org.apache.commons.lang3.builder.ToStringStyle; import java.io.Serializable; import java.util.ArrayList; import java.util.Collection; -import javax.persistence.Column; -import javax.persistence.Entity; -import javax.persistence.FetchType; -import javax.persistence.ForeignKey; -import javax.persistence.GeneratedValue; -import javax.persistence.GenerationType; -import javax.persistence.Id; -import javax.persistence.Index; -import javax.persistence.JoinColumn; -import javax.persistence.ManyToOne; -import javax.persistence.OneToMany; -import javax.persistence.OneToOne; -import javax.persistence.SequenceGenerator; -import javax.persistence.Table; -import javax.persistence.UniqueConstraint; import static org.apache.commons.lang3.StringUtils.length; diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/entity/TermConcept.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/entity/TermConcept.java index 1d615c4758f..2bc117ad202 100644 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/entity/TermConcept.java +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/entity/TermConcept.java @@ -24,6 +24,26 @@ import ca.uhn.fhir.i18n.Msg; import ca.uhn.fhir.jpa.entity.TermConceptParentChildLink.RelationshipTypeEnum; import ca.uhn.fhir.jpa.search.DeferConceptIndexingRoutingBinder; import ca.uhn.fhir.util.ValidateUtil; +import jakarta.annotation.Nonnull; +import jakarta.persistence.Column; +import jakarta.persistence.Entity; +import jakarta.persistence.FetchType; +import jakarta.persistence.ForeignKey; +import jakarta.persistence.GeneratedValue; +import jakarta.persistence.GenerationType; +import jakarta.persistence.Id; +import jakarta.persistence.Index; +import jakarta.persistence.JoinColumn; +import jakarta.persistence.Lob; +import jakarta.persistence.ManyToOne; +import jakarta.persistence.OneToMany; +import jakarta.persistence.PrePersist; +import jakarta.persistence.PreUpdate; +import jakarta.persistence.SequenceGenerator; +import jakarta.persistence.Table; +import jakarta.persistence.Temporal; +import jakarta.persistence.TemporalType; +import jakarta.persistence.UniqueConstraint; import org.apache.commons.lang3.Validate; import org.apache.commons.lang3.builder.EqualsBuilder; import org.apache.commons.lang3.builder.HashCodeBuilder; @@ -47,26 +67,6 @@ import java.util.HashSet; import java.util.List; import java.util.Set; import java.util.stream.Collectors; -import javax.annotation.Nonnull; -import javax.persistence.Column; -import javax.persistence.Entity; -import javax.persistence.FetchType; -import javax.persistence.ForeignKey; -import javax.persistence.GeneratedValue; -import javax.persistence.GenerationType; -import javax.persistence.Id; -import javax.persistence.Index; -import javax.persistence.JoinColumn; -import javax.persistence.Lob; -import javax.persistence.ManyToOne; -import javax.persistence.OneToMany; -import javax.persistence.PrePersist; -import javax.persistence.PreUpdate; -import javax.persistence.SequenceGenerator; -import javax.persistence.Table; -import javax.persistence.Temporal; -import javax.persistence.TemporalType; -import javax.persistence.UniqueConstraint; import static org.apache.commons.lang3.StringUtils.left; import static org.apache.commons.lang3.StringUtils.length; diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/entity/TermConceptDesignation.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/entity/TermConceptDesignation.java index c430577e061..14c1703dd06 100644 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/entity/TermConceptDesignation.java +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/entity/TermConceptDesignation.java @@ -20,23 +20,23 @@ package ca.uhn.fhir.jpa.entity; import ca.uhn.fhir.util.ValidateUtil; +import jakarta.annotation.Nonnull; +import jakarta.persistence.Column; +import jakarta.persistence.Entity; +import jakarta.persistence.FetchType; +import jakarta.persistence.ForeignKey; +import jakarta.persistence.GeneratedValue; +import jakarta.persistence.GenerationType; +import jakarta.persistence.Id; +import jakarta.persistence.Index; +import jakarta.persistence.JoinColumn; +import jakarta.persistence.ManyToOne; +import jakarta.persistence.SequenceGenerator; +import jakarta.persistence.Table; import org.apache.commons.lang3.builder.ToStringBuilder; import org.apache.commons.lang3.builder.ToStringStyle; import java.io.Serializable; -import javax.annotation.Nonnull; -import javax.persistence.Column; -import javax.persistence.Entity; -import javax.persistence.FetchType; -import javax.persistence.ForeignKey; -import javax.persistence.GeneratedValue; -import javax.persistence.GenerationType; -import javax.persistence.Id; -import javax.persistence.Index; -import javax.persistence.JoinColumn; -import javax.persistence.ManyToOne; -import javax.persistence.SequenceGenerator; -import javax.persistence.Table; import static org.apache.commons.lang3.StringUtils.left; import static org.apache.commons.lang3.StringUtils.length; diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/entity/TermConceptMap.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/entity/TermConceptMap.java index 0b0a511f232..548af3a74c9 100644 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/entity/TermConceptMap.java +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/entity/TermConceptMap.java @@ -21,14 +21,14 @@ package ca.uhn.fhir.jpa.entity; import ca.uhn.fhir.jpa.model.entity.ResourceTable; import ca.uhn.fhir.util.ValidateUtil; +import jakarta.annotation.Nonnull; +import jakarta.persistence.*; import org.apache.commons.lang3.builder.ToStringBuilder; import org.apache.commons.lang3.builder.ToStringStyle; import java.io.Serializable; import java.util.ArrayList; import java.util.List; -import javax.annotation.Nonnull; -import javax.persistence.*; import static org.apache.commons.lang3.StringUtils.length; diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/entity/TermConceptMapGroup.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/entity/TermConceptMapGroup.java index 4a8aaab630e..6a5de9a94bc 100644 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/entity/TermConceptMapGroup.java +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/entity/TermConceptMapGroup.java @@ -20,14 +20,14 @@ package ca.uhn.fhir.jpa.entity; import ca.uhn.fhir.util.ValidateUtil; +import jakarta.annotation.Nonnull; +import jakarta.persistence.*; import org.apache.commons.lang3.builder.ToStringBuilder; import org.apache.commons.lang3.builder.ToStringStyle; import java.io.Serializable; import java.util.ArrayList; import java.util.List; -import javax.annotation.Nonnull; -import javax.persistence.*; import static org.apache.commons.lang3.StringUtils.length; diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/entity/TermConceptMapGroupElement.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/entity/TermConceptMapGroupElement.java index 9c5aae83d37..b06f09a7284 100644 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/entity/TermConceptMapGroupElement.java +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/entity/TermConceptMapGroupElement.java @@ -20,6 +20,8 @@ package ca.uhn.fhir.jpa.entity; import ca.uhn.fhir.util.ValidateUtil; +import jakarta.annotation.Nonnull; +import jakarta.persistence.*; import org.apache.commons.lang3.builder.EqualsBuilder; import org.apache.commons.lang3.builder.HashCodeBuilder; import org.apache.commons.lang3.builder.ToStringBuilder; @@ -28,8 +30,6 @@ import org.apache.commons.lang3.builder.ToStringStyle; import java.io.Serializable; import java.util.ArrayList; import java.util.List; -import javax.annotation.Nonnull; -import javax.persistence.*; import static org.apache.commons.lang3.StringUtils.left; import static org.apache.commons.lang3.StringUtils.length; diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/entity/TermConceptMapGroupElementTarget.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/entity/TermConceptMapGroupElementTarget.java index fd51394c985..e38c9af6e42 100644 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/entity/TermConceptMapGroupElementTarget.java +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/entity/TermConceptMapGroupElementTarget.java @@ -20,6 +20,8 @@ package ca.uhn.fhir.jpa.entity; import ca.uhn.fhir.util.ValidateUtil; +import jakarta.annotation.Nonnull; +import jakarta.persistence.*; import org.apache.commons.lang3.builder.EqualsBuilder; import org.apache.commons.lang3.builder.HashCodeBuilder; import org.apache.commons.lang3.builder.ToStringBuilder; @@ -27,8 +29,6 @@ import org.apache.commons.lang3.builder.ToStringStyle; import org.hl7.fhir.r4.model.Enumerations.ConceptMapEquivalence; import java.io.Serializable; -import javax.annotation.Nonnull; -import javax.persistence.*; import static org.apache.commons.lang3.StringUtils.length; diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/entity/TermConceptParentChildLink.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/entity/TermConceptParentChildLink.java index 74ff7f63040..1ecca2cf2dd 100644 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/entity/TermConceptParentChildLink.java +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/entity/TermConceptParentChildLink.java @@ -19,23 +19,25 @@ */ package ca.uhn.fhir.jpa.entity; +import jakarta.persistence.Column; +import jakarta.persistence.Entity; +import jakarta.persistence.EnumType; +import jakarta.persistence.Enumerated; +import jakarta.persistence.FetchType; +import jakarta.persistence.ForeignKey; +import jakarta.persistence.GeneratedValue; +import jakarta.persistence.GenerationType; +import jakarta.persistence.Id; +import jakarta.persistence.Index; +import jakarta.persistence.JoinColumn; +import jakarta.persistence.ManyToOne; +import jakarta.persistence.SequenceGenerator; +import jakarta.persistence.Table; +import org.hibernate.annotations.JdbcTypeCode; import org.hibernate.search.mapper.pojo.mapping.definition.annotation.FullTextField; +import org.hibernate.type.SqlTypes; import java.io.Serializable; -import javax.persistence.Column; -import javax.persistence.Entity; -import javax.persistence.EnumType; -import javax.persistence.Enumerated; -import javax.persistence.FetchType; -import javax.persistence.ForeignKey; -import javax.persistence.GeneratedValue; -import javax.persistence.GenerationType; -import javax.persistence.Id; -import javax.persistence.Index; -import javax.persistence.JoinColumn; -import javax.persistence.ManyToOne; -import javax.persistence.SequenceGenerator; -import javax.persistence.Table; @Entity @Table( @@ -90,6 +92,7 @@ public class TermConceptParentChildLink implements Serializable { @Enumerated(EnumType.ORDINAL) @Column(name = "REL_TYPE", length = 5, nullable = true) + @JdbcTypeCode(SqlTypes.INTEGER) private RelationshipTypeEnum myRelationshipType; @Override diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/entity/TermConceptProperty.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/entity/TermConceptProperty.java index ca7a104bf31..1aecb591068 100644 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/entity/TermConceptProperty.java +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/entity/TermConceptProperty.java @@ -20,33 +20,37 @@ package ca.uhn.fhir.jpa.entity; import ca.uhn.fhir.util.ValidateUtil; +import jakarta.annotation.Nonnull; +import jakarta.persistence.Column; +import jakarta.persistence.Entity; +import jakarta.persistence.EnumType; +import jakarta.persistence.Enumerated; +import jakarta.persistence.FetchType; +import jakarta.persistence.ForeignKey; +import jakarta.persistence.GeneratedValue; +import jakarta.persistence.GenerationType; +import jakarta.persistence.Id; +import jakarta.persistence.Index; +import jakarta.persistence.JoinColumn; +import jakarta.persistence.Lob; +import jakarta.persistence.ManyToOne; +import jakarta.persistence.SequenceGenerator; +import jakarta.persistence.Table; import org.apache.commons.lang3.Validate; import org.apache.commons.lang3.builder.EqualsBuilder; import org.apache.commons.lang3.builder.HashCodeBuilder; import org.apache.commons.lang3.builder.ToStringBuilder; import org.apache.commons.lang3.builder.ToStringStyle; +import org.hibernate.annotations.JdbcTypeCode; import org.hibernate.search.engine.backend.types.Projectable; import org.hibernate.search.engine.backend.types.Searchable; import org.hibernate.search.mapper.pojo.mapping.definition.annotation.FullTextField; import org.hibernate.search.mapper.pojo.mapping.definition.annotation.GenericField; +import org.hibernate.type.SqlTypes; import org.hibernate.validator.constraints.NotBlank; import java.io.Serializable; import java.nio.charset.StandardCharsets; -import javax.annotation.Nonnull; -import javax.persistence.Column; -import javax.persistence.Entity; -import javax.persistence.FetchType; -import javax.persistence.ForeignKey; -import javax.persistence.GeneratedValue; -import javax.persistence.GenerationType; -import javax.persistence.Id; -import javax.persistence.Index; -import javax.persistence.JoinColumn; -import javax.persistence.Lob; -import javax.persistence.ManyToOne; -import javax.persistence.SequenceGenerator; -import javax.persistence.Table; import static org.apache.commons.lang3.StringUtils.left; import static org.apache.commons.lang3.StringUtils.length; @@ -106,7 +110,9 @@ public class TermConceptProperty implements Serializable { @Lob() private byte[] myValueLob; + @Enumerated(EnumType.ORDINAL) @Column(name = "PROP_TYPE", nullable = false, length = MAX_PROPTYPE_ENUM_LENGTH) + @JdbcTypeCode(SqlTypes.INTEGER) private TermConceptPropertyTypeEnum myType; /** diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/entity/TermValueSet.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/entity/TermValueSet.java index 80060822983..dd0d3cdfd39 100644 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/entity/TermValueSet.java +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/entity/TermValueSet.java @@ -21,6 +21,26 @@ package ca.uhn.fhir.jpa.entity; import ca.uhn.fhir.jpa.model.entity.ResourceTable; import ca.uhn.fhir.util.ValidateUtil; +import jakarta.annotation.Nonnull; +import jakarta.persistence.Column; +import jakarta.persistence.Entity; +import jakarta.persistence.EnumType; +import jakarta.persistence.Enumerated; +import jakarta.persistence.FetchType; +import jakarta.persistence.ForeignKey; +import jakarta.persistence.GeneratedValue; +import jakarta.persistence.GenerationType; +import jakarta.persistence.Id; +import jakarta.persistence.Index; +import jakarta.persistence.JoinColumn; +import jakarta.persistence.OneToMany; +import jakarta.persistence.OneToOne; +import jakarta.persistence.SequenceGenerator; +import jakarta.persistence.Table; +import jakarta.persistence.Temporal; +import jakarta.persistence.TemporalType; +import jakarta.persistence.Transient; +import jakarta.persistence.UniqueConstraint; import org.apache.commons.lang3.builder.EqualsBuilder; import org.apache.commons.lang3.builder.ToStringBuilder; import org.apache.commons.lang3.builder.ToStringStyle; @@ -30,26 +50,6 @@ import java.io.Serializable; import java.util.ArrayList; import java.util.Date; import java.util.List; -import javax.annotation.Nonnull; -import javax.persistence.Column; -import javax.persistence.Entity; -import javax.persistence.EnumType; -import javax.persistence.Enumerated; -import javax.persistence.FetchType; -import javax.persistence.ForeignKey; -import javax.persistence.GeneratedValue; -import javax.persistence.GenerationType; -import javax.persistence.Id; -import javax.persistence.Index; -import javax.persistence.JoinColumn; -import javax.persistence.OneToMany; -import javax.persistence.OneToOne; -import javax.persistence.SequenceGenerator; -import javax.persistence.Table; -import javax.persistence.Temporal; -import javax.persistence.TemporalType; -import javax.persistence.Transient; -import javax.persistence.UniqueConstraint; import static org.apache.commons.lang3.StringUtils.left; import static org.apache.commons.lang3.StringUtils.length; diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/entity/TermValueSetConcept.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/entity/TermValueSetConcept.java index 9f3a6a7b434..aad16cce80f 100644 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/entity/TermValueSetConcept.java +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/entity/TermValueSetConcept.java @@ -20,6 +20,22 @@ package ca.uhn.fhir.jpa.entity; import ca.uhn.fhir.util.ValidateUtil; +import jakarta.annotation.Nonnull; +import jakarta.persistence.Column; +import jakarta.persistence.Entity; +import jakarta.persistence.FetchType; +import jakarta.persistence.ForeignKey; +import jakarta.persistence.GeneratedValue; +import jakarta.persistence.GenerationType; +import jakarta.persistence.Id; +import jakarta.persistence.JoinColumn; +import jakarta.persistence.Lob; +import jakarta.persistence.ManyToOne; +import jakarta.persistence.OneToMany; +import jakarta.persistence.SequenceGenerator; +import jakarta.persistence.Table; +import jakarta.persistence.Transient; +import jakarta.persistence.UniqueConstraint; import org.apache.commons.lang3.builder.EqualsBuilder; import org.apache.commons.lang3.builder.HashCodeBuilder; import org.apache.commons.lang3.builder.ToStringBuilder; @@ -28,22 +44,6 @@ import org.apache.commons.lang3.builder.ToStringStyle; import java.io.Serializable; import java.util.ArrayList; import java.util.List; -import javax.annotation.Nonnull; -import javax.persistence.Column; -import javax.persistence.Entity; -import javax.persistence.FetchType; -import javax.persistence.ForeignKey; -import javax.persistence.GeneratedValue; -import javax.persistence.GenerationType; -import javax.persistence.Id; -import javax.persistence.JoinColumn; -import javax.persistence.Lob; -import javax.persistence.ManyToOne; -import javax.persistence.OneToMany; -import javax.persistence.SequenceGenerator; -import javax.persistence.Table; -import javax.persistence.Transient; -import javax.persistence.UniqueConstraint; import static org.apache.commons.lang3.StringUtils.left; import static org.apache.commons.lang3.StringUtils.length; diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/entity/TermValueSetConceptDesignation.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/entity/TermValueSetConceptDesignation.java index 0e7bb151c6f..76e2dde2d38 100644 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/entity/TermValueSetConceptDesignation.java +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/entity/TermValueSetConceptDesignation.java @@ -20,14 +20,26 @@ package ca.uhn.fhir.jpa.entity; import ca.uhn.fhir.util.ValidateUtil; +import jakarta.annotation.Nonnull; +import jakarta.persistence.Column; +import jakarta.persistence.Entity; +import jakarta.persistence.FetchType; +import jakarta.persistence.ForeignKey; +import jakarta.persistence.GeneratedValue; +import jakarta.persistence.GenerationType; +import jakarta.persistence.Id; +import jakarta.persistence.Index; +import jakarta.persistence.JoinColumn; +import jakarta.persistence.ManyToOne; +import jakarta.persistence.SequenceGenerator; +import jakarta.persistence.Table; +import jakarta.persistence.Transient; import org.apache.commons.lang3.builder.EqualsBuilder; import org.apache.commons.lang3.builder.HashCodeBuilder; import org.apache.commons.lang3.builder.ToStringBuilder; import org.apache.commons.lang3.builder.ToStringStyle; import java.io.Serializable; -import javax.annotation.Nonnull; -import javax.persistence.*; import static org.apache.commons.lang3.StringUtils.left; import static org.apache.commons.lang3.StringUtils.length; diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/entity/TermValueSetConceptView.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/entity/TermValueSetConceptView.java index ca1d099e66d..29ef27522d1 100644 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/entity/TermValueSetConceptView.java +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/entity/TermValueSetConceptView.java @@ -21,6 +21,10 @@ package ca.uhn.fhir.jpa.entity; import ca.uhn.fhir.i18n.Msg; import ca.uhn.fhir.rest.server.exceptions.InternalErrorException; +import jakarta.persistence.Column; +import jakarta.persistence.Entity; +import jakarta.persistence.Id; +import jakarta.persistence.Lob; import org.apache.commons.io.IOUtils; import org.hibernate.annotations.Immutable; import org.hibernate.annotations.Subselect; @@ -30,10 +34,6 @@ import java.io.Reader; import java.io.Serializable; import java.sql.Clob; import java.sql.SQLException; -import javax.persistence.Column; -import javax.persistence.Entity; -import javax.persistence.Id; -import javax.persistence.Lob; @Entity @Immutable diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/entity/TermValueSetConceptViewOracle.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/entity/TermValueSetConceptViewOracle.java index 5419c8affaf..7452e6c506a 100644 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/entity/TermValueSetConceptViewOracle.java +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/entity/TermValueSetConceptViewOracle.java @@ -21,6 +21,10 @@ package ca.uhn.fhir.jpa.entity; import ca.uhn.fhir.i18n.Msg; import ca.uhn.fhir.rest.server.exceptions.InternalErrorException; +import jakarta.persistence.Column; +import jakarta.persistence.Entity; +import jakarta.persistence.Id; +import jakarta.persistence.Lob; import org.apache.commons.io.IOUtils; import org.hibernate.annotations.Immutable; import org.hibernate.annotations.Subselect; @@ -30,10 +34,6 @@ import java.io.Reader; import java.io.Serializable; import java.sql.Clob; import java.sql.SQLException; -import javax.persistence.Column; -import javax.persistence.Entity; -import javax.persistence.Id; -import javax.persistence.Lob; @Entity @Immutable diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/esr/ExternallyStoredResourceServiceRegistry.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/esr/ExternallyStoredResourceServiceRegistry.java index a8a5caa06c0..928ae9e5b16 100644 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/esr/ExternallyStoredResourceServiceRegistry.java +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/esr/ExternallyStoredResourceServiceRegistry.java @@ -19,11 +19,11 @@ */ package ca.uhn.fhir.jpa.esr; +import jakarta.annotation.Nonnull; import org.apache.commons.lang3.Validate; import java.util.HashMap; import java.util.Map; -import javax.annotation.Nonnull; import static org.apache.commons.lang3.StringUtils.defaultString; diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/graphql/GraphQLProviderWithIntrospection.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/graphql/GraphQLProviderWithIntrospection.java index a1f918a9018..60ded14d06a 100644 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/graphql/GraphQLProviderWithIntrospection.java +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/graphql/GraphQLProviderWithIntrospection.java @@ -46,6 +46,8 @@ import graphql.schema.idl.SchemaGenerator; import graphql.schema.idl.SchemaParser; import graphql.schema.idl.TypeDefinitionRegistry; import graphql.schema.idl.TypeRuntimeWiring; +import jakarta.annotation.Nonnull; +import jakarta.annotation.Nullable; import org.apache.commons.io.output.StringBuilderWriter; import org.apache.commons.lang3.Validate; import org.hl7.fhir.common.hapi.validation.validator.VersionSpecificWorkerContextWrapper; @@ -68,8 +70,6 @@ import java.util.HashSet; import java.util.List; import java.util.Map; import java.util.stream.Collectors; -import javax.annotation.Nonnull; -import javax.annotation.Nullable; import static ca.uhn.fhir.util.MessageSupplier.msg; diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/interceptor/CascadingDeleteInterceptor.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/interceptor/CascadingDeleteInterceptor.java index a460257089e..6fdf75f8ccd 100644 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/interceptor/CascadingDeleteInterceptor.java +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/interceptor/CascadingDeleteInterceptor.java @@ -34,6 +34,8 @@ import ca.uhn.fhir.rest.api.server.ResponseDetails; import ca.uhn.fhir.rest.api.server.storage.TransactionDetails; import ca.uhn.fhir.rest.server.RestfulServerUtils; import ca.uhn.fhir.util.OperationOutcomeUtil; +import jakarta.annotation.Nonnull; +import jakarta.annotation.Nullable; import org.apache.commons.lang3.Validate; import org.hl7.fhir.instance.model.api.IBaseOperationOutcome; import org.hl7.fhir.instance.model.api.IBaseResource; @@ -43,8 +45,6 @@ import org.slf4j.LoggerFactory; import java.util.ArrayList; import java.util.List; -import javax.annotation.Nonnull; -import javax.annotation.Nullable; import static ca.uhn.fhir.jpa.delete.DeleteConflictService.MAX_RETRY_ATTEMPTS; import static org.apache.commons.lang3.StringUtils.isNotBlank; diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/migrate/tasks/HapiFhirJpaMigrationTasks.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/migrate/tasks/HapiFhirJpaMigrationTasks.java index b68af9bc768..dcc7c9b9c6d 100644 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/migrate/tasks/HapiFhirJpaMigrationTasks.java +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/migrate/tasks/HapiFhirJpaMigrationTasks.java @@ -28,6 +28,7 @@ import ca.uhn.fhir.jpa.migrate.taskdef.ArbitrarySqlTask; import ca.uhn.fhir.jpa.migrate.taskdef.CalculateHashesTask; import ca.uhn.fhir.jpa.migrate.taskdef.CalculateOrdinalDatesTask; import ca.uhn.fhir.jpa.migrate.taskdef.ColumnTypeEnum; +import ca.uhn.fhir.jpa.migrate.taskdef.ForceIdMigrationCopyTask; import ca.uhn.fhir.jpa.migrate.tasks.api.BaseMigrationTasks; import ca.uhn.fhir.jpa.migrate.tasks.api.Builder; import ca.uhn.fhir.jpa.model.config.PartitionSettings; @@ -61,6 +62,28 @@ public class HapiFhirJpaMigrationTasks extends BaseMigrationTasks { // H2, Derby, MariaDB, and MySql automatically add indexes to foreign keys public static final DriverTypeEnum[] NON_AUTOMATIC_FK_INDEX_PLATFORMS = new DriverTypeEnum[] {DriverTypeEnum.POSTGRES_9_4, DriverTypeEnum.ORACLE_12C, DriverTypeEnum.MSSQL_2012}; + private static final String QUERY_FOR_COLUMN_COLLATION_TEMPLATE = "WITH defcoll AS (\n" + + " SELECT datcollate AS coll\n" + + " FROM pg_database\n" + + " WHERE datname = current_database())\n" + + ", collation_by_column AS (\n" + + " SELECT a.attname,\n" + + " CASE WHEN c.collname = 'default'\n" + + " THEN defcoll.coll\n" + + " ELSE c.collname\n" + + " END AS my_collation\n" + + " FROM pg_attribute AS a\n" + + " CROSS JOIN defcoll\n" + + " LEFT JOIN pg_collation AS c ON a.attcollation = c.oid\n" + + " WHERE a.attrelid = '%s'::regclass\n" + + " AND a.attnum > 0\n" + + " AND attname = '%s'\n" + + ")\n" + + "SELECT TRUE as result\n" + + "FROM collation_by_column\n" + + "WHERE EXISTS (SELECT 1\n" + + " FROM collation_by_column\n" + + " WHERE my_collation != 'C')"; private final Set myFlags; /** @@ -93,6 +116,77 @@ public class HapiFhirJpaMigrationTasks extends BaseMigrationTasks { init640_after_20230126(); init660(); init680(); + init700(); + } + + protected void init700() { + /* ************************************************ + * Start of 6.10 migrations + *********************************************** */ + + Builder version = forVersion(VersionEnum.V7_0_0); + + // new indices on MdmLink + Builder.BuilderWithTableName mdmLinkTable = version.onTable("MPI_LINK"); + + mdmLinkTable + .addIndex("20230911.1", "IDX_EMPI_TGT_MR_LS") + .unique(false) + .withColumns("TARGET_TYPE", "MATCH_RESULT", "LINK_SOURCE"); + mdmLinkTable + .addIndex("20230911.2", "IDX_EMPi_TGT_MR_SCore") + .unique(false) + .withColumns("TARGET_TYPE", "MATCH_RESULT", "SCORE"); + + // Move forced_id constraints to hfj_resource and the new fhir_id column + // Note: we leave the HFJ_FORCED_ID.IDX_FORCEDID_TYPE_FID index in place to support old writers for a while. + version.addTask(new ForceIdMigrationCopyTask(version.getRelease(), "20231018.1")); + + Builder.BuilderWithTableName hfjResource = version.onTable("HFJ_RESOURCE"); + hfjResource.modifyColumn("20231018.2", "FHIR_ID").nonNullable(); + + hfjResource.dropIndex("20231027.1", "IDX_RES_FHIR_ID"); + hfjResource + .addIndex("20231027.2", "IDX_RES_TYPE_FHIR_ID") + .unique(true) + .online(true) + // include res_id and our deleted flag so we can satisfy Observation?_sort=_id from the index on + // platforms that support it. + .includeColumns("RES_ID, RES_DELETED_AT") + .withColumns("RES_TYPE", "FHIR_ID"); + + // For resolving references that don't supply the type. + hfjResource.addIndex("20231027.3", "IDX_RES_FHIR_ID").unique(false).withColumns("FHIR_ID"); + + Builder.BuilderWithTableName batch2JobInstanceTable = version.onTable("BT2_JOB_INSTANCE"); + + batch2JobInstanceTable.addColumn("20231128.1", "USER_NAME").nullable().type(ColumnTypeEnum.STRING, 200); + + batch2JobInstanceTable.addColumn("20231128.2", "CLIENT_ID").nullable().type(ColumnTypeEnum.STRING, 200); + + { + version.executeRawSql( + "20231212.1", + "CREATE INDEX idx_sp_string_hash_nrm_pattern_ops ON public.hfj_spidx_string USING btree (hash_norm_prefix, sp_value_normalized varchar_pattern_ops, res_id, partition_id)") + .onlyAppliesToPlatforms(DriverTypeEnum.POSTGRES_9_4) + .onlyIf( + String.format( + QUERY_FOR_COLUMN_COLLATION_TEMPLATE, + "HFJ_SPIDX_STRING".toLowerCase(), + "SP_VALUE_NORMALIZED".toLowerCase()), + "Column HFJ_SPIDX_STRING.SP_VALUE_NORMALIZED already has a collation of 'C' so doing nothing"); + + version.executeRawSql( + "20231212.2", + "CREATE UNIQUE INDEX idx_sp_uri_hash_identity_pattern_ops ON public.hfj_spidx_uri USING btree (hash_identity, sp_uri varchar_pattern_ops, res_id, partition_id)") + .onlyAppliesToPlatforms(DriverTypeEnum.POSTGRES_9_4) + .onlyIf( + String.format( + QUERY_FOR_COLUMN_COLLATION_TEMPLATE, + "HFJ_SPIDX_URI".toLowerCase(), + "SP_URI".toLowerCase()), + "Column HFJ_SPIDX_STRING.SP_VALUE_NORMALIZED already has a collation of 'C' so doing nothing"); + } } protected void init680() { @@ -939,27 +1033,34 @@ public class HapiFhirJpaMigrationTasks extends BaseMigrationTasks { // Ugh. Only oracle supports using IDX_TAG_DEF_TP_CD_SYS to enforce this constraint. The others will // create another index. // For Sql Server, should change the index to be unique with include columns. Do this in 6.1 - tagTable.dropIndex("20220429.8", "IDX_TAGDEF_TYPESYSCODE"); - Map addTagDefConstraint = new HashMap<>(); - addTagDefConstraint.put( - DriverTypeEnum.H2_EMBEDDED, - "ALTER TABLE HFJ_TAG_DEF ADD CONSTRAINT IDX_TAGDEF_TYPESYSCODE UNIQUE (TAG_TYPE, TAG_CODE, TAG_SYSTEM)"); - addTagDefConstraint.put( - DriverTypeEnum.MARIADB_10_1, - "ALTER TABLE HFJ_TAG_DEF ADD CONSTRAINT IDX_TAGDEF_TYPESYSCODE UNIQUE (TAG_TYPE, TAG_CODE, TAG_SYSTEM)"); - addTagDefConstraint.put( - DriverTypeEnum.MSSQL_2012, - "ALTER TABLE HFJ_TAG_DEF ADD CONSTRAINT IDX_TAGDEF_TYPESYSCODE UNIQUE (TAG_TYPE, TAG_CODE, TAG_SYSTEM)"); - addTagDefConstraint.put( - DriverTypeEnum.MYSQL_5_7, - "ALTER TABLE HFJ_TAG_DEF ADD CONSTRAINT IDX_TAGDEF_TYPESYSCODE UNIQUE (TAG_TYPE, TAG_CODE, TAG_SYSTEM)"); - addTagDefConstraint.put( - DriverTypeEnum.ORACLE_12C, - "ALTER TABLE HFJ_TAG_DEF ADD CONSTRAINT IDX_TAGDEF_TYPESYSCODE UNIQUE (TAG_TYPE, TAG_CODE, TAG_SYSTEM)"); - addTagDefConstraint.put( - DriverTypeEnum.POSTGRES_9_4, - "ALTER TABLE HFJ_TAG_DEF ADD CONSTRAINT IDX_TAGDEF_TYPESYSCODE UNIQUE (TAG_TYPE, TAG_CODE, TAG_SYSTEM)"); - version.executeRawSql("20220429.9", addTagDefConstraint); + // tagTable.dropIndex("20220429.8", "IDX_TAGDEF_TYPESYSCODE"); + // Map addTagDefConstraint = new HashMap<>(); + // addTagDefConstraint.put( + // DriverTypeEnum.H2_EMBEDDED, + // "ALTER TABLE HFJ_TAG_DEF ADD CONSTRAINT IDX_TAGDEF_TYPESYSCODE UNIQUE (TAG_TYPE, TAG_CODE, + // TAG_SYSTEM)"); + // addTagDefConstraint.put( + // DriverTypeEnum.MARIADB_10_1, + // "ALTER TABLE HFJ_TAG_DEF ADD CONSTRAINT IDX_TAGDEF_TYPESYSCODE UNIQUE (TAG_TYPE, TAG_CODE, + // TAG_SYSTEM)"); + // addTagDefConstraint.put( + // DriverTypeEnum.MSSQL_2012, + // "ALTER TABLE HFJ_TAG_DEF ADD CONSTRAINT IDX_TAGDEF_TYPESYSCODE UNIQUE (TAG_TYPE, TAG_CODE, + // TAG_SYSTEM)"); + // addTagDefConstraint.put( + // DriverTypeEnum.MYSQL_5_7, + // "ALTER TABLE HFJ_TAG_DEF ADD CONSTRAINT IDX_TAGDEF_TYPESYSCODE UNIQUE (TAG_TYPE, TAG_CODE, + // TAG_SYSTEM)"); + // addTagDefConstraint.put( + // DriverTypeEnum.ORACLE_12C, + // "ALTER TABLE HFJ_TAG_DEF ADD CONSTRAINT IDX_TAGDEF_TYPESYSCODE UNIQUE (TAG_TYPE, TAG_CODE, + // TAG_SYSTEM)"); + // addTagDefConstraint.put( + // DriverTypeEnum.POSTGRES_9_4, + // "ALTER TABLE HFJ_TAG_DEF ADD CONSTRAINT IDX_TAGDEF_TYPESYSCODE UNIQUE (TAG_TYPE, TAG_CODE, + // TAG_SYSTEM)"); + // version.executeRawSql("20220429.9", addTagDefConstraint); + version.addNop("20220429.9"); } // Fix for https://github.com/hapifhir/hapi-fhir-jpaserver-starter/issues/328 @@ -1454,11 +1555,12 @@ public class HapiFhirJpaMigrationTasks extends BaseMigrationTasks { Builder.BuilderWithTableName nrmlTable = version.onTable("HFJ_SPIDX_QUANTITY_NRML"); nrmlTable.addColumn("20210111.1", "PARTITION_ID").nullable().type(ColumnTypeEnum.INT); nrmlTable.addColumn("20210111.2", "PARTITION_DATE").nullable().type(ColumnTypeEnum.DATE_ONLY); - // - The fk name is generated from Hibernate, have to use this name here + // Disabled - superceded by 20220304.33 nrmlTable .addForeignKey("20210111.3", "FKRCJOVMUH5KC0O6FVBLE319PYV") .toColumn("RES_ID") - .references("HFJ_RESOURCE", "RES_ID"); + .references("HFJ_RESOURCE", "RES_ID") + .doNothing(); Builder.BuilderWithTableName quantityTable = version.onTable("HFJ_SPIDX_QUANTITY"); quantityTable diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/model/cross/JpaResourceLookup.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/model/cross/JpaResourceLookup.java index 2982ab20296..9891672a00f 100644 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/model/cross/JpaResourceLookup.java +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/model/cross/JpaResourceLookup.java @@ -23,7 +23,7 @@ import ca.uhn.fhir.jpa.model.dao.JpaPid; import java.util.Date; -public class JpaResourceLookup implements IResourceLookup { +public class JpaResourceLookup implements IResourceLookup { private final String myResourceType; private final Long myResourcePid; private final Date myDeletedAt; diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/packages/JpaPackageCache.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/packages/JpaPackageCache.java index 67af3c9a49d..f35730ec802 100644 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/packages/JpaPackageCache.java +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/packages/JpaPackageCache.java @@ -50,6 +50,17 @@ import ca.uhn.fhir.rest.server.exceptions.ResourceNotFoundException; import ca.uhn.fhir.util.BinaryUtil; import ca.uhn.fhir.util.ResourceUtil; import ca.uhn.fhir.util.StringUtil; +import jakarta.annotation.Nonnull; +import jakarta.annotation.Nullable; +import jakarta.persistence.EntityManager; +import jakarta.persistence.PersistenceContext; +import jakarta.persistence.TypedQuery; +import jakarta.persistence.criteria.CriteriaBuilder; +import jakarta.persistence.criteria.CriteriaQuery; +import jakarta.persistence.criteria.Join; +import jakarta.persistence.criteria.JoinType; +import jakarta.persistence.criteria.Predicate; +import jakarta.persistence.criteria.Root; import org.apache.commons.collections4.comparators.ReverseComparator; import org.apache.commons.lang3.Validate; import org.hl7.fhir.exceptions.FHIRException; @@ -83,17 +94,6 @@ import java.util.List; import java.util.Map; import java.util.Optional; import java.util.stream.Collectors; -import javax.annotation.Nonnull; -import javax.annotation.Nullable; -import javax.persistence.EntityManager; -import javax.persistence.PersistenceContext; -import javax.persistence.TypedQuery; -import javax.persistence.criteria.CriteriaBuilder; -import javax.persistence.criteria.CriteriaQuery; -import javax.persistence.criteria.Join; -import javax.persistence.criteria.JoinType; -import javax.persistence.criteria.Predicate; -import javax.persistence.criteria.Root; import static ca.uhn.fhir.jpa.util.QueryParameterUtils.toPredicateArray; import static ca.uhn.fhir.util.StringUtil.toUtf8String; @@ -788,25 +788,23 @@ public class JpaPackageCache extends BasePackageCacheManager implements IHapiPac Join resources = theRoot.join("myResources", JoinType.LEFT); - predicates.add(theCb.equal( - resources.get("myCanonicalUrl").as(String.class), thePackageSearchSpec.getResourceUrl())); + predicates.add(theCb.equal(resources.get("myCanonicalUrl"), thePackageSearchSpec.getResourceUrl())); } if (isNotBlank(thePackageSearchSpec.getDescription())) { String searchTerm = "%" + thePackageSearchSpec.getDescription() + "%"; searchTerm = StringUtil.normalizeStringForSearchIndexing(searchTerm); - predicates.add(theCb.like(theRoot.get("myDescriptionUpper").as(String.class), searchTerm)); + predicates.add(theCb.like(theRoot.get("myDescriptionUpper"), searchTerm)); } if (isNotBlank(thePackageSearchSpec.getFhirVersion())) { if (!thePackageSearchSpec.getFhirVersion().matches("([0-9]+\\.)+[0-9]+")) { FhirVersionEnum versionEnum = FhirVersionEnum.forVersionString(thePackageSearchSpec.getFhirVersion()); if (versionEnum != null) { - predicates.add(theCb.equal(theRoot.get("myFhirVersion").as(FhirVersionEnum.class), versionEnum)); + predicates.add(theCb.equal(theRoot.get("myFhirVersion").as(String.class), versionEnum.name())); } } else { - predicates.add(theCb.like( - theRoot.get("myFhirVersionId").as(String.class), thePackageSearchSpec.getFhirVersion() + "%")); + predicates.add(theCb.like(theRoot.get("myFhirVersionId"), thePackageSearchSpec.getFhirVersion() + "%")); } } diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/packages/NpmJpaValidationSupport.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/packages/NpmJpaValidationSupport.java index f60e6fbcd85..01a24d3999e 100644 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/packages/NpmJpaValidationSupport.java +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/packages/NpmJpaValidationSupport.java @@ -22,11 +22,11 @@ package ca.uhn.fhir.jpa.packages; import ca.uhn.fhir.context.FhirContext; import ca.uhn.fhir.context.FhirVersionEnum; import ca.uhn.fhir.context.support.IValidationSupport; +import jakarta.annotation.Nullable; import org.hl7.fhir.instance.model.api.IBaseResource; import org.springframework.beans.factory.annotation.Autowired; import java.util.List; -import javax.annotation.Nullable; public class NpmJpaValidationSupport implements IValidationSupport { diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/packages/NpmPackageMetadataJson.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/packages/NpmPackageMetadataJson.java index 2ba08c9ae98..a7c72e0274c 100644 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/packages/NpmPackageMetadataJson.java +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/packages/NpmPackageMetadataJson.java @@ -27,11 +27,11 @@ import com.fasterxml.jackson.annotation.JsonProperty; import com.fasterxml.jackson.databind.annotation.JsonDeserialize; import com.fasterxml.jackson.databind.annotation.JsonSerialize; import io.swagger.v3.oas.annotations.media.Schema; +import jakarta.annotation.Nonnull; import java.util.Date; import java.util.LinkedHashMap; import java.util.Map; -import javax.annotation.Nonnull; @Schema(description = "Represents an NPM package metadata response") @JsonInclude(JsonInclude.Include.NON_NULL) diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/packages/PackageInstallerSvcImpl.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/packages/PackageInstallerSvcImpl.java index 723450da4d0..b38552017dd 100644 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/packages/PackageInstallerSvcImpl.java +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/packages/PackageInstallerSvcImpl.java @@ -28,6 +28,7 @@ import ca.uhn.fhir.context.support.IValidationSupport; import ca.uhn.fhir.context.support.ValidationSupportContext; import ca.uhn.fhir.i18n.Msg; import ca.uhn.fhir.interceptor.model.RequestPartitionId; +import ca.uhn.fhir.jpa.api.config.JpaStorageSettings; import ca.uhn.fhir.jpa.api.dao.DaoRegistry; import ca.uhn.fhir.jpa.api.dao.IFhirResourceDao; import ca.uhn.fhir.jpa.api.model.DaoMethodOutcome; @@ -40,6 +41,7 @@ import ca.uhn.fhir.jpa.searchparam.SearchParameterMap; import ca.uhn.fhir.jpa.searchparam.registry.ISearchParamRegistryController; import ca.uhn.fhir.jpa.searchparam.util.SearchParameterHelper; import ca.uhn.fhir.rest.api.server.IBundleProvider; +import ca.uhn.fhir.rest.api.server.RequestDetails; import ca.uhn.fhir.rest.api.server.SystemRequestDetails; import ca.uhn.fhir.rest.param.StringParam; import ca.uhn.fhir.rest.param.TokenParam; @@ -48,6 +50,7 @@ import ca.uhn.fhir.rest.server.exceptions.ResourceVersionConflictException; import ca.uhn.fhir.util.FhirTerser; import ca.uhn.fhir.util.SearchParameterUtil; import com.google.common.annotations.VisibleForTesting; +import jakarta.annotation.PostConstruct; import org.apache.commons.lang3.Validate; import org.hl7.fhir.instance.model.api.IBase; import org.hl7.fhir.instance.model.api.IBaseResource; @@ -64,13 +67,12 @@ import org.springframework.beans.factory.annotation.Autowired; import java.io.IOException; import java.util.Collection; +import java.util.HashSet; import java.util.List; import java.util.Optional; -import javax.annotation.Nonnull; -import javax.annotation.PostConstruct; import static ca.uhn.fhir.jpa.packages.util.PackageUtils.DEFAULT_INSTALL_TYPES; -import static org.apache.commons.lang3.StringUtils.defaultString; +import static ca.uhn.fhir.util.SearchParameterUtil.getBaseAsStrings; import static org.apache.commons.lang3.StringUtils.isBlank; /** @@ -112,6 +114,9 @@ public class PackageInstallerSvcImpl implements IPackageInstallerSvc { @Autowired private PackageResourceParsingSvc myPackageResourceParsingSvc; + @Autowired + private JpaStorageSettings myStorageSettings; + /** * Constructor */ @@ -251,7 +256,7 @@ public class PackageInstallerSvcImpl implements IPackageInstallerSvc { for (IBaseResource next : resources) { try { next = isStructureDefinitionWithoutSnapshot(next) ? generateSnapshot(next) : next; - create(next, theInstallationSpec, theOutcome); + install(next, theInstallationSpec, theOutcome); } catch (Exception e) { ourLog.warn( "Failed to upload resource of type {} with ID {} - Error: {}", @@ -345,83 +350,42 @@ public class PackageInstallerSvcImpl implements IPackageInstallerSvc { * ============================= Utility methods =============================== */ @VisibleForTesting - void create( + void install( IBaseResource theResource, PackageInstallationSpec theInstallationSpec, PackageInstallOutcomeJson theOutcome) { - IFhirResourceDao dao = myDaoRegistry.getResourceDao(theResource.getClass()); - SearchParameterMap map = createSearchParameterMapFor(theResource); - IBundleProvider searchResult = searchResource(dao, map); - if (validForUpload(theResource)) { - if (searchResult.isEmpty()) { - ourLog.info("Creating new resource matching {}", map.toNormalizedQueryString(myFhirContext)); - theOutcome.incrementResourcesInstalled(myFhirContext.getResourceType(theResource)); - - IIdType id = theResource.getIdElement(); - - if (id.isEmpty()) { - createResource(dao, theResource); - ourLog.info("Created resource with new id"); - } else { - if (id.isIdPartValidLong()) { - String newIdPart = "npm-" + id.getIdPart(); - id.setParts(id.getBaseUrl(), id.getResourceType(), newIdPart, id.getVersionIdPart()); - } - - try { - updateResource(dao, theResource); - - ourLog.info("Created resource with existing id"); - } catch (ResourceVersionConflictException exception) { - final Optional optResource = readResourceById(dao, id); - - final String existingResourceUrlOrNull = optResource - .filter(MetadataResource.class::isInstance) - .map(MetadataResource.class::cast) - .map(MetadataResource::getUrl) - .orElse(null); - final String newResourceUrlOrNull = (theResource instanceof MetadataResource) - ? ((MetadataResource) theResource).getUrl() - : null; - - ourLog.error( - "Version conflict error: This is possibly due to a collision between ValueSets from different IGs that are coincidentally using the same resource ID: [{}] and new resource URL: [{}], with the exisitng resource having URL: [{}]. Ignoring this update and continuing: The first IG wins. ", - id.getIdPart(), - newResourceUrlOrNull, - existingResourceUrlOrNull, - exception); - } - } - } else { - if (theInstallationSpec.isReloadExisting()) { - ourLog.info("Updating existing resource matching {}", map.toNormalizedQueryString(myFhirContext)); - theResource.setId(searchResult - .getResources(0, 1) - .get(0) - .getIdElement() - .toUnqualifiedVersionless()); - DaoMethodOutcome outcome = updateResource(dao, theResource); - if (!outcome.isNop()) { - theOutcome.incrementResourcesInstalled(myFhirContext.getResourceType(theResource)); - } - } else { - ourLog.info( - "Skipping update of existing resource matching {}", - map.toNormalizedQueryString(myFhirContext)); - } - } - } else { + if (!validForUpload(theResource)) { ourLog.warn( "Failed to upload resource of type {} with ID {} - Error: Resource failed validation", theResource.fhirType(), theResource.getIdElement().getValue()); + return; + } + + IFhirResourceDao dao = myDaoRegistry.getResourceDao(theResource.getClass()); + SearchParameterMap map = createSearchParameterMapFor(theResource); + IBundleProvider searchResult = searchResource(dao, map); + + String resourceQuery = map.toNormalizedQueryString(myFhirContext); + if (!searchResult.isEmpty() && !theInstallationSpec.isReloadExisting()) { + ourLog.info("Skipping update of existing resource matching {}", resourceQuery); + return; + } + if (!searchResult.isEmpty()) { + ourLog.info("Updating existing resource matching {}", resourceQuery); + } + IBaseResource existingResource = + !searchResult.isEmpty() ? searchResult.getResources(0, 1).get(0) : null; + boolean isInstalled = createOrUpdateResource(dao, theResource, existingResource); + if (isInstalled) { + theOutcome.incrementResourcesInstalled(myFhirContext.getResourceType(theResource)); } } private Optional readResourceById(IFhirResourceDao dao, IIdType id) { try { - return Optional.ofNullable(dao.read(id.toUnqualifiedVersionless(), newSystemRequestDetails())); + return Optional.ofNullable(dao.read(id.toUnqualifiedVersionless(), createRequestDetails())); } catch (Exception exception) { // ignore because we're running this query to help build the log @@ -432,30 +396,112 @@ public class PackageInstallerSvcImpl implements IPackageInstallerSvc { } private IBundleProvider searchResource(IFhirResourceDao theDao, SearchParameterMap theMap) { - return theDao.search(theMap, newSystemRequestDetails()); + return theDao.search(theMap, createRequestDetails()); } - @Nonnull - private SystemRequestDetails newSystemRequestDetails() { - return new SystemRequestDetails().setRequestPartitionId(RequestPartitionId.defaultPartition()); - } + protected boolean createOrUpdateResource( + IFhirResourceDao theDao, IBaseResource theResource, IBaseResource theExistingResource) { + final IIdType id = theResource.getIdElement(); - private void createResource(IFhirResourceDao theDao, IBaseResource theResource) { - if (myPartitionSettings.isPartitioningEnabled()) { - SystemRequestDetails requestDetails = newSystemRequestDetails(); - theDao.create(theResource, requestDetails); - } else { - theDao.create(theResource); + if (theExistingResource == null && id.isEmpty()) { + ourLog.debug("Install resource without id will be created"); + theDao.create(theResource, createRequestDetails()); + return true; } + + if (theExistingResource == null && !id.isEmpty() && id.isIdPartValidLong()) { + String newIdPart = "npm-" + id.getIdPart(); + id.setParts(id.getBaseUrl(), id.getResourceType(), newIdPart, id.getVersionIdPart()); + } + + boolean isExistingUpdated = updateExistingResourceIfNecessary(theDao, theResource, theExistingResource); + boolean shouldOverrideId = theExistingResource != null && !isExistingUpdated; + + if (shouldOverrideId) { + ourLog.debug( + "Existing resource {} will be overridden with installed resource {}", + theExistingResource.getIdElement(), + id); + theResource.setId(theExistingResource.getIdElement().toUnqualifiedVersionless()); + } else { + ourLog.debug("Install resource {} will be created", id); + } + + DaoMethodOutcome outcome = updateResource(theDao, theResource); + return outcome != null && !outcome.isNop(); } - DaoMethodOutcome updateResource(IFhirResourceDao theDao, IBaseResource theResource) { - if (myPartitionSettings.isPartitioningEnabled()) { - SystemRequestDetails requestDetails = newSystemRequestDetails(); - return theDao.update(theResource, requestDetails); - } else { - return theDao.update(theResource, new SystemRequestDetails()); + private boolean updateExistingResourceIfNecessary( + IFhirResourceDao theDao, IBaseResource theResource, IBaseResource theExistingResource) { + if (!"SearchParameter".equals(theResource.getClass().getSimpleName())) { + return false; } + if (theExistingResource == null) { + return false; + } + if (theExistingResource + .getIdElement() + .getIdPart() + .equals(theResource.getIdElement().getIdPart())) { + return false; + } + Collection remainingBaseList = new HashSet<>(getBaseAsStrings(myFhirContext, theExistingResource)); + remainingBaseList.removeAll(getBaseAsStrings(myFhirContext, theResource)); + if (remainingBaseList.isEmpty()) { + return false; + } + myFhirContext + .getResourceDefinition(theExistingResource) + .getChildByName("base") + .getMutator() + .setValue(theExistingResource, null); + + for (String baseResourceName : remainingBaseList) { + myFhirContext.newTerser().addElement(theExistingResource, "base", baseResourceName); + } + ourLog.info( + "Existing SearchParameter {} will be updated with base {}", + theExistingResource.getIdElement().getIdPart(), + remainingBaseList); + updateResource(theDao, theExistingResource); + return true; + } + + private DaoMethodOutcome updateResource(IFhirResourceDao theDao, IBaseResource theResource) { + DaoMethodOutcome outcome = null; + + IIdType id = theResource.getIdElement(); + RequestDetails requestDetails = createRequestDetails(); + + try { + outcome = theDao.update(theResource, requestDetails); + } catch (ResourceVersionConflictException exception) { + final Optional optResource = readResourceById(theDao, id); + + final String existingResourceUrlOrNull = optResource + .filter(MetadataResource.class::isInstance) + .map(MetadataResource.class::cast) + .map(MetadataResource::getUrl) + .orElse(null); + final String newResourceUrlOrNull = + (theResource instanceof MetadataResource) ? ((MetadataResource) theResource).getUrl() : null; + + ourLog.error( + "Version conflict error: This is possibly due to a collision between ValueSets from different IGs that are coincidentally using the same resource ID: [{}] and new resource URL: [{}], with the exisitng resource having URL: [{}]. Ignoring this update and continuing: The first IG wins. ", + id.getIdPart(), + newResourceUrlOrNull, + existingResourceUrlOrNull, + exception); + } + return outcome; + } + + private RequestDetails createRequestDetails() { + SystemRequestDetails requestDetails = new SystemRequestDetails(); + if (myPartitionSettings.isPartitioningEnabled()) { + requestDetails.setRequestPartitionId(RequestPartitionId.defaultPartition()); + } + return requestDetails; } boolean validForUpload(IBaseResource theResource) { @@ -463,7 +509,7 @@ public class PackageInstallerSvcImpl implements IPackageInstallerSvc { if ("SearchParameter".equals(resourceType)) { String code = SearchParameterUtil.getCode(myFhirContext, theResource); - if (defaultString(code).startsWith("_")) { + if (!isBlank(code) && code.startsWith("_")) { ourLog.warn( "Failed to validate resource of type {} with url {} - Error: Resource code starts with \"_\"", theResource.fhirType(), @@ -480,7 +526,7 @@ public class PackageInstallerSvcImpl implements IPackageInstallerSvc { return false; } - if (SearchParameterUtil.getBaseAsStrings(myFhirContext, theResource).isEmpty()) { + if (getBaseAsStrings(myFhirContext, theResource).isEmpty()) { ourLog.warn( "Failed to validate resource of type {} with url {} - Error: Resource base is empty", theResource.fhirType(), @@ -505,7 +551,7 @@ public class PackageInstallerSvcImpl implements IPackageInstallerSvc { * and {@link org.hl7.fhir.r4.model.Communication}, the status field doesn't necessarily need to be set to 'active' * for that resource to be eligible for upload via packages. For example, all {@link org.hl7.fhir.r4.model.Subscription} * have a status of {@link org.hl7.fhir.r4.model.Subscription.SubscriptionStatus#REQUESTED} when they are originally - * inserted into the database, so we accept that value for {@link org.hl7.fhir.r4.model.Subscription} isntead. + * inserted into the database, so we accept that value for {@link org.hl7.fhir.r4.model.Subscription} instead. * Furthermore, {@link org.hl7.fhir.r4.model.DocumentReference} and {@link org.hl7.fhir.r4.model.Communication} can * exist with a wide variety of values for status that include ones such as * {@link org.hl7.fhir.r4.model.Communication.CommunicationStatus#ENTEREDINERROR}, @@ -517,6 +563,9 @@ public class PackageInstallerSvcImpl implements IPackageInstallerSvc { * @return {@link Boolean#TRUE} if the status value of this resource is acceptable for package upload. */ private boolean isValidResourceStatusForPackageUpload(IBaseResource theResource) { + if (!myStorageSettings.isValidateResourceStatusForPackageUpload()) { + return true; + } List statusTypes = myFhirContext.newFhirPath().evaluate(theResource, "status", IPrimitiveType.class); // Resource does not have a status field @@ -560,20 +609,21 @@ public class PackageInstallerSvcImpl implements IPackageInstallerSvc { } } - private SearchParameterMap createSearchParameterMapFor(IBaseResource resource) { - if (resource.getClass().getSimpleName().equals("NamingSystem")) { - String uniqueId = extractUniqeIdFromNamingSystem(resource); + private SearchParameterMap createSearchParameterMapFor(IBaseResource theResource) { + String resourceType = theResource.getClass().getSimpleName(); + if ("NamingSystem".equals(resourceType)) { + String uniqueId = extractUniqeIdFromNamingSystem(theResource); return SearchParameterMap.newSynchronous().add("value", new StringParam(uniqueId).setExact(true)); - } else if (resource.getClass().getSimpleName().equals("Subscription")) { - String id = extractIdFromSubscription(resource); + } else if ("Subscription".equals(resourceType)) { + String id = extractSimpleValue(theResource, "id"); return SearchParameterMap.newSynchronous().add("_id", new TokenParam(id)); - } else if (resource.getClass().getSimpleName().equals("SearchParameter")) { - return buildSearchParameterMapForSearchParameter(resource); - } else if (resourceHasUrlElement(resource)) { - String url = extractUniqueUrlFromMetadataResource(resource); + } else if ("SearchParameter".equals(resourceType)) { + return buildSearchParameterMapForSearchParameter(theResource); + } else if (resourceHasUrlElement(theResource)) { + String url = extractSimpleValue(theResource, "url"); return SearchParameterMap.newSynchronous().add("url", new UriParam(url)); } else { - TokenParam identifierToken = extractIdentifierFromOtherResourceTypes(resource); + TokenParam identifierToken = extractIdentifierFromOtherResourceTypes(theResource); return SearchParameterMap.newSynchronous().add("identifier", identifierToken); } } @@ -593,7 +643,7 @@ public class PackageInstallerSvcImpl implements IPackageInstallerSvc { } if (resourceHasUrlElement(theResource)) { - String url = extractUniqueUrlFromMetadataResource(theResource); + String url = extractSimpleValue(theResource, "url"); return SearchParameterMap.newSynchronous().add("url", new UriParam(url)); } else { TokenParam identifierToken = extractIdentifierFromOtherResourceTypes(theResource); @@ -601,32 +651,17 @@ public class PackageInstallerSvcImpl implements IPackageInstallerSvc { } } - private String extractUniqeIdFromNamingSystem(IBaseResource resource) { - FhirTerser terser = myFhirContext.newTerser(); - IBase uniqueIdComponent = (IBase) terser.getSingleValueOrNull(resource, "uniqueId"); + private String extractUniqeIdFromNamingSystem(IBaseResource theResource) { + IBase uniqueIdComponent = (IBase) extractValue(theResource, "uniqueId"); if (uniqueIdComponent == null) { throw new ImplementationGuideInstallationException( Msg.code(1291) + "NamingSystem does not have uniqueId component."); } - IPrimitiveType asPrimitiveType = (IPrimitiveType) terser.getSingleValueOrNull(uniqueIdComponent, "value"); - return (String) asPrimitiveType.getValue(); + return extractSimpleValue(uniqueIdComponent, "value"); } - private String extractIdFromSubscription(IBaseResource resource) { - FhirTerser terser = myFhirContext.newTerser(); - IPrimitiveType asPrimitiveType = (IPrimitiveType) terser.getSingleValueOrNull(resource, "id"); - return (String) asPrimitiveType.getValue(); - } - - private String extractUniqueUrlFromMetadataResource(IBaseResource resource) { - FhirTerser terser = myFhirContext.newTerser(); - IPrimitiveType asPrimitiveType = (IPrimitiveType) terser.getSingleValueOrNull(resource, "url"); - return (String) asPrimitiveType.getValue(); - } - - private TokenParam extractIdentifierFromOtherResourceTypes(IBaseResource resource) { - FhirTerser terser = myFhirContext.newTerser(); - Identifier identifier = (Identifier) terser.getSingleValueOrNull(resource, "identifier"); + private TokenParam extractIdentifierFromOtherResourceTypes(IBaseResource theResource) { + Identifier identifier = (Identifier) extractValue(theResource, "identifier"); if (identifier != null) { return new TokenParam(identifier.getSystem(), identifier.getValue()); } else { @@ -635,6 +670,15 @@ public class PackageInstallerSvcImpl implements IPackageInstallerSvc { } } + private Object extractValue(IBase theResource, String thePath) { + return myFhirContext.newTerser().getSingleValueOrNull(theResource, thePath); + } + + private String extractSimpleValue(IBase theResource, String thePath) { + IPrimitiveType asPrimitiveType = (IPrimitiveType) extractValue(theResource, thePath); + return (String) asPrimitiveType.getValue(); + } + private boolean resourceHasUrlElement(IBaseResource resource) { BaseRuntimeElementDefinition def = myFhirContext.getElementDefinition(resource.getClass()); if (!(def instanceof BaseRuntimeElementCompositeDefinition)) { diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/packages/loader/PackageLoaderSvc.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/packages/loader/PackageLoaderSvc.java index 1d02fae2c6d..55cbdc06686 100644 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/packages/loader/PackageLoaderSvc.java +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/packages/loader/PackageLoaderSvc.java @@ -24,6 +24,7 @@ import ca.uhn.fhir.jpa.packages.PackageInstallationSpec; import ca.uhn.fhir.rest.server.exceptions.InternalErrorException; import ca.uhn.fhir.rest.server.exceptions.ResourceNotFoundException; import ca.uhn.fhir.util.ClasspathUtil; +import jakarta.annotation.Nullable; import org.apache.commons.io.IOUtils; import org.apache.commons.lang3.Validate; import org.apache.http.client.methods.CloseableHttpResponse; @@ -44,7 +45,6 @@ import java.net.URI; import java.net.URISyntaxException; import java.nio.file.Files; import java.nio.file.Paths; -import javax.annotation.Nullable; import static org.apache.commons.lang3.StringUtils.isNotBlank; diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/partition/IPartitionLookupSvc.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/partition/IPartitionLookupSvc.java index 8150fb53c7a..ac6bda04378 100644 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/partition/IPartitionLookupSvc.java +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/partition/IPartitionLookupSvc.java @@ -22,9 +22,9 @@ package ca.uhn.fhir.jpa.partition; import ca.uhn.fhir.jpa.entity.PartitionEntity; import ca.uhn.fhir.rest.api.server.RequestDetails; import ca.uhn.fhir.rest.server.exceptions.ResourceNotFoundException; +import jakarta.annotation.Nullable; import java.util.List; -import javax.annotation.Nullable; public interface IPartitionLookupSvc { diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/partition/PartitionLookupSvcImpl.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/partition/PartitionLookupSvcImpl.java index 60c04157046..40f9d761577 100644 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/partition/PartitionLookupSvcImpl.java +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/partition/PartitionLookupSvcImpl.java @@ -36,6 +36,8 @@ import ca.uhn.fhir.rest.server.exceptions.MethodNotAllowedException; import ca.uhn.fhir.rest.server.exceptions.ResourceNotFoundException; import ca.uhn.fhir.rest.server.servlet.ServletRequestDetails; import ca.uhn.fhir.util.ICallable; +import jakarta.annotation.Nonnull; +import jakarta.annotation.PostConstruct; import org.apache.commons.lang3.Validate; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -49,8 +51,6 @@ import java.util.Optional; import java.util.concurrent.ThreadLocalRandom; import java.util.regex.Pattern; import java.util.stream.Collectors; -import javax.annotation.Nonnull; -import javax.annotation.PostConstruct; import static org.apache.commons.lang3.StringUtils.isBlank; diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/partition/PartitionManagementProvider.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/partition/PartitionManagementProvider.java index 31edc63401d..4c0fabb51c5 100644 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/partition/PartitionManagementProvider.java +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/partition/PartitionManagementProvider.java @@ -28,6 +28,7 @@ import ca.uhn.fhir.rest.annotation.ResourceParam; import ca.uhn.fhir.rest.api.server.RequestDetails; import ca.uhn.fhir.rest.server.provider.ProviderConstants; import ca.uhn.fhir.util.ParametersUtil; +import jakarta.annotation.Nonnull; import org.hl7.fhir.instance.model.api.IBase; import org.hl7.fhir.instance.model.api.IBaseParameters; import org.hl7.fhir.instance.model.api.IPrimitiveType; @@ -36,7 +37,6 @@ import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; import java.util.List; -import javax.annotation.Nonnull; import static ca.uhn.fhir.jpa.partition.PartitionLookupSvcImpl.validatePartitionIdSupplied; import static org.apache.commons.lang3.StringUtils.isNotBlank; diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/provider/BaseJpaResourceProviderCodeSystem.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/provider/BaseJpaResourceProviderCodeSystem.java index 1929635de94..671649edb29 100644 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/provider/BaseJpaResourceProviderCodeSystem.java +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/provider/BaseJpaResourceProviderCodeSystem.java @@ -32,6 +32,7 @@ import ca.uhn.fhir.rest.annotation.Operation; import ca.uhn.fhir.rest.annotation.OperationParam; import ca.uhn.fhir.rest.api.server.RequestDetails; import ca.uhn.fhir.rest.server.exceptions.InvalidRequestException; +import jakarta.servlet.http.HttpServletRequest; import org.hl7.fhir.instance.model.api.IBaseCoding; import org.hl7.fhir.instance.model.api.IBaseDatatype; import org.hl7.fhir.instance.model.api.IBaseParameters; @@ -43,9 +44,7 @@ import org.springframework.beans.factory.annotation.Autowired; import java.util.List; import java.util.Optional; import java.util.function.Supplier; -import javax.servlet.http.HttpServletRequest; -import static ca.uhn.fhir.jpa.provider.ValueSetOperationProvider.toValidateCodeResult; import static org.apache.commons.lang3.StringUtils.isNotBlank; public abstract class BaseJpaResourceProviderCodeSystem extends BaseJpaResourceProvider { @@ -65,6 +64,7 @@ public abstract class BaseJpaResourceProviderCodeSystem @OperationParam(name = "version", typeName = "string", min = 0), @OperationParam(name = "display", typeName = "string", min = 1), @OperationParam(name = "abstract", typeName = "boolean", min = 1), + @OperationParam(name = "property", min = 0, max = OperationParam.MAX_UNLIMITED, typeName = "code") }) public IBaseParameters lookup( HttpServletRequest theServletRequest, @@ -75,7 +75,7 @@ public abstract class BaseJpaResourceProviderCodeSystem @OperationParam(name = "displayLanguage", min = 0, max = 1, typeName = "code") IPrimitiveType theDisplayLanguage, @OperationParam(name = "property", min = 0, max = OperationParam.MAX_UNLIMITED, typeName = "code") - List> theProperties, + List> thePropertyNames, RequestDetails theRequestDetails) { startRequest(theServletRequest); @@ -83,9 +83,10 @@ public abstract class BaseJpaResourceProviderCodeSystem IFhirResourceDaoCodeSystem dao = (IFhirResourceDaoCodeSystem) getDao(); IValidationSupport.LookupCodeResult result; applyVersionToSystem(theSystem, theVersion); - result = dao.lookupCode(theCode, theSystem, theCoding, theDisplayLanguage, theRequestDetails); + result = dao.lookupCode( + theCode, theSystem, theCoding, theDisplayLanguage, thePropertyNames, theRequestDetails); result.throwNotFoundIfAppropriate(); - return result.toParameters(theRequestDetails.getFhirContext(), theProperties); + return result.toParameters(theRequestDetails.getFhirContext(), thePropertyNames); } finally { endRequest(theServletRequest); } @@ -191,7 +192,7 @@ public abstract class BaseJpaResourceProviderCodeSystem theCodeableConcept, theRequestDetails); } - return toValidateCodeResult(getContext(), result); + return result.toParameters(getContext()); } finally { endRequest(theServletRequest); } diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/provider/BaseJpaResourceProviderComposition.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/provider/BaseJpaResourceProviderComposition.java index 42eb7b187ce..7e733249f96 100644 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/provider/BaseJpaResourceProviderComposition.java +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/provider/BaseJpaResourceProviderComposition.java @@ -44,7 +44,7 @@ public abstract class BaseJpaResourceProviderComposition */ @Operation(name = JpaConstants.OPERATION_EVERYTHING, idempotent = true, bundleType = BundleTypeEnum.SEARCHSET) public IBundleProvider EncounterInstanceEverything( - javax.servlet.http.HttpServletRequest theServletRequest, + jakarta.servlet.http.HttpServletRequest theServletRequest, @IdParam IIdType theId, @Description( formalDefinition = @@ -76,7 +76,7 @@ public abstract class BaseJpaResourceProviderEncounter */ @Operation(name = JpaConstants.OPERATION_EVERYTHING, idempotent = true, bundleType = BundleTypeEnum.SEARCHSET) public IBundleProvider EncounterTypeEverything( - javax.servlet.http.HttpServletRequest theServletRequest, + jakarta.servlet.http.HttpServletRequest theServletRequest, @Description( formalDefinition = "Results from this method are returned across multiple pages. This parameter controls the size of those pages.") diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/provider/BaseJpaResourceProviderEncounterDstu2.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/provider/BaseJpaResourceProviderEncounterDstu2.java index ef78d56c9e3..92d43b2ce75 100644 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/provider/BaseJpaResourceProviderEncounterDstu2.java +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/provider/BaseJpaResourceProviderEncounterDstu2.java @@ -40,7 +40,7 @@ public abstract class BaseJpaResourceProviderEncounterDstu2 extends BaseJpaResou */ @Operation(name = JpaConstants.OPERATION_EVERYTHING, idempotent = true, bundleType = BundleTypeEnum.SEARCHSET) public IBundleProvider EncounterInstanceEverything( - javax.servlet.http.HttpServletRequest theServletRequest, + jakarta.servlet.http.HttpServletRequest theServletRequest, @IdParam ca.uhn.fhir.model.primitive.IdDt theId, @Description( formalDefinition = @@ -74,7 +74,7 @@ public abstract class BaseJpaResourceProviderEncounterDstu2 extends BaseJpaResou */ @Operation(name = JpaConstants.OPERATION_EVERYTHING, idempotent = true, bundleType = BundleTypeEnum.SEARCHSET) public IBundleProvider EncounterTypeEverything( - javax.servlet.http.HttpServletRequest theServletRequest, + jakarta.servlet.http.HttpServletRequest theServletRequest, @Description( formalDefinition = "Results from this method are returned across multiple pages. This parameter controls the size of those pages.") diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/provider/BaseJpaResourceProviderObservation.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/provider/BaseJpaResourceProviderObservation.java index da112acefe2..f74a8e684d1 100644 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/provider/BaseJpaResourceProviderObservation.java +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/provider/BaseJpaResourceProviderObservation.java @@ -45,8 +45,8 @@ public abstract class BaseJpaResourceProviderObservation ex idempotent = true, bundleType = BundleTypeEnum.SEARCHSET) public IBundleProvider patientInstanceEverything( - javax.servlet.http.HttpServletRequest theServletRequest, + jakarta.servlet.http.HttpServletRequest theServletRequest, @IdParam IIdType theId, @Description( shortDefinition = @@ -143,7 +143,7 @@ public abstract class BaseJpaResourceProviderPatient ex idempotent = true, bundleType = BundleTypeEnum.SEARCHSET) public IBundleProvider patientTypeEverything( - javax.servlet.http.HttpServletRequest theServletRequest, + jakarta.servlet.http.HttpServletRequest theServletRequest, @Description( shortDefinition = "Results from this method are returned across multiple pages. This parameter controls the size of those pages.") diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/provider/BaseJpaSystemProvider.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/provider/BaseJpaSystemProvider.java index 4e5b15d8eab..d264abdca15 100644 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/provider/BaseJpaSystemProvider.java +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/provider/BaseJpaSystemProvider.java @@ -35,6 +35,7 @@ import ca.uhn.fhir.rest.server.exceptions.InternalErrorException; import ca.uhn.fhir.rest.server.provider.ProviderConstants; import ca.uhn.fhir.util.ParametersUtil; import ca.uhn.fhir.util.StopWatch; +import jakarta.servlet.http.HttpServletRequest; import org.apache.commons.lang3.exception.ExceptionUtils; import org.hl7.fhir.instance.model.api.IBaseParameters; import org.slf4j.Logger; @@ -42,7 +43,6 @@ import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; import java.util.Date; -import javax.servlet.http.HttpServletRequest; public abstract class BaseJpaSystemProvider extends BaseStorageSystemProvider implements IJpaSystemProvider { diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/provider/DiffProvider.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/provider/DiffProvider.java index b20caebfd72..2a4310d9c6f 100644 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/provider/DiffProvider.java +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/provider/DiffProvider.java @@ -33,6 +33,7 @@ import ca.uhn.fhir.rest.server.exceptions.InvalidRequestException; import ca.uhn.fhir.rest.server.exceptions.ResourceNotFoundException; import ca.uhn.fhir.rest.server.provider.ProviderConstants; import com.google.common.base.Objects; +import jakarta.annotation.Nonnull; import org.hl7.fhir.instance.model.api.IBaseParameters; import org.hl7.fhir.instance.model.api.IBaseResource; import org.hl7.fhir.instance.model.api.IIdType; @@ -41,8 +42,6 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; -import javax.annotation.Nonnull; - public class DiffProvider { private static final Logger ourLog = LoggerFactory.getLogger(DiffProvider.class); diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/provider/InstanceReindexProvider.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/provider/InstanceReindexProvider.java index 646f1523b18..4d77a09251c 100644 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/provider/InstanceReindexProvider.java +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/provider/InstanceReindexProvider.java @@ -25,6 +25,7 @@ import ca.uhn.fhir.rest.annotation.Operation; import ca.uhn.fhir.rest.annotation.OperationParam; import ca.uhn.fhir.rest.api.server.RequestDetails; import ca.uhn.fhir.rest.server.provider.ProviderConstants; +import jakarta.annotation.Nonnull; import org.apache.commons.lang3.Validate; import org.hl7.fhir.instance.model.api.IBaseParameters; import org.hl7.fhir.instance.model.api.IIdType; @@ -33,7 +34,6 @@ import org.hl7.fhir.instance.model.api.IPrimitiveType; import java.util.List; import java.util.Set; import java.util.stream.Collectors; -import javax.annotation.Nonnull; public class InstanceReindexProvider { diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/provider/JpaCapabilityStatementProvider.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/provider/JpaCapabilityStatementProvider.java index 2983384f549..a164becf92a 100644 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/provider/JpaCapabilityStatementProvider.java +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/provider/JpaCapabilityStatementProvider.java @@ -31,6 +31,7 @@ import ca.uhn.fhir.util.CoverageIgnore; import ca.uhn.fhir.util.ExtensionConstants; import ca.uhn.fhir.util.ExtensionUtil; import ca.uhn.fhir.util.FhirTerser; +import jakarta.annotation.Nonnull; import org.apache.commons.lang3.Validate; import org.hl7.fhir.instance.model.api.IBase; import org.hl7.fhir.instance.model.api.IBaseConformance; @@ -40,7 +41,6 @@ import org.hl7.fhir.r4.model.CapabilityStatement.ResourceVersionPolicy; import org.hl7.fhir.r4.model.Meta; import java.util.Map; -import javax.annotation.Nonnull; import static org.apache.commons.lang3.StringUtils.isNotBlank; diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/provider/JpaConformanceProviderDstu2.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/provider/JpaConformanceProviderDstu2.java index dd06075d423..5cf700a423b 100644 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/provider/JpaConformanceProviderDstu2.java +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/provider/JpaConformanceProviderDstu2.java @@ -44,12 +44,12 @@ import ca.uhn.fhir.rest.server.RestfulServer; import ca.uhn.fhir.rest.server.provider.dstu2.ServerConformanceProvider; import ca.uhn.fhir.util.CoverageIgnore; import ca.uhn.fhir.util.ExtensionConstants; +import jakarta.servlet.http.HttpServletRequest; import org.hl7.fhir.dstu2.model.Subscription; import java.util.Collections; import java.util.List; import java.util.Map; -import javax.servlet.http.HttpServletRequest; import static org.apache.commons.lang3.ObjectUtils.defaultIfNull; import static org.apache.commons.lang3.StringUtils.isNotBlank; diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/provider/ProcessMessageProvider.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/provider/ProcessMessageProvider.java index 803254ea8bb..695544c4555 100644 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/provider/ProcessMessageProvider.java +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/provider/ProcessMessageProvider.java @@ -25,11 +25,10 @@ import ca.uhn.fhir.model.api.annotation.Description; import ca.uhn.fhir.rest.annotation.Operation; import ca.uhn.fhir.rest.annotation.OperationParam; import ca.uhn.fhir.rest.api.server.RequestDetails; +import jakarta.servlet.http.HttpServletRequest; import org.hl7.fhir.instance.model.api.IBaseBundle; import org.springframework.beans.factory.annotation.Autowired; -import javax.servlet.http.HttpServletRequest; - import static ca.uhn.fhir.jpa.provider.BaseJpaProvider.endRequest; import static ca.uhn.fhir.jpa.provider.BaseJpaProvider.startRequest; diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/provider/TerminologyUploaderProvider.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/provider/TerminologyUploaderProvider.java index 85928273a71..64c65e38aa7 100644 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/provider/TerminologyUploaderProvider.java +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/provider/TerminologyUploaderProvider.java @@ -40,6 +40,8 @@ import ca.uhn.fhir.util.ValidateUtil; import com.google.common.base.Charsets; import com.google.common.collect.ArrayListMultimap; import com.google.common.collect.Multimap; +import jakarta.annotation.Nonnull; +import jakarta.servlet.http.HttpServletRequest; import org.hl7.fhir.convertors.advisors.impl.BaseAdvisor_30_40; import org.hl7.fhir.convertors.advisors.impl.BaseAdvisor_40_50; import org.hl7.fhir.convertors.factory.VersionConvertorFactory_30_40; @@ -59,8 +61,6 @@ import java.util.ArrayList; import java.util.LinkedHashMap; import java.util.List; import java.util.Map; -import javax.annotation.Nonnull; -import javax.servlet.http.HttpServletRequest; import static org.apache.commons.lang3.StringUtils.*; @@ -446,6 +446,10 @@ public class TerminologyUploaderProvider extends BaseJpaProvider { return retVal; } + public void setTerminologyLoaderSvc(ITermLoaderSvc theTermLoaderSvc) { + myTerminologyLoaderSvc = theTermLoaderSvc; + } + public static class FileBackedFileDescriptor implements ITermLoaderSvc.FileDescriptor { private final File myNextFile; diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/provider/ValueSetOperationProvider.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/provider/ValueSetOperationProvider.java index 99189c92154..ba41fc11e68 100644 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/provider/ValueSetOperationProvider.java +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/provider/ValueSetOperationProvider.java @@ -19,7 +19,6 @@ */ package ca.uhn.fhir.jpa.provider; -import ca.uhn.fhir.context.FhirContext; import ca.uhn.fhir.context.support.ConceptValidationOptions; import ca.uhn.fhir.context.support.IValidationSupport; import ca.uhn.fhir.context.support.IValidationSupport.CodeValidationResult; @@ -40,6 +39,7 @@ import ca.uhn.fhir.rest.server.exceptions.InvalidRequestException; import ca.uhn.fhir.rest.server.provider.ProviderConstants; import ca.uhn.fhir.util.ParametersUtil; import com.google.common.annotations.VisibleForTesting; +import jakarta.servlet.http.HttpServletRequest; import org.hl7.fhir.common.hapi.validation.support.ValidationSupportChain; import org.hl7.fhir.instance.model.api.IBaseCoding; import org.hl7.fhir.instance.model.api.IBaseParameters; @@ -54,17 +54,12 @@ import org.springframework.beans.factory.annotation.Qualifier; import java.util.Optional; import java.util.function.Supplier; -import javax.servlet.http.HttpServletRequest; import static org.apache.commons.lang3.StringUtils.isNotBlank; public class ValueSetOperationProvider extends BaseJpaProvider { private static final Logger ourLog = LoggerFactory.getLogger(ValueSetOperationProvider.class); - public static final String SOURCE_DETAILS = "sourceDetails"; - public static final String RESULT = "result"; - public static final String MESSAGE = "message"; - public static final String DISPLAY = "display"; @Autowired protected IValidationSupport myValidationSupport; @@ -149,10 +144,10 @@ public class ValueSetOperationProvider extends BaseJpaProvider { idempotent = true, typeName = "ValueSet", returnParameters = { - @OperationParam(name = RESULT, typeName = "boolean", min = 1), - @OperationParam(name = MESSAGE, typeName = "string"), - @OperationParam(name = DISPLAY, typeName = "string"), - @OperationParam(name = SOURCE_DETAILS, typeName = "string") + @OperationParam(name = CodeValidationResult.RESULT, typeName = "boolean", min = 1), + @OperationParam(name = CodeValidationResult.MESSAGE, typeName = "string"), + @OperationParam(name = CodeValidationResult.DISPLAY, typeName = "string"), + @OperationParam(name = CodeValidationResult.SOURCE_DETAILS, typeName = "string") }) public IBaseParameters validateCode( HttpServletRequest theServletRequest, @@ -164,7 +159,8 @@ public class ValueSetOperationProvider extends BaseJpaProvider { @OperationParam(name = "system", min = 0, max = 1, typeName = "uri") IPrimitiveType theSystem, @OperationParam(name = "systemVersion", min = 0, max = 1, typeName = "string") IPrimitiveType theSystemVersion, - @OperationParam(name = DISPLAY, min = 0, max = 1, typeName = "string") IPrimitiveType theDisplay, + @OperationParam(name = CodeValidationResult.DISPLAY, min = 0, max = 1, typeName = "string") + IPrimitiveType theDisplay, @OperationParam(name = "coding", min = 0, max = 1, typeName = "Coding") IBaseCoding theCoding, @OperationParam(name = "codeableConcept", min = 0, max = 1, typeName = "CodeableConcept") ICompositeType theCodeableConcept, @@ -228,7 +224,7 @@ public class ValueSetOperationProvider extends BaseJpaProvider { theCodeableConcept, theRequestDetails); } - return toValidateCodeResult(getContext(), result); + return result.toParameters(getContext()); } finally { endRequest(theServletRequest); } @@ -256,7 +252,9 @@ public class ValueSetOperationProvider extends BaseJpaProvider { name = ProviderConstants.OPERATION_INVALIDATE_EXPANSION, idempotent = false, typeName = "ValueSet", - returnParameters = {@OperationParam(name = MESSAGE, typeName = "string", min = 1, max = 1)}) + returnParameters = { + @OperationParam(name = CodeValidationResult.MESSAGE, typeName = "string", min = 1, max = 1) + }) public IBaseParameters invalidateValueSetExpansion( @IdParam IIdType theValueSetId, RequestDetails theRequestDetails, HttpServletRequest theServletRequest) { startRequest(theServletRequest); @@ -265,7 +263,7 @@ public class ValueSetOperationProvider extends BaseJpaProvider { String outcome = myTermReadSvc.invalidatePreCalculatedExpansion(theValueSetId, theRequestDetails); IBaseParameters retVal = ParametersUtil.newInstance(getContext()); - ParametersUtil.addParameterToParametersString(getContext(), retVal, MESSAGE, outcome); + ParametersUtil.addParameterToParametersString(getContext(), retVal, CodeValidationResult.MESSAGE, outcome); return retVal; } finally { @@ -326,22 +324,4 @@ public class ValueSetOperationProvider extends BaseJpaProvider { return options; } - - public static IBaseParameters toValidateCodeResult(FhirContext theContext, CodeValidationResult theResult) { - IBaseParameters retVal = ParametersUtil.newInstance(theContext); - - ParametersUtil.addParameterToParametersBoolean(theContext, retVal, RESULT, theResult.isOk()); - if (isNotBlank(theResult.getMessage())) { - ParametersUtil.addParameterToParametersString(theContext, retVal, MESSAGE, theResult.getMessage()); - } - if (isNotBlank(theResult.getDisplay())) { - ParametersUtil.addParameterToParametersString(theContext, retVal, DISPLAY, theResult.getDisplay()); - } - if (isNotBlank(theResult.getSourceDetails())) { - ParametersUtil.addParameterToParametersString( - theContext, retVal, SOURCE_DETAILS, theResult.getSourceDetails()); - } - - return retVal; - } } diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/provider/ValueSetOperationProviderDstu2.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/provider/ValueSetOperationProviderDstu2.java index 372b030629b..5659c8129c4 100644 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/provider/ValueSetOperationProviderDstu2.java +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/provider/ValueSetOperationProviderDstu2.java @@ -27,6 +27,7 @@ import ca.uhn.fhir.rest.annotation.Operation; import ca.uhn.fhir.rest.annotation.OperationParam; import ca.uhn.fhir.rest.api.server.RequestDetails; import ca.uhn.fhir.util.FhirTerser; +import jakarta.servlet.http.HttpServletRequest; import org.hl7.fhir.instance.model.api.IBaseCoding; import org.hl7.fhir.instance.model.api.IBaseParameters; import org.hl7.fhir.instance.model.api.IBaseResource; @@ -35,7 +36,6 @@ import org.hl7.fhir.instance.model.api.IPrimitiveType; import org.springframework.beans.factory.annotation.Autowired; import java.util.List; -import javax.servlet.http.HttpServletRequest; import static ca.uhn.fhir.jpa.provider.BaseJpaResourceProviderCodeSystem.applyVersionToSystem; @@ -127,7 +127,7 @@ public class ValueSetOperationProviderDstu2 extends ValueSetOperationProvider { @OperationParam(name = "displayLanguage", min = 0, max = 1, typeName = "code") IPrimitiveType theDisplayLanguage, @OperationParam(name = "property", min = 0, max = OperationParam.MAX_UNLIMITED, typeName = "code") - List> theProperties, + List> thePropertyNames, RequestDetails theRequestDetails) { startRequest(theServletRequest); @@ -137,9 +137,16 @@ public class ValueSetOperationProviderDstu2 extends ValueSetOperationProvider { FhirTerser terser = getContext().newTerser(); result = JpaResourceDaoCodeSystem.doLookupCode( - getContext(), terser, myValidationSupport, theCode, theSystem, theCoding, theDisplayLanguage); + getContext(), + terser, + myValidationSupport, + theCode, + theSystem, + theCoding, + theDisplayLanguage, + thePropertyNames); result.throwNotFoundIfAppropriate(); - return result.toParameters(theRequestDetails.getFhirContext(), theProperties); + return result.toParameters(theRequestDetails.getFhirContext(), thePropertyNames); } finally { endRequest(theServletRequest); } diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/provider/dstu3/JpaConformanceProviderDstu3.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/provider/dstu3/JpaConformanceProviderDstu3.java index 2d971de810c..35a78056874 100644 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/provider/dstu3/JpaConformanceProviderDstu3.java +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/provider/dstu3/JpaConformanceProviderDstu3.java @@ -31,6 +31,7 @@ import ca.uhn.fhir.rest.server.util.ISearchParamRegistry; import ca.uhn.fhir.rest.server.util.ResourceSearchParams; import ca.uhn.fhir.util.CoverageIgnore; import ca.uhn.fhir.util.ExtensionConstants; +import jakarta.servlet.http.HttpServletRequest; import org.hl7.fhir.dstu3.model.Bundle; import org.hl7.fhir.dstu3.model.CapabilityStatement; import org.hl7.fhir.dstu3.model.CapabilityStatement.CapabilityStatementRestComponent; @@ -49,7 +50,6 @@ import java.util.List; import java.util.Map; import java.util.Set; import java.util.stream.Collectors; -import javax.servlet.http.HttpServletRequest; import static org.apache.commons.lang3.ObjectUtils.defaultIfNull; import static org.apache.commons.lang3.StringUtils.isBlank; diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/provider/r4/IConsentExtensionProvider.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/provider/r4/IConsentExtensionProvider.java deleted file mode 100644 index ae63a090f16..00000000000 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/provider/r4/IConsentExtensionProvider.java +++ /dev/null @@ -1,51 +0,0 @@ -/*- - * #%L - * HAPI FHIR JPA Server - * %% - * Copyright (C) 2014 - 2023 Smile CDR, Inc. - * %% - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * #L% - */ -package ca.uhn.fhir.jpa.provider.r4; - -import ca.uhn.fhir.util.ExtensionUtil; -import org.hl7.fhir.instance.model.api.IBaseExtension; -import org.hl7.fhir.instance.model.api.IBaseResource; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -import java.util.Collection; - -/** - * Hook for Consent pre-save additions. - * - * @deprecated - we just use Consumer now - * TODO delete this. - */ -@Deprecated(since = "6.3.6", forRemoval = true) -public interface IConsentExtensionProvider extends IMemberMatchConsentHook { - Logger ourLog = LoggerFactory.getLogger(IConsentExtensionProvider.class); - - Collection getConsentExtension(IBaseResource theConsentResource); - - default void accept(IBaseResource theResource) { - Collection extensions = getConsentExtension(theResource); - - for (IBaseExtension ext : extensions) { - IBaseExtension e = ExtensionUtil.addExtension(theResource, ext.getUrl()); - e.setValue(ext.getValue()); - } - ourLog.trace("{} extension(s) added to Consent", extensions.size()); - } -} diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/provider/r4/MemberMatchR4ResourceProvider.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/provider/r4/MemberMatchR4ResourceProvider.java index 5234782144a..6d4426c36c2 100644 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/provider/r4/MemberMatchR4ResourceProvider.java +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/provider/r4/MemberMatchR4ResourceProvider.java @@ -17,155 +17,3 @@ * limitations under the License. * #L% */ -package ca.uhn.fhir.jpa.provider.r4; - -import ca.uhn.fhir.context.FhirContext; -import ca.uhn.fhir.i18n.Msg; -import ca.uhn.fhir.model.api.annotation.Description; -import ca.uhn.fhir.rest.annotation.Operation; -import ca.uhn.fhir.rest.annotation.OperationParam; -import ca.uhn.fhir.rest.api.Constants; -import ca.uhn.fhir.rest.api.server.RequestDetails; -import ca.uhn.fhir.rest.server.exceptions.UnprocessableEntityException; -import ca.uhn.fhir.rest.server.provider.ProviderConstants; -import org.hl7.fhir.r4.model.Consent; -import org.hl7.fhir.r4.model.Coverage; -import org.hl7.fhir.r4.model.Parameters; -import org.hl7.fhir.r4.model.Patient; - -import java.util.Optional; -import javax.annotation.Nullable; - -public class MemberMatchR4ResourceProvider { - - private final MemberMatcherR4Helper myMemberMatcherR4Helper; - private final FhirContext myFhirContext; - - public MemberMatchR4ResourceProvider(FhirContext theFhirContext, MemberMatcherR4Helper theMemberMatcherR4Helper) { - myFhirContext = theFhirContext; - myMemberMatcherR4Helper = theMemberMatcherR4Helper; - } - - /** - * /Patient/$member-match operation - * Basic implementation matching by coverage id or by coverage identifier. Matching by - * Beneficiary (Patient) demographics on family name and birthdate in this version - */ - @Operation( - name = ProviderConstants.OPERATION_MEMBER_MATCH, - typeName = "Patient", - canonicalUrl = "http://hl7.org/fhir/us/davinci-hrex/OperationDefinition/member-match", - idempotent = false, - returnParameters = {@OperationParam(name = "MemberIdentifier", typeName = "string")}) - public Parameters patientMemberMatch( - javax.servlet.http.HttpServletRequest theServletRequest, - @Description( - shortDefinition = - "The target of the operation. Will be returned with Identifier for matched coverage added.") - @OperationParam(name = Constants.PARAM_MEMBER_PATIENT, min = 1, max = 1) - Patient theMemberPatient, - @Description(shortDefinition = "Old coverage information as extracted from beneficiary's card.") - @OperationParam(name = Constants.PARAM_OLD_COVERAGE, min = 1, max = 1) - Coverage oldCoverage, - @Description( - shortDefinition = - "New Coverage information. Provided as a reference. Optionally returned unmodified.") - @OperationParam(name = Constants.PARAM_NEW_COVERAGE, min = 1, max = 1) - Coverage newCoverage, - @Description( - shortDefinition = - "Consent information. Consent held by the system seeking the match that grants permission to access the patient information.") - @OperationParam(name = Constants.PARAM_CONSENT, min = 1, max = 1) - Consent theConsent, - RequestDetails theRequestDetails) { - return doMemberMatchOperation(theMemberPatient, oldCoverage, newCoverage, theConsent, theRequestDetails); - } - - private Parameters doMemberMatchOperation( - Patient theMemberPatient, - Coverage theCoverageToMatch, - Coverage theCoverageToLink, - Consent theConsent, - RequestDetails theRequestDetails) { - - validateParams(theMemberPatient, theCoverageToMatch, theCoverageToLink, theConsent); - - Optional coverageOpt = - myMemberMatcherR4Helper.findMatchingCoverage(theCoverageToMatch, theRequestDetails); - if (coverageOpt.isEmpty()) { - String i18nMessage = - myFhirContext.getLocalizer().getMessage("operation.member.match.error.coverage.not.found"); - throw new UnprocessableEntityException(Msg.code(1155) + i18nMessage); - } - Coverage coverage = coverageOpt.get(); - - Optional patientOpt = myMemberMatcherR4Helper.getBeneficiaryPatient(coverage, theRequestDetails); - if (patientOpt.isEmpty()) { - String i18nMessage = - myFhirContext.getLocalizer().getMessage("operation.member.match.error.beneficiary.not.found"); - throw new UnprocessableEntityException(Msg.code(1156) + i18nMessage); - } - - Patient patient = patientOpt.get(); - if (!myMemberMatcherR4Helper.validPatientMember(patient, theMemberPatient, theRequestDetails)) { - String i18nMessage = - myFhirContext.getLocalizer().getMessage("operation.member.match.error.patient.not.found"); - throw new UnprocessableEntityException(Msg.code(2146) + i18nMessage); - } - - if (patient.getIdentifier().isEmpty()) { - String i18nMessage = myFhirContext - .getLocalizer() - .getMessage("operation.member.match.error.beneficiary.without.identifier"); - throw new UnprocessableEntityException(Msg.code(1157) + i18nMessage); - } - - if (!myMemberMatcherR4Helper.validConsentDataAccess(theConsent)) { - String i18nMessage = myFhirContext - .getLocalizer() - .getMessage("operation.member.match.error.consent.release.data.mismatch"); - throw new UnprocessableEntityException(Msg.code(2147) + i18nMessage); - } - - myMemberMatcherR4Helper.addMemberIdentifierToMemberPatient(theMemberPatient, patient.getIdentifierFirstRep()); - myMemberMatcherR4Helper.updateConsentForMemberMatch(theConsent, patient, theMemberPatient, theRequestDetails); - return myMemberMatcherR4Helper.buildSuccessReturnParameters(theMemberPatient, theCoverageToLink, theConsent); - } - - private void validateParams( - Patient theMemberPatient, Coverage theOldCoverage, Coverage theNewCoverage, Consent theConsent) { - validateParam(theMemberPatient, Constants.PARAM_MEMBER_PATIENT); - validateParam(theOldCoverage, Constants.PARAM_OLD_COVERAGE); - validateParam(theNewCoverage, Constants.PARAM_NEW_COVERAGE); - validateParam(theConsent, Constants.PARAM_CONSENT); - validateMemberPatientParam(theMemberPatient); - validateConsentParam(theConsent); - } - - private void validateParam(@Nullable Object theParam, String theParamName) { - if (theParam == null) { - String i18nMessage = myFhirContext - .getLocalizer() - .getMessage("operation.member.match.error.missing.parameter", theParamName); - throw new UnprocessableEntityException(Msg.code(1158) + i18nMessage); - } - } - - private void validateMemberPatientParam(Patient theMemberPatient) { - if (theMemberPatient.getName().isEmpty()) { - validateParam(null, Constants.PARAM_MEMBER_PATIENT_NAME); - } - - validateParam(theMemberPatient.getName().get(0).getFamily(), Constants.PARAM_MEMBER_PATIENT_NAME); - validateParam(theMemberPatient.getBirthDate(), Constants.PARAM_MEMBER_PATIENT_BIRTHDATE); - } - - private void validateConsentParam(Consent theConsent) { - if (theConsent.getPatient().isEmpty()) { - validateParam(null, Constants.PARAM_CONSENT_PATIENT_REFERENCE); - } - if (theConsent.getPerformer().isEmpty()) { - validateParam(null, Constants.PARAM_CONSENT_PERFORMER_REFERENCE); - } - } -} diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/provider/r4/MemberMatcherR4Helper.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/provider/r4/MemberMatcherR4Helper.java index d9a271feac9..6d4426c36c2 100644 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/provider/r4/MemberMatcherR4Helper.java +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/provider/r4/MemberMatcherR4Helper.java @@ -1,4 +1,4 @@ -/* +/*- * #%L * HAPI FHIR JPA Server * %% @@ -17,280 +17,3 @@ * limitations under the License. * #L% */ -package ca.uhn.fhir.jpa.provider.r4; - -import ca.uhn.fhir.context.FhirContext; -import ca.uhn.fhir.i18n.Msg; -import ca.uhn.fhir.jpa.api.dao.IFhirResourceDao; -import ca.uhn.fhir.jpa.searchparam.SearchParameterMap; -import ca.uhn.fhir.model.primitive.IdDt; -import ca.uhn.fhir.rest.api.server.RequestDetails; -import ca.uhn.fhir.rest.param.DateParam; -import ca.uhn.fhir.rest.param.StringOrListParam; -import ca.uhn.fhir.rest.param.StringParam; -import ca.uhn.fhir.rest.param.TokenOrListParam; -import ca.uhn.fhir.rest.server.exceptions.UnprocessableEntityException; -import ca.uhn.fhir.util.ParametersUtil; -import com.google.common.collect.Lists; -import org.apache.commons.lang3.StringUtils; -import org.hl7.fhir.instance.model.api.IBaseParameters; -import org.hl7.fhir.instance.model.api.IBaseResource; -import org.hl7.fhir.instance.model.api.IIdType; -import org.hl7.fhir.r4.model.CodeableConcept; -import org.hl7.fhir.r4.model.Coding; -import org.hl7.fhir.r4.model.Consent; -import org.hl7.fhir.r4.model.Coverage; -import org.hl7.fhir.r4.model.HumanName; -import org.hl7.fhir.r4.model.Identifier; -import org.hl7.fhir.r4.model.Parameters; -import org.hl7.fhir.r4.model.Patient; -import org.hl7.fhir.r4.model.Reference; - -import java.util.List; -import java.util.Optional; -import java.util.function.Consumer; -import javax.annotation.Nullable; - -import static ca.uhn.fhir.rest.api.Constants.PARAM_CONSENT; -import static ca.uhn.fhir.rest.api.Constants.PARAM_MEMBER_IDENTIFIER; -import static ca.uhn.fhir.rest.api.Constants.PARAM_MEMBER_PATIENT; -import static ca.uhn.fhir.rest.api.Constants.PARAM_NEW_COVERAGE; - -public class MemberMatcherR4Helper { - static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(MemberMatcherR4Helper.class); - - private static final String OUT_COVERAGE_IDENTIFIER_CODE_SYSTEM = "http://terminology.hl7.org/CodeSystem/v2-0203"; - private static final String OUT_COVERAGE_IDENTIFIER_CODE = "MB"; - private static final String OUT_COVERAGE_IDENTIFIER_TEXT = "Member Number"; - private static final String COVERAGE_TYPE = "Coverage"; - private static final String CONSENT_POLICY_REGULAR_TYPE = "regular"; - private static final String CONSENT_POLICY_SENSITIVE_TYPE = "sensitive"; - public static final String CONSENT_IDENTIFIER_CODE_SYSTEM = - "https://smilecdr.com/fhir/ns/member-match-source-client"; - - private final FhirContext myFhirContext; - private final IFhirResourceDao myCoverageDao; - private final IFhirResourceDao myPatientDao; - private final IFhirResourceDao myConsentDao; - /** A hook to modify the Consent before save */ - private final Consumer myConsentModifier; - - private boolean myRegularFilterSupported = false; - - public MemberMatcherR4Helper( - FhirContext theContext, - IFhirResourceDao theCoverageDao, - IFhirResourceDao thePatientDao, - IFhirResourceDao theConsentDao, - @Nullable IMemberMatchConsentHook theConsentModifier) { - myFhirContext = theContext; - myConsentDao = theConsentDao; - myPatientDao = thePatientDao; - myCoverageDao = theCoverageDao; - myConsentModifier = (theConsentModifier != null) ? theConsentModifier : noop -> {}; - } - - /** - * Find Coverage matching the received member (Patient) by coverage id or by coverage identifier only - */ - public Optional findMatchingCoverage(Coverage theCoverageToMatch, RequestDetails theRequestDetails) { - // search by received old coverage id - List foundCoverages = findCoverageByCoverageId(theCoverageToMatch, theRequestDetails); - if (foundCoverages.size() == 1 && isCoverage(foundCoverages.get(0))) { - return Optional.of((Coverage) foundCoverages.get(0)); - } - - // search by received old coverage identifier - foundCoverages = findCoverageByCoverageIdentifier(theCoverageToMatch, theRequestDetails); - if (foundCoverages.size() == 1 && isCoverage(foundCoverages.get(0))) { - return Optional.of((Coverage) foundCoverages.get(0)); - } - - return Optional.empty(); - } - - private List findCoverageByCoverageIdentifier( - Coverage theCoverageToMatch, RequestDetails theRequestDetails) { - TokenOrListParam identifierParam = new TokenOrListParam(); - for (Identifier identifier : theCoverageToMatch.getIdentifier()) { - identifierParam.add(identifier.getSystem(), identifier.getValue()); - } - - SearchParameterMap paramMap = new SearchParameterMap().add("identifier", identifierParam); - ca.uhn.fhir.rest.api.server.IBundleProvider retVal = myCoverageDao.search(paramMap, theRequestDetails); - - return retVal.getAllResources(); - } - - private boolean isCoverage(IBaseResource theIBaseResource) { - return theIBaseResource.fhirType().equals(COVERAGE_TYPE); - } - - private List findCoverageByCoverageId( - Coverage theCoverageToMatch, RequestDetails theRequestDetails) { - SearchParameterMap paramMap = new SearchParameterMap().add("_id", new StringParam(theCoverageToMatch.getId())); - ca.uhn.fhir.rest.api.server.IBundleProvider retVal = myCoverageDao.search(paramMap, theRequestDetails); - - return retVal.getAllResources(); - } - - public void updateConsentForMemberMatch( - Consent theConsent, Patient thePatient, Patient theMemberPatient, RequestDetails theRequestDetails) { - addIdentifierToConsent(theConsent, theMemberPatient); - updateConsentPatientAndPerformer(theConsent, thePatient); - myConsentModifier.accept(theConsent); - - // Trust RequestTenantPartitionInterceptor or PatientIdPartitionInterceptor to assign the partition. - myConsentDao.create(theConsent, theRequestDetails); - } - - public Parameters buildSuccessReturnParameters(Patient theMemberPatient, Coverage theCoverage, Consent theConsent) { - IBaseParameters parameters = ParametersUtil.newInstance(myFhirContext); - ParametersUtil.addParameterToParameters(myFhirContext, parameters, PARAM_MEMBER_PATIENT, theMemberPatient); - ParametersUtil.addParameterToParameters(myFhirContext, parameters, PARAM_NEW_COVERAGE, theCoverage); - ParametersUtil.addParameterToParameters(myFhirContext, parameters, PARAM_CONSENT, theConsent); - ParametersUtil.addParameterToParameters( - myFhirContext, parameters, PARAM_MEMBER_IDENTIFIER, getIdentifier(theMemberPatient)); - return (Parameters) parameters; - } - - private Identifier getIdentifier(Patient theMemberPatient) { - return theMemberPatient.getIdentifier().stream() - .filter(this::isTypeMB) - .findFirst() - .orElseThrow(() -> { - String i18nMessage = myFhirContext - .getLocalizer() - .getMessage("operation.member.match.error.beneficiary.without.identifier"); - return new UnprocessableEntityException(Msg.code(2219) + i18nMessage); - }); - } - - private boolean isTypeMB(Identifier theMemberIdentifier) { - return theMemberIdentifier.getType() != null - && theMemberIdentifier.getType().getCoding().stream() - .anyMatch(typeCoding -> typeCoding.getCode().equals("MB")); - } - - public void addMemberIdentifierToMemberPatient(Patient theMemberPatient, Identifier theNewIdentifier) { - Coding coding = new Coding() - .setSystem(OUT_COVERAGE_IDENTIFIER_CODE_SYSTEM) - .setCode(OUT_COVERAGE_IDENTIFIER_CODE) - .setDisplay(OUT_COVERAGE_IDENTIFIER_TEXT) - .setUserSelected(false); - - CodeableConcept concept = - new CodeableConcept().setCoding(Lists.newArrayList(coding)).setText(OUT_COVERAGE_IDENTIFIER_TEXT); - - Identifier newIdentifier = new Identifier() - .setUse(Identifier.IdentifierUse.USUAL) - .setType(concept) - .setSystem(theNewIdentifier.getSystem()) - .setValue(theNewIdentifier.getValue()); - - theMemberPatient.addIdentifier(newIdentifier); - } - - public Optional getBeneficiaryPatient(Coverage theCoverage, RequestDetails theRequestDetails) { - if (theCoverage.getBeneficiaryTarget() == null && theCoverage.getBeneficiary() == null) { - return Optional.empty(); - } - - if (theCoverage.getBeneficiaryTarget() != null - && !theCoverage.getBeneficiaryTarget().getIdentifier().isEmpty()) { - return Optional.of(theCoverage.getBeneficiaryTarget()); - } - - Reference beneficiaryRef = theCoverage.getBeneficiary(); - if (beneficiaryRef == null) { - return Optional.empty(); - } - - if (beneficiaryRef.getResource() != null) { - return Optional.of((Patient) beneficiaryRef.getResource()); - } - - if (beneficiaryRef.getReference() == null) { - return Optional.empty(); - } - - Patient beneficiary = myPatientDao.read(new IdDt(beneficiaryRef.getReference()), theRequestDetails); - return Optional.ofNullable(beneficiary); - } - - /** - * Matching by member patient demographics - family name and birthdate only - */ - public boolean validPatientMember( - Patient thePatientFromContract, Patient thePatientToMatch, RequestDetails theRequestDetails) { - if (thePatientFromContract == null - || thePatientFromContract.getIdElement() == null - || thePatientToMatch == null) { - return false; - } - StringOrListParam familyName = new StringOrListParam(); - for (HumanName name : thePatientToMatch.getName()) { - familyName.addOr(new StringParam(name.getFamily())); - } - SearchParameterMap map = new SearchParameterMap() - .add("family", familyName) - .add( - "birthdate", - new DateParam(thePatientToMatch.getBirthDateElement().getValueAsString())); - ca.uhn.fhir.rest.api.server.IBundleProvider bundle = myPatientDao.search(map, theRequestDetails); - for (IBaseResource patientResource : bundle.getAllResources()) { - IIdType patientId = patientResource.getIdElement().toUnqualifiedVersionless(); - if (patientId - .getValue() - .equals(thePatientFromContract - .getIdElement() - .toUnqualifiedVersionless() - .getValue())) { - return true; - } - } - return false; - } - - public boolean validConsentDataAccess(Consent theConsent) { - if (theConsent.getPolicy().isEmpty()) { - return false; - } - for (Consent.ConsentPolicyComponent policyComponent : theConsent.getPolicy()) { - if (policyComponent.getUri() == null || !validConsentPolicy(policyComponent.getUri())) { - return false; - } - } - return true; - } - - /** - * The consent policy rules are - * - * described here. - */ - private boolean validConsentPolicy(String thePolicyUri) { - String policyTypes = StringUtils.substringAfterLast(thePolicyUri, "#"); - if (policyTypes.equals(CONSENT_POLICY_SENSITIVE_TYPE)) { - return true; - } - return policyTypes.equals(CONSENT_POLICY_REGULAR_TYPE) && myRegularFilterSupported; - } - - private void addIdentifierToConsent(Consent theConsent, Patient thePatient) { - String consentId = getIdentifier(thePatient).getValue(); - Identifier consentIdentifier = - new Identifier().setSystem(CONSENT_IDENTIFIER_CODE_SYSTEM).setValue(consentId); - theConsent.addIdentifier(consentIdentifier); - } - - public void setRegularFilterSupported(boolean theRegularFilterSupported) { - myRegularFilterSupported = theRegularFilterSupported; - } - - private void updateConsentPatientAndPerformer(Consent theConsent, Patient thePatient) { - String patientRef = thePatient.getIdElement().toUnqualifiedVersionless().getValue(); - theConsent.getPatient().setReference(patientRef); - theConsent.getPerformer().set(0, new Reference(patientRef)); - } -} diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/reindex/Batch2DaoSvcImpl.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/reindex/Batch2DaoSvcImpl.java index 1a8e3e63883..743639be839 100644 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/reindex/Batch2DaoSvcImpl.java +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/reindex/Batch2DaoSvcImpl.java @@ -23,13 +23,13 @@ import ca.uhn.fhir.context.FhirContext; import ca.uhn.fhir.context.RuntimeResourceDefinition; import ca.uhn.fhir.i18n.Msg; import ca.uhn.fhir.interceptor.model.RequestPartitionId; -import ca.uhn.fhir.jpa.api.config.JpaStorageSettings; import ca.uhn.fhir.jpa.api.dao.DaoRegistry; import ca.uhn.fhir.jpa.api.dao.IFhirResourceDao; -import ca.uhn.fhir.jpa.api.pid.EmptyResourcePidList; -import ca.uhn.fhir.jpa.api.pid.HomogeneousResourcePidList; import ca.uhn.fhir.jpa.api.pid.IResourcePidList; -import ca.uhn.fhir.jpa.api.pid.MixedResourcePidList; +import ca.uhn.fhir.jpa.api.pid.IResourcePidStream; +import ca.uhn.fhir.jpa.api.pid.StreamTemplate; +import ca.uhn.fhir.jpa.api.pid.TypedResourcePid; +import ca.uhn.fhir.jpa.api.pid.TypedResourceStream; import ca.uhn.fhir.jpa.api.svc.IBatch2DaoSvc; import ca.uhn.fhir.jpa.dao.data.IResourceTableDao; import ca.uhn.fhir.jpa.dao.tx.IHapiTransactionService; @@ -37,20 +37,17 @@ import ca.uhn.fhir.jpa.model.dao.JpaPid; import ca.uhn.fhir.jpa.searchparam.MatchUrlService; import ca.uhn.fhir.jpa.searchparam.SearchParameterMap; import ca.uhn.fhir.rest.api.Constants; -import ca.uhn.fhir.rest.api.SortOrderEnum; import ca.uhn.fhir.rest.api.SortSpec; import ca.uhn.fhir.rest.api.server.SystemRequestDetails; -import ca.uhn.fhir.rest.api.server.storage.IResourcePersistentId; import ca.uhn.fhir.rest.server.exceptions.InternalErrorException; -import org.springframework.data.domain.Pageable; -import org.springframework.data.domain.Slice; +import ca.uhn.fhir.util.DateRangeUtil; +import jakarta.annotation.Nonnull; +import jakarta.annotation.Nullable; +import org.apache.commons.lang3.Validate; -import java.util.ArrayList; import java.util.Date; -import java.util.List; -import java.util.stream.Collectors; -import javax.annotation.Nonnull; -import javax.annotation.Nullable; +import java.util.function.Supplier; +import java.util.stream.Stream; public class Batch2DaoSvcImpl implements IBatch2DaoSvc { private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(Batch2DaoSvcImpl.class); @@ -65,8 +62,6 @@ public class Batch2DaoSvcImpl implements IBatch2DaoSvc { private final IHapiTransactionService myTransactionService; - private final JpaStorageSettings myJpaStorageSettings; - @Override public boolean isAllResourceTypeSupported() { return true; @@ -77,113 +72,110 @@ public class Batch2DaoSvcImpl implements IBatch2DaoSvc { MatchUrlService theMatchUrlService, DaoRegistry theDaoRegistry, FhirContext theFhirContext, - IHapiTransactionService theTransactionService, - JpaStorageSettings theJpaStorageSettings) { + IHapiTransactionService theTransactionService) { myResourceTableDao = theResourceTableDao; myMatchUrlService = theMatchUrlService; myDaoRegistry = theDaoRegistry; myFhirContext = theFhirContext; myTransactionService = theTransactionService; - myJpaStorageSettings = theJpaStorageSettings; } @Override - public IResourcePidList fetchResourceIdsPage( - Date theStart, Date theEnd, @Nullable RequestPartitionId theRequestPartitionId, @Nullable String theUrl) { - return myTransactionService - .withSystemRequest() - .withRequestPartitionId(theRequestPartitionId) - .execute(() -> { - if (theUrl == null) { - return fetchResourceIdsPageNoUrl(theStart, theEnd, theRequestPartitionId); - } else { - return fetchResourceIdsPageWithUrl(theEnd, theUrl, theRequestPartitionId); - } - }); + public IResourcePidStream fetchResourceIdStream( + Date theStart, Date theEnd, RequestPartitionId theRequestPartitionId, String theUrl) { + if (theUrl == null) { + return makeStreamResult( + theRequestPartitionId, () -> streamResourceIdsNoUrl(theStart, theEnd, theRequestPartitionId)); + } else { + return makeStreamResult( + theRequestPartitionId, + () -> streamResourceIdsWithUrl(theStart, theEnd, theUrl, theRequestPartitionId)); + } + } + + private Stream streamResourceIdsWithUrl( + Date theStart, Date theEnd, String theUrl, RequestPartitionId theRequestPartitionId) { + validateUrl(theUrl); + + SearchParameterMap searchParamMap = parseQuery(theUrl); + searchParamMap.setLastUpdated(DateRangeUtil.narrowDateRange(searchParamMap.getLastUpdated(), theStart, theEnd)); + + String resourceType = theUrl.substring(0, theUrl.indexOf('?')); + IFhirResourceDao dao = myDaoRegistry.getResourceDao(resourceType); + + SystemRequestDetails request = new SystemRequestDetails().setRequestPartitionId(theRequestPartitionId); + + return dao.searchForIdStream(searchParamMap, request, null).map(pid -> new TypedResourcePid(resourceType, pid)); + } + + private static TypedResourcePid typedPidFromQueryArray(Object[] thePidTypeDateArray) { + String resourceType = (String) thePidTypeDateArray[1]; + Long pid = (Long) thePidTypeDateArray[0]; + return new TypedResourcePid(resourceType, JpaPid.fromId(pid)); } @Nonnull - private HomogeneousResourcePidList fetchResourceIdsPageWithUrl( - Date theEnd, @Nonnull String theUrl, @Nullable RequestPartitionId theRequestPartitionId) { + private TypedResourceStream makeStreamResult( + RequestPartitionId theRequestPartitionId, Supplier> streamSupplier) { + + IHapiTransactionService.IExecutionBuilder txSettings = + myTransactionService.withSystemRequest().withRequestPartitionId(theRequestPartitionId); + + StreamTemplate streamTemplate = + StreamTemplate.fromSupplier(streamSupplier).withTransactionAdvice(txSettings); + + return new TypedResourceStream(theRequestPartitionId, streamTemplate); + } + + @Nonnull + private Stream streamResourceIdsNoUrl( + Date theStart, Date theEnd, RequestPartitionId theRequestPartitionId) { + Stream rowStream; + if (theRequestPartitionId == null || theRequestPartitionId.isAllPartitions()) { + ourLog.debug("Search for resources - all partitions"); + rowStream = myResourceTableDao.streamIdsTypesAndUpdateTimesOfResourcesWithinUpdatedRangeOrderedFromOldest( + theStart, theEnd); + } else if (theRequestPartitionId.isDefaultPartition()) { + ourLog.debug("Search for resources - default partition"); + rowStream = + myResourceTableDao + .streamIdsTypesAndUpdateTimesOfResourcesWithinUpdatedRangeOrderedFromOldestForDefaultPartition( + theStart, theEnd); + } else { + ourLog.debug("Search for resources - partition {}", theRequestPartitionId); + rowStream = + myResourceTableDao + .streamIdsTypesAndUpdateTimesOfResourcesWithinUpdatedRangeOrderedFromOldestForPartitionIds( + theStart, theEnd, theRequestPartitionId.getPartitionIds()); + } + + return rowStream.map(Batch2DaoSvcImpl::typedPidFromQueryArray); + } + + @Deprecated(since = "6.11", forRemoval = true) // delete once the default method in the interface is gone. + @Override + public IResourcePidList fetchResourceIdsPage( + Date theStart, Date theEnd, @Nullable RequestPartitionId theRequestPartitionId, @Nullable String theUrl) { + Validate.isTrue(false, "Unimplemented"); + return null; + } + + private static void validateUrl(@Nonnull String theUrl) { if (!theUrl.contains("?")) { throw new InternalErrorException(Msg.code(2422) + "this should never happen: URL is missing a '?'"); } - - final Integer internalSynchronousSearchSize = myJpaStorageSettings.getInternalSynchronousSearchSize(); - - if (internalSynchronousSearchSize == null || internalSynchronousSearchSize <= 0) { - throw new InternalErrorException(Msg.code(2423) - + "this should never happen: internalSynchronousSearchSize is null or less than or equal to 0"); - } - - List currentIds = fetchResourceIdsPageWithUrl(0, theUrl, theRequestPartitionId); - ourLog.debug("FIRST currentIds: {}", currentIds.size()); - - final List allIds = new ArrayList<>(currentIds); - - while (internalSynchronousSearchSize < currentIds.size()) { - // Ensure the offset is set to the last ID in the cumulative List, otherwise, we'll be stuck in an infinite - // loop here: - currentIds = fetchResourceIdsPageWithUrl(allIds.size(), theUrl, theRequestPartitionId); - ourLog.debug("NEXT currentIds: {}", currentIds.size()); - - allIds.addAll(currentIds); - } - - final String resourceType = theUrl.substring(0, theUrl.indexOf('?')); - - return new HomogeneousResourcePidList(resourceType, allIds, theEnd, theRequestPartitionId); } - private List fetchResourceIdsPageWithUrl( - int theOffset, String theUrl, RequestPartitionId theRequestPartitionId) { + @Nonnull + private SearchParameterMap parseQuery(String theUrl) { String resourceType = theUrl.substring(0, theUrl.indexOf('?')); RuntimeResourceDefinition def = myFhirContext.getResourceDefinition(resourceType); SearchParameterMap searchParamMap = myMatchUrlService.translateMatchUrl(theUrl, def); - searchParamMap.setSort(new SortSpec(Constants.PARAM_ID, SortOrderEnum.ASC)); - searchParamMap.setOffset(theOffset); - searchParamMap.setLoadSynchronousUpTo(myJpaStorageSettings.getInternalSynchronousSearchSize() + 1); - - IFhirResourceDao dao = myDaoRegistry.getResourceDao(resourceType); - SystemRequestDetails request = new SystemRequestDetails(); - request.setRequestPartitionId(theRequestPartitionId); - - return dao.searchForIds(searchParamMap, request); - } - - @Nonnull - private IResourcePidList fetchResourceIdsPageNoUrl( - Date theStart, Date theEnd, RequestPartitionId theRequestPartitionId) { - final Pageable page = Pageable.unpaged(); - Slice slice; - if (theRequestPartitionId == null || theRequestPartitionId.isAllPartitions()) { - slice = myResourceTableDao.findIdsTypesAndUpdateTimesOfResourcesWithinUpdatedRangeOrderedFromOldest( - page, theStart, theEnd); - } else if (theRequestPartitionId.isDefaultPartition()) { - slice = - myResourceTableDao - .findIdsTypesAndUpdateTimesOfResourcesWithinUpdatedRangeOrderedFromOldestForDefaultPartition( - page, theStart, theEnd); - } else { - slice = - myResourceTableDao - .findIdsTypesAndUpdateTimesOfResourcesWithinUpdatedRangeOrderedFromOldestForPartitionIds( - page, theStart, theEnd, theRequestPartitionId.getPartitionIds()); - } - - List content = slice.getContent(); - if (content.isEmpty()) { - return new EmptyResourcePidList(); - } - - List ids = - content.stream().map(t -> JpaPid.fromId((Long) t[0])).collect(Collectors.toList()); - - List types = content.stream().map(t -> (String) t[1]).collect(Collectors.toList()); - - Date lastDate = (Date) content.get(content.size() - 1)[2]; - - return new MixedResourcePidList(types, ids, lastDate, theRequestPartitionId); + // this matches idx_res_type_del_updated + searchParamMap.setSort(new SortSpec(Constants.PARAM_LASTUPDATED).setChain(new SortSpec(Constants.PARAM_PID))); + // TODO this limits us to 2G resources. + searchParamMap.setLoadSynchronousUpTo(Integer.MAX_VALUE); + return searchParamMap; } } diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/search/DatabaseBackedPagingProvider.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/search/DatabaseBackedPagingProvider.java index 289744fca73..c81f1b78479 100644 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/search/DatabaseBackedPagingProvider.java +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/search/DatabaseBackedPagingProvider.java @@ -25,10 +25,9 @@ import ca.uhn.fhir.jpa.partition.IRequestPartitionHelperSvc; import ca.uhn.fhir.rest.api.server.IBundleProvider; import ca.uhn.fhir.rest.api.server.RequestDetails; import ca.uhn.fhir.rest.server.BasePagingProvider; +import jakarta.annotation.Nullable; import org.springframework.beans.factory.annotation.Autowired; -import javax.annotation.Nullable; - // Note: this class is not annotated with @Service because we want to // explicitly define it in BaseConfig.java. This is done so that // implementors can override if they want to. diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/search/ExceptionService.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/search/ExceptionService.java index 5802b090fe6..84eb70ba303 100644 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/search/ExceptionService.java +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/search/ExceptionService.java @@ -22,8 +22,7 @@ package ca.uhn.fhir.jpa.search; import ca.uhn.fhir.context.FhirContext; import ca.uhn.fhir.rest.server.exceptions.ResourceGoneException; import ca.uhn.fhir.rest.server.method.PageMethodBinding; - -import javax.annotation.Nonnull; +import jakarta.annotation.Nonnull; public class ExceptionService { private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(SearchCoordinatorSvcImpl.class); diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/search/HapiHSearchAnalysisConfigurers.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/search/HapiHSearchAnalysisConfigurers.java index a280d7e8293..b17cff10ace 100644 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/search/HapiHSearchAnalysisConfigurers.java +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/search/HapiHSearchAnalysisConfigurers.java @@ -60,68 +60,68 @@ public class HapiHSearchAnalysisConfigurers { theLuceneCtx .analyzer("autocompleteEdgeAnalyzer") .custom() - .tokenizer(PatternTokenizerFactory.class) - .param("pattern", "(.*)") - .param("group", "1") - .tokenFilter(LowerCaseFilterFactory.class) - .tokenFilter(StopFilterFactory.class) - .tokenFilter(EdgeNGramFilterFactory.class) + .tokenizer(PatternTokenizerFactory.NAME) + .param(PatternTokenizerFactory.PATTERN, "(.*)") + .param(PatternTokenizerFactory.GROUP, "1") + .tokenFilter(LowerCaseFilterFactory.NAME) + .tokenFilter(StopFilterFactory.NAME) + .tokenFilter(EdgeNGramFilterFactory.NAME) .param("minGramSize", "3") .param("maxGramSize", "50"); theLuceneCtx .analyzer("autocompletePhoneticAnalyzer") .custom() - .tokenizer(StandardTokenizerFactory.class) - .tokenFilter(StopFilterFactory.class) - .tokenFilter(PhoneticFilterFactory.class) - .param("encoder", "DoubleMetaphone") - .tokenFilter(SnowballPorterFilterFactory.class) + .tokenizer(StandardTokenizerFactory.NAME) + .tokenFilter(StopFilterFactory.NAME) + .tokenFilter(PhoneticFilterFactory.NAME) + .param(PhoneticFilterFactory.ENCODER, "DoubleMetaphone") + .tokenFilter(SnowballPorterFilterFactory.NAME) .param("language", "English"); theLuceneCtx .analyzer("autocompleteNGramAnalyzer") .custom() - .tokenizer(StandardTokenizerFactory.class) - .tokenFilter(WordDelimiterGraphFilterFactory.class) - .tokenFilter(LowerCaseFilterFactory.class) - .tokenFilter(NGramFilterFactory.class) + .tokenizer(StandardTokenizerFactory.NAME) + .tokenFilter(WordDelimiterGraphFilterFactory.NAME) + .tokenFilter(LowerCaseFilterFactory.NAME) + .tokenFilter(NGramFilterFactory.NAME) .param("minGramSize", "3") .param("maxGramSize", "20"); theLuceneCtx .analyzer("autocompleteWordEdgeAnalyzer") .custom() - .tokenizer(StandardTokenizerFactory.class) - .tokenFilter(LowerCaseFilterFactory.class) - .tokenFilter(StopFilterFactory.class) - .tokenFilter(EdgeNGramFilterFactory.class) + .tokenizer(StandardTokenizerFactory.NAME) + .tokenFilter(LowerCaseFilterFactory.NAME) + .tokenFilter(StopFilterFactory.NAME) + .tokenFilter(EdgeNGramFilterFactory.NAME) .param("minGramSize", "3") .param("maxGramSize", "20"); theLuceneCtx .analyzer(STANDARD_ANALYZER) .custom() - .tokenizer(StandardTokenizerFactory.class) - .tokenFilter(LowerCaseFilterFactory.class) - .tokenFilter(ASCIIFoldingFilterFactory.class); + .tokenizer(StandardTokenizerFactory.NAME) + .tokenFilter(LowerCaseFilterFactory.NAME) + .tokenFilter(ASCIIFoldingFilterFactory.NAME); theLuceneCtx .analyzer(NORM_STRING_ANALYZER) .custom() - .tokenizer(KeywordTokenizerFactory.class) - .tokenFilter(LowerCaseFilterFactory.class) - .tokenFilter(ASCIIFoldingFilterFactory.class); + .tokenizer(KeywordTokenizerFactory.NAME) + .tokenFilter(LowerCaseFilterFactory.NAME) + .tokenFilter(ASCIIFoldingFilterFactory.NAME); - theLuceneCtx.analyzer(EXACT_ANALYZER).custom().tokenizer(KeywordTokenizerFactory.class); + theLuceneCtx.analyzer(EXACT_ANALYZER).custom().tokenizer(KeywordTokenizerFactory.NAME); - theLuceneCtx.analyzer("conceptParentPidsAnalyzer").custom().tokenizer(WhitespaceTokenizerFactory.class); + theLuceneCtx.analyzer("conceptParentPidsAnalyzer").custom().tokenizer(WhitespaceTokenizerFactory.NAME); theLuceneCtx .normalizer(LOWERCASE_ASCIIFOLDING_NORMALIZER) .custom() - .tokenFilter(LowerCaseFilterFactory.class) - .tokenFilter(ASCIIFoldingFilterFactory.class); + .tokenFilter(LowerCaseFilterFactory.NAME) + .tokenFilter(ASCIIFoldingFilterFactory.NAME); } } diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/search/PersistedJpaBundleProvider.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/search/PersistedJpaBundleProvider.java index 1738b02f120..5eb365b37fd 100644 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/search/PersistedJpaBundleProvider.java +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/search/PersistedJpaBundleProvider.java @@ -46,7 +46,6 @@ import ca.uhn.fhir.jpa.search.cache.SearchCacheStatusEnum; import ca.uhn.fhir.jpa.searchparam.SearchParameterMap; import ca.uhn.fhir.jpa.util.MemoryCacheService; import ca.uhn.fhir.jpa.util.QueryParameterUtils; -import ca.uhn.fhir.model.api.Include; import ca.uhn.fhir.model.primitive.InstantDt; import ca.uhn.fhir.rest.api.server.IBundleProvider; import ca.uhn.fhir.rest.api.server.IPreResourceAccessDetails; @@ -59,6 +58,9 @@ import ca.uhn.fhir.rest.server.method.ResponsePage; import ca.uhn.fhir.rest.server.servlet.ServletRequestDetails; import ca.uhn.fhir.rest.server.util.CompositeInterceptorBroadcaster; import com.google.common.annotations.VisibleForTesting; +import jakarta.annotation.Nonnull; +import jakarta.persistence.EntityManager; +import jakarta.persistence.PersistenceContext; import org.hl7.fhir.instance.model.api.IBaseResource; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -70,9 +72,6 @@ import java.util.List; import java.util.Optional; import java.util.Set; import java.util.function.Function; -import javax.annotation.Nonnull; -import javax.persistence.EntityManager; -import javax.persistence.PersistenceContext; public class PersistedJpaBundleProvider implements IBundleProvider { @@ -126,7 +125,7 @@ public class PersistedJpaBundleProvider implements IBundleProvider { * of this class, since it's a prototype */ private Search mySearchEntity; - private String myUuid; + private final String myUuid; private SearchCacheStatusEnum myCacheStatus; private RequestPartitionId myRequestPartitionId; @@ -152,6 +151,11 @@ public class PersistedJpaBundleProvider implements IBundleProvider { myRequestPartitionHelperSvc = theRequestPartitionHelperSvc; } + @VisibleForTesting + public Search getSearchEntityForTesting() { + return getSearchEntity(); + } + protected Search getSearchEntity() { return mySearchEntity; } @@ -255,13 +259,21 @@ public class PersistedJpaBundleProvider implements IBundleProvider { final ISearchBuilder sb = mySearchBuilderFactory.newSearchBuilder(dao, resourceName, resourceType); RequestPartitionId requestPartitionId = getRequestPartitionId(); - final List pidsSubList = - mySearchCoordinatorSvc.getResources(myUuid, theFromIndex, theToIndex, myRequest, requestPartitionId); + // we request 1 more resource than we need + // this is so we can be sure of when we hit the last page + // (when doing offset searches) + final List pidsSubList = mySearchCoordinatorSvc.getResources( + myUuid, theFromIndex, theToIndex + 1, myRequest, requestPartitionId); + // max list size should be either the entire list, or from - to length + int maxSize = Math.min(theToIndex - theFromIndex, pidsSubList.size()); + theResponsePageBuilder.setTotalRequestedResourcesFetched(pidsSubList.size()); + + List firstBatchOfPids = pidsSubList.subList(0, maxSize); List resources = myTxService .withRequest(myRequest) .withRequestPartitionId(requestPartitionId) .execute(() -> { - return toResourceList(sb, pidsSubList, theResponsePageBuilder); + return toResourceList(sb, firstBatchOfPids, theResponsePageBuilder); }); return resources; @@ -463,72 +475,70 @@ public class PersistedJpaBundleProvider implements IBundleProvider { if (mySearchEntity.getSearchType() == SearchTypeEnum.SEARCH) { Integer maxIncludes = myStorageSettings.getMaximumIncludesToLoadPerPage(); - // Decide whether to perform include or revincludes first based on which one has iterate. - boolean performIncludesBeforeRevincludes = shouldPerformIncludesBeforeRevincudes(); - - if (performIncludesBeforeRevincludes) { - // Load _includes - Set includedPids = theSearchBuilder.loadIncludes( - myContext, - myEntityManager, - thePids, - mySearchEntity.toIncludesList(), - false, - mySearchEntity.getLastUpdated(), - myUuid, - myRequest, - maxIncludes); - if (maxIncludes != null) { - maxIncludes -= includedPids.size(); - } - thePids.addAll(includedPids); - includedPidList.addAll(includedPids); - - // Load _revincludes - Set revIncludedPids = theSearchBuilder.loadIncludes( - myContext, - myEntityManager, - thePids, - mySearchEntity.toRevIncludesList(), - true, - mySearchEntity.getLastUpdated(), - myUuid, - myRequest, - maxIncludes); - thePids.addAll(revIncludedPids); - includedPidList.addAll(revIncludedPids); - } else { - // Load _revincludes - Set revIncludedPids = theSearchBuilder.loadIncludes( - myContext, - myEntityManager, - thePids, - mySearchEntity.toRevIncludesList(), - true, - mySearchEntity.getLastUpdated(), - myUuid, - myRequest, - maxIncludes); - if (maxIncludes != null) { - maxIncludes -= revIncludedPids.size(); - } - thePids.addAll(revIncludedPids); - includedPidList.addAll(revIncludedPids); - - // Load _includes - Set includedPids = theSearchBuilder.loadIncludes( - myContext, - myEntityManager, - thePids, - mySearchEntity.toIncludesList(), - false, - mySearchEntity.getLastUpdated(), - myUuid, - myRequest, - maxIncludes); - thePids.addAll(includedPids); - includedPidList.addAll(includedPids); + // Load non-iterate _revincludes + Set nonIterateRevIncludedPids = theSearchBuilder.loadIncludes( + myContext, + myEntityManager, + thePids, + mySearchEntity.toRevIncludesList(false), + true, + mySearchEntity.getLastUpdated(), + myUuid, + myRequest, + maxIncludes); + if (maxIncludes != null) { + maxIncludes -= nonIterateRevIncludedPids.size(); } + thePids.addAll(nonIterateRevIncludedPids); + includedPidList.addAll(nonIterateRevIncludedPids); + + // Load non-iterate _includes + Set nonIterateIncludedPids = theSearchBuilder.loadIncludes( + myContext, + myEntityManager, + thePids, + mySearchEntity.toIncludesList(false), + false, + mySearchEntity.getLastUpdated(), + myUuid, + myRequest, + maxIncludes); + if (maxIncludes != null) { + maxIncludes -= nonIterateIncludedPids.size(); + } + thePids.addAll(nonIterateIncludedPids); + includedPidList.addAll(nonIterateIncludedPids); + + // Load iterate _revinclude + Set iterateRevIncludedPids = theSearchBuilder.loadIncludes( + myContext, + myEntityManager, + thePids, + mySearchEntity.toRevIncludesList(true), + true, + mySearchEntity.getLastUpdated(), + myUuid, + myRequest, + maxIncludes); + if (maxIncludes != null) { + maxIncludes -= iterateRevIncludedPids.size(); + } + thePids.addAll(iterateRevIncludedPids); + includedPidList.addAll(iterateRevIncludedPids); + + // Load iterate _includes + Set iterateIncludedPids = theSearchBuilder.loadIncludes( + myContext, + myEntityManager, + thePids, + mySearchEntity.toIncludesList(true), + false, + mySearchEntity.getLastUpdated(), + myUuid, + myRequest, + maxIncludes); + thePids.addAll(iterateIncludedPids); + includedPidList.addAll(iterateIncludedPids); } // Execute the query and make sure we return distinct results @@ -539,28 +549,14 @@ public class PersistedJpaBundleProvider implements IBundleProvider { // this can (potentially) change the results being returned. int precount = resources.size(); resources = ServerInterceptorUtil.fireStoragePreshowResource(resources, myRequest, myInterceptorBroadcaster); - // we only care about omitted results from *this* page - theResponsePageBuilder.setToOmittedResourceCount(precount - resources.size()); + // we only care about omitted results from this page + theResponsePageBuilder.setOmittedResourceCount(precount - resources.size()); theResponsePageBuilder.setResources(resources); theResponsePageBuilder.setIncludedResourceCount(includedPidList.size()); return resources; } - private boolean shouldPerformIncludesBeforeRevincudes() { - // When revincludes contain a :iterate, we should perform them last so they can iterate through the includes - // found so far - boolean retval = false; - - for (Include nextInclude : mySearchEntity.toRevIncludesList()) { - if (nextInclude.isRecurse()) { - retval = true; - break; - } - } - return retval; - } - public void setInterceptorBroadcaster(IInterceptorBroadcaster theInterceptorBroadcaster) { myInterceptorBroadcaster = theInterceptorBroadcaster; } diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/search/PersistedJpaBundleProviderFactory.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/search/PersistedJpaBundleProviderFactory.java index 5e89f521a09..6b9f48fc86e 100644 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/search/PersistedJpaBundleProviderFactory.java +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/search/PersistedJpaBundleProviderFactory.java @@ -55,14 +55,12 @@ public class PersistedJpaBundleProviderFactory { public PersistedJpaSearchFirstPageBundleProvider newInstanceFirstPage( RequestDetails theRequestDetails, - Search theSearch, SearchTask theTask, ISearchBuilder theSearchBuilder, RequestPartitionId theRequestPartitionId) { return (PersistedJpaSearchFirstPageBundleProvider) myApplicationContext.getBean( JpaConfig.PERSISTED_JPA_SEARCH_FIRST_PAGE_BUNDLE_PROVIDER, theRequestDetails, - theSearch, theTask, theSearchBuilder, theRequestPartitionId); diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/search/PersistedJpaSearchFirstPageBundleProvider.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/search/PersistedJpaSearchFirstPageBundleProvider.java index 7c61be7d556..6317dd37e6e 100644 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/search/PersistedJpaSearchFirstPageBundleProvider.java +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/search/PersistedJpaSearchFirstPageBundleProvider.java @@ -21,7 +21,6 @@ package ca.uhn.fhir.jpa.search; import ca.uhn.fhir.interceptor.model.RequestPartitionId; import ca.uhn.fhir.jpa.dao.ISearchBuilder; -import ca.uhn.fhir.jpa.entity.Search; import ca.uhn.fhir.jpa.entity.SearchTypeEnum; import ca.uhn.fhir.jpa.model.dao.JpaPid; import ca.uhn.fhir.jpa.model.search.SearchStatusEnum; @@ -31,6 +30,7 @@ import ca.uhn.fhir.model.api.ResourceMetadataKeyEnum; import ca.uhn.fhir.model.valueset.BundleEntrySearchModeEnum; import ca.uhn.fhir.rest.api.server.RequestDetails; import ca.uhn.fhir.rest.server.method.ResponsePage; +import jakarta.annotation.Nonnull; import org.hl7.fhir.instance.model.api.IBaseResource; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -38,7 +38,6 @@ import org.slf4j.LoggerFactory; import java.util.List; import java.util.Set; import java.util.stream.Collectors; -import javax.annotation.Nonnull; public class PersistedJpaSearchFirstPageBundleProvider extends PersistedJpaBundleProvider { private static final Logger ourLog = LoggerFactory.getLogger(PersistedJpaSearchFirstPageBundleProvider.class); @@ -52,16 +51,14 @@ public class PersistedJpaSearchFirstPageBundleProvider extends PersistedJpaBundl */ @SuppressWarnings("rawtypes") public PersistedJpaSearchFirstPageBundleProvider( - Search theSearch, SearchTask theSearchTask, ISearchBuilder theSearchBuilder, RequestDetails theRequest, RequestPartitionId theRequestPartitionId) { - super(theRequest, theSearch.getUuid()); + super(theRequest, theSearchTask.getSearch()); - assert theSearch.getSearchType() != SearchTypeEnum.HISTORY; + assert getSearchEntity().getSearchType() != SearchTypeEnum.HISTORY; - setSearchEntity(theSearch); mySearchTask = theSearchTask; mySearchBuilder = theSearchBuilder; super.setRequestPartitionId(theRequestPartitionId); @@ -76,16 +73,23 @@ public class PersistedJpaSearchFirstPageBundleProvider extends PersistedJpaBundl mySearchTask.awaitInitialSync(); + // request 1 more than we need to, in order to know if there are extra values ourLog.trace("Fetching search resource PIDs from task: {}", mySearchTask.getClass()); - final List pids = mySearchTask.getResourcePids(theFromIndex, theToIndex); + final List pids = mySearchTask.getResourcePids(theFromIndex, theToIndex + 1); ourLog.trace("Done fetching search resource PIDs"); + int countOfPids = pids.size(); + ; + int maxSize = Math.min(theToIndex - theFromIndex, countOfPids); + thePageBuilder.setTotalRequestedResourcesFetched(countOfPids); + RequestPartitionId requestPartitionId = getRequestPartitionId(); + List firstBatch = pids.subList(0, maxSize); List retVal = myTxService .withRequest(myRequest) .withRequestPartitionId(requestPartitionId) - .execute(() -> toResourceList(mySearchBuilder, pids, thePageBuilder)); + .execute(() -> toResourceList(mySearchBuilder, firstBatch, thePageBuilder)); long totalCountWanted = theToIndex - theFromIndex; long totalCountMatch = (int) retVal.stream().filter(t -> !isInclude(t)).count(); @@ -106,12 +110,15 @@ public class PersistedJpaSearchFirstPageBundleProvider extends PersistedJpaBundl long remainingWanted = totalCountWanted - totalCountMatch; long fromIndex = theToIndex - remainingWanted; - List remaining = super.getResources((int) fromIndex, theToIndex, thePageBuilder); + ResponsePage.ResponsePageBuilder pageBuilder = new ResponsePage.ResponsePageBuilder(); + pageBuilder.setBundleProvider(this); + List remaining = super.getResources((int) fromIndex, theToIndex, pageBuilder); remaining.forEach(t -> { if (!existingIds.contains(t.getIdElement().getValue())) { retVal.add(t); } }); + thePageBuilder.combineWith(pageBuilder); } } ourLog.trace("Loaded resources to return"); diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/search/ResourceSearchUrlSvc.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/search/ResourceSearchUrlSvc.java index ee380067c7e..47512f96648 100644 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/search/ResourceSearchUrlSvc.java +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/search/ResourceSearchUrlSvc.java @@ -26,13 +26,13 @@ import ca.uhn.fhir.jpa.model.dao.JpaPid; import ca.uhn.fhir.jpa.model.entity.ResourceSearchUrlEntity; import ca.uhn.fhir.jpa.searchparam.MatchUrlService; import ca.uhn.fhir.jpa.searchparam.SearchParameterMap; +import jakarta.persistence.EntityManager; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; import java.util.Date; -import javax.persistence.EntityManager; /** * This service ensures uniqueness of resources during create or create-on-update by storing the resource searchUrl. diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/search/SearchCoordinatorSvcImpl.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/search/SearchCoordinatorSvcImpl.java index 1ef70a8b469..5b5d540e0d9 100644 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/search/SearchCoordinatorSvcImpl.java +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/search/SearchCoordinatorSvcImpl.java @@ -64,6 +64,8 @@ import ca.uhn.fhir.util.AsyncUtil; import ca.uhn.fhir.util.StopWatch; import ca.uhn.fhir.util.UrlUtil; import com.google.common.annotations.VisibleForTesting; +import jakarta.annotation.Nonnull; +import jakarta.annotation.Nullable; import org.apache.commons.lang3.time.DateUtils; import org.hl7.fhir.instance.model.api.IBaseResource; import org.springframework.beans.factory.BeanFactory; @@ -84,8 +86,6 @@ import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.TimeUnit; import java.util.function.Consumer; import java.util.stream.Collectors; -import javax.annotation.Nonnull; -import javax.annotation.Nullable; import static ca.uhn.fhir.jpa.util.QueryParameterUtils.DEFAULT_SYNC_SIZE; import static org.apache.commons.lang3.StringUtils.isBlank; @@ -110,7 +110,7 @@ public class SearchCoordinatorSvcImpl implements ISearchCoordinatorSvc { private final SearchStrategyFactory mySearchStrategyFactory; private final ExceptionService myExceptionSvc; private final BeanFactory myBeanFactory; - private final ConcurrentHashMap myIdToSearchTask = new ConcurrentHashMap<>(); + private ConcurrentHashMap myIdToSearchTask = new ConcurrentHashMap<>(); private final Consumer myOnRemoveSearchTask = myIdToSearchTask::remove; @@ -162,6 +162,11 @@ public class SearchCoordinatorSvcImpl implements ISearchCoordinatorSvc { return myIdToSearchTask.keySet(); } + @VisibleForTesting + public void setIdToSearchTaskMapForUnitTests(ConcurrentHashMap theIdToSearchTaskMap) { + myIdToSearchTask = theIdToSearchTaskMap; + } + @VisibleForTesting public void setLoadingThrottleForUnitTests(Integer theLoadingThrottleForUnitTests) { myLoadingThrottleForUnitTests = theLoadingThrottleForUnitTests; @@ -571,7 +576,7 @@ public class SearchCoordinatorSvcImpl implements ISearchCoordinatorSvc { task.call(); PersistedJpaSearchFirstPageBundleProvider retVal = myPersistedJpaBundleProviderFactory.newInstanceFirstPage( - theRequestDetails, theSearch, task, theSb, theRequestPartitionId); + theRequestDetails, task, theSb, theRequestPartitionId); ourLog.debug("Search initial phase completed in {}ms", w.getMillis()); return retVal; diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/search/SearchStrategyFactory.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/search/SearchStrategyFactory.java index e6de39ec5d6..fb289ee2f8c 100644 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/search/SearchStrategyFactory.java +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/search/SearchStrategyFactory.java @@ -25,12 +25,12 @@ import ca.uhn.fhir.jpa.searchparam.SearchParameterMap; import ca.uhn.fhir.rest.api.server.IBundleProvider; import ca.uhn.fhir.rest.api.server.RequestDetails; import ca.uhn.fhir.rest.server.SimpleBundleProvider; +import jakarta.annotation.Nullable; import org.hl7.fhir.instance.model.api.IBaseResource; import java.util.Collections; import java.util.List; import java.util.function.Supplier; -import javax.annotation.Nullable; /** * Figure out how we're going to run the query up front, and build a branchless strategy object. diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/search/StaleSearchDeletingSvcImpl.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/search/StaleSearchDeletingSvcImpl.java index 700dfa9484e..b37b0be204d 100644 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/search/StaleSearchDeletingSvcImpl.java +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/search/StaleSearchDeletingSvcImpl.java @@ -25,12 +25,16 @@ import ca.uhn.fhir.jpa.model.sched.HapiJob; import ca.uhn.fhir.jpa.model.sched.IHasScheduledJobs; import ca.uhn.fhir.jpa.model.sched.ISchedulerService; import ca.uhn.fhir.jpa.model.sched.ScheduledJobDefinition; +import ca.uhn.fhir.jpa.search.cache.DatabaseSearchCacheSvcImpl; import ca.uhn.fhir.jpa.search.cache.ISearchCacheSvc; import org.quartz.JobExecutionContext; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.transaction.annotation.Propagation; import org.springframework.transaction.annotation.Transactional; +import java.time.Instant; +import java.time.temporal.ChronoUnit; + import static ca.uhn.fhir.jpa.search.cache.DatabaseSearchCacheSvcImpl.SEARCH_CLEANUP_JOB_INTERVAL_MILLIS; /** @@ -42,7 +46,6 @@ import static ca.uhn.fhir.jpa.search.cache.DatabaseSearchCacheSvcImpl.SEARCH_CLE // in Smile. // public class StaleSearchDeletingSvcImpl implements IStaleSearchDeletingSvc, IHasScheduledJobs { - private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(StaleSearchDeletingSvcImpl.class); @Autowired private JpaStorageSettings myStorageSettings; @@ -53,7 +56,16 @@ public class StaleSearchDeletingSvcImpl implements IStaleSearchDeletingSvc, IHas @Override @Transactional(propagation = Propagation.NEVER) public void pollForStaleSearchesAndDeleteThem() { - mySearchCacheSvc.pollForStaleSearchesAndDeleteThem(RequestPartitionId.allPartitions()); + mySearchCacheSvc.pollForStaleSearchesAndDeleteThem(RequestPartitionId.allPartitions(), getDeadline()); + } + + /** + * Calculate a deadline to finish before the next scheduled run. + */ + protected Instant getDeadline() { + return Instant.ofEpochMilli(DatabaseSearchCacheSvcImpl.now()) + // target a 90% duty-cycle to avoid confusing quartz + .plus((long) (SEARCH_CLEANUP_JOB_INTERVAL_MILLIS * 0.90), ChronoUnit.MILLIS); } @Override diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/search/SynchronousSearchSvcImpl.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/search/SynchronousSearchSvcImpl.java index 3627b72a5d4..8d2a9367820 100644 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/search/SynchronousSearchSvcImpl.java +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/search/SynchronousSearchSvcImpl.java @@ -47,6 +47,7 @@ import ca.uhn.fhir.rest.server.exceptions.InternalErrorException; import ca.uhn.fhir.rest.server.interceptor.ServerInterceptorUtil; import ca.uhn.fhir.rest.server.servlet.ServletRequestDetails; import ca.uhn.fhir.rest.server.util.CompositeInterceptorBroadcaster; +import jakarta.persistence.EntityManager; import org.hl7.fhir.instance.model.api.IBaseResource; import org.springframework.beans.factory.annotation.Autowired; @@ -55,7 +56,6 @@ import java.util.ArrayList; import java.util.List; import java.util.Set; import java.util.UUID; -import javax.persistence.EntityManager; import static ca.uhn.fhir.jpa.util.SearchParameterMapCalculator.isWantCount; import static ca.uhn.fhir.jpa.util.SearchParameterMapCalculator.isWantOnlyCount; @@ -115,7 +115,7 @@ public class SynchronousSearchSvcImpl implements ISynchronousSearchSvc { .execute(() -> { // Load the results synchronously - final List pids = new ArrayList<>(); + List pids = new ArrayList<>(); Long count = 0L; if (wantCount) { @@ -145,8 +145,17 @@ public class SynchronousSearchSvcImpl implements ISynchronousSearchSvc { return bundleProvider; } + // if we have a count, we'll want to request + // additional resources + SearchParameterMap clonedParams = theParams.clone(); + Integer requestedCount = clonedParams.getCount(); + boolean hasACount = requestedCount != null; + if (hasACount) { + clonedParams.setCount(requestedCount.intValue() + 1); + } + try (IResultIterator resultIter = theSb.createQuery( - theParams, searchRuntimeDetails, theRequestDetails, theRequestPartitionId)) { + clonedParams, searchRuntimeDetails, theRequestDetails, theRequestPartitionId)) { while (resultIter.hasNext()) { pids.add(resultIter.next()); if (theLoadSynchronousUpTo != null && pids.size() >= theLoadSynchronousUpTo) { @@ -162,6 +171,15 @@ public class SynchronousSearchSvcImpl implements ISynchronousSearchSvc { throw new InternalErrorException(Msg.code(1164) + e); } + // truncate the list we retrieved - if needed + int receivedResourceCount = -1; + if (hasACount) { + // we want the accurate received resource count + receivedResourceCount = pids.size(); + int resourcesToReturn = Math.min(theParams.getCount(), pids.size()); + pids = pids.subList(0, resourcesToReturn); + } + JpaPreResourceAccessDetails accessDetails = new JpaPreResourceAccessDetails(pids, () -> theSb); HookParams params = new HookParams() .add(IPreResourceAccessDetails.class, accessDetails) @@ -228,6 +246,9 @@ public class SynchronousSearchSvcImpl implements ISynchronousSearchSvc { resources, theRequestDetails, myInterceptorBroadcaster); SimpleBundleProvider bundleProvider = new SimpleBundleProvider(resources); + if (hasACount) { + bundleProvider.setTotalResourcesRequestedReturned(receivedResourceCount); + } if (theParams.isOffsetQuery()) { bundleProvider.setCurrentPageOffset(theParams.getOffset()); bundleProvider.setCurrentPageSize(theParams.getCount()); diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/search/autocomplete/RawElasticJsonBuilder.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/search/autocomplete/RawElasticJsonBuilder.java index c9c81baaa64..e4cda499c82 100644 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/search/autocomplete/RawElasticJsonBuilder.java +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/search/autocomplete/RawElasticJsonBuilder.java @@ -20,10 +20,9 @@ package ca.uhn.fhir.jpa.search.autocomplete; import com.google.gson.JsonObject; +import jakarta.annotation.Nonnull; import org.apache.commons.lang3.Validate; -import javax.annotation.Nonnull; - public class RawElasticJsonBuilder { @Nonnull static JsonObject makeMatchBoolPrefixPredicate(String theFieldName, String queryText) { diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/search/autocomplete/TokenAutocompleteAggregation.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/search/autocomplete/TokenAutocompleteAggregation.java index 574055d839d..c7e767f1508 100644 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/search/autocomplete/TokenAutocompleteAggregation.java +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/search/autocomplete/TokenAutocompleteAggregation.java @@ -28,13 +28,13 @@ import com.jayway.jsonpath.JsonPath; import com.jayway.jsonpath.ParseContext; import com.jayway.jsonpath.spi.json.GsonJsonProvider; import com.jayway.jsonpath.spi.mapper.GsonMappingProvider; +import jakarta.annotation.Nonnull; import org.apache.commons.lang3.StringUtils; import org.apache.commons.lang3.Validate; import java.util.List; import java.util.stream.Collectors; import java.util.stream.StreamSupport; -import javax.annotation.Nonnull; import static ca.uhn.fhir.jpa.model.search.HSearchIndexWriter.IDX_STRING_TEXT; import static ca.uhn.fhir.jpa.model.search.HSearchIndexWriter.NESTED_SEARCH_PARAM_ROOT; diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/search/autocomplete/TokenAutocompleteHit.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/search/autocomplete/TokenAutocompleteHit.java index 2780964ea3a..0532754e32d 100644 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/search/autocomplete/TokenAutocompleteHit.java +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/search/autocomplete/TokenAutocompleteHit.java @@ -19,11 +19,10 @@ */ package ca.uhn.fhir.jpa.search.autocomplete; +import jakarta.annotation.Nonnull; import org.apache.commons.lang3.Validate; import org.apache.commons.lang3.builder.ToStringBuilder; -import javax.annotation.Nonnull; - /** * A single autocomplete search hit. */ diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/search/autocomplete/TokenAutocompleteSearch.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/search/autocomplete/TokenAutocompleteSearch.java index dc14f226676..a9d959848a4 100644 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/search/autocomplete/TokenAutocompleteSearch.java +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/search/autocomplete/TokenAutocompleteSearch.java @@ -24,6 +24,7 @@ import ca.uhn.fhir.jpa.dao.search.ExtendedHSearchClauseBuilder; import ca.uhn.fhir.jpa.model.entity.ResourceTable; import ca.uhn.fhir.jpa.model.entity.StorageSettings; import com.google.gson.JsonObject; +import jakarta.annotation.Nonnull; import org.hibernate.search.backend.elasticsearch.ElasticsearchExtension; import org.hibernate.search.engine.search.aggregation.AggregationKey; import org.hibernate.search.engine.search.aggregation.SearchAggregation; @@ -35,7 +36,6 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; import java.util.List; -import javax.annotation.Nonnull; import static org.apache.commons.lang3.StringUtils.isNotBlank; diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/search/builder/QueryStack.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/search/builder/QueryStack.java index 16ed33dd153..cf0951ea8e9 100644 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/search/builder/QueryStack.java +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/search/builder/QueryStack.java @@ -43,7 +43,6 @@ import ca.uhn.fhir.jpa.search.builder.predicate.ComboNonUniqueSearchParameterPre import ca.uhn.fhir.jpa.search.builder.predicate.ComboUniqueSearchParameterPredicateBuilder; import ca.uhn.fhir.jpa.search.builder.predicate.CoordsPredicateBuilder; import ca.uhn.fhir.jpa.search.builder.predicate.DatePredicateBuilder; -import ca.uhn.fhir.jpa.search.builder.predicate.ForcedIdPredicateBuilder; import ca.uhn.fhir.jpa.search.builder.predicate.ICanMakeMissingParamPredicate; import ca.uhn.fhir.jpa.search.builder.predicate.NumberPredicateBuilder; import ca.uhn.fhir.jpa.search.builder.predicate.ParsedLocationParam; @@ -69,10 +68,10 @@ import ca.uhn.fhir.parser.DataFormatException; import ca.uhn.fhir.rest.api.Constants; import ca.uhn.fhir.rest.api.QualifiedParamList; import ca.uhn.fhir.rest.api.RestSearchParameterTypeEnum; -import ca.uhn.fhir.rest.api.SearchContainedModeEnum; import ca.uhn.fhir.rest.api.server.RequestDetails; import ca.uhn.fhir.rest.param.CompositeParam; import ca.uhn.fhir.rest.param.DateParam; +import ca.uhn.fhir.rest.param.DateRangeParam; import ca.uhn.fhir.rest.param.HasParam; import ca.uhn.fhir.rest.param.NumberParam; import ca.uhn.fhir.rest.param.QuantityParam; @@ -95,12 +94,12 @@ import com.healthmarketscience.sqlbuilder.ComboCondition; import com.healthmarketscience.sqlbuilder.Condition; import com.healthmarketscience.sqlbuilder.Expression; import com.healthmarketscience.sqlbuilder.InCondition; -import com.healthmarketscience.sqlbuilder.OrderObject; import com.healthmarketscience.sqlbuilder.SelectQuery; import com.healthmarketscience.sqlbuilder.SetOperationQuery; import com.healthmarketscience.sqlbuilder.Subquery; import com.healthmarketscience.sqlbuilder.UnionQuery; import com.healthmarketscience.sqlbuilder.dbspec.basic.DbColumn; +import jakarta.annotation.Nullable; import org.apache.commons.lang3.StringUtils; import org.apache.commons.lang3.tuple.Triple; import org.hl7.fhir.instance.model.api.IAnyResource; @@ -121,8 +120,8 @@ import java.util.Optional; import java.util.Set; import java.util.function.Supplier; import java.util.stream.Collectors; -import javax.annotation.Nullable; +import static ca.uhn.fhir.jpa.search.builder.QueryStack.SearchForIdsParams.with; import static ca.uhn.fhir.jpa.util.QueryParameterUtils.fromOperation; import static ca.uhn.fhir.jpa.util.QueryParameterUtils.getChainedPart; import static ca.uhn.fhir.jpa.util.QueryParameterUtils.getParamNameWithPrefix; @@ -275,16 +274,21 @@ public class QueryStack { } public void addSortOnResourceId(boolean theAscending) { + ResourceTablePredicateBuilder resourceTablePredicateBuilder; BaseJoiningPredicateBuilder firstPredicateBuilder = mySqlBuilder.getOrCreateFirstPredicateBuilder(); - ForcedIdPredicateBuilder sortPredicateBuilder = - mySqlBuilder.addForcedIdPredicateBuilder(firstPredicateBuilder.getResourceIdColumn()); - if (!theAscending) { - mySqlBuilder.addSortString( - sortPredicateBuilder.getColumnForcedId(), false, OrderObject.NullOrder.FIRST, myUseAggregate); + if (firstPredicateBuilder instanceof ResourceTablePredicateBuilder) { + resourceTablePredicateBuilder = (ResourceTablePredicateBuilder) firstPredicateBuilder; } else { - mySqlBuilder.addSortString(sortPredicateBuilder.getColumnForcedId(), true, myUseAggregate); + resourceTablePredicateBuilder = + mySqlBuilder.addResourceTablePredicateBuilder(firstPredicateBuilder.getResourceIdColumn()); } - mySqlBuilder.addSortNumeric(firstPredicateBuilder.getResourceIdColumn(), theAscending, myUseAggregate); + mySqlBuilder.addSortString(resourceTablePredicateBuilder.getColumnFhirId(), theAscending, myUseAggregate); + } + + /** Sort on RES_ID -- used to break ties for reliable sort */ + public void addSortOnResourcePID(boolean theAscending) { + BaseJoiningPredicateBuilder predicateBuilder = mySqlBuilder.getOrCreateFirstPredicateBuilder(); + mySqlBuilder.addSortString(predicateBuilder.getResourceIdColumn(), theAscending); } public void addSortOnResourceLink( @@ -1107,7 +1111,7 @@ public class QueryStack { if (paramName.startsWith("_has:")) { - ourLog.trace("Handing double _has query: {}", paramName); + ourLog.trace("Handling double _has query: {}", paramName); String qualifier = paramName.substring(4); for (IQueryParameterType next : nextOrList) { @@ -1160,26 +1164,30 @@ public class QueryStack { parameterName = parameterName.substring(0, colonIndex); } - ResourceLinkPredicateBuilder join = + ResourceLinkPredicateBuilder resourceLinkTableJoin = mySqlBuilder.addReferencePredicateBuilderReversed(this, theSourceJoinColumn); - Condition partitionPredicate = join.createPartitionIdPredicate(theRequestPartitionId); + Condition partitionPredicate = resourceLinkTableJoin.createPartitionIdPredicate(theRequestPartitionId); - List paths = join.createResourceLinkPaths(targetResourceType, paramReference, new ArrayList<>()); + List paths = resourceLinkTableJoin.createResourceLinkPaths( + targetResourceType, paramReference, new ArrayList<>()); if (CollectionUtils.isEmpty(paths)) { throw new InvalidRequestException(Msg.code(2305) + "Reference field does not exist: " + paramReference); } + Condition typePredicate = BinaryCondition.equalTo( - join.getColumnTargetResourceType(), mySqlBuilder.generatePlaceholder(theResourceType)); - Condition pathPredicate = - toEqualToOrInPredicate(join.getColumnSourcePath(), mySqlBuilder.generatePlaceholders(paths)); - Condition linkedPredicate = searchForIdsWithAndOr( - join.getColumnSrcResourceId(), - targetResourceType, - parameterName, - Collections.singletonList(orValues), - theRequest, - theRequestPartitionId, - SearchContainedModeEnum.FALSE); + resourceLinkTableJoin.getColumnTargetResourceType(), + mySqlBuilder.generatePlaceholder(theResourceType)); + Condition pathPredicate = toEqualToOrInPredicate( + resourceLinkTableJoin.getColumnSourcePath(), mySqlBuilder.generatePlaceholders(paths)); + + Condition linkedPredicate = + searchForIdsWithAndOr(with().setSourceJoinColumn(resourceLinkTableJoin.getColumnSrcResourceId()) + .setResourceName(targetResourceType) + .setParamName(parameterName) + .setAndOrParams(Collections.singletonList(orValues)) + .setRequest(theRequest) + .setRequestPartitionId(theRequestPartitionId)); + andPredicates.add(toAndPredicate(partitionPredicate, pathPredicate, typePredicate, linkedPredicate)); } @@ -2270,57 +2278,125 @@ public class QueryStack { } @Nullable - public Condition searchForIdsWithAndOr( - @Nullable DbColumn theSourceJoinColumn, - String theResourceName, - String theParamName, - List> theAndOrParams, - RequestDetails theRequest, - RequestPartitionId theRequestPartitionId, - SearchContainedModeEnum theSearchContainedMode) { + public Condition searchForIdsWithAndOr(SearchForIdsParams theSearchForIdsParams) { - if (theAndOrParams.isEmpty()) { + if (theSearchForIdsParams.myAndOrParams.isEmpty()) { return null; } - switch (theParamName) { + switch (theSearchForIdsParams.myParamName) { case IAnyResource.SP_RES_ID: return createPredicateResourceId( - theSourceJoinColumn, theAndOrParams, theResourceName, null, theRequestPartitionId); + theSearchForIdsParams.mySourceJoinColumn, + theSearchForIdsParams.myAndOrParams, + theSearchForIdsParams.myResourceName, + null, + theSearchForIdsParams.myRequestPartitionId); + + case Constants.PARAM_PID: + return createPredicateResourcePID( + theSearchForIdsParams.mySourceJoinColumn, theSearchForIdsParams.myAndOrParams); case PARAM_HAS: return createPredicateHas( - theSourceJoinColumn, theResourceName, theAndOrParams, theRequest, theRequestPartitionId); + theSearchForIdsParams.mySourceJoinColumn, + theSearchForIdsParams.myResourceName, + theSearchForIdsParams.myAndOrParams, + theSearchForIdsParams.myRequest, + theSearchForIdsParams.myRequestPartitionId); case Constants.PARAM_TAG: case Constants.PARAM_PROFILE: case Constants.PARAM_SECURITY: if (myStorageSettings.getTagStorageMode() == JpaStorageSettings.TagStorageModeEnum.INLINE) { return createPredicateSearchParameter( - theSourceJoinColumn, - theResourceName, - theParamName, - theAndOrParams, - theRequest, - theRequestPartitionId); + theSearchForIdsParams.mySourceJoinColumn, + theSearchForIdsParams.myResourceName, + theSearchForIdsParams.myParamName, + theSearchForIdsParams.myAndOrParams, + theSearchForIdsParams.myRequest, + theSearchForIdsParams.myRequestPartitionId); } else { - return createPredicateTag(theSourceJoinColumn, theAndOrParams, theParamName, theRequestPartitionId); + return createPredicateTag( + theSearchForIdsParams.mySourceJoinColumn, + theSearchForIdsParams.myAndOrParams, + theSearchForIdsParams.myParamName, + theSearchForIdsParams.myRequestPartitionId); } case Constants.PARAM_SOURCE: - return createPredicateSourceForAndList(theSourceJoinColumn, theAndOrParams); + return createPredicateSourceForAndList( + theSearchForIdsParams.mySourceJoinColumn, theSearchForIdsParams.myAndOrParams); + + case Constants.PARAM_LASTUPDATED: + // this case statement handles a _lastUpdated query as part of a reverse search + // only (/Patient?_has:Encounter:patient:_lastUpdated=ge2023-10-24). + // performing a _lastUpdated query on a resource (/Patient?_lastUpdated=eq2023-10-24) + // is handled in {@link SearchBuilder#createChunkedQuery}. + return createReverseSearchPredicateLastUpdated( + theSearchForIdsParams.myAndOrParams, theSearchForIdsParams.mySourceJoinColumn); default: return createPredicateSearchParameter( - theSourceJoinColumn, - theResourceName, - theParamName, - theAndOrParams, - theRequest, - theRequestPartitionId); + theSearchForIdsParams.mySourceJoinColumn, + theSearchForIdsParams.myResourceName, + theSearchForIdsParams.myParamName, + theSearchForIdsParams.myAndOrParams, + theSearchForIdsParams.myRequest, + theSearchForIdsParams.myRequestPartitionId); } } + /** + * Raw match on RES_ID + */ + private Condition createPredicateResourcePID( + DbColumn theSourceJoinColumn, List> theAndOrParams) { + + DbColumn pidColumn = theSourceJoinColumn; + + if (pidColumn == null) { + BaseJoiningPredicateBuilder predicateBuilder = mySqlBuilder.getOrCreateFirstPredicateBuilder(); + pidColumn = predicateBuilder.getResourceIdColumn(); + } + + // we don't support any modifiers for now + Set pids = theAndOrParams.stream() + .map(orList -> orList.stream() + .map(v -> v.getValueAsQueryToken(myFhirContext)) + .map(Long::valueOf) + .collect(Collectors.toSet())) + .reduce(Sets::intersection) + .orElse(Set.of()); + + if (pids.isEmpty()) { + mySqlBuilder.setMatchNothing(); + return null; + } + + return toEqualToOrInPredicate(pidColumn, mySqlBuilder.generatePlaceholders(pids)); + } + + private Condition createReverseSearchPredicateLastUpdated( + List> theAndOrParams, DbColumn theSourceColumn) { + + ResourceTablePredicateBuilder resourceTableJoin = + mySqlBuilder.addResourceTablePredicateBuilder(theSourceColumn); + + List andPredicates = new ArrayList<>(theAndOrParams.size()); + + for (List aList : theAndOrParams) { + if (!aList.isEmpty()) { + DateParam dateParam = (DateParam) aList.get(0); + DateRangeParam dateRangeParam = new DateRangeParam(dateParam); + Condition aCondition = mySqlBuilder.addPredicateLastUpdated(dateRangeParam, resourceTableJoin); + andPredicates.add(aCondition); + } + } + + return toAndPredicate(andPredicates); + } + @Nullable private Condition createPredicateSearchParameter( @Nullable DbColumn theSourceJoinColumn, @@ -3020,4 +3096,82 @@ public class QueryStack { theParamDefinition, myOrValues, myLeafTarget, myLeafParamName, myLeafPathPrefix, myQualifiers); } } + + public static class SearchForIdsParams { + DbColumn mySourceJoinColumn; + String myResourceName; + String myParamName; + List> myAndOrParams; + RequestDetails myRequest; + RequestPartitionId myRequestPartitionId; + ResourceTablePredicateBuilder myResourceTablePredicateBuilder; + + public static SearchForIdsParams with() { + return new SearchForIdsParams(); + } + + public DbColumn getSourceJoinColumn() { + return mySourceJoinColumn; + } + + public SearchForIdsParams setSourceJoinColumn(DbColumn theSourceJoinColumn) { + mySourceJoinColumn = theSourceJoinColumn; + return this; + } + + public String getResourceName() { + return myResourceName; + } + + public SearchForIdsParams setResourceName(String theResourceName) { + myResourceName = theResourceName; + return this; + } + + public String getParamName() { + return myParamName; + } + + public SearchForIdsParams setParamName(String theParamName) { + myParamName = theParamName; + return this; + } + + public List> getAndOrParams() { + return myAndOrParams; + } + + public SearchForIdsParams setAndOrParams(List> theAndOrParams) { + myAndOrParams = theAndOrParams; + return this; + } + + public RequestDetails getRequest() { + return myRequest; + } + + public SearchForIdsParams setRequest(RequestDetails theRequest) { + myRequest = theRequest; + return this; + } + + public RequestPartitionId getRequestPartitionId() { + return myRequestPartitionId; + } + + public SearchForIdsParams setRequestPartitionId(RequestPartitionId theRequestPartitionId) { + myRequestPartitionId = theRequestPartitionId; + return this; + } + + public ResourceTablePredicateBuilder getResourceTablePredicateBuilder() { + return myResourceTablePredicateBuilder; + } + + public SearchForIdsParams setResourceTablePredicateBuilder( + ResourceTablePredicateBuilder theResourceTablePredicateBuilder) { + myResourceTablePredicateBuilder = theResourceTablePredicateBuilder; + return this; + } + } } diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/search/builder/SearchBuilder.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/search/builder/SearchBuilder.java index 3c7f7613ceb..238f46c43d3 100644 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/search/builder/SearchBuilder.java +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/search/builder/SearchBuilder.java @@ -95,6 +95,15 @@ import ca.uhn.fhir.util.StringUtil; import ca.uhn.fhir.util.UrlUtil; import com.google.common.collect.Streams; import com.healthmarketscience.sqlbuilder.Condition; +import jakarta.annotation.Nonnull; +import jakarta.annotation.Nullable; +import jakarta.persistence.EntityManager; +import jakarta.persistence.PersistenceContext; +import jakarta.persistence.PersistenceContextType; +import jakarta.persistence.Query; +import jakarta.persistence.Tuple; +import jakarta.persistence.TypedQuery; +import jakarta.persistence.criteria.CriteriaBuilder; import org.apache.commons.lang3.StringUtils; import org.apache.commons.lang3.Validate; import org.apache.commons.lang3.math.NumberUtils; @@ -119,18 +128,10 @@ import java.util.Map; import java.util.Objects; import java.util.Set; import java.util.stream.Collectors; -import javax.annotation.Nonnull; -import javax.annotation.Nullable; -import javax.persistence.EntityManager; -import javax.persistence.PersistenceContext; -import javax.persistence.PersistenceContextType; -import javax.persistence.Query; -import javax.persistence.Tuple; -import javax.persistence.TypedQuery; -import javax.persistence.criteria.CriteriaBuilder; import static ca.uhn.fhir.jpa.model.util.JpaConstants.UNDESIRED_RESOURCE_LINKAGES_FOR_EVERYTHING_ON_PATIENT_INSTANCE; import static ca.uhn.fhir.jpa.search.builder.QueryStack.LOCATION_POSITION; +import static ca.uhn.fhir.jpa.search.builder.QueryStack.SearchForIdsParams.with; import static org.apache.commons.lang3.StringUtils.defaultString; import static org.apache.commons.lang3.StringUtils.isBlank; import static org.apache.commons.lang3.StringUtils.isNotBlank; @@ -281,14 +282,11 @@ public class SearchBuilder implements ISearchBuilder { continue; } List> andOrParams = myParams.get(nextParamName); - Condition predicate = theQueryStack.searchForIdsWithAndOr( - null, - myResourceName, - nextParamName, - andOrParams, - theRequest, - myRequestPartitionId, - searchContainedMode); + Condition predicate = theQueryStack.searchForIdsWithAndOr(with().setResourceName(myResourceName) + .setParamName(nextParamName) + .setAndOrParams(andOrParams) + .setRequest(theRequest) + .setRequestPartitionId(myRequestPartitionId)); if (predicate != null) { theSearchSqlBuilder.addPredicate(predicate); } @@ -341,7 +339,7 @@ public class SearchBuilder implements ISearchBuilder { @SuppressWarnings("ConstantConditions") @Override - public IResultIterator createQuery( + public IResultIterator createQuery( SearchParameterMap theParams, SearchRuntimeDetails theSearchRuntimeDetails, RequestDetails theRequest, @@ -497,15 +495,8 @@ public class SearchBuilder implements ISearchBuilder { myRequestPartitionId, myResourceName, String.valueOf(lastNResourceId))) .collect(Collectors.toList()); } else { - if (myIElasticsearchSvc == null) { - throw new InvalidRequestException(Msg.code(2033) - + "LastN operation is not enabled on this service, can not process this request"); - } - // use the dedicated observation ES/Lucene index to support lastN query - return myIElasticsearchSvc.executeLastN(myParams, myContext, theMaximumResults).stream() - .map(lastnResourceId -> myIdHelperService.resolveResourcePersistentIds( - myRequestPartitionId, myResourceName, lastnResourceId)) - .collect(Collectors.toList()); + throw new InvalidRequestException( + Msg.code(2033) + "LastN operation is not enabled on this service, can not process this request"); } } @@ -840,6 +831,10 @@ public class SearchBuilder implements ISearchBuilder { theQueryStack.addSortOnResourceId(ascending); + } else if (Constants.PARAM_PID.equals(theSort.getParamName())) { + + theQueryStack.addSortOnResourcePID(ascending); + } else if (Constants.PARAM_LASTUPDATED.equals(theSort.getParamName())) { theQueryStack.addSortOnLastUpdated(ascending); @@ -1400,7 +1395,7 @@ public class SearchBuilder implements ISearchBuilder { q.setMaxResults(maxCount); } if (hasDesiredResourceTypes) { - q.setParameter("desired_target_resource_types", String.join(", ", desiredResourceTypes)); + q.setParameter("desired_target_resource_types", desiredResourceTypes); } List results = q.getResultList(); for (Object nextRow : results) { diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/search/builder/SearchQueryExecutors.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/search/builder/SearchQueryExecutors.java index e1a6ddacf25..f183d13b528 100644 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/search/builder/SearchQueryExecutors.java +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/search/builder/SearchQueryExecutors.java @@ -21,11 +21,11 @@ package ca.uhn.fhir.jpa.search.builder; import ca.uhn.fhir.jpa.model.dao.JpaPid; import ca.uhn.fhir.jpa.search.builder.models.ResolvedSearchQueryExecutor; +import jakarta.annotation.Nonnull; import org.apache.commons.lang3.Validate; import java.util.Iterator; import java.util.List; -import javax.annotation.Nonnull; public class SearchQueryExecutors { diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/search/builder/models/ResolvedSearchQueryExecutor.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/search/builder/models/ResolvedSearchQueryExecutor.java index 62a19e3b89b..b78688ae91e 100644 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/search/builder/models/ResolvedSearchQueryExecutor.java +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/search/builder/models/ResolvedSearchQueryExecutor.java @@ -20,10 +20,10 @@ package ca.uhn.fhir.jpa.search.builder.models; import ca.uhn.fhir.jpa.search.builder.ISearchQueryExecutor; +import jakarta.annotation.Nonnull; import java.util.Iterator; import java.util.List; -import javax.annotation.Nonnull; public class ResolvedSearchQueryExecutor implements ISearchQueryExecutor { private final Iterator myIterator; diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/search/builder/predicate/BaseJoiningPredicateBuilder.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/search/builder/predicate/BaseJoiningPredicateBuilder.java index 0c34500bba3..482be7bac69 100644 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/search/builder/predicate/BaseJoiningPredicateBuilder.java +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/search/builder/predicate/BaseJoiningPredicateBuilder.java @@ -28,11 +28,11 @@ import com.healthmarketscience.sqlbuilder.NotCondition; import com.healthmarketscience.sqlbuilder.UnaryCondition; import com.healthmarketscience.sqlbuilder.dbspec.basic.DbColumn; import com.healthmarketscience.sqlbuilder.dbspec.basic.DbTable; +import jakarta.annotation.Nullable; import org.apache.commons.lang3.Validate; import java.util.List; import java.util.stream.Collectors; -import javax.annotation.Nullable; public abstract class BaseJoiningPredicateBuilder extends BasePredicateBuilder { diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/search/builder/predicate/BasePredicateBuilder.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/search/builder/predicate/BasePredicateBuilder.java index e168c357031..6673a43a2dc 100644 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/search/builder/predicate/BasePredicateBuilder.java +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/search/builder/predicate/BasePredicateBuilder.java @@ -28,10 +28,10 @@ import ca.uhn.fhir.rest.param.ParamPrefixEnum; import com.healthmarketscience.sqlbuilder.BinaryCondition; import com.healthmarketscience.sqlbuilder.dbspec.basic.DbColumn; import com.healthmarketscience.sqlbuilder.dbspec.basic.DbTable; +import jakarta.annotation.Nonnull; import java.util.Collection; import java.util.List; -import javax.annotation.Nonnull; public abstract class BasePredicateBuilder { diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/search/builder/predicate/BaseQuantityPredicateBuilder.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/search/builder/predicate/BaseQuantityPredicateBuilder.java index 78b7f115ab6..4f427431418 100644 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/search/builder/predicate/BaseQuantityPredicateBuilder.java +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/search/builder/predicate/BaseQuantityPredicateBuilder.java @@ -33,10 +33,10 @@ import com.healthmarketscience.sqlbuilder.ComboCondition; import com.healthmarketscience.sqlbuilder.Condition; import com.healthmarketscience.sqlbuilder.dbspec.basic.DbColumn; import com.healthmarketscience.sqlbuilder.dbspec.basic.DbTable; +import jakarta.persistence.criteria.CriteriaBuilder; import org.springframework.beans.factory.annotation.Autowired; import java.math.BigDecimal; -import javax.persistence.criteria.CriteriaBuilder; import static org.apache.commons.lang3.ObjectUtils.defaultIfNull; import static org.apache.commons.lang3.StringUtils.isBlank; diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/search/builder/predicate/BaseSearchParamPredicateBuilder.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/search/builder/predicate/BaseSearchParamPredicateBuilder.java index 74427f68a91..dcc0ad9bc46 100644 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/search/builder/predicate/BaseSearchParamPredicateBuilder.java +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/search/builder/predicate/BaseSearchParamPredicateBuilder.java @@ -32,10 +32,10 @@ import com.healthmarketscience.sqlbuilder.SelectQuery; import com.healthmarketscience.sqlbuilder.UnaryCondition; import com.healthmarketscience.sqlbuilder.dbspec.basic.DbColumn; import com.healthmarketscience.sqlbuilder.dbspec.basic.DbTable; +import jakarta.annotation.Nonnull; import java.util.ArrayList; import java.util.List; -import javax.annotation.Nonnull; public abstract class BaseSearchParamPredicateBuilder extends BaseJoiningPredicateBuilder implements ICanMakeMissingParamPredicate { diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/search/builder/predicate/ForcedIdPredicateBuilder.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/search/builder/predicate/ForcedIdPredicateBuilder.java deleted file mode 100644 index 6f8d1e1b66c..00000000000 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/search/builder/predicate/ForcedIdPredicateBuilder.java +++ /dev/null @@ -1,51 +0,0 @@ -/*- - * #%L - * HAPI FHIR JPA Server - * %% - * Copyright (C) 2014 - 2023 Smile CDR, Inc. - * %% - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * #L% - */ -package ca.uhn.fhir.jpa.search.builder.predicate; - -import ca.uhn.fhir.jpa.search.builder.sql.SearchQueryBuilder; -import com.healthmarketscience.sqlbuilder.dbspec.basic.DbColumn; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -public class ForcedIdPredicateBuilder extends BaseJoiningPredicateBuilder { - - private static final Logger ourLog = LoggerFactory.getLogger(ForcedIdPredicateBuilder.class); - private final DbColumn myColumnResourceId; - private final DbColumn myColumnForcedId; - - /** - * Constructor - */ - public ForcedIdPredicateBuilder(SearchQueryBuilder theSearchSqlBuilder) { - super(theSearchSqlBuilder, theSearchSqlBuilder.addTable("HFJ_FORCED_ID")); - - myColumnResourceId = getTable().addColumn("RESOURCE_PID"); - myColumnForcedId = getTable().addColumn("FORCED_ID"); - } - - @Override - public DbColumn getResourceIdColumn() { - return myColumnResourceId; - } - - public DbColumn getColumnForcedId() { - return myColumnForcedId; - } -} diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/search/builder/predicate/ResourceIdPredicateBuilder.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/search/builder/predicate/ResourceIdPredicateBuilder.java index 6aa5376a644..4d1c7592938 100644 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/search/builder/predicate/ResourceIdPredicateBuilder.java +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/search/builder/predicate/ResourceIdPredicateBuilder.java @@ -31,6 +31,7 @@ import ca.uhn.fhir.rest.param.TokenParamModifier; import ca.uhn.fhir.rest.server.exceptions.ResourceNotFoundException; import com.healthmarketscience.sqlbuilder.Condition; import com.healthmarketscience.sqlbuilder.dbspec.basic.DbColumn; +import jakarta.annotation.Nullable; import org.hl7.fhir.r4.model.IdType; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -39,7 +40,6 @@ import org.springframework.beans.factory.annotation.Autowired; import java.util.HashSet; import java.util.List; import java.util.Set; -import javax.annotation.Nullable; import static org.apache.commons.lang3.ObjectUtils.defaultIfNull; import static org.apache.commons.lang3.StringUtils.isNotBlank; diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/search/builder/predicate/ResourceLinkPredicateBuilder.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/search/builder/predicate/ResourceLinkPredicateBuilder.java index 5952f398c56..0dfa2ae005c 100644 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/search/builder/predicate/ResourceLinkPredicateBuilder.java +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/search/builder/predicate/ResourceLinkPredicateBuilder.java @@ -51,7 +51,6 @@ import ca.uhn.fhir.model.primitive.IdDt; import ca.uhn.fhir.parser.DataFormatException; import ca.uhn.fhir.rest.api.Constants; import ca.uhn.fhir.rest.api.RestSearchParameterTypeEnum; -import ca.uhn.fhir.rest.api.SearchContainedModeEnum; import ca.uhn.fhir.rest.api.server.RequestDetails; import ca.uhn.fhir.rest.param.ReferenceParam; import ca.uhn.fhir.rest.param.TokenParam; @@ -69,6 +68,8 @@ import com.healthmarketscience.sqlbuilder.NotCondition; import com.healthmarketscience.sqlbuilder.SelectQuery; import com.healthmarketscience.sqlbuilder.UnaryCondition; import com.healthmarketscience.sqlbuilder.dbspec.basic.DbColumn; +import jakarta.annotation.Nonnull; +import jakarta.annotation.Nullable; import org.hl7.fhir.instance.model.api.IBaseResource; import org.hl7.fhir.instance.model.api.IIdType; import org.slf4j.Logger; @@ -84,9 +85,8 @@ import java.util.List; import java.util.ListIterator; import java.util.Set; import java.util.stream.Collectors; -import javax.annotation.Nonnull; -import javax.annotation.Nullable; +import static ca.uhn.fhir.jpa.search.builder.QueryStack.SearchForIdsParams.with; import static org.apache.commons.lang3.StringUtils.isBlank; import static org.apache.commons.lang3.StringUtils.trim; @@ -456,14 +456,13 @@ public class ResourceLinkPredicateBuilder extends BaseJoiningPredicateBuilder im List andPredicates = new ArrayList<>(); List> chainParamValues = Collections.singletonList(orValues); - andPredicates.add(childQueryFactory.searchForIdsWithAndOr( - myColumnTargetResourceId, - subResourceName, - chain, - chainParamValues, - theRequest, - theRequestPartitionId, - SearchContainedModeEnum.FALSE)); + andPredicates.add( + childQueryFactory.searchForIdsWithAndOr(with().setSourceJoinColumn(myColumnTargetResourceId) + .setResourceName(subResourceName) + .setParamName(chain) + .setAndOrParams(chainParamValues) + .setRequest(theRequest) + .setRequestPartitionId(theRequestPartitionId))); orPredicates.add(QueryParameterUtils.toAndPredicate(andPredicates)); } diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/search/builder/predicate/ResourceTablePredicateBuilder.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/search/builder/predicate/ResourceTablePredicateBuilder.java index c9fb8845dd7..75ccddb1170 100644 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/search/builder/predicate/ResourceTablePredicateBuilder.java +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/search/builder/predicate/ResourceTablePredicateBuilder.java @@ -19,6 +19,7 @@ */ package ca.uhn.fhir.jpa.search.builder.predicate; +import ca.uhn.fhir.jpa.model.entity.ResourceTable; import ca.uhn.fhir.jpa.search.builder.sql.SearchQueryBuilder; import ca.uhn.fhir.jpa.util.QueryParameterUtils; import com.healthmarketscience.sqlbuilder.BinaryCondition; @@ -35,6 +36,7 @@ public class ResourceTablePredicateBuilder extends BaseJoiningPredicateBuilder { private final DbColumn myColumnResType; private final DbColumn myColumnLastUpdated; private final DbColumn myColumnLanguage; + private final DbColumn myColumnFhirId; /** * Constructor @@ -42,10 +44,11 @@ public class ResourceTablePredicateBuilder extends BaseJoiningPredicateBuilder { public ResourceTablePredicateBuilder(SearchQueryBuilder theSearchSqlBuilder) { super(theSearchSqlBuilder, theSearchSqlBuilder.addTable("HFJ_RESOURCE")); myColumnResId = getTable().addColumn("RES_ID"); - myColumnResType = getTable().addColumn("RES_TYPE"); + myColumnResType = getTable().addColumn(ResourceTable.RES_TYPE); myColumnResDeletedAt = getTable().addColumn("RES_DELETED_AT"); myColumnLastUpdated = getTable().addColumn("RES_UPDATED"); myColumnLanguage = getTable().addColumn("RES_LANGUAGE"); + myColumnFhirId = getTable().addColumn(ResourceTable.FHIR_ID); } @Override @@ -77,4 +80,8 @@ public class ResourceTablePredicateBuilder extends BaseJoiningPredicateBuilder { public DbColumn getColumnLastUpdated() { return myColumnLastUpdated; } + + public DbColumn getColumnFhirId() { + return myColumnFhirId; + } } diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/search/builder/predicate/StringPredicateBuilder.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/search/builder/predicate/StringPredicateBuilder.java index 2663e127fde..cef3b986184 100644 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/search/builder/predicate/StringPredicateBuilder.java +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/search/builder/predicate/StringPredicateBuilder.java @@ -37,10 +37,9 @@ import com.healthmarketscience.sqlbuilder.BinaryCondition; import com.healthmarketscience.sqlbuilder.ComboCondition; import com.healthmarketscience.sqlbuilder.Condition; import com.healthmarketscience.sqlbuilder.dbspec.basic.DbColumn; +import jakarta.annotation.Nonnull; import org.springframework.beans.factory.annotation.Autowired; -import javax.annotation.Nonnull; - public class StringPredicateBuilder extends BaseSearchParamPredicateBuilder { private final DbColumn myColumnResId; diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/search/builder/sql/SearchQueryBuilder.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/search/builder/sql/SearchQueryBuilder.java index cb1d3a74bd4..252034b4949 100644 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/search/builder/sql/SearchQueryBuilder.java +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/search/builder/sql/SearchQueryBuilder.java @@ -32,7 +32,6 @@ import ca.uhn.fhir.jpa.search.builder.predicate.ComboNonUniqueSearchParameterPre import ca.uhn.fhir.jpa.search.builder.predicate.ComboUniqueSearchParameterPredicateBuilder; import ca.uhn.fhir.jpa.search.builder.predicate.CoordsPredicateBuilder; import ca.uhn.fhir.jpa.search.builder.predicate.DatePredicateBuilder; -import ca.uhn.fhir.jpa.search.builder.predicate.ForcedIdPredicateBuilder; import ca.uhn.fhir.jpa.search.builder.predicate.NumberPredicateBuilder; import ca.uhn.fhir.jpa.search.builder.predicate.QuantityNormalizedPredicateBuilder; import ca.uhn.fhir.jpa.search.builder.predicate.QuantityPredicateBuilder; @@ -62,11 +61,14 @@ import com.healthmarketscience.sqlbuilder.dbspec.basic.DbJoin; import com.healthmarketscience.sqlbuilder.dbspec.basic.DbSchema; import com.healthmarketscience.sqlbuilder.dbspec.basic.DbSpec; import com.healthmarketscience.sqlbuilder.dbspec.basic.DbTable; -import org.apache.commons.lang3.Validate; +import jakarta.annotation.Nonnull; +import jakarta.annotation.Nullable; import org.hibernate.dialect.Dialect; import org.hibernate.dialect.SQLServerDialect; import org.hibernate.dialect.pagination.AbstractLimitHandler; -import org.hibernate.engine.spi.RowSelection; +import org.hibernate.query.internal.QueryOptionsImpl; +import org.hibernate.query.spi.Limit; +import org.hibernate.query.spi.QueryOptions; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -76,8 +78,6 @@ import java.util.List; import java.util.Set; import java.util.UUID; import java.util.stream.Collectors; -import javax.annotation.Nonnull; -import javax.annotation.Nullable; import static ca.uhn.fhir.rest.param.ParamPrefixEnum.GREATERTHAN; import static ca.uhn.fhir.rest.param.ParamPrefixEnum.GREATERTHAN_OR_EQUALS; @@ -222,18 +222,6 @@ public class SearchQueryBuilder { return mySqlBuilderFactory.dateIndexTable(this); } - /** - * Add and return a predicate builder for selecting a forced ID. This is only intended for use with sorts so it can not - * be the root query. - */ - public ForcedIdPredicateBuilder addForcedIdPredicateBuilder(@Nonnull DbColumn theSourceJoinColumn) { - Validate.isTrue(theSourceJoinColumn != null); - - ForcedIdPredicateBuilder retVal = mySqlBuilderFactory.newForcedIdPredicateBuilder(this); - addTableForSorting(retVal, theSourceJoinColumn); - return retVal; - } - /** * Create, add and return a predicate builder (or a root query if no root query exists yet) for selecting on a NUMBER search parameter */ @@ -417,11 +405,6 @@ public class SearchQueryBuilder { addTable(thePredicateBuilder, theSourceJoinColumn, SelectQuery.JoinType.INNER); } - private void addTableForSorting( - BaseJoiningPredicateBuilder thePredicateBuilder, @Nullable DbColumn theSourceJoinColumn) { - addTable(thePredicateBuilder, theSourceJoinColumn, SelectQuery.JoinType.LEFT_OUTER); - } - private void addTable( BaseJoiningPredicateBuilder thePredicateBuilder, @Nullable DbColumn theSourceJoinColumn, @@ -520,10 +503,11 @@ public class SearchQueryBuilder { maxResultsToFetch = defaultIfNull(maxResultsToFetch, 10000); AbstractLimitHandler limitHandler = (AbstractLimitHandler) myDialect.getLimitHandler(); - RowSelection selection = new RowSelection(); + Limit selection = new Limit(); selection.setFirstRow(offset); selection.setMaxRows(maxResultsToFetch); - sql = limitHandler.processSql(sql, selection); + QueryOptions queryOptions = new QueryOptionsImpl(); + sql = limitHandler.processSql(sql, selection, queryOptions); int startOfQueryParameterIndex = 0; @@ -536,14 +520,14 @@ public class SearchQueryBuilder { if (sql.contains("top(?)")) { bindVariables.add(0, maxResultsToFetch); } - if (sql.contains("offset 0 rows fetch next ? rows only")) { + if (sql.contains("offset 0 rows fetch first ? rows only")) { bindVariables.add(maxResultsToFetch); } if (sql.contains("offset ? rows fetch next ? rows only")) { bindVariables.add(theOffset); bindVariables.add(maxResultsToFetch); } - if (offset != null && sql.contains("__row__")) { + if (offset != null && sql.contains("rownumber_")) { bindVariables.add(theOffset + 1); bindVariables.add(theOffset + maxResultsToFetch + 1); } @@ -699,15 +683,24 @@ public class SearchQueryBuilder { public ComboCondition addPredicateLastUpdated(DateRangeParam theDateRange) { ResourceTablePredicateBuilder resourceTableRoot = getOrCreateResourceTablePredicateBuilder(false); + return addPredicateLastUpdated(theDateRange, resourceTableRoot); + } + + public ComboCondition addPredicateLastUpdated( + DateRangeParam theDateRange, ResourceTablePredicateBuilder theResourceTablePredicateBuilder) { List conditions = new ArrayList<>(2); BinaryCondition condition; if (isNotEqualsComparator(theDateRange)) { condition = createConditionForValueWithComparator( - LESSTHAN, resourceTableRoot.getLastUpdatedColumn(), theDateRange.getLowerBoundAsInstant()); + LESSTHAN, + theResourceTablePredicateBuilder.getLastUpdatedColumn(), + theDateRange.getLowerBoundAsInstant()); conditions.add(condition); condition = createConditionForValueWithComparator( - GREATERTHAN, resourceTableRoot.getLastUpdatedColumn(), theDateRange.getUpperBoundAsInstant()); + GREATERTHAN, + theResourceTablePredicateBuilder.getLastUpdatedColumn(), + theDateRange.getUpperBoundAsInstant()); conditions.add(condition); return ComboCondition.or(conditions.toArray(new Condition[0])); } @@ -715,7 +708,7 @@ public class SearchQueryBuilder { if (theDateRange.getLowerBoundAsInstant() != null) { condition = createConditionForValueWithComparator( GREATERTHAN_OR_EQUALS, - resourceTableRoot.getLastUpdatedColumn(), + theResourceTablePredicateBuilder.getLastUpdatedColumn(), theDateRange.getLowerBoundAsInstant()); conditions.add(condition); } @@ -723,7 +716,7 @@ public class SearchQueryBuilder { if (theDateRange.getUpperBoundAsInstant() != null) { condition = createConditionForValueWithComparator( LESSTHAN_OR_EQUALS, - resourceTableRoot.getLastUpdatedColumn(), + theResourceTablePredicateBuilder.getLastUpdatedColumn(), theDateRange.getUpperBoundAsInstant()); conditions.add(condition); } @@ -757,7 +750,7 @@ public class SearchQueryBuilder { List excludePids = JpaPid.toLongList(theExistingPidSetToExclude); - ourLog.trace("excludePids = " + excludePids); + ourLog.trace("excludePids = {}", excludePids); DbColumn resourceIdColumn = getOrCreateFirstPredicateBuilder().getResourceIdColumn(); InCondition predicate = new InCondition(resourceIdColumn, generatePlaceholders(excludePids)); diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/search/builder/sql/SearchQueryExecutor.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/search/builder/sql/SearchQueryExecutor.java index 8f8d4600ee5..d1edddeda4f 100644 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/search/builder/sql/SearchQueryExecutor.java +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/search/builder/sql/SearchQueryExecutor.java @@ -25,6 +25,11 @@ import ca.uhn.fhir.jpa.search.builder.ISearchQueryExecutor; import ca.uhn.fhir.jpa.util.ScrollableResultsIterator; import ca.uhn.fhir.rest.server.exceptions.InternalErrorException; import ca.uhn.fhir.util.IoUtil; +import jakarta.persistence.EntityManager; +import jakarta.persistence.FlushModeType; +import jakarta.persistence.PersistenceContext; +import jakarta.persistence.PersistenceContextType; +import jakarta.persistence.Query; import org.apache.commons.lang3.Validate; import org.hibernate.CacheMode; import org.hibernate.ScrollMode; @@ -33,11 +38,7 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; import java.util.Arrays; -import javax.persistence.EntityManager; -import javax.persistence.FlushModeType; -import javax.persistence.PersistenceContext; -import javax.persistence.PersistenceContextType; -import javax.persistence.Query; +import java.util.Objects; public class SearchQueryExecutor implements ISearchQueryExecutor { @@ -51,7 +52,7 @@ public class SearchQueryExecutor implements ISearchQueryExecutor { private EntityManager myEntityManager; private boolean myQueryInitialized; - private ScrollableResultsIterator myResultSet; + private ScrollableResultsIterator myResultSet; private Long myNext; /** @@ -144,7 +145,13 @@ public class SearchQueryExecutor implements ISearchQueryExecutor { if (myResultSet == null || !myResultSet.hasNext()) { myNext = NO_MORE; } else { - Number next = myResultSet.next(); + Object nextRow = Objects.requireNonNull(myResultSet.next()); + Number next; + if (nextRow instanceof Number) { + next = (Number) nextRow; + } else { + next = (Number) ((Object[]) nextRow)[0]; + } myNext = next.longValue(); } diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/search/builder/sql/SqlObjectFactory.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/search/builder/sql/SqlObjectFactory.java index 29e04527f96..621fe211185 100644 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/search/builder/sql/SqlObjectFactory.java +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/search/builder/sql/SqlObjectFactory.java @@ -24,7 +24,6 @@ import ca.uhn.fhir.jpa.search.builder.predicate.ComboNonUniqueSearchParameterPre import ca.uhn.fhir.jpa.search.builder.predicate.ComboUniqueSearchParameterPredicateBuilder; import ca.uhn.fhir.jpa.search.builder.predicate.CoordsPredicateBuilder; import ca.uhn.fhir.jpa.search.builder.predicate.DatePredicateBuilder; -import ca.uhn.fhir.jpa.search.builder.predicate.ForcedIdPredicateBuilder; import ca.uhn.fhir.jpa.search.builder.predicate.NumberPredicateBuilder; import ca.uhn.fhir.jpa.search.builder.predicate.QuantityNormalizedPredicateBuilder; import ca.uhn.fhir.jpa.search.builder.predicate.QuantityPredicateBuilder; @@ -63,10 +62,6 @@ public class SqlObjectFactory { return myApplicationContext.getBean(DatePredicateBuilder.class, theSearchSqlBuilder); } - public ForcedIdPredicateBuilder newForcedIdPredicateBuilder(SearchQueryBuilder theSearchSqlBuilder) { - return myApplicationContext.getBean(ForcedIdPredicateBuilder.class, theSearchSqlBuilder); - } - public NumberPredicateBuilder numberIndexTable(SearchQueryBuilder theSearchSqlBuilder) { return myApplicationContext.getBean(NumberPredicateBuilder.class, theSearchSqlBuilder); } diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/search/builder/tasks/SearchTask.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/search/builder/tasks/SearchTask.java index 9396f7d9542..fd39eb0b488 100644 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/search/builder/tasks/SearchTask.java +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/search/builder/tasks/SearchTask.java @@ -54,10 +54,10 @@ import ca.uhn.fhir.util.StopWatch; import co.elastic.apm.api.ElasticApm; import co.elastic.apm.api.Span; import co.elastic.apm.api.Transaction; +import jakarta.annotation.Nonnull; import org.apache.commons.lang3.Validate; import org.apache.commons.lang3.exception.ExceptionUtils; import org.hl7.fhir.instance.model.api.IBaseResource; -import org.springframework.transaction.annotation.Isolation; import org.springframework.transaction.annotation.Propagation; import java.io.IOException; @@ -68,7 +68,6 @@ import java.util.concurrent.Callable; import java.util.concurrent.CountDownLatch; import java.util.concurrent.TimeUnit; import java.util.function.Consumer; -import javax.annotation.Nonnull; import static ca.uhn.fhir.jpa.util.SearchParameterMapCalculator.isWantCount; import static ca.uhn.fhir.jpa.util.SearchParameterMapCalculator.isWantOnlyCount; @@ -447,7 +446,6 @@ public class SearchTask implements Callable { myTxService .withRequest(myRequest) .withRequestPartitionId(myRequestPartitionId) - .withIsolation(Isolation.READ_COMMITTED) .execute(() -> doSearch()); mySearchRuntimeDetails.setSearchStatus(mySearch.getStatus()); diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/search/cache/DatabaseSearchCacheSvcImpl.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/search/cache/DatabaseSearchCacheSvcImpl.java index 7bf60372f0a..2acd744ea0a 100644 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/search/cache/DatabaseSearchCacheSvcImpl.java +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/search/cache/DatabaseSearchCacheSvcImpl.java @@ -25,29 +25,35 @@ import ca.uhn.fhir.jpa.api.config.JpaStorageSettings; import ca.uhn.fhir.jpa.dao.data.ISearchDao; import ca.uhn.fhir.jpa.dao.data.ISearchIncludeDao; import ca.uhn.fhir.jpa.dao.data.ISearchResultDao; +import ca.uhn.fhir.jpa.dao.data.SearchIdAndResultSize; import ca.uhn.fhir.jpa.dao.tx.HapiTransactionService; import ca.uhn.fhir.jpa.dao.tx.IHapiTransactionService; +import ca.uhn.fhir.jpa.dao.tx.IHapiTransactionService.IExecutionBuilder; import ca.uhn.fhir.jpa.entity.Search; import ca.uhn.fhir.jpa.model.search.SearchStatusEnum; import ca.uhn.fhir.system.HapiSystemProperties; import com.google.common.annotations.VisibleForTesting; -import com.google.common.collect.Lists; +import jakarta.annotation.Nonnull; +import jakarta.persistence.EntityManager; import org.apache.commons.lang3.Validate; import org.apache.commons.lang3.time.DateUtils; +import org.hibernate.Session; import org.hl7.fhir.dstu3.model.InstantType; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.data.domain.PageRequest; -import org.springframework.data.domain.Slice; import org.springframework.transaction.annotation.Propagation; import org.springframework.transaction.annotation.Transactional; +import java.sql.Connection; import java.time.Instant; import java.util.Collection; import java.util.Date; -import java.util.List; +import java.util.HashSet; import java.util.Optional; +import java.util.Set; +import java.util.concurrent.atomic.AtomicInteger; +import java.util.stream.Stream; public class DatabaseSearchCacheSvcImpl implements ISearchCacheSvc { /* @@ -56,13 +62,12 @@ public class DatabaseSearchCacheSvcImpl implements ISearchCacheSvc { * type query and this can fail if we have 1000s of params */ public static final int DEFAULT_MAX_RESULTS_TO_DELETE_IN_ONE_STMT = 500; - public static final int DEFAULT_MAX_RESULTS_TO_DELETE_IN_ONE_PAS = 20000; + public static final int DEFAULT_MAX_RESULTS_TO_DELETE_IN_ONE_PAS = 50000; public static final long SEARCH_CLEANUP_JOB_INTERVAL_MILLIS = DateUtils.MILLIS_PER_MINUTE; public static final int DEFAULT_MAX_DELETE_CANDIDATES_TO_FIND = 2000; private static final Logger ourLog = LoggerFactory.getLogger(DatabaseSearchCacheSvcImpl.class); private static int ourMaximumResultsToDeleteInOneStatement = DEFAULT_MAX_RESULTS_TO_DELETE_IN_ONE_STMT; - private static int ourMaximumResultsToDeleteInOnePass = DEFAULT_MAX_RESULTS_TO_DELETE_IN_ONE_PAS; - private static int ourMaximumSearchesToCheckForDeletionCandidacy = DEFAULT_MAX_DELETE_CANDIDATES_TO_FIND; + private static int ourMaximumResultsToDeleteInOneCommit = DEFAULT_MAX_RESULTS_TO_DELETE_IN_ONE_PAS; private static Long ourNowForUnitTests; /* * We give a bit of extra leeway just to avoid race conditions where a query result @@ -74,6 +79,9 @@ public class DatabaseSearchCacheSvcImpl implements ISearchCacheSvc { @Autowired private ISearchDao mySearchDao; + @Autowired + private EntityManager myEntityManager; + @Autowired private ISearchResultDao mySearchResultDao; @@ -169,14 +177,249 @@ public class DatabaseSearchCacheSvcImpl implements ISearchCacheSvc { return Optional.empty(); } + /** + * A transient worker for a single pass through stale-search deletion. + */ + class DeleteRun { + final RequestPartitionId myRequestPartitionId; + final Instant myDeadline; + final Date myCutoffForDeletion; + final Set myUpdateDeletedFlagBatch = new HashSet<>(); + final Set myDeleteSearchBatch = new HashSet<>(); + /** the Search pids of the SearchResults we plan to delete in a chunk */ + final Set myDeleteSearchResultsBatch = new HashSet<>(); + /** + * Number of results we have queued up in mySearchPidsToDeleteResults to delete. + * We try to keep this to a reasonable size to avoid long transactions that may escalate to a table lock. + */ + private int myDeleteSearchResultsBatchCount = 0; + + DeleteRun(Instant theDeadline, Date theCutoffForDeletion, RequestPartitionId theRequestPartitionId) { + myDeadline = theDeadline; + myCutoffForDeletion = theCutoffForDeletion; + myRequestPartitionId = theRequestPartitionId; + } + + /** + * Mark all ids in the mySearchesToMarkForDeletion buffer as deleted, and clear the buffer. + */ + public void flushDeleteMarks() { + if (myUpdateDeletedFlagBatch.isEmpty()) { + return; + } + ourLog.debug("Marking {} searches as deleted", myUpdateDeletedFlagBatch.size()); + mySearchDao.updateDeleted(myUpdateDeletedFlagBatch, true); + myUpdateDeletedFlagBatch.clear(); + commitOpenChanges(); + } + + /** + * Dig into the guts of our Hibernate session, flush any changes in the session, and commit the underlying connection. + */ + private void commitOpenChanges() { + // flush to force Hibernate to actually get a connection from the pool + myEntityManager.flush(); + // get our connection from the underlying Hibernate session, and commit + //noinspection resource + myEntityManager.unwrap(Session.class).doWork(Connection::commit); + } + + void throwIfDeadlineExpired() { + boolean result = Instant.ofEpochMilli(now()).isAfter(myDeadline); + if (result) { + throw new DeadlineException( + Msg.code(2443) + "Deadline expired while cleaning Search cache - " + myDeadline); + } + } + + private int deleteMarkedSearchesInBatches() { + AtomicInteger deletedCounter = new AtomicInteger(0); + + try (final Stream toDelete = mySearchDao.findDeleted()) { + assert toDelete != null; + + toDelete.forEach(nextSearchToDelete -> { + throwIfDeadlineExpired(); + + deleteSearchAndResults(nextSearchToDelete.searchId, nextSearchToDelete.size); + + deletedCounter.incrementAndGet(); + }); + } + + // flush anything left in the buffers + flushSearchResultDeletes(); + flushSearchAndIncludeDeletes(); + + int deletedCount = deletedCounter.get(); + + ourLog.info("Deleted {} expired searches", deletedCount); + + return deletedCount; + } + + /** + * Schedule theSearchPid for deletion assuming it has theNumberOfResults SearchResults attached. + * + * We accumulate a batch of search pids for deletion, and then do a bulk DML as we reach a threshold number + * of SearchResults. + * + * @param theSearchPid pk of the Search + * @param theNumberOfResults the number of SearchResults attached + */ + private void deleteSearchAndResults(long theSearchPid, int theNumberOfResults) { + ourLog.trace("Buffering deletion of search pid {} and {} results", theSearchPid, theNumberOfResults); + + myDeleteSearchBatch.add(theSearchPid); + + if (theNumberOfResults > ourMaximumResultsToDeleteInOneCommit) { + // don't buffer this one - do it inline + deleteSearchResultsByChunk(theSearchPid, theNumberOfResults); + return; + } + myDeleteSearchResultsBatch.add(theSearchPid); + myDeleteSearchResultsBatchCount += theNumberOfResults; + + if (myDeleteSearchResultsBatchCount > ourMaximumResultsToDeleteInOneCommit) { + flushSearchResultDeletes(); + } + + if (myDeleteSearchBatch.size() > ourMaximumResultsToDeleteInOneStatement) { + // flush the results to make sure we don't have any references. + flushSearchResultDeletes(); + + flushSearchAndIncludeDeletes(); + } + } + + /** + * If this Search has more results than our max delete size, + * delete in by itself in range chunks. + * @param theSearchPid the target Search pid + * @param theNumberOfResults the number of search results present + */ + private void deleteSearchResultsByChunk(long theSearchPid, int theNumberOfResults) { + ourLog.debug( + "Search {} is large: has {} results. Deleting results in chunks.", + theSearchPid, + theNumberOfResults); + for (int rangeEnd = theNumberOfResults; rangeEnd >= 0; rangeEnd -= ourMaximumResultsToDeleteInOneCommit) { + int rangeStart = rangeEnd - ourMaximumResultsToDeleteInOneCommit; + ourLog.trace("Deleting results for search {}: {} - {}", theSearchPid, rangeStart, rangeEnd); + mySearchResultDao.deleteBySearchIdInRange(theSearchPid, rangeStart, rangeEnd); + commitOpenChanges(); + } + } + + private void flushSearchAndIncludeDeletes() { + if (myDeleteSearchBatch.isEmpty()) { + return; + } + ourLog.debug("Deleting {} Search records", myDeleteSearchBatch.size()); + // referential integrity requires we delete includes before the search + mySearchIncludeDao.deleteForSearch(myDeleteSearchBatch); + mySearchDao.deleteByPids(myDeleteSearchBatch); + myDeleteSearchBatch.clear(); + commitOpenChanges(); + } + + private void flushSearchResultDeletes() { + if (myDeleteSearchResultsBatch.isEmpty()) { + return; + } + ourLog.debug( + "Deleting {} Search Results from {} searches", + myDeleteSearchResultsBatchCount, + myDeleteSearchResultsBatch.size()); + mySearchResultDao.deleteBySearchIds(myDeleteSearchResultsBatch); + myDeleteSearchResultsBatch.clear(); + myDeleteSearchResultsBatchCount = 0; + commitOpenChanges(); + } + + IExecutionBuilder getTxBuilder() { + return myTransactionService.withSystemRequest().withRequestPartitionId(myRequestPartitionId); + } + + private void run() { + ourLog.debug("Searching for searches which are before {}", myCutoffForDeletion); + + // this tx builder is not really for tx management. + // Instead, it is used bind a Hibernate session + connection to this thread. + // We will run a streaming query to look for work, and then commit changes in batches during the loops. + getTxBuilder().execute(theStatus -> { + try { + markDeletedInBatches(); + + throwIfDeadlineExpired(); + + // Delete searches that are marked as deleted + int deletedCount = deleteMarkedSearchesInBatches(); + + throwIfDeadlineExpired(); + + if ((ourLog.isDebugEnabled() || HapiSystemProperties.isTestModeEnabled()) && (deletedCount > 0)) { + Long total = mySearchDao.count(); + ourLog.debug("Deleted {} searches, {} remaining", deletedCount, total); + } + } catch (DeadlineException theTimeoutException) { + ourLog.warn(theTimeoutException.getMessage()); + } + + return null; + }); + } + + /** + * Stream through a list of pids before our cutoff, and set myDeleted=true in batches in a DML statement. + */ + private void markDeletedInBatches() { + + try (Stream toMarkDeleted = + mySearchDao.findWhereCreatedBefore(myCutoffForDeletion, new Date(now()))) { + assert toMarkDeleted != null; + + toMarkDeleted.forEach(nextSearchToDelete -> { + throwIfDeadlineExpired(); + + if (myUpdateDeletedFlagBatch.size() >= ourMaximumResultsToDeleteInOneStatement) { + flushDeleteMarks(); + } + ourLog.trace("Marking search with PID {} as ready for deletion", nextSearchToDelete); + myUpdateDeletedFlagBatch.add(nextSearchToDelete); + }); + + flushDeleteMarks(); + } + } + } + + /** + * Marker to abandon our delete run when we are over time. + */ + private static class DeadlineException extends RuntimeException { + public DeadlineException(String message) { + super(message); + } + } + @Override - public void pollForStaleSearchesAndDeleteThem(RequestPartitionId theRequestPartitionId) { + public void pollForStaleSearchesAndDeleteThem(RequestPartitionId theRequestPartitionId, Instant theDeadline) { HapiTransactionService.noTransactionAllowed(); if (!myStorageSettings.isExpireSearchResults()) { return; } + final Date cutoff = getCutoff(); + + final DeleteRun run = new DeleteRun(theDeadline, cutoff, theRequestPartitionId); + + run.run(); + } + + @Nonnull + private Date getCutoff() { long cutoffMillis = myStorageSettings.getExpireSearchResultsAfterMillis(); if (myStorageSettings.getReuseCachedSearchResultsForMillis() != null) { cutoffMillis = cutoffMillis + myStorageSettings.getReuseCachedSearchResultsForMillis(); @@ -189,108 +432,16 @@ public class DatabaseSearchCacheSvcImpl implements ISearchCacheSvc { new InstantType(cutoff), new InstantType(new Date(now()))); } - - ourLog.debug("Searching for searches which are before {}", cutoff); - - // Mark searches as deleted if they should be - final Slice toMarkDeleted = myTransactionService - .withSystemRequestOnPartition(theRequestPartitionId) - .execute(theStatus -> mySearchDao.findWhereCreatedBefore( - cutoff, new Date(), PageRequest.of(0, ourMaximumSearchesToCheckForDeletionCandidacy))); - assert toMarkDeleted != null; - for (final Long nextSearchToDelete : toMarkDeleted) { - ourLog.debug("Deleting search with PID {}", nextSearchToDelete); - myTransactionService - .withSystemRequest() - .withRequestPartitionId(theRequestPartitionId) - .execute(t -> { - mySearchDao.updateDeleted(nextSearchToDelete, true); - return null; - }); - } - - // Delete searches that are marked as deleted - final Slice toDelete = myTransactionService - .withSystemRequestOnPartition(theRequestPartitionId) - .execute(theStatus -> - mySearchDao.findDeleted(PageRequest.of(0, ourMaximumSearchesToCheckForDeletionCandidacy))); - assert toDelete != null; - for (final Long nextSearchToDelete : toDelete) { - ourLog.debug("Deleting search with PID {}", nextSearchToDelete); - myTransactionService - .withSystemRequest() - .withRequestPartitionId(theRequestPartitionId) - .execute(t -> { - deleteSearch(nextSearchToDelete); - return null; - }); - } - - int count = toDelete.getContent().size(); - if (count > 0) { - if (ourLog.isDebugEnabled() || HapiSystemProperties.isTestModeEnabled()) { - Long total = myTransactionService - .withSystemRequest() - .withRequestPartitionId(theRequestPartitionId) - .execute(t -> mySearchDao.count()); - ourLog.debug("Deleted {} searches, {} remaining", count, total); - } - } - } - - private void deleteSearch(final Long theSearchPid) { - mySearchDao.findById(theSearchPid).ifPresent(searchToDelete -> { - mySearchIncludeDao.deleteForSearch(searchToDelete.getId()); - - /* - * Note, we're only deleting up to 500 results in an individual search here. This - * is to prevent really long running transactions in cases where there are - * huge searches with tons of results in them. By the time we've gotten here - * we have marked the parent Search entity as deleted, so it's not such a - * huge deal to be only partially deleting search results. They'll get deleted - * eventually - */ - int max = ourMaximumResultsToDeleteInOnePass; - Slice resultPids = mySearchResultDao.findForSearch(PageRequest.of(0, max), searchToDelete.getId()); - if (resultPids.hasContent()) { - List> partitions = - Lists.partition(resultPids.getContent(), ourMaximumResultsToDeleteInOneStatement); - for (List nextPartition : partitions) { - mySearchResultDao.deleteByIds(nextPartition); - } - } - - // Only delete if we don't have results left in this search - if (resultPids.getNumberOfElements() < max) { - ourLog.debug( - "Deleting search {}/{} - Created[{}]", - searchToDelete.getId(), - searchToDelete.getUuid(), - new InstantType(searchToDelete.getCreated())); - mySearchDao.deleteByPid(searchToDelete.getId()); - } else { - ourLog.debug( - "Purged {} search results for deleted search {}/{}", - resultPids.getSize(), - searchToDelete.getId(), - searchToDelete.getUuid()); - } - }); - } - - @VisibleForTesting - public static void setMaximumSearchesToCheckForDeletionCandidacyForUnitTest( - int theMaximumSearchesToCheckForDeletionCandidacy) { - ourMaximumSearchesToCheckForDeletionCandidacy = theMaximumSearchesToCheckForDeletionCandidacy; + return cutoff; } @VisibleForTesting public static void setMaximumResultsToDeleteInOnePassForUnitTest(int theMaximumResultsToDeleteInOnePass) { - ourMaximumResultsToDeleteInOnePass = theMaximumResultsToDeleteInOnePass; + ourMaximumResultsToDeleteInOneCommit = theMaximumResultsToDeleteInOnePass; } @VisibleForTesting - public static void setMaximumResultsToDeleteForUnitTest(int theMaximumResultsToDelete) { + public static void setMaximumResultsToDeleteInOneStatement(int theMaximumResultsToDelete) { ourMaximumResultsToDeleteInOneStatement = theMaximumResultsToDelete; } @@ -302,7 +453,7 @@ public class DatabaseSearchCacheSvcImpl implements ISearchCacheSvc { ourNowForUnitTests = theNowForUnitTests; } - private static long now() { + public static long now() { if (ourNowForUnitTests != null) { return ourNowForUnitTests; } diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/search/cache/ISearchCacheSvc.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/search/cache/ISearchCacheSvc.java index 34c662b83f7..b417ce9dd5e 100644 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/search/cache/ISearchCacheSvc.java +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/search/cache/ISearchCacheSvc.java @@ -86,5 +86,5 @@ public interface ISearchCacheSvc { * if they have some other mechanism for expiring stale results other than manually looking for them * and deleting them. */ - void pollForStaleSearchesAndDeleteThem(RequestPartitionId theRequestPartitionId); + void pollForStaleSearchesAndDeleteThem(RequestPartitionId theRequestPartitionId, Instant theDeadline); } diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/search/cache/ISearchResultCacheSvc.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/search/cache/ISearchResultCacheSvc.java index 68dc8745f28..9956db92198 100644 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/search/cache/ISearchResultCacheSvc.java +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/search/cache/ISearchResultCacheSvc.java @@ -23,9 +23,9 @@ import ca.uhn.fhir.interceptor.model.RequestPartitionId; import ca.uhn.fhir.jpa.entity.Search; import ca.uhn.fhir.jpa.model.dao.JpaPid; import ca.uhn.fhir.rest.api.server.RequestDetails; +import jakarta.annotation.Nullable; import java.util.List; -import javax.annotation.Nullable; public interface ISearchResultCacheSvc { /** diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/search/lastn/ElasticsearchRestClientFactory.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/search/lastn/ElasticsearchRestClientFactory.java index a5c8481b8a5..c89bebe91a9 100644 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/search/lastn/ElasticsearchRestClientFactory.java +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/search/lastn/ElasticsearchRestClientFactory.java @@ -21,6 +21,11 @@ package ca.uhn.fhir.jpa.search.lastn; import ca.uhn.fhir.context.ConfigurationException; import ca.uhn.fhir.i18n.Msg; +import co.elastic.clients.elasticsearch.ElasticsearchClient; +import co.elastic.clients.json.jackson.JacksonJsonpMapper; +import co.elastic.clients.transport.ElasticsearchTransport; +import co.elastic.clients.transport.rest_client.RestClientTransport; +import jakarta.annotation.Nullable; import org.apache.commons.lang3.StringUtils; import org.apache.http.Header; import org.apache.http.HttpHost; @@ -32,16 +37,14 @@ import org.apache.http.message.BasicHeader; import org.elasticsearch.client.Node; import org.elasticsearch.client.RestClient; import org.elasticsearch.client.RestClientBuilder; -import org.elasticsearch.client.RestHighLevelClient; import java.util.Arrays; import java.util.List; import java.util.stream.Collectors; -import javax.annotation.Nullable; public class ElasticsearchRestClientFactory { - public static RestHighLevelClient createElasticsearchHighLevelRestClient( + public static ElasticsearchClient createElasticsearchHighLevelRestClient( String protocol, String hosts, @Nullable String theUsername, @Nullable String thePassword) { if (hosts.contains("://")) { @@ -78,6 +81,12 @@ public class ElasticsearchRestClientFactory { Header[] defaultHeaders = new Header[] {new BasicHeader("Content-Type", "application/json")}; clientBuilder.setDefaultHeaders(defaultHeaders); - return new RestHighLevelClient(clientBuilder); + RestClient restClient = clientBuilder.build(); + + // Create the transport with a Jackson mapper + ElasticsearchTransport transport = new RestClientTransport(restClient, new JacksonJsonpMapper()); + + // And create the API client + return new ElasticsearchClient(transport); } } diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/search/lastn/ElasticsearchSvcImpl.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/search/lastn/ElasticsearchSvcImpl.java index 1479b017cd8..00871d5c251 100644 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/search/lastn/ElasticsearchSvcImpl.java +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/search/lastn/ElasticsearchSvcImpl.java @@ -23,108 +23,41 @@ import ca.uhn.fhir.context.FhirContext; import ca.uhn.fhir.i18n.Msg; import ca.uhn.fhir.jpa.dao.TolerantJsonParser; import ca.uhn.fhir.jpa.model.config.PartitionSettings; -import ca.uhn.fhir.jpa.model.util.CodeSystemHash; -import ca.uhn.fhir.jpa.search.lastn.json.CodeJson; import ca.uhn.fhir.jpa.search.lastn.json.ObservationJson; -import ca.uhn.fhir.jpa.searchparam.SearchParameterMap; -import ca.uhn.fhir.jpa.searchparam.util.LastNParameterHelper; -import ca.uhn.fhir.model.api.IQueryParameterType; import ca.uhn.fhir.parser.IParser; import ca.uhn.fhir.rest.api.server.storage.IResourcePersistentId; -import ca.uhn.fhir.rest.param.DateParam; -import ca.uhn.fhir.rest.param.ParamPrefixEnum; -import ca.uhn.fhir.rest.param.ReferenceParam; -import ca.uhn.fhir.rest.param.TokenParam; import ca.uhn.fhir.rest.server.exceptions.InvalidRequestException; -import com.fasterxml.jackson.core.JsonProcessingException; -import com.fasterxml.jackson.databind.ObjectMapper; +import co.elastic.clients.elasticsearch.ElasticsearchClient; +import co.elastic.clients.elasticsearch._types.FieldValue; +import co.elastic.clients.elasticsearch.core.SearchRequest; +import co.elastic.clients.elasticsearch.core.SearchResponse; +import co.elastic.clients.elasticsearch.core.search.Hit; +import co.elastic.clients.elasticsearch.indices.ExistsRequest; import com.google.common.annotations.VisibleForTesting; -import org.apache.commons.lang3.Validate; -import org.elasticsearch.action.DocWriteResponse; -import org.elasticsearch.action.admin.indices.refresh.RefreshRequest; -import org.elasticsearch.action.index.IndexRequest; -import org.elasticsearch.action.index.IndexResponse; -import org.elasticsearch.action.search.SearchRequest; -import org.elasticsearch.action.search.SearchResponse; -import org.elasticsearch.client.RequestOptions; -import org.elasticsearch.client.RestHighLevelClient; -import org.elasticsearch.client.indices.CreateIndexRequest; -import org.elasticsearch.client.indices.CreateIndexResponse; -import org.elasticsearch.client.indices.GetIndexRequest; -import org.elasticsearch.index.query.BoolQueryBuilder; -import org.elasticsearch.index.query.MatchQueryBuilder; -import org.elasticsearch.index.query.QueryBuilders; -import org.elasticsearch.index.query.RangeQueryBuilder; -import org.elasticsearch.index.reindex.DeleteByQueryRequest; -import org.elasticsearch.search.SearchHit; -import org.elasticsearch.search.SearchHits; -import org.elasticsearch.search.aggregations.AggregationBuilder; -import org.elasticsearch.search.aggregations.AggregationBuilders; -import org.elasticsearch.search.aggregations.Aggregations; -import org.elasticsearch.search.aggregations.BucketOrder; -import org.elasticsearch.search.aggregations.bucket.composite.CompositeAggregationBuilder; -import org.elasticsearch.search.aggregations.bucket.composite.CompositeValuesSourceBuilder; -import org.elasticsearch.search.aggregations.bucket.composite.ParsedComposite; -import org.elasticsearch.search.aggregations.bucket.composite.TermsValuesSourceBuilder; -import org.elasticsearch.search.aggregations.bucket.terms.ParsedTerms; -import org.elasticsearch.search.aggregations.bucket.terms.Terms; -import org.elasticsearch.search.aggregations.bucket.terms.TermsAggregationBuilder; -import org.elasticsearch.search.aggregations.metrics.ParsedTopHits; -import org.elasticsearch.search.builder.SearchSourceBuilder; -import org.elasticsearch.search.sort.SortOrder; -import org.elasticsearch.xcontent.XContentType; +import jakarta.annotation.Nullable; import org.hl7.fhir.instance.model.api.IBaseResource; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; import java.io.BufferedReader; import java.io.IOException; import java.io.InputStreamReader; -import java.util.ArrayList; -import java.util.Arrays; +import java.io.StringReader; import java.util.Collection; import java.util.List; -import java.util.function.Function; import java.util.stream.Collectors; -import javax.annotation.Nullable; - -import static org.apache.commons.lang3.StringUtils.isBlank; public class ElasticsearchSvcImpl implements IElasticsearchSvc { - private static final Logger ourLog = LoggerFactory.getLogger(ElasticsearchSvcImpl.class); - // Index Constants public static final String OBSERVATION_INDEX = "observation_index"; public static final String OBSERVATION_CODE_INDEX = "code_index"; - public static final String OBSERVATION_DOCUMENT_TYPE = - "ca.uhn.fhir.jpa.model.entity.ObservationIndexedSearchParamLastNEntity"; - public static final String CODE_DOCUMENT_TYPE = - "ca.uhn.fhir.jpa.model.entity.ObservationIndexedCodeCodeableConceptEntity"; public static final String OBSERVATION_INDEX_SCHEMA_FILE = "ObservationIndexSchema.json"; public static final String OBSERVATION_CODE_INDEX_SCHEMA_FILE = "ObservationCodeIndexSchema.json"; // Aggregation Constants - private static final String GROUP_BY_SUBJECT = "group_by_subject"; - private static final String GROUP_BY_SYSTEM = "group_by_system"; - private static final String GROUP_BY_CODE = "group_by_code"; - private static final String MOST_RECENT_EFFECTIVE = "most_recent_effective"; // Observation index document element names private static final String OBSERVATION_IDENTIFIER_FIELD_NAME = "identifier"; - private static final String OBSERVATION_SUBJECT_FIELD_NAME = "subject"; - private static final String OBSERVATION_CODEVALUE_FIELD_NAME = "codeconceptcodingcode"; - private static final String OBSERVATION_CODESYSTEM_FIELD_NAME = "codeconceptcodingsystem"; - private static final String OBSERVATION_CODEHASH_FIELD_NAME = "codeconceptcodingcode_system_hash"; - private static final String OBSERVATION_CODEDISPLAY_FIELD_NAME = "codeconceptcodingdisplay"; - private static final String OBSERVATION_CODE_TEXT_FIELD_NAME = "codeconcepttext"; - private static final String OBSERVATION_EFFECTIVEDTM_FIELD_NAME = "effectivedtm"; - private static final String OBSERVATION_CATEGORYHASH_FIELD_NAME = "categoryconceptcodingcode_system_hash"; - private static final String OBSERVATION_CATEGORYVALUE_FIELD_NAME = "categoryconceptcodingcode"; - private static final String OBSERVATION_CATEGORYSYSTEM_FIELD_NAME = "categoryconceptcodingsystem"; - private static final String OBSERVATION_CATEGORYDISPLAY_FIELD_NAME = "categoryconceptcodingdisplay"; - private static final String OBSERVATION_CATEGORYTEXT_FIELD_NAME = "categoryconcepttext"; // Code index document element names private static final String CODE_HASH = "codingcode_system_hash"; @@ -132,12 +65,7 @@ public class ElasticsearchSvcImpl implements IElasticsearchSvc { private static final String OBSERVATION_RESOURCE_NAME = "Observation"; - private final RestHighLevelClient myRestHighLevelClient; - - private final ObjectMapper objectMapper = new ObjectMapper(); - - @Autowired - private PartitionSettings myPartitionSettings; + private final ElasticsearchClient myRestHighLevelClient; @Autowired private FhirContext myContext; @@ -150,11 +78,11 @@ public class ElasticsearchSvcImpl implements IElasticsearchSvc { @Nullable String theUsername, @Nullable String thePassword) { this(theProtocol, theHostname, theUsername, thePassword); - this.myPartitionSettings = thePartitionSetings; } public ElasticsearchSvcImpl( String theProtocol, String theHostname, @Nullable String theUsername, @Nullable String thePassword) { + myRestHighLevelClient = ElasticsearchRestClientFactory.createElasticsearchHighLevelRestClient( theProtocol, theHostname, theUsername, thePassword); @@ -200,648 +128,30 @@ public class ElasticsearchSvcImpl implements IElasticsearchSvc { } private boolean createIndex(String theIndexName, String theMapping) throws IOException { - CreateIndexRequest request = new CreateIndexRequest(theIndexName); - request.source(theMapping, XContentType.JSON); - CreateIndexResponse createIndexResponse = - myRestHighLevelClient.indices().create(request, RequestOptions.DEFAULT); - return createIndexResponse.isAcknowledged(); + return myRestHighLevelClient + .indices() + .create(cir -> cir.index(theIndexName).withJson(new StringReader(theMapping))) + .acknowledged(); } private boolean indexExists(String theIndexName) throws IOException { - GetIndexRequest request = new GetIndexRequest(theIndexName); - return myRestHighLevelClient.indices().exists(request, RequestOptions.DEFAULT); - } - - @Override - public List executeLastN( - SearchParameterMap theSearchParameterMap, FhirContext theFhirContext, Integer theMaxResultsToFetch) { - Validate.isTrue( - !myPartitionSettings.isPartitioningEnabled(), - "$lastn is not currently supported on partitioned servers"); - - String[] topHitsInclude = {OBSERVATION_IDENTIFIER_FIELD_NAME}; - return buildAndExecuteSearch( - theSearchParameterMap, - theFhirContext, - topHitsInclude, - ObservationJson::getIdentifier, - theMaxResultsToFetch); - } - - private List buildAndExecuteSearch( - SearchParameterMap theSearchParameterMap, - FhirContext theFhirContext, - String[] topHitsInclude, - Function setValue, - Integer theMaxResultsToFetch) { - String patientParamName = LastNParameterHelper.getPatientParamName(theFhirContext); - String subjectParamName = LastNParameterHelper.getSubjectParamName(theFhirContext); - List searchResults = new ArrayList<>(); - if (theSearchParameterMap.containsKey(patientParamName) - || theSearchParameterMap.containsKey(subjectParamName)) { - for (String subject : - getSubjectReferenceCriteria(patientParamName, subjectParamName, theSearchParameterMap)) { - if (theMaxResultsToFetch != null && searchResults.size() >= theMaxResultsToFetch) { - break; - } - SearchRequest myLastNRequest = buildObservationsSearchRequest( - subject, - theSearchParameterMap, - theFhirContext, - createObservationSubjectAggregationBuilder( - getMaxParameter(theSearchParameterMap), topHitsInclude)); - ourLog.debug("ElasticSearch query: {}", myLastNRequest.source().toString()); - try { - SearchResponse lastnResponse = executeSearchRequest(myLastNRequest); - searchResults.addAll(buildObservationList( - lastnResponse, setValue, theSearchParameterMap, theFhirContext, theMaxResultsToFetch)); - } catch (IOException theE) { - throw new InvalidRequestException(Msg.code(1178) + "Unable to execute LastN request", theE); - } - } - } else { - SearchRequest myLastNRequest = buildObservationsSearchRequest( - theSearchParameterMap, - theFhirContext, - createObservationCodeAggregationBuilder(getMaxParameter(theSearchParameterMap), topHitsInclude)); - ourLog.debug("ElasticSearch query: {}", myLastNRequest.source().toString()); - try { - SearchResponse lastnResponse = executeSearchRequest(myLastNRequest); - searchResults.addAll(buildObservationList( - lastnResponse, setValue, theSearchParameterMap, theFhirContext, theMaxResultsToFetch)); - } catch (IOException theE) { - throw new InvalidRequestException(Msg.code(1179) + "Unable to execute LastN request", theE); - } - } - return searchResults; - } - - private int getMaxParameter(SearchParameterMap theSearchParameterMap) { - if (theSearchParameterMap.getLastNMax() == null) { - return 1; - } else { - return theSearchParameterMap.getLastNMax(); - } - } - - private List getSubjectReferenceCriteria( - String thePatientParamName, String theSubjectParamName, SearchParameterMap theSearchParameterMap) { - List subjectReferenceCriteria = new ArrayList<>(); - - List> patientParams = new ArrayList<>(); - if (theSearchParameterMap.get(thePatientParamName) != null) { - patientParams.addAll(theSearchParameterMap.get(thePatientParamName)); - } - if (theSearchParameterMap.get(theSubjectParamName) != null) { - patientParams.addAll(theSearchParameterMap.get(theSubjectParamName)); - } - for (List nextSubjectList : patientParams) { - subjectReferenceCriteria.addAll(getReferenceValues(nextSubjectList)); - } - return subjectReferenceCriteria; - } - - private List getReferenceValues(List referenceParams) { - List referenceList = new ArrayList<>(); - - for (IQueryParameterType nextOr : referenceParams) { - - if (nextOr instanceof ReferenceParam) { - ReferenceParam ref = (ReferenceParam) nextOr; - if (isBlank(ref.getChain())) { - referenceList.add(ref.getValue()); - } - } else { - throw new IllegalArgumentException( - Msg.code(1180) + "Invalid token type (expecting ReferenceParam): " + nextOr.getClass()); - } - } - return referenceList; - } - - private CompositeAggregationBuilder createObservationSubjectAggregationBuilder( - Integer theMaxNumberObservationsPerCode, String[] theTopHitsInclude) { - CompositeValuesSourceBuilder subjectValuesBuilder = - new TermsValuesSourceBuilder(OBSERVATION_SUBJECT_FIELD_NAME).field(OBSERVATION_SUBJECT_FIELD_NAME); - List> compositeAggSubjectSources = new ArrayList<>(); - compositeAggSubjectSources.add(subjectValuesBuilder); - CompositeAggregationBuilder compositeAggregationSubjectBuilder = - new CompositeAggregationBuilder(GROUP_BY_SUBJECT, compositeAggSubjectSources); - compositeAggregationSubjectBuilder.subAggregation( - createObservationCodeAggregationBuilder(theMaxNumberObservationsPerCode, theTopHitsInclude)); - compositeAggregationSubjectBuilder.size(10000); - - return compositeAggregationSubjectBuilder; - } - - private TermsAggregationBuilder createObservationCodeAggregationBuilder( - int theMaxNumberObservationsPerCode, String[] theTopHitsInclude) { - TermsAggregationBuilder observationCodeCodeAggregationBuilder = - new TermsAggregationBuilder(GROUP_BY_CODE).field(OBSERVATION_CODEVALUE_FIELD_NAME); - observationCodeCodeAggregationBuilder.order(BucketOrder.key(true)); - // Top Hits Aggregation - observationCodeCodeAggregationBuilder.subAggregation(AggregationBuilders.topHits(MOST_RECENT_EFFECTIVE) - .sort(OBSERVATION_EFFECTIVEDTM_FIELD_NAME, SortOrder.DESC) - .fetchSource(theTopHitsInclude, null) - .size(theMaxNumberObservationsPerCode)); - observationCodeCodeAggregationBuilder.size(10000); - TermsAggregationBuilder observationCodeSystemAggregationBuilder = - new TermsAggregationBuilder(GROUP_BY_SYSTEM).field(OBSERVATION_CODESYSTEM_FIELD_NAME); - observationCodeSystemAggregationBuilder.order(BucketOrder.key(true)); - observationCodeSystemAggregationBuilder.subAggregation(observationCodeCodeAggregationBuilder); - return observationCodeSystemAggregationBuilder; - } - - private SearchResponse executeSearchRequest(SearchRequest searchRequest) throws IOException { - return myRestHighLevelClient.search(searchRequest, RequestOptions.DEFAULT); - } - - private List buildObservationList( - SearchResponse theSearchResponse, - Function setValue, - SearchParameterMap theSearchParameterMap, - FhirContext theFhirContext, - Integer theMaxResultsToFetch) - throws IOException { - List theObservationList = new ArrayList<>(); - if (theSearchParameterMap.containsKey(LastNParameterHelper.getPatientParamName(theFhirContext)) - || theSearchParameterMap.containsKey(LastNParameterHelper.getSubjectParamName(theFhirContext))) { - for (ParsedComposite.ParsedBucket subjectBucket : getSubjectBuckets(theSearchResponse)) { - if (theMaxResultsToFetch != null && theObservationList.size() >= theMaxResultsToFetch) { - break; - } - for (Terms.Bucket observationCodeBucket : getObservationCodeBuckets(subjectBucket)) { - if (theMaxResultsToFetch != null && theObservationList.size() >= theMaxResultsToFetch) { - break; - } - for (SearchHit lastNMatch : getLastNMatches(observationCodeBucket)) { - if (theMaxResultsToFetch != null && theObservationList.size() >= theMaxResultsToFetch) { - break; - } - String indexedObservation = lastNMatch.getSourceAsString(); - ObservationJson observationJson = - objectMapper.readValue(indexedObservation, ObservationJson.class); - theObservationList.add(setValue.apply(observationJson)); - } - } - } - } else { - for (Terms.Bucket observationCodeBucket : getObservationCodeBuckets(theSearchResponse)) { - if (theMaxResultsToFetch != null && theObservationList.size() >= theMaxResultsToFetch) { - break; - } - for (SearchHit lastNMatch : getLastNMatches(observationCodeBucket)) { - if (theMaxResultsToFetch != null && theObservationList.size() >= theMaxResultsToFetch) { - break; - } - String indexedObservation = lastNMatch.getSourceAsString(); - ObservationJson observationJson = objectMapper.readValue(indexedObservation, ObservationJson.class); - theObservationList.add(setValue.apply(observationJson)); - } - } - } - - return theObservationList; - } - - private List getSubjectBuckets(SearchResponse theSearchResponse) { - Aggregations responseAggregations = theSearchResponse.getAggregations(); - ParsedComposite aggregatedSubjects = responseAggregations.get(GROUP_BY_SUBJECT); - return aggregatedSubjects.getBuckets(); - } - - private List getObservationCodeBuckets(SearchResponse theSearchResponse) { - Aggregations responseAggregations = theSearchResponse.getAggregations(); - return getObservationCodeBuckets(responseAggregations); - } - - private List getObservationCodeBuckets(ParsedComposite.ParsedBucket theSubjectBucket) { - Aggregations observationCodeSystemAggregations = theSubjectBucket.getAggregations(); - return getObservationCodeBuckets(observationCodeSystemAggregations); - } - - private List getObservationCodeBuckets(Aggregations theObservationCodeSystemAggregations) { - List retVal = new ArrayList<>(); - ParsedTerms aggregatedObservationCodeSystems = theObservationCodeSystemAggregations.get(GROUP_BY_SYSTEM); - for (Terms.Bucket observationCodeSystem : aggregatedObservationCodeSystems.getBuckets()) { - Aggregations observationCodeCodeAggregations = observationCodeSystem.getAggregations(); - ParsedTerms aggregatedObservationCodeCodes = observationCodeCodeAggregations.get(GROUP_BY_CODE); - retVal.addAll(aggregatedObservationCodeCodes.getBuckets()); - } - return retVal; - } - - private SearchHit[] getLastNMatches(Terms.Bucket theObservationCodeBucket) { - Aggregations topHitObservationCodes = theObservationCodeBucket.getAggregations(); - ParsedTopHits parsedTopHits = topHitObservationCodes.get(MOST_RECENT_EFFECTIVE); - return parsedTopHits.getHits().getHits(); - } - - private SearchRequest buildObservationsSearchRequest( - SearchParameterMap theSearchParameterMap, - FhirContext theFhirContext, - AggregationBuilder theAggregationBuilder) { - SearchRequest searchRequest = new SearchRequest(OBSERVATION_INDEX); - SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder(); - // Query - if (!searchParamsHaveLastNCriteria(theSearchParameterMap, theFhirContext)) { - searchSourceBuilder.query(QueryBuilders.matchAllQuery()); - } else { - BoolQueryBuilder boolQueryBuilder = QueryBuilders.boolQuery(); - addCategoriesCriteria(boolQueryBuilder, theSearchParameterMap, theFhirContext); - addObservationCodeCriteria(boolQueryBuilder, theSearchParameterMap, theFhirContext); - addDateCriteria(boolQueryBuilder, theSearchParameterMap, theFhirContext); - searchSourceBuilder.query(boolQueryBuilder); - } - searchSourceBuilder.size(0); - - // Aggregation by order codes - searchSourceBuilder.aggregation(theAggregationBuilder); - searchRequest.source(searchSourceBuilder); - - return searchRequest; - } - - private SearchRequest buildObservationsSearchRequest( - String theSubjectParam, - SearchParameterMap theSearchParameterMap, - FhirContext theFhirContext, - AggregationBuilder theAggregationBuilder) { - SearchRequest searchRequest = new SearchRequest(OBSERVATION_INDEX); - SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder(); - // Query - BoolQueryBuilder boolQueryBuilder = QueryBuilders.boolQuery(); - boolQueryBuilder.must(QueryBuilders.termQuery(OBSERVATION_SUBJECT_FIELD_NAME, theSubjectParam)); - addCategoriesCriteria(boolQueryBuilder, theSearchParameterMap, theFhirContext); - addObservationCodeCriteria(boolQueryBuilder, theSearchParameterMap, theFhirContext); - addDateCriteria(boolQueryBuilder, theSearchParameterMap, theFhirContext); - searchSourceBuilder.query(boolQueryBuilder); - searchSourceBuilder.size(0); - - // Aggregation by order codes - searchSourceBuilder.aggregation(theAggregationBuilder); - searchRequest.source(searchSourceBuilder); - - return searchRequest; - } - - private Boolean searchParamsHaveLastNCriteria( - SearchParameterMap theSearchParameterMap, FhirContext theFhirContext) { - return theSearchParameterMap != null - && (theSearchParameterMap.containsKey(LastNParameterHelper.getPatientParamName(theFhirContext)) - || theSearchParameterMap.containsKey(LastNParameterHelper.getSubjectParamName(theFhirContext)) - || theSearchParameterMap.containsKey(LastNParameterHelper.getCategoryParamName(theFhirContext)) - || theSearchParameterMap.containsKey(LastNParameterHelper.getCodeParamName(theFhirContext))); - } - - private void addCategoriesCriteria( - BoolQueryBuilder theBoolQueryBuilder, - SearchParameterMap theSearchParameterMap, - FhirContext theFhirContext) { - String categoryParamName = LastNParameterHelper.getCategoryParamName(theFhirContext); - if (theSearchParameterMap.containsKey(categoryParamName)) { - ArrayList codeSystemHashList = new ArrayList<>(); - ArrayList codeOnlyList = new ArrayList<>(); - ArrayList systemOnlyList = new ArrayList<>(); - ArrayList textOnlyList = new ArrayList<>(); - List> andOrParams = theSearchParameterMap.get(categoryParamName); - for (List nextAnd : andOrParams) { - codeSystemHashList.addAll(getCodingCodeSystemValues(nextAnd)); - codeOnlyList.addAll(getCodingCodeOnlyValues(nextAnd)); - systemOnlyList.addAll(getCodingSystemOnlyValues(nextAnd)); - textOnlyList.addAll(getCodingTextOnlyValues(nextAnd)); - } - if (codeSystemHashList.size() > 0) { - theBoolQueryBuilder.must( - QueryBuilders.termsQuery(OBSERVATION_CATEGORYHASH_FIELD_NAME, codeSystemHashList)); - } - if (codeOnlyList.size() > 0) { - theBoolQueryBuilder.must(QueryBuilders.termsQuery(OBSERVATION_CATEGORYVALUE_FIELD_NAME, codeOnlyList)); - } - if (systemOnlyList.size() > 0) { - theBoolQueryBuilder.must( - QueryBuilders.termsQuery(OBSERVATION_CATEGORYSYSTEM_FIELD_NAME, systemOnlyList)); - } - if (textOnlyList.size() > 0) { - BoolQueryBuilder myTextBoolQueryBuilder = QueryBuilders.boolQuery(); - for (String textOnlyParam : textOnlyList) { - myTextBoolQueryBuilder.should(QueryBuilders.matchPhrasePrefixQuery( - OBSERVATION_CATEGORYDISPLAY_FIELD_NAME, textOnlyParam)); - myTextBoolQueryBuilder.should( - QueryBuilders.matchPhrasePrefixQuery(OBSERVATION_CATEGORYTEXT_FIELD_NAME, textOnlyParam)); - } - theBoolQueryBuilder.must(myTextBoolQueryBuilder); - } - } - } - - private List getCodingCodeSystemValues(List codeParams) { - ArrayList codeSystemHashList = new ArrayList<>(); - for (IQueryParameterType nextOr : codeParams) { - if (nextOr instanceof TokenParam) { - TokenParam ref = (TokenParam) nextOr; - if (ref.getSystem() != null && ref.getValue() != null) { - codeSystemHashList.add( - String.valueOf(CodeSystemHash.hashCodeSystem(ref.getSystem(), ref.getValue()))); - } - } else { - throw new IllegalArgumentException( - Msg.code(1181) + "Invalid token type (expecting TokenParam): " + nextOr.getClass()); - } - } - return codeSystemHashList; - } - - private List getCodingCodeOnlyValues(List codeParams) { - ArrayList codeOnlyList = new ArrayList<>(); - for (IQueryParameterType nextOr : codeParams) { - - if (nextOr instanceof TokenParam) { - TokenParam ref = (TokenParam) nextOr; - if (ref.getValue() != null && ref.getSystem() == null && !ref.isText()) { - codeOnlyList.add(ref.getValue()); - } - } else { - throw new IllegalArgumentException( - Msg.code(1182) + "Invalid token type (expecting TokenParam): " + nextOr.getClass()); - } - } - return codeOnlyList; - } - - private List getCodingSystemOnlyValues(List codeParams) { - ArrayList systemOnlyList = new ArrayList<>(); - for (IQueryParameterType nextOr : codeParams) { - - if (nextOr instanceof TokenParam) { - TokenParam ref = (TokenParam) nextOr; - if (ref.getValue() == null && ref.getSystem() != null) { - systemOnlyList.add(ref.getSystem()); - } - } else { - throw new IllegalArgumentException( - Msg.code(1183) + "Invalid token type (expecting TokenParam): " + nextOr.getClass()); - } - } - return systemOnlyList; - } - - private List getCodingTextOnlyValues(List codeParams) { - ArrayList textOnlyList = new ArrayList<>(); - for (IQueryParameterType nextOr : codeParams) { - - if (nextOr instanceof TokenParam) { - TokenParam ref = (TokenParam) nextOr; - if (ref.isText() && ref.getValue() != null) { - textOnlyList.add(ref.getValue()); - } - } else { - throw new IllegalArgumentException( - Msg.code(1184) + "Invalid token type (expecting TokenParam): " + nextOr.getClass()); - } - } - return textOnlyList; - } - - private void addObservationCodeCriteria( - BoolQueryBuilder theBoolQueryBuilder, - SearchParameterMap theSearchParameterMap, - FhirContext theFhirContext) { - String codeParamName = LastNParameterHelper.getCodeParamName(theFhirContext); - if (theSearchParameterMap.containsKey(codeParamName)) { - ArrayList codeSystemHashList = new ArrayList<>(); - ArrayList codeOnlyList = new ArrayList<>(); - ArrayList systemOnlyList = new ArrayList<>(); - ArrayList textOnlyList = new ArrayList<>(); - List> andOrParams = theSearchParameterMap.get(codeParamName); - for (List nextAnd : andOrParams) { - codeSystemHashList.addAll(getCodingCodeSystemValues(nextAnd)); - codeOnlyList.addAll(getCodingCodeOnlyValues(nextAnd)); - systemOnlyList.addAll(getCodingSystemOnlyValues(nextAnd)); - textOnlyList.addAll(getCodingTextOnlyValues(nextAnd)); - } - if (codeSystemHashList.size() > 0) { - theBoolQueryBuilder.must(QueryBuilders.termsQuery(OBSERVATION_CODEHASH_FIELD_NAME, codeSystemHashList)); - } - if (codeOnlyList.size() > 0) { - theBoolQueryBuilder.must(QueryBuilders.termsQuery(OBSERVATION_CODEVALUE_FIELD_NAME, codeOnlyList)); - } - if (systemOnlyList.size() > 0) { - theBoolQueryBuilder.must(QueryBuilders.termsQuery(OBSERVATION_CODESYSTEM_FIELD_NAME, systemOnlyList)); - } - if (textOnlyList.size() > 0) { - BoolQueryBuilder myTextBoolQueryBuilder = QueryBuilders.boolQuery(); - for (String textOnlyParam : textOnlyList) { - myTextBoolQueryBuilder.should( - QueryBuilders.matchPhrasePrefixQuery(OBSERVATION_CODEDISPLAY_FIELD_NAME, textOnlyParam)); - myTextBoolQueryBuilder.should( - QueryBuilders.matchPhrasePrefixQuery(OBSERVATION_CODE_TEXT_FIELD_NAME, textOnlyParam)); - } - theBoolQueryBuilder.must(myTextBoolQueryBuilder); - } - } - } - - private void addDateCriteria( - BoolQueryBuilder theBoolQueryBuilder, - SearchParameterMap theSearchParameterMap, - FhirContext theFhirContext) { - String dateParamName = LastNParameterHelper.getEffectiveParamName(theFhirContext); - if (theSearchParameterMap.containsKey(dateParamName)) { - List> andOrParams = theSearchParameterMap.get(dateParamName); - for (List nextAnd : andOrParams) { - BoolQueryBuilder myDateBoolQueryBuilder = new BoolQueryBuilder(); - for (IQueryParameterType nextOr : nextAnd) { - if (nextOr instanceof DateParam) { - DateParam myDate = (DateParam) nextOr; - createDateCriteria(myDate, myDateBoolQueryBuilder); - } - } - theBoolQueryBuilder.must(myDateBoolQueryBuilder); - } - } - } - - private void createDateCriteria(DateParam theDate, BoolQueryBuilder theBoolQueryBuilder) { - Long dateInstant = theDate.getValue().getTime(); - RangeQueryBuilder myRangeQueryBuilder = new RangeQueryBuilder(OBSERVATION_EFFECTIVEDTM_FIELD_NAME); - - ParamPrefixEnum prefix = theDate.getPrefix(); - if (prefix == ParamPrefixEnum.GREATERTHAN || prefix == ParamPrefixEnum.STARTS_AFTER) { - theBoolQueryBuilder.should(myRangeQueryBuilder.gt(dateInstant)); - } else if (prefix == ParamPrefixEnum.LESSTHAN || prefix == ParamPrefixEnum.ENDS_BEFORE) { - theBoolQueryBuilder.should(myRangeQueryBuilder.lt(dateInstant)); - } else if (prefix == ParamPrefixEnum.LESSTHAN_OR_EQUALS) { - theBoolQueryBuilder.should(myRangeQueryBuilder.lte(dateInstant)); - } else if (prefix == ParamPrefixEnum.GREATERTHAN_OR_EQUALS) { - theBoolQueryBuilder.should(myRangeQueryBuilder.gte(dateInstant)); - } else { - theBoolQueryBuilder.should(new MatchQueryBuilder(OBSERVATION_EFFECTIVEDTM_FIELD_NAME, dateInstant)); - } - } - - @VisibleForTesting - public List executeLastNWithAllFieldsForTest( - SearchParameterMap theSearchParameterMap, FhirContext theFhirContext) { - return buildAndExecuteSearch(theSearchParameterMap, theFhirContext, null, t -> t, 100); - } - - @VisibleForTesting - List queryAllIndexedObservationCodesForTest() throws IOException { - SearchRequest codeSearchRequest = new SearchRequest(OBSERVATION_CODE_INDEX); - SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder(); - // Query - searchSourceBuilder.query(QueryBuilders.matchAllQuery()); - searchSourceBuilder.size(1000); - codeSearchRequest.source(searchSourceBuilder); - SearchResponse codeSearchResponse = executeSearchRequest(codeSearchRequest); - return buildCodeResult(codeSearchResponse); - } - - private List buildCodeResult(SearchResponse theSearchResponse) throws JsonProcessingException { - SearchHits codeHits = theSearchResponse.getHits(); - List codes = new ArrayList<>(); - for (SearchHit codeHit : codeHits) { - CodeJson code = objectMapper.readValue(codeHit.getSourceAsString(), CodeJson.class); - codes.add(code); - } - return codes; - } - - @Override - public ObservationJson getObservationDocument(String theDocumentID) { - if (theDocumentID == null) { - throw new InvalidRequestException( - Msg.code(1185) + "Require non-null document ID for observation document query"); - } - SearchRequest theSearchRequest = buildSingleObservationSearchRequest(theDocumentID); - ObservationJson observationDocumentJson = null; - try { - SearchResponse observationDocumentResponse = executeSearchRequest(theSearchRequest); - SearchHit[] observationDocumentHits = - observationDocumentResponse.getHits().getHits(); - if (observationDocumentHits.length > 0) { - // There should be no more than one hit for the identifier - String observationDocument = observationDocumentHits[0].getSourceAsString(); - observationDocumentJson = objectMapper.readValue(observationDocument, ObservationJson.class); - } - - } catch (IOException theE) { - throw new InvalidRequestException( - Msg.code(1186) + "Unable to execute observation document query for ID " + theDocumentID, theE); - } - - return observationDocumentJson; - } - - private SearchRequest buildSingleObservationSearchRequest(String theObservationIdentifier) { - SearchRequest searchRequest = new SearchRequest(OBSERVATION_INDEX); - SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder(); - BoolQueryBuilder boolQueryBuilder = QueryBuilders.boolQuery(); - boolQueryBuilder.must(QueryBuilders.termQuery(OBSERVATION_IDENTIFIER_FIELD_NAME, theObservationIdentifier)); - searchSourceBuilder.query(boolQueryBuilder); - searchSourceBuilder.size(1); - - searchRequest.source(searchSourceBuilder); - - return searchRequest; - } - - @Override - public CodeJson getObservationCodeDocument(String theCodeSystemHash, String theText) { - if (theCodeSystemHash == null && theText == null) { - throw new InvalidRequestException(Msg.code(1187) - + "Require a non-null code system hash value or display value for observation code document query"); - } - SearchRequest theSearchRequest = buildSingleObservationCodeSearchRequest(theCodeSystemHash, theText); - CodeJson observationCodeDocumentJson = null; - try { - SearchResponse observationCodeDocumentResponse = executeSearchRequest(theSearchRequest); - SearchHit[] observationCodeDocumentHits = - observationCodeDocumentResponse.getHits().getHits(); - if (observationCodeDocumentHits.length > 0) { - // There should be no more than one hit for the code lookup. - String observationCodeDocument = observationCodeDocumentHits[0].getSourceAsString(); - observationCodeDocumentJson = objectMapper.readValue(observationCodeDocument, CodeJson.class); - } - - } catch (IOException theE) { - throw new InvalidRequestException( - Msg.code(1188) + "Unable to execute observation code document query hash code or display", theE); - } - - return observationCodeDocumentJson; - } - - private SearchRequest buildSingleObservationCodeSearchRequest(String theCodeSystemHash, String theText) { - SearchRequest searchRequest = new SearchRequest(OBSERVATION_CODE_INDEX); - SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder(); - BoolQueryBuilder boolQueryBuilder = QueryBuilders.boolQuery(); - if (theCodeSystemHash != null) { - boolQueryBuilder.must(QueryBuilders.termQuery(CODE_HASH, theCodeSystemHash)); - } else { - boolQueryBuilder.must(QueryBuilders.matchPhraseQuery(CODE_TEXT, theText)); - } - - searchSourceBuilder.query(boolQueryBuilder); - searchSourceBuilder.size(1); - - searchRequest.source(searchSourceBuilder); - - return searchRequest; - } - - @Override - public Boolean createOrUpdateObservationIndex(String theDocumentId, ObservationJson theObservationDocument) { - try { - String documentToIndex = objectMapper.writeValueAsString(theObservationDocument); - return performIndex( - OBSERVATION_INDEX, theDocumentId, documentToIndex, ElasticsearchSvcImpl.OBSERVATION_DOCUMENT_TYPE); - } catch (IOException theE) { - throw new InvalidRequestException( - Msg.code(1189) + "Unable to persist Observation document " + theDocumentId); - } - } - - @Override - public Boolean createOrUpdateObservationCodeIndex( - String theCodeableConceptID, CodeJson theObservationCodeDocument) { - try { - String documentToIndex = objectMapper.writeValueAsString(theObservationCodeDocument); - return performIndex( - OBSERVATION_CODE_INDEX, - theCodeableConceptID, - documentToIndex, - ElasticsearchSvcImpl.CODE_DOCUMENT_TYPE); - } catch (IOException theE) { - throw new InvalidRequestException( - Msg.code(1190) + "Unable to persist Observation Code document " + theCodeableConceptID); - } - } - - private boolean performIndex( - String theIndexName, String theDocumentId, String theIndexDocument, String theDocumentType) - throws IOException { - IndexResponse indexResponse = myRestHighLevelClient.index( - createIndexRequest(theIndexName, theDocumentId, theIndexDocument, theDocumentType), - RequestOptions.DEFAULT); - - return (indexResponse.getResult() == DocWriteResponse.Result.CREATED) - || (indexResponse.getResult() == DocWriteResponse.Result.UPDATED); + ExistsRequest request = new ExistsRequest.Builder().index(theIndexName).build(); + return myRestHighLevelClient.indices().exists(request).value(); } @Override public void close() throws IOException { - myRestHighLevelClient.close(); + // nothing } @Override public List getObservationResources(Collection thePids) { SearchRequest searchRequest = buildObservationResourceSearchRequest(thePids); try { - SearchResponse observationDocumentResponse = executeSearchRequest(searchRequest); - SearchHit[] observationDocumentHits = - observationDocumentResponse.getHits().getHits(); + SearchResponse observationDocumentResponse = + myRestHighLevelClient.search(searchRequest, ObservationJson.class); + List> observationDocumentHits = + observationDocumentResponse.hits().hits(); IParser parser = TolerantJsonParser.createWithLenientErrorHandling(myContext, null); Class resourceType = myContext.getResourceDefinition(OBSERVATION_RESOURCE_NAME).getImplementingClass(); @@ -849,8 +159,8 @@ public class ElasticsearchSvcImpl implements IElasticsearchSvc { * @see ca.uhn.fhir.jpa.dao.BaseHapiFhirDao#toResource(Class, IBaseResourceEntity, Collection, boolean) for * details about parsing raw json to BaseResource */ - return Arrays.stream(observationDocumentHits) - .map(this::parseObservationJson) + return observationDocumentHits.stream() + .map(Hit::source) .map(observationJson -> parser.parseResource(resourceType, observationJson.getResource())) .collect(Collectors.toList()); } catch (IOException theE) { @@ -859,55 +169,23 @@ public class ElasticsearchSvcImpl implements IElasticsearchSvc { } } - private ObservationJson parseObservationJson(SearchHit theSearchHit) { - try { - return objectMapper.readValue(theSearchHit.getSourceAsString(), ObservationJson.class); - } catch (JsonProcessingException exp) { - throw new InvalidRequestException(Msg.code(2004) + "Unable to parse the observation resource json", exp); - } - } - private SearchRequest buildObservationResourceSearchRequest(Collection thePids) { - SearchRequest searchRequest = new SearchRequest(OBSERVATION_INDEX); - SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder(); - // Query - BoolQueryBuilder boolQueryBuilder = QueryBuilders.boolQuery(); - List pidParams = thePids.stream().map(Object::toString).collect(Collectors.toList()); - boolQueryBuilder.must(QueryBuilders.termsQuery(OBSERVATION_IDENTIFIER_FIELD_NAME, pidParams)); - searchSourceBuilder.query(boolQueryBuilder); - searchSourceBuilder.size(thePids.size()); - searchRequest.source(searchSourceBuilder); - return searchRequest; - } + List values = thePids.stream() + .map(Object::toString) + .map(v -> FieldValue.of(v)) + .collect(Collectors.toList()); - private IndexRequest createIndexRequest( - String theIndexName, String theDocumentId, String theObservationDocument, String theDocumentType) { - IndexRequest request = new IndexRequest(theIndexName); - request.id(theDocumentId); - request.source(theObservationDocument, XContentType.JSON); - return request; - } - - @Override - public void deleteObservationDocument(String theDocumentId) { - DeleteByQueryRequest deleteByQueryRequest = new DeleteByQueryRequest(OBSERVATION_INDEX); - deleteByQueryRequest.setQuery(QueryBuilders.termQuery(OBSERVATION_IDENTIFIER_FIELD_NAME, theDocumentId)); - try { - myRestHighLevelClient.deleteByQuery(deleteByQueryRequest, RequestOptions.DEFAULT); - } catch (IOException theE) { - throw new InvalidRequestException(Msg.code(1191) + "Unable to delete Observation " + theDocumentId); - } - } - - @VisibleForTesting - public void deleteAllDocumentsForTest(String theIndexName) throws IOException { - DeleteByQueryRequest deleteByQueryRequest = new DeleteByQueryRequest(theIndexName); - deleteByQueryRequest.setQuery(QueryBuilders.matchAllQuery()); - myRestHighLevelClient.deleteByQuery(deleteByQueryRequest, RequestOptions.DEFAULT); + return SearchRequest.of(sr -> sr.index(OBSERVATION_INDEX) + .query(qb -> qb.bool(bb -> bb.must(bbm -> { + bbm.terms(terms -> + terms.field(OBSERVATION_IDENTIFIER_FIELD_NAME).terms(termsb -> termsb.value(values))); + return bbm; + }))) + .size(thePids.size())); } @VisibleForTesting public void refreshIndex(String theIndexName) throws IOException { - myRestHighLevelClient.indices().refresh(new RefreshRequest(theIndexName), RequestOptions.DEFAULT); + myRestHighLevelClient.indices().refresh(fn -> fn.index(theIndexName)); } } diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/search/lastn/IElasticsearchSvc.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/search/lastn/IElasticsearchSvc.java index c6f8209fe0f..1ddf74ac6c0 100644 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/search/lastn/IElasticsearchSvc.java +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/search/lastn/IElasticsearchSvc.java @@ -19,10 +19,6 @@ */ package ca.uhn.fhir.jpa.search.lastn; -import ca.uhn.fhir.context.FhirContext; -import ca.uhn.fhir.jpa.search.lastn.json.CodeJson; -import ca.uhn.fhir.jpa.search.lastn.json.ObservationJson; -import ca.uhn.fhir.jpa.searchparam.SearchParameterMap; import ca.uhn.fhir.rest.api.server.storage.IResourcePersistentId; import org.hl7.fhir.instance.model.api.IBaseResource; @@ -32,59 +28,6 @@ import java.util.List; public interface IElasticsearchSvc { - /** - * Returns identifiers for the last most recent N observations that meet the specified criteria. - * - * @param theSearchParameterMap SearchParameterMap containing search parameters used for filtering the last N observations. Supported parameters include Subject, Patient, Code, Category and Max (the parameter used to determine N). - * @param theFhirContext Current FhirContext. - * @param theMaxResultsToFetch The maximum number of results to return for the purpose of paging. - * @return - */ - List executeLastN( - SearchParameterMap theSearchParameterMap, FhirContext theFhirContext, Integer theMaxResultsToFetch); - - /** - * Returns index document for a single Observation - * - * @param theDocumentID Identifier of Observation resource. - * @return - */ - ObservationJson getObservationDocument(String theDocumentID); - - /** - * Returns index document for a single Observation Code that either has a coding that matches a specified Code value and system or that has a specified text value. - * - * @param theCodeSystemHash A hash string constructed from a Code value and Code system used to match to an Observation Code. - * @param theText A text value used to match to an Observation Code. - * @return - */ - CodeJson getObservationCodeDocument(String theCodeSystemHash, String theText); - - /** - * Creates or updates index for an Observation Resource. - * - * @param theDocumentId Identifier for Observation resource. - * @param theObservationDocument Indexing document for Observation. - * @return True if Observation indexed successfully. - */ - Boolean createOrUpdateObservationIndex(String theDocumentId, ObservationJson theObservationDocument); - - /** - * Creates or updates index for an Observation Code. - * - * @param theCodeableConceptID Identifier for Observation resource. - * @param theObservationCodeDocument Indexing document for Observation. - * @return True if Observation Code indexed successfully. - */ - Boolean createOrUpdateObservationCodeIndex(String theCodeableConceptID, CodeJson theObservationCodeDocument); - - /** - * Deletes index for an Observation Resource. - * - * @param theDocumentId Identifier for Observation resource. - */ - void deleteObservationDocument(String theDocumentId); - /** * Invoked when shutting down. */ diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/search/reindex/IInstanceReindexService.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/search/reindex/IInstanceReindexService.java index c56932a5168..945158d6e50 100644 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/search/reindex/IInstanceReindexService.java +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/search/reindex/IInstanceReindexService.java @@ -20,11 +20,11 @@ package ca.uhn.fhir.jpa.search.reindex; import ca.uhn.fhir.rest.api.server.RequestDetails; +import jakarta.annotation.Nullable; import org.hl7.fhir.instance.model.api.IBaseParameters; import org.hl7.fhir.instance.model.api.IIdType; import java.util.Set; -import javax.annotation.Nullable; public interface IInstanceReindexService { diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/search/reindex/InstanceReindexServiceImpl.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/search/reindex/InstanceReindexServiceImpl.java index 574cda69677..8238bbf929b 100644 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/search/reindex/InstanceReindexServiceImpl.java +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/search/reindex/InstanceReindexServiceImpl.java @@ -46,6 +46,8 @@ import ca.uhn.fhir.rest.server.util.ResourceSearchParams; import ca.uhn.fhir.util.StopWatch; import ca.uhn.hapi.converters.canonical.VersionCanonicalizer; import com.google.common.annotations.VisibleForTesting; +import jakarta.annotation.Nonnull; +import jakarta.annotation.Nullable; import org.hl7.fhir.instance.model.api.IBaseParameters; import org.hl7.fhir.instance.model.api.IBaseResource; import org.hl7.fhir.instance.model.api.IIdType; @@ -66,8 +68,6 @@ import java.util.List; import java.util.Map; import java.util.Set; import java.util.stream.Collectors; -import javax.annotation.Nonnull; -import javax.annotation.Nullable; import static ca.uhn.fhir.jpa.dao.index.DaoSearchParamSynchronizer.subtract; import static java.util.Comparator.comparing; diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/search/reindex/ResourceReindexer.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/search/reindex/ResourceReindexer.java index ada96b93ad7..bd1de11154e 100644 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/search/reindex/ResourceReindexer.java +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/search/reindex/ResourceReindexer.java @@ -25,11 +25,9 @@ import ca.uhn.fhir.i18n.Msg; import ca.uhn.fhir.jpa.api.dao.DaoRegistry; import ca.uhn.fhir.jpa.api.dao.IFhirResourceDao; import ca.uhn.fhir.jpa.dao.IFulltextSearchSvc; -import ca.uhn.fhir.jpa.dao.data.IForcedIdDao; import ca.uhn.fhir.jpa.dao.data.IResourceHistoryTableDao; import ca.uhn.fhir.jpa.dao.data.IResourceTableDao; import ca.uhn.fhir.jpa.model.dao.JpaPid; -import ca.uhn.fhir.jpa.model.entity.ForcedId; import ca.uhn.fhir.jpa.model.entity.ResourceTable; import ca.uhn.fhir.rest.server.exceptions.InternalErrorException; import org.hl7.fhir.instance.model.api.IBaseResource; @@ -38,8 +36,6 @@ import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; -import static org.apache.commons.lang3.StringUtils.isBlank; - /** * @deprecated */ @@ -50,9 +46,6 @@ public class ResourceReindexer { @Autowired private IResourceHistoryTableDao myResourceHistoryTableDao; - @Autowired - private IForcedIdDao myForcedIdDao; - @Autowired private IResourceTableDao myResourceTableDao; @@ -75,21 +68,6 @@ public class ResourceReindexer { } public void reindexResourceEntity(ResourceTable theResourceTable) { - /* - * This part is because from HAPI 1.5 - 1.6 we changed the format of forced ID to be "type/id" instead of just "id" - */ - ForcedId forcedId = theResourceTable.getForcedId(); - if (forcedId != null) { - if (isBlank(forcedId.getResourceType())) { - ourLog.info( - "Updating resource {} forcedId type to {}", - forcedId.getForcedId(), - theResourceTable.getResourceType()); - forcedId.setResourceType(theResourceTable.getResourceType()); - myForcedIdDao.save(forcedId); - } - } - IFhirResourceDao dao = myDaoRegistry.getResourceDao(theResourceTable.getResourceType()); long expectedVersion = theResourceTable.getVersion(); IBaseResource resource = dao.readByPid(JpaPid.fromId(theResourceTable.getId()), true); diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/search/reindex/ResourceReindexingSvcImpl.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/search/reindex/ResourceReindexingSvcImpl.java index 38243681312..35c4f7244c8 100644 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/search/reindex/ResourceReindexingSvcImpl.java +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/search/reindex/ResourceReindexingSvcImpl.java @@ -25,7 +25,6 @@ import ca.uhn.fhir.i18n.Msg; import ca.uhn.fhir.jpa.api.config.JpaStorageSettings; import ca.uhn.fhir.jpa.api.dao.DaoRegistry; import ca.uhn.fhir.jpa.dao.BaseHapiFhirDao; -import ca.uhn.fhir.jpa.dao.data.IForcedIdDao; import ca.uhn.fhir.jpa.dao.data.IResourceReindexJobDao; import ca.uhn.fhir.jpa.dao.data.IResourceTableDao; import ca.uhn.fhir.jpa.entity.ResourceReindexJobEntity; @@ -40,6 +39,12 @@ import ca.uhn.fhir.rest.server.exceptions.ResourceVersionConflictException; import ca.uhn.fhir.rest.server.util.ISearchParamRegistry; import ca.uhn.fhir.util.StopWatch; import com.google.common.annotations.VisibleForTesting; +import jakarta.annotation.Nullable; +import jakarta.annotation.PostConstruct; +import jakarta.persistence.EntityManager; +import jakarta.persistence.PersistenceContext; +import jakarta.persistence.PersistenceContextType; +import jakarta.persistence.Query; import org.apache.commons.lang3.Validate; import org.apache.commons.lang3.concurrent.BasicThreadFactory; import org.apache.commons.lang3.time.DateUtils; @@ -70,12 +75,6 @@ import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicInteger; import java.util.concurrent.locks.ReentrantLock; import java.util.stream.Collectors; -import javax.annotation.Nullable; -import javax.annotation.PostConstruct; -import javax.persistence.EntityManager; -import javax.persistence.PersistenceContext; -import javax.persistence.PersistenceContextType; -import javax.persistence.Query; import static org.apache.commons.lang3.StringUtils.isNotBlank; @@ -111,9 +110,6 @@ public class ResourceReindexingSvcImpl implements IResourceReindexingSvc, IHasSc @Autowired private DaoRegistry myDaoRegistry; - @Autowired - private IForcedIdDao myForcedIdDao; - @Autowired private FhirContext myContext; diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/search/warm/CacheWarmingSvcImpl.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/search/warm/CacheWarmingSvcImpl.java index 1cbc2a5e69f..0e2305703b7 100644 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/search/warm/CacheWarmingSvcImpl.java +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/search/warm/CacheWarmingSvcImpl.java @@ -34,6 +34,7 @@ import ca.uhn.fhir.jpa.model.sched.ScheduledJobDefinition; import ca.uhn.fhir.jpa.searchparam.MatchUrlService; import ca.uhn.fhir.jpa.searchparam.SearchParameterMap; import ca.uhn.fhir.util.UrlUtil; +import jakarta.annotation.PostConstruct; import org.apache.commons.lang3.time.DateUtils; import org.quartz.JobExecutionContext; import org.slf4j.Logger; @@ -47,7 +48,6 @@ import java.util.LinkedHashMap; import java.util.List; import java.util.Map; import java.util.Set; -import javax.annotation.PostConstruct; @Component public class CacheWarmingSvcImpl implements ICacheWarmingSvc, IHasScheduledJobs { diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/subscription/ResourceModifiedMessagePersistenceSvcImpl.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/subscription/ResourceModifiedMessagePersistenceSvcImpl.java index 86e85a85c9b..893aa6e6744 100644 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/subscription/ResourceModifiedMessagePersistenceSvcImpl.java +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/subscription/ResourceModifiedMessagePersistenceSvcImpl.java @@ -35,6 +35,7 @@ import ca.uhn.fhir.jpa.subscription.async.AsyncResourceModifiedSubmitterSvc; import ca.uhn.fhir.jpa.subscription.model.ResourceModifiedMessage; import ca.uhn.fhir.model.primitive.IdDt; import ca.uhn.fhir.rest.api.server.SystemRequestDetails; +import ca.uhn.fhir.rest.server.exceptions.ResourceNotFoundException; import ca.uhn.fhir.subscription.api.IResourceModifiedMessagePersistenceSvc; import com.fasterxml.jackson.core.JsonProcessingException; import com.fasterxml.jackson.databind.ObjectMapper; @@ -45,6 +46,7 @@ import org.slf4j.LoggerFactory; import java.util.Date; import java.util.List; +import java.util.Optional; import static ca.uhn.fhir.jpa.model.entity.PersistedResourceModifiedMessageEntityPK.with; @@ -92,9 +94,43 @@ public class ResourceModifiedMessagePersistenceSvcImpl implements IResourceModif @Override public ResourceModifiedMessage inflatePersistedResourceModifiedMessage( - IPersistedResourceModifiedMessage thePersistedResourceModifiedMessage) { + ResourceModifiedMessage theResourceModifiedMessage) { - return inflateResourceModifiedMessageFromEntity((ResourceModifiedEntity) thePersistedResourceModifiedMessage); + return inflateResourceModifiedMessageFromEntity(createEntityFrom(theResourceModifiedMessage)); + } + + @Override + public Optional inflatePersistedResourceModifiedMessageOrNull( + ResourceModifiedMessage theResourceModifiedMessage) { + ResourceModifiedMessage inflatedResourceModifiedMessage = null; + + try { + inflatedResourceModifiedMessage = inflatePersistedResourceModifiedMessage(theResourceModifiedMessage); + } catch (ResourceNotFoundException e) { + IdDt idDt = new IdDt( + theResourceModifiedMessage.getPayloadType(myFhirContext), + theResourceModifiedMessage.getPayloadId(), + theResourceModifiedMessage.getPayloadVersion()); + + ourLog.warn("Scheduled submission will be ignored since resource {} cannot be found", idDt.getIdPart(), e); + } catch (Exception ex) { + ourLog.error("Unknown error encountered on inflation of resources.", ex); + } + + return Optional.ofNullable(inflatedResourceModifiedMessage); + } + + @Override + public ResourceModifiedMessage createResourceModifiedMessageFromEntityWithoutInflation( + IPersistedResourceModifiedMessage thePersistedResourceModifiedMessage) { + ResourceModifiedMessage resourceModifiedMessage = getPayloadLessMessageFromString( + ((ResourceModifiedEntity) thePersistedResourceModifiedMessage).getSummaryResourceModifiedMessage()); + + IdDt resourceId = + createIdDtFromResourceModifiedEntity((ResourceModifiedEntity) thePersistedResourceModifiedMessage); + resourceModifiedMessage.setPayloadId(resourceId); + + return resourceModifiedMessage; } @Override @@ -112,17 +148,13 @@ public class ResourceModifiedMessagePersistenceSvcImpl implements IResourceModif protected ResourceModifiedMessage inflateResourceModifiedMessageFromEntity( ResourceModifiedEntity theResourceModifiedEntity) { - String resourcePid = - theResourceModifiedEntity.getResourceModifiedEntityPK().getResourcePid(); - String resourceVersion = - theResourceModifiedEntity.getResourceModifiedEntityPK().getResourceVersion(); String resourceType = theResourceModifiedEntity.getResourceType(); ResourceModifiedMessage retVal = getPayloadLessMessageFromString(theResourceModifiedEntity.getSummaryResourceModifiedMessage()); SystemRequestDetails systemRequestDetails = new SystemRequestDetails().setRequestPartitionId(retVal.getPartitionId()); - IdDt resourceIdDt = new IdDt(resourceType, resourcePid, resourceVersion); + IdDt resourceIdDt = createIdDtFromResourceModifiedEntity(theResourceModifiedEntity); IFhirResourceDao dao = myDaoRegistry.getResourceDao(resourceType); IBaseResource iBaseResource = dao.read(resourceIdDt, systemRequestDetails, true); @@ -164,6 +196,16 @@ public class ResourceModifiedMessagePersistenceSvcImpl implements IResourceModif } } + private IdDt createIdDtFromResourceModifiedEntity(ResourceModifiedEntity theResourceModifiedEntity) { + String resourcePid = + theResourceModifiedEntity.getResourceModifiedEntityPK().getResourcePid(); + String resourceVersion = + theResourceModifiedEntity.getResourceModifiedEntityPK().getResourceVersion(); + String resourceType = theResourceModifiedEntity.getResourceType(); + + return new IdDt(resourceType, resourcePid, resourceVersion); + } + private static class PayloadLessResourceModifiedMessage extends ResourceModifiedMessage { public PayloadLessResourceModifiedMessage(ResourceModifiedMessage theMsg) { diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/term/ExpansionFilter.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/term/ExpansionFilter.java index f6f11baa8c8..70b6fe0aabb 100644 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/term/ExpansionFilter.java +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/term/ExpansionFilter.java @@ -21,13 +21,13 @@ package ca.uhn.fhir.jpa.term; import ca.uhn.fhir.jpa.model.util.JpaConstants; import ca.uhn.fhir.util.FhirVersionIndependentConcept; +import jakarta.annotation.Nonnull; +import jakarta.annotation.Nullable; import org.apache.commons.lang3.Validate; import org.hl7.fhir.r4.model.ValueSet; import java.util.Collections; import java.util.List; -import javax.annotation.Nonnull; -import javax.annotation.Nullable; import static org.apache.commons.lang3.StringUtils.isNoneBlank; import static org.apache.commons.lang3.StringUtils.isNotBlank; diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/term/IValueSetConceptAccumulator.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/term/IValueSetConceptAccumulator.java index f72f70c33ed..0a7e8faf046 100644 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/term/IValueSetConceptAccumulator.java +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/term/IValueSetConceptAccumulator.java @@ -20,9 +20,9 @@ package ca.uhn.fhir.jpa.term; import ca.uhn.fhir.jpa.entity.TermConceptDesignation; +import jakarta.annotation.Nullable; import java.util.Collection; -import javax.annotation.Nullable; public interface IValueSetConceptAccumulator { diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/term/TermCodeSystemStorageSvcImpl.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/term/TermCodeSystemStorageSvcImpl.java index fe35f7c8d4d..9aabe85cd9b 100644 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/term/TermCodeSystemStorageSvcImpl.java +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/term/TermCodeSystemStorageSvcImpl.java @@ -53,6 +53,10 @@ import ca.uhn.fhir.rest.server.exceptions.InvalidRequestException; import ca.uhn.fhir.rest.server.exceptions.UnprocessableEntityException; import ca.uhn.fhir.util.ObjectUtil; import ca.uhn.fhir.util.ValidateUtil; +import jakarta.annotation.Nonnull; +import jakarta.persistence.EntityManager; +import jakarta.persistence.PersistenceContext; +import jakarta.persistence.PersistenceContextType; import org.apache.commons.lang3.Validate; import org.hl7.fhir.instance.model.api.IIdType; import org.hl7.fhir.r4.model.CodeSystem; @@ -81,10 +85,6 @@ import java.util.Set; import java.util.UUID; import java.util.concurrent.atomic.AtomicInteger; import java.util.stream.Collectors; -import javax.annotation.Nonnull; -import javax.persistence.EntityManager; -import javax.persistence.PersistenceContext; -import javax.persistence.PersistenceContextType; import static ca.uhn.fhir.jpa.api.dao.IDao.RESOURCE_PID_KEY; import static org.apache.commons.lang3.StringUtils.isBlank; diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/term/TermConceptClientMappingSvcImpl.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/term/TermConceptClientMappingSvcImpl.java new file mode 100644 index 00000000000..63c6d2bc328 --- /dev/null +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/term/TermConceptClientMappingSvcImpl.java @@ -0,0 +1,439 @@ +/*- + * #%L + * HAPI FHIR JPA Server + * %% + * Copyright (C) 2014 - 2023 Smile CDR, Inc. + * %% + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * #L% + */ +package ca.uhn.fhir.jpa.term; + +import ca.uhn.fhir.context.FhirContext; +import ca.uhn.fhir.context.support.TranslateConceptResult; +import ca.uhn.fhir.context.support.TranslateConceptResults; +import ca.uhn.fhir.i18n.Msg; +import ca.uhn.fhir.interceptor.model.RequestPartitionId; +import ca.uhn.fhir.jpa.api.model.TranslationQuery; +import ca.uhn.fhir.jpa.api.model.TranslationRequest; +import ca.uhn.fhir.jpa.api.svc.IIdHelperService; +import ca.uhn.fhir.jpa.dao.data.ITermConceptMapDao; +import ca.uhn.fhir.jpa.entity.TermConceptMap; +import ca.uhn.fhir.jpa.entity.TermConceptMapGroup; +import ca.uhn.fhir.jpa.entity.TermConceptMapGroupElement; +import ca.uhn.fhir.jpa.entity.TermConceptMapGroupElementTarget; +import ca.uhn.fhir.jpa.model.dao.JpaPid; +import ca.uhn.fhir.jpa.term.api.ITermConceptClientMappingSvc; +import ca.uhn.fhir.jpa.util.MemoryCacheService; +import ca.uhn.fhir.jpa.util.ScrollableResultsIterator; +import ca.uhn.fhir.rest.server.exceptions.InvalidRequestException; +import jakarta.persistence.EntityManager; +import jakarta.persistence.PersistenceContext; +import jakarta.persistence.PersistenceContextType; +import jakarta.persistence.TypedQuery; +import jakarta.persistence.criteria.CriteriaBuilder; +import jakarta.persistence.criteria.CriteriaQuery; +import jakarta.persistence.criteria.Join; +import jakarta.persistence.criteria.Predicate; +import jakarta.persistence.criteria.Root; +import org.apache.commons.lang3.StringUtils; +import org.hibernate.ScrollMode; +import org.hibernate.ScrollableResults; +import org.hl7.fhir.instance.model.api.IIdType; +import org.hl7.fhir.r4.model.Coding; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.data.domain.PageRequest; +import org.springframework.data.domain.Pageable; +import org.springframework.transaction.annotation.Propagation; +import org.springframework.transaction.annotation.Transactional; + +import java.util.ArrayList; +import java.util.HashSet; +import java.util.List; +import java.util.Set; + +import static org.apache.commons.lang3.StringUtils.isBlank; +import static org.apache.commons.lang3.StringUtils.isNotBlank; + +public class TermConceptClientMappingSvcImpl implements ITermConceptClientMappingSvc { + private static final Logger ourLog = LoggerFactory.getLogger(TermConceptClientMappingSvcImpl.class); + + private final int myFetchSize = TermReadSvcImpl.DEFAULT_FETCH_SIZE; + + protected static boolean ourLastResultsFromTranslationCache; // For testing. + protected static boolean ourLastResultsFromTranslationWithReverseCache; // For testing. + + @PersistenceContext(type = PersistenceContextType.TRANSACTION) + protected EntityManager myEntityManager; + + @Autowired + protected FhirContext myContext; + + @Autowired + protected MemoryCacheService myMemoryCacheService; + + @Autowired + protected IIdHelperService myIdHelperService; + + @Autowired + protected ITermConceptMapDao myConceptMapDao; + + @Override + @Transactional(propagation = Propagation.REQUIRED) + public TranslateConceptResults translate(TranslationRequest theTranslationRequest) { + TranslateConceptResults retVal = new TranslateConceptResults(); + + CriteriaBuilder criteriaBuilder = myEntityManager.getCriteriaBuilder(); + CriteriaQuery query = + criteriaBuilder.createQuery(TermConceptMapGroupElementTarget.class); + Root root = query.from(TermConceptMapGroupElementTarget.class); + + Join elementJoin = + root.join("myConceptMapGroupElement"); + Join groupJoin = elementJoin.join("myConceptMapGroup"); + Join conceptMapJoin = groupJoin.join("myConceptMap"); + + List translationQueries = theTranslationRequest.getTranslationQueries(); + List cachedTargets; + ArrayList predicates; + Coding coding; + + // -- get the latest ConceptMapVersion if theTranslationRequest has ConceptMap url but no ConceptMap version + String latestConceptMapVersion = null; + if (theTranslationRequest.hasUrl() && !theTranslationRequest.hasConceptMapVersion()) + latestConceptMapVersion = getLatestConceptMapVersion(theTranslationRequest); + + for (TranslationQuery translationQuery : translationQueries) { + cachedTargets = myMemoryCacheService.getIfPresent( + MemoryCacheService.CacheEnum.CONCEPT_TRANSLATION, translationQuery); + if (cachedTargets == null) { + final List targets = new ArrayList<>(); + + predicates = new ArrayList<>(); + + coding = translationQuery.getCoding(); + if (coding.hasCode()) { + predicates.add(criteriaBuilder.equal(elementJoin.get("myCode"), coding.getCode())); + } else { + throw new InvalidRequestException( + Msg.code(842) + "A code must be provided for translation to occur."); + } + + if (coding.hasSystem()) { + predicates.add(criteriaBuilder.equal(groupJoin.get("mySource"), coding.getSystem())); + } + + if (coding.hasVersion()) { + predicates.add(criteriaBuilder.equal(groupJoin.get("mySourceVersion"), coding.getVersion())); + } + + if (translationQuery.hasTargetSystem()) { + predicates.add( + criteriaBuilder.equal(groupJoin.get("myTarget"), translationQuery.getTargetSystem())); + } + + if (translationQuery.hasUrl()) { + predicates.add(criteriaBuilder.equal(conceptMapJoin.get("myUrl"), translationQuery.getUrl())); + if (translationQuery.hasConceptMapVersion()) { + // both url and conceptMapVersion + predicates.add(criteriaBuilder.equal( + conceptMapJoin.get("myVersion"), translationQuery.getConceptMapVersion())); + } else { + if (StringUtils.isNotBlank(latestConceptMapVersion)) { + // only url and use latestConceptMapVersion + predicates.add( + criteriaBuilder.equal(conceptMapJoin.get("myVersion"), latestConceptMapVersion)); + } else { + predicates.add(criteriaBuilder.isNull(conceptMapJoin.get("myVersion"))); + } + } + } + + if (translationQuery.hasSource()) { + predicates.add(criteriaBuilder.equal(conceptMapJoin.get("mySource"), translationQuery.getSource())); + } + + if (translationQuery.hasTarget()) { + predicates.add(criteriaBuilder.equal(conceptMapJoin.get("myTarget"), translationQuery.getTarget())); + } + + if (translationQuery.hasResourceId()) { + IIdType resourceId = translationQuery.getResourceId(); + JpaPid resourcePid = + myIdHelperService.getPidOrThrowException(RequestPartitionId.defaultPartition(), resourceId); + predicates.add(criteriaBuilder.equal(conceptMapJoin.get("myResourcePid"), resourcePid.getId())); + } + + Predicate outerPredicate = criteriaBuilder.and(predicates.toArray(new Predicate[0])); + query.where(outerPredicate); + + // Use scrollable results. + final TypedQuery typedQuery = + myEntityManager.createQuery(query.select(root)); + org.hibernate.query.Query hibernateQuery = + (org.hibernate.query.Query) typedQuery; + hibernateQuery.setFetchSize(myFetchSize); + ScrollableResults scrollableResults = hibernateQuery.scroll(ScrollMode.FORWARD_ONLY); + try (ScrollableResultsIterator scrollableResultsIterator = + new ScrollableResultsIterator<>(scrollableResults)) { + + Set matches = new HashSet<>(); + while (scrollableResultsIterator.hasNext()) { + TermConceptMapGroupElementTarget next = scrollableResultsIterator.next(); + if (matches.add(next)) { + + TranslateConceptResult translationMatch = new TranslateConceptResult(); + if (next.getEquivalence() != null) { + translationMatch.setEquivalence( + next.getEquivalence().toCode()); + } + + translationMatch.setCode(next.getCode()); + translationMatch.setSystem(next.getSystem()); + translationMatch.setSystemVersion(next.getSystemVersion()); + translationMatch.setDisplay(next.getDisplay()); + translationMatch.setValueSet(next.getValueSet()); + translationMatch.setSystemVersion(next.getSystemVersion()); + translationMatch.setConceptMapUrl(next.getConceptMapUrl()); + + targets.add(translationMatch); + } + } + } + + ourLastResultsFromTranslationCache = false; // For testing. + myMemoryCacheService.put(MemoryCacheService.CacheEnum.CONCEPT_TRANSLATION, translationQuery, targets); + retVal.getResults().addAll(targets); + } else { + ourLastResultsFromTranslationCache = true; // For testing. + retVal.getResults().addAll(cachedTargets); + } + } + + buildTranslationResult(retVal); + return retVal; + } + + @Override + @Transactional(propagation = Propagation.REQUIRED) + public TranslateConceptResults translateWithReverse(TranslationRequest theTranslationRequest) { + TranslateConceptResults retVal = new TranslateConceptResults(); + + CriteriaBuilder criteriaBuilder = myEntityManager.getCriteriaBuilder(); + CriteriaQuery query = criteriaBuilder.createQuery(TermConceptMapGroupElement.class); + Root root = query.from(TermConceptMapGroupElement.class); + + Join targetJoin = + root.join("myConceptMapGroupElementTargets"); + Join groupJoin = root.join("myConceptMapGroup"); + Join conceptMapJoin = groupJoin.join("myConceptMap"); + + List translationQueries = theTranslationRequest.getTranslationQueries(); + List cachedElements; + ArrayList predicates; + Coding coding; + + // -- get the latest ConceptMapVersion if theTranslationRequest has ConceptMap url but no ConceptMap version + String latestConceptMapVersion = null; + if (theTranslationRequest.hasUrl() && !theTranslationRequest.hasConceptMapVersion()) + latestConceptMapVersion = getLatestConceptMapVersion(theTranslationRequest); + + for (TranslationQuery translationQuery : translationQueries) { + cachedElements = myMemoryCacheService.getIfPresent( + MemoryCacheService.CacheEnum.CONCEPT_TRANSLATION_REVERSE, translationQuery); + if (cachedElements == null) { + final List elements = new ArrayList<>(); + + predicates = new ArrayList<>(); + + coding = translationQuery.getCoding(); + String targetCode; + String targetCodeSystem = null; + if (coding.hasCode()) { + predicates.add(criteriaBuilder.equal(targetJoin.get("myCode"), coding.getCode())); + targetCode = coding.getCode(); + } else { + throw new InvalidRequestException( + Msg.code(843) + "A code must be provided for translation to occur."); + } + + if (coding.hasSystem()) { + predicates.add(criteriaBuilder.equal(groupJoin.get("myTarget"), coding.getSystem())); + targetCodeSystem = coding.getSystem(); + } + + if (coding.hasVersion()) { + predicates.add(criteriaBuilder.equal(groupJoin.get("myTargetVersion"), coding.getVersion())); + } + + if (translationQuery.hasUrl()) { + predicates.add(criteriaBuilder.equal(conceptMapJoin.get("myUrl"), translationQuery.getUrl())); + if (translationQuery.hasConceptMapVersion()) { + // both url and conceptMapVersion + predicates.add(criteriaBuilder.equal( + conceptMapJoin.get("myVersion"), translationQuery.getConceptMapVersion())); + } else { + if (StringUtils.isNotBlank(latestConceptMapVersion)) { + // only url and use latestConceptMapVersion + predicates.add( + criteriaBuilder.equal(conceptMapJoin.get("myVersion"), latestConceptMapVersion)); + } else { + predicates.add(criteriaBuilder.isNull(conceptMapJoin.get("myVersion"))); + } + } + } + + if (translationQuery.hasTargetSystem()) { + predicates.add( + criteriaBuilder.equal(groupJoin.get("mySource"), translationQuery.getTargetSystem())); + } + + if (translationQuery.hasSource()) { + predicates.add(criteriaBuilder.equal(conceptMapJoin.get("myTarget"), translationQuery.getSource())); + } + + if (translationQuery.hasTarget()) { + predicates.add(criteriaBuilder.equal(conceptMapJoin.get("mySource"), translationQuery.getTarget())); + } + + if (translationQuery.hasResourceId()) { + IIdType resourceId = translationQuery.getResourceId(); + JpaPid resourcePid = + myIdHelperService.getPidOrThrowException(RequestPartitionId.defaultPartition(), resourceId); + predicates.add(criteriaBuilder.equal(conceptMapJoin.get("myResourcePid"), resourcePid.getId())); + } + + Predicate outerPredicate = criteriaBuilder.and(predicates.toArray(new Predicate[0])); + query.where(outerPredicate); + + // Use scrollable results. + final TypedQuery typedQuery = + myEntityManager.createQuery(query.select(root)); + org.hibernate.query.Query hibernateQuery = + (org.hibernate.query.Query) typedQuery; + hibernateQuery.setFetchSize(myFetchSize); + ScrollableResults scrollableResults = hibernateQuery.scroll(ScrollMode.FORWARD_ONLY); + try (ScrollableResultsIterator scrollableResultsIterator = + new ScrollableResultsIterator<>(scrollableResults)) { + + Set matches = new HashSet<>(); + while (scrollableResultsIterator.hasNext()) { + TermConceptMapGroupElement nextElement = scrollableResultsIterator.next(); + + /* TODO: The invocation of the size() below does not seem to be necessary but for some reason, + * but removing it causes tests in TerminologySvcImplR4Test to fail. We use the outcome + * in a trace log to avoid ErrorProne flagging an unused return value. + */ + int size = + nextElement.getConceptMapGroupElementTargets().size(); + ourLog.trace("Have {} targets", size); + + myEntityManager.detach(nextElement); + + if (isNotBlank(targetCode)) { + for (TermConceptMapGroupElementTarget next : + nextElement.getConceptMapGroupElementTargets()) { + if (matches.add(next)) { + if (isBlank(targetCodeSystem) + || StringUtils.equals(targetCodeSystem, next.getSystem())) { + if (StringUtils.equals(targetCode, next.getCode())) { + TranslateConceptResult translationMatch = new TranslateConceptResult(); + translationMatch.setCode(nextElement.getCode()); + translationMatch.setSystem(nextElement.getSystem()); + translationMatch.setSystemVersion(nextElement.getSystemVersion()); + translationMatch.setDisplay(nextElement.getDisplay()); + translationMatch.setValueSet(nextElement.getValueSet()); + translationMatch.setSystemVersion(nextElement.getSystemVersion()); + translationMatch.setConceptMapUrl(nextElement.getConceptMapUrl()); + if (next.getEquivalence() != null) { + translationMatch.setEquivalence( + next.getEquivalence().toCode()); + } + + if (alreadyContainsMapping(elements, translationMatch) + || alreadyContainsMapping(retVal.getResults(), translationMatch)) { + continue; + } + + elements.add(translationMatch); + } + } + } + } + } + } + } + + ourLastResultsFromTranslationWithReverseCache = false; // For testing. + myMemoryCacheService.put( + MemoryCacheService.CacheEnum.CONCEPT_TRANSLATION_REVERSE, translationQuery, elements); + retVal.getResults().addAll(elements); + } else { + ourLastResultsFromTranslationWithReverseCache = true; // For testing. + retVal.getResults().addAll(cachedElements); + } + } + + buildTranslationResult(retVal); + return retVal; + } + + @Override + public FhirContext getFhirContext() { + return myContext; + } + + // Special case for the translate operation with url and without + // conceptMapVersion, find the latest conecptMapVersion + private String getLatestConceptMapVersion(TranslationRequest theTranslationRequest) { + + Pageable page = PageRequest.of(0, 1); + List theConceptMapList = myConceptMapDao.getTermConceptMapEntitiesByUrlOrderByMostRecentUpdate( + page, theTranslationRequest.getUrl()); + if (!theConceptMapList.isEmpty()) { + return theConceptMapList.get(0).getVersion(); + } + + return null; + } + + private void buildTranslationResult(TranslateConceptResults theTranslationResult) { + + String msg; + if (theTranslationResult.getResults().isEmpty()) { + theTranslationResult.setResult(false); + msg = myContext.getLocalizer().getMessage(TermConceptMappingSvcImpl.class, "noMatchesFound"); + theTranslationResult.setMessage(msg); + } else { + theTranslationResult.setResult(true); + msg = myContext.getLocalizer().getMessage(TermConceptMappingSvcImpl.class, "matchesFound"); + theTranslationResult.setMessage(msg); + } + } + + private boolean alreadyContainsMapping( + List elements, TranslateConceptResult translationMatch) { + for (TranslateConceptResult nextExistingElement : elements) { + if (StringUtils.equals(nextExistingElement.getSystem(), translationMatch.getSystem())) { + if (StringUtils.equals(nextExistingElement.getSystemVersion(), translationMatch.getSystemVersion())) { + if (StringUtils.equals(nextExistingElement.getCode(), translationMatch.getCode())) { + return true; + } + } + } + } + return false; + } +} diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/term/TermConceptMappingSvcImpl.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/term/TermConceptMappingSvcImpl.java index d4c7928b62a..603837aada6 100644 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/term/TermConceptMappingSvcImpl.java +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/term/TermConceptMappingSvcImpl.java @@ -19,15 +19,10 @@ */ package ca.uhn.fhir.jpa.term; -import ca.uhn.fhir.context.FhirContext; import ca.uhn.fhir.context.support.TranslateConceptResult; import ca.uhn.fhir.context.support.TranslateConceptResults; import ca.uhn.fhir.i18n.Msg; -import ca.uhn.fhir.interceptor.model.RequestPartitionId; -import ca.uhn.fhir.jpa.api.model.TranslationQuery; import ca.uhn.fhir.jpa.api.model.TranslationRequest; -import ca.uhn.fhir.jpa.api.svc.IIdHelperService; -import ca.uhn.fhir.jpa.dao.data.ITermConceptMapDao; import ca.uhn.fhir.jpa.dao.data.ITermConceptMapGroupDao; import ca.uhn.fhir.jpa.dao.data.ITermConceptMapGroupElementDao; import ca.uhn.fhir.jpa.dao.data.ITermConceptMapGroupElementTargetDao; @@ -35,21 +30,13 @@ import ca.uhn.fhir.jpa.entity.TermConceptMap; import ca.uhn.fhir.jpa.entity.TermConceptMapGroup; import ca.uhn.fhir.jpa.entity.TermConceptMapGroupElement; import ca.uhn.fhir.jpa.entity.TermConceptMapGroupElementTarget; -import ca.uhn.fhir.jpa.model.dao.JpaPid; import ca.uhn.fhir.jpa.model.entity.ResourceTable; import ca.uhn.fhir.jpa.term.api.ITermConceptMappingSvc; -import ca.uhn.fhir.jpa.util.MemoryCacheService; -import ca.uhn.fhir.jpa.util.ScrollableResultsIterator; import ca.uhn.fhir.rest.server.exceptions.InternalErrorException; -import ca.uhn.fhir.rest.server.exceptions.InvalidRequestException; import ca.uhn.fhir.rest.server.exceptions.UnprocessableEntityException; import ca.uhn.fhir.util.ValidateUtil; import com.google.common.annotations.VisibleForTesting; -import org.apache.commons.lang3.StringUtils; -import org.hibernate.ScrollMode; -import org.hibernate.ScrollableResults; import org.hl7.fhir.exceptions.FHIRException; -import org.hl7.fhir.instance.model.api.IIdType; import org.hl7.fhir.r4.model.BooleanType; import org.hl7.fhir.r4.model.CodeType; import org.hl7.fhir.r4.model.Coding; @@ -61,39 +48,17 @@ import org.hl7.fhir.r4.model.UriType; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.data.domain.PageRequest; -import org.springframework.data.domain.Pageable; -import org.springframework.transaction.annotation.Propagation; import org.springframework.transaction.annotation.Transactional; -import java.util.ArrayList; -import java.util.HashSet; -import java.util.List; import java.util.Optional; -import java.util.Set; -import javax.persistence.EntityManager; -import javax.persistence.PersistenceContext; -import javax.persistence.PersistenceContextType; -import javax.persistence.TypedQuery; -import javax.persistence.criteria.CriteriaBuilder; -import javax.persistence.criteria.CriteriaQuery; -import javax.persistence.criteria.Join; -import javax.persistence.criteria.Predicate; -import javax.persistence.criteria.Root; import static ca.uhn.fhir.jpa.term.TermReadSvcImpl.isPlaceholder; import static org.apache.commons.lang3.StringUtils.isBlank; import static org.apache.commons.lang3.StringUtils.isNotBlank; -public class TermConceptMappingSvcImpl implements ITermConceptMappingSvc { +public class TermConceptMappingSvcImpl extends TermConceptClientMappingSvcImpl implements ITermConceptMappingSvc { private static final Logger ourLog = LoggerFactory.getLogger(TermConceptMappingSvcImpl.class); - private static boolean ourLastResultsFromTranslationCache; // For testing. - private static boolean ourLastResultsFromTranslationWithReverseCache; // For testing. - private final int myFetchSize = TermReadSvcImpl.DEFAULT_FETCH_SIZE; - - @Autowired - protected ITermConceptMapDao myConceptMapDao; @Autowired protected ITermConceptMapGroupDao myConceptMapGroupDao; @@ -104,17 +69,10 @@ public class TermConceptMappingSvcImpl implements ITermConceptMappingSvc { @Autowired protected ITermConceptMapGroupElementTargetDao myConceptMapGroupElementTargetDao; - @PersistenceContext(type = PersistenceContextType.TRANSACTION) - protected EntityManager myEntityManager; - - @Autowired - private FhirContext myContext; - - @Autowired - private MemoryCacheService myMemoryCacheService; - - @Autowired - private IIdHelperService myIdHelperService; + @Override + public String getName() { + return getFhirContext().getVersion().getVersion() + " ConceptMap Validation Support"; + } @Override @Transactional @@ -122,11 +80,6 @@ public class TermConceptMappingSvcImpl implements ITermConceptMappingSvc { deleteConceptMap(theResourceTable); } - @Override - public FhirContext getFhirContext() { - return myContext; - } - @Override @Transactional public TranslateConceptResults translateConcept(TranslateCodeRequest theRequest) { @@ -205,7 +158,7 @@ public class TermConceptMappingSvcImpl implements ITermConceptMappingSvc { optionalExistingTermConceptMapByUrl = myConceptMapDao.findTermConceptMapByUrlAndVersion(conceptMapUrl, conceptMapVersion); } - if (!optionalExistingTermConceptMapByUrl.isPresent()) { + if (optionalExistingTermConceptMapByUrl.isEmpty()) { try { if (isNotBlank(source)) { termConceptMap.setSource(source); @@ -328,320 +281,6 @@ public class TermConceptMappingSvcImpl implements ITermConceptMappingSvc { theConceptMap.getIdElement().toVersionless().getValueAsString()); } - @Override - @Transactional(propagation = Propagation.REQUIRED) - public TranslateConceptResults translate(TranslationRequest theTranslationRequest) { - TranslateConceptResults retVal = new TranslateConceptResults(); - - CriteriaBuilder criteriaBuilder = myEntityManager.getCriteriaBuilder(); - CriteriaQuery query = - criteriaBuilder.createQuery(TermConceptMapGroupElementTarget.class); - Root root = query.from(TermConceptMapGroupElementTarget.class); - - Join elementJoin = - root.join("myConceptMapGroupElement"); - Join groupJoin = elementJoin.join("myConceptMapGroup"); - Join conceptMapJoin = groupJoin.join("myConceptMap"); - - List translationQueries = theTranslationRequest.getTranslationQueries(); - List cachedTargets; - ArrayList predicates; - Coding coding; - - // -- get the latest ConceptMapVersion if theTranslationRequest has ConceptMap url but no ConceptMap version - String latestConceptMapVersion = null; - if (theTranslationRequest.hasUrl() && !theTranslationRequest.hasConceptMapVersion()) - latestConceptMapVersion = getLatestConceptMapVersion(theTranslationRequest); - - for (TranslationQuery translationQuery : translationQueries) { - cachedTargets = myMemoryCacheService.getIfPresent( - MemoryCacheService.CacheEnum.CONCEPT_TRANSLATION, translationQuery); - if (cachedTargets == null) { - final List targets = new ArrayList<>(); - - predicates = new ArrayList<>(); - - coding = translationQuery.getCoding(); - if (coding.hasCode()) { - predicates.add(criteriaBuilder.equal(elementJoin.get("myCode"), coding.getCode())); - } else { - throw new InvalidRequestException( - Msg.code(842) + "A code must be provided for translation to occur."); - } - - if (coding.hasSystem()) { - predicates.add(criteriaBuilder.equal(groupJoin.get("mySource"), coding.getSystem())); - } - - if (coding.hasVersion()) { - predicates.add(criteriaBuilder.equal(groupJoin.get("mySourceVersion"), coding.getVersion())); - } - - if (translationQuery.hasTargetSystem()) { - predicates.add( - criteriaBuilder.equal(groupJoin.get("myTarget"), translationQuery.getTargetSystem())); - } - - if (translationQuery.hasUrl()) { - predicates.add(criteriaBuilder.equal(conceptMapJoin.get("myUrl"), translationQuery.getUrl())); - if (translationQuery.hasConceptMapVersion()) { - // both url and conceptMapVersion - predicates.add(criteriaBuilder.equal( - conceptMapJoin.get("myVersion"), translationQuery.getConceptMapVersion())); - } else { - if (StringUtils.isNotBlank(latestConceptMapVersion)) { - // only url and use latestConceptMapVersion - predicates.add( - criteriaBuilder.equal(conceptMapJoin.get("myVersion"), latestConceptMapVersion)); - } else { - predicates.add(criteriaBuilder.isNull(conceptMapJoin.get("myVersion"))); - } - } - } - - if (translationQuery.hasSource()) { - predicates.add(criteriaBuilder.equal(conceptMapJoin.get("mySource"), translationQuery.getSource())); - } - - if (translationQuery.hasTarget()) { - predicates.add(criteriaBuilder.equal(conceptMapJoin.get("myTarget"), translationQuery.getTarget())); - } - - if (translationQuery.hasResourceId()) { - IIdType resourceId = translationQuery.getResourceId(); - JpaPid resourcePid = - myIdHelperService.getPidOrThrowException(RequestPartitionId.defaultPartition(), resourceId); - predicates.add(criteriaBuilder.equal(conceptMapJoin.get("myResourcePid"), resourcePid.getId())); - } - - Predicate outerPredicate = criteriaBuilder.and(predicates.toArray(new Predicate[0])); - query.where(outerPredicate); - - // Use scrollable results. - final TypedQuery typedQuery = - myEntityManager.createQuery(query.select(root)); - org.hibernate.query.Query hibernateQuery = - (org.hibernate.query.Query) typedQuery; - hibernateQuery.setFetchSize(myFetchSize); - ScrollableResults scrollableResults = hibernateQuery.scroll(ScrollMode.FORWARD_ONLY); - try (ScrollableResultsIterator scrollableResultsIterator = - new ScrollableResultsIterator<>(scrollableResults)) { - - Set matches = new HashSet<>(); - while (scrollableResultsIterator.hasNext()) { - TermConceptMapGroupElementTarget next = scrollableResultsIterator.next(); - if (matches.add(next)) { - - TranslateConceptResult translationMatch = new TranslateConceptResult(); - if (next.getEquivalence() != null) { - translationMatch.setEquivalence( - next.getEquivalence().toCode()); - } - - translationMatch.setCode(next.getCode()); - translationMatch.setSystem(next.getSystem()); - translationMatch.setSystemVersion(next.getSystemVersion()); - translationMatch.setDisplay(next.getDisplay()); - translationMatch.setValueSet(next.getValueSet()); - translationMatch.setSystemVersion(next.getSystemVersion()); - translationMatch.setConceptMapUrl(next.getConceptMapUrl()); - - targets.add(translationMatch); - } - } - } - - ourLastResultsFromTranslationCache = false; // For testing. - myMemoryCacheService.put(MemoryCacheService.CacheEnum.CONCEPT_TRANSLATION, translationQuery, targets); - retVal.getResults().addAll(targets); - } else { - ourLastResultsFromTranslationCache = true; // For testing. - retVal.getResults().addAll(cachedTargets); - } - } - - buildTranslationResult(retVal); - return retVal; - } - - @Override - @Transactional(propagation = Propagation.REQUIRED) - public TranslateConceptResults translateWithReverse(TranslationRequest theTranslationRequest) { - TranslateConceptResults retVal = new TranslateConceptResults(); - - CriteriaBuilder criteriaBuilder = myEntityManager.getCriteriaBuilder(); - CriteriaQuery query = criteriaBuilder.createQuery(TermConceptMapGroupElement.class); - Root root = query.from(TermConceptMapGroupElement.class); - - Join targetJoin = - root.join("myConceptMapGroupElementTargets"); - Join groupJoin = root.join("myConceptMapGroup"); - Join conceptMapJoin = groupJoin.join("myConceptMap"); - - List translationQueries = theTranslationRequest.getTranslationQueries(); - List cachedElements; - ArrayList predicates; - Coding coding; - - // -- get the latest ConceptMapVersion if theTranslationRequest has ConceptMap url but no ConceptMap version - String latestConceptMapVersion = null; - if (theTranslationRequest.hasUrl() && !theTranslationRequest.hasConceptMapVersion()) - latestConceptMapVersion = getLatestConceptMapVersion(theTranslationRequest); - - for (TranslationQuery translationQuery : translationQueries) { - cachedElements = myMemoryCacheService.getIfPresent( - MemoryCacheService.CacheEnum.CONCEPT_TRANSLATION_REVERSE, translationQuery); - if (cachedElements == null) { - final List elements = new ArrayList<>(); - - predicates = new ArrayList<>(); - - coding = translationQuery.getCoding(); - String targetCode; - String targetCodeSystem = null; - if (coding.hasCode()) { - predicates.add(criteriaBuilder.equal(targetJoin.get("myCode"), coding.getCode())); - targetCode = coding.getCode(); - } else { - throw new InvalidRequestException( - Msg.code(843) + "A code must be provided for translation to occur."); - } - - if (coding.hasSystem()) { - predicates.add(criteriaBuilder.equal(groupJoin.get("myTarget"), coding.getSystem())); - targetCodeSystem = coding.getSystem(); - } - - if (coding.hasVersion()) { - predicates.add(criteriaBuilder.equal(groupJoin.get("myTargetVersion"), coding.getVersion())); - } - - if (translationQuery.hasUrl()) { - predicates.add(criteriaBuilder.equal(conceptMapJoin.get("myUrl"), translationQuery.getUrl())); - if (translationQuery.hasConceptMapVersion()) { - // both url and conceptMapVersion - predicates.add(criteriaBuilder.equal( - conceptMapJoin.get("myVersion"), translationQuery.getConceptMapVersion())); - } else { - if (StringUtils.isNotBlank(latestConceptMapVersion)) { - // only url and use latestConceptMapVersion - predicates.add( - criteriaBuilder.equal(conceptMapJoin.get("myVersion"), latestConceptMapVersion)); - } else { - predicates.add(criteriaBuilder.isNull(conceptMapJoin.get("myVersion"))); - } - } - } - - if (translationQuery.hasTargetSystem()) { - predicates.add( - criteriaBuilder.equal(groupJoin.get("mySource"), translationQuery.getTargetSystem())); - } - - if (translationQuery.hasSource()) { - predicates.add(criteriaBuilder.equal(conceptMapJoin.get("myTarget"), translationQuery.getSource())); - } - - if (translationQuery.hasTarget()) { - predicates.add(criteriaBuilder.equal(conceptMapJoin.get("mySource"), translationQuery.getTarget())); - } - - if (translationQuery.hasResourceId()) { - IIdType resourceId = translationQuery.getResourceId(); - JpaPid resourcePid = - myIdHelperService.getPidOrThrowException(RequestPartitionId.defaultPartition(), resourceId); - predicates.add(criteriaBuilder.equal(conceptMapJoin.get("myResourcePid"), resourcePid.getId())); - } - - Predicate outerPredicate = criteriaBuilder.and(predicates.toArray(new Predicate[0])); - query.where(outerPredicate); - - // Use scrollable results. - final TypedQuery typedQuery = - myEntityManager.createQuery(query.select(root)); - org.hibernate.query.Query hibernateQuery = - (org.hibernate.query.Query) typedQuery; - hibernateQuery.setFetchSize(myFetchSize); - ScrollableResults scrollableResults = hibernateQuery.scroll(ScrollMode.FORWARD_ONLY); - try (ScrollableResultsIterator scrollableResultsIterator = - new ScrollableResultsIterator<>(scrollableResults)) { - - Set matches = new HashSet<>(); - while (scrollableResultsIterator.hasNext()) { - TermConceptMapGroupElement nextElement = scrollableResultsIterator.next(); - - /* TODO: The invocation of the size() below does not seem to be necessary but for some reason, - * but removing it causes tests in TerminologySvcImplR4Test to fail. We use the outcome - * in a trace log to avoid ErrorProne flagging an unused return value. - */ - int size = - nextElement.getConceptMapGroupElementTargets().size(); - ourLog.trace("Have {} targets", size); - - myEntityManager.detach(nextElement); - - if (isNotBlank(targetCode)) { - for (TermConceptMapGroupElementTarget next : - nextElement.getConceptMapGroupElementTargets()) { - if (matches.add(next)) { - if (isBlank(targetCodeSystem) - || StringUtils.equals(targetCodeSystem, next.getSystem())) { - if (StringUtils.equals(targetCode, next.getCode())) { - TranslateConceptResult translationMatch = new TranslateConceptResult(); - translationMatch.setCode(nextElement.getCode()); - translationMatch.setSystem(nextElement.getSystem()); - translationMatch.setSystemVersion(nextElement.getSystemVersion()); - translationMatch.setDisplay(nextElement.getDisplay()); - translationMatch.setValueSet(nextElement.getValueSet()); - translationMatch.setSystemVersion(nextElement.getSystemVersion()); - translationMatch.setConceptMapUrl(nextElement.getConceptMapUrl()); - if (next.getEquivalence() != null) { - translationMatch.setEquivalence( - next.getEquivalence().toCode()); - } - - if (alreadyContainsMapping(elements, translationMatch) - || alreadyContainsMapping(retVal.getResults(), translationMatch)) { - continue; - } - - elements.add(translationMatch); - } - } - } - } - } - } - } - - ourLastResultsFromTranslationWithReverseCache = false; // For testing. - myMemoryCacheService.put( - MemoryCacheService.CacheEnum.CONCEPT_TRANSLATION_REVERSE, translationQuery, elements); - retVal.getResults().addAll(elements); - } else { - ourLastResultsFromTranslationWithReverseCache = true; // For testing. - retVal.getResults().addAll(cachedElements); - } - } - - buildTranslationResult(retVal); - return retVal; - } - - private boolean alreadyContainsMapping( - List elements, TranslateConceptResult translationMatch) { - for (TranslateConceptResult nextExistingElement : elements) { - if (StringUtils.equals(nextExistingElement.getSystem(), translationMatch.getSystem())) { - if (StringUtils.equals(nextExistingElement.getSystemVersion(), translationMatch.getSystemVersion())) { - if (StringUtils.equals(nextExistingElement.getCode(), translationMatch.getCode())) { - return true; - } - } - } - } - return false; - } - public void deleteConceptMap(ResourceTable theResourceTable) { // Get existing entity so it can be deleted. Optional optionalExistingTermConceptMapById = @@ -671,34 +310,6 @@ public class TermConceptMappingSvcImpl implements ITermConceptMappingSvc { } } - // Special case for the translate operation with url and without - // conceptMapVersion, find the latest conecptMapVersion - private String getLatestConceptMapVersion(TranslationRequest theTranslationRequest) { - - Pageable page = PageRequest.of(0, 1); - List theConceptMapList = myConceptMapDao.getTermConceptMapEntitiesByUrlOrderByMostRecentUpdate( - page, theTranslationRequest.getUrl()); - if (!theConceptMapList.isEmpty()) { - return theConceptMapList.get(0).getVersion(); - } - - return null; - } - - private void buildTranslationResult(TranslateConceptResults theTranslationResult) { - - String msg; - if (theTranslationResult.getResults().isEmpty()) { - theTranslationResult.setResult(false); - msg = myContext.getLocalizer().getMessage(TermConceptMappingSvcImpl.class, "noMatchesFound"); - theTranslationResult.setMessage(msg); - } else { - theTranslationResult.setResult(true); - msg = myContext.getLocalizer().getMessage(TermConceptMappingSvcImpl.class, "matchesFound"); - theTranslationResult.setMessage(msg); - } - } - /** * This method is present only for unit tests, do not call from client code */ diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/term/TermLoaderSvcImpl.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/term/TermLoaderSvcImpl.java index ba636bd5f83..78a5601a2a1 100644 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/term/TermLoaderSvcImpl.java +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/term/TermLoaderSvcImpl.java @@ -66,6 +66,7 @@ import ca.uhn.fhir.rest.server.exceptions.InvalidRequestException; import ca.uhn.fhir.util.ValidateUtil; import com.google.common.annotations.VisibleForTesting; import com.google.common.base.Charsets; +import jakarta.annotation.Nonnull; import org.apache.commons.csv.CSVFormat; import org.apache.commons.csv.CSVParser; import org.apache.commons.csv.CSVRecord; @@ -103,7 +104,6 @@ import java.util.Optional; import java.util.Properties; import java.util.Set; import java.util.stream.Collectors; -import javax.annotation.Nonnull; import static ca.uhn.fhir.jpa.term.api.ITermCodeSystemStorageSvc.MAKE_LOADING_VERSION_CURRENT; import static ca.uhn.fhir.jpa.term.loinc.LoincUploadPropertiesEnum.LOINC_ANSWERLIST_FILE; diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/term/TermReadSvcImpl.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/term/TermReadSvcImpl.java index 615c6fe57df..ac169cf84ca 100644 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/term/TermReadSvcImpl.java +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/term/TermReadSvcImpl.java @@ -23,6 +23,7 @@ import ca.uhn.fhir.context.FhirContext; import ca.uhn.fhir.context.FhirVersionEnum; import ca.uhn.fhir.context.support.ConceptValidationOptions; import ca.uhn.fhir.context.support.IValidationSupport; +import ca.uhn.fhir.context.support.LookupCodeRequest; import ca.uhn.fhir.context.support.ValidationSupportContext; import ca.uhn.fhir.context.support.ValueSetExpansionOptions; import ca.uhn.fhir.i18n.Msg; @@ -61,7 +62,6 @@ import ca.uhn.fhir.jpa.entity.TermValueSet; import ca.uhn.fhir.jpa.entity.TermValueSetConcept; import ca.uhn.fhir.jpa.entity.TermValueSetPreExpansionStatusEnum; import ca.uhn.fhir.jpa.model.dao.JpaPid; -import ca.uhn.fhir.jpa.model.entity.ForcedId; import ca.uhn.fhir.jpa.model.entity.ResourceTable; import ca.uhn.fhir.jpa.model.sched.HapiJob; import ca.uhn.fhir.jpa.model.sched.IHasScheduledJobs; @@ -91,6 +91,14 @@ import ca.uhn.hapi.converters.canonical.VersionCanonicalizer; import com.google.common.annotations.VisibleForTesting; import com.google.common.base.Stopwatch; import com.google.common.collect.ArrayListMultimap; +import jakarta.annotation.Nonnull; +import jakarta.annotation.Nullable; +import jakarta.annotation.PostConstruct; +import jakarta.persistence.EntityManager; +import jakarta.persistence.NonUniqueResultException; +import jakarta.persistence.PersistenceContext; +import jakarta.persistence.PersistenceContextType; +import org.apache.commons.collections4.CollectionUtils; import org.apache.commons.collections4.ListUtils; import org.apache.commons.lang3.ObjectUtils; import org.apache.commons.lang3.StringUtils; @@ -148,7 +156,6 @@ import org.springframework.transaction.interceptor.NoRollbackRuleAttribute; import org.springframework.transaction.interceptor.RuleBasedTransactionAttribute; import org.springframework.transaction.support.TransactionSynchronizationManager; import org.springframework.transaction.support.TransactionTemplate; -import org.springframework.util.CollectionUtils; import org.springframework.util.comparator.Comparators; import java.util.ArrayList; @@ -169,13 +176,6 @@ import java.util.UUID; import java.util.concurrent.TimeUnit; import java.util.function.Consumer; import java.util.stream.Collectors; -import javax.annotation.Nonnull; -import javax.annotation.Nullable; -import javax.annotation.PostConstruct; -import javax.persistence.EntityManager; -import javax.persistence.NonUniqueResultException; -import javax.persistence.PersistenceContext; -import javax.persistence.PersistenceContextType; import static ca.uhn.fhir.jpa.entity.TermConceptPropertyBinder.CONCEPT_PROPERTY_PREFIX_NAME; import static ca.uhn.fhir.jpa.term.api.ITermLoaderSvc.LOINC_URI; @@ -197,14 +197,9 @@ public class TermReadSvcImpl implements ITermReadSvc, IHasScheduledJobs { public static final int DEFAULT_MASS_INDEXER_OBJECT_LOADING_THREADS = 2; // doesn't seem to be much gain by using more threads than this value public static final int MAX_MASS_INDEXER_OBJECT_LOADING_THREADS = 6; - private static final int SINGLE_FETCH_SIZE = 1; private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(TermReadSvcImpl.class); private static final ValueSetExpansionOptions DEFAULT_EXPANSION_OPTIONS = new ValueSetExpansionOptions(); private static final TermCodeSystemVersionDetails NO_CURRENT_VERSION = new TermCodeSystemVersionDetails(-1L, null); - private static final String IDX_PROPERTIES = "myProperties"; - private static final String IDX_PROP_KEY = IDX_PROPERTIES + ".myKey"; - private static final String IDX_PROP_VALUE_STRING = IDX_PROPERTIES + ".myValueString"; - private static final String IDX_PROP_DISPLAY_STRING = IDX_PROPERTIES + ".myDisplayString"; private static final String OUR_PIPE_CHARACTER = "|"; private static final int SECONDS_IN_MINUTE = 60; private static final int INDEXED_ROOTS_LOGGING_COUNT = 50_000; @@ -541,6 +536,7 @@ public class TermReadSvcImpl implements ITermReadSvc, IHasScheduledJobs { @Nullable ValueSetExpansionOptions theExpansionOptions, ValueSet theValueSetToExpand, ExpansionFilter theFilter) { + Set addedCodes = new HashSet<>(); ValidateUtil.isNotNullOrThrowUnprocessableEntity(theValueSetToExpand, "ValueSet to expand can not be null"); ValueSetExpansionOptions expansionOptions = provideExpansionOptions(theExpansionOptions); @@ -561,9 +557,8 @@ public class TermReadSvcImpl implements ITermReadSvc, IHasScheduledJobs { accumulator.addParameter().setName("count").setValue(new IntegerType(count)); } - myTxTemplate.executeWithoutResult(tx -> { - expandValueSetIntoAccumulator(theValueSetToExpand, theExpansionOptions, accumulator, theFilter, true); - }); + myTxTemplate.executeWithoutResult(tx -> expandValueSetIntoAccumulator( + theValueSetToExpand, theExpansionOptions, accumulator, theFilter, true, addedCodes)); if (accumulator.getTotalConcepts() != null) { accumulator.setTotal(accumulator.getTotalConcepts()); @@ -595,7 +590,8 @@ public class TermReadSvcImpl implements ITermReadSvc, IHasScheduledJobs { ValueSetExpansionOptions theExpansionOptions, IValueSetConceptAccumulator theAccumulator, ExpansionFilter theFilter, - boolean theAdd) { + boolean theAdd, + Set theAddedCodes) { Optional optionalTermValueSet; if (theValueSetToExpand.hasUrl()) { if (theValueSetToExpand.hasVersion()) { @@ -622,7 +618,7 @@ public class TermReadSvcImpl implements ITermReadSvc, IHasScheduledJobs { "valueSetExpandedUsingInMemoryExpansion", getValueSetInfo(theValueSetToExpand)); theAccumulator.addMessage(msg); - doExpandValueSet(theExpansionOptions, theValueSetToExpand, theAccumulator, theFilter); + doExpandValueSet(theExpansionOptions, theValueSetToExpand, theAccumulator, theFilter, theAddedCodes); return; } @@ -640,7 +636,7 @@ public class TermReadSvcImpl implements ITermReadSvc, IHasScheduledJobs { termValueSet.getExpansionStatus().name(), termValueSet.getExpansionStatus().getDescription()); theAccumulator.addMessage(msg); - doExpandValueSet(theExpansionOptions, theValueSetToExpand, theAccumulator, theFilter); + doExpandValueSet(theExpansionOptions, theValueSetToExpand, theAccumulator, theFilter, theAddedCodes); return; } @@ -652,7 +648,8 @@ public class TermReadSvcImpl implements ITermReadSvc, IHasScheduledJobs { .getLocalizer() .getMessage(TermReadSvcImpl.class, "valueSetExpandedUsingPreExpansion", expansionTimestamp); theAccumulator.addMessage(msg); - expandConcepts(theExpansionOptions, theAccumulator, termValueSet, theFilter, theAdd, isOracleDialect()); + expandConcepts( + theExpansionOptions, theAccumulator, termValueSet, theFilter, theAdd, theAddedCodes, isOracleDialect()); } @Nonnull @@ -668,7 +665,7 @@ public class TermReadSvcImpl implements ITermReadSvc, IHasScheduledJobs { } private boolean isOracleDialect() { - return myHibernatePropertiesProvider.getDialect() instanceof org.hibernate.dialect.Oracle12cDialect; + return myHibernatePropertiesProvider.getDialect() instanceof org.hibernate.dialect.OracleDialect; } private void expandConcepts( @@ -677,6 +674,7 @@ public class TermReadSvcImpl implements ITermReadSvc, IHasScheduledJobs { TermValueSet theTermValueSet, ExpansionFilter theFilter, boolean theAdd, + Set theAddedCodes, boolean theOracle) { // NOTE: if you modifiy the logic here, look to `expandConceptsOracle` and see if your new code applies to its // copy pasted sibling @@ -709,10 +707,6 @@ public class TermReadSvcImpl implements ITermReadSvc, IHasScheduledJobs { } wasFilteredResult = true; } else { - // TODO JA HS: I'm pretty sure we are overfetching here. test says offset 3, count 4, but we are fetching - // index 3 -> 10 here, grabbing 7 concepts. - // Specifically this test - // testExpandInline_IncludePreExpandedValueSetByUri_FilterOnDisplay_LeftMatch_SelectRange if (theOracle) { conceptViews = myTermValueSetConceptViewOracleDao.findByTermValueSetId( offset, toIndex, theTermValueSet.getId()); @@ -803,26 +797,27 @@ public class TermReadSvcImpl implements ITermReadSvc, IHasScheduledJobs { Long sourceConceptPid = pidToSourcePid.get(nextPid); String sourceConceptDirectParentPids = pidToSourceDirectParentPids.get(nextPid); - theAccumulator.includeConceptWithDesignations( - system, - code, - display, - designations, - sourceConceptPid, - sourceConceptDirectParentPids, - systemVersion); + if (theAddedCodes.add(system + OUR_PIPE_CHARACTER + code)) { + theAccumulator.includeConceptWithDesignations( + system, + code, + display, + designations, + sourceConceptPid, + sourceConceptDirectParentPids, + systemVersion); + if (wasFilteredResult) { + theAccumulator.incrementOrDecrementTotalConcepts(true, 1); + } + } } else { - boolean removed = theAccumulator.excludeConcept(system, code); - if (removed) { + if (theAddedCodes.remove(system + OUR_PIPE_CHARACTER + code)) { + theAccumulator.excludeConcept(system, code); theAccumulator.incrementOrDecrementTotalConcepts(false, 1); } } } - if (wasFilteredResult && theAdd) { - theAccumulator.incrementOrDecrementTotalConcepts(true, pidToConcept.size()); - } - logDesignationsExpanded("Finished expanding designations. ", theTermValueSet, designationsExpanded); logConceptsExpanded("Finished expanding concepts. ", theTermValueSet, conceptsExpanded); } @@ -887,8 +882,13 @@ public class TermReadSvcImpl implements ITermReadSvc, IHasScheduledJobs { ValueSetExpansionOptions theExpansionOptions, ValueSet theValueSetToExpand, IValueSetConceptAccumulator theValueSetCodeAccumulator) { + Set addedCodes = new HashSet<>(); doExpandValueSet( - theExpansionOptions, theValueSetToExpand, theValueSetCodeAccumulator, ExpansionFilter.NO_FILTER); + theExpansionOptions, + theValueSetToExpand, + theValueSetCodeAccumulator, + ExpansionFilter.NO_FILTER, + addedCodes); } /** @@ -899,8 +899,8 @@ public class TermReadSvcImpl implements ITermReadSvc, IHasScheduledJobs { ValueSetExpansionOptions theExpansionOptions, ValueSet theValueSetToExpand, IValueSetConceptAccumulator theValueSetCodeAccumulator, - @Nonnull ExpansionFilter theExpansionFilter) { - Set addedCodes = new HashSet<>(); + @Nonnull ExpansionFilter theExpansionFilter, + Set theAddedCodes) { StopWatch sw = new StopWatch(); String valueSetInfo = getValueSetInfo(theValueSetToExpand); @@ -909,7 +909,7 @@ public class TermReadSvcImpl implements ITermReadSvc, IHasScheduledJobs { // Offset can't be combined with excludes Integer skipCountRemaining = theValueSetCodeAccumulator.getSkipCountRemaining(); if (skipCountRemaining != null && skipCountRemaining > 0) { - if (theValueSetToExpand.getCompose().getExclude().size() > 0) { + if (!theValueSetToExpand.getCompose().getExclude().isEmpty()) { String msg = myContext .getLocalizer() .getMessage(TermReadSvcImpl.class, "valueSetNotYetExpanded_OffsetNotAllowed", valueSetInfo); @@ -922,7 +922,7 @@ public class TermReadSvcImpl implements ITermReadSvc, IHasScheduledJobs { for (ValueSet.ConceptSetComponent include : theValueSetToExpand.getCompose().getInclude()) { myTxTemplate.executeWithoutResult(tx -> expandValueSetHandleIncludeOrExclude( - theExpansionOptions, theValueSetCodeAccumulator, addedCodes, include, true, theExpansionFilter)); + theExpansionOptions, theValueSetCodeAccumulator, theAddedCodes, include, true, theExpansionFilter)); } // Handle excludes @@ -932,7 +932,7 @@ public class TermReadSvcImpl implements ITermReadSvc, IHasScheduledJobs { myTxTemplate.executeWithoutResult(tx -> expandValueSetHandleIncludeOrExclude( theExpansionOptions, theValueSetCodeAccumulator, - addedCodes, + theAddedCodes, exclude, false, ExpansionFilter.NO_FILTER)); @@ -977,7 +977,7 @@ public class TermReadSvcImpl implements ITermReadSvc, IHasScheduledJobs { String system = theIncludeOrExclude.getSystem(); boolean hasSystem = isNotBlank(system); - boolean hasValueSet = theIncludeOrExclude.getValueSet().size() > 0; + boolean hasValueSet = !theIncludeOrExclude.getValueSet().isEmpty(); if (hasSystem) { @@ -1005,7 +1005,7 @@ public class TermReadSvcImpl implements ITermReadSvc, IHasScheduledJobs { } else { - if (theIncludeOrExclude.getConcept().size() > 0 && theExpansionFilter.hasCode()) { + if (!theIncludeOrExclude.getConcept().isEmpty() && theExpansionFilter.hasCode()) { if (defaultString(theIncludeOrExclude.getSystem()).equals(theExpansionFilter.getSystem())) { if (theIncludeOrExclude.getConcept().stream() .noneMatch(t -> t.getCode().equals(theExpansionFilter.getCode()))) { @@ -1066,7 +1066,12 @@ public class TermReadSvcImpl implements ITermReadSvc, IHasScheduledJobs { } expandValueSetIntoAccumulator( - valueSet, theExpansionOptions, theValueSetCodeAccumulator, subExpansionFilter, theAdd); + valueSet, + theExpansionOptions, + theValueSetCodeAccumulator, + subExpansionFilter, + theAdd, + theAddedCodes); } } else { @@ -1856,12 +1861,11 @@ public class TermReadSvcImpl implements ITermReadSvc, IHasScheduledJobs { if (!handled) { throwInvalidFilter( nextFilter, - " - Note that Hibernate Search is disabled on this server so not all ValueSet expansion funtionality is available."); + " - Note that Hibernate Search is disabled on this server so not all ValueSet expansion functionality is available."); } } - if (theInclude.getConcept().isEmpty()) { - + if (theInclude.getFilter().isEmpty() && theInclude.getConcept().isEmpty()) { Collection concepts = myConceptDao.fetchConceptsAndDesignationsByVersionPid(theVersion.getPid()); for (TermConcept next : concepts) { @@ -2393,7 +2397,8 @@ public class TermReadSvcImpl implements ITermReadSvc, IHasScheduledJobs { sw); } catch (Exception e) { - ourLog.error("Failed to pre-expand ValueSet: " + e.getMessage(), e); + ourLog.error( + "Failed to pre-expand ValueSet with URL[{}]: {}", valueSetToExpand.getUrl(), e.getMessage(), e); txTemplate.executeWithoutResult(t -> { valueSetToExpand.setExpansionStatus(TermValueSetPreExpansionStatusEnum.FAILED_TO_EXPAND); myTermValueSetDao.saveAndFlush(valueSetToExpand); @@ -2571,10 +2576,13 @@ public class TermReadSvcImpl implements ITermReadSvc, IHasScheduledJobs { return new IFhirResourceDaoCodeSystem.SubsumesResult(subsumes); } - protected IValidationSupport.LookupCodeResult lookupCode( - String theSystem, String theCode, String theDisplayLanguage) { + @Override + public IValidationSupport.LookupCodeResult lookupCode( + ValidationSupportContext theValidationSupportContext, @Nonnull LookupCodeRequest theLookupCodeRequest) { TransactionTemplate txTemplate = new TransactionTemplate(myTransactionManager); return txTemplate.execute(t -> { + final String theSystem = theLookupCodeRequest.getSystem(); + final String theCode = theLookupCodeRequest.getCode(); Optional codeOpt = findCode(theSystem, theCode); if (codeOpt.isPresent()) { TermConcept code = codeOpt.get(); @@ -2589,7 +2597,7 @@ public class TermReadSvcImpl implements ITermReadSvc, IHasScheduledJobs { for (TermConceptDesignation next : code.getDesignations()) { // filter out the designation based on displayLanguage if any - if (isDisplayLanguageMatch(theDisplayLanguage, next.getLanguage())) { + if (isDisplayLanguageMatch(theLookupCodeRequest.getDisplayLanguage(), next.getLanguage())) { IValidationSupport.ConceptDesignation designation = new IValidationSupport.ConceptDesignation(); designation.setLanguage(next.getLanguage()); designation.setUseSystem(next.getUseSystem()); @@ -2600,7 +2608,11 @@ public class TermReadSvcImpl implements ITermReadSvc, IHasScheduledJobs { } } + final Collection propertyNames = theLookupCodeRequest.getPropertyNames(); for (TermConceptProperty next : code.getProperties()) { + if (ObjectUtils.isNotEmpty(propertyNames) && !propertyNames.contains(next.getKey())) { + continue; + } if (next.getType() == TermConceptPropertyTypeEnum.CODING) { IValidationSupport.CodingConceptProperty property = new IValidationSupport.CodingConceptProperty( @@ -2972,14 +2984,15 @@ public class TermReadSvcImpl implements ITermReadSvc, IHasScheduledJobs { public Optional readCodeSystemByForcedId(String theForcedId) { @SuppressWarnings("unchecked") List resultList = (List) myEntityManager - .createQuery("select f.myResource from ForcedId f " - + "where f.myResourceType = 'CodeSystem' and f.myForcedId = '" + theForcedId + "'") + .createQuery("select r from ResourceTable r " + + "where r.myResourceType = 'CodeSystem' and r.myFhirId = :fhirId") + .setParameter("fhirId", theForcedId) .getResultList(); if (resultList.isEmpty()) return Optional.empty(); if (resultList.size() > 1) throw new NonUniqueResultException(Msg.code(911) + "More than one CodeSystem is pointed by forcedId: " - + theForcedId + ". Was constraint " + ForcedId.IDX_FORCEDID_TYPE_FID + " removed?"); + + theForcedId + ". Was constraint " + ResourceTable.IDX_RES_TYPE_FHIR_ID + " removed?"); IFhirResourceDao csDao = myDaoRegistry.getResourceDao("CodeSystem"); IBaseResource cs = myJpaStorageResourceParser.toResource(resultList.get(0), false); @@ -3112,15 +3125,6 @@ public class TermReadSvcImpl implements ITermReadSvc, IHasScheduledJobs { return isValueSetPreExpandedForCodeValidation(valueSetR4); } - @Override - public LookupCodeResult lookupCode( - ValidationSupportContext theValidationSupportContext, - String theSystem, - String theCode, - String theDisplayLanguage) { - return lookupCode(theSystem, theCode, theDisplayLanguage); - } - private static class TermCodeSystemVersionDetails { private final long myPid; diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/term/ValueSetConceptAccumulator.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/term/ValueSetConceptAccumulator.java index 18c91c828b0..3fa13ccfb6b 100644 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/term/ValueSetConceptAccumulator.java +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/term/ValueSetConceptAccumulator.java @@ -27,11 +27,11 @@ import ca.uhn.fhir.jpa.entity.TermValueSet; import ca.uhn.fhir.jpa.entity.TermValueSetConcept; import ca.uhn.fhir.jpa.entity.TermValueSetConceptDesignation; import ca.uhn.fhir.util.ValidateUtil; +import jakarta.annotation.Nonnull; import java.util.Collection; import java.util.List; import java.util.Optional; -import javax.annotation.Nonnull; import static org.apache.commons.lang3.StringUtils.isAnyBlank; import static org.apache.commons.lang3.StringUtils.isNoneBlank; diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/term/ValueSetExpansionComponentWithConceptAccumulator.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/term/ValueSetExpansionComponentWithConceptAccumulator.java index 94a6184120c..6cd56bfd5a6 100644 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/term/ValueSetExpansionComponentWithConceptAccumulator.java +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/term/ValueSetExpansionComponentWithConceptAccumulator.java @@ -25,6 +25,8 @@ import ca.uhn.fhir.jpa.entity.TermConceptDesignation; import ca.uhn.fhir.jpa.term.ex.ExpansionTooCostlyException; import ca.uhn.fhir.model.api.annotation.Block; import ca.uhn.fhir.rest.server.exceptions.InternalErrorException; +import jakarta.annotation.Nonnull; +import jakarta.annotation.Nullable; import org.apache.commons.lang3.StringUtils; import org.hl7.fhir.r4.model.ValueSet; @@ -36,8 +38,6 @@ import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.stream.Collectors; -import javax.annotation.Nonnull; -import javax.annotation.Nullable; import static org.apache.commons.lang3.StringUtils.isNotBlank; diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/term/api/ITermConceptClientMappingSvc.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/term/api/ITermConceptClientMappingSvc.java new file mode 100644 index 00000000000..a07e3a229b7 --- /dev/null +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/term/api/ITermConceptClientMappingSvc.java @@ -0,0 +1,34 @@ +/*- + * #%L + * HAPI FHIR JPA Server + * %% + * Copyright (C) 2014 - 2023 Smile CDR, Inc. + * %% + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * #L% + */ +package ca.uhn.fhir.jpa.term.api; + +import ca.uhn.fhir.context.support.IValidationSupport; +import ca.uhn.fhir.context.support.TranslateConceptResults; +import ca.uhn.fhir.jpa.api.model.TranslationRequest; + +/** + * Represents the terminology translate functions + */ +public interface ITermConceptClientMappingSvc extends IValidationSupport { + + TranslateConceptResults translate(TranslationRequest theTranslationRequest); + + TranslateConceptResults translateWithReverse(TranslationRequest theTranslationRequest); +} diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/term/api/ITermConceptMappingSvc.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/term/api/ITermConceptMappingSvc.java index 127861a1961..1fc1b494693 100644 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/term/api/ITermConceptMappingSvc.java +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/term/api/ITermConceptMappingSvc.java @@ -19,17 +19,13 @@ */ package ca.uhn.fhir.jpa.term.api; -import ca.uhn.fhir.context.support.IValidationSupport; -import ca.uhn.fhir.context.support.TranslateConceptResults; -import ca.uhn.fhir.jpa.api.model.TranslationRequest; import ca.uhn.fhir.jpa.model.entity.ResourceTable; import org.hl7.fhir.r4.model.ConceptMap; -public interface ITermConceptMappingSvc extends IValidationSupport { - - TranslateConceptResults translate(TranslationRequest theTranslationRequest); - - TranslateConceptResults translateWithReverse(TranslationRequest theTranslationRequest); +/** + * Represents ConceptMap translation and persistence operations + */ +public interface ITermConceptMappingSvc extends ITermConceptClientMappingSvc { void deleteConceptMapAndChildren(ResourceTable theResourceTable); diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/term/api/ITermReadSvc.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/term/api/ITermReadSvc.java index 52bbbd2cc2e..a606a6ab601 100644 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/term/api/ITermReadSvc.java +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/term/api/ITermReadSvc.java @@ -29,6 +29,8 @@ import ca.uhn.fhir.jpa.model.entity.ResourceTable; import ca.uhn.fhir.jpa.term.IValueSetConceptAccumulator; import ca.uhn.fhir.rest.api.server.RequestDetails; import ca.uhn.fhir.util.FhirVersionIndependentConcept; +import jakarta.annotation.Nonnull; +import jakarta.annotation.Nullable; import org.hl7.fhir.instance.model.api.IBaseCoding; import org.hl7.fhir.instance.model.api.IBaseDatatype; import org.hl7.fhir.instance.model.api.IBaseResource; @@ -41,8 +43,6 @@ import org.springframework.transaction.annotation.Transactional; import java.util.List; import java.util.Optional; import java.util.Set; -import javax.annotation.Nonnull; -import javax.annotation.Nullable; /** * This interface is the "read" interface for the terminology service. It handles things like diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/term/custom/CustomTerminologySet.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/term/custom/CustomTerminologySet.java index 72bd94f53a8..d9ef9b2790a 100644 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/term/custom/CustomTerminologySet.java +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/term/custom/CustomTerminologySet.java @@ -27,6 +27,7 @@ import ca.uhn.fhir.jpa.term.IZipContentsHandlerCsv; import ca.uhn.fhir.jpa.term.LoadedFileDescriptors; import ca.uhn.fhir.jpa.term.TermLoaderSvcImpl; import ca.uhn.fhir.rest.server.exceptions.InvalidRequestException; +import jakarta.annotation.Nonnull; import org.apache.commons.csv.QuoteMode; import org.apache.commons.lang3.Validate; @@ -39,7 +40,6 @@ import java.util.List; import java.util.Map; import java.util.Set; import java.util.stream.Collectors; -import javax.annotation.Nonnull; public class CustomTerminologySet { diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/term/loinc/LoincLinguisticVariantsHandler.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/term/loinc/LoincLinguisticVariantsHandler.java index 1245dad647c..234c089c715 100644 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/term/loinc/LoincLinguisticVariantsHandler.java +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/term/loinc/LoincLinguisticVariantsHandler.java @@ -20,10 +20,10 @@ package ca.uhn.fhir.jpa.term.loinc; import ca.uhn.fhir.jpa.term.IZipContentsHandlerCsv; +import jakarta.annotation.Nonnull; import org.apache.commons.csv.CSVRecord; import java.util.List; -import javax.annotation.Nonnull; import static org.apache.commons.lang3.StringUtils.isBlank; import static org.apache.commons.lang3.StringUtils.trim; diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/util/PersistenceContextProvider.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/util/PersistenceContextProvider.java index fec6c6290c1..18771e6ef27 100644 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/util/PersistenceContextProvider.java +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/util/PersistenceContextProvider.java @@ -19,8 +19,8 @@ */ package ca.uhn.fhir.jpa.util; -import javax.persistence.EntityManager; -import javax.persistence.PersistenceContext; +import jakarta.persistence.EntityManager; +import jakarta.persistence.PersistenceContext; /** * Utility class that provides a proxied entityManager. It can be directly injected or diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/util/QueryParameterUtils.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/util/QueryParameterUtils.java index 545935497c8..3e3444f395c 100644 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/util/QueryParameterUtils.java +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/util/QueryParameterUtils.java @@ -37,6 +37,11 @@ import com.healthmarketscience.sqlbuilder.ComboCondition; import com.healthmarketscience.sqlbuilder.Condition; import com.healthmarketscience.sqlbuilder.InCondition; import com.healthmarketscience.sqlbuilder.dbspec.basic.DbColumn; +import jakarta.annotation.Nonnull; +import jakarta.annotation.Nullable; +import jakarta.persistence.criteria.CriteriaBuilder; +import jakarta.persistence.criteria.From; +import jakarta.persistence.criteria.Predicate; import org.apache.commons.collections4.BidiMap; import org.apache.commons.collections4.bidimap.DualHashBidiMap; import org.apache.commons.collections4.bidimap.UnmodifiableBidiMap; @@ -51,11 +56,6 @@ import java.util.Date; import java.util.List; import java.util.Objects; import java.util.stream.Collectors; -import javax.annotation.Nonnull; -import javax.annotation.Nullable; -import javax.persistence.criteria.CriteriaBuilder; -import javax.persistence.criteria.From; -import javax.persistence.criteria.Predicate; import static org.apache.commons.lang3.ObjectUtils.defaultIfNull; diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/util/ScrollableResultsIterator.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/util/ScrollableResultsIterator.java index b201778ebc8..a79a7834e9b 100644 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/util/ScrollableResultsIterator.java +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/util/ScrollableResultsIterator.java @@ -39,7 +39,7 @@ public class ScrollableResultsIterator extends BaseIterator if (myNext == null) { if (myScroll.next()) { hasNext = true; - myNext = (T) myScroll.get(0); + myNext = (T) myScroll.get(); } else { hasNext = false; } diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/validation/JpaValidationSupportChain.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/validation/JpaValidationSupportChain.java index 8f88c3a88ec..5c8274fcda4 100644 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/validation/JpaValidationSupportChain.java +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/validation/JpaValidationSupportChain.java @@ -24,6 +24,8 @@ import ca.uhn.fhir.context.support.IValidationSupport; import ca.uhn.fhir.jpa.packages.NpmJpaValidationSupport; import ca.uhn.fhir.jpa.term.api.ITermConceptMappingSvc; import ca.uhn.fhir.jpa.term.api.ITermReadSvc; +import jakarta.annotation.PostConstruct; +import jakarta.annotation.PreDestroy; import org.hl7.fhir.common.hapi.validation.support.CommonCodeSystemsTerminologyService; import org.hl7.fhir.common.hapi.validation.support.InMemoryTerminologyServerValidationSupport; import org.hl7.fhir.common.hapi.validation.support.SnapshotGeneratingValidationSupport; @@ -32,9 +34,6 @@ import org.hl7.fhir.common.hapi.validation.support.ValidationSupportChain; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Qualifier; -import javax.annotation.PostConstruct; -import javax.annotation.PreDestroy; - public class JpaValidationSupportChain extends ValidationSupportChain { private final FhirContext myFhirContext; diff --git a/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/batch2/JobInstanceUtilTest.java b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/batch2/JobInstanceUtilTest.java index ae0e69d6561..2567a7b3846 100644 --- a/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/batch2/JobInstanceUtilTest.java +++ b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/batch2/JobInstanceUtilTest.java @@ -24,7 +24,6 @@ class JobInstanceUtilTest { assertTrue(EqualsBuilder.reflectionEquals(instance, instanceCopyBack)); - } } diff --git a/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/bulk/export/svc/BulkDataExportJobSchedulingHelperImplTest.java b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/bulk/export/svc/BulkDataExportJobSchedulingHelperImplTest.java index e14dcc61f0d..f7c90efb7c8 100644 --- a/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/bulk/export/svc/BulkDataExportJobSchedulingHelperImplTest.java +++ b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/bulk/export/svc/BulkDataExportJobSchedulingHelperImplTest.java @@ -29,7 +29,7 @@ import org.springframework.transaction.PlatformTransactionManager; import org.springframework.transaction.support.TransactionCallback; import org.springframework.transaction.support.TransactionTemplate; -import javax.annotation.Nonnull; +import jakarta.annotation.Nonnull; import java.time.LocalDateTime; import java.time.ZoneId; import java.time.temporal.ChronoUnit; diff --git a/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/bulk/export/svc/JpaBulkExportProcessorTest.java b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/bulk/export/svc/JpaBulkExportProcessorTest.java index 0382404fb38..9822399a55e 100644 --- a/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/bulk/export/svc/JpaBulkExportProcessorTest.java +++ b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/bulk/export/svc/JpaBulkExportProcessorTest.java @@ -2,6 +2,7 @@ package ca.uhn.fhir.jpa.bulk.export.svc; import ca.uhn.fhir.context.FhirContext; import ca.uhn.fhir.context.RuntimeResourceDefinition; +import ca.uhn.fhir.context.RuntimeSearchParam; import ca.uhn.fhir.interceptor.model.RequestPartitionId; import ca.uhn.fhir.jpa.api.config.JpaStorageSettings; import ca.uhn.fhir.jpa.api.dao.DaoRegistry; @@ -23,12 +24,15 @@ import ca.uhn.fhir.mdm.api.MdmMatchResultEnum; import ca.uhn.fhir.mdm.dao.IMdmLinkDao; import ca.uhn.fhir.mdm.model.MdmPidTuple; import ca.uhn.fhir.model.primitive.IdDt; +import ca.uhn.fhir.rest.api.RestSearchParameterTypeEnum; import ca.uhn.fhir.rest.api.server.RequestDetails; import ca.uhn.fhir.rest.api.server.SystemRequestDetails; import ca.uhn.fhir.rest.api.server.bulk.BulkExportJobParameters; import ca.uhn.fhir.rest.server.exceptions.InternalErrorException; +import ca.uhn.fhir.rest.server.util.ISearchParamRegistry; import org.hl7.fhir.instance.model.api.IIdType; import org.hl7.fhir.r4.model.Group; +import org.hl7.fhir.r4.model.IdType; import org.hl7.fhir.r4.model.Observation; import org.hl7.fhir.r4.model.Patient; import org.junit.jupiter.api.Test; @@ -139,6 +143,9 @@ public class JpaBulkExportProcessorTest { @Mock private MdmExpansionCacheSvc myMdmExpansionCacheSvc; + @Mock + private ISearchParamRegistry mySearchParamRegistry; + @Spy private IHapiTransactionService myTransactionService = new NonTransactionalHapiTransactionService(); @@ -409,6 +416,8 @@ public class JpaBulkExportProcessorTest { ISearchBuilder observationSearchBuilder = mock(ISearchBuilder.class); // when + RuntimeSearchParam searchParam = new RuntimeSearchParam(new IdType("1"), "", "", "", "", RestSearchParameterTypeEnum.STRING, Collections.singleton(""), Collections.singleton(""), RuntimeSearchParam.RuntimeSearchParamStatusEnum.ACTIVE, Collections.singleton("")); + when(mySearchParamRegistry.getActiveSearchParam(any(), any())).thenReturn(searchParam); // expandAllPatientPidsFromGroup when(myDaoRegistry.getResourceDao(eq("Group"))) .thenReturn(groupDao); diff --git a/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/dao/CodingSpyTest.java b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/dao/CodingSpyTest.java index f321c205e02..87f0d0b48cf 100644 --- a/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/dao/CodingSpyTest.java +++ b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/dao/CodingSpyTest.java @@ -9,7 +9,7 @@ import org.junit.jupiter.params.ParameterizedTest; import org.junit.jupiter.params.provider.Arguments; import org.junit.jupiter.params.provider.MethodSource; -import javax.annotation.Nonnull; +import jakarta.annotation.Nonnull; import java.util.List; import static org.hamcrest.MatcherAssert.assertThat; diff --git a/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/dao/index/DaoSearchParamSynchronizerTest.java b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/dao/index/DaoSearchParamSynchronizerTest.java index 672a023b65e..848f407b512 100644 --- a/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/dao/index/DaoSearchParamSynchronizerTest.java +++ b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/dao/index/DaoSearchParamSynchronizerTest.java @@ -12,7 +12,7 @@ import org.junit.jupiter.api.extension.ExtendWith; import org.mockito.Mock; import org.mockito.junit.jupiter.MockitoExtension; -import javax.persistence.EntityManager; +import jakarta.persistence.EntityManager; import java.math.BigDecimal; import java.util.List; diff --git a/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/dao/index/IdHelperServiceTest.java b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/dao/index/IdHelperServiceTest.java new file mode 100644 index 00000000000..12888335a95 --- /dev/null +++ b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/dao/index/IdHelperServiceTest.java @@ -0,0 +1,168 @@ +package ca.uhn.fhir.jpa.dao.index; + +import ca.uhn.fhir.context.FhirContext; +import ca.uhn.fhir.interceptor.model.RequestPartitionId; +import ca.uhn.fhir.jpa.api.config.JpaStorageSettings; +import ca.uhn.fhir.jpa.dao.data.IResourceTableDao; +import ca.uhn.fhir.jpa.model.config.PartitionSettings; +import ca.uhn.fhir.jpa.model.dao.JpaPid; +import ca.uhn.fhir.jpa.model.entity.ResourceTable; +import ca.uhn.fhir.jpa.util.MemoryCacheService; +import jakarta.persistence.EntityManager; +import jakarta.persistence.Tuple; +import jakarta.persistence.TypedQuery; +import jakarta.persistence.criteria.*; +import org.hl7.fhir.r4.hapi.ctx.FhirR4; +import org.hl7.fhir.r4.model.Patient; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import org.mockito.ArgumentMatchers; +import org.mockito.InjectMocks; +import org.mockito.Mock; +import org.mockito.junit.jupiter.MockitoExtension; + +import java.time.Instant; +import java.util.Collections; +import java.util.Date; +import java.util.List; +import java.util.Map; + +import static org.junit.jupiter.api.Assertions.*; +import static org.mockito.ArgumentMatchers.*; +import static org.mockito.Mockito.*; + +@ExtendWith(MockitoExtension.class) +public class IdHelperServiceTest { + + @InjectMocks + private final IdHelperService subject = new IdHelperService(); + + @Mock + protected IResourceTableDao myResourceTableDao; + + @Mock + private JpaStorageSettings myStorageSettings; + + @Mock + private FhirContext myFhirCtx; + + @Mock + private MemoryCacheService myMemoryCacheService; + + @Mock + private EntityManager myEntityManager; + + @Mock + private PartitionSettings myPartitionSettings; + + @BeforeEach + void setUp() { + subject.setDontCheckActiveTransactionForUnitTest(true); + + when(myStorageSettings.isDeleteEnabled()).thenReturn(true); + when(myStorageSettings.getResourceClientIdStrategy()).thenReturn(JpaStorageSettings.ClientIdStrategyEnum.ANY); + when(myPartitionSettings.isAllowUnqualifiedCrossPartitionReference()).thenReturn(true); + } + + @Test + public void testResolveResourcePersistentIds() { + //prepare params + RequestPartitionId requestPartitionId = RequestPartitionId.fromPartitionName("Partition-A"); + String resourceType = "Patient"; + Long id = 123L; + List ids = List.of(String.valueOf(id)); + boolean theExcludeDeleted = false; + + //prepare results + Patient expectedPatient = new Patient(); + expectedPatient.setId(ids.get(0)); + Object[] obj = new Object[] {resourceType, Long.parseLong(ids.get(0)), ids.get(0), Date.from(Instant.now())}; + + // configure mock behaviour + when(myStorageSettings.isDeleteEnabled()).thenReturn(true); + when(myResourceTableDao + .findAndResolveByForcedIdWithNoType(eq(resourceType), eq(ids), eq(theExcludeDeleted))) + .thenReturn(Collections.singletonList(obj)); + + Map actualIds = subject.resolveResourcePersistentIds(requestPartitionId, resourceType, ids, theExcludeDeleted); + + //verify results + assertFalse(actualIds.isEmpty()); + assertEquals(id, actualIds.get(ids.get(0)).getId()); + } + + @Test + public void testResolveResourcePersistentIdsDeleteFalse() { + //prepare Params + RequestPartitionId requestPartitionId = RequestPartitionId.fromPartitionName("Partition-A"); + Long id = 123L; + String resourceType = "Patient"; + List ids = List.of(String.valueOf(id)); + String forcedId = "(all)/" + resourceType + "/" + id; + boolean theExcludeDeleted = false; + + //prepare results + Patient expectedPatient = new Patient(); + expectedPatient.setId(ids.get(0)); + + // configure mock behaviour + configureCacheBehaviour(forcedId); + configureEntityManagerBehaviour(id, resourceType, ids.get(0)); + when(myStorageSettings.isDeleteEnabled()).thenReturn(false); + when(myFhirCtx.getVersion()).thenReturn(new FhirR4()); + + Map actualIds = subject.resolveResourcePersistentIds(requestPartitionId, resourceType, ids, theExcludeDeleted); + + //verifyResult + assertFalse(actualIds.isEmpty()); + assertEquals(id, actualIds.get(ids.get(0)).getId()); + } + + private void configureCacheBehaviour(String resourceUrl) { + when(myMemoryCacheService.getThenPutAfterCommit(eq(MemoryCacheService.CacheEnum.FORCED_ID_TO_PID), eq(resourceUrl), any())).thenCallRealMethod(); + doNothing().when(myMemoryCacheService).putAfterCommit(eq(MemoryCacheService.CacheEnum.FORCED_ID_TO_PID), eq(resourceUrl), ArgumentMatchers.any()); + when(myMemoryCacheService.getIfPresent(eq(MemoryCacheService.CacheEnum.FORCED_ID_TO_PID), eq(resourceUrl))).thenReturn(null); + } + + private void configureEntityManagerBehaviour(Long idNumber, String resourceType, String id) { + List mockedTupleList = getMockedTupleList(idNumber, resourceType, id); + CriteriaBuilder builder = getMockedCriteriaBuilder(); + Root from = getMockedFrom(); + + @SuppressWarnings("unchecked") + TypedQuery query = (TypedQuery) mock(TypedQuery.class); + @SuppressWarnings("unchecked") + CriteriaQuery cq = mock(CriteriaQuery.class); + + when(builder.createTupleQuery()).thenReturn(cq); + when(cq.from(ArgumentMatchers.>any())).thenReturn(from); + when(query.getResultList()).thenReturn(mockedTupleList); + + when(myEntityManager.getCriteriaBuilder()).thenReturn(builder); + when(myEntityManager.createQuery(ArgumentMatchers.>any())).thenReturn(query); + } + + private CriteriaBuilder getMockedCriteriaBuilder() { + Predicate pred = mock(Predicate.class); + CriteriaBuilder builder = mock(CriteriaBuilder.class); + lenient().when(builder.equal(any(), any())).thenReturn(pred); + return builder; + } + private Root getMockedFrom() { + @SuppressWarnings("unchecked") + Path path = mock(Path.class); + @SuppressWarnings("unchecked") + Root from = mock(Root.class); + lenient().when(from.get(ArgumentMatchers.any())).thenReturn(path); + return from; + } + + private List getMockedTupleList(Long idNumber, String resourceType, String id) { + Tuple tuple = mock(Tuple.class); + when(tuple.get(eq(0), eq(Long.class))).thenReturn(idNumber); + when(tuple.get(eq(1), eq(String.class))).thenReturn(resourceType); + when(tuple.get(eq(2), eq(String.class))).thenReturn(id); + return List.of(tuple); + } +} diff --git a/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/entity/GeneratedSchemaTest.java b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/entity/GeneratedSchemaTest.java new file mode 100644 index 00000000000..bd10ad84d05 --- /dev/null +++ b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/entity/GeneratedSchemaTest.java @@ -0,0 +1,40 @@ +package ca.uhn.fhir.jpa.entity; + +import ca.uhn.fhir.util.ClasspathUtil; +import org.apache.commons.lang3.StringUtils; +import org.junit.jupiter.api.Test; + +import java.util.Arrays; + +import static org.hamcrest.CoreMatchers.containsString; +import static org.hamcrest.MatcherAssert.assertThat; + +public class GeneratedSchemaTest { + + /** + * Make sure that the RES_TEXT_VC column, which is supposed to be an unlimited-length + * string datatype, actually uses an appropriate datatype on the various databases + * we care about. + */ + @Test + public void testVerifyLongVarcharColumnDefinition() { + validateLongVarcharDatatype("cockroachdb.sql", "varchar(2147483647)"); + validateLongVarcharDatatype("derby.sql", "clob"); + validateLongVarcharDatatype("mysql.sql", "longtext"); + validateLongVarcharDatatype("mariadb.sql", "longtext"); + + validateLongVarcharDatatype("h2.sql", "clob"); + validateLongVarcharDatatype("postgres.sql", "text"); + validateLongVarcharDatatype("oracle.sql", "clob"); + validateLongVarcharDatatype("sqlserver.sql", "varchar(max)"); + + } + + private static void validateLongVarcharDatatype(String schemaName, String expectedDatatype) { + String schema = ClasspathUtil.loadResource("ca/uhn/hapi/fhir/jpa/docs/database/" + schemaName); + String[] lines = StringUtils.split(schema, '\n'); + String resTextVc = Arrays.stream(lines).filter(t -> t.contains("RES_TEXT_VC ")).findFirst().orElseThrow(); + assertThat("Wrong type in " + schemaName, resTextVc, containsString("RES_TEXT_VC " + expectedDatatype)); + } + +} diff --git a/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/packages/PackageInstallerSvcImplTest.java b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/packages/PackageInstallerSvcImplTest.java index 2e2528697ce..e949c32c6d9 100644 --- a/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/packages/PackageInstallerSvcImplTest.java +++ b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/packages/PackageInstallerSvcImplTest.java @@ -3,9 +3,9 @@ package ca.uhn.fhir.jpa.packages; import ca.uhn.fhir.context.FhirContext; import ca.uhn.fhir.context.FhirVersionEnum; import ca.uhn.fhir.context.support.IValidationSupport; +import ca.uhn.fhir.jpa.api.config.JpaStorageSettings; import ca.uhn.fhir.jpa.api.dao.DaoRegistry; import ca.uhn.fhir.jpa.api.dao.IFhirResourceDao; -import ca.uhn.fhir.jpa.api.model.DaoMethodOutcome; import ca.uhn.fhir.jpa.dao.data.INpmPackageVersionDao; import ca.uhn.fhir.jpa.dao.tx.IHapiTransactionService; import ca.uhn.fhir.jpa.dao.tx.NonTransactionalHapiTransactionService; @@ -13,18 +13,26 @@ import ca.uhn.fhir.jpa.model.config.PartitionSettings; import ca.uhn.fhir.jpa.packages.loader.PackageResourceParsingSvc; import ca.uhn.fhir.jpa.searchparam.SearchParameterMap; import ca.uhn.fhir.jpa.searchparam.registry.ISearchParamRegistryController; +import ca.uhn.fhir.jpa.searchparam.util.SearchParameterHelper; import ca.uhn.fhir.rest.api.server.RequestDetails; import ca.uhn.fhir.rest.server.SimpleBundleProvider; +import org.hl7.fhir.instance.model.api.IBaseResource; import org.hl7.fhir.r4.model.CodeSystem; +import org.hl7.fhir.r4.model.CodeType; import org.hl7.fhir.r4.model.Communication; import org.hl7.fhir.r4.model.DocumentReference; import org.hl7.fhir.r4.model.Enumerations; +import org.hl7.fhir.r4.model.IdType; import org.hl7.fhir.r4.model.SearchParameter; import org.hl7.fhir.r4.model.Subscription; import org.hl7.fhir.utilities.npm.NpmPackage; import org.hl7.fhir.utilities.npm.PackageGenerator; +import org.junit.jupiter.api.Nested; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.ExtendWith; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.Arguments; +import org.junit.jupiter.params.provider.MethodSource; import org.mockito.ArgumentCaptor; import org.mockito.Captor; import org.mockito.InjectMocks; @@ -32,25 +40,26 @@ import org.mockito.Mock; import org.mockito.Spy; import org.mockito.junit.jupiter.MockitoExtension; -import javax.annotation.Nonnull; +import jakarta.annotation.Nonnull; import java.io.ByteArrayOutputStream; import java.io.IOException; import java.nio.charset.StandardCharsets; +import java.util.ArrayList; +import java.util.Collection; +import java.util.Iterator; import java.util.List; import java.util.Optional; +import java.util.stream.Stream; import static org.junit.jupiter.api.Assertions.assertEquals; -import static org.junit.jupiter.api.Assertions.assertFalse; -import static org.junit.jupiter.api.Assertions.assertTrue; +import static org.junit.jupiter.params.provider.Arguments.arguments; import static org.mockito.ArgumentMatchers.any; import static org.mockito.Mockito.times; import static org.mockito.Mockito.verify; -import static org.mockito.Mockito.verifyNoInteractions; import static org.mockito.Mockito.when; @ExtendWith(MockitoExtension.class) public class PackageInstallerSvcImplTest { - public static final String PACKAGE_VERSION = "1.0"; public static final String PACKAGE_ID_1 = "package1"; @@ -65,7 +74,15 @@ public class PackageInstallerSvcImplTest { @Mock private IFhirResourceDao myCodeSystemDao; @Mock + private IFhirResourceDao mySearchParameterDao; + @Mock private IValidationSupport myIValidationSupport; + @Mock + private SearchParameterHelper mySearchParameterHelper; + @Mock + private SearchParameterMap mySearchParameterMap; + @Mock + private JpaStorageSettings myStorageSettings; @Spy private FhirContext myCtx = FhirContext.forR4Cached(); @Spy @@ -77,116 +94,80 @@ public class PackageInstallerSvcImplTest { @InjectMocks private PackageInstallerSvcImpl mySvc; + @Captor + private ArgumentCaptor mySearchParameterMapCaptor; + @Captor + private ArgumentCaptor myCodeSystemCaptor; + @Captor + private ArgumentCaptor mySearchParameterCaptor; + @Captor + private ArgumentCaptor myRequestDetailsCaptor; + @Test public void testPackageCompatibility() { mySvc.assertFhirVersionsAreCompatible("R4", "R4B"); } - @Test - public void testValidForUpload_SearchParameterWithMetaParam() { - SearchParameter sp = new SearchParameter(); - sp.setCode("_id"); - assertFalse(mySvc.validForUpload(sp)); - } + @Nested + class ValidForUploadTest { + public static Stream parametersIsValidForUpload() { + SearchParameter sp1 = new SearchParameter(); + sp1.setCode("_id"); - @Test - public void testValidForUpload_SearchParameterWithNoBase() { - SearchParameter sp = new SearchParameter(); - sp.setCode("name"); - sp.setExpression("Patient.name"); - sp.setStatus(Enumerations.PublicationStatus.ACTIVE); - assertFalse(mySvc.validForUpload(sp)); - } + SearchParameter sp2 = new SearchParameter(); + sp2.setCode("name"); + sp2.setExpression("Patient.name"); + sp2.setStatus(Enumerations.PublicationStatus.ACTIVE); - @Test - public void testValidForUpload_SearchParameterWithNoExpression() { - SearchParameter sp = new SearchParameter(); - sp.setCode("name"); - sp.addBase("Patient"); - sp.setStatus(Enumerations.PublicationStatus.ACTIVE); - assertFalse(mySvc.validForUpload(sp)); - } + SearchParameter sp3 = new SearchParameter(); + sp3.setCode("name"); + sp3.addBase("Patient"); + sp3.setStatus(Enumerations.PublicationStatus.ACTIVE); + SearchParameter sp4 = new SearchParameter(); + sp4.setCode("name"); + sp4.addBase("Patient"); + sp4.setExpression("Patient.name"); + sp4.setStatus(Enumerations.PublicationStatus.ACTIVE); - @Test - public void testValidForUpload_GoodSearchParameter() { - SearchParameter sp = new SearchParameter(); - sp.setCode("name"); - sp.addBase("Patient"); - sp.setExpression("Patient.name"); - sp.setStatus(Enumerations.PublicationStatus.ACTIVE); - assertTrue(mySvc.validForUpload(sp)); - } + SearchParameter sp5 = new SearchParameter(); + sp5.setCode("name"); + sp5.addBase("Patient"); + sp5.setExpression("Patient.name"); + sp5.setStatus(Enumerations.PublicationStatus.DRAFT); - @Test - public void testValidForUpload_RequestedSubscription() { - Subscription.SubscriptionChannelComponent subscriptionChannelComponent = - new Subscription.SubscriptionChannelComponent() - .setType(Subscription.SubscriptionChannelType.RESTHOOK) - .setEndpoint("https://tinyurl.com/2p95e27r"); - Subscription subscription = new Subscription(); - subscription.setCriteria("Patient?name=smith"); - subscription.setChannel(subscriptionChannelComponent); - subscription.setStatus(Subscription.SubscriptionStatus.REQUESTED); - assertTrue(mySvc.validForUpload(subscription)); - } + return Stream.of( + arguments(sp1, false, false), + arguments(sp2, false, true), + arguments(sp3, false, true), + arguments(sp4, true, true), + arguments(sp5, true, false), + arguments(createSubscription(Subscription.SubscriptionStatus.REQUESTED), true, true), + arguments(createSubscription(Subscription.SubscriptionStatus.ERROR), true, false), + arguments(createSubscription(Subscription.SubscriptionStatus.ACTIVE), true, false), + arguments(createDocumentReference(Enumerations.DocumentReferenceStatus.ENTEREDINERROR), true, true), + arguments(createDocumentReference(Enumerations.DocumentReferenceStatus.NULL), true, false), + arguments(createDocumentReference(null), true, false), + arguments(createCommunication(Communication.CommunicationStatus.NOTDONE), true, true), + arguments(createCommunication(Communication.CommunicationStatus.NULL), true, false), + arguments(createCommunication(null), true, false)); + } - @Test - public void testValidForUpload_ErrorSubscription() { - Subscription.SubscriptionChannelComponent subscriptionChannelComponent = - new Subscription.SubscriptionChannelComponent() - .setType(Subscription.SubscriptionChannelType.RESTHOOK) - .setEndpoint("https://tinyurl.com/2p95e27r"); - Subscription subscription = new Subscription(); - subscription.setCriteria("Patient?name=smith"); - subscription.setChannel(subscriptionChannelComponent); - subscription.setStatus(Subscription.SubscriptionStatus.ERROR); - assertFalse(mySvc.validForUpload(subscription)); - } + @ParameterizedTest + @MethodSource(value = "parametersIsValidForUpload") + public void testValidForUpload_withResource(IBaseResource theResource, + boolean theTheMeetsOtherFilterCriteria, + boolean theMeetsStatusFilterCriteria) { + if (theTheMeetsOtherFilterCriteria) { + when(myStorageSettings.isValidateResourceStatusForPackageUpload()).thenReturn(true); + } + assertEquals(theTheMeetsOtherFilterCriteria && theMeetsStatusFilterCriteria, mySvc.validForUpload(theResource)); - @Test - public void testValidForUpload_ActiveSubscription() { - Subscription.SubscriptionChannelComponent subscriptionChannelComponent = - new Subscription.SubscriptionChannelComponent() - .setType(Subscription.SubscriptionChannelType.RESTHOOK) - .setEndpoint("https://tinyurl.com/2p95e27r"); - Subscription subscription = new Subscription(); - subscription.setCriteria("Patient?name=smith"); - subscription.setChannel(subscriptionChannelComponent); - subscription.setStatus(Subscription.SubscriptionStatus.ACTIVE); - assertFalse(mySvc.validForUpload(subscription)); - } - - @Test - public void testValidForUpload_DocumentRefStatusValuePresent() { - DocumentReference documentReference = new DocumentReference(); - documentReference.setStatus(Enumerations.DocumentReferenceStatus.ENTEREDINERROR); - assertTrue(mySvc.validForUpload(documentReference)); - } - - @Test - public void testValidForUpload_DocumentRefStatusValueNull() { - DocumentReference documentReference = new DocumentReference(); - documentReference.setStatus(Enumerations.DocumentReferenceStatus.NULL); - assertFalse(mySvc.validForUpload(documentReference)); - documentReference.setStatus(null); - assertFalse(mySvc.validForUpload(documentReference)); - } - - @Test - public void testValidForUpload_CommunicationStatusValuePresent() { - Communication communication = new Communication(); - communication.setStatus(Communication.CommunicationStatus.NOTDONE); - assertTrue(mySvc.validForUpload(communication)); - } - - @Test - public void testValidForUpload_CommunicationStatusValueNull() { - Communication communication = new Communication(); - communication.setStatus(Communication.CommunicationStatus.NULL); - assertFalse(mySvc.validForUpload(communication)); - communication.setStatus(null); - assertFalse(mySvc.validForUpload(communication)); + if (theTheMeetsOtherFilterCriteria) { + when(myStorageSettings.isValidateResourceStatusForPackageUpload()).thenReturn(false); + } + assertEquals(theTheMeetsOtherFilterCriteria, mySvc.validForUpload(theResource)); + } } @Test @@ -206,19 +187,7 @@ public class PackageInstallerSvcImplTest { cs.setUrl("http://my-code-system"); cs.setContent(CodeSystem.CodeSystemContentMode.COMPLETE); - NpmPackage pkg = createPackage(cs, PACKAGE_ID_1); - - when(myPackageVersionDao.findByPackageIdAndVersion(any(), any())).thenReturn(Optional.empty()); - when(myPackageCacheManager.installPackage(any())).thenReturn(pkg); - when(myDaoRegistry.getResourceDao(CodeSystem.class)).thenReturn(myCodeSystemDao); - when(myCodeSystemDao.search(any(), any())).thenReturn(new SimpleBundleProvider(existingCs)); - when(myCodeSystemDao.update(any(),any(RequestDetails.class))).thenReturn(new DaoMethodOutcome()); - - PackageInstallationSpec spec = new PackageInstallationSpec(); - spec.setName(PACKAGE_ID_1); - spec.setVersion(PACKAGE_VERSION); - spec.setInstallMode(PackageInstallationSpec.InstallModeEnum.STORE_AND_INSTALL); - spec.setPackageContents(packageToBytes(pkg)); + PackageInstallationSpec spec = setupResourceInPackage(existingCs, cs, myCodeSystemDao); // Test mySvc.install(spec); @@ -233,34 +202,132 @@ public class PackageInstallerSvcImplTest { assertEquals("existingcs", codeSystem.getIdPart()); } - @Nonnull - private static byte[] packageToBytes(NpmPackage pkg) throws IOException { - ByteArrayOutputStream stream = new ByteArrayOutputStream(); - pkg.save(stream); - byte[] bytes = stream.toByteArray(); - return bytes; + public enum InstallType { + CREATE, UPDATE_WITH_EXISTING, UPDATE, UPDATE_OVERRIDE } - @Captor - private ArgumentCaptor mySearchParameterMapCaptor; - @Captor - private ArgumentCaptor myCodeSystemCaptor; + public static List parameters() { + return List.of( + new Object[]{null, null, null, List.of("Patient"), InstallType.CREATE}, + new Object[]{null, null, "us-core-patient-given", List.of("Patient"), InstallType.UPDATE}, + new Object[]{"individual-given", List.of("Patient", "Practitioner"), "us-core-patient-given", List.of("Patient"), InstallType.UPDATE_WITH_EXISTING}, + new Object[]{"patient-given", List.of("Patient"), "us-core-patient-given", List.of("Patient"), InstallType.UPDATE_OVERRIDE} + ); + } + + @ParameterizedTest + @MethodSource("parameters") + public void testCreateOrUpdate_withSearchParameter(String theExistingId, Collection theExistingBase, + String theInstallId, Collection theInstallBase, + InstallType theInstallType) throws IOException { + // Setup + SearchParameter existingSP = null; + if (theExistingId != null) { + existingSP = createSearchParameter(theExistingId, theExistingBase); + } + SearchParameter installSP = createSearchParameter(theInstallId, theInstallBase); + PackageInstallationSpec spec = setupResourceInPackage(existingSP, installSP, mySearchParameterDao); + + // Test + mySvc.install(spec); + + // Verify + if (theInstallType == InstallType.CREATE) { + verify(mySearchParameterDao, times(1)).create(mySearchParameterCaptor.capture(), myRequestDetailsCaptor.capture()); + } else if (theInstallType == InstallType.UPDATE_WITH_EXISTING){ + verify(mySearchParameterDao, times(2)).update(mySearchParameterCaptor.capture(), myRequestDetailsCaptor.capture()); + } else { + verify(mySearchParameterDao, times(1)).update(mySearchParameterCaptor.capture(), myRequestDetailsCaptor.capture()); + } + + Iterator iteratorSP = mySearchParameterCaptor.getAllValues().iterator(); + if (theInstallType == InstallType.UPDATE_WITH_EXISTING) { + SearchParameter capturedSP = iteratorSP.next(); + assertEquals(theExistingId, capturedSP.getIdPart()); + List expectedBase = new ArrayList<>(theExistingBase); + expectedBase.removeAll(theInstallBase); + assertEquals(expectedBase, capturedSP.getBase().stream().map(CodeType::getCode).toList()); + } + SearchParameter capturedSP = iteratorSP.next(); + if (theInstallType == InstallType.UPDATE_OVERRIDE) { + assertEquals(theExistingId, capturedSP.getIdPart()); + } else { + assertEquals(theInstallId, capturedSP.getIdPart()); + } + assertEquals(theInstallBase, capturedSP.getBase().stream().map(CodeType::getCode).toList()); + } + + private PackageInstallationSpec setupResourceInPackage(IBaseResource myExistingResource, IBaseResource myInstallResource, + IFhirResourceDao myFhirResourceDao) throws IOException { + NpmPackage pkg = createPackage(myInstallResource, myInstallResource.getClass().getSimpleName()); + + when(myPackageVersionDao.findByPackageIdAndVersion(any(), any())).thenReturn(Optional.empty()); + when(myPackageCacheManager.installPackage(any())).thenReturn(pkg); + when(myDaoRegistry.getResourceDao(myInstallResource.getClass())).thenReturn(myFhirResourceDao); + when(myFhirResourceDao.search(any(), any())).thenReturn(myExistingResource != null ? + new SimpleBundleProvider(myExistingResource) : new SimpleBundleProvider()); + if (myInstallResource.getClass().getSimpleName().equals("SearchParameter")) { + when(mySearchParameterHelper.buildSearchParameterMapFromCanonical(any())).thenReturn(Optional.of(mySearchParameterMap)); + } + + PackageInstallationSpec spec = new PackageInstallationSpec(); + spec.setName(PACKAGE_ID_1); + spec.setVersion(PACKAGE_VERSION); + spec.setInstallMode(PackageInstallationSpec.InstallModeEnum.STORE_AND_INSTALL); + ByteArrayOutputStream stream = new ByteArrayOutputStream(); + pkg.save(stream); + spec.setPackageContents(stream.toByteArray()); + + return spec; + } @Nonnull - private NpmPackage createPackage(CodeSystem cs, String packageId) throws IOException { + private NpmPackage createPackage(IBaseResource theResource, String theResourceType) { PackageGenerator manifestGenerator = new PackageGenerator(); - manifestGenerator.name(packageId); + manifestGenerator.name(PACKAGE_ID_1); manifestGenerator.version(PACKAGE_VERSION); manifestGenerator.description("a package"); manifestGenerator.fhirVersions(List.of(FhirVersionEnum.R4.getFhirVersionString())); + String csString = myCtx.newJsonParser().encodeResourceToString(theResource); NpmPackage pkg = NpmPackage.empty(manifestGenerator); - - String csString = myCtx.newJsonParser().encodeResourceToString(cs); - pkg.addFile("package", "cs.json", csString.getBytes(StandardCharsets.UTF_8), "CodeSystem"); + pkg.addFile("package", theResourceType + ".json", csString.getBytes(StandardCharsets.UTF_8), theResourceType); return pkg; } + private static SearchParameter createSearchParameter(String theId, Collection theBase) { + SearchParameter searchParameter = new SearchParameter(); + if (theId != null) { + searchParameter.setId(new IdType("SearchParameter", theId)); + } + searchParameter.setCode("someCode"); + theBase.forEach(base -> searchParameter.getBase().add(new CodeType(base))); + searchParameter.setExpression("someExpression"); + return searchParameter; + } + private static Subscription createSubscription(Subscription.SubscriptionStatus theSubscriptionStatus) { + Subscription.SubscriptionChannelComponent subscriptionChannelComponent = + new Subscription.SubscriptionChannelComponent() + .setType(Subscription.SubscriptionChannelType.RESTHOOK) + .setEndpoint("https://tinyurl.com/2p95e27r"); + Subscription subscription = new Subscription(); + subscription.setCriteria("Patient?name=smith"); + subscription.setChannel(subscriptionChannelComponent); + subscription.setStatus(theSubscriptionStatus); + return subscription; + } + + private static DocumentReference createDocumentReference(Enumerations.DocumentReferenceStatus theDocumentStatus) { + DocumentReference documentReference = new DocumentReference(); + documentReference.setStatus(theDocumentStatus); + return documentReference; + } + + private static Communication createCommunication(Communication.CommunicationStatus theCommunicationStatus) { + Communication communication = new Communication(); + communication.setStatus(theCommunicationStatus); + return communication; + } } diff --git a/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/provider/r4/MemberMatcherR4HelperTest.java b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/provider/r4/MemberMatcherR4HelperTest.java deleted file mode 100644 index fa9384d5288..00000000000 --- a/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/provider/r4/MemberMatcherR4HelperTest.java +++ /dev/null @@ -1,578 +0,0 @@ -package ca.uhn.fhir.jpa.provider.r4; - -import ca.uhn.fhir.context.FhirContext; -import ca.uhn.fhir.i18n.Msg; -import ca.uhn.fhir.jpa.api.dao.IFhirResourceDao; -import ca.uhn.fhir.jpa.searchparam.SearchParameterMap; -import ca.uhn.fhir.model.api.IQueryParameterType; -import ca.uhn.fhir.model.primitive.IdDt; -import ca.uhn.fhir.rest.api.server.IBundleProvider; -import ca.uhn.fhir.rest.api.server.RequestDetails; -import ca.uhn.fhir.rest.api.server.SystemRequestDetails; -import ca.uhn.fhir.rest.server.SimpleBundleProvider; -import ca.uhn.test.util.LogbackCaptureTestExtension; -import ch.qos.logback.classic.Level; -import ch.qos.logback.classic.Logger; -import com.google.common.collect.Lists; -import org.hl7.fhir.r4.model.CodeableConcept; -import org.hl7.fhir.r4.model.Coding; -import org.hl7.fhir.r4.model.Consent; -import org.hl7.fhir.r4.model.Coverage; -import org.hl7.fhir.r4.model.DateType; -import org.hl7.fhir.r4.model.HumanName; -import org.hl7.fhir.r4.model.Identifier; -import org.hl7.fhir.r4.model.Parameters; -import org.hl7.fhir.r4.model.Patient; -import org.hl7.fhir.r4.model.Reference; -import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.Nested; -import org.junit.jupiter.api.Test; -import org.junit.jupiter.api.extension.ExtendWith; -import org.junit.jupiter.api.extension.RegisterExtension; -import org.mockito.Answers; -import org.mockito.ArgumentCaptor; -import org.mockito.Captor; -import org.mockito.Mock; -import org.mockito.Spy; -import org.mockito.junit.jupiter.MockitoExtension; - -import java.util.Collections; -import java.util.List; -import java.util.Optional; - -import static ca.uhn.fhir.rest.api.Constants.PARAM_CONSENT; -import static ca.uhn.fhir.rest.api.Constants.PARAM_MEMBER_IDENTIFIER; -import static ca.uhn.fhir.rest.api.Constants.PARAM_MEMBER_PATIENT; -import static ca.uhn.fhir.rest.api.Constants.PARAM_NEW_COVERAGE; -import static org.hamcrest.MatcherAssert.assertThat; -import static org.hamcrest.Matchers.empty; -import static org.hamcrest.Matchers.startsWith; -import static org.junit.jupiter.api.Assertions.assertEquals; -import static org.junit.jupiter.api.Assertions.assertFalse; -import static org.junit.jupiter.api.Assertions.assertNotNull; -import static org.junit.jupiter.api.Assertions.assertSame; -import static org.junit.jupiter.api.Assertions.assertTrue; -import static org.mockito.ArgumentMatchers.any; -import static org.mockito.ArgumentMatchers.isA; -import static org.mockito.ArgumentMatchers.same; -import static org.mockito.Mockito.times; -import static org.mockito.Mockito.verify; -import static org.mockito.Mockito.when; - -@ExtendWith(MockitoExtension.class) -public class MemberMatcherR4HelperTest { - - @RegisterExtension - LogbackCaptureTestExtension myLogCapture = new LogbackCaptureTestExtension((Logger) MemberMatcherR4Helper.ourLog, Level.TRACE); - @Spy - private final FhirContext myFhirContext = FhirContext.forR4Cached(); - @Mock - private IFhirResourceDao myCoverageDao; - @Mock - private IFhirResourceDao myPatientDao; - @Mock - private IFhirResourceDao myConsentDao; - - private MemberMatcherR4Helper myHelper; - RequestDetails myRequestDetails = new SystemRequestDetails(); - - @BeforeEach - public void before() { - myHelper = new MemberMatcherR4Helper( - myFhirContext, - myCoverageDao, - myPatientDao, - myConsentDao, - null // extension provider - ); - } - - @Mock private Coverage myCoverageToMatch; - @Mock private IBundleProvider myBundleProvider; - - private final Coverage myMatchedCoverage = new Coverage() - .setBeneficiary(new Reference("Patient/123")); - private final Identifier myMatchingIdentifier = new Identifier() - .setSystem("identifier-system").setValue("identifier-value"); - - @Captor - ArgumentCaptor mySearchParameterMapCaptor; - - @Test - void findMatchingCoverageMatchByIdReturnsMatched() { - when(myCoverageToMatch.getId()).thenReturn("cvg-to-match-id"); - when(myCoverageDao.search(isA(SearchParameterMap.class), same(myRequestDetails))).thenReturn(myBundleProvider); - when(myBundleProvider.getAllResources()).thenReturn(Collections.singletonList(myMatchedCoverage)); - - Optional result = myHelper.findMatchingCoverage(myCoverageToMatch, myRequestDetails); - - assertEquals(Optional.of(myMatchedCoverage), result); - verify(myCoverageDao).search(mySearchParameterMapCaptor.capture(), same(myRequestDetails)); - SearchParameterMap spMap = mySearchParameterMapCaptor.getValue(); - assertTrue(spMap.containsKey("_id")); - List> listListParams = spMap.get("_id"); - assertEquals(1, listListParams.size()); - assertEquals(1, listListParams.get(0).size()); - IQueryParameterType param = listListParams.get(0).get(0); - assertEquals("cvg-to-match-id", param.getValueAsQueryToken(myFhirContext)); - } - - - @Test - void findMatchingCoverageMatchByIdentifierReturnsMatched() { - when(myCoverageToMatch.getId()).thenReturn("non-matching-id"); - when(myCoverageToMatch.getIdentifier()).thenReturn(Collections.singletonList(myMatchingIdentifier)); - when(myCoverageDao.search(isA(SearchParameterMap.class), same(myRequestDetails))).thenReturn(myBundleProvider); - when(myBundleProvider.getAllResources()).thenReturn( - Collections.emptyList(), Collections.singletonList(myMatchedCoverage)); - - Optional result = myHelper.findMatchingCoverage(myCoverageToMatch, myRequestDetails); - - assertEquals(Optional.of(myMatchedCoverage), result); - verify(myCoverageDao, times(2)).search(mySearchParameterMapCaptor.capture(), same(myRequestDetails)); - List spMap = mySearchParameterMapCaptor.getAllValues(); - assertTrue(spMap.get(0).containsKey("_id")); - assertTrue(spMap.get(1).containsKey("identifier")); - List> listListParams = spMap.get(1).get("identifier"); - assertEquals(1, listListParams.size()); - assertEquals(1, listListParams.get(0).size()); - IQueryParameterType param = listListParams.get(0).get(0); - assertEquals(myMatchingIdentifier.getSystem() + "|" + myMatchingIdentifier.getValue(), - param.getValueAsQueryToken(myFhirContext)); - } - - - @Test - void findMatchingCoverageNoMatchReturnsEmpty() { - when(myCoverageToMatch.getId()).thenReturn("non-matching-id"); - when(myCoverageToMatch.getIdentifier()).thenReturn(Collections.singletonList(myMatchingIdentifier)); - when(myCoverageDao.search(any(SearchParameterMap.class), same(myRequestDetails))).thenReturn(myBundleProvider); - when(myBundleProvider.getAllResources()).thenReturn(Collections.emptyList(), Collections.emptyList()); - - Optional result = myHelper.findMatchingCoverage(myCoverageToMatch, myRequestDetails); - - assertFalse(result.isPresent()); - } - - - @Test - void buildSuccessReturnParameters() { - Identifier identifier = new Identifier(); - CodeableConcept identifierType = new CodeableConcept(); - identifierType.addCoding(new Coding("", "MB", "")); - identifier.setType(identifierType); - Patient patient = new Patient(); - Coverage coverage = new Coverage(); - Consent consent = new Consent(); - patient.addIdentifier(identifier); - - Parameters result = myHelper.buildSuccessReturnParameters(patient, coverage, consent); - - assertEquals(PARAM_MEMBER_PATIENT, result.getParameter().get(0).getName()); - assertEquals(patient, result.getParameter().get(0).getResource()); - - assertEquals(PARAM_NEW_COVERAGE, result.getParameter().get(1).getName()); - assertEquals(coverage, result.getParameter().get(1).getResource()); - - assertEquals(PARAM_CONSENT, result.getParameter().get(2).getName()); - assertEquals(consent, result.getParameter().get(2).getResource()); - - assertEquals(PARAM_MEMBER_IDENTIFIER, result.getParameter().get(3).getName()); - assertEquals(identifier, result.getParameter().get(3).getValue()); - } - - @Test - void buildNotSuccessReturnParameters_IncorrectPatientIdentifier() { - Identifier identifier = new Identifier(); - Patient patient = new Patient(); - Coverage coverage = new Coverage(); - Consent consent = new Consent(); - patient.addIdentifier(identifier); - - try { - myHelper.buildSuccessReturnParameters(patient, coverage, consent); - } catch (Exception e) { - assertThat(e.getMessage(), startsWith(Msg.code(2219))); - } - } - - @Test - void addMemberIdentifierToMemberPatient() { - Identifier originalIdentifier = new Identifier() - .setSystem("original-identifier-system").setValue("original-identifier-value"); - - Identifier newIdentifier = new Identifier() - .setSystem("new-identifier-system").setValue("new-identifier-value"); - - Patient patient = new Patient().setIdentifier(Lists.newArrayList(originalIdentifier)); - - myHelper.addMemberIdentifierToMemberPatient(patient, newIdentifier); - - assertEquals(2, patient.getIdentifier().size()); - - assertEquals("original-identifier-system", patient.getIdentifier().get(0).getSystem()); - assertEquals("original-identifier-value", patient.getIdentifier().get(0).getValue()); - - assertEquals("new-identifier-system", patient.getIdentifier().get(1).getSystem()); - assertEquals("new-identifier-value", patient.getIdentifier().get(1).getValue()); - } - - /** - * Testing multiple scenarios for getting patient resource from coverage's plan beneficiary - */ - @Nested - public class TestGetBeneficiaryPatient { - - @Mock(answer = Answers.RETURNS_DEEP_STUBS) - private Coverage coverage; - - - @Test - void noBeneficiaryOrBeneficiaryTargetReturnsEmpty() { - when(coverage.getBeneficiaryTarget()).thenReturn(null); - when(coverage.getBeneficiary()).thenReturn(null); - - Optional result = myHelper.getBeneficiaryPatient(coverage, myRequestDetails); - - assertFalse(result.isPresent()); - } - - - @Test - void beneficiaryTargetWithNoIdentifierReturnsEmpty() { - when(coverage.getBeneficiary()).thenReturn(null); - when(coverage.getBeneficiaryTarget()).thenReturn(new Patient()); - - Optional result = myHelper.getBeneficiaryPatient(coverage, myRequestDetails); - - assertFalse(result.isPresent()); - } - - - @Test - void beneficiaryTargetWithIdentifierReturnsBeneficiary() { - Patient patient = new Patient().setIdentifier(Collections.singletonList(new Identifier())); - when(coverage.getBeneficiaryTarget()).thenReturn(patient); - - Optional result = myHelper.getBeneficiaryPatient(coverage, myRequestDetails); - - assertTrue(result.isPresent()); - assertEquals(patient, result.get()); - } - - - @Test - void beneficiaryReferenceResourceReturnsBeneficiary() { - Patient patient = new Patient().setIdentifier(Collections.singletonList(new Identifier())); - when(coverage.getBeneficiaryTarget()).thenReturn(null); - when(coverage.getBeneficiary().getResource()).thenReturn(patient); - - Optional result = myHelper.getBeneficiaryPatient(coverage, myRequestDetails); - - assertTrue(result.isPresent()); - assertEquals(patient, result.get()); - } - - - @Test - void beneficiaryReferenceNoResourceOrReferenceReturnsEmpty() { - when(coverage.getBeneficiaryTarget()).thenReturn(null); - when(coverage.getBeneficiary()).thenReturn(new Reference()); - - Optional result = myHelper.getBeneficiaryPatient(coverage, myRequestDetails); - - assertFalse(result.isPresent()); - } - - - @Test - void beneficiaryReferenceReferenceReturnsReadPatient() { - when(coverage.getBeneficiaryTarget()).thenReturn(null); - when(coverage.getBeneficiary().getResource()).thenReturn(null); - when(coverage.getBeneficiary().getReference()).thenReturn("patient-id"); - - myHelper.getBeneficiaryPatient(coverage, myRequestDetails); - - verify(myPatientDao).read(new IdDt("patient-id"), myRequestDetails); - } - } - - /** - * Testing multiple scenarios for validity of Patient Member parameter - */ - @Nested - public class TestValidPatientMember { - - private final Patient patient = new Patient(); - - @Test - void noPatientFoundFromContractReturnsFalse() { - boolean result = myHelper.validPatientMember(null, patient, myRequestDetails); - assertFalse(result); - } - - @Test - void noPatientFoundFromPatientMemberReturnsFalse() { - boolean result = myHelper.validPatientMember(patient, null, myRequestDetails); - assertFalse(result); - } - - @Test - void noMatchingFamilyNameReturnsFalse() { - Patient patientFromMemberMatch = getPatientWithNoIDParm("Person", "2020-01-01"); - Patient patientFromContractFound = getPatientWithIDParm("A123", "Smith", "2020-01-01"); - when(myPatientDao.search(any(SearchParameterMap.class), same(myRequestDetails))).thenAnswer(t -> { - IBundleProvider provider = new SimpleBundleProvider(Collections.singletonList(new Patient().setId("B123"))); - return provider; - }); - boolean result = myHelper.validPatientMember(patientFromContractFound, patientFromMemberMatch, myRequestDetails); - assertFalse(result); - } - - - @Test - void noMatchingBirthdayReturnsFalse() { - Patient patientFromMemberMatch = getPatientWithNoIDParm("Person", "1990-01-01"); - Patient patientFromContractFound = getPatientWithIDParm("A123", "Person", "2020-01-01"); - when(myPatientDao.search(any(SearchParameterMap.class), same(myRequestDetails))).thenAnswer(t -> { - IBundleProvider provider = new SimpleBundleProvider(Collections.singletonList(new Patient().setId("B123"))); - return provider; - }); - boolean result = myHelper.validPatientMember(patientFromContractFound, patientFromMemberMatch, myRequestDetails); - assertFalse(result); - } - - @Test - void noMatchingFieldsReturnsFalse() { - Patient patientFromMemberMatch = getPatientWithNoIDParm("Person", "1990-01-01"); - Patient patientFromContractFound = getPatientWithIDParm("A123", "Smith", "2020-01-01"); - when(myPatientDao.search(any(SearchParameterMap.class), same(myRequestDetails))).thenAnswer(t -> { - IBundleProvider provider = new SimpleBundleProvider(Collections.singletonList(new Patient().setId("B123"))); - return provider; - }); - boolean result = myHelper.validPatientMember(patientFromContractFound, patientFromMemberMatch, myRequestDetails); - assertFalse(result); - } - - @Test - void patientMatchingReturnTrue() { - Patient patientFromMemberMatch = getPatientWithNoIDParm("Person", "2020-01-01"); - Patient patientFromContractFound = getPatientWithIDParm("A123", "Person", "2020-01-01"); - when(myPatientDao.search(any(SearchParameterMap.class), same(myRequestDetails))).thenAnswer(t -> { - IBundleProvider provider = new SimpleBundleProvider(Collections.singletonList(patientFromContractFound)); - return provider; - }); - boolean result = myHelper.validPatientMember(patientFromContractFound, patientFromMemberMatch, myRequestDetails); - assertTrue(result); - } - - private Patient getPatientWithNoIDParm(String familyName, String birthdate) { - Patient patient = new Patient().setName(Lists.newArrayList(new HumanName() - .setUse(HumanName.NameUse.OFFICIAL).setFamily(familyName))) - .setBirthDateElement(new DateType(birthdate)); - return patient; - } - - private Patient getPatientWithIDParm(String id, String familyName, String birthdate) { - Patient patient = getPatientWithNoIDParm(familyName, birthdate); - patient.setId(id); - return patient; - } - - } - - /** - * Testing multiple scenarios for consent's policy data that is defined in - * https://build.fhir.org/ig/HL7/davinci-ehrx/StructureDefinition-hrex-consent.html#notes - */ - @Nested - public class TestValidvalidConsentDataAccess { - - private Consent consent; - - @Test - void noConsentProfileFoundReturnsFalse() { - consent = new Consent(); - boolean result = myHelper.validConsentDataAccess(consent); - assertFalse(result); - } - - @Test - void noDataAccessValueProvidedReturnsFalse() { - consent = getConsent(); - boolean result = myHelper.validConsentDataAccess(consent); - assertFalse(result); - } - - @Test - void wrongDataAccessValueProvidedReturnsFalse() { - consent = getConsent(); - consent.addPolicy(constructConsentPolicyComponent("#access_data")); - boolean result = myHelper.validConsentDataAccess(consent); - assertFalse(result); - } - - @Test - void regularDataAccessWithRegularNotAllowedReturnsFalse() { - consent = getConsent(); - consent.addPolicy(constructConsentPolicyComponent("#regular")); - boolean result = myHelper.validConsentDataAccess(consent); - assertFalse(result); - } - - @Test - void regularDataAccessWithRegularAllowedReturnsTrue() { - myHelper.setRegularFilterSupported(true); - consent = getConsent(); - consent.addPolicy(constructConsentPolicyComponent("#regular")); - boolean result = myHelper.validConsentDataAccess(consent); - assertTrue(result); - } - - @Test - void sensitiveDataAccessAllowedReturnsTrue() { - consent = getConsent(); - consent.addPolicy(constructConsentPolicyComponent("#sensitive")); - boolean result = myHelper.validConsentDataAccess(consent); - assertTrue(result); - } - - @Test - void multipleSensitivePolicyDataAccessAllowedReturnsTrue() { - consent = getConsent(); - consent.addPolicy(constructConsentPolicyComponent("#sensitive")); - consent.addPolicy(constructConsentPolicyComponent("#sensitive")); - boolean result = myHelper.validConsentDataAccess(consent); - assertTrue(result); - } - - @Test - void multipleRegularPolicyDataAccessWithRegularAllowedReturnsTrue() { - myHelper.setRegularFilterSupported(true); - consent = getConsent(); - consent.addPolicy(constructConsentPolicyComponent("#regular")); - consent.addPolicy(constructConsentPolicyComponent("#regular")); - boolean result = myHelper.validConsentDataAccess(consent); - assertTrue(result); - } - - @Test - void multipleMixedPolicyDataAccessWithRegularNotAllowedReturnsFalse() { - consent = getConsent(); - consent.addPolicy(constructConsentPolicyComponent("#regular")); - consent.addPolicy(constructConsentPolicyComponent("#sensitive")); - boolean result = myHelper.validConsentDataAccess(consent); - assertFalse(result); - } - - @Test - void multipleMixedPolicyDataAccessWithRegularAllowedReturnsTrue() { - myHelper.setRegularFilterSupported(true); - consent = getConsent(); - consent.addPolicy(constructConsentPolicyComponent("#regular")); - consent.addPolicy(constructConsentPolicyComponent("#sensitive")); - boolean result = myHelper.validConsentDataAccess(consent); - assertTrue(result); - } - } - - private Consent getConsent() { - Consent consent = new Consent(); - consent.getPerformer().add(new Reference("Patient/1")); - return consent; - } - - private Consent.ConsentPolicyComponent constructConsentPolicyComponent(String uriAccess) { - String uri = "http://hl7.org/fhir/us/davinci-hrex/StructureDefinition-hrex-consent.html"; - return new Consent.ConsentPolicyComponent().setUri(uri + uriAccess); - } - - private Patient createPatientForMemberMatchUpdate(boolean addIdentifier) { - Patient patient = new Patient(); - patient.setId("Patient/RED"); - Identifier identifier = new Identifier(); - if (addIdentifier) { - CodeableConcept identifierType = new CodeableConcept(); - identifierType.addCoding(new Coding("", "MB", "")); - identifier.setType(identifierType); - } - identifier.setValue("RED-Patient"); - patient.addIdentifier(identifier); - - return patient; - } - - @Nested - public class MemberMatchWithoutConsentProvider { - @Captor - private ArgumentCaptor myDaoCaptor; - - @Test - public void updateConsentForMemberMatch_noProvider_addsIdentifierUpdatePatientButNotExtensionAndSaves() { - // setup - Consent consent = getConsent(); - consent.addPolicy(constructConsentPolicyComponent("#sensitive")); - Patient patient = createPatientForMemberMatchUpdate(false); - Patient memberPatient = createPatientForMemberMatchUpdate(true); - - // test - myHelper.updateConsentForMemberMatch(consent, patient, memberPatient, myRequestDetails); - - // verify - - verify(myConsentDao).create(myDaoCaptor.capture(), same(myRequestDetails)); - Consent saved = myDaoCaptor.getValue(); - // check consent identifier - assertEquals(1, saved.getIdentifier().size()); - assertEquals(MemberMatcherR4Helper.CONSENT_IDENTIFIER_CODE_SYSTEM, saved.getIdentifier().get(0).getSystem()); - assertNotNull(saved.getIdentifier().get(0).getValue()); - assertEquals(saved.getIdentifier().get(0).getValue(), memberPatient.getIdentifier().get(0).getValue()); - - // check consent patient info - String patientRef = patient.getIdElement().toUnqualifiedVersionless().getValue(); - assertEquals(patientRef, saved.getPatient().getReference()); - assertEquals(patientRef, saved.getPerformer().get(0).getReference()); - - assertThat(myLogCapture.getLogEvents(), empty()); - - } - } - - @Nested - public class MemberMatchWithConsentProvider { - @Mock - private IConsentExtensionProvider myExtensionProvider; - @Captor - private ArgumentCaptor myHookCaptor; - - @BeforeEach - public void before() { - myHelper = new MemberMatcherR4Helper( - myFhirContext, - myCoverageDao, - myPatientDao, - myConsentDao, - myExtensionProvider - ); - } - - @Test - public void addClientIdAsExtensionToConsentIfAvailable_withProvider_addsExtensionAndSaves() { - // setup - Consent consent = getConsent(); - consent.addPolicy(constructConsentPolicyComponent("#sensitive")); - consent.setId("Consent/RED"); - Patient patient = createPatientForMemberMatchUpdate(false); - Patient memberPatient = createPatientForMemberMatchUpdate(true); - - // test - myHelper.updateConsentForMemberMatch(consent, patient, memberPatient, myRequestDetails); - - // verify - verify(myExtensionProvider).accept(myHookCaptor.capture()); - - assertSame(consent, myHookCaptor.getValue()); - assertThat(myLogCapture.getLogEvents(), empty()); - } - } -} diff --git a/hapi-fhir-jpaserver-elastic-test-utilities/pom.xml b/hapi-fhir-jpaserver-elastic-test-utilities/pom.xml index aa1db0b0a66..646a34bf07a 100644 --- a/hapi-fhir-jpaserver-elastic-test-utilities/pom.xml +++ b/hapi-fhir-jpaserver-elastic-test-utilities/pom.xml @@ -6,7 +6,7 @@ ca.uhn.hapi.fhir hapi-deployable-pom - 6.9.7-SNAPSHOT + 6.11.7-SNAPSHOT ../hapi-deployable-pom/pom.xml @@ -71,38 +71,6 @@ org.apache.derby derby - - org.eclipse.jetty - jetty-servlets - - - org.eclipse.jetty - jetty-servlet - - - org.eclipse.jetty - jetty-server - - - org.eclipse.jetty - jetty-util - - - org.eclipse.jetty - jetty-webapp - - - org.eclipse.jetty.websocket - websocket-jetty-api - - - org.eclipse.jetty.websocket - websocket-core-client - - - org.eclipse.jetty.websocket - websocket-jetty-server - org.springframework.boot spring-boot-test @@ -161,12 +129,12 @@ mockito-core - com.helger - ph-schematron + com.helger.schematron + ph-schematron-api - com.helger - ph-commons + com.helger.schematron + ph-schematron-xslt org.testcontainers diff --git a/hapi-fhir-jpaserver-elastic-test-utilities/src/test/java/ca/uhn/fhir/jpa/config/ElasticsearchWithPrefixConfig.java b/hapi-fhir-jpaserver-elastic-test-utilities/src/test/java/ca/uhn/fhir/jpa/config/ElasticsearchWithPrefixConfig.java index 7d93aa506e4..d882b4ed076 100644 --- a/hapi-fhir-jpaserver-elastic-test-utilities/src/test/java/ca/uhn/fhir/jpa/config/ElasticsearchWithPrefixConfig.java +++ b/hapi-fhir-jpaserver-elastic-test-utilities/src/test/java/ca/uhn/fhir/jpa/config/ElasticsearchWithPrefixConfig.java @@ -3,6 +3,7 @@ package ca.uhn.fhir.jpa.config; import ca.uhn.fhir.context.ConfigurationException; import ca.uhn.fhir.context.FhirContext; import ca.uhn.fhir.jpa.api.config.JpaStorageSettings; +import ca.uhn.fhir.jpa.config.util.HapiEntityManagerFactoryUtil; import ca.uhn.fhir.jpa.dao.r4.ElasticsearchPrefixTest; import ca.uhn.fhir.jpa.model.dialect.HapiFhirH2Dialect; import ca.uhn.fhir.jpa.search.HapiHSearchAnalysisConfigurers; @@ -11,14 +12,12 @@ import ca.uhn.fhir.jpa.search.lastn.ElasticsearchRestClientFactory; import ca.uhn.fhir.jpa.test.config.BlockLargeNumbersOfParamsListener; import ca.uhn.fhir.jpa.test.config.TestHSearchAddInConfig; import ca.uhn.fhir.jpa.util.CurrentThreadCaptureQueriesListener; +import co.elastic.clients.elasticsearch.ElasticsearchClient; +import co.elastic.clients.elasticsearch.indices.PutTemplateResponse; +import co.elastic.clients.json.JsonData; import net.ttddyy.dsproxy.listener.logging.SLF4JLogLevel; import net.ttddyy.dsproxy.support.ProxyDataSourceBuilder; import org.apache.commons.dbcp2.BasicDataSource; -import org.elasticsearch.action.support.master.AcknowledgedResponse; -import org.elasticsearch.client.RequestOptions; -import org.elasticsearch.client.RestHighLevelClient; -import org.elasticsearch.client.indices.PutIndexTemplateRequest; -import org.elasticsearch.common.settings.Settings; import org.hibernate.jpa.HibernatePersistenceProvider; import org.hibernate.search.backend.elasticsearch.cfg.ElasticsearchBackendSettings; import org.hibernate.search.backend.elasticsearch.cfg.ElasticsearchIndexSettings; @@ -36,7 +35,7 @@ import org.testcontainers.elasticsearch.ElasticsearchContainer; import javax.sql.DataSource; import java.io.IOException; -import java.util.Arrays; +import java.util.Map; import java.util.Properties; import java.util.concurrent.TimeUnit; @@ -65,11 +64,9 @@ public class ElasticsearchWithPrefixConfig { } @Bean - public LocalContainerEntityManagerFactoryBean entityManagerFactory(ConfigurableListableBeanFactory theConfigurableListableBeanFactory) { - LocalContainerEntityManagerFactoryBean retVal = new HapiFhirLocalContainerEntityManagerFactoryBean(theConfigurableListableBeanFactory); - retVal.setJpaDialect(new HapiFhirHibernateJpaDialect(fhirContext().getLocalizer())); - retVal.setPackagesToScan("ca.uhn.fhir.jpa.model.entity", "ca.uhn.fhir.jpa.entity"); - retVal.setPersistenceProvider(new HibernatePersistenceProvider()); + public LocalContainerEntityManagerFactoryBean entityManagerFactory(ConfigurableListableBeanFactory theConfigurableListableBeanFactory, FhirContext theFhirContext, JpaStorageSettings theStorageSettings) { + LocalContainerEntityManagerFactoryBean retVal = HapiEntityManagerFactoryUtil.newEntityManagerFactory(theConfigurableListableBeanFactory, theFhirContext, theStorageSettings); + retVal.setPersistenceUnitName("PU_HapiFhirJpaR4"); retVal.setDataSource(dataSource()); retVal.setJpaProperties(jpaProperties()); @@ -126,14 +123,15 @@ public class ElasticsearchWithPrefixConfig { //This tells elasticsearch to use our custom index naming strategy. extraProperties.put(BackendSettings.backendKey(ElasticsearchBackendSettings.LAYOUT_STRATEGY), IndexNamePrefixLayoutStrategy.class.getName()); - PutIndexTemplateRequest ngramTemplate = new PutIndexTemplateRequest("ngram-template") - .patterns(Arrays.asList("*resourcetable-*", "*termconcept-*")) - .settings(Settings.builder().put("index.max_ngram_diff", 50)); - try { - RestHighLevelClient elasticsearchHighLevelRestClient = ElasticsearchRestClientFactory.createElasticsearchHighLevelRestClient("http", host + ":" + httpPort, "", ""); - AcknowledgedResponse acknowledgedResponse = elasticsearchHighLevelRestClient.indices().putTemplate(ngramTemplate, RequestOptions.DEFAULT); - assert acknowledgedResponse.isAcknowledged(); + ElasticsearchClient elasticsearchHighLevelRestClient = ElasticsearchRestClientFactory.createElasticsearchHighLevelRestClient("http", host + ":" + httpPort, "", ""); + PutTemplateResponse acknowledgedResponse = elasticsearchHighLevelRestClient + .indices() + .putTemplate(b -> b + .name("ngram-template") + .indexPatterns("*resourcetable-*", "*termconcept-*") + .settings(Map.of("index.max_ngram_diff", JsonData.of(50)))); + assert acknowledgedResponse.acknowledged(); } catch (IOException theE) { theE.printStackTrace(); throw new ConfigurationException("Couldn't connect to the elasticsearch server to create necessary templates. Ensure the Elasticsearch user has permissions to create templates."); diff --git a/hapi-fhir-jpaserver-elastic-test-utilities/src/test/java/ca/uhn/fhir/jpa/dao/r4/ElasticsearchPrefixTest.java b/hapi-fhir-jpaserver-elastic-test-utilities/src/test/java/ca/uhn/fhir/jpa/dao/r4/ElasticsearchPrefixTest.java index 2f85e652a52..f9bc8977988 100644 --- a/hapi-fhir-jpaserver-elastic-test-utilities/src/test/java/ca/uhn/fhir/jpa/dao/r4/ElasticsearchPrefixTest.java +++ b/hapi-fhir-jpaserver-elastic-test-utilities/src/test/java/ca/uhn/fhir/jpa/dao/r4/ElasticsearchPrefixTest.java @@ -3,11 +3,10 @@ package ca.uhn.fhir.jpa.dao.r4; import ca.uhn.fhir.jpa.config.ElasticsearchWithPrefixConfig; import ca.uhn.fhir.jpa.search.lastn.ElasticsearchRestClientFactory; import ca.uhn.fhir.test.utilities.docker.RequiresDocker; -import org.apache.http.util.EntityUtils; -import org.elasticsearch.client.Request; -import org.elasticsearch.client.Response; -import org.elasticsearch.client.RestClient; -import org.elasticsearch.client.RestHighLevelClient; +import co.elastic.clients.elasticsearch.ElasticsearchClient; +import co.elastic.clients.elasticsearch.cat.IndicesResponse; +import co.elastic.clients.elasticsearch.cat.indices.IndicesRecord; +import co.elastic.clients.elasticsearch.indices.GetIndexResponse; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.ExtendWith; import org.springframework.beans.factory.annotation.Autowired; @@ -17,6 +16,7 @@ import org.springframework.test.context.junit.jupiter.SpringExtension; import org.testcontainers.elasticsearch.ElasticsearchContainer; import java.io.IOException; +import java.util.stream.Collectors; import static org.hamcrest.MatcherAssert.assertThat; import static org.hamcrest.Matchers.containsString; @@ -36,13 +36,15 @@ public class ElasticsearchPrefixTest { @Test public void test() throws IOException { //Given - RestHighLevelClient elasticsearchHighLevelRestClient = ElasticsearchRestClientFactory.createElasticsearchHighLevelRestClient( + ElasticsearchClient elasticsearchHighLevelRestClient = ElasticsearchRestClientFactory.createElasticsearchHighLevelRestClient( "http", elasticsearchContainer.getHost() + ":" + elasticsearchContainer.getMappedPort(9200), "", ""); //When - RestClient lowLevelClient = elasticsearchHighLevelRestClient.getLowLevelClient(); - Response get = lowLevelClient.performRequest(new Request("GET", "/_cat/indices")); - String catIndexes = EntityUtils.toString(get.getEntity()); + IndicesResponse indicesResponse = elasticsearchHighLevelRestClient + .cat() + .indices(); + + String catIndexes = indicesResponse.valueBody().stream().map(IndicesRecord::index).collect(Collectors.joining(",")); //Then assertThat(catIndexes, containsString(ELASTIC_PREFIX + "-resourcetable-000001")); diff --git a/hapi-fhir-jpaserver-elastic-test-utilities/src/test/java/ca/uhn/fhir/jpa/dao/r4/FhirResourceDaoR4SearchLastNAsyncIT.java b/hapi-fhir-jpaserver-elastic-test-utilities/src/test/java/ca/uhn/fhir/jpa/dao/r4/FhirResourceDaoR4SearchLastNAsyncIT.java index 1e619d17e99..355925c596c 100644 --- a/hapi-fhir-jpaserver-elastic-test-utilities/src/test/java/ca/uhn/fhir/jpa/dao/r4/FhirResourceDaoR4SearchLastNAsyncIT.java +++ b/hapi-fhir-jpaserver-elastic-test-utilities/src/test/java/ca/uhn/fhir/jpa/dao/r4/FhirResourceDaoR4SearchLastNAsyncIT.java @@ -22,6 +22,7 @@ import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; +import java.util.Set; import java.util.stream.Collectors; import static org.hamcrest.MatcherAssert.assertThat; @@ -37,6 +38,17 @@ public class FhirResourceDaoR4SearchLastNAsyncIT extends BaseR4SearchLastN { @Autowired private ISearchDao mySearchDao; + @BeforeEach + public void enableAdvancedHSearchIndexing() { + myStorageSettings.setLastNEnabled(true); + myStorageSettings.setAdvancedHSearchIndexing(true); + } + + @AfterEach + public void disableAdvancedHSearchIndex() { + myStorageSettings.setAdvancedHSearchIndexing(new JpaStorageSettings().isAdvancedHSearchIndexing()); + } + @Override @BeforeEach public void before() throws Exception { @@ -72,9 +84,9 @@ public class FhirResourceDaoR4SearchLastNAsyncIT extends BaseR4SearchLastN { public void testLastNChunking() { runInTransaction(() -> { - for (Search search : mySearchDao.findAll()) { - mySearchDao.updateDeleted(search.getId(), true); - } + Set all = mySearchDao.findAll().stream().map(Search::getId).collect(Collectors.toSet()); + + mySearchDao.updateDeleted(all, true); }); // Set up search parameters that will return 75 Observations. @@ -122,7 +134,7 @@ public class FhirResourceDaoR4SearchLastNAsyncIT extends BaseR4SearchLastN { // The first chunked query should have a full complement of PIDs StringBuilder firstQueryPattern = new StringBuilder(".*RES_ID in \\('[0-9]+'"); for (int pidIndex = 1; pidIndex < 50; pidIndex++) { - firstQueryPattern.append(" , '[0-9]+'"); + firstQueryPattern.append(",'[0-9]+'"); } firstQueryPattern.append("\\).*"); assertThat(queries.get(4), matchesPattern(firstQueryPattern.toString())); @@ -130,10 +142,10 @@ public class FhirResourceDaoR4SearchLastNAsyncIT extends BaseR4SearchLastN { // the second chunked query should be padded with "-1". StringBuilder secondQueryPattern = new StringBuilder(".*RES_ID in \\('[0-9]+'"); for (int pidIndex = 1; pidIndex < 25; pidIndex++) { - secondQueryPattern.append(" , '[0-9]+'"); + secondQueryPattern.append(",'[0-9]+'"); } for (int pidIndex = 0; pidIndex < 25; pidIndex++) { - secondQueryPattern.append(" , '-1'"); + secondQueryPattern.append(",'-1'"); } secondQueryPattern.append("\\).*"); assertThat(queries.get(5), matchesPattern(secondQueryPattern.toString())); diff --git a/hapi-fhir-jpaserver-elastic-test-utilities/src/test/java/ca/uhn/fhir/jpa/dao/r4/FhirResourceDaoR4SearchLastNIT.java b/hapi-fhir-jpaserver-elastic-test-utilities/src/test/java/ca/uhn/fhir/jpa/dao/r4/FhirResourceDaoR4SearchLastNIT.java index f9307f1cfb2..33dbef51e44 100644 --- a/hapi-fhir-jpaserver-elastic-test-utilities/src/test/java/ca/uhn/fhir/jpa/dao/r4/FhirResourceDaoR4SearchLastNIT.java +++ b/hapi-fhir-jpaserver-elastic-test-utilities/src/test/java/ca/uhn/fhir/jpa/dao/r4/FhirResourceDaoR4SearchLastNIT.java @@ -1,10 +1,11 @@ package ca.uhn.fhir.jpa.dao.r4; import ca.uhn.fhir.jpa.api.config.JpaStorageSettings; -import ca.uhn.fhir.jpa.model.dao.JpaPid; +import ca.uhn.fhir.jpa.dao.IHSearchEventListener; import ca.uhn.fhir.jpa.search.builder.SearchBuilder; import ca.uhn.fhir.jpa.search.lastn.ElasticsearchSvcImpl; import ca.uhn.fhir.jpa.searchparam.SearchParameterMap; +import ca.uhn.fhir.jpa.test.util.TestHSearchEventDispatcher; import ca.uhn.fhir.rest.param.ReferenceParam; import ca.uhn.fhir.rest.param.TokenParam; import org.hl7.fhir.instance.model.api.IIdType; @@ -14,7 +15,11 @@ import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.ExtendWith; -import org.mockito.ArgumentCaptor; +import org.mockito.Mock; +import org.mockito.Mockito; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.annotation.Autowired; import org.springframework.test.context.junit.jupiter.SpringExtension; import java.io.IOException; @@ -25,26 +30,37 @@ import java.util.UUID; import java.util.stream.Collectors; import static org.hamcrest.MatcherAssert.assertThat; -import static org.hamcrest.Matchers.is; import static org.hamcrest.Matchers.matchesPattern; import static org.junit.jupiter.api.Assertions.assertEquals; -import static org.mockito.Mockito.times; -import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; @ExtendWith(SpringExtension.class) public class FhirResourceDaoR4SearchLastNIT extends BaseR4SearchLastN { + private static final Logger ourLog = LoggerFactory.getLogger(FhirResourceDaoR4SearchLastNIT.class); + @BeforeEach public void enableAdvancedHSearchIndexing() { myStorageSettings.setLastNEnabled(true); + myStorageSettings.setAdvancedHSearchIndexing(true); + myHSearchEventDispatcher.register(mySearchEventListener); + ourLog.info("enableAdvancedHSearchIndexing finished. lastn {} advancedHSearchIndexing {}", myStorageSettings.isLastNEnabled(), myStorageSettings.isAdvancedHSearchIndexing()); } @AfterEach public void reset() { SearchBuilder.setMaxPageSize50ForTest(false); myStorageSettings.setStoreResourceInHSearchIndex(new JpaStorageSettings().isStoreResourceInHSearchIndex()); + myStorageSettings.setAdvancedHSearchIndexing(new JpaStorageSettings().isAdvancedHSearchIndexing()); } + @Autowired + private TestHSearchEventDispatcher myHSearchEventDispatcher; + + @Mock + private IHSearchEventListener mySearchEventListener; + + + @Test public void testLastNChunking() { @@ -76,28 +92,22 @@ public class FhirResourceDaoR4SearchLastNIT extends BaseR4SearchLastN { .getSelectQueriesForCurrentThread() .stream() .map(t -> t.getSql(true, false)) - .collect(Collectors.toList()); + .toList(); // Two chunked queries executed by the QueryIterator (in current thread) and two chunked queries to retrieve resources by PID. assertEquals(4, queries.size()); // The first and third chunked queries should have a full complement of PIDs StringBuilder firstQueryPattern = new StringBuilder(".*RES_ID IN \\('[0-9]+'"); - for (int pidIndex = 1; pidIndex < 50; pidIndex++) { - firstQueryPattern.append(",'[0-9]+'"); - } + firstQueryPattern.append(",'[0-9]+'".repeat(49)); firstQueryPattern.append("\\).*"); assertThat(queries.get(0).toUpperCase().replaceAll(" , ", ","), matchesPattern(firstQueryPattern.toString())); assertThat(queries.get(2).toUpperCase().replaceAll(" , ", ","), matchesPattern(firstQueryPattern.toString())); // the second and fourth chunked queries should be padded with "-1". StringBuilder secondQueryPattern = new StringBuilder(".*RES_ID IN \\('[0-9]+'"); - for (int pidIndex = 1; pidIndex < 25; pidIndex++) { - secondQueryPattern.append(",'[0-9]+'"); - } - for (int pidIndex = 0; pidIndex < 25; pidIndex++) { - secondQueryPattern.append(",'-1'"); - } + secondQueryPattern.append(",'[0-9]+'".repeat(24)); + secondQueryPattern.append(",'-1'".repeat(25)); secondQueryPattern.append("\\).*"); assertThat(queries.get(1).toUpperCase().replaceAll(" , ", ","), matchesPattern(secondQueryPattern.toString())); assertThat(queries.get(3).toUpperCase().replaceAll(" , ", ","), matchesPattern(secondQueryPattern.toString())); @@ -134,18 +144,12 @@ public class FhirResourceDaoR4SearchLastNIT extends BaseR4SearchLastN { } + /** + * We pull the resources from Hibernate Search when LastN uses Hibernate Search + * Override the test verification to validate only one search was performed + */ void verifyResourcesLoadedFromElastic(List theObservationIds, List theResults) { - List expectedArgumentPids = JpaPid.fromLongList( - theObservationIds.stream().map(IIdType::getIdPartAsLong).collect(Collectors.toList()) - ); - ArgumentCaptor> actualPids = ArgumentCaptor.forClass(List.class); - verify(myElasticsearchSvc, times(1)).getObservationResources(actualPids.capture()); - assertThat(actualPids.getValue(), is(expectedArgumentPids)); - - List expectedObservationList = theObservationIds.stream() - .map(id -> id.toUnqualifiedVersionless().getValue()).collect(Collectors.toList()); - assertEquals(expectedObservationList, theResults); - + Mockito.verify(mySearchEventListener, Mockito.times(1)) + .hsearchEvent(IHSearchEventListener.HSearchEventType.SEARCH); } - } diff --git a/hapi-fhir-jpaserver-elastic-test-utilities/src/test/java/ca/uhn/fhir/jpa/dao/r4/FhirResourceDaoR4SearchLastNUsingExtendedHSearchIndexAsyncIT.java b/hapi-fhir-jpaserver-elastic-test-utilities/src/test/java/ca/uhn/fhir/jpa/dao/r4/FhirResourceDaoR4SearchLastNUsingExtendedHSearchIndexAsyncIT.java deleted file mode 100644 index 0e547dca1f7..00000000000 --- a/hapi-fhir-jpaserver-elastic-test-utilities/src/test/java/ca/uhn/fhir/jpa/dao/r4/FhirResourceDaoR4SearchLastNUsingExtendedHSearchIndexAsyncIT.java +++ /dev/null @@ -1,27 +0,0 @@ -package ca.uhn.fhir.jpa.dao.r4; - -import ca.uhn.fhir.jpa.api.config.JpaStorageSettings; -import org.junit.jupiter.api.AfterEach; -import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.extension.ExtendWith; -import org.springframework.test.context.junit.jupiter.SpringExtension; - -/** - * Run entire @see {@link FhirResourceDaoR4SearchLastNAsyncIT} test suite this time - * using Extended HSearch index as search target - */ -@ExtendWith(SpringExtension.class) -public class FhirResourceDaoR4SearchLastNUsingExtendedHSearchIndexAsyncIT extends FhirResourceDaoR4SearchLastNAsyncIT { - - @BeforeEach - public void enableAdvancedHSearchIndexing() { - myStorageSettings.setLastNEnabled(true); - myStorageSettings.setAdvancedHSearchIndexing(true); - } - - @AfterEach - public void disableAdvancedHSearchIndex() { - myStorageSettings.setAdvancedHSearchIndexing(new JpaStorageSettings().isAdvancedHSearchIndexing()); - } - -} diff --git a/hapi-fhir-jpaserver-elastic-test-utilities/src/test/java/ca/uhn/fhir/jpa/dao/r4/FhirResourceDaoR4SearchLastNUsingExtendedHSearchIndexIT.java b/hapi-fhir-jpaserver-elastic-test-utilities/src/test/java/ca/uhn/fhir/jpa/dao/r4/FhirResourceDaoR4SearchLastNUsingExtendedHSearchIndexIT.java deleted file mode 100644 index 057ed57e48e..00000000000 --- a/hapi-fhir-jpaserver-elastic-test-utilities/src/test/java/ca/uhn/fhir/jpa/dao/r4/FhirResourceDaoR4SearchLastNUsingExtendedHSearchIndexIT.java +++ /dev/null @@ -1,60 +0,0 @@ -package ca.uhn.fhir.jpa.dao.r4; - -import ca.uhn.fhir.jpa.api.config.JpaStorageSettings; -import ca.uhn.fhir.jpa.dao.IHSearchEventListener; -import ca.uhn.fhir.jpa.test.util.TestHSearchEventDispatcher; -import org.hl7.fhir.instance.model.api.IIdType; -import org.junit.jupiter.api.AfterEach; -import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.extension.ExtendWith; -import org.mockito.Mock; -import org.mockito.Mockito; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.test.context.junit.jupiter.SpringExtension; - -import java.util.List; - -/** - * Run entire @see {@link FhirResourceDaoR4SearchLastNIT} test suite this time - * using Extended HSearch index as search target. - * - * The other implementation is obsolete, and we can merge these someday. - */ -@ExtendWith(SpringExtension.class) -public class FhirResourceDaoR4SearchLastNUsingExtendedHSearchIndexIT extends FhirResourceDaoR4SearchLastNIT { - private static final Logger ourLog = LoggerFactory.getLogger(FhirResourceDaoR4SearchLastNUsingExtendedHSearchIndexIT.class); - - @Autowired - private TestHSearchEventDispatcher myHSearchEventDispatcher; - - @Mock - private IHSearchEventListener mySearchEventListener; - - - @BeforeEach - public void enableAdvancedHSearchIndexing() { - myStorageSettings.setLastNEnabled(true); - myStorageSettings.setAdvancedHSearchIndexing(true); - myHSearchEventDispatcher.register(mySearchEventListener); - ourLog.info("enableAdvancedHSearchIndexing finished. lastn {} advancedHSearchIndexing {}", myStorageSettings.isLastNEnabled(), myStorageSettings.isAdvancedHSearchIndexing()); - - } - - @AfterEach - public void disableAdvancedHSearchIndex() { - myStorageSettings.setAdvancedHSearchIndexing(new JpaStorageSettings().isAdvancedHSearchIndexing()); - } - - /** - * We pull the resources from Hibernate Search when LastN uses Hibernate Search - * Override the test verification to validate only one search was performed - */ - @Override - void verifyResourcesLoadedFromElastic(List theObservationIds, List theResults) { - Mockito.verify(mySearchEventListener, Mockito.times(1)) - .hsearchEvent(IHSearchEventListener.HSearchEventType.SEARCH); - } - -} diff --git a/hapi-fhir-jpaserver-elastic-test-utilities/src/test/java/ca/uhn/fhir/jpa/dao/r4/FhirResourceDaoR4SearchWithElasticSearchIT.java b/hapi-fhir-jpaserver-elastic-test-utilities/src/test/java/ca/uhn/fhir/jpa/dao/r4/FhirResourceDaoR4SearchWithElasticSearchIT.java index 16f6a63cc19..a17f61fe13e 100644 --- a/hapi-fhir-jpaserver-elastic-test-utilities/src/test/java/ca/uhn/fhir/jpa/dao/r4/FhirResourceDaoR4SearchWithElasticSearchIT.java +++ b/hapi-fhir-jpaserver-elastic-test-utilities/src/test/java/ca/uhn/fhir/jpa/dao/r4/FhirResourceDaoR4SearchWithElasticSearchIT.java @@ -103,8 +103,8 @@ import org.springframework.transaction.PlatformTransactionManager; import org.springframework.web.util.UriComponents; import org.springframework.web.util.UriComponentsBuilder; -import javax.annotation.Nonnull; -import javax.persistence.EntityManager; +import jakarta.annotation.Nonnull; +import jakarta.persistence.EntityManager; import java.io.IOException; import java.net.URLEncoder; import java.util.ArrayList; diff --git a/hapi-fhir-jpaserver-elastic-test-utilities/src/test/java/ca/uhn/fhir/jpa/dao/r4/HSearchSandboxTest.java b/hapi-fhir-jpaserver-elastic-test-utilities/src/test/java/ca/uhn/fhir/jpa/dao/r4/HSearchSandboxTest.java index 452217fe751..77d3a743f65 100644 --- a/hapi-fhir-jpaserver-elastic-test-utilities/src/test/java/ca/uhn/fhir/jpa/dao/r4/HSearchSandboxTest.java +++ b/hapi-fhir-jpaserver-elastic-test-utilities/src/test/java/ca/uhn/fhir/jpa/dao/r4/HSearchSandboxTest.java @@ -42,7 +42,7 @@ import org.springframework.test.context.ContextHierarchy; import org.springframework.test.context.junit.jupiter.SpringExtension; import org.springframework.transaction.PlatformTransactionManager; -import javax.persistence.EntityManager; +import jakarta.persistence.EntityManager; import java.util.Collections; import java.util.List; diff --git a/hapi-fhir-jpaserver-elastic-test-utilities/src/test/java/ca/uhn/fhir/jpa/dao/r4/PersistObservationIndexedSearchParamLastNR4IT.java b/hapi-fhir-jpaserver-elastic-test-utilities/src/test/java/ca/uhn/fhir/jpa/dao/r4/PersistObservationIndexedSearchParamLastNR4IT.java deleted file mode 100644 index 8d9d0d8c15e..00000000000 --- a/hapi-fhir-jpaserver-elastic-test-utilities/src/test/java/ca/uhn/fhir/jpa/dao/r4/PersistObservationIndexedSearchParamLastNR4IT.java +++ /dev/null @@ -1,474 +0,0 @@ -package ca.uhn.fhir.jpa.dao.r4; - -import ca.uhn.fhir.context.FhirContext; -import ca.uhn.fhir.jpa.api.config.JpaStorageSettings; -import ca.uhn.fhir.jpa.api.dao.IFhirSystemDao; -import ca.uhn.fhir.jpa.config.TestR4ConfigWithElasticHSearch; -import ca.uhn.fhir.jpa.dao.ObservationLastNIndexPersistSvc; -import ca.uhn.fhir.jpa.model.entity.ResourceTable; -import ca.uhn.fhir.jpa.search.lastn.ElasticsearchSvcImpl; -import ca.uhn.fhir.jpa.search.lastn.json.CodeJson; -import ca.uhn.fhir.jpa.search.lastn.json.ObservationJson; -import ca.uhn.fhir.jpa.searchparam.SearchParameterMap; -import ca.uhn.fhir.parser.IParser; -import ca.uhn.fhir.rest.param.ReferenceAndListParam; -import ca.uhn.fhir.rest.param.ReferenceOrListParam; -import ca.uhn.fhir.rest.param.ReferenceParam; -import ca.uhn.fhir.rest.param.TokenAndListParam; -import ca.uhn.fhir.rest.param.TokenOrListParam; -import ca.uhn.fhir.rest.param.TokenParam; -import ca.uhn.fhir.test.utilities.docker.RequiresDocker; -import com.google.common.base.Charsets; -import org.apache.commons.io.IOUtils; -import org.hl7.fhir.r4.model.Bundle; -import org.hl7.fhir.r4.model.CodeableConcept; -import org.hl7.fhir.r4.model.Coding; -import org.hl7.fhir.r4.model.DateTimeType; -import org.hl7.fhir.r4.model.IdType; -import org.hl7.fhir.r4.model.Meta; -import org.hl7.fhir.r4.model.Observation; -import org.hl7.fhir.r4.model.Reference; -import org.junit.jupiter.api.AfterEach; -import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.MethodOrderer; -import org.junit.jupiter.api.Order; -import org.junit.jupiter.api.Test; -import org.junit.jupiter.api.TestMethodOrder; -import org.junit.jupiter.api.extension.ExtendWith; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.core.io.Resource; -import org.springframework.core.io.support.PathMatchingResourcePatternResolver; -import org.springframework.test.context.ContextConfiguration; -import org.springframework.test.context.junit.jupiter.SpringExtension; - -import javax.persistence.EntityManager; -import javax.persistence.PersistenceContext; -import javax.persistence.PersistenceContextType; -import java.io.IOException; -import java.io.InputStream; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.Calendar; -import java.util.Date; -import java.util.GregorianCalendar; -import java.util.List; -import java.util.concurrent.atomic.AtomicInteger; - -import static org.junit.jupiter.api.Assertions.assertEquals; -import static org.junit.jupiter.api.Assertions.assertNotNull; -import static org.junit.jupiter.api.Assertions.assertNull; -import static org.junit.jupiter.api.Assertions.assertTrue; - - -@ExtendWith(SpringExtension.class) -@RequiresDocker -@ContextConfiguration(classes = TestR4ConfigWithElasticHSearch.class) -@TestMethodOrder(MethodOrderer.OrderAnnotation.class) -public class PersistObservationIndexedSearchParamLastNR4IT { - - private final String SINGLE_SUBJECT_ID = "4567"; - private final String SINGLE_OBSERVATION_PID = "123"; - private final Date SINGLE_EFFECTIVEDTM = new Date(); - private final String SINGLE_OBSERVATION_CODE_TEXT = "Test Codeable Concept Field for Code"; - private final String CATEGORYFIRSTCODINGSYSTEM = "http://mycodes.org/fhir/observation-category"; - private final String FIRSTCATEGORYFIRSTCODINGCODE = "test-heart-rate"; - private final String CODEFIRSTCODINGSYSTEM = "http://mycodes.org/fhir/observation-code"; - private final String CODEFIRSTCODINGCODE = "test-code"; - @PersistenceContext(type = PersistenceContextType.TRANSACTION) - protected EntityManager myEntityManager; - @Autowired - protected FhirContext myFhirCtx; - @Autowired - ObservationLastNIndexPersistSvc testObservationPersist; - @Autowired - private ElasticsearchSvcImpl elasticsearchSvc; - @Autowired - private IFhirSystemDao myDao; - @Autowired - private JpaStorageSettings myStorageSettings; - private ReferenceAndListParam multiSubjectParams = null; - - @BeforeEach - public void before() throws IOException { - myStorageSettings.setLastNEnabled(true); - - elasticsearchSvc.deleteAllDocumentsForTest(ElasticsearchSvcImpl.OBSERVATION_INDEX); - elasticsearchSvc.deleteAllDocumentsForTest(ElasticsearchSvcImpl.OBSERVATION_CODE_INDEX); - elasticsearchSvc.refreshIndex(ElasticsearchSvcImpl.OBSERVATION_INDEX); - elasticsearchSvc.refreshIndex(ElasticsearchSvcImpl.OBSERVATION_CODE_INDEX); - - } - - @AfterEach - public void afterDisableLastN() { - myStorageSettings.setLastNEnabled(new JpaStorageSettings().isLastNEnabled()); - } - - @Order(0) - @Test - public void testDeleteObservation() throws IOException { - indexMultipleObservations(); - SearchParameterMap searchParameterMap = new SearchParameterMap(); - searchParameterMap.setLastNMax(100); - List observationDocuments = elasticsearchSvc.executeLastNWithAllFieldsForTest(searchParameterMap, myFhirCtx); - assertEquals(100, observationDocuments.size()); - // Check that fifth observation for fifth patient has been indexed. - ObservationJson observation = elasticsearchSvc.getObservationDocument("55"); - assertNotNull(observation); - - searchParameterMap = new SearchParameterMap(); - searchParameterMap.add(Observation.SP_SUBJECT, multiSubjectParams); - searchParameterMap.setLastNMax(10); - List observationIdsOnly = elasticsearchSvc.executeLastN(searchParameterMap, myFhirCtx, 200); - assertEquals(100, observationIdsOnly.size()); - assertTrue(observationIdsOnly.contains("55")); - - // Delete fifth observation for fifth patient. - ResourceTable entity = new ResourceTable(); - entity.setId(55L); - entity.setResourceType("Observation"); - entity.setVersionForUnitTest(0L); - - testObservationPersist.deleteObservationIndex(entity); - elasticsearchSvc.refreshIndex(ElasticsearchSvcImpl.OBSERVATION_INDEX); - - // Confirm that observation was deleted. - searchParameterMap = new SearchParameterMap(); - searchParameterMap.setLastNMax(100); - observationDocuments = elasticsearchSvc.executeLastNWithAllFieldsForTest(searchParameterMap, myFhirCtx); - assertEquals(99, observationDocuments.size()); - observation = elasticsearchSvc.getObservationDocument("55"); - assertNull(observation); - - observationIdsOnly = elasticsearchSvc.executeLastN(searchParameterMap, myFhirCtx, 200); - assertEquals(99, observationIdsOnly.size()); - assertTrue(!observationIdsOnly.contains("55")); - - } - - - - private void indexSingleObservation() throws IOException { - - Observation myObservation = new Observation(); - IdType observationID = new IdType("Observation", SINGLE_OBSERVATION_PID, "1"); - myObservation.setId(observationID); - Reference subjectId = new Reference(SINGLE_SUBJECT_ID); - myObservation.setSubject(subjectId); - myObservation.setEffective(new DateTimeType(SINGLE_EFFECTIVEDTM)); - - myObservation.setCategory(getCategoryCode()); - - myObservation.setCode(getObservationCode()); - - testObservationPersist.indexObservation(myObservation); - - elasticsearchSvc.refreshIndex(ElasticsearchSvcImpl.OBSERVATION_INDEX); - elasticsearchSvc.refreshIndex(ElasticsearchSvcImpl.OBSERVATION_CODE_INDEX); - - } - - private List getCategoryCode() { - // Add three CodeableConcepts for category - List categoryConcepts = new ArrayList<>(); - // Create three codings and first category CodeableConcept - List category = new ArrayList<>(); - CodeableConcept categoryCodeableConcept1 = new CodeableConcept().setText("Test Codeable Concept Field for first category"); - category.add(new Coding(CATEGORYFIRSTCODINGSYSTEM, FIRSTCATEGORYFIRSTCODINGCODE, "test-heart-rate display")); - category.add(new Coding("http://myalternatecodes.org/fhir/observation-category", "test-alt-heart-rate", "test-alt-heart-rate display")); - category.add(new Coding("http://mysecondaltcodes.org/fhir/observation-category", "test-2nd-alt-heart-rate", "test-2nd-alt-heart-rate display")); - categoryCodeableConcept1.setCoding(category); - categoryConcepts.add(categoryCodeableConcept1); - // Create three codings and second category CodeableConcept - List category2 = new ArrayList<>(); - CodeableConcept categoryCodeableConcept2 = new CodeableConcept().setText("Test Codeable Concept Field for for second category"); - category2.add(new Coding(CATEGORYFIRSTCODINGSYSTEM, "test-vital-signs", "test-vital-signs display")); - category2.add(new Coding("http://myalternatecodes.org/fhir/observation-category", "test-alt-vitals", "test-alt-vitals display")); - category2.add(new Coding("http://mysecondaltcodes.org/fhir/observation-category", "test-2nd-alt-vitals", "test-2nd-alt-vitals display")); - categoryCodeableConcept2.setCoding(category2); - categoryConcepts.add(categoryCodeableConcept2); - // Create three codings and third category CodeableConcept - List category3 = new ArrayList<>(); - CodeableConcept categoryCodeableConcept3 = new CodeableConcept().setText("Test Codeable Concept Field for third category"); - category3.add(new Coding(CATEGORYFIRSTCODINGSYSTEM, "test-vitals-panel", "test-vitals-panel display")); - category3.add(new Coding("http://myalternatecodes.org/fhir/observation-category", "test-alt-vitals-panel", "test-alt-vitals-panel display")); - category3.add(new Coding("http://mysecondaltcodes.org/fhir/observation-category", "test-2nd-alt-vitals-panel", "test-2nd-alt-vitals-panel display")); - categoryCodeableConcept3.setCoding(category3); - categoryConcepts.add(categoryCodeableConcept3); - return categoryConcepts; - } - - private CodeableConcept getObservationCode() { - // Create CodeableConcept for Code with three codings. - CodeableConcept codeableConceptField = new CodeableConcept().setText(SINGLE_OBSERVATION_CODE_TEXT); - codeableConceptField.addCoding(new Coding(CODEFIRSTCODINGSYSTEM, CODEFIRSTCODINGCODE, "test-code display")); - return codeableConceptField; - } - - @Order(1) - @Test - public void testSampleBundleInTransaction() throws IOException { - FhirContext myFhirCtx = FhirContext.forR4Cached(); - - PathMatchingResourcePatternResolver provider = new PathMatchingResourcePatternResolver(); - final Resource[] bundleResources = provider.getResources("lastntestbundle.json"); - assertEquals(1, bundleResources.length); - - AtomicInteger index = new AtomicInteger(); - - Arrays.stream(bundleResources).forEach( - resource -> { - index.incrementAndGet(); - - InputStream resIs = null; - String nextBundleString; - try { - resIs = resource.getInputStream(); - nextBundleString = IOUtils.toString(resIs, Charsets.UTF_8); - } catch (IOException e) { - return; - } finally { - try { - if (resIs != null) { - resIs.close(); - } - } catch (final IOException ioe) { - // ignore - } - } - - IParser parser = myFhirCtx.newJsonParser(); - Bundle bundle = parser.parseResource(Bundle.class, nextBundleString); - - myDao.transaction(null, bundle); - - } - ); - - elasticsearchSvc.refreshIndex("*"); - - SearchParameterMap searchParameterMap = new SearchParameterMap(); - - // execute Observation ID search - Composite Aggregation - searchParameterMap.setLastNMax(1); - List observationIdsOnly = elasticsearchSvc.executeLastN(searchParameterMap, myFhirCtx, 200); - - assertEquals(20, observationIdsOnly.size()); - - searchParameterMap.setLastNMax(3); - observationIdsOnly = elasticsearchSvc.executeLastN(searchParameterMap, myFhirCtx, 200); - - assertEquals(38, observationIdsOnly.size()); - - } - - @Order(2) - @Test - public void testIndexObservationMultiple() throws IOException { - indexMultipleObservations(); - SearchParameterMap searchParameterMap = new SearchParameterMap(); - searchParameterMap.setLastNMax(100); - List observationDocuments = elasticsearchSvc.executeLastNWithAllFieldsForTest(searchParameterMap, myFhirCtx); - assertEquals(100, observationDocuments.size()); - - // Check that all observations were indexed. - searchParameterMap = new SearchParameterMap(); - searchParameterMap.add(Observation.SP_SUBJECT, multiSubjectParams); - - searchParameterMap.setLastNMax(10); - - List observationIdsOnly = elasticsearchSvc.executeLastN(searchParameterMap, myFhirCtx, 200); - assertEquals(100, observationIdsOnly.size()); - - // Filter the results by category code. - TokenParam categoryParam = new TokenParam(CATEGORYFIRSTCODINGSYSTEM, FIRSTCATEGORYFIRSTCODINGCODE); - searchParameterMap.add(Observation.SP_CATEGORY, new TokenAndListParam().addAnd(new TokenOrListParam().addOr(categoryParam))); - - - observationIdsOnly = elasticsearchSvc.executeLastN(searchParameterMap, myFhirCtx, 100); - - assertEquals(50, observationIdsOnly.size()); - - } - - private void indexMultipleObservations() throws IOException { - - // Create two CodeableConcept values each for a Code with three codings. - CodeableConcept codeableConceptField1 = new CodeableConcept().setText("Test Codeable Concept Field for First Code"); - codeableConceptField1.addCoding(new Coding(CODEFIRSTCODINGSYSTEM, "test-code-1", "test-code-1 display")); - codeableConceptField1.addCoding(new Coding("http://myalternatecodes.org/fhir/observation-code", "test-alt-code-1", "test-alt-code-1 display")); - codeableConceptField1.addCoding(new Coding("http://mysecondaltcodes.org/fhir/observation-code", "test-second-alt-code-1", "test-second-alt-code-1 display")); - - CodeableConcept codeableConceptField2 = new CodeableConcept().setText("Test Codeable Concept Field for Second Code"); - codeableConceptField2.addCoding(new Coding(CODEFIRSTCODINGSYSTEM, "test-code-2", "test-code-2 display")); - codeableConceptField2.addCoding(new Coding("http://myalternatecodes.org/fhir/observation-code", "test-alt-code-2", "test-alt-code-2 display")); - codeableConceptField2.addCoding(new Coding("http://mysecondaltcodes.org/fhir/observation-code", "test-second-alt-code-2", "test-second-alt-code-2 display")); - - // Create two CodeableConcept entities for category, each with three codings. - List category1 = new ArrayList<>(); - // Create three codings and first category CodeableConcept - category1.add(new Coding(CATEGORYFIRSTCODINGSYSTEM, FIRSTCATEGORYFIRSTCODINGCODE, "test-heart-rate display")); - category1.add(new Coding("http://myalternatecodes.org/fhir/observation-category", "test-alt-heart-rate", "test-alt-heart-rate display")); - category1.add(new Coding("http://mysecondaltcodes.org/fhir/observation-category", "test-2nd-alt-heart-rate", "test-2nd-alt-heart-rate display")); - List categoryConcepts1 = new ArrayList<>(); - CodeableConcept categoryCodeableConcept1 = new CodeableConcept().setText("Test Codeable Concept Field for first category"); - categoryCodeableConcept1.setCoding(category1); - categoryConcepts1.add(categoryCodeableConcept1); - // Create three codings and second category CodeableConcept - List category2 = new ArrayList<>(); - category2.add(new Coding(CATEGORYFIRSTCODINGSYSTEM, "test-vital-signs", "test-vital-signs display")); - category2.add(new Coding("http://myalternatecodes.org/fhir/observation-category", "test-alt-vitals", "test-alt-vitals display")); - category2.add(new Coding("http://mysecondaltcodes.org/fhir/observation-category", "test-2nd-alt-vitals", "test-2nd-alt-vitals display")); - List categoryConcepts2 = new ArrayList<>(); - CodeableConcept categoryCodeableConcept2 = new CodeableConcept().setText("Test Codeable Concept Field for second category"); - categoryCodeableConcept2.setCoding(category2); - categoryConcepts2.add(categoryCodeableConcept2); - - ReferenceOrListParam subjectParams = new ReferenceOrListParam(); - for (int patientCount = 0; patientCount < 10; patientCount++) { - - String subjectId = String.valueOf(patientCount); - - ReferenceParam subjectParam = new ReferenceParam("Patient", "", subjectId); - subjectParams.addOr(subjectParam); - - for (int entryCount = 0; entryCount < 10; entryCount++) { - - Observation observation = new Observation(); - IdType observationId = new IdType("Observation", String.valueOf(entryCount + patientCount * 10), "1"); - observation.setId(observationId); - Reference subject = new Reference(subjectId); - observation.setSubject(subject); - - if (entryCount % 2 == 1) { - observation.setCategory(categoryConcepts1); - observation.setCode(codeableConceptField1); - } else { - observation.setCategory(categoryConcepts2); - observation.setCode(codeableConceptField2); - } - - Calendar observationDate = new GregorianCalendar(); - observationDate.add(Calendar.HOUR, -10 + entryCount); - Date effectiveDtm = observationDate.getTime(); - observation.setEffective(new DateTimeType(effectiveDtm)); - - testObservationPersist.indexObservation(observation); - } - - } - - multiSubjectParams = new ReferenceAndListParam().addAnd(subjectParams); - - elasticsearchSvc.refreshIndex(ElasticsearchSvcImpl.OBSERVATION_INDEX); - elasticsearchSvc.refreshIndex(ElasticsearchSvcImpl.OBSERVATION_CODE_INDEX); - - } - - @Order(3) - @Test - public void testIndexObservationSingle() throws IOException { - indexSingleObservation(); - SearchParameterMap searchParameterMap = new SearchParameterMap(); - searchParameterMap.setLastNMax(10); - List persistedObservationEntities = elasticsearchSvc.executeLastNWithAllFieldsForTest(searchParameterMap, myFhirCtx); - assertEquals(1, persistedObservationEntities.size()); - ObservationJson persistedObservationEntity = persistedObservationEntities.get(0); - assertEquals(SINGLE_SUBJECT_ID, persistedObservationEntity.getSubject()); - assertEquals(SINGLE_OBSERVATION_PID, persistedObservationEntity.getIdentifier()); - assertEquals(SINGLE_EFFECTIVEDTM, persistedObservationEntity.getEffectiveDtm()); - - String observationCodeNormalizedId = persistedObservationEntity.getCode_concept_id(); - - // List persistedObservationCodes = elasticsearchSvc.queryAllIndexedObservationCodesForTest(); - // assertEquals(1, persistedObservationCodes.size()); - - // Check that we can retrieve code by hash value. - String codeSystemHash = persistedObservationEntity.getCode_coding_code_system_hash(); - CodeJson persistedObservationCode = elasticsearchSvc.getObservationCodeDocument(codeSystemHash, null); - assertNotNull(persistedObservationCode); - assertEquals(observationCodeNormalizedId, persistedObservationCode.getCodeableConceptId()); - assertEquals(SINGLE_OBSERVATION_CODE_TEXT, persistedObservationCode.getCodeableConceptText()); - - // Also confirm that we can retrieve code by text value. - persistedObservationCode = elasticsearchSvc.getObservationCodeDocument(null, SINGLE_OBSERVATION_CODE_TEXT); - assertNotNull(persistedObservationCode); - - searchParameterMap = new SearchParameterMap(); - ReferenceParam subjectParam = new ReferenceParam("Patient", "", SINGLE_SUBJECT_ID); - searchParameterMap.add(Observation.SP_SUBJECT, new ReferenceAndListParam().addAnd(new ReferenceOrListParam().addOr(subjectParam))); - TokenParam categoryParam = new TokenParam(CATEGORYFIRSTCODINGSYSTEM, FIRSTCATEGORYFIRSTCODINGCODE); - searchParameterMap.add(Observation.SP_CATEGORY, new TokenAndListParam().addAnd(new TokenOrListParam().addOr(categoryParam))); - TokenParam codeParam = new TokenParam(CODEFIRSTCODINGSYSTEM, CODEFIRSTCODINGCODE); - searchParameterMap.add(Observation.SP_CODE, new TokenAndListParam().addAnd(new TokenOrListParam().addOr(codeParam))); - searchParameterMap.setLastNMax(3); - - List observationIdsOnly = elasticsearchSvc.executeLastN(searchParameterMap, myFhirCtx, 100); - - assertEquals(1, observationIdsOnly.size()); - assertEquals(SINGLE_OBSERVATION_PID, observationIdsOnly.get(0)); - } - - @Order(4) - @Test - public void testUpdateObservation() throws IOException { - indexSingleObservation(); - SearchParameterMap searchParameterMap = new SearchParameterMap(); - searchParameterMap.setLastNMax(10); - ObservationJson observationIndexEntity = elasticsearchSvc.executeLastNWithAllFieldsForTest(searchParameterMap, myFhirCtx).get(0); - assertEquals(SINGLE_OBSERVATION_PID, observationIndexEntity.getIdentifier()); - assertEquals(SINGLE_SUBJECT_ID, observationIndexEntity.getSubject()); - assertEquals(SINGLE_EFFECTIVEDTM, observationIndexEntity.getEffectiveDtm()); - - searchParameterMap = new SearchParameterMap(); - ReferenceParam subjectParam = new ReferenceParam("Patient", "", SINGLE_SUBJECT_ID); - searchParameterMap.add(Observation.SP_SUBJECT, new ReferenceAndListParam().addAnd(new ReferenceOrListParam().addOr(subjectParam))); - TokenParam categoryParam = new TokenParam(CATEGORYFIRSTCODINGSYSTEM, FIRSTCATEGORYFIRSTCODINGCODE); - searchParameterMap.add(Observation.SP_CATEGORY, new TokenAndListParam().addAnd(new TokenOrListParam().addOr(categoryParam))); - TokenParam codeParam = new TokenParam(CODEFIRSTCODINGSYSTEM, CODEFIRSTCODINGCODE); - searchParameterMap.add(Observation.SP_CODE, new TokenAndListParam().addAnd(new TokenOrListParam().addOr(codeParam))); - searchParameterMap.setLastNMax(10); - - List observationIdsOnly = elasticsearchSvc.executeLastN(searchParameterMap, myFhirCtx, 200); - assertEquals(1, observationIdsOnly.size()); - assertTrue(observationIdsOnly.contains(SINGLE_OBSERVATION_PID)); - - // Update the Observation with a new Subject and effective date: - Observation updatedObservation = new Observation(); - IdType observationId = new IdType("Observation", observationIndexEntity.getIdentifier(), "2"); - updatedObservation.setId(observationId); - Reference subjectId = new Reference("1234"); - updatedObservation.setSubject(subjectId); - DateTimeType newEffectiveDtm = new DateTimeType(new Date()); - updatedObservation.setEffective(newEffectiveDtm); - updatedObservation.setCategory(getCategoryCode()); - updatedObservation.setCode(getObservationCode()); - - testObservationPersist.indexObservation(updatedObservation); - elasticsearchSvc.refreshIndex(ElasticsearchSvcImpl.OBSERVATION_INDEX); - - ObservationJson updatedObservationEntity = elasticsearchSvc.getObservationDocument(SINGLE_OBSERVATION_PID); - assertEquals("1234", updatedObservationEntity.getSubject()); - assertEquals(newEffectiveDtm.getValue(), updatedObservationEntity.getEffectiveDtm()); - - // Repeat earlier Elasticsearch query. This time, should return no matches. - observationIdsOnly = elasticsearchSvc.executeLastN(searchParameterMap, myFhirCtx, 200); - assertEquals(0, observationIdsOnly.size()); - - // Try again with the new patient ID. - searchParameterMap = new SearchParameterMap(); - subjectParam = new ReferenceParam("Patient", "", "1234"); - searchParameterMap.add(Observation.SP_SUBJECT, new ReferenceAndListParam().addAnd(new ReferenceOrListParam().addOr(subjectParam))); - searchParameterMap.add(Observation.SP_CATEGORY, new TokenAndListParam().addAnd(new TokenOrListParam().addOr(categoryParam))); - searchParameterMap.add(Observation.SP_CODE, new TokenAndListParam().addAnd(new TokenOrListParam().addOr(codeParam))); - searchParameterMap.setLastNMax(10); - observationIdsOnly = elasticsearchSvc.executeLastN(searchParameterMap, myFhirCtx, 200); - - // Should see the observation returned now. - assertEquals(1, observationIdsOnly.size()); - assertTrue(observationIdsOnly.contains(SINGLE_OBSERVATION_PID)); - - } - - -} diff --git a/hapi-fhir-jpaserver-elastic-test-utilities/src/test/java/ca/uhn/fhir/jpa/search/autocomplete/TokenAutocompleteElasticsearchIT.java b/hapi-fhir-jpaserver-elastic-test-utilities/src/test/java/ca/uhn/fhir/jpa/search/autocomplete/TokenAutocompleteElasticsearchIT.java index bb9573636b7..aab45bc35b1 100644 --- a/hapi-fhir-jpaserver-elastic-test-utilities/src/test/java/ca/uhn/fhir/jpa/search/autocomplete/TokenAutocompleteElasticsearchIT.java +++ b/hapi-fhir-jpaserver-elastic-test-utilities/src/test/java/ca/uhn/fhir/jpa/search/autocomplete/TokenAutocompleteElasticsearchIT.java @@ -37,8 +37,8 @@ import org.springframework.test.context.junit.jupiter.SpringExtension; import org.springframework.transaction.PlatformTransactionManager; import org.springframework.transaction.support.TransactionTemplate; -import javax.annotation.Nonnull; -import javax.persistence.EntityManager; +import jakarta.annotation.Nonnull; +import jakarta.persistence.EntityManager; import java.util.List; import java.util.Objects; diff --git a/hapi-fhir-jpaserver-elastic-test-utilities/src/test/java/ca/uhn/fhir/jpa/search/lastn/LastNElasticsearchSvcMultipleObservationsIT.java b/hapi-fhir-jpaserver-elastic-test-utilities/src/test/java/ca/uhn/fhir/jpa/search/lastn/LastNElasticsearchSvcMultipleObservationsIT.java deleted file mode 100644 index bd32380e149..00000000000 --- a/hapi-fhir-jpaserver-elastic-test-utilities/src/test/java/ca/uhn/fhir/jpa/search/lastn/LastNElasticsearchSvcMultipleObservationsIT.java +++ /dev/null @@ -1,511 +0,0 @@ -package ca.uhn.fhir.jpa.search.lastn; - -import ca.uhn.fhir.context.FhirContext; -import ca.uhn.fhir.jpa.model.config.PartitionSettings; -import ca.uhn.fhir.jpa.search.lastn.json.CodeJson; -import ca.uhn.fhir.jpa.search.lastn.json.ObservationJson; -import ca.uhn.fhir.jpa.searchparam.SearchParameterMap; -import ca.uhn.fhir.jpa.test.config.TestElasticsearchContainerHelper; -import ca.uhn.fhir.rest.param.DateAndListParam; -import ca.uhn.fhir.rest.param.DateOrListParam; -import ca.uhn.fhir.rest.param.DateParam; -import ca.uhn.fhir.rest.param.ParamPrefixEnum; -import ca.uhn.fhir.rest.param.ReferenceAndListParam; -import ca.uhn.fhir.rest.param.ReferenceOrListParam; -import ca.uhn.fhir.rest.param.ReferenceParam; -import ca.uhn.fhir.rest.param.TokenAndListParam; -import ca.uhn.fhir.rest.param.TokenOrListParam; -import ca.uhn.fhir.rest.param.TokenParam; -import ca.uhn.fhir.rest.param.TokenParamModifier; -import ca.uhn.fhir.test.utilities.docker.RequiresDocker; -import org.hl7.fhir.r4.model.Observation; -import org.junit.jupiter.api.AfterEach; -import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.Test; -import org.junit.jupiter.api.extension.ExtendWith; -import org.springframework.test.context.junit.jupiter.SpringExtension; -import org.testcontainers.elasticsearch.ElasticsearchContainer; -import org.testcontainers.junit.jupiter.Container; -import org.testcontainers.junit.jupiter.Testcontainers; - -import java.io.IOException; -import java.util.ArrayList; -import java.util.Collections; -import java.util.Date; -import java.util.HashMap; -import java.util.List; -import java.util.Map; -import java.util.stream.Collectors; -import java.util.stream.IntStream; - -import static ca.uhn.fhir.jpa.search.lastn.LastNTestDataGenerator.TEST_BASELINE_TIMESTAMP; -import static org.junit.jupiter.api.Assertions.assertEquals; -import static org.junit.jupiter.api.Assertions.assertFalse; -import static org.junit.jupiter.api.Assertions.assertNotEquals; -import static org.junit.jupiter.api.Assertions.assertNotNull; -import static org.junit.jupiter.api.Assertions.assertTrue; - -@ExtendWith({SpringExtension.class}) -@RequiresDocker -@Testcontainers -public class LastNElasticsearchSvcMultipleObservationsIT { - - @Container - public static ElasticsearchContainer elasticsearchContainer = TestElasticsearchContainerHelper.getEmbeddedElasticSearch(); - private static boolean indexLoaded = false; - private final Map>> createdPatientObservationMap = new HashMap<>(); - private final FhirContext myFhirContext = FhirContext.forR4Cached(); - private ElasticsearchSvcImpl elasticsearchSvc; - - @BeforeEach - public void before() throws IOException { - PartitionSettings partitionSettings = new PartitionSettings(); - partitionSettings.setPartitioningEnabled(false); - elasticsearchSvc = new ElasticsearchSvcImpl(partitionSettings, "http", elasticsearchContainer.getHost() + ":" + elasticsearchContainer.getMappedPort(9200), null, null); - - if (!indexLoaded) { - createMultiplePatientsAndObservations(); - indexLoaded = true; - } - } - - @AfterEach - public void after() throws IOException { - elasticsearchSvc.close(); - } - - @Test - public void testLastNAllPatientsQuery() { - - // execute Observation ID search (Composite Aggregation) last 3 observations for each patient - SearchParameterMap searchParameterMap = new SearchParameterMap(); - IntStream.range(0, 10).forEach(index -> { - ReferenceParam subjectParam = new ReferenceParam("Patient", "", String.valueOf(index)); - searchParameterMap.add(Observation.SP_SUBJECT, buildReferenceAndListParam(subjectParam)); - }); - searchParameterMap.setLastNMax(3); - - List observations = elasticsearchSvc.executeLastNWithAllFieldsForTest(searchParameterMap, myFhirContext); - - assertEquals(60, observations.size()); - - // Observation documents should be grouped by subject, then by observation code, and then sorted by effective date/time - // within each observation code. Verify the grouping by creating a nested Map. - Map>> queriedPatientObservationMap = new HashMap<>(); - ObservationJson previousObservationJson = null; - for (ObservationJson observationJson : observations) { - assertNotNull(observationJson.getIdentifier()); - assertNotNull(observationJson.getSubject()); - assertNotNull(observationJson.getCode_concept_id()); - assertNotNull(observationJson.getEffectiveDtm()); - if (previousObservationJson == null) { - ArrayList observationDates = new ArrayList<>(); - observationDates.add(observationJson.getEffectiveDtm()); - Map> codeObservationMap = new HashMap<>(); - codeObservationMap.put(observationJson.getCode_concept_id(), observationDates); - queriedPatientObservationMap.put(observationJson.getSubject(), codeObservationMap); - } else if (observationJson.getSubject().equals(previousObservationJson.getSubject())) { - if (observationJson.getCode_concept_id().equals(previousObservationJson.getCode_concept_id())) { - queriedPatientObservationMap.get(observationJson.getSubject()).get(observationJson.getCode_concept_id()). - add(observationJson.getEffectiveDtm()); - } else { - Map> codeObservationDateMap = queriedPatientObservationMap.get(observationJson.getSubject()); - // Ensure that code concept was not already retrieved out of order for this subject/patient. - assertFalse(codeObservationDateMap.containsKey(observationJson.getCode_concept_id())); - ArrayList observationDates = new ArrayList<>(); - observationDates.add(observationJson.getEffectiveDtm()); - codeObservationDateMap.put(observationJson.getCode_concept_id(), observationDates); - } - } else { - // Ensure that subject/patient was not already retrieved out of order - assertFalse(queriedPatientObservationMap.containsKey(observationJson.getSubject())); - ArrayList observationDates = new ArrayList<>(); - observationDates.add(observationJson.getEffectiveDtm()); - Map> codeObservationMap = new HashMap<>(); - codeObservationMap.put(observationJson.getCode_concept_id(), observationDates); - queriedPatientObservationMap.put(observationJson.getSubject(), codeObservationMap); - } - previousObservationJson = observationJson; - } - - // Finally check that only the most recent effective date/time values were returned and in the correct order. - for (String subjectId : queriedPatientObservationMap.keySet()) { - Map> queriedObservationCodeMap = queriedPatientObservationMap.get(subjectId); - Map> createdObservationCodeMap = createdPatientObservationMap.get(subjectId); - for (String observationCode : queriedObservationCodeMap.keySet()) { - List queriedObservationDates = queriedObservationCodeMap.get(observationCode); - List createdObservationDates = createdObservationCodeMap.get(observationCode); - for (int dateIdx = 0; dateIdx < queriedObservationDates.size(); dateIdx++) { - assertEquals(createdObservationDates.get(dateIdx), queriedObservationDates.get(dateIdx)); - } - } - } - - } - - @Test - public void testLastNMultiPatientMultiCodeHashMultiCategoryHash() { - // Multiple Subject references - SearchParameterMap searchParameterMap = new SearchParameterMap(); - ReferenceParam subjectParam1 = new ReferenceParam("Patient", "", "3"); - ReferenceParam subjectParam2 = new ReferenceParam("Patient", "", "5"); - searchParameterMap.add(Observation.SP_SUBJECT, buildReferenceAndListParam(subjectParam1, subjectParam2)); - TokenParam categoryParam1 = new TokenParam("http://mycodes.org/fhir/observation-category", "test-heart-rate"); - TokenParam categoryParam2 = new TokenParam("http://mycodes.org/fhir/observation-category", "test-vital-signs"); - searchParameterMap.add(Observation.SP_CATEGORY, buildTokenAndListParam(categoryParam1, categoryParam2)); - TokenParam codeParam1 = new TokenParam("http://mycodes.org/fhir/observation-code", "test-code-1"); - TokenParam codeParam2 = new TokenParam("http://mycodes.org/fhir/observation-code", "test-code-2"); - searchParameterMap.add(Observation.SP_CODE, buildTokenAndListParam(codeParam1, codeParam2)); - searchParameterMap.setLastNMax(100); - - List observations = elasticsearchSvc.executeLastN(searchParameterMap, myFhirContext, 100); - - assertEquals(20, observations.size()); - - // Repeat with multiple Patient parameter - searchParameterMap = new SearchParameterMap(); - ReferenceParam patientParam1 = new ReferenceParam("Patient", "", "8"); - ReferenceParam patientParam2 = new ReferenceParam("Patient", "", "6"); - searchParameterMap.add(Observation.SP_PATIENT, buildReferenceAndListParam(patientParam1, patientParam2)); - searchParameterMap.add(Observation.SP_CATEGORY, buildTokenAndListParam(categoryParam1, categoryParam2)); - searchParameterMap.add(Observation.SP_CODE, buildTokenAndListParam(codeParam1, codeParam2)); - searchParameterMap.setLastNMax(100); - - observations = elasticsearchSvc.executeLastN(searchParameterMap, myFhirContext, 100); - - assertEquals(20, observations.size()); - - } - - private ReferenceAndListParam buildReferenceAndListParam(ReferenceParam... theReference) { - ReferenceOrListParam myReferenceOrListParam = new ReferenceOrListParam(); - for (ReferenceParam referenceParam : theReference) { - myReferenceOrListParam.addOr(referenceParam); - } - return new ReferenceAndListParam().addAnd(myReferenceOrListParam); - } - - private TokenAndListParam buildTokenAndListParam(TokenParam... theToken) { - TokenOrListParam myTokenOrListParam = new TokenOrListParam(); - for (TokenParam tokenParam : theToken) { - myTokenOrListParam.addOr(tokenParam); - } - return new TokenAndListParam().addAnd(myTokenOrListParam); - } - - @Test - public void testLastNCodeCodeOnlyCategoryCodeOnly() { - // Include subject - SearchParameterMap searchParameterMap = new SearchParameterMap(); - ReferenceParam subjectParam = new ReferenceParam("Patient", "", "3"); - searchParameterMap.add(Observation.SP_SUBJECT, buildReferenceAndListParam(subjectParam)); - TokenParam categoryParam = new TokenParam(null, "test-heart-rate"); - searchParameterMap.add(Observation.SP_CATEGORY, buildTokenAndListParam(categoryParam)); - TokenParam codeParam = new TokenParam(null, "test-code-1"); - searchParameterMap.add(Observation.SP_CODE, buildTokenAndListParam(codeParam)); - searchParameterMap.setLastNMax(100); - - List observations = elasticsearchSvc.executeLastN(searchParameterMap, myFhirContext, 100); - - assertEquals(5, observations.size()); - - } - - @Test - public void testLastNCodeSystemOnlyCategorySystemOnly() { - // Include subject and patient - SearchParameterMap searchParameterMap = new SearchParameterMap(); - ReferenceParam subjectParam = new ReferenceParam("Patient", "", "3"); - searchParameterMap.add(Observation.SP_SUBJECT, buildReferenceAndListParam(subjectParam)); - TokenParam categoryParam = new TokenParam("http://mycodes.org/fhir/observation-category", null); - searchParameterMap.add(Observation.SP_CATEGORY, buildTokenAndListParam(categoryParam)); - TokenParam codeParam = new TokenParam("http://mycodes.org/fhir/observation-code", null); - searchParameterMap.add(Observation.SP_CODE, buildTokenAndListParam(codeParam)); - searchParameterMap.setLastNMax(100); - - List observations = elasticsearchSvc.executeLastN(searchParameterMap, myFhirContext, 100); - - assertEquals(10, observations.size()); - } - - @Test - public void testLastNCodeCodeTextCategoryTextOnly() { - SearchParameterMap searchParameterMap = new SearchParameterMap(); - ReferenceParam subjectParam = new ReferenceParam("Patient", "", "3"); - - // Check case match - searchParameterMap.add(Observation.SP_SUBJECT, buildReferenceAndListParam(subjectParam)); - TokenParam categoryParam = new TokenParam("Heart"); - categoryParam.setModifier(TokenParamModifier.TEXT); - searchParameterMap.add(Observation.SP_CATEGORY, buildTokenAndListParam(categoryParam)); - TokenParam codeParam = new TokenParam("Code1"); - codeParam.setModifier(TokenParamModifier.TEXT); - searchParameterMap.add(Observation.SP_CODE, buildTokenAndListParam(codeParam)); - searchParameterMap.setLastNMax(100); - - List observations = elasticsearchSvc.executeLastN(searchParameterMap, myFhirContext, 100); - - assertEquals(5, observations.size()); - - // Check case not match - searchParameterMap = new SearchParameterMap(); - searchParameterMap.add(Observation.SP_SUBJECT, buildReferenceAndListParam(subjectParam)); - categoryParam = new TokenParam("heart"); - categoryParam.setModifier(TokenParamModifier.TEXT); - searchParameterMap.add(Observation.SP_CATEGORY, buildTokenAndListParam(categoryParam)); - codeParam = new TokenParam("code1"); - codeParam.setModifier(TokenParamModifier.TEXT); - searchParameterMap.add(Observation.SP_CODE, buildTokenAndListParam(codeParam)); - searchParameterMap.setLastNMax(100); - - observations = elasticsearchSvc.executeLastN(searchParameterMap, myFhirContext, 100); - - assertEquals(5, observations.size()); - - // Check hyphenated strings - searchParameterMap = new SearchParameterMap(); - searchParameterMap.add(Observation.SP_SUBJECT, buildReferenceAndListParam(subjectParam)); - categoryParam = new TokenParam("heart-rate"); - categoryParam.setModifier(TokenParamModifier.TEXT); - searchParameterMap.add(Observation.SP_CATEGORY, buildTokenAndListParam(categoryParam)); - codeParam = new TokenParam("code1"); - codeParam.setModifier(TokenParamModifier.TEXT); - searchParameterMap.add(Observation.SP_CODE, buildTokenAndListParam(codeParam)); - searchParameterMap.setLastNMax(100); - - observations = elasticsearchSvc.executeLastN(searchParameterMap, myFhirContext, 100); - - assertEquals(5, observations.size()); - - // Check partial strings - searchParameterMap = new SearchParameterMap(); - searchParameterMap.add(Observation.SP_SUBJECT, buildReferenceAndListParam(subjectParam)); - categoryParam = new TokenParam("hear"); - categoryParam.setModifier(TokenParamModifier.TEXT); - searchParameterMap.add(Observation.SP_CATEGORY, buildTokenAndListParam(categoryParam)); - codeParam = new TokenParam("1-obs"); - codeParam.setModifier(TokenParamModifier.TEXT); - searchParameterMap.add(Observation.SP_CODE, buildTokenAndListParam(codeParam)); - searchParameterMap.setLastNMax(100); - - observations = elasticsearchSvc.executeLastN(searchParameterMap, myFhirContext, 100); - - assertEquals(5, observations.size()); - - } - - @Test - public void testLastNNoMatchQueries() { - - ReferenceParam validPatientParam = new ReferenceParam("Patient", "", "9"); - TokenParam validCategoryCodeParam = new TokenParam("http://mycodes.org/fhir/observation-category", "test-heart-rate"); - TokenParam validObservationCodeParam = new TokenParam("http://mycodes.org/fhir/observation-code", "test-code-1"); - DateParam validDateParam = new DateParam(ParamPrefixEnum.EQUAL, new Date(TEST_BASELINE_TIMESTAMP - (9 * 3600 * 1000))); - - // Ensure that valid parameters are indeed valid - SearchParameterMap searchParameterMap = new SearchParameterMap(); - searchParameterMap.add(Observation.SP_PATIENT, buildReferenceAndListParam(validPatientParam)); - searchParameterMap.add(Observation.SP_CATEGORY, buildTokenAndListParam(validCategoryCodeParam)); - searchParameterMap.add(Observation.SP_CODE, buildTokenAndListParam(validObservationCodeParam)); - searchParameterMap.add(Observation.SP_DATE, validDateParam); - searchParameterMap.setLastNMax(100); - - List observations = elasticsearchSvc.executeLastN(searchParameterMap, myFhirContext, 100); - assertEquals(1, observations.size()); - - // Invalid Patient - searchParameterMap = new SearchParameterMap(); - ReferenceParam patientParam = new ReferenceParam("Patient", "", "10"); - searchParameterMap.add(Observation.SP_PATIENT, buildReferenceAndListParam(patientParam)); - searchParameterMap.add(Observation.SP_CATEGORY, buildTokenAndListParam(validCategoryCodeParam)); - searchParameterMap.add(Observation.SP_CODE, buildTokenAndListParam(validObservationCodeParam)); - searchParameterMap.add(Observation.SP_DATE, validDateParam); - searchParameterMap.setLastNMax(100); - - observations = elasticsearchSvc.executeLastN(searchParameterMap, myFhirContext, 100); - assertEquals(0, observations.size()); - - // Invalid subject - searchParameterMap = new SearchParameterMap(); - searchParameterMap.add(Observation.SP_SUBJECT, buildReferenceAndListParam(patientParam)); - searchParameterMap.add(Observation.SP_CATEGORY, buildTokenAndListParam(validCategoryCodeParam)); - searchParameterMap.add(Observation.SP_CODE, buildTokenAndListParam(validObservationCodeParam)); - searchParameterMap.add(Observation.SP_DATE, validDateParam); - searchParameterMap.setLastNMax(100); - - observations = elasticsearchSvc.executeLastN(searchParameterMap, myFhirContext, 100); - assertEquals(0, observations.size()); - - // Invalid observation code - searchParameterMap = new SearchParameterMap(); - searchParameterMap.add(Observation.SP_SUBJECT, buildReferenceAndListParam(validPatientParam)); - searchParameterMap.add(Observation.SP_CATEGORY, buildTokenAndListParam(validCategoryCodeParam)); - TokenParam codeParam = new TokenParam("http://mycodes.org/fhir/observation-code", "test-code-999"); - searchParameterMap.add(Observation.SP_CODE, buildTokenAndListParam(codeParam)); - searchParameterMap.add(Observation.SP_DATE, validDateParam); - searchParameterMap.setLastNMax(100); - - observations = elasticsearchSvc.executeLastN(searchParameterMap, myFhirContext, 100); - assertEquals(0, observations.size()); - - // Invalid category code - searchParameterMap = new SearchParameterMap(); - searchParameterMap.add(Observation.SP_SUBJECT, buildReferenceAndListParam(validPatientParam)); - TokenParam categoryParam = new TokenParam("http://mycodes.org/fhir/observation-category", "test-not-a-category"); - searchParameterMap.add(Observation.SP_CATEGORY, buildTokenAndListParam(categoryParam)); - searchParameterMap.add(Observation.SP_CODE, buildTokenAndListParam(validObservationCodeParam)); - searchParameterMap.add(Observation.SP_DATE, validDateParam); - searchParameterMap.setLastNMax(100); - - observations = elasticsearchSvc.executeLastN(searchParameterMap, myFhirContext, 100); - assertEquals(0, observations.size()); - - // Invalid date - searchParameterMap = new SearchParameterMap(); - searchParameterMap.add(Observation.SP_SUBJECT, buildReferenceAndListParam(validPatientParam)); - searchParameterMap.add(Observation.SP_CATEGORY, buildTokenAndListParam(validCategoryCodeParam)); - searchParameterMap.add(Observation.SP_CODE, buildTokenAndListParam(validObservationCodeParam)); - searchParameterMap.add(Observation.SP_DATE, new DateParam(ParamPrefixEnum.GREATERTHAN, TEST_BASELINE_TIMESTAMP)); - searchParameterMap.setLastNMax(100); - observations = elasticsearchSvc.executeLastN(searchParameterMap, myFhirContext, 100); - assertEquals(0, observations.size()); - - } - - @Test - public void testLastNEffectiveDates() { - Date highDate = new Date(TEST_BASELINE_TIMESTAMP - (3600 * 1000)); - Date lowDate = new Date(TEST_BASELINE_TIMESTAMP - (10 * 3600 * 1000)); - - SearchParameterMap searchParameterMap = new SearchParameterMap(); - ReferenceParam subjectParam = new ReferenceParam("Patient", "", "3"); - searchParameterMap.add(Observation.SP_SUBJECT, buildReferenceAndListParam(subjectParam)); - DateParam dateParam = new DateParam(ParamPrefixEnum.EQUAL, lowDate); - searchParameterMap.add(Observation.SP_DATE, dateParam); - searchParameterMap.setLastNMax(100); - List observations = elasticsearchSvc.executeLastN(searchParameterMap, myFhirContext, 100); - assertEquals(1, observations.size()); - - searchParameterMap = new SearchParameterMap(); - searchParameterMap.add(Observation.SP_SUBJECT, buildReferenceAndListParam(subjectParam)); - dateParam = new DateParam(ParamPrefixEnum.GREATERTHAN_OR_EQUALS, lowDate); - searchParameterMap.add(Observation.SP_DATE, dateParam); - searchParameterMap.setLastNMax(100); - observations = elasticsearchSvc.executeLastN(searchParameterMap, myFhirContext, 100); - assertEquals(10, observations.size()); - - searchParameterMap = new SearchParameterMap(); - searchParameterMap.add(Observation.SP_SUBJECT, buildReferenceAndListParam(subjectParam)); - dateParam = new DateParam(ParamPrefixEnum.GREATERTHAN, lowDate); - searchParameterMap.add(Observation.SP_DATE, dateParam); - searchParameterMap.setLastNMax(100); - observations = elasticsearchSvc.executeLastN(searchParameterMap, myFhirContext, 100); - assertEquals(9, observations.size()); - - searchParameterMap = new SearchParameterMap(); - searchParameterMap.add(Observation.SP_SUBJECT, buildReferenceAndListParam(subjectParam)); - dateParam = new DateParam(ParamPrefixEnum.STARTS_AFTER, lowDate); - searchParameterMap.add(Observation.SP_DATE, dateParam); - searchParameterMap.setLastNMax(100); - observations = elasticsearchSvc.executeLastN(searchParameterMap, myFhirContext, 100); - assertEquals(9, observations.size()); - - searchParameterMap = new SearchParameterMap(); - searchParameterMap.add(Observation.SP_SUBJECT, buildReferenceAndListParam(subjectParam)); - dateParam = new DateParam(ParamPrefixEnum.LESSTHAN_OR_EQUALS, highDate); - searchParameterMap.add(Observation.SP_DATE, dateParam); - searchParameterMap.setLastNMax(100); - observations = elasticsearchSvc.executeLastN(searchParameterMap, myFhirContext, 100); - assertEquals(10, observations.size()); - - searchParameterMap = new SearchParameterMap(); - searchParameterMap.add(Observation.SP_SUBJECT, buildReferenceAndListParam(subjectParam)); - dateParam = new DateParam(ParamPrefixEnum.LESSTHAN, highDate); - searchParameterMap.add(Observation.SP_DATE, dateParam); - searchParameterMap.setLastNMax(100); - observations = elasticsearchSvc.executeLastN(searchParameterMap, myFhirContext, 100); - assertEquals(9, observations.size()); - - searchParameterMap = new SearchParameterMap(); - searchParameterMap.add(Observation.SP_SUBJECT, buildReferenceAndListParam(subjectParam)); - dateParam = new DateParam(ParamPrefixEnum.ENDS_BEFORE, highDate); - searchParameterMap.add(Observation.SP_DATE, dateParam); - searchParameterMap.setLastNMax(100); - observations = elasticsearchSvc.executeLastN(searchParameterMap, myFhirContext, 100); - assertEquals(9, observations.size()); - - searchParameterMap = new SearchParameterMap(); - searchParameterMap.add(Observation.SP_SUBJECT, buildReferenceAndListParam(subjectParam)); - DateParam startDateParam = new DateParam(ParamPrefixEnum.GREATERTHAN, new Date(TEST_BASELINE_TIMESTAMP - (4 * 3600 * 1000))); - DateAndListParam dateAndListParam = new DateAndListParam(); - dateAndListParam.addAnd(new DateOrListParam().addOr(startDateParam)); - dateParam = new DateParam(ParamPrefixEnum.LESSTHAN_OR_EQUALS, highDate); - dateAndListParam.addAnd(new DateOrListParam().addOr(dateParam)); - searchParameterMap.add(Observation.SP_DATE, dateAndListParam); - searchParameterMap.setLastNMax(100); - observations = elasticsearchSvc.executeLastN(searchParameterMap, myFhirContext, 100); - assertEquals(3, observations.size()); - - searchParameterMap = new SearchParameterMap(); - searchParameterMap.add(Observation.SP_SUBJECT, buildReferenceAndListParam(subjectParam)); - startDateParam = new DateParam(ParamPrefixEnum.GREATERTHAN, new Date(TEST_BASELINE_TIMESTAMP - (4 * 3600 * 1000))); - searchParameterMap.add(Observation.SP_DATE, startDateParam); - dateParam = new DateParam(ParamPrefixEnum.LESSTHAN, lowDate); - searchParameterMap.add(Observation.SP_DATE, dateParam); - searchParameterMap.setLastNMax(100); - observations = elasticsearchSvc.executeLastN(searchParameterMap, myFhirContext, 100); - assertEquals(0, observations.size()); - - } - - private void createMultiplePatientsAndObservations() throws IOException { - List patientIds = IntStream.range(0, 10).boxed().collect(Collectors.toList()); - List observations = LastNTestDataGenerator.createMultipleObservationJson(patientIds); - - observations.forEach(observation -> { - CodeJson codeJson = observation.getCode(); - assertTrue(elasticsearchSvc.createOrUpdateObservationCodeIndex(codeJson.getCodeableConceptId(), codeJson)); - assertTrue(elasticsearchSvc.createOrUpdateObservationIndex(observation.getIdentifier(), observation)); - - String subject = observation.getSubject(); - if (createdPatientObservationMap.containsKey(subject)) { - Map> observationCodeMap = createdPatientObservationMap.get(subject); - if (observationCodeMap.containsKey(observation.getCode_concept_id())) { - List observationDates = observationCodeMap.get(observation.getCode_concept_id()); - observationDates.add(observation.getEffectiveDtm()); - observationDates.sort(Collections.reverseOrder()); - } else { - ArrayList observationDates = new ArrayList<>(); - observationDates.add(observation.getEffectiveDtm()); - observationCodeMap.put(observation.getCode_concept_id(), observationDates); - } - } else { - ArrayList observationDates = new ArrayList<>(); - observationDates.add(observation.getEffectiveDtm()); - Map> codeObservationMap = new HashMap<>(); - codeObservationMap.put(observation.getCode_concept_id(), observationDates); - createdPatientObservationMap.put(subject, codeObservationMap); - } - }); - - elasticsearchSvc.refreshIndex(ElasticsearchSvcImpl.OBSERVATION_INDEX); - elasticsearchSvc.refreshIndex(ElasticsearchSvcImpl.OBSERVATION_CODE_INDEX); - - } - - @Test - public void testLastNNoParamsQuery() { - SearchParameterMap searchParameterMap = new SearchParameterMap(); - searchParameterMap.setLastNMax(1); - List observations = elasticsearchSvc.executeLastNWithAllFieldsForTest(searchParameterMap, myFhirContext); - - assertEquals(2, observations.size()); - - String observationCode1 = observations.get(0).getCode_coding_code_system_hash(); - String observationCode2 = observations.get(1).getCode_coding_code_system_hash(); - - assertNotEquals(observationCode1, observationCode2); - - } - -} diff --git a/hapi-fhir-jpaserver-elastic-test-utilities/src/test/java/ca/uhn/fhir/jpa/search/lastn/LastNElasticsearchSvcSingleObservationIT.java b/hapi-fhir-jpaserver-elastic-test-utilities/src/test/java/ca/uhn/fhir/jpa/search/lastn/LastNElasticsearchSvcSingleObservationIT.java deleted file mode 100644 index 962241e227b..00000000000 --- a/hapi-fhir-jpaserver-elastic-test-utilities/src/test/java/ca/uhn/fhir/jpa/search/lastn/LastNElasticsearchSvcSingleObservationIT.java +++ /dev/null @@ -1,262 +0,0 @@ -package ca.uhn.fhir.jpa.search.lastn; - -import ca.uhn.fhir.context.FhirContext; -import ca.uhn.fhir.jpa.model.config.PartitionSettings; -import ca.uhn.fhir.jpa.model.util.CodeSystemHash; -import ca.uhn.fhir.jpa.search.lastn.json.CodeJson; -import ca.uhn.fhir.jpa.search.lastn.json.ObservationJson; -import ca.uhn.fhir.jpa.searchparam.SearchParameterMap; -import ca.uhn.fhir.jpa.test.config.TestElasticsearchContainerHelper; -import ca.uhn.fhir.model.dstu2.resource.Observation; -import ca.uhn.fhir.rest.param.DateParam; -import ca.uhn.fhir.rest.param.ParamPrefixEnum; -import ca.uhn.fhir.rest.param.ReferenceAndListParam; -import ca.uhn.fhir.rest.param.ReferenceOrListParam; -import ca.uhn.fhir.rest.param.ReferenceParam; -import ca.uhn.fhir.rest.param.TokenAndListParam; -import ca.uhn.fhir.rest.param.TokenOrListParam; -import ca.uhn.fhir.rest.param.TokenParam; -import ca.uhn.fhir.test.utilities.docker.RequiresDocker; -import org.junit.jupiter.api.AfterEach; -import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.Test; -import org.junit.jupiter.api.extension.ExtendWith; -import org.springframework.test.context.junit.jupiter.SpringExtension; -import org.testcontainers.elasticsearch.ElasticsearchContainer; -import org.testcontainers.junit.jupiter.Container; -import org.testcontainers.junit.jupiter.Testcontainers; - -import java.io.IOException; -import java.util.Date; -import java.util.List; - -import static ca.uhn.fhir.jpa.search.lastn.LastNTestDataGenerator.CATEGORYFIRSTCODINGSYSTEM; -import static ca.uhn.fhir.jpa.search.lastn.LastNTestDataGenerator.CATEGORYSECONDCODINGSYSTEM; -import static ca.uhn.fhir.jpa.search.lastn.LastNTestDataGenerator.CATEGORYTHIRDCODINGSYSTEM; -import static ca.uhn.fhir.jpa.search.lastn.LastNTestDataGenerator.CODEFIRSTCODINGCODE; -import static ca.uhn.fhir.jpa.search.lastn.LastNTestDataGenerator.CODEFIRSTCODINGDISPLAY; -import static ca.uhn.fhir.jpa.search.lastn.LastNTestDataGenerator.CODEFIRSTCODINGSYSTEM; -import static ca.uhn.fhir.jpa.search.lastn.LastNTestDataGenerator.FIRSTCATEGORYFIRSTCODINGCODE; -import static ca.uhn.fhir.jpa.search.lastn.LastNTestDataGenerator.FIRSTCATEGORYFIRSTCODINGDISPLAY; -import static ca.uhn.fhir.jpa.search.lastn.LastNTestDataGenerator.FIRSTCATEGORYSECONDCODINGCODE; -import static ca.uhn.fhir.jpa.search.lastn.LastNTestDataGenerator.FIRSTCATEGORYSECONDCODINGDISPLAY; -import static ca.uhn.fhir.jpa.search.lastn.LastNTestDataGenerator.FIRSTCATEGORYTEXT; -import static ca.uhn.fhir.jpa.search.lastn.LastNTestDataGenerator.FIRSTCATEGORYTHIRDCODINGCODE; -import static ca.uhn.fhir.jpa.search.lastn.LastNTestDataGenerator.FIRSTCATEGORYTHIRDCODINGDISPLAY; -import static ca.uhn.fhir.jpa.search.lastn.LastNTestDataGenerator.OBSERVATIONSINGLECODEID; -import static ca.uhn.fhir.jpa.search.lastn.LastNTestDataGenerator.OBSERVATION_CODE_CONCEPT_TEXT_1; -import static ca.uhn.fhir.jpa.search.lastn.LastNTestDataGenerator.SECONDCATEGORYFIRSTCODINGCODE; -import static ca.uhn.fhir.jpa.search.lastn.LastNTestDataGenerator.SECONDCATEGORYFIRSTCODINGDISPLAY; -import static ca.uhn.fhir.jpa.search.lastn.LastNTestDataGenerator.SECONDCATEGORYSECONDCODINGCODE; -import static ca.uhn.fhir.jpa.search.lastn.LastNTestDataGenerator.SECONDCATEGORYSECONDCODINGDISPLAY; -import static ca.uhn.fhir.jpa.search.lastn.LastNTestDataGenerator.SECONDCATEGORYTEXT; -import static ca.uhn.fhir.jpa.search.lastn.LastNTestDataGenerator.SECONDCATEGORYTHIRDCODINGCODE; -import static ca.uhn.fhir.jpa.search.lastn.LastNTestDataGenerator.SECONDCATEGORYTHIRDCODINGDISPLAY; -import static ca.uhn.fhir.jpa.search.lastn.LastNTestDataGenerator.SINGLE_OBSERVATION_RESOURCE_PID; -import static ca.uhn.fhir.jpa.search.lastn.LastNTestDataGenerator.SINGLE_OBSERVATION_SUBJECT_ID; -import static ca.uhn.fhir.jpa.search.lastn.LastNTestDataGenerator.TEST_BASELINE_TIMESTAMP; -import static ca.uhn.fhir.jpa.search.lastn.LastNTestDataGenerator.THIRDCATEGORYFIRSTCODINGCODE; -import static ca.uhn.fhir.jpa.search.lastn.LastNTestDataGenerator.THIRDCATEGORYFIRSTCODINGDISPLAY; -import static ca.uhn.fhir.jpa.search.lastn.LastNTestDataGenerator.THIRDCATEGORYSECONDCODINGCODE; -import static ca.uhn.fhir.jpa.search.lastn.LastNTestDataGenerator.THIRDCATEGORYSECONDCODINGDISPLAY; -import static ca.uhn.fhir.jpa.search.lastn.LastNTestDataGenerator.THIRDCATEGORYTEXT; -import static ca.uhn.fhir.jpa.search.lastn.LastNTestDataGenerator.THIRDCATEGORYTHIRDCODINGCODE; -import static ca.uhn.fhir.jpa.search.lastn.LastNTestDataGenerator.THIRDCATEGORYTHIRDCODINGDISPLAY; -import static org.junit.jupiter.api.Assertions.assertEquals; -import static org.junit.jupiter.api.Assertions.assertTrue; - -@ExtendWith(SpringExtension.class) -@RequiresDocker -@Testcontainers -public class LastNElasticsearchSvcSingleObservationIT { - - private final FhirContext myFhirContext = FhirContext.forR4Cached(); - - ElasticsearchSvcImpl elasticsearchSvc; - - @Container - public static ElasticsearchContainer elasticsearchContainer = TestElasticsearchContainerHelper.getEmbeddedElasticSearch(); - - - @BeforeEach - public void before() { - PartitionSettings partitionSettings = new PartitionSettings(); - partitionSettings.setPartitioningEnabled(false); - elasticsearchSvc = new ElasticsearchSvcImpl(partitionSettings, "http", elasticsearchContainer.getHost() + ":" + elasticsearchContainer.getMappedPort(9200), "", ""); - } - - @AfterEach - public void after() throws IOException { - elasticsearchSvc.deleteAllDocumentsForTest(ElasticsearchSvcImpl.OBSERVATION_INDEX); - elasticsearchSvc.deleteAllDocumentsForTest(ElasticsearchSvcImpl.OBSERVATION_CODE_INDEX); - elasticsearchSvc.refreshIndex(ElasticsearchSvcImpl.OBSERVATION_INDEX); - elasticsearchSvc.refreshIndex(ElasticsearchSvcImpl.OBSERVATION_CODE_INDEX); - } - - @Test - public void testSingleObservationQuery() throws IOException { - - // Create test observation - ObservationJson indexedObservation = LastNTestDataGenerator.createSingleObservationJson(); - assertTrue(elasticsearchSvc.createOrUpdateObservationIndex(SINGLE_OBSERVATION_RESOURCE_PID, indexedObservation)); - assertTrue(elasticsearchSvc.createOrUpdateObservationCodeIndex(OBSERVATIONSINGLECODEID, indexedObservation.getCode())); - - elasticsearchSvc.refreshIndex(ElasticsearchSvcImpl.OBSERVATION_INDEX); - elasticsearchSvc.refreshIndex(ElasticsearchSvcImpl.OBSERVATION_CODE_INDEX); - - SearchParameterMap searchParameterMap = new SearchParameterMap(); - ReferenceParam subjectParam = new ReferenceParam("Patient", "", SINGLE_OBSERVATION_SUBJECT_ID); - searchParameterMap.add(Observation.SP_SUBJECT, new ReferenceAndListParam().addAnd(new ReferenceOrListParam().addOr(subjectParam))); - TokenParam categoryParam = new TokenParam(CATEGORYFIRSTCODINGSYSTEM, FIRSTCATEGORYFIRSTCODINGCODE); - searchParameterMap.add(Observation.SP_CATEGORY, new TokenAndListParam().addAnd(new TokenOrListParam().addOr(categoryParam))); - TokenParam codeParam = new TokenParam(CODEFIRSTCODINGSYSTEM, CODEFIRSTCODINGCODE); - searchParameterMap.add(Observation.SP_CODE, new TokenAndListParam().addAnd(new TokenOrListParam().addOr(codeParam))); - searchParameterMap.add(Observation.SP_DATE, new DateParam(ParamPrefixEnum.EQUAL, new Date(TEST_BASELINE_TIMESTAMP))); - - searchParameterMap.setLastNMax(3); - - // execute Observation ID search - List observationIdsOnly = elasticsearchSvc.executeLastN(searchParameterMap, myFhirContext, 100); - - assertEquals(1, observationIdsOnly.size()); - assertEquals(SINGLE_OBSERVATION_RESOURCE_PID, observationIdsOnly.get(0)); - - // execute Observation search for all search fields - List observations = elasticsearchSvc.executeLastNWithAllFieldsForTest(searchParameterMap, myFhirContext); - - validateFullObservationSearch(observations); - } - - private void validateFullObservationSearch(List observations) throws IOException { - - assertEquals(1, observations.size()); - ObservationJson observation = observations.get(0); - assertEquals(SINGLE_OBSERVATION_RESOURCE_PID, observation.getIdentifier()); - - assertEquals(SINGLE_OBSERVATION_SUBJECT_ID, observation.getSubject()); - assertEquals(SINGLE_OBSERVATION_RESOURCE_PID, observation.getIdentifier()); - assertEquals(new Date(TEST_BASELINE_TIMESTAMP), observation.getEffectiveDtm()); - assertEquals(OBSERVATIONSINGLECODEID, observation.getCode_concept_id()); - - List category_concept_text_values = observation.getCategory_concept_text(); - assertEquals(3, category_concept_text_values.size()); - assertEquals(FIRSTCATEGORYTEXT, category_concept_text_values.get(0)); - assertEquals(SECONDCATEGORYTEXT, category_concept_text_values.get(1)); - assertEquals(THIRDCATEGORYTEXT, category_concept_text_values.get(2)); - - List> category_codings_systems = observation.getCategory_coding_system(); - assertEquals(3, category_codings_systems.size()); - List category_coding_systems = category_codings_systems.get(0); - assertEquals(3, category_coding_systems.size()); - assertEquals(CATEGORYFIRSTCODINGSYSTEM, category_coding_systems.get(0)); - assertEquals(CATEGORYSECONDCODINGSYSTEM, category_coding_systems.get(1)); - assertEquals(CATEGORYTHIRDCODINGSYSTEM, category_coding_systems.get(2)); - category_coding_systems = category_codings_systems.get(1); - assertEquals(3, category_coding_systems.size()); - assertEquals(CATEGORYFIRSTCODINGSYSTEM, category_coding_systems.get(0)); - assertEquals(CATEGORYSECONDCODINGSYSTEM, category_coding_systems.get(1)); - assertEquals(CATEGORYTHIRDCODINGSYSTEM, category_coding_systems.get(2)); - category_coding_systems = category_codings_systems.get(2); - assertEquals(3, category_coding_systems.size()); - assertEquals(CATEGORYFIRSTCODINGSYSTEM, category_coding_systems.get(0)); - assertEquals(CATEGORYSECONDCODINGSYSTEM, category_coding_systems.get(1)); - assertEquals(CATEGORYTHIRDCODINGSYSTEM, category_coding_systems.get(2)); - - List> category_codings_codes = observation.getCategory_coding_code(); - assertEquals(3, category_codings_codes.size()); - List category_coding_codes = category_codings_codes.get(0); - assertEquals(3, category_coding_codes.size()); - assertEquals(FIRSTCATEGORYFIRSTCODINGCODE, category_coding_codes.get(0)); - assertEquals(FIRSTCATEGORYSECONDCODINGCODE, category_coding_codes.get(1)); - assertEquals(FIRSTCATEGORYTHIRDCODINGCODE, category_coding_codes.get(2)); - category_coding_codes = category_codings_codes.get(1); - assertEquals(3, category_coding_codes.size()); - assertEquals(SECONDCATEGORYFIRSTCODINGCODE, category_coding_codes.get(0)); - assertEquals(SECONDCATEGORYSECONDCODINGCODE, category_coding_codes.get(1)); - assertEquals(SECONDCATEGORYTHIRDCODINGCODE, category_coding_codes.get(2)); - category_coding_codes = category_codings_codes.get(2); - assertEquals(3, category_coding_codes.size()); - assertEquals(THIRDCATEGORYFIRSTCODINGCODE, category_coding_codes.get(0)); - assertEquals(THIRDCATEGORYSECONDCODINGCODE, category_coding_codes.get(1)); - assertEquals(THIRDCATEGORYTHIRDCODINGCODE, category_coding_codes.get(2)); - - List> category_codings_displays = observation.getCategory_coding_display(); - assertEquals(3, category_codings_displays.size()); - List category_coding_displays = category_codings_displays.get(0); - assertEquals(FIRSTCATEGORYFIRSTCODINGDISPLAY, category_coding_displays.get(0)); - assertEquals(FIRSTCATEGORYSECONDCODINGDISPLAY, category_coding_displays.get(1)); - assertEquals(FIRSTCATEGORYTHIRDCODINGDISPLAY, category_coding_displays.get(2)); - category_coding_displays = category_codings_displays.get(1); - assertEquals(3, category_coding_displays.size()); - assertEquals(SECONDCATEGORYFIRSTCODINGDISPLAY, category_coding_displays.get(0)); - assertEquals(SECONDCATEGORYSECONDCODINGDISPLAY, category_coding_displays.get(1)); - assertEquals(SECONDCATEGORYTHIRDCODINGDISPLAY, category_coding_displays.get(2)); - category_coding_displays = category_codings_displays.get(2); - assertEquals(3, category_coding_displays.size()); - assertEquals(THIRDCATEGORYFIRSTCODINGDISPLAY, category_coding_displays.get(0)); - assertEquals(THIRDCATEGORYSECONDCODINGDISPLAY, category_coding_displays.get(1)); - assertEquals(THIRDCATEGORYTHIRDCODINGDISPLAY, category_coding_displays.get(2)); - - List> category_codings_code_system_hashes = observation.getCategory_coding_code_system_hash(); - assertEquals(3, category_codings_code_system_hashes.size()); - List category_coding_code_system_hashes = category_codings_code_system_hashes.get(0); - assertEquals(String.valueOf(CodeSystemHash.hashCodeSystem(CATEGORYFIRSTCODINGSYSTEM, FIRSTCATEGORYFIRSTCODINGCODE)), category_coding_code_system_hashes.get(0)); - assertEquals(String.valueOf(CodeSystemHash.hashCodeSystem(CATEGORYSECONDCODINGSYSTEM, FIRSTCATEGORYSECONDCODINGCODE)), category_coding_code_system_hashes.get(1)); - assertEquals(String.valueOf(CodeSystemHash.hashCodeSystem(CATEGORYTHIRDCODINGSYSTEM, FIRSTCATEGORYTHIRDCODINGCODE)), category_coding_code_system_hashes.get(2)); - category_coding_code_system_hashes = category_codings_code_system_hashes.get(1); - assertEquals(3, category_coding_code_system_hashes.size()); - assertEquals(String.valueOf(CodeSystemHash.hashCodeSystem(CATEGORYFIRSTCODINGSYSTEM, SECONDCATEGORYFIRSTCODINGCODE)), category_coding_code_system_hashes.get(0)); - assertEquals(String.valueOf(CodeSystemHash.hashCodeSystem(CATEGORYSECONDCODINGSYSTEM, SECONDCATEGORYSECONDCODINGCODE)), category_coding_code_system_hashes.get(1)); - assertEquals(String.valueOf(CodeSystemHash.hashCodeSystem(CATEGORYTHIRDCODINGSYSTEM, SECONDCATEGORYTHIRDCODINGCODE)), category_coding_code_system_hashes.get(2)); - category_coding_code_system_hashes = category_codings_code_system_hashes.get(2); - assertEquals(3, category_coding_code_system_hashes.size()); - assertEquals(String.valueOf(CodeSystemHash.hashCodeSystem(CATEGORYFIRSTCODINGSYSTEM, THIRDCATEGORYFIRSTCODINGCODE)), category_coding_code_system_hashes.get(0)); - assertEquals(String.valueOf(CodeSystemHash.hashCodeSystem(CATEGORYSECONDCODINGSYSTEM, THIRDCATEGORYSECONDCODINGCODE)), category_coding_code_system_hashes.get(1)); - assertEquals(String.valueOf(CodeSystemHash.hashCodeSystem(CATEGORYTHIRDCODINGSYSTEM, THIRDCATEGORYTHIRDCODINGCODE)), category_coding_code_system_hashes.get(2)); - - String code_concept_text_values = observation.getCode_concept_text(); - assertEquals(OBSERVATION_CODE_CONCEPT_TEXT_1, code_concept_text_values); - - String code_coding_systems = observation.getCode_coding_system(); - assertEquals(CODEFIRSTCODINGSYSTEM, code_coding_systems); - - String code_coding_codes = observation.getCode_coding_code(); - assertEquals(CODEFIRSTCODINGCODE, code_coding_codes); - - String code_coding_display = observation.getCode_coding_display(); - assertEquals(CODEFIRSTCODINGDISPLAY, code_coding_display); - - String code_coding_code_system_hash = observation.getCode_coding_code_system_hash(); - assertEquals(String.valueOf(CodeSystemHash.hashCodeSystem(CODEFIRSTCODINGSYSTEM, CODEFIRSTCODINGCODE)), code_coding_code_system_hash); - - // Retrieve all Observation codes - List codes = elasticsearchSvc.queryAllIndexedObservationCodesForTest(); - assertEquals(1, codes.size()); - CodeJson persistedObservationCode = codes.get(0); - - String persistedCodeConceptID = persistedObservationCode.getCodeableConceptId(); - assertEquals(OBSERVATIONSINGLECODEID, persistedCodeConceptID); - String persistedCodeConceptText = persistedObservationCode.getCodeableConceptText(); - assertEquals(OBSERVATION_CODE_CONCEPT_TEXT_1, persistedCodeConceptText); - - List persistedCodeCodingSystems = persistedObservationCode.getCoding_system(); - assertEquals(1, persistedCodeCodingSystems.size()); - assertEquals(CODEFIRSTCODINGSYSTEM, persistedCodeCodingSystems.get(0)); - - List persistedCodeCodingCodes = persistedObservationCode.getCoding_code(); - assertEquals(1, persistedCodeCodingCodes.size()); - assertEquals(CODEFIRSTCODINGCODE, persistedCodeCodingCodes.get(0)); - - List persistedCodeCodingDisplays = persistedObservationCode.getCoding_display(); - assertEquals(1, persistedCodeCodingDisplays.size()); - assertEquals(CODEFIRSTCODINGDISPLAY, persistedCodeCodingDisplays.get(0)); - - List persistedCodeCodingCodeSystemHashes = persistedObservationCode.getCoding_code_system_hash(); - assertEquals(1, persistedCodeCodingCodeSystemHashes.size()); - assertEquals(String.valueOf(CodeSystemHash.hashCodeSystem(CODEFIRSTCODINGSYSTEM, CODEFIRSTCODINGCODE)), persistedCodeCodingCodeSystemHashes.get(0)); - - - } - -} - diff --git a/hapi-fhir-jpaserver-elastic-test-utilities/src/test/java/ca/uhn/fhir/jpa/search/lastn/LastNTestDataGenerator.java b/hapi-fhir-jpaserver-elastic-test-utilities/src/test/java/ca/uhn/fhir/jpa/search/lastn/LastNTestDataGenerator.java deleted file mode 100644 index ca7b3172d1b..00000000000 --- a/hapi-fhir-jpaserver-elastic-test-utilities/src/test/java/ca/uhn/fhir/jpa/search/lastn/LastNTestDataGenerator.java +++ /dev/null @@ -1,144 +0,0 @@ -package ca.uhn.fhir.jpa.search.lastn; - -import ca.uhn.fhir.jpa.search.lastn.json.CodeJson; -import ca.uhn.fhir.jpa.search.lastn.json.ObservationJson; - -import java.util.Arrays; -import java.util.Collections; -import java.util.Date; -import java.util.List; -import java.util.UUID; -import java.util.stream.Collectors; -import java.util.stream.IntStream; - -public class LastNTestDataGenerator { - - public static final long TEST_BASELINE_TIMESTAMP = new Date().toInstant().toEpochMilli(); - - public static final String SINGLE_OBSERVATION_RESOURCE_PID = "123"; - public static final String SINGLE_OBSERVATION_SUBJECT_ID = "Patient/4567"; - - public static final String FIRSTCATEGORYTEXT = "Test Codeable Concept Field for first category"; - public static final String CATEGORYFIRSTCODINGSYSTEM = "http://mycodes.org/fhir/observation-category"; - public static final String CATEGORYSECONDCODINGSYSTEM = "http://myalternatecodes.org/fhir/observation-category"; - public static final String CATEGORYTHIRDCODINGSYSTEM = "http://mysecondaltcodes.org/fhir/observation-category"; - public static final String FIRSTCATEGORYFIRSTCODINGCODE = "test-heart-rate"; - public static final String FIRSTCATEGORYFIRSTCODINGDISPLAY = "Test Heart Rate"; - public static final String FIRSTCATEGORYSECONDCODINGCODE = "test-alt-heart-rate"; - public static final String FIRSTCATEGORYSECONDCODINGDISPLAY = "Test HeartRate"; - public static final String FIRSTCATEGORYTHIRDCODINGCODE = "test-2nd-alt-heart-rate"; - public static final String FIRSTCATEGORYTHIRDCODINGDISPLAY = "Test Heart-Rate"; - public static final String SECONDCATEGORYTEXT = "Test Codeable Concept Field for for second category"; - public static final String SECONDCATEGORYFIRSTCODINGCODE = "test-vital-signs"; - public static final String SECONDCATEGORYFIRSTCODINGDISPLAY = "Test Vital Signs"; - public static final String SECONDCATEGORYSECONDCODINGCODE = "test-alt-vitals"; - public static final String SECONDCATEGORYSECONDCODINGDISPLAY = "Test Vital-Signs"; - public static final String SECONDCATEGORYTHIRDCODINGCODE = "test-2nd-alt-vitals"; - public static final String SECONDCATEGORYTHIRDCODINGDISPLAY = "Test Vitals"; - public static final String THIRDCATEGORYTEXT = "Test Codeable Concept Field for third category"; - public static final String THIRDCATEGORYFIRSTCODINGCODE = "test-vital-panel"; - public static final String THIRDCATEGORYFIRSTCODINGDISPLAY = "test-vitals-panel display"; - public static final String THIRDCATEGORYSECONDCODINGCODE = "test-alt-vitals-panel"; - public static final String THIRDCATEGORYSECONDCODINGDISPLAY = "test-alt-vitals display"; - public static final String THIRDCATEGORYTHIRDCODINGCODE = "test-2nd-alt-vitals-panel"; - public static final String THIRDCATEGORYTHIRDCODINGDISPLAY = "test-2nd-alt-vitals-panel display"; - public static final String OBSERVATIONSINGLECODEID = UUID.randomUUID().toString(); - public static final String OBSERVATION_CODE_CONCEPT_TEXT_1 = "Test Codeable Concept Field for First Code"; - public static final String OBSERVATION_CODE_CONCEPT_TEXT_2 = "Test Codeable Concept Field for Second Code"; - public static final String CODEFIRSTCODINGSYSTEM = "http://mycodes.org/fhir/observation-code"; - public static final String CODEFIRSTCODINGCODE = "test-code-1"; - public static final String CODEFIRSTCODINGDISPLAY = "1-Observation Code1"; - public static final String CODE_SECOND_CODING_SYSTEM = "http://mycodes.org/fhir/observation-code"; - public static final String CODE_SECOND_CODING_CODE = "test-code-2"; - public static final String CODE_SECOND_CODING_DISPLAY = "2-Observation Code2"; - - public static ObservationJson createSingleObservationJson() { - ObservationJson indexedObservation = new ObservationJson(); - indexedObservation.setIdentifier(SINGLE_OBSERVATION_RESOURCE_PID); - indexedObservation.setSubject(SINGLE_OBSERVATION_SUBJECT_ID); - indexedObservation.setEffectiveDtm(new Date(TEST_BASELINE_TIMESTAMP)); - - indexedObservation.setCategories(createCategoryCodeableConcepts()); - - // Create CodeableConcept for Code - CodeJson codeableConceptField = new CodeJson(); - codeableConceptField.setCodeableConceptId(OBSERVATIONSINGLECODEID); - codeableConceptField.setCodeableConceptText(OBSERVATION_CODE_CONCEPT_TEXT_1); - codeableConceptField.addCoding(CODEFIRSTCODINGSYSTEM, CODEFIRSTCODINGCODE, CODEFIRSTCODINGDISPLAY); - - indexedObservation.setCode(codeableConceptField); - - return indexedObservation; - } - - private static List createCategoryCodeableConcepts() { - CodeJson categoryCodeableConcept1 = new CodeJson(); - categoryCodeableConcept1.setCodeableConceptText(FIRSTCATEGORYTEXT); - categoryCodeableConcept1.addCoding(CATEGORYFIRSTCODINGSYSTEM, FIRSTCATEGORYFIRSTCODINGCODE, FIRSTCATEGORYFIRSTCODINGDISPLAY); - categoryCodeableConcept1.addCoding(CATEGORYSECONDCODINGSYSTEM, FIRSTCATEGORYSECONDCODINGCODE, FIRSTCATEGORYSECONDCODINGDISPLAY); - categoryCodeableConcept1.addCoding(CATEGORYTHIRDCODINGSYSTEM, FIRSTCATEGORYTHIRDCODINGCODE, FIRSTCATEGORYTHIRDCODINGDISPLAY); - - CodeJson categoryCodeableConcept2 = new CodeJson(); - categoryCodeableConcept2.setCodeableConceptText(SECONDCATEGORYTEXT); - categoryCodeableConcept2.addCoding(CATEGORYFIRSTCODINGSYSTEM, SECONDCATEGORYFIRSTCODINGCODE, SECONDCATEGORYFIRSTCODINGDISPLAY); - categoryCodeableConcept2.addCoding(CATEGORYSECONDCODINGSYSTEM, SECONDCATEGORYSECONDCODINGCODE, SECONDCATEGORYSECONDCODINGDISPLAY); - categoryCodeableConcept2.addCoding(CATEGORYTHIRDCODINGSYSTEM, SECONDCATEGORYTHIRDCODINGCODE, SECONDCATEGORYTHIRDCODINGDISPLAY); - - CodeJson categoryCodeableConcept3 = new CodeJson(); - categoryCodeableConcept3.setCodeableConceptText(THIRDCATEGORYTEXT); - categoryCodeableConcept3.addCoding(CATEGORYFIRSTCODINGSYSTEM, THIRDCATEGORYFIRSTCODINGCODE, THIRDCATEGORYFIRSTCODINGDISPLAY); - categoryCodeableConcept3.addCoding(CATEGORYSECONDCODINGSYSTEM, THIRDCATEGORYSECONDCODINGCODE, THIRDCATEGORYSECONDCODINGDISPLAY); - categoryCodeableConcept3.addCoding(CATEGORYTHIRDCODINGSYSTEM, THIRDCATEGORYTHIRDCODINGCODE, THIRDCATEGORYTHIRDCODINGDISPLAY); - - return Arrays.asList(categoryCodeableConcept1, categoryCodeableConcept2, categoryCodeableConcept3); - } - - public static List createMultipleObservationJson(List thePatientIds) { - - // CodeableConcept 1 - with 3 codings - String codeableConceptId1 = UUID.randomUUID().toString(); - CodeJson codeJson1 = new CodeJson(); - codeJson1.setCodeableConceptId(codeableConceptId1); - codeJson1.setCodeableConceptText(OBSERVATION_CODE_CONCEPT_TEXT_1); - codeJson1.addCoding(CODEFIRSTCODINGSYSTEM, CODEFIRSTCODINGCODE, CODEFIRSTCODINGDISPLAY); - - // CodeableConcept 2 - with 3 codings - String codeableConceptId2 = UUID.randomUUID().toString(); - CodeJson codeJson2 = new CodeJson(); - codeJson2.setCodeableConceptId(codeableConceptId2); - codeJson2.setCodeableConceptText(OBSERVATION_CODE_CONCEPT_TEXT_2); - codeJson2.addCoding(CODE_SECOND_CODING_SYSTEM, CODE_SECOND_CODING_CODE, CODE_SECOND_CODING_DISPLAY); - - List categoryCodeableConcepts = createCategoryCodeableConcepts(); - // CategoryCodeableConcept 1 - with 3 codings - List categoryConcepts1 = Collections.singletonList(categoryCodeableConcepts.get(0)); - - // CateogryCodeableConcept 2 - with 3 codings - List categoryConcepts2 = Collections.singletonList(categoryCodeableConcepts.get(1)); - - // Pair CodeableConcept 1 + CategoryCodeableConcept 1 for odd numbered observation - // Pair CodeableConcept 2 + CategoryCodeableConcept 2 for even numbered observation - - // For each patient - create 10 observations - return thePatientIds.stream() - .flatMap(patientId -> IntStream.range(0, 10) - .mapToObj(index -> { - ObservationJson observationJson = new ObservationJson(); - String identifier = String.valueOf((index + patientId * 10L)); - observationJson.setIdentifier(identifier); - observationJson.setSubject(String.valueOf(patientId)); - if (index % 2 == 1) { - observationJson.setCategories(categoryConcepts1); - observationJson.setCode(codeJson1); - } else { - observationJson.setCategories(categoryConcepts2); - observationJson.setCode(codeJson2); - } - Date effectiveDtm = new Date(TEST_BASELINE_TIMESTAMP - ((10L - index) * 3600L * 1000L)); - observationJson.setEffectiveDtm(effectiveDtm); - return observationJson; - })) - .collect(Collectors.toList()); - } - -} diff --git a/hapi-fhir-jpaserver-hfql/pom.xml b/hapi-fhir-jpaserver-hfql/pom.xml index 6362de695c2..254e53aa47f 100644 --- a/hapi-fhir-jpaserver-hfql/pom.xml +++ b/hapi-fhir-jpaserver-hfql/pom.xml @@ -3,7 +3,7 @@ ca.uhn.hapi.fhir hapi-deployable-pom - 6.9.7-SNAPSHOT + 6.11.7-SNAPSHOT ../hapi-deployable-pom/pom.xml @@ -20,8 +20,8 @@ - javax.servlet - javax.servlet-api + jakarta.servlet + jakarta.servlet-api provided diff --git a/hapi-fhir-jpaserver-hfql/src/main/java/ca/uhn/fhir/jpa/fql/executor/HfqlExecutor.java b/hapi-fhir-jpaserver-hfql/src/main/java/ca/uhn/fhir/jpa/fql/executor/HfqlExecutor.java index 424c713f90a..b364aa969b0 100644 --- a/hapi-fhir-jpaserver-hfql/src/main/java/ca/uhn/fhir/jpa/fql/executor/HfqlExecutor.java +++ b/hapi-fhir-jpaserver-hfql/src/main/java/ca/uhn/fhir/jpa/fql/executor/HfqlExecutor.java @@ -42,6 +42,7 @@ import ca.uhn.fhir.rest.api.server.IBundleProvider; import ca.uhn.fhir.rest.api.server.RequestDetails; import ca.uhn.fhir.rest.param.DateOrListParam; import ca.uhn.fhir.rest.param.DateParam; +import ca.uhn.fhir.rest.param.ParamPrefixEnum; import ca.uhn.fhir.rest.param.ParameterUtil; import ca.uhn.fhir.rest.param.QualifierDetails; import ca.uhn.fhir.rest.param.TokenOrListParam; @@ -51,6 +52,8 @@ import ca.uhn.fhir.rest.server.util.ISearchParamRegistry; import ca.uhn.fhir.rest.server.util.ResourceSearchParams; import ca.uhn.fhir.util.UrlUtil; import com.google.common.collect.Lists; +import jakarta.annotation.Nonnull; +import jakarta.annotation.Nullable; import org.apache.commons.collections4.ListUtils; import org.apache.commons.lang3.Validate; import org.apache.commons.lang3.builder.HashCodeBuilder; @@ -78,8 +81,6 @@ import java.util.concurrent.atomic.AtomicInteger; import java.util.function.Function; import java.util.function.Predicate; import java.util.stream.Collectors; -import javax.annotation.Nonnull; -import javax.annotation.Nullable; import static org.apache.commons.lang3.ObjectUtils.defaultIfNull; import static org.apache.commons.lang3.StringUtils.isBlank; @@ -199,18 +200,77 @@ public class HfqlExecutor implements IHfqlExecutor { } } + /** + * If the user has included a WHERE clause that has a FHIRPath expression but + * could actually be satisfied by a Search Parameter, we'll insert a + * search_match expression so that it's more efficient. + */ private void massageWhereClauses(HfqlStatement theStatement) { - ResourceSearchParams activeSearchParams = - mySearchParamRegistry.getActiveSearchParams(theStatement.getFromResourceName()); + String fromResourceName = theStatement.getFromResourceName(); + ResourceSearchParams activeSearchParams = mySearchParamRegistry.getActiveSearchParams(fromResourceName); for (HfqlStatement.WhereClause nextWhereClause : theStatement.getWhereClauses()) { + + String left = null; + List rightValues = null; + String comparator; if (isDataValueWhereClause(nextWhereClause)) { - if ("id".equals(nextWhereClause.getLeft())) { + left = nextWhereClause.getLeft(); + comparator = ""; + rightValues = nextWhereClause.getRightAsStrings(); + } else if (nextWhereClause.getOperator() == HfqlStatement.WhereClauseOperatorEnum.UNARY_BOOLEAN + && nextWhereClause.getRightAsStrings().size() > 1) { + left = nextWhereClause.getLeft(); + rightValues = nextWhereClause + .getRightAsStrings() + .subList(1, nextWhereClause.getRightAsStrings().size()); + switch (nextWhereClause.getRightAsStrings().get(0)) { + case "=": + comparator = ""; + break; + case "<": + comparator = ParamPrefixEnum.LESSTHAN.getValue(); + break; + case "<=": + comparator = ParamPrefixEnum.LESSTHAN_OR_EQUALS.getValue(); + break; + case ">": + comparator = ParamPrefixEnum.GREATERTHAN.getValue(); + break; + case ">=": + comparator = ParamPrefixEnum.GREATERTHAN_OR_EQUALS.getValue(); + break; + case "!=": + comparator = ParamPrefixEnum.NOT_EQUAL.getValue(); + break; + case "~": + comparator = ParamPrefixEnum.APPROXIMATE.getValue(); + break; + default: + left = null; + comparator = null; + rightValues = null; + } + } else { + comparator = null; + } + + if (left != null) { + if (isFhirPathExpressionEquivalent("id", left, fromResourceName)) { + // This is an expression for Resource.id + nextWhereClause.setLeft("id"); nextWhereClause.setOperator(HfqlStatement.WhereClauseOperatorEnum.SEARCH_MATCH); - String joinedParamValues = nextWhereClause.getRightAsStrings().stream() - .map(ParameterUtil::escape) + String joinedParamValues = + rightValues.stream().map(ParameterUtil::escape).collect(Collectors.joining(",")); + nextWhereClause.setRight(Constants.PARAM_ID, joinedParamValues); + } else if (isFhirPathExpressionEquivalent("meta.lastUpdated", left, fromResourceName)) { + // This is an expression for Resource.meta.lastUpdated + nextWhereClause.setLeft("id"); + nextWhereClause.setOperator(HfqlStatement.WhereClauseOperatorEnum.SEARCH_MATCH); + String joinedParamValues = rightValues.stream() + .map(value -> comparator + ParameterUtil.escape(value)) .collect(Collectors.joining(",")); - nextWhereClause.setRight("_id", joinedParamValues); + nextWhereClause.setRight(Constants.PARAM_LASTUPDATED, joinedParamValues); } } } @@ -490,8 +550,12 @@ public class HfqlExecutor implements IHfqlExecutor { } } } catch (FhirPathExecutionException e) { + String expression = + nextWhereClause.getOperator() == HfqlStatement.WhereClauseOperatorEnum.UNARY_BOOLEAN + ? nextWhereClause.asUnaryExpression() + : nextWhereClause.getLeft(); throw new InvalidRequestException(Msg.code(2403) + "Unable to evaluate FHIRPath expression \"" - + nextWhereClause.getLeft() + "\". Error: " + e.getMessage()); + + expression + "\". Error: " + e.getMessage()); } if (!haveMatch) { @@ -777,6 +841,17 @@ public class HfqlExecutor implements IHfqlExecutor { return new StaticHfqlExecutionResult(null, columns, dataTypes, rows); } + private static boolean isFhirPathExpressionEquivalent( + String wantedExpression, String actualExpression, String fromResourceName) { + if (wantedExpression.equals(actualExpression)) { + return true; + } + if (("Resource." + wantedExpression).equals(actualExpression)) { + return true; + } + return (fromResourceName + "." + wantedExpression).equals(actualExpression); + } + /** * Returns {@literal true} if a where clause has an operator of * {@link ca.uhn.fhir.jpa.fql.parser.HfqlStatement.WhereClauseOperatorEnum#EQUALS} @@ -796,9 +871,10 @@ public class HfqlExecutor implements IHfqlExecutor { private static boolean evaluateWhereClauseUnaryBoolean( HfqlExecutionContext theExecutionContext, IBaseResource r, HfqlStatement.WhereClause theNextWhereClause) { boolean haveMatch = false; - assert theNextWhereClause.getRight().isEmpty(); - List values = - theExecutionContext.evaluate(r, theNextWhereClause.getLeft(), IPrimitiveType.class); + + String fullExpression = theNextWhereClause.asUnaryExpression(); + + List values = theExecutionContext.evaluate(r, fullExpression, IPrimitiveType.class); for (IPrimitiveType nextValue : values) { if (Boolean.TRUE.equals(nextValue.getValue())) { haveMatch = true; diff --git a/hapi-fhir-jpaserver-hfql/src/main/java/ca/uhn/fhir/jpa/fql/executor/IHfqlExecutor.java b/hapi-fhir-jpaserver-hfql/src/main/java/ca/uhn/fhir/jpa/fql/executor/IHfqlExecutor.java index 348ccdb1ecf..8fc6088ab2d 100644 --- a/hapi-fhir-jpaserver-hfql/src/main/java/ca/uhn/fhir/jpa/fql/executor/IHfqlExecutor.java +++ b/hapi-fhir-jpaserver-hfql/src/main/java/ca/uhn/fhir/jpa/fql/executor/IHfqlExecutor.java @@ -21,8 +21,7 @@ package ca.uhn.fhir.jpa.fql.executor; import ca.uhn.fhir.jpa.fql.parser.HfqlStatement; import ca.uhn.fhir.rest.api.server.RequestDetails; - -import javax.annotation.Nullable; +import jakarta.annotation.Nullable; public interface IHfqlExecutor { diff --git a/hapi-fhir-jpaserver-hfql/src/main/java/ca/uhn/fhir/jpa/fql/executor/StaticHfqlExecutionResult.java b/hapi-fhir-jpaserver-hfql/src/main/java/ca/uhn/fhir/jpa/fql/executor/StaticHfqlExecutionResult.java index bc8237acb5a..6259d9f2d1a 100644 --- a/hapi-fhir-jpaserver-hfql/src/main/java/ca/uhn/fhir/jpa/fql/executor/StaticHfqlExecutionResult.java +++ b/hapi-fhir-jpaserver-hfql/src/main/java/ca/uhn/fhir/jpa/fql/executor/StaticHfqlExecutionResult.java @@ -20,11 +20,11 @@ package ca.uhn.fhir.jpa.fql.executor; import ca.uhn.fhir.jpa.fql.parser.HfqlStatement; +import jakarta.annotation.Nullable; import java.util.Collections; import java.util.Iterator; import java.util.List; -import javax.annotation.Nullable; /** * @see IHfqlExecutionResult for information about the purpose of this class diff --git a/hapi-fhir-jpaserver-hfql/src/main/java/ca/uhn/fhir/jpa/fql/jdbc/HfqlRestClient.java b/hapi-fhir-jpaserver-hfql/src/main/java/ca/uhn/fhir/jpa/fql/jdbc/HfqlRestClient.java index 7b8ad5e0f10..8bdfb644be6 100644 --- a/hapi-fhir-jpaserver-hfql/src/main/java/ca/uhn/fhir/jpa/fql/jdbc/HfqlRestClient.java +++ b/hapi-fhir-jpaserver-hfql/src/main/java/ca/uhn/fhir/jpa/fql/jdbc/HfqlRestClient.java @@ -23,6 +23,7 @@ import ca.uhn.fhir.jpa.fql.executor.IHfqlExecutionResult; import ca.uhn.fhir.jpa.fql.util.HfqlConstants; import ca.uhn.fhir.rest.client.impl.HttpBasicAuthInterceptor; import ca.uhn.fhir.util.IoUtil; +import jakarta.annotation.Nonnull; import org.apache.commons.csv.CSVFormat; import org.apache.commons.lang3.Validate; import org.apache.http.impl.client.CloseableHttpClient; @@ -35,7 +36,6 @@ import org.hl7.fhir.r4.model.StringType; import java.sql.SQLException; import java.util.concurrent.TimeUnit; -import javax.annotation.Nonnull; import static ca.uhn.fhir.jpa.fql.util.HfqlConstants.DEFAULT_FETCH_SIZE; import static org.apache.commons.lang3.ObjectUtils.defaultIfNull; diff --git a/hapi-fhir-jpaserver-hfql/src/main/java/ca/uhn/fhir/jpa/fql/jdbc/JdbcConnection.java b/hapi-fhir-jpaserver-hfql/src/main/java/ca/uhn/fhir/jpa/fql/jdbc/JdbcConnection.java index 79627b4450f..3147a290edc 100644 --- a/hapi-fhir-jpaserver-hfql/src/main/java/ca/uhn/fhir/jpa/fql/jdbc/JdbcConnection.java +++ b/hapi-fhir-jpaserver-hfql/src/main/java/ca/uhn/fhir/jpa/fql/jdbc/JdbcConnection.java @@ -20,6 +20,7 @@ package ca.uhn.fhir.jpa.fql.jdbc; import ca.uhn.fhir.i18n.Msg; +import jakarta.annotation.Nonnull; import java.sql.Array; import java.sql.Blob; @@ -39,7 +40,6 @@ import java.sql.Struct; import java.util.Map; import java.util.Properties; import java.util.concurrent.Executor; -import javax.annotation.Nonnull; class JdbcConnection implements Connection { private final String myServerUrl; diff --git a/hapi-fhir-jpaserver-hfql/src/main/java/ca/uhn/fhir/jpa/fql/jdbc/RemoteHfqlExecutionResult.java b/hapi-fhir-jpaserver-hfql/src/main/java/ca/uhn/fhir/jpa/fql/jdbc/RemoteHfqlExecutionResult.java index 5b2c65b31b7..9fee362d699 100644 --- a/hapi-fhir-jpaserver-hfql/src/main/java/ca/uhn/fhir/jpa/fql/jdbc/RemoteHfqlExecutionResult.java +++ b/hapi-fhir-jpaserver-hfql/src/main/java/ca/uhn/fhir/jpa/fql/jdbc/RemoteHfqlExecutionResult.java @@ -32,6 +32,7 @@ import ca.uhn.fhir.rest.server.exceptions.InternalErrorException; import ca.uhn.fhir.util.IoUtil; import ca.uhn.fhir.util.JsonUtil; import ca.uhn.fhir.util.ValidateUtil; +import jakarta.servlet.http.HttpServletResponse; import org.apache.commons.csv.CSVParser; import org.apache.commons.csv.CSVRecord; import org.apache.commons.lang3.Validate; @@ -56,7 +57,6 @@ import java.sql.SQLException; import java.util.ArrayList; import java.util.Iterator; import java.util.List; -import javax.servlet.http.HttpServletResponse; import static ca.uhn.fhir.jpa.fql.util.HfqlConstants.PROTOCOL_VERSION; import static org.apache.commons.lang3.StringUtils.defaultIfBlank; diff --git a/hapi-fhir-jpaserver-hfql/src/main/java/ca/uhn/fhir/jpa/fql/parser/HfqlFhirPathParser.java b/hapi-fhir-jpaserver-hfql/src/main/java/ca/uhn/fhir/jpa/fql/parser/HfqlFhirPathParser.java index 821fda283d2..c5933cc8633 100644 --- a/hapi-fhir-jpaserver-hfql/src/main/java/ca/uhn/fhir/jpa/fql/parser/HfqlFhirPathParser.java +++ b/hapi-fhir-jpaserver-hfql/src/main/java/ca/uhn/fhir/jpa/fql/parser/HfqlFhirPathParser.java @@ -21,10 +21,10 @@ package ca.uhn.fhir.jpa.fql.parser; import ca.uhn.fhir.context.*; import ca.uhn.fhir.jpa.fql.executor.HfqlDataTypeEnum; +import jakarta.annotation.Nullable; import org.apache.commons.text.WordUtils; import java.util.Map; -import javax.annotation.Nullable; import static java.util.Map.entry; diff --git a/hapi-fhir-jpaserver-hfql/src/main/java/ca/uhn/fhir/jpa/fql/parser/HfqlLexer.java b/hapi-fhir-jpaserver-hfql/src/main/java/ca/uhn/fhir/jpa/fql/parser/HfqlLexer.java index b8f4b850a53..07ca278737c 100644 --- a/hapi-fhir-jpaserver-hfql/src/main/java/ca/uhn/fhir/jpa/fql/parser/HfqlLexer.java +++ b/hapi-fhir-jpaserver-hfql/src/main/java/ca/uhn/fhir/jpa/fql/parser/HfqlLexer.java @@ -22,11 +22,11 @@ package ca.uhn.fhir.jpa.fql.parser; import ca.uhn.fhir.i18n.Msg; import ca.uhn.fhir.parser.DataFormatException; import ca.uhn.fhir.rest.server.exceptions.InvalidRequestException; +import jakarta.annotation.Nonnull; import org.apache.commons.lang3.Validate; import java.util.ArrayList; import java.util.List; -import javax.annotation.Nonnull; import static java.lang.Character.isWhitespace; @@ -139,6 +139,22 @@ class HfqlLexer { return; } + for (String nextMultiCharToken : theOptions.getMultiCharTokens()) { + boolean haveStringStartingHere = true; + for (int i = 0; i < nextMultiCharToken.length(); i++) { + if (myInput.length <= myPosition + 1 + || nextMultiCharToken.charAt(i) != myInput[myPosition + i]) { + haveStringStartingHere = false; + break; + } + } + if (haveStringStartingHere) { + setNextToken(theOptions, nextMultiCharToken); + myPosition += nextMultiCharToken.length(); + return; + } + } + if (theNextChar == '\'') { myNextTokenLine = myLine; myNextTokenColumn = myColumn; diff --git a/hapi-fhir-jpaserver-hfql/src/main/java/ca/uhn/fhir/jpa/fql/parser/HfqlLexerOptions.java b/hapi-fhir-jpaserver-hfql/src/main/java/ca/uhn/fhir/jpa/fql/parser/HfqlLexerOptions.java index f724d521c7c..3b98acb9ab7 100644 --- a/hapi-fhir-jpaserver-hfql/src/main/java/ca/uhn/fhir/jpa/fql/parser/HfqlLexerOptions.java +++ b/hapi-fhir-jpaserver-hfql/src/main/java/ca/uhn/fhir/jpa/fql/parser/HfqlLexerOptions.java @@ -19,6 +19,7 @@ */ package ca.uhn.fhir.jpa.fql.parser; +import java.util.List; import java.util.Set; public enum HfqlLexerOptions { @@ -28,18 +29,20 @@ public enum HfqlLexerOptions { * more specialized. */ HFQL_TOKEN( + List.of(">=", "<=", "!="), Set.of( 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z', 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z', '0', '1', '2', '3', '4', '5', '6', '7', - '8', '9', '.', '[', ']', '_'), - Set.of(',', '=', '(', ')', '|', ':', '*'), + '8', '9', '.', '[', ']', '_', '~'), + Set.of(',', '=', '(', ')', '|', ':', '*', '<', '>', '!'), false), /** * A FHIR search parameter name. */ SEARCH_PARAMETER_NAME( + List.of(), Set.of( 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z', 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', @@ -52,12 +55,13 @@ public enum HfqlLexerOptions { * A complete FHIRPath expression. */ FHIRPATH_EXPRESSION( + List.of(">=", "<=", "!="), Set.of( 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z', 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z', '0', '1', '2', '3', '4', '5', '6', '7', - '8', '9', '.', '[', ']', '_', '(', ')', '!', '~', '<', '>', '+', '-'), - Set.of(',', '|', ':', '*', '='), + '8', '9', '.', '[', ']', '_', '(', ')', '+', '-'), + Set.of(',', '|', ':', '*', '=', '<', '>', '!', '~'), true), /** @@ -65,22 +69,26 @@ public enum HfqlLexerOptions { * dots as separate tokens. */ FHIRPATH_EXPRESSION_PART( + List.of(">=", "<=", "!="), Set.of( 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z', 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '[', ']', '_', '(', ')', '+', '-'), - Set.of(',', '=', '|', ':', '*', '.'), + Set.of(',', '=', '|', ':', '*', '<', '>', '!', '~', '.'), true); private final Set myMultiCharTokenCharacters; private final boolean mySlurpParens; private final Set mySingleCharTokenCharacters; + private final List myMultiCharTokens; HfqlLexerOptions( + List theMultiCharTokens, Set theMultiCharTokenCharacters, Set theSingleCharTokenCharacters, boolean theSlurpParens) { + myMultiCharTokens = theMultiCharTokens; myMultiCharTokenCharacters = theMultiCharTokenCharacters; mySingleCharTokenCharacters = theSingleCharTokenCharacters; mySlurpParens = theSlurpParens; @@ -91,6 +99,14 @@ public enum HfqlLexerOptions { } } + /** + * These tokens are always treated as a single token if this string of characters + * is found in sequence + */ + public List getMultiCharTokens() { + return myMultiCharTokens; + } + /** * These characters are treated as a single character token if they are found */ diff --git a/hapi-fhir-jpaserver-hfql/src/main/java/ca/uhn/fhir/jpa/fql/parser/HfqlLexerToken.java b/hapi-fhir-jpaserver-hfql/src/main/java/ca/uhn/fhir/jpa/fql/parser/HfqlLexerToken.java index 2458f773d84..d15dd6dcc12 100644 --- a/hapi-fhir-jpaserver-hfql/src/main/java/ca/uhn/fhir/jpa/fql/parser/HfqlLexerToken.java +++ b/hapi-fhir-jpaserver-hfql/src/main/java/ca/uhn/fhir/jpa/fql/parser/HfqlLexerToken.java @@ -19,10 +19,10 @@ */ package ca.uhn.fhir.jpa.fql.parser; +import jakarta.annotation.Nonnull; import org.apache.commons.lang3.StringUtils; import java.util.Locale; -import javax.annotation.Nonnull; class HfqlLexerToken { diff --git a/hapi-fhir-jpaserver-hfql/src/main/java/ca/uhn/fhir/jpa/fql/parser/HfqlStatement.java b/hapi-fhir-jpaserver-hfql/src/main/java/ca/uhn/fhir/jpa/fql/parser/HfqlStatement.java index 333c8d2fa94..c9b0e0af0f5 100644 --- a/hapi-fhir-jpaserver-hfql/src/main/java/ca/uhn/fhir/jpa/fql/parser/HfqlStatement.java +++ b/hapi-fhir-jpaserver-hfql/src/main/java/ca/uhn/fhir/jpa/fql/parser/HfqlStatement.java @@ -24,13 +24,15 @@ import ca.uhn.fhir.model.api.IModelJson; import ca.uhn.fhir.util.ValidateUtil; import com.fasterxml.jackson.annotation.JsonInclude; import com.fasterxml.jackson.annotation.JsonProperty; +import jakarta.annotation.Nonnull; +import jakarta.annotation.Nullable; import java.util.ArrayList; import java.util.Arrays; import java.util.List; import java.util.stream.Collectors; -import javax.annotation.Nonnull; -import javax.annotation.Nullable; + +import static org.apache.commons.lang3.StringUtils.join; /** * This class represents a parsed HFQL expression tree. It is useful for @@ -327,5 +329,14 @@ public class HfqlStatement implements IModelJson { } return retVal; } + + /** + * Returns a concatenation of the {@link #getLeft() left} and all of the {@link #getRight() right} expressions, + * each joined by a single string. This is useful for obtaining expressions of + * type {@link WhereClauseOperatorEnum#UNARY_BOOLEAN}. + */ + public String asUnaryExpression() { + return getLeft() + " " + join(getRight(), ' '); + } } } diff --git a/hapi-fhir-jpaserver-hfql/src/main/java/ca/uhn/fhir/jpa/fql/parser/HfqlStatementParser.java b/hapi-fhir-jpaserver-hfql/src/main/java/ca/uhn/fhir/jpa/fql/parser/HfqlStatementParser.java index 0c6ede1489a..358acf1c4bf 100644 --- a/hapi-fhir-jpaserver-hfql/src/main/java/ca/uhn/fhir/jpa/fql/parser/HfqlStatementParser.java +++ b/hapi-fhir-jpaserver-hfql/src/main/java/ca/uhn/fhir/jpa/fql/parser/HfqlStatementParser.java @@ -23,14 +23,14 @@ import ca.uhn.fhir.context.FhirContext; import ca.uhn.fhir.i18n.Msg; import ca.uhn.fhir.parser.DataFormatException; import ca.uhn.fhir.util.UrlUtil; +import jakarta.annotation.Nonnull; +import jakarta.annotation.Nullable; import org.apache.commons.lang3.Validate; import java.util.HashSet; import java.util.List; import java.util.Locale; import java.util.Set; -import javax.annotation.Nonnull; -import javax.annotation.Nullable; import static org.apache.commons.lang3.StringUtils.isBlank; import static org.apache.commons.lang3.StringUtils.isNotBlank; @@ -331,10 +331,9 @@ public class HfqlStatementParser { HfqlLexerToken nextToken = theToken; if (!KEYWORD_AND.equals(nextToken.asKeyword()) && !DIRECTIVE_KEYWORDS.contains(nextToken.asKeyword())) { - StringBuilder expression = new StringBuilder(myWhereClause.getLeft()); - while (true) { - expression.append(' ').append(nextToken.getToken()); + myWhereClause.addRight(nextToken.getToken()); + while (true) { if (myLexer.hasNextToken(HfqlLexerOptions.FHIRPATH_EXPRESSION)) { nextToken = myLexer.getNextToken(HfqlLexerOptions.FHIRPATH_EXPRESSION); String nextTokenAsKeyword = nextToken.asKeyword(); @@ -342,13 +341,12 @@ public class HfqlStatementParser { || DIRECTIVE_KEYWORDS.contains(nextTokenAsKeyword)) { break; } + myWhereClause.addRight(nextToken.getToken()); } else { nextToken = null; break; } } - - myWhereClause.setLeft(expression.toString()); } if (nextToken != null) { diff --git a/hapi-fhir-jpaserver-hfql/src/main/java/ca/uhn/fhir/jpa/fql/provider/HfqlRestProvider.java b/hapi-fhir-jpaserver-hfql/src/main/java/ca/uhn/fhir/jpa/fql/provider/HfqlRestProvider.java index adb3d2d8335..13be8309154 100644 --- a/hapi-fhir-jpaserver-hfql/src/main/java/ca/uhn/fhir/jpa/fql/provider/HfqlRestProvider.java +++ b/hapi-fhir-jpaserver-hfql/src/main/java/ca/uhn/fhir/jpa/fql/provider/HfqlRestProvider.java @@ -30,15 +30,15 @@ import ca.uhn.fhir.util.DatatypeUtil; import ca.uhn.fhir.util.JsonUtil; import ca.uhn.fhir.util.ValidateUtil; import ca.uhn.fhir.util.VersionUtil; +import jakarta.annotation.Nullable; +import jakarta.servlet.ServletOutputStream; +import jakarta.servlet.http.HttpServletResponse; import org.apache.commons.csv.CSVPrinter; import org.hl7.fhir.instance.model.api.IPrimitiveType; import org.springframework.beans.factory.annotation.Autowired; import java.io.IOException; import java.io.OutputStreamWriter; -import javax.annotation.Nullable; -import javax.servlet.ServletOutputStream; -import javax.servlet.http.HttpServletResponse; import static ca.uhn.fhir.jpa.fql.jdbc.HfqlRestClient.CSV_FORMAT; import static ca.uhn.fhir.rest.api.Constants.CHARSET_UTF8_CTSUFFIX; diff --git a/hapi-fhir-jpaserver-hfql/src/test/java/ca/uhn/fhir/jpa/fql/executor/BaseHfqlExecutorTest.java b/hapi-fhir-jpaserver-hfql/src/test/java/ca/uhn/fhir/jpa/fql/executor/BaseHfqlExecutorTest.java new file mode 100644 index 00000000000..0a739f90e82 --- /dev/null +++ b/hapi-fhir-jpaserver-hfql/src/test/java/ca/uhn/fhir/jpa/fql/executor/BaseHfqlExecutorTest.java @@ -0,0 +1,175 @@ +package ca.uhn.fhir.jpa.fql.executor; + +import ca.uhn.fhir.context.FhirContext; +import ca.uhn.fhir.jpa.api.dao.DaoRegistry; +import ca.uhn.fhir.jpa.api.dao.IFhirResourceDao; +import ca.uhn.fhir.jpa.searchparam.SearchParameterMap; +import ca.uhn.fhir.rest.api.server.RequestDetails; +import ca.uhn.fhir.rest.api.server.SystemRequestDetails; +import ca.uhn.fhir.rest.server.IPagingProvider; +import ca.uhn.fhir.rest.server.SimpleBundleProvider; +import ca.uhn.fhir.rest.server.util.FhirContextSearchParamRegistry; +import ca.uhn.fhir.rest.server.util.ISearchParamRegistry; +import org.hl7.fhir.instance.model.api.IBaseResource; +import org.hl7.fhir.r4.model.DateType; +import org.hl7.fhir.r4.model.Observation; +import org.hl7.fhir.r4.model.Patient; +import org.hl7.fhir.r4.model.Quantity; +import org.hl7.fhir.r4.model.StringType; +import org.junit.jupiter.api.extension.ExtendWith; +import org.junit.jupiter.api.extension.RegisterExtension; +import org.mockito.ArgumentCaptor; +import org.mockito.Captor; +import org.mockito.InjectMocks; +import org.mockito.Mock; +import org.mockito.Spy; +import org.mockito.junit.jupiter.MockitoExtension; + +import javax.annotation.Nonnull; +import java.util.ArrayList; +import java.util.List; + +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; + +@ExtendWith(MockitoExtension.class) +public abstract class BaseHfqlExecutorTest { + + protected final RequestDetails mySrd = new SystemRequestDetails(); + @Spy + protected FhirContext myCtx = FhirContext.forR4Cached(); + @Mock + protected DaoRegistry myDaoRegistry; + @Mock + protected IPagingProvider myPagingProvider; + @Spy + protected ISearchParamRegistry mySearchParamRegistry = new FhirContextSearchParamRegistry(myCtx); + @InjectMocks + protected HfqlExecutor myHfqlExecutor = new HfqlExecutor(); + @Captor + protected ArgumentCaptor mySearchParameterMapCaptor; + + @SuppressWarnings("unchecked") + protected IFhirResourceDao initDao(Class theType) { + IFhirResourceDao retVal = mock(IFhirResourceDao.class); + String type = myCtx.getResourceType(theType); + when(myDaoRegistry.getResourceDao(type)).thenReturn(retVal); + return retVal; + } + + @Nonnull + protected static List> readAllRowValues(IHfqlExecutionResult result) { + List> rowValues = new ArrayList<>(); + while (result.hasNext()) { + rowValues.add(new ArrayList<>(result.getNextRow().getRowValues())); + } + return rowValues; + } + + @Nonnull + protected static Observation createCardiologyNoteObservation(String id, String noteText) { + Observation obs = new Observation(); + obs.setId(id); + obs.getCode().addCoding() + .setSystem("http://loinc.org") + .setCode("34752-6"); + obs.setValue(new StringType(noteText)); + return obs; + } + + @Nonnull + protected static Observation createWeightObservationWithKilos(String obsId, long kg) { + Observation obs = new Observation(); + obs.setId(obsId); + obs.getCode().addCoding() + .setSystem("http://loinc.org") + .setCode("29463-7"); + obs.setValue(new Quantity(null, kg, "http://unitsofmeasure.org", "kg", "kg")); + return obs; + } + + @Nonnull + protected static SimpleBundleProvider createProviderWithSparseNames() { + Patient patientNoValues = new Patient(); + patientNoValues.setActive(true); + Patient patientFamilyNameOnly = new Patient(); + patientFamilyNameOnly.addName().setFamily("Simpson"); + Patient patientGivenNameOnly = new Patient(); + patientGivenNameOnly.addName().addGiven("Homer"); + Patient patientBothNames = new Patient(); + patientBothNames.addName().setFamily("Simpson").addGiven("Homer"); + return new SimpleBundleProvider(List.of( + patientNoValues, patientFamilyNameOnly, patientGivenNameOnly, patientBothNames)); + } + + @Nonnull + protected static SimpleBundleProvider createProviderWithSomeSimpsonsAndFlanders() { + return new SimpleBundleProvider( + createPatientHomerSimpson(), + createPatientNedFlanders(), + createPatientBartSimpson(), + createPatientLisaSimpson(), + createPatientMaggieSimpson() + ); + } + + @Nonnull + protected static SimpleBundleProvider createProviderWithSomeSimpsonsAndFlandersWithSomeDuplicates() { + return new SimpleBundleProvider( + createPatientHomerSimpson(), + createPatientHomerSimpson(), + createPatientNedFlanders(), + createPatientNedFlanders(), + createPatientBartSimpson(), + createPatientLisaSimpson(), + createPatientMaggieSimpson()); + } + + @Nonnull + protected static Patient createPatientMaggieSimpson() { + Patient maggie = new Patient(); + maggie.addName().setFamily("Simpson").addGiven("Maggie").addGiven("Evelyn"); + maggie.addIdentifier().setSystem("http://system").setValue("value4"); + return maggie; + } + + @Nonnull + protected static Patient createPatientLisaSimpson() { + Patient lisa = new Patient(); + lisa.getMeta().setVersionId("1"); + lisa.addName().setFamily("Simpson").addGiven("Lisa").addGiven("Marie"); + lisa.addIdentifier().setSystem("http://system").setValue("value3"); + return lisa; + } + + @Nonnull + protected static Patient createPatientBartSimpson() { + Patient bart = new Patient(); + bart.getMeta().setVersionId("3"); + bart.addName().setFamily("Simpson").addGiven("Bart").addGiven("El Barto"); + bart.addIdentifier().setSystem("http://system").setValue("value2"); + return bart; + } + + @Nonnull + protected static Patient createPatientNedFlanders() { + Patient nedFlanders = new Patient(); + nedFlanders.getMeta().setVersionId("1"); + nedFlanders.addName().setFamily("Flanders").addGiven("Ned"); + nedFlanders.addIdentifier().setSystem("http://system").setValue("value1"); + return nedFlanders; + } + + @Nonnull + protected static Patient createPatientHomerSimpson() { + Patient homer = new Patient(); + homer.setId("HOMER0"); + homer.getMeta().setVersionId("2"); + homer.addName().setFamily("Simpson").addGiven("Homer").addGiven("Jay"); + homer.addIdentifier().setSystem("http://system").setValue("value0"); + homer.setBirthDateElement(new DateType("1950-01-01")); + return homer; + } + + +} diff --git a/hapi-fhir-jpaserver-hfql/src/test/java/ca/uhn/fhir/jpa/fql/executor/HfqlExecutorFhirPathTranslationToSearchParamTest.java b/hapi-fhir-jpaserver-hfql/src/test/java/ca/uhn/fhir/jpa/fql/executor/HfqlExecutorFhirPathTranslationToSearchParamTest.java new file mode 100644 index 00000000000..9a77d2a8264 --- /dev/null +++ b/hapi-fhir-jpaserver-hfql/src/test/java/ca/uhn/fhir/jpa/fql/executor/HfqlExecutorFhirPathTranslationToSearchParamTest.java @@ -0,0 +1,103 @@ +package ca.uhn.fhir.jpa.fql.executor; + +import ca.uhn.fhir.jpa.api.dao.IFhirResourceDao; +import ca.uhn.fhir.jpa.searchparam.SearchParameterMap; +import ca.uhn.fhir.rest.param.DateParam; +import ca.uhn.fhir.rest.param.ParamPrefixEnum; +import ca.uhn.fhir.rest.param.TokenParam; +import org.hl7.fhir.r4.model.Patient; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.CsvSource; +import org.junit.jupiter.params.provider.ValueSource; +import org.mockito.junit.jupiter.MockitoExtension; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNull; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.Mockito.times; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +/** + * We should auto-translate FHIRPath expressions like + * id or meta.lastUpdated + * to an equivalent search parameter since that's more efficient + */ +@ExtendWith(MockitoExtension.class) +public class HfqlExecutorFhirPathTranslationToSearchParamTest extends BaseHfqlExecutorTest { + + @ParameterizedTest + @CsvSource(textBlock = """ + id , true + Resource.id , true + Resource.id , true + foo.id , false + """ + ) + public void testId(String theExpression, boolean theShouldConvert) { + IFhirResourceDao patientDao = initDao(Patient.class); + when(patientDao.search(any(), any())).thenReturn(createProviderWithSomeSimpsonsAndFlanders()); + + String statement = """ + SELECT + id, birthDate, meta.lastUpdated + FROM + Patient + WHERE + id = 'ABC123' + """; + statement = statement.replace(" id =", " " + theExpression + " ="); + + myHfqlExecutor.executeInitialSearch(statement, null, mySrd); + + verify(patientDao, times(1)).search(mySearchParameterMapCaptor.capture(), any()); + SearchParameterMap map = mySearchParameterMapCaptor.getValue(); + if (theShouldConvert) { + assertEquals(1, map.get("_id").size()); + assertEquals(1, map.get("_id").get(0).size()); + assertNull(((TokenParam) map.get("_id").get(0).get(0)).getSystem()); + assertEquals("ABC123", ((TokenParam) map.get("_id").get(0).get(0)).getValue()); + } else { + assertNull(map.get("_id")); + } + } + + @ParameterizedTest + @CsvSource(textBlock = """ + meta.lastUpdated = '2023' , 2023 , + meta.lastUpdated > '2023' , 2023 , GREATERTHAN + meta.lastUpdated >= '2023' , 2023 , GREATERTHAN_OR_EQUALS + meta.lastUpdated < '2023' , 2023 , LESSTHAN + meta.lastUpdated <= '2023' , 2023 , LESSTHAN_OR_EQUALS + meta.lastUpdated != '2023' , 2023 , NOT_EQUAL + meta.lastUpdated ~ '2023' , 2023 , APPROXIMATE + """ + ) + public void testLastUpdated(String theExpression, String theExpectedParamValue, ParamPrefixEnum theExpectedParamPrefix) { + IFhirResourceDao patientDao = initDao(Patient.class); + when(patientDao.search(any(), any())).thenReturn(createProviderWithSomeSimpsonsAndFlanders()); + + String statement = """ + SELECT + id, birthDate, meta.lastUpdated + FROM + Patient + WHERE + meta.lastUpdated = '2023' + """; + statement = statement.replace("meta.lastUpdated = '2023'", theExpression); + + myHfqlExecutor.executeInitialSearch(statement, null, mySrd); + + verify(patientDao, times(1)).search(mySearchParameterMapCaptor.capture(), any()); + SearchParameterMap map = mySearchParameterMapCaptor.getValue(); + assertEquals(1, map.get("_lastUpdated").size()); + assertEquals(1, map.get("_lastUpdated").get(0).size()); + assertEquals(theExpectedParamValue, ((DateParam) map.get("_lastUpdated").get(0).get(0)).getValueAsString()); + assertEquals(theExpectedParamPrefix, ((DateParam) map.get("_lastUpdated").get(0).get(0)).getPrefix()); + } + + +} diff --git a/hapi-fhir-jpaserver-hfql/src/test/java/ca/uhn/fhir/jpa/fql/executor/HfqlExecutorTest.java b/hapi-fhir-jpaserver-hfql/src/test/java/ca/uhn/fhir/jpa/fql/executor/HfqlExecutorTest.java index 5136c116ad0..f28995fa8ca 100644 --- a/hapi-fhir-jpaserver-hfql/src/test/java/ca/uhn/fhir/jpa/fql/executor/HfqlExecutorTest.java +++ b/hapi-fhir-jpaserver-hfql/src/test/java/ca/uhn/fhir/jpa/fql/executor/HfqlExecutorTest.java @@ -36,7 +36,7 @@ import org.mockito.Mock; import org.mockito.Spy; import org.mockito.junit.jupiter.MockitoExtension; -import javax.annotation.Nonnull; +import jakarta.annotation.Nonnull; import java.sql.Types; import java.util.ArrayList; import java.util.List; @@ -59,22 +59,7 @@ import static org.mockito.Mockito.times; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; -@ExtendWith(MockitoExtension.class) -public class HfqlExecutorTest { - - private final RequestDetails mySrd = new SystemRequestDetails(); - @Spy - private FhirContext myCtx = FhirContext.forR4Cached(); - @Mock - private DaoRegistry myDaoRegistry; - @Mock - private IPagingProvider myPagingProvider; - @Spy - private ISearchParamRegistry mySearchParamRegistry = new FhirContextSearchParamRegistry(myCtx); - @InjectMocks - private HfqlExecutor myHfqlExecutor = new HfqlExecutor(); - @Captor - private ArgumentCaptor mySearchParameterMapCaptor; +public class HfqlExecutorTest extends BaseHfqlExecutorTest { @Test public void testContinuation() { @@ -1253,126 +1238,4 @@ public class HfqlExecutorTest { assertErrorMessage(result, "HAPI-2429: Resource type Patient does not have a root element named 'Blah'"); } - @SuppressWarnings("unchecked") - private IFhirResourceDao initDao(Class theType) { - IFhirResourceDao retVal = mock(IFhirResourceDao.class); - String type = myCtx.getResourceType(theType); - when(myDaoRegistry.getResourceDao(type)).thenReturn(retVal); - return retVal; - } - - @Nonnull - private static List> readAllRowValues(IHfqlExecutionResult result) { - List> rowValues = new ArrayList<>(); - while (result.hasNext()) { - rowValues.add(new ArrayList<>(result.getNextRow().getRowValues())); - } - return rowValues; - } - - @Nonnull - private static Observation createCardiologyNoteObservation(String id, String noteText) { - Observation obs = new Observation(); - obs.setId(id); - obs.getCode().addCoding() - .setSystem("http://loinc.org") - .setCode("34752-6"); - obs.setValue(new StringType(noteText)); - return obs; - } - - @Nonnull - private static Observation createWeightObservationWithKilos(String obsId, long kg) { - Observation obs = new Observation(); - obs.setId(obsId); - obs.getCode().addCoding() - .setSystem("http://loinc.org") - .setCode("29463-7"); - obs.setValue(new Quantity(null, kg, "http://unitsofmeasure.org", "kg", "kg")); - return obs; - } - - @Nonnull - private static SimpleBundleProvider createProviderWithSparseNames() { - Patient patientNoValues = new Patient(); - patientNoValues.setActive(true); - Patient patientFamilyNameOnly = new Patient(); - patientFamilyNameOnly.addName().setFamily("Simpson"); - Patient patientGivenNameOnly = new Patient(); - patientGivenNameOnly.addName().addGiven("Homer"); - Patient patientBothNames = new Patient(); - patientBothNames.addName().setFamily("Simpson").addGiven("Homer"); - return new SimpleBundleProvider(List.of( - patientNoValues, patientFamilyNameOnly, patientGivenNameOnly, patientBothNames)); - } - - @Nonnull - private static SimpleBundleProvider createProviderWithSomeSimpsonsAndFlanders() { - return new SimpleBundleProvider( - createPatientHomerSimpson(), - createPatientNedFlanders(), - createPatientBartSimpson(), - createPatientLisaSimpson(), - createPatientMaggieSimpson() - ); - } - - @Nonnull - private static SimpleBundleProvider createProviderWithSomeSimpsonsAndFlandersWithSomeDuplicates() { - return new SimpleBundleProvider( - createPatientHomerSimpson(), - createPatientHomerSimpson(), - createPatientNedFlanders(), - createPatientNedFlanders(), - createPatientBartSimpson(), - createPatientLisaSimpson(), - createPatientMaggieSimpson()); - } - - @Nonnull - private static Patient createPatientMaggieSimpson() { - Patient maggie = new Patient(); - maggie.addName().setFamily("Simpson").addGiven("Maggie").addGiven("Evelyn"); - maggie.addIdentifier().setSystem("http://system").setValue("value4"); - return maggie; - } - - @Nonnull - private static Patient createPatientLisaSimpson() { - Patient lisa = new Patient(); - lisa.getMeta().setVersionId("1"); - lisa.addName().setFamily("Simpson").addGiven("Lisa").addGiven("Marie"); - lisa.addIdentifier().setSystem("http://system").setValue("value3"); - return lisa; - } - - @Nonnull - private static Patient createPatientBartSimpson() { - Patient bart = new Patient(); - bart.getMeta().setVersionId("3"); - bart.addName().setFamily("Simpson").addGiven("Bart").addGiven("El Barto"); - bart.addIdentifier().setSystem("http://system").setValue("value2"); - return bart; - } - - @Nonnull - private static Patient createPatientNedFlanders() { - Patient nedFlanders = new Patient(); - nedFlanders.getMeta().setVersionId("1"); - nedFlanders.addName().setFamily("Flanders").addGiven("Ned"); - nedFlanders.addIdentifier().setSystem("http://system").setValue("value1"); - return nedFlanders; - } - - @Nonnull - private static Patient createPatientHomerSimpson() { - Patient homer = new Patient(); - homer.setId("HOMER0"); - homer.getMeta().setVersionId("2"); - homer.addName().setFamily("Simpson").addGiven("Homer").addGiven("Jay"); - homer.addIdentifier().setSystem("http://system").setValue("value0"); - homer.setBirthDateElement(new DateType("1950-01-01")); - return homer; - } - } diff --git a/hapi-fhir-jpaserver-hfql/src/test/java/ca/uhn/fhir/jpa/fql/jdbc/HfqlRestClientTest.java b/hapi-fhir-jpaserver-hfql/src/test/java/ca/uhn/fhir/jpa/fql/jdbc/HfqlRestClientTest.java index 044717fee96..decc334dd05 100644 --- a/hapi-fhir-jpaserver-hfql/src/test/java/ca/uhn/fhir/jpa/fql/jdbc/HfqlRestClientTest.java +++ b/hapi-fhir-jpaserver-hfql/src/test/java/ca/uhn/fhir/jpa/fql/jdbc/HfqlRestClientTest.java @@ -31,7 +31,7 @@ import org.mockito.Mock; import org.mockito.junit.jupiter.MockitoExtension; import org.springframework.util.Base64Utils; -import javax.annotation.Nonnull; +import jakarta.annotation.Nonnull; import java.nio.charset.StandardCharsets; import java.sql.SQLException; import java.util.ArrayList; diff --git a/hapi-fhir-jpaserver-hfql/src/test/java/ca/uhn/fhir/jpa/fql/parser/HfqlLexerTest.java b/hapi-fhir-jpaserver-hfql/src/test/java/ca/uhn/fhir/jpa/fql/parser/HfqlLexerTest.java index c094bc545ed..3fbadf31a97 100644 --- a/hapi-fhir-jpaserver-hfql/src/test/java/ca/uhn/fhir/jpa/fql/parser/HfqlLexerTest.java +++ b/hapi-fhir-jpaserver-hfql/src/test/java/ca/uhn/fhir/jpa/fql/parser/HfqlLexerTest.java @@ -5,6 +5,7 @@ import org.junit.jupiter.api.Test; import org.junit.jupiter.params.ParameterizedTest; import org.junit.jupiter.params.provider.CsvSource; +import java.util.ArrayList; import java.util.List; import static org.hamcrest.MatcherAssert.assertThat; @@ -144,6 +145,76 @@ public class HfqlLexerTest { assertEquals("( Observation.value.ofType ( Quantity ) ).unit", lexer.getNextToken(HfqlLexerOptions.FHIRPATH_EXPRESSION).getToken()); } + @ParameterizedTest + @CsvSource(textBlock = """ + >= , false , HFQL_TOKEN + <= , false , HFQL_TOKEN + != , false , HFQL_TOKEN + = , false , HFQL_TOKEN + >= , true , HFQL_TOKEN + <= , true , HFQL_TOKEN + != , true , HFQL_TOKEN + ~ , true , HFQL_TOKEN + = , true , HFQL_TOKEN + >= , false , FHIRPATH_EXPRESSION + <= , false , FHIRPATH_EXPRESSION + != , false , FHIRPATH_EXPRESSION + = , false , FHIRPATH_EXPRESSION + >= , true , FHIRPATH_EXPRESSION + <= , true , FHIRPATH_EXPRESSION + != , true , FHIRPATH_EXPRESSION + ~ , true , FHIRPATH_EXPRESSION + = , true , FHIRPATH_EXPRESSION + >= , false , FHIRPATH_EXPRESSION_PART + <= , false , FHIRPATH_EXPRESSION_PART + != , false , FHIRPATH_EXPRESSION_PART + = , false , FHIRPATH_EXPRESSION_PART + >= , true , FHIRPATH_EXPRESSION_PART + <= , true , FHIRPATH_EXPRESSION_PART + != , true , FHIRPATH_EXPRESSION_PART + ~ , true , FHIRPATH_EXPRESSION_PART + = , true , FHIRPATH_EXPRESSION_PART + """ + ) + void testComparators(String theComparator, boolean thePad, HfqlLexerOptions theOptions) { + String input = """ + SELECT + id + FROM + Patient + WHERE + meta.lastUpdated >= '2023-10-09' + """; + + String comparator = theComparator.trim(); + if (thePad) { + input = input.replace(" >= ", " " + comparator + " "); + } else { + input = input.replace(" >= ", comparator); + } + + List allTokens = new HfqlLexer(input).allTokens(theOptions); + + List expectedItems = new ArrayList<>(); + expectedItems.add("SELECT"); + expectedItems.add("id"); + expectedItems.add("FROM"); + expectedItems.add("Patient"); + expectedItems.add("WHERE"); + if (theOptions == HfqlLexerOptions.FHIRPATH_EXPRESSION_PART) { + expectedItems.add("meta"); + expectedItems.add("."); + expectedItems.add("lastUpdated"); + } else { + expectedItems.add("meta.lastUpdated"); + } + expectedItems.add(comparator); + expectedItems.add("'2023-10-09'"); + + assertThat(allTokens.toString(), allTokens, contains(expectedItems.toArray(new String[0]))); + } + + @ParameterizedTest @CsvSource({ "token1 token2 'token3, HFQL_TOKEN", diff --git a/hapi-fhir-jpaserver-hfql/src/test/java/ca/uhn/fhir/jpa/fql/parser/HfqlStatementParserTest.java b/hapi-fhir-jpaserver-hfql/src/test/java/ca/uhn/fhir/jpa/fql/parser/HfqlStatementParserTest.java index abea2b22c67..b6c802a7c44 100644 --- a/hapi-fhir-jpaserver-hfql/src/test/java/ca/uhn/fhir/jpa/fql/parser/HfqlStatementParserTest.java +++ b/hapi-fhir-jpaserver-hfql/src/test/java/ca/uhn/fhir/jpa/fql/parser/HfqlStatementParserTest.java @@ -246,9 +246,9 @@ public class HfqlStatementParserTest { HfqlStatement statement = parse(input); assertEquals(1, statement.getWhereClauses().size()); - assertEquals("value.ofType(Quantity).value > 100", statement.getWhereClauses().get(0).getLeft()); + assertEquals("value.ofType(Quantity).value", statement.getWhereClauses().get(0).getLeft()); + assertThat(statement.getWhereClauses().get(0).getRightAsStrings(), contains(">", "100")); assertEquals(HfqlStatement.WhereClauseOperatorEnum.UNARY_BOOLEAN, statement.getWhereClauses().get(0).getOperator()); - assertEquals(0, statement.getWhereClauses().get(0).getRight().size()); } @Test diff --git a/hapi-fhir-jpaserver-ips/pom.xml b/hapi-fhir-jpaserver-ips/pom.xml index 88fef02fb1c..c284d9ed955 100644 --- a/hapi-fhir-jpaserver-ips/pom.xml +++ b/hapi-fhir-jpaserver-ips/pom.xml @@ -3,7 +3,7 @@ ca.uhn.hapi.fhir hapi-deployable-pom - 6.9.7-SNAPSHOT + 6.11.7-SNAPSHOT ../hapi-deployable-pom/pom.xml @@ -21,8 +21,8 @@ - javax.servlet - javax.servlet-api + jakarta.servlet + jakarta.servlet-api provided @@ -40,7 +40,7 @@ test - net.sourceforge.htmlunit + org.htmlunit htmlunit test diff --git a/hapi-fhir-jpaserver-ips/src/main/java/ca/uhn/fhir/jpa/ips/api/IIpsGenerationStrategy.java b/hapi-fhir-jpaserver-ips/src/main/java/ca/uhn/fhir/jpa/ips/api/IIpsGenerationStrategy.java index 4a61fba6231..3f9300095a7 100644 --- a/hapi-fhir-jpaserver-ips/src/main/java/ca/uhn/fhir/jpa/ips/api/IIpsGenerationStrategy.java +++ b/hapi-fhir-jpaserver-ips/src/main/java/ca/uhn/fhir/jpa/ips/api/IIpsGenerationStrategy.java @@ -21,13 +21,13 @@ package ca.uhn.fhir.jpa.ips.api; import ca.uhn.fhir.jpa.searchparam.SearchParameterMap; import ca.uhn.fhir.model.api.Include; +import jakarta.annotation.Nonnull; +import jakarta.annotation.Nullable; import org.hl7.fhir.instance.model.api.IBaseResource; import org.hl7.fhir.instance.model.api.IIdType; import java.util.List; import java.util.Set; -import javax.annotation.Nonnull; -import javax.annotation.Nullable; /** * This interface is the primary configuration and strategy provider for the diff --git a/hapi-fhir-jpaserver-ips/src/main/java/ca/uhn/fhir/jpa/ips/api/SectionRegistry.java b/hapi-fhir-jpaserver-ips/src/main/java/ca/uhn/fhir/jpa/ips/api/SectionRegistry.java index a085d1b6ea3..09603c53bd4 100644 --- a/hapi-fhir-jpaserver-ips/src/main/java/ca/uhn/fhir/jpa/ips/api/SectionRegistry.java +++ b/hapi-fhir-jpaserver-ips/src/main/java/ca/uhn/fhir/jpa/ips/api/SectionRegistry.java @@ -19,6 +19,8 @@ */ package ca.uhn.fhir.jpa.ips.api; +import jakarta.annotation.Nullable; +import jakarta.annotation.PostConstruct; import org.apache.commons.lang3.Validate; import org.hl7.fhir.instance.model.api.IBaseResource; import org.hl7.fhir.instance.model.api.IIdType; @@ -35,8 +37,6 @@ import java.util.Arrays; import java.util.Collections; import java.util.List; import java.util.function.Consumer; -import javax.annotation.Nullable; -import javax.annotation.PostConstruct; /** * This class is the registry for sections for the IPS document. It can be extended diff --git a/hapi-fhir-jpaserver-ips/src/main/java/ca/uhn/fhir/jpa/ips/generator/IpsGeneratorSvcImpl.java b/hapi-fhir-jpaserver-ips/src/main/java/ca/uhn/fhir/jpa/ips/generator/IpsGeneratorSvcImpl.java index 69a69f3d0a0..d90a8b72cd6 100644 --- a/hapi-fhir-jpaserver-ips/src/main/java/ca/uhn/fhir/jpa/ips/generator/IpsGeneratorSvcImpl.java +++ b/hapi-fhir-jpaserver-ips/src/main/java/ca/uhn/fhir/jpa/ips/generator/IpsGeneratorSvcImpl.java @@ -45,6 +45,8 @@ import ca.uhn.fhir.util.ResourceReferenceInfo; import ca.uhn.fhir.util.ValidateUtil; import com.google.common.collect.BiMap; import com.google.common.collect.HashBiMap; +import jakarta.annotation.Nonnull; +import jakarta.annotation.Nullable; import org.hl7.fhir.instance.model.api.IBase; import org.hl7.fhir.instance.model.api.IBaseBundle; import org.hl7.fhir.instance.model.api.IBaseExtension; @@ -68,8 +70,6 @@ import java.util.Map; import java.util.Set; import java.util.UUID; import java.util.stream.Collectors; -import javax.annotation.Nonnull; -import javax.annotation.Nullable; import static ca.uhn.fhir.jpa.term.api.ITermLoaderSvc.LOINC_URI; import static org.apache.commons.lang3.StringUtils.isNotBlank; diff --git a/hapi-fhir-jpaserver-ips/src/main/java/ca/uhn/fhir/jpa/ips/strategy/DefaultIpsGenerationStrategy.java b/hapi-fhir-jpaserver-ips/src/main/java/ca/uhn/fhir/jpa/ips/strategy/DefaultIpsGenerationStrategy.java index c96add02935..dc56842cd8b 100644 --- a/hapi-fhir-jpaserver-ips/src/main/java/ca/uhn/fhir/jpa/ips/strategy/DefaultIpsGenerationStrategy.java +++ b/hapi-fhir-jpaserver-ips/src/main/java/ca/uhn/fhir/jpa/ips/strategy/DefaultIpsGenerationStrategy.java @@ -30,6 +30,8 @@ import ca.uhn.fhir.rest.param.TokenOrListParam; import ca.uhn.fhir.rest.param.TokenParam; import com.google.common.collect.Lists; import com.google.common.collect.Sets; +import jakarta.annotation.Nonnull; +import jakarta.annotation.Nullable; import org.hl7.fhir.instance.model.api.IBaseResource; import org.hl7.fhir.instance.model.api.IIdType; import org.hl7.fhir.r4.model.*; @@ -39,8 +41,6 @@ import java.time.format.DateTimeFormatter; import java.util.Collections; import java.util.List; import java.util.Set; -import javax.annotation.Nonnull; -import javax.annotation.Nullable; import static ca.uhn.fhir.jpa.term.api.ITermLoaderSvc.LOINC_URI; diff --git a/hapi-fhir-jpaserver-ips/src/test/java/ca/uhn/fhir/jpa/ips/generator/IpsGenerationR4Test.java b/hapi-fhir-jpaserver-ips/src/test/java/ca/uhn/fhir/jpa/ips/generator/IpsGenerationR4Test.java index ab94afa4404..fc9711c1331 100644 --- a/hapi-fhir-jpaserver-ips/src/test/java/ca/uhn/fhir/jpa/ips/generator/IpsGenerationR4Test.java +++ b/hapi-fhir-jpaserver-ips/src/test/java/ca/uhn/fhir/jpa/ips/generator/IpsGenerationR4Test.java @@ -38,8 +38,8 @@ import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.test.context.ContextConfiguration; -import javax.annotation.Nonnull; -import javax.annotation.Nullable; +import jakarta.annotation.Nonnull; +import jakarta.annotation.Nullable; import java.util.List; import java.util.stream.Collectors; diff --git a/hapi-fhir-jpaserver-ips/src/test/java/ca/uhn/fhir/jpa/ips/generator/IpsGeneratorSvcImplTest.java b/hapi-fhir-jpaserver-ips/src/test/java/ca/uhn/fhir/jpa/ips/generator/IpsGeneratorSvcImplTest.java index d89c4af3294..fbdaf9fa0d4 100644 --- a/hapi-fhir-jpaserver-ips/src/test/java/ca/uhn/fhir/jpa/ips/generator/IpsGeneratorSvcImplTest.java +++ b/hapi-fhir-jpaserver-ips/src/test/java/ca/uhn/fhir/jpa/ips/generator/IpsGeneratorSvcImplTest.java @@ -15,11 +15,11 @@ import ca.uhn.fhir.rest.param.TokenParam; import ca.uhn.fhir.rest.server.SimpleBundleProvider; import ca.uhn.fhir.test.utilities.HtmlUtil; import ca.uhn.fhir.util.ClasspathUtil; -import com.gargoylesoftware.htmlunit.html.DomElement; -import com.gargoylesoftware.htmlunit.html.DomNodeList; -import com.gargoylesoftware.htmlunit.html.HtmlPage; -import com.gargoylesoftware.htmlunit.html.HtmlTable; -import com.gargoylesoftware.htmlunit.html.HtmlTableRow; +import org.htmlunit.html.DomElement; +import org.htmlunit.html.DomNodeList; +import org.htmlunit.html.HtmlPage; +import org.htmlunit.html.HtmlTable; +import org.htmlunit.html.HtmlTableRow; import com.google.common.collect.Lists; import org.hl7.fhir.instance.model.api.IBaseResource; import org.hl7.fhir.r4.model.AllergyIntolerance; @@ -49,6 +49,11 @@ import org.hl7.fhir.r4.model.Procedure; import org.hl7.fhir.r4.model.Reference; import org.hl7.fhir.r4.model.Resource; import org.hl7.fhir.r4.model.StringType; +import org.htmlunit.html.DomElement; +import org.htmlunit.html.DomNodeList; +import org.htmlunit.html.HtmlPage; +import org.htmlunit.html.HtmlTable; +import org.htmlunit.html.HtmlTableRow; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.ExtendWith; @@ -56,7 +61,7 @@ import org.mockito.junit.jupiter.MockitoExtension; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import javax.annotation.Nonnull; +import jakarta.annotation.Nonnull; import java.io.IOException; import java.util.Collections; import java.util.Date; diff --git a/hapi-fhir-jpaserver-mdm/pom.xml b/hapi-fhir-jpaserver-mdm/pom.xml index 521442039ec..94bd39c91a2 100644 --- a/hapi-fhir-jpaserver-mdm/pom.xml +++ b/hapi-fhir-jpaserver-mdm/pom.xml @@ -6,7 +6,7 @@ ca.uhn.hapi.fhir hapi-deployable-pom - 6.9.7-SNAPSHOT + 6.11.7-SNAPSHOT ../hapi-deployable-pom/pom.xml @@ -61,8 +61,8 @@ test - javax.servlet - javax.servlet-api + jakarta.servlet + jakarta.servlet-api test diff --git a/hapi-fhir-jpaserver-mdm/src/main/java/ca/uhn/fhir/jpa/mdm/broker/MdmMessageHandler.java b/hapi-fhir-jpaserver-mdm/src/main/java/ca/uhn/fhir/jpa/mdm/broker/MdmMessageHandler.java index fd829d2eb3c..9be8ec8f76a 100644 --- a/hapi-fhir-jpaserver-mdm/src/main/java/ca/uhn/fhir/jpa/mdm/broker/MdmMessageHandler.java +++ b/hapi-fhir-jpaserver-mdm/src/main/java/ca/uhn/fhir/jpa/mdm/broker/MdmMessageHandler.java @@ -20,6 +20,7 @@ package ca.uhn.fhir.jpa.mdm.broker; import ca.uhn.fhir.context.FhirContext; +import ca.uhn.fhir.context.FhirVersionEnum; import ca.uhn.fhir.i18n.Msg; import ca.uhn.fhir.interceptor.api.HookParams; import ca.uhn.fhir.interceptor.api.IInterceptorBroadcaster; @@ -30,6 +31,7 @@ import ca.uhn.fhir.jpa.mdm.svc.MdmResourceFilteringSvc; import ca.uhn.fhir.jpa.mdm.svc.candidate.TooManyCandidatesException; import ca.uhn.fhir.jpa.subscription.model.ResourceModifiedJsonMessage; import ca.uhn.fhir.jpa.subscription.model.ResourceModifiedMessage; +import ca.uhn.fhir.jpa.topic.SubscriptionTopicUtil; import ca.uhn.fhir.mdm.api.IMdmSettings; import ca.uhn.fhir.mdm.log.Logs; import ca.uhn.fhir.mdm.model.MdmTransactionContext; @@ -39,6 +41,7 @@ import ca.uhn.fhir.rest.server.TransactionLogMessages; import ca.uhn.fhir.rest.server.exceptions.InvalidRequestException; import ca.uhn.fhir.rest.server.messaging.ResourceOperationMessage; import org.hl7.fhir.instance.model.api.IAnyResource; +import org.hl7.fhir.instance.model.api.IBaseBundle; import org.hl7.fhir.instance.model.api.IBaseResource; import org.slf4j.Logger; import org.springframework.beans.factory.annotation.Autowired; @@ -81,21 +84,27 @@ public class MdmMessageHandler implements MessageHandler { ResourceModifiedMessage msg = ((ResourceModifiedJsonMessage) theMessage).getPayload(); try { - IBaseResource sourceResource = msg.getNewPayload(myFhirContext); + IBaseResource sourceResource = extractSourceResource(msg); boolean toProcess = myMdmResourceFilteringSvc.shouldBeProcessed((IAnyResource) sourceResource); if (toProcess) { matchMdmAndUpdateLinks(sourceResource, msg); } - } catch (TooManyCandidatesException e) { - ourLog.error(e.getMessage(), e); - // skip this one with an error message and continue processing } catch (Exception e) { ourLog.error("Failed to handle MDM Matching Resource:", e); throw e; } } + private IBaseResource extractSourceResource(ResourceModifiedMessage theResourceModifiedMessage) { + IBaseResource sourceResource = theResourceModifiedMessage.getNewPayload(myFhirContext); + if (myFhirContext.getVersion().getVersion() == FhirVersionEnum.R5 && sourceResource instanceof IBaseBundle) { + return SubscriptionTopicUtil.extractResourceFromBundle(myFhirContext, (IBaseBundle) sourceResource); + } else { + return sourceResource; + } + } + private void matchMdmAndUpdateLinks(IBaseResource theSourceResource, ResourceModifiedMessage theMsg) { String resourceType = theSourceResource.getIdElement().getResourceType(); @@ -123,6 +132,12 @@ public class MdmMessageHandler implements MessageHandler { ourLog.trace("Not processing modified message for {}", theMsg.getOperationType()); } } catch (Exception e) { + if (e instanceof TooManyCandidatesException) { + ourLog.debug( + "Failed to handle MDM Matching for resource: {} since candidate matches exceeded the " + + "candidate search limit", + theSourceResource.getIdElement()); + } log(mdmContext, "Failure during MDM processing: " + e.getMessage(), e); mdmContext.addTransactionLogMessage(e.getMessage()); } finally { diff --git a/hapi-fhir-jpaserver-mdm/src/main/java/ca/uhn/fhir/jpa/mdm/broker/MdmMessageKeySvc.java b/hapi-fhir-jpaserver-mdm/src/main/java/ca/uhn/fhir/jpa/mdm/broker/MdmMessageKeySvc.java index 5f3f1c381f3..6cb3f810232 100644 --- a/hapi-fhir-jpaserver-mdm/src/main/java/ca/uhn/fhir/jpa/mdm/broker/MdmMessageKeySvc.java +++ b/hapi-fhir-jpaserver-mdm/src/main/java/ca/uhn/fhir/jpa/mdm/broker/MdmMessageKeySvc.java @@ -22,12 +22,12 @@ package ca.uhn.fhir.jpa.mdm.broker; import ca.uhn.fhir.jpa.subscription.api.ISubscriptionMessageKeySvc; import ca.uhn.fhir.mdm.model.CanonicalEID; import ca.uhn.fhir.mdm.util.EIDHelper; +import jakarta.annotation.Nullable; import org.hl7.fhir.instance.model.api.IBaseResource; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; import java.util.List; -import javax.annotation.Nullable; @Service public class MdmMessageKeySvc implements ISubscriptionMessageKeySvc { diff --git a/hapi-fhir-jpaserver-mdm/src/main/java/ca/uhn/fhir/jpa/mdm/broker/MdmQueueConsumerLoader.java b/hapi-fhir-jpaserver-mdm/src/main/java/ca/uhn/fhir/jpa/mdm/broker/MdmQueueConsumerLoader.java index 1acf874d65d..23ca40079fd 100644 --- a/hapi-fhir-jpaserver-mdm/src/main/java/ca/uhn/fhir/jpa/mdm/broker/MdmQueueConsumerLoader.java +++ b/hapi-fhir-jpaserver-mdm/src/main/java/ca/uhn/fhir/jpa/mdm/broker/MdmQueueConsumerLoader.java @@ -26,11 +26,10 @@ import ca.uhn.fhir.jpa.subscription.model.ResourceModifiedJsonMessage; import ca.uhn.fhir.mdm.api.IMdmSettings; import ca.uhn.fhir.mdm.log.Logs; import com.google.common.annotations.VisibleForTesting; +import jakarta.annotation.PreDestroy; import org.slf4j.Logger; import org.springframework.stereotype.Service; -import javax.annotation.PreDestroy; - @Service public class MdmQueueConsumerLoader { private static final Logger ourLog = Logs.getMdmTroubleshootingLog(); diff --git a/hapi-fhir-jpaserver-mdm/src/main/java/ca/uhn/fhir/jpa/mdm/config/MdmConsumerConfig.java b/hapi-fhir-jpaserver-mdm/src/main/java/ca/uhn/fhir/jpa/mdm/config/MdmConsumerConfig.java index 1d82d9e49a1..2c80e680249 100644 --- a/hapi-fhir-jpaserver-mdm/src/main/java/ca/uhn/fhir/jpa/mdm/config/MdmConsumerConfig.java +++ b/hapi-fhir-jpaserver-mdm/src/main/java/ca/uhn/fhir/jpa/mdm/config/MdmConsumerConfig.java @@ -39,7 +39,7 @@ import ca.uhn.fhir.jpa.mdm.svc.MdmLinkUpdaterSvcImpl; import ca.uhn.fhir.jpa.mdm.svc.MdmMatchFinderSvcImpl; import ca.uhn.fhir.jpa.mdm.svc.MdmMatchLinkSvc; import ca.uhn.fhir.jpa.mdm.svc.MdmModelConverterSvcImpl; -import ca.uhn.fhir.jpa.mdm.svc.MdmResourceDaoSvc; +import ca.uhn.fhir.jpa.mdm.svc.MdmResourceDaoSvcImpl; import ca.uhn.fhir.jpa.mdm.svc.MdmResourceFilteringSvc; import ca.uhn.fhir.jpa.mdm.svc.candidate.CandidateSearcher; import ca.uhn.fhir.jpa.mdm.svc.candidate.FindCandidateByEidSvc; @@ -57,6 +57,7 @@ import ca.uhn.fhir.mdm.api.IMdmLinkQuerySvc; import ca.uhn.fhir.mdm.api.IMdmLinkSvc; import ca.uhn.fhir.mdm.api.IMdmLinkUpdaterSvc; import ca.uhn.fhir.mdm.api.IMdmMatchFinderSvc; +import ca.uhn.fhir.mdm.api.IMdmResourceDaoSvc; import ca.uhn.fhir.mdm.api.IMdmSettings; import ca.uhn.fhir.mdm.batch2.MdmBatch2Config; import ca.uhn.fhir.mdm.blocklist.svc.IBlockListRuleProvider; @@ -123,8 +124,8 @@ public class MdmConsumerConfig { } @Bean - MdmResourceDaoSvc mdmResourceDaoSvc() { - return new MdmResourceDaoSvc(); + IMdmResourceDaoSvc mdmResourceDaoSvc() { + return new MdmResourceDaoSvcImpl(); } @Bean diff --git a/hapi-fhir-jpaserver-mdm/src/main/java/ca/uhn/fhir/jpa/mdm/config/MdmSubscriptionLoader.java b/hapi-fhir-jpaserver-mdm/src/main/java/ca/uhn/fhir/jpa/mdm/config/MdmSubscriptionLoader.java index 4cf52bb8f8a..5e665205acf 100644 --- a/hapi-fhir-jpaserver-mdm/src/main/java/ca/uhn/fhir/jpa/mdm/config/MdmSubscriptionLoader.java +++ b/hapi-fhir-jpaserver-mdm/src/main/java/ca/uhn/fhir/jpa/mdm/config/MdmSubscriptionLoader.java @@ -27,9 +27,12 @@ import ca.uhn.fhir.jpa.api.dao.IFhirResourceDao; import ca.uhn.fhir.jpa.subscription.channel.api.ChannelProducerSettings; import ca.uhn.fhir.jpa.subscription.channel.subscription.IChannelNamer; import ca.uhn.fhir.jpa.subscription.match.registry.SubscriptionLoader; +import ca.uhn.fhir.jpa.subscription.model.CanonicalSubscriptionChannelType; +import ca.uhn.fhir.jpa.topic.SubscriptionTopicLoader; import ca.uhn.fhir.mdm.api.IMdmSettings; import ca.uhn.fhir.mdm.api.MdmConstants; import ca.uhn.fhir.mdm.log.Logs; +import ca.uhn.fhir.rest.api.Constants; import ca.uhn.fhir.rest.api.server.SystemRequestDetails; import ca.uhn.fhir.rest.server.exceptions.ResourceGoneException; import ca.uhn.fhir.rest.server.exceptions.ResourceNotFoundException; @@ -37,17 +40,21 @@ import ca.uhn.fhir.util.HapiExtensions; import org.hl7.fhir.instance.model.api.IBaseResource; import org.hl7.fhir.r4.model.BooleanType; import org.hl7.fhir.r4.model.Subscription; +import org.hl7.fhir.r5.model.Coding; +import org.hl7.fhir.r5.model.Enumerations; +import org.hl7.fhir.r5.model.SubscriptionTopic; import org.slf4j.Logger; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; +import java.util.Collections; import java.util.List; import java.util.stream.Collectors; @Service public class MdmSubscriptionLoader { - public static final String MDM_SUBSCIPRION_ID_PREFIX = "mdm-"; + public static final String MDM_SUBSCRIPTION_ID_PREFIX = "mdm-"; private static final Logger ourLog = Logs.getMdmTroubleshootingLog(); @Autowired @@ -65,7 +72,11 @@ public class MdmSubscriptionLoader { @Autowired private IMdmSettings myMdmSettings; + @Autowired(required = false) + private SubscriptionTopicLoader mySubscriptionTopicLoader; + private IFhirResourceDao mySubscriptionDao; + private IFhirResourceDao mySubscriptionTopicDao; public synchronized void daoUpdateMdmSubscriptions() { List subscriptions; @@ -73,16 +84,24 @@ public class MdmSubscriptionLoader { switch (myFhirContext.getVersion().getVersion()) { case DSTU3: subscriptions = mdmResourceTypes.stream() - .map(resourceType -> - buildMdmSubscriptionDstu3(MDM_SUBSCIPRION_ID_PREFIX + resourceType, resourceType + "?")) + .map(resourceType -> buildMdmSubscriptionDstu3( + MDM_SUBSCRIPTION_ID_PREFIX + resourceType, resourceType + "?")) .collect(Collectors.toList()); break; case R4: subscriptions = mdmResourceTypes.stream() .map(resourceType -> - buildMdmSubscriptionR4(MDM_SUBSCIPRION_ID_PREFIX + resourceType, resourceType + "?")) + buildMdmSubscriptionR4(MDM_SUBSCRIPTION_ID_PREFIX + resourceType, resourceType + "?")) .collect(Collectors.toList()); break; + case R5: + SubscriptionTopic subscriptionTopic = buildMdmSubscriptionTopicR5(mdmResourceTypes); + updateSubscriptionTopic(subscriptionTopic); + // After loading subscriptionTopic, sync subscriptionTopic to the registry. + mySubscriptionTopicLoader.syncDatabaseToCache(); + + subscriptions = buildMdmSubscriptionR5(subscriptionTopic); + break; default: throw new ConfigurationException(Msg.code(736) + "MDM not supported for FHIR version " + myFhirContext.getVersion().getVersion()); @@ -107,6 +126,11 @@ public class MdmSubscriptionLoader { } } + synchronized void updateSubscriptionTopic(SubscriptionTopic theSubscriptionTopic) { + mySubscriptionTopicDao = myDaoRegistry.getResourceDao("SubscriptionTopic"); + mySubscriptionTopicDao.update(theSubscriptionTopic, SystemRequestDetails.forAllPartitions()); + } + private org.hl7.fhir.dstu3.model.Subscription buildMdmSubscriptionDstu3(String theId, String theCriteria) { org.hl7.fhir.dstu3.model.Subscription retval = new org.hl7.fhir.dstu3.model.Subscription(); retval.setId(theId); @@ -124,7 +148,7 @@ public class MdmSubscriptionLoader { channel.setType(org.hl7.fhir.dstu3.model.Subscription.SubscriptionChannelType.MESSAGE); channel.setEndpoint("channel:" + myChannelNamer.getChannelName(IMdmSettings.EMPI_CHANNEL_NAME, new ChannelProducerSettings())); - channel.setPayload("application/json"); + channel.setPayload(Constants.CT_JSON); return retval; } @@ -145,7 +169,56 @@ public class MdmSubscriptionLoader { channel.setType(Subscription.SubscriptionChannelType.MESSAGE); channel.setEndpoint("channel:" + myChannelNamer.getChannelName(IMdmSettings.EMPI_CHANNEL_NAME, new ChannelProducerSettings())); - channel.setPayload("application/json"); + channel.setPayload(Constants.CT_JSON); return retval; } + + private SubscriptionTopic buildMdmSubscriptionTopicR5(List theMdmResourceTypes) { + SubscriptionTopic subscriptionTopic = new SubscriptionTopic(); + subscriptionTopic.setId(MDM_SUBSCRIPTION_ID_PREFIX + "subscription-topic"); + subscriptionTopic + .getMeta() + .addTag() + .setSystem(MdmConstants.SYSTEM_MDM_MANAGED) + .setCode(MdmConstants.CODE_HAPI_MDM_MANAGED); + subscriptionTopic.setStatus(Enumerations.PublicationStatus.ACTIVE); + subscriptionTopic.setUrl(MdmConstants.SUBSCRIPTION_TOPIC_URL); + theMdmResourceTypes.forEach( + resourceType -> buildSubscriptionTopicResourceTriggerComponent(resourceType, subscriptionTopic)); + return subscriptionTopic; + } + + private static void buildSubscriptionTopicResourceTriggerComponent( + String theResourceType, SubscriptionTopic theSubscriptionTopic) { + SubscriptionTopic.SubscriptionTopicResourceTriggerComponent triggerComponent = + theSubscriptionTopic.addResourceTrigger(); + triggerComponent.setResource(theResourceType); + triggerComponent.addSupportedInteraction(SubscriptionTopic.InteractionTrigger.CREATE); + triggerComponent.addSupportedInteraction(SubscriptionTopic.InteractionTrigger.UPDATE); + } + + private List buildMdmSubscriptionR5(SubscriptionTopic theSubscriptionTopic) { + org.hl7.fhir.r5.model.Subscription subscription = new org.hl7.fhir.r5.model.Subscription(); + + subscription.setId(MDM_SUBSCRIPTION_ID_PREFIX + "subscription"); + subscription.setReason("MDM"); + subscription.setStatus(Enumerations.SubscriptionStatusCodes.REQUESTED); + + subscription.setTopic(theSubscriptionTopic.getUrl()); + subscription + .getMeta() + .addTag() + .setSystem(MdmConstants.SYSTEM_MDM_MANAGED) + .setCode(MdmConstants.CODE_HAPI_MDM_MANAGED); + + subscription.setChannelType(new Coding() + .setCode(CanonicalSubscriptionChannelType.MESSAGE.toCode()) + .setSystem(CanonicalSubscriptionChannelType.MESSAGE.getSystem())); + + subscription.setEndpoint("channel:" + + myChannelNamer.getChannelName(IMdmSettings.EMPI_CHANNEL_NAME, new ChannelProducerSettings())); + subscription.setContentType(Constants.CT_JSON); + + return Collections.singletonList(subscription); + } } diff --git a/hapi-fhir-jpaserver-mdm/src/main/java/ca/uhn/fhir/jpa/mdm/dao/MdmLinkDaoSvc.java b/hapi-fhir-jpaserver-mdm/src/main/java/ca/uhn/fhir/jpa/mdm/dao/MdmLinkDaoSvc.java index 57ca03f6774..1832b09a130 100644 --- a/hapi-fhir-jpaserver-mdm/src/main/java/ca/uhn/fhir/jpa/mdm/dao/MdmLinkDaoSvc.java +++ b/hapi-fhir-jpaserver-mdm/src/main/java/ca/uhn/fhir/jpa/mdm/dao/MdmLinkDaoSvc.java @@ -36,6 +36,8 @@ import ca.uhn.fhir.mdm.log.Logs; import ca.uhn.fhir.mdm.model.MdmTransactionContext; import ca.uhn.fhir.rest.api.Constants; import ca.uhn.fhir.rest.api.server.storage.IResourcePersistentId; +import jakarta.annotation.Nonnull; +import jakarta.annotation.Nullable; import org.hl7.fhir.instance.model.api.IAnyResource; import org.hl7.fhir.instance.model.api.IBaseResource; import org.slf4j.Logger; @@ -50,8 +52,6 @@ import java.util.Collections; import java.util.Date; import java.util.List; import java.util.Optional; -import javax.annotation.Nonnull; -import javax.annotation.Nullable; public class MdmLinkDaoSvc

    > { diff --git a/hapi-fhir-jpaserver-mdm/src/main/java/ca/uhn/fhir/jpa/mdm/svc/BlockRuleEvaluationSvcImpl.java b/hapi-fhir-jpaserver-mdm/src/main/java/ca/uhn/fhir/jpa/mdm/svc/BlockRuleEvaluationSvcImpl.java index ec118655786..decb338c77b 100644 --- a/hapi-fhir-jpaserver-mdm/src/main/java/ca/uhn/fhir/jpa/mdm/svc/BlockRuleEvaluationSvcImpl.java +++ b/hapi-fhir-jpaserver-mdm/src/main/java/ca/uhn/fhir/jpa/mdm/svc/BlockRuleEvaluationSvcImpl.java @@ -28,13 +28,13 @@ import ca.uhn.fhir.mdm.blocklist.json.BlockedFieldJson; import ca.uhn.fhir.mdm.blocklist.svc.IBlockListRuleProvider; import ca.uhn.fhir.mdm.blocklist.svc.IBlockRuleEvaluationSvc; import ca.uhn.fhir.util.FhirTypeUtil; +import jakarta.annotation.Nullable; import org.hl7.fhir.instance.model.api.IAnyResource; import org.hl7.fhir.instance.model.api.IBase; import org.hl7.fhir.instance.model.api.IPrimitiveType; import org.slf4j.Logger; import java.util.List; -import javax.annotation.Nullable; import static org.slf4j.LoggerFactory.getLogger; diff --git a/hapi-fhir-jpaserver-mdm/src/main/java/ca/uhn/fhir/jpa/mdm/svc/GoldenResourceMergerSvcImpl.java b/hapi-fhir-jpaserver-mdm/src/main/java/ca/uhn/fhir/jpa/mdm/svc/GoldenResourceMergerSvcImpl.java index b1e15c91a40..fb60e425f57 100644 --- a/hapi-fhir-jpaserver-mdm/src/main/java/ca/uhn/fhir/jpa/mdm/svc/GoldenResourceMergerSvcImpl.java +++ b/hapi-fhir-jpaserver-mdm/src/main/java/ca/uhn/fhir/jpa/mdm/svc/GoldenResourceMergerSvcImpl.java @@ -29,6 +29,7 @@ import ca.uhn.fhir.jpa.mdm.dao.MdmLinkDaoSvc; import ca.uhn.fhir.mdm.api.IGoldenResourceMergerSvc; import ca.uhn.fhir.mdm.api.IMdmLink; import ca.uhn.fhir.mdm.api.IMdmLinkSvc; +import ca.uhn.fhir.mdm.api.IMdmResourceDaoSvc; import ca.uhn.fhir.mdm.api.IMdmSurvivorshipService; import ca.uhn.fhir.mdm.api.MdmLinkSourceEnum; import ca.uhn.fhir.mdm.api.MdmMatchOutcome; @@ -74,7 +75,7 @@ public class GoldenResourceMergerSvcImpl implements IGoldenResourceMergerSvc { IIdHelperService myIdHelperService; @Autowired - MdmResourceDaoSvc myMdmResourceDaoSvc; + IMdmResourceDaoSvc myMdmResourceDaoSvc; @Autowired MdmPartitionHelper myMdmPartitionHelper; diff --git a/hapi-fhir-jpaserver-mdm/src/main/java/ca/uhn/fhir/jpa/mdm/svc/GoldenResourceSearchSvcImpl.java b/hapi-fhir-jpaserver-mdm/src/main/java/ca/uhn/fhir/jpa/mdm/svc/GoldenResourceSearchSvcImpl.java index c7e967075d6..d96cd3be0c1 100644 --- a/hapi-fhir-jpaserver-mdm/src/main/java/ca/uhn/fhir/jpa/mdm/svc/GoldenResourceSearchSvcImpl.java +++ b/hapi-fhir-jpaserver-mdm/src/main/java/ca/uhn/fhir/jpa/mdm/svc/GoldenResourceSearchSvcImpl.java @@ -24,9 +24,12 @@ import ca.uhn.fhir.context.RuntimeResourceDefinition; import ca.uhn.fhir.interceptor.model.RequestPartitionId; import ca.uhn.fhir.jpa.api.dao.DaoRegistry; import ca.uhn.fhir.jpa.api.dao.IFhirResourceDao; -import ca.uhn.fhir.jpa.api.pid.HomogeneousResourcePidList; -import ca.uhn.fhir.jpa.api.pid.IResourcePidList; +import ca.uhn.fhir.jpa.api.pid.IResourcePidStream; +import ca.uhn.fhir.jpa.api.pid.StreamTemplate; +import ca.uhn.fhir.jpa.api.pid.TypedResourcePid; +import ca.uhn.fhir.jpa.api.pid.TypedResourceStream; import ca.uhn.fhir.jpa.api.svc.IGoldenResourceSearchSvc; +import ca.uhn.fhir.jpa.dao.tx.IHapiTransactionService; import ca.uhn.fhir.jpa.searchparam.MatchUrlService; import ca.uhn.fhir.jpa.searchparam.SearchParameterMap; import ca.uhn.fhir.mdm.api.MdmConstants; @@ -34,17 +37,17 @@ import ca.uhn.fhir.rest.api.Constants; import ca.uhn.fhir.rest.api.SortOrderEnum; import ca.uhn.fhir.rest.api.SortSpec; import ca.uhn.fhir.rest.api.server.SystemRequestDetails; -import ca.uhn.fhir.rest.api.server.storage.IResourcePersistentId; import ca.uhn.fhir.rest.param.DateRangeParam; import ca.uhn.fhir.rest.param.TokenOrListParam; import ca.uhn.fhir.util.DateRangeUtil; +import jakarta.annotation.Nonnull; +import jakarta.annotation.Nullable; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.transaction.annotation.Transactional; import java.util.Date; -import java.util.List; -import javax.annotation.Nonnull; -import javax.annotation.Nullable; +import java.util.function.Supplier; +import java.util.stream.Stream; public class GoldenResourceSearchSvcImpl implements IGoldenResourceSearchSvc { @Autowired @@ -56,24 +59,31 @@ public class GoldenResourceSearchSvcImpl implements IGoldenResourceSearchSvc { @Autowired private FhirContext myFhirContext; + @Autowired + private IHapiTransactionService myTransactionService; + @Override @Transactional - public IResourcePidList fetchGoldenResourceIdsPage( + public IResourcePidStream fetchGoldenResourceIdStream( Date theStart, Date theEnd, - @Nonnull Integer thePageSize, @Nullable RequestPartitionId theRequestPartitionId, @Nonnull String theResourceType) { - return fetchResourceIdsPageWithResourceType( - theStart, theEnd, thePageSize, theResourceType, theRequestPartitionId); + + IHapiTransactionService.IExecutionBuilder txSettings = + myTransactionService.withSystemRequest().withRequestPartitionId(theRequestPartitionId); + + Supplier> streamSupplier = + () -> fetchResourceIdsPageWithResourceType(theStart, theEnd, theResourceType, theRequestPartitionId); + + StreamTemplate streamTemplate = + StreamTemplate.fromSupplier(streamSupplier).withTransactionAdvice(txSettings); + + return new TypedResourceStream(theRequestPartitionId, streamTemplate); } - private IResourcePidList fetchResourceIdsPageWithResourceType( - Date theStart, - Date theEnd, - int thePageSize, - String theResourceType, - RequestPartitionId theRequestPartitionId) { + private Stream fetchResourceIdsPageWithResourceType( + Date theStart, Date theEnd, String theResourceType, RequestPartitionId theRequestPartitionId) { RuntimeResourceDefinition def = myFhirContext.getResourceDefinition(theResourceType); @@ -89,15 +99,9 @@ public class GoldenResourceSearchSvcImpl implements IGoldenResourceSearchSvc { searchParamMap.add(Constants.PARAM_TAG, goldenRecordStatusToken); IFhirResourceDao dao = myDaoRegistry.getResourceDao(theResourceType); - SystemRequestDetails request = new SystemRequestDetails(); - request.setRequestPartitionId(theRequestPartitionId); - List ids = dao.searchForIds(searchParamMap, request); + SystemRequestDetails request = new SystemRequestDetails().setRequestPartitionId(theRequestPartitionId); - Date lastDate = null; - if (ids.size() > 0) { - lastDate = dao.readByPid(ids.get(ids.size() - 1)).getMeta().getLastUpdated(); - } - - return new HomogeneousResourcePidList(theResourceType, ids, lastDate, theRequestPartitionId); + return dao.searchForIdStream(searchParamMap, request, null) + .map(pid -> new TypedResourcePid(theResourceType, pid)); } } diff --git a/hapi-fhir-jpaserver-mdm/src/main/java/ca/uhn/fhir/jpa/mdm/svc/MdmControllerSvcImpl.java b/hapi-fhir-jpaserver-mdm/src/main/java/ca/uhn/fhir/jpa/mdm/svc/MdmControllerSvcImpl.java index 6ea10e08c9f..1071b9ba26b 100644 --- a/hapi-fhir-jpaserver-mdm/src/main/java/ca/uhn/fhir/jpa/mdm/svc/MdmControllerSvcImpl.java +++ b/hapi-fhir-jpaserver-mdm/src/main/java/ca/uhn/fhir/jpa/mdm/svc/MdmControllerSvcImpl.java @@ -28,6 +28,7 @@ import ca.uhn.fhir.interceptor.api.Pointcut; import ca.uhn.fhir.interceptor.model.ReadPartitionIdRequestDetails; import ca.uhn.fhir.interceptor.model.RequestPartitionId; import ca.uhn.fhir.jpa.batch.models.Batch2JobStartResponse; +import ca.uhn.fhir.jpa.dao.tx.HapiTransactionService; import ca.uhn.fhir.jpa.partition.IRequestPartitionHelperSvc; import ca.uhn.fhir.mdm.api.IGoldenResourceMergerSvc; import ca.uhn.fhir.mdm.api.IMdmControllerSvc; @@ -56,6 +57,8 @@ import ca.uhn.fhir.rest.api.server.RequestDetails; import ca.uhn.fhir.rest.server.provider.ProviderConstants; import ca.uhn.fhir.rest.server.servlet.ServletRequestDetails; import ca.uhn.fhir.util.ParametersUtil; +import jakarta.annotation.Nonnull; +import jakarta.annotation.Nullable; import org.hl7.fhir.instance.model.api.IAnyResource; import org.hl7.fhir.instance.model.api.IBaseParameters; import org.hl7.fhir.instance.model.api.IPrimitiveType; @@ -67,8 +70,6 @@ import java.math.BigDecimal; import java.util.HashSet; import java.util.List; import java.util.Set; -import javax.annotation.Nonnull; -import javax.annotation.Nullable; /** * This class acts as a layer between MdmProviders and MDM services to support a REST API that's not a FHIR Operation API. @@ -103,6 +104,9 @@ public class MdmControllerSvcImpl implements IMdmControllerSvc { @Autowired IInterceptorBroadcaster myInterceptorBroadcaster; + @Autowired + private HapiTransactionService myTxService; + public MdmControllerSvcImpl() {} @Override @@ -194,7 +198,9 @@ public class MdmControllerSvcImpl implements IMdmControllerSvc { @Override public List queryLinkHistory( MdmHistorySearchParameters theMdmHistorySearchParameters, RequestDetails theRequestDetails) { - return myMdmLinkQuerySvc.queryLinkHistory(theMdmHistorySearchParameters); + return myTxService + .withRequest(theRequestDetails) + .execute(() -> myMdmLinkQuerySvc.queryLinkHistory(theMdmHistorySearchParameters)); } @Override diff --git a/hapi-fhir-jpaserver-mdm/src/main/java/ca/uhn/fhir/jpa/mdm/svc/MdmEidUpdateService.java b/hapi-fhir-jpaserver-mdm/src/main/java/ca/uhn/fhir/jpa/mdm/svc/MdmEidUpdateService.java index 95e445012d3..c6e1359d540 100644 --- a/hapi-fhir-jpaserver-mdm/src/main/java/ca/uhn/fhir/jpa/mdm/svc/MdmEidUpdateService.java +++ b/hapi-fhir-jpaserver-mdm/src/main/java/ca/uhn/fhir/jpa/mdm/svc/MdmEidUpdateService.java @@ -24,6 +24,7 @@ import ca.uhn.fhir.jpa.mdm.svc.candidate.MatchedGoldenResourceCandidate; import ca.uhn.fhir.jpa.mdm.svc.candidate.MdmGoldenResourceFindingSvc; import ca.uhn.fhir.mdm.api.IMdmLink; import ca.uhn.fhir.mdm.api.IMdmLinkSvc; +import ca.uhn.fhir.mdm.api.IMdmResourceDaoSvc; import ca.uhn.fhir.mdm.api.IMdmSettings; import ca.uhn.fhir.mdm.api.IMdmSurvivorshipService; import ca.uhn.fhir.mdm.api.MdmLinkSourceEnum; @@ -34,6 +35,7 @@ import ca.uhn.fhir.mdm.model.MdmTransactionContext; import ca.uhn.fhir.mdm.util.EIDHelper; import ca.uhn.fhir.mdm.util.GoldenResourceHelper; import ca.uhn.fhir.rest.api.server.storage.IResourcePersistentId; +import jakarta.annotation.Nullable; import org.hl7.fhir.instance.model.api.IAnyResource; import org.slf4j.Logger; import org.springframework.beans.factory.annotation.Autowired; @@ -41,7 +43,6 @@ import org.springframework.stereotype.Service; import java.util.List; import java.util.Optional; -import javax.annotation.Nullable; @Service public class MdmEidUpdateService { @@ -49,7 +50,7 @@ public class MdmEidUpdateService { private static final Logger ourLog = Logs.getMdmTroubleshootingLog(); @Autowired - private MdmResourceDaoSvc myMdmResourceDaoSvc; + private IMdmResourceDaoSvc myMdmResourceDaoSvc; @Autowired private IMdmLinkSvc myMdmLinkSvc; diff --git a/hapi-fhir-jpaserver-mdm/src/main/java/ca/uhn/fhir/jpa/mdm/svc/MdmLinkSvcImpl.java b/hapi-fhir-jpaserver-mdm/src/main/java/ca/uhn/fhir/jpa/mdm/svc/MdmLinkSvcImpl.java index 3870b5d3393..b74d3ed3e8b 100644 --- a/hapi-fhir-jpaserver-mdm/src/main/java/ca/uhn/fhir/jpa/mdm/svc/MdmLinkSvcImpl.java +++ b/hapi-fhir-jpaserver-mdm/src/main/java/ca/uhn/fhir/jpa/mdm/svc/MdmLinkSvcImpl.java @@ -25,6 +25,7 @@ import ca.uhn.fhir.jpa.api.svc.IIdHelperService; import ca.uhn.fhir.jpa.mdm.dao.MdmLinkDaoSvc; import ca.uhn.fhir.mdm.api.IMdmLink; import ca.uhn.fhir.mdm.api.IMdmLinkSvc; +import ca.uhn.fhir.mdm.api.IMdmResourceDaoSvc; import ca.uhn.fhir.mdm.api.MdmLinkSourceEnum; import ca.uhn.fhir.mdm.api.MdmMatchOutcome; import ca.uhn.fhir.mdm.api.MdmMatchResultEnum; @@ -32,6 +33,7 @@ import ca.uhn.fhir.mdm.log.Logs; import ca.uhn.fhir.mdm.model.MdmTransactionContext; import ca.uhn.fhir.rest.api.server.storage.IResourcePersistentId; import ca.uhn.fhir.rest.server.exceptions.InternalErrorException; +import jakarta.annotation.Nonnull; import org.hl7.fhir.instance.model.api.IAnyResource; import org.slf4j.Logger; import org.springframework.beans.factory.annotation.Autowired; @@ -40,7 +42,6 @@ import org.springframework.transaction.annotation.Transactional; import java.util.List; import java.util.Optional; -import javax.annotation.Nonnull; /** * This class is in charge of managing MdmLinks between Golden Resources and source resources @@ -51,7 +52,7 @@ public class MdmLinkSvcImpl implements IMdmLinkSvc { private static final Logger ourLog = Logs.getMdmTroubleshootingLog(); @Autowired - private MdmResourceDaoSvc myMdmResourceDaoSvc; + private IMdmResourceDaoSvc myMdmResourceDaoSvc; @Autowired private MdmLinkDaoSvc myMdmLinkDaoSvc; diff --git a/hapi-fhir-jpaserver-mdm/src/main/java/ca/uhn/fhir/jpa/mdm/svc/MdmLinkUpdaterSvcImpl.java b/hapi-fhir-jpaserver-mdm/src/main/java/ca/uhn/fhir/jpa/mdm/svc/MdmLinkUpdaterSvcImpl.java index 96892d37965..c37ff21be1d 100644 --- a/hapi-fhir-jpaserver-mdm/src/main/java/ca/uhn/fhir/jpa/mdm/svc/MdmLinkUpdaterSvcImpl.java +++ b/hapi-fhir-jpaserver-mdm/src/main/java/ca/uhn/fhir/jpa/mdm/svc/MdmLinkUpdaterSvcImpl.java @@ -30,6 +30,7 @@ import ca.uhn.fhir.jpa.mdm.dao.MdmLinkDaoSvc; import ca.uhn.fhir.jpa.model.entity.PartitionablePartitionId; import ca.uhn.fhir.mdm.api.IMdmLink; import ca.uhn.fhir.mdm.api.IMdmLinkUpdaterSvc; +import ca.uhn.fhir.mdm.api.IMdmResourceDaoSvc; import ca.uhn.fhir.mdm.api.IMdmSettings; import ca.uhn.fhir.mdm.api.IMdmSurvivorshipService; import ca.uhn.fhir.mdm.api.MdmLinkSourceEnum; @@ -72,7 +73,7 @@ public class MdmLinkUpdaterSvcImpl implements IMdmLinkUpdaterSvc { MdmLinkDaoSvc myMdmLinkDaoSvc; @Autowired - MdmResourceDaoSvc myMdmResourceDaoSvc; + IMdmResourceDaoSvc myMdmResourceDaoSvc; @Autowired MdmMatchLinkSvc myMdmMatchLinkSvc; diff --git a/hapi-fhir-jpaserver-mdm/src/main/java/ca/uhn/fhir/jpa/mdm/svc/MdmMatchFinderSvcImpl.java b/hapi-fhir-jpaserver-mdm/src/main/java/ca/uhn/fhir/jpa/mdm/svc/MdmMatchFinderSvcImpl.java index 0d3ee482998..426b096cf11 100644 --- a/hapi-fhir-jpaserver-mdm/src/main/java/ca/uhn/fhir/jpa/mdm/svc/MdmMatchFinderSvcImpl.java +++ b/hapi-fhir-jpaserver-mdm/src/main/java/ca/uhn/fhir/jpa/mdm/svc/MdmMatchFinderSvcImpl.java @@ -25,6 +25,7 @@ import ca.uhn.fhir.mdm.api.IMdmMatchFinderSvc; import ca.uhn.fhir.mdm.api.MatchedTarget; import ca.uhn.fhir.mdm.log.Logs; import ca.uhn.fhir.mdm.rules.svc.MdmResourceMatcherSvc; +import jakarta.annotation.Nonnull; import org.hl7.fhir.instance.model.api.IAnyResource; import org.slf4j.Logger; import org.springframework.beans.factory.annotation.Autowired; @@ -34,7 +35,6 @@ import org.springframework.transaction.annotation.Transactional; import java.util.Collection; import java.util.List; import java.util.stream.Collectors; -import javax.annotation.Nonnull; import static ca.uhn.fhir.jpa.mdm.svc.candidate.CandidateSearcher.idOrType; diff --git a/hapi-fhir-jpaserver-mdm/src/main/java/ca/uhn/fhir/jpa/mdm/svc/MdmMatchLinkSvc.java b/hapi-fhir-jpaserver-mdm/src/main/java/ca/uhn/fhir/jpa/mdm/svc/MdmMatchLinkSvc.java index b7fba3ab877..f3512ea535a 100644 --- a/hapi-fhir-jpaserver-mdm/src/main/java/ca/uhn/fhir/jpa/mdm/svc/MdmMatchLinkSvc.java +++ b/hapi-fhir-jpaserver-mdm/src/main/java/ca/uhn/fhir/jpa/mdm/svc/MdmMatchLinkSvc.java @@ -19,6 +19,7 @@ */ package ca.uhn.fhir.jpa.mdm.svc; +import ca.uhn.fhir.jpa.api.dao.DaoRegistry; import ca.uhn.fhir.jpa.mdm.models.FindGoldenResourceCandidatesParams; import ca.uhn.fhir.jpa.mdm.svc.candidate.CandidateList; import ca.uhn.fhir.jpa.mdm.svc.candidate.CandidateStrategyEnum; @@ -70,6 +71,9 @@ public class MdmMatchLinkSvc { @Autowired private IBlockRuleEvaluationSvc myBlockRuleEvaluationSvc; + @Autowired + private DaoRegistry myDaoRegistry; + @Autowired private IMdmSurvivorshipService myMdmSurvivorshipService; @@ -106,6 +110,8 @@ public class MdmMatchLinkSvc { * (so that future resources may match to it). */ boolean isResourceBlocked = myBlockRuleEvaluationSvc.isMdmMatchingBlocked(theResource); + // we will mark the golden resource special for this + theMdmTransactionContext.setIsBlocked(isResourceBlocked); if (!isResourceBlocked) { FindGoldenResourceCandidatesParams params = diff --git a/hapi-fhir-jpaserver-mdm/src/main/java/ca/uhn/fhir/jpa/mdm/svc/MdmResourceDaoSvc.java b/hapi-fhir-jpaserver-mdm/src/main/java/ca/uhn/fhir/jpa/mdm/svc/MdmResourceDaoSvcImpl.java similarity index 83% rename from hapi-fhir-jpaserver-mdm/src/main/java/ca/uhn/fhir/jpa/mdm/svc/MdmResourceDaoSvc.java rename to hapi-fhir-jpaserver-mdm/src/main/java/ca/uhn/fhir/jpa/mdm/svc/MdmResourceDaoSvcImpl.java index 90d581ca649..2cf9489ee13 100644 --- a/hapi-fhir-jpaserver-mdm/src/main/java/ca/uhn/fhir/jpa/mdm/svc/MdmResourceDaoSvc.java +++ b/hapi-fhir-jpaserver-mdm/src/main/java/ca/uhn/fhir/jpa/mdm/svc/MdmResourceDaoSvcImpl.java @@ -26,14 +26,15 @@ import ca.uhn.fhir.jpa.api.dao.IFhirResourceDao; import ca.uhn.fhir.jpa.api.model.DaoMethodOutcome; import ca.uhn.fhir.jpa.model.entity.TagTypeEnum; import ca.uhn.fhir.jpa.searchparam.SearchParameterMap; +import ca.uhn.fhir.mdm.api.IMdmResourceDaoSvc; import ca.uhn.fhir.mdm.api.IMdmSettings; import ca.uhn.fhir.mdm.api.MdmConstants; +import ca.uhn.fhir.mdm.util.MdmSearchParamBuildingUtils; import ca.uhn.fhir.rest.api.Constants; import ca.uhn.fhir.rest.api.server.IBundleProvider; import ca.uhn.fhir.rest.api.server.RequestDetails; import ca.uhn.fhir.rest.api.server.SystemRequestDetails; import ca.uhn.fhir.rest.api.server.storage.IResourcePersistentId; -import ca.uhn.fhir.rest.param.TokenParam; import ca.uhn.fhir.rest.server.exceptions.InternalErrorException; import org.hl7.fhir.instance.model.api.IAnyResource; import org.hl7.fhir.instance.model.api.IBaseResource; @@ -42,10 +43,9 @@ import org.springframework.stereotype.Service; import java.util.List; import java.util.Optional; -import javax.annotation.Nonnull; @Service -public class MdmResourceDaoSvc { +public class MdmResourceDaoSvcImpl implements IMdmResourceDaoSvc { private static final int MAX_MATCHING_GOLDEN_RESOURCES = 1000; @@ -55,6 +55,7 @@ public class MdmResourceDaoSvc { @Autowired IMdmSettings myMdmSettings; + @Override public DaoMethodOutcome upsertGoldenResource(IAnyResource theGoldenResource, String theResourceType) { IFhirResourceDao resourceDao = myDaoRegistry.getResourceDao(theResourceType); RequestDetails requestDetails = new SystemRequestDetails().setRequestPartitionId((RequestPartitionId) @@ -66,12 +67,7 @@ public class MdmResourceDaoSvc { } } - /** - * Given a resource, remove its Golden Resource tag. - * - * @param theGoldenResource the {@link IAnyResource} to remove the tag from. - * @param theResourcetype the type of that resource - */ + @Override public void removeGoldenResourceTag(IAnyResource theGoldenResource, String theResourcetype) { IFhirResourceDao resourceDao = myDaoRegistry.getResourceDao(theResourcetype); RequestDetails requestDetails = new SystemRequestDetails().setRequestPartitionId((RequestPartitionId) @@ -84,18 +80,22 @@ public class MdmResourceDaoSvc { requestDetails); } + @Override public IAnyResource readGoldenResourceByPid(IResourcePersistentId theGoldenResourcePid, String theResourceType) { IFhirResourceDao resourceDao = myDaoRegistry.getResourceDao(theResourceType); return (IAnyResource) resourceDao.readByPid(theGoldenResourcePid); } + @Override public Optional searchGoldenResourceByEID(String theEid, String theResourceType) { return this.searchGoldenResourceByEID(theEid, theResourceType, null); } + @Override public Optional searchGoldenResourceByEID( String theEid, String theResourceType, RequestPartitionId thePartitionId) { - SearchParameterMap map = buildEidSearchParameterMap(theEid, theResourceType); + SearchParameterMap map = MdmSearchParamBuildingUtils.buildEidSearchParameterMap( + theEid, theResourceType, myMdmSettings.getMdmRules()); IFhirResourceDao resourceDao = myDaoRegistry.getResourceDao(theResourceType); SystemRequestDetails systemRequestDetails = new SystemRequestDetails(); @@ -118,16 +118,4 @@ public class MdmResourceDaoSvc { return Optional.of((IAnyResource) resources.get(0)); } } - - @Nonnull - private SearchParameterMap buildEidSearchParameterMap(String theEid, String theResourceType) { - SearchParameterMap map = new SearchParameterMap(); - map.setLoadSynchronous(true); - map.add( - "identifier", - new TokenParam( - myMdmSettings.getMdmRules().getEnterpriseEIDSystemForResourceType(theResourceType), theEid)); - map.add("_tag", new TokenParam(MdmConstants.SYSTEM_GOLDEN_RECORD_STATUS, MdmConstants.CODE_GOLDEN_RECORD)); - return map; - } } diff --git a/hapi-fhir-jpaserver-mdm/src/main/java/ca/uhn/fhir/jpa/mdm/svc/candidate/FindCandidateByEidSvc.java b/hapi-fhir-jpaserver-mdm/src/main/java/ca/uhn/fhir/jpa/mdm/svc/candidate/FindCandidateByEidSvc.java index 2ab9e19ace0..e887cb47b39 100644 --- a/hapi-fhir-jpaserver-mdm/src/main/java/ca/uhn/fhir/jpa/mdm/svc/candidate/FindCandidateByEidSvc.java +++ b/hapi-fhir-jpaserver-mdm/src/main/java/ca/uhn/fhir/jpa/mdm/svc/candidate/FindCandidateByEidSvc.java @@ -21,8 +21,8 @@ package ca.uhn.fhir.jpa.mdm.svc.candidate; import ca.uhn.fhir.interceptor.model.RequestPartitionId; import ca.uhn.fhir.jpa.mdm.dao.MdmLinkDaoSvc; -import ca.uhn.fhir.jpa.mdm.svc.MdmResourceDaoSvc; import ca.uhn.fhir.mdm.api.IMdmLink; +import ca.uhn.fhir.mdm.api.IMdmResourceDaoSvc; import ca.uhn.fhir.mdm.api.MdmMatchOutcome; import ca.uhn.fhir.mdm.log.Logs; import ca.uhn.fhir.mdm.model.CanonicalEID; @@ -47,7 +47,7 @@ public class FindCandidateByEidSvc extends BaseCandidateFinder { private EIDHelper myEIDHelper; @Autowired - private MdmResourceDaoSvc myMdmResourceDaoSvc; + private IMdmResourceDaoSvc myMdmResourceDaoSvc; @Autowired private MdmLinkDaoSvc myMdmLinkDaoSvc; diff --git a/hapi-fhir-jpaserver-mdm/src/main/java/ca/uhn/fhir/jpa/mdm/svc/candidate/MdmCandidateSearchCriteriaBuilderSvc.java b/hapi-fhir-jpaserver-mdm/src/main/java/ca/uhn/fhir/jpa/mdm/svc/candidate/MdmCandidateSearchCriteriaBuilderSvc.java index 7eddf52c5c3..fb5d0b0828c 100644 --- a/hapi-fhir-jpaserver-mdm/src/main/java/ca/uhn/fhir/jpa/mdm/svc/candidate/MdmCandidateSearchCriteriaBuilderSvc.java +++ b/hapi-fhir-jpaserver-mdm/src/main/java/ca/uhn/fhir/jpa/mdm/svc/candidate/MdmCandidateSearchCriteriaBuilderSvc.java @@ -22,6 +22,8 @@ package ca.uhn.fhir.jpa.mdm.svc.candidate; import ca.uhn.fhir.mdm.rules.json.MdmResourceSearchParamJson; import ca.uhn.fhir.mdm.svc.MdmSearchParamSvc; import ca.uhn.fhir.util.UrlUtil; +import jakarta.annotation.Nonnull; +import jakarta.annotation.Nullable; import org.hl7.fhir.instance.model.api.IAnyResource; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; @@ -30,8 +32,6 @@ import java.util.ArrayList; import java.util.List; import java.util.Optional; import java.util.stream.Collectors; -import javax.annotation.Nonnull; -import javax.annotation.Nullable; @Service public class MdmCandidateSearchCriteriaBuilderSvc { diff --git a/hapi-fhir-jpaserver-mdm/src/main/java/ca/uhn/fhir/jpa/mdm/svc/candidate/MdmCandidateSearchSvc.java b/hapi-fhir-jpaserver-mdm/src/main/java/ca/uhn/fhir/jpa/mdm/svc/candidate/MdmCandidateSearchSvc.java index aafdff442c6..e3a7a5e72d5 100644 --- a/hapi-fhir-jpaserver-mdm/src/main/java/ca/uhn/fhir/jpa/mdm/svc/candidate/MdmCandidateSearchSvc.java +++ b/hapi-fhir-jpaserver-mdm/src/main/java/ca/uhn/fhir/jpa/mdm/svc/candidate/MdmCandidateSearchSvc.java @@ -159,7 +159,8 @@ public class MdmCandidateSearchSvc { myCandidateSearcher.search(theResourceType, resourceCriteria, theRequestPartitionId); if (!bundleProvider.isPresent()) { throw new TooManyCandidatesException(Msg.code(762) + "More than " + myMdmSettings.getCandidateSearchLimit() - + " candidate matches found for " + resourceCriteria + ". Aborting mdm matching."); + + " candidate matches found for " + resourceCriteria + ". Aborting mdm matching. Updating the " + + "candidate search parameters is strongly recommended for better performance of MDM."); } List resources = bundleProvider.get().getAllResources(); diff --git a/hapi-fhir-jpaserver-mdm/src/main/java/ca/uhn/fhir/jpa/mdm/svc/candidate/MdmGoldenResourceFindingSvc.java b/hapi-fhir-jpaserver-mdm/src/main/java/ca/uhn/fhir/jpa/mdm/svc/candidate/MdmGoldenResourceFindingSvc.java index 073ca1ced2b..c6f235eda67 100644 --- a/hapi-fhir-jpaserver-mdm/src/main/java/ca/uhn/fhir/jpa/mdm/svc/candidate/MdmGoldenResourceFindingSvc.java +++ b/hapi-fhir-jpaserver-mdm/src/main/java/ca/uhn/fhir/jpa/mdm/svc/candidate/MdmGoldenResourceFindingSvc.java @@ -20,7 +20,7 @@ package ca.uhn.fhir.jpa.mdm.svc.candidate; import ca.uhn.fhir.jpa.mdm.models.FindGoldenResourceCandidatesParams; -import ca.uhn.fhir.jpa.mdm.svc.MdmResourceDaoSvc; +import ca.uhn.fhir.mdm.api.IMdmResourceDaoSvc; import ca.uhn.fhir.mdm.log.Logs; import ca.uhn.fhir.mdm.model.MdmTransactionContext; import ca.uhn.fhir.rest.api.server.storage.IResourcePersistentId; @@ -36,7 +36,7 @@ public class MdmGoldenResourceFindingSvc { private static final Logger ourLog = Logs.getMdmTroubleshootingLog(); @Autowired - private MdmResourceDaoSvc myMdmResourceDaoSvc; + private IMdmResourceDaoSvc myMdmResourceDaoSvc; @Autowired private FindCandidateByEidSvc myFindCandidateByEidSvc; diff --git a/hapi-fhir-jpaserver-mdm/src/test/java/ca/uhn/fhir/jpa/mdm/BaseMdmR4Test.java b/hapi-fhir-jpaserver-mdm/src/test/java/ca/uhn/fhir/jpa/mdm/BaseMdmR4Test.java index e1474eada1a..325313eaca1 100644 --- a/hapi-fhir-jpaserver-mdm/src/test/java/ca/uhn/fhir/jpa/mdm/BaseMdmR4Test.java +++ b/hapi-fhir-jpaserver-mdm/src/test/java/ca/uhn/fhir/jpa/mdm/BaseMdmR4Test.java @@ -58,7 +58,7 @@ import org.springframework.beans.factory.annotation.Autowired; import org.springframework.test.context.ContextConfiguration; import org.springframework.test.context.junit.jupiter.SpringExtension; -import javax.annotation.Nonnull; +import jakarta.annotation.Nonnull; import java.io.IOException; import java.util.Collections; import java.util.Date; @@ -98,8 +98,6 @@ abstract public class BaseMdmR4Test extends BaseJpaR4Test { .setValue("555-555-5555"); private static final String NAME_GIVEN_FRANK = "Frank"; - - @Autowired protected IFhirResourceDao myPatientDao; @Autowired diff --git a/hapi-fhir-jpaserver-mdm/src/test/java/ca/uhn/fhir/jpa/mdm/config/MdmSubscriptionLoaderR5Test.java b/hapi-fhir-jpaserver-mdm/src/test/java/ca/uhn/fhir/jpa/mdm/config/MdmSubscriptionLoaderR5Test.java new file mode 100644 index 00000000000..88f106c261a --- /dev/null +++ b/hapi-fhir-jpaserver-mdm/src/test/java/ca/uhn/fhir/jpa/mdm/config/MdmSubscriptionLoaderR5Test.java @@ -0,0 +1,95 @@ +package ca.uhn.fhir.jpa.mdm.config; + +import ca.uhn.fhir.context.FhirContext; +import ca.uhn.fhir.jpa.api.dao.DaoRegistry; +import ca.uhn.fhir.jpa.api.dao.IFhirResourceDao; +import ca.uhn.fhir.jpa.subscription.channel.subscription.IChannelNamer; +import ca.uhn.fhir.jpa.subscription.match.registry.SubscriptionLoader; +import ca.uhn.fhir.jpa.topic.SubscriptionTopicLoader; +import ca.uhn.fhir.mdm.api.IMdmSettings; +import ca.uhn.fhir.mdm.rules.json.MdmRulesJson; +import ca.uhn.fhir.rest.api.server.RequestDetails; +import ca.uhn.fhir.rest.server.exceptions.ResourceGoneException; +import org.hl7.fhir.instance.model.api.IBaseResource; +import org.hl7.fhir.r5.model.Subscription; +import org.hl7.fhir.r5.model.SubscriptionTopic; +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import org.mockito.ArgumentCaptor; +import org.mockito.InjectMocks; +import org.mockito.Mock; +import org.mockito.Spy; +import org.mockito.junit.jupiter.MockitoExtension; + +import java.util.Arrays; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNotNull; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.verifyNoMoreInteractions; +import static org.mockito.Mockito.when; + +@ExtendWith(MockitoExtension.class) +class MdmSubscriptionLoaderR5Test { + + @Mock + IFhirResourceDao mySubscriptionDao; + @Mock + IFhirResourceDao mySubscriptionTopicDao; + @Mock + DaoRegistry myDaoRegistry; + @Mock + IMdmSettings myMdmSettings; + @Spy + FhirContext myFhirContext = FhirContext.forR5Cached(); + @Mock + IChannelNamer myChannelNamer; + @Mock + SubscriptionLoader mySubscriptionLoader; + @Mock + SubscriptionTopicLoader mySubscriptionTopicLoader; + @InjectMocks + MdmSubscriptionLoader mySvc = new MdmSubscriptionLoader(); + + @AfterEach + public void after() { + verifyNoMoreInteractions(mySubscriptionTopicDao); + } + + @Test + public void testDaoUpdateMdmSubscriptions_withR5FhirContext_createsCorrectSubscriptions() { + // setup + MdmRulesJson mdmRulesJson = new MdmRulesJson(); + mdmRulesJson.setMdmTypes(Arrays.asList("Patient")); + when(myMdmSettings.getMdmRules()).thenReturn(mdmRulesJson); + when(myChannelNamer.getChannelName(any(), any())).thenReturn("Test"); + when(myDaoRegistry.getResourceDao(eq("Subscription"))).thenReturn(mySubscriptionDao); + when(myDaoRegistry.getResourceDao(eq("SubscriptionTopic"))).thenReturn(mySubscriptionTopicDao); + when(mySubscriptionDao.read(any(), any(RequestDetails.class))).thenThrow(new ResourceGoneException("")); + + // execute + mySvc.daoUpdateMdmSubscriptions(); + + // verify SubscriptionTopic + ArgumentCaptor subscriptionTopicCaptor = ArgumentCaptor.forClass(SubscriptionTopic.class); + verify(mySubscriptionTopicDao).update(subscriptionTopicCaptor.capture(), any(RequestDetails.class)); + + SubscriptionTopic subscriptionTopic = subscriptionTopicCaptor.getValue(); + assertNotNull(subscriptionTopic); + assertEquals("mdm-subscription-topic", subscriptionTopic.getId()); + assertEquals(1, subscriptionTopic.getResourceTrigger().size()); + SubscriptionTopic.SubscriptionTopicResourceTriggerComponent triggerComponent = subscriptionTopic.getResourceTrigger().get(0); + assertEquals("Patient", triggerComponent.getResource()); + + // verify Subscription + ArgumentCaptor subscriptionCaptor = ArgumentCaptor.forClass(Subscription.class); + verify(mySubscriptionDao).update(subscriptionCaptor.capture(), any(RequestDetails.class)); + + Subscription subscription = subscriptionCaptor.getValue(); + assertNotNull(subscription); + assertEquals("mdm-subscription", subscription.getId()); + } +} diff --git a/hapi-fhir-jpaserver-mdm/src/test/java/ca/uhn/fhir/jpa/mdm/dao/MdmLinkDaoSvcTest.java b/hapi-fhir-jpaserver-mdm/src/test/java/ca/uhn/fhir/jpa/mdm/dao/MdmLinkDaoSvcTest.java index 3078a06e0c0..fc1aaa49a8f 100644 --- a/hapi-fhir-jpaserver-mdm/src/test/java/ca/uhn/fhir/jpa/mdm/dao/MdmLinkDaoSvcTest.java +++ b/hapi-fhir-jpaserver-mdm/src/test/java/ca/uhn/fhir/jpa/mdm/dao/MdmLinkDaoSvcTest.java @@ -20,10 +20,12 @@ import org.hl7.fhir.r4.model.Enumerations; import org.hl7.fhir.r4.model.Patient; import org.hl7.fhir.r4.model.Practitioner; import org.junit.jupiter.api.Test; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.ValueSource; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import javax.annotation.Nonnull; +import jakarta.annotation.Nonnull; import java.util.ArrayList; import java.util.Arrays; import java.util.Date; @@ -276,6 +278,179 @@ public class MdmLinkDaoSvcTest extends BaseMdmR4Test { assertEquals(2, actualMdmLinkRevisions.size(), "Both Patient/p123 and Practitioner/p123 should be returned"); } + @ParameterizedTest + @ValueSource(strings = {"allUnknown", "someUnknown"}) + public void testHistoryForUnknownIdsSourceIdOnly(String mode) { + // setup + final List mdmLinksWithLinkedPatients1 = createMdmLinksWithLinkedPatients(MdmMatchResultEnum.MATCH, 3); + final List mdmLinksWithLinkedPatients2 = createMdmLinksWithLinkedPatients(MdmMatchResultEnum.MATCH, 4); + + MdmHistorySearchParameters mdmHistorySearchParameters = null; + List> expectedMdLinkRevisions = null; + switch (mode) { + // $mdm-link-history?resourceId=Patient/unknown + case "allUnknown" -> { + mdmHistorySearchParameters = new MdmHistorySearchParameters().setSourceIds(List.of("unknown")); + expectedMdLinkRevisions = new ArrayList<>(); + } + // $mdm-link-history?resourceId=Patient/1,Patient/2,Patient/unknown + case "someUnknown" -> { + List resourceIdsWithSomeUnknown = new ArrayList<>(getIdsFromMdmLinks(MdmLink::getSourcePersistenceId, mdmLinksWithLinkedPatients1.get(0), mdmLinksWithLinkedPatients2.get(0))); + resourceIdsWithSomeUnknown.add("unknown"); + mdmHistorySearchParameters = new MdmHistorySearchParameters().setSourceIds(resourceIdsWithSomeUnknown); + + final JpaPid goldenResourceId1 = mdmLinksWithLinkedPatients1.get(0).getGoldenResourcePersistenceId(); + final JpaPid goldenResourceId2 = mdmLinksWithLinkedPatients2.get(0).getGoldenResourcePersistenceId(); + final JpaPid sourceId1_1 = mdmLinksWithLinkedPatients1.get(0).getSourcePersistenceId(); + final JpaPid sourceId2_1 = mdmLinksWithLinkedPatients2.get(0).getSourcePersistenceId(); + expectedMdLinkRevisions = List.of( + buildMdmLinkWithRevision(1, RevisionType.ADD, MdmMatchResultEnum.MATCH, goldenResourceId1, sourceId1_1), + buildMdmLinkWithRevision(4, RevisionType.ADD, MdmMatchResultEnum.MATCH, goldenResourceId2, sourceId2_1) + ); + } + } + + // execute + final List> actualMdmLinkRevisions = myMdmLinkDaoSvc.findMdmLinkHistory(mdmHistorySearchParameters); + + // verify + assert expectedMdLinkRevisions != null; + assertMdmRevisionsEqual(expectedMdLinkRevisions, actualMdmLinkRevisions); + } + + @ParameterizedTest + @ValueSource(strings = {"allUnknown", "someUnknown"}) + public void testHistoryForUnknownIdsGoldenResourceIdOnly(String mode) { + // setup + final List mdmLinksWithLinkedPatients1 = createMdmLinksWithLinkedPatients(MdmMatchResultEnum.MATCH, 3); + final List mdmLinksWithLinkedPatients2 = createMdmLinksWithLinkedPatients(MdmMatchResultEnum.MATCH, 4); + + MdmHistorySearchParameters mdmHistorySearchParameters = null; + List> expectedMdLinkRevisions = null; + switch (mode) { + // $mdm-link-history?goldenResourceId=Patient/unknown + case "allUnknown" -> { + mdmHistorySearchParameters = new MdmHistorySearchParameters().setGoldenResourceIds(List.of("unknown")); + expectedMdLinkRevisions = new ArrayList<>(); + } + // $mdm-link-history?goldenResourceId=Patient/1,Patient/2,Patient/unknown + case "someUnknown" -> { + List resourceIdsWithSomeUnknown = new ArrayList<>(getIdsFromMdmLinks(MdmLink::getGoldenResourcePersistenceId, mdmLinksWithLinkedPatients1.get(0), mdmLinksWithLinkedPatients2.get(0))); + resourceIdsWithSomeUnknown.add("unknown"); + mdmHistorySearchParameters = new MdmHistorySearchParameters().setGoldenResourceIds(resourceIdsWithSomeUnknown); + + final JpaPid goldenResourceId1 = mdmLinksWithLinkedPatients1.get(0).getGoldenResourcePersistenceId(); + final JpaPid goldenResourceId2 = mdmLinksWithLinkedPatients2.get(0).getGoldenResourcePersistenceId(); + final JpaPid sourceId1_1 = mdmLinksWithLinkedPatients1.get(0).getSourcePersistenceId(); + final JpaPid sourceId1_2 = mdmLinksWithLinkedPatients1.get(1).getSourcePersistenceId(); + final JpaPid sourceId1_3 = mdmLinksWithLinkedPatients1.get(2).getSourcePersistenceId(); + final JpaPid sourceId2_1 = mdmLinksWithLinkedPatients2.get(0).getSourcePersistenceId(); + final JpaPid sourceId2_2 = mdmLinksWithLinkedPatients2.get(1).getSourcePersistenceId(); + final JpaPid sourceId2_3 = mdmLinksWithLinkedPatients2.get(2).getSourcePersistenceId(); + final JpaPid sourceId2_4 = mdmLinksWithLinkedPatients2.get(3).getSourcePersistenceId(); + expectedMdLinkRevisions = List.of( + buildMdmLinkWithRevision(1, RevisionType.ADD, MdmMatchResultEnum.MATCH, goldenResourceId1, sourceId1_1), + buildMdmLinkWithRevision(2, RevisionType.ADD, MdmMatchResultEnum.MATCH, goldenResourceId1, sourceId1_2), + buildMdmLinkWithRevision(3, RevisionType.ADD, MdmMatchResultEnum.MATCH, goldenResourceId1, sourceId1_3), + buildMdmLinkWithRevision(4, RevisionType.ADD, MdmMatchResultEnum.MATCH, goldenResourceId2, sourceId2_1), + buildMdmLinkWithRevision(5, RevisionType.ADD, MdmMatchResultEnum.MATCH, goldenResourceId2, sourceId2_2), + buildMdmLinkWithRevision(6, RevisionType.ADD, MdmMatchResultEnum.MATCH, goldenResourceId2, sourceId2_3), + buildMdmLinkWithRevision(7, RevisionType.ADD, MdmMatchResultEnum.MATCH, goldenResourceId2, sourceId2_4) + ); + } + } + + // execute + final List> actualMdmLinkRevisions = myMdmLinkDaoSvc.findMdmLinkHistory(mdmHistorySearchParameters); + + // verify + assert expectedMdLinkRevisions != null; + assertMdmRevisionsEqual(expectedMdLinkRevisions, actualMdmLinkRevisions); + } + + @ParameterizedTest + @ValueSource(strings = { + "allUnknownSourceId", + "allUnknownGoldenId", + "allUnknownBoth", + "someUnknownSourceId", + "someUnknownGoldenId", + "someUnknownBoth" + }) + public void testHistoryForUnknownIdsBothSourceAndGoldenResourceId(String mode) { + // setup + final List mdmLinksWithLinkedPatients1 = createMdmLinksWithLinkedPatients(MdmMatchResultEnum.MATCH, 3); + final List mdmLinksWithLinkedPatients2 = createMdmLinksWithLinkedPatients(MdmMatchResultEnum.MATCH, 4); + + MdmHistorySearchParameters mdmHistorySearchParameters = null; + final JpaPid goldenResourceId1 = mdmLinksWithLinkedPatients1.get(0).getGoldenResourcePersistenceId(); + final JpaPid goldenResourceId2 = mdmLinksWithLinkedPatients2.get(0).getGoldenResourcePersistenceId(); + final JpaPid sourceId1_1 = mdmLinksWithLinkedPatients1.get(0).getSourcePersistenceId(); + final JpaPid sourceId2_1 = mdmLinksWithLinkedPatients2.get(0).getSourcePersistenceId(); + List> expectedMdLinkRevisions = List.of( + buildMdmLinkWithRevision(1, RevisionType.ADD, MdmMatchResultEnum.MATCH, goldenResourceId1, sourceId1_1), + buildMdmLinkWithRevision(4, RevisionType.ADD, MdmMatchResultEnum.MATCH, goldenResourceId2, sourceId2_1) + ); + switch (mode) { + // $mdm-link-history?resourceId=Patient/unknown&goldenResourceId=Patient/1,Patient/2 + case "allUnknownSourceId" -> { + mdmHistorySearchParameters = new MdmHistorySearchParameters() + .setSourceIds(List.of("unknown")) + .setGoldenResourceIds(new ArrayList<>(getIdsFromMdmLinks(MdmLink::getGoldenResourcePersistenceId, mdmLinksWithLinkedPatients1.get(0), mdmLinksWithLinkedPatients2.get(0)))); + expectedMdLinkRevisions = new ArrayList<>(); + } + // $mdm-link-history?resourceId=Patient/1,Patient/2&goldenResourceId=Patient/unknown + case "allUnknownGoldenId" -> { + mdmHistorySearchParameters = new MdmHistorySearchParameters() + .setSourceIds(new ArrayList<>(getIdsFromMdmLinks(MdmLink::getSourcePersistenceId, mdmLinksWithLinkedPatients1.get(0), mdmLinksWithLinkedPatients2.get(0)))) + .setGoldenResourceIds(List.of("unknown")); + expectedMdLinkRevisions = new ArrayList<>(); + } + // $mdm-link-history?resourceId=Patient/unknown&goldenResourceId=Patient/unknownGolden + case "allUnknownBoth" -> { + mdmHistorySearchParameters = new MdmHistorySearchParameters() + .setSourceIds(List.of("unknown")) + .setGoldenResourceIds(List.of("unknownGolden")); + expectedMdLinkRevisions = new ArrayList<>(); + } + // $mdm-link-history?resourceId=Patient/1,Patient/2,Patient/unknown&goldenResourceId=Patient/3,Patient/4 + case "someUnknownSourceId" -> { + List sourceIdsWithSomeUnknown = new ArrayList<>(getIdsFromMdmLinks(MdmLink::getSourcePersistenceId, mdmLinksWithLinkedPatients1.get(0), mdmLinksWithLinkedPatients2.get(0))); + sourceIdsWithSomeUnknown.add("unknown"); + List goldenResourceIds = new ArrayList<>(getIdsFromMdmLinks(MdmLink::getGoldenResourcePersistenceId, mdmLinksWithLinkedPatients1.get(0), mdmLinksWithLinkedPatients2.get(0))); + mdmHistorySearchParameters = new MdmHistorySearchParameters() + .setSourceIds(sourceIdsWithSomeUnknown) + .setGoldenResourceIds(goldenResourceIds); + } + // $mdm-link-history?resourceId=Patient/1,Patient/2&goldenResourceId=Patient/3,Patient/4,Patient/unknown + case "someUnknownGoldenId" -> { + List sourceIds = new ArrayList<>(getIdsFromMdmLinks(MdmLink::getSourcePersistenceId, mdmLinksWithLinkedPatients1.get(0), mdmLinksWithLinkedPatients2.get(0))); + List goldenResourceIdsSomeUnknown = new ArrayList<>(getIdsFromMdmLinks(MdmLink::getGoldenResourcePersistenceId, mdmLinksWithLinkedPatients1.get(0), mdmLinksWithLinkedPatients2.get(0))); + goldenResourceIdsSomeUnknown.add("unknown"); + mdmHistorySearchParameters = new MdmHistorySearchParameters() + .setSourceIds(sourceIds) + .setGoldenResourceIds(goldenResourceIdsSomeUnknown); + } + // $mdm-link-history?resourceId=Patient/1,Patient/2,Patient/unknown&goldenResourceId=Patient/3,Patient/4,Patient/unknownGolden + case "someUnknownBoth" -> { + List sourceIdsSomeUnknown = new ArrayList<>(getIdsFromMdmLinks(MdmLink::getSourcePersistenceId, mdmLinksWithLinkedPatients1.get(0), mdmLinksWithLinkedPatients2.get(0))); + sourceIdsSomeUnknown.add("unknown"); + List goldenResourceIdsSomeUnknown = new ArrayList<>(getIdsFromMdmLinks(MdmLink::getGoldenResourcePersistenceId, mdmLinksWithLinkedPatients1.get(0), mdmLinksWithLinkedPatients2.get(0))); + goldenResourceIdsSomeUnknown.add("unknownGolden"); + mdmHistorySearchParameters = new MdmHistorySearchParameters() + .setSourceIds(sourceIdsSomeUnknown) + .setGoldenResourceIds(goldenResourceIdsSomeUnknown); + } + } + + // execute + final List> actualMdmLinkRevisions = myMdmLinkDaoSvc.findMdmLinkHistory(mdmHistorySearchParameters); + + // verify + assert expectedMdLinkRevisions != null; + assertMdmRevisionsEqual(expectedMdLinkRevisions, actualMdmLinkRevisions); + } + @Nonnull private static List getIdsFromMdmLinks(Function getIdFunction, MdmLink... mdmLinks) { return Arrays.stream(mdmLinks) diff --git a/hapi-fhir-jpaserver-mdm/src/test/java/ca/uhn/fhir/jpa/mdm/dao/MdmMetricSvcJpaIT.java b/hapi-fhir-jpaserver-mdm/src/test/java/ca/uhn/fhir/jpa/mdm/dao/MdmMetricSvcJpaIT.java new file mode 100644 index 00000000000..7953cc7316a --- /dev/null +++ b/hapi-fhir-jpaserver-mdm/src/test/java/ca/uhn/fhir/jpa/mdm/dao/MdmMetricSvcJpaIT.java @@ -0,0 +1,173 @@ +package ca.uhn.fhir.jpa.mdm.dao; + +import ca.uhn.fhir.jpa.api.dao.DaoRegistry; +import ca.uhn.fhir.jpa.config.HapiFhirLocalContainerEntityManagerFactoryBean; +import ca.uhn.fhir.jpa.dao.data.IMdmLinkJpaMetricsRepository; +import ca.uhn.fhir.jpa.dao.mdm.MdmMetricSvcJpaImpl; +import ca.uhn.fhir.jpa.entity.MdmLink; +import ca.uhn.fhir.jpa.mdm.BaseMdmR4Test; +import ca.uhn.fhir.jpa.mdm.IMdmMetricSvcTest; +import ca.uhn.fhir.jpa.mdm.helper.MdmLinkHelper; +import ca.uhn.fhir.jpa.mdm.helper.testmodels.MDMState; +import ca.uhn.fhir.jpa.mdm.models.GenerateMetricsTestParameters; +import ca.uhn.fhir.jpa.mdm.models.LinkMetricTestParameters; +import ca.uhn.fhir.jpa.mdm.models.LinkScoreMetricTestParams; +import ca.uhn.fhir.jpa.mdm.models.ResourceMetricTestParams; +import ca.uhn.fhir.jpa.model.dao.JpaPid; +import ca.uhn.fhir.mdm.api.IMdmMetricSvc; +import ca.uhn.fhir.mdm.model.MdmMetrics; +import ca.uhn.fhir.mdm.util.MdmResourceUtil; +import com.fasterxml.jackson.core.JsonProcessingException; +import com.fasterxml.jackson.databind.ObjectMapper; +import jakarta.persistence.EntityManagerFactory; +import org.apache.commons.lang3.StringUtils; +import org.hl7.fhir.r4.model.Patient; +import org.junit.jupiter.api.BeforeEach; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.beans.factory.annotation.Qualifier; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.test.context.ContextConfiguration; + +import java.util.List; + +import static org.junit.jupiter.api.Assertions.fail; + +@ContextConfiguration(classes = { + MdmMetricSvcJpaIT.TestConfig.class +}) +public class MdmMetricSvcJpaIT extends BaseMdmR4Test implements IMdmMetricSvcTest { + + private static final Logger ourLog = LoggerFactory.getLogger(MdmMetricSvcJpaIT.class); + + @Configuration + public static class TestConfig { + + @Autowired + @Qualifier("metricsRepository") + private IMdmLinkJpaMetricsRepository myJpaRepository; + + @Autowired + private DaoRegistry myDaoRegistry; + + @Autowired + private EntityManagerFactory myEntityManagerFactory; + + @Autowired + private HapiFhirLocalContainerEntityManagerFactoryBean myEntityFactory; + + // this has to be provided via spring, or the + // @Transactional barrier is never invoked + @Bean + IMdmMetricSvc mdmMetricSvc() { + return new MdmMetricSvcJpaImpl( + myJpaRepository, + myDaoRegistry, + myEntityManagerFactory + ); + } + } + + private final ObjectMapper myObjectMapper = new ObjectMapper(); + + @Autowired + private MdmLinkHelper myLinkHelper; + + @Autowired + private IMdmMetricSvc mySvc; + + @Override + @BeforeEach + public void before() throws Exception { + super.before(); + } + + @Override + public IMdmMetricSvc getMetricsSvc() { + return mySvc; + } + + @Override + public void generateMdmMetricsSetup(GenerateMetricsTestParameters theParameters) { + if (StringUtils.isNotBlank(theParameters.getInitialState())) { + MDMState state = new MDMState<>(); + state.setInputState(theParameters.getInitialState()); + myLinkHelper.setup(state); + + // update scores if needed + setupScores(theParameters.getScores()); + } + } + + @Override + public void generateLinkMetricsSetup(LinkMetricTestParameters theParameters) { + ourLog.info(theParameters.getInitialState()); + if (StringUtils.isNotBlank(theParameters.getInitialState())) { + // we can only initialize the state if there is a state to initialize + MDMState state = new MDMState<>(); + state.setInputState(theParameters.getInitialState()); + myLinkHelper.setup(state); + } + } + + @Override + public void generateResourceMetricsSetup(ResourceMetricTestParams theParams) { + MDMState state = new MDMState<>(); + String initialState = theParams.getInitialState(); + if (StringUtils.isNotBlank(initialState)) { + state.setInputState(initialState); + + for (String forcedBlockedGRId : theParams.getBlockedResourceGoldenResourceIds()) { + Patient gr = new Patient(); + gr.setActive(true); + gr.setId("Patient/" + forcedBlockedGRId); + MdmResourceUtil.setMdmManaged(gr); + MdmResourceUtil.setGoldenResource(gr); + MdmResourceUtil.setGoldenResourceAsBlockedResourceGoldenResource(gr); + + Patient p = createPatient(gr, true, false); + state.addParameter(forcedBlockedGRId, p); + } + + myLinkHelper.setup(state); + } + } + + @Override + public void generateLinkScoreMetricsSetup(LinkScoreMetricTestParams theParams) { + MDMState state = new MDMState<>(); + String initialState = theParams.getInitialState(); + + if (StringUtils.isNotBlank(initialState)) { + state.setInputState(initialState); + + myLinkHelper.setup(state); + + // update scores if needed + setupScores(theParams.getScores()); + } + } + + private void setupScores(List theParams) { + List links = myMdmLinkDao.findAll(); + for (int i = 0; i < theParams.size() && i < links.size(); i++) { + Double score = theParams.get(i); + MdmLink link = links.get(i); + link.setScore(score); + myMdmLinkDao.save(link); + } + } + + @Override + public String getStringMetrics(MdmMetrics theMetrics) { + try { + return myObjectMapper.writeValueAsString(theMetrics); + } catch (JsonProcessingException ex) { + // we've failed anyway - we might as well display the exception + fail(ex); + return "NOT PARSEABLE!"; + } + } +} diff --git a/hapi-fhir-jpaserver-mdm/src/test/java/ca/uhn/fhir/jpa/mdm/helper/BaseMdmHelper.java b/hapi-fhir-jpaserver-mdm/src/test/java/ca/uhn/fhir/jpa/mdm/helper/BaseMdmHelper.java index dd185421e9f..4a4089ef08e 100644 --- a/hapi-fhir-jpaserver-mdm/src/test/java/ca/uhn/fhir/jpa/mdm/helper/BaseMdmHelper.java +++ b/hapi-fhir-jpaserver-mdm/src/test/java/ca/uhn/fhir/jpa/mdm/helper/BaseMdmHelper.java @@ -19,7 +19,7 @@ import org.mockito.Mock; import org.mockito.MockitoAnnotations; import org.springframework.beans.factory.annotation.Autowired; -import javax.servlet.http.HttpServletRequest; +import jakarta.servlet.http.HttpServletRequest; import java.util.function.Supplier; import static org.awaitility.Awaitility.await; diff --git a/hapi-fhir-jpaserver-mdm/src/test/java/ca/uhn/fhir/jpa/mdm/helper/MdmHelperR4.java b/hapi-fhir-jpaserver-mdm/src/test/java/ca/uhn/fhir/jpa/mdm/helper/MdmHelperR4.java index 7e3023cc5b9..b3dea888987 100644 --- a/hapi-fhir-jpaserver-mdm/src/test/java/ca/uhn/fhir/jpa/mdm/helper/MdmHelperR4.java +++ b/hapi-fhir-jpaserver-mdm/src/test/java/ca/uhn/fhir/jpa/mdm/helper/MdmHelperR4.java @@ -13,7 +13,7 @@ import org.hl7.fhir.instance.model.api.IBaseResource; import org.hl7.fhir.r4.model.Patient; import org.springframework.beans.factory.annotation.Autowired; -import javax.annotation.Nonnull; +import jakarta.annotation.Nonnull; import java.util.List; import static ca.uhn.fhir.mdm.api.MdmConstants.CODE_GOLDEN_RECORD; diff --git a/hapi-fhir-jpaserver-mdm/src/test/java/ca/uhn/fhir/jpa/mdm/interceptor/MdmEventIT.java b/hapi-fhir-jpaserver-mdm/src/test/java/ca/uhn/fhir/jpa/mdm/interceptor/MdmEventIT.java index d5dba4b5100..ca31a16f384 100644 --- a/hapi-fhir-jpaserver-mdm/src/test/java/ca/uhn/fhir/jpa/mdm/interceptor/MdmEventIT.java +++ b/hapi-fhir-jpaserver-mdm/src/test/java/ca/uhn/fhir/jpa/mdm/interceptor/MdmEventIT.java @@ -27,7 +27,7 @@ import org.springframework.data.domain.Example; import org.springframework.test.context.ContextConfiguration; import org.springframework.test.context.TestPropertySource; -import javax.annotation.Nonnull; +import jakarta.annotation.Nonnull; import java.util.List; import java.util.Set; import java.util.UUID; diff --git a/hapi-fhir-jpaserver-mdm/src/test/java/ca/uhn/fhir/jpa/mdm/interceptor/MdmSearchExpandingInterceptorIT.java b/hapi-fhir-jpaserver-mdm/src/test/java/ca/uhn/fhir/jpa/mdm/interceptor/MdmSearchExpandingInterceptorIT.java index 31125dd941c..5b1150bc1ca 100644 --- a/hapi-fhir-jpaserver-mdm/src/test/java/ca/uhn/fhir/jpa/mdm/interceptor/MdmSearchExpandingInterceptorIT.java +++ b/hapi-fhir-jpaserver-mdm/src/test/java/ca/uhn/fhir/jpa/mdm/interceptor/MdmSearchExpandingInterceptorIT.java @@ -30,7 +30,7 @@ import org.slf4j.Logger; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.test.context.ContextConfiguration; -import javax.servlet.http.HttpServletRequest; +import jakarta.servlet.http.HttpServletRequest; import java.util.ArrayList; import java.util.HashMap; import java.util.List; diff --git a/hapi-fhir-jpaserver-mdm/src/test/java/ca/uhn/fhir/jpa/mdm/interceptor/MdmStorageInterceptorIT.java b/hapi-fhir-jpaserver-mdm/src/test/java/ca/uhn/fhir/jpa/mdm/interceptor/MdmStorageInterceptorIT.java index f7e8615c22d..953a7574618 100644 --- a/hapi-fhir-jpaserver-mdm/src/test/java/ca/uhn/fhir/jpa/mdm/interceptor/MdmStorageInterceptorIT.java +++ b/hapi-fhir-jpaserver-mdm/src/test/java/ca/uhn/fhir/jpa/mdm/interceptor/MdmStorageInterceptorIT.java @@ -172,8 +172,12 @@ public class MdmStorageInterceptorIT extends BaseMdmR4Test { assertLinksMatchResult(MdmMatchResultEnum.MATCH, MdmMatchResultEnum.POSSIBLE_MATCH, MdmMatchResultEnum.POSSIBLE_MATCH); + logAllTokenIndexes(); + // When - myPatientDao.delete(paulPatient.getIdElement()); + myPatientDao.delete(paulPatient.getIdElement(), new SystemRequestDetails()); + + logAllTokenIndexes(); // Then List resources = myPatientDao.search(new SearchParameterMap(), SystemRequestDetails.forAllPartitions()).getAllResources(); diff --git a/hapi-fhir-jpaserver-mdm/src/test/java/ca/uhn/fhir/jpa/mdm/matcher/BaseGoldenResourceMatcher.java b/hapi-fhir-jpaserver-mdm/src/test/java/ca/uhn/fhir/jpa/mdm/matcher/BaseGoldenResourceMatcher.java index 92cbc41a334..cc139087ad3 100644 --- a/hapi-fhir-jpaserver-mdm/src/test/java/ca/uhn/fhir/jpa/mdm/matcher/BaseGoldenResourceMatcher.java +++ b/hapi-fhir-jpaserver-mdm/src/test/java/ca/uhn/fhir/jpa/mdm/matcher/BaseGoldenResourceMatcher.java @@ -12,7 +12,7 @@ import org.hl7.fhir.instance.model.api.IAnyResource; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import javax.annotation.Nullable; +import jakarta.annotation.Nullable; import java.util.ArrayList; import java.util.Arrays; import java.util.Collection; diff --git a/hapi-fhir-jpaserver-mdm/src/test/java/ca/uhn/fhir/jpa/mdm/provider/BaseLinkR4Test.java b/hapi-fhir-jpaserver-mdm/src/test/java/ca/uhn/fhir/jpa/mdm/provider/BaseLinkR4Test.java index 7860c5d33d3..3ea0ea7e7df 100644 --- a/hapi-fhir-jpaserver-mdm/src/test/java/ca/uhn/fhir/jpa/mdm/provider/BaseLinkR4Test.java +++ b/hapi-fhir-jpaserver-mdm/src/test/java/ca/uhn/fhir/jpa/mdm/provider/BaseLinkR4Test.java @@ -14,7 +14,7 @@ import org.hl7.fhir.r4.model.StringType; import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.BeforeEach; -import javax.annotation.Nonnull; +import jakarta.annotation.Nonnull; import java.io.IOException; import java.util.List; diff --git a/hapi-fhir-jpaserver-mdm/src/test/java/ca/uhn/fhir/jpa/mdm/provider/BaseProviderR4Test.java b/hapi-fhir-jpaserver-mdm/src/test/java/ca/uhn/fhir/jpa/mdm/provider/BaseProviderR4Test.java index 5fa7ce7d19d..ddf5fa37f80 100644 --- a/hapi-fhir-jpaserver-mdm/src/test/java/ca/uhn/fhir/jpa/mdm/provider/BaseProviderR4Test.java +++ b/hapi-fhir-jpaserver-mdm/src/test/java/ca/uhn/fhir/jpa/mdm/provider/BaseProviderR4Test.java @@ -21,7 +21,7 @@ import org.springframework.beans.factory.annotation.Autowired; import org.springframework.core.io.DefaultResourceLoader; import org.springframework.core.io.Resource; -import javax.annotation.Nonnull; +import jakarta.annotation.Nonnull; import java.io.IOException; import java.util.ArrayList; import java.util.List; @@ -54,8 +54,10 @@ public abstract class BaseProviderR4Test extends BaseMdmR4Test { myMdmResourceMatcherSvc.setMdmRulesJson(myMdmSettings.getMdmRules()); } + @Override @BeforeEach public void before() throws Exception { + super.before(); myMdmProvider = new MdmProviderDstu3Plus(myFhirContext, myMdmControllerSvc, myMdmHelper, diff --git a/hapi-fhir-jpaserver-mdm/src/test/java/ca/uhn/fhir/jpa/mdm/provider/MdmOperationPointcutsIT.java b/hapi-fhir-jpaserver-mdm/src/test/java/ca/uhn/fhir/jpa/mdm/provider/MdmOperationPointcutsIT.java index e9f69df9592..76d6b6d065f 100644 --- a/hapi-fhir-jpaserver-mdm/src/test/java/ca/uhn/fhir/jpa/mdm/provider/MdmOperationPointcutsIT.java +++ b/hapi-fhir-jpaserver-mdm/src/test/java/ca/uhn/fhir/jpa/mdm/provider/MdmOperationPointcutsIT.java @@ -7,6 +7,7 @@ import ca.uhn.fhir.interceptor.api.IInterceptorService; import ca.uhn.fhir.interceptor.api.Pointcut; import ca.uhn.fhir.interceptor.model.RequestPartitionId; import ca.uhn.fhir.jpa.batch.models.Batch2JobStartResponse; +import ca.uhn.fhir.jpa.dao.expunge.ExpungeEverythingService; import ca.uhn.fhir.jpa.entity.MdmLink; import ca.uhn.fhir.jpa.mdm.helper.MdmLinkHelper; import ca.uhn.fhir.jpa.mdm.helper.testmodels.MDMState; @@ -14,6 +15,7 @@ import ca.uhn.fhir.jpa.model.dao.JpaPid; import ca.uhn.fhir.mdm.api.IMdmSubmitSvc; import ca.uhn.fhir.mdm.api.MdmLinkSourceEnum; import ca.uhn.fhir.mdm.api.MdmMatchResultEnum; +import ca.uhn.fhir.mdm.interceptor.MdmStorageInterceptor; import ca.uhn.fhir.mdm.model.mdmevents.MdmClearEvent; import ca.uhn.fhir.mdm.model.mdmevents.MdmHistoryEvent; import ca.uhn.fhir.mdm.model.mdmevents.MdmLinkEvent; @@ -119,10 +121,18 @@ public class MdmOperationPointcutsIT extends BaseProviderR4Test { @SpyBean private IMdmSubmitSvc myMdmSubmitSvc; + private MdmLinkHistoryProviderDstu3Plus myLinkHistoryProvider; private final List myInterceptors = new ArrayList<>(); + @Override + @AfterEach + public void afterPurgeDatabase() { + super.afterPurgeDatabase(); + } + + @Override @BeforeEach public void before() throws Exception { super.before(); @@ -134,6 +144,7 @@ public class MdmOperationPointcutsIT extends BaseProviderR4Test { ); } + @Override @AfterEach public void after() throws IOException { super.after(); @@ -141,8 +152,6 @@ public class MdmOperationPointcutsIT extends BaseProviderR4Test { myInterceptors.clear(); } - @Nested - class MdmProviderDstu3PlusTest { @Test public void mergeGoldenResources_withInterceptor_firesHook() { // setup @@ -521,7 +530,7 @@ public class MdmOperationPointcutsIT extends BaseProviderR4Test { } } } - } + private String createUrl(String theResourceType, StringType theCriteria) { String url = theResourceType; @@ -531,8 +540,6 @@ public class MdmOperationPointcutsIT extends BaseProviderR4Test { return url; } - @Nested - class MdmLinkHistoryProviderDstu3PlusTest { @ParameterizedTest @EnumSource(LinkHistoryParameters.class) @@ -597,6 +604,6 @@ public class MdmOperationPointcutsIT extends BaseProviderR4Test { assertTrue(called.get()); assertFalse(retval.isEmpty()); } - } + } diff --git a/hapi-fhir-jpaserver-mdm/src/test/java/ca/uhn/fhir/jpa/mdm/provider/MdmProviderClearLinkR4Test.java b/hapi-fhir-jpaserver-mdm/src/test/java/ca/uhn/fhir/jpa/mdm/provider/MdmProviderClearLinkR4Test.java index 11384438ca4..385b398653c 100644 --- a/hapi-fhir-jpaserver-mdm/src/test/java/ca/uhn/fhir/jpa/mdm/provider/MdmProviderClearLinkR4Test.java +++ b/hapi-fhir-jpaserver-mdm/src/test/java/ca/uhn/fhir/jpa/mdm/provider/MdmProviderClearLinkR4Test.java @@ -33,7 +33,7 @@ import org.junit.jupiter.api.Test; import org.mockito.ArgumentCaptor; import org.slf4j.LoggerFactory; -import javax.annotation.Nonnull; +import jakarta.annotation.Nonnull; import java.util.ArrayList; import java.util.List; import java.util.regex.Matcher; diff --git a/hapi-fhir-jpaserver-mdm/src/test/java/ca/uhn/fhir/jpa/mdm/provider/MdmProviderCrossPartitionR4Test.java b/hapi-fhir-jpaserver-mdm/src/test/java/ca/uhn/fhir/jpa/mdm/provider/MdmProviderCrossPartitionR4Test.java index 8a899634fda..90001543085 100644 --- a/hapi-fhir-jpaserver-mdm/src/test/java/ca/uhn/fhir/jpa/mdm/provider/MdmProviderCrossPartitionR4Test.java +++ b/hapi-fhir-jpaserver-mdm/src/test/java/ca/uhn/fhir/jpa/mdm/provider/MdmProviderCrossPartitionR4Test.java @@ -37,7 +37,8 @@ public class MdmProviderCrossPartitionR4Test extends BaseProviderR4Test{ private static final String PARTITION_GOLDEN_RESOURCE = "PARTITION-GOLDEN"; - @BeforeEach + @Override + @BeforeEach public void before() throws Exception { super.before(); diff --git a/hapi-fhir-jpaserver-mdm/src/test/java/ca/uhn/fhir/jpa/mdm/svc/MdmCandidateSearchSvcIT.java b/hapi-fhir-jpaserver-mdm/src/test/java/ca/uhn/fhir/jpa/mdm/svc/MdmCandidateSearchSvcIT.java index f27af6f4e6f..ad3487528f5 100644 --- a/hapi-fhir-jpaserver-mdm/src/test/java/ca/uhn/fhir/jpa/mdm/svc/MdmCandidateSearchSvcIT.java +++ b/hapi-fhir-jpaserver-mdm/src/test/java/ca/uhn/fhir/jpa/mdm/svc/MdmCandidateSearchSvcIT.java @@ -149,7 +149,7 @@ public class MdmCandidateSearchSvcIT extends BaseMdmR4Test { myMdmCandidateSearchSvc.findCandidates("Patient", newJane, RequestPartitionId.allPartitions()); fail(); } catch (TooManyCandidatesException e) { - assertEquals("HAPI-0762: More than 3 candidate matches found for Patient?identifier=http%3A%2F%2Fa.tv%2F%7CID.JANE.123&active=true. Aborting mdm matching.", e.getMessage()); + assertEquals("HAPI-0762: More than 3 candidate matches found for Patient?identifier=http%3A%2F%2Fa.tv%2F%7CID.JANE.123&active=true. Aborting mdm matching. Updating the candidate search parameters is strongly recommended for better performance of MDM.", e.getMessage()); } } diff --git a/hapi-fhir-jpaserver-mdm/src/test/java/ca/uhn/fhir/jpa/mdm/svc/MdmLinkUpdaterSvcImplIT.java b/hapi-fhir-jpaserver-mdm/src/test/java/ca/uhn/fhir/jpa/mdm/svc/MdmLinkUpdaterSvcImplIT.java index 6433348881e..549f818f399 100644 --- a/hapi-fhir-jpaserver-mdm/src/test/java/ca/uhn/fhir/jpa/mdm/svc/MdmLinkUpdaterSvcImplIT.java +++ b/hapi-fhir-jpaserver-mdm/src/test/java/ca/uhn/fhir/jpa/mdm/svc/MdmLinkUpdaterSvcImplIT.java @@ -41,7 +41,7 @@ class MdmLinkUpdaterSvcImplIT extends BaseMdmR4Test { private IMdmLinkUpdaterSvc myMdmLinkUpdaterSvc; @Autowired - private MdmResourceDaoSvc myMdmResourceDaoSvc; + private MdmResourceDaoSvcImpl myMdmResourceDaoSvc; @Autowired private MessageHelper myMessageHelper; diff --git a/hapi-fhir-jpaserver-mdm/src/test/java/ca/uhn/fhir/jpa/mdm/svc/MdmMatchLinkSvcTest.java b/hapi-fhir-jpaserver-mdm/src/test/java/ca/uhn/fhir/jpa/mdm/svc/MdmMatchLinkSvcTest.java index 967ccf4969c..57b19c41294 100644 --- a/hapi-fhir-jpaserver-mdm/src/test/java/ca/uhn/fhir/jpa/mdm/svc/MdmMatchLinkSvcTest.java +++ b/hapi-fhir-jpaserver-mdm/src/test/java/ca/uhn/fhir/jpa/mdm/svc/MdmMatchLinkSvcTest.java @@ -119,7 +119,6 @@ public class MdmMatchLinkSvcTest { assertLinksMatchVector((Long) null); } - @Test @RepeatedTest(20) public void testUpdatingAResourceToMatchACurrentlyUnmatchedResource_resultsInUpdatedLinksForBoth() { // setup diff --git a/hapi-fhir-jpaserver-mdm/src/test/java/ca/uhn/fhir/jpa/mdm/svc/MdmResourceDaoSvcTest.java b/hapi-fhir-jpaserver-mdm/src/test/java/ca/uhn/fhir/jpa/mdm/svc/MdmResourceDaoSvcTest.java index 1bd33081e2f..e51949689c4 100644 --- a/hapi-fhir-jpaserver-mdm/src/test/java/ca/uhn/fhir/jpa/mdm/svc/MdmResourceDaoSvcTest.java +++ b/hapi-fhir-jpaserver-mdm/src/test/java/ca/uhn/fhir/jpa/mdm/svc/MdmResourceDaoSvcTest.java @@ -7,13 +7,13 @@ import ca.uhn.fhir.jpa.mdm.BaseMdmR4Test; import ca.uhn.fhir.jpa.model.config.PartitionSettings; import ca.uhn.fhir.jpa.searchparam.SearchParameterMap; import ca.uhn.fhir.jpa.searchparam.extractor.ISearchParamExtractor; +import ca.uhn.fhir.mdm.api.IMdmResourceDaoSvc; import ca.uhn.fhir.mdm.util.MdmResourceUtil; import ca.uhn.fhir.rest.api.server.IBundleProvider; import ca.uhn.fhir.rest.api.server.SystemRequestDetails; import ca.uhn.fhir.rest.param.StringOrListParam; import ca.uhn.fhir.rest.param.StringParam; import org.hl7.fhir.instance.model.api.IAnyResource; -import org.hl7.fhir.instance.model.api.IBaseBundle; import org.hl7.fhir.instance.model.api.IBaseResource; import org.hl7.fhir.r4.model.Patient; import org.junit.jupiter.api.AfterEach; @@ -21,7 +21,6 @@ import org.junit.jupiter.api.Test; import org.springframework.beans.factory.annotation.Autowired; import java.io.IOException; -import java.util.ArrayList; import java.util.List; import java.util.Optional; @@ -35,7 +34,7 @@ import static org.junit.jupiter.api.Assertions.assertTrue; public class MdmResourceDaoSvcTest extends BaseMdmR4Test { private static final String TEST_EID = "TEST_EID"; @Autowired - MdmResourceDaoSvc myResourceDaoSvc; + IMdmResourceDaoSvc myResourceDaoSvc; @Autowired private ISearchParamExtractor mySearchParamExtractor; diff --git a/hapi-fhir-jpaserver-mdm/src/test/java/ca/uhn/fhir/mdm/batch2/clear/MdmClearStepTest.java b/hapi-fhir-jpaserver-mdm/src/test/java/ca/uhn/fhir/mdm/batch2/clear/MdmClearStepTest.java index 8af62eed33b..84e8c98a7fa 100644 --- a/hapi-fhir-jpaserver-mdm/src/test/java/ca/uhn/fhir/mdm/batch2/clear/MdmClearStepTest.java +++ b/hapi-fhir-jpaserver-mdm/src/test/java/ca/uhn/fhir/mdm/batch2/clear/MdmClearStepTest.java @@ -21,7 +21,7 @@ import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.springframework.beans.factory.annotation.Autowired; -import javax.annotation.Nonnull; +import jakarta.annotation.Nonnull; import java.util.UUID; import static org.junit.jupiter.api.Assertions.assertEquals; diff --git a/hapi-fhir-jpaserver-mdm/src/test/java/ca/uhn/fhir/mdm/batch2/clear/MdmLinkSlowDeletionSandboxIT.java b/hapi-fhir-jpaserver-mdm/src/test/java/ca/uhn/fhir/mdm/batch2/clear/MdmLinkSlowDeletionSandboxIT.java index da5734f574a..5cba1958901 100644 --- a/hapi-fhir-jpaserver-mdm/src/test/java/ca/uhn/fhir/mdm/batch2/clear/MdmLinkSlowDeletionSandboxIT.java +++ b/hapi-fhir-jpaserver-mdm/src/test/java/ca/uhn/fhir/mdm/batch2/clear/MdmLinkSlowDeletionSandboxIT.java @@ -9,7 +9,7 @@ import ca.uhn.fhir.mdm.api.MdmMatchResultEnum; import ca.uhn.fhir.rest.api.server.SystemRequestDetails; import ca.uhn.fhir.util.StopWatch; import org.apache.commons.dbcp2.BasicDataSource; -import org.hibernate.dialect.PostgreSQL9Dialect; +import org.hibernate.dialect.PostgreSQLDialect; import org.hl7.fhir.r4.model.Coding; import org.hl7.fhir.r4.model.Patient; import org.junit.jupiter.api.Disabled; @@ -115,9 +115,7 @@ public class MdmLinkSlowDeletionSandboxIT extends BaseJpaR4Test { @Override public String getHibernateDialect() { - return PostgreSQL9Dialect.class.getName(); - -// return Oracle12cDialect.class.getName(); + return PostgreSQLDialect.class.getName(); } @Override diff --git a/hapi-fhir-jpaserver-model/pom.xml b/hapi-fhir-jpaserver-model/pom.xml index 8e4fec76eb1..5000d617b40 100644 --- a/hapi-fhir-jpaserver-model/pom.xml +++ b/hapi-fhir-jpaserver-model/pom.xml @@ -5,7 +5,7 @@ ca.uhn.hapi.fhir hapi-deployable-pom - 6.9.7-SNAPSHOT + 6.11.7-SNAPSHOT ../hapi-deployable-pom/pom.xml @@ -81,19 +81,19 @@ ${project.version} - org.hibernate + org.hibernate.orm hibernate-core org.hibernate.search - hibernate-search-mapper-orm + hibernate-search-mapper-orm-orm6 org.hibernate.search hibernate-search-backend-elasticsearch - org.hibernate + org.hibernate.orm hibernate-envers diff --git a/hapi-fhir-jpaserver-model/src/main/java/ca/uhn/fhir/jpa/model/dialect/HapiFhirH2Dialect.java b/hapi-fhir-jpaserver-model/src/main/java/ca/uhn/fhir/jpa/model/dialect/HapiFhirH2Dialect.java deleted file mode 100644 index 074bf229726..00000000000 --- a/hapi-fhir-jpaserver-model/src/main/java/ca/uhn/fhir/jpa/model/dialect/HapiFhirH2Dialect.java +++ /dev/null @@ -1,56 +0,0 @@ -/*- - * #%L - * HAPI FHIR JPA Model - * %% - * Copyright (C) 2014 - 2023 Smile CDR, Inc. - * %% - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * #L% - */ -package ca.uhn.fhir.jpa.model.dialect; - -import org.hibernate.dialect.H2Dialect; - -import java.sql.Types; - -/** - * HAPI FHIR dialect for H2 database - */ -public class HapiFhirH2Dialect extends H2Dialect { - - /** - * Constructor - */ - public HapiFhirH2Dialect() { - super(); - - /* - * These mappings are already defined in the super() constructor, but they - * will only happen if the dialect can connect to the database and - * determine that it's a recent enough version of H2 to support this. This - * means that the Maven plugin that does schema generation doesn't add it. - * So this dialect forces the use of the right defs. - */ - registerColumnType(Types.LONGVARCHAR, "character varying"); - registerColumnType(Types.BINARY, "binary($l)"); - } - - /** - * Workaround until this bug is fixed: - * https://hibernate.atlassian.net/browse/HHH-15002 - */ - @Override - public String toBooleanValueString(boolean bool) { - return bool ? "true" : "false"; - } -} diff --git a/hapi-fhir-jpaserver-model/src/main/java/ca/uhn/fhir/jpa/model/dialect/HapiSequenceStyleGenerator.java b/hapi-fhir-jpaserver-model/src/main/java/ca/uhn/fhir/jpa/model/dialect/HapiSequenceStyleGenerator.java index 9e373c05e5d..cbdaa8a8aac 100644 --- a/hapi-fhir-jpaserver-model/src/main/java/ca/uhn/fhir/jpa/model/dialect/HapiSequenceStyleGenerator.java +++ b/hapi-fhir-jpaserver-model/src/main/java/ca/uhn/fhir/jpa/model/dialect/HapiSequenceStyleGenerator.java @@ -20,16 +20,19 @@ package ca.uhn.fhir.jpa.model.dialect; import ca.uhn.fhir.jpa.model.entity.StorageSettings; -import ca.uhn.fhir.util.ReflectionUtil; +import ca.uhn.fhir.jpa.util.ISequenceValueMassager; import org.apache.commons.lang3.Validate; import org.hibernate.HibernateException; import org.hibernate.MappingException; import org.hibernate.boot.model.relational.Database; +import org.hibernate.boot.model.relational.ExportableProducer; import org.hibernate.boot.model.relational.SqlStringGenerationContext; import org.hibernate.engine.spi.SharedSessionContractImplementor; import org.hibernate.id.BulkInsertionCapableIdentifierGenerator; import org.hibernate.id.IdentifierGenerator; +import org.hibernate.id.OptimizableGenerator; import org.hibernate.id.PersistentIdentifierGenerator; +import org.hibernate.id.enhanced.Optimizer; import org.hibernate.id.enhanced.SequenceStyleGenerator; import org.hibernate.id.enhanced.StandardOptimizerDescriptor; import org.hibernate.service.ServiceRegistry; @@ -45,7 +48,8 @@ import java.util.Properties; */ @SuppressWarnings("unused") public class HapiSequenceStyleGenerator - implements IdentifierGenerator, PersistentIdentifierGenerator, BulkInsertionCapableIdentifierGenerator { + implements PersistentIdentifierGenerator, BulkInsertionCapableIdentifierGenerator, ExportableProducer { + public static final String ID_MASSAGER_TYPE_KEY = "hapi_fhir.sequence_generator_massager"; private final SequenceStyleGenerator myGen = new SequenceStyleGenerator(); @Autowired @@ -68,7 +72,7 @@ public class HapiSequenceStyleGenerator @Override public Serializable generate(SharedSessionContractImplementor theSession, Object theObject) throws HibernateException { - Long retVal = myIdMassager.generate(myGeneratorName); + Long retVal = myIdMassager != null ? myIdMassager.generate(myGeneratorName) : null; if (retVal == null) { Long next = (Long) myGen.generate(theSession, theObject); retVal = myIdMassager.massage(myGeneratorName, next); @@ -80,10 +84,9 @@ public class HapiSequenceStyleGenerator public void configure(Type theType, Properties theParams, ServiceRegistry theServiceRegistry) throws MappingException { - // Instantiate the ID massager - // StorageSettings should only be null when running in the DDL generation maven plugin - if (myStorageSettings != null) { - myIdMassager = ReflectionUtil.newInstance(myStorageSettings.getSequenceValueMassagerClass()); + myIdMassager = theServiceRegistry.getService(ISequenceValueMassager.class); + if (myIdMassager == null) { + myIdMassager = new ISequenceValueMassager.NoopSequenceValueMassager(); } // Create a HAPI FHIR sequence style generator @@ -91,9 +94,10 @@ public class HapiSequenceStyleGenerator Validate.notBlank(myGeneratorName, "No generator name found"); Properties props = new Properties(theParams); - props.put(SequenceStyleGenerator.OPT_PARAM, StandardOptimizerDescriptor.POOLED.getExternalName()); - props.put(SequenceStyleGenerator.INITIAL_PARAM, "1"); - props.put(SequenceStyleGenerator.INCREMENT_PARAM, "50"); + props.put(OptimizableGenerator.OPT_PARAM, StandardOptimizerDescriptor.POOLED.getExternalName()); + props.put(OptimizableGenerator.INITIAL_PARAM, "1"); + props.put(OptimizableGenerator.INCREMENT_PARAM, "50"); + props.put(GENERATOR_NAME, myGeneratorName); myGen.configure(theType, props, theServiceRegistry); @@ -114,4 +118,9 @@ public class HapiSequenceStyleGenerator public boolean supportsJdbcBatchInserts() { return myGen.supportsJdbcBatchInserts(); } + + @Override + public Optimizer getOptimizer() { + return myGen.getOptimizer(); + } } diff --git a/hapi-fhir-jpaserver-model/src/main/java/ca/uhn/fhir/jpa/model/entity/AuditableBasePartitionable.java b/hapi-fhir-jpaserver-model/src/main/java/ca/uhn/fhir/jpa/model/entity/AuditableBasePartitionable.java index 473235d6e8d..5423b278373 100644 --- a/hapi-fhir-jpaserver-model/src/main/java/ca/uhn/fhir/jpa/model/entity/AuditableBasePartitionable.java +++ b/hapi-fhir-jpaserver-model/src/main/java/ca/uhn/fhir/jpa/model/entity/AuditableBasePartitionable.java @@ -20,14 +20,14 @@ package ca.uhn.fhir.jpa.model.entity; * #L% */ +import jakarta.annotation.Nullable; +import jakarta.persistence.Column; +import jakarta.persistence.Embedded; +import jakarta.persistence.MappedSuperclass; import org.hibernate.envers.Audited; import org.hibernate.envers.NotAudited; import java.io.Serializable; -import javax.annotation.Nullable; -import javax.persistence.Column; -import javax.persistence.Embedded; -import javax.persistence.MappedSuperclass; /** * This is a copy of (@link {@link BasePartitionable} used ONLY for entities that are audited by Hibernate Envers. diff --git a/hapi-fhir-jpaserver-model/src/main/java/ca/uhn/fhir/jpa/model/entity/BaseHasResource.java b/hapi-fhir-jpaserver-model/src/main/java/ca/uhn/fhir/jpa/model/entity/BaseHasResource.java index ad3205f2795..646742cac02 100644 --- a/hapi-fhir-jpaserver-model/src/main/java/ca/uhn/fhir/jpa/model/entity/BaseHasResource.java +++ b/hapi-fhir-jpaserver-model/src/main/java/ca/uhn/fhir/jpa/model/entity/BaseHasResource.java @@ -23,17 +23,17 @@ import ca.uhn.fhir.context.FhirVersionEnum; import ca.uhn.fhir.jpa.model.cross.IBasePersistedResource; import ca.uhn.fhir.jpa.model.dao.JpaPid; import ca.uhn.fhir.model.primitive.InstantDt; +import jakarta.persistence.Column; +import jakarta.persistence.EnumType; +import jakarta.persistence.Enumerated; +import jakarta.persistence.MappedSuperclass; +import jakarta.persistence.Temporal; +import jakarta.persistence.TemporalType; +import jakarta.persistence.Transient; import org.hibernate.annotations.OptimisticLock; import java.util.Collection; import java.util.Date; -import javax.persistence.Column; -import javax.persistence.EnumType; -import javax.persistence.Enumerated; -import javax.persistence.MappedSuperclass; -import javax.persistence.Temporal; -import javax.persistence.TemporalType; -import javax.persistence.Transient; @MappedSuperclass public abstract class BaseHasResource extends BasePartitionable @@ -70,6 +70,7 @@ public abstract class BaseHasResource extends BasePartitionable * after an update */ @Transient + // TODO MB forced_id delete this in step 3 private transient String myTransientForcedId; public String getTransientForcedId() { diff --git a/hapi-fhir-jpaserver-model/src/main/java/ca/uhn/fhir/jpa/model/entity/BasePartitionable.java b/hapi-fhir-jpaserver-model/src/main/java/ca/uhn/fhir/jpa/model/entity/BasePartitionable.java index 0c3fe7ceed0..939180a23b9 100644 --- a/hapi-fhir-jpaserver-model/src/main/java/ca/uhn/fhir/jpa/model/entity/BasePartitionable.java +++ b/hapi-fhir-jpaserver-model/src/main/java/ca/uhn/fhir/jpa/model/entity/BasePartitionable.java @@ -19,11 +19,12 @@ */ package ca.uhn.fhir.jpa.model.entity; +import jakarta.annotation.Nullable; +import jakarta.persistence.Column; +import jakarta.persistence.Embedded; +import jakarta.persistence.MappedSuperclass; + import java.io.Serializable; -import javax.annotation.Nullable; -import javax.persistence.Column; -import javax.persistence.Embedded; -import javax.persistence.MappedSuperclass; /** * This is the base class for entities with partitioning that does NOT include Hibernate Envers logging. diff --git a/hapi-fhir-jpaserver-model/src/main/java/ca/uhn/fhir/jpa/model/entity/BaseResourceIndex.java b/hapi-fhir-jpaserver-model/src/main/java/ca/uhn/fhir/jpa/model/entity/BaseResourceIndex.java index 9107f06862a..54a99aa9ccb 100644 --- a/hapi-fhir-jpaserver-model/src/main/java/ca/uhn/fhir/jpa/model/entity/BaseResourceIndex.java +++ b/hapi-fhir-jpaserver-model/src/main/java/ca/uhn/fhir/jpa/model/entity/BaseResourceIndex.java @@ -19,10 +19,10 @@ */ package ca.uhn.fhir.jpa.model.entity; +import jakarta.persistence.MappedSuperclass; import org.apache.commons.lang3.ObjectUtils; import java.io.Serializable; -import javax.persistence.MappedSuperclass; @MappedSuperclass public abstract class BaseResourceIndex extends BasePartitionable implements Serializable { diff --git a/hapi-fhir-jpaserver-model/src/main/java/ca/uhn/fhir/jpa/model/entity/BaseResourceIndexedSearchParam.java b/hapi-fhir-jpaserver-model/src/main/java/ca/uhn/fhir/jpa/model/entity/BaseResourceIndexedSearchParam.java index bdb31c75c8d..edcd363081a 100644 --- a/hapi-fhir-jpaserver-model/src/main/java/ca/uhn/fhir/jpa/model/entity/BaseResourceIndexedSearchParam.java +++ b/hapi-fhir-jpaserver-model/src/main/java/ca/uhn/fhir/jpa/model/entity/BaseResourceIndexedSearchParam.java @@ -31,17 +31,17 @@ import com.google.common.hash.HashCode; import com.google.common.hash.HashFunction; import com.google.common.hash.Hasher; import com.google.common.hash.Hashing; +import jakarta.persistence.Column; +import jakarta.persistence.MappedSuperclass; +import jakarta.persistence.Temporal; +import jakarta.persistence.TemporalType; +import jakarta.persistence.Transient; import org.apache.commons.lang3.StringUtils; import org.hibernate.search.mapper.pojo.mapping.definition.annotation.FullTextField; import org.hibernate.search.mapper.pojo.mapping.definition.annotation.GenericField; import java.util.Date; import java.util.List; -import javax.persistence.Column; -import javax.persistence.MappedSuperclass; -import javax.persistence.Temporal; -import javax.persistence.TemporalType; -import javax.persistence.Transient; @MappedSuperclass public abstract class BaseResourceIndexedSearchParam extends BaseResourceIndex { diff --git a/hapi-fhir-jpaserver-model/src/main/java/ca/uhn/fhir/jpa/model/entity/BaseResourceIndexedSearchParamQuantity.java b/hapi-fhir-jpaserver-model/src/main/java/ca/uhn/fhir/jpa/model/entity/BaseResourceIndexedSearchParamQuantity.java index ac1d27332b0..3850a31c010 100644 --- a/hapi-fhir-jpaserver-model/src/main/java/ca/uhn/fhir/jpa/model/entity/BaseResourceIndexedSearchParamQuantity.java +++ b/hapi-fhir-jpaserver-model/src/main/java/ca/uhn/fhir/jpa/model/entity/BaseResourceIndexedSearchParamQuantity.java @@ -21,12 +21,11 @@ package ca.uhn.fhir.jpa.model.entity; import ca.uhn.fhir.interceptor.model.RequestPartitionId; import ca.uhn.fhir.jpa.model.config.PartitionSettings; +import jakarta.persistence.Column; +import jakarta.persistence.MappedSuperclass; import org.apache.commons.lang3.builder.HashCodeBuilder; import org.hibernate.search.mapper.pojo.mapping.definition.annotation.FullTextField; -import javax.persistence.Column; -import javax.persistence.MappedSuperclass; - @MappedSuperclass public abstract class BaseResourceIndexedSearchParamQuantity extends BaseResourceIndexedSearchParam { diff --git a/hapi-fhir-jpaserver-model/src/main/java/ca/uhn/fhir/jpa/model/entity/BaseTag.java b/hapi-fhir-jpaserver-model/src/main/java/ca/uhn/fhir/jpa/model/entity/BaseTag.java index bc4405d2d1a..27db242c506 100644 --- a/hapi-fhir-jpaserver-model/src/main/java/ca/uhn/fhir/jpa/model/entity/BaseTag.java +++ b/hapi-fhir-jpaserver-model/src/main/java/ca/uhn/fhir/jpa/model/entity/BaseTag.java @@ -19,11 +19,12 @@ */ package ca.uhn.fhir.jpa.model.entity; +import jakarta.persistence.Column; +import jakarta.persistence.JoinColumn; +import jakarta.persistence.ManyToOne; +import jakarta.persistence.MappedSuperclass; + import java.io.Serializable; -import javax.persistence.Column; -import javax.persistence.JoinColumn; -import javax.persistence.ManyToOne; -import javax.persistence.MappedSuperclass; @MappedSuperclass public abstract class BaseTag extends BasePartitionable implements Serializable { diff --git a/hapi-fhir-jpaserver-model/src/main/java/ca/uhn/fhir/jpa/model/entity/BinaryStorageEntity.java b/hapi-fhir-jpaserver-model/src/main/java/ca/uhn/fhir/jpa/model/entity/BinaryStorageEntity.java index d44d8e446f5..f2f9742b607 100644 --- a/hapi-fhir-jpaserver-model/src/main/java/ca/uhn/fhir/jpa/model/entity/BinaryStorageEntity.java +++ b/hapi-fhir-jpaserver-model/src/main/java/ca/uhn/fhir/jpa/model/entity/BinaryStorageEntity.java @@ -19,15 +19,16 @@ */ package ca.uhn.fhir.jpa.model.entity; +import jakarta.persistence.Column; +import jakarta.persistence.Entity; +import jakarta.persistence.Id; +import jakarta.persistence.Lob; +import jakarta.persistence.Table; +import jakarta.persistence.Temporal; +import jakarta.persistence.TemporalType; + import java.sql.Blob; import java.util.Date; -import javax.persistence.Column; -import javax.persistence.Entity; -import javax.persistence.Id; -import javax.persistence.Lob; -import javax.persistence.Table; -import javax.persistence.Temporal; -import javax.persistence.TemporalType; @Entity @Table(name = "HFJ_BINARY_STORAGE_BLOB") diff --git a/hapi-fhir-jpaserver-model/src/main/java/ca/uhn/fhir/jpa/model/entity/ForcedId.java b/hapi-fhir-jpaserver-model/src/main/java/ca/uhn/fhir/jpa/model/entity/ForcedId.java index 7046e8c5c3e..c77c5bf87ba 100644 --- a/hapi-fhir-jpaserver-model/src/main/java/ca/uhn/fhir/jpa/model/entity/ForcedId.java +++ b/hapi-fhir-jpaserver-model/src/main/java/ca/uhn/fhir/jpa/model/entity/ForcedId.java @@ -19,24 +19,23 @@ */ package ca.uhn.fhir.jpa.model.entity; +import jakarta.persistence.Column; +import jakarta.persistence.Entity; +import jakarta.persistence.FetchType; +import jakarta.persistence.ForeignKey; +import jakarta.persistence.GeneratedValue; +import jakarta.persistence.GenerationType; +import jakarta.persistence.Id; +import jakarta.persistence.Index; +import jakarta.persistence.JoinColumn; +import jakarta.persistence.OneToOne; +import jakarta.persistence.SequenceGenerator; +import jakarta.persistence.Table; +import jakarta.persistence.UniqueConstraint; import org.apache.commons.lang3.builder.ToStringBuilder; import org.apache.commons.lang3.builder.ToStringStyle; import org.hibernate.annotations.ColumnDefault; -import javax.persistence.Column; -import javax.persistence.Entity; -import javax.persistence.FetchType; -import javax.persistence.ForeignKey; -import javax.persistence.GeneratedValue; -import javax.persistence.GenerationType; -import javax.persistence.Id; -import javax.persistence.Index; -import javax.persistence.JoinColumn; -import javax.persistence.OneToOne; -import javax.persistence.SequenceGenerator; -import javax.persistence.Table; -import javax.persistence.UniqueConstraint; - @Entity() @Table( name = ForcedId.HFJ_FORCED_ID, @@ -44,6 +43,7 @@ import javax.persistence.UniqueConstraint; @UniqueConstraint( name = "IDX_FORCEDID_RESID", columnNames = {"RESOURCE_PID"}), + /* * This index is called IDX_FORCEDID_TYPE_FID and guarantees * uniqueness of RESOURCE_TYPE,FORCED_ID. This doesn't make sense @@ -64,7 +64,6 @@ import javax.persistence.UniqueConstraint; */ @Index(name = "IDX_FORCEID_FID", columnList = "FORCED_ID"), // @Index(name = "IDX_FORCEID_RESID", columnList = "RESOURCE_PID"), - // TODO GGG potentiall add a type + res_id index here, specifically for deletion? }) public class ForcedId extends BasePartitionable { diff --git a/hapi-fhir-jpaserver-model/src/main/java/ca/uhn/fhir/jpa/model/entity/IBaseResourceEntity.java b/hapi-fhir-jpaserver-model/src/main/java/ca/uhn/fhir/jpa/model/entity/IBaseResourceEntity.java index 2da32bd3661..1e61691d8f3 100644 --- a/hapi-fhir-jpaserver-model/src/main/java/ca/uhn/fhir/jpa/model/entity/IBaseResourceEntity.java +++ b/hapi-fhir-jpaserver-model/src/main/java/ca/uhn/fhir/jpa/model/entity/IBaseResourceEntity.java @@ -22,9 +22,9 @@ package ca.uhn.fhir.jpa.model.entity; import ca.uhn.fhir.context.FhirVersionEnum; import ca.uhn.fhir.model.primitive.IdDt; import ca.uhn.fhir.model.primitive.InstantDt; +import jakarta.annotation.Nullable; import java.util.Date; -import javax.annotation.Nullable; public interface IBaseResourceEntity { diff --git a/hapi-fhir-jpaserver-model/src/main/java/ca/uhn/fhir/jpa/model/entity/NpmPackageEntity.java b/hapi-fhir-jpaserver-model/src/main/java/ca/uhn/fhir/jpa/model/entity/NpmPackageEntity.java index 0c637ff84d5..acc24f6db0f 100644 --- a/hapi-fhir-jpaserver-model/src/main/java/ca/uhn/fhir/jpa/model/entity/NpmPackageEntity.java +++ b/hapi-fhir-jpaserver-model/src/main/java/ca/uhn/fhir/jpa/model/entity/NpmPackageEntity.java @@ -19,23 +19,23 @@ */ package ca.uhn.fhir.jpa.model.entity; +import jakarta.persistence.Column; +import jakarta.persistence.Entity; +import jakarta.persistence.GeneratedValue; +import jakarta.persistence.GenerationType; +import jakarta.persistence.Id; +import jakarta.persistence.OneToMany; +import jakarta.persistence.SequenceGenerator; +import jakarta.persistence.Table; +import jakarta.persistence.Temporal; +import jakarta.persistence.TemporalType; +import jakarta.persistence.UniqueConstraint; +import jakarta.persistence.Version; import org.apache.commons.lang3.builder.EqualsBuilder; import org.apache.commons.lang3.builder.HashCodeBuilder; import java.util.Date; import java.util.List; -import javax.persistence.Column; -import javax.persistence.Entity; -import javax.persistence.GeneratedValue; -import javax.persistence.GenerationType; -import javax.persistence.Id; -import javax.persistence.OneToMany; -import javax.persistence.SequenceGenerator; -import javax.persistence.Table; -import javax.persistence.Temporal; -import javax.persistence.TemporalType; -import javax.persistence.UniqueConstraint; -import javax.persistence.Version; @Entity() @Table( diff --git a/hapi-fhir-jpaserver-model/src/main/java/ca/uhn/fhir/jpa/model/entity/NpmPackageVersionEntity.java b/hapi-fhir-jpaserver-model/src/main/java/ca/uhn/fhir/jpa/model/entity/NpmPackageVersionEntity.java index 00eee555884..a102576c0f9 100644 --- a/hapi-fhir-jpaserver-model/src/main/java/ca/uhn/fhir/jpa/model/entity/NpmPackageVersionEntity.java +++ b/hapi-fhir-jpaserver-model/src/main/java/ca/uhn/fhir/jpa/model/entity/NpmPackageVersionEntity.java @@ -21,29 +21,28 @@ package ca.uhn.fhir.jpa.model.entity; import ca.uhn.fhir.context.FhirVersionEnum; import ca.uhn.fhir.util.StringUtil; +import jakarta.persistence.Column; +import jakarta.persistence.Entity; +import jakarta.persistence.EnumType; +import jakarta.persistence.Enumerated; +import jakarta.persistence.ForeignKey; +import jakarta.persistence.GeneratedValue; +import jakarta.persistence.GenerationType; +import jakarta.persistence.Id; +import jakarta.persistence.Index; +import jakarta.persistence.JoinColumn; +import jakarta.persistence.ManyToOne; +import jakarta.persistence.OneToMany; +import jakarta.persistence.SequenceGenerator; +import jakarta.persistence.Table; +import jakarta.persistence.Temporal; +import jakarta.persistence.TemporalType; +import jakarta.persistence.Version; import org.apache.commons.lang3.builder.ToStringBuilder; import org.apache.commons.lang3.builder.ToStringStyle; import java.util.Date; import java.util.List; -import javax.persistence.Column; -import javax.persistence.Entity; -import javax.persistence.EnumType; -import javax.persistence.Enumerated; -import javax.persistence.ForeignKey; -import javax.persistence.GeneratedValue; -import javax.persistence.GenerationType; -import javax.persistence.Id; -import javax.persistence.Index; -import javax.persistence.JoinColumn; -import javax.persistence.ManyToOne; -import javax.persistence.OneToMany; -import javax.persistence.OneToOne; -import javax.persistence.SequenceGenerator; -import javax.persistence.Table; -import javax.persistence.Temporal; -import javax.persistence.TemporalType; -import javax.persistence.Version; @Entity() @Table( @@ -77,7 +76,7 @@ public class NpmPackageVersionEntity { @JoinColumn(name = "PACKAGE_PID", nullable = false, foreignKey = @ForeignKey(name = "FK_NPM_PKV_PKG")) private NpmPackageEntity myPackage; - @OneToOne + @ManyToOne @JoinColumn( name = "BINARY_RES_ID", referencedColumnName = "RES_ID", diff --git a/hapi-fhir-jpaserver-model/src/main/java/ca/uhn/fhir/jpa/model/entity/NpmPackageVersionResourceEntity.java b/hapi-fhir-jpaserver-model/src/main/java/ca/uhn/fhir/jpa/model/entity/NpmPackageVersionResourceEntity.java index d9d9f0d8b5a..04428a7ceda 100644 --- a/hapi-fhir-jpaserver-model/src/main/java/ca/uhn/fhir/jpa/model/entity/NpmPackageVersionResourceEntity.java +++ b/hapi-fhir-jpaserver-model/src/main/java/ca/uhn/fhir/jpa/model/entity/NpmPackageVersionResourceEntity.java @@ -20,32 +20,30 @@ package ca.uhn.fhir.jpa.model.entity; import ca.uhn.fhir.context.FhirVersionEnum; +import jakarta.persistence.Column; +import jakarta.persistence.Entity; +import jakarta.persistence.EnumType; +import jakarta.persistence.Enumerated; +import jakarta.persistence.ForeignKey; +import jakarta.persistence.GeneratedValue; +import jakarta.persistence.GenerationType; +import jakarta.persistence.Id; +import jakarta.persistence.Index; +import jakarta.persistence.JoinColumn; +import jakarta.persistence.ManyToOne; +import jakarta.persistence.SequenceGenerator; +import jakarta.persistence.Table; +import jakarta.persistence.Temporal; +import jakarta.persistence.TemporalType; +import jakarta.persistence.Version; import org.apache.commons.lang3.builder.ToStringBuilder; import org.apache.commons.lang3.builder.ToStringStyle; import java.util.Date; -import javax.persistence.Column; -import javax.persistence.Entity; -import javax.persistence.EnumType; -import javax.persistence.Enumerated; -import javax.persistence.ForeignKey; -import javax.persistence.GeneratedValue; -import javax.persistence.GenerationType; -import javax.persistence.Id; -import javax.persistence.Index; -import javax.persistence.JoinColumn; -import javax.persistence.ManyToOne; -import javax.persistence.OneToOne; -import javax.persistence.SequenceGenerator; -import javax.persistence.Table; -import javax.persistence.Temporal; -import javax.persistence.TemporalType; -import javax.persistence.Version; @Entity() @Table( name = "NPM_PACKAGE_VER_RES", - uniqueConstraints = {}, indexes = { @Index(name = "IDX_PACKVERRES_URL", columnList = "CANONICAL_URL"), @Index(name = "FK_NPM_PACKVERRES_PACKVER", columnList = "PACKVER_PID"), @@ -67,7 +65,7 @@ public class NpmPackageVersionResourceEntity { nullable = false) private NpmPackageVersionEntity myPackageVersion; - @OneToOne + @ManyToOne @JoinColumn( name = "BINARY_RES_ID", referencedColumnName = "RES_ID", diff --git a/hapi-fhir-jpaserver-model/src/main/java/ca/uhn/fhir/jpa/model/entity/PartitionablePartitionId.java b/hapi-fhir-jpaserver-model/src/main/java/ca/uhn/fhir/jpa/model/entity/PartitionablePartitionId.java index 30c8660c1f7..88a786410b1 100644 --- a/hapi-fhir-jpaserver-model/src/main/java/ca/uhn/fhir/jpa/model/entity/PartitionablePartitionId.java +++ b/hapi-fhir-jpaserver-model/src/main/java/ca/uhn/fhir/jpa/model/entity/PartitionablePartitionId.java @@ -21,14 +21,14 @@ package ca.uhn.fhir.jpa.model.entity; import ca.uhn.fhir.interceptor.model.RequestPartitionId; import ca.uhn.fhir.jpa.model.config.PartitionSettings; +import jakarta.annotation.Nonnull; +import jakarta.annotation.Nullable; +import jakarta.persistence.Column; +import jakarta.persistence.Embeddable; import org.apache.commons.lang3.builder.EqualsBuilder; import org.apache.commons.lang3.builder.HashCodeBuilder; import java.time.LocalDate; -import javax.annotation.Nonnull; -import javax.annotation.Nullable; -import javax.persistence.Column; -import javax.persistence.Embeddable; @Embeddable public class PartitionablePartitionId implements Cloneable { diff --git a/hapi-fhir-jpaserver-model/src/main/java/ca/uhn/fhir/jpa/model/entity/PersistedResourceModifiedMessageEntityPK.java b/hapi-fhir-jpaserver-model/src/main/java/ca/uhn/fhir/jpa/model/entity/PersistedResourceModifiedMessageEntityPK.java index 4cf7fa42516..a0fbf87d22a 100644 --- a/hapi-fhir-jpaserver-model/src/main/java/ca/uhn/fhir/jpa/model/entity/PersistedResourceModifiedMessageEntityPK.java +++ b/hapi-fhir-jpaserver-model/src/main/java/ca/uhn/fhir/jpa/model/entity/PersistedResourceModifiedMessageEntityPK.java @@ -20,10 +20,11 @@ package ca.uhn.fhir.jpa.model.entity; * #L% */ +import jakarta.persistence.Column; +import jakarta.persistence.Embeddable; + import java.io.Serializable; import java.util.Objects; -import javax.persistence.Column; -import javax.persistence.Embeddable; @Embeddable public class PersistedResourceModifiedMessageEntityPK implements IPersistedResourceModifiedMessagePK, Serializable { diff --git a/hapi-fhir-jpaserver-model/src/main/java/ca/uhn/fhir/jpa/model/entity/ResourceHistoryProvenanceEntity.java b/hapi-fhir-jpaserver-model/src/main/java/ca/uhn/fhir/jpa/model/entity/ResourceHistoryProvenanceEntity.java index 754ab9b613c..66fd55a9a85 100644 --- a/hapi-fhir-jpaserver-model/src/main/java/ca/uhn/fhir/jpa/model/entity/ResourceHistoryProvenanceEntity.java +++ b/hapi-fhir-jpaserver-model/src/main/java/ca/uhn/fhir/jpa/model/entity/ResourceHistoryProvenanceEntity.java @@ -20,21 +20,20 @@ package ca.uhn.fhir.jpa.model.entity; import ca.uhn.fhir.rest.api.Constants; +import jakarta.persistence.Column; +import jakarta.persistence.Entity; +import jakarta.persistence.FetchType; +import jakarta.persistence.ForeignKey; +import jakarta.persistence.Id; +import jakarta.persistence.Index; +import jakarta.persistence.JoinColumn; +import jakarta.persistence.ManyToOne; +import jakarta.persistence.MapsId; +import jakarta.persistence.OneToOne; +import jakarta.persistence.Table; import org.apache.commons.lang3.builder.ToStringBuilder; import org.apache.commons.lang3.builder.ToStringStyle; -import javax.persistence.Column; -import javax.persistence.Entity; -import javax.persistence.FetchType; -import javax.persistence.ForeignKey; -import javax.persistence.Id; -import javax.persistence.Index; -import javax.persistence.JoinColumn; -import javax.persistence.ManyToOne; -import javax.persistence.MapsId; -import javax.persistence.OneToOne; -import javax.persistence.Table; - import static ca.uhn.fhir.jpa.model.entity.ResourceHistoryTable.SOURCE_URI_LENGTH; @Table( diff --git a/hapi-fhir-jpaserver-model/src/main/java/ca/uhn/fhir/jpa/model/entity/ResourceHistoryTable.java b/hapi-fhir-jpaserver-model/src/main/java/ca/uhn/fhir/jpa/model/entity/ResourceHistoryTable.java index 784ad91ae5f..0d1e0f8adaa 100644 --- a/hapi-fhir-jpaserver-model/src/main/java/ca/uhn/fhir/jpa/model/entity/ResourceHistoryTable.java +++ b/hapi-fhir-jpaserver-model/src/main/java/ca/uhn/fhir/jpa/model/entity/ResourceHistoryTable.java @@ -20,17 +20,19 @@ package ca.uhn.fhir.jpa.model.entity; import ca.uhn.fhir.jpa.model.dao.JpaPid; -import ca.uhn.fhir.jpa.model.util.JpaConstants; import ca.uhn.fhir.model.primitive.IdDt; import ca.uhn.fhir.rest.api.Constants; +import jakarta.persistence.*; import org.apache.commons.lang3.builder.ToStringBuilder; import org.apache.commons.lang3.builder.ToStringStyle; +import org.hibernate.Length; import org.hibernate.annotations.OptimisticLock; import java.io.Serializable; import java.util.ArrayList; import java.util.Collection; -import javax.persistence.*; + +import static org.apache.commons.lang3.StringUtils.defaultString; @Entity @Table( @@ -56,7 +58,6 @@ public class ResourceHistoryTable extends BaseHasResource implements Serializabl public static final int ENCODING_COL_LENGTH = 5; public static final String HFJ_RES_VER = "HFJ_RES_VER"; - public static final int RES_TEXT_VC_MAX_LENGTH = 4000; private static final long serialVersionUID = 1L; @Id @@ -85,13 +86,15 @@ public class ResourceHistoryTable extends BaseHasResource implements Serializabl @OneToMany(mappedBy = "myResourceHistory", cascade = CascadeType.ALL, fetch = FetchType.LAZY, orphanRemoval = true) private Collection myTags; + /** + * Note: No setter for this field because it's only a legacy way of storing data now. + */ @Column(name = "RES_TEXT", length = Integer.MAX_VALUE - 1, nullable = true) @Lob() @OptimisticLock(excluded = true) private byte[] myResource; - @Column(name = "RES_TEXT_VC", length = RES_TEXT_VC_MAX_LENGTH, nullable = true) - @org.hibernate.annotations.Type(type = JpaConstants.ORG_HIBERNATE_TYPE_TEXT_TYPE) + @Column(name = "RES_TEXT_VC", nullable = true, length = Length.LONG32) @OptimisticLock(excluded = true) private String myResourceTextVc; @@ -152,7 +155,8 @@ public class ResourceHistoryTable extends BaseHasResource implements Serializabl } public void setResourceTextVc(String theResourceTextVc) { - myResourceTextVc = theResourceTextVc; + myResource = null; + myResourceTextVc = defaultString(theResourceTextVc); } public ResourceHistoryProvenanceEntity getProvenance() { @@ -208,10 +212,6 @@ public class ResourceHistoryTable extends BaseHasResource implements Serializabl return myResource; } - public void setResource(byte[] theResource) { - myResource = theResource; - } - @Override public Long getResourceId() { return myResourceId; @@ -277,12 +277,7 @@ public class ResourceHistoryTable extends BaseHasResource implements Serializabl if (getTransientForcedId() != null) { resourceIdPart = getTransientForcedId(); } else { - if (getResourceTable().getForcedId() == null) { - Long id = getResourceId(); - resourceIdPart = id.toString(); - } else { - resourceIdPart = getResourceTable().getForcedId().getForcedId(); - } + resourceIdPart = getResourceTable().getFhirId(); } return new IdDt(getResourceType() + '/' + resourceIdPart + '/' + Constants.PARAM_HISTORY + '/' + getVersion()); } diff --git a/hapi-fhir-jpaserver-model/src/main/java/ca/uhn/fhir/jpa/model/entity/ResourceHistoryTag.java b/hapi-fhir-jpaserver-model/src/main/java/ca/uhn/fhir/jpa/model/entity/ResourceHistoryTag.java index 21eb200e8aa..d77cf7c5538 100644 --- a/hapi-fhir-jpaserver-model/src/main/java/ca/uhn/fhir/jpa/model/entity/ResourceHistoryTag.java +++ b/hapi-fhir-jpaserver-model/src/main/java/ca/uhn/fhir/jpa/model/entity/ResourceHistoryTag.java @@ -19,20 +19,21 @@ */ package ca.uhn.fhir.jpa.model.entity; +import jakarta.persistence.Column; +import jakarta.persistence.Embeddable; +import jakarta.persistence.Entity; +import jakarta.persistence.ForeignKey; +import jakarta.persistence.GeneratedValue; +import jakarta.persistence.GenerationType; +import jakarta.persistence.Id; +import jakarta.persistence.Index; +import jakarta.persistence.JoinColumn; +import jakarta.persistence.ManyToOne; +import jakarta.persistence.SequenceGenerator; +import jakarta.persistence.Table; +import jakarta.persistence.UniqueConstraint; + import java.io.Serializable; -import javax.persistence.Column; -import javax.persistence.Embeddable; -import javax.persistence.Entity; -import javax.persistence.ForeignKey; -import javax.persistence.GeneratedValue; -import javax.persistence.GenerationType; -import javax.persistence.Id; -import javax.persistence.Index; -import javax.persistence.JoinColumn; -import javax.persistence.ManyToOne; -import javax.persistence.SequenceGenerator; -import javax.persistence.Table; -import javax.persistence.UniqueConstraint; @Embeddable @Entity diff --git a/hapi-fhir-jpaserver-model/src/main/java/ca/uhn/fhir/jpa/model/entity/ResourceIndexedComboStringUnique.java b/hapi-fhir-jpaserver-model/src/main/java/ca/uhn/fhir/jpa/model/entity/ResourceIndexedComboStringUnique.java index 1b386c23afb..898efaba2bc 100644 --- a/hapi-fhir-jpaserver-model/src/main/java/ca/uhn/fhir/jpa/model/entity/ResourceIndexedComboStringUnique.java +++ b/hapi-fhir-jpaserver-model/src/main/java/ca/uhn/fhir/jpa/model/entity/ResourceIndexedComboStringUnique.java @@ -19,6 +19,7 @@ */ package ca.uhn.fhir.jpa.model.entity; +import jakarta.persistence.*; import org.apache.commons.lang3.Validate; import org.apache.commons.lang3.builder.CompareToBuilder; import org.apache.commons.lang3.builder.EqualsBuilder; @@ -27,8 +28,6 @@ import org.apache.commons.lang3.builder.ToStringBuilder; import org.apache.commons.lang3.builder.ToStringStyle; import org.hl7.fhir.instance.model.api.IIdType; -import javax.persistence.*; - @Entity() @Table( name = "HFJ_IDX_CMP_STRING_UNIQ", diff --git a/hapi-fhir-jpaserver-model/src/main/java/ca/uhn/fhir/jpa/model/entity/ResourceIndexedComboTokenNonUnique.java b/hapi-fhir-jpaserver-model/src/main/java/ca/uhn/fhir/jpa/model/entity/ResourceIndexedComboTokenNonUnique.java index d4f96c2f7d0..75d6947c870 100644 --- a/hapi-fhir-jpaserver-model/src/main/java/ca/uhn/fhir/jpa/model/entity/ResourceIndexedComboTokenNonUnique.java +++ b/hapi-fhir-jpaserver-model/src/main/java/ca/uhn/fhir/jpa/model/entity/ResourceIndexedComboTokenNonUnique.java @@ -21,25 +21,24 @@ package ca.uhn.fhir.jpa.model.entity; import ca.uhn.fhir.interceptor.model.RequestPartitionId; import ca.uhn.fhir.jpa.model.config.PartitionSettings; +import jakarta.persistence.Column; +import jakarta.persistence.Entity; +import jakarta.persistence.ForeignKey; +import jakarta.persistence.GeneratedValue; +import jakarta.persistence.GenerationType; +import jakarta.persistence.Id; +import jakarta.persistence.Index; +import jakarta.persistence.JoinColumn; +import jakarta.persistence.ManyToOne; +import jakarta.persistence.SequenceGenerator; +import jakarta.persistence.Table; +import jakarta.persistence.Transient; import org.apache.commons.lang3.builder.CompareToBuilder; import org.apache.commons.lang3.builder.EqualsBuilder; import org.apache.commons.lang3.builder.HashCodeBuilder; import org.apache.commons.lang3.builder.ToStringBuilder; import org.hl7.fhir.instance.model.api.IIdType; -import javax.persistence.Column; -import javax.persistence.Entity; -import javax.persistence.ForeignKey; -import javax.persistence.GeneratedValue; -import javax.persistence.GenerationType; -import javax.persistence.Id; -import javax.persistence.Index; -import javax.persistence.JoinColumn; -import javax.persistence.ManyToOne; -import javax.persistence.SequenceGenerator; -import javax.persistence.Table; -import javax.persistence.Transient; - import static ca.uhn.fhir.jpa.model.entity.BaseResourceIndexedSearchParam.hash; @Entity diff --git a/hapi-fhir-jpaserver-model/src/main/java/ca/uhn/fhir/jpa/model/entity/ResourceIndexedSearchParamCoords.java b/hapi-fhir-jpaserver-model/src/main/java/ca/uhn/fhir/jpa/model/entity/ResourceIndexedSearchParamCoords.java index dbad03ccc81..505b36de92e 100644 --- a/hapi-fhir-jpaserver-model/src/main/java/ca/uhn/fhir/jpa/model/entity/ResourceIndexedSearchParamCoords.java +++ b/hapi-fhir-jpaserver-model/src/main/java/ca/uhn/fhir/jpa/model/entity/ResourceIndexedSearchParamCoords.java @@ -21,25 +21,24 @@ package ca.uhn.fhir.jpa.model.entity; import ca.uhn.fhir.jpa.model.config.PartitionSettings; import ca.uhn.fhir.model.api.IQueryParameterType; +import jakarta.persistence.Column; +import jakarta.persistence.Embeddable; +import jakarta.persistence.Entity; +import jakarta.persistence.FetchType; +import jakarta.persistence.ForeignKey; +import jakarta.persistence.GeneratedValue; +import jakarta.persistence.GenerationType; +import jakarta.persistence.Id; +import jakarta.persistence.Index; +import jakarta.persistence.JoinColumn; +import jakarta.persistence.ManyToOne; +import jakarta.persistence.SequenceGenerator; +import jakarta.persistence.Table; import org.apache.commons.lang3.builder.EqualsBuilder; import org.apache.commons.lang3.builder.HashCodeBuilder; import org.apache.commons.lang3.builder.ToStringBuilder; import org.apache.commons.lang3.builder.ToStringStyle; -import javax.persistence.Column; -import javax.persistence.Embeddable; -import javax.persistence.Entity; -import javax.persistence.FetchType; -import javax.persistence.ForeignKey; -import javax.persistence.GeneratedValue; -import javax.persistence.GenerationType; -import javax.persistence.Id; -import javax.persistence.Index; -import javax.persistence.JoinColumn; -import javax.persistence.ManyToOne; -import javax.persistence.SequenceGenerator; -import javax.persistence.Table; - @Embeddable @Entity @Table( diff --git a/hapi-fhir-jpaserver-model/src/main/java/ca/uhn/fhir/jpa/model/entity/ResourceIndexedSearchParamDate.java b/hapi-fhir-jpaserver-model/src/main/java/ca/uhn/fhir/jpa/model/entity/ResourceIndexedSearchParamDate.java index 01fcab6f275..4afac4201df 100644 --- a/hapi-fhir-jpaserver-model/src/main/java/ca/uhn/fhir/jpa/model/entity/ResourceIndexedSearchParamDate.java +++ b/hapi-fhir-jpaserver-model/src/main/java/ca/uhn/fhir/jpa/model/entity/ResourceIndexedSearchParamDate.java @@ -26,6 +26,22 @@ import ca.uhn.fhir.model.primitive.InstantDt; import ca.uhn.fhir.rest.param.DateParam; import ca.uhn.fhir.rest.param.DateRangeParam; import ca.uhn.fhir.util.DateUtils; +import jakarta.persistence.Column; +import jakarta.persistence.Embeddable; +import jakarta.persistence.Entity; +import jakarta.persistence.FetchType; +import jakarta.persistence.ForeignKey; +import jakarta.persistence.GeneratedValue; +import jakarta.persistence.GenerationType; +import jakarta.persistence.Id; +import jakarta.persistence.Index; +import jakarta.persistence.JoinColumn; +import jakarta.persistence.ManyToOne; +import jakarta.persistence.SequenceGenerator; +import jakarta.persistence.Table; +import jakarta.persistence.Temporal; +import jakarta.persistence.TemporalType; +import jakarta.persistence.Transient; import org.apache.commons.lang3.StringUtils; import org.apache.commons.lang3.builder.EqualsBuilder; import org.apache.commons.lang3.builder.HashCodeBuilder; @@ -37,22 +53,6 @@ import org.hl7.fhir.r4.model.DateTimeType; import java.text.ParseException; import java.text.SimpleDateFormat; import java.util.Date; -import javax.persistence.Column; -import javax.persistence.Embeddable; -import javax.persistence.Entity; -import javax.persistence.FetchType; -import javax.persistence.ForeignKey; -import javax.persistence.GeneratedValue; -import javax.persistence.GenerationType; -import javax.persistence.Id; -import javax.persistence.Index; -import javax.persistence.JoinColumn; -import javax.persistence.ManyToOne; -import javax.persistence.SequenceGenerator; -import javax.persistence.Table; -import javax.persistence.Temporal; -import javax.persistence.TemporalType; -import javax.persistence.Transient; @Embeddable @Entity diff --git a/hapi-fhir-jpaserver-model/src/main/java/ca/uhn/fhir/jpa/model/entity/ResourceIndexedSearchParamNumber.java b/hapi-fhir-jpaserver-model/src/main/java/ca/uhn/fhir/jpa/model/entity/ResourceIndexedSearchParamNumber.java index e98508339ce..60c327ab767 100644 --- a/hapi-fhir-jpaserver-model/src/main/java/ca/uhn/fhir/jpa/model/entity/ResourceIndexedSearchParamNumber.java +++ b/hapi-fhir-jpaserver-model/src/main/java/ca/uhn/fhir/jpa/model/entity/ResourceIndexedSearchParamNumber.java @@ -22,27 +22,29 @@ package ca.uhn.fhir.jpa.model.entity; import ca.uhn.fhir.jpa.model.config.PartitionSettings; import ca.uhn.fhir.model.api.IQueryParameterType; import ca.uhn.fhir.rest.param.NumberParam; +import jakarta.persistence.Column; +import jakarta.persistence.Embeddable; +import jakarta.persistence.Entity; +import jakarta.persistence.FetchType; +import jakarta.persistence.ForeignKey; +import jakarta.persistence.GeneratedValue; +import jakarta.persistence.GenerationType; +import jakarta.persistence.Id; +import jakarta.persistence.Index; +import jakarta.persistence.JoinColumn; +import jakarta.persistence.ManyToOne; +import jakarta.persistence.SequenceGenerator; +import jakarta.persistence.Table; import org.apache.commons.lang3.builder.EqualsBuilder; import org.apache.commons.lang3.builder.HashCodeBuilder; import org.apache.commons.lang3.builder.ToStringBuilder; import org.apache.commons.lang3.builder.ToStringStyle; +import org.hibernate.annotations.JdbcTypeCode; import org.hibernate.search.mapper.pojo.mapping.definition.annotation.ScaledNumberField; +import org.hibernate.type.SqlTypes; import java.math.BigDecimal; import java.util.Objects; -import javax.persistence.Column; -import javax.persistence.Embeddable; -import javax.persistence.Entity; -import javax.persistence.FetchType; -import javax.persistence.ForeignKey; -import javax.persistence.GeneratedValue; -import javax.persistence.GenerationType; -import javax.persistence.Id; -import javax.persistence.Index; -import javax.persistence.JoinColumn; -import javax.persistence.ManyToOne; -import javax.persistence.SequenceGenerator; -import javax.persistence.Table; @Embeddable @Entity @@ -57,8 +59,9 @@ public class ResourceIndexedSearchParamNumber extends BaseResourceIndexedSearchP private static final long serialVersionUID = 1L; - @Column(name = "SP_VALUE", nullable = true) + @Column(name = "SP_VALUE", nullable = true, precision = 19, scale = 2) @ScaledNumberField + @JdbcTypeCode(SqlTypes.DECIMAL) public BigDecimal myValue; @Id diff --git a/hapi-fhir-jpaserver-model/src/main/java/ca/uhn/fhir/jpa/model/entity/ResourceIndexedSearchParamQuantity.java b/hapi-fhir-jpaserver-model/src/main/java/ca/uhn/fhir/jpa/model/entity/ResourceIndexedSearchParamQuantity.java index 80841720449..7b988a19224 100644 --- a/hapi-fhir-jpaserver-model/src/main/java/ca/uhn/fhir/jpa/model/entity/ResourceIndexedSearchParamQuantity.java +++ b/hapi-fhir-jpaserver-model/src/main/java/ca/uhn/fhir/jpa/model/entity/ResourceIndexedSearchParamQuantity.java @@ -22,6 +22,19 @@ package ca.uhn.fhir.jpa.model.entity; import ca.uhn.fhir.jpa.model.config.PartitionSettings; import ca.uhn.fhir.model.api.IQueryParameterType; import ca.uhn.fhir.rest.param.QuantityParam; +import jakarta.persistence.Column; +import jakarta.persistence.Embeddable; +import jakarta.persistence.Entity; +import jakarta.persistence.FetchType; +import jakarta.persistence.ForeignKey; +import jakarta.persistence.GeneratedValue; +import jakarta.persistence.GenerationType; +import jakarta.persistence.Id; +import jakarta.persistence.Index; +import jakarta.persistence.JoinColumn; +import jakarta.persistence.ManyToOne; +import jakarta.persistence.SequenceGenerator; +import jakarta.persistence.Table; import org.apache.commons.lang3.builder.EqualsBuilder; import org.apache.commons.lang3.builder.ToStringBuilder; import org.apache.commons.lang3.builder.ToStringStyle; @@ -29,19 +42,6 @@ import org.hibernate.search.mapper.pojo.mapping.definition.annotation.ScaledNumb import java.math.BigDecimal; import java.util.Objects; -import javax.persistence.Column; -import javax.persistence.Embeddable; -import javax.persistence.Entity; -import javax.persistence.FetchType; -import javax.persistence.ForeignKey; -import javax.persistence.GeneratedValue; -import javax.persistence.GenerationType; -import javax.persistence.Id; -import javax.persistence.Index; -import javax.persistence.JoinColumn; -import javax.persistence.ManyToOne; -import javax.persistence.SequenceGenerator; -import javax.persistence.Table; import static org.apache.commons.lang3.StringUtils.defaultString; import static org.apache.commons.lang3.StringUtils.isBlank; diff --git a/hapi-fhir-jpaserver-model/src/main/java/ca/uhn/fhir/jpa/model/entity/ResourceIndexedSearchParamQuantityNormalized.java b/hapi-fhir-jpaserver-model/src/main/java/ca/uhn/fhir/jpa/model/entity/ResourceIndexedSearchParamQuantityNormalized.java index 2a7ca219558..7ba74219b4f 100644 --- a/hapi-fhir-jpaserver-model/src/main/java/ca/uhn/fhir/jpa/model/entity/ResourceIndexedSearchParamQuantityNormalized.java +++ b/hapi-fhir-jpaserver-model/src/main/java/ca/uhn/fhir/jpa/model/entity/ResourceIndexedSearchParamQuantityNormalized.java @@ -23,6 +23,19 @@ import ca.uhn.fhir.jpa.model.config.PartitionSettings; import ca.uhn.fhir.jpa.model.util.UcumServiceUtil; import ca.uhn.fhir.model.api.IQueryParameterType; import ca.uhn.fhir.rest.param.QuantityParam; +import jakarta.persistence.Column; +import jakarta.persistence.Embeddable; +import jakarta.persistence.Entity; +import jakarta.persistence.FetchType; +import jakarta.persistence.ForeignKey; +import jakarta.persistence.GeneratedValue; +import jakarta.persistence.GenerationType; +import jakarta.persistence.Id; +import jakarta.persistence.Index; +import jakarta.persistence.JoinColumn; +import jakarta.persistence.ManyToOne; +import jakarta.persistence.SequenceGenerator; +import jakarta.persistence.Table; import org.apache.commons.lang3.builder.EqualsBuilder; import org.apache.commons.lang3.builder.ToStringBuilder; import org.apache.commons.lang3.builder.ToStringStyle; @@ -31,19 +44,6 @@ import org.hibernate.search.mapper.pojo.mapping.definition.annotation.ScaledNumb import java.math.BigDecimal; import java.util.Objects; -import javax.persistence.Column; -import javax.persistence.Embeddable; -import javax.persistence.Entity; -import javax.persistence.FetchType; -import javax.persistence.ForeignKey; -import javax.persistence.GeneratedValue; -import javax.persistence.GenerationType; -import javax.persistence.Id; -import javax.persistence.Index; -import javax.persistence.JoinColumn; -import javax.persistence.ManyToOne; -import javax.persistence.SequenceGenerator; -import javax.persistence.Table; import static org.apache.commons.lang3.StringUtils.defaultString; import static org.apache.commons.lang3.StringUtils.isBlank; diff --git a/hapi-fhir-jpaserver-model/src/main/java/ca/uhn/fhir/jpa/model/entity/ResourceIndexedSearchParamString.java b/hapi-fhir-jpaserver-model/src/main/java/ca/uhn/fhir/jpa/model/entity/ResourceIndexedSearchParamString.java index 870c367a418..b5320e48cb0 100644 --- a/hapi-fhir-jpaserver-model/src/main/java/ca/uhn/fhir/jpa/model/entity/ResourceIndexedSearchParamString.java +++ b/hapi-fhir-jpaserver-model/src/main/java/ca/uhn/fhir/jpa/model/entity/ResourceIndexedSearchParamString.java @@ -25,24 +25,23 @@ import ca.uhn.fhir.jpa.model.config.PartitionSettings; import ca.uhn.fhir.model.api.IQueryParameterType; import ca.uhn.fhir.rest.param.StringParam; import ca.uhn.fhir.util.StringUtil; +import jakarta.persistence.Column; +import jakarta.persistence.Embeddable; +import jakarta.persistence.Entity; +import jakarta.persistence.ForeignKey; +import jakarta.persistence.GeneratedValue; +import jakarta.persistence.GenerationType; +import jakarta.persistence.Id; +import jakarta.persistence.Index; +import jakarta.persistence.JoinColumn; +import jakarta.persistence.ManyToOne; +import jakarta.persistence.SequenceGenerator; +import jakarta.persistence.Table; import org.apache.commons.lang3.builder.EqualsBuilder; import org.apache.commons.lang3.builder.HashCodeBuilder; import org.apache.commons.lang3.builder.ToStringBuilder; import org.apache.commons.lang3.builder.ToStringStyle; -import javax.persistence.Column; -import javax.persistence.Embeddable; -import javax.persistence.Entity; -import javax.persistence.ForeignKey; -import javax.persistence.GeneratedValue; -import javax.persistence.GenerationType; -import javax.persistence.Id; -import javax.persistence.Index; -import javax.persistence.JoinColumn; -import javax.persistence.ManyToOne; -import javax.persistence.SequenceGenerator; -import javax.persistence.Table; - import static org.apache.commons.lang3.StringUtils.defaultString; // @formatter:off diff --git a/hapi-fhir-jpaserver-model/src/main/java/ca/uhn/fhir/jpa/model/entity/ResourceIndexedSearchParamToken.java b/hapi-fhir-jpaserver-model/src/main/java/ca/uhn/fhir/jpa/model/entity/ResourceIndexedSearchParamToken.java index 098f167a909..d8c44831239 100644 --- a/hapi-fhir-jpaserver-model/src/main/java/ca/uhn/fhir/jpa/model/entity/ResourceIndexedSearchParamToken.java +++ b/hapi-fhir-jpaserver-model/src/main/java/ca/uhn/fhir/jpa/model/entity/ResourceIndexedSearchParamToken.java @@ -24,6 +24,20 @@ import ca.uhn.fhir.jpa.model.config.PartitionSettings; import ca.uhn.fhir.model.api.IQueryParameterType; import ca.uhn.fhir.rest.api.Constants; import ca.uhn.fhir.rest.param.TokenParam; +import jakarta.persistence.Column; +import jakarta.persistence.Embeddable; +import jakarta.persistence.Entity; +import jakarta.persistence.FetchType; +import jakarta.persistence.ForeignKey; +import jakarta.persistence.GeneratedValue; +import jakarta.persistence.GenerationType; +import jakarta.persistence.Id; +import jakarta.persistence.Index; +import jakarta.persistence.JoinColumn; +import jakarta.persistence.ManyToOne; +import jakarta.persistence.PrePersist; +import jakarta.persistence.SequenceGenerator; +import jakarta.persistence.Table; import org.apache.commons.lang3.StringUtils; import org.apache.commons.lang3.builder.EqualsBuilder; import org.apache.commons.lang3.builder.HashCodeBuilder; @@ -31,21 +45,6 @@ import org.apache.commons.lang3.builder.ToStringBuilder; import org.apache.commons.lang3.builder.ToStringStyle; import org.hibernate.search.mapper.pojo.mapping.definition.annotation.FullTextField; -import javax.persistence.Column; -import javax.persistence.Embeddable; -import javax.persistence.Entity; -import javax.persistence.FetchType; -import javax.persistence.ForeignKey; -import javax.persistence.GeneratedValue; -import javax.persistence.GenerationType; -import javax.persistence.Id; -import javax.persistence.Index; -import javax.persistence.JoinColumn; -import javax.persistence.ManyToOne; -import javax.persistence.PrePersist; -import javax.persistence.SequenceGenerator; -import javax.persistence.Table; - import static org.apache.commons.lang3.StringUtils.defaultString; import static org.apache.commons.lang3.StringUtils.trim; @@ -299,6 +298,7 @@ public class ResourceIndexedSearchParamToken extends BaseResourceIndexedSearchPa @Override public String toString() { ToStringBuilder b = new ToStringBuilder(this, ToStringStyle.SHORT_PREFIX_STYLE); + b.append("id", getId()); if (getPartitionId() != null) { b.append("partitionId", getPartitionId().getPartitionId()); } @@ -424,11 +424,11 @@ public class ResourceIndexedSearchParamToken extends BaseResourceIndexedSearchPa return this; } - @PrePersist /** * We truncate the fields at the last moment because the tables have limited size. * We don't truncate earlier in the flow because the index hashes MUST be calculated on the full string. */ + @PrePersist public void truncateFieldsForDB() { mySystem = StringUtils.truncate(mySystem, MAX_LENGTH); myValue = StringUtils.truncate(myValue, MAX_LENGTH); diff --git a/hapi-fhir-jpaserver-model/src/main/java/ca/uhn/fhir/jpa/model/entity/ResourceIndexedSearchParamUri.java b/hapi-fhir-jpaserver-model/src/main/java/ca/uhn/fhir/jpa/model/entity/ResourceIndexedSearchParamUri.java index f2a2d8fe33c..aac0e778d12 100644 --- a/hapi-fhir-jpaserver-model/src/main/java/ca/uhn/fhir/jpa/model/entity/ResourceIndexedSearchParamUri.java +++ b/hapi-fhir-jpaserver-model/src/main/java/ca/uhn/fhir/jpa/model/entity/ResourceIndexedSearchParamUri.java @@ -23,26 +23,25 @@ import ca.uhn.fhir.interceptor.model.RequestPartitionId; import ca.uhn.fhir.jpa.model.config.PartitionSettings; import ca.uhn.fhir.model.api.IQueryParameterType; import ca.uhn.fhir.rest.param.UriParam; +import jakarta.persistence.Column; +import jakarta.persistence.Embeddable; +import jakarta.persistence.Entity; +import jakarta.persistence.FetchType; +import jakarta.persistence.ForeignKey; +import jakarta.persistence.GeneratedValue; +import jakarta.persistence.GenerationType; +import jakarta.persistence.Id; +import jakarta.persistence.Index; +import jakarta.persistence.JoinColumn; +import jakarta.persistence.ManyToOne; +import jakarta.persistence.SequenceGenerator; +import jakarta.persistence.Table; import org.apache.commons.lang3.StringUtils; import org.apache.commons.lang3.builder.EqualsBuilder; import org.apache.commons.lang3.builder.HashCodeBuilder; import org.apache.commons.lang3.builder.ToStringBuilder; import org.hibernate.search.mapper.pojo.mapping.definition.annotation.FullTextField; -import javax.persistence.Column; -import javax.persistence.Embeddable; -import javax.persistence.Entity; -import javax.persistence.FetchType; -import javax.persistence.ForeignKey; -import javax.persistence.GeneratedValue; -import javax.persistence.GenerationType; -import javax.persistence.Id; -import javax.persistence.Index; -import javax.persistence.JoinColumn; -import javax.persistence.ManyToOne; -import javax.persistence.SequenceGenerator; -import javax.persistence.Table; - import static org.apache.commons.lang3.StringUtils.defaultString; @Embeddable diff --git a/hapi-fhir-jpaserver-model/src/main/java/ca/uhn/fhir/jpa/model/entity/ResourceLink.java b/hapi-fhir-jpaserver-model/src/main/java/ca/uhn/fhir/jpa/model/entity/ResourceLink.java index 8877dd90dbb..f1167937b13 100644 --- a/hapi-fhir-jpaserver-model/src/main/java/ca/uhn/fhir/jpa/model/entity/ResourceLink.java +++ b/hapi-fhir-jpaserver-model/src/main/java/ca/uhn/fhir/jpa/model/entity/ResourceLink.java @@ -19,6 +19,22 @@ */ package ca.uhn.fhir.jpa.model.entity; +import jakarta.annotation.Nullable; +import jakarta.persistence.Column; +import jakarta.persistence.Entity; +import jakarta.persistence.FetchType; +import jakarta.persistence.ForeignKey; +import jakarta.persistence.GeneratedValue; +import jakarta.persistence.GenerationType; +import jakarta.persistence.Id; +import jakarta.persistence.Index; +import jakarta.persistence.JoinColumn; +import jakarta.persistence.ManyToOne; +import jakarta.persistence.SequenceGenerator; +import jakarta.persistence.Table; +import jakarta.persistence.Temporal; +import jakarta.persistence.TemporalType; +import jakarta.persistence.Transient; import org.apache.commons.lang3.Validate; import org.apache.commons.lang3.builder.EqualsBuilder; import org.apache.commons.lang3.builder.HashCodeBuilder; @@ -26,22 +42,6 @@ import org.hibernate.search.mapper.pojo.mapping.definition.annotation.FullTextFi import org.hl7.fhir.instance.model.api.IIdType; import java.util.Date; -import javax.annotation.Nullable; -import javax.persistence.Column; -import javax.persistence.Entity; -import javax.persistence.FetchType; -import javax.persistence.ForeignKey; -import javax.persistence.GeneratedValue; -import javax.persistence.GenerationType; -import javax.persistence.Id; -import javax.persistence.Index; -import javax.persistence.JoinColumn; -import javax.persistence.ManyToOne; -import javax.persistence.SequenceGenerator; -import javax.persistence.Table; -import javax.persistence.Temporal; -import javax.persistence.TemporalType; -import javax.persistence.Transient; @Entity @Table( diff --git a/hapi-fhir-jpaserver-model/src/main/java/ca/uhn/fhir/jpa/model/entity/ResourceModifiedEntity.java b/hapi-fhir-jpaserver-model/src/main/java/ca/uhn/fhir/jpa/model/entity/ResourceModifiedEntity.java index 11ec35ff436..c7529a45270 100644 --- a/hapi-fhir-jpaserver-model/src/main/java/ca/uhn/fhir/jpa/model/entity/ResourceModifiedEntity.java +++ b/hapi-fhir-jpaserver-model/src/main/java/ca/uhn/fhir/jpa/model/entity/ResourceModifiedEntity.java @@ -20,14 +20,15 @@ package ca.uhn.fhir.jpa.model.entity; * #L% */ +import jakarta.persistence.Column; +import jakarta.persistence.EmbeddedId; +import jakarta.persistence.Entity; +import jakarta.persistence.Table; +import jakarta.persistence.Temporal; +import jakarta.persistence.TemporalType; + import java.io.Serializable; import java.util.Date; -import javax.persistence.Column; -import javax.persistence.EmbeddedId; -import javax.persistence.Entity; -import javax.persistence.Table; -import javax.persistence.Temporal; -import javax.persistence.TemporalType; /** * This class describes how a resourceModifiedMessage is stored for later processing in the event where diff --git a/hapi-fhir-jpaserver-model/src/main/java/ca/uhn/fhir/jpa/model/entity/ResourceSearchUrlEntity.java b/hapi-fhir-jpaserver-model/src/main/java/ca/uhn/fhir/jpa/model/entity/ResourceSearchUrlEntity.java index 681d20222f8..33b99846d2f 100644 --- a/hapi-fhir-jpaserver-model/src/main/java/ca/uhn/fhir/jpa/model/entity/ResourceSearchUrlEntity.java +++ b/hapi-fhir-jpaserver-model/src/main/java/ca/uhn/fhir/jpa/model/entity/ResourceSearchUrlEntity.java @@ -19,14 +19,15 @@ */ package ca.uhn.fhir.jpa.model.entity; +import jakarta.persistence.Column; +import jakarta.persistence.Entity; +import jakarta.persistence.Id; +import jakarta.persistence.Index; +import jakarta.persistence.Table; +import jakarta.persistence.Temporal; +import jakarta.persistence.TemporalType; + import java.util.Date; -import javax.persistence.Column; -import javax.persistence.Entity; -import javax.persistence.Id; -import javax.persistence.Index; -import javax.persistence.Table; -import javax.persistence.Temporal; -import javax.persistence.TemporalType; @Entity @Table( diff --git a/hapi-fhir-jpaserver-model/src/main/java/ca/uhn/fhir/jpa/model/entity/ResourceTable.java b/hapi-fhir-jpaserver-model/src/main/java/ca/uhn/fhir/jpa/model/entity/ResourceTable.java index b3bc742b248..f51159ec306 100644 --- a/hapi-fhir-jpaserver-model/src/main/java/ca/uhn/fhir/jpa/model/entity/ResourceTable.java +++ b/hapi-fhir-jpaserver-model/src/main/java/ca/uhn/fhir/jpa/model/entity/ResourceTable.java @@ -28,6 +28,24 @@ import ca.uhn.fhir.jpa.model.search.SearchParamTextPropertyBinder; import ca.uhn.fhir.model.primitive.IdDt; import ca.uhn.fhir.rest.api.Constants; import com.google.common.annotations.VisibleForTesting; +import jakarta.persistence.CascadeType; +import jakarta.persistence.Column; +import jakarta.persistence.Entity; +import jakarta.persistence.FetchType; +import jakarta.persistence.GeneratedValue; +import jakarta.persistence.GenerationType; +import jakarta.persistence.Id; +import jakarta.persistence.Index; +import jakarta.persistence.NamedEntityGraph; +import jakarta.persistence.OneToMany; +import jakarta.persistence.OneToOne; +import jakarta.persistence.PostPersist; +import jakarta.persistence.PrePersist; +import jakarta.persistence.PreUpdate; +import jakarta.persistence.Table; +import jakarta.persistence.Transient; +import jakarta.persistence.UniqueConstraint; +import jakarta.persistence.Version; import org.apache.commons.lang3.builder.ToStringBuilder; import org.apache.commons.lang3.builder.ToStringStyle; import org.hibernate.Session; @@ -57,44 +75,38 @@ import java.util.HashSet; import java.util.Objects; import java.util.Set; import java.util.stream.Collectors; -import javax.persistence.CascadeType; -import javax.persistence.Column; -import javax.persistence.Entity; -import javax.persistence.FetchType; -import javax.persistence.GeneratedValue; -import javax.persistence.GenerationType; -import javax.persistence.Id; -import javax.persistence.Index; -import javax.persistence.NamedEntityGraph; -import javax.persistence.OneToMany; -import javax.persistence.OneToOne; -import javax.persistence.PostPersist; -import javax.persistence.PrePersist; -import javax.persistence.PreUpdate; -import javax.persistence.Table; -import javax.persistence.Transient; -import javax.persistence.Version; + +import static ca.uhn.fhir.jpa.model.entity.ResourceTable.IDX_RES_TYPE_FHIR_ID; @Indexed(routingBinder = @RoutingBinderRef(type = ResourceTableRoutingBinder.class)) @Entity @Table( name = ResourceTable.HFJ_RESOURCE, - uniqueConstraints = {}, + uniqueConstraints = { + @UniqueConstraint( + name = IDX_RES_TYPE_FHIR_ID, + columnNames = {"RES_TYPE", "FHIR_ID"}) + }, indexes = { // Do not reuse previously used index name: IDX_INDEXSTATUS, IDX_RES_TYPE @Index(name = "IDX_RES_DATE", columnList = BaseHasResource.RES_UPDATED), + @Index(name = "IDX_RES_FHIR_ID", columnList = "FHIR_ID"), @Index( name = "IDX_RES_TYPE_DEL_UPDATED", columnList = "RES_TYPE,RES_DELETED_AT,RES_UPDATED,PARTITION_ID,RES_ID"), - @Index(name = "IDX_RES_RESID_UPDATED", columnList = "RES_ID,RES_UPDATED,PARTITION_ID"), + @Index(name = "IDX_RES_RESID_UPDATED", columnList = "RES_ID, RES_UPDATED, PARTITION_ID") }) @NamedEntityGraph(name = "Resource.noJoins") public class ResourceTable extends BaseHasResource implements Serializable, IBasePersistedResource { public static final int RESTYPE_LEN = 40; public static final String HFJ_RESOURCE = "HFJ_RESOURCE"; public static final String RES_TYPE = "RES_TYPE"; + public static final String FHIR_ID = "FHIR_ID"; private static final int MAX_LANGUAGE_LENGTH = 20; private static final long serialVersionUID = 1L; + public static final int MAX_FORCED_ID_LENGTH = 100; + public static final String IDX_RES_TYPE_FHIR_ID = "IDX_RES_TYPE_FHIR_ID"; + /** * Holds the narrative text only - Used for Fulltext searching but not directly stored in the DB * Note the extra config needed in HS6 for indexing transient props: @@ -136,7 +148,7 @@ public class ResourceTable extends BaseHasResource implements Serializable, IBas private boolean myHasLinks; @Id - @GenericGenerator(name = "SEQ_RESOURCE_ID", strategy = "ca.uhn.fhir.jpa.model.dialect.HapiSequenceStyleGenerator") + @GenericGenerator(name = "SEQ_RESOURCE_ID", type = ca.uhn.fhir.jpa.model.dialect.HapiSequenceStyleGenerator.class) @GeneratedValue(strategy = GenerationType.AUTO, generator = "SEQ_RESOURCE_ID") @Column(name = "RES_ID") @GenericField(projectable = Projectable.YES) @@ -371,7 +383,7 @@ public class ResourceTable extends BaseHasResource implements Serializable, IBas * Will be null during insert time until the first read. */ @Column( - name = "FHIR_ID", + name = FHIR_ID, // [A-Za-z0-9\-\.]{1,64} - https://www.hl7.org/fhir/datatypes.html#id length = 64, // we never update this after insert, and the Generator will otherwise "dirty" the object. @@ -982,24 +994,18 @@ public class ResourceTable extends BaseHasResource implements Serializable, IBas } private void populateId(IIdType retVal) { + String resourceId; if (myFhirId != null && !myFhirId.isEmpty()) { - retVal.setValue(getResourceType() + '/' + myFhirId + '/' + Constants.PARAM_HISTORY + '/' + getVersion()); + resourceId = myFhirId; } else if (getTransientForcedId() != null) { - // Avoid a join query if possible - retVal.setValue(getResourceType() - + '/' - + getTransientForcedId() - + '/' - + Constants.PARAM_HISTORY - + '/' - + getVersion()); - } else if (getForcedId() == null) { - Long id = this.getResourceId(); - retVal.setValue(getResourceType() + '/' + id + '/' + Constants.PARAM_HISTORY + '/' + getVersion()); + resourceId = getTransientForcedId(); + } else if (myForcedId != null) { + resourceId = myForcedId.getForcedId(); } else { - String forcedId = getForcedId().getForcedId(); - retVal.setValue(getResourceType() + '/' + forcedId + '/' + Constants.PARAM_HISTORY + '/' + getVersion()); + Long id = this.getResourceId(); + resourceId = Long.toString(id); } + retVal.setValue(getResourceType() + '/' + resourceId + '/' + Constants.PARAM_HISTORY + '/' + getVersion()); } public String getCreatedByMatchUrl() { @@ -1047,6 +1053,10 @@ public class ResourceTable extends BaseHasResource implements Serializable, IBas myFhirId = theFhirId; } + public String asTypedFhirResourceId() { + return getResourceType() + "/" + getFhirId(); + } + /** * Populate myFhirId with server-assigned sequence id when no client-id provided. * We eat this complexity during insert to simplify query time with a uniform column. diff --git a/hapi-fhir-jpaserver-model/src/main/java/ca/uhn/fhir/jpa/model/entity/ResourceTag.java b/hapi-fhir-jpaserver-model/src/main/java/ca/uhn/fhir/jpa/model/entity/ResourceTag.java index ba7f24a91ae..6c50caca82c 100644 --- a/hapi-fhir-jpaserver-model/src/main/java/ca/uhn/fhir/jpa/model/entity/ResourceTag.java +++ b/hapi-fhir-jpaserver-model/src/main/java/ca/uhn/fhir/jpa/model/entity/ResourceTag.java @@ -19,25 +19,24 @@ */ package ca.uhn.fhir.jpa.model.entity; +import jakarta.persistence.Column; +import jakarta.persistence.Entity; +import jakarta.persistence.FetchType; +import jakarta.persistence.ForeignKey; +import jakarta.persistence.GeneratedValue; +import jakarta.persistence.GenerationType; +import jakarta.persistence.Id; +import jakarta.persistence.Index; +import jakarta.persistence.JoinColumn; +import jakarta.persistence.ManyToOne; +import jakarta.persistence.SequenceGenerator; +import jakarta.persistence.Table; +import jakarta.persistence.UniqueConstraint; import org.apache.commons.lang3.builder.EqualsBuilder; import org.apache.commons.lang3.builder.HashCodeBuilder; import org.apache.commons.lang3.builder.ToStringBuilder; import org.apache.commons.lang3.builder.ToStringStyle; -import javax.persistence.Column; -import javax.persistence.Entity; -import javax.persistence.FetchType; -import javax.persistence.ForeignKey; -import javax.persistence.GeneratedValue; -import javax.persistence.GenerationType; -import javax.persistence.Id; -import javax.persistence.Index; -import javax.persistence.JoinColumn; -import javax.persistence.ManyToOne; -import javax.persistence.SequenceGenerator; -import javax.persistence.Table; -import javax.persistence.UniqueConstraint; - @Entity @Table( name = "HFJ_RES_TAG", diff --git a/hapi-fhir-jpaserver-model/src/main/java/ca/uhn/fhir/jpa/model/entity/SearchParamPresentEntity.java b/hapi-fhir-jpaserver-model/src/main/java/ca/uhn/fhir/jpa/model/entity/SearchParamPresentEntity.java index b4ceebd9235..cd23d54ffe1 100644 --- a/hapi-fhir-jpaserver-model/src/main/java/ca/uhn/fhir/jpa/model/entity/SearchParamPresentEntity.java +++ b/hapi-fhir-jpaserver-model/src/main/java/ca/uhn/fhir/jpa/model/entity/SearchParamPresentEntity.java @@ -21,6 +21,7 @@ package ca.uhn.fhir.jpa.model.entity; import ca.uhn.fhir.interceptor.model.RequestPartitionId; import ca.uhn.fhir.jpa.model.config.PartitionSettings; +import jakarta.persistence.*; import org.apache.commons.lang3.Validate; import org.apache.commons.lang3.builder.EqualsBuilder; import org.apache.commons.lang3.builder.HashCodeBuilder; @@ -28,7 +29,6 @@ import org.apache.commons.lang3.builder.ToStringBuilder; import org.apache.commons.lang3.builder.ToStringStyle; import java.io.Serializable; -import javax.persistence.*; @Entity @Table( diff --git a/hapi-fhir-jpaserver-model/src/main/java/ca/uhn/fhir/jpa/model/entity/StorageSettings.java b/hapi-fhir-jpaserver-model/src/main/java/ca/uhn/fhir/jpa/model/entity/StorageSettings.java index b14872263ce..fcb5c355d0e 100644 --- a/hapi-fhir-jpaserver-model/src/main/java/ca/uhn/fhir/jpa/model/entity/StorageSettings.java +++ b/hapi-fhir-jpaserver-model/src/main/java/ca/uhn/fhir/jpa/model/entity/StorageSettings.java @@ -21,7 +21,7 @@ package ca.uhn.fhir.jpa.model.entity; import ca.uhn.fhir.context.ParserOptions; import ca.uhn.fhir.i18n.Msg; -import ca.uhn.fhir.jpa.model.dialect.ISequenceValueMassager; +import ca.uhn.fhir.jpa.util.ISequenceValueMassager; import ca.uhn.fhir.model.api.TemporalPrecisionEnum; import ca.uhn.fhir.rest.server.interceptor.ResponseTerminologyTranslationSvc; import ca.uhn.fhir.util.HapiExtensions; @@ -146,6 +146,14 @@ public class StorageSettings { */ private boolean myLanguageSearchParameterEnabled = false; + /** + * If set to false, all resource types will be installed via package installer, regardless of their status. + * Otherwise, resources will be filtered based on status according to some criteria which can be found in + * PackageInstallerSvcImpl#isValidResourceStatusForPackageUpload + * @since 7.0.0 + */ + private boolean myValidateResourceStatusForPackageUpload = true; + /** * If set to true, the server will prevent the creation of Subscriptions which cannot be evaluated IN-MEMORY. This can improve * overall server performance. @@ -1319,6 +1327,22 @@ public class StorageSettings { myLanguageSearchParameterEnabled = theLanguageSearchParameterEnabled; } + /** + * @return true if the filter is enabled for resources installed via package installer, false otherwise + * @since 7.0.0 + */ + public boolean isValidateResourceStatusForPackageUpload() { + return myValidateResourceStatusForPackageUpload; + } + + /** + * Should resources being installed via package installer be filtered. + * @since 7.0.0 + */ + public void setValidateResourceStatusForPackageUpload(boolean theValidateResourceStatusForPackageUpload) { + myValidateResourceStatusForPackageUpload = theValidateResourceStatusForPackageUpload; + } + private static void validateTreatBaseUrlsAsLocal(String theUrl) { Validate.notBlank(theUrl, "Base URL must not be null or empty"); diff --git a/hapi-fhir-jpaserver-model/src/main/java/ca/uhn/fhir/jpa/model/entity/TagDefinition.java b/hapi-fhir-jpaserver-model/src/main/java/ca/uhn/fhir/jpa/model/entity/TagDefinition.java index 111c1c83e04..5f4ae0260b7 100644 --- a/hapi-fhir-jpaserver-model/src/main/java/ca/uhn/fhir/jpa/model/entity/TagDefinition.java +++ b/hapi-fhir-jpaserver-model/src/main/java/ca/uhn/fhir/jpa/model/entity/TagDefinition.java @@ -19,27 +19,29 @@ */ package ca.uhn.fhir.jpa.model.entity; +import jakarta.persistence.Column; +import jakarta.persistence.Entity; +import jakarta.persistence.EnumType; +import jakarta.persistence.Enumerated; +import jakarta.persistence.FetchType; +import jakarta.persistence.GeneratedValue; +import jakarta.persistence.GenerationType; +import jakarta.persistence.Id; +import jakarta.persistence.Index; +import jakarta.persistence.OneToMany; +import jakarta.persistence.SequenceGenerator; +import jakarta.persistence.Table; +import jakarta.persistence.Transient; import org.apache.commons.lang3.StringUtils; import org.apache.commons.lang3.builder.EqualsBuilder; import org.apache.commons.lang3.builder.HashCodeBuilder; import org.apache.commons.lang3.builder.ToStringBuilder; import org.apache.commons.lang3.builder.ToStringStyle; +import org.hibernate.annotations.JdbcTypeCode; +import org.hibernate.type.SqlTypes; import java.io.Serializable; import java.util.Collection; -import javax.persistence.Column; -import javax.persistence.Entity; -import javax.persistence.EnumType; -import javax.persistence.Enumerated; -import javax.persistence.FetchType; -import javax.persistence.GeneratedValue; -import javax.persistence.GenerationType; -import javax.persistence.Id; -import javax.persistence.Index; -import javax.persistence.OneToMany; -import javax.persistence.SequenceGenerator; -import javax.persistence.Table; -import javax.persistence.Transient; @Entity @Table( @@ -82,6 +84,7 @@ public class TagDefinition implements Serializable { @Column(name = "TAG_TYPE", nullable = false) @Enumerated(EnumType.ORDINAL) + @JdbcTypeCode(SqlTypes.INTEGER) private TagTypeEnum myTagType; @Column(name = "TAG_VERSION", length = 30) diff --git a/hapi-fhir-jpaserver-model/src/main/java/ca/uhn/fhir/jpa/model/search/HSearchElementCache.java b/hapi-fhir-jpaserver-model/src/main/java/ca/uhn/fhir/jpa/model/search/HSearchElementCache.java index c96ee902c79..36c0df1d0a3 100644 --- a/hapi-fhir-jpaserver-model/src/main/java/ca/uhn/fhir/jpa/model/search/HSearchElementCache.java +++ b/hapi-fhir-jpaserver-model/src/main/java/ca/uhn/fhir/jpa/model/search/HSearchElementCache.java @@ -19,6 +19,7 @@ */ package ca.uhn.fhir.jpa.model.search; +import jakarta.annotation.Nonnull; import org.hibernate.search.engine.backend.document.DocumentElement; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -27,7 +28,6 @@ import java.util.Arrays; import java.util.HashMap; import java.util.List; import java.util.Map; -import javax.annotation.Nonnull; /** * Provide a lookup of created Hibernate Search DocumentElement entries. diff --git a/hapi-fhir-jpaserver-model/src/main/java/ca/uhn/fhir/jpa/model/util/JpaConstants.java b/hapi-fhir-jpaserver-model/src/main/java/ca/uhn/fhir/jpa/model/util/JpaConstants.java index 7b62541c178..60fc4b7b68d 100644 --- a/hapi-fhir-jpaserver-model/src/main/java/ca/uhn/fhir/jpa/model/util/JpaConstants.java +++ b/hapi-fhir-jpaserver-model/src/main/java/ca/uhn/fhir/jpa/model/util/JpaConstants.java @@ -166,14 +166,6 @@ public class JpaConstants { * Operation name for the "$import-poll-status" operation */ public static final String OPERATION_IMPORT_POLL_STATUS = "$import-poll-status"; - /** - * Operation name for the "$export" operation - */ - public static final String OPERATION_EXPORT = "$export"; - /** - * Operation name for the "$export-poll-status" operation - */ - public static final String OPERATION_EXPORT_POLL_STATUS = "$export-poll-status"; /** * Operation name for the "$lastn" operation */ @@ -302,7 +294,6 @@ public class JpaConstants { */ public static final String SUMMARY_OPERATION_URL = "http://hl7.org/fhir/uv/ips/OperationDefinition/summary"; - public static final String ORG_HIBERNATE_TYPE_TEXT_TYPE = "org.hibernate.type.TextType"; public static final String BULK_META_EXTENSION_EXPORT_IDENTIFIER = "https://hapifhir.org/NamingSystem/bulk-export-identifier"; public static final String BULK_META_EXTENSION_JOB_ID = "https://hapifhir.org/NamingSystem/bulk-export-job-id"; @@ -310,6 +301,7 @@ public class JpaConstants { "https://hapifhir.org/NamingSystem/bulk-export-binary-resource-type"; public static final Set UNDESIRED_RESOURCE_LINKAGES_FOR_EVERYTHING_ON_PATIENT_INSTANCE = Set.of("Provenance", "List", "Group"); + /** * Non-instantiable */ diff --git a/hapi-fhir-jpaserver-model/src/main/java/ca/uhn/fhir/jpa/model/util/UcumServiceUtil.java b/hapi-fhir-jpaserver-model/src/main/java/ca/uhn/fhir/jpa/model/util/UcumServiceUtil.java index 56ef214852e..ab469668adc 100644 --- a/hapi-fhir-jpaserver-model/src/main/java/ca/uhn/fhir/jpa/model/util/UcumServiceUtil.java +++ b/hapi-fhir-jpaserver-model/src/main/java/ca/uhn/fhir/jpa/model/util/UcumServiceUtil.java @@ -23,6 +23,7 @@ import ca.uhn.fhir.i18n.Msg; import ca.uhn.fhir.rest.param.QuantityParam; import ca.uhn.fhir.rest.server.exceptions.InvalidRequestException; import ca.uhn.fhir.util.ClasspathUtil; +import jakarta.annotation.Nullable; import org.fhir.ucum.Decimal; import org.fhir.ucum.Pair; import org.fhir.ucum.UcumEssenceService; @@ -33,7 +34,6 @@ import org.slf4j.LoggerFactory; import java.io.InputStream; import java.math.BigDecimal; import java.math.RoundingMode; -import javax.annotation.Nullable; /** * It's a wrapper of UcumEssenceService diff --git a/hapi-fhir-jpaserver-model/src/test/java/ca/uhn/fhir/jpa/model/entity/ResourceTableTest.java b/hapi-fhir-jpaserver-model/src/test/java/ca/uhn/fhir/jpa/model/entity/ResourceTableTest.java index c170a18de47..a5ccb698720 100644 --- a/hapi-fhir-jpaserver-model/src/test/java/ca/uhn/fhir/jpa/model/entity/ResourceTableTest.java +++ b/hapi-fhir-jpaserver-model/src/test/java/ca/uhn/fhir/jpa/model/entity/ResourceTableTest.java @@ -43,6 +43,6 @@ public class ResourceTableTest { IdDt actual = t.getIdDt(); // Then - assertTrue(actual.equals(theExpected)); + assertEquals(theExpected, actual.getValueAsString()); } } diff --git a/hapi-fhir-jpaserver-searchparam/pom.xml b/hapi-fhir-jpaserver-searchparam/pom.xml index 3fd487db90d..20a05eaa091 100755 --- a/hapi-fhir-jpaserver-searchparam/pom.xml +++ b/hapi-fhir-jpaserver-searchparam/pom.xml @@ -5,7 +5,7 @@ ca.uhn.hapi.fhir hapi-deployable-pom - 6.9.7-SNAPSHOT + 6.11.7-SNAPSHOT ../hapi-deployable-pom/pom.xml @@ -13,7 +13,7 @@ hapi-fhir-jpaserver-searchparam jar - HAPI FHIR Search Parameters + HAPI FHIR JPA - Search Parameters @@ -125,7 +125,7 @@ org.hibernate.search - hibernate-search-mapper-orm + hibernate-search-mapper-orm-orm6 org.jscience @@ -160,8 +160,8 @@ test - javax.servlet - javax.servlet-api + jakarta.servlet + jakarta.servlet-api test diff --git a/hapi-fhir-jpaserver-searchparam/src/main/java/ca/uhn/fhir/interceptor/model/ReadPartitionIdRequestDetails.java b/hapi-fhir-jpaserver-searchparam/src/main/java/ca/uhn/fhir/interceptor/model/ReadPartitionIdRequestDetails.java index 4f80dd9bbc2..af9740918c9 100644 --- a/hapi-fhir-jpaserver-searchparam/src/main/java/ca/uhn/fhir/interceptor/model/ReadPartitionIdRequestDetails.java +++ b/hapi-fhir-jpaserver-searchparam/src/main/java/ca/uhn/fhir/interceptor/model/ReadPartitionIdRequestDetails.java @@ -1,6 +1,6 @@ /*- * #%L - * HAPI FHIR Search Parameters + * HAPI FHIR JPA - Search Parameters * %% * Copyright (C) 2014 - 2023 Smile CDR, Inc. * %% @@ -21,12 +21,11 @@ package ca.uhn.fhir.interceptor.model; import ca.uhn.fhir.jpa.searchparam.SearchParameterMap; import ca.uhn.fhir.rest.api.RestOperationTypeEnum; +import jakarta.annotation.Nonnull; +import jakarta.annotation.Nullable; import org.hl7.fhir.instance.model.api.IBaseResource; import org.hl7.fhir.instance.model.api.IIdType; -import javax.annotation.Nonnull; -import javax.annotation.Nullable; - import static org.apache.commons.lang3.StringUtils.isNotBlank; /** diff --git a/hapi-fhir-jpaserver-searchparam/src/main/java/ca/uhn/fhir/jpa/cache/IResourceChangeEvent.java b/hapi-fhir-jpaserver-searchparam/src/main/java/ca/uhn/fhir/jpa/cache/IResourceChangeEvent.java index 2ab2483c29f..8490bcabd03 100644 --- a/hapi-fhir-jpaserver-searchparam/src/main/java/ca/uhn/fhir/jpa/cache/IResourceChangeEvent.java +++ b/hapi-fhir-jpaserver-searchparam/src/main/java/ca/uhn/fhir/jpa/cache/IResourceChangeEvent.java @@ -1,6 +1,6 @@ /*- * #%L - * HAPI FHIR Search Parameters + * HAPI FHIR JPA - Search Parameters * %% * Copyright (C) 2014 - 2023 Smile CDR, Inc. * %% diff --git a/hapi-fhir-jpaserver-searchparam/src/main/java/ca/uhn/fhir/jpa/cache/IResourceChangeListener.java b/hapi-fhir-jpaserver-searchparam/src/main/java/ca/uhn/fhir/jpa/cache/IResourceChangeListener.java index 0f6b2a61a44..3e6c8b5e96d 100644 --- a/hapi-fhir-jpaserver-searchparam/src/main/java/ca/uhn/fhir/jpa/cache/IResourceChangeListener.java +++ b/hapi-fhir-jpaserver-searchparam/src/main/java/ca/uhn/fhir/jpa/cache/IResourceChangeListener.java @@ -1,6 +1,6 @@ /*- * #%L - * HAPI FHIR Search Parameters + * HAPI FHIR JPA - Search Parameters * %% * Copyright (C) 2014 - 2023 Smile CDR, Inc. * %% diff --git a/hapi-fhir-jpaserver-searchparam/src/main/java/ca/uhn/fhir/jpa/cache/IResourceChangeListenerCache.java b/hapi-fhir-jpaserver-searchparam/src/main/java/ca/uhn/fhir/jpa/cache/IResourceChangeListenerCache.java index a3c7cddcea9..d44b20ce2d3 100644 --- a/hapi-fhir-jpaserver-searchparam/src/main/java/ca/uhn/fhir/jpa/cache/IResourceChangeListenerCache.java +++ b/hapi-fhir-jpaserver-searchparam/src/main/java/ca/uhn/fhir/jpa/cache/IResourceChangeListenerCache.java @@ -1,6 +1,6 @@ /*- * #%L - * HAPI FHIR Search Parameters + * HAPI FHIR JPA - Search Parameters * %% * Copyright (C) 2014 - 2023 Smile CDR, Inc. * %% diff --git a/hapi-fhir-jpaserver-searchparam/src/main/java/ca/uhn/fhir/jpa/cache/IResourceChangeListenerCacheRefresher.java b/hapi-fhir-jpaserver-searchparam/src/main/java/ca/uhn/fhir/jpa/cache/IResourceChangeListenerCacheRefresher.java index 5cba0d76dd0..7afd92d97e0 100644 --- a/hapi-fhir-jpaserver-searchparam/src/main/java/ca/uhn/fhir/jpa/cache/IResourceChangeListenerCacheRefresher.java +++ b/hapi-fhir-jpaserver-searchparam/src/main/java/ca/uhn/fhir/jpa/cache/IResourceChangeListenerCacheRefresher.java @@ -1,6 +1,6 @@ /*- * #%L - * HAPI FHIR Search Parameters + * HAPI FHIR JPA - Search Parameters * %% * Copyright (C) 2014 - 2023 Smile CDR, Inc. * %% diff --git a/hapi-fhir-jpaserver-searchparam/src/main/java/ca/uhn/fhir/jpa/cache/IResourceChangeListenerRegistry.java b/hapi-fhir-jpaserver-searchparam/src/main/java/ca/uhn/fhir/jpa/cache/IResourceChangeListenerRegistry.java index fa6ba39cf72..e2144a84ddf 100644 --- a/hapi-fhir-jpaserver-searchparam/src/main/java/ca/uhn/fhir/jpa/cache/IResourceChangeListenerRegistry.java +++ b/hapi-fhir-jpaserver-searchparam/src/main/java/ca/uhn/fhir/jpa/cache/IResourceChangeListenerRegistry.java @@ -1,6 +1,6 @@ /*- * #%L - * HAPI FHIR Search Parameters + * HAPI FHIR JPA - Search Parameters * %% * Copyright (C) 2014 - 2023 Smile CDR, Inc. * %% diff --git a/hapi-fhir-jpaserver-searchparam/src/main/java/ca/uhn/fhir/jpa/cache/IResourceVersionSvc.java b/hapi-fhir-jpaserver-searchparam/src/main/java/ca/uhn/fhir/jpa/cache/IResourceVersionSvc.java index 56c554c1ab8..1ec8e09d97e 100644 --- a/hapi-fhir-jpaserver-searchparam/src/main/java/ca/uhn/fhir/jpa/cache/IResourceVersionSvc.java +++ b/hapi-fhir-jpaserver-searchparam/src/main/java/ca/uhn/fhir/jpa/cache/IResourceVersionSvc.java @@ -1,6 +1,6 @@ /*- * #%L - * HAPI FHIR Search Parameters + * HAPI FHIR JPA - Search Parameters * %% * Copyright (C) 2014 - 2023 Smile CDR, Inc. * %% @@ -21,10 +21,10 @@ package ca.uhn.fhir.jpa.cache; import ca.uhn.fhir.interceptor.model.RequestPartitionId; import ca.uhn.fhir.jpa.searchparam.SearchParameterMap; +import jakarta.annotation.Nonnull; import org.hl7.fhir.instance.model.api.IIdType; import java.util.List; -import javax.annotation.Nonnull; /** * This interface is used by the {@literal IResourceChangeListenerCacheRefresher} to read resources matching the provided diff --git a/hapi-fhir-jpaserver-searchparam/src/main/java/ca/uhn/fhir/jpa/cache/ResourceChangeEvent.java b/hapi-fhir-jpaserver-searchparam/src/main/java/ca/uhn/fhir/jpa/cache/ResourceChangeEvent.java index 47c39e0262a..2193b4c7f54 100644 --- a/hapi-fhir-jpaserver-searchparam/src/main/java/ca/uhn/fhir/jpa/cache/ResourceChangeEvent.java +++ b/hapi-fhir-jpaserver-searchparam/src/main/java/ca/uhn/fhir/jpa/cache/ResourceChangeEvent.java @@ -1,6 +1,6 @@ /*- * #%L - * HAPI FHIR Search Parameters + * HAPI FHIR JPA - Search Parameters * %% * Copyright (C) 2014 - 2023 Smile CDR, Inc. * %% diff --git a/hapi-fhir-jpaserver-searchparam/src/main/java/ca/uhn/fhir/jpa/cache/ResourceChangeListenerCache.java b/hapi-fhir-jpaserver-searchparam/src/main/java/ca/uhn/fhir/jpa/cache/ResourceChangeListenerCache.java index 1efec3226db..a3eb41953ed 100644 --- a/hapi-fhir-jpaserver-searchparam/src/main/java/ca/uhn/fhir/jpa/cache/ResourceChangeListenerCache.java +++ b/hapi-fhir-jpaserver-searchparam/src/main/java/ca/uhn/fhir/jpa/cache/ResourceChangeListenerCache.java @@ -1,6 +1,6 @@ /*- * #%L - * HAPI FHIR Search Parameters + * HAPI FHIR JPA - Search Parameters * %% * Copyright (C) 2014 - 2023 Smile CDR, Inc. * %% diff --git a/hapi-fhir-jpaserver-searchparam/src/main/java/ca/uhn/fhir/jpa/cache/ResourceChangeListenerCacheFactory.java b/hapi-fhir-jpaserver-searchparam/src/main/java/ca/uhn/fhir/jpa/cache/ResourceChangeListenerCacheFactory.java index 92e8f725db1..4f412bd5a6d 100644 --- a/hapi-fhir-jpaserver-searchparam/src/main/java/ca/uhn/fhir/jpa/cache/ResourceChangeListenerCacheFactory.java +++ b/hapi-fhir-jpaserver-searchparam/src/main/java/ca/uhn/fhir/jpa/cache/ResourceChangeListenerCacheFactory.java @@ -1,6 +1,6 @@ /*- * #%L - * HAPI FHIR Search Parameters + * HAPI FHIR JPA - Search Parameters * %% * Copyright (C) 2014 - 2023 Smile CDR, Inc. * %% diff --git a/hapi-fhir-jpaserver-searchparam/src/main/java/ca/uhn/fhir/jpa/cache/ResourceChangeListenerCacheRefresherImpl.java b/hapi-fhir-jpaserver-searchparam/src/main/java/ca/uhn/fhir/jpa/cache/ResourceChangeListenerCacheRefresherImpl.java index 131b5a6851d..09cf5b1de53 100644 --- a/hapi-fhir-jpaserver-searchparam/src/main/java/ca/uhn/fhir/jpa/cache/ResourceChangeListenerCacheRefresherImpl.java +++ b/hapi-fhir-jpaserver-searchparam/src/main/java/ca/uhn/fhir/jpa/cache/ResourceChangeListenerCacheRefresherImpl.java @@ -1,6 +1,6 @@ /*- * #%L - * HAPI FHIR Search Parameters + * HAPI FHIR JPA - Search Parameters * %% * Copyright (C) 2014 - 2023 Smile CDR, Inc. * %% diff --git a/hapi-fhir-jpaserver-searchparam/src/main/java/ca/uhn/fhir/jpa/cache/ResourceChangeListenerRegistryImpl.java b/hapi-fhir-jpaserver-searchparam/src/main/java/ca/uhn/fhir/jpa/cache/ResourceChangeListenerRegistryImpl.java index 8b30b1af72d..051a4a080ed 100644 --- a/hapi-fhir-jpaserver-searchparam/src/main/java/ca/uhn/fhir/jpa/cache/ResourceChangeListenerRegistryImpl.java +++ b/hapi-fhir-jpaserver-searchparam/src/main/java/ca/uhn/fhir/jpa/cache/ResourceChangeListenerRegistryImpl.java @@ -1,6 +1,6 @@ /*- * #%L - * HAPI FHIR Search Parameters + * HAPI FHIR JPA - Search Parameters * %% * Copyright (C) 2014 - 2023 Smile CDR, Inc. * %% @@ -26,6 +26,7 @@ import ca.uhn.fhir.jpa.searchparam.SearchParameterMap; import ca.uhn.fhir.jpa.searchparam.matcher.InMemoryMatchResult; import ca.uhn.fhir.jpa.searchparam.matcher.InMemoryResourceMatcher; import com.google.common.annotations.VisibleForTesting; +import jakarta.annotation.Nonnull; import org.hl7.fhir.instance.model.api.IBaseResource; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -36,7 +37,6 @@ import java.util.Queue; import java.util.Set; import java.util.concurrent.ConcurrentLinkedQueue; import java.util.stream.Collectors; -import javax.annotation.Nonnull; /** * This component holds an in-memory list of all registered {@link IResourceChangeListener} instances along diff --git a/hapi-fhir-jpaserver-searchparam/src/main/java/ca/uhn/fhir/jpa/cache/ResourceChangeListenerRegistryInterceptor.java b/hapi-fhir-jpaserver-searchparam/src/main/java/ca/uhn/fhir/jpa/cache/ResourceChangeListenerRegistryInterceptor.java index feec3df43ae..eba4a21f3ca 100644 --- a/hapi-fhir-jpaserver-searchparam/src/main/java/ca/uhn/fhir/jpa/cache/ResourceChangeListenerRegistryInterceptor.java +++ b/hapi-fhir-jpaserver-searchparam/src/main/java/ca/uhn/fhir/jpa/cache/ResourceChangeListenerRegistryInterceptor.java @@ -1,6 +1,6 @@ /*- * #%L - * HAPI FHIR Search Parameters + * HAPI FHIR JPA - Search Parameters * %% * Copyright (C) 2014 - 2023 Smile CDR, Inc. * %% @@ -23,6 +23,7 @@ import ca.uhn.fhir.IHapiBootOrder; import ca.uhn.fhir.interceptor.api.Hook; import ca.uhn.fhir.interceptor.api.IInterceptorService; import ca.uhn.fhir.interceptor.api.Pointcut; +import jakarta.annotation.PreDestroy; import org.hl7.fhir.instance.model.api.IBaseResource; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.context.event.ContextRefreshedEvent; @@ -30,8 +31,6 @@ import org.springframework.context.event.EventListener; import org.springframework.core.annotation.Order; import org.springframework.stereotype.Service; -import javax.annotation.PreDestroy; - /** * This interceptor watches all resource changes on the server and compares them to the {@link IResourceChangeListenerCache} * entries. If the resource matches the resource type and search parameter map of that entry, then the corresponding cache diff --git a/hapi-fhir-jpaserver-searchparam/src/main/java/ca/uhn/fhir/jpa/cache/ResourceChangeResult.java b/hapi-fhir-jpaserver-searchparam/src/main/java/ca/uhn/fhir/jpa/cache/ResourceChangeResult.java index c5ca4cffa6d..ca30c7decc1 100644 --- a/hapi-fhir-jpaserver-searchparam/src/main/java/ca/uhn/fhir/jpa/cache/ResourceChangeResult.java +++ b/hapi-fhir-jpaserver-searchparam/src/main/java/ca/uhn/fhir/jpa/cache/ResourceChangeResult.java @@ -1,6 +1,6 @@ /*- * #%L - * HAPI FHIR Search Parameters + * HAPI FHIR JPA - Search Parameters * %% * Copyright (C) 2014 - 2023 Smile CDR, Inc. * %% diff --git a/hapi-fhir-jpaserver-searchparam/src/main/java/ca/uhn/fhir/jpa/cache/ResourcePersistentIdMap.java b/hapi-fhir-jpaserver-searchparam/src/main/java/ca/uhn/fhir/jpa/cache/ResourcePersistentIdMap.java index 071dc8f1f20..ac01d8c0b54 100644 --- a/hapi-fhir-jpaserver-searchparam/src/main/java/ca/uhn/fhir/jpa/cache/ResourcePersistentIdMap.java +++ b/hapi-fhir-jpaserver-searchparam/src/main/java/ca/uhn/fhir/jpa/cache/ResourcePersistentIdMap.java @@ -1,6 +1,6 @@ /*- * #%L - * HAPI FHIR Search Parameters + * HAPI FHIR JPA - Search Parameters * %% * Copyright (C) 2014 - 2023 Smile CDR, Inc. * %% diff --git a/hapi-fhir-jpaserver-searchparam/src/main/java/ca/uhn/fhir/jpa/cache/ResourceVersionCache.java b/hapi-fhir-jpaserver-searchparam/src/main/java/ca/uhn/fhir/jpa/cache/ResourceVersionCache.java index 6b54a10a5ab..ec741ff7ab2 100644 --- a/hapi-fhir-jpaserver-searchparam/src/main/java/ca/uhn/fhir/jpa/cache/ResourceVersionCache.java +++ b/hapi-fhir-jpaserver-searchparam/src/main/java/ca/uhn/fhir/jpa/cache/ResourceVersionCache.java @@ -1,6 +1,6 @@ /*- * #%L - * HAPI FHIR Search Parameters + * HAPI FHIR JPA - Search Parameters * %% * Copyright (C) 2014 - 2023 Smile CDR, Inc. * %% diff --git a/hapi-fhir-jpaserver-searchparam/src/main/java/ca/uhn/fhir/jpa/cache/ResourceVersionMap.java b/hapi-fhir-jpaserver-searchparam/src/main/java/ca/uhn/fhir/jpa/cache/ResourceVersionMap.java index 81b7b4f580f..225db5831c1 100644 --- a/hapi-fhir-jpaserver-searchparam/src/main/java/ca/uhn/fhir/jpa/cache/ResourceVersionMap.java +++ b/hapi-fhir-jpaserver-searchparam/src/main/java/ca/uhn/fhir/jpa/cache/ResourceVersionMap.java @@ -1,6 +1,6 @@ /*- * #%L - * HAPI FHIR Search Parameters + * HAPI FHIR JPA - Search Parameters * %% * Copyright (C) 2014 - 2023 Smile CDR, Inc. * %% diff --git a/hapi-fhir-jpaserver-searchparam/src/main/java/ca/uhn/fhir/jpa/partition/IRequestPartitionHelperSvc.java b/hapi-fhir-jpaserver-searchparam/src/main/java/ca/uhn/fhir/jpa/partition/IRequestPartitionHelperSvc.java index ea03d2cb32d..235f4224b20 100644 --- a/hapi-fhir-jpaserver-searchparam/src/main/java/ca/uhn/fhir/jpa/partition/IRequestPartitionHelperSvc.java +++ b/hapi-fhir-jpaserver-searchparam/src/main/java/ca/uhn/fhir/jpa/partition/IRequestPartitionHelperSvc.java @@ -1,6 +1,6 @@ /*- * #%L - * HAPI FHIR Search Parameters + * HAPI FHIR JPA - Search Parameters * %% * Copyright (C) 2014 - 2023 Smile CDR, Inc. * %% @@ -23,12 +23,12 @@ import ca.uhn.fhir.interceptor.model.ReadPartitionIdRequestDetails; import ca.uhn.fhir.interceptor.model.RequestPartitionId; import ca.uhn.fhir.jpa.searchparam.SearchParameterMap; import ca.uhn.fhir.rest.api.server.RequestDetails; +import jakarta.annotation.Nonnull; +import jakarta.annotation.Nullable; import org.hl7.fhir.instance.model.api.IBaseResource; import org.hl7.fhir.instance.model.api.IIdType; import java.util.Set; -import javax.annotation.Nonnull; -import javax.annotation.Nullable; public interface IRequestPartitionHelperSvc { diff --git a/hapi-fhir-jpaserver-searchparam/src/main/java/ca/uhn/fhir/jpa/searchparam/MatchUrlService.java b/hapi-fhir-jpaserver-searchparam/src/main/java/ca/uhn/fhir/jpa/searchparam/MatchUrlService.java index f1d5a4a66b3..fcc2cc5e89b 100644 --- a/hapi-fhir-jpaserver-searchparam/src/main/java/ca/uhn/fhir/jpa/searchparam/MatchUrlService.java +++ b/hapi-fhir-jpaserver-searchparam/src/main/java/ca/uhn/fhir/jpa/searchparam/MatchUrlService.java @@ -1,6 +1,6 @@ /*- * #%L - * HAPI FHIR Search Parameters + * HAPI FHIR JPA - Search Parameters * %% * Copyright (C) 2014 - 2023 Smile CDR, Inc. * %% diff --git a/hapi-fhir-jpaserver-searchparam/src/main/java/ca/uhn/fhir/jpa/searchparam/ResourceMetaParams.java b/hapi-fhir-jpaserver-searchparam/src/main/java/ca/uhn/fhir/jpa/searchparam/ResourceMetaParams.java index 40a4658aeb5..78e0b475351 100644 --- a/hapi-fhir-jpaserver-searchparam/src/main/java/ca/uhn/fhir/jpa/searchparam/ResourceMetaParams.java +++ b/hapi-fhir-jpaserver-searchparam/src/main/java/ca/uhn/fhir/jpa/searchparam/ResourceMetaParams.java @@ -1,6 +1,6 @@ /*- * #%L - * HAPI FHIR Search Parameters + * HAPI FHIR JPA - Search Parameters * %% * Copyright (C) 2014 - 2023 Smile CDR, Inc. * %% @@ -51,6 +51,8 @@ public class ResourceMetaParams { Map>> resourceMetaAndParams = new HashMap<>(); resourceMetaParams.put(IAnyResource.SP_RES_ID, StringParam.class); resourceMetaAndParams.put(IAnyResource.SP_RES_ID, StringAndListParam.class); + resourceMetaParams.put(Constants.PARAM_PID, TokenParam.class); + resourceMetaAndParams.put(Constants.PARAM_PID, TokenAndListParam.class); resourceMetaParams.put(Constants.PARAM_TAG, TokenParam.class); resourceMetaAndParams.put(Constants.PARAM_TAG, TokenAndListParam.class); resourceMetaParams.put(Constants.PARAM_PROFILE, UriParam.class); diff --git a/hapi-fhir-jpaserver-searchparam/src/main/java/ca/uhn/fhir/jpa/searchparam/ResourceSearch.java b/hapi-fhir-jpaserver-searchparam/src/main/java/ca/uhn/fhir/jpa/searchparam/ResourceSearch.java index e37924741bf..9801b1468b1 100644 --- a/hapi-fhir-jpaserver-searchparam/src/main/java/ca/uhn/fhir/jpa/searchparam/ResourceSearch.java +++ b/hapi-fhir-jpaserver-searchparam/src/main/java/ca/uhn/fhir/jpa/searchparam/ResourceSearch.java @@ -1,6 +1,6 @@ /*- * #%L - * HAPI FHIR Search Parameters + * HAPI FHIR JPA - Search Parameters * %% * Copyright (C) 2014 - 2023 Smile CDR, Inc. * %% diff --git a/hapi-fhir-jpaserver-searchparam/src/main/java/ca/uhn/fhir/jpa/searchparam/SearchParamConstants.java b/hapi-fhir-jpaserver-searchparam/src/main/java/ca/uhn/fhir/jpa/searchparam/SearchParamConstants.java index 9478efdab78..4aa0a28bbc0 100644 --- a/hapi-fhir-jpaserver-searchparam/src/main/java/ca/uhn/fhir/jpa/searchparam/SearchParamConstants.java +++ b/hapi-fhir-jpaserver-searchparam/src/main/java/ca/uhn/fhir/jpa/searchparam/SearchParamConstants.java @@ -1,6 +1,6 @@ /*- * #%L - * HAPI FHIR Search Parameters + * HAPI FHIR JPA - Search Parameters * %% * Copyright (C) 2014 - 2023 Smile CDR, Inc. * %% diff --git a/hapi-fhir-jpaserver-searchparam/src/main/java/ca/uhn/fhir/jpa/searchparam/SearchParameterMap.java b/hapi-fhir-jpaserver-searchparam/src/main/java/ca/uhn/fhir/jpa/searchparam/SearchParameterMap.java index 0d5784a5818..7b7a56c2557 100644 --- a/hapi-fhir-jpaserver-searchparam/src/main/java/ca/uhn/fhir/jpa/searchparam/SearchParameterMap.java +++ b/hapi-fhir-jpaserver-searchparam/src/main/java/ca/uhn/fhir/jpa/searchparam/SearchParameterMap.java @@ -1,6 +1,6 @@ /* * #%L - * HAPI FHIR Search Parameters + * HAPI FHIR JPA - Search Parameters * %% * Copyright (C) 2014 - 2023 Smile CDR, Inc. * %% @@ -37,6 +37,7 @@ import ca.uhn.fhir.rest.param.QuantityParam; import ca.uhn.fhir.rest.param.TokenParamModifier; import ca.uhn.fhir.util.UrlUtil; import com.fasterxml.jackson.annotation.JsonIgnore; +import jakarta.annotation.Nonnull; import org.apache.commons.lang3.StringUtils; import org.apache.commons.lang3.Validate; import org.apache.commons.lang3.builder.CompareToBuilder; @@ -55,7 +56,6 @@ import java.util.List; import java.util.Map; import java.util.Set; import java.util.stream.Collectors; -import javax.annotation.Nonnull; import static ca.uhn.fhir.rest.param.ParamPrefixEnum.GREATERTHAN_OR_EQUALS; import static ca.uhn.fhir.rest.param.ParamPrefixEnum.LESSTHAN_OR_EQUALS; diff --git a/hapi-fhir-jpaserver-searchparam/src/main/java/ca/uhn/fhir/jpa/searchparam/config/NicknameServiceConfig.java b/hapi-fhir-jpaserver-searchparam/src/main/java/ca/uhn/fhir/jpa/searchparam/config/NicknameServiceConfig.java index 8aca1889e20..4ac537f3c2f 100644 --- a/hapi-fhir-jpaserver-searchparam/src/main/java/ca/uhn/fhir/jpa/searchparam/config/NicknameServiceConfig.java +++ b/hapi-fhir-jpaserver-searchparam/src/main/java/ca/uhn/fhir/jpa/searchparam/config/NicknameServiceConfig.java @@ -1,6 +1,6 @@ /*- * #%L - * HAPI FHIR Search Parameters + * HAPI FHIR JPA - Search Parameters * %% * Copyright (C) 2014 - 2023 Smile CDR, Inc. * %% diff --git a/hapi-fhir-jpaserver-searchparam/src/main/java/ca/uhn/fhir/jpa/searchparam/config/SearchParamConfig.java b/hapi-fhir-jpaserver-searchparam/src/main/java/ca/uhn/fhir/jpa/searchparam/config/SearchParamConfig.java index f1522621382..aa6c5233b40 100644 --- a/hapi-fhir-jpaserver-searchparam/src/main/java/ca/uhn/fhir/jpa/searchparam/config/SearchParamConfig.java +++ b/hapi-fhir-jpaserver-searchparam/src/main/java/ca/uhn/fhir/jpa/searchparam/config/SearchParamConfig.java @@ -1,6 +1,6 @@ /*- * #%L - * HAPI FHIR Search Parameters + * HAPI FHIR JPA - Search Parameters * %% * Copyright (C) 2014 - 2023 Smile CDR, Inc. * %% diff --git a/hapi-fhir-jpaserver-searchparam/src/main/java/ca/uhn/fhir/jpa/searchparam/extractor/BaseSearchParamExtractor.java b/hapi-fhir-jpaserver-searchparam/src/main/java/ca/uhn/fhir/jpa/searchparam/extractor/BaseSearchParamExtractor.java index a4a4425071c..32509e6ffb7 100644 --- a/hapi-fhir-jpaserver-searchparam/src/main/java/ca/uhn/fhir/jpa/searchparam/extractor/BaseSearchParamExtractor.java +++ b/hapi-fhir-jpaserver-searchparam/src/main/java/ca/uhn/fhir/jpa/searchparam/extractor/BaseSearchParamExtractor.java @@ -1,6 +1,6 @@ /* * #%L - * HAPI FHIR Search Parameters + * HAPI FHIR JPA - Search Parameters * %% * Copyright (C) 2014 - 2023 Smile CDR, Inc. * %% @@ -49,6 +49,9 @@ import ca.uhn.fhir.util.UrlUtil; import ca.uhn.fhir.util.bundle.BundleEntryParts; import com.google.common.annotations.VisibleForTesting; import com.google.common.collect.Sets; +import jakarta.annotation.Nonnull; +import jakarta.annotation.Nullable; +import jakarta.annotation.PostConstruct; import org.apache.commons.lang3.ObjectUtils; import org.apache.commons.lang3.StringUtils; import org.apache.commons.lang3.Validate; @@ -63,6 +66,7 @@ import org.hl7.fhir.instance.model.api.IBaseReference; import org.hl7.fhir.instance.model.api.IBaseResource; import org.hl7.fhir.instance.model.api.IIdType; import org.hl7.fhir.instance.model.api.IPrimitiveType; +import org.hl7.fhir.r4.model.IdType; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.context.ApplicationContext; @@ -79,9 +83,6 @@ import java.util.Optional; import java.util.Set; import java.util.TreeSet; import java.util.stream.Collectors; -import javax.annotation.Nonnull; -import javax.annotation.Nullable; -import javax.annotation.PostConstruct; import javax.measure.quantity.Quantity; import javax.measure.unit.NonSI; import javax.measure.unit.Unit; @@ -2010,17 +2011,31 @@ public abstract class BaseSearchParamExtractor implements ISearchParamExtractor * references within a Bundle */ if (theAppContext instanceof IBaseBundle && isNotBlank(theUrl) && !theUrl.startsWith("#")) { + String unqualifiedVersionlessReference; + boolean isPlaceholderReference; + if (theUrl.startsWith("urn:")) { + isPlaceholderReference = true; + unqualifiedVersionlessReference = null; + } else { + isPlaceholderReference = false; + unqualifiedVersionlessReference = + new IdType(theUrl).toUnqualifiedVersionless().getValue(); + } + List entries = BundleUtil.toListOfEntries(getContext(), (IBaseBundle) theAppContext); for (BundleEntryParts next : entries) { if (next.getResource() != null) { - if (theUrl.startsWith("urn:uuid:")) { + if (isPlaceholderReference) { if (theUrl.equals(next.getUrl()) || theUrl.equals( next.getResource().getIdElement().getValue())) { return (T) next.getResource(); } } else { - if (theUrl.equals(next.getResource().getIdElement().getValue())) { + if (unqualifiedVersionlessReference.equals(next.getResource() + .getIdElement() + .toUnqualifiedVersionless() + .getValue())) { return (T) next.getResource(); } } diff --git a/hapi-fhir-jpaserver-searchparam/src/main/java/ca/uhn/fhir/jpa/searchparam/extractor/CrossPartitionReferenceDetails.java b/hapi-fhir-jpaserver-searchparam/src/main/java/ca/uhn/fhir/jpa/searchparam/extractor/CrossPartitionReferenceDetails.java index e7f9c352a24..569c0cb89af 100644 --- a/hapi-fhir-jpaserver-searchparam/src/main/java/ca/uhn/fhir/jpa/searchparam/extractor/CrossPartitionReferenceDetails.java +++ b/hapi-fhir-jpaserver-searchparam/src/main/java/ca/uhn/fhir/jpa/searchparam/extractor/CrossPartitionReferenceDetails.java @@ -1,6 +1,6 @@ /*- * #%L - * HAPI FHIR Search Parameters + * HAPI FHIR JPA - Search Parameters * %% * Copyright (C) 2014 - 2023 Smile CDR, Inc. * %% @@ -22,8 +22,7 @@ package ca.uhn.fhir.jpa.searchparam.extractor; import ca.uhn.fhir.interceptor.model.RequestPartitionId; import ca.uhn.fhir.rest.api.server.RequestDetails; import ca.uhn.fhir.rest.api.server.storage.TransactionDetails; - -import javax.annotation.Nonnull; +import jakarta.annotation.Nonnull; public class CrossPartitionReferenceDetails { diff --git a/hapi-fhir-jpaserver-searchparam/src/main/java/ca/uhn/fhir/jpa/searchparam/extractor/GeopointNormalizer.java b/hapi-fhir-jpaserver-searchparam/src/main/java/ca/uhn/fhir/jpa/searchparam/extractor/GeopointNormalizer.java index 4d691e07b56..d5bc0957fab 100644 --- a/hapi-fhir-jpaserver-searchparam/src/main/java/ca/uhn/fhir/jpa/searchparam/extractor/GeopointNormalizer.java +++ b/hapi-fhir-jpaserver-searchparam/src/main/java/ca/uhn/fhir/jpa/searchparam/extractor/GeopointNormalizer.java @@ -1,6 +1,6 @@ /*- * #%L - * HAPI FHIR Search Parameters + * HAPI FHIR JPA - Search Parameters * %% * Copyright (C) 2014 - 2023 Smile CDR, Inc. * %% diff --git a/hapi-fhir-jpaserver-searchparam/src/main/java/ca/uhn/fhir/jpa/searchparam/extractor/IResourceLinkResolver.java b/hapi-fhir-jpaserver-searchparam/src/main/java/ca/uhn/fhir/jpa/searchparam/extractor/IResourceLinkResolver.java index dee9d778828..e837a65d084 100644 --- a/hapi-fhir-jpaserver-searchparam/src/main/java/ca/uhn/fhir/jpa/searchparam/extractor/IResourceLinkResolver.java +++ b/hapi-fhir-jpaserver-searchparam/src/main/java/ca/uhn/fhir/jpa/searchparam/extractor/IResourceLinkResolver.java @@ -1,6 +1,6 @@ /*- * #%L - * HAPI FHIR Search Parameters + * HAPI FHIR JPA - Search Parameters * %% * Copyright (C) 2014 - 2023 Smile CDR, Inc. * %% @@ -23,11 +23,10 @@ import ca.uhn.fhir.interceptor.model.RequestPartitionId; import ca.uhn.fhir.jpa.model.cross.IResourceLookup; import ca.uhn.fhir.rest.api.server.RequestDetails; import ca.uhn.fhir.rest.api.server.storage.TransactionDetails; +import jakarta.annotation.Nonnull; +import jakarta.annotation.Nullable; import org.hl7.fhir.instance.model.api.IBaseResource; -import javax.annotation.Nonnull; -import javax.annotation.Nullable; - public interface IResourceLinkResolver { /** diff --git a/hapi-fhir-jpaserver-searchparam/src/main/java/ca/uhn/fhir/jpa/searchparam/extractor/ISearchParamExtractor.java b/hapi-fhir-jpaserver-searchparam/src/main/java/ca/uhn/fhir/jpa/searchparam/extractor/ISearchParamExtractor.java index fe137364848..29e3eb7b2e2 100644 --- a/hapi-fhir-jpaserver-searchparam/src/main/java/ca/uhn/fhir/jpa/searchparam/extractor/ISearchParamExtractor.java +++ b/hapi-fhir-jpaserver-searchparam/src/main/java/ca/uhn/fhir/jpa/searchparam/extractor/ISearchParamExtractor.java @@ -1,6 +1,6 @@ /* * #%L - * HAPI FHIR Search Parameters + * HAPI FHIR JPA - Search Parameters * %% * Copyright (C) 2014 - 2023 Smile CDR, Inc. * %% diff --git a/hapi-fhir-jpaserver-searchparam/src/main/java/ca/uhn/fhir/jpa/searchparam/extractor/LogicalReferenceHelper.java b/hapi-fhir-jpaserver-searchparam/src/main/java/ca/uhn/fhir/jpa/searchparam/extractor/LogicalReferenceHelper.java index db6bf9bff5e..63f5e14f47d 100644 --- a/hapi-fhir-jpaserver-searchparam/src/main/java/ca/uhn/fhir/jpa/searchparam/extractor/LogicalReferenceHelper.java +++ b/hapi-fhir-jpaserver-searchparam/src/main/java/ca/uhn/fhir/jpa/searchparam/extractor/LogicalReferenceHelper.java @@ -1,6 +1,6 @@ /*- * #%L - * HAPI FHIR Search Parameters + * HAPI FHIR JPA - Search Parameters * %% * Copyright (C) 2014 - 2023 Smile CDR, Inc. * %% diff --git a/hapi-fhir-jpaserver-searchparam/src/main/java/ca/uhn/fhir/jpa/searchparam/extractor/PathAndRef.java b/hapi-fhir-jpaserver-searchparam/src/main/java/ca/uhn/fhir/jpa/searchparam/extractor/PathAndRef.java index a649c0601e1..027fb363c57 100644 --- a/hapi-fhir-jpaserver-searchparam/src/main/java/ca/uhn/fhir/jpa/searchparam/extractor/PathAndRef.java +++ b/hapi-fhir-jpaserver-searchparam/src/main/java/ca/uhn/fhir/jpa/searchparam/extractor/PathAndRef.java @@ -1,6 +1,6 @@ /* * #%L - * HAPI FHIR Search Parameters + * HAPI FHIR JPA - Search Parameters * %% * Copyright (C) 2014 - 2023 Smile CDR, Inc. * %% diff --git a/hapi-fhir-jpaserver-searchparam/src/main/java/ca/uhn/fhir/jpa/searchparam/extractor/ResourceIndexedSearchParamComposite.java b/hapi-fhir-jpaserver-searchparam/src/main/java/ca/uhn/fhir/jpa/searchparam/extractor/ResourceIndexedSearchParamComposite.java index dfd41f27635..2ca11dd124f 100644 --- a/hapi-fhir-jpaserver-searchparam/src/main/java/ca/uhn/fhir/jpa/searchparam/extractor/ResourceIndexedSearchParamComposite.java +++ b/hapi-fhir-jpaserver-searchparam/src/main/java/ca/uhn/fhir/jpa/searchparam/extractor/ResourceIndexedSearchParamComposite.java @@ -1,6 +1,6 @@ /*- * #%L - * HAPI FHIR Search Parameters + * HAPI FHIR JPA - Search Parameters * %% * Copyright (C) 2014 - 2023 Smile CDR, Inc. * %% diff --git a/hapi-fhir-jpaserver-searchparam/src/main/java/ca/uhn/fhir/jpa/searchparam/extractor/ResourceIndexedSearchParams.java b/hapi-fhir-jpaserver-searchparam/src/main/java/ca/uhn/fhir/jpa/searchparam/extractor/ResourceIndexedSearchParams.java index 3af3d4c2986..9d287030750 100644 --- a/hapi-fhir-jpaserver-searchparam/src/main/java/ca/uhn/fhir/jpa/searchparam/extractor/ResourceIndexedSearchParams.java +++ b/hapi-fhir-jpaserver-searchparam/src/main/java/ca/uhn/fhir/jpa/searchparam/extractor/ResourceIndexedSearchParams.java @@ -1,6 +1,6 @@ /*- * #%L - * HAPI FHIR Search Parameters + * HAPI FHIR JPA - Search Parameters * %% * Copyright (C) 2014 - 2023 Smile CDR, Inc. * %% @@ -31,6 +31,7 @@ import ca.uhn.fhir.rest.api.RestSearchParameterTypeEnum; import ca.uhn.fhir.rest.param.QuantityParam; import ca.uhn.fhir.rest.param.ReferenceParam; import ca.uhn.fhir.rest.server.util.ResourceSearchParams; +import jakarta.annotation.Nonnull; import org.apache.commons.lang3.StringUtils; import java.util.ArrayList; @@ -41,7 +42,6 @@ import java.util.HashSet; import java.util.List; import java.util.Set; import java.util.function.Predicate; -import javax.annotation.Nonnull; import static org.apache.commons.lang3.StringUtils.compare; import static org.apache.commons.lang3.StringUtils.isNotBlank; diff --git a/hapi-fhir-jpaserver-searchparam/src/main/java/ca/uhn/fhir/jpa/searchparam/extractor/SearchParamExtractorDstu2.java b/hapi-fhir-jpaserver-searchparam/src/main/java/ca/uhn/fhir/jpa/searchparam/extractor/SearchParamExtractorDstu2.java index 66d1bb1a047..983638e9e31 100644 --- a/hapi-fhir-jpaserver-searchparam/src/main/java/ca/uhn/fhir/jpa/searchparam/extractor/SearchParamExtractorDstu2.java +++ b/hapi-fhir-jpaserver-searchparam/src/main/java/ca/uhn/fhir/jpa/searchparam/extractor/SearchParamExtractorDstu2.java @@ -1,6 +1,6 @@ /* * #%L - * HAPI FHIR Search Parameters + * HAPI FHIR JPA - Search Parameters * %% * Copyright (C) 2014 - 2023 Smile CDR, Inc. * %% diff --git a/hapi-fhir-jpaserver-searchparam/src/main/java/ca/uhn/fhir/jpa/searchparam/extractor/SearchParamExtractorDstu3.java b/hapi-fhir-jpaserver-searchparam/src/main/java/ca/uhn/fhir/jpa/searchparam/extractor/SearchParamExtractorDstu3.java index a7e94c3ef19..f1e87737f5b 100644 --- a/hapi-fhir-jpaserver-searchparam/src/main/java/ca/uhn/fhir/jpa/searchparam/extractor/SearchParamExtractorDstu3.java +++ b/hapi-fhir-jpaserver-searchparam/src/main/java/ca/uhn/fhir/jpa/searchparam/extractor/SearchParamExtractorDstu3.java @@ -1,6 +1,6 @@ /* * #%L - * HAPI FHIR Search Parameters + * HAPI FHIR JPA - Search Parameters * %% * Copyright (C) 2014 - 2023 Smile CDR, Inc. * %% @@ -24,6 +24,7 @@ import ca.uhn.fhir.jpa.model.config.PartitionSettings; import ca.uhn.fhir.jpa.model.entity.StorageSettings; import ca.uhn.fhir.rest.server.util.ISearchParamRegistry; import com.google.common.annotations.VisibleForTesting; +import jakarta.annotation.PostConstruct; import org.hl7.fhir.dstu3.context.IWorkerContext; import org.hl7.fhir.dstu3.hapi.ctx.HapiWorkerContext; import org.hl7.fhir.dstu3.model.Base; @@ -32,7 +33,6 @@ import org.hl7.fhir.instance.model.api.IBase; import java.util.ArrayList; import java.util.List; -import javax.annotation.PostConstruct; public class SearchParamExtractorDstu3 extends BaseSearchParamExtractor implements ISearchParamExtractor { diff --git a/hapi-fhir-jpaserver-searchparam/src/main/java/ca/uhn/fhir/jpa/searchparam/extractor/SearchParamExtractorR4.java b/hapi-fhir-jpaserver-searchparam/src/main/java/ca/uhn/fhir/jpa/searchparam/extractor/SearchParamExtractorR4.java index 6b22d38b625..bc82a49e0ca 100644 --- a/hapi-fhir-jpaserver-searchparam/src/main/java/ca/uhn/fhir/jpa/searchparam/extractor/SearchParamExtractorR4.java +++ b/hapi-fhir-jpaserver-searchparam/src/main/java/ca/uhn/fhir/jpa/searchparam/extractor/SearchParamExtractorR4.java @@ -1,6 +1,6 @@ /* * #%L - * HAPI FHIR Search Parameters + * HAPI FHIR JPA - Search Parameters * %% * Copyright (C) 2014 - 2023 Smile CDR, Inc. * %% @@ -26,6 +26,7 @@ import ca.uhn.fhir.rest.server.util.ISearchParamRegistry; import ca.uhn.fhir.sl.cache.Cache; import ca.uhn.fhir.sl.cache.CacheFactory; import com.google.common.annotations.VisibleForTesting; +import jakarta.annotation.PostConstruct; import org.hl7.fhir.exceptions.FHIRException; import org.hl7.fhir.exceptions.PathEngineException; import org.hl7.fhir.instance.model.api.IBase; @@ -46,7 +47,6 @@ import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.concurrent.TimeUnit; -import javax.annotation.PostConstruct; import static org.apache.commons.lang3.StringUtils.isNotBlank; diff --git a/hapi-fhir-jpaserver-searchparam/src/main/java/ca/uhn/fhir/jpa/searchparam/extractor/SearchParamExtractorR4B.java b/hapi-fhir-jpaserver-searchparam/src/main/java/ca/uhn/fhir/jpa/searchparam/extractor/SearchParamExtractorR4B.java index 6ff518529a4..4add6601f1f 100644 --- a/hapi-fhir-jpaserver-searchparam/src/main/java/ca/uhn/fhir/jpa/searchparam/extractor/SearchParamExtractorR4B.java +++ b/hapi-fhir-jpaserver-searchparam/src/main/java/ca/uhn/fhir/jpa/searchparam/extractor/SearchParamExtractorR4B.java @@ -1,6 +1,6 @@ /* * #%L - * HAPI FHIR Search Parameters + * HAPI FHIR JPA - Search Parameters * %% * Copyright (C) 2014 - 2023 Smile CDR, Inc. * %% @@ -26,6 +26,7 @@ import ca.uhn.fhir.rest.server.util.ISearchParamRegistry; import ca.uhn.fhir.sl.cache.Cache; import ca.uhn.fhir.sl.cache.CacheFactory; import com.google.common.annotations.VisibleForTesting; +import jakarta.annotation.PostConstruct; import org.hl7.fhir.exceptions.FHIRException; import org.hl7.fhir.exceptions.PathEngineException; import org.hl7.fhir.instance.model.api.IBase; @@ -46,7 +47,6 @@ import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.concurrent.TimeUnit; -import javax.annotation.PostConstruct; import static org.apache.commons.lang3.StringUtils.isNotBlank; diff --git a/hapi-fhir-jpaserver-searchparam/src/main/java/ca/uhn/fhir/jpa/searchparam/extractor/SearchParamExtractorR5.java b/hapi-fhir-jpaserver-searchparam/src/main/java/ca/uhn/fhir/jpa/searchparam/extractor/SearchParamExtractorR5.java index 05ae9608475..44625b53a97 100644 --- a/hapi-fhir-jpaserver-searchparam/src/main/java/ca/uhn/fhir/jpa/searchparam/extractor/SearchParamExtractorR5.java +++ b/hapi-fhir-jpaserver-searchparam/src/main/java/ca/uhn/fhir/jpa/searchparam/extractor/SearchParamExtractorR5.java @@ -1,6 +1,6 @@ /* * #%L - * HAPI FHIR Search Parameters + * HAPI FHIR JPA - Search Parameters * %% * Copyright (C) 2014 - 2023 Smile CDR, Inc. * %% @@ -25,6 +25,7 @@ import ca.uhn.fhir.jpa.model.entity.StorageSettings; import ca.uhn.fhir.rest.server.util.ISearchParamRegistry; import ca.uhn.fhir.sl.cache.Cache; import ca.uhn.fhir.sl.cache.CacheFactory; +import jakarta.annotation.PostConstruct; import org.hl7.fhir.exceptions.FHIRException; import org.hl7.fhir.exceptions.PathEngineException; import org.hl7.fhir.instance.model.api.IBase; @@ -45,7 +46,6 @@ import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.concurrent.TimeUnit; -import javax.annotation.PostConstruct; import static org.apache.commons.lang3.StringUtils.isNotBlank; diff --git a/hapi-fhir-jpaserver-searchparam/src/main/java/ca/uhn/fhir/jpa/searchparam/extractor/SearchParamExtractorService.java b/hapi-fhir-jpaserver-searchparam/src/main/java/ca/uhn/fhir/jpa/searchparam/extractor/SearchParamExtractorService.java index 01e288499c4..1832bb9c2c9 100644 --- a/hapi-fhir-jpaserver-searchparam/src/main/java/ca/uhn/fhir/jpa/searchparam/extractor/SearchParamExtractorService.java +++ b/hapi-fhir-jpaserver-searchparam/src/main/java/ca/uhn/fhir/jpa/searchparam/extractor/SearchParamExtractorService.java @@ -1,6 +1,6 @@ /*- * #%L - * HAPI FHIR Search Parameters + * HAPI FHIR JPA - Search Parameters * %% * Copyright (C) 2014 - 2023 Smile CDR, Inc. * %% @@ -60,6 +60,8 @@ import ca.uhn.fhir.rest.server.util.ISearchParamRegistry; import ca.uhn.fhir.rest.server.util.ResourceSearchParams; import ca.uhn.fhir.util.FhirTerser; import com.google.common.annotations.VisibleForTesting; +import jakarta.annotation.Nonnull; +import jakarta.annotation.Nullable; import org.apache.commons.lang3.StringUtils; import org.hl7.fhir.instance.model.api.IBaseReference; import org.hl7.fhir.instance.model.api.IBaseResource; @@ -77,8 +79,6 @@ import java.util.Map; import java.util.Optional; import java.util.Set; import java.util.stream.Collectors; -import javax.annotation.Nonnull; -import javax.annotation.Nullable; import static org.apache.commons.lang3.StringUtils.isBlank; import static org.apache.commons.lang3.StringUtils.isNotBlank; diff --git a/hapi-fhir-jpaserver-searchparam/src/main/java/ca/uhn/fhir/jpa/searchparam/extractor/StringTrimmingTrimmerMatcher.java b/hapi-fhir-jpaserver-searchparam/src/main/java/ca/uhn/fhir/jpa/searchparam/extractor/StringTrimmingTrimmerMatcher.java index f091bdf3c1d..cc40da8191e 100644 --- a/hapi-fhir-jpaserver-searchparam/src/main/java/ca/uhn/fhir/jpa/searchparam/extractor/StringTrimmingTrimmerMatcher.java +++ b/hapi-fhir-jpaserver-searchparam/src/main/java/ca/uhn/fhir/jpa/searchparam/extractor/StringTrimmingTrimmerMatcher.java @@ -1,6 +1,6 @@ /*- * #%L - * HAPI FHIR Search Parameters + * HAPI FHIR JPA - Search Parameters * %% * Copyright (C) 2014 - 2023 Smile CDR, Inc. * %% diff --git a/hapi-fhir-jpaserver-searchparam/src/main/java/ca/uhn/fhir/jpa/searchparam/matcher/AuthorizationSearchParamMatcher.java b/hapi-fhir-jpaserver-searchparam/src/main/java/ca/uhn/fhir/jpa/searchparam/matcher/AuthorizationSearchParamMatcher.java index 25366064cfd..b8fb38ebaca 100644 --- a/hapi-fhir-jpaserver-searchparam/src/main/java/ca/uhn/fhir/jpa/searchparam/matcher/AuthorizationSearchParamMatcher.java +++ b/hapi-fhir-jpaserver-searchparam/src/main/java/ca/uhn/fhir/jpa/searchparam/matcher/AuthorizationSearchParamMatcher.java @@ -1,6 +1,6 @@ /*- * #%L - * HAPI FHIR Search Parameters + * HAPI FHIR JPA - Search Parameters * %% * Copyright (C) 2014 - 2023 Smile CDR, Inc. * %% diff --git a/hapi-fhir-jpaserver-searchparam/src/main/java/ca/uhn/fhir/jpa/searchparam/matcher/InMemoryMatchResult.java b/hapi-fhir-jpaserver-searchparam/src/main/java/ca/uhn/fhir/jpa/searchparam/matcher/InMemoryMatchResult.java index 06cc6c57715..c7cf4e0fe07 100644 --- a/hapi-fhir-jpaserver-searchparam/src/main/java/ca/uhn/fhir/jpa/searchparam/matcher/InMemoryMatchResult.java +++ b/hapi-fhir-jpaserver-searchparam/src/main/java/ca/uhn/fhir/jpa/searchparam/matcher/InMemoryMatchResult.java @@ -1,6 +1,6 @@ /*- * #%L - * HAPI FHIR Search Parameters + * HAPI FHIR JPA - Search Parameters * %% * Copyright (C) 2014 - 2023 Smile CDR, Inc. * %% diff --git a/hapi-fhir-jpaserver-searchparam/src/main/java/ca/uhn/fhir/jpa/searchparam/matcher/InMemoryResourceMatcher.java b/hapi-fhir-jpaserver-searchparam/src/main/java/ca/uhn/fhir/jpa/searchparam/matcher/InMemoryResourceMatcher.java index 829b369867e..4efaf6d2395 100644 --- a/hapi-fhir-jpaserver-searchparam/src/main/java/ca/uhn/fhir/jpa/searchparam/matcher/InMemoryResourceMatcher.java +++ b/hapi-fhir-jpaserver-searchparam/src/main/java/ca/uhn/fhir/jpa/searchparam/matcher/InMemoryResourceMatcher.java @@ -1,6 +1,6 @@ /*- * #%L - * HAPI FHIR Search Parameters + * HAPI FHIR JPA - Search Parameters * %% * Copyright (C) 2014 - 2023 Smile CDR, Inc. * %% @@ -51,6 +51,8 @@ import ca.uhn.fhir.rest.server.util.ISearchParamRegistry; import ca.uhn.fhir.util.MetaUtil; import ca.uhn.fhir.util.UrlUtil; import com.google.common.collect.Sets; +import jakarta.annotation.Nonnull; +import jakarta.annotation.Nullable; import org.apache.commons.lang3.StringUtils; import org.apache.commons.lang3.Validate; import org.hl7.fhir.dstu3.model.Location; @@ -68,8 +70,6 @@ import java.util.List; import java.util.Map; import java.util.Set; import java.util.stream.Collectors; -import javax.annotation.Nonnull; -import javax.annotation.Nullable; import static org.apache.commons.lang3.StringUtils.isBlank; import static org.apache.commons.lang3.StringUtils.isNotBlank; diff --git a/hapi-fhir-jpaserver-searchparam/src/main/java/ca/uhn/fhir/jpa/searchparam/matcher/IndexedSearchParamExtractor.java b/hapi-fhir-jpaserver-searchparam/src/main/java/ca/uhn/fhir/jpa/searchparam/matcher/IndexedSearchParamExtractor.java index 3deafea8642..c36e59bd9cb 100644 --- a/hapi-fhir-jpaserver-searchparam/src/main/java/ca/uhn/fhir/jpa/searchparam/matcher/IndexedSearchParamExtractor.java +++ b/hapi-fhir-jpaserver-searchparam/src/main/java/ca/uhn/fhir/jpa/searchparam/matcher/IndexedSearchParamExtractor.java @@ -1,6 +1,6 @@ /*- * #%L - * HAPI FHIR Search Parameters + * HAPI FHIR JPA - Search Parameters * %% * Copyright (C) 2014 - 2023 Smile CDR, Inc. * %% @@ -26,11 +26,10 @@ import ca.uhn.fhir.jpa.searchparam.extractor.ResourceIndexedSearchParams; import ca.uhn.fhir.jpa.searchparam.extractor.SearchParamExtractorService; import ca.uhn.fhir.rest.api.server.RequestDetails; import ca.uhn.fhir.rest.api.server.storage.TransactionDetails; +import jakarta.annotation.Nonnull; import org.hl7.fhir.instance.model.api.IBaseResource; import org.springframework.beans.factory.annotation.Autowired; -import javax.annotation.Nonnull; - public class IndexedSearchParamExtractor { @Autowired private FhirContext myContext; diff --git a/hapi-fhir-jpaserver-searchparam/src/main/java/ca/uhn/fhir/jpa/searchparam/matcher/SearchParamMatcher.java b/hapi-fhir-jpaserver-searchparam/src/main/java/ca/uhn/fhir/jpa/searchparam/matcher/SearchParamMatcher.java index 75367cca4bc..1f73d8bf1e0 100644 --- a/hapi-fhir-jpaserver-searchparam/src/main/java/ca/uhn/fhir/jpa/searchparam/matcher/SearchParamMatcher.java +++ b/hapi-fhir-jpaserver-searchparam/src/main/java/ca/uhn/fhir/jpa/searchparam/matcher/SearchParamMatcher.java @@ -1,6 +1,6 @@ /*- * #%L - * HAPI FHIR Search Parameters + * HAPI FHIR JPA - Search Parameters * %% * Copyright (C) 2014 - 2023 Smile CDR, Inc. * %% diff --git a/hapi-fhir-jpaserver-searchparam/src/main/java/ca/uhn/fhir/jpa/searchparam/nickname/NicknameInterceptor.java b/hapi-fhir-jpaserver-searchparam/src/main/java/ca/uhn/fhir/jpa/searchparam/nickname/NicknameInterceptor.java index 3dcf5fec1e4..11081c200c7 100644 --- a/hapi-fhir-jpaserver-searchparam/src/main/java/ca/uhn/fhir/jpa/searchparam/nickname/NicknameInterceptor.java +++ b/hapi-fhir-jpaserver-searchparam/src/main/java/ca/uhn/fhir/jpa/searchparam/nickname/NicknameInterceptor.java @@ -1,6 +1,6 @@ /*- * #%L - * HAPI FHIR Search Parameters + * HAPI FHIR JPA - Search Parameters * %% * Copyright (C) 2014 - 2023 Smile CDR, Inc. * %% diff --git a/hapi-fhir-jpaserver-searchparam/src/main/java/ca/uhn/fhir/jpa/searchparam/provider/SearchableHashMapResourceProvider.java b/hapi-fhir-jpaserver-searchparam/src/main/java/ca/uhn/fhir/jpa/searchparam/provider/SearchableHashMapResourceProvider.java index 41aed01c271..b5f8b513d94 100644 --- a/hapi-fhir-jpaserver-searchparam/src/main/java/ca/uhn/fhir/jpa/searchparam/provider/SearchableHashMapResourceProvider.java +++ b/hapi-fhir-jpaserver-searchparam/src/main/java/ca/uhn/fhir/jpa/searchparam/provider/SearchableHashMapResourceProvider.java @@ -1,6 +1,6 @@ /*- * #%L - * HAPI FHIR Search Parameters + * HAPI FHIR JPA - Search Parameters * %% * Copyright (C) 2014 - 2023 Smile CDR, Inc. * %% diff --git a/hapi-fhir-jpaserver-searchparam/src/main/java/ca/uhn/fhir/jpa/searchparam/registry/ISearchParamProvider.java b/hapi-fhir-jpaserver-searchparam/src/main/java/ca/uhn/fhir/jpa/searchparam/registry/ISearchParamProvider.java index ac050c36b9c..de4fb681c85 100644 --- a/hapi-fhir-jpaserver-searchparam/src/main/java/ca/uhn/fhir/jpa/searchparam/registry/ISearchParamProvider.java +++ b/hapi-fhir-jpaserver-searchparam/src/main/java/ca/uhn/fhir/jpa/searchparam/registry/ISearchParamProvider.java @@ -1,6 +1,6 @@ /*- * #%L - * HAPI FHIR Search Parameters + * HAPI FHIR JPA - Search Parameters * %% * Copyright (C) 2014 - 2023 Smile CDR, Inc. * %% diff --git a/hapi-fhir-jpaserver-searchparam/src/main/java/ca/uhn/fhir/jpa/searchparam/registry/ISearchParamRegistryController.java b/hapi-fhir-jpaserver-searchparam/src/main/java/ca/uhn/fhir/jpa/searchparam/registry/ISearchParamRegistryController.java index 35c9d14c4c2..c113b118b20 100644 --- a/hapi-fhir-jpaserver-searchparam/src/main/java/ca/uhn/fhir/jpa/searchparam/registry/ISearchParamRegistryController.java +++ b/hapi-fhir-jpaserver-searchparam/src/main/java/ca/uhn/fhir/jpa/searchparam/registry/ISearchParamRegistryController.java @@ -1,6 +1,6 @@ /*- * #%L - * HAPI FHIR Search Parameters + * HAPI FHIR JPA - Search Parameters * %% * Copyright (C) 2014 - 2023 Smile CDR, Inc. * %% diff --git a/hapi-fhir-jpaserver-searchparam/src/main/java/ca/uhn/fhir/jpa/searchparam/registry/JpaSearchParamCache.java b/hapi-fhir-jpaserver-searchparam/src/main/java/ca/uhn/fhir/jpa/searchparam/registry/JpaSearchParamCache.java index d1866bfcb3a..b6d88cafefc 100644 --- a/hapi-fhir-jpaserver-searchparam/src/main/java/ca/uhn/fhir/jpa/searchparam/registry/JpaSearchParamCache.java +++ b/hapi-fhir-jpaserver-searchparam/src/main/java/ca/uhn/fhir/jpa/searchparam/registry/JpaSearchParamCache.java @@ -1,6 +1,6 @@ /*- * #%L - * HAPI FHIR Search Parameters + * HAPI FHIR JPA - Search Parameters * %% * Copyright (C) 2014 - 2023 Smile CDR, Inc. * %% diff --git a/hapi-fhir-jpaserver-searchparam/src/main/java/ca/uhn/fhir/jpa/searchparam/registry/ReadOnlySearchParamCache.java b/hapi-fhir-jpaserver-searchparam/src/main/java/ca/uhn/fhir/jpa/searchparam/registry/ReadOnlySearchParamCache.java index a9c86f1fa8a..bee998af5bc 100644 --- a/hapi-fhir-jpaserver-searchparam/src/main/java/ca/uhn/fhir/jpa/searchparam/registry/ReadOnlySearchParamCache.java +++ b/hapi-fhir-jpaserver-searchparam/src/main/java/ca/uhn/fhir/jpa/searchparam/registry/ReadOnlySearchParamCache.java @@ -1,6 +1,6 @@ /*- * #%L - * HAPI FHIR Search Parameters + * HAPI FHIR JPA - Search Parameters * %% * Copyright (C) 2014 - 2023 Smile CDR, Inc. * %% @@ -26,6 +26,8 @@ import ca.uhn.fhir.context.RuntimeSearchParam; import ca.uhn.fhir.rest.server.util.ResourceSearchParams; import ca.uhn.fhir.util.BundleUtil; import ca.uhn.fhir.util.ClasspathUtil; +import jakarta.annotation.Nonnull; +import jakarta.annotation.Nullable; import org.apache.commons.lang3.Validate; import org.hl7.fhir.instance.model.api.IBaseBundle; import org.hl7.fhir.instance.model.api.IBaseResource; @@ -37,8 +39,6 @@ import java.util.List; import java.util.Map; import java.util.Set; import java.util.stream.Stream; -import javax.annotation.Nonnull; -import javax.annotation.Nullable; import static org.apache.commons.lang3.ObjectUtils.defaultIfNull; diff --git a/hapi-fhir-jpaserver-searchparam/src/main/java/ca/uhn/fhir/jpa/searchparam/registry/RuntimeSearchParamCache.java b/hapi-fhir-jpaserver-searchparam/src/main/java/ca/uhn/fhir/jpa/searchparam/registry/RuntimeSearchParamCache.java index f0d4b49d379..939a654c860 100644 --- a/hapi-fhir-jpaserver-searchparam/src/main/java/ca/uhn/fhir/jpa/searchparam/registry/RuntimeSearchParamCache.java +++ b/hapi-fhir-jpaserver-searchparam/src/main/java/ca/uhn/fhir/jpa/searchparam/registry/RuntimeSearchParamCache.java @@ -1,6 +1,6 @@ /*- * #%L - * HAPI FHIR Search Parameters + * HAPI FHIR JPA - Search Parameters * %% * Copyright (C) 2014 - 2023 Smile CDR, Inc. * %% diff --git a/hapi-fhir-jpaserver-searchparam/src/main/java/ca/uhn/fhir/jpa/searchparam/registry/SearchParamRegistryImpl.java b/hapi-fhir-jpaserver-searchparam/src/main/java/ca/uhn/fhir/jpa/searchparam/registry/SearchParamRegistryImpl.java index d8bd1b6b55d..6d3a5b4db8d 100644 --- a/hapi-fhir-jpaserver-searchparam/src/main/java/ca/uhn/fhir/jpa/searchparam/registry/SearchParamRegistryImpl.java +++ b/hapi-fhir-jpaserver-searchparam/src/main/java/ca/uhn/fhir/jpa/searchparam/registry/SearchParamRegistryImpl.java @@ -1,6 +1,6 @@ /* * #%L - * HAPI FHIR Search Parameters + * HAPI FHIR JPA - Search Parameters * %% * Copyright (C) 2014 - 2023 Smile CDR, Inc. * %% @@ -42,6 +42,10 @@ import ca.uhn.fhir.util.SearchParameterUtil; import ca.uhn.fhir.util.StopWatch; import com.google.common.annotations.VisibleForTesting; import com.google.common.collect.Sets; +import jakarta.annotation.Nonnull; +import jakarta.annotation.Nullable; +import jakarta.annotation.PostConstruct; +import jakarta.annotation.PreDestroy; import org.apache.commons.lang3.StringUtils; import org.apache.commons.lang3.time.DateUtils; import org.hl7.fhir.instance.model.api.IBaseResource; @@ -57,10 +61,6 @@ import java.util.Iterator; import java.util.List; import java.util.Optional; import java.util.Set; -import javax.annotation.Nonnull; -import javax.annotation.Nullable; -import javax.annotation.PostConstruct; -import javax.annotation.PreDestroy; import static org.apache.commons.lang3.StringUtils.isBlank; diff --git a/hapi-fhir-jpaserver-searchparam/src/main/java/ca/uhn/fhir/jpa/searchparam/registry/SearchParameterCanonicalizer.java b/hapi-fhir-jpaserver-searchparam/src/main/java/ca/uhn/fhir/jpa/searchparam/registry/SearchParameterCanonicalizer.java index f8b086965ca..cb2aa5ae87d 100644 --- a/hapi-fhir-jpaserver-searchparam/src/main/java/ca/uhn/fhir/jpa/searchparam/registry/SearchParameterCanonicalizer.java +++ b/hapi-fhir-jpaserver-searchparam/src/main/java/ca/uhn/fhir/jpa/searchparam/registry/SearchParameterCanonicalizer.java @@ -1,6 +1,6 @@ /*- * #%L - * HAPI FHIR Search Parameters + * HAPI FHIR JPA - Search Parameters * %% * Copyright (C) 2014 - 2023 Smile CDR, Inc. * %% diff --git a/hapi-fhir-jpaserver-searchparam/src/main/java/ca/uhn/fhir/jpa/searchparam/retry/Retrier.java b/hapi-fhir-jpaserver-searchparam/src/main/java/ca/uhn/fhir/jpa/searchparam/retry/Retrier.java index 9e6d8b204e9..40016c6c5ce 100644 --- a/hapi-fhir-jpaserver-searchparam/src/main/java/ca/uhn/fhir/jpa/searchparam/retry/Retrier.java +++ b/hapi-fhir-jpaserver-searchparam/src/main/java/ca/uhn/fhir/jpa/searchparam/retry/Retrier.java @@ -1,6 +1,6 @@ /*- * #%L - * HAPI FHIR Search Parameters + * HAPI FHIR JPA - Search Parameters * %% * Copyright (C) 2014 - 2023 Smile CDR, Inc. * %% diff --git a/hapi-fhir-jpaserver-searchparam/src/main/java/ca/uhn/fhir/jpa/searchparam/util/Dstu3DistanceHelper.java b/hapi-fhir-jpaserver-searchparam/src/main/java/ca/uhn/fhir/jpa/searchparam/util/Dstu3DistanceHelper.java index a1041a1b738..77514845a41 100644 --- a/hapi-fhir-jpaserver-searchparam/src/main/java/ca/uhn/fhir/jpa/searchparam/util/Dstu3DistanceHelper.java +++ b/hapi-fhir-jpaserver-searchparam/src/main/java/ca/uhn/fhir/jpa/searchparam/util/Dstu3DistanceHelper.java @@ -1,6 +1,6 @@ /*- * #%L - * HAPI FHIR Search Parameters + * HAPI FHIR JPA - Search Parameters * %% * Copyright (C) 2014 - 2023 Smile CDR, Inc. * %% diff --git a/hapi-fhir-jpaserver-searchparam/src/main/java/ca/uhn/fhir/jpa/searchparam/util/JpaParamUtil.java b/hapi-fhir-jpaserver-searchparam/src/main/java/ca/uhn/fhir/jpa/searchparam/util/JpaParamUtil.java index f7713bb924d..267ed55edb2 100644 --- a/hapi-fhir-jpaserver-searchparam/src/main/java/ca/uhn/fhir/jpa/searchparam/util/JpaParamUtil.java +++ b/hapi-fhir-jpaserver-searchparam/src/main/java/ca/uhn/fhir/jpa/searchparam/util/JpaParamUtil.java @@ -1,6 +1,6 @@ /*- * #%L - * HAPI FHIR Search Parameters + * HAPI FHIR JPA - Search Parameters * %% * Copyright (C) 2014 - 2023 Smile CDR, Inc. * %% @@ -49,12 +49,12 @@ import ca.uhn.fhir.rest.param.UriParam; import ca.uhn.fhir.rest.param.binder.QueryParameterAndBinder; import ca.uhn.fhir.rest.server.exceptions.InternalErrorException; import ca.uhn.fhir.rest.server.util.ISearchParamRegistry; +import jakarta.annotation.Nonnull; import java.util.ArrayList; import java.util.Collections; import java.util.Comparator; import java.util.List; -import javax.annotation.Nonnull; public enum JpaParamUtil { ; diff --git a/hapi-fhir-jpaserver-searchparam/src/main/java/ca/uhn/fhir/jpa/searchparam/util/LastNParameterHelper.java b/hapi-fhir-jpaserver-searchparam/src/main/java/ca/uhn/fhir/jpa/searchparam/util/LastNParameterHelper.java index caff223b6ea..4b86a7228af 100644 --- a/hapi-fhir-jpaserver-searchparam/src/main/java/ca/uhn/fhir/jpa/searchparam/util/LastNParameterHelper.java +++ b/hapi-fhir-jpaserver-searchparam/src/main/java/ca/uhn/fhir/jpa/searchparam/util/LastNParameterHelper.java @@ -1,6 +1,6 @@ /*- * #%L - * HAPI FHIR Search Parameters + * HAPI FHIR JPA - Search Parameters * %% * Copyright (C) 2014 - 2023 Smile CDR, Inc. * %% diff --git a/hapi-fhir-jpaserver-searchparam/src/main/java/ca/uhn/fhir/jpa/searchparam/util/RuntimeSearchParamHelper.java b/hapi-fhir-jpaserver-searchparam/src/main/java/ca/uhn/fhir/jpa/searchparam/util/RuntimeSearchParamHelper.java index 5601d73a6e7..312079b585a 100644 --- a/hapi-fhir-jpaserver-searchparam/src/main/java/ca/uhn/fhir/jpa/searchparam/util/RuntimeSearchParamHelper.java +++ b/hapi-fhir-jpaserver-searchparam/src/main/java/ca/uhn/fhir/jpa/searchparam/util/RuntimeSearchParamHelper.java @@ -1,6 +1,6 @@ /*- * #%L - * HAPI FHIR Search Parameters + * HAPI FHIR JPA - Search Parameters * %% * Copyright (C) 2014 - 2023 Smile CDR, Inc. * %% diff --git a/hapi-fhir-jpaserver-searchparam/src/main/java/ca/uhn/fhir/jpa/searchparam/util/SearchParameterHelper.java b/hapi-fhir-jpaserver-searchparam/src/main/java/ca/uhn/fhir/jpa/searchparam/util/SearchParameterHelper.java index ea566709e54..55e80e9d3ef 100644 --- a/hapi-fhir-jpaserver-searchparam/src/main/java/ca/uhn/fhir/jpa/searchparam/util/SearchParameterHelper.java +++ b/hapi-fhir-jpaserver-searchparam/src/main/java/ca/uhn/fhir/jpa/searchparam/util/SearchParameterHelper.java @@ -1,6 +1,6 @@ /*- * #%L - * HAPI FHIR Search Parameters + * HAPI FHIR JPA - Search Parameters * %% * Copyright (C) 2014 - 2023 Smile CDR, Inc. * %% diff --git a/hapi-fhir-jpaserver-searchparam/src/main/java/ca/uhn/fhir/jpa/searchparam/util/SourceParam.java b/hapi-fhir-jpaserver-searchparam/src/main/java/ca/uhn/fhir/jpa/searchparam/util/SourceParam.java index 4ec77935492..7b183ff7657 100644 --- a/hapi-fhir-jpaserver-searchparam/src/main/java/ca/uhn/fhir/jpa/searchparam/util/SourceParam.java +++ b/hapi-fhir-jpaserver-searchparam/src/main/java/ca/uhn/fhir/jpa/searchparam/util/SourceParam.java @@ -1,6 +1,6 @@ /* * #%L - * HAPI FHIR Search Parameters + * HAPI FHIR JPA - Search Parameters * %% * Copyright (C) 2014 - 2023 Smile CDR, Inc. * %% diff --git a/hapi-fhir-jpaserver-searchparam/src/test/java/ca/uhn/fhir/jpa/searchparam/extractor/SearchParamExtractorDstu3Test.java b/hapi-fhir-jpaserver-searchparam/src/test/java/ca/uhn/fhir/jpa/searchparam/extractor/SearchParamExtractorDstu3Test.java index eb3024f548d..79463fd5517 100644 --- a/hapi-fhir-jpaserver-searchparam/src/test/java/ca/uhn/fhir/jpa/searchparam/extractor/SearchParamExtractorDstu3Test.java +++ b/hapi-fhir-jpaserver-searchparam/src/test/java/ca/uhn/fhir/jpa/searchparam/extractor/SearchParamExtractorDstu3Test.java @@ -38,7 +38,7 @@ import org.hl7.fhir.instance.model.api.IIdType; import org.junit.jupiter.api.AfterAll; import org.junit.jupiter.api.Test; -import javax.annotation.Nullable; +import jakarta.annotation.Nullable; import java.text.Normalizer; import java.util.ArrayList; import java.util.List; diff --git a/hapi-fhir-jpaserver-searchparam/src/test/java/ca/uhn/fhir/jpa/searchparam/matcher/InMemoryResourceMatcherR5Test.java b/hapi-fhir-jpaserver-searchparam/src/test/java/ca/uhn/fhir/jpa/searchparam/matcher/InMemoryResourceMatcherR5Test.java index 908ba13a1c2..fdf20ec5eb1 100644 --- a/hapi-fhir-jpaserver-searchparam/src/test/java/ca/uhn/fhir/jpa/searchparam/matcher/InMemoryResourceMatcherR5Test.java +++ b/hapi-fhir-jpaserver-searchparam/src/test/java/ca/uhn/fhir/jpa/searchparam/matcher/InMemoryResourceMatcherR5Test.java @@ -35,7 +35,7 @@ import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.test.context.junit.jupiter.SpringExtension; -import javax.annotation.Nonnull; +import jakarta.annotation.Nonnull; import java.time.Duration; import java.time.Instant; import java.util.Date; diff --git a/hapi-fhir-jpaserver-searchparam/src/test/java/ca/uhn/fhir/jpa/searchparam/registry/SearchParamRegistryImplTest.java b/hapi-fhir-jpaserver-searchparam/src/test/java/ca/uhn/fhir/jpa/searchparam/registry/SearchParamRegistryImplTest.java index c559b1dcd62..5a9c356797d 100644 --- a/hapi-fhir-jpaserver-searchparam/src/test/java/ca/uhn/fhir/jpa/searchparam/registry/SearchParamRegistryImplTest.java +++ b/hapi-fhir-jpaserver-searchparam/src/test/java/ca/uhn/fhir/jpa/searchparam/registry/SearchParamRegistryImplTest.java @@ -36,7 +36,7 @@ import org.springframework.context.annotation.Import; import org.springframework.test.context.junit.jupiter.SpringExtension; import org.testcontainers.shaded.com.google.common.collect.Sets; -import javax.annotation.Nonnull; +import jakarta.annotation.Nonnull; import java.util.ArrayList; import java.util.List; import java.util.concurrent.atomic.AtomicBoolean; diff --git a/hapi-fhir-jpaserver-subscription/pom.xml b/hapi-fhir-jpaserver-subscription/pom.xml index 5beeda86ad3..dca57d868d6 100644 --- a/hapi-fhir-jpaserver-subscription/pom.xml +++ b/hapi-fhir-jpaserver-subscription/pom.xml @@ -5,7 +5,7 @@ ca.uhn.hapi.fhir hapi-deployable-pom - 6.9.7-SNAPSHOT + 6.11.7-SNAPSHOT ../hapi-deployable-pom/pom.xml @@ -55,7 +55,7 @@ org.thymeleaf - thymeleaf-spring5 + thymeleaf-spring6 org.springframework @@ -66,8 +66,8 @@ spring-context-support - javax.servlet - javax.servlet-api + jakarta.servlet + jakarta.servlet-api provided @@ -93,11 +93,6 @@ jetty-server test - - org.eclipse.jetty - jetty-servlet - test - org.springframework.boot spring-boot-starter-test diff --git a/hapi-fhir-jpaserver-subscription/src/main/java/ca/uhn/fhir/jpa/subscription/api/ISubscriptionMessageKeySvc.java b/hapi-fhir-jpaserver-subscription/src/main/java/ca/uhn/fhir/jpa/subscription/api/ISubscriptionMessageKeySvc.java index 54a509c23e0..27a5a6c4aba 100644 --- a/hapi-fhir-jpaserver-subscription/src/main/java/ca/uhn/fhir/jpa/subscription/api/ISubscriptionMessageKeySvc.java +++ b/hapi-fhir-jpaserver-subscription/src/main/java/ca/uhn/fhir/jpa/subscription/api/ISubscriptionMessageKeySvc.java @@ -19,10 +19,9 @@ */ package ca.uhn.fhir.jpa.subscription.api; +import jakarta.annotation.Nullable; import org.hl7.fhir.instance.model.api.IBaseResource; -import javax.annotation.Nullable; - /** * This is used by "message" type subscriptions to provide a key to the message wrapper before submitting it to the channel */ diff --git a/hapi-fhir-jpaserver-subscription/src/main/java/ca/uhn/fhir/jpa/subscription/match/deliver/BaseSubscriptionDeliverySubscriber.java b/hapi-fhir-jpaserver-subscription/src/main/java/ca/uhn/fhir/jpa/subscription/match/deliver/BaseSubscriptionDeliverySubscriber.java index 2e4dff6e7e8..641e732b642 100644 --- a/hapi-fhir-jpaserver-subscription/src/main/java/ca/uhn/fhir/jpa/subscription/match/deliver/BaseSubscriptionDeliverySubscriber.java +++ b/hapi-fhir-jpaserver-subscription/src/main/java/ca/uhn/fhir/jpa/subscription/match/deliver/BaseSubscriptionDeliverySubscriber.java @@ -33,7 +33,9 @@ import ca.uhn.fhir.jpa.subscription.match.registry.ActiveSubscription; import ca.uhn.fhir.jpa.subscription.match.registry.SubscriptionRegistry; import ca.uhn.fhir.jpa.subscription.model.CanonicalSubscription; import ca.uhn.fhir.jpa.subscription.model.ResourceDeliveryMessage; +import ca.uhn.fhir.jpa.subscription.model.ResourceModifiedMessage; import ca.uhn.fhir.rest.api.server.IBundleProvider; +import ca.uhn.fhir.subscription.api.IResourceModifiedMessagePersistenceSvc; import ca.uhn.fhir.util.BundleBuilder; import com.google.common.annotations.VisibleForTesting; import org.apache.commons.text.StringSubstitutor; @@ -48,6 +50,7 @@ import org.springframework.messaging.MessagingException; import java.util.HashMap; import java.util.Map; +import java.util.Optional; import static ca.uhn.fhir.jpa.subscription.util.SubscriptionUtil.createRequestDetailForPartitionedRequest; @@ -60,6 +63,9 @@ public abstract class BaseSubscriptionDeliverySubscriber implements MessageHandl @Autowired protected SubscriptionRegistry mySubscriptionRegistry; + @Autowired + protected IResourceModifiedMessagePersistenceSvc myResourceModifiedMessagePersistenceSvc; + @Autowired private IInterceptorBroadcaster myInterceptorBroadcaster; @@ -149,6 +155,13 @@ public abstract class BaseSubscriptionDeliverySubscriber implements MessageHandl return builder.getBundle(); } + protected Optional inflateResourceModifiedMessageFromDeliveryMessage( + ResourceDeliveryMessage theMsg) { + ResourceModifiedMessage payloadLess = + new ResourceModifiedMessage(theMsg.getPayloadId(myFhirContext), theMsg.getOperationType()); + return myResourceModifiedMessagePersistenceSvc.inflatePersistedResourceModifiedMessageOrNull(payloadLess); + } + @VisibleForTesting public void setFhirContextForUnitTest(FhirContext theCtx) { myFhirContext = theCtx; @@ -174,6 +187,12 @@ public abstract class BaseSubscriptionDeliverySubscriber implements MessageHandl myMatchUrlService = theMatchUrlService; } + @VisibleForTesting + public void setResourceModifiedMessagePersistenceSvcForUnitTest( + IResourceModifiedMessagePersistenceSvc theResourceModifiedMessagePersistenceSvc) { + myResourceModifiedMessagePersistenceSvc = theResourceModifiedMessagePersistenceSvc; + } + public IInterceptorBroadcaster getInterceptorBroadcaster() { return myInterceptorBroadcaster; } diff --git a/hapi-fhir-jpaserver-subscription/src/main/java/ca/uhn/fhir/jpa/subscription/match/deliver/email/EmailDetails.java b/hapi-fhir-jpaserver-subscription/src/main/java/ca/uhn/fhir/jpa/subscription/match/deliver/email/EmailDetails.java index 40fd11b611c..e6b73e500db 100644 --- a/hapi-fhir-jpaserver-subscription/src/main/java/ca/uhn/fhir/jpa/subscription/match/deliver/email/EmailDetails.java +++ b/hapi-fhir-jpaserver-subscription/src/main/java/ca/uhn/fhir/jpa/subscription/match/deliver/email/EmailDetails.java @@ -21,19 +21,19 @@ package ca.uhn.fhir.jpa.subscription.match.deliver.email; import ca.uhn.fhir.i18n.Msg; import ca.uhn.fhir.rest.server.exceptions.InternalErrorException; +import jakarta.annotation.Nonnull; import org.apache.commons.lang3.StringUtils; import org.hl7.fhir.instance.model.api.IIdType; import org.simplejavamail.api.email.Email; import org.simplejavamail.email.EmailBuilder; import org.thymeleaf.context.Context; -import org.thymeleaf.spring5.SpringTemplateEngine; -import org.thymeleaf.spring5.dialect.SpringStandardDialect; +import org.thymeleaf.spring6.SpringTemplateEngine; +import org.thymeleaf.spring6.dialect.SpringStandardDialect; import org.thymeleaf.templatemode.TemplateMode; import org.thymeleaf.templateresolver.StringTemplateResolver; import java.util.List; import java.util.stream.Collectors; -import javax.annotation.Nonnull; public class EmailDetails { private final SpringTemplateEngine myTemplateEngine; diff --git a/hapi-fhir-jpaserver-subscription/src/main/java/ca/uhn/fhir/jpa/subscription/match/deliver/email/EmailSenderImpl.java b/hapi-fhir-jpaserver-subscription/src/main/java/ca/uhn/fhir/jpa/subscription/match/deliver/email/EmailSenderImpl.java index b2cc733ecd2..1a37fbafe3f 100644 --- a/hapi-fhir-jpaserver-subscription/src/main/java/ca/uhn/fhir/jpa/subscription/match/deliver/email/EmailSenderImpl.java +++ b/hapi-fhir-jpaserver-subscription/src/main/java/ca/uhn/fhir/jpa/subscription/match/deliver/email/EmailSenderImpl.java @@ -21,13 +21,12 @@ package ca.uhn.fhir.jpa.subscription.match.deliver.email; import ca.uhn.fhir.rest.server.mail.IMailSvc; import ca.uhn.fhir.util.StopWatch; +import jakarta.annotation.Nonnull; import org.apache.commons.lang3.Validate; import org.simplejavamail.api.email.Email; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import javax.annotation.Nonnull; - public class EmailSenderImpl implements IEmailSender { private static final Logger ourLog = LoggerFactory.getLogger(EmailSenderImpl.class); diff --git a/hapi-fhir-jpaserver-subscription/src/main/java/ca/uhn/fhir/jpa/subscription/match/deliver/email/SubscriptionDeliveringEmailSubscriber.java b/hapi-fhir-jpaserver-subscription/src/main/java/ca/uhn/fhir/jpa/subscription/match/deliver/email/SubscriptionDeliveringEmailSubscriber.java index 01ccf19397a..80275a84317 100644 --- a/hapi-fhir-jpaserver-subscription/src/main/java/ca/uhn/fhir/jpa/subscription/match/deliver/email/SubscriptionDeliveringEmailSubscriber.java +++ b/hapi-fhir-jpaserver-subscription/src/main/java/ca/uhn/fhir/jpa/subscription/match/deliver/email/SubscriptionDeliveringEmailSubscriber.java @@ -24,6 +24,7 @@ import ca.uhn.fhir.jpa.model.entity.StorageSettings; import ca.uhn.fhir.jpa.subscription.match.deliver.BaseSubscriptionDeliverySubscriber; import ca.uhn.fhir.jpa.subscription.model.CanonicalSubscription; import ca.uhn.fhir.jpa.subscription.model.ResourceDeliveryMessage; +import ca.uhn.fhir.jpa.subscription.model.ResourceModifiedMessage; import ca.uhn.fhir.rest.api.EncodingEnum; import com.google.common.annotations.VisibleForTesting; import org.apache.commons.lang3.StringUtils; @@ -33,6 +34,7 @@ import org.springframework.beans.factory.annotation.Autowired; import java.util.ArrayList; import java.util.List; +import java.util.Optional; import static org.apache.commons.lang3.StringUtils.defaultString; import static org.apache.commons.lang3.StringUtils.isNotBlank; @@ -73,7 +75,7 @@ public class SubscriptionDeliveringEmailSubscriber extends BaseSubscriptionDeliv if (isNotBlank(subscription.getPayloadString())) { EncodingEnum encoding = EncodingEnum.forContentType(subscription.getPayloadString()); if (encoding != null) { - payload = theMessage.getPayloadString(); + payload = getPayloadStringFromMessageOrEmptyString(theMessage); } } @@ -112,4 +114,24 @@ public class SubscriptionDeliveringEmailSubscriber extends BaseSubscriptionDeliv public IEmailSender getEmailSender() { return myEmailSender; } + + /** + * Get the payload string, fetch it from the DB when the payload is null. + */ + private String getPayloadStringFromMessageOrEmptyString(ResourceDeliveryMessage theMessage) { + String payload = theMessage.getPayloadString(); + + if (theMessage.getPayload(myCtx) != null) { + return payload; + } + + Optional inflatedMessage = + inflateResourceModifiedMessageFromDeliveryMessage(theMessage); + if (inflatedMessage.isEmpty()) { + return ""; + } + + payload = inflatedMessage.get().getPayloadString(); + return payload; + } } diff --git a/hapi-fhir-jpaserver-subscription/src/main/java/ca/uhn/fhir/jpa/subscription/match/deliver/message/SubscriptionDeliveringMessageSubscriber.java b/hapi-fhir-jpaserver-subscription/src/main/java/ca/uhn/fhir/jpa/subscription/match/deliver/message/SubscriptionDeliveringMessageSubscriber.java index b801d0e8f95..c15854c143e 100644 --- a/hapi-fhir-jpaserver-subscription/src/main/java/ca/uhn/fhir/jpa/subscription/match/deliver/message/SubscriptionDeliveringMessageSubscriber.java +++ b/hapi-fhir-jpaserver-subscription/src/main/java/ca/uhn/fhir/jpa/subscription/match/deliver/message/SubscriptionDeliveringMessageSubscriber.java @@ -39,6 +39,7 @@ import org.springframework.messaging.MessagingException; import java.net.URI; import java.net.URISyntaxException; +import java.util.Optional; import static org.apache.commons.lang3.StringUtils.isNotBlank; @@ -66,7 +67,7 @@ public class SubscriptionDeliveringMessageSubscriber extends BaseSubscriptionDel IBaseResource payloadResource = createDeliveryBundleForPayloadSearchCriteria( theSubscription, theWrappedMessageToSend.getPayload().getPayload(myFhirContext)); ResourceModifiedJsonMessage newWrappedMessageToSend = - convertDeliveryMessageToResourceModifiedMessage(theSourceMessage, payloadResource); + convertDeliveryMessageToResourceModifiedJsonMessage(theSourceMessage, payloadResource); theWrappedMessageToSend.setPayload(newWrappedMessageToSend.getPayload()); payloadId = payloadResource.getIdElement().toUnqualifiedVersionless().getValue(); @@ -82,7 +83,7 @@ public class SubscriptionDeliveringMessageSubscriber extends BaseSubscriptionDel .getValue()); } - private ResourceModifiedJsonMessage convertDeliveryMessageToResourceModifiedMessage( + private ResourceModifiedJsonMessage convertDeliveryMessageToResourceModifiedJsonMessage( ResourceDeliveryMessage theMsg, IBaseResource thePayloadResource) { ResourceModifiedMessage payload = new ResourceModifiedMessage(myFhirContext, thePayloadResource, theMsg.getOperationType()); @@ -96,8 +97,17 @@ public class SubscriptionDeliveringMessageSubscriber extends BaseSubscriptionDel public void handleMessage(ResourceDeliveryMessage theMessage) throws MessagingException, URISyntaxException { CanonicalSubscription subscription = theMessage.getSubscription(); IBaseResource payloadResource = theMessage.getPayload(myFhirContext); + if (payloadResource == null) { + Optional inflatedMsg = + inflateResourceModifiedMessageFromDeliveryMessage(theMessage); + if (inflatedMsg.isEmpty()) { + return; + } + payloadResource = inflatedMsg.get().getPayload(myFhirContext); + } + ResourceModifiedJsonMessage messageWrapperToSend = - convertDeliveryMessageToResourceModifiedMessage(theMessage, payloadResource); + convertDeliveryMessageToResourceModifiedJsonMessage(theMessage, payloadResource); // Interceptor call: SUBSCRIPTION_BEFORE_MESSAGE_DELIVERY HookParams params = new HookParams() diff --git a/hapi-fhir-jpaserver-subscription/src/main/java/ca/uhn/fhir/jpa/subscription/match/deliver/resthook/SubscriptionDeliveringRestHookSubscriber.java b/hapi-fhir-jpaserver-subscription/src/main/java/ca/uhn/fhir/jpa/subscription/match/deliver/resthook/SubscriptionDeliveringRestHookSubscriber.java index 6d4054a7d30..6f83665014c 100644 --- a/hapi-fhir-jpaserver-subscription/src/main/java/ca/uhn/fhir/jpa/subscription/match/deliver/resthook/SubscriptionDeliveringRestHookSubscriber.java +++ b/hapi-fhir-jpaserver-subscription/src/main/java/ca/uhn/fhir/jpa/subscription/match/deliver/resthook/SubscriptionDeliveringRestHookSubscriber.java @@ -46,6 +46,7 @@ import ca.uhn.fhir.rest.server.messaging.BaseResourceModifiedMessage; import ca.uhn.fhir.util.BundleUtil; import ca.uhn.fhir.util.Logs; import ca.uhn.fhir.util.StopWatch; +import jakarta.annotation.Nullable; import org.hl7.fhir.instance.model.api.IBaseBundle; import org.hl7.fhir.instance.model.api.IBaseResource; import org.hl7.fhir.instance.model.api.IIdType; @@ -61,7 +62,6 @@ import java.util.Collections; import java.util.HashMap; import java.util.List; import java.util.Map; -import javax.annotation.Nullable; import static org.apache.commons.lang3.StringUtils.isNotBlank; diff --git a/hapi-fhir-jpaserver-subscription/src/main/java/ca/uhn/fhir/jpa/subscription/match/deliver/websocket/SubscriptionWebsocketHandler.java b/hapi-fhir-jpaserver-subscription/src/main/java/ca/uhn/fhir/jpa/subscription/match/deliver/websocket/SubscriptionWebsocketHandler.java index 706d01ee16d..982ba3d9082 100644 --- a/hapi-fhir-jpaserver-subscription/src/main/java/ca/uhn/fhir/jpa/subscription/match/deliver/websocket/SubscriptionWebsocketHandler.java +++ b/hapi-fhir-jpaserver-subscription/src/main/java/ca/uhn/fhir/jpa/subscription/match/deliver/websocket/SubscriptionWebsocketHandler.java @@ -24,6 +24,8 @@ import ca.uhn.fhir.jpa.subscription.channel.subscription.SubscriptionChannelRegi import ca.uhn.fhir.jpa.subscription.channel.subscription.SubscriptionChannelWithHandlers; import ca.uhn.fhir.jpa.subscription.match.registry.ActiveSubscription; import ca.uhn.fhir.jpa.subscription.model.ResourceDeliveryMessage; +import jakarta.annotation.PostConstruct; +import jakarta.annotation.PreDestroy; import org.hl7.fhir.instance.model.api.IIdType; import org.hl7.fhir.r4.model.IdType; import org.slf4j.Logger; @@ -39,8 +41,6 @@ import org.springframework.web.socket.WebSocketSession; import org.springframework.web.socket.handler.TextWebSocketHandler; import java.io.IOException; -import javax.annotation.PostConstruct; -import javax.annotation.PreDestroy; public class SubscriptionWebsocketHandler extends TextWebSocketHandler implements WebSocketHandler { private static Logger ourLog = LoggerFactory.getLogger(SubscriptionWebsocketHandler.class); diff --git a/hapi-fhir-jpaserver-subscription/src/main/java/ca/uhn/fhir/jpa/subscription/match/deliver/websocket/WebsocketConnectionValidator.java b/hapi-fhir-jpaserver-subscription/src/main/java/ca/uhn/fhir/jpa/subscription/match/deliver/websocket/WebsocketConnectionValidator.java index cf82917ba0f..dbc0f3d928f 100644 --- a/hapi-fhir-jpaserver-subscription/src/main/java/ca/uhn/fhir/jpa/subscription/match/deliver/websocket/WebsocketConnectionValidator.java +++ b/hapi-fhir-jpaserver-subscription/src/main/java/ca/uhn/fhir/jpa/subscription/match/deliver/websocket/WebsocketConnectionValidator.java @@ -22,13 +22,12 @@ package ca.uhn.fhir.jpa.subscription.match.deliver.websocket; import ca.uhn.fhir.jpa.subscription.match.registry.ActiveSubscription; import ca.uhn.fhir.jpa.subscription.match.registry.SubscriptionRegistry; import ca.uhn.fhir.jpa.subscription.model.CanonicalSubscriptionChannelType; +import jakarta.annotation.Nonnull; import org.hl7.fhir.r4.model.IdType; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; -import javax.annotation.Nonnull; - public class WebsocketConnectionValidator { private static Logger ourLog = LoggerFactory.getLogger(WebsocketConnectionValidator.class); diff --git a/hapi-fhir-jpaserver-subscription/src/main/java/ca/uhn/fhir/jpa/subscription/match/matcher/subscriber/MatchingQueueSubscriberLoader.java b/hapi-fhir-jpaserver-subscription/src/main/java/ca/uhn/fhir/jpa/subscription/match/matcher/subscriber/MatchingQueueSubscriberLoader.java index b16b1874cd3..14fade327b2 100644 --- a/hapi-fhir-jpaserver-subscription/src/main/java/ca/uhn/fhir/jpa/subscription/match/matcher/subscriber/MatchingQueueSubscriberLoader.java +++ b/hapi-fhir-jpaserver-subscription/src/main/java/ca/uhn/fhir/jpa/subscription/match/matcher/subscriber/MatchingQueueSubscriberLoader.java @@ -27,6 +27,7 @@ import ca.uhn.fhir.jpa.subscription.channel.api.IChannelReceiver; import ca.uhn.fhir.jpa.subscription.channel.subscription.SubscriptionChannelFactory; import ca.uhn.fhir.jpa.topic.SubscriptionTopicMatchingSubscriber; import ca.uhn.fhir.jpa.topic.SubscriptionTopicRegisteringSubscriber; +import jakarta.annotation.PreDestroy; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; @@ -34,8 +35,6 @@ import org.springframework.context.event.ContextRefreshedEvent; import org.springframework.context.event.EventListener; import org.springframework.core.annotation.Order; -import javax.annotation.PreDestroy; - import static ca.uhn.fhir.jpa.subscription.match.matcher.subscriber.SubscriptionMatchingSubscriber.SUBSCRIPTION_MATCHING_CHANNEL_NAME; public class MatchingQueueSubscriberLoader { diff --git a/hapi-fhir-jpaserver-subscription/src/main/java/ca/uhn/fhir/jpa/subscription/match/matcher/subscriber/SubscriptionActivatingSubscriber.java b/hapi-fhir-jpaserver-subscription/src/main/java/ca/uhn/fhir/jpa/subscription/match/matcher/subscriber/SubscriptionActivatingSubscriber.java index 55914efdde5..32640b9bbe5 100644 --- a/hapi-fhir-jpaserver-subscription/src/main/java/ca/uhn/fhir/jpa/subscription/match/matcher/subscriber/SubscriptionActivatingSubscriber.java +++ b/hapi-fhir-jpaserver-subscription/src/main/java/ca/uhn/fhir/jpa/subscription/match/matcher/subscriber/SubscriptionActivatingSubscriber.java @@ -31,7 +31,9 @@ import ca.uhn.fhir.rest.api.server.SystemRequestDetails; import ca.uhn.fhir.rest.server.exceptions.ResourceGoneException; import ca.uhn.fhir.rest.server.exceptions.UnprocessableEntityException; import ca.uhn.fhir.subscription.SubscriptionConstants; +import ca.uhn.fhir.subscription.api.IResourceModifiedMessagePersistenceSvc; import ca.uhn.fhir.util.SubscriptionUtil; +import jakarta.annotation.Nonnull; import org.hl7.fhir.dstu2.model.Subscription; import org.hl7.fhir.instance.model.api.IBaseResource; import org.slf4j.Logger; @@ -41,7 +43,7 @@ import org.springframework.messaging.Message; import org.springframework.messaging.MessageHandler; import org.springframework.messaging.MessagingException; -import javax.annotation.Nonnull; +import java.util.Optional; /** * Responsible for transitioning subscription resources from REQUESTED to ACTIVE @@ -64,6 +66,8 @@ public class SubscriptionActivatingSubscriber implements MessageHandler { @Autowired private StorageSettings myStorageSettings; + @Autowired + private IResourceModifiedMessagePersistenceSvc myResourceModifiedMessagePersistenceSvc; /** * Constructor */ @@ -86,6 +90,16 @@ public class SubscriptionActivatingSubscriber implements MessageHandler { switch (payload.getOperationType()) { case CREATE: case UPDATE: + if (payload.getPayload(myFhirContext) == null) { + Optional inflatedMsg = + myResourceModifiedMessagePersistenceSvc.inflatePersistedResourceModifiedMessageOrNull( + payload); + if (inflatedMsg.isEmpty()) { + return; + } + payload = inflatedMsg.get(); + } + activateSubscriptionIfRequired(payload.getNewPayload(myFhirContext)); break; case TRANSACTION: @@ -104,7 +118,7 @@ public class SubscriptionActivatingSubscriber implements MessageHandler { */ public synchronized boolean activateSubscriptionIfRequired(final IBaseResource theSubscription) { // Grab the value for "Subscription.channel.type" so we can see if this - // subscriber applies.. + // subscriber applies. CanonicalSubscriptionChannelType subscriptionChannelType = mySubscriptionCanonicalizer.getChannelType(theSubscription); diff --git a/hapi-fhir-jpaserver-subscription/src/main/java/ca/uhn/fhir/jpa/subscription/match/matcher/subscriber/SubscriptionCriteriaParser.java b/hapi-fhir-jpaserver-subscription/src/main/java/ca/uhn/fhir/jpa/subscription/match/matcher/subscriber/SubscriptionCriteriaParser.java index af2f5ac4d3a..a7914d72ee8 100644 --- a/hapi-fhir-jpaserver-subscription/src/main/java/ca/uhn/fhir/jpa/subscription/match/matcher/subscriber/SubscriptionCriteriaParser.java +++ b/hapi-fhir-jpaserver-subscription/src/main/java/ca/uhn/fhir/jpa/subscription/match/matcher/subscriber/SubscriptionCriteriaParser.java @@ -22,6 +22,7 @@ package ca.uhn.fhir.jpa.subscription.match.matcher.subscriber; import ca.uhn.fhir.jpa.searchparam.extractor.StringTrimmingTrimmerMatcher; import ca.uhn.fhir.rest.api.Constants; import com.google.common.collect.Sets; +import jakarta.annotation.Nullable; import org.apache.commons.lang3.builder.ToStringBuilder; import org.apache.commons.lang3.builder.ToStringStyle; import org.apache.commons.text.StringTokenizer; @@ -29,7 +30,6 @@ import org.apache.commons.text.StringTokenizer; import java.util.Collections; import java.util.List; import java.util.Set; -import javax.annotation.Nullable; import static org.apache.commons.lang3.StringUtils.isBlank; import static org.apache.commons.lang3.StringUtils.isNotBlank; diff --git a/hapi-fhir-jpaserver-subscription/src/main/java/ca/uhn/fhir/jpa/subscription/match/matcher/subscriber/SubscriptionDeliveryRequest.java b/hapi-fhir-jpaserver-subscription/src/main/java/ca/uhn/fhir/jpa/subscription/match/matcher/subscriber/SubscriptionDeliveryRequest.java index 0ad8112ca0e..c4515a4e8e7 100644 --- a/hapi-fhir-jpaserver-subscription/src/main/java/ca/uhn/fhir/jpa/subscription/match/matcher/subscriber/SubscriptionDeliveryRequest.java +++ b/hapi-fhir-jpaserver-subscription/src/main/java/ca/uhn/fhir/jpa/subscription/match/matcher/subscriber/SubscriptionDeliveryRequest.java @@ -26,12 +26,11 @@ import ca.uhn.fhir.jpa.subscription.model.ResourceModifiedMessage; import ca.uhn.fhir.jpa.topic.SubscriptionTopicDispatchRequest; import ca.uhn.fhir.rest.api.RestOperationTypeEnum; import ca.uhn.fhir.rest.server.messaging.BaseResourceModifiedMessage; +import jakarta.annotation.Nonnull; import org.hl7.fhir.instance.model.api.IBaseBundle; import org.hl7.fhir.instance.model.api.IBaseResource; import org.hl7.fhir.instance.model.api.IIdType; -import javax.annotation.Nonnull; - public class SubscriptionDeliveryRequest { // One of these two will be populated private final IBaseResource myPayload; diff --git a/hapi-fhir-jpaserver-subscription/src/main/java/ca/uhn/fhir/jpa/subscription/match/matcher/subscriber/SubscriptionMatchDeliverer.java b/hapi-fhir-jpaserver-subscription/src/main/java/ca/uhn/fhir/jpa/subscription/match/matcher/subscriber/SubscriptionMatchDeliverer.java index 488a961c89d..fb60960b54b 100644 --- a/hapi-fhir-jpaserver-subscription/src/main/java/ca/uhn/fhir/jpa/subscription/match/matcher/subscriber/SubscriptionMatchDeliverer.java +++ b/hapi-fhir-jpaserver-subscription/src/main/java/ca/uhn/fhir/jpa/subscription/match/matcher/subscriber/SubscriptionMatchDeliverer.java @@ -25,6 +25,7 @@ import ca.uhn.fhir.interceptor.api.HookParams; import ca.uhn.fhir.interceptor.api.IInterceptorBroadcaster; import ca.uhn.fhir.interceptor.api.Pointcut; import ca.uhn.fhir.jpa.searchparam.matcher.InMemoryMatchResult; +import ca.uhn.fhir.jpa.subscription.channel.api.PayloadTooLargeException; import ca.uhn.fhir.jpa.subscription.channel.subscription.SubscriptionChannelRegistry; import ca.uhn.fhir.jpa.subscription.match.registry.ActiveSubscription; import ca.uhn.fhir.jpa.subscription.model.CanonicalSubscription; @@ -32,14 +33,13 @@ import ca.uhn.fhir.jpa.subscription.model.ResourceDeliveryJsonMessage; import ca.uhn.fhir.jpa.subscription.model.ResourceDeliveryMessage; import ca.uhn.fhir.jpa.subscription.model.ResourceModifiedMessage; import ca.uhn.fhir.rest.api.EncodingEnum; +import jakarta.annotation.Nonnull; +import jakarta.annotation.Nullable; import org.hl7.fhir.instance.model.api.IBaseResource; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.messaging.MessageChannel; -import javax.annotation.Nonnull; -import javax.annotation.Nullable; - import static org.apache.commons.lang3.ObjectUtils.defaultIfNull; public class SubscriptionMatchDeliverer { @@ -156,8 +156,21 @@ public class SubscriptionMatchDeliverer { ourLog.warn("Failed to send message to Delivery Channel."); } } catch (RuntimeException e) { - ourLog.error("Failed to send message to Delivery Channel", e); - throw new RuntimeException(Msg.code(7) + "Failed to send message to Delivery Channel", e); + if (e.getCause() instanceof PayloadTooLargeException) { + ourLog.warn("Failed to send message to Delivery Channel because the payload size is larger than broker " + + "max message size. Retry is about to be performed without payload."); + ResourceDeliveryJsonMessage msgPayloadLess = nullOutPayload(theWrappedMsg); + trySendToDeliveryChannel(msgPayloadLess, theDeliveryChannel); + } else { + ourLog.error("Failed to send message to Delivery Channel", e); + throw new RuntimeException(Msg.code(7) + "Failed to send message to Delivery Channel", e); + } } } + + private ResourceDeliveryJsonMessage nullOutPayload(ResourceDeliveryJsonMessage theWrappedMsg) { + ResourceDeliveryMessage resourceDeliveryMessage = theWrappedMsg.getPayload(); + resourceDeliveryMessage.setPayloadToNull(); + return new ResourceDeliveryJsonMessage(resourceDeliveryMessage); + } } diff --git a/hapi-fhir-jpaserver-subscription/src/main/java/ca/uhn/fhir/jpa/subscription/match/matcher/subscriber/SubscriptionMatchingSubscriber.java b/hapi-fhir-jpaserver-subscription/src/main/java/ca/uhn/fhir/jpa/subscription/match/matcher/subscriber/SubscriptionMatchingSubscriber.java index 08623ae0221..422f8e8eb13 100644 --- a/hapi-fhir-jpaserver-subscription/src/main/java/ca/uhn/fhir/jpa/subscription/match/matcher/subscriber/SubscriptionMatchingSubscriber.java +++ b/hapi-fhir-jpaserver-subscription/src/main/java/ca/uhn/fhir/jpa/subscription/match/matcher/subscriber/SubscriptionMatchingSubscriber.java @@ -30,6 +30,8 @@ import ca.uhn.fhir.jpa.subscription.match.registry.SubscriptionRegistry; import ca.uhn.fhir.jpa.subscription.model.CanonicalSubscription; import ca.uhn.fhir.jpa.subscription.model.ResourceModifiedJsonMessage; import ca.uhn.fhir.jpa.subscription.model.ResourceModifiedMessage; +import ca.uhn.fhir.subscription.api.IResourceModifiedMessagePersistenceSvc; +import jakarta.annotation.Nonnull; import org.hl7.fhir.instance.model.api.IBaseResource; import org.hl7.fhir.instance.model.api.IIdType; import org.slf4j.Logger; @@ -40,7 +42,7 @@ import org.springframework.messaging.MessageHandler; import org.springframework.messaging.MessagingException; import java.util.Collection; -import javax.annotation.Nonnull; +import java.util.Optional; import static ca.uhn.fhir.rest.server.messaging.BaseResourceMessage.OperationTypeEnum.DELETE; import static org.apache.commons.lang3.StringUtils.isNotBlank; @@ -64,6 +66,9 @@ public class SubscriptionMatchingSubscriber implements MessageHandler { @Autowired private SubscriptionMatchDeliverer mySubscriptionMatchDeliverer; + @Autowired + private IResourceModifiedMessagePersistenceSvc myResourceModifiedMessagePersistenceSvc; + /** * Constructor */ @@ -97,6 +102,16 @@ public class SubscriptionMatchingSubscriber implements MessageHandler { return; } + if (theMsg.getPayload(myFhirContext) == null) { + // inflate the message and ignore any resource that cannot be found. + Optional inflatedMsg = + myResourceModifiedMessagePersistenceSvc.inflatePersistedResourceModifiedMessageOrNull(theMsg); + if (inflatedMsg.isEmpty()) { + return; + } + theMsg = inflatedMsg.get(); + } + // Interceptor call: SUBSCRIPTION_BEFORE_PERSISTED_RESOURCE_CHECKED HookParams params = new HookParams().add(ResourceModifiedMessage.class, theMsg); if (!myInterceptorBroadcaster.callHooks(Pointcut.SUBSCRIPTION_BEFORE_PERSISTED_RESOURCE_CHECKED, params)) { diff --git a/hapi-fhir-jpaserver-subscription/src/main/java/ca/uhn/fhir/jpa/subscription/match/matcher/subscriber/SubscriptionRegisteringSubscriber.java b/hapi-fhir-jpaserver-subscription/src/main/java/ca/uhn/fhir/jpa/subscription/match/matcher/subscriber/SubscriptionRegisteringSubscriber.java index c2ca97b0ee4..c07ec015e59 100644 --- a/hapi-fhir-jpaserver-subscription/src/main/java/ca/uhn/fhir/jpa/subscription/match/matcher/subscriber/SubscriptionRegisteringSubscriber.java +++ b/hapi-fhir-jpaserver-subscription/src/main/java/ca/uhn/fhir/jpa/subscription/match/matcher/subscriber/SubscriptionRegisteringSubscriber.java @@ -29,6 +29,7 @@ import ca.uhn.fhir.jpa.subscription.model.ResourceModifiedJsonMessage; import ca.uhn.fhir.jpa.subscription.model.ResourceModifiedMessage; import ca.uhn.fhir.rest.api.server.RequestDetails; import ca.uhn.fhir.rest.api.server.SystemRequestDetails; +import jakarta.annotation.Nonnull; import org.hl7.fhir.instance.model.api.IBaseResource; import org.hl7.fhir.instance.model.api.IIdType; import org.slf4j.Logger; @@ -38,8 +39,6 @@ import org.springframework.messaging.Message; import org.springframework.messaging.MessageHandler; import org.springframework.messaging.MessagingException; -import javax.annotation.Nonnull; - /** * Responsible for transitioning subscription resources from REQUESTED to ACTIVE * Once activated, the subscription is added to the SubscriptionRegistry. diff --git a/hapi-fhir-jpaserver-subscription/src/main/java/ca/uhn/fhir/jpa/subscription/match/registry/SubscriptionLoader.java b/hapi-fhir-jpaserver-subscription/src/main/java/ca/uhn/fhir/jpa/subscription/match/registry/SubscriptionLoader.java index 13e14e72dc2..ac567cf3969 100644 --- a/hapi-fhir-jpaserver-subscription/src/main/java/ca/uhn/fhir/jpa/subscription/match/registry/SubscriptionLoader.java +++ b/hapi-fhir-jpaserver-subscription/src/main/java/ca/uhn/fhir/jpa/subscription/match/registry/SubscriptionLoader.java @@ -25,6 +25,7 @@ import ca.uhn.fhir.jpa.subscription.match.matcher.subscriber.SubscriptionActivat import ca.uhn.fhir.rest.param.TokenOrListParam; import ca.uhn.fhir.rest.param.TokenParam; import ca.uhn.fhir.subscription.SubscriptionConstants; +import jakarta.annotation.Nonnull; import org.apache.commons.lang3.StringUtils; import org.hl7.fhir.instance.model.api.IBaseResource; import org.hl7.fhir.r4.model.Subscription; @@ -35,7 +36,6 @@ import org.springframework.beans.factory.annotation.Autowired; import java.util.HashSet; import java.util.List; import java.util.Set; -import javax.annotation.Nonnull; public class SubscriptionLoader extends BaseResourceCacheSynchronizer { private static final Logger ourLog = LoggerFactory.getLogger(SubscriptionLoader.class); diff --git a/hapi-fhir-jpaserver-subscription/src/main/java/ca/uhn/fhir/jpa/subscription/match/registry/SubscriptionRegistry.java b/hapi-fhir-jpaserver-subscription/src/main/java/ca/uhn/fhir/jpa/subscription/match/registry/SubscriptionRegistry.java index e791afa51e8..1a7742b13e4 100644 --- a/hapi-fhir-jpaserver-subscription/src/main/java/ca/uhn/fhir/jpa/subscription/match/registry/SubscriptionRegistry.java +++ b/hapi-fhir-jpaserver-subscription/src/main/java/ca/uhn/fhir/jpa/subscription/match/registry/SubscriptionRegistry.java @@ -27,6 +27,7 @@ import ca.uhn.fhir.jpa.subscription.channel.subscription.SubscriptionChannelRegi import ca.uhn.fhir.jpa.subscription.model.CanonicalSubscription; import ca.uhn.fhir.jpa.subscription.model.ChannelRetryConfiguration; import ca.uhn.fhir.util.HapiExtensions; +import jakarta.annotation.PreDestroy; import org.apache.commons.lang3.Validate; import org.hl7.fhir.instance.model.api.IBaseResource; import org.hl7.fhir.instance.model.api.IIdType; @@ -39,7 +40,6 @@ import java.util.Collection; import java.util.Collections; import java.util.List; import java.util.Optional; -import javax.annotation.PreDestroy; /** * Cache of active subscriptions. When a new subscription is added to the cache, a new Spring Channel is created diff --git a/hapi-fhir-jpaserver-subscription/src/main/java/ca/uhn/fhir/jpa/subscription/submit/interceptor/SubscriptionSubmitInterceptorLoader.java b/hapi-fhir-jpaserver-subscription/src/main/java/ca/uhn/fhir/jpa/subscription/submit/interceptor/SubscriptionSubmitInterceptorLoader.java index 2f72cb60509..c905a4960b6 100644 --- a/hapi-fhir-jpaserver-subscription/src/main/java/ca/uhn/fhir/jpa/subscription/submit/interceptor/SubscriptionSubmitInterceptorLoader.java +++ b/hapi-fhir-jpaserver-subscription/src/main/java/ca/uhn/fhir/jpa/subscription/submit/interceptor/SubscriptionSubmitInterceptorLoader.java @@ -23,13 +23,13 @@ import ca.uhn.fhir.interceptor.api.IInterceptorService; import ca.uhn.fhir.jpa.model.entity.StorageSettings; import ca.uhn.fhir.jpa.topic.SubscriptionTopicValidatingInterceptor; import com.google.common.annotations.VisibleForTesting; +import jakarta.annotation.PostConstruct; import org.hl7.fhir.dstu2.model.Subscription; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; import java.util.Set; -import javax.annotation.PostConstruct; public class SubscriptionSubmitInterceptorLoader { private static final Logger ourLog = LoggerFactory.getLogger(SubscriptionSubmitInterceptorLoader.class); diff --git a/hapi-fhir-jpaserver-subscription/src/main/java/ca/uhn/fhir/jpa/subscription/submit/interceptor/SynchronousSubscriptionMatcherInterceptor.java b/hapi-fhir-jpaserver-subscription/src/main/java/ca/uhn/fhir/jpa/subscription/submit/interceptor/SynchronousSubscriptionMatcherInterceptor.java index 33d655d6a78..33861b5e205 100644 --- a/hapi-fhir-jpaserver-subscription/src/main/java/ca/uhn/fhir/jpa/subscription/submit/interceptor/SynchronousSubscriptionMatcherInterceptor.java +++ b/hapi-fhir-jpaserver-subscription/src/main/java/ca/uhn/fhir/jpa/subscription/submit/interceptor/SynchronousSubscriptionMatcherInterceptor.java @@ -20,9 +20,11 @@ package ca.uhn.fhir.jpa.subscription.submit.interceptor; import ca.uhn.fhir.jpa.subscription.async.AsyncResourceModifiedProcessingSchedulerSvc; +import ca.uhn.fhir.jpa.subscription.channel.api.PayloadTooLargeException; import ca.uhn.fhir.jpa.subscription.match.matcher.matching.IResourceModifiedConsumer; import ca.uhn.fhir.jpa.subscription.model.ResourceModifiedMessage; import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.messaging.MessageDeliveryException; import org.springframework.transaction.support.TransactionSynchronizationAdapter; import org.springframework.transaction.support.TransactionSynchronizationManager; @@ -49,11 +51,33 @@ public class SynchronousSubscriptionMatcherInterceptor extends SubscriptionMatch @Override public void afterCommit() { - myResourceModifiedConsumer.submitResourceModified(theResourceModifiedMessage); + doSubmitResourceModified(theResourceModifiedMessage); } }); } else { + doSubmitResourceModified(theResourceModifiedMessage); + } + } + + /** + * Submit the message through the broker channel to the matcher. + * + * Note: most of our integrated tests for subscription assume we can successfully inflate the message and therefore + * does not run with an actual database to persist the data. In these cases, submitting the complete message (i.e. + * with payload) is OK. However, there are a few tests that do not assume it and do run with an actual DB. For them, + * we should null out the payload body before submitting. This try-catch block only covers the case where the + * payload is too large, which is enough for now. However, for better practice we might want to consider splitting + * this interceptor into two, each for tests with/without DB connection. + * @param theResourceModifiedMessage + */ + private void doSubmitResourceModified(ResourceModifiedMessage theResourceModifiedMessage) { + try { myResourceModifiedConsumer.submitResourceModified(theResourceModifiedMessage); + } catch (MessageDeliveryException e) { + if (e.getCause() instanceof PayloadTooLargeException) { + theResourceModifiedMessage.setPayloadToNull(); + myResourceModifiedConsumer.submitResourceModified(theResourceModifiedMessage); + } } } } diff --git a/hapi-fhir-jpaserver-subscription/src/main/java/ca/uhn/fhir/jpa/subscription/submit/svc/ResourceModifiedSubmitterSvc.java b/hapi-fhir-jpaserver-subscription/src/main/java/ca/uhn/fhir/jpa/subscription/submit/svc/ResourceModifiedSubmitterSvc.java index 7d768beefce..e57813bbc1a 100644 --- a/hapi-fhir-jpaserver-subscription/src/main/java/ca/uhn/fhir/jpa/subscription/submit/svc/ResourceModifiedSubmitterSvc.java +++ b/hapi-fhir-jpaserver-subscription/src/main/java/ca/uhn/fhir/jpa/subscription/submit/svc/ResourceModifiedSubmitterSvc.java @@ -35,7 +35,6 @@ import ca.uhn.fhir.rest.server.exceptions.ResourceNotFoundException; import ca.uhn.fhir.subscription.api.IResourceModifiedConsumerWithRetries; import ca.uhn.fhir.subscription.api.IResourceModifiedMessagePersistenceSvc; import org.apache.commons.lang3.Validate; -import org.hl7.fhir.r5.model.IdType; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.context.event.ContextRefreshedEvent; @@ -45,8 +44,6 @@ import org.springframework.messaging.MessageDeliveryException; import org.springframework.transaction.annotation.Propagation; import org.springframework.transaction.support.TransactionCallback; -import java.util.Optional; - import static ca.uhn.fhir.jpa.subscription.match.matcher.subscriber.SubscriptionMatchingSubscriber.SUBSCRIPTION_MATCHING_CHANNEL_NAME; /** @@ -145,62 +142,53 @@ public class ResourceModifiedSubmitterSvc implements IResourceModifiedConsumer, return theStatus -> { boolean processed = true; ResourceModifiedMessage resourceModifiedMessage = null; - try { + try { // delete the entry to lock the row to ensure unique processing boolean wasDeleted = deletePersistedResourceModifiedMessage( thePersistedResourceModifiedMessage.getPersistedResourceModifiedMessagePk()); - Optional optionalResourceModifiedMessage = - inflatePersistedResourceMessage(thePersistedResourceModifiedMessage); + // submit the resource modified message with empty payload, actual inflation is done by the matcher. + resourceModifiedMessage = + createResourceModifiedMessageWithoutInflation(thePersistedResourceModifiedMessage); - if (wasDeleted && optionalResourceModifiedMessage.isPresent()) { - // the PK did exist and we were able to deleted it, ie, we are the only one processing the message - resourceModifiedMessage = optionalResourceModifiedMessage.get(); + if (wasDeleted) { submitResourceModified(resourceModifiedMessage); } - } catch (MessageDeliveryException exception) { // we encountered an issue when trying to send the message so mark the transaction for rollback + String payloadId = "[unknown]"; + String subscriptionId = "[unknown]"; + if (resourceModifiedMessage != null) { + payloadId = resourceModifiedMessage.getPayloadId(); + subscriptionId = resourceModifiedMessage.getSubscriptionId(); + } ourLog.error( "Channel submission failed for resource with id {} matching subscription with id {}. Further attempts will be performed at later time.", - resourceModifiedMessage.getPayloadId(), - resourceModifiedMessage.getSubscriptionId()); + payloadId, + subscriptionId, + exception); processed = false; theStatus.setRollbackOnly(); + } catch (Exception ex) { + // catch other errors + ourLog.error( + "Unexpected error encountered while processing resource modified message. Marking as processed to prevent further errors.", + ex); + processed = true; } return processed; }; } - private Optional inflatePersistedResourceMessage( + private ResourceModifiedMessage createResourceModifiedMessageWithoutInflation( IPersistedResourceModifiedMessage thePersistedResourceModifiedMessage) { - ResourceModifiedMessage resourceModifiedMessage = null; - - try { - - resourceModifiedMessage = myResourceModifiedMessagePersistenceSvc.inflatePersistedResourceModifiedMessage( - thePersistedResourceModifiedMessage); - - } catch (ResourceNotFoundException e) { - IPersistedResourceModifiedMessagePK persistedResourceModifiedMessagePk = - thePersistedResourceModifiedMessage.getPersistedResourceModifiedMessagePk(); - - IdType idType = new IdType( - thePersistedResourceModifiedMessage.getResourceType(), - persistedResourceModifiedMessagePk.getResourcePid(), - persistedResourceModifiedMessagePk.getResourceVersion()); - - ourLog.warn( - "Scheduled submission will be ignored since resource {} cannot be found", idType.asStringValue()); - } - - return Optional.ofNullable(resourceModifiedMessage); + return myResourceModifiedMessagePersistenceSvc.createResourceModifiedMessageFromEntityWithoutInflation( + thePersistedResourceModifiedMessage); } private boolean deletePersistedResourceModifiedMessage(IPersistedResourceModifiedMessagePK theResourceModifiedPK) { - try { // delete the entry to lock the row to ensure unique processing return myResourceModifiedMessagePersistenceSvc.deleteByPK(theResourceModifiedPK); @@ -213,6 +201,9 @@ public class ResourceModifiedSubmitterSvc implements IResourceModifiedConsumer, // the message // successfully before we did. + return false; + } catch (Exception ex) { + ourLog.error("Unknown exception when deleting persisted resource modified message. Returning false.", ex); return false; } } diff --git a/hapi-fhir-jpaserver-subscription/src/main/java/ca/uhn/fhir/jpa/subscription/triggering/SubscriptionTriggeringSvcImpl.java b/hapi-fhir-jpaserver-subscription/src/main/java/ca/uhn/fhir/jpa/subscription/triggering/SubscriptionTriggeringSvcImpl.java index 2cbd1c0d291..7d543edf335 100644 --- a/hapi-fhir-jpaserver-subscription/src/main/java/ca/uhn/fhir/jpa/subscription/triggering/SubscriptionTriggeringSvcImpl.java +++ b/hapi-fhir-jpaserver-subscription/src/main/java/ca/uhn/fhir/jpa/subscription/triggering/SubscriptionTriggeringSvcImpl.java @@ -51,11 +51,13 @@ import ca.uhn.fhir.util.ParametersUtil; import ca.uhn.fhir.util.StopWatch; import ca.uhn.fhir.util.UrlUtil; import ca.uhn.fhir.util.ValidateUtil; +import com.google.common.collect.Lists; +import jakarta.annotation.Nullable; +import jakarta.annotation.PostConstruct; import org.apache.commons.lang3.ObjectUtils; import org.apache.commons.lang3.Validate; import org.apache.commons.lang3.concurrent.BasicThreadFactory; import org.apache.commons.lang3.time.DateUtils; -import org.apache.commons.lang3.tuple.Pair; import org.hl7.fhir.dstu2.model.IdType; import org.hl7.fhir.instance.model.api.IBaseParameters; import org.hl7.fhir.instance.model.api.IBaseResource; @@ -77,9 +79,8 @@ import java.util.concurrent.RejectedExecutionException; import java.util.concurrent.RejectedExecutionHandler; import java.util.concurrent.ThreadPoolExecutor; import java.util.concurrent.TimeUnit; +import java.util.concurrent.atomic.AtomicInteger; import java.util.stream.Collectors; -import javax.annotation.Nullable; -import javax.annotation.PostConstruct; import static ca.uhn.fhir.rest.server.provider.ProviderConstants.SUBSCRIPTION_TRIGGERING_PARAM_RESOURCE_ID; import static java.util.Objects.isNull; @@ -104,7 +105,7 @@ public class SubscriptionTriggeringSvcImpl implements ISubscriptionTriggeringSvc private JpaStorageSettings myStorageSettings; @Autowired - private ISearchCoordinatorSvc mySearchCoordinatorSvc; + private ISearchCoordinatorSvc> mySearchCoordinatorSvc; @Autowired private MatchUrlService myMatchUrlService; @@ -240,13 +241,12 @@ public class SubscriptionTriggeringSvcImpl implements ISubscriptionTriggeringSvc ourLog.info("Starting pass of subscription triggering job {}", theJobDetails.getJobId()); // Submit individual resources - int totalSubmitted = 0; - List>> futures = new ArrayList<>(); - while (theJobDetails.getRemainingResourceIds().size() > 0 && totalSubmitted < myMaxSubmitPerPass) { - totalSubmitted++; + AtomicInteger totalSubmitted = new AtomicInteger(0); + List> futures = new ArrayList<>(); + while (!theJobDetails.getRemainingResourceIds().isEmpty() && totalSubmitted.get() < myMaxSubmitPerPass) { + totalSubmitted.incrementAndGet(); String nextResourceId = theJobDetails.getRemainingResourceIds().remove(0); - Future future = submitResource(theJobDetails.getSubscriptionId(), nextResourceId); - futures.add(Pair.of(nextResourceId, future)); + submitResource(theJobDetails.getSubscriptionId(), nextResourceId); } // Make sure these all succeeded in submitting @@ -260,7 +260,7 @@ public class SubscriptionTriggeringSvcImpl implements ISubscriptionTriggeringSvc // to the broker. Note that querying of resource can be done synchronously or asynchronously if (isInitialStep(theJobDetails) && isNotEmpty(theJobDetails.getRemainingSearchUrls()) - && totalSubmitted < myMaxSubmitPerPass) { + && totalSubmitted.get() < myMaxSubmitPerPass) { String nextSearchUrl = theJobDetails.getRemainingSearchUrls().remove(0); RuntimeResourceDefinition resourceDef = UrlUtil.parseUrlResourceType(myFhirContext, nextSearchUrl); @@ -295,144 +295,88 @@ public class SubscriptionTriggeringSvcImpl implements ISubscriptionTriggeringSvc theJobDetails.setCurrentSearchLastUploadedIndex(-1); } - // processing step for synchronous processing mode - if (isNotBlank(theJobDetails.getCurrentSearchUrl()) && totalSubmitted < myMaxSubmitPerPass) { - List allCurrentResources; - - int fromIndex = theJobDetails.getCurrentSearchLastUploadedIndex() + 1; - - String searchUrl = theJobDetails.getCurrentSearchUrl(); - - ourLog.info( - "Triggered job [{}] - Starting synchronous processing at offset {} and index {}", - theJobDetails.getJobId(), - theJobDetails.getCurrentOffset(), - fromIndex); - - int submittableCount = myMaxSubmitPerPass - totalSubmitted; - int toIndex = fromIndex + submittableCount; - - if (nonNull(search) && !search.isEmpty()) { - - // we already have data from the initial step so process as much as we can. - ourLog.info("Triggered job[{}] will process up to {} resources", theJobDetails.getJobId(), toIndex); - allCurrentResources = search.getResources(0, toIndex); - - } else { - if (theJobDetails.getCurrentSearchCount() != null) { - toIndex = Math.min(toIndex, theJobDetails.getCurrentSearchCount()); - } - - RuntimeResourceDefinition resourceDef = UrlUtil.parseUrlResourceType(myFhirContext, searchUrl); - String queryPart = searchUrl.substring(searchUrl.indexOf('?')); - SearchParameterMap params = myMatchUrlService.translateMatchUrl(queryPart, resourceDef); - int offset = theJobDetails.getCurrentOffset() + fromIndex; - params.setOffset(offset); - params.setCount(toIndex); - - ourLog.info( - "Triggered job[{}] requesting {} resources from offset {}", - theJobDetails.getJobId(), - toIndex, - offset); - - search = - mySearchService.executeQuery(resourceDef.getName(), params, RequestPartitionId.allPartitions()); - allCurrentResources = search.getAllResources(); - } - - ourLog.info( - "Triggered job[{}] delivering {} resources", theJobDetails.getJobId(), allCurrentResources.size()); - int highestIndexSubmitted = theJobDetails.getCurrentSearchLastUploadedIndex(); - - for (IBaseResource nextResource : allCurrentResources) { - Future future = submitResource(theJobDetails.getSubscriptionId(), nextResource); - futures.add(Pair.of(nextResource.getIdElement().getIdPart(), future)); - totalSubmitted++; - highestIndexSubmitted++; - } - - if (validateFuturesAndReturnTrueIfWeShouldAbort(futures)) { - return; - } - - theJobDetails.setCurrentSearchLastUploadedIndex(highestIndexSubmitted); - - ourLog.info( - "Triggered job[{}] lastUploadedIndex is {}", - theJobDetails.getJobId(), - theJobDetails.getCurrentSearchLastUploadedIndex()); - - if (allCurrentResources.isEmpty() - || nonNull(theJobDetails.getCurrentSearchCount()) - && toIndex >= theJobDetails.getCurrentSearchCount()) { - ourLog.info( - "Triggered job[{}] for search URL {} has completed ", - theJobDetails.getJobId(), - theJobDetails.getCurrentSearchUrl()); - theJobDetails.setCurrentSearchResourceType(null); - theJobDetails.clearCurrentSearchUrl(); - theJobDetails.setCurrentSearchLastUploadedIndex(-1); - theJobDetails.setCurrentSearchCount(null); - } + /* + * Processing step for synchronous processing mode - This is only called if the + * server is configured to force offset searches, ie using ForceSynchronousSearchInterceptor. + * Otherwise, we'll always do async mode. + */ + if (isNotBlank(theJobDetails.getCurrentSearchUrl()) && totalSubmitted.get() < myMaxSubmitPerPass) { + processSynchronous(theJobDetails, totalSubmitted, futures, search); } // processing step for asynchronous processing mode - if (isNotBlank(theJobDetails.getCurrentSearchUuid()) && totalSubmitted < myMaxSubmitPerPass) { - int fromIndex = theJobDetails.getCurrentSearchLastUploadedIndex() + 1; + if (isNotBlank(theJobDetails.getCurrentSearchUuid()) && totalSubmitted.get() < myMaxSubmitPerPass) { + processAsynchronous(theJobDetails, totalSubmitted, futures); + } - IFhirResourceDao resourceDao = - myDaoRegistry.getResourceDao(theJobDetails.getCurrentSearchResourceType()); + ourLog.info( + "Subscription trigger job[{}] triggered {} resources in {}ms ({} res / second)", + theJobDetails.getJobId(), + totalSubmitted, + sw.getMillis(), + sw.getThroughput(totalSubmitted.get(), TimeUnit.SECONDS)); + } - int maxQuerySize = myMaxSubmitPerPass - totalSubmitted; - int toIndex; - if (theJobDetails.getCurrentSearchCount() != null) { - toIndex = Math.min(fromIndex + maxQuerySize, theJobDetails.getCurrentSearchCount()); - } else { - toIndex = fromIndex + maxQuerySize; - } + private void processAsynchronous( + SubscriptionTriggeringJobDetails theJobDetails, AtomicInteger totalSubmitted, List> futures) { + int fromIndex = theJobDetails.getCurrentSearchLastUploadedIndex() + 1; - ourLog.info( - "Triggering job[{}] search {} requesting resources {} - {}", - theJobDetails.getJobId(), - theJobDetails.getCurrentSearchUuid(), - fromIndex, - toIndex); + IFhirResourceDao resourceDao = myDaoRegistry.getResourceDao(theJobDetails.getCurrentSearchResourceType()); - List> resourceIds; - RequestPartitionId requestPartitionId = RequestPartitionId.allPartitions(); - resourceIds = mySearchCoordinatorSvc.getResources( - theJobDetails.getCurrentSearchUuid(), fromIndex, toIndex, null, requestPartitionId); + int maxQuerySize = myMaxSubmitPerPass - totalSubmitted.get(); + int toIndex; + if (theJobDetails.getCurrentSearchCount() != null) { + toIndex = Math.min(fromIndex + maxQuerySize, theJobDetails.getCurrentSearchCount()); + } else { + toIndex = fromIndex + maxQuerySize; + } - ourLog.info("Triggering job[{}] delivering {} resources", theJobDetails.getJobId(), resourceIds.size()); - int highestIndexSubmitted = theJobDetails.getCurrentSearchLastUploadedIndex(); + ourLog.info( + "Triggering job[{}] search {} requesting resources {} - {}", + theJobDetails.getJobId(), + theJobDetails.getCurrentSearchUuid(), + fromIndex, + toIndex); - String resourceType = myFhirContext.getResourceType(theJobDetails.getCurrentSearchResourceType()); - RuntimeResourceDefinition resourceDef = - myFhirContext.getResourceDefinition(theJobDetails.getCurrentSearchResourceType()); - ISearchBuilder searchBuilder = mySearchBuilderFactory.newSearchBuilder( - resourceDao, resourceType, resourceDef.getImplementingClass()); - List listToPopulate = new ArrayList<>(); + List> allResourceIds; + RequestPartitionId requestPartitionId = RequestPartitionId.allPartitions(); + allResourceIds = mySearchCoordinatorSvc.getResources( + theJobDetails.getCurrentSearchUuid(), fromIndex, toIndex, null, requestPartitionId); - myTransactionService.withSystemRequest().execute(() -> { - searchBuilder.loadResourcesByPid( - resourceIds, Collections.emptyList(), listToPopulate, false, new SystemRequestDetails()); - }); + ourLog.info("Triggering job[{}] delivering {} resources", theJobDetails.getJobId(), allResourceIds.size()); + AtomicInteger highestIndexSubmitted = new AtomicInteger(theJobDetails.getCurrentSearchLastUploadedIndex()); - for (IBaseResource nextResource : listToPopulate) { - Future future = submitResource(theJobDetails.getSubscriptionId(), nextResource); - futures.add(Pair.of(nextResource.getIdElement().getIdPart(), future)); - totalSubmitted++; - highestIndexSubmitted++; - } + List>> partitions = Lists.partition(allResourceIds, 100); + for (List> resourceIds : partitions) { + Runnable job = () -> { + String resourceType = myFhirContext.getResourceType(theJobDetails.getCurrentSearchResourceType()); + RuntimeResourceDefinition resourceDef = + myFhirContext.getResourceDefinition(theJobDetails.getCurrentSearchResourceType()); + ISearchBuilder searchBuilder = mySearchBuilderFactory.newSearchBuilder( + resourceDao, resourceType, resourceDef.getImplementingClass()); + List listToPopulate = new ArrayList<>(); - if (validateFuturesAndReturnTrueIfWeShouldAbort(futures)) { - return; - } + myTransactionService.withRequest(null).execute(() -> { + searchBuilder.loadResourcesByPid( + resourceIds, Collections.emptyList(), listToPopulate, false, new SystemRequestDetails()); + }); - theJobDetails.setCurrentSearchLastUploadedIndex(highestIndexSubmitted); + for (IBaseResource nextResource : listToPopulate) { + submitResource(theJobDetails.getSubscriptionId(), nextResource); + totalSubmitted.incrementAndGet(); + highestIndexSubmitted.incrementAndGet(); + } + }; - if (resourceIds.size() == 0 + Future future = myExecutorService.submit(job); + futures.add(future); + } + + if (!validateFuturesAndReturnTrueIfWeShouldAbort(futures)) { + + theJobDetails.setCurrentSearchLastUploadedIndex(highestIndexSubmitted.get()); + + if (allResourceIds.isEmpty() || (theJobDetails.getCurrentSearchCount() != null && toIndex >= theJobDetails.getCurrentSearchCount())) { ourLog.info( @@ -445,13 +389,93 @@ public class SubscriptionTriggeringSvcImpl implements ISubscriptionTriggeringSvc theJobDetails.setCurrentSearchCount(null); } } + } + + private void processSynchronous( + SubscriptionTriggeringJobDetails theJobDetails, + AtomicInteger totalSubmitted, + List> futures, + IBundleProvider search) { + List allCurrentResources; + + int fromIndex = theJobDetails.getCurrentSearchLastUploadedIndex() + 1; + + String searchUrl = theJobDetails.getCurrentSearchUrl(); ourLog.info( - "Subscription trigger job[{}] triggered {} resources in {}ms ({} res / second)", + "Triggered job [{}] - Starting synchronous processing at offset {} and index {}", theJobDetails.getJobId(), - totalSubmitted, - sw.getMillis(), - sw.getThroughput(totalSubmitted, TimeUnit.SECONDS)); + theJobDetails.getCurrentOffset(), + fromIndex); + + int submittableCount = myMaxSubmitPerPass - totalSubmitted.get(); + int toIndex = fromIndex + submittableCount; + + if (nonNull(search) && !search.isEmpty()) { + + if (search.getCurrentPageSize() != null) { + toIndex = search.getCurrentPageSize(); + } + + // we already have data from the initial step so process as much as we can. + ourLog.info("Triggered job[{}] will process up to {} resources", theJobDetails.getJobId(), toIndex); + allCurrentResources = search.getResources(0, toIndex); + + } else { + if (theJobDetails.getCurrentSearchCount() != null) { + toIndex = Math.min(toIndex, theJobDetails.getCurrentSearchCount()); + } + + RuntimeResourceDefinition resourceDef = UrlUtil.parseUrlResourceType(myFhirContext, searchUrl); + String queryPart = searchUrl.substring(searchUrl.indexOf('?')); + SearchParameterMap params = myMatchUrlService.translateMatchUrl(queryPart, resourceDef); + int offset = theJobDetails.getCurrentOffset() + fromIndex; + params.setOffset(offset); + params.setCount(toIndex); + + ourLog.info( + "Triggered job[{}] requesting {} resources from offset {}", + theJobDetails.getJobId(), + toIndex, + offset); + + search = mySearchService.executeQuery(resourceDef.getName(), params, RequestPartitionId.allPartitions()); + allCurrentResources = search.getResources(0, submittableCount); + } + + ourLog.info("Triggered job[{}] delivering {} resources", theJobDetails.getJobId(), allCurrentResources.size()); + AtomicInteger highestIndexSubmitted = new AtomicInteger(theJobDetails.getCurrentSearchLastUploadedIndex()); + + for (IBaseResource nextResource : allCurrentResources) { + Future future = + myExecutorService.submit(() -> submitResource(theJobDetails.getSubscriptionId(), nextResource)); + futures.add(future); + totalSubmitted.incrementAndGet(); + highestIndexSubmitted.incrementAndGet(); + } + + if (!validateFuturesAndReturnTrueIfWeShouldAbort(futures)) { + + theJobDetails.setCurrentSearchLastUploadedIndex(highestIndexSubmitted.get()); + + ourLog.info( + "Triggered job[{}] lastUploadedIndex is {}", + theJobDetails.getJobId(), + theJobDetails.getCurrentSearchLastUploadedIndex()); + + if (allCurrentResources.isEmpty() + || nonNull(theJobDetails.getCurrentSearchCount()) + && toIndex > theJobDetails.getCurrentSearchCount()) { + ourLog.info( + "Triggered job[{}] for search URL {} has completed ", + theJobDetails.getJobId(), + theJobDetails.getCurrentSearchUrl()); + theJobDetails.setCurrentSearchResourceType(null); + theJobDetails.clearCurrentSearchUrl(); + theJobDetails.setCurrentSearchLastUploadedIndex(-1); + theJobDetails.setCurrentSearchCount(null); + } + } } private boolean isInitialStep(SubscriptionTriggeringJobDetails theJobDetails) { @@ -462,34 +486,31 @@ public class SubscriptionTriggeringSvcImpl implements ISubscriptionTriggeringSvc return isInitialStep(theJobDetails); } - private boolean validateFuturesAndReturnTrueIfWeShouldAbort(List>> theIdToFutures) { + private boolean validateFuturesAndReturnTrueIfWeShouldAbort(List> theFutures) { - for (Pair> next : theIdToFutures) { - String nextDeliveredId = next.getKey(); + for (Future nextFuture : theFutures) { try { - Future nextFuture = next.getValue(); nextFuture.get(); - ourLog.info("Finished redelivering {}", nextDeliveredId); } catch (Exception e) { - ourLog.error("Failure triggering resource " + nextDeliveredId, e); + ourLog.error("Failure triggering resource", e); return true; } } // Clear the list since it will potentially get reused - theIdToFutures.clear(); + theFutures.clear(); return false; } - private Future submitResource(String theSubscriptionId, String theResourceIdToTrigger) { + private void submitResource(String theSubscriptionId, String theResourceIdToTrigger) { org.hl7.fhir.r4.model.IdType resourceId = new org.hl7.fhir.r4.model.IdType(theResourceIdToTrigger); IFhirResourceDao dao = myDaoRegistry.getResourceDao(resourceId.getResourceType()); IBaseResource resourceToTrigger = dao.read(resourceId, SystemRequestDetails.forAllPartitions()); - return submitResource(theSubscriptionId, resourceToTrigger); + submitResource(theSubscriptionId, resourceToTrigger); } - private Future submitResource(String theSubscriptionId, IBaseResource theResourceToTrigger) { + private void submitResource(String theSubscriptionId, IBaseResource theResourceToTrigger) { ourLog.info( "Submitting resource {} to subscription {}", @@ -500,24 +521,23 @@ public class SubscriptionTriggeringSvcImpl implements ISubscriptionTriggeringSvc myFhirContext, theResourceToTrigger, ResourceModifiedMessage.OperationTypeEnum.UPDATE); msg.setSubscriptionId(theSubscriptionId); - return myExecutorService.submit(() -> { - for (int i = 0; ; i++) { - try { - myResourceModifiedConsumer.submitResourceModified(msg); - break; - } catch (Exception e) { - if (i >= 3) { - throw new InternalErrorException(Msg.code(25) + e); - } + for (int i = 0; ; i++) { + try { + myResourceModifiedConsumer.submitResourceModified(msg); + break; + } catch (Exception e) { + if (i >= 3) { + throw new InternalErrorException(Msg.code(25) + e); + } - ourLog.warn( - "Exception while retriggering subscriptions (going to sleep and retry): {}", e.toString()); + ourLog.warn("Exception while retriggering subscriptions (going to sleep and retry): {}", e.toString()); + try { Thread.sleep(1000); + } catch (InterruptedException ex) { + Thread.currentThread().interrupt(); } } - - return null; - }); + } } public void cancelAll() { diff --git a/hapi-fhir-jpaserver-subscription/src/main/java/ca/uhn/fhir/jpa/topic/SubscriptionTopicConfig.java b/hapi-fhir-jpaserver-subscription/src/main/java/ca/uhn/fhir/jpa/topic/SubscriptionTopicConfig.java index 6e53878e4e4..c1c0dbbf8e8 100644 --- a/hapi-fhir-jpaserver-subscription/src/main/java/ca/uhn/fhir/jpa/topic/SubscriptionTopicConfig.java +++ b/hapi-fhir-jpaserver-subscription/src/main/java/ca/uhn/fhir/jpa/topic/SubscriptionTopicConfig.java @@ -22,39 +22,74 @@ package ca.uhn.fhir.jpa.topic; import ca.uhn.fhir.context.FhirContext; import ca.uhn.fhir.jpa.api.dao.DaoRegistry; import ca.uhn.fhir.jpa.searchparam.matcher.SearchParamMatcher; +import ca.uhn.fhir.jpa.subscription.match.matcher.matching.SubscriptionStrategyEvaluator; import ca.uhn.fhir.jpa.subscription.submit.interceptor.SubscriptionQueryValidator; import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Lazy; public class SubscriptionTopicConfig { @Bean SubscriptionTopicMatchingSubscriber subscriptionTopicMatchingSubscriber(FhirContext theFhirContext) { - return new SubscriptionTopicMatchingSubscriber(theFhirContext); + switch (theFhirContext.getVersion().getVersion()) { + case R5: + case R4B: + return new SubscriptionTopicMatchingSubscriber(theFhirContext); + default: + return null; + } } @Bean + @Lazy SubscriptionTopicRegistry subscriptionTopicRegistry() { return new SubscriptionTopicRegistry(); } @Bean + @Lazy SubscriptionTopicSupport subscriptionTopicSupport( FhirContext theFhirContext, DaoRegistry theDaoRegistry, SearchParamMatcher theSearchParamMatcher) { return new SubscriptionTopicSupport(theFhirContext, theDaoRegistry, theSearchParamMatcher); } @Bean - SubscriptionTopicLoader subscriptionTopicLoader() { - return new SubscriptionTopicLoader(); + SubscriptionTopicLoader subscriptionTopicLoader(FhirContext theFhirContext) { + switch (theFhirContext.getVersion().getVersion()) { + case R5: + case R4B: + return new SubscriptionTopicLoader(); + default: + return null; + } } @Bean - SubscriptionTopicRegisteringSubscriber subscriptionTopicRegisteringSubscriber() { - return new SubscriptionTopicRegisteringSubscriber(); + SubscriptionTopicRegisteringSubscriber subscriptionTopicRegisteringSubscriber(FhirContext theFhirContext) { + switch (theFhirContext.getVersion().getVersion()) { + case R5: + case R4B: + return new SubscriptionTopicRegisteringSubscriber(); + default: + return null; + } + } + + @Bean + @Lazy + public SubscriptionQueryValidator subscriptionQueryValidator( + DaoRegistry theDaoRegistry, SubscriptionStrategyEvaluator theSubscriptionStrategyEvaluator) { + return new SubscriptionQueryValidator(theDaoRegistry, theSubscriptionStrategyEvaluator); } @Bean SubscriptionTopicValidatingInterceptor subscriptionTopicValidatingInterceptor( FhirContext theFhirContext, SubscriptionQueryValidator theSubscriptionQueryValidator) { - return new SubscriptionTopicValidatingInterceptor(theFhirContext, theSubscriptionQueryValidator); + switch (theFhirContext.getVersion().getVersion()) { + case R5: + case R4B: + return new SubscriptionTopicValidatingInterceptor(theFhirContext, theSubscriptionQueryValidator); + default: + return null; + } } } diff --git a/hapi-fhir-jpaserver-subscription/src/main/java/ca/uhn/fhir/jpa/topic/SubscriptionTopicDispatchRequest.java b/hapi-fhir-jpaserver-subscription/src/main/java/ca/uhn/fhir/jpa/topic/SubscriptionTopicDispatchRequest.java index 4727829ff78..5d6246eba1a 100644 --- a/hapi-fhir-jpaserver-subscription/src/main/java/ca/uhn/fhir/jpa/topic/SubscriptionTopicDispatchRequest.java +++ b/hapi-fhir-jpaserver-subscription/src/main/java/ca/uhn/fhir/jpa/topic/SubscriptionTopicDispatchRequest.java @@ -23,11 +23,11 @@ import ca.uhn.fhir.interceptor.model.RequestPartitionId; import ca.uhn.fhir.jpa.searchparam.matcher.InMemoryMatchResult; import ca.uhn.fhir.jpa.topic.filter.ISubscriptionTopicFilterMatcher; import ca.uhn.fhir.rest.api.RestOperationTypeEnum; +import jakarta.annotation.Nonnull; +import jakarta.annotation.Nullable; import org.hl7.fhir.instance.model.api.IBaseResource; import java.util.List; -import javax.annotation.Nonnull; -import javax.annotation.Nullable; public class SubscriptionTopicDispatchRequest { @Nonnull diff --git a/hapi-fhir-jpaserver-subscription/src/main/java/ca/uhn/fhir/jpa/topic/SubscriptionTopicLoader.java b/hapi-fhir-jpaserver-subscription/src/main/java/ca/uhn/fhir/jpa/topic/SubscriptionTopicLoader.java index daaaa3bcc20..3b3e2c1e69a 100644 --- a/hapi-fhir-jpaserver-subscription/src/main/java/ca/uhn/fhir/jpa/topic/SubscriptionTopicLoader.java +++ b/hapi-fhir-jpaserver-subscription/src/main/java/ca/uhn/fhir/jpa/topic/SubscriptionTopicLoader.java @@ -27,6 +27,7 @@ import ca.uhn.fhir.jpa.searchparam.SearchParameterMap; import ca.uhn.fhir.rest.param.TokenParam; import ca.uhn.fhir.subscription.SubscriptionConstants; import ca.uhn.fhir.util.Logs; +import jakarta.annotation.Nonnull; import org.hl7.fhir.instance.model.api.IBaseResource; import org.hl7.fhir.r5.model.Enumerations; import org.hl7.fhir.r5.model.SubscriptionTopic; @@ -36,7 +37,6 @@ import org.springframework.beans.factory.annotation.Autowired; import java.util.HashSet; import java.util.List; import java.util.Set; -import javax.annotation.Nonnull; public class SubscriptionTopicLoader extends BaseResourceCacheSynchronizer { private static final Logger ourLog = Logs.getSubscriptionTopicLog(); diff --git a/hapi-fhir-jpaserver-subscription/src/main/java/ca/uhn/fhir/jpa/topic/SubscriptionTopicMatchingSubscriber.java b/hapi-fhir-jpaserver-subscription/src/main/java/ca/uhn/fhir/jpa/topic/SubscriptionTopicMatchingSubscriber.java index f8a195ca174..59c32c3ef36 100644 --- a/hapi-fhir-jpaserver-subscription/src/main/java/ca/uhn/fhir/jpa/topic/SubscriptionTopicMatchingSubscriber.java +++ b/hapi-fhir-jpaserver-subscription/src/main/java/ca/uhn/fhir/jpa/topic/SubscriptionTopicMatchingSubscriber.java @@ -24,13 +24,13 @@ import ca.uhn.fhir.interceptor.api.HookParams; import ca.uhn.fhir.interceptor.api.IInterceptorBroadcaster; import ca.uhn.fhir.interceptor.api.Pointcut; import ca.uhn.fhir.jpa.searchparam.matcher.InMemoryMatchResult; -import ca.uhn.fhir.jpa.subscription.match.matcher.subscriber.SubscriptionMatchDeliverer; -import ca.uhn.fhir.jpa.subscription.match.registry.SubscriptionRegistry; import ca.uhn.fhir.jpa.subscription.model.ResourceModifiedJsonMessage; import ca.uhn.fhir.jpa.subscription.model.ResourceModifiedMessage; import ca.uhn.fhir.jpa.topic.filter.InMemoryTopicFilterMatcher; import ca.uhn.fhir.rest.api.RestOperationTypeEnum; +import ca.uhn.fhir.subscription.api.IResourceModifiedMessagePersistenceSvc; import ca.uhn.fhir.util.Logs; +import jakarta.annotation.Nonnull; import org.hl7.fhir.instance.model.api.IBaseResource; import org.hl7.fhir.r5.model.SubscriptionTopic; import org.slf4j.Logger; @@ -42,7 +42,7 @@ import org.springframework.messaging.MessagingException; import java.util.Collection; import java.util.Collections; import java.util.List; -import javax.annotation.Nonnull; +import java.util.Optional; public class SubscriptionTopicMatchingSubscriber implements MessageHandler { private static final Logger ourLog = Logs.getSubscriptionTopicLog(); @@ -55,15 +55,6 @@ public class SubscriptionTopicMatchingSubscriber implements MessageHandler { @Autowired SubscriptionTopicRegistry mySubscriptionTopicRegistry; - @Autowired - SubscriptionRegistry mySubscriptionRegistry; - - @Autowired - SubscriptionMatchDeliverer mySubscriptionMatchDeliverer; - - @Autowired - SubscriptionTopicPayloadBuilder mySubscriptionTopicPayloadBuilder; - @Autowired private IInterceptorBroadcaster myInterceptorBroadcaster; @@ -73,6 +64,9 @@ public class SubscriptionTopicMatchingSubscriber implements MessageHandler { @Autowired private InMemoryTopicFilterMatcher myInMemoryTopicFilterMatcher; + @Autowired + private IResourceModifiedMessagePersistenceSvc myResourceModifiedMessagePersistenceSvc; + public SubscriptionTopicMatchingSubscriber(FhirContext theFhirContext) { myFhirContext = theFhirContext; } @@ -88,6 +82,16 @@ public class SubscriptionTopicMatchingSubscriber implements MessageHandler { ResourceModifiedMessage msg = ((ResourceModifiedJsonMessage) theMessage).getPayload(); + if (msg.getPayload(myFhirContext) == null) { + // inflate the message and ignore any resource that cannot be found. + Optional inflatedMsg = + myResourceModifiedMessagePersistenceSvc.inflatePersistedResourceModifiedMessageOrNull(msg); + if (inflatedMsg.isEmpty()) { + return; + } + msg = inflatedMsg.get(); + } + // Interceptor call: SUBSCRIPTION_TOPIC_BEFORE_PERSISTED_RESOURCE_CHECKED HookParams params = new HookParams().add(ResourceModifiedMessage.class, msg); if (!myInterceptorBroadcaster.callHooks( diff --git a/hapi-fhir-jpaserver-subscription/src/main/java/ca/uhn/fhir/jpa/topic/SubscriptionTopicPayloadBuilder.java b/hapi-fhir-jpaserver-subscription/src/main/java/ca/uhn/fhir/jpa/topic/SubscriptionTopicPayloadBuilder.java index 2efab3bb355..39b3c5c4e5f 100644 --- a/hapi-fhir-jpaserver-subscription/src/main/java/ca/uhn/fhir/jpa/topic/SubscriptionTopicPayloadBuilder.java +++ b/hapi-fhir-jpaserver-subscription/src/main/java/ca/uhn/fhir/jpa/topic/SubscriptionTopicPayloadBuilder.java @@ -23,12 +23,14 @@ import ca.uhn.fhir.context.FhirContext; import ca.uhn.fhir.context.FhirVersionEnum; import ca.uhn.fhir.i18n.Msg; import ca.uhn.fhir.jpa.subscription.match.registry.ActiveSubscription; +import ca.uhn.fhir.jpa.subscription.model.CanonicalSubscription; import ca.uhn.fhir.jpa.topic.status.INotificationStatusBuilder; import ca.uhn.fhir.jpa.topic.status.R4BNotificationStatusBuilder; import ca.uhn.fhir.jpa.topic.status.R4NotificationStatusBuilder; import ca.uhn.fhir.jpa.topic.status.R5NotificationStatusBuilder; import ca.uhn.fhir.rest.api.RestOperationTypeEnum; import ca.uhn.fhir.util.BundleBuilder; +import org.apache.commons.lang3.ObjectUtils; import org.hl7.fhir.instance.model.api.IBaseBundle; import org.hl7.fhir.instance.model.api.IBaseResource; import org.hl7.fhir.r5.model.Bundle; @@ -37,6 +39,8 @@ import org.slf4j.LoggerFactory; import java.util.List; +import static org.hl7.fhir.r5.model.Subscription.SubscriptionPayloadContent.FULLRESOURCE; + public class SubscriptionTopicPayloadBuilder { private static final Logger ourLog = LoggerFactory.getLogger(SubscriptionTopicPayloadBuilder.class); private final FhirContext myFhirContext; @@ -73,7 +77,7 @@ public class SubscriptionTopicPayloadBuilder { myNotificationStatusBuilder.buildNotificationStatus(theResources, theActiveSubscription, theTopicUrl); bundleBuilder.addCollectionEntry(notificationStatus); - addResources(bundleBuilder, theResources, theRestOperationType); + addResources(theResources, theActiveSubscription.getSubscription(), theRestOperationType, bundleBuilder); // WIP STR5 add support for notificationShape include, revinclude // Note we need to set the bundle type after we add the resources since adding the resources automatically sets @@ -87,7 +91,46 @@ public class SubscriptionTopicPayloadBuilder { return retval; } - private static void addResources( + private void addResources( + List theResources, + CanonicalSubscription theCanonicalSubscription, + RestOperationTypeEnum theRestOperationType, + BundleBuilder theBundleBuilder) { + + org.hl7.fhir.r5.model.Subscription.SubscriptionPayloadContent content = + ObjectUtils.defaultIfNull(theCanonicalSubscription.getContent(), FULLRESOURCE); + + switch (content) { + case EMPTY: + // skip adding resource to the Bundle + break; + case IDONLY: + addIdOnly(theBundleBuilder, theResources, theRestOperationType); + break; + case FULLRESOURCE: + addFullResources(theBundleBuilder, theResources, theRestOperationType); + break; + } + } + + private void addIdOnly( + BundleBuilder bundleBuilder, List theResources, RestOperationTypeEnum theRestOperationType) { + for (IBaseResource resource : theResources) { + switch (theRestOperationType) { + case CREATE: + bundleBuilder.addTransactionCreateEntryIdOnly(resource); + break; + case UPDATE: + bundleBuilder.addTransactionUpdateIdOnlyEntry(resource); + break; + case DELETE: + bundleBuilder.addTransactionDeleteEntry(resource); + break; + } + } + } + + private void addFullResources( BundleBuilder bundleBuilder, List theResources, RestOperationTypeEnum theRestOperationType) { for (IBaseResource resource : theResources) { switch (theRestOperationType) { diff --git a/hapi-fhir-jpaserver-subscription/src/main/java/ca/uhn/fhir/jpa/topic/SubscriptionTopicRegisteringSubscriber.java b/hapi-fhir-jpaserver-subscription/src/main/java/ca/uhn/fhir/jpa/topic/SubscriptionTopicRegisteringSubscriber.java index c2c17a7e5f2..27ba8d9971f 100644 --- a/hapi-fhir-jpaserver-subscription/src/main/java/ca/uhn/fhir/jpa/topic/SubscriptionTopicRegisteringSubscriber.java +++ b/hapi-fhir-jpaserver-subscription/src/main/java/ca/uhn/fhir/jpa/topic/SubscriptionTopicRegisteringSubscriber.java @@ -29,6 +29,7 @@ import ca.uhn.fhir.rest.api.server.RequestDetails; import ca.uhn.fhir.rest.api.server.SystemRequestDetails; import ca.uhn.fhir.rest.server.exceptions.ResourceGoneException; import ca.uhn.fhir.util.Logs; +import jakarta.annotation.Nonnull; import org.hl7.fhir.instance.model.api.IBaseResource; import org.hl7.fhir.instance.model.api.IIdType; import org.hl7.fhir.r5.model.Enumerations; @@ -39,8 +40,6 @@ import org.springframework.messaging.Message; import org.springframework.messaging.MessageHandler; import org.springframework.messaging.MessagingException; -import javax.annotation.Nonnull; - /** * Responsible for transitioning subscription resources from REQUESTED to ACTIVE * Once activated, the subscription is added to the SubscriptionRegistry. diff --git a/hapi-fhir-jpaserver-subscription/src/main/java/ca/uhn/fhir/jpa/topic/SubscriptionTopicUtil.java b/hapi-fhir-jpaserver-subscription/src/main/java/ca/uhn/fhir/jpa/topic/SubscriptionTopicUtil.java index 7d863fc077c..95908704ad0 100644 --- a/hapi-fhir-jpaserver-subscription/src/main/java/ca/uhn/fhir/jpa/topic/SubscriptionTopicUtil.java +++ b/hapi-fhir-jpaserver-subscription/src/main/java/ca/uhn/fhir/jpa/topic/SubscriptionTopicUtil.java @@ -19,11 +19,20 @@ */ package ca.uhn.fhir.jpa.topic; +import ca.uhn.fhir.context.FhirContext; +import ca.uhn.fhir.jpa.subscription.model.CanonicalSubscription; +import ca.uhn.fhir.jpa.subscription.model.ResourceModifiedJsonMessage; import ca.uhn.fhir.rest.server.messaging.BaseResourceMessage; +import ca.uhn.fhir.util.BundleUtil; +import org.hl7.fhir.instance.model.api.IBaseBundle; +import org.hl7.fhir.instance.model.api.IBaseResource; +import org.hl7.fhir.r5.model.BaseReference; import org.hl7.fhir.r5.model.Enumeration; +import org.hl7.fhir.r5.model.SubscriptionStatus; import org.hl7.fhir.r5.model.SubscriptionTopic; import java.util.List; +import java.util.Objects; public class SubscriptionTopicUtil { public static boolean matches( @@ -45,4 +54,33 @@ public class SubscriptionTopicUtil { } return false; } + + /** + * Extracts source resource from bundle contained in {@link ResourceModifiedJsonMessage} payload. + * Used for R5 resource modified message handling. + */ + public static IBaseResource extractResourceFromBundle(FhirContext myFhirContext, IBaseBundle theBundle) { + List resources = BundleUtil.toListOfResources(myFhirContext, theBundle); + + return resources.stream() + .filter(SubscriptionStatus.class::isInstance) + .map(SubscriptionStatus.class::cast) + .flatMap(subscriptionStatus -> subscriptionStatus.getNotificationEvent().stream()) + .filter(SubscriptionStatus.SubscriptionStatusNotificationEventComponent::hasFocus) + .map(SubscriptionStatus.SubscriptionStatusNotificationEventComponent::getFocus) + .map(BaseReference::getResource) + .filter(Objects::nonNull) + .findFirst() + .orElse(null); + } + + /** + * Checks if {@link CanonicalSubscription} has EMPTY {@link org.hl7.fhir.r5.model.Subscription.SubscriptionPayloadContent} + * Used for R5/R4B/R4 Notification Status object building. + */ + public static boolean isEmptyContentTopicSubscription(CanonicalSubscription theCanonicalSubscription) { + return theCanonicalSubscription.isTopicSubscription() + && org.hl7.fhir.r5.model.Subscription.SubscriptionPayloadContent.EMPTY + == theCanonicalSubscription.getTopicSubscription().getContent(); + } } diff --git a/hapi-fhir-jpaserver-subscription/src/main/java/ca/uhn/fhir/jpa/topic/filter/SubscriptionTopicFilterUtil.java b/hapi-fhir-jpaserver-subscription/src/main/java/ca/uhn/fhir/jpa/topic/filter/SubscriptionTopicFilterUtil.java index 99558c07b7f..a9deaf51a49 100644 --- a/hapi-fhir-jpaserver-subscription/src/main/java/ca/uhn/fhir/jpa/topic/filter/SubscriptionTopicFilterUtil.java +++ b/hapi-fhir-jpaserver-subscription/src/main/java/ca/uhn/fhir/jpa/topic/filter/SubscriptionTopicFilterUtil.java @@ -22,11 +22,10 @@ package ca.uhn.fhir.jpa.topic.filter; import ca.uhn.fhir.jpa.subscription.model.CanonicalTopicSubscription; import ca.uhn.fhir.jpa.subscription.model.CanonicalTopicSubscriptionFilter; import ca.uhn.fhir.util.Logs; +import jakarta.annotation.Nonnull; import org.hl7.fhir.instance.model.api.IBaseResource; import org.slf4j.Logger; -import javax.annotation.Nonnull; - public final class SubscriptionTopicFilterUtil { private static final Logger ourLog = Logs.getSubscriptionTopicLog(); diff --git a/hapi-fhir-jpaserver-subscription/src/main/java/ca/uhn/fhir/jpa/topic/status/R4NotificationStatusBuilder.java b/hapi-fhir-jpaserver-subscription/src/main/java/ca/uhn/fhir/jpa/topic/status/R4NotificationStatusBuilder.java index 68da40e3b99..16300900bda 100644 --- a/hapi-fhir-jpaserver-subscription/src/main/java/ca/uhn/fhir/jpa/topic/status/R4NotificationStatusBuilder.java +++ b/hapi-fhir-jpaserver-subscription/src/main/java/ca/uhn/fhir/jpa/topic/status/R4NotificationStatusBuilder.java @@ -21,6 +21,8 @@ package ca.uhn.fhir.jpa.topic.status; import ca.uhn.fhir.context.FhirContext; import ca.uhn.fhir.jpa.subscription.match.registry.ActiveSubscription; +import ca.uhn.fhir.jpa.subscription.model.CanonicalSubscription; +import ca.uhn.fhir.jpa.topic.SubscriptionTopicUtil; import ca.uhn.fhir.subscription.SubscriptionConstants; import org.hl7.fhir.instance.model.api.IBaseResource; import org.hl7.fhir.r4.model.CanonicalType; @@ -46,6 +48,7 @@ public class R4NotificationStatusBuilder implements INotificationStatusBuilder

    theResources, ActiveSubscription theActiveSubscription, String theTopicUrl) { Long eventNumber = theActiveSubscription.getDeliveriesCount(); + CanonicalSubscription canonicalSubscription = theActiveSubscription.getSubscription(); // See http://build.fhir.org/ig/HL7/fhir-subscription-backport-ig/Parameters-r4-notification-status.json.html // and @@ -66,12 +69,12 @@ public class R4NotificationStatusBuilder implements INotificationStatusBuilder

    0) { + if (!theResources.isEmpty() && !SubscriptionTopicUtil.isEmptyContentTopicSubscription(canonicalSubscription)) { IBaseResource firstResource = theResources.get(0); - notificationEvent - .addPart() - .setName("focus") - .setValue(new Reference(firstResource.getIdElement().toUnqualifiedVersionless())); + Reference resourceReference = + new Reference(firstResource.getIdElement().toUnqualifiedVersionless()); + + notificationEvent.addPart().setName("focus").setValue(resourceReference); } return parameters; diff --git a/hapi-fhir-jpaserver-subscription/src/main/java/ca/uhn/fhir/jpa/topic/status/R5NotificationStatusBuilder.java b/hapi-fhir-jpaserver-subscription/src/main/java/ca/uhn/fhir/jpa/topic/status/R5NotificationStatusBuilder.java index c9128d9f638..8140066b929 100644 --- a/hapi-fhir-jpaserver-subscription/src/main/java/ca/uhn/fhir/jpa/topic/status/R5NotificationStatusBuilder.java +++ b/hapi-fhir-jpaserver-subscription/src/main/java/ca/uhn/fhir/jpa/topic/status/R5NotificationStatusBuilder.java @@ -21,6 +21,8 @@ package ca.uhn.fhir.jpa.topic.status; import ca.uhn.fhir.context.FhirContext; import ca.uhn.fhir.jpa.subscription.match.registry.ActiveSubscription; +import ca.uhn.fhir.jpa.subscription.model.CanonicalSubscription; +import ca.uhn.fhir.jpa.topic.SubscriptionTopicUtil; import org.hl7.fhir.instance.model.api.IBaseResource; import org.hl7.fhir.r5.model.Enumerations; import org.hl7.fhir.r5.model.Reference; @@ -40,6 +42,7 @@ public class R5NotificationStatusBuilder implements INotificationStatusBuilder theResources, ActiveSubscription theActiveSubscription, String theTopicUrl) { long eventNumber = theActiveSubscription.getDeliveriesCount(); + CanonicalSubscription canonicalSubscription = theActiveSubscription.getSubscription(); SubscriptionStatus subscriptionStatus = new SubscriptionStatus(); subscriptionStatus.setId(UUID.randomUUID().toString()); @@ -50,7 +53,7 @@ public class R5NotificationStatusBuilder implements INotificationStatusBuilder 0) { + if (!theResources.isEmpty() && !SubscriptionTopicUtil.isEmptyContentTopicSubscription(canonicalSubscription)) { event.setFocus(new Reference(theResources.get(0).getIdElement())); } subscriptionStatus.setSubscription( diff --git a/hapi-fhir-jpaserver-subscription/src/test/java/ca/uhn/fhir/jpa/subscription/channel/subscription/BroadcastingSubscribableChannelWrapperTest.java b/hapi-fhir-jpaserver-subscription/src/test/java/ca/uhn/fhir/jpa/subscription/channel/subscription/BroadcastingSubscribableChannelWrapperTest.java index 4c29d2b86ad..f78878cb75f 100644 --- a/hapi-fhir-jpaserver-subscription/src/test/java/ca/uhn/fhir/jpa/subscription/channel/subscription/BroadcastingSubscribableChannelWrapperTest.java +++ b/hapi-fhir-jpaserver-subscription/src/test/java/ca/uhn/fhir/jpa/subscription/channel/subscription/BroadcastingSubscribableChannelWrapperTest.java @@ -27,7 +27,7 @@ class BroadcastingSubscribableChannelWrapperTest { try { svc.send(new ResourceModifiedJsonMessage(new ResourceModifiedMessage())); } catch (MessageDeliveryException e) { - assertThat(e.getMessage(), containsString("Channel has zero subscribers")); + assertThat(e.getCause().getMessage(), containsString("Channel has zero subscribers")); } } diff --git a/hapi-fhir-jpaserver-subscription/src/test/java/ca/uhn/fhir/jpa/subscription/match/deliver/BaseSubscriptionDeliverySubscriberTest.java b/hapi-fhir-jpaserver-subscription/src/test/java/ca/uhn/fhir/jpa/subscription/match/deliver/BaseSubscriptionDeliverySubscriberTest.java index 8bfb71cb182..42cb241235e 100644 --- a/hapi-fhir-jpaserver-subscription/src/test/java/ca/uhn/fhir/jpa/subscription/match/deliver/BaseSubscriptionDeliverySubscriberTest.java +++ b/hapi-fhir-jpaserver-subscription/src/test/java/ca/uhn/fhir/jpa/subscription/match/deliver/BaseSubscriptionDeliverySubscriberTest.java @@ -8,10 +8,13 @@ import ca.uhn.fhir.interceptor.api.Pointcut; import ca.uhn.fhir.interceptor.model.RequestPartitionId; import ca.uhn.fhir.jpa.api.dao.DaoRegistry; import ca.uhn.fhir.jpa.api.dao.IFhirResourceDao; +import ca.uhn.fhir.jpa.model.entity.StorageSettings; import ca.uhn.fhir.jpa.searchparam.MatchUrlService; import ca.uhn.fhir.jpa.searchparam.SearchParameterMap; import ca.uhn.fhir.jpa.subscription.channel.api.IChannelFactory; import ca.uhn.fhir.jpa.subscription.channel.api.IChannelProducer; +import ca.uhn.fhir.jpa.subscription.match.deliver.email.IEmailSender; +import ca.uhn.fhir.jpa.subscription.match.deliver.email.SubscriptionDeliveringEmailSubscriber; import ca.uhn.fhir.jpa.subscription.match.deliver.message.SubscriptionDeliveringMessageSubscriber; import ca.uhn.fhir.jpa.subscription.match.deliver.resthook.SubscriptionDeliveringRestHookSubscriber; import ca.uhn.fhir.jpa.subscription.match.registry.SubscriptionRegistry; @@ -26,6 +29,7 @@ import ca.uhn.fhir.rest.client.api.IGenericClient; import ca.uhn.fhir.rest.client.api.IRestfulClientFactory; import ca.uhn.fhir.rest.server.SimpleBundleProvider; import ca.uhn.fhir.rest.server.exceptions.InternalErrorException; +import ca.uhn.fhir.subscription.api.IResourceModifiedMessagePersistenceSvc; import com.fasterxml.jackson.core.JsonProcessingException; import org.hl7.fhir.r4.model.Bundle; import org.hl7.fhir.r4.model.IdType; @@ -33,6 +37,8 @@ import org.hl7.fhir.r4.model.Patient; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.ExtendWith; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.ValueSource; import org.mockito.Answers; import org.mockito.ArgumentCaptor; import org.mockito.ArgumentMatchers; @@ -44,7 +50,7 @@ import org.springframework.messaging.Message; import org.springframework.messaging.MessagingException; import org.springframework.messaging.support.GenericMessage; -import javax.annotation.Nonnull; +import jakarta.annotation.Nonnull; import java.net.URISyntaxException; import java.time.LocalDate; import java.util.Collection; @@ -57,6 +63,7 @@ import static org.hamcrest.Matchers.hasSize; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertFalse; import static org.junit.jupiter.api.Assertions.assertNotNull; +import static org.junit.jupiter.api.Assertions.assertThrows; import static org.junit.jupiter.api.Assertions.fail; import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.anyString; @@ -71,7 +78,8 @@ public class BaseSubscriptionDeliverySubscriberTest { private SubscriptionDeliveringRestHookSubscriber mySubscriber; private SubscriptionDeliveringMessageSubscriber myMessageSubscriber; - private final FhirContext myCtx = FhirContext.forR4(); + private SubscriptionDeliveringEmailSubscriber myEmailSubscriber; + private final FhirContext myCtx = FhirContext.forR4Cached(); @Mock private IInterceptorBroadcaster myInterceptorBroadcaster; @@ -96,6 +104,12 @@ public class BaseSubscriptionDeliverySubscriberTest { @Mock private MatchUrlService myMatchUrlService; + @Mock + private IResourceModifiedMessagePersistenceSvc myResourceModifiedMessagePersistenceSvc; + + @Mock + private IEmailSender myEmailSender; + @BeforeEach public void before() { mySubscriber = new SubscriptionDeliveringRestHookSubscriber(); @@ -109,8 +123,15 @@ public class BaseSubscriptionDeliverySubscriberTest { myMessageSubscriber.setSubscriptionRegistryForUnitTest(mySubscriptionRegistry); myMessageSubscriber.setDaoRegistryForUnitTest(myDaoRegistry); myMessageSubscriber.setMatchUrlServiceForUnitTest(myMatchUrlService); + myMessageSubscriber.setResourceModifiedMessagePersistenceSvcForUnitTest(myResourceModifiedMessagePersistenceSvc); myCtx.setRestfulClientFactory(myRestfulClientFactory); when(myRestfulClientFactory.newGenericClient(any())).thenReturn(myGenericClient); + + myEmailSubscriber = new SubscriptionDeliveringEmailSubscriber(myEmailSender); + myEmailSubscriber.setFhirContextForUnitTest(myCtx); + myEmailSubscriber.setInterceptorBroadcasterForUnitTest(myInterceptorBroadcaster); + myEmailSubscriber.setSubscriptionRegistryForUnitTest(mySubscriptionRegistry); + myEmailSubscriber.setResourceModifiedMessagePersistenceSvcForUnitTest(myResourceModifiedMessagePersistenceSvc); } @Test @@ -166,7 +187,7 @@ public class BaseSubscriptionDeliverySubscriberTest { mySubscriber.handleMessage(new ResourceDeliveryJsonMessage(payload)); fail(); } catch (MessagingException e) { - assertEquals(Msg.code(2) + "Failure handling subscription payload for subscription: Subscription/123; nested exception is ca.uhn.fhir.rest.server.exceptions.InternalErrorException: FOO", e.getMessage()); + assertEquals(Msg.code(2) + "Failure handling subscription payload for subscription: Subscription/123", e.getMessage()); } verify(myGenericClient, times(1)).update(); @@ -400,6 +421,38 @@ public class BaseSubscriptionDeliverySubscriberTest { } } + @ParameterizedTest + @ValueSource(strings = {"message", "email"}) + public void testMessageAndEmailSubscriber_whenPayloadIsNull_shouldTryInflateMessage(String theSubscriber) { + // setup + when(myInterceptorBroadcaster.callHooks(any(), any())).thenReturn(true); + + Patient patient = generatePatient(); + + CanonicalSubscription subscription = generateSubscription(); + + ResourceDeliveryMessage payload = new ResourceDeliveryMessage(); + payload.setSubscription(subscription); + payload.setPayload(myCtx, patient, EncodingEnum.JSON); + payload.setOperationType(ResourceModifiedMessage.OperationTypeEnum.CREATE); + + // mock the inflated message + when(myResourceModifiedMessagePersistenceSvc.inflatePersistedResourceModifiedMessageOrNull(any())).thenReturn(any()); + + // this will null out the payload but keep the resource id and version. + payload.setPayloadToNull(); + + // execute & verify + switch (theSubscriber) { + case "message" -> + assertThrows(MessagingException.class, () -> myMessageSubscriber.handleMessage(new ResourceDeliveryJsonMessage(payload))); + case "email" -> + assertThrows(MessagingException.class, () -> myEmailSubscriber.handleMessage(new ResourceDeliveryJsonMessage(payload))); + } + + verify(myResourceModifiedMessagePersistenceSvc, times(1)).inflatePersistedResourceModifiedMessageOrNull(any()); + } + @Nonnull private Patient generatePatient() { Patient patient = new Patient(); diff --git a/hapi-fhir-jpaserver-subscription/src/test/java/ca/uhn/fhir/jpa/subscription/match/matcher/matching/DaoSubscriptionMatcherTest.java b/hapi-fhir-jpaserver-subscription/src/test/java/ca/uhn/fhir/jpa/subscription/match/matcher/matching/DaoSubscriptionMatcherTest.java index f1933548254..817eca140e6 100644 --- a/hapi-fhir-jpaserver-subscription/src/test/java/ca/uhn/fhir/jpa/subscription/match/matcher/matching/DaoSubscriptionMatcherTest.java +++ b/hapi-fhir-jpaserver-subscription/src/test/java/ca/uhn/fhir/jpa/subscription/match/matcher/matching/DaoSubscriptionMatcherTest.java @@ -15,6 +15,7 @@ import ca.uhn.fhir.jpa.subscription.match.config.SubscriptionProcessorConfig; import ca.uhn.fhir.jpa.subscription.match.deliver.email.IEmailSender; import ca.uhn.fhir.jpa.subscription.submit.config.SubscriptionSubmitterConfig; import ca.uhn.fhir.jpa.subscription.submit.interceptor.SubscriptionQueryValidator; +import ca.uhn.fhir.subscription.api.IResourceModifiedMessagePersistenceSvc; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.ExtendWith; import org.springframework.beans.factory.annotation.Autowired; @@ -90,6 +91,11 @@ public class DaoSubscriptionMatcherTest { public IEmailSender emailSender(){ return mock(IEmailSender.class); } + + @Bean + public IResourceModifiedMessagePersistenceSvc resourceModifiedMessagePersistenceSvc() { + return mock(IResourceModifiedMessagePersistenceSvc.class); + } } } diff --git a/hapi-fhir-jpaserver-subscription/src/test/java/ca/uhn/fhir/jpa/subscription/module/SubscriptionTestConfig.java b/hapi-fhir-jpaserver-subscription/src/test/java/ca/uhn/fhir/jpa/subscription/module/SubscriptionTestConfig.java index e46ba54cf6f..0912e517285 100644 --- a/hapi-fhir-jpaserver-subscription/src/test/java/ca/uhn/fhir/jpa/subscription/module/SubscriptionTestConfig.java +++ b/hapi-fhir-jpaserver-subscription/src/test/java/ca/uhn/fhir/jpa/subscription/module/SubscriptionTestConfig.java @@ -46,7 +46,7 @@ public class SubscriptionTestConfig { private IChannelNamer myChannelNamer; @Primary - @Bean(autowire = Autowire.BY_NAME, name = "myJpaValidationSupportChain") + @Bean(name = "myJpaValidationSupportChain") public IValidationSupport validationSupportChainR4() { return myFhirContext.getValidationSupport(); } diff --git a/hapi-fhir-jpaserver-subscription/src/test/java/ca/uhn/fhir/jpa/subscription/module/cache/SubscriptionRegistrySharedTest.java b/hapi-fhir-jpaserver-subscription/src/test/java/ca/uhn/fhir/jpa/subscription/module/cache/SubscriptionRegistrySharedTest.java index b7fa0f3df6a..1dbb4cf0721 100644 --- a/hapi-fhir-jpaserver-subscription/src/test/java/ca/uhn/fhir/jpa/subscription/module/cache/SubscriptionRegistrySharedTest.java +++ b/hapi-fhir-jpaserver-subscription/src/test/java/ca/uhn/fhir/jpa/subscription/module/cache/SubscriptionRegistrySharedTest.java @@ -2,8 +2,10 @@ package ca.uhn.fhir.jpa.subscription.module.cache; import ca.uhn.fhir.jpa.subscription.channel.subscription.ISubscriptionDeliveryChannelNamer; import ca.uhn.fhir.jpa.subscription.model.CanonicalSubscription; +import ca.uhn.fhir.subscription.api.IResourceModifiedMessagePersistenceSvc; import org.hl7.fhir.dstu3.model.Subscription; import org.junit.jupiter.api.Test; +import org.springframework.beans.factory.annotation.Autowired; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.Primary; @@ -18,6 +20,9 @@ public class SubscriptionRegistrySharedTest extends BaseSubscriptionRegistryTest private static final String OTHER_ID = "OTHER_ID"; + @Autowired + private IResourceModifiedMessagePersistenceSvc myResourceModifiedMessagePersistenceSvc; + @Configuration public static class SpringConfig { diff --git a/hapi-fhir-jpaserver-subscription/src/test/java/ca/uhn/fhir/jpa/subscription/module/config/TestSubscriptionDstu3Config.java b/hapi-fhir-jpaserver-subscription/src/test/java/ca/uhn/fhir/jpa/subscription/module/config/TestSubscriptionDstu3Config.java index b53918743a8..76fd776b5e7 100644 --- a/hapi-fhir-jpaserver-subscription/src/test/java/ca/uhn/fhir/jpa/subscription/module/config/TestSubscriptionDstu3Config.java +++ b/hapi-fhir-jpaserver-subscription/src/test/java/ca/uhn/fhir/jpa/subscription/module/config/TestSubscriptionDstu3Config.java @@ -6,6 +6,7 @@ import ca.uhn.fhir.jpa.api.dao.DaoRegistry; import ca.uhn.fhir.jpa.api.dao.IFhirResourceDao; import ca.uhn.fhir.jpa.model.sched.ISchedulerService; import ca.uhn.fhir.jpa.searchparam.registry.ISearchParamProvider; +import ca.uhn.fhir.subscription.api.IResourceModifiedMessagePersistenceSvc; import com.google.common.collect.Lists; import org.hl7.fhir.dstu3.model.Subscription; import org.slf4j.Logger; @@ -62,4 +63,9 @@ public class TestSubscriptionDstu3Config { return mock; } + @Bean + public IResourceModifiedMessagePersistenceSvc resourceModifiedMessagePersistenceSvc() { + return mock(IResourceModifiedMessagePersistenceSvc.class); + } + } diff --git a/hapi-fhir-jpaserver-subscription/src/test/java/ca/uhn/fhir/jpa/subscription/module/standalone/BaseBlockingQueueSubscribableChannelDstu3Test.java b/hapi-fhir-jpaserver-subscription/src/test/java/ca/uhn/fhir/jpa/subscription/module/standalone/BaseBlockingQueueSubscribableChannelDstu3Test.java index e5fa3a5ee8e..c1d003c11c7 100644 --- a/hapi-fhir-jpaserver-subscription/src/test/java/ca/uhn/fhir/jpa/subscription/module/standalone/BaseBlockingQueueSubscribableChannelDstu3Test.java +++ b/hapi-fhir-jpaserver-subscription/src/test/java/ca/uhn/fhir/jpa/subscription/module/standalone/BaseBlockingQueueSubscribableChannelDstu3Test.java @@ -26,13 +26,14 @@ import ca.uhn.fhir.rest.api.Constants; import ca.uhn.fhir.rest.api.MethodOutcome; import ca.uhn.fhir.rest.server.IResourceProvider; import ca.uhn.fhir.rest.server.RestfulServer; +import ca.uhn.fhir.subscription.api.IResourceModifiedMessagePersistenceSvc; import ca.uhn.fhir.test.utilities.JettyUtil; import ca.uhn.test.concurrency.IPointcutLatch; import ca.uhn.test.concurrency.PointcutLatch; import com.google.common.collect.Lists; import org.eclipse.jetty.server.Server; -import org.eclipse.jetty.servlet.ServletContextHandler; -import org.eclipse.jetty.servlet.ServletHolder; +import org.eclipse.jetty.ee10.servlet.ServletContextHandler; +import org.eclipse.jetty.ee10.servlet.ServletHolder; import org.hl7.fhir.dstu3.model.CodeableConcept; import org.hl7.fhir.dstu3.model.Coding; import org.hl7.fhir.dstu3.model.IdType; @@ -50,10 +51,14 @@ import org.springframework.beans.factory.annotation.Qualifier; import org.springframework.messaging.MessageHandler; import org.springframework.messaging.SubscribableChannel; -import javax.servlet.http.HttpServletRequest; +import jakarta.servlet.http.HttpServletRequest; import java.util.ArrayList; import java.util.Collections; import java.util.List; +import java.util.Optional; + +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.Mockito.when; public abstract class BaseBlockingQueueSubscribableChannelDstu3Test extends BaseSubscriptionDstu3Test { public static final ChannelConsumerSettings CONSUMER_OPTIONS = new ChannelConsumerSettings().setConcurrentConsumers(1); @@ -100,6 +105,8 @@ public abstract class BaseBlockingQueueSubscribableChannelDstu3Test extends Base IInterceptorService myInterceptorRegistry; @Autowired private ISubscriptionDeliveryChannelNamer mySubscriptionDeliveryChannelNamer; + @Autowired + private IResourceModifiedMessagePersistenceSvc myResourceModifiedMessagePersistenceSvc; @BeforeEach public void beforeReset() { @@ -140,6 +147,8 @@ public abstract class BaseBlockingQueueSubscribableChannelDstu3Test extends Base public T sendResource(T theResource, RequestPartitionId theRequestPartitionId) throws InterruptedException { ResourceModifiedMessage msg = new ResourceModifiedMessage(myFhirContext, theResource, ResourceModifiedMessage.OperationTypeEnum.CREATE, null, theRequestPartitionId); ResourceModifiedJsonMessage message = new ResourceModifiedJsonMessage(msg); + when(myResourceModifiedMessagePersistenceSvc.inflatePersistedResourceModifiedMessageOrNull(any())).thenReturn(Optional.of(msg)); + mySubscriptionMatchingPost.setExpectedCount(1); ourSubscribableChannel.send(message); mySubscriptionMatchingPost.awaitExpected(); diff --git a/hapi-fhir-jpaserver-subscription/src/test/java/ca/uhn/fhir/jpa/subscription/module/subscriber/SubscriptionMatchingSubscriberTest.java b/hapi-fhir-jpaserver-subscription/src/test/java/ca/uhn/fhir/jpa/subscription/module/subscriber/SubscriptionMatchingSubscriberTest.java index 6f7710b042f..7730df2e400 100644 --- a/hapi-fhir-jpaserver-subscription/src/test/java/ca/uhn/fhir/jpa/subscription/module/subscriber/SubscriptionMatchingSubscriberTest.java +++ b/hapi-fhir-jpaserver-subscription/src/test/java/ca/uhn/fhir/jpa/subscription/module/subscriber/SubscriptionMatchingSubscriberTest.java @@ -17,6 +17,7 @@ import ca.uhn.fhir.jpa.subscription.module.standalone.BaseBlockingQueueSubscriba import ca.uhn.fhir.model.primitive.IdDt; import ca.uhn.fhir.rest.api.Constants; import ca.uhn.fhir.rest.server.messaging.BaseResourceModifiedMessage; +import ca.uhn.fhir.subscription.api.IResourceModifiedMessagePersistenceSvc; import ca.uhn.fhir.util.HapiExtensions; import com.google.common.collect.Lists; import org.hl7.fhir.dstu3.model.BooleanType; @@ -33,6 +34,7 @@ import org.mockito.Mockito; import java.util.Collections; import java.util.List; +import java.util.Optional; import static ca.uhn.fhir.jpa.subscription.match.matcher.subscriber.SubscriptionCriteriaParser.TypeEnum.STARTYPE_EXPRESSION; import static org.junit.jupiter.api.Assertions.assertEquals; @@ -434,6 +436,8 @@ public class SubscriptionMatchingSubscriberTest extends BaseBlockingQueueSubscri SubscriptionCriteriaParser.SubscriptionCriteria mySubscriptionCriteria; @Mock SubscriptionMatchDeliverer mySubscriptionMatchDeliverer; + @Mock + IResourceModifiedMessagePersistenceSvc myResourceModifiedMessagePersistenceSvc; @InjectMocks SubscriptionMatchingSubscriber subscriber; @@ -445,6 +449,7 @@ public class SubscriptionMatchingSubscriberTest extends BaseBlockingQueueSubscri when(myInterceptorBroadcaster.callHooks( eq(Pointcut.SUBSCRIPTION_BEFORE_PERSISTED_RESOURCE_CHECKED), any(HookParams.class))).thenReturn(true); when(mySubscriptionRegistry.getAll()).thenReturn(Collections.emptyList()); + when(myResourceModifiedMessagePersistenceSvc.inflatePersistedResourceModifiedMessageOrNull(any())).thenReturn(Optional.ofNullable(message)); subscriber.matchActiveSubscriptionsAndDeliver(message); @@ -465,6 +470,7 @@ public class SubscriptionMatchingSubscriberTest extends BaseBlockingQueueSubscri when(myActiveSubscription.getCriteria()).thenReturn(mySubscriptionCriteria); when(myActiveSubscription.getId()).thenReturn("Patient/123"); when(mySubscriptionCriteria.getType()).thenReturn(STARTYPE_EXPRESSION); + when(myResourceModifiedMessagePersistenceSvc.inflatePersistedResourceModifiedMessageOrNull(any())).thenReturn(Optional.ofNullable(message)); subscriber.matchActiveSubscriptionsAndDeliver(message); @@ -486,6 +492,7 @@ public class SubscriptionMatchingSubscriberTest extends BaseBlockingQueueSubscri when(myNonDeleteSubscription.getCriteria()).thenReturn(mySubscriptionCriteria); when(myNonDeleteSubscription.getId()).thenReturn("Patient/123"); when(mySubscriptionCriteria.getType()).thenReturn(STARTYPE_EXPRESSION); + when(myResourceModifiedMessagePersistenceSvc.inflatePersistedResourceModifiedMessageOrNull(any())).thenReturn(Optional.ofNullable(message)); subscriber.matchActiveSubscriptionsAndDeliver(message); @@ -505,6 +512,7 @@ public class SubscriptionMatchingSubscriberTest extends BaseBlockingQueueSubscri when(myActiveSubscription.getId()).thenReturn("Patient/123"); when(mySubscriptionCriteria.getType()).thenReturn(STARTYPE_EXPRESSION); when(myCanonicalSubscription.getSendDeleteMessages()).thenReturn(true); + when(myResourceModifiedMessagePersistenceSvc.inflatePersistedResourceModifiedMessageOrNull(any())).thenReturn(Optional.ofNullable(message)); subscriber.matchActiveSubscriptionsAndDeliver(message); diff --git a/hapi-fhir-jpaserver-subscription/src/test/java/ca/uhn/fhir/jpa/subscription/module/subscriber/websocket/WebsocketConnectionValidatorTest.java b/hapi-fhir-jpaserver-subscription/src/test/java/ca/uhn/fhir/jpa/subscription/module/subscriber/websocket/WebsocketConnectionValidatorTest.java index a14e8794964..4bf3e1fcf3b 100644 --- a/hapi-fhir-jpaserver-subscription/src/test/java/ca/uhn/fhir/jpa/subscription/module/subscriber/websocket/WebsocketConnectionValidatorTest.java +++ b/hapi-fhir-jpaserver-subscription/src/test/java/ca/uhn/fhir/jpa/subscription/module/subscriber/websocket/WebsocketConnectionValidatorTest.java @@ -20,6 +20,7 @@ import ca.uhn.fhir.jpa.subscription.match.registry.SubscriptionRegistry; import ca.uhn.fhir.jpa.subscription.model.CanonicalSubscription; import ca.uhn.fhir.jpa.subscription.model.CanonicalSubscriptionChannelType; import ca.uhn.fhir.rest.server.util.ISearchParamRegistry; +import ca.uhn.fhir.subscription.api.IResourceModifiedMessagePersistenceSvc; import org.hl7.fhir.r4.model.IdType; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; @@ -146,5 +147,10 @@ public class WebsocketConnectionValidatorTest { return mock(IEmailSender.class); } + @Bean + public IResourceModifiedMessagePersistenceSvc resourceModifiedMessagePersistenceSvc(){ + return mock(IResourceModifiedMessagePersistenceSvc.class); + } + } } diff --git a/hapi-fhir-jpaserver-subscription/src/test/java/ca/uhn/fhir/jpa/subscription/submit/interceptor/SubscriptionValidatingInterceptorTest.java b/hapi-fhir-jpaserver-subscription/src/test/java/ca/uhn/fhir/jpa/subscription/submit/interceptor/SubscriptionValidatingInterceptorTest.java index 02483240403..a89f16de667 100644 --- a/hapi-fhir-jpaserver-subscription/src/test/java/ca/uhn/fhir/jpa/subscription/submit/interceptor/SubscriptionValidatingInterceptorTest.java +++ b/hapi-fhir-jpaserver-subscription/src/test/java/ca/uhn/fhir/jpa/subscription/submit/interceptor/SubscriptionValidatingInterceptorTest.java @@ -28,7 +28,7 @@ import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.test.context.junit.jupiter.SpringExtension; -import javax.annotation.Nonnull; +import jakarta.annotation.Nonnull; import java.net.URI; import java.net.URISyntaxException; import java.util.Collections; diff --git a/hapi-fhir-jpaserver-subscription/src/test/java/ca/uhn/fhir/jpa/topic/SubscriptionTopicPayloadBuilderR4BTest.java b/hapi-fhir-jpaserver-subscription/src/test/java/ca/uhn/fhir/jpa/topic/SubscriptionTopicPayloadBuilderR4BTest.java index 20a6dd42e45..a37808dbfa5 100644 --- a/hapi-fhir-jpaserver-subscription/src/test/java/ca/uhn/fhir/jpa/topic/SubscriptionTopicPayloadBuilderR4BTest.java +++ b/hapi-fhir-jpaserver-subscription/src/test/java/ca/uhn/fhir/jpa/topic/SubscriptionTopicPayloadBuilderR4BTest.java @@ -8,74 +8,177 @@ import ca.uhn.fhir.util.BundleUtil; import org.hl7.fhir.r4b.model.Bundle; import org.hl7.fhir.r4b.model.Encounter; import org.hl7.fhir.r4b.model.Resource; -import org.junit.jupiter.api.Test; +import org.hl7.fhir.r4b.model.SubscriptionStatus; +import org.hl7.fhir.r5.model.Subscription; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.CsvSource; +import org.junit.jupiter.params.provider.ValueSource; import java.util.List; import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertNotNull; +import static org.junit.jupiter.api.Assertions.assertNull; +import static org.junit.jupiter.api.Assertions.assertTrue; class SubscriptionTopicPayloadBuilderR4BTest { private static final String TEST_TOPIC_URL = "test-builder-topic-url"; FhirContext ourFhirContext = FhirContext.forR4BCached(); - @Test - public void testBuildPayloadDelete() { + private SubscriptionTopicPayloadBuilder myStPayloadBuilder; + private Encounter myEncounter; + private CanonicalSubscription myCanonicalSubscription; + private ActiveSubscription myActiveSubscription; + + @BeforeEach + void before() { + myStPayloadBuilder = new SubscriptionTopicPayloadBuilder(ourFhirContext); + myEncounter = new Encounter(); + myEncounter.setId("Encounter/1"); + myCanonicalSubscription = new CanonicalSubscription(); + myCanonicalSubscription.setTopicSubscription(true); + myActiveSubscription = new ActiveSubscription(myCanonicalSubscription, "test"); + } + + @ParameterizedTest + @ValueSource(strings = { + "full-resource", + "" // payload content not provided + }) + public void testBuildPayload_deleteWithFullResourceContent_returnsCorrectPayload(String thePayloadContent) { // setup - var svc = new SubscriptionTopicPayloadBuilder(ourFhirContext); - var encounter = new Encounter(); - encounter.setId("Encounter/1"); - CanonicalSubscription sub = new CanonicalSubscription(); - ActiveSubscription subscription = new ActiveSubscription(sub, "test"); + Subscription.SubscriptionPayloadContent payloadContent = + Subscription.SubscriptionPayloadContent.fromCode(thePayloadContent); + myCanonicalSubscription.getTopicSubscription().setContent(payloadContent); // run - Bundle payload = (Bundle)svc.buildPayload(List.of(encounter), subscription, TEST_TOPIC_URL, RestOperationTypeEnum.DELETE); + Bundle payload = (Bundle) myStPayloadBuilder.buildPayload(List.of(myEncounter), myActiveSubscription, TEST_TOPIC_URL, RestOperationTypeEnum.DELETE); - // verify + // verify Bundle size + assertEquals(2, payload.getEntry().size()); List resources = BundleUtil.toListOfResourcesOfType(ourFhirContext, payload, Resource.class); assertEquals(1, resources.size()); - assertEquals("SubscriptionStatus", resources.get(0).getResourceType().name()); - assertEquals(Bundle.HTTPVerb.DELETE, payload.getEntry().get(1).getRequest().getMethod()); + // verify SubscriptionStatus.notificationEvent.focus + verifySubscriptionStatusNotificationEvent(resources.get(0)); + + // verify Encounter entry + Bundle.BundleEntryComponent encounterEntry = payload.getEntry().get(1); + assertNull(encounterEntry.getResource()); + assertNull(encounterEntry.getFullUrl()); + verifyRequestParameters(encounterEntry, org.hl7.fhir.r5.model.Bundle.HTTPVerb.DELETE.name(), "Encounter/1"); } - @Test - public void testBuildPayloadUpdate() { + @ParameterizedTest + @CsvSource({ + "create, POST , full-resource, Encounter/1, Encounter", + "update, PUT , full-resource, Encounter/1, Encounter/1", + "create, POST , , Encounter/1, Encounter", + "update, PUT , , Encounter/1, Encounter/1", + }) + public void testBuildPayload_createUpdateWithFullResourceContent_returnsCorrectPayload(String theRestOperationType, + String theHttpMethod, + String thePayloadContent, + String theFullUrl, + String theRequestUrl) { // setup - var svc = new SubscriptionTopicPayloadBuilder(ourFhirContext); - var encounter = new Encounter(); - encounter.setId("Encounter/1"); - CanonicalSubscription sub = new CanonicalSubscription(); - ActiveSubscription subscription = new ActiveSubscription(sub, "test"); + Subscription.SubscriptionPayloadContent payloadContent = + Subscription.SubscriptionPayloadContent.fromCode(thePayloadContent); + + myCanonicalSubscription.getTopicSubscription().setContent(payloadContent); + RestOperationTypeEnum restOperationType = RestOperationTypeEnum.forCode(theRestOperationType); // run - Bundle payload = (Bundle)svc.buildPayload(List.of(encounter), subscription, TEST_TOPIC_URL, RestOperationTypeEnum.UPDATE); + Bundle payload = (Bundle) myStPayloadBuilder.buildPayload(List.of(myEncounter), myActiveSubscription, TEST_TOPIC_URL, restOperationType); - // verify + // verify Bundle size + assertEquals(2, payload.getEntry().size()); List resources = BundleUtil.toListOfResourcesOfType(ourFhirContext, payload, Resource.class); assertEquals(2, resources.size()); - assertEquals("SubscriptionStatus", resources.get(0).getResourceType().name()); - assertEquals("Encounter", resources.get(1).getResourceType().name()); - assertEquals(Bundle.HTTPVerb.PUT, payload.getEntry().get(1).getRequest().getMethod()); + // verify SubscriptionStatus.notificationEvent.focus + verifySubscriptionStatusNotificationEvent(resources.get(0)); + + // verify Encounter entry + Bundle.BundleEntryComponent encounterEntry = payload.getEntry().get(1); + assertEquals("Encounter", resources.get(1).getResourceType().name()); + assertEquals(myEncounter, resources.get(1)); + assertEquals(theFullUrl, encounterEntry.getFullUrl()); + verifyRequestParameters(encounterEntry, theHttpMethod, theRequestUrl); } - @Test - public void testBuildPayloadCreate() { + @ParameterizedTest + @CsvSource({ + "create, POST , Encounter/1, Encounter", + "update, PUT , Encounter/1, Encounter/1", + "delete, DELETE, , Encounter/1" + }) + public void testBuildPayload_withIdOnlyContent_returnsCorrectPayload(String theRestOperationType, + String theHttpMethod, String theFullUrl, + String theRequestUrl) { // setup - var svc = new SubscriptionTopicPayloadBuilder(ourFhirContext); - var encounter = new Encounter(); - encounter.setId("Encounter/1"); - CanonicalSubscription sub = new CanonicalSubscription(); - ActiveSubscription subscription = new ActiveSubscription(sub, "test"); + myCanonicalSubscription.getTopicSubscription().setContent(Subscription.SubscriptionPayloadContent.IDONLY); + RestOperationTypeEnum restOperationType = RestOperationTypeEnum.forCode(theRestOperationType); // run - Bundle payload = (Bundle)svc.buildPayload(List.of(encounter), subscription, TEST_TOPIC_URL, RestOperationTypeEnum.CREATE); + Bundle payload = (Bundle) myStPayloadBuilder.buildPayload(List.of(myEncounter), myActiveSubscription, TEST_TOPIC_URL, restOperationType); - // verify + // verify Bundle size + assertEquals(2, payload.getEntry().size()); List resources = BundleUtil.toListOfResourcesOfType(ourFhirContext, payload, Resource.class); - assertEquals(2, resources.size()); - assertEquals("SubscriptionStatus", resources.get(0).getResourceType().name()); - assertEquals("Encounter", resources.get(1).getResourceType().name()); + assertEquals(1, resources.size()); - assertEquals(Bundle.HTTPVerb.POST, payload.getEntry().get(1).getRequest().getMethod()); + // verify SubscriptionStatus.notificationEvent.focus + verifySubscriptionStatusNotificationEvent(resources.get(0)); + + // verify Encounter entry + Bundle.BundleEntryComponent encounterEntry = payload.getEntry().get(1); + assertNull(encounterEntry.getResource()); + assertEquals(theFullUrl, encounterEntry.getFullUrl()); + verifyRequestParameters(encounterEntry, theHttpMethod, theRequestUrl); + } + + @ParameterizedTest + @CsvSource({ + "create", + "update", + "delete" + }) + public void testBuildPayload_withEmptyContent_returnsCorrectPayload(String theRestOperationType) { + // setup + myCanonicalSubscription.getTopicSubscription().setContent(Subscription.SubscriptionPayloadContent.EMPTY); + RestOperationTypeEnum restOperationType = RestOperationTypeEnum.forCode(theRestOperationType); + + // run + Bundle payload = (Bundle) myStPayloadBuilder.buildPayload(List.of(myEncounter), myActiveSubscription, TEST_TOPIC_URL, restOperationType); + + // verify Bundle size + assertEquals(1, payload.getEntry().size()); + List resources = BundleUtil.toListOfResourcesOfType(ourFhirContext, payload, Resource.class); + assertEquals(1, resources.size()); + + // verify SubscriptionStatus.notificationEvent.focus + assertEquals("SubscriptionStatus", resources.get(0).getResourceType().name()); + assertEquals(1, ((SubscriptionStatus) resources.get(0)).getNotificationEvent().size()); + SubscriptionStatus.SubscriptionStatusNotificationEventComponent notificationEvent = + ((SubscriptionStatus) resources.get(0)).getNotificationEventFirstRep(); + assertFalse(notificationEvent.hasFocus()); + } + + private void verifyRequestParameters(Bundle.BundleEntryComponent theEncounterEntry, + String theHttpMethod, String theRequestUrl) { + assertNotNull(theEncounterEntry.getRequest()); + assertEquals(theHttpMethod, theEncounterEntry.getRequest().getMethod().name()); + assertEquals(theRequestUrl, theEncounterEntry.getRequest().getUrl()); + } + + private void verifySubscriptionStatusNotificationEvent(Resource theResource) { + assertEquals("SubscriptionStatus", theResource.getResourceType().name()); + assertEquals(1, ((SubscriptionStatus) theResource).getNotificationEvent().size()); + SubscriptionStatus.SubscriptionStatusNotificationEventComponent notificationEvent = + ((SubscriptionStatus) theResource).getNotificationEventFirstRep(); + assertTrue(notificationEvent.hasFocus()); + assertEquals(myEncounter.getId(), notificationEvent.getFocus().getReference()); } } diff --git a/hapi-fhir-jpaserver-subscription/src/test/java/ca/uhn/fhir/jpa/topic/SubscriptionTopicPayloadBuilderR5Test.java b/hapi-fhir-jpaserver-subscription/src/test/java/ca/uhn/fhir/jpa/topic/SubscriptionTopicPayloadBuilderR5Test.java index 808ed42d8e9..d3f57de55ee 100644 --- a/hapi-fhir-jpaserver-subscription/src/test/java/ca/uhn/fhir/jpa/topic/SubscriptionTopicPayloadBuilderR5Test.java +++ b/hapi-fhir-jpaserver-subscription/src/test/java/ca/uhn/fhir/jpa/topic/SubscriptionTopicPayloadBuilderR5Test.java @@ -8,74 +8,177 @@ import ca.uhn.fhir.util.BundleUtil; import org.hl7.fhir.r5.model.Bundle; import org.hl7.fhir.r5.model.Encounter; import org.hl7.fhir.r5.model.Resource; -import org.junit.jupiter.api.Test; +import org.hl7.fhir.r5.model.Subscription; +import org.hl7.fhir.r5.model.SubscriptionStatus; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.CsvSource; +import org.junit.jupiter.params.provider.ValueSource; import java.util.List; import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertNotNull; +import static org.junit.jupiter.api.Assertions.assertNull; +import static org.junit.jupiter.api.Assertions.assertTrue; class SubscriptionTopicPayloadBuilderR5Test { - private static final String TEST_TOPIC_URL = "test-builder-topic-url"; - FhirContext ourFhirContext = FhirContext.forR5Cached(); - @Test - public void testBuildPayloadDelete() { - // setup - var svc = new SubscriptionTopicPayloadBuilder(ourFhirContext); - var encounter = new Encounter(); - encounter.setId("Encounter/1"); - CanonicalSubscription sub = new CanonicalSubscription(); - ActiveSubscription subscription = new ActiveSubscription(sub, "test"); + private static final String TEST_TOPIC_URL = "test-builder-topic-url"; + FhirContext ourFhirContext = FhirContext.forR5Cached(); + private SubscriptionTopicPayloadBuilder myStPayloadBuilder; + private Encounter myEncounter; + private CanonicalSubscription myCanonicalSubscription; + private ActiveSubscription myActiveSubscription; - // run - Bundle payload = (Bundle)svc.buildPayload(List.of(encounter), subscription, TEST_TOPIC_URL, RestOperationTypeEnum.DELETE); + @BeforeEach + void before() { + myStPayloadBuilder = new SubscriptionTopicPayloadBuilder(ourFhirContext); + myEncounter = new Encounter(); + myEncounter.setId("Encounter/1"); + myCanonicalSubscription = new CanonicalSubscription(); + myCanonicalSubscription.setTopicSubscription(true); + myActiveSubscription = new ActiveSubscription(myCanonicalSubscription, "test"); + } - // verify - List resources = BundleUtil.toListOfResourcesOfType(ourFhirContext, payload, Resource.class); - assertEquals(1, resources.size()); - assertEquals("SubscriptionStatus", resources.get(0).getResourceType().name()); + @ParameterizedTest + @ValueSource(strings = { + "full-resource", + "" // payload content not provided + }) + public void testBuildPayload_deleteWithFullResourceContent_returnsCorrectPayload(String thePayloadContent) { + // setup + Subscription.SubscriptionPayloadContent payloadContent = + Subscription.SubscriptionPayloadContent.fromCode(thePayloadContent); + myCanonicalSubscription.getTopicSubscription().setContent(payloadContent); - assertEquals(Bundle.HTTPVerb.DELETE, payload.getEntry().get(1).getRequest().getMethod()); - } + // run + Bundle payload = (Bundle) myStPayloadBuilder.buildPayload(List.of(myEncounter), myActiveSubscription, TEST_TOPIC_URL, RestOperationTypeEnum.DELETE); - @Test - public void testBuildPayloadUpdate() { - // setup - var svc = new SubscriptionTopicPayloadBuilder(ourFhirContext); - var encounter = new Encounter(); - encounter.setId("Encounter/1"); - CanonicalSubscription sub = new CanonicalSubscription(); - ActiveSubscription subscription = new ActiveSubscription(sub, "test"); + // verify Bundle size + assertEquals(2, payload.getEntry().size()); + List resources = BundleUtil.toListOfResourcesOfType(ourFhirContext, payload, Resource.class); + assertEquals(1, resources.size()); - // run - Bundle payload = (Bundle)svc.buildPayload(List.of(encounter), subscription, TEST_TOPIC_URL, RestOperationTypeEnum.UPDATE); + // verify SubscriptionStatus.notificationEvent.focus + verifySubscriptionStatusNotificationEvent(resources.get(0)); - // verify - List resources = BundleUtil.toListOfResourcesOfType(ourFhirContext, payload, Resource.class); - assertEquals(2, resources.size()); - assertEquals("SubscriptionStatus", resources.get(0).getResourceType().name()); - assertEquals("Encounter", resources.get(1).getResourceType().name()); + // verify Encounter entry + Bundle.BundleEntryComponent encounterEntry = payload.getEntry().get(1); + assertNull(encounterEntry.getResource()); + assertNull(encounterEntry.getFullUrl()); + verifyRequestParameters(encounterEntry, Bundle.HTTPVerb.DELETE.name(), "Encounter/1"); + } - assertEquals(Bundle.HTTPVerb.PUT, payload.getEntry().get(1).getRequest().getMethod()); - } + @ParameterizedTest + @CsvSource({ + "create, POST , full-resource, Encounter/1, Encounter", + "update, PUT , full-resource, Encounter/1, Encounter/1", + "create, POST , , Encounter/1, Encounter", + "update, PUT , , Encounter/1, Encounter/1", + }) + public void testBuildPayload_createUpdateWithFullResourceContent_returnsCorrectPayload(String theRestOperationType, + String theHttpMethod, + String thePayloadContent, + String theFullUrl, + String theRequestUrl) { + // setup + Subscription.SubscriptionPayloadContent payloadContent = + Subscription.SubscriptionPayloadContent.fromCode(thePayloadContent); - @Test - public void testBuildPayloadCreate() { - // setup - var svc = new SubscriptionTopicPayloadBuilder(ourFhirContext); - var encounter = new Encounter(); - encounter.setId("Encounter/1"); - CanonicalSubscription sub = new CanonicalSubscription(); - ActiveSubscription subscription = new ActiveSubscription(sub, "test"); + myCanonicalSubscription.getTopicSubscription().setContent(payloadContent); + RestOperationTypeEnum restOperationType = RestOperationTypeEnum.forCode(theRestOperationType); - // run - Bundle payload = (Bundle)svc.buildPayload(List.of(encounter), subscription, TEST_TOPIC_URL, RestOperationTypeEnum.CREATE); + // run + Bundle payload = (Bundle) myStPayloadBuilder.buildPayload(List.of(myEncounter), myActiveSubscription, TEST_TOPIC_URL, restOperationType); - // verify - List resources = BundleUtil.toListOfResourcesOfType(ourFhirContext, payload, Resource.class); - assertEquals(2, resources.size()); - assertEquals("SubscriptionStatus", resources.get(0).getResourceType().name()); - assertEquals("Encounter", resources.get(1).getResourceType().name()); + // verify Bundle size + assertEquals(2, payload.getEntry().size()); + List resources = BundleUtil.toListOfResourcesOfType(ourFhirContext, payload, Resource.class); + assertEquals(2, resources.size()); - assertEquals(Bundle.HTTPVerb.POST, payload.getEntry().get(1).getRequest().getMethod()); - } + // verify SubscriptionStatus.notificationEvent.focus + verifySubscriptionStatusNotificationEvent(resources.get(0)); + + // verify Encounter entry + Bundle.BundleEntryComponent encounterEntry = payload.getEntry().get(1); + assertEquals("Encounter", resources.get(1).getResourceType().name()); + assertEquals(myEncounter, resources.get(1)); + assertEquals(theFullUrl, encounterEntry.getFullUrl()); + verifyRequestParameters(encounterEntry, theHttpMethod, theRequestUrl); + } + + @ParameterizedTest + @CsvSource({ + "create, POST , Encounter/1, Encounter", + "update, PUT , Encounter/1, Encounter/1", + "delete, DELETE, , Encounter/1" + }) + public void testBuildPayload_withIdOnlyContent_returnsCorrectPayload(String theRestOperationType, + String theHttpMethod, String theFullUrl, + String theRequestUrl) { + // setup + myCanonicalSubscription.getTopicSubscription().setContent(Subscription.SubscriptionPayloadContent.IDONLY); + RestOperationTypeEnum restOperationType = RestOperationTypeEnum.forCode(theRestOperationType); + + // run + Bundle payload = (Bundle) myStPayloadBuilder.buildPayload(List.of(myEncounter), myActiveSubscription, TEST_TOPIC_URL, restOperationType); + + // verify Bundle size + assertEquals(2, payload.getEntry().size()); + List resources = BundleUtil.toListOfResourcesOfType(ourFhirContext, payload, Resource.class); + assertEquals(1, resources.size()); + + // verify SubscriptionStatus.notificationEvent.focus + verifySubscriptionStatusNotificationEvent(resources.get(0)); + + // verify Encounter entry + Bundle.BundleEntryComponent encounterEntry = payload.getEntry().get(1); + assertNull(encounterEntry.getResource()); + assertEquals(theFullUrl, encounterEntry.getFullUrl()); + verifyRequestParameters(encounterEntry, theHttpMethod, theRequestUrl); + } + + @ParameterizedTest + @CsvSource({ + "create", + "update", + "delete" + }) + public void testBuildPayload_withEmptyContent_returnsCorrectPayload(String theRestOperationType) { + // setup + myCanonicalSubscription.getTopicSubscription().setContent(Subscription.SubscriptionPayloadContent.EMPTY); + RestOperationTypeEnum restOperationType = RestOperationTypeEnum.forCode(theRestOperationType); + + // run + Bundle payload = (Bundle) myStPayloadBuilder.buildPayload(List.of(myEncounter), myActiveSubscription, TEST_TOPIC_URL, restOperationType); + + // verify Bundle size + assertEquals(1, payload.getEntry().size()); + List resources = BundleUtil.toListOfResourcesOfType(ourFhirContext, payload, Resource.class); + assertEquals(1, resources.size()); + + // verify SubscriptionStatus.notificationEvent.focus + assertEquals("SubscriptionStatus", resources.get(0).getResourceType().name()); + assertEquals(1, ((SubscriptionStatus) resources.get(0)).getNotificationEvent().size()); + SubscriptionStatus.SubscriptionStatusNotificationEventComponent notificationEvent = + ((SubscriptionStatus) resources.get(0)).getNotificationEventFirstRep(); + assertFalse(notificationEvent.hasFocus()); + } + + private void verifyRequestParameters(Bundle.BundleEntryComponent theEncounterEntry, + String theHttpMethod, String theRequestUrl) { + assertNotNull(theEncounterEntry.getRequest()); + assertEquals(theHttpMethod, theEncounterEntry.getRequest().getMethod().name()); + assertEquals(theRequestUrl, theEncounterEntry.getRequest().getUrl()); + } + + private void verifySubscriptionStatusNotificationEvent(Resource theResource) { + assertEquals("SubscriptionStatus", theResource.getResourceType().name()); + assertEquals(1, ((SubscriptionStatus) theResource).getNotificationEvent().size()); + SubscriptionStatus.SubscriptionStatusNotificationEventComponent notificationEvent = + ((SubscriptionStatus) theResource).getNotificationEventFirstRep(); + assertTrue(notificationEvent.hasFocus()); + assertEquals(myEncounter.getId(), notificationEvent.getFocus().getReference()); + } } diff --git a/hapi-fhir-jpaserver-subscription/src/test/java/ca/uhn/fhir/jpa/topic/SubscriptionTopicUtilTest.java b/hapi-fhir-jpaserver-subscription/src/test/java/ca/uhn/fhir/jpa/topic/SubscriptionTopicUtilTest.java index e96f3470b50..5915f8e4956 100644 --- a/hapi-fhir-jpaserver-subscription/src/test/java/ca/uhn/fhir/jpa/topic/SubscriptionTopicUtilTest.java +++ b/hapi-fhir-jpaserver-subscription/src/test/java/ca/uhn/fhir/jpa/topic/SubscriptionTopicUtilTest.java @@ -1,16 +1,36 @@ package ca.uhn.fhir.jpa.topic; +import ca.uhn.fhir.context.FhirContext; +import ca.uhn.fhir.jpa.subscription.match.registry.ActiveSubscription; +import ca.uhn.fhir.jpa.subscription.model.CanonicalSubscription; +import ca.uhn.fhir.jpa.subscription.model.CanonicalTopicSubscription; import ca.uhn.fhir.rest.server.messaging.BaseResourceMessage; +import org.hl7.fhir.instance.model.api.IBaseResource; +import org.hl7.fhir.r5.model.Bundle; import org.hl7.fhir.r5.model.Enumeration; +import org.hl7.fhir.r5.model.Patient; +import org.hl7.fhir.r5.model.Reference; +import org.hl7.fhir.r5.model.Resource; +import org.hl7.fhir.r5.model.Subscription; +import org.hl7.fhir.r5.model.SubscriptionStatus; import org.hl7.fhir.r5.model.SubscriptionTopic; import org.junit.jupiter.api.Test; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.CsvSource; import java.util.List; +import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertNull; import static org.junit.jupiter.api.Assertions.assertTrue; class SubscriptionTopicUtilTest { + + private static final String TEST_CHANNEL_NAME = "TEST_CHANNEL"; + + private final FhirContext myContext = FhirContext.forR5Cached(); + @Test public void testMatch() { // I know this is gross. I haven't found a nicer way to do this @@ -28,4 +48,78 @@ class SubscriptionTopicUtilTest { assertFalse(SubscriptionTopicUtil.matches(BaseResourceMessage.OperationTypeEnum.TRANSACTION, supportedTypes)); } + @Test + public void testExtractResourceFromBundle_withCorrectBundle_returnsCorrectResource() { + Patient patient = new Patient(); + patient.setId("Patient/1"); + Bundle bundle = buildSubscriptionStatus(patient); + + IBaseResource extractionResult = SubscriptionTopicUtil.extractResourceFromBundle(myContext, bundle); + assertEquals(patient, extractionResult); + } + + @Test + public void testExtractResourceFromBundle_withoutReferenceResource_returnsNull() { + Bundle bundle = buildSubscriptionStatus(null); + + IBaseResource extractionResult = SubscriptionTopicUtil.extractResourceFromBundle(myContext, bundle); + assertNull(extractionResult); + } + + private Bundle buildSubscriptionStatus(Resource theResource) { + SubscriptionStatus subscriptionStatus = new SubscriptionStatus(); + SubscriptionStatus.SubscriptionStatusNotificationEventComponent event = + subscriptionStatus.addNotificationEvent(); + Reference reference = new Reference(); + reference.setResource(theResource); + event.setFocus(reference); + + Bundle bundle = new Bundle(); + bundle.addEntry().setResource(subscriptionStatus); + bundle.addEntry().setResource(theResource); + return bundle; + } + + @Test + public void testExtractResourceFromBundle_withoutNotificationEvent_returnsNull() { + Bundle bundle = new Bundle(); + bundle.addEntry().setResource(new SubscriptionStatus()); + + IBaseResource extractionResult = SubscriptionTopicUtil.extractResourceFromBundle(myContext, bundle); + assertNull(extractionResult); + } + + @Test + public void testExtractResourceFromBundle_withEmptyBundle_returnsNull() { + IBaseResource extractionResult = SubscriptionTopicUtil.extractResourceFromBundle(myContext, new Bundle()); + assertNull(extractionResult); + } + + @Test + public void testIsEmptyContentTopicSubscription_withEmptySubscription_returnsFalse() { + CanonicalSubscription canonicalSubscription = new CanonicalSubscription(); + boolean result = SubscriptionTopicUtil.isEmptyContentTopicSubscription(canonicalSubscription); + + assertFalse(result); + } + + @ParameterizedTest + @CsvSource({ + "full-resource, false", + "id-only , false", + "empty , true", + " , false", + }) + public void testIsEmptyContentTopicSubscription_withContentPayload_returnsExpectedResult(String thePayloadContent, + boolean theExpectedResult) { + CanonicalTopicSubscription canonicalTopicSubscription = new CanonicalTopicSubscription(); + canonicalTopicSubscription.setContent(Subscription.SubscriptionPayloadContent.fromCode(thePayloadContent)); + CanonicalSubscription canonicalSubscription = new CanonicalSubscription(); + canonicalSubscription.setTopicSubscription(canonicalTopicSubscription); + canonicalSubscription.setTopicSubscription(true); + + boolean actualResult = SubscriptionTopicUtil.isEmptyContentTopicSubscription(canonicalSubscription); + + assertEquals(theExpectedResult, actualResult); + } } diff --git a/hapi-fhir-jpaserver-test-dstu2/pom.xml b/hapi-fhir-jpaserver-test-dstu2/pom.xml index 12677842d4b..d168f8148a2 100644 --- a/hapi-fhir-jpaserver-test-dstu2/pom.xml +++ b/hapi-fhir-jpaserver-test-dstu2/pom.xml @@ -6,7 +6,7 @@ ca.uhn.hapi.fhir hapi-deployable-pom - 6.9.7-SNAPSHOT + 6.11.7-SNAPSHOT ../hapi-deployable-pom/pom.xml diff --git a/hapi-fhir-jpaserver-test-dstu2/src/test/java/ca/uhn/fhir/jpa/dao/dstu2/BaseJpaDstu2SystemTest.java b/hapi-fhir-jpaserver-test-dstu2/src/test/java/ca/uhn/fhir/jpa/dao/dstu2/BaseJpaDstu2SystemTest.java index ae3d1ff799a..599a774b85f 100644 --- a/hapi-fhir-jpaserver-test-dstu2/src/test/java/ca/uhn/fhir/jpa/dao/dstu2/BaseJpaDstu2SystemTest.java +++ b/hapi-fhir-jpaserver-test-dstu2/src/test/java/ca/uhn/fhir/jpa/dao/dstu2/BaseJpaDstu2SystemTest.java @@ -6,9 +6,9 @@ import ca.uhn.fhir.rest.server.RestfulServer; import ca.uhn.fhir.rest.server.servlet.ServletRequestDetails; import org.junit.jupiter.api.BeforeEach; -import javax.servlet.ServletConfig; -import javax.servlet.ServletException; -import javax.servlet.http.HttpServletRequest; +import jakarta.servlet.ServletConfig; +import jakarta.servlet.ServletException; +import jakarta.servlet.http.HttpServletRequest; import java.util.Enumeration; import static org.mockito.Mockito.mock; diff --git a/hapi-fhir-jpaserver-test-dstu2/src/test/java/ca/uhn/fhir/jpa/dao/dstu2/BaseJpaDstu2Test.java b/hapi-fhir-jpaserver-test-dstu2/src/test/java/ca/uhn/fhir/jpa/dao/dstu2/BaseJpaDstu2Test.java index 15aa0f38c91..108a573859f 100644 --- a/hapi-fhir-jpaserver-test-dstu2/src/test/java/ca/uhn/fhir/jpa/dao/dstu2/BaseJpaDstu2Test.java +++ b/hapi-fhir-jpaserver-test-dstu2/src/test/java/ca/uhn/fhir/jpa/dao/dstu2/BaseJpaDstu2Test.java @@ -72,7 +72,7 @@ import org.springframework.transaction.TransactionDefinition; import org.springframework.transaction.annotation.Transactional; import org.springframework.transaction.support.TransactionTemplate; -import javax.persistence.EntityManager; +import jakarta.persistence.EntityManager; import java.util.ArrayList; import java.util.List; diff --git a/hapi-fhir-jpaserver-test-dstu2/src/test/java/ca/uhn/fhir/jpa/dao/dstu2/FhirResourceDaoDstu2SearchFtTest.java b/hapi-fhir-jpaserver-test-dstu2/src/test/java/ca/uhn/fhir/jpa/dao/dstu2/FhirResourceDaoDstu2SearchFtTest.java index 57c472b280d..e5c828100a2 100644 --- a/hapi-fhir-jpaserver-test-dstu2/src/test/java/ca/uhn/fhir/jpa/dao/dstu2/FhirResourceDaoDstu2SearchFtTest.java +++ b/hapi-fhir-jpaserver-test-dstu2/src/test/java/ca/uhn/fhir/jpa/dao/dstu2/FhirResourceDaoDstu2SearchFtTest.java @@ -17,7 +17,7 @@ import org.junit.jupiter.api.Test; import org.springframework.transaction.TransactionStatus; import org.springframework.transaction.support.TransactionCallbackWithoutResult; -import javax.servlet.http.HttpServletRequest; +import jakarta.servlet.http.HttpServletRequest; import java.util.List; import static org.hamcrest.MatcherAssert.assertThat; diff --git a/hapi-fhir-jpaserver-test-dstu2/src/test/java/ca/uhn/fhir/jpa/dao/dstu2/FhirResourceDaoDstu2SearchNoFtTest.java b/hapi-fhir-jpaserver-test-dstu2/src/test/java/ca/uhn/fhir/jpa/dao/dstu2/FhirResourceDaoDstu2SearchNoFtTest.java index 12bfe63c89d..13537d4a6a0 100644 --- a/hapi-fhir-jpaserver-test-dstu2/src/test/java/ca/uhn/fhir/jpa/dao/dstu2/FhirResourceDaoDstu2SearchNoFtTest.java +++ b/hapi-fhir-jpaserver-test-dstu2/src/test/java/ca/uhn/fhir/jpa/dao/dstu2/FhirResourceDaoDstu2SearchNoFtTest.java @@ -82,7 +82,7 @@ import org.junit.jupiter.api.Test; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.test.context.ContextConfiguration; -import javax.servlet.http.HttpServletRequest; +import jakarta.servlet.http.HttpServletRequest; import java.math.BigDecimal; import java.util.ArrayList; import java.util.Date; diff --git a/hapi-fhir-jpaserver-test-dstu2/src/test/java/ca/uhn/fhir/jpa/dao/dstu2/FhirResourceDaoDstu2Test.java b/hapi-fhir-jpaserver-test-dstu2/src/test/java/ca/uhn/fhir/jpa/dao/dstu2/FhirResourceDaoDstu2Test.java index 7037f682ec5..6fbd1c642c9 100644 --- a/hapi-fhir-jpaserver-test-dstu2/src/test/java/ca/uhn/fhir/jpa/dao/dstu2/FhirResourceDaoDstu2Test.java +++ b/hapi-fhir-jpaserver-test-dstu2/src/test/java/ca/uhn/fhir/jpa/dao/dstu2/FhirResourceDaoDstu2Test.java @@ -2189,21 +2189,21 @@ public class FhirResourceDaoDstu2Test extends BaseJpaDstu2Test { pm.setSort(new SortSpec(BaseResource.SP_RES_ID)); actual = toUnqualifiedVersionlessIds(myPatientDao.search(pm)); assertEquals(5, actual.size()); - assertThat(actual, contains(idMethodName, id1, id2, id3, id4)); + assertThat(actual, contains(id1, id2, id3, id4, idMethodName)); pm = new SearchParameterMap(); pm.add(Patient.SP_IDENTIFIER, new TokenParam("urn:system", methodName)); pm.setSort(new SortSpec(BaseResource.SP_RES_ID).setOrder(SortOrderEnum.ASC)); actual = toUnqualifiedVersionlessIds(myPatientDao.search(pm)); assertEquals(5, actual.size()); - assertThat(actual, contains(idMethodName, id1, id2, id3, id4)); + assertThat(actual, contains(id1, id2, id3, id4, idMethodName)); pm = new SearchParameterMap(); pm.add(Patient.SP_IDENTIFIER, new TokenParam("urn:system", methodName)); pm.setSort(new SortSpec(BaseResource.SP_RES_ID).setOrder(SortOrderEnum.DESC)); actual = toUnqualifiedVersionlessIds(myPatientDao.search(pm)); assertEquals(5, actual.size()); - assertThat(actual, contains(id4, id3, id2, id1, idMethodName)); + assertThat(actual, contains(idMethodName, id4, id3, id2, id1)); } @Test diff --git a/hapi-fhir-jpaserver-test-dstu2/src/test/java/ca/uhn/fhir/jpa/provider/ResourceProviderDstu2ValueSetTest.java b/hapi-fhir-jpaserver-test-dstu2/src/test/java/ca/uhn/fhir/jpa/provider/ResourceProviderDstu2ValueSetTest.java index 26b16537848..0786572596e 100644 --- a/hapi-fhir-jpaserver-test-dstu2/src/test/java/ca/uhn/fhir/jpa/provider/ResourceProviderDstu2ValueSetTest.java +++ b/hapi-fhir-jpaserver-test-dstu2/src/test/java/ca/uhn/fhir/jpa/provider/ResourceProviderDstu2ValueSetTest.java @@ -16,7 +16,7 @@ import org.junit.jupiter.api.Disabled; import org.junit.jupiter.api.Test; import org.springframework.transaction.annotation.Transactional; -import javax.annotation.Nonnull; +import jakarta.annotation.Nonnull; import java.io.IOException; import java.util.List; import java.util.stream.Collectors; diff --git a/hapi-fhir-jpaserver-test-dstu2/src/test/java/ca/uhn/fhir/jpa/provider/SubscriptionsDstu2Test.java b/hapi-fhir-jpaserver-test-dstu2/src/test/java/ca/uhn/fhir/jpa/provider/SubscriptionsDstu2Test.java index a428d1fb818..3bbca30528c 100644 --- a/hapi-fhir-jpaserver-test-dstu2/src/test/java/ca/uhn/fhir/jpa/provider/SubscriptionsDstu2Test.java +++ b/hapi-fhir-jpaserver-test-dstu2/src/test/java/ca/uhn/fhir/jpa/provider/SubscriptionsDstu2Test.java @@ -8,8 +8,8 @@ import ca.uhn.fhir.model.dstu2.valueset.SubscriptionStatusEnum; import ca.uhn.fhir.rest.api.EncodingEnum; import ca.uhn.fhir.rest.server.exceptions.UnprocessableEntityException; import org.eclipse.jetty.websocket.api.Session; -import org.eclipse.jetty.websocket.api.annotations.OnWebSocketConnect; import org.eclipse.jetty.websocket.api.annotations.OnWebSocketMessage; +import org.eclipse.jetty.websocket.api.annotations.OnWebSocketOpen; import org.eclipse.jetty.websocket.api.annotations.WebSocket; import org.hl7.fhir.instance.model.api.IBaseResource; import org.hl7.fhir.instance.model.api.IIdType; @@ -190,7 +190,7 @@ public class SubscriptionsDstu2Test extends BaseResourceProviderDstu2Test { /** * Basic Echo Client Socket */ - @WebSocket(maxTextMessageSize = 64 * 1024) + @WebSocket() public class SimpleEchoSocket extends BaseSocket { @SuppressWarnings("unused") @@ -200,14 +200,14 @@ public class SubscriptionsDstu2Test extends BaseResourceProviderDstu2Test { mySubsId = theSubsId; } - @OnWebSocketConnect + @OnWebSocketOpen public void onConnect(Session session) { ourLog.info("Got connect: {}", session); this.session = session; try { String sending = "bind " + mySubsId; ourLog.info("Sending: {}", sending); - session.getRemote().sendString(sending); + session.sendText(sending, null); } catch (Throwable t) { ourLog.error("Failure", t); } @@ -229,7 +229,7 @@ public class SubscriptionsDstu2Test extends BaseResourceProviderDstu2Test { /** * Basic Echo Client Socket */ - @WebSocket(maxTextMessageSize = 64 * 1024) + @WebSocket() public class DynamicEchoSocket extends BaseSocket { private List myReceived = new ArrayList(); @@ -243,14 +243,14 @@ public class SubscriptionsDstu2Test extends BaseResourceProviderDstu2Test { myEncoding = theEncoding; } - @OnWebSocketConnect + @OnWebSocketOpen public void onConnect(Session session) { ourLog.info("Got connect: {}", session); this.session = session; try { String sending = "bind " + myCriteria; ourLog.info("Sending: {}", sending); - session.getRemote().sendString(sending); + session.sendText(sending, null); } catch (Throwable t) { ourLog.error("Failure", t); } diff --git a/hapi-fhir-jpaserver-test-dstu2/src/test/java/ca/uhn/fhir/jpa/provider/SystemProviderDstu2Test.java b/hapi-fhir-jpaserver-test-dstu2/src/test/java/ca/uhn/fhir/jpa/provider/SystemProviderDstu2Test.java index 615309cca5a..1771c4de9d3 100644 --- a/hapi-fhir-jpaserver-test-dstu2/src/test/java/ca/uhn/fhir/jpa/provider/SystemProviderDstu2Test.java +++ b/hapi-fhir-jpaserver-test-dstu2/src/test/java/ca/uhn/fhir/jpa/provider/SystemProviderDstu2Test.java @@ -37,8 +37,8 @@ import org.apache.http.impl.client.CloseableHttpClient; import org.apache.http.impl.client.HttpClientBuilder; import org.apache.http.impl.conn.PoolingHttpClientConnectionManager; import org.eclipse.jetty.server.Server; -import org.eclipse.jetty.servlet.ServletContextHandler; -import org.eclipse.jetty.servlet.ServletHolder; +import org.eclipse.jetty.ee10.servlet.ServletContextHandler; +import org.eclipse.jetty.ee10.servlet.ServletHolder; import org.hl7.fhir.r4.model.IdType; import org.junit.jupiter.api.AfterAll; import org.junit.jupiter.api.BeforeEach; diff --git a/hapi-fhir-jpaserver-test-dstu2/src/test/java/ca/uhn/fhir/jpa/provider/SystemProviderTransactionSearchDstu2Test.java b/hapi-fhir-jpaserver-test-dstu2/src/test/java/ca/uhn/fhir/jpa/provider/SystemProviderTransactionSearchDstu2Test.java index 1a3509cd3f4..6e53d143310 100644 --- a/hapi-fhir-jpaserver-test-dstu2/src/test/java/ca/uhn/fhir/jpa/provider/SystemProviderTransactionSearchDstu2Test.java +++ b/hapi-fhir-jpaserver-test-dstu2/src/test/java/ca/uhn/fhir/jpa/provider/SystemProviderTransactionSearchDstu2Test.java @@ -18,8 +18,8 @@ import ca.uhn.fhir.rest.client.interceptor.SimpleRequestHeaderInterceptor; import ca.uhn.fhir.rest.server.RestfulServer; import ca.uhn.fhir.test.utilities.JettyUtil; import org.eclipse.jetty.server.Server; -import org.eclipse.jetty.servlet.ServletContextHandler; -import org.eclipse.jetty.servlet.ServletHolder; +import org.eclipse.jetty.ee10.servlet.ServletContextHandler; +import org.eclipse.jetty.ee10.servlet.ServletHolder; import org.junit.jupiter.api.AfterAll; import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.BeforeEach; diff --git a/hapi-fhir-jpaserver-test-dstu2/src/test/java/ca/uhn/fhir/jpa/search/SearchCoordinatorSvcImplTest.java b/hapi-fhir-jpaserver-test-dstu2/src/test/java/ca/uhn/fhir/jpa/search/SearchCoordinatorSvcImplTest.java index a3e94a48969..4ec3d81ca2d 100644 --- a/hapi-fhir-jpaserver-test-dstu2/src/test/java/ca/uhn/fhir/jpa/search/SearchCoordinatorSvcImplTest.java +++ b/hapi-fhir-jpaserver-test-dstu2/src/test/java/ca/uhn/fhir/jpa/search/SearchCoordinatorSvcImplTest.java @@ -43,7 +43,7 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.data.domain.Pageable; -import javax.annotation.Nonnull; +import jakarta.annotation.Nonnull; import java.util.ArrayList; import java.util.Collection; import java.util.Iterator; @@ -293,12 +293,11 @@ public class SearchCoordinatorSvcImplTest extends BaseSearchSvc { } private void initAsyncSearches() { - when(myPersistedJpaBundleProviderFactory.newInstanceFirstPage(nullable(RequestDetails.class), nullable(Search.class), nullable(SearchTask.class), nullable(ISearchBuilder.class), nullable(RequestPartitionId.class))).thenAnswer(t -> { + when(myPersistedJpaBundleProviderFactory.newInstanceFirstPage(nullable(RequestDetails.class), nullable(SearchTask.class), nullable(ISearchBuilder.class), nullable(RequestPartitionId.class))).thenAnswer(t -> { RequestDetails requestDetails = t.getArgument(0, RequestDetails.class); - Search search = t.getArgument(1, Search.class); - SearchTask searchTask = t.getArgument(2, SearchTask.class); - ISearchBuilder searchBuilder = t.getArgument(3, ISearchBuilder.class); - PersistedJpaSearchFirstPageBundleProvider retVal = new PersistedJpaSearchFirstPageBundleProvider(search, searchTask, searchBuilder, requestDetails, null); + SearchTask searchTask = t.getArgument(1, SearchTask.class); + ISearchBuilder searchBuilder = t.getArgument(2, ISearchBuilder.class); + PersistedJpaSearchFirstPageBundleProvider retVal = new PersistedJpaSearchFirstPageBundleProvider(searchTask, searchBuilder, requestDetails, null); retVal.setStorageSettingsForUnitTest(new JpaStorageSettings()); retVal.setTxServiceForUnitTest(myTransactionService); retVal.setSearchCoordinatorSvcForUnitTest(mySvc); diff --git a/hapi-fhir-jpaserver-test-dstu2/src/test/java/ca/uhn/fhir/jpa/search/SynchronousSearchSvcImplTest.java b/hapi-fhir-jpaserver-test-dstu2/src/test/java/ca/uhn/fhir/jpa/search/SynchronousSearchSvcImplTest.java index 5c9309b4369..456f42ee90a 100644 --- a/hapi-fhir-jpaserver-test-dstu2/src/test/java/ca/uhn/fhir/jpa/search/SynchronousSearchSvcImplTest.java +++ b/hapi-fhir-jpaserver-test-dstu2/src/test/java/ca/uhn/fhir/jpa/search/SynchronousSearchSvcImplTest.java @@ -42,14 +42,17 @@ public class SynchronousSearchSvcImplTest extends BaseSearchSvc { @Test public void testSynchronousSearch() { - when(mySearchBuilderFactory.newSearchBuilder(any(), any(), any())).thenReturn(mySearchBuilder); + when(mySearchBuilderFactory.newSearchBuilder(any(), any(), any())) + .thenReturn(mySearchBuilder); SearchParameterMap params = new SearchParameterMap(); List pids = createPidSequence(800); - when(mySearchBuilder.createQuery(same(params), any(), any(), nullable(RequestPartitionId.class))).thenReturn(new BaseSearchSvc.ResultIterator(pids.iterator())); + when(mySearchBuilder.createQuery(any(SearchParameterMap.class), any(), any(), nullable(RequestPartitionId.class))) + .thenReturn(new BaseSearchSvc.ResultIterator(pids.iterator())); - doAnswer(loadPids()).when(mySearchBuilder).loadResourcesByPid(any(Collection.class), any(Collection.class), any(List.class), anyBoolean(), any()); + doAnswer(loadPids()).when(mySearchBuilder) + .loadResourcesByPid(any(Collection.class), any(Collection.class), any(List.class), anyBoolean(), any()); IBundleProvider result = mySynchronousSearchSvc.executeQuery( "Patient", params, RequestPartitionId.allPartitions()); assertNull(result.getUuid()); @@ -71,8 +74,8 @@ public class SynchronousSearchSvcImplTest extends BaseSearchSvc { params.setSearchTotalMode(SearchTotalModeEnum.ACCURATE); List pids = createPidSequence(30); - when(mySearchBuilder.createCountQuery(same(params), any(String.class),nullable(RequestDetails.class), nullable(RequestPartitionId.class))).thenReturn(20L); - when(mySearchBuilder.createQuery(same(params), any(), nullable(RequestDetails.class), nullable(RequestPartitionId.class))).thenReturn(new BaseSearchSvc.ResultIterator(pids.subList(10, 20).iterator())); + when(mySearchBuilder.createCountQuery(any(SearchParameterMap.class), any(String.class),nullable(RequestDetails.class), nullable(RequestPartitionId.class))).thenReturn(20L); + when(mySearchBuilder.createQuery(any(SearchParameterMap.class), any(), nullable(RequestDetails.class), nullable(RequestPartitionId.class))).thenReturn(new BaseSearchSvc.ResultIterator(pids.subList(10, 20).iterator())); doAnswer(loadPids()).when(mySearchBuilder).loadResourcesByPid(any(Collection.class), any(Collection.class), any(List.class), anyBoolean(), any()); @@ -92,7 +95,8 @@ public class SynchronousSearchSvcImplTest extends BaseSearchSvc { params.setLoadSynchronousUpTo(100); List pids = createPidSequence(800); - when(mySearchBuilder.createQuery(same(params), any(), nullable(RequestDetails.class), nullable(RequestPartitionId.class))).thenReturn(new BaseSearchSvc.ResultIterator(pids.iterator())); + when(mySearchBuilder.createQuery(any(SearchParameterMap.class), any(), nullable(RequestDetails.class), nullable(RequestPartitionId.class))) + .thenReturn(new BaseSearchSvc.ResultIterator(pids.iterator())); pids = createPidSequence(110); List finalPids = pids; diff --git a/hapi-fhir-jpaserver-test-dstu2/src/test/java/ca/uhn/fhir/jpa/subscription/resthook/RestHookTestDstu2Test.java b/hapi-fhir-jpaserver-test-dstu2/src/test/java/ca/uhn/fhir/jpa/subscription/resthook/RestHookTestDstu2Test.java index 429a6cc69db..a6d637f0f38 100644 --- a/hapi-fhir-jpaserver-test-dstu2/src/test/java/ca/uhn/fhir/jpa/subscription/resthook/RestHookTestDstu2Test.java +++ b/hapi-fhir-jpaserver-test-dstu2/src/test/java/ca/uhn/fhir/jpa/subscription/resthook/RestHookTestDstu2Test.java @@ -24,8 +24,8 @@ import ca.uhn.fhir.rest.server.exceptions.UnprocessableEntityException; import ca.uhn.fhir.test.utilities.JettyUtil; import com.google.common.collect.Lists; import org.eclipse.jetty.server.Server; -import org.eclipse.jetty.servlet.ServletContextHandler; -import org.eclipse.jetty.servlet.ServletHolder; +import org.eclipse.jetty.ee10.servlet.ServletContextHandler; +import org.eclipse.jetty.ee10.servlet.ServletHolder; import org.hl7.fhir.instance.model.api.IBaseResource; import org.hl7.fhir.instance.model.api.IIdType; import org.junit.jupiter.api.AfterAll; @@ -35,7 +35,7 @@ import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.springframework.beans.factory.annotation.Autowired; -import javax.annotation.Nonnull; +import jakarta.annotation.Nonnull; import java.util.ArrayList; import java.util.List; import java.util.stream.Collectors; diff --git a/hapi-fhir-jpaserver-test-dstu2/src/test/java/ca/uhn/fhir/jpa/subscription/resthook/RestHookTestWithInterceptorRegisteredToStorageSettingsDstu2Test.java b/hapi-fhir-jpaserver-test-dstu2/src/test/java/ca/uhn/fhir/jpa/subscription/resthook/RestHookTestWithInterceptorRegisteredToStorageSettingsDstu2Test.java index 6f993478058..454743f20d2 100644 --- a/hapi-fhir-jpaserver-test-dstu2/src/test/java/ca/uhn/fhir/jpa/subscription/resthook/RestHookTestWithInterceptorRegisteredToStorageSettingsDstu2Test.java +++ b/hapi-fhir-jpaserver-test-dstu2/src/test/java/ca/uhn/fhir/jpa/subscription/resthook/RestHookTestWithInterceptorRegisteredToStorageSettingsDstu2Test.java @@ -23,8 +23,8 @@ import ca.uhn.fhir.rest.server.RestfulServer; import ca.uhn.fhir.test.utilities.JettyUtil; import com.google.common.collect.Lists; import org.eclipse.jetty.server.Server; -import org.eclipse.jetty.servlet.ServletContextHandler; -import org.eclipse.jetty.servlet.ServletHolder; +import org.eclipse.jetty.ee10.servlet.ServletContextHandler; +import org.eclipse.jetty.ee10.servlet.ServletHolder; import org.hl7.fhir.instance.model.api.IBaseResource; import org.junit.jupiter.api.AfterAll; import org.junit.jupiter.api.AfterEach; diff --git a/hapi-fhir-jpaserver-test-dstu3/pom.xml b/hapi-fhir-jpaserver-test-dstu3/pom.xml index 663c4258d5b..b23b2ed48f0 100644 --- a/hapi-fhir-jpaserver-test-dstu3/pom.xml +++ b/hapi-fhir-jpaserver-test-dstu3/pom.xml @@ -6,7 +6,7 @@ ca.uhn.hapi.fhir hapi-deployable-pom - 6.9.7-SNAPSHOT + 6.11.7-SNAPSHOT ../hapi-deployable-pom/pom.xml diff --git a/hapi-fhir-jpaserver-test-dstu3/src/test/java/ca/uhn/fhir/jpa/dao/dstu3/BaseJpaDstu3SystemTest.java b/hapi-fhir-jpaserver-test-dstu3/src/test/java/ca/uhn/fhir/jpa/dao/dstu3/BaseJpaDstu3SystemTest.java index f9835681abc..65f79eba0d0 100644 --- a/hapi-fhir-jpaserver-test-dstu3/src/test/java/ca/uhn/fhir/jpa/dao/dstu3/BaseJpaDstu3SystemTest.java +++ b/hapi-fhir-jpaserver-test-dstu3/src/test/java/ca/uhn/fhir/jpa/dao/dstu3/BaseJpaDstu3SystemTest.java @@ -7,9 +7,9 @@ import ca.uhn.fhir.rest.server.RestfulServer; import ca.uhn.fhir.rest.server.servlet.ServletRequestDetails; import org.junit.jupiter.api.BeforeEach; -import javax.servlet.ServletConfig; -import javax.servlet.ServletException; -import javax.servlet.http.HttpServletRequest; +import jakarta.servlet.ServletConfig; +import jakarta.servlet.ServletException; +import jakarta.servlet.http.HttpServletRequest; import java.util.Enumeration; import static org.mockito.Mockito.mock; diff --git a/hapi-fhir-jpaserver-test-dstu3/src/test/java/ca/uhn/fhir/jpa/dao/dstu3/FhirResourceDaoDstu3SearchFtTest.java b/hapi-fhir-jpaserver-test-dstu3/src/test/java/ca/uhn/fhir/jpa/dao/dstu3/FhirResourceDaoDstu3SearchFtTest.java index a20b849cacf..04c59ac49b9 100644 --- a/hapi-fhir-jpaserver-test-dstu3/src/test/java/ca/uhn/fhir/jpa/dao/dstu3/FhirResourceDaoDstu3SearchFtTest.java +++ b/hapi-fhir-jpaserver-test-dstu3/src/test/java/ca/uhn/fhir/jpa/dao/dstu3/FhirResourceDaoDstu3SearchFtTest.java @@ -22,7 +22,7 @@ import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Disabled; import org.junit.jupiter.api.Test; -import javax.servlet.http.HttpServletRequest; +import jakarta.servlet.http.HttpServletRequest; import java.util.Arrays; import java.util.List; diff --git a/hapi-fhir-jpaserver-test-dstu3/src/test/java/ca/uhn/fhir/jpa/dao/dstu3/FhirResourceDaoDstu3SearchNoFtTest.java b/hapi-fhir-jpaserver-test-dstu3/src/test/java/ca/uhn/fhir/jpa/dao/dstu3/FhirResourceDaoDstu3SearchNoFtTest.java index 7c2d95ac116..83123192373 100644 --- a/hapi-fhir-jpaserver-test-dstu3/src/test/java/ca/uhn/fhir/jpa/dao/dstu3/FhirResourceDaoDstu3SearchNoFtTest.java +++ b/hapi-fhir-jpaserver-test-dstu3/src/test/java/ca/uhn/fhir/jpa/dao/dstu3/FhirResourceDaoDstu3SearchNoFtTest.java @@ -106,7 +106,7 @@ import org.springframework.transaction.TransactionStatus; import org.springframework.transaction.support.TransactionCallback; import org.springframework.transaction.support.TransactionTemplate; -import javax.servlet.http.HttpServletRequest; +import jakarta.servlet.http.HttpServletRequest; import java.io.IOException; import java.math.BigDecimal; import java.nio.charset.StandardCharsets; @@ -3323,7 +3323,7 @@ public class FhirResourceDaoDstu3SearchNoFtTest extends BaseJpaDstu3Test { map = new SearchParameterMap(); map.setSort(new SortSpec("_id", SortOrderEnum.ASC)); ids = toUnqualifiedVersionlessIdValues(myPatientDao.search(map)); - assertThat(ids, contains("Patient/AA", "Patient/AB", id1, id2)); + assertThat(ids, contains(id1, id2, "Patient/AA", "Patient/AB")); } diff --git a/hapi-fhir-jpaserver-test-dstu3/src/test/java/ca/uhn/fhir/jpa/dao/dstu3/FhirResourceDaoDstu3Test.java b/hapi-fhir-jpaserver-test-dstu3/src/test/java/ca/uhn/fhir/jpa/dao/dstu3/FhirResourceDaoDstu3Test.java index f62de15e9cf..7b2b699d85f 100644 --- a/hapi-fhir-jpaserver-test-dstu3/src/test/java/ca/uhn/fhir/jpa/dao/dstu3/FhirResourceDaoDstu3Test.java +++ b/hapi-fhir-jpaserver-test-dstu3/src/test/java/ca/uhn/fhir/jpa/dao/dstu3/FhirResourceDaoDstu3Test.java @@ -44,7 +44,6 @@ import ca.uhn.fhir.rest.server.exceptions.ResourceVersionConflictException; import ca.uhn.fhir.rest.server.exceptions.UnprocessableEntityException; import ca.uhn.fhir.util.ClasspathUtil; import com.google.common.collect.Lists; -import org.apache.commons.io.IOUtils; import org.apache.commons.lang3.RandomStringUtils; import org.hamcrest.Matchers; import org.hamcrest.core.StringContains; @@ -104,7 +103,6 @@ import org.springframework.transaction.TransactionStatus; import org.springframework.transaction.support.TransactionCallbackWithoutResult; import org.springframework.transaction.support.TransactionTemplate; -import java.nio.charset.StandardCharsets; import java.util.ArrayList; import java.util.Collections; import java.util.Comparator; @@ -632,7 +630,7 @@ public class FhirResourceDaoDstu3Test extends BaseJpaDstu3Test { if (readBackResource.getForcedId() != null) { assertEquals(myExpectedId, readBackResource.getForcedId().getForcedId(), "legacy join populated"); - assertEquals(myExpectedId, readBackView.getForcedId(), + assertEquals(myExpectedId, readBackView.getFhirId(), "legacy join populated"); } else { assertEquals(IdStrategyEnum.SEQUENTIAL_NUMERIC, theServerIdStrategy, @@ -2790,21 +2788,21 @@ public class FhirResourceDaoDstu3Test extends BaseJpaDstu3Test { pm.setSort(new SortSpec(IAnyResource.SP_RES_ID)); actual = toUnqualifiedVersionlessIds(myPatientDao.search(pm)); assertEquals(5, actual.size()); - assertThat(actual.toString(), actual, contains(idMethodName, id1, id2, id3, id4)); + assertThat(actual.toString(), actual, contains(id1, id2, id3, id4, idMethodName)); pm = new SearchParameterMap(); pm.add(Patient.SP_IDENTIFIER, new TokenParam("urn:system", methodName)); pm.setSort(new SortSpec(IAnyResource.SP_RES_ID).setOrder(SortOrderEnum.ASC)); actual = toUnqualifiedVersionlessIds(myPatientDao.search(pm)); assertEquals(5, actual.size()); - assertThat(actual.toString(), actual, contains(idMethodName, id1, id2, id3, id4)); + assertThat(actual.toString(), actual, contains(id1, id2, id3, id4, idMethodName)); pm = new SearchParameterMap(); pm.add(Patient.SP_IDENTIFIER, new TokenParam("urn:system", methodName)); pm.setSort(new SortSpec(IAnyResource.SP_RES_ID).setOrder(SortOrderEnum.DESC)); actual = toUnqualifiedVersionlessIds(myPatientDao.search(pm)); assertEquals(5, actual.size()); - assertThat(actual.toString(), actual, contains(id4, id3, id2, id1, idMethodName)); + assertThat(actual.toString(), actual, contains(idMethodName, id4, id3, id2, id1)); } @Test diff --git a/hapi-fhir-jpaserver-test-dstu3/src/test/java/ca/uhn/fhir/jpa/dao/dstu3/FhirSystemDaoDstu3Test.java b/hapi-fhir-jpaserver-test-dstu3/src/test/java/ca/uhn/fhir/jpa/dao/dstu3/FhirSystemDaoDstu3Test.java index 7cda01d5905..61f5865c871 100644 --- a/hapi-fhir-jpaserver-test-dstu3/src/test/java/ca/uhn/fhir/jpa/dao/dstu3/FhirSystemDaoDstu3Test.java +++ b/hapi-fhir-jpaserver-test-dstu3/src/test/java/ca/uhn/fhir/jpa/dao/dstu3/FhirSystemDaoDstu3Test.java @@ -69,7 +69,7 @@ import org.springframework.transaction.TransactionStatus; import org.springframework.transaction.support.TransactionCallbackWithoutResult; import org.springframework.transaction.support.TransactionTemplate; -import javax.annotation.Nonnull; +import jakarta.annotation.Nonnull; import java.io.IOException; import java.io.InputStream; import java.math.BigDecimal; @@ -2350,7 +2350,7 @@ public class FhirSystemDaoDstu3Test extends BaseJpaDstu3SystemTest { try { mySystemDao.transaction(mySrd, b); } catch (ResourceVersionConflictException e) { - assertEquals(Msg.code(550) + Msg.code(550) + Msg.code(989) + "Trying to update Patient/P1/_history/2 but this is not the current version", e.getMessage()); + assertEquals(Msg.code(550) + Msg.code(989) + "Trying to update Patient/P1/_history/2 but this is not the current version", e.getMessage()); } b = new Bundle(); diff --git a/hapi-fhir-jpaserver-test-dstu3/src/test/java/ca/uhn/fhir/jpa/packages/IgInstallerDstu3Test.java b/hapi-fhir-jpaserver-test-dstu3/src/test/java/ca/uhn/fhir/jpa/packages/IgInstallerDstu3Test.java index 37e0a8ad6b8..e6c00f876bb 100644 --- a/hapi-fhir-jpaserver-test-dstu3/src/test/java/ca/uhn/fhir/jpa/packages/IgInstallerDstu3Test.java +++ b/hapi-fhir-jpaserver-test-dstu3/src/test/java/ca/uhn/fhir/jpa/packages/IgInstallerDstu3Test.java @@ -10,9 +10,10 @@ import ca.uhn.fhir.rest.server.exceptions.InvalidRequestException; import ca.uhn.fhir.rest.server.exceptions.ResourceNotFoundException; import ca.uhn.fhir.test.utilities.JettyUtil; import ca.uhn.fhir.test.utilities.ProxyUtil; +import ca.uhn.fhir.test.utilities.server.HttpServletExtension; import org.eclipse.jetty.server.Server; -import org.eclipse.jetty.servlet.ServletHandler; -import org.eclipse.jetty.servlet.ServletHolder; +import org.eclipse.jetty.ee10.servlet.ServletHandler; +import org.eclipse.jetty.ee10.servlet.ServletHolder; import org.hl7.fhir.instance.model.api.IBaseBinary; import org.hl7.fhir.instance.model.api.IBaseResource; import org.hl7.fhir.r4.model.Patient; @@ -21,6 +22,7 @@ import org.hl7.fhir.utilities.npm.PackageServer; import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.RegisterExtension; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; @@ -47,36 +49,29 @@ public class IgInstallerDstu3Test extends BaseJpaDstu3Test { @Autowired @Qualifier(PackageUtils.LOADER_WITH_CACHE) private IHapiPackageCacheManager myPackageCacheManager; - private Server myServer; - private FakeNpmServlet myFakeNpmServlet; @Autowired private INpmPackageVersionDao myPackageVersionDao; - private int myPort; + private FakeNpmServlet myFakeNpmServlet = new FakeNpmServlet(); + @RegisterExtension + public HttpServletExtension myServer = new HttpServletExtension() + .withServlet(myFakeNpmServlet); + + @Override @BeforeEach public void before() throws Exception { super.before(); JpaPackageCache jpaPackageCache = ProxyUtil.getSingletonTarget(myPackageCacheManager, JpaPackageCache.class); - - myServer = new Server(0); - ServletHandler proxyHandler = new ServletHandler(); - myFakeNpmServlet = new FakeNpmServlet(); - ServletHolder servletHolder = new ServletHolder(myFakeNpmServlet); - proxyHandler.addServletWithMapping(servletHolder, "/*"); - myServer.setHandler(proxyHandler); - myServer.start(); - - myPort = JettyUtil.getPortForStartedServer(myServer); + jpaPackageCache.getPackageServers().clear(); - jpaPackageCache.addPackageServer(new PackageServer("http://localhost:" + myPort)); + jpaPackageCache.addPackageServer(new PackageServer(myServer.getBaseUrl())); myFakeNpmServlet.getResponses().clear(); } @AfterEach public void after() throws Exception { - JettyUtil.closeServer(myServer); myStorageSettings.setAllowExternalReferences(new JpaStorageSettings().isAllowExternalReferences()); } @@ -156,7 +151,7 @@ public class IgInstallerDstu3Test extends BaseJpaDstu3Test { igInstaller.install(new PackageInstallationSpec() .setName("nictiz.fhir.nl.stu3.questionnaires") .setVersion("1.0.2") - .setPackageUrl("http://localhost:" + myPort + "/foo.tgz") + .setPackageUrl(myServer.getBaseUrl() + "/foo.tgz") ); runInTransaction(() -> { @@ -240,7 +235,7 @@ public class IgInstallerDstu3Test extends BaseJpaDstu3Test { igInstaller.install(new PackageInstallationSpec() .setName("blah") .setVersion("1.0.2") - .setPackageUrl("http://localhost:" + myPort + "/foo.tgz") + .setPackageUrl(myServer.getBaseUrl() + "/foo.tgz") ); fail(); } catch (InvalidRequestException e) { @@ -255,11 +250,11 @@ public class IgInstallerDstu3Test extends BaseJpaDstu3Test { igInstaller.install(new PackageInstallationSpec() .setName("blah") .setVersion("1.0.2") - .setPackageUrl("http://localhost:" + myPort + "/foo.tgz") + .setPackageUrl(myServer.getBaseUrl() + "/foo.tgz") ); fail(); } catch (ResourceNotFoundException e) { - assertEquals(Msg.code(1303) + "Received HTTP 404 from URL: http://localhost:" + myPort + "/foo.tgz", e.getMessage()); + assertEquals(Msg.code(1303) + "Received HTTP 404 from URL: " + myServer.getBaseUrl() + "/foo.tgz", e.getMessage()); } } diff --git a/hapi-fhir-jpaserver-test-dstu3/src/test/java/ca/uhn/fhir/jpa/packages/NpmDstu3Test.java b/hapi-fhir-jpaserver-test-dstu3/src/test/java/ca/uhn/fhir/jpa/packages/NpmDstu3Test.java index bee28e90161..0f293238783 100644 --- a/hapi-fhir-jpaserver-test-dstu3/src/test/java/ca/uhn/fhir/jpa/packages/NpmDstu3Test.java +++ b/hapi-fhir-jpaserver-test-dstu3/src/test/java/ca/uhn/fhir/jpa/packages/NpmDstu3Test.java @@ -1,35 +1,24 @@ package ca.uhn.fhir.jpa.packages; import ca.uhn.fhir.jpa.test.BaseJpaDstu3Test; -import ca.uhn.fhir.rest.api.Constants; import ca.uhn.fhir.rest.api.MethodOutcome; import ca.uhn.fhir.rest.api.ValidationModeEnum; -import ca.uhn.fhir.test.utilities.JettyUtil; import ca.uhn.fhir.test.utilities.ProxyUtil; +import ca.uhn.fhir.test.utilities.server.HttpServletExtension; import ca.uhn.fhir.util.ClasspathUtil; -import org.eclipse.jetty.server.Server; -import org.eclipse.jetty.servlet.ServletHandler; -import org.eclipse.jetty.servlet.ServletHolder; import org.hl7.fhir.dstu3.model.CodeSystem; import org.hl7.fhir.dstu3.model.Condition; import org.hl7.fhir.dstu3.model.OperationOutcome; import org.hl7.fhir.dstu3.model.StructureDefinition; import org.hl7.fhir.dstu3.model.ValueSet; import org.hl7.fhir.utilities.npm.PackageServer; -import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.RegisterExtension; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; -import javax.servlet.http.HttpServlet; -import javax.servlet.http.HttpServletRequest; -import javax.servlet.http.HttpServletResponse; -import java.io.IOException; -import java.util.HashMap; -import java.util.Map; - import static org.hamcrest.MatcherAssert.assertThat; import static org.hamcrest.Matchers.containsString; import static org.junit.jupiter.api.Assertions.assertEquals; @@ -44,43 +33,34 @@ public class NpmDstu3Test extends BaseJpaDstu3Test { @Autowired private NpmJpaValidationSupport myNpmJpaValidationSupport; - private Server myServer; - private final Map myResponses = new HashMap<>(); + private ca.uhn.fhir.jpa.packages.FakeNpmServlet myFakeNpmServlet = new ca.uhn.fhir.jpa.packages.FakeNpmServlet(); + @RegisterExtension + public HttpServletExtension myServer = new HttpServletExtension() + .withServlet(myFakeNpmServlet); + @Override @BeforeEach public void before() throws Exception { + super.before(); JpaPackageCache jpaPackageCache = ProxyUtil.getSingletonTarget(myPackageCacheManager, JpaPackageCache.class); - myServer = new Server(0); - ServletHandler proxyHandler = new ServletHandler(); - FakeNpmServlet fakeNpmServlet = new FakeNpmServlet(); - ServletHolder servletHolder = new ServletHolder(fakeNpmServlet); - proxyHandler.addServletWithMapping(servletHolder, "/*"); - myServer.setHandler(proxyHandler); - myServer.start(); - - int port = JettyUtil.getPortForStartedServer(myServer); jpaPackageCache.getPackageServers().clear(); - jpaPackageCache.addPackageServer(new PackageServer("http://localhost:" + port)); + jpaPackageCache.addPackageServer(new PackageServer(myServer.getBaseUrl())); - myResponses.clear(); + myFakeNpmServlet.getResponses().clear(); } - @AfterEach - public void after() throws Exception { - JettyUtil.closeServer(myServer); - } @Test public void installDstu3Package() throws Exception { byte[] bytes = ClasspathUtil.loadResourceAsByteArray("/packages/basisprofil.de.tar.gz"); - myResponses.put("/basisprofil.de/0.2.40", bytes); + myFakeNpmServlet.getResponses().put("/basisprofil.de/0.2.40", bytes); PackageInstallationSpec spec = new PackageInstallationSpec().setName("basisprofil.de").setVersion("0.2.40").setInstallMode(PackageInstallationSpec.InstallModeEnum.STORE_ONLY); igInstaller.install(spec); // Be sure no further communication with the server - JettyUtil.closeServer(myServer); + myServer.stopServer(); StructureDefinition sd = (StructureDefinition) myNpmJpaValidationSupport.fetchStructureDefinition("http://fhir.de/StructureDefinition/condition-de-basis/0.2"); assertEquals("http://fhir.de/StructureDefinition/condition-de-basis/0.2", sd.getUrl()); @@ -104,25 +84,4 @@ public class NpmDstu3Test extends BaseJpaDstu3Test { containsString("Condition.subject: minimum required = 1, but only found 0 (from http://fhir.de/StructureDefinition/condition-de-basis/0.2")); } - - private class FakeNpmServlet extends HttpServlet { - - @Override - protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws IOException { - String requestUrl = req.getRequestURI(); - if (myResponses.containsKey(requestUrl)) { - ourLog.info("Responding to request: {}", requestUrl); - - resp.setStatus(200); - resp.setHeader(Constants.HEADER_CONTENT_TYPE, "application/gzip"); - resp.getOutputStream().write(myResponses.get(requestUrl)); - resp.getOutputStream().close(); - } else { - ourLog.warn("Unknown request: {}", requestUrl); - - resp.sendError(404); - } - - } - } } diff --git a/hapi-fhir-jpaserver-test-dstu3/src/test/java/ca/uhn/fhir/jpa/provider/dstu3/ResourceProviderDstu3CodeSystemPropertiesTest.java b/hapi-fhir-jpaserver-test-dstu3/src/test/java/ca/uhn/fhir/jpa/provider/dstu3/ResourceProviderDstu3CodeSystemPropertiesTest.java new file mode 100644 index 00000000000..5ea44c64476 --- /dev/null +++ b/hapi-fhir-jpaserver-test-dstu3/src/test/java/ca/uhn/fhir/jpa/provider/dstu3/ResourceProviderDstu3CodeSystemPropertiesTest.java @@ -0,0 +1,107 @@ +package ca.uhn.fhir.jpa.provider.dstu3; + +import ca.uhn.fhir.jpa.model.util.JpaConstants; +import ca.uhn.fhir.jpa.provider.CodeSystemLookupWithPropertiesUtil; +import ca.uhn.fhir.rest.gclient.IOperationUntypedWithInputAndPartialOutput; +import org.hl7.fhir.dstu3.model.CodeSystem; +import org.hl7.fhir.dstu3.model.CodeSystem.ConceptDefinitionComponent; +import org.hl7.fhir.dstu3.model.CodeSystem.ConceptPropertyComponent; +import org.hl7.fhir.dstu3.model.CodeType; +import org.hl7.fhir.dstu3.model.Coding; +import org.hl7.fhir.dstu3.model.Parameters; +import org.hl7.fhir.dstu3.model.Parameters.ParametersParameterComponent; +import org.hl7.fhir.dstu3.model.StringType; +import org.hl7.fhir.dstu3.model.UriType; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.Arguments; +import org.junit.jupiter.params.provider.MethodSource; + +import java.util.Iterator; +import java.util.List; +import java.util.stream.Stream; + +import static ca.uhn.fhir.jpa.provider.CodeSystemLookupWithPropertiesUtil.ourCode; +import static ca.uhn.fhir.jpa.provider.CodeSystemLookupWithPropertiesUtil.ourCodeSystemId; +import static ca.uhn.fhir.jpa.provider.CodeSystemLookupWithPropertiesUtil.ourCodeSystemUrl; +import static ca.uhn.fhir.jpa.provider.CodeSystemLookupWithPropertiesUtil.ourPropertyA; +import static ca.uhn.fhir.jpa.provider.CodeSystemLookupWithPropertiesUtil.ourPropertyB; +import static ca.uhn.fhir.jpa.provider.CodeSystemLookupWithPropertiesUtil.ourPropertyC; +import static ca.uhn.fhir.jpa.provider.CodeSystemLookupWithPropertiesUtil.ourPropertyValueA; +import static ca.uhn.fhir.jpa.provider.CodeSystemLookupWithPropertiesUtil.ourPropertyValueB; +import static ca.uhn.fhir.jpa.provider.CodeSystemLookupWithPropertiesUtil.propertyCode; +import static ca.uhn.fhir.jpa.provider.CodeSystemLookupWithPropertiesUtil.propertyCodeSystem; +import static ca.uhn.fhir.jpa.provider.CodeSystemLookupWithPropertiesUtil.propertyDisplay; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNotNull; +import static org.junit.jupiter.api.Assertions.assertNull; +import static org.junit.jupiter.api.Assertions.assertTrue; + +public class ResourceProviderDstu3CodeSystemPropertiesTest extends BaseResourceProviderDstu3Test { + + public static Stream parametersLookup() { + return CodeSystemLookupWithPropertiesUtil.parametersLookupWithProperties(); + } + + @ParameterizedTest + @MethodSource(value = "parametersLookup") + public void testLookup_withProperties_returnsCorrectParameters(List theLookupProperties, List theExpectedReturnedProperties) { + // setup + CodeSystem codeSystem = new CodeSystem(); + codeSystem.setId(ourCodeSystemId); + codeSystem.setUrl(ourCodeSystemUrl); + ConceptDefinitionComponent concept = codeSystem.addConcept().setCode(ourCode) + .addProperty(new ConceptPropertyComponent().setCode(ourPropertyA).setValue(new StringType(ourPropertyValueA))) + .addProperty(new ConceptPropertyComponent().setCode(ourPropertyB).setValue(new StringType(ourPropertyValueB))) + .addProperty(new ConceptPropertyComponent().setCode(ourPropertyC).setValue(new Coding(propertyCodeSystem, propertyCode, propertyDisplay))); + myCodeSystemDao.create(codeSystem, mySrd); + + // test + IOperationUntypedWithInputAndPartialOutput respParam = myClient + .operation() + .onType(CodeSystem.class) + .named(JpaConstants.OPERATION_LOOKUP) + .withParameter(Parameters.class, "code", new CodeType(ourCode)) + .andParameter("system", new UriType(ourCodeSystemUrl)); + + theLookupProperties.forEach(p -> respParam.andParameter("property", new CodeType(p))); + Parameters parameters = respParam.execute(); + + Iterator paramIterator = parameters.getParameter().iterator(); + ParametersParameterComponent parameter = null; + while (paramIterator.hasNext()) { + ParametersParameterComponent currentParameter = paramIterator.next(); + if (currentParameter.getName().equals("property")) { + parameter = currentParameter; + break; + } + } + + // verify + if (theExpectedReturnedProperties.isEmpty()) { + assertNull(parameter); + return; + } + + Iterator propertyIterator = concept.getProperty().stream() + .filter(property -> theExpectedReturnedProperties.contains(property.getCode())).iterator(); + + while (propertyIterator.hasNext()) { + ConceptPropertyComponent property = propertyIterator.next(); + assertNotNull(parameter); + + Iterator parameterPartIterator = parameter.getPart().iterator(); + + parameter = parameterPartIterator.next(); + assertEquals("code", parameter.getName()); + assertEquals(property.getCode(), ((CodeType) parameter.getValue()).getValue()); + + parameter = parameterPartIterator.next(); + assertEquals("value", parameter.getName()); + assertTrue(property.getValue().equalsShallow(parameter.getValue())); + + if (paramIterator.hasNext()) { + parameter = paramIterator.next(); + } + } + } +} diff --git a/hapi-fhir-jpaserver-test-dstu3/src/test/java/ca/uhn/fhir/jpa/provider/dstu3/ResourceProviderDstu3ValueSetTest.java b/hapi-fhir-jpaserver-test-dstu3/src/test/java/ca/uhn/fhir/jpa/provider/dstu3/ResourceProviderDstu3ValueSetTest.java index 09ce4621a13..a5baecc2e3b 100644 --- a/hapi-fhir-jpaserver-test-dstu3/src/test/java/ca/uhn/fhir/jpa/provider/dstu3/ResourceProviderDstu3ValueSetTest.java +++ b/hapi-fhir-jpaserver-test-dstu3/src/test/java/ca/uhn/fhir/jpa/provider/dstu3/ResourceProviderDstu3ValueSetTest.java @@ -1,5 +1,6 @@ package ca.uhn.fhir.jpa.provider.dstu3; +import ca.uhn.fhir.context.support.IValidationSupport; import ca.uhn.fhir.i18n.Msg; import ca.uhn.fhir.jpa.api.config.JpaStorageSettings; import ca.uhn.fhir.jpa.api.dao.IFhirResourceDao; @@ -9,7 +10,6 @@ import ca.uhn.fhir.jpa.entity.TermConcept; import ca.uhn.fhir.jpa.entity.TermConceptParentChildLink.RelationshipTypeEnum; import ca.uhn.fhir.jpa.model.dao.JpaPid; import ca.uhn.fhir.jpa.model.entity.ResourceTable; -import ca.uhn.fhir.jpa.provider.ValueSetOperationProvider; import ca.uhn.fhir.jpa.term.api.ITermCodeSystemStorageSvc; import ca.uhn.fhir.jpa.util.CircularQueueCaptureQueriesListener; import ca.uhn.fhir.rest.server.exceptions.InvalidRequestException; @@ -43,7 +43,7 @@ import org.springframework.transaction.TransactionStatus; import org.springframework.transaction.support.TransactionCallbackWithoutResult; import org.springframework.transaction.support.TransactionTemplate; -import javax.annotation.Nonnull; +import jakarta.annotation.Nonnull; import java.io.IOException; import java.nio.charset.StandardCharsets; import java.util.ArrayList; @@ -790,13 +790,13 @@ public class ResourceProviderDstu3ValueSetTest extends BaseResourceProviderDstu3 String resp = myFhirContext.newXmlParser().setPrettyPrint(true).encodeResourceToString(respParam); ourLog.info(resp); - assertEquals(ValueSetOperationProvider.RESULT, respParam.getParameter().get(0).getName()); + assertEquals(IValidationSupport.CodeValidationResult.RESULT, respParam.getParameter().get(0).getName()); assertEquals(true, ((BooleanType) respParam.getParameter().get(0).getValue()).getValue()); - assertEquals(ValueSetOperationProvider.DISPLAY, respParam.getParameter().get(1).getName()); + assertEquals(IValidationSupport.CodeValidationResult.DISPLAY, respParam.getParameter().get(1).getName()); assertEquals("Male", ((StringType) respParam.getParameter().get(1).getValue()).getValue()); - assertEquals(ValueSetOperationProvider.SOURCE_DETAILS, respParam.getParameter().get(2).getName()); + assertEquals(IValidationSupport.CodeValidationResult.SOURCE_DETAILS, respParam.getParameter().get(2).getName()); assertEquals("Code was validated against in-memory expansion of ValueSet: http://hl7.org/fhir/ValueSet/administrative-gender", ((StringType) respParam.getParameter().get(2).getValue()).getValue()); } @@ -820,13 +820,13 @@ public class ResourceProviderDstu3ValueSetTest extends BaseResourceProviderDstu3 String resp = myFhirContext.newXmlParser().setPrettyPrint(true).encodeResourceToString(respParam); ourLog.info(resp); - assertEquals(ValueSetOperationProvider.RESULT, respParam.getParameter().get(0).getName()); + assertEquals(IValidationSupport.CodeValidationResult.RESULT, respParam.getParameter().get(0).getName()); assertEquals(true, ((BooleanType) respParam.getParameter().get(0).getValue()).getValue()); - assertEquals(ValueSetOperationProvider.DISPLAY, respParam.getParameter().get(1).getName()); + assertEquals(IValidationSupport.CodeValidationResult.DISPLAY, respParam.getParameter().get(1).getName()); assertEquals("Male", ((StringType) respParam.getParameter().get(1).getValue()).getValue()); - assertEquals(ValueSetOperationProvider.SOURCE_DETAILS, respParam.getParameter().get(2).getName()); + assertEquals(IValidationSupport.CodeValidationResult.SOURCE_DETAILS, respParam.getParameter().get(2).getName()); assertEquals("Code was validated against in-memory expansion of ValueSet: http://hl7.org/fhir/ValueSet/administrative-gender", ((StringType) respParam.getParameter().get(2).getValue()).getValue()); } diff --git a/hapi-fhir-jpaserver-test-dstu3/src/test/java/ca/uhn/fhir/jpa/provider/dstu3/ResourceProviderDstu3ValueSetVersionedTest.java b/hapi-fhir-jpaserver-test-dstu3/src/test/java/ca/uhn/fhir/jpa/provider/dstu3/ResourceProviderDstu3ValueSetVersionedTest.java index 888cd3b2497..aeb4cc7328c 100644 --- a/hapi-fhir-jpaserver-test-dstu3/src/test/java/ca/uhn/fhir/jpa/provider/dstu3/ResourceProviderDstu3ValueSetVersionedTest.java +++ b/hapi-fhir-jpaserver-test-dstu3/src/test/java/ca/uhn/fhir/jpa/provider/dstu3/ResourceProviderDstu3ValueSetVersionedTest.java @@ -40,7 +40,7 @@ import org.springframework.transaction.TransactionStatus; import org.springframework.transaction.support.TransactionCallbackWithoutResult; import org.springframework.transaction.support.TransactionTemplate; -import javax.annotation.Nonnull; +import jakarta.annotation.Nonnull; import java.io.IOException; import java.util.Optional; diff --git a/hapi-fhir-jpaserver-test-dstu3/src/test/java/ca/uhn/fhir/jpa/provider/dstu3/SubscriptionsDstu3Test.java b/hapi-fhir-jpaserver-test-dstu3/src/test/java/ca/uhn/fhir/jpa/provider/dstu3/SubscriptionsDstu3Test.java index 95c777537f6..c716da163e4 100644 --- a/hapi-fhir-jpaserver-test-dstu3/src/test/java/ca/uhn/fhir/jpa/provider/dstu3/SubscriptionsDstu3Test.java +++ b/hapi-fhir-jpaserver-test-dstu3/src/test/java/ca/uhn/fhir/jpa/provider/dstu3/SubscriptionsDstu3Test.java @@ -5,8 +5,8 @@ import ca.uhn.fhir.jpa.util.SubscriptionsRequireManualActivationInterceptorDstu3 import ca.uhn.fhir.rest.api.EncodingEnum; import ca.uhn.fhir.rest.server.exceptions.UnprocessableEntityException; import org.eclipse.jetty.websocket.api.Session; -import org.eclipse.jetty.websocket.api.annotations.OnWebSocketConnect; import org.eclipse.jetty.websocket.api.annotations.OnWebSocketMessage; +import org.eclipse.jetty.websocket.api.annotations.OnWebSocketOpen; import org.eclipse.jetty.websocket.api.annotations.WebSocket; import org.hl7.fhir.dstu3.model.Subscription; import org.hl7.fhir.dstu3.model.Subscription.SubscriptionChannelType; @@ -179,7 +179,7 @@ public class SubscriptionsDstu3Test extends BaseResourceProviderDstu3Test { /** * Basic Echo Client Socket */ - @WebSocket(maxTextMessageSize = 64 * 1024) + @WebSocket public class DynamicEchoSocket extends BaseSocket { private String myCriteria; @@ -193,14 +193,14 @@ public class SubscriptionsDstu3Test extends BaseResourceProviderDstu3Test { myEncoding = theEncoding; } - @OnWebSocketConnect + @OnWebSocketOpen public void onConnect(Session session) { ourLog.info("Got connect: {}", session); this.session = session; try { String sending = "bind " + myCriteria; ourLog.info("Sending: {}", sending); - session.getRemote().sendString(sending); + session.sendText(sending, null); } catch (Throwable t) { ourLog.error("Failure", t); } @@ -227,7 +227,7 @@ public class SubscriptionsDstu3Test extends BaseResourceProviderDstu3Test { /** * Basic Echo Client Socket */ - @WebSocket(maxTextMessageSize = 64 * 1024) + @WebSocket public class SimpleEchoSocket extends BaseSocket { @SuppressWarnings("unused") @@ -237,14 +237,14 @@ public class SubscriptionsDstu3Test extends BaseResourceProviderDstu3Test { mySubsId = theSubsId; } - @OnWebSocketConnect + @OnWebSocketOpen public void onConnect(Session session) { ourLog.info("Got connect: {}", session); this.session = session; try { String sending = "bind " + mySubsId; ourLog.info("Sending: {}", sending); - session.getRemote().sendString(sending); + session.sendText(sending, null); } catch (Throwable t) { ourLog.error("Failure", t); } diff --git a/hapi-fhir-jpaserver-test-dstu3/src/test/java/ca/uhn/fhir/jpa/provider/dstu3/SystemProviderTransactionSearchDstu3Test.java b/hapi-fhir-jpaserver-test-dstu3/src/test/java/ca/uhn/fhir/jpa/provider/dstu3/SystemProviderTransactionSearchDstu3Test.java index 75f5a7301d7..93e904b44a7 100644 --- a/hapi-fhir-jpaserver-test-dstu3/src/test/java/ca/uhn/fhir/jpa/provider/dstu3/SystemProviderTransactionSearchDstu3Test.java +++ b/hapi-fhir-jpaserver-test-dstu3/src/test/java/ca/uhn/fhir/jpa/provider/dstu3/SystemProviderTransactionSearchDstu3Test.java @@ -16,8 +16,8 @@ import org.apache.http.impl.client.CloseableHttpClient; import org.apache.http.impl.client.HttpClientBuilder; import org.apache.http.impl.conn.PoolingHttpClientConnectionManager; import org.eclipse.jetty.server.Server; -import org.eclipse.jetty.servlet.ServletContextHandler; -import org.eclipse.jetty.servlet.ServletHolder; +import org.eclipse.jetty.ee10.servlet.ServletContextHandler; +import org.eclipse.jetty.ee10.servlet.ServletHolder; import org.hl7.fhir.dstu3.model.Binary; import org.hl7.fhir.dstu3.model.Bundle; import org.hl7.fhir.dstu3.model.Bundle.BundleEntryComponent; diff --git a/hapi-fhir-jpaserver-test-dstu3/src/test/java/ca/uhn/fhir/jpa/subscription/resthook/RestHookTestDstu3Test.java b/hapi-fhir-jpaserver-test-dstu3/src/test/java/ca/uhn/fhir/jpa/subscription/resthook/RestHookTestDstu3Test.java index 43e66e09e3b..de033a89c7b 100644 --- a/hapi-fhir-jpaserver-test-dstu3/src/test/java/ca/uhn/fhir/jpa/subscription/resthook/RestHookTestDstu3Test.java +++ b/hapi-fhir-jpaserver-test-dstu3/src/test/java/ca/uhn/fhir/jpa/subscription/resthook/RestHookTestDstu3Test.java @@ -26,8 +26,8 @@ import ca.uhn.fhir.util.HapiExtensions; import ca.uhn.fhir.util.MetaUtil; import com.google.common.collect.Lists; import org.eclipse.jetty.server.Server; -import org.eclipse.jetty.servlet.ServletContextHandler; -import org.eclipse.jetty.servlet.ServletHolder; +import org.eclipse.jetty.ee10.servlet.ServletContextHandler; +import org.eclipse.jetty.ee10.servlet.ServletHolder; import org.hl7.fhir.dstu3.model.BooleanType; import org.hl7.fhir.dstu3.model.CodeableConcept; import org.hl7.fhir.dstu3.model.Coding; @@ -51,8 +51,8 @@ import org.junit.jupiter.params.ParameterizedTest; import org.junit.jupiter.params.provider.ValueSource; import org.springframework.beans.factory.annotation.Autowired; -import javax.annotation.Nonnull; -import javax.servlet.http.HttpServletRequest; +import jakarta.annotation.Nonnull; +import jakarta.servlet.http.HttpServletRequest; import java.util.ArrayList; import java.util.Collections; import java.util.List; diff --git a/hapi-fhir-jpaserver-test-dstu3/src/test/java/ca/uhn/fhir/jpa/subscription/resthook/RestHookTestWithInterceptorRegisteredToStorageSettingsDstu3Test.java b/hapi-fhir-jpaserver-test-dstu3/src/test/java/ca/uhn/fhir/jpa/subscription/resthook/RestHookTestWithInterceptorRegisteredToStorageSettingsDstu3Test.java index a4d4f22eff2..83a439228fc 100644 --- a/hapi-fhir-jpaserver-test-dstu3/src/test/java/ca/uhn/fhir/jpa/subscription/resthook/RestHookTestWithInterceptorRegisteredToStorageSettingsDstu3Test.java +++ b/hapi-fhir-jpaserver-test-dstu3/src/test/java/ca/uhn/fhir/jpa/subscription/resthook/RestHookTestWithInterceptorRegisteredToStorageSettingsDstu3Test.java @@ -15,8 +15,8 @@ import ca.uhn.fhir.rest.server.RestfulServer; import ca.uhn.fhir.test.utilities.JettyUtil; import com.google.common.collect.Lists; import org.eclipse.jetty.server.Server; -import org.eclipse.jetty.servlet.ServletContextHandler; -import org.eclipse.jetty.servlet.ServletHolder; +import org.eclipse.jetty.ee10.servlet.ServletContextHandler; +import org.eclipse.jetty.ee10.servlet.ServletHolder; import org.hl7.fhir.dstu3.model.CodeableConcept; import org.hl7.fhir.dstu3.model.Coding; import org.hl7.fhir.dstu3.model.IdType; diff --git a/hapi-fhir-jpaserver-test-dstu3/src/test/java/ca/uhn/fhir/jpa/subscription/resthook/SubscriptionTriggeringDstu3Test.java b/hapi-fhir-jpaserver-test-dstu3/src/test/java/ca/uhn/fhir/jpa/subscription/resthook/SubscriptionTriggeringDstu3Test.java index 79c2f798c1c..29eab34b330 100644 --- a/hapi-fhir-jpaserver-test-dstu3/src/test/java/ca/uhn/fhir/jpa/subscription/resthook/SubscriptionTriggeringDstu3Test.java +++ b/hapi-fhir-jpaserver-test-dstu3/src/test/java/ca/uhn/fhir/jpa/subscription/resthook/SubscriptionTriggeringDstu3Test.java @@ -23,8 +23,8 @@ import ca.uhn.fhir.test.utilities.JettyUtil; import ca.uhn.fhir.test.utilities.ProxyUtil; import com.google.common.collect.Lists; import org.eclipse.jetty.server.Server; -import org.eclipse.jetty.servlet.ServletContextHandler; -import org.eclipse.jetty.servlet.ServletHolder; +import org.eclipse.jetty.ee10.servlet.ServletContextHandler; +import org.eclipse.jetty.ee10.servlet.ServletHolder; import org.hl7.fhir.dstu3.model.CodeableConcept; import org.hl7.fhir.dstu3.model.Coding; import org.hl7.fhir.dstu3.model.IdType; @@ -44,9 +44,11 @@ import org.junit.jupiter.api.DisplayName; import org.junit.jupiter.api.Nested; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.TestInstance; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.CsvSource; import org.springframework.beans.factory.annotation.Autowired; -import javax.servlet.http.HttpServletRequest; +import jakarta.servlet.http.HttpServletRequest; import java.util.Collections; import java.util.List; import java.util.stream.Collectors; @@ -499,14 +501,18 @@ public class SubscriptionTriggeringDstu3Test extends BaseResourceProviderDstu3Te myInterceptorService.unregisterInterceptor(forceSynchronousSearchInterceptor); } - @Test - public void testTriggerSubscriptionInSynchronousQueryMode() throws Exception { - ((SubscriptionTriggeringSvcImpl)mySubscriptionTriggeringSvc).setMaxSubmitPerPass(10); + @ParameterizedTest + @CsvSource({ + "10", + "10000" + }) + public void testTriggerSubscriptionInSynchronousQueryMode(int theMaxSubmitPerpass) throws Exception { + ((SubscriptionTriggeringSvcImpl)mySubscriptionTriggeringSvc).setMaxSubmitPerPass(theMaxSubmitPerpass); String payload = "application/fhir+json"; IdType sub2id = createSubscription("Patient?", payload, ourListenerServerBase).getIdElement(); - int numberOfPatient = 15; + int numberOfPatient = 200; // Create lots createPatientsAndWait(numberOfPatient); @@ -522,9 +528,9 @@ public class SubscriptionTriggeringDstu3Test extends BaseResourceProviderDstu3Te .withParameter(Parameters.class, ProviderConstants.SUBSCRIPTION_TRIGGERING_PARAM_SEARCH_URL, new StringType("Patient?")) .execute(); - mySubscriptionTriggeringSvc.runDeliveryPass(); - mySubscriptionTriggeringSvc.runDeliveryPass(); - mySubscriptionTriggeringSvc.runDeliveryPass(); + for (int i = 0; i < 20; i++) { + mySubscriptionTriggeringSvc.runDeliveryPass(); + } waitForSize(0, ourCreatedPatients); waitForSize(numberOfPatient, ourUpdatedPatients); diff --git a/hapi-fhir-jpaserver-test-dstu3/src/test/java/ca/uhn/fhir/jpa/term/TerminologySvcImplDstu3Test.java b/hapi-fhir-jpaserver-test-dstu3/src/test/java/ca/uhn/fhir/jpa/term/TerminologySvcImplDstu3Test.java index b36ee053d6e..1eeff788b9f 100644 --- a/hapi-fhir-jpaserver-test-dstu3/src/test/java/ca/uhn/fhir/jpa/term/TerminologySvcImplDstu3Test.java +++ b/hapi-fhir-jpaserver-test-dstu3/src/test/java/ca/uhn/fhir/jpa/term/TerminologySvcImplDstu3Test.java @@ -32,7 +32,7 @@ import org.springframework.transaction.TransactionStatus; import org.springframework.transaction.support.TransactionCallbackWithoutResult; import org.springframework.transaction.support.TransactionTemplate; -import javax.annotation.Nonnull; +import jakarta.annotation.Nonnull; import java.util.ArrayList; import java.util.List; import java.util.Set; diff --git a/hapi-fhir-jpaserver-test-r4/pom.xml b/hapi-fhir-jpaserver-test-r4/pom.xml index 7d27d009288..fbcdfb36f6e 100644 --- a/hapi-fhir-jpaserver-test-r4/pom.xml +++ b/hapi-fhir-jpaserver-test-r4/pom.xml @@ -6,7 +6,7 @@ ca.uhn.hapi.fhir hapi-deployable-pom - 6.9.7-SNAPSHOT + 6.11.7-SNAPSHOT ../hapi-deployable-pom/pom.xml @@ -55,7 +55,12 @@ javax.json-api test - + + jakarta.servlet + jakarta.servlet-api + test + + @@ -78,10 +83,6 @@ - - org.apache.maven.plugins - maven-surefire-plugin - org.apache.maven.plugins maven-checkstyle-plugin diff --git a/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/batch2/Batch2CoordinatorIT.java b/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/batch2/Batch2CoordinatorIT.java index 8b20ca42b83..1114a7e4d58 100644 --- a/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/batch2/Batch2CoordinatorIT.java +++ b/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/batch2/Batch2CoordinatorIT.java @@ -42,7 +42,7 @@ import org.springframework.beans.factory.annotation.Autowired; import org.springframework.data.domain.Page; import org.springframework.data.domain.Sort; -import javax.annotation.Nonnull; +import jakarta.annotation.Nonnull; import java.util.ArrayList; import java.util.Iterator; import java.util.List; diff --git a/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/batch2/Batch2JobMaintenanceDatabaseIT.java b/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/batch2/Batch2JobMaintenanceDatabaseIT.java index b483c06fad4..8030e908f5a 100644 --- a/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/batch2/Batch2JobMaintenanceDatabaseIT.java +++ b/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/batch2/Batch2JobMaintenanceDatabaseIT.java @@ -39,7 +39,7 @@ import org.springframework.messaging.MessageChannel; import org.springframework.messaging.support.ChannelInterceptor; import org.springframework.transaction.support.TransactionTemplate; -import javax.annotation.Nonnull; +import jakarta.annotation.Nonnull; import java.util.ArrayList; import java.util.Date; import java.util.List; diff --git a/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/batch2/Batch2JobMaintenanceIT.java b/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/batch2/Batch2JobMaintenanceIT.java index 6c847de3250..6f989437e6e 100644 --- a/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/batch2/Batch2JobMaintenanceIT.java +++ b/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/batch2/Batch2JobMaintenanceIT.java @@ -31,8 +31,8 @@ import org.springframework.beans.factory.annotation.Autowired; import org.springframework.test.context.ContextConfiguration; import org.springframework.test.context.TestPropertySource; -import javax.annotation.Nonnull; -import javax.annotation.PostConstruct; +import jakarta.annotation.Nonnull; +import jakarta.annotation.PostConstruct; import java.util.ArrayList; import java.util.List; diff --git a/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/batch2/JobInstanceRepositoryTest.java b/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/batch2/JobInstanceRepositoryTest.java index df2f9d891a6..2485daacc91 100644 --- a/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/batch2/JobInstanceRepositoryTest.java +++ b/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/batch2/JobInstanceRepositoryTest.java @@ -18,6 +18,7 @@ import java.util.List; import java.util.Set; import static org.hamcrest.MatcherAssert.assertThat; +import static org.hamcrest.Matchers.equalTo; import static org.hamcrest.Matchers.hasSize; public class JobInstanceRepositoryTest extends BaseJpaR4Test { @@ -30,6 +31,9 @@ public class JobInstanceRepositoryTest extends BaseJpaR4Test { private static final String JOB_DEFINITION_ID = "my-job-def-id"; private static final String INSTANCE_ID = "abc-123"; + private static final String TRIGGERING_USER_NAME = "triggeringUser"; + private static final String TRIGGERING_CLIENT_ID = "clientId"; + @Test public void testSearchByJobParamsAndStatuses_SingleStatus() { Set statuses = Set.of(StatusEnum.IN_PROGRESS); @@ -69,6 +73,16 @@ public class JobInstanceRepositoryTest extends BaseJpaR4Test { assertThat(jobInstances, hasSize(2)); } + @Test + public void testPersistInitiatingUsernameAndClientId() { + Set statuses = Set.of(StatusEnum.IN_PROGRESS); + List instancesByJobIdParamsAndStatus = runInTransaction(()->myJobInstanceRepository.findInstancesByJobIdParamsAndStatus(JOB_DEFINITION_ID, PARAMS, statuses, PageRequest.of(0, 10))); + assertThat(instancesByJobIdParamsAndStatus, hasSize(1)); + Batch2JobInstanceEntity batch2JobInstanceEntity = instancesByJobIdParamsAndStatus.get(0); + assertThat(TRIGGERING_USER_NAME, equalTo(batch2JobInstanceEntity.getTriggeringUsername())); + assertThat(TRIGGERING_CLIENT_ID, equalTo(batch2JobInstanceEntity.getTriggeringClientId())); + } + @BeforeEach public void beforeEach() { //Create in-progress job. @@ -78,6 +92,8 @@ public class JobInstanceRepositoryTest extends BaseJpaR4Test { instance.setCreateTime(new Date()); instance.setDefinitionId(JOB_DEFINITION_ID); instance.setParams(PARAMS); + instance.setTriggeringUsername(TRIGGERING_USER_NAME); + instance.setTriggeringClientId(TRIGGERING_CLIENT_ID); myJobInstanceRepository.save(instance); Batch2JobInstanceEntity completedInstance = new Batch2JobInstanceEntity(); diff --git a/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/batch2/JpaJobPersistenceImplTest.java b/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/batch2/JpaJobPersistenceImplTest.java index 8cb5b12f8ea..c2cdca398fb 100644 --- a/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/batch2/JpaJobPersistenceImplTest.java +++ b/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/batch2/JpaJobPersistenceImplTest.java @@ -11,6 +11,8 @@ import ca.uhn.fhir.batch2.model.WorkChunkCreateEvent; import ca.uhn.fhir.batch2.model.WorkChunkErrorEvent; import ca.uhn.fhir.batch2.model.WorkChunkStatusEnum; import ca.uhn.fhir.batch2.models.JobInstanceFetchRequest; +import ca.uhn.fhir.interceptor.api.IAnonymousInterceptor; +import ca.uhn.fhir.interceptor.api.Pointcut; import ca.uhn.fhir.jpa.dao.data.IBatch2JobInstanceRepository; import ca.uhn.fhir.jpa.dao.data.IBatch2WorkChunkRepository; import ca.uhn.fhir.jpa.entity.Batch2JobInstanceEntity; @@ -33,7 +35,7 @@ import org.springframework.data.domain.PageRequest; import org.springframework.data.domain.Sort; import org.springframework.transaction.PlatformTransactionManager; -import javax.annotation.Nonnull; +import jakarta.annotation.Nonnull; import java.time.Instant; import java.time.LocalDateTime; import java.time.ZoneId; @@ -633,6 +635,30 @@ public class JpaJobPersistenceImplTest extends BaseJpaR4Test { } } + @Test + public void testPrestorageInterceptor_whenModifyingJobInstance_modifiedJobInstanceIsPersisted(){ + String expectedTriggeringUserName = "bobTheUncle"; + + IAnonymousInterceptor prestorageBatchJobCreateInterceptor = (pointcut, params) -> { + JobInstance jobInstance = params.get(JobInstance.class); + jobInstance.setTriggeringUsername(expectedTriggeringUserName); + }; + + try{ + myInterceptorRegistry.registerAnonymousInterceptor(Pointcut.STORAGE_PRESTORAGE_BATCH_JOB_CREATE, prestorageBatchJobCreateInterceptor); + JobInstance instance = createInstance(); + String instanceId = mySvc.storeNewInstance(instance); + + JobInstance foundInstance = mySvc.fetchInstance(instanceId).orElseThrow(IllegalStateException::new); + + assertEquals(expectedTriggeringUserName, foundInstance.getTriggeringUsername()); + + } finally { + myInterceptorRegistry.unregisterInterceptor(prestorageBatchJobCreateInterceptor); + } + + } + private WorkChunk freshFetchWorkChunk(String chunkId) { return runInTransaction(() -> myWorkChunkRepository.findById(chunkId) diff --git a/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/binstore/BinaryAccessProviderTest.java b/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/binstore/BinaryAccessProviderTest.java index 6b69c725c0b..3e4062c5c67 100644 --- a/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/binstore/BinaryAccessProviderTest.java +++ b/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/binstore/BinaryAccessProviderTest.java @@ -26,9 +26,9 @@ import org.mockito.Spy; import org.mockito.junit.jupiter.MockitoExtension; import org.springframework.beans.factory.annotation.Autowired; -import javax.servlet.ServletOutputStream; -import javax.servlet.http.HttpServletRequest; -import javax.servlet.http.HttpServletResponse; +import jakarta.servlet.ServletOutputStream; +import jakarta.servlet.http.HttpServletRequest; +import jakarta.servlet.http.HttpServletResponse; import java.io.IOException; import java.io.InputStream; import java.util.Date; diff --git a/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/bulk/BulkDataExportProviderTest.java b/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/bulk/BulkDataExportProviderTest.java index 4b354894d7d..bd8b9fedc49 100644 --- a/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/bulk/BulkDataExportProviderTest.java +++ b/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/bulk/BulkDataExportProviderTest.java @@ -24,6 +24,7 @@ import ca.uhn.fhir.rest.client.apache.ResourceEntity; import ca.uhn.fhir.rest.server.HardcodedServerAddressStrategy; import ca.uhn.fhir.rest.server.exceptions.ForbiddenOperationException; import ca.uhn.fhir.rest.server.exceptions.ResourceNotFoundException; +import ca.uhn.fhir.rest.server.provider.ProviderConstants; import ca.uhn.fhir.rest.server.tenant.UrlBaseTenantIdentificationStrategy; import ca.uhn.fhir.test.utilities.HttpClientExtension; import ca.uhn.fhir.test.utilities.server.RestfulServerExtension; @@ -192,7 +193,7 @@ public class BulkDataExportProviderTest { ourLog.debug(myCtx.newJsonParser().setPrettyPrint(true).encodeResourceToString(input)); // test - HttpPost post = new HttpPost(myBaseUriForExport + "/" + JpaConstants.OPERATION_EXPORT); + HttpPost post = new HttpPost(myBaseUriForExport + "/" + ProviderConstants.OPERATION_EXPORT); post.addHeader(Constants.HEADER_PREFER, Constants.HEADER_PREFER_RESPOND_ASYNC); post.setEntity(new ResourceEntity(myCtx, input)); ourLog.info("Request: {}", post); @@ -233,7 +234,7 @@ public class BulkDataExportProviderTest { .thenReturn(createJobStartResponse()); Parameters input = new Parameters(); - HttpPost post = new HttpPost(myServer.getBaseUrl() + "/" + JpaConstants.OPERATION_EXPORT); + HttpPost post = new HttpPost(myServer.getBaseUrl() + "/" + ProviderConstants.OPERATION_EXPORT); post.addHeader(Constants.HEADER_PREFER, Constants.HEADER_PREFER_RESPOND_ASYNC); post.setEntity(new ResourceEntity(myCtx, input)); @@ -261,7 +262,7 @@ public class BulkDataExportProviderTest { } else { myBaseUrl = myServer.getBaseUrl(); } - String url = myBaseUrl + "/" + JpaConstants.OPERATION_EXPORT + String url = myBaseUrl + "/" + ProviderConstants.OPERATION_EXPORT + "?" + JpaConstants.PARAM_EXPORT_OUTPUT_FORMAT + "=" + UrlUtil.escapeUrlParam(Constants.CT_FHIR_NDJSON) + "&" + JpaConstants.PARAM_EXPORT_TYPE + "=" + UrlUtil.escapeUrlParam("Patient, Practitioner") + "&" + JpaConstants.PARAM_EXPORT_SINCE + "=" + UrlUtil.escapeUrlParam(now.getValueAsString()) @@ -290,7 +291,7 @@ public class BulkDataExportProviderTest { when(myJobCoordinator.startInstance(isNotNull(), any())) .thenReturn(createJobStartResponse()); - String url = myServer.getBaseUrl() + "/" + JpaConstants.OPERATION_EXPORT + String url = myServer.getBaseUrl() + "/" + ProviderConstants.OPERATION_EXPORT + "?" + JpaConstants.PARAM_EXPORT_OUTPUT_FORMAT + "=" + UrlUtil.escapeUrlParam(Constants.CT_FHIR_NDJSON) + "&" + JpaConstants.PARAM_EXPORT_TYPE + "=" + UrlUtil.escapeUrlParam("Patient,EpisodeOfCare") + "&" + JpaConstants.PARAM_EXPORT_TYPE_FILTER + "=" + UrlUtil.escapeUrlParam("Patient?_id=P999999990") @@ -330,7 +331,7 @@ public class BulkDataExportProviderTest { .thenReturn(info); // test - String url = myServer.getBaseUrl() + "/" + JpaConstants.OPERATION_EXPORT_POLL_STATUS + "?" + + String url = myServer.getBaseUrl() + "/" + ProviderConstants.OPERATION_EXPORT_POLL_STATUS + "?" + JpaConstants.PARAM_EXPORT_POLL_STATUS_JOB_ID + "=" + A_JOB_ID; HttpGet get = new HttpGet(url); get.addHeader(Constants.HEADER_PREFER, Constants.HEADER_PREFER_RESPOND_ASYNC); @@ -362,7 +363,7 @@ public class BulkDataExportProviderTest { .thenReturn(info); // call - String url = myServer.getBaseUrl() + "/" + JpaConstants.OPERATION_EXPORT_POLL_STATUS + "?" + + String url = myServer.getBaseUrl() + "/" + ProviderConstants.OPERATION_EXPORT_POLL_STATUS + "?" + JpaConstants.PARAM_EXPORT_POLL_STATUS_JOB_ID + "=" + A_JOB_ID; HttpGet get = new HttpGet(url); get.addHeader(Constants.HEADER_PREFER, Constants.HEADER_PREFER_RESPOND_ASYNC); @@ -421,7 +422,7 @@ public class BulkDataExportProviderTest { .thenReturn(info); // call - String url = myBaseUriForExport + "/" + JpaConstants.OPERATION_EXPORT_POLL_STATUS + "?" + + String url = myBaseUriForExport + "/" + ProviderConstants.OPERATION_EXPORT_POLL_STATUS + "?" + JpaConstants.PARAM_EXPORT_POLL_STATUS_JOB_ID + "=" + A_JOB_ID; HttpGet get = new HttpGet(url); get.addHeader(Constants.HEADER_PREFER, Constants.HEADER_PREFER_RESPOND_ASYNC); @@ -490,7 +491,7 @@ public class BulkDataExportProviderTest { // call String myBaseUriForExport = myServer.getBaseUrl() + "/Partition-B"; - String url = myBaseUriForExport + "/" + JpaConstants.OPERATION_EXPORT_POLL_STATUS + "?" + + String url = myBaseUriForExport + "/" + ProviderConstants.OPERATION_EXPORT_POLL_STATUS + "?" + JpaConstants.PARAM_EXPORT_POLL_STATUS_JOB_ID + "=" + A_JOB_ID; HttpGet get = new HttpGet(url); get.addHeader(Constants.HEADER_PREFER, Constants.HEADER_PREFER_RESPOND_ASYNC); @@ -527,7 +528,7 @@ public class BulkDataExportProviderTest { .thenReturn(info); // test - String url = myServer.getBaseUrl() + "/" + JpaConstants.OPERATION_EXPORT_POLL_STATUS + "?" + + String url = myServer.getBaseUrl() + "/" + ProviderConstants.OPERATION_EXPORT_POLL_STATUS + "?" + JpaConstants.PARAM_EXPORT_POLL_STATUS_JOB_ID + "=" + A_JOB_ID; HttpGet get = new HttpGet(url); get.addHeader(Constants.HEADER_PREFER, Constants.HEADER_PREFER_RESPOND_ASYNC); @@ -553,7 +554,7 @@ public class BulkDataExportProviderTest { when(myJobCoordinator.getInstance(anyString())) .thenThrow(new ResourceNotFoundException("Unknown job: AAA")); - String url = myServer.getBaseUrl() + "/" + JpaConstants.OPERATION_EXPORT_POLL_STATUS + "?" + + String url = myServer.getBaseUrl() + "/" + ProviderConstants.OPERATION_EXPORT_POLL_STATUS + "?" + JpaConstants.PARAM_EXPORT_POLL_STATUS_JOB_ID + "=" + A_JOB_ID; HttpGet get = new HttpGet(url); get.addHeader(Constants.HEADER_PREFER, Constants.HEADER_PREFER_RESPOND_ASYNC); @@ -596,7 +597,7 @@ public class BulkDataExportProviderTest { ourLog.debug(myCtx.newJsonParser().setPrettyPrint(true).encodeResourceToString(input)); // call - HttpPost post = new HttpPost(myServer.getBaseUrl() + "/" + GROUP_ID + "/" + JpaConstants.OPERATION_EXPORT); + HttpPost post = new HttpPost(myServer.getBaseUrl() + "/" + GROUP_ID + "/" + ProviderConstants.OPERATION_EXPORT); post.addHeader(Constants.HEADER_PREFER, Constants.HEADER_PREFER_RESPOND_ASYNC); post.setEntity(new ResourceEntity(myCtx, input)); ourLog.info("Request: {}", post); @@ -625,7 +626,7 @@ public class BulkDataExportProviderTest { InstantType now = InstantType.now(); - String url = myServer.getBaseUrl() + "/" + GROUP_ID + "/" + JpaConstants.OPERATION_EXPORT + String url = myServer.getBaseUrl() + "/" + GROUP_ID + "/" + ProviderConstants.OPERATION_EXPORT + "?" + JpaConstants.PARAM_EXPORT_OUTPUT_FORMAT + "=" + UrlUtil.escapeUrlParam(Constants.CT_FHIR_NDJSON) + "&" + JpaConstants.PARAM_EXPORT_TYPE + "=" + UrlUtil.escapeUrlParam("Patient, Practitioner") + "&" + JpaConstants.PARAM_EXPORT_SINCE + "=" + UrlUtil.escapeUrlParam(now.getValueAsString()) @@ -660,7 +661,7 @@ public class BulkDataExportProviderTest { InstantType now = InstantType.now(); - String url = myServer.getBaseUrl() + "/" + GROUP_ID + "/" + JpaConstants.OPERATION_EXPORT + String url = myServer.getBaseUrl() + "/" + GROUP_ID + "/" + ProviderConstants.OPERATION_EXPORT + "?" + JpaConstants.PARAM_EXPORT_OUTPUT_FORMAT + "=" + UrlUtil.escapeUrlParam(Constants.CT_FHIR_NDJSON) + "&" + JpaConstants.PARAM_EXPORT_SINCE + "=" + UrlUtil.escapeUrlParam(now.getValueAsString()); @@ -693,7 +694,7 @@ public class BulkDataExportProviderTest { InstantType now = InstantType.now(); // manual construct - String url = myServer.getBaseUrl() + "/" + JpaConstants.OPERATION_EXPORT + String url = myServer.getBaseUrl() + "/" + ProviderConstants.OPERATION_EXPORT + "?" + JpaConstants.PARAM_EXPORT_OUTPUT_FORMAT + "=" + UrlUtil.escapeUrlParam(Constants.CT_FHIR_NDJSON) + "&" + JpaConstants.PARAM_EXPORT_TYPE + "=" + UrlUtil.escapeUrlParam("Immunization, Observation") + "&" + JpaConstants.PARAM_EXPORT_SINCE + "=" + UrlUtil.escapeUrlParam(now.getValueAsString()); @@ -726,7 +727,7 @@ public class BulkDataExportProviderTest { public void testInitiateGroupExportWithInvalidResourceTypesFails() throws IOException { // when - String url = myServer.getBaseUrl() + "/" + "Group/123/" + JpaConstants.OPERATION_EXPORT + String url = myServer.getBaseUrl() + "/" + "Group/123/" + ProviderConstants.OPERATION_EXPORT + "?" + JpaConstants.PARAM_EXPORT_OUTPUT_FORMAT + "=" + UrlUtil.escapeUrlParam(Constants.CT_FHIR_NDJSON) + "&" + JpaConstants.PARAM_EXPORT_TYPE + "=" + UrlUtil.escapeUrlParam("StructureDefinition,Observation"); @@ -747,7 +748,7 @@ public class BulkDataExportProviderTest { when(myJobCoordinator.startInstance(isNotNull(), any())).thenReturn(createJobStartResponse()); // test - String url = myServer.getBaseUrl() + "/" + "Group/123/" + JpaConstants.OPERATION_EXPORT + String url = myServer.getBaseUrl() + "/" + "Group/123/" + ProviderConstants.OPERATION_EXPORT + "?" + JpaConstants.PARAM_EXPORT_OUTPUT_FORMAT + "=" + UrlUtil.escapeUrlParam(Constants.CT_FHIR_NDJSON); HttpGet get = new HttpGet(url); @@ -779,7 +780,7 @@ public class BulkDataExportProviderTest { ourLog.debug(myCtx.newJsonParser().setPrettyPrint(true).encodeResourceToString(input)); // call - HttpPost post = new HttpPost(myServer.getBaseUrl() + "/" + JpaConstants.OPERATION_EXPORT); + HttpPost post = new HttpPost(myServer.getBaseUrl() + "/" + ProviderConstants.OPERATION_EXPORT); post.addHeader(Constants.HEADER_PREFER, Constants.HEADER_PREFER_RESPOND_ASYNC); post.setEntity(new ResourceEntity(myCtx, input)); ourLog.info("Request: {}", post); @@ -808,7 +809,7 @@ public class BulkDataExportProviderTest { input.addParameter(JpaConstants.PARAM_EXPORT_OUTPUT_FORMAT, new StringType(Constants.CT_FHIR_NDJSON)); // call - HttpPost post = new HttpPost(myServer.getBaseUrl() + "/Patient/" + JpaConstants.OPERATION_EXPORT); + HttpPost post = new HttpPost(myServer.getBaseUrl() + "/Patient/" + ProviderConstants.OPERATION_EXPORT); post.addHeader(Constants.HEADER_PREFER, Constants.HEADER_PREFER_RESPOND_ASYNC); post.setEntity(new ResourceEntity(myCtx, input)); ourLog.info("Request: {}", post); @@ -841,7 +842,7 @@ public class BulkDataExportProviderTest { ourLog.debug(myCtx.newJsonParser().setPrettyPrint(true).encodeResourceToString(input)); // call - HttpPost post = new HttpPost(myServer.getBaseUrl() + "/Patient/" + JpaConstants.OPERATION_EXPORT); + HttpPost post = new HttpPost(myServer.getBaseUrl() + "/Patient/" + ProviderConstants.OPERATION_EXPORT); post.addHeader(Constants.HEADER_PREFER, Constants.HEADER_PREFER_RESPOND_ASYNC); post.setEntity(new ResourceEntity(myCtx, input)); ourLog.info("Request: {}", post); @@ -874,7 +875,7 @@ public class BulkDataExportProviderTest { input.addParameter(JpaConstants.PARAM_EXPORT_TYPE, new StringType("Patient, Practitioner")); // call - HttpPost post = new HttpPost(myServer.getBaseUrl() + "/" + JpaConstants.OPERATION_EXPORT); + HttpPost post = new HttpPost(myServer.getBaseUrl() + "/" + ProviderConstants.OPERATION_EXPORT); post.addHeader(Constants.HEADER_PREFER, Constants.HEADER_PREFER_RESPOND_ASYNC); post.addHeader(Constants.HEADER_CACHE_CONTROL, Constants.CACHE_CONTROL_NO_CACHE); post.setEntity(new ResourceEntity(myCtx, input)); @@ -908,7 +909,7 @@ public class BulkDataExportProviderTest { input.addParameter(JpaConstants.PARAM_EXPORT_TYPE, new StringType("Patient, Practitioner")); // call - HttpPost post = new HttpPost(myServer.getBaseUrl() + "/" + JpaConstants.OPERATION_EXPORT); + HttpPost post = new HttpPost(myServer.getBaseUrl() + "/" + ProviderConstants.OPERATION_EXPORT); post.addHeader(Constants.HEADER_PREFER, Constants.HEADER_PREFER_RESPOND_ASYNC); post.setEntity(new ResourceEntity(myCtx, input)); ourLog.info("Request: {}", post); @@ -979,7 +980,7 @@ public class BulkDataExportProviderTest { baseUrl = myServer.getBaseUrl(); } - String url = baseUrl + "/" + JpaConstants.OPERATION_EXPORT_POLL_STATUS + "?" + + String url = baseUrl + "/" + ProviderConstants.OPERATION_EXPORT_POLL_STATUS + "?" + JpaConstants.PARAM_EXPORT_POLL_STATUS_JOB_ID + "=" + A_JOB_ID; HttpDelete delete = new HttpDelete(url); try (CloseableHttpResponse response = myClient.execute(delete)) { @@ -1011,7 +1012,7 @@ public class BulkDataExportProviderTest { .thenReturn(info); // call - String url = myServer.getBaseUrl() + "/" + JpaConstants.OPERATION_EXPORT_POLL_STATUS + "?" + + String url = myServer.getBaseUrl() + "/" + ProviderConstants.OPERATION_EXPORT_POLL_STATUS + "?" + JpaConstants.PARAM_EXPORT_POLL_STATUS_JOB_ID + "=" + A_JOB_ID; HttpDelete delete = new HttpDelete(url); try (CloseableHttpResponse response = myClient.execute(delete)) { @@ -1035,7 +1036,7 @@ public class BulkDataExportProviderTest { .thenReturn(createJobStartResponse()); // call - final HttpGet httpGet = new HttpGet(String.format("http://localhost:%s/%s", myServer.getPort(), JpaConstants.OPERATION_EXPORT)); + final HttpGet httpGet = new HttpGet(String.format("http://localhost:%s/%s", myServer.getPort(), ProviderConstants.OPERATION_EXPORT)); httpGet.addHeader("_outputFormat", Constants.CT_FHIR_NDJSON); httpGet.addHeader(Constants.HEADER_PREFER, Constants.HEADER_PREFER_RESPOND_ASYNC); @@ -1058,7 +1059,7 @@ public class BulkDataExportProviderTest { .thenReturn(createJobStartResponse()); // call - final HttpGet httpGet = new HttpGet(String.format("http://localhost:%s/%s?_outputFormat=%s", myServer.getPort(), JpaConstants.OPERATION_EXPORT, Constants.CT_FHIR_NDJSON)); + final HttpGet httpGet = new HttpGet(String.format("http://localhost:%s/%s?_outputFormat=%s", myServer.getPort(), ProviderConstants.OPERATION_EXPORT, Constants.CT_FHIR_NDJSON)); httpGet.addHeader(Constants.HEADER_PREFER, Constants.HEADER_PREFER_RESPOND_ASYNC); try (CloseableHttpResponse response = myClient.execute(httpGet)) { @@ -1086,7 +1087,7 @@ public class BulkDataExportProviderTest { input.addParameter(JpaConstants.PARAM_EXPORT_POLL_STATUS_JOB_ID, new StringType(jobId)); // Initiate Export Poll Status - HttpPost post = new HttpPost(myServer.getBaseUrl() + "/" + JpaConstants.OPERATION_EXPORT_POLL_STATUS); + HttpPost post = new HttpPost(myServer.getBaseUrl() + "/" + ProviderConstants.OPERATION_EXPORT_POLL_STATUS); post.addHeader(Constants.HEADER_PREFER, Constants.HEADER_PREFER_RESPOND_ASYNC); post.setEntity(new ResourceEntity(myCtx, input)); @@ -1130,7 +1131,7 @@ public class BulkDataExportProviderTest { } // Initiate Export Poll Status - HttpPost post = new HttpPost(baseUrl + "/" + JpaConstants.OPERATION_EXPORT_POLL_STATUS); + HttpPost post = new HttpPost(baseUrl + "/" + ProviderConstants.OPERATION_EXPORT_POLL_STATUS); post.addHeader(Constants.HEADER_PREFER, Constants.HEADER_PREFER_RESPOND_ASYNC); post.setEntity(new ResourceEntity(myCtx, input)); @@ -1148,7 +1149,7 @@ public class BulkDataExportProviderTest { input.addParameter(JpaConstants.PARAM_EXPORT_OUTPUT_FORMAT, new StringType(ca.uhn.fhir.rest.api.Constants.CT_FHIR_NDJSON)); // Initiate Export Poll Status - HttpPost post = new HttpPost(myServer.getBaseUrl() + "/" + JpaConstants.OPERATION_EXPORT_POLL_STATUS); + HttpPost post = new HttpPost(myServer.getBaseUrl() + "/" + ProviderConstants.OPERATION_EXPORT_POLL_STATUS); post.addHeader(Constants.HEADER_PREFER, Constants.HEADER_PREFER_RESPOND_ASYNC); post.setEntity(new ResourceEntity(myCtx, input)); @@ -1160,7 +1161,7 @@ public class BulkDataExportProviderTest { private void callExportAndAssertJobId(Parameters input, String theExpectedJobId) throws IOException { HttpPost post; - post = new HttpPost(myServer.getBaseUrl() + "/" + JpaConstants.OPERATION_EXPORT); + post = new HttpPost(myServer.getBaseUrl() + "/" + ProviderConstants.OPERATION_EXPORT); post.addHeader(Constants.HEADER_PREFER, Constants.HEADER_PREFER_RESPOND_ASYNC); post.addHeader(Constants.HEADER_CACHE_CONTROL, Constants.CACHE_CONTROL_NO_CACHE); post.setEntity(new ResourceEntity(myCtx, input)); @@ -1180,7 +1181,7 @@ public class BulkDataExportProviderTest { enablePartitioning(); // test - String url = myServer.getBaseUrl() + "/Partition-B/" + JpaConstants.OPERATION_EXPORT + String url = myServer.getBaseUrl() + "/Partition-B/" + ProviderConstants.OPERATION_EXPORT + "?" + JpaConstants.PARAM_EXPORT_OUTPUT_FORMAT + "=" + UrlUtil.escapeUrlParam(Constants.CT_FHIR_NDJSON) + "&" + JpaConstants.PARAM_EXPORT_TYPE + "=" + UrlUtil.escapeUrlParam("Patient, Practitioner"); @@ -1214,7 +1215,7 @@ public class BulkDataExportProviderTest { .thenReturn(info); // test - String url = myServer.getBaseUrl() + "/Partition-B/" + JpaConstants.OPERATION_EXPORT_POLL_STATUS + "?" + + String url = myServer.getBaseUrl() + "/Partition-B/" + ProviderConstants.OPERATION_EXPORT_POLL_STATUS + "?" + JpaConstants.PARAM_EXPORT_POLL_STATUS_JOB_ID + "=" + A_JOB_ID; HttpGet get = new HttpGet(url); get.addHeader(Constants.HEADER_PREFER, Constants.HEADER_PREFER_RESPOND_ASYNC); diff --git a/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/bulk/BulkDataExportTest.java b/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/bulk/BulkDataExportTest.java index d6224766797..b11c35d5f0f 100644 --- a/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/bulk/BulkDataExportTest.java +++ b/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/bulk/BulkDataExportTest.java @@ -12,13 +12,18 @@ import ca.uhn.fhir.jpa.api.model.BulkExportJobResults; import ca.uhn.fhir.jpa.batch.models.Batch2JobStartResponse; import ca.uhn.fhir.jpa.model.util.JpaConstants; import ca.uhn.fhir.jpa.provider.BaseResourceProviderR4Test; +import ca.uhn.fhir.jpa.searchparam.SearchParameterMap; import ca.uhn.fhir.rest.api.Constants; import ca.uhn.fhir.rest.api.RequestTypeEnum; +import ca.uhn.fhir.rest.api.server.IBundleProvider; import ca.uhn.fhir.rest.api.server.RequestDetails; import ca.uhn.fhir.rest.api.server.SystemRequestDetails; import ca.uhn.fhir.rest.api.server.bulk.BulkExportJobParameters; import ca.uhn.fhir.rest.client.apache.ResourceEntity; +import ca.uhn.fhir.rest.param.StringParam; +import ca.uhn.fhir.rest.param.TokenParam; import ca.uhn.fhir.rest.server.exceptions.InvalidRequestException; +import ca.uhn.fhir.rest.server.provider.ProviderConstants; import ca.uhn.fhir.test.utilities.HttpClientExtension; import ca.uhn.fhir.util.Batch2JobDefinitionConstants; import ca.uhn.fhir.util.JsonUtil; @@ -49,6 +54,7 @@ import org.hl7.fhir.r4.model.Practitioner; import org.hl7.fhir.r4.model.Provenance; import org.hl7.fhir.r4.model.QuestionnaireResponse; import org.hl7.fhir.r4.model.Reference; +import org.hl7.fhir.r4.model.SearchParameter; import org.hl7.fhir.r4.model.ServiceRequest; import org.hl7.fhir.r4.model.StringType; import org.junit.jupiter.api.AfterEach; @@ -64,7 +70,7 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; -import javax.annotation.Nonnull; +import jakarta.annotation.Nonnull; import java.io.IOException; import java.io.StringReader; import java.util.ArrayList; @@ -148,6 +154,99 @@ public class BulkDataExportTest extends BaseResourceProviderR4Test { verifyBulkExportResults(options, Collections.singletonList("Patient/PF"), Collections.singletonList("Patient/PM")); } + @Test + public void testGroupBulkExportWithMissingObservationSearchParams() { + mySearchParameterDao.update(createDisabledObservationPatientSearchParameter(), mySrd); + mySearchParamRegistry.forceRefresh(); + + // Create some resources + Patient patient = new Patient(); + patient.setId("PF"); + patient.setGender(Enumerations.AdministrativeGender.FEMALE); + patient.setActive(true); + myClient.update().resource(patient).execute(); + + patient = new Patient(); + patient.setId("PM"); + patient.setGender(Enumerations.AdministrativeGender.MALE); + patient.setActive(true); + myClient.update().resource(patient).execute(); + + Group group = new Group(); + group.setId("Group/G"); + group.setActive(true); + group.addMember().getEntity().setReference("Patient/PF"); + group.addMember().getEntity().setReference("Patient/PM"); + myClient.update().resource(group).execute(); + + Observation observation = new Observation(); + observation.setStatus(Observation.ObservationStatus.AMENDED); + observation.setSubject(new Reference("Patient/PF")); + String obsId = myClient.create().resource(observation).execute().getId().toUnqualifiedVersionless().getValue(); + + // set the export options + BulkExportJobParameters options = new BulkExportJobParameters(); + options.setResourceTypes(Sets.newHashSet("Patient", "Observation")); + options.setGroupId("Group/G"); + options.setExportStyle(BulkExportJobParameters.ExportStyle.GROUP); + options.setOutputFormat(Constants.CT_FHIR_NDJSON); + verifyBulkExportResults(options, List.of("Patient/PF", "Patient/PM"), Collections.singletonList(obsId)); + } + + @Test + public void testGroupBulkExportWithMissingPatientSearchParams() { + mySearchParameterDao.update(createDisabledPatientPractitionerSearchParameter(), mySrd); + mySearchParamRegistry.forceRefresh(); + + Practitioner practitioner = new Practitioner(); + practitioner.setActive(true); + String practId = myClient.create().resource(practitioner).execute().getId().toUnqualifiedVersionless().getValue(); + + // Create some resources + Patient patient = new Patient(); + patient.setId("P1"); + patient.setActive(true); + patient.addGeneralPractitioner().setReference(practId); + myClient.update().resource(patient).execute(); + + Group group = new Group(); + group.setId("Group/G"); + group.setActive(true); + group.addMember().getEntity().setReference("Patient/P1"); + myClient.update().resource(group).execute(); + + // set the export options + BulkExportJobParameters options = new BulkExportJobParameters(); + options.setResourceTypes(Sets.newHashSet("Patient", "Practitioner")); + options.setGroupId("Group/G"); + options.setExportStyle(BulkExportJobParameters.ExportStyle.GROUP); + options.setOutputFormat(Constants.CT_FHIR_NDJSON); + verifyBulkExportResults(options, Collections.singletonList("Patient/P1"), Collections.singletonList(practId)); + } + + private SearchParameter createDisabledObservationPatientSearchParameter() { + SearchParameter observation_patient = new SearchParameter(); + observation_patient.setId("clinical-patient"); + observation_patient.addBase("Observation"); + observation_patient.setStatus(Enumerations.PublicationStatus.RETIRED); + observation_patient.setCode("patient"); + observation_patient.setType(Enumerations.SearchParamType.REFERENCE); + observation_patient.addTarget("Patient"); + observation_patient.setExpression("Observation.subject.where(resolve() is Patient)"); + return observation_patient; + } + + private SearchParameter createDisabledPatientPractitionerSearchParameter() { + SearchParameter patient_practitioner = new SearchParameter(); + patient_practitioner.setId("Patient-general-practitioner"); + patient_practitioner.addBase("Patient"); + patient_practitioner.setStatus(Enumerations.PublicationStatus.RETIRED); + patient_practitioner.setCode("general-practitioner"); + patient_practitioner.setType(Enumerations.SearchParamType.REFERENCE); + patient_practitioner.addTarget("Practitioner"); + patient_practitioner.setExpression("Patient.generalPractitioner"); + return patient_practitioner; + } @Test public void testGroupBulkExportWithTypeFilter_OnTags_InlineTagMode() { @@ -445,12 +544,12 @@ public class BulkDataExportTest extends BaseResourceProviderR4Test { // set the export options BulkExportJobParameters options = new BulkExportJobParameters(); - options.setResourceTypes(Sets.newHashSet("Patient", "Encounter")); + options.setResourceTypes(Sets.newHashSet("Patient", "Encounter", "Practitioner", "Organization")); options.setGroupId("Group/G1"); options.setFilters(new HashSet<>()); options.setExportStyle(BulkExportJobParameters.ExportStyle.GROUP); options.setOutputFormat(Constants.CT_FHIR_NDJSON); - verifyBulkExportResults(options, List.of("Patient/P1", practId, orgId, encId, encId2, locId), List.of("Patient/P2", orgId2, encId3, locId2)); + verifyBulkExportResults(options, List.of("Patient/P1", practId, orgId, encId, encId2), List.of("Patient/P2", orgId2, encId3)); } @Test @@ -486,7 +585,7 @@ public class BulkDataExportTest extends BaseResourceProviderR4Test { // set the export options BulkExportJobParameters options = new BulkExportJobParameters(); - options.setResourceTypes(Sets.newHashSet("Patient", "Encounter", "Observation")); + options.setResourceTypes(Sets.newHashSet("Patient", "Encounter", "Observation", "Practitioner")); options.setGroupId("Group/G1"); options.setFilters(new HashSet<>()); options.setExportStyle(BulkExportJobParameters.ExportStyle.GROUP); @@ -543,7 +642,7 @@ public class BulkDataExportTest extends BaseResourceProviderR4Test { // set the export options BulkExportJobParameters options = new BulkExportJobParameters(); - options.setResourceTypes(Sets.newHashSet("Patient", "Observation", "Provenance")); + options.setResourceTypes(Sets.newHashSet("Patient", "Observation", "Provenance", "Device")); options.setGroupId("Group/G1"); options.setFilters(new HashSet<>()); options.setExportStyle(BulkExportJobParameters.ExportStyle.GROUP); @@ -618,11 +717,11 @@ public class BulkDataExportTest extends BaseResourceProviderR4Test { input.addParameter(JpaConstants.PARAM_EXPORT_OUTPUT_FORMAT, new StringType(Constants.CT_FHIR_NDJSON)); input.addParameter(JpaConstants.PARAM_EXPORT_TYPE, new StringType("Patient")); - HttpPost post = new HttpPost(myServer.getBaseUrl() + "/" + JpaConstants.OPERATION_EXPORT); + HttpPost post = new HttpPost(myServer.getBaseUrl() + "/" + ProviderConstants.OPERATION_EXPORT); post.addHeader(Constants.HEADER_PREFER, Constants.HEADER_PREFER_RESPOND_ASYNC); post.setEntity(new ResourceEntity(myCtx, input)); - HttpGet get = new HttpGet(myServer.getBaseUrl() + "/" + JpaConstants.OPERATION_EXPORT + "?_outputFormat=application%2Ffhir%2Bndjson&_type=Patient"); + HttpGet get = new HttpGet(myServer.getBaseUrl() + "/" + ProviderConstants.OPERATION_EXPORT + "?_outputFormat=application%2Ffhir%2Bndjson&_type=Patient"); get.addHeader(Constants.HEADER_PREFER, Constants.HEADER_PREFER_RESPOND_ASYNC); try(CloseableHttpResponse postResponse = mySender.execute(post)){ ourLog.info("Response: {}",postResponse); @@ -943,10 +1042,7 @@ public class BulkDataExportTest extends BaseResourceProviderR4Test { } catch (IOException e) { fail(e.toString()); } - } - - return jobInstance; } for (String containedString : theContainedList) { diff --git a/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/bulk/BulkExportUseCaseTest.java b/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/bulk/BulkExportUseCaseTest.java index d4119ef70e9..e1f05e2de11 100644 --- a/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/bulk/BulkExportUseCaseTest.java +++ b/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/bulk/BulkExportUseCaseTest.java @@ -23,6 +23,7 @@ import ca.uhn.fhir.rest.api.MethodOutcome; import ca.uhn.fhir.rest.api.server.RequestDetails; import ca.uhn.fhir.rest.api.server.SystemRequestDetails; import ca.uhn.fhir.rest.api.server.bulk.BulkExportJobParameters; +import ca.uhn.fhir.rest.server.provider.ProviderConstants; import ca.uhn.fhir.util.Batch2JobDefinitionConstants; import ca.uhn.fhir.util.BundleBuilder; import ca.uhn.fhir.util.JsonUtil; @@ -60,7 +61,7 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; -import javax.annotation.Nonnull; +import jakarta.annotation.Nonnull; import java.io.IOException; import java.nio.charset.StandardCharsets; import java.util.ArrayList; @@ -1452,7 +1453,7 @@ public class BulkExportUseCaseTest extends BaseResourceProviderR4Test { outcome = myClient .operation() .onInstance("Group/" + theGroupOrPatientId) - .named(JpaConstants.OPERATION_EXPORT) + .named(ProviderConstants.OPERATION_EXPORT) .withParameters(parameters) .returnMethodOutcome() .withAdditionalHeader(Constants.HEADER_PREFER, Constants.HEADER_PREFER_RESPOND_ASYNC) @@ -1463,7 +1464,7 @@ public class BulkExportUseCaseTest extends BaseResourceProviderR4Test { outcome = myClient .operation() .onInstance("Patient/" + theGroupOrPatientId) - .named(JpaConstants.OPERATION_EXPORT) + .named(ProviderConstants.OPERATION_EXPORT) .withParameters(parameters) .returnMethodOutcome() .withAdditionalHeader(Constants.HEADER_PREFER, Constants.HEADER_PREFER_RESPOND_ASYNC) @@ -1473,7 +1474,7 @@ public class BulkExportUseCaseTest extends BaseResourceProviderR4Test { outcome = myClient .operation() .onServer() - .named(JpaConstants.OPERATION_EXPORT) + .named(ProviderConstants.OPERATION_EXPORT) .withParameters(parameters) .returnMethodOutcome() .withAdditionalHeader(Constants.HEADER_PREFER, Constants.HEADER_PREFER_RESPOND_ASYNC) diff --git a/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/bulk/imprt/svc/BulkDataImportR4Test.java b/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/bulk/imprt/svc/BulkDataImportR4Test.java index c7c7bb69969..405d6b2bc5b 100644 --- a/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/bulk/imprt/svc/BulkDataImportR4Test.java +++ b/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/bulk/imprt/svc/BulkDataImportR4Test.java @@ -44,7 +44,7 @@ import org.springframework.messaging.MessageChannel; import org.springframework.messaging.MessageHandler; import org.springframework.messaging.support.ExecutorChannelInterceptor; -import javax.annotation.Nonnull; +import jakarta.annotation.Nonnull; import java.util.ArrayList; import java.util.HashSet; import java.util.List; diff --git a/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/bulk/imprt2/ConsumeFilesStepR4Test.java b/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/bulk/imprt2/ConsumeFilesStepR4Test.java index 58562b52960..5462f896a3c 100644 --- a/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/bulk/imprt2/ConsumeFilesStepR4Test.java +++ b/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/bulk/imprt2/ConsumeFilesStepR4Test.java @@ -41,14 +41,12 @@ public class ConsumeFilesStepR4Test extends BasePartitioningR4Test { public void before() throws Exception { super.before(); myPartitionSettings.setPartitioningEnabled(false); - myStorageSettings.setInlineResourceTextBelowSize(10000); } @AfterEach @Override public void after() { super.after(); - myStorageSettings.setInlineResourceTextBelowSize(new JpaStorageSettings().getInlineResourceTextBelowSize()); } @Test @@ -190,8 +188,8 @@ public class ConsumeFilesStepR4Test extends BasePartitioningR4Test { assertEquals(1, myCaptureQueriesListener.logSelectQueries().size()); assertThat(myCaptureQueriesListener.getSelectQueries().get(0).getSql(true, false), - either(containsString("forcedid0_.RESOURCE_TYPE='Patient' and forcedid0_.FORCED_ID='B' and (forcedid0_.PARTITION_ID is null) or forcedid0_.RESOURCE_TYPE='Patient' and forcedid0_.FORCED_ID='A' and (forcedid0_.PARTITION_ID is null)")) - .or(containsString("forcedid0_.RESOURCE_TYPE='Patient' and forcedid0_.FORCED_ID='A' and (forcedid0_.PARTITION_ID is null) or forcedid0_.RESOURCE_TYPE='Patient' and forcedid0_.FORCED_ID='B' and (forcedid0_.PARTITION_ID is null)"))); + either(containsString("rt1_0.RES_TYPE='Patient' and rt1_0.FHIR_ID='B' and rt1_0.PARTITION_ID is null or rt1_0.RES_TYPE='Patient' and rt1_0.FHIR_ID='A' and rt1_0.PARTITION_ID is null")) + .or(containsString("rt1_0.RES_TYPE='Patient' and rt1_0.FHIR_ID='A' and rt1_0.PARTITION_ID is null or rt1_0.RES_TYPE='Patient' and rt1_0.FHIR_ID='B' and rt1_0.PARTITION_ID is null"))); assertEquals(52, myCaptureQueriesListener.countInsertQueriesForCurrentThread()); assertEquals(0, myCaptureQueriesListener.countUpdateQueriesForCurrentThread()); assertEquals(0, myCaptureQueriesListener.countDeleteQueriesForCurrentThread()); diff --git a/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/dao/BaseHapiFhirDaoTest.java b/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/dao/BaseHapiFhirDaoTest.java index c05ea3cacf1..088b7504609 100644 --- a/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/dao/BaseHapiFhirDaoTest.java +++ b/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/dao/BaseHapiFhirDaoTest.java @@ -29,16 +29,16 @@ import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor; import org.springframework.transaction.PlatformTransactionManager; import org.springframework.transaction.support.TransactionSynchronizationManager; -import javax.annotation.Nullable; -import javax.persistence.EntityExistsException; -import javax.persistence.EntityManager; -import javax.persistence.NoResultException; -import javax.persistence.TypedQuery; -import javax.persistence.criteria.CriteriaBuilder; -import javax.persistence.criteria.CriteriaQuery; -import javax.persistence.criteria.Path; -import javax.persistence.criteria.Predicate; -import javax.persistence.criteria.Root; +import jakarta.annotation.Nullable; +import jakarta.persistence.EntityExistsException; +import jakarta.persistence.EntityManager; +import jakarta.persistence.NoResultException; +import jakarta.persistence.TypedQuery; +import jakarta.persistence.criteria.CriteriaBuilder; +import jakarta.persistence.criteria.CriteriaQuery; +import jakarta.persistence.criteria.Path; +import jakarta.persistence.criteria.Predicate; +import jakarta.persistence.criteria.Root; import java.util.ArrayList; import java.util.List; import java.util.concurrent.ConcurrentHashMap; diff --git a/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/dao/BaseHapiFhirResourceDaoTest.java b/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/dao/BaseHapiFhirResourceDaoTest.java index 98fea30536f..6aff8e400a7 100644 --- a/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/dao/BaseHapiFhirResourceDaoTest.java +++ b/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/dao/BaseHapiFhirResourceDaoTest.java @@ -44,9 +44,7 @@ import org.mockito.Mockito; import org.mockito.junit.jupiter.MockitoExtension; import org.springframework.context.ApplicationContext; -import javax.persistence.EntityManager; -import java.util.ArrayList; -import java.util.Collection; +import jakarta.persistence.EntityManager; import java.util.List; import java.util.stream.Stream; @@ -67,7 +65,7 @@ class BaseHapiFhirResourceDaoTest { private IRequestPartitionHelperSvc myRequestPartitionHelperSvc; @Mock - private IIdHelperService myIdHelperService; + private IIdHelperService myIdHelperService; @Mock private EntityManager myEntityManager; @@ -88,10 +86,10 @@ class BaseHapiFhirResourceDaoTest { private ISearchParamRegistry mySearchParamRegistry; @Mock - private SearchBuilderFactory mySearchBuilderFactory; + private SearchBuilderFactory mySearchBuilderFactory; @Mock - private ISearchBuilder myISearchBuilder; + private ISearchBuilder myISearchBuilder; @Captor private ArgumentCaptor mySearchParameterMapCaptor; diff --git a/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/dao/JpaPersistedResourceValidationSupportFromValidationChainTest.java b/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/dao/JpaPersistedResourceValidationSupportFromValidationChainTest.java index 995270607d2..599d026abec 100644 --- a/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/dao/JpaPersistedResourceValidationSupportFromValidationChainTest.java +++ b/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/dao/JpaPersistedResourceValidationSupportFromValidationChainTest.java @@ -26,7 +26,7 @@ import org.mockito.Mockito; import org.mockito.junit.jupiter.MockitoExtension; import org.springframework.test.util.ReflectionTestUtils; -import javax.annotation.Nonnull; +import jakarta.annotation.Nonnull; import java.util.function.Predicate; import static ca.uhn.fhir.util.ClasspathUtil.loadResource; diff --git a/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/dao/TransactionProcessorTest.java b/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/dao/TransactionProcessorTest.java index 6bc21dff961..18237f46d50 100644 --- a/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/dao/TransactionProcessorTest.java +++ b/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/dao/TransactionProcessorTest.java @@ -38,8 +38,8 @@ import org.springframework.test.context.ContextConfiguration; import org.springframework.test.context.junit.jupiter.SpringExtension; import org.springframework.transaction.PlatformTransactionManager; -import javax.persistence.EntityManager; -import javax.persistence.EntityManagerFactory; +import jakarta.persistence.EntityManager; +import jakarta.persistence.EntityManagerFactory; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.fail; diff --git a/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/dao/expunge/ResourceTableFKProviderTest.java b/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/dao/expunge/ResourceTableFKProviderTest.java index c62a069d4c8..558555ad570 100644 --- a/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/dao/expunge/ResourceTableFKProviderTest.java +++ b/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/dao/expunge/ResourceTableFKProviderTest.java @@ -6,9 +6,9 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; -import javax.persistence.EntityManager; -import javax.persistence.PersistenceContext; -import javax.persistence.PersistenceContextType; +import jakarta.persistence.EntityManager; +import jakarta.persistence.PersistenceContext; +import jakarta.persistence.PersistenceContextType; import javax.sql.DataSource; import java.sql.Connection; import java.sql.DatabaseMetaData; diff --git a/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/dao/index/IdHelperServiceTest.java b/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/dao/index/IdHelperServiceTest.java index fd986e6745b..7401eab2982 100644 --- a/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/dao/index/IdHelperServiceTest.java +++ b/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/dao/index/IdHelperServiceTest.java @@ -2,7 +2,7 @@ package ca.uhn.fhir.jpa.dao.index; import ca.uhn.fhir.interceptor.model.RequestPartitionId; import ca.uhn.fhir.jpa.api.config.JpaStorageSettings; -import ca.uhn.fhir.jpa.dao.data.IForcedIdDao; +import ca.uhn.fhir.jpa.dao.data.IResourceTableDao; import ca.uhn.fhir.jpa.model.config.PartitionSettings; import ca.uhn.fhir.jpa.model.cross.IResourceLookup; import ca.uhn.fhir.jpa.model.dao.JpaPid; @@ -41,7 +41,7 @@ public class IdHelperServiceTest { private JpaStorageSettings myStorageSettings; @Mock - private IForcedIdDao myForcedIdDao; + private IResourceTableDao myResourceTableDao; @Mock private MemoryCacheService myMemoryCacheService; @@ -100,7 +100,7 @@ public class IdHelperServiceTest { // when when(myStorageSettings.isDeleteEnabled()) .thenReturn(true); - when(myForcedIdDao.findAndResolveByForcedIdWithNoType(Mockito.anyString(), + when(myResourceTableDao.findAndResolveByForcedIdWithNoType(Mockito.anyString(), Mockito.anyList(), Mockito.anyBoolean())) .thenReturn(Collections.singletonList(redView)) .thenReturn(Collections.singletonList(blueView)); @@ -164,7 +164,7 @@ public class IdHelperServiceTest { Collection testForcedIdViews = new ArrayList<>(); testForcedIdViews.add(forcedIdView); - when(myForcedIdDao.findAndResolveByForcedIdWithNoTypeInPartition(any(), any(), any(), anyBoolean())).thenReturn(testForcedIdViews); + when(myResourceTableDao.findAndResolveByForcedIdWithNoTypeInPartition(any(), any(), any(), anyBoolean())).thenReturn(testForcedIdViews); IResourceLookup result = myHelperService.resolveResourceIdentity(partitionId, resourceType, resourceForcedId); assertEquals(forcedIdView[0], result.getResourceType()); diff --git a/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/dao/r4/BaseJpaR4SystemTest.java b/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/dao/r4/BaseJpaR4SystemTest.java index 438667dfbb3..404cc2a639d 100644 --- a/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/dao/r4/BaseJpaR4SystemTest.java +++ b/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/dao/r4/BaseJpaR4SystemTest.java @@ -5,8 +5,8 @@ import ca.uhn.fhir.jpa.test.BaseJpaR4Test; import ca.uhn.fhir.rest.server.RestfulServer; import org.junit.jupiter.api.BeforeEach; -import javax.servlet.ServletConfig; -import javax.servlet.http.HttpServletRequest; +import jakarta.servlet.ServletConfig; +import jakarta.servlet.http.HttpServletRequest; import java.util.Enumeration; import static org.mockito.Mockito.mock; diff --git a/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/dao/r4/BasePartitioningR4Test.java b/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/dao/r4/BasePartitioningR4Test.java index d37069791dc..e91e3421bf2 100644 --- a/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/dao/r4/BasePartitioningR4Test.java +++ b/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/dao/r4/BasePartitioningR4Test.java @@ -8,6 +8,7 @@ import ca.uhn.fhir.jpa.api.config.JpaStorageSettings; import ca.uhn.fhir.jpa.entity.PartitionEntity; import ca.uhn.fhir.jpa.model.config.PartitionSettings; import ca.uhn.fhir.jpa.model.entity.ForcedId; +import ca.uhn.fhir.jpa.model.entity.ResourceTable; import ca.uhn.fhir.jpa.model.util.JpaConstants; import ca.uhn.fhir.jpa.partition.IPartitionLookupSvc; import ca.uhn.fhir.jpa.searchparam.SearchParameterMap; @@ -137,6 +138,7 @@ public abstract class BasePartitioningR4Test extends BaseJpaR4SystemTest { protected void dropForcedIdUniqueConstraint() { runInTransaction(() -> { myEntityManager.createNativeQuery("alter table " + ForcedId.HFJ_FORCED_ID + " drop constraint " + ForcedId.IDX_FORCEDID_TYPE_FID).executeUpdate(); + myEntityManager.createNativeQuery("alter table " + ResourceTable.HFJ_RESOURCE + " drop constraint " + ResourceTable.IDX_RES_TYPE_FHIR_ID).executeUpdate(); }); myHaveDroppedForcedIdUniqueConstraint = true; } diff --git a/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/dao/r4/ConsentEventsDaoR4Test.java b/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/dao/r4/ConsentEventsDaoR4Test.java index 0c1e49496f6..b5493dcebd5 100644 --- a/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/dao/r4/ConsentEventsDaoR4Test.java +++ b/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/dao/r4/ConsentEventsDaoR4Test.java @@ -29,11 +29,11 @@ import org.slf4j.LoggerFactory; import org.springframework.test.context.ContextConfiguration; import org.springframework.test.context.junit.jupiter.SpringExtension; -import javax.servlet.ServletException; import java.util.ArrayList; import java.util.Arrays; import java.util.List; import java.util.concurrent.atomic.AtomicInteger; +import java.util.function.Function; import java.util.stream.Collectors; import static org.apache.commons.lang3.StringUtils.leftPad; @@ -54,7 +54,7 @@ public class ConsentEventsDaoR4Test extends BaseJpaR4SystemTest { private List myPatientIds; private List myObservationIdsOddOnly; private List myObservationIdsEvenOnly; - private List myObservationIdsWithVersions; + private List myObservationIdsWithoutVersions; private List myPatientIdsEvenOnly; @AfterEach @@ -64,13 +64,16 @@ public class ConsentEventsDaoR4Test extends BaseJpaR4SystemTest { } @BeforeEach - public void before() throws ServletException { + @Override + public void beforeInitMocks() throws Exception { + super.beforeInitMocks(); RestfulServer restfulServer = new RestfulServer(); restfulServer.setPagingProvider(myPagingProvider); when(mySrd.getServer()).thenReturn(restfulServer); myStorageSettings.setSearchPreFetchThresholds(Arrays.asList(20, 50, 190)); + restfulServer.setDefaultPageSize(null); } @Test @@ -147,6 +150,7 @@ public class ConsentEventsDaoR4Test extends BaseJpaR4SystemTest { @Test public void testSearchAndBlockSome_LoadSynchronous() { + // setup create50Observations(); AtomicInteger hitCount = new AtomicInteger(0); @@ -281,6 +285,7 @@ public class ConsentEventsDaoR4Test extends BaseJpaR4SystemTest { @Test public void testSearchAndBlockSomeOnIncludes_LoadSynchronous() { + // setup create50Observations(); AtomicInteger hitCount = new AtomicInteger(0); @@ -328,9 +333,8 @@ public class ConsentEventsDaoR4Test extends BaseJpaR4SystemTest { * returned results because we create it then update it in create50Observations() */ assertEquals(1, hitCount.get()); - assertEquals(myObservationIdsWithVersions.subList(90, myObservationIdsWithVersions.size()), sort(interceptedResourceIds)); + assertEquals(sort(myObservationIdsWithoutVersions.subList(90, myObservationIdsWithoutVersions.size())), sort(interceptedResourceIds)); returnedIdValues.forEach(t -> assertTrue(new IdType(t).getIdPartAsLong() % 2 == 0)); - } @Test @@ -363,7 +367,7 @@ public class ConsentEventsDaoR4Test extends BaseJpaR4SystemTest { private void create50Observations() { myPatientIds = new ArrayList<>(); myObservationIds = new ArrayList<>(); - myObservationIdsWithVersions = new ArrayList<>(); + myObservationIdsWithoutVersions = new ArrayList<>(); Patient p = new Patient(); p.setActive(true); @@ -383,9 +387,9 @@ public class ConsentEventsDaoR4Test extends BaseJpaR4SystemTest { final Observation obs1 = new Observation(); obs1.setStatus(Observation.ObservationStatus.FINAL); obs1.addIdentifier().setSystem("urn:system").setValue("I" + leftPad("" + i, 5, '0')); - IIdType obs1id = myObservationDao.create(obs1).getId().toUnqualifiedVersionless(); + IIdType obs1id = myObservationDao.create(obs1).getId(); myObservationIds.add(obs1id.toUnqualifiedVersionless().getValue()); - myObservationIdsWithVersions.add(obs1id.toUnqualifiedVersionless().getValue()); + myObservationIdsWithoutVersions.add(obs1id.toUnqualifiedVersionless().getValue()); obs1.setId(obs1id); if (obs1id.getIdPartAsLong() % 2 == 0) { @@ -394,7 +398,7 @@ public class ConsentEventsDaoR4Test extends BaseJpaR4SystemTest { obs1.getSubject().setReference(oddPid); } myObservationDao.update(obs1); - myObservationIdsWithVersions.add(obs1id.toUnqualifiedVersionless().getValue()); + myObservationIdsWithoutVersions.add(obs1id.toUnqualifiedVersionless().getValue()); } @@ -483,14 +487,24 @@ public class ConsentEventsDaoR4Test extends BaseJpaR4SystemTest { } } - private static List sort(List... theLists) { + private List sort(List... theLists) { + return sort(id -> { + String idParsed = id.substring(id.indexOf("/") + 1); + if (idParsed.contains("/_history")) { + idParsed = idParsed.substring(0, idParsed.indexOf("/")); + } + return Long.parseLong(idParsed); + }, theLists); + } + + private List sort(Function theParser, List... theLists) { ArrayList retVal = new ArrayList<>(); for (List next : theLists) { retVal.addAll(next); } retVal.sort((o0, o1) -> { - long i0 = Long.parseLong(o0.substring(o0.indexOf('/') + 1)); - long i1 = Long.parseLong(o1.substring(o1.indexOf('/') + 1)); + long i0 = theParser.apply(o0); + long i1 = theParser.apply(o1); return (int) (i0 - i1); }); return retVal; diff --git a/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/dao/r4/FhirResourceDaoR4ComboUniqueParamIT.java b/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/dao/r4/FhirResourceDaoR4ComboUniqueParamIT.java index 65dec5ee11c..06d8176a729 100644 --- a/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/dao/r4/FhirResourceDaoR4ComboUniqueParamIT.java +++ b/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/dao/r4/FhirResourceDaoR4ComboUniqueParamIT.java @@ -44,7 +44,7 @@ import org.springframework.transaction.TransactionStatus; import org.springframework.transaction.support.TransactionCallbackWithoutResult; import org.springframework.transaction.support.TransactionTemplate; -import javax.annotation.Nonnull; +import jakarta.annotation.Nonnull; import java.util.Collections; import java.util.List; import java.util.UUID; diff --git a/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/dao/r4/FhirResourceDaoR4ConceptMapTest.java b/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/dao/r4/FhirResourceDaoR4ConceptMapTest.java index 8e51cf917d6..da57450eb99 100644 --- a/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/dao/r4/FhirResourceDaoR4ConceptMapTest.java +++ b/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/dao/r4/FhirResourceDaoR4ConceptMapTest.java @@ -29,7 +29,7 @@ import org.springframework.transaction.annotation.Transactional; import org.springframework.transaction.support.TransactionCallbackWithoutResult; import org.springframework.transaction.support.TransactionTemplate; -import javax.annotation.Nonnull; +import jakarta.annotation.Nonnull; import java.io.IOException; import java.util.Collections; import java.util.HashSet; diff --git a/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/dao/r4/FhirResourceDaoR4ConcurrentWriteTest.java b/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/dao/r4/FhirResourceDaoR4ConcurrentWriteTest.java index 6756c8c0472..f2c80276000 100644 --- a/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/dao/r4/FhirResourceDaoR4ConcurrentWriteTest.java +++ b/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/dao/r4/FhirResourceDaoR4ConcurrentWriteTest.java @@ -40,7 +40,7 @@ import org.junit.jupiter.api.Test; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.test.annotation.DirtiesContext; -import javax.annotation.Nonnull; +import jakarta.annotation.Nonnull; import java.util.ArrayList; import java.util.Collection; import java.util.Collections; diff --git a/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/dao/r4/FhirResourceDaoR4CreateTest.java b/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/dao/r4/FhirResourceDaoR4CreateTest.java index 1fe09812342..db30329cbad 100644 --- a/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/dao/r4/FhirResourceDaoR4CreateTest.java +++ b/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/dao/r4/FhirResourceDaoR4CreateTest.java @@ -91,7 +91,6 @@ public class FhirResourceDaoR4CreateTest extends BaseJpaR4Test { myStorageSettings.setNormalizedQuantitySearchLevel(NormalizedQuantitySearchLevel.NORMALIZED_QUANTITY_SEARCH_NOT_SUPPORTED); myStorageSettings.setIndexOnContainedResources(new JpaStorageSettings().isIndexOnContainedResources()); myStorageSettings.setIndexOnContainedResourcesRecursively(new JpaStorageSettings().isIndexOnContainedResourcesRecursively()); - myStorageSettings.setInlineResourceTextBelowSize(new JpaStorageSettings().getInlineResourceTextBelowSize()); } @Test diff --git a/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/dao/r4/FhirResourceDaoR4HistoryRewriteTest.java b/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/dao/r4/FhirResourceDaoR4HistoryRewriteTest.java index 98d6761fa83..fdce11ad76c 100644 --- a/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/dao/r4/FhirResourceDaoR4HistoryRewriteTest.java +++ b/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/dao/r4/FhirResourceDaoR4HistoryRewriteTest.java @@ -14,7 +14,7 @@ import org.junit.jupiter.api.Test; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import javax.annotation.Nonnull; +import jakarta.annotation.Nonnull; import java.util.Date; import java.util.List; diff --git a/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/dao/r4/FhirResourceDaoR4InlineResourceModeTest.java b/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/dao/r4/FhirResourceDaoR4InlineResourceModeTest.java index 368519202f8..b37be63bfb5 100644 --- a/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/dao/r4/FhirResourceDaoR4InlineResourceModeTest.java +++ b/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/dao/r4/FhirResourceDaoR4InlineResourceModeTest.java @@ -1,102 +1,61 @@ package ca.uhn.fhir.jpa.dao.r4; -import ca.uhn.fhir.jpa.api.config.JpaStorageSettings; -import ca.uhn.fhir.jpa.api.model.DaoMethodOutcome; +import ca.uhn.fhir.jpa.dao.GZipUtil; +import ca.uhn.fhir.jpa.dao.data.IResourceHistoryTableDao; +import ca.uhn.fhir.jpa.model.entity.ResourceEncodingEnum; import ca.uhn.fhir.jpa.model.entity.ResourceHistoryTable; import ca.uhn.fhir.jpa.searchparam.SearchParameterMap; import ca.uhn.fhir.jpa.test.BaseJpaR4Test; -import org.apache.commons.lang3.StringUtils; -import org.hl7.fhir.r4.model.IdType; +import ca.uhn.fhir.rest.param.DateRangeParam; +import ca.uhn.fhir.rest.param.HistorySearchDateRangeParam; +import org.hl7.fhir.instance.model.api.IBaseResource; +import org.hl7.fhir.instance.model.api.IIdType; import org.hl7.fhir.r4.model.Patient; -import org.junit.jupiter.api.AfterEach; -import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; +import org.springframework.beans.factory.annotation.Autowired; + +import java.util.HashMap; -import static org.hamcrest.MatcherAssert.assertThat; -import static org.hamcrest.Matchers.containsString; import static org.junit.jupiter.api.Assertions.assertEquals; -import static org.junit.jupiter.api.Assertions.assertFalse; import static org.junit.jupiter.api.Assertions.assertNotNull; import static org.junit.jupiter.api.Assertions.assertNull; import static org.junit.jupiter.api.Assertions.assertTrue; public class FhirResourceDaoR4InlineResourceModeTest extends BaseJpaR4Test { - @BeforeEach - public void beforeSetDao() { - myStorageSettings.setInlineResourceTextBelowSize(5000); - } - - @AfterEach - public void afterResetDao() { - myStorageSettings.setInlineResourceTextBelowSize(new JpaStorageSettings().getInlineResourceTextBelowSize()); - } - @Test - public void testCreateWithInlineResourceTextStorage() { - Patient patient = new Patient(); - patient.setActive(true); - Long resourceId = myPatientDao.create(patient).getId().getIdPartAsLong(); + public void testRetrieveNonInlinedResource() { + IIdType id = createPatient(withActiveTrue()); + Long pid = id.getIdPartAsLong(); - patient = new Patient(); - patient.setId("Patient/" + resourceId); - patient.setActive(false); - myPatientDao.update(patient); + relocateResourceTextToCompressedColumn(pid, 1L); - runInTransaction(() -> { - // Version 1 - ResourceHistoryTable entity = myResourceHistoryTableDao.findForIdAndVersionAndFetchProvenance(resourceId, 1); - assertNull(entity.getResource()); - assertThat(entity.getResourceTextVc(), containsString("\"active\":true")); - // Version 2 - entity = myResourceHistoryTableDao.findForIdAndVersionAndFetchProvenance(resourceId, 2); - assertNull(entity.getResource()); - assertThat(entity.getResourceTextVc(), containsString("\"active\":false")); + runInTransaction(()->{ + ResourceHistoryTable historyEntity = myResourceHistoryTableDao.findForIdAndVersionAndFetchProvenance(pid, 1); + assertNotNull(historyEntity.getResource()); + assertNull(historyEntity.getResourceTextVc()); + assertEquals(ResourceEncodingEnum.JSONC, historyEntity.getEncoding()); }); - patient = myPatientDao.read(new IdType("Patient/" + resourceId)); - assertFalse(patient.getActive()); + // Read + validatePatient(myPatientDao.read(id.withVersion(null), mySrd)); - patient = (Patient) myPatientDao.search(SearchParameterMap.newSynchronous()).getAllResources().get(0); - assertFalse(patient.getActive()); + // VRead + validatePatient(myPatientDao.read(id.withVersion("1"), mySrd)); + // Search (Sync) + validatePatient(myPatientDao.search(SearchParameterMap.newSynchronous(), mySrd).getResources(0, 1).get(0)); + + // Search (Async) + validatePatient(myPatientDao.search(new SearchParameterMap(), mySrd).getResources(0, 1).get(0)); + + // History + validatePatient(myPatientDao.history(id, new HistorySearchDateRangeParam(new HashMap<>(), new DateRangeParam(), 0), mySrd).getResources(0, 1).get(0)); } - @Test - public void testDontUseInlineAboveThreshold() { - String veryLongFamilyName = StringUtils.leftPad("", 6000, 'a'); - - Patient patient = new Patient(); - patient.setActive(true); - patient.addName().setFamily(veryLongFamilyName); - Long resourceId = myPatientDao.create(patient).getId().getIdPartAsLong(); - - runInTransaction(() -> { - // Version 1 - ResourceHistoryTable entity = myResourceHistoryTableDao.findForIdAndVersionAndFetchProvenance(resourceId, 1); - assertNotNull(entity.getResource()); - assertNull(entity.getResourceTextVc()); - }); - - patient = myPatientDao.read(new IdType("Patient/" + resourceId)); - assertEquals(veryLongFamilyName, patient.getNameFirstRep().getFamily()); - } - - - @Test - public void testNopOnUnchangedUpdate() { - Patient patient = new Patient(); - patient.setActive(true); - Long resourceId = myPatientDao.create(patient).getId().getIdPartAsLong(); - - patient = new Patient(); - patient.setId("Patient/" + resourceId); - patient.setActive(true); - DaoMethodOutcome updateOutcome = myPatientDao.update(patient); - assertEquals("1", updateOutcome.getId().getVersionIdPart()); - assertTrue(updateOutcome.isNop()); - + private void validatePatient(IBaseResource theRead) { + assertTrue(((Patient)theRead).getActive()); } diff --git a/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/dao/r4/FhirResourceDaoR4QueryCountTest.java b/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/dao/r4/FhirResourceDaoR4QueryCountTest.java index 826a7b382c3..a65e6075ca3 100644 --- a/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/dao/r4/FhirResourceDaoR4QueryCountTest.java +++ b/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/dao/r4/FhirResourceDaoR4QueryCountTest.java @@ -19,15 +19,20 @@ import ca.uhn.fhir.jpa.api.model.HistoryCountModeEnum; import ca.uhn.fhir.jpa.dao.data.ISearchParamPresentDao; import ca.uhn.fhir.jpa.entity.TermValueSet; import ca.uhn.fhir.jpa.entity.TermValueSetPreExpansionStatusEnum; +import ca.uhn.fhir.jpa.interceptor.ForceOffsetSearchModeInterceptor; import ca.uhn.fhir.jpa.model.entity.ForcedId; import ca.uhn.fhir.jpa.model.entity.ResourceTable; import ca.uhn.fhir.jpa.model.util.JpaConstants; import ca.uhn.fhir.jpa.provider.BaseResourceProviderR4Test; import ca.uhn.fhir.jpa.search.PersistedJpaSearchFirstPageBundleProvider; import ca.uhn.fhir.jpa.searchparam.SearchParameterMap; +import ca.uhn.fhir.jpa.searchparam.submit.interceptor.SearchParamValidatingInterceptor; +import ca.uhn.fhir.jpa.subscription.submit.interceptor.SubscriptionValidatingInterceptor; import ca.uhn.fhir.jpa.subscription.submit.svc.ResourceModifiedSubmitterSvc; import ca.uhn.fhir.jpa.subscription.triggering.ISubscriptionTriggeringSvc; +import ca.uhn.fhir.jpa.subscription.triggering.SubscriptionTriggeringSvcImpl; import ca.uhn.fhir.jpa.term.TermReadSvcImpl; +import ca.uhn.fhir.jpa.test.util.SubscriptionTestUtil; import ca.uhn.fhir.jpa.util.SqlQuery; import ca.uhn.fhir.rest.api.Constants; import ca.uhn.fhir.rest.api.RestOperationTypeEnum; @@ -40,9 +45,12 @@ import ca.uhn.fhir.rest.server.SimpleBundleProvider; import ca.uhn.fhir.rest.server.exceptions.PreconditionFailedException; import ca.uhn.fhir.rest.server.interceptor.auth.AuthorizationInterceptor; import ca.uhn.fhir.rest.server.interceptor.auth.PolicyEnum; +import ca.uhn.fhir.rest.server.provider.ProviderConstants; +import ca.uhn.fhir.test.utilities.ProxyUtil; import ca.uhn.fhir.test.utilities.server.HashMapResourceProviderExtension; import ca.uhn.fhir.test.utilities.server.RestfulServerExtension; import ca.uhn.fhir.util.BundleBuilder; +import org.hamcrest.CoreMatchers; import org.hl7.fhir.instance.model.api.IBaseResource; import org.hl7.fhir.instance.model.api.IIdType; import org.hl7.fhir.r4.model.BooleanType; @@ -85,7 +93,7 @@ import org.springframework.data.domain.PageRequest; import org.springframework.data.domain.Slice; import org.springframework.util.comparator.ComparableComparator; -import javax.annotation.Nonnull; +import jakarta.annotation.Nonnull; import java.io.IOException; import java.util.ArrayList; import java.util.Collections; @@ -94,18 +102,24 @@ import java.util.List; import java.util.concurrent.atomic.AtomicInteger; import java.util.function.Supplier; import java.util.stream.Collectors; +import java.util.stream.IntStream; +import static ca.uhn.fhir.jpa.subscription.FhirR4Util.createSubscription; import static org.apache.commons.lang3.StringUtils.countMatches; +import static org.awaitility.Awaitility.await; import static org.hamcrest.MatcherAssert.assertThat; import static org.hamcrest.Matchers.containsInAnyOrder; import static org.hamcrest.Matchers.containsString; import static org.hamcrest.Matchers.empty; +import static org.hamcrest.Matchers.equalTo; import static org.hamcrest.Matchers.not; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertFalse; import static org.junit.jupiter.api.Assertions.assertNotNull; +import static org.junit.jupiter.api.Assertions.assertNull; import static org.junit.jupiter.api.Assertions.assertTrue; import static org.junit.jupiter.api.Assertions.fail; +import static org.mockito.ArgumentMatchers.contains; import static org.mockito.ArgumentMatchers.eq; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.when; @@ -145,6 +159,9 @@ public class FhirResourceDaoR4QueryCountTest extends BaseResourceProviderR4Test private ReindexStep myReindexStep; @Autowired private DeleteExpungeStep myDeleteExpungeStep; + @Autowired + protected SubscriptionTestUtil mySubscriptionTestUtil; + @AfterEach public void afterResetDao() { @@ -167,6 +184,8 @@ public class FhirResourceDaoR4QueryCountTest extends BaseResourceProviderR4Test myFhirContext.getParserOptions().setStripVersionsFromReferences(true); TermReadSvcImpl.setForceDisableHibernateSearchForUnitTest(false); + + mySubscriptionTestUtil.unregisterSubscriptionInterceptor(); } @Override @@ -576,7 +595,7 @@ public class FhirResourceDaoR4QueryCountTest extends BaseResourceProviderR4Test fail(myFhirContext.newJsonParser().setPrettyPrint(true).encodeResourceToString(e.getOperationOutcome())); } myCaptureQueriesListener.logSelectQueriesForCurrentThread(); - assertEquals(11, myCaptureQueriesListener.getSelectQueriesForCurrentThread().size()); + assertEquals(12, myCaptureQueriesListener.getSelectQueriesForCurrentThread().size()); assertEquals(0, myCaptureQueriesListener.getUpdateQueriesForCurrentThread().size()); assertEquals(0, myCaptureQueriesListener.getInsertQueriesForCurrentThread().size()); assertEquals(0, myCaptureQueriesListener.getDeleteQueriesForCurrentThread().size()); @@ -1012,26 +1031,15 @@ public class FhirResourceDaoR4QueryCountTest extends BaseResourceProviderR4Test @ParameterizedTest @CsvSource({ - // NoOp OptimisticLock OptimizeMode ExpectedSelect ExpectedUpdate - " false, false, CURRENT_VERSION, 2, 1", - " true, false, CURRENT_VERSION, 2, 0", - " false, true, CURRENT_VERSION, 12, 1", - " true, true, CURRENT_VERSION, 12, 0", - " false, false, ALL_VERSIONS, 12, 10", - " true, false, ALL_VERSIONS, 12, 0", - " false, true, ALL_VERSIONS, 22, 10", - " true, true, ALL_VERSIONS, 22, 0", + // OptimisticLock OptimizeMode ExpectedSelect ExpectedUpdate + " false, CURRENT_VERSION, 2, 0", + " true, CURRENT_VERSION, 12, 0", + " false, ALL_VERSIONS, 12, 0", + " true, ALL_VERSIONS, 22, 0", }) - public void testReindexJob_OptimizeStorage(boolean theNoOp, boolean theOptimisticLock, ReindexParameters.OptimizeStorageModeEnum theOptimizeStorageModeEnum, int theExpectedSelectCount, int theExpectedUpdateCount) { + public void testReindexJob_OptimizeStorage(boolean theOptimisticLock, ReindexParameters.OptimizeStorageModeEnum theOptimizeStorageModeEnum, int theExpectedSelectCount, int theExpectedUpdateCount) { // Setup - // In no-op mode, the inlining is already in the state it needs to be in - if (theNoOp) { - myStorageSettings.setInlineResourceTextBelowSize(10000); - } else { - myStorageSettings.setInlineResourceTextBelowSize(0); - } - ResourceIdListWorkChunkJson data = new ResourceIdListWorkChunkJson(); IIdType patientId = createPatient(withActiveTrue()); for (int i = 0; i < 10; i++) { @@ -1214,6 +1222,7 @@ public class FhirResourceDaoR4QueryCountTest extends BaseResourceProviderR4Test nextChunk.forEach(t -> foundIds.add(t.getIdElement().toUnqualifiedVersionless().getValue())); } + assertEquals(ids.size(), foundIds.size()); ids.sort(new ComparableComparator<>()); foundIds.sort(new ComparableComparator<>()); assertEquals(ids, foundIds); @@ -1312,7 +1321,7 @@ public class FhirResourceDaoR4QueryCountTest extends BaseResourceProviderR4Test myCaptureQueriesListener.logSelectQueries(); assertEquals(2, myCaptureQueriesListener.countSelectQueries()); assertThat(myCaptureQueriesListener.getSelectQueries().get(0).getSql(true, false), containsString("SELECT t0.RES_ID FROM HFJ_SPIDX_TOKEN t0")); - assertThat(myCaptureQueriesListener.getSelectQueries().get(0).getSql(true, false), containsString("limit '5'")); + assertThat(myCaptureQueriesListener.getSelectQueries().get(0).getSql(true, false), containsString("fetch first '6'")); assertEquals(0, myCaptureQueriesListener.countInsertQueries()); assertEquals(0, myCaptureQueriesListener.countUpdateQueries()); assertEquals(0, myCaptureQueriesListener.countDeleteQueries()); @@ -1328,7 +1337,7 @@ public class FhirResourceDaoR4QueryCountTest extends BaseResourceProviderR4Test myCaptureQueriesListener.logSelectQueries(); assertEquals(2, myCaptureQueriesListener.countSelectQueries()); assertThat(myCaptureQueriesListener.getSelectQueries().get(0).getSql(true, false), containsString("SELECT t0.RES_ID FROM HFJ_SPIDX_TOKEN t0")); - assertThat(myCaptureQueriesListener.getSelectQueries().get(0).getSql(true, false), containsString("limit '5'")); + assertThat(myCaptureQueriesListener.getSelectQueries().get(0).getSql(true, false), containsString("fetch next '6'")); assertThat(myCaptureQueriesListener.getSelectQueries().get(0).getSql(true, false), containsString("offset '5'")); assertEquals(0, myCaptureQueriesListener.countInsertQueries()); assertEquals(0, myCaptureQueriesListener.countUpdateQueries()); @@ -1336,22 +1345,7 @@ public class FhirResourceDaoR4QueryCountTest extends BaseResourceProviderR4Test assertEquals(1, myCaptureQueriesListener.countCommits()); assertEquals(0, myCaptureQueriesListener.countRollbacks()); - assertThat(outcome.getLink("next").getUrl(), containsString("Patient?_count=5&_offset=10&active=true")); - - // Third page (no results) - - myCaptureQueriesListener.clear(); - outcome = myClient.search().forResource("Patient").where(Patient.ACTIVE.exactly().code("true")).offset(10).count(5).returnBundle(Bundle.class).execute(); - assertThat(toUnqualifiedVersionlessIdValues(outcome).toString(), toUnqualifiedVersionlessIdValues(outcome), empty()); - myCaptureQueriesListener.logSelectQueries(); - assertEquals(1, myCaptureQueriesListener.countSelectQueries()); - assertThat(myCaptureQueriesListener.getSelectQueries().get(0).getSql(true, false), containsString("SELECT t0.RES_ID FROM HFJ_SPIDX_TOKEN t0")); - assertThat(myCaptureQueriesListener.getSelectQueries().get(0).getSql(true, false), containsString("limit '5'")); - assertThat(myCaptureQueriesListener.getSelectQueries().get(0).getSql(true, false), containsString("offset '10'")); - assertEquals(0, myCaptureQueriesListener.countInsertQueries()); - assertEquals(0, myCaptureQueriesListener.countUpdateQueries()); - assertEquals(0, myCaptureQueriesListener.countDeleteQueries()); - + assertNull(outcome.getLink("next")); } @@ -2403,9 +2397,9 @@ public class FhirResourceDaoR4QueryCountTest extends BaseResourceProviderR4Test assertEquals(0, myCaptureQueriesListener.countDeleteQueries()); // Make sure the match URL query uses a small limit - String matchUrlQuery = myCaptureQueriesListener.getSelectQueries().get(0).getSql(true, true); - assertThat(matchUrlQuery, containsString("HASH_SYS_AND_VALUE='-4132452001562191669'")); - assertThat(matchUrlQuery, containsString("limit '2'")); + String matchUrlQuery = myCaptureQueriesListener.getSelectQueries().get(0).getSql(true, false); + assertThat(matchUrlQuery, containsString("rispt1_0.HASH_SYS_AND_VALUE='-4132452001562191669'")); + assertThat(matchUrlQuery, containsString("fetch first '2'")); runInTransaction(() -> { List types = myResourceTableDao.findAll().stream().map(t -> t.getResourceType()).collect(Collectors.toList()); @@ -3073,8 +3067,6 @@ public class FhirResourceDaoR4QueryCountTest extends BaseResourceProviderR4Test Bundle input = bb.getBundleTyped(); -// input.getEntry().get(0). - myCaptureQueriesListener.clear(); mySystemDao.transaction(mySrd, input); assertEquals(1, myCaptureQueriesListener.countSelectQueriesForCurrentThread()); @@ -3087,37 +3079,102 @@ public class FhirResourceDaoR4QueryCountTest extends BaseResourceProviderR4Test */ @SuppressWarnings("unchecked") @Test - public void testTriggerSubscription() throws Exception { + public void testTriggerSubscription_Sync() throws Exception { // Setup + IntStream.range(0, 200).forEach(i->createAPatient()); - myStorageSettings.addSupportedSubscriptionType(org.hl7.fhir.dstu2.model.Subscription.SubscriptionChannelType.RESTHOOK); - myResourceModifiedSubmitterSvc.startIfNeeded(); + mySubscriptionTestUtil.registerRestHookInterceptor(); + ForceOffsetSearchModeInterceptor interceptor = new ForceOffsetSearchModeInterceptor(); + myInterceptorRegistry.registerInterceptor(interceptor); + try { + String payload = "application/fhir+json"; + Subscription subscription = createSubscription("Patient?", payload, ourServer.getBaseUrl(), null); + IIdType subscriptionId = mySubscriptionDao.create(subscription, mySrd).getId(); - for (int i = 0; i < 10; i++) { - createPatient(withActiveTrue()); + waitForActivatedSubscriptionCount(1); + + mySubscriptionTriggeringSvc.triggerSubscription(null, List.of(new StringType("Patient?")), subscriptionId); + + // Test + myCaptureQueriesListener.clear(); + mySubscriptionTriggeringSvc.runDeliveryPass(); + mySubscriptionTriggeringSvc.runDeliveryPass(); + mySubscriptionTriggeringSvc.runDeliveryPass(); + mySubscriptionTriggeringSvc.runDeliveryPass(); + mySubscriptionTriggeringSvc.runDeliveryPass(); + myCaptureQueriesListener.logSelectQueries(); + ourPatientProvider.waitForUpdateCount(200); + + // Validate + assertEquals(7, myCaptureQueriesListener.countSelectQueriesForCurrentThread()); + assertEquals(0, myCaptureQueriesListener.countUpdateQueriesForCurrentThread()); + assertEquals(0, myCaptureQueriesListener.countInsertQueriesForCurrentThread()); + assertEquals(0, myCaptureQueriesListener.countDeleteQueriesForCurrentThread()); + } finally { + myInterceptorRegistry.unregisterInterceptor(interceptor); } + } + + + @Test + public void testTriggerSubscription_Async() throws Exception { + // Setup + IntStream.range(0, 200).forEach(i->createAPatient()); + + mySubscriptionTestUtil.registerRestHookInterceptor(); + + String payload = "application/fhir+json"; + Subscription subscription = createSubscription("Patient?", payload, ourServer.getBaseUrl(), null); + IIdType subId = mySubscriptionDao.create(subscription, mySrd).getId(); - Subscription subscription = new Subscription(); - subscription.getChannel().setEndpoint(ourServer.getBaseUrl()); - subscription.getChannel().setType(Subscription.SubscriptionChannelType.RESTHOOK); - subscription.getChannel().setPayload(Constants.CT_FHIR_JSON_NEW); - subscription.setStatus(Subscription.SubscriptionStatus.REQUESTED); - subscription.setCriteria("Patient?active=true"); - IIdType subscriptionId = mySubscriptionDao.create(subscription, mySrd).getId().toUnqualifiedVersionless(); waitForActivatedSubscriptionCount(1); - mySubscriptionTriggeringSvc.triggerSubscription(null, List.of(new StringType("Patient?active=true")), subscriptionId); - // Test myCaptureQueriesListener.clear(); - mySubscriptionTriggeringSvc.runDeliveryPass(); - ourPatientProvider.waitForUpdateCount(10); + Parameters response = myClient + .operation() + .onInstance(subId) + .named(JpaConstants.OPERATION_TRIGGER_SUBSCRIPTION) + .withParameter(Parameters.class, ProviderConstants.SUBSCRIPTION_TRIGGERING_PARAM_SEARCH_URL, new StringType("Patient?")) + .execute(); + String responseValue = response.getParameter().get(0).getValue().primitiveValue(); + assertThat(responseValue, CoreMatchers.containsString("Subscription triggering job submitted as JOB ID")); - // Validate - assertEquals(6, myCaptureQueriesListener.countSelectQueriesForCurrentThread()); - assertEquals(1, myCaptureQueriesListener.countUpdateQueriesForCurrentThread()); - assertEquals(11, myCaptureQueriesListener.countInsertQueriesForCurrentThread()); - assertEquals(0, myCaptureQueriesListener.countDeleteQueriesForCurrentThread()); + assertEquals(3, myCaptureQueriesListener.countSelectQueries()); + assertEquals(0, myCaptureQueriesListener.countInsertQueries()); + assertEquals(0, myCaptureQueriesListener.countUpdateQueries()); + assertEquals(0, myCaptureQueriesListener.countDeleteQueries()); + + myCaptureQueriesListener.clear(); + mySubscriptionTriggeringSvc.runDeliveryPass(); + + myCaptureQueriesListener.logInsertQueries(); + assertEquals(15, myCaptureQueriesListener.countSelectQueries()); + assertEquals(201, myCaptureQueriesListener.countInsertQueries()); + assertEquals(3, myCaptureQueriesListener.countUpdateQueries()); + assertEquals(0, myCaptureQueriesListener.countDeleteQueries()); + + myCaptureQueriesListener.clear(); + mySubscriptionTriggeringSvc.runDeliveryPass(); + + assertEquals(2, myCaptureQueriesListener.countSelectQueries()); + assertEquals(0, myCaptureQueriesListener.countInsertQueries()); + assertEquals(0, myCaptureQueriesListener.countUpdateQueries()); + assertEquals(0, myCaptureQueriesListener.countDeleteQueries()); + + myCaptureQueriesListener.clear(); + mySubscriptionTriggeringSvc.runDeliveryPass(); + + assertEquals(0, myCaptureQueriesListener.countSelectQueries()); + assertEquals(0, myCaptureQueriesListener.countInsertQueries()); + assertEquals(0, myCaptureQueriesListener.countUpdateQueries()); + assertEquals(0, myCaptureQueriesListener.countDeleteQueries()); + + SubscriptionTriggeringSvcImpl svc = ProxyUtil.getSingletonTarget(mySubscriptionTriggeringSvc, SubscriptionTriggeringSvcImpl.class); + assertEquals(0, svc.getActiveJobCount()); + + assertEquals(0, ourPatientProvider.getCountCreate()); + await().until(ourPatientProvider::getCountUpdate, equalTo(200L)); } @@ -3141,7 +3198,7 @@ public class FhirResourceDaoR4QueryCountTest extends BaseResourceProviderR4Test assertEquals(7, expansion.getExpansion().getContains().size()); assertEquals(1, expansion.getExpansion().getContains().stream().filter(t -> t.getCode().equals("A")).findFirst().orElseThrow(() -> new IllegalArgumentException()).getDesignation().size()); myCaptureQueriesListener.logSelectQueriesForCurrentThread(); - assertEquals(5, myCaptureQueriesListener.countSelectQueries()); + assertEquals(6, myCaptureQueriesListener.countSelectQueries(), ()->"\n *" + myCaptureQueriesListener.getSelectQueries().stream().map(t->t.getSql(true, false)).collect(Collectors.joining("\n * "))); assertEquals(0, myCaptureQueriesListener.countDeleteQueries()); assertEquals(0, myCaptureQueriesListener.countUpdateQueries()); assertEquals(0, myCaptureQueriesListener.countInsertQueries()); @@ -3184,7 +3241,7 @@ public class FhirResourceDaoR4QueryCountTest extends BaseResourceProviderR4Test assertEquals(7, expansion.getExpansion().getContains().size()); assertEquals(1, expansion.getExpansion().getContains().stream().filter(t -> t.getCode().equals("A")).findFirst().orElseThrow(() -> new IllegalArgumentException()).getDesignation().size()); myCaptureQueriesListener.logSelectQueriesForCurrentThread(); - assertEquals(5, myCaptureQueriesListener.countSelectQueries()); + assertEquals(6, myCaptureQueriesListener.countSelectQueries()); assertEquals(0, myCaptureQueriesListener.countDeleteQueries()); assertEquals(0, myCaptureQueriesListener.countUpdateQueries()); assertEquals(0, myCaptureQueriesListener.countInsertQueries()); diff --git a/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/dao/r4/FhirResourceDaoR4QuerySandbox.java b/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/dao/r4/FhirResourceDaoR4QuerySandbox.java new file mode 100644 index 00000000000..7efc920ccaf --- /dev/null +++ b/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/dao/r4/FhirResourceDaoR4QuerySandbox.java @@ -0,0 +1,150 @@ +package ca.uhn.fhir.jpa.dao.r4; + +import ca.uhn.fhir.context.FhirContext; +import ca.uhn.fhir.interceptor.api.Hook; +import ca.uhn.fhir.interceptor.api.Pointcut; +import ca.uhn.fhir.jpa.dao.TestDaoSearch; +import ca.uhn.fhir.jpa.test.BaseJpaTest; +import ca.uhn.fhir.jpa.test.config.TestHSearchAddInConfig; +import ca.uhn.fhir.jpa.test.config.TestR4Config; +import ca.uhn.fhir.jpa.util.SqlQuery; +import ca.uhn.fhir.jpa.util.SqlQueryList; +import ca.uhn.fhir.rest.api.server.RequestDetails; +import ca.uhn.fhir.storage.test.DaoTestDataBuilder; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import org.junit.jupiter.api.extension.RegisterExtension; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.test.annotation.DirtiesContext; +import org.springframework.test.context.ContextConfiguration; +import org.springframework.test.context.TestContext; +import org.springframework.test.context.TestExecutionListeners; +import org.springframework.test.context.junit.jupiter.SpringExtension; +import org.springframework.test.context.support.DependencyInjectionTestExecutionListener; +import org.springframework.test.context.support.DirtiesContextTestExecutionListener; +import org.springframework.transaction.PlatformTransactionManager; + +import java.util.ArrayList; +import java.util.List; + +import static org.hamcrest.MatcherAssert.assertThat; +import static org.hamcrest.Matchers.empty; +import static org.hamcrest.Matchers.not; + +/** + * Sandbox for implementing queries. + * This will NOT run during the build - use this class as a convenient + * place to explore, debug, profile, and optimize. + */ +@ExtendWith(SpringExtension.class) +@ContextConfiguration(classes = { + TestR4Config.class, + TestHSearchAddInConfig.NoFT.class, + DaoTestDataBuilder.Config.class, + TestDaoSearch.Config.class +}) +@DirtiesContext(classMode = DirtiesContext.ClassMode.AFTER_CLASS) +@TestExecutionListeners(listeners = { + DependencyInjectionTestExecutionListener.class + , FhirResourceDaoR4QuerySandbox.TestDirtiesContextTestExecutionListener.class +}) +public class FhirResourceDaoR4QuerySandbox extends BaseJpaTest { + private static final Logger ourLog = LoggerFactory.getLogger(FhirResourceDaoR4QuerySandbox.class); + + @Autowired + PlatformTransactionManager myTxManager; + @Autowired + FhirContext myFhirCtx; + @RegisterExtension + @Autowired + DaoTestDataBuilder myDataBuilder; + @Autowired + TestDaoSearch myTestDaoSearch; + + @Override + protected PlatformTransactionManager getTxManager() { + return myTxManager; + } + + @Override + protected FhirContext getFhirContext() { + return myFhirCtx; + } + + List myCapturedQueries = new ArrayList<>(); + @BeforeEach + void registerLoggingInterceptor() { + registerInterceptor(new Object(){ + @Hook(Pointcut.JPA_PERFTRACE_RAW_SQL) + public void captureSql(RequestDetails theRequestDetails, SqlQueryList theQueries) { + for (SqlQuery next : theQueries) { + String output = next.getSql(true, true, true); + ourLog.info("Query: {}", output); + myCapturedQueries.add(output); + } + } + }); + + } + + @Test + public void testSearches_logQueries() { + myDataBuilder.createPatient(); + + myTestDaoSearch.searchForIds("Patient?name=smith"); + + assertThat(myCapturedQueries, not(empty())); + } + + @Test + void testQueryByPid() { + + // sentinel for over-match + myDataBuilder.createPatient(); + + String id = myDataBuilder.createPatient( + myDataBuilder.withBirthdate("1971-01-01"), + myDataBuilder.withActiveTrue(), + myDataBuilder.withFamily("Smith")).getIdPart(); + + myTestDaoSearch.assertSearchFindsOnly("search by server assigned id", "Patient?_pid=" + id, id); + } + + @Test + void testQueryByPid_withOtherSPAvoidsResourceTable() { + // sentinel for over-match + myDataBuilder.createPatient(); + + String id = myDataBuilder.createPatient( + myDataBuilder.withBirthdate("1971-01-01"), + myDataBuilder.withActiveTrue(), + myDataBuilder.withFamily("Smith")).getIdPart(); + + myTestDaoSearch.assertSearchFindsOnly("search by server assigned id", "Patient?name=smith&_pid=" + id, id); + } + + @Test + void testSortByPid() { + + String id1 = myDataBuilder.createPatient(myDataBuilder.withFamily("Smithy")).getIdPart(); + String id2 = myDataBuilder.createPatient(myDataBuilder.withFamily("Smithwick")).getIdPart(); + String id3 = myDataBuilder.createPatient(myDataBuilder.withFamily("Smith")).getIdPart(); + + myTestDaoSearch.assertSearchFindsInOrder("sort by server assigned id", "Patient?family=smith&_sort=_pid", id1,id2,id3); + myTestDaoSearch.assertSearchFindsInOrder("reverse sort by server assigned id", "Patient?family=smith&_sort=-_pid", id3,id2,id1); + } + + public static final class TestDirtiesContextTestExecutionListener extends DirtiesContextTestExecutionListener { + + @Override + protected void beforeOrAfterTestClass(TestContext testContext, DirtiesContext.ClassMode requiredClassMode) throws Exception { + if (!testContext.getTestClass().getName().contains("$")) { + super.beforeOrAfterTestClass(testContext, requiredClassMode); + } + } + } + +} diff --git a/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/dao/r4/FhirResourceDaoR4SearchFtTest.java b/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/dao/r4/FhirResourceDaoR4SearchFtTest.java index 0c26341ce74..a80c8002e3f 100644 --- a/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/dao/r4/FhirResourceDaoR4SearchFtTest.java +++ b/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/dao/r4/FhirResourceDaoR4SearchFtTest.java @@ -24,7 +24,7 @@ import org.hl7.fhir.r4.model.StringType; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; -import javax.servlet.http.HttpServletRequest; +import jakarta.servlet.http.HttpServletRequest; import java.util.Arrays; import java.util.List; diff --git a/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/dao/r4/FhirResourceDaoR4SearchNoFtTest.java b/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/dao/r4/FhirResourceDaoR4SearchNoFtTest.java index f28fec1fb98..2d668f24e5d 100644 --- a/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/dao/r4/FhirResourceDaoR4SearchNoFtTest.java +++ b/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/dao/r4/FhirResourceDaoR4SearchNoFtTest.java @@ -151,7 +151,7 @@ import org.springframework.transaction.support.TransactionCallback; import org.springframework.transaction.support.TransactionCallbackWithoutResult; import org.springframework.transaction.support.TransactionTemplate; -import javax.servlet.http.HttpServletRequest; +import jakarta.servlet.http.HttpServletRequest; import java.io.IOException; import java.math.BigDecimal; import java.nio.charset.StandardCharsets; @@ -176,6 +176,7 @@ import static ca.uhn.fhir.rest.param.ParamPrefixEnum.LESSTHAN; import static ca.uhn.fhir.rest.param.ParamPrefixEnum.LESSTHAN_OR_EQUALS; import static ca.uhn.fhir.rest.param.ParamPrefixEnum.NOT_EQUAL; import static ca.uhn.fhir.test.utilities.CustomMatchersUtil.assertDoesNotContainAnyOf; +import static ca.uhn.fhir.util.DateUtils.convertDateToIso8601String; import static org.apache.commons.lang3.StringUtils.countMatches; import static org.apache.commons.lang3.StringUtils.leftPad; import static org.hamcrest.CoreMatchers.is; @@ -447,6 +448,57 @@ public class FhirResourceDaoR4SearchNoFtTest extends BaseJpaR4Test { assertEquals(0, ids.size()); } + @Test + public void testHasEncounterAndLastUpdated() { + // setup + Patient patientA = new Patient(); + String patientIdA = myPatientDao.create(patientA).getId().toUnqualifiedVersionless().getValue(); + + Patient patientB = new Patient(); + String patientIdB = myPatientDao.create(patientA).getId().toUnqualifiedVersionless().getValue(); + + Encounter encounterA = new Encounter(); + encounterA.getClass_().setSystem("http://snomed.info/sct").setCode("55822004"); + encounterA.getSubject().setReference(patientIdA); + + // record time between encounter A and B + TestUtil.sleepOneClick(); + Date beforeA = new Date(); + TestUtil.sleepOneClick(); + + myEncounterDao.create(encounterA); + + Encounter encounterB = new Encounter(); + encounterB.getClass_().setSystem("http://snomed.info/sct").setCode("55822005"); + encounterB.getSubject().setReference(patientIdB); + + // record time between encounter A and B + TestUtil.sleepOneClick(); + Date beforeB = new Date(); + TestUtil.sleepOneClick(); + + myEncounterDao.create(encounterB); + + // execute + String criteriaA = "_has:Encounter:patient:_lastUpdated=ge" + convertDateToIso8601String(beforeA); + SearchParameterMap mapA = myMatchUrlService.translateMatchUrl(criteriaA, myFhirContext.getResourceDefinition(Patient.class)); + mapA.setLoadSynchronous(true); + myCaptureQueriesListener.clear(); + IBundleProvider resultA = myPatientDao.search(mapA); + myCaptureQueriesListener.logSelectQueries(); + List idsBeforeA = toUnqualifiedVersionlessIdValues(resultA); + + String criteriaB = "_has:Encounter:patient:_lastUpdated=ge" + convertDateToIso8601String(beforeB); + SearchParameterMap mapB = myMatchUrlService.translateMatchUrl(criteriaB, myFhirContext.getResourceDefinition(Patient.class)); + mapB.setLoadSynchronous(true); + IBundleProvider resultB = myPatientDao.search(mapB); + List idsBeforeB = toUnqualifiedVersionlessIdValues(resultB); + + // verify + assertEquals(2, idsBeforeA.size()); + assertEquals(1, idsBeforeB.size()); + } + @Test public void testGenderBirthdateHasCondition() { Patient patient = new Patient(); @@ -3807,33 +3859,6 @@ public class FhirResourceDaoR4SearchNoFtTest extends BaseJpaR4Test { assertEquals(2, countMatches(searchQuery.toUpperCase(), "RES_UPDATED"), searchQuery); } - @Disabled - @Test - public void testSearchWithContext() { - - - String url = "Procedure?_count=300&_format=json&_include%3Arecurse=*&category=CANN&encounter.identifier=A1057852019&status%3Anot=entered-in-error"; - RuntimeResourceDefinition def = myFhirContext.getResourceDefinition("Procedure"); - SearchParameterMap sp = myMatchUrlService.translateMatchUrl(url, def); - - - myCaptureQueriesListener.clear(); - sp.setLoadSynchronous(true); - myProcedureDao.search(sp); - - myCaptureQueriesListener.logSelectQueriesForCurrentThread(); -// List queries = myCaptureQueriesListener -// .getSelectQueriesForCurrentThread() -// .stream() -// .map(t -> t.getSql(true, true)) -// .collect(Collectors.toList()); -// -// String searchQuery = queries.get(0); -// assertEquals(searchQuery, 1, StringUtils.countMatches(searchQuery.toUpperCase(), "HFJ_SPIDX_TOKEN")); -// assertEquals(searchQuery, 1, StringUtils.countMatches(searchQuery.toUpperCase(), "LEFT OUTER JOIN")); -// assertEquals(searchQuery, 2, StringUtils.countMatches(searchQuery.toUpperCase(), "AND RESOURCETA0_.RES_UPDATED")); - } - @Test public void testSearchTokenParam() { Patient patient = new Patient(); @@ -4567,8 +4592,8 @@ public class FhirResourceDaoR4SearchNoFtTest extends BaseJpaR4Test { assertNull(values.size()); assertEquals(5, values.getResources(0, 1000).size()); - String sql = myCaptureQueriesListener.logSelectQueriesForCurrentThread(0); - assertEquals(1, countMatches(sql, "limit '5'"), sql); + String sql = myCaptureQueriesListener.getSelectQueriesForCurrentThread().get(0).getSql(true, false); + assertEquals(1, countMatches(sql, "fetch first '5'"), sql); } @Test @@ -5741,8 +5766,15 @@ public class FhirResourceDaoR4SearchNoFtTest extends BaseJpaR4Test { * [base]/Bundle?composition.patient.identifier=foo */ @ParameterizedTest - @CsvSource({"urn:uuid:5c34dc2c-9b5d-4ec1-b30b-3e2d4371508b", "Patient/ABC"}) - public void testCreateAndSearchForFullyChainedSearchParameter(String thePatientId) { + @CsvSource({ + "true , urn:uuid:5c34dc2c-9b5d-4ec1-b30b-3e2d4371508b , urn:uuid:5c34dc2c-9b5d-4ec1-b30b-3e2d4371508b", + "false, urn:uuid:5c34dc2c-9b5d-4ec1-b30b-3e2d4371508b , urn:uuid:5c34dc2c-9b5d-4ec1-b30b-3e2d4371508b", + "true , Patient/ABC , Patient/ABC ", + "false, Patient/ABC , Patient/ABC ", + "true , Patient/ABC , http://example.com/fhir/Patient/ABC ", + "false, Patient/ABC , http://example.com/fhir/Patient/ABC ", + }) + public void testCreateAndSearchForFullyChainedSearchParameter(boolean theUseFullChainInName, String thePatientId, String theFullUrl) { // Setup 1 myStorageSettings.setIndexMissingFields(JpaStorageSettings.IndexEnabledEnum.DISABLED); @@ -5767,13 +5799,18 @@ public class FhirResourceDaoR4SearchNoFtTest extends BaseJpaR4Test { composition.setSubject(new Reference(thePatientId)); Patient patient = new Patient(); - patient.setId(new IdType(thePatientId)); + patient.setId(new IdType(theFullUrl)); patient.addIdentifier().setSystem("http://foo").setValue("bar"); Bundle bundle = new Bundle(); bundle.setType(Bundle.BundleType.DOCUMENT); - bundle.addEntry().setResource(composition); - bundle.addEntry().setResource(patient); + bundle + .addEntry() + .setResource(composition); + bundle + .addEntry() + .setFullUrl(theFullUrl) + .setResource(patient); myBundleDao.create(bundle, mySrd); @@ -5781,35 +5818,40 @@ public class FhirResourceDaoR4SearchNoFtTest extends BaseJpaR4Test { bundle2.setType(Bundle.BundleType.DOCUMENT); myBundleDao.create(bundle2, mySrd); - // Verify 1 - runInTransaction(() -> { + // Test + + SearchParameterMap map; + if (theUseFullChainInName) { + map = SearchParameterMap.newSynchronous("composition.patient.identifier", new TokenParam("http://foo", "bar")); + } else { + map = SearchParameterMap.newSynchronous("composition", new ReferenceParam("patient.identifier", "http://foo|bar")); + } + IBundleProvider outcome = myBundleDao.search(map, mySrd); + + // Verify + + List params = extractAllTokenIndexes(); + assertThat(params.toString(), params, containsInAnyOrder( + "composition.patient.identifier http://foo|bar" + )); + assertEquals(1, outcome.size()); + } + + private List extractAllTokenIndexes() { + List params = runInTransaction(() -> { logAllTokenIndexes(); - List params = myResourceIndexedSearchParamTokenDao + return myResourceIndexedSearchParamTokenDao .findAll() .stream() .filter(t -> t.getParamName().contains(".")) .map(t -> t.getParamName() + " " + t.getSystem() + "|" + t.getValue()) .toList(); - assertThat(params.toString(), params, containsInAnyOrder( - "composition.patient.identifier http://foo|bar" - )); }); - - // Test 2 - IBundleProvider outcome; - - SearchParameterMap map = SearchParameterMap - .newSynchronous("composition.patient.identifier", new TokenParam("http://foo", "bar")); - outcome = myBundleDao.search(map, mySrd); - assertEquals(1, outcome.size()); - - map = SearchParameterMap - .newSynchronous("composition", new ReferenceParam("patient.identifier", "http://foo|bar")); - outcome = myBundleDao.search(map, mySrd); - assertEquals(1, outcome.size()); + return params; } + @Nested public class TagBelowTests { diff --git a/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/dao/r4/FhirResourceDaoR4SearchOptimizedTest.java b/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/dao/r4/FhirResourceDaoR4SearchOptimizedTest.java index 44828cd39ab..1e15bc49293 100644 --- a/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/dao/r4/FhirResourceDaoR4SearchOptimizedTest.java +++ b/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/dao/r4/FhirResourceDaoR4SearchOptimizedTest.java @@ -8,7 +8,9 @@ import ca.uhn.fhir.jpa.entity.Search; import ca.uhn.fhir.jpa.model.entity.ResourceTable; import ca.uhn.fhir.jpa.model.search.SearchStatusEnum; import ca.uhn.fhir.jpa.search.PersistedJpaBundleProvider; +import ca.uhn.fhir.jpa.search.PersistedJpaSearchFirstPageBundleProvider; import ca.uhn.fhir.jpa.search.SearchCoordinatorSvcImpl; +import ca.uhn.fhir.jpa.search.builder.tasks.SearchTask; import ca.uhn.fhir.jpa.searchparam.MatchUrlService; import ca.uhn.fhir.jpa.searchparam.SearchParameterMap; import ca.uhn.fhir.jpa.test.BaseJpaR4Test; @@ -29,7 +31,9 @@ import ca.uhn.fhir.rest.server.exceptions.InternalErrorException; import ca.uhn.fhir.test.utilities.ProxyUtil; import org.apache.commons.lang3.StringUtils; import org.apache.commons.lang3.exception.ExceptionUtils; +import org.checkerframework.checker.units.qual.A; import org.hl7.fhir.instance.model.api.IAnyResource; +import org.hl7.fhir.instance.model.api.IBaseResource; import org.hl7.fhir.instance.model.api.IIdType; import org.hl7.fhir.r4.model.BodyStructure; import org.hl7.fhir.r4.model.CodeableConcept; @@ -51,6 +55,8 @@ import org.hl7.fhir.r4.model.UriType; import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; +import org.mockito.ArgumentCaptor; +import org.mockito.Mockito; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.scheduling.concurrent.ThreadPoolExecutorFactoryBean; @@ -58,6 +64,7 @@ import java.util.ArrayList; import java.util.Arrays; import java.util.List; import java.util.Locale; +import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ExecutorService; import java.util.concurrent.Future; import java.util.stream.Collectors; @@ -89,9 +96,11 @@ public class FhirResourceDaoR4SearchOptimizedTest extends BaseJpaR4Test { @Autowired private MatchUrlService myMatchUrlService; + @Override @BeforeEach - public void before() { - mySearchCoordinatorSvcImpl = (SearchCoordinatorSvcImpl) ProxyUtil.getSingletonTarget(mySearchCoordinatorSvc, SearchCoordinatorSvcImpl.class); + public void before() throws Exception { + super.before(); + mySearchCoordinatorSvcImpl = ProxyUtil.getSingletonTarget(mySearchCoordinatorSvc, SearchCoordinatorSvcImpl.class); mySearchCoordinatorSvcImpl.setLoadingThrottleForUnitTests(null); mySearchCoordinatorSvcImpl.setSyncSizeForUnitTests(QueryParameterUtils.DEFAULT_SYNC_SIZE); myCaptureQueriesListener.setCaptureQueryStackTrace(true); @@ -102,6 +111,7 @@ public class FhirResourceDaoR4SearchOptimizedTest extends BaseJpaR4Test { public final void after() { mySearchCoordinatorSvcImpl.setLoadingThrottleForUnitTests(null); mySearchCoordinatorSvcImpl.setSyncSizeForUnitTests(QueryParameterUtils.DEFAULT_SYNC_SIZE); + mySearchCoordinatorSvcImpl.setIdToSearchTaskMapForUnitTests(new ConcurrentHashMap<>()); myStorageSettings.setSearchPreFetchThresholds(new JpaStorageSettings().getSearchPreFetchThresholds()); myCaptureQueriesListener.setCaptureQueryStackTrace(false); myStorageSettings.setIndexMissingFields(new JpaStorageSettings().getIndexMissingFields()); @@ -214,6 +224,39 @@ public class FhirResourceDaoR4SearchOptimizedTest extends BaseJpaR4Test { } + @Test + public void testSearchCoordinatorSvc_whenExecutingSearchWithParamTotal_returnsBundleSynchronizedWithBackingSearchCapabilities(){ + ArgumentCaptor keyArgumentCaptor = ArgumentCaptor.forClass(String.class); + ArgumentCaptor valueArgumentCaptor = ArgumentCaptor.forClass(SearchTask.class); + ConcurrentHashMap spyingIdToSearchTaskMap = Mockito.spy(new ConcurrentHashMap<>()); + mySearchCoordinatorSvcImpl.setIdToSearchTaskMapForUnitTests(spyingIdToSearchTaskMap); + create200Patients(); + + SearchParameterMap params = new SearchParameterMap(); + params.add(Patient.SP_NAME, new StringParam("FAM")); + params.setSearchTotalMode(SearchTotalModeEnum.ACCURATE); + + // calling dao.search will end up invoking the searchCoordinatorSvc. based on the provided search parameters, the svc + // generates and triggers a searchTask which will create chunked resultsets. the searchTask make use of a searchEntity + // to keep track of search progress and key indicators like the search total count. + PersistedJpaSearchFirstPageBundleProvider results = (PersistedJpaSearchFirstPageBundleProvider) myPatientDao.search(params); + + // to return the correct resources through method getResources(), the PersistedJpaSearchFirstPageBundleProvider generated by the + // searchCoordinatorSvc needs to access the same searchEntity that was used by the searchTask. this test ensures that the searchEntity + // operated upon by the searchTask is the same as the searchEntity that is found in the generated PersistedJpaSearchFirstPageBundleProvider. + Mockito.verify(spyingIdToSearchTaskMap, Mockito.times(1)).put(keyArgumentCaptor.capture(), valueArgumentCaptor.capture()); + + Search bundleProviderSearch = results.getSearchEntityForTesting(); + Search backingSearch = valueArgumentCaptor.getValue().getSearch(); + + assertThat(bundleProviderSearch.getUuid(), equalTo(keyArgumentCaptor.getValue())); + assertThat(bundleProviderSearch.getUuid(), equalTo(backingSearch.getUuid())); + + assertThat(bundleProviderSearch.getStatus(), equalTo(backingSearch.getStatus())); + assertThat(bundleProviderSearch.getId(), equalTo(backingSearch.getId())); + + } + @Test public void testFetchTotalAccurateForSlowLoading() { create200Patients(); @@ -853,12 +896,12 @@ public class FhirResourceDaoR4SearchOptimizedTest extends BaseJpaR4Test { myCaptureQueriesListener.logSelectQueriesForCurrentThread(); String selectQuery = myCaptureQueriesListener.getSelectQueriesForCurrentThread().get(0).getSql(true, false); - assertEquals(1, StringUtils.countMatches(selectQuery.toLowerCase(), "forcedid0_.resource_type='observation'"), selectQuery); - assertEquals(1, StringUtils.countMatches(selectQuery.toLowerCase(), "forcedid0_.forced_id in ('a')"), selectQuery); + assertEquals(1, StringUtils.countMatches(selectQuery.toLowerCase(), "rt1_0.res_type='observation'"), selectQuery); + assertEquals(1, StringUtils.countMatches(selectQuery.toLowerCase(), "rt1_0.fhir_id in ('a')"), selectQuery); selectQuery = myCaptureQueriesListener.getSelectQueriesForCurrentThread().get(1).getSql(true, false); assertEquals(1, StringUtils.countMatches(selectQuery.toLowerCase(), "select t1.res_id from hfj_resource t1"), selectQuery); - assertEquals(0, StringUtils.countMatches(selectQuery.toLowerCase(), "t1.res_type = 'observation'"), selectQuery); + assertEquals(0, StringUtils.countMatches(selectQuery.toLowerCase(), "t1.res_type='observation'"), selectQuery); assertEquals(0, StringUtils.countMatches(selectQuery.toLowerCase(), "t1.res_deleted_at is null"), selectQuery); } @@ -895,8 +938,8 @@ public class FhirResourceDaoR4SearchOptimizedTest extends BaseJpaR4Test { assertEquals(1, myCaptureQueriesListener.getSelectQueriesForCurrentThread().size()); String selectQuery = myCaptureQueriesListener.getSelectQueriesForCurrentThread().get(0).getSql(true, false); - assertEquals(1, StringUtils.countMatches(selectQuery.toLowerCase(), "forcedid0_.resource_type='observation'"), selectQuery); - assertEquals(1, StringUtils.countMatches(selectQuery.toLowerCase(), "forcedid0_.forced_id in ('a')"), selectQuery); + assertEquals(1, StringUtils.countMatches(selectQuery.toLowerCase(), "rt1_0.res_type='observation'"), selectQuery); + assertEquals(1, StringUtils.countMatches(selectQuery.toLowerCase(), "rt1_0.fhir_id in ('a')"), selectQuery); } // Search by ID where at least one ID is a numeric ID @@ -1504,8 +1547,8 @@ public class FhirResourceDaoR4SearchOptimizedTest extends BaseJpaR4Test { // Forced ID resolution resultingQueryNotFormatted = queries.get(0); - assertThat(resultingQueryNotFormatted, containsString("RESOURCE_TYPE='Organization'")); - assertThat(resultingQueryNotFormatted, containsString("forcedid0_.RESOURCE_TYPE='Organization' and forcedid0_.FORCED_ID='ORG1' or forcedid0_.RESOURCE_TYPE='Organization' and forcedid0_.FORCED_ID='ORG2'")); + assertThat(resultingQueryNotFormatted, containsString("RES_TYPE='Organization'")); + assertThat(resultingQueryNotFormatted, containsString("rt1_0.RES_TYPE='Organization' and rt1_0.FHIR_ID='ORG1' or rt1_0.RES_TYPE='Organization' and rt1_0.FHIR_ID='ORG2'")); // The search itself resultingQueryNotFormatted = queries.get(1); diff --git a/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/dao/r4/FhirResourceDaoR4SearchPageExpiryTest.java b/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/dao/r4/FhirResourceDaoR4SearchPageExpiryTest.java index 8e92cde712f..6140b4c69c5 100644 --- a/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/dao/r4/FhirResourceDaoR4SearchPageExpiryTest.java +++ b/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/dao/r4/FhirResourceDaoR4SearchPageExpiryTest.java @@ -29,7 +29,7 @@ import org.springframework.transaction.support.TransactionCallback; import org.springframework.transaction.support.TransactionCallbackWithoutResult; import org.springframework.transaction.support.TransactionTemplate; -import javax.annotation.Nullable; +import jakarta.annotation.Nullable; import java.util.Date; import java.util.concurrent.atomic.AtomicLong; diff --git a/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/dao/r4/FhirResourceDaoR4SearchSqlTest.java b/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/dao/r4/FhirResourceDaoR4SearchSqlTest.java index 0ccd6ae7d0e..710639e34fb 100644 --- a/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/dao/r4/FhirResourceDaoR4SearchSqlTest.java +++ b/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/dao/r4/FhirResourceDaoR4SearchSqlTest.java @@ -99,10 +99,10 @@ public class FhirResourceDaoR4SearchSqlTest extends BaseJpaR4Test { assertEquals("SELECT t0.RES_ID FROM HFJ_RESOURCE t0 INNER JOIN HFJ_RES_TAG t1 ON (t0.RES_ID = t1.RES_ID) INNER JOIN HFJ_TAG_DEF t2 ON (t1.TAG_ID = t2.TAG_ID) WHERE (((t0.RES_TYPE = ?) AND (t0.RES_DELETED_AT IS NULL)) AND ((t2.TAG_TYPE = ?) AND (t2.TAG_SYSTEM = ?) AND (t2.TAG_CODE = ?)))", sql); // Query 2 - Load resourece contents sql = myCaptureQueriesListener.getSelectQueriesForCurrentThread().get(1).getSql(false, false); - assertThat(sql, containsString("where resourcese0_.RES_ID in (?)")); + assertThat(sql, containsString("where rsv1_0.RES_ID in (?)")); // Query 3 - Load tags and defintions sql = myCaptureQueriesListener.getSelectQueriesForCurrentThread().get(2).getSql(false, false); - assertThat(sql, containsString("from HFJ_RES_TAG resourceta0_ inner join HFJ_TAG_DEF")); + assertThat(sql, containsString("from HFJ_RES_TAG rt1_0 join HFJ_TAG_DEF")); assertThat(toUnqualifiedVersionlessIds(outcome), Matchers.contains(id)); @@ -138,7 +138,7 @@ public class FhirResourceDaoR4SearchSqlTest extends BaseJpaR4Test { assertEquals("SELECT t0.RES_ID FROM HFJ_SPIDX_URI t0 WHERE (t0.HASH_URI = ?)", sql); // Query 2 - Load resourece contents sql = myCaptureQueriesListener.getSelectQueriesForCurrentThread().get(1).getSql(false, false); - assertThat(sql, containsString("where resourcese0_.RES_ID in (?)")); + assertThat(sql, containsString("where rsv1_0.RES_ID in (?)")); assertThat(toUnqualifiedVersionlessIds(outcome), Matchers.contains(id)); diff --git a/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/dao/r4/FhirResourceDaoR4SortTest.java b/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/dao/r4/FhirResourceDaoR4SortTest.java index ea781b9e4cc..23ab501a227 100644 --- a/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/dao/r4/FhirResourceDaoR4SortTest.java +++ b/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/dao/r4/FhirResourceDaoR4SortTest.java @@ -89,12 +89,12 @@ public class FhirResourceDaoR4SortTest extends BaseJpaR4Test { map = new SearchParameterMap(); map.setSort(new SortSpec("_id", SortOrderEnum.ASC)); ids = toUnqualifiedVersionlessIdValues(myPatientDao.search(map)); - assertThat(ids, contains("Patient/AA", "Patient/AB", id1, id2)); + assertThat(ids, contains(id1, id2, "Patient/AA", "Patient/AB")); map = new SearchParameterMap(); map.setSort(new SortSpec("_id", SortOrderEnum.DESC)); ids = toUnqualifiedVersionlessIdValues(myPatientDao.search(map)); - assertThat(ids, contains(id2, id1, "Patient/AB", "Patient/AA")); + assertThat(ids, contains("Patient/AB", "Patient/AA", id2, id1)); } @Test diff --git a/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/dao/r4/FhirResourceDaoR4StandardQueriesNoFTTest.java b/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/dao/r4/FhirResourceDaoR4StandardQueriesNoFTTest.java index 3236f6b60fd..3d23277dc19 100644 --- a/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/dao/r4/FhirResourceDaoR4StandardQueriesNoFTTest.java +++ b/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/dao/r4/FhirResourceDaoR4StandardQueriesNoFTTest.java @@ -4,15 +4,17 @@ import ca.uhn.fhir.context.FhirContext; import ca.uhn.fhir.jpa.api.dao.DaoRegistry; import ca.uhn.fhir.jpa.api.dao.IFhirResourceDao; import ca.uhn.fhir.jpa.dao.TestDaoSearch; -import ca.uhn.fhir.jpa.search.CompositeSearchParameterTestCases; -import ca.uhn.fhir.jpa.search.QuantitySearchParameterTestCases; import ca.uhn.fhir.jpa.search.BaseSourceSearchParameterTestCases; +import ca.uhn.fhir.jpa.search.CompositeSearchParameterTestCases; +import ca.uhn.fhir.jpa.search.IIdSearchTestTemplate; +import ca.uhn.fhir.jpa.search.QuantitySearchParameterTestCases; import ca.uhn.fhir.jpa.searchparam.MatchUrlService; import ca.uhn.fhir.jpa.test.BaseJpaTest; import ca.uhn.fhir.jpa.test.config.TestHSearchAddInConfig; import ca.uhn.fhir.jpa.test.config.TestR4Config; import ca.uhn.fhir.storage.test.BaseDateSearchDaoTests; import ca.uhn.fhir.storage.test.DaoTestDataBuilder; +import ca.uhn.fhir.test.utilities.ITestDataBuilder; import ca.uhn.fhir.test.utilities.ITestDataBuilder.ICreationArgument; import org.hl7.fhir.instance.model.api.IIdType; import org.hl7.fhir.r4.model.Observation; @@ -36,9 +38,14 @@ import static org.hamcrest.MatcherAssert.assertThat; import static org.hamcrest.Matchers.contains; import static org.hamcrest.Matchers.equalTo; import static org.hamcrest.Matchers.hasItem; -import static org.hamcrest.Matchers.hasItems; import static org.hamcrest.Matchers.not; +/** + * Verify that our query behaviour matches the spec. + * Note: we do not extend BaseJpaR4Test here. + * That does a full purge in @AfterEach which is a bit slow. + * Instead, this test tracks all created resources in DaoTestDataBuilder, and deletes them in teardown. + */ @ExtendWith(SpringExtension.class) @ContextConfiguration(classes = { TestR4Config.class, @@ -256,10 +263,10 @@ public class FhirResourceDaoR4StandardQueriesNoFTTest extends BaseJpaTest { String idExM = withObservation(myDataBuilder.withObservationCode("http://example.org", "MValue")).getIdPart(); List allIds = myTestDaoSearch.searchForIds("/Observation?_sort=code"); - assertThat(allIds, hasItems(idAlphaA, idAlphaM, idAlphaZ, idExA, idExD, idExM)); + assertThat(allIds, contains(idAlphaA, idAlphaM, idAlphaZ, idExA, idExD, idExM)); allIds = myTestDaoSearch.searchForIds("/Observation?_sort=code&code=http://example.org|"); - assertThat(allIds, hasItems(idExA, idExD, idExM)); + assertThat(allIds, contains(idExA, idExD, idExM)); } } } @@ -368,7 +375,7 @@ public class FhirResourceDaoR4StandardQueriesNoFTTest extends BaseJpaTest { String idAlpha5 = withRiskAssessmentWithProbabilty(0.5).getIdPart(); List allIds = myTestDaoSearch.searchForIds("/RiskAssessment?_sort=probability"); - assertThat(allIds, hasItems(idAlpha2, idAlpha5, idAlpha7)); + assertThat(allIds, contains(idAlpha2, idAlpha5, idAlpha7)); } } @@ -491,12 +498,51 @@ public class FhirResourceDaoR4StandardQueriesNoFTTest extends BaseJpaTest { String idAlpha5 = withObservationWithValueQuantity(0.5).getIdPart(); List allIds = myTestDaoSearch.searchForIds("/Observation?_sort=value-quantity"); - assertThat(allIds, hasItems(idAlpha2, idAlpha5, idAlpha7)); + assertThat(allIds, contains(idAlpha2, idAlpha5, idAlpha7)); } } } + @Test + void testQueryByPid() { + + // sentinel for over-match + myDataBuilder.createPatient(); + + String id = myDataBuilder.createPatient( + myDataBuilder.withBirthdate("1971-01-01"), + myDataBuilder.withActiveTrue(), + myDataBuilder.withFamily("Smith")).getIdPart(); + + myTestDaoSearch.assertSearchFindsOnly("search by server assigned id", "Patient?_pid=" + id, id); + myTestDaoSearch.assertSearchFindsOnly("search by server assigned id", "Patient?family=smith&_pid=" + id, id); + } + + @Test + void testSortByPid() { + + String id1 = myDataBuilder.createPatient(myDataBuilder.withFamily("Smithy")).getIdPart(); + String id2 = myDataBuilder.createPatient(myDataBuilder.withFamily("Smithwick")).getIdPart(); + String id3 = myDataBuilder.createPatient(myDataBuilder.withFamily("Smith")).getIdPart(); + + myTestDaoSearch.assertSearchFindsInOrder("sort by server assigned id", "Patient?family=smith&_sort=_pid", id1,id2,id3); + myTestDaoSearch.assertSearchFindsInOrder("reverse sort by server assigned id", "Patient?family=smith&_sort=-_pid", id3,id2,id1); + } + + @Nested + public class IdSearch implements IIdSearchTestTemplate { + @Override + public TestDaoSearch getSearch() { + return myTestDaoSearch; + } + + @Override + public ITestDataBuilder getBuilder() { + return myDataBuilder; + } + } + // todo mb re-enable this. Some of these fail! @Disabled @Nested diff --git a/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/dao/r4/FhirResourceDaoR4TagsInlineTest.java b/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/dao/r4/FhirResourceDaoR4TagsInlineTest.java index 02d829a3b11..0ca7116f5b2 100644 --- a/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/dao/r4/FhirResourceDaoR4TagsInlineTest.java +++ b/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/dao/r4/FhirResourceDaoR4TagsInlineTest.java @@ -12,7 +12,7 @@ import org.hl7.fhir.r4.model.SearchParameter; import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.Test; -import javax.annotation.Nonnull; +import jakarta.annotation.Nonnull; import static ca.uhn.fhir.jpa.dao.r4.FhirResourceDaoR4TagsTest.toProfiles; import static ca.uhn.fhir.jpa.dao.r4.FhirResourceDaoR4TagsTest.toSecurityLabels; diff --git a/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/dao/r4/FhirResourceDaoR4TagsTest.java b/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/dao/r4/FhirResourceDaoR4TagsTest.java index 20fb40ba531..ae5c099e611 100644 --- a/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/dao/r4/FhirResourceDaoR4TagsTest.java +++ b/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/dao/r4/FhirResourceDaoR4TagsTest.java @@ -18,7 +18,7 @@ import org.hl7.fhir.r4.model.SearchParameter; import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.Test; -import javax.annotation.Nonnull; +import jakarta.annotation.Nonnull; import java.util.List; import java.util.stream.Collectors; diff --git a/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/dao/r4/FhirResourceDaoR4Test.java b/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/dao/r4/FhirResourceDaoR4Test.java index d06cba7de2b..649359d3937 100644 --- a/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/dao/r4/FhirResourceDaoR4Test.java +++ b/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/dao/r4/FhirResourceDaoR4Test.java @@ -5,9 +5,11 @@ import ca.uhn.fhir.interceptor.model.RequestPartitionId; import ca.uhn.fhir.jpa.api.config.JpaStorageSettings; import ca.uhn.fhir.jpa.api.dao.IFhirResourceDao; import ca.uhn.fhir.jpa.api.model.HistoryCountModeEnum; +import ca.uhn.fhir.jpa.api.pid.StreamTemplate; import ca.uhn.fhir.jpa.dao.BaseHapiFhirDao; import ca.uhn.fhir.jpa.dao.BaseStorageDao; import ca.uhn.fhir.jpa.dao.JpaResourceDao; +import ca.uhn.fhir.jpa.dao.tx.IHapiTransactionService; import ca.uhn.fhir.jpa.entity.TermConcept; import ca.uhn.fhir.jpa.model.dao.JpaPid; import ca.uhn.fhir.jpa.model.entity.NormalizedQuantitySearchLevel; @@ -33,6 +35,7 @@ import ca.uhn.fhir.rest.api.MethodOutcome; import ca.uhn.fhir.rest.api.SortOrderEnum; import ca.uhn.fhir.rest.api.SortSpec; import ca.uhn.fhir.rest.api.server.IBundleProvider; +import ca.uhn.fhir.rest.api.server.SystemRequestDetails; import ca.uhn.fhir.rest.api.server.storage.IResourcePersistentId; import ca.uhn.fhir.rest.param.DateParam; import ca.uhn.fhir.rest.param.DateRangeParam; @@ -119,24 +122,27 @@ import org.junit.jupiter.api.Tag; import org.junit.jupiter.api.Test; import org.junit.jupiter.params.ParameterizedTest; import org.junit.jupiter.params.provider.ValueSource; +import org.springframework.beans.factory.annotation.Autowired; import org.springframework.transaction.TransactionDefinition; import org.springframework.transaction.TransactionStatus; import org.springframework.transaction.support.TransactionCallbackWithoutResult; import org.springframework.transaction.support.TransactionTemplate; import java.io.IOException; -import java.nio.charset.StandardCharsets; import java.util.ArrayList; import java.util.Collections; import java.util.Comparator; import java.util.Date; import java.util.List; import java.util.Optional; +import java.util.Set; import java.util.UUID; import java.util.concurrent.ExecutionException; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import java.util.concurrent.Future; +import java.util.stream.Collectors; +import java.util.stream.IntStream; import static ca.uhn.fhir.batch2.jobs.termcodesystem.TermCodeSystemJobConfig.TERM_CODE_SYSTEM_VERSION_DELETE_JOB_NAME; import static ca.uhn.fhir.rest.api.Constants.PARAM_HAS; @@ -165,6 +171,9 @@ import static org.junit.jupiter.api.Assertions.fail; @SuppressWarnings({"unchecked", "deprecation", "Duplicates"}) public class FhirResourceDaoR4Test extends BaseJpaR4Test { + @Autowired + IHapiTransactionService myHapiTransactionService; + private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(FhirResourceDaoR4Test.class); @AfterEach @@ -265,7 +274,7 @@ public class FhirResourceDaoR4Test extends BaseJpaR4Test { ResourceHistoryTable newHistory = table.toHistory(true); ResourceHistoryTable currentHistory = myResourceHistoryTableDao.findForIdAndVersionAndFetchProvenance(table.getId(), 1L); newHistory.setEncoding(currentHistory.getEncoding()); - newHistory.setResource(currentHistory.getResource()); + newHistory.setResourceTextVc(currentHistory.getResourceTextVc()); myResourceHistoryTableDao.save(newHistory); }); @@ -2919,7 +2928,7 @@ public class FhirResourceDaoR4Test extends BaseJpaR4Test { ResourceHistoryTable table = myResourceHistoryTableDao.findForIdAndVersionAndFetchProvenance(id.getIdPartAsLong(), 1L); String newContent = myFhirContext.newJsonParser().encodeResourceToString(p); newContent = newContent.replace("male", "foo"); - table.setResource(newContent.getBytes(Charsets.UTF_8)); + table.setResourceTextVc(newContent); table.setEncoding(ResourceEncodingEnum.JSON); myResourceHistoryTableDao.save(table); } @@ -3352,63 +3361,6 @@ public class FhirResourceDaoR4Test extends BaseJpaR4Test { } - @Test - public void testSortById() { - String methodName = "testSortBTyId"; - - Patient p = new Patient(); - p.addIdentifier().setSystem("urn:system").setValue(methodName); - IIdType id1 = myPatientDao.create(p, mySrd).getId().toUnqualifiedVersionless(); - - p = new Patient(); - p.addIdentifier().setSystem("urn:system").setValue(methodName); - IIdType id2 = myPatientDao.create(p, mySrd).getId().toUnqualifiedVersionless(); - - p = new Patient(); - p.setId(methodName + "1"); - p.addIdentifier().setSystem("urn:system").setValue(methodName); - IIdType idMethodName1 = myPatientDao.update(p, mySrd).getId().toUnqualifiedVersionless(); - assertEquals(methodName + "1", idMethodName1.getIdPart()); - - p = new Patient(); - p.addIdentifier().setSystem("urn:system").setValue(methodName); - IIdType id3 = myPatientDao.create(p, mySrd).getId().toUnqualifiedVersionless(); - - p = new Patient(); - p.setId(methodName + "2"); - p.addIdentifier().setSystem("urn:system").setValue(methodName); - IIdType idMethodName2 = myPatientDao.update(p, mySrd).getId().toUnqualifiedVersionless(); - assertEquals(methodName + "2", idMethodName2.getIdPart()); - - p = new Patient(); - p.addIdentifier().setSystem("urn:system").setValue(methodName); - IIdType id4 = myPatientDao.create(p, mySrd).getId().toUnqualifiedVersionless(); - - SearchParameterMap pm; - List actual; - - pm = SearchParameterMap.newSynchronous(); - pm.add(Patient.SP_IDENTIFIER, new TokenParam("urn:system", methodName)); - pm.setSort(new SortSpec(IAnyResource.SP_RES_ID)); - actual = toUnqualifiedVersionlessIds(myPatientDao.search(pm)); - assertEquals(6, actual.size()); - assertThat(actual, contains(idMethodName1, idMethodName2, id1, id2, id3, id4)); - - pm = SearchParameterMap.newSynchronous(); - pm.add(Patient.SP_IDENTIFIER, new TokenParam("urn:system", methodName)); - pm.setSort(new SortSpec(IAnyResource.SP_RES_ID).setOrder(SortOrderEnum.ASC)); - actual = toUnqualifiedVersionlessIds(myPatientDao.search(pm)); - assertEquals(6, actual.size()); - assertThat(actual, contains(idMethodName1, idMethodName2, id1, id2, id3, id4)); - - pm = SearchParameterMap.newSynchronous(); - pm.add(Patient.SP_IDENTIFIER, new TokenParam("urn:system", methodName)); - pm.setSort(new SortSpec(IAnyResource.SP_RES_ID).setOrder(SortOrderEnum.DESC)); - actual = toUnqualifiedVersionlessIds(myPatientDao.search(pm)); - assertEquals(6, actual.size()); - assertThat(actual, contains(id4, id3, id2, id1, idMethodName2, idMethodName1)); - } - @ParameterizedTest @ValueSource(booleans = {true, false}) public void testSortByMissingAttribute(boolean theIndexMissingData) { @@ -4317,6 +4269,30 @@ public class FhirResourceDaoR4Test extends BaseJpaR4Test { assertThat(actualNameList, contains(namesInAlpha)); } + + @Test + void testSearchForStream_carriesTxContext() { + // given + Set createdIds = IntStream.range(1, 5) + .mapToObj(i -> createObservation().getIdPart()) + .collect(Collectors.toSet()); + + SystemRequestDetails request = new SystemRequestDetails(); + + // call within a tx, but carry the tx definition in the StreamTemplate + StreamTemplate> streamTemplate = + StreamTemplate.fromSupplier(() -> myObservationDao.searchForIdStream(new SearchParameterMap(), request, null)) + .withTransactionAdvice(newTxTemplate()); + + + // does the stream work? + Set ids = streamTemplate.call(stream-> + stream.map(typedId->typedId.getId().toString()) + .collect(Collectors.toSet())); + + assertEquals(ids, createdIds); + } + public static void assertConflictException(String theResourceType, ResourceVersionConflictException e) { assertThat(e.getMessage(), matchesPattern( Msg.code(550) + Msg.code(515) + "Unable to delete [a-zA-Z]+/[0-9]+ because at least one resource has a reference to this resource. First reference found was resource " + theResourceType + "/[0-9]+ in path [a-zA-Z]+.[a-zA-Z]+")); diff --git a/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/dao/r4/FhirResourceDaoR4UpdateTest.java b/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/dao/r4/FhirResourceDaoR4UpdateTest.java index 44ae538e139..4f04779cdb8 100644 --- a/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/dao/r4/FhirResourceDaoR4UpdateTest.java +++ b/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/dao/r4/FhirResourceDaoR4UpdateTest.java @@ -45,7 +45,7 @@ import org.junit.jupiter.api.Disabled; import org.junit.jupiter.api.Nested; import org.junit.jupiter.api.Test; -import javax.persistence.Id; +import jakarta.persistence.Id; import java.util.ArrayList; import java.util.Date; import java.util.HashSet; diff --git a/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/dao/r4/FhirResourceDaoR4ValueSetTest.java b/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/dao/r4/FhirResourceDaoR4ValueSetTest.java index 7faf5984eea..0c1b142501c 100644 --- a/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/dao/r4/FhirResourceDaoR4ValueSetTest.java +++ b/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/dao/r4/FhirResourceDaoR4ValueSetTest.java @@ -27,6 +27,7 @@ import org.hl7.fhir.r4.model.UriType; import org.hl7.fhir.r4.model.ValueSet; import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Disabled; import org.junit.jupiter.api.Test; import org.springframework.transaction.annotation.Transactional; diff --git a/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/dao/r4/FhirSystemDaoR4Test.java b/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/dao/r4/FhirSystemDaoR4Test.java index 671416c2cba..93dc9dfc6e3 100644 --- a/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/dao/r4/FhirSystemDaoR4Test.java +++ b/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/dao/r4/FhirSystemDaoR4Test.java @@ -90,7 +90,7 @@ import org.springframework.transaction.support.TransactionCallback; import org.springframework.transaction.support.TransactionCallbackWithoutResult; import org.springframework.transaction.support.TransactionTemplate; -import javax.annotation.Nonnull; +import jakarta.annotation.Nonnull; import java.io.IOException; import java.io.InputStream; import java.io.UnsupportedEncodingException; @@ -620,11 +620,7 @@ public class FhirSystemDaoR4Test extends BaseJpaR4SystemTest { template.execute((TransactionCallback) t -> { ResourceHistoryTable resourceHistoryTable = myResourceHistoryTableDao.findForIdAndVersionAndFetchProvenance(id.getIdPartAsLong(), id.getVersionIdPartAsLong()); resourceHistoryTable.setEncoding(ResourceEncodingEnum.JSON); - try { - resourceHistoryTable.setResource("{\"resourceType\":\"FOO\"}".getBytes("UTF-8")); - } catch (UnsupportedEncodingException e) { - throw new Error(e); - } + resourceHistoryTable.setResourceTextVc("{\"resourceType\":\"FOO\"}"); myResourceHistoryTableDao.save(resourceHistoryTable); ResourceTable table = myResourceTableDao.findById(id.getIdPartAsLong()).orElseThrow(IllegalStateException::new); @@ -1917,11 +1913,11 @@ public class FhirSystemDaoR4Test extends BaseJpaR4SystemTest { Patient p = new Patient(); p.addIdentifier().setSystem("urn:system").setValue(methodName); - myPatientDao.create(p, mySrd).getId(); + myPatientDao.create(p, mySrd); p = new Patient(); p.addIdentifier().setSystem("urn:system").setValue(methodName); - myPatientDao.create(p, mySrd).getId(); + myPatientDao.create(p, mySrd); Observation o = new Observation(); o.getCode().setText("Some Observation"); @@ -4008,7 +4004,7 @@ public class FhirSystemDaoR4Test extends BaseJpaR4SystemTest { try { mySystemDao.transaction(mySrd, b); } catch (ResourceVersionConflictException e) { - assertEquals(Msg.code(550) + Msg.code(550) + Msg.code(989) + "Trying to update Patient/P1/_history/2 but this is not the current version", e.getMessage()); + assertEquals(Msg.code(550) + Msg.code(989) + "Trying to update Patient/P1/_history/2 but this is not the current version", e.getMessage()); } b = new Bundle(); diff --git a/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/dao/r4/PartitioningSqlR4Test.java b/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/dao/r4/PartitioningSqlR4Test.java index eabd086087b..42004dba52f 100644 --- a/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/dao/r4/PartitioningSqlR4Test.java +++ b/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/dao/r4/PartitioningSqlR4Test.java @@ -876,7 +876,7 @@ public class PartitioningSqlR4Test extends BasePartitioningR4Test { ourLog.info("Search SQL:\n{}", searchSql); // Only the read columns should be used, no criteria use partition - assertEquals(2, StringUtils.countMatches(searchSql, "PARTITION_ID as ")); + assertEquals(2, StringUtils.countMatches(searchSql, "PARTITION_ID,")); assertEquals(2, StringUtils.countMatches(searchSql, "PARTITION_ID")); } { @@ -888,7 +888,7 @@ public class PartitioningSqlR4Test extends BasePartitioningR4Test { ourLog.info("Search SQL:\n{}", searchSql); // Only the read columns should be used, no criteria use partition - assertEquals(2, StringUtils.countMatches(searchSql, "PARTITION_ID as ")); + assertEquals(2, StringUtils.countMatches(searchSql, "PARTITION_ID,")); assertEquals(2, StringUtils.countMatches(searchSql, "PARTITION_ID")); } } @@ -910,7 +910,7 @@ public class PartitioningSqlR4Test extends BasePartitioningR4Test { ourLog.info("Search SQL:\n{}", searchSql); // Only the read columns should be used, no criteria use partition - assertEquals(2, StringUtils.countMatches(searchSql, "PARTITION_ID as "), searchSql); + assertEquals(2, StringUtils.countMatches(searchSql, "PARTITION_ID,"), searchSql); assertEquals(1, StringUtils.countMatches(searchSql, "PARTITION_ID='1'"), searchSql); } @@ -954,7 +954,7 @@ public class PartitioningSqlR4Test extends BasePartitioningR4Test { // Only the read columns should be used, but no selectors on partition ID String searchSql = myCaptureQueriesListener.getSelectQueriesForCurrentThread().get(0).getSql(true, true); - assertEquals(2, StringUtils.countMatches(searchSql, "PARTITION_ID as "), searchSql); + assertEquals(2, StringUtils.countMatches(searchSql, "PARTITION_ID,"), searchSql); assertEquals(1, StringUtils.countMatches(searchSql, "PARTITION_ID in ("), searchSql); } @@ -967,7 +967,7 @@ public class PartitioningSqlR4Test extends BasePartitioningR4Test { // Only the read columns should be used, but no selectors on partition ID String searchSql = myCaptureQueriesListener.getSelectQueriesForCurrentThread().get(0).getSql(true, true); - assertEquals(2, StringUtils.countMatches(searchSql, "PARTITION_ID as "), searchSql); + assertEquals(2, StringUtils.countMatches(searchSql, "PARTITION_ID,"), searchSql); assertEquals(1, StringUtils.countMatches(searchSql, "PARTITION_ID is null"), searchSql); } @@ -1008,7 +1008,7 @@ public class PartitioningSqlR4Test extends BasePartitioningR4Test { // Only the read columns should be used, but no selectors on partition ID String searchSql = myCaptureQueriesListener.getSelectQueriesForCurrentThread().get(0).getSql(true, true); - assertEquals(2, StringUtils.countMatches(searchSql, "PARTITION_ID as "), searchSql); + assertEquals(2, StringUtils.countMatches(searchSql, "PARTITION_ID,"), searchSql); assertEquals(1, StringUtils.countMatches(searchSql, "PARTITION_ID in ("), searchSql); } @@ -1022,7 +1022,7 @@ public class PartitioningSqlR4Test extends BasePartitioningR4Test { // Only the read columns should be used, but no selectors on partition ID String searchSql = myCaptureQueriesListener.getSelectQueriesForCurrentThread().get(0).getSql(true, true); - assertEquals(2, StringUtils.countMatches(searchSql, "PARTITION_ID as "), searchSql); + assertEquals(2, StringUtils.countMatches(searchSql, "PARTITION_ID,"), searchSql); assertEquals(1, StringUtils.countMatches(searchSql, "PARTITION_ID is null"), searchSql); } @@ -1064,7 +1064,7 @@ public class PartitioningSqlR4Test extends BasePartitioningR4Test { ourLog.info("Search SQL:\n{}", searchSql); // Only the read columns should be used, no criteria use partition - assertEquals(2, StringUtils.countMatches(searchSql, "PARTITION_ID as ")); + assertEquals(2, StringUtils.countMatches(searchSql, "PARTITION_ID,")); assertEquals(1, StringUtils.countMatches(searchSql, "PARTITION_ID is null")); } @@ -2827,8 +2827,8 @@ public class PartitioningSqlR4Test extends BasePartitioningR4Test { ourLog.debug("Resp: {}", myFhirContext.newJsonParser().setPrettyPrint(true).encodeResourceToString(outcome)); myCaptureQueriesListener.logSelectQueriesForCurrentThread(); assertEquals(2, myCaptureQueriesListener.countSelectQueriesForCurrentThread()); - assertThat(myCaptureQueriesListener.getSelectQueriesForCurrentThread().get(0).getSql(true, false), containsString("esourcein0_.PARTITION_ID in ('1')")); - assertThat(myCaptureQueriesListener.getSelectQueriesForCurrentThread().get(0).getSql(true, false), containsString("HASH_SYS_AND_VALUE in ('7432183691485874662' , '-3772330830566471409'")); + assertThat(myCaptureQueriesListener.getSelectQueriesForCurrentThread().get(0).getSql(true, false), containsString("rispt1_0.PARTITION_ID in ('1')")); + assertThat(myCaptureQueriesListener.getSelectQueriesForCurrentThread().get(0).getSql(true, false), containsString("rispt1_0.HASH_SYS_AND_VALUE in ('7432183691485874662','-3772330830566471409','-4132452001562191669')")); myCaptureQueriesListener.logInsertQueriesForCurrentThread(); assertEquals(45, myCaptureQueriesListener.countInsertQueriesForCurrentThread()); myCaptureQueriesListener.logUpdateQueriesForCurrentThread(); @@ -2986,12 +2986,12 @@ public class PartitioningSqlR4Test extends BasePartitioningR4Test { // Fetch history resource searchSql = myCaptureQueriesListener.getSelectQueriesForCurrentThread().get(1).getSql(true, true); ourLog.info("SQL:{}", searchSql); - assertEquals(1, countMatches(searchSql, "PARTITION_ID in"), searchSql); + assertEquals(1, countMatches(searchSql, "rht1_0.PARTITION_ID in"), searchSql); // Fetch history resource searchSql = myCaptureQueriesListener.getSelectQueriesForCurrentThread().get(2).getSql(true, true); ourLog.info("SQL:{}", searchSql); - assertEquals(1, countMatches(searchSql, "PARTITION_ID in"), searchSql.replace(" ", "").toUpperCase()); + assertEquals(1, countMatches(searchSql, "rht1_0.PARTITION_ID in"), searchSql.replace(" ", "").toUpperCase()); } @Test @@ -3103,12 +3103,12 @@ public class PartitioningSqlR4Test extends BasePartitioningR4Test { ourLog.info("SQL:{}", myCaptureQueriesListener.getSelectQueriesForCurrentThread().get(0).getSql(true, true)); String sql = myCaptureQueriesListener.getSelectQueriesForCurrentThread().get(0).getSql(true, false).toUpperCase(); assertEquals(1, countMatches(sql, "COUNT("), sql); - assertEquals(1, countMatches(sql, "PARTITION_ID IN ('1')"), sql); + assertEquals(1, countMatches(sql, "RHT1_0.PARTITION_ID IN ('1')"), sql); // Fetch history sql = myCaptureQueriesListener.getSelectQueriesForCurrentThread().get(1).getSql(true, false).toUpperCase(); ourLog.info("SQL:{}", sql); - assertEquals(1, countMatches(sql, "PARTITION_ID IN ('1')"), sql); + assertEquals(1, countMatches(sql, "RHT1_0.PARTITION_ID IN ('1')"), sql); } @@ -3134,7 +3134,7 @@ public class PartitioningSqlR4Test extends BasePartitioningR4Test { // Count String searchSql = myCaptureQueriesListener.getSelectQueriesForCurrentThread().get(0).getSql(true, true); ourLog.info("SQL:{}", searchSql); - assertEquals(1, countMatches(searchSql, "PARTITION_ID is null"), searchSql); + assertEquals(1, countMatches(searchSql, "rht1_0.PARTITION_ID is null"), searchSql); // Fetch history resource searchSql = myCaptureQueriesListener.getSelectQueriesForCurrentThread().get(1).getSql(true, true); @@ -3213,12 +3213,12 @@ public class PartitioningSqlR4Test extends BasePartitioningR4Test { String sql = myCaptureQueriesListener.getSelectQueriesForCurrentThread().get(0).getSql(true, false).toUpperCase(); ourLog.info("SQL:{}", sql); assertEquals(1, countMatches(sql, "COUNT("), sql); - assertEquals(1, countMatches(sql, "PARTITION_ID IN ('1')"), sql); + assertEquals(1, countMatches(sql, "RHT1_0.PARTITION_ID IN ('1')"), sql); // History sql = myCaptureQueriesListener.getSelectQueriesForCurrentThread().get(1).getSql(true, false).toUpperCase(); ourLog.info("SQL:{}", sql); - assertEquals(1, countMatches(sql, "PARTITION_ID IN ('1')"), sql); + assertEquals(1, countMatches(sql, "RHT1_0.PARTITION_ID IN ('1')"), sql); } @@ -3244,12 +3244,12 @@ public class PartitioningSqlR4Test extends BasePartitioningR4Test { // Resolve resource String sql = myCaptureQueriesListener.getSelectQueriesForCurrentThread().get(0).getSql(true, true).toUpperCase(); - assertEquals(1, countMatches(sql, "PARTITION_ID IS NULL")); + assertEquals(1, countMatches(sql, "RHT1_0.PARTITION_ID IS NULL"), sql); assertEquals(1, countMatches(sql, "PARTITION_ID")); // Fetch history resource sql = myCaptureQueriesListener.getSelectQueriesForCurrentThread().get(1).getSql(true, true).toUpperCase(); - assertEquals(1, countMatches(sql, "PARTITION_ID IS NULL")); + assertEquals(1, countMatches(sql, "RHT1_0.PARTITION_ID IS NULL"), sql); } diff --git a/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/dao/r4/SearchCoordinatorSvcImplTest.java b/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/dao/r4/SearchCoordinatorSvcImplTest.java index 18657d1d5b0..26ff93cc64f 100644 --- a/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/dao/r4/SearchCoordinatorSvcImplTest.java +++ b/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/dao/r4/SearchCoordinatorSvcImplTest.java @@ -1,7 +1,6 @@ package ca.uhn.fhir.jpa.dao.r4; import ca.uhn.fhir.interceptor.model.RequestPartitionId; -import ca.uhn.fhir.jpa.api.svc.ISearchCoordinatorSvc; import ca.uhn.fhir.jpa.dao.data.ISearchDao; import ca.uhn.fhir.jpa.dao.data.ISearchResultDao; import ca.uhn.fhir.jpa.entity.Search; @@ -16,6 +15,8 @@ import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.Test; import org.springframework.beans.factory.annotation.Autowired; +import java.time.Instant; +import java.time.temporal.ChronoUnit; import java.util.Date; import java.util.UUID; @@ -31,22 +32,20 @@ public class SearchCoordinatorSvcImplTest extends BaseJpaR4Test { @Autowired private ISearchResultDao mySearchResultDao; - @Autowired - private ISearchCoordinatorSvc mySearchCoordinator; - @Autowired private ISearchCacheSvc myDatabaseCacheSvc; @AfterEach public void after() { DatabaseSearchCacheSvcImpl.setMaximumResultsToDeleteInOnePassForUnitTest(DatabaseSearchCacheSvcImpl.DEFAULT_MAX_RESULTS_TO_DELETE_IN_ONE_PAS); - DatabaseSearchCacheSvcImpl.setMaximumSearchesToCheckForDeletionCandidacyForUnitTest(DEFAULT_MAX_DELETE_CANDIDATES_TO_FIND); } + /** + * Semi-obsolete test. This used to test incremental deletion, but we now work until done or a timeout. + */ @Test public void testDeleteDontMarkPreviouslyMarkedSearchesAsDeleted() { DatabaseSearchCacheSvcImpl.setMaximumResultsToDeleteInOnePassForUnitTest(5); - DatabaseSearchCacheSvcImpl.setMaximumSearchesToCheckForDeletionCandidacyForUnitTest(10); runInTransaction(()->{ mySearchResultDao.deleteAll(); @@ -86,28 +85,12 @@ public class SearchCoordinatorSvcImplTest extends BaseJpaR4Test { assertEquals(30, mySearchResultDao.count()); }); - myDatabaseCacheSvc.pollForStaleSearchesAndDeleteThem(RequestPartitionId.allPartitions()); + myDatabaseCacheSvc.pollForStaleSearchesAndDeleteThem(RequestPartitionId.allPartitions(), Instant.now().plus(10, ChronoUnit.SECONDS)); runInTransaction(()->{ // We should delete up to 10, but 3 don't get deleted since they have too many results to delete in one pass - assertEquals(13, mySearchDao.count()); - assertEquals(3, mySearchDao.countDeleted()); - // We delete a max of 5 results per search, so half are gone - assertEquals(15, mySearchResultDao.count()); - }); - - myDatabaseCacheSvc.pollForStaleSearchesAndDeleteThem(RequestPartitionId.allPartitions()); - runInTransaction(()->{ - // Once again we attempt to delete 10, but the first 3 don't get deleted and still remain - // (total is 6 because 3 weren't deleted, and they blocked another 3 that might have been) - assertEquals(6, mySearchDao.count()); - assertEquals(6, mySearchDao.countDeleted()); - assertEquals(0, mySearchResultDao.count()); - }); - - myDatabaseCacheSvc.pollForStaleSearchesAndDeleteThem(RequestPartitionId.allPartitions()); - runInTransaction(()->{ assertEquals(0, mySearchDao.count()); assertEquals(0, mySearchDao.countDeleted()); + // We delete a max of 5 results per search, so half are gone assertEquals(0, mySearchResultDao.count()); }); } diff --git a/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/dao/tx/ExpungeOperationTest.java b/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/dao/tx/ExpungeOperationTest.java new file mode 100644 index 00000000000..1ef53f61b23 --- /dev/null +++ b/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/dao/tx/ExpungeOperationTest.java @@ -0,0 +1,91 @@ +package ca.uhn.fhir.jpa.dao.tx; + +import ca.uhn.fhir.jpa.api.config.JpaStorageSettings; +import ca.uhn.fhir.jpa.api.model.ExpungeOptions; +import ca.uhn.fhir.jpa.dao.expunge.ExpungeOperation; +import ca.uhn.fhir.jpa.dao.expunge.IResourceExpungeService; +import ca.uhn.fhir.jpa.model.dao.JpaPid; +import ca.uhn.fhir.jpa.svc.MockHapiTransactionService; +import ca.uhn.fhir.rest.api.server.RequestDetails; +import ca.uhn.fhir.rest.server.servlet.ServletRequestDetails; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import org.mockito.ArgumentCaptor; +import org.mockito.Captor; +import org.mockito.Mock; +import org.mockito.Spy; +import org.mockito.junit.jupiter.MockitoExtension; + +import java.util.List; + +import static org.hamcrest.MatcherAssert.assertThat; +import static org.hamcrest.Matchers.equalTo; +import static org.hamcrest.Matchers.is; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.anyInt; +import static org.mockito.Mockito.times; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +@ExtendWith(MockitoExtension.class) +public class ExpungeOperationTest { + + @Captor + private ArgumentCaptor myBuilderArgumentCaptor; + @Spy + private MockHapiTransactionService myHapiTransactionService; + private JpaStorageSettings myStorageSettings; + @Mock + private IResourceExpungeService myIResourceExpungeService; + private static final String ourExpectedTenantId = "TenantA"; + + @BeforeEach + public void beforeEach(){ + myStorageSettings = new JpaStorageSettings(); + } + + @Test + public void testExpunge_onSpecificTenant_willPerformExpungeOnSpecificTenant(){ + // given + when(myIResourceExpungeService.findHistoricalVersionsOfDeletedResources(any(), any(), anyInt())).thenReturn(List.of(JpaPid.fromId(1l))); + when(myIResourceExpungeService.findHistoricalVersionsOfNonDeletedResources(any(), any(), anyInt())).thenReturn(List.of(JpaPid.fromId(1l))); + myStorageSettings.setExpungeBatchSize(5); + + RequestDetails requestDetails = getRequestDetails(); + ExpungeOptions expungeOptions = new ExpungeOptions().setExpungeDeletedResources(true).setExpungeOldVersions(true); + + ExpungeOperation expungeOperation = new ExpungeOperation("Patient", null, expungeOptions, requestDetails); + + expungeOperation.setHapiTransactionServiceForTesting(myHapiTransactionService); + expungeOperation.setStorageSettingsForTesting(myStorageSettings); + expungeOperation.setExpungeDaoServiceForTesting(myIResourceExpungeService); + + expungeOperation.call(); + + // then + assertTransactionServiceWasInvokedWithTenantId(ourExpectedTenantId); + } + + private void assertTransactionServiceWasInvokedWithTenantId(String theExpectedTenantId) { + // we have set the expungeOptions to setExpungeDeletedResources and SetExpungeOldVersions to true. + // as a result, we will be making 5 trips to the db. let's make sure that each trip was done with + // the hapiTransaction service and that the tenantId was specified. + verify(myHapiTransactionService, times(5)).doExecute(myBuilderArgumentCaptor.capture(), any()); + List methodArgumentExecutionBuilders = myBuilderArgumentCaptor.getAllValues(); + + boolean allMatching = methodArgumentExecutionBuilders.stream() + .map(HapiTransactionService.ExecutionBuilder::getRequestDetailsForTesting) + .map(RequestDetails::getTenantId) + .allMatch(theExpectedTenantId::equals); + + assertThat(allMatching, is(equalTo(true))); + } + + private RequestDetails getRequestDetails() { + RequestDetails requestDetails = new ServletRequestDetails(); + requestDetails.setTenantId(ourExpectedTenantId); + return requestDetails; + } + +} diff --git a/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/dao/tx/PartitionAwareSupplierTest.java b/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/dao/tx/PartitionAwareSupplierTest.java new file mode 100644 index 00000000000..b9352eceea9 --- /dev/null +++ b/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/dao/tx/PartitionAwareSupplierTest.java @@ -0,0 +1,67 @@ +package ca.uhn.fhir.jpa.dao.tx; + +import ca.uhn.fhir.jpa.dao.expunge.PartitionAwareSupplier; +import ca.uhn.fhir.jpa.svc.MockHapiTransactionService; +import ca.uhn.fhir.rest.api.server.RequestDetails; +import ca.uhn.fhir.rest.api.server.storage.IResourcePersistentId; +import ca.uhn.fhir.rest.server.servlet.ServletRequestDetails; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import org.mockito.ArgumentCaptor; +import org.mockito.Captor; +import org.mockito.Spy; +import org.mockito.junit.jupiter.MockitoExtension; + +import java.util.Collections; +import java.util.List; +import java.util.function.Supplier; + +import static org.hamcrest.MatcherAssert.assertThat; +import static org.hamcrest.Matchers.equalTo; +import static org.hamcrest.Matchers.is; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.Mockito.times; +import static org.mockito.Mockito.verify; + +@ExtendWith(MockitoExtension.class) +public class PartitionAwareSupplierTest { + + @Spy + private MockHapiTransactionService myHapiTransactionService; + + @Captor + private ArgumentCaptor myBuilderArgumentCaptor; + + private static final String ourExpectedTenantId = "TenantA"; + + @Test + public void testMethodFindInPartitionedContext_withRequestDetailsHavingTenantId_willExecuteOnSpecifiedPartition(){ + RequestDetails requestDetails = getRequestDetails(); + + PartitionAwareSupplier partitionAwareSupplier = new PartitionAwareSupplier(myHapiTransactionService, requestDetails); + partitionAwareSupplier.supplyInPartitionedContext(getResourcePersistentIdSupplier()); + + assertTransactionServiceWasInvokedWithTenantId(ourExpectedTenantId); + + } + + private Supplier> getResourcePersistentIdSupplier(){ + return () -> Collections.emptyList(); + } + + private void assertTransactionServiceWasInvokedWithTenantId(String theExpectedTenantId) { + verify(myHapiTransactionService, times(1)).doExecute(myBuilderArgumentCaptor.capture(), any()); + HapiTransactionService.ExecutionBuilder methodArgumentExecutionBuilder = myBuilderArgumentCaptor.getValue(); + + String requestDetailsTenantId = methodArgumentExecutionBuilder.getRequestDetailsForTesting().getTenantId(); + + assertThat(requestDetailsTenantId, is(equalTo(theExpectedTenantId))); + } + + private RequestDetails getRequestDetails() { + RequestDetails requestDetails = new ServletRequestDetails(); + requestDetails.setTenantId(ourExpectedTenantId); + return requestDetails; + } + +} diff --git a/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/delete/ThreadSafeResourceDeleterSvcTest.java b/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/delete/ThreadSafeResourceDeleterSvcTest.java index 7f6ba9f9292..359faa820e0 100644 --- a/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/delete/ThreadSafeResourceDeleterSvcTest.java +++ b/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/delete/ThreadSafeResourceDeleterSvcTest.java @@ -24,7 +24,7 @@ import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.springframework.beans.factory.annotation.Autowired; -import javax.annotation.Nullable; +import jakarta.annotation.Nullable; import java.util.List; import java.util.concurrent.ExecutionException; import java.util.concurrent.ExecutorService; diff --git a/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/delete/job/ReindexJobTest.java b/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/delete/job/ReindexJobTest.java index 8492091e342..77cbd0d21ce 100644 --- a/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/delete/job/ReindexJobTest.java +++ b/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/delete/job/ReindexJobTest.java @@ -30,8 +30,8 @@ import org.junit.jupiter.params.provider.Arguments; import org.junit.jupiter.params.provider.MethodSource; import org.springframework.beans.factory.annotation.Autowired; -import javax.annotation.PostConstruct; -import javax.persistence.Query; +import jakarta.annotation.PostConstruct; +import jakarta.persistence.Query; import java.util.Date; import java.util.List; import java.util.stream.Stream; @@ -87,6 +87,11 @@ public class ReindexJobTest extends BaseJpaR4Test { createPatient(withActiveTrue()); } + // Move resource text to compressed storage, which we don't write to anymore but legacy + // data may exist that was previously stored there, so we're simulating that. + List allHistoryEntities = runInTransaction(() -> myResourceHistoryTableDao.findAll()); + allHistoryEntities.forEach(t->relocateResourceTextToCompressedColumn(t.getResourceId(), t.getVersion())); + runInTransaction(()->{ assertEquals(20, myResourceHistoryTableDao.count()); for (ResourceHistoryTable history : myResourceHistoryTableDao.findAll()) { @@ -141,6 +146,11 @@ public class ReindexJobTest extends BaseJpaR4Test { createPatient(withActiveTrue()); } + // Move resource text to compressed storage, which we don't write to anymore but legacy + // data may exist that was previously stored there, so we're simulating that. + List allHistoryEntities = runInTransaction(() -> myResourceHistoryTableDao.findAll()); + allHistoryEntities.forEach(t->relocateResourceTextToCompressedColumn(t.getResourceId(), t.getVersion())); + runInTransaction(()->{ assertEquals(20, myResourceHistoryTableDao.count()); for (ResourceHistoryTable history : myResourceHistoryTableDao.findAll()) { @@ -149,8 +159,6 @@ public class ReindexJobTest extends BaseJpaR4Test { } }); - myStorageSettings.setInlineResourceTextBelowSize(10000); - // execute JobInstanceStartRequest startRequest = new JobInstanceStartRequest(); startRequest.setJobDefinitionId(ReindexAppCtx.JOB_REINDEX); @@ -448,6 +456,9 @@ public class ReindexJobTest extends BaseJpaR4Test { @Test public void testReindex_withReindexingUponSearchParameterChangeEnabled_reindexJobCompleted() { + List jobInstances = myJobPersistence.fetchInstancesByJobDefinitionId(ReindexAppCtx.JOB_REINDEX, 10, 0); + assertEquals(0, jobInstances.size()); + // make sure the resources auto-reindex after the search parameter update is enabled myStorageSettings.setMarkResourcesForReindexingUponSearchParameterChange(true); @@ -456,7 +467,7 @@ public class ReindexJobTest extends BaseJpaR4Test { myReindexTestHelper.createCodeSearchParameter(); // check that reindex job was created - List jobInstances = myJobPersistence.fetchInstancesByJobDefinitionId(ReindexAppCtx.JOB_REINDEX, 10, 0); + jobInstances = myJobPersistence.fetchInstancesByJobDefinitionId(ReindexAppCtx.JOB_REINDEX, 10, 0); assertEquals(1, jobInstances.size()); // check that the job is completed (not stuck in QUEUED status) diff --git a/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/delete/job/ReindexTestHelper.java b/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/delete/job/ReindexTestHelper.java index a556bda3e08..9ed3490580f 100644 --- a/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/delete/job/ReindexTestHelper.java +++ b/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/delete/job/ReindexTestHelper.java @@ -27,7 +27,7 @@ import org.hl7.fhir.r4.model.StringType; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import javax.annotation.Nonnull; +import jakarta.annotation.Nonnull; import java.util.List; public class ReindexTestHelper { diff --git a/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/interceptor/CascadingDeleteInterceptorMultiThreadTest.java b/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/interceptor/CascadingDeleteInterceptorMultiThreadTest.java index 211b2456d85..e990c57ef1d 100644 --- a/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/interceptor/CascadingDeleteInterceptorMultiThreadTest.java +++ b/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/interceptor/CascadingDeleteInterceptorMultiThreadTest.java @@ -30,8 +30,8 @@ import org.apache.http.impl.client.CloseableHttpClient; import org.apache.http.impl.client.HttpClientBuilder; import org.apache.http.impl.conn.PoolingHttpClientConnectionManager; import org.eclipse.jetty.server.Server; -import org.eclipse.jetty.servlet.ServletContextHandler; -import org.eclipse.jetty.servlet.ServletHolder; +import org.eclipse.jetty.ee10.servlet.ServletContextHandler; +import org.eclipse.jetty.ee10.servlet.ServletHolder; import org.hl7.fhir.instance.model.api.IIdType; import org.hl7.fhir.r4.model.Bundle; import org.hl7.fhir.r4.model.Encounter; diff --git a/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/interceptor/ForceOffsetSearchModeInterceptorTest.java b/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/interceptor/ForceOffsetSearchModeInterceptorTest.java index 716d631232b..9892649972c 100644 --- a/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/interceptor/ForceOffsetSearchModeInterceptorTest.java +++ b/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/interceptor/ForceOffsetSearchModeInterceptorTest.java @@ -13,10 +13,7 @@ import org.junit.jupiter.api.Test; import static org.hamcrest.MatcherAssert.assertThat; import static org.hamcrest.Matchers.containsInAnyOrder; import static org.hamcrest.Matchers.containsString; -import static org.hamcrest.Matchers.empty; -import static org.hamcrest.Matchers.emptyOrNullString; import static org.hamcrest.Matchers.hasSize; -import static org.hamcrest.Matchers.is; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertNull; @@ -66,7 +63,7 @@ public class ForceOffsetSearchModeInterceptorTest extends BaseResourceProviderR4 myCaptureQueriesListener.logSelectQueries(); assertEquals(2, myCaptureQueriesListener.countSelectQueries()); assertThat(myCaptureQueriesListener.getSelectQueries().get(0).getSql(true, false), containsString("SELECT t0.RES_ID FROM HFJ_SPIDX_TOKEN t0")); - assertThat(myCaptureQueriesListener.getSelectQueries().get(0).getSql(true, false), containsString("limit '5'")); + assertThat(myCaptureQueriesListener.getSelectQueries().get(0).getSql(true, false), containsString("fetch first '6' rows only")); assertEquals(0, myCaptureQueriesListener.countInsertQueries()); assertEquals(0, myCaptureQueriesListener.countUpdateQueries()); assertEquals(0, myCaptureQueriesListener.countDeleteQueries()); @@ -91,7 +88,7 @@ public class ForceOffsetSearchModeInterceptorTest extends BaseResourceProviderR4 myCaptureQueriesListener.logSelectQueries(); assertEquals(2, myCaptureQueriesListener.countSelectQueries()); assertThat(myCaptureQueriesListener.getSelectQueries().get(0).getSql(true, false), containsString("SELECT t0.RES_ID FROM HFJ_SPIDX_TOKEN t0")); - assertThat(myCaptureQueriesListener.getSelectQueries().get(0).getSql(true, false), containsString("limit '5'")); + assertThat(myCaptureQueriesListener.getSelectQueries().get(0).getSql(true, false), containsString("fetch next '6' rows only")); assertThat(myCaptureQueriesListener.getSelectQueries().get(0).getSql(true, false), containsString("offset '5'")); assertEquals(0, myCaptureQueriesListener.countInsertQueries()); assertEquals(0, myCaptureQueriesListener.countUpdateQueries()); @@ -99,31 +96,7 @@ public class ForceOffsetSearchModeInterceptorTest extends BaseResourceProviderR4 assertEquals(1, myCaptureQueriesListener.countCommits()); assertEquals(0, myCaptureQueriesListener.countRollbacks()); - assertThat(outcome.getLink("next").getUrl(), containsString("Patient?_count=5&_offset=10&active=true")); - - // Third page (no results) - - myCaptureQueriesListener.clear(); - Bundle outcome3 = myClient - .search() - .forResource("Patient") - .where(Patient.ACTIVE.exactly().code("true")) - .offset(10) - .count(5) - .returnBundle(Bundle.class) - .execute(); - assertThat(toUnqualifiedVersionlessIdValues(outcome3).toString(), toUnqualifiedVersionlessIdValues(outcome3), empty()); - myCaptureQueriesListener.logSelectQueries(); - assertEquals(1, myCaptureQueriesListener.countSelectQueries()); - assertThat(myCaptureQueriesListener.getSelectQueries().get(0).getSql(true, false), containsString("SELECT t0.RES_ID FROM HFJ_SPIDX_TOKEN t0")); - assertThat(myCaptureQueriesListener.getSelectQueries().get(0).getSql(true, false), containsString("limit '5'")); - assertThat(myCaptureQueriesListener.getSelectQueries().get(0).getSql(true, false), containsString("offset '10'")); - assertEquals(0, myCaptureQueriesListener.countInsertQueries()); - assertEquals(0, myCaptureQueriesListener.countUpdateQueries()); - assertEquals(0, myCaptureQueriesListener.countDeleteQueries()); - - assertNull(outcome3.getLink("next"), () -> outcome3.getLink("next").getUrl()); - + assertNull(outcome.getLink("next")); } @Test @@ -148,11 +121,7 @@ public class ForceOffsetSearchModeInterceptorTest extends BaseResourceProviderR4 assertThat(secondPageBundle.getEntry(), hasSize(5)); - Bundle thirdPageBundle = myClient.loadPage().next(secondPageBundle).execute(); - - assertThat(thirdPageBundle.getEntry(), hasSize(0)); - assertNull(thirdPageBundle.getLink("next"), () -> thirdPageBundle.getLink("next").getUrl()); - + assertNull(secondPageBundle.getLink("next")); } @@ -180,7 +149,7 @@ public class ForceOffsetSearchModeInterceptorTest extends BaseResourceProviderR4 myCaptureQueriesListener.logSelectQueries(); assertEquals(2, myCaptureQueriesListener.countSelectQueries()); assertThat(myCaptureQueriesListener.getSelectQueries().get(0).getSql(true, false), containsString("SELECT t0.RES_ID FROM HFJ_SPIDX_TOKEN t0")); - assertThat(myCaptureQueriesListener.getSelectQueries().get(0).getSql(true, false), containsString("limit '7'")); + assertThat(myCaptureQueriesListener.getSelectQueries().get(0).getSql(true, false), containsString("fetch first '8' rows only")); assertEquals(0, myCaptureQueriesListener.countInsertQueries()); assertEquals(0, myCaptureQueriesListener.countUpdateQueries()); assertEquals(0, myCaptureQueriesListener.countDeleteQueries()); @@ -203,7 +172,7 @@ public class ForceOffsetSearchModeInterceptorTest extends BaseResourceProviderR4 myCaptureQueriesListener.logSelectQueries(); assertEquals(2, myCaptureQueriesListener.countSelectQueries()); assertThat(myCaptureQueriesListener.getSelectQueries().get(0).getSql(true, false), containsString("SELECT t0.RES_ID FROM HFJ_SPIDX_TOKEN t0")); - assertThat(myCaptureQueriesListener.getSelectQueries().get(0).getSql(true, false), containsString("limit '7'")); + assertThat(myCaptureQueriesListener.getSelectQueries().get(0).getSql(true, false), containsString("fetch next '8' rows only")); assertEquals(0, myCaptureQueriesListener.countInsertQueries()); assertEquals(0, myCaptureQueriesListener.countUpdateQueries()); assertEquals(0, myCaptureQueriesListener.countDeleteQueries()); diff --git a/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/interceptor/PartitioningInterceptorR4Test.java b/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/interceptor/PartitioningInterceptorR4Test.java index 8bf59f3fd96..9e80739a522 100644 --- a/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/interceptor/PartitioningInterceptorR4Test.java +++ b/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/interceptor/PartitioningInterceptorR4Test.java @@ -39,7 +39,7 @@ import org.junit.jupiter.api.Test; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.mock.web.MockHttpServletRequest; -import javax.servlet.http.HttpServletRequest; +import jakarta.servlet.http.HttpServletRequest; import java.time.LocalDate; import java.util.ArrayList; import java.util.List; diff --git a/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/interceptor/PatientIdPartitionInterceptorTest.java b/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/interceptor/PatientIdPartitionInterceptorTest.java index f760890030a..0e432bbcee5 100644 --- a/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/interceptor/PatientIdPartitionInterceptorTest.java +++ b/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/interceptor/PatientIdPartitionInterceptorTest.java @@ -181,8 +181,8 @@ public class PatientIdPartitionInterceptorTest extends BaseJpaR4SystemTest { assertTrue(patient.getActive()); myCaptureQueriesListener.logSelectQueries(); assertEquals(3, myCaptureQueriesListener.getSelectQueries().size()); - assertThat(myCaptureQueriesListener.getSelectQueries().get(0).getSql(false, false), containsString("forcedid0_.PARTITION_ID in (?)")); - assertThat(myCaptureQueriesListener.getSelectQueries().get(1).getSql(false, false), containsString("where resourceta0_.PARTITION_ID=? and resourceta0_.RES_ID=?")); + assertThat(myCaptureQueriesListener.getSelectQueries().get(0).getSql(false, false), containsString("rt1_0.PARTITION_ID in (?)")); + assertThat(myCaptureQueriesListener.getSelectQueries().get(1).getSql(false, false), containsString("where rt1_0.PARTITION_ID=? and rt1_0.RES_ID=?")); } @Test @@ -229,7 +229,7 @@ public class PatientIdPartitionInterceptorTest extends BaseJpaR4SystemTest { assertEquals(1, outcome.size()); myCaptureQueriesListener.logSelectQueries(); assertEquals(3, myCaptureQueriesListener.getSelectQueries().size()); - assertThat(myCaptureQueriesListener.getSelectQueries().get(0).getSql(false, false), containsString("forcedid0_.PARTITION_ID in (?)")); + assertThat(myCaptureQueriesListener.getSelectQueries().get(0).getSql(false, false), containsString("rt1_0.PARTITION_ID in (?)")); assertThat(myCaptureQueriesListener.getSelectQueries().get(1).getSql(false, false), containsString("t0.PARTITION_ID = ?")); } diff --git a/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/interceptor/ResponseTerminologyTranslationInterceptorTest.java b/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/interceptor/ResponseTerminologyTranslationInterceptorTest.java index f9967311119..f2ab212a437 100644 --- a/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/interceptor/ResponseTerminologyTranslationInterceptorTest.java +++ b/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/interceptor/ResponseTerminologyTranslationInterceptorTest.java @@ -23,7 +23,7 @@ import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.springframework.beans.factory.annotation.Autowired; -import javax.annotation.Nonnull; +import jakarta.annotation.Nonnull; import java.util.Arrays; import java.util.List; import java.util.Map; diff --git a/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/packages/JpaPackageCacheTest.java b/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/packages/JpaPackageCacheTest.java index 3ca0078506c..fdad3b86b7d 100644 --- a/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/packages/JpaPackageCacheTest.java +++ b/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/packages/JpaPackageCacheTest.java @@ -177,6 +177,9 @@ public class JpaPackageCacheTest extends BaseJpaR4Test { // The package has the ID in lower-case, so for the test we input the first parameter in upper-case & check that no error is thrown assertDoesNotThrow(() -> myPackageCacheManager.addPackageToCache(packageNameUppercase, "0.2.0", stream, "hl7.fhir.us.davinci-cdex")); + + // Ensure uninstalling it also works! + assertDoesNotThrow(() -> myPackageCacheManager.uninstallPackage(packageNameUppercase, "0.2.0")); } @Test diff --git a/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/packages/NpmR4Test.java b/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/packages/NpmR4Test.java index 799e6133124..a6a129549e0 100644 --- a/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/packages/NpmR4Test.java +++ b/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/packages/NpmR4Test.java @@ -26,12 +26,13 @@ import ca.uhn.fhir.rest.server.interceptor.partition.RequestTenantPartitionInter import ca.uhn.fhir.rest.server.servlet.ServletRequestDetails; import ca.uhn.fhir.test.utilities.JettyUtil; import ca.uhn.fhir.test.utilities.ProxyUtil; +import ca.uhn.fhir.test.utilities.server.HttpServletExtension; import ca.uhn.fhir.util.ClasspathUtil; import ca.uhn.fhir.util.JsonUtil; import ca.uhn.fhir.validation.ValidationResult; import org.eclipse.jetty.server.Server; -import org.eclipse.jetty.servlet.ServletHandler; -import org.eclipse.jetty.servlet.ServletHolder; +import org.eclipse.jetty.ee10.servlet.ServletHandler; +import org.eclipse.jetty.ee10.servlet.ServletHolder; import org.hl7.fhir.instance.model.api.IBaseResource; import org.hl7.fhir.instance.model.api.IIdType; import org.hl7.fhir.r4.model.Enumerations; @@ -51,6 +52,7 @@ import org.junit.jupiter.api.Disabled; import org.junit.jupiter.api.MethodOrderer; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.TestMethodOrder; +import org.junit.jupiter.api.extension.RegisterExtension; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; @@ -90,18 +92,20 @@ public class NpmR4Test extends BaseJpaR4Test { private IHapiPackageCacheManager myPackageCacheManager; @Autowired private NpmJpaValidationSupport myNpmJpaValidationSupport; - private Server myServer; @Autowired private INpmPackageDao myPackageDao; @Autowired private INpmPackageVersionDao myPackageVersionDao; @Autowired private INpmPackageVersionResourceDao myPackageVersionResourceDao; - private FakeNpmServlet myFakeNpmServlet; @Autowired private IInterceptorService myInterceptorService; @Autowired private RequestTenantPartitionInterceptor myRequestTenantPartitionInterceptor; + private FakeNpmServlet myFakeNpmServlet = new FakeNpmServlet(); + @RegisterExtension + public HttpServletExtension myServer = new HttpServletExtension() + .withServlet(myFakeNpmServlet); @Override @BeforeEach @@ -110,17 +114,8 @@ public class NpmR4Test extends BaseJpaR4Test { JpaPackageCache jpaPackageCache = ProxyUtil.getSingletonTarget(myPackageCacheManager, JpaPackageCache.class); - myServer = new Server(0); - ServletHandler proxyHandler = new ServletHandler(); - myFakeNpmServlet = new FakeNpmServlet(); - ServletHolder servletHolder = new ServletHolder(myFakeNpmServlet); - proxyHandler.addServletWithMapping(servletHolder, "/*"); - myServer.setHandler(proxyHandler); - myServer.start(); - - int port = JettyUtil.getPortForStartedServer(myServer); jpaPackageCache.getPackageServers().clear(); - String url = "http://localhost:" + port; + String url = myServer.getBaseUrl(); ourLog.info("Package server is at base: {}", url); jpaPackageCache.addPackageServer(new PackageServer(url)); @@ -129,7 +124,6 @@ public class NpmR4Test extends BaseJpaR4Test { @AfterEach public void after() throws Exception { - JettyUtil.closeServer(myServer); myStorageSettings.setAllowExternalReferences(new JpaStorageSettings().isAllowExternalReferences()); myStorageSettings.setAutoCreatePlaceholderReferenceTargets(new JpaStorageSettings().isAutoCreatePlaceholderReferenceTargets()); myPartitionSettings.setPartitioningEnabled(false); @@ -219,7 +213,7 @@ public class NpmR4Test extends BaseJpaR4Test { myPackageInstallerSvc.install(spec); // Be sure no further communication with the server - JettyUtil.closeServer(myServer); + myServer.stopServer(); // Make sure we can fetch the package by ID and Version NpmPackage pkg = myPackageCacheManager.loadPackage("nictiz.fhir.nl.stu3.questionnaires", "1.0.2"); @@ -262,7 +256,7 @@ public class NpmR4Test extends BaseJpaR4Test { assertEquals(1, outcome.getResourcesInstalled().get("CodeSystem")); // Be sure no further communication with the server - JettyUtil.closeServer(myServer); + myServer.stopServer(); // Make sure we can fetch the package by ID and Version NpmPackage pkg = myPackageCacheManager.loadPackage("hl7.fhir.uv.shorthand", "0.12.0"); @@ -332,7 +326,7 @@ public class NpmR4Test extends BaseJpaR4Test { assertEquals(1, outcome.getResourcesInstalled().get("CodeSystem")); // Be sure no further communication with the server - JettyUtil.closeServer(myServer); + myServer.stopServer(); // Make sure we can fetch the package by ID and Version NpmPackage pkg = myPackageCacheManager.loadPackage("hl7.fhir.uv.shorthand", "0.12.0"); @@ -402,7 +396,7 @@ public class NpmR4Test extends BaseJpaR4Test { PackageInstallationSpec spec = new PackageInstallationSpec().setName("hl7.fhir.uv.shorthand").setVersion("0.13.0").setInstallMode(PackageInstallationSpec.InstallModeEnum.STORE_AND_INSTALL); PackageInstallOutcomeJson outcome = myPackageInstallerSvc.install(spec); // Be sure no further communication with the server - JettyUtil.closeServer(myServer); + myServer.stopServer(); // Search for the installed resource runInTransaction(() -> { @@ -431,7 +425,7 @@ public class NpmR4Test extends BaseJpaR4Test { assertEquals(3, outcome.getResourcesInstalled().get("Organization")); // Be sure no further communication with the server - JettyUtil.closeServer(myServer); + myServer.stopServer(); // Search for the installed resources runInTransaction(() -> { @@ -471,7 +465,7 @@ public class NpmR4Test extends BaseJpaR4Test { assertEquals(3, outcome.getResourcesInstalled().get("Organization")); // Be sure no further communication with the server - JettyUtil.closeServer(myServer); + myServer.stopServer(); // Search for the installed resources mySrd = mock(ServletRequestDetails.class); @@ -540,7 +534,7 @@ public class NpmR4Test extends BaseJpaR4Test { assertEquals(1, outcome.getResourcesInstalled().get("ImplementationGuide")); // Be sure no further communication with the server - JettyUtil.closeServer(myServer); + myServer.stopServer(); // Search for the installed resources runInTransaction(() -> { @@ -621,7 +615,7 @@ public class NpmR4Test extends BaseJpaR4Test { myPackageInstallerSvc.install(spec); // Be sure no further communication with the server - JettyUtil.closeServer(myServer); + myServer.stopServer(); // Make sure we can fetch the package by ID and Version NpmPackage pkg = myPackageCacheManager.loadPackage("UK.Core.r4", "1.1.0"); @@ -930,7 +924,7 @@ public class NpmR4Test extends BaseJpaR4Test { assertEquals(2, outcome.getResourcesInstalled().get("StructureDefinition")); // Be sure no further communication with the server - JettyUtil.closeServer(myServer); + myServer.stopServer(); // Search for the installed resource runInTransaction(() -> { @@ -967,7 +961,7 @@ public class NpmR4Test extends BaseJpaR4Test { assertEquals(2, outcome.getResourcesInstalled().get("StructureDefinition")); // Be sure no further communication with the server - JettyUtil.closeServer(myServer); + myServer.stopServer(); // Search for the installed resource runInTransaction(() -> { diff --git a/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/packages/NpmSearchR4Test.java b/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/packages/NpmSearchR4Test.java index b8c8a28d8ca..aa06e958181 100644 --- a/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/packages/NpmSearchR4Test.java +++ b/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/packages/NpmSearchR4Test.java @@ -36,8 +36,10 @@ public class NpmSearchR4Test extends BaseJpaR4Test { @Autowired private INpmPackageVersionResourceDao myPackageVersionResourceDao; + @Override @BeforeEach public void before() throws Exception { + super.before(); JpaPackageCache jpaPackageCache = ProxyUtil.getSingletonTarget(myPackageCacheManager, JpaPackageCache.class); jpaPackageCache.getPackageServers().clear(); } @@ -155,7 +157,7 @@ public class NpmSearchR4Test extends BaseJpaR4Test { search = myPackageCacheManager.search(searchSpec); myCaptureQueriesListener.logSelectQueriesForCurrentThread(); - ourLog.info("Search rersults:\r{}", JsonUtil.serialize(search)); + ourLog.info("Search results:\r{}", JsonUtil.serialize(search)); assertEquals(1, search.getTotal()); assertEquals("hl7.fhir.uv.shorthand", search.getObjects().get(0).getPackage().getName()); assertEquals("4.0.1", search.getObjects().get(0).getPackage().getFhirVersion().get(0)); diff --git a/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/packages/PackageInstallerSvcImplCreateTest.java b/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/packages/PackageInstallerSvcImplCreateTest.java index da9b8ee62e0..2000ef23918 100644 --- a/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/packages/PackageInstallerSvcImplCreateTest.java +++ b/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/packages/PackageInstallerSvcImplCreateTest.java @@ -17,12 +17,11 @@ import org.hl7.fhir.utilities.npm.PackageGenerator; import org.junit.jupiter.api.Test; import org.springframework.beans.factory.annotation.Autowired; -import javax.annotation.Nonnull; +import jakarta.annotation.Nonnull; import java.io.ByteArrayOutputStream; import java.io.IOException; import java.nio.charset.StandardCharsets; import java.util.List; -import java.util.stream.Collectors; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertFalse; @@ -53,7 +52,7 @@ public class PackageInstallerSvcImplCreateTest extends BaseJpaR4Test { final NamingSystem namingSystem = new NamingSystem(); namingSystem.getUniqueId().add(new NamingSystem.NamingSystemUniqueIdComponent().setValue("123")); - create(namingSystem); + install(namingSystem); assertEquals(1, myNamingSystemDao.search(SearchParameterMap.newSynchronous(), REQUEST_DETAILS).getAllResources().size()); } @@ -184,7 +183,7 @@ public class PackageInstallerSvcImplCreateTest extends BaseJpaR4Test { } private void createValueSetAndCallCreate(String theOid, String theResourceVersion, String theValueSetVersion, String theUrl, String theCopyright) throws IOException { - create(createValueSet(theOid, theResourceVersion, theValueSetVersion, theUrl, theCopyright)); + install(createValueSet(theOid, theResourceVersion, theValueSetVersion, theUrl, theCopyright)); } @Nonnull @@ -199,8 +198,8 @@ public class PackageInstallerSvcImplCreateTest extends BaseJpaR4Test { return valueSetFromFirstIg; } - private void create(IBaseResource theResource) throws IOException { - mySvc.create(theResource, createInstallationSpec(packageToBytes()), new PackageInstallOutcomeJson()); + private void install(IBaseResource theResource) throws IOException { + mySvc.install(theResource, createInstallationSpec(packageToBytes()), new PackageInstallOutcomeJson()); } @Nonnull diff --git a/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/packages/PackageInstallerSvcImplRewriteHistoryTest.java b/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/packages/PackageInstallerSvcImplRewriteHistoryTest.java index b1c88f6f508..aff55ed8ed3 100644 --- a/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/packages/PackageInstallerSvcImplRewriteHistoryTest.java +++ b/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/packages/PackageInstallerSvcImplRewriteHistoryTest.java @@ -38,7 +38,7 @@ public class PackageInstallerSvcImplRewriteHistoryTest extends BaseJpaR4Test { // execute // red-green this threw a NPE before the fix - mySvc.updateResource(myConceptMapDao, conceptMap); + mySvc.createOrUpdateResource(myConceptMapDao, conceptMap, null); // verify ConceptMap readConceptMap = myConceptMapDao.read(CONCEPT_MAP_TEST_ID); diff --git a/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/packages/loader/PackageLoaderSvcIT.java b/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/packages/loader/PackageLoaderSvcIT.java index b6df9a61732..3c26a4ec46f 100644 --- a/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/packages/loader/PackageLoaderSvcIT.java +++ b/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/packages/loader/PackageLoaderSvcIT.java @@ -4,16 +4,18 @@ import ca.uhn.fhir.context.FhirContext; import ca.uhn.fhir.jpa.packages.FakeNpmServlet; import ca.uhn.fhir.jpa.packages.util.PackageUtils; import ca.uhn.fhir.test.utilities.JettyUtil; +import ca.uhn.fhir.test.utilities.server.HttpServletExtension; import ca.uhn.fhir.util.ClasspathUtil; import org.eclipse.jetty.server.Server; -import org.eclipse.jetty.servlet.ServletHandler; -import org.eclipse.jetty.servlet.ServletHolder; +import org.eclipse.jetty.ee10.servlet.ServletHandler; +import org.eclipse.jetty.ee10.servlet.ServletHolder; import org.hl7.fhir.instance.model.api.IBaseResource; import org.hl7.fhir.utilities.npm.NpmPackage; import org.hl7.fhir.utilities.npm.PackageServer; import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.RegisterExtension; import org.mockito.Mockito; import org.mockito.Spy; @@ -32,40 +34,23 @@ public class PackageLoaderSvcIT { @Spy private FhirContext myFhirContext = FhirContext.forR4Cached(); + private FakeNpmServlet myFakeNpmServlet = new FakeNpmServlet(); + private PackageLoaderSvc myPackageLoaderSvc = new PackageLoaderSvc(); + private PackageResourceParsingSvc myResourceParsingSvc = new PackageResourceParsingSvc(myFhirContext); - private Server myServer; - - private FakeNpmServlet myFakeNpmServlet; - - private PackageLoaderSvc myPackageLoaderSvc; - - private PackageResourceParsingSvc myResourceParsingSvc; + @RegisterExtension + public HttpServletExtension myServer = new HttpServletExtension() + .withServlet(myFakeNpmServlet); @BeforeEach public void before() throws Exception { - myPackageLoaderSvc = new PackageLoaderSvc(); - myResourceParsingSvc = new PackageResourceParsingSvc(myFhirContext); - myServer = new Server(0); - ServletHandler proxyHandler = new ServletHandler(); - myFakeNpmServlet = new FakeNpmServlet(); - ServletHolder servletHolder = new ServletHolder(myFakeNpmServlet); - proxyHandler.addServletWithMapping(servletHolder, "/*"); - myServer.setHandler(proxyHandler); - myServer.start(); - - int port = JettyUtil.getPortForStartedServer(myServer); myPackageLoaderSvc.getPackageServers().clear(); - myPackageLoaderSvc.addPackageServer(new PackageServer("http://localhost:" + port)); + myPackageLoaderSvc.addPackageServer(new PackageServer(myServer.getBaseUrl())); myFakeNpmServlet.getResponses().clear(); } - @AfterEach - public void after() throws Exception { - JettyUtil.closeServer(myServer); - } - @Test public void fetchPackageFromServer_thenParseoutResources_inMemory() throws IOException { // setup diff --git a/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/partition/PartitionManagementProviderTest.java b/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/partition/PartitionManagementProviderTest.java index cbafec0d2ea..792b64985bc 100644 --- a/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/partition/PartitionManagementProviderTest.java +++ b/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/partition/PartitionManagementProviderTest.java @@ -29,7 +29,7 @@ import org.springframework.context.annotation.Configuration; import org.springframework.test.context.ContextConfiguration; import org.springframework.test.context.junit.jupiter.SpringExtension; -import javax.annotation.Nonnull; +import jakarta.annotation.Nonnull; import java.util.ArrayList; import java.util.List; import java.util.stream.Collectors; diff --git a/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/partition/PartitionedSubscriptionTriggeringR4Test.java b/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/partition/PartitionedSubscriptionTriggeringR4Test.java index 1def4fbd776..cfec106c08a 100644 --- a/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/partition/PartitionedSubscriptionTriggeringR4Test.java +++ b/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/partition/PartitionedSubscriptionTriggeringR4Test.java @@ -30,7 +30,7 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; -import javax.servlet.ServletException; +import jakarta.servlet.ServletException; import java.time.LocalDate; import java.time.Month; import java.util.ArrayList; diff --git a/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/patch/FhirPatchApplyR4Test.java b/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/patch/FhirPatchApplyR4Test.java index 87eb166506f..87693e434b6 100644 --- a/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/patch/FhirPatchApplyR4Test.java +++ b/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/patch/FhirPatchApplyR4Test.java @@ -29,8 +29,8 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.test.util.XmlExpectationsHelper; -import javax.annotation.Nonnull; -import javax.annotation.Nullable; +import jakarta.annotation.Nonnull; +import jakarta.annotation.Nullable; import java.util.stream.Collectors; import static org.hamcrest.MatcherAssert.assertThat; diff --git a/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/provider/r4/BinaryStorageInterceptorR4Test.java b/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/provider/r4/BinaryStorageInterceptorR4Test.java index 91d4a521037..273b382d924 100644 --- a/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/provider/r4/BinaryStorageInterceptorR4Test.java +++ b/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/provider/r4/BinaryStorageInterceptorR4Test.java @@ -10,6 +10,7 @@ import ca.uhn.fhir.jpa.binary.interceptor.BinaryStorageInterceptor; import ca.uhn.fhir.jpa.binstore.MemoryBinaryStorageSvcImpl; import ca.uhn.fhir.jpa.model.entity.StorageSettings; import ca.uhn.fhir.jpa.provider.BaseResourceProviderR4Test; +import ca.uhn.fhir.model.primitive.IdDt; import ca.uhn.fhir.rest.api.RestOperationTypeEnum; import ca.uhn.fhir.rest.api.server.RequestDetails; import ca.uhn.fhir.rest.client.api.IClientInterceptor; @@ -18,13 +19,16 @@ import ca.uhn.fhir.rest.client.api.IHttpResponse; import ca.uhn.fhir.rest.server.exceptions.InvalidRequestException; import ca.uhn.fhir.util.HapiExtensions; import org.hl7.fhir.instance.model.api.IBaseHasExtensions; -import org.hl7.fhir.instance.model.api.IBaseMetaType; import org.hl7.fhir.instance.model.api.IBaseResource; import org.hl7.fhir.instance.model.api.IIdType; import org.hl7.fhir.r4.model.Binary; +import org.hl7.fhir.r4.model.Bundle; import org.hl7.fhir.r4.model.DocumentReference; import org.hl7.fhir.r4.model.Enumerations; import org.hl7.fhir.r4.model.Extension; +import org.hl7.fhir.r4.model.IdType; +import org.hl7.fhir.r4.model.Patient; +import org.hl7.fhir.r4.model.Resource; import org.hl7.fhir.r4.model.StringType; import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.BeforeEach; @@ -32,7 +36,6 @@ import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.ExtendWith; import org.junit.jupiter.params.ParameterizedTest; import org.junit.jupiter.params.provider.EnumSource; -import org.junit.jupiter.params.provider.ValueSource; import org.mockito.junit.jupiter.MockitoExtension; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -62,6 +65,7 @@ public class BinaryStorageInterceptorR4Test extends BaseResourceProviderR4Test { public static final byte[] FEW_BYTES = {4, 3, 2, 1}; public static final byte[] SOME_BYTES = {1, 2, 3, 4, 5, 6, 7, 8, 7, 6, 5, 4, 3, 2, 1, 8, 9, 0, 10, 9}; public static final byte[] SOME_BYTES_2 = {6, 7, 8, 7, 6, 5, 4, 3, 2, 1, 5, 5, 5, 6}; + public static final byte[] SOME_BYTES_3 = {5, 5, 5, 6, 6, 7, 7, 7, 8, 8, 8}; private static final Logger ourLog = LoggerFactory.getLogger(BinaryStorageInterceptorR4Test.class); @Autowired @@ -381,12 +385,8 @@ public class BinaryStorageInterceptorR4Test extends BaseResourceProviderR4Test { // Create a resource with a big enough docRef DocumentReference docRef = new DocumentReference(); - DocumentReference.DocumentReferenceContentComponent content = docRef.addContent(); - content.getAttachment().setContentType("application/octet-stream"); - content.getAttachment().setData(SOME_BYTES); - DocumentReference.DocumentReferenceContentComponent content2 = docRef.addContent(); - content2.getAttachment().setContentType("application/octet-stream"); - content2.getAttachment().setData(SOME_BYTES_2); + addDocumentAttachmentData(docRef, SOME_BYTES); + addDocumentAttachmentData(docRef, SOME_BYTES_2); DaoMethodOutcome outcome = myDocumentReferenceDao.create(docRef, mySrd); // Make sure it was externalized @@ -422,18 +422,73 @@ public class BinaryStorageInterceptorR4Test extends BaseResourceProviderR4Test { } + @Test + public void testCreateBinaryAttachments_bundleWithMultipleDocumentReferences_createdAndReadBackSuccessfully() { + // Create Patient + Patient patient = new Patient(); + patient.addIdentifier().setSystem("urn:system").setValue("001"); + patient.addName().addGiven("Johnny").setFamily("Walker"); + + // Create first DocumentReference with a big enough attachments + DocumentReference docRef = new DocumentReference(); + addDocumentAttachmentData(docRef, SOME_BYTES); + addDocumentAttachmentData(docRef, SOME_BYTES_2); + + // Create second DocumentReference with a big enough attachment + DocumentReference docRef2 = new DocumentReference(); + addDocumentAttachmentData(docRef2, SOME_BYTES_3); + + // Create Bundle + Bundle bundle = new Bundle(); + bundle.setType(Bundle.BundleType.TRANSACTION); + // Patient entry component + addBundleEntry(bundle, patient, "Patient"); + // First DocumentReference entry component + addBundleEntry(bundle, docRef, "DocumentReference"); + // Second DocumentReference entry component + addBundleEntry(bundle, docRef2, "DocumentReference"); + + // Execute transaction + Bundle output = myClient.transaction().withBundle(bundle).execute(); + ourLog.debug(myFhirContext.newXmlParser().setPrettyPrint(true).encodeResourceToString(output)); + + // Verify bundle response + assertEquals(3, output.getEntry().size()); + output.getEntry().forEach(entry -> assertEquals("201 Created", entry.getResponse().getStatus())); + + // Read back and verify first DocumentReference and attachments + IIdType firstDocRef = new IdType(output.getEntry().get(1).getResponse().getLocation()); + DocumentReference firstDoc = myDocumentReferenceDao.read(firstDocRef, mySrd); + assertEquals("application/octet-stream", firstDoc.getContentFirstRep().getAttachment().getContentType()); + assertArrayEquals(SOME_BYTES, firstDoc.getContentFirstRep().getAttachment().getData()); + assertEquals("application/octet-stream", firstDoc.getContent().get(1).getAttachment().getContentType()); + assertArrayEquals(SOME_BYTES_2, firstDoc.getContent().get(1).getAttachment().getData()); + + // Read back and verify second DocumentReference and attachment + IIdType secondDocRef = new IdType(output.getEntry().get(2).getResponse().getLocation()); + DocumentReference secondDoc = myDocumentReferenceDao.read(secondDocRef, mySrd); + assertEquals("application/octet-stream", secondDoc.getContentFirstRep().getAttachment().getContentType()); + assertArrayEquals(SOME_BYTES_3, secondDoc.getContentFirstRep().getAttachment().getData()); + } + + private void addBundleEntry(Bundle theBundle, Resource theResource, String theUrl) { + Bundle.BundleEntryComponent getComponent = new Bundle.BundleEntryComponent(); + Bundle.BundleEntryRequestComponent requestComponent = new Bundle.BundleEntryRequestComponent(); + requestComponent.setMethod(Bundle.HTTPVerb.POST); + requestComponent.setUrl(theUrl); + getComponent.setRequest(requestComponent); + getComponent.setResource(theResource); + getComponent.setFullUrl(IdDt.newRandomUuid().getValue()); + theBundle.addEntry(getComponent); + } @Test public void testUpdateRejectsIncorrectBinary() { // Create a resource with a big enough docRef DocumentReference docRef = new DocumentReference(); - DocumentReference.DocumentReferenceContentComponent content = docRef.addContent(); - content.getAttachment().setContentType("application/octet-stream"); - content.getAttachment().setData(SOME_BYTES); - DocumentReference.DocumentReferenceContentComponent content2 = docRef.addContent(); - content2.getAttachment().setContentType("application/octet-stream"); - content2.getAttachment().setData(SOME_BYTES_2); + addDocumentAttachmentData(docRef, SOME_BYTES); + addDocumentAttachmentData(docRef, SOME_BYTES_2); DaoMethodOutcome outcome = myDocumentReferenceDao.create(docRef, mySrd); // Make sure it was externalized @@ -449,13 +504,13 @@ public class BinaryStorageInterceptorR4Test extends BaseResourceProviderR4Test { docRef = new DocumentReference(); docRef.setId(id.toUnqualifiedVersionless()); docRef.setStatus(Enumerations.DocumentReferenceStatus.CURRENT); - content = docRef.addContent(); + DocumentReference.DocumentReferenceContentComponent content = docRef.addContent(); content.getAttachment().setContentType("application/octet-stream"); content.getAttachment().getDataElement().addExtension( HapiExtensions.EXT_EXTERNALIZED_BINARY_ID, new StringType(binaryId) ); - content2 = docRef.addContent(); + DocumentReference.DocumentReferenceContentComponent content2 = docRef.addContent(); content2.getAttachment().setContentType("application/octet-stream"); content2.getAttachment().getDataElement().addExtension( HapiExtensions.EXT_EXTERNALIZED_BINARY_ID, @@ -497,5 +552,10 @@ public class BinaryStorageInterceptorR4Test extends BaseResourceProviderR4Test { } + private void addDocumentAttachmentData(DocumentReference theDocumentReference, byte[] theData) { + DocumentReference.DocumentReferenceContentComponent content = theDocumentReference.addContent(); + content.getAttachment().setContentType("application/octet-stream"); + content.getAttachment().setData(theData); + } } diff --git a/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/provider/r4/BulkExportProviderR4Test.java b/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/provider/r4/BulkExportProviderR4Test.java index 45c93f32803..d0a1c576f1c 100644 --- a/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/provider/r4/BulkExportProviderR4Test.java +++ b/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/provider/r4/BulkExportProviderR4Test.java @@ -1,15 +1,12 @@ package ca.uhn.fhir.jpa.provider.r4; import ca.uhn.fhir.jpa.provider.BaseResourceProviderR4Test; -import ca.uhn.fhir.model.primitive.IdDt; -import ca.uhn.fhir.model.primitive.StringDt; -import ca.uhn.fhir.rest.api.MethodOutcome; import ca.uhn.fhir.rest.server.exceptions.ResourceNotFoundException; import org.hl7.fhir.r4.model.Parameters; import org.hl7.fhir.r4.model.StringType; import org.junit.jupiter.api.Test; -import static ca.uhn.fhir.jpa.model.util.JpaConstants.OPERATION_EXPORT; +import static ca.uhn.fhir.rest.server.provider.ProviderConstants.OPERATION_EXPORT; import static org.hamcrest.MatcherAssert.assertThat; import static org.hamcrest.Matchers.equalTo; import static org.junit.jupiter.api.Assertions.assertThrows; diff --git a/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/provider/r4/ConsentInterceptorResourceProviderR4IT.java b/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/provider/r4/ConsentInterceptorResourceProviderR4IT.java index 6fd56b30ee4..52e288f4727 100644 --- a/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/provider/r4/ConsentInterceptorResourceProviderR4IT.java +++ b/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/provider/r4/ConsentInterceptorResourceProviderR4IT.java @@ -12,6 +12,7 @@ import ca.uhn.fhir.rest.api.Constants; import ca.uhn.fhir.rest.api.PreferReturnEnum; import ca.uhn.fhir.rest.api.RestOperationTypeEnum; import ca.uhn.fhir.rest.api.server.RequestDetails; +import ca.uhn.fhir.rest.client.api.IHttpResponse; import ca.uhn.fhir.rest.client.interceptor.CapturingInterceptor; import ca.uhn.fhir.rest.gclient.StringClientParam; import ca.uhn.fhir.rest.server.exceptions.BaseServerResponseException; @@ -66,12 +67,15 @@ import java.util.Arrays; import java.util.List; import java.util.stream.Collectors; +import static org.apache.commons.lang3.StringUtils.isEmpty; import static org.apache.commons.lang3.StringUtils.leftPad; import static org.awaitility.Awaitility.await; -import static org.hamcrest.CoreMatchers.containsString; -import static org.hamcrest.CoreMatchers.equalTo; -import static org.hamcrest.CoreMatchers.is; -import static org.hamcrest.CoreMatchers.not; +import static org.hamcrest.Matchers.containsString; +import static org.hamcrest.Matchers.equalTo; +import static org.hamcrest.Matchers.hasItem; +import static org.hamcrest.Matchers.is; +import static org.hamcrest.Matchers.empty; +import static org.hamcrest.Matchers.not; import static org.hamcrest.MatcherAssert.assertThat; import static org.hamcrest.Matchers.blankOrNullString; import static org.hamcrest.Matchers.hasSize; @@ -189,8 +193,64 @@ public class ConsentInterceptorResourceProviderR4IT extends BaseResourceProvider myServer.getRestfulServer().getInterceptorService().registerInterceptor(myConsentInterceptor); // Perform a search and only allow even + String context = "active consent - hide odd"; consentService.setTarget(new ConsentSvcCantSeeOddNumbered()); - Bundle result = myClient + List returnedIdValues = searchForObservations(); + assertEquals(myObservationIdsEvenOnly.subList(0, 15), returnedIdValues); + assertResponseIsNotFromCache(context, capture.getLastResponse()); + + // Perform a search and only allow odd + context = "active consent - hide even"; + consentService.setTarget(new ConsentSvcCantSeeEvenNumbered()); + returnedIdValues = searchForObservations(); + assertEquals(myObservationIdsOddOnly.subList(0, 15), returnedIdValues); + assertResponseIsNotFromCache(context, capture.getLastResponse()); + + // Perform a search and allow all with a PROCEED + context = "active consent - PROCEED on cache"; + consentService.setTarget(new ConsentSvcNop(ConsentOperationStatusEnum.PROCEED)); + returnedIdValues = searchForObservations(); + assertEquals(myObservationIds.subList(0, 15), returnedIdValues); + assertResponseIsNotFromCache(context, capture.getLastResponse()); + + // Perform a search and allow all with an AUTHORIZED (no further checking) + context = "active consent - AUTHORIZED after a PROCEED"; + consentService.setTarget(new ConsentSvcNop(ConsentOperationStatusEnum.AUTHORIZED)); + returnedIdValues = searchForObservations(); + assertEquals(myObservationIds.subList(0, 15), returnedIdValues); + + // Perform a second search and allow all with an AUTHORIZED (no further checking) + // which means we should finally get one from the cache + context = "active consent - AUTHORIZED after AUTHORIZED"; + consentService.setTarget(new ConsentSvcNop(ConsentOperationStatusEnum.AUTHORIZED)); + returnedIdValues = searchForObservations(); + assertEquals(myObservationIds.subList(0, 15), returnedIdValues); + assertResponseIsFromCache(context, capture.getLastResponse()); + + // Perform another search, now with an active consent interceptor that promises not to use canSeeResource. + // Should re-use cache result + context = "active consent - canSeeResource disabled, after AUTHORIZED - should reuse cache"; + consentService.setTarget(new ConsentSvcNop(ConsentOperationStatusEnum.PROCEED, false)); + returnedIdValues = searchForObservations(); + assertEquals(myObservationIds.subList(0, 15), returnedIdValues); + assertResponseIsFromCache(context, capture.getLastResponse()); + + myClient.unregisterInterceptor(capture); + } + + private static void assertResponseIsNotFromCache(String theContext, IHttpResponse lastResponse) { + List cacheOutcome= lastResponse.getHeaders(Constants.HEADER_X_CACHE); + assertThat(theContext + " - No cache response headers", cacheOutcome, empty()); + } + + private static void assertResponseIsFromCache(String theContext, IHttpResponse lastResponse) { + List cacheOutcome = lastResponse.getHeaders(Constants.HEADER_X_CACHE); + assertThat(theContext + " - Response came from cache", cacheOutcome, hasItem(matchesPattern("^HIT from .*"))); + } + + private List searchForObservations() { + Bundle result; + result = myClient .search() .forResource("Observation") .sort() @@ -199,77 +259,7 @@ public class ConsentInterceptorResourceProviderR4IT extends BaseResourceProvider .count(15) .execute(); List resources = BundleUtil.toListOfResources(myFhirContext, result); - List returnedIdValues = toUnqualifiedVersionlessIdValues(resources); - assertEquals(myObservationIdsEvenOnly.subList(0, 15), returnedIdValues); - List cacheOutcome = capture.getLastResponse().getHeaders(Constants.HEADER_X_CACHE); - assertEquals(0, cacheOutcome.size()); - - // Perform a search and only allow odd - consentService.setTarget(new ConsentSvcCantSeeEvenNumbered()); - result = myClient - .search() - .forResource("Observation") - .sort() - .ascending(Observation.SP_IDENTIFIER) - .returnBundle(Bundle.class) - .count(15) - .execute(); - resources = BundleUtil.toListOfResources(myFhirContext, result); - returnedIdValues = toUnqualifiedVersionlessIdValues(resources); - assertEquals(myObservationIdsOddOnly.subList(0, 15), returnedIdValues); - cacheOutcome = capture.getLastResponse().getHeaders(Constants.HEADER_X_CACHE); - assertEquals(0, cacheOutcome.size()); - - // Perform a search and allow all with a PROCEED - consentService.setTarget(new ConsentSvcNop(ConsentOperationStatusEnum.PROCEED)); - result = myClient - .search() - .forResource("Observation") - .sort() - .ascending(Observation.SP_IDENTIFIER) - .returnBundle(Bundle.class) - .count(15) - .execute(); - resources = BundleUtil.toListOfResources(myFhirContext, result); - returnedIdValues = toUnqualifiedVersionlessIdValues(resources); - assertEquals(myObservationIds.subList(0, 15), returnedIdValues); - cacheOutcome = capture.getLastResponse().getHeaders(Constants.HEADER_X_CACHE); - assertEquals(0, cacheOutcome.size()); - - // Perform a search and allow all with an AUTHORIZED (no further checking) - consentService.setTarget(new ConsentSvcNop(ConsentOperationStatusEnum.AUTHORIZED)); - result = myClient - .search() - .forResource("Observation") - .sort() - .ascending(Observation.SP_IDENTIFIER) - .returnBundle(Bundle.class) - .count(15) - .execute(); - resources = BundleUtil.toListOfResources(myFhirContext, result); - returnedIdValues = toUnqualifiedVersionlessIdValues(resources); - assertEquals(myObservationIds.subList(0, 15), returnedIdValues); - cacheOutcome = capture.getLastResponse().getHeaders(Constants.HEADER_X_CACHE); - assertEquals(0, cacheOutcome.size()); - - // Perform a second search and allow all with an AUTHORIZED (no further checking) - // which means we should finally get one from the cache - consentService.setTarget(new ConsentSvcNop(ConsentOperationStatusEnum.AUTHORIZED)); - result = myClient - .search() - .forResource("Observation") - .sort() - .ascending(Observation.SP_IDENTIFIER) - .returnBundle(Bundle.class) - .count(15) - .execute(); - resources = BundleUtil.toListOfResources(myFhirContext, result); - returnedIdValues = toUnqualifiedVersionlessIdValues(resources); - assertEquals(myObservationIds.subList(0, 15), returnedIdValues); - cacheOutcome = capture.getLastResponse().getHeaders(Constants.HEADER_X_CACHE); - assertThat(cacheOutcome.get(0), matchesPattern("^HIT from .*")); - - myClient.unregisterInterceptor(capture); + return toUnqualifiedVersionlessIdValues(resources); } @Test @@ -528,6 +518,7 @@ public class ConsentInterceptorResourceProviderR4IT extends BaseResourceProvider IConsentService svc = mock(IConsentService.class); when(svc.startOperation(any(), any())).thenReturn(ConsentOutcome.PROCEED); + when(svc.shouldProcessCanSeeResource(any(), any())).thenReturn(true); when(svc.canSeeResource(any(), any(), any())).thenReturn(ConsentOutcome.REJECT); consentService.setTarget(svc); @@ -560,6 +551,7 @@ public class ConsentInterceptorResourceProviderR4IT extends BaseResourceProvider IConsentService svc = mock(IConsentService.class); when(svc.startOperation(any(), any())).thenReturn(ConsentOutcome.PROCEED); + when(svc.shouldProcessCanSeeResource(any(), any())).thenReturn(true); when(svc.canSeeResource(any(RequestDetails.class), any(IBaseResource.class), any())).thenAnswer(t -> { IBaseResource resource = t.getArgument(1, IBaseResource.class); if (resource instanceof Organization) { @@ -998,16 +990,27 @@ public class ConsentInterceptorResourceProviderR4IT extends BaseResourceProvider private static class ConsentSvcNop implements IConsentService { private final ConsentOperationStatusEnum myOperationStatus; + private boolean myEnableCanSeeResource = true; private ConsentSvcNop(ConsentOperationStatusEnum theOperationStatus) { myOperationStatus = theOperationStatus; } + private ConsentSvcNop(ConsentOperationStatusEnum theOperationStatus, boolean theEnableCanSeeResource) { + myOperationStatus = theOperationStatus; + myEnableCanSeeResource = theEnableCanSeeResource; + } + @Override public ConsentOutcome startOperation(RequestDetails theRequestDetails, IConsentContextServices theContextServices) { return new ConsentOutcome(myOperationStatus); } + @Override + public boolean shouldProcessCanSeeResource(RequestDetails theRequestDetails, IConsentContextServices theContextServices) { + return myEnableCanSeeResource; + } + @Override public ConsentOutcome canSeeResource(RequestDetails theRequestDetails, IBaseResource theResource, IConsentContextServices theContextServices) { return new ConsentOutcome(ConsentOperationStatusEnum.PROCEED); diff --git a/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/provider/r4/EmptyIndexesR4Test.java b/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/provider/r4/EmptyIndexesR4Test.java index 8619423aa9c..f84f607ef89 100644 --- a/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/provider/r4/EmptyIndexesR4Test.java +++ b/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/provider/r4/EmptyIndexesR4Test.java @@ -15,8 +15,8 @@ import org.apache.http.impl.client.CloseableHttpClient; import org.apache.http.impl.client.HttpClientBuilder; import org.apache.http.impl.conn.PoolingHttpClientConnectionManager; import org.eclipse.jetty.server.Server; -import org.eclipse.jetty.servlet.ServletContextHandler; -import org.eclipse.jetty.servlet.ServletHolder; +import org.eclipse.jetty.ee10.servlet.ServletContextHandler; +import org.eclipse.jetty.ee10.servlet.ServletHolder; import org.hl7.fhir.r4.model.Observation; import org.junit.jupiter.api.AfterAll; import org.junit.jupiter.api.AfterEach; diff --git a/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/provider/r4/MultitenantServerR4Test.java b/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/provider/r4/MultitenantServerR4Test.java index 64d0faeba73..e96a313c7a4 100644 --- a/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/provider/r4/MultitenantServerR4Test.java +++ b/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/provider/r4/MultitenantServerR4Test.java @@ -23,6 +23,8 @@ import ca.uhn.fhir.rest.server.RestfulServer; import ca.uhn.fhir.rest.server.exceptions.InvalidRequestException; import ca.uhn.fhir.rest.server.exceptions.MethodNotAllowedException; import ca.uhn.fhir.rest.server.exceptions.ResourceNotFoundException; +import ca.uhn.fhir.rest.server.interceptor.auth.SearchNarrowingInterceptor; +import ca.uhn.fhir.rest.server.provider.ProviderConstants; import ca.uhn.fhir.rest.server.servlet.ServletRequestDetails; import ca.uhn.fhir.test.utilities.ITestDataBuilder; import ca.uhn.fhir.util.JsonUtil; @@ -39,17 +41,20 @@ import org.hl7.fhir.r4.model.Condition; import org.hl7.fhir.r4.model.IdType; import org.hl7.fhir.r4.model.Organization; import org.hl7.fhir.r4.model.Patient; +import org.hl7.fhir.r4.model.Resource; import org.hl7.fhir.r4.model.StringType; import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Nested; import org.junit.jupiter.api.Test; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.ValueSource; import org.mockito.InjectMocks; import org.mockito.Mock; import org.mockito.Spy; import org.springframework.mock.web.MockHttpServletRequest; -import javax.servlet.http.HttpServletResponse; +import jakarta.servlet.http.HttpServletResponse; import java.io.IOException; import java.nio.charset.StandardCharsets; import java.util.ArrayList; @@ -64,6 +69,7 @@ import static org.hamcrest.Matchers.containsString; import static org.hamcrest.Matchers.hasSize; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertNotNull; import static org.junit.jupiter.api.Assertions.assertNull; import static org.junit.jupiter.api.Assertions.assertTrue; import static org.junit.jupiter.api.Assertions.fail; @@ -180,7 +186,7 @@ public class MultitenantServerR4Test extends BaseMultitenantResourceProviderR4Te // Search and include deleted runInTransaction(() -> { - Collection forcedIds = myForcedIdDao.findAndResolveByForcedIdWithNoTypeIncludeDeleted( + Collection forcedIds = myResourceTableDao.findAndResolveByForcedIdWithNoTypeIncludeDeleted( "Patient", Arrays.asList(patientId) ); assertContainsSingleForcedId(forcedIds, patientId); @@ -188,7 +194,7 @@ public class MultitenantServerR4Test extends BaseMultitenantResourceProviderR4Te // Search and filter deleted runInTransaction(() -> { - Collection forcedIds = myForcedIdDao.findAndResolveByForcedIdWithNoType( + Collection forcedIds = myResourceTableDao.findAndResolveByForcedIdWithNoType( "Patient", Arrays.asList(patientId), true ); assertContainsSingleForcedId(forcedIds, patientId); @@ -199,7 +205,7 @@ public class MultitenantServerR4Test extends BaseMultitenantResourceProviderR4Te // Search and include deleted runInTransaction(() -> { - Collection forcedIds = myForcedIdDao.findAndResolveByForcedIdWithNoTypeIncludeDeleted( + Collection forcedIds = myResourceTableDao.findAndResolveByForcedIdWithNoTypeIncludeDeleted( "Patient", Arrays.asList(patientId) ); assertContainsSingleForcedId(forcedIds, patientId); @@ -207,7 +213,7 @@ public class MultitenantServerR4Test extends BaseMultitenantResourceProviderR4Te // Search and filter deleted runInTransaction(() -> { - Collection forcedIds = myForcedIdDao.findAndResolveByForcedIdWithNoType( + Collection forcedIds = myResourceTableDao.findAndResolveByForcedIdWithNoType( "Patient", Arrays.asList(patientId), true ); assertThat(forcedIds, hasSize(0)); @@ -222,7 +228,7 @@ public class MultitenantServerR4Test extends BaseMultitenantResourceProviderR4Te // Search and include deleted runInTransaction(() -> { - Collection forcedIds = myForcedIdDao.findAndResolveByForcedIdWithNoTypeInPartitionNull( + Collection forcedIds = myResourceTableDao.findAndResolveByForcedIdWithNoTypeInPartitionNull( "Patient", Arrays.asList(patientId), false ); assertContainsSingleForcedId(forcedIds, patientId); @@ -230,7 +236,7 @@ public class MultitenantServerR4Test extends BaseMultitenantResourceProviderR4Te // Search and filter deleted runInTransaction(() -> { - Collection forcedIds = myForcedIdDao.findAndResolveByForcedIdWithNoTypeInPartitionNull( + Collection forcedIds = myResourceTableDao.findAndResolveByForcedIdWithNoTypeInPartitionNull( "Patient", Arrays.asList(patientId), true ); assertContainsSingleForcedId(forcedIds, patientId); @@ -241,7 +247,7 @@ public class MultitenantServerR4Test extends BaseMultitenantResourceProviderR4Te // Search and include deleted runInTransaction(() -> { - Collection forcedIds = myForcedIdDao.findAndResolveByForcedIdWithNoTypeInPartitionNull( + Collection forcedIds = myResourceTableDao.findAndResolveByForcedIdWithNoTypeInPartitionNull( "Patient", Arrays.asList(patientId), false ); assertContainsSingleForcedId(forcedIds, patientId); @@ -249,7 +255,7 @@ public class MultitenantServerR4Test extends BaseMultitenantResourceProviderR4Te // Search and filter deleted runInTransaction(() -> { - Collection forcedIds = myForcedIdDao.findAndResolveByForcedIdWithNoTypeInPartitionNull( + Collection forcedIds = myResourceTableDao.findAndResolveByForcedIdWithNoTypeInPartitionNull( "Patient", Arrays.asList(patientId), true ); assertEquals(0, forcedIds.size()); @@ -266,7 +272,7 @@ public class MultitenantServerR4Test extends BaseMultitenantResourceProviderR4Te // Search and include deleted runInTransaction(() -> { - Collection forcedIds = myForcedIdDao.findAndResolveByForcedIdWithNoTypeInPartitionIdOrNullPartitionId( + Collection forcedIds = myResourceTableDao.findAndResolveByForcedIdWithNoTypeInPartitionIdOrNullPartitionId( "Patient", Arrays.asList(patientId), Arrays.asList(TENANT_A_ID), false ); assertContainsSingleForcedId(forcedIds, patientId); @@ -274,7 +280,7 @@ public class MultitenantServerR4Test extends BaseMultitenantResourceProviderR4Te // Search and filter deleted runInTransaction(() -> { - Collection forcedIds = myForcedIdDao.findAndResolveByForcedIdWithNoTypeInPartitionIdOrNullPartitionId( + Collection forcedIds = myResourceTableDao.findAndResolveByForcedIdWithNoTypeInPartitionIdOrNullPartitionId( "Patient", Arrays.asList(patientId), Arrays.asList(TENANT_A_ID), true ); assertContainsSingleForcedId(forcedIds, patientId); @@ -284,7 +290,7 @@ public class MultitenantServerR4Test extends BaseMultitenantResourceProviderR4Te // Search and include deleted runInTransaction(() -> { - Collection forcedIds = myForcedIdDao.findAndResolveByForcedIdWithNoTypeInPartitionIdOrNullPartitionId( + Collection forcedIds = myResourceTableDao.findAndResolveByForcedIdWithNoTypeInPartitionIdOrNullPartitionId( "Patient", Arrays.asList(patientId), Arrays.asList(TENANT_A_ID), false ); assertContainsSingleForcedId(forcedIds, patientId); @@ -292,7 +298,7 @@ public class MultitenantServerR4Test extends BaseMultitenantResourceProviderR4Te // Search and filter deleted runInTransaction(() -> { - Collection forcedIds = myForcedIdDao.findAndResolveByForcedIdWithNoTypeInPartitionIdOrNullPartitionId( + Collection forcedIds = myResourceTableDao.findAndResolveByForcedIdWithNoTypeInPartitionIdOrNullPartitionId( "Patient", Arrays.asList(patientId), Arrays.asList(TENANT_A_ID), true ); assertEquals(0, forcedIds.size()); @@ -309,7 +315,7 @@ public class MultitenantServerR4Test extends BaseMultitenantResourceProviderR4Te // Search and include deleted runInTransaction(() -> { - Collection forcedIds = myForcedIdDao.findAndResolveByForcedIdWithNoTypeInPartition( + Collection forcedIds = myResourceTableDao.findAndResolveByForcedIdWithNoTypeInPartition( "Patient", Arrays.asList(patientId), Arrays.asList(TENANT_A_ID, TENANT_B_ID), false ); assertContainsSingleForcedId(forcedIds, patientId); @@ -317,7 +323,7 @@ public class MultitenantServerR4Test extends BaseMultitenantResourceProviderR4Te // Search and filter deleted runInTransaction(() -> { - Collection forcedIds = myForcedIdDao.findAndResolveByForcedIdWithNoTypeInPartition( + Collection forcedIds = myResourceTableDao.findAndResolveByForcedIdWithNoTypeInPartition( "Patient", Arrays.asList(patientId), Arrays.asList(TENANT_A_ID, TENANT_B_ID), true ); assertContainsSingleForcedId(forcedIds, patientId); @@ -327,7 +333,7 @@ public class MultitenantServerR4Test extends BaseMultitenantResourceProviderR4Te // Search and include deleted runInTransaction(() -> { - Collection forcedIds = myForcedIdDao.findAndResolveByForcedIdWithNoTypeInPartition( + Collection forcedIds = myResourceTableDao.findAndResolveByForcedIdWithNoTypeInPartition( "Patient", Arrays.asList(patientId), Arrays.asList(TENANT_A_ID, TENANT_B_ID), false ); assertContainsSingleForcedId(forcedIds, patientId); @@ -335,7 +341,7 @@ public class MultitenantServerR4Test extends BaseMultitenantResourceProviderR4Te // Search and filter deleted runInTransaction(() -> { - Collection forcedIds = myForcedIdDao.findAndResolveByForcedIdWithNoTypeInPartition( + Collection forcedIds = myResourceTableDao.findAndResolveByForcedIdWithNoTypeInPartition( "Patient", Arrays.asList(patientId), Arrays.asList(TENANT_A_ID, TENANT_B_ID), true ); assertEquals(0, forcedIds.size()); @@ -423,6 +429,55 @@ public class MultitenantServerR4Test extends BaseMultitenantResourceProviderR4Te } + @Test + public void testTransactionPut_withSearchNarrowingInterceptor_createsPatient() { + // setup + IBaseResource patientA = buildPatient(withTenant(TENANT_B), withActiveTrue(), withId("1234a"), + withFamily("Family"), withGiven("Given")); + + Bundle transactioBundle = new Bundle(); + transactioBundle.setType(Bundle.BundleType.TRANSACTION); + transactioBundle.addEntry() + .setFullUrl("http://localhost:8000/TENANT-A/Patient/1234a") + .setResource((Resource) patientA) + .getRequest().setUrl("Patient/1234a").setMethod(Bundle.HTTPVerb.PUT); + + myServer.registerInterceptor(new SearchNarrowingInterceptor()); + + // execute + myClient.transaction().withBundle(transactioBundle).execute(); + + // verify - read back using DAO + SystemRequestDetails requestDetails = new SystemRequestDetails(); + requestDetails.setTenantId(TENANT_B); + Patient patient1 = myPatientDao.read(new IdType("Patient/1234a"), requestDetails); + assertEquals("Family", patient1.getName().get(0).getFamily()); + } + + @ParameterizedTest + @ValueSource(strings = {"Patient/1234a", "TENANT-B/Patient/1234a"}) + public void testTransactionGet_withSearchNarrowingInterceptor_retrievesPatient(String theEntryUrl) { + // setup + createPatient(withTenant(TENANT_B), withActiveTrue(), withId("1234a"), + withFamily("Family"), withGiven("Given")); + + Bundle transactioBundle = new Bundle(); + transactioBundle.setType(Bundle.BundleType.TRANSACTION); + transactioBundle.addEntry() + .getRequest().setUrl(theEntryUrl).setMethod(Bundle.HTTPVerb.GET); + + myServer.registerInterceptor(new SearchNarrowingInterceptor()); + + // execute + Bundle result = myClient.transaction().withBundle(transactioBundle).execute(); + + // verify + assertEquals(1, result.getEntry().size()); + Patient retrievedPatient = (Patient) result.getEntry().get(0).getResource(); + assertNotNull(retrievedPatient); + assertEquals("Family", retrievedPatient.getName().get(0).getFamily()); + } + @Test public void testDirectDaoAccess_PartitionInRequestDetails_Create() { @@ -713,7 +768,7 @@ public class MultitenantServerR4Test extends BaseMultitenantResourceProviderR4Te } private String buildExportUrl(String createInPartition, String jobId) { - return myClient.getServerBase() + "/" + createInPartition + "/" + JpaConstants.OPERATION_EXPORT_POLL_STATUS + "?" + return myClient.getServerBase() + "/" + createInPartition + "/" + ProviderConstants.OPERATION_EXPORT_POLL_STATUS + "?" + JpaConstants.PARAM_EXPORT_POLL_STATUS_JOB_ID + "=" + jobId; } } diff --git a/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/provider/r4/PatientMemberMatchOperationR4Test.java b/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/provider/r4/PatientMemberMatchOperationR4Test.java deleted file mode 100644 index 638e9a5f0db..00000000000 --- a/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/provider/r4/PatientMemberMatchOperationR4Test.java +++ /dev/null @@ -1,494 +0,0 @@ -package ca.uhn.fhir.jpa.provider.r4; - -import ca.uhn.fhir.jpa.api.config.JpaStorageSettings; -import ca.uhn.fhir.jpa.provider.BaseResourceProviderR4Test; -import ca.uhn.fhir.parser.StrictErrorHandler; -import ca.uhn.fhir.rest.api.Constants; -import ca.uhn.fhir.rest.api.EncodingEnum; -import ca.uhn.fhir.rest.client.apache.ResourceEntity; -import ca.uhn.fhir.util.ParametersUtil; -import com.google.common.collect.Lists; -import org.apache.commons.io.IOUtils; -import org.apache.http.client.methods.CloseableHttpResponse; -import org.apache.http.client.methods.HttpPost; -import org.hl7.fhir.instance.model.api.IBase; -import org.hl7.fhir.r4.model.CodeableConcept; -import org.hl7.fhir.r4.model.Coding; -import org.hl7.fhir.r4.model.Consent; -import org.hl7.fhir.r4.model.Coverage; -import org.hl7.fhir.r4.model.DateType; -import org.hl7.fhir.r4.model.Enumerations; -import org.hl7.fhir.r4.model.HumanName; -import org.hl7.fhir.r4.model.Identifier; -import org.hl7.fhir.r4.model.Parameters; -import org.hl7.fhir.r4.model.Patient; -import org.hl7.fhir.r4.model.Reference; -import org.junit.jupiter.api.AfterEach; -import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.Nested; -import org.junit.jupiter.api.Test; -import org.springframework.beans.factory.annotation.Autowired; - -import java.nio.charset.StandardCharsets; -import java.util.Collections; -import java.util.List; - -import static ca.uhn.fhir.rest.api.Constants.PARAM_CONSENT; -import static ca.uhn.fhir.rest.api.Constants.PARAM_CONSENT_PATIENT_REFERENCE; -import static ca.uhn.fhir.rest.api.Constants.PARAM_CONSENT_PERFORMER_REFERENCE; -import static ca.uhn.fhir.rest.api.Constants.PARAM_MEMBER_PATIENT; -import static ca.uhn.fhir.rest.api.Constants.PARAM_MEMBER_PATIENT_BIRTHDATE; -import static ca.uhn.fhir.rest.api.Constants.PARAM_MEMBER_PATIENT_NAME; -import static ca.uhn.fhir.rest.api.Constants.PARAM_NEW_COVERAGE; -import static ca.uhn.fhir.rest.api.Constants.PARAM_OLD_COVERAGE; -import static org.hamcrest.MatcherAssert.assertThat; -import static org.hamcrest.Matchers.containsString; -import static org.junit.jupiter.api.Assertions.assertEquals; -import static org.junit.jupiter.api.Assertions.assertFalse; -import static org.junit.jupiter.api.Assertions.assertNotNull; - -@SuppressWarnings("Duplicates") -public class PatientMemberMatchOperationR4Test extends BaseResourceProviderR4Test { - private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(PatientMemberMatchOperationR4Test.class); - - private static final String ourQuery = "/Patient/$member-match?_format=json"; - private static final String EXISTING_COVERAGE_ID = "cov-id-123"; - private static final String EXISTING_COVERAGE_IDENT_SYSTEM = "http://centene.com/insurancePlanIds"; - private static final String EXISTING_COVERAGE_IDENT_VALUE = "U1234567890"; - private static final String EXISTING_COVERAGE_PATIENT_IDENT_SYSTEM = "http://oldhealthplan.example.com"; - private static final String EXISTING_COVERAGE_PATIENT_IDENT_VALUE = "DHU-55678"; - private static final String CONSENT_POLICY_SENSITIVE_DATA_URI = "http://hl7.org/fhir/us/davinci-hrex/StructureDefinition-hrex-consent.html#sensitive"; - private static final String CONSENT_POLICY_REGULAR_DATA_URI = "http://hl7.org/fhir/us/davinci-hrex/StructureDefinition-hrex-consent.html#regular"; - - private Identifier ourExistingCoverageIdentifier; - private Patient myPatient; - private Coverage oldCoverage; // Old Coverage (must match field) - private Coverage newCoverage; // New Coverage (must return unchanged) - private Consent myConsent; - - @Autowired - MemberMatcherR4Helper myMemberMatcherR4Helper; - - @Autowired - MemberMatchR4ResourceProvider theMemberMatchR4ResourceProvider; - - @BeforeEach - public void beforeDisableResultReuse() { - myStorageSettings.setReuseCachedSearchResultsForMillis(null); - } - - @Override - @AfterEach - public void after() throws Exception { - super.after(); - myStorageSettings.setReuseCachedSearchResultsForMillis(new JpaStorageSettings().getReuseCachedSearchResultsForMillis()); - myStorageSettings.setEverythingIncludesFetchPageSize(new JpaStorageSettings().getEverythingIncludesFetchPageSize()); - myStorageSettings.setSearchPreFetchThresholds(new JpaStorageSettings().getSearchPreFetchThresholds()); - myStorageSettings.setAllowExternalReferences(new JpaStorageSettings().isAllowExternalReferences()); - myServer.getRestfulServer().unregisterProvider(theMemberMatchR4ResourceProvider); - } - - @Override - @BeforeEach - public void before() throws Exception { - super.before(); - myFhirContext.setParserErrorHandler(new StrictErrorHandler()); - myPatient = (Patient) new Patient().setName(Lists.newArrayList(new HumanName() - .setUse(HumanName.NameUse.OFFICIAL).setFamily("Person"))) - .setBirthDateElement(new DateType("2020-01-01")) - .setId("Patient/A123"); - - // Old Coverage (must match field) - oldCoverage = (Coverage) new Coverage() - .setId(EXISTING_COVERAGE_ID); - - // New Coverage (must return unchanged) - newCoverage = (Coverage) new Coverage() - .setIdentifier(Lists.newArrayList(new Identifier().setSystem("http://newealthplan.example.com").setValue("234567"))) - .setId("AA87654"); - - myConsent = new Consent() - .setStatus(Consent.ConsentState.ACTIVE) - .setScope(new CodeableConcept().addCoding(new Coding("http://terminology.hl7.org/CodeSystem/consentscope", "patient-privacy", null))) - .addPolicy(new Consent.ConsentPolicyComponent().setUri(CONSENT_POLICY_SENSITIVE_DATA_URI)) - .setPatient(new Reference("Patient/A123")) - .addPerformer(new Reference("Patient/A123")); - myServer.getRestfulServer().registerProvider(theMemberMatchR4ResourceProvider); - myMemberMatcherR4Helper.setRegularFilterSupported(false); - } - - private void createCoverageWithBeneficiary( - boolean theAssociateBeneficiaryPatient, boolean includeBeneficiaryIdentifier) { - - Patient member = new Patient(); - if (theAssociateBeneficiaryPatient) { - // Patient - member.setName(Lists.newArrayList(new HumanName() - .setUse(HumanName.NameUse.OFFICIAL).setFamily("Person"))) - .setBirthDateElement(new DateType("2020-01-01")) - .setId("Patient/A123"); - if (includeBeneficiaryIdentifier) { - member.setIdentifier(Collections.singletonList(new Identifier() - .setSystem(EXISTING_COVERAGE_PATIENT_IDENT_SYSTEM).setValue(EXISTING_COVERAGE_PATIENT_IDENT_VALUE))); - } - member.setActive(true); - myClient.update().resource(member).execute(); - } - - // Coverage - ourExistingCoverageIdentifier = new Identifier() - .setSystem(EXISTING_COVERAGE_IDENT_SYSTEM).setValue(EXISTING_COVERAGE_IDENT_VALUE); - Coverage ourExistingCoverage = (Coverage) new Coverage() - .setStatus(Coverage.CoverageStatus.ACTIVE) - .setIdentifier(Collections.singletonList(ourExistingCoverageIdentifier)); - - if (theAssociateBeneficiaryPatient) { - ourExistingCoverage.setBeneficiary(new Reference(member)) - .setId(EXISTING_COVERAGE_ID); - } - - myClient.create().resource(ourExistingCoverage).execute().getId().toUnqualifiedVersionless().getValue(); - } - - @Test - public void testMemberMatchByCoverageId() throws Exception { - createCoverageWithBeneficiary(true, true); - - Parameters inputParameters = buildInputParameters(myPatient, oldCoverage, newCoverage, myConsent); - Parameters parametersResponse = performOperation(myServerBase + ourQuery, - EncodingEnum.JSON, inputParameters); - - validateMemberPatient(parametersResponse); - validateNewCoverage(parametersResponse, newCoverage); - } - - - @Test - public void testCoverageNoBeneficiaryReturns422() throws Exception { - createCoverageWithBeneficiary(false, false); - - Parameters inputParameters = buildInputParameters(myPatient, oldCoverage, newCoverage, myConsent); - performOperationExpecting422(myServerBase + ourQuery, EncodingEnum.JSON, inputParameters, - "Could not find beneficiary for coverage."); - } - - @Test - public void testCoverageBeneficiaryNoIdentifierReturns422() throws Exception { - createCoverageWithBeneficiary(true, false); - - Parameters inputParameters = buildInputParameters(myPatient, oldCoverage, newCoverage, myConsent); - performOperationExpecting422(myServerBase + ourQuery, EncodingEnum.JSON, inputParameters, - "Coverage beneficiary does not have an identifier."); - } - - @Test - public void testCoverageNoMatchingPatientFamilyNameReturns422() throws Exception { - createCoverageWithBeneficiary(true, true); - - myPatient.setName(Lists.newArrayList(new HumanName().setUse(HumanName.NameUse.OFFICIAL).setFamily("Smith"))); - Parameters inputParameters = buildInputParameters(myPatient, oldCoverage, newCoverage, myConsent); - performOperationExpecting422(myServerBase + ourQuery, EncodingEnum.JSON, inputParameters, - "Could not find matching patient for coverage."); - } - - @Test - public void testCoverageNoMatchingPatientBirthdateReturns422() throws Exception { - createCoverageWithBeneficiary(true, false); - - myPatient.setBirthDateElement(new DateType("2000-01-01")); - Parameters inputParameters = buildInputParameters(myPatient, oldCoverage, newCoverage, myConsent); - performOperationExpecting422(myServerBase + ourQuery, EncodingEnum.JSON, inputParameters, - "Could not find matching patient for coverage."); - } - - @Test - public void testRegularContentProfileAccessWithRegularNotAllowedReturns422() throws Exception { - createCoverageWithBeneficiary(true, true); - myConsent.getPolicy().get(0).setUri(CONSENT_POLICY_REGULAR_DATA_URI); - Parameters inputParameters = buildInputParameters(myPatient, oldCoverage, newCoverage, myConsent); - performOperationExpecting422(myServerBase + ourQuery, EncodingEnum.JSON, inputParameters, - "Consent policy does not match the data release segmentation capabilities."); - } - - @Test - public void testSensitiveContentProfileAccessWithRegularNotAllowed() throws Exception { - createCoverageWithBeneficiary(true, true); - Parameters inputParameters = buildInputParameters(myPatient, oldCoverage, newCoverage, myConsent); - Parameters parametersResponse = performOperation(myServerBase + ourQuery, - EncodingEnum.JSON, inputParameters); - - validateMemberPatient(parametersResponse); - validateNewCoverage(parametersResponse, newCoverage); - } - - @Test - public void testRegularContentProfileAccessWithRegularAllowed() throws Exception { - createCoverageWithBeneficiary(true, true); - myConsent.getPolicy().get(0).setUri(CONSENT_POLICY_REGULAR_DATA_URI); - myMemberMatcherR4Helper.setRegularFilterSupported(true); - Parameters inputParameters = buildInputParameters(myPatient, oldCoverage, newCoverage, myConsent); - Parameters parametersResponse = performOperation(myServerBase + ourQuery, - EncodingEnum.JSON, inputParameters); - - validateMemberPatient(parametersResponse); - validateNewCoverage(parametersResponse, newCoverage); - } - - @Test - public void testConsentReturns() throws Exception { - createCoverageWithBeneficiary(true, true); - myConsent.getPolicy().get(0).setUri(CONSENT_POLICY_REGULAR_DATA_URI); - myMemberMatcherR4Helper.setRegularFilterSupported(true); - Parameters inputParameters = buildInputParameters(myPatient, oldCoverage, newCoverage, myConsent); - Parameters parametersResponse = performOperation(myServerBase + ourQuery, - EncodingEnum.JSON, inputParameters); - - validateMemberPatient(parametersResponse); - validateNewCoverage(parametersResponse, newCoverage); - validateResponseConsent(parametersResponse, myConsent); - } - - @Test - public void testMemberMatchByCoverageIdentifier() throws Exception { - createCoverageWithBeneficiary(true, true); - - Parameters inputParameters = buildInputParameters(myPatient, oldCoverage, newCoverage, myConsent); - Parameters parametersResponse = performOperation(myServerBase + ourQuery, EncodingEnum.JSON, inputParameters); - - validateMemberPatient(parametersResponse); - validateNewCoverage(parametersResponse, newCoverage); - } - - /** - * Validates that second resource from the response is same as the received coverage - */ - private void validateNewCoverage(Parameters theResponse, Coverage theOriginalCoverage) { - List patientList = ParametersUtil.getNamedParameters(this.getFhirContext(), theResponse, PARAM_NEW_COVERAGE); - assertEquals(1, patientList.size()); - Coverage respCoverage = (Coverage) theResponse.getParameter().get(1).getResource(); - - assertEquals("Coverage/" + theOriginalCoverage.getId(), respCoverage.getId()); - assertEquals(theOriginalCoverage.getIdentifierFirstRep().getSystem(), respCoverage.getIdentifierFirstRep().getSystem()); - assertEquals(theOriginalCoverage.getIdentifierFirstRep().getValue(), respCoverage.getIdentifierFirstRep().getValue()); - } - - private void validateConsentPatientAndPerformerRef(Patient thePatient, Consent theConsent) { - String patientRef = thePatient.getIdElement().toUnqualifiedVersionless().getValue(); - assertEquals(patientRef, theConsent.getPatient().getReference()); - assertEquals(patientRef, theConsent.getPerformer().get(0).getReference()); - } - - private void validateMemberPatient(Parameters response) { -// parameter MemberPatient must have a new identifier with: -// { -// "use": "usual", -// "type": { -// "coding": [ -// { -// "system": "http://terminology.hl7.org/CodeSystem/v2-0203", -// "code": "UMB", -// "display": "Member Number", -// "userSelected": false -// } -// ], -// "text": "Member Number" -// }, -// "system": COVERAGE_PATIENT_IDENT_SYSTEM, -// "value": COVERAGE_PATIENT_IDENT_VALUE -// } - List patientList = ParametersUtil.getNamedParameters(this.getFhirContext(), response, PARAM_MEMBER_PATIENT); - assertEquals(1, patientList.size()); - Patient resultPatient = (Patient) response.getParameter().get(0).getResource(); - - assertNotNull(resultPatient.getIdentifier()); - assertEquals(1, resultPatient.getIdentifier().size()); - Identifier addedIdentifier = resultPatient.getIdentifier().get(0); - assertEquals(Identifier.IdentifierUse.USUAL, addedIdentifier.getUse()); - checkCoding(addedIdentifier.getType()); - assertEquals(EXISTING_COVERAGE_PATIENT_IDENT_SYSTEM, addedIdentifier.getSystem()); - assertEquals(EXISTING_COVERAGE_PATIENT_IDENT_VALUE, addedIdentifier.getValue()); - } - - @Test - public void testNoCoverageMatchFound() throws Exception { - Parameters inputParameters = buildInputParameters(myPatient, oldCoverage, newCoverage, myConsent); - performOperationExpecting422(myServerBase + ourQuery, EncodingEnum.JSON, inputParameters, - "Could not find coverage for member"); - } - - @Test - public void testConsentUpdatePatientAndPerformer() throws Exception { - createCoverageWithBeneficiary(true, true); - Parameters inputParameters = buildInputParameters(myPatient, oldCoverage, newCoverage, myConsent); - Parameters parametersResponse = performOperation(myServerBase + ourQuery, - EncodingEnum.JSON, inputParameters); - - Consent respConsent = validateResponseConsent(parametersResponse, myConsent); - validateConsentPatientAndPerformerRef(myPatient, respConsent); - } - - private Parameters buildInputParameters(Patient thePatient, Coverage theOldCoverage, Coverage theNewCoverage, Consent theConsent) { - Parameters p = new Parameters(); - ParametersUtil.addParameterToParameters(this.getFhirContext(), p, PARAM_MEMBER_PATIENT, thePatient); - ParametersUtil.addParameterToParameters(this.getFhirContext(), p, PARAM_OLD_COVERAGE, theOldCoverage); - ParametersUtil.addParameterToParameters(this.getFhirContext(), p, PARAM_NEW_COVERAGE, theNewCoverage); - ParametersUtil.addParameterToParameters(this.getFhirContext(), p, PARAM_CONSENT, theConsent); - return p; - } - - private Parameters performOperation(String theUrl, - EncodingEnum theEncoding, Parameters theInputParameters) throws Exception { - - HttpPost post = new HttpPost(theUrl); - post.addHeader(Constants.HEADER_ACCEPT_ENCODING, theEncoding.toString()); - post.setEntity(new ResourceEntity(this.getFhirContext(), theInputParameters)); - ourLog.info("Request: {}", post); - try (CloseableHttpResponse response = ourHttpClient.execute(post)) { - String responseString = IOUtils.toString(response.getEntity().getContent(), StandardCharsets.UTF_8); - ourLog.info("Response: {}", responseString); - assertEquals(200, response.getStatusLine().getStatusCode()); - - return theEncoding.newParser(myFhirContext).parseResource(Parameters.class, - responseString); - } - } - - private void performOperationExpecting422(String theUrl, EncodingEnum theEncoding, - Parameters theInputParameters, String theExpectedErrorMsg) throws Exception { - - HttpPost post = new HttpPost(theUrl); - post.addHeader(Constants.HEADER_ACCEPT_ENCODING, theEncoding.toString()); - post.setEntity(new ResourceEntity(this.getFhirContext(), theInputParameters)); - ourLog.info("Request: {}", post); - try (CloseableHttpResponse response = ourHttpClient.execute(post)) { - String responseString = IOUtils.toString(response.getEntity().getContent(), StandardCharsets.UTF_8); - ourLog.info("Response: {}", responseString); - assertEquals(422, response.getStatusLine().getStatusCode()); - assertThat(responseString, containsString(theExpectedErrorMsg)); - } - } - - private void checkCoding(CodeableConcept theType) { - // must match: - // "coding": [ - // { - // "system": "http://terminology.hl7.org/CodeSystem/v2-0203", - // "code": "MB", - // "display": "Member Number", - // "userSelected": false - // } - // * ] - Coding coding = theType.getCoding().get(0); - assertEquals("http://terminology.hl7.org/CodeSystem/v2-0203", coding.getSystem()); - assertEquals("MB", coding.getCode()); - assertEquals("Member Number", coding.getDisplay()); - assertFalse(coding.getUserSelected()); - } - - /** - * Validates that consent from the response is same as the received consent with additional identifier and extension - */ - private Consent validateResponseConsent(Parameters theResponse, Consent theOriginalConsent) { - List consentList = ParametersUtil.getNamedParameters(this.getFhirContext(), theResponse, PARAM_CONSENT); - assertEquals(1, consentList.size()); - Consent respConsent = (Consent) theResponse.getParameter().get(2).getResource(); - - assertEquals(theOriginalConsent.getScope().getCodingFirstRep().getSystem(), respConsent.getScope().getCodingFirstRep().getSystem()); - assertEquals(theOriginalConsent.getScope().getCodingFirstRep().getCode(), respConsent.getScope().getCodingFirstRep().getCode()); - assertEquals(myMemberMatcherR4Helper.CONSENT_IDENTIFIER_CODE_SYSTEM, respConsent.getIdentifier().get(0).getSystem()); - assertNotNull(respConsent.getIdentifier().get(0).getValue()); - return respConsent; - } - - @Nested - public class ValidateParameterErrors { - private Patient ourPatient; - private Coverage ourOldCoverage; - private Coverage ourNewCoverage; - private Consent ourConsent; - - @BeforeEach - public void beforeValidateParameterErrors() { - ourPatient = new Patient().setGender(Enumerations.AdministrativeGender.FEMALE); - - ourOldCoverage = new Coverage(); - ourOldCoverage.setId(EXISTING_COVERAGE_ID); - - ourNewCoverage = new Coverage(); - ourNewCoverage.setId("AA87654"); - ourNewCoverage.setIdentifier(Lists.newArrayList( - new Identifier().setSystem("http://newealthplan.example.com").setValue("234567"))); - - ourConsent = new Consent(); - ourConsent.setStatus(Consent.ConsentState.ACTIVE); - } - - @Test - public void testInvalidPatient() throws Exception { - Parameters inputParameters = buildInputParameters(new Patient(), ourOldCoverage, ourNewCoverage, ourConsent); - performOperationExpecting422(myServerBase + ourQuery, EncodingEnum.JSON, inputParameters, - "Parameter \\\"" + PARAM_MEMBER_PATIENT + "\\\" is required."); - } - - @Test - public void testInvalidOldCoverage() throws Exception { - Parameters inputParameters = buildInputParameters(ourPatient, new Coverage(), ourNewCoverage, ourConsent); - performOperationExpecting422(myServerBase + ourQuery, EncodingEnum.JSON, inputParameters, - "Parameter \\\"" + PARAM_OLD_COVERAGE + "\\\" is required."); - } - - @Test - public void testInvalidNewCoverage() throws Exception { - Parameters inputParameters = buildInputParameters(ourPatient, ourOldCoverage, new Coverage(), ourConsent); - performOperationExpecting422(myServerBase + ourQuery, EncodingEnum.JSON, inputParameters, - "Parameter \\\"" + PARAM_NEW_COVERAGE + "\\\" is required."); - } - - @Test - public void testInvalidConsent() throws Exception { - Parameters inputParameters = buildInputParameters(ourPatient, ourOldCoverage, ourNewCoverage, new Consent()); - performOperationExpecting422(myServerBase + ourQuery, EncodingEnum.JSON, inputParameters, - "Parameter \\\"" + PARAM_CONSENT + "\\\" is required."); - } - - @Test - public void testMissingPatientFamilyName() throws Exception { - Parameters inputParameters = buildInputParameters(ourPatient, ourOldCoverage, ourNewCoverage, ourConsent); - performOperationExpecting422(myServerBase + ourQuery, EncodingEnum.JSON, inputParameters, - "Parameter \\\"" + PARAM_MEMBER_PATIENT_NAME + "\\\" is required."); - } - - @Test - public void testMissingPatientBirthdate() throws Exception { - ourPatient.setName(Lists.newArrayList(new HumanName() - .setUse(HumanName.NameUse.OFFICIAL).setFamily("Person"))); - Parameters inputParameters = buildInputParameters(ourPatient, ourOldCoverage, ourNewCoverage, ourConsent); - performOperationExpecting422(myServerBase + ourQuery, EncodingEnum.JSON, inputParameters, - "Parameter \\\"" + PARAM_MEMBER_PATIENT_BIRTHDATE + "\\\" is required."); - } - - @Test - public void testMissingConsentPatientReference() throws Exception { - ourPatient.setName(Lists.newArrayList(new HumanName() - .setUse(HumanName.NameUse.OFFICIAL).setFamily("Person"))) - .setBirthDateElement(new DateType("2020-01-01")); - - Parameters inputParameters = buildInputParameters(ourPatient, ourOldCoverage, ourNewCoverage, ourConsent); - performOperationExpecting422(myServerBase + ourQuery, EncodingEnum.JSON, inputParameters, - "Parameter \\\"" + PARAM_CONSENT_PATIENT_REFERENCE + "\\\" is required."); - } - - @Test - public void testMissingConsentPerformerReference() throws Exception { - ourPatient.setName(Lists.newArrayList(new HumanName() - .setUse(HumanName.NameUse.OFFICIAL).setFamily("Person"))) - .setBirthDateElement(new DateType("2020-01-01")); - - ourConsent.setPatient(new Reference("Patient/1")); - Parameters inputParameters = buildInputParameters(ourPatient, ourOldCoverage, ourNewCoverage, ourConsent); - performOperationExpecting422(myServerBase + ourQuery, EncodingEnum.JSON, inputParameters, - "Parameter \\\"" + PARAM_CONSENT_PERFORMER_REFERENCE + "\\\" is required."); - } - } - -} diff --git a/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/provider/r4/RemoteTerminologyServiceResourceProviderR4Test.java b/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/provider/r4/RemoteTerminologyServiceResourceProviderR4Test.java index a02443b3f09..823438e450e 100644 --- a/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/provider/r4/RemoteTerminologyServiceResourceProviderR4Test.java +++ b/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/provider/r4/RemoteTerminologyServiceResourceProviderR4Test.java @@ -5,13 +5,11 @@ import ca.uhn.fhir.context.support.IValidationSupport; import ca.uhn.fhir.rest.annotation.IdParam; import ca.uhn.fhir.rest.annotation.Operation; import ca.uhn.fhir.rest.annotation.OperationParam; -import ca.uhn.fhir.rest.annotation.RequiredParam; -import ca.uhn.fhir.rest.annotation.Search; import ca.uhn.fhir.rest.client.api.ServerValidationModeEnum; import ca.uhn.fhir.rest.client.interceptor.LoggingInterceptor; -import ca.uhn.fhir.rest.param.UriParam; import ca.uhn.fhir.rest.server.IResourceProvider; import ca.uhn.fhir.test.utilities.server.RestfulServerExtension; +import jakarta.servlet.http.HttpServletRequest; import org.hl7.fhir.common.hapi.validation.support.RemoteTerminologyServiceValidationSupport; import org.hl7.fhir.instance.model.api.IBaseResource; import org.hl7.fhir.r4.model.BooleanType; @@ -27,10 +25,9 @@ import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.RegisterExtension; -import javax.servlet.http.HttpServletRequest; -import java.util.List; - import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertNotNull; import static org.junit.jupiter.api.Assertions.assertNull; /* @@ -45,18 +42,18 @@ public class RemoteTerminologyServiceResourceProviderR4Test { private static final String VALUE_SET_URL = "http://value.set/url"; private static final String SAMPLE_MESSAGE = "This is a sample message"; private static final FhirContext ourCtx = FhirContext.forR4Cached(); - private MyCodeSystemProvider myCodeSystemProvider = new MyCodeSystemProvider(); - private MyValueSetProvider myValueSetProvider = new MyValueSetProvider(); + private static final MyCodeSystemProvider ourCodeSystemProvider = new MyCodeSystemProvider(); + private static final MyValueSetProvider ourValueSetProvider = new MyValueSetProvider(); @RegisterExtension - public RestfulServerExtension myRestfulServerExtension = new RestfulServerExtension(ourCtx, myCodeSystemProvider, - myValueSetProvider); + public static RestfulServerExtension ourRestfulServerExtension = new RestfulServerExtension(ourCtx, ourCodeSystemProvider, + ourValueSetProvider); private RemoteTerminologyServiceValidationSupport mySvc; @BeforeEach public void before_ConfigureService() { - String myBaseUrl = "http://localhost:" + myRestfulServerExtension.getPort(); + String myBaseUrl = "http://localhost:" + ourRestfulServerExtension.getPort(); mySvc = new RemoteTerminologyServiceValidationSupport(ourCtx, myBaseUrl); mySvc.addClientInterceptor(new LoggingInterceptor(false).setLogRequestSummary(true).setLogResponseSummary(true)); } @@ -64,7 +61,7 @@ public class RemoteTerminologyServiceResourceProviderR4Test { @AfterEach public void after_UnregisterProviders() { ourCtx.getRestfulClientFactory().setServerValidationMode(ServerValidationModeEnum.ONCE); - myRestfulServerExtension.getRestfulServer().getInterceptorService().unregisterAllInterceptors(); + ourRestfulServerExtension.getRestfulServer().getInterceptorService().unregisterAllInterceptors(); } @Test @@ -80,12 +77,13 @@ public class RemoteTerminologyServiceResourceProviderR4Test { IValidationSupport.CodeValidationResult outcome = mySvc .validateCode(null, null, CODE_SYSTEM, CODE, null, null); + assertNotNull(outcome); assertEquals(CODE, outcome.getCode()); - assertEquals(null, outcome.getSeverity()); - assertEquals(null, outcome.getMessage()); + assertNull(outcome.getSeverity()); + assertNull(outcome.getMessage()); - assertEquals(CODE, myCodeSystemProvider.myLastCode.getCode()); - assertEquals(CODE_SYSTEM, myCodeSystemProvider.myLastUrl.getValueAsString()); + assertEquals(CODE, ourCodeSystemProvider.myLastCode.getCode()); + assertEquals(CODE_SYSTEM, ourCodeSystemProvider.myLastUrl.getValueAsString()); } @Test @@ -94,15 +92,16 @@ public class RemoteTerminologyServiceResourceProviderR4Test { IValidationSupport.CodeValidationResult outcome = mySvc .validateCode(null, null, CODE_SYSTEM, CODE, DISPLAY, null); + assertNotNull(outcome); assertEquals(CODE, outcome.getCode()); assertEquals(DISPLAY, outcome.getDisplay()); - assertEquals(null, outcome.getSeverity()); - assertEquals(null, outcome.getMessage()); + assertNull(outcome.getSeverity()); + assertNull(outcome.getMessage()); - assertEquals(CODE, myCodeSystemProvider.myLastCode.getCode()); - assertEquals(DISPLAY, myCodeSystemProvider.myLastDisplay.getValue()); - assertEquals(CODE_SYSTEM, myCodeSystemProvider.myLastUrl.getValueAsString()); - assertEquals(SAMPLE_MESSAGE, myCodeSystemProvider.myNextReturnParams.getParameterValue("message").toString()); + assertEquals(CODE, ourCodeSystemProvider.myLastCode.getCode()); + assertEquals(DISPLAY, ourCodeSystemProvider.myLastDisplay.getValue()); + assertEquals(CODE_SYSTEM, ourCodeSystemProvider.myLastUrl.getValueAsString()); + assertEquals(SAMPLE_MESSAGE, ourCodeSystemProvider.myNextReturnParams.getParameterValue("message").toString()); } @Test @@ -111,65 +110,58 @@ public class RemoteTerminologyServiceResourceProviderR4Test { IValidationSupport.CodeValidationResult outcome = mySvc .validateCode(null, null, CODE_SYSTEM, CODE, null, null); + assertNotNull(outcome); assertEquals(IValidationSupport.IssueSeverity.ERROR, outcome.getSeverity()); assertEquals(SAMPLE_MESSAGE, outcome.getMessage()); - assertEquals(false, ((BooleanType)myCodeSystemProvider.myNextReturnParams.getParameterValue("result")).booleanValue()); + assertFalse(((BooleanType) ourCodeSystemProvider.myNextReturnParams.getParameterValue("result")).booleanValue()); } @Test public void testValidateCodeInValueSet_ProvidingMinimalInputs_ReturnsSuccess() { - createNextValueSetReturnParameters(true, null, null); + ourValueSetProvider.myNextReturnParams = new Parameters().addParameter("result", true); IValidationSupport.CodeValidationResult outcome = mySvc .validateCode(null, null, CODE_SYSTEM, CODE, null, VALUE_SET_URL); + assertNotNull(outcome); assertEquals(CODE, outcome.getCode()); - assertEquals(null, outcome.getSeverity()); - assertEquals(null, outcome.getMessage()); + assertNull(outcome.getSeverity()); + assertNull(outcome.getMessage()); - assertEquals(CODE, myValueSetProvider.myLastCode.getCode()); - assertEquals(VALUE_SET_URL, myValueSetProvider.myLastUrl.getValueAsString()); + assertEquals(CODE, ourValueSetProvider.myLastCode.getCode()); + assertEquals(VALUE_SET_URL, ourValueSetProvider.myLastUrl.getValueAsString()); } @Test public void testValidateCodeInValueSet_WithMessageValue_ReturnsMessage() { - createNextValueSetReturnParameters(true, DISPLAY, SAMPLE_MESSAGE); + ourValueSetProvider.myNextReturnParams = new Parameters().addParameter("result", true) + .addParameter("display", DISPLAY) + .addParameter("message", SAMPLE_MESSAGE); IValidationSupport.CodeValidationResult outcome = mySvc .validateCode(null, null, CODE_SYSTEM, CODE, DISPLAY, VALUE_SET_URL); + assertNotNull(outcome); assertEquals(CODE, outcome.getCode()); assertEquals(DISPLAY, outcome.getDisplay()); - assertEquals(null, outcome.getSeverity()); - assertEquals(null, outcome.getMessage()); + assertNull(outcome.getSeverity()); + assertNull(outcome.getMessage()); - assertEquals(CODE, myValueSetProvider.myLastCode.getCode()); - assertEquals(DISPLAY, myValueSetProvider.myLastDisplay.getValue()); - assertEquals(VALUE_SET_URL, myValueSetProvider.myLastUrl.getValueAsString()); - assertEquals(SAMPLE_MESSAGE, myValueSetProvider.myNextReturnParams.getParameterValue("message").toString()); + assertEquals(CODE, ourValueSetProvider.myLastCode.getCode()); + assertEquals(DISPLAY, ourValueSetProvider.myLastDisplay.getValue()); + assertEquals(VALUE_SET_URL, ourValueSetProvider.myLastUrl.getValueAsString()); + assertEquals(SAMPLE_MESSAGE, ourValueSetProvider.myNextReturnParams.getParameterValue("message").toString()); } private void createNextCodeSystemReturnParameters(boolean theResult, String theDisplay, String theMessage) { - myCodeSystemProvider.myNextReturnParams = new Parameters(); - myCodeSystemProvider.myNextReturnParams.addParameter("result", theResult); - myCodeSystemProvider.myNextReturnParams.addParameter("display", theDisplay); + ourCodeSystemProvider.myNextReturnParams = new Parameters(); + ourCodeSystemProvider.myNextReturnParams.addParameter("result", theResult); + ourCodeSystemProvider.myNextReturnParams.addParameter("display", theDisplay); if (theMessage != null) { - myCodeSystemProvider.myNextReturnParams.addParameter("message", theMessage); - } - } - - private void createNextValueSetReturnParameters(boolean theResult, String theDisplay, String theMessage) { - myValueSetProvider.myNextReturnParams = new Parameters(); - myValueSetProvider.myNextReturnParams.addParameter("result", theResult); - myValueSetProvider.myNextReturnParams.addParameter("display", theDisplay); - if (theMessage != null) { - myValueSetProvider.myNextReturnParams.addParameter("message", theMessage); + ourCodeSystemProvider.myNextReturnParams.addParameter("message", theMessage); } } private static class MyCodeSystemProvider implements IResourceProvider { - private UriParam myLastUrlParam; - private List myNextReturnCodeSystems; - private int myInvocationCount; private UriType myLastUrl; private CodeType myLastCode; private StringType myLastDisplay; @@ -187,7 +179,6 @@ public class RemoteTerminologyServiceResourceProviderR4Test { @OperationParam(name = "code", min = 0, max = 1) CodeType theCode, @OperationParam(name = "display", min = 0, max = 1) StringType theDisplay ) { - myInvocationCount++; myLastUrl = theCodeSystemUrl; myLastCode = theCode; myLastDisplay = theDisplay; @@ -195,13 +186,6 @@ public class RemoteTerminologyServiceResourceProviderR4Test { } - @Search - public List find(@RequiredParam(name = "url") UriParam theUrlParam) { - myLastUrlParam = theUrlParam; - assert myNextReturnCodeSystems != null; - return myNextReturnCodeSystems; - } - @Override public Class getResourceType() { return CodeSystem.class; @@ -211,14 +195,9 @@ public class RemoteTerminologyServiceResourceProviderR4Test { private static class MyValueSetProvider implements IResourceProvider { private Parameters myNextReturnParams; - private List myNextReturnValueSets; private UriType myLastUrl; private CodeType myLastCode; - private int myInvocationCount; - private UriType myLastSystem; private StringType myLastDisplay; - private ValueSet myLastValueSet; - private UriParam myLastUrlParam; @Operation(name = "validate-code", idempotent = true, returnParameters = { @OperationParam(name = "result", type = BooleanType.class, min = 1), @@ -234,22 +213,12 @@ public class RemoteTerminologyServiceResourceProviderR4Test { @OperationParam(name = "display", min = 0, max = 1) StringType theDisplay, @OperationParam(name = "valueSet") ValueSet theValueSet ) { - myInvocationCount++; myLastUrl = theValueSetUrl; myLastCode = theCode; - myLastSystem = theSystem; myLastDisplay = theDisplay; - myLastValueSet = theValueSet; return myNextReturnParams; } - @Search - public List find(@RequiredParam(name = "url") UriParam theUrlParam) { - myLastUrlParam = theUrlParam; - assert myNextReturnValueSets != null; - return myNextReturnValueSets; - } - @Override public Class getResourceType() { return ValueSet.class; diff --git a/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/provider/r4/ResourceProviderCustomSearchParamR4Test.java b/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/provider/r4/ResourceProviderCustomSearchParamR4Test.java index 22f2e3e19df..f6b9cd4b9df 100644 --- a/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/provider/r4/ResourceProviderCustomSearchParamR4Test.java +++ b/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/provider/r4/ResourceProviderCustomSearchParamR4Test.java @@ -401,6 +401,9 @@ public class ResourceProviderCustomSearchParamR4Test extends BaseResourceProvide fooSp.setStatus(org.hl7.fhir.r4.model.Enumerations.PublicationStatus.ACTIVE); mySearchParameterDao.create(fooSp, mySrd); + mySearchParamRegistry.forceRefresh(); + assertNotNull(mySearchParamRegistry.getActiveSearchParam("Patient", "foo")); + Patient pat = new Patient(); pat.setGender(AdministrativeGender.MALE); IIdType patId = myPatientDao.create(pat, mySrd).getId().toUnqualifiedVersionless(); @@ -484,7 +487,6 @@ public class ResourceProviderCustomSearchParamR4Test extends BaseResourceProvide */ @Test public void testCustomParameterMatchingManyValues() { - List found = new ArrayList<>(); class Interceptor { @@ -496,7 +498,6 @@ public class ResourceProviderCustomSearchParamR4Test extends BaseResourceProvide Interceptor interceptor = new Interceptor(); myInterceptorRegistry.registerInterceptor(interceptor); try { - int textIndex = 0; List ids = new ArrayList<>(); for (int i = 0; i < 200; i++) { @@ -550,8 +551,8 @@ public class ResourceProviderCustomSearchParamR4Test extends BaseResourceProvide runInTransaction(() -> { - List currentResults = myEntityManager.createNativeQuery("select distinct resourceta0_.RES_ID as col_0_0_ from HFJ_RESOURCE resourceta0_ left outer join HFJ_SPIDX_STRING myparamsst1_ on resourceta0_.RES_ID=myparamsst1_.RES_ID where myparamsst1_.HASH_NORM_PREFIX='5901791607832193956' and (myparamsst1_.SP_VALUE_NORMALIZED like 'SECTION%') limit '500'").getResultList(); - List currentResources = myEntityManager.createNativeQuery("select resourceta0_.RES_ID as col_0_0_ from HFJ_RESOURCE resourceta0_").getResultList(); + List currentResults = myEntityManager.createNativeQuery("select distinct r1_0.RES_ID as col_0_0_ from HFJ_RESOURCE r1_0 left outer join HFJ_SPIDX_STRING myparamsst1_ on r1_0.RES_ID=myparamsst1_.RES_ID where myparamsst1_.HASH_NORM_PREFIX='5901791607832193956' and (myparamsst1_.SP_VALUE_NORMALIZED like 'SECTION%') limit '500'").getResultList(); + List currentResources = myEntityManager.createNativeQuery("select r1_0.RES_ID as col_0_0_ from HFJ_RESOURCE r1_0").getResultList(); List searches = mySearchEntityDao.findAll(); assertEquals(1, searches.size()); diff --git a/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/provider/r4/ResourceProviderInterceptorR4Test.java b/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/provider/r4/ResourceProviderInterceptorR4Test.java index 2e17f8d2780..7f3a0e06819 100644 --- a/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/provider/r4/ResourceProviderInterceptorR4Test.java +++ b/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/provider/r4/ResourceProviderInterceptorR4Test.java @@ -46,7 +46,7 @@ import org.mockito.Captor; import org.mockito.Mock; import org.springframework.beans.factory.annotation.Autowired; -import javax.servlet.ServletException; +import jakarta.servlet.ServletException; import java.io.IOException; import java.nio.charset.StandardCharsets; import java.util.ArrayList; diff --git a/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/provider/r4/ResourceProviderInvalidDataR4Test.java b/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/provider/r4/ResourceProviderInvalidDataR4Test.java index 52a3e23accf..b16956171b9 100644 --- a/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/provider/r4/ResourceProviderInvalidDataR4Test.java +++ b/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/provider/r4/ResourceProviderInvalidDataR4Test.java @@ -38,11 +38,9 @@ public class ResourceProviderInvalidDataR4Test extends BaseResourceProviderR4Tes // Manually set the value to be an invalid decimal number runInTransaction(() -> { ResourceHistoryTable resVer = myResourceHistoryTableDao.findForIdAndVersionAndFetchProvenance(id, 1); - byte[] bytesCompressed = resVer.getResource(); - String resourceText = GZipUtil.decompress(bytesCompressed); + String resourceText = resVer.getResourceTextVc(); resourceText = resourceText.replace("100", "-.100"); - bytesCompressed = GZipUtil.compress(resourceText); - resVer.setResource(bytesCompressed); + resVer.setResourceTextVc(resourceText); myResourceHistoryTableDao.save(resVer); }); diff --git a/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/provider/r4/ResourceProviderOnlySomeResourcesProvidedR4Test.java b/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/provider/r4/ResourceProviderOnlySomeResourcesProvidedR4Test.java index a1d3c5fd1bf..cc8a4b237ba 100644 --- a/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/provider/r4/ResourceProviderOnlySomeResourcesProvidedR4Test.java +++ b/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/provider/r4/ResourceProviderOnlySomeResourcesProvidedR4Test.java @@ -13,8 +13,8 @@ import org.springframework.context.annotation.Bean; import org.springframework.test.annotation.DirtiesContext; import org.springframework.test.context.ContextConfiguration; -import javax.annotation.PostConstruct; -import javax.annotation.PreDestroy; +import jakarta.annotation.PostConstruct; +import jakarta.annotation.PreDestroy; import static org.hamcrest.CoreMatchers.containsString; import static org.hamcrest.MatcherAssert.assertThat; diff --git a/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/provider/r4/ResourceProviderR4CodeSystemPropertiesTest.java b/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/provider/r4/ResourceProviderR4CodeSystemPropertiesTest.java new file mode 100644 index 00000000000..f96556ddc36 --- /dev/null +++ b/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/provider/r4/ResourceProviderR4CodeSystemPropertiesTest.java @@ -0,0 +1,99 @@ +package ca.uhn.fhir.jpa.provider.r4; + +import ca.uhn.fhir.jpa.model.util.JpaConstants; +import ca.uhn.fhir.jpa.provider.BaseResourceProviderR4Test; +import ca.uhn.fhir.jpa.provider.CodeSystemLookupWithPropertiesUtil; +import ca.uhn.fhir.rest.gclient.IOperationUntypedWithInputAndPartialOutput; +import org.hl7.fhir.r4.model.CodeSystem; +import org.hl7.fhir.r4.model.CodeSystem.ConceptDefinitionComponent; +import org.hl7.fhir.r4.model.CodeSystem.ConceptPropertyComponent; +import org.hl7.fhir.r4.model.CodeType; +import org.hl7.fhir.r4.model.Coding; +import org.hl7.fhir.r4.model.Parameters; +import org.hl7.fhir.r4.model.Parameters.ParametersParameterComponent; +import org.hl7.fhir.r4.model.StringType; +import org.hl7.fhir.r4.model.UriType; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.Arguments; +import org.junit.jupiter.params.provider.MethodSource; + +import java.util.Iterator; +import java.util.List; +import java.util.stream.Stream; + +import static ca.uhn.fhir.jpa.provider.CodeSystemLookupWithPropertiesUtil.ourCode; +import static ca.uhn.fhir.jpa.provider.CodeSystemLookupWithPropertiesUtil.ourCodeSystemId; +import static ca.uhn.fhir.jpa.provider.CodeSystemLookupWithPropertiesUtil.ourCodeSystemUrl; +import static ca.uhn.fhir.jpa.provider.CodeSystemLookupWithPropertiesUtil.ourPropertyA; +import static ca.uhn.fhir.jpa.provider.CodeSystemLookupWithPropertiesUtil.ourPropertyB; +import static ca.uhn.fhir.jpa.provider.CodeSystemLookupWithPropertiesUtil.ourPropertyC; +import static ca.uhn.fhir.jpa.provider.CodeSystemLookupWithPropertiesUtil.ourPropertyValueA; +import static ca.uhn.fhir.jpa.provider.CodeSystemLookupWithPropertiesUtil.ourPropertyValueB; +import static ca.uhn.fhir.jpa.provider.CodeSystemLookupWithPropertiesUtil.propertyCode; +import static ca.uhn.fhir.jpa.provider.CodeSystemLookupWithPropertiesUtil.propertyCodeSystem; +import static ca.uhn.fhir.jpa.provider.CodeSystemLookupWithPropertiesUtil.propertyDisplay; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertTrue; + +public class ResourceProviderR4CodeSystemPropertiesTest extends BaseResourceProviderR4Test { + public static Stream parametersLookup() { + return CodeSystemLookupWithPropertiesUtil.parametersLookupWithProperties(); + } + + @ParameterizedTest + @MethodSource(value = "parametersLookup") + public void testLookup_withProperties_returnsCorrectParameters(List theLookupProperties, List theExpectedReturnedProperties) { + // setup + CodeSystem codeSystem = new CodeSystem(); + codeSystem.setId(ourCodeSystemId); + codeSystem.setUrl(ourCodeSystemUrl); + ConceptDefinitionComponent concept = codeSystem.addConcept().setCode(ourCode) + .addProperty(new ConceptPropertyComponent().setCode(ourPropertyA).setValue(new StringType(ourPropertyValueA))) + .addProperty(new ConceptPropertyComponent().setCode(ourPropertyB).setValue(new StringType(ourPropertyValueB))) + .addProperty(new ConceptPropertyComponent().setCode(ourPropertyC).setValue(new Coding(propertyCodeSystem, propertyCode, propertyDisplay))); + + myCodeSystemDao.create(codeSystem, mySrd); + + // test + IOperationUntypedWithInputAndPartialOutput respParam = myClient + .operation() + .onType(CodeSystem.class) + .named(JpaConstants.OPERATION_LOOKUP) + .withParameter(Parameters.class, "code", new CodeType(ourCode)) + .andParameter("system", new UriType(ourCodeSystemUrl)); + + theLookupProperties.forEach(p -> respParam.andParameter("property", new CodeType(p))); + Parameters parameters = respParam.execute(); + + // verify + if (theExpectedReturnedProperties.isEmpty()) { + assertFalse(parameters.hasParameter("property")); + return; + } + + assertTrue(parameters.hasParameter("property")); + Iterator parameterPropertyIterator = parameters.getParameters("property").iterator(); + + Iterator propertyIterator = concept.getProperty().stream() + .filter(property -> theExpectedReturnedProperties.contains(property.getCode())).iterator(); + + while (propertyIterator.hasNext()) { + ConceptPropertyComponent property = propertyIterator.next(); + + assertTrue(parameterPropertyIterator.hasNext()); + ParametersParameterComponent parameter = parameterPropertyIterator.next(); + Iterator parameterPartIterator = parameter.getPart().iterator(); + + parameter = parameterPartIterator.next(); + assertEquals("code", parameter.getName()); + assertEquals(property.getCode(), ((CodeType) parameter.getValue()).getValue()); + + parameter = parameterPartIterator.next(); + assertEquals("value", parameter.getName()); + assertTrue(property.getValue().equalsShallow(parameter.getValue())); + + assertFalse(parameterPartIterator.hasNext()); + } + } +} diff --git a/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/provider/r4/ResourceProviderR4CodeSystemTest.java b/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/provider/r4/ResourceProviderR4CodeSystemTest.java index 24958f4153b..06693388bee 100644 --- a/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/provider/r4/ResourceProviderR4CodeSystemTest.java +++ b/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/provider/r4/ResourceProviderR4CodeSystemTest.java @@ -138,6 +138,29 @@ public class ResourceProviderR4CodeSystemTest extends BaseResourceProviderR4Test assertEquals(false, ((BooleanType) respParam.getParameter().get(3).getValue()).getValue()); } + @Test + public void testLookupOperationByCodeAndSystemWithPropertiesBuiltInCode() { + Parameters respParam = myClient + .operation() + .onType(CodeSystem.class) + .named("lookup") + .withParameter(Parameters.class, "code", new CodeType("ACSN")) + .andParameter("system", new UriType("http://terminology.hl7.org/CodeSystem/v2-0203")) + .execute(); + + String resp = myFhirContext.newXmlParser().setPrettyPrint(true).encodeResourceToString(respParam); + ourLog.info(resp); + + assertEquals("name", respParam.getParameter().get(0).getName()); + assertEquals("v2.0203", ((StringType) respParam.getParameter().get(0).getValue()).getValue()); + assertEquals("version", respParam.getParameter().get(1).getName()); + assertEquals("2.9", ((StringType) respParam.getParameter().get(1).getValue()).getValue()); + assertEquals("display", respParam.getParameter().get(2).getName()); + assertEquals("Accession ID", ((StringType) respParam.getParameter().get(2).getValue()).getValue()); + assertEquals("abstract", respParam.getParameter().get(3).getName()); + assertEquals(false, ((BooleanType) respParam.getParameter().get(3).getValue()).getValue()); + } + @Test public void testLookupOperationByCodeAndSystemBuiltInNonexistantCode() { try { diff --git a/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/provider/r4/ResourceProviderR4EverythingTest.java b/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/provider/r4/ResourceProviderR4EverythingTest.java index ea08b3b0b15..171120f54cb 100644 --- a/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/provider/r4/ResourceProviderR4EverythingTest.java +++ b/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/provider/r4/ResourceProviderR4EverythingTest.java @@ -1012,7 +1012,6 @@ public class ResourceProviderR4EverythingTest extends BaseResourceProviderR4Test assertThat(ids, containsInAnyOrder("Patient/FOO", "Observation/BAZ")); } - @Test public void testPagingOverEverythingSet() throws InterruptedException { Patient p = new Patient(); diff --git a/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/provider/r4/ResourceProviderR4RemoteTerminologyTest.java b/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/provider/r4/ResourceProviderR4RemoteTerminologyTest.java index b2cfe855dbd..0198b2c7ee9 100644 --- a/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/provider/r4/ResourceProviderR4RemoteTerminologyTest.java +++ b/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/provider/r4/ResourceProviderR4RemoteTerminologyTest.java @@ -32,7 +32,7 @@ import org.junit.jupiter.api.extension.RegisterExtension; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Qualifier; -import javax.servlet.http.HttpServletRequest; +import jakarta.servlet.http.HttpServletRequest; import java.util.ArrayList; import java.util.List; diff --git a/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/provider/r4/ResourceProviderR4Test.java b/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/provider/r4/ResourceProviderR4Test.java index f2cb94da312..6c28d39348a 100644 --- a/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/provider/r4/ResourceProviderR4Test.java +++ b/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/provider/r4/ResourceProviderR4Test.java @@ -1,5 +1,6 @@ package ca.uhn.fhir.jpa.provider.r4; +import ca.uhn.fhir.context.RuntimeSearchParam; import ca.uhn.fhir.i18n.HapiLocalizer; import ca.uhn.fhir.i18n.Msg; import ca.uhn.fhir.jpa.api.config.JpaStorageSettings; @@ -25,11 +26,13 @@ import ca.uhn.fhir.model.primitive.InstantDt; import ca.uhn.fhir.model.primitive.UriDt; import ca.uhn.fhir.parser.IParser; import ca.uhn.fhir.parser.StrictErrorHandler; +import ca.uhn.fhir.rest.api.CacheControlDirective; import ca.uhn.fhir.rest.api.Constants; import ca.uhn.fhir.rest.api.MethodOutcome; import ca.uhn.fhir.rest.api.PreferReturnEnum; import ca.uhn.fhir.rest.api.SearchTotalModeEnum; import ca.uhn.fhir.rest.api.SummaryEnum; +import ca.uhn.fhir.rest.api.server.IRestfulServer; import ca.uhn.fhir.rest.api.server.RequestDetails; import ca.uhn.fhir.rest.client.apache.ResourceEntity; import ca.uhn.fhir.rest.client.api.IClientInterceptor; @@ -42,6 +45,7 @@ import ca.uhn.fhir.rest.gclient.NumberClientParam; import ca.uhn.fhir.rest.gclient.StringClientParam; import ca.uhn.fhir.rest.param.DateRangeParam; import ca.uhn.fhir.rest.param.ParamPrefixEnum; +import ca.uhn.fhir.rest.server.IPagingProvider; import ca.uhn.fhir.rest.server.exceptions.InternalErrorException; import ca.uhn.fhir.rest.server.exceptions.InvalidRequestException; import ca.uhn.fhir.rest.server.exceptions.PreconditionFailedException; @@ -50,6 +54,7 @@ import ca.uhn.fhir.rest.server.exceptions.UnprocessableEntityException; import ca.uhn.fhir.rest.server.interceptor.RequestValidatingInterceptor; import ca.uhn.fhir.util.ClasspathUtil; import ca.uhn.fhir.util.StopWatch; +import ca.uhn.fhir.util.TestUtil; import ca.uhn.fhir.util.UrlUtil; import com.google.common.base.Charsets; import com.google.common.collect.Lists; @@ -159,15 +164,17 @@ import org.junit.jupiter.api.Nested; import org.junit.jupiter.api.Test; import org.junit.jupiter.params.ParameterizedTest; import org.junit.jupiter.params.provider.Arguments; +import org.junit.jupiter.params.provider.CsvSource; import org.junit.jupiter.params.provider.MethodSource; import org.junit.jupiter.params.provider.ValueSource; +import org.mockito.Spy; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.test.util.AopTestUtils; import org.springframework.transaction.TransactionStatus; import org.springframework.transaction.support.TransactionCallbackWithoutResult; import org.springframework.transaction.support.TransactionTemplate; -import javax.annotation.Nonnull; +import jakarta.annotation.Nonnull; import java.io.BufferedReader; import java.io.IOException; import java.io.InputStreamReader; @@ -220,6 +227,9 @@ import static org.junit.jupiter.api.Assertions.assertNotNull; import static org.junit.jupiter.api.Assertions.assertNull; import static org.junit.jupiter.api.Assertions.assertTrue; import static org.junit.jupiter.api.Assertions.fail; +import static org.mockito.Mockito.doReturn; +import static org.mockito.Mockito.spy; +import static org.mockito.Mockito.when; @SuppressWarnings("Duplicates") public class ResourceProviderR4Test extends BaseResourceProviderR4Test { @@ -255,6 +265,8 @@ public class ResourceProviderR4Test extends BaseResourceProviderR4Test { myStorageSettings.setUpdateWithHistoryRewriteEnabled(false); myStorageSettings.setPreserveRequestIdInResourceBody(false); + when(myPagingProvider.canStoreSearchResults()) + .thenCallRealMethod(); } @BeforeEach @@ -359,15 +371,20 @@ public class ResourceProviderR4Test extends BaseResourceProviderR4Test { IFhirResourceDao searchParameterDao = myDaoRegistry.getResourceDao(SearchParameter.class); searchParameterDao.create(searchParameter, (RequestDetails) null); + RuntimeSearchParam sp = mySearchParamRegistry.getActiveSearchParam("Organization", "_profile"); + assertNotNull(sp); + IFhirResourceDao organizationDao = myDaoRegistry.getResourceDao(Organization.class); Organization organizationWithNoProfile = new Organization(); organizationWithNoProfile.setName("noProfile"); - organizationDao.create(organizationWithNoProfile); + organizationDao.create(organizationWithNoProfile, mySrd); + myCaptureQueriesListener.clear(); Organization organizationWithProfile = new Organization(); organizationWithProfile.setName("withProfile"); organizationWithProfile.getMeta().addProfile("http://foo"); - organizationDao.create(organizationWithProfile); + organizationDao.create(organizationWithProfile, mySrd); + myCaptureQueriesListener.logInsertQueries(); runInTransaction(() -> { List matched = myResourceIndexedSearchParamUriDao.findAll().stream() @@ -932,12 +949,14 @@ public class ResourceProviderR4Test extends BaseResourceProviderR4Test { ourLog.info("About to perform search for: {}", theUri); + myCaptureQueriesListener.clear(); try (CloseableHttpResponse response = ourHttpClient.execute(get)) { String resp = IOUtils.toString(response.getEntity().getContent(), StandardCharsets.UTF_8); ourLog.info(resp); Bundle bundle = myFhirContext.newXmlParser().parseResource(Bundle.class, resp); ids = toUnqualifiedIdValues(bundle); } + myCaptureQueriesListener.logSelectQueries(true, true); return ids; } @@ -2718,6 +2737,90 @@ public class ResourceProviderR4Test extends BaseResourceProviderR4Test { assertEquals(total + 1, ids.size()); } + + @ParameterizedTest + @CsvSource({ + "true,19,10", + "false,19,10", + "true,20,0", + "false,20,0" + }) + public void testPagingWithIncludesReturnsConsistentValues( + boolean theAllowStoringSearchResults, + int theResourceCount, + int theOrgCount + ) { + // setup + + // create resources + { + Coding tagCode = new Coding(); + tagCode.setCode("test"); + tagCode.setSystem("http://example.com"); + int orgCount = theOrgCount; + for (int i = 0; i < theResourceCount; i++) { + Task t = new Task(); + t.getMeta() + .addTag(tagCode); + t.setStatus(Task.TaskStatus.REQUESTED); + if (orgCount > 0) { + Organization org = new Organization(); + org.setName("ORG"); + IIdType orgId = myOrganizationDao.create(org).getId().toUnqualifiedVersionless(); + + orgCount--; + t.getOwner().setReference(orgId.getValue()); + } + myTaskDao.create(t); + } + } + + // when + if (!theAllowStoringSearchResults) { + // we don't actually allow this in our current + // pagingProvider implementations (except for history). + // But we will test with it because our ResponsePage + // is what's under test here + when(myPagingProvider.canStoreSearchResults()) + .thenReturn(false); + } + + int requestedAmount = 10; + Bundle bundle = myClient + .search() + .byUrl("Task?_count=10&_tag=test&status=requested&_include=Task%3Aowner&_sort=status") + .returnBundle(Bundle.class) + .execute(); + int count = bundle.getEntry().size(); + assertFalse(bundle.getEntry().isEmpty()); + + String nextUrl = null; + do { + Bundle.BundleLinkComponent nextLink = bundle.getLink("next"); + if (nextLink != null) { + nextUrl = nextLink.getUrl(); + + // make sure we're always requesting 10 + assertTrue(nextUrl.contains(String.format("_count=%d", requestedAmount))); + + // get next batch + bundle = myClient.fetchResourceFromUrl(Bundle.class, nextUrl); + int received = bundle.getEntry().size(); + + // every next result should produce results + assertFalse(bundle.getEntry().isEmpty()); + count += received; + } else { + nextUrl = null; + } + } while (nextUrl != null); + + // verify + // we should receive all resources and linked resources + assertEquals(theResourceCount + theOrgCount, count); + } + + @Test public void testPagingWithIncludesReturnsConsistentValues() { // setup @@ -3204,7 +3307,11 @@ public class ResourceProviderR4Test extends BaseResourceProviderR4Test { }); myCaptureQueriesListener.logAllQueriesForCurrentThread(); - Bundle bundle = myClient.search().forResource("Patient").returnBundle(Bundle.class).execute(); + Bundle bundle = myClient + .search() + .forResource("Patient") + .returnBundle(Bundle.class) + .execute(); ourLog.debug("Result: {}", myFhirContext.newJsonParser().setPrettyPrint(true).encodeResourceToString(bundle)); assertEquals(2, bundle.getTotal()); assertEquals(1, bundle.getEntry().size()); @@ -6617,7 +6724,7 @@ public class ResourceProviderR4Test extends BaseResourceProviderR4Test { // Update Patient after delay int delayInMs = 1000; - TimeUnit.MILLISECONDS.sleep(delayInMs); + TestUtil.sleepAtLeast(delayInMs + 100); patient.getNameFirstRep().addGiven("Bob"); myClient.update().resource(patient).execute(); diff --git a/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/provider/r4/ResourceProviderR4ValueSetHSearchDisabledTest.java b/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/provider/r4/ResourceProviderR4ValueSetHSearchDisabledTest.java index a71da858d22..7b69a0a49e9 100644 --- a/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/provider/r4/ResourceProviderR4ValueSetHSearchDisabledTest.java +++ b/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/provider/r4/ResourceProviderR4ValueSetHSearchDisabledTest.java @@ -28,7 +28,7 @@ import org.springframework.transaction.TransactionStatus; import org.springframework.transaction.support.TransactionCallbackWithoutResult; import org.springframework.transaction.support.TransactionTemplate; -import javax.annotation.Nonnull; +import jakarta.annotation.Nonnull; import java.io.IOException; import static org.hamcrest.MatcherAssert.assertThat; diff --git a/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/provider/r4/ResourceProviderR4ValueSetNoVerCSNoVerTest.java b/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/provider/r4/ResourceProviderR4ValueSetNoVerCSNoVerTest.java index 3ac4d4ed784..2ab6dd76bf7 100644 --- a/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/provider/r4/ResourceProviderR4ValueSetNoVerCSNoVerTest.java +++ b/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/provider/r4/ResourceProviderR4ValueSetNoVerCSNoVerTest.java @@ -1,5 +1,6 @@ package ca.uhn.fhir.jpa.provider.r4; +import ca.uhn.fhir.context.support.IValidationSupport; import ca.uhn.fhir.i18n.Msg; import ca.uhn.fhir.jpa.api.config.JpaStorageSettings; import ca.uhn.fhir.jpa.api.dao.IFhirResourceDao; @@ -14,7 +15,6 @@ import ca.uhn.fhir.jpa.model.dao.JpaPid; import ca.uhn.fhir.jpa.model.entity.ResourceTable; import ca.uhn.fhir.jpa.model.util.JpaConstants; import ca.uhn.fhir.jpa.provider.BaseResourceProviderR4Test; -import ca.uhn.fhir.jpa.provider.ValueSetOperationProvider; import ca.uhn.fhir.jpa.term.api.ITermCodeSystemStorageSvc; import ca.uhn.fhir.rest.server.exceptions.InvalidRequestException; import ca.uhn.fhir.rest.server.exceptions.ResourceNotFoundException; @@ -55,7 +55,7 @@ import org.springframework.transaction.TransactionStatus; import org.springframework.transaction.support.TransactionCallbackWithoutResult; import org.springframework.transaction.support.TransactionTemplate; -import javax.annotation.Nonnull; +import jakarta.annotation.Nonnull; import java.io.IOException; import java.nio.charset.StandardCharsets; import java.util.List; @@ -1075,7 +1075,7 @@ public class ResourceProviderR4ValueSetNoVerCSNoVerTest extends BaseResourceProv .execute(); ourLog.debug(myFhirContext.newJsonParser().setPrettyPrint(true).encodeResourceToString(expansion)); assertThat(toDirectCodes(expansion.getExpansion().getContains()), containsInAnyOrder("A", "AA", "AB", "AAA")); - assertEquals(12, myCaptureQueriesListener.getSelectQueries().size()); + assertEquals(14, myCaptureQueriesListener.getSelectQueries().size(), ()->myCaptureQueriesListener.logSelectQueries().stream().map(t->t.getSql(true, false)).collect(Collectors.joining("\n * "))); assertEquals("ValueSet \"ValueSet.url[http://example.com/my_value_set]\" has not yet been pre-expanded. Performing in-memory expansion without parameters. Current status: NOT_EXPANDED | The ValueSet is waiting to be picked up and pre-expanded by a scheduled task.", expansion.getMeta().getExtensionString(EXT_VALUESET_EXPANSION_MESSAGE)); // Hierarchical @@ -1092,7 +1092,7 @@ public class ResourceProviderR4ValueSetNoVerCSNoVerTest extends BaseResourceProv assertThat(toDirectCodes(expansion.getExpansion().getContains()), containsInAnyOrder("A")); assertThat(toDirectCodes(expansion.getExpansion().getContains().get(0).getContains()), containsInAnyOrder("AA", "AB")); assertThat(toDirectCodes(expansion.getExpansion().getContains().get(0).getContains().stream().filter(t -> t.getCode().equals("AA")).findFirst().orElseThrow(() -> new IllegalArgumentException()).getContains()), containsInAnyOrder("AAA")); - assertEquals(13, myCaptureQueriesListener.getSelectQueries().size()); + assertEquals(15, myCaptureQueriesListener.getSelectQueries().size()); } @@ -1115,7 +1115,7 @@ public class ResourceProviderR4ValueSetNoVerCSNoVerTest extends BaseResourceProv .execute(); ourLog.debug(myFhirContext.newJsonParser().setPrettyPrint(true).encodeResourceToString(expansion)); assertThat(toDirectCodes(expansion.getExpansion().getContains()), containsInAnyOrder("A", "AA", "AB", "AAA")); - assertEquals(8, myCaptureQueriesListener.getSelectQueries().size()); + assertEquals(10, myCaptureQueriesListener.getSelectQueries().size(), ()->myCaptureQueriesListener.logSelectQueries().stream().map(t->t.getSql(true, false)).collect(Collectors.joining("\n * "))); assertEquals("ValueSet with URL \"Unidentified ValueSet\" was expanded using an in-memory expansion", expansion.getMeta().getExtensionString(EXT_VALUESET_EXPANSION_MESSAGE)); // Hierarchical @@ -1132,7 +1132,7 @@ public class ResourceProviderR4ValueSetNoVerCSNoVerTest extends BaseResourceProv assertThat(toDirectCodes(expansion.getExpansion().getContains()), containsInAnyOrder("A")); assertThat(toDirectCodes(expansion.getExpansion().getContains().get(0).getContains()), containsInAnyOrder("AA", "AB")); assertThat(toDirectCodes(expansion.getExpansion().getContains().get(0).getContains().stream().filter(t -> t.getCode().equals("AA")).findFirst().orElseThrow(() -> new IllegalArgumentException()).getContains()), containsInAnyOrder("AAA")); - assertEquals(11, myCaptureQueriesListener.getSelectQueries().size()); + assertEquals(13, myCaptureQueriesListener.getSelectQueries().size()); } @@ -1295,13 +1295,13 @@ public class ResourceProviderR4ValueSetNoVerCSNoVerTest extends BaseResourceProv String resp = myFhirContext.newXmlParser().setPrettyPrint(true).encodeResourceToString(respParam); ourLog.info(resp); - assertEquals(ValueSetOperationProvider.RESULT, respParam.getParameter().get(0).getName()); + assertEquals(IValidationSupport.CodeValidationResult.RESULT, respParam.getParameter().get(0).getName()); assertEquals(true, ((BooleanType) respParam.getParameter().get(0).getValue()).getValue()); - assertEquals(ValueSetOperationProvider.DISPLAY, respParam.getParameter().get(1).getName()); + assertEquals(IValidationSupport.CodeValidationResult.DISPLAY, respParam.getParameter().get(1).getName()); assertEquals("Male", ((StringType) respParam.getParameter().get(1).getValue()).getValue()); - assertEquals(ValueSetOperationProvider.SOURCE_DETAILS, respParam.getParameter().get(2).getName()); + assertEquals(IValidationSupport.CodeValidationResult.SOURCE_DETAILS, respParam.getParameter().get(2).getName()); assertEquals("Code was validated against in-memory expansion of ValueSet: http://hl7.org/fhir/ValueSet/administrative-gender", ((StringType) respParam.getParameter().get(2).getValue()).getValue()); } diff --git a/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/provider/r4/ResourceProviderR4ValueSetVerCSNoVerTest.java b/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/provider/r4/ResourceProviderR4ValueSetVerCSNoVerTest.java index 2ce02edde6a..7b56df6dbc8 100644 --- a/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/provider/r4/ResourceProviderR4ValueSetVerCSNoVerTest.java +++ b/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/provider/r4/ResourceProviderR4ValueSetVerCSNoVerTest.java @@ -49,7 +49,7 @@ import org.springframework.transaction.TransactionStatus; import org.springframework.transaction.support.TransactionCallbackWithoutResult; import org.springframework.transaction.support.TransactionTemplate; -import javax.annotation.Nonnull; +import jakarta.annotation.Nonnull; import java.io.IOException; import java.util.Optional; diff --git a/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/provider/r4/ResourceProviderR4ValueSetVerCSVerTest.java b/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/provider/r4/ResourceProviderR4ValueSetVerCSVerTest.java index 57f71d85b37..cc8d09d8ff7 100644 --- a/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/provider/r4/ResourceProviderR4ValueSetVerCSVerTest.java +++ b/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/provider/r4/ResourceProviderR4ValueSetVerCSVerTest.java @@ -42,7 +42,7 @@ import org.springframework.transaction.TransactionStatus; import org.springframework.transaction.support.TransactionCallbackWithoutResult; import org.springframework.transaction.support.TransactionTemplate; -import javax.annotation.Nonnull; +import jakarta.annotation.Nonnull; import java.io.IOException; import java.util.Optional; diff --git a/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/provider/r4/ResourceProviderRevIncludeTest.java b/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/provider/r4/ResourceProviderRevIncludeTest.java index eb42c570e31..64d36445856 100644 --- a/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/provider/r4/ResourceProviderRevIncludeTest.java +++ b/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/provider/r4/ResourceProviderRevIncludeTest.java @@ -12,11 +12,15 @@ import org.hl7.fhir.instance.model.api.IIdType; import org.hl7.fhir.r4.model.Bundle; import org.hl7.fhir.r4.model.CareTeam; import org.hl7.fhir.r4.model.DetectedIssue; +import org.hl7.fhir.r4.model.Encounter; +import org.hl7.fhir.r4.model.EpisodeOfCare; import org.hl7.fhir.r4.model.Group; +import org.hl7.fhir.r4.model.Identifier; import org.hl7.fhir.r4.model.Patient; import org.hl7.fhir.r4.model.Practitioner; import org.hl7.fhir.r4.model.PractitionerRole; import org.hl7.fhir.r4.model.Reference; +import org.hl7.fhir.r4.model.Task; import org.junit.jupiter.api.Test; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -140,7 +144,7 @@ public class ResourceProviderRevIncludeTest extends BaseResourceProviderR4Test { //Ensure that the revincludes are included in the query list of the sql trace. //TODO GGG/KHS reduce this to something less than 6 by smarter iterating and getting the resource types earlier when needed. - assertEquals(6, sqlCapturingInterceptor.getQueryList().size()); + assertEquals(5, sqlCapturingInterceptor.getQueryList().size()); myInterceptorRegistry.unregisterInterceptor(sqlCapturingInterceptor); } @@ -174,4 +178,42 @@ public class ResourceProviderRevIncludeTest extends BaseResourceProviderR4Test { assertEquals(practitionerRoleId.getIdPart(), foundResources.get(2).getIdElement().getIdPart()); } + @Test + public void includeRevIncludeIterate() { + Patient p = new Patient(); + String methodName = "includeRevIncludeIterate"; + p.addName().setFamily(methodName); + IIdType pid = myClient.create().resource(p).execute().getId().toUnqualifiedVersionless(); + + EpisodeOfCare episodeOfCare = new EpisodeOfCare(); + episodeOfCare.addIdentifier(new Identifier().setSystem("system1").setValue("value1")); + IIdType episodeOfCareId = myClient.create().resource(episodeOfCare).execute().getId().toUnqualifiedVersionless(); + + Encounter encounter = new Encounter(); + encounter.setSubject(new Reference(pid)); + encounter.addEpisodeOfCare(new Reference(episodeOfCareId)); + IIdType encounterId = myClient.create().resource(encounter).execute().getId().toUnqualifiedVersionless(); + + Task task = new Task(); + task.setEncounter(new Reference(encounterId)); + IIdType taskId = myClient.create().resource(task).execute().getId().toUnqualifiedVersionless(); + + // EpisodeOfCare?identifier=system1|value1&_revinclude=Encounter:episode-of-care&_include:iterate=Encounter:patient&_revinclude:iterate=Task:encounter + Bundle bundle = myClient.search() + .forResource(EpisodeOfCare.class) + .where(EpisodeOfCare.IDENTIFIER.exactly().systemAndIdentifier("system1", "value1")) + .revInclude(new Include("Encounter:episode-of-care")) + .include(new Include("Encounter:patient").setRecurse(true)) + .revInclude(new Include("Task:encounter").setRecurse(true)) + .returnBundle(Bundle.class) + .execute(); + + List foundResources = BundleUtil.toListOfResources(myFhirContext, bundle); + assertEquals(4, foundResources.size()); + assertEquals(episodeOfCareId.getIdPart(), foundResources.get(0).getIdElement().getIdPart()); + assertEquals(encounterId.getIdPart(), foundResources.get(1).getIdElement().getIdPart()); + assertEquals(taskId.getIdPart(), foundResources.get(2).getIdElement().getIdPart()); + assertEquals(pid.getIdPart(), foundResources.get(3).getIdElement().getIdPart()); + } + } diff --git a/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/provider/r4/ServerCapabilityStatementProviderJpaR4Test.java b/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/provider/r4/ServerCapabilityStatementProviderJpaR4Test.java index d1a8a599a01..e64c5ddd7d7 100644 --- a/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/provider/r4/ServerCapabilityStatementProviderJpaR4Test.java +++ b/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/provider/r4/ServerCapabilityStatementProviderJpaR4Test.java @@ -19,7 +19,7 @@ import org.junit.jupiter.api.Test; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import javax.annotation.Nonnull; +import jakarta.annotation.Nonnull; import java.io.IOException; import java.util.List; import java.util.Set; diff --git a/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/provider/r4/StaleSearchDeletingSvcR4Test.java b/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/provider/r4/StaleSearchDeletingSvcR4Test.java index 3d9f3e0a6e9..1e6432f0af1 100644 --- a/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/provider/r4/StaleSearchDeletingSvcR4Test.java +++ b/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/provider/r4/StaleSearchDeletingSvcR4Test.java @@ -48,7 +48,7 @@ public class StaleSearchDeletingSvcR4Test extends BaseResourceProviderR4Test { super.after(); DatabaseSearchCacheSvcImpl staleSearchDeletingSvc = AopTestUtils.getTargetObject(mySearchCacheSvc); staleSearchDeletingSvc.setCutoffSlackForUnitTest(DatabaseSearchCacheSvcImpl.SEARCH_CLEANUP_JOB_INTERVAL_MILLIS); - DatabaseSearchCacheSvcImpl.setMaximumResultsToDeleteForUnitTest(DatabaseSearchCacheSvcImpl.DEFAULT_MAX_RESULTS_TO_DELETE_IN_ONE_STMT); + DatabaseSearchCacheSvcImpl.setMaximumResultsToDeleteInOneStatement(DatabaseSearchCacheSvcImpl.DEFAULT_MAX_RESULTS_TO_DELETE_IN_ONE_STMT); DatabaseSearchCacheSvcImpl.setMaximumResultsToDeleteInOnePassForUnitTest(DatabaseSearchCacheSvcImpl.DEFAULT_MAX_RESULTS_TO_DELETE_IN_ONE_PAS); } @@ -108,7 +108,7 @@ public class StaleSearchDeletingSvcR4Test extends BaseResourceProviderR4Test { @Test public void testDeleteVeryLargeSearch() { - DatabaseSearchCacheSvcImpl.setMaximumResultsToDeleteForUnitTest(10); + DatabaseSearchCacheSvcImpl.setMaximumResultsToDeleteInOneStatement(10); DatabaseSearchCacheSvcImpl.setMaximumResultsToDeleteInOnePassForUnitTest(10); runInTransaction(() -> { @@ -120,24 +120,21 @@ public class StaleSearchDeletingSvcR4Test extends BaseResourceProviderR4Test { search.setResourceType("Patient"); search = mySearchEntityDao.save(search); - for (int i = 0; i < 15; i++) { - ResourceTable resource = new ResourceTable(); - resource.setPublished(new Date()); - resource.setUpdated(new Date()); - resource.setResourceType("Patient"); - resource = myResourceTableDao.saveAndFlush(resource); + ResourceTable resource = new ResourceTable(); + resource.setPublished(new Date()); + resource.setUpdated(new Date()); + resource.setResourceType("Patient"); + resource = myResourceTableDao.saveAndFlush(resource); + for (int i = 0; i < 50; i++) { SearchResult sr = new SearchResult(search); sr.setOrder(i); sr.setResourcePid(resource.getId()); mySearchResultDao.save(sr); } - }); - // It should take two passes to delete the search fully - runInTransaction(() -> assertEquals(1, mySearchEntityDao.count())); - myStaleSearchDeletingSvc.pollForStaleSearchesAndDeleteThem(); + // we are able to delete this in one pass. runInTransaction(() -> assertEquals(1, mySearchEntityDao.count())); myStaleSearchDeletingSvc.pollForStaleSearchesAndDeleteThem(); runInTransaction(() -> assertEquals(0, mySearchEntityDao.count())); @@ -146,9 +143,9 @@ public class StaleSearchDeletingSvcR4Test extends BaseResourceProviderR4Test { @Test public void testDeleteVerySmallSearch() { - DatabaseSearchCacheSvcImpl.setMaximumResultsToDeleteForUnitTest(10); + DatabaseSearchCacheSvcImpl.setMaximumResultsToDeleteInOneStatement(10); - runInTransaction(() -> { + runInTransaction(() -> { Search search = new Search(); search.setStatus(SearchStatusEnum.FINISHED); search.setUuid(UUID.randomUUID().toString()); @@ -172,9 +169,9 @@ public class StaleSearchDeletingSvcR4Test extends BaseResourceProviderR4Test { @Test public void testDontDeleteSearchBeforeExpiry() { - DatabaseSearchCacheSvcImpl.setMaximumResultsToDeleteForUnitTest(10); + DatabaseSearchCacheSvcImpl.setMaximumResultsToDeleteInOneStatement(10); - runInTransaction(() -> { + runInTransaction(() -> { Search search = new Search(); // Expires in one second, so it should not be deleted right away, @@ -186,7 +183,7 @@ public class StaleSearchDeletingSvcR4Test extends BaseResourceProviderR4Test { search.setCreated(DateUtils.addDays(new Date(), -10000)); search.setSearchType(SearchTypeEnum.SEARCH); search.setResourceType("Patient"); - search = mySearchEntityDao.save(search); + mySearchEntityDao.save(search); }); diff --git a/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/provider/r4/SubscriptionsR4Test.java b/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/provider/r4/SubscriptionsR4Test.java index d9a45aa3658..889027665a8 100644 --- a/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/provider/r4/SubscriptionsR4Test.java +++ b/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/provider/r4/SubscriptionsR4Test.java @@ -6,8 +6,8 @@ import ca.uhn.fhir.jpa.util.SubscriptionsRequireManualActivationInterceptorR4; import ca.uhn.fhir.rest.api.EncodingEnum; import ca.uhn.fhir.rest.server.exceptions.UnprocessableEntityException; import org.eclipse.jetty.websocket.api.Session; -import org.eclipse.jetty.websocket.api.annotations.OnWebSocketConnect; import org.eclipse.jetty.websocket.api.annotations.OnWebSocketMessage; +import org.eclipse.jetty.websocket.api.annotations.OnWebSocketOpen; import org.eclipse.jetty.websocket.api.annotations.WebSocket; import org.hl7.fhir.instance.model.api.IBaseResource; import org.hl7.fhir.instance.model.api.IIdType; @@ -186,7 +186,7 @@ public class SubscriptionsR4Test extends BaseResourceProviderR4Test { /** * Basic Echo Client Socket */ - @WebSocket(maxTextMessageSize = 64 * 1024) + @WebSocket public class DynamicEchoSocket extends BaseSocket { private String myCriteria; @@ -200,14 +200,14 @@ public class SubscriptionsR4Test extends BaseResourceProviderR4Test { myEncoding = theEncoding; } - @OnWebSocketConnect + @OnWebSocketOpen public void onConnect(Session session) { ourLog.info("Got connect: {}", session); this.session = session; try { String sending = "bind " + myCriteria; ourLog.info("Sending: {}", sending); - session.getRemote().sendString(sending); + session.sendText(sending, null); } catch (Throwable t) { ourLog.error("Failure", t); } @@ -234,7 +234,7 @@ public class SubscriptionsR4Test extends BaseResourceProviderR4Test { /** * Basic Echo Client Socket */ - @WebSocket(maxTextMessageSize = 64 * 1024) + @WebSocket public class SimpleEchoSocket extends BaseSocket { @SuppressWarnings("unused") @@ -244,14 +244,14 @@ public class SubscriptionsR4Test extends BaseResourceProviderR4Test { mySubsId = theSubsId; } - @OnWebSocketConnect + @OnWebSocketOpen public void onConnect(Session session) { ourLog.info("Got connect: {}", session); this.session = session; try { String sending = "bind " + mySubsId; ourLog.info("Sending: {}", sending); - session.getRemote().sendString(sending); + session.sendText(sending, null); } catch (Throwable t) { ourLog.error("Failure", t); } diff --git a/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/provider/r4/SystemProviderR4Test.java b/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/provider/r4/SystemProviderR4Test.java index 13a54906716..2e7b101b234 100644 --- a/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/provider/r4/SystemProviderR4Test.java +++ b/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/provider/r4/SystemProviderR4Test.java @@ -57,8 +57,8 @@ import org.apache.http.impl.client.CloseableHttpClient; import org.apache.http.impl.client.HttpClientBuilder; import org.apache.http.impl.conn.PoolingHttpClientConnectionManager; import org.eclipse.jetty.server.Server; -import org.eclipse.jetty.servlet.ServletContextHandler; -import org.eclipse.jetty.servlet.ServletHolder; +import org.eclipse.jetty.ee10.servlet.ServletContextHandler; +import org.eclipse.jetty.ee10.servlet.ServletHolder; import org.hl7.fhir.common.hapi.validation.validator.FhirInstanceValidator; import org.hl7.fhir.instance.model.api.IBaseBundle; import org.hl7.fhir.instance.model.api.IIdType; diff --git a/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/provider/r4/SystemProviderTransactionSearchR4Test.java b/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/provider/r4/SystemProviderTransactionSearchR4Test.java index 016e3de04e6..5a293f5610a 100644 --- a/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/provider/r4/SystemProviderTransactionSearchR4Test.java +++ b/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/provider/r4/SystemProviderTransactionSearchR4Test.java @@ -20,8 +20,8 @@ import org.apache.http.impl.client.CloseableHttpClient; import org.apache.http.impl.client.HttpClientBuilder; import org.apache.http.impl.conn.PoolingHttpClientConnectionManager; import org.eclipse.jetty.server.Server; -import org.eclipse.jetty.servlet.ServletContextHandler; -import org.eclipse.jetty.servlet.ServletHolder; +import org.eclipse.jetty.ee10.servlet.ServletContextHandler; +import org.eclipse.jetty.ee10.servlet.ServletHolder; import org.hl7.fhir.r4.model.Bundle; import org.hl7.fhir.r4.model.Bundle.BundleEntryComponent; import org.hl7.fhir.r4.model.Bundle.BundleType; diff --git a/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/reindex/Batch2DaoSvcImplTest.java b/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/reindex/Batch2DaoSvcImplTest.java index d4f5dbf07d2..685890f16df 100644 --- a/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/reindex/Batch2DaoSvcImplTest.java +++ b/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/reindex/Batch2DaoSvcImplTest.java @@ -1,14 +1,13 @@ package ca.uhn.fhir.jpa.reindex; import ca.uhn.fhir.interceptor.model.RequestPartitionId; -import ca.uhn.fhir.jpa.api.config.JpaStorageSettings; -import ca.uhn.fhir.jpa.api.dao.DaoRegistry; -import ca.uhn.fhir.jpa.api.pid.IResourcePidList; +import ca.uhn.fhir.jpa.api.pid.IResourcePidStream; import ca.uhn.fhir.jpa.api.svc.IBatch2DaoSvc; import ca.uhn.fhir.jpa.dao.tx.IHapiTransactionService; import ca.uhn.fhir.jpa.searchparam.MatchUrlService; import ca.uhn.fhir.jpa.test.BaseJpaR4Test; import ca.uhn.fhir.model.primitive.IdDt; +import ca.uhn.fhir.parser.DataFormatException; import ca.uhn.fhir.rest.server.exceptions.InternalErrorException; import org.hl7.fhir.instance.model.api.IIdType; import org.junit.jupiter.api.BeforeEach; @@ -17,19 +16,17 @@ import org.junit.jupiter.params.ParameterizedTest; import org.junit.jupiter.params.provider.ValueSource; import org.springframework.beans.factory.annotation.Autowired; -import javax.annotation.Nonnull; +import jakarta.annotation.Nonnull; import java.time.LocalDate; import java.time.Month; import java.time.ZoneId; import java.util.Date; import java.util.List; import java.util.stream.IntStream; +import java.util.stream.Stream; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertThrows; -import static org.mockito.Mockito.spy; -import static org.mockito.Mockito.times; -import static org.mockito.Mockito.verify; class Batch2DaoSvcImplTest extends BaseJpaR4Test { @@ -37,49 +34,46 @@ class Batch2DaoSvcImplTest extends BaseJpaR4Test { private static final Date TOMORROW = toDate(LocalDate.now().plusDays(1)); private static final String URL_PATIENT_EXPUNGE_TRUE = "Patient?_expunge=true"; private static final String PATIENT = "Patient"; - private static final int INTERNAL_SYNCHRONOUS_SEARCH_SIZE = 10; - @Autowired - private JpaStorageSettings myJpaStorageSettings; @Autowired private MatchUrlService myMatchUrlService; @Autowired private IHapiTransactionService myIHapiTransactionService ; - private DaoRegistry mySpiedDaoRegistry; - private IBatch2DaoSvc mySubject; + @BeforeEach void beforeEach() { - myJpaStorageSettings.setInternalSynchronousSearchSize(INTERNAL_SYNCHRONOUS_SEARCH_SIZE); - mySpiedDaoRegistry = spy(myDaoRegistry); - - mySubject = new Batch2DaoSvcImpl(myResourceTableDao, myMatchUrlService, mySpiedDaoRegistry, myFhirContext, myIHapiTransactionService, myJpaStorageSettings); + mySubject = new Batch2DaoSvcImpl(myResourceTableDao, myMatchUrlService, myDaoRegistry, myFhirContext, myIHapiTransactionService); } // TODO: LD this test won't work with the nonUrl variant yet: error: No existing transaction found for transaction marked with propagation 'mandatory' @Test void fetchResourcesByUrlEmptyUrl() { - final InternalErrorException exception = assertThrows(InternalErrorException.class, () -> mySubject.fetchResourceIdsPage(PREVIOUS_MILLENNIUM, TOMORROW, 800, RequestPartitionId.defaultPartition(), "")); + final InternalErrorException exception = + assertThrows( + InternalErrorException.class, + () -> mySubject.fetchResourceIdStream(PREVIOUS_MILLENNIUM, TOMORROW, RequestPartitionId.defaultPartition(), "") + .visitStream(Stream::toList)); assertEquals("HAPI-2422: this should never happen: URL is missing a '?'", exception.getMessage()); } @Test void fetchResourcesByUrlSingleQuestionMark() { - final InternalErrorException exception = assertThrows(InternalErrorException.class, () -> mySubject.fetchResourceIdsPage(PREVIOUS_MILLENNIUM, TOMORROW, 800, RequestPartitionId.defaultPartition(), "?")); + final IllegalArgumentException exception = assertThrows(IllegalArgumentException.class, () -> mySubject.fetchResourceIdStream(PREVIOUS_MILLENNIUM, TOMORROW, RequestPartitionId.defaultPartition(), "?").visitStream(Stream::toList)); - assertEquals("HAPI-2223: theResourceName must not be blank", exception.getMessage()); + assertEquals("theResourceName must not be blank", exception.getMessage()); } @Test void fetchResourcesByUrlNonsensicalResource() { - final InternalErrorException exception = assertThrows(InternalErrorException.class, () -> mySubject.fetchResourceIdsPage(PREVIOUS_MILLENNIUM, TOMORROW, 800, RequestPartitionId.defaultPartition(), "Banana?_expunge=true")); + final DataFormatException exception = assertThrows(DataFormatException.class, () -> mySubject.fetchResourceIdStream(PREVIOUS_MILLENNIUM, TOMORROW, RequestPartitionId.defaultPartition(), "Banana?_expunge=true").visitStream(Stream::toList)); - assertEquals("HAPI-2223: HAPI-1684: Unknown resource name \"Banana\" (this name is not known in FHIR version \"R4\")", exception.getMessage()); + assertEquals("HAPI-1684: Unknown resource name \"Banana\" (this name is not known in FHIR version \"R4\")", exception.getMessage()); } @ParameterizedTest @@ -89,16 +83,12 @@ class Batch2DaoSvcImplTest extends BaseJpaR4Test { .mapToObj(num -> createPatient()) .toList(); - final IResourcePidList resourcePidList = mySubject.fetchResourceIdsPage(PREVIOUS_MILLENNIUM, TOMORROW, 800, RequestPartitionId.defaultPartition(), URL_PATIENT_EXPUNGE_TRUE); + final IResourcePidStream resourcePidList = mySubject.fetchResourceIdStream(PREVIOUS_MILLENNIUM, TOMORROW, RequestPartitionId.defaultPartition(), URL_PATIENT_EXPUNGE_TRUE); final List actualPatientIds = - resourcePidList.getTypedResourcePids() - .stream() - .map(typePid -> new IdDt(typePid.resourceType, (Long) typePid.id.getId())) - .toList(); + resourcePidList.visitStream(s-> s.map(typePid -> new IdDt(typePid.resourceType, (Long) typePid.id.getId())) + .toList()); assertIdsEqual(patientIds, actualPatientIds); - - verify(mySpiedDaoRegistry, times(getExpectedNumOfInvocations(expectedNumResults))).getResourceDao(PATIENT); } @ParameterizedTest @@ -109,22 +99,14 @@ class Batch2DaoSvcImplTest extends BaseJpaR4Test { .mapToObj(num -> createPatient()) .toList(); - final IResourcePidList resourcePidList = mySubject.fetchResourceIdsPage(PREVIOUS_MILLENNIUM, TOMORROW, pageSizeWellBelowThreshold, RequestPartitionId.defaultPartition(), null); + final IResourcePidStream resourcePidList = mySubject.fetchResourceIdStream(PREVIOUS_MILLENNIUM, TOMORROW, RequestPartitionId.defaultPartition(), null); final List actualPatientIds = - resourcePidList.getTypedResourcePids() - .stream() - .map(typePid -> new IdDt(typePid.resourceType, (Long) typePid.id.getId())) - .toList(); + resourcePidList.visitStream(s-> s.map(typePid -> new IdDt(typePid.resourceType, (Long) typePid.id.getId())) + .toList()); assertIdsEqual(patientIds, actualPatientIds); } - private int getExpectedNumOfInvocations(int expectedNumResults) { - final int maxResultsPerQuery = INTERNAL_SYNCHRONOUS_SEARCH_SIZE + 1; - final int division = expectedNumResults / maxResultsPerQuery; - return division + 1; - } - private static void assertIdsEqual(List expectedResourceIds, List actualResourceIds) { assertEquals(expectedResourceIds.size(), actualResourceIds.size()); diff --git a/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/reindex/ResourceReindexSvcImplTest.java b/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/reindex/ResourceReindexSvcImplTest.java index 56c09865ea0..2fcf5b19b1e 100644 --- a/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/reindex/ResourceReindexSvcImplTest.java +++ b/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/reindex/ResourceReindexSvcImplTest.java @@ -1,10 +1,9 @@ package ca.uhn.fhir.jpa.reindex; -import ca.uhn.fhir.jpa.api.pid.IResourcePidList; +import ca.uhn.fhir.jpa.api.pid.IResourcePidStream; import ca.uhn.fhir.jpa.api.pid.TypedResourcePid; import ca.uhn.fhir.jpa.api.svc.IBatch2DaoSvc; import ca.uhn.fhir.jpa.test.BaseJpaR4Test; -import org.hl7.fhir.instance.model.api.IIdType; import org.junit.jupiter.api.MethodOrderer; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.TestMethodOrder; @@ -12,15 +11,13 @@ import org.springframework.beans.factory.annotation.Autowired; import java.util.Date; import java.util.List; +import java.util.stream.Stream; -import static ca.uhn.fhir.batch2.jobs.step.ResourceIdListStep.DEFAULT_PAGE_SIZE; import static org.hamcrest.MatcherAssert.assertThat; import static org.hamcrest.Matchers.contains; import static org.junit.jupiter.api.Assertions.assertEquals; -import static org.junit.jupiter.api.Assertions.assertNull; import static org.junit.jupiter.api.Assertions.assertTrue; -@SuppressWarnings("unchecked") @TestMethodOrder(value = MethodOrderer.MethodName.class) public class ResourceReindexSvcImplTest extends BaseJpaR4Test { @@ -55,14 +52,12 @@ public class ResourceReindexSvcImplTest extends BaseJpaR4Test { // Execute myCaptureQueriesListener.clear(); - IResourcePidList page = mySvc.fetchResourceIdsPage(start, end, DEFAULT_PAGE_SIZE, null, null); + IResourcePidStream queryStream = mySvc.fetchResourceIdStream(start, end, null, null); // Verify - - assertEquals(3, page.size()); - assertThat(page.getTypedResourcePids(), contains(new TypedResourcePid("Patient", id0), new TypedResourcePid("Patient", id1), new TypedResourcePid("Observation", id2))); - assertTrue(page.getLastDate().after(beforeLastInRange)); - assertTrue(page.getLastDate().before(end)); + List typedPids = queryStream.visitStream(Stream::toList); + assertEquals(3, typedPids.size()); + assertThat(typedPids, contains(new TypedResourcePid("Patient", id0), new TypedResourcePid("Patient", id1), new TypedResourcePid("Observation", id2))); assertEquals(1, myCaptureQueriesListener.logSelectQueries().size()); assertEquals(0, myCaptureQueriesListener.countInsertQueries()); @@ -85,13 +80,12 @@ public class ResourceReindexSvcImplTest extends BaseJpaR4Test { // Execute myCaptureQueriesListener.clear(); - IResourcePidList page = mySvc.fetchResourceIdsPage(start, end, DEFAULT_PAGE_SIZE, null, null); + IResourcePidStream queryStream = mySvc.fetchResourceIdStream(start, end, null, null); // Verify + List typedPids = queryStream.visitStream(Stream::toList); - assertTrue(page.isEmpty()); - assertEquals(0, page.size()); - assertNull(page.getLastDate()); + assertTrue(typedPids.isEmpty()); assertEquals(1, myCaptureQueriesListener.logSelectQueries().size()); assertEquals(0, myCaptureQueriesListener.countInsertQueries()); @@ -133,19 +127,16 @@ public class ResourceReindexSvcImplTest extends BaseJpaR4Test { // Execute myCaptureQueriesListener.clear(); - IResourcePidList page = mySvc.fetchResourceIdsPage(start, end, DEFAULT_PAGE_SIZE, null, "Patient?active=false"); + IResourcePidStream queryStream = mySvc.fetchResourceIdStream(start, end, null, "Patient?active=false"); // Verify + List typedResourcePids = queryStream.visitStream(Stream::toList); - assertEquals(4, page.size()); - List typedResourcePids = page.getTypedResourcePids(); - assertThat(page.getTypedResourcePids(), - contains(new TypedResourcePid("Patient", patientId0), + assertEquals(2, typedResourcePids.size()); + assertThat(typedResourcePids, + contains( new TypedResourcePid("Patient", patientId1), - new TypedResourcePid("Patient", patientId2), - new TypedResourcePid("Patient", patientId3))); - assertTrue(page.getLastDate().after(beforeLastInRange)); - assertTrue(page.getLastDate().before(end) || page.getLastDate().equals(end)); + new TypedResourcePid("Patient", patientId2))); assertEquals(1, myCaptureQueriesListener.logSelectQueries().size()); assertEquals(0, myCaptureQueriesListener.countInsertQueries()); diff --git a/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/search/reindex/ResourceReindexingSvcImplTest.java b/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/search/reindex/ResourceReindexingSvcImplTest.java index 4171540f847..b01715e9b27 100644 --- a/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/search/reindex/ResourceReindexingSvcImplTest.java +++ b/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/search/reindex/ResourceReindexingSvcImplTest.java @@ -6,7 +6,6 @@ import ca.uhn.fhir.jpa.api.dao.DaoRegistry; import ca.uhn.fhir.jpa.api.dao.IFhirResourceDao; import ca.uhn.fhir.jpa.dao.BaseHapiFhirDao; import ca.uhn.fhir.jpa.model.dao.JpaPid; -import ca.uhn.fhir.jpa.dao.data.IForcedIdDao; import ca.uhn.fhir.jpa.dao.data.IResourceReindexJobDao; import ca.uhn.fhir.jpa.dao.data.IResourceTableDao; import ca.uhn.fhir.jpa.entity.ResourceReindexJobEntity; @@ -61,8 +60,6 @@ public class ResourceReindexingSvcImplTest { @Mock private DaoRegistry myDaoRegistry; @Mock - private IForcedIdDao myForcedIdDao; - @Mock private IResourceReindexJobDao myReindexJobDao; @Mock private IResourceTableDao myResourceTableDao; diff --git a/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/stresstest/GiantTransactionPerfTest.java b/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/stresstest/GiantTransactionPerfTest.java index ef4e4b36e2b..ec9d5d8a4d5 100644 --- a/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/stresstest/GiantTransactionPerfTest.java +++ b/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/stresstest/GiantTransactionPerfTest.java @@ -77,22 +77,22 @@ import org.springframework.transaction.TransactionStatus; import org.springframework.transaction.support.SimpleTransactionStatus; import org.springframework.transaction.support.TransactionSynchronizationManager; -import javax.annotation.Nonnull; -import javax.annotation.Nullable; -import javax.persistence.EntityGraph; -import javax.persistence.EntityManager; -import javax.persistence.EntityManagerFactory; -import javax.persistence.EntityTransaction; -import javax.persistence.FlushModeType; -import javax.persistence.LockModeType; -import javax.persistence.Query; -import javax.persistence.StoredProcedureQuery; -import javax.persistence.TypedQuery; -import javax.persistence.criteria.CriteriaBuilder; -import javax.persistence.criteria.CriteriaDelete; -import javax.persistence.criteria.CriteriaQuery; -import javax.persistence.criteria.CriteriaUpdate; -import javax.persistence.metamodel.Metamodel; +import jakarta.annotation.Nonnull; +import jakarta.annotation.Nullable; +import jakarta.persistence.EntityGraph; +import jakarta.persistence.EntityManager; +import jakarta.persistence.EntityManagerFactory; +import jakarta.persistence.EntityTransaction; +import jakarta.persistence.FlushModeType; +import jakarta.persistence.LockModeType; +import jakarta.persistence.Query; +import jakarta.persistence.StoredProcedureQuery; +import jakarta.persistence.TypedQuery; +import jakarta.persistence.criteria.CriteriaBuilder; +import jakarta.persistence.criteria.CriteriaDelete; +import jakarta.persistence.criteria.CriteriaQuery; +import jakarta.persistence.criteria.CriteriaUpdate; +import jakarta.persistence.metamodel.Metamodel; import java.util.ArrayList; import java.util.Collections; import java.util.List; @@ -181,6 +181,7 @@ public class GiantTransactionPerfTest { myHapiTransactionService.setTransactionManager(myTransactionManager); myHapiTransactionService.setInterceptorBroadcaster(myInterceptorSvc); myHapiTransactionService.setRequestPartitionSvcForUnitTest(myRequestPartitionHelperSvc); + myHapiTransactionService.setPartitionSettingsForUnitTest(new PartitionSettings()); myTransactionProcessor = new TransactionProcessor(); myTransactionProcessor.setContext(ourFhirContext); @@ -396,6 +397,11 @@ public class GiantTransactionPerfTest { throw new UnsupportedOperationException(); } + @Override + public void updateNonInlinedContents(byte[] theText, long thePid) { + throw new UnsupportedOperationException(); + } + @Nonnull @Override public List findAll() { diff --git a/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/subscription/BaseSubscriptionsR4Test.java b/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/subscription/BaseSubscriptionsR4Test.java index 1b315cdca30..cd9eeecf4b2 100644 --- a/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/subscription/BaseSubscriptionsR4Test.java +++ b/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/subscription/BaseSubscriptionsR4Test.java @@ -35,7 +35,7 @@ import org.junit.jupiter.api.Order; import org.junit.jupiter.api.extension.RegisterExtension; import org.springframework.beans.factory.annotation.Autowired; -import javax.annotation.PostConstruct; +import jakarta.annotation.PostConstruct; import java.util.ArrayList; import java.util.Collections; import java.util.List; diff --git a/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/subscription/FhirR4Util.java b/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/subscription/FhirR4Util.java index fa8ea305503..d2e363ba1a6 100644 --- a/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/subscription/FhirR4Util.java +++ b/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/subscription/FhirR4Util.java @@ -12,12 +12,14 @@ import org.hl7.fhir.r4.model.Observation; import org.hl7.fhir.r4.model.Patient; import org.hl7.fhir.r4.model.Subscription; +import jakarta.annotation.Nullable; + public class FhirR4Util { public static final String LPI_CODESYSTEM = "http://cognitivemedicine.com/lpi"; public static final String LPI_CODE = "LPI-FHIR"; - public static Subscription createSubscription(String criteria, String payload, String endpoint, IGenericClient client) { + public static Subscription createSubscription(String criteria, String payload, String endpoint, @Nullable IGenericClient client) { Subscription subscription = new Subscription(); subscription.setReason("Monitor new neonatal function (note, age will be determined by the monitor)"); subscription.setStatus(Subscription.SubscriptionStatus.REQUESTED); @@ -29,8 +31,10 @@ public class FhirR4Util { channel.setEndpoint(endpoint); subscription.setChannel(channel); - MethodOutcome methodOutcome = client.create().resource(subscription).execute(); - subscription.setId(methodOutcome.getId().getIdPart()); + if (client != null) { + MethodOutcome methodOutcome = client.create().resource(subscription).execute(); + subscription.setId(methodOutcome.getId().getIdPart()); + } return subscription; } diff --git a/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/subscription/message/MessageSubscriptionR4Test.java b/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/subscription/message/MessageSubscriptionR4Test.java index 540f5ae1f7d..03b12841055 100644 --- a/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/subscription/message/MessageSubscriptionR4Test.java +++ b/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/subscription/message/MessageSubscriptionR4Test.java @@ -79,7 +79,8 @@ public class MessageSubscriptionR4Test extends BaseSubscriptionsR4Test { myStorageSettings.setTagStorageMode(new JpaStorageSettings().getTagStorageMode()); } - @BeforeEach + @Override + @BeforeEach public void beforeRegisterRestHookListener() { mySubscriptionTestUtil.registerMessageInterceptor(); @@ -252,26 +253,28 @@ public class MessageSubscriptionR4Test extends BaseSubscriptionsR4Test { } @Test - public void testPersistedResourceModifiedMessage_whenFetchFromDb_willEqualOriginalMessage() throws JsonProcessingException { + public void testMethodInflatePersistedResourceModifiedMessage_whenGivenResourceModifiedMessageWithEmptyPayload_willEqualOriginalMessage() { mySubscriptionTestUtil.unregisterSubscriptionInterceptor(); - // given + // setup TransactionTemplate transactionTemplate = new TransactionTemplate(myTxManager); Observation obs = sendObservation("zoop", "SNOMED-CT", "theExplicitSource", "theRequestId"); ResourceModifiedMessage originalResourceModifiedMessage = createResourceModifiedMessage(obs); + ResourceModifiedMessage resourceModifiedMessageWithEmptyPayload = createResourceModifiedMessage(obs); + resourceModifiedMessageWithEmptyPayload.setPayloadToNull(); transactionTemplate.execute(tx -> { - IPersistedResourceModifiedMessage persistedResourceModifiedMessage = myResourceModifiedMessagePersistenceSvc.persist(originalResourceModifiedMessage); + myResourceModifiedMessagePersistenceSvc.persist(originalResourceModifiedMessage); - // when - ResourceModifiedMessage restoredResourceModifiedMessage = myResourceModifiedMessagePersistenceSvc.inflatePersistedResourceModifiedMessage(persistedResourceModifiedMessage); + // execute + ResourceModifiedMessage restoredResourceModifiedMessage = myResourceModifiedMessagePersistenceSvc.inflatePersistedResourceModifiedMessage(resourceModifiedMessageWithEmptyPayload); - // then - assertEquals(toJson(originalResourceModifiedMessage), toJson(restoredResourceModifiedMessage)); - assertEquals(originalResourceModifiedMessage, restoredResourceModifiedMessage); + // verify + assertEquals(toJson(originalResourceModifiedMessage), toJson(restoredResourceModifiedMessage)); + assertEquals(originalResourceModifiedMessage, restoredResourceModifiedMessage); - return null; + return null; }); } diff --git a/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/subscription/resthook/RestHookActivatesPreExistingSubscriptionsR4Test.java b/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/subscription/resthook/RestHookActivatesPreExistingSubscriptionsR4Test.java index b4f22254adb..f5e504131f4 100644 --- a/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/subscription/resthook/RestHookActivatesPreExistingSubscriptionsR4Test.java +++ b/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/subscription/resthook/RestHookActivatesPreExistingSubscriptionsR4Test.java @@ -13,8 +13,8 @@ import ca.uhn.fhir.rest.server.RestfulServer; import ca.uhn.fhir.test.utilities.JettyUtil; import com.google.common.collect.Lists; import org.eclipse.jetty.server.Server; -import org.eclipse.jetty.servlet.ServletContextHandler; -import org.eclipse.jetty.servlet.ServletHolder; +import org.eclipse.jetty.ee10.servlet.ServletContextHandler; +import org.eclipse.jetty.ee10.servlet.ServletHolder; import org.hl7.fhir.instance.model.api.IBaseResource; import org.hl7.fhir.r4.model.CodeableConcept; import org.hl7.fhir.r4.model.Coding; @@ -30,7 +30,7 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; -import javax.servlet.http.HttpServletRequest; +import jakarta.servlet.http.HttpServletRequest; import java.util.ArrayList; import java.util.Collections; import java.util.Enumeration; diff --git a/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/subscription/resthook/RestHookTestR4Test.java b/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/subscription/resthook/RestHookTestR4Test.java index ace7684317f..525f909a088 100644 --- a/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/subscription/resthook/RestHookTestR4Test.java +++ b/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/subscription/resthook/RestHookTestR4Test.java @@ -14,6 +14,7 @@ import ca.uhn.fhir.rest.api.MethodOutcome; import ca.uhn.fhir.rest.api.RestOperationTypeEnum; import ca.uhn.fhir.rest.server.exceptions.UnprocessableEntityException; import ca.uhn.fhir.subscription.SubscriptionTestDataHelper; +import ca.uhn.fhir.util.BundleUtil; import ca.uhn.fhir.util.HapiExtensions; import org.hl7.fhir.instance.model.api.IBaseBundle; import org.hl7.fhir.instance.model.api.IIdType; @@ -28,11 +29,12 @@ import org.hl7.fhir.r4.model.Meta; import org.hl7.fhir.r4.model.Observation; import org.hl7.fhir.r4.model.Parameters; import org.hl7.fhir.r4.model.Patient; +import org.hl7.fhir.r4.model.Reference; +import org.hl7.fhir.r4.model.Resource; import org.hl7.fhir.r4.model.SearchParameter; import org.hl7.fhir.r4.model.StringType; import org.hl7.fhir.r4.model.Subscription; import org.junit.jupiter.api.AfterEach; -import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.junit.jupiter.params.ParameterizedTest; import org.junit.jupiter.params.provider.ValueSource; @@ -41,6 +43,7 @@ import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; import java.util.List; +import java.util.Optional; import java.util.Set; import java.util.concurrent.CountDownLatch; import java.util.concurrent.TimeUnit; @@ -65,6 +68,8 @@ import static org.junit.jupiter.api.Assertions.fail; */ public class RestHookTestR4Test extends BaseSubscriptionsR4Test { private static final Logger ourLog = LoggerFactory.getLogger(RestHookTestR4Test.class); + public static final String TEST_PATIENT_ID = "topic-test-patient-id"; + public static final String PATIENT_REFERENCE = "Patient/" + TEST_PATIENT_ID; @Autowired ResourceModifiedSubmitterSvc myResourceModifiedSubmitterSvc; @@ -1305,8 +1310,73 @@ public class RestHookTestR4Test extends BaseSubscriptionsR4Test { } @Test - public void testTopicSubscription() throws Exception { - Subscription subscription = SubscriptionTestDataHelper.buildR4TopicSubscription(); + public void testRestHoodTopicSubscription_withEmptyPayloadContent_generateCorrectPayload() throws Exception { + String payloadContent = "empty"; + + // execute + Bundle bundle = createAndDispatchTopicSubscription(payloadContent); + + // verify Bundle size + assertEquals(1, bundle.getEntry().size()); + List resources = BundleUtil.toListOfResourcesOfType(myFhirContext, bundle, Resource.class); + assertEquals(1, resources.size()); + + // verify SubscriptionStatus.notificationEvent.focus + Optional focus = getNotificationEventFocus(resources); + assertFalse(focus.isPresent()); + } + + @Test + public void testRestHoodTopicSubscription_withIdOnlyPayloadContent_generateCorrectPayload() throws Exception { + String payloadContent = "id-only"; + + // execute + Bundle bundle = createAndDispatchTopicSubscription(payloadContent); + + // verify Bundle size + assertEquals(2, bundle.getEntry().size()); + List resources = BundleUtil.toListOfResourcesOfType(myFhirContext, bundle, Resource.class); + assertEquals(1, resources.size()); + + // verify SubscriptionStatus.notificationEvent.focus + Optional focus = getNotificationEventFocus(resources); + assertTrue(focus.isPresent()); + assertEquals(TEST_PATIENT_ID, ((Reference) focus.get().getValue()).getReference()); + + // verify Patient Entry + Bundle.BundleEntryComponent patientEntry = bundle.getEntry().get(1); + validateRequestParameters(patientEntry); + Patient bundlePatient = (Patient) patientEntry.getResource(); + assertNull(bundlePatient); + } + + @Test + public void testRestHoodTopicSubscription_withFullResourcePayloadContent_generateCorrectPayload() throws Exception { + String payloadContent = "full-resource"; + + // execute + Bundle bundle = createAndDispatchTopicSubscription(payloadContent); + + // verify Bundle size + assertEquals(2, bundle.getEntry().size()); + List resources = BundleUtil.toListOfResourcesOfType(myFhirContext, bundle, Resource.class); + assertEquals(2, resources.size()); + + // verify SubscriptionStatus.notificationEvent.focus + Optional focus = getNotificationEventFocus(resources); + assertTrue(focus.isPresent()); + assertEquals(PATIENT_REFERENCE, ((Reference) focus.get().getValue()).getReference()); + + // verify Patient Entry + Bundle.BundleEntryComponent patientEntry = bundle.getEntry().get(1); + validateRequestParameters(patientEntry); + Patient bundlePatient = (Patient) patientEntry.getResource(); + assertTrue(bundlePatient.getActive()); + assertEquals(Enumerations.AdministrativeGender.FEMALE, bundlePatient.getGender()); + } + + private Bundle createAndDispatchTopicSubscription(String thePayloadContent) throws Exception { + Subscription subscription = SubscriptionTestDataHelper.buildR4TopicSubscriptionWithContent(thePayloadContent); subscription.setIdElement(null); subscription.setStatus(Subscription.SubscriptionStatus.REQUESTED); Subscription.SubscriptionChannelComponent channel = subscription.getChannel(); @@ -1318,9 +1388,8 @@ public class RestHookTestR4Test extends BaseSubscriptionsR4Test { mySubscriptionIds.add(methodOutcome.getId()); waitForActivatedSubscriptionCount(1); - String patientId = "topic-test-patient-id"; Patient patient = new Patient(); - patient.setId(patientId); + patient.setId(TEST_PATIENT_ID); patient.setActive(true); patient.setGender(Enumerations.AdministrativeGender.FEMALE); @@ -1331,12 +1400,22 @@ public class RestHookTestR4Test extends BaseSubscriptionsR4Test { ourTransactionProvider.waitForTransactionCount(1); - Bundle bundle = ourTransactionProvider.getTransactions().get(0); - assertEquals(2, bundle.getEntry().size()); - Parameters parameters = (Parameters) bundle.getEntry().get(0).getResource(); - // WIP STR5 assert parameters contents - Patient bundlePatient = (Patient) bundle.getEntry().get(1).getResource(); - assertTrue(bundlePatient.getActive()); - assertEquals(Enumerations.AdministrativeGender.FEMALE, bundlePatient.getGender()); + return ourTransactionProvider.getTransactions().get(0); + } + + private Optional getNotificationEventFocus(List theResources) { + assertEquals("Parameters", theResources.get(0).getResourceType().name()); + Parameters parameters = (Parameters) theResources.get(0); + Parameters.ParametersParameterComponent notificationEvent = parameters.getParameter("notification-event"); + assertNotNull(notificationEvent); + return notificationEvent.getPart().stream() + .filter(part -> part.getName().equals("focus")) + .findFirst(); + } + + private void validateRequestParameters(Bundle.BundleEntryComponent thePatientEntry) { + assertNotNull(thePatientEntry.getRequest()); + assertEquals("POST", thePatientEntry.getRequest().getMethod().name()); + assertEquals("Patient", thePatientEntry.getRequest().getUrl()); } } diff --git a/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/subscription/resthook/RestHookTestWithInterceptorRegisteredToStorageSettingsR4Test.java b/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/subscription/resthook/RestHookTestWithInterceptorRegisteredToStorageSettingsR4Test.java index 1cd12d66bce..3410c4877a7 100644 --- a/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/subscription/resthook/RestHookTestWithInterceptorRegisteredToStorageSettingsR4Test.java +++ b/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/subscription/resthook/RestHookTestWithInterceptorRegisteredToStorageSettingsR4Test.java @@ -15,8 +15,8 @@ import ca.uhn.fhir.rest.server.RestfulServer; import ca.uhn.fhir.test.utilities.JettyUtil; import com.google.common.collect.Lists; import org.eclipse.jetty.server.Server; -import org.eclipse.jetty.servlet.ServletContextHandler; -import org.eclipse.jetty.servlet.ServletHolder; +import org.eclipse.jetty.ee10.servlet.ServletContextHandler; +import org.eclipse.jetty.ee10.servlet.ServletHolder; import org.hl7.fhir.instance.model.api.IBaseResource; import org.hl7.fhir.r4.model.CodeableConcept; import org.hl7.fhir.r4.model.Coding; diff --git a/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/subscription/svc/ResourceModifiedSubmitterSvcTest.java b/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/subscription/svc/ResourceModifiedSubmitterSvcTest.java index 872ec955c81..f8194204aaa 100644 --- a/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/subscription/svc/ResourceModifiedSubmitterSvcTest.java +++ b/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/subscription/svc/ResourceModifiedSubmitterSvcTest.java @@ -1,6 +1,7 @@ package ca.uhn.fhir.jpa.subscription.svc; import ca.uhn.fhir.jpa.dao.tx.IHapiTransactionService; +import ca.uhn.fhir.jpa.model.entity.PersistedResourceModifiedMessageEntityPK; import ca.uhn.fhir.jpa.model.entity.ResourceModifiedEntity; import ca.uhn.fhir.jpa.model.entity.StorageSettings; import ca.uhn.fhir.jpa.subscription.channel.api.ChannelProducerSettings; @@ -9,7 +10,12 @@ import ca.uhn.fhir.jpa.subscription.channel.subscription.SubscriptionChannelFact import ca.uhn.fhir.jpa.subscription.model.ResourceModifiedMessage; import ca.uhn.fhir.jpa.subscription.submit.svc.ResourceModifiedSubmitterSvc; import ca.uhn.fhir.jpa.svc.MockHapiTransactionService; +import ca.uhn.fhir.rest.server.exceptions.ResourceNotFoundException; import ca.uhn.fhir.subscription.api.IResourceModifiedMessagePersistenceSvc; +import ch.qos.logback.classic.Level; +import ch.qos.logback.classic.Logger; +import ch.qos.logback.classic.spi.ILoggingEvent; +import ch.qos.logback.core.read.ListAppender; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.ExtendWith; @@ -19,15 +25,24 @@ import org.mockito.ArgumentCaptor; import org.mockito.Captor; import org.mockito.Mock; import org.mockito.junit.jupiter.MockitoExtension; +import org.slf4j.LoggerFactory; import org.springframework.messaging.MessageDeliveryException; import org.springframework.transaction.TransactionStatus; import org.springframework.transaction.support.SimpleTransactionStatus; +import java.util.List; +import java.util.Optional; + import static org.hamcrest.MatcherAssert.assertThat; import static org.hamcrest.Matchers.is; +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertNotNull; +import static org.junit.jupiter.api.Assertions.assertTrue; import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.anyString; +import static org.mockito.Mockito.atLeast; import static org.mockito.Mockito.lenient; +import static org.mockito.Mockito.never; import static org.mockito.Mockito.times; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; @@ -35,6 +50,8 @@ import static org.mockito.Mockito.when; @ExtendWith(MockitoExtension.class) public class ResourceModifiedSubmitterSvcTest { + private final ch.qos.logback.classic.Logger ourLogger = (Logger) LoggerFactory.getLogger(ResourceModifiedSubmitterSvc.class); + @Mock StorageSettings myStorageSettings; @Mock @@ -46,6 +63,9 @@ public class ResourceModifiedSubmitterSvcTest { @Mock IChannelProducer myChannelProducer; + @Mock + ListAppender myListAppender; + ResourceModifiedSubmitterSvc myResourceModifiedSubmitterSvc; TransactionStatus myCapturingTransactionStatus; @@ -81,11 +101,11 @@ public class ResourceModifiedSubmitterSvcTest { } @Test - public void testSubmitPersisedResourceModifiedMessage_withExistingPersistedResourceModifiedMessage_willSucceed(){ + public void testSubmitPersistedResourceModifiedMessage_withExistingPersistedResourceModifiedMessage_willSucceed(){ // given // a successful deletion implies that the message did exist. when(myResourceModifiedMessagePersistenceSvc.deleteByPK(any())).thenReturn(true); - when(myResourceModifiedMessagePersistenceSvc.inflatePersistedResourceModifiedMessage(any())).thenReturn(new ResourceModifiedMessage()); + when(myResourceModifiedMessagePersistenceSvc.createResourceModifiedMessageFromEntityWithoutInflation(any())).thenReturn(new ResourceModifiedMessage()); // when boolean wasProcessed = myResourceModifiedSubmitterSvc.submitPersisedResourceModifiedMessage(new ResourceModifiedEntity()); @@ -94,15 +114,99 @@ public class ResourceModifiedSubmitterSvcTest { assertThat(wasProcessed, is(Boolean.TRUE)); assertThat(myCapturingTransactionStatus.isRollbackOnly(), is(Boolean.FALSE)); verify(myChannelProducer, times(1)).send(any()); - } @Test - public void testSubmitPersisedResourceModifiedMessage_whenMessageWasAlreadyProcess_willSucceed(){ + public void testSubmitPersistedResource_logsDeleteAndInflationExceptions() { + // setup + String deleteExMsg = "Delete Exception"; + String inflationExMsg = "Inflation Exception"; + String patientId = "/Patient/123/_history/1"; + ResourceModifiedEntity resourceModified = new ResourceModifiedEntity(); + PersistedResourceModifiedMessageEntityPK rpm = new PersistedResourceModifiedMessageEntityPK(); + rpm.setResourcePid(patientId); + rpm.setResourceVersion("1"); + resourceModified.setResourceModifiedEntityPK(rpm); + + ourLogger.addAppender(myListAppender); + ourLogger.setLevel(Level.ERROR); + + // when + when(myResourceModifiedMessagePersistenceSvc.deleteByPK(any())) + .thenThrow(new RuntimeException(deleteExMsg)); + when(myResourceModifiedMessagePersistenceSvc.createResourceModifiedMessageFromEntityWithoutInflation(any())) + .thenThrow(new RuntimeException(inflationExMsg)); + + // test + boolean processed = myResourceModifiedSubmitterSvc.submitPersisedResourceModifiedMessage(resourceModified); + + // verify + assertTrue(processed); + + ArgumentCaptor logEvent = ArgumentCaptor.forClass(ILoggingEvent.class); + verify(myListAppender, atLeast(2)) + .doAppend(logEvent.capture()); + + List logs = logEvent.getAllValues(); + boolean hasDeleteException = false; + boolean hasInflationException = false; + for (ILoggingEvent log : logs) { + if (log.getThrowableProxy().getMessage().contains(deleteExMsg)) { + hasDeleteException = true; + } + if (log.getThrowableProxy().getMessage().contains(inflationExMsg)) { + hasInflationException = true; + } + } + assertTrue(hasDeleteException); + assertTrue(hasInflationException); + } + + @Test + public void testSubmitPersistedResource_withMissingResource_processes() { + // setup + String patientId = "Patient/123"; + String exceptionString = "A random exception"; + ResourceModifiedEntity resourceModified = new ResourceModifiedEntity(); + PersistedResourceModifiedMessageEntityPK rpm = new PersistedResourceModifiedMessageEntityPK(); + rpm.setResourcePid(patientId); + rpm.setResourceVersion("1"); + resourceModified.setResourceModifiedEntityPK(rpm); + ResourceModifiedMessage msg = new ResourceModifiedMessage(); + + ourLogger.addAppender(myListAppender); + ourLogger.setLevel(Level.ERROR); + + // when + when(myResourceModifiedMessagePersistenceSvc.deleteByPK(any())) + .thenReturn(true); + when(myResourceModifiedMessagePersistenceSvc.createResourceModifiedMessageFromEntityWithoutInflation(any())) + .thenReturn(msg); + when(myChannelProducer.send(any())) + .thenThrow(new RuntimeException(exceptionString)); + + // test + boolean processed = myResourceModifiedSubmitterSvc.submitPersisedResourceModifiedMessage(resourceModified); + + // then + assertTrue(processed); + + // verify + verify(myChannelProducer) + .send(any()); + ArgumentCaptor loggingCaptor = ArgumentCaptor.forClass(ILoggingEvent.class); + verify(myListAppender).doAppend(loggingCaptor.capture()); + ILoggingEvent event = loggingCaptor.getValue(); + assertNotNull(event); + assertTrue(event.getThrowableProxy().getMessage().contains(exceptionString)); + } + + @Test + public void testSubmitPersistedResourceModifiedMessage_whenMessageWasAlreadyProcess_willSucceed(){ // given // deletion fails, someone else was faster and processed the message when(myResourceModifiedMessagePersistenceSvc.deleteByPK(any())).thenReturn(false); - when(myResourceModifiedMessagePersistenceSvc.inflatePersistedResourceModifiedMessage(any())).thenReturn(new ResourceModifiedMessage()); + when(myResourceModifiedMessagePersistenceSvc.createResourceModifiedMessageFromEntityWithoutInflation(any())).thenReturn(new ResourceModifiedMessage()); // when boolean wasProcessed = myResourceModifiedSubmitterSvc.submitPersisedResourceModifiedMessage(new ResourceModifiedEntity()); @@ -116,10 +220,10 @@ public class ResourceModifiedSubmitterSvcTest { } @Test - public void testSubmitPersisedResourceModifiedMessage_whitErrorOnSending_willRollbackDeletion(){ + public void testSubmitPersistedResourceModifiedMessage_whitErrorOnSending_willRollbackDeletion(){ // given when(myResourceModifiedMessagePersistenceSvc.deleteByPK(any())).thenReturn(true); - when(myResourceModifiedMessagePersistenceSvc.inflatePersistedResourceModifiedMessage(any())).thenReturn(new ResourceModifiedMessage()); + when(myResourceModifiedMessagePersistenceSvc.createResourceModifiedMessageFromEntityWithoutInflation(any())).thenReturn(new ResourceModifiedMessage()); // simulate failure writing to the channel when(myChannelProducer.send(any())).thenThrow(new MessageDeliveryException("sendingError")); diff --git a/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/term/BaseTermR4Test.java b/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/term/BaseTermR4Test.java index 9b0f3063a8f..ca7a9ba63a7 100644 --- a/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/term/BaseTermR4Test.java +++ b/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/term/BaseTermR4Test.java @@ -17,7 +17,7 @@ import org.springframework.transaction.TransactionStatus; import org.springframework.transaction.support.TransactionCallbackWithoutResult; import org.springframework.transaction.support.TransactionTemplate; -import javax.annotation.Nonnull; +import jakarta.annotation.Nonnull; import java.io.IOException; public abstract class BaseTermR4Test extends BaseJpaR4Test { diff --git a/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/term/ITermReadSvcTest.java b/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/term/ITermReadSvcTest.java index a9610ed7f54..95f6f583f31 100644 --- a/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/term/ITermReadSvcTest.java +++ b/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/term/ITermReadSvcTest.java @@ -49,8 +49,8 @@ import org.mockito.junit.jupiter.MockitoExtension; import org.springframework.data.domain.Pageable; import org.springframework.test.util.ReflectionTestUtils; -import javax.persistence.EntityManager; -import javax.persistence.NonUniqueResultException; +import jakarta.persistence.EntityManager; +import jakarta.persistence.NonUniqueResultException; import javax.sql.DataSource; import java.util.Arrays; import java.util.Collections; @@ -224,7 +224,7 @@ class ITermReadSvcTest { @Test void getNoneReturnsOptionalEmpty() { - when(myEntityManager.createQuery(anyString()).getResultList()) + when(myEntityManager.createQuery(anyString()).setParameter(anyString(), any()).getResultList()) .thenReturn(Collections.emptyList()); Optional result = testedClass.readCodeSystemByForcedId("a-cs-id"); @@ -233,7 +233,7 @@ class ITermReadSvcTest { @Test void getMultipleThrows() { - when(myEntityManager.createQuery(anyString()).getResultList()) + when(myEntityManager.createQuery(anyString()).setParameter(anyString(), any()).getResultList()) .thenReturn(Lists.newArrayList(resource1, resource2)); NonUniqueResultException thrown = assertThrows( @@ -247,7 +247,7 @@ class ITermReadSvcTest { void getOneConvertToResource() { ReflectionTestUtils.setField(testedClass, "myDaoRegistry", myDaoRegistry); - when(myEntityManager.createQuery(anyString()).getResultList()) + when(myEntityManager.createQuery(anyString()).setParameter(anyString(), any()).getResultList()) .thenReturn(Lists.newArrayList(resource1)); when(myDaoRegistry.getResourceDao("CodeSystem")).thenReturn(myFhirResourceDao); when(myJpaStorageResourceParser.toResource(resource1, false)).thenReturn(myCodeSystemResource); diff --git a/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/term/TermConceptMappingSvcImplTest.java b/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/term/TermConceptMappingSvcImplTest.java index 6af60b7d6a0..a4a18667b1c 100644 --- a/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/term/TermConceptMappingSvcImplTest.java +++ b/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/term/TermConceptMappingSvcImplTest.java @@ -31,7 +31,7 @@ import org.springframework.transaction.TransactionStatus; import org.springframework.transaction.support.TransactionCallbackWithoutResult; import org.springframework.transaction.support.TransactionTemplate; -import javax.annotation.Nonnull; +import jakarta.annotation.Nonnull; import java.util.Collections; import java.util.List; diff --git a/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/term/TerminologySvcDeltaR4Test.java b/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/term/TerminologySvcDeltaR4Test.java index dd0fe20bf61..24b0673fd16 100644 --- a/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/term/TerminologySvcDeltaR4Test.java +++ b/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/term/TerminologySvcDeltaR4Test.java @@ -2,6 +2,7 @@ package ca.uhn.fhir.jpa.term; import ca.uhn.fhir.context.support.IValidationSupport; import ca.uhn.fhir.context.support.ValidationSupportContext; +import ca.uhn.fhir.context.support.LookupCodeRequest; import ca.uhn.fhir.i18n.Msg; import ca.uhn.fhir.jpa.api.config.JpaStorageSettings; import ca.uhn.fhir.jpa.entity.TermConcept; @@ -369,7 +370,7 @@ public class TerminologySvcDeltaR4Test extends BaseJpaR4Test { assertEquals("http://foo", outcome.getUrl()); assertEquals(CodeSystem.CodeSystemContentMode.NOTPRESENT, outcome.getContent()); - IValidationSupport.LookupCodeResult lookup = myTermSvc.lookupCode(new ValidationSupportContext(myValidationSupport), "http://foo", "CBC", null); + IValidationSupport.LookupCodeResult lookup = myTermSvc.lookupCode(new ValidationSupportContext(myValidationSupport), new LookupCodeRequest("http://foo", "CBC")); assertEquals("Complete Blood Count", lookup.getCodeDisplay()); } @@ -433,7 +434,8 @@ public class TerminologySvcDeltaR4Test extends BaseJpaR4Test { UploadStatistics outcome = myTermCodeSystemStorageSvc.applyDeltaCodeSystemsAdd("http://foo", delta); assertEquals(2, outcome.getUpdatedConceptCount()); - assertEquals("CODEA0", myTermSvc.lookupCode(new ValidationSupportContext(myValidationSupport), "http://foo", "codea", null).getCodeDisplay()); + assertEquals("CODEA0", myTermSvc.lookupCode(new ValidationSupportContext(myValidationSupport), + new LookupCodeRequest("http://foo", "codea")).getCodeDisplay()); // Add codes again with different display delta = new CustomTerminologySet(); @@ -441,12 +443,14 @@ public class TerminologySvcDeltaR4Test extends BaseJpaR4Test { delta.addRootConcept("codeb", "CODEB1"); outcome = myTermCodeSystemStorageSvc.applyDeltaCodeSystemsAdd("http://foo", delta); assertEquals(2, outcome.getUpdatedConceptCount()); - assertEquals("CODEA1", myTermSvc.lookupCode(new ValidationSupportContext(myValidationSupport), "http://foo", "codea", null).getCodeDisplay()); + assertEquals("CODEA1", myTermSvc.lookupCode(new ValidationSupportContext(myValidationSupport), + new LookupCodeRequest("http://foo", "codea")).getCodeDisplay()); // Add codes again with no changes outcome = myTermCodeSystemStorageSvc.applyDeltaCodeSystemsAdd("http://foo", delta); assertEquals(2, outcome.getUpdatedConceptCount()); - assertEquals("CODEA1", myTermSvc.lookupCode(new ValidationSupportContext(myValidationSupport), "http://foo", "codea", null).getCodeDisplay()); + assertEquals("CODEA1", myTermSvc.lookupCode(new ValidationSupportContext(myValidationSupport), + new LookupCodeRequest("http://foo", "codea")).getCodeDisplay()); } @Test @@ -481,7 +485,8 @@ public class TerminologySvcDeltaR4Test extends BaseJpaR4Test { .setValue(new Coding("http://snomed.info", "1234567", "Choked on large meal (finding)")); myCodeSystemDao.create(cs, mySrd); - IValidationSupport.LookupCodeResult result = myTermSvc.lookupCode(new ValidationSupportContext(myValidationSupport), "http://foo/cs", "lunch", null); + IValidationSupport.LookupCodeResult result = myTermSvc.lookupCode(new ValidationSupportContext(myValidationSupport), + new LookupCodeRequest("http://foo/cs", "lunch")); assertEquals(true, result.isFound()); assertEquals("lunch", result.getSearchedForCode()); assertEquals("http://foo/cs", result.getSearchedForSystem()); @@ -505,7 +510,7 @@ public class TerminologySvcDeltaR4Test extends BaseJpaR4Test { List properties = output.getParameter().stream().filter(t -> t.getName().equals("property")).collect(Collectors.toList()); assertEquals("code", properties.get(0).getPart().get(0).getName()); - assertEquals("flavour", ((CodeType) properties.get(0).getPart().get(0).getValue()).getValueAsString()); + assertEquals("flavour", ((StringType) properties.get(0).getPart().get(0).getValue()).getValueAsString()); assertEquals("value", properties.get(0).getPart().get(1).getName()); assertEquals("Hints of lime", ((StringType) properties.get(0).getPart().get(1).getValue()).getValueAsString()); diff --git a/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/term/TerminologySvcImplCurrentVersionR4Test.java b/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/term/TerminologySvcImplCurrentVersionR4Test.java index 387609117bf..e3d7d61c031 100644 --- a/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/term/TerminologySvcImplCurrentVersionR4Test.java +++ b/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/term/TerminologySvcImplCurrentVersionR4Test.java @@ -2,6 +2,7 @@ package ca.uhn.fhir.jpa.term; import ca.uhn.fhir.context.support.IValidationSupport; import ca.uhn.fhir.context.support.ValidationSupportContext; +import ca.uhn.fhir.context.support.LookupCodeRequest; import ca.uhn.fhir.jpa.api.dao.IFhirResourceDao; import ca.uhn.fhir.jpa.config.JpaConfig; import ca.uhn.fhir.jpa.entity.TermCodeSystem; @@ -38,8 +39,8 @@ import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Qualifier; import org.springframework.core.io.ClassPathResource; -import javax.persistence.EntityManager; -import javax.servlet.http.HttpServletResponse; +import jakarta.persistence.EntityManager; +import jakarta.servlet.http.HttpServletResponse; import java.io.IOException; import java.io.InputStream; import java.util.ArrayList; @@ -214,13 +215,13 @@ public class TerminologySvcImplCurrentVersionR4Test extends BaseJpaR4Test { private void validateValueLookup(String theCurrentVersion, Collection allVersions) { IValidationSupport.LookupCodeResult resultNoVer = myValidationSupport.lookupCode( - new ValidationSupportContext(myValidationSupport), BASE_LOINC_URL, VS_NO_VERSIONED_ON_UPLOAD_FIRST_CODE, null); + new ValidationSupportContext(myValidationSupport), new LookupCodeRequest(BASE_LOINC_URL, VS_NO_VERSIONED_ON_UPLOAD_FIRST_CODE)); assertNotNull(resultNoVer); String expectedNoVer = prefixWithVersion(theCurrentVersion, VS_NO_VERSIONED_ON_UPLOAD_FIRST_DISPLAY); assertEquals(expectedNoVer, resultNoVer.getCodeDisplay()); IValidationSupport.LookupCodeResult resultWithVer = myValidationSupport.lookupCode( - new ValidationSupportContext(myValidationSupport), BASE_LOINC_URL, VS_VERSIONED_ON_UPLOAD_FIRST_CODE, null); + new ValidationSupportContext(myValidationSupport), new LookupCodeRequest(BASE_LOINC_URL, VS_VERSIONED_ON_UPLOAD_FIRST_CODE)); assertNotNull(resultWithVer); String expectedWithVer = prefixWithVersion(theCurrentVersion, VS_VERSIONED_ON_UPLOAD_FIRST_DISPLAY); assertEquals(expectedWithVer, resultWithVer.getCodeDisplay()); @@ -231,15 +232,15 @@ public class TerminologySvcImplCurrentVersionR4Test extends BaseJpaR4Test { private void lookupForVersion(String theVersion) { IValidationSupport.LookupCodeResult resultNoVer = myValidationSupport.lookupCode( - new ValidationSupportContext(myValidationSupport), BASE_LOINC_URL + "|" + theVersion, - VS_NO_VERSIONED_ON_UPLOAD_FIRST_CODE, null); + new ValidationSupportContext(myValidationSupport), + new LookupCodeRequest(BASE_LOINC_URL + "|" + theVersion, VS_NO_VERSIONED_ON_UPLOAD_FIRST_CODE)); assertNotNull(resultNoVer); String expectedNoVer = prefixWithVersion(theVersion, VS_NO_VERSIONED_ON_UPLOAD_FIRST_DISPLAY); assertEquals(expectedNoVer, resultNoVer.getCodeDisplay()); IValidationSupport.LookupCodeResult resultWithVer = myValidationSupport.lookupCode( - new ValidationSupportContext(myValidationSupport), BASE_LOINC_URL + "|" + theVersion, - VS_VERSIONED_ON_UPLOAD_FIRST_CODE, null); + new ValidationSupportContext(myValidationSupport), + new LookupCodeRequest(BASE_LOINC_URL + "|" + theVersion, VS_VERSIONED_ON_UPLOAD_FIRST_CODE)); assertNotNull(resultWithVer); String expectedWithVer = prefixWithVersion(theVersion, VS_VERSIONED_ON_UPLOAD_FIRST_DISPLAY); assertEquals(expectedWithVer, resultWithVer.getCodeDisplay()); diff --git a/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/term/TerminologySvcImplR4Test.java b/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/term/TerminologySvcImplR4Test.java index 2edf0f57ee5..b74959b785a 100644 --- a/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/term/TerminologySvcImplR4Test.java +++ b/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/term/TerminologySvcImplR4Test.java @@ -27,7 +27,7 @@ import org.springframework.transaction.TransactionStatus; import org.springframework.transaction.support.TransactionCallbackWithoutResult; import org.springframework.transaction.support.TransactionTemplate; -import javax.annotation.Nonnull; +import jakarta.annotation.Nonnull; import java.util.ArrayList; import java.util.HashSet; import java.util.List; diff --git a/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/term/ValueSetExpansionR4Test.java b/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/term/ValueSetExpansionR4Test.java index bc22114e25f..365af4839e8 100644 --- a/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/term/ValueSetExpansionR4Test.java +++ b/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/term/ValueSetExpansionR4Test.java @@ -36,7 +36,6 @@ import org.hl7.fhir.r4.model.codesystems.HttpVerb; import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; -import org.mockito.Mock; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.data.domain.Pageable; @@ -44,7 +43,7 @@ import org.springframework.transaction.TransactionStatus; import org.springframework.transaction.support.TransactionCallbackWithoutResult; import org.springframework.transaction.support.TransactionTemplate; -import javax.annotation.Nonnull; +import jakarta.annotation.Nonnull; import java.io.IOException; import java.util.List; import java.util.Optional; @@ -73,9 +72,6 @@ public class ValueSetExpansionR4Test extends BaseTermR4Test { private final ValueSetTestUtil myValueSetTestUtil = new ValueSetTestUtil(FhirVersionEnum.R4); - @Mock - private IValueSetConceptAccumulator myValueSetCodeAccumulator; - @AfterEach public void afterEach() { SearchBuilder.setMaxPageSize50ForTest(false); @@ -280,8 +276,9 @@ public class ValueSetExpansionR4Test extends BaseTermR4Test { //Ensure that the subsequent expansion with offset returns the same slice we are anticipating. assertThat(myValueSetTestUtil.toCodes(expandedValueSet).toString(), myValueSetTestUtil.toCodes(expandedValueSet), is(equalTo(expandedConceptCodes.subList(offset, offset + count)))); - Assertions.assertEquals(4, expandedValueSet.getExpansion().getContains().size(), myValueSetTestUtil.toCodes(expandedValueSet).toString()); - assertEquals(11, expandedValueSet.getExpansion().getTotal()); + Assertions.assertEquals(count, expandedValueSet.getExpansion().getContains().size(), myValueSetTestUtil.toCodes(expandedValueSet).toString()); + assertEquals(offset + count, expandedValueSet.getExpansion().getTotal()); + assertEquals(count, expandedValueSet.getExpansion().getContains().size()); // Make sure we used the pre-expanded version List selectQueries = myCaptureQueriesListener.getSelectQueries(); diff --git a/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/term/ValueSetExpansionWithHierarchyR4Test.java b/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/term/ValueSetExpansionWithHierarchyR4Test.java new file mode 100644 index 00000000000..607491ec002 --- /dev/null +++ b/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/term/ValueSetExpansionWithHierarchyR4Test.java @@ -0,0 +1,109 @@ +package ca.uhn.fhir.jpa.term; + +import ca.uhn.fhir.jpa.entity.TermValueSet; +import ca.uhn.fhir.jpa.entity.TermValueSetPreExpansionStatusEnum; +import org.hl7.fhir.r4.model.CodeSystem; +import org.hl7.fhir.r4.model.ValueSet; +import org.junit.jupiter.api.AfterAll; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.Arguments; +import org.junit.jupiter.params.provider.MethodSource; + +import java.util.Optional; +import java.util.stream.Stream; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertTrue; +import static org.junit.jupiter.params.provider.Arguments.arguments; + +public class ValueSetExpansionWithHierarchyR4Test extends BaseTermR4Test { + private static final String ourCodeSystemId = "CodeSystem-WithHierarchy", ourCodeSystemUrl = "http://example/" + ourCodeSystemId; + private static final String ourCodeA = "CodeA", ourCodeB = "CodeB", ourCodeC = "CodeC", ourCodeD = "CodeD"; + private static final String ourValueSetAUrl = "http://example/ValueSetA", ourValueSetBUrl = "http://example/ValueSetB", ourValueSetUrl = "http://example/ValueSet"; + private static final int ourChildConceptCount = 10; + + @BeforeAll + public static void setup() { + TermReadSvcImpl.setForceDisableHibernateSearchForUnitTest(true); + } + @AfterAll + public static void tearDown() { + TermReadSvcImpl.setForceDisableHibernateSearchForUnitTest(false); + } + @BeforeEach + public void setupHierarchicalCodeSystemWithValueSets() { + myStorageSettings.setPreExpandValueSets(true); + + CodeSystem codeSystem = new CodeSystem(); + codeSystem.setId(ourCodeSystemId); + codeSystem.setUrl(ourCodeSystemUrl); + CodeSystem.ConceptDefinitionComponent concept1 = codeSystem.addConcept().setCode(ourCodeA); + CodeSystem.ConceptDefinitionComponent concept2 = codeSystem.addConcept().setCode(ourCodeB); + for (int i = 0; i < ourChildConceptCount; i++) { + concept1.addConcept().setCode(concept1.getCode() + i); + concept2.addConcept().setCode(concept2.getCode() + i); + } + codeSystem.addConcept().setCode(ourCodeC); + codeSystem.addConcept().setCode(ourCodeD); + myCodeSystemDao.create(codeSystem, mySrd); + + ValueSet valueSetA = new ValueSet(); + valueSetA.setUrl(ourValueSetAUrl); + valueSetA.getCompose().addInclude().setSystem(ourCodeSystemUrl) + .addFilter().setProperty("concept").setOp(ValueSet.FilterOperator.ISA).setValue(ourCodeA); + myValueSetDao.create(valueSetA, mySrd); + + ValueSet valueSetB = new ValueSet(); + valueSetB.setUrl(ourValueSetBUrl); + valueSetA.getCompose().addInclude().setSystem(ourCodeSystemUrl) + .addFilter().setProperty("concept").setOp(ValueSet.FilterOperator.ISA).setValue(ourCodeB); + myValueSetDao.create(valueSetB, mySrd); + } + + static Stream parametersValueSets() { + ValueSet valueSet0 = new ValueSet(); + valueSet0.setUrl(ourValueSetUrl + "-WithIncludeChildValueSet"); + valueSet0.getCompose().addInclude().addValueSet(ourValueSetAUrl); + + ValueSet valueSet1 = new ValueSet(); + valueSet1.setUrl(ourValueSetUrl + "-WithIncludeChildValueSetAndCodeSystem"); + valueSet1.getCompose().addInclude().addValueSet(ourValueSetAUrl); + valueSet1.getCompose().addInclude().addValueSet(ourValueSetBUrl); + valueSet1.getCompose().addInclude().setSystem(ourCodeSystemUrl); + + ValueSet valueSet2 = new ValueSet(); + valueSet2.setUrl(ourValueSetUrl + "-WithIncludeChildValueSetAndCodeSystemConceptSet-NoIntersectionCodes"); + valueSet2.getCompose().addInclude().addValueSet(ourValueSetAUrl); + ValueSet.ConceptSetComponent conceptSetWithCodeSystem = valueSet2.getCompose().addInclude().setSystem(ourCodeSystemUrl); + conceptSetWithCodeSystem.addConcept().setCode(ourCodeC); + conceptSetWithCodeSystem.addConcept().setCode(ourCodeD); + + ValueSet valueSet3 = new ValueSet(); + valueSet3.setUrl(ourValueSetUrl + "-WithIncludeChildValueSetAndCodeSystemConceptSet-WithIntersectionCodes"); + valueSet3.getCompose().addInclude().addValueSet(ourValueSetAUrl); + conceptSetWithCodeSystem = valueSet3.getCompose().addInclude().setSystem(ourCodeSystemUrl); + conceptSetWithCodeSystem.addConcept().setCode(ourCodeA + "1"); + conceptSetWithCodeSystem.addConcept().setCode(ourCodeA + "2"); + + return Stream.of( + arguments(valueSet0, ourChildConceptCount), + arguments(valueSet1, 4 + ourChildConceptCount * 2), + arguments(valueSet2, 2 + ourChildConceptCount), + arguments(valueSet3, ourChildConceptCount) + ); + } + + @ParameterizedTest + @MethodSource(value = "parametersValueSets") + public void testExpandValueSet_whenUsingHierarchicalCodeSystem_willExpandSuccessfully(ValueSet theValueSet, int theExpectedConceptExpansionCount) { + myValueSetDao.create(theValueSet, mySrd); + myTermSvc.preExpandDeferredValueSetsToTerminologyTables(); + Optional optionalTermValueSet = runInTransaction(() -> myTermValueSetDao.findTermValueSetByUrlAndNullVersion(theValueSet.getUrl())); + assertTrue(optionalTermValueSet.isPresent()); + TermValueSet expandedTermValueSet = optionalTermValueSet.get(); + assertEquals(TermValueSetPreExpansionStatusEnum.EXPANDED, expandedTermValueSet.getExpansionStatus()); + assertEquals(theExpectedConceptExpansionCount, expandedTermValueSet.getTotalConcepts()); + } +} diff --git a/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/term/hsearch/ReindexTerminologyHSearchR4Test.java b/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/term/hsearch/ReindexTerminologyHSearchR4Test.java index 92a19636014..623c94d6f34 100644 --- a/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/term/hsearch/ReindexTerminologyHSearchR4Test.java +++ b/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/term/hsearch/ReindexTerminologyHSearchR4Test.java @@ -34,7 +34,7 @@ import org.springframework.test.context.ContextConfiguration; import org.springframework.test.context.junit.jupiter.SpringExtension; import org.springframework.util.ResourceUtils; -import javax.persistence.EntityManager; +import jakarta.persistence.EntityManager; import java.io.File; import java.io.FileNotFoundException; import java.util.ArrayList; diff --git a/hapi-fhir-jpaserver-test-r4b/pom.xml b/hapi-fhir-jpaserver-test-r4b/pom.xml index fd24ac730a3..fc9a0d3a742 100644 --- a/hapi-fhir-jpaserver-test-r4b/pom.xml +++ b/hapi-fhir-jpaserver-test-r4b/pom.xml @@ -6,7 +6,7 @@ ca.uhn.hapi.fhir hapi-deployable-pom - 6.9.7-SNAPSHOT + 6.11.7-SNAPSHOT ../hapi-deployable-pom/pom.xml diff --git a/hapi-fhir-jpaserver-test-r4b/src/test/java/ca/uhn/fhir/jpa/dao/r4b/BaseJpaR4BTest.java b/hapi-fhir-jpaserver-test-r4b/src/test/java/ca/uhn/fhir/jpa/dao/r4b/BaseJpaR4BTest.java index d3268b3f732..0a275e83112 100644 --- a/hapi-fhir-jpaserver-test-r4b/src/test/java/ca/uhn/fhir/jpa/dao/r4b/BaseJpaR4BTest.java +++ b/hapi-fhir-jpaserver-test-r4b/src/test/java/ca/uhn/fhir/jpa/dao/r4b/BaseJpaR4BTest.java @@ -124,7 +124,7 @@ import org.springframework.test.context.junit.jupiter.SpringExtension; import org.springframework.transaction.PlatformTransactionManager; import org.springframework.transaction.annotation.Transactional; -import javax.persistence.EntityManager; +import jakarta.persistence.EntityManager; import java.io.IOException; import java.util.List; import java.util.Optional; diff --git a/hapi-fhir-jpaserver-test-r4b/src/test/java/ca/uhn/fhir/jpa/subscription/BaseSubscriptionsR4BTest.java b/hapi-fhir-jpaserver-test-r4b/src/test/java/ca/uhn/fhir/jpa/subscription/BaseSubscriptionsR4BTest.java index bd551395c22..86a71394cdd 100644 --- a/hapi-fhir-jpaserver-test-r4b/src/test/java/ca/uhn/fhir/jpa/subscription/BaseSubscriptionsR4BTest.java +++ b/hapi-fhir-jpaserver-test-r4b/src/test/java/ca/uhn/fhir/jpa/subscription/BaseSubscriptionsR4BTest.java @@ -34,8 +34,8 @@ import org.junit.jupiter.api.Order; import org.junit.jupiter.api.extension.RegisterExtension; import org.springframework.beans.factory.annotation.Autowired; -import javax.annotation.Nonnull; -import javax.annotation.PostConstruct; +import jakarta.annotation.Nonnull; +import jakarta.annotation.PostConstruct; import java.util.ArrayList; import java.util.Collections; import java.util.List; diff --git a/hapi-fhir-jpaserver-test-r5/pom.xml b/hapi-fhir-jpaserver-test-r5/pom.xml index 5ce759bc927..eb2a7a64796 100644 --- a/hapi-fhir-jpaserver-test-r5/pom.xml +++ b/hapi-fhir-jpaserver-test-r5/pom.xml @@ -6,7 +6,7 @@ ca.uhn.hapi.fhir hapi-deployable-pom - 6.9.7-SNAPSHOT + 6.11.7-SNAPSHOT ../hapi-deployable-pom/pom.xml @@ -29,11 +29,25 @@ test - net.sourceforge.htmlunit + org.htmlunit htmlunit test + + + junit + junit + 4.13.2 + provided + + + org.hamcrest + hamcrest-core + + + + diff --git a/hapi-fhir-jpaserver-test-r5/src/test/java/ca/uhn/fhir/jpa/dao/r5/BaseJpaR5Test.java b/hapi-fhir-jpaserver-test-r5/src/test/java/ca/uhn/fhir/jpa/dao/r5/BaseJpaR5Test.java index aa65349989c..1ef5161e6ab 100644 --- a/hapi-fhir-jpaserver-test-r5/src/test/java/ca/uhn/fhir/jpa/dao/r5/BaseJpaR5Test.java +++ b/hapi-fhir-jpaserver-test-r5/src/test/java/ca/uhn/fhir/jpa/dao/r5/BaseJpaR5Test.java @@ -133,7 +133,7 @@ import org.springframework.test.util.AopTestUtils; import org.springframework.transaction.PlatformTransactionManager; import org.springframework.transaction.annotation.Transactional; -import javax.persistence.EntityManager; +import jakarta.persistence.EntityManager; import java.util.List; import java.util.Optional; import java.util.stream.Collectors; diff --git a/hapi-fhir-jpaserver-test-r5/src/test/java/ca/uhn/fhir/jpa/dao/r5/CrossPartitionReferencesTest.java b/hapi-fhir-jpaserver-test-r5/src/test/java/ca/uhn/fhir/jpa/dao/r5/CrossPartitionReferencesTest.java index e5aea038ccf..c1d6bb2e381 100644 --- a/hapi-fhir-jpaserver-test-r5/src/test/java/ca/uhn/fhir/jpa/dao/r5/CrossPartitionReferencesTest.java +++ b/hapi-fhir-jpaserver-test-r5/src/test/java/ca/uhn/fhir/jpa/dao/r5/CrossPartitionReferencesTest.java @@ -34,7 +34,7 @@ import org.mockito.Mock; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.transaction.annotation.Propagation; -import javax.annotation.Nonnull; +import jakarta.annotation.Nonnull; import static org.hamcrest.MatcherAssert.assertThat; import static org.hamcrest.Matchers.contains; diff --git a/hapi-fhir-jpaserver-test-r5/src/test/java/ca/uhn/fhir/jpa/dao/r5/FhirResourceDaoR5HistoryDisabledTest.java b/hapi-fhir-jpaserver-test-r5/src/test/java/ca/uhn/fhir/jpa/dao/r5/FhirResourceDaoR5HistoryDisabledTest.java index d3cb0806d8d..d663e8bbb75 100644 --- a/hapi-fhir-jpaserver-test-r5/src/test/java/ca/uhn/fhir/jpa/dao/r5/FhirResourceDaoR5HistoryDisabledTest.java +++ b/hapi-fhir-jpaserver-test-r5/src/test/java/ca/uhn/fhir/jpa/dao/r5/FhirResourceDaoR5HistoryDisabledTest.java @@ -18,7 +18,7 @@ import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; -import javax.annotation.Nonnull; +import jakarta.annotation.Nonnull; import java.util.List; import static org.hamcrest.MatcherAssert.assertThat; diff --git a/hapi-fhir-jpaserver-test-r5/src/test/java/ca/uhn/fhir/jpa/dao/r5/FhirSystemDaoTransactionR5Test.java b/hapi-fhir-jpaserver-test-r5/src/test/java/ca/uhn/fhir/jpa/dao/r5/FhirSystemDaoTransactionR5Test.java index fcd1bf9dc0e..75bc00c23c9 100644 --- a/hapi-fhir-jpaserver-test-r5/src/test/java/ca/uhn/fhir/jpa/dao/r5/FhirSystemDaoTransactionR5Test.java +++ b/hapi-fhir-jpaserver-test-r5/src/test/java/ca/uhn/fhir/jpa/dao/r5/FhirSystemDaoTransactionR5Test.java @@ -19,7 +19,7 @@ import org.junit.jupiter.params.ParameterizedTest; import org.junit.jupiter.params.provider.CsvSource; import org.junit.jupiter.params.provider.ValueSource; -import javax.annotation.Nonnull; +import jakarta.annotation.Nonnull; import java.io.IOException; import java.util.UUID; @@ -92,7 +92,7 @@ public class FhirSystemDaoTransactionR5Test extends BaseJpaR5Test { // One select to resolve the 3 match URLs assertEquals(1, myCaptureQueriesListener.countSelectQueriesForCurrentThread()); String firstSelectQuery = myCaptureQueriesListener.getSelectQueries().get(0).getSql(false, false); - assertEquals(1, countMatches(firstSelectQuery, "HASH_SYS_AND_VALUE in (? , ? , ? , ?)"), firstSelectQuery); + assertEquals(1, countMatches(firstSelectQuery, "rispt1_0.HASH_SYS_AND_VALUE in (?,?,?,?)"), firstSelectQuery); assertEquals(23, myCaptureQueriesListener.countInsertQueriesForCurrentThread()); assertEquals(3, myCaptureQueriesListener.countUpdateQueriesForCurrentThread()); assertEquals(0, myCaptureQueriesListener.countDeleteQueriesForCurrentThread()); diff --git a/hapi-fhir-jpaserver-test-r5/src/test/java/ca/uhn/fhir/jpa/dao/r5/UpliftedRefchainsAndChainedSortingR5Test.java b/hapi-fhir-jpaserver-test-r5/src/test/java/ca/uhn/fhir/jpa/dao/r5/UpliftedRefchainsAndChainedSortingR5Test.java index b2ce084ef63..453b541eebb 100644 --- a/hapi-fhir-jpaserver-test-r5/src/test/java/ca/uhn/fhir/jpa/dao/r5/UpliftedRefchainsAndChainedSortingR5Test.java +++ b/hapi-fhir-jpaserver-test-r5/src/test/java/ca/uhn/fhir/jpa/dao/r5/UpliftedRefchainsAndChainedSortingR5Test.java @@ -31,7 +31,7 @@ import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.springframework.test.context.ContextConfiguration; -import javax.annotation.Nonnull; +import jakarta.annotation.Nonnull; import java.util.List; import static org.apache.commons.lang3.StringUtils.countMatches; diff --git a/hapi-fhir-jpaserver-test-r5/src/test/java/ca/uhn/fhir/jpa/dao/r5/database/BaseDatabaseVerificationIT.java b/hapi-fhir-jpaserver-test-r5/src/test/java/ca/uhn/fhir/jpa/dao/r5/database/BaseDatabaseVerificationIT.java new file mode 100644 index 00000000000..bfc75f33643 --- /dev/null +++ b/hapi-fhir-jpaserver-test-r5/src/test/java/ca/uhn/fhir/jpa/dao/r5/database/BaseDatabaseVerificationIT.java @@ -0,0 +1,148 @@ +package ca.uhn.fhir.jpa.dao.r5.database; + +import ca.uhn.fhir.jpa.api.dao.IFhirResourceDao; +import ca.uhn.fhir.jpa.embedded.JpaEmbeddedDatabase; +import ca.uhn.fhir.jpa.migrate.HapiMigrationStorageSvc; +import ca.uhn.fhir.jpa.migrate.MigrationTaskList; +import ca.uhn.fhir.jpa.migrate.SchemaMigrator; +import ca.uhn.fhir.jpa.migrate.dao.HapiMigrationDao; +import ca.uhn.fhir.jpa.migrate.tasks.HapiFhirJpaMigrationTasks; +import ca.uhn.fhir.jpa.test.config.TestR5Config; +import ca.uhn.fhir.rest.api.server.SystemRequestDetails; +import ca.uhn.fhir.rest.server.exceptions.ResourceGoneException; +import ca.uhn.fhir.util.VersionEnum; +import jakarta.persistence.EntityManagerFactory; +import org.apache.commons.lang3.StringUtils; +import org.hl7.fhir.instance.model.api.IIdType; +import org.hl7.fhir.r5.model.Patient; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.ValueSource; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.data.envers.repository.support.EnversRevisionRepositoryFactoryBean; +import org.springframework.data.jpa.repository.config.EnableJpaRepositories; +import org.springframework.test.context.ContextConfiguration; +import org.springframework.test.context.junit.jupiter.SpringExtension; + +import javax.sql.DataSource; +import java.util.Properties; +import java.util.Set; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertThrows; + +@ExtendWith(SpringExtension.class) +@EnableJpaRepositories(repositoryFactoryBeanClass = EnversRevisionRepositoryFactoryBean.class) +@ContextConfiguration(classes = {BaseDatabaseVerificationIT.TestConfig.class}) +public abstract class BaseDatabaseVerificationIT { + private static final Logger ourLog = LoggerFactory.getLogger(BaseDatabaseVerificationIT.class); + private static final String MIGRATION_TABLENAME = "MIGRATIONS"; + + @Autowired + EntityManagerFactory myEntityManagerFactory; + + @Autowired + JpaEmbeddedDatabase myJpaEmbeddedDatabase; + + @Autowired + IFhirResourceDao myPatientDao; + + + @ParameterizedTest + @ValueSource(ints = {10, 100000}) + public void testCreateRead(int theSize) { + String name = StringUtils.leftPad("", theSize, "a"); + + Patient patient = new Patient(); + patient.setActive(true); + patient.addName().setFamily(name); + IIdType id = myPatientDao.create(patient, new SystemRequestDetails()).getId(); + + Patient actual = myPatientDao.read(id, new SystemRequestDetails()); + assertEquals(name, actual.getName().get(0).getFamily()); + } + + + @Test + public void testDelete() { + Patient patient = new Patient(); + patient.setActive(true); + IIdType id = myPatientDao.create(patient, new SystemRequestDetails()).getId().toUnqualifiedVersionless(); + + myPatientDao.delete(id, new SystemRequestDetails()); + + assertThrows(ResourceGoneException.class, () -> myPatientDao.read(id, new SystemRequestDetails())); + } + + + @Configuration + public static class TestConfig extends TestR5Config { + + @Autowired + private JpaDatabaseContextConfigParamObject myJpaDatabaseContextConfigParamObject; + + @Override + @Bean + public DataSource dataSource() { + DataSource dataSource = myJpaDatabaseContextConfigParamObject.getJpaEmbeddedDatabase().getDataSource(); + + HapiMigrationDao hapiMigrationDao = new HapiMigrationDao(dataSource, myJpaDatabaseContextConfigParamObject.getJpaEmbeddedDatabase().getDriverType(), MIGRATION_TABLENAME); + HapiMigrationStorageSvc hapiMigrationStorageSvc = new HapiMigrationStorageSvc(hapiMigrationDao); + + MigrationTaskList tasks = new HapiFhirJpaMigrationTasks(Set.of()).getAllTasks(VersionEnum.values()); + + SchemaMigrator schemaMigrator = new SchemaMigrator( + "HAPI FHIR", MIGRATION_TABLENAME, dataSource, new Properties(), tasks, hapiMigrationStorageSvc); + schemaMigrator.setDriverType(myJpaDatabaseContextConfigParamObject.getJpaEmbeddedDatabase().getDriverType()); + + ourLog.info("About to run migration..."); + schemaMigrator.createMigrationTableIfRequired(); + schemaMigrator.migrate(); + ourLog.info("Migration complete"); + + + return dataSource; + } + + @Bean + public JpaEmbeddedDatabase jpaEmbeddedDatabase(JpaDatabaseContextConfigParamObject theJpaDatabaseContextConfigParamObject) { + return theJpaDatabaseContextConfigParamObject.getJpaEmbeddedDatabase(); + } + + @Override + protected Properties jpaProperties() { + Properties retVal = super.jpaProperties(); + retVal.put("hibernate.hbm2ddl.auto", "none"); + retVal.put("hibernate.dialect", myJpaDatabaseContextConfigParamObject.getDialect()); + return retVal; + } + + } + + public static class JpaDatabaseContextConfigParamObject { + private JpaEmbeddedDatabase myJpaEmbeddedDatabase; + private String myDialect; + + public JpaDatabaseContextConfigParamObject(JpaEmbeddedDatabase theJpaEmbeddedDatabase, String theDialect) { + myJpaEmbeddedDatabase = theJpaEmbeddedDatabase; + myDialect = theDialect; + } + + public JpaEmbeddedDatabase getJpaEmbeddedDatabase() { + return myJpaEmbeddedDatabase; + } + + public String getDialect() { + return myDialect; + } + } + + +} + + diff --git a/hapi-fhir-jpaserver-test-r5/src/test/java/ca/uhn/fhir/jpa/dao/r5/database/DatabaseVerificationWithMsSqlIT.java b/hapi-fhir-jpaserver-test-r5/src/test/java/ca/uhn/fhir/jpa/dao/r5/database/DatabaseVerificationWithMsSqlIT.java new file mode 100644 index 00000000000..a6cf07f394d --- /dev/null +++ b/hapi-fhir-jpaserver-test-r5/src/test/java/ca/uhn/fhir/jpa/dao/r5/database/DatabaseVerificationWithMsSqlIT.java @@ -0,0 +1,27 @@ +package ca.uhn.fhir.jpa.dao.r5.database; + +import ca.uhn.fhir.jpa.embedded.MsSqlEmbeddedDatabase; +import ca.uhn.fhir.jpa.model.dialect.HapiFhirPostgresDialect; +import ca.uhn.fhir.jpa.model.dialect.HapiFhirSQLServerDialect; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.test.context.ContextConfiguration; + +@ContextConfiguration(classes = { + DatabaseVerificationWithMsSqlIT.TestConfig.class +}) +public class DatabaseVerificationWithMsSqlIT extends BaseDatabaseVerificationIT { + + @Configuration + public static class TestConfig { + @Bean + public JpaDatabaseContextConfigParamObject jpaDatabaseParamObject() { + return new JpaDatabaseContextConfigParamObject( + new MsSqlEmbeddedDatabase(), + HapiFhirSQLServerDialect.class.getName() + ); + } + } + + +} diff --git a/hapi-fhir-jpaserver-test-r5/src/test/java/ca/uhn/fhir/jpa/dao/r5/database/DatabaseVerificationWithOracleIT.java b/hapi-fhir-jpaserver-test-r5/src/test/java/ca/uhn/fhir/jpa/dao/r5/database/DatabaseVerificationWithOracleIT.java new file mode 100644 index 00000000000..d2c72605d6c --- /dev/null +++ b/hapi-fhir-jpaserver-test-r5/src/test/java/ca/uhn/fhir/jpa/dao/r5/database/DatabaseVerificationWithOracleIT.java @@ -0,0 +1,26 @@ +package ca.uhn.fhir.jpa.dao.r5.database; + +import ca.uhn.fhir.jpa.embedded.OracleEmbeddedDatabase; +import ca.uhn.fhir.jpa.model.dialect.HapiFhirOracleDialect; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.test.context.ContextConfiguration; + +@ContextConfiguration(classes = { + DatabaseVerificationWithOracleIT.TestConfig.class +}) +public class DatabaseVerificationWithOracleIT extends BaseDatabaseVerificationIT { + + @Configuration + public static class TestConfig { + @Bean + public JpaDatabaseContextConfigParamObject jpaDatabaseParamObject(){ + return new JpaDatabaseContextConfigParamObject( + new OracleEmbeddedDatabase(), + HapiFhirOracleDialect.class.getName() + ); + } + } + + +} diff --git a/hapi-fhir-jpaserver-test-r5/src/test/java/ca/uhn/fhir/jpa/dao/r5/database/DatabaseVerificationWithPostgresIT.java b/hapi-fhir-jpaserver-test-r5/src/test/java/ca/uhn/fhir/jpa/dao/r5/database/DatabaseVerificationWithPostgresIT.java new file mode 100644 index 00000000000..7bf2a5712b9 --- /dev/null +++ b/hapi-fhir-jpaserver-test-r5/src/test/java/ca/uhn/fhir/jpa/dao/r5/database/DatabaseVerificationWithPostgresIT.java @@ -0,0 +1,26 @@ +package ca.uhn.fhir.jpa.dao.r5.database; + +import ca.uhn.fhir.jpa.embedded.PostgresEmbeddedDatabase; +import ca.uhn.fhir.jpa.model.dialect.HapiFhirPostgresDialect; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.test.context.ContextConfiguration; + +@ContextConfiguration(classes = { + DatabaseVerificationWithPostgresIT.TestConfig.class +}) +public class DatabaseVerificationWithPostgresIT extends BaseDatabaseVerificationIT { + + @Configuration + public static class TestConfig { + @Bean + public JpaDatabaseContextConfigParamObject jpaDatabaseParamObject() { + return new JpaDatabaseContextConfigParamObject( + new PostgresEmbeddedDatabase(), + HapiFhirPostgresDialect.class.getName() + ); + } + } + + +} diff --git a/hapi-fhir-jpaserver-test-r5/src/test/java/ca/uhn/fhir/jpa/provider/r5/ResourceProviderR5CodeSystemPropertiesTest.java b/hapi-fhir-jpaserver-test-r5/src/test/java/ca/uhn/fhir/jpa/provider/r5/ResourceProviderR5CodeSystemPropertiesTest.java new file mode 100644 index 00000000000..5a9f26f8c1c --- /dev/null +++ b/hapi-fhir-jpaserver-test-r5/src/test/java/ca/uhn/fhir/jpa/provider/r5/ResourceProviderR5CodeSystemPropertiesTest.java @@ -0,0 +1,96 @@ +package ca.uhn.fhir.jpa.provider.r5; + +import ca.uhn.fhir.jpa.model.util.JpaConstants; +import ca.uhn.fhir.rest.gclient.IOperationUntypedWithInputAndPartialOutput; +import ca.uhn.fhir.jpa.provider.CodeSystemLookupWithPropertiesUtil; +import org.hl7.fhir.r5.model.CodeSystem; +import org.hl7.fhir.r5.model.CodeSystem.ConceptPropertyComponent; +import org.hl7.fhir.r5.model.CodeType; +import org.hl7.fhir.r5.model.Coding; +import org.hl7.fhir.r5.model.Parameters; +import org.hl7.fhir.r5.model.Parameters.ParametersParameterComponent; +import org.hl7.fhir.r5.model.StringType; +import org.hl7.fhir.r5.model.UriType; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.Arguments; +import org.junit.jupiter.params.provider.MethodSource; + +import java.util.Iterator; +import java.util.List; +import java.util.stream.Stream; + +import static ca.uhn.fhir.jpa.provider.CodeSystemLookupWithPropertiesUtil.ourCode; +import static ca.uhn.fhir.jpa.provider.CodeSystemLookupWithPropertiesUtil.ourCodeSystemId; +import static ca.uhn.fhir.jpa.provider.CodeSystemLookupWithPropertiesUtil.ourCodeSystemUrl; +import static ca.uhn.fhir.jpa.provider.CodeSystemLookupWithPropertiesUtil.ourPropertyA; +import static ca.uhn.fhir.jpa.provider.CodeSystemLookupWithPropertiesUtil.ourPropertyB; +import static ca.uhn.fhir.jpa.provider.CodeSystemLookupWithPropertiesUtil.ourPropertyC; +import static ca.uhn.fhir.jpa.provider.CodeSystemLookupWithPropertiesUtil.ourPropertyValueA; +import static ca.uhn.fhir.jpa.provider.CodeSystemLookupWithPropertiesUtil.ourPropertyValueB; +import static ca.uhn.fhir.jpa.provider.CodeSystemLookupWithPropertiesUtil.propertyCode; +import static ca.uhn.fhir.jpa.provider.CodeSystemLookupWithPropertiesUtil.propertyCodeSystem; +import static ca.uhn.fhir.jpa.provider.CodeSystemLookupWithPropertiesUtil.propertyDisplay; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertTrue; + +public class ResourceProviderR5CodeSystemPropertiesTest extends BaseResourceProviderR5Test { + public static Stream parametersLookup() { + return CodeSystemLookupWithPropertiesUtil.parametersLookupWithProperties(); + } + + @ParameterizedTest + @MethodSource(value = "parametersLookup") + public void testLookup_withProperties_returnsCorrectParameters(List theLookupProperties, List theExpectedReturnedProperties) { + // setup + CodeSystem codeSystem = new CodeSystem(); + codeSystem.setId(ourCodeSystemId); + codeSystem.setUrl(ourCodeSystemUrl); + CodeSystem.ConceptDefinitionComponent concept = codeSystem.addConcept().setCode(ourCode) + .addProperty(new ConceptPropertyComponent().setCode(ourPropertyA).setValue(new StringType(ourPropertyValueA))) + .addProperty(new ConceptPropertyComponent().setCode(ourPropertyB).setValue(new StringType(ourPropertyValueB))) + .addProperty(new ConceptPropertyComponent().setCode(ourPropertyC).setValue(new Coding(propertyCodeSystem, propertyCode, propertyDisplay))); + myCodeSystemDao.create(codeSystem, mySrd); + + // test + IOperationUntypedWithInputAndPartialOutput respParam = myClient + .operation() + .onType(CodeSystem.class) + .named(JpaConstants.OPERATION_LOOKUP) + .withParameter(Parameters.class, "code", new CodeType(ourCode)) + .andParameter("system", new UriType(ourCodeSystemUrl)); + + theLookupProperties.forEach(p -> respParam.andParameter("property", new CodeType(p))); + Parameters parameters = respParam.execute(); + + // verify + if (theExpectedReturnedProperties.isEmpty()) { + assertFalse(parameters.hasParameter("property")); + return; + } + + assertTrue(parameters.hasParameter("property")); + Iterator parameterPropertyIterator = parameters.getParameters("property").iterator(); + + Iterator propertyIterator = concept.getProperty().stream() + .filter(property -> theExpectedReturnedProperties.contains(property.getCode())).iterator(); + + while (propertyIterator.hasNext()) { + ConceptPropertyComponent property = propertyIterator.next(); + + assertTrue(parameterPropertyIterator.hasNext()); + ParametersParameterComponent parameter = parameterPropertyIterator.next(); + Iterator parameterPartIterator = parameter.getPart().iterator(); + + parameter = parameterPartIterator.next(); + assertEquals("code", parameter.getName()); + assertEquals(property.getCode(), ((CodeType) parameter.getValue()).getValue()); + + parameter = parameterPartIterator.next(); + assertEquals("value", parameter.getName()); + assertTrue(property.getValue().equalsShallow(parameter.getValue())); + + assertFalse(parameterPartIterator.hasNext()); + } + } +} diff --git a/hapi-fhir-jpaserver-test-r5/src/test/java/ca/uhn/fhir/jpa/provider/r5/ResourceProviderR5ValueSetTest.java b/hapi-fhir-jpaserver-test-r5/src/test/java/ca/uhn/fhir/jpa/provider/r5/ResourceProviderR5ValueSetTest.java index 6d9b8a299c7..f5f882e38ff 100644 --- a/hapi-fhir-jpaserver-test-r5/src/test/java/ca/uhn/fhir/jpa/provider/r5/ResourceProviderR5ValueSetTest.java +++ b/hapi-fhir-jpaserver-test-r5/src/test/java/ca/uhn/fhir/jpa/provider/r5/ResourceProviderR5ValueSetTest.java @@ -1,5 +1,6 @@ package ca.uhn.fhir.jpa.provider.r5; +import ca.uhn.fhir.context.support.IValidationSupport; import ca.uhn.fhir.i18n.Msg; import ca.uhn.fhir.jpa.api.config.JpaStorageSettings; import ca.uhn.fhir.jpa.api.dao.IFhirResourceDao; @@ -12,7 +13,6 @@ import ca.uhn.fhir.jpa.entity.TermValueSetConcept; import ca.uhn.fhir.jpa.entity.TermValueSetPreExpansionStatusEnum; import ca.uhn.fhir.jpa.model.dao.JpaPid; import ca.uhn.fhir.jpa.model.entity.ResourceTable; -import ca.uhn.fhir.jpa.provider.ValueSetOperationProvider; import ca.uhn.fhir.jpa.term.api.ITermCodeSystemStorageSvc; import ca.uhn.fhir.jpa.term.api.ITermReadSvc; import ca.uhn.fhir.rest.server.exceptions.InvalidRequestException; @@ -48,7 +48,7 @@ import org.springframework.transaction.TransactionStatus; import org.springframework.transaction.support.TransactionCallbackWithoutResult; import org.springframework.transaction.support.TransactionTemplate; -import javax.annotation.Nonnull; +import jakarta.annotation.Nonnull; import java.io.IOException; import java.nio.charset.StandardCharsets; import java.util.List; @@ -1229,13 +1229,13 @@ public class ResourceProviderR5ValueSetTest extends BaseResourceProviderR5Test { String resp = myFhirCtx.newXmlParser().setPrettyPrint(true).encodeResourceToString(respParam); ourLog.info(resp); - assertEquals(ValueSetOperationProvider.RESULT, respParam.getParameter().get(0).getName()); + assertEquals(IValidationSupport.CodeValidationResult.RESULT, respParam.getParameter().get(0).getName()); assertEquals(true, ((BooleanType) respParam.getParameter().get(0).getValue()).getValue()); - assertEquals(ValueSetOperationProvider.DISPLAY, respParam.getParameter().get(1).getName()); + assertEquals(IValidationSupport.CodeValidationResult.DISPLAY, respParam.getParameter().get(1).getName()); assertEquals("Male", ((StringType) respParam.getParameter().get(1).getValue()).getValue()); - assertEquals(ValueSetOperationProvider.SOURCE_DETAILS, respParam.getParameter().get(2).getName()); + assertEquals(IValidationSupport.CodeValidationResult.SOURCE_DETAILS, respParam.getParameter().get(2).getName()); assertEquals("Code was validated against in-memory expansion of ValueSet: http://hl7.org/fhir/ValueSet/administrative-gender", ((StringType) respParam.getParameter().get(2).getValue()).getValue()); } diff --git a/hapi-fhir-jpaserver-test-r5/src/test/java/ca/uhn/fhir/jpa/provider/r5/ResourceProviderR5ValueSetVersionedTest.java b/hapi-fhir-jpaserver-test-r5/src/test/java/ca/uhn/fhir/jpa/provider/r5/ResourceProviderR5ValueSetVersionedTest.java index 95f1cb3d62d..824384e57c4 100644 --- a/hapi-fhir-jpaserver-test-r5/src/test/java/ca/uhn/fhir/jpa/provider/r5/ResourceProviderR5ValueSetVersionedTest.java +++ b/hapi-fhir-jpaserver-test-r5/src/test/java/ca/uhn/fhir/jpa/provider/r5/ResourceProviderR5ValueSetVersionedTest.java @@ -40,7 +40,7 @@ import org.springframework.transaction.TransactionStatus; import org.springframework.transaction.support.TransactionCallbackWithoutResult; import org.springframework.transaction.support.TransactionTemplate; -import javax.annotation.Nonnull; +import jakarta.annotation.Nonnull; import java.io.IOException; import java.util.Optional; diff --git a/hapi-fhir-jpaserver-test-r5/src/test/java/ca/uhn/fhir/jpa/search/reindex/InstanceReindexServiceImplNarrativeR5Test.java b/hapi-fhir-jpaserver-test-r5/src/test/java/ca/uhn/fhir/jpa/search/reindex/InstanceReindexServiceImplNarrativeR5Test.java index f523b6e2243..472d68df121 100644 --- a/hapi-fhir-jpaserver-test-r5/src/test/java/ca/uhn/fhir/jpa/search/reindex/InstanceReindexServiceImplNarrativeR5Test.java +++ b/hapi-fhir-jpaserver-test-r5/src/test/java/ca/uhn/fhir/jpa/search/reindex/InstanceReindexServiceImplNarrativeR5Test.java @@ -16,8 +16,8 @@ import ca.uhn.fhir.jpa.model.entity.ResourceTable; import ca.uhn.fhir.jpa.model.entity.SearchParamPresentEntity; import ca.uhn.fhir.jpa.searchparam.extractor.ResourceIndexedSearchParams; import ca.uhn.fhir.test.utilities.HtmlUtil; -import com.gargoylesoftware.htmlunit.html.HtmlPage; -import com.gargoylesoftware.htmlunit.html.HtmlTable; +import org.htmlunit.html.HtmlPage; +import org.htmlunit.html.HtmlTable; import org.hl7.fhir.r4.model.IdType; import org.hl7.fhir.r4.model.Parameters; import org.hl7.fhir.r4.model.StringType; @@ -25,7 +25,7 @@ import org.junit.jupiter.api.Test; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import javax.annotation.Nonnull; +import jakarta.annotation.Nonnull; import java.io.IOException; import java.math.BigDecimal; import java.util.Collections; diff --git a/hapi-fhir-jpaserver-test-r5/src/test/java/ca/uhn/fhir/jpa/search/reindex/InstanceReindexServiceImplR5Test.java b/hapi-fhir-jpaserver-test-r5/src/test/java/ca/uhn/fhir/jpa/search/reindex/InstanceReindexServiceImplR5Test.java index c8e4d6fe47f..18c32173d39 100644 --- a/hapi-fhir-jpaserver-test-r5/src/test/java/ca/uhn/fhir/jpa/search/reindex/InstanceReindexServiceImplR5Test.java +++ b/hapi-fhir-jpaserver-test-r5/src/test/java/ca/uhn/fhir/jpa/search/reindex/InstanceReindexServiceImplR5Test.java @@ -20,7 +20,7 @@ import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.springframework.beans.factory.annotation.Autowired; -import javax.annotation.Nonnull; +import jakarta.annotation.Nonnull; import java.util.List; import java.util.Set; diff --git a/hapi-fhir-jpaserver-test-r5/src/test/java/ca/uhn/fhir/jpa/subscription/BaseSubscriptionsR5Test.java b/hapi-fhir-jpaserver-test-r5/src/test/java/ca/uhn/fhir/jpa/subscription/BaseSubscriptionsR5Test.java index a39f187b5ae..44dd5e99568 100644 --- a/hapi-fhir-jpaserver-test-r5/src/test/java/ca/uhn/fhir/jpa/subscription/BaseSubscriptionsR5Test.java +++ b/hapi-fhir-jpaserver-test-r5/src/test/java/ca/uhn/fhir/jpa/subscription/BaseSubscriptionsR5Test.java @@ -27,8 +27,8 @@ import ca.uhn.test.concurrency.PointcutLatch; import net.ttddyy.dsproxy.QueryCount; import net.ttddyy.dsproxy.listener.SingleQueryCountHolder; import org.eclipse.jetty.server.Server; -import org.eclipse.jetty.servlet.ServletContextHandler; -import org.eclipse.jetty.servlet.ServletHolder; +import org.eclipse.jetty.ee10.servlet.ServletContextHandler; +import org.eclipse.jetty.ee10.servlet.ServletHolder; import org.hl7.fhir.instance.model.api.IBaseResource; import org.hl7.fhir.instance.model.api.IIdType; import org.hl7.fhir.r5.model.Bundle; @@ -44,9 +44,9 @@ import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Disabled; import org.springframework.beans.factory.annotation.Autowired; -import javax.annotation.Nonnull; -import javax.annotation.PostConstruct; -import javax.servlet.http.HttpServletRequest; +import jakarta.annotation.Nonnull; +import jakarta.annotation.PostConstruct; +import jakarta.servlet.http.HttpServletRequest; import java.util.ArrayList; import java.util.Collections; import java.util.Enumeration; diff --git a/hapi-fhir-jpaserver-test-r5/src/test/java/ca/uhn/fhir/jpa/subscription/resthook/RestHookTestR5IT.java b/hapi-fhir-jpaserver-test-r5/src/test/java/ca/uhn/fhir/jpa/subscription/resthook/RestHookTestR5IT.java index 86c779a5a50..c943839c189 100644 --- a/hapi-fhir-jpaserver-test-r5/src/test/java/ca/uhn/fhir/jpa/subscription/resthook/RestHookTestR5IT.java +++ b/hapi-fhir-jpaserver-test-r5/src/test/java/ca/uhn/fhir/jpa/subscription/resthook/RestHookTestR5IT.java @@ -33,7 +33,7 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; -import javax.annotation.Nonnull; +import jakarta.annotation.Nonnull; import java.util.List; import java.util.concurrent.CountDownLatch; import java.util.concurrent.TimeUnit; diff --git a/hapi-fhir-jpaserver-test-utilities/ResourceTable/segments_1 b/hapi-fhir-jpaserver-test-utilities/ResourceTable/segments_1 new file mode 100644 index 00000000000..45ec119a611 Binary files /dev/null and b/hapi-fhir-jpaserver-test-utilities/ResourceTable/segments_1 differ diff --git a/hapi-fhir-jpaserver-test-utilities/ResourceTable/write.lock b/hapi-fhir-jpaserver-test-utilities/ResourceTable/write.lock new file mode 100644 index 00000000000..e69de29bb2d diff --git a/hapi-fhir-jpaserver-test-utilities/TermConcept/segments_1 b/hapi-fhir-jpaserver-test-utilities/TermConcept/segments_1 new file mode 100644 index 00000000000..bebbd510042 Binary files /dev/null and b/hapi-fhir-jpaserver-test-utilities/TermConcept/segments_1 differ diff --git a/hapi-fhir-jpaserver-test-utilities/TermConcept/write.lock b/hapi-fhir-jpaserver-test-utilities/TermConcept/write.lock new file mode 100644 index 00000000000..e69de29bb2d diff --git a/hapi-fhir-jpaserver-test-utilities/pom.xml b/hapi-fhir-jpaserver-test-utilities/pom.xml index 39be13b38d2..225aae4f29f 100644 --- a/hapi-fhir-jpaserver-test-utilities/pom.xml +++ b/hapi-fhir-jpaserver-test-utilities/pom.xml @@ -6,7 +6,7 @@ ca.uhn.hapi.fhir hapi-deployable-pom - 6.9.7-SNAPSHOT + 6.11.7-SNAPSHOT ../hapi-deployable-pom/pom.xml @@ -85,42 +85,6 @@ org.apache.derby derby - - org.eclipse.jetty - jetty-servlets - - - org.eclipse.jetty - jetty-servlet - - - org.eclipse.jetty - jetty-server - - - org.eclipse.jetty - jetty-util - - - org.eclipse.jetty - jetty-webapp - - - org.eclipse.jetty.websocket - websocket-jetty-api - - - org.eclipse.jetty.websocket - websocket-core-client - - - org.eclipse.jetty.websocket - websocket-jetty-client - - - org.eclipse.jetty.websocket - websocket-jetty-server - org.springframework.boot spring-boot-test @@ -167,20 +131,14 @@ org.testcontainers postgresql - 1.17.6 - compile org.testcontainers mssqlserver - 1.17.6 - compile org.testcontainers oracle-xe - 1.17.6 - compile org.postgresql @@ -215,17 +173,21 @@ mockito-core - com.helger - ph-schematron + com.helger.schematron + ph-schematron-api - com.helger - ph-commons + com.helger.schematron + ph-schematron-xslt org.testcontainers junit-jupiter + + jakarta.websocket + jakarta.websocket-client-api + com.jayway.jsonpath @@ -259,7 +221,6 @@ - diff --git a/hapi-fhir-jpaserver-test-utilities/src/main/java/ca/uhn/fhir/jpa/dao/TestDaoSearch.java b/hapi-fhir-jpaserver-test-utilities/src/main/java/ca/uhn/fhir/jpa/dao/TestDaoSearch.java index 670b8e07875..0609fb32fe8 100644 --- a/hapi-fhir-jpaserver-test-utilities/src/main/java/ca/uhn/fhir/jpa/dao/TestDaoSearch.java +++ b/hapi-fhir-jpaserver-test-utilities/src/main/java/ca/uhn/fhir/jpa/dao/TestDaoSearch.java @@ -32,6 +32,7 @@ import ca.uhn.fhir.rest.server.IPagingProvider; import ca.uhn.fhir.rest.server.IRestfulServerDefaults; import ca.uhn.fhir.rest.server.method.SortParameter; import ca.uhn.fhir.rest.server.util.ISearchParamRegistry; +import jakarta.annotation.Nonnull; import org.hamcrest.Matcher; import org.hamcrest.MatcherAssert; import org.hl7.fhir.instance.model.api.IBaseResource; @@ -44,8 +45,10 @@ import org.springframework.web.util.UriComponentsBuilder; import java.util.List; import java.util.stream.Collectors; -import javax.annotation.Nonnull; +import static org.apache.commons.lang3.ArrayUtils.EMPTY_STRING_ARRAY; +import static org.hamcrest.Matchers.contains; +import static org.hamcrest.Matchers.containsInAnyOrder; import static org.hamcrest.Matchers.everyItem; import static org.hamcrest.Matchers.hasItems; import static org.hamcrest.Matchers.in; @@ -105,6 +108,10 @@ public class TestDaoSearch { assertSearchResultIds(theQueryUrl, theReason, hasItems(theIds)); } + public void assertSearchFinds(String theReason, String theQueryUrl, List theIds) { + assertSearchFinds(theReason, theQueryUrl, theIds.toArray(EMPTY_STRING_ARRAY)); + } + /** * Assert that the FHIR search has theIds in the search results. * @param theReason junit reason message @@ -117,6 +124,27 @@ public class TestDaoSearch { assertSearchResultIds(theQueryUrl, theReason, hasItems(bareIds)); } + public void assertSearchFindsInOrder(String theReason, String theQueryUrl, String... theIds) { + List ids = searchForIds(theQueryUrl); + + MatcherAssert.assertThat(theReason, ids, contains(theIds)); + } + + public void assertSearchFindsInOrder(String theReason, String theQueryUrl, List theIds) { + assertSearchFindsInOrder(theReason, theQueryUrl, theIds.toArray(EMPTY_STRING_ARRAY)); + } + + public void assertSearchFindsOnly(String theReason, String theQueryUrl, String... theIds) { + assertSearchIdsMatch(theReason, theQueryUrl, containsInAnyOrder(theIds)); + } + + public void assertSearchIdsMatch( + String theReason, String theQueryUrl, Matcher> theMatchers) { + List ids = searchForIds(theQueryUrl); + + MatcherAssert.assertThat(theReason, ids, theMatchers); + } + public void assertSearchResultIds(String theQueryUrl, String theReason, Matcher> matcher) { List ids = searchForIds(theQueryUrl); diff --git a/hapi-fhir-jpaserver-test-utilities/src/main/java/ca/uhn/fhir/jpa/embedded/HapiEmbeddedDatabasesExtension.java b/hapi-fhir-jpaserver-test-utilities/src/main/java/ca/uhn/fhir/jpa/embedded/HapiEmbeddedDatabasesExtension.java index 4b09d4fd13c..697f6a96f7c 100644 --- a/hapi-fhir-jpaserver-test-utilities/src/main/java/ca/uhn/fhir/jpa/embedded/HapiEmbeddedDatabasesExtension.java +++ b/hapi-fhir-jpaserver-test-utilities/src/main/java/ca/uhn/fhir/jpa/embedded/HapiEmbeddedDatabasesExtension.java @@ -20,6 +20,7 @@ package ca.uhn.fhir.jpa.embedded; import ca.uhn.fhir.jpa.migrate.DriverTypeEnum; +import ca.uhn.fhir.test.utilities.docker.DockerRequiredCondition; import ca.uhn.fhir.util.VersionEnum; import org.apache.commons.lang3.StringUtils; import org.apache.commons.lang3.SystemUtils; @@ -51,20 +52,24 @@ public class HapiEmbeddedDatabasesExtension implements AfterAllCallback { private final DatabaseInitializerHelper myDatabaseInitializerHelper = new DatabaseInitializerHelper(); public HapiEmbeddedDatabasesExtension() { - myEmbeddedDatabases.add(new H2EmbeddedDatabase()); - myEmbeddedDatabases.add(new PostgresEmbeddedDatabase()); - myEmbeddedDatabases.add(new MsSqlEmbeddedDatabase()); - if (canUseOracle()) { - myEmbeddedDatabases.add(new OracleEmbeddedDatabase()); + if (DockerRequiredCondition.isDockerAvailable()) { + myEmbeddedDatabases.add(new H2EmbeddedDatabase()); + myEmbeddedDatabases.add(new PostgresEmbeddedDatabase()); + myEmbeddedDatabases.add(new MsSqlEmbeddedDatabase()); + if (canUseOracle()) { + myEmbeddedDatabases.add(new OracleEmbeddedDatabase()); + } else { + String message = + "Cannot add OracleEmbeddedDatabase. If you are using a Mac you must configure the TestContainers API to run using Colima (https://www.testcontainers.org/supported_docker_environment#using-colima)"; + ourLog.warn(message); + } } else { - String message = - "Cannot add OracleEmbeddedDatabase. If you are using a Mac you must configure the TestContainers API to run using Colima (https://www.testcontainers.org/supported_docker_environment#using-colima)"; - ourLog.warn(message); + ourLog.warn("Docker is not available! Not going to start any embedded databases."); } } @Override - public void afterAll(ExtensionContext theExtensionContext) throws Exception { + public void afterAll(ExtensionContext theExtensionContext) { for (JpaEmbeddedDatabase database : getAllEmbeddedDatabases()) { database.stop(); } diff --git a/hapi-fhir-jpaserver-test-utilities/src/main/java/ca/uhn/fhir/jpa/embedded/JpaEmbeddedDatabase.java b/hapi-fhir-jpaserver-test-utilities/src/main/java/ca/uhn/fhir/jpa/embedded/JpaEmbeddedDatabase.java index ae26426bdee..f87d1d25d14 100644 --- a/hapi-fhir-jpaserver-test-utilities/src/main/java/ca/uhn/fhir/jpa/embedded/JpaEmbeddedDatabase.java +++ b/hapi-fhir-jpaserver-test-utilities/src/main/java/ca/uhn/fhir/jpa/embedded/JpaEmbeddedDatabase.java @@ -20,6 +20,7 @@ package ca.uhn.fhir.jpa.embedded; import ca.uhn.fhir.jpa.migrate.DriverTypeEnum; +import jakarta.annotation.PreDestroy; import org.apache.commons.lang3.StringUtils; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -53,6 +54,7 @@ public abstract class JpaEmbeddedDatabase { private JdbcTemplate myJdbcTemplate; private Connection myConnection; + @PreDestroy public abstract void stop(); public abstract void disableConstraints(); @@ -116,7 +118,7 @@ public abstract class JpaEmbeddedDatabase { for (String sql : theStatements) { if (!StringUtils.isBlank(sql)) { statement.addBatch(sql); - ourLog.info("Added to batch: {}", sql); + ourLog.debug("Added to batch: {}", sql); } } statement.executeBatch(); diff --git a/hapi-fhir-jpaserver-test-utilities/src/main/java/ca/uhn/fhir/jpa/interceptor/ex/PartitionInterceptorReadPartitionsBasedOnScopes.java b/hapi-fhir-jpaserver-test-utilities/src/main/java/ca/uhn/fhir/jpa/interceptor/ex/PartitionInterceptorReadPartitionsBasedOnScopes.java index 588ffa0cb2e..256c57078a9 100644 --- a/hapi-fhir-jpaserver-test-utilities/src/main/java/ca/uhn/fhir/jpa/interceptor/ex/PartitionInterceptorReadPartitionsBasedOnScopes.java +++ b/hapi-fhir-jpaserver-test-utilities/src/main/java/ca/uhn/fhir/jpa/interceptor/ex/PartitionInterceptorReadPartitionsBasedOnScopes.java @@ -25,9 +25,9 @@ import ca.uhn.fhir.interceptor.api.Pointcut; import ca.uhn.fhir.interceptor.model.RequestPartitionId; import ca.uhn.fhir.rest.server.exceptions.InvalidRequestException; import ca.uhn.fhir.rest.server.servlet.ServletRequestDetails; +import jakarta.servlet.http.HttpServletRequest; import java.util.Set; -import javax.servlet.http.HttpServletRequest; // This class is replicated in PartitionExamples.java -- Keep it up to date there too!! @Interceptor diff --git a/hapi-fhir-jpaserver-test-utilities/src/main/java/ca/uhn/fhir/jpa/mdm/IMdmMetricSvcTest.java b/hapi-fhir-jpaserver-test-utilities/src/main/java/ca/uhn/fhir/jpa/mdm/IMdmMetricSvcTest.java new file mode 100644 index 00000000000..dd19eccec34 --- /dev/null +++ b/hapi-fhir-jpaserver-test-utilities/src/main/java/ca/uhn/fhir/jpa/mdm/IMdmMetricSvcTest.java @@ -0,0 +1,196 @@ +/*- + * #%L + * HAPI FHIR JPA Server Test Utilities + * %% + * Copyright (C) 2014 - 2023 Smile CDR, Inc. + * %% + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * #L% + */ +package ca.uhn.fhir.jpa.mdm; + +import ca.uhn.fhir.jpa.mdm.models.GenerateMetricsTestParameters; +import ca.uhn.fhir.jpa.mdm.models.LinkMetricTestParameters; +import ca.uhn.fhir.jpa.mdm.models.LinkScoreMetricTestParams; +import ca.uhn.fhir.jpa.mdm.models.ResourceMetricTestParams; +import ca.uhn.fhir.jpa.mdm.util.MdmMetricSvcTestUtil; +import ca.uhn.fhir.mdm.api.IMdmMetricSvc; +import ca.uhn.fhir.mdm.api.MdmLinkSourceEnum; +import ca.uhn.fhir.mdm.api.MdmMatchResultEnum; +import ca.uhn.fhir.mdm.api.params.GenerateMdmMetricsParameters; +import ca.uhn.fhir.mdm.model.MdmMetrics; +import ca.uhn.fhir.mdm.model.MdmResourceMetrics; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.MethodSource; + +import java.util.Arrays; +import java.util.Map; +import java.util.function.Supplier; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNotNull; +import static org.junit.jupiter.api.Assertions.assertTrue; + +/** + * Tests the various metrics returned by IMdmMetricSvc + * Because of the way these metrics are broken down in 3 different ways, + * these results are tested separately, even though there is a single + * entry point. + */ +public interface IMdmMetricSvcTest { + + IMdmMetricSvc getMetricsSvc(); + + void generateMdmMetricsSetup(GenerateMetricsTestParameters theParameters); + + @Test + default void generateMdmMetrics_generalTest_happyPath() { + // setup + GenerateMetricsTestParameters testParameters = new GenerateMetricsTestParameters(); + testParameters.setInitialState(MdmMetricSvcTestUtil.OUR_BASIC_STATE); + testParameters.setScores(Arrays.asList(0.1, 0.2, 0.3, 0.4)); + + generateMdmMetricsSetup(testParameters); + + // test + GenerateMdmMetricsParameters parameters = new GenerateMdmMetricsParameters("Patient"); + MdmMetrics results = getMetricsSvc().generateMdmMetrics(parameters); + + // verify + assertNotNull(results); + assertEquals("Patient", results.getResourceType()); + assertEquals(4, results.getGoldenResourcesCount()); + assertEquals(4, results.getSourceResourcesCount()); + assertEquals(0, results.getExcludedResources()); + + Map> map = results.getMatchTypeToLinkToCountMap(); + // See OUR_BASIC_STATE + assertEquals(3, map.size()); + for (MdmMatchResultEnum matchResult : new MdmMatchResultEnum[] { + MdmMatchResultEnum.MATCH, MdmMatchResultEnum.NO_MATCH, MdmMatchResultEnum.POSSIBLE_MATCH + }) { + assertTrue(map.containsKey(matchResult)); + Map source2Count = map.get(matchResult); + assertNotNull(source2Count); + for (MdmLinkSourceEnum ls : MdmLinkSourceEnum.values()) { + assertNotNull(source2Count.get(ls)); + } + } + } + + void generateLinkMetricsSetup(LinkMetricTestParameters theParameters); + + @ParameterizedTest + @MethodSource("ca.uhn.fhir.jpa.mdm.util.MdmMetricSvcTestUtil#linkMetricsParameters") + default void test_generateLinkMetrics_multipleInputs(LinkMetricTestParameters theParameters) { + // setup + generateLinkMetricsSetup(theParameters); + + // all tests use Patient resource type + GenerateMdmMetricsParameters parameters = new GenerateMdmMetricsParameters("Patient"); + for (MdmLinkSourceEnum linkSource : theParameters.getLinkSourceFilters()) { + parameters.addLinkSource(linkSource); + } + for (MdmMatchResultEnum matchResultEnum : theParameters.getMatchFilters()) { + parameters.addMatchResult(matchResultEnum); + } + + // test + MdmMetrics metrics = getMetricsSvc().generateMdmMetrics(parameters); + + // verify + assertNotNull(metrics); + assertEquals(metrics.getResourceType(), "Patient"); + + MdmMetrics expectedMetrics = theParameters.getExpectedMetrics(); + + Supplier err = () -> getComparingMetrics(metrics, expectedMetrics); + + Map> actual = metrics.getMatchTypeToLinkToCountMap(); + Map> expected = expectedMetrics.getMatchTypeToLinkToCountMap(); + assertEquals(expected, actual, err.get()); + + for (MdmMatchResultEnum matchResult : MdmMatchResultEnum.values()) { + assertEquals(expected.containsKey(matchResult), actual.containsKey(matchResult), err.get()); + if (actual.containsKey(matchResult)) { + Map actualMatch = actual.get(matchResult); + Map expectedMatch = expected.get(matchResult); + assertEquals(expectedMatch, actualMatch, err.get()); + for (MdmLinkSourceEnum linkSource : MdmLinkSourceEnum.values()) { + assertEquals(expectedMatch.get(linkSource), actualMatch.get(linkSource), err.get()); + } + } + } + } + + void generateResourceMetricsSetup(ResourceMetricTestParams theParams); + + @ParameterizedTest + @MethodSource("ca.uhn.fhir.jpa.mdm.util.MdmMetricSvcTestUtil#resourceMetricParameters") + default void test_generateResourceMetrics_multipleInputs(ResourceMetricTestParams theParams) { + // setup + generateResourceMetricsSetup(theParams); + + // test + GenerateMdmMetricsParameters parameters = new GenerateMdmMetricsParameters("Patient"); + MdmResourceMetrics results = getMetricsSvc().generateMdmMetrics(parameters); + + // verify + assertNotNull(results); + assertEquals("Patient", results.getResourceType()); + assertEquals( + theParams.getExpectedResourceCount(), + results.getSourceResourcesCount() + results.getGoldenResourcesCount()); + assertEquals(theParams.getExpectedBlockedResourceCount(), results.getExcludedResources()); + assertEquals(theParams.getExpectedGoldenResourceCount(), results.getGoldenResourcesCount()); + } + + void generateLinkScoreMetricsSetup(LinkScoreMetricTestParams theParams); + + @ParameterizedTest + @MethodSource("ca.uhn.fhir.jpa.mdm.util.MdmMetricSvcTestUtil#linkScoreParameters") + default void test_generateLinkScoreMetrics_multipleInputs(LinkScoreMetricTestParams theParams) { + // setup + generateLinkScoreMetricsSetup(theParams); + + GenerateMdmMetricsParameters scoreMetricsParameters = new GenerateMdmMetricsParameters("Patient"); + for (MdmMatchResultEnum matchType : theParams.getMatchFilter()) { + scoreMetricsParameters.addMatchResult(matchType); + } + + // test + MdmMetrics actualMetrics = getMetricsSvc().generateMdmMetrics(scoreMetricsParameters); + + // verify + assertNotNull(actualMetrics); + assertEquals("Patient", actualMetrics.getResourceType()); + + MdmMetrics expectedMetrics = theParams.getExpectedMetrics(); + + Map actual = actualMetrics.getScoreCounts(); + Map expected = expectedMetrics.getScoreCounts(); + assertEquals(expected.size(), actual.size()); + for (String score : expected.keySet()) { + assertTrue(actual.containsKey(score), String.format("Score of %s is not in results", score)); + assertEquals(expected.get(score), actual.get(score), score); + } + } + + private String getComparingMetrics(MdmMetrics theActual, MdmMetrics theExpected) { + return String.format( + "\nExpected: \n%s - \nActual: \n%s", getStringMetrics(theExpected), getStringMetrics(theActual)); + } + + String getStringMetrics(MdmMetrics theMetrics); +} diff --git a/hapi-fhir-jpaserver-test-utilities/src/main/java/ca/uhn/fhir/jpa/mdm/models/GenerateMetricsTestParameters.java b/hapi-fhir-jpaserver-test-utilities/src/main/java/ca/uhn/fhir/jpa/mdm/models/GenerateMetricsTestParameters.java new file mode 100644 index 00000000000..3e1a3a7d0f5 --- /dev/null +++ b/hapi-fhir-jpaserver-test-utilities/src/main/java/ca/uhn/fhir/jpa/mdm/models/GenerateMetricsTestParameters.java @@ -0,0 +1,50 @@ +/*- + * #%L + * HAPI FHIR JPA Server Test Utilities + * %% + * Copyright (C) 2014 - 2023 Smile CDR, Inc. + * %% + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * #L% + */ +package ca.uhn.fhir.jpa.mdm.models; + +import java.util.List; + +public class GenerateMetricsTestParameters { + + private String myInitialState; + + /** + * The scores for each link. + * The order should match the order of the + * links listed in initial state. + */ + private List myScores; + + public String getInitialState() { + return myInitialState; + } + + public void setInitialState(String theInitialState) { + myInitialState = theInitialState; + } + + public List getScores() { + return myScores; + } + + public void setScores(List theScores) { + myScores = theScores; + } +} diff --git a/hapi-fhir-jpaserver-test-utilities/src/main/java/ca/uhn/fhir/jpa/mdm/models/LinkMetricTestParameters.java b/hapi-fhir-jpaserver-test-utilities/src/main/java/ca/uhn/fhir/jpa/mdm/models/LinkMetricTestParameters.java new file mode 100644 index 00000000000..cc02c9cecef --- /dev/null +++ b/hapi-fhir-jpaserver-test-utilities/src/main/java/ca/uhn/fhir/jpa/mdm/models/LinkMetricTestParameters.java @@ -0,0 +1,87 @@ +/*- + * #%L + * HAPI FHIR JPA Server Test Utilities + * %% + * Copyright (C) 2014 - 2023 Smile CDR, Inc. + * %% + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * #L% + */ +package ca.uhn.fhir.jpa.mdm.models; + +import ca.uhn.fhir.mdm.api.MdmLinkSourceEnum; +import ca.uhn.fhir.mdm.api.MdmMatchResultEnum; +import ca.uhn.fhir.mdm.model.MdmMetrics; + +import java.util.ArrayList; +import java.util.List; + +public class LinkMetricTestParameters { + /** + * The initial state (as to be fed into MdmLinkHelper) + */ + private String myInitialState; + + /** + * The filters for MatchResult + */ + private List myMatchFilters; + + /** + * The filters for LinkSource + */ + private List myLinkSourceEnums; + + /** + * The expected metrics to be returned + */ + private MdmMetrics myExpectedMetrics; + + public String getInitialState() { + return myInitialState; + } + + public void setInitialState(String theInitialState) { + myInitialState = theInitialState; + } + + public List getMatchFilters() { + if (myMatchFilters == null) { + myMatchFilters = new ArrayList<>(); + } + return myMatchFilters; + } + + public void setMatchFilters(List theMatchFilters) { + myMatchFilters = theMatchFilters; + } + + public List getLinkSourceFilters() { + if (myLinkSourceEnums == null) { + myLinkSourceEnums = new ArrayList<>(); + } + return myLinkSourceEnums; + } + + public void setLinkSourceFilters(List theLinkSourceEnums) { + myLinkSourceEnums = theLinkSourceEnums; + } + + public MdmMetrics getExpectedMetrics() { + return myExpectedMetrics; + } + + public void setExpectedMetrics(MdmMetrics theExpectedMetrics) { + myExpectedMetrics = theExpectedMetrics; + } +} diff --git a/hapi-fhir-jpaserver-test-utilities/src/main/java/ca/uhn/fhir/jpa/mdm/models/LinkScoreMetricTestParams.java b/hapi-fhir-jpaserver-test-utilities/src/main/java/ca/uhn/fhir/jpa/mdm/models/LinkScoreMetricTestParams.java new file mode 100644 index 00000000000..4f44267c87f --- /dev/null +++ b/hapi-fhir-jpaserver-test-utilities/src/main/java/ca/uhn/fhir/jpa/mdm/models/LinkScoreMetricTestParams.java @@ -0,0 +1,79 @@ +/*- + * #%L + * HAPI FHIR JPA Server Test Utilities + * %% + * Copyright (C) 2014 - 2023 Smile CDR, Inc. + * %% + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * #L% + */ +package ca.uhn.fhir.jpa.mdm.models; + +import ca.uhn.fhir.mdm.api.MdmMatchResultEnum; +import ca.uhn.fhir.mdm.model.MdmMetrics; + +import java.util.ArrayList; +import java.util.List; + +public class LinkScoreMetricTestParams { + private String myInitialState; + + private List myMatchFilter; + + private MdmMetrics myExpectedMetrics; + + /** + * The scores for each link. + * The order should match the order of the + * links listed in initial state. + */ + private List myScores; + + public String getInitialState() { + return myInitialState; + } + + public void setInitialState(String theInitialState) { + myInitialState = theInitialState; + } + + public MdmMetrics getExpectedMetrics() { + return myExpectedMetrics; + } + + public void setExpectedMetrics(MdmMetrics theExpectedMetrics) { + myExpectedMetrics = theExpectedMetrics; + } + + public List getMatchFilter() { + if (myMatchFilter == null) { + myMatchFilter = new ArrayList<>(); + } + return myMatchFilter; + } + + public void addMatchType(MdmMatchResultEnum theResultEnum) { + getMatchFilter().add(theResultEnum); + } + + public List getScores() { + if (myScores == null) { + myScores = new ArrayList<>(); + } + return myScores; + } + + public void setScores(List theScores) { + myScores = theScores; + } +} diff --git a/hapi-fhir-jpaserver-test-utilities/src/main/java/ca/uhn/fhir/jpa/mdm/models/ResourceMetricTestParams.java b/hapi-fhir-jpaserver-test-utilities/src/main/java/ca/uhn/fhir/jpa/mdm/models/ResourceMetricTestParams.java new file mode 100644 index 00000000000..5d61242e70c --- /dev/null +++ b/hapi-fhir-jpaserver-test-utilities/src/main/java/ca/uhn/fhir/jpa/mdm/models/ResourceMetricTestParams.java @@ -0,0 +1,80 @@ +/*- + * #%L + * HAPI FHIR JPA Server Test Utilities + * %% + * Copyright (C) 2014 - 2023 Smile CDR, Inc. + * %% + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * #L% + */ +package ca.uhn.fhir.jpa.mdm.models; + +import java.util.ArrayList; +import java.util.List; + +public class ResourceMetricTestParams { + /** + * The initial state, as consumable by + * MdmLinkHelper. + */ + private String myInitialState; + + /** + * The list of Golden Resource Ids (in initial state) that should be + * saved as BlockedResources + */ + private List myBlockedResourceGoldenResourceIds; + + private long myExpectedResourceCount; + + private long myExpectedGoldenResourceCount; + + public String getInitialState() { + return myInitialState; + } + + public void setInitialState(String theInitialState) { + myInitialState = theInitialState; + } + + public List getBlockedResourceGoldenResourceIds() { + if (myBlockedResourceGoldenResourceIds == null) { + myBlockedResourceGoldenResourceIds = new ArrayList<>(); + } + return myBlockedResourceGoldenResourceIds; + } + + public void addBlockedResourceGoldenResources(String theBlockedResourceId) { + getBlockedResourceGoldenResourceIds().add(theBlockedResourceId); + } + + public long getExpectedResourceCount() { + return myExpectedResourceCount; + } + + public void setExpectedResourceCount(long theExpectedResourceCount) { + myExpectedResourceCount = theExpectedResourceCount; + } + + public long getExpectedGoldenResourceCount() { + return myExpectedGoldenResourceCount; + } + + public void setExpectedGoldenResourceCount(long theExpectedGoldenResourceCount) { + myExpectedGoldenResourceCount = theExpectedGoldenResourceCount; + } + + public long getExpectedBlockedResourceCount() { + return getBlockedResourceGoldenResourceIds().size(); + } +} diff --git a/hapi-fhir-jpaserver-test-utilities/src/main/java/ca/uhn/fhir/jpa/mdm/package-info.java b/hapi-fhir-jpaserver-test-utilities/src/main/java/ca/uhn/fhir/jpa/mdm/package-info.java new file mode 100644 index 00000000000..5887575653c --- /dev/null +++ b/hapi-fhir-jpaserver-test-utilities/src/main/java/ca/uhn/fhir/jpa/mdm/package-info.java @@ -0,0 +1,25 @@ +/*- + * #%L + * HAPI FHIR JPA Server Test Utilities + * %% + * Copyright (C) 2014 - 2023 Smile CDR, Inc. + * %% + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * #L% + */ +/** + * This package is for persistence-agnostic mdm tests. + * Even though the package is "jpaserver-test-utils", these + * classes are not dependent on jpa backed persistence. + */ +package ca.uhn.fhir.jpa.mdm; diff --git a/hapi-fhir-jpaserver-test-utilities/src/main/java/ca/uhn/fhir/jpa/mdm/util/MdmMetricSvcTestUtil.java b/hapi-fhir-jpaserver-test-utilities/src/main/java/ca/uhn/fhir/jpa/mdm/util/MdmMetricSvcTestUtil.java new file mode 100644 index 00000000000..027ee7282c1 --- /dev/null +++ b/hapi-fhir-jpaserver-test-utilities/src/main/java/ca/uhn/fhir/jpa/mdm/util/MdmMetricSvcTestUtil.java @@ -0,0 +1,357 @@ +/*- + * #%L + * HAPI FHIR JPA Server Test Utilities + * %% + * Copyright (C) 2014 - 2023 Smile CDR, Inc. + * %% + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * #L% + */ +package ca.uhn.fhir.jpa.mdm.util; + +import ca.uhn.fhir.jpa.mdm.models.LinkMetricTestParameters; +import ca.uhn.fhir.jpa.mdm.models.LinkScoreMetricTestParams; +import ca.uhn.fhir.jpa.mdm.models.ResourceMetricTestParams; +import ca.uhn.fhir.mdm.api.BaseMdmMetricSvc; +import ca.uhn.fhir.mdm.api.MdmLinkSourceEnum; +import ca.uhn.fhir.mdm.api.MdmMatchResultEnum; +import ca.uhn.fhir.mdm.model.MdmMetrics; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import static org.apache.commons.lang3.StringUtils.isNotBlank; + +/** + * This provides parameter methods for the {@link ca.uhn.fhir.jpa.mdm.IMdmMetricSvcTest}. + */ +public class MdmMetricSvcTestUtil { + + public static final String OUR_BASIC_STATE = + """ + G1, AUTO, MATCH, P1 + G2, AUTO, MATCH, P2, + G3, AUTO, POSSIBLE_MATCH, P3, + G4, MANUAL, MATCH, P4 + G2, AUTO, NO_MATCH, P1 + G1, MANUAL, NO_MATCH, P2 + G1, MANUAL, POSSIBLE_MATCH, P3 + """; + + /** + * Parameters supplied to {@link ca.uhn.fhir.jpa.mdm.IMdmMetricSvcTest#test_generateLinkMetrics_multipleInputs(LinkMetricTestParameters)} + */ + public static List linkMetricsParameters() { + List params = new ArrayList<>(); + + // 1 + { + LinkMetricTestParameters testParameters = new LinkMetricTestParameters(); + testParameters.setInitialState(OUR_BASIC_STATE); + MdmMetrics metrics = new MdmMetrics(); + metrics.addMetric(MdmMatchResultEnum.MATCH, MdmLinkSourceEnum.AUTO, 2); + metrics.addMetric(MdmMatchResultEnum.MATCH, MdmLinkSourceEnum.MANUAL, 1); + metrics.addMetric(MdmMatchResultEnum.NO_MATCH, MdmLinkSourceEnum.AUTO, 1); + metrics.addMetric(MdmMatchResultEnum.NO_MATCH, MdmLinkSourceEnum.MANUAL, 1); + metrics.addMetric(MdmMatchResultEnum.POSSIBLE_MATCH, MdmLinkSourceEnum.AUTO, 1); + metrics.addMetric(MdmMatchResultEnum.POSSIBLE_MATCH, MdmLinkSourceEnum.MANUAL, 1); + testParameters.setExpectedMetrics(metrics); + params.add(testParameters); + } + + // 2 + { + // link source filter + LinkMetricTestParameters testParameters = new LinkMetricTestParameters(); + testParameters.setInitialState(OUR_BASIC_STATE); + testParameters.setLinkSourceFilters(Arrays.asList(MdmLinkSourceEnum.AUTO)); + MdmMetrics metrics = new MdmMetrics(); + metrics.addMetric(MdmMatchResultEnum.MATCH, MdmLinkSourceEnum.AUTO, 2); + metrics.addMetric(MdmMatchResultEnum.NO_MATCH, MdmLinkSourceEnum.AUTO, 1); + metrics.addMetric(MdmMatchResultEnum.POSSIBLE_MATCH, MdmLinkSourceEnum.AUTO, 1); + testParameters.setExpectedMetrics(metrics); + params.add(testParameters); + } + + // 3 + { + // match result filter + LinkMetricTestParameters testParameters = new LinkMetricTestParameters(); + testParameters.setInitialState(OUR_BASIC_STATE); + testParameters.setMatchFilters(Arrays.asList(MdmMatchResultEnum.MATCH, MdmMatchResultEnum.POSSIBLE_MATCH)); + MdmMetrics metrics = new MdmMetrics(); + metrics.addMetric(MdmMatchResultEnum.MATCH, MdmLinkSourceEnum.AUTO, 2); + metrics.addMetric(MdmMatchResultEnum.MATCH, MdmLinkSourceEnum.MANUAL, 1); + metrics.addMetric(MdmMatchResultEnum.POSSIBLE_MATCH, MdmLinkSourceEnum.AUTO, 1); + metrics.addMetric(MdmMatchResultEnum.POSSIBLE_MATCH, MdmLinkSourceEnum.MANUAL, 1); + testParameters.setExpectedMetrics(metrics); + params.add(testParameters); + } + + // 4 + { + // match result and link source filters + LinkMetricTestParameters testParameters = new LinkMetricTestParameters(); + testParameters.setInitialState(OUR_BASIC_STATE); + testParameters.setMatchFilters(Arrays.asList(MdmMatchResultEnum.MATCH)); + testParameters.setLinkSourceFilters(Arrays.asList(MdmLinkSourceEnum.MANUAL)); + MdmMetrics metrics = new MdmMetrics(); + metrics.addMetric(MdmMatchResultEnum.MATCH, MdmLinkSourceEnum.MANUAL, 1); + testParameters.setExpectedMetrics(metrics); + params.add(testParameters); + } + + // 5 + { + // no initial state + LinkMetricTestParameters testParameters = new LinkMetricTestParameters(); + testParameters.setInitialState(""); + MdmMetrics metrics = new MdmMetrics(); + testParameters.setExpectedMetrics(metrics); + params.add(testParameters); + } + + // 6 + { + // initial state with filters to omit all values + LinkMetricTestParameters testParameters = new LinkMetricTestParameters(); + testParameters.setInitialState(""" + G1, AUTO, NO_MATCH, P1 + G2, MANUAL, MATCH, P2 + """); + testParameters.setMatchFilters(Arrays.asList(MdmMatchResultEnum.MATCH)); + testParameters.setLinkSourceFilters(Arrays.asList(MdmLinkSourceEnum.AUTO)); + testParameters.setExpectedMetrics(new MdmMetrics()); + params.add(testParameters); + } + + // 7 + { + // initial state with filters to omit some values + LinkMetricTestParameters testParameters = new LinkMetricTestParameters(); + testParameters.setInitialState(""" + G1, AUTO, NO_MATCH, P1 + G2, MANUAL, MATCH, P2 + """); + testParameters.setMatchFilters(Arrays.asList(MdmMatchResultEnum.NO_MATCH)); + testParameters.setLinkSourceFilters(Arrays.asList(MdmLinkSourceEnum.AUTO)); + MdmMetrics metrics = new MdmMetrics(); + metrics.addMetric(MdmMatchResultEnum.NO_MATCH, MdmLinkSourceEnum.AUTO, 1); + testParameters.setExpectedMetrics(metrics); + params.add(testParameters); + } + + return params; + } + + /** + * Parameters supplied to {@link ca.uhn.fhir.jpa.mdm.IMdmMetricSvcTest#test_generateResourceMetrics_multipleInputs(ResourceMetricTestParams)} + */ + public static List resourceMetricParameters() { + List params = new ArrayList<>(); + + // 1 + { + // a mix of golden, regular, and blocked resources + ResourceMetricTestParams p = new ResourceMetricTestParams(); + p.setInitialState( + """ + G1, AUTO, MATCH, P1 + G2, AUTO, MATCH, P2 + G2, AUTO, MATCH, P1, + G3, AUTO, MATCH, P3 + """); + p.addBlockedResourceGoldenResources("G2"); + p.addBlockedResourceGoldenResources("G3"); + p.setExpectedResourceCount(6); + p.setExpectedGoldenResourceCount(3); + params.add(p); + } + + // 2 + { + // 2 non-golden, 1 golden + ResourceMetricTestParams p = new ResourceMetricTestParams(); + p.setInitialState(""" + G1, AUTO, MATCH, P1, + G1, MANUAL, MATCH, P2 + """); + p.setExpectedResourceCount(3); + p.setExpectedGoldenResourceCount(1); + params.add(p); + } + + // 3 + { + // 2 golden, 1 non-golden + ResourceMetricTestParams p = new ResourceMetricTestParams(); + p.setInitialState(""" + G1, AUTO, MATCH, P1 + G2, AUTO, POSSIBLE_DUPLICATE, G1 + """); + p.setExpectedGoldenResourceCount(2); + p.setExpectedResourceCount(3); + params.add(p); + } + + // 4 + { + // 2 golden, 1 blocked, 0 non-golden + ResourceMetricTestParams p = new ResourceMetricTestParams(); + p.setInitialState(""" + G1, AUTO, POSSIBLE_DUPLICATE, G2 + """); + p.addBlockedResourceGoldenResources("G1"); + p.setExpectedResourceCount(2); + p.setExpectedGoldenResourceCount(2); + params.add(p); + } + + // 5 + { + // no resources + ResourceMetricTestParams p = new ResourceMetricTestParams(); + p.setInitialState(""); + params.add(p); + } + + return params; + } + + /** + * Parameters supplied to {@link ca.uhn.fhir.jpa.mdm.IMdmMetricSvcTest#generateLinkScoreMetricsSetup(LinkScoreMetricTestParams)} + */ + public static List linkScoreParameters() { + List parameters = new ArrayList<>(); + + // 1 + { + // score counts + LinkScoreMetricTestParams p = new LinkScoreMetricTestParams(); + p.setInitialState( + """ + G1, AUTO, MATCH, P1 + G2, AUTO, POSSIBLE_MATCH, P2, + G3, AUTO, POSSIBLE_MATCH, P1 + """); + p.setScores(Arrays.asList(.2D, .2D, .1D)); + MdmMetrics metrics = new MdmMetrics(); + metrics.setResourceType("Patient"); + populateScoreIntoMetrics(p, metrics); + p.setExpectedMetrics(metrics); + parameters.add(p); + } + + // 2 + { + // a null score + LinkScoreMetricTestParams p = new LinkScoreMetricTestParams(); + p.setInitialState(""" + G1, AUTO, POSSIBLE_MATCH, P1, + G2, AUTO, POSSIBLE_MATCH, P2 + """); + p.setScores(Arrays.asList(null, 0.02D)); + MdmMetrics metrics = new MdmMetrics(); + metrics.setResourceType("Patient"); + populateScoreIntoMetrics(p, metrics); + p.setExpectedMetrics(metrics); + parameters.add(p); + } + + // 3 + { + // match type filtering + LinkScoreMetricTestParams p = new LinkScoreMetricTestParams(); + p.setInitialState( + """ + G1, AUTO, POSSIBLE_MATCH, P1 + G2, AUTO, MATCH, P2 + G3, AUTO, POSSIBLE_MATCH, P3 + G4, AUTO, MATCH, P4 + """); + p.setScores(Arrays.asList(0.4D, 0.4D, 0.1D, 0.3D)); + p.addMatchType(MdmMatchResultEnum.POSSIBLE_MATCH); + MdmMetrics metrics = new MdmMetrics(); + metrics.setResourceType("Patient"); + populateScoreIntoMetrics(p, metrics); + p.setExpectedMetrics(metrics); + parameters.add(p); + } + + // 4 + { + // no links + LinkScoreMetricTestParams p = new LinkScoreMetricTestParams(); + p.setInitialState(""); + MdmMetrics metrics = new MdmMetrics(); + metrics.setResourceType("Patient"); + p.setExpectedMetrics(metrics); + populateScoreIntoMetrics(p, metrics); + parameters.add(p); + } + + return parameters; + } + + private static void populateScoreIntoMetrics(LinkScoreMetricTestParams p, MdmMetrics metrics) { + String initialState = p.getInitialState(); + Map indexToMatchResult = new HashMap<>(); + if (isNotBlank(initialState)) { + String[] states = initialState.split("\n"); + int len = states.length; + for (int i = 0; i < len; i++) { + String state = states[i]; + String[] values = state.split(","); + indexToMatchResult.put(i, MdmMatchResultEnum.valueOf(values[2].trim())); + } + } + + Map score2Count = new HashMap<>(); + long nullCount = 0; + for (int i = 0; i < p.getScores().size(); i++) { + MdmMatchResultEnum matchResult = indexToMatchResult.get(i); + // if it's not a filtered value, add it to the expected metrics + if (p.getMatchFilter().isEmpty() || p.getMatchFilter().contains(matchResult)) { + Double d = p.getScores().get(i); + if (d == null) { + nullCount++; + } else { + if (!score2Count.containsKey(d)) { + score2Count.put(d, 0L); + } + score2Count.put(d, score2Count.get(d) + 1); + } + } + } + metrics.addScore(BaseMdmMetricSvc.NULL_VALUE, nullCount); + for (int i = 0; i < BaseMdmMetricSvc.BUCKETS; i++) { + double bucket = (double) Math.round((float) (100 * (i + 1)) / BaseMdmMetricSvc.BUCKETS) / 100; + long count = 0; + // TODO - do not add it if the corresponding link does not have + // the correct MATCH_RESULT value + if (score2Count.containsKey(bucket)) { + count = score2Count.get(bucket); + } + if (i == 0) { + metrics.addScore(String.format(BaseMdmMetricSvc.FIRST_BUCKET, bucket), count); + } else { + metrics.addScore( + String.format(BaseMdmMetricSvc.NTH_BUCKET, (float) i / BaseMdmMetricSvc.BUCKETS, bucket), + count); + } + } + } +} diff --git a/hapi-fhir-jpaserver-test-utilities/src/main/java/ca/uhn/fhir/jpa/packages/FakeNpmServlet.java b/hapi-fhir-jpaserver-test-utilities/src/main/java/ca/uhn/fhir/jpa/packages/FakeNpmServlet.java index 7a0e28df80c..dbc29a6d8bf 100644 --- a/hapi-fhir-jpaserver-test-utilities/src/main/java/ca/uhn/fhir/jpa/packages/FakeNpmServlet.java +++ b/hapi-fhir-jpaserver-test-utilities/src/main/java/ca/uhn/fhir/jpa/packages/FakeNpmServlet.java @@ -20,6 +20,9 @@ package ca.uhn.fhir.jpa.packages; import ca.uhn.fhir.rest.api.Constants; +import jakarta.servlet.http.HttpServlet; +import jakarta.servlet.http.HttpServletRequest; +import jakarta.servlet.http.HttpServletResponse; import org.apache.commons.lang3.StringUtils; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -27,9 +30,6 @@ import org.slf4j.LoggerFactory; import java.io.IOException; import java.util.HashMap; import java.util.Map; -import javax.servlet.http.HttpServlet; -import javax.servlet.http.HttpServletRequest; -import javax.servlet.http.HttpServletResponse; public class FakeNpmServlet extends HttpServlet { private static final Logger ourLog = LoggerFactory.getLogger(FakeNpmServlet.class); diff --git a/hapi-fhir-jpaserver-test-utilities/src/main/java/ca/uhn/fhir/jpa/provider/CodeSystemLookupWithPropertiesUtil.java b/hapi-fhir-jpaserver-test-utilities/src/main/java/ca/uhn/fhir/jpa/provider/CodeSystemLookupWithPropertiesUtil.java new file mode 100644 index 00000000000..d68fcee92ba --- /dev/null +++ b/hapi-fhir-jpaserver-test-utilities/src/main/java/ca/uhn/fhir/jpa/provider/CodeSystemLookupWithPropertiesUtil.java @@ -0,0 +1,53 @@ +/*- + * #%L + * HAPI FHIR JPA Server Test Utilities + * %% + * Copyright (C) 2014 - 2023 Smile CDR, Inc. + * %% + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * #L% + */ +package ca.uhn.fhir.jpa.provider; + +import org.junit.jupiter.params.provider.Arguments; + +import java.util.Collections; +import java.util.List; +import java.util.stream.Stream; + +import static org.junit.jupiter.params.provider.Arguments.arguments; + +public class CodeSystemLookupWithPropertiesUtil { + public static final String ourCodeSystemId = "CodeSystem-Example", + ourCodeSystemUrl = "http://example/" + ourCodeSystemId; + public static final String ourCode = "Code-WithProperties"; + public static final String ourPropertyA = "Property-A", ourPropertyB = "Property-B", ourPropertyC = "Property-C"; + public static final String ourPropertyValueA = "Value-A", ourPropertyValueB = "Value-B"; + public static final String propertyCodeSystem = "CodeSystem-C", + propertyCode = "Code-C", + propertyDisplay = "Display-C"; + + public static Stream parametersLookupWithProperties() { + return Stream.of( + arguments(Collections.emptyList(), List.of(ourPropertyA, ourPropertyB, ourPropertyC)), + arguments(List.of(ourPropertyB), List.of(ourPropertyB)), + arguments( + List.of(ourPropertyA, ourPropertyB, ourPropertyC), + List.of(ourPropertyA, ourPropertyB, ourPropertyC)), + arguments(List.of(ourPropertyB, ourPropertyA), List.of(ourPropertyB, ourPropertyA)), + arguments(List.of(ourPropertyA, ourPropertyA), List.of(ourPropertyA, ourPropertyA)), + arguments(List.of(ourPropertyB, "ABC"), List.of(ourPropertyB)), + arguments(List.of("ABC", ourPropertyA), List.of(ourPropertyA)), + arguments(List.of("ABC"), Collections.emptyList())); + } +} diff --git a/hapi-fhir-jpaserver-test-utilities/src/main/java/ca/uhn/fhir/jpa/provider/r4/BaseResourceProviderR4Test.java b/hapi-fhir-jpaserver-test-utilities/src/main/java/ca/uhn/fhir/jpa/provider/r4/BaseResourceProviderR4Test.java index 943fa6bbd81..7e4b86df570 100644 --- a/hapi-fhir-jpaserver-test-utilities/src/main/java/ca/uhn/fhir/jpa/provider/r4/BaseResourceProviderR4Test.java +++ b/hapi-fhir-jpaserver-test-utilities/src/main/java/ca/uhn/fhir/jpa/provider/r4/BaseResourceProviderR4Test.java @@ -49,9 +49,9 @@ import ca.uhn.fhir.test.utilities.JettyUtil; import org.apache.http.impl.client.CloseableHttpClient; import org.apache.http.impl.client.HttpClientBuilder; import org.apache.http.impl.conn.PoolingHttpClientConnectionManager; +import org.eclipse.jetty.ee10.servlet.ServletContextHandler; +import org.eclipse.jetty.ee10.servlet.ServletHolder; import org.eclipse.jetty.server.Server; -import org.eclipse.jetty.servlet.ServletContextHandler; -import org.eclipse.jetty.servlet.ServletHolder; import org.hl7.fhir.r4.model.Bundle; import org.hl7.fhir.r4.model.Bundle.BundleEntryComponent; import org.hl7.fhir.r4.model.Parameters; diff --git a/hapi-fhir-jpaserver-test-utilities/src/main/java/ca/uhn/fhir/jpa/search/IIdSearchTestTemplate.java b/hapi-fhir-jpaserver-test-utilities/src/main/java/ca/uhn/fhir/jpa/search/IIdSearchTestTemplate.java new file mode 100644 index 00000000000..1319b22b835 --- /dev/null +++ b/hapi-fhir-jpaserver-test-utilities/src/main/java/ca/uhn/fhir/jpa/search/IIdSearchTestTemplate.java @@ -0,0 +1,74 @@ +/*- + * #%L + * HAPI FHIR JPA Server Test Utilities + * %% + * Copyright (C) 2014 - 2023 Smile CDR, Inc. + * %% + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * #L% + */ +package ca.uhn.fhir.jpa.search; + +import ca.uhn.fhir.jpa.dao.TestDaoSearch; +import ca.uhn.fhir.test.utilities.ITestDataBuilder; +import org.hl7.fhir.instance.model.api.IIdType; +import org.junit.jupiter.api.Test; + +import java.util.List; + +public interface IIdSearchTestTemplate { + TestDaoSearch getSearch(); + + ITestDataBuilder getBuilder(); + + @Test + default void testSearchByServerAssignedId_findsResource() { + IIdType id = getBuilder().createPatient(); + + getSearch().assertSearchFinds("search by server assigned id", "Patient?_id=" + id.getIdPart(), id); + } + + @Test + default void testSearchByClientAssignedId_findsResource() { + ITestDataBuilder b = getBuilder(); + b.createPatient(b.withId("client-assigned-id")); + + getSearch() + .assertSearchFinds( + "search by client assigned id", "Patient?_id=client-assigned-id", "client-assigned-id"); + } + + /** + * The _id SP is defined as token, and there is no system. + * So sorting should be string order of the value. + */ + @Test + default void testSortById_treatsIdsAsString() { + ITestDataBuilder b = getBuilder(); + b.createPatient(b.withId("client-assigned-id")); + IIdType serverId = b.createPatient(); + b.createPatient(b.withId("0-sorts-before-other-numbers")); + + getSearch() + .assertSearchFindsInOrder( + "sort by resource id", + "Patient?_sort=_id", + List.of("0-sorts-before-other-numbers", serverId.getIdPart(), "client-assigned-id")); + + getSearch() + .assertSearchFindsInOrder( + "reverse sort by resource id", + "Patient?_sort=-_id", + List.of("client-assigned-id", serverId.getIdPart(), "0-sorts-before-other-numbers")); + } +} diff --git a/hapi-fhir-jpaserver-test-utilities/src/main/java/ca/uhn/fhir/jpa/subscription/NotificationServlet.java b/hapi-fhir-jpaserver-test-utilities/src/main/java/ca/uhn/fhir/jpa/subscription/NotificationServlet.java index 825c0edff25..780eab57d4d 100644 --- a/hapi-fhir-jpaserver-test-utilities/src/main/java/ca/uhn/fhir/jpa/subscription/NotificationServlet.java +++ b/hapi-fhir-jpaserver-test-utilities/src/main/java/ca/uhn/fhir/jpa/subscription/NotificationServlet.java @@ -19,15 +19,16 @@ */ package ca.uhn.fhir.jpa.subscription; +import jakarta.servlet.ServletException; +import jakarta.servlet.http.HttpServlet; +import jakarta.servlet.http.HttpServletRequest; +import jakarta.servlet.http.HttpServletResponse; + import java.io.IOException; import java.util.ArrayList; import java.util.Collections; import java.util.List; import java.util.concurrent.atomic.AtomicLong; -import javax.servlet.ServletException; -import javax.servlet.http.HttpServlet; -import javax.servlet.http.HttpServletRequest; -import javax.servlet.http.HttpServletResponse; /** * Receives subscription notification without payloads. diff --git a/hapi-fhir-jpaserver-test-utilities/src/main/java/ca/uhn/fhir/jpa/subscription/SocketImplementation.java b/hapi-fhir-jpaserver-test-utilities/src/main/java/ca/uhn/fhir/jpa/subscription/SocketImplementation.java index c4e67c55b92..2d52a950cd2 100644 --- a/hapi-fhir-jpaserver-test-utilities/src/main/java/ca/uhn/fhir/jpa/subscription/SocketImplementation.java +++ b/hapi-fhir-jpaserver-test-utilities/src/main/java/ca/uhn/fhir/jpa/subscription/SocketImplementation.java @@ -20,17 +20,17 @@ package ca.uhn.fhir.jpa.subscription; import ca.uhn.fhir.rest.api.EncodingEnum; -import org.eclipse.jetty.websocket.api.Session; -import org.eclipse.jetty.websocket.api.annotations.OnWebSocketConnect; -import org.eclipse.jetty.websocket.api.annotations.OnWebSocketMessage; -import org.eclipse.jetty.websocket.api.annotations.WebSocket; +import jakarta.websocket.ClientEndpoint; +import jakarta.websocket.OnMessage; +import jakarta.websocket.OnOpen; +import jakarta.websocket.Session; import org.slf4j.Logger; import java.util.ArrayList; import java.util.Collections; import java.util.List; -@WebSocket +@ClientEndpoint public class SocketImplementation { private static final Logger ourLog = org.slf4j.LoggerFactory.getLogger(SocketImplementation.class); @@ -53,7 +53,7 @@ public class SocketImplementation { public void keepAlive() { if (this.session != null) { try { - session.getRemote().sendString("keep alive"); + session.getBasicRemote().sendText("keep alive"); } catch (Throwable t) { ourLog.error("Failure", t); } @@ -66,14 +66,14 @@ public class SocketImplementation { * * @param session */ - @OnWebSocketConnect + @OnOpen public void onConnect(Session session) { ourLog.info("Got connect: {}", session); this.session = session; try { String sending = "bind " + myCriteria; ourLog.info("Sending: {}", sending); - session.getRemote().sendString(sending); + session.getBasicRemote().sendText(sending); ourLog.info("Connection: DONE"); } catch (Throwable t) { @@ -87,7 +87,7 @@ public class SocketImplementation { * * @param theMsg */ - @OnWebSocketMessage + @OnMessage public void onMessage(String theMsg) { ourLog.info("Got msg: " + theMsg); myMessages.add(theMsg); diff --git a/hapi-fhir-jpaserver-test-utilities/src/main/java/ca/uhn/fhir/jpa/svc/MockHapiTransactionService.java b/hapi-fhir-jpaserver-test-utilities/src/main/java/ca/uhn/fhir/jpa/svc/MockHapiTransactionService.java index 29c2eae81ea..1ad996ca623 100644 --- a/hapi-fhir-jpaserver-test-utilities/src/main/java/ca/uhn/fhir/jpa/svc/MockHapiTransactionService.java +++ b/hapi-fhir-jpaserver-test-utilities/src/main/java/ca/uhn/fhir/jpa/svc/MockHapiTransactionService.java @@ -20,12 +20,11 @@ package ca.uhn.fhir.jpa.svc; import ca.uhn.fhir.jpa.dao.tx.HapiTransactionService; +import jakarta.annotation.Nullable; import org.springframework.transaction.TransactionStatus; import org.springframework.transaction.support.SimpleTransactionStatus; import org.springframework.transaction.support.TransactionCallback; -import javax.annotation.Nullable; - public class MockHapiTransactionService extends HapiTransactionService { private TransactionStatus myTransactionStatus; @@ -40,7 +39,7 @@ public class MockHapiTransactionService extends HapiTransactionService { @Nullable @Override - protected T doExecute(ExecutionBuilder theExecutionBuilder, TransactionCallback theCallback) { + public T doExecute(ExecutionBuilder theExecutionBuilder, TransactionCallback theCallback) { return theCallback.doInTransaction(myTransactionStatus); } } diff --git a/hapi-fhir-jpaserver-test-utilities/src/main/java/ca/uhn/fhir/jpa/test/BaseJpaDstu3Test.java b/hapi-fhir-jpaserver-test-utilities/src/main/java/ca/uhn/fhir/jpa/test/BaseJpaDstu3Test.java index a699670da89..b43ccc2172e 100644 --- a/hapi-fhir-jpaserver-test-utilities/src/main/java/ca/uhn/fhir/jpa/test/BaseJpaDstu3Test.java +++ b/hapi-fhir-jpaserver-test-utilities/src/main/java/ca/uhn/fhir/jpa/test/BaseJpaDstu3Test.java @@ -129,7 +129,7 @@ import org.springframework.transaction.TransactionDefinition; import org.springframework.transaction.annotation.Transactional; import org.springframework.transaction.support.TransactionTemplate; -import javax.persistence.EntityManager; +import jakarta.persistence.EntityManager; import java.util.Map; @ExtendWith(SpringExtension.class) diff --git a/hapi-fhir-jpaserver-test-utilities/src/main/java/ca/uhn/fhir/jpa/test/BaseJpaR4Test.java b/hapi-fhir-jpaserver-test-utilities/src/main/java/ca/uhn/fhir/jpa/test/BaseJpaR4Test.java index 920285cf3ff..9515d61bbe7 100644 --- a/hapi-fhir-jpaserver-test-utilities/src/main/java/ca/uhn/fhir/jpa/test/BaseJpaR4Test.java +++ b/hapi-fhir-jpaserver-test-utilities/src/main/java/ca/uhn/fhir/jpa/test/BaseJpaR4Test.java @@ -20,6 +20,8 @@ package ca.uhn.fhir.jpa.test; import ca.uhn.fhir.batch2.jobs.export.BulkDataExportProvider; +import ca.uhn.fhir.batch2.jobs.reindex.ReindexAppCtx; +import ca.uhn.fhir.batch2.model.JobInstance; import ca.uhn.fhir.context.FhirContext; import ca.uhn.fhir.context.support.IValidationSupport; import ca.uhn.fhir.interceptor.api.IInterceptorService; @@ -38,7 +40,10 @@ import ca.uhn.fhir.jpa.api.svc.ISearchCoordinatorSvc; import ca.uhn.fhir.jpa.binary.interceptor.BinaryStorageInterceptor; import ca.uhn.fhir.jpa.binary.provider.BinaryAccessProvider; import ca.uhn.fhir.jpa.bulk.export.api.IBulkDataExportJobSchedulingHelper; +import ca.uhn.fhir.jpa.dao.GZipUtil; import ca.uhn.fhir.jpa.dao.IFulltextSearchSvc; +import ca.uhn.fhir.jpa.dao.data.IBatch2JobInstanceRepository; +import ca.uhn.fhir.jpa.dao.data.IBatch2WorkChunkRepository; import ca.uhn.fhir.jpa.dao.data.IForcedIdDao; import ca.uhn.fhir.jpa.dao.data.IMdmLinkJpaRepository; import ca.uhn.fhir.jpa.dao.data.IPartitionDao; @@ -78,6 +83,7 @@ import ca.uhn.fhir.jpa.entity.TermValueSet; import ca.uhn.fhir.jpa.entity.TermValueSetConcept; import ca.uhn.fhir.jpa.interceptor.PerformanceTracingLoggingInterceptor; import ca.uhn.fhir.jpa.model.config.PartitionSettings; +import ca.uhn.fhir.jpa.model.entity.ResourceHistoryTable; import ca.uhn.fhir.jpa.packages.IPackageInstallerSvc; import ca.uhn.fhir.jpa.partition.IPartitionLookupSvc; import ca.uhn.fhir.jpa.provider.JpaSystemProvider; @@ -98,6 +104,7 @@ import ca.uhn.fhir.jpa.test.config.TestR4Config; import ca.uhn.fhir.jpa.util.MemoryCacheService; import ca.uhn.fhir.jpa.util.ResourceCountCache; import ca.uhn.fhir.jpa.validation.ValidationSettings; +import ca.uhn.fhir.mdm.interceptor.MdmStorageInterceptor; import ca.uhn.fhir.parser.IParser; import ca.uhn.fhir.parser.StrictErrorHandler; import ca.uhn.fhir.rest.api.Constants; @@ -199,7 +206,7 @@ import org.springframework.test.context.junit.jupiter.SpringExtension; import org.springframework.test.util.AopTestUtils; import org.springframework.transaction.PlatformTransactionManager; -import javax.persistence.EntityManager; +import jakarta.persistence.EntityManager; import java.io.IOException; import java.util.ArrayList; import java.util.List; @@ -215,7 +222,9 @@ import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.fail; @ExtendWith(SpringExtension.class) -@ContextConfiguration(classes = {TestR4Config.class}) +@ContextConfiguration(classes = { + TestR4Config.class +}) public abstract class BaseJpaR4Test extends BaseJpaTest implements ITestDataBuilder { public static final String MY_VALUE_SET = "my-value-set"; public static final String URL_MY_VALUE_SET = "http://example.com/my_value_set"; @@ -238,6 +247,10 @@ public abstract class BaseJpaR4Test extends BaseJpaTest implements ITestDataBuil protected ITermCodeSystemStorageSvc myTermCodeSystemStorageSvc; @Autowired protected ISearchDao mySearchEntityDao; + @Autowired + private IBatch2JobInstanceRepository myJobInstanceRepository; + @Autowired + private IBatch2WorkChunkRepository myWorkChunkRepository; @Autowired protected ISearchIncludeDao mySearchIncludeEntityDao; @@ -398,6 +411,7 @@ public abstract class BaseJpaR4Test extends BaseJpaTest implements ITestDataBuil @Autowired @Qualifier("myOrganizationAffiliationDaoR4") protected IFhirResourceDao myOrganizationAffiliationDao; + @Autowired protected DatabaseBackedPagingProvider myPagingProvider; @Autowired @@ -425,8 +439,6 @@ public abstract class BaseJpaR4Test extends BaseJpaTest implements ITestDataBuil @Autowired protected IResourceHistoryProvenanceDao myResourceHistoryProvenanceDao; @Autowired - protected IForcedIdDao myForcedIdDao; - @Autowired @Qualifier("myCoverageDaoR4") protected IFhirResourceDao myCoverageDao; @Autowired @@ -532,6 +544,10 @@ public abstract class BaseJpaR4Test extends BaseJpaTest implements ITestDataBuil private IBulkDataExportJobSchedulingHelper myBulkDataScheduleHelper; @Autowired protected IResourceSearchUrlDao myResourceSearchUrlDao; + @Autowired + private IInterceptorService myInterceptorService; + @Autowired(required = false) + private MdmStorageInterceptor myMdmStorageInterceptor; @RegisterExtension private final PreventDanglingInterceptorsExtension myPreventDanglingInterceptorsExtension = new PreventDanglingInterceptorsExtension(()-> myInterceptorRegistry); @@ -593,11 +609,28 @@ public abstract class BaseJpaR4Test extends BaseJpaTest implements ITestDataBuil @AfterEach public void afterPurgeDatabase() { - runInTransaction(() -> { - myMdmLinkHistoryDao.deleteAll(); - myMdmLinkDao.deleteAll(); - }); - purgeDatabase(myStorageSettings, mySystemDao, myResourceReindexingSvc, mySearchCoordinatorSvc, mySearchParamRegistry, myBulkDataScheduleHelper); + boolean registeredStorageInterceptor = false; + if (myMdmStorageInterceptor != null && !myInterceptorService.getAllRegisteredInterceptors().contains(myMdmStorageInterceptor)) { + myInterceptorService.registerInterceptor(myMdmStorageInterceptor); + registeredStorageInterceptor = true; + } + try { + runInTransaction(() -> { + myMdmLinkHistoryDao.deleteAll(); + myMdmLinkDao.deleteAll(); + }); + purgeDatabase(myStorageSettings, mySystemDao, myResourceReindexingSvc, mySearchCoordinatorSvc, mySearchParamRegistry, myBulkDataScheduleHelper); + + myBatch2JobHelper.cancelAllJobsAndAwaitCancellation(); + runInTransaction(() -> { + myWorkChunkRepository.deleteAll(); + myJobInstanceRepository.deleteAll(); + }); + } finally { + if (registeredStorageInterceptor) { + myInterceptorService.unregisterInterceptor(myMdmStorageInterceptor); + } + } } @BeforeEach @@ -632,6 +665,14 @@ public abstract class BaseJpaR4Test extends BaseJpaTest implements ITestDataBuil return myTxManager; } + protected void relocateResourceTextToCompressedColumn(Long theResourcePid, Long theVersion) { + runInTransaction(()->{ + ResourceHistoryTable historyEntity = myResourceHistoryTableDao.findForIdAndVersionAndFetchProvenance(theResourcePid, theVersion); + byte[] contents = GZipUtil.compress(historyEntity.getResourceTextVc()); + myResourceHistoryTableDao.updateNonInlinedContents(contents, historyEntity.getId()); + }); + } + protected ValidationResult validateWithResult(IBaseResource theResource) { FhirValidator validatorModule = myFhirContext.newValidator(); FhirInstanceValidator instanceValidator = new FhirInstanceValidator(myValidationSupport); diff --git a/hapi-fhir-jpaserver-test-utilities/src/main/java/ca/uhn/fhir/jpa/test/BaseJpaTest.java b/hapi-fhir-jpaserver-test-utilities/src/main/java/ca/uhn/fhir/jpa/test/BaseJpaTest.java index dd44fea8b2c..ee74e496737 100644 --- a/hapi-fhir-jpaserver-test-utilities/src/main/java/ca/uhn/fhir/jpa/test/BaseJpaTest.java +++ b/hapi-fhir-jpaserver-test-utilities/src/main/java/ca/uhn/fhir/jpa/test/BaseJpaTest.java @@ -129,8 +129,8 @@ import org.springframework.transaction.TransactionStatus; import org.springframework.transaction.support.TransactionCallbackWithoutResult; import org.springframework.transaction.support.TransactionTemplate; -import javax.annotation.Nonnull; -import javax.persistence.EntityManager; +import jakarta.annotation.Nonnull; +import jakarta.persistence.EntityManager; import java.io.IOException; import java.time.Duration; import java.time.temporal.ChronoUnit; @@ -257,7 +257,7 @@ public abstract class BaseJpaTest extends BaseTest { @Autowired private IResourceHistoryTableDao myResourceHistoryTableDao; @Autowired - private IForcedIdDao myForcedIdDao; + protected IForcedIdDao myForcedIdDao; @Autowired private DaoRegistry myDaoRegistry; private final List myRegisteredInterceptors = new ArrayList<>(1); @@ -431,6 +431,11 @@ public abstract class BaseJpaTest extends BaseTest { return deliveryLatch; } + protected void registerInterceptor(Object theInterceptor) { + myRegisteredInterceptors.add(theInterceptor); + myInterceptorRegistry.registerInterceptor(theInterceptor); + } + protected void purgeHibernateSearch(EntityManager theEntityManager) { runInTransaction(() -> { if (myFulltestSearchSvc != null && !myFulltestSearchSvc.isDisabled()) { diff --git a/hapi-fhir-jpaserver-test-utilities/src/main/java/ca/uhn/fhir/jpa/test/BaseValueSetHSearchExpansionR4Test.java b/hapi-fhir-jpaserver-test-utilities/src/main/java/ca/uhn/fhir/jpa/test/BaseValueSetHSearchExpansionR4Test.java index bc8facbfec4..fb7b241ebc5 100644 --- a/hapi-fhir-jpaserver-test-utilities/src/main/java/ca/uhn/fhir/jpa/test/BaseValueSetHSearchExpansionR4Test.java +++ b/hapi-fhir-jpaserver-test-utilities/src/main/java/ca/uhn/fhir/jpa/test/BaseValueSetHSearchExpansionR4Test.java @@ -76,7 +76,7 @@ import org.springframework.test.util.ReflectionTestUtils; import org.springframework.transaction.PlatformTransactionManager; import org.springframework.transaction.annotation.Transactional; -import javax.persistence.EntityManager; +import jakarta.persistence.EntityManager; import java.util.ArrayList; import java.util.Arrays; import java.util.List; diff --git a/hapi-fhir-jpaserver-test-utilities/src/main/java/ca/uhn/fhir/jpa/test/PatientReindexTestHelper.java b/hapi-fhir-jpaserver-test-utilities/src/main/java/ca/uhn/fhir/jpa/test/PatientReindexTestHelper.java index 684b428a784..5f877ac815e 100644 --- a/hapi-fhir-jpaserver-test-utilities/src/main/java/ca/uhn/fhir/jpa/test/PatientReindexTestHelper.java +++ b/hapi-fhir-jpaserver-test-utilities/src/main/java/ca/uhn/fhir/jpa/test/PatientReindexTestHelper.java @@ -27,10 +27,10 @@ import ca.uhn.fhir.batch2.model.JobInstanceStartRequest; import ca.uhn.fhir.batch2.model.StatusEnum; import ca.uhn.fhir.jpa.api.dao.IFhirResourceDao; import ca.uhn.fhir.jpa.batch.models.Batch2JobStartResponse; -import ca.uhn.fhir.rest.api.server.SystemRequestDetails; import ca.uhn.fhir.jpa.searchparam.SearchParameterMap; import ca.uhn.fhir.jpa.util.TestUtil; import ca.uhn.fhir.rest.api.server.RequestDetails; +import ca.uhn.fhir.rest.api.server.SystemRequestDetails; import org.hl7.fhir.instance.model.api.IBaseResource; import org.hl7.fhir.r4.model.Patient; import org.junit.jupiter.params.provider.Arguments; @@ -52,6 +52,7 @@ public class PatientReindexTestHelper { private final Batch2JobHelper myBatch2JobHelper; private final IFhirResourceDao myPatientDao; private final boolean myIncrementVersionOnReindex; + private final RequestDetails myRequestDetails = new SystemRequestDetails(); public static Stream numResourcesParams(){ return Stream.of( @@ -79,7 +80,7 @@ public class PatientReindexTestHelper { // Reindex 1 JobInstanceStartRequest reindexRequest1 = createPatientReindexRequest(theNumResources); - Batch2JobStartResponse reindexResponse1 = myJobCoordinator.startInstance(reindexRequest1); + Batch2JobStartResponse reindexResponse1 = myJobCoordinator.startInstance(myRequestDetails, reindexRequest1); JobInstance instance1 = myBatch2JobHelper.awaitJobHasStatus(reindexResponse1.getInstanceId(), JOB_WAIT_TIME, StatusEnum.COMPLETED); validateReindexJob(instance1, theNumResources); @@ -95,7 +96,7 @@ public class PatientReindexTestHelper { // Reindex 1 JobInstanceStartRequest reindexRequest1 = createPatientReindexRequest(theNumResources); - Batch2JobStartResponse reindexResponse1 = myJobCoordinator.startInstance(reindexRequest1); + Batch2JobStartResponse reindexResponse1 = myJobCoordinator.startInstance(myRequestDetails, reindexRequest1); JobInstance instance1 = myBatch2JobHelper.awaitJobHasStatus(reindexResponse1.getInstanceId(), JOB_WAIT_TIME, StatusEnum.COMPLETED); validateReindexJob(instance1, theNumResources); @@ -104,7 +105,7 @@ public class PatientReindexTestHelper { // Reindex 2 JobInstanceStartRequest reindexRequest2 = createPatientReindexRequest(theNumResources); - Batch2JobStartResponse reindexResponse2 = myJobCoordinator.startInstance(reindexRequest2); + Batch2JobStartResponse reindexResponse2 = myJobCoordinator.startInstance(myRequestDetails, reindexRequest2); JobInstance instance2 = myBatch2JobHelper.awaitJobHasStatus(reindexResponse2.getInstanceId(), JOB_WAIT_TIME, StatusEnum.COMPLETED); validateReindexJob(instance2, theNumResources); @@ -119,11 +120,11 @@ public class PatientReindexTestHelper { // Reindex 1 JobInstanceStartRequest reindexRequest1 = createPatientReindexRequest(theNumResources); - Batch2JobStartResponse reindexResponse1 = myJobCoordinator.startInstance(reindexRequest1); + Batch2JobStartResponse reindexResponse1 = myJobCoordinator.startInstance(myRequestDetails, reindexRequest1); // Reindex 2 JobInstanceStartRequest reindexRequest2 = createPatientReindexRequest(theNumResources); - Batch2JobStartResponse reindexResponse2 = myJobCoordinator.startInstance(reindexRequest2); + Batch2JobStartResponse reindexResponse2 = myJobCoordinator.startInstance(myRequestDetails, reindexRequest2); // Wait for jobs to finish JobInstance instance1 = myBatch2JobHelper.awaitJobHasStatus(reindexResponse1.getInstanceId(), JOB_WAIT_TIME, StatusEnum.COMPLETED); @@ -170,7 +171,7 @@ public class PatientReindexTestHelper { startRequest.setJobDefinitionId(ReindexAppCtx.JOB_REINDEX); ReindexJobParameters reindexJobParameters = new ReindexJobParameters(); - reindexJobParameters.setBatchSize(theBatchSize); + reindexJobParameters.setBatchSize(Math.max(theBatchSize,1)); reindexJobParameters.addUrl("Patient?"); startRequest.setParameters(reindexJobParameters); diff --git a/hapi-fhir-jpaserver-test-utilities/src/main/java/ca/uhn/fhir/jpa/test/config/ConnectionWrapper.java b/hapi-fhir-jpaserver-test-utilities/src/main/java/ca/uhn/fhir/jpa/test/config/ConnectionWrapper.java index a628e4a854d..e211fd0f89d 100644 --- a/hapi-fhir-jpaserver-test-utilities/src/main/java/ca/uhn/fhir/jpa/test/config/ConnectionWrapper.java +++ b/hapi-fhir-jpaserver-test-utilities/src/main/java/ca/uhn/fhir/jpa/test/config/ConnectionWrapper.java @@ -38,13 +38,15 @@ import java.util.Map; import java.util.Properties; import java.util.concurrent.Executor; +@SuppressWarnings("SqlSourceToSinkFlow") public class ConnectionWrapper implements Connection { private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(ConnectionWrapper.class); - private Connection myWrap; + private final Connection myWrap; public ConnectionWrapper(Connection theConnection) { + ourLog.trace("new connection - {}", theConnection); myWrap = theConnection; } @@ -60,11 +62,13 @@ public class ConnectionWrapper implements Connection { @Override public void close() throws SQLException { + ourLog.trace("close connection - {}", myWrap); myWrap.close(); } @Override public void commit() throws SQLException { + if (ourLog.isTraceEnabled()) { ourLog.trace("commit: {}", myWrap.hashCode()); } myWrap.commit(); } diff --git a/hapi-fhir-jpaserver-test-utilities/src/main/java/ca/uhn/fhir/jpa/test/config/TestDstu2Config.java b/hapi-fhir-jpaserver-test-utilities/src/main/java/ca/uhn/fhir/jpa/test/config/TestDstu2Config.java index ad9e8ebf4ce..39ab80ce66f 100644 --- a/hapi-fhir-jpaserver-test-utilities/src/main/java/ca/uhn/fhir/jpa/test/config/TestDstu2Config.java +++ b/hapi-fhir-jpaserver-test-utilities/src/main/java/ca/uhn/fhir/jpa/test/config/TestDstu2Config.java @@ -21,6 +21,7 @@ package ca.uhn.fhir.jpa.test.config; import ca.uhn.fhir.batch2.jobs.config.Batch2JobsConfig; import ca.uhn.fhir.context.FhirContext; +import ca.uhn.fhir.jpa.api.config.JpaStorageSettings; import ca.uhn.fhir.jpa.batch2.JpaBatch2Config; import ca.uhn.fhir.jpa.config.HapiJpaConfig; import ca.uhn.fhir.jpa.config.JpaDstu2Config; @@ -57,6 +58,7 @@ import java.sql.SQLException; import java.util.Properties; import java.util.concurrent.TimeUnit; +import static ca.uhn.fhir.jpa.test.config.TestR5Config.SELECT_QUERY_INCLUSION_CRITERIA_EXCLUDING_SEQUENCE_QUERIES; import static org.junit.jupiter.api.Assertions.fail; @Configuration @@ -95,7 +97,8 @@ public class TestDstu2Config { @Bean public CircularQueueCaptureQueriesListener captureQueriesListener() { - return new CircularQueueCaptureQueriesListener(); + return new CircularQueueCaptureQueriesListener() + .setSelectQueryInclusionCriteria(SELECT_QUERY_INCLUSION_CRITERIA_EXCLUDING_SEQUENCE_QUERIES); } @Bean @@ -169,8 +172,8 @@ public class TestDstu2Config { } @Bean - public LocalContainerEntityManagerFactoryBean entityManagerFactory(ConfigurableListableBeanFactory theConfigurableListableBeanFactory, FhirContext theFhirContext) { - LocalContainerEntityManagerFactoryBean retVal = HapiEntityManagerFactoryUtil.newEntityManagerFactory(theConfigurableListableBeanFactory, theFhirContext); + public LocalContainerEntityManagerFactoryBean entityManagerFactory(ConfigurableListableBeanFactory theConfigurableListableBeanFactory, FhirContext theFhirContext, JpaStorageSettings theStorageSettings) { + LocalContainerEntityManagerFactoryBean retVal = HapiEntityManagerFactoryUtil.newEntityManagerFactory(theConfigurableListableBeanFactory, theFhirContext, theStorageSettings); retVal.setPersistenceUnitName("PU_HapiFhirJpaDstu2"); retVal.setDataSource(dataSource()); retVal.setJpaProperties(jpaProperties()); diff --git a/hapi-fhir-jpaserver-test-utilities/src/main/java/ca/uhn/fhir/jpa/test/config/TestDstu3Config.java b/hapi-fhir-jpaserver-test-utilities/src/main/java/ca/uhn/fhir/jpa/test/config/TestDstu3Config.java index 08515a33a31..521aa935c90 100644 --- a/hapi-fhir-jpaserver-test-utilities/src/main/java/ca/uhn/fhir/jpa/test/config/TestDstu3Config.java +++ b/hapi-fhir-jpaserver-test-utilities/src/main/java/ca/uhn/fhir/jpa/test/config/TestDstu3Config.java @@ -21,6 +21,7 @@ package ca.uhn.fhir.jpa.test.config; import ca.uhn.fhir.batch2.jobs.config.Batch2JobsConfig; import ca.uhn.fhir.context.FhirContext; +import ca.uhn.fhir.jpa.api.config.JpaStorageSettings; import ca.uhn.fhir.jpa.batch2.JpaBatch2Config; import ca.uhn.fhir.jpa.config.HapiJpaConfig; import ca.uhn.fhir.jpa.config.PackageLoaderConfig; @@ -61,6 +62,7 @@ import java.sql.Connection; import java.util.Properties; import java.util.concurrent.TimeUnit; +import static ca.uhn.fhir.jpa.test.config.TestR5Config.SELECT_QUERY_INCLUSION_CRITERIA_EXCLUDING_SEQUENCE_QUERIES; import static org.junit.jupiter.api.Assertions.fail; @Configuration @@ -82,7 +84,8 @@ public class TestDstu3Config { @Bean public CircularQueueCaptureQueriesListener captureQueriesListener() { - return new CircularQueueCaptureQueriesListener(); + return new CircularQueueCaptureQueriesListener() + .setSelectQueryInclusionCriteria(SELECT_QUERY_INCLUSION_CRITERIA_EXCLUDING_SEQUENCE_QUERIES); } @Bean @@ -180,8 +183,8 @@ public class TestDstu3Config { } @Bean - public LocalContainerEntityManagerFactoryBean entityManagerFactory(ConfigurableListableBeanFactory theConfigurableListableBeanFactory, FhirContext theFhirContext) { - LocalContainerEntityManagerFactoryBean retVal = HapiEntityManagerFactoryUtil.newEntityManagerFactory(theConfigurableListableBeanFactory, theFhirContext); + public LocalContainerEntityManagerFactoryBean entityManagerFactory(ConfigurableListableBeanFactory theConfigurableListableBeanFactory, FhirContext theFhirContext, JpaStorageSettings theStorageSettings) { + LocalContainerEntityManagerFactoryBean retVal = HapiEntityManagerFactoryUtil.newEntityManagerFactory(theConfigurableListableBeanFactory, theFhirContext, theStorageSettings); retVal.setPersistenceUnitName("PU_HapiFhirJpaDstu3"); retVal.setDataSource(dataSource()); retVal.setJpaProperties(jpaProperties()); diff --git a/hapi-fhir-jpaserver-test-utilities/src/main/java/ca/uhn/fhir/jpa/test/config/TestHSearchAddInConfig.java b/hapi-fhir-jpaserver-test-utilities/src/main/java/ca/uhn/fhir/jpa/test/config/TestHSearchAddInConfig.java index eefbc9e8139..532f8636507 100644 --- a/hapi-fhir-jpaserver-test-utilities/src/main/java/ca/uhn/fhir/jpa/test/config/TestHSearchAddInConfig.java +++ b/hapi-fhir-jpaserver-test-utilities/src/main/java/ca/uhn/fhir/jpa/test/config/TestHSearchAddInConfig.java @@ -44,7 +44,7 @@ import org.springframework.context.annotation.Import; import org.springframework.context.annotation.Primary; import org.testcontainers.elasticsearch.ElasticsearchContainer; -import javax.annotation.PreDestroy; +import jakarta.annotation.PreDestroy; import java.io.IOException; import java.nio.file.Files; import java.nio.file.Path; diff --git a/hapi-fhir-jpaserver-test-utilities/src/main/java/ca/uhn/fhir/jpa/test/config/TestHapiJpaConfig.java b/hapi-fhir-jpaserver-test-utilities/src/main/java/ca/uhn/fhir/jpa/test/config/TestHapiJpaConfig.java new file mode 100644 index 00000000000..10fb896beed --- /dev/null +++ b/hapi-fhir-jpaserver-test-utilities/src/main/java/ca/uhn/fhir/jpa/test/config/TestHapiJpaConfig.java @@ -0,0 +1,40 @@ +/*- + * #%L + * HAPI FHIR JPA Server Test Utilities + * %% + * Copyright (C) 2014 - 2023 Smile CDR, Inc. + * %% + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * #L% + */ +package ca.uhn.fhir.jpa.test.config; + +import ca.uhn.fhir.jpa.config.HapiJpaConfig; +import ca.uhn.fhir.jpa.search.DatabaseBackedPagingProvider; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; + +import static org.mockito.Mockito.spy; + +/** + * This is a Test configuration class that allows spying underlying JpaConfigs beans + */ +@Configuration +public class TestHapiJpaConfig extends HapiJpaConfig { + + @Override + @Bean + public DatabaseBackedPagingProvider databaseBackedPagingProvider() { + return spy(super.databaseBackedPagingProvider()); + } +} diff --git a/hapi-fhir-jpaserver-test-utilities/src/main/java/ca/uhn/fhir/jpa/test/config/TestJPAConfig.java b/hapi-fhir-jpaserver-test-utilities/src/main/java/ca/uhn/fhir/jpa/test/config/TestJPAConfig.java index 4c334819646..716ceccd079 100644 --- a/hapi-fhir-jpaserver-test-utilities/src/main/java/ca/uhn/fhir/jpa/test/config/TestJPAConfig.java +++ b/hapi-fhir-jpaserver-test-utilities/src/main/java/ca/uhn/fhir/jpa/test/config/TestJPAConfig.java @@ -47,7 +47,7 @@ import org.springframework.context.annotation.Lazy; import org.springframework.context.annotation.Primary; import org.springframework.orm.jpa.JpaTransactionManager; -import javax.persistence.EntityManagerFactory; +import jakarta.persistence.EntityManagerFactory; @Configuration @Import({ diff --git a/hapi-fhir-jpaserver-test-utilities/src/main/java/ca/uhn/fhir/jpa/test/config/TestR4BConfig.java b/hapi-fhir-jpaserver-test-utilities/src/main/java/ca/uhn/fhir/jpa/test/config/TestR4BConfig.java index e6473f15647..0cf3d61f1a0 100644 --- a/hapi-fhir-jpaserver-test-utilities/src/main/java/ca/uhn/fhir/jpa/test/config/TestR4BConfig.java +++ b/hapi-fhir-jpaserver-test-utilities/src/main/java/ca/uhn/fhir/jpa/test/config/TestR4BConfig.java @@ -21,6 +21,7 @@ package ca.uhn.fhir.jpa.test.config; import ca.uhn.fhir.batch2.jobs.config.Batch2JobsConfig; import ca.uhn.fhir.context.FhirContext; +import ca.uhn.fhir.jpa.api.config.JpaStorageSettings; import ca.uhn.fhir.jpa.batch2.JpaBatch2Config; import ca.uhn.fhir.jpa.binary.api.IBinaryStorageSvc; import ca.uhn.fhir.jpa.binstore.MemoryBinaryStorageSvcImpl; @@ -64,6 +65,7 @@ import java.util.LinkedList; import java.util.Properties; import java.util.concurrent.TimeUnit; +import static ca.uhn.fhir.jpa.test.config.TestR5Config.SELECT_QUERY_INCLUSION_CRITERIA_EXCLUDING_SEQUENCE_QUERIES; import static org.junit.jupiter.api.Assertions.fail; @Configuration @@ -106,7 +108,8 @@ public class TestR4BConfig { @Bean public CircularQueueCaptureQueriesListener captureQueriesListener() { - return new CircularQueueCaptureQueriesListener(); + return new CircularQueueCaptureQueriesListener() + .setSelectQueryInclusionCriteria(SELECT_QUERY_INCLUSION_CRITERIA_EXCLUDING_SEQUENCE_QUERIES); } @Bean @@ -212,8 +215,8 @@ public class TestR4BConfig { @Bean - public LocalContainerEntityManagerFactoryBean entityManagerFactory(ConfigurableListableBeanFactory theConfigurableListableBeanFactory, FhirContext theFhirContext) { - LocalContainerEntityManagerFactoryBean retVal = HapiEntityManagerFactoryUtil.newEntityManagerFactory(theConfigurableListableBeanFactory, theFhirContext); + public LocalContainerEntityManagerFactoryBean entityManagerFactory(ConfigurableListableBeanFactory theConfigurableListableBeanFactory, FhirContext theFhirContext, JpaStorageSettings theStorageSettings) { + LocalContainerEntityManagerFactoryBean retVal = HapiEntityManagerFactoryUtil.newEntityManagerFactory(theConfigurableListableBeanFactory, theFhirContext, theStorageSettings); retVal.setPersistenceUnitName("PU_HapiFhirJpaR4B"); retVal.setDataSource(dataSource()); retVal.setJpaProperties(jpaProperties()); diff --git a/hapi-fhir-jpaserver-test-utilities/src/main/java/ca/uhn/fhir/jpa/test/config/TestR4Config.java b/hapi-fhir-jpaserver-test-utilities/src/main/java/ca/uhn/fhir/jpa/test/config/TestR4Config.java index 98765d50c18..d61a4670331 100644 --- a/hapi-fhir-jpaserver-test-utilities/src/main/java/ca/uhn/fhir/jpa/test/config/TestR4Config.java +++ b/hapi-fhir-jpaserver-test-utilities/src/main/java/ca/uhn/fhir/jpa/test/config/TestR4Config.java @@ -21,10 +21,10 @@ package ca.uhn.fhir.jpa.test.config; import ca.uhn.fhir.batch2.jobs.config.Batch2JobsConfig; import ca.uhn.fhir.context.FhirContext; +import ca.uhn.fhir.jpa.api.config.JpaStorageSettings; import ca.uhn.fhir.jpa.batch2.JpaBatch2Config; import ca.uhn.fhir.jpa.binary.api.IBinaryStorageSvc; import ca.uhn.fhir.jpa.binstore.MemoryBinaryStorageSvcImpl; -import ca.uhn.fhir.jpa.config.HapiJpaConfig; import ca.uhn.fhir.jpa.config.PackageLoaderConfig; import ca.uhn.fhir.jpa.config.r4.JpaR4Config; import ca.uhn.fhir.jpa.config.util.HapiEntityManagerFactoryUtil; @@ -53,19 +53,27 @@ import java.lang.management.ManagementFactory; import java.lang.management.ThreadInfo; import java.lang.management.ThreadMXBean; import java.sql.Connection; +import java.sql.SQLException; +import java.util.ArrayList; +import java.util.Collections; import java.util.Deque; +import java.util.HashMap; import java.util.Iterator; +import java.util.LinkedHashMap; import java.util.LinkedList; +import java.util.Map; import java.util.Properties; import java.util.concurrent.TimeUnit; +import java.util.concurrent.atomic.AtomicInteger; +import static ca.uhn.fhir.jpa.test.config.TestR5Config.SELECT_QUERY_INCLUSION_CRITERIA_EXCLUDING_SEQUENCE_QUERIES; import static org.junit.jupiter.api.Assertions.fail; @Configuration @Import({ JpaR4Config.class, PackageLoaderConfig.class, - HapiJpaConfig.class, + TestHapiJpaConfig.class, TestJPAConfig.class, TestHSearchAddInConfig.DefaultLuceneHeap.class, JpaBatch2Config.class, @@ -76,6 +84,8 @@ public class TestR4Config { private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(TestR4Config.class); public static Integer ourMaxThreads; + private final AtomicInteger myBorrowedConnectionCount = new AtomicInteger(0); + private final AtomicInteger myReturnedConnectionCount = new AtomicInteger(0); static { /* @@ -96,14 +106,18 @@ public class TestR4Config { ourLog.warn("ourMaxThreads={}", ourMaxThreads); } - private final Deque myLastStackTrace = new LinkedList<>(); + private Map myConnectionRequestStackTraces = Collections.synchronizedMap(new LinkedHashMap<>()); + @Autowired TestHSearchAddInConfig.IHSearchConfigurer hibernateSearchConfigurer; private boolean myHaveDumpedThreads; + @Autowired + private JpaStorageSettings myStorageSettings; @Bean public CircularQueueCaptureQueriesListener captureQueriesListener() { - return new CircularQueueCaptureQueriesListener(); + return new CircularQueueCaptureQueriesListener() + .setSelectQueryInclusionCriteria(SELECT_QUERY_INCLUSION_CRITERIA_EXCLUDING_SEQUENCE_QUERIES); } @Bean @@ -117,6 +131,7 @@ public class TestR4Config { retVal = new ConnectionWrapper(super.getConnection()); } catch (Exception e) { ourLog.error("Exceeded maximum wait for connection (" + ourMaxThreads + " max)", e); + ourLog.info("Have {} outstanding - {} borrowed {} returned", (myBorrowedConnectionCount.get() - myReturnedConnectionCount.get()), myBorrowedConnectionCount.get(), myReturnedConnectionCount.get()); logGetConnectionStackTrace(); fail("Exceeded maximum wait for connection (" + ourMaxThreads + " max): " + e); retVal = null; @@ -125,38 +140,42 @@ public class TestR4Config { try { throw new Exception(); } catch (Exception e) { - synchronized (myLastStackTrace) { - myLastStackTrace.add(e); - while (myLastStackTrace.size() > ourMaxThreads) { - myLastStackTrace.removeFirst(); - } - } + myConnectionRequestStackTraces.put(retVal, e); } - return retVal; + myBorrowedConnectionCount.incrementAndGet(); + ConnectionWrapper finalRetVal = retVal; + return new ConnectionWrapper(finalRetVal){ + @Override + public void close() throws SQLException { + myConnectionRequestStackTraces.remove(finalRetVal); + myReturnedConnectionCount.incrementAndGet(); + super.close(); + } + }; } private void logGetConnectionStackTrace() { StringBuilder b = new StringBuilder(); - int i = 0; - synchronized (myLastStackTrace) { - for (Iterator iter = myLastStackTrace.descendingIterator(); iter.hasNext(); ) { - Exception nextStack = iter.next(); - b.append("\n\nPrevious request stack trace "); - b.append(i++); + ArrayList stackTraces = new ArrayList<>(myConnectionRequestStackTraces.values()); + + for (int i = 0; i < stackTraces.size(); i++) { + Exception nextStack = stackTraces.get(i); + b.append("\nPrevious request stack trace "); + b.append(i); + b.append(":"); + for (StackTraceElement next : nextStack.getStackTrace()) { + b.append("\n "); + b.append(next.getClassName()); + b.append("."); + b.append(next.getMethodName()); + b.append("("); + b.append(next.getFileName()); b.append(":"); - for (StackTraceElement next : nextStack.getStackTrace()) { - b.append("\n "); - b.append(next.getClassName()); - b.append("."); - b.append(next.getMethodName()); - b.append("("); - b.append(next.getFileName()); - b.append(":"); - b.append(next.getLineNumber()); - b.append(")"); - } + b.append(next.getLineNumber()); + b.append(")"); } + b.append("\n"); } ourLog.info(b.toString()); @@ -209,8 +228,8 @@ public class TestR4Config { @Bean - public LocalContainerEntityManagerFactoryBean entityManagerFactory(ConfigurableListableBeanFactory theConfigurableListableBeanFactory, FhirContext theFhirContext) { - LocalContainerEntityManagerFactoryBean retVal = HapiEntityManagerFactoryUtil.newEntityManagerFactory(theConfigurableListableBeanFactory, theFhirContext); + public LocalContainerEntityManagerFactoryBean entityManagerFactory(ConfigurableListableBeanFactory theConfigurableListableBeanFactory, FhirContext theFhirContext, JpaStorageSettings theStorageSettings) { + LocalContainerEntityManagerFactoryBean retVal = HapiEntityManagerFactoryUtil.newEntityManagerFactory(theConfigurableListableBeanFactory, theFhirContext, theStorageSettings); retVal.setPersistenceUnitName("PU_HapiFhirJpaR4"); retVal.setDataSource(dataSource()); retVal.setJpaProperties(jpaProperties()); diff --git a/hapi-fhir-jpaserver-test-utilities/src/main/java/ca/uhn/fhir/jpa/test/config/TestR5Config.java b/hapi-fhir-jpaserver-test-utilities/src/main/java/ca/uhn/fhir/jpa/test/config/TestR5Config.java index f4d46f5d342..828bd153d1c 100644 --- a/hapi-fhir-jpaserver-test-utilities/src/main/java/ca/uhn/fhir/jpa/test/config/TestR5Config.java +++ b/hapi-fhir-jpaserver-test-utilities/src/main/java/ca/uhn/fhir/jpa/test/config/TestR5Config.java @@ -21,6 +21,7 @@ package ca.uhn.fhir.jpa.test.config; import ca.uhn.fhir.batch2.jobs.config.Batch2JobsConfig; import ca.uhn.fhir.context.FhirContext; +import ca.uhn.fhir.jpa.api.config.JpaStorageSettings; import ca.uhn.fhir.jpa.batch2.JpaBatch2Config; import ca.uhn.fhir.jpa.binary.api.IBinaryStorageSvc; import ca.uhn.fhir.jpa.binstore.MemoryBinaryStorageSvcImpl; @@ -50,7 +51,9 @@ import org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean; import javax.sql.DataSource; import java.sql.Connection; +import java.util.Locale; import java.util.Properties; +import java.util.function.Predicate; import static org.junit.jupiter.api.Assertions.fail; @@ -68,6 +71,7 @@ import static org.junit.jupiter.api.Assertions.fail; public class TestR5Config { private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(TestR5Config.class); + public static final Predicate SELECT_QUERY_INCLUSION_CRITERIA_EXCLUDING_SEQUENCE_QUERIES = CircularQueueCaptureQueriesListener.DEFAULT_SELECT_INCLUSION_CRITERIA.and(t -> !t.toLowerCase(Locale.US).startsWith("select next value")); public static Integer ourMaxThreads; static { @@ -97,7 +101,8 @@ public class TestR5Config { @Bean public CircularQueueCaptureQueriesListener captureQueriesListener() { - return new CircularQueueCaptureQueriesListener(); + return new CircularQueueCaptureQueriesListener() + .setSelectQueryInclusionCriteria(SELECT_QUERY_INCLUSION_CRITERIA_EXCLUDING_SEQUENCE_QUERIES); } @Bean @@ -173,15 +178,15 @@ public class TestR5Config { } @Bean - public LocalContainerEntityManagerFactoryBean entityManagerFactory(ConfigurableListableBeanFactory theConfigurableListableBeanFactory, FhirContext theFhirContext) { - LocalContainerEntityManagerFactoryBean retVal = HapiEntityManagerFactoryUtil.newEntityManagerFactory(theConfigurableListableBeanFactory, theFhirContext); + public LocalContainerEntityManagerFactoryBean entityManagerFactory(ConfigurableListableBeanFactory theConfigurableListableBeanFactory, FhirContext theFhirContext, JpaStorageSettings theStorageSettings) { + LocalContainerEntityManagerFactoryBean retVal = HapiEntityManagerFactoryUtil.newEntityManagerFactory(theConfigurableListableBeanFactory, theFhirContext, theStorageSettings); retVal.setPersistenceUnitName("PU_HapiFhirJpaR5"); retVal.setDataSource(dataSource()); retVal.setJpaProperties(jpaProperties()); return retVal; } - private Properties jpaProperties() { + protected Properties jpaProperties() { Properties extraProperties = new Properties(); extraProperties.put("hibernate.format_sql", "false"); extraProperties.put("hibernate.show_sql", "false"); diff --git a/hapi-fhir-jpaserver-test-utilities/src/main/java/ca/uhn/fhir/jpa/util/ValueSetTestUtil.java b/hapi-fhir-jpaserver-test-utilities/src/main/java/ca/uhn/fhir/jpa/util/ValueSetTestUtil.java index c5d72f13825..809d9d39c56 100644 --- a/hapi-fhir-jpaserver-test-utilities/src/main/java/ca/uhn/fhir/jpa/util/ValueSetTestUtil.java +++ b/hapi-fhir-jpaserver-test-utilities/src/main/java/ca/uhn/fhir/jpa/util/ValueSetTestUtil.java @@ -21,13 +21,13 @@ package ca.uhn.fhir.jpa.util; import ca.uhn.fhir.context.FhirVersionEnum; import ca.uhn.hapi.converters.canonical.VersionCanonicalizer; +import jakarta.annotation.Nonnull; import org.hl7.fhir.instance.model.api.IBaseResource; import org.hl7.fhir.r4.model.Extension; import org.hl7.fhir.r4.model.ValueSet; import java.util.List; import java.util.stream.Collectors; -import javax.annotation.Nonnull; import static ca.uhn.fhir.util.HapiExtensions.EXT_VALUESET_EXPANSION_MESSAGE; import static org.junit.jupiter.api.Assertions.assertEquals; diff --git a/hapi-fhir-jpaserver-test-utilities/src/main/java/ca/uhn/fhir/jpa/util/WebsocketSubscriptionClient.java b/hapi-fhir-jpaserver-test-utilities/src/main/java/ca/uhn/fhir/jpa/util/WebsocketSubscriptionClient.java index 62480239d6a..7c8232108ca 100644 --- a/hapi-fhir-jpaserver-test-utilities/src/main/java/ca/uhn/fhir/jpa/util/WebsocketSubscriptionClient.java +++ b/hapi-fhir-jpaserver-test-utilities/src/main/java/ca/uhn/fhir/jpa/util/WebsocketSubscriptionClient.java @@ -24,9 +24,9 @@ import ca.uhn.fhir.jpa.subscription.SocketImplementation; import ca.uhn.fhir.rest.api.EncodingEnum; import ca.uhn.fhir.rest.server.exceptions.InternalErrorException; import ca.uhn.fhir.test.utilities.server.RestfulServerExtension; -import org.eclipse.jetty.websocket.api.Session; -import org.eclipse.jetty.websocket.client.ClientUpgradeRequest; -import org.eclipse.jetty.websocket.client.WebSocketClient; +import jakarta.websocket.ClientEndpoint; +import jakarta.websocket.ContainerProvider; +import jakarta.websocket.WebSocketContainer; import org.junit.jupiter.api.extension.AfterEachCallback; import org.junit.jupiter.api.extension.ExtensionContext; import org.slf4j.Logger; @@ -34,15 +34,14 @@ import org.slf4j.LoggerFactory; import java.net.URI; import java.util.List; -import java.util.concurrent.Future; -import java.util.concurrent.TimeUnit; import java.util.function.Supplier; +@ClientEndpoint public class WebsocketSubscriptionClient implements AfterEachCallback { private static final Logger ourLog = LoggerFactory.getLogger(WebsocketSubscriptionClient.class); private final Supplier myServerSupplier; private final Supplier myStorageSettings; - private WebSocketClient myWebSocketClient; + private jakarta.websocket.Session mySession; private SocketImplementation mySocketImplementation; /** @@ -64,20 +63,15 @@ public class WebsocketSubscriptionClient implements AfterEachCallback { RestfulServerExtension server = myServerSupplier.get(); assert server != null; - myWebSocketClient = new WebSocketClient(); mySocketImplementation = new SocketImplementation(theSubscriptionId, EncodingEnum.JSON); try { - myWebSocketClient.start(); URI echoUri = new URI("ws://localhost:" + server.getPort() + server.getWebsocketContextPath() + myStorageSettings.get().getWebsocketContextPath()); - ClientUpgradeRequest request = new ClientUpgradeRequest(); - ourLog.info("Connecting to : {}", echoUri); - Future connection; - connection = myWebSocketClient.connect(mySocketImplementation, echoUri, request); - Session session = connection.get(20, TimeUnit.SECONDS); - ourLog.info("Connected to WS: {}", session.isOpen()); + WebSocketContainer container = ContainerProvider.getWebSocketContainer(); + mySession = container.connectToServer(mySocketImplementation, echoUri); + ourLog.info("Connected to WS: {}", mySession.isOpen()); } catch (Exception e) { throw new InternalErrorException(e); } @@ -85,9 +79,9 @@ public class WebsocketSubscriptionClient implements AfterEachCallback { @Override public void afterEach(ExtensionContext theExtensionContext) throws Exception { - if (myWebSocketClient != null) { + if (mySession != null) { ourLog.info("Shutting down websocket client"); - myWebSocketClient.stop(); + mySession.close(); } } diff --git a/hapi-fhir-jpaserver-test-utilities/src/test/java/ca/uhn/fhir/jpa/auth/FhirQueryRuleImplTest.java b/hapi-fhir-jpaserver-test-utilities/src/test/java/ca/uhn/fhir/jpa/auth/FhirQueryRuleImplTest.java index 6b6d5bb746d..930b20367b9 100644 --- a/hapi-fhir-jpaserver-test-utilities/src/test/java/ca/uhn/fhir/jpa/auth/FhirQueryRuleImplTest.java +++ b/hapi-fhir-jpaserver-test-utilities/src/test/java/ca/uhn/fhir/jpa/auth/FhirQueryRuleImplTest.java @@ -25,7 +25,7 @@ import org.mockito.ArgumentMatchers; import org.mockito.Mock; import org.mockito.junit.jupiter.MockitoSettings; -import javax.annotation.Nullable; +import jakarta.annotation.Nullable; import java.util.HashSet; import static ca.uhn.fhir.rest.server.interceptor.auth.IAuthorizationSearchParamMatcher.MatchResult.buildMatched; diff --git a/hapi-fhir-jpaserver-test-utilities/src/test/java/ca/uhn/fhir/jpa/auth/TestRuleApplier.java b/hapi-fhir-jpaserver-test-utilities/src/test/java/ca/uhn/fhir/jpa/auth/TestRuleApplier.java index 20646fa3eab..abaaad4314e 100644 --- a/hapi-fhir-jpaserver-test-utilities/src/test/java/ca/uhn/fhir/jpa/auth/TestRuleApplier.java +++ b/hapi-fhir-jpaserver-test-utilities/src/test/java/ca/uhn/fhir/jpa/auth/TestRuleApplier.java @@ -12,8 +12,8 @@ import org.hl7.fhir.instance.model.api.IIdType; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import javax.annotation.Nonnull; -import javax.annotation.Nullable; +import jakarta.annotation.Nonnull; +import jakarta.annotation.Nullable; /** * Empty implementation to base a stub. diff --git a/hapi-fhir-jpaserver-test-utilities/src/test/java/ca/uhn/fhir/jpa/config/ConnectionWrapper.java b/hapi-fhir-jpaserver-test-utilities/src/test/java/ca/uhn/fhir/jpa/config/ConnectionWrapper.java index ce598bed25b..a6e222f9c2c 100644 --- a/hapi-fhir-jpaserver-test-utilities/src/test/java/ca/uhn/fhir/jpa/config/ConnectionWrapper.java +++ b/hapi-fhir-jpaserver-test-utilities/src/test/java/ca/uhn/fhir/jpa/config/ConnectionWrapper.java @@ -46,6 +46,7 @@ public class ConnectionWrapper implements Connection { @Override public void commit() throws SQLException { + if (ourLog.isTraceEnabled()) { ourLog.trace("Commit: {}", myWrap.hashCode()); } myWrap.commit(); } diff --git a/hapi-fhir-jpaserver-test-utilities/src/test/java/ca/uhn/fhir/jpa/config/DispatcherServletConfig.java b/hapi-fhir-jpaserver-test-utilities/src/test/java/ca/uhn/fhir/jpa/config/DispatcherServletConfig.java index 8fb525d7509..26ae8841f28 100644 --- a/hapi-fhir-jpaserver-test-utilities/src/test/java/ca/uhn/fhir/jpa/config/DispatcherServletConfig.java +++ b/hapi-fhir-jpaserver-test-utilities/src/test/java/ca/uhn/fhir/jpa/config/DispatcherServletConfig.java @@ -1,6 +1,6 @@ package ca.uhn.fhir.jpa.config; -import javax.el.ExpressionFactory; +import jakarta.el.ExpressionFactory; import org.springframework.context.annotation.Configuration; diff --git a/hapi-fhir-jpaserver-test-utilities/src/test/java/ca/uhn/fhir/jpa/config/HapiFhirHibernateJpaDialectTest.java b/hapi-fhir-jpaserver-test-utilities/src/test/java/ca/uhn/fhir/jpa/config/HapiFhirHibernateJpaDialectTest.java index 5b942fdd69b..7a283a495b1 100644 --- a/hapi-fhir-jpaserver-test-utilities/src/test/java/ca/uhn/fhir/jpa/config/HapiFhirHibernateJpaDialectTest.java +++ b/hapi-fhir-jpaserver-test-utilities/src/test/java/ca/uhn/fhir/jpa/config/HapiFhirHibernateJpaDialectTest.java @@ -1,8 +1,8 @@ package ca.uhn.fhir.jpa.config; import ca.uhn.fhir.i18n.HapiLocalizer; -import ca.uhn.fhir.jpa.model.entity.ForcedId; import ca.uhn.fhir.jpa.model.entity.ResourceSearchUrlEntity; +import ca.uhn.fhir.jpa.model.entity.ResourceTable; import ca.uhn.fhir.rest.server.exceptions.ResourceVersionConflictException; import org.hibernate.HibernateException; import org.hibernate.PersistentObjectException; @@ -13,7 +13,7 @@ import org.junit.jupiter.api.Test; import org.springframework.dao.DataAccessException; import org.springframework.dao.DataIntegrityViolationException; -import javax.persistence.PersistenceException; +import jakarta.persistence.PersistenceException; import java.sql.SQLException; import static org.hamcrest.MatcherAssert.assertThat; @@ -36,7 +36,7 @@ public class HapiFhirHibernateJpaDialectTest { assertThat(outcome.getMessage(), containsString("this is a message")); try { - mySvc.convertHibernateAccessException(new ConstraintViolationException("this is a message", new SQLException("reason"), ForcedId.IDX_FORCEDID_TYPE_FID)); + mySvc.convertHibernateAccessException(new ConstraintViolationException("this is a message", new SQLException("reason"), ResourceTable.IDX_RES_TYPE_FHIR_ID)); fail(); } catch (ResourceVersionConflictException e) { assertThat(e.getMessage(), containsString("The operation has failed with a client-assigned ID constraint failure")); @@ -57,7 +57,7 @@ public class HapiFhirHibernateJpaDialectTest { } outcome = mySvc.convertHibernateAccessException(new HibernateException("this is a message")); - assertThat(outcome.getMessage(), containsString("HibernateException: this is a message")); + assertThat(outcome.getMessage(), containsString("this is a message")); } @@ -67,7 +67,7 @@ public class HapiFhirHibernateJpaDialectTest { assertEquals("FOO", outcome.getMessage()); try { - PersistenceException exception = new PersistenceException("a message", new ConstraintViolationException("this is a message", new SQLException("reason"), ForcedId.IDX_FORCEDID_TYPE_FID)); + PersistenceException exception = new PersistenceException("a message", new ConstraintViolationException("this is a message", new SQLException("reason"), ResourceTable.IDX_RES_TYPE_FHIR_ID)); mySvc.translate(exception, "a message"); fail(); } catch (ResourceVersionConflictException e) { diff --git a/hapi-fhir-jpaserver-test-utilities/src/test/java/ca/uhn/fhir/jpa/dao/index/ResourceVersionSvcTest.java b/hapi-fhir-jpaserver-test-utilities/src/test/java/ca/uhn/fhir/jpa/dao/index/ResourceVersionSvcTest.java index 9c37bce5cf0..6d14d780291 100644 --- a/hapi-fhir-jpaserver-test-utilities/src/test/java/ca/uhn/fhir/jpa/dao/index/ResourceVersionSvcTest.java +++ b/hapi-fhir-jpaserver-test-utilities/src/test/java/ca/uhn/fhir/jpa/dao/index/ResourceVersionSvcTest.java @@ -9,6 +9,7 @@ import ca.uhn.fhir.jpa.dao.data.IResourceTableDao; import ca.uhn.fhir.jpa.model.config.PartitionSettings; import ca.uhn.fhir.jpa.model.dao.JpaPid; import ca.uhn.fhir.jpa.model.entity.ForcedId; +import ca.uhn.fhir.jpa.model.entity.ResourceTable; import ca.uhn.fhir.model.primitive.IdDt; import ca.uhn.fhir.rest.api.server.storage.IResourcePersistentId; import org.hl7.fhir.instance.model.api.IIdType; @@ -20,11 +21,11 @@ import org.mockito.Mock; import org.mockito.Mockito; import org.mockito.junit.jupiter.MockitoExtension; -import javax.persistence.TypedQuery; -import javax.persistence.criteria.CriteriaBuilder; -import javax.persistence.criteria.CriteriaQuery; -import javax.persistence.criteria.Path; -import javax.persistence.criteria.Root; +import jakarta.persistence.TypedQuery; +import jakarta.persistence.criteria.CriteriaBuilder; +import jakarta.persistence.criteria.CriteriaQuery; +import jakarta.persistence.criteria.Path; +import jakarta.persistence.criteria.Root; import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; @@ -33,6 +34,7 @@ import java.util.List; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertSame; +import static org.junit.jupiter.api.Assertions.fail; import static org.mockito.ArgumentMatchers.any; import static org.mockito.Mockito.when; @@ -73,8 +75,8 @@ public class ResourceVersionSvcTest { */ private void mock_resolveResourcePersistentIdsWithCache_toReturnNothing() { CriteriaBuilder cb = Mockito.mock(CriteriaBuilder.class); - CriteriaQuery criteriaQuery = Mockito.mock(CriteriaQuery.class); - Root from = Mockito.mock(Root.class); + CriteriaQuery criteriaQuery = Mockito.mock(CriteriaQuery.class); + Root from = Mockito.mock(Root.class); Path path = Mockito.mock(Path.class); TypedQuery queryMock = Mockito.mock(TypedQuery.class); diff --git a/hapi-fhir-jpaserver-test-utilities/src/test/java/ca/uhn/fhir/jpa/delete/DeleteConflictServiceTest.java b/hapi-fhir-jpaserver-test-utilities/src/test/java/ca/uhn/fhir/jpa/delete/DeleteConflictServiceTest.java index f9a70508c29..117deb50a97 100644 --- a/hapi-fhir-jpaserver-test-utilities/src/test/java/ca/uhn/fhir/jpa/delete/DeleteConflictServiceTest.java +++ b/hapi-fhir-jpaserver-test-utilities/src/test/java/ca/uhn/fhir/jpa/delete/DeleteConflictServiceTest.java @@ -50,6 +50,7 @@ public class DeleteConflictServiceTest { @Test public void noInterceptorTwoConflictsDoesntRetry() { ResourceTable entity = new ResourceTable(); + entity.setId(22L); DeleteConflictList deleteConflicts = new DeleteConflictList(); List list = new ArrayList<>(); diff --git a/hapi-fhir-jpaserver-test-utilities/src/test/java/ca/uhn/fhir/jpa/embedded/HapiSchemaMigrationTest.java b/hapi-fhir-jpaserver-test-utilities/src/test/java/ca/uhn/fhir/jpa/embedded/HapiSchemaMigrationTest.java index 2783ea3439e..e6b023dd6e0 100644 --- a/hapi-fhir-jpaserver-test-utilities/src/test/java/ca/uhn/fhir/jpa/embedded/HapiSchemaMigrationTest.java +++ b/hapi-fhir-jpaserver-test-utilities/src/test/java/ca/uhn/fhir/jpa/embedded/HapiSchemaMigrationTest.java @@ -8,15 +8,18 @@ import ca.uhn.fhir.jpa.migrate.SchemaMigrator; import ca.uhn.fhir.jpa.migrate.dao.HapiMigrationDao; import ca.uhn.fhir.jpa.migrate.tasks.HapiFhirJpaMigrationTasks; import ca.uhn.fhir.system.HapiSystemProperties; +import ca.uhn.fhir.test.utilities.docker.RequiresDocker; import ca.uhn.fhir.util.VersionEnum; import org.apache.commons.dbcp2.BasicDataSource; import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.condition.DisabledIf; import org.junit.jupiter.api.extension.RegisterExtension; import org.junit.jupiter.params.ParameterizedTest; import org.junit.jupiter.params.provider.ArgumentsSource; import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import org.testcontainers.junit.jupiter.Testcontainers; import javax.sql.DataSource; import java.sql.SQLException; @@ -30,6 +33,7 @@ import static org.junit.jupiter.api.Assertions.assertFalse; import static org.junit.jupiter.api.Assertions.assertTrue; +@RequiresDocker public class HapiSchemaMigrationTest { private static final Logger ourLog = LoggerFactory.getLogger(HapiSchemaMigrationTest.class); diff --git a/hapi-fhir-jpaserver-test-utilities/src/test/java/ca/uhn/fhir/jpa/migrate/taskdef/AddIndexTaskTest.java b/hapi-fhir-jpaserver-test-utilities/src/test/java/ca/uhn/fhir/jpa/migrate/taskdef/AddIndexTaskTest.java new file mode 100644 index 00000000000..9e7d5f1ce71 --- /dev/null +++ b/hapi-fhir-jpaserver-test-utilities/src/test/java/ca/uhn/fhir/jpa/migrate/taskdef/AddIndexTaskTest.java @@ -0,0 +1,70 @@ +package ca.uhn.fhir.jpa.migrate.taskdef; + +import ca.uhn.fhir.jpa.migrate.DriverTypeEnum; +import ca.uhn.test.util.LogbackCaptureTestExtension; +import ch.qos.logback.classic.Level; +import ch.qos.logback.classic.Logger; +import ch.qos.logback.classic.spi.ILoggingEvent; +import ch.qos.logback.classic.spi.LoggingEvent; +import oracle.jdbc.OracleDatabaseException; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import org.junit.jupiter.api.extension.RegisterExtension; +import org.mockito.Mock; +import org.mockito.junit.jupiter.MockitoExtension; +import org.springframework.jdbc.UncategorizedSQLException; +import org.springframework.transaction.TransactionException; +import org.springframework.transaction.support.TransactionTemplate; + +import javax.sql.DataSource; +import java.sql.SQLException; +import java.util.Collections; +import java.util.List; + +import static org.hamcrest.CoreMatchers.containsString; +import static org.hamcrest.MatcherAssert.assertThat; +import static org.hamcrest.Matchers.hasSize; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.Mockito.when; + +@ExtendWith(MockitoExtension.class) +class AddIndexTaskTest { + @Mock + DriverTypeEnum.ConnectionProperties myConnectionProperties; + @Mock + DataSource myDataSource; + @Mock + TransactionTemplate myTransactionTemplate; + + @RegisterExtension + LogbackCaptureTestExtension myLogCapture = new LogbackCaptureTestExtension((Logger) AddIndexTask.ourLog, Level.WARN); + + + @Test + void testOracleException() throws SQLException { + final AddIndexTask task = new AddIndexTask("1", "1"); + task.setColumns(Collections.singletonList("COLUMN_NAME")); + task.setUnique(true); + task.setIndexName("INDEX_NAME"); + task.setConnectionProperties(myConnectionProperties); + + when(myConnectionProperties.getDataSource()).thenReturn(myDataSource); + when(myConnectionProperties.getTxTemplate()).thenReturn(myTransactionTemplate); + + final String sql = "create index INDEX_NAME on TABLE_NAME (COLUMN_NAME)"; + when(myTransactionTemplate.execute(any())) + .thenReturn(Collections.emptySet()) + .thenThrow(new UncategorizedSQLException("ORA-01408: such column list already indexed", sql, new SQLException("ORA-01408: such column list already indexed", "72000", 1408))); + + myLogCapture.clearEvents(); + + // Red-green: this used to throw an exception. Now it logs a warning. + task.execute(); + + List events = myLogCapture.getLogEvents(); + assertThat(events, hasSize(1)); + LoggingEvent event = (LoggingEvent) events.get(0); + assertThat(event.getFormattedMessage(), containsString("ORA-01408: such column list already indexed")); + } +} diff --git a/hapi-fhir-jpaserver-test-utilities/src/test/java/ca/uhn/fhir/jpa/patch/FhirPatchCoreTest.java b/hapi-fhir-jpaserver-test-utilities/src/test/java/ca/uhn/fhir/jpa/patch/FhirPatchCoreTest.java index 1da0afb4e49..d9bffba1cc9 100644 --- a/hapi-fhir-jpaserver-test-utilities/src/test/java/ca/uhn/fhir/jpa/patch/FhirPatchCoreTest.java +++ b/hapi-fhir-jpaserver-test-utilities/src/test/java/ca/uhn/fhir/jpa/patch/FhirPatchCoreTest.java @@ -14,7 +14,7 @@ import org.w3c.dom.Element; import org.w3c.dom.NodeList; import org.xml.sax.SAXException; -import javax.annotation.Nonnull; +import jakarta.annotation.Nonnull; import javax.xml.transform.TransformerException; import java.io.IOException; import java.util.ArrayList; diff --git a/hapi-fhir-jpaserver-test-utilities/src/test/java/ca/uhn/fhir/jpa/sched/SchedulerServiceImplIT.java b/hapi-fhir-jpaserver-test-utilities/src/test/java/ca/uhn/fhir/jpa/sched/SchedulerServiceImplTest.java similarity index 97% rename from hapi-fhir-jpaserver-test-utilities/src/test/java/ca/uhn/fhir/jpa/sched/SchedulerServiceImplIT.java rename to hapi-fhir-jpaserver-test-utilities/src/test/java/ca/uhn/fhir/jpa/sched/SchedulerServiceImplTest.java index 7d88e567953..7fc5187295b 100644 --- a/hapi-fhir-jpaserver-test-utilities/src/test/java/ca/uhn/fhir/jpa/sched/SchedulerServiceImplIT.java +++ b/hapi-fhir-jpaserver-test-utilities/src/test/java/ca/uhn/fhir/jpa/sched/SchedulerServiceImplTest.java @@ -36,12 +36,12 @@ import static org.hamcrest.Matchers.greaterThanOrEqualTo; import static org.hamcrest.Matchers.lessThan; import static org.junit.jupiter.api.Assertions.fail; -@ContextConfiguration(classes = SchedulerServiceImplIT.TestConfiguration.class) +@ContextConfiguration(classes = SchedulerServiceImplTest.TestConfiguration.class) @ExtendWith(SpringExtension.class) @DirtiesContext -public class SchedulerServiceImplIT { +public class SchedulerServiceImplTest { - private static final Logger ourLog = LoggerFactory.getLogger(SchedulerServiceImplIT.class); + private static final Logger ourLog = LoggerFactory.getLogger(SchedulerServiceImplTest.class); public static final String SCHEDULED_JOB_ID = CountingJob.class.getName(); private static final AtomicInteger ourNameCounter = new AtomicInteger(); private static long ourTaskDelay; diff --git a/hapi-fhir-jpaserver-test-utilities/src/test/java/ca/uhn/fhir/jpa/search/builder/sql/BaseSearchQueryBuilderDialectTest.java b/hapi-fhir-jpaserver-test-utilities/src/test/java/ca/uhn/fhir/jpa/search/builder/sql/BaseSearchQueryBuilderDialectTest.java index 2546214bec9..26448d72544 100644 --- a/hapi-fhir-jpaserver-test-utilities/src/test/java/ca/uhn/fhir/jpa/search/builder/sql/BaseSearchQueryBuilderDialectTest.java +++ b/hapi-fhir-jpaserver-test-utilities/src/test/java/ca/uhn/fhir/jpa/search/builder/sql/BaseSearchQueryBuilderDialectTest.java @@ -17,7 +17,7 @@ import org.mockito.Mock; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import javax.annotation.Nonnull; +import jakarta.annotation.Nonnull; import static org.mockito.ArgumentMatchers.any; import static org.mockito.Mockito.when; diff --git a/hapi-fhir-jpaserver-test-utilities/src/test/java/ca/uhn/fhir/jpa/search/builder/sql/SearchQueryBuilderDialectMySqlTest.java b/hapi-fhir-jpaserver-test-utilities/src/test/java/ca/uhn/fhir/jpa/search/builder/sql/SearchQueryBuilderDialectMySqlTest.java index af0b511a83e..4f4a9afecaf 100644 --- a/hapi-fhir-jpaserver-test-utilities/src/test/java/ca/uhn/fhir/jpa/search/builder/sql/SearchQueryBuilderDialectMySqlTest.java +++ b/hapi-fhir-jpaserver-test-utilities/src/test/java/ca/uhn/fhir/jpa/search/builder/sql/SearchQueryBuilderDialectMySqlTest.java @@ -1,5 +1,6 @@ package ca.uhn.fhir.jpa.search.builder.sql; +import ca.uhn.fhir.jpa.model.dialect.HapiFhirMySQLDialect; import ca.uhn.fhir.jpa.search.builder.predicate.BaseJoiningPredicateBuilder; import ca.uhn.fhir.jpa.search.builder.predicate.DatePredicateBuilder; import ca.uhn.fhir.jpa.search.builder.predicate.ResourceTablePredicateBuilder; @@ -11,7 +12,7 @@ import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.ExtendWith; import org.mockito.junit.jupiter.MockitoExtension; -import javax.annotation.Nonnull; +import jakarta.annotation.Nonnull; import static org.junit.jupiter.api.Assertions.assertTrue; import static org.mockito.ArgumentMatchers.any; @@ -147,6 +148,6 @@ public class SearchQueryBuilderDialectMySqlTest extends BaseSearchQueryBuilderDi @Nonnull @Override protected Dialect createDialect() { - return new org.hibernate.dialect.MySQL57Dialect(); + return new HapiFhirMySQLDialect(); } } diff --git a/hapi-fhir-jpaserver-test-utilities/src/test/java/ca/uhn/fhir/jpa/search/builder/sql/SearchQueryBuilderDialectPostgresTest.java b/hapi-fhir-jpaserver-test-utilities/src/test/java/ca/uhn/fhir/jpa/search/builder/sql/SearchQueryBuilderDialectPostgresTest.java index 07eca1057f3..37aa38fdb42 100644 --- a/hapi-fhir-jpaserver-test-utilities/src/test/java/ca/uhn/fhir/jpa/search/builder/sql/SearchQueryBuilderDialectPostgresTest.java +++ b/hapi-fhir-jpaserver-test-utilities/src/test/java/ca/uhn/fhir/jpa/search/builder/sql/SearchQueryBuilderDialectPostgresTest.java @@ -2,17 +2,18 @@ package ca.uhn.fhir.jpa.search.builder.sql; import ca.uhn.fhir.jpa.api.config.JpaStorageSettings; import ca.uhn.fhir.jpa.dao.predicate.SearchFilterParser; +import ca.uhn.fhir.jpa.model.dialect.HapiFhirPostgresDialect; import ca.uhn.fhir.jpa.search.builder.predicate.DatePredicateBuilder; import ca.uhn.fhir.rest.param.DateParam; import com.healthmarketscience.sqlbuilder.Condition; import org.apache.commons.lang3.StringUtils; import org.hibernate.dialect.Dialect; -import org.hibernate.dialect.PostgreSQL10Dialect; +import org.hibernate.dialect.PostgreSQLDialect; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.ExtendWith; import org.mockito.junit.jupiter.MockitoExtension; -import javax.annotation.Nonnull; +import jakarta.annotation.Nonnull; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.mockito.ArgumentMatchers.any; @@ -45,7 +46,7 @@ public class SearchQueryBuilderDialectPostgresTest extends BaseSearchQueryBuilde logSql(generatedSql); String sql = generatedSql.getSql(); - assertEquals("SELECT t0.RES_ID FROM HFJ_SPIDX_DATE t0 WHERE ((t0.HASH_IDENTITY = ?) AND ((t0.SP_VALUE_LOW_DATE_ORDINAL >= ?) AND (t0.SP_VALUE_HIGH_DATE_ORDINAL <= ?))) limit ?", sql); + assertEquals("SELECT t0.RES_ID FROM HFJ_SPIDX_DATE t0 WHERE ((t0.HASH_IDENTITY = ?) AND ((t0.SP_VALUE_LOW_DATE_ORDINAL >= ?) AND (t0.SP_VALUE_HIGH_DATE_ORDINAL <= ?))) fetch first ? rows only", sql); assertEquals(4, StringUtils.countMatches(sql, "?")); assertEquals(4, generatedSql.getBindVariables().size()); @@ -58,6 +59,6 @@ public class SearchQueryBuilderDialectPostgresTest extends BaseSearchQueryBuilde @Nonnull @Override protected Dialect createDialect() { - return new PostgreSQL10Dialect(); + return new HapiFhirPostgresDialect(); } } diff --git a/hapi-fhir-jpaserver-test-utilities/src/test/java/ca/uhn/fhir/jpa/search/builder/sql/SearchQueryBuilderDialectSqlServerTest.java b/hapi-fhir-jpaserver-test-utilities/src/test/java/ca/uhn/fhir/jpa/search/builder/sql/SearchQueryBuilderDialectSqlServerTest.java index 2a6c042227b..afb1805d51c 100644 --- a/hapi-fhir-jpaserver-test-utilities/src/test/java/ca/uhn/fhir/jpa/search/builder/sql/SearchQueryBuilderDialectSqlServerTest.java +++ b/hapi-fhir-jpaserver-test-utilities/src/test/java/ca/uhn/fhir/jpa/search/builder/sql/SearchQueryBuilderDialectSqlServerTest.java @@ -1,5 +1,6 @@ package ca.uhn.fhir.jpa.search.builder.sql; +import ca.uhn.fhir.jpa.model.dialect.HapiFhirSQLServerDialect; import ca.uhn.fhir.jpa.search.builder.predicate.ResourceTablePredicateBuilder; import org.apache.commons.lang3.StringUtils; import org.hibernate.dialect.Dialect; @@ -8,7 +9,7 @@ import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.ExtendWith; import org.mockito.junit.jupiter.MockitoExtension; -import javax.annotation.Nonnull; +import jakarta.annotation.Nonnull; import java.util.Locale; import static org.junit.jupiter.api.Assertions.assertEquals; @@ -25,12 +26,19 @@ public class SearchQueryBuilderDialectSqlServerTest extends BaseSearchQueryBuild logSql(generatedSql); String sql = generatedSql.getSql(); - assertTrue(sql.endsWith("ORDER BY -t1.SP_VALUE_LOW DESC offset 0 rows fetch next ? rows only"), sql); + sql = massageSql(sql); + assertTrue(sql.endsWith("ORDER BY -t1.SP_VALUE_LOW DESC offset 0 rows fetch first ? rows only"), sql); assertEquals(3, StringUtils.countMatches(sql, "?")); assertEquals(3, generatedSql.getBindVariables().size()); } + @Nonnull + private static String massageSql(String sql) { + sql = sql.replace("\n", " ").replaceAll(" +", " "); + return sql; + } + @Test public void testRangeWithOffset() { SearchQueryBuilder searchQueryBuilder = createSearchQueryBuilder(); @@ -40,7 +48,8 @@ public class SearchQueryBuilderDialectSqlServerTest extends BaseSearchQueryBuild logSql(generatedSql); String sql = generatedSql.getSql(); - assertTrue(sql.endsWith("select page0_ from query where __row__ >= ? and __row__ < ?"), sql); + sql = massageSql(sql); + assertTrue(sql.endsWith("order by @@version offset ? rows fetch next ? rows only"), sql); assertEquals(3, StringUtils.countMatches(sql, "?")); assertEquals(3, generatedSql.getBindVariables().size()); @@ -55,7 +64,8 @@ public class SearchQueryBuilderDialectSqlServerTest extends BaseSearchQueryBuild logSql(generatedSql); String sql = generatedSql.getSql(); - assertTrue(sql.toUpperCase(Locale.ROOT).contains("SELECT TOP(?) T0.RES_ID FROM"), sql); + sql = massageSql(sql); + assertTrue(sql.endsWith("order by @@version offset 0 rows fetch first ? rows only"), sql); assertEquals(2, StringUtils.countMatches(sql, "?")); assertEquals(2, generatedSql.getBindVariables().size()); @@ -64,6 +74,6 @@ public class SearchQueryBuilderDialectSqlServerTest extends BaseSearchQueryBuild @Nonnull @Override protected Dialect createDialect() { - return new SQLServer2012Dialect(); + return new HapiFhirSQLServerDialect(); } } diff --git a/hapi-fhir-jpaserver-test-utilities/src/test/java/ca/uhn/fhir/jpa/search/builder/sql/SearchQueryBuilderTest.java b/hapi-fhir-jpaserver-test-utilities/src/test/java/ca/uhn/fhir/jpa/search/builder/sql/SearchQueryBuilderTest.java index 8821649c729..c0c1f0a8209 100644 --- a/hapi-fhir-jpaserver-test-utilities/src/test/java/ca/uhn/fhir/jpa/search/builder/sql/SearchQueryBuilderTest.java +++ b/hapi-fhir-jpaserver-test-utilities/src/test/java/ca/uhn/fhir/jpa/search/builder/sql/SearchQueryBuilderTest.java @@ -4,16 +4,16 @@ import ca.uhn.fhir.context.FhirContext; import ca.uhn.fhir.interceptor.model.RequestPartitionId; import ca.uhn.fhir.jpa.config.HibernatePropertiesProvider; import ca.uhn.fhir.jpa.model.config.PartitionSettings; +import ca.uhn.fhir.jpa.model.dialect.HapiFhirMariaDBDialect; +import ca.uhn.fhir.jpa.model.dialect.HapiFhirOracleDialect; import ca.uhn.fhir.jpa.model.entity.StorageSettings; import ca.uhn.fhir.jpa.search.builder.predicate.ResourceTablePredicateBuilder; import com.google.common.collect.Lists; -import org.hibernate.dialect.DerbyTenSevenDialect; -import org.hibernate.dialect.MariaDB103Dialect; +import org.hibernate.dialect.DerbyDialect; import org.hibernate.dialect.MySQL8Dialect; -import org.hibernate.dialect.Oracle12cDialect; -import org.hibernate.dialect.PostgreSQL95Dialect; -import org.hibernate.dialect.SQLServer2005Dialect; +import org.hibernate.dialect.PostgreSQLDialect; import org.hibernate.dialect.SQLServer2012Dialect; +import org.hibernate.dialect.SQLServerDialect; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.ExtendWith; @@ -54,7 +54,7 @@ public class SearchQueryBuilderTest { public void testRangeSqlServer2005_NoSort() { HibernatePropertiesProvider dialectProvider = new HibernatePropertiesProvider(); - dialectProvider.setDialectForUnitTest(new SQLServer2005Dialect()); + dialectProvider.setDialectForUnitTest(new SQLServerDialect()); SearchQueryBuilder builder = new SearchQueryBuilder(myFhirContext, myStorageSettings, myPartitionSettings, myRequestPartitionId, "Patient", mySqlBuilderFactory, dialectProvider, false); builder.addResourceIdsPredicate(Lists.newArrayList(500L, 501L)); GeneratedSql generated; @@ -66,13 +66,13 @@ public class SearchQueryBuilderTest { // Max only generated = builder.generate(null, 10); - assertEquals("SELECT TOP(?) T0.RES_ID FROM HFJ_RESOURCE T0 WHERE (((T0.RES_TYPE = ?) AND (T0.RES_DELETED_AT IS NULL)) AND (T0.RES_ID IN (?,?) ))", generated.getSql().toUpperCase(Locale.ROOT)); - assertThat(generated.getBindVariables().toString(), generated.getBindVariables(), contains(10, "Patient", 500L, 501L)); + assertEquals("SELECT T0.RES_ID FROM HFJ_RESOURCE T0 WHERE (((T0.RES_TYPE = ?) AND (T0.RES_DELETED_AT IS NULL)) AND (T0.RES_ID IN (?,?) )) ORDER BY @@VERSION OFFSET 0 ROWS FETCH FIRST ? ROWS ONLY", generated.getSql().toUpperCase(Locale.ROOT)); + assertThat(generated.getBindVariables().toString(), generated.getBindVariables(), contains("Patient", 500L, 501L, 10)); // Range generated = builder.generate(10, 5); - assertEquals("with query as (select inner_query.*, row_number() over (order by current_timestamp) as __row__ from ( SELECT t0.RES_ID as page0_ FROM HFJ_RESOURCE t0 WHERE (((t0.RES_TYPE = ?) AND (t0.RES_DELETED_AT IS NULL)) AND (t0.RES_ID IN (?,?) )) ) inner_query ) select page0_ from query where __row__ >= ? and __row__ < ?", generated.getSql()); - assertThat(generated.getBindVariables().toString(), generated.getBindVariables(), contains("Patient", 500L, 501L, 11, 16)); + assertEquals("SELECT t0.RES_ID FROM HFJ_RESOURCE t0 WHERE (((t0.RES_TYPE = ?) AND (t0.RES_DELETED_AT IS NULL)) AND (t0.RES_ID IN (?,?) )) order by @@version offset ? rows fetch next ? rows only", generated.getSql()); + assertThat(generated.getBindVariables().toString(), generated.getBindVariables(), contains("Patient", 500L, 501L, 10, 5)); } @@ -80,7 +80,7 @@ public class SearchQueryBuilderTest { public void testRangeSqlServer2005_WithSort() { HibernatePropertiesProvider dialectProvider = new HibernatePropertiesProvider(); - dialectProvider.setDialectForUnitTest(new SQLServer2005Dialect()); + dialectProvider.setDialectForUnitTest(new SQLServerDialect()); SearchQueryBuilder builder = new SearchQueryBuilder(myFhirContext, myStorageSettings, myPartitionSettings, myRequestPartitionId, "Patient", mySqlBuilderFactory, dialectProvider, false); builder.addResourceIdsPredicate(Lists.newArrayList(500L, 501L)); builder.addSortDate(builder.getOrCreateResourceTablePredicateBuilder().getColumnLastUpdated(), true); @@ -88,21 +88,18 @@ public class SearchQueryBuilderTest { // No range generated = builder.generate(null, null); -// assertEquals("SELECT t0.RES_ID FROM HFJ_RESOURCE t0 WHERE (((t0.RES_TYPE = ?) AND (t0.RES_DELETED_AT IS NULL)) AND (t0.RES_ID IN (?,?) )) ORDER BY CASE WHEN t0.RES_UPDATED IS NULL THEN 1 ELSE 0 END ASC, t0.RES_UPDATED ASC", generated.getSql()); assertEquals("SELECT t0.RES_ID FROM HFJ_RESOURCE t0 WHERE (((t0.RES_TYPE = ?) AND (t0.RES_DELETED_AT IS NULL)) AND (t0.RES_ID IN (?,?) )) ORDER BY t0.RES_UPDATED ASC", generated.getSql()); assertThat(generated.getBindVariables().toString(), generated.getBindVariables(), contains("Patient", 500L, 501L)); // Max only generated = builder.generate(null, 10); -// assertEquals("SELECT TOP(?) t0.RES_ID FROM HFJ_RESOURCE t0 WHERE (((t0.RES_TYPE = ?) AND (t0.RES_DELETED_AT IS NULL)) AND (t0.RES_ID IN (?,?) )) ORDER BY CASE WHEN t0.RES_UPDATED IS NULL THEN 1 ELSE 0 END ASC, t0.RES_UPDATED ASC", generated.getSql()); - assertEquals("SELECT TOP(?) T0.RES_ID FROM HFJ_RESOURCE T0 WHERE (((T0.RES_TYPE = ?) AND (T0.RES_DELETED_AT IS NULL)) AND (T0.RES_ID IN (?,?) )) ORDER BY T0.RES_UPDATED ASC", generated.getSql().toUpperCase(Locale.ROOT)); - assertThat(generated.getBindVariables().toString(), generated.getBindVariables(), contains(10, "Patient", 500L, 501L)); + assertEquals("SELECT T0.RES_ID FROM HFJ_RESOURCE T0 WHERE (((T0.RES_TYPE = ?) AND (T0.RES_DELETED_AT IS NULL)) AND (T0.RES_ID IN (?,?) )) ORDER BY T0.RES_UPDATED ASC OFFSET 0 ROWS FETCH FIRST ? ROWS ONLY", generated.getSql().toUpperCase(Locale.ROOT)); + assertThat(generated.getBindVariables().toString(), generated.getBindVariables(), contains("Patient", 500L, 501L, 10)); // Range generated = builder.generate(10, 5); -// assertEquals("WITH query AS (SELECT inner_query.*, ROW_NUMBER() OVER (ORDER BY CURRENT_TIMESTAMP) as __hibernate_row_nr__ FROM ( SELECT TOP(?) t0.RES_ID as page0_ FROM HFJ_RESOURCE t0 WHERE (((t0.RES_TYPE = ?) AND (t0.RES_DELETED_AT IS NULL)) AND (t0.RES_ID IN (?,?) )) ORDER BY CASE WHEN t0.RES_UPDATED IS NULL THEN 1 ELSE 0 END ASC, t0.RES_UPDATED ASC ) inner_query ) SELECT page0_ FROM query WHERE __hibernate_row_nr__ >= ? AND __hibernate_row_nr__ < ?", generated.getSql()); - assertEquals("with query as (select inner_query.*, row_number() over (order by current_timestamp) as __row__ from ( SELECT top(?) t0.RES_ID as page0_ FROM HFJ_RESOURCE t0 WHERE (((t0.RES_TYPE = ?) AND (t0.RES_DELETED_AT IS NULL)) AND (t0.RES_ID IN (?,?) )) ORDER BY t0.RES_UPDATED ASC ) inner_query ) select page0_ from query where __row__ >= ? and __row__ < ?", generated.getSql()); - assertThat(generated.getBindVariables().toString(), generated.getBindVariables(), contains(5, "Patient", 500L, 501L, 11, 16)); + assertEquals("SELECT t0.RES_ID FROM HFJ_RESOURCE t0 WHERE (((t0.RES_TYPE = ?) AND (t0.RES_DELETED_AT IS NULL)) AND (t0.RES_ID IN (?,?) )) ORDER BY t0.RES_UPDATED ASC offset ? rows fetch next ? rows only", generated.getSql()); + assertThat(generated.getBindVariables().toString(), generated.getBindVariables(), contains("Patient", 500L, 501L, 10, 5)); } @@ -123,13 +120,13 @@ public class SearchQueryBuilderTest { // Max only generated = builder.generate(null, 10); - assertEquals("SELECT TOP(?) T0.RES_ID FROM HFJ_RESOURCE T0 WHERE (((T0.RES_TYPE = ?) AND (T0.RES_DELETED_AT IS NULL)) AND (T0.RES_ID IN (?,?) ))", generated.getSql().toUpperCase(Locale.ROOT)); - assertThat(generated.getBindVariables().toString(), generated.getBindVariables(), contains(10, "Patient", 500L, 501L)); + assertEquals("SELECT T0.RES_ID FROM HFJ_RESOURCE T0 WHERE (((T0.RES_TYPE = ?) AND (T0.RES_DELETED_AT IS NULL)) AND (T0.RES_ID IN (?,?) )) ORDER BY @@VERSION OFFSET 0 ROWS FETCH FIRST ? ROWS ONLY", generated.getSql().toUpperCase(Locale.ROOT)); + assertThat(generated.getBindVariables().toString(), generated.getBindVariables(), contains("Patient", 500L, 501L, 10)); // Range generated = builder.generate(10, 5); - assertEquals("with query as (select inner_query.*, row_number() over (order by current_timestamp) as __row__ from ( SELECT t0.RES_ID as page0_ FROM HFJ_RESOURCE t0 WHERE (((t0.RES_TYPE = ?) AND (t0.RES_DELETED_AT IS NULL)) AND (t0.RES_ID IN (?,?) )) ) inner_query ) select page0_ from query where __row__ >= ? and __row__ < ?", generated.getSql()); - assertThat(generated.getBindVariables().toString(), generated.getBindVariables(), contains("Patient", 500L, 501L, 11, 16)); + assertEquals("SELECT t0.RES_ID FROM HFJ_RESOURCE t0 WHERE (((t0.RES_TYPE = ?) AND (t0.RES_DELETED_AT IS NULL)) AND (t0.RES_ID IN (?,?) )) order by @@version offset ? rows fetch next ? rows only", generated.getSql()); + assertThat(generated.getBindVariables().toString(), generated.getBindVariables(), contains("Patient", 500L, 501L, 10, 5)); } @@ -145,19 +142,16 @@ public class SearchQueryBuilderTest { // No range generated = builder.generate(null, null); -// assertEquals("SELECT t0.RES_ID FROM HFJ_RESOURCE t0 WHERE (((t0.RES_TYPE = ?) AND (t0.RES_DELETED_AT IS NULL)) AND (t0.RES_ID IN (?,?) )) ORDER BY CASE WHEN t0.RES_UPDATED IS NULL THEN 1 ELSE 0 END ASC, t0.RES_UPDATED ASC", generated.getSql()); assertEquals("SELECT t0.RES_ID FROM HFJ_RESOURCE t0 WHERE (((t0.RES_TYPE = ?) AND (t0.RES_DELETED_AT IS NULL)) AND (t0.RES_ID IN (?,?) )) ORDER BY t0.RES_UPDATED ASC", generated.getSql()); assertThat(generated.getBindVariables().toString(), generated.getBindVariables(), contains("Patient", 500L, 501L)); // Max only generated = builder.generate(null, 10); -// assertEquals("SELECT t0.RES_ID FROM HFJ_RESOURCE t0 WHERE (((t0.RES_TYPE = ?) AND (t0.RES_DELETED_AT IS NULL)) AND (t0.RES_ID IN (?,?) )) ORDER BY CASE WHEN t0.RES_UPDATED IS NULL THEN 1 ELSE 0 END ASC, t0.RES_UPDATED ASC offset 0 rows fetch next ? rows only", generated.getSql()); - assertEquals("SELECT t0.RES_ID FROM HFJ_RESOURCE t0 WHERE (((t0.RES_TYPE = ?) AND (t0.RES_DELETED_AT IS NULL)) AND (t0.RES_ID IN (?,?) )) ORDER BY t0.RES_UPDATED ASC offset 0 rows fetch next ? rows only", generated.getSql()); + assertEquals("SELECT t0.RES_ID FROM HFJ_RESOURCE t0 WHERE (((t0.RES_TYPE = ?) AND (t0.RES_DELETED_AT IS NULL)) AND (t0.RES_ID IN (?,?) )) ORDER BY t0.RES_UPDATED ASC offset 0 rows fetch first ? rows only", generated.getSql()); assertThat(generated.getBindVariables().toString(), generated.getBindVariables(), contains("Patient", 500L, 501L, 10)); // Range generated = builder.generate(10, 5); -// assertEquals("SELECT t0.RES_ID FROM HFJ_RESOURCE t0 WHERE (((t0.RES_TYPE = ?) AND (t0.RES_DELETED_AT IS NULL)) AND (t0.RES_ID IN (?,?) )) ORDER BY CASE WHEN t0.RES_UPDATED IS NULL THEN 1 ELSE 0 END ASC, t0.RES_UPDATED ASC offset ? rows fetch next ? rows only", generated.getSql()); assertEquals("SELECT t0.RES_ID FROM HFJ_RESOURCE t0 WHERE (((t0.RES_TYPE = ?) AND (t0.RES_DELETED_AT IS NULL)) AND (t0.RES_ID IN (?,?) )) ORDER BY t0.RES_UPDATED ASC offset ? rows fetch next ? rows only", generated.getSql()); assertThat(generated.getBindVariables().toString(), generated.getBindVariables(), contains("Patient", 500L, 501L, 10, 5)); @@ -167,7 +161,7 @@ public class SearchQueryBuilderTest { public void testRangePostgreSQL95_NoSort() { HibernatePropertiesProvider dialectProvider = new HibernatePropertiesProvider(); - dialectProvider.setDialectForUnitTest(new PostgreSQL95Dialect()); + dialectProvider.setDialectForUnitTest(new PostgreSQLDialect()); SearchQueryBuilder builder = new SearchQueryBuilder(myFhirContext, myStorageSettings, myPartitionSettings, myRequestPartitionId, "Patient", mySqlBuilderFactory, dialectProvider, false); builder.addResourceIdsPredicate(Lists.newArrayList(500L, 501L)); GeneratedSql generated; @@ -179,13 +173,13 @@ public class SearchQueryBuilderTest { // Max only generated = builder.generate(null, 10); - assertEquals("SELECT t0.RES_ID FROM HFJ_RESOURCE t0 WHERE (((t0.RES_TYPE = ?) AND (t0.RES_DELETED_AT IS NULL)) AND (t0.RES_ID IN (?,?) )) limit ?", generated.getSql()); + assertEquals("SELECT t0.RES_ID FROM HFJ_RESOURCE t0 WHERE (((t0.RES_TYPE = ?) AND (t0.RES_DELETED_AT IS NULL)) AND (t0.RES_ID IN (?,?) )) fetch first ? rows only", generated.getSql()); assertThat(generated.getBindVariables().toString(), generated.getBindVariables(), contains("Patient", 500L, 501L, 10)); // Range generated = builder.generate(10, 5); - assertEquals("SELECT t0.RES_ID FROM HFJ_RESOURCE t0 WHERE (((t0.RES_TYPE = ?) AND (t0.RES_DELETED_AT IS NULL)) AND (t0.RES_ID IN (?,?) )) limit ? offset ?", generated.getSql()); - assertThat(generated.getBindVariables().toString(), generated.getBindVariables(), contains("Patient", 500L, 501L, 5, 10)); + assertEquals("SELECT t0.RES_ID FROM HFJ_RESOURCE t0 WHERE (((t0.RES_TYPE = ?) AND (t0.RES_DELETED_AT IS NULL)) AND (t0.RES_ID IN (?,?) )) offset ? rows fetch next ? rows only", generated.getSql()); + assertThat(generated.getBindVariables().toString(), generated.getBindVariables(), contains("Patient", 500L, 501L, 10, 5)); } @@ -193,7 +187,7 @@ public class SearchQueryBuilderTest { public void testRangePostgreSQL95_WithSort() { HibernatePropertiesProvider dialectProvider = new HibernatePropertiesProvider(); - dialectProvider.setDialectForUnitTest(new PostgreSQL95Dialect()); + dialectProvider.setDialectForUnitTest(new PostgreSQLDialect()); SearchQueryBuilder builder = new SearchQueryBuilder(myFhirContext, myStorageSettings, myPartitionSettings, myRequestPartitionId, "Patient", mySqlBuilderFactory, dialectProvider, false); builder.addResourceIdsPredicate(Lists.newArrayList(500L, 501L)); builder.addSortDate(builder.getOrCreateResourceTablePredicateBuilder().getColumnLastUpdated(), true); @@ -206,13 +200,13 @@ public class SearchQueryBuilderTest { // Max only generated = builder.generate(null, 10); - assertEquals("SELECT t0.RES_ID FROM HFJ_RESOURCE t0 WHERE (((t0.RES_TYPE = ?) AND (t0.RES_DELETED_AT IS NULL)) AND (t0.RES_ID IN (?,?) )) ORDER BY t0.RES_UPDATED ASC NULLS LAST limit ?", generated.getSql()); + assertEquals("SELECT t0.RES_ID FROM HFJ_RESOURCE t0 WHERE (((t0.RES_TYPE = ?) AND (t0.RES_DELETED_AT IS NULL)) AND (t0.RES_ID IN (?,?) )) ORDER BY t0.RES_UPDATED ASC NULLS LAST fetch first ? rows only", generated.getSql()); assertThat(generated.getBindVariables().toString(), generated.getBindVariables(), contains("Patient", 500L, 501L, 10)); // Range generated = builder.generate(10, 5); - assertEquals("SELECT t0.RES_ID FROM HFJ_RESOURCE t0 WHERE (((t0.RES_TYPE = ?) AND (t0.RES_DELETED_AT IS NULL)) AND (t0.RES_ID IN (?,?) )) ORDER BY t0.RES_UPDATED ASC NULLS LAST limit ? offset ?", generated.getSql()); - assertThat(generated.getBindVariables().toString(), generated.getBindVariables(), contains("Patient", 500L, 501L, 5, 10)); + assertEquals("SELECT t0.RES_ID FROM HFJ_RESOURCE t0 WHERE (((t0.RES_TYPE = ?) AND (t0.RES_DELETED_AT IS NULL)) AND (t0.RES_ID IN (?,?) )) ORDER BY t0.RES_UPDATED ASC NULLS LAST offset ? rows fetch next ? rows only", generated.getSql()); + assertThat(generated.getBindVariables().toString(), generated.getBindVariables(), contains("Patient", 500L, 501L, 10, 5)); } @@ -220,7 +214,7 @@ public class SearchQueryBuilderTest { public void testRangeOracle12c_NoSort() { HibernatePropertiesProvider dialectProvider = new HibernatePropertiesProvider(); - dialectProvider.setDialectForUnitTest(new Oracle12cDialect()); + dialectProvider.setDialectForUnitTest(new HapiFhirOracleDialect()); SearchQueryBuilder builder = new SearchQueryBuilder(myFhirContext, myStorageSettings, myPartitionSettings, myRequestPartitionId, "Patient", mySqlBuilderFactory, dialectProvider, false); builder.addResourceIdsPredicate(Lists.newArrayList(500L, 501L)); GeneratedSql generated; @@ -232,13 +226,13 @@ public class SearchQueryBuilderTest { // Max only generated = builder.generate(null, 10); - assertEquals("select * from ( SELECT t0.RES_ID FROM HFJ_RESOURCE t0 WHERE (((t0.RES_TYPE = ?) AND (t0.RES_DELETED_AT IS NULL)) AND (t0.RES_ID IN (?,?) )) ) where rownum <= ?", generated.getSql()); + assertEquals("SELECT t0.RES_ID FROM HFJ_RESOURCE t0 WHERE (((t0.RES_TYPE = ?) AND (t0.RES_DELETED_AT IS NULL)) AND (t0.RES_ID IN (?,?) )) fetch first ? rows only", generated.getSql()); assertThat(generated.getBindVariables().toString(), generated.getBindVariables(), contains("Patient", 500L, 501L, 10)); // Range generated = builder.generate(10, 5); - assertEquals("select * from ( select row_.*, rownum rownum_ from ( SELECT t0.RES_ID FROM HFJ_RESOURCE t0 WHERE (((t0.RES_TYPE = ?) AND (t0.RES_DELETED_AT IS NULL)) AND (t0.RES_ID IN (?,?) )) ) row_ where rownum <= ?) where rownum_ > ?", generated.getSql()); - assertThat(generated.getBindVariables().toString(), generated.getBindVariables(), contains("Patient", 500L, 501L, 15, 10)); + assertEquals("SELECT t0.RES_ID FROM HFJ_RESOURCE t0 WHERE (((t0.RES_TYPE = ?) AND (t0.RES_DELETED_AT IS NULL)) AND (t0.RES_ID IN (?,?) )) offset ? rows fetch next ? rows only", generated.getSql()); + assertThat(generated.getBindVariables().toString(), generated.getBindVariables(), contains("Patient", 500L, 501L, 10, 5)); } @@ -246,7 +240,7 @@ public class SearchQueryBuilderTest { public void testRangeOracle12c_WithSort() { HibernatePropertiesProvider dialectProvider = new HibernatePropertiesProvider(); - dialectProvider.setDialectForUnitTest(new Oracle12cDialect()); + dialectProvider.setDialectForUnitTest(new HapiFhirOracleDialect()); SearchQueryBuilder builder = new SearchQueryBuilder(myFhirContext, myStorageSettings, myPartitionSettings, myRequestPartitionId, "Patient", mySqlBuilderFactory, dialectProvider, false); builder.addResourceIdsPredicate(Lists.newArrayList(500L, 501L)); builder.addSortDate(builder.getOrCreateResourceTablePredicateBuilder().getColumnLastUpdated(), true); @@ -259,13 +253,13 @@ public class SearchQueryBuilderTest { // Max only generated = builder.generate(null, 10); - assertEquals("select * from ( SELECT t0.RES_ID FROM HFJ_RESOURCE t0 WHERE (((t0.RES_TYPE = ?) AND (t0.RES_DELETED_AT IS NULL)) AND (t0.RES_ID IN (?,?) )) ORDER BY t0.RES_UPDATED ASC NULLS LAST ) where rownum <= ?", generated.getSql()); + assertEquals("SELECT t0.RES_ID FROM HFJ_RESOURCE t0 WHERE (((t0.RES_TYPE = ?) AND (t0.RES_DELETED_AT IS NULL)) AND (t0.RES_ID IN (?,?) )) ORDER BY t0.RES_UPDATED ASC NULLS LAST fetch first ? rows only", generated.getSql()); assertThat(generated.getBindVariables().toString(), generated.getBindVariables(), contains("Patient", 500L, 501L, 10)); // Range generated = builder.generate(10, 5); - assertEquals("select * from ( select row_.*, rownum rownum_ from ( SELECT t0.RES_ID FROM HFJ_RESOURCE t0 WHERE (((t0.RES_TYPE = ?) AND (t0.RES_DELETED_AT IS NULL)) AND (t0.RES_ID IN (?,?) )) ORDER BY t0.RES_UPDATED ASC NULLS LAST ) row_ where rownum <= ?) where rownum_ > ?", generated.getSql()); - assertThat(generated.getBindVariables().toString(), generated.getBindVariables(), contains("Patient", 500L, 501L, 15, 10)); + assertEquals("SELECT t0.RES_ID FROM HFJ_RESOURCE t0 WHERE (((t0.RES_TYPE = ?) AND (t0.RES_DELETED_AT IS NULL)) AND (t0.RES_ID IN (?,?) )) ORDER BY t0.RES_UPDATED ASC NULLS LAST offset ? rows fetch next ? rows only", generated.getSql()); + assertThat(generated.getBindVariables().toString(), generated.getBindVariables(), contains("Patient", 500L, 501L, 10, 5)); } @@ -290,7 +284,7 @@ public class SearchQueryBuilderTest { // Range generated = builder.generate(10, 5); - assertEquals("SELECT t0.RES_ID FROM HFJ_RESOURCE t0 WHERE (((t0.RES_TYPE = ?) AND (t0.RES_DELETED_AT IS NULL)) AND (t0.RES_ID IN (?,?) )) limit ?, ?", generated.getSql()); + assertEquals("SELECT t0.RES_ID FROM HFJ_RESOURCE t0 WHERE (((t0.RES_TYPE = ?) AND (t0.RES_DELETED_AT IS NULL)) AND (t0.RES_ID IN (?,?) )) limit ?,?", generated.getSql()); assertThat(generated.getBindVariables().toString(), generated.getBindVariables(), contains("Patient", 500L, 501L, 10, 5)); } @@ -320,7 +314,7 @@ public class SearchQueryBuilderTest { // Range generated = builder.generate(10, 5); // assertEquals("SELECT t0.RES_ID FROM HFJ_RESOURCE t0 WHERE (((t0.RES_TYPE = ?) AND (t0.RES_DELETED_AT IS NULL)) AND (t0.RES_ID IN (?,?) )) ORDER BY CASE WHEN t0.RES_UPDATED IS NULL THEN 1 ELSE 0 END ASC, t0.RES_UPDATED ASC limit ?, ?", generated.getSql()); - assertEquals("SELECT t0.RES_ID FROM HFJ_RESOURCE t0 WHERE (((t0.RES_TYPE = ?) AND (t0.RES_DELETED_AT IS NULL)) AND (t0.RES_ID IN (?,?) )) ORDER BY t0.RES_UPDATED ASC limit ?, ?", generated.getSql()); + assertEquals("SELECT t0.RES_ID FROM HFJ_RESOURCE t0 WHERE (((t0.RES_TYPE = ?) AND (t0.RES_DELETED_AT IS NULL)) AND (t0.RES_ID IN (?,?) )) ORDER BY t0.RES_UPDATED ASC limit ?,?", generated.getSql()); assertThat(generated.getBindVariables().toString(), generated.getBindVariables(), contains("Patient", 500L, 501L, 10, 5)); } @@ -330,7 +324,7 @@ public class SearchQueryBuilderTest { public void testRangeMariaDB103_NoSort() { HibernatePropertiesProvider dialectProvider = new HibernatePropertiesProvider(); - dialectProvider.setDialectForUnitTest(new MariaDB103Dialect()); + dialectProvider.setDialectForUnitTest(new HapiFhirMariaDBDialect()); SearchQueryBuilder builder = new SearchQueryBuilder(myFhirContext, myStorageSettings, myPartitionSettings, myRequestPartitionId, "Patient", mySqlBuilderFactory, dialectProvider, false); builder.addResourceIdsPredicate(Lists.newArrayList(500L, 501L)); GeneratedSql generated; @@ -347,7 +341,7 @@ public class SearchQueryBuilderTest { // Range generated = builder.generate(10, 5); - assertEquals("SELECT t0.RES_ID FROM HFJ_RESOURCE t0 WHERE (((t0.RES_TYPE = ?) AND (t0.RES_DELETED_AT IS NULL)) AND (t0.RES_ID IN (?,?) )) limit ?, ?", generated.getSql()); + assertEquals("SELECT t0.RES_ID FROM HFJ_RESOURCE t0 WHERE (((t0.RES_TYPE = ?) AND (t0.RES_DELETED_AT IS NULL)) AND (t0.RES_ID IN (?,?) )) limit ?,?", generated.getSql()); assertThat(generated.getBindVariables().toString(), generated.getBindVariables(), contains("Patient", 500L, 501L, 10, 5)); } @@ -356,7 +350,7 @@ public class SearchQueryBuilderTest { public void testRangeMariaDB103_WithSort() { HibernatePropertiesProvider dialectProvider = new HibernatePropertiesProvider(); - dialectProvider.setDialectForUnitTest(new MariaDB103Dialect()); + dialectProvider.setDialectForUnitTest(new HapiFhirMariaDBDialect()); SearchQueryBuilder builder = new SearchQueryBuilder(myFhirContext, myStorageSettings, myPartitionSettings, myRequestPartitionId, "Patient", mySqlBuilderFactory, dialectProvider, false); builder.addResourceIdsPredicate(Lists.newArrayList(500L, 501L)); builder.addSortDate(builder.getOrCreateResourceTablePredicateBuilder().getColumnLastUpdated(), true); @@ -377,7 +371,7 @@ public class SearchQueryBuilderTest { // Range generated = builder.generate(10, 5); // assertEquals("SELECT t0.RES_ID FROM HFJ_RESOURCE t0 WHERE (((t0.RES_TYPE = ?) AND (t0.RES_DELETED_AT IS NULL)) AND (t0.RES_ID IN (?,?) )) ORDER BY CASE WHEN t0.RES_UPDATED IS NULL THEN 1 ELSE 0 END ASC, t0.RES_UPDATED ASC limit ?, ?", generated.getSql()); - assertEquals("SELECT t0.RES_ID FROM HFJ_RESOURCE t0 WHERE (((t0.RES_TYPE = ?) AND (t0.RES_DELETED_AT IS NULL)) AND (t0.RES_ID IN (?,?) )) ORDER BY t0.RES_UPDATED ASC limit ?, ?", generated.getSql()); + assertEquals("SELECT t0.RES_ID FROM HFJ_RESOURCE t0 WHERE (((t0.RES_TYPE = ?) AND (t0.RES_DELETED_AT IS NULL)) AND (t0.RES_ID IN (?,?) )) ORDER BY t0.RES_UPDATED ASC limit ?,?", generated.getSql()); assertThat(generated.getBindVariables().toString(), generated.getBindVariables(), contains("Patient", 500L, 501L, 10, 5)); } @@ -387,7 +381,7 @@ public class SearchQueryBuilderTest { public void testRangeDerbyTenSeven_NoSort() { HibernatePropertiesProvider dialectProvider = new HibernatePropertiesProvider(); - dialectProvider.setDialectForUnitTest(new DerbyTenSevenDialect()); + dialectProvider.setDialectForUnitTest(new DerbyDialect()); SearchQueryBuilder builder = new SearchQueryBuilder(myFhirContext, myStorageSettings, myPartitionSettings, myRequestPartitionId, "Patient", mySqlBuilderFactory, dialectProvider, false); builder.addResourceIdsPredicate(Lists.newArrayList(500L, 501L)); GeneratedSql generated; @@ -399,13 +393,13 @@ public class SearchQueryBuilderTest { // Max only generated = builder.generate(null, 10); - assertEquals("SELECT t0.RES_ID FROM HFJ_RESOURCE t0 WHERE (((t0.RES_TYPE = ?) AND (t0.RES_DELETED_AT IS NULL)) AND (t0.RES_ID IN (?,?) )) fetch first 10 rows only", generated.getSql()); - assertThat(generated.getBindVariables().toString(), generated.getBindVariables(), contains("Patient", 500L, 501L)); + assertEquals("SELECT t0.RES_ID FROM HFJ_RESOURCE t0 WHERE (((t0.RES_TYPE = ?) AND (t0.RES_DELETED_AT IS NULL)) AND (t0.RES_ID IN (?,?) )) fetch first ? rows only", generated.getSql()); + assertThat(generated.getBindVariables().toString(), generated.getBindVariables(), contains("Patient", 500L, 501L, 10)); // Range generated = builder.generate(10, 5); - assertEquals("SELECT t0.RES_ID FROM HFJ_RESOURCE t0 WHERE (((t0.RES_TYPE = ?) AND (t0.RES_DELETED_AT IS NULL)) AND (t0.RES_ID IN (?,?) )) offset 10 rows fetch next 5 rows only", generated.getSql()); - assertThat(generated.getBindVariables().toString(), generated.getBindVariables(), contains("Patient", 500L, 501L)); + assertEquals("SELECT t0.RES_ID FROM HFJ_RESOURCE t0 WHERE (((t0.RES_TYPE = ?) AND (t0.RES_DELETED_AT IS NULL)) AND (t0.RES_ID IN (?,?) )) offset ? rows fetch next ? rows only", generated.getSql()); + assertThat(generated.getBindVariables().toString(), generated.getBindVariables(), contains("Patient", 500L, 501L, 10, 5)); } @@ -413,7 +407,7 @@ public class SearchQueryBuilderTest { public void testRangeDerbyTenSeven_WithSort() { HibernatePropertiesProvider dialectProvider = new HibernatePropertiesProvider(); - dialectProvider.setDialectForUnitTest(new DerbyTenSevenDialect()); + dialectProvider.setDialectForUnitTest(new DerbyDialect()); SearchQueryBuilder builder = new SearchQueryBuilder(myFhirContext, myStorageSettings, myPartitionSettings, myRequestPartitionId, "Patient", mySqlBuilderFactory, dialectProvider, false); builder.addResourceIdsPredicate(Lists.newArrayList(500L, 501L)); builder.addSortDate(builder.getOrCreateResourceTablePredicateBuilder().getColumnLastUpdated(), true); @@ -426,13 +420,13 @@ public class SearchQueryBuilderTest { // Max only generated = builder.generate(null, 10); - assertEquals("SELECT t0.RES_ID FROM HFJ_RESOURCE t0 WHERE (((t0.RES_TYPE = ?) AND (t0.RES_DELETED_AT IS NULL)) AND (t0.RES_ID IN (?,?) )) ORDER BY t0.RES_UPDATED ASC NULLS LAST fetch first 10 rows only", generated.getSql()); - assertThat(generated.getBindVariables().toString(), generated.getBindVariables(), contains("Patient", 500L, 501L)); + assertEquals("SELECT t0.RES_ID FROM HFJ_RESOURCE t0 WHERE (((t0.RES_TYPE = ?) AND (t0.RES_DELETED_AT IS NULL)) AND (t0.RES_ID IN (?,?) )) ORDER BY t0.RES_UPDATED ASC NULLS LAST fetch first ? rows only", generated.getSql()); + assertThat(generated.getBindVariables().toString(), generated.getBindVariables(), contains("Patient", 500L, 501L, 10)); // Range generated = builder.generate(10, 5); - assertEquals("SELECT t0.RES_ID FROM HFJ_RESOURCE t0 WHERE (((t0.RES_TYPE = ?) AND (t0.RES_DELETED_AT IS NULL)) AND (t0.RES_ID IN (?,?) )) ORDER BY t0.RES_UPDATED ASC NULLS LAST offset 10 rows fetch next 5 rows only", generated.getSql()); - assertThat(generated.getBindVariables().toString(), generated.getBindVariables(), contains("Patient", 500L, 501L)); + assertEquals("SELECT t0.RES_ID FROM HFJ_RESOURCE t0 WHERE (((t0.RES_TYPE = ?) AND (t0.RES_DELETED_AT IS NULL)) AND (t0.RES_ID IN (?,?) )) ORDER BY t0.RES_UPDATED ASC NULLS LAST offset ? rows fetch next ? rows only", generated.getSql()); + assertThat(generated.getBindVariables().toString(), generated.getBindVariables(), contains("Patient", 500L, 501L, 10, 5)); } diff --git a/hapi-fhir-jpaserver-test-utilities/src/test/java/ca/uhn/fhir/jpa/term/LoincFullLoadR4SandboxIT.java b/hapi-fhir-jpaserver-test-utilities/src/test/java/ca/uhn/fhir/jpa/term/LoincFullLoadR4SandboxIT.java index 67ad68cba3b..3e21618a1f3 100644 --- a/hapi-fhir-jpaserver-test-utilities/src/test/java/ca/uhn/fhir/jpa/term/LoincFullLoadR4SandboxIT.java +++ b/hapi-fhir-jpaserver-test-utilities/src/test/java/ca/uhn/fhir/jpa/term/LoincFullLoadR4SandboxIT.java @@ -11,7 +11,6 @@ import ca.uhn.fhir.jpa.entity.TermCodeSystemVersion; import ca.uhn.fhir.jpa.entity.TermConcept; import ca.uhn.fhir.jpa.entity.TermConceptProperty; import ca.uhn.fhir.jpa.entity.TermValueSet; -import ca.uhn.fhir.jpa.model.entity.ForcedId; import ca.uhn.fhir.jpa.model.entity.ResourceTable; import ca.uhn.fhir.jpa.provider.TerminologyUploaderProvider; import ca.uhn.fhir.jpa.term.api.ITermDeferredStorageSvc; @@ -33,7 +32,7 @@ import org.apache.commons.dbcp2.BasicDataSource; import org.apache.commons.io.IOUtils; import org.apache.commons.lang3.StringUtils; import org.apache.commons.lang3.tuple.Pair; -import org.hibernate.dialect.PostgreSQL10Dialect; +import org.hibernate.dialect.PostgreSQLDialect; import org.hl7.fhir.r4.model.ValueSet; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Disabled; @@ -52,9 +51,9 @@ import org.springframework.transaction.PlatformTransactionManager; import org.springframework.util.CollectionUtils; import org.springframework.util.ResourceUtils; -import javax.annotation.Nonnull; -import javax.persistence.EntityManager; -import javax.persistence.Query; +import jakarta.annotation.Nonnull; +import jakarta.persistence.EntityManager; +import jakarta.persistence.Query; import java.io.File; import java.io.FileNotFoundException; import java.io.IOException; @@ -599,12 +598,9 @@ public class LoincFullLoadR4SandboxIT extends BaseJpaTest { */ private void queryForSpecificValueSet() { runInTransaction(() -> { - Query q = myEntityManager.createQuery("from ForcedId where myForcedId like 'LG8749-6%'"); - @SuppressWarnings("unchecked") - List fIds = (List) q.getResultList(); - long res_id = fIds.stream().map(ForcedId::getId).sorted().findFirst().orElse(fail("ForcedId not found")); - - Query q1 = myEntityManager.createQuery("from ResourceTable where id = " + res_id); + Query q1 = myEntityManager + .createQuery("from ResourceTable where myFhirId like :fhir_id") + .setParameter("fhir_id", "LG8749-6%"); @SuppressWarnings("unchecked") List vsList = (List) q1.getResultList(); assertEquals(1, vsList.size()); @@ -710,7 +706,7 @@ public class LoincFullLoadR4SandboxIT extends BaseJpaTest { @Override public String getHibernateDialect() { if (USE_REAL_DB) { - return PostgreSQL10Dialect.class.getName(); + return PostgreSQLDialect.class.getName(); } return super.getHibernateDialect(); diff --git a/hapi-fhir-jpaserver-test-utilities/src/test/java/ca/uhn/fhir/jpa/util/JpaHapiTransactionServiceTest.java b/hapi-fhir-jpaserver-test-utilities/src/test/java/ca/uhn/fhir/jpa/util/JpaHapiTransactionServiceTest.java new file mode 100644 index 00000000000..9f3d588d923 --- /dev/null +++ b/hapi-fhir-jpaserver-test-utilities/src/test/java/ca/uhn/fhir/jpa/util/JpaHapiTransactionServiceTest.java @@ -0,0 +1,115 @@ +package ca.uhn.fhir.jpa.util; + +import ca.uhn.fhir.context.FhirContext; +import ca.uhn.fhir.jpa.api.dao.IFhirResourceDao; +import ca.uhn.fhir.jpa.dao.tx.HapiTransactionService; +import ca.uhn.fhir.jpa.test.BaseJpaTest; +import ca.uhn.fhir.jpa.test.config.TestR4Config; +import ca.uhn.fhir.rest.api.server.SystemRequestDetails; +import ca.uhn.fhir.rest.server.exceptions.ResourceNotFoundException; +import org.hl7.fhir.instance.model.api.IIdType; +import org.hl7.fhir.r4.model.Observation; +import org.hl7.fhir.r4.model.Patient; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.test.context.ContextConfiguration; +import org.springframework.test.context.junit.jupiter.SpringExtension; +import org.springframework.transaction.PlatformTransactionManager; +import org.springframework.transaction.annotation.Propagation; + +import java.util.concurrent.atomic.AtomicReference; + +import static org.junit.jupiter.api.Assertions.assertNotNull; +import static org.junit.jupiter.api.Assertions.assertThrows; + +@ExtendWith(SpringExtension.class) +@ContextConfiguration(classes = { + TestR4Config.class +}) +public class JpaHapiTransactionServiceTest extends BaseJpaTest { + @Autowired PlatformTransactionManager myTxManager; + @Autowired IFhirResourceDao myPatientDao; + @Autowired IFhirResourceDao myObservationDao; + SystemRequestDetails myRequestDetails = new SystemRequestDetails(); + final AtomicReference myObservationId = new AtomicReference<>(); + final AtomicReference myPatientId = new AtomicReference<>(); + + @Override + protected FhirContext getFhirContext() { + return myFhirContext; + } + + @Override + protected PlatformTransactionManager getTxManager() { + return myTxManager; + } + + @Autowired + HapiTransactionService myHapiTransactionService; + + @Test + void testNewTransactionCommitInsideOldTransactionRollback() { + + try { + myHapiTransactionService.withSystemRequest().withPropagation(Propagation.REQUIRED).execute(()->{ + myObservationId.set(myObservationDao.create(new Observation(), myRequestDetails).getId()); + + myHapiTransactionService.withSystemRequest().withPropagation(Propagation.REQUIRES_NEW) + .execute(()-> myPatientId.set(myPatientDao.create(new Patient(), myRequestDetails).getId())); + // roll back the Observation. The Patient has committed + throw new RuntimeException("roll back the Observation."); + }); + } catch (RuntimeException e) { + // expected + } + + assertNotFound(myObservationDao, myObservationId.get()); + assertFound(myPatientDao, myPatientId.get()); + } + + + + @Test + void testRequiredTransactionCommitInsideExistingTx_rollsBackWithMainTx() { + // given + + try { + myHapiTransactionService.withSystemRequest().withPropagation(Propagation.REQUIRED).execute(()->{ + myObservationId.set(myObservationDao.create(new Observation(), myRequestDetails).getId()); + + myHapiTransactionService.withSystemRequest().withPropagation(Propagation.REQUIRED).execute(()-> myPatientId.set(myPatientDao.create(new Patient(), myRequestDetails).getId())); + throw new RuntimeException("roll back both."); + }); + } catch (RuntimeException e) { + // expected + } + + assertNotFound(myObservationDao, myObservationId.get()); + assertNotFound(myPatientDao, myPatientId.get()); + } + + @Test + void testTransactionCommitRespectsRollbackOnly() { + + try { + myHapiTransactionService.withSystemRequest().withPropagation(Propagation.REQUIRED).execute((theTransactionStatus)->{ + myObservationId.set(myObservationDao.create(new Observation(), myRequestDetails).getId()); + theTransactionStatus.setRollbackOnly(); + return null; + }); + } catch (RuntimeException e) { + // expected + } + + assertNotFound(myObservationDao, myObservationId.get()); + } + + void assertNotFound(IFhirResourceDao theDao, IIdType id) { + assertThrows(ResourceNotFoundException.class, ()-> theDao.read(id, myRequestDetails)); + } + + void assertFound(IFhirResourceDao theDao, IIdType theId) { + assertNotNull(theDao.read(theId, myRequestDetails)); + } +} diff --git a/hapi-fhir-jpaserver-uhnfhirtest/pom.xml b/hapi-fhir-jpaserver-uhnfhirtest/pom.xml index e5b8861c32f..0409211c054 100644 --- a/hapi-fhir-jpaserver-uhnfhirtest/pom.xml +++ b/hapi-fhir-jpaserver-uhnfhirtest/pom.xml @@ -5,7 +5,7 @@ ca.uhn.hapi.fhir hapi-fhir - 6.9.7-SNAPSHOT + 6.11.7-SNAPSHOT ../pom.xml @@ -65,21 +65,6 @@ ${project.version} - - com.helger - ph-schematron - - - Saxon-HE - net.sf.saxon - - - - - com.helger - ph-commons - - org.springframework spring-web @@ -108,8 +93,8 @@ - javax.servlet - javax.servlet-api + jakarta.servlet + jakarta.servlet-api provided @@ -119,48 +104,6 @@ h2 test - - - org.eclipse.jetty - jetty-servlets - test - - - org.eclipse.jetty - jetty-webapp - test - - - org.eclipse.jetty - jetty-servlet - test - - - org.eclipse.jetty - jetty-server - test - - - org.eclipse.jetty - jetty-util - test - - - org.eclipse.jetty.websocket - websocket-jetty-api - test - - - org.eclipse.jetty.websocket - websocket-core-client - test - - - org.eclipse.jetty.websocket - websocket-jetty-server - test - - org.apache.jena apache-jena-libs @@ -190,6 +133,13 @@ ${project.version} + + ca.uhn.hapi.fhir + hapi-fhir-test-utilities + test + ${project.version} + + @@ -235,6 +185,14 @@ + + org.sonatype.plugins + nexus-staging-maven-plugin + + true + true + + org.apache.maven.plugins maven-deploy-plugin diff --git a/hapi-fhir-jpaserver-uhnfhirtest/src/main/java/ca/uhn/fhirtest/TestRestfulServer.java b/hapi-fhir-jpaserver-uhnfhirtest/src/main/java/ca/uhn/fhirtest/TestRestfulServer.java index 84e393ce4fd..58b7f4f9e4b 100644 --- a/hapi-fhir-jpaserver-uhnfhirtest/src/main/java/ca/uhn/fhirtest/TestRestfulServer.java +++ b/hapi-fhir-jpaserver-uhnfhirtest/src/main/java/ca/uhn/fhirtest/TestRestfulServer.java @@ -47,6 +47,9 @@ import ca.uhn.fhirtest.config.TestR4BConfig; import ca.uhn.fhirtest.config.TestR4Config; import ca.uhn.fhirtest.config.TestR5Config; import ca.uhn.hapi.converters.server.VersionedApiConverterInterceptor; +import jakarta.servlet.ServletContext; +import jakarta.servlet.ServletException; +import jakarta.servlet.http.HttpServletRequest; import org.apache.commons.lang3.StringUtils; import org.apache.commons.lang3.Validate; import org.springframework.web.context.ContextLoaderListener; @@ -55,9 +58,6 @@ import org.springframework.web.context.support.AnnotationConfigWebApplicationCon import java.util.ArrayList; import java.util.List; -import javax.servlet.ServletContext; -import javax.servlet.ServletException; -import javax.servlet.http.HttpServletRequest; public class TestRestfulServer extends RestfulServer { @@ -80,7 +80,7 @@ public class TestRestfulServer extends RestfulServer { public void destroy() { super.destroy(); ourLog.info("Server is shutting down"); - myAppCtx.destroy(); + myAppCtx.close(); } @SuppressWarnings("unchecked") diff --git a/hapi-fhir-jpaserver-uhnfhirtest/src/main/java/ca/uhn/fhirtest/config/CommonConfig.java b/hapi-fhir-jpaserver-uhnfhirtest/src/main/java/ca/uhn/fhirtest/config/CommonConfig.java index b28a9367462..1d84422e1a2 100644 --- a/hapi-fhir-jpaserver-uhnfhirtest/src/main/java/ca/uhn/fhirtest/config/CommonConfig.java +++ b/hapi-fhir-jpaserver-uhnfhirtest/src/main/java/ca/uhn/fhirtest/config/CommonConfig.java @@ -23,6 +23,7 @@ import ca.uhn.fhir.storage.interceptor.balp.IBalpAuditEventSink; import ca.uhn.fhirtest.ScheduledSubscriptionDeleter; import ca.uhn.fhirtest.interceptor.AnalyticsInterceptor; import ca.uhn.fhirtest.joke.HolyFooCowInterceptor; +import ca.uhn.fhirtest.migrate.FhirTestAutoMigrator; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.Import; @@ -131,6 +132,11 @@ public class CommonConfig { return new FhirTestBalpAuditContextServices(); } + @Bean + public FhirTestAutoMigrator migrator() { + return new FhirTestAutoMigrator(); + } + public static boolean isLocalTestMode() { return "true".equalsIgnoreCase(System.getProperty("testmode.local")); } diff --git a/hapi-fhir-jpaserver-uhnfhirtest/src/main/java/ca/uhn/fhirtest/config/FhirTestBalpAuditContextServices.java b/hapi-fhir-jpaserver-uhnfhirtest/src/main/java/ca/uhn/fhirtest/config/FhirTestBalpAuditContextServices.java index 394e35eb70d..2a454340628 100644 --- a/hapi-fhir-jpaserver-uhnfhirtest/src/main/java/ca/uhn/fhirtest/config/FhirTestBalpAuditContextServices.java +++ b/hapi-fhir-jpaserver-uhnfhirtest/src/main/java/ca/uhn/fhirtest/config/FhirTestBalpAuditContextServices.java @@ -3,11 +3,9 @@ package ca.uhn.fhirtest.config; import ca.uhn.fhir.rest.api.server.RequestDetails; import ca.uhn.fhir.rest.server.servlet.ServletRequestDetails; import ca.uhn.fhir.storage.interceptor.balp.IBalpAuditContextServices; -import joptsimple.internal.Strings; +import jakarta.annotation.Nonnull; import org.hl7.fhir.r4.model.Reference; -import javax.annotation.Nonnull; - import static org.apache.commons.lang3.StringUtils.defaultString; import static org.apache.commons.lang3.StringUtils.isBlank; @@ -73,6 +71,6 @@ public class FhirTestBalpAuditContextServices implements IBalpAuditContextServic parts[2] = "X"; parts[3] = "X"; } - return Strings.join(parts, "."); + return String.join(".", parts); } } diff --git a/hapi-fhir-jpaserver-uhnfhirtest/src/main/java/ca/uhn/fhirtest/config/FhirTesterConfig.java b/hapi-fhir-jpaserver-uhnfhirtest/src/main/java/ca/uhn/fhirtest/config/FhirTesterConfig.java index b41af3fa429..d6c493cf78a 100644 --- a/hapi-fhir-jpaserver-uhnfhirtest/src/main/java/ca/uhn/fhirtest/config/FhirTesterConfig.java +++ b/hapi-fhir-jpaserver-uhnfhirtest/src/main/java/ca/uhn/fhirtest/config/FhirTesterConfig.java @@ -4,7 +4,6 @@ import ca.uhn.fhir.context.FhirVersionEnum; import ca.uhn.fhir.to.FhirTesterMvcConfig; import ca.uhn.fhir.to.TesterConfig; import ca.uhn.fhirtest.mvc.SubscriptionPlaygroundController; -import org.springframework.beans.factory.annotation.Autowire; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.Import; @@ -116,7 +115,7 @@ public class FhirTesterConfig { return retVal; } - @Bean(autowire = Autowire.BY_TYPE) + @Bean public SubscriptionPlaygroundController subscriptionPlaygroundController() { return new SubscriptionPlaygroundController(); } diff --git a/hapi-fhir-jpaserver-uhnfhirtest/src/main/java/ca/uhn/fhirtest/config/TestAuditConfig.java b/hapi-fhir-jpaserver-uhnfhirtest/src/main/java/ca/uhn/fhirtest/config/TestAuditConfig.java index bd6c6690f16..7d68437d1f5 100644 --- a/hapi-fhir-jpaserver-uhnfhirtest/src/main/java/ca/uhn/fhirtest/config/TestAuditConfig.java +++ b/hapi-fhir-jpaserver-uhnfhirtest/src/main/java/ca/uhn/fhirtest/config/TestAuditConfig.java @@ -12,6 +12,8 @@ import ca.uhn.fhir.jpa.search.DatabaseBackedPagingProvider; import ca.uhn.fhir.jpa.util.CurrentThreadCaptureQueriesListener; import ca.uhn.fhir.jpa.validation.ValidationSettings; import ca.uhn.fhirtest.interceptor.PublicSecurityInterceptor; +import jakarta.annotation.PostConstruct; +import jakarta.persistence.EntityManagerFactory; import net.ttddyy.dsproxy.support.ProxyDataSourceBuilder; import org.apache.commons.dbcp2.BasicDataSource; import org.hl7.fhir.r5.utils.validation.constants.ReferenceValidationPolicy; @@ -28,8 +30,6 @@ import org.springframework.transaction.annotation.EnableTransactionManagement; import java.util.Properties; import java.util.concurrent.TimeUnit; -import javax.annotation.PostConstruct; -import javax.persistence.EntityManagerFactory; import javax.sql.DataSource; @Configuration @@ -102,9 +102,11 @@ public class TestAuditConfig { @Bean public LocalContainerEntityManagerFactoryBean entityManagerFactory( - ConfigurableListableBeanFactory theConfigurableListableBeanFactory, FhirContext theFhirContext) { + ConfigurableListableBeanFactory theConfigurableListableBeanFactory, + FhirContext theFhirContext, + JpaStorageSettings theStorageSettings) { LocalContainerEntityManagerFactoryBean retVal = HapiEntityManagerFactoryUtil.newEntityManagerFactory( - theConfigurableListableBeanFactory, theFhirContext); + theConfigurableListableBeanFactory, theFhirContext, theStorageSettings); retVal.setPersistenceUnitName("PU_HapiFhirJpaAudit"); retVal.setDataSource(dataSource()); retVal.setJpaProperties(jpaProperties()); @@ -120,7 +122,7 @@ public class TestAuditConfig { } extraProperties.put("hibernate.format_sql", "false"); extraProperties.put("hibernate.show_sql", "false"); - extraProperties.put("hibernate.hbm2ddl.auto", "update"); + extraProperties.put("hibernate.hbm2ddl.auto", "none"); extraProperties.put("hibernate.jdbc.batch_size", "20"); extraProperties.put("hibernate.cache.use_query_cache", "false"); extraProperties.put("hibernate.cache.use_second_level_cache", "false"); diff --git a/hapi-fhir-jpaserver-uhnfhirtest/src/main/java/ca/uhn/fhirtest/config/TestDstu2Config.java b/hapi-fhir-jpaserver-uhnfhirtest/src/main/java/ca/uhn/fhirtest/config/TestDstu2Config.java index 7d957240c90..e19ac54a653 100644 --- a/hapi-fhir-jpaserver-uhnfhirtest/src/main/java/ca/uhn/fhirtest/config/TestDstu2Config.java +++ b/hapi-fhir-jpaserver-uhnfhirtest/src/main/java/ca/uhn/fhirtest/config/TestDstu2Config.java @@ -14,6 +14,7 @@ import ca.uhn.fhir.rest.server.interceptor.RequestValidatingInterceptor; import ca.uhn.fhir.validation.IValidatorModule; import ca.uhn.fhir.validation.ResultSeverityEnum; import ca.uhn.fhirtest.interceptor.PublicSecurityInterceptor; +import jakarta.persistence.EntityManagerFactory; import net.ttddyy.dsproxy.support.ProxyDataSourceBuilder; import org.apache.commons.dbcp2.BasicDataSource; import org.hibernate.search.backend.lucene.cfg.LuceneBackendSettings; @@ -34,7 +35,6 @@ import org.springframework.transaction.annotation.EnableTransactionManagement; import java.util.Properties; import java.util.concurrent.TimeUnit; -import javax.persistence.EntityManagerFactory; import javax.sql.DataSource; @Configuration @@ -120,9 +120,11 @@ public class TestDstu2Config { @Bean public LocalContainerEntityManagerFactoryBean entityManagerFactory( - ConfigurableListableBeanFactory theConfigurableListableBeanFactory, FhirContext theFhirContext) { + ConfigurableListableBeanFactory theConfigurableListableBeanFactory, + FhirContext theFhirContext, + JpaStorageSettings theStorageSettings) { LocalContainerEntityManagerFactoryBean retVal = HapiEntityManagerFactoryUtil.newEntityManagerFactory( - theConfigurableListableBeanFactory, theFhirContext); + theConfigurableListableBeanFactory, theFhirContext, theStorageSettings); retVal.setPersistenceUnitName("PU_HapiFhirJpaDstu2"); retVal.setDataSource(dataSource()); retVal.setJpaProperties(jpaProperties()); @@ -138,7 +140,7 @@ public class TestDstu2Config { } extraProperties.put("hibernate.format_sql", "false"); extraProperties.put("hibernate.show_sql", "false"); - extraProperties.put("hibernate.hbm2ddl.auto", "update"); + extraProperties.put("hibernate.hbm2ddl.auto", "none"); extraProperties.put("hibernate.jdbc.batch_size", "20"); extraProperties.put("hibernate.cache.use_query_cache", "false"); extraProperties.put("hibernate.cache.use_second_level_cache", "false"); diff --git a/hapi-fhir-jpaserver-uhnfhirtest/src/main/java/ca/uhn/fhirtest/config/TestDstu3Config.java b/hapi-fhir-jpaserver-uhnfhirtest/src/main/java/ca/uhn/fhirtest/config/TestDstu3Config.java index da6b07ac91b..2531b87b4b8 100644 --- a/hapi-fhir-jpaserver-uhnfhirtest/src/main/java/ca/uhn/fhirtest/config/TestDstu3Config.java +++ b/hapi-fhir-jpaserver-uhnfhirtest/src/main/java/ca/uhn/fhirtest/config/TestDstu3Config.java @@ -15,6 +15,7 @@ import ca.uhn.fhir.rest.server.interceptor.RequestValidatingInterceptor; import ca.uhn.fhir.validation.IInstanceValidatorModule; import ca.uhn.fhir.validation.ResultSeverityEnum; import ca.uhn.fhirtest.interceptor.PublicSecurityInterceptor; +import jakarta.persistence.EntityManagerFactory; import net.ttddyy.dsproxy.support.ProxyDataSourceBuilder; import org.apache.commons.dbcp2.BasicDataSource; import org.hibernate.search.backend.lucene.cfg.LuceneBackendSettings; @@ -35,7 +36,6 @@ import org.springframework.transaction.annotation.EnableTransactionManagement; import java.util.Properties; import java.util.concurrent.TimeUnit; -import javax.persistence.EntityManagerFactory; import javax.sql.DataSource; @Configuration @@ -120,9 +120,11 @@ public class TestDstu3Config { @Bean public LocalContainerEntityManagerFactoryBean entityManagerFactory( - ConfigurableListableBeanFactory theConfigurableListableBeanFactory, FhirContext theFhirContext) { + ConfigurableListableBeanFactory theConfigurableListableBeanFactory, + FhirContext theFhirContext, + JpaStorageSettings theStorageSettings) { LocalContainerEntityManagerFactoryBean retVal = HapiEntityManagerFactoryUtil.newEntityManagerFactory( - theConfigurableListableBeanFactory, theFhirContext); + theConfigurableListableBeanFactory, theFhirContext, theStorageSettings); retVal.setPersistenceUnitName("PU_HapiFhirJpaDstu3"); retVal.setDataSource(dataSource()); retVal.setJpaProperties(jpaProperties()); @@ -138,7 +140,7 @@ public class TestDstu3Config { } extraProperties.put("hibernate.format_sql", "false"); extraProperties.put("hibernate.show_sql", "false"); - extraProperties.put("hibernate.hbm2ddl.auto", "update"); + extraProperties.put("hibernate.hbm2ddl.auto", "none"); extraProperties.put("hibernate.jdbc.batch_size", "20"); extraProperties.put("hibernate.cache.use_query_cache", "false"); extraProperties.put("hibernate.cache.use_second_level_cache", "false"); diff --git a/hapi-fhir-jpaserver-uhnfhirtest/src/main/java/ca/uhn/fhirtest/config/TestR4BConfig.java b/hapi-fhir-jpaserver-uhnfhirtest/src/main/java/ca/uhn/fhirtest/config/TestR4BConfig.java index 454faeab90a..6cbb003cd29 100644 --- a/hapi-fhir-jpaserver-uhnfhirtest/src/main/java/ca/uhn/fhirtest/config/TestR4BConfig.java +++ b/hapi-fhir-jpaserver-uhnfhirtest/src/main/java/ca/uhn/fhirtest/config/TestR4BConfig.java @@ -15,6 +15,7 @@ import ca.uhn.fhir.rest.server.interceptor.RequestValidatingInterceptor; import ca.uhn.fhir.validation.IInstanceValidatorModule; import ca.uhn.fhir.validation.ResultSeverityEnum; import ca.uhn.fhirtest.interceptor.PublicSecurityInterceptor; +import jakarta.persistence.EntityManagerFactory; import net.ttddyy.dsproxy.support.ProxyDataSourceBuilder; import org.apache.commons.dbcp2.BasicDataSource; import org.hibernate.search.backend.lucene.cfg.LuceneBackendSettings; @@ -36,7 +37,6 @@ import org.springframework.transaction.annotation.EnableTransactionManagement; import java.util.Properties; import java.util.concurrent.TimeUnit; -import javax.persistence.EntityManagerFactory; import javax.sql.DataSource; @Configuration @@ -118,9 +118,11 @@ public class TestR4BConfig { @Bean public LocalContainerEntityManagerFactoryBean entityManagerFactory( - ConfigurableListableBeanFactory theConfigurableListableBeanFactory, FhirContext theFhirContext) { + ConfigurableListableBeanFactory theConfigurableListableBeanFactory, + FhirContext theFhirContext, + JpaStorageSettings theStorageSettings) { LocalContainerEntityManagerFactoryBean retVal = HapiEntityManagerFactoryUtil.newEntityManagerFactory( - theConfigurableListableBeanFactory, theFhirContext); + theConfigurableListableBeanFactory, theFhirContext, theStorageSettings); retVal.setPersistenceUnitName("PU_HapiFhirJpaR4B"); retVal.setDataSource(dataSource()); retVal.setJpaProperties(jpaProperties()); @@ -136,7 +138,7 @@ public class TestR4BConfig { } extraProperties.put("hibernate.format_sql", "false"); extraProperties.put("hibernate.show_sql", "false"); - extraProperties.put("hibernate.hbm2ddl.auto", "update"); + extraProperties.put("hibernate.hbm2ddl.auto", "none"); extraProperties.put("hibernate.jdbc.batch_size", "20"); extraProperties.put("hibernate.cache.use_query_cache", "false"); extraProperties.put("hibernate.cache.use_second_level_cache", "false"); diff --git a/hapi-fhir-jpaserver-uhnfhirtest/src/main/java/ca/uhn/fhirtest/config/TestR4Config.java b/hapi-fhir-jpaserver-uhnfhirtest/src/main/java/ca/uhn/fhirtest/config/TestR4Config.java index f95782ccc96..ba0a5db50af 100644 --- a/hapi-fhir-jpaserver-uhnfhirtest/src/main/java/ca/uhn/fhirtest/config/TestR4Config.java +++ b/hapi-fhir-jpaserver-uhnfhirtest/src/main/java/ca/uhn/fhirtest/config/TestR4Config.java @@ -21,6 +21,7 @@ import ca.uhn.fhir.rest.server.interceptor.RequestValidatingInterceptor; import ca.uhn.fhir.validation.IInstanceValidatorModule; import ca.uhn.fhir.validation.ResultSeverityEnum; import ca.uhn.fhirtest.interceptor.PublicSecurityInterceptor; +import jakarta.persistence.EntityManagerFactory; import net.ttddyy.dsproxy.support.ProxyDataSourceBuilder; import org.apache.commons.dbcp2.BasicDataSource; import org.hibernate.search.backend.lucene.cfg.LuceneBackendSettings; @@ -41,7 +42,6 @@ import org.springframework.transaction.annotation.EnableTransactionManagement; import java.util.Properties; import java.util.concurrent.TimeUnit; -import javax.persistence.EntityManagerFactory; import javax.sql.DataSource; @Configuration @@ -121,9 +121,11 @@ public class TestR4Config { @Bean public LocalContainerEntityManagerFactoryBean entityManagerFactory( - ConfigurableListableBeanFactory theConfigurableListableBeanFactory, FhirContext theFhirContext) { + ConfigurableListableBeanFactory theConfigurableListableBeanFactory, + FhirContext theFhirContext, + JpaStorageSettings theStorageSettings) { LocalContainerEntityManagerFactoryBean retVal = HapiEntityManagerFactoryUtil.newEntityManagerFactory( - theConfigurableListableBeanFactory, theFhirContext); + theConfigurableListableBeanFactory, theFhirContext, theStorageSettings); retVal.setPersistenceUnitName("PU_HapiFhirJpaR4"); retVal.setDataSource(dataSource()); retVal.setJpaProperties(jpaProperties()); @@ -139,7 +141,7 @@ public class TestR4Config { } extraProperties.put("hibernate.format_sql", "false"); extraProperties.put("hibernate.show_sql", "false"); - extraProperties.put("hibernate.hbm2ddl.auto", "update"); + extraProperties.put("hibernate.hbm2ddl.auto", "none"); extraProperties.put("hibernate.jdbc.batch_size", "20"); extraProperties.put("hibernate.cache.use_query_cache", "false"); extraProperties.put("hibernate.cache.use_second_level_cache", "false"); diff --git a/hapi-fhir-jpaserver-uhnfhirtest/src/main/java/ca/uhn/fhirtest/config/TestR5Config.java b/hapi-fhir-jpaserver-uhnfhirtest/src/main/java/ca/uhn/fhirtest/config/TestR5Config.java index 2870a925c32..775125e84fe 100644 --- a/hapi-fhir-jpaserver-uhnfhirtest/src/main/java/ca/uhn/fhirtest/config/TestR5Config.java +++ b/hapi-fhir-jpaserver-uhnfhirtest/src/main/java/ca/uhn/fhirtest/config/TestR5Config.java @@ -15,6 +15,7 @@ import ca.uhn.fhir.rest.server.interceptor.RequestValidatingInterceptor; import ca.uhn.fhir.validation.IInstanceValidatorModule; import ca.uhn.fhir.validation.ResultSeverityEnum; import ca.uhn.fhirtest.interceptor.PublicSecurityInterceptor; +import jakarta.persistence.EntityManagerFactory; import net.ttddyy.dsproxy.support.ProxyDataSourceBuilder; import org.apache.commons.dbcp2.BasicDataSource; import org.hibernate.search.backend.lucene.cfg.LuceneBackendSettings; @@ -37,7 +38,6 @@ import org.springframework.transaction.annotation.EnableTransactionManagement; import java.util.Properties; import java.util.concurrent.TimeUnit; -import javax.persistence.EntityManagerFactory; import javax.sql.DataSource; @Configuration @@ -130,9 +130,11 @@ public class TestR5Config { @Bean public LocalContainerEntityManagerFactoryBean entityManagerFactory( - ConfigurableListableBeanFactory theConfigurableListableBeanFactory, FhirContext theFhirContext) { + ConfigurableListableBeanFactory theConfigurableListableBeanFactory, + FhirContext theFhirContext, + JpaStorageSettings theStorageSettings) { LocalContainerEntityManagerFactoryBean retVal = HapiEntityManagerFactoryUtil.newEntityManagerFactory( - theConfigurableListableBeanFactory, theFhirContext); + theConfigurableListableBeanFactory, theFhirContext, theStorageSettings); retVal.setPersistenceUnitName("PU_HapiFhirJpaR5"); retVal.setDataSource(dataSource()); retVal.setJpaProperties(jpaProperties()); @@ -148,7 +150,7 @@ public class TestR5Config { } extraProperties.put("hibernate.format_sql", "false"); extraProperties.put("hibernate.show_sql", "false"); - extraProperties.put("hibernate.hbm2ddl.auto", "update"); + extraProperties.put("hibernate.hbm2ddl.auto", "none"); extraProperties.put("hibernate.jdbc.batch_size", "20"); extraProperties.put("hibernate.cache.use_query_cache", "false"); extraProperties.put("hibernate.cache.use_second_level_cache", "false"); diff --git a/hapi-fhir-jpaserver-uhnfhirtest/src/main/java/ca/uhn/fhirtest/interceptor/AnalyticsInterceptor.java b/hapi-fhir-jpaserver-uhnfhirtest/src/main/java/ca/uhn/fhirtest/interceptor/AnalyticsInterceptor.java index 65043a50226..4ef6091af33 100644 --- a/hapi-fhir-jpaserver-uhnfhirtest/src/main/java/ca/uhn/fhirtest/interceptor/AnalyticsInterceptor.java +++ b/hapi-fhir-jpaserver-uhnfhirtest/src/main/java/ca/uhn/fhirtest/interceptor/AnalyticsInterceptor.java @@ -10,6 +10,7 @@ import ca.uhn.fhir.rest.client.apache.ApacheRestfulClientFactory; import ca.uhn.fhir.rest.server.interceptor.InterceptorAdapter; import ca.uhn.fhir.rest.server.servlet.ServletRequestDetails; import ca.uhn.fhir.util.UrlUtil; +import jakarta.annotation.PreDestroy; import org.apache.http.client.HttpClient; import org.apache.http.client.methods.CloseableHttpResponse; import org.apache.http.client.methods.HttpPost; @@ -26,7 +27,6 @@ import java.util.ArrayList; import java.util.LinkedList; import java.util.List; import java.util.UUID; -import javax.annotation.PreDestroy; import static org.apache.commons.lang3.StringUtils.defaultIfBlank; import static org.apache.commons.lang3.StringUtils.isBlank; diff --git a/hapi-fhir-jpaserver-uhnfhirtest/src/main/java/ca/uhn/fhirtest/joke/HolyFooCowInterceptor.java b/hapi-fhir-jpaserver-uhnfhirtest/src/main/java/ca/uhn/fhirtest/joke/HolyFooCowInterceptor.java index 2405d91b39a..5a730df3671 100644 --- a/hapi-fhir-jpaserver-uhnfhirtest/src/main/java/ca/uhn/fhirtest/joke/HolyFooCowInterceptor.java +++ b/hapi-fhir-jpaserver-uhnfhirtest/src/main/java/ca/uhn/fhirtest/joke/HolyFooCowInterceptor.java @@ -3,9 +3,8 @@ package ca.uhn.fhirtest.joke; import ca.uhn.fhir.i18n.Msg; import ca.uhn.fhir.rest.server.exceptions.UnclassifiedServerFailureException; import ca.uhn.fhir.rest.server.interceptor.InterceptorAdapter; - -import javax.servlet.http.HttpServletRequest; -import javax.servlet.http.HttpServletResponse; +import jakarta.servlet.http.HttpServletRequest; +import jakarta.servlet.http.HttpServletResponse; import static org.apache.commons.lang3.StringUtils.isNotBlank; diff --git a/hapi-fhir-jpaserver-uhnfhirtest/src/main/java/ca/uhn/fhirtest/migrate/FhirTestAutoMigrator.java b/hapi-fhir-jpaserver-uhnfhirtest/src/main/java/ca/uhn/fhirtest/migrate/FhirTestAutoMigrator.java new file mode 100644 index 00000000000..438eff48732 --- /dev/null +++ b/hapi-fhir-jpaserver-uhnfhirtest/src/main/java/ca/uhn/fhirtest/migrate/FhirTestAutoMigrator.java @@ -0,0 +1,51 @@ +package ca.uhn.fhirtest.migrate; + +import ca.uhn.fhir.jpa.migrate.DriverTypeEnum; +import ca.uhn.fhir.jpa.migrate.HapiMigrationStorageSvc; +import ca.uhn.fhir.jpa.migrate.MigrationTaskList; +import ca.uhn.fhir.jpa.migrate.SchemaMigrator; +import ca.uhn.fhir.jpa.migrate.dao.HapiMigrationDao; +import ca.uhn.fhir.jpa.migrate.tasks.HapiFhirJpaMigrationTasks; +import ca.uhn.fhir.util.VersionEnum; +import ca.uhn.fhirtest.config.CommonConfig; +import jakarta.annotation.PostConstruct; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.annotation.Autowired; + +import java.util.Properties; +import java.util.Set; +import javax.sql.DataSource; + +public class FhirTestAutoMigrator { + + private static final Logger ourLog = LoggerFactory.getLogger(FhirTestAutoMigrator.class); + public static final String MIGRATION_TABLENAME = "MIGRATIONS"; + + @Autowired + private DataSource myDataSource; + + @PostConstruct + public void run() { + DriverTypeEnum driver; + if (CommonConfig.isLocalTestMode()) { + driver = DriverTypeEnum.H2_EMBEDDED; + } else { + driver = DriverTypeEnum.POSTGRES_9_4; + } + + HapiMigrationDao hapiMigrationDao = new HapiMigrationDao(myDataSource, driver, MIGRATION_TABLENAME); + HapiMigrationStorageSvc hapiMigrationStorageSvc = new HapiMigrationStorageSvc(hapiMigrationDao); + + MigrationTaskList tasks = new HapiFhirJpaMigrationTasks(Set.of()).getAllTasks(VersionEnum.values()); + + SchemaMigrator schemaMigrator = new SchemaMigrator( + "HAPI FHIR", MIGRATION_TABLENAME, myDataSource, new Properties(), tasks, hapiMigrationStorageSvc); + schemaMigrator.setDriverType(driver); + + ourLog.info("About to run migration..."); + schemaMigrator.createMigrationTableIfRequired(); + schemaMigrator.migrate(); + ourLog.info("Migration complete"); + } +} diff --git a/hapi-fhir-jpaserver-uhnfhirtest/src/main/java/ca/uhn/fhirtest/mvc/SubscriptionPlaygroundController.java b/hapi-fhir-jpaserver-uhnfhirtest/src/main/java/ca/uhn/fhirtest/mvc/SubscriptionPlaygroundController.java index f8dc38ef043..1b9a3f72e5f 100644 --- a/hapi-fhir-jpaserver-uhnfhirtest/src/main/java/ca/uhn/fhirtest/mvc/SubscriptionPlaygroundController.java +++ b/hapi-fhir-jpaserver-uhnfhirtest/src/main/java/ca/uhn/fhirtest/mvc/SubscriptionPlaygroundController.java @@ -3,6 +3,7 @@ package ca.uhn.fhirtest.mvc; import ca.uhn.fhir.rest.client.impl.GenericClient; import ca.uhn.fhir.to.BaseController; import ca.uhn.fhir.to.model.HomeRequest; +import jakarta.servlet.http.HttpServletRequest; import org.hl7.fhir.dstu3.model.Bundle; import org.hl7.fhir.dstu3.model.Subscription; import org.springframework.ui.ModelMap; @@ -10,7 +11,6 @@ import org.springframework.web.bind.annotation.RequestMapping; import java.util.ArrayList; import java.util.List; -import javax.servlet.http.HttpServletRequest; @org.springframework.stereotype.Controller() public class SubscriptionPlaygroundController extends BaseController { diff --git a/hapi-fhir-jpaserver-uhnfhirtest/src/test/java/ca/uhn/fhirtest/UhnFhirTestApp.java b/hapi-fhir-jpaserver-uhnfhirtest/src/test/java/ca/uhn/fhirtest/UhnFhirTestApp.java index 9b5dd2c68f5..4c79a0ca6cc 100644 --- a/hapi-fhir-jpaserver-uhnfhirtest/src/test/java/ca/uhn/fhirtest/UhnFhirTestApp.java +++ b/hapi-fhir-jpaserver-uhnfhirtest/src/test/java/ca/uhn/fhirtest/UhnFhirTestApp.java @@ -3,8 +3,8 @@ package ca.uhn.fhirtest; import ca.uhn.fhir.context.FhirContext; import ca.uhn.fhir.rest.api.MethodOutcome; import ca.uhn.fhir.rest.client.api.IGenericClient; +import org.eclipse.jetty.ee10.webapp.WebAppContext; import org.eclipse.jetty.server.Server; -import org.eclipse.jetty.webapp.WebAppContext; import org.hl7.fhir.dstu3.model.Organization; import org.hl7.fhir.dstu3.model.Patient; import org.hl7.fhir.dstu3.model.Subscription; @@ -63,7 +63,7 @@ public class UhnFhirTestApp { root.setContextPath("/"); root.setDescriptor("hapi-fhir-jpaserver-uhnfhirtest/src/main/webapp/WEB-INF/web.xml"); - root.setResourceBase("hapi-fhir-jpaserver-uhnfhirtest/target/hapi-fhir-jpaserver"); + root.setBaseResourceAsString("hapi-fhir-jpaserver-uhnfhirtest/target/hapi-fhir-jpaserver"); root.setParentLoaderPriority(true); diff --git a/hapi-fhir-server-cds-hooks/pom.xml b/hapi-fhir-server-cds-hooks/pom.xml index 93e6f3d05a2..dd01ffff387 100644 --- a/hapi-fhir-server-cds-hooks/pom.xml +++ b/hapi-fhir-server-cds-hooks/pom.xml @@ -7,7 +7,7 @@ ca.uhn.hapi.fhir hapi-deployable-pom - 6.9.7-SNAPSHOT + 6.11.7-SNAPSHOT ../hapi-deployable-pom/pom.xml @@ -33,7 +33,11 @@ hapi-fhir-storage ${project.version} - + + ca.uhn.hapi.fhir + hapi-fhir-storage-cr + ${project.version} + org.springframework spring-beans @@ -72,6 +76,29 @@ ${reflections_version} test + + org.simplejavamail + simple-java-mail + + + + com.sun.activation + jakarta.activation + + + + + com.icegreen + greenmail + compile + + + + com.sun.activation + jakarta.activation + + + diff --git a/hapi-fhir-server-cds-hooks/src/main/java/ca/uhn/hapi/fhir/cdshooks/api/ICdsConfigService.java b/hapi-fhir-server-cds-hooks/src/main/java/ca/uhn/hapi/fhir/cdshooks/api/ICdsConfigService.java index 78f74247e0d..f709daef3d8 100644 --- a/hapi-fhir-server-cds-hooks/src/main/java/ca/uhn/hapi/fhir/cdshooks/api/ICdsConfigService.java +++ b/hapi-fhir-server-cds-hooks/src/main/java/ca/uhn/hapi/fhir/cdshooks/api/ICdsConfigService.java @@ -20,11 +20,17 @@ package ca.uhn.hapi.fhir.cdshooks.api; import ca.uhn.fhir.context.FhirContext; +import ca.uhn.fhir.cr.common.IRepositoryFactory; import ca.uhn.fhir.jpa.api.dao.DaoRegistry; +import ca.uhn.fhir.rest.api.server.RequestDetails; +import ca.uhn.fhir.rest.api.server.SystemRequestDetails; +import ca.uhn.fhir.rest.api.server.SystemRestfulResponse; +import ca.uhn.fhir.rest.server.RestfulServer; +import ca.uhn.hapi.fhir.cdshooks.svc.cr.CdsCrSettings; import com.fasterxml.jackson.databind.ObjectMapper; - -import javax.annotation.Nonnull; -import javax.annotation.Nullable; +import jakarta.annotation.Nonnull; +import jakarta.annotation.Nullable; +import org.opencds.cqf.fhir.utility.Ids; public interface ICdsConfigService { @Nonnull @@ -33,8 +39,29 @@ public interface ICdsConfigService { @Nonnull ObjectMapper getObjectMapper(); + @Nonnull + CdsCrSettings getCdsCrSettings(); + @Nullable default DaoRegistry getDaoRegistry() { return null; } + + @Nullable + default IRepositoryFactory getRepositoryFactory() { + return null; + } + + @Nullable + default RestfulServer getRestfulServer() { + return null; + } + + default RequestDetails createRequestDetails(FhirContext theFhirContext, String theId, String theResourceType) { + SystemRequestDetails rd = new SystemRequestDetails(); + rd.setServer(getRestfulServer()); + rd.setResponse(new SystemRestfulResponse(rd)); + rd.setId(Ids.newId(theFhirContext.getVersion().getVersion(), theResourceType, theId)); + return rd; + } } diff --git a/hapi-fhir-server-cds-hooks/src/main/java/ca/uhn/hapi/fhir/cdshooks/api/ICdsServiceRegistry.java b/hapi-fhir-server-cds-hooks/src/main/java/ca/uhn/hapi/fhir/cdshooks/api/ICdsServiceRegistry.java index a979ce49557..869b38f23fb 100644 --- a/hapi-fhir-server-cds-hooks/src/main/java/ca/uhn/hapi/fhir/cdshooks/api/ICdsServiceRegistry.java +++ b/hapi-fhir-server-cds-hooks/src/main/java/ca/uhn/hapi/fhir/cdshooks/api/ICdsServiceRegistry.java @@ -72,6 +72,14 @@ public interface ICdsServiceRegistry { boolean theAllowAutoFhirClientPrefetch, String theModuleId); + /** + * Register a new Clinical Reasoning CDS Service with the endpoint. + * + * @param theServiceId the id of the service PlanDefinition + * @return the service was registered + */ + boolean registerCrService(String theServiceId); + /** * Remove registered CDS service with the service ID, only removes dynamically registered service * diff --git a/hapi-fhir-server-cds-hooks/src/main/java/ca/uhn/hapi/fhir/cdshooks/api/json/CdsServiceRequestContextJson.java b/hapi-fhir-server-cds-hooks/src/main/java/ca/uhn/hapi/fhir/cdshooks/api/json/CdsServiceRequestContextJson.java index a4d999a4ae5..d2832a79f56 100644 --- a/hapi-fhir-server-cds-hooks/src/main/java/ca/uhn/hapi/fhir/cdshooks/api/json/CdsServiceRequestContextJson.java +++ b/hapi-fhir-server-cds-hooks/src/main/java/ca/uhn/hapi/fhir/cdshooks/api/json/CdsServiceRequestContextJson.java @@ -20,6 +20,7 @@ package ca.uhn.hapi.fhir.cdshooks.api.json; import ca.uhn.fhir.model.api.IModelJson; +import com.fasterxml.jackson.annotation.JsonAnyGetter; import org.hl7.fhir.instance.model.api.IBaseResource; import java.util.Collections; @@ -29,6 +30,8 @@ import java.util.Map; import java.util.Set; public class CdsServiceRequestContextJson extends BaseCdsServiceJson implements IModelJson { + + @JsonAnyGetter private Map myMap; public String getString(String theKey) { diff --git a/hapi-fhir-server-cds-hooks/src/main/java/ca/uhn/hapi/fhir/cdshooks/api/json/CdsServiceRequestJson.java b/hapi-fhir-server-cds-hooks/src/main/java/ca/uhn/hapi/fhir/cdshooks/api/json/CdsServiceRequestJson.java index eb3e9a5f887..4ff00180895 100644 --- a/hapi-fhir-server-cds-hooks/src/main/java/ca/uhn/hapi/fhir/cdshooks/api/json/CdsServiceRequestJson.java +++ b/hapi-fhir-server-cds-hooks/src/main/java/ca/uhn/hapi/fhir/cdshooks/api/json/CdsServiceRequestJson.java @@ -21,6 +21,7 @@ package ca.uhn.hapi.fhir.cdshooks.api.json; import ca.uhn.fhir.model.api.IModelJson; import com.fasterxml.jackson.annotation.JsonProperty; +import jakarta.annotation.Nonnull; import org.hl7.fhir.instance.model.api.IBaseResource; import java.util.Collections; @@ -28,7 +29,6 @@ import java.util.HashSet; import java.util.LinkedHashMap; import java.util.Map; import java.util.Set; -import javax.annotation.Nonnull; /** * Represents a CDS Hooks Service Request diff --git a/hapi-fhir-server-cds-hooks/src/main/java/ca/uhn/hapi/fhir/cdshooks/config/CdsCrConfig.java b/hapi-fhir-server-cds-hooks/src/main/java/ca/uhn/hapi/fhir/cdshooks/config/CdsCrConfig.java new file mode 100644 index 00000000000..9aa78815ce6 --- /dev/null +++ b/hapi-fhir-server-cds-hooks/src/main/java/ca/uhn/hapi/fhir/cdshooks/config/CdsCrConfig.java @@ -0,0 +1,37 @@ +/*- + * #%L + * HAPI FHIR - CDS Hooks + * %% + * Copyright (C) 2014 - 2023 Smile CDR, Inc. + * %% + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * #L% + */ +package ca.uhn.hapi.fhir.cdshooks.config; + +import ca.uhn.fhir.cr.config.CrConfigCondition; +import ca.uhn.fhir.cr.config.RepositoryConfig; +import ca.uhn.fhir.cr.config.r4.ApplyOperationConfig; +import org.springframework.context.annotation.Conditional; +import org.springframework.context.annotation.Configuration; +import org.springframework.context.annotation.Import; + +/** + * This class exists as a wrapper for the CR configs required for CDS on FHIR to be loaded only when dependencies are met. + * Adding the condition to the configs themselves causes issues with downstream projects. + * + */ +@Configuration +@Conditional(CrConfigCondition.class) +@Import({RepositoryConfig.class, ApplyOperationConfig.class}) +public class CdsCrConfig {} diff --git a/hapi-fhir-server-cds-hooks/src/main/java/ca/uhn/hapi/fhir/cdshooks/config/CdsHooksConfig.java b/hapi-fhir-server-cds-hooks/src/main/java/ca/uhn/hapi/fhir/cdshooks/config/CdsHooksConfig.java index 737a341f460..6f679212d65 100644 --- a/hapi-fhir-server-cds-hooks/src/main/java/ca/uhn/hapi/fhir/cdshooks/config/CdsHooksConfig.java +++ b/hapi-fhir-server-cds-hooks/src/main/java/ca/uhn/hapi/fhir/cdshooks/config/CdsHooksConfig.java @@ -20,8 +20,13 @@ package ca.uhn.hapi.fhir.cdshooks.config; import ca.uhn.fhir.context.FhirContext; +import ca.uhn.fhir.cr.common.IRepositoryFactory; import ca.uhn.fhir.jpa.api.dao.DaoRegistry; +import ca.uhn.fhir.jpa.cache.IResourceChangeListenerRegistry; import ca.uhn.fhir.jpa.searchparam.MatchUrlService; +import ca.uhn.fhir.jpa.searchparam.SearchParameterMap; +import ca.uhn.fhir.rest.api.server.RequestDetails; +import ca.uhn.fhir.rest.server.RestfulServer; import ca.uhn.hapi.fhir.cdshooks.api.ICdsConfigService; import ca.uhn.hapi.fhir.cdshooks.api.ICdsHooksDaoAuthorizationSvc; import ca.uhn.hapi.fhir.cdshooks.api.ICdsServiceRegistry; @@ -29,27 +34,59 @@ import ca.uhn.hapi.fhir.cdshooks.module.CdsHooksObjectMapperFactory; import ca.uhn.hapi.fhir.cdshooks.svc.CdsConfigServiceImpl; import ca.uhn.hapi.fhir.cdshooks.svc.CdsHooksContextBooter; import ca.uhn.hapi.fhir.cdshooks.svc.CdsServiceRegistryImpl; +import ca.uhn.hapi.fhir.cdshooks.svc.cr.CdsCrServiceRegistry; +import ca.uhn.hapi.fhir.cdshooks.svc.cr.CdsCrSettings; +import ca.uhn.hapi.fhir.cdshooks.svc.cr.CdsServiceInterceptor; +import ca.uhn.hapi.fhir.cdshooks.svc.cr.ICdsCrService; +import ca.uhn.hapi.fhir.cdshooks.svc.cr.ICdsCrServiceFactory; +import ca.uhn.hapi.fhir.cdshooks.svc.cr.ICdsCrServiceRegistry; +import ca.uhn.hapi.fhir.cdshooks.svc.cr.discovery.CdsCrDiscoveryServiceRegistry; +import ca.uhn.hapi.fhir.cdshooks.svc.cr.discovery.ICdsCrDiscoveryServiceRegistry; +import ca.uhn.hapi.fhir.cdshooks.svc.cr.discovery.ICrDiscoveryService; +import ca.uhn.hapi.fhir.cdshooks.svc.cr.discovery.ICrDiscoveryServiceFactory; import ca.uhn.hapi.fhir.cdshooks.svc.prefetch.CdsPrefetchDaoSvc; import ca.uhn.hapi.fhir.cdshooks.svc.prefetch.CdsPrefetchFhirClientSvc; import ca.uhn.hapi.fhir.cdshooks.svc.prefetch.CdsPrefetchSvc; import ca.uhn.hapi.fhir.cdshooks.svc.prefetch.CdsResolutionStrategySvc; import com.fasterxml.jackson.databind.ObjectMapper; +import org.hl7.fhir.instance.model.api.IIdType; +import org.opencds.cqf.fhir.api.Repository; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Qualifier; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; +import org.springframework.context.annotation.Import; + +import java.lang.reflect.Constructor; +import java.lang.reflect.InvocationTargetException; +import java.util.Optional; @Configuration +@Import(CdsCrConfig.class) public class CdsHooksConfig { + private static final Logger ourLog = LoggerFactory.getLogger(CdsHooksConfig.class); public static final String CDS_HOOKS_OBJECT_MAPPER_FACTORY = "cdsHooksObjectMapperFactory"; + public static final String PLAN_DEFINITION_RESOURCE_NAME = "PlanDefinition"; + @Autowired(required = false) private DaoRegistry myDaoRegistry; @Autowired(required = false) private MatchUrlService myMatchUrlService; + @Autowired(required = false) + private IResourceChangeListenerRegistry myResourceChangeListenerRegistry; + + @Autowired(required = false) + private IRepositoryFactory myRepositoryFactory; + + @Autowired(required = false) + private RestfulServer myRestfulServer; + @Bean(name = CDS_HOOKS_OBJECT_MAPPER_FACTORY) public ObjectMapper objectMapper(FhirContext theFhirContext) { return new CdsHooksObjectMapperFactory(theFhirContext).newMapper(); @@ -59,14 +96,107 @@ public class CdsHooksConfig { public ICdsServiceRegistry cdsServiceRegistry( CdsHooksContextBooter theCdsHooksContextBooter, CdsPrefetchSvc theCdsPrefetchSvc, - @Qualifier(CDS_HOOKS_OBJECT_MAPPER_FACTORY) ObjectMapper theObjectMapper) { - return new CdsServiceRegistryImpl(theCdsHooksContextBooter, theCdsPrefetchSvc, theObjectMapper); + @Qualifier(CDS_HOOKS_OBJECT_MAPPER_FACTORY) ObjectMapper theObjectMapper, + ICdsCrServiceFactory theCdsCrServiceFactory, + ICrDiscoveryServiceFactory theCrDiscoveryServiceFactory) { + return new CdsServiceRegistryImpl( + theCdsHooksContextBooter, + theCdsPrefetchSvc, + theObjectMapper, + theCdsCrServiceFactory, + theCrDiscoveryServiceFactory); + } + + @Bean + public ICdsCrServiceRegistry cdsCrServiceRegistry() { + return new CdsCrServiceRegistry(); + } + + @Bean + public ICdsCrServiceFactory cdsCrServiceFactory( + FhirContext theFhirContext, + ICdsConfigService theCdsConfigService, + ICdsCrServiceRegistry theCdsCrServiceRegistry) { + return id -> { + if (myRepositoryFactory == null) { + return null; + } + RequestDetails rd = + theCdsConfigService.createRequestDetails(theFhirContext, id, PLAN_DEFINITION_RESOURCE_NAME); + Repository repository = myRepositoryFactory.create(rd); + Optional> clazz = + theCdsCrServiceRegistry.find(theFhirContext.getVersion().getVersion()); + if (clazz.isEmpty()) { + return null; + } + try { + Constructor constructor = + clazz.get().getConstructor(RequestDetails.class, Repository.class, ICdsConfigService.class); + return constructor.newInstance(rd, repository, theCdsConfigService); + } catch (NoSuchMethodException + | InvocationTargetException + | InstantiationException + | IllegalAccessException e) { + ourLog.error("Error encountered attempting to construct the CdsCrService: " + e.getMessage()); + return null; + } + }; + } + + @Bean + public ICdsCrDiscoveryServiceRegistry cdsCrDiscoveryServiceRegistry() { + return new CdsCrDiscoveryServiceRegistry(); + } + + @Bean + public ICrDiscoveryServiceFactory crDiscoveryServiceFactory( + FhirContext theFhirContext, + ICdsConfigService theCdsConfigService, + ICdsCrDiscoveryServiceRegistry theCdsCrDiscoveryServiceRegistry) { + return id -> { + if (myRepositoryFactory == null) { + return null; + } + RequestDetails rd = + theCdsConfigService.createRequestDetails(theFhirContext, id, PLAN_DEFINITION_RESOURCE_NAME); + Repository repository = myRepositoryFactory.create(rd); + Optional> clazz = theCdsCrDiscoveryServiceRegistry.find( + theFhirContext.getVersion().getVersion()); + if (clazz.isEmpty()) { + return null; + } + try { + Constructor constructor = + clazz.get().getConstructor(IIdType.class, Repository.class); + return constructor.newInstance(rd.getId(), repository); + } catch (NoSuchMethodException + | InvocationTargetException + | InstantiationException + | IllegalAccessException e) { + ourLog.error("Error encountered attempting to construct the CrDiscoveryService: " + e.getMessage()); + return null; + } + }; + } + + @Bean + public CdsServiceInterceptor cdsServiceInterceptor() { + if (myResourceChangeListenerRegistry == null) { + return null; + } + CdsServiceInterceptor listener = new CdsServiceInterceptor(); + myResourceChangeListenerRegistry.registerResourceResourceChangeListener( + PLAN_DEFINITION_RESOURCE_NAME, SearchParameterMap.newSynchronous(), listener, 1000); + return listener; } @Bean public ICdsConfigService cdsConfigService( - FhirContext theFhirContext, @Qualifier(CDS_HOOKS_OBJECT_MAPPER_FACTORY) ObjectMapper theObjectMapper) { - return new CdsConfigServiceImpl(theFhirContext, theObjectMapper, myDaoRegistry); + FhirContext theFhirContext, + @Qualifier(CDS_HOOKS_OBJECT_MAPPER_FACTORY) ObjectMapper theObjectMapper, + CdsCrSettings theCdsCrSettings) { + return new CdsConfigServiceImpl( + theFhirContext, theObjectMapper, theCdsCrSettings, myDaoRegistry, myRepositoryFactory, myRestfulServer); } @Bean diff --git a/hapi-fhir-server-cds-hooks/src/main/java/ca/uhn/hapi/fhir/cdshooks/svc/BaseCdsCrMethod.java b/hapi-fhir-server-cds-hooks/src/main/java/ca/uhn/hapi/fhir/cdshooks/svc/BaseCdsCrMethod.java new file mode 100644 index 00000000000..53f43879fad --- /dev/null +++ b/hapi-fhir-server-cds-hooks/src/main/java/ca/uhn/hapi/fhir/cdshooks/svc/BaseCdsCrMethod.java @@ -0,0 +1,47 @@ +/*- + * #%L + * HAPI FHIR - CDS Hooks + * %% + * Copyright (C) 2014 - 2023 Smile CDR, Inc. + * %% + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * #L% + */ +package ca.uhn.hapi.fhir.cdshooks.svc; + +import ca.uhn.fhir.context.ConfigurationException; +import ca.uhn.fhir.i18n.Msg; +import ca.uhn.fhir.model.api.IModelJson; +import ca.uhn.fhir.rest.server.exceptions.BaseServerResponseException; +import ca.uhn.hapi.fhir.cdshooks.api.ICdsMethod; +import ca.uhn.hapi.fhir.cdshooks.svc.cr.ICdsCrServiceFactory; +import com.fasterxml.jackson.databind.ObjectMapper; + +abstract class BaseCdsCrMethod implements ICdsMethod { + private ICdsCrServiceFactory myCdsCrServiceFactory; + + public BaseCdsCrMethod(ICdsCrServiceFactory theCdsCrServiceFactory) { + myCdsCrServiceFactory = theCdsCrServiceFactory; + } + + public Object invoke(ObjectMapper theObjectMapper, IModelJson theJson, String theServiceId) { + try { + return myCdsCrServiceFactory.create(theServiceId).invoke(theJson); + } catch (Exception e) { + if (e.getCause() != null && e.getCause() instanceof BaseServerResponseException) { + throw (BaseServerResponseException) e.getCause(); + } + throw new ConfigurationException(Msg.code(2434) + "Failed to invoke $apply on " + theServiceId, e); + } + } +} diff --git a/hapi-fhir-server-cds-hooks/src/main/java/ca/uhn/hapi/fhir/cdshooks/svc/BaseDynamicCdsServiceMethod.java b/hapi-fhir-server-cds-hooks/src/main/java/ca/uhn/hapi/fhir/cdshooks/svc/BaseDynamicCdsServiceMethod.java index e545a12b752..66f429a0697 100644 --- a/hapi-fhir-server-cds-hooks/src/main/java/ca/uhn/hapi/fhir/cdshooks/svc/BaseDynamicCdsServiceMethod.java +++ b/hapi-fhir-server-cds-hooks/src/main/java/ca/uhn/hapi/fhir/cdshooks/svc/BaseDynamicCdsServiceMethod.java @@ -24,9 +24,9 @@ import ca.uhn.hapi.fhir.cdshooks.api.ICdsMethod; import ca.uhn.hapi.fhir.cdshooks.api.json.CdsServiceRequestJson; import ca.uhn.hapi.fhir.cdshooks.api.json.CdsServiceResponseJson; import com.fasterxml.jackson.databind.ObjectMapper; +import jakarta.annotation.Nonnull; import java.util.function.Function; -import javax.annotation.Nonnull; abstract class BaseDynamicCdsServiceMethod implements ICdsMethod { private final Function myFunction; diff --git a/hapi-fhir-server-cds-hooks/src/main/java/ca/uhn/hapi/fhir/cdshooks/svc/CdsConfigServiceImpl.java b/hapi-fhir-server-cds-hooks/src/main/java/ca/uhn/hapi/fhir/cdshooks/svc/CdsConfigServiceImpl.java index c97b69f2760..dd0f79b62f3 100644 --- a/hapi-fhir-server-cds-hooks/src/main/java/ca/uhn/hapi/fhir/cdshooks/svc/CdsConfigServiceImpl.java +++ b/hapi-fhir-server-cds-hooks/src/main/java/ca/uhn/hapi/fhir/cdshooks/svc/CdsConfigServiceImpl.java @@ -20,25 +20,36 @@ package ca.uhn.hapi.fhir.cdshooks.svc; import ca.uhn.fhir.context.FhirContext; +import ca.uhn.fhir.cr.common.IRepositoryFactory; import ca.uhn.fhir.jpa.api.dao.DaoRegistry; +import ca.uhn.fhir.rest.server.RestfulServer; import ca.uhn.hapi.fhir.cdshooks.api.ICdsConfigService; +import ca.uhn.hapi.fhir.cdshooks.svc.cr.CdsCrSettings; import com.fasterxml.jackson.databind.ObjectMapper; - -import javax.annotation.Nonnull; -import javax.annotation.Nullable; +import jakarta.annotation.Nonnull; +import jakarta.annotation.Nullable; public class CdsConfigServiceImpl implements ICdsConfigService { private final FhirContext myFhirContext; private final ObjectMapper myObjectMapper; + private final CdsCrSettings myCdsCrSettings; private final DaoRegistry myDaoRegistry; + private final IRepositoryFactory myRepositoryFactory; + private final RestfulServer myRestfulServer; public CdsConfigServiceImpl( @Nonnull FhirContext theFhirContext, @Nonnull ObjectMapper theObjectMapper, - @Nullable DaoRegistry theDaoRegistry) { + @Nonnull CdsCrSettings theCdsCrSettings, + @Nullable DaoRegistry theDaoRegistry, + @Nullable IRepositoryFactory theRepositoryFactory, + @Nullable RestfulServer theRestfulServer) { myFhirContext = theFhirContext; myObjectMapper = theObjectMapper; + myCdsCrSettings = theCdsCrSettings; myDaoRegistry = theDaoRegistry; + myRepositoryFactory = theRepositoryFactory; + myRestfulServer = theRestfulServer; } @Nonnull @@ -53,9 +64,27 @@ public class CdsConfigServiceImpl implements ICdsConfigService { return myObjectMapper; } + @Nonnull + @Override + public CdsCrSettings getCdsCrSettings() { + return myCdsCrSettings; + } + @Nullable @Override public DaoRegistry getDaoRegistry() { return myDaoRegistry; } + + @Nullable + @Override + public IRepositoryFactory getRepositoryFactory() { + return myRepositoryFactory; + } + + @Nullable + @Override + public RestfulServer getRestfulServer() { + return myRestfulServer; + } } diff --git a/hapi-fhir-server-cds-hooks/src/main/java/ca/uhn/hapi/fhir/cdshooks/svc/CdsCrServiceMethod.java b/hapi-fhir-server-cds-hooks/src/main/java/ca/uhn/hapi/fhir/cdshooks/svc/CdsCrServiceMethod.java new file mode 100644 index 00000000000..98f41f2291e --- /dev/null +++ b/hapi-fhir-server-cds-hooks/src/main/java/ca/uhn/hapi/fhir/cdshooks/svc/CdsCrServiceMethod.java @@ -0,0 +1,45 @@ +/*- + * #%L + * HAPI FHIR - CDS Hooks + * %% + * Copyright (C) 2014 - 2023 Smile CDR, Inc. + * %% + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * #L% + */ +package ca.uhn.hapi.fhir.cdshooks.svc; + +import ca.uhn.hapi.fhir.cdshooks.api.ICdsServiceMethod; +import ca.uhn.hapi.fhir.cdshooks.api.json.CdsServiceJson; +import ca.uhn.hapi.fhir.cdshooks.svc.cr.ICdsCrServiceFactory; + +public class CdsCrServiceMethod extends BaseCdsCrMethod implements ICdsServiceMethod { + private final CdsServiceJson myCdsServiceJson; + + public CdsCrServiceMethod(CdsServiceJson theCdsServiceJson, ICdsCrServiceFactory theCdsCrServiceFactory) { + super(theCdsCrServiceFactory); + myCdsServiceJson = theCdsServiceJson; + } + + @Override + public CdsServiceJson getCdsServiceJson() { + return myCdsServiceJson; + } + + @Override + public boolean isAllowAutoFhirClientPrefetch() { + // The $apply operation will make FHIR requests for any data it needs + // directly against the fhirServer of the ServiceRequest. + return false; + } +} diff --git a/hapi-fhir-server-cds-hooks/src/main/java/ca/uhn/hapi/fhir/cdshooks/svc/CdsHooksContextBooter.java b/hapi-fhir-server-cds-hooks/src/main/java/ca/uhn/hapi/fhir/cdshooks/svc/CdsHooksContextBooter.java index 48e2fab4ab5..df6762d7151 100644 --- a/hapi-fhir-server-cds-hooks/src/main/java/ca/uhn/hapi/fhir/cdshooks/svc/CdsHooksContextBooter.java +++ b/hapi-fhir-server-cds-hooks/src/main/java/ca/uhn/hapi/fhir/cdshooks/svc/CdsHooksContextBooter.java @@ -28,6 +28,7 @@ import ca.uhn.hapi.fhir.cdshooks.api.CdsServicePrefetch; import ca.uhn.hapi.fhir.cdshooks.api.json.CdsServiceJson; import com.fasterxml.jackson.core.JsonProcessingException; import com.fasterxml.jackson.databind.ObjectMapper; +import jakarta.annotation.PreDestroy; import org.apache.commons.lang3.StringUtils; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -39,7 +40,6 @@ import java.util.Arrays; import java.util.Comparator; import java.util.List; import java.util.stream.Collectors; -import javax.annotation.PreDestroy; /** * This bean creates a customer-defined spring context which will diff --git a/hapi-fhir-server-cds-hooks/src/main/java/ca/uhn/hapi/fhir/cdshooks/svc/CdsServiceCache.java b/hapi-fhir-server-cds-hooks/src/main/java/ca/uhn/hapi/fhir/cdshooks/svc/CdsServiceCache.java index 660e9152288..eb89ff98bb6 100644 --- a/hapi-fhir-server-cds-hooks/src/main/java/ca/uhn/hapi/fhir/cdshooks/svc/CdsServiceCache.java +++ b/hapi-fhir-server-cds-hooks/src/main/java/ca/uhn/hapi/fhir/cdshooks/svc/CdsServiceCache.java @@ -25,6 +25,8 @@ import ca.uhn.hapi.fhir.cdshooks.api.json.CdsServiceJson; import ca.uhn.hapi.fhir.cdshooks.api.json.CdsServiceRequestJson; import ca.uhn.hapi.fhir.cdshooks.api.json.CdsServiceResponseJson; import ca.uhn.hapi.fhir.cdshooks.api.json.CdsServicesJson; +import ca.uhn.hapi.fhir.cdshooks.svc.cr.ICdsCrServiceFactory; +import ca.uhn.hapi.fhir.cdshooks.svc.cr.discovery.ICrDiscoveryServiceFactory; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -33,6 +35,8 @@ import java.util.LinkedHashMap; import java.util.Map; import java.util.function.Function; +import static ca.uhn.hapi.fhir.cdshooks.svc.cr.CdsCrConstants.CDS_CR_MODULE_ID; + public class CdsServiceCache { static final Logger ourLog = LoggerFactory.getLogger(CdsServiceCache.class); final Map myServiceMap = new LinkedHashMap<>(); @@ -66,6 +70,23 @@ public class CdsServiceCache { } } + public void registerCrService( + String theServiceId, + ICrDiscoveryServiceFactory theDiscoveryServiceFactory, + ICdsCrServiceFactory theCrServiceFactory) { + if (!isCdsServiceAlreadyRegistered(theServiceId, CDS_CR_MODULE_ID)) { + CdsServiceJson cdsServiceJson = + theDiscoveryServiceFactory.create(theServiceId).resolveService(); + if (cdsServiceJson != null) { + final CdsCrServiceMethod cdsCrServiceMethod = + new CdsCrServiceMethod(cdsServiceJson, theCrServiceFactory); + myServiceMap.put(theServiceId, cdsCrServiceMethod); + myCdsServiceJson.addService(cdsServiceJson); + ourLog.info("Created service for {}", theServiceId); + } + } + } + public void registerFeedback(String theServiceId, Object theServiceBean, Method theMethod) { final CdsFeedbackMethod cdsFeedbackMethod = new CdsFeedbackMethod(theServiceBean, theMethod); myFeedbackMap.put(theServiceId, cdsFeedbackMethod); diff --git a/hapi-fhir-server-cds-hooks/src/main/java/ca/uhn/hapi/fhir/cdshooks/svc/CdsServiceRegistryImpl.java b/hapi-fhir-server-cds-hooks/src/main/java/ca/uhn/hapi/fhir/cdshooks/svc/CdsServiceRegistryImpl.java index 69429a47f96..a421f2d3757 100644 --- a/hapi-fhir-server-cds-hooks/src/main/java/ca/uhn/hapi/fhir/cdshooks/svc/CdsServiceRegistryImpl.java +++ b/hapi-fhir-server-cds-hooks/src/main/java/ca/uhn/hapi/fhir/cdshooks/svc/CdsServiceRegistryImpl.java @@ -30,16 +30,18 @@ import ca.uhn.hapi.fhir.cdshooks.api.json.CdsServiceJson; import ca.uhn.hapi.fhir.cdshooks.api.json.CdsServiceRequestJson; import ca.uhn.hapi.fhir.cdshooks.api.json.CdsServiceResponseJson; import ca.uhn.hapi.fhir.cdshooks.api.json.CdsServicesJson; +import ca.uhn.hapi.fhir.cdshooks.svc.cr.ICdsCrServiceFactory; +import ca.uhn.hapi.fhir.cdshooks.svc.cr.discovery.ICrDiscoveryServiceFactory; import ca.uhn.hapi.fhir.cdshooks.svc.prefetch.CdsPrefetchSvc; import com.fasterxml.jackson.core.JsonProcessingException; import com.fasterxml.jackson.databind.ObjectMapper; +import jakarta.annotation.Nonnull; +import jakarta.annotation.PostConstruct; import org.apache.commons.lang3.Validate; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import java.util.function.Function; -import javax.annotation.Nonnull; -import javax.annotation.PostConstruct; public class CdsServiceRegistryImpl implements ICdsServiceRegistry { private static final Logger ourLog = LoggerFactory.getLogger(CdsServiceRegistryImpl.class); @@ -49,14 +51,20 @@ public class CdsServiceRegistryImpl implements ICdsServiceRegistry { private final CdsHooksContextBooter myCdsHooksContextBooter; private final CdsPrefetchSvc myCdsPrefetchSvc; private final ObjectMapper myObjectMapper; + private final ICdsCrServiceFactory myCdsCrServiceFactory; + private final ICrDiscoveryServiceFactory myCrDiscoveryServiceFactory; public CdsServiceRegistryImpl( CdsHooksContextBooter theCdsHooksContextBooter, CdsPrefetchSvc theCdsPrefetchSvc, - ObjectMapper theObjectMapper) { + ObjectMapper theObjectMapper, + ICdsCrServiceFactory theCdsCrServiceFactory, + ICrDiscoveryServiceFactory theCrDiscoveryServiceFactory) { myCdsHooksContextBooter = theCdsHooksContextBooter; myCdsPrefetchSvc = theCdsPrefetchSvc; myObjectMapper = theObjectMapper; + myCdsCrServiceFactory = theCdsCrServiceFactory; + myCrDiscoveryServiceFactory = theCrDiscoveryServiceFactory; } @PostConstruct @@ -142,6 +150,17 @@ public class CdsServiceRegistryImpl implements ICdsServiceRegistry { theServiceId, theServiceFunction, theCdsServiceJson, theAllowAutoFhirClientPrefetch, theModuleId); } + @Override + public boolean registerCrService(String theServiceId) { + try { + myServiceCache.registerCrService(theServiceId, myCrDiscoveryServiceFactory, myCdsCrServiceFactory); + } catch (Exception e) { + ourLog.error("Error received during CR CDS Service registration: {}", e.getMessage()); + return false; + } + return true; + } + @Override public void unregisterService(String theServiceId, String theModuleId) { Validate.notNull(theServiceId); diff --git a/hapi-fhir-server-cds-hooks/src/main/java/ca/uhn/hapi/fhir/cdshooks/svc/cr/CdsCrConstants.java b/hapi-fhir-server-cds-hooks/src/main/java/ca/uhn/hapi/fhir/cdshooks/svc/cr/CdsCrConstants.java new file mode 100644 index 00000000000..62615852d76 --- /dev/null +++ b/hapi-fhir-server-cds-hooks/src/main/java/ca/uhn/hapi/fhir/cdshooks/svc/cr/CdsCrConstants.java @@ -0,0 +1,48 @@ +/*- + * #%L + * HAPI FHIR - CDS Hooks + * %% + * Copyright (C) 2014 - 2023 Smile CDR, Inc. + * %% + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * #L% + */ +package ca.uhn.hapi.fhir.cdshooks.svc.cr; + +public class CdsCrConstants { + private CdsCrConstants() {} + + public static final String CDS_CR_MODULE_ID = "CR"; + + // CDS Hook field names + public static final String CDS_PARAMETER_USER_ID = "userId"; + public static final String CDS_PARAMETER_PATIENT_ID = "patientId"; + public static final String CDS_PARAMETER_ENCOUNTER_ID = "encounterId"; + public static final String CDS_PARAMETER_MEDICATIONS = "medications"; + public static final String CDS_PARAMETER_PERFORMER = "performer"; + public static final String CDS_PARAMETER_TASK = "task"; + public static final String CDS_PARAMETER_ORDERS = "orders"; + public static final String CDS_PARAMETER_SELECTIONS = "selections"; + public static final String CDS_PARAMETER_DRAFT_ORDERS = "draftOrders"; + public static final String CDS_PARAMETER_APPOINTMENTS = "appointments"; + + // $apply parameter names + public static final String APPLY_PARAMETER_PLAN_DEFINITION = "planDefinition"; + public static final String APPLY_PARAMETER_CANONICAL = "canonical"; + public static final String APPLY_PARAMETER_SUBJECT = "subject"; + public static final String APPLY_PARAMETER_PRACTITIONER = "practitioner"; + public static final String APPLY_PARAMETER_ENCOUNTER = "encounter"; + public static final String APPLY_PARAMETER_PARAMETERS = "parameters"; + public static final String APPLY_PARAMETER_DATA = "data"; + public static final String APPLY_PARAMETER_DATA_ENDPOINT = "dataEndpoint"; +} diff --git a/hapi-fhir-server-cds-hooks/src/main/java/ca/uhn/hapi/fhir/cdshooks/svc/cr/CdsCrServiceDstu3.java b/hapi-fhir-server-cds-hooks/src/main/java/ca/uhn/hapi/fhir/cdshooks/svc/cr/CdsCrServiceDstu3.java new file mode 100644 index 00000000000..5a6c97375ad --- /dev/null +++ b/hapi-fhir-server-cds-hooks/src/main/java/ca/uhn/hapi/fhir/cdshooks/svc/cr/CdsCrServiceDstu3.java @@ -0,0 +1,309 @@ +/*- + * #%L + * HAPI FHIR - CDS Hooks + * %% + * Copyright (C) 2014 - 2023 Smile CDR, Inc. + * %% + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * #L% + */ +package ca.uhn.hapi.fhir.cdshooks.svc.cr; + +import ca.uhn.fhir.context.FhirVersionEnum; +import ca.uhn.fhir.rest.api.server.RequestDetails; +import ca.uhn.hapi.fhir.cdshooks.api.ICdsConfigService; +import ca.uhn.hapi.fhir.cdshooks.api.json.CdsServiceRequestAuthorizationJson; +import ca.uhn.hapi.fhir.cdshooks.api.json.CdsServiceRequestJson; +import ca.uhn.hapi.fhir.cdshooks.api.json.CdsServiceResponseCardJson; +import ca.uhn.hapi.fhir.cdshooks.api.json.CdsServiceResponseCardSourceJson; +import ca.uhn.hapi.fhir.cdshooks.api.json.CdsServiceResponseJson; +import ca.uhn.hapi.fhir.cdshooks.api.json.CdsServiceResponseLinkJson; +import ca.uhn.hapi.fhir.cdshooks.api.json.CdsServiceResponseSuggestionActionJson; +import ca.uhn.hapi.fhir.cdshooks.api.json.CdsServiceResponseSuggestionJson; +import ca.uhn.hapi.fhir.cdshooks.api.json.CdsServiceResponseSystemActionJson; +import org.hl7.fhir.dstu3.model.Bundle; +import org.hl7.fhir.dstu3.model.CarePlan; +import org.hl7.fhir.dstu3.model.Endpoint; +import org.hl7.fhir.dstu3.model.Extension; +import org.hl7.fhir.dstu3.model.IdType; +import org.hl7.fhir.dstu3.model.ParameterDefinition; +import org.hl7.fhir.dstu3.model.Parameters; +import org.hl7.fhir.dstu3.model.PlanDefinition; +import org.hl7.fhir.dstu3.model.Reference; +import org.hl7.fhir.dstu3.model.RelatedArtifact; +import org.hl7.fhir.dstu3.model.RequestGroup; +import org.hl7.fhir.dstu3.model.Resource; +import org.hl7.fhir.dstu3.model.StringType; +import org.hl7.fhir.instance.model.api.IBaseResource; +import org.opencds.cqf.fhir.api.Repository; +import org.opencds.cqf.fhir.utility.Canonicals; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import static ca.uhn.hapi.fhir.cdshooks.svc.cr.CdsCrConstants.APPLY_PARAMETER_DATA; +import static ca.uhn.hapi.fhir.cdshooks.svc.cr.CdsCrConstants.APPLY_PARAMETER_DATA_ENDPOINT; +import static ca.uhn.hapi.fhir.cdshooks.svc.cr.CdsCrConstants.APPLY_PARAMETER_ENCOUNTER; +import static ca.uhn.hapi.fhir.cdshooks.svc.cr.CdsCrConstants.APPLY_PARAMETER_PARAMETERS; +import static ca.uhn.hapi.fhir.cdshooks.svc.cr.CdsCrConstants.APPLY_PARAMETER_PRACTITIONER; +import static ca.uhn.hapi.fhir.cdshooks.svc.cr.CdsCrConstants.APPLY_PARAMETER_SUBJECT; +import static ca.uhn.hapi.fhir.cdshooks.svc.cr.CdsCrConstants.CDS_PARAMETER_DRAFT_ORDERS; +import static ca.uhn.hapi.fhir.cdshooks.svc.cr.CdsCrConstants.CDS_PARAMETER_ENCOUNTER_ID; +import static ca.uhn.hapi.fhir.cdshooks.svc.cr.CdsCrConstants.CDS_PARAMETER_PATIENT_ID; +import static ca.uhn.hapi.fhir.cdshooks.svc.cr.CdsCrConstants.CDS_PARAMETER_USER_ID; +import static org.opencds.cqf.fhir.utility.dstu3.Parameters.parameters; +import static org.opencds.cqf.fhir.utility.dstu3.Parameters.part; + +public class CdsCrServiceDstu3 implements ICdsCrService { + protected final RequestDetails myRequestDetails; + protected final Repository myRepository; + protected final ICdsConfigService myCdsConfigService; + protected CarePlan myResponse; + protected CdsServiceResponseJson myServiceResponse; + + public CdsCrServiceDstu3( + RequestDetails theRequestDetails, Repository theRepository, ICdsConfigService theCdsConfigService) { + myCdsConfigService = theCdsConfigService; + myRequestDetails = theRequestDetails; + myRepository = theRepository; + } + + public FhirVersionEnum getFhirVersion() { + return FhirVersionEnum.DSTU3; + } + + public Repository getRepository() { + return myRepository; + } + + public Parameters encodeParams(CdsServiceRequestJson theJson) { + Parameters parameters = parameters() + .addParameter(part(APPLY_PARAMETER_SUBJECT, theJson.getContext().getString(CDS_PARAMETER_PATIENT_ID))); + if (theJson.getContext().containsKey(CDS_PARAMETER_USER_ID)) { + parameters.addParameter( + part(APPLY_PARAMETER_PRACTITIONER, theJson.getContext().getString(CDS_PARAMETER_USER_ID))); + } + if (theJson.getContext().containsKey(CDS_PARAMETER_ENCOUNTER_ID)) { + parameters.addParameter( + part(APPLY_PARAMETER_ENCOUNTER, theJson.getContext().getString(CDS_PARAMETER_ENCOUNTER_ID))); + } + var cqlParameters = parameters(); + if (theJson.getContext().containsKey(CDS_PARAMETER_DRAFT_ORDERS)) { + addCqlParameters( + cqlParameters, + theJson.getContext().getResource(CDS_PARAMETER_DRAFT_ORDERS), + CDS_PARAMETER_DRAFT_ORDERS); + } + if (cqlParameters.hasParameter()) { + parameters.addParameter(part(APPLY_PARAMETER_PARAMETERS, cqlParameters)); + } + Bundle data = getPrefetchResources(theJson); + if (data.hasEntry()) { + parameters.addParameter(part(APPLY_PARAMETER_DATA, data)); + } + if (theJson.getFhirServer() != null) { + Endpoint endpoint = new Endpoint().setAddress(theJson.getFhirServer()); + if (theJson.getServiceRequestAuthorizationJson().getAccessToken() != null) { + String tokenType = getTokenType(theJson.getServiceRequestAuthorizationJson()); + endpoint.addHeader(String.format( + "Authorization: %s %s", + tokenType, theJson.getServiceRequestAuthorizationJson().getAccessToken())); + if (theJson.getServiceRequestAuthorizationJson().getSubject() != null) { + endpoint.addHeader(String.format( + "%s: %s", + myCdsConfigService.getCdsCrSettings().getClientIdHeaderName(), + theJson.getServiceRequestAuthorizationJson().getSubject())); + } + } + parameters.addParameter(part(APPLY_PARAMETER_DATA_ENDPOINT, endpoint)); + } + return parameters; + } + + protected String getTokenType(CdsServiceRequestAuthorizationJson theJson) { + String tokenType = theJson.getTokenType(); + return tokenType == null || tokenType.isEmpty() ? "Bearer" : tokenType; + } + + protected Parameters addCqlParameters( + Parameters theParameters, IBaseResource theContextResource, String theParamName) { + // We are making the assumption that a Library created for a hook will provide parameters for the fields + // specified for the hook + if (theContextResource instanceof Bundle) { + ((Bundle) theContextResource) + .getEntry() + .forEach(x -> theParameters.addParameter(part(theParamName, x.getResource()))); + } else { + theParameters.addParameter(part(theParamName, (Resource) theContextResource)); + } + if (theParameters.getParameter().size() == 1) { + Extension listExtension = new Extension( + "http://hl7.org/fhir/uv/cpg/StructureDefinition/cpg-parameterDefinition", + new ParameterDefinition() + .setMax("*") + .setName(theParameters.getParameterFirstRep().getName())); + theParameters.getParameterFirstRep().addExtension(listExtension); + } + return theParameters; + } + + protected Map getResourcesFromBundle(Bundle theBundle) { + // using HashMap to avoid duplicates + Map resourceMap = new HashMap<>(); + theBundle + .getEntry() + .forEach(x -> resourceMap.put(x.fhirType() + x.getResource().getId(), x.getResource())); + return resourceMap; + } + + protected Bundle getPrefetchResources(CdsServiceRequestJson theJson) { + // using HashMap to avoid duplicates + Map resourceMap = new HashMap<>(); + Bundle prefetchResources = new Bundle(); + Resource resource; + for (String key : theJson.getPrefetchKeys()) { + resource = (Resource) theJson.getPrefetch(key); + if (resource == null) { + continue; + } + if (resource instanceof Bundle) { + resourceMap.putAll(getResourcesFromBundle((Bundle) resource)); + } else { + resourceMap.put(resource.fhirType() + resource.getId(), resource); + } + } + resourceMap.forEach((key, value) -> prefetchResources.addEntry().setResource(value)); + return prefetchResources; + } + + public CdsServiceResponseJson encodeResponse(Object theResponse) { + assert theResponse instanceof CarePlan; + myResponse = (CarePlan) theResponse; + CdsServiceResponseJson serviceResponse = new CdsServiceResponseJson(); + if (myResponse.hasActivity()) { + Reference requestGroupRef = myResponse.getActivity().get(0).getReference(); + RequestGroup mainRequest = (RequestGroup) resolveResource(requestGroupRef); + StringType canonical = mainRequest.getDefinition().get(0).getReferenceElement_(); + PlanDefinition planDef = myRepository.read( + PlanDefinition.class, + new IdType(Canonicals.getResourceType(canonical), Canonicals.getIdPart(canonical))); + List links = resolvePlanLinks(planDef); + mainRequest.getAction().forEach(action -> serviceResponse.addCard(resolveAction(action, links))); + } + + return serviceResponse; + } + + protected List resolvePlanLinks(PlanDefinition thePlanDefinition) { + List links = new ArrayList<>(); + // links - listed on each card + if (thePlanDefinition.hasRelatedArtifact()) { + thePlanDefinition.getRelatedArtifact().forEach(ra -> { + String linkUrl = ra.getUrl(); + if (linkUrl != null) { + CdsServiceResponseLinkJson link = new CdsServiceResponseLinkJson().setUrl(linkUrl); + if (ra.hasDisplay()) { + link.setLabel(ra.getDisplay()); + } + if (ra.hasExtension()) { + link.setType(ra.getExtensionFirstRep().getValue().primitiveValue()); + } else link.setType("absolute"); // default + links.add(link); + } + }); + } + return links; + } + + protected CdsServiceResponseCardJson resolveAction( + RequestGroup.RequestGroupActionComponent theAction, List theLinks) { + CdsServiceResponseCardJson card = new CdsServiceResponseCardJson() + .setSummary(theAction.getTitle()) + .setDetail(theAction.getDescription()) + .setLinks(theLinks); + + if (theAction.hasDocumentation()) { + card.setSource(resolveSource(theAction)); + } + + if (theAction.hasSelectionBehavior()) { + card.setSelectionBehaviour(theAction.getSelectionBehavior().toCode()); + theAction.getAction().forEach(action -> resolveSuggestion(action)); + } + + // Leaving this out until the spec details how to map system actions. + // if (theAction.hasType() && theAction.hasResource()) { + // resolveSystemAction(theAction); + // } + + return card; + } + + protected void resolveSystemAction(RequestGroup.RequestGroupActionComponent theAction) { + if (theAction.hasType() + && theAction.getType().hasCode() + && !theAction.getType().getCode().equals("fire-event")) { + myServiceResponse.addServiceAction(new CdsServiceResponseSystemActionJson() + .setResource(resolveResource(theAction.getResource())) + .setType(theAction.getType().getCode())); + } + } + + protected CdsServiceResponseCardSourceJson resolveSource(RequestGroup.RequestGroupActionComponent theAction) { + RelatedArtifact documentation = theAction.getDocumentationFirstRep(); + CdsServiceResponseCardSourceJson source = new CdsServiceResponseCardSourceJson() + .setLabel(documentation.getDisplay()) + .setUrl(documentation.getUrl()); + + if (documentation.hasDocument() && documentation.getDocument().hasUrl()) { + source.setIcon(documentation.getDocument().getUrl()); + } + + return source; + } + + protected CdsServiceResponseSuggestionJson resolveSuggestion(RequestGroup.RequestGroupActionComponent theAction) { + CdsServiceResponseSuggestionJson suggestion = new CdsServiceResponseSuggestionJson() + .setLabel(theAction.getTitle()) + .setUuid(theAction.getId()); + theAction.getAction().forEach(action -> suggestion.addAction(resolveSuggestionAction(action))); + + return suggestion; + } + + protected CdsServiceResponseSuggestionActionJson resolveSuggestionAction( + RequestGroup.RequestGroupActionComponent theAction) { + CdsServiceResponseSuggestionActionJson suggestionAction = + new CdsServiceResponseSuggestionActionJson().setDescription(theAction.getDescription()); + if (theAction.hasType() + && theAction.getType().hasCode() + && !theAction.getType().getCode().equals("fire-event")) { + String actionCode = theAction.getType().getCode(); + suggestionAction.setType(actionCode); + } + if (theAction.hasResource()) { + suggestionAction.setResource(resolveResource(theAction.getResource())); + } + + return suggestionAction; + } + + protected IBaseResource resolveResource(Reference theReference) { + return myResponse.getContained().stream() + .filter(resource -> resource.getId().equals(theReference.getReference())) + .findFirst() + .orElse(null); + } +} diff --git a/hapi-fhir-server-cds-hooks/src/main/java/ca/uhn/hapi/fhir/cdshooks/svc/cr/CdsCrServiceR4.java b/hapi-fhir-server-cds-hooks/src/main/java/ca/uhn/hapi/fhir/cdshooks/svc/cr/CdsCrServiceR4.java new file mode 100644 index 00000000000..920f63e319f --- /dev/null +++ b/hapi-fhir-server-cds-hooks/src/main/java/ca/uhn/hapi/fhir/cdshooks/svc/cr/CdsCrServiceR4.java @@ -0,0 +1,352 @@ +/*- + * #%L + * HAPI FHIR - CDS Hooks + * %% + * Copyright (C) 2014 - 2023 Smile CDR, Inc. + * %% + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * #L% + */ +package ca.uhn.hapi.fhir.cdshooks.svc.cr; + +import ca.uhn.fhir.context.FhirVersionEnum; +import ca.uhn.fhir.i18n.Msg; +import ca.uhn.fhir.rest.api.server.RequestDetails; +import ca.uhn.hapi.fhir.cdshooks.api.ICdsConfigService; +import ca.uhn.hapi.fhir.cdshooks.api.json.CdsServiceIndicatorEnum; +import ca.uhn.hapi.fhir.cdshooks.api.json.CdsServiceRequestAuthorizationJson; +import ca.uhn.hapi.fhir.cdshooks.api.json.CdsServiceRequestJson; +import ca.uhn.hapi.fhir.cdshooks.api.json.CdsServiceResponseCardJson; +import ca.uhn.hapi.fhir.cdshooks.api.json.CdsServiceResponseCardSourceJson; +import ca.uhn.hapi.fhir.cdshooks.api.json.CdsServiceResponseJson; +import ca.uhn.hapi.fhir.cdshooks.api.json.CdsServiceResponseLinkJson; +import ca.uhn.hapi.fhir.cdshooks.api.json.CdsServiceResponseSuggestionActionJson; +import ca.uhn.hapi.fhir.cdshooks.api.json.CdsServiceResponseSuggestionJson; +import ca.uhn.hapi.fhir.cdshooks.api.json.CdsServiceResponseSystemActionJson; +import org.hl7.fhir.instance.model.api.IBaseResource; +import org.hl7.fhir.r4.model.Bundle; +import org.hl7.fhir.r4.model.CanonicalType; +import org.hl7.fhir.r4.model.Endpoint; +import org.hl7.fhir.r4.model.Extension; +import org.hl7.fhir.r4.model.IdType; +import org.hl7.fhir.r4.model.ParameterDefinition; +import org.hl7.fhir.r4.model.Parameters; +import org.hl7.fhir.r4.model.PlanDefinition; +import org.hl7.fhir.r4.model.Reference; +import org.hl7.fhir.r4.model.RelatedArtifact; +import org.hl7.fhir.r4.model.RequestGroup; +import org.hl7.fhir.r4.model.Resource; +import org.opencds.cqf.fhir.api.Repository; +import org.opencds.cqf.fhir.utility.Canonicals; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.stream.Collectors; + +import static ca.uhn.hapi.fhir.cdshooks.svc.cr.CdsCrConstants.APPLY_PARAMETER_DATA; +import static ca.uhn.hapi.fhir.cdshooks.svc.cr.CdsCrConstants.APPLY_PARAMETER_DATA_ENDPOINT; +import static ca.uhn.hapi.fhir.cdshooks.svc.cr.CdsCrConstants.APPLY_PARAMETER_ENCOUNTER; +import static ca.uhn.hapi.fhir.cdshooks.svc.cr.CdsCrConstants.APPLY_PARAMETER_PARAMETERS; +import static ca.uhn.hapi.fhir.cdshooks.svc.cr.CdsCrConstants.APPLY_PARAMETER_PRACTITIONER; +import static ca.uhn.hapi.fhir.cdshooks.svc.cr.CdsCrConstants.APPLY_PARAMETER_SUBJECT; +import static ca.uhn.hapi.fhir.cdshooks.svc.cr.CdsCrConstants.CDS_PARAMETER_DRAFT_ORDERS; +import static ca.uhn.hapi.fhir.cdshooks.svc.cr.CdsCrConstants.CDS_PARAMETER_ENCOUNTER_ID; +import static ca.uhn.hapi.fhir.cdshooks.svc.cr.CdsCrConstants.CDS_PARAMETER_PATIENT_ID; +import static ca.uhn.hapi.fhir.cdshooks.svc.cr.CdsCrConstants.CDS_PARAMETER_USER_ID; +import static org.opencds.cqf.fhir.utility.r4.Parameters.parameters; +import static org.opencds.cqf.fhir.utility.r4.Parameters.part; + +public class CdsCrServiceR4 implements ICdsCrService { + protected final RequestDetails myRequestDetails; + protected final Repository myRepository; + protected final ICdsConfigService myCdsConfigService; + protected Bundle myResponseBundle; + protected CdsServiceResponseJson myServiceResponse; + + public CdsCrServiceR4( + RequestDetails theRequestDetails, Repository theRepository, ICdsConfigService theCdsConfigService) { + myCdsConfigService = theCdsConfigService; + myRequestDetails = theRequestDetails; + myRepository = theRepository; + } + + public FhirVersionEnum getFhirVersion() { + return FhirVersionEnum.R4; + } + + public Repository getRepository() { + return myRepository; + } + + public Parameters encodeParams(CdsServiceRequestJson theJson) { + Parameters parameters = parameters() + .addParameter(part(APPLY_PARAMETER_SUBJECT, theJson.getContext().getString(CDS_PARAMETER_PATIENT_ID))); + if (theJson.getContext().containsKey(CDS_PARAMETER_USER_ID)) { + parameters.addParameter( + part(APPLY_PARAMETER_PRACTITIONER, theJson.getContext().getString(CDS_PARAMETER_USER_ID))); + } + if (theJson.getContext().containsKey(CDS_PARAMETER_ENCOUNTER_ID)) { + parameters.addParameter( + part(APPLY_PARAMETER_ENCOUNTER, theJson.getContext().getString(CDS_PARAMETER_ENCOUNTER_ID))); + } + var cqlParameters = parameters(); + if (theJson.getContext().containsKey(CDS_PARAMETER_DRAFT_ORDERS)) { + addCqlParameters( + cqlParameters, + theJson.getContext().getResource(CDS_PARAMETER_DRAFT_ORDERS), + CDS_PARAMETER_DRAFT_ORDERS); + } + if (cqlParameters.hasParameter()) { + parameters.addParameter(part(APPLY_PARAMETER_PARAMETERS, cqlParameters)); + } + Bundle data = getPrefetchResources(theJson); + if (data.hasEntry()) { + parameters.addParameter(part(APPLY_PARAMETER_DATA, data)); + } + if (theJson.getFhirServer() != null) { + Endpoint endpoint = new Endpoint().setAddress(theJson.getFhirServer()); + if (theJson.getServiceRequestAuthorizationJson().getAccessToken() != null) { + String tokenType = getTokenType(theJson.getServiceRequestAuthorizationJson()); + endpoint.addHeader(String.format( + "Authorization: %s %s", + tokenType, theJson.getServiceRequestAuthorizationJson().getAccessToken())); + if (theJson.getServiceRequestAuthorizationJson().getSubject() != null) { + endpoint.addHeader(String.format( + "%s: %s", + myCdsConfigService.getCdsCrSettings().getClientIdHeaderName(), + theJson.getServiceRequestAuthorizationJson().getSubject())); + } + } + parameters.addParameter(part(APPLY_PARAMETER_DATA_ENDPOINT, endpoint)); + } + return parameters; + } + + protected String getTokenType(CdsServiceRequestAuthorizationJson theJson) { + String tokenType = theJson.getTokenType(); + return tokenType == null || tokenType.isEmpty() ? "Bearer" : tokenType; + } + + protected Parameters addCqlParameters( + Parameters theParameters, IBaseResource theContextResource, String theParamName) { + // We are making the assumption that a Library created for a hook will provide parameters for the fields + // specified for the hook + if (theContextResource instanceof Bundle) { + ((Bundle) theContextResource) + .getEntry() + .forEach(x -> theParameters.addParameter(part(theParamName, x.getResource()))); + } else { + theParameters.addParameter(part(theParamName, (Resource) theContextResource)); + } + if (theParameters.getParameter().size() == 1) { + Extension listExtension = new Extension( + "http://hl7.org/fhir/uv/cpg/StructureDefinition/cpg-parameterDefinition", + new ParameterDefinition() + .setMax("*") + .setName(theParameters.getParameterFirstRep().getName())); + theParameters.getParameterFirstRep().addExtension(listExtension); + } + return theParameters; + } + + protected Map getResourcesFromBundle(Bundle theBundle) { + // using HashMap to avoid duplicates + Map resourceMap = new HashMap<>(); + theBundle + .getEntry() + .forEach(x -> resourceMap.put(x.fhirType() + x.getResource().getId(), x.getResource())); + return resourceMap; + } + + protected Bundle getPrefetchResources(CdsServiceRequestJson theJson) { + // using HashMap to avoid duplicates + Map resourceMap = new HashMap<>(); + Bundle prefetchResources = new Bundle(); + Resource resource; + for (String key : theJson.getPrefetchKeys()) { + resource = (Resource) theJson.getPrefetch(key); + if (resource == null) { + continue; + } + if (resource instanceof Bundle) { + resourceMap.putAll(getResourcesFromBundle((Bundle) resource)); + } else { + resourceMap.put(resource.fhirType() + resource.getId(), resource); + } + } + resourceMap.forEach((key, value) -> prefetchResources.addEntry().setResource(value)); + return prefetchResources; + } + + public CdsServiceResponseJson encodeResponse(Object theResponse) { + assert theResponse instanceof Bundle; + myResponseBundle = (Bundle) theResponse; + myServiceResponse = new CdsServiceResponseJson(); + if (myResponseBundle.hasEntry()) { + RequestGroup mainRequest = + (RequestGroup) myResponseBundle.getEntry().get(0).getResource(); + CanonicalType canonical = mainRequest.getInstantiatesCanonical().get(0); + PlanDefinition planDef = myRepository.read( + PlanDefinition.class, + new IdType(Canonicals.getResourceType(canonical), Canonicals.getIdPart(canonical))); + List links = resolvePlanLinks(planDef); + mainRequest.getAction().forEach(action -> myServiceResponse.addCard(resolveAction(action, links))); + } + + return myServiceResponse; + } + + protected List resolvePlanLinks(PlanDefinition thePlanDefinition) { + List links = new ArrayList<>(); + // links - listed on each card + if (thePlanDefinition.hasRelatedArtifact()) { + thePlanDefinition.getRelatedArtifact().forEach(ra -> { + String linkUrl = ra.getUrl(); + if (linkUrl != null) { + CdsServiceResponseLinkJson link = new CdsServiceResponseLinkJson().setUrl(linkUrl); + if (ra.hasDisplay()) { + link.setLabel(ra.getDisplay()); + } + if (ra.hasExtension()) { + link.setType(ra.getExtensionFirstRep().getValue().primitiveValue()); + } else link.setType("absolute"); // default + links.add(link); + } + }); + } + return links; + } + + protected CdsServiceResponseCardJson resolveAction( + RequestGroup.RequestGroupActionComponent theAction, List theLinks) { + CdsServiceResponseCardJson card = new CdsServiceResponseCardJson() + .setSummary(theAction.getTitle()) + .setDetail(theAction.getDescription()) + .setLinks(theLinks); + + if (theAction.hasPriority()) { + card.setIndicator(resolveIndicator(theAction.getPriority().toCode())); + } + + if (theAction.hasDocumentation()) { + card.setSource(resolveSource(theAction)); + } + + if (theAction.hasSelectionBehavior()) { + card.setSelectionBehaviour(theAction.getSelectionBehavior().toCode()); + theAction.getAction().forEach(action -> resolveSuggestion(action)); + } + + // Leaving this out until the spec details how to map system actions. + // if (theAction.hasType() && theAction.hasResource()) { + // resolveSystemAction(theAction); + // } + + return card; + } + + protected CdsServiceIndicatorEnum resolveIndicator(String theCode) { + CdsServiceIndicatorEnum indicator; + switch (theCode) { + case "routine": + indicator = CdsServiceIndicatorEnum.INFO; + break; + case "urgent": + indicator = CdsServiceIndicatorEnum.WARNING; + break; + case "stat": + indicator = CdsServiceIndicatorEnum.CRITICAL; + break; + default: + indicator = null; + break; + } + if (indicator == null) { + // Code 2435-2440 are reserved for this error message across versions + throw new IllegalArgumentException(Msg.code(2435) + "Invalid priority code: " + theCode); + } + + return indicator; + } + + protected void resolveSystemAction(RequestGroup.RequestGroupActionComponent theAction) { + if (theAction.hasType() + && theAction.getType().hasCoding() + && theAction.getType().getCodingFirstRep().hasCode() + && !theAction.getType().getCodingFirstRep().getCode().equals("fire-event")) { + myServiceResponse.addServiceAction(new CdsServiceResponseSystemActionJson() + .setResource(resolveResource(theAction.getResource())) + .setType(theAction.getType().getCodingFirstRep().getCode())); + } + } + + protected CdsServiceResponseCardSourceJson resolveSource(RequestGroup.RequestGroupActionComponent theAction) { + RelatedArtifact documentation = theAction.getDocumentationFirstRep(); + CdsServiceResponseCardSourceJson source = new CdsServiceResponseCardSourceJson() + .setLabel(documentation.getDisplay()) + .setUrl(documentation.getUrl()); + + if (documentation.hasDocument() && documentation.getDocument().hasUrl()) { + source.setIcon(documentation.getDocument().getUrl()); + } + + return source; + } + + protected CdsServiceResponseSuggestionJson resolveSuggestion(RequestGroup.RequestGroupActionComponent theAction) { + CdsServiceResponseSuggestionJson suggestion = new CdsServiceResponseSuggestionJson() + .setLabel(theAction.getTitle()) + .setUuid(theAction.getId()); + theAction.getAction().forEach(action -> suggestion.addAction(resolveSuggestionAction(action))); + + return suggestion; + } + + protected CdsServiceResponseSuggestionActionJson resolveSuggestionAction( + RequestGroup.RequestGroupActionComponent theAction) { + CdsServiceResponseSuggestionActionJson suggestionAction = + new CdsServiceResponseSuggestionActionJson().setDescription(theAction.getDescription()); + if (theAction.hasType() + && theAction.getType().hasCoding() + && theAction.getType().getCodingFirstRep().hasCode() + && !theAction.getType().getCodingFirstRep().getCode().equals("fire-event")) { + String actionCode = theAction.getType().getCodingFirstRep().getCode(); + suggestionAction.setType(actionCode); + } + if (theAction.hasResource()) { + suggestionAction.setResource(resolveResource(theAction.getResource())); + // Leaving this out until the spec details how to map system actions. + // if (!suggestionAction.getType().isEmpty()) { + // resolveSystemAction(theAction); + // } + } + + return suggestionAction; + } + + protected IBaseResource resolveResource(Reference theReference) { + String reference = theReference.getReference(); + String[] split = reference.split("/"); + String id = reference.contains("/") ? split[1] : reference; + String resourceType = reference.contains("/") ? split[0] : theReference.getType(); + List results = myResponseBundle.getEntry().stream() + .filter(entry -> entry.hasResource() + && entry.getResource().getResourceType().toString().equals(resourceType) + && entry.getResource().getIdPart().equals(id)) + .map(entry -> entry.getResource()) + .collect(Collectors.toList()); + return results.isEmpty() ? null : results.get(0); + } +} diff --git a/hapi-fhir-server-cds-hooks/src/main/java/ca/uhn/hapi/fhir/cdshooks/svc/cr/CdsCrServiceR5.java b/hapi-fhir-server-cds-hooks/src/main/java/ca/uhn/hapi/fhir/cdshooks/svc/cr/CdsCrServiceR5.java new file mode 100644 index 00000000000..8f76db3a2af --- /dev/null +++ b/hapi-fhir-server-cds-hooks/src/main/java/ca/uhn/hapi/fhir/cdshooks/svc/cr/CdsCrServiceR5.java @@ -0,0 +1,356 @@ +/*- + * #%L + * HAPI FHIR - CDS Hooks + * %% + * Copyright (C) 2014 - 2023 Smile CDR, Inc. + * %% + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * #L% + */ +package ca.uhn.hapi.fhir.cdshooks.svc.cr; + +import ca.uhn.fhir.context.FhirVersionEnum; +import ca.uhn.fhir.i18n.Msg; +import ca.uhn.fhir.rest.api.server.RequestDetails; +import ca.uhn.hapi.fhir.cdshooks.api.ICdsConfigService; +import ca.uhn.hapi.fhir.cdshooks.api.json.CdsServiceIndicatorEnum; +import ca.uhn.hapi.fhir.cdshooks.api.json.CdsServiceRequestAuthorizationJson; +import ca.uhn.hapi.fhir.cdshooks.api.json.CdsServiceRequestJson; +import ca.uhn.hapi.fhir.cdshooks.api.json.CdsServiceResponseCardJson; +import ca.uhn.hapi.fhir.cdshooks.api.json.CdsServiceResponseCardSourceJson; +import ca.uhn.hapi.fhir.cdshooks.api.json.CdsServiceResponseJson; +import ca.uhn.hapi.fhir.cdshooks.api.json.CdsServiceResponseLinkJson; +import ca.uhn.hapi.fhir.cdshooks.api.json.CdsServiceResponseSuggestionActionJson; +import ca.uhn.hapi.fhir.cdshooks.api.json.CdsServiceResponseSuggestionJson; +import ca.uhn.hapi.fhir.cdshooks.api.json.CdsServiceResponseSystemActionJson; +import org.hl7.fhir.instance.model.api.IBaseResource; +import org.hl7.fhir.r5.model.Bundle; +import org.hl7.fhir.r5.model.CanonicalType; +import org.hl7.fhir.r5.model.Endpoint; +import org.hl7.fhir.r5.model.Extension; +import org.hl7.fhir.r5.model.IdType; +import org.hl7.fhir.r5.model.ParameterDefinition; +import org.hl7.fhir.r5.model.Parameters; +import org.hl7.fhir.r5.model.PlanDefinition; +import org.hl7.fhir.r5.model.Reference; +import org.hl7.fhir.r5.model.RelatedArtifact; +import org.hl7.fhir.r5.model.RequestOrchestration; +import org.hl7.fhir.r5.model.Resource; +import org.opencds.cqf.fhir.api.Repository; +import org.opencds.cqf.fhir.utility.Canonicals; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.stream.Collectors; + +import static ca.uhn.hapi.fhir.cdshooks.svc.cr.CdsCrConstants.APPLY_PARAMETER_DATA; +import static ca.uhn.hapi.fhir.cdshooks.svc.cr.CdsCrConstants.APPLY_PARAMETER_DATA_ENDPOINT; +import static ca.uhn.hapi.fhir.cdshooks.svc.cr.CdsCrConstants.APPLY_PARAMETER_ENCOUNTER; +import static ca.uhn.hapi.fhir.cdshooks.svc.cr.CdsCrConstants.APPLY_PARAMETER_PARAMETERS; +import static ca.uhn.hapi.fhir.cdshooks.svc.cr.CdsCrConstants.APPLY_PARAMETER_PRACTITIONER; +import static ca.uhn.hapi.fhir.cdshooks.svc.cr.CdsCrConstants.APPLY_PARAMETER_SUBJECT; +import static ca.uhn.hapi.fhir.cdshooks.svc.cr.CdsCrConstants.CDS_PARAMETER_DRAFT_ORDERS; +import static ca.uhn.hapi.fhir.cdshooks.svc.cr.CdsCrConstants.CDS_PARAMETER_ENCOUNTER_ID; +import static ca.uhn.hapi.fhir.cdshooks.svc.cr.CdsCrConstants.CDS_PARAMETER_PATIENT_ID; +import static ca.uhn.hapi.fhir.cdshooks.svc.cr.CdsCrConstants.CDS_PARAMETER_USER_ID; +import static org.opencds.cqf.fhir.utility.r5.Parameters.parameters; +import static org.opencds.cqf.fhir.utility.r5.Parameters.part; + +public class CdsCrServiceR5 implements ICdsCrService { + protected final RequestDetails myRequestDetails; + protected final Repository myRepository; + protected final ICdsConfigService myCdsConfigService; + protected Bundle myResponseBundle; + protected CdsServiceResponseJson myServiceResponse; + + public CdsCrServiceR5( + RequestDetails theRequestDetails, Repository theRepository, ICdsConfigService theCdsConfigService) { + myCdsConfigService = theCdsConfigService; + myRequestDetails = theRequestDetails; + myRepository = theRepository; + } + + public FhirVersionEnum getFhirVersion() { + return FhirVersionEnum.R5; + } + + public Repository getRepository() { + return myRepository; + } + + public Parameters encodeParams(CdsServiceRequestJson theJson) { + Parameters parameters = parameters() + .addParameter(part(APPLY_PARAMETER_SUBJECT, theJson.getContext().getString(CDS_PARAMETER_PATIENT_ID))); + if (theJson.getContext().containsKey(CDS_PARAMETER_USER_ID)) { + parameters.addParameter( + part(APPLY_PARAMETER_PRACTITIONER, theJson.getContext().getString(CDS_PARAMETER_USER_ID))); + } + if (theJson.getContext().containsKey(CDS_PARAMETER_ENCOUNTER_ID)) { + parameters.addParameter( + part(APPLY_PARAMETER_ENCOUNTER, theJson.getContext().getString(CDS_PARAMETER_ENCOUNTER_ID))); + } + var cqlParameters = parameters(); + if (theJson.getContext().containsKey(CDS_PARAMETER_DRAFT_ORDERS)) { + addCqlParameters( + cqlParameters, + theJson.getContext().getResource(CDS_PARAMETER_DRAFT_ORDERS), + CDS_PARAMETER_DRAFT_ORDERS); + } + if (cqlParameters.hasParameter()) { + parameters.addParameter(part(APPLY_PARAMETER_PARAMETERS, cqlParameters)); + } + Bundle data = getPrefetchResources(theJson); + if (data.hasEntry()) { + parameters.addParameter(part(APPLY_PARAMETER_DATA, data)); + } + if (theJson.getFhirServer() != null) { + Endpoint endpoint = new Endpoint().setAddress(theJson.getFhirServer()); + if (theJson.getServiceRequestAuthorizationJson().getAccessToken() != null) { + String tokenType = getTokenType(theJson.getServiceRequestAuthorizationJson()); + endpoint.addHeader(String.format( + "Authorization: %s %s", + tokenType, theJson.getServiceRequestAuthorizationJson().getAccessToken())); + if (theJson.getServiceRequestAuthorizationJson().getSubject() != null) { + endpoint.addHeader(String.format( + "%s: %s", + myCdsConfigService.getCdsCrSettings().getClientIdHeaderName(), + theJson.getServiceRequestAuthorizationJson().getSubject())); + } + } + parameters.addParameter(part(APPLY_PARAMETER_DATA_ENDPOINT, endpoint)); + } + return parameters; + } + + protected String getTokenType(CdsServiceRequestAuthorizationJson theJson) { + String tokenType = theJson.getTokenType(); + return tokenType == null || tokenType.isEmpty() ? "Bearer" : tokenType; + } + + protected Parameters addCqlParameters( + Parameters theParameters, IBaseResource theContextResource, String theParamName) { + // We are making the assumption that a Library created for a hook will provide parameters for the fields + // specified for the hook + if (theContextResource instanceof Bundle) { + ((Bundle) theContextResource) + .getEntry() + .forEach(x -> theParameters.addParameter(part(theParamName, x.getResource()))); + } else { + theParameters.addParameter(part(theParamName, (Resource) theContextResource)); + } + if (theParameters.getParameter().size() == 1) { + Extension listExtension = new Extension( + "http://hl7.org/fhir/uv/cpg/StructureDefinition/cpg-parameterDefinition", + new ParameterDefinition() + .setMax("*") + .setName(theParameters.getParameterFirstRep().getName())); + theParameters.getParameterFirstRep().addExtension(listExtension); + } + return theParameters; + } + + protected Map getResourcesFromBundle(Bundle theBundle) { + // using HashMap to avoid duplicates + Map resourceMap = new HashMap<>(); + theBundle + .getEntry() + .forEach(x -> resourceMap.put(x.fhirType() + x.getResource().getId(), x.getResource())); + return resourceMap; + } + + protected Bundle getPrefetchResources(CdsServiceRequestJson theJson) { + // using HashMap to avoid duplicates + Map resourceMap = new HashMap<>(); + Bundle prefetchResources = new Bundle(); + Resource resource; + for (String key : theJson.getPrefetchKeys()) { + resource = (Resource) theJson.getPrefetch(key); + if (resource == null) { + continue; + } + if (resource instanceof Bundle) { + resourceMap.putAll(getResourcesFromBundle((Bundle) resource)); + } else { + resourceMap.put(resource.fhirType() + resource.getId(), resource); + } + } + resourceMap.forEach((key, value) -> prefetchResources.addEntry().setResource(value)); + return prefetchResources; + } + + public CdsServiceResponseJson encodeResponse(Object theResponse) { + assert theResponse instanceof Bundle; + myResponseBundle = (Bundle) theResponse; + CdsServiceResponseJson serviceResponse = new CdsServiceResponseJson(); + if (myResponseBundle.hasEntry()) { + RequestOrchestration mainRequest = + (RequestOrchestration) myResponseBundle.getEntry().get(0).getResource(); + CanonicalType canonical = mainRequest.getInstantiatesCanonical().get(0); + PlanDefinition planDef = myRepository.read( + PlanDefinition.class, + new IdType(Canonicals.getResourceType(canonical), Canonicals.getIdPart(canonical))); + List links = resolvePlanLinks(planDef); + mainRequest.getAction().forEach(action -> serviceResponse.addCard(resolveAction(action, links))); + } + + return serviceResponse; + } + + protected List resolvePlanLinks(PlanDefinition thePlanDefinition) { + List links = new ArrayList<>(); + // links - listed on each card + if (thePlanDefinition.hasRelatedArtifact()) { + thePlanDefinition.getRelatedArtifact().forEach(ra -> { + String linkUrl = ra.getDocument().getUrl(); + if (linkUrl != null) { + CdsServiceResponseLinkJson link = new CdsServiceResponseLinkJson().setUrl(linkUrl); + if (ra.hasDisplay()) { + link.setLabel(ra.getDisplay()); + } + if (ra.hasExtension()) { + link.setType(ra.getExtensionFirstRep().getValue().primitiveValue()); + } else link.setType("absolute"); // default + links.add(link); + } + }); + } + return links; + } + + protected CdsServiceResponseCardJson resolveAction( + RequestOrchestration.RequestOrchestrationActionComponent theAction, + List theLinks) { + CdsServiceResponseCardJson card = new CdsServiceResponseCardJson() + .setSummary(theAction.getTitle()) + .setDetail(theAction.getDescription()) + .setLinks(theLinks); + + if (theAction.hasPriority()) { + card.setIndicator(resolveIndicator(theAction.getPriority().toCode())); + } + + if (theAction.hasDocumentation()) { + card.setSource(resolveSource(theAction)); + } + + if (theAction.hasSelectionBehavior()) { + card.setSelectionBehaviour(theAction.getSelectionBehavior().toCode()); + theAction.getAction().forEach(action -> resolveSuggestion(action)); + } + + // Leaving this out until the spec details how to map system actions. + // if (theAction.hasType() && theAction.hasResource()) { + // resolveSystemAction(theAction); + // } + + return card; + } + + protected CdsServiceIndicatorEnum resolveIndicator(String theCode) { + CdsServiceIndicatorEnum indicator; + switch (theCode) { + case "routine": + indicator = CdsServiceIndicatorEnum.INFO; + break; + case "urgent": + indicator = CdsServiceIndicatorEnum.WARNING; + break; + case "stat": + indicator = CdsServiceIndicatorEnum.CRITICAL; + break; + default: + indicator = null; + break; + } + if (indicator == null) { + // Code 2435-2440 are reserved for this error message across versions + throw new IllegalArgumentException(Msg.code(2436) + "Invalid priority code: " + theCode); + } + + return indicator; + } + + protected void resolveSystemAction(RequestOrchestration.RequestOrchestrationActionComponent theAction) { + if (theAction.hasType() + && theAction.getType().hasCoding() + && theAction.getType().getCodingFirstRep().hasCode() + && !theAction.getType().getCodingFirstRep().getCode().equals("fire-event")) { + myServiceResponse.addServiceAction(new CdsServiceResponseSystemActionJson() + .setResource(resolveResource(theAction.getResource())) + .setType(theAction.getType().getCodingFirstRep().getCode())); + } + } + + protected CdsServiceResponseCardSourceJson resolveSource( + RequestOrchestration.RequestOrchestrationActionComponent theAction) { + RelatedArtifact documentation = theAction.getDocumentationFirstRep(); + CdsServiceResponseCardSourceJson source = new CdsServiceResponseCardSourceJson() + .setLabel(documentation.getDisplay()) + .setUrl(documentation.getDocument().getUrl()); + + // If we use the document for the url, what do we use for the icon? + // if (documentation.hasDocument() && documentation.getDocument().hasUrl()) { + // source.setIcon(documentation.getDocument().getUrl()); + // } + + return source; + } + + protected CdsServiceResponseSuggestionJson resolveSuggestion( + RequestOrchestration.RequestOrchestrationActionComponent theAction) { + CdsServiceResponseSuggestionJson suggestion = new CdsServiceResponseSuggestionJson() + .setLabel(theAction.getTitle()) + .setUuid(theAction.getId()); + theAction.getAction().forEach(action -> suggestion.addAction(resolveSuggestionAction(action))); + + return suggestion; + } + + protected CdsServiceResponseSuggestionActionJson resolveSuggestionAction( + RequestOrchestration.RequestOrchestrationActionComponent theAction) { + CdsServiceResponseSuggestionActionJson suggestionAction = + new CdsServiceResponseSuggestionActionJson().setDescription(theAction.getDescription()); + if (theAction.hasType() + && theAction.getType().hasCoding() + && theAction.getType().getCodingFirstRep().hasCode() + && !theAction.getType().getCodingFirstRep().getCode().equals("fire-event")) { + String actionCode = theAction.getType().getCodingFirstRep().getCode(); + suggestionAction.setType(actionCode); + } + if (theAction.hasResource()) { + suggestionAction.setResource(resolveResource(theAction.getResource())); + // Leaving this out until the spec details how to map system actions. + // if (!suggestionAction.getType().isEmpty()) { + // resolveSystemAction(theAction); + // } + } + + return suggestionAction; + } + + protected IBaseResource resolveResource(Reference theReference) { + String reference = theReference.getReference(); + String[] split = reference.split("/"); + String id = reference.contains("/") ? split[1] : reference; + String resourceType = reference.contains("/") ? split[0] : theReference.getType(); + List results = myResponseBundle.getEntry().stream() + .filter(entry -> entry.hasResource() + && entry.getResource().getResourceType().toString().equals(resourceType) + && entry.getResource().getIdPart().equals(id)) + .map(entry -> entry.getResource()) + .collect(Collectors.toList()); + return results.isEmpty() ? null : results.get(0); + } +} diff --git a/hapi-fhir-server-cds-hooks/src/main/java/ca/uhn/hapi/fhir/cdshooks/svc/cr/CdsCrServiceRegistry.java b/hapi-fhir-server-cds-hooks/src/main/java/ca/uhn/hapi/fhir/cdshooks/svc/cr/CdsCrServiceRegistry.java new file mode 100644 index 00000000000..8c7e32ab8ea --- /dev/null +++ b/hapi-fhir-server-cds-hooks/src/main/java/ca/uhn/hapi/fhir/cdshooks/svc/cr/CdsCrServiceRegistry.java @@ -0,0 +1,51 @@ +/*- + * #%L + * HAPI FHIR - CDS Hooks + * %% + * Copyright (C) 2014 - 2023 Smile CDR, Inc. + * %% + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * #L% + */ +package ca.uhn.hapi.fhir.cdshooks.svc.cr; + +import ca.uhn.fhir.context.FhirVersionEnum; +import jakarta.annotation.Nonnull; + +import java.util.HashMap; +import java.util.Map; +import java.util.Optional; + +public class CdsCrServiceRegistry implements ICdsCrServiceRegistry { + private final Map> myCdsCrServices; + + public CdsCrServiceRegistry() { + myCdsCrServices = new HashMap<>(); + myCdsCrServices.put(FhirVersionEnum.DSTU3, CdsCrServiceDstu3.class); + myCdsCrServices.put(FhirVersionEnum.R4, CdsCrServiceR4.class); + myCdsCrServices.put(FhirVersionEnum.R5, CdsCrServiceR5.class); + } + + public void register( + @Nonnull FhirVersionEnum theFhirVersion, @Nonnull Class theCdsCrService) { + myCdsCrServices.put(theFhirVersion, theCdsCrService); + } + + public void unregister(@Nonnull FhirVersionEnum theFhirVersion) { + myCdsCrServices.remove(theFhirVersion); + } + + public Optional> find(@Nonnull FhirVersionEnum theFhirVersion) { + return Optional.ofNullable(myCdsCrServices.get(theFhirVersion)); + } +} diff --git a/hapi-fhir-server-cds-hooks/src/main/java/ca/uhn/hapi/fhir/cdshooks/svc/cr/CdsCrSettings.java b/hapi-fhir-server-cds-hooks/src/main/java/ca/uhn/hapi/fhir/cdshooks/svc/cr/CdsCrSettings.java new file mode 100644 index 00000000000..70258940829 --- /dev/null +++ b/hapi-fhir-server-cds-hooks/src/main/java/ca/uhn/hapi/fhir/cdshooks/svc/cr/CdsCrSettings.java @@ -0,0 +1,44 @@ +/*- + * #%L + * HAPI FHIR - CDS Hooks + * %% + * Copyright (C) 2014 - 2023 Smile CDR, Inc. + * %% + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * #L% + */ +package ca.uhn.hapi.fhir.cdshooks.svc.cr; + +public class CdsCrSettings { + private final String DEFAULT_CLIENT_ID_HEADER_NAME = "client_id"; + private String myClientIdHeaderName; + + public static CdsCrSettings getDefault() { + CdsCrSettings settings = new CdsCrSettings(); + settings.setClientIdHeaderName(settings.DEFAULT_CLIENT_ID_HEADER_NAME); + return settings; + } + + public void setClientIdHeaderName(String theName) { + myClientIdHeaderName = theName; + } + + public String getClientIdHeaderName() { + return myClientIdHeaderName; + } + + public CdsCrSettings withClientIdHeaderName(String theName) { + myClientIdHeaderName = theName; + return this; + } +} diff --git a/hapi-fhir-server-cds-hooks/src/main/java/ca/uhn/hapi/fhir/cdshooks/svc/cr/CdsCrUtils.java b/hapi-fhir-server-cds-hooks/src/main/java/ca/uhn/hapi/fhir/cdshooks/svc/cr/CdsCrUtils.java new file mode 100644 index 00000000000..5f57c5dab31 --- /dev/null +++ b/hapi-fhir-server-cds-hooks/src/main/java/ca/uhn/hapi/fhir/cdshooks/svc/cr/CdsCrUtils.java @@ -0,0 +1,41 @@ +/*- + * #%L + * HAPI FHIR - CDS Hooks + * %% + * Copyright (C) 2014 - 2023 Smile CDR, Inc. + * %% + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * #L% + */ +package ca.uhn.hapi.fhir.cdshooks.svc.cr; + +import ca.uhn.fhir.context.FhirVersionEnum; +import org.hl7.fhir.instance.model.api.IBaseResource; +import org.hl7.fhir.instance.model.api.IIdType; +import org.opencds.cqf.fhir.api.Repository; + +public class CdsCrUtils { + public static IBaseResource readPlanDefinitionFromRepository( + FhirVersionEnum theFhirVersion, Repository theRepository, IIdType theId) { + switch (theFhirVersion) { + case DSTU3: + return theRepository.read(org.hl7.fhir.dstu3.model.PlanDefinition.class, theId); + case R4: + return theRepository.read(org.hl7.fhir.r4.model.PlanDefinition.class, theId); + case R5: + return theRepository.read(org.hl7.fhir.r5.model.PlanDefinition.class, theId); + default: + return null; + } + } +} diff --git a/hapi-fhir-server-cds-hooks/src/main/java/ca/uhn/hapi/fhir/cdshooks/svc/cr/CdsServiceInterceptor.java b/hapi-fhir-server-cds-hooks/src/main/java/ca/uhn/hapi/fhir/cdshooks/svc/cr/CdsServiceInterceptor.java new file mode 100644 index 00000000000..0e393c24e43 --- /dev/null +++ b/hapi-fhir-server-cds-hooks/src/main/java/ca/uhn/hapi/fhir/cdshooks/svc/cr/CdsServiceInterceptor.java @@ -0,0 +1,93 @@ +/*- + * #%L + * HAPI FHIR - CDS Hooks + * %% + * Copyright (C) 2014 - 2023 Smile CDR, Inc. + * %% + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * #L% + */ +package ca.uhn.hapi.fhir.cdshooks.svc.cr; + +import ca.uhn.fhir.jpa.cache.IResourceChangeEvent; +import ca.uhn.fhir.jpa.cache.IResourceChangeListener; +import ca.uhn.fhir.jpa.cache.ResourceChangeEvent; +import ca.uhn.hapi.fhir.cdshooks.svc.CdsServiceRegistryImpl; +import org.hl7.fhir.instance.model.api.IIdType; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.annotation.Autowired; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.Collections; +import java.util.List; + +import static ca.uhn.hapi.fhir.cdshooks.svc.cr.CdsCrConstants.CDS_CR_MODULE_ID; + +public class CdsServiceInterceptor implements IResourceChangeListener { + static final Logger ourLog = LoggerFactory.getLogger(CdsServiceInterceptor.class); + + @Autowired + CdsServiceRegistryImpl myCdsServiceRegistry; + + public CdsServiceInterceptor() {} + + @Override + public void handleInit(Collection theResourceIds) { + handleChange(ResourceChangeEvent.fromCreatedUpdatedDeletedResourceIds( + new ArrayList<>(theResourceIds), Collections.emptyList(), Collections.emptyList())); + } + + @Override + public void handleChange(IResourceChangeEvent theResourceChangeEvent) { + if (theResourceChangeEvent == null) return; + if (theResourceChangeEvent.getCreatedResourceIds() != null + && !theResourceChangeEvent.getCreatedResourceIds().isEmpty()) { + insert(theResourceChangeEvent.getCreatedResourceIds()); + } + if (theResourceChangeEvent.getUpdatedResourceIds() != null + && !theResourceChangeEvent.getUpdatedResourceIds().isEmpty()) { + update(theResourceChangeEvent.getUpdatedResourceIds()); + } + if (theResourceChangeEvent.getDeletedResourceIds() != null + && !theResourceChangeEvent.getDeletedResourceIds().isEmpty()) { + delete(theResourceChangeEvent.getDeletedResourceIds()); + } + } + + private void insert(List theCreatedIds) { + for (IIdType id : theCreatedIds) { + try { + myCdsServiceRegistry.registerCrService(id.getIdPart()); + } catch (Exception e) { + ourLog.info(String.format("Failed to create service for %s", id.getIdPart())); + } + } + } + + private void update(List updatedIds) { + try { + delete(updatedIds); + insert(updatedIds); + } catch (Exception e) { + ourLog.info(String.format("Failed to update service(s) for %s", updatedIds)); + } + } + + private void delete(List deletedIds) { + for (IIdType id : deletedIds) { + myCdsServiceRegistry.unregisterService(id.getIdPart(), CDS_CR_MODULE_ID); + } + } +} diff --git a/hapi-fhir-server-cds-hooks/src/main/java/ca/uhn/hapi/fhir/cdshooks/svc/cr/ICdsCrService.java b/hapi-fhir-server-cds-hooks/src/main/java/ca/uhn/hapi/fhir/cdshooks/svc/cr/ICdsCrService.java new file mode 100644 index 00000000000..0e14d092958 --- /dev/null +++ b/hapi-fhir-server-cds-hooks/src/main/java/ca/uhn/hapi/fhir/cdshooks/svc/cr/ICdsCrService.java @@ -0,0 +1,82 @@ +/*- + * #%L + * HAPI FHIR - CDS Hooks + * %% + * Copyright (C) 2014 - 2023 Smile CDR, Inc. + * %% + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * #L% + */ +package ca.uhn.hapi.fhir.cdshooks.svc.cr; + +import ca.uhn.fhir.context.FhirVersionEnum; +import ca.uhn.fhir.model.api.IModelJson; +import ca.uhn.fhir.rest.api.Constants; +import ca.uhn.fhir.rest.server.provider.ProviderConstants; +import ca.uhn.hapi.fhir.cdshooks.api.json.CdsServiceRequestJson; +import ca.uhn.hapi.fhir.cdshooks.api.json.CdsServiceResponseJson; +import org.hl7.fhir.instance.model.api.IBaseParameters; +import org.hl7.fhir.instance.model.api.IBaseResource; +import org.opencds.cqf.fhir.api.Repository; + +import java.util.Collections; + +public interface ICdsCrService { + IBaseParameters encodeParams(CdsServiceRequestJson theJson); + + CdsServiceResponseJson encodeResponse(Object theResponse); + + FhirVersionEnum getFhirVersion(); + + Repository getRepository(); + + default Object invoke(IModelJson theJson) { + IBaseParameters params = encodeParams((CdsServiceRequestJson) theJson); + IBaseResource response = invokeApply(params); + return encodeResponse(response); + } + + default IBaseResource invokeApply(IBaseParameters theParams) { + var operationName = getFhirVersion() == FhirVersionEnum.R4 + ? ProviderConstants.CR_OPERATION_R5_APPLY + : ProviderConstants.CR_OPERATION_APPLY; + switch (getFhirVersion()) { + case DSTU3: + return getRepository() + .invoke( + org.hl7.fhir.dstu3.model.PlanDefinition.class, + operationName, + theParams, + org.hl7.fhir.dstu3.model.CarePlan.class, + Collections.singletonMap(Constants.HEADER_CONTENT_TYPE, Constants.CT_FHIR_JSON)); + case R4: + return getRepository() + .invoke( + org.hl7.fhir.r4.model.PlanDefinition.class, + operationName, + theParams, + org.hl7.fhir.r4.model.Bundle.class, + Collections.singletonMap(Constants.HEADER_CONTENT_TYPE, Constants.CT_FHIR_JSON)); + case R5: + return getRepository() + .invoke( + org.hl7.fhir.r5.model.PlanDefinition.class, + operationName, + theParams, + org.hl7.fhir.r5.model.Bundle.class, + Collections.singletonMap(Constants.HEADER_CONTENT_TYPE, Constants.CT_FHIR_JSON)); + default: + return null; + } + } +} diff --git a/hapi-fhir-server-cds-hooks/src/main/java/ca/uhn/hapi/fhir/cdshooks/svc/cr/ICdsCrServiceFactory.java b/hapi-fhir-server-cds-hooks/src/main/java/ca/uhn/hapi/fhir/cdshooks/svc/cr/ICdsCrServiceFactory.java new file mode 100644 index 00000000000..a17d8efe2db --- /dev/null +++ b/hapi-fhir-server-cds-hooks/src/main/java/ca/uhn/hapi/fhir/cdshooks/svc/cr/ICdsCrServiceFactory.java @@ -0,0 +1,24 @@ +/*- + * #%L + * HAPI FHIR - CDS Hooks + * %% + * Copyright (C) 2014 - 2023 Smile CDR, Inc. + * %% + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * #L% + */ +package ca.uhn.hapi.fhir.cdshooks.svc.cr; + +public interface ICdsCrServiceFactory { + ICdsCrService create(String theServiceId); +} diff --git a/hapi-fhir-server-cds-hooks/src/main/java/ca/uhn/hapi/fhir/cdshooks/svc/cr/ICdsCrServiceRegistry.java b/hapi-fhir-server-cds-hooks/src/main/java/ca/uhn/hapi/fhir/cdshooks/svc/cr/ICdsCrServiceRegistry.java new file mode 100644 index 00000000000..1b5465bade6 --- /dev/null +++ b/hapi-fhir-server-cds-hooks/src/main/java/ca/uhn/hapi/fhir/cdshooks/svc/cr/ICdsCrServiceRegistry.java @@ -0,0 +1,33 @@ +/*- + * #%L + * HAPI FHIR - CDS Hooks + * %% + * Copyright (C) 2014 - 2023 Smile CDR, Inc. + * %% + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * #L% + */ +package ca.uhn.hapi.fhir.cdshooks.svc.cr; + +import ca.uhn.fhir.context.FhirVersionEnum; +import jakarta.annotation.Nonnull; + +import java.util.Optional; + +public interface ICdsCrServiceRegistry { + void register(@Nonnull FhirVersionEnum theFhirVersion, @Nonnull Class theCdsCrService); + + void unregister(@Nonnull FhirVersionEnum theFhirVersion); + + Optional> find(@Nonnull FhirVersionEnum theFhirVersion); +} diff --git a/hapi-fhir-server-cds-hooks/src/main/java/ca/uhn/hapi/fhir/cdshooks/svc/cr/discovery/CdsCrDiscoveryServiceRegistry.java b/hapi-fhir-server-cds-hooks/src/main/java/ca/uhn/hapi/fhir/cdshooks/svc/cr/discovery/CdsCrDiscoveryServiceRegistry.java new file mode 100644 index 00000000000..d2dda2f73a0 --- /dev/null +++ b/hapi-fhir-server-cds-hooks/src/main/java/ca/uhn/hapi/fhir/cdshooks/svc/cr/discovery/CdsCrDiscoveryServiceRegistry.java @@ -0,0 +1,53 @@ +/*- + * #%L + * HAPI FHIR - CDS Hooks + * %% + * Copyright (C) 2014 - 2023 Smile CDR, Inc. + * %% + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * #L% + */ +package ca.uhn.hapi.fhir.cdshooks.svc.cr.discovery; + +import ca.uhn.fhir.context.FhirVersionEnum; +import jakarta.annotation.Nonnull; + +import java.util.HashMap; +import java.util.Map; +import java.util.Optional; + +public class CdsCrDiscoveryServiceRegistry implements ICdsCrDiscoveryServiceRegistry { + private final Map> myCrDiscoveryServices; + + public CdsCrDiscoveryServiceRegistry() { + myCrDiscoveryServices = new HashMap<>(); + myCrDiscoveryServices.put(FhirVersionEnum.DSTU3, CrDiscoveryServiceDstu3.class); + myCrDiscoveryServices.put(FhirVersionEnum.R4, CrDiscoveryServiceR4.class); + myCrDiscoveryServices.put(FhirVersionEnum.R5, CrDiscoveryServiceR5.class); + } + + public void register( + @Nonnull FhirVersionEnum theFhirVersion, + @Nonnull Class theCrDiscoveryService) { + myCrDiscoveryServices.put(theFhirVersion, theCrDiscoveryService); + } + + public void unregister(@Nonnull FhirVersionEnum theFhirVersion) { + myCrDiscoveryServices.remove(theFhirVersion); + } + + @Override + public Optional> find(@Nonnull FhirVersionEnum theFhirVersion) { + return Optional.ofNullable(myCrDiscoveryServices.get(theFhirVersion)); + } +} diff --git a/hapi-fhir-server-cds-hooks/src/main/java/ca/uhn/hapi/fhir/cdshooks/svc/cr/discovery/CrDiscoveryElementDstu3.java b/hapi-fhir-server-cds-hooks/src/main/java/ca/uhn/hapi/fhir/cdshooks/svc/cr/discovery/CrDiscoveryElementDstu3.java new file mode 100644 index 00000000000..3e847e18eb2 --- /dev/null +++ b/hapi-fhir-server-cds-hooks/src/main/java/ca/uhn/hapi/fhir/cdshooks/svc/cr/discovery/CrDiscoveryElementDstu3.java @@ -0,0 +1,82 @@ +/*- + * #%L + * HAPI FHIR - CDS Hooks + * %% + * Copyright (C) 2014 - 2023 Smile CDR, Inc. + * %% + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * #L% + */ +package ca.uhn.hapi.fhir.cdshooks.svc.cr.discovery; + +import ca.uhn.hapi.fhir.cdshooks.api.CdsResolutionStrategyEnum; +import ca.uhn.hapi.fhir.cdshooks.api.json.CdsServiceJson; +import org.hl7.fhir.dstu3.model.PlanDefinition; +import org.hl7.fhir.r4.model.TriggerDefinition; + +import java.util.stream.Collectors; + +public class CrDiscoveryElementDstu3 implements ICrDiscoveryElement { + protected PlanDefinition myPlanDefinition; + protected PrefetchUrlList myPrefetchUrlList; + + public CrDiscoveryElementDstu3(PlanDefinition thePlanDefinition, PrefetchUrlList thePrefetchUrlList) { + myPlanDefinition = thePlanDefinition; + myPrefetchUrlList = thePrefetchUrlList; + } + + public CdsServiceJson getCdsServiceJson() { + if (myPlanDefinition == null + || !myPlanDefinition.hasAction() + || myPlanDefinition.getAction().stream().noneMatch(a -> a.hasTriggerDefinition())) { + return null; + } + + var triggerDefs = myPlanDefinition.getAction().stream() + .filter(a -> a.hasTriggerDefinition()) + .flatMap(a -> a.getTriggerDefinition().stream()) + .filter(t -> t.getType().equals(TriggerDefinition.TriggerType.NAMEDEVENT)) + .collect(Collectors.toList()); + if (triggerDefs == null || triggerDefs.isEmpty()) { + return null; + } + + var service = new CdsServiceJson() + .setId(myPlanDefinition.getIdElement().getIdPart()) + .setTitle(myPlanDefinition.getTitle()) + .setDescription(myPlanDefinition.getDescription()) + .setHook(triggerDefs.get(0).getEventName()); + + if (myPrefetchUrlList == null) { + myPrefetchUrlList = new PrefetchUrlList(); + } + + int itemNo = 0; + if (!myPrefetchUrlList.stream() + .anyMatch(p -> p.equals("Patient/{{context.patientId}}") + || p.equals("Patient?_id={{context.patientId}}") + || p.equals("Patient?_id=Patient/{{context.patientId}}"))) { + String key = getKey(++itemNo); + service.addPrefetch(key, "Patient?_id={{context.patientId}}"); + service.addSource(key, CdsResolutionStrategyEnum.SERVICE); + } + + for (String item : myPrefetchUrlList) { + String key = getKey(++itemNo); + service.addPrefetch(key, item); + service.addSource(key, CdsResolutionStrategyEnum.SERVICE); + } + + return service; + } +} diff --git a/hapi-fhir-server-cds-hooks/src/main/java/ca/uhn/hapi/fhir/cdshooks/svc/cr/discovery/CrDiscoveryElementR4.java b/hapi-fhir-server-cds-hooks/src/main/java/ca/uhn/hapi/fhir/cdshooks/svc/cr/discovery/CrDiscoveryElementR4.java new file mode 100644 index 00000000000..b028001a8c8 --- /dev/null +++ b/hapi-fhir-server-cds-hooks/src/main/java/ca/uhn/hapi/fhir/cdshooks/svc/cr/discovery/CrDiscoveryElementR4.java @@ -0,0 +1,82 @@ +/*- + * #%L + * HAPI FHIR - CDS Hooks + * %% + * Copyright (C) 2014 - 2023 Smile CDR, Inc. + * %% + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * #L% + */ +package ca.uhn.hapi.fhir.cdshooks.svc.cr.discovery; + +import ca.uhn.hapi.fhir.cdshooks.api.CdsResolutionStrategyEnum; +import ca.uhn.hapi.fhir.cdshooks.api.json.CdsServiceJson; +import org.hl7.fhir.r4.model.PlanDefinition; +import org.hl7.fhir.r4.model.TriggerDefinition; + +import java.util.stream.Collectors; + +public class CrDiscoveryElementR4 implements ICrDiscoveryElement { + protected PlanDefinition myPlanDefinition; + protected PrefetchUrlList myPrefetchUrlList; + + public CrDiscoveryElementR4(PlanDefinition thePlanDefinition, PrefetchUrlList thePrefetchUrlList) { + myPlanDefinition = thePlanDefinition; + myPrefetchUrlList = thePrefetchUrlList; + } + + public CdsServiceJson getCdsServiceJson() { + if (myPlanDefinition == null + || !myPlanDefinition.hasAction() + || myPlanDefinition.getAction().stream().noneMatch(a -> a.hasTrigger())) { + return null; + } + + var triggerDefs = myPlanDefinition.getAction().stream() + .filter(a -> a.hasTrigger()) + .flatMap(a -> a.getTrigger().stream()) + .filter(t -> t.getType().equals(TriggerDefinition.TriggerType.NAMEDEVENT)) + .collect(Collectors.toList()); + if (triggerDefs == null || triggerDefs.isEmpty()) { + return null; + } + + var service = new CdsServiceJson() + .setId(myPlanDefinition.getIdElement().getIdPart()) + .setTitle(myPlanDefinition.getTitle()) + .setDescription(myPlanDefinition.getDescription()) + .setHook(triggerDefs.get(0).getName()); + + if (myPrefetchUrlList == null) { + myPrefetchUrlList = new PrefetchUrlList(); + } + + int itemNo = 0; + if (!myPrefetchUrlList.stream() + .anyMatch(p -> p.equals("Patient/{{context.patientId}}") + || p.equals("Patient?_id={{context.patientId}}") + || p.equals("Patient?_id=Patient/{{context.patientId}}"))) { + String key = getKey(++itemNo); + service.addPrefetch(key, "Patient?_id={{context.patientId}}"); + service.addSource(key, CdsResolutionStrategyEnum.NONE); + } + + for (String item : myPrefetchUrlList) { + String key = getKey(++itemNo); + service.addPrefetch(key, item); + service.addSource(key, CdsResolutionStrategyEnum.NONE); + } + + return service; + } +} diff --git a/hapi-fhir-server-cds-hooks/src/main/java/ca/uhn/hapi/fhir/cdshooks/svc/cr/discovery/CrDiscoveryElementR5.java b/hapi-fhir-server-cds-hooks/src/main/java/ca/uhn/hapi/fhir/cdshooks/svc/cr/discovery/CrDiscoveryElementR5.java new file mode 100644 index 00000000000..bdfbd98e531 --- /dev/null +++ b/hapi-fhir-server-cds-hooks/src/main/java/ca/uhn/hapi/fhir/cdshooks/svc/cr/discovery/CrDiscoveryElementR5.java @@ -0,0 +1,82 @@ +/*- + * #%L + * HAPI FHIR - CDS Hooks + * %% + * Copyright (C) 2014 - 2023 Smile CDR, Inc. + * %% + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * #L% + */ +package ca.uhn.hapi.fhir.cdshooks.svc.cr.discovery; + +import ca.uhn.hapi.fhir.cdshooks.api.CdsResolutionStrategyEnum; +import ca.uhn.hapi.fhir.cdshooks.api.json.CdsServiceJson; +import org.hl7.fhir.r4.model.TriggerDefinition; +import org.hl7.fhir.r5.model.PlanDefinition; + +import java.util.stream.Collectors; + +public class CrDiscoveryElementR5 implements ICrDiscoveryElement { + protected PlanDefinition myPlanDefinition; + protected PrefetchUrlList myPrefetchUrlList; + + public CrDiscoveryElementR5(PlanDefinition thePlanDefinition, PrefetchUrlList thePrefetchUrlList) { + myPlanDefinition = thePlanDefinition; + myPrefetchUrlList = thePrefetchUrlList; + } + + public CdsServiceJson getCdsServiceJson() { + if (myPlanDefinition == null + || !myPlanDefinition.hasAction() + || myPlanDefinition.getAction().stream().noneMatch(a -> a.hasTrigger())) { + return null; + } + + var triggerDefs = myPlanDefinition.getAction().stream() + .filter(a -> a.hasTrigger()) + .flatMap(a -> a.getTrigger().stream()) + .filter(t -> t.getType().equals(TriggerDefinition.TriggerType.NAMEDEVENT)) + .collect(Collectors.toList()); + if (triggerDefs == null || triggerDefs.isEmpty()) { + return null; + } + + var service = new CdsServiceJson() + .setId(myPlanDefinition.getIdElement().getIdPart()) + .setTitle(myPlanDefinition.getTitle()) + .setDescription(myPlanDefinition.getDescription()) + .setHook(triggerDefs.get(0).getName()); + + if (myPrefetchUrlList == null) { + myPrefetchUrlList = new PrefetchUrlList(); + } + + int itemNo = 0; + if (!myPrefetchUrlList.stream() + .anyMatch(p -> p.equals("Patient/{{context.patientId}}") + || p.equals("Patient?_id={{context.patientId}}") + || p.equals("Patient?_id=Patient/{{context.patientId}}"))) { + String key = getKey(++itemNo); + service.addPrefetch(key, "Patient?_id={{context.patientId}}"); + service.addSource(key, CdsResolutionStrategyEnum.SERVICE); + } + + for (String item : myPrefetchUrlList) { + String key = getKey(++itemNo); + service.addPrefetch(key, item); + service.addSource(key, CdsResolutionStrategyEnum.SERVICE); + } + + return service; + } +} diff --git a/hapi-fhir-server-cds-hooks/src/main/java/ca/uhn/hapi/fhir/cdshooks/svc/cr/discovery/CrDiscoveryServiceDstu3.java b/hapi-fhir-server-cds-hooks/src/main/java/ca/uhn/hapi/fhir/cdshooks/svc/cr/discovery/CrDiscoveryServiceDstu3.java new file mode 100644 index 00000000000..b71649340b0 --- /dev/null +++ b/hapi-fhir-server-cds-hooks/src/main/java/ca/uhn/hapi/fhir/cdshooks/svc/cr/discovery/CrDiscoveryServiceDstu3.java @@ -0,0 +1,445 @@ +/*- + * #%L + * HAPI FHIR - CDS Hooks + * %% + * Copyright (C) 2014 - 2023 Smile CDR, Inc. + * %% + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * #L% + */ +package ca.uhn.hapi.fhir.cdshooks.svc.cr.discovery; + +import ca.uhn.fhir.context.FhirVersionEnum; +import ca.uhn.hapi.fhir.cdshooks.api.json.CdsServiceJson; +import ca.uhn.hapi.fhir.cdshooks.svc.cr.CdsCrUtils; +import org.hl7.fhir.dstu3.model.Coding; +import org.hl7.fhir.dstu3.model.DataRequirement; +import org.hl7.fhir.dstu3.model.Library; +import org.hl7.fhir.dstu3.model.PlanDefinition; +import org.hl7.fhir.dstu3.model.StringType; +import org.hl7.fhir.dstu3.model.ValueSet; +import org.hl7.fhir.instance.model.api.IBaseResource; +import org.hl7.fhir.instance.model.api.IIdType; +import org.opencds.cqf.fhir.api.Repository; +import org.opencds.cqf.fhir.utility.dstu3.SearchHelper; + +import java.util.ArrayList; +import java.util.List; + +public class CrDiscoveryServiceDstu3 implements ICrDiscoveryService { + + protected final String PATIENT_ID_CONTEXT = "{{context.patientId}}"; + protected final int DEFAULT_MAX_URI_LENGTH = 8000; + protected int myMaxUriLength; + + protected Repository myRepository; + protected final IIdType myPlanDefinitionId; + + public CrDiscoveryServiceDstu3(IIdType thePlanDefinitionId, Repository theRepository) { + myPlanDefinitionId = thePlanDefinitionId; + myRepository = theRepository; + myMaxUriLength = DEFAULT_MAX_URI_LENGTH; + } + + public CdsServiceJson resolveService() { + return resolveService( + CdsCrUtils.readPlanDefinitionFromRepository(FhirVersionEnum.DSTU3, myRepository, myPlanDefinitionId)); + } + + protected CdsServiceJson resolveService(IBaseResource thePlanDefinition) { + if (thePlanDefinition instanceof PlanDefinition) { + PlanDefinition planDef = (PlanDefinition) thePlanDefinition; + return new CrDiscoveryElementDstu3(planDef, getPrefetchUrlList(planDef)).getCdsServiceJson(); + } + return null; + } + + public boolean isEca(PlanDefinition thePlanDefinition) { + if (thePlanDefinition.hasType() && thePlanDefinition.getType().hasCoding()) { + for (Coding coding : thePlanDefinition.getType().getCoding()) { + if (coding.getCode().equals("eca-rule")) { + return true; + } + } + } + return false; + } + + public Library resolvePrimaryLibrary(PlanDefinition thePlanDefinition) { + // Assuming 1 library + // TODO: enhance to handle multiple libraries - need a way to identify primary + // library + Library library = null; + if (thePlanDefinition.hasLibrary() + && thePlanDefinition.getLibraryFirstRep().hasReference()) { + library = myRepository.read( + Library.class, thePlanDefinition.getLibraryFirstRep().getReferenceElement()); + } + return library; + } + + public List resolveValueCodingCodes(List theValueCodings) { + List result = new ArrayList<>(); + + StringBuilder codes = new StringBuilder(); + for (Coding coding : theValueCodings) { + if (coding.hasCode()) { + String system = coding.getSystem(); + String code = coding.getCode(); + + codes = getCodesStringBuilder(result, codes, system, code); + } + } + + result.add(codes.toString()); + return result; + } + + public List resolveValueSetCodes(StringType theValueSetId) { + ValueSet valueSet = (ValueSet) SearchHelper.searchRepositoryByCanonical(myRepository, theValueSetId); + List result = new ArrayList<>(); + StringBuilder codes = new StringBuilder(); + if (valueSet.hasExpansion() && valueSet.getExpansion().hasContains()) { + for (ValueSet.ValueSetExpansionContainsComponent contains : + valueSet.getExpansion().getContains()) { + String system = contains.getSystem(); + String code = contains.getCode(); + + codes = getCodesStringBuilder(result, codes, system, code); + } + } else if (valueSet.hasCompose() && valueSet.getCompose().hasInclude()) { + for (ValueSet.ConceptSetComponent concepts : valueSet.getCompose().getInclude()) { + String system = concepts.getSystem(); + if (concepts.hasConcept()) { + for (ValueSet.ConceptReferenceComponent concept : concepts.getConcept()) { + String code = concept.getCode(); + + codes = getCodesStringBuilder(result, codes, system, code); + } + } + } + } + result.add(codes.toString()); + return result; + } + + protected StringBuilder getCodesStringBuilder( + List theList, StringBuilder theCodes, String theSystem, String theCode) { + String codeToken = theSystem + "|" + theCode; + int postAppendLength = theCodes.length() + codeToken.length(); + + if (theCodes.length() > 0 && postAppendLength < myMaxUriLength) { + theCodes.append(","); + } else if (postAppendLength > myMaxUriLength) { + theList.add(theCodes.toString()); + theCodes = new StringBuilder(); + } + theCodes.append(codeToken); + return theCodes; + } + + public List createRequestUrl(DataRequirement theDataRequirement) { + if (!isPatientCompartment(theDataRequirement.getType())) return null; + String patientRelatedResource = theDataRequirement.getType() + "?" + + getPatientSearchParam(theDataRequirement.getType()) + + "=Patient/" + PATIENT_ID_CONTEXT; + List ret = new ArrayList<>(); + if (theDataRequirement.hasCodeFilter()) { + for (DataRequirement.DataRequirementCodeFilterComponent codeFilterComponent : + theDataRequirement.getCodeFilter()) { + if (!codeFilterComponent.hasPath()) continue; + String path = mapCodePathToSearchParam(theDataRequirement.getType(), codeFilterComponent.getPath()); + + StringType codeFilterComponentString = null; + if (codeFilterComponent.hasValueSetStringType()) { + codeFilterComponentString = codeFilterComponent.getValueSetStringType(); + } else if (codeFilterComponent.hasValueSetReference()) { + codeFilterComponentString = new StringType( + codeFilterComponent.getValueSetReference().getReference()); + } else if (codeFilterComponent.hasValueCoding()) { + List codeFilterValueCodings = codeFilterComponent.getValueCoding(); + boolean isFirstCodingInFilter = true; + for (String code : resolveValueCodingCodes(codeFilterValueCodings)) { + if (isFirstCodingInFilter) { + ret.add(patientRelatedResource + "&" + path + "=" + code); + } else { + ret.add("," + code); + } + + isFirstCodingInFilter = false; + } + } + + if (codeFilterComponentString != null) { + for (String codes : resolveValueSetCodes(codeFilterComponentString)) { + ret.add(patientRelatedResource + "&" + path + "=" + codes); + } + } + } + return ret; + } else { + ret.add(patientRelatedResource); + return ret; + } + } + + public PrefetchUrlList getPrefetchUrlList(PlanDefinition thePlanDefinition) { + PrefetchUrlList prefetchList = new PrefetchUrlList(); + if (thePlanDefinition == null) return null; + if (!isEca(thePlanDefinition)) return null; + Library library = resolvePrimaryLibrary(thePlanDefinition); + // TODO: resolve data requirements + if (!library.hasDataRequirement()) return null; + for (DataRequirement dataRequirement : library.getDataRequirement()) { + List requestUrls = createRequestUrl(dataRequirement); + if (requestUrls != null) { + prefetchList.addAll(requestUrls); + } + } + + return prefetchList; + } + + protected String mapCodePathToSearchParam(String theDataType, String thePath) { + switch (theDataType) { + case "MedicationAdministration": + if (thePath.equals("medication")) return "code"; + break; + case "MedicationDispense": + if (thePath.equals("medication")) return "code"; + break; + case "MedicationRequest": + if (thePath.equals("medication")) return "code"; + break; + case "MedicationStatement": + if (thePath.equals("medication")) return "code"; + break; + case "ProcedureRequest": + if (thePath.equals("bodySite")) return "body-site"; + break; + default: + if (thePath.equals("vaccineCode")) return "vaccine-code"; + break; + } + return thePath.replace('.', '-').toLowerCase(); + } + + public static boolean isPatientCompartment(String theDataType) { + if (theDataType == null) { + return false; + } + switch (theDataType) { + case "Account": + case "AdverseEvent": + case "AllergyIntolerance": + case "Appointment": + case "AppointmentResponse": + case "AuditEvent": + case "Basic": + case "BodySite": + case "CarePlan": + case "CareTeam": + case "ChargeItem": + case "Claim": + case "ClaimResponse": + case "ClinicalImpression": + case "Communication": + case "CommunicationRequest": + case "Composition": + case "Condition": + case "Consent": + case "Coverage": + case "DetectedIssue": + case "DeviceRequest": + case "DeviceUseStatement": + case "DiagnosticReport": + case "DocumentManifest": + case "EligibilityRequest": + case "Encounter": + case "EnrollmentRequest": + case "EpisodeOfCare": + case "ExplanationOfBenefit": + case "FamilyMemberHistory": + case "Flag": + case "Goal": + case "Group": + case "ImagingManifest": + case "ImagingStudy": + case "Immunization": + case "ImmunizationRecommendation": + case "List": + case "MeasureReport": + case "Media": + case "MedicationAdministration": + case "MedicationDispense": + case "MedicationRequest": + case "MedicationStatement": + case "NutritionOrder": + case "Observation": + case "Patient": + case "Person": + case "Procedure": + case "ProcedureRequest": + case "Provenance": + case "QuestionnaireResponse": + case "ReferralRequest": + case "RelatedPerson": + case "RequestGroup": + case "ResearchSubject": + case "RiskAssessment": + case "Schedule": + case "Specimen": + case "SupplyDelivery": + case "SupplyRequest": + case "VisionPrescription": + return true; + default: + return false; + } + } + + public String getPatientSearchParam(String theDataType) { + switch (theDataType) { + case "Account": + return "subject"; + case "AdverseEvent": + return "subject"; + case "AllergyIntolerance": + return "patient"; + case "Appointment": + return "actor"; + case "AppointmentResponse": + return "actor"; + case "AuditEvent": + return "patient"; + case "Basic": + return "patient"; + case "BodySite": + return "patient"; + case "CarePlan": + return "patient"; + case "CareTeam": + return "patient"; + case "ChargeItem": + return "subject"; + case "Claim": + return "patient"; + case "ClaimResponse": + return "patient"; + case "ClinicalImpression": + return "subject"; + case "Communication": + return "subject"; + case "CommunicationRequest": + return "subject"; + case "Composition": + return "subject"; + case "Condition": + return "patient"; + case "Consent": + return "patient"; + case "Coverage": + return "patient"; + case "DetectedIssue": + return "patient"; + case "DeviceRequest": + return "subject"; + case "DeviceUseStatement": + return "subject"; + case "DiagnosticReport": + return "subject"; + case "DocumentManifest": + return "subject"; + case "DocumentReference": + return "subject"; + case "EligibilityRequest": + return "patient"; + case "Encounter": + return "patient"; + case "EnrollmentRequest": + return "subject"; + case "EpisodeOfCare": + return "patient"; + case "ExplanationOfBenefit": + return "patient"; + case "FamilyMemberHistory": + return "patient"; + case "Flag": + return "patient"; + case "Goal": + return "patient"; + case "Group": + return "member"; + case "ImagingManifest": + return "patient"; + case "ImagingStudy": + return "patient"; + case "Immunization": + return "patient"; + case "ImmunizationRecommendation": + return "patient"; + case "List": + return "subject"; + case "MeasureReport": + return "patient"; + case "Media": + return "subject"; + case "MedicationAdministration": + return "patient"; + case "MedicationDispense": + return "patient"; + case "MedicationRequest": + return "subject"; + case "MedicationStatement": + return "subject"; + case "NutritionOrder": + return "patient"; + case "Observation": + return "subject"; + case "Patient": + return "_id"; + case "Person": + return "patient"; + case "Procedure": + return "patient"; + case "ProcedureRequest": + return "patient"; + case "Provenance": + return "patient"; + case "QuestionnaireResponse": + return "subject"; + case "ReferralRequest": + return "patient"; + case "RelatedPerson": + return "patient"; + case "RequestGroup": + return "subject"; + case "ResearchSubject": + return "individual"; + case "RiskAssessment": + return "subject"; + case "Schedule": + return "actor"; + case "Specimen": + return "subject"; + case "SupplyDelivery": + return "patient"; + case "SupplyRequest": + return "subject"; + case "VisionPrescription": + return "patient"; + } + + return null; + } +} diff --git a/hapi-fhir-server-cds-hooks/src/main/java/ca/uhn/hapi/fhir/cdshooks/svc/cr/discovery/CrDiscoveryServiceR4.java b/hapi-fhir-server-cds-hooks/src/main/java/ca/uhn/hapi/fhir/cdshooks/svc/cr/discovery/CrDiscoveryServiceR4.java new file mode 100644 index 00000000000..0f58472b344 --- /dev/null +++ b/hapi-fhir-server-cds-hooks/src/main/java/ca/uhn/hapi/fhir/cdshooks/svc/cr/discovery/CrDiscoveryServiceR4.java @@ -0,0 +1,429 @@ +/*- + * #%L + * HAPI FHIR - CDS Hooks + * %% + * Copyright (C) 2014 - 2023 Smile CDR, Inc. + * %% + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * #L% + */ +package ca.uhn.hapi.fhir.cdshooks.svc.cr.discovery; + +import ca.uhn.fhir.context.FhirVersionEnum; +import ca.uhn.hapi.fhir.cdshooks.api.json.CdsServiceJson; +import ca.uhn.hapi.fhir.cdshooks.svc.cr.CdsCrUtils; +import org.hl7.fhir.instance.model.api.IBaseResource; +import org.hl7.fhir.instance.model.api.IIdType; +import org.hl7.fhir.r4.model.CanonicalType; +import org.hl7.fhir.r4.model.Coding; +import org.hl7.fhir.r4.model.DataRequirement; +import org.hl7.fhir.r4.model.Library; +import org.hl7.fhir.r4.model.PlanDefinition; +import org.hl7.fhir.r4.model.ValueSet; +import org.opencds.cqf.fhir.api.Repository; +import org.opencds.cqf.fhir.utility.r4.SearchHelper; + +import java.util.ArrayList; +import java.util.List; + +public class CrDiscoveryServiceR4 implements ICrDiscoveryService { + + protected final String PATIENT_ID_CONTEXT = "{{context.patientId}}"; + protected final int DEFAULT_MAX_URI_LENGTH = 8000; + protected int myMaxUriLength; + + protected final Repository myRepository; + protected final IIdType myPlanDefinitionId; + + public CrDiscoveryServiceR4(IIdType thePlanDefinitionId, Repository theRepository) { + myPlanDefinitionId = thePlanDefinitionId; + myRepository = theRepository; + myMaxUriLength = DEFAULT_MAX_URI_LENGTH; + } + + public CdsServiceJson resolveService() { + return resolveService( + CdsCrUtils.readPlanDefinitionFromRepository(FhirVersionEnum.R4, myRepository, myPlanDefinitionId)); + } + + protected CdsServiceJson resolveService(IBaseResource thePlanDefinition) { + if (thePlanDefinition instanceof PlanDefinition) { + PlanDefinition planDef = (PlanDefinition) thePlanDefinition; + return new CrDiscoveryElementR4(planDef, getPrefetchUrlList(planDef)).getCdsServiceJson(); + } + return null; + } + + public boolean isEca(PlanDefinition planDefinition) { + if (planDefinition.hasType() && planDefinition.getType().hasCoding()) { + for (Coding coding : planDefinition.getType().getCoding()) { + if (coding.getCode().equals("eca-rule")) { + return true; + } + } + } + return false; + } + + public Library resolvePrimaryLibrary(PlanDefinition thePlanDefinition) { + // The CPGComputablePlanDefinition profile limits the cardinality of library to 1 + Library library = null; + if (thePlanDefinition.hasLibrary() && !thePlanDefinition.getLibrary().isEmpty()) { + library = (Library) SearchHelper.searchRepositoryByCanonical( + myRepository, thePlanDefinition.getLibrary().get(0)); + } + return library; + } + + public List resolveValueCodingCodes(List valueCodings) { + List result = new ArrayList<>(); + + StringBuilder codes = new StringBuilder(); + for (Coding coding : valueCodings) { + if (coding.hasCode()) { + String system = coding.getSystem(); + String code = coding.getCode(); + + codes = getCodesStringBuilder(result, codes, system, code); + } + } + + result.add(codes.toString()); + return result; + } + + public List resolveValueSetCodes(CanonicalType valueSetId) { + ValueSet valueSet = (ValueSet) SearchHelper.searchRepositoryByCanonical(myRepository, valueSetId); + List result = new ArrayList<>(); + StringBuilder codes = new StringBuilder(); + if (valueSet.hasExpansion() && valueSet.getExpansion().hasContains()) { + for (ValueSet.ValueSetExpansionContainsComponent contains : + valueSet.getExpansion().getContains()) { + String system = contains.getSystem(); + String code = contains.getCode(); + + codes = getCodesStringBuilder(result, codes, system, code); + } + } else if (valueSet.hasCompose() && valueSet.getCompose().hasInclude()) { + for (ValueSet.ConceptSetComponent concepts : valueSet.getCompose().getInclude()) { + String system = concepts.getSystem(); + if (concepts.hasConcept()) { + for (ValueSet.ConceptReferenceComponent concept : concepts.getConcept()) { + String code = concept.getCode(); + + codes = getCodesStringBuilder(result, codes, system, code); + } + } + } + } + result.add(codes.toString()); + return result; + } + + protected StringBuilder getCodesStringBuilder(List ret, StringBuilder codes, String system, String code) { + String codeToken = system + "|" + code; + int postAppendLength = codes.length() + codeToken.length(); + + if (codes.length() > 0 && postAppendLength < myMaxUriLength) { + codes.append(","); + } else if (postAppendLength > myMaxUriLength) { + ret.add(codes.toString()); + codes = new StringBuilder(); + } + codes.append(codeToken); + return codes; + } + + public List createRequestUrl(DataRequirement theDataRequirement) { + if (!isPatientCompartment(theDataRequirement.getType())) return null; + String patientRelatedResource = theDataRequirement.getType() + "?" + + getPatientSearchParam(theDataRequirement.getType()) + + "=Patient/" + PATIENT_ID_CONTEXT; + List ret = new ArrayList<>(); + if (theDataRequirement.hasCodeFilter()) { + for (DataRequirement.DataRequirementCodeFilterComponent codeFilterComponent : + theDataRequirement.getCodeFilter()) { + if (!codeFilterComponent.hasPath()) continue; + String path = mapCodePathToSearchParam(theDataRequirement.getType(), codeFilterComponent.getPath()); + if (codeFilterComponent.hasValueSetElement()) { + for (String codes : resolveValueSetCodes(codeFilterComponent.getValueSetElement())) { + ret.add(patientRelatedResource + "&" + path + "=" + codes); + } + } else if (codeFilterComponent.hasCode()) { + List codeFilterValueCodings = codeFilterComponent.getCode(); + boolean isFirstCodingInFilter = true; + for (String code : resolveValueCodingCodes(codeFilterValueCodings)) { + if (isFirstCodingInFilter) { + ret.add(patientRelatedResource + "&" + path + "=" + code); + } else { + ret.add("," + code); + } + + isFirstCodingInFilter = false; + } + } + } + return ret; + } else { + ret.add(patientRelatedResource); + return ret; + } + } + + public PrefetchUrlList getPrefetchUrlList(PlanDefinition thePlanDefinition) { + PrefetchUrlList prefetchList = new PrefetchUrlList(); + if (thePlanDefinition == null) return null; + if (!isEca(thePlanDefinition)) return null; + Library library = resolvePrimaryLibrary(thePlanDefinition); + // TODO: resolve data requirements + if (library == null || !library.hasDataRequirement()) return null; + for (DataRequirement dataRequirement : library.getDataRequirement()) { + List requestUrls = createRequestUrl(dataRequirement); + if (requestUrls != null) { + prefetchList.addAll(requestUrls); + } + } + return prefetchList; + } + + protected String mapCodePathToSearchParam(String theDataType, String thePath) { + switch (theDataType) { + case "MedicationAdministration": + if (thePath.equals("medication")) return "code"; + break; + case "MedicationDispense": + if (thePath.equals("medication")) return "code"; + break; + case "MedicationRequest": + if (thePath.equals("medication")) return "code"; + break; + case "MedicationStatement": + if (thePath.equals("medication")) return "code"; + break; + default: + if (thePath.equals("vaccineCode")) return "vaccine-code"; + break; + } + return thePath.replace('.', '-').toLowerCase(); + } + + public static boolean isPatientCompartment(String theDataType) { + if (theDataType == null) { + return false; + } + switch (theDataType) { + case "Account": + case "AdverseEvent": + case "AllergyIntolerance": + case "Appointment": + case "AppointmentResponse": + case "AuditEvent": + case "Basic": + case "BodyStructure": + case "CarePlan": + case "CareTeam": + case "ChargeItem": + case "Claim": + case "ClaimResponse": + case "ClinicalImpression": + case "Communication": + case "CommunicationRequest": + case "Composition": + case "Condition": + case "Consent": + case "Coverage": + case "CoverageEligibilityRequest": + case "CoverageEligibilityResponse": + case "DetectedIssue": + case "DeviceRequest": + case "DeviceUseStatement": + case "DiagnosticReport": + case "DocumentManifest": + case "DocumentReference": + case "Encounter": + case "EnrollmentRequest": + case "EpisodeOfCare": + case "ExplanationOfBenefit": + case "FamilyMemberHistory": + case "Flag": + case "Goal": + case "Group": + case "ImagingStudy": + case "Immunization": + case "ImmunizationEvaluation": + case "ImmunizationRecommendation": + case "Invoice": + case "List": + case "MeasureReport": + case "Media": + case "MedicationAdministration": + case "MedicationDispense": + case "MedicationRequest": + case "MedicationStatement": + case "MolecularSequence": + case "NutritionOrder": + case "Observation": + case "Patient": + case "Person": + case "Procedure": + case "Provenance": + case "QuestionnaireResponse": + case "RelatedPerson": + case "RequestGroup": + case "ResearchSubject": + case "RiskAssessment": + case "Schedule": + case "ServiceRequest": + case "Specimen": + case "SupplyDelivery": + case "SupplyRequest": + case "VisionPrescription": + return true; + default: + return false; + } + } + + public String getPatientSearchParam(String theDataType) { + switch (theDataType) { + case "Account": + return "subject"; + case "AdverseEvent": + return "subject"; + case "AllergyIntolerance": + return "patient"; + case "Appointment": + return "actor"; + case "AppointmentResponse": + return "actor"; + case "AuditEvent": + return "patient"; + case "Basic": + return "patient"; + case "BodyStructure": + return "patient"; + case "CarePlan": + return "patient"; + case "CareTeam": + return "patient"; + case "ChargeItem": + return "subject"; + case "Claim": + return "patient"; + case "ClaimResponse": + return "patient"; + case "ClinicalImpression": + return "subject"; + case "Communication": + return "subject"; + case "CommunicationRequest": + return "subject"; + case "Composition": + return "subject"; + case "Condition": + return "patient"; + case "Consent": + return "patient"; + case "Coverage": + return "policy-holder"; + case "DetectedIssue": + return "patient"; + case "DeviceRequest": + return "subject"; + case "DeviceUseStatement": + return "subject"; + case "DiagnosticReport": + return "subject"; + case "DocumentManifest": + return "subject"; + case "DocumentReference": + return "subject"; + case "Encounter": + return "patient"; + case "EnrollmentRequest": + return "subject"; + case "EpisodeOfCare": + return "patient"; + case "ExplanationOfBenefit": + return "patient"; + case "FamilyMemberHistory": + return "patient"; + case "Flag": + return "patient"; + case "Goal": + return "patient"; + case "Group": + return "member"; + case "ImagingStudy": + return "patient"; + case "Immunization": + return "patient"; + case "ImmunizationRecommendation": + return "patient"; + case "Invoice": + return "subject"; + case "List": + return "subject"; + case "MeasureReport": + return "patient"; + case "Media": + return "subject"; + case "MedicationAdministration": + return "patient"; + case "MedicationDispense": + return "patient"; + case "MedicationRequest": + return "subject"; + case "MedicationStatement": + return "subject"; + case "MolecularSequence": + return "patient"; + case "NutritionOrder": + return "patient"; + case "Observation": + return "subject"; + case "Patient": + return "_id"; + case "Person": + return "patient"; + case "Procedure": + return "patient"; + case "Provenance": + return "patient"; + case "QuestionnaireResponse": + return "subject"; + case "RelatedPerson": + return "patient"; + case "RequestGroup": + return "subject"; + case "ResearchSubject": + return "individual"; + case "RiskAssessment": + return "subject"; + case "Schedule": + return "actor"; + case "ServiceRequest": + return "patient"; + case "Specimen": + return "subject"; + case "SupplyDelivery": + return "patient"; + case "SupplyRequest": + return "subject"; + case "VisionPrescription": + return "patient"; + } + + return null; + } +} diff --git a/hapi-fhir-server-cds-hooks/src/main/java/ca/uhn/hapi/fhir/cdshooks/svc/cr/discovery/CrDiscoveryServiceR5.java b/hapi-fhir-server-cds-hooks/src/main/java/ca/uhn/hapi/fhir/cdshooks/svc/cr/discovery/CrDiscoveryServiceR5.java new file mode 100644 index 00000000000..fb9bd78e9e8 --- /dev/null +++ b/hapi-fhir-server-cds-hooks/src/main/java/ca/uhn/hapi/fhir/cdshooks/svc/cr/discovery/CrDiscoveryServiceR5.java @@ -0,0 +1,431 @@ +/*- + * #%L + * HAPI FHIR - CDS Hooks + * %% + * Copyright (C) 2014 - 2023 Smile CDR, Inc. + * %% + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * #L% + */ +package ca.uhn.hapi.fhir.cdshooks.svc.cr.discovery; + +import ca.uhn.fhir.context.FhirVersionEnum; +import ca.uhn.hapi.fhir.cdshooks.api.json.CdsServiceJson; +import ca.uhn.hapi.fhir.cdshooks.svc.cr.CdsCrUtils; +import org.hl7.fhir.instance.model.api.IBaseResource; +import org.hl7.fhir.instance.model.api.IIdType; +import org.hl7.fhir.r5.model.CanonicalType; +import org.hl7.fhir.r5.model.Coding; +import org.hl7.fhir.r5.model.DataRequirement; +import org.hl7.fhir.r5.model.Library; +import org.hl7.fhir.r5.model.PlanDefinition; +import org.hl7.fhir.r5.model.ValueSet; +import org.opencds.cqf.fhir.api.Repository; +import org.opencds.cqf.fhir.utility.r5.SearchHelper; + +import java.util.ArrayList; +import java.util.List; + +public class CrDiscoveryServiceR5 implements ICrDiscoveryService { + + protected final String PATIENT_ID_CONTEXT = "{{context.patientId}}"; + protected final int DEFAULT_MAX_URI_LENGTH = 8000; + protected int myMaxUriLength; + + protected final Repository myRepository; + protected final IIdType myPlanDefinitionId; + + public CrDiscoveryServiceR5(IIdType thePlanDefinitionId, Repository theRepository) { + myPlanDefinitionId = thePlanDefinitionId; + myRepository = theRepository; + myMaxUriLength = DEFAULT_MAX_URI_LENGTH; + } + + public CdsServiceJson resolveService() { + return resolveService( + CdsCrUtils.readPlanDefinitionFromRepository(FhirVersionEnum.R5, myRepository, myPlanDefinitionId)); + } + + protected CdsServiceJson resolveService(IBaseResource thePlanDefinition) { + if (thePlanDefinition instanceof PlanDefinition) { + PlanDefinition planDef = (PlanDefinition) thePlanDefinition; + return new CrDiscoveryElementR5(planDef, getPrefetchUrlList(planDef)).getCdsServiceJson(); + } + return null; + } + + public boolean isEca(PlanDefinition thePlanDefinition) { + if (thePlanDefinition.hasType() && thePlanDefinition.getType().hasCoding()) { + for (Coding coding : thePlanDefinition.getType().getCoding()) { + if (coding.getCode().equals("eca-rule")) { + return true; + } + } + } + return false; + } + + public Library resolvePrimaryLibrary(PlanDefinition thePlanDefinition) { + // The CPGComputablePlanDefinition profile limits the cardinality of library to 1 + Library library = null; + if (thePlanDefinition.hasLibrary() && !thePlanDefinition.getLibrary().isEmpty()) { + library = (Library) SearchHelper.searchRepositoryByCanonical( + myRepository, thePlanDefinition.getLibrary().get(0)); + } + return library; + } + + public List resolveValueCodingCodes(List theValueCodings) { + List result = new ArrayList<>(); + + StringBuilder codes = new StringBuilder(); + for (Coding coding : theValueCodings) { + if (coding.hasCode()) { + String system = coding.getSystem(); + String code = coding.getCode(); + + codes = getCodesStringBuilder(result, codes, system, code); + } + } + + result.add(codes.toString()); + return result; + } + + public List resolveValueSetCodes(CanonicalType theValueSetId) { + ValueSet valueSet = (ValueSet) SearchHelper.searchRepositoryByCanonical(myRepository, theValueSetId); + List result = new ArrayList<>(); + StringBuilder codes = new StringBuilder(); + if (valueSet.hasExpansion() && valueSet.getExpansion().hasContains()) { + for (ValueSet.ValueSetExpansionContainsComponent contains : + valueSet.getExpansion().getContains()) { + String system = contains.getSystem(); + String code = contains.getCode(); + + codes = getCodesStringBuilder(result, codes, system, code); + } + } else if (valueSet.hasCompose() && valueSet.getCompose().hasInclude()) { + for (ValueSet.ConceptSetComponent concepts : valueSet.getCompose().getInclude()) { + String system = concepts.getSystem(); + if (concepts.hasConcept()) { + for (ValueSet.ConceptReferenceComponent concept : concepts.getConcept()) { + String code = concept.getCode(); + + codes = getCodesStringBuilder(result, codes, system, code); + } + } + } + } + result.add(codes.toString()); + return result; + } + + protected StringBuilder getCodesStringBuilder( + List theList, StringBuilder theCodes, String theSystem, String theCode) { + String codeToken = theSystem + "|" + theCode; + int postAppendLength = theCodes.length() + codeToken.length(); + + if (theCodes.length() > 0 && postAppendLength < myMaxUriLength) { + theCodes.append(","); + } else if (postAppendLength > myMaxUriLength) { + theList.add(theCodes.toString()); + theCodes = new StringBuilder(); + } + theCodes.append(codeToken); + return theCodes; + } + + public List createRequestUrl(DataRequirement theDataRequirement) { + if (!isPatientCompartment(theDataRequirement.getType().toCode())) return null; + String patientRelatedResource = theDataRequirement.getType() + "?" + + getPatientSearchParam(theDataRequirement.getType().toCode()) + + "=Patient/" + PATIENT_ID_CONTEXT; + List ret = new ArrayList<>(); + if (theDataRequirement.hasCodeFilter()) { + for (DataRequirement.DataRequirementCodeFilterComponent codeFilterComponent : + theDataRequirement.getCodeFilter()) { + if (!codeFilterComponent.hasPath()) continue; + String path = + mapCodePathToSearchParam(theDataRequirement.getType().toCode(), codeFilterComponent.getPath()); + if (codeFilterComponent.hasValueSetElement()) { + for (String codes : resolveValueSetCodes(codeFilterComponent.getValueSetElement())) { + ret.add(patientRelatedResource + "&" + path + "=" + codes); + } + } else if (codeFilterComponent.hasCode()) { + List codeFilterValueCodings = codeFilterComponent.getCode(); + boolean isFirstCodingInFilter = true; + for (String code : resolveValueCodingCodes(codeFilterValueCodings)) { + if (isFirstCodingInFilter) { + ret.add(patientRelatedResource + "&" + path + "=" + code); + } else { + ret.add("," + code); + } + + isFirstCodingInFilter = false; + } + } + } + return ret; + } else { + ret.add(patientRelatedResource); + return ret; + } + } + + public PrefetchUrlList getPrefetchUrlList(PlanDefinition thePlanDefinition) { + PrefetchUrlList prefetchList = new PrefetchUrlList(); + if (thePlanDefinition == null) return null; + if (!isEca(thePlanDefinition)) return null; + Library library = resolvePrimaryLibrary(thePlanDefinition); + // TODO: resolve data requirements + if (library == null || !library.hasDataRequirement()) return null; + for (DataRequirement dataRequirement : library.getDataRequirement()) { + List requestUrls = createRequestUrl(dataRequirement); + if (requestUrls != null) { + prefetchList.addAll(requestUrls); + } + } + return prefetchList; + } + + protected String mapCodePathToSearchParam(String theDataType, String thePath) { + switch (theDataType) { + case "MedicationAdministration": + if (thePath.equals("medication")) return "code"; + break; + case "MedicationDispense": + if (thePath.equals("medication")) return "code"; + break; + case "MedicationRequest": + if (thePath.equals("medication")) return "code"; + break; + case "MedicationStatement": + if (thePath.equals("medication")) return "code"; + break; + default: + if (thePath.equals("vaccineCode")) return "vaccine-code"; + break; + } + return thePath.replace('.', '-').toLowerCase(); + } + + public static boolean isPatientCompartment(String theDataType) { + if (theDataType == null) { + return false; + } + switch (theDataType) { + case "Account": + case "AdverseEvent": + case "AllergyIntolerance": + case "Appointment": + case "AppointmentResponse": + case "AuditEvent": + case "Basic": + case "BodyStructure": + case "CarePlan": + case "CareTeam": + case "ChargeItem": + case "Claim": + case "ClaimResponse": + case "ClinicalImpression": + case "Communication": + case "CommunicationRequest": + case "Composition": + case "Condition": + case "Consent": + case "Coverage": + case "CoverageEligibilityRequest": + case "CoverageEligibilityResponse": + case "DetectedIssue": + case "DeviceRequest": + case "DeviceUseStatement": + case "DiagnosticReport": + case "DocumentManifest": + case "DocumentReference": + case "Encounter": + case "EnrollmentRequest": + case "EpisodeOfCare": + case "ExplanationOfBenefit": + case "FamilyMemberHistory": + case "Flag": + case "Goal": + case "Group": + case "ImagingStudy": + case "Immunization": + case "ImmunizationEvaluation": + case "ImmunizationRecommendation": + case "Invoice": + case "List": + case "MeasureReport": + case "Media": + case "MedicationAdministration": + case "MedicationDispense": + case "MedicationRequest": + case "MedicationStatement": + case "MolecularSequence": + case "NutritionOrder": + case "Observation": + case "Patient": + case "Person": + case "Procedure": + case "Provenance": + case "QuestionnaireResponse": + case "RelatedPerson": + case "RequestGroup": + case "ResearchSubject": + case "RiskAssessment": + case "Schedule": + case "ServiceRequest": + case "Specimen": + case "SupplyDelivery": + case "SupplyRequest": + case "VisionPrescription": + return true; + default: + return false; + } + } + + public String getPatientSearchParam(String theDataType) { + switch (theDataType) { + case "Account": + return "subject"; + case "AdverseEvent": + return "subject"; + case "AllergyIntolerance": + return "patient"; + case "Appointment": + return "actor"; + case "AppointmentResponse": + return "actor"; + case "AuditEvent": + return "patient"; + case "Basic": + return "patient"; + case "BodyStructure": + return "patient"; + case "CarePlan": + return "patient"; + case "CareTeam": + return "patient"; + case "ChargeItem": + return "subject"; + case "Claim": + return "patient"; + case "ClaimResponse": + return "patient"; + case "ClinicalImpression": + return "subject"; + case "Communication": + return "subject"; + case "CommunicationRequest": + return "subject"; + case "Composition": + return "subject"; + case "Condition": + return "patient"; + case "Consent": + return "patient"; + case "Coverage": + return "policy-holder"; + case "DetectedIssue": + return "patient"; + case "DeviceRequest": + return "subject"; + case "DeviceUseStatement": + return "subject"; + case "DiagnosticReport": + return "subject"; + case "DocumentManifest": + return "subject"; + case "DocumentReference": + return "subject"; + case "Encounter": + return "patient"; + case "EnrollmentRequest": + return "subject"; + case "EpisodeOfCare": + return "patient"; + case "ExplanationOfBenefit": + return "patient"; + case "FamilyMemberHistory": + return "patient"; + case "Flag": + return "patient"; + case "Goal": + return "patient"; + case "Group": + return "member"; + case "ImagingStudy": + return "patient"; + case "Immunization": + return "patient"; + case "ImmunizationRecommendation": + return "patient"; + case "Invoice": + return "subject"; + case "List": + return "subject"; + case "MeasureReport": + return "patient"; + case "Media": + return "subject"; + case "MedicationAdministration": + return "patient"; + case "MedicationDispense": + return "patient"; + case "MedicationRequest": + return "subject"; + case "MedicationStatement": + return "subject"; + case "MolecularSequence": + return "patient"; + case "NutritionOrder": + return "patient"; + case "Observation": + return "subject"; + case "Patient": + return "_id"; + case "Person": + return "patient"; + case "Procedure": + return "patient"; + case "Provenance": + return "patient"; + case "QuestionnaireResponse": + return "subject"; + case "RelatedPerson": + return "patient"; + case "RequestGroup": + return "subject"; + case "ResearchSubject": + return "individual"; + case "RiskAssessment": + return "subject"; + case "Schedule": + return "actor"; + case "ServiceRequest": + return "patient"; + case "Specimen": + return "subject"; + case "SupplyDelivery": + return "patient"; + case "SupplyRequest": + return "subject"; + case "VisionPrescription": + return "patient"; + } + + return null; + } +} diff --git a/hapi-fhir-server-cds-hooks/src/main/java/ca/uhn/hapi/fhir/cdshooks/svc/cr/discovery/ICdsCrDiscoveryServiceRegistry.java b/hapi-fhir-server-cds-hooks/src/main/java/ca/uhn/hapi/fhir/cdshooks/svc/cr/discovery/ICdsCrDiscoveryServiceRegistry.java new file mode 100644 index 00000000000..34f55015220 --- /dev/null +++ b/hapi-fhir-server-cds-hooks/src/main/java/ca/uhn/hapi/fhir/cdshooks/svc/cr/discovery/ICdsCrDiscoveryServiceRegistry.java @@ -0,0 +1,34 @@ +/*- + * #%L + * HAPI FHIR - CDS Hooks + * %% + * Copyright (C) 2014 - 2023 Smile CDR, Inc. + * %% + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * #L% + */ +package ca.uhn.hapi.fhir.cdshooks.svc.cr.discovery; + +import ca.uhn.fhir.context.FhirVersionEnum; +import jakarta.annotation.Nonnull; + +import java.util.Optional; + +public interface ICdsCrDiscoveryServiceRegistry { + void register( + @Nonnull FhirVersionEnum theFhirVersion, @Nonnull Class ICrDiscoveryService); + + void unregister(@Nonnull FhirVersionEnum theFhirVersion); + + Optional> find(@Nonnull FhirVersionEnum theFhirVersion); +} diff --git a/hapi-fhir-server-cds-hooks/src/main/java/ca/uhn/hapi/fhir/cdshooks/svc/cr/discovery/ICrDiscoveryElement.java b/hapi-fhir-server-cds-hooks/src/main/java/ca/uhn/hapi/fhir/cdshooks/svc/cr/discovery/ICrDiscoveryElement.java new file mode 100644 index 00000000000..f15792ea08d --- /dev/null +++ b/hapi-fhir-server-cds-hooks/src/main/java/ca/uhn/hapi/fhir/cdshooks/svc/cr/discovery/ICrDiscoveryElement.java @@ -0,0 +1,30 @@ +/*- + * #%L + * HAPI FHIR - CDS Hooks + * %% + * Copyright (C) 2014 - 2023 Smile CDR, Inc. + * %% + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * #L% + */ +package ca.uhn.hapi.fhir.cdshooks.svc.cr.discovery; + +import ca.uhn.hapi.fhir.cdshooks.api.json.CdsServiceJson; + +public interface ICrDiscoveryElement { + CdsServiceJson getCdsServiceJson(); + + default String getKey(int itemNo) { + return "item" + Integer.toString(itemNo); + } +} diff --git a/hapi-fhir-server-cds-hooks/src/main/java/ca/uhn/hapi/fhir/cdshooks/svc/cr/discovery/ICrDiscoveryService.java b/hapi-fhir-server-cds-hooks/src/main/java/ca/uhn/hapi/fhir/cdshooks/svc/cr/discovery/ICrDiscoveryService.java new file mode 100644 index 00000000000..b75a4a2e958 --- /dev/null +++ b/hapi-fhir-server-cds-hooks/src/main/java/ca/uhn/hapi/fhir/cdshooks/svc/cr/discovery/ICrDiscoveryService.java @@ -0,0 +1,26 @@ +/*- + * #%L + * HAPI FHIR - CDS Hooks + * %% + * Copyright (C) 2014 - 2023 Smile CDR, Inc. + * %% + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * #L% + */ +package ca.uhn.hapi.fhir.cdshooks.svc.cr.discovery; + +import ca.uhn.hapi.fhir.cdshooks.api.json.CdsServiceJson; + +public interface ICrDiscoveryService { + CdsServiceJson resolveService(); +} diff --git a/hapi-fhir-server-cds-hooks/src/main/java/ca/uhn/hapi/fhir/cdshooks/svc/cr/discovery/ICrDiscoveryServiceFactory.java b/hapi-fhir-server-cds-hooks/src/main/java/ca/uhn/hapi/fhir/cdshooks/svc/cr/discovery/ICrDiscoveryServiceFactory.java new file mode 100644 index 00000000000..250f8db9f0f --- /dev/null +++ b/hapi-fhir-server-cds-hooks/src/main/java/ca/uhn/hapi/fhir/cdshooks/svc/cr/discovery/ICrDiscoveryServiceFactory.java @@ -0,0 +1,24 @@ +/*- + * #%L + * HAPI FHIR - CDS Hooks + * %% + * Copyright (C) 2014 - 2023 Smile CDR, Inc. + * %% + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * #L% + */ +package ca.uhn.hapi.fhir.cdshooks.svc.cr.discovery; + +public interface ICrDiscoveryServiceFactory { + ICrDiscoveryService create(String theServiceId); +} diff --git a/hapi-fhir-server-cds-hooks/src/main/java/ca/uhn/hapi/fhir/cdshooks/svc/cr/discovery/PrefetchUrlList.java b/hapi-fhir-server-cds-hooks/src/main/java/ca/uhn/hapi/fhir/cdshooks/svc/cr/discovery/PrefetchUrlList.java new file mode 100644 index 00000000000..95820cf610c --- /dev/null +++ b/hapi-fhir-server-cds-hooks/src/main/java/ca/uhn/hapi/fhir/cdshooks/svc/cr/discovery/PrefetchUrlList.java @@ -0,0 +1,45 @@ +/*- + * #%L + * HAPI FHIR - CDS Hooks + * %% + * Copyright (C) 2014 - 2023 Smile CDR, Inc. + * %% + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * #L% + */ +package ca.uhn.hapi.fhir.cdshooks.svc.cr.discovery; + +import java.util.Collection; +import java.util.concurrent.CopyOnWriteArrayList; + +public class PrefetchUrlList extends CopyOnWriteArrayList { + + @Override + public boolean add(String theElement) { + for (String s : this) { + if (s.equals(theElement)) return false; + if (theElement.startsWith(s)) return false; + } + return super.add(theElement); + } + + @Override + public boolean addAll(Collection theAdd) { + if (theAdd != null) { + for (String s : theAdd) { + add(s); + } + } + return true; + } +} diff --git a/hapi-fhir-server-cds-hooks/src/main/java/ca/uhn/hapi/fhir/cdshooks/svc/prefetch/CdsPrefetchSvc.java b/hapi-fhir-server-cds-hooks/src/main/java/ca/uhn/hapi/fhir/cdshooks/svc/prefetch/CdsPrefetchSvc.java index 967da77b47e..81d1cc36a4c 100644 --- a/hapi-fhir-server-cds-hooks/src/main/java/ca/uhn/hapi/fhir/cdshooks/svc/prefetch/CdsPrefetchSvc.java +++ b/hapi-fhir-server-cds-hooks/src/main/java/ca/uhn/hapi/fhir/cdshooks/svc/prefetch/CdsPrefetchSvc.java @@ -27,6 +27,7 @@ import ca.uhn.hapi.fhir.cdshooks.api.ICdsHooksDaoAuthorizationSvc; import ca.uhn.hapi.fhir.cdshooks.api.ICdsServiceMethod; import ca.uhn.hapi.fhir.cdshooks.api.json.CdsServiceJson; import ca.uhn.hapi.fhir.cdshooks.api.json.CdsServiceRequestJson; +import ca.uhn.hapi.fhir.cdshooks.svc.CdsCrServiceMethod; import org.hl7.fhir.instance.model.api.IBaseResource; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -56,6 +57,11 @@ public class CdsPrefetchSvc { public void augmentRequest(CdsServiceRequestJson theCdsServiceRequestJson, ICdsServiceMethod theServiceMethod) { CdsServiceJson serviceSpec = theServiceMethod.getCdsServiceJson(); + if (theServiceMethod instanceof CdsCrServiceMethod) { + // CdsCrServices will retrieve data from the dao or fhir server passed in as needed, + // checking for missing prefetch is not necessary. + return; + } Set missingPrefetch = findMissingPrefetch(serviceSpec, theCdsServiceRequestJson); if (missingPrefetch.isEmpty()) { return; diff --git a/hapi-fhir-server-cds-hooks/src/test/java/ca/uhn/hapi/fhir/cdshooks/config/TestCdsHooksConfig.java b/hapi-fhir-server-cds-hooks/src/test/java/ca/uhn/hapi/fhir/cdshooks/config/TestCdsHooksConfig.java index 6b55053f357..367d496bb3b 100644 --- a/hapi-fhir-server-cds-hooks/src/test/java/ca/uhn/hapi/fhir/cdshooks/config/TestCdsHooksConfig.java +++ b/hapi-fhir-server-cds-hooks/src/test/java/ca/uhn/hapi/fhir/cdshooks/config/TestCdsHooksConfig.java @@ -5,6 +5,7 @@ import ca.uhn.fhir.jpa.api.dao.DaoRegistry; import ca.uhn.hapi.fhir.cdshooks.api.ICdsHooksDaoAuthorizationSvc; import ca.uhn.hapi.fhir.cdshooks.controller.TestServerAppCtx; import ca.uhn.hapi.fhir.cdshooks.svc.CdsHooksContextBooter; +import ca.uhn.hapi.fhir.cdshooks.svc.cr.CdsCrSettings; import org.hl7.fhir.instance.model.api.IBaseResource; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; @@ -16,6 +17,9 @@ public class TestCdsHooksConfig { return FhirContext.forR4Cached(); } + @Bean + CdsCrSettings cdsCrSettings() { return CdsCrSettings.getDefault(); } + @Bean public CdsHooksContextBooter cdsHooksContextBooter() { CdsHooksContextBooter retVal = new CdsHooksContextBooter(); diff --git a/hapi-fhir-server-cds-hooks/src/test/java/ca/uhn/hapi/fhir/cdshooks/controller/CdsHooksControllerTest.java b/hapi-fhir-server-cds-hooks/src/test/java/ca/uhn/hapi/fhir/cdshooks/controller/CdsHooksControllerTest.java index 8347de11ad4..0349fd27957 100644 --- a/hapi-fhir-server-cds-hooks/src/test/java/ca/uhn/hapi/fhir/cdshooks/controller/CdsHooksControllerTest.java +++ b/hapi-fhir-server-cds-hooks/src/test/java/ca/uhn/hapi/fhir/cdshooks/controller/CdsHooksControllerTest.java @@ -34,7 +34,7 @@ import org.springframework.test.web.servlet.MockMvc; import org.springframework.test.web.servlet.MvcResult; import org.springframework.test.web.servlet.setup.MockMvcBuilders; -import javax.annotation.Nonnull; +import jakarta.annotation.Nonnull; import java.util.UUID; import java.util.function.Function; diff --git a/hapi-fhir-server-cds-hooks/src/test/java/ca/uhn/hapi/fhir/cdshooks/svc/CdsServiceCacheTest.java b/hapi-fhir-server-cds-hooks/src/test/java/ca/uhn/hapi/fhir/cdshooks/svc/CdsServiceCacheTest.java index d604b83c7b4..ac94e684985 100644 --- a/hapi-fhir-server-cds-hooks/src/test/java/ca/uhn/hapi/fhir/cdshooks/svc/CdsServiceCacheTest.java +++ b/hapi-fhir-server-cds-hooks/src/test/java/ca/uhn/hapi/fhir/cdshooks/svc/CdsServiceCacheTest.java @@ -14,7 +14,7 @@ import org.junit.jupiter.api.extension.RegisterExtension; import org.mockito.InjectMocks; import org.mockito.junit.jupiter.MockitoExtension; -import javax.annotation.Nonnull; +import jakarta.annotation.Nonnull; import java.util.function.Function; import static ca.uhn.test.util.LogbackCaptureTestExtension.eventWithLevelAndMessageContains; diff --git a/hapi-fhir-server-cds-hooks/src/test/java/ca/uhn/hapi/fhir/cdshooks/svc/cr/BaseCrTest.java b/hapi-fhir-server-cds-hooks/src/test/java/ca/uhn/hapi/fhir/cdshooks/svc/cr/BaseCrTest.java new file mode 100644 index 00000000000..f00675fdacc --- /dev/null +++ b/hapi-fhir-server-cds-hooks/src/test/java/ca/uhn/hapi/fhir/cdshooks/svc/cr/BaseCrTest.java @@ -0,0 +1,19 @@ +package ca.uhn.hapi.fhir.cdshooks.svc.cr; + +import ca.uhn.fhir.context.FhirContext; +import org.junit.jupiter.api.extension.ExtendWith; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.test.context.ContextConfiguration; +import org.springframework.test.context.junit.jupiter.SpringExtension; + +@ExtendWith(SpringExtension.class) +@ContextConfiguration(classes = {TestCrConfig.class}) +public abstract class BaseCrTest { + public static final String PLAN_DEFINITION_RESOURCE_NAME = "PlanDefinition"; + protected static final String TEST_ADDRESS = "http://test:8000/fhir"; + + @Autowired + protected FhirContext myFhirContext; + @Autowired + protected CdsCrSettings myCdsCrSettings; +} diff --git a/hapi-fhir-server-cds-hooks/src/test/java/ca/uhn/hapi/fhir/cdshooks/svc/cr/TestCrConfig.java b/hapi-fhir-server-cds-hooks/src/test/java/ca/uhn/hapi/fhir/cdshooks/svc/cr/TestCrConfig.java new file mode 100644 index 00000000000..5b7410c2726 --- /dev/null +++ b/hapi-fhir-server-cds-hooks/src/test/java/ca/uhn/hapi/fhir/cdshooks/svc/cr/TestCrConfig.java @@ -0,0 +1,38 @@ +package ca.uhn.hapi.fhir.cdshooks.svc.cr; + +import ca.uhn.fhir.context.FhirContext; +import ca.uhn.fhir.jpa.api.dao.DaoRegistry; +import ca.uhn.hapi.fhir.cdshooks.api.ICdsConfigService; +import ca.uhn.hapi.fhir.cdshooks.module.CdsHooksObjectMapperFactory; +import ca.uhn.hapi.fhir.cdshooks.svc.CdsConfigServiceImpl; +import com.fasterxml.jackson.databind.ObjectMapper; +import org.springframework.beans.factory.annotation.Qualifier; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; + +import static ca.uhn.hapi.fhir.cdshooks.config.CdsHooksConfig.CDS_HOOKS_OBJECT_MAPPER_FACTORY; + +@Configuration +public class TestCrConfig { + @Bean + FhirContext fhirContext() { + return FhirContext.forR4Cached(); + } + + @Bean(name = CDS_HOOKS_OBJECT_MAPPER_FACTORY) + public ObjectMapper objectMapper(FhirContext theFhirContext) { + return new CdsHooksObjectMapperFactory(theFhirContext).newMapper(); + } + + @Bean + CdsCrSettings cdsCrSettings() { return CdsCrSettings.getDefault(); } + + @Bean + public ICdsConfigService cdsConfigService( + FhirContext theFhirContext, + @Qualifier(CDS_HOOKS_OBJECT_MAPPER_FACTORY) ObjectMapper theObjectMapper, + CdsCrSettings theCdsCrSettings) { + return new CdsConfigServiceImpl( + theFhirContext, theObjectMapper, theCdsCrSettings, null, null, null); + } +} diff --git a/hapi-fhir-server-cds-hooks/src/test/java/ca/uhn/hapi/fhir/cdshooks/svc/cr/discovery/CrDiscoveryServiceR4Test.java b/hapi-fhir-server-cds-hooks/src/test/java/ca/uhn/hapi/fhir/cdshooks/svc/cr/discovery/CrDiscoveryServiceR4Test.java new file mode 100644 index 00000000000..e8b5eee22aa --- /dev/null +++ b/hapi-fhir-server-cds-hooks/src/test/java/ca/uhn/hapi/fhir/cdshooks/svc/cr/discovery/CrDiscoveryServiceR4Test.java @@ -0,0 +1,44 @@ +package ca.uhn.hapi.fhir.cdshooks.svc.cr.discovery; + +import ca.uhn.fhir.util.ClasspathUtil; +import ca.uhn.hapi.fhir.cdshooks.api.json.CdsServiceJson; +import ca.uhn.hapi.fhir.cdshooks.module.CdsHooksObjectMapperFactory; +import ca.uhn.hapi.fhir.cdshooks.svc.cr.BaseCrTest; +import com.fasterxml.jackson.core.JsonProcessingException; +import com.fasterxml.jackson.databind.ObjectMapper; +import org.hl7.fhir.r4.model.Bundle; +import org.hl7.fhir.r4.model.IdType; +import org.junit.jupiter.api.Test; +import org.opencds.cqf.fhir.api.Repository; +import org.opencds.cqf.fhir.utility.repository.InMemoryFhirRepository; + +import static org.junit.jupiter.api.Assertions.assertEquals; + +public class CrDiscoveryServiceR4Test extends BaseCrTest { + @Test + public void testR4DiscoveryService() throws JsonProcessingException { + Bundle bundle = ClasspathUtil.loadResource(myFhirContext, Bundle.class, "Bundle-ASLPCrd-Content.json"); + Repository repository = new InMemoryFhirRepository(myFhirContext, bundle); + + final IdType planDefinitionId = new IdType(PLAN_DEFINITION_RESOURCE_NAME, "ASLPCrd"); + final CdsServiceJson cdsServiceJson = new CrDiscoveryServiceR4(planDefinitionId, repository).resolveService(); + final ObjectMapper objectMapper = new CdsHooksObjectMapperFactory(myFhirContext).newMapper(); + // execute + final String actual = objectMapper.writeValueAsString(cdsServiceJson); + final String expected = "{\n" + + " \"hook\" : \"order-sign\",\n" + + " \"title\" : \"ASLPCrd Workflow\",\n" + + " \"description\" : \"An example workflow for the CRD step of DaVinci Burden Reduction.\",\n" + + " \"id\" : \"ASLPCrd\",\n" + + " \"prefetch\" : {\n" + + " \"item1\" : \"Patient?_id=Patient/{{context.patientId}}\",\n" + + " \"item2\" : \"ServiceRequest?patient=Patient/{{context.patientId}}\",\n" + + " \"item3\" : \"Condition?patient=Patient/{{context.patientId}}&code=http://example.org/sdh/dtr/aslp/CodeSystem/aslp-codes|ASLP.A1.DE19\",\n" + + " \"item4\" : \"Condition?patient=Patient/{{context.patientId}}&code=http://example.org/sdh/dtr/aslp/CodeSystem/aslp-codes|ASLP.A1.DE18\",\n" + + " \"item5\" : \"Observation?subject=Patient/{{context.patientId}}&code=http://example.org/sdh/dtr/aslp/CodeSystem/aslp-codes|ASLP.A1.DE19\"\n" + + " }\n" + + "}"; + assertEquals(expected, actual); + } + +} diff --git a/hapi-fhir-server-cds-hooks/src/test/java/ca/uhn/hapi/fhir/cdshooks/svc/cr/resolution/CdsCrServiceR4Test.java b/hapi-fhir-server-cds-hooks/src/test/java/ca/uhn/hapi/fhir/cdshooks/svc/cr/resolution/CdsCrServiceR4Test.java new file mode 100644 index 00000000000..6243ab8a8bb --- /dev/null +++ b/hapi-fhir-server-cds-hooks/src/test/java/ca/uhn/hapi/fhir/cdshooks/svc/cr/resolution/CdsCrServiceR4Test.java @@ -0,0 +1,84 @@ +package ca.uhn.hapi.fhir.cdshooks.svc.cr.resolution; + +import ca.uhn.fhir.rest.api.server.RequestDetails; +import ca.uhn.fhir.rest.api.server.SystemRequestDetails; +import ca.uhn.fhir.util.ClasspathUtil; +import ca.uhn.hapi.fhir.cdshooks.api.ICdsConfigService; +import ca.uhn.hapi.fhir.cdshooks.api.json.CdsServiceRequestJson; +import ca.uhn.hapi.fhir.cdshooks.api.json.CdsServiceResponseJson; +import ca.uhn.hapi.fhir.cdshooks.module.CdsHooksObjectMapperFactory; +import ca.uhn.hapi.fhir.cdshooks.svc.CdsConfigServiceImpl; +import ca.uhn.hapi.fhir.cdshooks.svc.cr.BaseCrTest; +import ca.uhn.hapi.fhir.cdshooks.svc.cr.CdsCrServiceR4; +import com.fasterxml.jackson.databind.ObjectMapper; +import org.hl7.fhir.r4.model.Bundle; +import org.hl7.fhir.r4.model.IdType; +import org.hl7.fhir.r4.model.Parameters; +import org.hl7.fhir.r4.model.codesystems.ActionType; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Disabled; +import org.junit.jupiter.api.Test; +import org.opencds.cqf.fhir.api.Repository; +import org.opencds.cqf.fhir.utility.repository.InMemoryFhirRepository; + +import java.io.IOException; + +import static org.junit.jupiter.api.Assertions.assertNotNull; +import static org.junit.jupiter.api.Assertions.assertTrue; + +public class CdsCrServiceR4Test extends BaseCrTest { + private ObjectMapper myObjectMapper; + private ICdsConfigService myCdsConfigService; + + @BeforeEach + public void loadJson() throws IOException { + myObjectMapper = new CdsHooksObjectMapperFactory(myFhirContext).newMapper(); + myCdsConfigService = new CdsConfigServiceImpl(myFhirContext, myObjectMapper, myCdsCrSettings, null, null, null); + } + + @Test + public void testR4Params() throws IOException { + final String rawRequest = ClasspathUtil.loadResource("ASLPCrdServiceRequest.json"); + final CdsServiceRequestJson cdsServiceRequestJson = myObjectMapper.readValue(rawRequest, CdsServiceRequestJson.class); + final Bundle bundle = ClasspathUtil.loadResource(myFhirContext, Bundle.class, "Bundle-ASLPCrd-Content.json"); + final Repository repository = new InMemoryFhirRepository(myFhirContext, bundle); + final RequestDetails requestDetails = new SystemRequestDetails(); + final IdType planDefinitionId = new IdType(PLAN_DEFINITION_RESOURCE_NAME, "ASLPCrd"); + requestDetails.setId(planDefinitionId); + final Parameters params = new CdsCrServiceR4(requestDetails, repository, myCdsConfigService).encodeParams(cdsServiceRequestJson); + + assertTrue(params.getParameter().size() == 3); + assertTrue(params.getParameter("parameters").hasResource()); + } + + @Test + public void testR4Response() { + final Bundle bundle = ClasspathUtil.loadResource(myFhirContext, Bundle.class, "Bundle-ASLPCrd-Content.json"); + final Repository repository = new InMemoryFhirRepository(myFhirContext, bundle); + final Bundle responseBundle = ClasspathUtil.loadResource(myFhirContext, Bundle.class, "Bundle-ASLPCrd-Response.json"); + final RequestDetails requestDetails = new SystemRequestDetails(); + final IdType planDefinitionId = new IdType(PLAN_DEFINITION_RESOURCE_NAME, "ASLPCrd"); + requestDetails.setId(planDefinitionId); + final CdsServiceResponseJson cdsServiceResponseJson = new CdsCrServiceR4(requestDetails, repository, myCdsConfigService).encodeResponse(responseBundle); + + assertTrue(cdsServiceResponseJson.getCards().size() == 1); + assertTrue(!cdsServiceResponseJson.getCards().get(0).getSummary().isEmpty()); + assertTrue(!cdsServiceResponseJson.getCards().get(0).getDetail().isEmpty()); + } + + @Test + @Disabled // Disabled until the CDS on FHIR specification details how to map system actions. + public void testSystemActionResponse() { + final Bundle bundle = ClasspathUtil.loadResource(myFhirContext, Bundle.class, "Bundle-DischargeInstructionsPlan-Content.json"); + final Repository repository = new InMemoryFhirRepository(myFhirContext, bundle); + final Bundle responseBundle = ClasspathUtil.loadResource(myFhirContext, Bundle.class, "Bundle-DischargeInstructionsPlan-Response.json"); + final RequestDetails requestDetails = new SystemRequestDetails(); + final IdType planDefinitionId = new IdType(PLAN_DEFINITION_RESOURCE_NAME, "DischargeInstructionsPlan"); + requestDetails.setId(planDefinitionId); + final CdsServiceResponseJson cdsServiceResponseJson = new CdsCrServiceR4(requestDetails, repository, myCdsConfigService).encodeResponse(responseBundle); + + assertTrue(cdsServiceResponseJson.getServiceActions().size() == 1); + assertTrue(cdsServiceResponseJson.getServiceActions().get(0).getType().equals(ActionType.CREATE.toCode())); + assertNotNull(cdsServiceResponseJson.getServiceActions().get(0).getResource()); + } +} diff --git a/hapi-fhir-server-cds-hooks/src/test/resources/ASLPCrdServiceRequest.json b/hapi-fhir-server-cds-hooks/src/test/resources/ASLPCrdServiceRequest.json new file mode 100644 index 00000000000..422a9a05ea2 --- /dev/null +++ b/hapi-fhir-server-cds-hooks/src/test/resources/ASLPCrdServiceRequest.json @@ -0,0 +1,94 @@ +{ + "hook" : "order-sign", + "hookInstance": "randomGUIDforthehookevent", + "fhirServer" : "https://localhost:8000", + "fhirAuthorization": { + "access_token": "sometoken", + "token_type": "Bearer", + "expires_in": 300000, + "scope": "", + "subject": "clientIdHeaderTest" + }, + "context" : { + "patientId" : "Patient/123", + "draftOrders" : { + "resourceType": "Bundle", + "entry": [ + { + "resource": { + "resourceType": "ServiceRequest", + "id": "SleepStudy", + "meta": { + "profile": [ + "http://example.org/sdh/dtr/aslp/StructureDefinition/aslp-sleep-study-order", + "http://hl7.org/fhir/us/davinci-crd/StructureDefinition/profile-servicerequest" + ] + }, + "status": "draft", + "intent": "order", + "code": { + "coding": [ + { + "system": "http://example.org/sdh/dtr/aslp/CodeSystem/aslp-codes", + "code": "ASLP.A1.DE2", + "display": "Home sleep apnea testing (HSAT)" + } + ], + "text": "Home sleep apnea testing (HSAT)" + }, + "subject": { + "reference": "Patient/positive" + }, + "authoredOn": "2023-04-06", + "reasonReference": [ + { + "reference": "Condition/SleepApnea" + } + ], + "occurrenceDateTime": "2023-04-10T08:00:00.000Z", + "requester": { + "reference": "Practitioner/Practitioner-positive" + } + } + }, + { + "resource": { + "resourceType": "ServiceRequest", + "id": "SleepStudy2", + "meta": { + "profile": [ + "http://example.org/sdh/dtr/aslp/StructureDefinition/aslp-sleep-study-order", + "http://hl7.org/fhir/us/davinci-crd/StructureDefinition/profile-servicerequest" + ] + }, + "status": "draft", + "intent": "order", + "code": { + "coding": [ + { + "system": "http://example.org/sdh/dtr/aslp/CodeSystem/aslp-codes", + "code": "ASLP.A1.DE14", + "display": "Artificial intelligence (AI)" + } + ], + "text": "Artificial intelligence (AI)" + }, + "subject": { + "reference": "Patient/positive" + }, + "authoredOn": "2023-04-06", + "reasonReference": [ + { + "reference": "Condition/SleepApnea" + } + ], + "occurrenceDateTime": "2023-04-15T08:00:00.000Z", + "requester": { + "reference": "Practitioner/Practitioner-positive" + } + } + } + ] + } + } +} diff --git a/hapi-fhir-server-cds-hooks/src/test/resources/Bundle-ASLPCrd-Content.json b/hapi-fhir-server-cds-hooks/src/test/resources/Bundle-ASLPCrd-Content.json new file mode 100644 index 00000000000..9b99b5f794c --- /dev/null +++ b/hapi-fhir-server-cds-hooks/src/test/resources/Bundle-ASLPCrd-Content.json @@ -0,0 +1,1450 @@ +{ + "resourceType": "Bundle", + "type": "transaction", + "entry": [ + { + "resource": { + "resourceType": "Library", + "id": "ASLPCrd", + "extension": [ + { + "url": "http://hl7.org/fhir/us/cqfmeasures/StructureDefinition/cqfm-softwaresystem", + "valueReference": { + "reference": "Device/cqf-tooling" + } + } + ], + "url": "http://example.org/sdh/dtr/aslp/Library/ASLPCrd", + "name": "ASLPCrd", + "type": { + "coding": [ + { + "code": "logic-library" + } + ] + }, + "relatedArtifact": [ + { + "type": "depends-on", + "display": "FHIR model information", + "resource": "http://fhir.org/guides/cqf/common/Library/FHIR-ModelInfo|4.0.1" + }, + { + "type": "depends-on", + "display": "Library Cx", + "resource": "http://example.org/sdh/dtr/aslp/Library/ASLPConcepts" + }, + { + "type": "depends-on", + "display": "Library Dx", + "resource": "http://example.org/sdh/dtr/aslp/Library/ASLPDataElements" + }, + { + "type": "depends-on", + "display": "Library FHIRHelpers", + "resource": "http://example.org/sdh/dtr/aslp/Library/FHIRHelpers|4.1.000" + }, + { + "type": "depends-on", + "display": "Library SC", + "resource": "http://example.org/sdh/dtr/aslp/Library/SDHCommon" + }, + { + "type": "depends-on", + "display": "Library FC", + "resource": "http://example.org/sdh/dtr/aslp/Library/FHIRCommon|1.1.000" + }, + { + "type": "depends-on", + "display": "Code system ASLP Codes", + "resource": "http://example.org/sdh/dtr/aslp/CodeSystem/aslp-codes" + }, + { + "type": "depends-on", + "display": "Code system ConditionVerificationStatusCodes", + "resource": "http://terminology.hl7.org/CodeSystem/condition-ver-status" + }, + { + "type": "depends-on", + "display": "Value set Home Based Testing Sleep Studies Codes", + "resource": "http://example.org/sdh/dtr/aslp/ValueSet/aslp-a1-de2" + }, + { + "type": "depends-on", + "display": "Value set Active Condition", + "resource": "http://fhir.org/guides/cqf/common/ValueSet/active-condition" + } + ], + "parameter": [ + { + "name": "Service Request Id", + "use": "in", + "min": 0, + "max": "*", + "type": "string" + }, + { + "name": "Service Request", + "use": "in", + "min": 0, + "max": "*", + "type": "ServiceRequest" + }, + { + "name": "Patient", + "use": "out", + "min": 0, + "max": "1", + "type": "Patient" + }, + { + "name": "Is Sleep Study Service Request", + "use": "out", + "min": 0, + "max": "1", + "type": "boolean" + }, + { + "name": "Has Comorbidities", + "use": "out", + "min": 0, + "max": "1", + "type": "boolean" + }, + { + "name": "Is Prior Auth Required", + "use": "out", + "min": 0, + "max": "1", + "type": "boolean" + }, + { + "name": "Get Card Summary", + "use": "out", + "min": 0, + "max": "1", + "type": "string" + }, + { + "name": "Rationale", + "use": "out", + "min": 0, + "max": "1", + "type": "string" + }, + { + "name": "Get Card Detail", + "use": "out", + "min": 0, + "max": "1", + "type": "string" + }, + { + "name": "Get Card Indicator", + "use": "out", + "min": 0, + "max": "1", + "type": "string" + } + ], + "dataRequirement": [ + { + "type": "Patient", + "profile": [ + "http://hl7.org/fhir/StructureDefinition/Patient" + ] + }, + { + "type": "string", + "profile": [ + "http://hl7.org/fhir/string" + ], + "mustSupport": [ + "value" + ] + }, + { + "type": "ServiceRequest", + "profile": [ + "http://hl7.org/fhir/StructureDefinition/ServiceRequest" + ], + "mustSupport": [ + "id" + ] + }, + { + "profile": [ + "http://hl7.org/fhir/ObservationStatus" + ], + "mustSupport": [ + "value" + ] + }, + { + "type": "Quantity", + "profile": [ + "http://hl7.org/fhir/Quantity" + ], + "mustSupport": [ + "value", + "comparator", + "system", + "system.value", + "value.value", + "code", + "code.value", + "unit", + "unit.value" + ] + }, + { + "type": "Condition", + "profile": [ + "http://hl7.org/fhir/StructureDefinition/Condition" + ], + "mustSupport": [ + "code", + "clinicalStatus", + "verificationStatus" + ], + "codeFilter": [ + { + "path": "code", + "code": [ + { + "system": "http://example.org/sdh/dtr/aslp/CodeSystem/aslp-codes", + "code": "ASLP.A1.DE19", + "display": "History of Diabetes" + } + ] + } + ] + }, + { + "type": "Condition", + "profile": [ + "http://hl7.org/fhir/StructureDefinition/Condition" + ], + "mustSupport": [ + "code", + "clinicalStatus", + "verificationStatus" + ], + "codeFilter": [ + { + "path": "code", + "code": [ + { + "system": "http://example.org/sdh/dtr/aslp/CodeSystem/aslp-codes", + "code": "ASLP.A1.DE18", + "display": "History of Hypertension" + } + ] + } + ] + }, + { + "type": "Observation", + "profile": [ + "http://hl7.org/fhir/StructureDefinition/Observation" + ], + "mustSupport": [ + "code", + "status", + "value" + ], + "codeFilter": [ + { + "path": "code", + "code": [ + { + "system": "http://example.org/sdh/dtr/aslp/CodeSystem/aslp-codes", + "code": "ASLP.A1.DE19", + "display": "History of Diabetes" + } + ] + } + ] + }, + { + "type": "Quantity", + "profile": [ + "urn:hl7-org:elm-types:r1/Quantity" + ], + "mustSupport": [ + "value" + ] + } + ], + "content": [ + { + "contentType": "text/cql", + "data": "bGlicmFyeSBBU0xQQ3JkCgp1c2luZyBGSElSIHZlcnNpb24gJzQuMC4xJwoKaW5jbHVkZSBBU0xQQ29uY2VwdHMgY2FsbGVkIEN4CmluY2x1ZGUgQVNMUERhdGFFbGVtZW50cyBjYWxsZWQgRHgKaW5jbHVkZSBGSElSSGVscGVycyB2ZXJzaW9uICc0LjEuMDAwJwoKY29udGV4dCBQYXRpZW50CgpkZWZpbmUgIklzIFByaW9yIEF1dGggUmVxdWlyZWQiOgogICAgIklzIFNsZWVwIFN0dWR5IFNlcnZpY2UgUmVxdWVzdCIgYW5kICJIYXMgQ29tb3JiaWRpdGllcyIKCmRlZmluZSAiSGFzIENvbW9yYmlkaXRpZXMiOgogRHguIkhpc3Rvcnkgb2YgRGlhYmV0ZXMiIG9yIER4LiJIaXN0b3J5IG9mIEh5cGVydGVuc2lvbiIKCmRlZmluZSAiSXMgU2xlZXAgU3R1ZHkgU2VydmljZSBSZXF1ZXN0IjoKICAgIGV4aXN0cyAoRHguIlNsZWVwIFN0dWR5IiBTIHdoZXJlIFMuY29kZSBpbiBDeC4iSG9tZSBCYXNlZCBUZXN0aW5nIFNsZWVwIFN0dWRpZXMgQ29kZXMiKQoKZGVmaW5lICJHZXQgQ2FyZCBTdW1tYXJ5IjoKICBpZiAiSXMgUHJpb3IgQXV0aCBSZXF1aXJlZCIgdGhlbgogICAgJ1BhdGllbnQgcmVxdWlyZXMgcHJpb3IgYXV0aG9yaXp0aW9uIGZvciBhIHNsZWVwIHN0dWR5JwogIGVsc2UKICAgICdQYXRpZW50IGRvZXMgbm90IHJlcXVpcmUgcHJpb3IgYXV0aG9yaXphdGlvbiBmb3IgYSBzbGVlcCBzdHVkeScKCmRlZmluZSAiR2V0IENhcmQgRGV0YWlsIjoKICBpZiAiSXMgUHJpb3IgQXV0aCBSZXF1aXJlZCIgdGhlbgogICAgJ1BhdGllbnQgcmVxdWlyZXMgcHJpb3IgYXV0aG9yaXphdGlvbiBkdWUgdG86ICcgKyBSYXRpb25hbGUgKyAnLiAnICsKICAgICdQbGVhc2Ugb3BlbiB5b3VyIERUUiBhcHBsaWNhdGlvbiBhbmQgY29tcGxldGUgUXVlc3Rpb25uaWFyZScKICBlbHNlCiAgICAnUGF0aWVudCBkb2VzIG5vdCByZXF1aXJlIHByaW9yIGF1dGhvcml6YXRpb24uJwoKZGVmaW5lICJSYXRpb25hbGUiOgogIENvYWxlc2NlKHsKICAgIGlmIER4LiJIaXN0b3J5IG9mIERpYWJldGVzIiB0aGVuICdoaXN0b3J5IG9mIGRpYWJldGVzJyBlbHNlIG51bGwsCiAgICBpZiBEeC4iSGlzdG9yeSBvZiBIeXBlcnRlbnNpb24iIHRoZW4gJ2hpc3Rvcnkgb2YgaHlwZXJ0ZW5zaW9uJyBlbHNlIG51bGwsCiAgICAnbm8gcmF0aW9uYWxlIHByb3ZpZGVkJwogIH0pCgoKZGVmaW5lICJHZXQgQ2FyZCBJbmRpY2F0b3IiOgogIGlmICJJcyBQcmlvciBBdXRoIFJlcXVpcmVkIiB0aGVuCiAgICAnd2FybmluZycKICBlbHNlCiAgICAnaW5mbycKCmRlZmluZSAiUXVlc3Rpb25uYWlyZSBJbnB1dCI6CiAgeyB0eXBlOiAnY29sbGVjdC1pbmZvcm1hdGlvbicsIHZhbHVlQ2Fub25pY2FsOiAnaHR0cDovL2V4YW1wbGUub3JnL3NkaC9kdHIvYXNscC9RdWVzdGlvbm5haXJlL0FTTFBBMScgfQoKZGVmaW5lICJRdWVzdGlvbm5haXJlIElucHV0IFR5cGUiOgogICdjb2xsZWN0LWluZm9ybWF0aW9uJwoKZGVmaW5lICJRdWVzdGlvbm5haXJlIElucHV0IENhbm9uaWNhbCI6CiAgJ2h0dHA6Ly9leGFtcGxlLm9yZy9zZGgvZHRyL2FzbHAvUXVlc3Rpb25uYWlyZS9BU0xQQTEnCg==" + } + ] + }, + "request": { + "method": "PUT", + "url": "Library/ASLPCrd" + } + }, + { + "resource": { + "resourceType": "ActivityDefinition", + "id": "ASLPCrd", + "meta": { + "profile": "http://hl7.org/fhir/uv/cpg/StructureDefinition/cpg-collectinformationactivity" + }, + "url": "http://example.org/sdh/dtr/aslp/ActivityDefinition/ASLPCrd", + "version": "1.0.0", + "kind": "Task", + "profile": "http://hl7.org/fhir/uv/cpg/StructureDefinition/cpg-questionnairetask", + "intent": "proposal", + "priority": "routine", + "code": { + "coding": [ + { + "system": "http://hl7.org/fhir/uv/cpg/CodeSystem/cpg-activity-type", + "code": "collect-information", + "display": "Collect Information" + } + ] + }, + "library": [ + "http://example.org/sdh/dtr/aslp/Library/ASLPCrd" + ], + "dynamicValue": [ + { + "path": "input[0].type", + "expression": { + "language": "text/cql-identifier", + "expression": "Questionnaire Input Type" + } + }, + { + "path": "input[0].valueCanonical", + "expression": { + "language": "text/cql-identifier", + "expression": "Questionnaire Input Canonical" + } + }, + { + "path": "input[1].type", + "expression": { + "language": "text/cql-identifier", + "expression": "Questionnaire Input Type" + } + }, + { + "path": "input[1].valueCanonical", + "expression": { + "language": "text/cql", + "expression": "'http://example.org/sdh/dtr/aslp/Questionnaire/ASLPA2'" + } + } + ] + }, + "request": { + "method": "PUT", + "url": "ActivityDefinition/ASLPCrd" + } + }, + { + "resource": { + "resourceType": "PlanDefinition", + "id": "ASLPCrd", + "meta": { + "profile": [ + "http://hl7.org/fhir/uv/cpg/StructureDefinition/cpg-recommendationdefinition" + ] + }, + "extension": [ + { + "url": "http://hl7.org/fhir/uv/cpg/StructureDefinition/cpg-questionnaire-generate", + "valueBoolean": true + } + ], + "url": "http://example.org/sdh/dtr/aslp/PlanDefinition/ASLPCrd", + "identifier": [ + { + "use": "official", + "value": "generate-questionnaire-sample" + } + ], + "version": "1.0.0", + "name": "ASLPCrd", + "title": "ASLPCrd Workflow", + "type": { + "coding": [ + { + "system": "http://terminology.hl7.org/CodeSystem/plan-definition-type", + "code": "eca-rule", + "display": "ECA Rule" + } + ] + }, + "status": "draft", + "experimental": true, + "description": "An example workflow for the CRD step of DaVinci Burden Reduction.", + "useContext": [ + { + "code": { + "system": "http://terminology.hl7.org/CodeSystem/usage-context-type", + "code": "task", + "display": "Workflow Task" + }, + "valueCodeableConcept": { + "coding": [ + { + "system": "http://fhir.org/guides/nachc/hiv-cds/CodeSystem/activity-codes", + "code": "ASLP.A1", + "display": "Adult Sleep Studies" + } + ] + } + } + ], + "jurisdiction": [ + { + "coding": [ + { + "system": "http://hl7.org/fhir/ValueSet/iso3166-1-3", + "version": "4.0.1", + "code": "USA", + "display": "United States of America" + } + ] + } + ], + "purpose": "The purpose of this is to test the system to make sure we have complete end-to-end functionality", + "usage": "This is to be used in conjunction with a patient-facing FHIR application.", + "relatedArtifact": [ + { + "type": "depends-on", + "resource": "http://example.org/sdh/dtr/aslp/StructureDefinition/aslp-sleep-study-order" + } + ], + "library": [ + "http://example.org/sdh/dtr/aslp/Library/ASLPCrd" + ], + "action": [ + { + "trigger": [ + { + "type": "named-event", + "name": "order-sign" + } + ], + "extension": [], + "title": "Does order require PriorAuth?", + "description": "", + "condition": [ + { + "kind": "applicability", + "expression": { + "language": "text/cql-identifier", + "expression": "Is Prior Auth Required" + } + } + ], + "input": [ + { + "type": "ServiceRequest", + "profile": [ + "http://example.org/sdh/dtr/aslp/StructureDefinition/aslp-sleep-study-order" + ] + } + ], + "definitionCanonical": "http://example.org/sdh/dtr/aslp/ActivityDefinition/ASLPCrd", + "dynamicValue": [ + { + "path": "action.title", + "expression": { + "language": "text/cql-identifier", + "expression": "Get Card Summary" + } + }, + { + "path": "action.description", + "expression": { + "language": "text/cql-identifier", + "expression": "Get Card Detail" + } + }, + { + "path": "action.extension", + "expression": { + "language": "text/cql-identifier", + "expression": "Get Card Indicator" + } + } + ], + "action": [ + { + "input": [ + { + "type": "Condition", + "profile": [ + "http://example.org/sdh/dtr/aslp/StructureDefinition/aslp-diagnosis-of-obstructive-sleep-apnea" + ] + } + ] + }, + { + "input": [ + { + "type": "Observation", + "profile": [ + "http://example.org/sdh/dtr/aslp/StructureDefinition/aslp-history-of-hypertension" + ] + } + ] + }, + { + "input": [ + { + "type": "Observation", + "profile": [ + "http://example.org/sdh/dtr/aslp/StructureDefinition/aslp-history-of-diabetes" + ] + } + ] + }, + { + "input": [ + { + "type": "Observation", + "profile": [ + "http://example.org/sdh/dtr/aslp/StructureDefinition/aslp-neck-circumference" + ] + } + ] + }, + { + "input": [ + { + "type": "Observation", + "profile": [ + "http://example.org/sdh/dtr/aslp/StructureDefinition/aslp-height" + ] + } + ] + }, + { + "input": [ + { + "type": "Observation", + "profile": [ + "http://example.org/sdh/dtr/aslp/StructureDefinition/aslp-weight" + ] + } + ] + }, + { + "input": [ + { + "type": "Observation", + "profile": [ + "http://example.org/sdh/dtr/aslp/StructureDefinition/aslp-bmi" + ] + } + ] + } + ] + } + ] + }, + "request": { + "method": "PUT", + "url": "PlanDefinition/ASLPCrd" + } + }, + { + "resource": { + "resourceType": "StructureDefinition", + "id": "aslp-bmi", + "url": "http://example.org/sdh/dtr/aslp/StructureDefinition/aslp-bmi", + "name": "ASLPBMI", + "title": "ASLP BMI", + "status": "draft", + "experimental": false, + "description": "ASLP BMI", + "useContext": [ + { + "code": { + "system": "http://terminology.hl7.org/CodeSystem/usage-context-type", + "code": "task", + "display": "Workflow Task" + }, + "valueCodeableConcept": { + "coding": [ + { + "system": "http://fhir.org/guides/nachc/hiv-cds/CodeSystem/activity-codes", + "code": "ASLP.A1", + "display": "Adult Sleep Studies" + } + ] + } + } + ], + "fhirVersion": "4.0.1", + "mapping": [ + { + "identity": "ASLP" + } + ], + "kind": "resource", + "abstract": false, + "type": "Observation", + "baseDefinition": "http://hl7.org/fhir/us/qicore/StructureDefinition/qicore-observation", + "derivation": "constraint", + "differential": { + "element": [ + { + "id": "Observation", + "path": "Observation", + "mustSupport": false + }, + { + "id": "Observation.code", + "path": "Observation.code", + "short": "BMI", + "definition": "Body mass index (BMI)", + "min": 1, + "max": "1", + "type": [ + { + "code": "CodeableConcept" + } + ], + "mustSupport": true, + "mapping": [ + { + "identity": "ASLP", + "map": "ASLP.A1.DE22" + } + ] + }, + { + "id": "Observation.value[x]", + "path": "Observation.value[x]", + "short": "BMI", + "definition": "Body mass index (BMI)", + "min": 1, + "max": "1", + "type": [ + { + "code": "Quantity" + } + ], + "mustSupport": true, + "mapping": [ + { + "identity": "ASLP", + "map": "ASLP.A1.DE22" + } + ] + } + ] + } + }, + "request": { + "method": "PUT", + "url": "StructureDefinition/aslp-bmi" + } + }, + { + "resource": { + "resourceType": "StructureDefinition", + "id": "aslp-diagnosis-of-obstructive-sleep-apnea", + "url": "http://example.org/sdh/dtr/aslp/StructureDefinition/aslp-diagnosis-of-obstructive-sleep-apnea", + "name": "ASLPDiagnosisofObstructiveSleepApnea", + "title": "ASLP Diagnosis of Obstructive Sleep Apnea", + "status": "draft", + "experimental": false, + "description": "ASLP Diagnosis of Obstructive Sleep Apnea", + "useContext": [ + { + "code": { + "system": "http://terminology.hl7.org/CodeSystem/usage-context-type", + "code": "task", + "display": "Workflow Task" + }, + "valueCodeableConcept": { + "coding": [ + { + "system": "http://fhir.org/guides/nachc/hiv-cds/CodeSystem/activity-codes", + "code": "ASLP.A1", + "display": "Adult Sleep Studies" + } + ] + } + } + ], + "fhirVersion": "4.0.1", + "mapping": [ + { + "identity": "ASLP" + } + ], + "kind": "resource", + "abstract": false, + "type": "Condition", + "baseDefinition": "http://hl7.org/fhir/us/qicore/StructureDefinition/qicore-condition", + "derivation": "constraint", + "differential": { + "element": [ + { + "id": "Condition", + "path": "Condition", + "mustSupport": false + }, + { + "id": "Condition.code", + "path": "Condition.code", + "short": "Diagnosis of Obstructive Sleep Apnea", + "definition": "Diagnosis of Obstructive Sleep Apnea", + "min": 1, + "max": "1", + "type": [ + { + "code": "CodeableConcept" + } + ], + "mustSupport": true, + "binding": { + "extension": [ + { + "url": "http://hl7.org/fhir/StructureDefinition/elementdefinition-bindingName", + "valueString": "Diagnosis of Obstructive Sleep Apnea Codes" + } + ], + "strength": "required", + "valueSet": "http://example.org/sdh/dtr/aslp/ValueSet/aslp-a1-de17" + }, + "mapping": [ + { + "identity": "ASLP", + "map": "ASLP.A1.DE16" + } + ] + } + ] + } + }, + "request": { + "method": "PUT", + "url": "StructureDefinition/aslp-diagnosis-of-obstructive-sleep-apnea" + } + }, + { + "resource": { + "resourceType": "StructureDefinition", + "id": "aslp-height", + "url": "http://example.org/sdh/dtr/aslp/StructureDefinition/aslp-height", + "name": "ASLPHeight", + "title": "ASLP Height", + "status": "draft", + "experimental": false, + "description": "ASLP Height", + "useContext": [ + { + "code": { + "system": "http://terminology.hl7.org/CodeSystem/usage-context-type", + "code": "task", + "display": "Workflow Task" + }, + "valueCodeableConcept": { + "coding": [ + { + "system": "http://fhir.org/guides/nachc/hiv-cds/CodeSystem/activity-codes", + "code": "ASLP.A1", + "display": "Adult Sleep Studies" + } + ] + } + } + ], + "fhirVersion": "4.0.1", + "mapping": [ + { + "identity": "ASLP" + } + ], + "kind": "resource", + "abstract": false, + "type": "Observation", + "baseDefinition": "http://hl7.org/fhir/us/qicore/StructureDefinition/qicore-observation", + "derivation": "constraint", + "differential": { + "element": [ + { + "id": "Observation", + "path": "Observation", + "mustSupport": false + }, + { + "id": "Observation.code", + "path": "Observation.code", + "short": "Height", + "definition": "Height (in inches)", + "min": 1, + "max": "1", + "type": [ + { + "code": "CodeableConcept" + } + ], + "mustSupport": true, + "mapping": [ + { + "identity": "ASLP", + "map": "ASLP.A1.DE20" + } + ] + }, + { + "id": "Observation.value[x]", + "path": "Observation.value[x]", + "short": "Height", + "definition": "Height (in inches)", + "min": 1, + "max": "1", + "type": [ + { + "code": "Quantity" + } + ], + "mustSupport": true, + "mapping": [ + { + "identity": "ASLP", + "map": "ASLP.A1.DE20" + } + ] + } + ] + } + }, + "request": { + "method": "PUT", + "url": "StructureDefinition/aslp-height" + } + }, + { + "resource": { + "resourceType": "StructureDefinition", + "id": "aslp-history-of-diabetes", + "url": "http://example.org/sdh/dtr/aslp/StructureDefinition/aslp-history-of-diabetes", + "name": "ASLPHistoryofDiabetes", + "title": "ASLP History of Diabetes", + "status": "draft", + "experimental": false, + "description": "ASLP History of Diabetes", + "useContext": [ + { + "code": { + "system": "http://terminology.hl7.org/CodeSystem/usage-context-type", + "code": "task", + "display": "Workflow Task" + }, + "valueCodeableConcept": { + "coding": [ + { + "system": "http://fhir.org/guides/nachc/hiv-cds/CodeSystem/activity-codes", + "code": "ASLP.A1", + "display": "Adult Sleep Studies" + } + ] + } + } + ], + "fhirVersion": "4.0.1", + "mapping": [ + { + "identity": "ASLP" + } + ], + "kind": "resource", + "abstract": false, + "type": "Observation", + "baseDefinition": "http://hl7.org/fhir/us/qicore/StructureDefinition/qicore-observation", + "derivation": "constraint", + "differential": { + "element": [ + { + "id": "Observation", + "path": "Observation", + "mustSupport": false + }, + { + "id": "Observation.code", + "path": "Observation.code", + "short": "History of Diabetes", + "definition": "History of Diabetes", + "min": 1, + "max": "1", + "type": [ + { + "code": "CodeableConcept" + } + ], + "mustSupport": true, + "mapping": [ + { + "identity": "ASLP", + "map": "ASLP.A1.DE19" + } + ] + }, + { + "id": "Observation.value[x]", + "path": "Observation.value[x]", + "short": "History of Diabetes", + "definition": "History of Diabetes", + "min": 1, + "max": "1", + "type": [ + { + "code": "boolean" + } + ], + "mustSupport": true, + "mapping": [ + { + "identity": "ASLP", + "map": "ASLP.A1.DE19" + } + ] + } + ] + } + }, + "request": { + "method": "PUT", + "url": "StructureDefinition/aslp-history-of-diabetes" + } + }, + { + "resource": { + "resourceType": "StructureDefinition", + "id": "aslp-history-of-hypertension", + "url": "http://example.org/sdh/dtr/aslp/StructureDefinition/aslp-history-of-hypertension", + "name": "ASLPHistoryofHypertension", + "title": "ASLP History of Hypertension", + "status": "draft", + "experimental": false, + "description": "ASLP History of Hypertension", + "useContext": [ + { + "code": { + "system": "http://terminology.hl7.org/CodeSystem/usage-context-type", + "code": "task", + "display": "Workflow Task" + }, + "valueCodeableConcept": { + "coding": [ + { + "system": "http://fhir.org/guides/nachc/hiv-cds/CodeSystem/activity-codes", + "code": "ASLP.A1", + "display": "Adult Sleep Studies" + } + ] + } + } + ], + "fhirVersion": "4.0.1", + "mapping": [ + { + "identity": "ASLP" + } + ], + "kind": "resource", + "abstract": false, + "type": "Observation", + "baseDefinition": "http://hl7.org/fhir/us/qicore/StructureDefinition/qicore-observation", + "derivation": "constraint", + "differential": { + "element": [ + { + "id": "Observation", + "path": "Observation", + "mustSupport": false + }, + { + "id": "Observation.code", + "path": "Observation.code", + "short": "History of Hypertension", + "definition": "History of Hypertension", + "min": 1, + "max": "1", + "type": [ + { + "code": "CodeableConcept" + } + ], + "mustSupport": true, + "mapping": [ + { + "identity": "ASLP", + "map": "ASLP.A1.DE18" + } + ] + }, + { + "id": "Observation.value[x]", + "path": "Observation.value[x]", + "short": "History of Hypertension", + "definition": "History of Hypertension", + "min": 1, + "max": "1", + "type": [ + { + "code": "boolean" + } + ], + "mustSupport": true, + "mapping": [ + { + "identity": "ASLP", + "map": "ASLP.A1.DE18" + } + ] + } + ] + } + }, + "request": { + "method": "PUT", + "url": "StructureDefinition/aslp-history-of-hypertension" + } + }, + { + "resource": { + "resourceType": "StructureDefinition", + "id": "aslp-neck-circumference", + "url": "http://example.org/sdh/dtr/aslp/StructureDefinition/aslp-neck-circumference", + "name": "ASLPNeckCircumference", + "title": "ASLP Neck Circumference", + "status": "draft", + "experimental": false, + "description": "ASLP Neck Circumference", + "useContext": [ + { + "code": { + "system": "http://terminology.hl7.org/CodeSystem/usage-context-type", + "code": "task", + "display": "Workflow Task" + }, + "valueCodeableConcept": { + "coding": [ + { + "system": "http://fhir.org/guides/nachc/hiv-cds/CodeSystem/activity-codes", + "code": "ASLP.A1", + "display": "Adult Sleep Studies" + } + ] + } + } + ], + "fhirVersion": "4.0.1", + "mapping": [ + { + "identity": "ASLP" + } + ], + "kind": "resource", + "abstract": false, + "type": "Observation", + "baseDefinition": "http://hl7.org/fhir/us/qicore/StructureDefinition/qicore-observation", + "derivation": "constraint", + "differential": { + "element": [ + { + "id": "Observation", + "path": "Observation", + "mustSupport": false + }, + { + "id": "Observation.code", + "path": "Observation.code", + "short": "Neck Circumference", + "definition": "Neck circumference (in inches)", + "min": 1, + "max": "1", + "type": [ + { + "code": "CodeableConcept" + } + ], + "mustSupport": true, + "mapping": [ + { + "identity": "ASLP", + "map": "ASLP.A1.DE20" + } + ] + }, + { + "id": "Observation.value[x]", + "path": "Observation.value[x]", + "short": "Neck Circumference", + "definition": "Neck circumference (in inches)", + "min": 1, + "max": "1", + "type": [ + { + "code": "Quantity" + } + ], + "mustSupport": true, + "mapping": [ + { + "identity": "ASLP", + "map": "ASLP.A1.DE20" + } + ] + } + ] + } + }, + "request": { + "method": "PUT", + "url": "StructureDefinition/aslp-neck-circumference" + } + }, + { + "resource": { + "resourceType": "StructureDefinition", + "id": "aslp-servicerequest", + "url": "http://example.org/sdh/dtr/aslp/StructureDefinition/aslp-servicerequest", + "name": "ASLPServiceRequest", + "title": "ASLP ServiceRequest", + "status": "draft", + "experimental": false, + "description": "ASLP ServiceRequest", + "useContext": [ + { + "code": { + "system": "http://terminology.hl7.org/CodeSystem/usage-context-type", + "code": "task", + "display": "Workflow Task" + }, + "valueCodeableConcept": { + "coding": [ + { + "system": "http://fhir.org/guides/nachc/hiv-cds/CodeSystem/activity-codes", + "code": "ASLP.A1", + "display": "Adult Sleep Studies" + } + ] + } + } + ], + "fhirVersion": "4.0.1", + "mapping": [ + { + "identity": "ASLP" + } + ], + "kind": "resource", + "abstract": false, + "type": "ServiceRequest", + "baseDefinition": "http://hl7.org/fhir/us/qicore/StructureDefinition/qicore-servicerequest", + "derivation": "constraint", + "differential": { + "element": [ + { + "id": "ServiceRequest", + "path": "ServiceRequest", + "mustSupport": false + }, + { + "id": "ServiceRequest.code", + "path": "ServiceRequest.code", + "short": "Procedure Code", + "definition": "The procedures being approved", + "comment": "The procedures for which approval is being requested", + "min": 1, + "max": "1", + "type": [ + { + "code": "CodeableConcept" + } + ], + "mustSupport": true, + "binding": { + "extension": [ + { + "url": "http://hl7.org/fhir/StructureDefinition/elementdefinition-bindingName", + "valueString": "Procedure Code Codes Grouper" + } + ], + "strength": "required", + "valueSet": "http://example.org/sdh/dtr/aslp/ValueSet/aslp-a1-de1-codes-grouper" + }, + "mapping": [ + { + "identity": "ASLP", + "map": "ASLP.A1.DE1" + } + ] + }, + { + "id": "ServiceRequest.occurrence[x]", + "path": "ServiceRequest.occurrence[x]", + "short": "Procedure Date", + "definition": "Date of the procedure", + "min": 1, + "max": "1", + "type": [ + { + "code": "dateTime" + } + ], + "mustSupport": true, + "mapping": [ + { + "identity": "ASLP", + "map": "ALSP.A1.DE15" + } + ] + } + ] + } + }, + "request": { + "method": "PUT", + "url": "StructureDefinition/aslp-servicerequest" + } + }, + { + "resource": { + "resourceType": "StructureDefinition", + "id": "aslp-sleep-study-order", + "url": "http://example.org/sdh/dtr/aslp/StructureDefinition/aslp-sleep-study-order", + "name": "ASLPSleepStudyOrder", + "title": "ASLP Sleep Study Order", + "status": "draft", + "experimental": false, + "description": "ASLP Sleep Study Order", + "useContext": [ + { + "code": { + "system": "http://terminology.hl7.org/CodeSystem/usage-context-type", + "code": "task", + "display": "Workflow Task" + }, + "valueCodeableConcept": { + "coding": [ + { + "system": "http://fhir.org/guides/nachc/hiv-cds/CodeSystem/activity-codes", + "code": "ASLP.A1", + "display": "Adult Sleep Studies" + } + ] + } + } + ], + "fhirVersion": "4.0.1", + "mapping": [ + { + "identity": "ASLP" + } + ], + "kind": "resource", + "abstract": false, + "type": "ServiceRequest", + "baseDefinition": "http://hl7.org/fhir/us/qicore/StructureDefinition/qicore-servicerequest", + "derivation": "constraint", + "differential": { + "element": [ + { + "id": "ServiceRequest", + "path": "ServiceRequest", + "mustSupport": false + }, + { + "id": "ServiceRequest.code", + "extension": [ + { + "url": "http://hl7.org/fhir/StructureDefinition/cqf-expression", + "valueExpression": { + "language": "text/cql-identifier", + "expression": "Sleep Study Code", + "reference": "http://example.org/sdh/dtr/aslp/Library/ASLPDataElements" + } + } + ], + "path": "ServiceRequest.code", + "short": "Sleep Study", + "definition": "A sleep study procedure being ordered", + "comment": "The procedures for which approval is being requested", + "min": 1, + "max": "1", + "type": [ + { + "code": "CodeableConcept" + } + ], + "mustSupport": true, + "binding": { + "extension": [ + { + "url": "http://hl7.org/fhir/StructureDefinition/elementdefinition-bindingName", + "valueString": "Sleep Study Codes Grouper" + } + ], + "strength": "required", + "valueSet": "http://example.org/sdh/dtr/aslp/ValueSet/aslp-a1-de1-codes-grouper" + }, + "mapping": [ + { + "identity": "ASLP", + "map": "ASLP.A1.DE1" + } + ] + }, + { + "id": "ServiceRequest.occurrence[x]", + "extension": [ + { + "url": "http://hl7.org/fhir/StructureDefinition/cqf-expression", + "valueExpression": { + "language": "text/cql-identifier", + "expression": "Sleep Study Date", + "reference": "http://example.org/sdh/dtr/aslp/Library/ASLPDataElements" + } + } + ], + "path": "ServiceRequest.occurrence[x]", + "short": "Sleep Study Date", + "definition": "Date of the procedure", + "min": 1, + "max": "1", + "type": [ + { + "code": "dateTime" + } + ], + "mustSupport": true, + "mapping": [ + { + "identity": "ASLP", + "map": "ALSP.A1.DE15" + } + ] + } + ] + } + }, + "request": { + "method": "PUT", + "url": "StructureDefinition/aslp-sleep-study-order" + } + }, + { + "resource": { + "resourceType": "StructureDefinition", + "id": "aslp-weight", + "url": "http://example.org/sdh/dtr/aslp/StructureDefinition/aslp-weight", + "name": "ASLPWeight", + "title": "ASLP Weight", + "status": "draft", + "experimental": false, + "description": "ASLP Weight", + "useContext": [ + { + "code": { + "system": "http://terminology.hl7.org/CodeSystem/usage-context-type", + "code": "task", + "display": "Workflow Task" + }, + "valueCodeableConcept": { + "coding": [ + { + "system": "http://fhir.org/guides/nachc/hiv-cds/CodeSystem/activity-codes", + "code": "ASLP.A1", + "display": "Adult Sleep Studies" + } + ] + } + } + ], + "fhirVersion": "4.0.1", + "mapping": [ + { + "identity": "ASLP" + } + ], + "kind": "resource", + "abstract": false, + "type": "Observation", + "baseDefinition": "http://hl7.org/fhir/us/qicore/StructureDefinition/qicore-observation", + "derivation": "constraint", + "differential": { + "element": [ + { + "id": "Observation", + "path": "Observation", + "mustSupport": false + }, + { + "id": "Observation.code", + "path": "Observation.code", + "short": "Weight", + "definition": "Weight (in pounds)", + "type": [ + { + "code": "CodeableConcept" + } + ], + "patternCodeableConcept": { + "coding": [ + { + "system": "http://example.org/sdh/dtr/aslp/CodeSystem/aslp-codes", + "code": "ASLP.A1.DE21", + "display": "Body weight" + } + ] + }, + "min": 1, + "max": "1", + "mustSupport": true, + "mapping": [ + { + "identity": "ASLP", + "map": "ASLP.A1.DE21" + } + ] + }, + { + "id": "Observation.value[x]", + "path": "Observation.value[x]", + "short": "Weight", + "definition": "Weight (in pounds)", + "min": 1, + "max": "1", + "type": [ + { + "code": "Quantity" + } + ], + "mustSupport": true, + "mapping": [ + { + "identity": "ASLP", + "map": "ASLP.A1.DE21" + } + ] + } + ] + } + }, + "request": { + "method": "PUT", + "url": "StructureDefinition/aslp-weight" + } + } + ] +} diff --git a/hapi-fhir-server-cds-hooks/src/test/resources/Bundle-ASLPCrd-Response.json b/hapi-fhir-server-cds-hooks/src/test/resources/Bundle-ASLPCrd-Response.json new file mode 100644 index 00000000000..3994ba29beb --- /dev/null +++ b/hapi-fhir-server-cds-hooks/src/test/resources/Bundle-ASLPCrd-Response.json @@ -0,0 +1,388 @@ +{ + "resourceType": "Bundle", + "id": "ASLPCrd", + "type": "collection", + "entry": [ + { + "resource": { + "resourceType": "RequestGroup", + "id": "ASLPCrd", + "instantiatesCanonical": [ + "http://example.org/sdh/dtr/aslp/PlanDefinition/ASLPCrd|1.0.0" + ], + "status": "draft", + "intent": "proposal", + "subject": { + "reference": "positive" + }, + "action": [ + { + "extension": [ + { + "url": null, + "valueString": "warning" + } + ], + "title": "Patient requires prior authoriztion for a sleep study", + "description": "Patient requires prior authorization due to: history of diabetes. Please open your DTR application and complete Questionniare", + "resource": { + "reference": "Task/ASLPCrd" + } + } + ] + } + }, + { + "resource": { + "resourceType": "Task", + "id": "ASLPCrd", + "meta": { + "versionId": "1" + }, + "extension": [ + { + "url": "http://hl7.org/fhir/aphl/StructureDefinition/condition", + "valueExpression": { + "language": "text/cql-identifier", + "expression": "Is Prior Auth Required" + } + }, + { + "url": "http://hl7.org/fhir/aphl/StructureDefinition/input", + "valueDataRequirement": { + "type": "ServiceRequest", + "profile": [ + "http://example.org/sdh/dtr/aslp/StructureDefinition/aslp-sleep-study-order" + ] + } + } + ], + "basedOn": [ + { + "reference": "RequestGroup/ASLPCrd", + "type": "RequestGroup" + } + ], + "status": "draft", + "intent": "proposal", + "code": { + "coding": [ + { + "system": "http://hl7.org/fhir/uv/cpg/CodeSystem/cpg-activity-type", + "code": "collect-information", + "display": "Collect Information" + } + ] + }, + "for": { + "reference": "positive" + }, + "input": [ + { + "type": { + "coding": [ + { + "code": "collect-information" + } + ] + }, + "valueCanonical": "http://example.org/sdh/dtr/aslp/Questionnaire/ASLPA1" + }, + { + "type": { + "coding": [ + { + "code": "collect-information" + } + ] + }, + "valueCanonical": "http://example.org/sdh/dtr/aslp/Questionnaire/ASLPA2" + } + ] + } + }, + { + "resource": { + "resourceType": "Questionnaire", + "id": "ASLPCrd", + "item": [ + { + "linkId": "1", + "definition": "http://example.org/sdh/dtr/aslp/StructureDefinition/aslp-sleep-study-order", + "text": "ASLP Sleep Study Order", + "type": "group", + "item": [ + { + "linkId": "1.1", + "definition": "http://example.org/sdh/dtr/aslp/StructureDefinition/aslp-sleep-study-order#ServiceRequest.code", + "text": "Sleep Study", + "type": "choice", + "required": true, + "answerOption": [ + { + "valueCoding": { + "system": "http://example.org/sdh/dtr/aslp/CodeSystem/aslp-codes", + "code": "ASLP.A1.DE2", + "display": "Home sleep apnea testing (HSAT)" + } + }, + { + "valueCoding": { + "system": "http://example.org/sdh/dtr/aslp/CodeSystem/aslp-codes", + "code": "ASLP.A1.DE3", + "display": "Peripheral artery tonometry (PAT)" + } + }, + { + "valueCoding": { + "system": "http://example.org/sdh/dtr/aslp/CodeSystem/aslp-codes", + "code": "ASLP.A1.DE4", + "display": "Actigraphy" + } + }, + { + "valueCoding": { + "system": "http://example.org/sdh/dtr/aslp/CodeSystem/aslp-codes", + "code": "ASLP.A1.DE5", + "display": "Prescreening devices or procedures" + } + }, + { + "valueCoding": { + "system": "http://example.org/sdh/dtr/aslp/CodeSystem/aslp-codes", + "code": "ASLP.A1.DE6", + "display": "Acoustic pharyngometry" + } + }, + { + "valueCoding": { + "system": "http://example.org/sdh/dtr/aslp/CodeSystem/aslp-codes", + "code": "ASLP.A1.DE7", + "display": "Digital therapeutics" + } + }, + { + "valueCoding": { + "system": "http://example.org/sdh/dtr/aslp/CodeSystem/aslp-codes", + "code": "ASLP.A1.DE8", + "display": "Home oximetry monitoring" + } + }, + { + "valueCoding": { + "system": "http://example.org/sdh/dtr/aslp/CodeSystem/aslp-codes", + "code": "ASLP.A1.DE9", + "display": "Polysomnogram" + } + }, + { + "valueCoding": { + "system": "http://example.org/sdh/dtr/aslp/CodeSystem/aslp-codes", + "code": "ASLP.A1.DE10", + "display": "Facility-based positive airway pressure (PAP) titration study" + } + }, + { + "valueCoding": { + "system": "http://example.org/sdh/dtr/aslp/CodeSystem/aslp-codes", + "code": "ASLP.A1.DE11", + "display": "Facility-based, daytime, abbreviated, cardiorespiratory sleep studies (PAP NAP testing)" + } + }, + { + "valueCoding": { + "system": "http://example.org/sdh/dtr/aslp/CodeSystem/aslp-codes", + "code": "ASLP.A1.DE12", + "display": "Multiple sleep latency test (MSLT)" + } + }, + { + "valueCoding": { + "system": "http://example.org/sdh/dtr/aslp/CodeSystem/aslp-codes", + "code": "ASLP.A1.DE13", + "display": "Maintenance of wakefulness test (MWT)" + } + }, + { + "valueCoding": { + "system": "http://example.org/sdh/dtr/aslp/CodeSystem/aslp-codes", + "code": "ASLP.A1.DE14", + "display": "Artificial intelligence (AI)" + } + } + ], + "initial": [ + { + "valueCoding": { + "system": "http://example.org/sdh/dtr/aslp/CodeSystem/aslp-codes", + "code": "ASLP.A1.DE2", + "display": "Home sleep apnea testing (HSAT)" + } + }, + { + "valueCoding": { + "system": "http://example.org/sdh/dtr/aslp/CodeSystem/aslp-codes", + "code": "ASLP.A1.DE14", + "display": "Artificial intelligence (AI)" + } + } + ] + }, + { + "linkId": "1.2", + "definition": "http://example.org/sdh/dtr/aslp/StructureDefinition/aslp-sleep-study-order#ServiceRequest.occurrence[x]", + "text": "Sleep Study Date", + "type": "dateTime", + "required": true, + "initial": [ + { + "valueDateTime": "2023-04-10T08:00:00.000Z" + }, + { + "valueDateTime": "2023-04-15T08:00:00.000Z" + } + ] + } + ] + }, + { + "linkId": "2", + "definition": "http://example.org/sdh/dtr/aslp/StructureDefinition/aslp-diagnosis-of-obstructive-sleep-apnea", + "text": "ASLP Diagnosis of Obstructive Sleep Apnea", + "type": "group", + "item": [ + { + "linkId": "2.1", + "definition": "http://example.org/sdh/dtr/aslp/StructureDefinition/aslp-diagnosis-of-obstructive-sleep-apnea#Condition.code", + "text": "Diagnosis of Obstructive Sleep Apnea", + "type": "choice", + "required": true, + "answerOption": [ + { + "valueCoding": { + "system": "http://example.org/sdh/dtr/aslp/CodeSystem/aslp-codes", + "code": "ASLP.A1.DE17", + "display": "Obstructive sleep apnea (OSA)" + } + } + ] + } + ] + }, + { + "linkId": "3", + "definition": "http://example.org/sdh/dtr/aslp/StructureDefinition/aslp-history-of-hypertension", + "text": "ASLP History of Hypertension", + "type": "group", + "item": [ + { + "linkId": "3.1", + "text": "An error occurred during item creation: null", + "type": "display" + }, + { + "linkId": "3.2", + "definition": "http://example.org/sdh/dtr/aslp/StructureDefinition/aslp-history-of-hypertension#Observation.value[x]", + "text": "History of Hypertension", + "type": "boolean", + "required": true + } + ] + }, + { + "linkId": "4", + "definition": "http://example.org/sdh/dtr/aslp/StructureDefinition/aslp-history-of-diabetes", + "text": "ASLP History of Diabetes", + "type": "group", + "item": [ + { + "linkId": "4.1", + "text": "An error occurred during item creation: null", + "type": "display" + }, + { + "linkId": "4.2", + "definition": "http://example.org/sdh/dtr/aslp/StructureDefinition/aslp-history-of-diabetes#Observation.value[x]", + "text": "History of Diabetes", + "type": "boolean", + "required": true + } + ] + }, + { + "linkId": "5", + "definition": "http://example.org/sdh/dtr/aslp/StructureDefinition/aslp-neck-circumference", + "text": "ASLP Neck Circumference", + "type": "group", + "item": [ + { + "linkId": "5.1", + "text": "An error occurred during item creation: null", + "type": "display" + }, + { + "linkId": "5.2", + "text": "An error occurred during item creation: Unknown QuestionnaireItemType code 'Quantity'", + "type": "display" + } + ] + }, + { + "linkId": "6", + "definition": "http://example.org/sdh/dtr/aslp/StructureDefinition/aslp-height", + "text": "ASLP Height", + "type": "group", + "item": [ + { + "linkId": "6.1", + "text": "An error occurred during item creation: null", + "type": "display" + }, + { + "linkId": "6.2", + "text": "An error occurred during item creation: Unknown QuestionnaireItemType code 'Quantity'", + "type": "display" + } + ] + }, + { + "linkId": "7", + "definition": "http://example.org/sdh/dtr/aslp/StructureDefinition/aslp-weight", + "text": "ASLP Weight", + "type": "group", + "item": [ + { + "linkId": "7.1", + "text": "An error occurred during item creation: null", + "type": "display" + }, + { + "linkId": "7.2", + "text": "An error occurred during item creation: Unknown QuestionnaireItemType code 'Quantity'", + "type": "display" + } + ] + }, + { + "linkId": "8", + "definition": "http://example.org/sdh/dtr/aslp/StructureDefinition/aslp-bmi", + "text": "ASLP BMI", + "type": "group", + "item": [ + { + "linkId": "8.1", + "text": "An error occurred during item creation: null", + "type": "display" + }, + { + "linkId": "8.2", + "text": "An error occurred during item creation: Unknown QuestionnaireItemType code 'Quantity'", + "type": "display" + } + ] + } + ] + } + } + ] +} diff --git a/hapi-fhir-server-cds-hooks/src/test/resources/Bundle-DischargeInstructionsPlan-Content.json b/hapi-fhir-server-cds-hooks/src/test/resources/Bundle-DischargeInstructionsPlan-Content.json new file mode 100644 index 00000000000..0193c55f157 --- /dev/null +++ b/hapi-fhir-server-cds-hooks/src/test/resources/Bundle-DischargeInstructionsPlan-Content.json @@ -0,0 +1,161 @@ +{ + "resourceType": "Bundle", + "type": "transaction", + "entry": [ + { + "fullUrl": "ActivityDefinition/SendMessageActivity", + "resource": { + "resourceType": "ActivityDefinition", + "id": "SendMessageActivity", + "meta": { + "profile": [ + "http://hl7.org/fhir/uv/cpg/StructureDefinition/cpg-communicationactivity" + ] + }, + "kind": "CommunicationRequest", + "profile": "http://hl7.org/fhir/uv/cpg/StructureDefinition/cpg-communicationrequest", + "intent": "proposal", + "extension": [ + { + "url": "http://hl7.org/fhir/uv/cpg/StructureDefinition/cpg-knowledgeCapability", + "valueCode": "publishable" + }, + { + "url": "http://hl7.org/fhir/uv/cpg/StructureDefinition/cpg-knowledgeRepresentationLevel", + "valueCode": "structured" + } + ], + "url": "http://example.org/ActivityDefinition/SendMessageActivity", + "name": "SendMessageActivity", + "title": "ActivityDefinition SendMessageActivity", + "status": "draft", + "experimental": true, + "publisher": "Example", + "jurisdiction": [ + { + "coding": [ + { + "code": "001", + "system": "http://unstats.un.org/unsd/methods/m49/m49.htm", + "display": "World" + } + ] + } + ], + "version": "0.1.0", + "description": "Example Activity Definition for a recommendation to send a message", + "code": { + "coding": [ + { + "code": "send-message", + "system": "http://hl7.org/fhir/uv/cpg/CodeSystem/cpg-activity-type", + "display": "Send a message" + } + ] + }, + "doNotPerform": false, + "dynamicValue": [ + { + "path": "payload[0].contentString", + "expression": { + "language": "text/fhirpath", + "expression": "'Greeting: Hello! ' + %subject.name.given.first() + ' Message: Example Activity Definition for a recommendation to send a message Practitioner: ' + %practitioner.name.given.first()" + } + } + ] + }, + "request": { + "method": "PUT", + "url": "ActivityDefinition/SendMessageActivity" + } + }, + { + "fullUrl": "PlanDefinition/DischargeInstructionsPlan", + "resource": { + "resourceType": "PlanDefinition", + "id": "DischargeInstructionsPlan", + "meta": { + "profile": [ + "http://hl7.org/fhir/uv/cpg/StructureDefinition/cpg-computableplandefinition" + ] + }, + "extension": [ + { + "url": "http://hl7.org/fhir/uv/cpg/StructureDefinition/cpg-knowledgeCapability", + "valueCode": "publishable" + }, + { + "url": "http://hl7.org/fhir/uv/cpg/StructureDefinition/cpg-knowledgeRepresentationLevel", + "valueCode": "structured" + } + ], + "url": "http://example.org/PlanDefinition/DischargeInstructionsPlan", + "name": "DischargeInstructionsPlan", + "title": "PlanDefinition DischargeInstructionsPlan", + "status": "draft", + "experimental": true, + "publisher": "Example", + "jurisdiction": [ + { + "coding": [ + { + "code": "001", + "system": "http://unstats.un.org/unsd/methods/m49/m49.htm", + "display": "World" + } + ] + } + ], + "version": "0.1.0", + "description": "Provide patient discharge instructions", + "type": { + "coding": [ + { + "code": "clinical-protocol", + "system": "http://terminology.hl7.org/CodeSystem/plan-definition-type", + "display": "Clinical Protocol" + } + ] + }, + "action": [ + { + "title": "Send message with discharge instructions", + "code": [ + { + "coding": [ + { + "code": "provide-counseling", + "system": "http://hl7.org/fhir/uv/cpg/CodeSystem/cpg-common-process", + "display": "Provide Counseling" + } + ] + } + ], + "type": { + "coding": [ + { + "code": "create", + "system": "http://terminology.hl7.org/CodeSystem/action-type" + } + ] + }, + "dynamicValue": [ + { + "path": "payload[0].contentString", + "expression": { + "language": "text/fhirpath", + "expression": "'Provide patient discharge instructions for ' + %subject.name.given.first()" + } + } + ], + "definitionCanonical": "http://example.org/ActivityDefinition/SendMessageActivity" + } + ] + }, + "request": { + "method": "PUT", + "url": "PlanDefinition/DischargeInstructionsPlan" + } + } + ] +} diff --git a/hapi-fhir-server-cds-hooks/src/test/resources/Bundle-DischargeInstructionsPlan-Response.json b/hapi-fhir-server-cds-hooks/src/test/resources/Bundle-DischargeInstructionsPlan-Response.json new file mode 100644 index 00000000000..f722c2f511f --- /dev/null +++ b/hapi-fhir-server-cds-hooks/src/test/resources/Bundle-DischargeInstructionsPlan-Response.json @@ -0,0 +1,87 @@ +{ + "resourceType": "Bundle", + "id": "DischargeInstructionsPlan", + "type": "collection", + "entry": [ + { + "resource": { + "resourceType": "RequestGroup", + "id": "DischargeInstructionsPlan", + "meta": { + "profile": [ + "http://hl7.org/fhir/uv/cpg/StructureDefinition/cpg-strategy" + ] + }, + "instantiatesCanonical": [ + "http://example.org/PlanDefinition/DischargeInstructionsPlan|0.1.0" + ], + "status": "draft", + "intent": "proposal", + "subject": { + "reference": "Patient/Patient1" + }, + "encounter": { + "reference": "Encounter/Encounter1" + }, + "author": { + "reference": "Practitioner/Practitioner1" + }, + "action": [ + { + "title": "Send message with discharge instructions", + "code": [ + { + "coding": [ + { + "system": "http://hl7.org/fhir/uv/cpg/CodeSystem/cpg-common-process", + "code": "provide-counseling", + "display": "Provide Counseling" + } + ] + } + ], + "type": { + "coding": [ + { + "system": "http://terminology.hl7.org/CodeSystem/action-type", + "code": "create" + } + ] + }, + "resource": { + "reference": "CommunicationRequest/SendMessageActivity" + } + } + ] + } + }, + { + "resource": { + "resourceType": "CommunicationRequest", + "id": "SendMessageActivity", + "meta": { + "versionId": "2", + "profile": [ + "http://hl7.org/fhir/uv/cpg/StructureDefinition/cpg-communicationrequest" + ] + }, + "status": "draft", + "doNotPerform": false, + "subject": { + "reference": "Patient/Patient1" + }, + "encounter": { + "reference": "Encounter/Encounter1" + }, + "payload": [ + { + "contentString": "Provide patient discharge instructions for Alice" + } + ], + "requester": { + "reference": "Practitioner/Practitioner1" + } + } + } + ] +} diff --git a/hapi-fhir-server-mdm/pom.xml b/hapi-fhir-server-mdm/pom.xml index b541b0f0a2c..6e163215517 100644 --- a/hapi-fhir-server-mdm/pom.xml +++ b/hapi-fhir-server-mdm/pom.xml @@ -7,7 +7,7 @@ ca.uhn.hapi.fhir hapi-deployable-pom - 6.9.7-SNAPSHOT + 6.11.7-SNAPSHOT ../hapi-deployable-pom/pom.xml diff --git a/hapi-fhir-server-mdm/src/main/java/ca/uhn/fhir/mdm/api/BaseMdmMetricSvc.java b/hapi-fhir-server-mdm/src/main/java/ca/uhn/fhir/mdm/api/BaseMdmMetricSvc.java new file mode 100644 index 00000000000..cd80f84c29a --- /dev/null +++ b/hapi-fhir-server-mdm/src/main/java/ca/uhn/fhir/mdm/api/BaseMdmMetricSvc.java @@ -0,0 +1,113 @@ +/*- + * #%L + * HAPI FHIR - Master Data Management + * %% + * Copyright (C) 2014 - 2023 Smile CDR, Inc. + * %% + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * #L% + */ +package ca.uhn.fhir.mdm.api; + +import ca.uhn.fhir.jpa.api.dao.DaoRegistry; +import ca.uhn.fhir.jpa.api.dao.IFhirResourceDao; +import ca.uhn.fhir.jpa.searchparam.SearchParameterMap; +import ca.uhn.fhir.mdm.api.params.GenerateMdmMetricsParameters; +import ca.uhn.fhir.mdm.model.MdmResourceMetrics; +import ca.uhn.fhir.mdm.util.MdmSearchParamBuildingUtils; +import ca.uhn.fhir.rest.api.SearchTotalModeEnum; +import ca.uhn.fhir.rest.api.server.IBundleProvider; +import ca.uhn.fhir.rest.api.server.SystemRequestDetails; + +public abstract class BaseMdmMetricSvc implements IMdmMetricSvc { + + /** + * Count of numbered buckets. + * There will also be a NULL bucket, so there will be a total + * of BUCKETS + 1 buckets. + */ + public static final int BUCKETS = 100; + + /** + * The NULL label + */ + public static final String NULL_VALUE = "NULL"; + + /** + * The label for the first bucket + */ + public static final String FIRST_BUCKET = "x_<_%.2f"; + + /** + * The label for the nth bucket (2... buckets) + */ + public static final String NTH_BUCKET = "%.2f_<_x_<=_%.2f"; + + protected final DaoRegistry myDaoRegistry; + + public BaseMdmMetricSvc(DaoRegistry theDaoRegistry) { + myDaoRegistry = theDaoRegistry; + } + + protected double getBucket(int theBucketId) { + return (double) Math.round((float) (100 * theBucketId) / BUCKETS) / 100; + } + + protected MdmResourceMetrics generateResourceMetrics(GenerateMdmMetricsParameters theParameters) { + String resourceType = theParameters.getResourceType(); + @SuppressWarnings("rawtypes") + IFhirResourceDao dao = myDaoRegistry.getResourceDao(resourceType); + + // TODO + /* + * We are using 3 different queries to count: + * * all resources + * * all golden resources + * * all blocked resources. + * + * This is inefficient and if we want, we can speed it up with + * a custom query in the future. + */ + IBundleProvider outcome = null; + SearchParameterMap map = null; + + MdmResourceMetrics metrics = new MdmResourceMetrics(); + metrics.setResourceType(resourceType); + + // find golden resources + map = MdmSearchParamBuildingUtils.buildBasicGoldenResourceSearchParameterMap(resourceType); + setCountOnly(map); + outcome = dao.search(map, new SystemRequestDetails()); + metrics.setGoldenResourcesCount(outcome.size()); + + // find blocked resources + map = MdmSearchParamBuildingUtils.buildSearchParameterForBlockedResourceCount(resourceType); + setCountOnly(map); + outcome = dao.search(map, new SystemRequestDetails()); + metrics.setExcludedResources(outcome.size()); + + // find all resources + map = new SearchParameterMap(); + setCountOnly(map); + outcome = dao.search(map, new SystemRequestDetails()); + metrics.setSourceResourcesCount(outcome.size() - metrics.getGoldenResourcesCount()); + + return metrics; + } + + private void setCountOnly(SearchParameterMap theMap) { + theMap.setCount(0); + theMap.setLoadSynchronous(true); + theMap.setSearchTotalMode(SearchTotalModeEnum.ACCURATE); + } +} diff --git a/hapi-fhir-server-mdm/src/main/java/ca/uhn/fhir/mdm/api/IMdmControllerSvc.java b/hapi-fhir-server-mdm/src/main/java/ca/uhn/fhir/mdm/api/IMdmControllerSvc.java index dc6051daa13..e9c86871cf1 100644 --- a/hapi-fhir-server-mdm/src/main/java/ca/uhn/fhir/mdm/api/IMdmControllerSvc.java +++ b/hapi-fhir-server-mdm/src/main/java/ca/uhn/fhir/mdm/api/IMdmControllerSvc.java @@ -30,6 +30,7 @@ import ca.uhn.fhir.mdm.model.mdmevents.MdmLinkJson; import ca.uhn.fhir.mdm.model.mdmevents.MdmLinkWithRevisionJson; import ca.uhn.fhir.rest.api.server.RequestDetails; import ca.uhn.fhir.rest.server.servlet.ServletRequestDetails; +import jakarta.annotation.Nullable; import org.hl7.fhir.instance.model.api.IAnyResource; import org.hl7.fhir.instance.model.api.IBaseParameters; import org.hl7.fhir.instance.model.api.IPrimitiveType; @@ -37,7 +38,6 @@ import org.springframework.data.domain.Page; import java.math.BigDecimal; import java.util.List; -import javax.annotation.Nullable; public interface IMdmControllerSvc { @Deprecated diff --git a/hapi-fhir-server-mdm/src/main/java/ca/uhn/fhir/mdm/api/IMdmMatchFinderSvc.java b/hapi-fhir-server-mdm/src/main/java/ca/uhn/fhir/mdm/api/IMdmMatchFinderSvc.java index 9d94270d192..0acc5d12946 100644 --- a/hapi-fhir-server-mdm/src/main/java/ca/uhn/fhir/mdm/api/IMdmMatchFinderSvc.java +++ b/hapi-fhir-server-mdm/src/main/java/ca/uhn/fhir/mdm/api/IMdmMatchFinderSvc.java @@ -20,10 +20,10 @@ package ca.uhn.fhir.mdm.api; import ca.uhn.fhir.interceptor.model.RequestPartitionId; +import jakarta.annotation.Nonnull; import org.hl7.fhir.instance.model.api.IAnyResource; import java.util.List; -import javax.annotation.Nonnull; public interface IMdmMatchFinderSvc { diff --git a/hapi-fhir-server-mdm/src/main/java/ca/uhn/fhir/mdm/api/IMdmMetricSvc.java b/hapi-fhir-server-mdm/src/main/java/ca/uhn/fhir/mdm/api/IMdmMetricSvc.java new file mode 100644 index 00000000000..aaea222e2cc --- /dev/null +++ b/hapi-fhir-server-mdm/src/main/java/ca/uhn/fhir/mdm/api/IMdmMetricSvc.java @@ -0,0 +1,38 @@ +/*- + * #%L + * HAPI FHIR - Master Data Management + * %% + * Copyright (C) 2014 - 2023 Smile CDR, Inc. + * %% + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * #L% + */ +package ca.uhn.fhir.mdm.api; + +import ca.uhn.fhir.mdm.api.params.GenerateMdmMetricsParameters; +import ca.uhn.fhir.mdm.model.MdmMetrics; + +public interface IMdmMetricSvc { + + /** + * Generates metrics on MDM Links. + * Metrics include: + * * breakdowns of counts of MATCH_RESULT types by LINK_SOURCE types. + * * counts of resources of each type + * * a histogram of score 'buckets' with the appropriate counts. + * @param theParameters - Parameters defining resource type of interest, + * as well as MatchResult and LinkSource filters. + * @return The metrics in a JSON format. + */ + MdmMetrics generateMdmMetrics(GenerateMdmMetricsParameters theParameters); +} diff --git a/hapi-fhir-server-mdm/src/main/java/ca/uhn/fhir/mdm/api/IMdmResourceDaoSvc.java b/hapi-fhir-server-mdm/src/main/java/ca/uhn/fhir/mdm/api/IMdmResourceDaoSvc.java new file mode 100644 index 00000000000..866dd0b87dc --- /dev/null +++ b/hapi-fhir-server-mdm/src/main/java/ca/uhn/fhir/mdm/api/IMdmResourceDaoSvc.java @@ -0,0 +1,46 @@ +/*- + * #%L + * HAPI FHIR - Master Data Management + * %% + * Copyright (C) 2014 - 2023 Smile CDR, Inc. + * %% + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * #L% + */ +package ca.uhn.fhir.mdm.api; + +import ca.uhn.fhir.interceptor.model.RequestPartitionId; +import ca.uhn.fhir.jpa.api.model.DaoMethodOutcome; +import ca.uhn.fhir.rest.api.server.storage.IResourcePersistentId; +import org.hl7.fhir.instance.model.api.IAnyResource; + +import java.util.Optional; + +public interface IMdmResourceDaoSvc { + DaoMethodOutcome upsertGoldenResource(IAnyResource theGoldenResource, String theResourceType); + + /** + * Given a resource, remove its Golden Resource tag. + * + * @param theGoldenResource the {@link IAnyResource} to remove the tag from. + * @param theResourcetype the type of that resource + */ + void removeGoldenResourceTag(IAnyResource theGoldenResource, String theResourcetype); + + IAnyResource readGoldenResourceByPid(IResourcePersistentId theGoldenResourcePid, String theResourceType); + + Optional searchGoldenResourceByEID(String theEid, String theResourceType); + + Optional searchGoldenResourceByEID( + String theEid, String theResourceType, RequestPartitionId thePartitionId); +} diff --git a/hapi-fhir-server-mdm/src/main/java/ca/uhn/fhir/mdm/api/IMdmSubmitSvc.java b/hapi-fhir-server-mdm/src/main/java/ca/uhn/fhir/mdm/api/IMdmSubmitSvc.java index ec1181f2238..24ca6045289 100644 --- a/hapi-fhir-server-mdm/src/main/java/ca/uhn/fhir/mdm/api/IMdmSubmitSvc.java +++ b/hapi-fhir-server-mdm/src/main/java/ca/uhn/fhir/mdm/api/IMdmSubmitSvc.java @@ -20,10 +20,9 @@ package ca.uhn.fhir.mdm.api; import ca.uhn.fhir.rest.api.server.RequestDetails; +import jakarta.annotation.Nullable; import org.hl7.fhir.instance.model.api.IIdType; -import javax.annotation.Nullable; - public interface IMdmSubmitSvc { /** diff --git a/hapi-fhir-server-mdm/src/main/java/ca/uhn/fhir/mdm/api/MdmConstants.java b/hapi-fhir-server-mdm/src/main/java/ca/uhn/fhir/mdm/api/MdmConstants.java index 7d35442c202..448c32856ee 100644 --- a/hapi-fhir-server-mdm/src/main/java/ca/uhn/fhir/mdm/api/MdmConstants.java +++ b/hapi-fhir-server-mdm/src/main/java/ca/uhn/fhir/mdm/api/MdmConstants.java @@ -34,10 +34,18 @@ public class MdmConstants { "http://hapifhir.io/fhir/NamingSystem/mdm-golden-resource-enterprise-id"; public static final String ALL_RESOURCE_SEARCH_PARAM_TYPE = "*"; + /** + * Blocked resource tag info + */ + public static final String CODE_BLOCKED = "BLOCKED_RESOURCE"; + + public static final String CODE_BLOCKED_DISPLAY = "Source Resource is omitted from MDM matching."; + public static final String FIHR_STRUCTURE_DEF_MATCH_GRADE_URL_NAMESPACE = "http://hl7.org/fhir/StructureDefinition/match-grade"; public static final String SYSTEM_GOLDEN_RECORD_STATUS = "http://hapifhir.io/fhir/NamingSystem/mdm-record-status"; + public static final String SUBSCRIPTION_TOPIC_URL = "http://hapifhir.io/fhir/r5/SubscriptionTopic/mdm"; public static final String CODE_GOLDEN_RECORD = "GOLDEN_RECORD"; public static final String CODE_GOLDEN_RECORD_REDIRECTED = "REDIRECTED"; public static final String DISPLAY_GOLDEN_RECORD = "Golden Record"; diff --git a/hapi-fhir-server-mdm/src/main/java/ca/uhn/fhir/mdm/api/paging/MdmPageRequest.java b/hapi-fhir-server-mdm/src/main/java/ca/uhn/fhir/mdm/api/paging/MdmPageRequest.java index 64c5667c3d9..982a54b5c4f 100644 --- a/hapi-fhir-server-mdm/src/main/java/ca/uhn/fhir/mdm/api/paging/MdmPageRequest.java +++ b/hapi-fhir-server-mdm/src/main/java/ca/uhn/fhir/mdm/api/paging/MdmPageRequest.java @@ -21,12 +21,11 @@ package ca.uhn.fhir.mdm.api.paging; import ca.uhn.fhir.i18n.Msg; import ca.uhn.fhir.rest.server.exceptions.InvalidRequestException; +import jakarta.annotation.Nullable; import org.apache.commons.lang3.StringUtils; import org.hl7.fhir.instance.model.api.IPrimitiveType; import org.springframework.data.domain.PageRequest; -import javax.annotation.Nullable; - import static ca.uhn.fhir.rest.api.Constants.PARAM_COUNT; import static ca.uhn.fhir.rest.api.Constants.PARAM_OFFSET; diff --git a/hapi-fhir-server-mdm/src/main/java/ca/uhn/fhir/mdm/api/params/GenerateMdmLinkMetricParameters.java b/hapi-fhir-server-mdm/src/main/java/ca/uhn/fhir/mdm/api/params/GenerateMdmLinkMetricParameters.java new file mode 100644 index 00000000000..6a0ba65b3b8 --- /dev/null +++ b/hapi-fhir-server-mdm/src/main/java/ca/uhn/fhir/mdm/api/params/GenerateMdmLinkMetricParameters.java @@ -0,0 +1,79 @@ +/*- + * #%L + * HAPI FHIR - Master Data Management + * %% + * Copyright (C) 2014 - 2023 Smile CDR, Inc. + * %% + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * #L% + */ +package ca.uhn.fhir.mdm.api.params; + +import ca.uhn.fhir.mdm.api.MdmLinkSourceEnum; +import ca.uhn.fhir.mdm.api.MdmMatchResultEnum; + +import java.util.ArrayList; +import java.util.List; + +public class GenerateMdmLinkMetricParameters { + + /** + * The resource type of interest. + * Must be provided! + */ + private final String myResourceType; + + /** + * The MDM MatchResult types of interest. + * Specified MatchResults will be included. + * If none are specified, all will be included. + */ + private List myMatchResultFilters; + + /** + * The MDM Link values of interest. + * Specified LinkSources will be included. + * If none are specified, all are included. + */ + private List myLinkSourceFilters; + + public GenerateMdmLinkMetricParameters(String theResourceType) { + myResourceType = theResourceType; + } + + public String getResourceType() { + return myResourceType; + } + + public List getMatchResultFilters() { + if (myMatchResultFilters == null) { + myMatchResultFilters = new ArrayList<>(); + } + return myMatchResultFilters; + } + + public void addMatchResultFilter(MdmMatchResultEnum theMdmMatchResultEnum) { + getMatchResultFilters().add(theMdmMatchResultEnum); + } + + public List getLinkSourceFilters() { + if (myLinkSourceFilters == null) { + myLinkSourceFilters = new ArrayList<>(); + } + return myLinkSourceFilters; + } + + public void addLinkSourceFilter(MdmLinkSourceEnum theLinkSource) { + getLinkSourceFilters().add(theLinkSource); + } +} diff --git a/hapi-fhir-server-mdm/src/main/java/ca/uhn/fhir/mdm/api/params/GenerateMdmMetricsParameters.java b/hapi-fhir-server-mdm/src/main/java/ca/uhn/fhir/mdm/api/params/GenerateMdmMetricsParameters.java new file mode 100644 index 00000000000..6af297b7786 --- /dev/null +++ b/hapi-fhir-server-mdm/src/main/java/ca/uhn/fhir/mdm/api/params/GenerateMdmMetricsParameters.java @@ -0,0 +1,88 @@ +/*- + * #%L + * HAPI FHIR - Master Data Management + * %% + * Copyright (C) 2014 - 2023 Smile CDR, Inc. + * %% + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * #L% + */ +package ca.uhn.fhir.mdm.api.params; + +import ca.uhn.fhir.mdm.api.MdmLinkSourceEnum; +import ca.uhn.fhir.mdm.api.MdmMatchResultEnum; + +import java.util.ArrayList; +import java.util.List; + +public class GenerateMdmMetricsParameters { + + /** + * We only allow finding metrics by resource type + */ + private final String myResourceType; + + /** + * The MDM MatchResult types of interest. + * Specified MatchResults will be included. + * If none are specified, all will be included. + */ + private List myMatchResultFilters; + + /** + * The MDM Link values of interest. + * Specified LinkSources will be included. + * If none are specified, all are included. + */ + private List myLinkSourceFilters; + + public GenerateMdmMetricsParameters(String theResourceType) { + myResourceType = theResourceType; + } + + public String getResourceType() { + return myResourceType; + } + + public List getMatchResultFilters() { + if (myMatchResultFilters == null) { + myMatchResultFilters = new ArrayList<>(); + } + return myMatchResultFilters; + } + + public void addMatchResult(MdmMatchResultEnum theMdmMatchResultEnum) { + getMatchResultFilters().add(theMdmMatchResultEnum); + } + + public List getLinkSourceFilters() { + if (myLinkSourceFilters == null) { + myLinkSourceFilters = new ArrayList<>(); + } + return myLinkSourceFilters; + } + + public void addLinkSource(MdmLinkSourceEnum theLinkSource) { + getLinkSourceFilters().add(theLinkSource); + } + + // public GenerateMdmLinkMetricParameters toLinkMetricParams() { + // + // } + // + // public GenerateMdmResourceMetricsParameters toResourceMetricParams() { + // + // } + // + +} diff --git a/hapi-fhir-server-mdm/src/main/java/ca/uhn/fhir/mdm/api/params/GenerateMdmResourceMetricsParameters.java b/hapi-fhir-server-mdm/src/main/java/ca/uhn/fhir/mdm/api/params/GenerateMdmResourceMetricsParameters.java new file mode 100644 index 00000000000..550227100d4 --- /dev/null +++ b/hapi-fhir-server-mdm/src/main/java/ca/uhn/fhir/mdm/api/params/GenerateMdmResourceMetricsParameters.java @@ -0,0 +1,36 @@ +/*- + * #%L + * HAPI FHIR - Master Data Management + * %% + * Copyright (C) 2014 - 2023 Smile CDR, Inc. + * %% + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * #L% + */ +package ca.uhn.fhir.mdm.api.params; + +public class GenerateMdmResourceMetricsParameters { + + /** + * We only allow finding metrics by resource type + */ + private final String myResourceType; + + public GenerateMdmResourceMetricsParameters(String theResourceType) { + myResourceType = theResourceType; + } + + public String getResourceType() { + return myResourceType; + } +} diff --git a/hapi-fhir-server-mdm/src/main/java/ca/uhn/fhir/mdm/api/params/GenerateScoreMetricsParameters.java b/hapi-fhir-server-mdm/src/main/java/ca/uhn/fhir/mdm/api/params/GenerateScoreMetricsParameters.java new file mode 100644 index 00000000000..19b94db4218 --- /dev/null +++ b/hapi-fhir-server-mdm/src/main/java/ca/uhn/fhir/mdm/api/params/GenerateScoreMetricsParameters.java @@ -0,0 +1,58 @@ +/*- + * #%L + * HAPI FHIR - Master Data Management + * %% + * Copyright (C) 2014 - 2023 Smile CDR, Inc. + * %% + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * #L% + */ +package ca.uhn.fhir.mdm.api.params; + +import ca.uhn.fhir.mdm.api.MdmMatchResultEnum; + +import java.util.ArrayList; +import java.util.List; + +public class GenerateScoreMetricsParameters { + /** + * The resource type of interest. + */ + private final String myResourceType; + + /** + * MatchResult types to filter for. + * Specified MatchResults will be included. + * If none specified, all will be included. + */ + private List myMatchTypeFilters; + + public GenerateScoreMetricsParameters(String theResourceType) { + myResourceType = theResourceType; + } + + public String getResourceType() { + return myResourceType; + } + + public List getMatchTypes() { + if (myMatchTypeFilters == null) { + myMatchTypeFilters = new ArrayList<>(); + } + return myMatchTypeFilters; + } + + public void addMatchType(MdmMatchResultEnum theMatchType) { + getMatchTypes().add(theMatchType); + } +} diff --git a/hapi-fhir-server-mdm/src/main/java/ca/uhn/fhir/mdm/api/params/MdmHistorySearchParameters.java b/hapi-fhir-server-mdm/src/main/java/ca/uhn/fhir/mdm/api/params/MdmHistorySearchParameters.java index c16c3ecdeea..fb11027d86d 100644 --- a/hapi-fhir-server-mdm/src/main/java/ca/uhn/fhir/mdm/api/params/MdmHistorySearchParameters.java +++ b/hapi-fhir-server-mdm/src/main/java/ca/uhn/fhir/mdm/api/params/MdmHistorySearchParameters.java @@ -22,6 +22,8 @@ package ca.uhn.fhir.mdm.api.params; import ca.uhn.fhir.mdm.provider.MdmControllerUtil; import ca.uhn.fhir.rest.server.provider.ProviderConstants; +import jakarta.annotation.Nonnull; +import jakarta.annotation.Nullable; import org.apache.commons.lang3.builder.ToStringBuilder; import org.hl7.fhir.instance.model.api.IIdType; @@ -29,8 +31,6 @@ import java.util.ArrayList; import java.util.List; import java.util.Objects; import java.util.stream.Collectors; -import javax.annotation.Nonnull; -import javax.annotation.Nullable; public class MdmHistorySearchParameters { private List myGoldenResourceIds = new ArrayList<>(); diff --git a/hapi-fhir-server-mdm/src/main/java/ca/uhn/fhir/mdm/api/params/MdmQuerySearchParameters.java b/hapi-fhir-server-mdm/src/main/java/ca/uhn/fhir/mdm/api/params/MdmQuerySearchParameters.java index 6deeb0c2d46..3b42afcacb2 100644 --- a/hapi-fhir-server-mdm/src/main/java/ca/uhn/fhir/mdm/api/params/MdmQuerySearchParameters.java +++ b/hapi-fhir-server-mdm/src/main/java/ca/uhn/fhir/mdm/api/params/MdmQuerySearchParameters.java @@ -28,13 +28,13 @@ import ca.uhn.fhir.rest.api.SortOrderEnum; import ca.uhn.fhir.rest.api.SortSpec; import ca.uhn.fhir.rest.server.exceptions.InvalidRequestException; import ca.uhn.fhir.rest.server.provider.ProviderConstants; +import jakarta.annotation.Nullable; import org.apache.commons.lang3.builder.ToStringBuilder; import org.hl7.fhir.instance.model.api.IIdType; import java.util.ArrayList; import java.util.List; import java.util.Set; -import javax.annotation.Nullable; import static org.hibernate.internal.util.StringHelper.isBlank; diff --git a/hapi-fhir-server-mdm/src/main/java/ca/uhn/fhir/mdm/model/CanonicalEID.java b/hapi-fhir-server-mdm/src/main/java/ca/uhn/fhir/mdm/model/CanonicalEID.java index 07f7858bc3a..7072483ec05 100644 --- a/hapi-fhir-server-mdm/src/main/java/ca/uhn/fhir/mdm/model/CanonicalEID.java +++ b/hapi-fhir-server-mdm/src/main/java/ca/uhn/fhir/mdm/model/CanonicalEID.java @@ -82,6 +82,13 @@ public class CanonicalEID { .setValue(myValue); } + public org.hl7.fhir.r5.model.Identifier toR5() { + return new org.hl7.fhir.r5.model.Identifier() + .setUse(org.hl7.fhir.r5.model.Identifier.IdentifierUse.fromCode(myUse)) + .setSystem(mySystem) + .setValue(myValue); + } + public String getSystem() { return mySystem; } diff --git a/hapi-fhir-server-mdm/src/main/java/ca/uhn/fhir/mdm/model/MdmLinkMetrics.java b/hapi-fhir-server-mdm/src/main/java/ca/uhn/fhir/mdm/model/MdmLinkMetrics.java new file mode 100644 index 00000000000..35c63a36b4b --- /dev/null +++ b/hapi-fhir-server-mdm/src/main/java/ca/uhn/fhir/mdm/model/MdmLinkMetrics.java @@ -0,0 +1,71 @@ +/*- + * #%L + * HAPI FHIR - Master Data Management + * %% + * Copyright (C) 2014 - 2023 Smile CDR, Inc. + * %% + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * #L% + */ +package ca.uhn.fhir.mdm.model; + +import ca.uhn.fhir.mdm.api.MdmLinkSourceEnum; +import ca.uhn.fhir.mdm.api.MdmMatchResultEnum; + +import java.util.HashMap; +import java.util.Map; + +public class MdmLinkMetrics { + /** + * The resource type to which these metrics apply. + */ + private String myResourceType; + + /** + * A mapping of MatchType -> LinkSource -> count. + * Eg: + * MATCH + * AUTO - 2 + * MANUAL - 1 + * NO_MATCH + * AUTO - 1 + * MANUAL - 3 + */ + private Map> myMatchTypeToLinkToCountMap; + + public String getResourceType() { + return myResourceType; + } + + public void setResourceType(String theResourceType) { + myResourceType = theResourceType; + } + + public Map> getMatchTypeToLinkToCountMap() { + if (myMatchTypeToLinkToCountMap == null) { + myMatchTypeToLinkToCountMap = new HashMap<>(); + } + return myMatchTypeToLinkToCountMap; + } + + public void addMetric( + MdmMatchResultEnum theMdmMatchResultEnum, MdmLinkSourceEnum theLinkSourceEnum, long theCount) { + Map> map = getMatchTypeToLinkToCountMap(); + + if (!map.containsKey(theMdmMatchResultEnum)) { + map.put(theMdmMatchResultEnum, new HashMap<>()); + } + Map lsToCountMap = map.get(theMdmMatchResultEnum); + lsToCountMap.put(theLinkSourceEnum, theCount); + } +} diff --git a/hapi-fhir-server-mdm/src/main/java/ca/uhn/fhir/mdm/model/MdmLinkScoreMetrics.java b/hapi-fhir-server-mdm/src/main/java/ca/uhn/fhir/mdm/model/MdmLinkScoreMetrics.java new file mode 100644 index 00000000000..423d7bc3cfb --- /dev/null +++ b/hapi-fhir-server-mdm/src/main/java/ca/uhn/fhir/mdm/model/MdmLinkScoreMetrics.java @@ -0,0 +1,54 @@ +/*- + * #%L + * HAPI FHIR - Master Data Management + * %% + * Copyright (C) 2014 - 2023 Smile CDR, Inc. + * %% + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * #L% + */ +package ca.uhn.fhir.mdm.model; + +import java.util.LinkedHashMap; +import java.util.Map; + +public class MdmLinkScoreMetrics { + + private String myResourceType; + + /** + * Map of Score:Count + * Scores are typically Doubles. But we cast to string because + * Score is not a non-null field, and so "NULL" is a value. + */ + private Map myScoreCounts; + + public void setResourceType(String theResourceType) { + myResourceType = theResourceType; + } + + public String getResourceType() { + return myResourceType; + } + + public Map getScoreCounts() { + if (myScoreCounts == null) { + myScoreCounts = new LinkedHashMap<>(); + } + return myScoreCounts; + } + + public void addScore(String theScore, Long theCount) { + getScoreCounts().put(theScore, theCount); + } +} diff --git a/hapi-fhir-server-mdm/src/main/java/ca/uhn/fhir/mdm/model/MdmMetrics.java b/hapi-fhir-server-mdm/src/main/java/ca/uhn/fhir/mdm/model/MdmMetrics.java new file mode 100644 index 00000000000..80316efd82b --- /dev/null +++ b/hapi-fhir-server-mdm/src/main/java/ca/uhn/fhir/mdm/model/MdmMetrics.java @@ -0,0 +1,151 @@ +/*- + * #%L + * HAPI FHIR - Master Data Management + * %% + * Copyright (C) 2014 - 2023 Smile CDR, Inc. + * %% + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * #L% + */ +package ca.uhn.fhir.mdm.model; + +import ca.uhn.fhir.mdm.api.MdmLinkSourceEnum; +import ca.uhn.fhir.mdm.api.MdmMatchResultEnum; +import ca.uhn.fhir.model.api.IModelJson; +import com.fasterxml.jackson.annotation.JsonProperty; + +import java.util.HashMap; +import java.util.LinkedHashMap; +import java.util.Map; + +public class MdmMetrics extends MdmResourceMetrics implements IModelJson { + + @JsonProperty("resourceType") + private String myResourceType; + + /** + * A mapping of MatchType -> LinkSource -> count. + * Eg: + * MATCH + * AUTO - 2 + * MANUAL - 1 + * NO_MATCH + * AUTO - 1 + * MANUAL - 3 + */ + @JsonProperty("matchResult2linkSource2count") + private Map> myMatchTypeToLinkToCountMap; + + /** + * Score buckets (in brackets of 0.01 size, and null) to counts. + */ + @JsonProperty("scoreCounts") + private Map myScoreCounts; + + /** + * The number of golden resources. + */ + @JsonProperty("goldenResources") + private long myGoldenResourcesCount; + + /** + * The number of source resources. + */ + @JsonProperty("sourceResources") + private long mySourceResourcesCount; + + /** + * The number of excluded resources. + * These are necessarily a subset of both + * GoldenResources and SourceResources + * (as each Blocked resource will still generate + * a GoldenResource) + */ + @JsonProperty("excludedResources") + private long myExcludedResources; + + public String getResourceType() { + return myResourceType; + } + + public Map> getMatchTypeToLinkToCountMap() { + if (myMatchTypeToLinkToCountMap == null) { + myMatchTypeToLinkToCountMap = new HashMap<>(); + } + return myMatchTypeToLinkToCountMap; + } + + public void addMetric( + MdmMatchResultEnum theMdmMatchResultEnum, MdmLinkSourceEnum theLinkSourceEnum, long theCount) { + Map> map = getMatchTypeToLinkToCountMap(); + + if (!map.containsKey(theMdmMatchResultEnum)) { + map.put(theMdmMatchResultEnum, new HashMap<>()); + } + Map lsToCountMap = map.get(theMdmMatchResultEnum); + lsToCountMap.put(theLinkSourceEnum, theCount); + } + + public void setResourceType(String theResourceType) { + myResourceType = theResourceType; + } + + public long getGoldenResourcesCount() { + return myGoldenResourcesCount; + } + + public void setGoldenResourcesCount(long theGoldenResourcesCount) { + myGoldenResourcesCount = theGoldenResourcesCount; + } + + public long getSourceResourcesCount() { + return mySourceResourcesCount; + } + + public void setSourceResourcesCount(long theSourceResourcesCount) { + mySourceResourcesCount = theSourceResourcesCount; + } + + public long getExcludedResources() { + return myExcludedResources; + } + + public void setExcludedResources(long theExcludedResources) { + myExcludedResources = theExcludedResources; + } + + public Map getScoreCounts() { + if (myScoreCounts == null) { + myScoreCounts = new LinkedHashMap<>(); + } + return myScoreCounts; + } + + public void addScore(String theScore, Long theCount) { + getScoreCounts().put(theScore, theCount); + } + + public static MdmMetrics fromSeperableMetrics( + MdmResourceMetrics theMdmResourceMetrics, + MdmLinkMetrics theLinkMetrics, + MdmLinkScoreMetrics theLinkScoreMetrics) { + MdmMetrics metrics = new MdmMetrics(); + metrics.setResourceType(theMdmResourceMetrics.getResourceType()); + metrics.setExcludedResources(theMdmResourceMetrics.getExcludedResources()); + metrics.setGoldenResourcesCount(theMdmResourceMetrics.getGoldenResourcesCount()); + metrics.setSourceResourcesCount(theMdmResourceMetrics.getSourceResourcesCount()); + metrics.myMatchTypeToLinkToCountMap = theLinkMetrics.getMatchTypeToLinkToCountMap(); + metrics.myScoreCounts = theLinkScoreMetrics.getScoreCounts(); + return metrics; + } +} diff --git a/hapi-fhir-server-mdm/src/main/java/ca/uhn/fhir/mdm/model/MdmResourceMetrics.java b/hapi-fhir-server-mdm/src/main/java/ca/uhn/fhir/mdm/model/MdmResourceMetrics.java new file mode 100644 index 00000000000..6d35c28b010 --- /dev/null +++ b/hapi-fhir-server-mdm/src/main/java/ca/uhn/fhir/mdm/model/MdmResourceMetrics.java @@ -0,0 +1,79 @@ +/*- + * #%L + * HAPI FHIR - Master Data Management + * %% + * Copyright (C) 2014 - 2023 Smile CDR, Inc. + * %% + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * #L% + */ +package ca.uhn.fhir.mdm.model; + +public class MdmResourceMetrics { + + /** + * The resource type to which these metrics apply. + */ + private String myResourceType; + + /** + * The number of golden resources. + */ + private long myGoldenResourcesCount; + + /** + * The number of source resources. + */ + private long mySourceResourcesCount; + + /** + * The number of excluded resources. + * These are necessarily a subset of both + * GoldenResources and SourceResources + * (as each Blocked resource will still generate + * a GoldenResource) + */ + private long myExcludedResources; + + public String getResourceType() { + return myResourceType; + } + + public void setResourceType(String theResourceType) { + myResourceType = theResourceType; + } + + public long getGoldenResourcesCount() { + return myGoldenResourcesCount; + } + + public void setGoldenResourcesCount(long theGoldenResourcesCount) { + myGoldenResourcesCount = theGoldenResourcesCount; + } + + public long getSourceResourcesCount() { + return mySourceResourcesCount; + } + + public void setSourceResourcesCount(long theSourceResourcesCount) { + mySourceResourcesCount = theSourceResourcesCount; + } + + public long getExcludedResources() { + return myExcludedResources; + } + + public void setExcludedResources(long theExcludedResources) { + myExcludedResources = theExcludedResources; + } +} diff --git a/hapi-fhir-server-mdm/src/main/java/ca/uhn/fhir/mdm/model/MdmTransactionContext.java b/hapi-fhir-server-mdm/src/main/java/ca/uhn/fhir/mdm/model/MdmTransactionContext.java index a2ca1b94930..17613f59be8 100644 --- a/hapi-fhir-server-mdm/src/main/java/ca/uhn/fhir/mdm/model/MdmTransactionContext.java +++ b/hapi-fhir-server-mdm/src/main/java/ca/uhn/fhir/mdm/model/MdmTransactionContext.java @@ -49,6 +49,12 @@ public class MdmTransactionContext { private String myResourceType; + /** + * Whether or not the currently processed resource is a 'blocked resource'. + * This will only be set on matching. + */ + private boolean myIsBlockedResource; + private List myMdmLinkEvents = new ArrayList<>(); public TransactionLogMessages getTransactionLogMessages() { @@ -111,4 +117,12 @@ public class MdmTransactionContext { public void setMdmLinks(List theMdmLinkEvents) { myMdmLinkEvents = theMdmLinkEvents; } + + public void setIsBlocked(boolean theIsBlocked) { + myIsBlockedResource = theIsBlocked; + } + + public boolean getIsBlocked() { + return myIsBlockedResource; + } } diff --git a/hapi-fhir-server-mdm/src/main/java/ca/uhn/fhir/mdm/provider/BaseMdmProvider.java b/hapi-fhir-server-mdm/src/main/java/ca/uhn/fhir/mdm/provider/BaseMdmProvider.java index 710d1da5bc2..c4abecfe614 100644 --- a/hapi-fhir-server-mdm/src/main/java/ca/uhn/fhir/mdm/provider/BaseMdmProvider.java +++ b/hapi-fhir-server-mdm/src/main/java/ca/uhn/fhir/mdm/provider/BaseMdmProvider.java @@ -34,6 +34,8 @@ import ca.uhn.fhir.rest.server.exceptions.InvalidRequestException; import ca.uhn.fhir.rest.server.provider.ProviderConstants; import ca.uhn.fhir.rest.server.servlet.ServletRequestDetails; import ca.uhn.fhir.util.ParametersUtil; +import jakarta.annotation.Nonnull; +import jakarta.annotation.Nullable; import org.hl7.fhir.instance.model.api.IBase; import org.hl7.fhir.instance.model.api.IBaseParameters; import org.hl7.fhir.instance.model.api.IPrimitiveType; @@ -45,8 +47,6 @@ import java.util.Collections; import java.util.List; import java.util.Objects; import java.util.stream.Collectors; -import javax.annotation.Nonnull; -import javax.annotation.Nullable; public abstract class BaseMdmProvider { diff --git a/hapi-fhir-server-mdm/src/main/java/ca/uhn/fhir/mdm/provider/MdmControllerHelper.java b/hapi-fhir-server-mdm/src/main/java/ca/uhn/fhir/mdm/provider/MdmControllerHelper.java index 03c67a66f50..0c815a79508 100644 --- a/hapi-fhir-server-mdm/src/main/java/ca/uhn/fhir/mdm/provider/MdmControllerHelper.java +++ b/hapi-fhir-server-mdm/src/main/java/ca/uhn/fhir/mdm/provider/MdmControllerHelper.java @@ -37,6 +37,7 @@ import ca.uhn.fhir.rest.server.exceptions.ResourceVersionConflictException; import ca.uhn.fhir.rest.server.provider.ProviderConstants; import ca.uhn.fhir.util.BundleBuilder; import ca.uhn.fhir.validation.IResourceLoader; +import jakarta.annotation.Nonnull; import org.hl7.fhir.instance.model.api.IAnyResource; import org.hl7.fhir.instance.model.api.IBase; import org.hl7.fhir.instance.model.api.IBaseBackboneElement; @@ -53,7 +54,6 @@ import java.util.Comparator; import java.util.Date; import java.util.List; import java.util.UUID; -import javax.annotation.Nonnull; @Service public class MdmControllerHelper { diff --git a/hapi-fhir-server-mdm/src/main/java/ca/uhn/fhir/mdm/provider/MdmProviderDstu3Plus.java b/hapi-fhir-server-mdm/src/main/java/ca/uhn/fhir/mdm/provider/MdmProviderDstu3Plus.java index f62427e46d2..e58136c72d4 100644 --- a/hapi-fhir-server-mdm/src/main/java/ca/uhn/fhir/mdm/provider/MdmProviderDstu3Plus.java +++ b/hapi-fhir-server-mdm/src/main/java/ca/uhn/fhir/mdm/provider/MdmProviderDstu3Plus.java @@ -46,6 +46,7 @@ import ca.uhn.fhir.rest.server.exceptions.InvalidRequestException; import ca.uhn.fhir.rest.server.provider.ProviderConstants; import ca.uhn.fhir.rest.server.servlet.ServletRequestDetails; import ca.uhn.fhir.util.ParametersUtil; +import jakarta.annotation.Nonnull; import org.apache.commons.lang3.StringUtils; import org.hl7.fhir.instance.model.api.IAnyResource; import org.hl7.fhir.instance.model.api.IBaseBundle; @@ -61,7 +62,6 @@ import java.util.ArrayList; import java.util.Collections; import java.util.List; import java.util.stream.Collectors; -import javax.annotation.Nonnull; import static ca.uhn.fhir.rest.api.Constants.PARAM_OFFSET; import static org.apache.commons.lang3.ObjectUtils.isNotEmpty; diff --git a/hapi-fhir-server-mdm/src/main/java/ca/uhn/fhir/mdm/provider/MdmProviderLoader.java b/hapi-fhir-server-mdm/src/main/java/ca/uhn/fhir/mdm/provider/MdmProviderLoader.java index 2bcdf08b4ee..67c643d07fe 100644 --- a/hapi-fhir-server-mdm/src/main/java/ca/uhn/fhir/mdm/provider/MdmProviderLoader.java +++ b/hapi-fhir-server-mdm/src/main/java/ca/uhn/fhir/mdm/provider/MdmProviderLoader.java @@ -28,11 +28,10 @@ import ca.uhn.fhir.mdm.api.IMdmControllerSvc; import ca.uhn.fhir.mdm.api.IMdmSettings; import ca.uhn.fhir.mdm.api.IMdmSubmitSvc; import ca.uhn.fhir.rest.server.provider.ResourceProviderFactory; +import jakarta.annotation.PreDestroy; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; -import javax.annotation.PreDestroy; - @Service public class MdmProviderLoader { @Autowired @@ -66,6 +65,7 @@ public class MdmProviderLoader { switch (myFhirContext.getVersion().getVersion()) { case DSTU3: case R4: + case R5: myResourceProviderFactory.addSupplier(() -> new MdmProviderDstu3Plus( myFhirContext, myMdmControllerSvc, diff --git a/hapi-fhir-server-mdm/src/main/java/ca/uhn/fhir/mdm/rules/config/MdmRuleValidator.java b/hapi-fhir-server-mdm/src/main/java/ca/uhn/fhir/mdm/rules/config/MdmRuleValidator.java index 3189d149783..83340958397 100644 --- a/hapi-fhir-server-mdm/src/main/java/ca/uhn/fhir/mdm/rules/config/MdmRuleValidator.java +++ b/hapi-fhir-server-mdm/src/main/java/ca/uhn/fhir/mdm/rules/config/MdmRuleValidator.java @@ -136,7 +136,10 @@ public class MdmRuleValidator implements IMdmRuleValidator { private void validateSearchParams(MdmRulesJson theMdmRulesJson) { ourLog.info("Validating search parameters {}", theMdmRulesJson.getCandidateSearchParams()); - + if (theMdmRulesJson.getCandidateSearchParams().isEmpty()) { + ourLog.warn("No candidate search parameter was found. Defining candidate search parameter is strongly " + + "recommended for better performance of MDM"); + } for (MdmResourceSearchParamJson searchParams : theMdmRulesJson.getCandidateSearchParams()) { searchParams .iterator() diff --git a/hapi-fhir-server-mdm/src/main/java/ca/uhn/fhir/mdm/rules/json/MdmFieldMatchJson.java b/hapi-fhir-server-mdm/src/main/java/ca/uhn/fhir/mdm/rules/json/MdmFieldMatchJson.java index d809d0f122b..9a70b6b947e 100644 --- a/hapi-fhir-server-mdm/src/main/java/ca/uhn/fhir/mdm/rules/json/MdmFieldMatchJson.java +++ b/hapi-fhir-server-mdm/src/main/java/ca/uhn/fhir/mdm/rules/json/MdmFieldMatchJson.java @@ -22,8 +22,7 @@ package ca.uhn.fhir.mdm.rules.json; import ca.uhn.fhir.mdm.rules.matcher.models.MatchTypeEnum; import ca.uhn.fhir.model.api.IModelJson; import com.fasterxml.jackson.annotation.JsonProperty; - -import javax.annotation.Nonnull; +import jakarta.annotation.Nonnull; /** * Contains all business data for determining if a match exists on a particular field, given: diff --git a/hapi-fhir-server-mdm/src/main/java/ca/uhn/fhir/mdm/rules/json/MdmSimilarityJson.java b/hapi-fhir-server-mdm/src/main/java/ca/uhn/fhir/mdm/rules/json/MdmSimilarityJson.java index fc122d2148a..3d21b9e86d9 100644 --- a/hapi-fhir-server-mdm/src/main/java/ca/uhn/fhir/mdm/rules/json/MdmSimilarityJson.java +++ b/hapi-fhir-server-mdm/src/main/java/ca/uhn/fhir/mdm/rules/json/MdmSimilarityJson.java @@ -24,10 +24,9 @@ import ca.uhn.fhir.mdm.api.MdmMatchEvaluation; import ca.uhn.fhir.mdm.rules.similarity.MdmSimilarityEnum; import ca.uhn.fhir.model.api.IModelJson; import com.fasterxml.jackson.annotation.JsonProperty; +import jakarta.annotation.Nullable; import org.hl7.fhir.instance.model.api.IBase; -import javax.annotation.Nullable; - public class MdmSimilarityJson implements IModelJson { @JsonProperty(value = "algorithm", required = true) MdmSimilarityEnum myAlgorithm; diff --git a/hapi-fhir-server-mdm/src/main/java/ca/uhn/fhir/mdm/rules/json/VectorMatchResultMap.java b/hapi-fhir-server-mdm/src/main/java/ca/uhn/fhir/mdm/rules/json/VectorMatchResultMap.java index 93bc658c8d7..654b00b06cd 100644 --- a/hapi-fhir-server-mdm/src/main/java/ca/uhn/fhir/mdm/rules/json/VectorMatchResultMap.java +++ b/hapi-fhir-server-mdm/src/main/java/ca/uhn/fhir/mdm/rules/json/VectorMatchResultMap.java @@ -22,12 +22,12 @@ package ca.uhn.fhir.mdm.rules.json; import ca.uhn.fhir.context.ConfigurationException; import ca.uhn.fhir.i18n.Msg; import ca.uhn.fhir.mdm.api.MdmMatchResultEnum; +import jakarta.annotation.Nonnull; import java.util.HashMap; import java.util.HashSet; import java.util.Map; import java.util.Set; -import javax.annotation.Nonnull; public class VectorMatchResultMap { private final MdmRulesJson myMdmRulesJson; diff --git a/hapi-fhir-server-mdm/src/main/java/ca/uhn/fhir/mdm/rules/matcher/fieldmatchers/DateTimeWrapper.java b/hapi-fhir-server-mdm/src/main/java/ca/uhn/fhir/mdm/rules/matcher/fieldmatchers/DateTimeWrapper.java index 966a0e2ebd2..27e9b0603f9 100644 --- a/hapi-fhir-server-mdm/src/main/java/ca/uhn/fhir/mdm/rules/matcher/fieldmatchers/DateTimeWrapper.java +++ b/hapi-fhir-server-mdm/src/main/java/ca/uhn/fhir/mdm/rules/matcher/fieldmatchers/DateTimeWrapper.java @@ -49,6 +49,10 @@ public class DateTimeWrapper { org.hl7.fhir.r4.model.BaseDateTimeType r4Date = (org.hl7.fhir.r4.model.BaseDateTimeType) theDate; myPrecision = r4Date.getPrecision(); myValueAsString = r4Date.getValueAsString(); + } else if (theDate instanceof org.hl7.fhir.r5.model.BaseDateTimeType) { + org.hl7.fhir.r5.model.BaseDateTimeType r5Date = (org.hl7.fhir.r5.model.BaseDateTimeType) theDate; + myPrecision = r5Date.getPrecision(); + myValueAsString = r5Date.getValueAsString(); } else { // we should consider changing this error so we don't need the fhir context at all throw new UnsupportedOperationException(Msg.code(1520) + "Version not supported: " diff --git a/hapi-fhir-server-mdm/src/main/java/ca/uhn/fhir/mdm/rules/similarity/MdmSimilarityEnum.java b/hapi-fhir-server-mdm/src/main/java/ca/uhn/fhir/mdm/rules/similarity/MdmSimilarityEnum.java index a1460c2ff68..7ef286c916a 100644 --- a/hapi-fhir-server-mdm/src/main/java/ca/uhn/fhir/mdm/rules/similarity/MdmSimilarityEnum.java +++ b/hapi-fhir-server-mdm/src/main/java/ca/uhn/fhir/mdm/rules/similarity/MdmSimilarityEnum.java @@ -26,10 +26,9 @@ import info.debatty.java.stringsimilarity.Jaccard; import info.debatty.java.stringsimilarity.JaroWinkler; import info.debatty.java.stringsimilarity.NormalizedLevenshtein; import info.debatty.java.stringsimilarity.SorensenDice; +import jakarta.annotation.Nullable; import org.hl7.fhir.instance.model.api.IBase; -import javax.annotation.Nullable; - public enum MdmSimilarityEnum { JARO_WINKLER(new HapiStringSimilarity(new JaroWinkler())), COSINE(new HapiStringSimilarity(new Cosine())), diff --git a/hapi-fhir-server-mdm/src/main/java/ca/uhn/fhir/mdm/svc/MdmLinkExpandSvc.java b/hapi-fhir-server-mdm/src/main/java/ca/uhn/fhir/mdm/svc/MdmLinkExpandSvc.java index 0b2c4294669..1d5428da059 100644 --- a/hapi-fhir-server-mdm/src/main/java/ca/uhn/fhir/mdm/svc/MdmLinkExpandSvc.java +++ b/hapi-fhir-server-mdm/src/main/java/ca/uhn/fhir/mdm/svc/MdmLinkExpandSvc.java @@ -28,6 +28,7 @@ import ca.uhn.fhir.mdm.log.Logs; import ca.uhn.fhir.mdm.model.MdmPidTuple; import ca.uhn.fhir.model.primitive.IdDt; import ca.uhn.fhir.rest.api.server.storage.IResourcePersistentId; +import jakarta.annotation.Nonnull; import org.hl7.fhir.instance.model.api.IBaseResource; import org.hl7.fhir.instance.model.api.IIdType; import org.slf4j.Logger; @@ -38,7 +39,6 @@ import org.springframework.transaction.annotation.Transactional; import java.util.HashSet; import java.util.List; import java.util.Set; -import javax.annotation.Nonnull; @Service @Transactional diff --git a/hapi-fhir-server-mdm/src/main/java/ca/uhn/fhir/mdm/svc/MdmSearchParamSvc.java b/hapi-fhir-server-mdm/src/main/java/ca/uhn/fhir/mdm/svc/MdmSearchParamSvc.java index 39bbdd3ac98..ca85dd77d22 100644 --- a/hapi-fhir-server-mdm/src/main/java/ca/uhn/fhir/mdm/svc/MdmSearchParamSvc.java +++ b/hapi-fhir-server-mdm/src/main/java/ca/uhn/fhir/mdm/svc/MdmSearchParamSvc.java @@ -31,13 +31,13 @@ import ca.uhn.fhir.jpa.searchparam.SearchParameterMap; import ca.uhn.fhir.jpa.searchparam.extractor.SearchParamExtractorService; import ca.uhn.fhir.rest.server.util.ISearchParamRegistry; import ca.uhn.fhir.util.SearchParameterUtil; +import jakarta.annotation.Nullable; import org.apache.commons.lang3.StringUtils; import org.hl7.fhir.instance.model.api.IBaseResource; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; import java.util.List; -import javax.annotation.Nullable; @Service public class MdmSearchParamSvc { diff --git a/hapi-fhir-server-mdm/src/main/java/ca/uhn/fhir/mdm/svc/MdmSubmitSvcImpl.java b/hapi-fhir-server-mdm/src/main/java/ca/uhn/fhir/mdm/svc/MdmSubmitSvcImpl.java index 9c6150c5d24..1b81732196a 100644 --- a/hapi-fhir-server-mdm/src/main/java/ca/uhn/fhir/mdm/svc/MdmSubmitSvcImpl.java +++ b/hapi-fhir-server-mdm/src/main/java/ca/uhn/fhir/mdm/svc/MdmSubmitSvcImpl.java @@ -37,6 +37,8 @@ import ca.uhn.fhir.rest.api.server.storage.IResourcePersistentId; import ca.uhn.fhir.rest.server.exceptions.InternalErrorException; import ca.uhn.fhir.rest.server.exceptions.InvalidRequestException; import ca.uhn.fhir.rest.server.provider.ProviderConstants; +import jakarta.annotation.Nonnull; +import jakarta.annotation.Nullable; import org.hl7.fhir.instance.model.api.IBaseResource; import org.hl7.fhir.instance.model.api.IIdType; import org.slf4j.Logger; @@ -49,8 +51,6 @@ import java.util.Collection; import java.util.Collections; import java.util.List; import java.util.UUID; -import javax.annotation.Nonnull; -import javax.annotation.Nullable; public class MdmSubmitSvcImpl implements IMdmSubmitSvc { diff --git a/hapi-fhir-server-mdm/src/main/java/ca/uhn/fhir/mdm/util/GoldenResourceHelper.java b/hapi-fhir-server-mdm/src/main/java/ca/uhn/fhir/mdm/util/GoldenResourceHelper.java index d133a7a9605..a08b04b251b 100644 --- a/hapi-fhir-server-mdm/src/main/java/ca/uhn/fhir/mdm/util/GoldenResourceHelper.java +++ b/hapi-fhir-server-mdm/src/main/java/ca/uhn/fhir/mdm/util/GoldenResourceHelper.java @@ -33,6 +33,7 @@ import ca.uhn.fhir.mdm.model.CanonicalEID; import ca.uhn.fhir.mdm.model.MdmTransactionContext; import ca.uhn.fhir.rest.api.Constants; import ca.uhn.fhir.util.FhirTerser; +import jakarta.annotation.Nonnull; import org.hl7.fhir.instance.model.api.IAnyResource; import org.hl7.fhir.instance.model.api.IBase; import org.hl7.fhir.instance.model.api.IBaseResource; @@ -46,10 +47,10 @@ import java.util.List; import java.util.Objects; import java.util.Optional; import java.util.stream.Collectors; -import javax.annotation.Nonnull; import static ca.uhn.fhir.context.FhirVersionEnum.DSTU3; import static ca.uhn.fhir.context.FhirVersionEnum.R4; +import static ca.uhn.fhir.context.FhirVersionEnum.R5; @Service public class GoldenResourceHelper { @@ -116,6 +117,14 @@ public class GoldenResourceHelper { MdmResourceUtil.setMdmManaged(newGoldenResource); MdmResourceUtil.setGoldenResource(newGoldenResource); + // TODO - on updating links, if resolving a link, this should go away? + // blocked resource's golden resource will be marked special + // they are not part of MDM matching algorithm (will not link to other resources) + // but other resources can link to them + if (theMdmTransactionContext.getIsBlocked()) { + MdmResourceUtil.setGoldenResourceAsBlockedResourceGoldenResource(newGoldenResource); + } + // add the partition id to the new resource newGoldenResource.setUserData( Constants.RESOURCE_PARTITION_ID, @@ -192,7 +201,7 @@ public class GoldenResourceHelper { private void validateContextSupported() { FhirVersionEnum fhirVersion = myFhirContext.getVersion().getVersion(); - if (fhirVersion == R4 || fhirVersion == DSTU3) { + if (fhirVersion == R4 || fhirVersion == DSTU3 || fhirVersion == R5) { return; } throw new UnsupportedOperationException(Msg.code(1489) + "Version not supported: " diff --git a/hapi-fhir-server-mdm/src/main/java/ca/uhn/fhir/mdm/util/IdentifierUtil.java b/hapi-fhir-server-mdm/src/main/java/ca/uhn/fhir/mdm/util/IdentifierUtil.java index 403ddd2c4ff..7a0f728647d 100644 --- a/hapi-fhir-server-mdm/src/main/java/ca/uhn/fhir/mdm/util/IdentifierUtil.java +++ b/hapi-fhir-server-mdm/src/main/java/ca/uhn/fhir/mdm/util/IdentifierUtil.java @@ -56,10 +56,12 @@ public final class IdentifierUtil { * @param theFhirContext FHIR context to use for determining the identifier version * @param eid EID to get equivalent FHIR Identifier from * @param Generic Identifier base interface - * @return Returns appropriate R4 or DSTU3 Identifier instance + * @return Returns appropriate R5, R4 or DSTU3 Identifier instance */ public static T toId(FhirContext theFhirContext, CanonicalEID eid) { switch (theFhirContext.getVersion().getVersion()) { + case R5: + return (T) eid.toR5(); case R4: return (T) eid.toR4(); case DSTU3: diff --git a/hapi-fhir-server-mdm/src/main/java/ca/uhn/fhir/mdm/util/MdmResourceUtil.java b/hapi-fhir-server-mdm/src/main/java/ca/uhn/fhir/mdm/util/MdmResourceUtil.java index 44f34ea094f..3ede05c5741 100644 --- a/hapi-fhir-server-mdm/src/main/java/ca/uhn/fhir/mdm/util/MdmResourceUtil.java +++ b/hapi-fhir-server-mdm/src/main/java/ca/uhn/fhir/mdm/util/MdmResourceUtil.java @@ -20,11 +20,11 @@ package ca.uhn.fhir.mdm.util; import ca.uhn.fhir.mdm.api.MdmConstants; +import jakarta.annotation.Nonnull; import org.hl7.fhir.instance.model.api.IBaseCoding; import org.hl7.fhir.instance.model.api.IBaseResource; import java.util.Optional; -import javax.annotation.Nonnull; public final class MdmResourceUtil { @@ -132,6 +132,24 @@ public final class MdmResourceUtil { MdmConstants.DISPLAY_GOLDEN_REDIRECT); } + /** + * Adds the BLOCKED tag to the golden resource. + * Because this is called *before* a resource is saved, + * we must add a new system/code combo to it + * @param theBaseResource + * @return + */ + public static IBaseResource setGoldenResourceAsBlockedResourceGoldenResource(IBaseResource theBaseResource) { + IBaseCoding tag = theBaseResource.getMeta().addTag(); + tag.setSystem(MdmConstants.SYSTEM_GOLDEN_RECORD_STATUS); + tag.setCode(MdmConstants.CODE_BLOCKED); + tag.setDisplay(MdmConstants.CODE_BLOCKED_DISPLAY); + tag.setUserSelected(false); + tag.setVersion("1"); + + return theBaseResource; + } + /** * WARNING: This code may _look_ like it replaces in place a code of a tag, but this DOES NOT ACTUALLY WORK!. In reality what will * happen is a secondary tag will be created with the same system. the only way to actually remove a tag from a resource diff --git a/hapi-fhir-server-mdm/src/main/java/ca/uhn/fhir/mdm/util/MdmSearchParamBuildingUtils.java b/hapi-fhir-server-mdm/src/main/java/ca/uhn/fhir/mdm/util/MdmSearchParamBuildingUtils.java new file mode 100644 index 00000000000..a33bb91e077 --- /dev/null +++ b/hapi-fhir-server-mdm/src/main/java/ca/uhn/fhir/mdm/util/MdmSearchParamBuildingUtils.java @@ -0,0 +1,70 @@ +/*- + * #%L + * HAPI FHIR - Master Data Management + * %% + * Copyright (C) 2014 - 2023 Smile CDR, Inc. + * %% + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * #L% + */ +package ca.uhn.fhir.mdm.util; + +import ca.uhn.fhir.jpa.searchparam.SearchParameterMap; +import ca.uhn.fhir.mdm.api.MdmConstants; +import ca.uhn.fhir.mdm.rules.json.MdmRulesJson; +import ca.uhn.fhir.rest.param.TokenAndListParam; +import ca.uhn.fhir.rest.param.TokenParam; + +public class MdmSearchParamBuildingUtils { + + private static final String IDENTIFIER = "identifier"; + + private static final String TAG = "_tag"; + + /** + * Builds a search parameter map that can be used to find the + * golden resources associated with MDM blocked resources (ie, those + * resources that were omitted from MDM matching). + */ + public static SearchParameterMap buildSearchParameterForBlockedResourceCount(String theResourceType) { + SearchParameterMap map = new SearchParameterMap(); + map.setLoadSynchronous(true); + TokenAndListParam tagsToSearch = new TokenAndListParam(); + tagsToSearch.addAnd(new TokenParam(MdmConstants.SYSTEM_GOLDEN_RECORD_STATUS, MdmConstants.CODE_GOLDEN_RECORD)); + tagsToSearch.addAnd(new TokenParam(MdmConstants.SYSTEM_GOLDEN_RECORD_STATUS, MdmConstants.CODE_BLOCKED)); + + map.add(TAG, tagsToSearch); + return map; + } + + /** + * Creates a SearchParameterMap used for searching for golden resources + * by EID specifically. + */ + public static SearchParameterMap buildEidSearchParameterMap( + String theEid, String theResourceType, MdmRulesJson theMdmRules) { + SearchParameterMap map = buildBasicGoldenResourceSearchParameterMap(theEid); + map.add(IDENTIFIER, new TokenParam(theMdmRules.getEnterpriseEIDSystemForResourceType(theResourceType), theEid)); + return map; + } + + /** + * Creates a SearchParameterMap that can be used to find golden resources. + */ + public static SearchParameterMap buildBasicGoldenResourceSearchParameterMap(String theResourceType) { + SearchParameterMap map = new SearchParameterMap(); + map.setLoadSynchronous(true); + map.add(TAG, new TokenParam(MdmConstants.SYSTEM_GOLDEN_RECORD_STATUS, MdmConstants.CODE_GOLDEN_RECORD)); + return map; + } +} diff --git a/hapi-fhir-server-mdm/src/test/java/ca/uhn/fhir/mdm/rules/matcher/StringMatcherR4Test.java b/hapi-fhir-server-mdm/src/test/java/ca/uhn/fhir/mdm/rules/matcher/StringMatcherR4Test.java index 646641f8966..d596bb2375b 100644 --- a/hapi-fhir-server-mdm/src/test/java/ca/uhn/fhir/mdm/rules/matcher/StringMatcherR4Test.java +++ b/hapi-fhir-server-mdm/src/test/java/ca/uhn/fhir/mdm/rules/matcher/StringMatcherR4Test.java @@ -14,7 +14,7 @@ import org.junit.jupiter.api.Test; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import javax.annotation.Nonnull; +import jakarta.annotation.Nonnull; import static org.junit.jupiter.api.Assertions.assertFalse; import static org.junit.jupiter.api.Assertions.assertTrue; diff --git a/hapi-fhir-server-mdm/src/test/java/ca/uhn/fhir/mdm/util/MdmResourceUtilTest.java b/hapi-fhir-server-mdm/src/test/java/ca/uhn/fhir/mdm/util/MdmResourceUtilTest.java index 8b128896f1d..85671682cc0 100644 --- a/hapi-fhir-server-mdm/src/test/java/ca/uhn/fhir/mdm/util/MdmResourceUtilTest.java +++ b/hapi-fhir-server-mdm/src/test/java/ca/uhn/fhir/mdm/util/MdmResourceUtilTest.java @@ -1,11 +1,21 @@ package ca.uhn.fhir.mdm.util; +import ca.uhn.fhir.mdm.api.MdmConstants; +import org.hl7.fhir.r4.model.Coding; import org.hl7.fhir.r4.model.Organization; +import org.hl7.fhir.r4.model.Patient; import org.junit.jupiter.api.Test; +import java.util.HashSet; +import java.util.List; +import java.util.Set; + import static org.hamcrest.CoreMatchers.equalTo; import static org.hamcrest.CoreMatchers.is; import static org.hamcrest.MatcherAssert.assertThat; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNotNull; +import static org.junit.jupiter.api.Assertions.assertTrue; class MdmResourceUtilTest { @@ -20,4 +30,28 @@ class MdmResourceUtilTest { assertThat(hasGoldenRecordTag, is(equalTo(false))); } + + @Test + public void testSetGoldenAndBlockedResource() { + // setup + Patient patient = new Patient(); + patient.setActive(true); + + // test + Patient changed = (Patient) MdmResourceUtil.setGoldenResourceAsBlockedResourceGoldenResource( + MdmResourceUtil.setGoldenResource(patient) + ); + + // verify + assertNotNull(changed); + List tags = changed.getMeta().getTag(); + Set codes = new HashSet<>(); + codes.add(MdmConstants.CODE_BLOCKED); + codes.add(MdmConstants.CODE_GOLDEN_RECORD); + assertEquals(2, tags.size()); + for (Coding code : tags) { + assertEquals(MdmConstants.SYSTEM_GOLDEN_RECORD_STATUS, code.getSystem()); + assertTrue(codes.contains(code.getCode())); + } + } } diff --git a/hapi-fhir-server-openapi/pom.xml b/hapi-fhir-server-openapi/pom.xml index 49c0ef80517..dfe33aeeb93 100644 --- a/hapi-fhir-server-openapi/pom.xml +++ b/hapi-fhir-server-openapi/pom.xml @@ -5,7 +5,7 @@ ca.uhn.hapi.fhir hapi-deployable-pom - 6.9.7-SNAPSHOT + 6.11.7-SNAPSHOT ../hapi-deployable-pom/pom.xml @@ -35,12 +35,17 @@ io.swagger.core.v3 - swagger-models - 2.1.7 + swagger-models-jakarta io.swagger.core.v3 - swagger-core + swagger-core-jakarta + + + io.swagger.core.v3 + swagger-models + + org.webjars @@ -59,8 +64,8 @@ - javax.servlet - javax.servlet-api + jakarta.servlet + jakarta.servlet-api provided @@ -83,7 +88,7 @@ test - net.sourceforge.htmlunit + org.htmlunit htmlunit test diff --git a/hapi-fhir-server-openapi/src/main/java/ca/uhn/fhir/rest/openapi/OpenApiInterceptor.java b/hapi-fhir-server-openapi/src/main/java/ca/uhn/fhir/rest/openapi/OpenApiInterceptor.java index 783e082a795..00185920370 100644 --- a/hapi-fhir-server-openapi/src/main/java/ca/uhn/fhir/rest/openapi/OpenApiInterceptor.java +++ b/hapi-fhir-server-openapi/src/main/java/ca/uhn/fhir/rest/openapi/OpenApiInterceptor.java @@ -56,6 +56,10 @@ import io.swagger.v3.oas.models.responses.ApiResponse; import io.swagger.v3.oas.models.responses.ApiResponses; import io.swagger.v3.oas.models.servers.Server; import io.swagger.v3.oas.models.tags.Tag; +import jakarta.annotation.Nonnull; +import jakarta.servlet.ServletContext; +import jakarta.servlet.http.HttpServletRequest; +import jakarta.servlet.http.HttpServletResponse; import org.apache.commons.io.IOUtils; import org.apache.commons.lang3.StringUtils; import org.hl7.fhir.convertors.factory.VersionConvertorFactory_30_40; @@ -64,6 +68,7 @@ import org.hl7.fhir.convertors.factory.VersionConvertorFactory_43_50; import org.hl7.fhir.instance.model.api.IBaseConformance; import org.hl7.fhir.instance.model.api.IBaseResource; import org.hl7.fhir.instance.model.api.IPrimitiveType; +import org.hl7.fhir.r4.model.CanonicalType; import org.hl7.fhir.r4.model.CapabilityStatement; import org.hl7.fhir.r4.model.CodeableConcept; import org.hl7.fhir.r4.model.Coding; @@ -92,7 +97,7 @@ import org.thymeleaf.templateresolver.ITemplateResolver; import org.thymeleaf.templateresolver.TemplateResolution; import org.thymeleaf.templateresource.ClassLoaderTemplateResource; import org.thymeleaf.web.servlet.IServletWebExchange; -import org.thymeleaf.web.servlet.JavaxServletWebApplication; +import org.thymeleaf.web.servlet.JakartaServletWebApplication; import java.io.IOException; import java.io.InputStream; @@ -108,10 +113,8 @@ import java.util.Properties; import java.util.Set; import java.util.function.Supplier; import java.util.stream.Collectors; -import javax.servlet.ServletContext; -import javax.servlet.http.HttpServletRequest; -import javax.servlet.http.HttpServletResponse; +import static ca.uhn.fhir.rest.server.util.NarrativeUtil.sanitizeHtmlFragment; import static org.apache.commons.lang3.ObjectUtils.defaultIfNull; import static org.apache.commons.lang3.StringUtils.defaultString; import static org.apache.commons.lang3.StringUtils.isBlank; @@ -337,7 +340,7 @@ public class OpenApiInterceptor { HttpServletRequest servletRequest = theRequestDetails.getServletRequest(); ServletContext servletContext = servletRequest.getServletContext(); - JavaxServletWebApplication application = JavaxServletWebApplication.buildApplication(servletContext); + JakartaServletWebApplication application = JakartaServletWebApplication.buildApplication(servletContext); IServletWebExchange exchange = application.buildExchange(servletRequest, theResponse); WebContext context = new WebContext(exchange); context.setVariable(REQUEST_DETAILS, theRequestDetails); @@ -356,7 +359,7 @@ public class OpenApiInterceptor { String copyright = cs.getCopyright(); if (isNotBlank(copyright)) { - copyright = myFlexmarkRenderer.render(myFlexmarkParser.parse(copyright)); + copyright = renderMarkdown(copyright); context.setVariable("COPYRIGHT_HTML", copyright); } @@ -411,6 +414,11 @@ public class OpenApiInterceptor { theResponse.getWriter().close(); } + @Nonnull + private String renderMarkdown(String copyright) { + return myFlexmarkRenderer.render(myFlexmarkParser.parse(copyright)); + } + protected void populateOIDCVariables(ServletRequestDetails theRequestDetails, WebContext theContext) { theContext.setVariable("OAUTH2_REDIRECT_URL_PROPERTY", ""); } @@ -515,7 +523,7 @@ public class OpenApiInterceptor { Tag resourceTag = new Tag(); resourceTag.setName(resourceType); - resourceTag.setDescription("The " + resourceType + " FHIR resource type"); + resourceTag.setDescription(createResourceDescription(nextResource)); openApi.addTagsItem(resourceTag); // Instance Read @@ -624,6 +632,36 @@ public class OpenApiInterceptor { return openApi; } + @Nonnull + protected String createResourceDescription( + CapabilityStatement.CapabilityStatementRestResourceComponent theResource) { + StringBuilder b = new StringBuilder(); + b.append("The ").append(theResource.getType()).append(" FHIR resource type"); + + String documentation = theResource.getDocumentation(); + if (isNotBlank(documentation)) { + b.append("
    "); + b.append(sanitizeHtmlFragment(renderMarkdown(documentation))); + } + + if (isNotBlank(theResource.getProfile())) { + b.append("
    "); + b.append("Base profile: "); + b.append(sanitizeHtmlFragment(theResource.getProfile())); + } + + for (CanonicalType next : theResource.getSupportedProfile()) { + String nextSupportedProfile = next.getValueAsString(); + if (isNotBlank(nextSupportedProfile)) { + b.append("
    "); + b.append("Supported profile: "); + b.append(sanitizeHtmlFragment(nextSupportedProfile)); + } + } + + return b.toString(); + } + protected void addSearchOperation( final OpenAPI openApi, final Operation operation, diff --git a/hapi-fhir-server-openapi/src/test/java/ca/uhn/fhir/rest/openapi/OpenApiInterceptorTest.java b/hapi-fhir-server-openapi/src/test/java/ca/uhn/fhir/rest/openapi/OpenApiInterceptorTest.java index 2e0b0ac5067..3ea0ddea35a 100644 --- a/hapi-fhir-server-openapi/src/test/java/ca/uhn/fhir/rest/openapi/OpenApiInterceptorTest.java +++ b/hapi-fhir-server-openapi/src/test/java/ca/uhn/fhir/rest/openapi/OpenApiInterceptorTest.java @@ -2,9 +2,16 @@ package ca.uhn.fhir.rest.openapi; import ca.uhn.fhir.context.FhirContext; import ca.uhn.fhir.interceptor.api.Hook; +import ca.uhn.fhir.interceptor.api.Interceptor; import ca.uhn.fhir.interceptor.api.Pointcut; import ca.uhn.fhir.model.api.annotation.Description; -import ca.uhn.fhir.rest.annotation.*; +import ca.uhn.fhir.rest.annotation.ConditionalUrlParam; +import ca.uhn.fhir.rest.annotation.IdParam; +import ca.uhn.fhir.rest.annotation.Operation; +import ca.uhn.fhir.rest.annotation.OperationParam; +import ca.uhn.fhir.rest.annotation.Patch; +import ca.uhn.fhir.rest.annotation.ResourceParam; +import ca.uhn.fhir.rest.annotation.Validate; import ca.uhn.fhir.rest.api.Constants; import ca.uhn.fhir.rest.api.EncodingEnum; import ca.uhn.fhir.rest.api.MethodOutcome; @@ -19,35 +26,47 @@ import ca.uhn.fhir.test.utilities.HtmlUtil; import ca.uhn.fhir.test.utilities.HttpClientExtension; import ca.uhn.fhir.test.utilities.server.RestfulServerExtension; import ca.uhn.fhir.util.ExtensionConstants; -import com.gargoylesoftware.htmlunit.html.DomElement; -import com.gargoylesoftware.htmlunit.html.HtmlDivision; -import com.gargoylesoftware.htmlunit.html.HtmlPage; +import org.htmlunit.html.DomElement; +import org.htmlunit.html.HtmlDivision; +import org.htmlunit.html.HtmlPage; import io.swagger.v3.core.util.Yaml; import io.swagger.v3.oas.models.OpenAPI; import io.swagger.v3.oas.models.PathItem; +import jakarta.servlet.http.HttpServletRequest; import org.apache.commons.io.IOUtils; import org.apache.commons.lang3.StringUtils; import org.apache.http.client.methods.CloseableHttpResponse; import org.apache.http.client.methods.HttpGet; import org.hamcrest.Matchers; -import org.hl7.fhir.instance.model.api.*; +import org.hl7.fhir.instance.model.api.IBaseBundle; +import org.hl7.fhir.instance.model.api.IBaseCoding; +import org.hl7.fhir.instance.model.api.IBaseConformance; +import org.hl7.fhir.instance.model.api.IBaseParameters; +import org.hl7.fhir.instance.model.api.IBaseReference; +import org.hl7.fhir.instance.model.api.IBaseResource; +import org.hl7.fhir.instance.model.api.IIdType; +import org.hl7.fhir.instance.model.api.IPrimitiveType; import org.hl7.fhir.r5.model.ActorDefinition; +import org.hl7.fhir.r5.model.CapabilityStatement; import org.junit.jupiter.api.*; import org.junit.jupiter.api.extension.RegisterExtension; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import javax.servlet.http.HttpServletRequest; +import jakarta.annotation.Nonnull; import java.io.IOException; import java.net.URL; import java.nio.charset.StandardCharsets; import java.util.*; +import java.util.function.Consumer; import static org.hamcrest.CoreMatchers.not; import static org.hamcrest.MatcherAssert.assertThat; import static org.hamcrest.Matchers.containsString; import static org.hamcrest.Matchers.empty; -import static org.junit.jupiter.api.Assertions.*; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNotNull; +import static org.junit.jupiter.api.Assertions.assertNull; public class OpenApiInterceptorTest { @@ -83,6 +102,28 @@ public class OpenApiInterceptorTest { assertThat(buttonTexts.toString(), buttonTexts, Matchers.contains("All", "System Level Operations", "OperationDefinition 1", "Observation", "Patient")); } + + @Test + public void testResourceDocsCopied() throws IOException { + myServer.getRestfulServer().registerInterceptor(new AddResourceCountsInterceptor("OperationDefinition")); + myServer.getRestfulServer().registerInterceptor(new OpenApiInterceptor()); + myServer.registerInterceptor(new CapabilityStatementEnhancingInterceptor(cs->{ + org.hl7.fhir.r4.model.CapabilityStatement.CapabilityStatementRestResourceComponent patientResource = findPatientResource(cs); + patientResource.setProfile("http://baseProfile"); + patientResource.addSupportedProfile("http://foo"); + patientResource.addSupportedProfile("http://bar"); + patientResource.setDocumentation("This is **bolded** documentation"); + })); + + org.hl7.fhir.r4.model.CapabilityStatement cs = myServer.getFhirClient().capabilities().ofType(org.hl7.fhir.r4.model.CapabilityStatement.class).execute(); + org.hl7.fhir.r4.model.CapabilityStatement.CapabilityStatementRestResourceComponent patientResource = findPatientResource(cs); + assertEquals("This is **bolded** documentation", patientResource.getDocumentation()); + + String url = "http://localhost:" + myServer.getPort() + "/fhir/swagger-ui/"; + String resp = fetchSwaggerUi(url); + } + + } @Nested @@ -111,6 +152,35 @@ public class OpenApiInterceptorTest { } } + @Interceptor + private static class CapabilityStatementEnhancingInterceptor { + + private final Consumer myConsumer; + + public CapabilityStatementEnhancingInterceptor(Consumer theConsumer) { + myConsumer = theConsumer; + } + + @Hook(Pointcut.SERVER_CAPABILITY_STATEMENT_GENERATED) + public void massageCapabilityStatement(IBaseConformance theCs) { + myConsumer.accept((org.hl7.fhir.r4.model.CapabilityStatement) theCs); + } + + } + + @Nonnull + private static org.hl7.fhir.r4.model.CapabilityStatement.CapabilityStatementRestResourceComponent findPatientResource(org.hl7.fhir.r4.model.CapabilityStatement theCs) { + org.hl7.fhir.r4.model.CapabilityStatement.CapabilityStatementRestResourceComponent patientResource = theCs + .getRest() + .get(0) + .getResource() + .stream() + .filter(t -> "Patient".equals(t.getType())) + .findFirst() + .orElseThrow(); + return patientResource; + } + @SuppressWarnings("JUnitMalformedDeclaration") abstract static class BaseOpenApiInterceptorTest { diff --git a/hapi-fhir-server/pom.xml b/hapi-fhir-server/pom.xml index 8bf19a63b51..3a521cd7eae 100644 --- a/hapi-fhir-server/pom.xml +++ b/hapi-fhir-server/pom.xml @@ -5,7 +5,7 @@ ca.uhn.hapi.fhir hapi-deployable-pom - 6.9.7-SNAPSHOT + 6.11.7-SNAPSHOT ../hapi-deployable-pom/pom.xml @@ -35,8 +35,8 @@ - javax.servlet - javax.servlet-api + jakarta.servlet + jakarta.servlet-api provided diff --git a/hapi-fhir-server/src/main/java/ca/uhn/fhir/rest/api/server/IBundleProvider.java b/hapi-fhir-server/src/main/java/ca/uhn/fhir/rest/api/server/IBundleProvider.java index 5e1b96d63c7..9cd0b3c8793 100644 --- a/hapi-fhir-server/src/main/java/ca/uhn/fhir/rest/api/server/IBundleProvider.java +++ b/hapi-fhir-server/src/main/java/ca/uhn/fhir/rest/api/server/IBundleProvider.java @@ -22,6 +22,8 @@ package ca.uhn.fhir.rest.api.server; import ca.uhn.fhir.context.ConfigurationException; import ca.uhn.fhir.i18n.Msg; import ca.uhn.fhir.rest.server.method.ResponsePage; +import jakarta.annotation.Nonnull; +import jakarta.annotation.Nullable; import org.apache.commons.lang3.Validate; import org.hl7.fhir.instance.model.api.IBaseResource; import org.hl7.fhir.instance.model.api.IPrimitiveType; @@ -30,8 +32,6 @@ import java.util.ArrayList; import java.util.Date; import java.util.List; import java.util.stream.Collectors; -import javax.annotation.Nonnull; -import javax.annotation.Nullable; public interface IBundleProvider { @@ -119,6 +119,7 @@ public interface IBundleProvider { * server's processing rules (e.g. _include'd resources, OperationOutcome, etc.). For example, * if the method is invoked with index 0,10 the method might return 10 search results, plus an * additional 20 resources which matched a client's _include specification. + *

    *

    * Note that if this bundle provider was loaded using a * page ID (i.e. via {@link ca.uhn.fhir.rest.server.IPagingProvider#retrieveResultList(RequestDetails, String, String)} diff --git a/hapi-fhir-server/src/main/java/ca/uhn/fhir/rest/api/server/IRestfulResponse.java b/hapi-fhir-server/src/main/java/ca/uhn/fhir/rest/api/server/IRestfulResponse.java index 936fc86801d..3c60366adad 100644 --- a/hapi-fhir-server/src/main/java/ca/uhn/fhir/rest/api/server/IRestfulResponse.java +++ b/hapi-fhir-server/src/main/java/ca/uhn/fhir/rest/api/server/IRestfulResponse.java @@ -19,18 +19,19 @@ */ package ca.uhn.fhir.rest.api.server; +import jakarta.annotation.Nonnull; +import jakarta.annotation.Nullable; + import java.io.Closeable; import java.io.IOException; import java.io.OutputStream; import java.io.Writer; import java.util.List; import java.util.Map; -import javax.annotation.Nonnull; -import javax.annotation.Nullable; /** * Implementations of this interface represent a response back to the client from the server. It is - * conceptually similar to {@link javax.servlet.http.HttpServletResponse} but intended to be agnostic + * conceptually similar to {@link jakarta.servlet.http.HttpServletResponse} but intended to be agnostic * of the server framework being used. *

    * This class is a bit of an awkward abstraction given the two styles of servers it supports. diff --git a/hapi-fhir-server/src/main/java/ca/uhn/fhir/rest/api/server/RequestDetails.java b/hapi-fhir-server/src/main/java/ca/uhn/fhir/rest/api/server/RequestDetails.java index a8a0ae4af20..0a9d904c1bb 100644 --- a/hapi-fhir-server/src/main/java/ca/uhn/fhir/rest/api/server/RequestDetails.java +++ b/hapi-fhir-server/src/main/java/ca/uhn/fhir/rest/api/server/RequestDetails.java @@ -28,6 +28,8 @@ import ca.uhn.fhir.rest.server.IRestfulServerDefaults; import ca.uhn.fhir.rest.server.interceptor.IServerInterceptor; import ca.uhn.fhir.util.StopWatch; import ca.uhn.fhir.util.UrlUtil; +import jakarta.servlet.http.HttpServletRequest; +import jakarta.servlet.http.HttpServletResponse; import org.apache.commons.lang3.Validate; import org.hl7.fhir.instance.model.api.IBaseResource; import org.hl7.fhir.instance.model.api.IIdType; @@ -43,8 +45,6 @@ import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.stream.Collectors; -import javax.servlet.http.HttpServletRequest; -import javax.servlet.http.HttpServletResponse; import static org.apache.commons.lang3.StringUtils.isBlank; @@ -325,7 +325,7 @@ public abstract class RequestDetails { * @throws UnsupportedEncodingException if the character set encoding used is not supported and the text cannot be decoded * @throws IllegalStateException if {@link #getInputStream} method has been called on this request * @throws IOException if an input or output exception occurred - * @see javax.servlet.http.HttpServletRequest#getInputStream + * @see jakarta.servlet.http.HttpServletRequest#getInputStream */ public abstract Reader getReader() throws IOException; @@ -397,7 +397,10 @@ public abstract class RequestDetails { /** * Returns the server base URL (with no trailing '/') for a given request + * + * @deprecated Use {@link #getFhirServerBase()} instead. Deprecated in HAPI FHIR 7.0.0 */ + @Deprecated public abstract String getServerBaseForRequest(); /** diff --git a/hapi-fhir-server/src/main/java/ca/uhn/fhir/rest/api/server/SystemRequestDetails.java b/hapi-fhir-server/src/main/java/ca/uhn/fhir/rest/api/server/SystemRequestDetails.java index 0dd4f0667ba..b5e25ade7ee 100644 --- a/hapi-fhir-server/src/main/java/ca/uhn/fhir/rest/api/server/SystemRequestDetails.java +++ b/hapi-fhir-server/src/main/java/ca/uhn/fhir/rest/api/server/SystemRequestDetails.java @@ -31,6 +31,7 @@ import ca.uhn.fhir.rest.server.ETagSupportEnum; import ca.uhn.fhir.rest.server.ElementsSupportEnum; import ca.uhn.fhir.rest.server.IPagingProvider; import ca.uhn.fhir.rest.server.IRestfulServerDefaults; +import ca.uhn.fhir.rest.server.RestfulServer; import ca.uhn.fhir.rest.server.interceptor.IServerInterceptor; import com.google.common.collect.ArrayListMultimap; import com.google.common.collect.ImmutableListMultimap; @@ -72,6 +73,7 @@ public class SystemRequestDetails extends RequestDetails { super(theDetails); if (nonNull(theDetails.getServer())) { myServer = theDetails.getServer(); + myFhirContext = theDetails.getFhirContext(); } } @@ -152,6 +154,10 @@ public class SystemRequestDetails extends RequestDetails { return myServer; } + public void setServer(RestfulServer theServer) { + this.myServer = theServer; + } + @Override public String getServerBaseForRequest() { return null; diff --git a/hapi-fhir-server/src/main/java/ca/uhn/fhir/rest/api/server/SystemRestfulResponse.java b/hapi-fhir-server/src/main/java/ca/uhn/fhir/rest/api/server/SystemRestfulResponse.java new file mode 100644 index 00000000000..caa41d07de0 --- /dev/null +++ b/hapi-fhir-server/src/main/java/ca/uhn/fhir/rest/api/server/SystemRestfulResponse.java @@ -0,0 +1,74 @@ +/*- + * #%L + * HAPI FHIR - Server Framework + * %% + * Copyright (C) 2014 - 2023 Smile CDR, Inc. + * %% + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * #L% + */ +package ca.uhn.fhir.rest.api.server; + +import ca.uhn.fhir.rest.server.BaseRestfulResponse; +import ca.uhn.fhir.util.IoUtil; +import jakarta.annotation.Nonnull; +import jakarta.annotation.Nullable; +import org.apache.commons.lang3.Validate; + +import java.io.ByteArrayOutputStream; +import java.io.Closeable; +import java.io.IOException; +import java.io.OutputStream; +import java.io.StringWriter; +import java.io.Writer; + +/** + * A default RestfulResponse that returns the body as an IBaseResource and ignores everything else. + */ +public class SystemRestfulResponse extends BaseRestfulResponse { + private Writer myWriter; + private ByteArrayOutputStream myOutputStream; + + public SystemRestfulResponse(SystemRequestDetails theSystemRequestDetails) { + super(theSystemRequestDetails); + } + + @Nonnull + @Override + public Writer getResponseWriter(int theStatusCode, String theContentType, String theCharset, boolean theRespondGzip) + throws IOException { + Validate.isTrue(myWriter == null, "getResponseWriter() called multiple times"); + Validate.isTrue(myOutputStream == null, "getResponseWriter() called after getResponseOutputStream()"); + + myWriter = new StringWriter(); + return myWriter; + } + + @Nonnull + @Override + public OutputStream getResponseOutputStream( + int theStatusCode, String theContentType, @Nullable Integer theContentLength) throws IOException { + Validate.isTrue(myWriter == null, "getResponseOutputStream() called multiple times"); + Validate.isTrue(myOutputStream == null, "getResponseOutputStream() called after getResponseWriter()"); + + myOutputStream = new ByteArrayOutputStream(); + return myOutputStream; + } + + @Override + public Object commitResponse(@Nonnull Closeable theWriterOrOutputStream) throws IOException { + IoUtil.closeQuietly(theWriterOrOutputStream); + + return getRequestDetails().getServer().getFhirContext().newJsonParser().parseResource(myWriter.toString()); + } +} diff --git a/hapi-fhir-server/src/main/java/ca/uhn/fhir/rest/api/server/storage/TransactionDetails.java b/hapi-fhir-server/src/main/java/ca/uhn/fhir/rest/api/server/storage/TransactionDetails.java index 4762a79e99c..8c44beb626d 100644 --- a/hapi-fhir-server/src/main/java/ca/uhn/fhir/rest/api/server/storage/TransactionDetails.java +++ b/hapi-fhir-server/src/main/java/ca/uhn/fhir/rest/api/server/storage/TransactionDetails.java @@ -27,14 +27,23 @@ import ca.uhn.fhir.rest.api.InterceptorInvocationTimingEnum; import ca.uhn.fhir.rest.server.exceptions.PreconditionFailedException; import com.google.common.collect.ArrayListMultimap; import com.google.common.collect.ListMultimap; +import jakarta.annotation.Nonnull; +import jakarta.annotation.Nullable; import org.apache.commons.lang3.Validate; import org.hl7.fhir.instance.model.api.IBaseResource; import org.hl7.fhir.instance.model.api.IIdType; +import java.util.ArrayList; +import java.util.Collection; +import java.util.Collections; +import java.util.Date; +import java.util.EnumSet; +import java.util.HashMap; +import java.util.HashSet; +import java.util.List; +import java.util.Map; +import java.util.Set; import java.util.function.Supplier; -import java.util.*; -import javax.annotation.Nonnull; -import javax.annotation.Nullable; /** * This object contains runtime information that is gathered and relevant to a single database transaction. diff --git a/hapi-fhir-server/src/main/java/ca/uhn/fhir/rest/server/ApacheProxyAddressStrategy.java b/hapi-fhir-server/src/main/java/ca/uhn/fhir/rest/server/ApacheProxyAddressStrategy.java index 561bc7dfb08..5f1952bdd3d 100644 --- a/hapi-fhir-server/src/main/java/ca/uhn/fhir/rest/server/ApacheProxyAddressStrategy.java +++ b/hapi-fhir-server/src/main/java/ca/uhn/fhir/rest/server/ApacheProxyAddressStrategy.java @@ -19,6 +19,8 @@ */ package ca.uhn.fhir.rest.server; +import jakarta.servlet.ServletContext; +import jakarta.servlet.http.HttpServletRequest; import org.apache.commons.lang3.StringUtils; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -28,8 +30,6 @@ import org.springframework.web.util.UriComponents; import org.springframework.web.util.UriComponentsBuilder; import java.util.Optional; -import javax.servlet.ServletContext; -import javax.servlet.http.HttpServletRequest; import static java.util.Optional.ofNullable; diff --git a/hapi-fhir-server/src/main/java/ca/uhn/fhir/rest/server/BundleProviderWithNamedPages.java b/hapi-fhir-server/src/main/java/ca/uhn/fhir/rest/server/BundleProviderWithNamedPages.java index 7f7adc1ab62..7663d7a54b8 100644 --- a/hapi-fhir-server/src/main/java/ca/uhn/fhir/rest/server/BundleProviderWithNamedPages.java +++ b/hapi-fhir-server/src/main/java/ca/uhn/fhir/rest/server/BundleProviderWithNamedPages.java @@ -20,11 +20,11 @@ package ca.uhn.fhir.rest.server; import ca.uhn.fhir.rest.server.method.ResponsePage; +import jakarta.annotation.Nonnull; import org.apache.commons.lang3.Validate; import org.hl7.fhir.instance.model.api.IBaseResource; import java.util.List; -import javax.annotation.Nonnull; /** * Bundle provider that uses named pages instead of counts diff --git a/hapi-fhir-server/src/main/java/ca/uhn/fhir/rest/server/BundleProviders.java b/hapi-fhir-server/src/main/java/ca/uhn/fhir/rest/server/BundleProviders.java index f8933f50640..95c47a4dd0e 100644 --- a/hapi-fhir-server/src/main/java/ca/uhn/fhir/rest/server/BundleProviders.java +++ b/hapi-fhir-server/src/main/java/ca/uhn/fhir/rest/server/BundleProviders.java @@ -23,11 +23,11 @@ import ca.uhn.fhir.model.primitive.InstantDt; import ca.uhn.fhir.rest.api.server.IBundleProvider; import ca.uhn.fhir.rest.server.method.ResponsePage; import ca.uhn.fhir.util.CoverageIgnore; +import jakarta.annotation.Nonnull; import org.hl7.fhir.instance.model.api.IBaseResource; import java.util.Collections; import java.util.List; -import javax.annotation.Nonnull; /** * Utility methods for working with {@link IBundleProvider} diff --git a/hapi-fhir-server/src/main/java/ca/uhn/fhir/rest/server/HardcodedServerAddressStrategy.java b/hapi-fhir-server/src/main/java/ca/uhn/fhir/rest/server/HardcodedServerAddressStrategy.java index 5782290bdd2..6d192d30a1e 100644 --- a/hapi-fhir-server/src/main/java/ca/uhn/fhir/rest/server/HardcodedServerAddressStrategy.java +++ b/hapi-fhir-server/src/main/java/ca/uhn/fhir/rest/server/HardcodedServerAddressStrategy.java @@ -19,11 +19,10 @@ */ package ca.uhn.fhir.rest.server; +import jakarta.servlet.ServletContext; +import jakarta.servlet.http.HttpServletRequest; import org.apache.commons.lang3.Validate; -import javax.servlet.ServletContext; -import javax.servlet.http.HttpServletRequest; - /** * Server address strategy which simply returns a hardcoded URL */ diff --git a/hapi-fhir-server/src/main/java/ca/uhn/fhir/rest/server/IPagingProvider.java b/hapi-fhir-server/src/main/java/ca/uhn/fhir/rest/server/IPagingProvider.java index 58300d2be11..310f09a0534 100644 --- a/hapi-fhir-server/src/main/java/ca/uhn/fhir/rest/server/IPagingProvider.java +++ b/hapi-fhir-server/src/main/java/ca/uhn/fhir/rest/server/IPagingProvider.java @@ -21,9 +21,8 @@ package ca.uhn.fhir.rest.server; import ca.uhn.fhir.rest.api.server.IBundleProvider; import ca.uhn.fhir.rest.api.server.RequestDetails; - -import javax.annotation.Nonnull; -import javax.annotation.Nullable; +import jakarta.annotation.Nonnull; +import jakarta.annotation.Nullable; public interface IPagingProvider { diff --git a/hapi-fhir-server/src/main/java/ca/uhn/fhir/rest/server/IServerAddressStrategy.java b/hapi-fhir-server/src/main/java/ca/uhn/fhir/rest/server/IServerAddressStrategy.java index 96d682e1a0e..7fbdd99491c 100644 --- a/hapi-fhir-server/src/main/java/ca/uhn/fhir/rest/server/IServerAddressStrategy.java +++ b/hapi-fhir-server/src/main/java/ca/uhn/fhir/rest/server/IServerAddressStrategy.java @@ -19,8 +19,8 @@ */ package ca.uhn.fhir.rest.server; -import javax.servlet.ServletContext; -import javax.servlet.http.HttpServletRequest; +import jakarta.servlet.ServletContext; +import jakarta.servlet.http.HttpServletRequest; /** * Provides the server base for a given incoming request. This can be used to account for diff --git a/hapi-fhir-server/src/main/java/ca/uhn/fhir/rest/server/IServerConformanceProvider.java b/hapi-fhir-server/src/main/java/ca/uhn/fhir/rest/server/IServerConformanceProvider.java index f2b5eb31b9d..65ea9876d78 100644 --- a/hapi-fhir-server/src/main/java/ca/uhn/fhir/rest/server/IServerConformanceProvider.java +++ b/hapi-fhir-server/src/main/java/ca/uhn/fhir/rest/server/IServerConformanceProvider.java @@ -22,11 +22,10 @@ package ca.uhn.fhir.rest.server; import ca.uhn.fhir.rest.annotation.IdParam; import ca.uhn.fhir.rest.annotation.Read; import ca.uhn.fhir.rest.api.server.RequestDetails; +import jakarta.servlet.http.HttpServletRequest; import org.hl7.fhir.instance.model.api.IBaseResource; import org.hl7.fhir.instance.model.api.IIdType; -import javax.servlet.http.HttpServletRequest; - public interface IServerConformanceProvider { /** diff --git a/hapi-fhir-server/src/main/java/ca/uhn/fhir/rest/server/IncomingRequestAddressStrategy.java b/hapi-fhir-server/src/main/java/ca/uhn/fhir/rest/server/IncomingRequestAddressStrategy.java index 4653bdbca74..5220bdef551 100644 --- a/hapi-fhir-server/src/main/java/ca/uhn/fhir/rest/server/IncomingRequestAddressStrategy.java +++ b/hapi-fhir-server/src/main/java/ca/uhn/fhir/rest/server/IncomingRequestAddressStrategy.java @@ -19,11 +19,10 @@ */ package ca.uhn.fhir.rest.server; +import jakarta.servlet.ServletContext; +import jakarta.servlet.http.HttpServletRequest; import org.apache.commons.lang3.StringUtils; -import javax.servlet.ServletContext; -import javax.servlet.http.HttpServletRequest; - /** * Determines the server's base using the incoming request */ diff --git a/hapi-fhir-server/src/main/java/ca/uhn/fhir/rest/server/ResourceBinding.java b/hapi-fhir-server/src/main/java/ca/uhn/fhir/rest/server/ResourceBinding.java index febb8a729c9..f30a25e93c8 100644 --- a/hapi-fhir-server/src/main/java/ca/uhn/fhir/rest/server/ResourceBinding.java +++ b/hapi-fhir-server/src/main/java/ca/uhn/fhir/rest/server/ResourceBinding.java @@ -33,8 +33,8 @@ public class ResourceBinding { private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(ResourceBinding.class); - private String resourceName; - private LinkedList myMethodBindings = new LinkedList<>(); + private String myResourceName; + private final LinkedList myMethodBindings = new LinkedList<>(); /** * Constructor @@ -44,8 +44,8 @@ public class ResourceBinding { } public BaseMethodBinding getMethod(RequestDetails theRequest) { - if (null == myMethodBindings) { - ourLog.warn("No methods exist for resource: {}", resourceName); + if (myMethodBindings.isEmpty()) { + ourLog.warn("No methods exist for resource: {}", myResourceName); return null; } @@ -75,11 +75,11 @@ public class ResourceBinding { } public String getResourceName() { - return resourceName; + return myResourceName; } public void setResourceName(String resourceName) { - this.resourceName = resourceName; + this.myResourceName = resourceName; } public List getMethodBindings() { @@ -87,13 +87,20 @@ public class ResourceBinding { } public void addMethod(BaseMethodBinding method) { + if (myMethodBindings.stream() + .anyMatch( + t -> t.getMethod().toString().equals(method.getMethod().toString()))) { + ourLog.warn( + "The following method has been registered twice against this RestfulServer: {}", + method.getMethod()); + } this.myMethodBindings.push(method); } @Override public boolean equals(Object o) { if (!(o instanceof ResourceBinding)) return false; - return resourceName.equals(((ResourceBinding) o).getResourceName()); + return myResourceName.equals(((ResourceBinding) o).getResourceName()); } @Override diff --git a/hapi-fhir-server/src/main/java/ca/uhn/fhir/rest/server/RestfulServer.java b/hapi-fhir-server/src/main/java/ca/uhn/fhir/rest/server/RestfulServer.java index 02dbd0d5ccd..9a7f4768216 100644 --- a/hapi-fhir-server/src/main/java/ca/uhn/fhir/rest/server/RestfulServer.java +++ b/hapi-fhir-server/src/main/java/ca/uhn/fhir/rest/server/RestfulServer.java @@ -65,6 +65,13 @@ import ca.uhn.fhir.util.UrlPathTokenizer; import ca.uhn.fhir.util.UrlUtil; import ca.uhn.fhir.util.VersionUtil; import com.google.common.collect.Lists; +import jakarta.annotation.Nonnull; +import jakarta.annotation.Nullable; +import jakarta.servlet.ServletException; +import jakarta.servlet.UnavailableException; +import jakarta.servlet.http.HttpServlet; +import jakarta.servlet.http.HttpServletRequest; +import jakarta.servlet.http.HttpServletResponse; import org.apache.commons.lang3.RandomStringUtils; import org.apache.commons.lang3.StringUtils; import org.apache.commons.lang3.Validate; @@ -97,13 +104,6 @@ import java.util.Set; import java.util.concurrent.locks.Lock; import java.util.concurrent.locks.ReentrantLock; import java.util.jar.Manifest; -import javax.annotation.Nonnull; -import javax.annotation.Nullable; -import javax.servlet.ServletException; -import javax.servlet.UnavailableException; -import javax.servlet.http.HttpServlet; -import javax.servlet.http.HttpServletRequest; -import javax.servlet.http.HttpServletResponse; import static ca.uhn.fhir.util.StringUtil.toUtf8String; import static java.util.stream.Collectors.toList; @@ -367,6 +367,10 @@ public class RestfulServer extends HttpServlet implements IRestfulServer t.close()); myGlobalBinding.getMethodBindings().forEach(t -> t.close()); myServerBinding.getMethodBindings().forEach(t -> t.close()); + + myResourceNameToBinding.clear(); + myGlobalBinding.getMethodBindings().clear(); + myServerBinding.getMethodBindings().clear(); } /** @@ -965,6 +969,10 @@ public class RestfulServer extends HttpServlet implements IRestfulServer excludedParameterNames) { + String tenantId = StringUtils.defaultString(theRequest.getTenantId()); + String requestPath = StringUtils.defaultString(theRequest.getRequestPath()); + StringBuilder b = new StringBuilder(); b.append(theServerBase); + requestPath = StringUtils.substringAfter(requestPath, tenantId); - if (isNotBlank(theRequest.getRequestPath())) { - b.append('/'); - if (isNotBlank(theRequest.getTenantId()) - && theRequest.getRequestPath().startsWith(theRequest.getTenantId() + "/")) { - b.append(theRequest - .getRequestPath() - .substring(theRequest.getTenantId().length() + 1)); - } else { - b.append(theRequest.getRequestPath()); - } + if (isNotBlank(requestPath)) { + requestPath = StringUtils.prependIfMissing(requestPath, "/"); } + + b.append(requestPath); + // For POST the URL parameters get jumbled with the post body parameters so don't include them, they might be // huge if (theRequest.getRequestType() == RequestTypeEnum.GET) { @@ -211,7 +242,6 @@ public class RestfulServerUtils { } } } - return b.toString(); } diff --git a/hapi-fhir-server/src/main/java/ca/uhn/fhir/rest/server/ServletRequestTracing.java b/hapi-fhir-server/src/main/java/ca/uhn/fhir/rest/server/ServletRequestTracing.java index 6de5ac23b19..886a31bad1e 100644 --- a/hapi-fhir-server/src/main/java/ca/uhn/fhir/rest/server/ServletRequestTracing.java +++ b/hapi-fhir-server/src/main/java/ca/uhn/fhir/rest/server/ServletRequestTracing.java @@ -20,14 +20,13 @@ package ca.uhn.fhir.rest.server; import ca.uhn.fhir.rest.api.Constants; +import jakarta.annotation.Nullable; +import jakarta.servlet.ServletRequest; +import jakarta.servlet.http.HttpServletRequest; import org.apache.commons.lang3.RandomStringUtils; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import javax.annotation.Nullable; -import javax.servlet.ServletRequest; -import javax.servlet.http.HttpServletRequest; - import static org.apache.commons.lang3.StringUtils.isBlank; import static org.apache.commons.lang3.StringUtils.isNotBlank; diff --git a/hapi-fhir-server/src/main/java/ca/uhn/fhir/rest/server/SimpleBundleProvider.java b/hapi-fhir-server/src/main/java/ca/uhn/fhir/rest/server/SimpleBundleProvider.java index e9479ccd7ad..7916ec7922f 100644 --- a/hapi-fhir-server/src/main/java/ca/uhn/fhir/rest/server/SimpleBundleProvider.java +++ b/hapi-fhir-server/src/main/java/ca/uhn/fhir/rest/server/SimpleBundleProvider.java @@ -22,6 +22,7 @@ package ca.uhn.fhir.rest.server; import ca.uhn.fhir.model.primitive.InstantDt; import ca.uhn.fhir.rest.api.server.IBundleProvider; import ca.uhn.fhir.rest.server.method.ResponsePage; +import jakarta.annotation.Nonnull; import org.apache.commons.lang3.builder.ToStringBuilder; import org.hl7.fhir.instance.model.api.IBaseResource; import org.hl7.fhir.instance.model.api.IPrimitiveType; @@ -30,7 +31,6 @@ import java.util.Arrays; import java.util.Collections; import java.util.Date; import java.util.List; -import javax.annotation.Nonnull; public class SimpleBundleProvider implements IBundleProvider { @@ -43,6 +43,15 @@ public class SimpleBundleProvider implements IBundleProvider { private Integer myCurrentPageSize; private ResponsePage.ResponsePageBuilder myPageBuilder; + /** + * The actual number of resources we have tried to fetch. + * This value will only be populated if there is a + * _count query parameter provided. + * In which case, it will be the total number of resources + * we tried to fetch (should be _count + 1 for accurate paging) + */ + private int myTotalResourcesRequestedReturned = -1; + /** * Constructor */ @@ -144,6 +153,7 @@ public class SimpleBundleProvider implements IBundleProvider { @Override public List getResources( int theFromIndex, int theToIndex, @Nonnull ResponsePage.ResponsePageBuilder theResponsePageBuilder) { + theResponsePageBuilder.setTotalRequestedResourcesFetched(myTotalResourcesRequestedReturned); return (List) myList.subList(Math.min(theFromIndex, myList.size()), Math.min(theToIndex, myList.size())); } @@ -153,6 +163,10 @@ public class SimpleBundleProvider implements IBundleProvider { return myUuid; } + public void setTotalResourcesRequestedReturned(int theAmount) { + myTotalResourcesRequestedReturned = theAmount; + } + /** * Defaults to null */ diff --git a/hapi-fhir-server/src/main/java/ca/uhn/fhir/rest/server/interceptor/BanUnsupportedHttpMethodsInterceptor.java b/hapi-fhir-server/src/main/java/ca/uhn/fhir/rest/server/interceptor/BanUnsupportedHttpMethodsInterceptor.java index a0b07e904ef..0dd19421145 100644 --- a/hapi-fhir-server/src/main/java/ca/uhn/fhir/rest/server/interceptor/BanUnsupportedHttpMethodsInterceptor.java +++ b/hapi-fhir-server/src/main/java/ca/uhn/fhir/rest/server/interceptor/BanUnsupportedHttpMethodsInterceptor.java @@ -22,11 +22,11 @@ package ca.uhn.fhir.rest.server.interceptor; import ca.uhn.fhir.i18n.Msg; import ca.uhn.fhir.rest.api.RequestTypeEnum; import ca.uhn.fhir.rest.server.exceptions.MethodNotAllowedException; +import jakarta.servlet.http.HttpServletRequest; +import jakarta.servlet.http.HttpServletResponse; import java.util.HashSet; import java.util.Set; -import javax.servlet.http.HttpServletRequest; -import javax.servlet.http.HttpServletResponse; /** * This interceptor causes the server to reject invocations for HTTP methods diff --git a/hapi-fhir-server/src/main/java/ca/uhn/fhir/rest/server/interceptor/BaseResponseTerminologyInterceptor.java b/hapi-fhir-server/src/main/java/ca/uhn/fhir/rest/server/interceptor/BaseResponseTerminologyInterceptor.java index cca85d6242b..638f17b0690 100644 --- a/hapi-fhir-server/src/main/java/ca/uhn/fhir/rest/server/interceptor/BaseResponseTerminologyInterceptor.java +++ b/hapi-fhir-server/src/main/java/ca/uhn/fhir/rest/server/interceptor/BaseResponseTerminologyInterceptor.java @@ -23,13 +23,13 @@ import ca.uhn.fhir.context.FhirContext; import ca.uhn.fhir.context.support.IValidationSupport; import ca.uhn.fhir.rest.api.server.RequestDetails; import ca.uhn.fhir.util.BundleUtil; +import jakarta.annotation.Nonnull; import org.apache.commons.lang3.Validate; import org.hl7.fhir.instance.model.api.IBaseBundle; import org.hl7.fhir.instance.model.api.IBaseResource; import java.util.Collections; import java.util.List; -import javax.annotation.Nonnull; public abstract class BaseResponseTerminologyInterceptor { protected final IValidationSupport myValidationSupport; diff --git a/hapi-fhir-server/src/main/java/ca/uhn/fhir/rest/server/interceptor/CorsInterceptor.java b/hapi-fhir-server/src/main/java/ca/uhn/fhir/rest/server/interceptor/CorsInterceptor.java index 1d928ae0b5b..ee7a54a6c3e 100644 --- a/hapi-fhir-server/src/main/java/ca/uhn/fhir/rest/server/interceptor/CorsInterceptor.java +++ b/hapi-fhir-server/src/main/java/ca/uhn/fhir/rest/server/interceptor/CorsInterceptor.java @@ -22,6 +22,8 @@ package ca.uhn.fhir.rest.server.interceptor; import ca.uhn.fhir.i18n.Msg; import ca.uhn.fhir.rest.api.Constants; import ca.uhn.fhir.rest.server.exceptions.InternalErrorException; +import jakarta.servlet.http.HttpServletRequest; +import jakarta.servlet.http.HttpServletResponse; import org.apache.commons.lang3.Validate; import org.springframework.web.cors.CorsConfiguration; import org.springframework.web.cors.CorsProcessor; @@ -30,8 +32,6 @@ import org.springframework.web.cors.DefaultCorsProcessor; import java.io.IOException; import java.util.ArrayList; -import javax.servlet.http.HttpServletRequest; -import javax.servlet.http.HttpServletResponse; public class CorsInterceptor extends InterceptorAdapter { diff --git a/hapi-fhir-server/src/main/java/ca/uhn/fhir/rest/server/interceptor/ExceptionHandlingInterceptor.java b/hapi-fhir-server/src/main/java/ca/uhn/fhir/rest/server/interceptor/ExceptionHandlingInterceptor.java index 68a8122a99b..f995231c058 100644 --- a/hapi-fhir-server/src/main/java/ca/uhn/fhir/rest/server/interceptor/ExceptionHandlingInterceptor.java +++ b/hapi-fhir-server/src/main/java/ca/uhn/fhir/rest/server/interceptor/ExceptionHandlingInterceptor.java @@ -37,6 +37,9 @@ import ca.uhn.fhir.rest.server.exceptions.UnclassifiedServerFailureException; import ca.uhn.fhir.rest.server.method.BaseResourceReturningMethodBinding; import ca.uhn.fhir.rest.server.servlet.ServletRestfulResponse; import ca.uhn.fhir.util.OperationOutcomeUtil; +import jakarta.servlet.ServletException; +import jakarta.servlet.http.HttpServletRequest; +import jakarta.servlet.http.HttpServletResponse; import org.apache.commons.collections4.map.HashedMap; import org.apache.commons.lang3.exception.ExceptionUtils; import org.hl7.fhir.instance.model.api.IBaseOperationOutcome; @@ -48,9 +51,6 @@ import java.util.List; import java.util.Map; import java.util.Map.Entry; import java.util.Set; -import javax.servlet.ServletException; -import javax.servlet.http.HttpServletRequest; -import javax.servlet.http.HttpServletResponse; import static org.apache.commons.lang3.StringUtils.isNotBlank; import static org.apache.http.HttpHeaders.CONTENT_ENCODING; diff --git a/hapi-fhir-server/src/main/java/ca/uhn/fhir/rest/server/interceptor/IServerInterceptor.java b/hapi-fhir-server/src/main/java/ca/uhn/fhir/rest/server/interceptor/IServerInterceptor.java index c8e8b63c769..d5fb2727fa8 100644 --- a/hapi-fhir-server/src/main/java/ca/uhn/fhir/rest/server/interceptor/IServerInterceptor.java +++ b/hapi-fhir-server/src/main/java/ca/uhn/fhir/rest/server/interceptor/IServerInterceptor.java @@ -31,12 +31,12 @@ import ca.uhn.fhir.rest.api.server.ResponseDetails; import ca.uhn.fhir.rest.server.exceptions.AuthenticationException; import ca.uhn.fhir.rest.server.exceptions.BaseServerResponseException; import ca.uhn.fhir.rest.server.servlet.ServletRequestDetails; +import jakarta.servlet.ServletException; +import jakarta.servlet.http.HttpServletRequest; +import jakarta.servlet.http.HttpServletResponse; import org.hl7.fhir.instance.model.api.IBaseResource; import java.io.IOException; -import javax.servlet.ServletException; -import javax.servlet.http.HttpServletRequest; -import javax.servlet.http.HttpServletResponse; /** * Provides methods to intercept requests and responses. Note that implementations of this interface may wish to use @@ -64,12 +64,12 @@ public interface IServerInterceptor { * * @param theRequestDetails A bean containing details about the request that is about to be processed, including details such as the * resource type and logical ID (if any) and other FHIR-specific aspects of the request which have been - * pulled out of the {@link javax.servlet.http.HttpServletRequest servlet request}. Note that the bean + * pulled out of the {@link jakarta.servlet.http.HttpServletRequest servlet request}. Note that the bean * properties are not all guaranteed to be populated, depending on how early during processing the * exception occurred. * @param theServletRequest The incoming request * @param theServletResponse The response. Note that interceptors may choose to provide a response (i.e. by calling - * {@link javax.servlet.http.HttpServletResponse#getWriter()}) but in that case it is important to return + * {@link jakarta.servlet.http.HttpServletResponse#getWriter()}) but in that case it is important to return * false to indicate that the server itself should not also provide a response. * @return Return true if processing should continue normally. This is generally the right thing to do. * If your interceptor is providing a response rather than letting HAPI handle the response normally, you diff --git a/hapi-fhir-server/src/main/java/ca/uhn/fhir/rest/server/interceptor/InteractionBlockingInterceptor.java b/hapi-fhir-server/src/main/java/ca/uhn/fhir/rest/server/interceptor/InteractionBlockingInterceptor.java index a003ab59f14..b6c61a1b47b 100644 --- a/hapi-fhir-server/src/main/java/ca/uhn/fhir/rest/server/interceptor/InteractionBlockingInterceptor.java +++ b/hapi-fhir-server/src/main/java/ca/uhn/fhir/rest/server/interceptor/InteractionBlockingInterceptor.java @@ -26,6 +26,7 @@ import ca.uhn.fhir.interceptor.api.Pointcut; import ca.uhn.fhir.rest.api.RestOperationTypeEnum; import ca.uhn.fhir.rest.server.method.BaseMethodBinding; import ca.uhn.fhir.rest.server.method.OperationMethodBinding; +import jakarta.annotation.Nonnull; import org.apache.commons.lang3.Validate; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -34,7 +35,6 @@ import java.util.Collections; import java.util.HashSet; import java.util.Set; import java.util.TreeSet; -import javax.annotation.Nonnull; import static org.apache.commons.lang3.StringUtils.isBlank; diff --git a/hapi-fhir-server/src/main/java/ca/uhn/fhir/rest/server/interceptor/InterceptorAdapter.java b/hapi-fhir-server/src/main/java/ca/uhn/fhir/rest/server/interceptor/InterceptorAdapter.java index a736b12725f..b4c8b559809 100644 --- a/hapi-fhir-server/src/main/java/ca/uhn/fhir/rest/server/interceptor/InterceptorAdapter.java +++ b/hapi-fhir-server/src/main/java/ca/uhn/fhir/rest/server/interceptor/InterceptorAdapter.java @@ -26,12 +26,12 @@ import ca.uhn.fhir.rest.api.server.ResponseDetails; import ca.uhn.fhir.rest.server.exceptions.AuthenticationException; import ca.uhn.fhir.rest.server.exceptions.BaseServerResponseException; import ca.uhn.fhir.rest.server.servlet.ServletRequestDetails; +import jakarta.servlet.ServletException; +import jakarta.servlet.http.HttpServletRequest; +import jakarta.servlet.http.HttpServletResponse; import org.hl7.fhir.instance.model.api.IBaseResource; import java.io.IOException; -import javax.servlet.ServletException; -import javax.servlet.http.HttpServletRequest; -import javax.servlet.http.HttpServletResponse; /** * Base class for {@link IServerInterceptor} implementations. Provides a No-op implementation diff --git a/hapi-fhir-server/src/main/java/ca/uhn/fhir/rest/server/interceptor/LoggingInterceptor.java b/hapi-fhir-server/src/main/java/ca/uhn/fhir/rest/server/interceptor/LoggingInterceptor.java index 40c74261f2f..f58f02a37e9 100644 --- a/hapi-fhir-server/src/main/java/ca/uhn/fhir/rest/server/interceptor/LoggingInterceptor.java +++ b/hapi-fhir-server/src/main/java/ca/uhn/fhir/rest/server/interceptor/LoggingInterceptor.java @@ -31,6 +31,9 @@ import ca.uhn.fhir.rest.server.RestfulServerUtils.ResponseEncoding; import ca.uhn.fhir.rest.server.exceptions.BaseServerResponseException; import ca.uhn.fhir.rest.server.servlet.ServletRequestDetails; import ca.uhn.fhir.util.UrlUtil; +import jakarta.servlet.ServletException; +import jakarta.servlet.http.HttpServletRequest; +import jakarta.servlet.http.HttpServletResponse; import org.apache.commons.lang3.StringUtils; import org.apache.commons.lang3.Validate; import org.apache.commons.text.StringSubstitutor; @@ -41,9 +44,6 @@ import org.slf4j.LoggerFactory; import java.io.IOException; import java.util.Date; import java.util.Map.Entry; -import javax.servlet.ServletException; -import javax.servlet.http.HttpServletRequest; -import javax.servlet.http.HttpServletResponse; import static org.apache.commons.lang3.StringUtils.isNotBlank; diff --git a/hapi-fhir-server/src/main/java/ca/uhn/fhir/rest/server/interceptor/RequestValidatingInterceptor.java b/hapi-fhir-server/src/main/java/ca/uhn/fhir/rest/server/interceptor/RequestValidatingInterceptor.java index 69208893551..a734c691245 100644 --- a/hapi-fhir-server/src/main/java/ca/uhn/fhir/rest/server/interceptor/RequestValidatingInterceptor.java +++ b/hapi-fhir-server/src/main/java/ca/uhn/fhir/rest/server/interceptor/RequestValidatingInterceptor.java @@ -30,10 +30,10 @@ import ca.uhn.fhir.rest.server.method.ResourceParameter; import ca.uhn.fhir.validation.FhirValidator; import ca.uhn.fhir.validation.ResultSeverityEnum; import ca.uhn.fhir.validation.ValidationResult; +import jakarta.servlet.http.HttpServletRequest; +import jakarta.servlet.http.HttpServletResponse; import java.nio.charset.Charset; -import javax.servlet.http.HttpServletRequest; -import javax.servlet.http.HttpServletResponse; import static org.apache.commons.lang3.StringUtils.isBlank; diff --git a/hapi-fhir-server/src/main/java/ca/uhn/fhir/rest/server/interceptor/ResponseHighlighterInterceptor.java b/hapi-fhir-server/src/main/java/ca/uhn/fhir/rest/server/interceptor/ResponseHighlighterInterceptor.java index 6ad6ec0716e..7052517262b 100644 --- a/hapi-fhir-server/src/main/java/ca/uhn/fhir/rest/server/interceptor/ResponseHighlighterInterceptor.java +++ b/hapi-fhir-server/src/main/java/ca/uhn/fhir/rest/server/interceptor/ResponseHighlighterInterceptor.java @@ -45,6 +45,11 @@ import ca.uhn.fhir.util.FhirTerser; import ca.uhn.fhir.util.StopWatch; import ca.uhn.fhir.util.UrlUtil; import com.google.common.annotations.VisibleForTesting; +import jakarta.annotation.Nonnull; +import jakarta.annotation.Nullable; +import jakarta.servlet.ServletRequest; +import jakarta.servlet.http.HttpServletRequest; +import jakarta.servlet.http.HttpServletResponse; import org.apache.commons.io.FileUtils; import org.apache.commons.io.IOUtils; import org.apache.commons.text.StringEscapeUtils; @@ -64,11 +69,6 @@ import java.util.List; import java.util.Map; import java.util.Set; import java.util.stream.Collectors; -import javax.annotation.Nonnull; -import javax.annotation.Nullable; -import javax.servlet.ServletRequest; -import javax.servlet.http.HttpServletRequest; -import javax.servlet.http.HttpServletResponse; import static org.apache.commons.lang3.StringUtils.defaultString; import static org.apache.commons.lang3.StringUtils.isBlank; diff --git a/hapi-fhir-server/src/main/java/ca/uhn/fhir/rest/server/interceptor/ResponseSizeCapturingInterceptor.java b/hapi-fhir-server/src/main/java/ca/uhn/fhir/rest/server/interceptor/ResponseSizeCapturingInterceptor.java index 5db02636da9..88b89c7c945 100644 --- a/hapi-fhir-server/src/main/java/ca/uhn/fhir/rest/server/interceptor/ResponseSizeCapturingInterceptor.java +++ b/hapi-fhir-server/src/main/java/ca/uhn/fhir/rest/server/interceptor/ResponseSizeCapturingInterceptor.java @@ -22,6 +22,7 @@ package ca.uhn.fhir.rest.server.interceptor; import ca.uhn.fhir.interceptor.api.Hook; import ca.uhn.fhir.interceptor.api.Pointcut; import ca.uhn.fhir.rest.api.server.RequestDetails; +import jakarta.annotation.Nonnull; import org.apache.commons.lang3.Validate; import java.io.IOException; @@ -29,7 +30,6 @@ import java.io.Writer; import java.util.ArrayList; import java.util.List; import java.util.function.Consumer; -import javax.annotation.Nonnull; /** * This interceptor captures and makes diff --git a/hapi-fhir-server/src/main/java/ca/uhn/fhir/rest/server/interceptor/ResponseTerminologyDisplayPopulationInterceptor.java b/hapi-fhir-server/src/main/java/ca/uhn/fhir/rest/server/interceptor/ResponseTerminologyDisplayPopulationInterceptor.java index c6a7c901ee6..e86facffe09 100644 --- a/hapi-fhir-server/src/main/java/ca/uhn/fhir/rest/server/interceptor/ResponseTerminologyDisplayPopulationInterceptor.java +++ b/hapi-fhir-server/src/main/java/ca/uhn/fhir/rest/server/interceptor/ResponseTerminologyDisplayPopulationInterceptor.java @@ -24,6 +24,7 @@ import ca.uhn.fhir.context.BaseRuntimeElementCompositeDefinition; import ca.uhn.fhir.context.BaseRuntimeElementDefinition; import ca.uhn.fhir.context.RuntimePrimitiveDatatypeDefinition; import ca.uhn.fhir.context.support.IValidationSupport; +import ca.uhn.fhir.context.support.LookupCodeRequest; import ca.uhn.fhir.context.support.ValidationSupportContext; import ca.uhn.fhir.interceptor.api.Hook; import ca.uhn.fhir.interceptor.api.Pointcut; @@ -122,8 +123,8 @@ public class ResponseTerminologyDisplayPopulationInterceptor extends BaseRespons ValidationSupportContext validationSupportContext = new ValidationSupportContext(myValidationSupport); if (myValidationSupport.isCodeSystemSupported(validationSupportContext, system)) { - IValidationSupport.LookupCodeResult lookupCodeResult = - myValidationSupport.lookupCode(validationSupportContext, system, code); + IValidationSupport.LookupCodeResult lookupCodeResult = myValidationSupport.lookupCode( + validationSupportContext, new LookupCodeRequest(system, code)); if (lookupCodeResult != null && lookupCodeResult.isFound()) { String newDisplay = lookupCodeResult.getCodeDisplay(); IPrimitiveType newString = myStringDefinition.newInstance(newDisplay); diff --git a/hapi-fhir-server/src/main/java/ca/uhn/fhir/rest/server/interceptor/ResponseTerminologyTranslationSvc.java b/hapi-fhir-server/src/main/java/ca/uhn/fhir/rest/server/interceptor/ResponseTerminologyTranslationSvc.java index 8b54c02186c..88639d4bde3 100644 --- a/hapi-fhir-server/src/main/java/ca/uhn/fhir/rest/server/interceptor/ResponseTerminologyTranslationSvc.java +++ b/hapi-fhir-server/src/main/java/ca/uhn/fhir/rest/server/interceptor/ResponseTerminologyTranslationSvc.java @@ -31,6 +31,7 @@ import ca.uhn.fhir.util.FhirTerser; import ca.uhn.fhir.util.IModelVisitor; import com.google.common.collect.ArrayListMultimap; import com.google.common.collect.Multimap; +import jakarta.annotation.Nonnull; import org.apache.commons.lang3.StringUtils; import org.apache.commons.lang3.Validate; import org.hl7.fhir.instance.model.api.IBase; @@ -43,7 +44,6 @@ import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.Objects; -import javax.annotation.Nonnull; public class ResponseTerminologyTranslationSvc { private BaseRuntimeChildDefinition myCodingSystemChild; diff --git a/hapi-fhir-server/src/main/java/ca/uhn/fhir/rest/server/interceptor/SearchPreferHandlingInterceptor.java b/hapi-fhir-server/src/main/java/ca/uhn/fhir/rest/server/interceptor/SearchPreferHandlingInterceptor.java index 51df7c08ff8..4a729bc63ab 100644 --- a/hapi-fhir-server/src/main/java/ca/uhn/fhir/rest/server/interceptor/SearchPreferHandlingInterceptor.java +++ b/hapi-fhir-server/src/main/java/ca/uhn/fhir/rest/server/interceptor/SearchPreferHandlingInterceptor.java @@ -36,15 +36,15 @@ import ca.uhn.fhir.rest.server.exceptions.AuthenticationException; import ca.uhn.fhir.rest.server.exceptions.InvalidRequestException; import ca.uhn.fhir.rest.server.method.SearchMethodBinding; import ca.uhn.fhir.rest.server.util.ISearchParamRegistry; +import jakarta.annotation.Nonnull; +import jakarta.annotation.Nullable; +import jakarta.servlet.http.HttpServletRequest; +import jakarta.servlet.http.HttpServletResponse; import org.apache.commons.lang3.Validate; import java.util.HashMap; import java.util.List; import java.util.stream.Collectors; -import javax.annotation.Nonnull; -import javax.annotation.Nullable; -import javax.servlet.http.HttpServletRequest; -import javax.servlet.http.HttpServletResponse; import static org.apache.commons.lang3.StringUtils.isNotBlank; diff --git a/hapi-fhir-server/src/main/java/ca/uhn/fhir/rest/server/interceptor/ServeMediaResourceRawInterceptor.java b/hapi-fhir-server/src/main/java/ca/uhn/fhir/rest/server/interceptor/ServeMediaResourceRawInterceptor.java index d1ceea5cab0..0fbbeb67d4b 100644 --- a/hapi-fhir-server/src/main/java/ca/uhn/fhir/rest/server/interceptor/ServeMediaResourceRawInterceptor.java +++ b/hapi-fhir-server/src/main/java/ca/uhn/fhir/rest/server/interceptor/ServeMediaResourceRawInterceptor.java @@ -31,6 +31,8 @@ import ca.uhn.fhir.rest.server.RestfulServer; import ca.uhn.fhir.rest.server.RestfulServerUtils; import ca.uhn.fhir.rest.server.exceptions.AuthenticationException; import ca.uhn.fhir.rest.server.exceptions.InternalErrorException; +import jakarta.servlet.http.HttpServletRequest; +import jakarta.servlet.http.HttpServletResponse; import org.hl7.fhir.instance.model.api.IBaseResource; import org.hl7.fhir.instance.model.api.IPrimitiveType; @@ -39,8 +41,6 @@ import java.util.Collections; import java.util.HashSet; import java.util.Optional; import java.util.Set; -import javax.servlet.http.HttpServletRequest; -import javax.servlet.http.HttpServletResponse; import static org.apache.commons.lang3.StringUtils.isBlank; diff --git a/hapi-fhir-server/src/main/java/ca/uhn/fhir/rest/server/interceptor/ValidationResultEnrichingInterceptor.java b/hapi-fhir-server/src/main/java/ca/uhn/fhir/rest/server/interceptor/ValidationResultEnrichingInterceptor.java index 3adefa98466..19753fab55e 100644 --- a/hapi-fhir-server/src/main/java/ca/uhn/fhir/rest/server/interceptor/ValidationResultEnrichingInterceptor.java +++ b/hapi-fhir-server/src/main/java/ca/uhn/fhir/rest/server/interceptor/ValidationResultEnrichingInterceptor.java @@ -24,13 +24,13 @@ import ca.uhn.fhir.interceptor.api.Interceptor; import ca.uhn.fhir.interceptor.api.Pointcut; import ca.uhn.fhir.rest.api.server.RequestDetails; import ca.uhn.fhir.validation.ValidationResult; +import jakarta.annotation.Nonnull; +import jakarta.annotation.Nullable; import org.hl7.fhir.instance.model.api.IBaseOperationOutcome; import org.hl7.fhir.instance.model.api.IBaseResource; import java.util.ArrayList; import java.util.List; -import javax.annotation.Nonnull; -import javax.annotation.Nullable; @Interceptor public class ValidationResultEnrichingInterceptor { diff --git a/hapi-fhir-server/src/main/java/ca/uhn/fhir/rest/server/interceptor/VerboseLoggingInterceptor.java b/hapi-fhir-server/src/main/java/ca/uhn/fhir/rest/server/interceptor/VerboseLoggingInterceptor.java index 7b000b05ab5..7dbcf69e73b 100644 --- a/hapi-fhir-server/src/main/java/ca/uhn/fhir/rest/server/interceptor/VerboseLoggingInterceptor.java +++ b/hapi-fhir-server/src/main/java/ca/uhn/fhir/rest/server/interceptor/VerboseLoggingInterceptor.java @@ -21,10 +21,10 @@ package ca.uhn.fhir.rest.server.interceptor; import ca.uhn.fhir.rest.api.server.RequestDetails; import ca.uhn.fhir.rest.server.exceptions.AuthenticationException; +import jakarta.servlet.http.HttpServletRequest; +import jakarta.servlet.http.HttpServletResponse; import java.util.Enumeration; -import javax.servlet.http.HttpServletRequest; -import javax.servlet.http.HttpServletResponse; /** * This interceptor creates verbose server log entries containing the complete request and response payloads. diff --git a/hapi-fhir-server/src/main/java/ca/uhn/fhir/rest/server/interceptor/auth/AdditionalCompartmentSearchParameters.java b/hapi-fhir-server/src/main/java/ca/uhn/fhir/rest/server/interceptor/auth/AdditionalCompartmentSearchParameters.java index 661495f782b..9f5f563148d 100644 --- a/hapi-fhir-server/src/main/java/ca/uhn/fhir/rest/server/interceptor/auth/AdditionalCompartmentSearchParameters.java +++ b/hapi-fhir-server/src/main/java/ca/uhn/fhir/rest/server/interceptor/auth/AdditionalCompartmentSearchParameters.java @@ -20,13 +20,13 @@ package ca.uhn.fhir.rest.server.interceptor.auth; import ca.uhn.fhir.i18n.Msg; +import jakarta.annotation.Nonnull; import java.util.Arrays; import java.util.HashMap; import java.util.HashSet; import java.util.Map; import java.util.Set; -import javax.annotation.Nonnull; /** * This class is used in RuleBuilder, as a way to provide a compartment permission additional resource search params that diff --git a/hapi-fhir-server/src/main/java/ca/uhn/fhir/rest/server/interceptor/auth/AllowedCodeInValueSet.java b/hapi-fhir-server/src/main/java/ca/uhn/fhir/rest/server/interceptor/auth/AllowedCodeInValueSet.java index 2f7f217259a..36c04799488 100644 --- a/hapi-fhir-server/src/main/java/ca/uhn/fhir/rest/server/interceptor/auth/AllowedCodeInValueSet.java +++ b/hapi-fhir-server/src/main/java/ca/uhn/fhir/rest/server/interceptor/auth/AllowedCodeInValueSet.java @@ -19,7 +19,7 @@ */ package ca.uhn.fhir.rest.server.interceptor.auth; -import javax.annotation.Nonnull; +import jakarta.annotation.Nonnull; import static org.apache.commons.lang3.StringUtils.isNotBlank; diff --git a/hapi-fhir-server/src/main/java/ca/uhn/fhir/rest/server/interceptor/auth/AuthorizationInterceptor.java b/hapi-fhir-server/src/main/java/ca/uhn/fhir/rest/server/interceptor/auth/AuthorizationInterceptor.java index 853a90c6f7e..57175a84db3 100644 --- a/hapi-fhir-server/src/main/java/ca/uhn/fhir/rest/server/interceptor/auth/AuthorizationInterceptor.java +++ b/hapi-fhir-server/src/main/java/ca/uhn/fhir/rest/server/interceptor/auth/AuthorizationInterceptor.java @@ -32,6 +32,8 @@ import ca.uhn.fhir.rest.api.server.bulk.BulkExportJobParameters; import ca.uhn.fhir.rest.server.exceptions.ForbiddenOperationException; import ca.uhn.fhir.rest.server.interceptor.consent.ConsentInterceptor; import com.google.common.collect.Lists; +import jakarta.annotation.Nonnull; +import jakarta.annotation.Nullable; import org.apache.commons.lang3.StringUtils; import org.apache.commons.lang3.Validate; import org.apache.commons.lang3.builder.ToStringBuilder; @@ -52,8 +54,6 @@ import java.util.IdentityHashMap; import java.util.List; import java.util.Set; import java.util.concurrent.atomic.AtomicInteger; -import javax.annotation.Nonnull; -import javax.annotation.Nullable; import static java.util.Objects.isNull; import static java.util.Objects.nonNull; diff --git a/hapi-fhir-server/src/main/java/ca/uhn/fhir/rest/server/interceptor/auth/AuthorizedList.java b/hapi-fhir-server/src/main/java/ca/uhn/fhir/rest/server/interceptor/auth/AuthorizedList.java index d49ec122d68..b781e388e6c 100644 --- a/hapi-fhir-server/src/main/java/ca/uhn/fhir/rest/server/interceptor/auth/AuthorizedList.java +++ b/hapi-fhir-server/src/main/java/ca/uhn/fhir/rest/server/interceptor/auth/AuthorizedList.java @@ -20,12 +20,12 @@ package ca.uhn.fhir.rest.server.interceptor.auth; import ca.uhn.fhir.rest.api.server.RequestDetails; +import jakarta.annotation.Nonnull; +import jakarta.annotation.Nullable; import org.apache.commons.lang3.Validate; import java.util.ArrayList; import java.util.List; -import javax.annotation.Nonnull; -import javax.annotation.Nullable; /** * Return type for {@link SearchNarrowingInterceptor#buildAuthorizedList(RequestDetails)} diff --git a/hapi-fhir-server/src/main/java/ca/uhn/fhir/rest/server/interceptor/auth/IAuthRuleBuilderRuleBulkExport.java b/hapi-fhir-server/src/main/java/ca/uhn/fhir/rest/server/interceptor/auth/IAuthRuleBuilderRuleBulkExport.java index 448e9bd2512..87491b033b9 100644 --- a/hapi-fhir-server/src/main/java/ca/uhn/fhir/rest/server/interceptor/auth/IAuthRuleBuilderRuleBulkExport.java +++ b/hapi-fhir-server/src/main/java/ca/uhn/fhir/rest/server/interceptor/auth/IAuthRuleBuilderRuleBulkExport.java @@ -19,10 +19,9 @@ */ package ca.uhn.fhir.rest.server.interceptor.auth; +import jakarta.annotation.Nonnull; import org.hl7.fhir.instance.model.api.IIdType; -import javax.annotation.Nonnull; - /** * @since 5.5.0 */ diff --git a/hapi-fhir-server/src/main/java/ca/uhn/fhir/rest/server/interceptor/auth/IAuthRuleBuilderRuleOpClassifier.java b/hapi-fhir-server/src/main/java/ca/uhn/fhir/rest/server/interceptor/auth/IAuthRuleBuilderRuleOpClassifier.java index 328fd530dc5..27b63223b75 100644 --- a/hapi-fhir-server/src/main/java/ca/uhn/fhir/rest/server/interceptor/auth/IAuthRuleBuilderRuleOpClassifier.java +++ b/hapi-fhir-server/src/main/java/ca/uhn/fhir/rest/server/interceptor/auth/IAuthRuleBuilderRuleOpClassifier.java @@ -19,10 +19,10 @@ */ package ca.uhn.fhir.rest.server.interceptor.auth; +import jakarta.annotation.Nonnull; import org.hl7.fhir.instance.model.api.IIdType; import java.util.Collection; -import javax.annotation.Nonnull; public interface IAuthRuleBuilderRuleOpClassifier { diff --git a/hapi-fhir-server/src/main/java/ca/uhn/fhir/rest/server/interceptor/auth/IAuthRuleFinished.java b/hapi-fhir-server/src/main/java/ca/uhn/fhir/rest/server/interceptor/auth/IAuthRuleFinished.java index 5a3d2397cd6..9ab7f5b6995 100644 --- a/hapi-fhir-server/src/main/java/ca/uhn/fhir/rest/server/interceptor/auth/IAuthRuleFinished.java +++ b/hapi-fhir-server/src/main/java/ca/uhn/fhir/rest/server/interceptor/auth/IAuthRuleFinished.java @@ -19,8 +19,9 @@ */ package ca.uhn.fhir.rest.server.interceptor.auth; +import jakarta.annotation.Nullable; + import java.util.List; -import javax.annotation.Nullable; public interface IAuthRuleFinished { diff --git a/hapi-fhir-server/src/main/java/ca/uhn/fhir/rest/server/interceptor/auth/IAuthRuleTester.java b/hapi-fhir-server/src/main/java/ca/uhn/fhir/rest/server/interceptor/auth/IAuthRuleTester.java index 05eabea9f0e..7bfa152aae2 100644 --- a/hapi-fhir-server/src/main/java/ca/uhn/fhir/rest/server/interceptor/auth/IAuthRuleTester.java +++ b/hapi-fhir-server/src/main/java/ca/uhn/fhir/rest/server/interceptor/auth/IAuthRuleTester.java @@ -21,13 +21,12 @@ package ca.uhn.fhir.rest.server.interceptor.auth; import ca.uhn.fhir.rest.api.RestOperationTypeEnum; import ca.uhn.fhir.rest.api.server.RequestDetails; +import jakarta.annotation.Nonnull; +import jakarta.annotation.Nullable; import org.apache.commons.lang3.Validate; import org.hl7.fhir.instance.model.api.IBaseResource; import org.hl7.fhir.instance.model.api.IIdType; -import javax.annotation.Nonnull; -import javax.annotation.Nullable; - /** * Allows user-supplied logic for authorization rules. *

    diff --git a/hapi-fhir-server/src/main/java/ca/uhn/fhir/rest/server/interceptor/auth/IAuthorizationSearchParamMatcher.java b/hapi-fhir-server/src/main/java/ca/uhn/fhir/rest/server/interceptor/auth/IAuthorizationSearchParamMatcher.java index 7b941dc6165..7c5c7719d18 100644 --- a/hapi-fhir-server/src/main/java/ca/uhn/fhir/rest/server/interceptor/auth/IAuthorizationSearchParamMatcher.java +++ b/hapi-fhir-server/src/main/java/ca/uhn/fhir/rest/server/interceptor/auth/IAuthorizationSearchParamMatcher.java @@ -19,11 +19,10 @@ */ package ca.uhn.fhir.rest.server.interceptor.auth; +import jakarta.annotation.Nonnull; +import jakarta.annotation.Nullable; import org.hl7.fhir.instance.model.api.IBaseResource; -import javax.annotation.Nonnull; -import javax.annotation.Nullable; - /** * Adapt the InMemoryMatcher to support authorization filters in {@link FhirQueryRuleTester}. * Exists because filters may be applied to resources that don't support all paramters, and UNSUPPORTED diff --git a/hapi-fhir-server/src/main/java/ca/uhn/fhir/rest/server/interceptor/auth/IRuleApplier.java b/hapi-fhir-server/src/main/java/ca/uhn/fhir/rest/server/interceptor/auth/IRuleApplier.java index 7f88de3b9ac..4dbb76516a5 100644 --- a/hapi-fhir-server/src/main/java/ca/uhn/fhir/rest/server/interceptor/auth/IRuleApplier.java +++ b/hapi-fhir-server/src/main/java/ca/uhn/fhir/rest/server/interceptor/auth/IRuleApplier.java @@ -24,13 +24,12 @@ import ca.uhn.fhir.interceptor.api.Pointcut; import ca.uhn.fhir.rest.api.RestOperationTypeEnum; import ca.uhn.fhir.rest.api.server.RequestDetails; import ca.uhn.fhir.rest.server.interceptor.auth.AuthorizationInterceptor.Verdict; +import jakarta.annotation.Nonnull; +import jakarta.annotation.Nullable; import org.hl7.fhir.instance.model.api.IBaseResource; import org.hl7.fhir.instance.model.api.IIdType; import org.slf4j.Logger; -import javax.annotation.Nonnull; -import javax.annotation.Nullable; - public interface IRuleApplier { @Nonnull diff --git a/hapi-fhir-server/src/main/java/ca/uhn/fhir/rest/server/interceptor/auth/OperationRule.java b/hapi-fhir-server/src/main/java/ca/uhn/fhir/rest/server/interceptor/auth/OperationRule.java index f9c14953462..1dff7f1217a 100644 --- a/hapi-fhir-server/src/main/java/ca/uhn/fhir/rest/server/interceptor/auth/OperationRule.java +++ b/hapi-fhir-server/src/main/java/ca/uhn/fhir/rest/server/interceptor/auth/OperationRule.java @@ -24,6 +24,7 @@ import ca.uhn.fhir.interceptor.api.Pointcut; import ca.uhn.fhir.rest.api.RestOperationTypeEnum; import ca.uhn.fhir.rest.api.server.RequestDetails; import ca.uhn.fhir.rest.server.interceptor.auth.AuthorizationInterceptor.Verdict; +import jakarta.annotation.Nonnull; import org.apache.commons.lang3.builder.ToStringBuilder; import org.hl7.fhir.instance.model.api.IBaseResource; import org.hl7.fhir.instance.model.api.IIdType; @@ -31,7 +32,6 @@ import org.hl7.fhir.instance.model.api.IIdType; import java.util.HashSet; import java.util.List; import java.util.Set; -import javax.annotation.Nonnull; class OperationRule extends BaseRule implements IAuthRule { private String myOperationName; diff --git a/hapi-fhir-server/src/main/java/ca/uhn/fhir/rest/server/interceptor/auth/RuleBuilder.java b/hapi-fhir-server/src/main/java/ca/uhn/fhir/rest/server/interceptor/auth/RuleBuilder.java index 804465dc069..c8f8396ab90 100644 --- a/hapi-fhir-server/src/main/java/ca/uhn/fhir/rest/server/interceptor/auth/RuleBuilder.java +++ b/hapi-fhir-server/src/main/java/ca/uhn/fhir/rest/server/interceptor/auth/RuleBuilder.java @@ -26,6 +26,7 @@ import ca.uhn.fhir.rest.api.Constants; import ca.uhn.fhir.rest.api.RestOperationTypeEnum; import ca.uhn.fhir.rest.api.server.RequestDetails; import com.google.common.collect.Lists; +import jakarta.annotation.Nonnull; import org.apache.commons.lang3.Validate; import org.hl7.fhir.instance.model.api.IBaseResource; import org.hl7.fhir.instance.model.api.IIdType; @@ -39,7 +40,6 @@ import java.util.List; import java.util.Optional; import java.util.Set; import java.util.concurrent.ConcurrentHashMap; -import javax.annotation.Nonnull; import static org.apache.commons.lang3.ObjectUtils.defaultIfNull; diff --git a/hapi-fhir-server/src/main/java/ca/uhn/fhir/rest/server/interceptor/auth/RuleImplOp.java b/hapi-fhir-server/src/main/java/ca/uhn/fhir/rest/server/interceptor/auth/RuleImplOp.java index 5b108b982c4..782573e8942 100644 --- a/hapi-fhir-server/src/main/java/ca/uhn/fhir/rest/server/interceptor/auth/RuleImplOp.java +++ b/hapi-fhir-server/src/main/java/ca/uhn/fhir/rest/server/interceptor/auth/RuleImplOp.java @@ -37,6 +37,8 @@ import ca.uhn.fhir.util.FhirTerser; import ca.uhn.fhir.util.UrlUtil; import ca.uhn.fhir.util.bundle.BundleEntryParts; import com.google.common.annotations.VisibleForTesting; +import jakarta.annotation.Nonnull; +import jakarta.annotation.Nullable; import org.apache.commons.lang3.StringUtils; import org.apache.commons.lang3.builder.ToStringBuilder; import org.hl7.fhir.instance.model.api.IBaseBundle; @@ -53,8 +55,6 @@ import java.util.Map; import java.util.Objects; import java.util.Set; import java.util.stream.Collectors; -import javax.annotation.Nonnull; -import javax.annotation.Nullable; import static org.apache.commons.lang3.StringUtils.defaultString; import static org.apache.commons.lang3.StringUtils.isNotBlank; diff --git a/hapi-fhir-server/src/main/java/ca/uhn/fhir/rest/server/interceptor/auth/SearchNarrowingConsentService.java b/hapi-fhir-server/src/main/java/ca/uhn/fhir/rest/server/interceptor/auth/SearchNarrowingConsentService.java index ff8855b6596..01225b39543 100644 --- a/hapi-fhir-server/src/main/java/ca/uhn/fhir/rest/server/interceptor/auth/SearchNarrowingConsentService.java +++ b/hapi-fhir-server/src/main/java/ca/uhn/fhir/rest/server/interceptor/auth/SearchNarrowingConsentService.java @@ -27,13 +27,13 @@ import ca.uhn.fhir.rest.server.interceptor.consent.IConsentContextServices; import ca.uhn.fhir.rest.server.interceptor.consent.IConsentService; import ca.uhn.fhir.rest.server.util.FhirContextSearchParamRegistry; import ca.uhn.fhir.rest.server.util.ISearchParamRegistry; +import jakarta.annotation.Nonnull; import org.apache.commons.lang3.Validate; import org.hl7.fhir.instance.model.api.IBaseResource; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import java.util.List; -import javax.annotation.Nonnull; public class SearchNarrowingConsentService implements IConsentService { private static final Logger ourLog = LoggerFactory.getLogger(SearchNarrowingConsentService.class); diff --git a/hapi-fhir-server/src/main/java/ca/uhn/fhir/rest/server/interceptor/auth/SearchNarrowingInterceptor.java b/hapi-fhir-server/src/main/java/ca/uhn/fhir/rest/server/interceptor/auth/SearchNarrowingInterceptor.java index 776a6221778..db87b01b2aa 100644 --- a/hapi-fhir-server/src/main/java/ca/uhn/fhir/rest/server/interceptor/auth/SearchNarrowingInterceptor.java +++ b/hapi-fhir-server/src/main/java/ca/uhn/fhir/rest/server/interceptor/auth/SearchNarrowingInterceptor.java @@ -45,6 +45,9 @@ import ca.uhn.fhir.util.UrlUtil; import ca.uhn.fhir.util.ValidateUtil; import ca.uhn.fhir.util.bundle.ModifiableBundleEntry; import com.google.common.collect.ArrayListMultimap; +import jakarta.annotation.Nullable; +import jakarta.servlet.http.HttpServletRequest; +import jakarta.servlet.http.HttpServletResponse; import org.apache.commons.collections4.ListUtils; import org.apache.commons.lang3.StringUtils; import org.apache.commons.lang3.Validate; @@ -61,9 +64,6 @@ import java.util.Optional; import java.util.Set; import java.util.function.Consumer; import java.util.stream.Collectors; -import javax.annotation.Nullable; -import javax.servlet.http.HttpServletRequest; -import javax.servlet.http.HttpServletResponse; /** * This interceptor can be used to automatically narrow the scope of searches in order to diff --git a/hapi-fhir-server/src/main/java/ca/uhn/fhir/rest/server/interceptor/auth/SearchParameterAndValueSetRuleImpl.java b/hapi-fhir-server/src/main/java/ca/uhn/fhir/rest/server/interceptor/auth/SearchParameterAndValueSetRuleImpl.java index 6008304c3ab..e0d211c2f7e 100644 --- a/hapi-fhir-server/src/main/java/ca/uhn/fhir/rest/server/interceptor/auth/SearchParameterAndValueSetRuleImpl.java +++ b/hapi-fhir-server/src/main/java/ca/uhn/fhir/rest/server/interceptor/auth/SearchParameterAndValueSetRuleImpl.java @@ -31,6 +31,7 @@ import ca.uhn.fhir.rest.api.server.RequestDetails; import ca.uhn.fhir.rest.server.exceptions.InternalErrorException; import ca.uhn.fhir.rest.server.util.ISearchParamRegistry; import ca.uhn.fhir.util.FhirTerser; +import jakarta.annotation.Nonnull; import org.apache.commons.lang3.Validate; import org.hl7.fhir.instance.model.api.IBase; import org.hl7.fhir.instance.model.api.IBaseResource; @@ -40,7 +41,6 @@ import org.slf4j.Logger; import java.util.List; import java.util.Set; -import javax.annotation.Nonnull; import static org.apache.commons.lang3.StringUtils.isNotBlank; diff --git a/hapi-fhir-server/src/main/java/ca/uhn/fhir/rest/server/interceptor/consent/ConsentInterceptor.java b/hapi-fhir-server/src/main/java/ca/uhn/fhir/rest/server/interceptor/consent/ConsentInterceptor.java index 95b0481c9bf..8f2a16e63c9 100644 --- a/hapi-fhir-server/src/main/java/ca/uhn/fhir/rest/server/interceptor/consent/ConsentInterceptor.java +++ b/hapi-fhir-server/src/main/java/ca/uhn/fhir/rest/server/interceptor/consent/ConsentInterceptor.java @@ -38,6 +38,8 @@ import ca.uhn.fhir.rest.server.interceptor.auth.AuthorizationConstants; import ca.uhn.fhir.rest.server.util.ICachedSearchDetails; import ca.uhn.fhir.util.BundleUtil; import ca.uhn.fhir.util.IModelVisitor2; +import jakarta.annotation.Nonnull; +import jakarta.annotation.Nullable; import org.apache.commons.lang3.Validate; import org.hl7.fhir.instance.model.api.IBase; import org.hl7.fhir.instance.model.api.IBaseBundle; @@ -178,18 +180,29 @@ public class ConsentInterceptor { } } + /** + * Check if this request is eligible for cached search results. + * We can't use a cached result if consent may use canSeeResource. + * This checks for AUTHORIZED requests, and the responses from shouldProcessCanSeeResource() + * to see if this holds. + * @return may the request be satisfied from cache. + */ @Hook(value = Pointcut.STORAGE_PRECHECK_FOR_CACHED_SEARCH) - public boolean interceptPreCheckForCachedSearch(RequestDetails theRequestDetails) { - if (isRequestAuthorized(theRequestDetails)) { - return true; - } - return false; + public boolean interceptPreCheckForCachedSearch(@Nonnull RequestDetails theRequestDetails) { + return !isProcessCanSeeResource(theRequestDetails, null); } + /** + * Check if the search results from this request might be reused by later searches. + * We can't use a cached result if consent may use canSeeResource. + * This checks for AUTHORIZED requests, and the responses from shouldProcessCanSeeResource() + * to see if this holds. + * If not, marks the result as single-use. + */ @Hook(value = Pointcut.STORAGE_PRESEARCH_REGISTERED) public void interceptPreSearchRegistered( RequestDetails theRequestDetails, ICachedSearchDetails theCachedSearchDetails) { - if (!isRequestAuthorized(theRequestDetails)) { + if (isProcessCanSeeResource(theRequestDetails, null)) { theCachedSearchDetails.setCannotBeReused(); } } @@ -197,28 +210,10 @@ public class ConsentInterceptor { @Hook(value = Pointcut.STORAGE_PREACCESS_RESOURCES) public void interceptPreAccess( RequestDetails theRequestDetails, IPreResourceAccessDetails thePreResourceAccessDetails) { - if (isRequestAuthorized(theRequestDetails)) { - return; - } - if (isSkipServiceForRequest(theRequestDetails)) { - return; - } - if (myConsentService.isEmpty()) { - return; - } - // First check if we should be calling canSeeResource for the individual - // consent services + // Flags for each service boolean[] processConsentSvcs = new boolean[myConsentService.size()]; - boolean processAnyConsentSvcs = false; - for (int consentSvcIdx = 0; consentSvcIdx < myConsentService.size(); consentSvcIdx++) { - IConsentService nextService = myConsentService.get(consentSvcIdx); - - boolean shouldCallCanSeeResource = - nextService.shouldProcessCanSeeResource(theRequestDetails, myContextConsentServices); - processAnyConsentSvcs |= shouldCallCanSeeResource; - processConsentSvcs[consentSvcIdx] = shouldCallCanSeeResource; - } + boolean processAnyConsentSvcs = isProcessCanSeeResource(theRequestDetails, processConsentSvcs); if (!processAnyConsentSvcs) { return; @@ -262,6 +257,39 @@ public class ConsentInterceptor { } } + /** + * Is canSeeResource() active in any services? + * @param theProcessConsentSvcsFlags filled in with the responses from shouldProcessCanSeeResource each service + * @return true of any service responded true to shouldProcessCanSeeResource() + */ + private boolean isProcessCanSeeResource( + @Nonnull RequestDetails theRequestDetails, @Nullable boolean[] theProcessConsentSvcsFlags) { + if (isRequestAuthorized(theRequestDetails)) { + return false; + } + if (isSkipServiceForRequest(theRequestDetails)) { + return false; + } + if (myConsentService.isEmpty()) { + return false; + } + + if (theProcessConsentSvcsFlags == null) { + theProcessConsentSvcsFlags = new boolean[myConsentService.size()]; + } + Validate.isTrue(theProcessConsentSvcsFlags.length == myConsentService.size()); + boolean processAnyConsentSvcs = false; + for (int consentSvcIdx = 0; consentSvcIdx < myConsentService.size(); consentSvcIdx++) { + IConsentService nextService = myConsentService.get(consentSvcIdx); + + boolean shouldCallCanSeeResource = + nextService.shouldProcessCanSeeResource(theRequestDetails, myContextConsentServices); + processAnyConsentSvcs |= shouldCallCanSeeResource; + theProcessConsentSvcsFlags[consentSvcIdx] = shouldCallCanSeeResource; + } + return processAnyConsentSvcs; + } + @Hook(value = Pointcut.STORAGE_PRESHOW_RESOURCES) public void interceptPreShow(RequestDetails theRequestDetails, IPreResourceShowDetails thePreResourceShowDetails) { if (isRequestAuthorized(theRequestDetails)) { diff --git a/hapi-fhir-server/src/main/java/ca/uhn/fhir/rest/server/interceptor/consent/DelegatingConsentService.java b/hapi-fhir-server/src/main/java/ca/uhn/fhir/rest/server/interceptor/consent/DelegatingConsentService.java index f073a436b0a..e44fc3d80b1 100644 --- a/hapi-fhir-server/src/main/java/ca/uhn/fhir/rest/server/interceptor/consent/DelegatingConsentService.java +++ b/hapi-fhir-server/src/main/java/ca/uhn/fhir/rest/server/interceptor/consent/DelegatingConsentService.java @@ -37,6 +37,12 @@ public class DelegatingConsentService implements IConsentService { return myTarget.startOperation(theRequestDetails, theContextServices); } + @Override + public boolean shouldProcessCanSeeResource( + RequestDetails theRequestDetails, IConsentContextServices theContextServices) { + return myTarget.shouldProcessCanSeeResource(theRequestDetails, theContextServices); + } + @Override public ConsentOutcome canSeeResource( RequestDetails theRequestDetails, IBaseResource theResource, IConsentContextServices theContextServices) { diff --git a/hapi-fhir-server/src/main/java/ca/uhn/fhir/rest/server/interceptor/partition/RequestTenantPartitionInterceptor.java b/hapi-fhir-server/src/main/java/ca/uhn/fhir/rest/server/interceptor/partition/RequestTenantPartitionInterceptor.java index 8e035e5a2d0..500e29fdd10 100644 --- a/hapi-fhir-server/src/main/java/ca/uhn/fhir/rest/server/interceptor/partition/RequestTenantPartitionInterceptor.java +++ b/hapi-fhir-server/src/main/java/ca/uhn/fhir/rest/server/interceptor/partition/RequestTenantPartitionInterceptor.java @@ -28,8 +28,7 @@ import ca.uhn.fhir.rest.api.server.RequestDetails; import ca.uhn.fhir.rest.api.server.SystemRequestDetails; import ca.uhn.fhir.rest.server.exceptions.InternalErrorException; import ca.uhn.fhir.rest.server.tenant.ITenantIdentificationStrategy; - -import javax.annotation.Nonnull; +import jakarta.annotation.Nonnull; import static org.apache.commons.lang3.StringUtils.isBlank; diff --git a/hapi-fhir-server/src/main/java/ca/uhn/fhir/rest/server/interceptor/validation/address/impl/LoquateAddressValidator.java b/hapi-fhir-server/src/main/java/ca/uhn/fhir/rest/server/interceptor/validation/address/impl/LoquateAddressValidator.java index 3cb331d777b..302025326a5 100644 --- a/hapi-fhir-server/src/main/java/ca/uhn/fhir/rest/server/interceptor/validation/address/impl/LoquateAddressValidator.java +++ b/hapi-fhir-server/src/main/java/ca/uhn/fhir/rest/server/interceptor/validation/address/impl/LoquateAddressValidator.java @@ -29,6 +29,7 @@ import com.fasterxml.jackson.databind.JsonNode; import com.fasterxml.jackson.databind.ObjectMapper; import com.fasterxml.jackson.databind.node.ArrayNode; import com.fasterxml.jackson.databind.node.ObjectNode; +import jakarta.annotation.Nullable; import org.apache.commons.lang3.StringUtils; import org.apache.commons.lang3.Validate; import org.apache.http.entity.ContentType; @@ -44,7 +45,6 @@ import java.math.BigDecimal; import java.util.Properties; import java.util.regex.Matcher; import java.util.regex.Pattern; -import javax.annotation.Nullable; import static ca.uhn.fhir.rest.server.interceptor.validation.address.IAddressValidator.ADDRESS_QUALITY_EXTENSION_URL; import static ca.uhn.fhir.rest.server.interceptor.validation.address.IAddressValidator.ADDRESS_VERIFICATION_CODE_EXTENSION_URL; diff --git a/hapi-fhir-server/src/main/java/ca/uhn/fhir/rest/server/mail/IMailSvc.java b/hapi-fhir-server/src/main/java/ca/uhn/fhir/rest/server/mail/IMailSvc.java index 53526b4ffe8..42f37636b54 100644 --- a/hapi-fhir-server/src/main/java/ca/uhn/fhir/rest/server/mail/IMailSvc.java +++ b/hapi-fhir-server/src/main/java/ca/uhn/fhir/rest/server/mail/IMailSvc.java @@ -19,11 +19,11 @@ */ package ca.uhn.fhir.rest.server.mail; +import jakarta.annotation.Nonnull; import org.simplejavamail.api.email.Email; import org.simplejavamail.api.mailer.AsyncResponse; import java.util.List; -import javax.annotation.Nonnull; public interface IMailSvc { void sendMail(@Nonnull List theEmails); diff --git a/hapi-fhir-server/src/main/java/ca/uhn/fhir/rest/server/mail/MailSvc.java b/hapi-fhir-server/src/main/java/ca/uhn/fhir/rest/server/mail/MailSvc.java index e635414d151..be21c3df3e8 100644 --- a/hapi-fhir-server/src/main/java/ca/uhn/fhir/rest/server/mail/MailSvc.java +++ b/hapi-fhir-server/src/main/java/ca/uhn/fhir/rest/server/mail/MailSvc.java @@ -19,6 +19,7 @@ */ package ca.uhn.fhir.rest.server.mail; +import jakarta.annotation.Nonnull; import org.apache.commons.lang3.Validate; import org.simplejavamail.MailException; import org.simplejavamail.api.email.Email; @@ -33,7 +34,6 @@ import org.slf4j.LoggerFactory; import java.util.List; import java.util.stream.Collectors; -import javax.annotation.Nonnull; public class MailSvc implements IMailSvc { private static final Logger ourLog = LoggerFactory.getLogger(MailSvc.class); diff --git a/hapi-fhir-server/src/main/java/ca/uhn/fhir/rest/server/messaging/BaseResourceMessage.java b/hapi-fhir-server/src/main/java/ca/uhn/fhir/rest/server/messaging/BaseResourceMessage.java index 66283ec0682..1fdddf93cef 100644 --- a/hapi-fhir-server/src/main/java/ca/uhn/fhir/rest/server/messaging/BaseResourceMessage.java +++ b/hapi-fhir-server/src/main/java/ca/uhn/fhir/rest/server/messaging/BaseResourceMessage.java @@ -24,6 +24,7 @@ import ca.uhn.fhir.model.api.IModelJson; import ca.uhn.fhir.rest.api.RestOperationTypeEnum; import com.fasterxml.jackson.annotation.JsonProperty; import com.google.common.annotations.VisibleForTesting; +import jakarta.annotation.Nullable; import org.apache.commons.lang3.ObjectUtils; import org.apache.commons.lang3.Validate; @@ -31,7 +32,6 @@ import java.util.Collections; import java.util.HashMap; import java.util.Map; import java.util.Optional; -import javax.annotation.Nullable; import static org.apache.commons.lang3.StringUtils.defaultString; diff --git a/hapi-fhir-server/src/main/java/ca/uhn/fhir/rest/server/messaging/BaseResourceModifiedMessage.java b/hapi-fhir-server/src/main/java/ca/uhn/fhir/rest/server/messaging/BaseResourceModifiedMessage.java index c98030e643a..3fc0e27e713 100644 --- a/hapi-fhir-server/src/main/java/ca/uhn/fhir/rest/server/messaging/BaseResourceModifiedMessage.java +++ b/hapi-fhir-server/src/main/java/ca/uhn/fhir/rest/server/messaging/BaseResourceModifiedMessage.java @@ -29,14 +29,14 @@ import ca.uhn.fhir.rest.api.server.RequestDetails; import ca.uhn.fhir.util.ResourceReferenceInfo; import com.fasterxml.jackson.annotation.JsonIgnore; import com.fasterxml.jackson.annotation.JsonProperty; +import jakarta.annotation.Nonnull; +import jakarta.annotation.Nullable; import org.apache.commons.lang3.StringUtils; import org.apache.commons.lang3.builder.ToStringBuilder; import org.hl7.fhir.instance.model.api.IBaseResource; import org.hl7.fhir.instance.model.api.IIdType; import java.util.List; -import javax.annotation.Nonnull; -import javax.annotation.Nullable; import static org.apache.commons.lang3.StringUtils.isBlank; import static org.apache.commons.lang3.StringUtils.isNotBlank; @@ -52,15 +52,15 @@ public abstract class BaseResourceModifiedMessage extends BaseResourceMessage im @JsonProperty(value = "partitionId") protected RequestPartitionId myPartitionId; + @JsonProperty(value = "payloadVersion") + protected String myPayloadVersion; + @JsonIgnore protected transient IBaseResource myPayloadDecoded; @JsonIgnore protected transient String myPayloadType; - @JsonIgnore - protected String myPayloadVersion; - /** * Constructor */ @@ -68,6 +68,12 @@ public abstract class BaseResourceModifiedMessage extends BaseResourceMessage im super(); } + public BaseResourceModifiedMessage(IIdType theIdType, OperationTypeEnum theOperationType) { + this(); + setOperationType(theOperationType); + setPayloadId(theIdType); + } + public BaseResourceModifiedMessage( FhirContext theFhirContext, IBaseResource theResource, OperationTypeEnum theOperationType) { this(); diff --git a/hapi-fhir-server/src/main/java/ca/uhn/fhir/rest/server/messaging/json/BaseJsonMessage.java b/hapi-fhir-server/src/main/java/ca/uhn/fhir/rest/server/messaging/json/BaseJsonMessage.java index b914fd1bbdf..f2ee8e35ce1 100644 --- a/hapi-fhir-server/src/main/java/ca/uhn/fhir/rest/server/messaging/json/BaseJsonMessage.java +++ b/hapi-fhir-server/src/main/java/ca/uhn/fhir/rest/server/messaging/json/BaseJsonMessage.java @@ -21,11 +21,10 @@ package ca.uhn.fhir.rest.server.messaging.json; import ca.uhn.fhir.model.api.IModelJson; import com.fasterxml.jackson.annotation.JsonProperty; +import jakarta.annotation.Nullable; import org.springframework.messaging.Message; import org.springframework.messaging.MessageHeaders; -import javax.annotation.Nullable; - import static java.util.Objects.isNull; import static org.apache.commons.lang3.StringUtils.defaultString; diff --git a/hapi-fhir-server/src/main/java/ca/uhn/fhir/rest/server/messaging/json/ResourceOperationJsonMessage.java b/hapi-fhir-server/src/main/java/ca/uhn/fhir/rest/server/messaging/json/ResourceOperationJsonMessage.java index 7b90f8c0461..b27db0de2a0 100644 --- a/hapi-fhir-server/src/main/java/ca/uhn/fhir/rest/server/messaging/json/ResourceOperationJsonMessage.java +++ b/hapi-fhir-server/src/main/java/ca/uhn/fhir/rest/server/messaging/json/ResourceOperationJsonMessage.java @@ -21,10 +21,9 @@ package ca.uhn.fhir.rest.server.messaging.json; import ca.uhn.fhir.rest.server.messaging.ResourceOperationMessage; import com.fasterxml.jackson.annotation.JsonProperty; +import jakarta.annotation.Nullable; import org.apache.commons.lang3.builder.ToStringBuilder; -import javax.annotation.Nullable; - public class ResourceOperationJsonMessage extends BaseJsonMessage { @JsonProperty("payload") diff --git a/hapi-fhir-server/src/main/java/ca/uhn/fhir/rest/server/method/BaseMethodBinding.java b/hapi-fhir-server/src/main/java/ca/uhn/fhir/rest/server/method/BaseMethodBinding.java index e6fa8df04f2..6d3600486eb 100644 --- a/hapi-fhir-server/src/main/java/ca/uhn/fhir/rest/server/method/BaseMethodBinding.java +++ b/hapi-fhir-server/src/main/java/ca/uhn/fhir/rest/server/method/BaseMethodBinding.java @@ -50,6 +50,7 @@ import ca.uhn.fhir.rest.server.IResourceProvider; import ca.uhn.fhir.rest.server.exceptions.BaseServerResponseException; import ca.uhn.fhir.rest.server.exceptions.InternalErrorException; import ca.uhn.fhir.util.ReflectionUtil; +import jakarta.annotation.Nonnull; import org.hl7.fhir.instance.model.api.IAnyResource; import org.hl7.fhir.instance.model.api.IBaseBundle; import org.hl7.fhir.instance.model.api.IBaseResource; @@ -64,7 +65,6 @@ import java.util.List; import java.util.Set; import java.util.TreeSet; import java.util.stream.Collectors; -import javax.annotation.Nonnull; import static org.apache.commons.lang3.StringUtils.isNotBlank; diff --git a/hapi-fhir-server/src/main/java/ca/uhn/fhir/rest/server/method/BaseOutcomeReturningMethodBinding.java b/hapi-fhir-server/src/main/java/ca/uhn/fhir/rest/server/method/BaseOutcomeReturningMethodBinding.java index e9c7e56458c..5142e676d8b 100644 --- a/hapi-fhir-server/src/main/java/ca/uhn/fhir/rest/server/method/BaseOutcomeReturningMethodBinding.java +++ b/hapi-fhir-server/src/main/java/ca/uhn/fhir/rest/server/method/BaseOutcomeReturningMethodBinding.java @@ -22,11 +22,17 @@ package ca.uhn.fhir.rest.server.method; import ca.uhn.fhir.context.ConfigurationException; import ca.uhn.fhir.context.FhirContext; import ca.uhn.fhir.i18n.Msg; +import ca.uhn.fhir.rest.api.Constants; +import ca.uhn.fhir.rest.api.MethodOutcome; +import ca.uhn.fhir.rest.api.PreferHeader; +import ca.uhn.fhir.rest.api.PreferReturnEnum; +import ca.uhn.fhir.rest.api.RequestTypeEnum; +import ca.uhn.fhir.rest.api.RestOperationTypeEnum; +import ca.uhn.fhir.rest.api.SummaryEnum; import ca.uhn.fhir.rest.api.server.IRestfulResponse; import ca.uhn.fhir.rest.api.server.IRestfulServer; import ca.uhn.fhir.rest.api.server.RequestDetails; import ca.uhn.fhir.rest.api.server.ResponseDetails; -import ca.uhn.fhir.rest.api.*; import ca.uhn.fhir.rest.server.RestfulServerUtils; import ca.uhn.fhir.rest.server.exceptions.BaseServerResponseException; import ca.uhn.fhir.rest.server.exceptions.InternalErrorException; diff --git a/hapi-fhir-server/src/main/java/ca/uhn/fhir/rest/server/method/BaseOutcomeReturningMethodBindingWithResourceIdButNoResourceBody.java b/hapi-fhir-server/src/main/java/ca/uhn/fhir/rest/server/method/BaseOutcomeReturningMethodBindingWithResourceIdButNoResourceBody.java index 19a43078ce3..fc90d4a2b01 100644 --- a/hapi-fhir-server/src/main/java/ca/uhn/fhir/rest/server/method/BaseOutcomeReturningMethodBindingWithResourceIdButNoResourceBody.java +++ b/hapi-fhir-server/src/main/java/ca/uhn/fhir/rest/server/method/BaseOutcomeReturningMethodBindingWithResourceIdButNoResourceBody.java @@ -19,9 +19,11 @@ */ package ca.uhn.fhir.rest.server.method; -import ca.uhn.fhir.context.*; +import ca.uhn.fhir.context.ConfigurationException; +import ca.uhn.fhir.context.FhirContext; +import ca.uhn.fhir.context.RuntimeResourceDefinition; import ca.uhn.fhir.i18n.Msg; -import ca.uhn.fhir.rest.annotation.*; +import ca.uhn.fhir.rest.annotation.IdParam; import ca.uhn.fhir.rest.param.ParameterUtil; import ca.uhn.fhir.rest.server.IResourceProvider; import org.hl7.fhir.instance.model.api.IBaseResource; diff --git a/hapi-fhir-server/src/main/java/ca/uhn/fhir/rest/server/method/BaseResourceReturningMethodBinding.java b/hapi-fhir-server/src/main/java/ca/uhn/fhir/rest/server/method/BaseResourceReturningMethodBinding.java index a2084c42b04..5491c57094b 100644 --- a/hapi-fhir-server/src/main/java/ca/uhn/fhir/rest/server/method/BaseResourceReturningMethodBinding.java +++ b/hapi-fhir-server/src/main/java/ca/uhn/fhir/rest/server/method/BaseResourceReturningMethodBinding.java @@ -44,6 +44,8 @@ import ca.uhn.fhir.rest.server.exceptions.InvalidRequestException; import ca.uhn.fhir.rest.server.exceptions.ResourceNotFoundException; import ca.uhn.fhir.rest.server.servlet.ServletRequestDetails; import ca.uhn.fhir.util.ReflectionUtil; +import jakarta.servlet.http.HttpServletRequest; +import jakarta.servlet.http.HttpServletResponse; import org.apache.commons.lang3.Validate; import org.hl7.fhir.instance.model.api.IBaseBundle; import org.hl7.fhir.instance.model.api.IBaseOperationOutcome; @@ -57,8 +59,6 @@ import java.util.Collection; import java.util.Date; import java.util.List; import java.util.Set; -import javax.servlet.http.HttpServletRequest; -import javax.servlet.http.HttpServletResponse; public abstract class BaseResourceReturningMethodBinding extends BaseMethodBinding { protected final ResponseBundleBuilder myResponseBundleBuilder; diff --git a/hapi-fhir-server/src/main/java/ca/uhn/fhir/rest/server/method/ConformanceMethodBinding.java b/hapi-fhir-server/src/main/java/ca/uhn/fhir/rest/server/method/ConformanceMethodBinding.java index 3d6dab05c5d..1b7bfeec4dd 100644 --- a/hapi-fhir-server/src/main/java/ca/uhn/fhir/rest/server/method/ConformanceMethodBinding.java +++ b/hapi-fhir-server/src/main/java/ca/uhn/fhir/rest/server/method/ConformanceMethodBinding.java @@ -39,6 +39,7 @@ import ca.uhn.fhir.rest.server.exceptions.BaseServerResponseException; import ca.uhn.fhir.rest.server.exceptions.MethodNotAllowedException; import ca.uhn.fhir.rest.server.servlet.ServletRequestDetails; import ca.uhn.fhir.system.HapiSystemProperties; +import jakarta.annotation.Nonnull; import org.hl7.fhir.instance.model.api.IBaseConformance; import java.lang.reflect.Method; @@ -49,7 +50,6 @@ import java.util.concurrent.ThreadPoolExecutor; import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicLong; import java.util.concurrent.atomic.AtomicReference; -import javax.annotation.Nonnull; public class ConformanceMethodBinding extends BaseResourceReturningMethodBinding { public static final String CACHE_THREAD_PREFIX = "capabilitystatement-cache-"; diff --git a/hapi-fhir-server/src/main/java/ca/uhn/fhir/rest/server/method/CreateMethodBinding.java b/hapi-fhir-server/src/main/java/ca/uhn/fhir/rest/server/method/CreateMethodBinding.java index c281da37bfd..30956fc268d 100644 --- a/hapi-fhir-server/src/main/java/ca/uhn/fhir/rest/server/method/CreateMethodBinding.java +++ b/hapi-fhir-server/src/main/java/ca/uhn/fhir/rest/server/method/CreateMethodBinding.java @@ -26,13 +26,13 @@ import ca.uhn.fhir.rest.annotation.Create; import ca.uhn.fhir.rest.api.RequestTypeEnum; import ca.uhn.fhir.rest.api.RestOperationTypeEnum; import ca.uhn.fhir.rest.server.exceptions.InvalidRequestException; +import jakarta.annotation.Nonnull; import org.hl7.fhir.instance.model.api.IBaseResource; import org.hl7.fhir.instance.model.api.IIdType; import java.lang.reflect.Method; import java.util.Collections; import java.util.Set; -import javax.annotation.Nonnull; import static org.apache.commons.lang3.StringUtils.isNotBlank; diff --git a/hapi-fhir-server/src/main/java/ca/uhn/fhir/rest/server/method/DeleteMethodBinding.java b/hapi-fhir-server/src/main/java/ca/uhn/fhir/rest/server/method/DeleteMethodBinding.java index ecb730951c7..3539fa13b3c 100644 --- a/hapi-fhir-server/src/main/java/ca/uhn/fhir/rest/server/method/DeleteMethodBinding.java +++ b/hapi-fhir-server/src/main/java/ca/uhn/fhir/rest/server/method/DeleteMethodBinding.java @@ -24,11 +24,11 @@ import ca.uhn.fhir.rest.annotation.Delete; import ca.uhn.fhir.rest.api.RequestTypeEnum; import ca.uhn.fhir.rest.api.RestOperationTypeEnum; import ca.uhn.fhir.rest.api.server.RequestDetails; +import jakarta.annotation.Nonnull; import java.lang.reflect.Method; import java.util.Collections; import java.util.Set; -import javax.annotation.Nonnull; public class DeleteMethodBinding extends BaseOutcomeReturningMethodBindingWithResourceIdButNoResourceBody { diff --git a/hapi-fhir-server/src/main/java/ca/uhn/fhir/rest/server/method/GraphQLMethodBinding.java b/hapi-fhir-server/src/main/java/ca/uhn/fhir/rest/server/method/GraphQLMethodBinding.java index 7d716075a87..3e111bf66d3 100644 --- a/hapi-fhir-server/src/main/java/ca/uhn/fhir/rest/server/method/GraphQLMethodBinding.java +++ b/hapi-fhir-server/src/main/java/ca/uhn/fhir/rest/server/method/GraphQLMethodBinding.java @@ -33,6 +33,9 @@ import ca.uhn.fhir.rest.api.server.ResponseDetails; import ca.uhn.fhir.rest.param.ParameterUtil; import ca.uhn.fhir.rest.server.exceptions.BaseServerResponseException; import ca.uhn.fhir.rest.server.servlet.ServletRequestDetails; +import jakarta.annotation.Nonnull; +import jakarta.servlet.http.HttpServletRequest; +import jakarta.servlet.http.HttpServletResponse; import org.apache.commons.lang3.Validate; import org.hl7.fhir.instance.model.api.IBaseResource; @@ -41,9 +44,6 @@ import java.io.Writer; import java.lang.reflect.Method; import java.util.Collections; import java.util.Set; -import javax.annotation.Nonnull; -import javax.servlet.http.HttpServletRequest; -import javax.servlet.http.HttpServletResponse; public class GraphQLMethodBinding extends OperationMethodBinding { diff --git a/hapi-fhir-server/src/main/java/ca/uhn/fhir/rest/server/method/HistoryMethodBinding.java b/hapi-fhir-server/src/main/java/ca/uhn/fhir/rest/server/method/HistoryMethodBinding.java index 2a7ea30cd82..cc50af9654b 100644 --- a/hapi-fhir-server/src/main/java/ca/uhn/fhir/rest/server/method/HistoryMethodBinding.java +++ b/hapi-fhir-server/src/main/java/ca/uhn/fhir/rest/server/method/HistoryMethodBinding.java @@ -35,6 +35,7 @@ import ca.uhn.fhir.rest.param.ParameterUtil; import ca.uhn.fhir.rest.server.IResourceProvider; import ca.uhn.fhir.rest.server.exceptions.InternalErrorException; import ca.uhn.fhir.rest.server.exceptions.InvalidRequestException; +import jakarta.annotation.Nonnull; import org.apache.commons.lang3.StringUtils; import org.hl7.fhir.instance.model.api.IBaseResource; import org.hl7.fhir.instance.model.api.IPrimitiveType; @@ -43,7 +44,6 @@ import java.lang.reflect.Method; import java.lang.reflect.Modifier; import java.util.Date; import java.util.List; -import javax.annotation.Nonnull; import static org.apache.commons.lang3.StringUtils.isBlank; diff --git a/hapi-fhir-server/src/main/java/ca/uhn/fhir/rest/server/method/MethodUtil.java b/hapi-fhir-server/src/main/java/ca/uhn/fhir/rest/server/method/MethodUtil.java index cdfa6b625d1..a66fe54607c 100644 --- a/hapi-fhir-server/src/main/java/ca/uhn/fhir/rest/server/method/MethodUtil.java +++ b/hapi-fhir-server/src/main/java/ca/uhn/fhir/rest/server/method/MethodUtil.java @@ -27,15 +27,43 @@ import ca.uhn.fhir.interceptor.api.IInterceptorBroadcaster; import ca.uhn.fhir.model.api.Include; import ca.uhn.fhir.model.api.TagList; import ca.uhn.fhir.model.api.annotation.Description; -import ca.uhn.fhir.rest.annotation.*; +import ca.uhn.fhir.rest.annotation.At; +import ca.uhn.fhir.rest.annotation.ConditionalUrlParam; +import ca.uhn.fhir.rest.annotation.Count; +import ca.uhn.fhir.rest.annotation.Elements; +import ca.uhn.fhir.rest.annotation.GraphQLQueryBody; +import ca.uhn.fhir.rest.annotation.GraphQLQueryUrl; +import ca.uhn.fhir.rest.annotation.IdParam; +import ca.uhn.fhir.rest.annotation.IncludeParam; +import ca.uhn.fhir.rest.annotation.Offset; +import ca.uhn.fhir.rest.annotation.Operation; +import ca.uhn.fhir.rest.annotation.OperationParam; +import ca.uhn.fhir.rest.annotation.OptionalParam; +import ca.uhn.fhir.rest.annotation.Patch; +import ca.uhn.fhir.rest.annotation.RawParam; +import ca.uhn.fhir.rest.annotation.RequiredParam; +import ca.uhn.fhir.rest.annotation.ResourceParam; +import ca.uhn.fhir.rest.annotation.ServerBase; +import ca.uhn.fhir.rest.annotation.Since; +import ca.uhn.fhir.rest.annotation.Sort; +import ca.uhn.fhir.rest.annotation.TransactionParam; +import ca.uhn.fhir.rest.annotation.Validate; +import ca.uhn.fhir.rest.api.Constants; +import ca.uhn.fhir.rest.api.EncodingEnum; +import ca.uhn.fhir.rest.api.PatchTypeEnum; +import ca.uhn.fhir.rest.api.SearchContainedModeEnum; +import ca.uhn.fhir.rest.api.SearchTotalModeEnum; +import ca.uhn.fhir.rest.api.SummaryEnum; +import ca.uhn.fhir.rest.api.ValidationModeEnum; import ca.uhn.fhir.rest.api.server.RequestDetails; -import ca.uhn.fhir.rest.api.*; import ca.uhn.fhir.rest.param.binder.CollectionBinder; import ca.uhn.fhir.rest.server.method.OperationParameter.IOperationParamConverter; import ca.uhn.fhir.rest.server.method.ResourceParameter.Mode; import ca.uhn.fhir.rest.server.servlet.ServletRequestDetails; import ca.uhn.fhir.util.ParametersUtil; import ca.uhn.fhir.util.ReflectionUtil; +import jakarta.servlet.ServletRequest; +import jakarta.servlet.ServletResponse; import org.hl7.fhir.instance.model.api.IBaseResource; import org.hl7.fhir.instance.model.api.IPrimitiveType; @@ -45,8 +73,6 @@ import java.util.ArrayList; import java.util.Collection; import java.util.Date; import java.util.List; -import javax.servlet.ServletRequest; -import javax.servlet.ServletResponse; import static org.apache.commons.lang3.StringUtils.isNotBlank; diff --git a/hapi-fhir-server/src/main/java/ca/uhn/fhir/rest/server/method/OperationMethodBinding.java b/hapi-fhir-server/src/main/java/ca/uhn/fhir/rest/server/method/OperationMethodBinding.java index 4addaf9ee80..095eabfc1a7 100644 --- a/hapi-fhir-server/src/main/java/ca/uhn/fhir/rest/server/method/OperationMethodBinding.java +++ b/hapi-fhir-server/src/main/java/ca/uhn/fhir/rest/server/method/OperationMethodBinding.java @@ -38,6 +38,7 @@ import ca.uhn.fhir.rest.param.ParameterUtil; import ca.uhn.fhir.rest.server.exceptions.BaseServerResponseException; import ca.uhn.fhir.rest.server.exceptions.MethodNotAllowedException; import ca.uhn.fhir.util.ParametersUtil; +import jakarta.annotation.Nonnull; import org.apache.commons.lang3.builder.ToStringBuilder; import org.apache.commons.lang3.builder.ToStringStyle; import org.hl7.fhir.instance.model.api.IBase; @@ -51,7 +52,6 @@ import java.util.ArrayList; import java.util.Collections; import java.util.List; import java.util.stream.Collectors; -import javax.annotation.Nonnull; import static org.apache.commons.lang3.StringUtils.isBlank; import static org.apache.commons.lang3.StringUtils.isNotBlank; diff --git a/hapi-fhir-server/src/main/java/ca/uhn/fhir/rest/server/method/PageMethodBinding.java b/hapi-fhir-server/src/main/java/ca/uhn/fhir/rest/server/method/PageMethodBinding.java index ecf91cd264b..38192741daf 100644 --- a/hapi-fhir-server/src/main/java/ca/uhn/fhir/rest/server/method/PageMethodBinding.java +++ b/hapi-fhir-server/src/main/java/ca/uhn/fhir/rest/server/method/PageMethodBinding.java @@ -38,12 +38,12 @@ import ca.uhn.fhir.rest.server.exceptions.InvalidRequestException; import ca.uhn.fhir.rest.server.exceptions.ResourceGoneException; import ca.uhn.fhir.rest.server.servlet.ServletRequestDetails; import ca.uhn.fhir.util.ReflectionUtil; +import jakarta.annotation.Nonnull; import org.hl7.fhir.instance.model.api.IBaseResource; import java.lang.reflect.Method; import java.util.HashSet; import java.util.Set; -import javax.annotation.Nonnull; import static org.apache.commons.lang3.StringUtils.isBlank; import static org.apache.commons.lang3.StringUtils.isNotBlank; diff --git a/hapi-fhir-server/src/main/java/ca/uhn/fhir/rest/server/method/PatchMethodBinding.java b/hapi-fhir-server/src/main/java/ca/uhn/fhir/rest/server/method/PatchMethodBinding.java index 8c2a4318b4f..de528c51648 100644 --- a/hapi-fhir-server/src/main/java/ca/uhn/fhir/rest/server/method/PatchMethodBinding.java +++ b/hapi-fhir-server/src/main/java/ca/uhn/fhir/rest/server/method/PatchMethodBinding.java @@ -28,6 +28,7 @@ import ca.uhn.fhir.rest.api.PatchTypeEnum; import ca.uhn.fhir.rest.api.RequestTypeEnum; import ca.uhn.fhir.rest.api.RestOperationTypeEnum; import ca.uhn.fhir.rest.api.server.RequestDetails; +import jakarta.annotation.Nonnull; import org.hl7.fhir.instance.model.api.IIdType; import java.lang.annotation.Annotation; @@ -36,7 +37,6 @@ import java.util.Arrays; import java.util.Collections; import java.util.ListIterator; import java.util.Set; -import javax.annotation.Nonnull; /** * Base class for an operation that has a resource type but not a resource body in the diff --git a/hapi-fhir-server/src/main/java/ca/uhn/fhir/rest/server/method/ReadMethodBinding.java b/hapi-fhir-server/src/main/java/ca/uhn/fhir/rest/server/method/ReadMethodBinding.java index 098b36c006a..30ad9f13081 100644 --- a/hapi-fhir-server/src/main/java/ca/uhn/fhir/rest/server/method/ReadMethodBinding.java +++ b/hapi-fhir-server/src/main/java/ca/uhn/fhir/rest/server/method/ReadMethodBinding.java @@ -41,6 +41,7 @@ import ca.uhn.fhir.rest.server.exceptions.InternalErrorException; import ca.uhn.fhir.rest.server.exceptions.InvalidRequestException; import ca.uhn.fhir.rest.server.exceptions.NotModifiedException; import ca.uhn.fhir.util.DateUtils; +import jakarta.annotation.Nonnull; import org.apache.commons.lang3.StringUtils; import org.apache.commons.lang3.Validate; import org.hl7.fhir.instance.model.api.IBaseResource; @@ -51,7 +52,6 @@ import java.util.ArrayList; import java.util.Date; import java.util.List; import java.util.Set; -import javax.annotation.Nonnull; import static org.apache.commons.lang3.StringUtils.isNotBlank; diff --git a/hapi-fhir-server/src/main/java/ca/uhn/fhir/rest/server/method/ResourceParameter.java b/hapi-fhir-server/src/main/java/ca/uhn/fhir/rest/server/method/ResourceParameter.java index 4165d16e959..4a9ca1452fb 100644 --- a/hapi-fhir-server/src/main/java/ca/uhn/fhir/rest/server/method/ResourceParameter.java +++ b/hapi-fhir-server/src/main/java/ca/uhn/fhir/rest/server/method/ResourceParameter.java @@ -34,6 +34,7 @@ import ca.uhn.fhir.rest.server.RestfulServerUtils; import ca.uhn.fhir.rest.server.exceptions.InternalErrorException; import ca.uhn.fhir.rest.server.exceptions.InvalidRequestException; import ca.uhn.fhir.util.BinaryUtil; +import jakarta.annotation.Nonnull; import org.apache.commons.io.IOUtils; import org.apache.commons.lang3.Validate; import org.hl7.fhir.instance.model.api.IBaseBinary; @@ -47,7 +48,6 @@ import java.lang.reflect.Method; import java.lang.reflect.Modifier; import java.nio.charset.Charset; import java.util.Collection; -import javax.annotation.Nonnull; import static org.apache.commons.lang3.StringUtils.isBlank; import static org.apache.commons.lang3.StringUtils.isNotBlank; diff --git a/hapi-fhir-server/src/main/java/ca/uhn/fhir/rest/server/method/ResponseBundleBuilder.java b/hapi-fhir-server/src/main/java/ca/uhn/fhir/rest/server/method/ResponseBundleBuilder.java index c294d524962..c17d9281119 100644 --- a/hapi-fhir-server/src/main/java/ca/uhn/fhir/rest/server/method/ResponseBundleBuilder.java +++ b/hapi-fhir-server/src/main/java/ca/uhn/fhir/rest/server/method/ResponseBundleBuilder.java @@ -106,8 +106,11 @@ public class ResponseBundleBuilder { pageSize = pagingCalculatePageSize(requestedPage, server.getPagingProvider()); Integer size = bundleProvider.size(); - numToReturn = - (size == null) ? pageSize : Math.min(pageSize, size.intValue() - theResponseBundleRequest.offset); + if (size == null) { + numToReturn = pageSize; + } else { + numToReturn = Math.min(pageSize, size.intValue() - theResponseBundleRequest.offset); + } resourceList = pagingBuildResourceList(theResponseBundleRequest, bundleProvider, numToReturn, responsePageBuilder); @@ -257,6 +260,7 @@ public class ResponseBundleBuilder { RestfulServerUtils.prettyPrintResponse(server, theResponseBundleRequest.requestDetails), theResponseBundleRequest.bundleType); + // set self link retval.setSelf(theResponseBundleRequest.linkSelf); // determine if we are using offset / uncached pages diff --git a/hapi-fhir-server/src/main/java/ca/uhn/fhir/rest/server/method/ResponsePage.java b/hapi-fhir-server/src/main/java/ca/uhn/fhir/rest/server/method/ResponsePage.java index 5f797ffd435..7bfa3294938 100644 --- a/hapi-fhir-server/src/main/java/ca/uhn/fhir/rest/server/method/ResponsePage.java +++ b/hapi-fhir-server/src/main/java/ca/uhn/fhir/rest/server/method/ResponsePage.java @@ -71,6 +71,16 @@ public class ResponsePage { * even though it will change number of resources returned. */ private final int myOmittedResourceCount; + /** + * This is the total count of requested resources + * (ie, non-omitted, non-_include'd resource count). + * We typically fetch (for offset queries) 1 more than + * we need so we know if there is an additional page + * to fetch. + * But this is determined by the implementers of + * IBundleProvider. + */ + private final int myTotalRequestedResourcesFetched; /** * The bundle provider. @@ -109,6 +119,7 @@ public class ResponsePage { int theNumToReturn, int theIncludedResourceCount, int theOmittedResourceCount, + int theTotalRequestedResourcesFetched, IBundleProvider theBundleProvider) { mySearchId = theSearchId; myResourceList = theResourceList; @@ -116,6 +127,7 @@ public class ResponsePage { myNumToReturn = theNumToReturn; myIncludedResourceCount = theIncludedResourceCount; myOmittedResourceCount = theOmittedResourceCount; + myTotalRequestedResourcesFetched = theTotalRequestedResourcesFetched; myBundleProvider = theBundleProvider; myNumTotalResults = myBundleProvider.size(); @@ -190,24 +202,16 @@ public class ResponsePage { return StringUtils.isNotBlank(myBundleProvider.getNextPageId()); case NONCACHED_OFFSET: if (myNumTotalResults == null) { - /* - * Having a null total results is synonymous with - * having a next link. Once our results are exhausted, - * we will always have a myNumTotalResults value. - * - * Alternatively, if _total=accurate is provided, - * we'll also have a myNumTotalResults value. - */ - return true; + if (hasNextPageWithoutKnowingTotal()) { + return true; + } } else if (myNumTotalResults > myNumToReturn + ObjectUtils.defaultIfNull(myRequestedPage.offset, 0)) { return true; } break; case SAVED_SEARCH: if (myNumTotalResults == null) { - if (myPageSize == myResourceList.size() + myOmittedResourceCount - myIncludedResourceCount) { - // if the size of the resource list - included resources + omitted resources == pagesize - // we have more pages + if (hasNextPageWithoutKnowingTotal()) { return true; } } else if (myResponseBundleRequest.offset + myNumToReturn < myNumTotalResults) { @@ -220,6 +224,53 @@ public class ResponsePage { return false; } + /** + * If myNumTotalResults is null, it typically means we don't + * have an accurate total. + * + * Ie, we're in the middle of a set of pages (of non-named page results), + * and _total=accurate was not passed. + * + * This typically always means that a + * 'next' link definitely exists. + * + * But there are cases where this might not be true: + * * the last page of a search that also has an _include + * query parameter where the total of resources + _include'd + * resources is > the page size expected to be returned. + * * the last page of a search that returns the exact number + * of resources requested + * + * In these case, we must check to see if the returned + * number of *requested* resources. + * If our bundleprovider has fetched > requested, + * we'll know that there are more resources already. + * But if it hasn't, we'll have to check pagesize compared to + * _include'd count, omitted count, and resource count. + */ + private boolean hasNextPageWithoutKnowingTotal() { + // if we have totalRequestedResource count, and it's not equal to pagesize, + // then we can use this, alone, to determine if there are more pages + if (myTotalRequestedResourcesFetched >= 0) { + if (myPageSize < myTotalRequestedResourcesFetched) { + return true; + } + } else { + // otherwise we'll try and determine if there are next links based on the following + // calculation: + // resourceList.size - included resources + omitted resources == pagesize + // -> we (most likely) have more resources + if (myPageSize == myResourceList.size() - myIncludedResourceCount + myOmittedResourceCount) { + ourLog.warn( + "Returning a next page based on calculated resource count." + + " This could be inaccurate if the exact number of resources were fetched is equal to the pagesize requested. " + + " Consider setting ResponseBundleBuilder.setTotalResourcesFetchedRequest after fetching resources."); + return true; + } + } + return false; + } + public void setNextPageIfNecessary(BundleLinks theLinks) { if (hasNextPage()) { String next; @@ -356,9 +407,10 @@ public class ResponsePage { private int myIncludedResourceCount; private int myOmittedResourceCount; private IBundleProvider myBundleProvider; + private int myTotalRequestedResourcesFetched = -1; - public ResponsePageBuilder setToOmittedResourceCount(int theOmittedResourcesCountToAdd) { - myOmittedResourceCount = theOmittedResourcesCountToAdd; + public ResponsePageBuilder setOmittedResourceCount(int theOmittedResourceCount) { + myOmittedResourceCount = theOmittedResourceCount; return this; } @@ -392,6 +444,36 @@ public class ResponsePage { return this; } + public ResponsePageBuilder setTotalRequestedResourcesFetched(int theTotalRequestedResourcesFetched) { + myTotalRequestedResourcesFetched = theTotalRequestedResourcesFetched; + return this; + } + + /** + * Combine this builder with a second buider. + * Useful if a second page is requested, but you do not wish to + * overwrite the current values. + * + * Will not replace searchId, nor IBundleProvider (which should be + * the exact same for any subsequent searches anyways). + * + * Will also not copy pageSize nor numToReturn, as these should be + * the same for any single search result set. + * + * @param theSecondBuilder - a second builder (cannot be this one) + */ + public void combineWith(ResponsePageBuilder theSecondBuilder) { + assert theSecondBuilder != this; // don't want to combine with itself + + if (myTotalRequestedResourcesFetched != -1 && theSecondBuilder.myTotalRequestedResourcesFetched != -1) { + myTotalRequestedResourcesFetched += theSecondBuilder.myTotalRequestedResourcesFetched; + } + + // primitives can always be added + myIncludedResourceCount += theSecondBuilder.myIncludedResourceCount; + myOmittedResourceCount += theSecondBuilder.myOmittedResourceCount; + } + public ResponsePage build() { return new ResponsePage( mySearchId, // search id @@ -400,6 +482,7 @@ public class ResponsePage { myNumToReturn, // num to return myIncludedResourceCount, // included count myOmittedResourceCount, // omitted resources + myTotalRequestedResourcesFetched, // total count of requested resources myBundleProvider // the bundle provider ); } diff --git a/hapi-fhir-server/src/main/java/ca/uhn/fhir/rest/server/method/SearchMethodBinding.java b/hapi-fhir-server/src/main/java/ca/uhn/fhir/rest/server/method/SearchMethodBinding.java index f8987491af3..33307f2d556 100644 --- a/hapi-fhir-server/src/main/java/ca/uhn/fhir/rest/server/method/SearchMethodBinding.java +++ b/hapi-fhir-server/src/main/java/ca/uhn/fhir/rest/server/method/SearchMethodBinding.java @@ -35,6 +35,7 @@ import ca.uhn.fhir.rest.param.QualifierDetails; import ca.uhn.fhir.rest.server.exceptions.InternalErrorException; import ca.uhn.fhir.rest.server.exceptions.InvalidRequestException; import ca.uhn.fhir.util.ParametersUtil; +import jakarta.annotation.Nonnull; import org.apache.commons.lang3.StringUtils; import org.hl7.fhir.instance.model.api.IAnyResource; import org.hl7.fhir.instance.model.api.IBaseResource; @@ -45,7 +46,6 @@ import java.util.HashSet; import java.util.List; import java.util.Set; import java.util.stream.Collectors; -import javax.annotation.Nonnull; import static org.apache.commons.lang3.StringUtils.isBlank; import static org.apache.commons.lang3.StringUtils.isNotBlank; diff --git a/hapi-fhir-server/src/main/java/ca/uhn/fhir/rest/server/method/SearchParameter.java b/hapi-fhir-server/src/main/java/ca/uhn/fhir/rest/server/method/SearchParameter.java index 29c0d40fe1f..7a4316d535e 100644 --- a/hapi-fhir-server/src/main/java/ca/uhn/fhir/rest/server/method/SearchParameter.java +++ b/hapi-fhir-server/src/main/java/ca/uhn/fhir/rest/server/method/SearchParameter.java @@ -19,16 +19,59 @@ */ package ca.uhn.fhir.rest.server.method; -import ca.uhn.fhir.context.*; +import ca.uhn.fhir.context.ConfigurationException; +import ca.uhn.fhir.context.FhirContext; +import ca.uhn.fhir.context.RuntimePrimitiveDatatypeDefinition; import ca.uhn.fhir.i18n.Msg; -import ca.uhn.fhir.model.api.*; +import ca.uhn.fhir.model.api.IQueryParameterAnd; +import ca.uhn.fhir.model.api.IQueryParameterOr; +import ca.uhn.fhir.model.api.IQueryParameterType; import ca.uhn.fhir.model.base.composite.BaseIdentifierDt; import ca.uhn.fhir.model.base.composite.BaseQuantityDt; import ca.uhn.fhir.model.primitive.StringDt; import ca.uhn.fhir.rest.annotation.OptionalParam; -import ca.uhn.fhir.rest.api.*; -import ca.uhn.fhir.rest.param.*; -import ca.uhn.fhir.rest.param.binder.*; +import ca.uhn.fhir.rest.api.Constants; +import ca.uhn.fhir.rest.api.QualifiedParamList; +import ca.uhn.fhir.rest.api.RestSearchParameterTypeEnum; +import ca.uhn.fhir.rest.param.CompositeAndListParam; +import ca.uhn.fhir.rest.param.CompositeOrListParam; +import ca.uhn.fhir.rest.param.CompositeParam; +import ca.uhn.fhir.rest.param.DateAndListParam; +import ca.uhn.fhir.rest.param.DateOrListParam; +import ca.uhn.fhir.rest.param.DateParam; +import ca.uhn.fhir.rest.param.DateRangeParam; +import ca.uhn.fhir.rest.param.HasAndListParam; +import ca.uhn.fhir.rest.param.HasOrListParam; +import ca.uhn.fhir.rest.param.HasParam; +import ca.uhn.fhir.rest.param.NumberAndListParam; +import ca.uhn.fhir.rest.param.NumberOrListParam; +import ca.uhn.fhir.rest.param.NumberParam; +import ca.uhn.fhir.rest.param.QuantityAndListParam; +import ca.uhn.fhir.rest.param.QuantityOrListParam; +import ca.uhn.fhir.rest.param.QuantityParam; +import ca.uhn.fhir.rest.param.ReferenceAndListParam; +import ca.uhn.fhir.rest.param.ReferenceOrListParam; +import ca.uhn.fhir.rest.param.ReferenceParam; +import ca.uhn.fhir.rest.param.SpecialAndListParam; +import ca.uhn.fhir.rest.param.SpecialOrListParam; +import ca.uhn.fhir.rest.param.SpecialParam; +import ca.uhn.fhir.rest.param.StringAndListParam; +import ca.uhn.fhir.rest.param.StringOrListParam; +import ca.uhn.fhir.rest.param.StringParam; +import ca.uhn.fhir.rest.param.TokenAndListParam; +import ca.uhn.fhir.rest.param.TokenOrListParam; +import ca.uhn.fhir.rest.param.TokenParam; +import ca.uhn.fhir.rest.param.UriAndListParam; +import ca.uhn.fhir.rest.param.UriOrListParam; +import ca.uhn.fhir.rest.param.UriParam; +import ca.uhn.fhir.rest.param.binder.CalendarBinder; +import ca.uhn.fhir.rest.param.binder.DateBinder; +import ca.uhn.fhir.rest.param.binder.FhirPrimitiveBinder; +import ca.uhn.fhir.rest.param.binder.IParamBinder; +import ca.uhn.fhir.rest.param.binder.QueryParameterAndBinder; +import ca.uhn.fhir.rest.param.binder.QueryParameterOrBinder; +import ca.uhn.fhir.rest.param.binder.QueryParameterTypeBinder; +import ca.uhn.fhir.rest.param.binder.StringBinder; import ca.uhn.fhir.rest.server.exceptions.InternalErrorException; import ca.uhn.fhir.rest.server.exceptions.InvalidRequestException; import ca.uhn.fhir.util.CollectionUtil; @@ -37,7 +80,16 @@ import org.apache.commons.lang3.builder.ToStringBuilder; import org.hl7.fhir.instance.model.api.IBaseResource; import org.hl7.fhir.instance.model.api.IPrimitiveType; -import java.util.*; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Calendar; +import java.util.Collection; +import java.util.Collections; +import java.util.Date; +import java.util.HashMap; +import java.util.HashSet; +import java.util.List; +import java.util.Set; public class SearchParameter extends BaseQueryParameter { diff --git a/hapi-fhir-server/src/main/java/ca/uhn/fhir/rest/server/method/SortParameter.java b/hapi-fhir-server/src/main/java/ca/uhn/fhir/rest/server/method/SortParameter.java index 0bd2c343c75..98cf3753987 100644 --- a/hapi-fhir-server/src/main/java/ca/uhn/fhir/rest/server/method/SortParameter.java +++ b/hapi-fhir-server/src/main/java/ca/uhn/fhir/rest/server/method/SortParameter.java @@ -19,11 +19,15 @@ */ package ca.uhn.fhir.rest.server.method; -import ca.uhn.fhir.context.*; +import ca.uhn.fhir.context.ConfigurationException; +import ca.uhn.fhir.context.FhirContext; +import ca.uhn.fhir.context.FhirVersionEnum; import ca.uhn.fhir.i18n.Msg; import ca.uhn.fhir.rest.annotation.Sort; +import ca.uhn.fhir.rest.api.Constants; +import ca.uhn.fhir.rest.api.SortOrderEnum; +import ca.uhn.fhir.rest.api.SortSpec; import ca.uhn.fhir.rest.api.server.RequestDetails; -import ca.uhn.fhir.rest.api.*; import ca.uhn.fhir.rest.param.ParameterUtil; import ca.uhn.fhir.rest.server.exceptions.InternalErrorException; import ca.uhn.fhir.rest.server.exceptions.InvalidRequestException; diff --git a/hapi-fhir-server/src/main/java/ca/uhn/fhir/rest/server/method/SummaryEnumParameter.java b/hapi-fhir-server/src/main/java/ca/uhn/fhir/rest/server/method/SummaryEnumParameter.java index ec6f632cba4..00ed63c3457 100644 --- a/hapi-fhir-server/src/main/java/ca/uhn/fhir/rest/server/method/SummaryEnumParameter.java +++ b/hapi-fhir-server/src/main/java/ca/uhn/fhir/rest/server/method/SummaryEnumParameter.java @@ -29,7 +29,10 @@ import ca.uhn.fhir.rest.server.exceptions.InternalErrorException; import ca.uhn.fhir.rest.server.exceptions.InvalidRequestException; import java.lang.reflect.Method; -import java.util.*; +import java.util.Collection; +import java.util.Collections; +import java.util.HashSet; +import java.util.Set; import static org.apache.commons.lang3.StringUtils.isBlank; diff --git a/hapi-fhir-server/src/main/java/ca/uhn/fhir/rest/server/method/TransactionMethodBinding.java b/hapi-fhir-server/src/main/java/ca/uhn/fhir/rest/server/method/TransactionMethodBinding.java index 59e9eb52300..ec5e9c90a90 100644 --- a/hapi-fhir-server/src/main/java/ca/uhn/fhir/rest/server/method/TransactionMethodBinding.java +++ b/hapi-fhir-server/src/main/java/ca/uhn/fhir/rest/server/method/TransactionMethodBinding.java @@ -34,11 +34,11 @@ import ca.uhn.fhir.rest.api.server.RequestDetails; import ca.uhn.fhir.rest.server.exceptions.InternalErrorException; import ca.uhn.fhir.rest.server.exceptions.InvalidRequestException; import ca.uhn.fhir.rest.server.method.TransactionParameter.ParamStyle; +import jakarta.annotation.Nonnull; import org.hl7.fhir.instance.model.api.IBaseResource; import java.lang.reflect.Method; import java.util.List; -import javax.annotation.Nonnull; import static org.apache.commons.lang3.StringUtils.isNotBlank; diff --git a/hapi-fhir-server/src/main/java/ca/uhn/fhir/rest/server/method/UpdateMethodBinding.java b/hapi-fhir-server/src/main/java/ca/uhn/fhir/rest/server/method/UpdateMethodBinding.java index 0b42bc547a2..bce13fa7c3d 100644 --- a/hapi-fhir-server/src/main/java/ca/uhn/fhir/rest/server/method/UpdateMethodBinding.java +++ b/hapi-fhir-server/src/main/java/ca/uhn/fhir/rest/server/method/UpdateMethodBinding.java @@ -28,13 +28,13 @@ import ca.uhn.fhir.rest.api.RestOperationTypeEnum; import ca.uhn.fhir.rest.api.server.RequestDetails; import ca.uhn.fhir.rest.param.ParameterUtil; import ca.uhn.fhir.rest.server.exceptions.InvalidRequestException; +import jakarta.annotation.Nonnull; import org.hl7.fhir.instance.model.api.IBaseResource; import org.hl7.fhir.instance.model.api.IIdType; import java.lang.reflect.Method; import java.util.Collections; import java.util.Set; -import javax.annotation.Nonnull; import static org.apache.commons.lang3.StringUtils.isBlank; import static org.apache.commons.lang3.StringUtils.isNotBlank; diff --git a/hapi-fhir-server/src/main/java/ca/uhn/fhir/rest/server/provider/HashMapResourceProvider.java b/hapi-fhir-server/src/main/java/ca/uhn/fhir/rest/server/provider/HashMapResourceProvider.java index 71e2b8d8be3..a154e878439 100644 --- a/hapi-fhir-server/src/main/java/ca/uhn/fhir/rest/server/provider/HashMapResourceProvider.java +++ b/hapi-fhir-server/src/main/java/ca/uhn/fhir/rest/server/provider/HashMapResourceProvider.java @@ -57,6 +57,7 @@ import ca.uhn.fhir.rest.server.method.ResponsePage; import ca.uhn.fhir.rest.server.servlet.ServletRequestDetails; import ca.uhn.fhir.util.ValidateUtil; import com.google.common.collect.Lists; +import jakarta.annotation.Nonnull; import org.apache.commons.lang3.StringUtils; import org.hl7.fhir.instance.model.api.IBase; import org.hl7.fhir.instance.model.api.IBaseResource; @@ -77,7 +78,6 @@ import java.util.Map; import java.util.TreeMap; import java.util.concurrent.atomic.AtomicLong; import java.util.stream.Collectors; -import javax.annotation.Nonnull; import static java.lang.Math.max; import static java.lang.Math.min; diff --git a/hapi-fhir-server/src/main/java/ca/uhn/fhir/rest/server/provider/IResourceProviderFactoryObserver.java b/hapi-fhir-server/src/main/java/ca/uhn/fhir/rest/server/provider/IResourceProviderFactoryObserver.java index 0256d43ebac..fbaa949074b 100644 --- a/hapi-fhir-server/src/main/java/ca/uhn/fhir/rest/server/provider/IResourceProviderFactoryObserver.java +++ b/hapi-fhir-server/src/main/java/ca/uhn/fhir/rest/server/provider/IResourceProviderFactoryObserver.java @@ -19,8 +19,9 @@ */ package ca.uhn.fhir.rest.server.provider; +import jakarta.annotation.Nonnull; + import java.util.function.Supplier; -import javax.annotation.Nonnull; public interface IResourceProviderFactoryObserver { void update(@Nonnull Supplier theSupplier); diff --git a/hapi-fhir-server/src/main/java/ca/uhn/fhir/rest/server/provider/ProviderConstants.java b/hapi-fhir-server/src/main/java/ca/uhn/fhir/rest/server/provider/ProviderConstants.java index f5ceb74ec62..0d7cd66aaff 100644 --- a/hapi-fhir-server/src/main/java/ca/uhn/fhir/rest/server/provider/ProviderConstants.java +++ b/hapi-fhir-server/src/main/java/ca/uhn/fhir/rest/server/provider/ProviderConstants.java @@ -213,11 +213,6 @@ public class ProviderConstants { */ public static final String OPERATION_REINDEX_RESPONSE_JOB_ID = "jobId"; - /** - * Operation name for the $member-match operation - */ - public static final String OPERATION_MEMBER_MATCH = "$member-match"; - /** * Operation name for the $reindex-terminology operation */ @@ -231,4 +226,13 @@ public class ProviderConstants { */ @Deprecated public static final String PERFORM_REINDEXING_PASS = "$perform-reindexing-pass"; + + /** + * Operation name for the "$export-poll-status" operation + */ + public static final String OPERATION_EXPORT_POLL_STATUS = "$export-poll-status"; + /** + * Operation name for the "$export" operation + */ + public static final String OPERATION_EXPORT = "$export"; } diff --git a/hapi-fhir-server/src/main/java/ca/uhn/fhir/rest/server/provider/ResourceProviderFactory.java b/hapi-fhir-server/src/main/java/ca/uhn/fhir/rest/server/provider/ResourceProviderFactory.java index 1a9fd7dcfc5..9caf01839e3 100644 --- a/hapi-fhir-server/src/main/java/ca/uhn/fhir/rest/server/provider/ResourceProviderFactory.java +++ b/hapi-fhir-server/src/main/java/ca/uhn/fhir/rest/server/provider/ResourceProviderFactory.java @@ -19,13 +19,14 @@ */ package ca.uhn.fhir.rest.server.provider; +import jakarta.annotation.Nonnull; + import java.util.ArrayList; import java.util.Collections; import java.util.HashSet; import java.util.List; import java.util.Set; import java.util.function.Supplier; -import javax.annotation.Nonnull; public class ResourceProviderFactory { private Set myObservers = Collections.synchronizedSet(new HashSet<>()); diff --git a/hapi-fhir-server/src/main/java/ca/uhn/fhir/rest/server/provider/ServerCapabilityStatementProvider.java b/hapi-fhir-server/src/main/java/ca/uhn/fhir/rest/server/provider/ServerCapabilityStatementProvider.java index b7532e20d5d..771d199649f 100644 --- a/hapi-fhir-server/src/main/java/ca/uhn/fhir/rest/server/provider/ServerCapabilityStatementProvider.java +++ b/hapi-fhir-server/src/main/java/ca/uhn/fhir/rest/server/provider/ServerCapabilityStatementProvider.java @@ -52,6 +52,9 @@ import ca.uhn.fhir.util.ExtensionUtil; import ca.uhn.fhir.util.FhirTerser; import ca.uhn.fhir.util.HapiExtensions; import com.google.common.collect.TreeMultimap; +import jakarta.annotation.Nonnull; +import jakarta.servlet.ServletContext; +import jakarta.servlet.http.HttpServletRequest; import org.apache.commons.text.WordUtils; import org.hl7.fhir.instance.model.api.IBase; import org.hl7.fhir.instance.model.api.IBaseConformance; @@ -74,9 +77,6 @@ import java.util.Set; import java.util.TreeSet; import java.util.UUID; import java.util.stream.Collectors; -import javax.annotation.Nonnull; -import javax.servlet.ServletContext; -import javax.servlet.http.HttpServletRequest; import static org.apache.commons.lang3.StringUtils.defaultString; import static org.apache.commons.lang3.StringUtils.isBlank; @@ -536,12 +536,27 @@ public class ServerCapabilityStatementProvider implements IServerConformanceProv } } + maybeAddBulkDataDeclarationToConformingToIg(terser, retVal, configuration.getServerBindings()); + postProcessRest(terser, rest); postProcess(terser, retVal); return retVal; } + private void maybeAddBulkDataDeclarationToConformingToIg( + FhirTerser theTerser, IBaseConformance theBaseConformance, List theServerBindings) { + boolean bulkExportEnabled = theServerBindings.stream() + .filter(OperationMethodBinding.class::isInstance) + .map(OperationMethodBinding.class::cast) + .map(OperationMethodBinding::getName) + .anyMatch(ProviderConstants.OPERATION_EXPORT::equals); + + if (bulkExportEnabled) { + theTerser.addElement(theBaseConformance, "instantiates", Constants.BULK_DATA_ACCESS_IG_URL); + } + } + /** * * @param theSearchParam diff --git a/hapi-fhir-server/src/main/java/ca/uhn/fhir/rest/server/servlet/ServletRequestDetails.java b/hapi-fhir-server/src/main/java/ca/uhn/fhir/rest/server/servlet/ServletRequestDetails.java index fead7ac698c..0879221f341 100644 --- a/hapi-fhir-server/src/main/java/ca/uhn/fhir/rest/server/servlet/ServletRequestDetails.java +++ b/hapi-fhir-server/src/main/java/ca/uhn/fhir/rest/server/servlet/ServletRequestDetails.java @@ -28,6 +28,9 @@ import ca.uhn.fhir.rest.api.server.RequestDetails; import ca.uhn.fhir.rest.server.RestfulServer; import ca.uhn.fhir.rest.server.RestfulServerUtils; import ca.uhn.fhir.rest.server.exceptions.InvalidRequestException; +import jakarta.annotation.Nonnull; +import jakarta.servlet.http.HttpServletRequest; +import jakarta.servlet.http.HttpServletResponse; import org.apache.commons.io.IOUtils; import org.apache.commons.lang3.Validate; @@ -45,9 +48,6 @@ import java.util.List; import java.util.Map; import java.util.StringTokenizer; import java.util.zip.GZIPInputStream; -import javax.annotation.Nonnull; -import javax.servlet.http.HttpServletRequest; -import javax.servlet.http.HttpServletResponse; import static org.apache.commons.lang3.StringUtils.isNotBlank; import static org.apache.commons.lang3.StringUtils.trim; diff --git a/hapi-fhir-server/src/main/java/ca/uhn/fhir/rest/server/servlet/ServletRestfulResponse.java b/hapi-fhir-server/src/main/java/ca/uhn/fhir/rest/server/servlet/ServletRestfulResponse.java index 7b991a17c7a..26105e5f548 100644 --- a/hapi-fhir-server/src/main/java/ca/uhn/fhir/rest/server/servlet/ServletRestfulResponse.java +++ b/hapi-fhir-server/src/main/java/ca/uhn/fhir/rest/server/servlet/ServletRestfulResponse.java @@ -22,6 +22,9 @@ package ca.uhn.fhir.rest.server.servlet; import ca.uhn.fhir.rest.api.Constants; import ca.uhn.fhir.rest.server.BaseRestfulResponse; import ca.uhn.fhir.util.IoUtil; +import jakarta.annotation.Nonnull; +import jakarta.servlet.ServletOutputStream; +import jakarta.servlet.http.HttpServletResponse; import org.apache.commons.lang3.StringUtils; import org.apache.commons.lang3.Validate; @@ -34,9 +37,6 @@ import java.nio.charset.StandardCharsets; import java.util.List; import java.util.Map.Entry; import java.util.zip.GZIPOutputStream; -import javax.annotation.Nonnull; -import javax.servlet.ServletOutputStream; -import javax.servlet.http.HttpServletResponse; public class ServletRestfulResponse extends BaseRestfulResponse { diff --git a/hapi-fhir-server/src/main/java/ca/uhn/fhir/rest/server/servlet/ServletSubRequestDetails.java b/hapi-fhir-server/src/main/java/ca/uhn/fhir/rest/server/servlet/ServletSubRequestDetails.java index e57ea6b157d..77effb67ab4 100644 --- a/hapi-fhir-server/src/main/java/ca/uhn/fhir/rest/server/servlet/ServletSubRequestDetails.java +++ b/hapi-fhir-server/src/main/java/ca/uhn/fhir/rest/server/servlet/ServletSubRequestDetails.java @@ -19,12 +19,13 @@ */ package ca.uhn.fhir.rest.server.servlet; +import jakarta.servlet.http.HttpServletRequest; +import jakarta.servlet.http.HttpServletResponse; + import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; -import javax.servlet.http.HttpServletRequest; -import javax.servlet.http.HttpServletResponse; public class ServletSubRequestDetails extends ServletRequestDetails { diff --git a/hapi-fhir-server/src/main/java/ca/uhn/fhir/rest/server/tenant/ITenantIdentificationStrategy.java b/hapi-fhir-server/src/main/java/ca/uhn/fhir/rest/server/tenant/ITenantIdentificationStrategy.java index adac4d18ad9..356fc12cda6 100644 --- a/hapi-fhir-server/src/main/java/ca/uhn/fhir/rest/server/tenant/ITenantIdentificationStrategy.java +++ b/hapi-fhir-server/src/main/java/ca/uhn/fhir/rest/server/tenant/ITenantIdentificationStrategy.java @@ -26,7 +26,7 @@ public interface ITenantIdentificationStrategy { /** * Implementations should use this method to determine the tenant ID - * based on the incoming request andand populate it in the + * based on the incoming request and populate it in the * {@link RequestDetails#setTenantId(String)}. * * @param theUrlPathTokenizer The tokenizer which is used to parse the request path @@ -39,4 +39,13 @@ public interface ITenantIdentificationStrategy { * if necessary based on the tenant ID */ String massageServerBaseUrl(String theFhirServerBase, RequestDetails theRequestDetails); + + /** + * Implementations may use this method to resolve relative URL based on the tenant ID from RequestDetails. + * + * @param theRelativeUrl URL that only includes the path, e.g. "Patient/123" + * @param theRequestDetails The request details object which can be used to access tenant ID + * @return Resolved relative URL that starts with tenant ID (if tenant ID present in RequestDetails). Example: "TENANT-A/Patient/123". + */ + String resolveRelativeUrl(String theRelativeUrl, RequestDetails theRequestDetails); } diff --git a/hapi-fhir-server/src/main/java/ca/uhn/fhir/rest/server/tenant/UrlBaseTenantIdentificationStrategy.java b/hapi-fhir-server/src/main/java/ca/uhn/fhir/rest/server/tenant/UrlBaseTenantIdentificationStrategy.java index affeaea0a12..344a515a583 100644 --- a/hapi-fhir-server/src/main/java/ca/uhn/fhir/rest/server/tenant/UrlBaseTenantIdentificationStrategy.java +++ b/hapi-fhir-server/src/main/java/ca/uhn/fhir/rest/server/tenant/UrlBaseTenantIdentificationStrategy.java @@ -69,7 +69,7 @@ public class UrlBaseTenantIdentificationStrategy implements ITenantIdentificatio tenantId = defaultIfBlank(theUrlPathTokenizer.peek(), null); // If it's "metadata" or starts with "$", use DEFAULT partition and don't consume this token: - if (tenantId != null && (tenantId.equals("metadata") || tenantId.startsWith("$"))) { + if (tenantId != null && (tenantId.equals("metadata") || isOperation(tenantId))) { tenantId = "DEFAULT"; theRequestDetails.setTenantId(tenantId); ourLog.trace("No tenant ID found for metadata or system request; using DEFAULT."); @@ -94,6 +94,10 @@ public class UrlBaseTenantIdentificationStrategy implements ITenantIdentificatio } } + private boolean isOperation(String theToken) { + return theToken.startsWith("$"); + } + @Override public String massageServerBaseUrl(String theFhirServerBase, RequestDetails theRequestDetails) { String result = theFhirServerBase; @@ -102,4 +106,29 @@ public class UrlBaseTenantIdentificationStrategy implements ITenantIdentificatio } return result; } + + @Override + public String resolveRelativeUrl(String theRelativeUrl, RequestDetails theRequestDetails) { + UrlPathTokenizer tokenizer = new UrlPathTokenizer(theRelativeUrl); + // there is no more tokens in the URL - skip url resolution + if (!tokenizer.hasMoreTokens() || tokenizer.peek() == null) { + return theRelativeUrl; + } + String nextToken = tokenizer.peek(); + // there is no tenant ID in parent request details or tenant ID is already present in URL - skip url resolution + if (theRequestDetails.getTenantId() == null || nextToken.equals(theRequestDetails.getTenantId())) { + return theRelativeUrl; + } + + // token is Resource type or operation - adding tenant ID from parent request details + if (isResourceType(nextToken, theRequestDetails) || isOperation(nextToken)) { + return theRequestDetails.getTenantId() + "/" + theRelativeUrl; + } else { + return theRelativeUrl; + } + } + + private boolean isResourceType(String token, RequestDetails theRequestDetails) { + return theRequestDetails.getFhirContext().getResourceTypes().stream().anyMatch(type -> type.equals(token)); + } } diff --git a/hapi-fhir-server/src/main/java/ca/uhn/fhir/rest/server/util/BaseServerCapabilityStatementProvider.java b/hapi-fhir-server/src/main/java/ca/uhn/fhir/rest/server/util/BaseServerCapabilityStatementProvider.java index 612f3afca2c..fea0f63a29e 100644 --- a/hapi-fhir-server/src/main/java/ca/uhn/fhir/rest/server/util/BaseServerCapabilityStatementProvider.java +++ b/hapi-fhir-server/src/main/java/ca/uhn/fhir/rest/server/util/BaseServerCapabilityStatementProvider.java @@ -22,10 +22,9 @@ package ca.uhn.fhir.rest.server.util; import ca.uhn.fhir.rest.api.server.RequestDetails; import ca.uhn.fhir.rest.server.RestfulServer; import ca.uhn.fhir.rest.server.RestfulServerConfiguration; +import jakarta.annotation.Nullable; import org.apache.commons.lang3.Validate; -import javax.annotation.Nullable; - public abstract class BaseServerCapabilityStatementProvider { private RestfulServerConfiguration myConfiguration; diff --git a/hapi-fhir-server/src/main/java/ca/uhn/fhir/rest/server/util/CompositeInterceptorBroadcaster.java b/hapi-fhir-server/src/main/java/ca/uhn/fhir/rest/server/util/CompositeInterceptorBroadcaster.java index c33928452f1..5cfd8dfb4a5 100644 --- a/hapi-fhir-server/src/main/java/ca/uhn/fhir/rest/server/util/CompositeInterceptorBroadcaster.java +++ b/hapi-fhir-server/src/main/java/ca/uhn/fhir/rest/server/util/CompositeInterceptorBroadcaster.java @@ -23,8 +23,7 @@ import ca.uhn.fhir.interceptor.api.HookParams; import ca.uhn.fhir.interceptor.api.IInterceptorBroadcaster; import ca.uhn.fhir.interceptor.api.Pointcut; import ca.uhn.fhir.rest.api.server.RequestDetails; - -import javax.annotation.Nullable; +import jakarta.annotation.Nullable; public class CompositeInterceptorBroadcaster { diff --git a/hapi-fhir-server/src/main/java/ca/uhn/fhir/rest/server/util/FhirContextSearchParamRegistry.java b/hapi-fhir-server/src/main/java/ca/uhn/fhir/rest/server/util/FhirContextSearchParamRegistry.java index a98872fec87..b321574bc5d 100644 --- a/hapi-fhir-server/src/main/java/ca/uhn/fhir/rest/server/util/FhirContextSearchParamRegistry.java +++ b/hapi-fhir-server/src/main/java/ca/uhn/fhir/rest/server/util/FhirContextSearchParamRegistry.java @@ -25,6 +25,8 @@ import ca.uhn.fhir.context.RuntimeResourceDefinition; import ca.uhn.fhir.context.RuntimeSearchParam; import ca.uhn.fhir.context.phonetic.IPhoneticEncoder; import ca.uhn.fhir.i18n.Msg; +import jakarta.annotation.Nonnull; +import jakarta.annotation.Nullable; import org.apache.commons.lang3.Validate; import org.hl7.fhir.instance.model.api.IIdType; @@ -32,8 +34,6 @@ import java.util.ArrayList; import java.util.List; import java.util.Optional; import java.util.Set; -import javax.annotation.Nonnull; -import javax.annotation.Nullable; public class FhirContextSearchParamRegistry implements ISearchParamRegistry { diff --git a/hapi-fhir-server/src/main/java/ca/uhn/fhir/rest/server/util/ISearchParamRegistry.java b/hapi-fhir-server/src/main/java/ca/uhn/fhir/rest/server/util/ISearchParamRegistry.java index d37ed620067..fe43823a31a 100644 --- a/hapi-fhir-server/src/main/java/ca/uhn/fhir/rest/server/util/ISearchParamRegistry.java +++ b/hapi-fhir-server/src/main/java/ca/uhn/fhir/rest/server/util/ISearchParamRegistry.java @@ -25,6 +25,7 @@ import ca.uhn.fhir.context.phonetic.IPhoneticEncoder; import ca.uhn.fhir.i18n.Msg; import ca.uhn.fhir.rest.api.Constants; import ca.uhn.fhir.rest.server.exceptions.InvalidRequestException; +import jakarta.annotation.Nullable; import org.hl7.fhir.instance.model.api.IAnyResource; import org.hl7.fhir.instance.model.api.IIdType; @@ -34,7 +35,6 @@ import java.util.List; import java.util.Optional; import java.util.Set; import java.util.TreeSet; -import javax.annotation.Nullable; // TODO: JA remove default methods public interface ISearchParamRegistry { diff --git a/hapi-fhir-server/src/main/java/ca/uhn/fhir/rest/server/util/ITestingUiClientFactory.java b/hapi-fhir-server/src/main/java/ca/uhn/fhir/rest/server/util/ITestingUiClientFactory.java index 7692cd61767..9058a0cd0d9 100644 --- a/hapi-fhir-server/src/main/java/ca/uhn/fhir/rest/server/util/ITestingUiClientFactory.java +++ b/hapi-fhir-server/src/main/java/ca/uhn/fhir/rest/server/util/ITestingUiClientFactory.java @@ -21,8 +21,7 @@ package ca.uhn.fhir.rest.server.util; import ca.uhn.fhir.context.FhirContext; import ca.uhn.fhir.rest.client.api.IGenericClient; - -import javax.servlet.http.HttpServletRequest; +import jakarta.servlet.http.HttpServletRequest; /** * This interface isn't used by hapi-fhir-base, but is used by the diff --git a/hapi-fhir-server/src/main/java/ca/uhn/fhir/rest/server/util/NarrativeUtil.java b/hapi-fhir-server/src/main/java/ca/uhn/fhir/rest/server/util/NarrativeUtil.java index bb7b89c0516..eb7ab4d9bfb 100644 --- a/hapi-fhir-server/src/main/java/ca/uhn/fhir/rest/server/util/NarrativeUtil.java +++ b/hapi-fhir-server/src/main/java/ca/uhn/fhir/rest/server/util/NarrativeUtil.java @@ -47,10 +47,16 @@ public class NarrativeUtil { *
  • All other elements and attributes are removed
  • * */ - public static String sanitize(String theHtml) { - XhtmlNode node = new XhtmlNode(); - node.setValueAsString(theHtml); - return sanitize(node).getValueAsString(); + public static String sanitizeHtmlFragment(String theHtml) { + PolicyFactory idPolicy = + new HtmlPolicyBuilder().allowAttributes("id").globally().toFactory(); + + PolicyFactory policy = Sanitizers.FORMATTING + .and(Sanitizers.BLOCKS) + .and(Sanitizers.TABLES) + .and(Sanitizers.STYLES) + .and(idPolicy); + return policy.sanitize(theHtml); } /** @@ -70,15 +76,7 @@ public class NarrativeUtil { public static XhtmlNode sanitize(XhtmlNode theNode) { String html = theNode.getValueAsString(); - PolicyFactory idPolicy = - new HtmlPolicyBuilder().allowAttributes("id").globally().toFactory(); - - PolicyFactory policy = Sanitizers.FORMATTING - .and(Sanitizers.BLOCKS) - .and(Sanitizers.TABLES) - .and(Sanitizers.STYLES) - .and(idPolicy); - String safeHTML = policy.sanitize(html); + String safeHTML = sanitizeHtmlFragment(html); XhtmlNode retVal = new XhtmlNode(); retVal.setValueAsString(safeHTML); diff --git a/hapi-fhir-server/src/main/java/ca/uhn/fhir/rest/server/util/ServletRequestUtil.java b/hapi-fhir-server/src/main/java/ca/uhn/fhir/rest/server/util/ServletRequestUtil.java index 44ce54a368a..24bafe71c62 100644 --- a/hapi-fhir-server/src/main/java/ca/uhn/fhir/rest/server/util/ServletRequestUtil.java +++ b/hapi-fhir-server/src/main/java/ca/uhn/fhir/rest/server/util/ServletRequestUtil.java @@ -63,6 +63,7 @@ public class ServletRequestUtil { requestDetails.setRequestPath(url); requestDetails.setFhirServerBase(theRequestDetails.getFhirServerBase()); + requestDetails.setTenantId(theRequestDetails.getTenantId()); theRequestDetails.getServer().populateRequestDetailsFromRequestPath(requestDetails, url); return requestDetails; diff --git a/hapi-fhir-server/src/test/java/ca/uhn/fhir/rest/api/server/method/ResponsePageTest.java b/hapi-fhir-server/src/test/java/ca/uhn/fhir/rest/api/server/method/ResponsePageTest.java index 0546cdd8cd5..b5e00d6839b 100644 --- a/hapi-fhir-server/src/test/java/ca/uhn/fhir/rest/api/server/method/ResponsePageTest.java +++ b/hapi-fhir-server/src/test/java/ca/uhn/fhir/rest/api/server/method/ResponsePageTest.java @@ -161,17 +161,24 @@ public class ResponsePageTest { */ @ParameterizedTest @CsvSource({ - "true,false,true", - "true,true,true", - "false,false,false", - "false,true,false", - "false,false,true", - "false,true,true" + "true,false,true,true", + "true,true,true,true", + "false,false,false,true", + "false,true,false,true", + "false,false,true,true", + "false,true,true,true", + "true,false,true,false", + "true,true,true,false", + "false,false,false,false", + "false,true,false,false", + "false,false,true,false", + "false,true,true,false" }) public void nonCachedOffsetPaging_setsNextPreviousLinks_test( boolean theNumTotalResultsIsNull, boolean theHasPreviousBoolean, - boolean theHasNextBoolean + boolean theHasNextBoolean, + boolean theHasTotalRequestedCountBool ) { // setup myBundleBuilder @@ -193,6 +200,11 @@ public class ResponsePageTest { } else { when(myBundleProvider.size()) .thenReturn(null); + if (theHasTotalRequestedCountBool) { + myBundleBuilder.setTotalRequestedResourcesFetched(11); // 1 more than pagesize + } else { + myBundleBuilder.setPageSize(10); + } } RequestedPage requestedPage = new RequestedPage( @@ -215,19 +227,28 @@ public class ResponsePageTest { @ParameterizedTest @CsvSource({ - "true,false,false", - "true,true,false", - "true,false,true", - "true,true,true", - "false,false,false", - "false,true,false", - "false,false,true", - "false,true,true" + "true,false,false,true", + "true,true,false,true", + "true,false,true,true", + "true,true,true,true", + "false,false,false,true", + "false,true,false,true", + "false,false,true,true", + "false,true,true,true", + "true,false,false,false", + "true,true,false,false", + "true,false,true,false", + "true,true,true,false", + "false,false,false,false", + "false,true,false,false", + "false,false,true,false", + "false,true,true,false" }) public void savedSearch_setsNextPreviousLinks_test( boolean theNumTotalResultsIsNull, boolean theHasPreviousBoolean, - boolean theHasNextBoolean + boolean theHasNextBoolean, + boolean theHasTotalRequestedFetched ) { // setup int pageSize = myList.size(); @@ -255,6 +276,12 @@ public class ResponsePageTest { if (!theHasNextBoolean) { myBundleBuilder.setNumToReturn(pageSize + offset + includeResourceCount); } + } else if (theHasTotalRequestedFetched) { + if (theHasNextBoolean) { + myBundleBuilder.setTotalRequestedResourcesFetched(pageSize + 1); // 1 more than page size + } else { + myBundleBuilder.setTotalRequestedResourcesFetched(pageSize); + } } // when diff --git a/hapi-fhir-server/src/test/java/ca/uhn/fhir/rest/server/IncomingRequestAddressStrategyTest.java b/hapi-fhir-server/src/test/java/ca/uhn/fhir/rest/server/IncomingRequestAddressStrategyTest.java index d80f8dcf6f3..723aa57633b 100644 --- a/hapi-fhir-server/src/test/java/ca/uhn/fhir/rest/server/IncomingRequestAddressStrategyTest.java +++ b/hapi-fhir-server/src/test/java/ca/uhn/fhir/rest/server/IncomingRequestAddressStrategyTest.java @@ -2,7 +2,7 @@ package ca.uhn.fhir.rest.server; import org.junit.jupiter.api.Test; -import static org.junit.jupiter.api.Assertions.*; +import static org.junit.jupiter.api.Assertions.assertNull; public class IncomingRequestAddressStrategyTest { diff --git a/hapi-fhir-server/src/test/java/ca/uhn/fhir/rest/server/RestfulServerUtilsTest.java b/hapi-fhir-server/src/test/java/ca/uhn/fhir/rest/server/RestfulServerUtilsTest.java index 9b8a159663b..d3399c06bb9 100644 --- a/hapi-fhir-server/src/test/java/ca/uhn/fhir/rest/server/RestfulServerUtilsTest.java +++ b/hapi-fhir-server/src/test/java/ca/uhn/fhir/rest/server/RestfulServerUtilsTest.java @@ -1,13 +1,20 @@ package ca.uhn.fhir.rest.server; -import ca.uhn.fhir.rest.api.*; +import ca.uhn.fhir.rest.api.Constants; +import ca.uhn.fhir.rest.api.DeleteCascadeModeEnum; +import ca.uhn.fhir.rest.api.PreferHandlingEnum; +import ca.uhn.fhir.rest.api.PreferHeader; +import ca.uhn.fhir.rest.api.PreferReturnEnum; import ca.uhn.fhir.rest.api.server.RequestDetails; import ca.uhn.fhir.rest.server.exceptions.InvalidRequestException; import ca.uhn.fhir.rest.server.servlet.ServletRequestDetails; +import org.apache.commons.lang3.StringUtils; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.ExtendWith; import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.Arguments; import org.junit.jupiter.params.provider.CsvSource; +import org.junit.jupiter.params.provider.MethodSource; import org.mockito.Mock; import org.mockito.junit.jupiter.MockitoExtension; @@ -15,13 +22,19 @@ import java.util.Arrays; import java.util.HashMap; import java.util.List; import java.util.Map; +import java.util.stream.Stream; import static ca.uhn.fhir.rest.api.RequestTypeEnum.GET; +import static ca.uhn.fhir.rest.api.RequestTypeEnum.POST; import static org.apache.commons.lang3.StringUtils.isNotBlank; +import static org.apache.http.util.TextUtils.isBlank; import static org.hamcrest.CoreMatchers.is; import static org.hamcrest.MatcherAssert.assertThat; import static org.hamcrest.Matchers.containsString; -import static org.junit.jupiter.api.Assertions.*; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertTrue; +import static org.junit.jupiter.api.Assertions.fail; import static org.mockito.Mockito.when; @ExtendWith(MockitoExtension.class) @@ -149,6 +162,33 @@ public class RestfulServerUtilsTest { //Then assertThat(linkSelfWithoutGivenParameters, is(containsString("http://localhost:8000/$my-operation?"))); assertThat(linkSelfWithoutGivenParameters, is(containsString("_format=json"))); + } + @ParameterizedTest + @MethodSource("testParameters") + public void testCreateSelfLinks_withDifferentResourcePathAndTenantId(String theServerBaseUrl, String theRequestPath, + String theTenantId, String theExpectedUrl) { + //When + ServletRequestDetails servletRequestDetails = new ServletRequestDetails(); + servletRequestDetails.setRequestType(POST); + servletRequestDetails.setTenantId(StringUtils.defaultString(theTenantId)); + servletRequestDetails.setRequestPath(StringUtils.defaultString(theRequestPath)); + + //Then + String linkSelfWithoutGivenParameters = RestfulServerUtils.createLinkSelfWithoutGivenParameters(theServerBaseUrl, servletRequestDetails, null); + //Test + assertEquals(theExpectedUrl, linkSelfWithoutGivenParameters); + } + static Stream testParameters(){ + return Stream.of( + Arguments.of("http://localhost:8000/Partition-B","" ,"Partition-B","http://localhost:8000/Partition-B"), + Arguments.of("http://localhost:8000/Partition-B","Partition-B" ,"Partition-B","http://localhost:8000/Partition-B"), + Arguments.of("http://localhost:8000/Partition-B","Partition-B/Patient" ,"Partition-B","http://localhost:8000/Partition-B/Patient"), + Arguments.of("http://localhost:8000/Partition-B","Partition-B/$my-operation" ,"Partition-B","http://localhost:8000/Partition-B/$my-operation"), + Arguments.of("http://localhost:8000","","","http://localhost:8000"), + Arguments.of("", "","",""), + Arguments.of("http://localhost:8000","Patient","","http://localhost:8000/Patient"), + Arguments.of("http://localhost:8000/Patient","","","http://localhost:8000/Patient") + ); } } diff --git a/hapi-fhir-server/src/test/java/ca/uhn/fhir/rest/server/SimpleBundleProviderTest.java b/hapi-fhir-server/src/test/java/ca/uhn/fhir/rest/server/SimpleBundleProviderTest.java index a80f58d2bba..07d3f5e2e66 100644 --- a/hapi-fhir-server/src/test/java/ca/uhn/fhir/rest/server/SimpleBundleProviderTest.java +++ b/hapi-fhir-server/src/test/java/ca/uhn/fhir/rest/server/SimpleBundleProviderTest.java @@ -2,7 +2,7 @@ package ca.uhn.fhir.rest.server; import org.junit.jupiter.api.Test; -import static org.junit.jupiter.api.Assertions.*; +import static org.junit.jupiter.api.Assertions.assertEquals; public class SimpleBundleProviderTest { diff --git a/hapi-fhir-server/src/test/java/ca/uhn/fhir/rest/server/interceptor/ConfigLoaderTest.java b/hapi-fhir-server/src/test/java/ca/uhn/fhir/rest/server/interceptor/ConfigLoaderTest.java index fe377df7773..9694eccae74 100644 --- a/hapi-fhir-server/src/test/java/ca/uhn/fhir/rest/server/interceptor/ConfigLoaderTest.java +++ b/hapi-fhir-server/src/test/java/ca/uhn/fhir/rest/server/interceptor/ConfigLoaderTest.java @@ -5,7 +5,9 @@ import org.junit.jupiter.api.Test; import java.util.Map; import java.util.Properties; -import static org.junit.jupiter.api.Assertions.*; +import static org.junit.jupiter.api.Assertions.assertNotNull; +import static org.junit.jupiter.api.Assertions.assertTrue; +import static org.junit.jupiter.api.Assertions.fail; class ConfigLoaderTest { diff --git a/hapi-fhir-server/src/test/java/ca/uhn/fhir/rest/server/mail/MailSvcIT.java b/hapi-fhir-server/src/test/java/ca/uhn/fhir/rest/server/mail/MailSvcIT.java index 87f039401cb..9e6e2099520 100644 --- a/hapi-fhir-server/src/test/java/ca/uhn/fhir/rest/server/mail/MailSvcIT.java +++ b/hapi-fhir-server/src/test/java/ca/uhn/fhir/rest/server/mail/MailSvcIT.java @@ -10,7 +10,7 @@ import org.simplejavamail.MailException; import org.simplejavamail.api.email.Email; import org.simplejavamail.email.EmailBuilder; -import javax.annotation.Nonnull; +import jakarta.annotation.Nonnull; import javax.mail.internet.MimeMessage; import java.util.Arrays; import java.util.List; diff --git a/hapi-fhir-server/src/test/java/ca/uhn/fhir/rest/server/method/ConformanceMethodBindingTest.java b/hapi-fhir-server/src/test/java/ca/uhn/fhir/rest/server/method/ConformanceMethodBindingTest.java index af3c956d1d8..2648b618be3 100644 --- a/hapi-fhir-server/src/test/java/ca/uhn/fhir/rest/server/method/ConformanceMethodBindingTest.java +++ b/hapi-fhir-server/src/test/java/ca/uhn/fhir/rest/server/method/ConformanceMethodBindingTest.java @@ -16,7 +16,7 @@ import org.mockito.Answers; import org.mockito.Mock; import org.mockito.junit.jupiter.MockitoExtension; -import javax.servlet.http.HttpServletRequest; +import jakarta.servlet.http.HttpServletRequest; import java.lang.reflect.Method; import static org.mockito.ArgumentMatchers.any; diff --git a/hapi-fhir-server/src/test/java/ca/uhn/fhir/rest/server/method/MethodMatchEnumTest.java b/hapi-fhir-server/src/test/java/ca/uhn/fhir/rest/server/method/MethodMatchEnumTest.java index a91acb56545..2cfa9aae844 100644 --- a/hapi-fhir-server/src/test/java/ca/uhn/fhir/rest/server/method/MethodMatchEnumTest.java +++ b/hapi-fhir-server/src/test/java/ca/uhn/fhir/rest/server/method/MethodMatchEnumTest.java @@ -2,7 +2,7 @@ package ca.uhn.fhir.rest.server.method; import org.junit.jupiter.api.Test; -import static org.junit.jupiter.api.Assertions.*; +import static org.junit.jupiter.api.Assertions.assertEquals; public class MethodMatchEnumTest { diff --git a/hapi-fhir-server/src/test/java/ca/uhn/fhir/rest/server/servlet/ServletRequestDetailsTest.java b/hapi-fhir-server/src/test/java/ca/uhn/fhir/rest/server/servlet/ServletRequestDetailsTest.java index 799a7f77482..3eeb771b22f 100644 --- a/hapi-fhir-server/src/test/java/ca/uhn/fhir/rest/server/servlet/ServletRequestDetailsTest.java +++ b/hapi-fhir-server/src/test/java/ca/uhn/fhir/rest/server/servlet/ServletRequestDetailsTest.java @@ -3,7 +3,7 @@ package ca.uhn.fhir.rest.server.servlet; import ca.uhn.fhir.rest.api.Constants; import org.junit.jupiter.api.Test; -import javax.servlet.http.HttpServletRequest; +import jakarta.servlet.http.HttpServletRequest; import static org.junit.jupiter.api.Assertions.assertFalse; import static org.junit.jupiter.api.Assertions.assertTrue; diff --git a/hapi-fhir-server/src/test/java/ca/uhn/fhir/rest/server/servlet/ServletRestfulResponseTest.java b/hapi-fhir-server/src/test/java/ca/uhn/fhir/rest/server/servlet/ServletRestfulResponseTest.java index bb28d105053..bb6e6b97979 100644 --- a/hapi-fhir-server/src/test/java/ca/uhn/fhir/rest/server/servlet/ServletRestfulResponseTest.java +++ b/hapi-fhir-server/src/test/java/ca/uhn/fhir/rest/server/servlet/ServletRestfulResponseTest.java @@ -10,7 +10,7 @@ import org.mockito.Mock; import org.mockito.Mockito; import org.mockito.junit.jupiter.MockitoExtension; -import javax.servlet.http.HttpServletResponse; +import jakarta.servlet.http.HttpServletResponse; import java.io.IOException; import static org.junit.jupiter.api.Assertions.assertEquals; diff --git a/hapi-fhir-server/src/test/java/ca/uhn/fhir/rest/server/util/NarrativeUtilTest.java b/hapi-fhir-server/src/test/java/ca/uhn/fhir/rest/server/util/NarrativeUtilTest.java index 7278c25f8af..a8bc5d34a46 100644 --- a/hapi-fhir-server/src/test/java/ca/uhn/fhir/rest/server/util/NarrativeUtilTest.java +++ b/hapi-fhir-server/src/test/java/ca/uhn/fhir/rest/server/util/NarrativeUtilTest.java @@ -9,21 +9,21 @@ public class NarrativeUtilTest { @ParameterizedTest @CsvSource({ - "
    hello
    ,
    hello
    ", - "
    hello
    ,
    hello
    ", - "
    hello
    ,
    hello
    ", - "
    hello
    ,
    hello
    ", - " ,
    hello
    ", - "
    hello
    ,
    hello
    ", - "
    hello
    ,
    hello
    ", - "
    hello
    ,
    hello
    ", - "hello ,
    hello
    ", - "empty , null", - "null , null" + "
    hello
    ,
    hello
    ", + "
    hello
    ,
    hello
    ", + "
    hello
    ,
    hello
    ", + "
    hello
    ,
    hello
    ", + " ,
    hello
    ", + "
    hello
    ,
    hello
    ", + "
    hello
    ,
    hello
    ", + "
    hello
    ,
    hello
    ", + "hello , hello", + "empty , empty", + "null , empty" }) public void testValidateIsCaseInsensitive(String theHtml, String theExpected) { - String output = NarrativeUtil.sanitize(fixNull(theHtml)); - assertEquals(fixNull(theExpected), output); + String output = NarrativeUtil.sanitizeHtmlFragment(fixNull(theHtml)); + assertEquals(fixNull(theExpected), fixNull(output)); } private String fixNull(String theExpected) { diff --git a/hapi-fhir-serviceloaders/hapi-fhir-caching-api/pom.xml b/hapi-fhir-serviceloaders/hapi-fhir-caching-api/pom.xml index 3ce49430a7c..717dc503b22 100644 --- a/hapi-fhir-serviceloaders/hapi-fhir-caching-api/pom.xml +++ b/hapi-fhir-serviceloaders/hapi-fhir-caching-api/pom.xml @@ -7,7 +7,7 @@ hapi-fhir-serviceloaders ca.uhn.hapi.fhir - 6.9.7-SNAPSHOT + 6.11.7-SNAPSHOT ../pom.xml diff --git a/hapi-fhir-serviceloaders/hapi-fhir-caching-caffeine/pom.xml b/hapi-fhir-serviceloaders/hapi-fhir-caching-caffeine/pom.xml index 11e3f12b2f8..4892de65b91 100644 --- a/hapi-fhir-serviceloaders/hapi-fhir-caching-caffeine/pom.xml +++ b/hapi-fhir-serviceloaders/hapi-fhir-caching-caffeine/pom.xml @@ -7,7 +7,7 @@ hapi-fhir-serviceloaders ca.uhn.hapi.fhir - 6.9.7-SNAPSHOT + 6.11.7-SNAPSHOT ../pom.xml @@ -21,7 +21,7 @@ ca.uhn.hapi.fhir hapi-fhir-caching-api - 6.9.7-SNAPSHOT + 6.11.7-SNAPSHOT diff --git a/hapi-fhir-serviceloaders/hapi-fhir-caching-guava/pom.xml b/hapi-fhir-serviceloaders/hapi-fhir-caching-guava/pom.xml index a149f89c490..4ac7744f800 100644 --- a/hapi-fhir-serviceloaders/hapi-fhir-caching-guava/pom.xml +++ b/hapi-fhir-serviceloaders/hapi-fhir-caching-guava/pom.xml @@ -7,7 +7,7 @@ hapi-fhir-serviceloaders ca.uhn.hapi.fhir - 6.9.7-SNAPSHOT + 6.11.7-SNAPSHOT ../pom.xml diff --git a/hapi-fhir-serviceloaders/hapi-fhir-caching-testing/pom.xml b/hapi-fhir-serviceloaders/hapi-fhir-caching-testing/pom.xml index 31c40af7b65..68182ea7940 100644 --- a/hapi-fhir-serviceloaders/hapi-fhir-caching-testing/pom.xml +++ b/hapi-fhir-serviceloaders/hapi-fhir-caching-testing/pom.xml @@ -7,7 +7,7 @@ hapi-fhir ca.uhn.hapi.fhir - 6.9.7-SNAPSHOT + 6.11.7-SNAPSHOT ../../pom.xml diff --git a/hapi-fhir-serviceloaders/pom.xml b/hapi-fhir-serviceloaders/pom.xml index 660ad79aacc..45d0307e9d7 100644 --- a/hapi-fhir-serviceloaders/pom.xml +++ b/hapi-fhir-serviceloaders/pom.xml @@ -5,7 +5,7 @@ hapi-deployable-pom ca.uhn.hapi.fhir - 6.9.7-SNAPSHOT + 6.11.7-SNAPSHOT ../hapi-deployable-pom/pom.xml diff --git a/hapi-fhir-spring-boot/hapi-fhir-spring-boot-autoconfigure/pom.xml b/hapi-fhir-spring-boot/hapi-fhir-spring-boot-autoconfigure/pom.xml index f9079b8cfab..4e76a3e78e7 100644 --- a/hapi-fhir-spring-boot/hapi-fhir-spring-boot-autoconfigure/pom.xml +++ b/hapi-fhir-spring-boot/hapi-fhir-spring-boot-autoconfigure/pom.xml @@ -5,7 +5,7 @@ ca.uhn.hapi.fhir hapi-deployable-pom - 6.9.7-SNAPSHOT + 6.11.7-SNAPSHOT ../../hapi-deployable-pom/pom.xml @@ -60,8 +60,8 @@ true - javax.servlet - javax.servlet-api + jakarta.servlet + jakarta.servlet-api true diff --git a/hapi-fhir-spring-boot/hapi-fhir-spring-boot-autoconfigure/src/main/java/ca/uhn/fhir/spring/boot/autoconfigure/FhirAutoConfiguration.java b/hapi-fhir-spring-boot/hapi-fhir-spring-boot-autoconfigure/src/main/java/ca/uhn/fhir/spring/boot/autoconfigure/FhirAutoConfiguration.java index 2e524810521..66f674ab2fb 100644 --- a/hapi-fhir-spring-boot/hapi-fhir-spring-boot-autoconfigure/src/main/java/ca/uhn/fhir/spring/boot/autoconfigure/FhirAutoConfiguration.java +++ b/hapi-fhir-spring-boot/hapi-fhir-spring-boot-autoconfigure/src/main/java/ca/uhn/fhir/spring/boot/autoconfigure/FhirAutoConfiguration.java @@ -45,6 +45,8 @@ import ca.uhn.fhir.rest.server.RestfulServer; import ca.uhn.fhir.rest.server.interceptor.IServerInterceptor; import ca.uhn.fhir.rest.server.interceptor.RequestValidatingInterceptor; import ca.uhn.fhir.rest.server.interceptor.ResponseValidatingInterceptor; +import jakarta.persistence.EntityManagerFactory; +import jakarta.servlet.ServletException; import okhttp3.OkHttpClient; import org.apache.http.client.HttpClient; import org.springframework.beans.factory.ObjectProvider; @@ -75,8 +77,6 @@ import org.springframework.util.CollectionUtils; import java.util.List; import java.util.concurrent.ScheduledExecutorService; -import javax.persistence.EntityManagerFactory; -import javax.servlet.ServletException; import javax.sql.DataSource; /** diff --git a/hapi-fhir-spring-boot/hapi-fhir-spring-boot-samples/hapi-fhir-spring-boot-sample-client-apache/pom.xml b/hapi-fhir-spring-boot/hapi-fhir-spring-boot-samples/hapi-fhir-spring-boot-sample-client-apache/pom.xml index fb4d0312cc7..2b50aacd7ef 100644 --- a/hapi-fhir-spring-boot/hapi-fhir-spring-boot-samples/hapi-fhir-spring-boot-sample-client-apache/pom.xml +++ b/hapi-fhir-spring-boot/hapi-fhir-spring-boot-samples/hapi-fhir-spring-boot-sample-client-apache/pom.xml @@ -5,7 +5,7 @@ ca.uhn.hapi.fhir hapi-fhir-spring-boot-samples - 6.9.7-SNAPSHOT + 6.11.7-SNAPSHOT hapi-fhir-spring-boot-sample-client-apache diff --git a/hapi-fhir-spring-boot/hapi-fhir-spring-boot-samples/hapi-fhir-spring-boot-sample-client-okhttp/pom.xml b/hapi-fhir-spring-boot/hapi-fhir-spring-boot-samples/hapi-fhir-spring-boot-sample-client-okhttp/pom.xml index dbe096cd8c5..4f5bae777e6 100644 --- a/hapi-fhir-spring-boot/hapi-fhir-spring-boot-samples/hapi-fhir-spring-boot-sample-client-okhttp/pom.xml +++ b/hapi-fhir-spring-boot/hapi-fhir-spring-boot-samples/hapi-fhir-spring-boot-sample-client-okhttp/pom.xml @@ -5,7 +5,7 @@ ca.uhn.hapi.fhir hapi-fhir-spring-boot-samples - 6.9.7-SNAPSHOT + 6.11.7-SNAPSHOT diff --git a/hapi-fhir-spring-boot/hapi-fhir-spring-boot-samples/hapi-fhir-spring-boot-sample-server-jersey/pom.xml b/hapi-fhir-spring-boot/hapi-fhir-spring-boot-samples/hapi-fhir-spring-boot-sample-server-jersey/pom.xml index 2f669d15070..6ac1fcffe05 100644 --- a/hapi-fhir-spring-boot/hapi-fhir-spring-boot-samples/hapi-fhir-spring-boot-sample-server-jersey/pom.xml +++ b/hapi-fhir-spring-boot/hapi-fhir-spring-boot-samples/hapi-fhir-spring-boot-sample-server-jersey/pom.xml @@ -5,7 +5,7 @@ ca.uhn.hapi.fhir hapi-fhir-spring-boot-samples - 6.9.7-SNAPSHOT + 6.11.7-SNAPSHOT diff --git a/hapi-fhir-spring-boot/hapi-fhir-spring-boot-samples/hapi-fhir-spring-boot-sample-server-jersey/src/main/java/sample/fhir/server/jersey/provider/PatientResourceProvider.java b/hapi-fhir-spring-boot/hapi-fhir-spring-boot-samples/hapi-fhir-spring-boot-sample-server-jersey/src/main/java/sample/fhir/server/jersey/provider/PatientResourceProvider.java index f5866d7e615..9c11a4ed57f 100644 --- a/hapi-fhir-spring-boot/hapi-fhir-spring-boot-samples/hapi-fhir-spring-boot-sample-server-jersey/src/main/java/sample/fhir/server/jersey/provider/PatientResourceProvider.java +++ b/hapi-fhir-spring-boot/hapi-fhir-spring-boot-samples/hapi-fhir-spring-boot-sample-server-jersey/src/main/java/sample/fhir/server/jersey/provider/PatientResourceProvider.java @@ -50,8 +50,8 @@ public class PatientResourceProvider extends AbstractJaxRsResourceProvider ca.uhn.hapi.fhir hapi-fhir-spring-boot - 6.9.7-SNAPSHOT + 6.11.7-SNAPSHOT diff --git a/hapi-fhir-spring-boot/hapi-fhir-spring-boot-starter/pom.xml b/hapi-fhir-spring-boot/hapi-fhir-spring-boot-starter/pom.xml index 01b07e351b5..4c620fb3e24 100644 --- a/hapi-fhir-spring-boot/hapi-fhir-spring-boot-starter/pom.xml +++ b/hapi-fhir-spring-boot/hapi-fhir-spring-boot-starter/pom.xml @@ -5,7 +5,7 @@ ca.uhn.hapi.fhir hapi-deployable-pom - 6.9.7-SNAPSHOT + 6.11.7-SNAPSHOT ../../hapi-deployable-pom/pom.xml diff --git a/hapi-fhir-spring-boot/pom.xml b/hapi-fhir-spring-boot/pom.xml index 93c95a33faf..08ebc710049 100644 --- a/hapi-fhir-spring-boot/pom.xml +++ b/hapi-fhir-spring-boot/pom.xml @@ -5,7 +5,7 @@ ca.uhn.hapi.fhir hapi-fhir - 6.9.7-SNAPSHOT + 6.11.7-SNAPSHOT ../pom.xml diff --git a/hapi-fhir-sql-migrate/pom.xml b/hapi-fhir-sql-migrate/pom.xml index 88816897c56..5152c3ba85f 100644 --- a/hapi-fhir-sql-migrate/pom.xml +++ b/hapi-fhir-sql-migrate/pom.xml @@ -5,7 +5,7 @@ ca.uhn.hapi.fhir hapi-deployable-pom - 6.9.7-SNAPSHOT + 6.11.7-SNAPSHOT ../hapi-deployable-pom/pom.xml @@ -32,9 +32,13 @@ - org.hibernate + org.hibernate.orm hibernate-core + + jakarta.annotation + jakarta.annotation-api + jakarta.transaction jakarta.transaction-api diff --git a/hapi-fhir-sql-migrate/src/main/java/ca/uhn/fhir/jpa/migrate/DriverTypeEnum.java b/hapi-fhir-sql-migrate/src/main/java/ca/uhn/fhir/jpa/migrate/DriverTypeEnum.java index 0c49ad0ab9b..00e909afe9a 100644 --- a/hapi-fhir-sql-migrate/src/main/java/ca/uhn/fhir/jpa/migrate/DriverTypeEnum.java +++ b/hapi-fhir-sql-migrate/src/main/java/ca/uhn/fhir/jpa/migrate/DriverTypeEnum.java @@ -22,6 +22,7 @@ package ca.uhn.fhir.jpa.migrate; import ca.uhn.fhir.context.ConfigurationException; import ca.uhn.fhir.i18n.Msg; import ca.uhn.fhir.rest.server.exceptions.InternalErrorException; +import jakarta.annotation.Nonnull; import org.apache.commons.dbcp2.BasicDataSource; import org.apache.commons.lang3.Validate; import org.slf4j.Logger; @@ -35,7 +36,6 @@ import org.springframework.transaction.support.TransactionTemplate; import java.lang.reflect.InvocationTargetException; import java.sql.Connection; import java.sql.SQLException; -import javax.annotation.Nonnull; import javax.sql.DataSource; public enum DriverTypeEnum { @@ -84,26 +84,26 @@ public enum DriverTypeEnum { String retval; switch (this) { case H2_EMBEDDED: - retval = "hapifhirh2.sql"; + retval = "h2.sql"; break; case DERBY_EMBEDDED: - retval = "derbytenseven.sql"; + retval = "derby.sql"; break; case MYSQL_5_7: case MARIADB_10_1: - retval = "mysql57.sql"; + retval = "mysql.sql"; break; case POSTGRES_9_4: - retval = "hapifhirpostgres94-complete.sql"; + retval = "postgres.sql"; break; case ORACLE_12C: - retval = "oracle12c.sql"; + retval = "oracle.sql"; break; case MSSQL_2012: - retval = "sqlserver2012.sql"; + retval = "sqlserver.sql"; break; case COCKROACHDB_21_1: - retval = "cockroachdb201.sql"; + retval = "cockroachdb.sql"; break; default: throw new ConfigurationException( diff --git a/hapi-fhir-sql-migrate/src/main/java/ca/uhn/fhir/jpa/migrate/HapiMigrator.java b/hapi-fhir-sql-migrate/src/main/java/ca/uhn/fhir/jpa/migrate/HapiMigrator.java index 302b05cde23..72e2bcf5a55 100644 --- a/hapi-fhir-sql-migrate/src/main/java/ca/uhn/fhir/jpa/migrate/HapiMigrator.java +++ b/hapi-fhir-sql-migrate/src/main/java/ca/uhn/fhir/jpa/migrate/HapiMigrator.java @@ -26,6 +26,7 @@ import ca.uhn.fhir.jpa.migrate.taskdef.InitializeSchemaTask; import ca.uhn.fhir.system.HapiSystemProperties; import ca.uhn.fhir.util.StopWatch; import com.google.common.annotations.VisibleForTesting; +import jakarta.annotation.Nonnull; import org.apache.commons.lang3.Validate; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -34,7 +35,6 @@ import java.sql.SQLException; import java.util.Collections; import java.util.List; import java.util.Objects; -import javax.annotation.Nonnull; import javax.sql.DataSource; import static org.apache.commons.lang3.StringUtils.isBlank; @@ -188,7 +188,9 @@ public class HapiMigrator { } private void postExecute(BaseTask theNext, StopWatch theStopWatch, boolean theSuccess) { - myHapiMigrationStorageSvc.saveTask(theNext, Math.toIntExact(theStopWatch.getMillis()), theSuccess); + if (!theNext.isDryRun()) { + myHapiMigrationStorageSvc.saveTask(theNext, Math.toIntExact(theStopWatch.getMillis()), theSuccess); + } } public void addTasks(Iterable theMigrationTasks) { @@ -219,6 +221,8 @@ public class HapiMigrator { } public void createMigrationTableIfRequired() { - myHapiMigrationStorageSvc.createMigrationTableIfRequired(); + if (!myDryRun) { + myHapiMigrationStorageSvc.createMigrationTableIfRequired(); + } } } diff --git a/hapi-fhir-sql-migrate/src/main/java/ca/uhn/fhir/jpa/migrate/JdbcUtils.java b/hapi-fhir-sql-migrate/src/main/java/ca/uhn/fhir/jpa/migrate/JdbcUtils.java index b9863105de6..95c21f2af17 100644 --- a/hapi-fhir-sql-migrate/src/main/java/ca/uhn/fhir/jpa/migrate/JdbcUtils.java +++ b/hapi-fhir-sql-migrate/src/main/java/ca/uhn/fhir/jpa/migrate/JdbcUtils.java @@ -22,6 +22,7 @@ package ca.uhn.fhir.jpa.migrate; import ca.uhn.fhir.i18n.Msg; import ca.uhn.fhir.jpa.migrate.taskdef.ColumnTypeEnum; import ca.uhn.fhir.rest.server.exceptions.InternalErrorException; +import jakarta.annotation.Nullable; import org.apache.commons.lang3.builder.EqualsBuilder; import org.apache.commons.lang3.builder.HashCodeBuilder; import org.apache.commons.lang3.builder.ToStringBuilder; @@ -39,6 +40,7 @@ import org.hibernate.engine.jdbc.env.spi.NameQualifierSupport; import org.hibernate.engine.jdbc.env.spi.QualifiedObjectNameFormatter; import org.hibernate.engine.jdbc.spi.SqlExceptionHelper; import org.hibernate.service.ServiceRegistry; +import org.hibernate.sql.ast.SqlAstTranslatorFactory; import org.hibernate.tool.schema.extract.spi.ExtractionContext; import org.hibernate.tool.schema.extract.spi.SequenceInformation; import org.hibernate.tool.schema.extract.spi.SequenceInformationExtractor; @@ -46,6 +48,7 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.jdbc.core.ColumnMapRowMapper; import org.springframework.transaction.support.TransactionTemplate; +import org.springframework.util.LinkedCaseInsensitiveMap; import java.sql.Connection; import java.sql.DatabaseMetaData; @@ -60,7 +63,6 @@ import java.util.Locale; import java.util.Objects; import java.util.Set; import java.util.stream.Collectors; -import javax.annotation.Nullable; import javax.sql.DataSource; public class JdbcUtils { @@ -230,6 +232,8 @@ public class JdbcUtils { return new ColumnType(ColumnTypeEnum.DOUBLE, length); case Types.FLOAT: return new ColumnType(ColumnTypeEnum.FLOAT, length); + case Types.TINYINT: + return new ColumnType(ColumnTypeEnum.TINYINT, length); default: throw new IllegalArgumentException(Msg.code(34) + "Don't know how to handle datatype " + dataType + " for column " + theColumnName + " on table " + theTableName); @@ -357,7 +361,7 @@ public class JdbcUtils { massageIdentifier(metadata, theTableName), null); - Set columnNames = new HashSet<>(); + LinkedCaseInsensitiveMap columnNames = new LinkedCaseInsensitiveMap<>(); while (indexes.next()) { String tableName = indexes.getString("TABLE_NAME").toUpperCase(Locale.US); if (!theTableName.equalsIgnoreCase(tableName)) { @@ -366,10 +370,10 @@ public class JdbcUtils { String columnName = indexes.getString("COLUMN_NAME"); columnName = columnName.toUpperCase(Locale.US); - columnNames.add(columnName); + columnNames.put(columnName, columnName); } - return columnNames; + return columnNames.keySet(); } catch (SQLException e) { throw new InternalErrorException(Msg.code(38) + e); } @@ -388,7 +392,7 @@ public class JdbcUtils { new DatabaseMetaDataDialectResolutionInfoAdapter(connection.getMetaData())); Set sequenceNames = new HashSet<>(); - if (dialect.supportsSequences()) { + if (dialect.getSequenceSupport().supportsSequences()) { // Use Hibernate to get a list of current sequences SequenceInformationExtractor sequenceInformationExtractor = @@ -412,6 +416,11 @@ public class JdbcUtils { return dialect; } + @Override + public SqlAstTranslatorFactory getSqlAstTranslatorFactory() { + return null; + } + @Override public ExtractedDatabaseMetaData getExtractedDatabaseMetaData() { return null; @@ -435,7 +444,7 @@ public class JdbcUtils { @Override public IdentifierHelper getIdentifierHelper() { return new NormalizingIdentifierHelperImpl( - this, null, true, true, true, null, null, null); + this, null, true, true, true, true, null, null, null); } @Override diff --git a/hapi-fhir-sql-migrate/src/main/java/ca/uhn/fhir/jpa/migrate/MigrationJdbcUtils.java b/hapi-fhir-sql-migrate/src/main/java/ca/uhn/fhir/jpa/migrate/MigrationJdbcUtils.java new file mode 100644 index 00000000000..521ec208012 --- /dev/null +++ b/hapi-fhir-sql-migrate/src/main/java/ca/uhn/fhir/jpa/migrate/MigrationJdbcUtils.java @@ -0,0 +1,66 @@ +/*- + * #%L + * HAPI FHIR Server - SQL Migration + * %% + * Copyright (C) 2014 - 2023 Smile CDR, Inc. + * %% + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * #L% + */ +package ca.uhn.fhir.jpa.migrate; + +import ca.uhn.fhir.i18n.Msg; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.jdbc.core.JdbcTemplate; +import org.springframework.jdbc.core.RowMapper; + +import java.util.List; +import java.util.Optional; + +/** + * Utility methods to be used by migrator functionality that needs to invoke JDBC directly. + */ +public class MigrationJdbcUtils { + private static final Logger ourLog = LoggerFactory.getLogger(MigrationJdbcUtils.class); + + public static boolean queryForSingleBooleanResultMultipleThrowsException( + String theSql, JdbcTemplate theJdbcTemplate) { + final RowMapper booleanRowMapper = (theResultSet, theRowNumber) -> theResultSet.getBoolean(1); + return queryForSingle(theSql, theJdbcTemplate, booleanRowMapper).orElse(false); + } + + private static Optional queryForSingle( + String theSql, JdbcTemplate theJdbcTemplate, RowMapper theRowMapper) { + final List results = queryForMultiple(theSql, theJdbcTemplate, theRowMapper); + + if (results.isEmpty()) { + return Optional.empty(); + } + + if (results.size() > 1) { + // Presumably other callers may want different behaviour but in this case more than one result should be + // considered a hard failure distinct from an empty result, which is one expected outcome. + throw new IllegalArgumentException(Msg.code(2474) + + String.format( + "Failure due to query returning more than one result: %s for SQL: [%s].", results, theSql)); + } + + return Optional.ofNullable(results.get(0)); + } + + private static List queryForMultiple( + String theSql, JdbcTemplate theJdbcTemplate, RowMapper theRowMapper) { + return theJdbcTemplate.query(theSql, theRowMapper); + } +} diff --git a/hapi-fhir-sql-migrate/src/main/java/ca/uhn/fhir/jpa/migrate/MigrationTaskList.java b/hapi-fhir-sql-migrate/src/main/java/ca/uhn/fhir/jpa/migrate/MigrationTaskList.java index 1275412c875..afc3a003fa6 100644 --- a/hapi-fhir-sql-migrate/src/main/java/ca/uhn/fhir/jpa/migrate/MigrationTaskList.java +++ b/hapi-fhir-sql-migrate/src/main/java/ca/uhn/fhir/jpa/migrate/MigrationTaskList.java @@ -20,6 +20,7 @@ package ca.uhn.fhir.jpa.migrate; import ca.uhn.fhir.jpa.migrate.taskdef.BaseTask; +import jakarta.annotation.Nonnull; import org.flywaydb.core.api.MigrationVersion; import java.util.ArrayList; @@ -29,7 +30,6 @@ import java.util.List; import java.util.Set; import java.util.function.Consumer; import java.util.stream.Collectors; -import javax.annotation.Nonnull; public class MigrationTaskList implements Iterable { private final List myTasks; diff --git a/hapi-fhir-sql-migrate/src/main/java/ca/uhn/fhir/jpa/migrate/entity/HapiMigrationEntity.java b/hapi-fhir-sql-migrate/src/main/java/ca/uhn/fhir/jpa/migrate/entity/HapiMigrationEntity.java index 7e9db2b7e3d..7ae686d268f 100644 --- a/hapi-fhir-sql-migrate/src/main/java/ca/uhn/fhir/jpa/migrate/entity/HapiMigrationEntity.java +++ b/hapi-fhir-sql-migrate/src/main/java/ca/uhn/fhir/jpa/migrate/entity/HapiMigrationEntity.java @@ -21,16 +21,16 @@ package ca.uhn.fhir.jpa.migrate.entity; import ca.uhn.fhir.jpa.migrate.taskdef.BaseTask; import ca.uhn.fhir.util.VersionEnum; +import jakarta.persistence.Column; +import jakarta.persistence.Entity; +import jakarta.persistence.GeneratedValue; +import jakarta.persistence.GenerationType; +import jakarta.persistence.Id; import org.hibernate.annotations.GenericGenerator; import org.springframework.jdbc.core.PreparedStatementSetter; import org.springframework.jdbc.core.RowMapper; import java.util.Date; -import javax.persistence.Column; -import javax.persistence.Entity; -import javax.persistence.GeneratedValue; -import javax.persistence.GenerationType; -import javax.persistence.Id; // Note even though we are using javax.persistence annotations here, we are managing these records outside of jpa // so these annotations are for informational purposes only diff --git a/hapi-fhir-sql-migrate/src/main/java/ca/uhn/fhir/jpa/migrate/taskdef/AddColumnTask.java b/hapi-fhir-sql-migrate/src/main/java/ca/uhn/fhir/jpa/migrate/taskdef/AddColumnTask.java index d39bc434614..4ecd8a92dee 100644 --- a/hapi-fhir-sql-migrate/src/main/java/ca/uhn/fhir/jpa/migrate/taskdef/AddColumnTask.java +++ b/hapi-fhir-sql-migrate/src/main/java/ca/uhn/fhir/jpa/migrate/taskdef/AddColumnTask.java @@ -31,6 +31,10 @@ public class AddColumnTask extends BaseTableColumnTypeTask { private static final Logger ourLog = LoggerFactory.getLogger(AddColumnTask.class); + public static AddColumnTask lowerCase(Set theColumnDriverMappingOverrides) { + return new AddColumnTask(null, null, ColumnNameCase.ALL_LOWER, theColumnDriverMappingOverrides); + } + public AddColumnTask() { this(null, null); setDryRun(true); @@ -41,6 +45,14 @@ public class AddColumnTask extends BaseTableColumnTypeTask { super(theProductVersion, theSchemaVersion); } + private AddColumnTask( + String theProductVersion, + String theSchemaVersion, + ColumnNameCase theColumnNameCase, + Set theColumnDriverMappingOverrides) { + super(theProductVersion, theSchemaVersion, theColumnNameCase, theColumnDriverMappingOverrides); + } + @Override public void validate() { super.validate(); diff --git a/hapi-fhir-sql-migrate/src/main/java/ca/uhn/fhir/jpa/migrate/taskdef/AddIndexTask.java b/hapi-fhir-sql-migrate/src/main/java/ca/uhn/fhir/jpa/migrate/taskdef/AddIndexTask.java index b0d2ae6ccd4..5349436f29f 100644 --- a/hapi-fhir-sql-migrate/src/main/java/ca/uhn/fhir/jpa/migrate/taskdef/AddIndexTask.java +++ b/hapi-fhir-sql-migrate/src/main/java/ca/uhn/fhir/jpa/migrate/taskdef/AddIndexTask.java @@ -21,6 +21,7 @@ package ca.uhn.fhir.jpa.migrate.taskdef; import ca.uhn.fhir.jpa.migrate.DriverTypeEnum; import ca.uhn.fhir.jpa.migrate.JdbcUtils; +import jakarta.annotation.Nonnull; import org.apache.commons.lang3.Validate; import org.apache.commons.lang3.builder.EqualsBuilder; import org.apache.commons.lang3.builder.HashCodeBuilder; @@ -33,11 +34,10 @@ import java.util.Collections; import java.util.List; import java.util.Locale; import java.util.Set; -import javax.annotation.Nonnull; public class AddIndexTask extends BaseTableTask { - private static final Logger ourLog = LoggerFactory.getLogger(AddIndexTask.class); + static final Logger ourLog = LoggerFactory.getLogger(AddIndexTask.class); private String myIndexName; private List myColumns; @@ -97,8 +97,15 @@ public class AddIndexTask extends BaseTableTask { try { executeSql(tableName, sql); } catch (Exception e) { - if (e.toString().contains("already exists")) { - ourLog.warn("Index {} already exists", myIndexName); + String message = e.toString(); + if (message.contains("already exists") + || + // The Oracle message is ORA-01408: such column list already indexed + // TODO KHS consider db-specific handling here that uses the error code instead of the message so + // this is language independent + // e.g. if the db is Oracle than checking e.getErrorCode() == 1408 should detect this case + message.contains("already indexed")) { + ourLog.warn("Index {} already exists: {}", myIndexName, e.getMessage()); } else { throw e; } diff --git a/hapi-fhir-sql-migrate/src/main/java/ca/uhn/fhir/jpa/migrate/taskdef/AddTableByColumnTask.java b/hapi-fhir-sql-migrate/src/main/java/ca/uhn/fhir/jpa/migrate/taskdef/AddTableByColumnTask.java index 45e254bce89..bf17e6def25 100644 --- a/hapi-fhir-sql-migrate/src/main/java/ca/uhn/fhir/jpa/migrate/taskdef/AddTableByColumnTask.java +++ b/hapi-fhir-sql-migrate/src/main/java/ca/uhn/fhir/jpa/migrate/taskdef/AddTableByColumnTask.java @@ -29,7 +29,9 @@ import org.slf4j.LoggerFactory; import java.sql.SQLException; import java.util.ArrayList; +import java.util.Comparator; import java.util.List; +import java.util.stream.Collectors; public class AddTableByColumnTask extends BaseTableTask { @@ -38,15 +40,26 @@ public class AddTableByColumnTask extends BaseTableTask { private final List myAddColumnTasks = new ArrayList<>(); private List myPkColumns; private final List myFKColumns = new ArrayList<>(); + private final Comparator myColumnSortingRules; public AddTableByColumnTask() { - this(null, null); + this(null); + } + + public AddTableByColumnTask(Comparator theColumnSortingRules) { + this(null, null, theColumnSortingRules); setDryRun(true); myCheckForExistingTables = false; } public AddTableByColumnTask(String theProductVersion, String theSchemaVersion) { + this(theProductVersion, theSchemaVersion, null); + } + + private AddTableByColumnTask( + String theProductVersion, String theSchemaVersion, Comparator theColumnSortingRules) { super(theProductVersion, theSchemaVersion); + myColumnSortingRules = theColumnSortingRules; } @Override @@ -83,7 +96,7 @@ public class AddTableByColumnTask extends BaseTableTask { sb.append(" "); } - for (AddColumnTask next : myAddColumnTasks) { + for (AddColumnTask next : getOrderedAddColumnTasks()) { next.setDriverType(getDriverType()); next.setTableName(getTableName()); next.validate(); @@ -194,4 +207,12 @@ public class AddTableByColumnTask extends BaseTableTask { theBuilder.append(myAddColumnTasks); theBuilder.append(myPkColumns); } + + private List getOrderedAddColumnTasks() { + if (myColumnSortingRules == null) { + return myAddColumnTasks; + } + + return myAddColumnTasks.stream().sorted(myColumnSortingRules).collect(Collectors.toUnmodifiableList()); + } } diff --git a/hapi-fhir-sql-migrate/src/main/java/ca/uhn/fhir/jpa/migrate/taskdef/BaseTableColumnTask.java b/hapi-fhir-sql-migrate/src/main/java/ca/uhn/fhir/jpa/migrate/taskdef/BaseTableColumnTask.java index c7616bde567..36fcb164543 100644 --- a/hapi-fhir-sql-migrate/src/main/java/ca/uhn/fhir/jpa/migrate/taskdef/BaseTableColumnTask.java +++ b/hapi-fhir-sql-migrate/src/main/java/ca/uhn/fhir/jpa/migrate/taskdef/BaseTableColumnTask.java @@ -19,12 +19,15 @@ */ package ca.uhn.fhir.jpa.migrate.taskdef; +import ca.uhn.fhir.i18n.Msg; import org.apache.commons.lang3.Validate; import org.apache.commons.lang3.builder.EqualsBuilder; import org.apache.commons.lang3.builder.HashCodeBuilder; +import java.util.Collections; import java.util.HashMap; import java.util.Map; +import java.util.Set; import java.util.function.Function; public abstract class BaseTableColumnTask extends BaseTableTask { @@ -35,8 +38,19 @@ public abstract class BaseTableColumnTask extends BaseTableTask { // If a concrete class decides to, they can define a custom WHERE clause for the task. protected String myWhereClause; + private final ColumnNameCase myColumnNameCase; + public BaseTableColumnTask(String theProductVersion, String theSchemaVersion) { - super(theProductVersion, theSchemaVersion); + this(theProductVersion, theSchemaVersion, ColumnNameCase.ALL_UPPER, Collections.emptySet()); + } + + BaseTableColumnTask( + String theProductVersion, + String theSchemaVersion, + ColumnNameCase theColumnNameCase, + Set theColumnDriverMappingOverrides) { + super(theProductVersion, theSchemaVersion, theColumnDriverMappingOverrides); + myColumnNameCase = theColumnNameCase; } public String getColumnName() { @@ -44,7 +58,17 @@ public abstract class BaseTableColumnTask extends BaseTableTask { } public BaseTableColumnTask setColumnName(String theColumnName) { - myColumnName = theColumnName.toUpperCase(); + switch (myColumnNameCase) { + case ALL_UPPER: + myColumnName = theColumnName.toUpperCase(); + break; + case ALL_LOWER: + myColumnName = theColumnName.toLowerCase(); + break; + default: + throw new IllegalArgumentException(Msg.code(2448) + + " Unknown ColumnNameCase was passed when setting column name case: " + myColumnNameCase); + } return this; } diff --git a/hapi-fhir-sql-migrate/src/main/java/ca/uhn/fhir/jpa/migrate/taskdef/BaseTableColumnTypeTask.java b/hapi-fhir-sql-migrate/src/main/java/ca/uhn/fhir/jpa/migrate/taskdef/BaseTableColumnTypeTask.java index 50abfa63d33..75dc343f2af 100644 --- a/hapi-fhir-sql-migrate/src/main/java/ca/uhn/fhir/jpa/migrate/taskdef/BaseTableColumnTypeTask.java +++ b/hapi-fhir-sql-migrate/src/main/java/ca/uhn/fhir/jpa/migrate/taskdef/BaseTableColumnTypeTask.java @@ -19,11 +19,12 @@ */ package ca.uhn.fhir.jpa.migrate.taskdef; +import jakarta.annotation.Nullable; import org.apache.commons.lang3.Validate; import org.apache.commons.lang3.builder.EqualsBuilder; import org.apache.commons.lang3.builder.HashCodeBuilder; -import javax.annotation.Nullable; +import java.util.Set; public abstract class BaseTableColumnTypeTask extends BaseTableColumnTask { private ColumnTypeEnum myColumnType; @@ -37,6 +38,14 @@ public abstract class BaseTableColumnTypeTask extends BaseTableColumnTask { super(theProductVersion, theSchemaVersion); } + BaseTableColumnTypeTask( + String theProductVersion, + String theSchemaVersion, + ColumnNameCase theColumnNameCase, + Set theColumnDriverMappingOverrides) { + super(theProductVersion, theSchemaVersion, theColumnNameCase, theColumnDriverMappingOverrides); + } + public ColumnTypeEnum getColumnType() { return myColumnType; } diff --git a/hapi-fhir-sql-migrate/src/main/java/ca/uhn/fhir/jpa/migrate/taskdef/BaseTableTask.java b/hapi-fhir-sql-migrate/src/main/java/ca/uhn/fhir/jpa/migrate/taskdef/BaseTableTask.java index 049ed847d35..5ffd6c38569 100644 --- a/hapi-fhir-sql-migrate/src/main/java/ca/uhn/fhir/jpa/migrate/taskdef/BaseTableTask.java +++ b/hapi-fhir-sql-migrate/src/main/java/ca/uhn/fhir/jpa/migrate/taskdef/BaseTableTask.java @@ -19,17 +19,37 @@ */ package ca.uhn.fhir.jpa.migrate.taskdef; +import ca.uhn.fhir.i18n.Msg; +import jakarta.annotation.Nonnull; import org.apache.commons.lang3.Validate; import org.apache.commons.lang3.builder.EqualsBuilder; import org.apache.commons.lang3.builder.HashCodeBuilder; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; import java.util.Objects; +import java.util.Set; +import java.util.stream.Collectors; public abstract class BaseTableTask extends BaseTask { + private static final Logger ourLog = LoggerFactory.getLogger(BaseTableTask.class); private String myTableName; + private final List myColumnDriverMappingOverrides; + public BaseTableTask(String theProductVersion, String theSchemaVersion) { + this(theProductVersion, theSchemaVersion, Collections.emptySet()); + } + + public BaseTableTask( + String theProductVersion, + String theSchemaVersion, + Set theColumnDriverMappingOverrides) { super(theProductVersion, theSchemaVersion); + myColumnDriverMappingOverrides = new ArrayList<>(theColumnDriverMappingOverrides); } public String getTableName() { @@ -54,13 +74,12 @@ public abstract class BaseTableTask extends BaseTask { } protected String getSqlType(ColumnTypeEnum theColumnType, Long theColumnLength) { - String retVal = ColumnTypeToDriverTypeToSqlType.getColumnTypeToDriverTypeToSqlType() - .get(theColumnType) - .get(getDriverType()); + final String retVal = getColumnSqlWithToken(theColumnType); + Objects.requireNonNull(retVal); if (theColumnType == ColumnTypeEnum.STRING) { - retVal = retVal.replace("?", Long.toString(theColumnLength)); + return retVal.replace("?", Long.toString(theColumnLength)); } return retVal; @@ -70,4 +89,29 @@ public abstract class BaseTableTask extends BaseTask { protected void generateHashCode(HashCodeBuilder theBuilder) { theBuilder.append(myTableName); } + + @Nonnull + private String getColumnSqlWithToken(ColumnTypeEnum theColumnType) { + final List eligibleOverrides = myColumnDriverMappingOverrides.stream() + .filter(override -> override.getColumnType() == theColumnType) + .filter(override -> override.getDriverType() == getDriverType()) + .collect(Collectors.toUnmodifiableList()); + + if (eligibleOverrides.size() > 1) { + ourLog.info("There is more than one eligible override: {}. Picking the first one", eligibleOverrides); + } + + if (eligibleOverrides.size() == 1) { + return eligibleOverrides.get(0).getColumnTypeSql(); + } + + if (!ColumnTypeToDriverTypeToSqlType.getColumnTypeToDriverTypeToSqlType() + .containsKey(theColumnType)) { + throw new IllegalArgumentException(Msg.code(2449) + "Column type does not exist: " + theColumnType); + } + + return ColumnTypeToDriverTypeToSqlType.getColumnTypeToDriverTypeToSqlType() + .get(theColumnType) + .get(getDriverType()); + } } diff --git a/hapi-fhir-sql-migrate/src/main/java/ca/uhn/fhir/jpa/migrate/taskdef/BaseTask.java b/hapi-fhir-sql-migrate/src/main/java/ca/uhn/fhir/jpa/migrate/taskdef/BaseTask.java index cb7edd5ceb2..dafc4b75730 100644 --- a/hapi-fhir-sql-migrate/src/main/java/ca/uhn/fhir/jpa/migrate/taskdef/BaseTask.java +++ b/hapi-fhir-sql-migrate/src/main/java/ca/uhn/fhir/jpa/migrate/taskdef/BaseTask.java @@ -250,6 +250,8 @@ public abstract class BaseTask { return getConnectionProperties().newJdbcTemplate(); } + private final List myPreconditions = new ArrayList<>(); + public void execute() throws SQLException { if (myDoNothing) { ourLog.info("Skipping stubbed task: {}", getDescription()); @@ -257,7 +259,17 @@ public abstract class BaseTask { } if (!myOnlyAppliesToPlatforms.isEmpty()) { if (!myOnlyAppliesToPlatforms.contains(getDriverType())) { - ourLog.debug("Skipping task {} as it does not apply to {}", getDescription(), getDriverType()); + ourLog.info("Skipping task {} as it does not apply to {}", getDescription(), getDriverType()); + return; + } + } + + for (ExecuteTaskPrecondition precondition : myPreconditions) { + ourLog.debug("precondition to evaluate: {}", precondition); + if (!precondition.getPreconditionRunner().get()) { + ourLog.info( + "Skipping task since one of the preconditions was not met: {}", + precondition.getPreconditionReason()); return; } } @@ -305,6 +317,10 @@ public abstract class BaseTask { return this; } + public void addPrecondition(ExecuteTaskPrecondition thePrecondition) { + myPreconditions.add(thePrecondition); + } + @Override public final int hashCode() { HashCodeBuilder builder = new HashCodeBuilder(); diff --git a/hapi-fhir-sql-migrate/src/main/java/ca/uhn/fhir/jpa/migrate/taskdef/ColumnDriverMappingOverride.java b/hapi-fhir-sql-migrate/src/main/java/ca/uhn/fhir/jpa/migrate/taskdef/ColumnDriverMappingOverride.java new file mode 100644 index 00000000000..8a669bad7e3 --- /dev/null +++ b/hapi-fhir-sql-migrate/src/main/java/ca/uhn/fhir/jpa/migrate/taskdef/ColumnDriverMappingOverride.java @@ -0,0 +1,88 @@ +/*- + * #%L + * HAPI FHIR Server - SQL Migration + * %% + * Copyright (C) 2014 - 2023 Smile CDR, Inc. + * %% + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * #L% + */ +package ca.uhn.fhir.jpa.migrate.taskdef; + +import ca.uhn.fhir.jpa.migrate.DriverTypeEnum; +import jakarta.annotation.Nonnull; + +import java.util.Objects; +import java.util.StringJoiner; + +/** + * Capture a single SQL column type text override to the logic in {@link ColumnDriverMappingOverride}, namely, + * by column type and driver type. + *

    + * Several overrides can be passed down together at the same time to override said logic. + */ +public class ColumnDriverMappingOverride { + private final ColumnTypeEnum myColumnType; + private final DriverTypeEnum myDriverType; + + private final String myColumnTypeSql; + + public ColumnDriverMappingOverride( + @Nonnull ColumnTypeEnum theColumnType, + @Nonnull DriverTypeEnum theDriverType, + @Nonnull String theColumnTypeSql) { + myColumnType = theColumnType; + myDriverType = theDriverType; + myColumnTypeSql = theColumnTypeSql; + } + + public ColumnTypeEnum getColumnType() { + return myColumnType; + } + + public DriverTypeEnum getDriverType() { + return myDriverType; + } + + public String getColumnTypeSql() { + return myColumnTypeSql; + } + + @Override + public boolean equals(Object theO) { + if (this == theO) { + return true; + } + if (theO == null || getClass() != theO.getClass()) { + return false; + } + ColumnDriverMappingOverride that = (ColumnDriverMappingOverride) theO; + return myColumnType == that.myColumnType + && myDriverType == that.myDriverType + && Objects.equals(myColumnTypeSql, that.myColumnTypeSql); + } + + @Override + public int hashCode() { + return Objects.hash(myColumnType, myDriverType, myColumnTypeSql); + } + + @Override + public String toString() { + return new StringJoiner(", ", ColumnDriverMappingOverride.class.getSimpleName() + "[", "]") + .add("myColumnType=" + myColumnType) + .add("myDriverType=" + myDriverType) + .add("myColumnTypeSql='" + myColumnTypeSql + "'") + .toString(); + } +} diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/provider/r4/IMemberMatchConsentHook.java b/hapi-fhir-sql-migrate/src/main/java/ca/uhn/fhir/jpa/migrate/taskdef/ColumnNameCase.java similarity index 69% rename from hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/provider/r4/IMemberMatchConsentHook.java rename to hapi-fhir-sql-migrate/src/main/java/ca/uhn/fhir/jpa/migrate/taskdef/ColumnNameCase.java index 8c2f59f4023..47b243e0a59 100644 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/provider/r4/IMemberMatchConsentHook.java +++ b/hapi-fhir-sql-migrate/src/main/java/ca/uhn/fhir/jpa/migrate/taskdef/ColumnNameCase.java @@ -1,6 +1,6 @@ /*- * #%L - * HAPI FHIR JPA Server + * HAPI FHIR Server - SQL Migration * %% * Copyright (C) 2014 - 2023 Smile CDR, Inc. * %% @@ -17,13 +17,12 @@ * limitations under the License. * #L% */ -package ca.uhn.fhir.jpa.provider.r4; - -import org.hl7.fhir.instance.model.api.IBaseResource; - -import java.util.function.Consumer; +package ca.uhn.fhir.jpa.migrate.taskdef; /** - * Pre-save hook for Consent saved during $member-match. + * Determine whether to display column names in upper case, lower case, or eventually some form of mixed case. */ -public interface IMemberMatchConsentHook extends Consumer {} +public enum ColumnNameCase { + ALL_UPPER, + ALL_LOWER +} diff --git a/hapi-fhir-sql-migrate/src/main/java/ca/uhn/fhir/jpa/migrate/taskdef/ColumnTypeEnum.java b/hapi-fhir-sql-migrate/src/main/java/ca/uhn/fhir/jpa/migrate/taskdef/ColumnTypeEnum.java index d78223f7773..d1306fbe103 100644 --- a/hapi-fhir-sql-migrate/src/main/java/ca/uhn/fhir/jpa/migrate/taskdef/ColumnTypeEnum.java +++ b/hapi-fhir-sql-migrate/src/main/java/ca/uhn/fhir/jpa/migrate/taskdef/ColumnTypeEnum.java @@ -31,5 +31,11 @@ public enum ColumnTypeEnum { BLOB, CLOB, DOUBLE, - TEXT; + + /** + * Unlimited length text, with a column definition containing the annotation: + * @Column(length=Integer.MAX_VALUE) + */ + TEXT, + BIG_DECIMAL; } diff --git a/hapi-fhir-sql-migrate/src/main/java/ca/uhn/fhir/jpa/migrate/taskdef/ColumnTypeToDriverTypeToSqlType.java b/hapi-fhir-sql-migrate/src/main/java/ca/uhn/fhir/jpa/migrate/taskdef/ColumnTypeToDriverTypeToSqlType.java index 03b222718cd..f64c34554c0 100644 --- a/hapi-fhir-sql-migrate/src/main/java/ca/uhn/fhir/jpa/migrate/taskdef/ColumnTypeToDriverTypeToSqlType.java +++ b/hapi-fhir-sql-migrate/src/main/java/ca/uhn/fhir/jpa/migrate/taskdef/ColumnTypeToDriverTypeToSqlType.java @@ -62,7 +62,7 @@ public final class ColumnTypeToDriverTypeToSqlType { setColumnType(ColumnTypeEnum.DOUBLE, DriverTypeEnum.MYSQL_5_7, "double precision"); setColumnType(ColumnTypeEnum.DOUBLE, DriverTypeEnum.MSSQL_2012, "double precision"); setColumnType(ColumnTypeEnum.DOUBLE, DriverTypeEnum.ORACLE_12C, "double precision"); - setColumnType(ColumnTypeEnum.DOUBLE, DriverTypeEnum.POSTGRES_9_4, "float8"); + setColumnType(ColumnTypeEnum.DOUBLE, DriverTypeEnum.POSTGRES_9_4, "double precision"); setColumnType(ColumnTypeEnum.LONG, DriverTypeEnum.H2_EMBEDDED, "bigint"); setColumnType(ColumnTypeEnum.LONG, DriverTypeEnum.DERBY_EMBEDDED, "bigint"); @@ -123,13 +123,21 @@ public final class ColumnTypeToDriverTypeToSqlType { "oid"); // the PG driver will write oid into a `text` column setColumnType(ColumnTypeEnum.CLOB, DriverTypeEnum.MSSQL_2012, "varchar(MAX)"); - setColumnType(ColumnTypeEnum.TEXT, DriverTypeEnum.H2_EMBEDDED, "character varying"); - setColumnType(ColumnTypeEnum.TEXT, DriverTypeEnum.DERBY_EMBEDDED, "long varchar"); + setColumnType(ColumnTypeEnum.TEXT, DriverTypeEnum.H2_EMBEDDED, "clob"); + setColumnType(ColumnTypeEnum.TEXT, DriverTypeEnum.DERBY_EMBEDDED, "clob"); setColumnType(ColumnTypeEnum.TEXT, DriverTypeEnum.MARIADB_10_1, "longtext"); setColumnType(ColumnTypeEnum.TEXT, DriverTypeEnum.MYSQL_5_7, "longtext"); - setColumnType(ColumnTypeEnum.TEXT, DriverTypeEnum.ORACLE_12C, "long"); + setColumnType(ColumnTypeEnum.TEXT, DriverTypeEnum.ORACLE_12C, "clob"); setColumnType(ColumnTypeEnum.TEXT, DriverTypeEnum.POSTGRES_9_4, "text"); setColumnType(ColumnTypeEnum.TEXT, DriverTypeEnum.MSSQL_2012, "varchar(MAX)"); + + setColumnType(ColumnTypeEnum.BIG_DECIMAL, DriverTypeEnum.H2_EMBEDDED, "numeric(38,2)"); + setColumnType(ColumnTypeEnum.BIG_DECIMAL, DriverTypeEnum.DERBY_EMBEDDED, "decimal(31,2)"); + setColumnType(ColumnTypeEnum.BIG_DECIMAL, DriverTypeEnum.MARIADB_10_1, "decimal(38,2)"); + setColumnType(ColumnTypeEnum.BIG_DECIMAL, DriverTypeEnum.MYSQL_5_7, "decimal(38,2)"); + setColumnType(ColumnTypeEnum.BIG_DECIMAL, DriverTypeEnum.ORACLE_12C, "number(38,2)"); + setColumnType(ColumnTypeEnum.BIG_DECIMAL, DriverTypeEnum.POSTGRES_9_4, "numeric(38,2)"); + setColumnType(ColumnTypeEnum.BIG_DECIMAL, DriverTypeEnum.MSSQL_2012, "numeric(38,2)"); } public static Map> getColumnTypeToDriverTypeToSqlType() { diff --git a/hapi-fhir-sql-migrate/src/main/java/ca/uhn/fhir/jpa/migrate/taskdef/DropForeignKeyTask.java b/hapi-fhir-sql-migrate/src/main/java/ca/uhn/fhir/jpa/migrate/taskdef/DropForeignKeyTask.java index b8f99f221ef..c5db9820eb5 100644 --- a/hapi-fhir-sql-migrate/src/main/java/ca/uhn/fhir/jpa/migrate/taskdef/DropForeignKeyTask.java +++ b/hapi-fhir-sql-migrate/src/main/java/ca/uhn/fhir/jpa/migrate/taskdef/DropForeignKeyTask.java @@ -22,6 +22,7 @@ package ca.uhn.fhir.jpa.migrate.taskdef; import ca.uhn.fhir.i18n.Msg; import ca.uhn.fhir.jpa.migrate.DriverTypeEnum; import ca.uhn.fhir.jpa.migrate.JdbcUtils; +import jakarta.annotation.Nonnull; import org.apache.commons.lang3.Validate; import org.apache.commons.lang3.builder.EqualsBuilder; import org.apache.commons.lang3.builder.HashCodeBuilder; @@ -32,7 +33,6 @@ import java.sql.SQLException; import java.util.ArrayList; import java.util.List; import java.util.Set; -import javax.annotation.Nonnull; import static org.apache.commons.lang3.StringUtils.isNotBlank; diff --git a/hapi-fhir-sql-migrate/src/main/java/ca/uhn/fhir/jpa/migrate/taskdef/DropIndexTask.java b/hapi-fhir-sql-migrate/src/main/java/ca/uhn/fhir/jpa/migrate/taskdef/DropIndexTask.java index 8689efaec96..8ee052f776e 100644 --- a/hapi-fhir-sql-migrate/src/main/java/ca/uhn/fhir/jpa/migrate/taskdef/DropIndexTask.java +++ b/hapi-fhir-sql-migrate/src/main/java/ca/uhn/fhir/jpa/migrate/taskdef/DropIndexTask.java @@ -21,6 +21,7 @@ package ca.uhn.fhir.jpa.migrate.taskdef; import ca.uhn.fhir.jpa.migrate.DriverTypeEnum; import ca.uhn.fhir.jpa.migrate.JdbcUtils; +import jakarta.annotation.Nonnull; import org.apache.commons.lang3.Validate; import org.apache.commons.lang3.builder.EqualsBuilder; import org.apache.commons.lang3.builder.HashCodeBuilder; @@ -37,7 +38,6 @@ import java.util.Collections; import java.util.List; import java.util.Objects; import java.util.Set; -import javax.annotation.Nonnull; import javax.sql.DataSource; public class DropIndexTask extends BaseTableTask { diff --git a/hapi-fhir-sql-migrate/src/main/java/ca/uhn/fhir/jpa/migrate/taskdef/ExecuteTaskPrecondition.java b/hapi-fhir-sql-migrate/src/main/java/ca/uhn/fhir/jpa/migrate/taskdef/ExecuteTaskPrecondition.java new file mode 100644 index 00000000000..36cc86b611f --- /dev/null +++ b/hapi-fhir-sql-migrate/src/main/java/ca/uhn/fhir/jpa/migrate/taskdef/ExecuteTaskPrecondition.java @@ -0,0 +1,74 @@ +/*- + * #%L + * HAPI FHIR Server - SQL Migration + * %% + * Copyright (C) 2014 - 2023 Smile CDR, Inc. + * %% + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * #L% + */ +package ca.uhn.fhir.jpa.migrate.taskdef; + +import java.util.Objects; +import java.util.StringJoiner; +import java.util.function.Supplier; + +/** + * Contains a pre-built precondition to evaluate once {@link BaseTask#execute()} is called. + *

    + * Includes both a {@link Supplier} containing the logic to determine if the precondition evaluates to true or false and + * a reason String to output to the logs if the precondition evaluates to false and halts execution of the task. + */ +public class ExecuteTaskPrecondition { + private final Supplier myPreconditionRunner; + private final String myPreconditionReason; + + public ExecuteTaskPrecondition(Supplier thePreconditionRunner, String thePreconditionReason) { + myPreconditionRunner = thePreconditionRunner; + myPreconditionReason = thePreconditionReason; + } + + public Supplier getPreconditionRunner() { + return myPreconditionRunner; + } + + public String getPreconditionReason() { + return myPreconditionReason; + } + + @Override + public boolean equals(Object theO) { + if (this == theO) { + return true; + } + if (theO == null || getClass() != theO.getClass()) { + return false; + } + ExecuteTaskPrecondition that = (ExecuteTaskPrecondition) theO; + return Objects.equals(myPreconditionRunner, that.myPreconditionRunner) + && Objects.equals(myPreconditionReason, that.myPreconditionReason); + } + + @Override + public int hashCode() { + return Objects.hash(myPreconditionRunner, myPreconditionReason); + } + + @Override + public String toString() { + return new StringJoiner(", ", ExecuteTaskPrecondition.class.getSimpleName() + "[", "]") + .add("myPreconditionRunner=" + myPreconditionRunner) + .add("myPreconditionReason='" + myPreconditionReason + "'") + .toString(); + } +} diff --git a/hapi-fhir-sql-migrate/src/main/java/ca/uhn/fhir/jpa/migrate/taskdef/ForceIdMigrationCopyTask.java b/hapi-fhir-sql-migrate/src/main/java/ca/uhn/fhir/jpa/migrate/taskdef/ForceIdMigrationCopyTask.java new file mode 100644 index 00000000000..bdf1030c639 --- /dev/null +++ b/hapi-fhir-sql-migrate/src/main/java/ca/uhn/fhir/jpa/migrate/taskdef/ForceIdMigrationCopyTask.java @@ -0,0 +1,90 @@ +/*- + * #%L + * HAPI FHIR Server - SQL Migration + * %% + * Copyright (C) 2014 - 2023 Smile CDR, Inc. + * %% + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * #L% + */ +package ca.uhn.fhir.jpa.migrate.taskdef; + +import org.apache.commons.lang3.builder.EqualsBuilder; +import org.apache.commons.lang3.builder.HashCodeBuilder; +import org.apache.commons.lang3.tuple.Pair; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.jdbc.core.JdbcTemplate; + +import java.sql.SQLException; + +public class ForceIdMigrationCopyTask extends BaseTask { + private static final Logger ourLog = LoggerFactory.getLogger(ForceIdMigrationCopyTask.class); + + public ForceIdMigrationCopyTask(String theProductVersion, String theSchemaVersion) { + super(theProductVersion, theSchemaVersion); + } + + @Override + public void validate() { + // no-op + } + + @Override + protected void doExecute() throws SQLException { + logInfo(ourLog, "Starting: migrate fhir_id from hfj_forced_id to hfj_resource.fhir_id"); + + JdbcTemplate jdbcTemplate = newJdbcTemplate(); + + Pair range = jdbcTemplate.queryForObject( + "select min(RES_ID), max(RES_ID) from HFJ_RESOURCE", + (rs, rowNum) -> Pair.of(rs.getLong(1), rs.getLong(2))); + + if (range == null || range.getLeft() == null) { + logInfo(ourLog, "HFJ_RESOURCE is empty. No work to do."); + return; + } + + // run update in batches. + int rowsPerBlock = 50; // hfj_resource has roughly 50 rows per 8k block. + int batchSize = rowsPerBlock * 2000; // a few thousand IOPS gives a batch size around a second. + for (long batchStart = range.getLeft(); batchStart <= range.getRight(); batchStart = batchStart + batchSize) { + long batchEnd = batchStart + batchSize; + ourLog.info("Migrating client-assigned ids for pids: {}-{}", batchStart, batchEnd); + + // This should be fast-ish since fhir_id isn't indexed yet, + // and we're walking both hfj_resource and hfj_forced_id in insertion order. + executeSql( + "hfj_resource", + "update hfj_resource " + "set fhir_id = coalesce( " + + // use first non-null value: forced_id if present, otherwise res_id + " (select f.forced_id from hfj_forced_id f where f.resource_pid = res_id), " + + " cast(res_id as char(64)) " + + " ) " + + "where fhir_id is null " + + "and res_id >= ? and res_id < ?", + batchStart, + batchEnd); + } + } + + @Override + protected void generateHashCode(HashCodeBuilder theBuilder) { + // no-op - this is a singleton. + } + + @Override + protected void generateEquals(EqualsBuilder theBuilder, BaseTask theOtherObject) { + // no-op - this is a singleton. + } +} diff --git a/hapi-fhir-sql-migrate/src/main/java/ca/uhn/fhir/jpa/migrate/taskdef/ForeignKeyContainer.java b/hapi-fhir-sql-migrate/src/main/java/ca/uhn/fhir/jpa/migrate/taskdef/ForeignKeyContainer.java index b0846ee464c..345e79a963d 100644 --- a/hapi-fhir-sql-migrate/src/main/java/ca/uhn/fhir/jpa/migrate/taskdef/ForeignKeyContainer.java +++ b/hapi-fhir-sql-migrate/src/main/java/ca/uhn/fhir/jpa/migrate/taskdef/ForeignKeyContainer.java @@ -21,8 +21,7 @@ package ca.uhn.fhir.jpa.migrate.taskdef; import ca.uhn.fhir.i18n.Msg; import ca.uhn.fhir.jpa.migrate.DriverTypeEnum; - -import javax.annotation.Nonnull; +import jakarta.annotation.Nonnull; public class ForeignKeyContainer { diff --git a/hapi-fhir-sql-migrate/src/main/java/ca/uhn/fhir/jpa/migrate/tasks/SchemaInitializationProvider.java b/hapi-fhir-sql-migrate/src/main/java/ca/uhn/fhir/jpa/migrate/tasks/SchemaInitializationProvider.java index 68b3b51733b..9feddafa381 100644 --- a/hapi-fhir-sql-migrate/src/main/java/ca/uhn/fhir/jpa/migrate/tasks/SchemaInitializationProvider.java +++ b/hapi-fhir-sql-migrate/src/main/java/ca/uhn/fhir/jpa/migrate/tasks/SchemaInitializationProvider.java @@ -25,6 +25,7 @@ import ca.uhn.fhir.jpa.migrate.DriverTypeEnum; import ca.uhn.fhir.jpa.migrate.tasks.api.ISchemaInitializationProvider; import com.google.common.annotations.VisibleForTesting; import com.google.common.base.Charsets; +import jakarta.annotation.Nonnull; import org.apache.commons.io.IOUtils; import org.apache.commons.lang3.builder.HashCodeBuilder; @@ -33,7 +34,6 @@ import java.io.InputStream; import java.util.ArrayList; import java.util.Collections; import java.util.List; -import javax.annotation.Nonnull; import static org.apache.commons.lang3.StringUtils.isBlank; import static org.apache.commons.lang3.StringUtils.trim; diff --git a/hapi-fhir-sql-migrate/src/main/java/ca/uhn/fhir/jpa/migrate/tasks/api/BaseMigrationTasks.java b/hapi-fhir-sql-migrate/src/main/java/ca/uhn/fhir/jpa/migrate/tasks/api/BaseMigrationTasks.java index e5976112e0c..bc8fa10689d 100644 --- a/hapi-fhir-sql-migrate/src/main/java/ca/uhn/fhir/jpa/migrate/tasks/api/BaseMigrationTasks.java +++ b/hapi-fhir-sql-migrate/src/main/java/ca/uhn/fhir/jpa/migrate/tasks/api/BaseMigrationTasks.java @@ -24,12 +24,12 @@ import ca.uhn.fhir.jpa.migrate.MigrationTaskList; import ca.uhn.fhir.jpa.migrate.taskdef.BaseTask; import com.google.common.collect.Multimap; import com.google.common.collect.MultimapBuilder; +import jakarta.annotation.Nonnull; import org.apache.commons.lang3.EnumUtils; import org.apache.commons.lang3.Validate; import org.flywaydb.core.api.MigrationVersion; import java.util.Collection; -import javax.annotation.Nonnull; public class BaseMigrationTasks { MigrationVersion lastVersion; diff --git a/hapi-fhir-sql-migrate/src/main/java/ca/uhn/fhir/jpa/migrate/tasks/api/Builder.java b/hapi-fhir-sql-migrate/src/main/java/ca/uhn/fhir/jpa/migrate/tasks/api/Builder.java index ad830f51f11..2c0bbf25fc2 100644 --- a/hapi-fhir-sql-migrate/src/main/java/ca/uhn/fhir/jpa/migrate/tasks/api/Builder.java +++ b/hapi-fhir-sql-migrate/src/main/java/ca/uhn/fhir/jpa/migrate/tasks/api/Builder.java @@ -21,6 +21,7 @@ package ca.uhn.fhir.jpa.migrate.tasks.api; import ca.uhn.fhir.i18n.Msg; import ca.uhn.fhir.jpa.migrate.DriverTypeEnum; +import ca.uhn.fhir.jpa.migrate.MigrationJdbcUtils; import ca.uhn.fhir.jpa.migrate.taskdef.AddColumnTask; import ca.uhn.fhir.jpa.migrate.taskdef.AddForeignKeyTask; import ca.uhn.fhir.jpa.migrate.taskdef.AddIdGeneratorTask; @@ -36,6 +37,7 @@ import ca.uhn.fhir.jpa.migrate.taskdef.DropIdGeneratorTask; import ca.uhn.fhir.jpa.migrate.taskdef.DropIndexTask; import ca.uhn.fhir.jpa.migrate.taskdef.DropTableTask; import ca.uhn.fhir.jpa.migrate.taskdef.ExecuteRawSqlTask; +import ca.uhn.fhir.jpa.migrate.taskdef.ExecuteTaskPrecondition; import ca.uhn.fhir.jpa.migrate.taskdef.InitializeSchemaTask; import ca.uhn.fhir.jpa.migrate.taskdef.MigratePostgresTextClobToBinaryClobTask; import ca.uhn.fhir.jpa.migrate.taskdef.ModifyColumnTask; @@ -44,6 +46,8 @@ import ca.uhn.fhir.jpa.migrate.taskdef.RenameColumnTask; import ca.uhn.fhir.jpa.migrate.taskdef.RenameIndexTask; import org.apache.commons.lang3.Validate; import org.intellij.lang.annotations.Language; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; import java.util.Arrays; import java.util.Collections; @@ -54,6 +58,7 @@ import java.util.Set; import java.util.stream.Collectors; public class Builder { + private static final Logger ourLog = LoggerFactory.getLogger(Builder.class); private final String myRelease; private final BaseMigrationTasks.IAcceptsTasks mySink; @@ -571,6 +576,40 @@ public class Builder { return this; } + /** + * Introduce precondition checking logic into the execution of the enclosed task. This conditional logic will + * be implemented by running an SQL SELECT (including CTEs) to obtain a boolean indicating whether a certain + * condition has been met. + * One example is to check for a specific collation on a column to decide whether to create a new index. + *

    + * This method may be called multiple times to add multiple preconditions. The precondition that evaluates to + * false will stop execution of the task irrespective of any or all other tasks evaluating to true. + * + * @param theSql The SELECT or CTE used to determine if the precondition is valid. + * @param reason A String to indicate the text that is logged if the precondition is not met. + * @return The BuilderCompleteTask in order to chain further method calls on this builder. + */ + public BuilderCompleteTask onlyIf(@Language("SQL") String theSql, String reason) { + if (!theSql.toUpperCase().startsWith("WITH") + && !theSql.toUpperCase().startsWith("SELECT")) { + throw new IllegalArgumentException(Msg.code(2455) + + String.format( + "Only SELECT statements (including CTEs) are allowed here. Please check your SQL: [%s]", + theSql)); + } + ourLog.info("SQL to evaluate: {}", theSql); + + myTask.addPrecondition(new ExecuteTaskPrecondition( + () -> { + ourLog.info("Checking precondition for SQL: {}", theSql); + return MigrationJdbcUtils.queryForSingleBooleanResultMultipleThrowsException( + theSql, myTask.newJdbcTemplate()); + }, + reason)); + + return this; + } + public BuilderCompleteTask runEvenDuringSchemaInitialization() { myTask.setRunDuringSchemaInitialization(true); return this; @@ -633,4 +672,8 @@ public class Builder { return this; } } + + public String getRelease() { + return myRelease; + } } diff --git a/hapi-fhir-sql-migrate/src/test/java/ca/uhn/fhir/jpa/migrate/HapiMigratorIT.java b/hapi-fhir-sql-migrate/src/test/java/ca/uhn/fhir/jpa/migrate/HapiMigratorIT.java index 47ebb9cfbab..f8ad7225e96 100644 --- a/hapi-fhir-sql-migrate/src/test/java/ca/uhn/fhir/jpa/migrate/HapiMigratorIT.java +++ b/hapi-fhir-sql-migrate/src/test/java/ca/uhn/fhir/jpa/migrate/HapiMigratorIT.java @@ -16,7 +16,7 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.jdbc.core.JdbcTemplate; -import javax.annotation.Nonnull; +import jakarta.annotation.Nonnull; import java.util.List; import java.util.UUID; import java.util.concurrent.ExecutionException; diff --git a/hapi-fhir-sql-migrate/src/test/java/ca/uhn/fhir/jpa/migrate/MigrationTaskSkipperTest.java b/hapi-fhir-sql-migrate/src/test/java/ca/uhn/fhir/jpa/migrate/MigrationTaskSkipperTest.java index 2c1fa653e6e..cc660122408 100644 --- a/hapi-fhir-sql-migrate/src/test/java/ca/uhn/fhir/jpa/migrate/MigrationTaskSkipperTest.java +++ b/hapi-fhir-sql-migrate/src/test/java/ca/uhn/fhir/jpa/migrate/MigrationTaskSkipperTest.java @@ -5,7 +5,7 @@ import ca.uhn.fhir.jpa.migrate.taskdef.DropIndexTask; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; -import javax.annotation.Nonnull; +import jakarta.annotation.Nonnull; import java.util.ArrayList; import java.util.List; import java.util.Set; diff --git a/hapi-fhir-sql-migrate/src/test/java/ca/uhn/fhir/jpa/migrate/SchemaMigratorTest.java b/hapi-fhir-sql-migrate/src/test/java/ca/uhn/fhir/jpa/migrate/SchemaMigratorTest.java index df2d9cfa02b..0df4e68d27e 100644 --- a/hapi-fhir-sql-migrate/src/test/java/ca/uhn/fhir/jpa/migrate/SchemaMigratorTest.java +++ b/hapi-fhir-sql-migrate/src/test/java/ca/uhn/fhir/jpa/migrate/SchemaMigratorTest.java @@ -12,7 +12,7 @@ import org.hamcrest.Matchers; import org.junit.jupiter.params.ParameterizedTest; import org.junit.jupiter.params.provider.MethodSource; -import javax.annotation.Nonnull; +import jakarta.annotation.Nonnull; import java.sql.SQLException; import java.util.List; import java.util.Properties; diff --git a/hapi-fhir-sql-migrate/src/test/java/ca/uhn/fhir/jpa/migrate/taskdef/AddColumnTest.java b/hapi-fhir-sql-migrate/src/test/java/ca/uhn/fhir/jpa/migrate/taskdef/AddColumnTest.java index 77620832c74..7cae6c1ae7e 100644 --- a/hapi-fhir-sql-migrate/src/test/java/ca/uhn/fhir/jpa/migrate/taskdef/AddColumnTest.java +++ b/hapi-fhir-sql-migrate/src/test/java/ca/uhn/fhir/jpa/migrate/taskdef/AddColumnTest.java @@ -77,7 +77,7 @@ public class AddColumnTest extends BaseTest { @ParameterizedTest(name = "{index}: {0}") @MethodSource("data") - public void testAddColumnToNonExistantTable_Failing(Supplier theTestDatabaseDetails) { + public void testAddColumnToNonExistentTable_Failing(Supplier theTestDatabaseDetails) { before(theTestDatabaseDetails); BaseMigrationTasks tasks = new BaseMigrationTasks<>(); diff --git a/hapi-fhir-sql-migrate/src/test/java/ca/uhn/fhir/jpa/migrate/taskdef/AddTableByColumnTaskTest.java b/hapi-fhir-sql-migrate/src/test/java/ca/uhn/fhir/jpa/migrate/taskdef/AddTableByColumnTaskTest.java index 06e1bbbf766..f416ed8f66e 100644 --- a/hapi-fhir-sql-migrate/src/test/java/ca/uhn/fhir/jpa/migrate/taskdef/AddTableByColumnTaskTest.java +++ b/hapi-fhir-sql-migrate/src/test/java/ca/uhn/fhir/jpa/migrate/taskdef/AddTableByColumnTaskTest.java @@ -5,16 +5,20 @@ import ca.uhn.fhir.jpa.migrate.JdbcUtils; import ca.uhn.fhir.jpa.migrate.tasks.api.BaseMigrationTasks; import ca.uhn.fhir.jpa.migrate.tasks.api.Builder; import ca.uhn.fhir.util.VersionEnum; +import org.junit.jupiter.api.Test; import org.junit.jupiter.params.ParameterizedTest; import org.junit.jupiter.params.provider.MethodSource; import java.sql.SQLException; +import java.util.Collections; +import java.util.Comparator; import java.util.Set; import java.util.function.Supplier; import java.util.stream.Collectors; import static org.hamcrest.MatcherAssert.assertThat; import static org.hamcrest.Matchers.containsInAnyOrder; +import static org.hamcrest.Matchers.is; public class AddTableByColumnTaskTest extends BaseTest { @@ -42,6 +46,96 @@ public class AddTableByColumnTaskTest extends BaseTest { assertThat(indexes.toString(), indexes, containsInAnyOrder("IDX_BONJOUR")); } + @Test + public void testLowercaseColumnsNoOverridesDefaultSorting() { + final String tableName = "table_3_columns"; + final String columnName1 = "a_column"; + final String columnName3 = "z_column"; + final String columnNameId = "id"; + final DriverTypeEnum driverType = DriverTypeEnum.MSSQL_2012; + final ColumnTypeEnum columnType = ColumnTypeEnum.STRING; + + final AddTableByColumnTask addTableByColumnTask = new AddTableByColumnTask(); + addTableByColumnTask.setTableName(tableName); + addTableByColumnTask.setDriverType(driverType); + addTableByColumnTask.setPkColumns(Collections.singletonList(columnNameId)); + + addTableByColumnTask.addAddColumnTask(buildAddColumnTask(driverType, columnType, tableName, columnName3, true, 10, Collections.emptySet())); + addTableByColumnTask.addAddColumnTask(buildAddColumnTask(driverType, columnType, tableName, columnNameId, false, 25, Collections.emptySet())); + addTableByColumnTask.addAddColumnTask(buildAddColumnTask(driverType, columnType, tableName, columnName1, true, 20, Collections.emptySet())); + + final String actualCreateTableSql = addTableByColumnTask.generateSQLCreateScript(); + assertThat("CREATE TABLE table_3_columns ( z_column varchar(10), id varchar(25) not null, a_column varchar(20), PRIMARY KEY (id) )", is(actualCreateTableSql));; + } + + @Test + public void testLowercaseColumnsNvarcharOverrideDefaultSorting() { + final String tableName = "table_3_columns"; + final String columnName1 = "a_column"; + final String columnName3 = "z_column"; + final String columnNameId = "id"; + final DriverTypeEnum driverType = DriverTypeEnum.MSSQL_2012; + final ColumnTypeEnum columnType = ColumnTypeEnum.STRING; + final ColumnDriverMappingOverride override = new ColumnDriverMappingOverride(columnType, driverType, "nvarchar(?)"); + + final AddTableByColumnTask addTableByColumnTask = new AddTableByColumnTask(); + addTableByColumnTask.setTableName(tableName); + addTableByColumnTask.setDriverType(driverType); + addTableByColumnTask.setPkColumns(Collections.singletonList(columnNameId)); + + addTableByColumnTask.addAddColumnTask(buildAddColumnTask(driverType, columnType, tableName, columnName3, true, 10, Collections.singleton(override))); + addTableByColumnTask.addAddColumnTask(buildAddColumnTask(driverType, columnType, tableName, columnNameId, false, 25, Collections.singleton(override))); + addTableByColumnTask.addAddColumnTask(buildAddColumnTask(driverType, columnType, tableName, columnName1, true, 20, Collections.singleton(override))); + + final String actualCreateTableSql = addTableByColumnTask.generateSQLCreateScript(); + assertThat("CREATE TABLE table_3_columns ( z_column nvarchar(10), id nvarchar(25) not null, a_column nvarchar(20), PRIMARY KEY (id) )", is(actualCreateTableSql));; + } + + @Test + public void testLowercaseColumnsNoOverridesCustomSorting() { + final String tableName = "table_4_columns"; + final String columnName1 = "a_column"; + final String columnName2 = "b_column"; + final String columnName3 = "z_column"; + final String columnNameId = "id"; + final DriverTypeEnum driverType = DriverTypeEnum.MSSQL_2012; + final ColumnTypeEnum columnType = ColumnTypeEnum.STRING; + final ColumnDriverMappingOverride override = new ColumnDriverMappingOverride(columnType, driverType, "nvarchar(?)"); + final Comparator comparator = (theTask1, theTask2) -> { + if (columnNameId.equals(theTask1.getColumnName())) { + return -1; + } + + return theTask1.getColumnName().compareTo(theTask2.getColumnName()); + }; + + final AddTableByColumnTask addTableByColumnTask = new AddTableByColumnTask(comparator); + addTableByColumnTask.setTableName(tableName); + addTableByColumnTask.setDriverType(driverType); + addTableByColumnTask.setPkColumns(Collections.singletonList(columnNameId)); + + addTableByColumnTask.addAddColumnTask(buildAddColumnTask(driverType, columnType, tableName, columnName3, true, 10, Collections.singleton(override))); + addTableByColumnTask.addAddColumnTask(buildAddColumnTask(driverType, columnType, tableName, columnName2, false, 15, Collections.singleton(override))); + addTableByColumnTask.addAddColumnTask(buildAddColumnTask(driverType, columnType, tableName, columnName1, true, 20, Collections.singleton(override))); + addTableByColumnTask.addAddColumnTask(buildAddColumnTask(driverType, columnType, tableName, columnNameId, false, 25, Collections.singleton(override))); + + final String actualCreateTableSql = addTableByColumnTask.generateSQLCreateScript(); + assertThat("CREATE TABLE table_4_columns ( id nvarchar(25) not null, a_column nvarchar(20), b_column nvarchar(15) not null, z_column nvarchar(10), PRIMARY KEY (id) )", is(actualCreateTableSql));; + } + + private static AddColumnTask buildAddColumnTask(DriverTypeEnum theDriverTypeEnum, ColumnTypeEnum theColumnTypeEnum, String theTableName, String theColumnName, boolean theNullable, int theColumnLength, Set theColumnDriverMappingOverrides) { + final AddColumnTask task = AddColumnTask.lowerCase(theColumnDriverMappingOverrides); + + task.setTableName(theTableName); + task.setColumnName(theColumnName); + task.setColumnType(theColumnTypeEnum); + task.setDriverType(theDriverTypeEnum); + task.setNullable(theNullable); + task.setColumnLength(theColumnLength); + + return task; + } + private static class MyMigrationTasks extends BaseMigrationTasks { public MyMigrationTasks() { Builder v = forVersion(VersionEnum.V3_5_0); diff --git a/hapi-fhir-sql-migrate/src/test/java/ca/uhn/fhir/jpa/migrate/taskdef/BaseTest.java b/hapi-fhir-sql-migrate/src/test/java/ca/uhn/fhir/jpa/migrate/taskdef/BaseTest.java index 1000008fde8..b729e82bafb 100644 --- a/hapi-fhir-sql-migrate/src/test/java/ca/uhn/fhir/jpa/migrate/taskdef/BaseTest.java +++ b/hapi-fhir-sql-migrate/src/test/java/ca/uhn/fhir/jpa/migrate/taskdef/BaseTest.java @@ -7,8 +7,10 @@ import ca.uhn.fhir.jpa.migrate.JdbcUtils; import ca.uhn.fhir.jpa.migrate.SchemaMigrator; import ca.uhn.fhir.jpa.migrate.dao.HapiMigrationDao; import org.apache.commons.dbcp2.BasicDataSource; +import org.h2.Driver; import org.intellij.lang.annotations.Language; import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.params.provider.Arguments; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.jdbc.core.ColumnMapRowMapper; @@ -25,8 +27,53 @@ import java.util.stream.Stream; public abstract class BaseTest { private static final String DATABASE_NAME = "DATABASE"; + static final String H2 = "H2"; + static final String DERBY = "Derby"; private static final Logger ourLog = LoggerFactory.getLogger(BaseTest.class); private static int ourDatabaseUrl = 0; + private static final Supplier TEST_DATABASE_DETAILS_DERBY_SUPPLIER = new Supplier<>() { + @Override + public TestDatabaseDetails get() { + ourLog.info("Derby: {}", DriverTypeEnum.DERBY_EMBEDDED.getDriverClassName()); + + String url = "jdbc:derby:memory:" + DATABASE_NAME + ourDatabaseUrl++ + ";create=true"; + DriverTypeEnum.ConnectionProperties connectionProperties = DriverTypeEnum.DERBY_EMBEDDED.newConnectionProperties(url, "SA", "SA"); + BasicDataSource dataSource = new BasicDataSource(); + dataSource.setUrl(url); + dataSource.setUsername("SA"); + dataSource.setPassword("SA"); + dataSource.setDriverClassName(DriverTypeEnum.DERBY_EMBEDDED.getDriverClassName()); + HapiMigrator migrator = new HapiMigrator(SchemaMigrator.HAPI_FHIR_MIGRATION_TABLENAME, dataSource, DriverTypeEnum.DERBY_EMBEDDED); + return new TestDatabaseDetails(url, connectionProperties, dataSource, migrator); + } + + @Override + public String toString() { + return DERBY; + } + }; + + private static final Supplier TEST_DATABASE_DETAILS_H2_SUPPLIER = new Supplier<>() { + @Override + public TestDatabaseDetails get() { + ourLog.info("H2: {}", Driver.class); + String url = "jdbc:h2:mem:" + DATABASE_NAME + ourDatabaseUrl++; + DriverTypeEnum.ConnectionProperties connectionProperties = DriverTypeEnum.H2_EMBEDDED.newConnectionProperties(url, "SA", "SA"); + BasicDataSource dataSource = new BasicDataSource(); + dataSource.setUrl(url); + dataSource.setUsername("SA"); + dataSource.setPassword("SA"); + dataSource.setDriverClassName(DriverTypeEnum.H2_EMBEDDED.getDriverClassName()); + HapiMigrator migrator = new HapiMigrator(SchemaMigrator.HAPI_FHIR_MIGRATION_TABLENAME, dataSource, DriverTypeEnum.H2_EMBEDDED); + return new TestDatabaseDetails(url, connectionProperties, dataSource, migrator); + } + + @Override + public String toString() { + return H2; + } + }; + private BasicDataSource myDataSource; private String myUrl; private HapiMigrator myMigrator; @@ -34,54 +81,28 @@ public abstract class BaseTest { protected HapiMigrationDao myHapiMigrationDao; protected HapiMigrationStorageSvc myHapiMigrationStorageSvc; + public static Stream dataWithEvaluationResults() { + return Stream.of( + Arguments.of(TEST_DATABASE_DETAILS_H2_SUPPLIER, List.of(true, true), true), + Arguments.of(TEST_DATABASE_DETAILS_H2_SUPPLIER, List.of(false, true), false), + Arguments.of(TEST_DATABASE_DETAILS_H2_SUPPLIER, List.of(true, false), false), + Arguments.of(TEST_DATABASE_DETAILS_H2_SUPPLIER, List.of(false, false), false), + Arguments.of(TEST_DATABASE_DETAILS_DERBY_SUPPLIER, List.of(true, true), true), + Arguments.of(TEST_DATABASE_DETAILS_DERBY_SUPPLIER, List.of(false, true), false), + Arguments.of(TEST_DATABASE_DETAILS_DERBY_SUPPLIER, List.of(true, false), false), + Arguments.of(TEST_DATABASE_DETAILS_DERBY_SUPPLIER, List.of(false, false), false) + ); + } + public static Stream> data() { ArrayList> retVal = new ArrayList<>(); // H2 - retVal.add(new Supplier() { - @Override - public TestDatabaseDetails get() { - ourLog.info("H2: {}", org.h2.Driver.class.toString()); - String url = "jdbc:h2:mem:" + DATABASE_NAME + ourDatabaseUrl++; - DriverTypeEnum.ConnectionProperties connectionProperties = DriverTypeEnum.H2_EMBEDDED.newConnectionProperties(url, "SA", "SA"); - BasicDataSource dataSource = new BasicDataSource(); - dataSource.setUrl(url); - dataSource.setUsername("SA"); - dataSource.setPassword("SA"); - dataSource.setDriverClassName(DriverTypeEnum.H2_EMBEDDED.getDriverClassName()); - HapiMigrator migrator = new HapiMigrator(SchemaMigrator.HAPI_FHIR_MIGRATION_TABLENAME, dataSource, DriverTypeEnum.H2_EMBEDDED); - return new TestDatabaseDetails(url, connectionProperties, dataSource, migrator); - } - - @Override - public String toString() { - return "H2"; - } - }); + retVal.add(TEST_DATABASE_DETAILS_H2_SUPPLIER); // Derby - retVal.add(new Supplier() { - @Override - public TestDatabaseDetails get() { - ourLog.info("Derby: {}", DriverTypeEnum.DERBY_EMBEDDED.getDriverClassName()); - - String url = "jdbc:derby:memory:" + DATABASE_NAME + ourDatabaseUrl++ + ";create=true"; - DriverTypeEnum.ConnectionProperties connectionProperties = DriverTypeEnum.DERBY_EMBEDDED.newConnectionProperties(url, "SA", "SA"); - BasicDataSource dataSource = new BasicDataSource(); - dataSource.setUrl(url); - dataSource.setUsername("SA"); - dataSource.setPassword("SA"); - dataSource.setDriverClassName(DriverTypeEnum.DERBY_EMBEDDED.getDriverClassName()); - HapiMigrator migrator = new HapiMigrator(SchemaMigrator.HAPI_FHIR_MIGRATION_TABLENAME, dataSource, DriverTypeEnum.DERBY_EMBEDDED); - return new TestDatabaseDetails(url, connectionProperties, dataSource, migrator); - } - - @Override - public String toString() { - return "Derby"; - } - }); + retVal.add(TEST_DATABASE_DETAILS_DERBY_SUPPLIER); return retVal.stream(); } diff --git a/hapi-fhir-sql-migrate/src/test/java/ca/uhn/fhir/jpa/migrate/taskdef/ExecuteRawSqlTaskTest.java b/hapi-fhir-sql-migrate/src/test/java/ca/uhn/fhir/jpa/migrate/taskdef/ExecuteRawSqlTaskTest.java index d2eba6920ed..55d513dc761 100644 --- a/hapi-fhir-sql-migrate/src/test/java/ca/uhn/fhir/jpa/migrate/taskdef/ExecuteRawSqlTaskTest.java +++ b/hapi-fhir-sql-migrate/src/test/java/ca/uhn/fhir/jpa/migrate/taskdef/ExecuteRawSqlTaskTest.java @@ -2,9 +2,12 @@ package ca.uhn.fhir.jpa.migrate.taskdef; import ca.uhn.fhir.jpa.migrate.DriverTypeEnum; import ca.uhn.fhir.jpa.migrate.tasks.api.BaseMigrationTasks; +import ca.uhn.fhir.jpa.migrate.tasks.api.Builder; import ca.uhn.fhir.util.VersionEnum; import org.junit.jupiter.params.ParameterizedTest; import org.junit.jupiter.params.provider.MethodSource; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; import java.util.HashMap; import java.util.List; @@ -12,9 +15,11 @@ import java.util.Map; import java.util.function.Supplier; import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertTrue; +import static org.junit.jupiter.api.Assertions.fail; public class ExecuteRawSqlTaskTest extends BaseTest { - + private static final Logger ourLog = LoggerFactory.getLogger(ExecuteRawSqlTaskTest.class); @ParameterizedTest(name = "{index}: {0}") @MethodSource("data") @@ -135,4 +140,100 @@ public class ExecuteRawSqlTaskTest extends BaseTest { assertEquals(0, output.size()); } + + @ParameterizedTest() + @MethodSource("dataWithEvaluationResults") + public void testExecuteRawSqlTaskWithPrecondition(Supplier theTestDatabaseDetails, List thePreconditionOutcomes, boolean theIsExecutionExpected) { + before(theTestDatabaseDetails); + executeSql("create table SOMETABLE (PID bigint not null, TEXTCOL varchar(255))"); + + final List> outputPreMigrate = executeQuery("SELECT PID,TEXTCOL FROM SOMETABLE"); + + assertTrue(outputPreMigrate.isEmpty()); + + final String someFakeUpdateSql = "INSERT INTO SOMETABLE (PID, TEXTCOL) VALUES (123, 'abc')"; + final String someReason = "I dont feel like it!"; + + final BaseMigrationTasks tasks = new BaseMigrationTasks<>(); + + final Builder.BuilderCompleteTask builderCompleteTask = tasks.forVersion(VersionEnum.V4_0_0) + .executeRawSql("2024.02", someFakeUpdateSql); + + for (boolean preconditionOutcome: thePreconditionOutcomes) { + final String someFakeSelectSql = + String.format("SELECT %s %s", preconditionOutcome, + (BaseTest.DERBY.equals(theTestDatabaseDetails.toString())) ? "FROM SYSIBM.SYSDUMMY1" : ""); + builderCompleteTask.onlyIf(someFakeSelectSql, someReason); + } + + getMigrator().addTasks(tasks.getTaskList(VersionEnum.V0_1, VersionEnum.V4_0_0)); + getMigrator().migrate(); + + final List> outputPostMigrate = executeQuery("SELECT PID,TEXTCOL FROM SOMETABLE"); + + if (theIsExecutionExpected) { + assertEquals(1, outputPostMigrate.size()); + assertEquals(123L, outputPostMigrate.get(0).get("PID")); + assertEquals("abc", outputPostMigrate.get(0).get("TEXTCOL")); + } else { + assertTrue(outputPreMigrate.isEmpty()); + } + } + + @ParameterizedTest() + @MethodSource("data") + public void testExecuteRawSqlTaskWithPreconditionInvalidPreconditionSql(Supplier theTestDatabaseDetails) { + before(theTestDatabaseDetails); + executeSql("create table SOMETABLE (PID bigint not null, TEXTCOL varchar(255))"); + + final List> outputPreMigrate = executeQuery("SELECT PID,TEXTCOL FROM SOMETABLE"); + + assertTrue(outputPreMigrate.isEmpty()); + + final String someFakeUpdateSql = "INSERT INTO SOMETABLE (PID, TEXTCOL) VALUES (123, 'abc')"; + final String someFakeSelectSql = "UPDATE SOMETABLE SET PID = 1"; + final String someReason = "I dont feel like it!"; + + try { + final BaseMigrationTasks tasks = new BaseMigrationTasks<>(); + tasks.forVersion(VersionEnum.V4_0_0) + .executeRawSql("2024.02", someFakeUpdateSql) + .onlyIf(someFakeSelectSql, someReason); + + fail(); + } catch (IllegalArgumentException exception) { + assertEquals("HAPI-2455: Only SELECT statements (including CTEs) are allowed here. Please check your SQL: [UPDATE SOMETABLE SET PID = 1]", exception.getMessage()); + } + } + + @ParameterizedTest() + @MethodSource("data") + public void testExecuteRawSqlTaskWithPreconditionPreconditionSqlReturnsMultiple(Supplier theTestDatabaseDetails) { + before(theTestDatabaseDetails); + executeSql("create table SOMETABLE (PID bigint not null, TEXTCOL varchar(255))"); + executeSql("INSERT INTO SOMETABLE (PID, TEXTCOL) VALUES (123, 'abc')"); + executeSql("INSERT INTO SOMETABLE (PID, TEXTCOL) VALUES (456, 'def')"); + + final List> outputPreMigrate = executeQuery("SELECT PID,TEXTCOL FROM SOMETABLE"); + + assertEquals(2, outputPreMigrate.size()); + + final String someFakeUpdateSql = "INSERT INTO SOMETABLE (PID, TEXTCOL) VALUES (789, 'xyz')"; + final String someFakeSelectSql = "SELECT PID != 0 FROM SOMETABLE"; + final String someReason = "I dont feel like it!"; + + final BaseMigrationTasks tasks = new BaseMigrationTasks<>(); + + final Builder.BuilderCompleteTask builderCompleteTask = tasks.forVersion(VersionEnum.V4_0_0) + .executeRawSql("2024.02", someFakeUpdateSql); + builderCompleteTask.onlyIf(someFakeSelectSql, someReason); + + getMigrator().addTasks(tasks.getTaskList(VersionEnum.V0_1, VersionEnum.V4_0_0)); + try { + getMigrator().migrate(); + fail(); + } catch (IllegalArgumentException exception) { + assertEquals("HAPI-2474: Failure due to query returning more than one result: [true, true] for SQL: [SELECT PID != 0 FROM SOMETABLE].", exception.getMessage()); + } + } } diff --git a/hapi-fhir-sql-migrate/src/test/java/ca/uhn/fhir/jpa/migrate/taskdef/ModifyColumnTest.java b/hapi-fhir-sql-migrate/src/test/java/ca/uhn/fhir/jpa/migrate/taskdef/ModifyColumnTest.java index d9119765cf4..ce34d2fb6f0 100644 --- a/hapi-fhir-sql-migrate/src/test/java/ca/uhn/fhir/jpa/migrate/taskdef/ModifyColumnTest.java +++ b/hapi-fhir-sql-migrate/src/test/java/ca/uhn/fhir/jpa/migrate/taskdef/ModifyColumnTest.java @@ -6,7 +6,7 @@ import ca.uhn.fhir.jpa.migrate.JdbcUtils; import org.junit.jupiter.params.ParameterizedTest; import org.junit.jupiter.params.provider.MethodSource; -import javax.annotation.Nonnull; +import jakarta.annotation.Nonnull; import java.sql.SQLException; import java.util.function.Supplier; diff --git a/hapi-fhir-storage-batch2-jobs/pom.xml b/hapi-fhir-storage-batch2-jobs/pom.xml index 4dddaeb11f5..a7f5d8ecbaf 100644 --- a/hapi-fhir-storage-batch2-jobs/pom.xml +++ b/hapi-fhir-storage-batch2-jobs/pom.xml @@ -5,7 +5,7 @@ ca.uhn.hapi.fhir hapi-deployable-pom - 6.9.7-SNAPSHOT + 6.11.7-SNAPSHOT ../hapi-deployable-pom/pom.xml @@ -26,8 +26,8 @@ - javax.servlet - javax.servlet-api + jakarta.servlet + jakarta.servlet-api provided diff --git a/hapi-fhir-storage-batch2-jobs/src/main/java/ca/uhn/fhir/batch2/jobs/export/BulkDataExportProvider.java b/hapi-fhir-storage-batch2-jobs/src/main/java/ca/uhn/fhir/batch2/jobs/export/BulkDataExportProvider.java index 9fd2ef9d265..928c1f45f45 100644 --- a/hapi-fhir-storage-batch2-jobs/src/main/java/ca/uhn/fhir/batch2/jobs/export/BulkDataExportProvider.java +++ b/hapi-fhir-storage-batch2-jobs/src/main/java/ca/uhn/fhir/batch2/jobs/export/BulkDataExportProvider.java @@ -52,6 +52,7 @@ import ca.uhn.fhir.rest.api.server.bulk.BulkExportJobParameters; import ca.uhn.fhir.rest.server.RestfulServerUtils; import ca.uhn.fhir.rest.server.exceptions.InternalErrorException; import ca.uhn.fhir.rest.server.exceptions.InvalidRequestException; +import ca.uhn.fhir.rest.server.provider.ProviderConstants; import ca.uhn.fhir.rest.server.servlet.ServletRequestDetails; import ca.uhn.fhir.rest.server.util.CompositeInterceptorBroadcaster; import ca.uhn.fhir.util.ArrayUtil; @@ -61,6 +62,7 @@ import ca.uhn.fhir.util.OperationOutcomeUtil; import ca.uhn.fhir.util.SearchParameterUtil; import ca.uhn.fhir.util.UrlUtil; import com.google.common.annotations.VisibleForTesting; +import jakarta.servlet.http.HttpServletResponse; import org.apache.commons.lang3.StringUtils; import org.hl7.fhir.instance.model.api.IBaseOperationOutcome; import org.hl7.fhir.instance.model.api.IIdType; @@ -82,7 +84,6 @@ import java.util.List; import java.util.Map; import java.util.Set; import java.util.stream.Collectors; -import javax.servlet.http.HttpServletResponse; import static ca.uhn.fhir.rest.api.server.bulk.BulkExportJobParameters.ExportStyle; import static ca.uhn.fhir.util.DatatypeUtil.toStringValue; @@ -125,10 +126,11 @@ public class BulkDataExportProvider { * $export */ @Operation( - name = JpaConstants.OPERATION_EXPORT, + name = ProviderConstants.OPERATION_EXPORT, global = false /* set to true once we can handle this */, manualResponse = true, - idempotent = true) + idempotent = true, + canonicalUrl = "http://hl7.org/fhir/uv/bulkdata/OperationDefinition/export") public void export( @OperationParam(name = JpaConstants.PARAM_EXPORT_OUTPUT_FORMAT, min = 0, max = 1, typeName = "string") IPrimitiveType theOutputFormat, @@ -152,7 +154,7 @@ public class BulkDataExportProvider { IPrimitiveType theExportId, ServletRequestDetails theRequestDetails) { // JPA export provider - validatePreferAsyncHeader(theRequestDetails, JpaConstants.OPERATION_EXPORT); + validatePreferAsyncHeader(theRequestDetails, ProviderConstants.OPERATION_EXPORT); BulkExportJobParameters BulkExportJobParameters = buildSystemBulkExportOptions( theOutputFormat, theType, theSince, theTypeFilter, theExportId, theTypePostFetchFilterUrl); @@ -212,7 +214,12 @@ public class BulkDataExportProvider { /** * Group/[id]/$export */ - @Operation(name = JpaConstants.OPERATION_EXPORT, manualResponse = true, idempotent = true, typeName = "Group") + @Operation( + name = ProviderConstants.OPERATION_EXPORT, + manualResponse = true, + idempotent = true, + typeName = "Group", + canonicalUrl = "http://hl7.org/fhir/uv/bulkdata/OperationDefinition/group-export") public void groupExport( @IdParam IIdType theIdParam, @OperationParam(name = JpaConstants.PARAM_EXPORT_OUTPUT_FORMAT, min = 0, max = 1, typeName = "string") @@ -244,7 +251,7 @@ public class BulkDataExportProvider { ourLog.debug("_typeFilter={}", theTypeFilter); ourLog.debug("_mdm={}", theMdm); - validatePreferAsyncHeader(theRequestDetails, JpaConstants.OPERATION_EXPORT); + validatePreferAsyncHeader(theRequestDetails, ProviderConstants.OPERATION_EXPORT); // verify the Group exists before starting the job validateTargetsExists(theRequestDetails, "Group", List.of(theIdParam)); @@ -322,7 +329,12 @@ public class BulkDataExportProvider { /** * Patient/$export */ - @Operation(name = JpaConstants.OPERATION_EXPORT, manualResponse = true, idempotent = true, typeName = "Patient") + @Operation( + name = ProviderConstants.OPERATION_EXPORT, + manualResponse = true, + idempotent = true, + typeName = "Patient", + canonicalUrl = "http://hl7.org/fhir/uv/bulkdata/OperationDefinition/patient-export") public void patientExport( @OperationParam(name = JpaConstants.PARAM_EXPORT_OUTPUT_FORMAT, min = 0, max = 1, typeName = "string") IPrimitiveType theOutputFormat, @@ -351,7 +363,7 @@ public class BulkDataExportProvider { @OperationParam(name = JpaConstants.PARAM_EXPORT_IDENTIFIER, min = 0, max = 1, typeName = "string") IPrimitiveType theExportIdentifier, ServletRequestDetails theRequestDetails) { - validatePreferAsyncHeader(theRequestDetails, JpaConstants.OPERATION_EXPORT); + validatePreferAsyncHeader(theRequestDetails, ProviderConstants.OPERATION_EXPORT); if (thePatient != null) { validateTargetsExists( @@ -376,7 +388,11 @@ public class BulkDataExportProvider { /** * Patient/[id]/$export */ - @Operation(name = JpaConstants.OPERATION_EXPORT, manualResponse = true, idempotent = true, typeName = "Patient") + @Operation( + name = ProviderConstants.OPERATION_EXPORT, + manualResponse = true, + idempotent = true, + typeName = "Patient") public void patientInstanceExport( @IdParam IIdType theIdParam, @OperationParam(name = JpaConstants.PARAM_EXPORT_OUTPUT_FORMAT, min = 0, max = 1, typeName = "string") @@ -400,7 +416,7 @@ public class BulkDataExportProvider { @OperationParam(name = JpaConstants.PARAM_EXPORT_IDENTIFIER, min = 0, max = 1, typeName = "string") IPrimitiveType theExportIdentifier, ServletRequestDetails theRequestDetails) { - validatePreferAsyncHeader(theRequestDetails, JpaConstants.OPERATION_EXPORT); + validatePreferAsyncHeader(theRequestDetails, ProviderConstants.OPERATION_EXPORT); validateTargetsExists(theRequestDetails, "Patient", List.of(theIdParam)); @@ -422,7 +438,7 @@ public class BulkDataExportProvider { */ @SuppressWarnings("unchecked") @Operation( - name = JpaConstants.OPERATION_EXPORT_POLL_STATUS, + name = ProviderConstants.OPERATION_EXPORT_POLL_STATUS, manualResponse = true, idempotent = true, deleteEnabled = true) @@ -712,7 +728,7 @@ public class BulkDataExportProvider { if (serverBase == null) { throw new InternalErrorException(Msg.code(2136) + "Unable to get the server base."); } - String pollLocation = serverBase + "/" + JpaConstants.OPERATION_EXPORT_POLL_STATUS + "?" + String pollLocation = serverBase + "/" + ProviderConstants.OPERATION_EXPORT_POLL_STATUS + "?" + JpaConstants.PARAM_EXPORT_POLL_STATUS_JOB_ID + "=" + theInstanceId; pollLocation = UrlUtil.sanitizeHeaderValue(pollLocation); diff --git a/hapi-fhir-storage-batch2-jobs/src/main/java/ca/uhn/fhir/batch2/jobs/export/BulkExportCreateReportStep.java b/hapi-fhir-storage-batch2-jobs/src/main/java/ca/uhn/fhir/batch2/jobs/export/BulkExportCreateReportStep.java index 41a2f600f2a..868f744ed15 100644 --- a/hapi-fhir-storage-batch2-jobs/src/main/java/ca/uhn/fhir/batch2/jobs/export/BulkExportCreateReportStep.java +++ b/hapi-fhir-storage-batch2-jobs/src/main/java/ca/uhn/fhir/batch2/jobs/export/BulkExportCreateReportStep.java @@ -31,13 +31,13 @@ import ca.uhn.fhir.batch2.model.ChunkOutcome; import ca.uhn.fhir.batch2.model.JobInstance; import ca.uhn.fhir.jpa.api.model.BulkExportJobResults; import ca.uhn.fhir.rest.api.server.bulk.BulkExportJobParameters; +import jakarta.annotation.Nonnull; import org.slf4j.Logger; import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; -import javax.annotation.Nonnull; import static org.slf4j.LoggerFactory.getLogger; diff --git a/hapi-fhir-storage-batch2-jobs/src/main/java/ca/uhn/fhir/batch2/jobs/export/BulkExportJobParametersValidator.java b/hapi-fhir-storage-batch2-jobs/src/main/java/ca/uhn/fhir/batch2/jobs/export/BulkExportJobParametersValidator.java index caa330197a6..cace5dda943 100644 --- a/hapi-fhir-storage-batch2-jobs/src/main/java/ca/uhn/fhir/batch2/jobs/export/BulkExportJobParametersValidator.java +++ b/hapi-fhir-storage-batch2-jobs/src/main/java/ca/uhn/fhir/batch2/jobs/export/BulkExportJobParametersValidator.java @@ -28,13 +28,13 @@ import ca.uhn.fhir.rest.api.Constants; import ca.uhn.fhir.rest.api.server.RequestDetails; import ca.uhn.fhir.rest.api.server.bulk.BulkExportJobParameters; import ca.uhn.fhir.rest.server.exceptions.InvalidRequestException; +import jakarta.annotation.Nonnull; +import jakarta.annotation.Nullable; import org.apache.commons.lang3.StringUtils; import org.springframework.beans.factory.annotation.Autowired; import java.util.ArrayList; import java.util.List; -import javax.annotation.Nonnull; -import javax.annotation.Nullable; import static org.apache.commons.lang3.StringUtils.isBlank; diff --git a/hapi-fhir-storage-batch2-jobs/src/main/java/ca/uhn/fhir/batch2/jobs/export/ExpandResourceAndWriteBinaryStep.java b/hapi-fhir-storage-batch2-jobs/src/main/java/ca/uhn/fhir/batch2/jobs/export/ExpandResourceAndWriteBinaryStep.java index 15e2f052b72..63542528297 100644 --- a/hapi-fhir-storage-batch2-jobs/src/main/java/ca/uhn/fhir/batch2/jobs/export/ExpandResourceAndWriteBinaryStep.java +++ b/hapi-fhir-storage-batch2-jobs/src/main/java/ca/uhn/fhir/batch2/jobs/export/ExpandResourceAndWriteBinaryStep.java @@ -60,6 +60,7 @@ import ca.uhn.fhir.util.BinaryUtil; import ca.uhn.fhir.util.FhirTerser; import com.google.common.collect.ArrayListMultimap; import com.google.common.collect.ListMultimap; +import jakarta.annotation.Nonnull; import org.apache.commons.lang3.StringUtils; import org.hl7.fhir.instance.model.api.IBaseBinary; import org.hl7.fhir.instance.model.api.IBaseExtension; @@ -79,7 +80,6 @@ import java.util.List; import java.util.Optional; import java.util.Set; import java.util.stream.Collectors; -import javax.annotation.Nonnull; import static ca.uhn.fhir.rest.api.Constants.PARAM_ID; import static org.apache.commons.lang3.StringUtils.isNotBlank; diff --git a/hapi-fhir-storage-batch2-jobs/src/main/java/ca/uhn/fhir/batch2/jobs/export/ExpandResourcesStep.java b/hapi-fhir-storage-batch2-jobs/src/main/java/ca/uhn/fhir/batch2/jobs/export/ExpandResourcesStep.java index f0b228993b3..a06439c952f 100644 --- a/hapi-fhir-storage-batch2-jobs/src/main/java/ca/uhn/fhir/batch2/jobs/export/ExpandResourcesStep.java +++ b/hapi-fhir-storage-batch2-jobs/src/main/java/ca/uhn/fhir/batch2/jobs/export/ExpandResourcesStep.java @@ -50,6 +50,7 @@ import ca.uhn.fhir.rest.param.TokenOrListParam; import ca.uhn.fhir.rest.server.interceptor.ResponseTerminologyTranslationSvc; import com.google.common.collect.ArrayListMultimap; import com.google.common.collect.ListMultimap; +import jakarta.annotation.Nonnull; import org.hl7.fhir.instance.model.api.IBaseResource; import org.slf4j.Logger; import org.springframework.beans.factory.annotation.Autowired; @@ -61,7 +62,6 @@ import java.util.List; import java.util.Optional; import java.util.Set; import java.util.stream.Collectors; -import javax.annotation.Nonnull; import static ca.uhn.fhir.rest.api.Constants.PARAM_ID; import static org.slf4j.LoggerFactory.getLogger; diff --git a/hapi-fhir-storage-batch2-jobs/src/main/java/ca/uhn/fhir/batch2/jobs/export/FetchResourceIdsStep.java b/hapi-fhir-storage-batch2-jobs/src/main/java/ca/uhn/fhir/batch2/jobs/export/FetchResourceIdsStep.java index 62626d7e424..982f35894da 100644 --- a/hapi-fhir-storage-batch2-jobs/src/main/java/ca/uhn/fhir/batch2/jobs/export/FetchResourceIdsStep.java +++ b/hapi-fhir-storage-batch2-jobs/src/main/java/ca/uhn/fhir/batch2/jobs/export/FetchResourceIdsStep.java @@ -33,6 +33,7 @@ import ca.uhn.fhir.jpa.bulk.export.api.IBulkExportProcessor; import ca.uhn.fhir.jpa.bulk.export.model.ExportPIDIteratorParameters; import ca.uhn.fhir.rest.api.server.bulk.BulkExportJobParameters; import ca.uhn.fhir.rest.api.server.storage.IResourcePersistentId; +import jakarta.annotation.Nonnull; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; @@ -42,7 +43,6 @@ import java.util.HashSet; import java.util.Iterator; import java.util.List; import java.util.Set; -import javax.annotation.Nonnull; public class FetchResourceIdsStep implements IFirstJobStepWorker { private static final Logger ourLog = LoggerFactory.getLogger(FetchResourceIdsStep.class); diff --git a/hapi-fhir-storage-batch2-jobs/src/main/java/ca/uhn/fhir/batch2/jobs/export/WriteBinaryStep.java b/hapi-fhir-storage-batch2-jobs/src/main/java/ca/uhn/fhir/batch2/jobs/export/WriteBinaryStep.java index 265bf9a0b80..f639d31eb15 100644 --- a/hapi-fhir-storage-batch2-jobs/src/main/java/ca/uhn/fhir/batch2/jobs/export/WriteBinaryStep.java +++ b/hapi-fhir-storage-batch2-jobs/src/main/java/ca/uhn/fhir/batch2/jobs/export/WriteBinaryStep.java @@ -41,6 +41,7 @@ import ca.uhn.fhir.rest.api.server.bulk.BulkExportJobParameters; import ca.uhn.fhir.rest.server.exceptions.ResourceNotFoundException; import ca.uhn.fhir.util.BinaryUtil; import ca.uhn.fhir.util.FhirTerser; +import jakarta.annotation.Nonnull; import org.apache.commons.lang3.StringUtils; import org.hl7.fhir.instance.model.api.IBaseBinary; import org.hl7.fhir.instance.model.api.IBaseExtension; @@ -52,7 +53,6 @@ import org.springframework.beans.factory.annotation.Autowired; import java.io.ByteArrayOutputStream; import java.io.IOException; import java.io.OutputStreamWriter; -import javax.annotation.Nonnull; import static org.apache.commons.lang3.StringUtils.isNotBlank; import static org.slf4j.LoggerFactory.getLogger; diff --git a/hapi-fhir-storage-batch2-jobs/src/main/java/ca/uhn/fhir/batch2/jobs/expunge/DeleteExpungeJobParametersValidator.java b/hapi-fhir-storage-batch2-jobs/src/main/java/ca/uhn/fhir/batch2/jobs/expunge/DeleteExpungeJobParametersValidator.java index 8ac32866735..dd347ae0eb8 100644 --- a/hapi-fhir-storage-batch2-jobs/src/main/java/ca/uhn/fhir/batch2/jobs/expunge/DeleteExpungeJobParametersValidator.java +++ b/hapi-fhir-storage-batch2-jobs/src/main/java/ca/uhn/fhir/batch2/jobs/expunge/DeleteExpungeJobParametersValidator.java @@ -26,10 +26,10 @@ import ca.uhn.fhir.jpa.api.svc.IDeleteExpungeSvc; import ca.uhn.fhir.jpa.partition.IRequestPartitionHelperSvc; import ca.uhn.fhir.rest.api.server.RequestDetails; import ca.uhn.fhir.util.ValidateUtil; +import jakarta.annotation.Nonnull; +import jakarta.annotation.Nullable; import java.util.List; -import javax.annotation.Nonnull; -import javax.annotation.Nullable; public class DeleteExpungeJobParametersValidator implements IJobParametersValidator { private final IUrlListValidator myUrlListValidator; diff --git a/hapi-fhir-storage-batch2-jobs/src/main/java/ca/uhn/fhir/batch2/jobs/expunge/DeleteExpungeStep.java b/hapi-fhir-storage-batch2-jobs/src/main/java/ca/uhn/fhir/batch2/jobs/expunge/DeleteExpungeStep.java index 44bff8ef4ab..70abd6440c7 100644 --- a/hapi-fhir-storage-batch2-jobs/src/main/java/ca/uhn/fhir/batch2/jobs/expunge/DeleteExpungeStep.java +++ b/hapi-fhir-storage-batch2-jobs/src/main/java/ca/uhn/fhir/batch2/jobs/expunge/DeleteExpungeStep.java @@ -28,13 +28,13 @@ import ca.uhn.fhir.jpa.model.dao.JpaPid; import ca.uhn.fhir.rest.api.server.RequestDetails; import ca.uhn.fhir.rest.api.server.SystemRequestDetails; import ca.uhn.fhir.rest.api.server.storage.TransactionDetails; +import jakarta.annotation.Nonnull; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.transaction.TransactionStatus; import org.springframework.transaction.support.TransactionCallback; import java.util.List; -import javax.annotation.Nonnull; public class DeleteExpungeStep implements IJobStepWorker { diff --git a/hapi-fhir-storage-batch2-jobs/src/main/java/ca/uhn/fhir/batch2/jobs/importpull/BulkImportParameterValidator.java b/hapi-fhir-storage-batch2-jobs/src/main/java/ca/uhn/fhir/batch2/jobs/importpull/BulkImportParameterValidator.java index 0a287f635eb..fa5b6b7ef9a 100644 --- a/hapi-fhir-storage-batch2-jobs/src/main/java/ca/uhn/fhir/batch2/jobs/importpull/BulkImportParameterValidator.java +++ b/hapi-fhir-storage-batch2-jobs/src/main/java/ca/uhn/fhir/batch2/jobs/importpull/BulkImportParameterValidator.java @@ -24,13 +24,13 @@ import ca.uhn.fhir.batch2.importpull.models.Batch2BulkImportPullJobParameters; import ca.uhn.fhir.jpa.bulk.imprt.api.IBulkDataImportSvc; import ca.uhn.fhir.jpa.bulk.imprt.model.BulkImportJobJson; import ca.uhn.fhir.rest.api.server.RequestDetails; +import jakarta.annotation.Nonnull; +import jakarta.annotation.Nullable; import org.apache.commons.lang3.StringUtils; import org.slf4j.Logger; import java.util.ArrayList; import java.util.List; -import javax.annotation.Nonnull; -import javax.annotation.Nullable; import static org.slf4j.LoggerFactory.getLogger; diff --git a/hapi-fhir-storage-batch2-jobs/src/main/java/ca/uhn/fhir/batch2/jobs/importpull/FetchPartitionedFilesStep.java b/hapi-fhir-storage-batch2-jobs/src/main/java/ca/uhn/fhir/batch2/jobs/importpull/FetchPartitionedFilesStep.java index e1e9b2a595e..fcf2f53501b 100644 --- a/hapi-fhir-storage-batch2-jobs/src/main/java/ca/uhn/fhir/batch2/jobs/importpull/FetchPartitionedFilesStep.java +++ b/hapi-fhir-storage-batch2-jobs/src/main/java/ca/uhn/fhir/batch2/jobs/importpull/FetchPartitionedFilesStep.java @@ -29,10 +29,9 @@ import ca.uhn.fhir.batch2.importpull.models.Batch2BulkImportPullJobParameters; import ca.uhn.fhir.batch2.importpull.models.BulkImportFilePartitionResult; import ca.uhn.fhir.jpa.bulk.imprt.api.IBulkDataImportSvc; import ca.uhn.fhir.jpa.bulk.imprt.model.BulkImportJobJson; +import jakarta.annotation.Nonnull; import org.slf4j.Logger; -import javax.annotation.Nonnull; - import static org.slf4j.LoggerFactory.getLogger; public class FetchPartitionedFilesStep diff --git a/hapi-fhir-storage-batch2-jobs/src/main/java/ca/uhn/fhir/batch2/jobs/importpull/ReadInResourcesFromFileStep.java b/hapi-fhir-storage-batch2-jobs/src/main/java/ca/uhn/fhir/batch2/jobs/importpull/ReadInResourcesFromFileStep.java index f62e3aeaa15..88c57c7a90c 100644 --- a/hapi-fhir-storage-batch2-jobs/src/main/java/ca/uhn/fhir/batch2/jobs/importpull/ReadInResourcesFromFileStep.java +++ b/hapi-fhir-storage-batch2-jobs/src/main/java/ca/uhn/fhir/batch2/jobs/importpull/ReadInResourcesFromFileStep.java @@ -33,12 +33,12 @@ import ca.uhn.fhir.jpa.bulk.imprt.model.BulkImportJobFileJson; import ca.uhn.fhir.jpa.bulk.imprt.model.JobFileRowProcessingModeEnum; import ca.uhn.fhir.util.IoUtil; import com.google.common.io.LineReader; +import jakarta.annotation.Nonnull; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import java.io.IOException; import java.io.StringReader; -import javax.annotation.Nonnull; public class ReadInResourcesFromFileStep implements IJobStepWorker { diff --git a/hapi-fhir-storage-batch2-jobs/src/main/java/ca/uhn/fhir/batch2/jobs/importpull/WriteBundleForImportStep.java b/hapi-fhir-storage-batch2-jobs/src/main/java/ca/uhn/fhir/batch2/jobs/importpull/WriteBundleForImportStep.java index ee028fa5300..bab95053c7c 100644 --- a/hapi-fhir-storage-batch2-jobs/src/main/java/ca/uhn/fhir/batch2/jobs/importpull/WriteBundleForImportStep.java +++ b/hapi-fhir-storage-batch2-jobs/src/main/java/ca/uhn/fhir/batch2/jobs/importpull/WriteBundleForImportStep.java @@ -33,12 +33,11 @@ import ca.uhn.fhir.jpa.api.dao.IFhirSystemDao; import ca.uhn.fhir.jpa.bulk.imprt.model.JobFileRowProcessingModeEnum; import ca.uhn.fhir.parser.IParser; import ca.uhn.fhir.rest.api.server.SystemRequestDetails; +import jakarta.annotation.Nonnull; import org.hl7.fhir.instance.model.api.IBaseResource; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import javax.annotation.Nonnull; - public class WriteBundleForImportStep implements ILastJobStepWorker { diff --git a/hapi-fhir-storage-batch2-jobs/src/main/java/ca/uhn/fhir/batch2/jobs/imprt/BulkDataImportProvider.java b/hapi-fhir-storage-batch2-jobs/src/main/java/ca/uhn/fhir/batch2/jobs/imprt/BulkDataImportProvider.java index c75342c414b..1de6c2476be 100644 --- a/hapi-fhir-storage-batch2-jobs/src/main/java/ca/uhn/fhir/batch2/jobs/imprt/BulkDataImportProvider.java +++ b/hapi-fhir-storage-batch2-jobs/src/main/java/ca/uhn/fhir/batch2/jobs/imprt/BulkDataImportProvider.java @@ -38,6 +38,8 @@ import ca.uhn.fhir.util.OperationOutcomeUtil; import ca.uhn.fhir.util.ParametersUtil; import ca.uhn.fhir.util.UrlUtil; import ca.uhn.fhir.util.ValidateUtil; +import jakarta.annotation.Nonnull; +import jakarta.servlet.http.HttpServletResponse; import org.apache.commons.lang3.StringUtils; import org.apache.commons.lang3.tuple.Pair; import org.hl7.fhir.instance.model.api.IBase; @@ -56,8 +58,6 @@ import java.util.Comparator; import java.util.Date; import java.util.List; import java.util.Optional; -import javax.annotation.Nonnull; -import javax.servlet.http.HttpServletResponse; import static ca.uhn.fhir.batch2.jobs.export.BulkDataExportProvider.validatePreferAsyncHeader; import static org.apache.commons.lang3.StringUtils.isNotBlank; @@ -76,13 +76,10 @@ public class BulkDataImportProvider { public static final String PARAM_INPUT_TYPE = "type"; private static final Logger ourLog = LoggerFactory.getLogger(BulkDataImportProvider.class); - @Autowired private IJobCoordinator myJobCoordinator; - @Autowired private FhirContext myFhirCtx; - @Autowired private IRequestPartitionHelperSvc myRequestPartitionHelperService; private volatile List myResourceTypeOrder; @@ -94,14 +91,17 @@ public class BulkDataImportProvider { super(); } + @Autowired public void setJobCoordinator(IJobCoordinator theJobCoordinator) { myJobCoordinator = theJobCoordinator; } + @Autowired public void setFhirContext(FhirContext theCtx) { myFhirCtx = theCtx; } + @Autowired public void setRequestPartitionHelperService(IRequestPartitionHelperSvc theRequestPartitionHelperSvc) { myRequestPartitionHelperService = theRequestPartitionHelperSvc; } diff --git a/hapi-fhir-storage-batch2-jobs/src/main/java/ca/uhn/fhir/batch2/jobs/imprt/BulkImportFileServlet.java b/hapi-fhir-storage-batch2-jobs/src/main/java/ca/uhn/fhir/batch2/jobs/imprt/BulkImportFileServlet.java index c0579dd167d..6f69dfe9eb6 100644 --- a/hapi-fhir-storage-batch2-jobs/src/main/java/ca/uhn/fhir/batch2/jobs/imprt/BulkImportFileServlet.java +++ b/hapi-fhir-storage-batch2-jobs/src/main/java/ca/uhn/fhir/batch2/jobs/imprt/BulkImportFileServlet.java @@ -24,6 +24,9 @@ import ca.uhn.fhir.rest.api.Constants; import ca.uhn.fhir.rest.server.exceptions.BaseServerResponseException; import ca.uhn.fhir.rest.server.exceptions.ResourceNotFoundException; import ca.uhn.fhir.util.UrlUtil; +import jakarta.servlet.http.HttpServlet; +import jakarta.servlet.http.HttpServletRequest; +import jakarta.servlet.http.HttpServletResponse; import org.apache.commons.io.IOUtils; import org.apache.commons.io.input.ReaderInputStream; import org.slf4j.Logger; @@ -38,9 +41,6 @@ import java.nio.charset.StandardCharsets; import java.util.HashMap; import java.util.Map; import java.util.UUID; -import javax.servlet.http.HttpServlet; -import javax.servlet.http.HttpServletRequest; -import javax.servlet.http.HttpServletResponse; import static ca.uhn.fhir.rest.api.Constants.CHARSET_UTF8_CTSUFFIX; import static ca.uhn.fhir.rest.api.Constants.CT_FHIR_NDJSON; diff --git a/hapi-fhir-storage-batch2-jobs/src/main/java/ca/uhn/fhir/batch2/jobs/imprt/BulkImportJobParameters.java b/hapi-fhir-storage-batch2-jobs/src/main/java/ca/uhn/fhir/batch2/jobs/imprt/BulkImportJobParameters.java index 22077e858ac..4b4294cda39 100644 --- a/hapi-fhir-storage-batch2-jobs/src/main/java/ca/uhn/fhir/batch2/jobs/imprt/BulkImportJobParameters.java +++ b/hapi-fhir-storage-batch2-jobs/src/main/java/ca/uhn/fhir/batch2/jobs/imprt/BulkImportJobParameters.java @@ -22,15 +22,15 @@ package ca.uhn.fhir.batch2.jobs.imprt; import ca.uhn.fhir.interceptor.model.RequestPartitionId; import ca.uhn.fhir.model.api.IModelJson; import com.fasterxml.jackson.annotation.JsonProperty; +import jakarta.annotation.Nullable; +import jakarta.validation.constraints.Min; +import jakarta.validation.constraints.NotNull; +import jakarta.validation.constraints.Pattern; +import jakarta.validation.constraints.Size; import org.apache.commons.lang3.Validate; import java.util.ArrayList; import java.util.List; -import javax.annotation.Nullable; -import javax.validation.constraints.Min; -import javax.validation.constraints.NotNull; -import javax.validation.constraints.Pattern; -import javax.validation.constraints.Size; /** * This class is the parameters model object for starting a diff --git a/hapi-fhir-storage-batch2-jobs/src/main/java/ca/uhn/fhir/batch2/jobs/imprt/ConsumeFilesStep.java b/hapi-fhir-storage-batch2-jobs/src/main/java/ca/uhn/fhir/batch2/jobs/imprt/ConsumeFilesStep.java index 40edfd65ad7..81056e98540 100644 --- a/hapi-fhir-storage-batch2-jobs/src/main/java/ca/uhn/fhir/batch2/jobs/imprt/ConsumeFilesStep.java +++ b/hapi-fhir-storage-batch2-jobs/src/main/java/ca/uhn/fhir/batch2/jobs/imprt/ConsumeFilesStep.java @@ -41,6 +41,7 @@ import ca.uhn.fhir.rest.api.server.storage.IResourcePersistentId; import ca.uhn.fhir.rest.api.server.storage.TransactionDetails; import ca.uhn.fhir.rest.server.exceptions.InvalidRequestException; import ca.uhn.fhir.rest.server.exceptions.PreconditionFailedException; +import jakarta.annotation.Nonnull; import org.apache.commons.io.LineIterator; import org.hl7.fhir.instance.model.api.IBaseResource; import org.hl7.fhir.instance.model.api.IIdType; @@ -53,7 +54,6 @@ import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; -import javax.annotation.Nonnull; import static org.apache.commons.lang3.StringUtils.isNotBlank; diff --git a/hapi-fhir-storage-batch2-jobs/src/main/java/ca/uhn/fhir/batch2/jobs/imprt/FetchFilesStep.java b/hapi-fhir-storage-batch2-jobs/src/main/java/ca/uhn/fhir/batch2/jobs/imprt/FetchFilesStep.java index d16285363d2..2163f8dd109 100644 --- a/hapi-fhir-storage-batch2-jobs/src/main/java/ca/uhn/fhir/batch2/jobs/imprt/FetchFilesStep.java +++ b/hapi-fhir-storage-batch2-jobs/src/main/java/ca/uhn/fhir/batch2/jobs/imprt/FetchFilesStep.java @@ -31,6 +31,7 @@ import ca.uhn.fhir.rest.client.impl.HttpBasicAuthInterceptor; import ca.uhn.fhir.rest.server.exceptions.InternalErrorException; import ca.uhn.fhir.util.FileUtil; import ca.uhn.fhir.util.StopWatch; +import jakarta.annotation.Nonnull; import org.apache.commons.io.FileUtils; import org.apache.commons.io.LineIterator; import org.apache.commons.lang3.Validate; @@ -47,7 +48,6 @@ import java.io.InputStreamReader; import java.nio.charset.StandardCharsets; import java.util.Arrays; import java.util.List; -import javax.annotation.Nonnull; import static org.apache.commons.lang3.StringUtils.isNotBlank; diff --git a/hapi-fhir-storage-batch2-jobs/src/main/java/ca/uhn/fhir/batch2/jobs/reindex/ReindexGenerateRangeChunksStep.java b/hapi-fhir-storage-batch2-jobs/src/main/java/ca/uhn/fhir/batch2/jobs/reindex/ReindexGenerateRangeChunksStep.java index 6def68e4997..c127f741f10 100644 --- a/hapi-fhir-storage-batch2-jobs/src/main/java/ca/uhn/fhir/batch2/jobs/reindex/ReindexGenerateRangeChunksStep.java +++ b/hapi-fhir-storage-batch2-jobs/src/main/java/ca/uhn/fhir/batch2/jobs/reindex/ReindexGenerateRangeChunksStep.java @@ -26,11 +26,10 @@ import ca.uhn.fhir.batch2.api.StepExecutionDetails; import ca.uhn.fhir.batch2.api.VoidModel; import ca.uhn.fhir.batch2.jobs.chunk.PartitionedUrlChunkRangeJson; import ca.uhn.fhir.batch2.jobs.step.GenerateRangeChunksStep; +import jakarta.annotation.Nonnull; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import javax.annotation.Nonnull; - public class ReindexGenerateRangeChunksStep extends GenerateRangeChunksStep { private static final Logger ourLog = LoggerFactory.getLogger(ReindexGenerateRangeChunksStep.class); diff --git a/hapi-fhir-storage-batch2-jobs/src/main/java/ca/uhn/fhir/batch2/jobs/reindex/ReindexJobParameters.java b/hapi-fhir-storage-batch2-jobs/src/main/java/ca/uhn/fhir/batch2/jobs/reindex/ReindexJobParameters.java index 9ba7802e8ed..5480a65b45c 100644 --- a/hapi-fhir-storage-batch2-jobs/src/main/java/ca/uhn/fhir/batch2/jobs/reindex/ReindexJobParameters.java +++ b/hapi-fhir-storage-batch2-jobs/src/main/java/ca/uhn/fhir/batch2/jobs/reindex/ReindexJobParameters.java @@ -22,8 +22,7 @@ package ca.uhn.fhir.batch2.jobs.reindex; import ca.uhn.fhir.batch2.jobs.parameters.PartitionedUrlListJobParameters; import ca.uhn.fhir.jpa.api.dao.ReindexParameters; import com.fasterxml.jackson.annotation.JsonProperty; - -import javax.annotation.Nullable; +import jakarta.annotation.Nullable; import static org.apache.commons.lang3.ObjectUtils.defaultIfNull; diff --git a/hapi-fhir-storage-batch2-jobs/src/main/java/ca/uhn/fhir/batch2/jobs/reindex/ReindexJobParametersValidator.java b/hapi-fhir-storage-batch2-jobs/src/main/java/ca/uhn/fhir/batch2/jobs/reindex/ReindexJobParametersValidator.java index ce00a180973..e9142c1a741 100644 --- a/hapi-fhir-storage-batch2-jobs/src/main/java/ca/uhn/fhir/batch2/jobs/reindex/ReindexJobParametersValidator.java +++ b/hapi-fhir-storage-batch2-jobs/src/main/java/ca/uhn/fhir/batch2/jobs/reindex/ReindexJobParametersValidator.java @@ -23,11 +23,11 @@ import ca.uhn.fhir.batch2.api.IJobParametersValidator; import ca.uhn.fhir.batch2.jobs.parameters.PartitionedUrl; import ca.uhn.fhir.batch2.jobs.parameters.UrlListValidator; import ca.uhn.fhir.rest.api.server.RequestDetails; +import jakarta.annotation.Nonnull; +import jakarta.annotation.Nullable; import java.util.ArrayList; import java.util.List; -import javax.annotation.Nonnull; -import javax.annotation.Nullable; public class ReindexJobParametersValidator implements IJobParametersValidator { diff --git a/hapi-fhir-storage-batch2-jobs/src/main/java/ca/uhn/fhir/batch2/jobs/reindex/ReindexStep.java b/hapi-fhir-storage-batch2-jobs/src/main/java/ca/uhn/fhir/batch2/jobs/reindex/ReindexStep.java index 0b301e55a77..45b1c440a98 100644 --- a/hapi-fhir-storage-batch2-jobs/src/main/java/ca/uhn/fhir/batch2/jobs/reindex/ReindexStep.java +++ b/hapi-fhir-storage-batch2-jobs/src/main/java/ca/uhn/fhir/batch2/jobs/reindex/ReindexStep.java @@ -40,6 +40,7 @@ import ca.uhn.fhir.rest.api.server.storage.IResourcePersistentId; import ca.uhn.fhir.rest.api.server.storage.TransactionDetails; import ca.uhn.fhir.rest.server.exceptions.BaseServerResponseException; import ca.uhn.fhir.util.StopWatch; +import jakarta.annotation.Nonnull; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; @@ -48,7 +49,6 @@ import org.springframework.transaction.support.TransactionCallback; import java.util.List; import java.util.concurrent.TimeUnit; -import javax.annotation.Nonnull; public class ReindexStep implements IJobStepWorker { diff --git a/hapi-fhir-storage-batch2-jobs/src/main/java/ca/uhn/fhir/batch2/jobs/termcodesystem/codesystemdelete/DeleteCodeSystemConceptsByVersionStep.java b/hapi-fhir-storage-batch2-jobs/src/main/java/ca/uhn/fhir/batch2/jobs/termcodesystem/codesystemdelete/DeleteCodeSystemConceptsByVersionStep.java index 798d795f3d9..ebdcf0d8f97 100644 --- a/hapi-fhir-storage-batch2-jobs/src/main/java/ca/uhn/fhir/batch2/jobs/termcodesystem/codesystemdelete/DeleteCodeSystemConceptsByVersionStep.java +++ b/hapi-fhir-storage-batch2-jobs/src/main/java/ca/uhn/fhir/batch2/jobs/termcodesystem/codesystemdelete/DeleteCodeSystemConceptsByVersionStep.java @@ -27,8 +27,7 @@ import ca.uhn.fhir.batch2.api.StepExecutionDetails; import ca.uhn.fhir.jpa.term.api.ITermCodeSystemDeleteJobSvc; import ca.uhn.fhir.jpa.term.models.CodeSystemVersionPIDResult; import ca.uhn.fhir.jpa.term.models.TermCodeSystemDeleteJobParameters; - -import javax.annotation.Nonnull; +import jakarta.annotation.Nonnull; public class DeleteCodeSystemConceptsByVersionStep implements IJobStepWorker< diff --git a/hapi-fhir-storage-batch2-jobs/src/main/java/ca/uhn/fhir/batch2/jobs/termcodesystem/codesystemdelete/DeleteCodeSystemStep.java b/hapi-fhir-storage-batch2-jobs/src/main/java/ca/uhn/fhir/batch2/jobs/termcodesystem/codesystemdelete/DeleteCodeSystemStep.java index 9627546b191..411776db0a0 100644 --- a/hapi-fhir-storage-batch2-jobs/src/main/java/ca/uhn/fhir/batch2/jobs/termcodesystem/codesystemdelete/DeleteCodeSystemStep.java +++ b/hapi-fhir-storage-batch2-jobs/src/main/java/ca/uhn/fhir/batch2/jobs/termcodesystem/codesystemdelete/DeleteCodeSystemStep.java @@ -30,8 +30,7 @@ import ca.uhn.fhir.batch2.model.ChunkOutcome; import ca.uhn.fhir.jpa.term.api.ITermCodeSystemDeleteJobSvc; import ca.uhn.fhir.jpa.term.models.CodeSystemVersionPIDResult; import ca.uhn.fhir.jpa.term.models.TermCodeSystemDeleteJobParameters; - -import javax.annotation.Nonnull; +import jakarta.annotation.Nonnull; public class DeleteCodeSystemStep implements IReductionStepWorker { diff --git a/hapi-fhir-storage-batch2-jobs/src/main/java/ca/uhn/fhir/batch2/jobs/termcodesystem/codesystemdelete/DeleteCodeSystemVersionStep.java b/hapi-fhir-storage-batch2-jobs/src/main/java/ca/uhn/fhir/batch2/jobs/termcodesystem/codesystemdelete/DeleteCodeSystemVersionStep.java index 91e0bc8ea49..b4c8c2604b7 100644 --- a/hapi-fhir-storage-batch2-jobs/src/main/java/ca/uhn/fhir/batch2/jobs/termcodesystem/codesystemdelete/DeleteCodeSystemVersionStep.java +++ b/hapi-fhir-storage-batch2-jobs/src/main/java/ca/uhn/fhir/batch2/jobs/termcodesystem/codesystemdelete/DeleteCodeSystemVersionStep.java @@ -27,8 +27,7 @@ import ca.uhn.fhir.batch2.api.StepExecutionDetails; import ca.uhn.fhir.jpa.term.api.ITermCodeSystemDeleteJobSvc; import ca.uhn.fhir.jpa.term.models.CodeSystemVersionPIDResult; import ca.uhn.fhir.jpa.term.models.TermCodeSystemDeleteJobParameters; - -import javax.annotation.Nonnull; +import jakarta.annotation.Nonnull; public class DeleteCodeSystemVersionStep implements IJobStepWorker< diff --git a/hapi-fhir-storage-batch2-jobs/src/main/java/ca/uhn/fhir/batch2/jobs/termcodesystem/codesystemdelete/ReadTermConceptVersionsStep.java b/hapi-fhir-storage-batch2-jobs/src/main/java/ca/uhn/fhir/batch2/jobs/termcodesystem/codesystemdelete/ReadTermConceptVersionsStep.java index a8a5a309c21..7f87fb4b78c 100644 --- a/hapi-fhir-storage-batch2-jobs/src/main/java/ca/uhn/fhir/batch2/jobs/termcodesystem/codesystemdelete/ReadTermConceptVersionsStep.java +++ b/hapi-fhir-storage-batch2-jobs/src/main/java/ca/uhn/fhir/batch2/jobs/termcodesystem/codesystemdelete/ReadTermConceptVersionsStep.java @@ -28,9 +28,9 @@ import ca.uhn.fhir.batch2.api.VoidModel; import ca.uhn.fhir.jpa.term.api.ITermCodeSystemDeleteJobSvc; import ca.uhn.fhir.jpa.term.models.CodeSystemVersionPIDResult; import ca.uhn.fhir.jpa.term.models.TermCodeSystemDeleteJobParameters; +import jakarta.annotation.Nonnull; import java.util.Iterator; -import javax.annotation.Nonnull; public class ReadTermConceptVersionsStep implements IFirstJobStepWorker { diff --git a/hapi-fhir-storage-batch2-jobs/src/main/java/ca/uhn/fhir/batch2/jobs/termcodesystem/codesystemdelete/TermCodeSystemDeleteJobParametersValidator.java b/hapi-fhir-storage-batch2-jobs/src/main/java/ca/uhn/fhir/batch2/jobs/termcodesystem/codesystemdelete/TermCodeSystemDeleteJobParametersValidator.java index e9e33d7cf0c..044e5d0eda0 100644 --- a/hapi-fhir-storage-batch2-jobs/src/main/java/ca/uhn/fhir/batch2/jobs/termcodesystem/codesystemdelete/TermCodeSystemDeleteJobParametersValidator.java +++ b/hapi-fhir-storage-batch2-jobs/src/main/java/ca/uhn/fhir/batch2/jobs/termcodesystem/codesystemdelete/TermCodeSystemDeleteJobParametersValidator.java @@ -22,11 +22,11 @@ package ca.uhn.fhir.batch2.jobs.termcodesystem.codesystemdelete; import ca.uhn.fhir.batch2.api.IJobParametersValidator; import ca.uhn.fhir.jpa.term.models.TermCodeSystemDeleteJobParameters; import ca.uhn.fhir.rest.api.server.RequestDetails; +import jakarta.annotation.Nonnull; +import jakarta.annotation.Nullable; import java.util.ArrayList; import java.util.List; -import javax.annotation.Nonnull; -import javax.annotation.Nullable; public class TermCodeSystemDeleteJobParametersValidator implements IJobParametersValidator { diff --git a/hapi-fhir-storage-batch2-jobs/src/main/java/ca/uhn/fhir/batch2/jobs/termcodesystem/codesystemversiondelete/DeleteCodeSystemVersionFinalStep.java b/hapi-fhir-storage-batch2-jobs/src/main/java/ca/uhn/fhir/batch2/jobs/termcodesystem/codesystemversiondelete/DeleteCodeSystemVersionFinalStep.java index 2785fa08189..7744a5b3373 100644 --- a/hapi-fhir-storage-batch2-jobs/src/main/java/ca/uhn/fhir/batch2/jobs/termcodesystem/codesystemversiondelete/DeleteCodeSystemVersionFinalStep.java +++ b/hapi-fhir-storage-batch2-jobs/src/main/java/ca/uhn/fhir/batch2/jobs/termcodesystem/codesystemversiondelete/DeleteCodeSystemVersionFinalStep.java @@ -28,8 +28,7 @@ import ca.uhn.fhir.batch2.api.VoidModel; import ca.uhn.fhir.jpa.term.api.ITermCodeSystemDeleteJobSvc; import ca.uhn.fhir.jpa.term.models.CodeSystemVersionPIDResult; import ca.uhn.fhir.jpa.term.models.TermCodeSystemDeleteVersionJobParameters; - -import javax.annotation.Nonnull; +import jakarta.annotation.Nonnull; public class DeleteCodeSystemVersionFinalStep implements ILastJobStepWorker { diff --git a/hapi-fhir-storage-batch2-jobs/src/main/java/ca/uhn/fhir/batch2/jobs/termcodesystem/codesystemversiondelete/DeleteCodeSystemVersionFirstStep.java b/hapi-fhir-storage-batch2-jobs/src/main/java/ca/uhn/fhir/batch2/jobs/termcodesystem/codesystemversiondelete/DeleteCodeSystemVersionFirstStep.java index 55049ed5272..db8f7e16bf5 100644 --- a/hapi-fhir-storage-batch2-jobs/src/main/java/ca/uhn/fhir/batch2/jobs/termcodesystem/codesystemversiondelete/DeleteCodeSystemVersionFirstStep.java +++ b/hapi-fhir-storage-batch2-jobs/src/main/java/ca/uhn/fhir/batch2/jobs/termcodesystem/codesystemversiondelete/DeleteCodeSystemVersionFirstStep.java @@ -28,8 +28,7 @@ import ca.uhn.fhir.batch2.api.VoidModel; import ca.uhn.fhir.jpa.term.api.ITermCodeSystemDeleteJobSvc; import ca.uhn.fhir.jpa.term.models.CodeSystemVersionPIDResult; import ca.uhn.fhir.jpa.term.models.TermCodeSystemDeleteVersionJobParameters; - -import javax.annotation.Nonnull; +import jakarta.annotation.Nonnull; public class DeleteCodeSystemVersionFirstStep implements IFirstJobStepWorker { diff --git a/hapi-fhir-storage-batch2-jobs/src/main/java/ca/uhn/fhir/batch2/jobs/termcodesystem/codesystemversiondelete/DeleteCodeSystemVersionParameterValidator.java b/hapi-fhir-storage-batch2-jobs/src/main/java/ca/uhn/fhir/batch2/jobs/termcodesystem/codesystemversiondelete/DeleteCodeSystemVersionParameterValidator.java index 7e7ad78d5cb..2533adcd361 100644 --- a/hapi-fhir-storage-batch2-jobs/src/main/java/ca/uhn/fhir/batch2/jobs/termcodesystem/codesystemversiondelete/DeleteCodeSystemVersionParameterValidator.java +++ b/hapi-fhir-storage-batch2-jobs/src/main/java/ca/uhn/fhir/batch2/jobs/termcodesystem/codesystemversiondelete/DeleteCodeSystemVersionParameterValidator.java @@ -22,11 +22,11 @@ package ca.uhn.fhir.batch2.jobs.termcodesystem.codesystemversiondelete; import ca.uhn.fhir.batch2.api.IJobParametersValidator; import ca.uhn.fhir.jpa.term.models.TermCodeSystemDeleteVersionJobParameters; import ca.uhn.fhir.rest.api.server.RequestDetails; +import jakarta.annotation.Nonnull; +import jakarta.annotation.Nullable; import java.util.ArrayList; import java.util.List; -import javax.annotation.Nonnull; -import javax.annotation.Nullable; public class DeleteCodeSystemVersionParameterValidator implements IJobParametersValidator { diff --git a/hapi-fhir-storage-batch2-jobs/src/test/java/ca/uhn/fhir/batch2/jobs/BaseR4ServerTest.java b/hapi-fhir-storage-batch2-jobs/src/test/java/ca/uhn/fhir/batch2/jobs/BaseR4ServerTest.java index 0b631445657..23fe01f5fb2 100644 --- a/hapi-fhir-storage-batch2-jobs/src/test/java/ca/uhn/fhir/batch2/jobs/BaseR4ServerTest.java +++ b/hapi-fhir-storage-batch2-jobs/src/test/java/ca/uhn/fhir/batch2/jobs/BaseR4ServerTest.java @@ -8,8 +8,8 @@ import ca.uhn.fhir.rest.client.api.ServerValidationModeEnum; import ca.uhn.fhir.rest.server.RestfulServer; import ca.uhn.fhir.test.utilities.JettyUtil; import org.eclipse.jetty.server.Server; -import org.eclipse.jetty.servlet.ServletHandler; -import org.eclipse.jetty.servlet.ServletHolder; +import org.eclipse.jetty.ee10.servlet.ServletHandler; +import org.eclipse.jetty.ee10.servlet.ServletHolder; import org.junit.jupiter.api.AfterEach; public class BaseR4ServerTest { diff --git a/hapi-fhir-storage-batch2-jobs/src/test/java/ca/uhn/fhir/batch2/jobs/imprt/BulkDataImportProviderTest.java b/hapi-fhir-storage-batch2-jobs/src/test/java/ca/uhn/fhir/batch2/jobs/imprt/BulkDataImportProviderTest.java index 7ba6807d180..4d79239db44 100644 --- a/hapi-fhir-storage-batch2-jobs/src/test/java/ca/uhn/fhir/batch2/jobs/imprt/BulkDataImportProviderTest.java +++ b/hapi-fhir-storage-batch2-jobs/src/test/java/ca/uhn/fhir/batch2/jobs/imprt/BulkDataImportProviderTest.java @@ -50,7 +50,7 @@ import org.mockito.junit.jupiter.MockitoExtension; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import javax.annotation.Nonnull; +import jakarta.annotation.Nonnull; import java.io.IOException; import java.nio.charset.StandardCharsets; import java.util.Date; @@ -279,7 +279,7 @@ public class BulkDataImportProviderTest { assertEquals(202, response.getStatusLine().getStatusCode()); assertEquals("Accepted", response.getStatusLine().getReasonPhrase()); assertEquals("120", response.getFirstHeader(Constants.HEADER_RETRY_AFTER).getValue()); - assertThat(response.getFirstHeader(Constants.HEADER_X_PROGRESS).getValue(), containsString("Job was created at 2022-01-01")); + assertThat(response.getFirstHeader(Constants.HEADER_X_PROGRESS).getValue(), containsString("Job was created at 2022-01")); } } diff --git a/hapi-fhir-storage-batch2-test-utilities/pom.xml b/hapi-fhir-storage-batch2-test-utilities/pom.xml index 428fe50b7e9..38658c74f86 100644 --- a/hapi-fhir-storage-batch2-test-utilities/pom.xml +++ b/hapi-fhir-storage-batch2-test-utilities/pom.xml @@ -7,7 +7,7 @@ ca.uhn.hapi.fhir hapi-deployable-pom - 6.9.7-SNAPSHOT + 6.11.7-SNAPSHOT ../hapi-deployable-pom/pom.xml diff --git a/hapi-fhir-storage-batch2-test-utilities/src/main/java/ca/uhn/hapi/fhir/batch2/test/AbstractIJobPersistenceSpecificationTest.java b/hapi-fhir-storage-batch2-test-utilities/src/main/java/ca/uhn/hapi/fhir/batch2/test/AbstractIJobPersistenceSpecificationTest.java index 30b09c1ce8a..7e188e624b2 100644 --- a/hapi-fhir-storage-batch2-test-utilities/src/main/java/ca/uhn/hapi/fhir/batch2/test/AbstractIJobPersistenceSpecificationTest.java +++ b/hapi-fhir-storage-batch2-test-utilities/src/main/java/ca/uhn/hapi/fhir/batch2/test/AbstractIJobPersistenceSpecificationTest.java @@ -53,7 +53,7 @@ import org.springframework.transaction.TransactionStatus; import org.springframework.transaction.support.TransactionCallbackWithoutResult; import org.springframework.transaction.support.TransactionTemplate; -import javax.annotation.Nonnull; +import jakarta.annotation.Nonnull; import java.util.ArrayList; import java.util.Iterator; import java.util.List; diff --git a/hapi-fhir-storage-batch2-test-utilities/src/main/java/ca/uhn/hapi/fhir/batch2/test/support/TestJobParameters.java b/hapi-fhir-storage-batch2-test-utilities/src/main/java/ca/uhn/hapi/fhir/batch2/test/support/TestJobParameters.java index 9ae48d15300..9c6cd0832ca 100644 --- a/hapi-fhir-storage-batch2-test-utilities/src/main/java/ca/uhn/hapi/fhir/batch2/test/support/TestJobParameters.java +++ b/hapi-fhir-storage-batch2-test-utilities/src/main/java/ca/uhn/hapi/fhir/batch2/test/support/TestJobParameters.java @@ -24,7 +24,7 @@ import ca.uhn.fhir.model.api.annotation.PasswordField; import com.fasterxml.jackson.annotation.JsonProperty; import org.hibernate.validator.constraints.Length; -import javax.validation.constraints.NotBlank; +import jakarta.validation.constraints.NotBlank; public class TestJobParameters implements IModelJson { diff --git a/hapi-fhir-storage-batch2/pom.xml b/hapi-fhir-storage-batch2/pom.xml index 6f750898ab8..42541c03098 100644 --- a/hapi-fhir-storage-batch2/pom.xml +++ b/hapi-fhir-storage-batch2/pom.xml @@ -7,7 +7,7 @@ ca.uhn.hapi.fhir hapi-deployable-pom - 6.9.7-SNAPSHOT + 6.11.7-SNAPSHOT ../hapi-deployable-pom/pom.xml @@ -20,6 +20,18 @@ + + + org.slf4j + slf4j-api + + + ch.qos.logback + logback-classic + test + + ca.uhn.hapi.fhir hapi-fhir-base @@ -50,6 +62,7 @@ org.hibernate.validator hibernate-validator + org.glassfish jakarta.el @@ -72,11 +85,6 @@ spring-test test - - ch.qos.logback - logback-classic - test - org.springframework.data spring-data-commons diff --git a/hapi-fhir-storage-batch2/src/main/java/ca/uhn/fhir/batch2/api/IJobCoordinator.java b/hapi-fhir-storage-batch2/src/main/java/ca/uhn/fhir/batch2/api/IJobCoordinator.java index 46d38ff13b2..3fb685c34bf 100644 --- a/hapi-fhir-storage-batch2/src/main/java/ca/uhn/fhir/batch2/api/IJobCoordinator.java +++ b/hapi-fhir-storage-batch2/src/main/java/ca/uhn/fhir/batch2/api/IJobCoordinator.java @@ -27,12 +27,12 @@ import ca.uhn.fhir.jpa.batch.models.Batch2JobStartResponse; import ca.uhn.fhir.rest.api.server.RequestDetails; import ca.uhn.fhir.rest.server.exceptions.InvalidRequestException; import ca.uhn.fhir.rest.server.exceptions.ResourceNotFoundException; +import jakarta.annotation.Nonnull; +import jakarta.annotation.Nullable; import org.springframework.data.domain.Page; import java.util.List; import java.util.Set; -import javax.annotation.Nonnull; -import javax.annotation.Nullable; public interface IJobCoordinator { diff --git a/hapi-fhir-storage-batch2/src/main/java/ca/uhn/fhir/batch2/api/IJobParametersValidator.java b/hapi-fhir-storage-batch2/src/main/java/ca/uhn/fhir/batch2/api/IJobParametersValidator.java index 98ebb948adf..f1a8aa7c238 100644 --- a/hapi-fhir-storage-batch2/src/main/java/ca/uhn/fhir/batch2/api/IJobParametersValidator.java +++ b/hapi-fhir-storage-batch2/src/main/java/ca/uhn/fhir/batch2/api/IJobParametersValidator.java @@ -21,10 +21,10 @@ package ca.uhn.fhir.batch2.api; import ca.uhn.fhir.model.api.IModelJson; import ca.uhn.fhir.rest.api.server.RequestDetails; +import jakarta.annotation.Nonnull; +import jakarta.annotation.Nullable; import java.util.List; -import javax.annotation.Nonnull; -import javax.annotation.Nullable; /** * This interface can be used to validate the parameters diff --git a/hapi-fhir-storage-batch2/src/main/java/ca/uhn/fhir/batch2/api/IJobPersistence.java b/hapi-fhir-storage-batch2/src/main/java/ca/uhn/fhir/batch2/api/IJobPersistence.java index 5f4c50e5b79..05c2b489536 100644 --- a/hapi-fhir-storage-batch2/src/main/java/ca/uhn/fhir/batch2/api/IJobPersistence.java +++ b/hapi-fhir-storage-batch2/src/main/java/ca/uhn/fhir/batch2/api/IJobPersistence.java @@ -26,6 +26,7 @@ import ca.uhn.fhir.batch2.model.StatusEnum; import ca.uhn.fhir.batch2.model.WorkChunk; import ca.uhn.fhir.batch2.model.WorkChunkCreateEvent; import ca.uhn.fhir.batch2.models.JobInstanceFetchRequest; +import jakarta.annotation.Nonnull; import org.apache.commons.lang3.builder.ToStringBuilder; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -41,7 +42,6 @@ import java.util.List; import java.util.Optional; import java.util.Set; import java.util.stream.Stream; -import javax.annotation.Nonnull; /** * diff --git a/hapi-fhir-storage-batch2/src/main/java/ca/uhn/fhir/batch2/api/IJobStepWorker.java b/hapi-fhir-storage-batch2/src/main/java/ca/uhn/fhir/batch2/api/IJobStepWorker.java index 3ce89138632..0ce95bc7789 100644 --- a/hapi-fhir-storage-batch2/src/main/java/ca/uhn/fhir/batch2/api/IJobStepWorker.java +++ b/hapi-fhir-storage-batch2/src/main/java/ca/uhn/fhir/batch2/api/IJobStepWorker.java @@ -20,8 +20,7 @@ package ca.uhn.fhir.batch2.api; import ca.uhn.fhir.model.api.IModelJson; - -import javax.annotation.Nonnull; +import jakarta.annotation.Nonnull; /** * This interface is implemented by step workers within the Batch2 framework. It will be called diff --git a/hapi-fhir-storage-batch2/src/main/java/ca/uhn/fhir/batch2/api/IReductionStepWorker.java b/hapi-fhir-storage-batch2/src/main/java/ca/uhn/fhir/batch2/api/IReductionStepWorker.java index 78e0519fc5c..c68b513ba0c 100644 --- a/hapi-fhir-storage-batch2/src/main/java/ca/uhn/fhir/batch2/api/IReductionStepWorker.java +++ b/hapi-fhir-storage-batch2/src/main/java/ca/uhn/fhir/batch2/api/IReductionStepWorker.java @@ -21,8 +21,7 @@ package ca.uhn.fhir.batch2.api; import ca.uhn.fhir.batch2.model.ChunkOutcome; import ca.uhn.fhir.model.api.IModelJson; - -import javax.annotation.Nonnull; +import jakarta.annotation.Nonnull; /** * Reduction step worker. diff --git a/hapi-fhir-storage-batch2/src/main/java/ca/uhn/fhir/batch2/api/IWorkChunkPersistence.java b/hapi-fhir-storage-batch2/src/main/java/ca/uhn/fhir/batch2/api/IWorkChunkPersistence.java index d29a476c214..1850a4bfb4c 100644 --- a/hapi-fhir-storage-batch2/src/main/java/ca/uhn/fhir/batch2/api/IWorkChunkPersistence.java +++ b/hapi-fhir-storage-batch2/src/main/java/ca/uhn/fhir/batch2/api/IWorkChunkPersistence.java @@ -60,7 +60,6 @@ public interface IWorkChunkPersistence { * @param theBatchWorkChunk the batch work chunk to be stored * @return a globally unique identifier for this chunk. */ - @Transactional(propagation = Propagation.REQUIRED) String onWorkChunkCreate(WorkChunkCreateEvent theBatchWorkChunk); /** @@ -71,7 +70,7 @@ public interface IWorkChunkPersistence { * @param theChunkId The ID from {@link #onWorkChunkCreate} * @return The WorkChunk or empty if no chunk exists, or not in a runnable state (QUEUED or ERRORRED) */ - @Transactional(propagation = Propagation.REQUIRED) + @Transactional(propagation = Propagation.MANDATORY) Optional onWorkChunkDequeue(String theChunkId); /** diff --git a/hapi-fhir-storage-batch2/src/main/java/ca/uhn/fhir/batch2/api/JobCompletionDetails.java b/hapi-fhir-storage-batch2/src/main/java/ca/uhn/fhir/batch2/api/JobCompletionDetails.java index ca3d04a259c..e7e99327b16 100644 --- a/hapi-fhir-storage-batch2/src/main/java/ca/uhn/fhir/batch2/api/JobCompletionDetails.java +++ b/hapi-fhir-storage-batch2/src/main/java/ca/uhn/fhir/batch2/api/JobCompletionDetails.java @@ -21,10 +21,9 @@ package ca.uhn.fhir.batch2.api; import ca.uhn.fhir.batch2.model.JobInstance; import ca.uhn.fhir.model.api.IModelJson; +import jakarta.annotation.Nonnull; import org.apache.commons.lang3.Validate; -import javax.annotation.Nonnull; - public class JobCompletionDetails { private final PT myParameters; diff --git a/hapi-fhir-storage-batch2/src/main/java/ca/uhn/fhir/batch2/api/ReductionStepExecutionDetails.java b/hapi-fhir-storage-batch2/src/main/java/ca/uhn/fhir/batch2/api/ReductionStepExecutionDetails.java index 620a8414b5e..8da61f1bf2c 100644 --- a/hapi-fhir-storage-batch2/src/main/java/ca/uhn/fhir/batch2/api/ReductionStepExecutionDetails.java +++ b/hapi-fhir-storage-batch2/src/main/java/ca/uhn/fhir/batch2/api/ReductionStepExecutionDetails.java @@ -22,9 +22,8 @@ package ca.uhn.fhir.batch2.api; import ca.uhn.fhir.batch2.model.JobInstance; import ca.uhn.fhir.i18n.Msg; import ca.uhn.fhir.model.api.IModelJson; - -import javax.annotation.Nonnull; -import javax.annotation.Nullable; +import jakarta.annotation.Nonnull; +import jakarta.annotation.Nullable; /** * This class is used for Reduction Step for Batch2 Jobs. diff --git a/hapi-fhir-storage-batch2/src/main/java/ca/uhn/fhir/batch2/api/StepExecutionDetails.java b/hapi-fhir-storage-batch2/src/main/java/ca/uhn/fhir/batch2/api/StepExecutionDetails.java index 93261940461..7617f6e9444 100644 --- a/hapi-fhir-storage-batch2/src/main/java/ca/uhn/fhir/batch2/api/StepExecutionDetails.java +++ b/hapi-fhir-storage-batch2/src/main/java/ca/uhn/fhir/batch2/api/StepExecutionDetails.java @@ -21,11 +21,10 @@ package ca.uhn.fhir.batch2.api; import ca.uhn.fhir.batch2.model.JobInstance; import ca.uhn.fhir.model.api.IModelJson; +import jakarta.annotation.Nonnull; +import jakarta.annotation.Nullable; import org.apache.commons.lang3.Validate; -import javax.annotation.Nonnull; -import javax.annotation.Nullable; - public class StepExecutionDetails { private final PT myParameters; diff --git a/hapi-fhir-storage-batch2/src/main/java/ca/uhn/fhir/batch2/channel/BatchJobSender.java b/hapi-fhir-storage-batch2/src/main/java/ca/uhn/fhir/batch2/channel/BatchJobSender.java index f7272a0d2de..2754119214a 100644 --- a/hapi-fhir-storage-batch2/src/main/java/ca/uhn/fhir/batch2/channel/BatchJobSender.java +++ b/hapi-fhir-storage-batch2/src/main/java/ca/uhn/fhir/batch2/channel/BatchJobSender.java @@ -22,11 +22,10 @@ package ca.uhn.fhir.batch2.channel; import ca.uhn.fhir.batch2.model.JobWorkNotification; import ca.uhn.fhir.batch2.model.JobWorkNotificationJsonMessage; import ca.uhn.fhir.jpa.subscription.channel.api.IChannelProducer; +import jakarta.annotation.Nonnull; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import javax.annotation.Nonnull; - public class BatchJobSender { private static final Logger ourLog = LoggerFactory.getLogger(BatchJobSender.class); private final IChannelProducer myWorkChannelProducer; diff --git a/hapi-fhir-storage-batch2/src/main/java/ca/uhn/fhir/batch2/config/BaseBatch2Config.java b/hapi-fhir-storage-batch2/src/main/java/ca/uhn/fhir/batch2/config/BaseBatch2Config.java index 071a89fe716..4395264fcd5 100644 --- a/hapi-fhir-storage-batch2/src/main/java/ca/uhn/fhir/batch2/config/BaseBatch2Config.java +++ b/hapi-fhir-storage-batch2/src/main/java/ca/uhn/fhir/batch2/config/BaseBatch2Config.java @@ -48,10 +48,13 @@ public abstract class BaseBatch2Config { public static final String CHANNEL_NAME = "batch2-work-notification"; @Autowired - private IJobPersistence myPersistence; + IJobPersistence myPersistence; @Autowired - private IChannelFactory myChannelFactory; + IChannelFactory myChannelFactory; + + @Autowired + IHapiTransactionService myHapiTransactionService; @Bean public JobDefinitionRegistry batch2JobDefinitionRegistry() { @@ -60,7 +63,7 @@ public abstract class BaseBatch2Config { @Bean public WorkChunkProcessor jobStepExecutorService(BatchJobSender theBatchJobSender) { - return new WorkChunkProcessor(myPersistence, theBatchJobSender); + return new WorkChunkProcessor(myPersistence, theBatchJobSender, myHapiTransactionService); } @Bean diff --git a/hapi-fhir-storage-batch2/src/main/java/ca/uhn/fhir/batch2/coordinator/FinalStepDataSink.java b/hapi-fhir-storage-batch2/src/main/java/ca/uhn/fhir/batch2/coordinator/FinalStepDataSink.java index 14335a5d2c1..30d2638fe82 100644 --- a/hapi-fhir-storage-batch2/src/main/java/ca/uhn/fhir/batch2/coordinator/FinalStepDataSink.java +++ b/hapi-fhir-storage-batch2/src/main/java/ca/uhn/fhir/batch2/coordinator/FinalStepDataSink.java @@ -26,10 +26,9 @@ import ca.uhn.fhir.batch2.model.WorkChunkData; import ca.uhn.fhir.i18n.Msg; import ca.uhn.fhir.model.api.IModelJson; import ca.uhn.fhir.util.Logs; +import jakarta.annotation.Nonnull; import org.slf4j.Logger; -import javax.annotation.Nonnull; - class FinalStepDataSink extends BaseDataSink { private static final Logger ourLog = Logs.getBatchTroubleshootingLog(); diff --git a/hapi-fhir-storage-batch2/src/main/java/ca/uhn/fhir/batch2/coordinator/JobCoordinatorImpl.java b/hapi-fhir-storage-batch2/src/main/java/ca/uhn/fhir/batch2/coordinator/JobCoordinatorImpl.java index 23cf995fa6d..d41742eac72 100644 --- a/hapi-fhir-storage-batch2/src/main/java/ca/uhn/fhir/batch2/coordinator/JobCoordinatorImpl.java +++ b/hapi-fhir-storage-batch2/src/main/java/ca/uhn/fhir/batch2/coordinator/JobCoordinatorImpl.java @@ -39,21 +39,22 @@ import ca.uhn.fhir.rest.api.server.RequestDetails; import ca.uhn.fhir.rest.server.exceptions.InvalidRequestException; import ca.uhn.fhir.rest.server.exceptions.ResourceNotFoundException; import ca.uhn.fhir.util.Logs; +import jakarta.annotation.Nonnull; +import jakarta.annotation.Nullable; +import jakarta.annotation.PostConstruct; +import jakarta.annotation.PreDestroy; import org.apache.commons.lang3.Validate; import org.slf4j.Logger; import org.springframework.data.domain.Page; import org.springframework.messaging.MessageHandler; -import org.springframework.transaction.support.TransactionSynchronizationAdapter; +import org.springframework.transaction.annotation.Propagation; +import org.springframework.transaction.support.TransactionSynchronization; import org.springframework.transaction.support.TransactionSynchronizationManager; import java.util.Arrays; import java.util.HashSet; import java.util.List; import java.util.Set; -import javax.annotation.Nonnull; -import javax.annotation.Nullable; -import javax.annotation.PostConstruct; -import javax.annotation.PreDestroy; import static org.apache.commons.lang3.StringUtils.isBlank; @@ -143,7 +144,8 @@ public class JobCoordinatorImpl implements IJobCoordinator { myJobParameterJsonValidator.validateJobParameters(theRequestDetails, theStartRequest, jobDefinition); IJobPersistence.CreateResult instanceAndFirstChunk = myTransactionService - .withSystemRequest() + .withSystemRequestOnDefaultPartition() + .withPropagation(Propagation.REQUIRES_NEW) .execute(() -> myJobPersistence.onCreateWithFirstChunk(jobDefinition, theStartRequest.getParameters())); JobWorkNotification workNotification = JobWorkNotification.firstStepNotification( @@ -163,7 +165,7 @@ public class JobCoordinatorImpl implements IJobCoordinator { */ private void sendBatchJobWorkNotificationAfterCommit(final JobWorkNotification theJobWorkNotification) { if (TransactionSynchronizationManager.isSynchronizationActive()) { - TransactionSynchronizationManager.registerSynchronization(new TransactionSynchronizationAdapter() { + TransactionSynchronizationManager.registerSynchronization(new TransactionSynchronization() { @Override public int getOrder() { return 0; diff --git a/hapi-fhir-storage-batch2/src/main/java/ca/uhn/fhir/batch2/coordinator/JobDataSink.java b/hapi-fhir-storage-batch2/src/main/java/ca/uhn/fhir/batch2/coordinator/JobDataSink.java index 26fc1c922c7..4e093d27df9 100644 --- a/hapi-fhir-storage-batch2/src/main/java/ca/uhn/fhir/batch2/coordinator/JobDataSink.java +++ b/hapi-fhir-storage-batch2/src/main/java/ca/uhn/fhir/batch2/coordinator/JobDataSink.java @@ -28,14 +28,16 @@ import ca.uhn.fhir.batch2.model.JobWorkNotification; import ca.uhn.fhir.batch2.model.WorkChunkCreateEvent; import ca.uhn.fhir.batch2.model.WorkChunkData; import ca.uhn.fhir.i18n.Msg; +import ca.uhn.fhir.jpa.dao.tx.IHapiTransactionService; import ca.uhn.fhir.model.api.IModelJson; import ca.uhn.fhir.util.JsonUtil; import ca.uhn.fhir.util.Logs; +import jakarta.annotation.Nonnull; import org.slf4j.Logger; +import org.springframework.transaction.annotation.Propagation; import java.util.concurrent.atomic.AtomicInteger; import java.util.concurrent.atomic.AtomicReference; -import javax.annotation.Nonnull; class JobDataSink extends BaseDataSink { @@ -49,13 +51,15 @@ class JobDataSink myLastChunkId = new AtomicReference<>(); private final boolean myGatedExecution; + private final IHapiTransactionService myHapiTransactionService; JobDataSink( @Nonnull BatchJobSender theBatchJobSender, @Nonnull IJobPersistence theJobPersistence, @Nonnull JobDefinition theDefinition, @Nonnull String theInstanceId, - @Nonnull JobWorkCursor theJobWorkCursor) { + @Nonnull JobWorkCursor theJobWorkCursor, + IHapiTransactionService theHapiTransactionService) { super(theInstanceId, theJobWorkCursor); myBatchJobSender = theBatchJobSender; myJobPersistence = theJobPersistence; @@ -63,6 +67,7 @@ class JobDataSink myJobPersistence.onWorkChunkCreate(batchWorkChunk)); + myLastChunkId.set(chunkId); if (!myGatedExecution) { diff --git a/hapi-fhir-storage-batch2/src/main/java/ca/uhn/fhir/batch2/coordinator/JobDefinitionRegistry.java b/hapi-fhir-storage-batch2/src/main/java/ca/uhn/fhir/batch2/coordinator/JobDefinitionRegistry.java index 407f5d7906a..9f322519414 100644 --- a/hapi-fhir-storage-batch2/src/main/java/ca/uhn/fhir/batch2/coordinator/JobDefinitionRegistry.java +++ b/hapi-fhir-storage-batch2/src/main/java/ca/uhn/fhir/batch2/coordinator/JobDefinitionRegistry.java @@ -28,6 +28,7 @@ import ca.uhn.fhir.i18n.Msg; import ca.uhn.fhir.model.api.IModelJson; import ca.uhn.fhir.util.Logs; import com.google.common.collect.ImmutableSortedMap; +import jakarta.annotation.Nonnull; import org.apache.commons.lang3.Validate; import org.slf4j.Logger; @@ -41,7 +42,6 @@ import java.util.Set; import java.util.TreeMap; import java.util.concurrent.ConcurrentHashMap; import java.util.stream.Collectors; -import javax.annotation.Nonnull; public class JobDefinitionRegistry { private static final Logger ourLog = Logs.getBatchTroubleshootingLog(); diff --git a/hapi-fhir-storage-batch2/src/main/java/ca/uhn/fhir/batch2/coordinator/JobParameterJsonValidator.java b/hapi-fhir-storage-batch2/src/main/java/ca/uhn/fhir/batch2/coordinator/JobParameterJsonValidator.java index 279d0d8d9e0..639feb92f92 100644 --- a/hapi-fhir-storage-batch2/src/main/java/ca/uhn/fhir/batch2/coordinator/JobParameterJsonValidator.java +++ b/hapi-fhir-storage-batch2/src/main/java/ca/uhn/fhir/batch2/coordinator/JobParameterJsonValidator.java @@ -26,16 +26,16 @@ import ca.uhn.fhir.i18n.Msg; import ca.uhn.fhir.model.api.IModelJson; import ca.uhn.fhir.rest.api.server.RequestDetails; import ca.uhn.fhir.rest.server.exceptions.InvalidRequestException; +import jakarta.annotation.Nonnull; +import jakarta.validation.ConstraintViolation; +import jakarta.validation.Validation; +import jakarta.validation.Validator; +import jakarta.validation.ValidatorFactory; import java.util.Collections; import java.util.List; import java.util.Set; import java.util.stream.Collectors; -import javax.annotation.Nonnull; -import javax.validation.ConstraintViolation; -import javax.validation.Validation; -import javax.validation.Validator; -import javax.validation.ValidatorFactory; import static org.apache.commons.lang3.ObjectUtils.defaultIfNull; diff --git a/hapi-fhir-storage-batch2/src/main/java/ca/uhn/fhir/batch2/coordinator/JobQuerySvc.java b/hapi-fhir-storage-batch2/src/main/java/ca/uhn/fhir/batch2/coordinator/JobQuerySvc.java index d5507d66b22..facf62ec93d 100644 --- a/hapi-fhir-storage-batch2/src/main/java/ca/uhn/fhir/batch2/coordinator/JobQuerySvc.java +++ b/hapi-fhir-storage-batch2/src/main/java/ca/uhn/fhir/batch2/coordinator/JobQuerySvc.java @@ -32,14 +32,14 @@ import ca.uhn.fhir.rest.server.exceptions.ResourceNotFoundException; import ca.uhn.fhir.util.JsonUtil; import ca.uhn.fhir.util.UrlUtil; import com.fasterxml.jackson.annotation.JsonProperty; +import jakarta.annotation.Nonnull; +import jakarta.annotation.Nullable; import org.springframework.data.domain.Page; import java.lang.reflect.Field; import java.util.List; import java.util.Set; import java.util.stream.Collectors; -import javax.annotation.Nonnull; -import javax.annotation.Nullable; /** * Job Query services intended for end-users querying the status of jobs diff --git a/hapi-fhir-storage-batch2/src/main/java/ca/uhn/fhir/batch2/coordinator/JobStepExecutor.java b/hapi-fhir-storage-batch2/src/main/java/ca/uhn/fhir/batch2/coordinator/JobStepExecutor.java index 1347d0d4dd2..c509b07a01b 100644 --- a/hapi-fhir-storage-batch2/src/main/java/ca/uhn/fhir/batch2/coordinator/JobStepExecutor.java +++ b/hapi-fhir-storage-batch2/src/main/java/ca/uhn/fhir/batch2/coordinator/JobStepExecutor.java @@ -29,10 +29,10 @@ import ca.uhn.fhir.batch2.model.WorkChunk; import ca.uhn.fhir.batch2.progress.JobInstanceStatusUpdater; import ca.uhn.fhir.model.api.IModelJson; import ca.uhn.fhir.util.Logs; +import jakarta.annotation.Nonnull; import org.slf4j.Logger; import java.util.Date; -import javax.annotation.Nonnull; public class JobStepExecutor { private static final Logger ourLog = Logs.getBatchTroubleshootingLog(); diff --git a/hapi-fhir-storage-batch2/src/main/java/ca/uhn/fhir/batch2/coordinator/JobStepExecutorFactory.java b/hapi-fhir-storage-batch2/src/main/java/ca/uhn/fhir/batch2/coordinator/JobStepExecutorFactory.java index 867436cbee0..d0c49dc4e83 100644 --- a/hapi-fhir-storage-batch2/src/main/java/ca/uhn/fhir/batch2/coordinator/JobStepExecutorFactory.java +++ b/hapi-fhir-storage-batch2/src/main/java/ca/uhn/fhir/batch2/coordinator/JobStepExecutorFactory.java @@ -26,8 +26,7 @@ import ca.uhn.fhir.batch2.model.JobInstance; import ca.uhn.fhir.batch2.model.JobWorkCursor; import ca.uhn.fhir.batch2.model.WorkChunk; import ca.uhn.fhir.model.api.IModelJson; - -import javax.annotation.Nonnull; +import jakarta.annotation.Nonnull; public class JobStepExecutorFactory { private final IJobPersistence myJobPersistence; diff --git a/hapi-fhir-storage-batch2/src/main/java/ca/uhn/fhir/batch2/coordinator/ReductionStepExecutorServiceImpl.java b/hapi-fhir-storage-batch2/src/main/java/ca/uhn/fhir/batch2/coordinator/ReductionStepExecutorServiceImpl.java index 21d00887907..882e18251b2 100644 --- a/hapi-fhir-storage-batch2/src/main/java/ca/uhn/fhir/batch2/coordinator/ReductionStepExecutorServiceImpl.java +++ b/hapi-fhir-storage-batch2/src/main/java/ca/uhn/fhir/batch2/coordinator/ReductionStepExecutorServiceImpl.java @@ -38,6 +38,7 @@ import ca.uhn.fhir.jpa.model.sched.ScheduledJobDefinition; import ca.uhn.fhir.model.api.IModelJson; import ca.uhn.fhir.rest.server.exceptions.InternalErrorException; import com.google.common.annotations.VisibleForTesting; +import jakarta.annotation.Nonnull; import org.apache.commons.lang3.time.DateUtils; import org.quartz.JobExecutionContext; import org.slf4j.Logger; @@ -61,7 +62,6 @@ import java.util.concurrent.Executors; import java.util.concurrent.Semaphore; import java.util.concurrent.atomic.AtomicReference; import java.util.stream.Stream; -import javax.annotation.Nonnull; import static ca.uhn.fhir.batch2.model.StatusEnum.ERRORED; import static ca.uhn.fhir.batch2.model.StatusEnum.FINALIZE; diff --git a/hapi-fhir-storage-batch2/src/main/java/ca/uhn/fhir/batch2/coordinator/WorkChannelMessageHandler.java b/hapi-fhir-storage-batch2/src/main/java/ca/uhn/fhir/batch2/coordinator/WorkChannelMessageHandler.java index 21d5f2cba87..5aa81a34f62 100644 --- a/hapi-fhir-storage-batch2/src/main/java/ca/uhn/fhir/batch2/coordinator/WorkChannelMessageHandler.java +++ b/hapi-fhir-storage-batch2/src/main/java/ca/uhn/fhir/batch2/coordinator/WorkChannelMessageHandler.java @@ -30,6 +30,7 @@ import ca.uhn.fhir.batch2.model.JobWorkNotificationJsonMessage; import ca.uhn.fhir.batch2.model.WorkChunk; import ca.uhn.fhir.jpa.dao.tx.IHapiTransactionService; import ca.uhn.fhir.util.Logs; +import jakarta.annotation.Nonnull; import org.slf4j.Logger; import org.springframework.messaging.Message; import org.springframework.messaging.MessageHandler; @@ -37,7 +38,6 @@ import org.springframework.messaging.MessagingException; import java.util.Optional; import java.util.function.Supplier; -import javax.annotation.Nonnull; /** * This handler receives batch work request messages and performs the batch work requested by the message @@ -231,46 +231,50 @@ class WorkChannelMessageHandler implements MessageHandler { // We use Optional chaining here to simplify all the cases where we short-circuit exit. // A step that returns an empty Optional means discard the chunk. // - executeInTxRollbackWhenEmpty(() -> - ( - // Use a chain of Optional flatMap to handle all the setup short-circuit exits cleanly. - Optional.of(new MessageProcess(workNotification)) - // validate and load info - .flatMap(MessageProcess::validateChunkId) - // no job definition should be retried - we must be a stale process encountering a new - // job definition. - .flatMap(MessageProcess::loadJobDefinitionOrThrow) - .flatMap(MessageProcess::loadJobInstance) - // update statuses now in the db: QUEUED->IN_PROGRESS - .flatMap(MessageProcess::updateChunkStatusAndValidate) - .flatMap(MessageProcess::updateAndValidateJobStatus) - // ready to execute - .flatMap(MessageProcess::buildCursor) - .flatMap(MessageProcess::buildStepExecutor))) - .ifPresentOrElse( - // all the setup is happy and committed. Do the work. - process -> process.myStepExector.executeStep(), - // discard the chunk - () -> ourLog.debug("Discarding chunk notification {}", workNotification)); + Optional processingPreparation = executeInTxRollbackWhenEmpty(() -> + + // Use a chain of Optional flatMap to handle all the setup short-circuit exits cleanly. + Optional.of(new MessageProcess(workNotification)) + // validate and load info + .flatMap(MessageProcess::validateChunkId) + // no job definition should be retried - we must be a stale process encountering a new + // job definition. + .flatMap(MessageProcess::loadJobDefinitionOrThrow) + .flatMap(MessageProcess::loadJobInstance) + // update statuses now in the db: QUEUED->IN_PROGRESS + .flatMap(MessageProcess::updateChunkStatusAndValidate) + .flatMap(MessageProcess::updateAndValidateJobStatus) + // ready to execute + .flatMap(MessageProcess::buildCursor) + .flatMap(MessageProcess::buildStepExecutor)); + + processingPreparation.ifPresentOrElse( + // all the setup is happy and committed. Do the work. + process -> process.myStepExector.executeStep(), + // discard the chunk + () -> ourLog.debug("Discarding chunk notification {}", workNotification)); } /** * Run theCallback in TX, rolling back if the supplied Optional is empty. */ Optional executeInTxRollbackWhenEmpty(Supplier> theCallback) { - return myHapiTransactionService.withSystemRequest().execute(theTransactionStatus -> { + return myHapiTransactionService + // batch storage is not partitioned. + .withSystemRequestOnDefaultPartition() + .execute(theTransactionStatus -> { - // run the processing - Optional setupProcessing = theCallback.get(); + // run the processing + Optional setupProcessing = theCallback.get(); - if (setupProcessing.isEmpty()) { - // If any setup failed, roll back the chunk and instance status changes. - ourLog.debug("WorkChunk setup failed - rollback tx"); - theTransactionStatus.setRollbackOnly(); - } - // else COMMIT the work. + if (setupProcessing.isEmpty()) { + // If any setup failed, roll back the chunk and instance status changes. + ourLog.debug("WorkChunk setup failed - rollback tx"); + theTransactionStatus.setRollbackOnly(); + } + // else COMMIT the work. - return setupProcessing; - }); + return setupProcessing; + }); } } diff --git a/hapi-fhir-storage-batch2/src/main/java/ca/uhn/fhir/batch2/coordinator/WorkChunkProcessor.java b/hapi-fhir-storage-batch2/src/main/java/ca/uhn/fhir/batch2/coordinator/WorkChunkProcessor.java index 0c5dd4ba7c9..0d87ecb17e1 100644 --- a/hapi-fhir-storage-batch2/src/main/java/ca/uhn/fhir/batch2/coordinator/WorkChunkProcessor.java +++ b/hapi-fhir-storage-batch2/src/main/java/ca/uhn/fhir/batch2/coordinator/WorkChunkProcessor.java @@ -29,13 +29,14 @@ import ca.uhn.fhir.batch2.model.JobDefinitionStep; import ca.uhn.fhir.batch2.model.JobInstance; import ca.uhn.fhir.batch2.model.JobWorkCursor; import ca.uhn.fhir.batch2.model.WorkChunk; +import ca.uhn.fhir.jpa.dao.tx.IHapiTransactionService; import ca.uhn.fhir.model.api.IModelJson; import ca.uhn.fhir.util.Logs; +import jakarta.annotation.Nullable; import org.apache.commons.lang3.Validate; import org.slf4j.Logger; import java.util.Optional; -import javax.annotation.Nullable; import static org.apache.commons.lang3.StringUtils.isBlank; @@ -55,11 +56,16 @@ public class WorkChunkProcessor { private final IJobPersistence myJobPersistence; private final BatchJobSender myBatchJobSender; private final StepExecutor myStepExecutor; + private final IHapiTransactionService myHapiTransactionService; - public WorkChunkProcessor(IJobPersistence theJobPersistence, BatchJobSender theSender) { + public WorkChunkProcessor( + IJobPersistence theJobPersistence, + BatchJobSender theSender, + IHapiTransactionService theHapiTransactionService) { myJobPersistence = theJobPersistence; myBatchJobSender = theSender; myStepExecutor = new StepExecutor(theJobPersistence); + myHapiTransactionService = theHapiTransactionService; } /** @@ -118,8 +124,13 @@ public class WorkChunkProcessor { dataSink = (BaseDataSink) new FinalStepDataSink<>( theJobDefinition.getJobDefinitionId(), theInstanceId, theCursor.asFinalCursor()); } else { - dataSink = - new JobDataSink<>(myBatchJobSender, myJobPersistence, theJobDefinition, theInstanceId, theCursor); + dataSink = new JobDataSink<>( + myBatchJobSender, + myJobPersistence, + theJobDefinition, + theInstanceId, + theCursor, + myHapiTransactionService); } return dataSink; } diff --git a/hapi-fhir-storage-batch2/src/main/java/ca/uhn/fhir/batch2/jobs/chunk/ChunkRangeJson.java b/hapi-fhir-storage-batch2/src/main/java/ca/uhn/fhir/batch2/jobs/chunk/ChunkRangeJson.java index ec9fdf1ec87..0efc48a5863 100644 --- a/hapi-fhir-storage-batch2/src/main/java/ca/uhn/fhir/batch2/jobs/chunk/ChunkRangeJson.java +++ b/hapi-fhir-storage-batch2/src/main/java/ca/uhn/fhir/batch2/jobs/chunk/ChunkRangeJson.java @@ -25,9 +25,9 @@ import ca.uhn.fhir.rest.server.util.JsonDateSerializer; import com.fasterxml.jackson.annotation.JsonProperty; import com.fasterxml.jackson.databind.annotation.JsonDeserialize; import com.fasterxml.jackson.databind.annotation.JsonSerialize; +import jakarta.annotation.Nonnull; import java.util.Date; -import javax.annotation.Nonnull; public class ChunkRangeJson implements IModelJson { @JsonSerialize(using = JsonDateSerializer.class) diff --git a/hapi-fhir-storage-batch2/src/main/java/ca/uhn/fhir/batch2/jobs/chunk/PartitionedUrlChunkRangeJson.java b/hapi-fhir-storage-batch2/src/main/java/ca/uhn/fhir/batch2/jobs/chunk/PartitionedUrlChunkRangeJson.java index c2fc509632d..13358b5a948 100644 --- a/hapi-fhir-storage-batch2/src/main/java/ca/uhn/fhir/batch2/jobs/chunk/PartitionedUrlChunkRangeJson.java +++ b/hapi-fhir-storage-batch2/src/main/java/ca/uhn/fhir/batch2/jobs/chunk/PartitionedUrlChunkRangeJson.java @@ -21,8 +21,7 @@ package ca.uhn.fhir.batch2.jobs.chunk; import ca.uhn.fhir.batch2.jobs.parameters.PartitionedUrl; import com.fasterxml.jackson.annotation.JsonProperty; - -import javax.annotation.Nullable; +import jakarta.annotation.Nullable; public class PartitionedUrlChunkRangeJson extends ChunkRangeJson { @Nullable diff --git a/hapi-fhir-storage-batch2/src/main/java/ca/uhn/fhir/batch2/jobs/parameters/IUrlListValidator.java b/hapi-fhir-storage-batch2/src/main/java/ca/uhn/fhir/batch2/jobs/parameters/IUrlListValidator.java index 9b04957a2b6..bc108092584 100644 --- a/hapi-fhir-storage-batch2/src/main/java/ca/uhn/fhir/batch2/jobs/parameters/IUrlListValidator.java +++ b/hapi-fhir-storage-batch2/src/main/java/ca/uhn/fhir/batch2/jobs/parameters/IUrlListValidator.java @@ -19,9 +19,10 @@ */ package ca.uhn.fhir.batch2.jobs.parameters; +import jakarta.annotation.Nonnull; +import jakarta.annotation.Nullable; + import java.util.List; -import javax.annotation.Nonnull; -import javax.annotation.Nullable; public interface IUrlListValidator { @Nullable diff --git a/hapi-fhir-storage-batch2/src/main/java/ca/uhn/fhir/batch2/jobs/parameters/PartitionedJobParameters.java b/hapi-fhir-storage-batch2/src/main/java/ca/uhn/fhir/batch2/jobs/parameters/PartitionedJobParameters.java index 097daa08a49..3aa9657a589 100644 --- a/hapi-fhir-storage-batch2/src/main/java/ca/uhn/fhir/batch2/jobs/parameters/PartitionedJobParameters.java +++ b/hapi-fhir-storage-batch2/src/main/java/ca/uhn/fhir/batch2/jobs/parameters/PartitionedJobParameters.java @@ -22,8 +22,7 @@ package ca.uhn.fhir.batch2.jobs.parameters; import ca.uhn.fhir.interceptor.model.RequestPartitionId; import ca.uhn.fhir.model.api.IModelJson; import com.fasterxml.jackson.annotation.JsonProperty; - -import javax.annotation.Nullable; +import jakarta.annotation.Nullable; public class PartitionedJobParameters implements IModelJson { @JsonProperty(value = "partitionId") diff --git a/hapi-fhir-storage-batch2/src/main/java/ca/uhn/fhir/batch2/jobs/parameters/PartitionedUrl.java b/hapi-fhir-storage-batch2/src/main/java/ca/uhn/fhir/batch2/jobs/parameters/PartitionedUrl.java index 5504f50c4b2..9990b79070e 100644 --- a/hapi-fhir-storage-batch2/src/main/java/ca/uhn/fhir/batch2/jobs/parameters/PartitionedUrl.java +++ b/hapi-fhir-storage-batch2/src/main/java/ca/uhn/fhir/batch2/jobs/parameters/PartitionedUrl.java @@ -22,11 +22,10 @@ package ca.uhn.fhir.batch2.jobs.parameters; import ca.uhn.fhir.interceptor.model.RequestPartitionId; import ca.uhn.fhir.model.api.IModelJson; import com.fasterxml.jackson.annotation.JsonProperty; +import jakarta.validation.constraints.Pattern; import org.apache.commons.lang3.builder.ToStringBuilder; import org.apache.commons.lang3.builder.ToStringStyle; -import javax.validation.constraints.Pattern; - public class PartitionedUrl implements IModelJson { @Override public String toString() { diff --git a/hapi-fhir-storage-batch2/src/main/java/ca/uhn/fhir/batch2/jobs/parameters/PartitionedUrlListJobParameters.java b/hapi-fhir-storage-batch2/src/main/java/ca/uhn/fhir/batch2/jobs/parameters/PartitionedUrlListJobParameters.java index b5de120c791..0355fa4e0d3 100644 --- a/hapi-fhir-storage-batch2/src/main/java/ca/uhn/fhir/batch2/jobs/parameters/PartitionedUrlListJobParameters.java +++ b/hapi-fhir-storage-batch2/src/main/java/ca/uhn/fhir/batch2/jobs/parameters/PartitionedUrlListJobParameters.java @@ -20,12 +20,12 @@ package ca.uhn.fhir.batch2.jobs.parameters; import com.fasterxml.jackson.annotation.JsonProperty; +import jakarta.annotation.Nonnull; +import jakarta.annotation.Nullable; import org.apache.commons.lang3.Validate; import java.util.ArrayList; import java.util.List; -import javax.annotation.Nonnull; -import javax.annotation.Nullable; public class PartitionedUrlListJobParameters extends PartitionedJobParameters { @JsonProperty("partitionedUrl") diff --git a/hapi-fhir-storage-batch2/src/main/java/ca/uhn/fhir/batch2/jobs/parameters/UrlListValidator.java b/hapi-fhir-storage-batch2/src/main/java/ca/uhn/fhir/batch2/jobs/parameters/UrlListValidator.java index 83dbf5d59e3..d4dbda52bfc 100644 --- a/hapi-fhir-storage-batch2/src/main/java/ca/uhn/fhir/batch2/jobs/parameters/UrlListValidator.java +++ b/hapi-fhir-storage-batch2/src/main/java/ca/uhn/fhir/batch2/jobs/parameters/UrlListValidator.java @@ -20,12 +20,12 @@ package ca.uhn.fhir.batch2.jobs.parameters; import ca.uhn.fhir.jpa.api.svc.IBatch2DaoSvc; +import jakarta.annotation.Nonnull; +import jakarta.annotation.Nullable; import java.util.Collections; import java.util.List; import java.util.stream.Collectors; -import javax.annotation.Nonnull; -import javax.annotation.Nullable; public class UrlListValidator implements IUrlListValidator { private final String myOperationName; diff --git a/hapi-fhir-storage-batch2/src/main/java/ca/uhn/fhir/batch2/jobs/step/GenerateRangeChunksStep.java b/hapi-fhir-storage-batch2/src/main/java/ca/uhn/fhir/batch2/jobs/step/GenerateRangeChunksStep.java index 23e590f6009..971ca7fdb82 100644 --- a/hapi-fhir-storage-batch2/src/main/java/ca/uhn/fhir/batch2/jobs/step/GenerateRangeChunksStep.java +++ b/hapi-fhir-storage-batch2/src/main/java/ca/uhn/fhir/batch2/jobs/step/GenerateRangeChunksStep.java @@ -29,10 +29,10 @@ import ca.uhn.fhir.batch2.jobs.chunk.PartitionedUrlChunkRangeJson; import ca.uhn.fhir.batch2.jobs.parameters.PartitionedUrl; import ca.uhn.fhir.batch2.jobs.parameters.PartitionedUrlListJobParameters; import ca.uhn.fhir.util.Logs; +import jakarta.annotation.Nonnull; import org.slf4j.Logger; import java.util.Date; -import javax.annotation.Nonnull; import static ca.uhn.fhir.batch2.util.Batch2Constants.BATCH_START_DATE; diff --git a/hapi-fhir-storage-batch2/src/main/java/ca/uhn/fhir/batch2/jobs/step/IIdChunkProducer.java b/hapi-fhir-storage-batch2/src/main/java/ca/uhn/fhir/batch2/jobs/step/IIdChunkProducer.java index ac16061c87c..9ed4a9d23c7 100644 --- a/hapi-fhir-storage-batch2/src/main/java/ca/uhn/fhir/batch2/jobs/step/IIdChunkProducer.java +++ b/hapi-fhir-storage-batch2/src/main/java/ca/uhn/fhir/batch2/jobs/step/IIdChunkProducer.java @@ -21,11 +21,10 @@ package ca.uhn.fhir.batch2.jobs.step; import ca.uhn.fhir.batch2.jobs.chunk.ChunkRangeJson; import ca.uhn.fhir.interceptor.model.RequestPartitionId; -import ca.uhn.fhir.jpa.api.pid.IResourcePidList; +import ca.uhn.fhir.jpa.api.pid.IResourcePidStream; +import jakarta.annotation.Nullable; import java.util.Date; -import javax.annotation.Nonnull; -import javax.annotation.Nullable; /** * A service that produces pages of resource pids based on the data provided by a previous batch step. Typically the @@ -35,19 +34,6 @@ import javax.annotation.Nullable; * @param This parameter defines constraints on the types of pids we are pulling (e.g. resource type, url, etc). */ public interface IIdChunkProducer { - /** - * Actually fetch the resource pids - * @param theNextStart pids are pulled with lastUpdated >= this date - * @param theEnd pids are pulled with lastUpdate <= this date - * @param thePageSize the number of pids to query at a time - * @param theRequestPartitionId partition for operation if rtequired - * @param theData defines the query we are using - * @return a list of Resource pids - */ - IResourcePidList fetchResourceIdsPage( - Date theNextStart, - Date theEnd, - @Nonnull Integer thePageSize, - @Nullable RequestPartitionId theRequestPartitionId, - IT theData); + IResourcePidStream fetchResourceIdStream( + Date theStart, Date theEnd, @Nullable RequestPartitionId theRequestPartitionId, IT theData); } diff --git a/hapi-fhir-storage-batch2/src/main/java/ca/uhn/fhir/batch2/jobs/step/LoadIdsStep.java b/hapi-fhir-storage-batch2/src/main/java/ca/uhn/fhir/batch2/jobs/step/LoadIdsStep.java index abc285b51bd..8209ca89fdf 100644 --- a/hapi-fhir-storage-batch2/src/main/java/ca/uhn/fhir/batch2/jobs/step/LoadIdsStep.java +++ b/hapi-fhir-storage-batch2/src/main/java/ca/uhn/fhir/batch2/jobs/step/LoadIdsStep.java @@ -28,10 +28,9 @@ import ca.uhn.fhir.batch2.jobs.chunk.PartitionedUrlChunkRangeJson; import ca.uhn.fhir.batch2.jobs.chunk.ResourceIdListWorkChunkJson; import ca.uhn.fhir.batch2.jobs.parameters.PartitionedUrlListJobParameters; import ca.uhn.fhir.jpa.api.svc.IBatch2DaoSvc; +import jakarta.annotation.Nonnull; import org.slf4j.Logger; -import javax.annotation.Nonnull; - import static org.slf4j.LoggerFactory.getLogger; public class LoadIdsStep diff --git a/hapi-fhir-storage-batch2/src/main/java/ca/uhn/fhir/batch2/jobs/step/PartitionedUrlListIdChunkProducer.java b/hapi-fhir-storage-batch2/src/main/java/ca/uhn/fhir/batch2/jobs/step/PartitionedUrlListIdChunkProducer.java index 9e23825ab12..991a71152ff 100644 --- a/hapi-fhir-storage-batch2/src/main/java/ca/uhn/fhir/batch2/jobs/step/PartitionedUrlListIdChunkProducer.java +++ b/hapi-fhir-storage-batch2/src/main/java/ca/uhn/fhir/batch2/jobs/step/PartitionedUrlListIdChunkProducer.java @@ -22,14 +22,15 @@ package ca.uhn.fhir.batch2.jobs.step; import ca.uhn.fhir.batch2.jobs.chunk.PartitionedUrlChunkRangeJson; import ca.uhn.fhir.batch2.jobs.parameters.PartitionedUrl; import ca.uhn.fhir.interceptor.model.RequestPartitionId; -import ca.uhn.fhir.jpa.api.pid.IResourcePidList; +import ca.uhn.fhir.jpa.api.pid.IResourcePidStream; import ca.uhn.fhir.jpa.api.svc.IBatch2DaoSvc; import ca.uhn.fhir.util.Logs; +import jakarta.annotation.Nullable; import org.slf4j.Logger; import java.util.Date; -import javax.annotation.Nonnull; -import javax.annotation.Nullable; + +import static org.apache.commons.lang3.ObjectUtils.defaultIfNull; public class PartitionedUrlListIdChunkProducer implements IIdChunkProducer { private static final Logger ourLog = Logs.getBatchTroubleshootingLog(); @@ -40,29 +41,27 @@ public class PartitionedUrlListIdChunkProducer implements IIdChunkProducer implements IJobStepWorker { @@ -67,49 +66,32 @@ public class ResourceIdListStep { + AtomicInteger totalIdsFound = new AtomicInteger(); + AtomicInteger chunkCount = new AtomicInteger(); - final Set idBuffer = nextChunk.getTypedResourcePids().stream() - .map(TypedPidJson::new) - .collect(Collectors.toCollection(LinkedHashSet::new)); - - final UnmodifiableIterator> partition = - Iterators.partition(idBuffer.iterator(), maxBatchId); - - while (partition.hasNext()) { - final List submissionIds = partition.next(); - - totalIdsFound += submissionIds.size(); - chunkCount++; - submitWorkChunk(submissionIds, nextChunk.getRequestPartitionId(), theDataSink); - } + Stream jsonStream = typedResourcePidStream.map(TypedPidJson::new); + // chunk by size maxBatchId and submit the batches + partition(jsonStream, chunkSize).forEach(idBatch -> { + totalIdsFound.addAndGet(idBatch.size()); + chunkCount.getAndIncrement(); + submitWorkChunk(idBatch, searchResult.getRequestPartitionId(), theDataSink); + }); ourLog.info("Submitted {} chunks with {} resource IDs", chunkCount, totalIdsFound); - } + }); + return RunOutcome.SUCCESS; } diff --git a/hapi-fhir-storage-batch2/src/main/java/ca/uhn/fhir/batch2/maintenance/JobChunkProgressAccumulator.java b/hapi-fhir-storage-batch2/src/main/java/ca/uhn/fhir/batch2/maintenance/JobChunkProgressAccumulator.java index 64709b227d6..e9b21e0f72d 100644 --- a/hapi-fhir-storage-batch2/src/main/java/ca/uhn/fhir/batch2/maintenance/JobChunkProgressAccumulator.java +++ b/hapi-fhir-storage-batch2/src/main/java/ca/uhn/fhir/batch2/maintenance/JobChunkProgressAccumulator.java @@ -24,6 +24,7 @@ import ca.uhn.fhir.batch2.model.WorkChunkStatusEnum; import ca.uhn.fhir.util.Logs; import com.google.common.collect.ArrayListMultimap; import com.google.common.collect.Multimap; +import jakarta.annotation.Nonnull; import org.apache.commons.lang3.ArrayUtils; import org.slf4j.Logger; @@ -32,7 +33,6 @@ import java.util.HashSet; import java.util.List; import java.util.Set; import java.util.stream.Collectors; -import javax.annotation.Nonnull; import static java.util.Collections.emptyList; import static org.apache.commons.lang3.ObjectUtils.defaultIfNull; diff --git a/hapi-fhir-storage-batch2/src/main/java/ca/uhn/fhir/batch2/maintenance/JobMaintenanceServiceImpl.java b/hapi-fhir-storage-batch2/src/main/java/ca/uhn/fhir/batch2/maintenance/JobMaintenanceServiceImpl.java index 899cf55ad60..b4fb6714312 100644 --- a/hapi-fhir-storage-batch2/src/main/java/ca/uhn/fhir/batch2/maintenance/JobMaintenanceServiceImpl.java +++ b/hapi-fhir-storage-batch2/src/main/java/ca/uhn/fhir/batch2/maintenance/JobMaintenanceServiceImpl.java @@ -34,6 +34,7 @@ import ca.uhn.fhir.jpa.model.sched.ISchedulerService; import ca.uhn.fhir.jpa.model.sched.ScheduledJobDefinition; import ca.uhn.fhir.util.Logs; import com.google.common.annotations.VisibleForTesting; +import jakarta.annotation.Nonnull; import org.apache.commons.lang3.Validate; import org.apache.commons.lang3.time.DateUtils; import org.quartz.JobExecutionContext; @@ -45,7 +46,6 @@ import java.util.List; import java.util.Set; import java.util.concurrent.Semaphore; import java.util.concurrent.TimeUnit; -import javax.annotation.Nonnull; /** * This class performs regular polls of the stored jobs in order to diff --git a/hapi-fhir-storage-batch2/src/main/java/ca/uhn/fhir/batch2/model/FetchJobInstancesRequest.java b/hapi-fhir-storage-batch2/src/main/java/ca/uhn/fhir/batch2/model/FetchJobInstancesRequest.java index a29e24754fe..dc719163856 100644 --- a/hapi-fhir-storage-batch2/src/main/java/ca/uhn/fhir/batch2/model/FetchJobInstancesRequest.java +++ b/hapi-fhir-storage-batch2/src/main/java/ca/uhn/fhir/batch2/model/FetchJobInstancesRequest.java @@ -19,9 +19,10 @@ */ package ca.uhn.fhir.batch2.model; +import jakarta.annotation.Nonnull; + import java.util.HashSet; import java.util.Set; -import javax.annotation.Nonnull; public class FetchJobInstancesRequest { diff --git a/hapi-fhir-storage-batch2/src/main/java/ca/uhn/fhir/batch2/model/JobDefinition.java b/hapi-fhir-storage-batch2/src/main/java/ca/uhn/fhir/batch2/model/JobDefinition.java index 8a7aea8190b..aced7d86fc5 100644 --- a/hapi-fhir-storage-batch2/src/main/java/ca/uhn/fhir/batch2/model/JobDefinition.java +++ b/hapi-fhir-storage-batch2/src/main/java/ca/uhn/fhir/batch2/model/JobDefinition.java @@ -28,6 +28,8 @@ import ca.uhn.fhir.context.ConfigurationException; import ca.uhn.fhir.i18n.Msg; import ca.uhn.fhir.model.api.IModelJson; import ca.uhn.fhir.util.Logs; +import jakarta.annotation.Nonnull; +import jakarta.annotation.Nullable; import org.apache.commons.lang3.Validate; import org.slf4j.Logger; @@ -35,8 +37,6 @@ import java.util.ArrayList; import java.util.Collections; import java.util.List; import java.util.stream.Collectors; -import javax.annotation.Nonnull; -import javax.annotation.Nullable; public class JobDefinition { private static final Logger ourLog = Logs.getBatchTroubleshootingLog(); @@ -345,8 +345,8 @@ public class JobDefinition { *

    * Validation: * Fields should be annotated with - * any appropriate javax.validation (JSR 380) annotations (e.g. - * {@link javax.validation.constraints.Min} or {@link javax.validation.constraints.Pattern}). + * any appropriate jakarta.validation (JSR 380) annotations (e.g. + * {@link jakarta.validation.constraints.Min} or {@link jakarta.validation.constraints.Pattern}). * In addition, if there are validation rules that are too complex to express using * JSR 380, you can also specify a programmatic validator using {@link #setParametersValidator(IJobParametersValidator)}. *

    @@ -357,7 +357,7 @@ public class JobDefinition { *

    * * @see ca.uhn.fhir.model.api.annotation.PasswordField - * @see javax.validation.constraints + * @see jakarta.validation.constraints * @see JobDefinition.Builder#setParametersValidator(IJobParametersValidator) */ @SuppressWarnings("unchecked") diff --git a/hapi-fhir-storage-batch2/src/main/java/ca/uhn/fhir/batch2/model/JobDefinitionReductionStep.java b/hapi-fhir-storage-batch2/src/main/java/ca/uhn/fhir/batch2/model/JobDefinitionReductionStep.java index 93cd06ca461..90f4878ff54 100644 --- a/hapi-fhir-storage-batch2/src/main/java/ca/uhn/fhir/batch2/model/JobDefinitionReductionStep.java +++ b/hapi-fhir-storage-batch2/src/main/java/ca/uhn/fhir/batch2/model/JobDefinitionReductionStep.java @@ -22,8 +22,7 @@ package ca.uhn.fhir.batch2.model; import ca.uhn.fhir.batch2.api.IJobStepWorker; import ca.uhn.fhir.batch2.api.IReductionStepWorker; import ca.uhn.fhir.model.api.IModelJson; - -import javax.annotation.Nonnull; +import jakarta.annotation.Nonnull; public class JobDefinitionReductionStep extends JobDefinitionStep { diff --git a/hapi-fhir-storage-batch2/src/main/java/ca/uhn/fhir/batch2/model/JobDefinitionStep.java b/hapi-fhir-storage-batch2/src/main/java/ca/uhn/fhir/batch2/model/JobDefinitionStep.java index 47b5e6a32a6..2ec27a81ec4 100644 --- a/hapi-fhir-storage-batch2/src/main/java/ca/uhn/fhir/batch2/model/JobDefinitionStep.java +++ b/hapi-fhir-storage-batch2/src/main/java/ca/uhn/fhir/batch2/model/JobDefinitionStep.java @@ -21,10 +21,9 @@ package ca.uhn.fhir.batch2.model; import ca.uhn.fhir.batch2.api.IJobStepWorker; import ca.uhn.fhir.model.api.IModelJson; +import jakarta.annotation.Nonnull; import org.apache.commons.lang3.Validate; -import javax.annotation.Nonnull; - import static ca.uhn.fhir.batch2.model.JobDefinition.ID_MAX_LENGTH; public class JobDefinitionStep { diff --git a/hapi-fhir-storage-batch2/src/main/java/ca/uhn/fhir/batch2/model/JobInstance.java b/hapi-fhir-storage-batch2/src/main/java/ca/uhn/fhir/batch2/model/JobInstance.java index 09fb8344dea..4294a27fefc 100644 --- a/hapi-fhir-storage-batch2/src/main/java/ca/uhn/fhir/batch2/model/JobInstance.java +++ b/hapi-fhir-storage-batch2/src/main/java/ca/uhn/fhir/batch2/model/JobInstance.java @@ -116,6 +116,12 @@ public class JobInstance implements IModelJson, IJobInstance { @JsonProperty(value = "warningMessages", access = JsonProperty.Access.READ_ONLY) private String myWarningMessages; + @JsonProperty(value = "triggeringUsername", access = JsonProperty.Access.READ_ONLY) + private String myTriggeringUsername; + + @JsonProperty(value = "triggeringClientId", access = JsonProperty.Access.READ_ONLY) + private String myTriggeringClientId; + /** * Constructor */ @@ -149,6 +155,8 @@ public class JobInstance implements IModelJson, IJobInstance { setCurrentGatedStepId(theJobInstance.getCurrentGatedStepId()); setReport(theJobInstance.getReport()); setWarningMessages(theJobInstance.getWarningMessages()); + setTriggeringUsername(theJobInstance.getTriggeringUsername()); + setTriggeringClientId(theJobInstance.getTriggeringClientId()); } public String getJobDefinitionId() { @@ -375,6 +383,24 @@ public class JobInstance implements IModelJson, IJobInstance { myReport = theReport; } + public String getTriggeringUsername() { + return myTriggeringUsername; + } + + public JobInstance setTriggeringUsername(String theTriggeringUsername) { + myTriggeringUsername = theTriggeringUsername; + return this; + } + + public String getTriggeringClientId() { + return myTriggeringClientId; + } + + public JobInstance setTriggeringClientId(String theTriggeringClientId) { + myTriggeringClientId = theTriggeringClientId; + return this; + } + @Override public String toString() { return new ToStringBuilder(this, ToStringStyle.SHORT_PREFIX_STYLE) @@ -396,6 +422,8 @@ public class JobInstance implements IModelJson, IJobInstance { .append("estimatedTimeRemaining", myEstimatedTimeRemaining) .append("report", myReport) .append("warningMessages", myWarningMessages) + .append("triggeringUsername", myTriggeringUsername) + .append("triggeringClientId", myTriggeringClientId) .toString(); } diff --git a/hapi-fhir-storage-batch2/src/main/java/ca/uhn/fhir/batch2/model/JobWorkNotification.java b/hapi-fhir-storage-batch2/src/main/java/ca/uhn/fhir/batch2/model/JobWorkNotification.java index 55246403af3..bd53df05ee3 100644 --- a/hapi-fhir-storage-batch2/src/main/java/ca/uhn/fhir/batch2/model/JobWorkNotification.java +++ b/hapi-fhir-storage-batch2/src/main/java/ca/uhn/fhir/batch2/model/JobWorkNotification.java @@ -21,8 +21,7 @@ package ca.uhn.fhir.batch2.model; import ca.uhn.fhir.model.api.IModelJson; import com.fasterxml.jackson.annotation.JsonProperty; - -import javax.annotation.Nonnull; +import jakarta.annotation.Nonnull; public class JobWorkNotification implements IModelJson { diff --git a/hapi-fhir-storage-batch2/src/main/java/ca/uhn/fhir/batch2/model/JobWorkNotificationJsonMessage.java b/hapi-fhir-storage-batch2/src/main/java/ca/uhn/fhir/batch2/model/JobWorkNotificationJsonMessage.java index f5413b63210..8c32b032371 100644 --- a/hapi-fhir-storage-batch2/src/main/java/ca/uhn/fhir/batch2/model/JobWorkNotificationJsonMessage.java +++ b/hapi-fhir-storage-batch2/src/main/java/ca/uhn/fhir/batch2/model/JobWorkNotificationJsonMessage.java @@ -21,8 +21,7 @@ package ca.uhn.fhir.batch2.model; import ca.uhn.fhir.rest.server.messaging.json.BaseJsonMessage; import com.fasterxml.jackson.annotation.JsonProperty; - -import javax.annotation.Nonnull; +import jakarta.annotation.Nonnull; public class JobWorkNotificationJsonMessage extends BaseJsonMessage { diff --git a/hapi-fhir-storage-batch2/src/main/java/ca/uhn/fhir/batch2/model/StatusEnum.java b/hapi-fhir-storage-batch2/src/main/java/ca/uhn/fhir/batch2/model/StatusEnum.java index 2526b352a3e..a99df83142e 100644 --- a/hapi-fhir-storage-batch2/src/main/java/ca/uhn/fhir/batch2/model/StatusEnum.java +++ b/hapi-fhir-storage-batch2/src/main/java/ca/uhn/fhir/batch2/model/StatusEnum.java @@ -22,6 +22,7 @@ package ca.uhn.fhir.batch2.model; import ca.uhn.fhir.i18n.Msg; import ca.uhn.fhir.util.Logs; import com.google.common.collect.Maps; +import jakarta.annotation.Nonnull; import org.slf4j.Logger; import java.util.Collections; @@ -29,7 +30,6 @@ import java.util.EnumMap; import java.util.EnumSet; import java.util.Map; import java.util.Set; -import javax.annotation.Nonnull; /** * Status of a Batch2 Job Instance. diff --git a/hapi-fhir-storage-batch2/src/main/java/ca/uhn/fhir/batch2/model/WorkChunkCreateEvent.java b/hapi-fhir-storage-batch2/src/main/java/ca/uhn/fhir/batch2/model/WorkChunkCreateEvent.java index c381711c5db..bdbf4e87983 100644 --- a/hapi-fhir-storage-batch2/src/main/java/ca/uhn/fhir/batch2/model/WorkChunkCreateEvent.java +++ b/hapi-fhir-storage-batch2/src/main/java/ca/uhn/fhir/batch2/model/WorkChunkCreateEvent.java @@ -19,12 +19,11 @@ */ package ca.uhn.fhir.batch2.model; +import jakarta.annotation.Nonnull; +import jakarta.annotation.Nullable; import org.apache.commons.lang3.builder.EqualsBuilder; import org.apache.commons.lang3.builder.HashCodeBuilder; -import javax.annotation.Nonnull; -import javax.annotation.Nullable; - /** * The data required for the create transition. * Payload for the work-chunk creation event including all the job coordinates, the chunk data, and a sequence within the step. diff --git a/hapi-fhir-storage-batch2/src/main/java/ca/uhn/fhir/batch2/progress/JobInstanceProgressCalculator.java b/hapi-fhir-storage-batch2/src/main/java/ca/uhn/fhir/batch2/progress/JobInstanceProgressCalculator.java index 1cfde264368..210df77d700 100644 --- a/hapi-fhir-storage-batch2/src/main/java/ca/uhn/fhir/batch2/progress/JobInstanceProgressCalculator.java +++ b/hapi-fhir-storage-batch2/src/main/java/ca/uhn/fhir/batch2/progress/JobInstanceProgressCalculator.java @@ -25,10 +25,10 @@ import ca.uhn.fhir.batch2.maintenance.JobChunkProgressAccumulator; import ca.uhn.fhir.batch2.model.WorkChunk; import ca.uhn.fhir.util.Logs; import ca.uhn.fhir.util.StopWatch; +import jakarta.annotation.Nonnull; import org.slf4j.Logger; import java.util.Iterator; -import javax.annotation.Nonnull; public class JobInstanceProgressCalculator { private static final Logger ourLog = Logs.getBatchTroubleshootingLog(); diff --git a/hapi-fhir-storage-batch2/src/test/java/ca/uhn/fhir/batch2/coordinator/BaseBatch2Test.java b/hapi-fhir-storage-batch2/src/test/java/ca/uhn/fhir/batch2/coordinator/BaseBatch2Test.java index 25b6536e323..39c45190f55 100644 --- a/hapi-fhir-storage-batch2/src/test/java/ca/uhn/fhir/batch2/coordinator/BaseBatch2Test.java +++ b/hapi-fhir-storage-batch2/src/test/java/ca/uhn/fhir/batch2/coordinator/BaseBatch2Test.java @@ -10,7 +10,7 @@ import org.junit.jupiter.api.extension.ExtendWith; import org.mockito.Mock; import org.mockito.junit.jupiter.MockitoExtension; -import javax.annotation.Nonnull; +import jakarta.annotation.Nonnull; import java.util.function.Consumer; @ExtendWith(MockitoExtension.class) diff --git a/hapi-fhir-storage-batch2/src/test/java/ca/uhn/fhir/batch2/coordinator/JobCoordinatorImplTest.java b/hapi-fhir-storage-batch2/src/test/java/ca/uhn/fhir/batch2/coordinator/JobCoordinatorImplTest.java index 12ef54af08c..ebd682f2386 100644 --- a/hapi-fhir-storage-batch2/src/test/java/ca/uhn/fhir/batch2/coordinator/JobCoordinatorImplTest.java +++ b/hapi-fhir-storage-batch2/src/test/java/ca/uhn/fhir/batch2/coordinator/JobCoordinatorImplTest.java @@ -41,7 +41,7 @@ import org.mockito.junit.jupiter.MockitoExtension; import org.mockito.stubbing.Answer; import org.springframework.messaging.MessageDeliveryException; -import javax.annotation.Nonnull; +import jakarta.annotation.Nonnull; import java.util.Arrays; import java.util.Optional; import java.util.concurrent.atomic.AtomicInteger; @@ -94,7 +94,7 @@ public class JobCoordinatorImplTest extends BaseBatch2Test { public void beforeEach() { // The code refactored to keep the same functionality, // but in this service (so it's a real service here!) - WorkChunkProcessor jobStepExecutorSvc = new WorkChunkProcessor(myJobInstancePersister, myBatchJobSender); + WorkChunkProcessor jobStepExecutorSvc = new WorkChunkProcessor(myJobInstancePersister, myBatchJobSender, new NonTransactionalHapiTransactionService()); mySvc = new JobCoordinatorImpl(myBatchJobSender, myWorkChannelReceiver, myJobInstancePersister, myJobDefinitionRegistry, jobStepExecutorSvc, myJobMaintenanceService, myTransactionService); } diff --git a/hapi-fhir-storage-batch2/src/test/java/ca/uhn/fhir/batch2/coordinator/JobDataSinkTest.java b/hapi-fhir-storage-batch2/src/test/java/ca/uhn/fhir/batch2/coordinator/JobDataSinkTest.java index 1fb4274bcbe..5b2f89dae96 100644 --- a/hapi-fhir-storage-batch2/src/test/java/ca/uhn/fhir/batch2/coordinator/JobDataSinkTest.java +++ b/hapi-fhir-storage-batch2/src/test/java/ca/uhn/fhir/batch2/coordinator/JobDataSinkTest.java @@ -14,6 +14,8 @@ import ca.uhn.fhir.batch2.model.JobInstance; import ca.uhn.fhir.batch2.model.JobWorkCursor; import ca.uhn.fhir.batch2.model.JobWorkNotification; import ca.uhn.fhir.batch2.model.WorkChunkCreateEvent; +import ca.uhn.fhir.jpa.dao.tx.IHapiTransactionService; +import ca.uhn.fhir.jpa.dao.tx.NonTransactionalHapiTransactionService; import ca.uhn.fhir.model.api.IModelJson; import ca.uhn.fhir.util.JsonUtil; import com.fasterxml.jackson.annotation.JsonProperty; @@ -24,13 +26,14 @@ import org.mockito.Captor; import org.mockito.Mock; import org.mockito.junit.jupiter.MockitoExtension; -import javax.annotation.Nonnull; +import jakarta.annotation.Nonnull; import java.util.ArrayList; import java.util.List; import static org.hamcrest.MatcherAssert.assertThat; import static org.hamcrest.Matchers.hasSize; import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNotNull; import static org.junit.jupiter.api.Assertions.fail; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; @@ -54,6 +57,7 @@ class JobDataSinkTest { private ArgumentCaptor myJobWorkNotificationCaptor; @Captor private ArgumentCaptor myBatchWorkChunkCaptor; + private final IHapiTransactionService myHapiTransactionService = new NonTransactionalHapiTransactionService(); @Test public void test_sink_accept() { @@ -98,7 +102,7 @@ class JobDataSinkTest { JobInstance instance = JobInstance.fromInstanceId(JOB_INSTANCE_ID); StepExecutionDetails details = new StepExecutionDetails<>(new TestJobParameters().setParam1("" + PID_COUNT), null, instance, CHUNK_ID); JobWorkCursor cursor = new JobWorkCursor<>(job, true, firstStep, lastStep); - JobDataSink sink = new JobDataSink<>(myBatchJobSender, myJobPersistence, job, JOB_INSTANCE_ID, cursor); + JobDataSink sink = new JobDataSink<>(myBatchJobSender, myJobPersistence, job, JOB_INSTANCE_ID, cursor, myHapiTransactionService); RunOutcome result = firstStepWorker.run(details, sink); @@ -122,6 +126,7 @@ class JobDataSinkTest { assertEquals(JOB_DEF_ID, batchWorkChunk.jobDefinitionId); assertEquals(JOB_INSTANCE_ID, batchWorkChunk.instanceId); assertEquals(LAST_STEP_ID, batchWorkChunk.targetStepId); + assertNotNull(batchWorkChunk.serializedData); Step1Output stepOutput = JsonUtil.deserialize(batchWorkChunk.serializedData, Step1Output.class); assertThat(stepOutput.getPids(), hasSize(PID_COUNT)); } diff --git a/hapi-fhir-storage-batch2/src/test/java/ca/uhn/fhir/batch2/coordinator/TestJobParameters.java b/hapi-fhir-storage-batch2/src/test/java/ca/uhn/fhir/batch2/coordinator/TestJobParameters.java index bea3a00065f..3bfb3b14ca3 100644 --- a/hapi-fhir-storage-batch2/src/test/java/ca/uhn/fhir/batch2/coordinator/TestJobParameters.java +++ b/hapi-fhir-storage-batch2/src/test/java/ca/uhn/fhir/batch2/coordinator/TestJobParameters.java @@ -5,7 +5,7 @@ import ca.uhn.fhir.model.api.IModelJson; import com.fasterxml.jackson.annotation.JsonProperty; import org.hibernate.validator.constraints.Length; -import javax.validation.constraints.NotBlank; +import jakarta.validation.constraints.NotBlank; public class TestJobParameters implements IModelJson { diff --git a/hapi-fhir-storage-batch2/src/test/java/ca/uhn/fhir/batch2/coordinator/WorkChunkProcessorTest.java b/hapi-fhir-storage-batch2/src/test/java/ca/uhn/fhir/batch2/coordinator/WorkChunkProcessorTest.java index 83cc133cb95..10f1a007e5c 100644 --- a/hapi-fhir-storage-batch2/src/test/java/ca/uhn/fhir/batch2/coordinator/WorkChunkProcessorTest.java +++ b/hapi-fhir-storage-batch2/src/test/java/ca/uhn/fhir/batch2/coordinator/WorkChunkProcessorTest.java @@ -21,6 +21,7 @@ import ca.uhn.fhir.batch2.model.WorkChunkCompletionEvent; import ca.uhn.fhir.batch2.model.WorkChunkData; import ca.uhn.fhir.batch2.model.WorkChunkErrorEvent; import ca.uhn.fhir.batch2.model.WorkChunkStatusEnum; +import ca.uhn.fhir.jpa.dao.tx.NonTransactionalHapiTransactionService; import ca.uhn.fhir.model.api.IModelJson; import ca.uhn.fhir.util.JsonUtil; import org.junit.jupiter.api.BeforeEach; @@ -428,7 +429,7 @@ public class WorkChunkProcessorTest { private class TestWorkChunkProcessor extends WorkChunkProcessor { public TestWorkChunkProcessor(IJobPersistence thePersistence, BatchJobSender theSender) { - super(thePersistence, theSender); + super(thePersistence, theSender, new NonTransactionalHapiTransactionService()); } @Override diff --git a/hapi-fhir-storage-batch2/src/test/java/ca/uhn/fhir/batch2/jobs/step/LoadIdsStepTest.java b/hapi-fhir-storage-batch2/src/test/java/ca/uhn/fhir/batch2/jobs/step/LoadIdsStepTest.java index ac06d4ba3ce..25a4967fcad 100644 --- a/hapi-fhir-storage-batch2/src/test/java/ca/uhn/fhir/batch2/jobs/step/LoadIdsStepTest.java +++ b/hapi-fhir-storage-batch2/src/test/java/ca/uhn/fhir/batch2/jobs/step/LoadIdsStepTest.java @@ -8,6 +8,8 @@ import ca.uhn.fhir.batch2.jobs.parameters.PartitionedUrlListJobParameters; import ca.uhn.fhir.batch2.model.JobInstance; import ca.uhn.fhir.jpa.api.pid.HomogeneousResourcePidList; import ca.uhn.fhir.jpa.api.pid.IResourcePidList; +import ca.uhn.fhir.jpa.api.pid.IResourcePidStream; +import ca.uhn.fhir.jpa.api.pid.ListWrappingPidStream; import ca.uhn.fhir.jpa.api.svc.IBatch2DaoSvc; import ca.uhn.fhir.jpa.model.dao.JpaPid; import ca.uhn.fhir.rest.api.server.storage.IResourcePersistentId; @@ -20,12 +22,11 @@ import org.mockito.Captor; import org.mockito.Mock; import org.mockito.junit.jupiter.MockitoExtension; -import javax.annotation.Nonnull; +import jakarta.annotation.Nonnull; import java.util.ArrayList; import java.util.Date; import java.util.List; -import static ca.uhn.fhir.batch2.jobs.step.ResourceIdListStep.DEFAULT_PAGE_SIZE; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.mockito.ArgumentMatchers.eq; import static org.mockito.ArgumentMatchers.isNull; @@ -68,7 +69,7 @@ public class LoadIdsStepTest { // First Execution - when(myBatch2DaoSvc.fetchResourceIdsPage(eq(DATE_1), eq(DATE_END), eq(DEFAULT_PAGE_SIZE), isNull(), isNull())) + when(myBatch2DaoSvc.fetchResourceIdStream(eq(DATE_1), eq(DATE_END), isNull(), isNull())) .thenReturn(createIdChunk(0L, 20000L, DATE_2)); mySvc.run(details, mySink); @@ -96,14 +97,14 @@ public class LoadIdsStepTest { } @Nonnull - private IResourcePidList createIdChunk(long idLow, long idHigh, Date lastDate) { + private IResourcePidStream createIdChunk(long idLow, long idHigh, Date lastDate) { List ids = new ArrayList<>(); List resourceTypes = new ArrayList<>(); for (long i = idLow; i < idHigh; i++) { ids.add(JpaPid.fromId(i)); } IResourcePidList chunk = new HomogeneousResourcePidList("Patient", ids, lastDate, null); - return chunk; + return new ListWrappingPidStream(chunk); } } diff --git a/hapi-fhir-storage-batch2/src/test/java/ca/uhn/fhir/batch2/jobs/step/ResourceIdListStepTest.java b/hapi-fhir-storage-batch2/src/test/java/ca/uhn/fhir/batch2/jobs/step/ResourceIdListStepTest.java index 3670bfa2bc5..1806b5428ea 100644 --- a/hapi-fhir-storage-batch2/src/test/java/ca/uhn/fhir/batch2/jobs/step/ResourceIdListStepTest.java +++ b/hapi-fhir-storage-batch2/src/test/java/ca/uhn/fhir/batch2/jobs/step/ResourceIdListStepTest.java @@ -7,7 +7,8 @@ import ca.uhn.fhir.batch2.jobs.chunk.PartitionedUrlChunkRangeJson; import ca.uhn.fhir.batch2.jobs.chunk.ResourceIdListWorkChunkJson; import ca.uhn.fhir.batch2.jobs.parameters.PartitionedUrlListJobParameters; import ca.uhn.fhir.jpa.api.pid.HomogeneousResourcePidList; -import ca.uhn.fhir.jpa.api.pid.TypedResourcePid; +import ca.uhn.fhir.jpa.api.pid.IResourcePidStream; +import ca.uhn.fhir.jpa.api.pid.ListWrappingPidStream; import ca.uhn.fhir.rest.api.server.storage.IResourcePersistentId; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.BeforeEach; @@ -20,7 +21,6 @@ import org.mockito.Mock; import org.mockito.junit.jupiter.MockitoExtension; import java.util.ArrayList; -import java.util.Date; import java.util.List; import static org.junit.jupiter.api.Assertions.assertEquals; @@ -58,15 +58,13 @@ class ResourceIdListStepTest { @ParameterizedTest @ValueSource(ints = {0, 1, 100, 500, 501, 2345, 10500}) void testResourceIdListBatchSizeLimit(int theListSize) { - List idList = generateIdList(theListSize); + List idList = generateIdList(theListSize); when(myStepExecutionDetails.getData()).thenReturn(myData); - when(myParameters.getBatchSize()).thenReturn(theListSize); + when(myParameters.getBatchSize()).thenReturn(500); when(myStepExecutionDetails.getParameters()).thenReturn(myParameters); - HomogeneousResourcePidList homogeneousResourcePidList = mock(HomogeneousResourcePidList.class); + IResourcePidStream mockStream = new ListWrappingPidStream( + new HomogeneousResourcePidList("Patient", idList, null, null)); if (theListSize > 0) { - when(homogeneousResourcePidList.getTypedResourcePids()).thenReturn(idList); - when(homogeneousResourcePidList.getLastDate()).thenReturn(new Date()); - when(homogeneousResourcePidList.isEmpty()).thenReturn(false); // Ensure none of the work chunks exceed MAX_BATCH_OF_IDS in size: doAnswer(i -> { ResourceIdListWorkChunkJson list = i.getArgument(0); @@ -74,12 +72,9 @@ class ResourceIdListStepTest { "Id batch size should never exceed " + ResourceIdListStep.MAX_BATCH_OF_IDS); return null; }).when(myDataSink).accept(any(ResourceIdListWorkChunkJson.class)); - } else { - when(homogeneousResourcePidList.isEmpty()).thenReturn(true); } - when(myIdChunkProducer.fetchResourceIdsPage(any(), any(), any(), any(), any())) - .thenReturn(homogeneousResourcePidList); - + when(myIdChunkProducer.fetchResourceIdStream(any(), any(), any(), any())) + .thenReturn(mockStream); final RunOutcome run = myResourceIdListStep.run(myStepExecutionDetails, myDataSink); assertNotEquals(null, run); @@ -103,13 +98,12 @@ class ResourceIdListStepTest { } } - private List generateIdList(int theListSize) { - List idList = new ArrayList<>(); + private List generateIdList(int theListSize) { + List idList = new ArrayList<>(); for (int id = 0; id < theListSize; id++) { - IResourcePersistentId theId = mock(IResourcePersistentId.class); + IResourcePersistentId theId = mock(IResourcePersistentId.class); when(theId.toString()).thenReturn(Integer.toString(id + 1)); - TypedResourcePid typedId = new TypedResourcePid("Patient", theId); - idList.add(typedId); + idList.add(theId); } return idList; } diff --git a/hapi-fhir-storage-batch2/src/test/java/ca/uhn/fhir/batch2/maintenance/JobMaintenanceServiceImplTest.java b/hapi-fhir-storage-batch2/src/test/java/ca/uhn/fhir/batch2/maintenance/JobMaintenanceServiceImplTest.java index 13104c12187..ba11ac13560 100644 --- a/hapi-fhir-storage-batch2/src/test/java/ca/uhn/fhir/batch2/maintenance/JobMaintenanceServiceImplTest.java +++ b/hapi-fhir-storage-batch2/src/test/java/ca/uhn/fhir/batch2/maintenance/JobMaintenanceServiceImplTest.java @@ -19,6 +19,7 @@ import ca.uhn.fhir.batch2.model.WorkChunkStatusEnum; import ca.uhn.fhir.jpa.api.config.JpaStorageSettings; import ca.uhn.fhir.jpa.model.sched.ISchedulerService; import ca.uhn.fhir.jpa.subscription.channel.api.IChannelProducer; +import ca.uhn.fhir.util.Logs; import ca.uhn.test.util.LogbackCaptureTestExtension; import ch.qos.logback.classic.Level; import ch.qos.logback.classic.Logger; @@ -34,6 +35,7 @@ import org.mockito.Captor; import org.mockito.Mock; import org.mockito.Spy; import org.mockito.junit.jupiter.MockitoExtension; +import org.slf4j.LoggerFactory; import org.springframework.messaging.Message; import java.util.Arrays; @@ -68,7 +70,7 @@ import static org.mockito.Mockito.when; public class JobMaintenanceServiceImplTest extends BaseBatch2Test { @RegisterExtension - LogbackCaptureTestExtension myLogCapture = new LogbackCaptureTestExtension((Logger) JobMaintenanceServiceImpl.ourLog, Level.WARN); + LogbackCaptureTestExtension myLogCapture = new LogbackCaptureTestExtension((Logger) LoggerFactory.getLogger("ca.uhn.fhir.log.batch_troubleshooting"), Level.WARN); @Mock IJobCompletionHandler myCompletionHandler; @Mock diff --git a/hapi-fhir-storage-cr/pom.xml b/hapi-fhir-storage-cr/pom.xml index bf135680f03..9ff0f31d888 100644 --- a/hapi-fhir-storage-cr/pom.xml +++ b/hapi-fhir-storage-cr/pom.xml @@ -7,7 +7,7 @@ ca.uhn.hapi.fhir hapi-deployable-pom - 6.9.7-SNAPSHOT + 6.11.7-SNAPSHOT ../hapi-deployable-pom/pom.xml @@ -19,7 +19,6 @@ 4.10.1 - 1.1.6 5.7.8 @@ -66,10 +65,40 @@ hapi-fhir-base ${project.version}
    + + com.icegreen + greenmail + 1.6.4 + compile + + + + com.sun.activation + jakarta.activation + + + + + org.simplejavamail + simple-java-mail + 6.6.1 + + + + com.sun.activation + jakarta.activation + + + + + com.fasterxml.jackson.module + jackson-module-jakarta-xmlbind-annotations + + ca.uhn.hapi.fhir hapi-fhir-converter @@ -84,52 +113,23 @@ org.opencds.cqf.fhir cqf-fhir-jackson ${clinical-reasoning.version} - - - xpp3 - xpp3_min - - - xmlpull - xmlpull - - - xmlpull - xmlpull - - pom - - org.opencds.cqf.fhir - cqf-fhir-cql - ${clinical-reasoning.version} - - - xpp3 - xpp3_min - - - xmlpull - xmlpull - - - xmlpull - xmlpull - - - pom - - - org.ogce - xpp3 - ${xpp3-version} - - ca.uhn.hapi.fhir hapi-fhir-storage ${project.version} + + + com.sun.activation + jakarta.activation + + + + + + org.apache.poi + poi @@ -141,8 +141,13 @@ - javax.servlet - javax.servlet-api + jakarta.servlet + jakarta.servlet-api + + + + jakarta.xml.bind + jakarta.xml.bind-api @@ -185,10 +190,6 @@ ${project.version} test - - org.apache.poi - poi - diff --git a/hapi-fhir-storage-cr/src/main/java/ca/uhn/fhir/cr/common/CodeCacheResourceChangeListener.java b/hapi-fhir-storage-cr/src/main/java/ca/uhn/fhir/cr/common/CodeCacheResourceChangeListener.java index 8055ea1b1b4..4c7a8556c13 100644 --- a/hapi-fhir-storage-cr/src/main/java/ca/uhn/fhir/cr/common/CodeCacheResourceChangeListener.java +++ b/hapi-fhir-storage-cr/src/main/java/ca/uhn/fhir/cr/common/CodeCacheResourceChangeListener.java @@ -25,7 +25,6 @@ import ca.uhn.fhir.jpa.cache.IResourceChangeEvent; import ca.uhn.fhir.jpa.cache.IResourceChangeListener; import ca.uhn.fhir.rest.server.exceptions.ResourceGoneException; import ca.uhn.fhir.rest.server.exceptions.ResourceNotFoundException; -import org.hl7.elm.r1.VersionedIdentifier; import org.hl7.fhir.instance.model.api.IBaseResource; import org.hl7.fhir.instance.model.api.IIdType; import org.opencds.cqf.cql.engine.runtime.Code; @@ -45,14 +44,13 @@ public class CodeCacheResourceChangeListener implements IResourceChangeListener org.slf4j.LoggerFactory.getLogger(CodeCacheResourceChangeListener.class); private final IFhirResourceDao myValueSetDao; - private final Map> myGlobalCodeCache; + private final Map> myGlobalValueSetCache; private final Function myUrlFunction; private final Function myVersionFunction; - public CodeCacheResourceChangeListener( - DaoRegistry theDaoRegistry, Map> theGlobalCodeCache) { + public CodeCacheResourceChangeListener(DaoRegistry theDaoRegistry, Map> theGlobalValueSetCache) { this.myValueSetDao = theDaoRegistry.getResourceDao("ValueSet"); - this.myGlobalCodeCache = theGlobalCodeCache; + this.myGlobalValueSetCache = theGlobalValueSetCache; this.myUrlFunction = Reflections.getUrlFunction(myValueSetDao.getResourceType()); this.myVersionFunction = Reflections.getVersionFunction(myValueSetDao.getResourceType()); } @@ -89,7 +87,7 @@ public class CodeCacheResourceChangeListener implements IResourceChangeListener IBaseResource valueSet; try { - valueSet = this.myValueSetDao.read(theId); + valueSet = this.myValueSetDao.read(theId.toUnqualifiedVersionless()); } // This happens when a Library is deleted entirely, so it's impossible to look up // name and version. @@ -97,13 +95,20 @@ public class CodeCacheResourceChangeListener implements IResourceChangeListener ourLog.debug( "Failed to locate resource {} to look up url and version. Clearing all codes from cache.", theId.getValueAsString()); - this.myGlobalCodeCache.clear(); + myGlobalValueSetCache.clear(); return; } String url = this.myUrlFunction.apply(valueSet); - String version = this.myVersionFunction.apply(valueSet); - this.myGlobalCodeCache.remove(new VersionedIdentifier().withId(url).withVersion(version)); + var valuesets = myGlobalValueSetCache.keySet(); + + for (String key : valuesets) { + var urlKey = key; + if (urlKey.contains(url)) { + myGlobalValueSetCache.remove(key); + ourLog.warn("Successfully removed valueSet from ValueSetCache: " + url + " due to updated resource"); + } + } } } diff --git a/hapi-fhir-storage-cr/src/main/java/ca/uhn/fhir/cr/config/CrConfigCondition.java b/hapi-fhir-storage-cr/src/main/java/ca/uhn/fhir/cr/config/CrConfigCondition.java new file mode 100644 index 00000000000..0461701e492 --- /dev/null +++ b/hapi-fhir-storage-cr/src/main/java/ca/uhn/fhir/cr/config/CrConfigCondition.java @@ -0,0 +1,60 @@ +/*- + * #%L + * HAPI FHIR - Clinical Reasoning + * %% + * Copyright (C) 2014 - 2023 Smile CDR, Inc. + * %% + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * #L% + */ +package ca.uhn.fhir.cr.config; + +import ca.uhn.fhir.rest.server.RestfulServer; +import org.opencds.cqf.fhir.cql.EvaluationSettings; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.config.ConfigurableListableBeanFactory; +import org.springframework.context.annotation.Condition; +import org.springframework.context.annotation.ConditionContext; +import org.springframework.core.type.AnnotatedTypeMetadata; + +/** + * The purpose of this Condition is to verify that the CR dependent beans RestfulServer and EvaluationSettings exist. + */ +public class CrConfigCondition implements Condition { + private static final Logger ourLog = LoggerFactory.getLogger(CrConfigCondition.class); + + @Override + public boolean matches(ConditionContext theConditionContext, AnnotatedTypeMetadata theAnnotatedTypeMetadata) { + ConfigurableListableBeanFactory beanFactory = theConditionContext.getBeanFactory(); + try { + RestfulServer bean = beanFactory.getBean(RestfulServer.class); + if (bean == null) { + return false; + } + } catch (Exception e) { + ourLog.warn("CrConfigCondition not met: Missing RestfulServer bean"); + return false; + } + try { + EvaluationSettings bean = beanFactory.getBean(EvaluationSettings.class); + if (bean == null) { + return false; + } + } catch (Exception e) { + ourLog.warn("CrConfigCondition not met: Missing EvaluationSettings bean"); + return false; + } + return true; + } +} diff --git a/hapi-fhir-storage-cr/src/main/java/ca/uhn/fhir/cr/config/RepositoryConfigCondition.java b/hapi-fhir-storage-cr/src/main/java/ca/uhn/fhir/cr/config/RepositoryConfigCondition.java new file mode 100644 index 00000000000..caa43d8cd6c --- /dev/null +++ b/hapi-fhir-storage-cr/src/main/java/ca/uhn/fhir/cr/config/RepositoryConfigCondition.java @@ -0,0 +1,47 @@ +/*- + * #%L + * HAPI FHIR - Clinical Reasoning + * %% + * Copyright (C) 2014 - 2023 Smile CDR, Inc. + * %% + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * #L% + */ +package ca.uhn.fhir.cr.config; + +import ca.uhn.fhir.rest.server.RestfulServer; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.config.ConfigurableListableBeanFactory; +import org.springframework.context.annotation.Condition; +import org.springframework.context.annotation.ConditionContext; +import org.springframework.core.type.AnnotatedTypeMetadata; + +public class RepositoryConfigCondition implements Condition { + private static final Logger ourLog = LoggerFactory.getLogger(RepositoryConfigCondition.class); + + @Override + public boolean matches(ConditionContext theConditionContext, AnnotatedTypeMetadata theAnnotatedTypeMetadata) { + ConfigurableListableBeanFactory beanFactory = theConditionContext.getBeanFactory(); + try { + RestfulServer bean = beanFactory.getBean(RestfulServer.class); + if (bean == null) { + return false; + } + } catch (Exception e) { + ourLog.warn("Unable to create bean IRepositoryFactory: Missing RestfulServer"); + return false; + } + return true; + } +} diff --git a/hapi-fhir-storage-cr/src/main/java/ca/uhn/fhir/cr/config/dstu3/ApplyOperationConfig.java b/hapi-fhir-storage-cr/src/main/java/ca/uhn/fhir/cr/config/dstu3/ApplyOperationConfig.java index 0c6240813a8..cbbcd5aa82f 100644 --- a/hapi-fhir-storage-cr/src/main/java/ca/uhn/fhir/cr/config/dstu3/ApplyOperationConfig.java +++ b/hapi-fhir-storage-cr/src/main/java/ca/uhn/fhir/cr/config/dstu3/ApplyOperationConfig.java @@ -21,33 +21,20 @@ package ca.uhn.fhir.cr.config.dstu3; import ca.uhn.fhir.context.FhirContext; import ca.uhn.fhir.context.FhirVersionEnum; -import ca.uhn.fhir.cr.common.IRepositoryFactory; import ca.uhn.fhir.cr.config.ProviderLoader; import ca.uhn.fhir.cr.config.ProviderSelector; import ca.uhn.fhir.rest.server.RestfulServer; -import org.opencds.cqf.fhir.cql.EvaluationSettings; import org.springframework.context.ApplicationContext; import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.context.annotation.Import; import java.util.Arrays; import java.util.Map; +@Configuration +@Import(CrProcessorConfig.class) public class ApplyOperationConfig { - - @Bean - ca.uhn.fhir.cr.dstu3.IActivityDefinitionProcessorFactory dstu3ActivityDefinitionProcessorFactory( - IRepositoryFactory theRepositoryFactory, EvaluationSettings theEvaluationSettings) { - return rd -> new org.opencds.cqf.fhir.cr.activitydefinition.dstu3.ActivityDefinitionProcessor( - theRepositoryFactory.create(rd), theEvaluationSettings); - } - - @Bean - ca.uhn.fhir.cr.dstu3.IPlanDefinitionProcessorFactory dstu3PlanDefinitionProcessorFactory( - IRepositoryFactory theRepositoryFactory, EvaluationSettings theEvaluationSettings) { - return rd -> new org.opencds.cqf.fhir.cr.plandefinition.dstu3.PlanDefinitionProcessor( - theRepositoryFactory.create(rd), theEvaluationSettings); - } - @Bean ca.uhn.fhir.cr.dstu3.activitydefinition.ActivityDefinitionApplyProvider dstu3ActivityDefinitionApplyProvider() { return new ca.uhn.fhir.cr.dstu3.activitydefinition.ActivityDefinitionApplyProvider(); @@ -61,7 +48,6 @@ public class ApplyOperationConfig { @Bean(name = "applyOperationLoader") public ProviderLoader applyOperationLoader( ApplicationContext theApplicationContext, FhirContext theFhirContext, RestfulServer theRestfulServer) { - var selector = new ProviderSelector( theFhirContext, Map.of( diff --git a/hapi-fhir-storage-cr/src/main/java/ca/uhn/fhir/cr/config/dstu3/CrDstu3Config.java b/hapi-fhir-storage-cr/src/main/java/ca/uhn/fhir/cr/config/dstu3/CrDstu3Config.java index 1a19d6c3214..ed8e17306ae 100644 --- a/hapi-fhir-storage-cr/src/main/java/ca/uhn/fhir/cr/config/dstu3/CrDstu3Config.java +++ b/hapi-fhir-storage-cr/src/main/java/ca/uhn/fhir/cr/config/dstu3/CrDstu3Config.java @@ -27,9 +27,9 @@ import ca.uhn.fhir.cr.config.ProviderSelector; import ca.uhn.fhir.cr.config.RepositoryConfig; import ca.uhn.fhir.cr.dstu3.IMeasureServiceFactory; import ca.uhn.fhir.cr.dstu3.measure.MeasureOperationsProvider; -import ca.uhn.fhir.cr.dstu3.measure.MeasureService; import ca.uhn.fhir.rest.server.RestfulServer; import org.opencds.cqf.fhir.cr.measure.MeasureEvaluationOptions; +import org.opencds.cqf.fhir.cr.measure.dstu3.Dstu3MeasureService; import org.springframework.context.ApplicationContext; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; @@ -45,7 +45,7 @@ public class CrDstu3Config { @Bean IMeasureServiceFactory dstu3MeasureServiceFactory( IRepositoryFactory theRepositoryFactory, MeasureEvaluationOptions theEvaluationOptions) { - return rd -> new MeasureService(theRepositoryFactory.create(rd), theEvaluationOptions); + return rd -> new Dstu3MeasureService(theRepositoryFactory.create(rd), theEvaluationOptions); } @Bean diff --git a/hapi-fhir-storage-cr/src/main/java/ca/uhn/fhir/cr/config/dstu3/CrProcessorConfig.java b/hapi-fhir-storage-cr/src/main/java/ca/uhn/fhir/cr/config/dstu3/CrProcessorConfig.java new file mode 100644 index 00000000000..3d87c18b698 --- /dev/null +++ b/hapi-fhir-storage-cr/src/main/java/ca/uhn/fhir/cr/config/dstu3/CrProcessorConfig.java @@ -0,0 +1,56 @@ +/*- + * #%L + * HAPI FHIR - Clinical Reasoning + * %% + * Copyright (C) 2014 - 2023 Smile CDR, Inc. + * %% + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * #L% + */ +package ca.uhn.fhir.cr.config.dstu3; + +import ca.uhn.fhir.cr.common.IRepositoryFactory; +import org.opencds.cqf.fhir.cql.EvaluationSettings; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; + +@Configuration +public class CrProcessorConfig { + @Bean + ca.uhn.fhir.cr.dstu3.IActivityDefinitionProcessorFactory dstu3ActivityDefinitionProcessorFactory( + IRepositoryFactory theRepositoryFactory, EvaluationSettings theEvaluationSettings) { + return rd -> new org.opencds.cqf.fhir.cr.activitydefinition.dstu3.ActivityDefinitionProcessor( + theRepositoryFactory.create(rd), theEvaluationSettings); + } + + @Bean + ca.uhn.fhir.cr.dstu3.IPlanDefinitionProcessorFactory dstu3PlanDefinitionProcessorFactory( + IRepositoryFactory theRepositoryFactory, EvaluationSettings theEvaluationSettings) { + return rd -> new org.opencds.cqf.fhir.cr.plandefinition.dstu3.PlanDefinitionProcessor( + theRepositoryFactory.create(rd), theEvaluationSettings); + } + + @Bean + ca.uhn.fhir.cr.dstu3.IQuestionnaireProcessorFactory dstu3QuestionnaireProcessorFactory( + IRepositoryFactory theRepositoryFactory, EvaluationSettings theEvaluationSettings) { + return rd -> new org.opencds.cqf.fhir.cr.questionnaire.dstu3.processor.QuestionnaireProcessor( + theRepositoryFactory.create(rd), theEvaluationSettings); + } + + @Bean + ca.uhn.fhir.cr.dstu3.IQuestionnaireResponseProcessorFactory dstu3QuestionnaireResponseProcessorFactory( + IRepositoryFactory theRepositoryFactory, EvaluationSettings theEvaluationSettings) { + return rd -> new org.opencds.cqf.fhir.cr.questionnaireresponse.dstu3.QuestionnaireResponseProcessor( + theRepositoryFactory.create(rd), theEvaluationSettings); + } +} diff --git a/hapi-fhir-storage-cr/src/main/java/ca/uhn/fhir/cr/config/dstu3/ExtractOperationConfig.java b/hapi-fhir-storage-cr/src/main/java/ca/uhn/fhir/cr/config/dstu3/ExtractOperationConfig.java index 3b0fb8c9a93..6dd20561530 100644 --- a/hapi-fhir-storage-cr/src/main/java/ca/uhn/fhir/cr/config/dstu3/ExtractOperationConfig.java +++ b/hapi-fhir-storage-cr/src/main/java/ca/uhn/fhir/cr/config/dstu3/ExtractOperationConfig.java @@ -21,25 +21,20 @@ package ca.uhn.fhir.cr.config.dstu3; import ca.uhn.fhir.context.FhirContext; import ca.uhn.fhir.context.FhirVersionEnum; -import ca.uhn.fhir.cr.common.IRepositoryFactory; import ca.uhn.fhir.cr.config.ProviderLoader; import ca.uhn.fhir.cr.config.ProviderSelector; import ca.uhn.fhir.rest.server.RestfulServer; -import org.opencds.cqf.fhir.cql.EvaluationSettings; import org.springframework.context.ApplicationContext; import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.context.annotation.Import; import java.util.Arrays; import java.util.Map; +@Configuration +@Import(CrProcessorConfig.class) public class ExtractOperationConfig { - @Bean - ca.uhn.fhir.cr.dstu3.IQuestionnaireResponseProcessorFactory dstu3QuestionnaireResponseProcessorFactory( - IRepositoryFactory theRepositoryFactory, EvaluationSettings theEvaluationSettings) { - return rd -> new org.opencds.cqf.fhir.cr.questionnaireresponse.dstu3.QuestionnaireResponseProcessor( - theRepositoryFactory.create(rd), theEvaluationSettings); - } - @Bean ca.uhn.fhir.cr.dstu3.questionnaireresponse.QuestionnaireResponseExtractProvider dstu3QuestionnaireResponseExtractProvider() { @@ -49,7 +44,6 @@ public class ExtractOperationConfig { @Bean(name = "extractOperationLoader") public ProviderLoader extractOperationLoader( ApplicationContext theApplicationContext, FhirContext theFhirContext, RestfulServer theRestfulServer) { - var selector = new ProviderSelector( theFhirContext, Map.of( diff --git a/hapi-fhir-storage-cr/src/main/java/ca/uhn/fhir/cr/config/dstu3/PackageOperationConfig.java b/hapi-fhir-storage-cr/src/main/java/ca/uhn/fhir/cr/config/dstu3/PackageOperationConfig.java index 0596e0eb0b7..0c6d37040cd 100644 --- a/hapi-fhir-storage-cr/src/main/java/ca/uhn/fhir/cr/config/dstu3/PackageOperationConfig.java +++ b/hapi-fhir-storage-cr/src/main/java/ca/uhn/fhir/cr/config/dstu3/PackageOperationConfig.java @@ -21,37 +21,25 @@ package ca.uhn.fhir.cr.config.dstu3; import ca.uhn.fhir.context.FhirContext; import ca.uhn.fhir.context.FhirVersionEnum; -import ca.uhn.fhir.cr.common.IRepositoryFactory; import ca.uhn.fhir.cr.config.ProviderLoader; import ca.uhn.fhir.cr.config.ProviderSelector; import ca.uhn.fhir.rest.server.RestfulServer; -import org.opencds.cqf.fhir.cql.EvaluationSettings; import org.springframework.context.ApplicationContext; import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.context.annotation.Import; import java.util.Arrays; import java.util.Map; +@Configuration +@Import(CrProcessorConfig.class) public class PackageOperationConfig { - @Bean - ca.uhn.fhir.cr.dstu3.IPlanDefinitionProcessorFactory dstu3PlanDefinitionProcessorFactory( - IRepositoryFactory theRepositoryFactory, EvaluationSettings theEvaluationSettings) { - return rd -> new org.opencds.cqf.fhir.cr.plandefinition.dstu3.PlanDefinitionProcessor( - theRepositoryFactory.create(rd), theEvaluationSettings); - } - @Bean ca.uhn.fhir.cr.dstu3.plandefinition.PlanDefinitionPackageProvider dstu3PlanDefinitionPackageProvider() { return new ca.uhn.fhir.cr.dstu3.plandefinition.PlanDefinitionPackageProvider(); } - @Bean - ca.uhn.fhir.cr.dstu3.IQuestionnaireProcessorFactory dstu3QuestionnaireProcessorFactory( - IRepositoryFactory theRepositoryFactory, EvaluationSettings theEvaluationSettings) { - return rd -> new org.opencds.cqf.fhir.cr.questionnaire.dstu3.QuestionnaireProcessor( - theRepositoryFactory.create(rd), theEvaluationSettings); - } - @Bean ca.uhn.fhir.cr.dstu3.questionnaire.QuestionnairePackageProvider dstu3QuestionnairePackageProvider() { return new ca.uhn.fhir.cr.dstu3.questionnaire.QuestionnairePackageProvider(); @@ -60,7 +48,6 @@ public class PackageOperationConfig { @Bean(name = "packageOperationLoader") public ProviderLoader packageOperationLoader( ApplicationContext theApplicationContext, FhirContext theFhirContext, RestfulServer theRestfulServer) { - var selector = new ProviderSelector( theFhirContext, Map.of( diff --git a/hapi-fhir-storage-cr/src/main/java/ca/uhn/fhir/cr/config/dstu3/PopulateOperationConfig.java b/hapi-fhir-storage-cr/src/main/java/ca/uhn/fhir/cr/config/dstu3/PopulateOperationConfig.java index a7ce50adae6..8cb3da6c55b 100644 --- a/hapi-fhir-storage-cr/src/main/java/ca/uhn/fhir/cr/config/dstu3/PopulateOperationConfig.java +++ b/hapi-fhir-storage-cr/src/main/java/ca/uhn/fhir/cr/config/dstu3/PopulateOperationConfig.java @@ -21,25 +21,20 @@ package ca.uhn.fhir.cr.config.dstu3; import ca.uhn.fhir.context.FhirContext; import ca.uhn.fhir.context.FhirVersionEnum; -import ca.uhn.fhir.cr.common.IRepositoryFactory; import ca.uhn.fhir.cr.config.ProviderLoader; import ca.uhn.fhir.cr.config.ProviderSelector; import ca.uhn.fhir.rest.server.RestfulServer; -import org.opencds.cqf.fhir.cql.EvaluationSettings; import org.springframework.context.ApplicationContext; import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.context.annotation.Import; import java.util.Arrays; import java.util.Map; +@Configuration +@Import(CrProcessorConfig.class) public class PopulateOperationConfig { - @Bean - ca.uhn.fhir.cr.dstu3.IQuestionnaireProcessorFactory dstu3QuestionnaireProcessorFactory( - IRepositoryFactory theRepositoryFactory, EvaluationSettings theEvaluationSettings) { - return rd -> new org.opencds.cqf.fhir.cr.questionnaire.dstu3.QuestionnaireProcessor( - theRepositoryFactory.create(rd), theEvaluationSettings); - } - @Bean ca.uhn.fhir.cr.dstu3.questionnaire.QuestionnairePopulateProvider dstu3QuestionnairePopulateProvider() { return new ca.uhn.fhir.cr.dstu3.questionnaire.QuestionnairePopulateProvider(); @@ -48,7 +43,6 @@ public class PopulateOperationConfig { @Bean(name = "populateOperationLoader") public ProviderLoader populateOperationLoader( ApplicationContext theApplicationContext, FhirContext theFhirContext, RestfulServer theRestfulServer) { - var selector = new ProviderSelector( theFhirContext, Map.of( diff --git a/hapi-fhir-storage-cr/src/main/java/ca/uhn/fhir/cr/config/r4/ApplyOperationConfig.java b/hapi-fhir-storage-cr/src/main/java/ca/uhn/fhir/cr/config/r4/ApplyOperationConfig.java index cc21be6340e..a4cb102e45b 100644 --- a/hapi-fhir-storage-cr/src/main/java/ca/uhn/fhir/cr/config/r4/ApplyOperationConfig.java +++ b/hapi-fhir-storage-cr/src/main/java/ca/uhn/fhir/cr/config/r4/ApplyOperationConfig.java @@ -21,33 +21,20 @@ package ca.uhn.fhir.cr.config.r4; import ca.uhn.fhir.context.FhirContext; import ca.uhn.fhir.context.FhirVersionEnum; -import ca.uhn.fhir.cr.common.IRepositoryFactory; import ca.uhn.fhir.cr.config.ProviderLoader; import ca.uhn.fhir.cr.config.ProviderSelector; import ca.uhn.fhir.rest.server.RestfulServer; -import org.opencds.cqf.fhir.cql.EvaluationSettings; import org.springframework.context.ApplicationContext; import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.context.annotation.Import; import java.util.Arrays; import java.util.Map; +@Configuration +@Import(CrProcessorConfig.class) public class ApplyOperationConfig { - - @Bean - ca.uhn.fhir.cr.r4.IActivityDefinitionProcessorFactory r4ActivityDefinitionProcessorFactory( - IRepositoryFactory theRepositoryFactory, EvaluationSettings theEvaluationSettings) { - return rd -> new org.opencds.cqf.fhir.cr.activitydefinition.r4.ActivityDefinitionProcessor( - theRepositoryFactory.create(rd), theEvaluationSettings); - } - - @Bean - ca.uhn.fhir.cr.r4.IPlanDefinitionProcessorFactory r4PlanDefinitionProcessorFactory( - IRepositoryFactory theRepositoryFactory, EvaluationSettings theEvaluationSettings) { - return rd -> new org.opencds.cqf.fhir.cr.plandefinition.r4.PlanDefinitionProcessor( - theRepositoryFactory.create(rd), theEvaluationSettings); - } - @Bean ca.uhn.fhir.cr.r4.activitydefinition.ActivityDefinitionApplyProvider r4ActivityDefinitionApplyProvider() { return new ca.uhn.fhir.cr.r4.activitydefinition.ActivityDefinitionApplyProvider(); @@ -61,7 +48,6 @@ public class ApplyOperationConfig { @Bean(name = "applyOperationLoader") public ProviderLoader applyOperationLoader( ApplicationContext theApplicationContext, FhirContext theFhirContext, RestfulServer theRestfulServer) { - var selector = new ProviderSelector( theFhirContext, Map.of( diff --git a/hapi-fhir-storage-cr/src/main/java/ca/uhn/fhir/cr/config/r4/CrProcessorConfig.java b/hapi-fhir-storage-cr/src/main/java/ca/uhn/fhir/cr/config/r4/CrProcessorConfig.java new file mode 100644 index 00000000000..dffd4b2f68b --- /dev/null +++ b/hapi-fhir-storage-cr/src/main/java/ca/uhn/fhir/cr/config/r4/CrProcessorConfig.java @@ -0,0 +1,56 @@ +/*- + * #%L + * HAPI FHIR - Clinical Reasoning + * %% + * Copyright (C) 2014 - 2023 Smile CDR, Inc. + * %% + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * #L% + */ +package ca.uhn.fhir.cr.config.r4; + +import ca.uhn.fhir.cr.common.IRepositoryFactory; +import org.opencds.cqf.fhir.cql.EvaluationSettings; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; + +@Configuration +public class CrProcessorConfig { + @Bean + ca.uhn.fhir.cr.r4.IActivityDefinitionProcessorFactory r4ActivityDefinitionProcessorFactory( + IRepositoryFactory theRepositoryFactory, EvaluationSettings theEvaluationSettings) { + return rd -> new org.opencds.cqf.fhir.cr.activitydefinition.r4.ActivityDefinitionProcessor( + theRepositoryFactory.create(rd), theEvaluationSettings); + } + + @Bean + ca.uhn.fhir.cr.r4.IPlanDefinitionProcessorFactory r4PlanDefinitionProcessorFactory( + IRepositoryFactory theRepositoryFactory, EvaluationSettings theEvaluationSettings) { + return rd -> new org.opencds.cqf.fhir.cr.plandefinition.r4.PlanDefinitionProcessor( + theRepositoryFactory.create(rd), theEvaluationSettings); + } + + @Bean + ca.uhn.fhir.cr.r4.IQuestionnaireProcessorFactory r4QuestionnaireProcessorFactory( + IRepositoryFactory theRepositoryFactory, EvaluationSettings theEvaluationSettings) { + return rd -> new org.opencds.cqf.fhir.cr.questionnaire.r4.processor.QuestionnaireProcessor( + theRepositoryFactory.create(rd), theEvaluationSettings); + } + + @Bean + ca.uhn.fhir.cr.r4.IQuestionnaireResponseProcessorFactory r4QuestionnaireResponseProcessorFactory( + IRepositoryFactory theRepositoryFactory, EvaluationSettings theEvaluationSettings) { + return rd -> new org.opencds.cqf.fhir.cr.questionnaireresponse.r4.QuestionnaireResponseProcessor( + theRepositoryFactory.create(rd), theEvaluationSettings); + } +} diff --git a/hapi-fhir-storage-cr/src/main/java/ca/uhn/fhir/cr/config/r4/CrR4Config.java b/hapi-fhir-storage-cr/src/main/java/ca/uhn/fhir/cr/config/r4/CrR4Config.java index f4a053eb473..d77e3073047 100644 --- a/hapi-fhir-storage-cr/src/main/java/ca/uhn/fhir/cr/config/r4/CrR4Config.java +++ b/hapi-fhir-storage-cr/src/main/java/ca/uhn/fhir/cr/config/r4/CrR4Config.java @@ -32,7 +32,6 @@ import ca.uhn.fhir.cr.r4.ISubmitDataProcessorFactory; import ca.uhn.fhir.cr.r4.cqlexecution.CqlExecutionOperationProvider; import ca.uhn.fhir.cr.r4.measure.CareGapsOperationProvider; import ca.uhn.fhir.cr.r4.measure.MeasureOperationsProvider; -import ca.uhn.fhir.cr.r4.measure.MeasureService; import ca.uhn.fhir.cr.r4.measure.SubmitDataProvider; import ca.uhn.fhir.rest.server.RestfulServer; import org.opencds.cqf.fhir.cql.EvaluationSettings; @@ -40,6 +39,7 @@ import org.opencds.cqf.fhir.cr.cql.r4.R4CqlExecutionService; import org.opencds.cqf.fhir.cr.measure.CareGapsProperties; import org.opencds.cqf.fhir.cr.measure.MeasureEvaluationOptions; import org.opencds.cqf.fhir.cr.measure.r4.R4CareGapsService; +import org.opencds.cqf.fhir.cr.measure.r4.R4MeasureService; import org.opencds.cqf.fhir.cr.measure.r4.R4SubmitDataService; import org.springframework.beans.factory.annotation.Qualifier; import org.springframework.context.ApplicationContext; @@ -58,7 +58,7 @@ public class CrR4Config { @Bean IMeasureServiceFactory r4MeasureServiceFactory( IRepositoryFactory theRepositoryFactory, MeasureEvaluationOptions theEvaluationOptions) { - return rd -> new MeasureService(theRepositoryFactory.create(rd), theEvaluationOptions); + return rd -> new R4MeasureService(theRepositoryFactory.create(rd), theEvaluationOptions); } @Bean diff --git a/hapi-fhir-storage-cr/src/main/java/ca/uhn/fhir/cr/config/r4/ExtractOperationConfig.java b/hapi-fhir-storage-cr/src/main/java/ca/uhn/fhir/cr/config/r4/ExtractOperationConfig.java index a4edeec5d6a..45a031dd910 100644 --- a/hapi-fhir-storage-cr/src/main/java/ca/uhn/fhir/cr/config/r4/ExtractOperationConfig.java +++ b/hapi-fhir-storage-cr/src/main/java/ca/uhn/fhir/cr/config/r4/ExtractOperationConfig.java @@ -21,25 +21,20 @@ package ca.uhn.fhir.cr.config.r4; import ca.uhn.fhir.context.FhirContext; import ca.uhn.fhir.context.FhirVersionEnum; -import ca.uhn.fhir.cr.common.IRepositoryFactory; import ca.uhn.fhir.cr.config.ProviderLoader; import ca.uhn.fhir.cr.config.ProviderSelector; import ca.uhn.fhir.rest.server.RestfulServer; -import org.opencds.cqf.fhir.cql.EvaluationSettings; import org.springframework.context.ApplicationContext; import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.context.annotation.Import; import java.util.Arrays; import java.util.Map; +@Configuration +@Import(CrProcessorConfig.class) public class ExtractOperationConfig { - @Bean - ca.uhn.fhir.cr.r4.IQuestionnaireResponseProcessorFactory r4QuestionnaireResponseProcessorFactory( - IRepositoryFactory theRepositoryFactory, EvaluationSettings theEvaluationSettings) { - return rd -> new org.opencds.cqf.fhir.cr.questionnaireresponse.r4.QuestionnaireResponseProcessor( - theRepositoryFactory.create(rd), theEvaluationSettings); - } - @Bean ca.uhn.fhir.cr.r4.questionnaireresponse.QuestionnaireResponseExtractProvider r4QuestionnaireResponseExtractProvider() { @@ -49,7 +44,6 @@ public class ExtractOperationConfig { @Bean(name = "extractOperationLoader") public ProviderLoader extractOperationLoader( ApplicationContext theApplicationContext, FhirContext theFhirContext, RestfulServer theRestfulServer) { - var selector = new ProviderSelector( theFhirContext, Map.of( diff --git a/hapi-fhir-storage-cr/src/main/java/ca/uhn/fhir/cr/config/r4/PackageOperationConfig.java b/hapi-fhir-storage-cr/src/main/java/ca/uhn/fhir/cr/config/r4/PackageOperationConfig.java index 640fdfd0239..ffbd0c29dfc 100644 --- a/hapi-fhir-storage-cr/src/main/java/ca/uhn/fhir/cr/config/r4/PackageOperationConfig.java +++ b/hapi-fhir-storage-cr/src/main/java/ca/uhn/fhir/cr/config/r4/PackageOperationConfig.java @@ -21,37 +21,25 @@ package ca.uhn.fhir.cr.config.r4; import ca.uhn.fhir.context.FhirContext; import ca.uhn.fhir.context.FhirVersionEnum; -import ca.uhn.fhir.cr.common.IRepositoryFactory; import ca.uhn.fhir.cr.config.ProviderLoader; import ca.uhn.fhir.cr.config.ProviderSelector; import ca.uhn.fhir.rest.server.RestfulServer; -import org.opencds.cqf.fhir.cql.EvaluationSettings; import org.springframework.context.ApplicationContext; import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.context.annotation.Import; import java.util.Arrays; import java.util.Map; +@Configuration +@Import(CrProcessorConfig.class) public class PackageOperationConfig { - @Bean - ca.uhn.fhir.cr.r4.IPlanDefinitionProcessorFactory r4PlanDefinitionProcessorFactory( - IRepositoryFactory theRepositoryFactory, EvaluationSettings theEvaluationSettings) { - return rd -> new org.opencds.cqf.fhir.cr.plandefinition.r4.PlanDefinitionProcessor( - theRepositoryFactory.create(rd), theEvaluationSettings); - } - @Bean ca.uhn.fhir.cr.r4.plandefinition.PlanDefinitionPackageProvider r4PlanDefinitionPackageProvider() { return new ca.uhn.fhir.cr.r4.plandefinition.PlanDefinitionPackageProvider(); } - @Bean - ca.uhn.fhir.cr.r4.IQuestionnaireProcessorFactory r4QuestionnaireProcessorFactory( - IRepositoryFactory theRepositoryFactory, EvaluationSettings theEvaluationSettings) { - return rd -> new org.opencds.cqf.fhir.cr.questionnaire.r4.QuestionnaireProcessor( - theRepositoryFactory.create(rd), theEvaluationSettings); - } - @Bean ca.uhn.fhir.cr.r4.questionnaire.QuestionnairePackageProvider r4QuestionnairePackageProvider() { return new ca.uhn.fhir.cr.r4.questionnaire.QuestionnairePackageProvider(); @@ -60,7 +48,6 @@ public class PackageOperationConfig { @Bean(name = "packageOperationLoader") public ProviderLoader packageOperationLoader( ApplicationContext theApplicationContext, FhirContext theFhirContext, RestfulServer theRestfulServer) { - var selector = new ProviderSelector( theFhirContext, Map.of( diff --git a/hapi-fhir-storage-cr/src/main/java/ca/uhn/fhir/cr/config/r4/PopulateOperationConfig.java b/hapi-fhir-storage-cr/src/main/java/ca/uhn/fhir/cr/config/r4/PopulateOperationConfig.java index 9d604475435..87cdc729be3 100644 --- a/hapi-fhir-storage-cr/src/main/java/ca/uhn/fhir/cr/config/r4/PopulateOperationConfig.java +++ b/hapi-fhir-storage-cr/src/main/java/ca/uhn/fhir/cr/config/r4/PopulateOperationConfig.java @@ -21,25 +21,20 @@ package ca.uhn.fhir.cr.config.r4; import ca.uhn.fhir.context.FhirContext; import ca.uhn.fhir.context.FhirVersionEnum; -import ca.uhn.fhir.cr.common.IRepositoryFactory; import ca.uhn.fhir.cr.config.ProviderLoader; import ca.uhn.fhir.cr.config.ProviderSelector; import ca.uhn.fhir.rest.server.RestfulServer; -import org.opencds.cqf.fhir.cql.EvaluationSettings; import org.springframework.context.ApplicationContext; import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.context.annotation.Import; import java.util.Arrays; import java.util.Map; +@Configuration +@Import(CrProcessorConfig.class) public class PopulateOperationConfig { - @Bean - ca.uhn.fhir.cr.r4.IQuestionnaireProcessorFactory r4QuestionnaireProcessorFactory( - IRepositoryFactory theRepositoryFactory, EvaluationSettings theEvaluationSettings) { - return rd -> new org.opencds.cqf.fhir.cr.questionnaire.r4.QuestionnaireProcessor( - theRepositoryFactory.create(rd), theEvaluationSettings); - } - @Bean ca.uhn.fhir.cr.r4.questionnaire.QuestionnairePopulateProvider r4QuestionnairePopulateProvider() { return new ca.uhn.fhir.cr.r4.questionnaire.QuestionnairePopulateProvider(); @@ -48,7 +43,6 @@ public class PopulateOperationConfig { @Bean(name = "populateOperationLoader") public ProviderLoader populateOperationLoader( ApplicationContext theApplicationContext, FhirContext theFhirContext, RestfulServer theRestfulServer) { - var selector = new ProviderSelector( theFhirContext, Map.of( diff --git a/hapi-fhir-storage-cr/src/main/java/ca/uhn/fhir/cr/dstu3/IMeasureServiceFactory.java b/hapi-fhir-storage-cr/src/main/java/ca/uhn/fhir/cr/dstu3/IMeasureServiceFactory.java index 0f8951bce54..cbf6696adf2 100644 --- a/hapi-fhir-storage-cr/src/main/java/ca/uhn/fhir/cr/dstu3/IMeasureServiceFactory.java +++ b/hapi-fhir-storage-cr/src/main/java/ca/uhn/fhir/cr/dstu3/IMeasureServiceFactory.java @@ -19,10 +19,10 @@ */ package ca.uhn.fhir.cr.dstu3; -import ca.uhn.fhir.cr.dstu3.measure.MeasureService; import ca.uhn.fhir.rest.api.server.RequestDetails; +import org.opencds.cqf.fhir.cr.measure.dstu3.Dstu3MeasureService; @FunctionalInterface public interface IMeasureServiceFactory { - MeasureService create(RequestDetails theRequestDetails); + Dstu3MeasureService create(RequestDetails theRequestDetails); } diff --git a/hapi-fhir-storage-cr/src/main/java/ca/uhn/fhir/cr/dstu3/IQuestionnaireProcessorFactory.java b/hapi-fhir-storage-cr/src/main/java/ca/uhn/fhir/cr/dstu3/IQuestionnaireProcessorFactory.java index 3cc35c83356..3ee321958ca 100644 --- a/hapi-fhir-storage-cr/src/main/java/ca/uhn/fhir/cr/dstu3/IQuestionnaireProcessorFactory.java +++ b/hapi-fhir-storage-cr/src/main/java/ca/uhn/fhir/cr/dstu3/IQuestionnaireProcessorFactory.java @@ -20,7 +20,7 @@ package ca.uhn.fhir.cr.dstu3; import ca.uhn.fhir.rest.api.server.RequestDetails; -import org.opencds.cqf.fhir.cr.questionnaire.dstu3.QuestionnaireProcessor; +import org.opencds.cqf.fhir.cr.questionnaire.dstu3.processor.QuestionnaireProcessor; @FunctionalInterface public interface IQuestionnaireProcessorFactory { diff --git a/hapi-fhir-storage-cr/src/main/java/ca/uhn/fhir/cr/dstu3/measure/MeasureOperationsProvider.java b/hapi-fhir-storage-cr/src/main/java/ca/uhn/fhir/cr/dstu3/measure/MeasureOperationsProvider.java index e6ca48d241a..5130d6030ea 100644 --- a/hapi-fhir-storage-cr/src/main/java/ca/uhn/fhir/cr/dstu3/measure/MeasureOperationsProvider.java +++ b/hapi-fhir-storage-cr/src/main/java/ca/uhn/fhir/cr/dstu3/measure/MeasureOperationsProvider.java @@ -31,6 +31,7 @@ import org.hl7.fhir.dstu3.model.Endpoint; import org.hl7.fhir.dstu3.model.IdType; import org.hl7.fhir.dstu3.model.Measure; import org.hl7.fhir.dstu3.model.MeasureReport; +import org.hl7.fhir.dstu3.model.Parameters; import org.hl7.fhir.exceptions.FHIRException; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Component; @@ -76,6 +77,7 @@ public class MeasureOperationsProvider { @OperationParam(name = "productLine") String theProductLine, @OperationParam(name = "additionalData") Bundle theAdditionalData, @OperationParam(name = "terminologyEndpoint") Endpoint theTerminologyEndpoint, + @OperationParam(name = "parameters") Parameters theParameters, RequestDetails theRequestDetails) throws InternalErrorException, FHIRException { return myDstu3MeasureProcessorFactory @@ -90,6 +92,7 @@ public class MeasureOperationsProvider { theLastReceivedOn, theProductLine, theAdditionalData, + theParameters, theTerminologyEndpoint); } } diff --git a/hapi-fhir-storage-cr/src/main/java/ca/uhn/fhir/cr/dstu3/measure/MeasureService.java b/hapi-fhir-storage-cr/src/main/java/ca/uhn/fhir/cr/dstu3/measure/MeasureService.java deleted file mode 100644 index 24876ddc9f0..00000000000 --- a/hapi-fhir-storage-cr/src/main/java/ca/uhn/fhir/cr/dstu3/measure/MeasureService.java +++ /dev/null @@ -1,158 +0,0 @@ -/*- - * #%L - * HAPI FHIR - Clinical Reasoning - * %% - * Copyright (C) 2014 - 2023 Smile CDR, Inc. - * %% - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * #L% - */ -package ca.uhn.fhir.cr.dstu3.measure; - -import ca.uhn.fhir.util.BundleBuilder; -import org.hl7.fhir.dstu3.model.Bundle; -import org.hl7.fhir.dstu3.model.CodeableConcept; -import org.hl7.fhir.dstu3.model.Coding; -import org.hl7.fhir.dstu3.model.ContactDetail; -import org.hl7.fhir.dstu3.model.ContactPoint; -import org.hl7.fhir.dstu3.model.Endpoint; -import org.hl7.fhir.dstu3.model.Enumerations; -import org.hl7.fhir.dstu3.model.Extension; -import org.hl7.fhir.dstu3.model.IdType; -import org.hl7.fhir.dstu3.model.MeasureReport; -import org.hl7.fhir.dstu3.model.SearchParameter; -import org.hl7.fhir.dstu3.model.StringType; -import org.opencds.cqf.fhir.api.Repository; -import org.opencds.cqf.fhir.cr.measure.MeasureEvaluationOptions; -import org.opencds.cqf.fhir.cr.measure.dstu3.Dstu3MeasureProcessor; - -import java.util.Collections; -import java.util.List; - -import static org.opencds.cqf.fhir.cr.measure.constant.MeasureReportConstants.COUNTRY_CODING_SYSTEM_CODE; -import static org.opencds.cqf.fhir.cr.measure.constant.MeasureReportConstants.MEASUREREPORT_MEASURE_SUPPLEMENTALDATA_EXTENSION; -import static org.opencds.cqf.fhir.cr.measure.constant.MeasureReportConstants.MEASUREREPORT_SUPPLEMENTALDATA_SEARCHPARAMETER_DEFINITION_DATE; -import static org.opencds.cqf.fhir.cr.measure.constant.MeasureReportConstants.MEASUREREPORT_SUPPLEMENTALDATA_SEARCHPARAMETER_URL; -import static org.opencds.cqf.fhir.cr.measure.constant.MeasureReportConstants.MEASUREREPORT_SUPPLEMENTALDATA_SEARCHPARAMETER_VERSION; -import static org.opencds.cqf.fhir.cr.measure.constant.MeasureReportConstants.US_COUNTRY_CODE; -import static org.opencds.cqf.fhir.cr.measure.constant.MeasureReportConstants.US_COUNTRY_DISPLAY; - -public class MeasureService { - private final Repository myRepository; - private final MeasureEvaluationOptions myMeasureEvaluationOptions; - - public MeasureService(Repository theRepository, MeasureEvaluationOptions theMeasureEvaluationOptions) { - myRepository = theRepository; - myMeasureEvaluationOptions = theMeasureEvaluationOptions; - } - - public static final List CQI_CONTACT_DETAIL = Collections.singletonList(new ContactDetail() - .addTelecom(new ContactPoint() - .setSystem(ContactPoint.ContactPointSystem.URL) - .setValue("http://www.hl7.org/Special/committees/cqi/index.cfm"))); - - public static final List US_JURISDICTION_CODING = Collections.singletonList(new CodeableConcept() - .addCoding(new Coding(COUNTRY_CODING_SYSTEM_CODE, US_COUNTRY_CODE, US_COUNTRY_DISPLAY))); - - public static final SearchParameter SUPPLEMENTAL_DATA_SEARCHPARAMETER = (SearchParameter) new SearchParameter() - .setUrl(MEASUREREPORT_SUPPLEMENTALDATA_SEARCHPARAMETER_URL) - .setVersion(MEASUREREPORT_SUPPLEMENTALDATA_SEARCHPARAMETER_VERSION) - .setName("DEQMMeasureReportSupplementalData") - .setStatus(Enumerations.PublicationStatus.ACTIVE) - .setDate(MEASUREREPORT_SUPPLEMENTALDATA_SEARCHPARAMETER_DEFINITION_DATE) - .setPublisher("HL7 International - Clinical Quality Information Work Group") - .setContact(CQI_CONTACT_DETAIL) - .setDescription(String.format( - "Returns resources (supplemental data) from references on extensions on the MeasureReport with urls matching %s.", - MEASUREREPORT_MEASURE_SUPPLEMENTALDATA_EXTENSION)) - .setJurisdiction(US_JURISDICTION_CODING) - .addBase("MeasureReport") - .setCode("supplemental-data") - .setType(Enumerations.SearchParamType.REFERENCE) - .setExpression(String.format( - "MeasureReport.extension('%s').value", MEASUREREPORT_MEASURE_SUPPLEMENTALDATA_EXTENSION)) - .setXpath(String.format( - "f:MeasureReport/f:extension[@url='%s'].value", MEASUREREPORT_MEASURE_SUPPLEMENTALDATA_EXTENSION)) - .setXpathUsage(SearchParameter.XPathUsageType.NORMAL) - .setTitle("Supplemental Data") - .setId("deqm-measurereport-supplemental-data"); - - /** - * Get The details (such as tenant) of this request. Usually auto-populated HAPI. - * - */ - - /** - * Implements the $evaluate-measure - * operation found in the - * FHIR Clinical - * Reasoning Module. This implementation aims to be compatible with the CQF - * IG. - * - * @param theId the Id of the Measure to evaluate - * @param thePeriodStart The start of the reporting period - * @param thePeriodEnd The end of the reporting period - * @param theReportType The type of MeasureReport to generate - * @param thePractitioner the practitioner to use for the evaluation - * @param theLastReceivedOn the date the results of this measure were last - * received. - * @param theProductLine the productLine (e.g. Medicare, Medicaid, etc) to use - * for the evaluation. This is a non-standard parameter. - * @param theAdditionalData the data bundle containing additional data - * @param theTerminologyEndpoint the endpoint of terminology services for your measure valuesets - * @return the calculated MeasureReport - */ - public MeasureReport evaluateMeasure( - IdType theId, - String thePeriodStart, - String thePeriodEnd, - String theReportType, - String theSubject, - String thePractitioner, - String theLastReceivedOn, - String theProductLine, - Bundle theAdditionalData, - Endpoint theTerminologyEndpoint) { - - ensureSupplementalDataElementSearchParameter(); - - var dstu3MeasureProcessor = new Dstu3MeasureProcessor(myRepository, myMeasureEvaluationOptions); - - MeasureReport report = dstu3MeasureProcessor.evaluateMeasure( - theId, - thePeriodStart, - thePeriodEnd, - theReportType, - Collections.singletonList(theSubject), - theAdditionalData); - - if (theProductLine != null) { - Extension ext = new Extension(); - ext.setUrl("http://hl7.org/fhir/us/cqframework/cqfmeasures/StructureDefinition/cqfm-productLine"); - ext.setValue(new StringType(theProductLine)); - report.addExtension(ext); - } - - return report; - } - - protected void ensureSupplementalDataElementSearchParameter() { - // create a transaction bundle - BundleBuilder builder = new BundleBuilder(myRepository.fhirContext()); - - // set the request to be condition on code == supplemental data - builder.addTransactionCreateEntry(SUPPLEMENTAL_DATA_SEARCHPARAMETER).conditional("code=supplemental-data"); - myRepository.transaction(builder.getBundle()); - } -} diff --git a/hapi-fhir-storage-cr/src/main/java/ca/uhn/fhir/cr/r4/IMeasureServiceFactory.java b/hapi-fhir-storage-cr/src/main/java/ca/uhn/fhir/cr/r4/IMeasureServiceFactory.java index 374f82e26a1..e99ce0547ed 100644 --- a/hapi-fhir-storage-cr/src/main/java/ca/uhn/fhir/cr/r4/IMeasureServiceFactory.java +++ b/hapi-fhir-storage-cr/src/main/java/ca/uhn/fhir/cr/r4/IMeasureServiceFactory.java @@ -19,10 +19,10 @@ */ package ca.uhn.fhir.cr.r4; -import ca.uhn.fhir.cr.r4.measure.MeasureService; import ca.uhn.fhir.rest.api.server.RequestDetails; +import org.opencds.cqf.fhir.cr.measure.r4.R4MeasureService; @FunctionalInterface public interface IMeasureServiceFactory { - MeasureService create(RequestDetails theRequestDetails); + R4MeasureService create(RequestDetails theRequestDetails); } diff --git a/hapi-fhir-storage-cr/src/main/java/ca/uhn/fhir/cr/r4/IQuestionnaireProcessorFactory.java b/hapi-fhir-storage-cr/src/main/java/ca/uhn/fhir/cr/r4/IQuestionnaireProcessorFactory.java index 6105ea61517..82d77f2b956 100644 --- a/hapi-fhir-storage-cr/src/main/java/ca/uhn/fhir/cr/r4/IQuestionnaireProcessorFactory.java +++ b/hapi-fhir-storage-cr/src/main/java/ca/uhn/fhir/cr/r4/IQuestionnaireProcessorFactory.java @@ -20,7 +20,7 @@ package ca.uhn.fhir.cr.r4; import ca.uhn.fhir.rest.api.server.RequestDetails; -import org.opencds.cqf.fhir.cr.questionnaire.r4.QuestionnaireProcessor; +import org.opencds.cqf.fhir.cr.questionnaire.r4.processor.QuestionnaireProcessor; @FunctionalInterface public interface IQuestionnaireProcessorFactory { diff --git a/hapi-fhir-storage-cr/src/main/java/ca/uhn/fhir/cr/r4/measure/MeasureOperationsProvider.java b/hapi-fhir-storage-cr/src/main/java/ca/uhn/fhir/cr/r4/measure/MeasureOperationsProvider.java index 772c0e491bd..0a533224e3e 100644 --- a/hapi-fhir-storage-cr/src/main/java/ca/uhn/fhir/cr/r4/measure/MeasureOperationsProvider.java +++ b/hapi-fhir-storage-cr/src/main/java/ca/uhn/fhir/cr/r4/measure/MeasureOperationsProvider.java @@ -32,6 +32,8 @@ import org.hl7.fhir.r4.model.Endpoint; import org.hl7.fhir.r4.model.IdType; import org.hl7.fhir.r4.model.Measure; import org.hl7.fhir.r4.model.MeasureReport; +import org.hl7.fhir.r4.model.Parameters; +import org.opencds.cqf.fhir.utility.monad.Eithers; import org.springframework.beans.factory.annotation.Autowired; public class MeasureOperationsProvider { @@ -73,20 +75,24 @@ public class MeasureOperationsProvider { @OperationParam(name = "productLine") String theProductLine, @OperationParam(name = "additionalData") Bundle theAdditionalData, @OperationParam(name = "terminologyEndpoint") Endpoint theTerminologyEndpoint, + @OperationParam(name = "parameters") Parameters theParameters, RequestDetails theRequestDetails) throws InternalErrorException, FHIRException { return myR4MeasureServiceFactory .create(theRequestDetails) - .evaluateMeasure( - theId, + .evaluate( + Eithers.forMiddle3(theId), thePeriodStart, thePeriodEnd, theReportType, theSubject, - thePractitioner, theLastReceivedOn, - theProductLine, + null, + theTerminologyEndpoint, + null, theAdditionalData, - theTerminologyEndpoint); + theParameters, + theProductLine, + thePractitioner); } } diff --git a/hapi-fhir-storage-cr/src/main/java/ca/uhn/fhir/cr/r4/measure/MeasureService.java b/hapi-fhir-storage-cr/src/main/java/ca/uhn/fhir/cr/r4/measure/MeasureService.java deleted file mode 100644 index 05eae83cccd..00000000000 --- a/hapi-fhir-storage-cr/src/main/java/ca/uhn/fhir/cr/r4/measure/MeasureService.java +++ /dev/null @@ -1,216 +0,0 @@ -/*- - * #%L - * HAPI FHIR - Clinical Reasoning - * %% - * Copyright (C) 2014 - 2023 Smile CDR, Inc. - * %% - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * #L% - */ -package ca.uhn.fhir.cr.r4.measure; - -import ca.uhn.fhir.model.api.IQueryParameterType; -import ca.uhn.fhir.rest.param.ReferenceParam; -import ca.uhn.fhir.util.BundleBuilder; -import org.apache.commons.lang3.StringUtils; -import org.hl7.fhir.r4.model.Bundle; -import org.hl7.fhir.r4.model.CodeableConcept; -import org.hl7.fhir.r4.model.Coding; -import org.hl7.fhir.r4.model.ContactDetail; -import org.hl7.fhir.r4.model.ContactPoint; -import org.hl7.fhir.r4.model.Endpoint; -import org.hl7.fhir.r4.model.Enumerations; -import org.hl7.fhir.r4.model.Extension; -import org.hl7.fhir.r4.model.IdType; -import org.hl7.fhir.r4.model.MeasureReport; -import org.hl7.fhir.r4.model.Patient; -import org.hl7.fhir.r4.model.SearchParameter; -import org.hl7.fhir.r4.model.StringType; -import org.opencds.cqf.fhir.api.Repository; -import org.opencds.cqf.fhir.cr.measure.MeasureEvaluationOptions; -import org.opencds.cqf.fhir.cr.measure.r4.R4MeasureProcessor; -import org.opencds.cqf.fhir.cr.measure.r4.R4RepositorySubjectProvider; -import org.opencds.cqf.fhir.utility.iterable.BundleIterator; -import org.opencds.cqf.fhir.utility.monad.Eithers; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -import java.util.ArrayList; -import java.util.Collections; -import java.util.HashMap; -import java.util.List; -import java.util.Map; - -import static org.opencds.cqf.fhir.cr.measure.constant.MeasureReportConstants.COUNTRY_CODING_SYSTEM_CODE; -import static org.opencds.cqf.fhir.cr.measure.constant.MeasureReportConstants.MEASUREREPORT_MEASURE_SUPPLEMENTALDATA_EXTENSION; -import static org.opencds.cqf.fhir.cr.measure.constant.MeasureReportConstants.MEASUREREPORT_PRODUCT_LINE_EXT_URL; -import static org.opencds.cqf.fhir.cr.measure.constant.MeasureReportConstants.MEASUREREPORT_SUPPLEMENTALDATA_SEARCHPARAMETER_DEFINITION_DATE; -import static org.opencds.cqf.fhir.cr.measure.constant.MeasureReportConstants.MEASUREREPORT_SUPPLEMENTALDATA_SEARCHPARAMETER_URL; -import static org.opencds.cqf.fhir.cr.measure.constant.MeasureReportConstants.MEASUREREPORT_SUPPLEMENTALDATA_SEARCHPARAMETER_VERSION; -import static org.opencds.cqf.fhir.cr.measure.constant.MeasureReportConstants.US_COUNTRY_CODE; -import static org.opencds.cqf.fhir.cr.measure.constant.MeasureReportConstants.US_COUNTRY_DISPLAY; - -public class MeasureService { - - private final Repository myRepository; - private final MeasureEvaluationOptions myMeasureEvaluationOptions; - private Logger ourLogger = LoggerFactory.getLogger(MeasureService.class); - - public MeasureService(Repository theRepository, MeasureEvaluationOptions theMeasureEvaluationOptions) { - this.myRepository = theRepository; - this.myMeasureEvaluationOptions = theMeasureEvaluationOptions; - } - - public static final List CQI_CONTACTDETAIL = Collections.singletonList(new ContactDetail() - .addTelecom(new ContactPoint() - .setSystem(ContactPoint.ContactPointSystem.URL) - .setValue("http://www.hl7.org/Special/committees/cqi/index.cfm"))); - - public static final List US_JURISDICTION_CODING = Collections.singletonList(new CodeableConcept() - .addCoding(new Coding(COUNTRY_CODING_SYSTEM_CODE, US_COUNTRY_CODE, US_COUNTRY_DISPLAY))); - - public static final SearchParameter SUPPLEMENTAL_DATA_SEARCHPARAMETER = (SearchParameter) new SearchParameter() - .setUrl(MEASUREREPORT_SUPPLEMENTALDATA_SEARCHPARAMETER_URL) - .setVersion(MEASUREREPORT_SUPPLEMENTALDATA_SEARCHPARAMETER_VERSION) - .setName("DEQMMeasureReportSupplementalData") - .setStatus(Enumerations.PublicationStatus.ACTIVE) - .setDate(MEASUREREPORT_SUPPLEMENTALDATA_SEARCHPARAMETER_DEFINITION_DATE) - .setPublisher("HL7 International - Clinical Quality Information Work Group") - .setContact(CQI_CONTACTDETAIL) - .setDescription(String.format( - "Returns resources (supplemental data) from references on extensions on the MeasureReport with urls matching %s.", - MEASUREREPORT_MEASURE_SUPPLEMENTALDATA_EXTENSION)) - .setJurisdiction(US_JURISDICTION_CODING) - .addBase("MeasureReport") - .setCode("supplemental-data") - .setType(Enumerations.SearchParamType.REFERENCE) - .setExpression(String.format( - "MeasureReport.extension('%s').value", MEASUREREPORT_MEASURE_SUPPLEMENTALDATA_EXTENSION)) - .setXpath(String.format( - "f:MeasureReport/f:extension[@url='%s'].value", MEASUREREPORT_MEASURE_SUPPLEMENTALDATA_EXTENSION)) - .setXpathUsage(SearchParameter.XPathUsageType.NORMAL) - .setTitle("Supplemental Data") - .setId("deqm-measurereport-supplemental-data"); - - /** - * Implements the $evaluate-measure - * operation found in the - * FHIR Clinical - * Reasoning Module. This implementation aims to be compatible with the CQF - * IG. - * - * @param theId the Id of the Measure to evaluate - * @param thePeriodStart The start of the reporting period - * @param thePeriodEnd The end of the reporting period - * @param theReportType The type of MeasureReport to generate - * @param thePractitioner the thePractitioner to use for the evaluation - * @param theLastReceivedOn the date the results of this measure were last - * received. - * @param theProductLine the theProductLine (e.g. Medicare, Medicaid, etc) to use - * for the evaluation. This is a non-standard parameter. - * @param theAdditionalData the data bundle containing additional data - * @param theTerminologyEndpoint the endpoint of terminology services for your measure valuesets - * @return the calculated MeasureReport - */ - public MeasureReport evaluateMeasure( - IdType theId, - String thePeriodStart, - String thePeriodEnd, - String theReportType, - String theSubject, - String thePractitioner, - String theLastReceivedOn, - String theProductLine, - Bundle theAdditionalData, - Endpoint theTerminologyEndpoint) { - - ensureSupplementalDataElementSearchParameter(); - - var r4MeasureProcessor = - new R4MeasureProcessor(myRepository, myMeasureEvaluationOptions, new R4RepositorySubjectProvider()); - - MeasureReport measureReport = null; - - // SUBJECT LIST SETTERS - if (StringUtils.isBlank(theSubject) && StringUtils.isNotBlank(thePractitioner)) { - List subjectIds = getPractitionerPatients(thePractitioner, myRepository); - - measureReport = r4MeasureProcessor.evaluateMeasure( - Eithers.forMiddle3(theId), - thePeriodStart, - thePeriodEnd, - theReportType, - subjectIds, - theAdditionalData); - - } else if (StringUtils.isNotBlank(theSubject)) { - measureReport = r4MeasureProcessor.evaluateMeasure( - Eithers.forMiddle3(theId), - thePeriodStart, - thePeriodEnd, - theReportType, - Collections.singletonList(theSubject), - theAdditionalData); - - } else if (StringUtils.isBlank(theSubject) && StringUtils.isBlank(thePractitioner)) { - measureReport = r4MeasureProcessor.evaluateMeasure( - Eithers.forMiddle3(theId), thePeriodStart, thePeriodEnd, theReportType, null, theAdditionalData); - } - // add ProductLine after report is generated - addProductLineExtension(measureReport, theProductLine); - - return measureReport; - } - - private List getPractitionerPatients(String thePractitioner, Repository theRepository) { - List patients = new ArrayList<>(); - - Map> map = new HashMap<>(); - map.put( - "general-practitioner", - Collections.singletonList(new ReferenceParam( - thePractitioner.startsWith("Practitioner/") - ? thePractitioner - : "Practitioner/" + thePractitioner))); - - var bundle = theRepository.search(Bundle.class, Patient.class, map); - var iterator = new BundleIterator<>(theRepository, bundle); - - while (iterator.hasNext()) { - var patient = iterator.next().getResource(); - var refString = patient.getIdElement().getResourceType() + "/" - + patient.getIdElement().getIdPart(); - patients.add(refString); - } - return patients; - } - - private void addProductLineExtension(MeasureReport theMeasureReport, String theProductLine) { - if (theProductLine != null) { - Extension ext = new Extension(); - ext.setUrl(MEASUREREPORT_PRODUCT_LINE_EXT_URL); - ext.setValue(new StringType(theProductLine)); - theMeasureReport.addExtension(ext); - } - } - - protected void ensureSupplementalDataElementSearchParameter() { - // create a transaction bundle - BundleBuilder builder = new BundleBuilder(myRepository.fhirContext()); - - // set the request to be condition on code == supplemental data - builder.addTransactionCreateEntry(SUPPLEMENTAL_DATA_SEARCHPARAMETER).conditional("code=supplemental-data"); - myRepository.transaction(builder.getBundle()); - } -} diff --git a/hapi-fhir-storage-cr/src/main/java/ca/uhn/fhir/cr/repo/RequestDetailsCloner.java b/hapi-fhir-storage-cr/src/main/java/ca/uhn/fhir/cr/repo/RequestDetailsCloner.java index 6d6e68049d1..31a4a147a0e 100644 --- a/hapi-fhir-storage-cr/src/main/java/ca/uhn/fhir/cr/repo/RequestDetailsCloner.java +++ b/hapi-fhir-storage-cr/src/main/java/ca/uhn/fhir/cr/repo/RequestDetailsCloner.java @@ -19,6 +19,7 @@ */ package ca.uhn.fhir.cr.repo; +import ca.uhn.fhir.parser.IParser; import ca.uhn.fhir.rest.api.RequestTypeEnum; import ca.uhn.fhir.rest.api.server.RequestDetails; import ca.uhn.fhir.rest.api.server.SystemRequestDetails; @@ -43,6 +44,7 @@ class RequestDetailsCloner { newDetails.setParameters(new HashMap<>()); newDetails.setResourceName(null); newDetails.setCompartmentName(null); + newDetails.setResponse(theDetails.getResponse()); return new DetailsBuilder(newDetails); } @@ -65,7 +67,9 @@ class RequestDetailsCloner { } DetailsBuilder setParameters(IBaseParameters theParameters) { - myDetails.setResource(theParameters); + IParser parser = myDetails.getServer().getFhirContext().newJsonParser(); + myDetails.setRequestContents( + parser.encodeResourceToString(theParameters).getBytes()); return this; } diff --git a/hapi-fhir-storage-cr/src/main/java/ca/uhn/fhir/cr/repo/SearchConverter.java b/hapi-fhir-storage-cr/src/main/java/ca/uhn/fhir/cr/repo/SearchConverter.java index 991fc9e4522..6a36164642c 100644 --- a/hapi-fhir-storage-cr/src/main/java/ca/uhn/fhir/cr/repo/SearchConverter.java +++ b/hapi-fhir-storage-cr/src/main/java/ca/uhn/fhir/cr/repo/SearchConverter.java @@ -27,12 +27,12 @@ import ca.uhn.fhir.model.api.IQueryParameterOr; import ca.uhn.fhir.model.api.IQueryParameterType; import ca.uhn.fhir.rest.param.TokenOrListParam; import ca.uhn.fhir.rest.param.TokenParam; +import jakarta.annotation.Nonnull; import java.util.Arrays; import java.util.HashMap; import java.util.List; import java.util.Map; -import javax.annotation.Nonnull; /** * The IGenericClient API represents searches with OrLists, while the FhirRepository API uses nested diff --git a/hapi-fhir-storage-cr/src/test/java/ca/uhn/fhir/cr/TestCrConfig.java b/hapi-fhir-storage-cr/src/test/java/ca/uhn/fhir/cr/TestCrConfig.java index 3814bba357f..4f42ae49d52 100644 --- a/hapi-fhir-storage-cr/src/test/java/ca/uhn/fhir/cr/TestCrConfig.java +++ b/hapi-fhir-storage-cr/src/test/java/ca/uhn/fhir/cr/TestCrConfig.java @@ -1,14 +1,19 @@ package ca.uhn.fhir.cr; import ca.uhn.fhir.batch2.jobs.reindex.ReindexProvider; +import ca.uhn.fhir.context.FhirContext; import ca.uhn.fhir.context.support.IValidationSupport; - import ca.uhn.fhir.cr.common.CodeCacheResourceChangeListener; import ca.uhn.fhir.cr.common.ElmCacheResourceChangeListener; import ca.uhn.fhir.jpa.api.config.JpaStorageSettings; import ca.uhn.fhir.jpa.api.dao.DaoRegistry; import ca.uhn.fhir.jpa.api.dao.IFhirSystemDao; +import ca.uhn.fhir.jpa.cache.IResourceChangeListenerCacheRefresher; import ca.uhn.fhir.jpa.cache.IResourceChangeListenerRegistry; +import ca.uhn.fhir.jpa.cache.ResourceChangeListenerCacheFactory; +import ca.uhn.fhir.jpa.cache.ResourceChangeListenerCacheRefresherImpl; +import ca.uhn.fhir.jpa.cache.ResourceChangeListenerRegistryImpl; +import ca.uhn.fhir.jpa.cache.ResourceChangeListenerRegistryInterceptor; import ca.uhn.fhir.jpa.graphql.GraphQLProvider; import ca.uhn.fhir.jpa.provider.DiffProvider; import ca.uhn.fhir.jpa.provider.IJpaSystemProvider; @@ -16,6 +21,7 @@ import ca.uhn.fhir.jpa.provider.TerminologyUploaderProvider; import ca.uhn.fhir.jpa.provider.ValueSetOperationProvider; import ca.uhn.fhir.jpa.search.DatabaseBackedPagingProvider; import ca.uhn.fhir.jpa.searchparam.SearchParameterMap; +import ca.uhn.fhir.jpa.searchparam.matcher.InMemoryResourceMatcher; import ca.uhn.fhir.jpa.subscription.channel.config.SubscriptionChannelConfig; import ca.uhn.fhir.jpa.subscription.submit.config.SubscriptionSubmitterConfig; import ca.uhn.fhir.rest.server.HardcodedServerAddressStrategy; @@ -24,20 +30,17 @@ import ca.uhn.fhir.rest.server.RestfulServer; import ca.uhn.fhir.rest.server.provider.ResourceProviderFactory; import ca.uhn.fhir.rest.server.util.ISearchParamRegistry; import com.google.common.base.Strings; -import org.cqframework.cql.cql2elm.LibraryManager; import org.cqframework.cql.cql2elm.ModelManager; import org.cqframework.cql.cql2elm.model.CompiledLibrary; import org.cqframework.cql.cql2elm.model.Model; - import org.hl7.cql.model.ModelIdentifier; import org.hl7.elm.r1.VersionedIdentifier; import org.opencds.cqf.cql.engine.runtime.Code; +import org.opencds.cqf.fhir.cql.EvaluationSettings; import org.springframework.context.ApplicationContext; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.Import; -import org.springframework.context.annotation.Primary; -import org.springframework.context.annotation.Scope; import java.util.List; import java.util.Map; @@ -91,7 +94,6 @@ public class TestCrConfig { } @Bean - @Scope("prototype") public ModelManager modelManager(Map theGlobalModelCache) { return new ModelManager(theGlobalModelCache); } @@ -107,34 +109,51 @@ public class TestCrConfig { } @Bean - public Map> globalCodeCache() { + public Map> globalValueSetCache() { return new ConcurrentHashMap<>(); } + @Bean - @Primary public ElmCacheResourceChangeListener elmCacheResourceChangeListener( IResourceChangeListenerRegistry theResourceChangeListenerRegistry, DaoRegistry theDaoRegistry, - Map theGlobalLibraryCache) { + EvaluationSettings theEvaluationSettings) { ElmCacheResourceChangeListener listener = - new ElmCacheResourceChangeListener(theDaoRegistry, theGlobalLibraryCache); + new ElmCacheResourceChangeListener(theDaoRegistry, theEvaluationSettings.getLibraryCache()); theResourceChangeListenerRegistry.registerResourceResourceChangeListener( "Library", SearchParameterMap.newSynchronous(), listener, 1000); return listener; } @Bean - @Primary public CodeCacheResourceChangeListener codeCacheResourceChangeListener( IResourceChangeListenerRegistry theResourceChangeListenerRegistry, - DaoRegistry theDaoRegistry, - Map> theGlobalCodeCache) { - CodeCacheResourceChangeListener listener = - new CodeCacheResourceChangeListener(theDaoRegistry, theGlobalCodeCache); + EvaluationSettings theEvaluationSettings, + DaoRegistry theDaoRegistry) { + + CodeCacheResourceChangeListener listener = new CodeCacheResourceChangeListener(theDaoRegistry, theEvaluationSettings.getValueSetCache()); + //registry theResourceChangeListenerRegistry.registerResourceResourceChangeListener( - "ValueSet", SearchParameterMap.newSynchronous(), listener, 1000); + "ValueSet", SearchParameterMap.newSynchronous(), listener,1000); + return listener; } + @Bean + public IResourceChangeListenerRegistry resourceChangeListenerRegistry(InMemoryResourceMatcher theInMemoryResourceMatcher, FhirContext theFhirContext, ResourceChangeListenerCacheFactory theResourceChangeListenerCacheFactory) { + return new ResourceChangeListenerRegistryImpl(theFhirContext, theResourceChangeListenerCacheFactory, theInMemoryResourceMatcher); + } + + @Bean + IResourceChangeListenerCacheRefresher resourceChangeListenerCacheRefresher() { + return new ResourceChangeListenerCacheRefresherImpl(); + } + @Bean + public ResourceChangeListenerRegistryInterceptor resourceChangeListenerRegistryInterceptor() { + return new ResourceChangeListenerRegistryInterceptor(); + } + + + } diff --git a/hapi-fhir-storage-cr/src/test/java/ca/uhn/fhir/cr/dstu3/BaseCrDstu3TestServer.java b/hapi-fhir-storage-cr/src/test/java/ca/uhn/fhir/cr/dstu3/BaseCrDstu3TestServer.java index b88bb544a63..de10ff63f25 100644 --- a/hapi-fhir-storage-cr/src/test/java/ca/uhn/fhir/cr/dstu3/BaseCrDstu3TestServer.java +++ b/hapi-fhir-storage-cr/src/test/java/ca/uhn/fhir/cr/dstu3/BaseCrDstu3TestServer.java @@ -19,8 +19,8 @@ import org.apache.http.impl.client.CloseableHttpClient; import org.apache.http.impl.client.HttpClientBuilder; import org.apache.http.impl.conn.PoolingHttpClientConnectionManager; import org.eclipse.jetty.server.Server; -import org.eclipse.jetty.servlet.ServletContextHandler; -import org.eclipse.jetty.servlet.ServletHolder; +import org.eclipse.jetty.ee10.servlet.ServletContextHandler; +import org.eclipse.jetty.ee10.servlet.ServletHolder; import org.hl7.fhir.dstu3.model.Bundle; import org.hl7.fhir.dstu3.model.Resource; import org.junit.jupiter.api.AfterEach; diff --git a/hapi-fhir-storage-cr/src/test/java/ca/uhn/fhir/cr/dstu3/TestCrDstu3Config.java b/hapi-fhir-storage-cr/src/test/java/ca/uhn/fhir/cr/dstu3/TestCrDstu3Config.java index 2b6d5eb5025..67a8c1a6f82 100644 --- a/hapi-fhir-storage-cr/src/test/java/ca/uhn/fhir/cr/dstu3/TestCrDstu3Config.java +++ b/hapi-fhir-storage-cr/src/test/java/ca/uhn/fhir/cr/dstu3/TestCrDstu3Config.java @@ -14,7 +14,10 @@ import org.hl7.cql.model.ModelIdentifier; import org.hl7.elm.r1.VersionedIdentifier; import org.opencds.cqf.cql.engine.execution.CqlEngine; +import org.opencds.cqf.cql.engine.runtime.Code; import org.opencds.cqf.fhir.cql.EvaluationSettings; +import org.opencds.cqf.fhir.cql.engine.retrieve.RetrieveSettings; +import org.opencds.cqf.fhir.cql.engine.terminology.TerminologySettings; import org.opencds.cqf.fhir.cr.measure.MeasureEvaluationOptions; import org.opencds.cqf.fhir.utility.ValidationProfile; import org.springframework.context.annotation.Bean; @@ -23,6 +26,7 @@ import org.springframework.context.annotation.Import; import java.util.EnumSet; +import java.util.List; import java.util.Map; import java.util.Set; @@ -41,11 +45,35 @@ public class TestCrDstu3Config { } return measureEvalOptions; } - @Bean - public EvaluationSettings evaluationSettings(TestCqlProperties theCqlProperties, Map theGlobalLibraryCache, Map theGlobalModelCache) { + public TerminologySettings terminologySettings(){ + var termSettings = new TerminologySettings(); + termSettings.setCodeLookupMode(TerminologySettings.CODE_LOOKUP_MODE.USE_CODESYSTEM_URL); + termSettings.setValuesetExpansionMode(TerminologySettings.VALUESET_EXPANSION_MODE.PERFORM_NAIVE_EXPANSION); + termSettings.setValuesetMembershipMode(TerminologySettings.VALUESET_MEMBERSHIP_MODE.USE_EXPANSION); + termSettings.setValuesetPreExpansionMode(TerminologySettings.VALUESET_PRE_EXPANSION_MODE.USE_IF_PRESENT); + + return termSettings; + } + @Bean + public RetrieveSettings retrieveSettings(){ + var retrieveSettings = new RetrieveSettings(); + retrieveSettings.setSearchParameterMode(RetrieveSettings.SEARCH_FILTER_MODE.USE_SEARCH_PARAMETERS); + retrieveSettings.setTerminologyParameterMode(RetrieveSettings.TERMINOLOGY_FILTER_MODE.FILTER_IN_MEMORY); + retrieveSettings.setProfileMode(RetrieveSettings.PROFILE_MODE.OFF); + + return retrieveSettings; + } + @Bean + public EvaluationSettings evaluationSettings(TestCqlProperties theCqlProperties, Map theGlobalLibraryCache, Map theGlobalModelCache, + Map> theGlobalValueSetCache, + RetrieveSettings theRetrieveSettings, + TerminologySettings theTerminologySettings) { var evaluationSettings = EvaluationSettings.getDefault(); - var cqlEngineOptions = evaluationSettings.getEngineOptions(); + var cqlOptions = evaluationSettings.getCqlOptions(); + + var cqlEngineOptions = cqlOptions.getCqlEngineOptions(); Set options = EnumSet.noneOf(CqlEngine.Options.class); if (theCqlProperties.isCqlRuntimeEnableExpressionCaching()) { options.add(CqlEngine.Options.EnableExpressionCaching); @@ -54,18 +82,63 @@ public class TestCrDstu3Config { options.add(CqlEngine.Options.EnableValidation); } cqlEngineOptions.setOptions(options); - var cqlOptions = evaluationSettings.getCqlOptions(); cqlOptions.setCqlEngineOptions(cqlEngineOptions); var cqlCompilerOptions = new CqlCompilerOptions(); - cqlCompilerOptions.setCompatibilityLevel("1.3"); + if (theCqlProperties.isEnableDateRangeOptimization()) { + cqlCompilerOptions.setOptions(CqlCompilerOptions.Options.EnableDateRangeOptimization); + } + if (theCqlProperties.isEnableAnnotations()) { + cqlCompilerOptions.setOptions(CqlCompilerOptions.Options.EnableAnnotations); + } + if (theCqlProperties.isEnableLocators()) { + cqlCompilerOptions.setOptions(CqlCompilerOptions.Options.EnableLocators); + } + if (theCqlProperties.isEnableResultsType()) { + cqlCompilerOptions.setOptions(CqlCompilerOptions.Options.EnableResultTypes); + } + cqlCompilerOptions.setVerifyOnly(theCqlProperties.isCqlCompilerVerifyOnly()); + if (theCqlProperties.isEnableDetailedErrors()) { + cqlCompilerOptions.setOptions(CqlCompilerOptions.Options.EnableDetailedErrors); + } + cqlCompilerOptions.setErrorLevel(theCqlProperties.getCqlCompilerErrorSeverityLevel()); + if (theCqlProperties.isDisableListTraversal()) { + cqlCompilerOptions.setOptions(CqlCompilerOptions.Options.DisableListTraversal); + } + if (theCqlProperties.isDisableListDemotion()) { + cqlCompilerOptions.setOptions(CqlCompilerOptions.Options.DisableListDemotion); + } + if (theCqlProperties.isDisableListPromotion()) { + cqlCompilerOptions.setOptions(CqlCompilerOptions.Options.DisableListPromotion); + } + if (theCqlProperties.isEnableIntervalDemotion()) { + cqlCompilerOptions.setOptions(CqlCompilerOptions.Options.EnableIntervalDemotion); + } + if (theCqlProperties.isEnableIntervalPromotion()) { + cqlCompilerOptions.setOptions(CqlCompilerOptions.Options.EnableIntervalPromotion); + } + if (theCqlProperties.isDisableMethodInvocation()) { + cqlCompilerOptions.setOptions(CqlCompilerOptions.Options.DisableMethodInvocation); + } + if (theCqlProperties.isRequireFromKeyword()) { + cqlCompilerOptions.setOptions(CqlCompilerOptions.Options.RequireFromKeyword); + } + cqlCompilerOptions.setValidateUnits(theCqlProperties.isCqlCompilerValidateUnits()); + if (theCqlProperties.isDisableDefaultModelInfoLoad()) { + cqlCompilerOptions.setOptions(CqlCompilerOptions.Options.DisableDefaultModelInfoLoad); + } + cqlCompilerOptions.setSignatureLevel(theCqlProperties.getCqlCompilerSignatureLevel()); + cqlCompilerOptions.setCompatibilityLevel(theCqlProperties.getCqlCompilerCompatibilityLevel()); cqlCompilerOptions.setAnalyzeDataRequirements(theCqlProperties.isCqlCompilerAnalyzeDataRequirements()); cqlCompilerOptions.setCollapseDataRequirements(theCqlProperties.isCqlCompilerCollapseDataRequirements()); cqlOptions.setCqlCompilerOptions(cqlCompilerOptions); + evaluationSettings.setTerminologySettings(theTerminologySettings); + evaluationSettings.setRetrieveSettings(theRetrieveSettings); evaluationSettings.setLibraryCache(theGlobalLibraryCache); evaluationSettings.setModelCache(theGlobalModelCache); + evaluationSettings.setValueSetCache(theGlobalValueSetCache); return evaluationSettings; } } diff --git a/hapi-fhir-storage-cr/src/test/java/ca/uhn/fhir/cr/r4/BaseCrR4TestServer.java b/hapi-fhir-storage-cr/src/test/java/ca/uhn/fhir/cr/r4/BaseCrR4TestServer.java index a29d3960493..a4449b3f0d1 100644 --- a/hapi-fhir-storage-cr/src/test/java/ca/uhn/fhir/cr/r4/BaseCrR4TestServer.java +++ b/hapi-fhir-storage-cr/src/test/java/ca/uhn/fhir/cr/r4/BaseCrR4TestServer.java @@ -2,8 +2,15 @@ package ca.uhn.fhir.cr.r4; import ca.uhn.fhir.context.FhirContext; import ca.uhn.fhir.cr.IResourceLoader; +import ca.uhn.fhir.cr.config.r4.ApplyOperationConfig; +import ca.uhn.fhir.cr.config.r4.ExtractOperationConfig; +import ca.uhn.fhir.cr.config.r4.PackageOperationConfig; +import ca.uhn.fhir.cr.config.r4.PopulateOperationConfig; import ca.uhn.fhir.jpa.api.config.JpaStorageSettings; import ca.uhn.fhir.jpa.api.dao.DaoRegistry; +import ca.uhn.fhir.jpa.cache.IResourceChangeListenerCache; +import ca.uhn.fhir.jpa.cache.IResourceChangeListenerRegistry; +import ca.uhn.fhir.jpa.cache.ResourceChangeListenerRegistryImpl; import ca.uhn.fhir.jpa.search.DatabaseBackedPagingProvider; import ca.uhn.fhir.jpa.test.BaseJpaR4Test; import ca.uhn.fhir.parser.IParser; @@ -19,8 +26,8 @@ import org.apache.http.impl.client.HttpClientBuilder; import org.apache.http.impl.conn.PoolingHttpClientConnectionManager; import org.eclipse.jetty.server.Server; -import org.eclipse.jetty.servlet.ServletContextHandler; -import org.eclipse.jetty.servlet.ServletHolder; +import org.eclipse.jetty.ee10.servlet.ServletContextHandler; +import org.eclipse.jetty.ee10.servlet.ServletHolder; import org.hl7.fhir.r4.model.Bundle; import org.hl7.fhir.r4.model.Resource; import org.junit.jupiter.api.AfterEach; @@ -31,11 +38,19 @@ import org.springframework.beans.factory.annotation.Autowired; import org.springframework.mock.web.MockHttpServletRequest; import org.springframework.test.context.ContextConfiguration; +import java.util.HashSet; import java.util.List; +import java.util.Set; import java.util.concurrent.TimeUnit; -@ContextConfiguration(classes = {TestCrR4Config.class}) +@ContextConfiguration(classes = { + TestCrR4Config.class, + ApplyOperationConfig.class, + ExtractOperationConfig.class, + PackageOperationConfig.class, + PopulateOperationConfig.class +}) public abstract class BaseCrR4TestServer extends BaseJpaR4Test implements IResourceLoader { public static IGenericClient ourClient; @@ -60,6 +75,7 @@ public abstract class BaseCrR4TestServer extends BaseJpaR4Test implements IResou ourClient.unregisterInterceptor(mySimpleHeaderInterceptor); myStorageSettings.setIndexMissingFields(new JpaStorageSettings().getIndexMissingFields()); myEvaluationSettings.getLibraryCache().clear(); + myEvaluationSettings.getValueSetCache().clear(); } @Autowired RestfulServer ourRestfulServer; diff --git a/hapi-fhir-storage-cr/src/test/java/ca/uhn/fhir/cr/r4/CrResourceListenerTests.java b/hapi-fhir-storage-cr/src/test/java/ca/uhn/fhir/cr/r4/CrResourceListenerTests.java new file mode 100644 index 00000000000..0a637103901 --- /dev/null +++ b/hapi-fhir-storage-cr/src/test/java/ca/uhn/fhir/cr/r4/CrResourceListenerTests.java @@ -0,0 +1,176 @@ +package ca.uhn.fhir.cr.r4; + +import ca.uhn.fhir.jpa.cache.ResourceChangeListenerCacheRefresherImpl; +import ca.uhn.fhir.jpa.cache.ResourceChangeListenerRegistryImpl; +import org.hl7.fhir.r4.model.DateType; +import org.hl7.fhir.r4.model.IdType; +import org.hl7.fhir.r4.model.Library; +import org.hl7.fhir.r4.model.MeasureReport; +import org.hl7.fhir.r4.model.Parameters; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import org.opencds.cqf.fhir.cql.EvaluationSettings; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.test.context.junit.jupiter.SpringExtension; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNotNull; +import static org.junit.jupiter.api.Assertions.assertTrue; + +@ExtendWith(SpringExtension.class) +public class CrResourceListenerTests extends BaseCrR4TestServer { + @Autowired + EvaluationSettings myEvaluationSettings; + @Autowired + ResourceChangeListenerRegistryImpl myResourceChangeListenerRegistry; + @Autowired + ResourceChangeListenerCacheRefresherImpl myResourceChangeListenerCacheRefresher; + + + public MeasureReport runEvaluateMeasure(String periodStart, String periodEnd, String subject, String measureId, String reportType, String practitioner){ + + var parametersEval = new Parameters(); + parametersEval.addParameter("periodStart", new DateType(periodStart)); + parametersEval.addParameter("periodEnd", new DateType(periodEnd)); + parametersEval.addParameter("practitioner", practitioner); + parametersEval.addParameter("reportType", reportType); + parametersEval.addParameter("subject", subject); + + var report = ourClient.operation().onInstance("Measure/" + measureId) + .named("$evaluate-measure") + .withParameters(parametersEval) + .returnResourceType(MeasureReport.class) + .execute(); + + assertNotNull(report); + + return report; + } + + @Test + void testCodeCacheInvalidation() throws InterruptedException { + + assertTrue(myResourceChangeListenerRegistry.getWatchedResourceNames().contains("ValueSet")); + + loadBundle("ColorectalCancerScreeningsFHIR-bundle.json"); + runEvaluateMeasure("2019-01-01", "2019-12-31", "Patient/numer-EXM130", "ColorectalCancerScreeningsFHIR", "Individual", null); + + // This is a manual init + myResourceChangeListenerCacheRefresher.refreshExpiredCachesAndNotifyListeners(); + + //cached valueSets + assertEquals(7, myEvaluationSettings.getValueSetCache().size()); + + //remove valueset from server + var id = new IdType("ValueSet/2.16.840.1.113883.3.464.1003.101.12.1001"); + ourClient.delete().resourceById(id).execute(); + + // This is a manual refresh + myResourceChangeListenerCacheRefresher.refreshExpiredCachesAndNotifyListeners(); + + //_ALL_ valuesets should be removed from cache + assertEquals(0, myEvaluationSettings.getValueSetCache().size()); + } + + @Test + void testElmCacheInvalidation() throws InterruptedException { + + assertTrue(myResourceChangeListenerRegistry.getWatchedResourceNames().contains("Library")); + + loadBundle("ColorectalCancerScreeningsFHIR-bundle.json"); + // evaluate-measure adds library to repository cache + runEvaluateMeasure("2019-01-01", "2019-12-31", "Patient/numer-EXM130", "ColorectalCancerScreeningsFHIR", "Individual", null); + + // This is a manual init + myResourceChangeListenerCacheRefresher.refreshExpiredCachesAndNotifyListeners(); + + //cached libraries + assertEquals(7, myEvaluationSettings.getLibraryCache().size()); + + //remove Library from server + var id = new IdType("Library/ColorectalCancerScreeningsFHIR"); + ourClient.delete().resourceById(id).execute(); + + // This is a manual refresh + myResourceChangeListenerCacheRefresher.refreshExpiredCachesAndNotifyListeners(); + + //_ALL_ Libraries should be removed from cache + assertEquals(0, myEvaluationSettings.getLibraryCache().size()); + } + + @Test + void testAddNewVersionOfSameLibrary() throws InterruptedException { + + assertTrue(myResourceChangeListenerRegistry.getWatchedResourceNames().contains("Library")); + // load measure bundle with measure library version + loadBundle("ColorectalCancerScreeningsFHIR-bundle.json"); + // evaluate-measure adds library to repository cache + runEvaluateMeasure("2019-01-01", "2019-12-31", "Patient/numer-EXM130", "ColorectalCancerScreeningsFHIR", "Individual", null); + + //cached libraries from bundle + assertEquals(7, myEvaluationSettings.getLibraryCache().size()); + + // manually refresh cache + myResourceChangeListenerCacheRefresher.refreshExpiredCachesAndNotifyListeners(); + + // add same version of measure Library to server with minor edits + loadBundle("multiversion/EXM130-0.0.001-bundle.json"); + + // manually refresh cache + myResourceChangeListenerCacheRefresher.refreshExpiredCachesAndNotifyListeners(); + + //cache should be invalidated for matching library name and version + assertEquals(6, myEvaluationSettings.getLibraryCache().size()); + } + + @Test + void testNewVersionLibraryAdd() throws InterruptedException { + + assertTrue(myResourceChangeListenerRegistry.getWatchedResourceNames().contains("Library")); + // load measure bundle with measure library version + loadBundle("ColorectalCancerScreeningsFHIR-bundle.json"); + // evaluate-measure adds library to repository cache + runEvaluateMeasure("2019-01-01", "2019-12-31", "Patient/numer-EXM130", "ColorectalCancerScreeningsFHIR", "Individual", null); + + //cached libraries from bundle + assertEquals(7, myEvaluationSettings.getLibraryCache().size()); + + // manually refresh cache + myResourceChangeListenerCacheRefresher.refreshExpiredCachesAndNotifyListeners(); + + // add same version of measure Library to server with minor edits + loadBundle("multiversion/EXM130-0.0.002-bundle.json"); + + // manually refresh cache + myResourceChangeListenerCacheRefresher.refreshExpiredCachesAndNotifyListeners(); + + //cache should not be invalidated because name and version don't have a match in cache + assertEquals(7, myEvaluationSettings.getLibraryCache().size()); + } + + @Test + void testNewVersionValueSetAdd() throws InterruptedException { + + assertTrue(myResourceChangeListenerRegistry.getWatchedResourceNames().contains("ValueSet")); + // load measure bundle with measure library version + loadBundle("ColorectalCancerScreeningsFHIR-bundle.json"); + // evaluate-measure adds valueset to repository cache + runEvaluateMeasure("2019-01-01", "2019-12-31", "Patient/numer-EXM130", "ColorectalCancerScreeningsFHIR", "Individual", null); + + //cached valueset from bundle + assertEquals(8, myEvaluationSettings.getValueSetCache().size()); + + // manually refresh cache + myResourceChangeListenerCacheRefresher.refreshExpiredCachesAndNotifyListeners(); + + // add new version of valueset to server + loadBundle("multiversion/valueset-version-bundle.json"); + + // manually refresh cache + myResourceChangeListenerCacheRefresher.refreshExpiredCachesAndNotifyListeners(); + + //cache should be invalidated for valueset url and removed + assertEquals(7, myEvaluationSettings.getValueSetCache().size()); + } + +} diff --git a/hapi-fhir-storage-cr/src/test/java/ca/uhn/fhir/cr/r4/R4MeasureOperationProviderIT.java b/hapi-fhir-storage-cr/src/test/java/ca/uhn/fhir/cr/r4/R4MeasureOperationProviderIT.java index a5f4e7964a0..d822fe1ad1a 100644 --- a/hapi-fhir-storage-cr/src/test/java/ca/uhn/fhir/cr/r4/R4MeasureOperationProviderIT.java +++ b/hapi-fhir-storage-cr/src/test/java/ca/uhn/fhir/cr/r4/R4MeasureOperationProviderIT.java @@ -1,6 +1,5 @@ package ca.uhn.fhir.cr.r4; -import ca.uhn.fhir.cr.r4.measure.MeasureOperationsProvider; import org.hl7.fhir.r4.model.DateType; import org.hl7.fhir.r4.model.IdType; import org.hl7.fhir.r4.model.MeasureReport; @@ -9,7 +8,7 @@ import org.hl7.fhir.r4.model.Parameters; import org.hl7.fhir.r4.model.Resource; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.ExtendWith; -import org.springframework.beans.factory.annotation.Autowired; + import org.springframework.test.context.junit.jupiter.SpringExtension; import java.util.Optional; @@ -20,8 +19,6 @@ import static org.junit.jupiter.api.Assertions.assertTrue; @ExtendWith(SpringExtension.class) class R4MeasureOperationProviderIT extends BaseCrR4TestServer { - @Autowired - MeasureOperationsProvider myMeasureOperationsProvider; public MeasureReport runEvaluateMeasure(String periodStart, String periodEnd, String subject, String measureId, String reportType, String practitioner){ @@ -44,7 +41,7 @@ class R4MeasureOperationProviderIT extends BaseCrR4TestServer { } @Test - void testMeasureEvaluate_EXM130() { + void testMeasureEvaluate_EXM130() throws InterruptedException { loadBundle("ColorectalCancerScreeningsFHIR-bundle.json"); runEvaluateMeasure("2019-01-01", "2019-12-31", "Patient/numer-EXM130", "ColorectalCancerScreeningsFHIR", "Individual", null); } @@ -169,4 +166,5 @@ class R4MeasureOperationProviderIT extends BaseCrR4TestServer { } + } diff --git a/hapi-fhir-storage-cr/src/test/java/ca/uhn/fhir/cr/r4/TestCrR4Config.java b/hapi-fhir-storage-cr/src/test/java/ca/uhn/fhir/cr/r4/TestCrR4Config.java index 7f4cc6f46c8..c6efed8ddee 100644 --- a/hapi-fhir-storage-cr/src/test/java/ca/uhn/fhir/cr/r4/TestCrR4Config.java +++ b/hapi-fhir-storage-cr/src/test/java/ca/uhn/fhir/cr/r4/TestCrR4Config.java @@ -3,18 +3,19 @@ package ca.uhn.fhir.cr.r4; import ca.uhn.fhir.cr.TestCqlProperties; import ca.uhn.fhir.cr.TestCrConfig; import ca.uhn.fhir.cr.common.CqlThreadFactory; -import ca.uhn.fhir.cr.config.r4.ApplyOperationConfig; -import ca.uhn.fhir.cr.config.r4.ExtractOperationConfig; -import ca.uhn.fhir.cr.config.r4.PackageOperationConfig; -import ca.uhn.fhir.cr.config.r4.PopulateOperationConfig; import ca.uhn.fhir.cr.config.r4.CrR4Config; +import ca.uhn.fhir.rest.api.SearchStyleEnum; import org.cqframework.cql.cql2elm.CqlCompilerOptions; import org.cqframework.cql.cql2elm.model.CompiledLibrary; import org.cqframework.cql.cql2elm.model.Model; import org.hl7.cql.model.ModelIdentifier; import org.hl7.elm.r1.VersionedIdentifier; import org.opencds.cqf.cql.engine.execution.CqlEngine; +import org.opencds.cqf.cql.engine.runtime.Code; import org.opencds.cqf.fhir.cql.EvaluationSettings; +import org.opencds.cqf.fhir.cql.engine.retrieve.BaseRetrieveProvider; +import org.opencds.cqf.fhir.cql.engine.retrieve.RetrieveSettings; +import org.opencds.cqf.fhir.cql.engine.terminology.TerminologySettings; import org.opencds.cqf.fhir.cr.measure.CareGapsProperties; import org.opencds.cqf.fhir.cr.measure.MeasureEvaluationOptions; import org.opencds.cqf.fhir.utility.ValidationProfile; @@ -25,18 +26,14 @@ import org.springframework.context.annotation.Primary; import org.springframework.security.concurrent.DelegatingSecurityContextExecutorService; import java.util.EnumSet; +import java.util.List; import java.util.Map; import java.util.Set; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; @Configuration -@Import({TestCrConfig.class, CrR4Config.class, - ApplyOperationConfig.class, - ExtractOperationConfig.class, - PackageOperationConfig.class, - PopulateOperationConfig.class -}) +@Import({TestCrConfig.class, CrR4Config.class}) public class TestCrR4Config { @Primary @Bean @@ -67,11 +64,36 @@ public class TestCrR4Config { } return measureEvalOptions; } - @Bean - public EvaluationSettings evaluationSettings(TestCqlProperties theCqlProperties, Map theGlobalLibraryCache, Map theGlobalModelCache) { - var evaluationSettings = EvaluationSettings.getDefault(); - var cqlEngineOptions = evaluationSettings.getEngineOptions(); + @Bean + public TerminologySettings terminologySettings(){ + var termSettings = new TerminologySettings(); + termSettings.setCodeLookupMode(TerminologySettings.CODE_LOOKUP_MODE.USE_CODESYSTEM_URL); + termSettings.setValuesetExpansionMode(TerminologySettings.VALUESET_EXPANSION_MODE.PERFORM_NAIVE_EXPANSION); + termSettings.setValuesetMembershipMode(TerminologySettings.VALUESET_MEMBERSHIP_MODE.USE_EXPANSION); + termSettings.setValuesetPreExpansionMode(TerminologySettings.VALUESET_PRE_EXPANSION_MODE.USE_IF_PRESENT); + + return termSettings; + } + @Bean + public RetrieveSettings retrieveSettings(){ + var retrieveSettings = new RetrieveSettings(); + retrieveSettings.setSearchParameterMode(RetrieveSettings.SEARCH_FILTER_MODE.USE_SEARCH_PARAMETERS); + retrieveSettings.setTerminologyParameterMode(RetrieveSettings.TERMINOLOGY_FILTER_MODE.FILTER_IN_MEMORY); + retrieveSettings.setProfileMode(RetrieveSettings.PROFILE_MODE.OFF); + + return retrieveSettings; + } + @Bean + public EvaluationSettings evaluationSettings(TestCqlProperties theCqlProperties, Map theGlobalLibraryCache, Map theGlobalModelCache, + Map> theGlobalValueSetCache, + RetrieveSettings theRetrieveSettings, + TerminologySettings theTerminologySettings) { + var evaluationSettings = EvaluationSettings.getDefault(); + var cqlOptions = evaluationSettings.getCqlOptions(); + + var cqlEngineOptions = cqlOptions.getCqlEngineOptions(); Set options = EnumSet.noneOf(CqlEngine.Options.class); if (theCqlProperties.isCqlRuntimeEnableExpressionCaching()) { options.add(CqlEngine.Options.EnableExpressionCaching); @@ -80,20 +102,63 @@ public class TestCrR4Config { options.add(CqlEngine.Options.EnableValidation); } cqlEngineOptions.setOptions(options); - var cqlOptions = evaluationSettings.getCqlOptions(); cqlOptions.setCqlEngineOptions(cqlEngineOptions); var cqlCompilerOptions = new CqlCompilerOptions(); - cqlCompilerOptions.setCompatibilityLevel("1.5"); - cqlOptions.setUseEmbeddedLibraries(true); - + if (theCqlProperties.isEnableDateRangeOptimization()) { + cqlCompilerOptions.setOptions(CqlCompilerOptions.Options.EnableDateRangeOptimization); + } + if (theCqlProperties.isEnableAnnotations()) { + cqlCompilerOptions.setOptions(CqlCompilerOptions.Options.EnableAnnotations); + } + if (theCqlProperties.isEnableLocators()) { + cqlCompilerOptions.setOptions(CqlCompilerOptions.Options.EnableLocators); + } + if (theCqlProperties.isEnableResultsType()) { + cqlCompilerOptions.setOptions(CqlCompilerOptions.Options.EnableResultTypes); + } + cqlCompilerOptions.setVerifyOnly(theCqlProperties.isCqlCompilerVerifyOnly()); + if (theCqlProperties.isEnableDetailedErrors()) { + cqlCompilerOptions.setOptions(CqlCompilerOptions.Options.EnableDetailedErrors); + } + cqlCompilerOptions.setErrorLevel(theCqlProperties.getCqlCompilerErrorSeverityLevel()); + if (theCqlProperties.isDisableListTraversal()) { + cqlCompilerOptions.setOptions(CqlCompilerOptions.Options.DisableListTraversal); + } + if (theCqlProperties.isDisableListDemotion()) { + cqlCompilerOptions.setOptions(CqlCompilerOptions.Options.DisableListDemotion); + } + if (theCqlProperties.isDisableListPromotion()) { + cqlCompilerOptions.setOptions(CqlCompilerOptions.Options.DisableListPromotion); + } + if (theCqlProperties.isEnableIntervalDemotion()) { + cqlCompilerOptions.setOptions(CqlCompilerOptions.Options.EnableIntervalDemotion); + } + if (theCqlProperties.isEnableIntervalPromotion()) { + cqlCompilerOptions.setOptions(CqlCompilerOptions.Options.EnableIntervalPromotion); + } + if (theCqlProperties.isDisableMethodInvocation()) { + cqlCompilerOptions.setOptions(CqlCompilerOptions.Options.DisableMethodInvocation); + } + if (theCqlProperties.isRequireFromKeyword()) { + cqlCompilerOptions.setOptions(CqlCompilerOptions.Options.RequireFromKeyword); + } + cqlCompilerOptions.setValidateUnits(theCqlProperties.isCqlCompilerValidateUnits()); + if (theCqlProperties.isDisableDefaultModelInfoLoad()) { + cqlCompilerOptions.setOptions(CqlCompilerOptions.Options.DisableDefaultModelInfoLoad); + } + cqlCompilerOptions.setSignatureLevel(theCqlProperties.getCqlCompilerSignatureLevel()); + cqlCompilerOptions.setCompatibilityLevel(theCqlProperties.getCqlCompilerCompatibilityLevel()); cqlCompilerOptions.setAnalyzeDataRequirements(theCqlProperties.isCqlCompilerAnalyzeDataRequirements()); cqlCompilerOptions.setCollapseDataRequirements(theCqlProperties.isCqlCompilerCollapseDataRequirements()); cqlOptions.setCqlCompilerOptions(cqlCompilerOptions); + evaluationSettings.setTerminologySettings(theTerminologySettings); + evaluationSettings.setRetrieveSettings(theRetrieveSettings); evaluationSettings.setLibraryCache(theGlobalLibraryCache); evaluationSettings.setModelCache(theGlobalModelCache); + evaluationSettings.setValueSetCache(theGlobalValueSetCache); return evaluationSettings; } diff --git a/hapi-fhir-storage-cr/src/test/resources/ColorectalCancerScreeningsFHIR-bundle.json b/hapi-fhir-storage-cr/src/test/resources/ColorectalCancerScreeningsFHIR-bundle.json index b48340e557e..87864f5254d 100644 --- a/hapi-fhir-storage-cr/src/test/resources/ColorectalCancerScreeningsFHIR-bundle.json +++ b/hapi-fhir-storage-cr/src/test/resources/ColorectalCancerScreeningsFHIR-bundle.json @@ -147712,7 +147712,7 @@ } ], "library": [ - "http://ecqi.healthit.gov/ecqms/Library/ColorectalCancerScreeningsFHIR" + "http://ecqi.healthit.gov/ecqms/Library/ColorectalCancerScreeningsFHIR|0.0.001" ], "disclaimer": "The performance Measure is not a clinical guideline and does not establish a standard of medical care, and has not been tested for all potential applications. THE MEASURE AND SPECIFICATIONS ARE PROVIDED \"AS IS\" WITHOUT WARRANTY OF ANY KIND.\n \nDue to technical limitations, registered trademarks are indicated by (R) or [R] and unregistered trademarks are indicated by (TM) or [TM].", "scoring": { diff --git a/hapi-fhir-storage-cr/src/test/resources/largeValueSetMeasureTest-Bundle.json b/hapi-fhir-storage-cr/src/test/resources/largeValueSetMeasureTest-Bundle.json index bd9ba5bb084..a4785ce2534 100644 --- a/hapi-fhir-storage-cr/src/test/resources/largeValueSetMeasureTest-Bundle.json +++ b/hapi-fhir-storage-cr/src/test/resources/largeValueSetMeasureTest-Bundle.json @@ -1,13276 +1,13256 @@ { - "resourceType": "Bundle", - "id": "CMSTest-bundle", - "type": "transaction", - "entry": [ - { - "resource": { - "resourceType": "ValueSet", - "id": "2.16.840.1.114222.4.11.837", - "url": "http://cts.nlm.nih.gov/fhir/ValueSet/2.16.840.1.114222.4.11.837", - "identifier": [ - { - "system": "urn:ietf:rfc:3986", - "value": "2.16.840.1.114222.4.11.837" - } - ], - "version": "20121025", - "name": "Ethnicity", - "title": "Ethnicity", - "status": "active", - "experimental": false, - "publisher": "NLM", - "description": "Codes representing possible values for Ethnicity.", - "expansion": { - "identifier": "20210506", - "timestamp": "2021-08-19T13:27:33-06:00", - "contains": [ - { - "system": "http://terminology.hl7.org/CodeSystem/PHRaceAndEthnicityCDC", - "version": "1.2", - "code": "2135-2", - "display": "Hispanic or Latino" - }, - { - "system": "http://terminology.hl7.org/CodeSystem/PHRaceAndEthnicityCDC", - "version": "1.2", - "code": "2186-5", - "display": "Not Hispanic or Latino" - } - ] - } - }, - "request": { - "method": "PUT", - "url": "ValueSet/2.16.840.1.114222.4.11.837" - } - }, - { - "resource": { - "resourceType": "ValueSet", - "id": "2.16.840.1.114222.4.11.3591", - "url": "http://cts.nlm.nih.gov/fhir/ValueSet/2.16.840.1.114222.4.11.3591", - "identifier": [ - { - "system": "urn:ietf:rfc:3986", - "value": "2.16.840.1.114222.4.11.3591" - } - ], - "version": "20180718", - "name": "Payer", - "title": "Payer", - "status": "active", - "experimental": false, - "publisher": "NLM", - "description": "Codes representing possible values for Payer.", - "expansion": { - "identifier": "20210506", - "timestamp": "2021-08-19T13:27:33-06:00", - "contains": [ - { - "system": "https://nahdo.org/sopt", - "version": "9.2", - "code": "1", - "display": "MEDICARE" - }, - { - "system": "https://nahdo.org/sopt", - "version": "9.2", - "code": "11", - "display": "Medicare Managed Care (Includes Medicare Advantage Plans)" - }, - { - "system": "https://nahdo.org/sopt", - "version": "9.2", - "code": "111", - "display": "Medicare HMO" - }, - { - "system": "https://nahdo.org/sopt", - "version": "9.2", - "code": "112", - "display": "Medicare PPO" - }, - { - "system": "https://nahdo.org/sopt", - "version": "9.2", - "code": "113", - "display": "Medicare POS" - }, - { - "system": "https://nahdo.org/sopt", - "version": "9.2", - "code": "119", - "display": "Medicare Managed Care Other" - }, - { - "system": "https://nahdo.org/sopt", - "version": "9.2", - "code": "12", - "display": "Medicare (Non-managed Care)" - }, - { - "system": "https://nahdo.org/sopt", - "version": "9.2", - "code": "121", - "display": "Medicare FFS" - }, - { - "system": "https://nahdo.org/sopt", - "version": "9.2", - "code": "122", - "display": "Medicare Drug Benefit" - }, - { - "system": "https://nahdo.org/sopt", - "version": "9.2", - "code": "123", - "display": "Medicare Medical Savings Account (MSA)" - }, - { - "system": "https://nahdo.org/sopt", - "version": "9.2", - "code": "129", - "display": "Medicare Non-managed Care Other" - }, - { - "system": "https://nahdo.org/sopt", - "version": "9.2", - "code": "13", - "display": "Medicare Hospice" - }, - { - "system": "https://nahdo.org/sopt", - "version": "9.2", - "code": "14", - "display": "Dual Eligibility Medicare/Medicaid Organization" - }, - { - "system": "https://nahdo.org/sopt", - "version": "9.2", - "code": "19", - "display": "Medicare Other" - }, - { - "system": "https://nahdo.org/sopt", - "version": "9.2", - "code": "191", - "display": "Medicare Pharmacy Benefit Manager" - }, - { - "system": "https://nahdo.org/sopt", - "version": "9.2", - "code": "2", - "display": "MEDICAID" - }, - { - "system": "https://nahdo.org/sopt", - "version": "9.2", - "code": "21", - "display": "Medicaid (Managed Care)" - }, - { - "system": "https://nahdo.org/sopt", - "version": "9.2", - "code": "211", - "display": "Medicaid HMO" - }, - { - "system": "https://nahdo.org/sopt", - "version": "9.2", - "code": "212", - "display": "Medicaid PPO" - }, - { - "system": "https://nahdo.org/sopt", - "version": "9.2", - "code": "213", - "display": "Medicaid PCCM (Primary Care Case Management)" - }, - { - "system": "https://nahdo.org/sopt", - "version": "9.2", - "code": "219", - "display": "Medicaid Managed Care Other" - }, - { - "system": "https://nahdo.org/sopt", - "version": "9.2", - "code": "22", - "display": "Medicaid (Non-managed Care Plan)" - }, - { - "system": "https://nahdo.org/sopt", - "version": "9.2", - "code": "23", - "display": "Medicaid/SCHIP" - }, - { - "system": "https://nahdo.org/sopt", - "version": "9.2", - "code": "25", - "display": "Medicaid - Out of State" - }, - { - "system": "https://nahdo.org/sopt", - "version": "9.2", - "code": "26", - "display": "Medicaid - Long Term Care" - }, - { - "system": "https://nahdo.org/sopt", - "version": "9.2", - "code": "29", - "display": "Medicaid Other" - }, - { - "system": "https://nahdo.org/sopt", - "version": "9.2", - "code": "291", - "display": "Medicaid Pharmacy Benefit Manager" - }, - { - "system": "https://nahdo.org/sopt", - "version": "9.2", - "code": "299", - "display": "Medicaid - Dental" - }, - { - "system": "https://nahdo.org/sopt", - "version": "9.2", - "code": "3", - "display": "OTHER GOVERNMENT (Federal/State/Local) (excluding Department of Corrections)" - }, - { - "system": "https://nahdo.org/sopt", - "version": "9.2", - "code": "31", - "display": "Department of Defense" - }, - { - "system": "https://nahdo.org/sopt", - "version": "9.2", - "code": "311", - "display": "TRICARE (CHAMPUS)" - }, - { - "system": "https://nahdo.org/sopt", - "version": "9.2", - "code": "3111", - "display": "TRICARE Prime--HMO" - }, - { - "system": "https://nahdo.org/sopt", - "version": "9.2", - "code": "3112", - "display": "TRICARE Extra--PPO" - }, - { - "system": "https://nahdo.org/sopt", - "version": "9.2", - "code": "3113", - "display": "TRICARE Standard - Fee For Service" - }, - { - "system": "https://nahdo.org/sopt", - "version": "9.2", - "code": "3114", - "display": "TRICARE For Life--Medicare Supplement" - }, - { - "system": "https://nahdo.org/sopt", - "version": "9.2", - "code": "3115", - "display": "TRICARE Reserve Select" - }, - { - "system": "https://nahdo.org/sopt", - "version": "9.2", - "code": "3116", - "display": "Uniformed Services Family Health Plan (USFHP) -- HMO" - }, - { - "system": "https://nahdo.org/sopt", - "version": "9.2", - "code": "3119", - "display": "Department of Defense - (other)" - }, - { - "system": "https://nahdo.org/sopt", - "version": "9.2", - "code": "312", - "display": "Military Treatment Facility" - }, - { - "system": "https://nahdo.org/sopt", - "version": "9.2", - "code": "3121", - "display": "Enrolled Prime--HMO" - }, - { - "system": "https://nahdo.org/sopt", - "version": "9.2", - "code": "3122", - "display": "Non-enrolled Space Available" - }, - { - "system": "https://nahdo.org/sopt", - "version": "9.2", - "code": "3123", - "display": "TRICARE For Life (TFL)" - }, - { - "system": "https://nahdo.org/sopt", - "version": "9.2", - "code": "313", - "display": "Dental --Stand Alone" - }, - { - "system": "https://nahdo.org/sopt", - "version": "9.2", - "code": "32", - "display": "Department of Veterans Affairs" - }, - { - "system": "https://nahdo.org/sopt", - "version": "9.2", - "code": "321", - "display": "Veteran care-Care provided to Veterans" - }, - { - "system": "https://nahdo.org/sopt", - "version": "9.2", - "code": "3211", - "display": "Direct Care-Care provided in VA facilities" - }, - { - "system": "https://nahdo.org/sopt", - "version": "9.2", - "code": "3212", - "display": "Indirect Care-Care provided outside VA facilities" - }, - { - "system": "https://nahdo.org/sopt", - "version": "9.2", - "code": "32121", - "display": "Fee Basis" - }, - { - "system": "https://nahdo.org/sopt", - "version": "9.2", - "code": "32122", - "display": "Foreign Fee/Foreign Medical Program (FMP)" - }, - { - "system": "https://nahdo.org/sopt", - "version": "9.2", - "code": "32123", - "display": "Contract Nursing Home/Community Nursing Home" - }, - { - "system": "https://nahdo.org/sopt", - "version": "9.2", - "code": "32124", - "display": "State Veterans Home" - }, - { - "system": "https://nahdo.org/sopt", - "version": "9.2", - "code": "32125", - "display": "Sharing Agreements" - }, - { - "system": "https://nahdo.org/sopt", - "version": "9.2", - "code": "32126", - "display": "Other Federal Agency" - }, - { - "system": "https://nahdo.org/sopt", - "version": "9.2", - "code": "32127", - "display": "Dental Care" - }, - { - "system": "https://nahdo.org/sopt", - "version": "9.2", - "code": "32128", - "display": "Vision Care" - }, - { - "system": "https://nahdo.org/sopt", - "version": "9.2", - "code": "322", - "display": "Non-veteran care" - }, - { - "system": "https://nahdo.org/sopt", - "version": "9.2", - "code": "3221", - "display": "Civilian Health and Medical Program for the VA (CHAMPVA)" - }, - { - "system": "https://nahdo.org/sopt", - "version": "9.2", - "code": "3222", - "display": "Spina Bifida Health Care Program (SB)" - }, - { - "system": "https://nahdo.org/sopt", - "version": "9.2", - "code": "3223", - "display": "Children of Women Vietnam Veterans (CWVV)" - }, - { - "system": "https://nahdo.org/sopt", - "version": "9.2", - "code": "3229", - "display": "Other non-veteran care" - }, - { - "system": "https://nahdo.org/sopt", - "version": "9.2", - "code": "33", - "display": "Indian Health Service or Tribe" - }, - { - "system": "https://nahdo.org/sopt", - "version": "9.2", - "code": "331", - "display": "Indian Health Service - Regular" - }, - { - "system": "https://nahdo.org/sopt", - "version": "9.2", - "code": "332", - "display": "Indian Health Service - Contract" - }, - { - "system": "https://nahdo.org/sopt", - "version": "9.2", - "code": "333", - "display": "Indian Health Service - Managed Care" - }, - { - "system": "https://nahdo.org/sopt", - "version": "9.2", - "code": "334", - "display": "Indian Tribe - Sponsored Coverage" - }, - { - "system": "https://nahdo.org/sopt", - "version": "9.2", - "code": "34", - "display": "HRSA Program" - }, - { - "system": "https://nahdo.org/sopt", - "version": "9.2", - "code": "341", - "display": "Title V (MCH Block Grant)" - }, - { - "system": "https://nahdo.org/sopt", - "version": "9.2", - "code": "342", - "display": "Migrant Health Program" - }, - { - "system": "https://nahdo.org/sopt", - "version": "9.2", - "code": "343", - "display": "Ryan White Act" - }, - { - "system": "https://nahdo.org/sopt", - "version": "9.2", - "code": "349", - "display": "Other" - }, - { - "system": "https://nahdo.org/sopt", - "version": "9.2", - "code": "35", - "display": "Black Lung" - }, - { - "system": "https://nahdo.org/sopt", - "version": "9.2", - "code": "36", - "display": "State Government" - }, - { - "system": "https://nahdo.org/sopt", - "version": "9.2", - "code": "361", - "display": "State SCHIP program (codes for individual states)" - }, - { - "system": "https://nahdo.org/sopt", - "version": "9.2", - "code": "362", - "display": "Specific state programs (list/ local code)" - }, - { - "system": "https://nahdo.org/sopt", - "version": "9.2", - "code": "369", - "display": "State, not otherwise specified (other state)" - }, - { - "system": "https://nahdo.org/sopt", - "version": "9.2", - "code": "37", - "display": "Local Government" - }, - { - "system": "https://nahdo.org/sopt", - "version": "9.2", - "code": "371", - "display": "Local - Managed care" - }, - { - "system": "https://nahdo.org/sopt", - "version": "9.2", - "code": "3711", - "display": "HMO" - }, - { - "system": "https://nahdo.org/sopt", - "version": "9.2", - "code": "3712", - "display": "PPO" - }, - { - "system": "https://nahdo.org/sopt", - "version": "9.2", - "code": "3713", - "display": "POS" - }, - { - "system": "https://nahdo.org/sopt", - "version": "9.2", - "code": "372", - "display": "FFS/Indemnity" - }, - { - "system": "https://nahdo.org/sopt", - "version": "9.2", - "code": "379", - "display": "Local, not otherwise specified (other local, county)" - }, - { - "system": "https://nahdo.org/sopt", - "version": "9.2", - "code": "38", - "display": "Other Government (Federal, State, Local not specified)" - }, - { - "system": "https://nahdo.org/sopt", - "version": "9.2", - "code": "381", - "display": "Federal, State, Local not specified managed care" - }, - { - "system": "https://nahdo.org/sopt", - "version": "9.2", - "code": "3811", - "display": "Federal, State, Local not specified - HMO" - }, - { - "system": "https://nahdo.org/sopt", - "version": "9.2", - "code": "3812", - "display": "Federal, State, Local not specified - PPO" - }, - { - "system": "https://nahdo.org/sopt", - "version": "9.2", - "code": "3813", - "display": "Federal, State, Local not specified - POS" - }, - { - "system": "https://nahdo.org/sopt", - "version": "9.2", - "code": "3819", - "display": "Federal, State, Local not specified - not specified managed care" - }, - { - "system": "https://nahdo.org/sopt", - "version": "9.2", - "code": "382", - "display": "Federal, State, Local not specified - FFS" - }, - { - "system": "https://nahdo.org/sopt", - "version": "9.2", - "code": "389", - "display": "Federal, State, Local not specified - Other" - }, - { - "system": "https://nahdo.org/sopt", - "version": "9.2", - "code": "39", - "display": "Other Federal" - }, - { - "system": "https://nahdo.org/sopt", - "version": "9.2", - "code": "391", - "display": "Federal Employee Health Plan - Use when known" - }, - { - "system": "https://nahdo.org/sopt", - "version": "9.2", - "code": "4", - "display": "DEPARTMENTS OF CORRECTIONS" - }, - { - "system": "https://nahdo.org/sopt", - "version": "9.2", - "code": "41", - "display": "Corrections Federal" - }, - { - "system": "https://nahdo.org/sopt", - "version": "9.2", - "code": "42", - "display": "Corrections State" - }, - { - "system": "https://nahdo.org/sopt", - "version": "9.2", - "code": "43", - "display": "Corrections Local" - }, - { - "system": "https://nahdo.org/sopt", - "version": "9.2", - "code": "44", - "display": "Corrections Unknown Level" - }, - { - "system": "https://nahdo.org/sopt", - "version": "9.2", - "code": "5", - "display": "PRIVATE HEALTH INSURANCE" - }, - { - "system": "https://nahdo.org/sopt", - "version": "9.2", - "code": "51", - "display": "Managed Care (Private)" - }, - { - "system": "https://nahdo.org/sopt", - "version": "9.2", - "code": "511", - "display": "Commercial Managed Care - HMO" - }, - { - "system": "https://nahdo.org/sopt", - "version": "9.2", - "code": "512", - "display": "Commercial Managed Care - PPO" - }, - { - "system": "https://nahdo.org/sopt", - "version": "9.2", - "code": "513", - "display": "Commercial Managed Care - POS" - }, - { - "system": "https://nahdo.org/sopt", - "version": "9.2", - "code": "514", - "display": "Exclusive Provider Organization" - }, - { - "system": "https://nahdo.org/sopt", - "version": "9.2", - "code": "515", - "display": "Gatekeeper PPO (GPPO)" - }, - { - "system": "https://nahdo.org/sopt", - "version": "9.2", - "code": "516", - "display": "Commercial Managed Care - Pharmacy Benefit Manager" - }, - { - "system": "https://nahdo.org/sopt", - "version": "9.2", - "code": "517", - "display": "Commercial Managed Care - Dental" - }, - { - "system": "https://nahdo.org/sopt", - "version": "9.2", - "code": "519", - "display": "Managed Care, Other (non HMO)" - }, - { - "system": "https://nahdo.org/sopt", - "version": "9.2", - "code": "52", - "display": "Private Health Insurance - Indemnity" - }, - { - "system": "https://nahdo.org/sopt", - "version": "9.2", - "code": "521", - "display": "Commercial Indemnity" - }, - { - "system": "https://nahdo.org/sopt", - "version": "9.2", - "code": "522", - "display": "Self-insured (ERISA) Administrative Services Only (ASO) plan" - }, - { - "system": "https://nahdo.org/sopt", - "version": "9.2", - "code": "523", - "display": "Medicare supplemental policy (as second payer)" - }, - { - "system": "https://nahdo.org/sopt", - "version": "9.2", - "code": "524", - "display": "Indemnity Insurance - Dental" - }, - { - "system": "https://nahdo.org/sopt", - "version": "9.2", - "code": "529", - "display": "Private health insurance--other commercial Indemnity" - }, - { - "system": "https://nahdo.org/sopt", - "version": "9.2", - "code": "53", - "display": "Managed Care (private) or private health insurance (indemnity), not otherwise specified" - }, - { - "system": "https://nahdo.org/sopt", - "version": "9.2", - "code": "54", - "display": "Organized Delivery System" - }, - { - "system": "https://nahdo.org/sopt", - "version": "9.2", - "code": "55", - "display": "Small Employer Purchasing Group" - }, - { - "system": "https://nahdo.org/sopt", - "version": "9.2", - "code": "56", - "display": "Specialized Stand-Alone Plan" - }, - { - "system": "https://nahdo.org/sopt", - "version": "9.2", - "code": "561", - "display": "Dental" - }, - { - "system": "https://nahdo.org/sopt", - "version": "9.2", - "code": "562", - "display": "Vision" - }, - { - "system": "https://nahdo.org/sopt", - "version": "9.2", - "code": "59", - "display": "Other Private Insurance" - }, - { - "system": "https://nahdo.org/sopt", - "version": "9.2", - "code": "6", - "display": "BLUE CROSS/BLUE SHIELD" - }, - { - "system": "https://nahdo.org/sopt", - "version": "9.2", - "code": "61", - "display": "BC Managed Care" - }, - { - "system": "https://nahdo.org/sopt", - "version": "9.2", - "code": "611", - "display": "BC Managed Care - HMO" - }, - { - "system": "https://nahdo.org/sopt", - "version": "9.2", - "code": "612", - "display": "BC Managed Care - PPO" - }, - { - "system": "https://nahdo.org/sopt", - "version": "9.2", - "code": "613", - "display": "BC Managed Care - POS" - }, - { - "system": "https://nahdo.org/sopt", - "version": "9.2", - "code": "614", - "display": "BC Managed Care - Dental" - }, - { - "system": "https://nahdo.org/sopt", - "version": "9.2", - "code": "619", - "display": "BC Managed Care - Other" - }, - { - "system": "https://nahdo.org/sopt", - "version": "9.2", - "code": "62", - "display": "BC Insurance Indemnity" - }, - { - "system": "https://nahdo.org/sopt", - "version": "9.2", - "code": "621", - "display": "BC Indemnity" - }, - { - "system": "https://nahdo.org/sopt", - "version": "9.2", - "code": "622", - "display": "BC Self-insured (ERISA) Administrative Services Only (ASO)Plan" - }, - { - "system": "https://nahdo.org/sopt", - "version": "9.2", - "code": "623", - "display": "BC Medicare Supplemental Plan" - }, - { - "system": "https://nahdo.org/sopt", - "version": "9.2", - "code": "629", - "display": "BC Indemnity - Dental" - }, - { - "system": "https://nahdo.org/sopt", - "version": "9.2", - "code": "7", - "display": "MANAGED CARE, UNSPECIFIED (to be used only if one can't distinguish public from private)" - }, - { - "system": "https://nahdo.org/sopt", - "version": "9.2", - "code": "71", - "display": "HMO" - }, - { - "system": "https://nahdo.org/sopt", - "version": "9.2", - "code": "72", - "display": "PPO" - }, - { - "system": "https://nahdo.org/sopt", - "version": "9.2", - "code": "73", - "display": "POS" - }, - { - "system": "https://nahdo.org/sopt", - "version": "9.2", - "code": "79", - "display": "Other Managed Care" - }, - { - "system": "https://nahdo.org/sopt", - "version": "9.2", - "code": "8", - "display": "NO PAYMENT from an Organization/Agency/Program/Private Payer Listed" - }, - { - "system": "https://nahdo.org/sopt", - "version": "9.2", - "code": "81", - "display": "Self-pay (Includes applicants for insurance and Medicaid applicants)" - }, - { - "system": "https://nahdo.org/sopt", - "version": "9.2", - "code": "82", - "display": "No Charge" - }, - { - "system": "https://nahdo.org/sopt", - "version": "9.2", - "code": "821", - "display": "Charity" - }, - { - "system": "https://nahdo.org/sopt", - "version": "9.2", - "code": "822", - "display": "Professional Courtesy" - }, - { - "system": "https://nahdo.org/sopt", - "version": "9.2", - "code": "823", - "display": "Research/Clinical Trial" - }, - { - "system": "https://nahdo.org/sopt", - "version": "9.2", - "code": "83", - "display": "Refusal to Pay/Bad Debt" - }, - { - "system": "https://nahdo.org/sopt", - "version": "9.2", - "code": "84", - "display": "Hill Burton Free Care" - }, - { - "system": "https://nahdo.org/sopt", - "version": "9.2", - "code": "85", - "display": "Research/Donor" - }, - { - "system": "https://nahdo.org/sopt", - "version": "9.2", - "code": "89", - "display": "No Payment, Other" - }, - { - "system": "https://nahdo.org/sopt", - "version": "9.2", - "code": "9", - "display": "MISCELLANEOUS/OTHER" - }, - { - "system": "https://nahdo.org/sopt", - "version": "9.2", - "code": "91", - "display": "Foreign National" - }, - { - "system": "https://nahdo.org/sopt", - "version": "9.2", - "code": "92", - "display": "Other (Non-government)" - }, - { - "system": "https://nahdo.org/sopt", - "version": "9.2", - "code": "93", - "display": "Disability Insurance" - }, - { - "system": "https://nahdo.org/sopt", - "version": "9.2", - "code": "94", - "display": "Long-term Care Insurance" - }, - { - "system": "https://nahdo.org/sopt", - "version": "9.2", - "code": "95", - "display": "Worker's Compensation" - }, - { - "system": "https://nahdo.org/sopt", - "version": "9.2", - "code": "951", - "display": "Worker's Comp HMO" - }, - { - "system": "https://nahdo.org/sopt", - "version": "9.2", - "code": "953", - "display": "Worker's Comp Fee-for-Service" - }, - { - "system": "https://nahdo.org/sopt", - "version": "9.2", - "code": "954", - "display": "Worker's Comp Other Managed Care" - }, - { - "system": "https://nahdo.org/sopt", - "version": "9.2", - "code": "959", - "display": "Worker's Comp, Other unspecified" - }, - { - "system": "https://nahdo.org/sopt", - "version": "9.2", - "code": "96", - "display": "Auto Insurance (includes no fault)" - }, - { - "system": "https://nahdo.org/sopt", - "version": "9.2", - "code": "97", - "display": "Legal Liability / Liability Insurance" - }, - { - "system": "https://nahdo.org/sopt", - "version": "9.2", - "code": "98", - "display": "Other specified but not otherwise classifiable (includes Hospice - Unspecified plan)" - }, - { - "system": "https://nahdo.org/sopt", - "version": "9.2", - "code": "99", - "display": "No Typology Code available for payment source" - }, - { - "system": "https://nahdo.org/sopt", - "version": "9.2", - "code": "9999", - "display": "Unavailable / No Payer Specified / Blank" - } - ] - } - }, - "request": { - "method": "PUT", - "url": "ValueSet/2.16.840.1.114222.4.11.3591" - } - }, - { - "resource": { - "resourceType": "Condition", - "id": "CMSTest-patient-1-condition-1", - "meta": { - "profile": [ - "http://hl7.org/fhir/us/qicore/StructureDefinition/qicore-condition" - ] - }, - "clinicalStatus": { - "coding": [ - { - "system": "http://terminology.hl7.org/CodeSystem/condition-clinical", - "version": "0.5.0", - "code": "active", - "display": "Active" - } - ] - }, - "verificationStatus": { - "coding": [ - { - "system": "http://terminology.hl7.org/CodeSystem/condition-ver-status", - "version": "0.5.0", - "code": "confirmed", - "display": "Confirmed" - } - ] - }, - "category": [ - { - "coding": [ - { - "system": "http://terminology.hl7.org/ValueSet/condition-category", - "version": "0.5.0", - "code": "encounter-diagnosis", - "display": "Encounter Diagnosis" - } - ] - } - ], - "code": { - "coding": [ - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2021", - "code": "O30.833", - "display": "Other specified multiple gestation, number of chorions and amnions are both equal to the number of fetuses, third trimester" - } - ] - }, - "subject": { - "reference": "Patient/CMSTest-patient-1" - }, - "onsetDateTime": "2023-01-05T10:00:00-07:00" - }, - "request": { - "method": "PUT", - "url": "Condition/CMSTest-patient-1-condition-1" - } - }, - { - "resource": { - "resourceType": "ValueSet", - "id": "2.16.840.1.114222.4.11.836", - "url": "http://cts.nlm.nih.gov/fhir/ValueSet/2.16.840.1.114222.4.11.836", - "identifier": [ - { - "system": "urn:ietf:rfc:3986", - "value": "2.16.840.1.114222.4.11.836" - } - ], - "version": "20121025", - "name": "Race", - "title": "Race", - "status": "active", - "experimental": false, - "publisher": "NLM", - "description": "Codes representing possible values for Race.", - "expansion": { - "identifier": "20210506", - "timestamp": "2021-08-19T13:27:33-06:00", - "contains": [ - { - "system": "http://terminology.hl7.org/CodeSystem/PHRaceAndEthnicityCDC", - "version": "1.2", - "code": "1002-5", - "display": "American Indian or Alaska Native" - }, - { - "system": "http://terminology.hl7.org/CodeSystem/PHRaceAndEthnicityCDC", - "version": "1.2", - "code": "2028-9", - "display": "Asian" - }, - { - "system": "http://terminology.hl7.org/CodeSystem/PHRaceAndEthnicityCDC", - "version": "1.2", - "code": "2054-5", - "display": "Black or African American" - }, - { - "system": "http://terminology.hl7.org/CodeSystem/PHRaceAndEthnicityCDC", - "version": "1.2", - "code": "2076-8", - "display": "Native Hawaiian or Other Pacific Islander" - }, - { - "system": "http://terminology.hl7.org/CodeSystem/PHRaceAndEthnicityCDC", - "version": "1.2", - "code": "2106-3", - "display": "White" - }, - { - "system": "http://terminology.hl7.org/CodeSystem/PHRaceAndEthnicityCDC", - "version": "1.2", - "code": "2131-1", - "display": "Other Race" - } - ] - } - }, - "request": { - "method": "PUT", - "url": "ValueSet/2.16.840.1.114222.4.11.836" - } - }, - { - "resource": { - "resourceType": "Measure", - "id": "CMSTest", - "meta": { - "profile": [ - "http://hl7.org/fhir/us/cqfmeasures/StructureDefinition/computable-measure-cqfm" - ] - }, - "contained": [ - { - "resourceType": "Library", - "id": "effective-data-requirements", - "status": "active", - "type": { - "coding": [ - { - "system": "http://terminology.hl7.org/CodeSystem/library-type", - "code": "module-definition" - } - ] - }, - "relatedArtifact": [ - { - "type": "depends-on", - "display": "Library SDE", - "resource": "http://content.alphora.com/fhir/dqm/Library/SDE" - }, - { - "type": "depends-on", - "display": "Library FHIRHelpers", - "resource": "http://content.alphora.com/fhir/dqm/Library/DQMFHIRHelpers" - } - ], - "parameter": [ - { - "name": "Measurement Period", - "use": "in", - "min": 0, - "max": "1", - "type": "Period" - }, - { - "name": "SDE Sex", - "use": "out", - "min": 0, - "max": "1", - "type": "Coding" - }, - { - "name": "Numerator", - "use": "out", - "min": 0, - "max": "1", - "type": "boolean" - }, - { - "name": "Denominator", - "use": "out", - "min": 0, - "max": "1", - "type": "boolean" - }, - { - "name": "Initial Population", - "use": "out", - "min": 0, - "max": "1", - "type": "boolean" - } - ], - "dataRequirement": [ - { - "type": "Patient", - "profile": [ - "http://hl7.org/fhir/StructureDefinition/Patient" - ] - }, - { - "type": "Patient", - "profile": [ - "http://hl7.org/fhir/StructureDefinition/Patient" - ] - } - ] - } - ], - "extension": [ - { - "url": "http://hl7.org/fhir/us/cqfmeasures/StructureDefinition/cqfm-softwaresystem", - "valueReference": { - "reference": "Device/cqf-tooling" - } - }, - { - "id": "effective-data-requirements", - "url": "http://hl7.org/fhir/us/cqfmeasures/StructureDefinition/cqfm-effectiveDataRequirements", - "valueReference": { - "reference": "#effective-data-requirements" - } - } - ], - "url": "http://content.alphora.com/fhir/dqm/Measure/CMSTest", - "name": "CMSTest", - "title": "Measure - CMSTest: test", - "status": "active", - "experimental": true, - "date": "2023-06-21T16:16:00-07:00", - "publisher": "Alphora", - "contact": [ - { - "telecom": [ - { - "system": "url", - "value": "https://alphora.com" - } - ] - } - ], - "description": "Percentage of visits for patients aged 18 years and older for which the eligible professional or eligible clinician attests to documenting a list of current medications using all immediate resources available on the date of the encounter", - "useContext": [ - { - "code": { - "system": "http://terminology.hl7.org/CodeSystem/usage-context-type", - "version": "4.0.1", - "code": "program", - "display": "Program" - }, - "valueCodeableConcept": { - "text": "eligible-provider" - } - } - ], - "jurisdiction": [ - { - "coding": [ - { - "system": "urn:iso:std:iso:3166", - "version": "4.0.1", - "code": "US", - "display": "United States of America" - } - ] - } - ], - "purpose": "Percentage of visits for patients aged 18 years and older for which the eligible professional or eligible clinician attests to documenting a list of current medications using all immediate resources available on the date of the encounter", - "effectivePeriod": { - "start": "2020-01-01T00:00:00-07:00", - "end": "2020-12-31T23:59:59-07:00" - }, - "topic": [ - { - "coding": [ - { - "system": "http://loinc.org", - "code": "57024-2", - "display": "Health Quality Measure Document" - } - ] - } - ], - "library": [ - "http://content.alphora.com/fhir/dqm/Library/CMSTest" - ], - "scoring": { - "coding": [ - { - "system": "http://terminology.hl7.org/CodeSystem/measure-scoring", - "version": "4.0.1", - "code": "proportion", - "display": "Proportion" - } - ] - }, - "type": [ - { - "coding": [ - { - "system": "http://terminology.hl7.org/CodeSystem/measure-type", - "version": "4.2.0", - "code": "process", - "display": "Process" - } - ] - } - ], - "rationale": "test.", - "clinicalRecommendationStatement": "test", - "improvementNotation": { - "coding": [ - { - "system": "http://terminology.hl7.org/CodeSystem/measure-improvement-notation", - "version": "0.1.0", - "code": "increase", - "display": "Increased score indicates improvement" - } - ] - }, - "group": [ - { - "id": "group-1", - "population": [ - { - "code": { - "coding": [ - { - "system": "http://terminology.hl7.org/CodeSystem/measure-population", - "code": "initial-population", - "display": "Initial Population" - } - ] - }, - "criteria": { - "language": "text/cql.identifier", - "expression": "Initial Population" - } - }, - { - "code": { - "coding": [ - { - "system": "http://terminology.hl7.org/CodeSystem/measure-population", - "code": "denominator", - "display": "Denominator" - } - ] - }, - "criteria": { - "language": "text/cql.identifier", - "expression": "Denominator" - } - }, - { - "code": { - "coding": [ - { - "system": "http://terminology.hl7.org/CodeSystem/measure-population", - "code": "numerator", - "display": "Numerator" - } - ] - }, - "criteria": { - "language": "text/cql.identifier", - "expression": "Numerator" - } - } - ] - } - ], - "supplementalData": [ - { - "id": "sde-sex", - "code": { - "text": "sde-sex" - }, - "usage": [ - { - "coding": [ - { - "system": "http://terminology.hl7.org/CodeSystem/measure-data-usage", - "code": "supplemental-data" - } - ] - } - ], - "criteria": { - "language": "text/cql.identifier", - "expression": "SDE Sex" - } - } - ] - }, - "request": { - "method": "PUT", - "url": "Measure/CMSTest" - } - }, - { - "resource": { - "resourceType": "Library", - "id": "CMSTest", - "extension": [ - { - "url": "http://hl7.org/fhir/us/cqfmeasures/StructureDefinition/cqfm-softwaresystem", - "valueReference": { - "reference": "Device/cqf-tooling" - } - } - ], - "url": "http://content.alphora.com/fhir/dqm/Library/CMSTest", - "name": "CMSTest", - "relatedArtifact": [ - { - "type": "depends-on", - "display": "FHIR model information", - "resource": "http://fhir.org/guides/cqf/common/Library/FHIR-ModelInfo|4.0.1" - }, - { - "type": "depends-on", - "display": "Library FHIRHelpers", - "resource": "http://content.alphora.com/fhir/dqm/Library/FHIRHelpers|4.0.1" - }, - { - "type": "depends-on", - "display": "Library FC", - "resource": "http://content.alphora.com/fhir/dqm/Library/FHIRCommon|4.0.1" - }, - { - "type": "depends-on", - "display": "Library SDE", - "resource": "http://content.alphora.com/fhir/dqm/Library/SDE" - }, - { - "type": "depends-on", - "display": "Library FHIRHelpers", - "resource": "http://content.alphora.com/fhir/dqm/Library/DQMFHIRHelpers" - }, - { - "type": "depends-on", - "display": "Code system ConditionClinicalStatusCodes", - "resource": "http://terminology.hl7.org/CodeSystem/condition-clinical" - }, - { - "type": "depends-on", - "display": "Code system ConditionVerificationStatusCodes", - "resource": "http://terminology.hl7.org/CodeSystem/condition-ver-status" - }, - { - "type": "depends-on", - "display": "Value set Pregnancy", - "resource": "http://cts.nlm.nih.gov/fhir/ValueSet/2.16.840.1.113883.3.526.3.378" - } - ], - "parameter": [ - { - "name": "Measurement Period", - "use": "in", - "min": 0, - "max": "1", - "type": "Period" - }, - { - "name": "Patient", - "use": "out", - "min": 0, - "max": "1", - "type": "Patient" - }, - { - "name": "SDE Sex", - "use": "out", - "min": 0, - "max": "1", - "type": "Coding" - }, - { - "name": "Initial Population", - "use": "out", - "min": 0, - "max": "1", - "type": "boolean" - }, - { - "name": "Denominator", - "use": "out", - "min": 0, - "max": "1", - "type": "boolean" - }, - { - "name": "Pregnant Patient", - "use": "out", - "min": 0, - "max": "*", - "type": "Condition" - }, - { - "name": "Numerator", - "use": "out", - "min": 0, - "max": "1", - "type": "boolean" - } - ], - "dataRequirement": [ - { - "type": "Patient", - "profile": [ - "http://hl7.org/fhir/StructureDefinition/Patient" - ] - }, - { - "type": "Patient", - "profile": [ - "http://hl7.org/fhir/StructureDefinition/Patient" - ] - }, - { - "type": "Condition", - "profile": [ - "http://hl7.org/fhir/StructureDefinition/Condition" - ], - "mustSupport": [ - "code" - ], - "codeFilter": [ - { - "path": "code", - "valueSet": "http://cts.nlm.nih.gov/fhir/ValueSet/2.16.840.1.113883.3.526.3.378" - } - ] - }, - { - "type": "Condition", - "profile": [ - "http://hl7.org/fhir/StructureDefinition/Condition" - ], - "mustSupport": [ - "code" - ], - "codeFilter": [ - { - "path": "code", - "valueSet": "http://cts.nlm.nih.gov/fhir/ValueSet/2.16.840.1.113883.3.526.3.378" - } - ] - }, - { - "type": "Condition", - "profile": [ - "http://hl7.org/fhir/StructureDefinition/Condition" - ], - "mustSupport": [ - "code" - ], - "codeFilter": [ - { - "path": "code", - "valueSet": "http://cts.nlm.nih.gov/fhir/ValueSet/2.16.840.1.113883.3.526.3.378" - } - ] - } - ], - "content": [ - { - "contentType": "text/cql", - "data": "bGlicmFyeSBDTVNUZXN0Cgp1c2luZyBGSElSIHZlcnNpb24gJzQuMC4xJwppbmNsdWRlIEZISVJIZWxwZXJzIHZlcnNpb24gJzQuMC4xJyBjYWxsZWQgRkhJUkhlbHBlcnMKaW5jbHVkZSBGSElSQ29tbW9uIHZlcnNpb24gJzQuMC4xJyBjYWxsZWQgRkMKCmluY2x1ZGUgU0RFIGNhbGxlZCBTREUKdmFsdWVzZXQgIlByZWduYW5jeSI6ICdodHRwOi8vY3RzLm5sbS5uaWguZ292L2ZoaXIvVmFsdWVTZXQvMi4xNi44NDAuMS4xMTM4ODMuMy41MjYuMy4zNzgnCgpwYXJhbWV0ZXIgIk1lYXN1cmVtZW50IFBlcmlvZCIgZGVmYXVsdCBJbnRlcnZhbFtAMjAyMy0wMS0wMVQwMDowMDowMC4wMDAsIEAyMDIzLTEyLTMxVDAwOjAwOjAwLjAwMCkKCmNvbnRleHQgUGF0aWVudAoKZGVmaW5lICJTREUgU2V4IjoKICBTREUuIlNERSBTZXgiCgpkZWZpbmUgIkluaXRpYWwgUG9wdWxhdGlvbiI6CiAgQWdlSW5ZZWFyc0F0KHN0YXJ0IG9mICJNZWFzdXJlbWVudCBQZXJpb2QiKSA+IDE4CiAgICAgICAgICAgIApkZWZpbmUgZnVuY3Rpb24gUXVhbGlmaWVkQ29uZGl0aW9ucyh2YWx1ZSBMaXN0PEZISVIuQ29uZGl0aW9uPik6CiAgdmFsdWUgQ29uZGl0aW9uCiAgICB3aGVyZSAoCiAgICAgIEZISVJIZWxwZXJzLlRvQ29uY2VwdChDb25kaXRpb24uY2xpbmljYWxTdGF0dXMpIH4gRkMuImFjdGl2ZSIKICAgICkKICAgIGFuZCAoCiAgICAgIEZISVJIZWxwZXJzLlRvQ29uY2VwdChDb25kaXRpb24udmVyaWZpY2F0aW9uU3RhdHVzKSB+IEZDLiJjb25maXJtZWQiCiAgICApCgpkZWZpbmUgIkRlbm9taW5hdG9yIjoKICAgQWdlSW5ZZWFyc0F0KHN0YXJ0IG9mICJNZWFzdXJlbWVudCBQZXJpb2QiKSA8IDk5CgoKZGVmaW5lICJOdW1lcmF0b3IiOgogIGV4aXN0cyAiUHJlZ25hbnQgUGF0aWVudCIKCmRlZmluZSAiUHJlZ25hbnQgUGF0aWVudCI6ICAgCiAgKFF1YWxpZmllZENvbmRpdGlvbnMoW0NvbmRpdGlvbjogIlByZWduYW5jeSJdKSkgUHJlZ25hbmN5CiB3aGVyZSBGQy5Ub1ByZXZhbGVuY2VJbnRlcnZhbChQcmVnbmFuY3kpIG92ZXJsYXBzICJNZWFzdXJlbWVudCBQZXJpb2QiICAgICAgICAgCiAgCgoKCg==" - }, - { - "contentType": "application/elm+xml", - "data": "PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0iVVRGLTgiPz4KPGxpYnJhcnkgeG1sbnM9InVybjpobDctb3JnOmVsbTpyMSIgeG1sbnM6dD0idXJuOmhsNy1vcmc6ZWxtLXR5cGVzOnIxIiB4bWxuczp4c2k9Imh0dHA6Ly93d3cudzMub3JnLzIwMDEvWE1MU2NoZW1hLWluc3RhbmNlIiB4bWxuczp4c2Q9Imh0dHA6Ly93d3cudzMub3JnLzIwMDEvWE1MU2NoZW1hIiB4bWxuczpmaGlyPSJodHRwOi8vaGw3Lm9yZy9maGlyIiB4bWxuczpxZG00Mz0idXJuOmhlYWx0aGl0LWdvdjpxZG06djRfMyIgeG1sbnM6cWRtNTM9InVybjpoZWFsdGhpdC1nb3Y6cWRtOnY1XzMiIHhtbG5zOmE9InVybjpobDctb3JnOmNxbC1hbm5vdGF0aW9uczpyMSI+CiAgIDxhbm5vdGF0aW9uIHRyYW5zbGF0b3JWZXJzaW9uPSIyLjcuMCIgdHJhbnNsYXRvck9wdGlvbnM9IkVuYWJsZUxvY2F0b3JzLERpc2FibGVMaXN0RGVtb3Rpb24sRGlzYWJsZUxpc3RQcm9tb3Rpb24iIHhzaTp0eXBlPSJhOkNxbFRvRWxtSW5mbyIvPgogICA8aWRlbnRpZmllciBpZD0iQ01TVGVzdCIgc3lzdGVtPSJodHRwOi8vY29udGVudC5hbHBob3JhLmNvbS9maGlyL2RxbSIvPgogICA8c2NoZW1hSWRlbnRpZmllciBpZD0idXJuOmhsNy1vcmc6ZWxtIiB2ZXJzaW9uPSJyMSIvPgogICA8dXNpbmdzPgogICAgICA8ZGVmIGxvY2FsSWRlbnRpZmllcj0iU3lzdGVtIiB1cmk9InVybjpobDctb3JnOmVsbS10eXBlczpyMSIvPgogICAgICA8ZGVmIGxvY2F0b3I9IjM6MS0zOjI2IiBsb2NhbElkZW50aWZpZXI9IkZISVIiIHVyaT0iaHR0cDovL2hsNy5vcmcvZmhpciIgdmVyc2lvbj0iNC4wLjEiLz4KICAgPC91c2luZ3M+CiAgIDxpbmNsdWRlcz4KICAgICAgPGRlZiBsb2NhdG9yPSI0OjEtNDo1NCIgbG9jYWxJZGVudGlmaWVyPSJGSElSSGVscGVycyIgcGF0aD0iaHR0cDovL2NvbnRlbnQuYWxwaG9yYS5jb20vZmhpci9kcW0vRkhJUkhlbHBlcnMiIHZlcnNpb249IjQuMC4xIi8+CiAgICAgIDxkZWYgbG9jYXRvcj0iNToxLTU6NDQiIGxvY2FsSWRlbnRpZmllcj0iRkMiIHBhdGg9Imh0dHA6Ly9jb250ZW50LmFscGhvcmEuY29tL2ZoaXIvZHFtL0ZISVJDb21tb24iIHZlcnNpb249IjQuMC4xIi8+CiAgICAgIDxkZWYgbG9jYXRvcj0iNzoxLTc6MjIiIGxvY2FsSWRlbnRpZmllcj0iU0RFIiBwYXRoPSJodHRwOi8vY29udGVudC5hbHBob3JhLmNvbS9maGlyL2RxbS9TREUiLz4KICAgPC9pbmNsdWRlcz4KICAgPHBhcmFtZXRlcnM+CiAgICAgIDxkZWYgbG9jYXRvcj0iMTA6MS0xMDo5OSIgbmFtZT0iTWVhc3VyZW1lbnQgUGVyaW9kIiBhY2Nlc3NMZXZlbD0iUHVibGljIj4KICAgICAgICAgPGRlZmF1bHQgbG9jYXRvcj0iMTA6NDAtMTA6OTkiIGxvd0Nsb3NlZD0idHJ1ZSIgaGlnaENsb3NlZD0iZmFsc2UiIHhzaTp0eXBlPSJJbnRlcnZhbCI+CiAgICAgICAgICAgIDxsb3cgbG9jYXRvcj0iMTA6NDktMTA6NzIiIHhzaTp0eXBlPSJEYXRlVGltZSI+CiAgICAgICAgICAgICAgIDx5ZWFyIHZhbHVlVHlwZT0idDpJbnRlZ2VyIiB2YWx1ZT0iMjAyMyIgeHNpOnR5cGU9IkxpdGVyYWwiLz4KICAgICAgICAgICAgICAgPG1vbnRoIHZhbHVlVHlwZT0idDpJbnRlZ2VyIiB2YWx1ZT0iMSIgeHNpOnR5cGU9IkxpdGVyYWwiLz4KICAgICAgICAgICAgICAgPGRheSB2YWx1ZVR5cGU9InQ6SW50ZWdlciIgdmFsdWU9IjEiIHhzaTp0eXBlPSJMaXRlcmFsIi8+CiAgICAgICAgICAgICAgIDxob3VyIHZhbHVlVHlwZT0idDpJbnRlZ2VyIiB2YWx1ZT0iMCIgeHNpOnR5cGU9IkxpdGVyYWwiLz4KICAgICAgICAgICAgICAgPG1pbnV0ZSB2YWx1ZVR5cGU9InQ6SW50ZWdlciIgdmFsdWU9IjAiIHhzaTp0eXBlPSJMaXRlcmFsIi8+CiAgICAgICAgICAgICAgIDxzZWNvbmQgdmFsdWVUeXBlPSJ0OkludGVnZXIiIHZhbHVlPSIwIiB4c2k6dHlwZT0iTGl0ZXJhbCIvPgogICAgICAgICAgICAgICA8bWlsbGlzZWNvbmQgdmFsdWVUeXBlPSJ0OkludGVnZXIiIHZhbHVlPSIwIiB4c2k6dHlwZT0iTGl0ZXJhbCIvPgogICAgICAgICAgICA8L2xvdz4KICAgICAgICAgICAgPGhpZ2ggbG9jYXRvcj0iMTA6NzUtMTA6OTgiIHhzaTp0eXBlPSJEYXRlVGltZSI+CiAgICAgICAgICAgICAgIDx5ZWFyIHZhbHVlVHlwZT0idDpJbnRlZ2VyIiB2YWx1ZT0iMjAyMyIgeHNpOnR5cGU9IkxpdGVyYWwiLz4KICAgICAgICAgICAgICAgPG1vbnRoIHZhbHVlVHlwZT0idDpJbnRlZ2VyIiB2YWx1ZT0iMTIiIHhzaTp0eXBlPSJMaXRlcmFsIi8+CiAgICAgICAgICAgICAgIDxkYXkgdmFsdWVUeXBlPSJ0OkludGVnZXIiIHZhbHVlPSIzMSIgeHNpOnR5cGU9IkxpdGVyYWwiLz4KICAgICAgICAgICAgICAgPGhvdXIgdmFsdWVUeXBlPSJ0OkludGVnZXIiIHZhbHVlPSIwIiB4c2k6dHlwZT0iTGl0ZXJhbCIvPgogICAgICAgICAgICAgICA8bWludXRlIHZhbHVlVHlwZT0idDpJbnRlZ2VyIiB2YWx1ZT0iMCIgeHNpOnR5cGU9IkxpdGVyYWwiLz4KICAgICAgICAgICAgICAgPHNlY29uZCB2YWx1ZVR5cGU9InQ6SW50ZWdlciIgdmFsdWU9IjAiIHhzaTp0eXBlPSJMaXRlcmFsIi8+CiAgICAgICAgICAgICAgIDxtaWxsaXNlY29uZCB2YWx1ZVR5cGU9InQ6SW50ZWdlciIgdmFsdWU9IjAiIHhzaTp0eXBlPSJMaXRlcmFsIi8+CiAgICAgICAgICAgIDwvaGlnaD4KICAgICAgICAgPC9kZWZhdWx0PgogICAgICA8L2RlZj4KICAgPC9wYXJhbWV0ZXJzPgogICA8dmFsdWVTZXRzPgogICAgICA8ZGVmIGxvY2F0b3I9Ijg6MS04OjkwIiBuYW1lPSJQcmVnbmFuY3kiIGlkPSJodHRwOi8vY3RzLm5sbS5uaWguZ292L2ZoaXIvVmFsdWVTZXQvMi4xNi44NDAuMS4xMTM4ODMuMy41MjYuMy4zNzgiIGFjY2Vzc0xldmVsPSJQdWJsaWMiLz4KICAgPC92YWx1ZVNldHM+CiAgIDxjb250ZXh0cz4KICAgICAgPGRlZiBsb2NhdG9yPSIxMjoxLTEyOjE1IiBuYW1lPSJQYXRpZW50Ii8+CiAgIDwvY29udGV4dHM+CiAgIDxzdGF0ZW1lbnRzPgogICAgICA8ZGVmIGxvY2F0b3I9IjEyOjEtMTI6MTUiIG5hbWU9IlBhdGllbnQiIGNvbnRleHQ9IlBhdGllbnQiPgogICAgICAgICA8ZXhwcmVzc2lvbiB4c2k6dHlwZT0iU2luZ2xldG9uRnJvbSI+CiAgICAgICAgICAgIDxvcGVyYW5kIGxvY2F0b3I9IjEyOjEtMTI6MTUiIGRhdGFUeXBlPSJmaGlyOlBhdGllbnQiIHRlbXBsYXRlSWQ9Imh0dHA6Ly9obDcub3JnL2ZoaXIvU3RydWN0dXJlRGVmaW5pdGlvbi9QYXRpZW50IiB4c2k6dHlwZT0iUmV0cmlldmUiLz4KICAgICAgICAgPC9leHByZXNzaW9uPgogICAgICA8L2RlZj4KICAgICAgPGRlZiBsb2NhdG9yPSIxNDoxLTE1OjE1IiBuYW1lPSJTREUgU2V4IiBjb250ZXh0PSJQYXRpZW50IiBhY2Nlc3NMZXZlbD0iUHVibGljIj4KICAgICAgICAgPGV4cHJlc3Npb24gbG9jYXRvcj0iMTU6My0xNToxNSIgbmFtZT0iU0RFIFNleCIgbGlicmFyeU5hbWU9IlNERSIgeHNpOnR5cGU9IkV4cHJlc3Npb25SZWYiLz4KICAgICAgPC9kZWY+CiAgICAgIDxkZWYgbG9jYXRvcj0iMTc6MS0xODo1MCIgbmFtZT0iSW5pdGlhbCBQb3B1bGF0aW9uIiBjb250ZXh0PSJQYXRpZW50IiBhY2Nlc3NMZXZlbD0iUHVibGljIj4KICAgICAgICAgPGV4cHJlc3Npb24gbG9jYXRvcj0iMTg6My0xODo1MCIgeHNpOnR5cGU9IkdyZWF0ZXIiPgogICAgICAgICAgICA8b3BlcmFuZCBsb2NhdG9yPSIxODozLTE4OjQ1IiBwcmVjaXNpb249IlllYXIiIHhzaTp0eXBlPSJDYWxjdWxhdGVBZ2VBdCI+CiAgICAgICAgICAgICAgIDxvcGVyYW5kIHhzaTp0eXBlPSJUb0RhdGVUaW1lIj4KICAgICAgICAgICAgICAgICAgPG9wZXJhbmQgcGF0aD0iYmlydGhEYXRlLnZhbHVlIiB4c2k6dHlwZT0iUHJvcGVydHkiPgogICAgICAgICAgICAgICAgICAgICA8c291cmNlIG5hbWU9IlBhdGllbnQiIHhzaTp0eXBlPSJFeHByZXNzaW9uUmVmIi8+CiAgICAgICAgICAgICAgICAgIDwvb3BlcmFuZD4KICAgICAgICAgICAgICAgPC9vcGVyYW5kPgogICAgICAgICAgICAgICA8b3BlcmFuZCBsb2NhdG9yPSIxODoxNi0xODo0NCIgeHNpOnR5cGU9IlN0YXJ0Ij4KICAgICAgICAgICAgICAgICAgPG9wZXJhbmQgbG9jYXRvcj0iMTg6MjUtMTg6NDQiIG5hbWU9Ik1lYXN1cmVtZW50IFBlcmlvZCIgeHNpOnR5cGU9IlBhcmFtZXRlclJlZiIvPgogICAgICAgICAgICAgICA8L29wZXJhbmQ+CiAgICAgICAgICAgIDwvb3BlcmFuZD4KICAgICAgICAgICAgPG9wZXJhbmQgbG9jYXRvcj0iMTg6NDktMTg6NTAiIHZhbHVlVHlwZT0idDpJbnRlZ2VyIiB2YWx1ZT0iMTgiIHhzaTp0eXBlPSJMaXRlcmFsIi8+CiAgICAgICAgIDwvZXhwcmVzc2lvbj4KICAgICAgPC9kZWY+CiAgICAgIDxkZWYgbG9jYXRvcj0iMjA6MS0yNzo1IiBuYW1lPSJRdWFsaWZpZWRDb25kaXRpb25zIiBjb250ZXh0PSJQYXRpZW50IiBhY2Nlc3NMZXZlbD0iUHVibGljIiB4c2k6dHlwZT0iRnVuY3Rpb25EZWYiPgogICAgICAgICA8ZXhwcmVzc2lvbiBsb2NhdG9yPSIyMTozLTI3OjUiIHhzaTp0eXBlPSJRdWVyeSI+CiAgICAgICAgICAgIDxzb3VyY2UgbG9jYXRvcj0iMjE6My0yMToxNyIgYWxpYXM9IkNvbmRpdGlvbiI+CiAgICAgICAgICAgICAgIDxleHByZXNzaW9uIGxvY2F0b3I9IjIxOjMtMjE6NyIgbmFtZT0idmFsdWUiIHhzaTp0eXBlPSJPcGVyYW5kUmVmIi8+CiAgICAgICAgICAgIDwvc291cmNlPgogICAgICAgICAgICA8d2hlcmUgbG9jYXRvcj0iMjI6NS0yNzo1IiB4c2k6dHlwZT0iQW5kIj4KICAgICAgICAgICAgICAgPG9wZXJhbmQgbG9jYXRvcj0iMjI6MTEtMjQ6NSIgeHNpOnR5cGU9IkVxdWl2YWxlbnQiPgogICAgICAgICAgICAgICAgICA8b3BlcmFuZCBsb2NhdG9yPSIyMzo3LTIzOjUzIiBuYW1lPSJUb0NvbmNlcHQiIGxpYnJhcnlOYW1lPSJGSElSSGVscGVycyIgeHNpOnR5cGU9IkZ1bmN0aW9uUmVmIj4KICAgICAgICAgICAgICAgICAgICAgPG9wZXJhbmQgbG9jYXRvcj0iMjM6MjktMjM6NTIiIHBhdGg9ImNsaW5pY2FsU3RhdHVzIiBzY29wZT0iQ29uZGl0aW9uIiB4c2k6dHlwZT0iUHJvcGVydHkiLz4KICAgICAgICAgICAgICAgICAgPC9vcGVyYW5kPgogICAgICAgICAgICAgICAgICA8b3BlcmFuZCB4c2k6dHlwZT0iVG9Db25jZXB0Ij4KICAgICAgICAgICAgICAgICAgICAgPG9wZXJhbmQgbG9jYXRvcj0iMjM6NTctMjM6NjciIG5hbWU9ImFjdGl2ZSIgbGlicmFyeU5hbWU9IkZDIiB4c2k6dHlwZT0iQ29kZVJlZiIvPgogICAgICAgICAgICAgICAgICA8L29wZXJhbmQ+CiAgICAgICAgICAgICAgIDwvb3BlcmFuZD4KICAgICAgICAgICAgICAgPG9wZXJhbmQgbG9jYXRvcj0iMjU6OS0yNzo1IiB4c2k6dHlwZT0iRXF1aXZhbGVudCI+CiAgICAgICAgICAgICAgICAgIDxvcGVyYW5kIGxvY2F0b3I9IjI2OjctMjY6NTciIG5hbWU9IlRvQ29uY2VwdCIgbGlicmFyeU5hbWU9IkZISVJIZWxwZXJzIiB4c2k6dHlwZT0iRnVuY3Rpb25SZWYiPgogICAgICAgICAgICAgICAgICAgICA8b3BlcmFuZCBsb2NhdG9yPSIyNjoyOS0yNjo1NiIgcGF0aD0idmVyaWZpY2F0aW9uU3RhdHVzIiBzY29wZT0iQ29uZGl0aW9uIiB4c2k6dHlwZT0iUHJvcGVydHkiLz4KICAgICAgICAgICAgICAgICAgPC9vcGVyYW5kPgogICAgICAgICAgICAgICAgICA8b3BlcmFuZCB4c2k6dHlwZT0iVG9Db25jZXB0Ij4KICAgICAgICAgICAgICAgICAgICAgPG9wZXJhbmQgbG9jYXRvcj0iMjY6NjEtMjY6NzQiIG5hbWU9ImNvbmZpcm1lZCIgbGlicmFyeU5hbWU9IkZDIiB4c2k6dHlwZT0iQ29kZVJlZiIvPgogICAgICAgICAgICAgICAgICA8L29wZXJhbmQ+CiAgICAgICAgICAgICAgIDwvb3BlcmFuZD4KICAgICAgICAgICAgPC93aGVyZT4KICAgICAgICAgPC9leHByZXNzaW9uPgogICAgICAgICA8b3BlcmFuZCBuYW1lPSJ2YWx1ZSI+CiAgICAgICAgICAgIDxvcGVyYW5kVHlwZVNwZWNpZmllciBsb2NhdG9yPSIyMDo0My0yMDo2MiIgeHNpOnR5cGU9Ikxpc3RUeXBlU3BlY2lmaWVyIj4KICAgICAgICAgICAgICAgPGVsZW1lbnRUeXBlIGxvY2F0b3I9IjIwOjQ4LTIwOjYxIiBuYW1lPSJmaGlyOkNvbmRpdGlvbiIgeHNpOnR5cGU9Ik5hbWVkVHlwZVNwZWNpZmllciIvPgogICAgICAgICAgICA8L29wZXJhbmRUeXBlU3BlY2lmaWVyPgogICAgICAgICA8L29wZXJhbmQ+CiAgICAgIDwvZGVmPgogICAgICA8ZGVmIGxvY2F0b3I9IjI5OjEtMzA6NTEiIG5hbWU9IkRlbm9taW5hdG9yIiBjb250ZXh0PSJQYXRpZW50IiBhY2Nlc3NMZXZlbD0iUHVibGljIj4KICAgICAgICAgPGV4cHJlc3Npb24gbG9jYXRvcj0iMzA6NC0zMDo1MSIgeHNpOnR5cGU9Ikxlc3MiPgogICAgICAgICAgICA8b3BlcmFuZCBsb2NhdG9yPSIzMDo0LTMwOjQ2IiBwcmVjaXNpb249IlllYXIiIHhzaTp0eXBlPSJDYWxjdWxhdGVBZ2VBdCI+CiAgICAgICAgICAgICAgIDxvcGVyYW5kIHhzaTp0eXBlPSJUb0RhdGVUaW1lIj4KICAgICAgICAgICAgICAgICAgPG9wZXJhbmQgcGF0aD0iYmlydGhEYXRlLnZhbHVlIiB4c2k6dHlwZT0iUHJvcGVydHkiPgogICAgICAgICAgICAgICAgICAgICA8c291cmNlIG5hbWU9IlBhdGllbnQiIHhzaTp0eXBlPSJFeHByZXNzaW9uUmVmIi8+CiAgICAgICAgICAgICAgICAgIDwvb3BlcmFuZD4KICAgICAgICAgICAgICAgPC9vcGVyYW5kPgogICAgICAgICAgICAgICA8b3BlcmFuZCBsb2NhdG9yPSIzMDoxNy0zMDo0NSIgeHNpOnR5cGU9IlN0YXJ0Ij4KICAgICAgICAgICAgICAgICAgPG9wZXJhbmQgbG9jYXRvcj0iMzA6MjYtMzA6NDUiIG5hbWU9Ik1lYXN1cmVtZW50IFBlcmlvZCIgeHNpOnR5cGU9IlBhcmFtZXRlclJlZiIvPgogICAgICAgICAgICAgICA8L29wZXJhbmQ+CiAgICAgICAgICAgIDwvb3BlcmFuZD4KICAgICAgICAgICAgPG9wZXJhbmQgbG9jYXRvcj0iMzA6NTAtMzA6NTEiIHZhbHVlVHlwZT0idDpJbnRlZ2VyIiB2YWx1ZT0iOTkiIHhzaTp0eXBlPSJMaXRlcmFsIi8+CiAgICAgICAgIDwvZXhwcmVzc2lvbj4KICAgICAgPC9kZWY+CiAgICAgIDxkZWYgbG9jYXRvcj0iMzY6MS0zODo3MSIgbmFtZT0iUHJlZ25hbnQgUGF0aWVudCIgY29udGV4dD0iUGF0aWVudCIgYWNjZXNzTGV2ZWw9IlB1YmxpYyI+CiAgICAgICAgIDxleHByZXNzaW9uIGxvY2F0b3I9IjM3OjMtMzg6NzEiIHhzaTp0eXBlPSJRdWVyeSI+CiAgICAgICAgICAgIDxzb3VyY2UgbG9jYXRvcj0iMzc6My0zNzo1OSIgYWxpYXM9IlByZWduYW5jeSI+CiAgICAgICAgICAgICAgIDxleHByZXNzaW9uIGxvY2F0b3I9IjM3OjMtMzc6NDkiIG5hbWU9IlF1YWxpZmllZENvbmRpdGlvbnMiIHhzaTp0eXBlPSJGdW5jdGlvblJlZiI+CiAgICAgICAgICAgICAgICAgIDxvcGVyYW5kIGxvY2F0b3I9IjM3OjI0LTM3OjQ3IiBkYXRhVHlwZT0iZmhpcjpDb25kaXRpb24iIHRlbXBsYXRlSWQ9Imh0dHA6Ly9obDcub3JnL2ZoaXIvU3RydWN0dXJlRGVmaW5pdGlvbi9Db25kaXRpb24iIGNvZGVQcm9wZXJ0eT0iY29kZSIgY29kZUNvbXBhcmF0b3I9ImluIiB4c2k6dHlwZT0iUmV0cmlldmUiPgogICAgICAgICAgICAgICAgICAgICA8Y29kZXMgbG9jYXRvcj0iMzc6MzYtMzc6NDYiIG5hbWU9IlByZWduYW5jeSIgcHJlc2VydmU9InRydWUiIHhzaTp0eXBlPSJWYWx1ZVNldFJlZiIvPgogICAgICAgICAgICAgICAgICA8L29wZXJhbmQ+CiAgICAgICAgICAgICAgIDwvZXhwcmVzc2lvbj4KICAgICAgICAgICAgPC9zb3VyY2U+CiAgICAgICAgICAgIDx3aGVyZSBsb2NhdG9yPSIzODoyLTM4OjcxIiB4c2k6dHlwZT0iT3ZlcmxhcHMiPgogICAgICAgICAgICAgICA8b3BlcmFuZCBsb2NhdG9yPSIzODo4LTM4OjQxIiBuYW1lPSJUb1ByZXZhbGVuY2VJbnRlcnZhbCIgbGlicmFyeU5hbWU9IkZDIiB4c2k6dHlwZT0iRnVuY3Rpb25SZWYiPgogICAgICAgICAgICAgICAgICA8b3BlcmFuZCBsb2NhdG9yPSIzODozMi0zODo0MCIgbmFtZT0iUHJlZ25hbmN5IiB4c2k6dHlwZT0iQWxpYXNSZWYiLz4KICAgICAgICAgICAgICAgPC9vcGVyYW5kPgogICAgICAgICAgICAgICA8b3BlcmFuZCBsb2NhdG9yPSIzODo1Mi0zODo3MSIgbmFtZT0iTWVhc3VyZW1lbnQgUGVyaW9kIiB4c2k6dHlwZT0iUGFyYW1ldGVyUmVmIi8+CiAgICAgICAgICAgIDwvd2hlcmU+CiAgICAgICAgIDwvZXhwcmVzc2lvbj4KICAgICAgPC9kZWY+CiAgICAgIDxkZWYgbG9jYXRvcj0iMzM6MS0zNDoyNyIgbmFtZT0iTnVtZXJhdG9yIiBjb250ZXh0PSJQYXRpZW50IiBhY2Nlc3NMZXZlbD0iUHVibGljIj4KICAgICAgICAgPGV4cHJlc3Npb24gbG9jYXRvcj0iMzQ6My0zNDoyNyIgeHNpOnR5cGU9IkV4aXN0cyI+CiAgICAgICAgICAgIDxvcGVyYW5kIGxvY2F0b3I9IjM0OjEwLTM0OjI3IiBuYW1lPSJQcmVnbmFudCBQYXRpZW50IiB4c2k6dHlwZT0iRXhwcmVzc2lvblJlZiIvPgogICAgICAgICA8L2V4cHJlc3Npb24+CiAgICAgIDwvZGVmPgogICA8L3N0YXRlbWVudHM+CjwvbGlicmFyeT4K" - }, - { - "contentType": "application/elm+json", - "data": "ewogICAibGlicmFyeSIgOiB7CiAgICAgICJhbm5vdGF0aW9uIiA6IFsgewogICAgICAgICAidHJhbnNsYXRvclZlcnNpb24iIDogIjIuNy4wIiwKICAgICAgICAgInRyYW5zbGF0b3JPcHRpb25zIiA6ICJFbmFibGVMb2NhdG9ycyxEaXNhYmxlTGlzdERlbW90aW9uLERpc2FibGVMaXN0UHJvbW90aW9uIiwKICAgICAgICAgInR5cGUiIDogIkNxbFRvRWxtSW5mbyIKICAgICAgfSBdLAogICAgICAiaWRlbnRpZmllciIgOiB7CiAgICAgICAgICJpZCIgOiAiQ01TVGVzdCIsCiAgICAgICAgICJzeXN0ZW0iIDogImh0dHA6Ly9jb250ZW50LmFscGhvcmEuY29tL2ZoaXIvZHFtIgogICAgICB9LAogICAgICAic2NoZW1hSWRlbnRpZmllciIgOiB7CiAgICAgICAgICJpZCIgOiAidXJuOmhsNy1vcmc6ZWxtIiwKICAgICAgICAgInZlcnNpb24iIDogInIxIgogICAgICB9LAogICAgICAidXNpbmdzIiA6IHsKICAgICAgICAgImRlZiIgOiBbIHsKICAgICAgICAgICAgImxvY2FsSWRlbnRpZmllciIgOiAiU3lzdGVtIiwKICAgICAgICAgICAgInVyaSIgOiAidXJuOmhsNy1vcmc6ZWxtLXR5cGVzOnIxIgogICAgICAgICB9LCB7CiAgICAgICAgICAgICJsb2NhdG9yIiA6ICIzOjEtMzoyNiIsCiAgICAgICAgICAgICJsb2NhbElkZW50aWZpZXIiIDogIkZISVIiLAogICAgICAgICAgICAidXJpIiA6ICJodHRwOi8vaGw3Lm9yZy9maGlyIiwKICAgICAgICAgICAgInZlcnNpb24iIDogIjQuMC4xIgogICAgICAgICB9IF0KICAgICAgfSwKICAgICAgImluY2x1ZGVzIiA6IHsKICAgICAgICAgImRlZiIgOiBbIHsKICAgICAgICAgICAgImxvY2F0b3IiIDogIjQ6MS00OjU0IiwKICAgICAgICAgICAgImxvY2FsSWRlbnRpZmllciIgOiAiRkhJUkhlbHBlcnMiLAogICAgICAgICAgICAicGF0aCIgOiAiaHR0cDovL2NvbnRlbnQuYWxwaG9yYS5jb20vZmhpci9kcW0vRkhJUkhlbHBlcnMiLAogICAgICAgICAgICAidmVyc2lvbiIgOiAiNC4wLjEiCiAgICAgICAgIH0sIHsKICAgICAgICAgICAgImxvY2F0b3IiIDogIjU6MS01OjQ0IiwKICAgICAgICAgICAgImxvY2FsSWRlbnRpZmllciIgOiAiRkMiLAogICAgICAgICAgICAicGF0aCIgOiAiaHR0cDovL2NvbnRlbnQuYWxwaG9yYS5jb20vZmhpci9kcW0vRkhJUkNvbW1vbiIsCiAgICAgICAgICAgICJ2ZXJzaW9uIiA6ICI0LjAuMSIKICAgICAgICAgfSwgewogICAgICAgICAgICAibG9jYXRvciIgOiAiNzoxLTc6MjIiLAogICAgICAgICAgICAibG9jYWxJZGVudGlmaWVyIiA6ICJTREUiLAogICAgICAgICAgICAicGF0aCIgOiAiaHR0cDovL2NvbnRlbnQuYWxwaG9yYS5jb20vZmhpci9kcW0vU0RFIgogICAgICAgICB9IF0KICAgICAgfSwKICAgICAgInBhcmFtZXRlcnMiIDogewogICAgICAgICAiZGVmIiA6IFsgewogICAgICAgICAgICAibG9jYXRvciIgOiAiMTA6MS0xMDo5OSIsCiAgICAgICAgICAgICJuYW1lIiA6ICJNZWFzdXJlbWVudCBQZXJpb2QiLAogICAgICAgICAgICAiYWNjZXNzTGV2ZWwiIDogIlB1YmxpYyIsCiAgICAgICAgICAgICJkZWZhdWx0IiA6IHsKICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjEwOjQwLTEwOjk5IiwKICAgICAgICAgICAgICAgImxvd0Nsb3NlZCIgOiB0cnVlLAogICAgICAgICAgICAgICAiaGlnaENsb3NlZCIgOiBmYWxzZSwKICAgICAgICAgICAgICAgInR5cGUiIDogIkludGVydmFsIiwKICAgICAgICAgICAgICAgImxvdyIgOiB7CiAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIxMDo0OS0xMDo3MiIsCiAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJEYXRlVGltZSIsCiAgICAgICAgICAgICAgICAgICJ5ZWFyIiA6IHsKICAgICAgICAgICAgICAgICAgICAgInZhbHVlVHlwZSIgOiAie3VybjpobDctb3JnOmVsbS10eXBlczpyMX1JbnRlZ2VyIiwKICAgICAgICAgICAgICAgICAgICAgInZhbHVlIiA6ICIyMDIzIiwKICAgICAgICAgICAgICAgICAgICAgInR5cGUiIDogIkxpdGVyYWwiCiAgICAgICAgICAgICAgICAgIH0sCiAgICAgICAgICAgICAgICAgICJtb250aCIgOiB7CiAgICAgICAgICAgICAgICAgICAgICJ2YWx1ZVR5cGUiIDogInt1cm46aGw3LW9yZzplbG0tdHlwZXM6cjF9SW50ZWdlciIsCiAgICAgICAgICAgICAgICAgICAgICJ2YWx1ZSIgOiAiMSIsCiAgICAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJMaXRlcmFsIgogICAgICAgICAgICAgICAgICB9LAogICAgICAgICAgICAgICAgICAiZGF5IiA6IHsKICAgICAgICAgICAgICAgICAgICAgInZhbHVlVHlwZSIgOiAie3VybjpobDctb3JnOmVsbS10eXBlczpyMX1JbnRlZ2VyIiwKICAgICAgICAgICAgICAgICAgICAgInZhbHVlIiA6ICIxIiwKICAgICAgICAgICAgICAgICAgICAgInR5cGUiIDogIkxpdGVyYWwiCiAgICAgICAgICAgICAgICAgIH0sCiAgICAgICAgICAgICAgICAgICJob3VyIiA6IHsKICAgICAgICAgICAgICAgICAgICAgInZhbHVlVHlwZSIgOiAie3VybjpobDctb3JnOmVsbS10eXBlczpyMX1JbnRlZ2VyIiwKICAgICAgICAgICAgICAgICAgICAgInZhbHVlIiA6ICIwIiwKICAgICAgICAgICAgICAgICAgICAgInR5cGUiIDogIkxpdGVyYWwiCiAgICAgICAgICAgICAgICAgIH0sCiAgICAgICAgICAgICAgICAgICJtaW51dGUiIDogewogICAgICAgICAgICAgICAgICAgICAidmFsdWVUeXBlIiA6ICJ7dXJuOmhsNy1vcmc6ZWxtLXR5cGVzOnIxfUludGVnZXIiLAogICAgICAgICAgICAgICAgICAgICAidmFsdWUiIDogIjAiLAogICAgICAgICAgICAgICAgICAgICAidHlwZSIgOiAiTGl0ZXJhbCIKICAgICAgICAgICAgICAgICAgfSwKICAgICAgICAgICAgICAgICAgInNlY29uZCIgOiB7CiAgICAgICAgICAgICAgICAgICAgICJ2YWx1ZVR5cGUiIDogInt1cm46aGw3LW9yZzplbG0tdHlwZXM6cjF9SW50ZWdlciIsCiAgICAgICAgICAgICAgICAgICAgICJ2YWx1ZSIgOiAiMCIsCiAgICAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJMaXRlcmFsIgogICAgICAgICAgICAgICAgICB9LAogICAgICAgICAgICAgICAgICAibWlsbGlzZWNvbmQiIDogewogICAgICAgICAgICAgICAgICAgICAidmFsdWVUeXBlIiA6ICJ7dXJuOmhsNy1vcmc6ZWxtLXR5cGVzOnIxfUludGVnZXIiLAogICAgICAgICAgICAgICAgICAgICAidmFsdWUiIDogIjAiLAogICAgICAgICAgICAgICAgICAgICAidHlwZSIgOiAiTGl0ZXJhbCIKICAgICAgICAgICAgICAgICAgfQogICAgICAgICAgICAgICB9LAogICAgICAgICAgICAgICAiaGlnaCIgOiB7CiAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIxMDo3NS0xMDo5OCIsCiAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJEYXRlVGltZSIsCiAgICAgICAgICAgICAgICAgICJ5ZWFyIiA6IHsKICAgICAgICAgICAgICAgICAgICAgInZhbHVlVHlwZSIgOiAie3VybjpobDctb3JnOmVsbS10eXBlczpyMX1JbnRlZ2VyIiwKICAgICAgICAgICAgICAgICAgICAgInZhbHVlIiA6ICIyMDIzIiwKICAgICAgICAgICAgICAgICAgICAgInR5cGUiIDogIkxpdGVyYWwiCiAgICAgICAgICAgICAgICAgIH0sCiAgICAgICAgICAgICAgICAgICJtb250aCIgOiB7CiAgICAgICAgICAgICAgICAgICAgICJ2YWx1ZVR5cGUiIDogInt1cm46aGw3LW9yZzplbG0tdHlwZXM6cjF9SW50ZWdlciIsCiAgICAgICAgICAgICAgICAgICAgICJ2YWx1ZSIgOiAiMTIiLAogICAgICAgICAgICAgICAgICAgICAidHlwZSIgOiAiTGl0ZXJhbCIKICAgICAgICAgICAgICAgICAgfSwKICAgICAgICAgICAgICAgICAgImRheSIgOiB7CiAgICAgICAgICAgICAgICAgICAgICJ2YWx1ZVR5cGUiIDogInt1cm46aGw3LW9yZzplbG0tdHlwZXM6cjF9SW50ZWdlciIsCiAgICAgICAgICAgICAgICAgICAgICJ2YWx1ZSIgOiAiMzEiLAogICAgICAgICAgICAgICAgICAgICAidHlwZSIgOiAiTGl0ZXJhbCIKICAgICAgICAgICAgICAgICAgfSwKICAgICAgICAgICAgICAgICAgImhvdXIiIDogewogICAgICAgICAgICAgICAgICAgICAidmFsdWVUeXBlIiA6ICJ7dXJuOmhsNy1vcmc6ZWxtLXR5cGVzOnIxfUludGVnZXIiLAogICAgICAgICAgICAgICAgICAgICAidmFsdWUiIDogIjAiLAogICAgICAgICAgICAgICAgICAgICAidHlwZSIgOiAiTGl0ZXJhbCIKICAgICAgICAgICAgICAgICAgfSwKICAgICAgICAgICAgICAgICAgIm1pbnV0ZSIgOiB7CiAgICAgICAgICAgICAgICAgICAgICJ2YWx1ZVR5cGUiIDogInt1cm46aGw3LW9yZzplbG0tdHlwZXM6cjF9SW50ZWdlciIsCiAgICAgICAgICAgICAgICAgICAgICJ2YWx1ZSIgOiAiMCIsCiAgICAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJMaXRlcmFsIgogICAgICAgICAgICAgICAgICB9LAogICAgICAgICAgICAgICAgICAic2Vjb25kIiA6IHsKICAgICAgICAgICAgICAgICAgICAgInZhbHVlVHlwZSIgOiAie3VybjpobDctb3JnOmVsbS10eXBlczpyMX1JbnRlZ2VyIiwKICAgICAgICAgICAgICAgICAgICAgInZhbHVlIiA6ICIwIiwKICAgICAgICAgICAgICAgICAgICAgInR5cGUiIDogIkxpdGVyYWwiCiAgICAgICAgICAgICAgICAgIH0sCiAgICAgICAgICAgICAgICAgICJtaWxsaXNlY29uZCIgOiB7CiAgICAgICAgICAgICAgICAgICAgICJ2YWx1ZVR5cGUiIDogInt1cm46aGw3LW9yZzplbG0tdHlwZXM6cjF9SW50ZWdlciIsCiAgICAgICAgICAgICAgICAgICAgICJ2YWx1ZSIgOiAiMCIsCiAgICAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJMaXRlcmFsIgogICAgICAgICAgICAgICAgICB9CiAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgfQogICAgICAgICB9IF0KICAgICAgfSwKICAgICAgInZhbHVlU2V0cyIgOiB7CiAgICAgICAgICJkZWYiIDogWyB7CiAgICAgICAgICAgICJsb2NhdG9yIiA6ICI4OjEtODo5MCIsCiAgICAgICAgICAgICJuYW1lIiA6ICJQcmVnbmFuY3kiLAogICAgICAgICAgICAiaWQiIDogImh0dHA6Ly9jdHMubmxtLm5paC5nb3YvZmhpci9WYWx1ZVNldC8yLjE2Ljg0MC4xLjExMzg4My4zLjUyNi4zLjM3OCIsCiAgICAgICAgICAgICJhY2Nlc3NMZXZlbCIgOiAiUHVibGljIgogICAgICAgICB9IF0KICAgICAgfSwKICAgICAgImNvbnRleHRzIiA6IHsKICAgICAgICAgImRlZiIgOiBbIHsKICAgICAgICAgICAgImxvY2F0b3IiIDogIjEyOjEtMTI6MTUiLAogICAgICAgICAgICAibmFtZSIgOiAiUGF0aWVudCIKICAgICAgICAgfSBdCiAgICAgIH0sCiAgICAgICJzdGF0ZW1lbnRzIiA6IHsKICAgICAgICAgImRlZiIgOiBbIHsKICAgICAgICAgICAgImxvY2F0b3IiIDogIjEyOjEtMTI6MTUiLAogICAgICAgICAgICAibmFtZSIgOiAiUGF0aWVudCIsCiAgICAgICAgICAgICJjb250ZXh0IiA6ICJQYXRpZW50IiwKICAgICAgICAgICAgImV4cHJlc3Npb24iIDogewogICAgICAgICAgICAgICAidHlwZSIgOiAiU2luZ2xldG9uRnJvbSIsCiAgICAgICAgICAgICAgICJvcGVyYW5kIiA6IHsKICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjEyOjEtMTI6MTUiLAogICAgICAgICAgICAgICAgICAiZGF0YVR5cGUiIDogIntodHRwOi8vaGw3Lm9yZy9maGlyfVBhdGllbnQiLAogICAgICAgICAgICAgICAgICAidGVtcGxhdGVJZCIgOiAiaHR0cDovL2hsNy5vcmcvZmhpci9TdHJ1Y3R1cmVEZWZpbml0aW9uL1BhdGllbnQiLAogICAgICAgICAgICAgICAgICAidHlwZSIgOiAiUmV0cmlldmUiCiAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgfQogICAgICAgICB9LCB7CiAgICAgICAgICAgICJsb2NhdG9yIiA6ICIxNDoxLTE1OjE1IiwKICAgICAgICAgICAgIm5hbWUiIDogIlNERSBTZXgiLAogICAgICAgICAgICAiY29udGV4dCIgOiAiUGF0aWVudCIsCiAgICAgICAgICAgICJhY2Nlc3NMZXZlbCIgOiAiUHVibGljIiwKICAgICAgICAgICAgImV4cHJlc3Npb24iIDogewogICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMTU6My0xNToxNSIsCiAgICAgICAgICAgICAgICJuYW1lIiA6ICJTREUgU2V4IiwKICAgICAgICAgICAgICAgImxpYnJhcnlOYW1lIiA6ICJTREUiLAogICAgICAgICAgICAgICAidHlwZSIgOiAiRXhwcmVzc2lvblJlZiIKICAgICAgICAgICAgfQogICAgICAgICB9LCB7CiAgICAgICAgICAgICJsb2NhdG9yIiA6ICIxNzoxLTE4OjUwIiwKICAgICAgICAgICAgIm5hbWUiIDogIkluaXRpYWwgUG9wdWxhdGlvbiIsCiAgICAgICAgICAgICJjb250ZXh0IiA6ICJQYXRpZW50IiwKICAgICAgICAgICAgImFjY2Vzc0xldmVsIiA6ICJQdWJsaWMiLAogICAgICAgICAgICAiZXhwcmVzc2lvbiIgOiB7CiAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIxODozLTE4OjUwIiwKICAgICAgICAgICAgICAgInR5cGUiIDogIkdyZWF0ZXIiLAogICAgICAgICAgICAgICAib3BlcmFuZCIgOiBbIHsKICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjE4OjMtMTg6NDUiLAogICAgICAgICAgICAgICAgICAicHJlY2lzaW9uIiA6ICJZZWFyIiwKICAgICAgICAgICAgICAgICAgInR5cGUiIDogIkNhbGN1bGF0ZUFnZUF0IiwKICAgICAgICAgICAgICAgICAgIm9wZXJhbmQiIDogWyB7CiAgICAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJUb0RhdGVUaW1lIiwKICAgICAgICAgICAgICAgICAgICAgIm9wZXJhbmQiIDogewogICAgICAgICAgICAgICAgICAgICAgICAicGF0aCIgOiAiYmlydGhEYXRlLnZhbHVlIiwKICAgICAgICAgICAgICAgICAgICAgICAgInR5cGUiIDogIlByb3BlcnR5IiwKICAgICAgICAgICAgICAgICAgICAgICAgInNvdXJjZSIgOiB7CiAgICAgICAgICAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJQYXRpZW50IiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgInR5cGUiIDogIkV4cHJlc3Npb25SZWYiCiAgICAgICAgICAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgICAgICAgICAgfQogICAgICAgICAgICAgICAgICB9LCB7CiAgICAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIxODoxNi0xODo0NCIsCiAgICAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJTdGFydCIsCiAgICAgICAgICAgICAgICAgICAgICJvcGVyYW5kIiA6IHsKICAgICAgICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjE4OjI1LTE4OjQ0IiwKICAgICAgICAgICAgICAgICAgICAgICAgIm5hbWUiIDogIk1lYXN1cmVtZW50IFBlcmlvZCIsCiAgICAgICAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJQYXJhbWV0ZXJSZWYiCiAgICAgICAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgICAgICAgfSBdCiAgICAgICAgICAgICAgIH0sIHsKICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjE4OjQ5LTE4OjUwIiwKICAgICAgICAgICAgICAgICAgInZhbHVlVHlwZSIgOiAie3VybjpobDctb3JnOmVsbS10eXBlczpyMX1JbnRlZ2VyIiwKICAgICAgICAgICAgICAgICAgInZhbHVlIiA6ICIxOCIsCiAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJMaXRlcmFsIgogICAgICAgICAgICAgICB9IF0KICAgICAgICAgICAgfQogICAgICAgICB9LCB7CiAgICAgICAgICAgICJsb2NhdG9yIiA6ICIyMDoxLTI3OjUiLAogICAgICAgICAgICAibmFtZSIgOiAiUXVhbGlmaWVkQ29uZGl0aW9ucyIsCiAgICAgICAgICAgICJjb250ZXh0IiA6ICJQYXRpZW50IiwKICAgICAgICAgICAgImFjY2Vzc0xldmVsIiA6ICJQdWJsaWMiLAogICAgICAgICAgICAidHlwZSIgOiAiRnVuY3Rpb25EZWYiLAogICAgICAgICAgICAiZXhwcmVzc2lvbiIgOiB7CiAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIyMTozLTI3OjUiLAogICAgICAgICAgICAgICAidHlwZSIgOiAiUXVlcnkiLAogICAgICAgICAgICAgICAic291cmNlIiA6IFsgewogICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMjE6My0yMToxNyIsCiAgICAgICAgICAgICAgICAgICJhbGlhcyIgOiAiQ29uZGl0aW9uIiwKICAgICAgICAgICAgICAgICAgImV4cHJlc3Npb24iIDogewogICAgICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMjE6My0yMTo3IiwKICAgICAgICAgICAgICAgICAgICAgIm5hbWUiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk9wZXJhbmRSZWYiCiAgICAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgICAgfSBdLAogICAgICAgICAgICAgICAicmVsYXRpb25zaGlwIiA6IFsgXSwKICAgICAgICAgICAgICAgIndoZXJlIiA6IHsKICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjIyOjUtMjc6NSIsCiAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJBbmQiLAogICAgICAgICAgICAgICAgICAib3BlcmFuZCIgOiBbIHsKICAgICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjIyOjExLTI0OjUiLAogICAgICAgICAgICAgICAgICAgICAidHlwZSIgOiAiRXF1aXZhbGVudCIsCiAgICAgICAgICAgICAgICAgICAgICJvcGVyYW5kIiA6IFsgewogICAgICAgICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMjM6Ny0yMzo1MyIsCiAgICAgICAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJUb0NvbmNlcHQiLAogICAgICAgICAgICAgICAgICAgICAgICAibGlicmFyeU5hbWUiIDogIkZISVJIZWxwZXJzIiwKICAgICAgICAgICAgICAgICAgICAgICAgInR5cGUiIDogIkZ1bmN0aW9uUmVmIiwKICAgICAgICAgICAgICAgICAgICAgICAgIm9wZXJhbmQiIDogWyB7CiAgICAgICAgICAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIyMzoyOS0yMzo1MiIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICJwYXRoIiA6ICJjbGluaWNhbFN0YXR1cyIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICJzY29wZSIgOiAiQ29uZGl0aW9uIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgInR5cGUiIDogIlByb3BlcnR5IgogICAgICAgICAgICAgICAgICAgICAgICB9IF0KICAgICAgICAgICAgICAgICAgICAgfSwgewogICAgICAgICAgICAgICAgICAgICAgICAidHlwZSIgOiAiVG9Db25jZXB0IiwKICAgICAgICAgICAgICAgICAgICAgICAgIm9wZXJhbmQiIDogewogICAgICAgICAgICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMjM6NTctMjM6NjciLAogICAgICAgICAgICAgICAgICAgICAgICAgICAibmFtZSIgOiAiYWN0aXZlIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgImxpYnJhcnlOYW1lIiA6ICJGQyIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJDb2RlUmVmIgogICAgICAgICAgICAgICAgICAgICAgICB9CiAgICAgICAgICAgICAgICAgICAgIH0gXQogICAgICAgICAgICAgICAgICB9LCB7CiAgICAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIyNTo5LTI3OjUiLAogICAgICAgICAgICAgICAgICAgICAidHlwZSIgOiAiRXF1aXZhbGVudCIsCiAgICAgICAgICAgICAgICAgICAgICJvcGVyYW5kIiA6IFsgewogICAgICAgICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMjY6Ny0yNjo1NyIsCiAgICAgICAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJUb0NvbmNlcHQiLAogICAgICAgICAgICAgICAgICAgICAgICAibGlicmFyeU5hbWUiIDogIkZISVJIZWxwZXJzIiwKICAgICAgICAgICAgICAgICAgICAgICAgInR5cGUiIDogIkZ1bmN0aW9uUmVmIiwKICAgICAgICAgICAgICAgICAgICAgICAgIm9wZXJhbmQiIDogWyB7CiAgICAgICAgICAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIyNjoyOS0yNjo1NiIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICJwYXRoIiA6ICJ2ZXJpZmljYXRpb25TdGF0dXMiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAic2NvcGUiIDogIkNvbmRpdGlvbiIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJQcm9wZXJ0eSIKICAgICAgICAgICAgICAgICAgICAgICAgfSBdCiAgICAgICAgICAgICAgICAgICAgIH0sIHsKICAgICAgICAgICAgICAgICAgICAgICAgInR5cGUiIDogIlRvQ29uY2VwdCIsCiAgICAgICAgICAgICAgICAgICAgICAgICJvcGVyYW5kIiA6IHsKICAgICAgICAgICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjI2OjYxLTI2Ojc0IiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgIm5hbWUiIDogImNvbmZpcm1lZCIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICJsaWJyYXJ5TmFtZSIgOiAiRkMiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAidHlwZSIgOiAiQ29kZVJlZiIKICAgICAgICAgICAgICAgICAgICAgICAgfQogICAgICAgICAgICAgICAgICAgICB9IF0KICAgICAgICAgICAgICAgICAgfSBdCiAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgfSwKICAgICAgICAgICAgIm9wZXJhbmQiIDogWyB7CiAgICAgICAgICAgICAgICJuYW1lIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICJvcGVyYW5kVHlwZVNwZWNpZmllciIgOiB7CiAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIyMDo0My0yMDo2MiIsCiAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJMaXN0VHlwZVNwZWNpZmllciIsCiAgICAgICAgICAgICAgICAgICJlbGVtZW50VHlwZSIgOiB7CiAgICAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIyMDo0OC0yMDo2MSIsCiAgICAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJ7aHR0cDovL2hsNy5vcmcvZmhpcn1Db25kaXRpb24iLAogICAgICAgICAgICAgICAgICAgICAidHlwZSIgOiAiTmFtZWRUeXBlU3BlY2lmaWVyIgogICAgICAgICAgICAgICAgICB9CiAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgfSBdCiAgICAgICAgIH0sIHsKICAgICAgICAgICAgImxvY2F0b3IiIDogIjI5OjEtMzA6NTEiLAogICAgICAgICAgICAibmFtZSIgOiAiRGVub21pbmF0b3IiLAogICAgICAgICAgICAiY29udGV4dCIgOiAiUGF0aWVudCIsCiAgICAgICAgICAgICJhY2Nlc3NMZXZlbCIgOiAiUHVibGljIiwKICAgICAgICAgICAgImV4cHJlc3Npb24iIDogewogICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMzA6NC0zMDo1MSIsCiAgICAgICAgICAgICAgICJ0eXBlIiA6ICJMZXNzIiwKICAgICAgICAgICAgICAgIm9wZXJhbmQiIDogWyB7CiAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIzMDo0LTMwOjQ2IiwKICAgICAgICAgICAgICAgICAgInByZWNpc2lvbiIgOiAiWWVhciIsCiAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJDYWxjdWxhdGVBZ2VBdCIsCiAgICAgICAgICAgICAgICAgICJvcGVyYW5kIiA6IFsgewogICAgICAgICAgICAgICAgICAgICAidHlwZSIgOiAiVG9EYXRlVGltZSIsCiAgICAgICAgICAgICAgICAgICAgICJvcGVyYW5kIiA6IHsKICAgICAgICAgICAgICAgICAgICAgICAgInBhdGgiIDogImJpcnRoRGF0ZS52YWx1ZSIsCiAgICAgICAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJQcm9wZXJ0eSIsCiAgICAgICAgICAgICAgICAgICAgICAgICJzb3VyY2UiIDogewogICAgICAgICAgICAgICAgICAgICAgICAgICAibmFtZSIgOiAiUGF0aWVudCIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJFeHByZXNzaW9uUmVmIgogICAgICAgICAgICAgICAgICAgICAgICB9CiAgICAgICAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgICAgICAgfSwgewogICAgICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMzA6MTctMzA6NDUiLAogICAgICAgICAgICAgICAgICAgICAidHlwZSIgOiAiU3RhcnQiLAogICAgICAgICAgICAgICAgICAgICAib3BlcmFuZCIgOiB7CiAgICAgICAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIzMDoyNi0zMDo0NSIsCiAgICAgICAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJNZWFzdXJlbWVudCBQZXJpb2QiLAogICAgICAgICAgICAgICAgICAgICAgICAidHlwZSIgOiAiUGFyYW1ldGVyUmVmIgogICAgICAgICAgICAgICAgICAgICB9CiAgICAgICAgICAgICAgICAgIH0gXQogICAgICAgICAgICAgICB9LCB7CiAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIzMDo1MC0zMDo1MSIsCiAgICAgICAgICAgICAgICAgICJ2YWx1ZVR5cGUiIDogInt1cm46aGw3LW9yZzplbG0tdHlwZXM6cjF9SW50ZWdlciIsCiAgICAgICAgICAgICAgICAgICJ2YWx1ZSIgOiAiOTkiLAogICAgICAgICAgICAgICAgICAidHlwZSIgOiAiTGl0ZXJhbCIKICAgICAgICAgICAgICAgfSBdCiAgICAgICAgICAgIH0KICAgICAgICAgfSwgewogICAgICAgICAgICAibG9jYXRvciIgOiAiMzY6MS0zODo3MSIsCiAgICAgICAgICAgICJuYW1lIiA6ICJQcmVnbmFudCBQYXRpZW50IiwKICAgICAgICAgICAgImNvbnRleHQiIDogIlBhdGllbnQiLAogICAgICAgICAgICAiYWNjZXNzTGV2ZWwiIDogIlB1YmxpYyIsCiAgICAgICAgICAgICJleHByZXNzaW9uIiA6IHsKICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjM3OjMtMzg6NzEiLAogICAgICAgICAgICAgICAidHlwZSIgOiAiUXVlcnkiLAogICAgICAgICAgICAgICAic291cmNlIiA6IFsgewogICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMzc6My0zNzo1OSIsCiAgICAgICAgICAgICAgICAgICJhbGlhcyIgOiAiUHJlZ25hbmN5IiwKICAgICAgICAgICAgICAgICAgImV4cHJlc3Npb24iIDogewogICAgICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMzc6My0zNzo0OSIsCiAgICAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJRdWFsaWZpZWRDb25kaXRpb25zIiwKICAgICAgICAgICAgICAgICAgICAgInR5cGUiIDogIkZ1bmN0aW9uUmVmIiwKICAgICAgICAgICAgICAgICAgICAgIm9wZXJhbmQiIDogWyB7CiAgICAgICAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIzNzoyNC0zNzo0NyIsCiAgICAgICAgICAgICAgICAgICAgICAgICJkYXRhVHlwZSIgOiAie2h0dHA6Ly9obDcub3JnL2ZoaXJ9Q29uZGl0aW9uIiwKICAgICAgICAgICAgICAgICAgICAgICAgInRlbXBsYXRlSWQiIDogImh0dHA6Ly9obDcub3JnL2ZoaXIvU3RydWN0dXJlRGVmaW5pdGlvbi9Db25kaXRpb24iLAogICAgICAgICAgICAgICAgICAgICAgICAiY29kZVByb3BlcnR5IiA6ICJjb2RlIiwKICAgICAgICAgICAgICAgICAgICAgICAgImNvZGVDb21wYXJhdG9yIiA6ICJpbiIsCiAgICAgICAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJSZXRyaWV2ZSIsCiAgICAgICAgICAgICAgICAgICAgICAgICJjb2RlcyIgOiB7CiAgICAgICAgICAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIzNzozNi0zNzo0NiIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJQcmVnbmFuY3kiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAicHJlc2VydmUiIDogdHJ1ZSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgInR5cGUiIDogIlZhbHVlU2V0UmVmIgogICAgICAgICAgICAgICAgICAgICAgICB9CiAgICAgICAgICAgICAgICAgICAgIH0gXQogICAgICAgICAgICAgICAgICB9CiAgICAgICAgICAgICAgIH0gXSwKICAgICAgICAgICAgICAgInJlbGF0aW9uc2hpcCIgOiBbIF0sCiAgICAgICAgICAgICAgICJ3aGVyZSIgOiB7CiAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIzODoyLTM4OjcxIiwKICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk92ZXJsYXBzIiwKICAgICAgICAgICAgICAgICAgIm9wZXJhbmQiIDogWyB7CiAgICAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIzODo4LTM4OjQxIiwKICAgICAgICAgICAgICAgICAgICAgIm5hbWUiIDogIlRvUHJldmFsZW5jZUludGVydmFsIiwKICAgICAgICAgICAgICAgICAgICAgImxpYnJhcnlOYW1lIiA6ICJGQyIsCiAgICAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJGdW5jdGlvblJlZiIsCiAgICAgICAgICAgICAgICAgICAgICJvcGVyYW5kIiA6IFsgewogICAgICAgICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMzg6MzItMzg6NDAiLAogICAgICAgICAgICAgICAgICAgICAgICAibmFtZSIgOiAiUHJlZ25hbmN5IiwKICAgICAgICAgICAgICAgICAgICAgICAgInR5cGUiIDogIkFsaWFzUmVmIgogICAgICAgICAgICAgICAgICAgICB9IF0KICAgICAgICAgICAgICAgICAgfSwgewogICAgICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMzg6NTItMzg6NzEiLAogICAgICAgICAgICAgICAgICAgICAibmFtZSIgOiAiTWVhc3VyZW1lbnQgUGVyaW9kIiwKICAgICAgICAgICAgICAgICAgICAgInR5cGUiIDogIlBhcmFtZXRlclJlZiIKICAgICAgICAgICAgICAgICAgfSBdCiAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgfQogICAgICAgICB9LCB7CiAgICAgICAgICAgICJsb2NhdG9yIiA6ICIzMzoxLTM0OjI3IiwKICAgICAgICAgICAgIm5hbWUiIDogIk51bWVyYXRvciIsCiAgICAgICAgICAgICJjb250ZXh0IiA6ICJQYXRpZW50IiwKICAgICAgICAgICAgImFjY2Vzc0xldmVsIiA6ICJQdWJsaWMiLAogICAgICAgICAgICAiZXhwcmVzc2lvbiIgOiB7CiAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIzNDozLTM0OjI3IiwKICAgICAgICAgICAgICAgInR5cGUiIDogIkV4aXN0cyIsCiAgICAgICAgICAgICAgICJvcGVyYW5kIiA6IHsKICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjM0OjEwLTM0OjI3IiwKICAgICAgICAgICAgICAgICAgIm5hbWUiIDogIlByZWduYW50IFBhdGllbnQiLAogICAgICAgICAgICAgICAgICAidHlwZSIgOiAiRXhwcmVzc2lvblJlZiIKICAgICAgICAgICAgICAgfQogICAgICAgICAgICB9CiAgICAgICAgIH0gXQogICAgICB9CiAgIH0KfQ==" - } - ] - }, - "request": { - "method": "PUT", - "url": "Library/CMSTest" - } - }, - { - "resource": { - "resourceType": "Library", - "id": "FHIRHelpers", - "extension": [ - { - "url": "http://hl7.org/fhir/us/cqfmeasures/StructureDefinition/cqfm-softwaresystem", - "valueReference": { - "reference": "Device/cqf-tooling" - } - } - ], - "url": "http://content.alphora.com/fhir/dqm/Library/FHIRHelpers", - "version": "4.0.1", - "name": "FHIRHelpers", - "title": "Library - FHIR Helpers", - "type": { - "coding": [ - { - "system": "http://terminology.hl7.org/CodeSystem/library-type", - "code": "logic-library" - } - ] - }, - "description": "Logic library used in mapping CQL logic with the R4 FHIR Data Model.", - "jurisdiction": [ - { - "coding": [ - { - "system": "urn:iso:std:iso:3166", - "version": "4.0.1", - "code": "US", - "display": "United States of America" - } - ], - "text": "United States of America" - } - ], - "relatedArtifact": [ - { - "type": "depends-on", - "display": "FHIR model information", - "resource": "http://fhir.org/guides/cqf/common/Library/FHIR-ModelInfo|4.0.1" - } - ], - "content": [ - { - "contentType": "text/cql", - "data": "LyoKQGF1dGhvcjogQnJ5biBSaG9kZXMKQGRlc2NyaXB0aW9uOiBUaGlzIGxpYnJhcnkgZGVmaW5lcyBmdW5jdGlvbnMgdG8gY29udmVydCBiZXR3ZWVuIEZISVIKIGRhdGEgdHlwZXMgYW5kIENRTCBzeXN0ZW0tZGVmaW5lZCB0eXBlcywgYXMgd2VsbCBhcyBmdW5jdGlvbnMgdG8gc3VwcG9ydAogRkhJUlBhdGggaW1wbGVtZW50YXRpb24uIEZvciBtb3JlIGluZm9ybWF0aW9uLCBzZWUgdGhlIEZISVJIZWxwZXJzIHdpa2kgcGFnZToKIGh0dHBzOi8vZ2l0aHViLmNvbS9jcWZyYW1ld29yay9jbGluaWNhbF9xdWFsaXR5X2xhbmd1YWdlL3dpa2kvRkhJUkhlbHBlcnMKQGFsbG93Rmx1ZW50OiB0cnVlCiovCmxpYnJhcnkgRkhJUkhlbHBlcnMgdmVyc2lvbiAnNC4wLjEnCgp1c2luZyBGSElSIHZlcnNpb24gJzQuMC4xJwoKZGVmaW5lIGZ1bmN0aW9uIFRvSW50ZXJ2YWwocGVyaW9kIEZISVIuUGVyaW9kKToKICAgIGlmIHBlcmlvZCBpcyBudWxsIHRoZW4KICAgICAgICBudWxsCiAgICBlbHNlCiAgICAgICAgaWYgcGVyaW9kLiJzdGFydCIgaXMgbnVsbCB0aGVuCiAgICAgICAgICAgIEludGVydmFsKHBlcmlvZC4ic3RhcnQiLnZhbHVlLCBwZXJpb2QuImVuZCIudmFsdWVdCiAgICAgICAgZWxzZQogICAgICAgICAgICBJbnRlcnZhbFtwZXJpb2QuInN0YXJ0Ii52YWx1ZSwgcGVyaW9kLiJlbmQiLnZhbHVlXQoKZGVmaW5lIGZ1bmN0aW9uIFRvQ2FsZW5kYXJVbml0KHVuaXQgU3lzdGVtLlN0cmluZyk6CiAgICBjYXNlIHVuaXQKICAgICAgICB3aGVuICdtcycgdGhlbiAnbWlsbGlzZWNvbmQnCiAgICAgICAgd2hlbiAncycgdGhlbiAnc2Vjb25kJwogICAgICAgIHdoZW4gJ21pbicgdGhlbiAnbWludXRlJwogICAgICAgIHdoZW4gJ2gnIHRoZW4gJ2hvdXInCiAgICAgICAgd2hlbiAnZCcgdGhlbiAnZGF5JwogICAgICAgIHdoZW4gJ3drJyB0aGVuICd3ZWVrJwogICAgICAgIHdoZW4gJ21vJyB0aGVuICdtb250aCcKICAgICAgICB3aGVuICdhJyB0aGVuICd5ZWFyJwogICAgICAgIGVsc2UgdW5pdAogICAgZW5kCgpkZWZpbmUgZnVuY3Rpb24gVG9RdWFudGl0eShxdWFudGl0eSBGSElSLlF1YW50aXR5KToKICAgIGNhc2UKICAgICAgICB3aGVuIHF1YW50aXR5IGlzIG51bGwgdGhlbiBudWxsCiAgICAgICAgd2hlbiBxdWFudGl0eS52YWx1ZSBpcyBudWxsIHRoZW4gbnVsbAogICAgICAgIHdoZW4gcXVhbnRpdHkuY29tcGFyYXRvciBpcyBub3QgbnVsbCB0aGVuCiAgICAgICAgICAgIE1lc3NhZ2UobnVsbCwgdHJ1ZSwgJ0ZISVJIZWxwZXJzLlRvUXVhbnRpdHkuQ29tcGFyYXRvclF1YW50aXR5Tm90U3VwcG9ydGVkJywgJ0Vycm9yJywgJ0ZISVIgUXVhbnRpdHkgdmFsdWUgaGFzIGEgY29tcGFyYXRvciBhbmQgY2Fubm90IGJlIGNvbnZlcnRlZCB0byBhIFN5c3RlbS5RdWFudGl0eSB2YWx1ZS4nKQogICAgICAgIHdoZW4gcXVhbnRpdHkuc3lzdGVtIGlzIG51bGwgb3IgcXVhbnRpdHkuc3lzdGVtLnZhbHVlID0gJ2h0dHA6Ly91bml0c29mbWVhc3VyZS5vcmcnCiAgICAgICAgICAgICAgb3IgcXVhbnRpdHkuc3lzdGVtLnZhbHVlID0gJ2h0dHA6Ly9obDcub3JnL2ZoaXJwYXRoL0NvZGVTeXN0ZW0vY2FsZW5kYXItdW5pdHMnIHRoZW4KICAgICAgICAgICAgU3lzdGVtLlF1YW50aXR5IHsgdmFsdWU6IHF1YW50aXR5LnZhbHVlLnZhbHVlLCB1bml0OiBUb0NhbGVuZGFyVW5pdChDb2FsZXNjZShxdWFudGl0eS5jb2RlLnZhbHVlLCBxdWFudGl0eS51bml0LnZhbHVlLCAnMScpKSB9CiAgICAgICAgZWxzZQogICAgICAgICAgICBNZXNzYWdlKG51bGwsIHRydWUsICdGSElSSGVscGVycy5Ub1F1YW50aXR5LkludmFsaWRGSElSUXVhbnRpdHknLCAnRXJyb3InLCAnSW52YWxpZCBGSElSIFF1YW50aXR5IGNvZGU6ICcgJiBxdWFudGl0eS51bml0LnZhbHVlICYgJyAoJyAmIHF1YW50aXR5LnN5c3RlbS52YWx1ZSAmICd8JyAmIHF1YW50aXR5LmNvZGUudmFsdWUgJiAnKScpCiAgICBlbmQKCmRlZmluZSBmdW5jdGlvbiBUb1F1YW50aXR5SWdub3JpbmdDb21wYXJhdG9yKHF1YW50aXR5IEZISVIuUXVhbnRpdHkpOgogICAgY2FzZQogICAgICAgIHdoZW4gcXVhbnRpdHkgaXMgbnVsbCB0aGVuIG51bGwKICAgICAgICB3aGVuIHF1YW50aXR5LnZhbHVlIGlzIG51bGwgdGhlbiBudWxsCiAgICAgICAgd2hlbiBxdWFudGl0eS5zeXN0ZW0gaXMgbnVsbCBvciBxdWFudGl0eS5zeXN0ZW0udmFsdWUgPSAnaHR0cDovL3VuaXRzb2ZtZWFzdXJlLm9yZycKICAgICAgICAgICAgICBvciBxdWFudGl0eS5zeXN0ZW0udmFsdWUgPSAnaHR0cDovL2hsNy5vcmcvZmhpcnBhdGgvQ29kZVN5c3RlbS9jYWxlbmRhci11bml0cycgdGhlbgogICAgICAgICAgICBTeXN0ZW0uUXVhbnRpdHkgeyB2YWx1ZTogcXVhbnRpdHkudmFsdWUudmFsdWUsIHVuaXQ6IFRvQ2FsZW5kYXJVbml0KENvYWxlc2NlKHF1YW50aXR5LmNvZGUudmFsdWUsIHF1YW50aXR5LnVuaXQudmFsdWUsICcxJykpIH0KICAgICAgICBlbHNlCiAgICAgICAgICAgIE1lc3NhZ2UobnVsbCwgdHJ1ZSwgJ0ZISVJIZWxwZXJzLlRvUXVhbnRpdHkuSW52YWxpZEZISVJRdWFudGl0eScsICdFcnJvcicsICdJbnZhbGlkIEZISVIgUXVhbnRpdHkgY29kZTogJyAmIHF1YW50aXR5LnVuaXQudmFsdWUgJiAnICgnICYgcXVhbnRpdHkuc3lzdGVtLnZhbHVlICYgJ3wnICYgcXVhbnRpdHkuY29kZS52YWx1ZSAmICcpJykKICAgIGVuZAoKZGVmaW5lIGZ1bmN0aW9uIFRvSW50ZXJ2YWwocXVhbnRpdHkgRkhJUi5RdWFudGl0eSk6CiAgICBpZiBxdWFudGl0eSBpcyBudWxsIHRoZW4gbnVsbCBlbHNlCiAgICAgICAgY2FzZSBxdWFudGl0eS5jb21wYXJhdG9yLnZhbHVlCiAgICAgICAgICAgIHdoZW4gJzwnIHRoZW4KICAgICAgICAgICAgICAgIEludGVydmFsWwogICAgICAgICAgICAgICAgICAgIG51bGwsCiAgICAgICAgICAgICAgICAgICAgVG9RdWFudGl0eUlnbm9yaW5nQ29tcGFyYXRvcihxdWFudGl0eSkKICAgICAgICAgICAgICAgICkKICAgICAgICAgICAgd2hlbiAnPD0nIHRoZW4KICAgICAgICAgICAgICAgIEludGVydmFsWwogICAgICAgICAgICAgICAgICAgIG51bGwsCiAgICAgICAgICAgICAgICAgICAgVG9RdWFudGl0eUlnbm9yaW5nQ29tcGFyYXRvcihxdWFudGl0eSkKICAgICAgICAgICAgICAgIF0KICAgICAgICAgICAgd2hlbiAnPj0nIHRoZW4KICAgICAgICAgICAgICAgIEludGVydmFsWwogICAgICAgICAgICAgICAgICAgIFRvUXVhbnRpdHlJZ25vcmluZ0NvbXBhcmF0b3IocXVhbnRpdHkpLAogICAgICAgICAgICAgICAgICAgIG51bGwKICAgICAgICAgICAgICAgIF0KICAgICAgICAgICAgd2hlbiAnPicgdGhlbgogICAgICAgICAgICAgICAgSW50ZXJ2YWwoCiAgICAgICAgICAgICAgICAgICAgVG9RdWFudGl0eUlnbm9yaW5nQ29tcGFyYXRvcihxdWFudGl0eSksCiAgICAgICAgICAgICAgICAgICAgbnVsbAogICAgICAgICAgICAgICAgXQogICAgICAgICAgICBlbHNlCiAgICAgICAgICAgICAgICBJbnRlcnZhbFtUb1F1YW50aXR5KHF1YW50aXR5KSwgVG9RdWFudGl0eShxdWFudGl0eSldCiAgICAgICAgZW5kCgpkZWZpbmUgZnVuY3Rpb24gVG9SYXRpbyhyYXRpbyBGSElSLlJhdGlvKToKICAgIGlmIHJhdGlvIGlzIG51bGwgdGhlbgogICAgICAgIG51bGwKICAgIGVsc2UKICAgICAgICBTeXN0ZW0uUmF0aW8geyBudW1lcmF0b3I6IFRvUXVhbnRpdHkocmF0aW8ubnVtZXJhdG9yKSwgZGVub21pbmF0b3I6IFRvUXVhbnRpdHkocmF0aW8uZGVub21pbmF0b3IpIH0KCmRlZmluZSBmdW5jdGlvbiBUb0ludGVydmFsKHJhbmdlIEZISVIuUmFuZ2UpOgogICAgaWYgcmFuZ2UgaXMgbnVsbCB0aGVuCiAgICAgICAgbnVsbAogICAgZWxzZQogICAgICAgIEludGVydmFsW1RvUXVhbnRpdHkocmFuZ2UubG93KSwgVG9RdWFudGl0eShyYW5nZS5oaWdoKV0KCmRlZmluZSBmdW5jdGlvbiBUb0NvZGUoY29kaW5nIEZISVIuQ29kaW5nKToKICAgIGlmIGNvZGluZyBpcyBudWxsIHRoZW4KICAgICAgICBudWxsCiAgICBlbHNlCiAgICAgICAgU3lzdGVtLkNvZGUgewogICAgICAgICAgY29kZTogY29kaW5nLmNvZGUudmFsdWUsCiAgICAgICAgICBzeXN0ZW06IGNvZGluZy5zeXN0ZW0udmFsdWUsCiAgICAgICAgICB2ZXJzaW9uOiBjb2RpbmcudmVyc2lvbi52YWx1ZSwKICAgICAgICAgIGRpc3BsYXk6IGNvZGluZy5kaXNwbGF5LnZhbHVlCiAgICAgICAgfQoKZGVmaW5lIGZ1bmN0aW9uIFRvQ29uY2VwdChjb25jZXB0IEZISVIuQ29kZWFibGVDb25jZXB0KToKICAgIGlmIGNvbmNlcHQgaXMgbnVsbCB0aGVuCiAgICAgICAgbnVsbAogICAgZWxzZQogICAgICAgIFN5c3RlbS5Db25jZXB0IHsKICAgICAgICAgICAgY29kZXM6IGNvbmNlcHQuY29kaW5nIEMgcmV0dXJuIFRvQ29kZShDKSwKICAgICAgICAgICAgZGlzcGxheTogY29uY2VwdC50ZXh0LnZhbHVlCiAgICAgICAgfQoKZGVmaW5lIGZ1bmN0aW9uIHJlZmVyZW5jZShyZWZlcmVuY2UgU3RyaW5nKToKICAgIGlmIHJlZmVyZW5jZSBpcyBudWxsIHRoZW4KICAgICAgICBudWxsCiAgICBlbHNlCiAgICAgICAgUmVmZXJlbmNlIHsgcmVmZXJlbmNlOiBzdHJpbmcgeyB2YWx1ZTogcmVmZXJlbmNlIH0gfQoKZGVmaW5lIGZ1bmN0aW9uIHJlc29sdmUocmVmZXJlbmNlIFN0cmluZykgcmV0dXJucyBSZXNvdXJjZTogZXh0ZXJuYWwKZGVmaW5lIGZ1bmN0aW9uIHJlc29sdmUocmVmZXJlbmNlIFJlZmVyZW5jZSkgcmV0dXJucyBSZXNvdXJjZTogZXh0ZXJuYWwKZGVmaW5lIGZ1bmN0aW9uIHJlZmVyZW5jZShyZXNvdXJjZSBSZXNvdXJjZSkgcmV0dXJucyBSZWZlcmVuY2U6IGV4dGVybmFsCmRlZmluZSBmdW5jdGlvbiBleHRlbnNpb24oZWxlbWVudCBFbGVtZW50LCB1cmwgU3RyaW5nKSByZXR1cm5zIExpc3Q8RWxlbWVudD46IGV4dGVybmFsCmRlZmluZSBmdW5jdGlvbiBleHRlbnNpb24ocmVzb3VyY2UgUmVzb3VyY2UsIHVybCBTdHJpbmcpIHJldHVybnMgTGlzdDxFbGVtZW50PjogZXh0ZXJuYWwKZGVmaW5lIGZ1bmN0aW9uIGhhc1ZhbHVlKGVsZW1lbnQgRWxlbWVudCkgcmV0dXJucyBCb29sZWFuOiBleHRlcm5hbApkZWZpbmUgZnVuY3Rpb24gZ2V0VmFsdWUoZWxlbWVudCBFbGVtZW50KSByZXR1cm5zIEFueTogZXh0ZXJuYWwKZGVmaW5lIGZ1bmN0aW9uIG9mVHlwZShpZGVudGlmaWVyIFN0cmluZykgcmV0dXJucyBMaXN0PEFueT46IGV4dGVybmFsCmRlZmluZSBmdW5jdGlvbiBpcyhpZGVudGlmaWVyIFN0cmluZykgcmV0dXJucyBCb29sZWFuOiBleHRlcm5hbApkZWZpbmUgZnVuY3Rpb24gYXMoaWRlbnRpZmllciBTdHJpbmcpIHJldHVybnMgQW55OiBleHRlcm5hbApkZWZpbmUgZnVuY3Rpb24gZWxlbWVudERlZmluaXRpb24oZWxlbWVudCBFbGVtZW50KSByZXR1cm5zIEVsZW1lbnREZWZpbml0aW9uOiBleHRlcm5hbApkZWZpbmUgZnVuY3Rpb24gc2xpY2UoZWxlbWVudCBFbGVtZW50LCB1cmwgU3RyaW5nLCBuYW1lIFN0cmluZykgcmV0dXJucyBMaXN0PEVsZW1lbnQ+OiBleHRlcm5hbApkZWZpbmUgZnVuY3Rpb24gY2hlY2tNb2RpZmllcnMocmVzb3VyY2UgUmVzb3VyY2UpIHJldHVybnMgUmVzb3VyY2U6IGV4dGVybmFsCmRlZmluZSBmdW5jdGlvbiBjaGVja01vZGlmaWVycyhyZXNvdXJjZSBSZXNvdXJjZSwgbW9kaWZpZXIgU3RyaW5nKSByZXR1cm5zIFJlc291cmNlOiBleHRlcm5hbApkZWZpbmUgZnVuY3Rpb24gY2hlY2tNb2RpZmllcnMoZWxlbWVudCBFbGVtZW50KSByZXR1cm5zIEVsZW1lbnQ6IGV4dGVybmFsCmRlZmluZSBmdW5jdGlvbiBjaGVja01vZGlmaWVycyhlbGVtZW50IEVsZW1lbnQsIG1vZGlmaWVyIFN0cmluZykgcmV0dXJucyBFbGVtZW50OiBleHRlcm5hbApkZWZpbmUgZnVuY3Rpb24gY29uZm9ybXNUbyhyZXNvdXJjZSBSZXNvdXJjZSwgc3RydWN0dXJlIFN0cmluZykgcmV0dXJucyBCb29sZWFuOiBleHRlcm5hbApkZWZpbmUgZnVuY3Rpb24gbWVtYmVyT2YoY29kZSBjb2RlLCB2YWx1ZVNldCBTdHJpbmcpIHJldHVybnMgQm9vbGVhbjogZXh0ZXJuYWwKZGVmaW5lIGZ1bmN0aW9uIG1lbWJlck9mKGNvZGluZyBDb2RpbmcsIHZhbHVlU2V0IFN0cmluZykgcmV0dXJucyBCb29sZWFuOiBleHRlcm5hbApkZWZpbmUgZnVuY3Rpb24gbWVtYmVyT2YoY29uY2VwdCBDb2RlYWJsZUNvbmNlcHQsIHZhbHVlU2V0IFN0cmluZykgcmV0dXJucyBCb29sZWFuOiBleHRlcm5hbApkZWZpbmUgZnVuY3Rpb24gc3Vic3VtZXMoY29kaW5nIENvZGluZywgc3Vic3VtZWRDb2RpbmcgQ29kaW5nKSByZXR1cm5zIEJvb2xlYW46IGV4dGVybmFsCmRlZmluZSBmdW5jdGlvbiBzdWJzdW1lcyhjb25jZXB0IENvZGVhYmxlQ29uY2VwdCwgc3Vic3VtZWRDb25jZXB0IENvZGVhYmxlQ29uY2VwdCkgcmV0dXJucyBCb29sZWFuOiBleHRlcm5hbApkZWZpbmUgZnVuY3Rpb24gc3Vic3VtZWRCeShjb2RpbmcgQ29kaW5nLCBzdWJzdW1pbmdDb2RpbmcgQ29kaW5nKSByZXR1cm5zIEJvb2xlYW46IGV4dGVybmFsCmRlZmluZSBmdW5jdGlvbiBzdWJzdW1lZEJ5KGNvbmNlcHQgQ29kZWFibGVDb25jZXB0LCBzdWJzdW1pbmdDb25jZXB0IENvZGVhYmxlQ29uY2VwdCkgcmV0dXJucyBCb29sZWFuOiBleHRlcm5hbApkZWZpbmUgZnVuY3Rpb24gaHRtbENoZWNrcyhlbGVtZW50IEVsZW1lbnQpIHJldHVybnMgQm9vbGVhbjogZXh0ZXJuYWwKCmRlZmluZSBmdW5jdGlvbiBUb1N0cmluZyh2YWx1ZSBBY2NvdW50U3RhdHVzKTogdmFsdWUudmFsdWUKZGVmaW5lIGZ1bmN0aW9uIFRvU3RyaW5nKHZhbHVlIEFjdGlvbkNhcmRpbmFsaXR5QmVoYXZpb3IpOiB2YWx1ZS52YWx1ZQpkZWZpbmUgZnVuY3Rpb24gVG9TdHJpbmcodmFsdWUgQWN0aW9uQ29uZGl0aW9uS2luZCk6IHZhbHVlLnZhbHVlCmRlZmluZSBmdW5jdGlvbiBUb1N0cmluZyh2YWx1ZSBBY3Rpb25Hcm91cGluZ0JlaGF2aW9yKTogdmFsdWUudmFsdWUKZGVmaW5lIGZ1bmN0aW9uIFRvU3RyaW5nKHZhbHVlIEFjdGlvblBhcnRpY2lwYW50VHlwZSk6IHZhbHVlLnZhbHVlCmRlZmluZSBmdW5jdGlvbiBUb1N0cmluZyh2YWx1ZSBBY3Rpb25QcmVjaGVja0JlaGF2aW9yKTogdmFsdWUudmFsdWUKZGVmaW5lIGZ1bmN0aW9uIFRvU3RyaW5nKHZhbHVlIEFjdGlvblJlbGF0aW9uc2hpcFR5cGUpOiB2YWx1ZS52YWx1ZQpkZWZpbmUgZnVuY3Rpb24gVG9TdHJpbmcodmFsdWUgQWN0aW9uUmVxdWlyZWRCZWhhdmlvcik6IHZhbHVlLnZhbHVlCmRlZmluZSBmdW5jdGlvbiBUb1N0cmluZyh2YWx1ZSBBY3Rpb25TZWxlY3Rpb25CZWhhdmlvcik6IHZhbHVlLnZhbHVlCmRlZmluZSBmdW5jdGlvbiBUb1N0cmluZyh2YWx1ZSBBY3Rpdml0eURlZmluaXRpb25LaW5kKTogdmFsdWUudmFsdWUKZGVmaW5lIGZ1bmN0aW9uIFRvU3RyaW5nKHZhbHVlIEFjdGl2aXR5UGFydGljaXBhbnRUeXBlKTogdmFsdWUudmFsdWUKZGVmaW5lIGZ1bmN0aW9uIFRvU3RyaW5nKHZhbHVlIEFkZHJlc3NUeXBlKTogdmFsdWUudmFsdWUKZGVmaW5lIGZ1bmN0aW9uIFRvU3RyaW5nKHZhbHVlIEFkZHJlc3NVc2UpOiB2YWx1ZS52YWx1ZQpkZWZpbmUgZnVuY3Rpb24gVG9TdHJpbmcodmFsdWUgQWRtaW5pc3RyYXRpdmVHZW5kZXIpOiB2YWx1ZS52YWx1ZQpkZWZpbmUgZnVuY3Rpb24gVG9TdHJpbmcodmFsdWUgQWR2ZXJzZUV2ZW50QWN0dWFsaXR5KTogdmFsdWUudmFsdWUKZGVmaW5lIGZ1bmN0aW9uIFRvU3RyaW5nKHZhbHVlIEFnZ3JlZ2F0aW9uTW9kZSk6IHZhbHVlLnZhbHVlCmRlZmluZSBmdW5jdGlvbiBUb1N0cmluZyh2YWx1ZSBBbGxlcmd5SW50b2xlcmFuY2VDYXRlZ29yeSk6IHZhbHVlLnZhbHVlCmRlZmluZSBmdW5jdGlvbiBUb1N0cmluZyh2YWx1ZSBBbGxlcmd5SW50b2xlcmFuY2VDcml0aWNhbGl0eSk6IHZhbHVlLnZhbHVlCmRlZmluZSBmdW5jdGlvbiBUb1N0cmluZyh2YWx1ZSBBbGxlcmd5SW50b2xlcmFuY2VTZXZlcml0eSk6IHZhbHVlLnZhbHVlCmRlZmluZSBmdW5jdGlvbiBUb1N0cmluZyh2YWx1ZSBBbGxlcmd5SW50b2xlcmFuY2VUeXBlKTogdmFsdWUudmFsdWUKZGVmaW5lIGZ1bmN0aW9uIFRvU3RyaW5nKHZhbHVlIEFwcG9pbnRtZW50U3RhdHVzKTogdmFsdWUudmFsdWUKZGVmaW5lIGZ1bmN0aW9uIFRvU3RyaW5nKHZhbHVlIEFzc2VydGlvbkRpcmVjdGlvblR5cGUpOiB2YWx1ZS52YWx1ZQpkZWZpbmUgZnVuY3Rpb24gVG9TdHJpbmcodmFsdWUgQXNzZXJ0aW9uT3BlcmF0b3JUeXBlKTogdmFsdWUudmFsdWUKZGVmaW5lIGZ1bmN0aW9uIFRvU3RyaW5nKHZhbHVlIEFzc2VydGlvblJlc3BvbnNlVHlwZXMpOiB2YWx1ZS52YWx1ZQpkZWZpbmUgZnVuY3Rpb24gVG9TdHJpbmcodmFsdWUgQXVkaXRFdmVudEFjdGlvbik6IHZhbHVlLnZhbHVlCmRlZmluZSBmdW5jdGlvbiBUb1N0cmluZyh2YWx1ZSBBdWRpdEV2ZW50QWdlbnROZXR3b3JrVHlwZSk6IHZhbHVlLnZhbHVlCmRlZmluZSBmdW5jdGlvbiBUb1N0cmluZyh2YWx1ZSBBdWRpdEV2ZW50T3V0Y29tZSk6IHZhbHVlLnZhbHVlCmRlZmluZSBmdW5jdGlvbiBUb1N0cmluZyh2YWx1ZSBCaW5kaW5nU3RyZW5ndGgpOiB2YWx1ZS52YWx1ZQpkZWZpbmUgZnVuY3Rpb24gVG9TdHJpbmcodmFsdWUgQmlvbG9naWNhbGx5RGVyaXZlZFByb2R1Y3RDYXRlZ29yeSk6IHZhbHVlLnZhbHVlCmRlZmluZSBmdW5jdGlvbiBUb1N0cmluZyh2YWx1ZSBCaW9sb2dpY2FsbHlEZXJpdmVkUHJvZHVjdFN0YXR1cyk6IHZhbHVlLnZhbHVlCmRlZmluZSBmdW5jdGlvbiBUb1N0cmluZyh2YWx1ZSBCaW9sb2dpY2FsbHlEZXJpdmVkUHJvZHVjdFN0b3JhZ2VTY2FsZSk6IHZhbHVlLnZhbHVlCmRlZmluZSBmdW5jdGlvbiBUb1N0cmluZyh2YWx1ZSBCdW5kbGVUeXBlKTogdmFsdWUudmFsdWUKZGVmaW5lIGZ1bmN0aW9uIFRvU3RyaW5nKHZhbHVlIENhcGFiaWxpdHlTdGF0ZW1lbnRLaW5kKTogdmFsdWUudmFsdWUKZGVmaW5lIGZ1bmN0aW9uIFRvU3RyaW5nKHZhbHVlIENhcmVQbGFuQWN0aXZpdHlLaW5kKTogdmFsdWUudmFsdWUKZGVmaW5lIGZ1bmN0aW9uIFRvU3RyaW5nKHZhbHVlIENhcmVQbGFuQWN0aXZpdHlTdGF0dXMpOiB2YWx1ZS52YWx1ZQpkZWZpbmUgZnVuY3Rpb24gVG9TdHJpbmcodmFsdWUgQ2FyZVBsYW5JbnRlbnQpOiB2YWx1ZS52YWx1ZQpkZWZpbmUgZnVuY3Rpb24gVG9TdHJpbmcodmFsdWUgQ2FyZVBsYW5TdGF0dXMpOiB2YWx1ZS52YWx1ZQpkZWZpbmUgZnVuY3Rpb24gVG9TdHJpbmcodmFsdWUgQ2FyZVRlYW1TdGF0dXMpOiB2YWx1ZS52YWx1ZQpkZWZpbmUgZnVuY3Rpb24gVG9TdHJpbmcodmFsdWUgQ2F0YWxvZ0VudHJ5UmVsYXRpb25UeXBlKTogdmFsdWUudmFsdWUKZGVmaW5lIGZ1bmN0aW9uIFRvU3RyaW5nKHZhbHVlIENoYXJnZUl0ZW1EZWZpbml0aW9uUHJpY2VDb21wb25lbnRUeXBlKTogdmFsdWUudmFsdWUKZGVmaW5lIGZ1bmN0aW9uIFRvU3RyaW5nKHZhbHVlIENoYXJnZUl0ZW1TdGF0dXMpOiB2YWx1ZS52YWx1ZQpkZWZpbmUgZnVuY3Rpb24gVG9TdHJpbmcodmFsdWUgQ2xhaW1SZXNwb25zZVN0YXR1cyk6IHZhbHVlLnZhbHVlCmRlZmluZSBmdW5jdGlvbiBUb1N0cmluZyh2YWx1ZSBDbGFpbVN0YXR1cyk6IHZhbHVlLnZhbHVlCmRlZmluZSBmdW5jdGlvbiBUb1N0cmluZyh2YWx1ZSBDbGluaWNhbEltcHJlc3Npb25TdGF0dXMpOiB2YWx1ZS52YWx1ZQpkZWZpbmUgZnVuY3Rpb24gVG9TdHJpbmcodmFsdWUgQ29kZVNlYXJjaFN1cHBvcnQpOiB2YWx1ZS52YWx1ZQpkZWZpbmUgZnVuY3Rpb24gVG9TdHJpbmcodmFsdWUgQ29kZVN5c3RlbUNvbnRlbnRNb2RlKTogdmFsdWUudmFsdWUKZGVmaW5lIGZ1bmN0aW9uIFRvU3RyaW5nKHZhbHVlIENvZGVTeXN0ZW1IaWVyYXJjaHlNZWFuaW5nKTogdmFsdWUudmFsdWUKZGVmaW5lIGZ1bmN0aW9uIFRvU3RyaW5nKHZhbHVlIENvbW11bmljYXRpb25Qcmlvcml0eSk6IHZhbHVlLnZhbHVlCmRlZmluZSBmdW5jdGlvbiBUb1N0cmluZyh2YWx1ZSBDb21tdW5pY2F0aW9uUmVxdWVzdFN0YXR1cyk6IHZhbHVlLnZhbHVlCmRlZmluZSBmdW5jdGlvbiBUb1N0cmluZyh2YWx1ZSBDb21tdW5pY2F0aW9uU3RhdHVzKTogdmFsdWUudmFsdWUKZGVmaW5lIGZ1bmN0aW9uIFRvU3RyaW5nKHZhbHVlIENvbXBhcnRtZW50Q29kZSk6IHZhbHVlLnZhbHVlCmRlZmluZSBmdW5jdGlvbiBUb1N0cmluZyh2YWx1ZSBDb21wYXJ0bWVudFR5cGUpOiB2YWx1ZS52YWx1ZQpkZWZpbmUgZnVuY3Rpb24gVG9TdHJpbmcodmFsdWUgQ29tcG9zaXRpb25BdHRlc3RhdGlvbk1vZGUpOiB2YWx1ZS52YWx1ZQpkZWZpbmUgZnVuY3Rpb24gVG9TdHJpbmcodmFsdWUgQ29tcG9zaXRpb25TdGF0dXMpOiB2YWx1ZS52YWx1ZQpkZWZpbmUgZnVuY3Rpb24gVG9TdHJpbmcodmFsdWUgQ29uY2VwdE1hcEVxdWl2YWxlbmNlKTogdmFsdWUudmFsdWUKZGVmaW5lIGZ1bmN0aW9uIFRvU3RyaW5nKHZhbHVlIENvbmNlcHRNYXBHcm91cFVubWFwcGVkTW9kZSk6IHZhbHVlLnZhbHVlCmRlZmluZSBmdW5jdGlvbiBUb1N0cmluZyh2YWx1ZSBDb25kaXRpb25hbERlbGV0ZVN0YXR1cyk6IHZhbHVlLnZhbHVlCmRlZmluZSBmdW5jdGlvbiBUb1N0cmluZyh2YWx1ZSBDb25kaXRpb25hbFJlYWRTdGF0dXMpOiB2YWx1ZS52YWx1ZQpkZWZpbmUgZnVuY3Rpb24gVG9TdHJpbmcodmFsdWUgQ29uc2VudERhdGFNZWFuaW5nKTogdmFsdWUudmFsdWUKZGVmaW5lIGZ1bmN0aW9uIFRvU3RyaW5nKHZhbHVlIENvbnNlbnRQcm92aXNpb25UeXBlKTogdmFsdWUudmFsdWUKZGVmaW5lIGZ1bmN0aW9uIFRvU3RyaW5nKHZhbHVlIENvbnNlbnRTdGF0ZSk6IHZhbHVlLnZhbHVlCmRlZmluZSBmdW5jdGlvbiBUb1N0cmluZyh2YWx1ZSBDb25zdHJhaW50U2V2ZXJpdHkpOiB2YWx1ZS52YWx1ZQpkZWZpbmUgZnVuY3Rpb24gVG9TdHJpbmcodmFsdWUgQ29udGFjdFBvaW50U3lzdGVtKTogdmFsdWUudmFsdWUKZGVmaW5lIGZ1bmN0aW9uIFRvU3RyaW5nKHZhbHVlIENvbnRhY3RQb2ludFVzZSk6IHZhbHVlLnZhbHVlCmRlZmluZSBmdW5jdGlvbiBUb1N0cmluZyh2YWx1ZSBDb250cmFjdFB1YmxpY2F0aW9uU3RhdHVzKTogdmFsdWUudmFsdWUKZGVmaW5lIGZ1bmN0aW9uIFRvU3RyaW5nKHZhbHVlIENvbnRyYWN0U3RhdHVzKTogdmFsdWUudmFsdWUKZGVmaW5lIGZ1bmN0aW9uIFRvU3RyaW5nKHZhbHVlIENvbnRyaWJ1dG9yVHlwZSk6IHZhbHVlLnZhbHVlCmRlZmluZSBmdW5jdGlvbiBUb1N0cmluZyh2YWx1ZSBDb3ZlcmFnZVN0YXR1cyk6IHZhbHVlLnZhbHVlCmRlZmluZSBmdW5jdGlvbiBUb1N0cmluZyh2YWx1ZSBDdXJyZW5jeUNvZGUpOiB2YWx1ZS52YWx1ZQpkZWZpbmUgZnVuY3Rpb24gVG9TdHJpbmcodmFsdWUgRGF5T2ZXZWVrKTogdmFsdWUudmFsdWUKZGVmaW5lIGZ1bmN0aW9uIFRvU3RyaW5nKHZhbHVlIERheXNPZldlZWspOiB2YWx1ZS52YWx1ZQpkZWZpbmUgZnVuY3Rpb24gVG9TdHJpbmcodmFsdWUgRGV0ZWN0ZWRJc3N1ZVNldmVyaXR5KTogdmFsdWUudmFsdWUKZGVmaW5lIGZ1bmN0aW9uIFRvU3RyaW5nKHZhbHVlIERldGVjdGVkSXNzdWVTdGF0dXMpOiB2YWx1ZS52YWx1ZQpkZWZpbmUgZnVuY3Rpb24gVG9TdHJpbmcodmFsdWUgRGV2aWNlTWV0cmljQ2FsaWJyYXRpb25TdGF0ZSk6IHZhbHVlLnZhbHVlCmRlZmluZSBmdW5jdGlvbiBUb1N0cmluZyh2YWx1ZSBEZXZpY2VNZXRyaWNDYWxpYnJhdGlvblR5cGUpOiB2YWx1ZS52YWx1ZQpkZWZpbmUgZnVuY3Rpb24gVG9TdHJpbmcodmFsdWUgRGV2aWNlTWV0cmljQ2F0ZWdvcnkpOiB2YWx1ZS52YWx1ZQpkZWZpbmUgZnVuY3Rpb24gVG9TdHJpbmcodmFsdWUgRGV2aWNlTWV0cmljQ29sb3IpOiB2YWx1ZS52YWx1ZQpkZWZpbmUgZnVuY3Rpb24gVG9TdHJpbmcodmFsdWUgRGV2aWNlTWV0cmljT3BlcmF0aW9uYWxTdGF0dXMpOiB2YWx1ZS52YWx1ZQpkZWZpbmUgZnVuY3Rpb24gVG9TdHJpbmcodmFsdWUgRGV2aWNlTmFtZVR5cGUpOiB2YWx1ZS52YWx1ZQpkZWZpbmUgZnVuY3Rpb24gVG9TdHJpbmcodmFsdWUgRGV2aWNlUmVxdWVzdFN0YXR1cyk6IHZhbHVlLnZhbHVlCmRlZmluZSBmdW5jdGlvbiBUb1N0cmluZyh2YWx1ZSBEZXZpY2VVc2VTdGF0ZW1lbnRTdGF0dXMpOiB2YWx1ZS52YWx1ZQpkZWZpbmUgZnVuY3Rpb24gVG9TdHJpbmcodmFsdWUgRGlhZ25vc3RpY1JlcG9ydFN0YXR1cyk6IHZhbHVlLnZhbHVlCmRlZmluZSBmdW5jdGlvbiBUb1N0cmluZyh2YWx1ZSBEaXNjcmltaW5hdG9yVHlwZSk6IHZhbHVlLnZhbHVlCmRlZmluZSBmdW5jdGlvbiBUb1N0cmluZyh2YWx1ZSBEb2N1bWVudENvbmZpZGVudGlhbGl0eSk6IHZhbHVlLnZhbHVlCmRlZmluZSBmdW5jdGlvbiBUb1N0cmluZyh2YWx1ZSBEb2N1bWVudE1vZGUpOiB2YWx1ZS52YWx1ZQpkZWZpbmUgZnVuY3Rpb24gVG9TdHJpbmcodmFsdWUgRG9jdW1lbnRSZWZlcmVuY2VTdGF0dXMpOiB2YWx1ZS52YWx1ZQpkZWZpbmUgZnVuY3Rpb24gVG9TdHJpbmcodmFsdWUgRG9jdW1lbnRSZWxhdGlvbnNoaXBUeXBlKTogdmFsdWUudmFsdWUKZGVmaW5lIGZ1bmN0aW9uIFRvU3RyaW5nKHZhbHVlIEVsaWdpYmlsaXR5UmVxdWVzdFB1cnBvc2UpOiB2YWx1ZS52YWx1ZQpkZWZpbmUgZnVuY3Rpb24gVG9TdHJpbmcodmFsdWUgRWxpZ2liaWxpdHlSZXF1ZXN0U3RhdHVzKTogdmFsdWUudmFsdWUKZGVmaW5lIGZ1bmN0aW9uIFRvU3RyaW5nKHZhbHVlIEVsaWdpYmlsaXR5UmVzcG9uc2VQdXJwb3NlKTogdmFsdWUudmFsdWUKZGVmaW5lIGZ1bmN0aW9uIFRvU3RyaW5nKHZhbHVlIEVsaWdpYmlsaXR5UmVzcG9uc2VTdGF0dXMpOiB2YWx1ZS52YWx1ZQpkZWZpbmUgZnVuY3Rpb24gVG9TdHJpbmcodmFsdWUgRW5hYmxlV2hlbkJlaGF2aW9yKTogdmFsdWUudmFsdWUKZGVmaW5lIGZ1bmN0aW9uIFRvU3RyaW5nKHZhbHVlIEVuY291bnRlckxvY2F0aW9uU3RhdHVzKTogdmFsdWUudmFsdWUKZGVmaW5lIGZ1bmN0aW9uIFRvU3RyaW5nKHZhbHVlIEVuY291bnRlclN0YXR1cyk6IHZhbHVlLnZhbHVlCmRlZmluZSBmdW5jdGlvbiBUb1N0cmluZyh2YWx1ZSBFbmRwb2ludFN0YXR1cyk6IHZhbHVlLnZhbHVlCmRlZmluZSBmdW5jdGlvbiBUb1N0cmluZyh2YWx1ZSBFbnJvbGxtZW50UmVxdWVzdFN0YXR1cyk6IHZhbHVlLnZhbHVlCmRlZmluZSBmdW5jdGlvbiBUb1N0cmluZyh2YWx1ZSBFbnJvbGxtZW50UmVzcG9uc2VTdGF0dXMpOiB2YWx1ZS52YWx1ZQpkZWZpbmUgZnVuY3Rpb24gVG9TdHJpbmcodmFsdWUgRXBpc29kZU9mQ2FyZVN0YXR1cyk6IHZhbHVlLnZhbHVlCmRlZmluZSBmdW5jdGlvbiBUb1N0cmluZyh2YWx1ZSBFdmVudENhcGFiaWxpdHlNb2RlKTogdmFsdWUudmFsdWUKZGVmaW5lIGZ1bmN0aW9uIFRvU3RyaW5nKHZhbHVlIEV2ZW50VGltaW5nKTogdmFsdWUudmFsdWUKZGVmaW5lIGZ1bmN0aW9uIFRvU3RyaW5nKHZhbHVlIEV2aWRlbmNlVmFyaWFibGVUeXBlKTogdmFsdWUudmFsdWUKZGVmaW5lIGZ1bmN0aW9uIFRvU3RyaW5nKHZhbHVlIEV4YW1wbGVTY2VuYXJpb0FjdG9yVHlwZSk6IHZhbHVlLnZhbHVlCmRlZmluZSBmdW5jdGlvbiBUb1N0cmluZyh2YWx1ZSBFeHBsYW5hdGlvbk9mQmVuZWZpdFN0YXR1cyk6IHZhbHVlLnZhbHVlCmRlZmluZSBmdW5jdGlvbiBUb1N0cmluZyh2YWx1ZSBFeHBvc3VyZVN0YXRlKTogdmFsdWUudmFsdWUKZGVmaW5lIGZ1bmN0aW9uIFRvU3RyaW5nKHZhbHVlIEV4dGVuc2lvbkNvbnRleHRUeXBlKTogdmFsdWUudmFsdWUKZGVmaW5lIGZ1bmN0aW9uIFRvU3RyaW5nKHZhbHVlIEZISVJBbGxUeXBlcyk6IHZhbHVlLnZhbHVlCmRlZmluZSBmdW5jdGlvbiBUb1N0cmluZyh2YWx1ZSBGSElSRGVmaW5lZFR5cGUpOiB2YWx1ZS52YWx1ZQpkZWZpbmUgZnVuY3Rpb24gVG9TdHJpbmcodmFsdWUgRkhJUkRldmljZVN0YXR1cyk6IHZhbHVlLnZhbHVlCmRlZmluZSBmdW5jdGlvbiBUb1N0cmluZyh2YWx1ZSBGSElSUmVzb3VyY2VUeXBlKTogdmFsdWUudmFsdWUKZGVmaW5lIGZ1bmN0aW9uIFRvU3RyaW5nKHZhbHVlIEZISVJTdWJzdGFuY2VTdGF0dXMpOiB2YWx1ZS52YWx1ZQpkZWZpbmUgZnVuY3Rpb24gVG9TdHJpbmcodmFsdWUgRkhJUlZlcnNpb24pOiB2YWx1ZS52YWx1ZQpkZWZpbmUgZnVuY3Rpb24gVG9TdHJpbmcodmFsdWUgRmFtaWx5SGlzdG9yeVN0YXR1cyk6IHZhbHVlLnZhbHVlCmRlZmluZSBmdW5jdGlvbiBUb1N0cmluZyh2YWx1ZSBGaWx0ZXJPcGVyYXRvcik6IHZhbHVlLnZhbHVlCmRlZmluZSBmdW5jdGlvbiBUb1N0cmluZyh2YWx1ZSBGbGFnU3RhdHVzKTogdmFsdWUudmFsdWUKZGVmaW5lIGZ1bmN0aW9uIFRvU3RyaW5nKHZhbHVlIEdvYWxMaWZlY3ljbGVTdGF0dXMpOiB2YWx1ZS52YWx1ZQpkZWZpbmUgZnVuY3Rpb24gVG9TdHJpbmcodmFsdWUgR3JhcGhDb21wYXJ0bWVudFJ1bGUpOiB2YWx1ZS52YWx1ZQpkZWZpbmUgZnVuY3Rpb24gVG9TdHJpbmcodmFsdWUgR3JhcGhDb21wYXJ0bWVudFVzZSk6IHZhbHVlLnZhbHVlCmRlZmluZSBmdW5jdGlvbiBUb1N0cmluZyh2YWx1ZSBHcm91cE1lYXN1cmUpOiB2YWx1ZS52YWx1ZQpkZWZpbmUgZnVuY3Rpb24gVG9TdHJpbmcodmFsdWUgR3JvdXBUeXBlKTogdmFsdWUudmFsdWUKZGVmaW5lIGZ1bmN0aW9uIFRvU3RyaW5nKHZhbHVlIEd1aWRhbmNlUmVzcG9uc2VTdGF0dXMpOiB2YWx1ZS52YWx1ZQpkZWZpbmUgZnVuY3Rpb24gVG9TdHJpbmcodmFsdWUgR3VpZGVQYWdlR2VuZXJhdGlvbik6IHZhbHVlLnZhbHVlCmRlZmluZSBmdW5jdGlvbiBUb1N0cmluZyh2YWx1ZSBHdWlkZVBhcmFtZXRlckNvZGUpOiB2YWx1ZS52YWx1ZQpkZWZpbmUgZnVuY3Rpb24gVG9TdHJpbmcodmFsdWUgSFRUUFZlcmIpOiB2YWx1ZS52YWx1ZQpkZWZpbmUgZnVuY3Rpb24gVG9TdHJpbmcodmFsdWUgSWRlbnRpZmllclVzZSk6IHZhbHVlLnZhbHVlCmRlZmluZSBmdW5jdGlvbiBUb1N0cmluZyh2YWx1ZSBJZGVudGl0eUFzc3VyYW5jZUxldmVsKTogdmFsdWUudmFsdWUKZGVmaW5lIGZ1bmN0aW9uIFRvU3RyaW5nKHZhbHVlIEltYWdpbmdTdHVkeVN0YXR1cyk6IHZhbHVlLnZhbHVlCmRlZmluZSBmdW5jdGlvbiBUb1N0cmluZyh2YWx1ZSBJbW11bml6YXRpb25FdmFsdWF0aW9uU3RhdHVzKTogdmFsdWUudmFsdWUKZGVmaW5lIGZ1bmN0aW9uIFRvU3RyaW5nKHZhbHVlIEltbXVuaXphdGlvblN0YXR1cyk6IHZhbHVlLnZhbHVlCmRlZmluZSBmdW5jdGlvbiBUb1N0cmluZyh2YWx1ZSBJbnZvaWNlUHJpY2VDb21wb25lbnRUeXBlKTogdmFsdWUudmFsdWUKZGVmaW5lIGZ1bmN0aW9uIFRvU3RyaW5nKHZhbHVlIEludm9pY2VTdGF0dXMpOiB2YWx1ZS52YWx1ZQpkZWZpbmUgZnVuY3Rpb24gVG9TdHJpbmcodmFsdWUgSXNzdWVTZXZlcml0eSk6IHZhbHVlLnZhbHVlCmRlZmluZSBmdW5jdGlvbiBUb1N0cmluZyh2YWx1ZSBJc3N1ZVR5cGUpOiB2YWx1ZS52YWx1ZQpkZWZpbmUgZnVuY3Rpb24gVG9TdHJpbmcodmFsdWUgTGlua1R5cGUpOiB2YWx1ZS52YWx1ZQpkZWZpbmUgZnVuY3Rpb24gVG9TdHJpbmcodmFsdWUgTGlua2FnZVR5cGUpOiB2YWx1ZS52YWx1ZQpkZWZpbmUgZnVuY3Rpb24gVG9TdHJpbmcodmFsdWUgTGlzdE1vZGUpOiB2YWx1ZS52YWx1ZQpkZWZpbmUgZnVuY3Rpb24gVG9TdHJpbmcodmFsdWUgTGlzdFN0YXR1cyk6IHZhbHVlLnZhbHVlCmRlZmluZSBmdW5jdGlvbiBUb1N0cmluZyh2YWx1ZSBMb2NhdGlvbk1vZGUpOiB2YWx1ZS52YWx1ZQpkZWZpbmUgZnVuY3Rpb24gVG9TdHJpbmcodmFsdWUgTG9jYXRpb25TdGF0dXMpOiB2YWx1ZS52YWx1ZQpkZWZpbmUgZnVuY3Rpb24gVG9TdHJpbmcodmFsdWUgTWVhc3VyZVJlcG9ydFN0YXR1cyk6IHZhbHVlLnZhbHVlCmRlZmluZSBmdW5jdGlvbiBUb1N0cmluZyh2YWx1ZSBNZWFzdXJlUmVwb3J0VHlwZSk6IHZhbHVlLnZhbHVlCmRlZmluZSBmdW5jdGlvbiBUb1N0cmluZyh2YWx1ZSBNZWRpYVN0YXR1cyk6IHZhbHVlLnZhbHVlCmRlZmluZSBmdW5jdGlvbiBUb1N0cmluZyh2YWx1ZSBNZWRpY2F0aW9uQWRtaW5pc3RyYXRpb25TdGF0dXMpOiB2YWx1ZS52YWx1ZQpkZWZpbmUgZnVuY3Rpb24gVG9TdHJpbmcodmFsdWUgTWVkaWNhdGlvbkRpc3BlbnNlU3RhdHVzKTogdmFsdWUudmFsdWUKZGVmaW5lIGZ1bmN0aW9uIFRvU3RyaW5nKHZhbHVlIE1lZGljYXRpb25Lbm93bGVkZ2VTdGF0dXMpOiB2YWx1ZS52YWx1ZQpkZWZpbmUgZnVuY3Rpb24gVG9TdHJpbmcodmFsdWUgTWVkaWNhdGlvblJlcXVlc3RJbnRlbnQpOiB2YWx1ZS52YWx1ZQpkZWZpbmUgZnVuY3Rpb24gVG9TdHJpbmcodmFsdWUgTWVkaWNhdGlvblJlcXVlc3RQcmlvcml0eSk6IHZhbHVlLnZhbHVlCmRlZmluZSBmdW5jdGlvbiBUb1N0cmluZyh2YWx1ZSBNZWRpY2F0aW9uUmVxdWVzdFN0YXR1cyk6IHZhbHVlLnZhbHVlCmRlZmluZSBmdW5jdGlvbiBUb1N0cmluZyh2YWx1ZSBNZWRpY2F0aW9uU3RhdGVtZW50U3RhdHVzKTogdmFsdWUudmFsdWUKZGVmaW5lIGZ1bmN0aW9uIFRvU3RyaW5nKHZhbHVlIE1lZGljYXRpb25TdGF0dXMpOiB2YWx1ZS52YWx1ZQpkZWZpbmUgZnVuY3Rpb24gVG9TdHJpbmcodmFsdWUgTWVzc2FnZVNpZ25pZmljYW5jZUNhdGVnb3J5KTogdmFsdWUudmFsdWUKZGVmaW5lIGZ1bmN0aW9uIFRvU3RyaW5nKHZhbHVlIE1lc3NhZ2VoZWFkZXJfUmVzcG9uc2VfUmVxdWVzdCk6IHZhbHVlLnZhbHVlCmRlZmluZSBmdW5jdGlvbiBUb1N0cmluZyh2YWx1ZSBNaW1lVHlwZSk6IHZhbHVlLnZhbHVlCmRlZmluZSBmdW5jdGlvbiBUb1N0cmluZyh2YWx1ZSBOYW1lVXNlKTogdmFsdWUudmFsdWUKZGVmaW5lIGZ1bmN0aW9uIFRvU3RyaW5nKHZhbHVlIE5hbWluZ1N5c3RlbUlkZW50aWZpZXJUeXBlKTogdmFsdWUudmFsdWUKZGVmaW5lIGZ1bmN0aW9uIFRvU3RyaW5nKHZhbHVlIE5hbWluZ1N5c3RlbVR5cGUpOiB2YWx1ZS52YWx1ZQpkZWZpbmUgZnVuY3Rpb24gVG9TdHJpbmcodmFsdWUgTmFycmF0aXZlU3RhdHVzKTogdmFsdWUudmFsdWUKZGVmaW5lIGZ1bmN0aW9uIFRvU3RyaW5nKHZhbHVlIE5vdGVUeXBlKTogdmFsdWUudmFsdWUKZGVmaW5lIGZ1bmN0aW9uIFRvU3RyaW5nKHZhbHVlIE51dHJpdGlpb25PcmRlckludGVudCk6IHZhbHVlLnZhbHVlCmRlZmluZSBmdW5jdGlvbiBUb1N0cmluZyh2YWx1ZSBOdXRyaXRpb25PcmRlclN0YXR1cyk6IHZhbHVlLnZhbHVlCmRlZmluZSBmdW5jdGlvbiBUb1N0cmluZyh2YWx1ZSBPYnNlcnZhdGlvbkRhdGFUeXBlKTogdmFsdWUudmFsdWUKZGVmaW5lIGZ1bmN0aW9uIFRvU3RyaW5nKHZhbHVlIE9ic2VydmF0aW9uUmFuZ2VDYXRlZ29yeSk6IHZhbHVlLnZhbHVlCmRlZmluZSBmdW5jdGlvbiBUb1N0cmluZyh2YWx1ZSBPYnNlcnZhdGlvblN0YXR1cyk6IHZhbHVlLnZhbHVlCmRlZmluZSBmdW5jdGlvbiBUb1N0cmluZyh2YWx1ZSBPcGVyYXRpb25LaW5kKTogdmFsdWUudmFsdWUKZGVmaW5lIGZ1bmN0aW9uIFRvU3RyaW5nKHZhbHVlIE9wZXJhdGlvblBhcmFtZXRlclVzZSk6IHZhbHVlLnZhbHVlCmRlZmluZSBmdW5jdGlvbiBUb1N0cmluZyh2YWx1ZSBPcmllbnRhdGlvblR5cGUpOiB2YWx1ZS52YWx1ZQpkZWZpbmUgZnVuY3Rpb24gVG9TdHJpbmcodmFsdWUgUGFyYW1ldGVyVXNlKTogdmFsdWUudmFsdWUKZGVmaW5lIGZ1bmN0aW9uIFRvU3RyaW5nKHZhbHVlIFBhcnRpY2lwYW50UmVxdWlyZWQpOiB2YWx1ZS52YWx1ZQpkZWZpbmUgZnVuY3Rpb24gVG9TdHJpbmcodmFsdWUgUGFydGljaXBhbnRTdGF0dXMpOiB2YWx1ZS52YWx1ZQpkZWZpbmUgZnVuY3Rpb24gVG9TdHJpbmcodmFsdWUgUGFydGljaXBhdGlvblN0YXR1cyk6IHZhbHVlLnZhbHVlCmRlZmluZSBmdW5jdGlvbiBUb1N0cmluZyh2YWx1ZSBQYXltZW50Tm90aWNlU3RhdHVzKTogdmFsdWUudmFsdWUKZGVmaW5lIGZ1bmN0aW9uIFRvU3RyaW5nKHZhbHVlIFBheW1lbnRSZWNvbmNpbGlhdGlvblN0YXR1cyk6IHZhbHVlLnZhbHVlCmRlZmluZSBmdW5jdGlvbiBUb1N0cmluZyh2YWx1ZSBQcm9jZWR1cmVTdGF0dXMpOiB2YWx1ZS52YWx1ZQpkZWZpbmUgZnVuY3Rpb24gVG9TdHJpbmcodmFsdWUgUHJvcGVydHlSZXByZXNlbnRhdGlvbik6IHZhbHVlLnZhbHVlCmRlZmluZSBmdW5jdGlvbiBUb1N0cmluZyh2YWx1ZSBQcm9wZXJ0eVR5cGUpOiB2YWx1ZS52YWx1ZQpkZWZpbmUgZnVuY3Rpb24gVG9TdHJpbmcodmFsdWUgUHJvdmVuYW5jZUVudGl0eVJvbGUpOiB2YWx1ZS52YWx1ZQpkZWZpbmUgZnVuY3Rpb24gVG9TdHJpbmcodmFsdWUgUHVibGljYXRpb25TdGF0dXMpOiB2YWx1ZS52YWx1ZQpkZWZpbmUgZnVuY3Rpb24gVG9TdHJpbmcodmFsdWUgUXVhbGl0eVR5cGUpOiB2YWx1ZS52YWx1ZQpkZWZpbmUgZnVuY3Rpb24gVG9TdHJpbmcodmFsdWUgUXVhbnRpdHlDb21wYXJhdG9yKTogdmFsdWUudmFsdWUKZGVmaW5lIGZ1bmN0aW9uIFRvU3RyaW5nKHZhbHVlIFF1ZXN0aW9ubmFpcmVJdGVtT3BlcmF0b3IpOiB2YWx1ZS52YWx1ZQpkZWZpbmUgZnVuY3Rpb24gVG9TdHJpbmcodmFsdWUgUXVlc3Rpb25uYWlyZUl0ZW1UeXBlKTogdmFsdWUudmFsdWUKZGVmaW5lIGZ1bmN0aW9uIFRvU3RyaW5nKHZhbHVlIFF1ZXN0aW9ubmFpcmVSZXNwb25zZVN0YXR1cyk6IHZhbHVlLnZhbHVlCmRlZmluZSBmdW5jdGlvbiBUb1N0cmluZyh2YWx1ZSBSZWZlcmVuY2VIYW5kbGluZ1BvbGljeSk6IHZhbHVlLnZhbHVlCmRlZmluZSBmdW5jdGlvbiBUb1N0cmluZyh2YWx1ZSBSZWZlcmVuY2VWZXJzaW9uUnVsZXMpOiB2YWx1ZS52YWx1ZQpkZWZpbmUgZnVuY3Rpb24gVG9TdHJpbmcodmFsdWUgUmVmZXJyZWREb2N1bWVudFN0YXR1cyk6IHZhbHVlLnZhbHVlCmRlZmluZSBmdW5jdGlvbiBUb1N0cmluZyh2YWx1ZSBSZWxhdGVkQXJ0aWZhY3RUeXBlKTogdmFsdWUudmFsdWUKZGVmaW5lIGZ1bmN0aW9uIFRvU3RyaW5nKHZhbHVlIFJlbWl0dGFuY2VPdXRjb21lKTogdmFsdWUudmFsdWUKZGVmaW5lIGZ1bmN0aW9uIFRvU3RyaW5nKHZhbHVlIFJlcG9zaXRvcnlUeXBlKTogdmFsdWUudmFsdWUKZGVmaW5lIGZ1bmN0aW9uIFRvU3RyaW5nKHZhbHVlIFJlcXVlc3RJbnRlbnQpOiB2YWx1ZS52YWx1ZQpkZWZpbmUgZnVuY3Rpb24gVG9TdHJpbmcodmFsdWUgUmVxdWVzdFByaW9yaXR5KTogdmFsdWUudmFsdWUKZGVmaW5lIGZ1bmN0aW9uIFRvU3RyaW5nKHZhbHVlIFJlcXVlc3RTdGF0dXMpOiB2YWx1ZS52YWx1ZQpkZWZpbmUgZnVuY3Rpb24gVG9TdHJpbmcodmFsdWUgUmVzZWFyY2hFbGVtZW50VHlwZSk6IHZhbHVlLnZhbHVlCmRlZmluZSBmdW5jdGlvbiBUb1N0cmluZyh2YWx1ZSBSZXNlYXJjaFN0dWR5U3RhdHVzKTogdmFsdWUudmFsdWUKZGVmaW5lIGZ1bmN0aW9uIFRvU3RyaW5nKHZhbHVlIFJlc2VhcmNoU3ViamVjdFN0YXR1cyk6IHZhbHVlLnZhbHVlCmRlZmluZSBmdW5jdGlvbiBUb1N0cmluZyh2YWx1ZSBSZXNvdXJjZVR5cGUpOiB2YWx1ZS52YWx1ZQpkZWZpbmUgZnVuY3Rpb24gVG9TdHJpbmcodmFsdWUgUmVzb3VyY2VWZXJzaW9uUG9saWN5KTogdmFsdWUudmFsdWUKZGVmaW5lIGZ1bmN0aW9uIFRvU3RyaW5nKHZhbHVlIFJlc3BvbnNlVHlwZSk6IHZhbHVlLnZhbHVlCmRlZmluZSBmdW5jdGlvbiBUb1N0cmluZyh2YWx1ZSBSZXN0ZnVsQ2FwYWJpbGl0eU1vZGUpOiB2YWx1ZS52YWx1ZQpkZWZpbmUgZnVuY3Rpb24gVG9TdHJpbmcodmFsdWUgUmlza0Fzc2Vzc21lbnRTdGF0dXMpOiB2YWx1ZS52YWx1ZQpkZWZpbmUgZnVuY3Rpb24gVG9TdHJpbmcodmFsdWUgU1BEWExpY2Vuc2UpOiB2YWx1ZS52YWx1ZQpkZWZpbmUgZnVuY3Rpb24gVG9TdHJpbmcodmFsdWUgU2VhcmNoQ29tcGFyYXRvcik6IHZhbHVlLnZhbHVlCmRlZmluZSBmdW5jdGlvbiBUb1N0cmluZyh2YWx1ZSBTZWFyY2hFbnRyeU1vZGUpOiB2YWx1ZS52YWx1ZQpkZWZpbmUgZnVuY3Rpb24gVG9TdHJpbmcodmFsdWUgU2VhcmNoTW9kaWZpZXJDb2RlKTogdmFsdWUudmFsdWUKZGVmaW5lIGZ1bmN0aW9uIFRvU3RyaW5nKHZhbHVlIFNlYXJjaFBhcmFtVHlwZSk6IHZhbHVlLnZhbHVlCmRlZmluZSBmdW5jdGlvbiBUb1N0cmluZyh2YWx1ZSBTZWN0aW9uTW9kZSk6IHZhbHVlLnZhbHVlCmRlZmluZSBmdW5jdGlvbiBUb1N0cmluZyh2YWx1ZSBTZXF1ZW5jZVR5cGUpOiB2YWx1ZS52YWx1ZQpkZWZpbmUgZnVuY3Rpb24gVG9TdHJpbmcodmFsdWUgU2VydmljZVJlcXVlc3RJbnRlbnQpOiB2YWx1ZS52YWx1ZQpkZWZpbmUgZnVuY3Rpb24gVG9TdHJpbmcodmFsdWUgU2VydmljZVJlcXVlc3RQcmlvcml0eSk6IHZhbHVlLnZhbHVlCmRlZmluZSBmdW5jdGlvbiBUb1N0cmluZyh2YWx1ZSBTZXJ2aWNlUmVxdWVzdFN0YXR1cyk6IHZhbHVlLnZhbHVlCmRlZmluZSBmdW5jdGlvbiBUb1N0cmluZyh2YWx1ZSBTbGljaW5nUnVsZXMpOiB2YWx1ZS52YWx1ZQpkZWZpbmUgZnVuY3Rpb24gVG9TdHJpbmcodmFsdWUgU2xvdFN0YXR1cyk6IHZhbHVlLnZhbHVlCmRlZmluZSBmdW5jdGlvbiBUb1N0cmluZyh2YWx1ZSBTb3J0RGlyZWN0aW9uKTogdmFsdWUudmFsdWUKZGVmaW5lIGZ1bmN0aW9uIFRvU3RyaW5nKHZhbHVlIFNwZWNpbWVuQ29udGFpbmVkUHJlZmVyZW5jZSk6IHZhbHVlLnZhbHVlCmRlZmluZSBmdW5jdGlvbiBUb1N0cmluZyh2YWx1ZSBTcGVjaW1lblN0YXR1cyk6IHZhbHVlLnZhbHVlCmRlZmluZSBmdW5jdGlvbiBUb1N0cmluZyh2YWx1ZSBTdGF0dXMpOiB2YWx1ZS52YWx1ZQpkZWZpbmUgZnVuY3Rpb24gVG9TdHJpbmcodmFsdWUgU3RyYW5kVHlwZSk6IHZhbHVlLnZhbHVlCmRlZmluZSBmdW5jdGlvbiBUb1N0cmluZyh2YWx1ZSBTdHJ1Y3R1cmVEZWZpbml0aW9uS2luZCk6IHZhbHVlLnZhbHVlCmRlZmluZSBmdW5jdGlvbiBUb1N0cmluZyh2YWx1ZSBTdHJ1Y3R1cmVNYXBDb250ZXh0VHlwZSk6IHZhbHVlLnZhbHVlCmRlZmluZSBmdW5jdGlvbiBUb1N0cmluZyh2YWx1ZSBTdHJ1Y3R1cmVNYXBHcm91cFR5cGVNb2RlKTogdmFsdWUudmFsdWUKZGVmaW5lIGZ1bmN0aW9uIFRvU3RyaW5nKHZhbHVlIFN0cnVjdHVyZU1hcElucHV0TW9kZSk6IHZhbHVlLnZhbHVlCmRlZmluZSBmdW5jdGlvbiBUb1N0cmluZyh2YWx1ZSBTdHJ1Y3R1cmVNYXBNb2RlbE1vZGUpOiB2YWx1ZS52YWx1ZQpkZWZpbmUgZnVuY3Rpb24gVG9TdHJpbmcodmFsdWUgU3RydWN0dXJlTWFwU291cmNlTGlzdE1vZGUpOiB2YWx1ZS52YWx1ZQpkZWZpbmUgZnVuY3Rpb24gVG9TdHJpbmcodmFsdWUgU3RydWN0dXJlTWFwVGFyZ2V0TGlzdE1vZGUpOiB2YWx1ZS52YWx1ZQpkZWZpbmUgZnVuY3Rpb24gVG9TdHJpbmcodmFsdWUgU3RydWN0dXJlTWFwVHJhbnNmb3JtKTogdmFsdWUudmFsdWUKZGVmaW5lIGZ1bmN0aW9uIFRvU3RyaW5nKHZhbHVlIFN1YnNjcmlwdGlvbkNoYW5uZWxUeXBlKTogdmFsdWUudmFsdWUKZGVmaW5lIGZ1bmN0aW9uIFRvU3RyaW5nKHZhbHVlIFN1YnNjcmlwdGlvblN0YXR1cyk6IHZhbHVlLnZhbHVlCmRlZmluZSBmdW5jdGlvbiBUb1N0cmluZyh2YWx1ZSBTdXBwbHlEZWxpdmVyeVN0YXR1cyk6IHZhbHVlLnZhbHVlCmRlZmluZSBmdW5jdGlvbiBUb1N0cmluZyh2YWx1ZSBTdXBwbHlSZXF1ZXN0U3RhdHVzKTogdmFsdWUudmFsdWUKZGVmaW5lIGZ1bmN0aW9uIFRvU3RyaW5nKHZhbHVlIFN5c3RlbVJlc3RmdWxJbnRlcmFjdGlvbik6IHZhbHVlLnZhbHVlCmRlZmluZSBmdW5jdGlvbiBUb1N0cmluZyh2YWx1ZSBUYXNrSW50ZW50KTogdmFsdWUudmFsdWUKZGVmaW5lIGZ1bmN0aW9uIFRvU3RyaW5nKHZhbHVlIFRhc2tQcmlvcml0eSk6IHZhbHVlLnZhbHVlCmRlZmluZSBmdW5jdGlvbiBUb1N0cmluZyh2YWx1ZSBUYXNrU3RhdHVzKTogdmFsdWUudmFsdWUKZGVmaW5lIGZ1bmN0aW9uIFRvU3RyaW5nKHZhbHVlIFRlc3RSZXBvcnRBY3Rpb25SZXN1bHQpOiB2YWx1ZS52YWx1ZQpkZWZpbmUgZnVuY3Rpb24gVG9TdHJpbmcodmFsdWUgVGVzdFJlcG9ydFBhcnRpY2lwYW50VHlwZSk6IHZhbHVlLnZhbHVlCmRlZmluZSBmdW5jdGlvbiBUb1N0cmluZyh2YWx1ZSBUZXN0UmVwb3J0UmVzdWx0KTogdmFsdWUudmFsdWUKZGVmaW5lIGZ1bmN0aW9uIFRvU3RyaW5nKHZhbHVlIFRlc3RSZXBvcnRTdGF0dXMpOiB2YWx1ZS52YWx1ZQpkZWZpbmUgZnVuY3Rpb24gVG9TdHJpbmcodmFsdWUgVGVzdFNjcmlwdFJlcXVlc3RNZXRob2RDb2RlKTogdmFsdWUudmFsdWUKZGVmaW5lIGZ1bmN0aW9uIFRvU3RyaW5nKHZhbHVlIFRyaWdnZXJUeXBlKTogdmFsdWUudmFsdWUKZGVmaW5lIGZ1bmN0aW9uIFRvU3RyaW5nKHZhbHVlIFR5cGVEZXJpdmF0aW9uUnVsZSk6IHZhbHVlLnZhbHVlCmRlZmluZSBmdW5jdGlvbiBUb1N0cmluZyh2YWx1ZSBUeXBlUmVzdGZ1bEludGVyYWN0aW9uKTogdmFsdWUudmFsdWUKZGVmaW5lIGZ1bmN0aW9uIFRvU3RyaW5nKHZhbHVlIFVESUVudHJ5VHlwZSk6IHZhbHVlLnZhbHVlCmRlZmluZSBmdW5jdGlvbiBUb1N0cmluZyh2YWx1ZSBVbml0c09mVGltZSk6IHZhbHVlLnZhbHVlCmRlZmluZSBmdW5jdGlvbiBUb1N0cmluZyh2YWx1ZSBVc2UpOiB2YWx1ZS52YWx1ZQpkZWZpbmUgZnVuY3Rpb24gVG9TdHJpbmcodmFsdWUgVmFyaWFibGVUeXBlKTogdmFsdWUudmFsdWUKZGVmaW5lIGZ1bmN0aW9uIFRvU3RyaW5nKHZhbHVlIFZpc2lvbkJhc2UpOiB2YWx1ZS52YWx1ZQpkZWZpbmUgZnVuY3Rpb24gVG9TdHJpbmcodmFsdWUgVmlzaW9uRXllcyk6IHZhbHVlLnZhbHVlCmRlZmluZSBmdW5jdGlvbiBUb1N0cmluZyh2YWx1ZSBWaXNpb25TdGF0dXMpOiB2YWx1ZS52YWx1ZQpkZWZpbmUgZnVuY3Rpb24gVG9TdHJpbmcodmFsdWUgWFBhdGhVc2FnZVR5cGUpOiB2YWx1ZS52YWx1ZQpkZWZpbmUgZnVuY3Rpb24gVG9TdHJpbmcodmFsdWUgYmFzZTY0QmluYXJ5KTogdmFsdWUudmFsdWUKZGVmaW5lIGZ1bmN0aW9uIFRvQm9vbGVhbih2YWx1ZSBib29sZWFuKTogdmFsdWUudmFsdWUKZGVmaW5lIGZ1bmN0aW9uIFRvRGF0ZSh2YWx1ZSBkYXRlKTogdmFsdWUudmFsdWUKZGVmaW5lIGZ1bmN0aW9uIFRvRGF0ZVRpbWUodmFsdWUgZGF0ZVRpbWUpOiB2YWx1ZS52YWx1ZQpkZWZpbmUgZnVuY3Rpb24gVG9EZWNpbWFsKHZhbHVlIGRlY2ltYWwpOiB2YWx1ZS52YWx1ZQpkZWZpbmUgZnVuY3Rpb24gVG9EYXRlVGltZSh2YWx1ZSBpbnN0YW50KTogdmFsdWUudmFsdWUKZGVmaW5lIGZ1bmN0aW9uIFRvSW50ZWdlcih2YWx1ZSBpbnRlZ2VyKTogdmFsdWUudmFsdWUKZGVmaW5lIGZ1bmN0aW9uIFRvU3RyaW5nKHZhbHVlIHN0cmluZyk6IHZhbHVlLnZhbHVlCmRlZmluZSBmdW5jdGlvbiBUb1RpbWUodmFsdWUgdGltZSk6IHZhbHVlLnZhbHVlCmRlZmluZSBmdW5jdGlvbiBUb1N0cmluZyh2YWx1ZSB1cmkpOiB2YWx1ZS52YWx1ZQpkZWZpbmUgZnVuY3Rpb24gVG9TdHJpbmcodmFsdWUgeGh0bWwpOiB2YWx1ZS52YWx1ZQo=" - }, - { - "contentType": "application/elm+xml", - "data": "PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0iVVRGLTgiPz4KPGxpYnJhcnkgeG1sbnM9InVybjpobDctb3JnOmVsbTpyMSIgeG1sbnM6dD0idXJuOmhsNy1vcmc6ZWxtLXR5cGVzOnIxIiB4bWxuczp4c2k9Imh0dHA6Ly93d3cudzMub3JnLzIwMDEvWE1MU2NoZW1hLWluc3RhbmNlIiB4bWxuczp4c2Q9Imh0dHA6Ly93d3cudzMub3JnLzIwMDEvWE1MU2NoZW1hIiB4bWxuczpmaGlyPSJodHRwOi8vaGw3Lm9yZy9maGlyIiB4bWxuczpxZG00Mz0idXJuOmhlYWx0aGl0LWdvdjpxZG06djRfMyIgeG1sbnM6cWRtNTM9InVybjpoZWFsdGhpdC1nb3Y6cWRtOnY1XzMiIHhtbG5zOmE9InVybjpobDctb3JnOmNxbC1hbm5vdGF0aW9uczpyMSI+CiAgIDxhbm5vdGF0aW9uIHRyYW5zbGF0b3JWZXJzaW9uPSIyLjcuMCIgdHJhbnNsYXRvck9wdGlvbnM9IkVuYWJsZUxvY2F0b3JzLERpc2FibGVMaXN0RGVtb3Rpb24sRGlzYWJsZUxpc3RQcm9tb3Rpb24iIHhzaTp0eXBlPSJhOkNxbFRvRWxtSW5mbyIvPgogICA8YW5ub3RhdGlvbiB4c2k6dHlwZT0iYTpBbm5vdGF0aW9uIj4KICAgICAgPGE6dCBuYW1lPSJhdXRob3IiIHZhbHVlPSJCcnluIFJob2RlcyIvPgogICAgICA8YTp0IG5hbWU9ImRlc2NyaXB0aW9uIiB2YWx1ZT0iVGhpcyBsaWJyYXJ5IGRlZmluZXMgZnVuY3Rpb25zIHRvIGNvbnZlcnQgYmV0d2VlbiBGSElSJiN4YTsgZGF0YSB0eXBlcyBhbmQgQ1FMIHN5c3RlbS1kZWZpbmVkIHR5cGVzLCBhcyB3ZWxsIGFzIGZ1bmN0aW9ucyB0byBzdXBwb3J0JiN4YTsgRkhJUlBhdGggaW1wbGVtZW50YXRpb24uIEZvciBtb3JlIGluZm9ybWF0aW9uLCBzZWUgdGhlIEZISVJIZWxwZXJzIHdpa2kgcGFnZTomI3hhOyBodHRwczovL2dpdGh1Yi5jb20vY3FmcmFtZXdvcmsvY2xpbmljYWxfcXVhbGl0eV9sYW5ndWFnZS93aWtpL0ZISVJIZWxwZXJzIi8+CiAgICAgIDxhOnQgbmFtZT0iYWxsb3dGbHVlbnQiIHZhbHVlPSJ0cnVlIi8+CiAgIDwvYW5ub3RhdGlvbj4KICAgPGlkZW50aWZpZXIgaWQ9IkZISVJIZWxwZXJzIiBzeXN0ZW09Imh0dHA6Ly9jb250ZW50LmFscGhvcmEuY29tL2ZoaXIvZHFtIiB2ZXJzaW9uPSI0LjAuMSIvPgogICA8c2NoZW1hSWRlbnRpZmllciBpZD0idXJuOmhsNy1vcmc6ZWxtIiB2ZXJzaW9uPSJyMSIvPgogICA8dXNpbmdzPgogICAgICA8ZGVmIGxvY2FsSWRlbnRpZmllcj0iU3lzdGVtIiB1cmk9InVybjpobDctb3JnOmVsbS10eXBlczpyMSIvPgogICAgICA8ZGVmIGxvY2F0b3I9IjExOjEtMTE6MjYiIGxvY2FsSWRlbnRpZmllcj0iRkhJUiIgdXJpPSJodHRwOi8vaGw3Lm9yZy9maGlyIiB2ZXJzaW9uPSI0LjAuMSIvPgogICA8L3VzaW5ncz4KICAgPHN0YXRlbWVudHM+CiAgICAgIDxkZWYgbG9jYXRvcj0iMTM6MS0yMDo2MiIgbmFtZT0iVG9JbnRlcnZhbCIgY29udGV4dD0iUGF0aWVudCIgYWNjZXNzTGV2ZWw9IlB1YmxpYyIgeHNpOnR5cGU9IkZ1bmN0aW9uRGVmIj4KICAgICAgICAgPGV4cHJlc3Npb24gbG9jYXRvcj0iMTQ6NS0yMDo2MiIgeHNpOnR5cGU9IklmIj4KICAgICAgICAgICAgPGNvbmRpdGlvbiBsb2NhdG9yPSIxNDo4LTE0OjIxIiB4c2k6dHlwZT0iSXNOdWxsIj4KICAgICAgICAgICAgICAgPG9wZXJhbmQgbG9jYXRvcj0iMTQ6OC0xNDoxMyIgbmFtZT0icGVyaW9kIiB4c2k6dHlwZT0iT3BlcmFuZFJlZiIvPgogICAgICAgICAgICA8L2NvbmRpdGlvbj4KICAgICAgICAgICAgPHRoZW4geHNpOnR5cGU9IkFzIj4KICAgICAgICAgICAgICAgPG9wZXJhbmQgbG9jYXRvcj0iMTU6OS0xNToxMiIgeHNpOnR5cGU9Ik51bGwiLz4KICAgICAgICAgICAgICAgPGFzVHlwZVNwZWNpZmllciB4c2k6dHlwZT0iSW50ZXJ2YWxUeXBlU3BlY2lmaWVyIj4KICAgICAgICAgICAgICAgICAgPHBvaW50VHlwZSBuYW1lPSJ0OkRhdGVUaW1lIiB4c2k6dHlwZT0iTmFtZWRUeXBlU3BlY2lmaWVyIi8+CiAgICAgICAgICAgICAgIDwvYXNUeXBlU3BlY2lmaWVyPgogICAgICAgICAgICA8L3RoZW4+CiAgICAgICAgICAgIDxlbHNlIGxvY2F0b3I9IjE3OjktMjA6NjIiIHhzaTp0eXBlPSJJZiI+CiAgICAgICAgICAgICAgIDxjb25kaXRpb24gbG9jYXRvcj0iMTc6MTItMTc6MzMiIHhzaTp0eXBlPSJJc051bGwiPgogICAgICAgICAgICAgICAgICA8b3BlcmFuZCBsb2NhdG9yPSIxNzoxMi0xNzoyNSIgcGF0aD0ic3RhcnQiIHhzaTp0eXBlPSJQcm9wZXJ0eSI+CiAgICAgICAgICAgICAgICAgICAgIDxzb3VyY2UgbG9jYXRvcj0iMTc6MTItMTc6MTciIG5hbWU9InBlcmlvZCIgeHNpOnR5cGU9Ik9wZXJhbmRSZWYiLz4KICAgICAgICAgICAgICAgICAgPC9vcGVyYW5kPgogICAgICAgICAgICAgICA8L2NvbmRpdGlvbj4KICAgICAgICAgICAgICAgPHRoZW4gbG9jYXRvcj0iMTg6MTMtMTg6NjIiIGxvd0Nsb3NlZD0iZmFsc2UiIGhpZ2hDbG9zZWQ9InRydWUiIHhzaTp0eXBlPSJJbnRlcnZhbCI+CiAgICAgICAgICAgICAgICAgIDxsb3cgbG9jYXRvcj0iMTg6MjItMTg6NDEiIHBhdGg9InZhbHVlIiB4c2k6dHlwZT0iUHJvcGVydHkiPgogICAgICAgICAgICAgICAgICAgICA8c291cmNlIGxvY2F0b3I9IjE4OjIyLTE4OjM1IiBwYXRoPSJzdGFydCIgeHNpOnR5cGU9IlByb3BlcnR5Ij4KICAgICAgICAgICAgICAgICAgICAgICAgPHNvdXJjZSBsb2NhdG9yPSIxODoyMi0xODoyNyIgbmFtZT0icGVyaW9kIiB4c2k6dHlwZT0iT3BlcmFuZFJlZiIvPgogICAgICAgICAgICAgICAgICAgICA8L3NvdXJjZT4KICAgICAgICAgICAgICAgICAgPC9sb3c+CiAgICAgICAgICAgICAgICAgIDxoaWdoIGxvY2F0b3I9IjE4OjQ0LTE4OjYxIiBwYXRoPSJ2YWx1ZSIgeHNpOnR5cGU9IlByb3BlcnR5Ij4KICAgICAgICAgICAgICAgICAgICAgPHNvdXJjZSBsb2NhdG9yPSIxODo0NC0xODo1NSIgcGF0aD0iZW5kIiB4c2k6dHlwZT0iUHJvcGVydHkiPgogICAgICAgICAgICAgICAgICAgICAgICA8c291cmNlIGxvY2F0b3I9IjE4OjQ0LTE4OjQ5IiBuYW1lPSJwZXJpb2QiIHhzaTp0eXBlPSJPcGVyYW5kUmVmIi8+CiAgICAgICAgICAgICAgICAgICAgIDwvc291cmNlPgogICAgICAgICAgICAgICAgICA8L2hpZ2g+CiAgICAgICAgICAgICAgIDwvdGhlbj4KICAgICAgICAgICAgICAgPGVsc2UgbG9jYXRvcj0iMjA6MTMtMjA6NjIiIGxvd0Nsb3NlZD0idHJ1ZSIgaGlnaENsb3NlZD0idHJ1ZSIgeHNpOnR5cGU9IkludGVydmFsIj4KICAgICAgICAgICAgICAgICAgPGxvdyBsb2NhdG9yPSIyMDoyMi0yMDo0MSIgcGF0aD0idmFsdWUiIHhzaTp0eXBlPSJQcm9wZXJ0eSI+CiAgICAgICAgICAgICAgICAgICAgIDxzb3VyY2UgbG9jYXRvcj0iMjA6MjItMjA6MzUiIHBhdGg9InN0YXJ0IiB4c2k6dHlwZT0iUHJvcGVydHkiPgogICAgICAgICAgICAgICAgICAgICAgICA8c291cmNlIGxvY2F0b3I9IjIwOjIyLTIwOjI3IiBuYW1lPSJwZXJpb2QiIHhzaTp0eXBlPSJPcGVyYW5kUmVmIi8+CiAgICAgICAgICAgICAgICAgICAgIDwvc291cmNlPgogICAgICAgICAgICAgICAgICA8L2xvdz4KICAgICAgICAgICAgICAgICAgPGhpZ2ggbG9jYXRvcj0iMjA6NDQtMjA6NjEiIHBhdGg9InZhbHVlIiB4c2k6dHlwZT0iUHJvcGVydHkiPgogICAgICAgICAgICAgICAgICAgICA8c291cmNlIGxvY2F0b3I9IjIwOjQ0LTIwOjU1IiBwYXRoPSJlbmQiIHhzaTp0eXBlPSJQcm9wZXJ0eSI+CiAgICAgICAgICAgICAgICAgICAgICAgIDxzb3VyY2UgbG9jYXRvcj0iMjA6NDQtMjA6NDkiIG5hbWU9InBlcmlvZCIgeHNpOnR5cGU9Ik9wZXJhbmRSZWYiLz4KICAgICAgICAgICAgICAgICAgICAgPC9zb3VyY2U+CiAgICAgICAgICAgICAgICAgIDwvaGlnaD4KICAgICAgICAgICAgICAgPC9lbHNlPgogICAgICAgICAgICA8L2Vsc2U+CiAgICAgICAgIDwvZXhwcmVzc2lvbj4KICAgICAgICAgPG9wZXJhbmQgbmFtZT0icGVyaW9kIj4KICAgICAgICAgICAgPG9wZXJhbmRUeXBlU3BlY2lmaWVyIGxvY2F0b3I9IjEzOjM1LTEzOjQ1IiBuYW1lPSJmaGlyOlBlcmlvZCIgeHNpOnR5cGU9Ik5hbWVkVHlwZVNwZWNpZmllciIvPgogICAgICAgICA8L29wZXJhbmQ+CiAgICAgIDwvZGVmPgogICAgICA8ZGVmIGxvY2F0b3I9IjIyOjEtMzM6NyIgbmFtZT0iVG9DYWxlbmRhclVuaXQiIGNvbnRleHQ9IlBhdGllbnQiIGFjY2Vzc0xldmVsPSJQdWJsaWMiIHhzaTp0eXBlPSJGdW5jdGlvbkRlZiI+CiAgICAgICAgIDxleHByZXNzaW9uIGxvY2F0b3I9IjIzOjUtMzM6NyIgeHNpOnR5cGU9IkNhc2UiPgogICAgICAgICAgICA8Y29tcGFyYW5kIGxvY2F0b3I9IjIzOjEwLTIzOjEzIiBuYW1lPSJ1bml0IiB4c2k6dHlwZT0iT3BlcmFuZFJlZiIvPgogICAgICAgICAgICA8Y2FzZUl0ZW0gbG9jYXRvcj0iMjQ6OS0yNDozNiI+CiAgICAgICAgICAgICAgIDx3aGVuIGxvY2F0b3I9IjI0OjE0LTI0OjE3IiB2YWx1ZVR5cGU9InQ6U3RyaW5nIiB2YWx1ZT0ibXMiIHhzaTp0eXBlPSJMaXRlcmFsIi8+CiAgICAgICAgICAgICAgIDx0aGVuIGxvY2F0b3I9IjI0OjI0LTI0OjM2IiB2YWx1ZVR5cGU9InQ6U3RyaW5nIiB2YWx1ZT0ibWlsbGlzZWNvbmQiIHhzaTp0eXBlPSJMaXRlcmFsIi8+CiAgICAgICAgICAgIDwvY2FzZUl0ZW0+CiAgICAgICAgICAgIDxjYXNlSXRlbSBsb2NhdG9yPSIyNTo5LTI1OjMwIj4KICAgICAgICAgICAgICAgPHdoZW4gbG9jYXRvcj0iMjU6MTQtMjU6MTYiIHZhbHVlVHlwZT0idDpTdHJpbmciIHZhbHVlPSJzIiB4c2k6dHlwZT0iTGl0ZXJhbCIvPgogICAgICAgICAgICAgICA8dGhlbiBsb2NhdG9yPSIyNToyMy0yNTozMCIgdmFsdWVUeXBlPSJ0OlN0cmluZyIgdmFsdWU9InNlY29uZCIgeHNpOnR5cGU9IkxpdGVyYWwiLz4KICAgICAgICAgICAgPC9jYXNlSXRlbT4KICAgICAgICAgICAgPGNhc2VJdGVtIGxvY2F0b3I9IjI2OjktMjY6MzIiPgogICAgICAgICAgICAgICA8d2hlbiBsb2NhdG9yPSIyNjoxNC0yNjoxOCIgdmFsdWVUeXBlPSJ0OlN0cmluZyIgdmFsdWU9Im1pbiIgeHNpOnR5cGU9IkxpdGVyYWwiLz4KICAgICAgICAgICAgICAgPHRoZW4gbG9jYXRvcj0iMjY6MjUtMjY6MzIiIHZhbHVlVHlwZT0idDpTdHJpbmciIHZhbHVlPSJtaW51dGUiIHhzaTp0eXBlPSJMaXRlcmFsIi8+CiAgICAgICAgICAgIDwvY2FzZUl0ZW0+CiAgICAgICAgICAgIDxjYXNlSXRlbSBsb2NhdG9yPSIyNzo5LTI3OjI4Ij4KICAgICAgICAgICAgICAgPHdoZW4gbG9jYXRvcj0iMjc6MTQtMjc6MTYiIHZhbHVlVHlwZT0idDpTdHJpbmciIHZhbHVlPSJoIiB4c2k6dHlwZT0iTGl0ZXJhbCIvPgogICAgICAgICAgICAgICA8dGhlbiBsb2NhdG9yPSIyNzoyMy0yNzoyOCIgdmFsdWVUeXBlPSJ0OlN0cmluZyIgdmFsdWU9ImhvdXIiIHhzaTp0eXBlPSJMaXRlcmFsIi8+CiAgICAgICAgICAgIDwvY2FzZUl0ZW0+CiAgICAgICAgICAgIDxjYXNlSXRlbSBsb2NhdG9yPSIyODo5LTI4OjI3Ij4KICAgICAgICAgICAgICAgPHdoZW4gbG9jYXRvcj0iMjg6MTQtMjg6MTYiIHZhbHVlVHlwZT0idDpTdHJpbmciIHZhbHVlPSJkIiB4c2k6dHlwZT0iTGl0ZXJhbCIvPgogICAgICAgICAgICAgICA8dGhlbiBsb2NhdG9yPSIyODoyMy0yODoyNyIgdmFsdWVUeXBlPSJ0OlN0cmluZyIgdmFsdWU9ImRheSIgeHNpOnR5cGU9IkxpdGVyYWwiLz4KICAgICAgICAgICAgPC9jYXNlSXRlbT4KICAgICAgICAgICAgPGNhc2VJdGVtIGxvY2F0b3I9IjI5OjktMjk6MjkiPgogICAgICAgICAgICAgICA8d2hlbiBsb2NhdG9yPSIyOToxNC0yOToxNyIgdmFsdWVUeXBlPSJ0OlN0cmluZyIgdmFsdWU9IndrIiB4c2k6dHlwZT0iTGl0ZXJhbCIvPgogICAgICAgICAgICAgICA8dGhlbiBsb2NhdG9yPSIyOToyNC0yOToyOSIgdmFsdWVUeXBlPSJ0OlN0cmluZyIgdmFsdWU9IndlZWsiIHhzaTp0eXBlPSJMaXRlcmFsIi8+CiAgICAgICAgICAgIDwvY2FzZUl0ZW0+CiAgICAgICAgICAgIDxjYXNlSXRlbSBsb2NhdG9yPSIzMDo5LTMwOjMwIj4KICAgICAgICAgICAgICAgPHdoZW4gbG9jYXRvcj0iMzA6MTQtMzA6MTciIHZhbHVlVHlwZT0idDpTdHJpbmciIHZhbHVlPSJtbyIgeHNpOnR5cGU9IkxpdGVyYWwiLz4KICAgICAgICAgICAgICAgPHRoZW4gbG9jYXRvcj0iMzA6MjQtMzA6MzAiIHZhbHVlVHlwZT0idDpTdHJpbmciIHZhbHVlPSJtb250aCIgeHNpOnR5cGU9IkxpdGVyYWwiLz4KICAgICAgICAgICAgPC9jYXNlSXRlbT4KICAgICAgICAgICAgPGNhc2VJdGVtIGxvY2F0b3I9IjMxOjktMzE6MjgiPgogICAgICAgICAgICAgICA8d2hlbiBsb2NhdG9yPSIzMToxNC0zMToxNiIgdmFsdWVUeXBlPSJ0OlN0cmluZyIgdmFsdWU9ImEiIHhzaTp0eXBlPSJMaXRlcmFsIi8+CiAgICAgICAgICAgICAgIDx0aGVuIGxvY2F0b3I9IjMxOjIzLTMxOjI4IiB2YWx1ZVR5cGU9InQ6U3RyaW5nIiB2YWx1ZT0ieWVhciIgeHNpOnR5cGU9IkxpdGVyYWwiLz4KICAgICAgICAgICAgPC9jYXNlSXRlbT4KICAgICAgICAgICAgPGVsc2UgbG9jYXRvcj0iMzI6MTQtMzI6MTciIG5hbWU9InVuaXQiIHhzaTp0eXBlPSJPcGVyYW5kUmVmIi8+CiAgICAgICAgIDwvZXhwcmVzc2lvbj4KICAgICAgICAgPG9wZXJhbmQgbmFtZT0idW5pdCI+CiAgICAgICAgICAgIDxvcGVyYW5kVHlwZVNwZWNpZmllciBsb2NhdG9yPSIyMjozNy0yMjo0OSIgbmFtZT0idDpTdHJpbmciIHhzaTp0eXBlPSJOYW1lZFR5cGVTcGVjaWZpZXIiLz4KICAgICAgICAgPC9vcGVyYW5kPgogICAgICA8L2RlZj4KICAgICAgPGRlZiBsb2NhdG9yPSIzNToxLTQ2OjciIG5hbWU9IlRvUXVhbnRpdHkiIGNvbnRleHQ9IlBhdGllbnQiIGFjY2Vzc0xldmVsPSJQdWJsaWMiIHhzaTp0eXBlPSJGdW5jdGlvbkRlZiI+CiAgICAgICAgIDxleHByZXNzaW9uIGxvY2F0b3I9IjM2OjUtNDY6NyIgeHNpOnR5cGU9IkNhc2UiPgogICAgICAgICAgICA8Y2FzZUl0ZW0gbG9jYXRvcj0iMzc6OS0zNzozOSI+CiAgICAgICAgICAgICAgIDx3aGVuIGxvY2F0b3I9IjM3OjE0LTM3OjI5IiB4c2k6dHlwZT0iSXNOdWxsIj4KICAgICAgICAgICAgICAgICAgPG9wZXJhbmQgbG9jYXRvcj0iMzc6MTQtMzc6MjEiIG5hbWU9InF1YW50aXR5IiB4c2k6dHlwZT0iT3BlcmFuZFJlZiIvPgogICAgICAgICAgICAgICA8L3doZW4+CiAgICAgICAgICAgICAgIDx0aGVuIGFzVHlwZT0idDpRdWFudGl0eSIgeHNpOnR5cGU9IkFzIj4KICAgICAgICAgICAgICAgICAgPG9wZXJhbmQgbG9jYXRvcj0iMzc6MzYtMzc6MzkiIHhzaTp0eXBlPSJOdWxsIi8+CiAgICAgICAgICAgICAgIDwvdGhlbj4KICAgICAgICAgICAgPC9jYXNlSXRlbT4KICAgICAgICAgICAgPGNhc2VJdGVtIGxvY2F0b3I9IjM4OjktMzg6NDUiPgogICAgICAgICAgICAgICA8d2hlbiBsb2NhdG9yPSIzODoxNC0zODozNSIgeHNpOnR5cGU9IklzTnVsbCI+CiAgICAgICAgICAgICAgICAgIDxvcGVyYW5kIGxvY2F0b3I9IjM4OjE0LTM4OjI3IiBwYXRoPSJ2YWx1ZSIgeHNpOnR5cGU9IlByb3BlcnR5Ij4KICAgICAgICAgICAgICAgICAgICAgPHNvdXJjZSBsb2NhdG9yPSIzODoxNC0zODoyMSIgbmFtZT0icXVhbnRpdHkiIHhzaTp0eXBlPSJPcGVyYW5kUmVmIi8+CiAgICAgICAgICAgICAgICAgIDwvb3BlcmFuZD4KICAgICAgICAgICAgICAgPC93aGVuPgogICAgICAgICAgICAgICA8dGhlbiBhc1R5cGU9InQ6UXVhbnRpdHkiIHhzaTp0eXBlPSJBcyI+CiAgICAgICAgICAgICAgICAgIDxvcGVyYW5kIGxvY2F0b3I9IjM4OjQyLTM4OjQ1IiB4c2k6dHlwZT0iTnVsbCIvPgogICAgICAgICAgICAgICA8L3RoZW4+CiAgICAgICAgICAgIDwvY2FzZUl0ZW0+CiAgICAgICAgICAgIDxjYXNlSXRlbSBsb2NhdG9yPSIzOTo5LTQwOjE4OSI+CiAgICAgICAgICAgICAgIDx3aGVuIGxvY2F0b3I9IjM5OjE0LTM5OjQ0IiB4c2k6dHlwZT0iTm90Ij4KICAgICAgICAgICAgICAgICAgPG9wZXJhbmQgbG9jYXRvcj0iMzk6MTQtMzk6NDQiIHhzaTp0eXBlPSJJc051bGwiPgogICAgICAgICAgICAgICAgICAgICA8b3BlcmFuZCBsb2NhdG9yPSIzOToxNC0zOTozMiIgcGF0aD0iY29tcGFyYXRvciIgeHNpOnR5cGU9IlByb3BlcnR5Ij4KICAgICAgICAgICAgICAgICAgICAgICAgPHNvdXJjZSBsb2NhdG9yPSIzOToxNC0zOToyMSIgbmFtZT0icXVhbnRpdHkiIHhzaTp0eXBlPSJPcGVyYW5kUmVmIi8+CiAgICAgICAgICAgICAgICAgICAgIDwvb3BlcmFuZD4KICAgICAgICAgICAgICAgICAgPC9vcGVyYW5kPgogICAgICAgICAgICAgICA8L3doZW4+CiAgICAgICAgICAgICAgIDx0aGVuIGFzVHlwZT0idDpRdWFudGl0eSIgeHNpOnR5cGU9IkFzIj4KICAgICAgICAgICAgICAgICAgPG9wZXJhbmQgbG9jYXRvcj0iNDA6MTMtNDA6MTg5IiB4c2k6dHlwZT0iTWVzc2FnZSI+CiAgICAgICAgICAgICAgICAgICAgIDxzb3VyY2UgbG9jYXRvcj0iNDA6MjEtNDA6MjQiIHhzaTp0eXBlPSJOdWxsIi8+CiAgICAgICAgICAgICAgICAgICAgIDxjb25kaXRpb24gbG9jYXRvcj0iNDA6MjctNDA6MzAiIHZhbHVlVHlwZT0idDpCb29sZWFuIiB2YWx1ZT0idHJ1ZSIgeHNpOnR5cGU9IkxpdGVyYWwiLz4KICAgICAgICAgICAgICAgICAgICAgPGNvZGUgbG9jYXRvcj0iNDA6MzMtNDA6ODciIHZhbHVlVHlwZT0idDpTdHJpbmciIHZhbHVlPSJGSElSSGVscGVycy5Ub1F1YW50aXR5LkNvbXBhcmF0b3JRdWFudGl0eU5vdFN1cHBvcnRlZCIgeHNpOnR5cGU9IkxpdGVyYWwiLz4KICAgICAgICAgICAgICAgICAgICAgPHNldmVyaXR5IGxvY2F0b3I9IjQwOjkwLTQwOjk2IiB2YWx1ZVR5cGU9InQ6U3RyaW5nIiB2YWx1ZT0iRXJyb3IiIHhzaTp0eXBlPSJMaXRlcmFsIi8+CiAgICAgICAgICAgICAgICAgICAgIDxtZXNzYWdlIGxvY2F0b3I9IjQwOjk5LTQwOjE4OCIgdmFsdWVUeXBlPSJ0OlN0cmluZyIgdmFsdWU9IkZISVIgUXVhbnRpdHkgdmFsdWUgaGFzIGEgY29tcGFyYXRvciBhbmQgY2Fubm90IGJlIGNvbnZlcnRlZCB0byBhIFN5c3RlbS5RdWFudGl0eSB2YWx1ZS4iIHhzaTp0eXBlPSJMaXRlcmFsIi8+CiAgICAgICAgICAgICAgICAgIDwvb3BlcmFuZD4KICAgICAgICAgICAgICAgPC90aGVuPgogICAgICAgICAgICA8L2Nhc2VJdGVtPgogICAgICAgICAgICA8Y2FzZUl0ZW0gbG9jYXRvcj0iNDE6OS00MzoxMzgiPgogICAgICAgICAgICAgICA8d2hlbiBsb2NhdG9yPSI0MToxNC00Mjo5MiIgeHNpOnR5cGU9Ik9yIj4KICAgICAgICAgICAgICAgICAgPG9wZXJhbmQgbG9jYXRvcj0iNDE6MTQtNDE6OTEiIHhzaTp0eXBlPSJPciI+CiAgICAgICAgICAgICAgICAgICAgIDxvcGVyYW5kIGxvY2F0b3I9IjQxOjE0LTQxOjM2IiB4c2k6dHlwZT0iSXNOdWxsIj4KICAgICAgICAgICAgICAgICAgICAgICAgPG9wZXJhbmQgbG9jYXRvcj0iNDE6MTQtNDE6MjgiIHBhdGg9InN5c3RlbSIgeHNpOnR5cGU9IlByb3BlcnR5Ij4KICAgICAgICAgICAgICAgICAgICAgICAgICAgPHNvdXJjZSBsb2NhdG9yPSI0MToxNC00MToyMSIgbmFtZT0icXVhbnRpdHkiIHhzaTp0eXBlPSJPcGVyYW5kUmVmIi8+CiAgICAgICAgICAgICAgICAgICAgICAgIDwvb3BlcmFuZD4KICAgICAgICAgICAgICAgICAgICAgPC9vcGVyYW5kPgogICAgICAgICAgICAgICAgICAgICA8b3BlcmFuZCBsb2NhdG9yPSI0MTo0MS00MTo5MSIgeHNpOnR5cGU9IkVxdWFsIj4KICAgICAgICAgICAgICAgICAgICAgICAgPG9wZXJhbmQgbG9jYXRvcj0iNDE6NDEtNDE6NjEiIHBhdGg9InZhbHVlIiB4c2k6dHlwZT0iUHJvcGVydHkiPgogICAgICAgICAgICAgICAgICAgICAgICAgICA8c291cmNlIGxvY2F0b3I9IjQxOjQxLTQxOjU1IiBwYXRoPSJzeXN0ZW0iIHhzaTp0eXBlPSJQcm9wZXJ0eSI+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDxzb3VyY2UgbG9jYXRvcj0iNDE6NDEtNDE6NDgiIG5hbWU9InF1YW50aXR5IiB4c2k6dHlwZT0iT3BlcmFuZFJlZiIvPgogICAgICAgICAgICAgICAgICAgICAgICAgICA8L3NvdXJjZT4KICAgICAgICAgICAgICAgICAgICAgICAgPC9vcGVyYW5kPgogICAgICAgICAgICAgICAgICAgICAgICA8b3BlcmFuZCBsb2NhdG9yPSI0MTo2NS00MTo5MSIgdmFsdWVUeXBlPSJ0OlN0cmluZyIgdmFsdWU9Imh0dHA6Ly91bml0c29mbWVhc3VyZS5vcmciIHhzaTp0eXBlPSJMaXRlcmFsIi8+CiAgICAgICAgICAgICAgICAgICAgIDwvb3BlcmFuZD4KICAgICAgICAgICAgICAgICAgPC9vcGVyYW5kPgogICAgICAgICAgICAgICAgICA8b3BlcmFuZCBsb2NhdG9yPSI0MjoxOC00Mjo5MiIgeHNpOnR5cGU9IkVxdWFsIj4KICAgICAgICAgICAgICAgICAgICAgPG9wZXJhbmQgbG9jYXRvcj0iNDI6MTgtNDI6MzgiIHBhdGg9InZhbHVlIiB4c2k6dHlwZT0iUHJvcGVydHkiPgogICAgICAgICAgICAgICAgICAgICAgICA8c291cmNlIGxvY2F0b3I9IjQyOjE4LTQyOjMyIiBwYXRoPSJzeXN0ZW0iIHhzaTp0eXBlPSJQcm9wZXJ0eSI+CiAgICAgICAgICAgICAgICAgICAgICAgICAgIDxzb3VyY2UgbG9jYXRvcj0iNDI6MTgtNDI6MjUiIG5hbWU9InF1YW50aXR5IiB4c2k6dHlwZT0iT3BlcmFuZFJlZiIvPgogICAgICAgICAgICAgICAgICAgICAgICA8L3NvdXJjZT4KICAgICAgICAgICAgICAgICAgICAgPC9vcGVyYW5kPgogICAgICAgICAgICAgICAgICAgICA8b3BlcmFuZCBsb2NhdG9yPSI0Mjo0Mi00Mjo5MiIgdmFsdWVUeXBlPSJ0OlN0cmluZyIgdmFsdWU9Imh0dHA6Ly9obDcub3JnL2ZoaXJwYXRoL0NvZGVTeXN0ZW0vY2FsZW5kYXItdW5pdHMiIHhzaTp0eXBlPSJMaXRlcmFsIi8+CiAgICAgICAgICAgICAgICAgIDwvb3BlcmFuZD4KICAgICAgICAgICAgICAgPC93aGVuPgogICAgICAgICAgICAgICA8dGhlbiBsb2NhdG9yPSI0MzoxMy00MzoxMzgiIGNsYXNzVHlwZT0idDpRdWFudGl0eSIgeHNpOnR5cGU9Ikluc3RhbmNlIj4KICAgICAgICAgICAgICAgICAgPGVsZW1lbnQgbmFtZT0idmFsdWUiPgogICAgICAgICAgICAgICAgICAgICA8dmFsdWUgbG9jYXRvcj0iNDM6MzgtNDM6NTciIHBhdGg9InZhbHVlIiB4c2k6dHlwZT0iUHJvcGVydHkiPgogICAgICAgICAgICAgICAgICAgICAgICA8c291cmNlIGxvY2F0b3I9IjQzOjM4LTQzOjUxIiBwYXRoPSJ2YWx1ZSIgeHNpOnR5cGU9IlByb3BlcnR5Ij4KICAgICAgICAgICAgICAgICAgICAgICAgICAgPHNvdXJjZSBsb2NhdG9yPSI0MzozOC00Mzo0NSIgbmFtZT0icXVhbnRpdHkiIHhzaTp0eXBlPSJPcGVyYW5kUmVmIi8+CiAgICAgICAgICAgICAgICAgICAgICAgIDwvc291cmNlPgogICAgICAgICAgICAgICAgICAgICA8L3ZhbHVlPgogICAgICAgICAgICAgICAgICA8L2VsZW1lbnQ+CiAgICAgICAgICAgICAgICAgIDxlbGVtZW50IG5hbWU9InVuaXQiPgogICAgICAgICAgICAgICAgICAgICA8dmFsdWUgbG9jYXRvcj0iNDM6NjYtNDM6MTM2IiBuYW1lPSJUb0NhbGVuZGFyVW5pdCIgeHNpOnR5cGU9IkZ1bmN0aW9uUmVmIj4KICAgICAgICAgICAgICAgICAgICAgICAgPG9wZXJhbmQgbG9jYXRvcj0iNDM6ODEtNDM6MTM1IiB4c2k6dHlwZT0iQ29hbGVzY2UiPgogICAgICAgICAgICAgICAgICAgICAgICAgICA8b3BlcmFuZCBsb2NhdG9yPSI0Mzo5MC00MzoxMDgiIHBhdGg9InZhbHVlIiB4c2k6dHlwZT0iUHJvcGVydHkiPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8c291cmNlIGxvY2F0b3I9IjQzOjkwLTQzOjEwMiIgcGF0aD0iY29kZSIgeHNpOnR5cGU9IlByb3BlcnR5Ij4KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPHNvdXJjZSBsb2NhdG9yPSI0Mzo5MC00Mzo5NyIgbmFtZT0icXVhbnRpdHkiIHhzaTp0eXBlPSJPcGVyYW5kUmVmIi8+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDwvc291cmNlPgogICAgICAgICAgICAgICAgICAgICAgICAgICA8L29wZXJhbmQ+CiAgICAgICAgICAgICAgICAgICAgICAgICAgIDxvcGVyYW5kIGxvY2F0b3I9IjQzOjExMS00MzoxMjkiIHBhdGg9InZhbHVlIiB4c2k6dHlwZT0iUHJvcGVydHkiPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8c291cmNlIGxvY2F0b3I9IjQzOjExMS00MzoxMjMiIHBhdGg9InVuaXQiIHhzaTp0eXBlPSJQcm9wZXJ0eSI+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDxzb3VyY2UgbG9jYXRvcj0iNDM6MTExLTQzOjExOCIgbmFtZT0icXVhbnRpdHkiIHhzaTp0eXBlPSJPcGVyYW5kUmVmIi8+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDwvc291cmNlPgogICAgICAgICAgICAgICAgICAgICAgICAgICA8L29wZXJhbmQ+CiAgICAgICAgICAgICAgICAgICAgICAgICAgIDxvcGVyYW5kIGxvY2F0b3I9IjQzOjEzMi00MzoxMzQiIHZhbHVlVHlwZT0idDpTdHJpbmciIHZhbHVlPSIxIiB4c2k6dHlwZT0iTGl0ZXJhbCIvPgogICAgICAgICAgICAgICAgICAgICAgICA8L29wZXJhbmQ+CiAgICAgICAgICAgICAgICAgICAgIDwvdmFsdWU+CiAgICAgICAgICAgICAgICAgIDwvZWxlbWVudD4KICAgICAgICAgICAgICAgPC90aGVuPgogICAgICAgICAgICA8L2Nhc2VJdGVtPgogICAgICAgICAgICA8ZWxzZSBhc1R5cGU9InQ6UXVhbnRpdHkiIHhzaTp0eXBlPSJBcyI+CiAgICAgICAgICAgICAgIDxvcGVyYW5kIGxvY2F0b3I9IjQ1OjEzLTQ1OjIwNSIgeHNpOnR5cGU9Ik1lc3NhZ2UiPgogICAgICAgICAgICAgICAgICA8c291cmNlIGxvY2F0b3I9IjQ1OjIxLTQ1OjI0IiB4c2k6dHlwZT0iTnVsbCIvPgogICAgICAgICAgICAgICAgICA8Y29uZGl0aW9uIGxvY2F0b3I9IjQ1OjI3LTQ1OjMwIiB2YWx1ZVR5cGU9InQ6Qm9vbGVhbiIgdmFsdWU9InRydWUiIHhzaTp0eXBlPSJMaXRlcmFsIi8+CiAgICAgICAgICAgICAgICAgIDxjb2RlIGxvY2F0b3I9IjQ1OjMzLTQ1Ojc2IiB2YWx1ZVR5cGU9InQ6U3RyaW5nIiB2YWx1ZT0iRkhJUkhlbHBlcnMuVG9RdWFudGl0eS5JbnZhbGlkRkhJUlF1YW50aXR5IiB4c2k6dHlwZT0iTGl0ZXJhbCIvPgogICAgICAgICAgICAgICAgICA8c2V2ZXJpdHkgbG9jYXRvcj0iNDU6NzktNDU6ODUiIHZhbHVlVHlwZT0idDpTdHJpbmciIHZhbHVlPSJFcnJvciIgeHNpOnR5cGU9IkxpdGVyYWwiLz4KICAgICAgICAgICAgICAgICAgPG1lc3NhZ2UgbG9jYXRvcj0iNDU6ODgtNDU6MjA0IiB4c2k6dHlwZT0iQ29uY2F0ZW5hdGUiPgogICAgICAgICAgICAgICAgICAgICA8b3BlcmFuZCB4c2k6dHlwZT0iQ29hbGVzY2UiPgogICAgICAgICAgICAgICAgICAgICAgICA8b3BlcmFuZCBsb2NhdG9yPSI0NTo4OC00NToxOTgiIHhzaTp0eXBlPSJDb25jYXRlbmF0ZSI+CiAgICAgICAgICAgICAgICAgICAgICAgICAgIDxvcGVyYW5kIHhzaTp0eXBlPSJDb2FsZXNjZSI+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDxvcGVyYW5kIGxvY2F0b3I9IjQ1Ojg4LTQ1OjE3NiIgeHNpOnR5cGU9IkNvbmNhdGVuYXRlIj4KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPG9wZXJhbmQgeHNpOnR5cGU9IkNvYWxlc2NlIj4KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPG9wZXJhbmQgbG9jYXRvcj0iNDU6ODgtNDU6MTcwIiB4c2k6dHlwZT0iQ29uY2F0ZW5hdGUiPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8b3BlcmFuZCB4c2k6dHlwZT0iQ29hbGVzY2UiPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8b3BlcmFuZCBsb2NhdG9yPSI0NTo4OC00NToxNDYiIHhzaTp0eXBlPSJDb25jYXRlbmF0ZSI+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDxvcGVyYW5kIHhzaTp0eXBlPSJDb2FsZXNjZSI+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDxvcGVyYW5kIGxvY2F0b3I9IjQ1Ojg4LTQ1OjEzOSIgeHNpOnR5cGU9IkNvbmNhdGVuYXRlIj4KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPG9wZXJhbmQgeHNpOnR5cGU9IkNvYWxlc2NlIj4KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPG9wZXJhbmQgbG9jYXRvcj0iNDU6ODgtNDU6MTE3IiB2YWx1ZVR5cGU9InQ6U3RyaW5nIiB2YWx1ZT0iSW52YWxpZCBGSElSIFF1YW50aXR5IGNvZGU6ICIgeHNpOnR5cGU9IkxpdGVyYWwiLz4KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPG9wZXJhbmQgdmFsdWVUeXBlPSJ0OlN0cmluZyIgdmFsdWU9IiIgeHNpOnR5cGU9IkxpdGVyYWwiLz4KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPC9vcGVyYW5kPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8b3BlcmFuZCB4c2k6dHlwZT0iQ29hbGVzY2UiPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8b3BlcmFuZCBsb2NhdG9yPSI0NToxMjEtNDU6MTM5IiBwYXRoPSJ2YWx1ZSIgeHNpOnR5cGU9IlByb3BlcnR5Ij4KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPHNvdXJjZSBsb2NhdG9yPSI0NToxMjEtNDU6MTMzIiBwYXRoPSJ1bml0IiB4c2k6dHlwZT0iUHJvcGVydHkiPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8c291cmNlIGxvY2F0b3I9IjQ1OjEyMS00NToxMjgiIG5hbWU9InF1YW50aXR5IiB4c2k6dHlwZT0iT3BlcmFuZFJlZiIvPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8L3NvdXJjZT4KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPC9vcGVyYW5kPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8b3BlcmFuZCB2YWx1ZVR5cGU9InQ6U3RyaW5nIiB2YWx1ZT0iIiB4c2k6dHlwZT0iTGl0ZXJhbCIvPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8L29wZXJhbmQ+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDwvb3BlcmFuZD4KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPG9wZXJhbmQgdmFsdWVUeXBlPSJ0OlN0cmluZyIgdmFsdWU9IiIgeHNpOnR5cGU9IkxpdGVyYWwiLz4KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPC9vcGVyYW5kPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8b3BlcmFuZCB4c2k6dHlwZT0iQ29hbGVzY2UiPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8b3BlcmFuZCBsb2NhdG9yPSI0NToxNDMtNDU6MTQ2IiB2YWx1ZVR5cGU9InQ6U3RyaW5nIiB2YWx1ZT0iICgiIHhzaTp0eXBlPSJMaXRlcmFsIi8+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDxvcGVyYW5kIHZhbHVlVHlwZT0idDpTdHJpbmciIHZhbHVlPSIiIHhzaTp0eXBlPSJMaXRlcmFsIi8+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDwvb3BlcmFuZD4KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPC9vcGVyYW5kPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8b3BlcmFuZCB2YWx1ZVR5cGU9InQ6U3RyaW5nIiB2YWx1ZT0iIiB4c2k6dHlwZT0iTGl0ZXJhbCIvPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8L29wZXJhbmQ+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDxvcGVyYW5kIHhzaTp0eXBlPSJDb2FsZXNjZSI+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDxvcGVyYW5kIGxvY2F0b3I9IjQ1OjE1MC00NToxNzAiIHBhdGg9InZhbHVlIiB4c2k6dHlwZT0iUHJvcGVydHkiPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8c291cmNlIGxvY2F0b3I9IjQ1OjE1MC00NToxNjQiIHBhdGg9InN5c3RlbSIgeHNpOnR5cGU9IlByb3BlcnR5Ij4KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPHNvdXJjZSBsb2NhdG9yPSI0NToxNTAtNDU6MTU3IiBuYW1lPSJxdWFudGl0eSIgeHNpOnR5cGU9Ik9wZXJhbmRSZWYiLz4KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPC9zb3VyY2U+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDwvb3BlcmFuZD4KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPG9wZXJhbmQgdmFsdWVUeXBlPSJ0OlN0cmluZyIgdmFsdWU9IiIgeHNpOnR5cGU9IkxpdGVyYWwiLz4KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPC9vcGVyYW5kPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8L29wZXJhbmQ+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDxvcGVyYW5kIHZhbHVlVHlwZT0idDpTdHJpbmciIHZhbHVlPSIiIHhzaTp0eXBlPSJMaXRlcmFsIi8+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDwvb3BlcmFuZD4KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPG9wZXJhbmQgeHNpOnR5cGU9IkNvYWxlc2NlIj4KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPG9wZXJhbmQgbG9jYXRvcj0iNDU6MTc0LTQ1OjE3NiIgdmFsdWVUeXBlPSJ0OlN0cmluZyIgdmFsdWU9InwiIHhzaTp0eXBlPSJMaXRlcmFsIi8+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDxvcGVyYW5kIHZhbHVlVHlwZT0idDpTdHJpbmciIHZhbHVlPSIiIHhzaTp0eXBlPSJMaXRlcmFsIi8+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDwvb3BlcmFuZD4KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPC9vcGVyYW5kPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8b3BlcmFuZCB2YWx1ZVR5cGU9InQ6U3RyaW5nIiB2YWx1ZT0iIiB4c2k6dHlwZT0iTGl0ZXJhbCIvPgogICAgICAgICAgICAgICAgICAgICAgICAgICA8L29wZXJhbmQ+CiAgICAgICAgICAgICAgICAgICAgICAgICAgIDxvcGVyYW5kIHhzaTp0eXBlPSJDb2FsZXNjZSI+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDxvcGVyYW5kIGxvY2F0b3I9IjQ1OjE4MC00NToxOTgiIHBhdGg9InZhbHVlIiB4c2k6dHlwZT0iUHJvcGVydHkiPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8c291cmNlIGxvY2F0b3I9IjQ1OjE4MC00NToxOTIiIHBhdGg9ImNvZGUiIHhzaTp0eXBlPSJQcm9wZXJ0eSI+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDxzb3VyY2UgbG9jYXRvcj0iNDU6MTgwLTQ1OjE4NyIgbmFtZT0icXVhbnRpdHkiIHhzaTp0eXBlPSJPcGVyYW5kUmVmIi8+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDwvc291cmNlPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8L29wZXJhbmQ+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDxvcGVyYW5kIHZhbHVlVHlwZT0idDpTdHJpbmciIHZhbHVlPSIiIHhzaTp0eXBlPSJMaXRlcmFsIi8+CiAgICAgICAgICAgICAgICAgICAgICAgICAgIDwvb3BlcmFuZD4KICAgICAgICAgICAgICAgICAgICAgICAgPC9vcGVyYW5kPgogICAgICAgICAgICAgICAgICAgICAgICA8b3BlcmFuZCB2YWx1ZVR5cGU9InQ6U3RyaW5nIiB2YWx1ZT0iIiB4c2k6dHlwZT0iTGl0ZXJhbCIvPgogICAgICAgICAgICAgICAgICAgICA8L29wZXJhbmQ+CiAgICAgICAgICAgICAgICAgICAgIDxvcGVyYW5kIHhzaTp0eXBlPSJDb2FsZXNjZSI+CiAgICAgICAgICAgICAgICAgICAgICAgIDxvcGVyYW5kIGxvY2F0b3I9IjQ1OjIwMi00NToyMDQiIHZhbHVlVHlwZT0idDpTdHJpbmciIHZhbHVlPSIpIiB4c2k6dHlwZT0iTGl0ZXJhbCIvPgogICAgICAgICAgICAgICAgICAgICAgICA8b3BlcmFuZCB2YWx1ZVR5cGU9InQ6U3RyaW5nIiB2YWx1ZT0iIiB4c2k6dHlwZT0iTGl0ZXJhbCIvPgogICAgICAgICAgICAgICAgICAgICA8L29wZXJhbmQ+CiAgICAgICAgICAgICAgICAgIDwvbWVzc2FnZT4KICAgICAgICAgICAgICAgPC9vcGVyYW5kPgogICAgICAgICAgICA8L2Vsc2U+CiAgICAgICAgIDwvZXhwcmVzc2lvbj4KICAgICAgICAgPG9wZXJhbmQgbmFtZT0icXVhbnRpdHkiPgogICAgICAgICAgICA8b3BlcmFuZFR5cGVTcGVjaWZpZXIgbG9jYXRvcj0iMzU6MzctMzU6NDkiIG5hbWU9ImZoaXI6UXVhbnRpdHkiIHhzaTp0eXBlPSJOYW1lZFR5cGVTcGVjaWZpZXIiLz4KICAgICAgICAgPC9vcGVyYW5kPgogICAgICA8L2RlZj4KICAgICAgPGRlZiBsb2NhdG9yPSI0ODoxLTU3OjciIG5hbWU9IlRvUXVhbnRpdHlJZ25vcmluZ0NvbXBhcmF0b3IiIGNvbnRleHQ9IlBhdGllbnQiIGFjY2Vzc0xldmVsPSJQdWJsaWMiIHhzaTp0eXBlPSJGdW5jdGlvbkRlZiI+CiAgICAgICAgIDxleHByZXNzaW9uIGxvY2F0b3I9IjQ5OjUtNTc6NyIgeHNpOnR5cGU9IkNhc2UiPgogICAgICAgICAgICA8Y2FzZUl0ZW0gbG9jYXRvcj0iNTA6OS01MDozOSI+CiAgICAgICAgICAgICAgIDx3aGVuIGxvY2F0b3I9IjUwOjE0LTUwOjI5IiB4c2k6dHlwZT0iSXNOdWxsIj4KICAgICAgICAgICAgICAgICAgPG9wZXJhbmQgbG9jYXRvcj0iNTA6MTQtNTA6MjEiIG5hbWU9InF1YW50aXR5IiB4c2k6dHlwZT0iT3BlcmFuZFJlZiIvPgogICAgICAgICAgICAgICA8L3doZW4+CiAgICAgICAgICAgICAgIDx0aGVuIGFzVHlwZT0idDpRdWFudGl0eSIgeHNpOnR5cGU9IkFzIj4KICAgICAgICAgICAgICAgICAgPG9wZXJhbmQgbG9jYXRvcj0iNTA6MzYtNTA6MzkiIHhzaTp0eXBlPSJOdWxsIi8+CiAgICAgICAgICAgICAgIDwvdGhlbj4KICAgICAgICAgICAgPC9jYXNlSXRlbT4KICAgICAgICAgICAgPGNhc2VJdGVtIGxvY2F0b3I9IjUxOjktNTE6NDUiPgogICAgICAgICAgICAgICA8d2hlbiBsb2NhdG9yPSI1MToxNC01MTozNSIgeHNpOnR5cGU9IklzTnVsbCI+CiAgICAgICAgICAgICAgICAgIDxvcGVyYW5kIGxvY2F0b3I9IjUxOjE0LTUxOjI3IiBwYXRoPSJ2YWx1ZSIgeHNpOnR5cGU9IlByb3BlcnR5Ij4KICAgICAgICAgICAgICAgICAgICAgPHNvdXJjZSBsb2NhdG9yPSI1MToxNC01MToyMSIgbmFtZT0icXVhbnRpdHkiIHhzaTp0eXBlPSJPcGVyYW5kUmVmIi8+CiAgICAgICAgICAgICAgICAgIDwvb3BlcmFuZD4KICAgICAgICAgICAgICAgPC93aGVuPgogICAgICAgICAgICAgICA8dGhlbiBhc1R5cGU9InQ6UXVhbnRpdHkiIHhzaTp0eXBlPSJBcyI+CiAgICAgICAgICAgICAgICAgIDxvcGVyYW5kIGxvY2F0b3I9IjUxOjQyLTUxOjQ1IiB4c2k6dHlwZT0iTnVsbCIvPgogICAgICAgICAgICAgICA8L3RoZW4+CiAgICAgICAgICAgIDwvY2FzZUl0ZW0+CiAgICAgICAgICAgIDxjYXNlSXRlbSBsb2NhdG9yPSI1Mjo5LTU0OjEzOCI+CiAgICAgICAgICAgICAgIDx3aGVuIGxvY2F0b3I9IjUyOjE0LTUzOjkyIiB4c2k6dHlwZT0iT3IiPgogICAgICAgICAgICAgICAgICA8b3BlcmFuZCBsb2NhdG9yPSI1MjoxNC01Mjo5MSIgeHNpOnR5cGU9Ik9yIj4KICAgICAgICAgICAgICAgICAgICAgPG9wZXJhbmQgbG9jYXRvcj0iNTI6MTQtNTI6MzYiIHhzaTp0eXBlPSJJc051bGwiPgogICAgICAgICAgICAgICAgICAgICAgICA8b3BlcmFuZCBsb2NhdG9yPSI1MjoxNC01MjoyOCIgcGF0aD0ic3lzdGVtIiB4c2k6dHlwZT0iUHJvcGVydHkiPgogICAgICAgICAgICAgICAgICAgICAgICAgICA8c291cmNlIGxvY2F0b3I9IjUyOjE0LTUyOjIxIiBuYW1lPSJxdWFudGl0eSIgeHNpOnR5cGU9Ik9wZXJhbmRSZWYiLz4KICAgICAgICAgICAgICAgICAgICAgICAgPC9vcGVyYW5kPgogICAgICAgICAgICAgICAgICAgICA8L29wZXJhbmQ+CiAgICAgICAgICAgICAgICAgICAgIDxvcGVyYW5kIGxvY2F0b3I9IjUyOjQxLTUyOjkxIiB4c2k6dHlwZT0iRXF1YWwiPgogICAgICAgICAgICAgICAgICAgICAgICA8b3BlcmFuZCBsb2NhdG9yPSI1Mjo0MS01Mjo2MSIgcGF0aD0idmFsdWUiIHhzaTp0eXBlPSJQcm9wZXJ0eSI+CiAgICAgICAgICAgICAgICAgICAgICAgICAgIDxzb3VyY2UgbG9jYXRvcj0iNTI6NDEtNTI6NTUiIHBhdGg9InN5c3RlbSIgeHNpOnR5cGU9IlByb3BlcnR5Ij4KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPHNvdXJjZSBsb2NhdG9yPSI1Mjo0MS01Mjo0OCIgbmFtZT0icXVhbnRpdHkiIHhzaTp0eXBlPSJPcGVyYW5kUmVmIi8+CiAgICAgICAgICAgICAgICAgICAgICAgICAgIDwvc291cmNlPgogICAgICAgICAgICAgICAgICAgICAgICA8L29wZXJhbmQ+CiAgICAgICAgICAgICAgICAgICAgICAgIDxvcGVyYW5kIGxvY2F0b3I9IjUyOjY1LTUyOjkxIiB2YWx1ZVR5cGU9InQ6U3RyaW5nIiB2YWx1ZT0iaHR0cDovL3VuaXRzb2ZtZWFzdXJlLm9yZyIgeHNpOnR5cGU9IkxpdGVyYWwiLz4KICAgICAgICAgICAgICAgICAgICAgPC9vcGVyYW5kPgogICAgICAgICAgICAgICAgICA8L29wZXJhbmQ+CiAgICAgICAgICAgICAgICAgIDxvcGVyYW5kIGxvY2F0b3I9IjUzOjE4LTUzOjkyIiB4c2k6dHlwZT0iRXF1YWwiPgogICAgICAgICAgICAgICAgICAgICA8b3BlcmFuZCBsb2NhdG9yPSI1MzoxOC01MzozOCIgcGF0aD0idmFsdWUiIHhzaTp0eXBlPSJQcm9wZXJ0eSI+CiAgICAgICAgICAgICAgICAgICAgICAgIDxzb3VyY2UgbG9jYXRvcj0iNTM6MTgtNTM6MzIiIHBhdGg9InN5c3RlbSIgeHNpOnR5cGU9IlByb3BlcnR5Ij4KICAgICAgICAgICAgICAgICAgICAgICAgICAgPHNvdXJjZSBsb2NhdG9yPSI1MzoxOC01MzoyNSIgbmFtZT0icXVhbnRpdHkiIHhzaTp0eXBlPSJPcGVyYW5kUmVmIi8+CiAgICAgICAgICAgICAgICAgICAgICAgIDwvc291cmNlPgogICAgICAgICAgICAgICAgICAgICA8L29wZXJhbmQ+CiAgICAgICAgICAgICAgICAgICAgIDxvcGVyYW5kIGxvY2F0b3I9IjUzOjQyLTUzOjkyIiB2YWx1ZVR5cGU9InQ6U3RyaW5nIiB2YWx1ZT0iaHR0cDovL2hsNy5vcmcvZmhpcnBhdGgvQ29kZVN5c3RlbS9jYWxlbmRhci11bml0cyIgeHNpOnR5cGU9IkxpdGVyYWwiLz4KICAgICAgICAgICAgICAgICAgPC9vcGVyYW5kPgogICAgICAgICAgICAgICA8L3doZW4+CiAgICAgICAgICAgICAgIDx0aGVuIGxvY2F0b3I9IjU0OjEzLTU0OjEzOCIgY2xhc3NUeXBlPSJ0OlF1YW50aXR5IiB4c2k6dHlwZT0iSW5zdGFuY2UiPgogICAgICAgICAgICAgICAgICA8ZWxlbWVudCBuYW1lPSJ2YWx1ZSI+CiAgICAgICAgICAgICAgICAgICAgIDx2YWx1ZSBsb2NhdG9yPSI1NDozOC01NDo1NyIgcGF0aD0idmFsdWUiIHhzaTp0eXBlPSJQcm9wZXJ0eSI+CiAgICAgICAgICAgICAgICAgICAgICAgIDxzb3VyY2UgbG9jYXRvcj0iNTQ6MzgtNTQ6NTEiIHBhdGg9InZhbHVlIiB4c2k6dHlwZT0iUHJvcGVydHkiPgogICAgICAgICAgICAgICAgICAgICAgICAgICA8c291cmNlIGxvY2F0b3I9IjU0OjM4LTU0OjQ1IiBuYW1lPSJxdWFudGl0eSIgeHNpOnR5cGU9Ik9wZXJhbmRSZWYiLz4KICAgICAgICAgICAgICAgICAgICAgICAgPC9zb3VyY2U+CiAgICAgICAgICAgICAgICAgICAgIDwvdmFsdWU+CiAgICAgICAgICAgICAgICAgIDwvZWxlbWVudD4KICAgICAgICAgICAgICAgICAgPGVsZW1lbnQgbmFtZT0idW5pdCI+CiAgICAgICAgICAgICAgICAgICAgIDx2YWx1ZSBsb2NhdG9yPSI1NDo2Ni01NDoxMzYiIG5hbWU9IlRvQ2FsZW5kYXJVbml0IiB4c2k6dHlwZT0iRnVuY3Rpb25SZWYiPgogICAgICAgICAgICAgICAgICAgICAgICA8b3BlcmFuZCBsb2NhdG9yPSI1NDo4MS01NDoxMzUiIHhzaTp0eXBlPSJDb2FsZXNjZSI+CiAgICAgICAgICAgICAgICAgICAgICAgICAgIDxvcGVyYW5kIGxvY2F0b3I9IjU0OjkwLTU0OjEwOCIgcGF0aD0idmFsdWUiIHhzaTp0eXBlPSJQcm9wZXJ0eSI+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDxzb3VyY2UgbG9jYXRvcj0iNTQ6OTAtNTQ6MTAyIiBwYXRoPSJjb2RlIiB4c2k6dHlwZT0iUHJvcGVydHkiPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8c291cmNlIGxvY2F0b3I9IjU0OjkwLTU0Ojk3IiBuYW1lPSJxdWFudGl0eSIgeHNpOnR5cGU9Ik9wZXJhbmRSZWYiLz4KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPC9zb3VyY2U+CiAgICAgICAgICAgICAgICAgICAgICAgICAgIDwvb3BlcmFuZD4KICAgICAgICAgICAgICAgICAgICAgICAgICAgPG9wZXJhbmQgbG9jYXRvcj0iNTQ6MTExLTU0OjEyOSIgcGF0aD0idmFsdWUiIHhzaTp0eXBlPSJQcm9wZXJ0eSI+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDxzb3VyY2UgbG9jYXRvcj0iNTQ6MTExLTU0OjEyMyIgcGF0aD0idW5pdCIgeHNpOnR5cGU9IlByb3BlcnR5Ij4KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPHNvdXJjZSBsb2NhdG9yPSI1NDoxMTEtNTQ6MTE4IiBuYW1lPSJxdWFudGl0eSIgeHNpOnR5cGU9Ik9wZXJhbmRSZWYiLz4KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPC9zb3VyY2U+CiAgICAgICAgICAgICAgICAgICAgICAgICAgIDwvb3BlcmFuZD4KICAgICAgICAgICAgICAgICAgICAgICAgICAgPG9wZXJhbmQgbG9jYXRvcj0iNTQ6MTMyLTU0OjEzNCIgdmFsdWVUeXBlPSJ0OlN0cmluZyIgdmFsdWU9IjEiIHhzaTp0eXBlPSJMaXRlcmFsIi8+CiAgICAgICAgICAgICAgICAgICAgICAgIDwvb3BlcmFuZD4KICAgICAgICAgICAgICAgICAgICAgPC92YWx1ZT4KICAgICAgICAgICAgICAgICAgPC9lbGVtZW50PgogICAgICAgICAgICAgICA8L3RoZW4+CiAgICAgICAgICAgIDwvY2FzZUl0ZW0+CiAgICAgICAgICAgIDxlbHNlIGFzVHlwZT0idDpRdWFudGl0eSIgeHNpOnR5cGU9IkFzIj4KICAgICAgICAgICAgICAgPG9wZXJhbmQgbG9jYXRvcj0iNTY6MTMtNTY6MjA1IiB4c2k6dHlwZT0iTWVzc2FnZSI+CiAgICAgICAgICAgICAgICAgIDxzb3VyY2UgbG9jYXRvcj0iNTY6MjEtNTY6MjQiIHhzaTp0eXBlPSJOdWxsIi8+CiAgICAgICAgICAgICAgICAgIDxjb25kaXRpb24gbG9jYXRvcj0iNTY6MjctNTY6MzAiIHZhbHVlVHlwZT0idDpCb29sZWFuIiB2YWx1ZT0idHJ1ZSIgeHNpOnR5cGU9IkxpdGVyYWwiLz4KICAgICAgICAgICAgICAgICAgPGNvZGUgbG9jYXRvcj0iNTY6MzMtNTY6NzYiIHZhbHVlVHlwZT0idDpTdHJpbmciIHZhbHVlPSJGSElSSGVscGVycy5Ub1F1YW50aXR5LkludmFsaWRGSElSUXVhbnRpdHkiIHhzaTp0eXBlPSJMaXRlcmFsIi8+CiAgICAgICAgICAgICAgICAgIDxzZXZlcml0eSBsb2NhdG9yPSI1Njo3OS01Njo4NSIgdmFsdWVUeXBlPSJ0OlN0cmluZyIgdmFsdWU9IkVycm9yIiB4c2k6dHlwZT0iTGl0ZXJhbCIvPgogICAgICAgICAgICAgICAgICA8bWVzc2FnZSBsb2NhdG9yPSI1Njo4OC01NjoyMDQiIHhzaTp0eXBlPSJDb25jYXRlbmF0ZSI+CiAgICAgICAgICAgICAgICAgICAgIDxvcGVyYW5kIHhzaTp0eXBlPSJDb2FsZXNjZSI+CiAgICAgICAgICAgICAgICAgICAgICAgIDxvcGVyYW5kIGxvY2F0b3I9IjU2Ojg4LTU2OjE5OCIgeHNpOnR5cGU9IkNvbmNhdGVuYXRlIj4KICAgICAgICAgICAgICAgICAgICAgICAgICAgPG9wZXJhbmQgeHNpOnR5cGU9IkNvYWxlc2NlIj4KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPG9wZXJhbmQgbG9jYXRvcj0iNTY6ODgtNTY6MTc2IiB4c2k6dHlwZT0iQ29uY2F0ZW5hdGUiPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8b3BlcmFuZCB4c2k6dHlwZT0iQ29hbGVzY2UiPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8b3BlcmFuZCBsb2NhdG9yPSI1Njo4OC01NjoxNzAiIHhzaTp0eXBlPSJDb25jYXRlbmF0ZSI+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDxvcGVyYW5kIHhzaTp0eXBlPSJDb2FsZXNjZSI+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDxvcGVyYW5kIGxvY2F0b3I9IjU2Ojg4LTU2OjE0NiIgeHNpOnR5cGU9IkNvbmNhdGVuYXRlIj4KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPG9wZXJhbmQgeHNpOnR5cGU9IkNvYWxlc2NlIj4KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPG9wZXJhbmQgbG9jYXRvcj0iNTY6ODgtNTY6MTM5IiB4c2k6dHlwZT0iQ29uY2F0ZW5hdGUiPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8b3BlcmFuZCB4c2k6dHlwZT0iQ29hbGVzY2UiPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8b3BlcmFuZCBsb2NhdG9yPSI1Njo4OC01NjoxMTciIHZhbHVlVHlwZT0idDpTdHJpbmciIHZhbHVlPSJJbnZhbGlkIEZISVIgUXVhbnRpdHkgY29kZTogIiB4c2k6dHlwZT0iTGl0ZXJhbCIvPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8b3BlcmFuZCB2YWx1ZVR5cGU9InQ6U3RyaW5nIiB2YWx1ZT0iIiB4c2k6dHlwZT0iTGl0ZXJhbCIvPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8L29wZXJhbmQ+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDxvcGVyYW5kIHhzaTp0eXBlPSJDb2FsZXNjZSI+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDxvcGVyYW5kIGxvY2F0b3I9IjU2OjEyMS01NjoxMzkiIHBhdGg9InZhbHVlIiB4c2k6dHlwZT0iUHJvcGVydHkiPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8c291cmNlIGxvY2F0b3I9IjU2OjEyMS01NjoxMzMiIHBhdGg9InVuaXQiIHhzaTp0eXBlPSJQcm9wZXJ0eSI+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDxzb3VyY2UgbG9jYXRvcj0iNTY6MTIxLTU2OjEyOCIgbmFtZT0icXVhbnRpdHkiIHhzaTp0eXBlPSJPcGVyYW5kUmVmIi8+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDwvc291cmNlPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8L29wZXJhbmQ+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDxvcGVyYW5kIHZhbHVlVHlwZT0idDpTdHJpbmciIHZhbHVlPSIiIHhzaTp0eXBlPSJMaXRlcmFsIi8+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDwvb3BlcmFuZD4KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPC9vcGVyYW5kPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8b3BlcmFuZCB2YWx1ZVR5cGU9InQ6U3RyaW5nIiB2YWx1ZT0iIiB4c2k6dHlwZT0iTGl0ZXJhbCIvPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8L29wZXJhbmQ+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDxvcGVyYW5kIHhzaTp0eXBlPSJDb2FsZXNjZSI+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDxvcGVyYW5kIGxvY2F0b3I9IjU2OjE0My01NjoxNDYiIHZhbHVlVHlwZT0idDpTdHJpbmciIHZhbHVlPSIgKCIgeHNpOnR5cGU9IkxpdGVyYWwiLz4KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPG9wZXJhbmQgdmFsdWVUeXBlPSJ0OlN0cmluZyIgdmFsdWU9IiIgeHNpOnR5cGU9IkxpdGVyYWwiLz4KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPC9vcGVyYW5kPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8L29wZXJhbmQ+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDxvcGVyYW5kIHZhbHVlVHlwZT0idDpTdHJpbmciIHZhbHVlPSIiIHhzaTp0eXBlPSJMaXRlcmFsIi8+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDwvb3BlcmFuZD4KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPG9wZXJhbmQgeHNpOnR5cGU9IkNvYWxlc2NlIj4KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPG9wZXJhbmQgbG9jYXRvcj0iNTY6MTUwLTU2OjE3MCIgcGF0aD0idmFsdWUiIHhzaTp0eXBlPSJQcm9wZXJ0eSI+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDxzb3VyY2UgbG9jYXRvcj0iNTY6MTUwLTU2OjE2NCIgcGF0aD0ic3lzdGVtIiB4c2k6dHlwZT0iUHJvcGVydHkiPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8c291cmNlIGxvY2F0b3I9IjU2OjE1MC01NjoxNTciIG5hbWU9InF1YW50aXR5IiB4c2k6dHlwZT0iT3BlcmFuZFJlZiIvPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8L3NvdXJjZT4KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPC9vcGVyYW5kPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8b3BlcmFuZCB2YWx1ZVR5cGU9InQ6U3RyaW5nIiB2YWx1ZT0iIiB4c2k6dHlwZT0iTGl0ZXJhbCIvPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8L29wZXJhbmQ+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDwvb3BlcmFuZD4KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPG9wZXJhbmQgdmFsdWVUeXBlPSJ0OlN0cmluZyIgdmFsdWU9IiIgeHNpOnR5cGU9IkxpdGVyYWwiLz4KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPC9vcGVyYW5kPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8b3BlcmFuZCB4c2k6dHlwZT0iQ29hbGVzY2UiPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8b3BlcmFuZCBsb2NhdG9yPSI1NjoxNzQtNTY6MTc2IiB2YWx1ZVR5cGU9InQ6U3RyaW5nIiB2YWx1ZT0ifCIgeHNpOnR5cGU9IkxpdGVyYWwiLz4KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPG9wZXJhbmQgdmFsdWVUeXBlPSJ0OlN0cmluZyIgdmFsdWU9IiIgeHNpOnR5cGU9IkxpdGVyYWwiLz4KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPC9vcGVyYW5kPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8L29wZXJhbmQ+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDxvcGVyYW5kIHZhbHVlVHlwZT0idDpTdHJpbmciIHZhbHVlPSIiIHhzaTp0eXBlPSJMaXRlcmFsIi8+CiAgICAgICAgICAgICAgICAgICAgICAgICAgIDwvb3BlcmFuZD4KICAgICAgICAgICAgICAgICAgICAgICAgICAgPG9wZXJhbmQgeHNpOnR5cGU9IkNvYWxlc2NlIj4KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPG9wZXJhbmQgbG9jYXRvcj0iNTY6MTgwLTU2OjE5OCIgcGF0aD0idmFsdWUiIHhzaTp0eXBlPSJQcm9wZXJ0eSI+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDxzb3VyY2UgbG9jYXRvcj0iNTY6MTgwLTU2OjE5MiIgcGF0aD0iY29kZSIgeHNpOnR5cGU9IlByb3BlcnR5Ij4KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPHNvdXJjZSBsb2NhdG9yPSI1NjoxODAtNTY6MTg3IiBuYW1lPSJxdWFudGl0eSIgeHNpOnR5cGU9Ik9wZXJhbmRSZWYiLz4KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPC9zb3VyY2U+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDwvb3BlcmFuZD4KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPG9wZXJhbmQgdmFsdWVUeXBlPSJ0OlN0cmluZyIgdmFsdWU9IiIgeHNpOnR5cGU9IkxpdGVyYWwiLz4KICAgICAgICAgICAgICAgICAgICAgICAgICAgPC9vcGVyYW5kPgogICAgICAgICAgICAgICAgICAgICAgICA8L29wZXJhbmQ+CiAgICAgICAgICAgICAgICAgICAgICAgIDxvcGVyYW5kIHZhbHVlVHlwZT0idDpTdHJpbmciIHZhbHVlPSIiIHhzaTp0eXBlPSJMaXRlcmFsIi8+CiAgICAgICAgICAgICAgICAgICAgIDwvb3BlcmFuZD4KICAgICAgICAgICAgICAgICAgICAgPG9wZXJhbmQgeHNpOnR5cGU9IkNvYWxlc2NlIj4KICAgICAgICAgICAgICAgICAgICAgICAgPG9wZXJhbmQgbG9jYXRvcj0iNTY6MjAyLTU2OjIwNCIgdmFsdWVUeXBlPSJ0OlN0cmluZyIgdmFsdWU9IikiIHhzaTp0eXBlPSJMaXRlcmFsIi8+CiAgICAgICAgICAgICAgICAgICAgICAgIDxvcGVyYW5kIHZhbHVlVHlwZT0idDpTdHJpbmciIHZhbHVlPSIiIHhzaTp0eXBlPSJMaXRlcmFsIi8+CiAgICAgICAgICAgICAgICAgICAgIDwvb3BlcmFuZD4KICAgICAgICAgICAgICAgICAgPC9tZXNzYWdlPgogICAgICAgICAgICAgICA8L29wZXJhbmQ+CiAgICAgICAgICAgIDwvZWxzZT4KICAgICAgICAgPC9leHByZXNzaW9uPgogICAgICAgICA8b3BlcmFuZCBuYW1lPSJxdWFudGl0eSI+CiAgICAgICAgICAgIDxvcGVyYW5kVHlwZVNwZWNpZmllciBsb2NhdG9yPSI0ODo1NS00ODo2NyIgbmFtZT0iZmhpcjpRdWFudGl0eSIgeHNpOnR5cGU9Ik5hbWVkVHlwZVNwZWNpZmllciIvPgogICAgICAgICA8L29wZXJhbmQ+CiAgICAgIDwvZGVmPgogICAgICA8ZGVmIGxvY2F0b3I9IjU5OjEtODQ6MTEiIG5hbWU9IlRvSW50ZXJ2YWwiIGNvbnRleHQ9IlBhdGllbnQiIGFjY2Vzc0xldmVsPSJQdWJsaWMiIHhzaTp0eXBlPSJGdW5jdGlvbkRlZiI+CiAgICAgICAgIDxleHByZXNzaW9uIGxvY2F0b3I9IjYwOjUtODQ6MTEiIHhzaTp0eXBlPSJJZiI+CiAgICAgICAgICAgIDxjb25kaXRpb24gbG9jYXRvcj0iNjA6OC02MDoyMyIgeHNpOnR5cGU9IklzTnVsbCI+CiAgICAgICAgICAgICAgIDxvcGVyYW5kIGxvY2F0b3I9IjYwOjgtNjA6MTUiIG5hbWU9InF1YW50aXR5IiB4c2k6dHlwZT0iT3BlcmFuZFJlZiIvPgogICAgICAgICAgICA8L2NvbmRpdGlvbj4KICAgICAgICAgICAgPHRoZW4geHNpOnR5cGU9IkFzIj4KICAgICAgICAgICAgICAgPG9wZXJhbmQgbG9jYXRvcj0iNjA6MzAtNjA6MzMiIHhzaTp0eXBlPSJOdWxsIi8+CiAgICAgICAgICAgICAgIDxhc1R5cGVTcGVjaWZpZXIgeHNpOnR5cGU9IkludGVydmFsVHlwZVNwZWNpZmllciI+CiAgICAgICAgICAgICAgICAgIDxwb2ludFR5cGUgbmFtZT0idDpRdWFudGl0eSIgeHNpOnR5cGU9Ik5hbWVkVHlwZVNwZWNpZmllciIvPgogICAgICAgICAgICAgICA8L2FzVHlwZVNwZWNpZmllcj4KICAgICAgICAgICAgPC90aGVuPgogICAgICAgICAgICA8ZWxzZSBsb2NhdG9yPSI2MTo5LTg0OjExIiB4c2k6dHlwZT0iQ2FzZSI+CiAgICAgICAgICAgICAgIDxjb21wYXJhbmQgbG9jYXRvcj0iNjE6MTQtNjE6MzgiIHBhdGg9InZhbHVlIiB4c2k6dHlwZT0iUHJvcGVydHkiPgogICAgICAgICAgICAgICAgICA8c291cmNlIGxvY2F0b3I9IjYxOjE0LTYxOjMyIiBwYXRoPSJjb21wYXJhdG9yIiB4c2k6dHlwZT0iUHJvcGVydHkiPgogICAgICAgICAgICAgICAgICAgICA8c291cmNlIGxvY2F0b3I9IjYxOjE0LTYxOjIxIiBuYW1lPSJxdWFudGl0eSIgeHNpOnR5cGU9Ik9wZXJhbmRSZWYiLz4KICAgICAgICAgICAgICAgICAgPC9zb3VyY2U+CiAgICAgICAgICAgICAgIDwvY29tcGFyYW5kPgogICAgICAgICAgICAgICA8Y2FzZUl0ZW0gbG9jYXRvcj0iNjI6MTMtNjY6MTciPgogICAgICAgICAgICAgICAgICA8d2hlbiBsb2NhdG9yPSI2MjoxOC02MjoyMCIgdmFsdWVUeXBlPSJ0OlN0cmluZyIgdmFsdWU9IiZsdDsiIHhzaTp0eXBlPSJMaXRlcmFsIi8+CiAgICAgICAgICAgICAgICAgIDx0aGVuIGxvY2F0b3I9IjYzOjE3LTY2OjE3IiBsb3dDbG9zZWQ9InRydWUiIGhpZ2hDbG9zZWQ9ImZhbHNlIiB4c2k6dHlwZT0iSW50ZXJ2YWwiPgogICAgICAgICAgICAgICAgICAgICA8bG93IGFzVHlwZT0idDpRdWFudGl0eSIgeHNpOnR5cGU9IkFzIj4KICAgICAgICAgICAgICAgICAgICAgICAgPG9wZXJhbmQgbG9jYXRvcj0iNjQ6MjEtNjQ6MjQiIHhzaTp0eXBlPSJOdWxsIi8+CiAgICAgICAgICAgICAgICAgICAgIDwvbG93PgogICAgICAgICAgICAgICAgICAgICA8aGlnaCBsb2NhdG9yPSI2NToyMS02NTo1OCIgbmFtZT0iVG9RdWFudGl0eUlnbm9yaW5nQ29tcGFyYXRvciIgeHNpOnR5cGU9IkZ1bmN0aW9uUmVmIj4KICAgICAgICAgICAgICAgICAgICAgICAgPG9wZXJhbmQgbG9jYXRvcj0iNjU6NTAtNjU6NTciIG5hbWU9InF1YW50aXR5IiB4c2k6dHlwZT0iT3BlcmFuZFJlZiIvPgogICAgICAgICAgICAgICAgICAgICA8L2hpZ2g+CiAgICAgICAgICAgICAgICAgIDwvdGhlbj4KICAgICAgICAgICAgICAgPC9jYXNlSXRlbT4KICAgICAgICAgICAgICAgPGNhc2VJdGVtIGxvY2F0b3I9IjY3OjEzLTcxOjE3Ij4KICAgICAgICAgICAgICAgICAgPHdoZW4gbG9jYXRvcj0iNjc6MTgtNjc6MjEiIHZhbHVlVHlwZT0idDpTdHJpbmciIHZhbHVlPSImbHQ7PSIgeHNpOnR5cGU9IkxpdGVyYWwiLz4KICAgICAgICAgICAgICAgICAgPHRoZW4gbG9jYXRvcj0iNjg6MTctNzE6MTciIGxvd0Nsb3NlZD0idHJ1ZSIgaGlnaENsb3NlZD0idHJ1ZSIgeHNpOnR5cGU9IkludGVydmFsIj4KICAgICAgICAgICAgICAgICAgICAgPGxvdyBhc1R5cGU9InQ6UXVhbnRpdHkiIHhzaTp0eXBlPSJBcyI+CiAgICAgICAgICAgICAgICAgICAgICAgIDxvcGVyYW5kIGxvY2F0b3I9IjY5OjIxLTY5OjI0IiB4c2k6dHlwZT0iTnVsbCIvPgogICAgICAgICAgICAgICAgICAgICA8L2xvdz4KICAgICAgICAgICAgICAgICAgICAgPGhpZ2ggbG9jYXRvcj0iNzA6MjEtNzA6NTgiIG5hbWU9IlRvUXVhbnRpdHlJZ25vcmluZ0NvbXBhcmF0b3IiIHhzaTp0eXBlPSJGdW5jdGlvblJlZiI+CiAgICAgICAgICAgICAgICAgICAgICAgIDxvcGVyYW5kIGxvY2F0b3I9IjcwOjUwLTcwOjU3IiBuYW1lPSJxdWFudGl0eSIgeHNpOnR5cGU9Ik9wZXJhbmRSZWYiLz4KICAgICAgICAgICAgICAgICAgICAgPC9oaWdoPgogICAgICAgICAgICAgICAgICA8L3RoZW4+CiAgICAgICAgICAgICAgIDwvY2FzZUl0ZW0+CiAgICAgICAgICAgICAgIDxjYXNlSXRlbSBsb2NhdG9yPSI3MjoxMy03NjoxNyI+CiAgICAgICAgICAgICAgICAgIDx3aGVuIGxvY2F0b3I9IjcyOjE4LTcyOjIxIiB2YWx1ZVR5cGU9InQ6U3RyaW5nIiB2YWx1ZT0iPj0iIHhzaTp0eXBlPSJMaXRlcmFsIi8+CiAgICAgICAgICAgICAgICAgIDx0aGVuIGxvY2F0b3I9IjczOjE3LTc2OjE3IiBsb3dDbG9zZWQ9InRydWUiIGhpZ2hDbG9zZWQ9InRydWUiIHhzaTp0eXBlPSJJbnRlcnZhbCI+CiAgICAgICAgICAgICAgICAgICAgIDxsb3cgbG9jYXRvcj0iNzQ6MjEtNzQ6NTgiIG5hbWU9IlRvUXVhbnRpdHlJZ25vcmluZ0NvbXBhcmF0b3IiIHhzaTp0eXBlPSJGdW5jdGlvblJlZiI+CiAgICAgICAgICAgICAgICAgICAgICAgIDxvcGVyYW5kIGxvY2F0b3I9Ijc0OjUwLTc0OjU3IiBuYW1lPSJxdWFudGl0eSIgeHNpOnR5cGU9Ik9wZXJhbmRSZWYiLz4KICAgICAgICAgICAgICAgICAgICAgPC9sb3c+CiAgICAgICAgICAgICAgICAgICAgIDxoaWdoIGFzVHlwZT0idDpRdWFudGl0eSIgeHNpOnR5cGU9IkFzIj4KICAgICAgICAgICAgICAgICAgICAgICAgPG9wZXJhbmQgbG9jYXRvcj0iNzU6MjEtNzU6MjQiIHhzaTp0eXBlPSJOdWxsIi8+CiAgICAgICAgICAgICAgICAgICAgIDwvaGlnaD4KICAgICAgICAgICAgICAgICAgPC90aGVuPgogICAgICAgICAgICAgICA8L2Nhc2VJdGVtPgogICAgICAgICAgICAgICA8Y2FzZUl0ZW0gbG9jYXRvcj0iNzc6MTMtODE6MTciPgogICAgICAgICAgICAgICAgICA8d2hlbiBsb2NhdG9yPSI3NzoxOC03NzoyMCIgdmFsdWVUeXBlPSJ0OlN0cmluZyIgdmFsdWU9Ij4iIHhzaTp0eXBlPSJMaXRlcmFsIi8+CiAgICAgICAgICAgICAgICAgIDx0aGVuIGxvY2F0b3I9Ijc4OjE3LTgxOjE3IiBsb3dDbG9zZWQ9ImZhbHNlIiBoaWdoQ2xvc2VkPSJ0cnVlIiB4c2k6dHlwZT0iSW50ZXJ2YWwiPgogICAgICAgICAgICAgICAgICAgICA8bG93IGxvY2F0b3I9Ijc5OjIxLTc5OjU4IiBuYW1lPSJUb1F1YW50aXR5SWdub3JpbmdDb21wYXJhdG9yIiB4c2k6dHlwZT0iRnVuY3Rpb25SZWYiPgogICAgICAgICAgICAgICAgICAgICAgICA8b3BlcmFuZCBsb2NhdG9yPSI3OTo1MC03OTo1NyIgbmFtZT0icXVhbnRpdHkiIHhzaTp0eXBlPSJPcGVyYW5kUmVmIi8+CiAgICAgICAgICAgICAgICAgICAgIDwvbG93PgogICAgICAgICAgICAgICAgICAgICA8aGlnaCBhc1R5cGU9InQ6UXVhbnRpdHkiIHhzaTp0eXBlPSJBcyI+CiAgICAgICAgICAgICAgICAgICAgICAgIDxvcGVyYW5kIGxvY2F0b3I9IjgwOjIxLTgwOjI0IiB4c2k6dHlwZT0iTnVsbCIvPgogICAgICAgICAgICAgICAgICAgICA8L2hpZ2g+CiAgICAgICAgICAgICAgICAgIDwvdGhlbj4KICAgICAgICAgICAgICAgPC9jYXNlSXRlbT4KICAgICAgICAgICAgICAgPGVsc2UgbG9jYXRvcj0iODM6MTctODM6NjgiIGxvd0Nsb3NlZD0idHJ1ZSIgaGlnaENsb3NlZD0idHJ1ZSIgeHNpOnR5cGU9IkludGVydmFsIj4KICAgICAgICAgICAgICAgICAgPGxvdyBsb2NhdG9yPSI4MzoyNi04Mzo0NSIgbmFtZT0iVG9RdWFudGl0eSIgeHNpOnR5cGU9IkZ1bmN0aW9uUmVmIj4KICAgICAgICAgICAgICAgICAgICAgPG9wZXJhbmQgbG9jYXRvcj0iODM6MzctODM6NDQiIG5hbWU9InF1YW50aXR5IiB4c2k6dHlwZT0iT3BlcmFuZFJlZiIvPgogICAgICAgICAgICAgICAgICA8L2xvdz4KICAgICAgICAgICAgICAgICAgPGhpZ2ggbG9jYXRvcj0iODM6NDgtODM6NjciIG5hbWU9IlRvUXVhbnRpdHkiIHhzaTp0eXBlPSJGdW5jdGlvblJlZiI+CiAgICAgICAgICAgICAgICAgICAgIDxvcGVyYW5kIGxvY2F0b3I9IjgzOjU5LTgzOjY2IiBuYW1lPSJxdWFudGl0eSIgeHNpOnR5cGU9Ik9wZXJhbmRSZWYiLz4KICAgICAgICAgICAgICAgICAgPC9oaWdoPgogICAgICAgICAgICAgICA8L2Vsc2U+CiAgICAgICAgICAgIDwvZWxzZT4KICAgICAgICAgPC9leHByZXNzaW9uPgogICAgICAgICA8b3BlcmFuZCBuYW1lPSJxdWFudGl0eSI+CiAgICAgICAgICAgIDxvcGVyYW5kVHlwZVNwZWNpZmllciBsb2NhdG9yPSI1OTozNy01OTo0OSIgbmFtZT0iZmhpcjpRdWFudGl0eSIgeHNpOnR5cGU9Ik5hbWVkVHlwZVNwZWNpZmllciIvPgogICAgICAgICA8L29wZXJhbmQ+CiAgICAgIDwvZGVmPgogICAgICA8ZGVmIGxvY2F0b3I9Ijg2OjEtOTA6MTA3IiBuYW1lPSJUb1JhdGlvIiBjb250ZXh0PSJQYXRpZW50IiBhY2Nlc3NMZXZlbD0iUHVibGljIiB4c2k6dHlwZT0iRnVuY3Rpb25EZWYiPgogICAgICAgICA8ZXhwcmVzc2lvbiBsb2NhdG9yPSI4Nzo1LTkwOjEwNyIgeHNpOnR5cGU9IklmIj4KICAgICAgICAgICAgPGNvbmRpdGlvbiBsb2NhdG9yPSI4Nzo4LTg3OjIwIiB4c2k6dHlwZT0iSXNOdWxsIj4KICAgICAgICAgICAgICAgPG9wZXJhbmQgbG9jYXRvcj0iODc6OC04NzoxMiIgbmFtZT0icmF0aW8iIHhzaTp0eXBlPSJPcGVyYW5kUmVmIi8+CiAgICAgICAgICAgIDwvY29uZGl0aW9uPgogICAgICAgICAgICA8dGhlbiBhc1R5cGU9InQ6UmF0aW8iIHhzaTp0eXBlPSJBcyI+CiAgICAgICAgICAgICAgIDxvcGVyYW5kIGxvY2F0b3I9Ijg4OjktODg6MTIiIHhzaTp0eXBlPSJOdWxsIi8+CiAgICAgICAgICAgIDwvdGhlbj4KICAgICAgICAgICAgPGVsc2UgbG9jYXRvcj0iOTA6OS05MDoxMDciIGNsYXNzVHlwZT0idDpSYXRpbyIgeHNpOnR5cGU9Ikluc3RhbmNlIj4KICAgICAgICAgICAgICAgPGVsZW1lbnQgbmFtZT0ibnVtZXJhdG9yIj4KICAgICAgICAgICAgICAgICAgPHZhbHVlIGxvY2F0b3I9IjkwOjM1LTkwOjYxIiBuYW1lPSJUb1F1YW50aXR5IiB4c2k6dHlwZT0iRnVuY3Rpb25SZWYiPgogICAgICAgICAgICAgICAgICAgICA8b3BlcmFuZCBsb2NhdG9yPSI5MDo0Ni05MDo2MCIgcGF0aD0ibnVtZXJhdG9yIiB4c2k6dHlwZT0iUHJvcGVydHkiPgogICAgICAgICAgICAgICAgICAgICAgICA8c291cmNlIGxvY2F0b3I9IjkwOjQ2LTkwOjUwIiBuYW1lPSJyYXRpbyIgeHNpOnR5cGU9Ik9wZXJhbmRSZWYiLz4KICAgICAgICAgICAgICAgICAgICAgPC9vcGVyYW5kPgogICAgICAgICAgICAgICAgICA8L3ZhbHVlPgogICAgICAgICAgICAgICA8L2VsZW1lbnQ+CiAgICAgICAgICAgICAgIDxlbGVtZW50IG5hbWU9ImRlbm9taW5hdG9yIj4KICAgICAgICAgICAgICAgICAgPHZhbHVlIGxvY2F0b3I9IjkwOjc3LTkwOjEwNSIgbmFtZT0iVG9RdWFudGl0eSIgeHNpOnR5cGU9IkZ1bmN0aW9uUmVmIj4KICAgICAgICAgICAgICAgICAgICAgPG9wZXJhbmQgbG9jYXRvcj0iOTA6ODgtOTA6MTA0IiBwYXRoPSJkZW5vbWluYXRvciIgeHNpOnR5cGU9IlByb3BlcnR5Ij4KICAgICAgICAgICAgICAgICAgICAgICAgPHNvdXJjZSBsb2NhdG9yPSI5MDo4OC05MDo5MiIgbmFtZT0icmF0aW8iIHhzaTp0eXBlPSJPcGVyYW5kUmVmIi8+CiAgICAgICAgICAgICAgICAgICAgIDwvb3BlcmFuZD4KICAgICAgICAgICAgICAgICAgPC92YWx1ZT4KICAgICAgICAgICAgICAgPC9lbGVtZW50PgogICAgICAgICAgICA8L2Vsc2U+CiAgICAgICAgIDwvZXhwcmVzc2lvbj4KICAgICAgICAgPG9wZXJhbmQgbmFtZT0icmF0aW8iPgogICAgICAgICAgICA8b3BlcmFuZFR5cGVTcGVjaWZpZXIgbG9jYXRvcj0iODY6MzEtODY6NDAiIG5hbWU9ImZoaXI6UmF0aW8iIHhzaTp0eXBlPSJOYW1lZFR5cGVTcGVjaWZpZXIiLz4KICAgICAgICAgPC9vcGVyYW5kPgogICAgICA8L2RlZj4KICAgICAgPGRlZiBsb2NhdG9yPSI5MjoxLTk2OjYzIiBuYW1lPSJUb0ludGVydmFsIiBjb250ZXh0PSJQYXRpZW50IiBhY2Nlc3NMZXZlbD0iUHVibGljIiB4c2k6dHlwZT0iRnVuY3Rpb25EZWYiPgogICAgICAgICA8ZXhwcmVzc2lvbiBsb2NhdG9yPSI5Mzo1LTk2OjYzIiB4c2k6dHlwZT0iSWYiPgogICAgICAgICAgICA8Y29uZGl0aW9uIGxvY2F0b3I9IjkzOjgtOTM6MjAiIHhzaTp0eXBlPSJJc051bGwiPgogICAgICAgICAgICAgICA8b3BlcmFuZCBsb2NhdG9yPSI5Mzo4LTkzOjEyIiBuYW1lPSJyYW5nZSIgeHNpOnR5cGU9Ik9wZXJhbmRSZWYiLz4KICAgICAgICAgICAgPC9jb25kaXRpb24+CiAgICAgICAgICAgIDx0aGVuIHhzaTp0eXBlPSJBcyI+CiAgICAgICAgICAgICAgIDxvcGVyYW5kIGxvY2F0b3I9Ijk0OjktOTQ6MTIiIHhzaTp0eXBlPSJOdWxsIi8+CiAgICAgICAgICAgICAgIDxhc1R5cGVTcGVjaWZpZXIgeHNpOnR5cGU9IkludGVydmFsVHlwZVNwZWNpZmllciI+CiAgICAgICAgICAgICAgICAgIDxwb2ludFR5cGUgbmFtZT0idDpRdWFudGl0eSIgeHNpOnR5cGU9Ik5hbWVkVHlwZVNwZWNpZmllciIvPgogICAgICAgICAgICAgICA8L2FzVHlwZVNwZWNpZmllcj4KICAgICAgICAgICAgPC90aGVuPgogICAgICAgICAgICA8ZWxzZSBsb2NhdG9yPSI5Njo5LTk2OjYzIiBsb3dDbG9zZWQ9InRydWUiIGhpZ2hDbG9zZWQ9InRydWUiIHhzaTp0eXBlPSJJbnRlcnZhbCI+CiAgICAgICAgICAgICAgIDxsb3cgbG9jYXRvcj0iOTY6MTgtOTY6MzgiIG5hbWU9IlRvUXVhbnRpdHkiIHhzaTp0eXBlPSJGdW5jdGlvblJlZiI+CiAgICAgICAgICAgICAgICAgIDxvcGVyYW5kIGxvY2F0b3I9Ijk2OjI5LTk2OjM3IiBwYXRoPSJsb3ciIHhzaTp0eXBlPSJQcm9wZXJ0eSI+CiAgICAgICAgICAgICAgICAgICAgIDxzb3VyY2UgbG9jYXRvcj0iOTY6MjktOTY6MzMiIG5hbWU9InJhbmdlIiB4c2k6dHlwZT0iT3BlcmFuZFJlZiIvPgogICAgICAgICAgICAgICAgICA8L29wZXJhbmQ+CiAgICAgICAgICAgICAgIDwvbG93PgogICAgICAgICAgICAgICA8aGlnaCBsb2NhdG9yPSI5Njo0MS05Njo2MiIgbmFtZT0iVG9RdWFudGl0eSIgeHNpOnR5cGU9IkZ1bmN0aW9uUmVmIj4KICAgICAgICAgICAgICAgICAgPG9wZXJhbmQgbG9jYXRvcj0iOTY6NTItOTY6NjEiIHBhdGg9ImhpZ2giIHhzaTp0eXBlPSJQcm9wZXJ0eSI+CiAgICAgICAgICAgICAgICAgICAgIDxzb3VyY2UgbG9jYXRvcj0iOTY6NTItOTY6NTYiIG5hbWU9InJhbmdlIiB4c2k6dHlwZT0iT3BlcmFuZFJlZiIvPgogICAgICAgICAgICAgICAgICA8L29wZXJhbmQ+CiAgICAgICAgICAgICAgIDwvaGlnaD4KICAgICAgICAgICAgPC9lbHNlPgogICAgICAgICA8L2V4cHJlc3Npb24+CiAgICAgICAgIDxvcGVyYW5kIG5hbWU9InJhbmdlIj4KICAgICAgICAgICAgPG9wZXJhbmRUeXBlU3BlY2lmaWVyIGxvY2F0b3I9IjkyOjM0LTkyOjQzIiBuYW1lPSJmaGlyOlJhbmdlIiB4c2k6dHlwZT0iTmFtZWRUeXBlU3BlY2lmaWVyIi8+CiAgICAgICAgIDwvb3BlcmFuZD4KICAgICAgPC9kZWY+CiAgICAgIDxkZWYgbG9jYXRvcj0iOTg6MS0xMDc6OSIgbmFtZT0iVG9Db2RlIiBjb250ZXh0PSJQYXRpZW50IiBhY2Nlc3NMZXZlbD0iUHVibGljIiB4c2k6dHlwZT0iRnVuY3Rpb25EZWYiPgogICAgICAgICA8ZXhwcmVzc2lvbiBsb2NhdG9yPSI5OTo1LTEwNzo5IiB4c2k6dHlwZT0iSWYiPgogICAgICAgICAgICA8Y29uZGl0aW9uIGxvY2F0b3I9Ijk5OjgtOTk6MjEiIHhzaTp0eXBlPSJJc051bGwiPgogICAgICAgICAgICAgICA8b3BlcmFuZCBsb2NhdG9yPSI5OTo4LTk5OjEzIiBuYW1lPSJjb2RpbmciIHhzaTp0eXBlPSJPcGVyYW5kUmVmIi8+CiAgICAgICAgICAgIDwvY29uZGl0aW9uPgogICAgICAgICAgICA8dGhlbiBhc1R5cGU9InQ6Q29kZSIgeHNpOnR5cGU9IkFzIj4KICAgICAgICAgICAgICAgPG9wZXJhbmQgbG9jYXRvcj0iMTAwOjktMTAwOjEyIiB4c2k6dHlwZT0iTnVsbCIvPgogICAgICAgICAgICA8L3RoZW4+CiAgICAgICAgICAgIDxlbHNlIGxvY2F0b3I9IjEwMjo5LTEwNzo5IiBjbGFzc1R5cGU9InQ6Q29kZSIgeHNpOnR5cGU9Ikluc3RhbmNlIj4KICAgICAgICAgICAgICAgPGVsZW1lbnQgbmFtZT0iY29kZSI+CiAgICAgICAgICAgICAgICAgIDx2YWx1ZSBsb2NhdG9yPSIxMDM6MTctMTAzOjMzIiBwYXRoPSJ2YWx1ZSIgeHNpOnR5cGU9IlByb3BlcnR5Ij4KICAgICAgICAgICAgICAgICAgICAgPHNvdXJjZSBsb2NhdG9yPSIxMDM6MTctMTAzOjI3IiBwYXRoPSJjb2RlIiB4c2k6dHlwZT0iUHJvcGVydHkiPgogICAgICAgICAgICAgICAgICAgICAgICA8c291cmNlIGxvY2F0b3I9IjEwMzoxNy0xMDM6MjIiIG5hbWU9ImNvZGluZyIgeHNpOnR5cGU9Ik9wZXJhbmRSZWYiLz4KICAgICAgICAgICAgICAgICAgICAgPC9zb3VyY2U+CiAgICAgICAgICAgICAgICAgIDwvdmFsdWU+CiAgICAgICAgICAgICAgIDwvZWxlbWVudD4KICAgICAgICAgICAgICAgPGVsZW1lbnQgbmFtZT0ic3lzdGVtIj4KICAgICAgICAgICAgICAgICAgPHZhbHVlIGxvY2F0b3I9IjEwNDoxOS0xMDQ6MzciIHBhdGg9InZhbHVlIiB4c2k6dHlwZT0iUHJvcGVydHkiPgogICAgICAgICAgICAgICAgICAgICA8c291cmNlIGxvY2F0b3I9IjEwNDoxOS0xMDQ6MzEiIHBhdGg9InN5c3RlbSIgeHNpOnR5cGU9IlByb3BlcnR5Ij4KICAgICAgICAgICAgICAgICAgICAgICAgPHNvdXJjZSBsb2NhdG9yPSIxMDQ6MTktMTA0OjI0IiBuYW1lPSJjb2RpbmciIHhzaTp0eXBlPSJPcGVyYW5kUmVmIi8+CiAgICAgICAgICAgICAgICAgICAgIDwvc291cmNlPgogICAgICAgICAgICAgICAgICA8L3ZhbHVlPgogICAgICAgICAgICAgICA8L2VsZW1lbnQ+CiAgICAgICAgICAgICAgIDxlbGVtZW50IG5hbWU9InZlcnNpb24iPgogICAgICAgICAgICAgICAgICA8dmFsdWUgbG9jYXRvcj0iMTA1OjIwLTEwNTozOSIgcGF0aD0idmFsdWUiIHhzaTp0eXBlPSJQcm9wZXJ0eSI+CiAgICAgICAgICAgICAgICAgICAgIDxzb3VyY2UgbG9jYXRvcj0iMTA1OjIwLTEwNTozMyIgcGF0aD0idmVyc2lvbiIgeHNpOnR5cGU9IlByb3BlcnR5Ij4KICAgICAgICAgICAgICAgICAgICAgICAgPHNvdXJjZSBsb2NhdG9yPSIxMDU6MjAtMTA1OjI1IiBuYW1lPSJjb2RpbmciIHhzaTp0eXBlPSJPcGVyYW5kUmVmIi8+CiAgICAgICAgICAgICAgICAgICAgIDwvc291cmNlPgogICAgICAgICAgICAgICAgICA8L3ZhbHVlPgogICAgICAgICAgICAgICA8L2VsZW1lbnQ+CiAgICAgICAgICAgICAgIDxlbGVtZW50IG5hbWU9ImRpc3BsYXkiPgogICAgICAgICAgICAgICAgICA8dmFsdWUgbG9jYXRvcj0iMTA2OjIwLTEwNjozOSIgcGF0aD0idmFsdWUiIHhzaTp0eXBlPSJQcm9wZXJ0eSI+CiAgICAgICAgICAgICAgICAgICAgIDxzb3VyY2UgbG9jYXRvcj0iMTA2OjIwLTEwNjozMyIgcGF0aD0iZGlzcGxheSIgeHNpOnR5cGU9IlByb3BlcnR5Ij4KICAgICAgICAgICAgICAgICAgICAgICAgPHNvdXJjZSBsb2NhdG9yPSIxMDY6MjAtMTA2OjI1IiBuYW1lPSJjb2RpbmciIHhzaTp0eXBlPSJPcGVyYW5kUmVmIi8+CiAgICAgICAgICAgICAgICAgICAgIDwvc291cmNlPgogICAgICAgICAgICAgICAgICA8L3ZhbHVlPgogICAgICAgICAgICAgICA8L2VsZW1lbnQ+CiAgICAgICAgICAgIDwvZWxzZT4KICAgICAgICAgPC9leHByZXNzaW9uPgogICAgICAgICA8b3BlcmFuZCBuYW1lPSJjb2RpbmciPgogICAgICAgICAgICA8b3BlcmFuZFR5cGVTcGVjaWZpZXIgbG9jYXRvcj0iOTg6MzEtOTg6NDEiIG5hbWU9ImZoaXI6Q29kaW5nIiB4c2k6dHlwZT0iTmFtZWRUeXBlU3BlY2lmaWVyIi8+CiAgICAgICAgIDwvb3BlcmFuZD4KICAgICAgPC9kZWY+CiAgICAgIDxkZWYgbG9jYXRvcj0iMTA5OjEtMTE2OjkiIG5hbWU9IlRvQ29uY2VwdCIgY29udGV4dD0iUGF0aWVudCIgYWNjZXNzTGV2ZWw9IlB1YmxpYyIgeHNpOnR5cGU9IkZ1bmN0aW9uRGVmIj4KICAgICAgICAgPGV4cHJlc3Npb24gbG9jYXRvcj0iMTEwOjUtMTE2OjkiIHhzaTp0eXBlPSJJZiI+CiAgICAgICAgICAgIDxjb25kaXRpb24gbG9jYXRvcj0iMTEwOjgtMTEwOjIyIiB4c2k6dHlwZT0iSXNOdWxsIj4KICAgICAgICAgICAgICAgPG9wZXJhbmQgbG9jYXRvcj0iMTEwOjgtMTEwOjE0IiBuYW1lPSJjb25jZXB0IiB4c2k6dHlwZT0iT3BlcmFuZFJlZiIvPgogICAgICAgICAgICA8L2NvbmRpdGlvbj4KICAgICAgICAgICAgPHRoZW4gYXNUeXBlPSJ0OkNvbmNlcHQiIHhzaTp0eXBlPSJBcyI+CiAgICAgICAgICAgICAgIDxvcGVyYW5kIGxvY2F0b3I9IjExMTo5LTExMToxMiIgeHNpOnR5cGU9Ik51bGwiLz4KICAgICAgICAgICAgPC90aGVuPgogICAgICAgICAgICA8ZWxzZSBsb2NhdG9yPSIxMTM6OS0xMTY6OSIgY2xhc3NUeXBlPSJ0OkNvbmNlcHQiIHhzaTp0eXBlPSJJbnN0YW5jZSI+CiAgICAgICAgICAgICAgIDxlbGVtZW50IG5hbWU9ImNvZGVzIj4KICAgICAgICAgICAgICAgICAgPHZhbHVlIGxvY2F0b3I9IjExNDoyMC0xMTQ6NTIiIHhzaTp0eXBlPSJRdWVyeSI+CiAgICAgICAgICAgICAgICAgICAgIDxzb3VyY2UgbG9jYXRvcj0iMTE0OjIwLTExNDozNSIgYWxpYXM9IkMiPgogICAgICAgICAgICAgICAgICAgICAgICA8ZXhwcmVzc2lvbiBsb2NhdG9yPSIxMTQ6MjAtMTE0OjMzIiBwYXRoPSJjb2RpbmciIHhzaTp0eXBlPSJQcm9wZXJ0eSI+CiAgICAgICAgICAgICAgICAgICAgICAgICAgIDxzb3VyY2UgbmFtZT0iY29uY2VwdCIgeHNpOnR5cGU9Ik9wZXJhbmRSZWYiLz4KICAgICAgICAgICAgICAgICAgICAgICAgPC9leHByZXNzaW9uPgogICAgICAgICAgICAgICAgICAgICA8L3NvdXJjZT4KICAgICAgICAgICAgICAgICAgICAgPHJldHVybiBsb2NhdG9yPSIxMTQ6MzctMTE0OjUyIj4KICAgICAgICAgICAgICAgICAgICAgICAgPGV4cHJlc3Npb24gbG9jYXRvcj0iMTE0OjQ0LTExNDo1MiIgbmFtZT0iVG9Db2RlIiB4c2k6dHlwZT0iRnVuY3Rpb25SZWYiPgogICAgICAgICAgICAgICAgICAgICAgICAgICA8b3BlcmFuZCBsb2NhdG9yPSIxMTQ6NTEiIG5hbWU9IkMiIHhzaTp0eXBlPSJBbGlhc1JlZiIvPgogICAgICAgICAgICAgICAgICAgICAgICA8L2V4cHJlc3Npb24+CiAgICAgICAgICAgICAgICAgICAgIDwvcmV0dXJuPgogICAgICAgICAgICAgICAgICA8L3ZhbHVlPgogICAgICAgICAgICAgICA8L2VsZW1lbnQ+CiAgICAgICAgICAgICAgIDxlbGVtZW50IG5hbWU9ImRpc3BsYXkiPgogICAgICAgICAgICAgICAgICA8dmFsdWUgbG9jYXRvcj0iMTE1OjIyLTExNTozOSIgcGF0aD0idmFsdWUiIHhzaTp0eXBlPSJQcm9wZXJ0eSI+CiAgICAgICAgICAgICAgICAgICAgIDxzb3VyY2UgbG9jYXRvcj0iMTE1OjIyLTExNTozMyIgcGF0aD0idGV4dCIgeHNpOnR5cGU9IlByb3BlcnR5Ij4KICAgICAgICAgICAgICAgICAgICAgICAgPHNvdXJjZSBsb2NhdG9yPSIxMTU6MjItMTE1OjI4IiBuYW1lPSJjb25jZXB0IiB4c2k6dHlwZT0iT3BlcmFuZFJlZiIvPgogICAgICAgICAgICAgICAgICAgICA8L3NvdXJjZT4KICAgICAgICAgICAgICAgICAgPC92YWx1ZT4KICAgICAgICAgICAgICAgPC9lbGVtZW50PgogICAgICAgICAgICA8L2Vsc2U+CiAgICAgICAgIDwvZXhwcmVzc2lvbj4KICAgICAgICAgPG9wZXJhbmQgbmFtZT0iY29uY2VwdCI+CiAgICAgICAgICAgIDxvcGVyYW5kVHlwZVNwZWNpZmllciBsb2NhdG9yPSIxMDk6MzUtMTA5OjU0IiBuYW1lPSJmaGlyOkNvZGVhYmxlQ29uY2VwdCIgeHNpOnR5cGU9Ik5hbWVkVHlwZVNwZWNpZmllciIvPgogICAgICAgICA8L29wZXJhbmQ+CiAgICAgIDwvZGVmPgogICAgICA8ZGVmIGxvY2F0b3I9IjExODoxLTEyMjo2MCIgbmFtZT0icmVmZXJlbmNlIiBjb250ZXh0PSJQYXRpZW50IiBhY2Nlc3NMZXZlbD0iUHVibGljIiB4c2k6dHlwZT0iRnVuY3Rpb25EZWYiPgogICAgICAgICA8ZXhwcmVzc2lvbiBsb2NhdG9yPSIxMTk6NS0xMjI6NjAiIHhzaTp0eXBlPSJJZiI+CiAgICAgICAgICAgIDxjb25kaXRpb24gbG9jYXRvcj0iMTE5OjgtMTE5OjI0IiB4c2k6dHlwZT0iSXNOdWxsIj4KICAgICAgICAgICAgICAgPG9wZXJhbmQgbG9jYXRvcj0iMTE5OjgtMTE5OjE2IiBuYW1lPSJyZWZlcmVuY2UiIHhzaTp0eXBlPSJPcGVyYW5kUmVmIi8+CiAgICAgICAgICAgIDwvY29uZGl0aW9uPgogICAgICAgICAgICA8dGhlbiBhc1R5cGU9ImZoaXI6UmVmZXJlbmNlIiB4c2k6dHlwZT0iQXMiPgogICAgICAgICAgICAgICA8b3BlcmFuZCBsb2NhdG9yPSIxMjA6OS0xMjA6MTIiIHhzaTp0eXBlPSJOdWxsIi8+CiAgICAgICAgICAgIDwvdGhlbj4KICAgICAgICAgICAgPGVsc2UgbG9jYXRvcj0iMTIyOjktMTIyOjYwIiBjbGFzc1R5cGU9ImZoaXI6UmVmZXJlbmNlIiB4c2k6dHlwZT0iSW5zdGFuY2UiPgogICAgICAgICAgICAgICA8ZWxlbWVudCBuYW1lPSJyZWZlcmVuY2UiPgogICAgICAgICAgICAgICAgICA8dmFsdWUgbG9jYXRvcj0iMTIyOjMyLTEyMjo1OCIgY2xhc3NUeXBlPSJmaGlyOnN0cmluZyIgeHNpOnR5cGU9Ikluc3RhbmNlIj4KICAgICAgICAgICAgICAgICAgICAgPGVsZW1lbnQgbmFtZT0idmFsdWUiPgogICAgICAgICAgICAgICAgICAgICAgICA8dmFsdWUgbG9jYXRvcj0iMTIyOjQ4LTEyMjo1NiIgbmFtZT0icmVmZXJlbmNlIiB4c2k6dHlwZT0iT3BlcmFuZFJlZiIvPgogICAgICAgICAgICAgICAgICAgICA8L2VsZW1lbnQ+CiAgICAgICAgICAgICAgICAgIDwvdmFsdWU+CiAgICAgICAgICAgICAgIDwvZWxlbWVudD4KICAgICAgICAgICAgPC9lbHNlPgogICAgICAgICA8L2V4cHJlc3Npb24+CiAgICAgICAgIDxvcGVyYW5kIG5hbWU9InJlZmVyZW5jZSI+CiAgICAgICAgICAgIDxvcGVyYW5kVHlwZVNwZWNpZmllciBsb2NhdG9yPSIxMTg6MzctMTE4OjQyIiBuYW1lPSJ0OlN0cmluZyIgeHNpOnR5cGU9Ik5hbWVkVHlwZVNwZWNpZmllciIvPgogICAgICAgICA8L29wZXJhbmQ+CiAgICAgIDwvZGVmPgogICAgICA8ZGVmIGxvY2F0b3I9IjEyNDoxLTEyNDo2OCIgbmFtZT0icmVzb2x2ZSIgY29udGV4dD0iUGF0aWVudCIgYWNjZXNzTGV2ZWw9IlB1YmxpYyIgZXh0ZXJuYWw9InRydWUiIHhzaTp0eXBlPSJGdW5jdGlvbkRlZiI+CiAgICAgICAgIDxvcGVyYW5kIG5hbWU9InJlZmVyZW5jZSI+CiAgICAgICAgICAgIDxvcGVyYW5kVHlwZVNwZWNpZmllciBsb2NhdG9yPSIxMjQ6MzUtMTI0OjQwIiBuYW1lPSJ0OlN0cmluZyIgeHNpOnR5cGU9Ik5hbWVkVHlwZVNwZWNpZmllciIvPgogICAgICAgICA8L29wZXJhbmQ+CiAgICAgIDwvZGVmPgogICAgICA8ZGVmIGxvY2F0b3I9IjEyNToxLTEyNTo3MSIgbmFtZT0icmVzb2x2ZSIgY29udGV4dD0iUGF0aWVudCIgYWNjZXNzTGV2ZWw9IlB1YmxpYyIgZXh0ZXJuYWw9InRydWUiIHhzaTp0eXBlPSJGdW5jdGlvbkRlZiI+CiAgICAgICAgIDxvcGVyYW5kIG5hbWU9InJlZmVyZW5jZSI+CiAgICAgICAgICAgIDxvcGVyYW5kVHlwZVNwZWNpZmllciBsb2NhdG9yPSIxMjU6MzUtMTI1OjQzIiBuYW1lPSJmaGlyOlJlZmVyZW5jZSIgeHNpOnR5cGU9Ik5hbWVkVHlwZVNwZWNpZmllciIvPgogICAgICAgICA8L29wZXJhbmQ+CiAgICAgIDwvZGVmPgogICAgICA8ZGVmIGxvY2F0b3I9IjEyNjoxLTEyNjo3MiIgbmFtZT0icmVmZXJlbmNlIiBjb250ZXh0PSJQYXRpZW50IiBhY2Nlc3NMZXZlbD0iUHVibGljIiBleHRlcm5hbD0idHJ1ZSIgeHNpOnR5cGU9IkZ1bmN0aW9uRGVmIj4KICAgICAgICAgPG9wZXJhbmQgbmFtZT0icmVzb3VyY2UiPgogICAgICAgICAgICA8b3BlcmFuZFR5cGVTcGVjaWZpZXIgbG9jYXRvcj0iMTI2OjM2LTEyNjo0MyIgbmFtZT0iZmhpcjpSZXNvdXJjZSIgeHNpOnR5cGU9Ik5hbWVkVHlwZVNwZWNpZmllciIvPgogICAgICAgICA8L29wZXJhbmQ+CiAgICAgIDwvZGVmPgogICAgICA8ZGVmIGxvY2F0b3I9IjEyNzoxLTEyNzo4NiIgbmFtZT0iZXh0ZW5zaW9uIiBjb250ZXh0PSJQYXRpZW50IiBhY2Nlc3NMZXZlbD0iUHVibGljIiBleHRlcm5hbD0idHJ1ZSIgeHNpOnR5cGU9IkZ1bmN0aW9uRGVmIj4KICAgICAgICAgPG9wZXJhbmQgbmFtZT0iZWxlbWVudCI+CiAgICAgICAgICAgIDxvcGVyYW5kVHlwZVNwZWNpZmllciBsb2NhdG9yPSIxMjc6MzUtMTI3OjQxIiBuYW1lPSJmaGlyOkVsZW1lbnQiIHhzaTp0eXBlPSJOYW1lZFR5cGVTcGVjaWZpZXIiLz4KICAgICAgICAgPC9vcGVyYW5kPgogICAgICAgICA8b3BlcmFuZCBuYW1lPSJ1cmwiPgogICAgICAgICAgICA8b3BlcmFuZFR5cGVTcGVjaWZpZXIgbG9jYXRvcj0iMTI3OjQ4LTEyNzo1MyIgbmFtZT0idDpTdHJpbmciIHhzaTp0eXBlPSJOYW1lZFR5cGVTcGVjaWZpZXIiLz4KICAgICAgICAgPC9vcGVyYW5kPgogICAgICA8L2RlZj4KICAgICAgPGRlZiBsb2NhdG9yPSIxMjg6MS0xMjg6ODgiIG5hbWU9ImV4dGVuc2lvbiIgY29udGV4dD0iUGF0aWVudCIgYWNjZXNzTGV2ZWw9IlB1YmxpYyIgZXh0ZXJuYWw9InRydWUiIHhzaTp0eXBlPSJGdW5jdGlvbkRlZiI+CiAgICAgICAgIDxvcGVyYW5kIG5hbWU9InJlc291cmNlIj4KICAgICAgICAgICAgPG9wZXJhbmRUeXBlU3BlY2lmaWVyIGxvY2F0b3I9IjEyODozNi0xMjg6NDMiIG5hbWU9ImZoaXI6UmVzb3VyY2UiIHhzaTp0eXBlPSJOYW1lZFR5cGVTcGVjaWZpZXIiLz4KICAgICAgICAgPC9vcGVyYW5kPgogICAgICAgICA8b3BlcmFuZCBuYW1lPSJ1cmwiPgogICAgICAgICAgICA8b3BlcmFuZFR5cGVTcGVjaWZpZXIgbG9jYXRvcj0iMTI4OjUwLTEyODo1NSIgbmFtZT0idDpTdHJpbmciIHhzaTp0eXBlPSJOYW1lZFR5cGVTcGVjaWZpZXIiLz4KICAgICAgICAgPC9vcGVyYW5kPgogICAgICA8L2RlZj4KICAgICAgPGRlZiBsb2NhdG9yPSIxMjk6MS0xMjk6NjciIG5hbWU9Imhhc1ZhbHVlIiBjb250ZXh0PSJQYXRpZW50IiBhY2Nlc3NMZXZlbD0iUHVibGljIiBleHRlcm5hbD0idHJ1ZSIgeHNpOnR5cGU9IkZ1bmN0aW9uRGVmIj4KICAgICAgICAgPG9wZXJhbmQgbmFtZT0iZWxlbWVudCI+CiAgICAgICAgICAgIDxvcGVyYW5kVHlwZVNwZWNpZmllciBsb2NhdG9yPSIxMjk6MzQtMTI5OjQwIiBuYW1lPSJmaGlyOkVsZW1lbnQiIHhzaTp0eXBlPSJOYW1lZFR5cGVTcGVjaWZpZXIiLz4KICAgICAgICAgPC9vcGVyYW5kPgogICAgICA8L2RlZj4KICAgICAgPGRlZiBsb2NhdG9yPSIxMzA6MS0xMzA6NjMiIG5hbWU9ImdldFZhbHVlIiBjb250ZXh0PSJQYXRpZW50IiBhY2Nlc3NMZXZlbD0iUHVibGljIiBleHRlcm5hbD0idHJ1ZSIgeHNpOnR5cGU9IkZ1bmN0aW9uRGVmIj4KICAgICAgICAgPG9wZXJhbmQgbmFtZT0iZWxlbWVudCI+CiAgICAgICAgICAgIDxvcGVyYW5kVHlwZVNwZWNpZmllciBsb2NhdG9yPSIxMzA6MzQtMTMwOjQwIiBuYW1lPSJmaGlyOkVsZW1lbnQiIHhzaTp0eXBlPSJOYW1lZFR5cGVTcGVjaWZpZXIiLz4KICAgICAgICAgPC9vcGVyYW5kPgogICAgICA8L2RlZj4KICAgICAgPGRlZiBsb2NhdG9yPSIxMzE6MS0xMzE6NjkiIG5hbWU9Im9mVHlwZSIgY29udGV4dD0iUGF0aWVudCIgYWNjZXNzTGV2ZWw9IlB1YmxpYyIgZXh0ZXJuYWw9InRydWUiIHhzaTp0eXBlPSJGdW5jdGlvbkRlZiI+CiAgICAgICAgIDxvcGVyYW5kIG5hbWU9ImlkZW50aWZpZXIiPgogICAgICAgICAgICA8b3BlcmFuZFR5cGVTcGVjaWZpZXIgbG9jYXRvcj0iMTMxOjM1LTEzMTo0MCIgbmFtZT0idDpTdHJpbmciIHhzaTp0eXBlPSJOYW1lZFR5cGVTcGVjaWZpZXIiLz4KICAgICAgICAgPC9vcGVyYW5kPgogICAgICA8L2RlZj4KICAgICAgPGRlZiBsb2NhdG9yPSIxMzI6MS0xMzI6NjMiIG5hbWU9ImlzIiBjb250ZXh0PSJQYXRpZW50IiBhY2Nlc3NMZXZlbD0iUHVibGljIiBleHRlcm5hbD0idHJ1ZSIgeHNpOnR5cGU9IkZ1bmN0aW9uRGVmIj4KICAgICAgICAgPG9wZXJhbmQgbmFtZT0iaWRlbnRpZmllciI+CiAgICAgICAgICAgIDxvcGVyYW5kVHlwZVNwZWNpZmllciBsb2NhdG9yPSIxMzI6MzEtMTMyOjM2IiBuYW1lPSJ0OlN0cmluZyIgeHNpOnR5cGU9Ik5hbWVkVHlwZVNwZWNpZmllciIvPgogICAgICAgICA8L29wZXJhbmQ+CiAgICAgIDwvZGVmPgogICAgICA8ZGVmIGxvY2F0b3I9IjEzMzoxLTEzMzo1OSIgbmFtZT0iYXMiIGNvbnRleHQ9IlBhdGllbnQiIGFjY2Vzc0xldmVsPSJQdWJsaWMiIGV4dGVybmFsPSJ0cnVlIiB4c2k6dHlwZT0iRnVuY3Rpb25EZWYiPgogICAgICAgICA8b3BlcmFuZCBuYW1lPSJpZGVudGlmaWVyIj4KICAgICAgICAgICAgPG9wZXJhbmRUeXBlU3BlY2lmaWVyIGxvY2F0b3I9IjEzMzozMS0xMzM6MzYiIG5hbWU9InQ6U3RyaW5nIiB4c2k6dHlwZT0iTmFtZWRUeXBlU3BlY2lmaWVyIi8+CiAgICAgICAgIDwvb3BlcmFuZD4KICAgICAgPC9kZWY+CiAgICAgIDxkZWYgbG9jYXRvcj0iMTM0OjEtMTM0Ojg2IiBuYW1lPSJlbGVtZW50RGVmaW5pdGlvbiIgY29udGV4dD0iUGF0aWVudCIgYWNjZXNzTGV2ZWw9IlB1YmxpYyIgZXh0ZXJuYWw9InRydWUiIHhzaTp0eXBlPSJGdW5jdGlvbkRlZiI+CiAgICAgICAgIDxvcGVyYW5kIG5hbWU9ImVsZW1lbnQiPgogICAgICAgICAgICA8b3BlcmFuZFR5cGVTcGVjaWZpZXIgbG9jYXRvcj0iMTM0OjQzLTEzNDo0OSIgbmFtZT0iZmhpcjpFbGVtZW50IiB4c2k6dHlwZT0iTmFtZWRUeXBlU3BlY2lmaWVyIi8+CiAgICAgICAgIDwvb3BlcmFuZD4KICAgICAgPC9kZWY+CiAgICAgIDxkZWYgbG9jYXRvcj0iMTM1OjEtMTM1Ojk1IiBuYW1lPSJzbGljZSIgY29udGV4dD0iUGF0aWVudCIgYWNjZXNzTGV2ZWw9IlB1YmxpYyIgZXh0ZXJuYWw9InRydWUiIHhzaTp0eXBlPSJGdW5jdGlvbkRlZiI+CiAgICAgICAgIDxvcGVyYW5kIG5hbWU9ImVsZW1lbnQiPgogICAgICAgICAgICA8b3BlcmFuZFR5cGVTcGVjaWZpZXIgbG9jYXRvcj0iMTM1OjMxLTEzNTozNyIgbmFtZT0iZmhpcjpFbGVtZW50IiB4c2k6dHlwZT0iTmFtZWRUeXBlU3BlY2lmaWVyIi8+CiAgICAgICAgIDwvb3BlcmFuZD4KICAgICAgICAgPG9wZXJhbmQgbmFtZT0idXJsIj4KICAgICAgICAgICAgPG9wZXJhbmRUeXBlU3BlY2lmaWVyIGxvY2F0b3I9IjEzNTo0NC0xMzU6NDkiIG5hbWU9InQ6U3RyaW5nIiB4c2k6dHlwZT0iTmFtZWRUeXBlU3BlY2lmaWVyIi8+CiAgICAgICAgIDwvb3BlcmFuZD4KICAgICAgICAgPG9wZXJhbmQgbmFtZT0ibmFtZSI+CiAgICAgICAgICAgIDxvcGVyYW5kVHlwZVNwZWNpZmllciBsb2NhdG9yPSIxMzU6NTctMTM1OjYyIiBuYW1lPSJ0OlN0cmluZyIgeHNpOnR5cGU9Ik5hbWVkVHlwZVNwZWNpZmllciIvPgogICAgICAgICA8L29wZXJhbmQ+CiAgICAgIDwvZGVmPgogICAgICA8ZGVmIGxvY2F0b3I9IjEzNjoxLTEzNjo3NiIgbmFtZT0iY2hlY2tNb2RpZmllcnMiIGNvbnRleHQ9IlBhdGllbnQiIGFjY2Vzc0xldmVsPSJQdWJsaWMiIGV4dGVybmFsPSJ0cnVlIiB4c2k6dHlwZT0iRnVuY3Rpb25EZWYiPgogICAgICAgICA8b3BlcmFuZCBuYW1lPSJyZXNvdXJjZSI+CiAgICAgICAgICAgIDxvcGVyYW5kVHlwZVNwZWNpZmllciBsb2NhdG9yPSIxMzY6NDEtMTM2OjQ4IiBuYW1lPSJmaGlyOlJlc291cmNlIiB4c2k6dHlwZT0iTmFtZWRUeXBlU3BlY2lmaWVyIi8+CiAgICAgICAgIDwvb3BlcmFuZD4KICAgICAgPC9kZWY+CiAgICAgIDxkZWYgbG9jYXRvcj0iMTM3OjEtMTM3OjkzIiBuYW1lPSJjaGVja01vZGlmaWVycyIgY29udGV4dD0iUGF0aWVudCIgYWNjZXNzTGV2ZWw9IlB1YmxpYyIgZXh0ZXJuYWw9InRydWUiIHhzaTp0eXBlPSJGdW5jdGlvbkRlZiI+CiAgICAgICAgIDxvcGVyYW5kIG5hbWU9InJlc291cmNlIj4KICAgICAgICAgICAgPG9wZXJhbmRUeXBlU3BlY2lmaWVyIGxvY2F0b3I9IjEzNzo0MS0xMzc6NDgiIG5hbWU9ImZoaXI6UmVzb3VyY2UiIHhzaTp0eXBlPSJOYW1lZFR5cGVTcGVjaWZpZXIiLz4KICAgICAgICAgPC9vcGVyYW5kPgogICAgICAgICA8b3BlcmFuZCBuYW1lPSJtb2RpZmllciI+CiAgICAgICAgICAgIDxvcGVyYW5kVHlwZVNwZWNpZmllciBsb2NhdG9yPSIxMzc6NjAtMTM3OjY1IiBuYW1lPSJ0OlN0cmluZyIgeHNpOnR5cGU9Ik5hbWVkVHlwZVNwZWNpZmllciIvPgogICAgICAgICA8L29wZXJhbmQ+CiAgICAgIDwvZGVmPgogICAgICA8ZGVmIGxvY2F0b3I9IjEzODoxLTEzODo3MyIgbmFtZT0iY2hlY2tNb2RpZmllcnMiIGNvbnRleHQ9IlBhdGllbnQiIGFjY2Vzc0xldmVsPSJQdWJsaWMiIGV4dGVybmFsPSJ0cnVlIiB4c2k6dHlwZT0iRnVuY3Rpb25EZWYiPgogICAgICAgICA8b3BlcmFuZCBuYW1lPSJlbGVtZW50Ij4KICAgICAgICAgICAgPG9wZXJhbmRUeXBlU3BlY2lmaWVyIGxvY2F0b3I9IjEzODo0MC0xMzg6NDYiIG5hbWU9ImZoaXI6RWxlbWVudCIgeHNpOnR5cGU9Ik5hbWVkVHlwZVNwZWNpZmllciIvPgogICAgICAgICA8L29wZXJhbmQ+CiAgICAgIDwvZGVmPgogICAgICA8ZGVmIGxvY2F0b3I9IjEzOToxLTEzOTo5MCIgbmFtZT0iY2hlY2tNb2RpZmllcnMiIGNvbnRleHQ9IlBhdGllbnQiIGFjY2Vzc0xldmVsPSJQdWJsaWMiIGV4dGVybmFsPSJ0cnVlIiB4c2k6dHlwZT0iRnVuY3Rpb25EZWYiPgogICAgICAgICA8b3BlcmFuZCBuYW1lPSJlbGVtZW50Ij4KICAgICAgICAgICAgPG9wZXJhbmRUeXBlU3BlY2lmaWVyIGxvY2F0b3I9IjEzOTo0MC0xMzk6NDYiIG5hbWU9ImZoaXI6RWxlbWVudCIgeHNpOnR5cGU9Ik5hbWVkVHlwZVNwZWNpZmllciIvPgogICAgICAgICA8L29wZXJhbmQ+CiAgICAgICAgIDxvcGVyYW5kIG5hbWU9Im1vZGlmaWVyIj4KICAgICAgICAgICAgPG9wZXJhbmRUeXBlU3BlY2lmaWVyIGxvY2F0b3I9IjEzOTo1OC0xMzk6NjMiIG5hbWU9InQ6U3RyaW5nIiB4c2k6dHlwZT0iTmFtZWRUeXBlU3BlY2lmaWVyIi8+CiAgICAgICAgIDwvb3BlcmFuZD4KICAgICAgPC9kZWY+CiAgICAgIDxkZWYgbG9jYXRvcj0iMTQwOjEtMTQwOjg5IiBuYW1lPSJjb25mb3Jtc1RvIiBjb250ZXh0PSJQYXRpZW50IiBhY2Nlc3NMZXZlbD0iUHVibGljIiBleHRlcm5hbD0idHJ1ZSIgeHNpOnR5cGU9IkZ1bmN0aW9uRGVmIj4KICAgICAgICAgPG9wZXJhbmQgbmFtZT0icmVzb3VyY2UiPgogICAgICAgICAgICA8b3BlcmFuZFR5cGVTcGVjaWZpZXIgbG9jYXRvcj0iMTQwOjM3LTE0MDo0NCIgbmFtZT0iZmhpcjpSZXNvdXJjZSIgeHNpOnR5cGU9Ik5hbWVkVHlwZVNwZWNpZmllciIvPgogICAgICAgICA8L29wZXJhbmQ+CiAgICAgICAgIDxvcGVyYW5kIG5hbWU9InN0cnVjdHVyZSI+CiAgICAgICAgICAgIDxvcGVyYW5kVHlwZVNwZWNpZmllciBsb2NhdG9yPSIxNDA6NTctMTQwOjYyIiBuYW1lPSJ0OlN0cmluZyIgeHNpOnR5cGU9Ik5hbWVkVHlwZVNwZWNpZmllciIvPgogICAgICAgICA8L29wZXJhbmQ+CiAgICAgIDwvZGVmPgogICAgICA8ZGVmIGxvY2F0b3I9IjE0MToxLTE0MTo3OCIgbmFtZT0ibWVtYmVyT2YiIGNvbnRleHQ9IlBhdGllbnQiIGFjY2Vzc0xldmVsPSJQdWJsaWMiIGV4dGVybmFsPSJ0cnVlIiB4c2k6dHlwZT0iRnVuY3Rpb25EZWYiPgogICAgICAgICA8b3BlcmFuZCBuYW1lPSJjb2RlIj4KICAgICAgICAgICAgPG9wZXJhbmRUeXBlU3BlY2lmaWVyIGxvY2F0b3I9IjE0MTozMS0xNDE6MzQiIG5hbWU9ImZoaXI6Y29kZSIgeHNpOnR5cGU9Ik5hbWVkVHlwZVNwZWNpZmllciIvPgogICAgICAgICA8L29wZXJhbmQ+CiAgICAgICAgIDxvcGVyYW5kIG5hbWU9InZhbHVlU2V0Ij4KICAgICAgICAgICAgPG9wZXJhbmRUeXBlU3BlY2lmaWVyIGxvY2F0b3I9IjE0MTo0Ni0xNDE6NTEiIG5hbWU9InQ6U3RyaW5nIiB4c2k6dHlwZT0iTmFtZWRUeXBlU3BlY2lmaWVyIi8+CiAgICAgICAgIDwvb3BlcmFuZD4KICAgICAgPC9kZWY+CiAgICAgIDxkZWYgbG9jYXRvcj0iMTQyOjEtMTQyOjgyIiBuYW1lPSJtZW1iZXJPZiIgY29udGV4dD0iUGF0aWVudCIgYWNjZXNzTGV2ZWw9IlB1YmxpYyIgZXh0ZXJuYWw9InRydWUiIHhzaTp0eXBlPSJGdW5jdGlvbkRlZiI+CiAgICAgICAgIDxvcGVyYW5kIG5hbWU9ImNvZGluZyI+CiAgICAgICAgICAgIDxvcGVyYW5kVHlwZVNwZWNpZmllciBsb2NhdG9yPSIxNDI6MzMtMTQyOjM4IiBuYW1lPSJmaGlyOkNvZGluZyIgeHNpOnR5cGU9Ik5hbWVkVHlwZVNwZWNpZmllciIvPgogICAgICAgICA8L29wZXJhbmQ+CiAgICAgICAgIDxvcGVyYW5kIG5hbWU9InZhbHVlU2V0Ij4KICAgICAgICAgICAgPG9wZXJhbmRUeXBlU3BlY2lmaWVyIGxvY2F0b3I9IjE0Mjo1MC0xNDI6NTUiIG5hbWU9InQ6U3RyaW5nIiB4c2k6dHlwZT0iTmFtZWRUeXBlU3BlY2lmaWVyIi8+CiAgICAgICAgIDwvb3BlcmFuZD4KICAgICAgPC9kZWY+CiAgICAgIDxkZWYgbG9jYXRvcj0iMTQzOjEtMTQzOjkyIiBuYW1lPSJtZW1iZXJPZiIgY29udGV4dD0iUGF0aWVudCIgYWNjZXNzTGV2ZWw9IlB1YmxpYyIgZXh0ZXJuYWw9InRydWUiIHhzaTp0eXBlPSJGdW5jdGlvbkRlZiI+CiAgICAgICAgIDxvcGVyYW5kIG5hbWU9ImNvbmNlcHQiPgogICAgICAgICAgICA8b3BlcmFuZFR5cGVTcGVjaWZpZXIgbG9jYXRvcj0iMTQzOjM0LTE0Mzo0OCIgbmFtZT0iZmhpcjpDb2RlYWJsZUNvbmNlcHQiIHhzaTp0eXBlPSJOYW1lZFR5cGVTcGVjaWZpZXIiLz4KICAgICAgICAgPC9vcGVyYW5kPgogICAgICAgICA8b3BlcmFuZCBuYW1lPSJ2YWx1ZVNldCI+CiAgICAgICAgICAgIDxvcGVyYW5kVHlwZVNwZWNpZmllciBsb2NhdG9yPSIxNDM6NjAtMTQzOjY1IiBuYW1lPSJ0OlN0cmluZyIgeHNpOnR5cGU9Ik5hbWVkVHlwZVNwZWNpZmllciIvPgogICAgICAgICA8L29wZXJhbmQ+CiAgICAgIDwvZGVmPgogICAgICA8ZGVmIGxvY2F0b3I9IjE0NDoxLTE0NDo4OCIgbmFtZT0ic3Vic3VtZXMiIGNvbnRleHQ9IlBhdGllbnQiIGFjY2Vzc0xldmVsPSJQdWJsaWMiIGV4dGVybmFsPSJ0cnVlIiB4c2k6dHlwZT0iRnVuY3Rpb25EZWYiPgogICAgICAgICA8b3BlcmFuZCBuYW1lPSJjb2RpbmciPgogICAgICAgICAgICA8b3BlcmFuZFR5cGVTcGVjaWZpZXIgbG9jYXRvcj0iMTQ0OjMzLTE0NDozOCIgbmFtZT0iZmhpcjpDb2RpbmciIHhzaTp0eXBlPSJOYW1lZFR5cGVTcGVjaWZpZXIiLz4KICAgICAgICAgPC9vcGVyYW5kPgogICAgICAgICA8b3BlcmFuZCBuYW1lPSJzdWJzdW1lZENvZGluZyI+CiAgICAgICAgICAgIDxvcGVyYW5kVHlwZVNwZWNpZmllciBsb2NhdG9yPSIxNDQ6NTYtMTQ0OjYxIiBuYW1lPSJmaGlyOkNvZGluZyIgeHNpOnR5cGU9Ik5hbWVkVHlwZVNwZWNpZmllciIvPgogICAgICAgICA8L29wZXJhbmQ+CiAgICAgIDwvZGVmPgogICAgICA8ZGVmIGxvY2F0b3I9IjE0NToxLTE0NToxMDgiIG5hbWU9InN1YnN1bWVzIiBjb250ZXh0PSJQYXRpZW50IiBhY2Nlc3NMZXZlbD0iUHVibGljIiBleHRlcm5hbD0idHJ1ZSIgeHNpOnR5cGU9IkZ1bmN0aW9uRGVmIj4KICAgICAgICAgPG9wZXJhbmQgbmFtZT0iY29uY2VwdCI+CiAgICAgICAgICAgIDxvcGVyYW5kVHlwZVNwZWNpZmllciBsb2NhdG9yPSIxNDU6MzQtMTQ1OjQ4IiBuYW1lPSJmaGlyOkNvZGVhYmxlQ29uY2VwdCIgeHNpOnR5cGU9Ik5hbWVkVHlwZVNwZWNpZmllciIvPgogICAgICAgICA8L29wZXJhbmQ+CiAgICAgICAgIDxvcGVyYW5kIG5hbWU9InN1YnN1bWVkQ29uY2VwdCI+CiAgICAgICAgICAgIDxvcGVyYW5kVHlwZVNwZWNpZmllciBsb2NhdG9yPSIxNDU6NjctMTQ1OjgxIiBuYW1lPSJmaGlyOkNvZGVhYmxlQ29uY2VwdCIgeHNpOnR5cGU9Ik5hbWVkVHlwZVNwZWNpZmllciIvPgogICAgICAgICA8L29wZXJhbmQ+CiAgICAgIDwvZGVmPgogICAgICA8ZGVmIGxvY2F0b3I9IjE0NjoxLTE0Njo5MSIgbmFtZT0ic3Vic3VtZWRCeSIgY29udGV4dD0iUGF0aWVudCIgYWNjZXNzTGV2ZWw9IlB1YmxpYyIgZXh0ZXJuYWw9InRydWUiIHhzaTp0eXBlPSJGdW5jdGlvbkRlZiI+CiAgICAgICAgIDxvcGVyYW5kIG5hbWU9ImNvZGluZyI+CiAgICAgICAgICAgIDxvcGVyYW5kVHlwZVNwZWNpZmllciBsb2NhdG9yPSIxNDY6MzUtMTQ2OjQwIiBuYW1lPSJmaGlyOkNvZGluZyIgeHNpOnR5cGU9Ik5hbWVkVHlwZVNwZWNpZmllciIvPgogICAgICAgICA8L29wZXJhbmQ+CiAgICAgICAgIDxvcGVyYW5kIG5hbWU9InN1YnN1bWluZ0NvZGluZyI+CiAgICAgICAgICAgIDxvcGVyYW5kVHlwZVNwZWNpZmllciBsb2NhdG9yPSIxNDY6NTktMTQ2OjY0IiBuYW1lPSJmaGlyOkNvZGluZyIgeHNpOnR5cGU9Ik5hbWVkVHlwZVNwZWNpZmllciIvPgogICAgICAgICA8L29wZXJhbmQ+CiAgICAgIDwvZGVmPgogICAgICA8ZGVmIGxvY2F0b3I9IjE0NzoxLTE0NzoxMTEiIG5hbWU9InN1YnN1bWVkQnkiIGNvbnRleHQ9IlBhdGllbnQiIGFjY2Vzc0xldmVsPSJQdWJsaWMiIGV4dGVybmFsPSJ0cnVlIiB4c2k6dHlwZT0iRnVuY3Rpb25EZWYiPgogICAgICAgICA8b3BlcmFuZCBuYW1lPSJjb25jZXB0Ij4KICAgICAgICAgICAgPG9wZXJhbmRUeXBlU3BlY2lmaWVyIGxvY2F0b3I9IjE0NzozNi0xNDc6NTAiIG5hbWU9ImZoaXI6Q29kZWFibGVDb25jZXB0IiB4c2k6dHlwZT0iTmFtZWRUeXBlU3BlY2lmaWVyIi8+CiAgICAgICAgIDwvb3BlcmFuZD4KICAgICAgICAgPG9wZXJhbmQgbmFtZT0ic3Vic3VtaW5nQ29uY2VwdCI+CiAgICAgICAgICAgIDxvcGVyYW5kVHlwZVNwZWNpZmllciBsb2NhdG9yPSIxNDc6NzAtMTQ3Ojg0IiBuYW1lPSJmaGlyOkNvZGVhYmxlQ29uY2VwdCIgeHNpOnR5cGU9Ik5hbWVkVHlwZVNwZWNpZmllciIvPgogICAgICAgICA8L29wZXJhbmQ+CiAgICAgIDwvZGVmPgogICAgICA8ZGVmIGxvY2F0b3I9IjE0ODoxLTE0ODo2OSIgbmFtZT0iaHRtbENoZWNrcyIgY29udGV4dD0iUGF0aWVudCIgYWNjZXNzTGV2ZWw9IlB1YmxpYyIgZXh0ZXJuYWw9InRydWUiIHhzaTp0eXBlPSJGdW5jdGlvbkRlZiI+CiAgICAgICAgIDxvcGVyYW5kIG5hbWU9ImVsZW1lbnQiPgogICAgICAgICAgICA8b3BlcmFuZFR5cGVTcGVjaWZpZXIgbG9jYXRvcj0iMTQ4OjM2LTE0ODo0MiIgbmFtZT0iZmhpcjpFbGVtZW50IiB4c2k6dHlwZT0iTmFtZWRUeXBlU3BlY2lmaWVyIi8+CiAgICAgICAgIDwvb3BlcmFuZD4KICAgICAgPC9kZWY+CiAgICAgIDxkZWYgbG9jYXRvcj0iMTUwOjEtMTUwOjU4IiBuYW1lPSJUb1N0cmluZyIgY29udGV4dD0iUGF0aWVudCIgYWNjZXNzTGV2ZWw9IlB1YmxpYyIgeHNpOnR5cGU9IkZ1bmN0aW9uRGVmIj4KICAgICAgICAgPGV4cHJlc3Npb24gbG9jYXRvcj0iMTUwOjQ4LTE1MDo1OCIgcGF0aD0idmFsdWUiIHhzaTp0eXBlPSJQcm9wZXJ0eSI+CiAgICAgICAgICAgIDxzb3VyY2UgbG9jYXRvcj0iMTUwOjQ4LTE1MDo1MiIgbmFtZT0idmFsdWUiIHhzaTp0eXBlPSJPcGVyYW5kUmVmIi8+CiAgICAgICAgIDwvZXhwcmVzc2lvbj4KICAgICAgICAgPG9wZXJhbmQgbmFtZT0idmFsdWUiPgogICAgICAgICAgICA8b3BlcmFuZFR5cGVTcGVjaWZpZXIgbG9jYXRvcj0iMTUwOjMyLTE1MDo0NCIgbmFtZT0iZmhpcjpBY2NvdW50U3RhdHVzIiB4c2k6dHlwZT0iTmFtZWRUeXBlU3BlY2lmaWVyIi8+CiAgICAgICAgIDwvb3BlcmFuZD4KICAgICAgPC9kZWY+CiAgICAgIDxkZWYgbG9jYXRvcj0iMTUxOjEtMTUxOjcwIiBuYW1lPSJUb1N0cmluZyIgY29udGV4dD0iUGF0aWVudCIgYWNjZXNzTGV2ZWw9IlB1YmxpYyIgeHNpOnR5cGU9IkZ1bmN0aW9uRGVmIj4KICAgICAgICAgPGV4cHJlc3Npb24gbG9jYXRvcj0iMTUxOjYwLTE1MTo3MCIgcGF0aD0idmFsdWUiIHhzaTp0eXBlPSJQcm9wZXJ0eSI+CiAgICAgICAgICAgIDxzb3VyY2UgbG9jYXRvcj0iMTUxOjYwLTE1MTo2NCIgbmFtZT0idmFsdWUiIHhzaTp0eXBlPSJPcGVyYW5kUmVmIi8+CiAgICAgICAgIDwvZXhwcmVzc2lvbj4KICAgICAgICAgPG9wZXJhbmQgbmFtZT0idmFsdWUiPgogICAgICAgICAgICA8b3BlcmFuZFR5cGVTcGVjaWZpZXIgbG9jYXRvcj0iMTUxOjMyLTE1MTo1NiIgbmFtZT0iZmhpcjpBY3Rpb25DYXJkaW5hbGl0eUJlaGF2aW9yIiB4c2k6dHlwZT0iTmFtZWRUeXBlU3BlY2lmaWVyIi8+CiAgICAgICAgIDwvb3BlcmFuZD4KICAgICAgPC9kZWY+CiAgICAgIDxkZWYgbG9jYXRvcj0iMTUyOjEtMTUyOjY0IiBuYW1lPSJUb1N0cmluZyIgY29udGV4dD0iUGF0aWVudCIgYWNjZXNzTGV2ZWw9IlB1YmxpYyIgeHNpOnR5cGU9IkZ1bmN0aW9uRGVmIj4KICAgICAgICAgPGV4cHJlc3Npb24gbG9jYXRvcj0iMTUyOjU0LTE1Mjo2NCIgcGF0aD0idmFsdWUiIHhzaTp0eXBlPSJQcm9wZXJ0eSI+CiAgICAgICAgICAgIDxzb3VyY2UgbG9jYXRvcj0iMTUyOjU0LTE1Mjo1OCIgbmFtZT0idmFsdWUiIHhzaTp0eXBlPSJPcGVyYW5kUmVmIi8+CiAgICAgICAgIDwvZXhwcmVzc2lvbj4KICAgICAgICAgPG9wZXJhbmQgbmFtZT0idmFsdWUiPgogICAgICAgICAgICA8b3BlcmFuZFR5cGVTcGVjaWZpZXIgbG9jYXRvcj0iMTUyOjMyLTE1Mjo1MCIgbmFtZT0iZmhpcjpBY3Rpb25Db25kaXRpb25LaW5kIiB4c2k6dHlwZT0iTmFtZWRUeXBlU3BlY2lmaWVyIi8+CiAgICAgICAgIDwvb3BlcmFuZD4KICAgICAgPC9kZWY+CiAgICAgIDxkZWYgbG9jYXRvcj0iMTUzOjEtMTUzOjY3IiBuYW1lPSJUb1N0cmluZyIgY29udGV4dD0iUGF0aWVudCIgYWNjZXNzTGV2ZWw9IlB1YmxpYyIgeHNpOnR5cGU9IkZ1bmN0aW9uRGVmIj4KICAgICAgICAgPGV4cHJlc3Npb24gbG9jYXRvcj0iMTUzOjU3LTE1Mzo2NyIgcGF0aD0idmFsdWUiIHhzaTp0eXBlPSJQcm9wZXJ0eSI+CiAgICAgICAgICAgIDxzb3VyY2UgbG9jYXRvcj0iMTUzOjU3LTE1Mzo2MSIgbmFtZT0idmFsdWUiIHhzaTp0eXBlPSJPcGVyYW5kUmVmIi8+CiAgICAgICAgIDwvZXhwcmVzc2lvbj4KICAgICAgICAgPG9wZXJhbmQgbmFtZT0idmFsdWUiPgogICAgICAgICAgICA8b3BlcmFuZFR5cGVTcGVjaWZpZXIgbG9jYXRvcj0iMTUzOjMyLTE1Mzo1MyIgbmFtZT0iZmhpcjpBY3Rpb25Hcm91cGluZ0JlaGF2aW9yIiB4c2k6dHlwZT0iTmFtZWRUeXBlU3BlY2lmaWVyIi8+CiAgICAgICAgIDwvb3BlcmFuZD4KICAgICAgPC9kZWY+CiAgICAgIDxkZWYgbG9jYXRvcj0iMTU0OjEtMTU0OjY2IiBuYW1lPSJUb1N0cmluZyIgY29udGV4dD0iUGF0aWVudCIgYWNjZXNzTGV2ZWw9IlB1YmxpYyIgeHNpOnR5cGU9IkZ1bmN0aW9uRGVmIj4KICAgICAgICAgPGV4cHJlc3Npb24gbG9jYXRvcj0iMTU0OjU2LTE1NDo2NiIgcGF0aD0idmFsdWUiIHhzaTp0eXBlPSJQcm9wZXJ0eSI+CiAgICAgICAgICAgIDxzb3VyY2UgbG9jYXRvcj0iMTU0OjU2LTE1NDo2MCIgbmFtZT0idmFsdWUiIHhzaTp0eXBlPSJPcGVyYW5kUmVmIi8+CiAgICAgICAgIDwvZXhwcmVzc2lvbj4KICAgICAgICAgPG9wZXJhbmQgbmFtZT0idmFsdWUiPgogICAgICAgICAgICA8b3BlcmFuZFR5cGVTcGVjaWZpZXIgbG9jYXRvcj0iMTU0OjMyLTE1NDo1MiIgbmFtZT0iZmhpcjpBY3Rpb25QYXJ0aWNpcGFudFR5cGUiIHhzaTp0eXBlPSJOYW1lZFR5cGVTcGVjaWZpZXIiLz4KICAgICAgICAgPC9vcGVyYW5kPgogICAgICA8L2RlZj4KICAgICAgPGRlZiBsb2NhdG9yPSIxNTU6MS0xNTU6NjciIG5hbWU9IlRvU3RyaW5nIiBjb250ZXh0PSJQYXRpZW50IiBhY2Nlc3NMZXZlbD0iUHVibGljIiB4c2k6dHlwZT0iRnVuY3Rpb25EZWYiPgogICAgICAgICA8ZXhwcmVzc2lvbiBsb2NhdG9yPSIxNTU6NTctMTU1OjY3IiBwYXRoPSJ2YWx1ZSIgeHNpOnR5cGU9IlByb3BlcnR5Ij4KICAgICAgICAgICAgPHNvdXJjZSBsb2NhdG9yPSIxNTU6NTctMTU1OjYxIiBuYW1lPSJ2YWx1ZSIgeHNpOnR5cGU9Ik9wZXJhbmRSZWYiLz4KICAgICAgICAgPC9leHByZXNzaW9uPgogICAgICAgICA8b3BlcmFuZCBuYW1lPSJ2YWx1ZSI+CiAgICAgICAgICAgIDxvcGVyYW5kVHlwZVNwZWNpZmllciBsb2NhdG9yPSIxNTU6MzItMTU1OjUzIiBuYW1lPSJmaGlyOkFjdGlvblByZWNoZWNrQmVoYXZpb3IiIHhzaTp0eXBlPSJOYW1lZFR5cGVTcGVjaWZpZXIiLz4KICAgICAgICAgPC9vcGVyYW5kPgogICAgICA8L2RlZj4KICAgICAgPGRlZiBsb2NhdG9yPSIxNTY6MS0xNTY6NjciIG5hbWU9IlRvU3RyaW5nIiBjb250ZXh0PSJQYXRpZW50IiBhY2Nlc3NMZXZlbD0iUHVibGljIiB4c2k6dHlwZT0iRnVuY3Rpb25EZWYiPgogICAgICAgICA8ZXhwcmVzc2lvbiBsb2NhdG9yPSIxNTY6NTctMTU2OjY3IiBwYXRoPSJ2YWx1ZSIgeHNpOnR5cGU9IlByb3BlcnR5Ij4KICAgICAgICAgICAgPHNvdXJjZSBsb2NhdG9yPSIxNTY6NTctMTU2OjYxIiBuYW1lPSJ2YWx1ZSIgeHNpOnR5cGU9Ik9wZXJhbmRSZWYiLz4KICAgICAgICAgPC9leHByZXNzaW9uPgogICAgICAgICA8b3BlcmFuZCBuYW1lPSJ2YWx1ZSI+CiAgICAgICAgICAgIDxvcGVyYW5kVHlwZVNwZWNpZmllciBsb2NhdG9yPSIxNTY6MzItMTU2OjUzIiBuYW1lPSJmaGlyOkFjdGlvblJlbGF0aW9uc2hpcFR5cGUiIHhzaTp0eXBlPSJOYW1lZFR5cGVTcGVjaWZpZXIiLz4KICAgICAgICAgPC9vcGVyYW5kPgogICAgICA8L2RlZj4KICAgICAgPGRlZiBsb2NhdG9yPSIxNTc6MS0xNTc6NjciIG5hbWU9IlRvU3RyaW5nIiBjb250ZXh0PSJQYXRpZW50IiBhY2Nlc3NMZXZlbD0iUHVibGljIiB4c2k6dHlwZT0iRnVuY3Rpb25EZWYiPgogICAgICAgICA8ZXhwcmVzc2lvbiBsb2NhdG9yPSIxNTc6NTctMTU3OjY3IiBwYXRoPSJ2YWx1ZSIgeHNpOnR5cGU9IlByb3BlcnR5Ij4KICAgICAgICAgICAgPHNvdXJjZSBsb2NhdG9yPSIxNTc6NTctMTU3OjYxIiBuYW1lPSJ2YWx1ZSIgeHNpOnR5cGU9Ik9wZXJhbmRSZWYiLz4KICAgICAgICAgPC9leHByZXNzaW9uPgogICAgICAgICA8b3BlcmFuZCBuYW1lPSJ2YWx1ZSI+CiAgICAgICAgICAgIDxvcGVyYW5kVHlwZVNwZWNpZmllciBsb2NhdG9yPSIxNTc6MzItMTU3OjUzIiBuYW1lPSJmaGlyOkFjdGlvblJlcXVpcmVkQmVoYXZpb3IiIHhzaTp0eXBlPSJOYW1lZFR5cGVTcGVjaWZpZXIiLz4KICAgICAgICAgPC9vcGVyYW5kPgogICAgICA8L2RlZj4KICAgICAgPGRlZiBsb2NhdG9yPSIxNTg6MS0xNTg6NjgiIG5hbWU9IlRvU3RyaW5nIiBjb250ZXh0PSJQYXRpZW50IiBhY2Nlc3NMZXZlbD0iUHVibGljIiB4c2k6dHlwZT0iRnVuY3Rpb25EZWYiPgogICAgICAgICA8ZXhwcmVzc2lvbiBsb2NhdG9yPSIxNTg6NTgtMTU4OjY4IiBwYXRoPSJ2YWx1ZSIgeHNpOnR5cGU9IlByb3BlcnR5Ij4KICAgICAgICAgICAgPHNvdXJjZSBsb2NhdG9yPSIxNTg6NTgtMTU4OjYyIiBuYW1lPSJ2YWx1ZSIgeHNpOnR5cGU9Ik9wZXJhbmRSZWYiLz4KICAgICAgICAgPC9leHByZXNzaW9uPgogICAgICAgICA8b3BlcmFuZCBuYW1lPSJ2YWx1ZSI+CiAgICAgICAgICAgIDxvcGVyYW5kVHlwZVNwZWNpZmllciBsb2NhdG9yPSIxNTg6MzItMTU4OjU0IiBuYW1lPSJmaGlyOkFjdGlvblNlbGVjdGlvbkJlaGF2aW9yIiB4c2k6dHlwZT0iTmFtZWRUeXBlU3BlY2lmaWVyIi8+CiAgICAgICAgIDwvb3BlcmFuZD4KICAgICAgPC9kZWY+CiAgICAgIDxkZWYgbG9jYXRvcj0iMTU5OjEtMTU5OjY3IiBuYW1lPSJUb1N0cmluZyIgY29udGV4dD0iUGF0aWVudCIgYWNjZXNzTGV2ZWw9IlB1YmxpYyIgeHNpOnR5cGU9IkZ1bmN0aW9uRGVmIj4KICAgICAgICAgPGV4cHJlc3Npb24gbG9jYXRvcj0iMTU5OjU3LTE1OTo2NyIgcGF0aD0idmFsdWUiIHhzaTp0eXBlPSJQcm9wZXJ0eSI+CiAgICAgICAgICAgIDxzb3VyY2UgbG9jYXRvcj0iMTU5OjU3LTE1OTo2MSIgbmFtZT0idmFsdWUiIHhzaTp0eXBlPSJPcGVyYW5kUmVmIi8+CiAgICAgICAgIDwvZXhwcmVzc2lvbj4KICAgICAgICAgPG9wZXJhbmQgbmFtZT0idmFsdWUiPgogICAgICAgICAgICA8b3BlcmFuZFR5cGVTcGVjaWZpZXIgbG9jYXRvcj0iMTU5OjMyLTE1OTo1MyIgbmFtZT0iZmhpcjpBY3Rpdml0eURlZmluaXRpb25LaW5kIiB4c2k6dHlwZT0iTmFtZWRUeXBlU3BlY2lmaWVyIi8+CiAgICAgICAgIDwvb3BlcmFuZD4KICAgICAgPC9kZWY+CiAgICAgIDxkZWYgbG9jYXRvcj0iMTYwOjEtMTYwOjY4IiBuYW1lPSJUb1N0cmluZyIgY29udGV4dD0iUGF0aWVudCIgYWNjZXNzTGV2ZWw9IlB1YmxpYyIgeHNpOnR5cGU9IkZ1bmN0aW9uRGVmIj4KICAgICAgICAgPGV4cHJlc3Npb24gbG9jYXRvcj0iMTYwOjU4LTE2MDo2OCIgcGF0aD0idmFsdWUiIHhzaTp0eXBlPSJQcm9wZXJ0eSI+CiAgICAgICAgICAgIDxzb3VyY2UgbG9jYXRvcj0iMTYwOjU4LTE2MDo2MiIgbmFtZT0idmFsdWUiIHhzaTp0eXBlPSJPcGVyYW5kUmVmIi8+CiAgICAgICAgIDwvZXhwcmVzc2lvbj4KICAgICAgICAgPG9wZXJhbmQgbmFtZT0idmFsdWUiPgogICAgICAgICAgICA8b3BlcmFuZFR5cGVTcGVjaWZpZXIgbG9jYXRvcj0iMTYwOjMyLTE2MDo1NCIgbmFtZT0iZmhpcjpBY3Rpdml0eVBhcnRpY2lwYW50VHlwZSIgeHNpOnR5cGU9Ik5hbWVkVHlwZVNwZWNpZmllciIvPgogICAgICAgICA8L29wZXJhbmQ+CiAgICAgIDwvZGVmPgogICAgICA8ZGVmIGxvY2F0b3I9IjE2MToxLTE2MTo1NiIgbmFtZT0iVG9TdHJpbmciIGNvbnRleHQ9IlBhdGllbnQiIGFjY2Vzc0xldmVsPSJQdWJsaWMiIHhzaTp0eXBlPSJGdW5jdGlvbkRlZiI+CiAgICAgICAgIDxleHByZXNzaW9uIGxvY2F0b3I9IjE2MTo0Ni0xNjE6NTYiIHBhdGg9InZhbHVlIiB4c2k6dHlwZT0iUHJvcGVydHkiPgogICAgICAgICAgICA8c291cmNlIGxvY2F0b3I9IjE2MTo0Ni0xNjE6NTAiIG5hbWU9InZhbHVlIiB4c2k6dHlwZT0iT3BlcmFuZFJlZiIvPgogICAgICAgICA8L2V4cHJlc3Npb24+CiAgICAgICAgIDxvcGVyYW5kIG5hbWU9InZhbHVlIj4KICAgICAgICAgICAgPG9wZXJhbmRUeXBlU3BlY2lmaWVyIGxvY2F0b3I9IjE2MTozMi0xNjE6NDIiIG5hbWU9ImZoaXI6QWRkcmVzc1R5cGUiIHhzaTp0eXBlPSJOYW1lZFR5cGVTcGVjaWZpZXIiLz4KICAgICAgICAgPC9vcGVyYW5kPgogICAgICA8L2RlZj4KICAgICAgPGRlZiBsb2NhdG9yPSIxNjI6MS0xNjI6NTUiIG5hbWU9IlRvU3RyaW5nIiBjb250ZXh0PSJQYXRpZW50IiBhY2Nlc3NMZXZlbD0iUHVibGljIiB4c2k6dHlwZT0iRnVuY3Rpb25EZWYiPgogICAgICAgICA8ZXhwcmVzc2lvbiBsb2NhdG9yPSIxNjI6NDUtMTYyOjU1IiBwYXRoPSJ2YWx1ZSIgeHNpOnR5cGU9IlByb3BlcnR5Ij4KICAgICAgICAgICAgPHNvdXJjZSBsb2NhdG9yPSIxNjI6NDUtMTYyOjQ5IiBuYW1lPSJ2YWx1ZSIgeHNpOnR5cGU9Ik9wZXJhbmRSZWYiLz4KICAgICAgICAgPC9leHByZXNzaW9uPgogICAgICAgICA8b3BlcmFuZCBuYW1lPSJ2YWx1ZSI+CiAgICAgICAgICAgIDxvcGVyYW5kVHlwZVNwZWNpZmllciBsb2NhdG9yPSIxNjI6MzItMTYyOjQxIiBuYW1lPSJmaGlyOkFkZHJlc3NVc2UiIHhzaTp0eXBlPSJOYW1lZFR5cGVTcGVjaWZpZXIiLz4KICAgICAgICAgPC9vcGVyYW5kPgogICAgICA8L2RlZj4KICAgICAgPGRlZiBsb2NhdG9yPSIxNjM6MS0xNjM6NjUiIG5hbWU9IlRvU3RyaW5nIiBjb250ZXh0PSJQYXRpZW50IiBhY2Nlc3NMZXZlbD0iUHVibGljIiB4c2k6dHlwZT0iRnVuY3Rpb25EZWYiPgogICAgICAgICA8ZXhwcmVzc2lvbiBsb2NhdG9yPSIxNjM6NTUtMTYzOjY1IiBwYXRoPSJ2YWx1ZSIgeHNpOnR5cGU9IlByb3BlcnR5Ij4KICAgICAgICAgICAgPHNvdXJjZSBsb2NhdG9yPSIxNjM6NTUtMTYzOjU5IiBuYW1lPSJ2YWx1ZSIgeHNpOnR5cGU9Ik9wZXJhbmRSZWYiLz4KICAgICAgICAgPC9leHByZXNzaW9uPgogICAgICAgICA8b3BlcmFuZCBuYW1lPSJ2YWx1ZSI+CiAgICAgICAgICAgIDxvcGVyYW5kVHlwZVNwZWNpZmllciBsb2NhdG9yPSIxNjM6MzItMTYzOjUxIiBuYW1lPSJmaGlyOkFkbWluaXN0cmF0aXZlR2VuZGVyIiB4c2k6dHlwZT0iTmFtZWRUeXBlU3BlY2lmaWVyIi8+CiAgICAgICAgIDwvb3BlcmFuZD4KICAgICAgPC9kZWY+CiAgICAgIDxkZWYgbG9jYXRvcj0iMTY0OjEtMTY0OjY2IiBuYW1lPSJUb1N0cmluZyIgY29udGV4dD0iUGF0aWVudCIgYWNjZXNzTGV2ZWw9IlB1YmxpYyIgeHNpOnR5cGU9IkZ1bmN0aW9uRGVmIj4KICAgICAgICAgPGV4cHJlc3Npb24gbG9jYXRvcj0iMTY0OjU2LTE2NDo2NiIgcGF0aD0idmFsdWUiIHhzaTp0eXBlPSJQcm9wZXJ0eSI+CiAgICAgICAgICAgIDxzb3VyY2UgbG9jYXRvcj0iMTY0OjU2LTE2NDo2MCIgbmFtZT0idmFsdWUiIHhzaTp0eXBlPSJPcGVyYW5kUmVmIi8+CiAgICAgICAgIDwvZXhwcmVzc2lvbj4KICAgICAgICAgPG9wZXJhbmQgbmFtZT0idmFsdWUiPgogICAgICAgICAgICA8b3BlcmFuZFR5cGVTcGVjaWZpZXIgbG9jYXRvcj0iMTY0OjMyLTE2NDo1MiIgbmFtZT0iZmhpcjpBZHZlcnNlRXZlbnRBY3R1YWxpdHkiIHhzaTp0eXBlPSJOYW1lZFR5cGVTcGVjaWZpZXIiLz4KICAgICAgICAgPC9vcGVyYW5kPgogICAgICA8L2RlZj4KICAgICAgPGRlZiBsb2NhdG9yPSIxNjU6MS0xNjU6NjAiIG5hbWU9IlRvU3RyaW5nIiBjb250ZXh0PSJQYXRpZW50IiBhY2Nlc3NMZXZlbD0iUHVibGljIiB4c2k6dHlwZT0iRnVuY3Rpb25EZWYiPgogICAgICAgICA8ZXhwcmVzc2lvbiBsb2NhdG9yPSIxNjU6NTAtMTY1OjYwIiBwYXRoPSJ2YWx1ZSIgeHNpOnR5cGU9IlByb3BlcnR5Ij4KICAgICAgICAgICAgPHNvdXJjZSBsb2NhdG9yPSIxNjU6NTAtMTY1OjU0IiBuYW1lPSJ2YWx1ZSIgeHNpOnR5cGU9Ik9wZXJhbmRSZWYiLz4KICAgICAgICAgPC9leHByZXNzaW9uPgogICAgICAgICA8b3BlcmFuZCBuYW1lPSJ2YWx1ZSI+CiAgICAgICAgICAgIDxvcGVyYW5kVHlwZVNwZWNpZmllciBsb2NhdG9yPSIxNjU6MzItMTY1OjQ2IiBuYW1lPSJmaGlyOkFnZ3JlZ2F0aW9uTW9kZSIgeHNpOnR5cGU9Ik5hbWVkVHlwZVNwZWNpZmllciIvPgogICAgICAgICA8L29wZXJhbmQ+CiAgICAgIDwvZGVmPgogICAgICA8ZGVmIGxvY2F0b3I9IjE2NjoxLTE2Njo3MSIgbmFtZT0iVG9TdHJpbmciIGNvbnRleHQ9IlBhdGllbnQiIGFjY2Vzc0xldmVsPSJQdWJsaWMiIHhzaTp0eXBlPSJGdW5jdGlvbkRlZiI+CiAgICAgICAgIDxleHByZXNzaW9uIGxvY2F0b3I9IjE2Njo2MS0xNjY6NzEiIHBhdGg9InZhbHVlIiB4c2k6dHlwZT0iUHJvcGVydHkiPgogICAgICAgICAgICA8c291cmNlIGxvY2F0b3I9IjE2Njo2MS0xNjY6NjUiIG5hbWU9InZhbHVlIiB4c2k6dHlwZT0iT3BlcmFuZFJlZiIvPgogICAgICAgICA8L2V4cHJlc3Npb24+CiAgICAgICAgIDxvcGVyYW5kIG5hbWU9InZhbHVlIj4KICAgICAgICAgICAgPG9wZXJhbmRUeXBlU3BlY2lmaWVyIGxvY2F0b3I9IjE2NjozMi0xNjY6NTciIG5hbWU9ImZoaXI6QWxsZXJneUludG9sZXJhbmNlQ2F0ZWdvcnkiIHhzaTp0eXBlPSJOYW1lZFR5cGVTcGVjaWZpZXIiLz4KICAgICAgICAgPC9vcGVyYW5kPgogICAgICA8L2RlZj4KICAgICAgPGRlZiBsb2NhdG9yPSIxNjc6MS0xNjc6NzQiIG5hbWU9IlRvU3RyaW5nIiBjb250ZXh0PSJQYXRpZW50IiBhY2Nlc3NMZXZlbD0iUHVibGljIiB4c2k6dHlwZT0iRnVuY3Rpb25EZWYiPgogICAgICAgICA8ZXhwcmVzc2lvbiBsb2NhdG9yPSIxNjc6NjQtMTY3Ojc0IiBwYXRoPSJ2YWx1ZSIgeHNpOnR5cGU9IlByb3BlcnR5Ij4KICAgICAgICAgICAgPHNvdXJjZSBsb2NhdG9yPSIxNjc6NjQtMTY3OjY4IiBuYW1lPSJ2YWx1ZSIgeHNpOnR5cGU9Ik9wZXJhbmRSZWYiLz4KICAgICAgICAgPC9leHByZXNzaW9uPgogICAgICAgICA8b3BlcmFuZCBuYW1lPSJ2YWx1ZSI+CiAgICAgICAgICAgIDxvcGVyYW5kVHlwZVNwZWNpZmllciBsb2NhdG9yPSIxNjc6MzItMTY3OjYwIiBuYW1lPSJmaGlyOkFsbGVyZ3lJbnRvbGVyYW5jZUNyaXRpY2FsaXR5IiB4c2k6dHlwZT0iTmFtZWRUeXBlU3BlY2lmaWVyIi8+CiAgICAgICAgIDwvb3BlcmFuZD4KICAgICAgPC9kZWY+CiAgICAgIDxkZWYgbG9jYXRvcj0iMTY4OjEtMTY4OjcxIiBuYW1lPSJUb1N0cmluZyIgY29udGV4dD0iUGF0aWVudCIgYWNjZXNzTGV2ZWw9IlB1YmxpYyIgeHNpOnR5cGU9IkZ1bmN0aW9uRGVmIj4KICAgICAgICAgPGV4cHJlc3Npb24gbG9jYXRvcj0iMTY4OjYxLTE2ODo3MSIgcGF0aD0idmFsdWUiIHhzaTp0eXBlPSJQcm9wZXJ0eSI+CiAgICAgICAgICAgIDxzb3VyY2UgbG9jYXRvcj0iMTY4OjYxLTE2ODo2NSIgbmFtZT0idmFsdWUiIHhzaTp0eXBlPSJPcGVyYW5kUmVmIi8+CiAgICAgICAgIDwvZXhwcmVzc2lvbj4KICAgICAgICAgPG9wZXJhbmQgbmFtZT0idmFsdWUiPgogICAgICAgICAgICA8b3BlcmFuZFR5cGVTcGVjaWZpZXIgbG9jYXRvcj0iMTY4OjMyLTE2ODo1NyIgbmFtZT0iZmhpcjpBbGxlcmd5SW50b2xlcmFuY2VTZXZlcml0eSIgeHNpOnR5cGU9Ik5hbWVkVHlwZVNwZWNpZmllciIvPgogICAgICAgICA8L29wZXJhbmQ+CiAgICAgIDwvZGVmPgogICAgICA8ZGVmIGxvY2F0b3I9IjE2OToxLTE2OTo2NyIgbmFtZT0iVG9TdHJpbmciIGNvbnRleHQ9IlBhdGllbnQiIGFjY2Vzc0xldmVsPSJQdWJsaWMiIHhzaTp0eXBlPSJGdW5jdGlvbkRlZiI+CiAgICAgICAgIDxleHByZXNzaW9uIGxvY2F0b3I9IjE2OTo1Ny0xNjk6NjciIHBhdGg9InZhbHVlIiB4c2k6dHlwZT0iUHJvcGVydHkiPgogICAgICAgICAgICA8c291cmNlIGxvY2F0b3I9IjE2OTo1Ny0xNjk6NjEiIG5hbWU9InZhbHVlIiB4c2k6dHlwZT0iT3BlcmFuZFJlZiIvPgogICAgICAgICA8L2V4cHJlc3Npb24+CiAgICAgICAgIDxvcGVyYW5kIG5hbWU9InZhbHVlIj4KICAgICAgICAgICAgPG9wZXJhbmRUeXBlU3BlY2lmaWVyIGxvY2F0b3I9IjE2OTozMi0xNjk6NTMiIG5hbWU9ImZoaXI6QWxsZXJneUludG9sZXJhbmNlVHlwZSIgeHNpOnR5cGU9Ik5hbWVkVHlwZVNwZWNpZmllciIvPgogICAgICAgICA8L29wZXJhbmQ+CiAgICAgIDwvZGVmPgogICAgICA8ZGVmIGxvY2F0b3I9IjE3MDoxLTE3MDo2MiIgbmFtZT0iVG9TdHJpbmciIGNvbnRleHQ9IlBhdGllbnQiIGFjY2Vzc0xldmVsPSJQdWJsaWMiIHhzaTp0eXBlPSJGdW5jdGlvbkRlZiI+CiAgICAgICAgIDxleHByZXNzaW9uIGxvY2F0b3I9IjE3MDo1Mi0xNzA6NjIiIHBhdGg9InZhbHVlIiB4c2k6dHlwZT0iUHJvcGVydHkiPgogICAgICAgICAgICA8c291cmNlIGxvY2F0b3I9IjE3MDo1Mi0xNzA6NTYiIG5hbWU9InZhbHVlIiB4c2k6dHlwZT0iT3BlcmFuZFJlZiIvPgogICAgICAgICA8L2V4cHJlc3Npb24+CiAgICAgICAgIDxvcGVyYW5kIG5hbWU9InZhbHVlIj4KICAgICAgICAgICAgPG9wZXJhbmRUeXBlU3BlY2lmaWVyIGxvY2F0b3I9IjE3MDozMi0xNzA6NDgiIG5hbWU9ImZoaXI6QXBwb2ludG1lbnRTdGF0dXMiIHhzaTp0eXBlPSJOYW1lZFR5cGVTcGVjaWZpZXIiLz4KICAgICAgICAgPC9vcGVyYW5kPgogICAgICA8L2RlZj4KICAgICAgPGRlZiBsb2NhdG9yPSIxNzE6MS0xNzE6NjciIG5hbWU9IlRvU3RyaW5nIiBjb250ZXh0PSJQYXRpZW50IiBhY2Nlc3NMZXZlbD0iUHVibGljIiB4c2k6dHlwZT0iRnVuY3Rpb25EZWYiPgogICAgICAgICA8ZXhwcmVzc2lvbiBsb2NhdG9yPSIxNzE6NTctMTcxOjY3IiBwYXRoPSJ2YWx1ZSIgeHNpOnR5cGU9IlByb3BlcnR5Ij4KICAgICAgICAgICAgPHNvdXJjZSBsb2NhdG9yPSIxNzE6NTctMTcxOjYxIiBuYW1lPSJ2YWx1ZSIgeHNpOnR5cGU9Ik9wZXJhbmRSZWYiLz4KICAgICAgICAgPC9leHByZXNzaW9uPgogICAgICAgICA8b3BlcmFuZCBuYW1lPSJ2YWx1ZSI+CiAgICAgICAgICAgIDxvcGVyYW5kVHlwZVNwZWNpZmllciBsb2NhdG9yPSIxNzE6MzItMTcxOjUzIiBuYW1lPSJmaGlyOkFzc2VydGlvbkRpcmVjdGlvblR5cGUiIHhzaTp0eXBlPSJOYW1lZFR5cGVTcGVjaWZpZXIiLz4KICAgICAgICAgPC9vcGVyYW5kPgogICAgICA8L2RlZj4KICAgICAgPGRlZiBsb2NhdG9yPSIxNzI6MS0xNzI6NjYiIG5hbWU9IlRvU3RyaW5nIiBjb250ZXh0PSJQYXRpZW50IiBhY2Nlc3NMZXZlbD0iUHVibGljIiB4c2k6dHlwZT0iRnVuY3Rpb25EZWYiPgogICAgICAgICA8ZXhwcmVzc2lvbiBsb2NhdG9yPSIxNzI6NTYtMTcyOjY2IiBwYXRoPSJ2YWx1ZSIgeHNpOnR5cGU9IlByb3BlcnR5Ij4KICAgICAgICAgICAgPHNvdXJjZSBsb2NhdG9yPSIxNzI6NTYtMTcyOjYwIiBuYW1lPSJ2YWx1ZSIgeHNpOnR5cGU9Ik9wZXJhbmRSZWYiLz4KICAgICAgICAgPC9leHByZXNzaW9uPgogICAgICAgICA8b3BlcmFuZCBuYW1lPSJ2YWx1ZSI+CiAgICAgICAgICAgIDxvcGVyYW5kVHlwZVNwZWNpZmllciBsb2NhdG9yPSIxNzI6MzItMTcyOjUyIiBuYW1lPSJmaGlyOkFzc2VydGlvbk9wZXJhdG9yVHlwZSIgeHNpOnR5cGU9Ik5hbWVkVHlwZVNwZWNpZmllciIvPgogICAgICAgICA8L29wZXJhbmQ+CiAgICAgIDwvZGVmPgogICAgICA8ZGVmIGxvY2F0b3I9IjE3MzoxLTE3Mzo2NyIgbmFtZT0iVG9TdHJpbmciIGNvbnRleHQ9IlBhdGllbnQiIGFjY2Vzc0xldmVsPSJQdWJsaWMiIHhzaTp0eXBlPSJGdW5jdGlvbkRlZiI+CiAgICAgICAgIDxleHByZXNzaW9uIGxvY2F0b3I9IjE3Mzo1Ny0xNzM6NjciIHBhdGg9InZhbHVlIiB4c2k6dHlwZT0iUHJvcGVydHkiPgogICAgICAgICAgICA8c291cmNlIGxvY2F0b3I9IjE3Mzo1Ny0xNzM6NjEiIG5hbWU9InZhbHVlIiB4c2k6dHlwZT0iT3BlcmFuZFJlZiIvPgogICAgICAgICA8L2V4cHJlc3Npb24+CiAgICAgICAgIDxvcGVyYW5kIG5hbWU9InZhbHVlIj4KICAgICAgICAgICAgPG9wZXJhbmRUeXBlU3BlY2lmaWVyIGxvY2F0b3I9IjE3MzozMi0xNzM6NTMiIG5hbWU9ImZoaXI6QXNzZXJ0aW9uUmVzcG9uc2VUeXBlcyIgeHNpOnR5cGU9Ik5hbWVkVHlwZVNwZWNpZmllciIvPgogICAgICAgICA8L29wZXJhbmQ+CiAgICAgIDwvZGVmPgogICAgICA8ZGVmIGxvY2F0b3I9IjE3NDoxLTE3NDo2MSIgbmFtZT0iVG9TdHJpbmciIGNvbnRleHQ9IlBhdGllbnQiIGFjY2Vzc0xldmVsPSJQdWJsaWMiIHhzaTp0eXBlPSJGdW5jdGlvbkRlZiI+CiAgICAgICAgIDxleHByZXNzaW9uIGxvY2F0b3I9IjE3NDo1MS0xNzQ6NjEiIHBhdGg9InZhbHVlIiB4c2k6dHlwZT0iUHJvcGVydHkiPgogICAgICAgICAgICA8c291cmNlIGxvY2F0b3I9IjE3NDo1MS0xNzQ6NTUiIG5hbWU9InZhbHVlIiB4c2k6dHlwZT0iT3BlcmFuZFJlZiIvPgogICAgICAgICA8L2V4cHJlc3Npb24+CiAgICAgICAgIDxvcGVyYW5kIG5hbWU9InZhbHVlIj4KICAgICAgICAgICAgPG9wZXJhbmRUeXBlU3BlY2lmaWVyIGxvY2F0b3I9IjE3NDozMi0xNzQ6NDciIG5hbWU9ImZoaXI6QXVkaXRFdmVudEFjdGlvbiIgeHNpOnR5cGU9Ik5hbWVkVHlwZVNwZWNpZmllciIvPgogICAgICAgICA8L29wZXJhbmQ+CiAgICAgIDwvZGVmPgogICAgICA8ZGVmIGxvY2F0b3I9IjE3NToxLTE3NTo3MSIgbmFtZT0iVG9TdHJpbmciIGNvbnRleHQ9IlBhdGllbnQiIGFjY2Vzc0xldmVsPSJQdWJsaWMiIHhzaTp0eXBlPSJGdW5jdGlvbkRlZiI+CiAgICAgICAgIDxleHByZXNzaW9uIGxvY2F0b3I9IjE3NTo2MS0xNzU6NzEiIHBhdGg9InZhbHVlIiB4c2k6dHlwZT0iUHJvcGVydHkiPgogICAgICAgICAgICA8c291cmNlIGxvY2F0b3I9IjE3NTo2MS0xNzU6NjUiIG5hbWU9InZhbHVlIiB4c2k6dHlwZT0iT3BlcmFuZFJlZiIvPgogICAgICAgICA8L2V4cHJlc3Npb24+CiAgICAgICAgIDxvcGVyYW5kIG5hbWU9InZhbHVlIj4KICAgICAgICAgICAgPG9wZXJhbmRUeXBlU3BlY2lmaWVyIGxvY2F0b3I9IjE3NTozMi0xNzU6NTciIG5hbWU9ImZoaXI6QXVkaXRFdmVudEFnZW50TmV0d29ya1R5cGUiIHhzaTp0eXBlPSJOYW1lZFR5cGVTcGVjaWZpZXIiLz4KICAgICAgICAgPC9vcGVyYW5kPgogICAgICA8L2RlZj4KICAgICAgPGRlZiBsb2NhdG9yPSIxNzY6MS0xNzY6NjIiIG5hbWU9IlRvU3RyaW5nIiBjb250ZXh0PSJQYXRpZW50IiBhY2Nlc3NMZXZlbD0iUHVibGljIiB4c2k6dHlwZT0iRnVuY3Rpb25EZWYiPgogICAgICAgICA8ZXhwcmVzc2lvbiBsb2NhdG9yPSIxNzY6NTItMTc2OjYyIiBwYXRoPSJ2YWx1ZSIgeHNpOnR5cGU9IlByb3BlcnR5Ij4KICAgICAgICAgICAgPHNvdXJjZSBsb2NhdG9yPSIxNzY6NTItMTc2OjU2IiBuYW1lPSJ2YWx1ZSIgeHNpOnR5cGU9Ik9wZXJhbmRSZWYiLz4KICAgICAgICAgPC9leHByZXNzaW9uPgogICAgICAgICA8b3BlcmFuZCBuYW1lPSJ2YWx1ZSI+CiAgICAgICAgICAgIDxvcGVyYW5kVHlwZVNwZWNpZmllciBsb2NhdG9yPSIxNzY6MzItMTc2OjQ4IiBuYW1lPSJmaGlyOkF1ZGl0RXZlbnRPdXRjb21lIiB4c2k6dHlwZT0iTmFtZWRUeXBlU3BlY2lmaWVyIi8+CiAgICAgICAgIDwvb3BlcmFuZD4KICAgICAgPC9kZWY+CiAgICAgIDxkZWYgbG9jYXRvcj0iMTc3OjEtMTc3OjYwIiBuYW1lPSJUb1N0cmluZyIgY29udGV4dD0iUGF0aWVudCIgYWNjZXNzTGV2ZWw9IlB1YmxpYyIgeHNpOnR5cGU9IkZ1bmN0aW9uRGVmIj4KICAgICAgICAgPGV4cHJlc3Npb24gbG9jYXRvcj0iMTc3OjUwLTE3Nzo2MCIgcGF0aD0idmFsdWUiIHhzaTp0eXBlPSJQcm9wZXJ0eSI+CiAgICAgICAgICAgIDxzb3VyY2UgbG9jYXRvcj0iMTc3OjUwLTE3Nzo1NCIgbmFtZT0idmFsdWUiIHhzaTp0eXBlPSJPcGVyYW5kUmVmIi8+CiAgICAgICAgIDwvZXhwcmVzc2lvbj4KICAgICAgICAgPG9wZXJhbmQgbmFtZT0idmFsdWUiPgogICAgICAgICAgICA8b3BlcmFuZFR5cGVTcGVjaWZpZXIgbG9jYXRvcj0iMTc3OjMyLTE3Nzo0NiIgbmFtZT0iZmhpcjpCaW5kaW5nU3RyZW5ndGgiIHhzaTp0eXBlPSJOYW1lZFR5cGVTcGVjaWZpZXIiLz4KICAgICAgICAgPC9vcGVyYW5kPgogICAgICA8L2RlZj4KICAgICAgPGRlZiBsb2NhdG9yPSIxNzg6MS0xNzg6NzkiIG5hbWU9IlRvU3RyaW5nIiBjb250ZXh0PSJQYXRpZW50IiBhY2Nlc3NMZXZlbD0iUHVibGljIiB4c2k6dHlwZT0iRnVuY3Rpb25EZWYiPgogICAgICAgICA8ZXhwcmVzc2lvbiBsb2NhdG9yPSIxNzg6NjktMTc4Ojc5IiBwYXRoPSJ2YWx1ZSIgeHNpOnR5cGU9IlByb3BlcnR5Ij4KICAgICAgICAgICAgPHNvdXJjZSBsb2NhdG9yPSIxNzg6NjktMTc4OjczIiBuYW1lPSJ2YWx1ZSIgeHNpOnR5cGU9Ik9wZXJhbmRSZWYiLz4KICAgICAgICAgPC9leHByZXNzaW9uPgogICAgICAgICA8b3BlcmFuZCBuYW1lPSJ2YWx1ZSI+CiAgICAgICAgICAgIDxvcGVyYW5kVHlwZVNwZWNpZmllciBsb2NhdG9yPSIxNzg6MzItMTc4OjY1IiBuYW1lPSJmaGlyOkJpb2xvZ2ljYWxseURlcml2ZWRQcm9kdWN0Q2F0ZWdvcnkiIHhzaTp0eXBlPSJOYW1lZFR5cGVTcGVjaWZpZXIiLz4KICAgICAgICAgPC9vcGVyYW5kPgogICAgICA8L2RlZj4KICAgICAgPGRlZiBsb2NhdG9yPSIxNzk6MS0xNzk6NzciIG5hbWU9IlRvU3RyaW5nIiBjb250ZXh0PSJQYXRpZW50IiBhY2Nlc3NMZXZlbD0iUHVibGljIiB4c2k6dHlwZT0iRnVuY3Rpb25EZWYiPgogICAgICAgICA8ZXhwcmVzc2lvbiBsb2NhdG9yPSIxNzk6NjctMTc5Ojc3IiBwYXRoPSJ2YWx1ZSIgeHNpOnR5cGU9IlByb3BlcnR5Ij4KICAgICAgICAgICAgPHNvdXJjZSBsb2NhdG9yPSIxNzk6NjctMTc5OjcxIiBuYW1lPSJ2YWx1ZSIgeHNpOnR5cGU9Ik9wZXJhbmRSZWYiLz4KICAgICAgICAgPC9leHByZXNzaW9uPgogICAgICAgICA8b3BlcmFuZCBuYW1lPSJ2YWx1ZSI+CiAgICAgICAgICAgIDxvcGVyYW5kVHlwZVNwZWNpZmllciBsb2NhdG9yPSIxNzk6MzItMTc5OjYzIiBuYW1lPSJmaGlyOkJpb2xvZ2ljYWxseURlcml2ZWRQcm9kdWN0U3RhdHVzIiB4c2k6dHlwZT0iTmFtZWRUeXBlU3BlY2lmaWVyIi8+CiAgICAgICAgIDwvb3BlcmFuZD4KICAgICAgPC9kZWY+CiAgICAgIDxkZWYgbG9jYXRvcj0iMTgwOjEtMTgwOjgzIiBuYW1lPSJUb1N0cmluZyIgY29udGV4dD0iUGF0aWVudCIgYWNjZXNzTGV2ZWw9IlB1YmxpYyIgeHNpOnR5cGU9IkZ1bmN0aW9uRGVmIj4KICAgICAgICAgPGV4cHJlc3Npb24gbG9jYXRvcj0iMTgwOjczLTE4MDo4MyIgcGF0aD0idmFsdWUiIHhzaTp0eXBlPSJQcm9wZXJ0eSI+CiAgICAgICAgICAgIDxzb3VyY2UgbG9jYXRvcj0iMTgwOjczLTE4MDo3NyIgbmFtZT0idmFsdWUiIHhzaTp0eXBlPSJPcGVyYW5kUmVmIi8+CiAgICAgICAgIDwvZXhwcmVzc2lvbj4KICAgICAgICAgPG9wZXJhbmQgbmFtZT0idmFsdWUiPgogICAgICAgICAgICA8b3BlcmFuZFR5cGVTcGVjaWZpZXIgbG9jYXRvcj0iMTgwOjMyLTE4MDo2OSIgbmFtZT0iZmhpcjpCaW9sb2dpY2FsbHlEZXJpdmVkUHJvZHVjdFN0b3JhZ2VTY2FsZSIgeHNpOnR5cGU9Ik5hbWVkVHlwZVNwZWNpZmllciIvPgogICAgICAgICA8L29wZXJhbmQ+CiAgICAgIDwvZGVmPgogICAgICA8ZGVmIGxvY2F0b3I9IjE4MToxLTE4MTo1NSIgbmFtZT0iVG9TdHJpbmciIGNvbnRleHQ9IlBhdGllbnQiIGFjY2Vzc0xldmVsPSJQdWJsaWMiIHhzaTp0eXBlPSJGdW5jdGlvbkRlZiI+CiAgICAgICAgIDxleHByZXNzaW9uIGxvY2F0b3I9IjE4MTo0NS0xODE6NTUiIHBhdGg9InZhbHVlIiB4c2k6dHlwZT0iUHJvcGVydHkiPgogICAgICAgICAgICA8c291cmNlIGxvY2F0b3I9IjE4MTo0NS0xODE6NDkiIG5hbWU9InZhbHVlIiB4c2k6dHlwZT0iT3BlcmFuZFJlZiIvPgogICAgICAgICA8L2V4cHJlc3Npb24+CiAgICAgICAgIDxvcGVyYW5kIG5hbWU9InZhbHVlIj4KICAgICAgICAgICAgPG9wZXJhbmRUeXBlU3BlY2lmaWVyIGxvY2F0b3I9IjE4MTozMi0xODE6NDEiIG5hbWU9ImZoaXI6QnVuZGxlVHlwZSIgeHNpOnR5cGU9Ik5hbWVkVHlwZVNwZWNpZmllciIvPgogICAgICAgICA8L29wZXJhbmQ+CiAgICAgIDwvZGVmPgogICAgICA8ZGVmIGxvY2F0b3I9IjE4MjoxLTE4Mjo2OCIgbmFtZT0iVG9TdHJpbmciIGNvbnRleHQ9IlBhdGllbnQiIGFjY2Vzc0xldmVsPSJQdWJsaWMiIHhzaTp0eXBlPSJGdW5jdGlvbkRlZiI+CiAgICAgICAgIDxleHByZXNzaW9uIGxvY2F0b3I9IjE4Mjo1OC0xODI6NjgiIHBhdGg9InZhbHVlIiB4c2k6dHlwZT0iUHJvcGVydHkiPgogICAgICAgICAgICA8c291cmNlIGxvY2F0b3I9IjE4Mjo1OC0xODI6NjIiIG5hbWU9InZhbHVlIiB4c2k6dHlwZT0iT3BlcmFuZFJlZiIvPgogICAgICAgICA8L2V4cHJlc3Npb24+CiAgICAgICAgIDxvcGVyYW5kIG5hbWU9InZhbHVlIj4KICAgICAgICAgICAgPG9wZXJhbmRUeXBlU3BlY2lmaWVyIGxvY2F0b3I9IjE4MjozMi0xODI6NTQiIG5hbWU9ImZoaXI6Q2FwYWJpbGl0eVN0YXRlbWVudEtpbmQiIHhzaTp0eXBlPSJOYW1lZFR5cGVTcGVjaWZpZXIiLz4KICAgICAgICAgPC9vcGVyYW5kPgogICAgICA8L2RlZj4KICAgICAgPGRlZiBsb2NhdG9yPSIxODM6MS0xODM6NjUiIG5hbWU9IlRvU3RyaW5nIiBjb250ZXh0PSJQYXRpZW50IiBhY2Nlc3NMZXZlbD0iUHVibGljIiB4c2k6dHlwZT0iRnVuY3Rpb25EZWYiPgogICAgICAgICA8ZXhwcmVzc2lvbiBsb2NhdG9yPSIxODM6NTUtMTgzOjY1IiBwYXRoPSJ2YWx1ZSIgeHNpOnR5cGU9IlByb3BlcnR5Ij4KICAgICAgICAgICAgPHNvdXJjZSBsb2NhdG9yPSIxODM6NTUtMTgzOjU5IiBuYW1lPSJ2YWx1ZSIgeHNpOnR5cGU9Ik9wZXJhbmRSZWYiLz4KICAgICAgICAgPC9leHByZXNzaW9uPgogICAgICAgICA8b3BlcmFuZCBuYW1lPSJ2YWx1ZSI+CiAgICAgICAgICAgIDxvcGVyYW5kVHlwZVNwZWNpZmllciBsb2NhdG9yPSIxODM6MzItMTgzOjUxIiBuYW1lPSJmaGlyOkNhcmVQbGFuQWN0aXZpdHlLaW5kIiB4c2k6dHlwZT0iTmFtZWRUeXBlU3BlY2lmaWVyIi8+CiAgICAgICAgIDwvb3BlcmFuZD4KICAgICAgPC9kZWY+CiAgICAgIDxkZWYgbG9jYXRvcj0iMTg0OjEtMTg0OjY3IiBuYW1lPSJUb1N0cmluZyIgY29udGV4dD0iUGF0aWVudCIgYWNjZXNzTGV2ZWw9IlB1YmxpYyIgeHNpOnR5cGU9IkZ1bmN0aW9uRGVmIj4KICAgICAgICAgPGV4cHJlc3Npb24gbG9jYXRvcj0iMTg0OjU3LTE4NDo2NyIgcGF0aD0idmFsdWUiIHhzaTp0eXBlPSJQcm9wZXJ0eSI+CiAgICAgICAgICAgIDxzb3VyY2UgbG9jYXRvcj0iMTg0OjU3LTE4NDo2MSIgbmFtZT0idmFsdWUiIHhzaTp0eXBlPSJPcGVyYW5kUmVmIi8+CiAgICAgICAgIDwvZXhwcmVzc2lvbj4KICAgICAgICAgPG9wZXJhbmQgbmFtZT0idmFsdWUiPgogICAgICAgICAgICA8b3BlcmFuZFR5cGVTcGVjaWZpZXIgbG9jYXRvcj0iMTg0OjMyLTE4NDo1MyIgbmFtZT0iZmhpcjpDYXJlUGxhbkFjdGl2aXR5U3RhdHVzIiB4c2k6dHlwZT0iTmFtZWRUeXBlU3BlY2lmaWVyIi8+CiAgICAgICAgIDwvb3BlcmFuZD4KICAgICAgPC9kZWY+CiAgICAgIDxkZWYgbG9jYXRvcj0iMTg1OjEtMTg1OjU5IiBuYW1lPSJUb1N0cmluZyIgY29udGV4dD0iUGF0aWVudCIgYWNjZXNzTGV2ZWw9IlB1YmxpYyIgeHNpOnR5cGU9IkZ1bmN0aW9uRGVmIj4KICAgICAgICAgPGV4cHJlc3Npb24gbG9jYXRvcj0iMTg1OjQ5LTE4NTo1OSIgcGF0aD0idmFsdWUiIHhzaTp0eXBlPSJQcm9wZXJ0eSI+CiAgICAgICAgICAgIDxzb3VyY2UgbG9jYXRvcj0iMTg1OjQ5LTE4NTo1MyIgbmFtZT0idmFsdWUiIHhzaTp0eXBlPSJPcGVyYW5kUmVmIi8+CiAgICAgICAgIDwvZXhwcmVzc2lvbj4KICAgICAgICAgPG9wZXJhbmQgbmFtZT0idmFsdWUiPgogICAgICAgICAgICA8b3BlcmFuZFR5cGVTcGVjaWZpZXIgbG9jYXRvcj0iMTg1OjMyLTE4NTo0NSIgbmFtZT0iZmhpcjpDYXJlUGxhbkludGVudCIgeHNpOnR5cGU9Ik5hbWVkVHlwZVNwZWNpZmllciIvPgogICAgICAgICA8L29wZXJhbmQ+CiAgICAgIDwvZGVmPgogICAgICA8ZGVmIGxvY2F0b3I9IjE4NjoxLTE4Njo1OSIgbmFtZT0iVG9TdHJpbmciIGNvbnRleHQ9IlBhdGllbnQiIGFjY2Vzc0xldmVsPSJQdWJsaWMiIHhzaTp0eXBlPSJGdW5jdGlvbkRlZiI+CiAgICAgICAgIDxleHByZXNzaW9uIGxvY2F0b3I9IjE4Njo0OS0xODY6NTkiIHBhdGg9InZhbHVlIiB4c2k6dHlwZT0iUHJvcGVydHkiPgogICAgICAgICAgICA8c291cmNlIGxvY2F0b3I9IjE4Njo0OS0xODY6NTMiIG5hbWU9InZhbHVlIiB4c2k6dHlwZT0iT3BlcmFuZFJlZiIvPgogICAgICAgICA8L2V4cHJlc3Npb24+CiAgICAgICAgIDxvcGVyYW5kIG5hbWU9InZhbHVlIj4KICAgICAgICAgICAgPG9wZXJhbmRUeXBlU3BlY2lmaWVyIGxvY2F0b3I9IjE4NjozMi0xODY6NDUiIG5hbWU9ImZoaXI6Q2FyZVBsYW5TdGF0dXMiIHhzaTp0eXBlPSJOYW1lZFR5cGVTcGVjaWZpZXIiLz4KICAgICAgICAgPC9vcGVyYW5kPgogICAgICA8L2RlZj4KICAgICAgPGRlZiBsb2NhdG9yPSIxODc6MS0xODc6NTkiIG5hbWU9IlRvU3RyaW5nIiBjb250ZXh0PSJQYXRpZW50IiBhY2Nlc3NMZXZlbD0iUHVibGljIiB4c2k6dHlwZT0iRnVuY3Rpb25EZWYiPgogICAgICAgICA8ZXhwcmVzc2lvbiBsb2NhdG9yPSIxODc6NDktMTg3OjU5IiBwYXRoPSJ2YWx1ZSIgeHNpOnR5cGU9IlByb3BlcnR5Ij4KICAgICAgICAgICAgPHNvdXJjZSBsb2NhdG9yPSIxODc6NDktMTg3OjUzIiBuYW1lPSJ2YWx1ZSIgeHNpOnR5cGU9Ik9wZXJhbmRSZWYiLz4KICAgICAgICAgPC9leHByZXNzaW9uPgogICAgICAgICA8b3BlcmFuZCBuYW1lPSJ2YWx1ZSI+CiAgICAgICAgICAgIDxvcGVyYW5kVHlwZVNwZWNpZmllciBsb2NhdG9yPSIxODc6MzItMTg3OjQ1IiBuYW1lPSJmaGlyOkNhcmVUZWFtU3RhdHVzIiB4c2k6dHlwZT0iTmFtZWRUeXBlU3BlY2lmaWVyIi8+CiAgICAgICAgIDwvb3BlcmFuZD4KICAgICAgPC9kZWY+CiAgICAgIDxkZWYgbG9jYXRvcj0iMTg4OjEtMTg4OjY5IiBuYW1lPSJUb1N0cmluZyIgY29udGV4dD0iUGF0aWVudCIgYWNjZXNzTGV2ZWw9IlB1YmxpYyIgeHNpOnR5cGU9IkZ1bmN0aW9uRGVmIj4KICAgICAgICAgPGV4cHJlc3Npb24gbG9jYXRvcj0iMTg4OjU5LTE4ODo2OSIgcGF0aD0idmFsdWUiIHhzaTp0eXBlPSJQcm9wZXJ0eSI+CiAgICAgICAgICAgIDxzb3VyY2UgbG9jYXRvcj0iMTg4OjU5LTE4ODo2MyIgbmFtZT0idmFsdWUiIHhzaTp0eXBlPSJPcGVyYW5kUmVmIi8+CiAgICAgICAgIDwvZXhwcmVzc2lvbj4KICAgICAgICAgPG9wZXJhbmQgbmFtZT0idmFsdWUiPgogICAgICAgICAgICA8b3BlcmFuZFR5cGVTcGVjaWZpZXIgbG9jYXRvcj0iMTg4OjMyLTE4ODo1NSIgbmFtZT0iZmhpcjpDYXRhbG9nRW50cnlSZWxhdGlvblR5cGUiIHhzaTp0eXBlPSJOYW1lZFR5cGVTcGVjaWZpZXIiLz4KICAgICAgICAgPC9vcGVyYW5kPgogICAgICA8L2RlZj4KICAgICAgPGRlZiBsb2NhdG9yPSIxODk6MS0xODk6ODMiIG5hbWU9IlRvU3RyaW5nIiBjb250ZXh0PSJQYXRpZW50IiBhY2Nlc3NMZXZlbD0iUHVibGljIiB4c2k6dHlwZT0iRnVuY3Rpb25EZWYiPgogICAgICAgICA8ZXhwcmVzc2lvbiBsb2NhdG9yPSIxODk6NzMtMTg5OjgzIiBwYXRoPSJ2YWx1ZSIgeHNpOnR5cGU9IlByb3BlcnR5Ij4KICAgICAgICAgICAgPHNvdXJjZSBsb2NhdG9yPSIxODk6NzMtMTg5Ojc3IiBuYW1lPSJ2YWx1ZSIgeHNpOnR5cGU9Ik9wZXJhbmRSZWYiLz4KICAgICAgICAgPC9leHByZXNzaW9uPgogICAgICAgICA8b3BlcmFuZCBuYW1lPSJ2YWx1ZSI+CiAgICAgICAgICAgIDxvcGVyYW5kVHlwZVNwZWNpZmllciBsb2NhdG9yPSIxODk6MzItMTg5OjY5IiBuYW1lPSJmaGlyOkNoYXJnZUl0ZW1EZWZpbml0aW9uUHJpY2VDb21wb25lbnRUeXBlIiB4c2k6dHlwZT0iTmFtZWRUeXBlU3BlY2lmaWVyIi8+CiAgICAgICAgIDwvb3BlcmFuZD4KICAgICAgPC9kZWY+CiAgICAgIDxkZWYgbG9jYXRvcj0iMTkwOjEtMTkwOjYxIiBuYW1lPSJUb1N0cmluZyIgY29udGV4dD0iUGF0aWVudCIgYWNjZXNzTGV2ZWw9IlB1YmxpYyIgeHNpOnR5cGU9IkZ1bmN0aW9uRGVmIj4KICAgICAgICAgPGV4cHJlc3Npb24gbG9jYXRvcj0iMTkwOjUxLTE5MDo2MSIgcGF0aD0idmFsdWUiIHhzaTp0eXBlPSJQcm9wZXJ0eSI+CiAgICAgICAgICAgIDxzb3VyY2UgbG9jYXRvcj0iMTkwOjUxLTE5MDo1NSIgbmFtZT0idmFsdWUiIHhzaTp0eXBlPSJPcGVyYW5kUmVmIi8+CiAgICAgICAgIDwvZXhwcmVzc2lvbj4KICAgICAgICAgPG9wZXJhbmQgbmFtZT0idmFsdWUiPgogICAgICAgICAgICA8b3BlcmFuZFR5cGVTcGVjaWZpZXIgbG9jYXRvcj0iMTkwOjMyLTE5MDo0NyIgbmFtZT0iZmhpcjpDaGFyZ2VJdGVtU3RhdHVzIiB4c2k6dHlwZT0iTmFtZWRUeXBlU3BlY2lmaWVyIi8+CiAgICAgICAgIDwvb3BlcmFuZD4KICAgICAgPC9kZWY+CiAgICAgIDxkZWYgbG9jYXRvcj0iMTkxOjEtMTkxOjY0IiBuYW1lPSJUb1N0cmluZyIgY29udGV4dD0iUGF0aWVudCIgYWNjZXNzTGV2ZWw9IlB1YmxpYyIgeHNpOnR5cGU9IkZ1bmN0aW9uRGVmIj4KICAgICAgICAgPGV4cHJlc3Npb24gbG9jYXRvcj0iMTkxOjU0LTE5MTo2NCIgcGF0aD0idmFsdWUiIHhzaTp0eXBlPSJQcm9wZXJ0eSI+CiAgICAgICAgICAgIDxzb3VyY2UgbG9jYXRvcj0iMTkxOjU0LTE5MTo1OCIgbmFtZT0idmFsdWUiIHhzaTp0eXBlPSJPcGVyYW5kUmVmIi8+CiAgICAgICAgIDwvZXhwcmVzc2lvbj4KICAgICAgICAgPG9wZXJhbmQgbmFtZT0idmFsdWUiPgogICAgICAgICAgICA8b3BlcmFuZFR5cGVTcGVjaWZpZXIgbG9jYXRvcj0iMTkxOjMyLTE5MTo1MCIgbmFtZT0iZmhpcjpDbGFpbVJlc3BvbnNlU3RhdHVzIiB4c2k6dHlwZT0iTmFtZWRUeXBlU3BlY2lmaWVyIi8+CiAgICAgICAgIDwvb3BlcmFuZD4KICAgICAgPC9kZWY+CiAgICAgIDxkZWYgbG9jYXRvcj0iMTkyOjEtMTkyOjU2IiBuYW1lPSJUb1N0cmluZyIgY29udGV4dD0iUGF0aWVudCIgYWNjZXNzTGV2ZWw9IlB1YmxpYyIgeHNpOnR5cGU9IkZ1bmN0aW9uRGVmIj4KICAgICAgICAgPGV4cHJlc3Npb24gbG9jYXRvcj0iMTkyOjQ2LTE5Mjo1NiIgcGF0aD0idmFsdWUiIHhzaTp0eXBlPSJQcm9wZXJ0eSI+CiAgICAgICAgICAgIDxzb3VyY2UgbG9jYXRvcj0iMTkyOjQ2LTE5Mjo1MCIgbmFtZT0idmFsdWUiIHhzaTp0eXBlPSJPcGVyYW5kUmVmIi8+CiAgICAgICAgIDwvZXhwcmVzc2lvbj4KICAgICAgICAgPG9wZXJhbmQgbmFtZT0idmFsdWUiPgogICAgICAgICAgICA8b3BlcmFuZFR5cGVTcGVjaWZpZXIgbG9jYXRvcj0iMTkyOjMyLTE5Mjo0MiIgbmFtZT0iZmhpcjpDbGFpbVN0YXR1cyIgeHNpOnR5cGU9Ik5hbWVkVHlwZVNwZWNpZmllciIvPgogICAgICAgICA8L29wZXJhbmQ+CiAgICAgIDwvZGVmPgogICAgICA8ZGVmIGxvY2F0b3I9IjE5MzoxLTE5Mzo2OSIgbmFtZT0iVG9TdHJpbmciIGNvbnRleHQ9IlBhdGllbnQiIGFjY2Vzc0xldmVsPSJQdWJsaWMiIHhzaTp0eXBlPSJGdW5jdGlvbkRlZiI+CiAgICAgICAgIDxleHByZXNzaW9uIGxvY2F0b3I9IjE5Mzo1OS0xOTM6NjkiIHBhdGg9InZhbHVlIiB4c2k6dHlwZT0iUHJvcGVydHkiPgogICAgICAgICAgICA8c291cmNlIGxvY2F0b3I9IjE5Mzo1OS0xOTM6NjMiIG5hbWU9InZhbHVlIiB4c2k6dHlwZT0iT3BlcmFuZFJlZiIvPgogICAgICAgICA8L2V4cHJlc3Npb24+CiAgICAgICAgIDxvcGVyYW5kIG5hbWU9InZhbHVlIj4KICAgICAgICAgICAgPG9wZXJhbmRUeXBlU3BlY2lmaWVyIGxvY2F0b3I9IjE5MzozMi0xOTM6NTUiIG5hbWU9ImZoaXI6Q2xpbmljYWxJbXByZXNzaW9uU3RhdHVzIiB4c2k6dHlwZT0iTmFtZWRUeXBlU3BlY2lmaWVyIi8+CiAgICAgICAgIDwvb3BlcmFuZD4KICAgICAgPC9kZWY+CiAgICAgIDxkZWYgbG9jYXRvcj0iMTk0OjEtMTk0OjYyIiBuYW1lPSJUb1N0cmluZyIgY29udGV4dD0iUGF0aWVudCIgYWNjZXNzTGV2ZWw9IlB1YmxpYyIgeHNpOnR5cGU9IkZ1bmN0aW9uRGVmIj4KICAgICAgICAgPGV4cHJlc3Npb24gbG9jYXRvcj0iMTk0OjUyLTE5NDo2MiIgcGF0aD0idmFsdWUiIHhzaTp0eXBlPSJQcm9wZXJ0eSI+CiAgICAgICAgICAgIDxzb3VyY2UgbG9jYXRvcj0iMTk0OjUyLTE5NDo1NiIgbmFtZT0idmFsdWUiIHhzaTp0eXBlPSJPcGVyYW5kUmVmIi8+CiAgICAgICAgIDwvZXhwcmVzc2lvbj4KICAgICAgICAgPG9wZXJhbmQgbmFtZT0idmFsdWUiPgogICAgICAgICAgICA8b3BlcmFuZFR5cGVTcGVjaWZpZXIgbG9jYXRvcj0iMTk0OjMyLTE5NDo0OCIgbmFtZT0iZmhpcjpDb2RlU2VhcmNoU3VwcG9ydCIgeHNpOnR5cGU9Ik5hbWVkVHlwZVNwZWNpZmllciIvPgogICAgICAgICA8L29wZXJhbmQ+CiAgICAgIDwvZGVmPgogICAgICA8ZGVmIGxvY2F0b3I9IjE5NToxLTE5NTo2NiIgbmFtZT0iVG9TdHJpbmciIGNvbnRleHQ9IlBhdGllbnQiIGFjY2Vzc0xldmVsPSJQdWJsaWMiIHhzaTp0eXBlPSJGdW5jdGlvbkRlZiI+CiAgICAgICAgIDxleHByZXNzaW9uIGxvY2F0b3I9IjE5NTo1Ni0xOTU6NjYiIHBhdGg9InZhbHVlIiB4c2k6dHlwZT0iUHJvcGVydHkiPgogICAgICAgICAgICA8c291cmNlIGxvY2F0b3I9IjE5NTo1Ni0xOTU6NjAiIG5hbWU9InZhbHVlIiB4c2k6dHlwZT0iT3BlcmFuZFJlZiIvPgogICAgICAgICA8L2V4cHJlc3Npb24+CiAgICAgICAgIDxvcGVyYW5kIG5hbWU9InZhbHVlIj4KICAgICAgICAgICAgPG9wZXJhbmRUeXBlU3BlY2lmaWVyIGxvY2F0b3I9IjE5NTozMi0xOTU6NTIiIG5hbWU9ImZoaXI6Q29kZVN5c3RlbUNvbnRlbnRNb2RlIiB4c2k6dHlwZT0iTmFtZWRUeXBlU3BlY2lmaWVyIi8+CiAgICAgICAgIDwvb3BlcmFuZD4KICAgICAgPC9kZWY+CiAgICAgIDxkZWYgbG9jYXRvcj0iMTk2OjEtMTk2OjcxIiBuYW1lPSJUb1N0cmluZyIgY29udGV4dD0iUGF0aWVudCIgYWNjZXNzTGV2ZWw9IlB1YmxpYyIgeHNpOnR5cGU9IkZ1bmN0aW9uRGVmIj4KICAgICAgICAgPGV4cHJlc3Npb24gbG9jYXRvcj0iMTk2OjYxLTE5Njo3MSIgcGF0aD0idmFsdWUiIHhzaTp0eXBlPSJQcm9wZXJ0eSI+CiAgICAgICAgICAgIDxzb3VyY2UgbG9jYXRvcj0iMTk2OjYxLTE5Njo2NSIgbmFtZT0idmFsdWUiIHhzaTp0eXBlPSJPcGVyYW5kUmVmIi8+CiAgICAgICAgIDwvZXhwcmVzc2lvbj4KICAgICAgICAgPG9wZXJhbmQgbmFtZT0idmFsdWUiPgogICAgICAgICAgICA8b3BlcmFuZFR5cGVTcGVjaWZpZXIgbG9jYXRvcj0iMTk2OjMyLTE5Njo1NyIgbmFtZT0iZmhpcjpDb2RlU3lzdGVtSGllcmFyY2h5TWVhbmluZyIgeHNpOnR5cGU9Ik5hbWVkVHlwZVNwZWNpZmllciIvPgogICAgICAgICA8L29wZXJhbmQ+CiAgICAgIDwvZGVmPgogICAgICA8ZGVmIGxvY2F0b3I9IjE5NzoxLTE5Nzo2NiIgbmFtZT0iVG9TdHJpbmciIGNvbnRleHQ9IlBhdGllbnQiIGFjY2Vzc0xldmVsPSJQdWJsaWMiIHhzaTp0eXBlPSJGdW5jdGlvbkRlZiI+CiAgICAgICAgIDxleHByZXNzaW9uIGxvY2F0b3I9IjE5Nzo1Ni0xOTc6NjYiIHBhdGg9InZhbHVlIiB4c2k6dHlwZT0iUHJvcGVydHkiPgogICAgICAgICAgICA8c291cmNlIGxvY2F0b3I9IjE5Nzo1Ni0xOTc6NjAiIG5hbWU9InZhbHVlIiB4c2k6dHlwZT0iT3BlcmFuZFJlZiIvPgogICAgICAgICA8L2V4cHJlc3Npb24+CiAgICAgICAgIDxvcGVyYW5kIG5hbWU9InZhbHVlIj4KICAgICAgICAgICAgPG9wZXJhbmRUeXBlU3BlY2lmaWVyIGxvY2F0b3I9IjE5NzozMi0xOTc6NTIiIG5hbWU9ImZoaXI6Q29tbXVuaWNhdGlvblByaW9yaXR5IiB4c2k6dHlwZT0iTmFtZWRUeXBlU3BlY2lmaWVyIi8+CiAgICAgICAgIDwvb3BlcmFuZD4KICAgICAgPC9kZWY+CiAgICAgIDxkZWYgbG9jYXRvcj0iMTk4OjEtMTk4OjcxIiBuYW1lPSJUb1N0cmluZyIgY29udGV4dD0iUGF0aWVudCIgYWNjZXNzTGV2ZWw9IlB1YmxpYyIgeHNpOnR5cGU9IkZ1bmN0aW9uRGVmIj4KICAgICAgICAgPGV4cHJlc3Npb24gbG9jYXRvcj0iMTk4OjYxLTE5ODo3MSIgcGF0aD0idmFsdWUiIHhzaTp0eXBlPSJQcm9wZXJ0eSI+CiAgICAgICAgICAgIDxzb3VyY2UgbG9jYXRvcj0iMTk4OjYxLTE5ODo2NSIgbmFtZT0idmFsdWUiIHhzaTp0eXBlPSJPcGVyYW5kUmVmIi8+CiAgICAgICAgIDwvZXhwcmVzc2lvbj4KICAgICAgICAgPG9wZXJhbmQgbmFtZT0idmFsdWUiPgogICAgICAgICAgICA8b3BlcmFuZFR5cGVTcGVjaWZpZXIgbG9jYXRvcj0iMTk4OjMyLTE5ODo1NyIgbmFtZT0iZmhpcjpDb21tdW5pY2F0aW9uUmVxdWVzdFN0YXR1cyIgeHNpOnR5cGU9Ik5hbWVkVHlwZVNwZWNpZmllciIvPgogICAgICAgICA8L29wZXJhbmQ+CiAgICAgIDwvZGVmPgogICAgICA8ZGVmIGxvY2F0b3I9IjE5OToxLTE5OTo2NCIgbmFtZT0iVG9TdHJpbmciIGNvbnRleHQ9IlBhdGllbnQiIGFjY2Vzc0xldmVsPSJQdWJsaWMiIHhzaTp0eXBlPSJGdW5jdGlvbkRlZiI+CiAgICAgICAgIDxleHByZXNzaW9uIGxvY2F0b3I9IjE5OTo1NC0xOTk6NjQiIHBhdGg9InZhbHVlIiB4c2k6dHlwZT0iUHJvcGVydHkiPgogICAgICAgICAgICA8c291cmNlIGxvY2F0b3I9IjE5OTo1NC0xOTk6NTgiIG5hbWU9InZhbHVlIiB4c2k6dHlwZT0iT3BlcmFuZFJlZiIvPgogICAgICAgICA8L2V4cHJlc3Npb24+CiAgICAgICAgIDxvcGVyYW5kIG5hbWU9InZhbHVlIj4KICAgICAgICAgICAgPG9wZXJhbmRUeXBlU3BlY2lmaWVyIGxvY2F0b3I9IjE5OTozMi0xOTk6NTAiIG5hbWU9ImZoaXI6Q29tbXVuaWNhdGlvblN0YXR1cyIgeHNpOnR5cGU9Ik5hbWVkVHlwZVNwZWNpZmllciIvPgogICAgICAgICA8L29wZXJhbmQ+CiAgICAgIDwvZGVmPgogICAgICA8ZGVmIGxvY2F0b3I9IjIwMDoxLTIwMDo2MCIgbmFtZT0iVG9TdHJpbmciIGNvbnRleHQ9IlBhdGllbnQiIGFjY2Vzc0xldmVsPSJQdWJsaWMiIHhzaTp0eXBlPSJGdW5jdGlvbkRlZiI+CiAgICAgICAgIDxleHByZXNzaW9uIGxvY2F0b3I9IjIwMDo1MC0yMDA6NjAiIHBhdGg9InZhbHVlIiB4c2k6dHlwZT0iUHJvcGVydHkiPgogICAgICAgICAgICA8c291cmNlIGxvY2F0b3I9IjIwMDo1MC0yMDA6NTQiIG5hbWU9InZhbHVlIiB4c2k6dHlwZT0iT3BlcmFuZFJlZiIvPgogICAgICAgICA8L2V4cHJlc3Npb24+CiAgICAgICAgIDxvcGVyYW5kIG5hbWU9InZhbHVlIj4KICAgICAgICAgICAgPG9wZXJhbmRUeXBlU3BlY2lmaWVyIGxvY2F0b3I9IjIwMDozMi0yMDA6NDYiIG5hbWU9ImZoaXI6Q29tcGFydG1lbnRDb2RlIiB4c2k6dHlwZT0iTmFtZWRUeXBlU3BlY2lmaWVyIi8+CiAgICAgICAgIDwvb3BlcmFuZD4KICAgICAgPC9kZWY+CiAgICAgIDxkZWYgbG9jYXRvcj0iMjAxOjEtMjAxOjYwIiBuYW1lPSJUb1N0cmluZyIgY29udGV4dD0iUGF0aWVudCIgYWNjZXNzTGV2ZWw9IlB1YmxpYyIgeHNpOnR5cGU9IkZ1bmN0aW9uRGVmIj4KICAgICAgICAgPGV4cHJlc3Npb24gbG9jYXRvcj0iMjAxOjUwLTIwMTo2MCIgcGF0aD0idmFsdWUiIHhzaTp0eXBlPSJQcm9wZXJ0eSI+CiAgICAgICAgICAgIDxzb3VyY2UgbG9jYXRvcj0iMjAxOjUwLTIwMTo1NCIgbmFtZT0idmFsdWUiIHhzaTp0eXBlPSJPcGVyYW5kUmVmIi8+CiAgICAgICAgIDwvZXhwcmVzc2lvbj4KICAgICAgICAgPG9wZXJhbmQgbmFtZT0idmFsdWUiPgogICAgICAgICAgICA8b3BlcmFuZFR5cGVTcGVjaWZpZXIgbG9jYXRvcj0iMjAxOjMyLTIwMTo0NiIgbmFtZT0iZmhpcjpDb21wYXJ0bWVudFR5cGUiIHhzaTp0eXBlPSJOYW1lZFR5cGVTcGVjaWZpZXIiLz4KICAgICAgICAgPC9vcGVyYW5kPgogICAgICA8L2RlZj4KICAgICAgPGRlZiBsb2NhdG9yPSIyMDI6MS0yMDI6NzEiIG5hbWU9IlRvU3RyaW5nIiBjb250ZXh0PSJQYXRpZW50IiBhY2Nlc3NMZXZlbD0iUHVibGljIiB4c2k6dHlwZT0iRnVuY3Rpb25EZWYiPgogICAgICAgICA8ZXhwcmVzc2lvbiBsb2NhdG9yPSIyMDI6NjEtMjAyOjcxIiBwYXRoPSJ2YWx1ZSIgeHNpOnR5cGU9IlByb3BlcnR5Ij4KICAgICAgICAgICAgPHNvdXJjZSBsb2NhdG9yPSIyMDI6NjEtMjAyOjY1IiBuYW1lPSJ2YWx1ZSIgeHNpOnR5cGU9Ik9wZXJhbmRSZWYiLz4KICAgICAgICAgPC9leHByZXNzaW9uPgogICAgICAgICA8b3BlcmFuZCBuYW1lPSJ2YWx1ZSI+CiAgICAgICAgICAgIDxvcGVyYW5kVHlwZVNwZWNpZmllciBsb2NhdG9yPSIyMDI6MzItMjAyOjU3IiBuYW1lPSJmaGlyOkNvbXBvc2l0aW9uQXR0ZXN0YXRpb25Nb2RlIiB4c2k6dHlwZT0iTmFtZWRUeXBlU3BlY2lmaWVyIi8+CiAgICAgICAgIDwvb3BlcmFuZD4KICAgICAgPC9kZWY+CiAgICAgIDxkZWYgbG9jYXRvcj0iMjAzOjEtMjAzOjYyIiBuYW1lPSJUb1N0cmluZyIgY29udGV4dD0iUGF0aWVudCIgYWNjZXNzTGV2ZWw9IlB1YmxpYyIgeHNpOnR5cGU9IkZ1bmN0aW9uRGVmIj4KICAgICAgICAgPGV4cHJlc3Npb24gbG9jYXRvcj0iMjAzOjUyLTIwMzo2MiIgcGF0aD0idmFsdWUiIHhzaTp0eXBlPSJQcm9wZXJ0eSI+CiAgICAgICAgICAgIDxzb3VyY2UgbG9jYXRvcj0iMjAzOjUyLTIwMzo1NiIgbmFtZT0idmFsdWUiIHhzaTp0eXBlPSJPcGVyYW5kUmVmIi8+CiAgICAgICAgIDwvZXhwcmVzc2lvbj4KICAgICAgICAgPG9wZXJhbmQgbmFtZT0idmFsdWUiPgogICAgICAgICAgICA8b3BlcmFuZFR5cGVTcGVjaWZpZXIgbG9jYXRvcj0iMjAzOjMyLTIwMzo0OCIgbmFtZT0iZmhpcjpDb21wb3NpdGlvblN0YXR1cyIgeHNpOnR5cGU9Ik5hbWVkVHlwZVNwZWNpZmllciIvPgogICAgICAgICA8L29wZXJhbmQ+CiAgICAgIDwvZGVmPgogICAgICA8ZGVmIGxvY2F0b3I9IjIwNDoxLTIwNDo2NiIgbmFtZT0iVG9TdHJpbmciIGNvbnRleHQ9IlBhdGllbnQiIGFjY2Vzc0xldmVsPSJQdWJsaWMiIHhzaTp0eXBlPSJGdW5jdGlvbkRlZiI+CiAgICAgICAgIDxleHByZXNzaW9uIGxvY2F0b3I9IjIwNDo1Ni0yMDQ6NjYiIHBhdGg9InZhbHVlIiB4c2k6dHlwZT0iUHJvcGVydHkiPgogICAgICAgICAgICA8c291cmNlIGxvY2F0b3I9IjIwNDo1Ni0yMDQ6NjAiIG5hbWU9InZhbHVlIiB4c2k6dHlwZT0iT3BlcmFuZFJlZiIvPgogICAgICAgICA8L2V4cHJlc3Npb24+CiAgICAgICAgIDxvcGVyYW5kIG5hbWU9InZhbHVlIj4KICAgICAgICAgICAgPG9wZXJhbmRUeXBlU3BlY2lmaWVyIGxvY2F0b3I9IjIwNDozMi0yMDQ6NTIiIG5hbWU9ImZoaXI6Q29uY2VwdE1hcEVxdWl2YWxlbmNlIiB4c2k6dHlwZT0iTmFtZWRUeXBlU3BlY2lmaWVyIi8+CiAgICAgICAgIDwvb3BlcmFuZD4KICAgICAgPC9kZWY+CiAgICAgIDxkZWYgbG9jYXRvcj0iMjA1OjEtMjA1OjcyIiBuYW1lPSJUb1N0cmluZyIgY29udGV4dD0iUGF0aWVudCIgYWNjZXNzTGV2ZWw9IlB1YmxpYyIgeHNpOnR5cGU9IkZ1bmN0aW9uRGVmIj4KICAgICAgICAgPGV4cHJlc3Npb24gbG9jYXRvcj0iMjA1OjYyLTIwNTo3MiIgcGF0aD0idmFsdWUiIHhzaTp0eXBlPSJQcm9wZXJ0eSI+CiAgICAgICAgICAgIDxzb3VyY2UgbG9jYXRvcj0iMjA1OjYyLTIwNTo2NiIgbmFtZT0idmFsdWUiIHhzaTp0eXBlPSJPcGVyYW5kUmVmIi8+CiAgICAgICAgIDwvZXhwcmVzc2lvbj4KICAgICAgICAgPG9wZXJhbmQgbmFtZT0idmFsdWUiPgogICAgICAgICAgICA8b3BlcmFuZFR5cGVTcGVjaWZpZXIgbG9jYXRvcj0iMjA1OjMyLTIwNTo1OCIgbmFtZT0iZmhpcjpDb25jZXB0TWFwR3JvdXBVbm1hcHBlZE1vZGUiIHhzaTp0eXBlPSJOYW1lZFR5cGVTcGVjaWZpZXIiLz4KICAgICAgICAgPC9vcGVyYW5kPgogICAgICA8L2RlZj4KICAgICAgPGRlZiBsb2NhdG9yPSIyMDY6MS0yMDY6NjgiIG5hbWU9IlRvU3RyaW5nIiBjb250ZXh0PSJQYXRpZW50IiBhY2Nlc3NMZXZlbD0iUHVibGljIiB4c2k6dHlwZT0iRnVuY3Rpb25EZWYiPgogICAgICAgICA8ZXhwcmVzc2lvbiBsb2NhdG9yPSIyMDY6NTgtMjA2OjY4IiBwYXRoPSJ2YWx1ZSIgeHNpOnR5cGU9IlByb3BlcnR5Ij4KICAgICAgICAgICAgPHNvdXJjZSBsb2NhdG9yPSIyMDY6NTgtMjA2OjYyIiBuYW1lPSJ2YWx1ZSIgeHNpOnR5cGU9Ik9wZXJhbmRSZWYiLz4KICAgICAgICAgPC9leHByZXNzaW9uPgogICAgICAgICA8b3BlcmFuZCBuYW1lPSJ2YWx1ZSI+CiAgICAgICAgICAgIDxvcGVyYW5kVHlwZVNwZWNpZmllciBsb2NhdG9yPSIyMDY6MzItMjA2OjU0IiBuYW1lPSJmaGlyOkNvbmRpdGlvbmFsRGVsZXRlU3RhdHVzIiB4c2k6dHlwZT0iTmFtZWRUeXBlU3BlY2lmaWVyIi8+CiAgICAgICAgIDwvb3BlcmFuZD4KICAgICAgPC9kZWY+CiAgICAgIDxkZWYgbG9jYXRvcj0iMjA3OjEtMjA3OjY2IiBuYW1lPSJUb1N0cmluZyIgY29udGV4dD0iUGF0aWVudCIgYWNjZXNzTGV2ZWw9IlB1YmxpYyIgeHNpOnR5cGU9IkZ1bmN0aW9uRGVmIj4KICAgICAgICAgPGV4cHJlc3Npb24gbG9jYXRvcj0iMjA3OjU2LTIwNzo2NiIgcGF0aD0idmFsdWUiIHhzaTp0eXBlPSJQcm9wZXJ0eSI+CiAgICAgICAgICAgIDxzb3VyY2UgbG9jYXRvcj0iMjA3OjU2LTIwNzo2MCIgbmFtZT0idmFsdWUiIHhzaTp0eXBlPSJPcGVyYW5kUmVmIi8+CiAgICAgICAgIDwvZXhwcmVzc2lvbj4KICAgICAgICAgPG9wZXJhbmQgbmFtZT0idmFsdWUiPgogICAgICAgICAgICA8b3BlcmFuZFR5cGVTcGVjaWZpZXIgbG9jYXRvcj0iMjA3OjMyLTIwNzo1MiIgbmFtZT0iZmhpcjpDb25kaXRpb25hbFJlYWRTdGF0dXMiIHhzaTp0eXBlPSJOYW1lZFR5cGVTcGVjaWZpZXIiLz4KICAgICAgICAgPC9vcGVyYW5kPgogICAgICA8L2RlZj4KICAgICAgPGRlZiBsb2NhdG9yPSIyMDg6MS0yMDg6NjMiIG5hbWU9IlRvU3RyaW5nIiBjb250ZXh0PSJQYXRpZW50IiBhY2Nlc3NMZXZlbD0iUHVibGljIiB4c2k6dHlwZT0iRnVuY3Rpb25EZWYiPgogICAgICAgICA8ZXhwcmVzc2lvbiBsb2NhdG9yPSIyMDg6NTMtMjA4OjYzIiBwYXRoPSJ2YWx1ZSIgeHNpOnR5cGU9IlByb3BlcnR5Ij4KICAgICAgICAgICAgPHNvdXJjZSBsb2NhdG9yPSIyMDg6NTMtMjA4OjU3IiBuYW1lPSJ2YWx1ZSIgeHNpOnR5cGU9Ik9wZXJhbmRSZWYiLz4KICAgICAgICAgPC9leHByZXNzaW9uPgogICAgICAgICA8b3BlcmFuZCBuYW1lPSJ2YWx1ZSI+CiAgICAgICAgICAgIDxvcGVyYW5kVHlwZVNwZWNpZmllciBsb2NhdG9yPSIyMDg6MzItMjA4OjQ5IiBuYW1lPSJmaGlyOkNvbnNlbnREYXRhTWVhbmluZyIgeHNpOnR5cGU9Ik5hbWVkVHlwZVNwZWNpZmllciIvPgogICAgICAgICA8L29wZXJhbmQ+CiAgICAgIDwvZGVmPgogICAgICA8ZGVmIGxvY2F0b3I9IjIwOToxLTIwOTo2NSIgbmFtZT0iVG9TdHJpbmciIGNvbnRleHQ9IlBhdGllbnQiIGFjY2Vzc0xldmVsPSJQdWJsaWMiIHhzaTp0eXBlPSJGdW5jdGlvbkRlZiI+CiAgICAgICAgIDxleHByZXNzaW9uIGxvY2F0b3I9IjIwOTo1NS0yMDk6NjUiIHBhdGg9InZhbHVlIiB4c2k6dHlwZT0iUHJvcGVydHkiPgogICAgICAgICAgICA8c291cmNlIGxvY2F0b3I9IjIwOTo1NS0yMDk6NTkiIG5hbWU9InZhbHVlIiB4c2k6dHlwZT0iT3BlcmFuZFJlZiIvPgogICAgICAgICA8L2V4cHJlc3Npb24+CiAgICAgICAgIDxvcGVyYW5kIG5hbWU9InZhbHVlIj4KICAgICAgICAgICAgPG9wZXJhbmRUeXBlU3BlY2lmaWVyIGxvY2F0b3I9IjIwOTozMi0yMDk6NTEiIG5hbWU9ImZoaXI6Q29uc2VudFByb3Zpc2lvblR5cGUiIHhzaTp0eXBlPSJOYW1lZFR5cGVTcGVjaWZpZXIiLz4KICAgICAgICAgPC9vcGVyYW5kPgogICAgICA8L2RlZj4KICAgICAgPGRlZiBsb2NhdG9yPSIyMTA6MS0yMTA6NTciIG5hbWU9IlRvU3RyaW5nIiBjb250ZXh0PSJQYXRpZW50IiBhY2Nlc3NMZXZlbD0iUHVibGljIiB4c2k6dHlwZT0iRnVuY3Rpb25EZWYiPgogICAgICAgICA8ZXhwcmVzc2lvbiBsb2NhdG9yPSIyMTA6NDctMjEwOjU3IiBwYXRoPSJ2YWx1ZSIgeHNpOnR5cGU9IlByb3BlcnR5Ij4KICAgICAgICAgICAgPHNvdXJjZSBsb2NhdG9yPSIyMTA6NDctMjEwOjUxIiBuYW1lPSJ2YWx1ZSIgeHNpOnR5cGU9Ik9wZXJhbmRSZWYiLz4KICAgICAgICAgPC9leHByZXNzaW9uPgogICAgICAgICA8b3BlcmFuZCBuYW1lPSJ2YWx1ZSI+CiAgICAgICAgICAgIDxvcGVyYW5kVHlwZVNwZWNpZmllciBsb2NhdG9yPSIyMTA6MzItMjEwOjQzIiBuYW1lPSJmaGlyOkNvbnNlbnRTdGF0ZSIgeHNpOnR5cGU9Ik5hbWVkVHlwZVNwZWNpZmllciIvPgogICAgICAgICA8L29wZXJhbmQ+CiAgICAgIDwvZGVmPgogICAgICA8ZGVmIGxvY2F0b3I9IjIxMToxLTIxMTo2MyIgbmFtZT0iVG9TdHJpbmciIGNvbnRleHQ9IlBhdGllbnQiIGFjY2Vzc0xldmVsPSJQdWJsaWMiIHhzaTp0eXBlPSJGdW5jdGlvbkRlZiI+CiAgICAgICAgIDxleHByZXNzaW9uIGxvY2F0b3I9IjIxMTo1My0yMTE6NjMiIHBhdGg9InZhbHVlIiB4c2k6dHlwZT0iUHJvcGVydHkiPgogICAgICAgICAgICA8c291cmNlIGxvY2F0b3I9IjIxMTo1My0yMTE6NTciIG5hbWU9InZhbHVlIiB4c2k6dHlwZT0iT3BlcmFuZFJlZiIvPgogICAgICAgICA8L2V4cHJlc3Npb24+CiAgICAgICAgIDxvcGVyYW5kIG5hbWU9InZhbHVlIj4KICAgICAgICAgICAgPG9wZXJhbmRUeXBlU3BlY2lmaWVyIGxvY2F0b3I9IjIxMTozMi0yMTE6NDkiIG5hbWU9ImZoaXI6Q29uc3RyYWludFNldmVyaXR5IiB4c2k6dHlwZT0iTmFtZWRUeXBlU3BlY2lmaWVyIi8+CiAgICAgICAgIDwvb3BlcmFuZD4KICAgICAgPC9kZWY+CiAgICAgIDxkZWYgbG9jYXRvcj0iMjEyOjEtMjEyOjYzIiBuYW1lPSJUb1N0cmluZyIgY29udGV4dD0iUGF0aWVudCIgYWNjZXNzTGV2ZWw9IlB1YmxpYyIgeHNpOnR5cGU9IkZ1bmN0aW9uRGVmIj4KICAgICAgICAgPGV4cHJlc3Npb24gbG9jYXRvcj0iMjEyOjUzLTIxMjo2MyIgcGF0aD0idmFsdWUiIHhzaTp0eXBlPSJQcm9wZXJ0eSI+CiAgICAgICAgICAgIDxzb3VyY2UgbG9jYXRvcj0iMjEyOjUzLTIxMjo1NyIgbmFtZT0idmFsdWUiIHhzaTp0eXBlPSJPcGVyYW5kUmVmIi8+CiAgICAgICAgIDwvZXhwcmVzc2lvbj4KICAgICAgICAgPG9wZXJhbmQgbmFtZT0idmFsdWUiPgogICAgICAgICAgICA8b3BlcmFuZFR5cGVTcGVjaWZpZXIgbG9jYXRvcj0iMjEyOjMyLTIxMjo0OSIgbmFtZT0iZmhpcjpDb250YWN0UG9pbnRTeXN0ZW0iIHhzaTp0eXBlPSJOYW1lZFR5cGVTcGVjaWZpZXIiLz4KICAgICAgICAgPC9vcGVyYW5kPgogICAgICA8L2RlZj4KICAgICAgPGRlZiBsb2NhdG9yPSIyMTM6MS0yMTM6NjAiIG5hbWU9IlRvU3RyaW5nIiBjb250ZXh0PSJQYXRpZW50IiBhY2Nlc3NMZXZlbD0iUHVibGljIiB4c2k6dHlwZT0iRnVuY3Rpb25EZWYiPgogICAgICAgICA8ZXhwcmVzc2lvbiBsb2NhdG9yPSIyMTM6NTAtMjEzOjYwIiBwYXRoPSJ2YWx1ZSIgeHNpOnR5cGU9IlByb3BlcnR5Ij4KICAgICAgICAgICAgPHNvdXJjZSBsb2NhdG9yPSIyMTM6NTAtMjEzOjU0IiBuYW1lPSJ2YWx1ZSIgeHNpOnR5cGU9Ik9wZXJhbmRSZWYiLz4KICAgICAgICAgPC9leHByZXNzaW9uPgogICAgICAgICA8b3BlcmFuZCBuYW1lPSJ2YWx1ZSI+CiAgICAgICAgICAgIDxvcGVyYW5kVHlwZVNwZWNpZmllciBsb2NhdG9yPSIyMTM6MzItMjEzOjQ2IiBuYW1lPSJmaGlyOkNvbnRhY3RQb2ludFVzZSIgeHNpOnR5cGU9Ik5hbWVkVHlwZVNwZWNpZmllciIvPgogICAgICAgICA8L29wZXJhbmQ+CiAgICAgIDwvZGVmPgogICAgICA8ZGVmIGxvY2F0b3I9IjIxNDoxLTIxNDo3MCIgbmFtZT0iVG9TdHJpbmciIGNvbnRleHQ9IlBhdGllbnQiIGFjY2Vzc0xldmVsPSJQdWJsaWMiIHhzaTp0eXBlPSJGdW5jdGlvbkRlZiI+CiAgICAgICAgIDxleHByZXNzaW9uIGxvY2F0b3I9IjIxNDo2MC0yMTQ6NzAiIHBhdGg9InZhbHVlIiB4c2k6dHlwZT0iUHJvcGVydHkiPgogICAgICAgICAgICA8c291cmNlIGxvY2F0b3I9IjIxNDo2MC0yMTQ6NjQiIG5hbWU9InZhbHVlIiB4c2k6dHlwZT0iT3BlcmFuZFJlZiIvPgogICAgICAgICA8L2V4cHJlc3Npb24+CiAgICAgICAgIDxvcGVyYW5kIG5hbWU9InZhbHVlIj4KICAgICAgICAgICAgPG9wZXJhbmRUeXBlU3BlY2lmaWVyIGxvY2F0b3I9IjIxNDozMi0yMTQ6NTYiIG5hbWU9ImZoaXI6Q29udHJhY3RQdWJsaWNhdGlvblN0YXR1cyIgeHNpOnR5cGU9Ik5hbWVkVHlwZVNwZWNpZmllciIvPgogICAgICAgICA8L29wZXJhbmQ+CiAgICAgIDwvZGVmPgogICAgICA8ZGVmIGxvY2F0b3I9IjIxNToxLTIxNTo1OSIgbmFtZT0iVG9TdHJpbmciIGNvbnRleHQ9IlBhdGllbnQiIGFjY2Vzc0xldmVsPSJQdWJsaWMiIHhzaTp0eXBlPSJGdW5jdGlvbkRlZiI+CiAgICAgICAgIDxleHByZXNzaW9uIGxvY2F0b3I9IjIxNTo0OS0yMTU6NTkiIHBhdGg9InZhbHVlIiB4c2k6dHlwZT0iUHJvcGVydHkiPgogICAgICAgICAgICA8c291cmNlIGxvY2F0b3I9IjIxNTo0OS0yMTU6NTMiIG5hbWU9InZhbHVlIiB4c2k6dHlwZT0iT3BlcmFuZFJlZiIvPgogICAgICAgICA8L2V4cHJlc3Npb24+CiAgICAgICAgIDxvcGVyYW5kIG5hbWU9InZhbHVlIj4KICAgICAgICAgICAgPG9wZXJhbmRUeXBlU3BlY2lmaWVyIGxvY2F0b3I9IjIxNTozMi0yMTU6NDUiIG5hbWU9ImZoaXI6Q29udHJhY3RTdGF0dXMiIHhzaTp0eXBlPSJOYW1lZFR5cGVTcGVjaWZpZXIiLz4KICAgICAgICAgPC9vcGVyYW5kPgogICAgICA8L2RlZj4KICAgICAgPGRlZiBsb2NhdG9yPSIyMTY6MS0yMTY6NjAiIG5hbWU9IlRvU3RyaW5nIiBjb250ZXh0PSJQYXRpZW50IiBhY2Nlc3NMZXZlbD0iUHVibGljIiB4c2k6dHlwZT0iRnVuY3Rpb25EZWYiPgogICAgICAgICA8ZXhwcmVzc2lvbiBsb2NhdG9yPSIyMTY6NTAtMjE2OjYwIiBwYXRoPSJ2YWx1ZSIgeHNpOnR5cGU9IlByb3BlcnR5Ij4KICAgICAgICAgICAgPHNvdXJjZSBsb2NhdG9yPSIyMTY6NTAtMjE2OjU0IiBuYW1lPSJ2YWx1ZSIgeHNpOnR5cGU9Ik9wZXJhbmRSZWYiLz4KICAgICAgICAgPC9leHByZXNzaW9uPgogICAgICAgICA8b3BlcmFuZCBuYW1lPSJ2YWx1ZSI+CiAgICAgICAgICAgIDxvcGVyYW5kVHlwZVNwZWNpZmllciBsb2NhdG9yPSIyMTY6MzItMjE2OjQ2IiBuYW1lPSJmaGlyOkNvbnRyaWJ1dG9yVHlwZSIgeHNpOnR5cGU9Ik5hbWVkVHlwZVNwZWNpZmllciIvPgogICAgICAgICA8L29wZXJhbmQ+CiAgICAgIDwvZGVmPgogICAgICA8ZGVmIGxvY2F0b3I9IjIxNzoxLTIxNzo1OSIgbmFtZT0iVG9TdHJpbmciIGNvbnRleHQ9IlBhdGllbnQiIGFjY2Vzc0xldmVsPSJQdWJsaWMiIHhzaTp0eXBlPSJGdW5jdGlvbkRlZiI+CiAgICAgICAgIDxleHByZXNzaW9uIGxvY2F0b3I9IjIxNzo0OS0yMTc6NTkiIHBhdGg9InZhbHVlIiB4c2k6dHlwZT0iUHJvcGVydHkiPgogICAgICAgICAgICA8c291cmNlIGxvY2F0b3I9IjIxNzo0OS0yMTc6NTMiIG5hbWU9InZhbHVlIiB4c2k6dHlwZT0iT3BlcmFuZFJlZiIvPgogICAgICAgICA8L2V4cHJlc3Npb24+CiAgICAgICAgIDxvcGVyYW5kIG5hbWU9InZhbHVlIj4KICAgICAgICAgICAgPG9wZXJhbmRUeXBlU3BlY2lmaWVyIGxvY2F0b3I9IjIxNzozMi0yMTc6NDUiIG5hbWU9ImZoaXI6Q292ZXJhZ2VTdGF0dXMiIHhzaTp0eXBlPSJOYW1lZFR5cGVTcGVjaWZpZXIiLz4KICAgICAgICAgPC9vcGVyYW5kPgogICAgICA8L2RlZj4KICAgICAgPGRlZiBsb2NhdG9yPSIyMTg6MS0yMTg6NTciIG5hbWU9IlRvU3RyaW5nIiBjb250ZXh0PSJQYXRpZW50IiBhY2Nlc3NMZXZlbD0iUHVibGljIiB4c2k6dHlwZT0iRnVuY3Rpb25EZWYiPgogICAgICAgICA8ZXhwcmVzc2lvbiBsb2NhdG9yPSIyMTg6NDctMjE4OjU3IiBwYXRoPSJ2YWx1ZSIgeHNpOnR5cGU9IlByb3BlcnR5Ij4KICAgICAgICAgICAgPHNvdXJjZSBsb2NhdG9yPSIyMTg6NDctMjE4OjUxIiBuYW1lPSJ2YWx1ZSIgeHNpOnR5cGU9Ik9wZXJhbmRSZWYiLz4KICAgICAgICAgPC9leHByZXNzaW9uPgogICAgICAgICA8b3BlcmFuZCBuYW1lPSJ2YWx1ZSI+CiAgICAgICAgICAgIDxvcGVyYW5kVHlwZVNwZWNpZmllciBsb2NhdG9yPSIyMTg6MzItMjE4OjQzIiBuYW1lPSJmaGlyOkN1cnJlbmN5Q29kZSIgeHNpOnR5cGU9Ik5hbWVkVHlwZVNwZWNpZmllciIvPgogICAgICAgICA8L29wZXJhbmQ+CiAgICAgIDwvZGVmPgogICAgICA8ZGVmIGxvY2F0b3I9IjIxOToxLTIxOTo1NCIgbmFtZT0iVG9TdHJpbmciIGNvbnRleHQ9IlBhdGllbnQiIGFjY2Vzc0xldmVsPSJQdWJsaWMiIHhzaTp0eXBlPSJGdW5jdGlvbkRlZiI+CiAgICAgICAgIDxleHByZXNzaW9uIGxvY2F0b3I9IjIxOTo0NC0yMTk6NTQiIHBhdGg9InZhbHVlIiB4c2k6dHlwZT0iUHJvcGVydHkiPgogICAgICAgICAgICA8c291cmNlIGxvY2F0b3I9IjIxOTo0NC0yMTk6NDgiIG5hbWU9InZhbHVlIiB4c2k6dHlwZT0iT3BlcmFuZFJlZiIvPgogICAgICAgICA8L2V4cHJlc3Npb24+CiAgICAgICAgIDxvcGVyYW5kIG5hbWU9InZhbHVlIj4KICAgICAgICAgICAgPG9wZXJhbmRUeXBlU3BlY2lmaWVyIGxvY2F0b3I9IjIxOTozMi0yMTk6NDAiIG5hbWU9ImZoaXI6RGF5T2ZXZWVrIiB4c2k6dHlwZT0iTmFtZWRUeXBlU3BlY2lmaWVyIi8+CiAgICAgICAgIDwvb3BlcmFuZD4KICAgICAgPC9kZWY+CiAgICAgIDxkZWYgbG9jYXRvcj0iMjIwOjEtMjIwOjU1IiBuYW1lPSJUb1N0cmluZyIgY29udGV4dD0iUGF0aWVudCIgYWNjZXNzTGV2ZWw9IlB1YmxpYyIgeHNpOnR5cGU9IkZ1bmN0aW9uRGVmIj4KICAgICAgICAgPGV4cHJlc3Npb24gbG9jYXRvcj0iMjIwOjQ1LTIyMDo1NSIgcGF0aD0idmFsdWUiIHhzaTp0eXBlPSJQcm9wZXJ0eSI+CiAgICAgICAgICAgIDxzb3VyY2UgbG9jYXRvcj0iMjIwOjQ1LTIyMDo0OSIgbmFtZT0idmFsdWUiIHhzaTp0eXBlPSJPcGVyYW5kUmVmIi8+CiAgICAgICAgIDwvZXhwcmVzc2lvbj4KICAgICAgICAgPG9wZXJhbmQgbmFtZT0idmFsdWUiPgogICAgICAgICAgICA8b3BlcmFuZFR5cGVTcGVjaWZpZXIgbG9jYXRvcj0iMjIwOjMyLTIyMDo0MSIgbmFtZT0iZmhpcjpEYXlzT2ZXZWVrIiB4c2k6dHlwZT0iTmFtZWRUeXBlU3BlY2lmaWVyIi8+CiAgICAgICAgIDwvb3BlcmFuZD4KICAgICAgPC9kZWY+CiAgICAgIDxkZWYgbG9jYXRvcj0iMjIxOjEtMjIxOjY2IiBuYW1lPSJUb1N0cmluZyIgY29udGV4dD0iUGF0aWVudCIgYWNjZXNzTGV2ZWw9IlB1YmxpYyIgeHNpOnR5cGU9IkZ1bmN0aW9uRGVmIj4KICAgICAgICAgPGV4cHJlc3Npb24gbG9jYXRvcj0iMjIxOjU2LTIyMTo2NiIgcGF0aD0idmFsdWUiIHhzaTp0eXBlPSJQcm9wZXJ0eSI+CiAgICAgICAgICAgIDxzb3VyY2UgbG9jYXRvcj0iMjIxOjU2LTIyMTo2MCIgbmFtZT0idmFsdWUiIHhzaTp0eXBlPSJPcGVyYW5kUmVmIi8+CiAgICAgICAgIDwvZXhwcmVzc2lvbj4KICAgICAgICAgPG9wZXJhbmQgbmFtZT0idmFsdWUiPgogICAgICAgICAgICA8b3BlcmFuZFR5cGVTcGVjaWZpZXIgbG9jYXRvcj0iMjIxOjMyLTIyMTo1MiIgbmFtZT0iZmhpcjpEZXRlY3RlZElzc3VlU2V2ZXJpdHkiIHhzaTp0eXBlPSJOYW1lZFR5cGVTcGVjaWZpZXIiLz4KICAgICAgICAgPC9vcGVyYW5kPgogICAgICA8L2RlZj4KICAgICAgPGRlZiBsb2NhdG9yPSIyMjI6MS0yMjI6NjQiIG5hbWU9IlRvU3RyaW5nIiBjb250ZXh0PSJQYXRpZW50IiBhY2Nlc3NMZXZlbD0iUHVibGljIiB4c2k6dHlwZT0iRnVuY3Rpb25EZWYiPgogICAgICAgICA8ZXhwcmVzc2lvbiBsb2NhdG9yPSIyMjI6NTQtMjIyOjY0IiBwYXRoPSJ2YWx1ZSIgeHNpOnR5cGU9IlByb3BlcnR5Ij4KICAgICAgICAgICAgPHNvdXJjZSBsb2NhdG9yPSIyMjI6NTQtMjIyOjU4IiBuYW1lPSJ2YWx1ZSIgeHNpOnR5cGU9Ik9wZXJhbmRSZWYiLz4KICAgICAgICAgPC9leHByZXNzaW9uPgogICAgICAgICA8b3BlcmFuZCBuYW1lPSJ2YWx1ZSI+CiAgICAgICAgICAgIDxvcGVyYW5kVHlwZVNwZWNpZmllciBsb2NhdG9yPSIyMjI6MzItMjIyOjUwIiBuYW1lPSJmaGlyOkRldGVjdGVkSXNzdWVTdGF0dXMiIHhzaTp0eXBlPSJOYW1lZFR5cGVTcGVjaWZpZXIiLz4KICAgICAgICAgPC9vcGVyYW5kPgogICAgICA8L2RlZj4KICAgICAgPGRlZiBsb2NhdG9yPSIyMjM6MS0yMjM6NzMiIG5hbWU9IlRvU3RyaW5nIiBjb250ZXh0PSJQYXRpZW50IiBhY2Nlc3NMZXZlbD0iUHVibGljIiB4c2k6dHlwZT0iRnVuY3Rpb25EZWYiPgogICAgICAgICA8ZXhwcmVzc2lvbiBsb2NhdG9yPSIyMjM6NjMtMjIzOjczIiBwYXRoPSJ2YWx1ZSIgeHNpOnR5cGU9IlByb3BlcnR5Ij4KICAgICAgICAgICAgPHNvdXJjZSBsb2NhdG9yPSIyMjM6NjMtMjIzOjY3IiBuYW1lPSJ2YWx1ZSIgeHNpOnR5cGU9Ik9wZXJhbmRSZWYiLz4KICAgICAgICAgPC9leHByZXNzaW9uPgogICAgICAgICA8b3BlcmFuZCBuYW1lPSJ2YWx1ZSI+CiAgICAgICAgICAgIDxvcGVyYW5kVHlwZVNwZWNpZmllciBsb2NhdG9yPSIyMjM6MzItMjIzOjU5IiBuYW1lPSJmaGlyOkRldmljZU1ldHJpY0NhbGlicmF0aW9uU3RhdGUiIHhzaTp0eXBlPSJOYW1lZFR5cGVTcGVjaWZpZXIiLz4KICAgICAgICAgPC9vcGVyYW5kPgogICAgICA8L2RlZj4KICAgICAgPGRlZiBsb2NhdG9yPSIyMjQ6MS0yMjQ6NzIiIG5hbWU9IlRvU3RyaW5nIiBjb250ZXh0PSJQYXRpZW50IiBhY2Nlc3NMZXZlbD0iUHVibGljIiB4c2k6dHlwZT0iRnVuY3Rpb25EZWYiPgogICAgICAgICA8ZXhwcmVzc2lvbiBsb2NhdG9yPSIyMjQ6NjItMjI0OjcyIiBwYXRoPSJ2YWx1ZSIgeHNpOnR5cGU9IlByb3BlcnR5Ij4KICAgICAgICAgICAgPHNvdXJjZSBsb2NhdG9yPSIyMjQ6NjItMjI0OjY2IiBuYW1lPSJ2YWx1ZSIgeHNpOnR5cGU9Ik9wZXJhbmRSZWYiLz4KICAgICAgICAgPC9leHByZXNzaW9uPgogICAgICAgICA8b3BlcmFuZCBuYW1lPSJ2YWx1ZSI+CiAgICAgICAgICAgIDxvcGVyYW5kVHlwZVNwZWNpZmllciBsb2NhdG9yPSIyMjQ6MzItMjI0OjU4IiBuYW1lPSJmaGlyOkRldmljZU1ldHJpY0NhbGlicmF0aW9uVHlwZSIgeHNpOnR5cGU9Ik5hbWVkVHlwZVNwZWNpZmllciIvPgogICAgICAgICA8L29wZXJhbmQ+CiAgICAgIDwvZGVmPgogICAgICA8ZGVmIGxvY2F0b3I9IjIyNToxLTIyNTo2NSIgbmFtZT0iVG9TdHJpbmciIGNvbnRleHQ9IlBhdGllbnQiIGFjY2Vzc0xldmVsPSJQdWJsaWMiIHhzaTp0eXBlPSJGdW5jdGlvbkRlZiI+CiAgICAgICAgIDxleHByZXNzaW9uIGxvY2F0b3I9IjIyNTo1NS0yMjU6NjUiIHBhdGg9InZhbHVlIiB4c2k6dHlwZT0iUHJvcGVydHkiPgogICAgICAgICAgICA8c291cmNlIGxvY2F0b3I9IjIyNTo1NS0yMjU6NTkiIG5hbWU9InZhbHVlIiB4c2k6dHlwZT0iT3BlcmFuZFJlZiIvPgogICAgICAgICA8L2V4cHJlc3Npb24+CiAgICAgICAgIDxvcGVyYW5kIG5hbWU9InZhbHVlIj4KICAgICAgICAgICAgPG9wZXJhbmRUeXBlU3BlY2lmaWVyIGxvY2F0b3I9IjIyNTozMi0yMjU6NTEiIG5hbWU9ImZoaXI6RGV2aWNlTWV0cmljQ2F0ZWdvcnkiIHhzaTp0eXBlPSJOYW1lZFR5cGVTcGVjaWZpZXIiLz4KICAgICAgICAgPC9vcGVyYW5kPgogICAgICA8L2RlZj4KICAgICAgPGRlZiBsb2NhdG9yPSIyMjY6MS0yMjY6NjIiIG5hbWU9IlRvU3RyaW5nIiBjb250ZXh0PSJQYXRpZW50IiBhY2Nlc3NMZXZlbD0iUHVibGljIiB4c2k6dHlwZT0iRnVuY3Rpb25EZWYiPgogICAgICAgICA8ZXhwcmVzc2lvbiBsb2NhdG9yPSIyMjY6NTItMjI2OjYyIiBwYXRoPSJ2YWx1ZSIgeHNpOnR5cGU9IlByb3BlcnR5Ij4KICAgICAgICAgICAgPHNvdXJjZSBsb2NhdG9yPSIyMjY6NTItMjI2OjU2IiBuYW1lPSJ2YWx1ZSIgeHNpOnR5cGU9Ik9wZXJhbmRSZWYiLz4KICAgICAgICAgPC9leHByZXNzaW9uPgogICAgICAgICA8b3BlcmFuZCBuYW1lPSJ2YWx1ZSI+CiAgICAgICAgICAgIDxvcGVyYW5kVHlwZVNwZWNpZmllciBsb2NhdG9yPSIyMjY6MzItMjI2OjQ4IiBuYW1lPSJmaGlyOkRldmljZU1ldHJpY0NvbG9yIiB4c2k6dHlwZT0iTmFtZWRUeXBlU3BlY2lmaWVyIi8+CiAgICAgICAgIDwvb3BlcmFuZD4KICAgICAgPC9kZWY+CiAgICAgIDxkZWYgbG9jYXRvcj0iMjI3OjEtMjI3Ojc0IiBuYW1lPSJUb1N0cmluZyIgY29udGV4dD0iUGF0aWVudCIgYWNjZXNzTGV2ZWw9IlB1YmxpYyIgeHNpOnR5cGU9IkZ1bmN0aW9uRGVmIj4KICAgICAgICAgPGV4cHJlc3Npb24gbG9jYXRvcj0iMjI3OjY0LTIyNzo3NCIgcGF0aD0idmFsdWUiIHhzaTp0eXBlPSJQcm9wZXJ0eSI+CiAgICAgICAgICAgIDxzb3VyY2UgbG9jYXRvcj0iMjI3OjY0LTIyNzo2OCIgbmFtZT0idmFsdWUiIHhzaTp0eXBlPSJPcGVyYW5kUmVmIi8+CiAgICAgICAgIDwvZXhwcmVzc2lvbj4KICAgICAgICAgPG9wZXJhbmQgbmFtZT0idmFsdWUiPgogICAgICAgICAgICA8b3BlcmFuZFR5cGVTcGVjaWZpZXIgbG9jYXRvcj0iMjI3OjMyLTIyNzo2MCIgbmFtZT0iZmhpcjpEZXZpY2VNZXRyaWNPcGVyYXRpb25hbFN0YXR1cyIgeHNpOnR5cGU9Ik5hbWVkVHlwZVNwZWNpZmllciIvPgogICAgICAgICA8L29wZXJhbmQ+CiAgICAgIDwvZGVmPgogICAgICA8ZGVmIGxvY2F0b3I9IjIyODoxLTIyODo1OSIgbmFtZT0iVG9TdHJpbmciIGNvbnRleHQ9IlBhdGllbnQiIGFjY2Vzc0xldmVsPSJQdWJsaWMiIHhzaTp0eXBlPSJGdW5jdGlvbkRlZiI+CiAgICAgICAgIDxleHByZXNzaW9uIGxvY2F0b3I9IjIyODo0OS0yMjg6NTkiIHBhdGg9InZhbHVlIiB4c2k6dHlwZT0iUHJvcGVydHkiPgogICAgICAgICAgICA8c291cmNlIGxvY2F0b3I9IjIyODo0OS0yMjg6NTMiIG5hbWU9InZhbHVlIiB4c2k6dHlwZT0iT3BlcmFuZFJlZiIvPgogICAgICAgICA8L2V4cHJlc3Npb24+CiAgICAgICAgIDxvcGVyYW5kIG5hbWU9InZhbHVlIj4KICAgICAgICAgICAgPG9wZXJhbmRUeXBlU3BlY2lmaWVyIGxvY2F0b3I9IjIyODozMi0yMjg6NDUiIG5hbWU9ImZoaXI6RGV2aWNlTmFtZVR5cGUiIHhzaTp0eXBlPSJOYW1lZFR5cGVTcGVjaWZpZXIiLz4KICAgICAgICAgPC9vcGVyYW5kPgogICAgICA8L2RlZj4KICAgICAgPGRlZiBsb2NhdG9yPSIyMjk6MS0yMjk6NjQiIG5hbWU9IlRvU3RyaW5nIiBjb250ZXh0PSJQYXRpZW50IiBhY2Nlc3NMZXZlbD0iUHVibGljIiB4c2k6dHlwZT0iRnVuY3Rpb25EZWYiPgogICAgICAgICA8ZXhwcmVzc2lvbiBsb2NhdG9yPSIyMjk6NTQtMjI5OjY0IiBwYXRoPSJ2YWx1ZSIgeHNpOnR5cGU9IlByb3BlcnR5Ij4KICAgICAgICAgICAgPHNvdXJjZSBsb2NhdG9yPSIyMjk6NTQtMjI5OjU4IiBuYW1lPSJ2YWx1ZSIgeHNpOnR5cGU9Ik9wZXJhbmRSZWYiLz4KICAgICAgICAgPC9leHByZXNzaW9uPgogICAgICAgICA8b3BlcmFuZCBuYW1lPSJ2YWx1ZSI+CiAgICAgICAgICAgIDxvcGVyYW5kVHlwZVNwZWNpZmllciBsb2NhdG9yPSIyMjk6MzItMjI5OjUwIiBuYW1lPSJmaGlyOkRldmljZVJlcXVlc3RTdGF0dXMiIHhzaTp0eXBlPSJOYW1lZFR5cGVTcGVjaWZpZXIiLz4KICAgICAgICAgPC9vcGVyYW5kPgogICAgICA8L2RlZj4KICAgICAgPGRlZiBsb2NhdG9yPSIyMzA6MS0yMzA6NjkiIG5hbWU9IlRvU3RyaW5nIiBjb250ZXh0PSJQYXRpZW50IiBhY2Nlc3NMZXZlbD0iUHVibGljIiB4c2k6dHlwZT0iRnVuY3Rpb25EZWYiPgogICAgICAgICA8ZXhwcmVzc2lvbiBsb2NhdG9yPSIyMzA6NTktMjMwOjY5IiBwYXRoPSJ2YWx1ZSIgeHNpOnR5cGU9IlByb3BlcnR5Ij4KICAgICAgICAgICAgPHNvdXJjZSBsb2NhdG9yPSIyMzA6NTktMjMwOjYzIiBuYW1lPSJ2YWx1ZSIgeHNpOnR5cGU9Ik9wZXJhbmRSZWYiLz4KICAgICAgICAgPC9leHByZXNzaW9uPgogICAgICAgICA8b3BlcmFuZCBuYW1lPSJ2YWx1ZSI+CiAgICAgICAgICAgIDxvcGVyYW5kVHlwZVNwZWNpZmllciBsb2NhdG9yPSIyMzA6MzItMjMwOjU1IiBuYW1lPSJmaGlyOkRldmljZVVzZVN0YXRlbWVudFN0YXR1cyIgeHNpOnR5cGU9Ik5hbWVkVHlwZVNwZWNpZmllciIvPgogICAgICAgICA8L29wZXJhbmQ+CiAgICAgIDwvZGVmPgogICAgICA8ZGVmIGxvY2F0b3I9IjIzMToxLTIzMTo2NyIgbmFtZT0iVG9TdHJpbmciIGNvbnRleHQ9IlBhdGllbnQiIGFjY2Vzc0xldmVsPSJQdWJsaWMiIHhzaTp0eXBlPSJGdW5jdGlvbkRlZiI+CiAgICAgICAgIDxleHByZXNzaW9uIGxvY2F0b3I9IjIzMTo1Ny0yMzE6NjciIHBhdGg9InZhbHVlIiB4c2k6dHlwZT0iUHJvcGVydHkiPgogICAgICAgICAgICA8c291cmNlIGxvY2F0b3I9IjIzMTo1Ny0yMzE6NjEiIG5hbWU9InZhbHVlIiB4c2k6dHlwZT0iT3BlcmFuZFJlZiIvPgogICAgICAgICA8L2V4cHJlc3Npb24+CiAgICAgICAgIDxvcGVyYW5kIG5hbWU9InZhbHVlIj4KICAgICAgICAgICAgPG9wZXJhbmRUeXBlU3BlY2lmaWVyIGxvY2F0b3I9IjIzMTozMi0yMzE6NTMiIG5hbWU9ImZoaXI6RGlhZ25vc3RpY1JlcG9ydFN0YXR1cyIgeHNpOnR5cGU9Ik5hbWVkVHlwZVNwZWNpZmllciIvPgogICAgICAgICA8L29wZXJhbmQ+CiAgICAgIDwvZGVmPgogICAgICA8ZGVmIGxvY2F0b3I9IjIzMjoxLTIzMjo2MiIgbmFtZT0iVG9TdHJpbmciIGNvbnRleHQ9IlBhdGllbnQiIGFjY2Vzc0xldmVsPSJQdWJsaWMiIHhzaTp0eXBlPSJGdW5jdGlvbkRlZiI+CiAgICAgICAgIDxleHByZXNzaW9uIGxvY2F0b3I9IjIzMjo1Mi0yMzI6NjIiIHBhdGg9InZhbHVlIiB4c2k6dHlwZT0iUHJvcGVydHkiPgogICAgICAgICAgICA8c291cmNlIGxvY2F0b3I9IjIzMjo1Mi0yMzI6NTYiIG5hbWU9InZhbHVlIiB4c2k6dHlwZT0iT3BlcmFuZFJlZiIvPgogICAgICAgICA8L2V4cHJlc3Npb24+CiAgICAgICAgIDxvcGVyYW5kIG5hbWU9InZhbHVlIj4KICAgICAgICAgICAgPG9wZXJhbmRUeXBlU3BlY2lmaWVyIGxvY2F0b3I9IjIzMjozMi0yMzI6NDgiIG5hbWU9ImZoaXI6RGlzY3JpbWluYXRvclR5cGUiIHhzaTp0eXBlPSJOYW1lZFR5cGVTcGVjaWZpZXIiLz4KICAgICAgICAgPC9vcGVyYW5kPgogICAgICA8L2RlZj4KICAgICAgPGRlZiBsb2NhdG9yPSIyMzM6MS0yMzM6NjgiIG5hbWU9IlRvU3RyaW5nIiBjb250ZXh0PSJQYXRpZW50IiBhY2Nlc3NMZXZlbD0iUHVibGljIiB4c2k6dHlwZT0iRnVuY3Rpb25EZWYiPgogICAgICAgICA8ZXhwcmVzc2lvbiBsb2NhdG9yPSIyMzM6NTgtMjMzOjY4IiBwYXRoPSJ2YWx1ZSIgeHNpOnR5cGU9IlByb3BlcnR5Ij4KICAgICAgICAgICAgPHNvdXJjZSBsb2NhdG9yPSIyMzM6NTgtMjMzOjYyIiBuYW1lPSJ2YWx1ZSIgeHNpOnR5cGU9Ik9wZXJhbmRSZWYiLz4KICAgICAgICAgPC9leHByZXNzaW9uPgogICAgICAgICA8b3BlcmFuZCBuYW1lPSJ2YWx1ZSI+CiAgICAgICAgICAgIDxvcGVyYW5kVHlwZVNwZWNpZmllciBsb2NhdG9yPSIyMzM6MzItMjMzOjU0IiBuYW1lPSJmaGlyOkRvY3VtZW50Q29uZmlkZW50aWFsaXR5IiB4c2k6dHlwZT0iTmFtZWRUeXBlU3BlY2lmaWVyIi8+CiAgICAgICAgIDwvb3BlcmFuZD4KICAgICAgPC9kZWY+CiAgICAgIDxkZWYgbG9jYXRvcj0iMjM0OjEtMjM0OjU3IiBuYW1lPSJUb1N0cmluZyIgY29udGV4dD0iUGF0aWVudCIgYWNjZXNzTGV2ZWw9IlB1YmxpYyIgeHNpOnR5cGU9IkZ1bmN0aW9uRGVmIj4KICAgICAgICAgPGV4cHJlc3Npb24gbG9jYXRvcj0iMjM0OjQ3LTIzNDo1NyIgcGF0aD0idmFsdWUiIHhzaTp0eXBlPSJQcm9wZXJ0eSI+CiAgICAgICAgICAgIDxzb3VyY2UgbG9jYXRvcj0iMjM0OjQ3LTIzNDo1MSIgbmFtZT0idmFsdWUiIHhzaTp0eXBlPSJPcGVyYW5kUmVmIi8+CiAgICAgICAgIDwvZXhwcmVzc2lvbj4KICAgICAgICAgPG9wZXJhbmQgbmFtZT0idmFsdWUiPgogICAgICAgICAgICA8b3BlcmFuZFR5cGVTcGVjaWZpZXIgbG9jYXRvcj0iMjM0OjMyLTIzNDo0MyIgbmFtZT0iZmhpcjpEb2N1bWVudE1vZGUiIHhzaTp0eXBlPSJOYW1lZFR5cGVTcGVjaWZpZXIiLz4KICAgICAgICAgPC9vcGVyYW5kPgogICAgICA8L2RlZj4KICAgICAgPGRlZiBsb2NhdG9yPSIyMzU6MS0yMzU6NjgiIG5hbWU9IlRvU3RyaW5nIiBjb250ZXh0PSJQYXRpZW50IiBhY2Nlc3NMZXZlbD0iUHVibGljIiB4c2k6dHlwZT0iRnVuY3Rpb25EZWYiPgogICAgICAgICA8ZXhwcmVzc2lvbiBsb2NhdG9yPSIyMzU6NTgtMjM1OjY4IiBwYXRoPSJ2YWx1ZSIgeHNpOnR5cGU9IlByb3BlcnR5Ij4KICAgICAgICAgICAgPHNvdXJjZSBsb2NhdG9yPSIyMzU6NTgtMjM1OjYyIiBuYW1lPSJ2YWx1ZSIgeHNpOnR5cGU9Ik9wZXJhbmRSZWYiLz4KICAgICAgICAgPC9leHByZXNzaW9uPgogICAgICAgICA8b3BlcmFuZCBuYW1lPSJ2YWx1ZSI+CiAgICAgICAgICAgIDxvcGVyYW5kVHlwZVNwZWNpZmllciBsb2NhdG9yPSIyMzU6MzItMjM1OjU0IiBuYW1lPSJmaGlyOkRvY3VtZW50UmVmZXJlbmNlU3RhdHVzIiB4c2k6dHlwZT0iTmFtZWRUeXBlU3BlY2lmaWVyIi8+CiAgICAgICAgIDwvb3BlcmFuZD4KICAgICAgPC9kZWY+CiAgICAgIDxkZWYgbG9jYXRvcj0iMjM2OjEtMjM2OjY5IiBuYW1lPSJUb1N0cmluZyIgY29udGV4dD0iUGF0aWVudCIgYWNjZXNzTGV2ZWw9IlB1YmxpYyIgeHNpOnR5cGU9IkZ1bmN0aW9uRGVmIj4KICAgICAgICAgPGV4cHJlc3Npb24gbG9jYXRvcj0iMjM2OjU5LTIzNjo2OSIgcGF0aD0idmFsdWUiIHhzaTp0eXBlPSJQcm9wZXJ0eSI+CiAgICAgICAgICAgIDxzb3VyY2UgbG9jYXRvcj0iMjM2OjU5LTIzNjo2MyIgbmFtZT0idmFsdWUiIHhzaTp0eXBlPSJPcGVyYW5kUmVmIi8+CiAgICAgICAgIDwvZXhwcmVzc2lvbj4KICAgICAgICAgPG9wZXJhbmQgbmFtZT0idmFsdWUiPgogICAgICAgICAgICA8b3BlcmFuZFR5cGVTcGVjaWZpZXIgbG9jYXRvcj0iMjM2OjMyLTIzNjo1NSIgbmFtZT0iZmhpcjpEb2N1bWVudFJlbGF0aW9uc2hpcFR5cGUiIHhzaTp0eXBlPSJOYW1lZFR5cGVTcGVjaWZpZXIiLz4KICAgICAgICAgPC9vcGVyYW5kPgogICAgICA8L2RlZj4KICAgICAgPGRlZiBsb2NhdG9yPSIyMzc6MS0yMzc6NzAiIG5hbWU9IlRvU3RyaW5nIiBjb250ZXh0PSJQYXRpZW50IiBhY2Nlc3NMZXZlbD0iUHVibGljIiB4c2k6dHlwZT0iRnVuY3Rpb25EZWYiPgogICAgICAgICA8ZXhwcmVzc2lvbiBsb2NhdG9yPSIyMzc6NjAtMjM3OjcwIiBwYXRoPSJ2YWx1ZSIgeHNpOnR5cGU9IlByb3BlcnR5Ij4KICAgICAgICAgICAgPHNvdXJjZSBsb2NhdG9yPSIyMzc6NjAtMjM3OjY0IiBuYW1lPSJ2YWx1ZSIgeHNpOnR5cGU9Ik9wZXJhbmRSZWYiLz4KICAgICAgICAgPC9leHByZXNzaW9uPgogICAgICAgICA8b3BlcmFuZCBuYW1lPSJ2YWx1ZSI+CiAgICAgICAgICAgIDxvcGVyYW5kVHlwZVNwZWNpZmllciBsb2NhdG9yPSIyMzc6MzItMjM3OjU2IiBuYW1lPSJmaGlyOkVsaWdpYmlsaXR5UmVxdWVzdFB1cnBvc2UiIHhzaTp0eXBlPSJOYW1lZFR5cGVTcGVjaWZpZXIiLz4KICAgICAgICAgPC9vcGVyYW5kPgogICAgICA8L2RlZj4KICAgICAgPGRlZiBsb2NhdG9yPSIyMzg6MS0yMzg6NjkiIG5hbWU9IlRvU3RyaW5nIiBjb250ZXh0PSJQYXRpZW50IiBhY2Nlc3NMZXZlbD0iUHVibGljIiB4c2k6dHlwZT0iRnVuY3Rpb25EZWYiPgogICAgICAgICA8ZXhwcmVzc2lvbiBsb2NhdG9yPSIyMzg6NTktMjM4OjY5IiBwYXRoPSJ2YWx1ZSIgeHNpOnR5cGU9IlByb3BlcnR5Ij4KICAgICAgICAgICAgPHNvdXJjZSBsb2NhdG9yPSIyMzg6NTktMjM4OjYzIiBuYW1lPSJ2YWx1ZSIgeHNpOnR5cGU9Ik9wZXJhbmRSZWYiLz4KICAgICAgICAgPC9leHByZXNzaW9uPgogICAgICAgICA8b3BlcmFuZCBuYW1lPSJ2YWx1ZSI+CiAgICAgICAgICAgIDxvcGVyYW5kVHlwZVNwZWNpZmllciBsb2NhdG9yPSIyMzg6MzItMjM4OjU1IiBuYW1lPSJmaGlyOkVsaWdpYmlsaXR5UmVxdWVzdFN0YXR1cyIgeHNpOnR5cGU9Ik5hbWVkVHlwZVNwZWNpZmllciIvPgogICAgICAgICA8L29wZXJhbmQ+CiAgICAgIDwvZGVmPgogICAgICA8ZGVmIGxvY2F0b3I9IjIzOToxLTIzOTo3MSIgbmFtZT0iVG9TdHJpbmciIGNvbnRleHQ9IlBhdGllbnQiIGFjY2Vzc0xldmVsPSJQdWJsaWMiIHhzaTp0eXBlPSJGdW5jdGlvbkRlZiI+CiAgICAgICAgIDxleHByZXNzaW9uIGxvY2F0b3I9IjIzOTo2MS0yMzk6NzEiIHBhdGg9InZhbHVlIiB4c2k6dHlwZT0iUHJvcGVydHkiPgogICAgICAgICAgICA8c291cmNlIGxvY2F0b3I9IjIzOTo2MS0yMzk6NjUiIG5hbWU9InZhbHVlIiB4c2k6dHlwZT0iT3BlcmFuZFJlZiIvPgogICAgICAgICA8L2V4cHJlc3Npb24+CiAgICAgICAgIDxvcGVyYW5kIG5hbWU9InZhbHVlIj4KICAgICAgICAgICAgPG9wZXJhbmRUeXBlU3BlY2lmaWVyIGxvY2F0b3I9IjIzOTozMi0yMzk6NTciIG5hbWU9ImZoaXI6RWxpZ2liaWxpdHlSZXNwb25zZVB1cnBvc2UiIHhzaTp0eXBlPSJOYW1lZFR5cGVTcGVjaWZpZXIiLz4KICAgICAgICAgPC9vcGVyYW5kPgogICAgICA8L2RlZj4KICAgICAgPGRlZiBsb2NhdG9yPSIyNDA6MS0yNDA6NzAiIG5hbWU9IlRvU3RyaW5nIiBjb250ZXh0PSJQYXRpZW50IiBhY2Nlc3NMZXZlbD0iUHVibGljIiB4c2k6dHlwZT0iRnVuY3Rpb25EZWYiPgogICAgICAgICA8ZXhwcmVzc2lvbiBsb2NhdG9yPSIyNDA6NjAtMjQwOjcwIiBwYXRoPSJ2YWx1ZSIgeHNpOnR5cGU9IlByb3BlcnR5Ij4KICAgICAgICAgICAgPHNvdXJjZSBsb2NhdG9yPSIyNDA6NjAtMjQwOjY0IiBuYW1lPSJ2YWx1ZSIgeHNpOnR5cGU9Ik9wZXJhbmRSZWYiLz4KICAgICAgICAgPC9leHByZXNzaW9uPgogICAgICAgICA8b3BlcmFuZCBuYW1lPSJ2YWx1ZSI+CiAgICAgICAgICAgIDxvcGVyYW5kVHlwZVNwZWNpZmllciBsb2NhdG9yPSIyNDA6MzItMjQwOjU2IiBuYW1lPSJmaGlyOkVsaWdpYmlsaXR5UmVzcG9uc2VTdGF0dXMiIHhzaTp0eXBlPSJOYW1lZFR5cGVTcGVjaWZpZXIiLz4KICAgICAgICAgPC9vcGVyYW5kPgogICAgICA8L2RlZj4KICAgICAgPGRlZiBsb2NhdG9yPSIyNDE6MS0yNDE6NjMiIG5hbWU9IlRvU3RyaW5nIiBjb250ZXh0PSJQYXRpZW50IiBhY2Nlc3NMZXZlbD0iUHVibGljIiB4c2k6dHlwZT0iRnVuY3Rpb25EZWYiPgogICAgICAgICA8ZXhwcmVzc2lvbiBsb2NhdG9yPSIyNDE6NTMtMjQxOjYzIiBwYXRoPSJ2YWx1ZSIgeHNpOnR5cGU9IlByb3BlcnR5Ij4KICAgICAgICAgICAgPHNvdXJjZSBsb2NhdG9yPSIyNDE6NTMtMjQxOjU3IiBuYW1lPSJ2YWx1ZSIgeHNpOnR5cGU9Ik9wZXJhbmRSZWYiLz4KICAgICAgICAgPC9leHByZXNzaW9uPgogICAgICAgICA8b3BlcmFuZCBuYW1lPSJ2YWx1ZSI+CiAgICAgICAgICAgIDxvcGVyYW5kVHlwZVNwZWNpZmllciBsb2NhdG9yPSIyNDE6MzItMjQxOjQ5IiBuYW1lPSJmaGlyOkVuYWJsZVdoZW5CZWhhdmlvciIgeHNpOnR5cGU9Ik5hbWVkVHlwZVNwZWNpZmllciIvPgogICAgICAgICA8L29wZXJhbmQ+CiAgICAgIDwvZGVmPgogICAgICA8ZGVmIGxvY2F0b3I9IjI0MjoxLTI0Mjo2OCIgbmFtZT0iVG9TdHJpbmciIGNvbnRleHQ9IlBhdGllbnQiIGFjY2Vzc0xldmVsPSJQdWJsaWMiIHhzaTp0eXBlPSJGdW5jdGlvbkRlZiI+CiAgICAgICAgIDxleHByZXNzaW9uIGxvY2F0b3I9IjI0Mjo1OC0yNDI6NjgiIHBhdGg9InZhbHVlIiB4c2k6dHlwZT0iUHJvcGVydHkiPgogICAgICAgICAgICA8c291cmNlIGxvY2F0b3I9IjI0Mjo1OC0yNDI6NjIiIG5hbWU9InZhbHVlIiB4c2k6dHlwZT0iT3BlcmFuZFJlZiIvPgogICAgICAgICA8L2V4cHJlc3Npb24+CiAgICAgICAgIDxvcGVyYW5kIG5hbWU9InZhbHVlIj4KICAgICAgICAgICAgPG9wZXJhbmRUeXBlU3BlY2lmaWVyIGxvY2F0b3I9IjI0MjozMi0yNDI6NTQiIG5hbWU9ImZoaXI6RW5jb3VudGVyTG9jYXRpb25TdGF0dXMiIHhzaTp0eXBlPSJOYW1lZFR5cGVTcGVjaWZpZXIiLz4KICAgICAgICAgPC9vcGVyYW5kPgogICAgICA8L2RlZj4KICAgICAgPGRlZiBsb2NhdG9yPSIyNDM6MS0yNDM6NjAiIG5hbWU9IlRvU3RyaW5nIiBjb250ZXh0PSJQYXRpZW50IiBhY2Nlc3NMZXZlbD0iUHVibGljIiB4c2k6dHlwZT0iRnVuY3Rpb25EZWYiPgogICAgICAgICA8ZXhwcmVzc2lvbiBsb2NhdG9yPSIyNDM6NTAtMjQzOjYwIiBwYXRoPSJ2YWx1ZSIgeHNpOnR5cGU9IlByb3BlcnR5Ij4KICAgICAgICAgICAgPHNvdXJjZSBsb2NhdG9yPSIyNDM6NTAtMjQzOjU0IiBuYW1lPSJ2YWx1ZSIgeHNpOnR5cGU9Ik9wZXJhbmRSZWYiLz4KICAgICAgICAgPC9leHByZXNzaW9uPgogICAgICAgICA8b3BlcmFuZCBuYW1lPSJ2YWx1ZSI+CiAgICAgICAgICAgIDxvcGVyYW5kVHlwZVNwZWNpZmllciBsb2NhdG9yPSIyNDM6MzItMjQzOjQ2IiBuYW1lPSJmaGlyOkVuY291bnRlclN0YXR1cyIgeHNpOnR5cGU9Ik5hbWVkVHlwZVNwZWNpZmllciIvPgogICAgICAgICA8L29wZXJhbmQ+CiAgICAgIDwvZGVmPgogICAgICA8ZGVmIGxvY2F0b3I9IjI0NDoxLTI0NDo1OSIgbmFtZT0iVG9TdHJpbmciIGNvbnRleHQ9IlBhdGllbnQiIGFjY2Vzc0xldmVsPSJQdWJsaWMiIHhzaTp0eXBlPSJGdW5jdGlvbkRlZiI+CiAgICAgICAgIDxleHByZXNzaW9uIGxvY2F0b3I9IjI0NDo0OS0yNDQ6NTkiIHBhdGg9InZhbHVlIiB4c2k6dHlwZT0iUHJvcGVydHkiPgogICAgICAgICAgICA8c291cmNlIGxvY2F0b3I9IjI0NDo0OS0yNDQ6NTMiIG5hbWU9InZhbHVlIiB4c2k6dHlwZT0iT3BlcmFuZFJlZiIvPgogICAgICAgICA8L2V4cHJlc3Npb24+CiAgICAgICAgIDxvcGVyYW5kIG5hbWU9InZhbHVlIj4KICAgICAgICAgICAgPG9wZXJhbmRUeXBlU3BlY2lmaWVyIGxvY2F0b3I9IjI0NDozMi0yNDQ6NDUiIG5hbWU9ImZoaXI6RW5kcG9pbnRTdGF0dXMiIHhzaTp0eXBlPSJOYW1lZFR5cGVTcGVjaWZpZXIiLz4KICAgICAgICAgPC9vcGVyYW5kPgogICAgICA8L2RlZj4KICAgICAgPGRlZiBsb2NhdG9yPSIyNDU6MS0yNDU6NjgiIG5hbWU9IlRvU3RyaW5nIiBjb250ZXh0PSJQYXRpZW50IiBhY2Nlc3NMZXZlbD0iUHVibGljIiB4c2k6dHlwZT0iRnVuY3Rpb25EZWYiPgogICAgICAgICA8ZXhwcmVzc2lvbiBsb2NhdG9yPSIyNDU6NTgtMjQ1OjY4IiBwYXRoPSJ2YWx1ZSIgeHNpOnR5cGU9IlByb3BlcnR5Ij4KICAgICAgICAgICAgPHNvdXJjZSBsb2NhdG9yPSIyNDU6NTgtMjQ1OjYyIiBuYW1lPSJ2YWx1ZSIgeHNpOnR5cGU9Ik9wZXJhbmRSZWYiLz4KICAgICAgICAgPC9leHByZXNzaW9uPgogICAgICAgICA8b3BlcmFuZCBuYW1lPSJ2YWx1ZSI+CiAgICAgICAgICAgIDxvcGVyYW5kVHlwZVNwZWNpZmllciBsb2NhdG9yPSIyNDU6MzItMjQ1OjU0IiBuYW1lPSJmaGlyOkVucm9sbG1lbnRSZXF1ZXN0U3RhdHVzIiB4c2k6dHlwZT0iTmFtZWRUeXBlU3BlY2lmaWVyIi8+CiAgICAgICAgIDwvb3BlcmFuZD4KICAgICAgPC9kZWY+CiAgICAgIDxkZWYgbG9jYXRvcj0iMjQ2OjEtMjQ2OjY5IiBuYW1lPSJUb1N0cmluZyIgY29udGV4dD0iUGF0aWVudCIgYWNjZXNzTGV2ZWw9IlB1YmxpYyIgeHNpOnR5cGU9IkZ1bmN0aW9uRGVmIj4KICAgICAgICAgPGV4cHJlc3Npb24gbG9jYXRvcj0iMjQ2OjU5LTI0Njo2OSIgcGF0aD0idmFsdWUiIHhzaTp0eXBlPSJQcm9wZXJ0eSI+CiAgICAgICAgICAgIDxzb3VyY2UgbG9jYXRvcj0iMjQ2OjU5LTI0Njo2MyIgbmFtZT0idmFsdWUiIHhzaTp0eXBlPSJPcGVyYW5kUmVmIi8+CiAgICAgICAgIDwvZXhwcmVzc2lvbj4KICAgICAgICAgPG9wZXJhbmQgbmFtZT0idmFsdWUiPgogICAgICAgICAgICA8b3BlcmFuZFR5cGVTcGVjaWZpZXIgbG9jYXRvcj0iMjQ2OjMyLTI0Njo1NSIgbmFtZT0iZmhpcjpFbnJvbGxtZW50UmVzcG9uc2VTdGF0dXMiIHhzaTp0eXBlPSJOYW1lZFR5cGVTcGVjaWZpZXIiLz4KICAgICAgICAgPC9vcGVyYW5kPgogICAgICA8L2RlZj4KICAgICAgPGRlZiBsb2NhdG9yPSIyNDc6MS0yNDc6NjQiIG5hbWU9IlRvU3RyaW5nIiBjb250ZXh0PSJQYXRpZW50IiBhY2Nlc3NMZXZlbD0iUHVibGljIiB4c2k6dHlwZT0iRnVuY3Rpb25EZWYiPgogICAgICAgICA8ZXhwcmVzc2lvbiBsb2NhdG9yPSIyNDc6NTQtMjQ3OjY0IiBwYXRoPSJ2YWx1ZSIgeHNpOnR5cGU9IlByb3BlcnR5Ij4KICAgICAgICAgICAgPHNvdXJjZSBsb2NhdG9yPSIyNDc6NTQtMjQ3OjU4IiBuYW1lPSJ2YWx1ZSIgeHNpOnR5cGU9Ik9wZXJhbmRSZWYiLz4KICAgICAgICAgPC9leHByZXNzaW9uPgogICAgICAgICA8b3BlcmFuZCBuYW1lPSJ2YWx1ZSI+CiAgICAgICAgICAgIDxvcGVyYW5kVHlwZVNwZWNpZmllciBsb2NhdG9yPSIyNDc6MzItMjQ3OjUwIiBuYW1lPSJmaGlyOkVwaXNvZGVPZkNhcmVTdGF0dXMiIHhzaTp0eXBlPSJOYW1lZFR5cGVTcGVjaWZpZXIiLz4KICAgICAgICAgPC9vcGVyYW5kPgogICAgICA8L2RlZj4KICAgICAgPGRlZiBsb2NhdG9yPSIyNDg6MS0yNDg6NjQiIG5hbWU9IlRvU3RyaW5nIiBjb250ZXh0PSJQYXRpZW50IiBhY2Nlc3NMZXZlbD0iUHVibGljIiB4c2k6dHlwZT0iRnVuY3Rpb25EZWYiPgogICAgICAgICA8ZXhwcmVzc2lvbiBsb2NhdG9yPSIyNDg6NTQtMjQ4OjY0IiBwYXRoPSJ2YWx1ZSIgeHNpOnR5cGU9IlByb3BlcnR5Ij4KICAgICAgICAgICAgPHNvdXJjZSBsb2NhdG9yPSIyNDg6NTQtMjQ4OjU4IiBuYW1lPSJ2YWx1ZSIgeHNpOnR5cGU9Ik9wZXJhbmRSZWYiLz4KICAgICAgICAgPC9leHByZXNzaW9uPgogICAgICAgICA8b3BlcmFuZCBuYW1lPSJ2YWx1ZSI+CiAgICAgICAgICAgIDxvcGVyYW5kVHlwZVNwZWNpZmllciBsb2NhdG9yPSIyNDg6MzItMjQ4OjUwIiBuYW1lPSJmaGlyOkV2ZW50Q2FwYWJpbGl0eU1vZGUiIHhzaTp0eXBlPSJOYW1lZFR5cGVTcGVjaWZpZXIiLz4KICAgICAgICAgPC9vcGVyYW5kPgogICAgICA8L2RlZj4KICAgICAgPGRlZiBsb2NhdG9yPSIyNDk6MS0yNDk6NTYiIG5hbWU9IlRvU3RyaW5nIiBjb250ZXh0PSJQYXRpZW50IiBhY2Nlc3NMZXZlbD0iUHVibGljIiB4c2k6dHlwZT0iRnVuY3Rpb25EZWYiPgogICAgICAgICA8ZXhwcmVzc2lvbiBsb2NhdG9yPSIyNDk6NDYtMjQ5OjU2IiBwYXRoPSJ2YWx1ZSIgeHNpOnR5cGU9IlByb3BlcnR5Ij4KICAgICAgICAgICAgPHNvdXJjZSBsb2NhdG9yPSIyNDk6NDYtMjQ5OjUwIiBuYW1lPSJ2YWx1ZSIgeHNpOnR5cGU9Ik9wZXJhbmRSZWYiLz4KICAgICAgICAgPC9leHByZXNzaW9uPgogICAgICAgICA8b3BlcmFuZCBuYW1lPSJ2YWx1ZSI+CiAgICAgICAgICAgIDxvcGVyYW5kVHlwZVNwZWNpZmllciBsb2NhdG9yPSIyNDk6MzItMjQ5OjQyIiBuYW1lPSJmaGlyOkV2ZW50VGltaW5nIiB4c2k6dHlwZT0iTmFtZWRUeXBlU3BlY2lmaWVyIi8+CiAgICAgICAgIDwvb3BlcmFuZD4KICAgICAgPC9kZWY+CiAgICAgIDxkZWYgbG9jYXRvcj0iMjUwOjEtMjUwOjY1IiBuYW1lPSJUb1N0cmluZyIgY29udGV4dD0iUGF0aWVudCIgYWNjZXNzTGV2ZWw9IlB1YmxpYyIgeHNpOnR5cGU9IkZ1bmN0aW9uRGVmIj4KICAgICAgICAgPGV4cHJlc3Npb24gbG9jYXRvcj0iMjUwOjU1LTI1MDo2NSIgcGF0aD0idmFsdWUiIHhzaTp0eXBlPSJQcm9wZXJ0eSI+CiAgICAgICAgICAgIDxzb3VyY2UgbG9jYXRvcj0iMjUwOjU1LTI1MDo1OSIgbmFtZT0idmFsdWUiIHhzaTp0eXBlPSJPcGVyYW5kUmVmIi8+CiAgICAgICAgIDwvZXhwcmVzc2lvbj4KICAgICAgICAgPG9wZXJhbmQgbmFtZT0idmFsdWUiPgogICAgICAgICAgICA8b3BlcmFuZFR5cGVTcGVjaWZpZXIgbG9jYXRvcj0iMjUwOjMyLTI1MDo1MSIgbmFtZT0iZmhpcjpFdmlkZW5jZVZhcmlhYmxlVHlwZSIgeHNpOnR5cGU9Ik5hbWVkVHlwZVNwZWNpZmllciIvPgogICAgICAgICA8L29wZXJhbmQ+CiAgICAgIDwvZGVmPgogICAgICA8ZGVmIGxvY2F0b3I9IjI1MToxLTI1MTo2OSIgbmFtZT0iVG9TdHJpbmciIGNvbnRleHQ9IlBhdGllbnQiIGFjY2Vzc0xldmVsPSJQdWJsaWMiIHhzaTp0eXBlPSJGdW5jdGlvbkRlZiI+CiAgICAgICAgIDxleHByZXNzaW9uIGxvY2F0b3I9IjI1MTo1OS0yNTE6NjkiIHBhdGg9InZhbHVlIiB4c2k6dHlwZT0iUHJvcGVydHkiPgogICAgICAgICAgICA8c291cmNlIGxvY2F0b3I9IjI1MTo1OS0yNTE6NjMiIG5hbWU9InZhbHVlIiB4c2k6dHlwZT0iT3BlcmFuZFJlZiIvPgogICAgICAgICA8L2V4cHJlc3Npb24+CiAgICAgICAgIDxvcGVyYW5kIG5hbWU9InZhbHVlIj4KICAgICAgICAgICAgPG9wZXJhbmRUeXBlU3BlY2lmaWVyIGxvY2F0b3I9IjI1MTozMi0yNTE6NTUiIG5hbWU9ImZoaXI6RXhhbXBsZVNjZW5hcmlvQWN0b3JUeXBlIiB4c2k6dHlwZT0iTmFtZWRUeXBlU3BlY2lmaWVyIi8+CiAgICAgICAgIDwvb3BlcmFuZD4KICAgICAgPC9kZWY+CiAgICAgIDxkZWYgbG9jYXRvcj0iMjUyOjEtMjUyOjcxIiBuYW1lPSJUb1N0cmluZyIgY29udGV4dD0iUGF0aWVudCIgYWNjZXNzTGV2ZWw9IlB1YmxpYyIgeHNpOnR5cGU9IkZ1bmN0aW9uRGVmIj4KICAgICAgICAgPGV4cHJlc3Npb24gbG9jYXRvcj0iMjUyOjYxLTI1Mjo3MSIgcGF0aD0idmFsdWUiIHhzaTp0eXBlPSJQcm9wZXJ0eSI+CiAgICAgICAgICAgIDxzb3VyY2UgbG9jYXRvcj0iMjUyOjYxLTI1Mjo2NSIgbmFtZT0idmFsdWUiIHhzaTp0eXBlPSJPcGVyYW5kUmVmIi8+CiAgICAgICAgIDwvZXhwcmVzc2lvbj4KICAgICAgICAgPG9wZXJhbmQgbmFtZT0idmFsdWUiPgogICAgICAgICAgICA8b3BlcmFuZFR5cGVTcGVjaWZpZXIgbG9jYXRvcj0iMjUyOjMyLTI1Mjo1NyIgbmFtZT0iZmhpcjpFeHBsYW5hdGlvbk9mQmVuZWZpdFN0YXR1cyIgeHNpOnR5cGU9Ik5hbWVkVHlwZVNwZWNpZmllciIvPgogICAgICAgICA8L29wZXJhbmQ+CiAgICAgIDwvZGVmPgogICAgICA8ZGVmIGxvY2F0b3I9IjI1MzoxLTI1Mzo1OCIgbmFtZT0iVG9TdHJpbmciIGNvbnRleHQ9IlBhdGllbnQiIGFjY2Vzc0xldmVsPSJQdWJsaWMiIHhzaTp0eXBlPSJGdW5jdGlvbkRlZiI+CiAgICAgICAgIDxleHByZXNzaW9uIGxvY2F0b3I9IjI1Mzo0OC0yNTM6NTgiIHBhdGg9InZhbHVlIiB4c2k6dHlwZT0iUHJvcGVydHkiPgogICAgICAgICAgICA8c291cmNlIGxvY2F0b3I9IjI1Mzo0OC0yNTM6NTIiIG5hbWU9InZhbHVlIiB4c2k6dHlwZT0iT3BlcmFuZFJlZiIvPgogICAgICAgICA8L2V4cHJlc3Npb24+CiAgICAgICAgIDxvcGVyYW5kIG5hbWU9InZhbHVlIj4KICAgICAgICAgICAgPG9wZXJhbmRUeXBlU3BlY2lmaWVyIGxvY2F0b3I9IjI1MzozMi0yNTM6NDQiIG5hbWU9ImZoaXI6RXhwb3N1cmVTdGF0ZSIgeHNpOnR5cGU9Ik5hbWVkVHlwZVNwZWNpZmllciIvPgogICAgICAgICA8L29wZXJhbmQ+CiAgICAgIDwvZGVmPgogICAgICA8ZGVmIGxvY2F0b3I9IjI1NDoxLTI1NDo2NSIgbmFtZT0iVG9TdHJpbmciIGNvbnRleHQ9IlBhdGllbnQiIGFjY2Vzc0xldmVsPSJQdWJsaWMiIHhzaTp0eXBlPSJGdW5jdGlvbkRlZiI+CiAgICAgICAgIDxleHByZXNzaW9uIGxvY2F0b3I9IjI1NDo1NS0yNTQ6NjUiIHBhdGg9InZhbHVlIiB4c2k6dHlwZT0iUHJvcGVydHkiPgogICAgICAgICAgICA8c291cmNlIGxvY2F0b3I9IjI1NDo1NS0yNTQ6NTkiIG5hbWU9InZhbHVlIiB4c2k6dHlwZT0iT3BlcmFuZFJlZiIvPgogICAgICAgICA8L2V4cHJlc3Npb24+CiAgICAgICAgIDxvcGVyYW5kIG5hbWU9InZhbHVlIj4KICAgICAgICAgICAgPG9wZXJhbmRUeXBlU3BlY2lmaWVyIGxvY2F0b3I9IjI1NDozMi0yNTQ6NTEiIG5hbWU9ImZoaXI6RXh0ZW5zaW9uQ29udGV4dFR5cGUiIHhzaTp0eXBlPSJOYW1lZFR5cGVTcGVjaWZpZXIiLz4KICAgICAgICAgPC9vcGVyYW5kPgogICAgICA8L2RlZj4KICAgICAgPGRlZiBsb2NhdG9yPSIyNTU6MS0yNTU6NTciIG5hbWU9IlRvU3RyaW5nIiBjb250ZXh0PSJQYXRpZW50IiBhY2Nlc3NMZXZlbD0iUHVibGljIiB4c2k6dHlwZT0iRnVuY3Rpb25EZWYiPgogICAgICAgICA8ZXhwcmVzc2lvbiBsb2NhdG9yPSIyNTU6NDctMjU1OjU3IiBwYXRoPSJ2YWx1ZSIgeHNpOnR5cGU9IlByb3BlcnR5Ij4KICAgICAgICAgICAgPHNvdXJjZSBsb2NhdG9yPSIyNTU6NDctMjU1OjUxIiBuYW1lPSJ2YWx1ZSIgeHNpOnR5cGU9Ik9wZXJhbmRSZWYiLz4KICAgICAgICAgPC9leHByZXNzaW9uPgogICAgICAgICA8b3BlcmFuZCBuYW1lPSJ2YWx1ZSI+CiAgICAgICAgICAgIDxvcGVyYW5kVHlwZVNwZWNpZmllciBsb2NhdG9yPSIyNTU6MzItMjU1OjQzIiBuYW1lPSJmaGlyOkZISVJBbGxUeXBlcyIgeHNpOnR5cGU9Ik5hbWVkVHlwZVNwZWNpZmllciIvPgogICAgICAgICA8L29wZXJhbmQ+CiAgICAgIDwvZGVmPgogICAgICA8ZGVmIGxvY2F0b3I9IjI1NjoxLTI1Njo2MCIgbmFtZT0iVG9TdHJpbmciIGNvbnRleHQ9IlBhdGllbnQiIGFjY2Vzc0xldmVsPSJQdWJsaWMiIHhzaTp0eXBlPSJGdW5jdGlvbkRlZiI+CiAgICAgICAgIDxleHByZXNzaW9uIGxvY2F0b3I9IjI1Njo1MC0yNTY6NjAiIHBhdGg9InZhbHVlIiB4c2k6dHlwZT0iUHJvcGVydHkiPgogICAgICAgICAgICA8c291cmNlIGxvY2F0b3I9IjI1Njo1MC0yNTY6NTQiIG5hbWU9InZhbHVlIiB4c2k6dHlwZT0iT3BlcmFuZFJlZiIvPgogICAgICAgICA8L2V4cHJlc3Npb24+CiAgICAgICAgIDxvcGVyYW5kIG5hbWU9InZhbHVlIj4KICAgICAgICAgICAgPG9wZXJhbmRUeXBlU3BlY2lmaWVyIGxvY2F0b3I9IjI1NjozMi0yNTY6NDYiIG5hbWU9ImZoaXI6RkhJUkRlZmluZWRUeXBlIiB4c2k6dHlwZT0iTmFtZWRUeXBlU3BlY2lmaWVyIi8+CiAgICAgICAgIDwvb3BlcmFuZD4KICAgICAgPC9kZWY+CiAgICAgIDxkZWYgbG9jYXRvcj0iMjU3OjEtMjU3OjYxIiBuYW1lPSJUb1N0cmluZyIgY29udGV4dD0iUGF0aWVudCIgYWNjZXNzTGV2ZWw9IlB1YmxpYyIgeHNpOnR5cGU9IkZ1bmN0aW9uRGVmIj4KICAgICAgICAgPGV4cHJlc3Npb24gbG9jYXRvcj0iMjU3OjUxLTI1Nzo2MSIgcGF0aD0idmFsdWUiIHhzaTp0eXBlPSJQcm9wZXJ0eSI+CiAgICAgICAgICAgIDxzb3VyY2UgbG9jYXRvcj0iMjU3OjUxLTI1Nzo1NSIgbmFtZT0idmFsdWUiIHhzaTp0eXBlPSJPcGVyYW5kUmVmIi8+CiAgICAgICAgIDwvZXhwcmVzc2lvbj4KICAgICAgICAgPG9wZXJhbmQgbmFtZT0idmFsdWUiPgogICAgICAgICAgICA8b3BlcmFuZFR5cGVTcGVjaWZpZXIgbG9jYXRvcj0iMjU3OjMyLTI1Nzo0NyIgbmFtZT0iZmhpcjpGSElSRGV2aWNlU3RhdHVzIiB4c2k6dHlwZT0iTmFtZWRUeXBlU3BlY2lmaWVyIi8+CiAgICAgICAgIDwvb3BlcmFuZD4KICAgICAgPC9kZWY+CiAgICAgIDxkZWYgbG9jYXRvcj0iMjU4OjEtMjU4OjYxIiBuYW1lPSJUb1N0cmluZyIgY29udGV4dD0iUGF0aWVudCIgYWNjZXNzTGV2ZWw9IlB1YmxpYyIgeHNpOnR5cGU9IkZ1bmN0aW9uRGVmIj4KICAgICAgICAgPGV4cHJlc3Npb24gbG9jYXRvcj0iMjU4OjUxLTI1ODo2MSIgcGF0aD0idmFsdWUiIHhzaTp0eXBlPSJQcm9wZXJ0eSI+CiAgICAgICAgICAgIDxzb3VyY2UgbG9jYXRvcj0iMjU4OjUxLTI1ODo1NSIgbmFtZT0idmFsdWUiIHhzaTp0eXBlPSJPcGVyYW5kUmVmIi8+CiAgICAgICAgIDwvZXhwcmVzc2lvbj4KICAgICAgICAgPG9wZXJhbmQgbmFtZT0idmFsdWUiPgogICAgICAgICAgICA8b3BlcmFuZFR5cGVTcGVjaWZpZXIgbG9jYXRvcj0iMjU4OjMyLTI1ODo0NyIgbmFtZT0iZmhpcjpGSElSUmVzb3VyY2VUeXBlIiB4c2k6dHlwZT0iTmFtZWRUeXBlU3BlY2lmaWVyIi8+CiAgICAgICAgIDwvb3BlcmFuZD4KICAgICAgPC9kZWY+CiAgICAgIDxkZWYgbG9jYXRvcj0iMjU5OjEtMjU5OjY0IiBuYW1lPSJUb1N0cmluZyIgY29udGV4dD0iUGF0aWVudCIgYWNjZXNzTGV2ZWw9IlB1YmxpYyIgeHNpOnR5cGU9IkZ1bmN0aW9uRGVmIj4KICAgICAgICAgPGV4cHJlc3Npb24gbG9jYXRvcj0iMjU5OjU0LTI1OTo2NCIgcGF0aD0idmFsdWUiIHhzaTp0eXBlPSJQcm9wZXJ0eSI+CiAgICAgICAgICAgIDxzb3VyY2UgbG9jYXRvcj0iMjU5OjU0LTI1OTo1OCIgbmFtZT0idmFsdWUiIHhzaTp0eXBlPSJPcGVyYW5kUmVmIi8+CiAgICAgICAgIDwvZXhwcmVzc2lvbj4KICAgICAgICAgPG9wZXJhbmQgbmFtZT0idmFsdWUiPgogICAgICAgICAgICA8b3BlcmFuZFR5cGVTcGVjaWZpZXIgbG9jYXRvcj0iMjU5OjMyLTI1OTo1MCIgbmFtZT0iZmhpcjpGSElSU3Vic3RhbmNlU3RhdHVzIiB4c2k6dHlwZT0iTmFtZWRUeXBlU3BlY2lmaWVyIi8+CiAgICAgICAgIDwvb3BlcmFuZD4KICAgICAgPC9kZWY+CiAgICAgIDxkZWYgbG9jYXRvcj0iMjYwOjEtMjYwOjU2IiBuYW1lPSJUb1N0cmluZyIgY29udGV4dD0iUGF0aWVudCIgYWNjZXNzTGV2ZWw9IlB1YmxpYyIgeHNpOnR5cGU9IkZ1bmN0aW9uRGVmIj4KICAgICAgICAgPGV4cHJlc3Npb24gbG9jYXRvcj0iMjYwOjQ2LTI2MDo1NiIgcGF0aD0idmFsdWUiIHhzaTp0eXBlPSJQcm9wZXJ0eSI+CiAgICAgICAgICAgIDxzb3VyY2UgbG9jYXRvcj0iMjYwOjQ2LTI2MDo1MCIgbmFtZT0idmFsdWUiIHhzaTp0eXBlPSJPcGVyYW5kUmVmIi8+CiAgICAgICAgIDwvZXhwcmVzc2lvbj4KICAgICAgICAgPG9wZXJhbmQgbmFtZT0idmFsdWUiPgogICAgICAgICAgICA8b3BlcmFuZFR5cGVTcGVjaWZpZXIgbG9jYXRvcj0iMjYwOjMyLTI2MDo0MiIgbmFtZT0iZmhpcjpGSElSVmVyc2lvbiIgeHNpOnR5cGU9Ik5hbWVkVHlwZVNwZWNpZmllciIvPgogICAgICAgICA8L29wZXJhbmQ+CiAgICAgIDwvZGVmPgogICAgICA8ZGVmIGxvY2F0b3I9IjI2MToxLTI2MTo2NCIgbmFtZT0iVG9TdHJpbmciIGNvbnRleHQ9IlBhdGllbnQiIGFjY2Vzc0xldmVsPSJQdWJsaWMiIHhzaTp0eXBlPSJGdW5jdGlvbkRlZiI+CiAgICAgICAgIDxleHByZXNzaW9uIGxvY2F0b3I9IjI2MTo1NC0yNjE6NjQiIHBhdGg9InZhbHVlIiB4c2k6dHlwZT0iUHJvcGVydHkiPgogICAgICAgICAgICA8c291cmNlIGxvY2F0b3I9IjI2MTo1NC0yNjE6NTgiIG5hbWU9InZhbHVlIiB4c2k6dHlwZT0iT3BlcmFuZFJlZiIvPgogICAgICAgICA8L2V4cHJlc3Npb24+CiAgICAgICAgIDxvcGVyYW5kIG5hbWU9InZhbHVlIj4KICAgICAgICAgICAgPG9wZXJhbmRUeXBlU3BlY2lmaWVyIGxvY2F0b3I9IjI2MTozMi0yNjE6NTAiIG5hbWU9ImZoaXI6RmFtaWx5SGlzdG9yeVN0YXR1cyIgeHNpOnR5cGU9Ik5hbWVkVHlwZVNwZWNpZmllciIvPgogICAgICAgICA8L29wZXJhbmQ+CiAgICAgIDwvZGVmPgogICAgICA8ZGVmIGxvY2F0b3I9IjI2MjoxLTI2Mjo1OSIgbmFtZT0iVG9TdHJpbmciIGNvbnRleHQ9IlBhdGllbnQiIGFjY2Vzc0xldmVsPSJQdWJsaWMiIHhzaTp0eXBlPSJGdW5jdGlvbkRlZiI+CiAgICAgICAgIDxleHByZXNzaW9uIGxvY2F0b3I9IjI2Mjo0OS0yNjI6NTkiIHBhdGg9InZhbHVlIiB4c2k6dHlwZT0iUHJvcGVydHkiPgogICAgICAgICAgICA8c291cmNlIGxvY2F0b3I9IjI2Mjo0OS0yNjI6NTMiIG5hbWU9InZhbHVlIiB4c2k6dHlwZT0iT3BlcmFuZFJlZiIvPgogICAgICAgICA8L2V4cHJlc3Npb24+CiAgICAgICAgIDxvcGVyYW5kIG5hbWU9InZhbHVlIj4KICAgICAgICAgICAgPG9wZXJhbmRUeXBlU3BlY2lmaWVyIGxvY2F0b3I9IjI2MjozMi0yNjI6NDUiIG5hbWU9ImZoaXI6RmlsdGVyT3BlcmF0b3IiIHhzaTp0eXBlPSJOYW1lZFR5cGVTcGVjaWZpZXIiLz4KICAgICAgICAgPC9vcGVyYW5kPgogICAgICA8L2RlZj4KICAgICAgPGRlZiBsb2NhdG9yPSIyNjM6MS0yNjM6NTUiIG5hbWU9IlRvU3RyaW5nIiBjb250ZXh0PSJQYXRpZW50IiBhY2Nlc3NMZXZlbD0iUHVibGljIiB4c2k6dHlwZT0iRnVuY3Rpb25EZWYiPgogICAgICAgICA8ZXhwcmVzc2lvbiBsb2NhdG9yPSIyNjM6NDUtMjYzOjU1IiBwYXRoPSJ2YWx1ZSIgeHNpOnR5cGU9IlByb3BlcnR5Ij4KICAgICAgICAgICAgPHNvdXJjZSBsb2NhdG9yPSIyNjM6NDUtMjYzOjQ5IiBuYW1lPSJ2YWx1ZSIgeHNpOnR5cGU9Ik9wZXJhbmRSZWYiLz4KICAgICAgICAgPC9leHByZXNzaW9uPgogICAgICAgICA8b3BlcmFuZCBuYW1lPSJ2YWx1ZSI+CiAgICAgICAgICAgIDxvcGVyYW5kVHlwZVNwZWNpZmllciBsb2NhdG9yPSIyNjM6MzItMjYzOjQxIiBuYW1lPSJmaGlyOkZsYWdTdGF0dXMiIHhzaTp0eXBlPSJOYW1lZFR5cGVTcGVjaWZpZXIiLz4KICAgICAgICAgPC9vcGVyYW5kPgogICAgICA8L2RlZj4KICAgICAgPGRlZiBsb2NhdG9yPSIyNjQ6MS0yNjQ6NjQiIG5hbWU9IlRvU3RyaW5nIiBjb250ZXh0PSJQYXRpZW50IiBhY2Nlc3NMZXZlbD0iUHVibGljIiB4c2k6dHlwZT0iRnVuY3Rpb25EZWYiPgogICAgICAgICA8ZXhwcmVzc2lvbiBsb2NhdG9yPSIyNjQ6NTQtMjY0OjY0IiBwYXRoPSJ2YWx1ZSIgeHNpOnR5cGU9IlByb3BlcnR5Ij4KICAgICAgICAgICAgPHNvdXJjZSBsb2NhdG9yPSIyNjQ6NTQtMjY0OjU4IiBuYW1lPSJ2YWx1ZSIgeHNpOnR5cGU9Ik9wZXJhbmRSZWYiLz4KICAgICAgICAgPC9leHByZXNzaW9uPgogICAgICAgICA8b3BlcmFuZCBuYW1lPSJ2YWx1ZSI+CiAgICAgICAgICAgIDxvcGVyYW5kVHlwZVNwZWNpZmllciBsb2NhdG9yPSIyNjQ6MzItMjY0OjUwIiBuYW1lPSJmaGlyOkdvYWxMaWZlY3ljbGVTdGF0dXMiIHhzaTp0eXBlPSJOYW1lZFR5cGVTcGVjaWZpZXIiLz4KICAgICAgICAgPC9vcGVyYW5kPgogICAgICA8L2RlZj4KICAgICAgPGRlZiBsb2NhdG9yPSIyNjU6MS0yNjU6NjUiIG5hbWU9IlRvU3RyaW5nIiBjb250ZXh0PSJQYXRpZW50IiBhY2Nlc3NMZXZlbD0iUHVibGljIiB4c2k6dHlwZT0iRnVuY3Rpb25EZWYiPgogICAgICAgICA8ZXhwcmVzc2lvbiBsb2NhdG9yPSIyNjU6NTUtMjY1OjY1IiBwYXRoPSJ2YWx1ZSIgeHNpOnR5cGU9IlByb3BlcnR5Ij4KICAgICAgICAgICAgPHNvdXJjZSBsb2NhdG9yPSIyNjU6NTUtMjY1OjU5IiBuYW1lPSJ2YWx1ZSIgeHNpOnR5cGU9Ik9wZXJhbmRSZWYiLz4KICAgICAgICAgPC9leHByZXNzaW9uPgogICAgICAgICA8b3BlcmFuZCBuYW1lPSJ2YWx1ZSI+CiAgICAgICAgICAgIDxvcGVyYW5kVHlwZVNwZWNpZmllciBsb2NhdG9yPSIyNjU6MzItMjY1OjUxIiBuYW1lPSJmaGlyOkdyYXBoQ29tcGFydG1lbnRSdWxlIiB4c2k6dHlwZT0iTmFtZWRUeXBlU3BlY2lmaWVyIi8+CiAgICAgICAgIDwvb3BlcmFuZD4KICAgICAgPC9kZWY+CiAgICAgIDxkZWYgbG9jYXRvcj0iMjY2OjEtMjY2OjY0IiBuYW1lPSJUb1N0cmluZyIgY29udGV4dD0iUGF0aWVudCIgYWNjZXNzTGV2ZWw9IlB1YmxpYyIgeHNpOnR5cGU9IkZ1bmN0aW9uRGVmIj4KICAgICAgICAgPGV4cHJlc3Npb24gbG9jYXRvcj0iMjY2OjU0LTI2Njo2NCIgcGF0aD0idmFsdWUiIHhzaTp0eXBlPSJQcm9wZXJ0eSI+CiAgICAgICAgICAgIDxzb3VyY2UgbG9jYXRvcj0iMjY2OjU0LTI2Njo1OCIgbmFtZT0idmFsdWUiIHhzaTp0eXBlPSJPcGVyYW5kUmVmIi8+CiAgICAgICAgIDwvZXhwcmVzc2lvbj4KICAgICAgICAgPG9wZXJhbmQgbmFtZT0idmFsdWUiPgogICAgICAgICAgICA8b3BlcmFuZFR5cGVTcGVjaWZpZXIgbG9jYXRvcj0iMjY2OjMyLTI2Njo1MCIgbmFtZT0iZmhpcjpHcmFwaENvbXBhcnRtZW50VXNlIiB4c2k6dHlwZT0iTmFtZWRUeXBlU3BlY2lmaWVyIi8+CiAgICAgICAgIDwvb3BlcmFuZD4KICAgICAgPC9kZWY+CiAgICAgIDxkZWYgbG9jYXRvcj0iMjY3OjEtMjY3OjU3IiBuYW1lPSJUb1N0cmluZyIgY29udGV4dD0iUGF0aWVudCIgYWNjZXNzTGV2ZWw9IlB1YmxpYyIgeHNpOnR5cGU9IkZ1bmN0aW9uRGVmIj4KICAgICAgICAgPGV4cHJlc3Npb24gbG9jYXRvcj0iMjY3OjQ3LTI2Nzo1NyIgcGF0aD0idmFsdWUiIHhzaTp0eXBlPSJQcm9wZXJ0eSI+CiAgICAgICAgICAgIDxzb3VyY2UgbG9jYXRvcj0iMjY3OjQ3LTI2Nzo1MSIgbmFtZT0idmFsdWUiIHhzaTp0eXBlPSJPcGVyYW5kUmVmIi8+CiAgICAgICAgIDwvZXhwcmVzc2lvbj4KICAgICAgICAgPG9wZXJhbmQgbmFtZT0idmFsdWUiPgogICAgICAgICAgICA8b3BlcmFuZFR5cGVTcGVjaWZpZXIgbG9jYXRvcj0iMjY3OjMyLTI2Nzo0MyIgbmFtZT0iZmhpcjpHcm91cE1lYXN1cmUiIHhzaTp0eXBlPSJOYW1lZFR5cGVTcGVjaWZpZXIiLz4KICAgICAgICAgPC9vcGVyYW5kPgogICAgICA8L2RlZj4KICAgICAgPGRlZiBsb2NhdG9yPSIyNjg6MS0yNjg6NTQiIG5hbWU9IlRvU3RyaW5nIiBjb250ZXh0PSJQYXRpZW50IiBhY2Nlc3NMZXZlbD0iUHVibGljIiB4c2k6dHlwZT0iRnVuY3Rpb25EZWYiPgogICAgICAgICA8ZXhwcmVzc2lvbiBsb2NhdG9yPSIyNjg6NDQtMjY4OjU0IiBwYXRoPSJ2YWx1ZSIgeHNpOnR5cGU9IlByb3BlcnR5Ij4KICAgICAgICAgICAgPHNvdXJjZSBsb2NhdG9yPSIyNjg6NDQtMjY4OjQ4IiBuYW1lPSJ2YWx1ZSIgeHNpOnR5cGU9Ik9wZXJhbmRSZWYiLz4KICAgICAgICAgPC9leHByZXNzaW9uPgogICAgICAgICA8b3BlcmFuZCBuYW1lPSJ2YWx1ZSI+CiAgICAgICAgICAgIDxvcGVyYW5kVHlwZVNwZWNpZmllciBsb2NhdG9yPSIyNjg6MzItMjY4OjQwIiBuYW1lPSJmaGlyOkdyb3VwVHlwZSIgeHNpOnR5cGU9Ik5hbWVkVHlwZVNwZWNpZmllciIvPgogICAgICAgICA8L29wZXJhbmQ+CiAgICAgIDwvZGVmPgogICAgICA8ZGVmIGxvY2F0b3I9IjI2OToxLTI2OTo2NyIgbmFtZT0iVG9TdHJpbmciIGNvbnRleHQ9IlBhdGllbnQiIGFjY2Vzc0xldmVsPSJQdWJsaWMiIHhzaTp0eXBlPSJGdW5jdGlvbkRlZiI+CiAgICAgICAgIDxleHByZXNzaW9uIGxvY2F0b3I9IjI2OTo1Ny0yNjk6NjciIHBhdGg9InZhbHVlIiB4c2k6dHlwZT0iUHJvcGVydHkiPgogICAgICAgICAgICA8c291cmNlIGxvY2F0b3I9IjI2OTo1Ny0yNjk6NjEiIG5hbWU9InZhbHVlIiB4c2k6dHlwZT0iT3BlcmFuZFJlZiIvPgogICAgICAgICA8L2V4cHJlc3Npb24+CiAgICAgICAgIDxvcGVyYW5kIG5hbWU9InZhbHVlIj4KICAgICAgICAgICAgPG9wZXJhbmRUeXBlU3BlY2lmaWVyIGxvY2F0b3I9IjI2OTozMi0yNjk6NTMiIG5hbWU9ImZoaXI6R3VpZGFuY2VSZXNwb25zZVN0YXR1cyIgeHNpOnR5cGU9Ik5hbWVkVHlwZVNwZWNpZmllciIvPgogICAgICAgICA8L29wZXJhbmQ+CiAgICAgIDwvZGVmPgogICAgICA8ZGVmIGxvY2F0b3I9IjI3MDoxLTI3MDo2NCIgbmFtZT0iVG9TdHJpbmciIGNvbnRleHQ9IlBhdGllbnQiIGFjY2Vzc0xldmVsPSJQdWJsaWMiIHhzaTp0eXBlPSJGdW5jdGlvbkRlZiI+CiAgICAgICAgIDxleHByZXNzaW9uIGxvY2F0b3I9IjI3MDo1NC0yNzA6NjQiIHBhdGg9InZhbHVlIiB4c2k6dHlwZT0iUHJvcGVydHkiPgogICAgICAgICAgICA8c291cmNlIGxvY2F0b3I9IjI3MDo1NC0yNzA6NTgiIG5hbWU9InZhbHVlIiB4c2k6dHlwZT0iT3BlcmFuZFJlZiIvPgogICAgICAgICA8L2V4cHJlc3Npb24+CiAgICAgICAgIDxvcGVyYW5kIG5hbWU9InZhbHVlIj4KICAgICAgICAgICAgPG9wZXJhbmRUeXBlU3BlY2lmaWVyIGxvY2F0b3I9IjI3MDozMi0yNzA6NTAiIG5hbWU9ImZoaXI6R3VpZGVQYWdlR2VuZXJhdGlvbiIgeHNpOnR5cGU9Ik5hbWVkVHlwZVNwZWNpZmllciIvPgogICAgICAgICA8L29wZXJhbmQ+CiAgICAgIDwvZGVmPgogICAgICA8ZGVmIGxvY2F0b3I9IjI3MToxLTI3MTo2MyIgbmFtZT0iVG9TdHJpbmciIGNvbnRleHQ9IlBhdGllbnQiIGFjY2Vzc0xldmVsPSJQdWJsaWMiIHhzaTp0eXBlPSJGdW5jdGlvbkRlZiI+CiAgICAgICAgIDxleHByZXNzaW9uIGxvY2F0b3I9IjI3MTo1My0yNzE6NjMiIHBhdGg9InZhbHVlIiB4c2k6dHlwZT0iUHJvcGVydHkiPgogICAgICAgICAgICA8c291cmNlIGxvY2F0b3I9IjI3MTo1My0yNzE6NTciIG5hbWU9InZhbHVlIiB4c2k6dHlwZT0iT3BlcmFuZFJlZiIvPgogICAgICAgICA8L2V4cHJlc3Npb24+CiAgICAgICAgIDxvcGVyYW5kIG5hbWU9InZhbHVlIj4KICAgICAgICAgICAgPG9wZXJhbmRUeXBlU3BlY2lmaWVyIGxvY2F0b3I9IjI3MTozMi0yNzE6NDkiIG5hbWU9ImZoaXI6R3VpZGVQYXJhbWV0ZXJDb2RlIiB4c2k6dHlwZT0iTmFtZWRUeXBlU3BlY2lmaWVyIi8+CiAgICAgICAgIDwvb3BlcmFuZD4KICAgICAgPC9kZWY+CiAgICAgIDxkZWYgbG9jYXRvcj0iMjcyOjEtMjcyOjUzIiBuYW1lPSJUb1N0cmluZyIgY29udGV4dD0iUGF0aWVudCIgYWNjZXNzTGV2ZWw9IlB1YmxpYyIgeHNpOnR5cGU9IkZ1bmN0aW9uRGVmIj4KICAgICAgICAgPGV4cHJlc3Npb24gbG9jYXRvcj0iMjcyOjQzLTI3Mjo1MyIgcGF0aD0idmFsdWUiIHhzaTp0eXBlPSJQcm9wZXJ0eSI+CiAgICAgICAgICAgIDxzb3VyY2UgbG9jYXRvcj0iMjcyOjQzLTI3Mjo0NyIgbmFtZT0idmFsdWUiIHhzaTp0eXBlPSJPcGVyYW5kUmVmIi8+CiAgICAgICAgIDwvZXhwcmVzc2lvbj4KICAgICAgICAgPG9wZXJhbmQgbmFtZT0idmFsdWUiPgogICAgICAgICAgICA8b3BlcmFuZFR5cGVTcGVjaWZpZXIgbG9jYXRvcj0iMjcyOjMyLTI3MjozOSIgbmFtZT0iZmhpcjpIVFRQVmVyYiIgeHNpOnR5cGU9Ik5hbWVkVHlwZVNwZWNpZmllciIvPgogICAgICAgICA8L29wZXJhbmQ+CiAgICAgIDwvZGVmPgogICAgICA8ZGVmIGxvY2F0b3I9IjI3MzoxLTI3Mzo1OCIgbmFtZT0iVG9TdHJpbmciIGNvbnRleHQ9IlBhdGllbnQiIGFjY2Vzc0xldmVsPSJQdWJsaWMiIHhzaTp0eXBlPSJGdW5jdGlvbkRlZiI+CiAgICAgICAgIDxleHByZXNzaW9uIGxvY2F0b3I9IjI3Mzo0OC0yNzM6NTgiIHBhdGg9InZhbHVlIiB4c2k6dHlwZT0iUHJvcGVydHkiPgogICAgICAgICAgICA8c291cmNlIGxvY2F0b3I9IjI3Mzo0OC0yNzM6NTIiIG5hbWU9InZhbHVlIiB4c2k6dHlwZT0iT3BlcmFuZFJlZiIvPgogICAgICAgICA8L2V4cHJlc3Npb24+CiAgICAgICAgIDxvcGVyYW5kIG5hbWU9InZhbHVlIj4KICAgICAgICAgICAgPG9wZXJhbmRUeXBlU3BlY2lmaWVyIGxvY2F0b3I9IjI3MzozMi0yNzM6NDQiIG5hbWU9ImZoaXI6SWRlbnRpZmllclVzZSIgeHNpOnR5cGU9Ik5hbWVkVHlwZVNwZWNpZmllciIvPgogICAgICAgICA8L29wZXJhbmQ+CiAgICAgIDwvZGVmPgogICAgICA8ZGVmIGxvY2F0b3I9IjI3NDoxLTI3NDo2NyIgbmFtZT0iVG9TdHJpbmciIGNvbnRleHQ9IlBhdGllbnQiIGFjY2Vzc0xldmVsPSJQdWJsaWMiIHhzaTp0eXBlPSJGdW5jdGlvbkRlZiI+CiAgICAgICAgIDxleHByZXNzaW9uIGxvY2F0b3I9IjI3NDo1Ny0yNzQ6NjciIHBhdGg9InZhbHVlIiB4c2k6dHlwZT0iUHJvcGVydHkiPgogICAgICAgICAgICA8c291cmNlIGxvY2F0b3I9IjI3NDo1Ny0yNzQ6NjEiIG5hbWU9InZhbHVlIiB4c2k6dHlwZT0iT3BlcmFuZFJlZiIvPgogICAgICAgICA8L2V4cHJlc3Npb24+CiAgICAgICAgIDxvcGVyYW5kIG5hbWU9InZhbHVlIj4KICAgICAgICAgICAgPG9wZXJhbmRUeXBlU3BlY2lmaWVyIGxvY2F0b3I9IjI3NDozMi0yNzQ6NTMiIG5hbWU9ImZoaXI6SWRlbnRpdHlBc3N1cmFuY2VMZXZlbCIgeHNpOnR5cGU9Ik5hbWVkVHlwZVNwZWNpZmllciIvPgogICAgICAgICA8L29wZXJhbmQ+CiAgICAgIDwvZGVmPgogICAgICA8ZGVmIGxvY2F0b3I9IjI3NToxLTI3NTo2MyIgbmFtZT0iVG9TdHJpbmciIGNvbnRleHQ9IlBhdGllbnQiIGFjY2Vzc0xldmVsPSJQdWJsaWMiIHhzaTp0eXBlPSJGdW5jdGlvbkRlZiI+CiAgICAgICAgIDxleHByZXNzaW9uIGxvY2F0b3I9IjI3NTo1My0yNzU6NjMiIHBhdGg9InZhbHVlIiB4c2k6dHlwZT0iUHJvcGVydHkiPgogICAgICAgICAgICA8c291cmNlIGxvY2F0b3I9IjI3NTo1My0yNzU6NTciIG5hbWU9InZhbHVlIiB4c2k6dHlwZT0iT3BlcmFuZFJlZiIvPgogICAgICAgICA8L2V4cHJlc3Npb24+CiAgICAgICAgIDxvcGVyYW5kIG5hbWU9InZhbHVlIj4KICAgICAgICAgICAgPG9wZXJhbmRUeXBlU3BlY2lmaWVyIGxvY2F0b3I9IjI3NTozMi0yNzU6NDkiIG5hbWU9ImZoaXI6SW1hZ2luZ1N0dWR5U3RhdHVzIiB4c2k6dHlwZT0iTmFtZWRUeXBlU3BlY2lmaWVyIi8+CiAgICAgICAgIDwvb3BlcmFuZD4KICAgICAgPC9kZWY+CiAgICAgIDxkZWYgbG9jYXRvcj0iMjc2OjEtMjc2OjczIiBuYW1lPSJUb1N0cmluZyIgY29udGV4dD0iUGF0aWVudCIgYWNjZXNzTGV2ZWw9IlB1YmxpYyIgeHNpOnR5cGU9IkZ1bmN0aW9uRGVmIj4KICAgICAgICAgPGV4cHJlc3Npb24gbG9jYXRvcj0iMjc2OjYzLTI3Njo3MyIgcGF0aD0idmFsdWUiIHhzaTp0eXBlPSJQcm9wZXJ0eSI+CiAgICAgICAgICAgIDxzb3VyY2UgbG9jYXRvcj0iMjc2OjYzLTI3Njo2NyIgbmFtZT0idmFsdWUiIHhzaTp0eXBlPSJPcGVyYW5kUmVmIi8+CiAgICAgICAgIDwvZXhwcmVzc2lvbj4KICAgICAgICAgPG9wZXJhbmQgbmFtZT0idmFsdWUiPgogICAgICAgICAgICA8b3BlcmFuZFR5cGVTcGVjaWZpZXIgbG9jYXRvcj0iMjc2OjMyLTI3Njo1OSIgbmFtZT0iZmhpcjpJbW11bml6YXRpb25FdmFsdWF0aW9uU3RhdHVzIiB4c2k6dHlwZT0iTmFtZWRUeXBlU3BlY2lmaWVyIi8+CiAgICAgICAgIDwvb3BlcmFuZD4KICAgICAgPC9kZWY+CiAgICAgIDxkZWYgbG9jYXRvcj0iMjc3OjEtMjc3OjYzIiBuYW1lPSJUb1N0cmluZyIgY29udGV4dD0iUGF0aWVudCIgYWNjZXNzTGV2ZWw9IlB1YmxpYyIgeHNpOnR5cGU9IkZ1bmN0aW9uRGVmIj4KICAgICAgICAgPGV4cHJlc3Npb24gbG9jYXRvcj0iMjc3OjUzLTI3Nzo2MyIgcGF0aD0idmFsdWUiIHhzaTp0eXBlPSJQcm9wZXJ0eSI+CiAgICAgICAgICAgIDxzb3VyY2UgbG9jYXRvcj0iMjc3OjUzLTI3Nzo1NyIgbmFtZT0idmFsdWUiIHhzaTp0eXBlPSJPcGVyYW5kUmVmIi8+CiAgICAgICAgIDwvZXhwcmVzc2lvbj4KICAgICAgICAgPG9wZXJhbmQgbmFtZT0idmFsdWUiPgogICAgICAgICAgICA8b3BlcmFuZFR5cGVTcGVjaWZpZXIgbG9jYXRvcj0iMjc3OjMyLTI3Nzo0OSIgbmFtZT0iZmhpcjpJbW11bml6YXRpb25TdGF0dXMiIHhzaTp0eXBlPSJOYW1lZFR5cGVTcGVjaWZpZXIiLz4KICAgICAgICAgPC9vcGVyYW5kPgogICAgICA8L2RlZj4KICAgICAgPGRlZiBsb2NhdG9yPSIyNzg6MS0yNzg6NzAiIG5hbWU9IlRvU3RyaW5nIiBjb250ZXh0PSJQYXRpZW50IiBhY2Nlc3NMZXZlbD0iUHVibGljIiB4c2k6dHlwZT0iRnVuY3Rpb25EZWYiPgogICAgICAgICA8ZXhwcmVzc2lvbiBsb2NhdG9yPSIyNzg6NjAtMjc4OjcwIiBwYXRoPSJ2YWx1ZSIgeHNpOnR5cGU9IlByb3BlcnR5Ij4KICAgICAgICAgICAgPHNvdXJjZSBsb2NhdG9yPSIyNzg6NjAtMjc4OjY0IiBuYW1lPSJ2YWx1ZSIgeHNpOnR5cGU9Ik9wZXJhbmRSZWYiLz4KICAgICAgICAgPC9leHByZXNzaW9uPgogICAgICAgICA8b3BlcmFuZCBuYW1lPSJ2YWx1ZSI+CiAgICAgICAgICAgIDxvcGVyYW5kVHlwZVNwZWNpZmllciBsb2NhdG9yPSIyNzg6MzItMjc4OjU2IiBuYW1lPSJmaGlyOkludm9pY2VQcmljZUNvbXBvbmVudFR5cGUiIHhzaTp0eXBlPSJOYW1lZFR5cGVTcGVjaWZpZXIiLz4KICAgICAgICAgPC9vcGVyYW5kPgogICAgICA8L2RlZj4KICAgICAgPGRlZiBsb2NhdG9yPSIyNzk6MS0yNzk6NTgiIG5hbWU9IlRvU3RyaW5nIiBjb250ZXh0PSJQYXRpZW50IiBhY2Nlc3NMZXZlbD0iUHVibGljIiB4c2k6dHlwZT0iRnVuY3Rpb25EZWYiPgogICAgICAgICA8ZXhwcmVzc2lvbiBsb2NhdG9yPSIyNzk6NDgtMjc5OjU4IiBwYXRoPSJ2YWx1ZSIgeHNpOnR5cGU9IlByb3BlcnR5Ij4KICAgICAgICAgICAgPHNvdXJjZSBsb2NhdG9yPSIyNzk6NDgtMjc5OjUyIiBuYW1lPSJ2YWx1ZSIgeHNpOnR5cGU9Ik9wZXJhbmRSZWYiLz4KICAgICAgICAgPC9leHByZXNzaW9uPgogICAgICAgICA8b3BlcmFuZCBuYW1lPSJ2YWx1ZSI+CiAgICAgICAgICAgIDxvcGVyYW5kVHlwZVNwZWNpZmllciBsb2NhdG9yPSIyNzk6MzItMjc5OjQ0IiBuYW1lPSJmaGlyOkludm9pY2VTdGF0dXMiIHhzaTp0eXBlPSJOYW1lZFR5cGVTcGVjaWZpZXIiLz4KICAgICAgICAgPC9vcGVyYW5kPgogICAgICA8L2RlZj4KICAgICAgPGRlZiBsb2NhdG9yPSIyODA6MS0yODA6NTgiIG5hbWU9IlRvU3RyaW5nIiBjb250ZXh0PSJQYXRpZW50IiBhY2Nlc3NMZXZlbD0iUHVibGljIiB4c2k6dHlwZT0iRnVuY3Rpb25EZWYiPgogICAgICAgICA8ZXhwcmVzc2lvbiBsb2NhdG9yPSIyODA6NDgtMjgwOjU4IiBwYXRoPSJ2YWx1ZSIgeHNpOnR5cGU9IlByb3BlcnR5Ij4KICAgICAgICAgICAgPHNvdXJjZSBsb2NhdG9yPSIyODA6NDgtMjgwOjUyIiBuYW1lPSJ2YWx1ZSIgeHNpOnR5cGU9Ik9wZXJhbmRSZWYiLz4KICAgICAgICAgPC9leHByZXNzaW9uPgogICAgICAgICA8b3BlcmFuZCBuYW1lPSJ2YWx1ZSI+CiAgICAgICAgICAgIDxvcGVyYW5kVHlwZVNwZWNpZmllciBsb2NhdG9yPSIyODA6MzItMjgwOjQ0IiBuYW1lPSJmaGlyOklzc3VlU2V2ZXJpdHkiIHhzaTp0eXBlPSJOYW1lZFR5cGVTcGVjaWZpZXIiLz4KICAgICAgICAgPC9vcGVyYW5kPgogICAgICA8L2RlZj4KICAgICAgPGRlZiBsb2NhdG9yPSIyODE6MS0yODE6NTQiIG5hbWU9IlRvU3RyaW5nIiBjb250ZXh0PSJQYXRpZW50IiBhY2Nlc3NMZXZlbD0iUHVibGljIiB4c2k6dHlwZT0iRnVuY3Rpb25EZWYiPgogICAgICAgICA8ZXhwcmVzc2lvbiBsb2NhdG9yPSIyODE6NDQtMjgxOjU0IiBwYXRoPSJ2YWx1ZSIgeHNpOnR5cGU9IlByb3BlcnR5Ij4KICAgICAgICAgICAgPHNvdXJjZSBsb2NhdG9yPSIyODE6NDQtMjgxOjQ4IiBuYW1lPSJ2YWx1ZSIgeHNpOnR5cGU9Ik9wZXJhbmRSZWYiLz4KICAgICAgICAgPC9leHByZXNzaW9uPgogICAgICAgICA8b3BlcmFuZCBuYW1lPSJ2YWx1ZSI+CiAgICAgICAgICAgIDxvcGVyYW5kVHlwZVNwZWNpZmllciBsb2NhdG9yPSIyODE6MzItMjgxOjQwIiBuYW1lPSJmaGlyOklzc3VlVHlwZSIgeHNpOnR5cGU9Ik5hbWVkVHlwZVNwZWNpZmllciIvPgogICAgICAgICA8L29wZXJhbmQ+CiAgICAgIDwvZGVmPgogICAgICA8ZGVmIGxvY2F0b3I9IjI4MjoxLTI4Mjo1MyIgbmFtZT0iVG9TdHJpbmciIGNvbnRleHQ9IlBhdGllbnQiIGFjY2Vzc0xldmVsPSJQdWJsaWMiIHhzaTp0eXBlPSJGdW5jdGlvbkRlZiI+CiAgICAgICAgIDxleHByZXNzaW9uIGxvY2F0b3I9IjI4Mjo0My0yODI6NTMiIHBhdGg9InZhbHVlIiB4c2k6dHlwZT0iUHJvcGVydHkiPgogICAgICAgICAgICA8c291cmNlIGxvY2F0b3I9IjI4Mjo0My0yODI6NDciIG5hbWU9InZhbHVlIiB4c2k6dHlwZT0iT3BlcmFuZFJlZiIvPgogICAgICAgICA8L2V4cHJlc3Npb24+CiAgICAgICAgIDxvcGVyYW5kIG5hbWU9InZhbHVlIj4KICAgICAgICAgICAgPG9wZXJhbmRUeXBlU3BlY2lmaWVyIGxvY2F0b3I9IjI4MjozMi0yODI6MzkiIG5hbWU9ImZoaXI6TGlua1R5cGUiIHhzaTp0eXBlPSJOYW1lZFR5cGVTcGVjaWZpZXIiLz4KICAgICAgICAgPC9vcGVyYW5kPgogICAgICA8L2RlZj4KICAgICAgPGRlZiBsb2NhdG9yPSIyODM6MS0yODM6NTYiIG5hbWU9IlRvU3RyaW5nIiBjb250ZXh0PSJQYXRpZW50IiBhY2Nlc3NMZXZlbD0iUHVibGljIiB4c2k6dHlwZT0iRnVuY3Rpb25EZWYiPgogICAgICAgICA8ZXhwcmVzc2lvbiBsb2NhdG9yPSIyODM6NDYtMjgzOjU2IiBwYXRoPSJ2YWx1ZSIgeHNpOnR5cGU9IlByb3BlcnR5Ij4KICAgICAgICAgICAgPHNvdXJjZSBsb2NhdG9yPSIyODM6NDYtMjgzOjUwIiBuYW1lPSJ2YWx1ZSIgeHNpOnR5cGU9Ik9wZXJhbmRSZWYiLz4KICAgICAgICAgPC9leHByZXNzaW9uPgogICAgICAgICA8b3BlcmFuZCBuYW1lPSJ2YWx1ZSI+CiAgICAgICAgICAgIDxvcGVyYW5kVHlwZVNwZWNpZmllciBsb2NhdG9yPSIyODM6MzItMjgzOjQyIiBuYW1lPSJmaGlyOkxpbmthZ2VUeXBlIiB4c2k6dHlwZT0iTmFtZWRUeXBlU3BlY2lmaWVyIi8+CiAgICAgICAgIDwvb3BlcmFuZD4KICAgICAgPC9kZWY+CiAgICAgIDxkZWYgbG9jYXRvcj0iMjg0OjEtMjg0OjUzIiBuYW1lPSJUb1N0cmluZyIgY29udGV4dD0iUGF0aWVudCIgYWNjZXNzTGV2ZWw9IlB1YmxpYyIgeHNpOnR5cGU9IkZ1bmN0aW9uRGVmIj4KICAgICAgICAgPGV4cHJlc3Npb24gbG9jYXRvcj0iMjg0OjQzLTI4NDo1MyIgcGF0aD0idmFsdWUiIHhzaTp0eXBlPSJQcm9wZXJ0eSI+CiAgICAgICAgICAgIDxzb3VyY2UgbG9jYXRvcj0iMjg0OjQzLTI4NDo0NyIgbmFtZT0idmFsdWUiIHhzaTp0eXBlPSJPcGVyYW5kUmVmIi8+CiAgICAgICAgIDwvZXhwcmVzc2lvbj4KICAgICAgICAgPG9wZXJhbmQgbmFtZT0idmFsdWUiPgogICAgICAgICAgICA8b3BlcmFuZFR5cGVTcGVjaWZpZXIgbG9jYXRvcj0iMjg0OjMyLTI4NDozOSIgbmFtZT0iZmhpcjpMaXN0TW9kZSIgeHNpOnR5cGU9Ik5hbWVkVHlwZVNwZWNpZmllciIvPgogICAgICAgICA8L29wZXJhbmQ+CiAgICAgIDwvZGVmPgogICAgICA8ZGVmIGxvY2F0b3I9IjI4NToxLTI4NTo1NSIgbmFtZT0iVG9TdHJpbmciIGNvbnRleHQ9IlBhdGllbnQiIGFjY2Vzc0xldmVsPSJQdWJsaWMiIHhzaTp0eXBlPSJGdW5jdGlvbkRlZiI+CiAgICAgICAgIDxleHByZXNzaW9uIGxvY2F0b3I9IjI4NTo0NS0yODU6NTUiIHBhdGg9InZhbHVlIiB4c2k6dHlwZT0iUHJvcGVydHkiPgogICAgICAgICAgICA8c291cmNlIGxvY2F0b3I9IjI4NTo0NS0yODU6NDkiIG5hbWU9InZhbHVlIiB4c2k6dHlwZT0iT3BlcmFuZFJlZiIvPgogICAgICAgICA8L2V4cHJlc3Npb24+CiAgICAgICAgIDxvcGVyYW5kIG5hbWU9InZhbHVlIj4KICAgICAgICAgICAgPG9wZXJhbmRUeXBlU3BlY2lmaWVyIGxvY2F0b3I9IjI4NTozMi0yODU6NDEiIG5hbWU9ImZoaXI6TGlzdFN0YXR1cyIgeHNpOnR5cGU9Ik5hbWVkVHlwZVNwZWNpZmllciIvPgogICAgICAgICA8L29wZXJhbmQ+CiAgICAgIDwvZGVmPgogICAgICA8ZGVmIGxvY2F0b3I9IjI4NjoxLTI4Njo1NyIgbmFtZT0iVG9TdHJpbmciIGNvbnRleHQ9IlBhdGllbnQiIGFjY2Vzc0xldmVsPSJQdWJsaWMiIHhzaTp0eXBlPSJGdW5jdGlvbkRlZiI+CiAgICAgICAgIDxleHByZXNzaW9uIGxvY2F0b3I9IjI4Njo0Ny0yODY6NTciIHBhdGg9InZhbHVlIiB4c2k6dHlwZT0iUHJvcGVydHkiPgogICAgICAgICAgICA8c291cmNlIGxvY2F0b3I9IjI4Njo0Ny0yODY6NTEiIG5hbWU9InZhbHVlIiB4c2k6dHlwZT0iT3BlcmFuZFJlZiIvPgogICAgICAgICA8L2V4cHJlc3Npb24+CiAgICAgICAgIDxvcGVyYW5kIG5hbWU9InZhbHVlIj4KICAgICAgICAgICAgPG9wZXJhbmRUeXBlU3BlY2lmaWVyIGxvY2F0b3I9IjI4NjozMi0yODY6NDMiIG5hbWU9ImZoaXI6TG9jYXRpb25Nb2RlIiB4c2k6dHlwZT0iTmFtZWRUeXBlU3BlY2lmaWVyIi8+CiAgICAgICAgIDwvb3BlcmFuZD4KICAgICAgPC9kZWY+CiAgICAgIDxkZWYgbG9jYXRvcj0iMjg3OjEtMjg3OjU5IiBuYW1lPSJUb1N0cmluZyIgY29udGV4dD0iUGF0aWVudCIgYWNjZXNzTGV2ZWw9IlB1YmxpYyIgeHNpOnR5cGU9IkZ1bmN0aW9uRGVmIj4KICAgICAgICAgPGV4cHJlc3Npb24gbG9jYXRvcj0iMjg3OjQ5LTI4Nzo1OSIgcGF0aD0idmFsdWUiIHhzaTp0eXBlPSJQcm9wZXJ0eSI+CiAgICAgICAgICAgIDxzb3VyY2UgbG9jYXRvcj0iMjg3OjQ5LTI4Nzo1MyIgbmFtZT0idmFsdWUiIHhzaTp0eXBlPSJPcGVyYW5kUmVmIi8+CiAgICAgICAgIDwvZXhwcmVzc2lvbj4KICAgICAgICAgPG9wZXJhbmQgbmFtZT0idmFsdWUiPgogICAgICAgICAgICA8b3BlcmFuZFR5cGVTcGVjaWZpZXIgbG9jYXRvcj0iMjg3OjMyLTI4Nzo0NSIgbmFtZT0iZmhpcjpMb2NhdGlvblN0YXR1cyIgeHNpOnR5cGU9Ik5hbWVkVHlwZVNwZWNpZmllciIvPgogICAgICAgICA8L29wZXJhbmQ+CiAgICAgIDwvZGVmPgogICAgICA8ZGVmIGxvY2F0b3I9IjI4ODoxLTI4ODo2NCIgbmFtZT0iVG9TdHJpbmciIGNvbnRleHQ9IlBhdGllbnQiIGFjY2Vzc0xldmVsPSJQdWJsaWMiIHhzaTp0eXBlPSJGdW5jdGlvbkRlZiI+CiAgICAgICAgIDxleHByZXNzaW9uIGxvY2F0b3I9IjI4ODo1NC0yODg6NjQiIHBhdGg9InZhbHVlIiB4c2k6dHlwZT0iUHJvcGVydHkiPgogICAgICAgICAgICA8c291cmNlIGxvY2F0b3I9IjI4ODo1NC0yODg6NTgiIG5hbWU9InZhbHVlIiB4c2k6dHlwZT0iT3BlcmFuZFJlZiIvPgogICAgICAgICA8L2V4cHJlc3Npb24+CiAgICAgICAgIDxvcGVyYW5kIG5hbWU9InZhbHVlIj4KICAgICAgICAgICAgPG9wZXJhbmRUeXBlU3BlY2lmaWVyIGxvY2F0b3I9IjI4ODozMi0yODg6NTAiIG5hbWU9ImZoaXI6TWVhc3VyZVJlcG9ydFN0YXR1cyIgeHNpOnR5cGU9Ik5hbWVkVHlwZVNwZWNpZmllciIvPgogICAgICAgICA8L29wZXJhbmQ+CiAgICAgIDwvZGVmPgogICAgICA8ZGVmIGxvY2F0b3I9IjI4OToxLTI4OTo2MiIgbmFtZT0iVG9TdHJpbmciIGNvbnRleHQ9IlBhdGllbnQiIGFjY2Vzc0xldmVsPSJQdWJsaWMiIHhzaTp0eXBlPSJGdW5jdGlvbkRlZiI+CiAgICAgICAgIDxleHByZXNzaW9uIGxvY2F0b3I9IjI4OTo1Mi0yODk6NjIiIHBhdGg9InZhbHVlIiB4c2k6dHlwZT0iUHJvcGVydHkiPgogICAgICAgICAgICA8c291cmNlIGxvY2F0b3I9IjI4OTo1Mi0yODk6NTYiIG5hbWU9InZhbHVlIiB4c2k6dHlwZT0iT3BlcmFuZFJlZiIvPgogICAgICAgICA8L2V4cHJlc3Npb24+CiAgICAgICAgIDxvcGVyYW5kIG5hbWU9InZhbHVlIj4KICAgICAgICAgICAgPG9wZXJhbmRUeXBlU3BlY2lmaWVyIGxvY2F0b3I9IjI4OTozMi0yODk6NDgiIG5hbWU9ImZoaXI6TWVhc3VyZVJlcG9ydFR5cGUiIHhzaTp0eXBlPSJOYW1lZFR5cGVTcGVjaWZpZXIiLz4KICAgICAgICAgPC9vcGVyYW5kPgogICAgICA8L2RlZj4KICAgICAgPGRlZiBsb2NhdG9yPSIyOTA6MS0yOTA6NTYiIG5hbWU9IlRvU3RyaW5nIiBjb250ZXh0PSJQYXRpZW50IiBhY2Nlc3NMZXZlbD0iUHVibGljIiB4c2k6dHlwZT0iRnVuY3Rpb25EZWYiPgogICAgICAgICA8ZXhwcmVzc2lvbiBsb2NhdG9yPSIyOTA6NDYtMjkwOjU2IiBwYXRoPSJ2YWx1ZSIgeHNpOnR5cGU9IlByb3BlcnR5Ij4KICAgICAgICAgICAgPHNvdXJjZSBsb2NhdG9yPSIyOTA6NDYtMjkwOjUwIiBuYW1lPSJ2YWx1ZSIgeHNpOnR5cGU9Ik9wZXJhbmRSZWYiLz4KICAgICAgICAgPC9leHByZXNzaW9uPgogICAgICAgICA8b3BlcmFuZCBuYW1lPSJ2YWx1ZSI+CiAgICAgICAgICAgIDxvcGVyYW5kVHlwZVNwZWNpZmllciBsb2NhdG9yPSIyOTA6MzItMjkwOjQyIiBuYW1lPSJmaGlyOk1lZGlhU3RhdHVzIiB4c2k6dHlwZT0iTmFtZWRUeXBlU3BlY2lmaWVyIi8+CiAgICAgICAgIDwvb3BlcmFuZD4KICAgICAgPC9kZWY+CiAgICAgIDxkZWYgbG9jYXRvcj0iMjkxOjEtMjkxOjc1IiBuYW1lPSJUb1N0cmluZyIgY29udGV4dD0iUGF0aWVudCIgYWNjZXNzTGV2ZWw9IlB1YmxpYyIgeHNpOnR5cGU9IkZ1bmN0aW9uRGVmIj4KICAgICAgICAgPGV4cHJlc3Npb24gbG9jYXRvcj0iMjkxOjY1LTI5MTo3NSIgcGF0aD0idmFsdWUiIHhzaTp0eXBlPSJQcm9wZXJ0eSI+CiAgICAgICAgICAgIDxzb3VyY2UgbG9jYXRvcj0iMjkxOjY1LTI5MTo2OSIgbmFtZT0idmFsdWUiIHhzaTp0eXBlPSJPcGVyYW5kUmVmIi8+CiAgICAgICAgIDwvZXhwcmVzc2lvbj4KICAgICAgICAgPG9wZXJhbmQgbmFtZT0idmFsdWUiPgogICAgICAgICAgICA8b3BlcmFuZFR5cGVTcGVjaWZpZXIgbG9jYXRvcj0iMjkxOjMyLTI5MTo2MSIgbmFtZT0iZmhpcjpNZWRpY2F0aW9uQWRtaW5pc3RyYXRpb25TdGF0dXMiIHhzaTp0eXBlPSJOYW1lZFR5cGVTcGVjaWZpZXIiLz4KICAgICAgICAgPC9vcGVyYW5kPgogICAgICA8L2RlZj4KICAgICAgPGRlZiBsb2NhdG9yPSIyOTI6MS0yOTI6NjkiIG5hbWU9IlRvU3RyaW5nIiBjb250ZXh0PSJQYXRpZW50IiBhY2Nlc3NMZXZlbD0iUHVibGljIiB4c2k6dHlwZT0iRnVuY3Rpb25EZWYiPgogICAgICAgICA8ZXhwcmVzc2lvbiBsb2NhdG9yPSIyOTI6NTktMjkyOjY5IiBwYXRoPSJ2YWx1ZSIgeHNpOnR5cGU9IlByb3BlcnR5Ij4KICAgICAgICAgICAgPHNvdXJjZSBsb2NhdG9yPSIyOTI6NTktMjkyOjYzIiBuYW1lPSJ2YWx1ZSIgeHNpOnR5cGU9Ik9wZXJhbmRSZWYiLz4KICAgICAgICAgPC9leHByZXNzaW9uPgogICAgICAgICA8b3BlcmFuZCBuYW1lPSJ2YWx1ZSI+CiAgICAgICAgICAgIDxvcGVyYW5kVHlwZVNwZWNpZmllciBsb2NhdG9yPSIyOTI6MzItMjkyOjU1IiBuYW1lPSJmaGlyOk1lZGljYXRpb25EaXNwZW5zZVN0YXR1cyIgeHNpOnR5cGU9Ik5hbWVkVHlwZVNwZWNpZmllciIvPgogICAgICAgICA8L29wZXJhbmQ+CiAgICAgIDwvZGVmPgogICAgICA8ZGVmIGxvY2F0b3I9IjI5MzoxLTI5Mzo3MCIgbmFtZT0iVG9TdHJpbmciIGNvbnRleHQ9IlBhdGllbnQiIGFjY2Vzc0xldmVsPSJQdWJsaWMiIHhzaTp0eXBlPSJGdW5jdGlvbkRlZiI+CiAgICAgICAgIDxleHByZXNzaW9uIGxvY2F0b3I9IjI5Mzo2MC0yOTM6NzAiIHBhdGg9InZhbHVlIiB4c2k6dHlwZT0iUHJvcGVydHkiPgogICAgICAgICAgICA8c291cmNlIGxvY2F0b3I9IjI5Mzo2MC0yOTM6NjQiIG5hbWU9InZhbHVlIiB4c2k6dHlwZT0iT3BlcmFuZFJlZiIvPgogICAgICAgICA8L2V4cHJlc3Npb24+CiAgICAgICAgIDxvcGVyYW5kIG5hbWU9InZhbHVlIj4KICAgICAgICAgICAgPG9wZXJhbmRUeXBlU3BlY2lmaWVyIGxvY2F0b3I9IjI5MzozMi0yOTM6NTYiIG5hbWU9ImZoaXI6TWVkaWNhdGlvbktub3dsZWRnZVN0YXR1cyIgeHNpOnR5cGU9Ik5hbWVkVHlwZVNwZWNpZmllciIvPgogICAgICAgICA8L29wZXJhbmQ+CiAgICAgIDwvZGVmPgogICAgICA8ZGVmIGxvY2F0b3I9IjI5NDoxLTI5NDo2OCIgbmFtZT0iVG9TdHJpbmciIGNvbnRleHQ9IlBhdGllbnQiIGFjY2Vzc0xldmVsPSJQdWJsaWMiIHhzaTp0eXBlPSJGdW5jdGlvbkRlZiI+CiAgICAgICAgIDxleHByZXNzaW9uIGxvY2F0b3I9IjI5NDo1OC0yOTQ6NjgiIHBhdGg9InZhbHVlIiB4c2k6dHlwZT0iUHJvcGVydHkiPgogICAgICAgICAgICA8c291cmNlIGxvY2F0b3I9IjI5NDo1OC0yOTQ6NjIiIG5hbWU9InZhbHVlIiB4c2k6dHlwZT0iT3BlcmFuZFJlZiIvPgogICAgICAgICA8L2V4cHJlc3Npb24+CiAgICAgICAgIDxvcGVyYW5kIG5hbWU9InZhbHVlIj4KICAgICAgICAgICAgPG9wZXJhbmRUeXBlU3BlY2lmaWVyIGxvY2F0b3I9IjI5NDozMi0yOTQ6NTQiIG5hbWU9ImZoaXI6TWVkaWNhdGlvblJlcXVlc3RJbnRlbnQiIHhzaTp0eXBlPSJOYW1lZFR5cGVTcGVjaWZpZXIiLz4KICAgICAgICAgPC9vcGVyYW5kPgogICAgICA8L2RlZj4KICAgICAgPGRlZiBsb2NhdG9yPSIyOTU6MS0yOTU6NzAiIG5hbWU9IlRvU3RyaW5nIiBjb250ZXh0PSJQYXRpZW50IiBhY2Nlc3NMZXZlbD0iUHVibGljIiB4c2k6dHlwZT0iRnVuY3Rpb25EZWYiPgogICAgICAgICA8ZXhwcmVzc2lvbiBsb2NhdG9yPSIyOTU6NjAtMjk1OjcwIiBwYXRoPSJ2YWx1ZSIgeHNpOnR5cGU9IlByb3BlcnR5Ij4KICAgICAgICAgICAgPHNvdXJjZSBsb2NhdG9yPSIyOTU6NjAtMjk1OjY0IiBuYW1lPSJ2YWx1ZSIgeHNpOnR5cGU9Ik9wZXJhbmRSZWYiLz4KICAgICAgICAgPC9leHByZXNzaW9uPgogICAgICAgICA8b3BlcmFuZCBuYW1lPSJ2YWx1ZSI+CiAgICAgICAgICAgIDxvcGVyYW5kVHlwZVNwZWNpZmllciBsb2NhdG9yPSIyOTU6MzItMjk1OjU2IiBuYW1lPSJmaGlyOk1lZGljYXRpb25SZXF1ZXN0UHJpb3JpdHkiIHhzaTp0eXBlPSJOYW1lZFR5cGVTcGVjaWZpZXIiLz4KICAgICAgICAgPC9vcGVyYW5kPgogICAgICA8L2RlZj4KICAgICAgPGRlZiBsb2NhdG9yPSIyOTY6MS0yOTY6NjgiIG5hbWU9IlRvU3RyaW5nIiBjb250ZXh0PSJQYXRpZW50IiBhY2Nlc3NMZXZlbD0iUHVibGljIiB4c2k6dHlwZT0iRnVuY3Rpb25EZWYiPgogICAgICAgICA8ZXhwcmVzc2lvbiBsb2NhdG9yPSIyOTY6NTgtMjk2OjY4IiBwYXRoPSJ2YWx1ZSIgeHNpOnR5cGU9IlByb3BlcnR5Ij4KICAgICAgICAgICAgPHNvdXJjZSBsb2NhdG9yPSIyOTY6NTgtMjk2OjYyIiBuYW1lPSJ2YWx1ZSIgeHNpOnR5cGU9Ik9wZXJhbmRSZWYiLz4KICAgICAgICAgPC9leHByZXNzaW9uPgogICAgICAgICA8b3BlcmFuZCBuYW1lPSJ2YWx1ZSI+CiAgICAgICAgICAgIDxvcGVyYW5kVHlwZVNwZWNpZmllciBsb2NhdG9yPSIyOTY6MzItMjk2OjU0IiBuYW1lPSJmaGlyOk1lZGljYXRpb25SZXF1ZXN0U3RhdHVzIiB4c2k6dHlwZT0iTmFtZWRUeXBlU3BlY2lmaWVyIi8+CiAgICAgICAgIDwvb3BlcmFuZD4KICAgICAgPC9kZWY+CiAgICAgIDxkZWYgbG9jYXRvcj0iMjk3OjEtMjk3OjcwIiBuYW1lPSJUb1N0cmluZyIgY29udGV4dD0iUGF0aWVudCIgYWNjZXNzTGV2ZWw9IlB1YmxpYyIgeHNpOnR5cGU9IkZ1bmN0aW9uRGVmIj4KICAgICAgICAgPGV4cHJlc3Npb24gbG9jYXRvcj0iMjk3OjYwLTI5Nzo3MCIgcGF0aD0idmFsdWUiIHhzaTp0eXBlPSJQcm9wZXJ0eSI+CiAgICAgICAgICAgIDxzb3VyY2UgbG9jYXRvcj0iMjk3OjYwLTI5Nzo2NCIgbmFtZT0idmFsdWUiIHhzaTp0eXBlPSJPcGVyYW5kUmVmIi8+CiAgICAgICAgIDwvZXhwcmVzc2lvbj4KICAgICAgICAgPG9wZXJhbmQgbmFtZT0idmFsdWUiPgogICAgICAgICAgICA8b3BlcmFuZFR5cGVTcGVjaWZpZXIgbG9jYXRvcj0iMjk3OjMyLTI5Nzo1NiIgbmFtZT0iZmhpcjpNZWRpY2F0aW9uU3RhdGVtZW50U3RhdHVzIiB4c2k6dHlwZT0iTmFtZWRUeXBlU3BlY2lmaWVyIi8+CiAgICAgICAgIDwvb3BlcmFuZD4KICAgICAgPC9kZWY+CiAgICAgIDxkZWYgbG9jYXRvcj0iMjk4OjEtMjk4OjYxIiBuYW1lPSJUb1N0cmluZyIgY29udGV4dD0iUGF0aWVudCIgYWNjZXNzTGV2ZWw9IlB1YmxpYyIgeHNpOnR5cGU9IkZ1bmN0aW9uRGVmIj4KICAgICAgICAgPGV4cHJlc3Npb24gbG9jYXRvcj0iMjk4OjUxLTI5ODo2MSIgcGF0aD0idmFsdWUiIHhzaTp0eXBlPSJQcm9wZXJ0eSI+CiAgICAgICAgICAgIDxzb3VyY2UgbG9jYXRvcj0iMjk4OjUxLTI5ODo1NSIgbmFtZT0idmFsdWUiIHhzaTp0eXBlPSJPcGVyYW5kUmVmIi8+CiAgICAgICAgIDwvZXhwcmVzc2lvbj4KICAgICAgICAgPG9wZXJhbmQgbmFtZT0idmFsdWUiPgogICAgICAgICAgICA8b3BlcmFuZFR5cGVTcGVjaWZpZXIgbG9jYXRvcj0iMjk4OjMyLTI5ODo0NyIgbmFtZT0iZmhpcjpNZWRpY2F0aW9uU3RhdHVzIiB4c2k6dHlwZT0iTmFtZWRUeXBlU3BlY2lmaWVyIi8+CiAgICAgICAgIDwvb3BlcmFuZD4KICAgICAgPC9kZWY+CiAgICAgIDxkZWYgbG9jYXRvcj0iMjk5OjEtMjk5OjcyIiBuYW1lPSJUb1N0cmluZyIgY29udGV4dD0iUGF0aWVudCIgYWNjZXNzTGV2ZWw9IlB1YmxpYyIgeHNpOnR5cGU9IkZ1bmN0aW9uRGVmIj4KICAgICAgICAgPGV4cHJlc3Npb24gbG9jYXRvcj0iMjk5OjYyLTI5OTo3MiIgcGF0aD0idmFsdWUiIHhzaTp0eXBlPSJQcm9wZXJ0eSI+CiAgICAgICAgICAgIDxzb3VyY2UgbG9jYXRvcj0iMjk5OjYyLTI5OTo2NiIgbmFtZT0idmFsdWUiIHhzaTp0eXBlPSJPcGVyYW5kUmVmIi8+CiAgICAgICAgIDwvZXhwcmVzc2lvbj4KICAgICAgICAgPG9wZXJhbmQgbmFtZT0idmFsdWUiPgogICAgICAgICAgICA8b3BlcmFuZFR5cGVTcGVjaWZpZXIgbG9jYXRvcj0iMjk5OjMyLTI5OTo1OCIgbmFtZT0iZmhpcjpNZXNzYWdlU2lnbmlmaWNhbmNlQ2F0ZWdvcnkiIHhzaTp0eXBlPSJOYW1lZFR5cGVTcGVjaWZpZXIiLz4KICAgICAgICAgPC9vcGVyYW5kPgogICAgICA8L2RlZj4KICAgICAgPGRlZiBsb2NhdG9yPSIzMDA6MS0zMDA6NzUiIG5hbWU9IlRvU3RyaW5nIiBjb250ZXh0PSJQYXRpZW50IiBhY2Nlc3NMZXZlbD0iUHVibGljIiB4c2k6dHlwZT0iRnVuY3Rpb25EZWYiPgogICAgICAgICA8ZXhwcmVzc2lvbiBsb2NhdG9yPSIzMDA6NjUtMzAwOjc1IiBwYXRoPSJ2YWx1ZSIgeHNpOnR5cGU9IlByb3BlcnR5Ij4KICAgICAgICAgICAgPHNvdXJjZSBsb2NhdG9yPSIzMDA6NjUtMzAwOjY5IiBuYW1lPSJ2YWx1ZSIgeHNpOnR5cGU9Ik9wZXJhbmRSZWYiLz4KICAgICAgICAgPC9leHByZXNzaW9uPgogICAgICAgICA8b3BlcmFuZCBuYW1lPSJ2YWx1ZSI+CiAgICAgICAgICAgIDxvcGVyYW5kVHlwZVNwZWNpZmllciBsb2NhdG9yPSIzMDA6MzItMzAwOjYxIiBuYW1lPSJmaGlyOk1lc3NhZ2VoZWFkZXJfUmVzcG9uc2VfUmVxdWVzdCIgeHNpOnR5cGU9Ik5hbWVkVHlwZVNwZWNpZmllciIvPgogICAgICAgICA8L29wZXJhbmQ+CiAgICAgIDwvZGVmPgogICAgICA8ZGVmIGxvY2F0b3I9IjMwMToxLTMwMTo1MyIgbmFtZT0iVG9TdHJpbmciIGNvbnRleHQ9IlBhdGllbnQiIGFjY2Vzc0xldmVsPSJQdWJsaWMiIHhzaTp0eXBlPSJGdW5jdGlvbkRlZiI+CiAgICAgICAgIDxleHByZXNzaW9uIGxvY2F0b3I9IjMwMTo0My0zMDE6NTMiIHBhdGg9InZhbHVlIiB4c2k6dHlwZT0iUHJvcGVydHkiPgogICAgICAgICAgICA8c291cmNlIGxvY2F0b3I9IjMwMTo0My0zMDE6NDciIG5hbWU9InZhbHVlIiB4c2k6dHlwZT0iT3BlcmFuZFJlZiIvPgogICAgICAgICA8L2V4cHJlc3Npb24+CiAgICAgICAgIDxvcGVyYW5kIG5hbWU9InZhbHVlIj4KICAgICAgICAgICAgPG9wZXJhbmRUeXBlU3BlY2lmaWVyIGxvY2F0b3I9IjMwMTozMi0zMDE6MzkiIG5hbWU9ImZoaXI6TWltZVR5cGUiIHhzaTp0eXBlPSJOYW1lZFR5cGVTcGVjaWZpZXIiLz4KICAgICAgICAgPC9vcGVyYW5kPgogICAgICA8L2RlZj4KICAgICAgPGRlZiBsb2NhdG9yPSIzMDI6MS0zMDI6NTIiIG5hbWU9IlRvU3RyaW5nIiBjb250ZXh0PSJQYXRpZW50IiBhY2Nlc3NMZXZlbD0iUHVibGljIiB4c2k6dHlwZT0iRnVuY3Rpb25EZWYiPgogICAgICAgICA8ZXhwcmVzc2lvbiBsb2NhdG9yPSIzMDI6NDItMzAyOjUyIiBwYXRoPSJ2YWx1ZSIgeHNpOnR5cGU9IlByb3BlcnR5Ij4KICAgICAgICAgICAgPHNvdXJjZSBsb2NhdG9yPSIzMDI6NDItMzAyOjQ2IiBuYW1lPSJ2YWx1ZSIgeHNpOnR5cGU9Ik9wZXJhbmRSZWYiLz4KICAgICAgICAgPC9leHByZXNzaW9uPgogICAgICAgICA8b3BlcmFuZCBuYW1lPSJ2YWx1ZSI+CiAgICAgICAgICAgIDxvcGVyYW5kVHlwZVNwZWNpZmllciBsb2NhdG9yPSIzMDI6MzItMzAyOjM4IiBuYW1lPSJmaGlyOk5hbWVVc2UiIHhzaTp0eXBlPSJOYW1lZFR5cGVTcGVjaWZpZXIiLz4KICAgICAgICAgPC9vcGVyYW5kPgogICAgICA8L2RlZj4KICAgICAgPGRlZiBsb2NhdG9yPSIzMDM6MS0zMDM6NzEiIG5hbWU9IlRvU3RyaW5nIiBjb250ZXh0PSJQYXRpZW50IiBhY2Nlc3NMZXZlbD0iUHVibGljIiB4c2k6dHlwZT0iRnVuY3Rpb25EZWYiPgogICAgICAgICA8ZXhwcmVzc2lvbiBsb2NhdG9yPSIzMDM6NjEtMzAzOjcxIiBwYXRoPSJ2YWx1ZSIgeHNpOnR5cGU9IlByb3BlcnR5Ij4KICAgICAgICAgICAgPHNvdXJjZSBsb2NhdG9yPSIzMDM6NjEtMzAzOjY1IiBuYW1lPSJ2YWx1ZSIgeHNpOnR5cGU9Ik9wZXJhbmRSZWYiLz4KICAgICAgICAgPC9leHByZXNzaW9uPgogICAgICAgICA8b3BlcmFuZCBuYW1lPSJ2YWx1ZSI+CiAgICAgICAgICAgIDxvcGVyYW5kVHlwZVNwZWNpZmllciBsb2NhdG9yPSIzMDM6MzItMzAzOjU3IiBuYW1lPSJmaGlyOk5hbWluZ1N5c3RlbUlkZW50aWZpZXJUeXBlIiB4c2k6dHlwZT0iTmFtZWRUeXBlU3BlY2lmaWVyIi8+CiAgICAgICAgIDwvb3BlcmFuZD4KICAgICAgPC9kZWY+CiAgICAgIDxkZWYgbG9jYXRvcj0iMzA0OjEtMzA0OjYxIiBuYW1lPSJUb1N0cmluZyIgY29udGV4dD0iUGF0aWVudCIgYWNjZXNzTGV2ZWw9IlB1YmxpYyIgeHNpOnR5cGU9IkZ1bmN0aW9uRGVmIj4KICAgICAgICAgPGV4cHJlc3Npb24gbG9jYXRvcj0iMzA0OjUxLTMwNDo2MSIgcGF0aD0idmFsdWUiIHhzaTp0eXBlPSJQcm9wZXJ0eSI+CiAgICAgICAgICAgIDxzb3VyY2UgbG9jYXRvcj0iMzA0OjUxLTMwNDo1NSIgbmFtZT0idmFsdWUiIHhzaTp0eXBlPSJPcGVyYW5kUmVmIi8+CiAgICAgICAgIDwvZXhwcmVzc2lvbj4KICAgICAgICAgPG9wZXJhbmQgbmFtZT0idmFsdWUiPgogICAgICAgICAgICA8b3BlcmFuZFR5cGVTcGVjaWZpZXIgbG9jYXRvcj0iMzA0OjMyLTMwNDo0NyIgbmFtZT0iZmhpcjpOYW1pbmdTeXN0ZW1UeXBlIiB4c2k6dHlwZT0iTmFtZWRUeXBlU3BlY2lmaWVyIi8+CiAgICAgICAgIDwvb3BlcmFuZD4KICAgICAgPC9kZWY+CiAgICAgIDxkZWYgbG9jYXRvcj0iMzA1OjEtMzA1OjYwIiBuYW1lPSJUb1N0cmluZyIgY29udGV4dD0iUGF0aWVudCIgYWNjZXNzTGV2ZWw9IlB1YmxpYyIgeHNpOnR5cGU9IkZ1bmN0aW9uRGVmIj4KICAgICAgICAgPGV4cHJlc3Npb24gbG9jYXRvcj0iMzA1OjUwLTMwNTo2MCIgcGF0aD0idmFsdWUiIHhzaTp0eXBlPSJQcm9wZXJ0eSI+CiAgICAgICAgICAgIDxzb3VyY2UgbG9jYXRvcj0iMzA1OjUwLTMwNTo1NCIgbmFtZT0idmFsdWUiIHhzaTp0eXBlPSJPcGVyYW5kUmVmIi8+CiAgICAgICAgIDwvZXhwcmVzc2lvbj4KICAgICAgICAgPG9wZXJhbmQgbmFtZT0idmFsdWUiPgogICAgICAgICAgICA8b3BlcmFuZFR5cGVTcGVjaWZpZXIgbG9jYXRvcj0iMzA1OjMyLTMwNTo0NiIgbmFtZT0iZmhpcjpOYXJyYXRpdmVTdGF0dXMiIHhzaTp0eXBlPSJOYW1lZFR5cGVTcGVjaWZpZXIiLz4KICAgICAgICAgPC9vcGVyYW5kPgogICAgICA8L2RlZj4KICAgICAgPGRlZiBsb2NhdG9yPSIzMDY6MS0zMDY6NTMiIG5hbWU9IlRvU3RyaW5nIiBjb250ZXh0PSJQYXRpZW50IiBhY2Nlc3NMZXZlbD0iUHVibGljIiB4c2k6dHlwZT0iRnVuY3Rpb25EZWYiPgogICAgICAgICA8ZXhwcmVzc2lvbiBsb2NhdG9yPSIzMDY6NDMtMzA2OjUzIiBwYXRoPSJ2YWx1ZSIgeHNpOnR5cGU9IlByb3BlcnR5Ij4KICAgICAgICAgICAgPHNvdXJjZSBsb2NhdG9yPSIzMDY6NDMtMzA2OjQ3IiBuYW1lPSJ2YWx1ZSIgeHNpOnR5cGU9Ik9wZXJhbmRSZWYiLz4KICAgICAgICAgPC9leHByZXNzaW9uPgogICAgICAgICA8b3BlcmFuZCBuYW1lPSJ2YWx1ZSI+CiAgICAgICAgICAgIDxvcGVyYW5kVHlwZVNwZWNpZmllciBsb2NhdG9yPSIzMDY6MzItMzA2OjM5IiBuYW1lPSJmaGlyOk5vdGVUeXBlIiB4c2k6dHlwZT0iTmFtZWRUeXBlU3BlY2lmaWVyIi8+CiAgICAgICAgIDwvb3BlcmFuZD4KICAgICAgPC9kZWY+CiAgICAgIDxkZWYgbG9jYXRvcj0iMzA3OjEtMzA3OjY2IiBuYW1lPSJUb1N0cmluZyIgY29udGV4dD0iUGF0aWVudCIgYWNjZXNzTGV2ZWw9IlB1YmxpYyIgeHNpOnR5cGU9IkZ1bmN0aW9uRGVmIj4KICAgICAgICAgPGV4cHJlc3Npb24gbG9jYXRvcj0iMzA3OjU2LTMwNzo2NiIgcGF0aD0idmFsdWUiIHhzaTp0eXBlPSJQcm9wZXJ0eSI+CiAgICAgICAgICAgIDxzb3VyY2UgbG9jYXRvcj0iMzA3OjU2LTMwNzo2MCIgbmFtZT0idmFsdWUiIHhzaTp0eXBlPSJPcGVyYW5kUmVmIi8+CiAgICAgICAgIDwvZXhwcmVzc2lvbj4KICAgICAgICAgPG9wZXJhbmQgbmFtZT0idmFsdWUiPgogICAgICAgICAgICA8b3BlcmFuZFR5cGVTcGVjaWZpZXIgbG9jYXRvcj0iMzA3OjMyLTMwNzo1MiIgbmFtZT0iZmhpcjpOdXRyaXRpaW9uT3JkZXJJbnRlbnQiIHhzaTp0eXBlPSJOYW1lZFR5cGVTcGVjaWZpZXIiLz4KICAgICAgICAgPC9vcGVyYW5kPgogICAgICA8L2RlZj4KICAgICAgPGRlZiBsb2NhdG9yPSIzMDg6MS0zMDg6NjUiIG5hbWU9IlRvU3RyaW5nIiBjb250ZXh0PSJQYXRpZW50IiBhY2Nlc3NMZXZlbD0iUHVibGljIiB4c2k6dHlwZT0iRnVuY3Rpb25EZWYiPgogICAgICAgICA8ZXhwcmVzc2lvbiBsb2NhdG9yPSIzMDg6NTUtMzA4OjY1IiBwYXRoPSJ2YWx1ZSIgeHNpOnR5cGU9IlByb3BlcnR5Ij4KICAgICAgICAgICAgPHNvdXJjZSBsb2NhdG9yPSIzMDg6NTUtMzA4OjU5IiBuYW1lPSJ2YWx1ZSIgeHNpOnR5cGU9Ik9wZXJhbmRSZWYiLz4KICAgICAgICAgPC9leHByZXNzaW9uPgogICAgICAgICA8b3BlcmFuZCBuYW1lPSJ2YWx1ZSI+CiAgICAgICAgICAgIDxvcGVyYW5kVHlwZVNwZWNpZmllciBsb2NhdG9yPSIzMDg6MzItMzA4OjUxIiBuYW1lPSJmaGlyOk51dHJpdGlvbk9yZGVyU3RhdHVzIiB4c2k6dHlwZT0iTmFtZWRUeXBlU3BlY2lmaWVyIi8+CiAgICAgICAgIDwvb3BlcmFuZD4KICAgICAgPC9kZWY+CiAgICAgIDxkZWYgbG9jYXRvcj0iMzA5OjEtMzA5OjY0IiBuYW1lPSJUb1N0cmluZyIgY29udGV4dD0iUGF0aWVudCIgYWNjZXNzTGV2ZWw9IlB1YmxpYyIgeHNpOnR5cGU9IkZ1bmN0aW9uRGVmIj4KICAgICAgICAgPGV4cHJlc3Npb24gbG9jYXRvcj0iMzA5OjU0LTMwOTo2NCIgcGF0aD0idmFsdWUiIHhzaTp0eXBlPSJQcm9wZXJ0eSI+CiAgICAgICAgICAgIDxzb3VyY2UgbG9jYXRvcj0iMzA5OjU0LTMwOTo1OCIgbmFtZT0idmFsdWUiIHhzaTp0eXBlPSJPcGVyYW5kUmVmIi8+CiAgICAgICAgIDwvZXhwcmVzc2lvbj4KICAgICAgICAgPG9wZXJhbmQgbmFtZT0idmFsdWUiPgogICAgICAgICAgICA8b3BlcmFuZFR5cGVTcGVjaWZpZXIgbG9jYXRvcj0iMzA5OjMyLTMwOTo1MCIgbmFtZT0iZmhpcjpPYnNlcnZhdGlvbkRhdGFUeXBlIiB4c2k6dHlwZT0iTmFtZWRUeXBlU3BlY2lmaWVyIi8+CiAgICAgICAgIDwvb3BlcmFuZD4KICAgICAgPC9kZWY+CiAgICAgIDxkZWYgbG9jYXRvcj0iMzEwOjEtMzEwOjY5IiBuYW1lPSJUb1N0cmluZyIgY29udGV4dD0iUGF0aWVudCIgYWNjZXNzTGV2ZWw9IlB1YmxpYyIgeHNpOnR5cGU9IkZ1bmN0aW9uRGVmIj4KICAgICAgICAgPGV4cHJlc3Npb24gbG9jYXRvcj0iMzEwOjU5LTMxMDo2OSIgcGF0aD0idmFsdWUiIHhzaTp0eXBlPSJQcm9wZXJ0eSI+CiAgICAgICAgICAgIDxzb3VyY2UgbG9jYXRvcj0iMzEwOjU5LTMxMDo2MyIgbmFtZT0idmFsdWUiIHhzaTp0eXBlPSJPcGVyYW5kUmVmIi8+CiAgICAgICAgIDwvZXhwcmVzc2lvbj4KICAgICAgICAgPG9wZXJhbmQgbmFtZT0idmFsdWUiPgogICAgICAgICAgICA8b3BlcmFuZFR5cGVTcGVjaWZpZXIgbG9jYXRvcj0iMzEwOjMyLTMxMDo1NSIgbmFtZT0iZmhpcjpPYnNlcnZhdGlvblJhbmdlQ2F0ZWdvcnkiIHhzaTp0eXBlPSJOYW1lZFR5cGVTcGVjaWZpZXIiLz4KICAgICAgICAgPC9vcGVyYW5kPgogICAgICA8L2RlZj4KICAgICAgPGRlZiBsb2NhdG9yPSIzMTE6MS0zMTE6NjIiIG5hbWU9IlRvU3RyaW5nIiBjb250ZXh0PSJQYXRpZW50IiBhY2Nlc3NMZXZlbD0iUHVibGljIiB4c2k6dHlwZT0iRnVuY3Rpb25EZWYiPgogICAgICAgICA8ZXhwcmVzc2lvbiBsb2NhdG9yPSIzMTE6NTItMzExOjYyIiBwYXRoPSJ2YWx1ZSIgeHNpOnR5cGU9IlByb3BlcnR5Ij4KICAgICAgICAgICAgPHNvdXJjZSBsb2NhdG9yPSIzMTE6NTItMzExOjU2IiBuYW1lPSJ2YWx1ZSIgeHNpOnR5cGU9Ik9wZXJhbmRSZWYiLz4KICAgICAgICAgPC9leHByZXNzaW9uPgogICAgICAgICA8b3BlcmFuZCBuYW1lPSJ2YWx1ZSI+CiAgICAgICAgICAgIDxvcGVyYW5kVHlwZVNwZWNpZmllciBsb2NhdG9yPSIzMTE6MzItMzExOjQ4IiBuYW1lPSJmaGlyOk9ic2VydmF0aW9uU3RhdHVzIiB4c2k6dHlwZT0iTmFtZWRUeXBlU3BlY2lmaWVyIi8+CiAgICAgICAgIDwvb3BlcmFuZD4KICAgICAgPC9kZWY+CiAgICAgIDxkZWYgbG9jYXRvcj0iMzEyOjEtMzEyOjU4IiBuYW1lPSJUb1N0cmluZyIgY29udGV4dD0iUGF0aWVudCIgYWNjZXNzTGV2ZWw9IlB1YmxpYyIgeHNpOnR5cGU9IkZ1bmN0aW9uRGVmIj4KICAgICAgICAgPGV4cHJlc3Npb24gbG9jYXRvcj0iMzEyOjQ4LTMxMjo1OCIgcGF0aD0idmFsdWUiIHhzaTp0eXBlPSJQcm9wZXJ0eSI+CiAgICAgICAgICAgIDxzb3VyY2UgbG9jYXRvcj0iMzEyOjQ4LTMxMjo1MiIgbmFtZT0idmFsdWUiIHhzaTp0eXBlPSJPcGVyYW5kUmVmIi8+CiAgICAgICAgIDwvZXhwcmVzc2lvbj4KICAgICAgICAgPG9wZXJhbmQgbmFtZT0idmFsdWUiPgogICAgICAgICAgICA8b3BlcmFuZFR5cGVTcGVjaWZpZXIgbG9jYXRvcj0iMzEyOjMyLTMxMjo0NCIgbmFtZT0iZmhpcjpPcGVyYXRpb25LaW5kIiB4c2k6dHlwZT0iTmFtZWRUeXBlU3BlY2lmaWVyIi8+CiAgICAgICAgIDwvb3BlcmFuZD4KICAgICAgPC9kZWY+CiAgICAgIDxkZWYgbG9jYXRvcj0iMzEzOjEtMzEzOjY2IiBuYW1lPSJUb1N0cmluZyIgY29udGV4dD0iUGF0aWVudCIgYWNjZXNzTGV2ZWw9IlB1YmxpYyIgeHNpOnR5cGU9IkZ1bmN0aW9uRGVmIj4KICAgICAgICAgPGV4cHJlc3Npb24gbG9jYXRvcj0iMzEzOjU2LTMxMzo2NiIgcGF0aD0idmFsdWUiIHhzaTp0eXBlPSJQcm9wZXJ0eSI+CiAgICAgICAgICAgIDxzb3VyY2UgbG9jYXRvcj0iMzEzOjU2LTMxMzo2MCIgbmFtZT0idmFsdWUiIHhzaTp0eXBlPSJPcGVyYW5kUmVmIi8+CiAgICAgICAgIDwvZXhwcmVzc2lvbj4KICAgICAgICAgPG9wZXJhbmQgbmFtZT0idmFsdWUiPgogICAgICAgICAgICA8b3BlcmFuZFR5cGVTcGVjaWZpZXIgbG9jYXRvcj0iMzEzOjMyLTMxMzo1MiIgbmFtZT0iZmhpcjpPcGVyYXRpb25QYXJhbWV0ZXJVc2UiIHhzaTp0eXBlPSJOYW1lZFR5cGVTcGVjaWZpZXIiLz4KICAgICAgICAgPC9vcGVyYW5kPgogICAgICA8L2RlZj4KICAgICAgPGRlZiBsb2NhdG9yPSIzMTQ6MS0zMTQ6NjAiIG5hbWU9IlRvU3RyaW5nIiBjb250ZXh0PSJQYXRpZW50IiBhY2Nlc3NMZXZlbD0iUHVibGljIiB4c2k6dHlwZT0iRnVuY3Rpb25EZWYiPgogICAgICAgICA8ZXhwcmVzc2lvbiBsb2NhdG9yPSIzMTQ6NTAtMzE0OjYwIiBwYXRoPSJ2YWx1ZSIgeHNpOnR5cGU9IlByb3BlcnR5Ij4KICAgICAgICAgICAgPHNvdXJjZSBsb2NhdG9yPSIzMTQ6NTAtMzE0OjU0IiBuYW1lPSJ2YWx1ZSIgeHNpOnR5cGU9Ik9wZXJhbmRSZWYiLz4KICAgICAgICAgPC9leHByZXNzaW9uPgogICAgICAgICA8b3BlcmFuZCBuYW1lPSJ2YWx1ZSI+CiAgICAgICAgICAgIDxvcGVyYW5kVHlwZVNwZWNpZmllciBsb2NhdG9yPSIzMTQ6MzItMzE0OjQ2IiBuYW1lPSJmaGlyOk9yaWVudGF0aW9uVHlwZSIgeHNpOnR5cGU9Ik5hbWVkVHlwZVNwZWNpZmllciIvPgogICAgICAgICA8L29wZXJhbmQ+CiAgICAgIDwvZGVmPgogICAgICA8ZGVmIGxvY2F0b3I9IjMxNToxLTMxNTo1NyIgbmFtZT0iVG9TdHJpbmciIGNvbnRleHQ9IlBhdGllbnQiIGFjY2Vzc0xldmVsPSJQdWJsaWMiIHhzaTp0eXBlPSJGdW5jdGlvbkRlZiI+CiAgICAgICAgIDxleHByZXNzaW9uIGxvY2F0b3I9IjMxNTo0Ny0zMTU6NTciIHBhdGg9InZhbHVlIiB4c2k6dHlwZT0iUHJvcGVydHkiPgogICAgICAgICAgICA8c291cmNlIGxvY2F0b3I9IjMxNTo0Ny0zMTU6NTEiIG5hbWU9InZhbHVlIiB4c2k6dHlwZT0iT3BlcmFuZFJlZiIvPgogICAgICAgICA8L2V4cHJlc3Npb24+CiAgICAgICAgIDxvcGVyYW5kIG5hbWU9InZhbHVlIj4KICAgICAgICAgICAgPG9wZXJhbmRUeXBlU3BlY2lmaWVyIGxvY2F0b3I9IjMxNTozMi0zMTU6NDMiIG5hbWU9ImZoaXI6UGFyYW1ldGVyVXNlIiB4c2k6dHlwZT0iTmFtZWRUeXBlU3BlY2lmaWVyIi8+CiAgICAgICAgIDwvb3BlcmFuZD4KICAgICAgPC9kZWY+CiAgICAgIDxkZWYgbG9jYXRvcj0iMzE2OjEtMzE2OjY0IiBuYW1lPSJUb1N0cmluZyIgY29udGV4dD0iUGF0aWVudCIgYWNjZXNzTGV2ZWw9IlB1YmxpYyIgeHNpOnR5cGU9IkZ1bmN0aW9uRGVmIj4KICAgICAgICAgPGV4cHJlc3Npb24gbG9jYXRvcj0iMzE2OjU0LTMxNjo2NCIgcGF0aD0idmFsdWUiIHhzaTp0eXBlPSJQcm9wZXJ0eSI+CiAgICAgICAgICAgIDxzb3VyY2UgbG9jYXRvcj0iMzE2OjU0LTMxNjo1OCIgbmFtZT0idmFsdWUiIHhzaTp0eXBlPSJPcGVyYW5kUmVmIi8+CiAgICAgICAgIDwvZXhwcmVzc2lvbj4KICAgICAgICAgPG9wZXJhbmQgbmFtZT0idmFsdWUiPgogICAgICAgICAgICA8b3BlcmFuZFR5cGVTcGVjaWZpZXIgbG9jYXRvcj0iMzE2OjMyLTMxNjo1MCIgbmFtZT0iZmhpcjpQYXJ0aWNpcGFudFJlcXVpcmVkIiB4c2k6dHlwZT0iTmFtZWRUeXBlU3BlY2lmaWVyIi8+CiAgICAgICAgIDwvb3BlcmFuZD4KICAgICAgPC9kZWY+CiAgICAgIDxkZWYgbG9jYXRvcj0iMzE3OjEtMzE3OjYyIiBuYW1lPSJUb1N0cmluZyIgY29udGV4dD0iUGF0aWVudCIgYWNjZXNzTGV2ZWw9IlB1YmxpYyIgeHNpOnR5cGU9IkZ1bmN0aW9uRGVmIj4KICAgICAgICAgPGV4cHJlc3Npb24gbG9jYXRvcj0iMzE3OjUyLTMxNzo2MiIgcGF0aD0idmFsdWUiIHhzaTp0eXBlPSJQcm9wZXJ0eSI+CiAgICAgICAgICAgIDxzb3VyY2UgbG9jYXRvcj0iMzE3OjUyLTMxNzo1NiIgbmFtZT0idmFsdWUiIHhzaTp0eXBlPSJPcGVyYW5kUmVmIi8+CiAgICAgICAgIDwvZXhwcmVzc2lvbj4KICAgICAgICAgPG9wZXJhbmQgbmFtZT0idmFsdWUiPgogICAgICAgICAgICA8b3BlcmFuZFR5cGVTcGVjaWZpZXIgbG9jYXRvcj0iMzE3OjMyLTMxNzo0OCIgbmFtZT0iZmhpcjpQYXJ0aWNpcGFudFN0YXR1cyIgeHNpOnR5cGU9Ik5hbWVkVHlwZVNwZWNpZmllciIvPgogICAgICAgICA8L29wZXJhbmQ+CiAgICAgIDwvZGVmPgogICAgICA8ZGVmIGxvY2F0b3I9IjMxODoxLTMxODo2NCIgbmFtZT0iVG9TdHJpbmciIGNvbnRleHQ9IlBhdGllbnQiIGFjY2Vzc0xldmVsPSJQdWJsaWMiIHhzaTp0eXBlPSJGdW5jdGlvbkRlZiI+CiAgICAgICAgIDxleHByZXNzaW9uIGxvY2F0b3I9IjMxODo1NC0zMTg6NjQiIHBhdGg9InZhbHVlIiB4c2k6dHlwZT0iUHJvcGVydHkiPgogICAgICAgICAgICA8c291cmNlIGxvY2F0b3I9IjMxODo1NC0zMTg6NTgiIG5hbWU9InZhbHVlIiB4c2k6dHlwZT0iT3BlcmFuZFJlZiIvPgogICAgICAgICA8L2V4cHJlc3Npb24+CiAgICAgICAgIDxvcGVyYW5kIG5hbWU9InZhbHVlIj4KICAgICAgICAgICAgPG9wZXJhbmRUeXBlU3BlY2lmaWVyIGxvY2F0b3I9IjMxODozMi0zMTg6NTAiIG5hbWU9ImZoaXI6UGFydGljaXBhdGlvblN0YXR1cyIgeHNpOnR5cGU9Ik5hbWVkVHlwZVNwZWNpZmllciIvPgogICAgICAgICA8L29wZXJhbmQ+CiAgICAgIDwvZGVmPgogICAgICA8ZGVmIGxvY2F0b3I9IjMxOToxLTMxOTo2NCIgbmFtZT0iVG9TdHJpbmciIGNvbnRleHQ9IlBhdGllbnQiIGFjY2Vzc0xldmVsPSJQdWJsaWMiIHhzaTp0eXBlPSJGdW5jdGlvbkRlZiI+CiAgICAgICAgIDxleHByZXNzaW9uIGxvY2F0b3I9IjMxOTo1NC0zMTk6NjQiIHBhdGg9InZhbHVlIiB4c2k6dHlwZT0iUHJvcGVydHkiPgogICAgICAgICAgICA8c291cmNlIGxvY2F0b3I9IjMxOTo1NC0zMTk6NTgiIG5hbWU9InZhbHVlIiB4c2k6dHlwZT0iT3BlcmFuZFJlZiIvPgogICAgICAgICA8L2V4cHJlc3Npb24+CiAgICAgICAgIDxvcGVyYW5kIG5hbWU9InZhbHVlIj4KICAgICAgICAgICAgPG9wZXJhbmRUeXBlU3BlY2lmaWVyIGxvY2F0b3I9IjMxOTozMi0zMTk6NTAiIG5hbWU9ImZoaXI6UGF5bWVudE5vdGljZVN0YXR1cyIgeHNpOnR5cGU9Ik5hbWVkVHlwZVNwZWNpZmllciIvPgogICAgICAgICA8L29wZXJhbmQ+CiAgICAgIDwvZGVmPgogICAgICA8ZGVmIGxvY2F0b3I9IjMyMDoxLTMyMDo3MiIgbmFtZT0iVG9TdHJpbmciIGNvbnRleHQ9IlBhdGllbnQiIGFjY2Vzc0xldmVsPSJQdWJsaWMiIHhzaTp0eXBlPSJGdW5jdGlvbkRlZiI+CiAgICAgICAgIDxleHByZXNzaW9uIGxvY2F0b3I9IjMyMDo2Mi0zMjA6NzIiIHBhdGg9InZhbHVlIiB4c2k6dHlwZT0iUHJvcGVydHkiPgogICAgICAgICAgICA8c291cmNlIGxvY2F0b3I9IjMyMDo2Mi0zMjA6NjYiIG5hbWU9InZhbHVlIiB4c2k6dHlwZT0iT3BlcmFuZFJlZiIvPgogICAgICAgICA8L2V4cHJlc3Npb24+CiAgICAgICAgIDxvcGVyYW5kIG5hbWU9InZhbHVlIj4KICAgICAgICAgICAgPG9wZXJhbmRUeXBlU3BlY2lmaWVyIGxvY2F0b3I9IjMyMDozMi0zMjA6NTgiIG5hbWU9ImZoaXI6UGF5bWVudFJlY29uY2lsaWF0aW9uU3RhdHVzIiB4c2k6dHlwZT0iTmFtZWRUeXBlU3BlY2lmaWVyIi8+CiAgICAgICAgIDwvb3BlcmFuZD4KICAgICAgPC9kZWY+CiAgICAgIDxkZWYgbG9jYXRvcj0iMzIxOjEtMzIxOjYwIiBuYW1lPSJUb1N0cmluZyIgY29udGV4dD0iUGF0aWVudCIgYWNjZXNzTGV2ZWw9IlB1YmxpYyIgeHNpOnR5cGU9IkZ1bmN0aW9uRGVmIj4KICAgICAgICAgPGV4cHJlc3Npb24gbG9jYXRvcj0iMzIxOjUwLTMyMTo2MCIgcGF0aD0idmFsdWUiIHhzaTp0eXBlPSJQcm9wZXJ0eSI+CiAgICAgICAgICAgIDxzb3VyY2UgbG9jYXRvcj0iMzIxOjUwLTMyMTo1NCIgbmFtZT0idmFsdWUiIHhzaTp0eXBlPSJPcGVyYW5kUmVmIi8+CiAgICAgICAgIDwvZXhwcmVzc2lvbj4KICAgICAgICAgPG9wZXJhbmQgbmFtZT0idmFsdWUiPgogICAgICAgICAgICA8b3BlcmFuZFR5cGVTcGVjaWZpZXIgbG9jYXRvcj0iMzIxOjMyLTMyMTo0NiIgbmFtZT0iZmhpcjpQcm9jZWR1cmVTdGF0dXMiIHhzaTp0eXBlPSJOYW1lZFR5cGVTcGVjaWZpZXIiLz4KICAgICAgICAgPC9vcGVyYW5kPgogICAgICA8L2RlZj4KICAgICAgPGRlZiBsb2NhdG9yPSIzMjI6MS0zMjI6NjciIG5hbWU9IlRvU3RyaW5nIiBjb250ZXh0PSJQYXRpZW50IiBhY2Nlc3NMZXZlbD0iUHVibGljIiB4c2k6dHlwZT0iRnVuY3Rpb25EZWYiPgogICAgICAgICA8ZXhwcmVzc2lvbiBsb2NhdG9yPSIzMjI6NTctMzIyOjY3IiBwYXRoPSJ2YWx1ZSIgeHNpOnR5cGU9IlByb3BlcnR5Ij4KICAgICAgICAgICAgPHNvdXJjZSBsb2NhdG9yPSIzMjI6NTctMzIyOjYxIiBuYW1lPSJ2YWx1ZSIgeHNpOnR5cGU9Ik9wZXJhbmRSZWYiLz4KICAgICAgICAgPC9leHByZXNzaW9uPgogICAgICAgICA8b3BlcmFuZCBuYW1lPSJ2YWx1ZSI+CiAgICAgICAgICAgIDxvcGVyYW5kVHlwZVNwZWNpZmllciBsb2NhdG9yPSIzMjI6MzItMzIyOjUzIiBuYW1lPSJmaGlyOlByb3BlcnR5UmVwcmVzZW50YXRpb24iIHhzaTp0eXBlPSJOYW1lZFR5cGVTcGVjaWZpZXIiLz4KICAgICAgICAgPC9vcGVyYW5kPgogICAgICA8L2RlZj4KICAgICAgPGRlZiBsb2NhdG9yPSIzMjM6MS0zMjM6NTciIG5hbWU9IlRvU3RyaW5nIiBjb250ZXh0PSJQYXRpZW50IiBhY2Nlc3NMZXZlbD0iUHVibGljIiB4c2k6dHlwZT0iRnVuY3Rpb25EZWYiPgogICAgICAgICA8ZXhwcmVzc2lvbiBsb2NhdG9yPSIzMjM6NDctMzIzOjU3IiBwYXRoPSJ2YWx1ZSIgeHNpOnR5cGU9IlByb3BlcnR5Ij4KICAgICAgICAgICAgPHNvdXJjZSBsb2NhdG9yPSIzMjM6NDctMzIzOjUxIiBuYW1lPSJ2YWx1ZSIgeHNpOnR5cGU9Ik9wZXJhbmRSZWYiLz4KICAgICAgICAgPC9leHByZXNzaW9uPgogICAgICAgICA8b3BlcmFuZCBuYW1lPSJ2YWx1ZSI+CiAgICAgICAgICAgIDxvcGVyYW5kVHlwZVNwZWNpZmllciBsb2NhdG9yPSIzMjM6MzItMzIzOjQzIiBuYW1lPSJmaGlyOlByb3BlcnR5VHlwZSIgeHNpOnR5cGU9Ik5hbWVkVHlwZVNwZWNpZmllciIvPgogICAgICAgICA8L29wZXJhbmQ+CiAgICAgIDwvZGVmPgogICAgICA8ZGVmIGxvY2F0b3I9IjMyNDoxLTMyNDo2NSIgbmFtZT0iVG9TdHJpbmciIGNvbnRleHQ9IlBhdGllbnQiIGFjY2Vzc0xldmVsPSJQdWJsaWMiIHhzaTp0eXBlPSJGdW5jdGlvbkRlZiI+CiAgICAgICAgIDxleHByZXNzaW9uIGxvY2F0b3I9IjMyNDo1NS0zMjQ6NjUiIHBhdGg9InZhbHVlIiB4c2k6dHlwZT0iUHJvcGVydHkiPgogICAgICAgICAgICA8c291cmNlIGxvY2F0b3I9IjMyNDo1NS0zMjQ6NTkiIG5hbWU9InZhbHVlIiB4c2k6dHlwZT0iT3BlcmFuZFJlZiIvPgogICAgICAgICA8L2V4cHJlc3Npb24+CiAgICAgICAgIDxvcGVyYW5kIG5hbWU9InZhbHVlIj4KICAgICAgICAgICAgPG9wZXJhbmRUeXBlU3BlY2lmaWVyIGxvY2F0b3I9IjMyNDozMi0zMjQ6NTEiIG5hbWU9ImZoaXI6UHJvdmVuYW5jZUVudGl0eVJvbGUiIHhzaTp0eXBlPSJOYW1lZFR5cGVTcGVjaWZpZXIiLz4KICAgICAgICAgPC9vcGVyYW5kPgogICAgICA8L2RlZj4KICAgICAgPGRlZiBsb2NhdG9yPSIzMjU6MS0zMjU6NjIiIG5hbWU9IlRvU3RyaW5nIiBjb250ZXh0PSJQYXRpZW50IiBhY2Nlc3NMZXZlbD0iUHVibGljIiB4c2k6dHlwZT0iRnVuY3Rpb25EZWYiPgogICAgICAgICA8ZXhwcmVzc2lvbiBsb2NhdG9yPSIzMjU6NTItMzI1OjYyIiBwYXRoPSJ2YWx1ZSIgeHNpOnR5cGU9IlByb3BlcnR5Ij4KICAgICAgICAgICAgPHNvdXJjZSBsb2NhdG9yPSIzMjU6NTItMzI1OjU2IiBuYW1lPSJ2YWx1ZSIgeHNpOnR5cGU9Ik9wZXJhbmRSZWYiLz4KICAgICAgICAgPC9leHByZXNzaW9uPgogICAgICAgICA8b3BlcmFuZCBuYW1lPSJ2YWx1ZSI+CiAgICAgICAgICAgIDxvcGVyYW5kVHlwZVNwZWNpZmllciBsb2NhdG9yPSIzMjU6MzItMzI1OjQ4IiBuYW1lPSJmaGlyOlB1YmxpY2F0aW9uU3RhdHVzIiB4c2k6dHlwZT0iTmFtZWRUeXBlU3BlY2lmaWVyIi8+CiAgICAgICAgIDwvb3BlcmFuZD4KICAgICAgPC9kZWY+CiAgICAgIDxkZWYgbG9jYXRvcj0iMzI2OjEtMzI2OjU2IiBuYW1lPSJUb1N0cmluZyIgY29udGV4dD0iUGF0aWVudCIgYWNjZXNzTGV2ZWw9IlB1YmxpYyIgeHNpOnR5cGU9IkZ1bmN0aW9uRGVmIj4KICAgICAgICAgPGV4cHJlc3Npb24gbG9jYXRvcj0iMzI2OjQ2LTMyNjo1NiIgcGF0aD0idmFsdWUiIHhzaTp0eXBlPSJQcm9wZXJ0eSI+CiAgICAgICAgICAgIDxzb3VyY2UgbG9jYXRvcj0iMzI2OjQ2LTMyNjo1MCIgbmFtZT0idmFsdWUiIHhzaTp0eXBlPSJPcGVyYW5kUmVmIi8+CiAgICAgICAgIDwvZXhwcmVzc2lvbj4KICAgICAgICAgPG9wZXJhbmQgbmFtZT0idmFsdWUiPgogICAgICAgICAgICA8b3BlcmFuZFR5cGVTcGVjaWZpZXIgbG9jYXRvcj0iMzI2OjMyLTMyNjo0MiIgbmFtZT0iZmhpcjpRdWFsaXR5VHlwZSIgeHNpOnR5cGU9Ik5hbWVkVHlwZVNwZWNpZmllciIvPgogICAgICAgICA8L29wZXJhbmQ+CiAgICAgIDwvZGVmPgogICAgICA8ZGVmIGxvY2F0b3I9IjMyNzoxLTMyNzo2MyIgbmFtZT0iVG9TdHJpbmciIGNvbnRleHQ9IlBhdGllbnQiIGFjY2Vzc0xldmVsPSJQdWJsaWMiIHhzaTp0eXBlPSJGdW5jdGlvbkRlZiI+CiAgICAgICAgIDxleHByZXNzaW9uIGxvY2F0b3I9IjMyNzo1My0zMjc6NjMiIHBhdGg9InZhbHVlIiB4c2k6dHlwZT0iUHJvcGVydHkiPgogICAgICAgICAgICA8c291cmNlIGxvY2F0b3I9IjMyNzo1My0zMjc6NTciIG5hbWU9InZhbHVlIiB4c2k6dHlwZT0iT3BlcmFuZFJlZiIvPgogICAgICAgICA8L2V4cHJlc3Npb24+CiAgICAgICAgIDxvcGVyYW5kIG5hbWU9InZhbHVlIj4KICAgICAgICAgICAgPG9wZXJhbmRUeXBlU3BlY2lmaWVyIGxvY2F0b3I9IjMyNzozMi0zMjc6NDkiIG5hbWU9ImZoaXI6UXVhbnRpdHlDb21wYXJhdG9yIiB4c2k6dHlwZT0iTmFtZWRUeXBlU3BlY2lmaWVyIi8+CiAgICAgICAgIDwvb3BlcmFuZD4KICAgICAgPC9kZWY+CiAgICAgIDxkZWYgbG9jYXRvcj0iMzI4OjEtMzI4OjcwIiBuYW1lPSJUb1N0cmluZyIgY29udGV4dD0iUGF0aWVudCIgYWNjZXNzTGV2ZWw9IlB1YmxpYyIgeHNpOnR5cGU9IkZ1bmN0aW9uRGVmIj4KICAgICAgICAgPGV4cHJlc3Npb24gbG9jYXRvcj0iMzI4OjYwLTMyODo3MCIgcGF0aD0idmFsdWUiIHhzaTp0eXBlPSJQcm9wZXJ0eSI+CiAgICAgICAgICAgIDxzb3VyY2UgbG9jYXRvcj0iMzI4OjYwLTMyODo2NCIgbmFtZT0idmFsdWUiIHhzaTp0eXBlPSJPcGVyYW5kUmVmIi8+CiAgICAgICAgIDwvZXhwcmVzc2lvbj4KICAgICAgICAgPG9wZXJhbmQgbmFtZT0idmFsdWUiPgogICAgICAgICAgICA8b3BlcmFuZFR5cGVTcGVjaWZpZXIgbG9jYXRvcj0iMzI4OjMyLTMyODo1NiIgbmFtZT0iZmhpcjpRdWVzdGlvbm5haXJlSXRlbU9wZXJhdG9yIiB4c2k6dHlwZT0iTmFtZWRUeXBlU3BlY2lmaWVyIi8+CiAgICAgICAgIDwvb3BlcmFuZD4KICAgICAgPC9kZWY+CiAgICAgIDxkZWYgbG9jYXRvcj0iMzI5OjEtMzI5OjY2IiBuYW1lPSJUb1N0cmluZyIgY29udGV4dD0iUGF0aWVudCIgYWNjZXNzTGV2ZWw9IlB1YmxpYyIgeHNpOnR5cGU9IkZ1bmN0aW9uRGVmIj4KICAgICAgICAgPGV4cHJlc3Npb24gbG9jYXRvcj0iMzI5OjU2LTMyOTo2NiIgcGF0aD0idmFsdWUiIHhzaTp0eXBlPSJQcm9wZXJ0eSI+CiAgICAgICAgICAgIDxzb3VyY2UgbG9jYXRvcj0iMzI5OjU2LTMyOTo2MCIgbmFtZT0idmFsdWUiIHhzaTp0eXBlPSJPcGVyYW5kUmVmIi8+CiAgICAgICAgIDwvZXhwcmVzc2lvbj4KICAgICAgICAgPG9wZXJhbmQgbmFtZT0idmFsdWUiPgogICAgICAgICAgICA8b3BlcmFuZFR5cGVTcGVjaWZpZXIgbG9jYXRvcj0iMzI5OjMyLTMyOTo1MiIgbmFtZT0iZmhpcjpRdWVzdGlvbm5haXJlSXRlbVR5cGUiIHhzaTp0eXBlPSJOYW1lZFR5cGVTcGVjaWZpZXIiLz4KICAgICAgICAgPC9vcGVyYW5kPgogICAgICA8L2RlZj4KICAgICAgPGRlZiBsb2NhdG9yPSIzMzA6MS0zMzA6NzIiIG5hbWU9IlRvU3RyaW5nIiBjb250ZXh0PSJQYXRpZW50IiBhY2Nlc3NMZXZlbD0iUHVibGljIiB4c2k6dHlwZT0iRnVuY3Rpb25EZWYiPgogICAgICAgICA8ZXhwcmVzc2lvbiBsb2NhdG9yPSIzMzA6NjItMzMwOjcyIiBwYXRoPSJ2YWx1ZSIgeHNpOnR5cGU9IlByb3BlcnR5Ij4KICAgICAgICAgICAgPHNvdXJjZSBsb2NhdG9yPSIzMzA6NjItMzMwOjY2IiBuYW1lPSJ2YWx1ZSIgeHNpOnR5cGU9Ik9wZXJhbmRSZWYiLz4KICAgICAgICAgPC9leHByZXNzaW9uPgogICAgICAgICA8b3BlcmFuZCBuYW1lPSJ2YWx1ZSI+CiAgICAgICAgICAgIDxvcGVyYW5kVHlwZVNwZWNpZmllciBsb2NhdG9yPSIzMzA6MzItMzMwOjU4IiBuYW1lPSJmaGlyOlF1ZXN0aW9ubmFpcmVSZXNwb25zZVN0YXR1cyIgeHNpOnR5cGU9Ik5hbWVkVHlwZVNwZWNpZmllciIvPgogICAgICAgICA8L29wZXJhbmQ+CiAgICAgIDwvZGVmPgogICAgICA8ZGVmIGxvY2F0b3I9IjMzMToxLTMzMTo2OCIgbmFtZT0iVG9TdHJpbmciIGNvbnRleHQ9IlBhdGllbnQiIGFjY2Vzc0xldmVsPSJQdWJsaWMiIHhzaTp0eXBlPSJGdW5jdGlvbkRlZiI+CiAgICAgICAgIDxleHByZXNzaW9uIGxvY2F0b3I9IjMzMTo1OC0zMzE6NjgiIHBhdGg9InZhbHVlIiB4c2k6dHlwZT0iUHJvcGVydHkiPgogICAgICAgICAgICA8c291cmNlIGxvY2F0b3I9IjMzMTo1OC0zMzE6NjIiIG5hbWU9InZhbHVlIiB4c2k6dHlwZT0iT3BlcmFuZFJlZiIvPgogICAgICAgICA8L2V4cHJlc3Npb24+CiAgICAgICAgIDxvcGVyYW5kIG5hbWU9InZhbHVlIj4KICAgICAgICAgICAgPG9wZXJhbmRUeXBlU3BlY2lmaWVyIGxvY2F0b3I9IjMzMTozMi0zMzE6NTQiIG5hbWU9ImZoaXI6UmVmZXJlbmNlSGFuZGxpbmdQb2xpY3kiIHhzaTp0eXBlPSJOYW1lZFR5cGVTcGVjaWZpZXIiLz4KICAgICAgICAgPC9vcGVyYW5kPgogICAgICA8L2RlZj4KICAgICAgPGRlZiBsb2NhdG9yPSIzMzI6MS0zMzI6NjYiIG5hbWU9IlRvU3RyaW5nIiBjb250ZXh0PSJQYXRpZW50IiBhY2Nlc3NMZXZlbD0iUHVibGljIiB4c2k6dHlwZT0iRnVuY3Rpb25EZWYiPgogICAgICAgICA8ZXhwcmVzc2lvbiBsb2NhdG9yPSIzMzI6NTYtMzMyOjY2IiBwYXRoPSJ2YWx1ZSIgeHNpOnR5cGU9IlByb3BlcnR5Ij4KICAgICAgICAgICAgPHNvdXJjZSBsb2NhdG9yPSIzMzI6NTYtMzMyOjYwIiBuYW1lPSJ2YWx1ZSIgeHNpOnR5cGU9Ik9wZXJhbmRSZWYiLz4KICAgICAgICAgPC9leHByZXNzaW9uPgogICAgICAgICA8b3BlcmFuZCBuYW1lPSJ2YWx1ZSI+CiAgICAgICAgICAgIDxvcGVyYW5kVHlwZVNwZWNpZmllciBsb2NhdG9yPSIzMzI6MzItMzMyOjUyIiBuYW1lPSJmaGlyOlJlZmVyZW5jZVZlcnNpb25SdWxlcyIgeHNpOnR5cGU9Ik5hbWVkVHlwZVNwZWNpZmllciIvPgogICAgICAgICA8L29wZXJhbmQ+CiAgICAgIDwvZGVmPgogICAgICA8ZGVmIGxvY2F0b3I9IjMzMzoxLTMzMzo2NyIgbmFtZT0iVG9TdHJpbmciIGNvbnRleHQ9IlBhdGllbnQiIGFjY2Vzc0xldmVsPSJQdWJsaWMiIHhzaTp0eXBlPSJGdW5jdGlvbkRlZiI+CiAgICAgICAgIDxleHByZXNzaW9uIGxvY2F0b3I9IjMzMzo1Ny0zMzM6NjciIHBhdGg9InZhbHVlIiB4c2k6dHlwZT0iUHJvcGVydHkiPgogICAgICAgICAgICA8c291cmNlIGxvY2F0b3I9IjMzMzo1Ny0zMzM6NjEiIG5hbWU9InZhbHVlIiB4c2k6dHlwZT0iT3BlcmFuZFJlZiIvPgogICAgICAgICA8L2V4cHJlc3Npb24+CiAgICAgICAgIDxvcGVyYW5kIG5hbWU9InZhbHVlIj4KICAgICAgICAgICAgPG9wZXJhbmRUeXBlU3BlY2lmaWVyIGxvY2F0b3I9IjMzMzozMi0zMzM6NTMiIG5hbWU9ImZoaXI6UmVmZXJyZWREb2N1bWVudFN0YXR1cyIgeHNpOnR5cGU9Ik5hbWVkVHlwZVNwZWNpZmllciIvPgogICAgICAgICA8L29wZXJhbmQ+CiAgICAgIDwvZGVmPgogICAgICA8ZGVmIGxvY2F0b3I9IjMzNDoxLTMzNDo2NCIgbmFtZT0iVG9TdHJpbmciIGNvbnRleHQ9IlBhdGllbnQiIGFjY2Vzc0xldmVsPSJQdWJsaWMiIHhzaTp0eXBlPSJGdW5jdGlvbkRlZiI+CiAgICAgICAgIDxleHByZXNzaW9uIGxvY2F0b3I9IjMzNDo1NC0zMzQ6NjQiIHBhdGg9InZhbHVlIiB4c2k6dHlwZT0iUHJvcGVydHkiPgogICAgICAgICAgICA8c291cmNlIGxvY2F0b3I9IjMzNDo1NC0zMzQ6NTgiIG5hbWU9InZhbHVlIiB4c2k6dHlwZT0iT3BlcmFuZFJlZiIvPgogICAgICAgICA8L2V4cHJlc3Npb24+CiAgICAgICAgIDxvcGVyYW5kIG5hbWU9InZhbHVlIj4KICAgICAgICAgICAgPG9wZXJhbmRUeXBlU3BlY2lmaWVyIGxvY2F0b3I9IjMzNDozMi0zMzQ6NTAiIG5hbWU9ImZoaXI6UmVsYXRlZEFydGlmYWN0VHlwZSIgeHNpOnR5cGU9Ik5hbWVkVHlwZVNwZWNpZmllciIvPgogICAgICAgICA8L29wZXJhbmQ+CiAgICAgIDwvZGVmPgogICAgICA8ZGVmIGxvY2F0b3I9IjMzNToxLTMzNTo2MiIgbmFtZT0iVG9TdHJpbmciIGNvbnRleHQ9IlBhdGllbnQiIGFjY2Vzc0xldmVsPSJQdWJsaWMiIHhzaTp0eXBlPSJGdW5jdGlvbkRlZiI+CiAgICAgICAgIDxleHByZXNzaW9uIGxvY2F0b3I9IjMzNTo1Mi0zMzU6NjIiIHBhdGg9InZhbHVlIiB4c2k6dHlwZT0iUHJvcGVydHkiPgogICAgICAgICAgICA8c291cmNlIGxvY2F0b3I9IjMzNTo1Mi0zMzU6NTYiIG5hbWU9InZhbHVlIiB4c2k6dHlwZT0iT3BlcmFuZFJlZiIvPgogICAgICAgICA8L2V4cHJlc3Npb24+CiAgICAgICAgIDxvcGVyYW5kIG5hbWU9InZhbHVlIj4KICAgICAgICAgICAgPG9wZXJhbmRUeXBlU3BlY2lmaWVyIGxvY2F0b3I9IjMzNTozMi0zMzU6NDgiIG5hbWU9ImZoaXI6UmVtaXR0YW5jZU91dGNvbWUiIHhzaTp0eXBlPSJOYW1lZFR5cGVTcGVjaWZpZXIiLz4KICAgICAgICAgPC9vcGVyYW5kPgogICAgICA8L2RlZj4KICAgICAgPGRlZiBsb2NhdG9yPSIzMzY6MS0zMzY6NTkiIG5hbWU9IlRvU3RyaW5nIiBjb250ZXh0PSJQYXRpZW50IiBhY2Nlc3NMZXZlbD0iUHVibGljIiB4c2k6dHlwZT0iRnVuY3Rpb25EZWYiPgogICAgICAgICA8ZXhwcmVzc2lvbiBsb2NhdG9yPSIzMzY6NDktMzM2OjU5IiBwYXRoPSJ2YWx1ZSIgeHNpOnR5cGU9IlByb3BlcnR5Ij4KICAgICAgICAgICAgPHNvdXJjZSBsb2NhdG9yPSIzMzY6NDktMzM2OjUzIiBuYW1lPSJ2YWx1ZSIgeHNpOnR5cGU9Ik9wZXJhbmRSZWYiLz4KICAgICAgICAgPC9leHByZXNzaW9uPgogICAgICAgICA8b3BlcmFuZCBuYW1lPSJ2YWx1ZSI+CiAgICAgICAgICAgIDxvcGVyYW5kVHlwZVNwZWNpZmllciBsb2NhdG9yPSIzMzY6MzItMzM2OjQ1IiBuYW1lPSJmaGlyOlJlcG9zaXRvcnlUeXBlIiB4c2k6dHlwZT0iTmFtZWRUeXBlU3BlY2lmaWVyIi8+CiAgICAgICAgIDwvb3BlcmFuZD4KICAgICAgPC9kZWY+CiAgICAgIDxkZWYgbG9jYXRvcj0iMzM3OjEtMzM3OjU4IiBuYW1lPSJUb1N0cmluZyIgY29udGV4dD0iUGF0aWVudCIgYWNjZXNzTGV2ZWw9IlB1YmxpYyIgeHNpOnR5cGU9IkZ1bmN0aW9uRGVmIj4KICAgICAgICAgPGV4cHJlc3Npb24gbG9jYXRvcj0iMzM3OjQ4LTMzNzo1OCIgcGF0aD0idmFsdWUiIHhzaTp0eXBlPSJQcm9wZXJ0eSI+CiAgICAgICAgICAgIDxzb3VyY2UgbG9jYXRvcj0iMzM3OjQ4LTMzNzo1MiIgbmFtZT0idmFsdWUiIHhzaTp0eXBlPSJPcGVyYW5kUmVmIi8+CiAgICAgICAgIDwvZXhwcmVzc2lvbj4KICAgICAgICAgPG9wZXJhbmQgbmFtZT0idmFsdWUiPgogICAgICAgICAgICA8b3BlcmFuZFR5cGVTcGVjaWZpZXIgbG9jYXRvcj0iMzM3OjMyLTMzNzo0NCIgbmFtZT0iZmhpcjpSZXF1ZXN0SW50ZW50IiB4c2k6dHlwZT0iTmFtZWRUeXBlU3BlY2lmaWVyIi8+CiAgICAgICAgIDwvb3BlcmFuZD4KICAgICAgPC9kZWY+CiAgICAgIDxkZWYgbG9jYXRvcj0iMzM4OjEtMzM4OjYwIiBuYW1lPSJUb1N0cmluZyIgY29udGV4dD0iUGF0aWVudCIgYWNjZXNzTGV2ZWw9IlB1YmxpYyIgeHNpOnR5cGU9IkZ1bmN0aW9uRGVmIj4KICAgICAgICAgPGV4cHJlc3Npb24gbG9jYXRvcj0iMzM4OjUwLTMzODo2MCIgcGF0aD0idmFsdWUiIHhzaTp0eXBlPSJQcm9wZXJ0eSI+CiAgICAgICAgICAgIDxzb3VyY2UgbG9jYXRvcj0iMzM4OjUwLTMzODo1NCIgbmFtZT0idmFsdWUiIHhzaTp0eXBlPSJPcGVyYW5kUmVmIi8+CiAgICAgICAgIDwvZXhwcmVzc2lvbj4KICAgICAgICAgPG9wZXJhbmQgbmFtZT0idmFsdWUiPgogICAgICAgICAgICA8b3BlcmFuZFR5cGVTcGVjaWZpZXIgbG9jYXRvcj0iMzM4OjMyLTMzODo0NiIgbmFtZT0iZmhpcjpSZXF1ZXN0UHJpb3JpdHkiIHhzaTp0eXBlPSJOYW1lZFR5cGVTcGVjaWZpZXIiLz4KICAgICAgICAgPC9vcGVyYW5kPgogICAgICA8L2RlZj4KICAgICAgPGRlZiBsb2NhdG9yPSIzMzk6MS0zMzk6NTgiIG5hbWU9IlRvU3RyaW5nIiBjb250ZXh0PSJQYXRpZW50IiBhY2Nlc3NMZXZlbD0iUHVibGljIiB4c2k6dHlwZT0iRnVuY3Rpb25EZWYiPgogICAgICAgICA8ZXhwcmVzc2lvbiBsb2NhdG9yPSIzMzk6NDgtMzM5OjU4IiBwYXRoPSJ2YWx1ZSIgeHNpOnR5cGU9IlByb3BlcnR5Ij4KICAgICAgICAgICAgPHNvdXJjZSBsb2NhdG9yPSIzMzk6NDgtMzM5OjUyIiBuYW1lPSJ2YWx1ZSIgeHNpOnR5cGU9Ik9wZXJhbmRSZWYiLz4KICAgICAgICAgPC9leHByZXNzaW9uPgogICAgICAgICA8b3BlcmFuZCBuYW1lPSJ2YWx1ZSI+CiAgICAgICAgICAgIDxvcGVyYW5kVHlwZVNwZWNpZmllciBsb2NhdG9yPSIzMzk6MzItMzM5OjQ0IiBuYW1lPSJmaGlyOlJlcXVlc3RTdGF0dXMiIHhzaTp0eXBlPSJOYW1lZFR5cGVTcGVjaWZpZXIiLz4KICAgICAgICAgPC9vcGVyYW5kPgogICAgICA8L2RlZj4KICAgICAgPGRlZiBsb2NhdG9yPSIzNDA6MS0zNDA6NjQiIG5hbWU9IlRvU3RyaW5nIiBjb250ZXh0PSJQYXRpZW50IiBhY2Nlc3NMZXZlbD0iUHVibGljIiB4c2k6dHlwZT0iRnVuY3Rpb25EZWYiPgogICAgICAgICA8ZXhwcmVzc2lvbiBsb2NhdG9yPSIzNDA6NTQtMzQwOjY0IiBwYXRoPSJ2YWx1ZSIgeHNpOnR5cGU9IlByb3BlcnR5Ij4KICAgICAgICAgICAgPHNvdXJjZSBsb2NhdG9yPSIzNDA6NTQtMzQwOjU4IiBuYW1lPSJ2YWx1ZSIgeHNpOnR5cGU9Ik9wZXJhbmRSZWYiLz4KICAgICAgICAgPC9leHByZXNzaW9uPgogICAgICAgICA8b3BlcmFuZCBuYW1lPSJ2YWx1ZSI+CiAgICAgICAgICAgIDxvcGVyYW5kVHlwZVNwZWNpZmllciBsb2NhdG9yPSIzNDA6MzItMzQwOjUwIiBuYW1lPSJmaGlyOlJlc2VhcmNoRWxlbWVudFR5cGUiIHhzaTp0eXBlPSJOYW1lZFR5cGVTcGVjaWZpZXIiLz4KICAgICAgICAgPC9vcGVyYW5kPgogICAgICA8L2RlZj4KICAgICAgPGRlZiBsb2NhdG9yPSIzNDE6MS0zNDE6NjQiIG5hbWU9IlRvU3RyaW5nIiBjb250ZXh0PSJQYXRpZW50IiBhY2Nlc3NMZXZlbD0iUHVibGljIiB4c2k6dHlwZT0iRnVuY3Rpb25EZWYiPgogICAgICAgICA8ZXhwcmVzc2lvbiBsb2NhdG9yPSIzNDE6NTQtMzQxOjY0IiBwYXRoPSJ2YWx1ZSIgeHNpOnR5cGU9IlByb3BlcnR5Ij4KICAgICAgICAgICAgPHNvdXJjZSBsb2NhdG9yPSIzNDE6NTQtMzQxOjU4IiBuYW1lPSJ2YWx1ZSIgeHNpOnR5cGU9Ik9wZXJhbmRSZWYiLz4KICAgICAgICAgPC9leHByZXNzaW9uPgogICAgICAgICA8b3BlcmFuZCBuYW1lPSJ2YWx1ZSI+CiAgICAgICAgICAgIDxvcGVyYW5kVHlwZVNwZWNpZmllciBsb2NhdG9yPSIzNDE6MzItMzQxOjUwIiBuYW1lPSJmaGlyOlJlc2VhcmNoU3R1ZHlTdGF0dXMiIHhzaTp0eXBlPSJOYW1lZFR5cGVTcGVjaWZpZXIiLz4KICAgICAgICAgPC9vcGVyYW5kPgogICAgICA8L2RlZj4KICAgICAgPGRlZiBsb2NhdG9yPSIzNDI6MS0zNDI6NjYiIG5hbWU9IlRvU3RyaW5nIiBjb250ZXh0PSJQYXRpZW50IiBhY2Nlc3NMZXZlbD0iUHVibGljIiB4c2k6dHlwZT0iRnVuY3Rpb25EZWYiPgogICAgICAgICA8ZXhwcmVzc2lvbiBsb2NhdG9yPSIzNDI6NTYtMzQyOjY2IiBwYXRoPSJ2YWx1ZSIgeHNpOnR5cGU9IlByb3BlcnR5Ij4KICAgICAgICAgICAgPHNvdXJjZSBsb2NhdG9yPSIzNDI6NTYtMzQyOjYwIiBuYW1lPSJ2YWx1ZSIgeHNpOnR5cGU9Ik9wZXJhbmRSZWYiLz4KICAgICAgICAgPC9leHByZXNzaW9uPgogICAgICAgICA8b3BlcmFuZCBuYW1lPSJ2YWx1ZSI+CiAgICAgICAgICAgIDxvcGVyYW5kVHlwZVNwZWNpZmllciBsb2NhdG9yPSIzNDI6MzItMzQyOjUyIiBuYW1lPSJmaGlyOlJlc2VhcmNoU3ViamVjdFN0YXR1cyIgeHNpOnR5cGU9Ik5hbWVkVHlwZVNwZWNpZmllciIvPgogICAgICAgICA8L29wZXJhbmQ+CiAgICAgIDwvZGVmPgogICAgICA8ZGVmIGxvY2F0b3I9IjM0MzoxLTM0Mzo1NyIgbmFtZT0iVG9TdHJpbmciIGNvbnRleHQ9IlBhdGllbnQiIGFjY2Vzc0xldmVsPSJQdWJsaWMiIHhzaTp0eXBlPSJGdW5jdGlvbkRlZiI+CiAgICAgICAgIDxleHByZXNzaW9uIGxvY2F0b3I9IjM0Mzo0Ny0zNDM6NTciIHBhdGg9InZhbHVlIiB4c2k6dHlwZT0iUHJvcGVydHkiPgogICAgICAgICAgICA8c291cmNlIGxvY2F0b3I9IjM0Mzo0Ny0zNDM6NTEiIG5hbWU9InZhbHVlIiB4c2k6dHlwZT0iT3BlcmFuZFJlZiIvPgogICAgICAgICA8L2V4cHJlc3Npb24+CiAgICAgICAgIDxvcGVyYW5kIG5hbWU9InZhbHVlIj4KICAgICAgICAgICAgPG9wZXJhbmRUeXBlU3BlY2lmaWVyIGxvY2F0b3I9IjM0MzozMi0zNDM6NDMiIG5hbWU9ImZoaXI6UmVzb3VyY2VUeXBlIiB4c2k6dHlwZT0iTmFtZWRUeXBlU3BlY2lmaWVyIi8+CiAgICAgICAgIDwvb3BlcmFuZD4KICAgICAgPC9kZWY+CiAgICAgIDxkZWYgbG9jYXRvcj0iMzQ0OjEtMzQ0OjY2IiBuYW1lPSJUb1N0cmluZyIgY29udGV4dD0iUGF0aWVudCIgYWNjZXNzTGV2ZWw9IlB1YmxpYyIgeHNpOnR5cGU9IkZ1bmN0aW9uRGVmIj4KICAgICAgICAgPGV4cHJlc3Npb24gbG9jYXRvcj0iMzQ0OjU2LTM0NDo2NiIgcGF0aD0idmFsdWUiIHhzaTp0eXBlPSJQcm9wZXJ0eSI+CiAgICAgICAgICAgIDxzb3VyY2UgbG9jYXRvcj0iMzQ0OjU2LTM0NDo2MCIgbmFtZT0idmFsdWUiIHhzaTp0eXBlPSJPcGVyYW5kUmVmIi8+CiAgICAgICAgIDwvZXhwcmVzc2lvbj4KICAgICAgICAgPG9wZXJhbmQgbmFtZT0idmFsdWUiPgogICAgICAgICAgICA8b3BlcmFuZFR5cGVTcGVjaWZpZXIgbG9jYXRvcj0iMzQ0OjMyLTM0NDo1MiIgbmFtZT0iZmhpcjpSZXNvdXJjZVZlcnNpb25Qb2xpY3kiIHhzaTp0eXBlPSJOYW1lZFR5cGVTcGVjaWZpZXIiLz4KICAgICAgICAgPC9vcGVyYW5kPgogICAgICA8L2RlZj4KICAgICAgPGRlZiBsb2NhdG9yPSIzNDU6MS0zNDU6NTciIG5hbWU9IlRvU3RyaW5nIiBjb250ZXh0PSJQYXRpZW50IiBhY2Nlc3NMZXZlbD0iUHVibGljIiB4c2k6dHlwZT0iRnVuY3Rpb25EZWYiPgogICAgICAgICA8ZXhwcmVzc2lvbiBsb2NhdG9yPSIzNDU6NDctMzQ1OjU3IiBwYXRoPSJ2YWx1ZSIgeHNpOnR5cGU9IlByb3BlcnR5Ij4KICAgICAgICAgICAgPHNvdXJjZSBsb2NhdG9yPSIzNDU6NDctMzQ1OjUxIiBuYW1lPSJ2YWx1ZSIgeHNpOnR5cGU9Ik9wZXJhbmRSZWYiLz4KICAgICAgICAgPC9leHByZXNzaW9uPgogICAgICAgICA8b3BlcmFuZCBuYW1lPSJ2YWx1ZSI+CiAgICAgICAgICAgIDxvcGVyYW5kVHlwZVNwZWNpZmllciBsb2NhdG9yPSIzNDU6MzItMzQ1OjQzIiBuYW1lPSJmaGlyOlJlc3BvbnNlVHlwZSIgeHNpOnR5cGU9Ik5hbWVkVHlwZVNwZWNpZmllciIvPgogICAgICAgICA8L29wZXJhbmQ+CiAgICAgIDwvZGVmPgogICAgICA8ZGVmIGxvY2F0b3I9IjM0NjoxLTM0Njo2NiIgbmFtZT0iVG9TdHJpbmciIGNvbnRleHQ9IlBhdGllbnQiIGFjY2Vzc0xldmVsPSJQdWJsaWMiIHhzaTp0eXBlPSJGdW5jdGlvbkRlZiI+CiAgICAgICAgIDxleHByZXNzaW9uIGxvY2F0b3I9IjM0Njo1Ni0zNDY6NjYiIHBhdGg9InZhbHVlIiB4c2k6dHlwZT0iUHJvcGVydHkiPgogICAgICAgICAgICA8c291cmNlIGxvY2F0b3I9IjM0Njo1Ni0zNDY6NjAiIG5hbWU9InZhbHVlIiB4c2k6dHlwZT0iT3BlcmFuZFJlZiIvPgogICAgICAgICA8L2V4cHJlc3Npb24+CiAgICAgICAgIDxvcGVyYW5kIG5hbWU9InZhbHVlIj4KICAgICAgICAgICAgPG9wZXJhbmRUeXBlU3BlY2lmaWVyIGxvY2F0b3I9IjM0NjozMi0zNDY6NTIiIG5hbWU9ImZoaXI6UmVzdGZ1bENhcGFiaWxpdHlNb2RlIiB4c2k6dHlwZT0iTmFtZWRUeXBlU3BlY2lmaWVyIi8+CiAgICAgICAgIDwvb3BlcmFuZD4KICAgICAgPC9kZWY+CiAgICAgIDxkZWYgbG9jYXRvcj0iMzQ3OjEtMzQ3OjY1IiBuYW1lPSJUb1N0cmluZyIgY29udGV4dD0iUGF0aWVudCIgYWNjZXNzTGV2ZWw9IlB1YmxpYyIgeHNpOnR5cGU9IkZ1bmN0aW9uRGVmIj4KICAgICAgICAgPGV4cHJlc3Npb24gbG9jYXRvcj0iMzQ3OjU1LTM0Nzo2NSIgcGF0aD0idmFsdWUiIHhzaTp0eXBlPSJQcm9wZXJ0eSI+CiAgICAgICAgICAgIDxzb3VyY2UgbG9jYXRvcj0iMzQ3OjU1LTM0Nzo1OSIgbmFtZT0idmFsdWUiIHhzaTp0eXBlPSJPcGVyYW5kUmVmIi8+CiAgICAgICAgIDwvZXhwcmVzc2lvbj4KICAgICAgICAgPG9wZXJhbmQgbmFtZT0idmFsdWUiPgogICAgICAgICAgICA8b3BlcmFuZFR5cGVTcGVjaWZpZXIgbG9jYXRvcj0iMzQ3OjMyLTM0Nzo1MSIgbmFtZT0iZmhpcjpSaXNrQXNzZXNzbWVudFN0YXR1cyIgeHNpOnR5cGU9Ik5hbWVkVHlwZVNwZWNpZmllciIvPgogICAgICAgICA8L29wZXJhbmQ+CiAgICAgIDwvZGVmPgogICAgICA8ZGVmIGxvY2F0b3I9IjM0ODoxLTM0ODo1NiIgbmFtZT0iVG9TdHJpbmciIGNvbnRleHQ9IlBhdGllbnQiIGFjY2Vzc0xldmVsPSJQdWJsaWMiIHhzaTp0eXBlPSJGdW5jdGlvbkRlZiI+CiAgICAgICAgIDxleHByZXNzaW9uIGxvY2F0b3I9IjM0ODo0Ni0zNDg6NTYiIHBhdGg9InZhbHVlIiB4c2k6dHlwZT0iUHJvcGVydHkiPgogICAgICAgICAgICA8c291cmNlIGxvY2F0b3I9IjM0ODo0Ni0zNDg6NTAiIG5hbWU9InZhbHVlIiB4c2k6dHlwZT0iT3BlcmFuZFJlZiIvPgogICAgICAgICA8L2V4cHJlc3Npb24+CiAgICAgICAgIDxvcGVyYW5kIG5hbWU9InZhbHVlIj4KICAgICAgICAgICAgPG9wZXJhbmRUeXBlU3BlY2lmaWVyIGxvY2F0b3I9IjM0ODozMi0zNDg6NDIiIG5hbWU9ImZoaXI6U1BEWExpY2Vuc2UiIHhzaTp0eXBlPSJOYW1lZFR5cGVTcGVjaWZpZXIiLz4KICAgICAgICAgPC9vcGVyYW5kPgogICAgICA8L2RlZj4KICAgICAgPGRlZiBsb2NhdG9yPSIzNDk6MS0zNDk6NjEiIG5hbWU9IlRvU3RyaW5nIiBjb250ZXh0PSJQYXRpZW50IiBhY2Nlc3NMZXZlbD0iUHVibGljIiB4c2k6dHlwZT0iRnVuY3Rpb25EZWYiPgogICAgICAgICA8ZXhwcmVzc2lvbiBsb2NhdG9yPSIzNDk6NTEtMzQ5OjYxIiBwYXRoPSJ2YWx1ZSIgeHNpOnR5cGU9IlByb3BlcnR5Ij4KICAgICAgICAgICAgPHNvdXJjZSBsb2NhdG9yPSIzNDk6NTEtMzQ5OjU1IiBuYW1lPSJ2YWx1ZSIgeHNpOnR5cGU9Ik9wZXJhbmRSZWYiLz4KICAgICAgICAgPC9leHByZXNzaW9uPgogICAgICAgICA8b3BlcmFuZCBuYW1lPSJ2YWx1ZSI+CiAgICAgICAgICAgIDxvcGVyYW5kVHlwZVNwZWNpZmllciBsb2NhdG9yPSIzNDk6MzItMzQ5OjQ3IiBuYW1lPSJmaGlyOlNlYXJjaENvbXBhcmF0b3IiIHhzaTp0eXBlPSJOYW1lZFR5cGVTcGVjaWZpZXIiLz4KICAgICAgICAgPC9vcGVyYW5kPgogICAgICA8L2RlZj4KICAgICAgPGRlZiBsb2NhdG9yPSIzNTA6MS0zNTA6NjAiIG5hbWU9IlRvU3RyaW5nIiBjb250ZXh0PSJQYXRpZW50IiBhY2Nlc3NMZXZlbD0iUHVibGljIiB4c2k6dHlwZT0iRnVuY3Rpb25EZWYiPgogICAgICAgICA8ZXhwcmVzc2lvbiBsb2NhdG9yPSIzNTA6NTAtMzUwOjYwIiBwYXRoPSJ2YWx1ZSIgeHNpOnR5cGU9IlByb3BlcnR5Ij4KICAgICAgICAgICAgPHNvdXJjZSBsb2NhdG9yPSIzNTA6NTAtMzUwOjU0IiBuYW1lPSJ2YWx1ZSIgeHNpOnR5cGU9Ik9wZXJhbmRSZWYiLz4KICAgICAgICAgPC9leHByZXNzaW9uPgogICAgICAgICA8b3BlcmFuZCBuYW1lPSJ2YWx1ZSI+CiAgICAgICAgICAgIDxvcGVyYW5kVHlwZVNwZWNpZmllciBsb2NhdG9yPSIzNTA6MzItMzUwOjQ2IiBuYW1lPSJmaGlyOlNlYXJjaEVudHJ5TW9kZSIgeHNpOnR5cGU9Ik5hbWVkVHlwZVNwZWNpZmllciIvPgogICAgICAgICA8L29wZXJhbmQ+CiAgICAgIDwvZGVmPgogICAgICA8ZGVmIGxvY2F0b3I9IjM1MToxLTM1MTo2MyIgbmFtZT0iVG9TdHJpbmciIGNvbnRleHQ9IlBhdGllbnQiIGFjY2Vzc0xldmVsPSJQdWJsaWMiIHhzaTp0eXBlPSJGdW5jdGlvbkRlZiI+CiAgICAgICAgIDxleHByZXNzaW9uIGxvY2F0b3I9IjM1MTo1My0zNTE6NjMiIHBhdGg9InZhbHVlIiB4c2k6dHlwZT0iUHJvcGVydHkiPgogICAgICAgICAgICA8c291cmNlIGxvY2F0b3I9IjM1MTo1My0zNTE6NTciIG5hbWU9InZhbHVlIiB4c2k6dHlwZT0iT3BlcmFuZFJlZiIvPgogICAgICAgICA8L2V4cHJlc3Npb24+CiAgICAgICAgIDxvcGVyYW5kIG5hbWU9InZhbHVlIj4KICAgICAgICAgICAgPG9wZXJhbmRUeXBlU3BlY2lmaWVyIGxvY2F0b3I9IjM1MTozMi0zNTE6NDkiIG5hbWU9ImZoaXI6U2VhcmNoTW9kaWZpZXJDb2RlIiB4c2k6dHlwZT0iTmFtZWRUeXBlU3BlY2lmaWVyIi8+CiAgICAgICAgIDwvb3BlcmFuZD4KICAgICAgPC9kZWY+CiAgICAgIDxkZWYgbG9jYXRvcj0iMzUyOjEtMzUyOjYwIiBuYW1lPSJUb1N0cmluZyIgY29udGV4dD0iUGF0aWVudCIgYWNjZXNzTGV2ZWw9IlB1YmxpYyIgeHNpOnR5cGU9IkZ1bmN0aW9uRGVmIj4KICAgICAgICAgPGV4cHJlc3Npb24gbG9jYXRvcj0iMzUyOjUwLTM1Mjo2MCIgcGF0aD0idmFsdWUiIHhzaTp0eXBlPSJQcm9wZXJ0eSI+CiAgICAgICAgICAgIDxzb3VyY2UgbG9jYXRvcj0iMzUyOjUwLTM1Mjo1NCIgbmFtZT0idmFsdWUiIHhzaTp0eXBlPSJPcGVyYW5kUmVmIi8+CiAgICAgICAgIDwvZXhwcmVzc2lvbj4KICAgICAgICAgPG9wZXJhbmQgbmFtZT0idmFsdWUiPgogICAgICAgICAgICA8b3BlcmFuZFR5cGVTcGVjaWZpZXIgbG9jYXRvcj0iMzUyOjMyLTM1Mjo0NiIgbmFtZT0iZmhpcjpTZWFyY2hQYXJhbVR5cGUiIHhzaTp0eXBlPSJOYW1lZFR5cGVTcGVjaWZpZXIiLz4KICAgICAgICAgPC9vcGVyYW5kPgogICAgICA8L2RlZj4KICAgICAgPGRlZiBsb2NhdG9yPSIzNTM6MS0zNTM6NTYiIG5hbWU9IlRvU3RyaW5nIiBjb250ZXh0PSJQYXRpZW50IiBhY2Nlc3NMZXZlbD0iUHVibGljIiB4c2k6dHlwZT0iRnVuY3Rpb25EZWYiPgogICAgICAgICA8ZXhwcmVzc2lvbiBsb2NhdG9yPSIzNTM6NDYtMzUzOjU2IiBwYXRoPSJ2YWx1ZSIgeHNpOnR5cGU9IlByb3BlcnR5Ij4KICAgICAgICAgICAgPHNvdXJjZSBsb2NhdG9yPSIzNTM6NDYtMzUzOjUwIiBuYW1lPSJ2YWx1ZSIgeHNpOnR5cGU9Ik9wZXJhbmRSZWYiLz4KICAgICAgICAgPC9leHByZXNzaW9uPgogICAgICAgICA8b3BlcmFuZCBuYW1lPSJ2YWx1ZSI+CiAgICAgICAgICAgIDxvcGVyYW5kVHlwZVNwZWNpZmllciBsb2NhdG9yPSIzNTM6MzItMzUzOjQyIiBuYW1lPSJmaGlyOlNlY3Rpb25Nb2RlIiB4c2k6dHlwZT0iTmFtZWRUeXBlU3BlY2lmaWVyIi8+CiAgICAgICAgIDwvb3BlcmFuZD4KICAgICAgPC9kZWY+CiAgICAgIDxkZWYgbG9jYXRvcj0iMzU0OjEtMzU0OjU3IiBuYW1lPSJUb1N0cmluZyIgY29udGV4dD0iUGF0aWVudCIgYWNjZXNzTGV2ZWw9IlB1YmxpYyIgeHNpOnR5cGU9IkZ1bmN0aW9uRGVmIj4KICAgICAgICAgPGV4cHJlc3Npb24gbG9jYXRvcj0iMzU0OjQ3LTM1NDo1NyIgcGF0aD0idmFsdWUiIHhzaTp0eXBlPSJQcm9wZXJ0eSI+CiAgICAgICAgICAgIDxzb3VyY2UgbG9jYXRvcj0iMzU0OjQ3LTM1NDo1MSIgbmFtZT0idmFsdWUiIHhzaTp0eXBlPSJPcGVyYW5kUmVmIi8+CiAgICAgICAgIDwvZXhwcmVzc2lvbj4KICAgICAgICAgPG9wZXJhbmQgbmFtZT0idmFsdWUiPgogICAgICAgICAgICA8b3BlcmFuZFR5cGVTcGVjaWZpZXIgbG9jYXRvcj0iMzU0OjMyLTM1NDo0MyIgbmFtZT0iZmhpcjpTZXF1ZW5jZVR5cGUiIHhzaTp0eXBlPSJOYW1lZFR5cGVTcGVjaWZpZXIiLz4KICAgICAgICAgPC9vcGVyYW5kPgogICAgICA8L2RlZj4KICAgICAgPGRlZiBsb2NhdG9yPSIzNTU6MS0zNTU6NjUiIG5hbWU9IlRvU3RyaW5nIiBjb250ZXh0PSJQYXRpZW50IiBhY2Nlc3NMZXZlbD0iUHVibGljIiB4c2k6dHlwZT0iRnVuY3Rpb25EZWYiPgogICAgICAgICA8ZXhwcmVzc2lvbiBsb2NhdG9yPSIzNTU6NTUtMzU1OjY1IiBwYXRoPSJ2YWx1ZSIgeHNpOnR5cGU9IlByb3BlcnR5Ij4KICAgICAgICAgICAgPHNvdXJjZSBsb2NhdG9yPSIzNTU6NTUtMzU1OjU5IiBuYW1lPSJ2YWx1ZSIgeHNpOnR5cGU9Ik9wZXJhbmRSZWYiLz4KICAgICAgICAgPC9leHByZXNzaW9uPgogICAgICAgICA8b3BlcmFuZCBuYW1lPSJ2YWx1ZSI+CiAgICAgICAgICAgIDxvcGVyYW5kVHlwZVNwZWNpZmllciBsb2NhdG9yPSIzNTU6MzItMzU1OjUxIiBuYW1lPSJmaGlyOlNlcnZpY2VSZXF1ZXN0SW50ZW50IiB4c2k6dHlwZT0iTmFtZWRUeXBlU3BlY2lmaWVyIi8+CiAgICAgICAgIDwvb3BlcmFuZD4KICAgICAgPC9kZWY+CiAgICAgIDxkZWYgbG9jYXRvcj0iMzU2OjEtMzU2OjY3IiBuYW1lPSJUb1N0cmluZyIgY29udGV4dD0iUGF0aWVudCIgYWNjZXNzTGV2ZWw9IlB1YmxpYyIgeHNpOnR5cGU9IkZ1bmN0aW9uRGVmIj4KICAgICAgICAgPGV4cHJlc3Npb24gbG9jYXRvcj0iMzU2OjU3LTM1Njo2NyIgcGF0aD0idmFsdWUiIHhzaTp0eXBlPSJQcm9wZXJ0eSI+CiAgICAgICAgICAgIDxzb3VyY2UgbG9jYXRvcj0iMzU2OjU3LTM1Njo2MSIgbmFtZT0idmFsdWUiIHhzaTp0eXBlPSJPcGVyYW5kUmVmIi8+CiAgICAgICAgIDwvZXhwcmVzc2lvbj4KICAgICAgICAgPG9wZXJhbmQgbmFtZT0idmFsdWUiPgogICAgICAgICAgICA8b3BlcmFuZFR5cGVTcGVjaWZpZXIgbG9jYXRvcj0iMzU2OjMyLTM1Njo1MyIgbmFtZT0iZmhpcjpTZXJ2aWNlUmVxdWVzdFByaW9yaXR5IiB4c2k6dHlwZT0iTmFtZWRUeXBlU3BlY2lmaWVyIi8+CiAgICAgICAgIDwvb3BlcmFuZD4KICAgICAgPC9kZWY+CiAgICAgIDxkZWYgbG9jYXRvcj0iMzU3OjEtMzU3OjY1IiBuYW1lPSJUb1N0cmluZyIgY29udGV4dD0iUGF0aWVudCIgYWNjZXNzTGV2ZWw9IlB1YmxpYyIgeHNpOnR5cGU9IkZ1bmN0aW9uRGVmIj4KICAgICAgICAgPGV4cHJlc3Npb24gbG9jYXRvcj0iMzU3OjU1LTM1Nzo2NSIgcGF0aD0idmFsdWUiIHhzaTp0eXBlPSJQcm9wZXJ0eSI+CiAgICAgICAgICAgIDxzb3VyY2UgbG9jYXRvcj0iMzU3OjU1LTM1Nzo1OSIgbmFtZT0idmFsdWUiIHhzaTp0eXBlPSJPcGVyYW5kUmVmIi8+CiAgICAgICAgIDwvZXhwcmVzc2lvbj4KICAgICAgICAgPG9wZXJhbmQgbmFtZT0idmFsdWUiPgogICAgICAgICAgICA8b3BlcmFuZFR5cGVTcGVjaWZpZXIgbG9jYXRvcj0iMzU3OjMyLTM1Nzo1MSIgbmFtZT0iZmhpcjpTZXJ2aWNlUmVxdWVzdFN0YXR1cyIgeHNpOnR5cGU9Ik5hbWVkVHlwZVNwZWNpZmllciIvPgogICAgICAgICA8L29wZXJhbmQ+CiAgICAgIDwvZGVmPgogICAgICA8ZGVmIGxvY2F0b3I9IjM1ODoxLTM1ODo1NyIgbmFtZT0iVG9TdHJpbmciIGNvbnRleHQ9IlBhdGllbnQiIGFjY2Vzc0xldmVsPSJQdWJsaWMiIHhzaTp0eXBlPSJGdW5jdGlvbkRlZiI+CiAgICAgICAgIDxleHByZXNzaW9uIGxvY2F0b3I9IjM1ODo0Ny0zNTg6NTciIHBhdGg9InZhbHVlIiB4c2k6dHlwZT0iUHJvcGVydHkiPgogICAgICAgICAgICA8c291cmNlIGxvY2F0b3I9IjM1ODo0Ny0zNTg6NTEiIG5hbWU9InZhbHVlIiB4c2k6dHlwZT0iT3BlcmFuZFJlZiIvPgogICAgICAgICA8L2V4cHJlc3Npb24+CiAgICAgICAgIDxvcGVyYW5kIG5hbWU9InZhbHVlIj4KICAgICAgICAgICAgPG9wZXJhbmRUeXBlU3BlY2lmaWVyIGxvY2F0b3I9IjM1ODozMi0zNTg6NDMiIG5hbWU9ImZoaXI6U2xpY2luZ1J1bGVzIiB4c2k6dHlwZT0iTmFtZWRUeXBlU3BlY2lmaWVyIi8+CiAgICAgICAgIDwvb3BlcmFuZD4KICAgICAgPC9kZWY+CiAgICAgIDxkZWYgbG9jYXRvcj0iMzU5OjEtMzU5OjU1IiBuYW1lPSJUb1N0cmluZyIgY29udGV4dD0iUGF0aWVudCIgYWNjZXNzTGV2ZWw9IlB1YmxpYyIgeHNpOnR5cGU9IkZ1bmN0aW9uRGVmIj4KICAgICAgICAgPGV4cHJlc3Npb24gbG9jYXRvcj0iMzU5OjQ1LTM1OTo1NSIgcGF0aD0idmFsdWUiIHhzaTp0eXBlPSJQcm9wZXJ0eSI+CiAgICAgICAgICAgIDxzb3VyY2UgbG9jYXRvcj0iMzU5OjQ1LTM1OTo0OSIgbmFtZT0idmFsdWUiIHhzaTp0eXBlPSJPcGVyYW5kUmVmIi8+CiAgICAgICAgIDwvZXhwcmVzc2lvbj4KICAgICAgICAgPG9wZXJhbmQgbmFtZT0idmFsdWUiPgogICAgICAgICAgICA8b3BlcmFuZFR5cGVTcGVjaWZpZXIgbG9jYXRvcj0iMzU5OjMyLTM1OTo0MSIgbmFtZT0iZmhpcjpTbG90U3RhdHVzIiB4c2k6dHlwZT0iTmFtZWRUeXBlU3BlY2lmaWVyIi8+CiAgICAgICAgIDwvb3BlcmFuZD4KICAgICAgPC9kZWY+CiAgICAgIDxkZWYgbG9jYXRvcj0iMzYwOjEtMzYwOjU4IiBuYW1lPSJUb1N0cmluZyIgY29udGV4dD0iUGF0aWVudCIgYWNjZXNzTGV2ZWw9IlB1YmxpYyIgeHNpOnR5cGU9IkZ1bmN0aW9uRGVmIj4KICAgICAgICAgPGV4cHJlc3Npb24gbG9jYXRvcj0iMzYwOjQ4LTM2MDo1OCIgcGF0aD0idmFsdWUiIHhzaTp0eXBlPSJQcm9wZXJ0eSI+CiAgICAgICAgICAgIDxzb3VyY2UgbG9jYXRvcj0iMzYwOjQ4LTM2MDo1MiIgbmFtZT0idmFsdWUiIHhzaTp0eXBlPSJPcGVyYW5kUmVmIi8+CiAgICAgICAgIDwvZXhwcmVzc2lvbj4KICAgICAgICAgPG9wZXJhbmQgbmFtZT0idmFsdWUiPgogICAgICAgICAgICA8b3BlcmFuZFR5cGVTcGVjaWZpZXIgbG9jYXRvcj0iMzYwOjMyLTM2MDo0NCIgbmFtZT0iZmhpcjpTb3J0RGlyZWN0aW9uIiB4c2k6dHlwZT0iTmFtZWRUeXBlU3BlY2lmaWVyIi8+CiAgICAgICAgIDwvb3BlcmFuZD4KICAgICAgPC9kZWY+CiAgICAgIDxkZWYgbG9jYXRvcj0iMzYxOjEtMzYxOjcyIiBuYW1lPSJUb1N0cmluZyIgY29udGV4dD0iUGF0aWVudCIgYWNjZXNzTGV2ZWw9IlB1YmxpYyIgeHNpOnR5cGU9IkZ1bmN0aW9uRGVmIj4KICAgICAgICAgPGV4cHJlc3Npb24gbG9jYXRvcj0iMzYxOjYyLTM2MTo3MiIgcGF0aD0idmFsdWUiIHhzaTp0eXBlPSJQcm9wZXJ0eSI+CiAgICAgICAgICAgIDxzb3VyY2UgbG9jYXRvcj0iMzYxOjYyLTM2MTo2NiIgbmFtZT0idmFsdWUiIHhzaTp0eXBlPSJPcGVyYW5kUmVmIi8+CiAgICAgICAgIDwvZXhwcmVzc2lvbj4KICAgICAgICAgPG9wZXJhbmQgbmFtZT0idmFsdWUiPgogICAgICAgICAgICA8b3BlcmFuZFR5cGVTcGVjaWZpZXIgbG9jYXRvcj0iMzYxOjMyLTM2MTo1OCIgbmFtZT0iZmhpcjpTcGVjaW1lbkNvbnRhaW5lZFByZWZlcmVuY2UiIHhzaTp0eXBlPSJOYW1lZFR5cGVTcGVjaWZpZXIiLz4KICAgICAgICAgPC9vcGVyYW5kPgogICAgICA8L2RlZj4KICAgICAgPGRlZiBsb2NhdG9yPSIzNjI6MS0zNjI6NTkiIG5hbWU9IlRvU3RyaW5nIiBjb250ZXh0PSJQYXRpZW50IiBhY2Nlc3NMZXZlbD0iUHVibGljIiB4c2k6dHlwZT0iRnVuY3Rpb25EZWYiPgogICAgICAgICA8ZXhwcmVzc2lvbiBsb2NhdG9yPSIzNjI6NDktMzYyOjU5IiBwYXRoPSJ2YWx1ZSIgeHNpOnR5cGU9IlByb3BlcnR5Ij4KICAgICAgICAgICAgPHNvdXJjZSBsb2NhdG9yPSIzNjI6NDktMzYyOjUzIiBuYW1lPSJ2YWx1ZSIgeHNpOnR5cGU9Ik9wZXJhbmRSZWYiLz4KICAgICAgICAgPC9leHByZXNzaW9uPgogICAgICAgICA8b3BlcmFuZCBuYW1lPSJ2YWx1ZSI+CiAgICAgICAgICAgIDxvcGVyYW5kVHlwZVNwZWNpZmllciBsb2NhdG9yPSIzNjI6MzItMzYyOjQ1IiBuYW1lPSJmaGlyOlNwZWNpbWVuU3RhdHVzIiB4c2k6dHlwZT0iTmFtZWRUeXBlU3BlY2lmaWVyIi8+CiAgICAgICAgIDwvb3BlcmFuZD4KICAgICAgPC9kZWY+CiAgICAgIDxkZWYgbG9jYXRvcj0iMzYzOjEtMzYzOjUxIiBuYW1lPSJUb1N0cmluZyIgY29udGV4dD0iUGF0aWVudCIgYWNjZXNzTGV2ZWw9IlB1YmxpYyIgeHNpOnR5cGU9IkZ1bmN0aW9uRGVmIj4KICAgICAgICAgPGV4cHJlc3Npb24gbG9jYXRvcj0iMzYzOjQxLTM2Mzo1MSIgcGF0aD0idmFsdWUiIHhzaTp0eXBlPSJQcm9wZXJ0eSI+CiAgICAgICAgICAgIDxzb3VyY2UgbG9jYXRvcj0iMzYzOjQxLTM2Mzo0NSIgbmFtZT0idmFsdWUiIHhzaTp0eXBlPSJPcGVyYW5kUmVmIi8+CiAgICAgICAgIDwvZXhwcmVzc2lvbj4KICAgICAgICAgPG9wZXJhbmQgbmFtZT0idmFsdWUiPgogICAgICAgICAgICA8b3BlcmFuZFR5cGVTcGVjaWZpZXIgbG9jYXRvcj0iMzYzOjMyLTM2MzozNyIgbmFtZT0iZmhpcjpTdGF0dXMiIHhzaTp0eXBlPSJOYW1lZFR5cGVTcGVjaWZpZXIiLz4KICAgICAgICAgPC9vcGVyYW5kPgogICAgICA8L2RlZj4KICAgICAgPGRlZiBsb2NhdG9yPSIzNjQ6MS0zNjQ6NTUiIG5hbWU9IlRvU3RyaW5nIiBjb250ZXh0PSJQYXRpZW50IiBhY2Nlc3NMZXZlbD0iUHVibGljIiB4c2k6dHlwZT0iRnVuY3Rpb25EZWYiPgogICAgICAgICA8ZXhwcmVzc2lvbiBsb2NhdG9yPSIzNjQ6NDUtMzY0OjU1IiBwYXRoPSJ2YWx1ZSIgeHNpOnR5cGU9IlByb3BlcnR5Ij4KICAgICAgICAgICAgPHNvdXJjZSBsb2NhdG9yPSIzNjQ6NDUtMzY0OjQ5IiBuYW1lPSJ2YWx1ZSIgeHNpOnR5cGU9Ik9wZXJhbmRSZWYiLz4KICAgICAgICAgPC9leHByZXNzaW9uPgogICAgICAgICA8b3BlcmFuZCBuYW1lPSJ2YWx1ZSI+CiAgICAgICAgICAgIDxvcGVyYW5kVHlwZVNwZWNpZmllciBsb2NhdG9yPSIzNjQ6MzItMzY0OjQxIiBuYW1lPSJmaGlyOlN0cmFuZFR5cGUiIHhzaTp0eXBlPSJOYW1lZFR5cGVTcGVjaWZpZXIiLz4KICAgICAgICAgPC9vcGVyYW5kPgogICAgICA8L2RlZj4KICAgICAgPGRlZiBsb2NhdG9yPSIzNjU6MS0zNjU6NjgiIG5hbWU9IlRvU3RyaW5nIiBjb250ZXh0PSJQYXRpZW50IiBhY2Nlc3NMZXZlbD0iUHVibGljIiB4c2k6dHlwZT0iRnVuY3Rpb25EZWYiPgogICAgICAgICA8ZXhwcmVzc2lvbiBsb2NhdG9yPSIzNjU6NTgtMzY1OjY4IiBwYXRoPSJ2YWx1ZSIgeHNpOnR5cGU9IlByb3BlcnR5Ij4KICAgICAgICAgICAgPHNvdXJjZSBsb2NhdG9yPSIzNjU6NTgtMzY1OjYyIiBuYW1lPSJ2YWx1ZSIgeHNpOnR5cGU9Ik9wZXJhbmRSZWYiLz4KICAgICAgICAgPC9leHByZXNzaW9uPgogICAgICAgICA8b3BlcmFuZCBuYW1lPSJ2YWx1ZSI+CiAgICAgICAgICAgIDxvcGVyYW5kVHlwZVNwZWNpZmllciBsb2NhdG9yPSIzNjU6MzItMzY1OjU0IiBuYW1lPSJmaGlyOlN0cnVjdHVyZURlZmluaXRpb25LaW5kIiB4c2k6dHlwZT0iTmFtZWRUeXBlU3BlY2lmaWVyIi8+CiAgICAgICAgIDwvb3BlcmFuZD4KICAgICAgPC9kZWY+CiAgICAgIDxkZWYgbG9jYXRvcj0iMzY2OjEtMzY2OjY4IiBuYW1lPSJUb1N0cmluZyIgY29udGV4dD0iUGF0aWVudCIgYWNjZXNzTGV2ZWw9IlB1YmxpYyIgeHNpOnR5cGU9IkZ1bmN0aW9uRGVmIj4KICAgICAgICAgPGV4cHJlc3Npb24gbG9jYXRvcj0iMzY2OjU4LTM2Njo2OCIgcGF0aD0idmFsdWUiIHhzaTp0eXBlPSJQcm9wZXJ0eSI+CiAgICAgICAgICAgIDxzb3VyY2UgbG9jYXRvcj0iMzY2OjU4LTM2Njo2MiIgbmFtZT0idmFsdWUiIHhzaTp0eXBlPSJPcGVyYW5kUmVmIi8+CiAgICAgICAgIDwvZXhwcmVzc2lvbj4KICAgICAgICAgPG9wZXJhbmQgbmFtZT0idmFsdWUiPgogICAgICAgICAgICA8b3BlcmFuZFR5cGVTcGVjaWZpZXIgbG9jYXRvcj0iMzY2OjMyLTM2Njo1NCIgbmFtZT0iZmhpcjpTdHJ1Y3R1cmVNYXBDb250ZXh0VHlwZSIgeHNpOnR5cGU9Ik5hbWVkVHlwZVNwZWNpZmllciIvPgogICAgICAgICA8L29wZXJhbmQ+CiAgICAgIDwvZGVmPgogICAgICA8ZGVmIGxvY2F0b3I9IjM2NzoxLTM2Nzo3MCIgbmFtZT0iVG9TdHJpbmciIGNvbnRleHQ9IlBhdGllbnQiIGFjY2Vzc0xldmVsPSJQdWJsaWMiIHhzaTp0eXBlPSJGdW5jdGlvbkRlZiI+CiAgICAgICAgIDxleHByZXNzaW9uIGxvY2F0b3I9IjM2Nzo2MC0zNjc6NzAiIHBhdGg9InZhbHVlIiB4c2k6dHlwZT0iUHJvcGVydHkiPgogICAgICAgICAgICA8c291cmNlIGxvY2F0b3I9IjM2Nzo2MC0zNjc6NjQiIG5hbWU9InZhbHVlIiB4c2k6dHlwZT0iT3BlcmFuZFJlZiIvPgogICAgICAgICA8L2V4cHJlc3Npb24+CiAgICAgICAgIDxvcGVyYW5kIG5hbWU9InZhbHVlIj4KICAgICAgICAgICAgPG9wZXJhbmRUeXBlU3BlY2lmaWVyIGxvY2F0b3I9IjM2NzozMi0zNjc6NTYiIG5hbWU9ImZoaXI6U3RydWN0dXJlTWFwR3JvdXBUeXBlTW9kZSIgeHNpOnR5cGU9Ik5hbWVkVHlwZVNwZWNpZmllciIvPgogICAgICAgICA8L29wZXJhbmQ+CiAgICAgIDwvZGVmPgogICAgICA8ZGVmIGxvY2F0b3I9IjM2ODoxLTM2ODo2NiIgbmFtZT0iVG9TdHJpbmciIGNvbnRleHQ9IlBhdGllbnQiIGFjY2Vzc0xldmVsPSJQdWJsaWMiIHhzaTp0eXBlPSJGdW5jdGlvbkRlZiI+CiAgICAgICAgIDxleHByZXNzaW9uIGxvY2F0b3I9IjM2ODo1Ni0zNjg6NjYiIHBhdGg9InZhbHVlIiB4c2k6dHlwZT0iUHJvcGVydHkiPgogICAgICAgICAgICA8c291cmNlIGxvY2F0b3I9IjM2ODo1Ni0zNjg6NjAiIG5hbWU9InZhbHVlIiB4c2k6dHlwZT0iT3BlcmFuZFJlZiIvPgogICAgICAgICA8L2V4cHJlc3Npb24+CiAgICAgICAgIDxvcGVyYW5kIG5hbWU9InZhbHVlIj4KICAgICAgICAgICAgPG9wZXJhbmRUeXBlU3BlY2lmaWVyIGxvY2F0b3I9IjM2ODozMi0zNjg6NTIiIG5hbWU9ImZoaXI6U3RydWN0dXJlTWFwSW5wdXRNb2RlIiB4c2k6dHlwZT0iTmFtZWRUeXBlU3BlY2lmaWVyIi8+CiAgICAgICAgIDwvb3BlcmFuZD4KICAgICAgPC9kZWY+CiAgICAgIDxkZWYgbG9jYXRvcj0iMzY5OjEtMzY5OjY2IiBuYW1lPSJUb1N0cmluZyIgY29udGV4dD0iUGF0aWVudCIgYWNjZXNzTGV2ZWw9IlB1YmxpYyIgeHNpOnR5cGU9IkZ1bmN0aW9uRGVmIj4KICAgICAgICAgPGV4cHJlc3Npb24gbG9jYXRvcj0iMzY5OjU2LTM2OTo2NiIgcGF0aD0idmFsdWUiIHhzaTp0eXBlPSJQcm9wZXJ0eSI+CiAgICAgICAgICAgIDxzb3VyY2UgbG9jYXRvcj0iMzY5OjU2LTM2OTo2MCIgbmFtZT0idmFsdWUiIHhzaTp0eXBlPSJPcGVyYW5kUmVmIi8+CiAgICAgICAgIDwvZXhwcmVzc2lvbj4KICAgICAgICAgPG9wZXJhbmQgbmFtZT0idmFsdWUiPgogICAgICAgICAgICA8b3BlcmFuZFR5cGVTcGVjaWZpZXIgbG9jYXRvcj0iMzY5OjMyLTM2OTo1MiIgbmFtZT0iZmhpcjpTdHJ1Y3R1cmVNYXBNb2RlbE1vZGUiIHhzaTp0eXBlPSJOYW1lZFR5cGVTcGVjaWZpZXIiLz4KICAgICAgICAgPC9vcGVyYW5kPgogICAgICA8L2RlZj4KICAgICAgPGRlZiBsb2NhdG9yPSIzNzA6MS0zNzA6NzEiIG5hbWU9IlRvU3RyaW5nIiBjb250ZXh0PSJQYXRpZW50IiBhY2Nlc3NMZXZlbD0iUHVibGljIiB4c2k6dHlwZT0iRnVuY3Rpb25EZWYiPgogICAgICAgICA8ZXhwcmVzc2lvbiBsb2NhdG9yPSIzNzA6NjEtMzcwOjcxIiBwYXRoPSJ2YWx1ZSIgeHNpOnR5cGU9IlByb3BlcnR5Ij4KICAgICAgICAgICAgPHNvdXJjZSBsb2NhdG9yPSIzNzA6NjEtMzcwOjY1IiBuYW1lPSJ2YWx1ZSIgeHNpOnR5cGU9Ik9wZXJhbmRSZWYiLz4KICAgICAgICAgPC9leHByZXNzaW9uPgogICAgICAgICA8b3BlcmFuZCBuYW1lPSJ2YWx1ZSI+CiAgICAgICAgICAgIDxvcGVyYW5kVHlwZVNwZWNpZmllciBsb2NhdG9yPSIzNzA6MzItMzcwOjU3IiBuYW1lPSJmaGlyOlN0cnVjdHVyZU1hcFNvdXJjZUxpc3RNb2RlIiB4c2k6dHlwZT0iTmFtZWRUeXBlU3BlY2lmaWVyIi8+CiAgICAgICAgIDwvb3BlcmFuZD4KICAgICAgPC9kZWY+CiAgICAgIDxkZWYgbG9jYXRvcj0iMzcxOjEtMzcxOjcxIiBuYW1lPSJUb1N0cmluZyIgY29udGV4dD0iUGF0aWVudCIgYWNjZXNzTGV2ZWw9IlB1YmxpYyIgeHNpOnR5cGU9IkZ1bmN0aW9uRGVmIj4KICAgICAgICAgPGV4cHJlc3Npb24gbG9jYXRvcj0iMzcxOjYxLTM3MTo3MSIgcGF0aD0idmFsdWUiIHhzaTp0eXBlPSJQcm9wZXJ0eSI+CiAgICAgICAgICAgIDxzb3VyY2UgbG9jYXRvcj0iMzcxOjYxLTM3MTo2NSIgbmFtZT0idmFsdWUiIHhzaTp0eXBlPSJPcGVyYW5kUmVmIi8+CiAgICAgICAgIDwvZXhwcmVzc2lvbj4KICAgICAgICAgPG9wZXJhbmQgbmFtZT0idmFsdWUiPgogICAgICAgICAgICA8b3BlcmFuZFR5cGVTcGVjaWZpZXIgbG9jYXRvcj0iMzcxOjMyLTM3MTo1NyIgbmFtZT0iZmhpcjpTdHJ1Y3R1cmVNYXBUYXJnZXRMaXN0TW9kZSIgeHNpOnR5cGU9Ik5hbWVkVHlwZVNwZWNpZmllciIvPgogICAgICAgICA8L29wZXJhbmQ+CiAgICAgIDwvZGVmPgogICAgICA8ZGVmIGxvY2F0b3I9IjM3MjoxLTM3Mjo2NiIgbmFtZT0iVG9TdHJpbmciIGNvbnRleHQ9IlBhdGllbnQiIGFjY2Vzc0xldmVsPSJQdWJsaWMiIHhzaTp0eXBlPSJGdW5jdGlvbkRlZiI+CiAgICAgICAgIDxleHByZXNzaW9uIGxvY2F0b3I9IjM3Mjo1Ni0zNzI6NjYiIHBhdGg9InZhbHVlIiB4c2k6dHlwZT0iUHJvcGVydHkiPgogICAgICAgICAgICA8c291cmNlIGxvY2F0b3I9IjM3Mjo1Ni0zNzI6NjAiIG5hbWU9InZhbHVlIiB4c2k6dHlwZT0iT3BlcmFuZFJlZiIvPgogICAgICAgICA8L2V4cHJlc3Npb24+CiAgICAgICAgIDxvcGVyYW5kIG5hbWU9InZhbHVlIj4KICAgICAgICAgICAgPG9wZXJhbmRUeXBlU3BlY2lmaWVyIGxvY2F0b3I9IjM3MjozMi0zNzI6NTIiIG5hbWU9ImZoaXI6U3RydWN0dXJlTWFwVHJhbnNmb3JtIiB4c2k6dHlwZT0iTmFtZWRUeXBlU3BlY2lmaWVyIi8+CiAgICAgICAgIDwvb3BlcmFuZD4KICAgICAgPC9kZWY+CiAgICAgIDxkZWYgbG9jYXRvcj0iMzczOjEtMzczOjY4IiBuYW1lPSJUb1N0cmluZyIgY29udGV4dD0iUGF0aWVudCIgYWNjZXNzTGV2ZWw9IlB1YmxpYyIgeHNpOnR5cGU9IkZ1bmN0aW9uRGVmIj4KICAgICAgICAgPGV4cHJlc3Npb24gbG9jYXRvcj0iMzczOjU4LTM3Mzo2OCIgcGF0aD0idmFsdWUiIHhzaTp0eXBlPSJQcm9wZXJ0eSI+CiAgICAgICAgICAgIDxzb3VyY2UgbG9jYXRvcj0iMzczOjU4LTM3Mzo2MiIgbmFtZT0idmFsdWUiIHhzaTp0eXBlPSJPcGVyYW5kUmVmIi8+CiAgICAgICAgIDwvZXhwcmVzc2lvbj4KICAgICAgICAgPG9wZXJhbmQgbmFtZT0idmFsdWUiPgogICAgICAgICAgICA8b3BlcmFuZFR5cGVTcGVjaWZpZXIgbG9jYXRvcj0iMzczOjMyLTM3Mzo1NCIgbmFtZT0iZmhpcjpTdWJzY3JpcHRpb25DaGFubmVsVHlwZSIgeHNpOnR5cGU9Ik5hbWVkVHlwZVNwZWNpZmllciIvPgogICAgICAgICA8L29wZXJhbmQ+CiAgICAgIDwvZGVmPgogICAgICA8ZGVmIGxvY2F0b3I9IjM3NDoxLTM3NDo2MyIgbmFtZT0iVG9TdHJpbmciIGNvbnRleHQ9IlBhdGllbnQiIGFjY2Vzc0xldmVsPSJQdWJsaWMiIHhzaTp0eXBlPSJGdW5jdGlvbkRlZiI+CiAgICAgICAgIDxleHByZXNzaW9uIGxvY2F0b3I9IjM3NDo1My0zNzQ6NjMiIHBhdGg9InZhbHVlIiB4c2k6dHlwZT0iUHJvcGVydHkiPgogICAgICAgICAgICA8c291cmNlIGxvY2F0b3I9IjM3NDo1My0zNzQ6NTciIG5hbWU9InZhbHVlIiB4c2k6dHlwZT0iT3BlcmFuZFJlZiIvPgogICAgICAgICA8L2V4cHJlc3Npb24+CiAgICAgICAgIDxvcGVyYW5kIG5hbWU9InZhbHVlIj4KICAgICAgICAgICAgPG9wZXJhbmRUeXBlU3BlY2lmaWVyIGxvY2F0b3I9IjM3NDozMi0zNzQ6NDkiIG5hbWU9ImZoaXI6U3Vic2NyaXB0aW9uU3RhdHVzIiB4c2k6dHlwZT0iTmFtZWRUeXBlU3BlY2lmaWVyIi8+CiAgICAgICAgIDwvb3BlcmFuZD4KICAgICAgPC9kZWY+CiAgICAgIDxkZWYgbG9jYXRvcj0iMzc1OjEtMzc1OjY1IiBuYW1lPSJUb1N0cmluZyIgY29udGV4dD0iUGF0aWVudCIgYWNjZXNzTGV2ZWw9IlB1YmxpYyIgeHNpOnR5cGU9IkZ1bmN0aW9uRGVmIj4KICAgICAgICAgPGV4cHJlc3Npb24gbG9jYXRvcj0iMzc1OjU1LTM3NTo2NSIgcGF0aD0idmFsdWUiIHhzaTp0eXBlPSJQcm9wZXJ0eSI+CiAgICAgICAgICAgIDxzb3VyY2UgbG9jYXRvcj0iMzc1OjU1LTM3NTo1OSIgbmFtZT0idmFsdWUiIHhzaTp0eXBlPSJPcGVyYW5kUmVmIi8+CiAgICAgICAgIDwvZXhwcmVzc2lvbj4KICAgICAgICAgPG9wZXJhbmQgbmFtZT0idmFsdWUiPgogICAgICAgICAgICA8b3BlcmFuZFR5cGVTcGVjaWZpZXIgbG9jYXRvcj0iMzc1OjMyLTM3NTo1MSIgbmFtZT0iZmhpcjpTdXBwbHlEZWxpdmVyeVN0YXR1cyIgeHNpOnR5cGU9Ik5hbWVkVHlwZVNwZWNpZmllciIvPgogICAgICAgICA8L29wZXJhbmQ+CiAgICAgIDwvZGVmPgogICAgICA8ZGVmIGxvY2F0b3I9IjM3NjoxLTM3Njo2NCIgbmFtZT0iVG9TdHJpbmciIGNvbnRleHQ9IlBhdGllbnQiIGFjY2Vzc0xldmVsPSJQdWJsaWMiIHhzaTp0eXBlPSJGdW5jdGlvbkRlZiI+CiAgICAgICAgIDxleHByZXNzaW9uIGxvY2F0b3I9IjM3Njo1NC0zNzY6NjQiIHBhdGg9InZhbHVlIiB4c2k6dHlwZT0iUHJvcGVydHkiPgogICAgICAgICAgICA8c291cmNlIGxvY2F0b3I9IjM3Njo1NC0zNzY6NTgiIG5hbWU9InZhbHVlIiB4c2k6dHlwZT0iT3BlcmFuZFJlZiIvPgogICAgICAgICA8L2V4cHJlc3Npb24+CiAgICAgICAgIDxvcGVyYW5kIG5hbWU9InZhbHVlIj4KICAgICAgICAgICAgPG9wZXJhbmRUeXBlU3BlY2lmaWVyIGxvY2F0b3I9IjM3NjozMi0zNzY6NTAiIG5hbWU9ImZoaXI6U3VwcGx5UmVxdWVzdFN0YXR1cyIgeHNpOnR5cGU9Ik5hbWVkVHlwZVNwZWNpZmllciIvPgogICAgICAgICA8L29wZXJhbmQ+CiAgICAgIDwvZGVmPgogICAgICA8ZGVmIGxvY2F0b3I9IjM3NzoxLTM3Nzo2OSIgbmFtZT0iVG9TdHJpbmciIGNvbnRleHQ9IlBhdGllbnQiIGFjY2Vzc0xldmVsPSJQdWJsaWMiIHhzaTp0eXBlPSJGdW5jdGlvbkRlZiI+CiAgICAgICAgIDxleHByZXNzaW9uIGxvY2F0b3I9IjM3Nzo1OS0zNzc6NjkiIHBhdGg9InZhbHVlIiB4c2k6dHlwZT0iUHJvcGVydHkiPgogICAgICAgICAgICA8c291cmNlIGxvY2F0b3I9IjM3Nzo1OS0zNzc6NjMiIG5hbWU9InZhbHVlIiB4c2k6dHlwZT0iT3BlcmFuZFJlZiIvPgogICAgICAgICA8L2V4cHJlc3Npb24+CiAgICAgICAgIDxvcGVyYW5kIG5hbWU9InZhbHVlIj4KICAgICAgICAgICAgPG9wZXJhbmRUeXBlU3BlY2lmaWVyIGxvY2F0b3I9IjM3NzozMi0zNzc6NTUiIG5hbWU9ImZoaXI6U3lzdGVtUmVzdGZ1bEludGVyYWN0aW9uIiB4c2k6dHlwZT0iTmFtZWRUeXBlU3BlY2lmaWVyIi8+CiAgICAgICAgIDwvb3BlcmFuZD4KICAgICAgPC9kZWY+CiAgICAgIDxkZWYgbG9jYXRvcj0iMzc4OjEtMzc4OjU1IiBuYW1lPSJUb1N0cmluZyIgY29udGV4dD0iUGF0aWVudCIgYWNjZXNzTGV2ZWw9IlB1YmxpYyIgeHNpOnR5cGU9IkZ1bmN0aW9uRGVmIj4KICAgICAgICAgPGV4cHJlc3Npb24gbG9jYXRvcj0iMzc4OjQ1LTM3ODo1NSIgcGF0aD0idmFsdWUiIHhzaTp0eXBlPSJQcm9wZXJ0eSI+CiAgICAgICAgICAgIDxzb3VyY2UgbG9jYXRvcj0iMzc4OjQ1LTM3ODo0OSIgbmFtZT0idmFsdWUiIHhzaTp0eXBlPSJPcGVyYW5kUmVmIi8+CiAgICAgICAgIDwvZXhwcmVzc2lvbj4KICAgICAgICAgPG9wZXJhbmQgbmFtZT0idmFsdWUiPgogICAgICAgICAgICA8b3BlcmFuZFR5cGVTcGVjaWZpZXIgbG9jYXRvcj0iMzc4OjMyLTM3ODo0MSIgbmFtZT0iZmhpcjpUYXNrSW50ZW50IiB4c2k6dHlwZT0iTmFtZWRUeXBlU3BlY2lmaWVyIi8+CiAgICAgICAgIDwvb3BlcmFuZD4KICAgICAgPC9kZWY+CiAgICAgIDxkZWYgbG9jYXRvcj0iMzc5OjEtMzc5OjU3IiBuYW1lPSJUb1N0cmluZyIgY29udGV4dD0iUGF0aWVudCIgYWNjZXNzTGV2ZWw9IlB1YmxpYyIgeHNpOnR5cGU9IkZ1bmN0aW9uRGVmIj4KICAgICAgICAgPGV4cHJlc3Npb24gbG9jYXRvcj0iMzc5OjQ3LTM3OTo1NyIgcGF0aD0idmFsdWUiIHhzaTp0eXBlPSJQcm9wZXJ0eSI+CiAgICAgICAgICAgIDxzb3VyY2UgbG9jYXRvcj0iMzc5OjQ3LTM3OTo1MSIgbmFtZT0idmFsdWUiIHhzaTp0eXBlPSJPcGVyYW5kUmVmIi8+CiAgICAgICAgIDwvZXhwcmVzc2lvbj4KICAgICAgICAgPG9wZXJhbmQgbmFtZT0idmFsdWUiPgogICAgICAgICAgICA8b3BlcmFuZFR5cGVTcGVjaWZpZXIgbG9jYXRvcj0iMzc5OjMyLTM3OTo0MyIgbmFtZT0iZmhpcjpUYXNrUHJpb3JpdHkiIHhzaTp0eXBlPSJOYW1lZFR5cGVTcGVjaWZpZXIiLz4KICAgICAgICAgPC9vcGVyYW5kPgogICAgICA8L2RlZj4KICAgICAgPGRlZiBsb2NhdG9yPSIzODA6MS0zODA6NTUiIG5hbWU9IlRvU3RyaW5nIiBjb250ZXh0PSJQYXRpZW50IiBhY2Nlc3NMZXZlbD0iUHVibGljIiB4c2k6dHlwZT0iRnVuY3Rpb25EZWYiPgogICAgICAgICA8ZXhwcmVzc2lvbiBsb2NhdG9yPSIzODA6NDUtMzgwOjU1IiBwYXRoPSJ2YWx1ZSIgeHNpOnR5cGU9IlByb3BlcnR5Ij4KICAgICAgICAgICAgPHNvdXJjZSBsb2NhdG9yPSIzODA6NDUtMzgwOjQ5IiBuYW1lPSJ2YWx1ZSIgeHNpOnR5cGU9Ik9wZXJhbmRSZWYiLz4KICAgICAgICAgPC9leHByZXNzaW9uPgogICAgICAgICA8b3BlcmFuZCBuYW1lPSJ2YWx1ZSI+CiAgICAgICAgICAgIDxvcGVyYW5kVHlwZVNwZWNpZmllciBsb2NhdG9yPSIzODA6MzItMzgwOjQxIiBuYW1lPSJmaGlyOlRhc2tTdGF0dXMiIHhzaTp0eXBlPSJOYW1lZFR5cGVTcGVjaWZpZXIiLz4KICAgICAgICAgPC9vcGVyYW5kPgogICAgICA8L2RlZj4KICAgICAgPGRlZiBsb2NhdG9yPSIzODE6MS0zODE6NjciIG5hbWU9IlRvU3RyaW5nIiBjb250ZXh0PSJQYXRpZW50IiBhY2Nlc3NMZXZlbD0iUHVibGljIiB4c2k6dHlwZT0iRnVuY3Rpb25EZWYiPgogICAgICAgICA8ZXhwcmVzc2lvbiBsb2NhdG9yPSIzODE6NTctMzgxOjY3IiBwYXRoPSJ2YWx1ZSIgeHNpOnR5cGU9IlByb3BlcnR5Ij4KICAgICAgICAgICAgPHNvdXJjZSBsb2NhdG9yPSIzODE6NTctMzgxOjYxIiBuYW1lPSJ2YWx1ZSIgeHNpOnR5cGU9Ik9wZXJhbmRSZWYiLz4KICAgICAgICAgPC9leHByZXNzaW9uPgogICAgICAgICA8b3BlcmFuZCBuYW1lPSJ2YWx1ZSI+CiAgICAgICAgICAgIDxvcGVyYW5kVHlwZVNwZWNpZmllciBsb2NhdG9yPSIzODE6MzItMzgxOjUzIiBuYW1lPSJmaGlyOlRlc3RSZXBvcnRBY3Rpb25SZXN1bHQiIHhzaTp0eXBlPSJOYW1lZFR5cGVTcGVjaWZpZXIiLz4KICAgICAgICAgPC9vcGVyYW5kPgogICAgICA8L2RlZj4KICAgICAgPGRlZiBsb2NhdG9yPSIzODI6MS0zODI6NzAiIG5hbWU9IlRvU3RyaW5nIiBjb250ZXh0PSJQYXRpZW50IiBhY2Nlc3NMZXZlbD0iUHVibGljIiB4c2k6dHlwZT0iRnVuY3Rpb25EZWYiPgogICAgICAgICA8ZXhwcmVzc2lvbiBsb2NhdG9yPSIzODI6NjAtMzgyOjcwIiBwYXRoPSJ2YWx1ZSIgeHNpOnR5cGU9IlByb3BlcnR5Ij4KICAgICAgICAgICAgPHNvdXJjZSBsb2NhdG9yPSIzODI6NjAtMzgyOjY0IiBuYW1lPSJ2YWx1ZSIgeHNpOnR5cGU9Ik9wZXJhbmRSZWYiLz4KICAgICAgICAgPC9leHByZXNzaW9uPgogICAgICAgICA8b3BlcmFuZCBuYW1lPSJ2YWx1ZSI+CiAgICAgICAgICAgIDxvcGVyYW5kVHlwZVNwZWNpZmllciBsb2NhdG9yPSIzODI6MzItMzgyOjU2IiBuYW1lPSJmaGlyOlRlc3RSZXBvcnRQYXJ0aWNpcGFudFR5cGUiIHhzaTp0eXBlPSJOYW1lZFR5cGVTcGVjaWZpZXIiLz4KICAgICAgICAgPC9vcGVyYW5kPgogICAgICA8L2RlZj4KICAgICAgPGRlZiBsb2NhdG9yPSIzODM6MS0zODM6NjEiIG5hbWU9IlRvU3RyaW5nIiBjb250ZXh0PSJQYXRpZW50IiBhY2Nlc3NMZXZlbD0iUHVibGljIiB4c2k6dHlwZT0iRnVuY3Rpb25EZWYiPgogICAgICAgICA8ZXhwcmVzc2lvbiBsb2NhdG9yPSIzODM6NTEtMzgzOjYxIiBwYXRoPSJ2YWx1ZSIgeHNpOnR5cGU9IlByb3BlcnR5Ij4KICAgICAgICAgICAgPHNvdXJjZSBsb2NhdG9yPSIzODM6NTEtMzgzOjU1IiBuYW1lPSJ2YWx1ZSIgeHNpOnR5cGU9Ik9wZXJhbmRSZWYiLz4KICAgICAgICAgPC9leHByZXNzaW9uPgogICAgICAgICA8b3BlcmFuZCBuYW1lPSJ2YWx1ZSI+CiAgICAgICAgICAgIDxvcGVyYW5kVHlwZVNwZWNpZmllciBsb2NhdG9yPSIzODM6MzItMzgzOjQ3IiBuYW1lPSJmaGlyOlRlc3RSZXBvcnRSZXN1bHQiIHhzaTp0eXBlPSJOYW1lZFR5cGVTcGVjaWZpZXIiLz4KICAgICAgICAgPC9vcGVyYW5kPgogICAgICA8L2RlZj4KICAgICAgPGRlZiBsb2NhdG9yPSIzODQ6MS0zODQ6NjEiIG5hbWU9IlRvU3RyaW5nIiBjb250ZXh0PSJQYXRpZW50IiBhY2Nlc3NMZXZlbD0iUHVibGljIiB4c2k6dHlwZT0iRnVuY3Rpb25EZWYiPgogICAgICAgICA8ZXhwcmVzc2lvbiBsb2NhdG9yPSIzODQ6NTEtMzg0OjYxIiBwYXRoPSJ2YWx1ZSIgeHNpOnR5cGU9IlByb3BlcnR5Ij4KICAgICAgICAgICAgPHNvdXJjZSBsb2NhdG9yPSIzODQ6NTEtMzg0OjU1IiBuYW1lPSJ2YWx1ZSIgeHNpOnR5cGU9Ik9wZXJhbmRSZWYiLz4KICAgICAgICAgPC9leHByZXNzaW9uPgogICAgICAgICA8b3BlcmFuZCBuYW1lPSJ2YWx1ZSI+CiAgICAgICAgICAgIDxvcGVyYW5kVHlwZVNwZWNpZmllciBsb2NhdG9yPSIzODQ6MzItMzg0OjQ3IiBuYW1lPSJmaGlyOlRlc3RSZXBvcnRTdGF0dXMiIHhzaTp0eXBlPSJOYW1lZFR5cGVTcGVjaWZpZXIiLz4KICAgICAgICAgPC9vcGVyYW5kPgogICAgICA8L2RlZj4KICAgICAgPGRlZiBsb2NhdG9yPSIzODU6MS0zODU6NzIiIG5hbWU9IlRvU3RyaW5nIiBjb250ZXh0PSJQYXRpZW50IiBhY2Nlc3NMZXZlbD0iUHVibGljIiB4c2k6dHlwZT0iRnVuY3Rpb25EZWYiPgogICAgICAgICA8ZXhwcmVzc2lvbiBsb2NhdG9yPSIzODU6NjItMzg1OjcyIiBwYXRoPSJ2YWx1ZSIgeHNpOnR5cGU9IlByb3BlcnR5Ij4KICAgICAgICAgICAgPHNvdXJjZSBsb2NhdG9yPSIzODU6NjItMzg1OjY2IiBuYW1lPSJ2YWx1ZSIgeHNpOnR5cGU9Ik9wZXJhbmRSZWYiLz4KICAgICAgICAgPC9leHByZXNzaW9uPgogICAgICAgICA8b3BlcmFuZCBuYW1lPSJ2YWx1ZSI+CiAgICAgICAgICAgIDxvcGVyYW5kVHlwZVNwZWNpZmllciBsb2NhdG9yPSIzODU6MzItMzg1OjU4IiBuYW1lPSJmaGlyOlRlc3RTY3JpcHRSZXF1ZXN0TWV0aG9kQ29kZSIgeHNpOnR5cGU9Ik5hbWVkVHlwZVNwZWNpZmllciIvPgogICAgICAgICA8L29wZXJhbmQ+CiAgICAgIDwvZGVmPgogICAgICA8ZGVmIGxvY2F0b3I9IjM4NjoxLTM4Njo1NiIgbmFtZT0iVG9TdHJpbmciIGNvbnRleHQ9IlBhdGllbnQiIGFjY2Vzc0xldmVsPSJQdWJsaWMiIHhzaTp0eXBlPSJGdW5jdGlvbkRlZiI+CiAgICAgICAgIDxleHByZXNzaW9uIGxvY2F0b3I9IjM4Njo0Ni0zODY6NTYiIHBhdGg9InZhbHVlIiB4c2k6dHlwZT0iUHJvcGVydHkiPgogICAgICAgICAgICA8c291cmNlIGxvY2F0b3I9IjM4Njo0Ni0zODY6NTAiIG5hbWU9InZhbHVlIiB4c2k6dHlwZT0iT3BlcmFuZFJlZiIvPgogICAgICAgICA8L2V4cHJlc3Npb24+CiAgICAgICAgIDxvcGVyYW5kIG5hbWU9InZhbHVlIj4KICAgICAgICAgICAgPG9wZXJhbmRUeXBlU3BlY2lmaWVyIGxvY2F0b3I9IjM4NjozMi0zODY6NDIiIG5hbWU9ImZoaXI6VHJpZ2dlclR5cGUiIHhzaTp0eXBlPSJOYW1lZFR5cGVTcGVjaWZpZXIiLz4KICAgICAgICAgPC9vcGVyYW5kPgogICAgICA8L2RlZj4KICAgICAgPGRlZiBsb2NhdG9yPSIzODc6MS0zODc6NjMiIG5hbWU9IlRvU3RyaW5nIiBjb250ZXh0PSJQYXRpZW50IiBhY2Nlc3NMZXZlbD0iUHVibGljIiB4c2k6dHlwZT0iRnVuY3Rpb25EZWYiPgogICAgICAgICA8ZXhwcmVzc2lvbiBsb2NhdG9yPSIzODc6NTMtMzg3OjYzIiBwYXRoPSJ2YWx1ZSIgeHNpOnR5cGU9IlByb3BlcnR5Ij4KICAgICAgICAgICAgPHNvdXJjZSBsb2NhdG9yPSIzODc6NTMtMzg3OjU3IiBuYW1lPSJ2YWx1ZSIgeHNpOnR5cGU9Ik9wZXJhbmRSZWYiLz4KICAgICAgICAgPC9leHByZXNzaW9uPgogICAgICAgICA8b3BlcmFuZCBuYW1lPSJ2YWx1ZSI+CiAgICAgICAgICAgIDxvcGVyYW5kVHlwZVNwZWNpZmllciBsb2NhdG9yPSIzODc6MzItMzg3OjQ5IiBuYW1lPSJmaGlyOlR5cGVEZXJpdmF0aW9uUnVsZSIgeHNpOnR5cGU9Ik5hbWVkVHlwZVNwZWNpZmllciIvPgogICAgICAgICA8L29wZXJhbmQ+CiAgICAgIDwvZGVmPgogICAgICA8ZGVmIGxvY2F0b3I9IjM4ODoxLTM4ODo2NyIgbmFtZT0iVG9TdHJpbmciIGNvbnRleHQ9IlBhdGllbnQiIGFjY2Vzc0xldmVsPSJQdWJsaWMiIHhzaTp0eXBlPSJGdW5jdGlvbkRlZiI+CiAgICAgICAgIDxleHByZXNzaW9uIGxvY2F0b3I9IjM4ODo1Ny0zODg6NjciIHBhdGg9InZhbHVlIiB4c2k6dHlwZT0iUHJvcGVydHkiPgogICAgICAgICAgICA8c291cmNlIGxvY2F0b3I9IjM4ODo1Ny0zODg6NjEiIG5hbWU9InZhbHVlIiB4c2k6dHlwZT0iT3BlcmFuZFJlZiIvPgogICAgICAgICA8L2V4cHJlc3Npb24+CiAgICAgICAgIDxvcGVyYW5kIG5hbWU9InZhbHVlIj4KICAgICAgICAgICAgPG9wZXJhbmRUeXBlU3BlY2lmaWVyIGxvY2F0b3I9IjM4ODozMi0zODg6NTMiIG5hbWU9ImZoaXI6VHlwZVJlc3RmdWxJbnRlcmFjdGlvbiIgeHNpOnR5cGU9Ik5hbWVkVHlwZVNwZWNpZmllciIvPgogICAgICAgICA8L29wZXJhbmQ+CiAgICAgIDwvZGVmPgogICAgICA8ZGVmIGxvY2F0b3I9IjM4OToxLTM4OTo1NyIgbmFtZT0iVG9TdHJpbmciIGNvbnRleHQ9IlBhdGllbnQiIGFjY2Vzc0xldmVsPSJQdWJsaWMiIHhzaTp0eXBlPSJGdW5jdGlvbkRlZiI+CiAgICAgICAgIDxleHByZXNzaW9uIGxvY2F0b3I9IjM4OTo0Ny0zODk6NTciIHBhdGg9InZhbHVlIiB4c2k6dHlwZT0iUHJvcGVydHkiPgogICAgICAgICAgICA8c291cmNlIGxvY2F0b3I9IjM4OTo0Ny0zODk6NTEiIG5hbWU9InZhbHVlIiB4c2k6dHlwZT0iT3BlcmFuZFJlZiIvPgogICAgICAgICA8L2V4cHJlc3Npb24+CiAgICAgICAgIDxvcGVyYW5kIG5hbWU9InZhbHVlIj4KICAgICAgICAgICAgPG9wZXJhbmRUeXBlU3BlY2lmaWVyIGxvY2F0b3I9IjM4OTozMi0zODk6NDMiIG5hbWU9ImZoaXI6VURJRW50cnlUeXBlIiB4c2k6dHlwZT0iTmFtZWRUeXBlU3BlY2lmaWVyIi8+CiAgICAgICAgIDwvb3BlcmFuZD4KICAgICAgPC9kZWY+CiAgICAgIDxkZWYgbG9jYXRvcj0iMzkwOjEtMzkwOjU2IiBuYW1lPSJUb1N0cmluZyIgY29udGV4dD0iUGF0aWVudCIgYWNjZXNzTGV2ZWw9IlB1YmxpYyIgeHNpOnR5cGU9IkZ1bmN0aW9uRGVmIj4KICAgICAgICAgPGV4cHJlc3Npb24gbG9jYXRvcj0iMzkwOjQ2LTM5MDo1NiIgcGF0aD0idmFsdWUiIHhzaTp0eXBlPSJQcm9wZXJ0eSI+CiAgICAgICAgICAgIDxzb3VyY2UgbG9jYXRvcj0iMzkwOjQ2LTM5MDo1MCIgbmFtZT0idmFsdWUiIHhzaTp0eXBlPSJPcGVyYW5kUmVmIi8+CiAgICAgICAgIDwvZXhwcmVzc2lvbj4KICAgICAgICAgPG9wZXJhbmQgbmFtZT0idmFsdWUiPgogICAgICAgICAgICA8b3BlcmFuZFR5cGVTcGVjaWZpZXIgbG9jYXRvcj0iMzkwOjMyLTM5MDo0MiIgbmFtZT0iZmhpcjpVbml0c09mVGltZSIgeHNpOnR5cGU9Ik5hbWVkVHlwZVNwZWNpZmllciIvPgogICAgICAgICA8L29wZXJhbmQ+CiAgICAgIDwvZGVmPgogICAgICA8ZGVmIGxvY2F0b3I9IjM5MToxLTM5MTo0OCIgbmFtZT0iVG9TdHJpbmciIGNvbnRleHQ9IlBhdGllbnQiIGFjY2Vzc0xldmVsPSJQdWJsaWMiIHhzaTp0eXBlPSJGdW5jdGlvbkRlZiI+CiAgICAgICAgIDxleHByZXNzaW9uIGxvY2F0b3I9IjM5MTozOC0zOTE6NDgiIHBhdGg9InZhbHVlIiB4c2k6dHlwZT0iUHJvcGVydHkiPgogICAgICAgICAgICA8c291cmNlIGxvY2F0b3I9IjM5MTozOC0zOTE6NDIiIG5hbWU9InZhbHVlIiB4c2k6dHlwZT0iT3BlcmFuZFJlZiIvPgogICAgICAgICA8L2V4cHJlc3Npb24+CiAgICAgICAgIDxvcGVyYW5kIG5hbWU9InZhbHVlIj4KICAgICAgICAgICAgPG9wZXJhbmRUeXBlU3BlY2lmaWVyIGxvY2F0b3I9IjM5MTozMi0zOTE6MzQiIG5hbWU9ImZoaXI6VXNlIiB4c2k6dHlwZT0iTmFtZWRUeXBlU3BlY2lmaWVyIi8+CiAgICAgICAgIDwvb3BlcmFuZD4KICAgICAgPC9kZWY+CiAgICAgIDxkZWYgbG9jYXRvcj0iMzkyOjEtMzkyOjU3IiBuYW1lPSJUb1N0cmluZyIgY29udGV4dD0iUGF0aWVudCIgYWNjZXNzTGV2ZWw9IlB1YmxpYyIgeHNpOnR5cGU9IkZ1bmN0aW9uRGVmIj4KICAgICAgICAgPGV4cHJlc3Npb24gbG9jYXRvcj0iMzkyOjQ3LTM5Mjo1NyIgcGF0aD0idmFsdWUiIHhzaTp0eXBlPSJQcm9wZXJ0eSI+CiAgICAgICAgICAgIDxzb3VyY2UgbG9jYXRvcj0iMzkyOjQ3LTM5Mjo1MSIgbmFtZT0idmFsdWUiIHhzaTp0eXBlPSJPcGVyYW5kUmVmIi8+CiAgICAgICAgIDwvZXhwcmVzc2lvbj4KICAgICAgICAgPG9wZXJhbmQgbmFtZT0idmFsdWUiPgogICAgICAgICAgICA8b3BlcmFuZFR5cGVTcGVjaWZpZXIgbG9jYXRvcj0iMzkyOjMyLTM5Mjo0MyIgbmFtZT0iZmhpcjpWYXJpYWJsZVR5cGUiIHhzaTp0eXBlPSJOYW1lZFR5cGVTcGVjaWZpZXIiLz4KICAgICAgICAgPC9vcGVyYW5kPgogICAgICA8L2RlZj4KICAgICAgPGRlZiBsb2NhdG9yPSIzOTM6MS0zOTM6NTUiIG5hbWU9IlRvU3RyaW5nIiBjb250ZXh0PSJQYXRpZW50IiBhY2Nlc3NMZXZlbD0iUHVibGljIiB4c2k6dHlwZT0iRnVuY3Rpb25EZWYiPgogICAgICAgICA8ZXhwcmVzc2lvbiBsb2NhdG9yPSIzOTM6NDUtMzkzOjU1IiBwYXRoPSJ2YWx1ZSIgeHNpOnR5cGU9IlByb3BlcnR5Ij4KICAgICAgICAgICAgPHNvdXJjZSBsb2NhdG9yPSIzOTM6NDUtMzkzOjQ5IiBuYW1lPSJ2YWx1ZSIgeHNpOnR5cGU9Ik9wZXJhbmRSZWYiLz4KICAgICAgICAgPC9leHByZXNzaW9uPgogICAgICAgICA8b3BlcmFuZCBuYW1lPSJ2YWx1ZSI+CiAgICAgICAgICAgIDxvcGVyYW5kVHlwZVNwZWNpZmllciBsb2NhdG9yPSIzOTM6MzItMzkzOjQxIiBuYW1lPSJmaGlyOlZpc2lvbkJhc2UiIHhzaTp0eXBlPSJOYW1lZFR5cGVTcGVjaWZpZXIiLz4KICAgICAgICAgPC9vcGVyYW5kPgogICAgICA8L2RlZj4KICAgICAgPGRlZiBsb2NhdG9yPSIzOTQ6MS0zOTQ6NTUiIG5hbWU9IlRvU3RyaW5nIiBjb250ZXh0PSJQYXRpZW50IiBhY2Nlc3NMZXZlbD0iUHVibGljIiB4c2k6dHlwZT0iRnVuY3Rpb25EZWYiPgogICAgICAgICA8ZXhwcmVzc2lvbiBsb2NhdG9yPSIzOTQ6NDUtMzk0OjU1IiBwYXRoPSJ2YWx1ZSIgeHNpOnR5cGU9IlByb3BlcnR5Ij4KICAgICAgICAgICAgPHNvdXJjZSBsb2NhdG9yPSIzOTQ6NDUtMzk0OjQ5IiBuYW1lPSJ2YWx1ZSIgeHNpOnR5cGU9Ik9wZXJhbmRSZWYiLz4KICAgICAgICAgPC9leHByZXNzaW9uPgogICAgICAgICA8b3BlcmFuZCBuYW1lPSJ2YWx1ZSI+CiAgICAgICAgICAgIDxvcGVyYW5kVHlwZVNwZWNpZmllciBsb2NhdG9yPSIzOTQ6MzItMzk0OjQxIiBuYW1lPSJmaGlyOlZpc2lvbkV5ZXMiIHhzaTp0eXBlPSJOYW1lZFR5cGVTcGVjaWZpZXIiLz4KICAgICAgICAgPC9vcGVyYW5kPgogICAgICA8L2RlZj4KICAgICAgPGRlZiBsb2NhdG9yPSIzOTU6MS0zOTU6NTciIG5hbWU9IlRvU3RyaW5nIiBjb250ZXh0PSJQYXRpZW50IiBhY2Nlc3NMZXZlbD0iUHVibGljIiB4c2k6dHlwZT0iRnVuY3Rpb25EZWYiPgogICAgICAgICA8ZXhwcmVzc2lvbiBsb2NhdG9yPSIzOTU6NDctMzk1OjU3IiBwYXRoPSJ2YWx1ZSIgeHNpOnR5cGU9IlByb3BlcnR5Ij4KICAgICAgICAgICAgPHNvdXJjZSBsb2NhdG9yPSIzOTU6NDctMzk1OjUxIiBuYW1lPSJ2YWx1ZSIgeHNpOnR5cGU9Ik9wZXJhbmRSZWYiLz4KICAgICAgICAgPC9leHByZXNzaW9uPgogICAgICAgICA8b3BlcmFuZCBuYW1lPSJ2YWx1ZSI+CiAgICAgICAgICAgIDxvcGVyYW5kVHlwZVNwZWNpZmllciBsb2NhdG9yPSIzOTU6MzItMzk1OjQzIiBuYW1lPSJmaGlyOlZpc2lvblN0YXR1cyIgeHNpOnR5cGU9Ik5hbWVkVHlwZVNwZWNpZmllciIvPgogICAgICAgICA8L29wZXJhbmQ+CiAgICAgIDwvZGVmPgogICAgICA8ZGVmIGxvY2F0b3I9IjM5NjoxLTM5Njo1OSIgbmFtZT0iVG9TdHJpbmciIGNvbnRleHQ9IlBhdGllbnQiIGFjY2Vzc0xldmVsPSJQdWJsaWMiIHhzaTp0eXBlPSJGdW5jdGlvbkRlZiI+CiAgICAgICAgIDxleHByZXNzaW9uIGxvY2F0b3I9IjM5Njo0OS0zOTY6NTkiIHBhdGg9InZhbHVlIiB4c2k6dHlwZT0iUHJvcGVydHkiPgogICAgICAgICAgICA8c291cmNlIGxvY2F0b3I9IjM5Njo0OS0zOTY6NTMiIG5hbWU9InZhbHVlIiB4c2k6dHlwZT0iT3BlcmFuZFJlZiIvPgogICAgICAgICA8L2V4cHJlc3Npb24+CiAgICAgICAgIDxvcGVyYW5kIG5hbWU9InZhbHVlIj4KICAgICAgICAgICAgPG9wZXJhbmRUeXBlU3BlY2lmaWVyIGxvY2F0b3I9IjM5NjozMi0zOTY6NDUiIG5hbWU9ImZoaXI6WFBhdGhVc2FnZVR5cGUiIHhzaTp0eXBlPSJOYW1lZFR5cGVTcGVjaWZpZXIiLz4KICAgICAgICAgPC9vcGVyYW5kPgogICAgICA8L2RlZj4KICAgICAgPGRlZiBsb2NhdG9yPSIzOTc6MS0zOTc6NTciIG5hbWU9IlRvU3RyaW5nIiBjb250ZXh0PSJQYXRpZW50IiBhY2Nlc3NMZXZlbD0iUHVibGljIiB4c2k6dHlwZT0iRnVuY3Rpb25EZWYiPgogICAgICAgICA8ZXhwcmVzc2lvbiBsb2NhdG9yPSIzOTc6NDctMzk3OjU3IiBwYXRoPSJ2YWx1ZSIgeHNpOnR5cGU9IlByb3BlcnR5Ij4KICAgICAgICAgICAgPHNvdXJjZSBsb2NhdG9yPSIzOTc6NDctMzk3OjUxIiBuYW1lPSJ2YWx1ZSIgeHNpOnR5cGU9Ik9wZXJhbmRSZWYiLz4KICAgICAgICAgPC9leHByZXNzaW9uPgogICAgICAgICA8b3BlcmFuZCBuYW1lPSJ2YWx1ZSI+CiAgICAgICAgICAgIDxvcGVyYW5kVHlwZVNwZWNpZmllciBsb2NhdG9yPSIzOTc6MzItMzk3OjQzIiBuYW1lPSJmaGlyOmJhc2U2NEJpbmFyeSIgeHNpOnR5cGU9Ik5hbWVkVHlwZVNwZWNpZmllciIvPgogICAgICAgICA8L29wZXJhbmQ+CiAgICAgIDwvZGVmPgogICAgICA8ZGVmIGxvY2F0b3I9IjM5ODoxLTM5ODo1MyIgbmFtZT0iVG9Cb29sZWFuIiBjb250ZXh0PSJQYXRpZW50IiBhY2Nlc3NMZXZlbD0iUHVibGljIiB4c2k6dHlwZT0iRnVuY3Rpb25EZWYiPgogICAgICAgICA8ZXhwcmVzc2lvbiBsb2NhdG9yPSIzOTg6NDMtMzk4OjUzIiBwYXRoPSJ2YWx1ZSIgeHNpOnR5cGU9IlByb3BlcnR5Ij4KICAgICAgICAgICAgPHNvdXJjZSBsb2NhdG9yPSIzOTg6NDMtMzk4OjQ3IiBuYW1lPSJ2YWx1ZSIgeHNpOnR5cGU9Ik9wZXJhbmRSZWYiLz4KICAgICAgICAgPC9leHByZXNzaW9uPgogICAgICAgICA8b3BlcmFuZCBuYW1lPSJ2YWx1ZSI+CiAgICAgICAgICAgIDxvcGVyYW5kVHlwZVNwZWNpZmllciBsb2NhdG9yPSIzOTg6MzMtMzk4OjM5IiBuYW1lPSJmaGlyOmJvb2xlYW4iIHhzaTp0eXBlPSJOYW1lZFR5cGVTcGVjaWZpZXIiLz4KICAgICAgICAgPC9vcGVyYW5kPgogICAgICA8L2RlZj4KICAgICAgPGRlZiBsb2NhdG9yPSIzOTk6MS0zOTk6NDciIG5hbWU9IlRvRGF0ZSIgY29udGV4dD0iUGF0aWVudCIgYWNjZXNzTGV2ZWw9IlB1YmxpYyIgeHNpOnR5cGU9IkZ1bmN0aW9uRGVmIj4KICAgICAgICAgPGV4cHJlc3Npb24gbG9jYXRvcj0iMzk5OjM3LTM5OTo0NyIgcGF0aD0idmFsdWUiIHhzaTp0eXBlPSJQcm9wZXJ0eSI+CiAgICAgICAgICAgIDxzb3VyY2UgbG9jYXRvcj0iMzk5OjM3LTM5OTo0MSIgbmFtZT0idmFsdWUiIHhzaTp0eXBlPSJPcGVyYW5kUmVmIi8+CiAgICAgICAgIDwvZXhwcmVzc2lvbj4KICAgICAgICAgPG9wZXJhbmQgbmFtZT0idmFsdWUiPgogICAgICAgICAgICA8b3BlcmFuZFR5cGVTcGVjaWZpZXIgbG9jYXRvcj0iMzk5OjMwLTM5OTozMyIgbmFtZT0iZmhpcjpkYXRlIiB4c2k6dHlwZT0iTmFtZWRUeXBlU3BlY2lmaWVyIi8+CiAgICAgICAgIDwvb3BlcmFuZD4KICAgICAgPC9kZWY+CiAgICAgIDxkZWYgbG9jYXRvcj0iNDAwOjEtNDAwOjU1IiBuYW1lPSJUb0RhdGVUaW1lIiBjb250ZXh0PSJQYXRpZW50IiBhY2Nlc3NMZXZlbD0iUHVibGljIiB4c2k6dHlwZT0iRnVuY3Rpb25EZWYiPgogICAgICAgICA8ZXhwcmVzc2lvbiBsb2NhdG9yPSI0MDA6NDUtNDAwOjU1IiBwYXRoPSJ2YWx1ZSIgeHNpOnR5cGU9IlByb3BlcnR5Ij4KICAgICAgICAgICAgPHNvdXJjZSBsb2NhdG9yPSI0MDA6NDUtNDAwOjQ5IiBuYW1lPSJ2YWx1ZSIgeHNpOnR5cGU9Ik9wZXJhbmRSZWYiLz4KICAgICAgICAgPC9leHByZXNzaW9uPgogICAgICAgICA8b3BlcmFuZCBuYW1lPSJ2YWx1ZSI+CiAgICAgICAgICAgIDxvcGVyYW5kVHlwZVNwZWNpZmllciBsb2NhdG9yPSI0MDA6MzQtNDAwOjQxIiBuYW1lPSJmaGlyOmRhdGVUaW1lIiB4c2k6dHlwZT0iTmFtZWRUeXBlU3BlY2lmaWVyIi8+CiAgICAgICAgIDwvb3BlcmFuZD4KICAgICAgPC9kZWY+CiAgICAgIDxkZWYgbG9jYXRvcj0iNDAxOjEtNDAxOjUzIiBuYW1lPSJUb0RlY2ltYWwiIGNvbnRleHQ9IlBhdGllbnQiIGFjY2Vzc0xldmVsPSJQdWJsaWMiIHhzaTp0eXBlPSJGdW5jdGlvbkRlZiI+CiAgICAgICAgIDxleHByZXNzaW9uIGxvY2F0b3I9IjQwMTo0My00MDE6NTMiIHBhdGg9InZhbHVlIiB4c2k6dHlwZT0iUHJvcGVydHkiPgogICAgICAgICAgICA8c291cmNlIGxvY2F0b3I9IjQwMTo0My00MDE6NDciIG5hbWU9InZhbHVlIiB4c2k6dHlwZT0iT3BlcmFuZFJlZiIvPgogICAgICAgICA8L2V4cHJlc3Npb24+CiAgICAgICAgIDxvcGVyYW5kIG5hbWU9InZhbHVlIj4KICAgICAgICAgICAgPG9wZXJhbmRUeXBlU3BlY2lmaWVyIGxvY2F0b3I9IjQwMTozMy00MDE6MzkiIG5hbWU9ImZoaXI6ZGVjaW1hbCIgeHNpOnR5cGU9Ik5hbWVkVHlwZVNwZWNpZmllciIvPgogICAgICAgICA8L29wZXJhbmQ+CiAgICAgIDwvZGVmPgogICAgICA8ZGVmIGxvY2F0b3I9IjQwMjoxLTQwMjo1NCIgbmFtZT0iVG9EYXRlVGltZSIgY29udGV4dD0iUGF0aWVudCIgYWNjZXNzTGV2ZWw9IlB1YmxpYyIgeHNpOnR5cGU9IkZ1bmN0aW9uRGVmIj4KICAgICAgICAgPGV4cHJlc3Npb24gbG9jYXRvcj0iNDAyOjQ0LTQwMjo1NCIgcGF0aD0idmFsdWUiIHhzaTp0eXBlPSJQcm9wZXJ0eSI+CiAgICAgICAgICAgIDxzb3VyY2UgbG9jYXRvcj0iNDAyOjQ0LTQwMjo0OCIgbmFtZT0idmFsdWUiIHhzaTp0eXBlPSJPcGVyYW5kUmVmIi8+CiAgICAgICAgIDwvZXhwcmVzc2lvbj4KICAgICAgICAgPG9wZXJhbmQgbmFtZT0idmFsdWUiPgogICAgICAgICAgICA8b3BlcmFuZFR5cGVTcGVjaWZpZXIgbG9jYXRvcj0iNDAyOjM0LTQwMjo0MCIgbmFtZT0iZmhpcjppbnN0YW50IiB4c2k6dHlwZT0iTmFtZWRUeXBlU3BlY2lmaWVyIi8+CiAgICAgICAgIDwvb3BlcmFuZD4KICAgICAgPC9kZWY+CiAgICAgIDxkZWYgbG9jYXRvcj0iNDAzOjEtNDAzOjUzIiBuYW1lPSJUb0ludGVnZXIiIGNvbnRleHQ9IlBhdGllbnQiIGFjY2Vzc0xldmVsPSJQdWJsaWMiIHhzaTp0eXBlPSJGdW5jdGlvbkRlZiI+CiAgICAgICAgIDxleHByZXNzaW9uIGxvY2F0b3I9IjQwMzo0My00MDM6NTMiIHBhdGg9InZhbHVlIiB4c2k6dHlwZT0iUHJvcGVydHkiPgogICAgICAgICAgICA8c291cmNlIGxvY2F0b3I9IjQwMzo0My00MDM6NDciIG5hbWU9InZhbHVlIiB4c2k6dHlwZT0iT3BlcmFuZFJlZiIvPgogICAgICAgICA8L2V4cHJlc3Npb24+CiAgICAgICAgIDxvcGVyYW5kIG5hbWU9InZhbHVlIj4KICAgICAgICAgICAgPG9wZXJhbmRUeXBlU3BlY2lmaWVyIGxvY2F0b3I9IjQwMzozMy00MDM6MzkiIG5hbWU9ImZoaXI6aW50ZWdlciIgeHNpOnR5cGU9Ik5hbWVkVHlwZVNwZWNpZmllciIvPgogICAgICAgICA8L29wZXJhbmQ+CiAgICAgIDwvZGVmPgogICAgICA8ZGVmIGxvY2F0b3I9IjQwNDoxLTQwNDo1MSIgbmFtZT0iVG9TdHJpbmciIGNvbnRleHQ9IlBhdGllbnQiIGFjY2Vzc0xldmVsPSJQdWJsaWMiIHhzaTp0eXBlPSJGdW5jdGlvbkRlZiI+CiAgICAgICAgIDxleHByZXNzaW9uIGxvY2F0b3I9IjQwNDo0MS00MDQ6NTEiIHBhdGg9InZhbHVlIiB4c2k6dHlwZT0iUHJvcGVydHkiPgogICAgICAgICAgICA8c291cmNlIGxvY2F0b3I9IjQwNDo0MS00MDQ6NDUiIG5hbWU9InZhbHVlIiB4c2k6dHlwZT0iT3BlcmFuZFJlZiIvPgogICAgICAgICA8L2V4cHJlc3Npb24+CiAgICAgICAgIDxvcGVyYW5kIG5hbWU9InZhbHVlIj4KICAgICAgICAgICAgPG9wZXJhbmRUeXBlU3BlY2lmaWVyIGxvY2F0b3I9IjQwNDozMi00MDQ6MzciIG5hbWU9ImZoaXI6c3RyaW5nIiB4c2k6dHlwZT0iTmFtZWRUeXBlU3BlY2lmaWVyIi8+CiAgICAgICAgIDwvb3BlcmFuZD4KICAgICAgPC9kZWY+CiAgICAgIDxkZWYgbG9jYXRvcj0iNDA1OjEtNDA1OjQ3IiBuYW1lPSJUb1RpbWUiIGNvbnRleHQ9IlBhdGllbnQiIGFjY2Vzc0xldmVsPSJQdWJsaWMiIHhzaTp0eXBlPSJGdW5jdGlvbkRlZiI+CiAgICAgICAgIDxleHByZXNzaW9uIGxvY2F0b3I9IjQwNTozNy00MDU6NDciIHBhdGg9InZhbHVlIiB4c2k6dHlwZT0iUHJvcGVydHkiPgogICAgICAgICAgICA8c291cmNlIGxvY2F0b3I9IjQwNTozNy00MDU6NDEiIG5hbWU9InZhbHVlIiB4c2k6dHlwZT0iT3BlcmFuZFJlZiIvPgogICAgICAgICA8L2V4cHJlc3Npb24+CiAgICAgICAgIDxvcGVyYW5kIG5hbWU9InZhbHVlIj4KICAgICAgICAgICAgPG9wZXJhbmRUeXBlU3BlY2lmaWVyIGxvY2F0b3I9IjQwNTozMC00MDU6MzMiIG5hbWU9ImZoaXI6dGltZSIgeHNpOnR5cGU9Ik5hbWVkVHlwZVNwZWNpZmllciIvPgogICAgICAgICA8L29wZXJhbmQ+CiAgICAgIDwvZGVmPgogICAgICA8ZGVmIGxvY2F0b3I9IjQwNjoxLTQwNjo0OCIgbmFtZT0iVG9TdHJpbmciIGNvbnRleHQ9IlBhdGllbnQiIGFjY2Vzc0xldmVsPSJQdWJsaWMiIHhzaTp0eXBlPSJGdW5jdGlvbkRlZiI+CiAgICAgICAgIDxleHByZXNzaW9uIGxvY2F0b3I9IjQwNjozOC00MDY6NDgiIHBhdGg9InZhbHVlIiB4c2k6dHlwZT0iUHJvcGVydHkiPgogICAgICAgICAgICA8c291cmNlIGxvY2F0b3I9IjQwNjozOC00MDY6NDIiIG5hbWU9InZhbHVlIiB4c2k6dHlwZT0iT3BlcmFuZFJlZiIvPgogICAgICAgICA8L2V4cHJlc3Npb24+CiAgICAgICAgIDxvcGVyYW5kIG5hbWU9InZhbHVlIj4KICAgICAgICAgICAgPG9wZXJhbmRUeXBlU3BlY2lmaWVyIGxvY2F0b3I9IjQwNjozMi00MDY6MzQiIG5hbWU9ImZoaXI6dXJpIiB4c2k6dHlwZT0iTmFtZWRUeXBlU3BlY2lmaWVyIi8+CiAgICAgICAgIDwvb3BlcmFuZD4KICAgICAgPC9kZWY+CiAgICAgIDxkZWYgbG9jYXRvcj0iNDA3OjEtNDA3OjUwIiBuYW1lPSJUb1N0cmluZyIgY29udGV4dD0iUGF0aWVudCIgYWNjZXNzTGV2ZWw9IlB1YmxpYyIgeHNpOnR5cGU9IkZ1bmN0aW9uRGVmIj4KICAgICAgICAgPGV4cHJlc3Npb24gbG9jYXRvcj0iNDA3OjQwLTQwNzo1MCIgcGF0aD0idmFsdWUiIHhzaTp0eXBlPSJQcm9wZXJ0eSI+CiAgICAgICAgICAgIDxzb3VyY2UgbG9jYXRvcj0iNDA3OjQwLTQwNzo0NCIgbmFtZT0idmFsdWUiIHhzaTp0eXBlPSJPcGVyYW5kUmVmIi8+CiAgICAgICAgIDwvZXhwcmVzc2lvbj4KICAgICAgICAgPG9wZXJhbmQgbmFtZT0idmFsdWUiPgogICAgICAgICAgICA8b3BlcmFuZFR5cGVTcGVjaWZpZXIgbG9jYXRvcj0iNDA3OjMyLTQwNzozNiIgbmFtZT0iZmhpcjp4aHRtbCIgeHNpOnR5cGU9Ik5hbWVkVHlwZVNwZWNpZmllciIvPgogICAgICAgICA8L29wZXJhbmQ+CiAgICAgIDwvZGVmPgogICA8L3N0YXRlbWVudHM+CjwvbGlicmFyeT4K" - }, - { - "contentType": "application/elm+json", - "data": "ewogICAibGlicmFyeSIgOiB7CiAgICAgICJhbm5vdGF0aW9uIiA6IFsgewogICAgICAgICAidHJhbnNsYXRvclZlcnNpb24iIDogIjIuNy4wIiwKICAgICAgICAgInRyYW5zbGF0b3JPcHRpb25zIiA6ICJFbmFibGVMb2NhdG9ycyxEaXNhYmxlTGlzdERlbW90aW9uLERpc2FibGVMaXN0UHJvbW90aW9uIiwKICAgICAgICAgInR5cGUiIDogIkNxbFRvRWxtSW5mbyIKICAgICAgfSwgewogICAgICAgICAidHlwZSIgOiAiQW5ub3RhdGlvbiIsCiAgICAgICAgICJ0IiA6IFsgewogICAgICAgICAgICAibmFtZSIgOiAiYXV0aG9yIiwKICAgICAgICAgICAgInZhbHVlIiA6ICJCcnluIFJob2RlcyIKICAgICAgICAgfSwgewogICAgICAgICAgICAibmFtZSIgOiAiZGVzY3JpcHRpb24iLAogICAgICAgICAgICAidmFsdWUiIDogIlRoaXMgbGlicmFyeSBkZWZpbmVzIGZ1bmN0aW9ucyB0byBjb252ZXJ0IGJldHdlZW4gRkhJUlxuIGRhdGEgdHlwZXMgYW5kIENRTCBzeXN0ZW0tZGVmaW5lZCB0eXBlcywgYXMgd2VsbCBhcyBmdW5jdGlvbnMgdG8gc3VwcG9ydFxuIEZISVJQYXRoIGltcGxlbWVudGF0aW9uLiBGb3IgbW9yZSBpbmZvcm1hdGlvbiwgc2VlIHRoZSBGSElSSGVscGVycyB3aWtpIHBhZ2U6XG4gaHR0cHM6Ly9naXRodWIuY29tL2NxZnJhbWV3b3JrL2NsaW5pY2FsX3F1YWxpdHlfbGFuZ3VhZ2Uvd2lraS9GSElSSGVscGVycyIKICAgICAgICAgfSwgewogICAgICAgICAgICAibmFtZSIgOiAiYWxsb3dGbHVlbnQiLAogICAgICAgICAgICAidmFsdWUiIDogInRydWUiCiAgICAgICAgIH0gXQogICAgICB9IF0sCiAgICAgICJpZGVudGlmaWVyIiA6IHsKICAgICAgICAgImlkIiA6ICJGSElSSGVscGVycyIsCiAgICAgICAgICJzeXN0ZW0iIDogImh0dHA6Ly9jb250ZW50LmFscGhvcmEuY29tL2ZoaXIvZHFtIiwKICAgICAgICAgInZlcnNpb24iIDogIjQuMC4xIgogICAgICB9LAogICAgICAic2NoZW1hSWRlbnRpZmllciIgOiB7CiAgICAgICAgICJpZCIgOiAidXJuOmhsNy1vcmc6ZWxtIiwKICAgICAgICAgInZlcnNpb24iIDogInIxIgogICAgICB9LAogICAgICAidXNpbmdzIiA6IHsKICAgICAgICAgImRlZiIgOiBbIHsKICAgICAgICAgICAgImxvY2FsSWRlbnRpZmllciIgOiAiU3lzdGVtIiwKICAgICAgICAgICAgInVyaSIgOiAidXJuOmhsNy1vcmc6ZWxtLXR5cGVzOnIxIgogICAgICAgICB9LCB7CiAgICAgICAgICAgICJsb2NhdG9yIiA6ICIxMToxLTExOjI2IiwKICAgICAgICAgICAgImxvY2FsSWRlbnRpZmllciIgOiAiRkhJUiIsCiAgICAgICAgICAgICJ1cmkiIDogImh0dHA6Ly9obDcub3JnL2ZoaXIiLAogICAgICAgICAgICAidmVyc2lvbiIgOiAiNC4wLjEiCiAgICAgICAgIH0gXQogICAgICB9LAogICAgICAic3RhdGVtZW50cyIgOiB7CiAgICAgICAgICJkZWYiIDogWyB7CiAgICAgICAgICAgICJsb2NhdG9yIiA6ICIxMzoxLTIwOjYyIiwKICAgICAgICAgICAgIm5hbWUiIDogIlRvSW50ZXJ2YWwiLAogICAgICAgICAgICAiY29udGV4dCIgOiAiUGF0aWVudCIsCiAgICAgICAgICAgICJhY2Nlc3NMZXZlbCIgOiAiUHVibGljIiwKICAgICAgICAgICAgInR5cGUiIDogIkZ1bmN0aW9uRGVmIiwKICAgICAgICAgICAgImV4cHJlc3Npb24iIDogewogICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMTQ6NS0yMDo2MiIsCiAgICAgICAgICAgICAgICJ0eXBlIiA6ICJJZiIsCiAgICAgICAgICAgICAgICJjb25kaXRpb24iIDogewogICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMTQ6OC0xNDoyMSIsCiAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJJc051bGwiLAogICAgICAgICAgICAgICAgICAib3BlcmFuZCIgOiB7CiAgICAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIxNDo4LTE0OjEzIiwKICAgICAgICAgICAgICAgICAgICAgIm5hbWUiIDogInBlcmlvZCIsCiAgICAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJPcGVyYW5kUmVmIgogICAgICAgICAgICAgICAgICB9CiAgICAgICAgICAgICAgIH0sCiAgICAgICAgICAgICAgICJ0aGVuIiA6IHsKICAgICAgICAgICAgICAgICAgInR5cGUiIDogIkFzIiwKICAgICAgICAgICAgICAgICAgIm9wZXJhbmQiIDogewogICAgICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMTU6OS0xNToxMiIsCiAgICAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJOdWxsIgogICAgICAgICAgICAgICAgICB9LAogICAgICAgICAgICAgICAgICAiYXNUeXBlU3BlY2lmaWVyIiA6IHsKICAgICAgICAgICAgICAgICAgICAgInR5cGUiIDogIkludGVydmFsVHlwZVNwZWNpZmllciIsCiAgICAgICAgICAgICAgICAgICAgICJwb2ludFR5cGUiIDogewogICAgICAgICAgICAgICAgICAgICAgICAibmFtZSIgOiAie3VybjpobDctb3JnOmVsbS10eXBlczpyMX1EYXRlVGltZSIsCiAgICAgICAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJOYW1lZFR5cGVTcGVjaWZpZXIiCiAgICAgICAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgICAgICAgfQogICAgICAgICAgICAgICB9LAogICAgICAgICAgICAgICAiZWxzZSIgOiB7CiAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIxNzo5LTIwOjYyIiwKICAgICAgICAgICAgICAgICAgInR5cGUiIDogIklmIiwKICAgICAgICAgICAgICAgICAgImNvbmRpdGlvbiIgOiB7CiAgICAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIxNzoxMi0xNzozMyIsCiAgICAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJJc051bGwiLAogICAgICAgICAgICAgICAgICAgICAib3BlcmFuZCIgOiB7CiAgICAgICAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIxNzoxMi0xNzoyNSIsCiAgICAgICAgICAgICAgICAgICAgICAgICJwYXRoIiA6ICJzdGFydCIsCiAgICAgICAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJQcm9wZXJ0eSIsCiAgICAgICAgICAgICAgICAgICAgICAgICJzb3VyY2UiIDogewogICAgICAgICAgICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMTc6MTItMTc6MTciLAogICAgICAgICAgICAgICAgICAgICAgICAgICAibmFtZSIgOiAicGVyaW9kIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk9wZXJhbmRSZWYiCiAgICAgICAgICAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgICAgICAgICAgfQogICAgICAgICAgICAgICAgICB9LAogICAgICAgICAgICAgICAgICAidGhlbiIgOiB7CiAgICAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIxODoxMy0xODo2MiIsCiAgICAgICAgICAgICAgICAgICAgICJsb3dDbG9zZWQiIDogZmFsc2UsCiAgICAgICAgICAgICAgICAgICAgICJoaWdoQ2xvc2VkIiA6IHRydWUsCiAgICAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJJbnRlcnZhbCIsCiAgICAgICAgICAgICAgICAgICAgICJsb3ciIDogewogICAgICAgICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMTg6MjItMTg6NDEiLAogICAgICAgICAgICAgICAgICAgICAgICAicGF0aCIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAgICAgICAgICAidHlwZSIgOiAiUHJvcGVydHkiLAogICAgICAgICAgICAgICAgICAgICAgICAic291cmNlIiA6IHsKICAgICAgICAgICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjE4OjIyLTE4OjM1IiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgInBhdGgiIDogInN0YXJ0IiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgInR5cGUiIDogIlByb3BlcnR5IiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgInNvdXJjZSIgOiB7CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIxODoyMi0xODoyNyIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJwZXJpb2QiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAidHlwZSIgOiAiT3BlcmFuZFJlZiIKICAgICAgICAgICAgICAgICAgICAgICAgICAgfQogICAgICAgICAgICAgICAgICAgICAgICB9CiAgICAgICAgICAgICAgICAgICAgIH0sCiAgICAgICAgICAgICAgICAgICAgICJoaWdoIiA6IHsKICAgICAgICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjE4OjQ0LTE4OjYxIiwKICAgICAgICAgICAgICAgICAgICAgICAgInBhdGgiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgICAgICAgICAgInR5cGUiIDogIlByb3BlcnR5IiwKICAgICAgICAgICAgICAgICAgICAgICAgInNvdXJjZSIgOiB7CiAgICAgICAgICAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIxODo0NC0xODo1NSIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICJwYXRoIiA6ICJlbmQiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAidHlwZSIgOiAiUHJvcGVydHkiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAic291cmNlIiA6IHsKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjE4OjQ0LTE4OjQ5IiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIm5hbWUiIDogInBlcmlvZCIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJPcGVyYW5kUmVmIgogICAgICAgICAgICAgICAgICAgICAgICAgICB9CiAgICAgICAgICAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgICAgICAgICAgfQogICAgICAgICAgICAgICAgICB9LAogICAgICAgICAgICAgICAgICAiZWxzZSIgOiB7CiAgICAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIyMDoxMy0yMDo2MiIsCiAgICAgICAgICAgICAgICAgICAgICJsb3dDbG9zZWQiIDogdHJ1ZSwKICAgICAgICAgICAgICAgICAgICAgImhpZ2hDbG9zZWQiIDogdHJ1ZSwKICAgICAgICAgICAgICAgICAgICAgInR5cGUiIDogIkludGVydmFsIiwKICAgICAgICAgICAgICAgICAgICAgImxvdyIgOiB7CiAgICAgICAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIyMDoyMi0yMDo0MSIsCiAgICAgICAgICAgICAgICAgICAgICAgICJwYXRoIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJQcm9wZXJ0eSIsCiAgICAgICAgICAgICAgICAgICAgICAgICJzb3VyY2UiIDogewogICAgICAgICAgICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMjA6MjItMjA6MzUiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAicGF0aCIgOiAic3RhcnQiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAidHlwZSIgOiAiUHJvcGVydHkiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAic291cmNlIiA6IHsKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjIwOjIyLTIwOjI3IiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIm5hbWUiIDogInBlcmlvZCIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJPcGVyYW5kUmVmIgogICAgICAgICAgICAgICAgICAgICAgICAgICB9CiAgICAgICAgICAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgICAgICAgICAgfSwKICAgICAgICAgICAgICAgICAgICAgImhpZ2giIDogewogICAgICAgICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMjA6NDQtMjA6NjEiLAogICAgICAgICAgICAgICAgICAgICAgICAicGF0aCIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAgICAgICAgICAidHlwZSIgOiAiUHJvcGVydHkiLAogICAgICAgICAgICAgICAgICAgICAgICAic291cmNlIiA6IHsKICAgICAgICAgICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjIwOjQ0LTIwOjU1IiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgInBhdGgiIDogImVuZCIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJQcm9wZXJ0eSIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICJzb3VyY2UiIDogewogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMjA6NDQtMjA6NDkiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAibmFtZSIgOiAicGVyaW9kIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk9wZXJhbmRSZWYiCiAgICAgICAgICAgICAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgICAgICAgICAgICAgfQogICAgICAgICAgICAgICAgICAgICB9CiAgICAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgICAgfQogICAgICAgICAgICB9LAogICAgICAgICAgICAib3BlcmFuZCIgOiBbIHsKICAgICAgICAgICAgICAgIm5hbWUiIDogInBlcmlvZCIsCiAgICAgICAgICAgICAgICJvcGVyYW5kVHlwZVNwZWNpZmllciIgOiB7CiAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIxMzozNS0xMzo0NSIsCiAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJ7aHR0cDovL2hsNy5vcmcvZmhpcn1QZXJpb2QiLAogICAgICAgICAgICAgICAgICAidHlwZSIgOiAiTmFtZWRUeXBlU3BlY2lmaWVyIgogICAgICAgICAgICAgICB9CiAgICAgICAgICAgIH0gXQogICAgICAgICB9LCB7CiAgICAgICAgICAgICJsb2NhdG9yIiA6ICIyMjoxLTMzOjciLAogICAgICAgICAgICAibmFtZSIgOiAiVG9DYWxlbmRhclVuaXQiLAogICAgICAgICAgICAiY29udGV4dCIgOiAiUGF0aWVudCIsCiAgICAgICAgICAgICJhY2Nlc3NMZXZlbCIgOiAiUHVibGljIiwKICAgICAgICAgICAgInR5cGUiIDogIkZ1bmN0aW9uRGVmIiwKICAgICAgICAgICAgImV4cHJlc3Npb24iIDogewogICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMjM6NS0zMzo3IiwKICAgICAgICAgICAgICAgInR5cGUiIDogIkNhc2UiLAogICAgICAgICAgICAgICAiY29tcGFyYW5kIiA6IHsKICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjIzOjEwLTIzOjEzIiwKICAgICAgICAgICAgICAgICAgIm5hbWUiIDogInVuaXQiLAogICAgICAgICAgICAgICAgICAidHlwZSIgOiAiT3BlcmFuZFJlZiIKICAgICAgICAgICAgICAgfSwKICAgICAgICAgICAgICAgImNhc2VJdGVtIiA6IFsgewogICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMjQ6OS0yNDozNiIsCiAgICAgICAgICAgICAgICAgICJ3aGVuIiA6IHsKICAgICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjI0OjE0LTI0OjE3IiwKICAgICAgICAgICAgICAgICAgICAgInZhbHVlVHlwZSIgOiAie3VybjpobDctb3JnOmVsbS10eXBlczpyMX1TdHJpbmciLAogICAgICAgICAgICAgICAgICAgICAidmFsdWUiIDogIm1zIiwKICAgICAgICAgICAgICAgICAgICAgInR5cGUiIDogIkxpdGVyYWwiCiAgICAgICAgICAgICAgICAgIH0sCiAgICAgICAgICAgICAgICAgICJ0aGVuIiA6IHsKICAgICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjI0OjI0LTI0OjM2IiwKICAgICAgICAgICAgICAgICAgICAgInZhbHVlVHlwZSIgOiAie3VybjpobDctb3JnOmVsbS10eXBlczpyMX1TdHJpbmciLAogICAgICAgICAgICAgICAgICAgICAidmFsdWUiIDogIm1pbGxpc2Vjb25kIiwKICAgICAgICAgICAgICAgICAgICAgInR5cGUiIDogIkxpdGVyYWwiCiAgICAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgICAgfSwgewogICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMjU6OS0yNTozMCIsCiAgICAgICAgICAgICAgICAgICJ3aGVuIiA6IHsKICAgICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjI1OjE0LTI1OjE2IiwKICAgICAgICAgICAgICAgICAgICAgInZhbHVlVHlwZSIgOiAie3VybjpobDctb3JnOmVsbS10eXBlczpyMX1TdHJpbmciLAogICAgICAgICAgICAgICAgICAgICAidmFsdWUiIDogInMiLAogICAgICAgICAgICAgICAgICAgICAidHlwZSIgOiAiTGl0ZXJhbCIKICAgICAgICAgICAgICAgICAgfSwKICAgICAgICAgICAgICAgICAgInRoZW4iIDogewogICAgICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMjU6MjMtMjU6MzAiLAogICAgICAgICAgICAgICAgICAgICAidmFsdWVUeXBlIiA6ICJ7dXJuOmhsNy1vcmc6ZWxtLXR5cGVzOnIxfVN0cmluZyIsCiAgICAgICAgICAgICAgICAgICAgICJ2YWx1ZSIgOiAic2Vjb25kIiwKICAgICAgICAgICAgICAgICAgICAgInR5cGUiIDogIkxpdGVyYWwiCiAgICAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgICAgfSwgewogICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMjY6OS0yNjozMiIsCiAgICAgICAgICAgICAgICAgICJ3aGVuIiA6IHsKICAgICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjI2OjE0LTI2OjE4IiwKICAgICAgICAgICAgICAgICAgICAgInZhbHVlVHlwZSIgOiAie3VybjpobDctb3JnOmVsbS10eXBlczpyMX1TdHJpbmciLAogICAgICAgICAgICAgICAgICAgICAidmFsdWUiIDogIm1pbiIsCiAgICAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJMaXRlcmFsIgogICAgICAgICAgICAgICAgICB9LAogICAgICAgICAgICAgICAgICAidGhlbiIgOiB7CiAgICAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIyNjoyNS0yNjozMiIsCiAgICAgICAgICAgICAgICAgICAgICJ2YWx1ZVR5cGUiIDogInt1cm46aGw3LW9yZzplbG0tdHlwZXM6cjF9U3RyaW5nIiwKICAgICAgICAgICAgICAgICAgICAgInZhbHVlIiA6ICJtaW51dGUiLAogICAgICAgICAgICAgICAgICAgICAidHlwZSIgOiAiTGl0ZXJhbCIKICAgICAgICAgICAgICAgICAgfQogICAgICAgICAgICAgICB9LCB7CiAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIyNzo5LTI3OjI4IiwKICAgICAgICAgICAgICAgICAgIndoZW4iIDogewogICAgICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMjc6MTQtMjc6MTYiLAogICAgICAgICAgICAgICAgICAgICAidmFsdWVUeXBlIiA6ICJ7dXJuOmhsNy1vcmc6ZWxtLXR5cGVzOnIxfVN0cmluZyIsCiAgICAgICAgICAgICAgICAgICAgICJ2YWx1ZSIgOiAiaCIsCiAgICAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJMaXRlcmFsIgogICAgICAgICAgICAgICAgICB9LAogICAgICAgICAgICAgICAgICAidGhlbiIgOiB7CiAgICAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIyNzoyMy0yNzoyOCIsCiAgICAgICAgICAgICAgICAgICAgICJ2YWx1ZVR5cGUiIDogInt1cm46aGw3LW9yZzplbG0tdHlwZXM6cjF9U3RyaW5nIiwKICAgICAgICAgICAgICAgICAgICAgInZhbHVlIiA6ICJob3VyIiwKICAgICAgICAgICAgICAgICAgICAgInR5cGUiIDogIkxpdGVyYWwiCiAgICAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgICAgfSwgewogICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMjg6OS0yODoyNyIsCiAgICAgICAgICAgICAgICAgICJ3aGVuIiA6IHsKICAgICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjI4OjE0LTI4OjE2IiwKICAgICAgICAgICAgICAgICAgICAgInZhbHVlVHlwZSIgOiAie3VybjpobDctb3JnOmVsbS10eXBlczpyMX1TdHJpbmciLAogICAgICAgICAgICAgICAgICAgICAidmFsdWUiIDogImQiLAogICAgICAgICAgICAgICAgICAgICAidHlwZSIgOiAiTGl0ZXJhbCIKICAgICAgICAgICAgICAgICAgfSwKICAgICAgICAgICAgICAgICAgInRoZW4iIDogewogICAgICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMjg6MjMtMjg6MjciLAogICAgICAgICAgICAgICAgICAgICAidmFsdWVUeXBlIiA6ICJ7dXJuOmhsNy1vcmc6ZWxtLXR5cGVzOnIxfVN0cmluZyIsCiAgICAgICAgICAgICAgICAgICAgICJ2YWx1ZSIgOiAiZGF5IiwKICAgICAgICAgICAgICAgICAgICAgInR5cGUiIDogIkxpdGVyYWwiCiAgICAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgICAgfSwgewogICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMjk6OS0yOToyOSIsCiAgICAgICAgICAgICAgICAgICJ3aGVuIiA6IHsKICAgICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjI5OjE0LTI5OjE3IiwKICAgICAgICAgICAgICAgICAgICAgInZhbHVlVHlwZSIgOiAie3VybjpobDctb3JnOmVsbS10eXBlczpyMX1TdHJpbmciLAogICAgICAgICAgICAgICAgICAgICAidmFsdWUiIDogIndrIiwKICAgICAgICAgICAgICAgICAgICAgInR5cGUiIDogIkxpdGVyYWwiCiAgICAgICAgICAgICAgICAgIH0sCiAgICAgICAgICAgICAgICAgICJ0aGVuIiA6IHsKICAgICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjI5OjI0LTI5OjI5IiwKICAgICAgICAgICAgICAgICAgICAgInZhbHVlVHlwZSIgOiAie3VybjpobDctb3JnOmVsbS10eXBlczpyMX1TdHJpbmciLAogICAgICAgICAgICAgICAgICAgICAidmFsdWUiIDogIndlZWsiLAogICAgICAgICAgICAgICAgICAgICAidHlwZSIgOiAiTGl0ZXJhbCIKICAgICAgICAgICAgICAgICAgfQogICAgICAgICAgICAgICB9LCB7CiAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIzMDo5LTMwOjMwIiwKICAgICAgICAgICAgICAgICAgIndoZW4iIDogewogICAgICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMzA6MTQtMzA6MTciLAogICAgICAgICAgICAgICAgICAgICAidmFsdWVUeXBlIiA6ICJ7dXJuOmhsNy1vcmc6ZWxtLXR5cGVzOnIxfVN0cmluZyIsCiAgICAgICAgICAgICAgICAgICAgICJ2YWx1ZSIgOiAibW8iLAogICAgICAgICAgICAgICAgICAgICAidHlwZSIgOiAiTGl0ZXJhbCIKICAgICAgICAgICAgICAgICAgfSwKICAgICAgICAgICAgICAgICAgInRoZW4iIDogewogICAgICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMzA6MjQtMzA6MzAiLAogICAgICAgICAgICAgICAgICAgICAidmFsdWVUeXBlIiA6ICJ7dXJuOmhsNy1vcmc6ZWxtLXR5cGVzOnIxfVN0cmluZyIsCiAgICAgICAgICAgICAgICAgICAgICJ2YWx1ZSIgOiAibW9udGgiLAogICAgICAgICAgICAgICAgICAgICAidHlwZSIgOiAiTGl0ZXJhbCIKICAgICAgICAgICAgICAgICAgfQogICAgICAgICAgICAgICB9LCB7CiAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIzMTo5LTMxOjI4IiwKICAgICAgICAgICAgICAgICAgIndoZW4iIDogewogICAgICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMzE6MTQtMzE6MTYiLAogICAgICAgICAgICAgICAgICAgICAidmFsdWVUeXBlIiA6ICJ7dXJuOmhsNy1vcmc6ZWxtLXR5cGVzOnIxfVN0cmluZyIsCiAgICAgICAgICAgICAgICAgICAgICJ2YWx1ZSIgOiAiYSIsCiAgICAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJMaXRlcmFsIgogICAgICAgICAgICAgICAgICB9LAogICAgICAgICAgICAgICAgICAidGhlbiIgOiB7CiAgICAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIzMToyMy0zMToyOCIsCiAgICAgICAgICAgICAgICAgICAgICJ2YWx1ZVR5cGUiIDogInt1cm46aGw3LW9yZzplbG0tdHlwZXM6cjF9U3RyaW5nIiwKICAgICAgICAgICAgICAgICAgICAgInZhbHVlIiA6ICJ5ZWFyIiwKICAgICAgICAgICAgICAgICAgICAgInR5cGUiIDogIkxpdGVyYWwiCiAgICAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgICAgfSBdLAogICAgICAgICAgICAgICAiZWxzZSIgOiB7CiAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIzMjoxNC0zMjoxNyIsCiAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJ1bml0IiwKICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk9wZXJhbmRSZWYiCiAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgfSwKICAgICAgICAgICAgIm9wZXJhbmQiIDogWyB7CiAgICAgICAgICAgICAgICJuYW1lIiA6ICJ1bml0IiwKICAgICAgICAgICAgICAgIm9wZXJhbmRUeXBlU3BlY2lmaWVyIiA6IHsKICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjIyOjM3LTIyOjQ5IiwKICAgICAgICAgICAgICAgICAgIm5hbWUiIDogInt1cm46aGw3LW9yZzplbG0tdHlwZXM6cjF9U3RyaW5nIiwKICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk5hbWVkVHlwZVNwZWNpZmllciIKICAgICAgICAgICAgICAgfQogICAgICAgICAgICB9IF0KICAgICAgICAgfSwgewogICAgICAgICAgICAibG9jYXRvciIgOiAiMzU6MS00Njo3IiwKICAgICAgICAgICAgIm5hbWUiIDogIlRvUXVhbnRpdHkiLAogICAgICAgICAgICAiY29udGV4dCIgOiAiUGF0aWVudCIsCiAgICAgICAgICAgICJhY2Nlc3NMZXZlbCIgOiAiUHVibGljIiwKICAgICAgICAgICAgInR5cGUiIDogIkZ1bmN0aW9uRGVmIiwKICAgICAgICAgICAgImV4cHJlc3Npb24iIDogewogICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMzY6NS00Njo3IiwKICAgICAgICAgICAgICAgInR5cGUiIDogIkNhc2UiLAogICAgICAgICAgICAgICAiY2FzZUl0ZW0iIDogWyB7CiAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIzNzo5LTM3OjM5IiwKICAgICAgICAgICAgICAgICAgIndoZW4iIDogewogICAgICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMzc6MTQtMzc6MjkiLAogICAgICAgICAgICAgICAgICAgICAidHlwZSIgOiAiSXNOdWxsIiwKICAgICAgICAgICAgICAgICAgICAgIm9wZXJhbmQiIDogewogICAgICAgICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMzc6MTQtMzc6MjEiLAogICAgICAgICAgICAgICAgICAgICAgICAibmFtZSIgOiAicXVhbnRpdHkiLAogICAgICAgICAgICAgICAgICAgICAgICAidHlwZSIgOiAiT3BlcmFuZFJlZiIKICAgICAgICAgICAgICAgICAgICAgfQogICAgICAgICAgICAgICAgICB9LAogICAgICAgICAgICAgICAgICAidGhlbiIgOiB7CiAgICAgICAgICAgICAgICAgICAgICJhc1R5cGUiIDogInt1cm46aGw3LW9yZzplbG0tdHlwZXM6cjF9UXVhbnRpdHkiLAogICAgICAgICAgICAgICAgICAgICAidHlwZSIgOiAiQXMiLAogICAgICAgICAgICAgICAgICAgICAib3BlcmFuZCIgOiB7CiAgICAgICAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIzNzozNi0zNzozOSIsCiAgICAgICAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJOdWxsIgogICAgICAgICAgICAgICAgICAgICB9CiAgICAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgICAgfSwgewogICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMzg6OS0zODo0NSIsCiAgICAgICAgICAgICAgICAgICJ3aGVuIiA6IHsKICAgICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjM4OjE0LTM4OjM1IiwKICAgICAgICAgICAgICAgICAgICAgInR5cGUiIDogIklzTnVsbCIsCiAgICAgICAgICAgICAgICAgICAgICJvcGVyYW5kIiA6IHsKICAgICAgICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjM4OjE0LTM4OjI3IiwKICAgICAgICAgICAgICAgICAgICAgICAgInBhdGgiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgICAgICAgICAgInR5cGUiIDogIlByb3BlcnR5IiwKICAgICAgICAgICAgICAgICAgICAgICAgInNvdXJjZSIgOiB7CiAgICAgICAgICAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIzODoxNC0zODoyMSIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJxdWFudGl0eSIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJPcGVyYW5kUmVmIgogICAgICAgICAgICAgICAgICAgICAgICB9CiAgICAgICAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgICAgICAgfSwKICAgICAgICAgICAgICAgICAgInRoZW4iIDogewogICAgICAgICAgICAgICAgICAgICAiYXNUeXBlIiA6ICJ7dXJuOmhsNy1vcmc6ZWxtLXR5cGVzOnIxfVF1YW50aXR5IiwKICAgICAgICAgICAgICAgICAgICAgInR5cGUiIDogIkFzIiwKICAgICAgICAgICAgICAgICAgICAgIm9wZXJhbmQiIDogewogICAgICAgICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMzg6NDItMzg6NDUiLAogICAgICAgICAgICAgICAgICAgICAgICAidHlwZSIgOiAiTnVsbCIKICAgICAgICAgICAgICAgICAgICAgfQogICAgICAgICAgICAgICAgICB9CiAgICAgICAgICAgICAgIH0sIHsKICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjM5OjktNDA6MTg5IiwKICAgICAgICAgICAgICAgICAgIndoZW4iIDogewogICAgICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMzk6MTQtMzk6NDQiLAogICAgICAgICAgICAgICAgICAgICAidHlwZSIgOiAiTm90IiwKICAgICAgICAgICAgICAgICAgICAgIm9wZXJhbmQiIDogewogICAgICAgICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMzk6MTQtMzk6NDQiLAogICAgICAgICAgICAgICAgICAgICAgICAidHlwZSIgOiAiSXNOdWxsIiwKICAgICAgICAgICAgICAgICAgICAgICAgIm9wZXJhbmQiIDogewogICAgICAgICAgICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMzk6MTQtMzk6MzIiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAicGF0aCIgOiAiY29tcGFyYXRvciIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJQcm9wZXJ0eSIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICJzb3VyY2UiIDogewogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMzk6MTQtMzk6MjEiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAibmFtZSIgOiAicXVhbnRpdHkiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAidHlwZSIgOiAiT3BlcmFuZFJlZiIKICAgICAgICAgICAgICAgICAgICAgICAgICAgfQogICAgICAgICAgICAgICAgICAgICAgICB9CiAgICAgICAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgICAgICAgfSwKICAgICAgICAgICAgICAgICAgInRoZW4iIDogewogICAgICAgICAgICAgICAgICAgICAiYXNUeXBlIiA6ICJ7dXJuOmhsNy1vcmc6ZWxtLXR5cGVzOnIxfVF1YW50aXR5IiwKICAgICAgICAgICAgICAgICAgICAgInR5cGUiIDogIkFzIiwKICAgICAgICAgICAgICAgICAgICAgIm9wZXJhbmQiIDogewogICAgICAgICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiNDA6MTMtNDA6MTg5IiwKICAgICAgICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk1lc3NhZ2UiLAogICAgICAgICAgICAgICAgICAgICAgICAic291cmNlIiA6IHsKICAgICAgICAgICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjQwOjIxLTQwOjI0IiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk51bGwiCiAgICAgICAgICAgICAgICAgICAgICAgIH0sCiAgICAgICAgICAgICAgICAgICAgICAgICJjb25kaXRpb24iIDogewogICAgICAgICAgICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiNDA6MjctNDA6MzAiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAidmFsdWVUeXBlIiA6ICJ7dXJuOmhsNy1vcmc6ZWxtLXR5cGVzOnIxfUJvb2xlYW4iLAogICAgICAgICAgICAgICAgICAgICAgICAgICAidmFsdWUiIDogInRydWUiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAidHlwZSIgOiAiTGl0ZXJhbCIKICAgICAgICAgICAgICAgICAgICAgICAgfSwKICAgICAgICAgICAgICAgICAgICAgICAgImNvZGUiIDogewogICAgICAgICAgICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiNDA6MzMtNDA6ODciLAogICAgICAgICAgICAgICAgICAgICAgICAgICAidmFsdWVUeXBlIiA6ICJ7dXJuOmhsNy1vcmc6ZWxtLXR5cGVzOnIxfVN0cmluZyIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICJ2YWx1ZSIgOiAiRkhJUkhlbHBlcnMuVG9RdWFudGl0eS5Db21wYXJhdG9yUXVhbnRpdHlOb3RTdXBwb3J0ZWQiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAidHlwZSIgOiAiTGl0ZXJhbCIKICAgICAgICAgICAgICAgICAgICAgICAgfSwKICAgICAgICAgICAgICAgICAgICAgICAgInNldmVyaXR5IiA6IHsKICAgICAgICAgICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjQwOjkwLTQwOjk2IiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgInZhbHVlVHlwZSIgOiAie3VybjpobDctb3JnOmVsbS10eXBlczpyMX1TdHJpbmciLAogICAgICAgICAgICAgICAgICAgICAgICAgICAidmFsdWUiIDogIkVycm9yIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgInR5cGUiIDogIkxpdGVyYWwiCiAgICAgICAgICAgICAgICAgICAgICAgIH0sCiAgICAgICAgICAgICAgICAgICAgICAgICJtZXNzYWdlIiA6IHsKICAgICAgICAgICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjQwOjk5LTQwOjE4OCIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICJ2YWx1ZVR5cGUiIDogInt1cm46aGw3LW9yZzplbG0tdHlwZXM6cjF9U3RyaW5nIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgInZhbHVlIiA6ICJGSElSIFF1YW50aXR5IHZhbHVlIGhhcyBhIGNvbXBhcmF0b3IgYW5kIGNhbm5vdCBiZSBjb252ZXJ0ZWQgdG8gYSBTeXN0ZW0uUXVhbnRpdHkgdmFsdWUuIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgInR5cGUiIDogIkxpdGVyYWwiCiAgICAgICAgICAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgICAgICAgICAgfQogICAgICAgICAgICAgICAgICB9CiAgICAgICAgICAgICAgIH0sIHsKICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjQxOjktNDM6MTM4IiwKICAgICAgICAgICAgICAgICAgIndoZW4iIDogewogICAgICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiNDE6MTQtNDI6OTIiLAogICAgICAgICAgICAgICAgICAgICAidHlwZSIgOiAiT3IiLAogICAgICAgICAgICAgICAgICAgICAib3BlcmFuZCIgOiBbIHsKICAgICAgICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjQxOjE0LTQxOjkxIiwKICAgICAgICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk9yIiwKICAgICAgICAgICAgICAgICAgICAgICAgIm9wZXJhbmQiIDogWyB7CiAgICAgICAgICAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICI0MToxNC00MTozNiIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJJc051bGwiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAib3BlcmFuZCIgOiB7CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICI0MToxNC00MToyOCIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJwYXRoIiA6ICJzeXN0ZW0iLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAidHlwZSIgOiAiUHJvcGVydHkiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAic291cmNlIiA6IHsKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjQxOjE0LTQxOjIxIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIm5hbWUiIDogInF1YW50aXR5IiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk9wZXJhbmRSZWYiCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgICAgICAgICAgICAgICAgfQogICAgICAgICAgICAgICAgICAgICAgICB9LCB7CiAgICAgICAgICAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICI0MTo0MS00MTo5MSIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJFcXVhbCIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICJvcGVyYW5kIiA6IFsgewogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiNDE6NDEtNDE6NjEiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAicGF0aCIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAidHlwZSIgOiAiUHJvcGVydHkiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAic291cmNlIiA6IHsKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjQxOjQxLTQxOjU1IiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInBhdGgiIDogInN5c3RlbSIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJQcm9wZXJ0eSIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJzb3VyY2UiIDogewogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiNDE6NDEtNDE6NDgiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAibmFtZSIgOiAicXVhbnRpdHkiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAidHlwZSIgOiAiT3BlcmFuZFJlZiIKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICB9CiAgICAgICAgICAgICAgICAgICAgICAgICAgIH0sIHsKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjQxOjY1LTQxOjkxIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInZhbHVlVHlwZSIgOiAie3VybjpobDctb3JnOmVsbS10eXBlczpyMX1TdHJpbmciLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAidmFsdWUiIDogImh0dHA6Ly91bml0c29mbWVhc3VyZS5vcmciLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAidHlwZSIgOiAiTGl0ZXJhbCIKICAgICAgICAgICAgICAgICAgICAgICAgICAgfSBdCiAgICAgICAgICAgICAgICAgICAgICAgIH0gXQogICAgICAgICAgICAgICAgICAgICB9LCB7CiAgICAgICAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICI0MjoxOC00Mjo5MiIsCiAgICAgICAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJFcXVhbCIsCiAgICAgICAgICAgICAgICAgICAgICAgICJvcGVyYW5kIiA6IFsgewogICAgICAgICAgICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiNDI6MTgtNDI6MzgiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAicGF0aCIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAidHlwZSIgOiAiUHJvcGVydHkiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAic291cmNlIiA6IHsKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjQyOjE4LTQyOjMyIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInBhdGgiIDogInN5c3RlbSIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJQcm9wZXJ0eSIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJzb3VyY2UiIDogewogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiNDI6MTgtNDI6MjUiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAibmFtZSIgOiAicXVhbnRpdHkiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAidHlwZSIgOiAiT3BlcmFuZFJlZiIKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfQogICAgICAgICAgICAgICAgICAgICAgICAgICB9CiAgICAgICAgICAgICAgICAgICAgICAgIH0sIHsKICAgICAgICAgICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjQyOjQyLTQyOjkyIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgInZhbHVlVHlwZSIgOiAie3VybjpobDctb3JnOmVsbS10eXBlczpyMX1TdHJpbmciLAogICAgICAgICAgICAgICAgICAgICAgICAgICAidmFsdWUiIDogImh0dHA6Ly9obDcub3JnL2ZoaXJwYXRoL0NvZGVTeXN0ZW0vY2FsZW5kYXItdW5pdHMiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAidHlwZSIgOiAiTGl0ZXJhbCIKICAgICAgICAgICAgICAgICAgICAgICAgfSBdCiAgICAgICAgICAgICAgICAgICAgIH0gXQogICAgICAgICAgICAgICAgICB9LAogICAgICAgICAgICAgICAgICAidGhlbiIgOiB7CiAgICAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICI0MzoxMy00MzoxMzgiLAogICAgICAgICAgICAgICAgICAgICAiY2xhc3NUeXBlIiA6ICJ7dXJuOmhsNy1vcmc6ZWxtLXR5cGVzOnIxfVF1YW50aXR5IiwKICAgICAgICAgICAgICAgICAgICAgInR5cGUiIDogIkluc3RhbmNlIiwKICAgICAgICAgICAgICAgICAgICAgImVsZW1lbnQiIDogWyB7CiAgICAgICAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICAgICAgICAgICJ2YWx1ZSIgOiB7CiAgICAgICAgICAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICI0MzozOC00Mzo1NyIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICJwYXRoIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJQcm9wZXJ0eSIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICJzb3VyY2UiIDogewogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiNDM6MzgtNDM6NTEiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAicGF0aCIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAidHlwZSIgOiAiUHJvcGVydHkiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAic291cmNlIiA6IHsKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjQzOjM4LTQzOjQ1IiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIm5hbWUiIDogInF1YW50aXR5IiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk9wZXJhbmRSZWYiCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgICAgICAgICAgICAgICAgfQogICAgICAgICAgICAgICAgICAgICAgICB9CiAgICAgICAgICAgICAgICAgICAgIH0sIHsKICAgICAgICAgICAgICAgICAgICAgICAgIm5hbWUiIDogInVuaXQiLAogICAgICAgICAgICAgICAgICAgICAgICAidmFsdWUiIDogewogICAgICAgICAgICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiNDM6NjYtNDM6MTM2IiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgIm5hbWUiIDogIlRvQ2FsZW5kYXJVbml0IiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgInR5cGUiIDogIkZ1bmN0aW9uUmVmIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgIm9wZXJhbmQiIDogWyB7CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICI0Mzo4MS00MzoxMzUiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAidHlwZSIgOiAiQ29hbGVzY2UiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAib3BlcmFuZCIgOiBbIHsKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjQzOjkwLTQzOjEwOCIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJwYXRoIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJQcm9wZXJ0eSIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJzb3VyY2UiIDogewogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiNDM6OTAtNDM6MTAyIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInBhdGgiIDogImNvZGUiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAidHlwZSIgOiAiUHJvcGVydHkiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAic291cmNlIiA6IHsKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjQzOjkwLTQzOjk3IiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIm5hbWUiIDogInF1YW50aXR5IiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk9wZXJhbmRSZWYiCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICB9LCB7CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICI0MzoxMTEtNDM6MTI5IiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInBhdGgiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInR5cGUiIDogIlByb3BlcnR5IiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInNvdXJjZSIgOiB7CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICI0MzoxMTEtNDM6MTIzIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInBhdGgiIDogInVuaXQiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAidHlwZSIgOiAiUHJvcGVydHkiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAic291cmNlIiA6IHsKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjQzOjExMS00MzoxMTgiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAibmFtZSIgOiAicXVhbnRpdHkiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAidHlwZSIgOiAiT3BlcmFuZFJlZiIKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB9CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIH0sIHsKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjQzOjEzMi00MzoxMzQiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAidmFsdWVUeXBlIiA6ICJ7dXJuOmhsNy1vcmc6ZWxtLXR5cGVzOnIxfVN0cmluZyIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJ2YWx1ZSIgOiAiMSIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJMaXRlcmFsIgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICB9IF0KICAgICAgICAgICAgICAgICAgICAgICAgICAgfSBdCiAgICAgICAgICAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgICAgICAgICAgfSBdCiAgICAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgICAgfSBdLAogICAgICAgICAgICAgICAiZWxzZSIgOiB7CiAgICAgICAgICAgICAgICAgICJhc1R5cGUiIDogInt1cm46aGw3LW9yZzplbG0tdHlwZXM6cjF9UXVhbnRpdHkiLAogICAgICAgICAgICAgICAgICAidHlwZSIgOiAiQXMiLAogICAgICAgICAgICAgICAgICAib3BlcmFuZCIgOiB7CiAgICAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICI0NToxMy00NToyMDUiLAogICAgICAgICAgICAgICAgICAgICAidHlwZSIgOiAiTWVzc2FnZSIsCiAgICAgICAgICAgICAgICAgICAgICJzb3VyY2UiIDogewogICAgICAgICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiNDU6MjEtNDU6MjQiLAogICAgICAgICAgICAgICAgICAgICAgICAidHlwZSIgOiAiTnVsbCIKICAgICAgICAgICAgICAgICAgICAgfSwKICAgICAgICAgICAgICAgICAgICAgImNvbmRpdGlvbiIgOiB7CiAgICAgICAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICI0NToyNy00NTozMCIsCiAgICAgICAgICAgICAgICAgICAgICAgICJ2YWx1ZVR5cGUiIDogInt1cm46aGw3LW9yZzplbG0tdHlwZXM6cjF9Qm9vbGVhbiIsCiAgICAgICAgICAgICAgICAgICAgICAgICJ2YWx1ZSIgOiAidHJ1ZSIsCiAgICAgICAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJMaXRlcmFsIgogICAgICAgICAgICAgICAgICAgICB9LAogICAgICAgICAgICAgICAgICAgICAiY29kZSIgOiB7CiAgICAgICAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICI0NTozMy00NTo3NiIsCiAgICAgICAgICAgICAgICAgICAgICAgICJ2YWx1ZVR5cGUiIDogInt1cm46aGw3LW9yZzplbG0tdHlwZXM6cjF9U3RyaW5nIiwKICAgICAgICAgICAgICAgICAgICAgICAgInZhbHVlIiA6ICJGSElSSGVscGVycy5Ub1F1YW50aXR5LkludmFsaWRGSElSUXVhbnRpdHkiLAogICAgICAgICAgICAgICAgICAgICAgICAidHlwZSIgOiAiTGl0ZXJhbCIKICAgICAgICAgICAgICAgICAgICAgfSwKICAgICAgICAgICAgICAgICAgICAgInNldmVyaXR5IiA6IHsKICAgICAgICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjQ1Ojc5LTQ1Ojg1IiwKICAgICAgICAgICAgICAgICAgICAgICAgInZhbHVlVHlwZSIgOiAie3VybjpobDctb3JnOmVsbS10eXBlczpyMX1TdHJpbmciLAogICAgICAgICAgICAgICAgICAgICAgICAidmFsdWUiIDogIkVycm9yIiwKICAgICAgICAgICAgICAgICAgICAgICAgInR5cGUiIDogIkxpdGVyYWwiCiAgICAgICAgICAgICAgICAgICAgIH0sCiAgICAgICAgICAgICAgICAgICAgICJtZXNzYWdlIiA6IHsKICAgICAgICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjQ1Ojg4LTQ1OjIwNCIsCiAgICAgICAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJDb25jYXRlbmF0ZSIsCiAgICAgICAgICAgICAgICAgICAgICAgICJvcGVyYW5kIiA6IFsgewogICAgICAgICAgICAgICAgICAgICAgICAgICAidHlwZSIgOiAiQ29hbGVzY2UiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAib3BlcmFuZCIgOiBbIHsKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjQ1Ojg4LTQ1OjE5OCIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJDb25jYXRlbmF0ZSIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJvcGVyYW5kIiA6IFsgewogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAidHlwZSIgOiAiQ29hbGVzY2UiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAib3BlcmFuZCIgOiBbIHsKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjQ1Ojg4LTQ1OjE3NiIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJDb25jYXRlbmF0ZSIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJvcGVyYW5kIiA6IFsgewogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAidHlwZSIgOiAiQ29hbGVzY2UiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAib3BlcmFuZCIgOiBbIHsKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjQ1Ojg4LTQ1OjE3MCIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJDb25jYXRlbmF0ZSIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJvcGVyYW5kIiA6IFsgewogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAidHlwZSIgOiAiQ29hbGVzY2UiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAib3BlcmFuZCIgOiBbIHsKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjQ1Ojg4LTQ1OjE0NiIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJDb25jYXRlbmF0ZSIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJvcGVyYW5kIiA6IFsgewogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAidHlwZSIgOiAiQ29hbGVzY2UiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAib3BlcmFuZCIgOiBbIHsKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjQ1Ojg4LTQ1OjEzOSIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJDb25jYXRlbmF0ZSIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJvcGVyYW5kIiA6IFsgewogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAidHlwZSIgOiAiQ29hbGVzY2UiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAib3BlcmFuZCIgOiBbIHsKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjQ1Ojg4LTQ1OjExNyIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJ2YWx1ZVR5cGUiIDogInt1cm46aGw3LW9yZzplbG0tdHlwZXM6cjF9U3RyaW5nIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInZhbHVlIiA6ICJJbnZhbGlkIEZISVIgUXVhbnRpdHkgY29kZTogIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInR5cGUiIDogIkxpdGVyYWwiCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIH0sIHsKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInZhbHVlVHlwZSIgOiAie3VybjpobDctb3JnOmVsbS10eXBlczpyMX1TdHJpbmciLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAidmFsdWUiIDogIiIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJMaXRlcmFsIgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB9IF0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfSwgewogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAidHlwZSIgOiAiQ29hbGVzY2UiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAib3BlcmFuZCIgOiBbIHsKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjQ1OjEyMS00NToxMzkiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAicGF0aCIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAidHlwZSIgOiAiUHJvcGVydHkiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAic291cmNlIiA6IHsKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjQ1OjEyMS00NToxMzMiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAicGF0aCIgOiAidW5pdCIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJQcm9wZXJ0eSIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJzb3VyY2UiIDogewogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiNDU6MTIxLTQ1OjEyOCIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJxdWFudGl0eSIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJPcGVyYW5kUmVmIgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB9CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfSwgewogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAidmFsdWVUeXBlIiA6ICJ7dXJuOmhsNy1vcmc6ZWxtLXR5cGVzOnIxfVN0cmluZyIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJ2YWx1ZSIgOiAiIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInR5cGUiIDogIkxpdGVyYWwiCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIH0gXQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB9IF0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfSwgewogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAidmFsdWVUeXBlIiA6ICJ7dXJuOmhsNy1vcmc6ZWxtLXR5cGVzOnIxfVN0cmluZyIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJ2YWx1ZSIgOiAiIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInR5cGUiIDogIkxpdGVyYWwiCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIH0gXQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB9LCB7CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJDb2FsZXNjZSIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJvcGVyYW5kIiA6IFsgewogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiNDU6MTQzLTQ1OjE0NiIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJ2YWx1ZVR5cGUiIDogInt1cm46aGw3LW9yZzplbG0tdHlwZXM6cjF9U3RyaW5nIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInZhbHVlIiA6ICIgKCIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJMaXRlcmFsIgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB9LCB7CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJ2YWx1ZVR5cGUiIDogInt1cm46aGw3LW9yZzplbG0tdHlwZXM6cjF9U3RyaW5nIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInZhbHVlIiA6ICIiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAidHlwZSIgOiAiTGl0ZXJhbCIKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfSBdCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIH0gXQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB9LCB7CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJ2YWx1ZVR5cGUiIDogInt1cm46aGw3LW9yZzplbG0tdHlwZXM6cjF9U3RyaW5nIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInZhbHVlIiA6ICIiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAidHlwZSIgOiAiTGl0ZXJhbCIKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfSBdCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIH0sIHsKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInR5cGUiIDogIkNvYWxlc2NlIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIm9wZXJhbmQiIDogWyB7CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICI0NToxNTAtNDU6MTcwIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInBhdGgiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInR5cGUiIDogIlByb3BlcnR5IiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInNvdXJjZSIgOiB7CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICI0NToxNTAtNDU6MTY0IiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInBhdGgiIDogInN5c3RlbSIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJQcm9wZXJ0eSIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJzb3VyY2UiIDogewogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiNDU6MTUwLTQ1OjE1NyIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJxdWFudGl0eSIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJPcGVyYW5kUmVmIgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB9CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfSwgewogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAidmFsdWVUeXBlIiA6ICJ7dXJuOmhsNy1vcmc6ZWxtLXR5cGVzOnIxfVN0cmluZyIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJ2YWx1ZSIgOiAiIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInR5cGUiIDogIkxpdGVyYWwiCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIH0gXQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB9IF0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfSwgewogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAidmFsdWVUeXBlIiA6ICJ7dXJuOmhsNy1vcmc6ZWxtLXR5cGVzOnIxfVN0cmluZyIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJ2YWx1ZSIgOiAiIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInR5cGUiIDogIkxpdGVyYWwiCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIH0gXQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB9LCB7CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJDb2FsZXNjZSIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJvcGVyYW5kIiA6IFsgewogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiNDU6MTc0LTQ1OjE3NiIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJ2YWx1ZVR5cGUiIDogInt1cm46aGw3LW9yZzplbG0tdHlwZXM6cjF9U3RyaW5nIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInZhbHVlIiA6ICJ8IiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInR5cGUiIDogIkxpdGVyYWwiCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIH0sIHsKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInZhbHVlVHlwZSIgOiAie3VybjpobDctb3JnOmVsbS10eXBlczpyMX1TdHJpbmciLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAidmFsdWUiIDogIiIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJMaXRlcmFsIgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB9IF0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfSBdCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIH0sIHsKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInZhbHVlVHlwZSIgOiAie3VybjpobDctb3JnOmVsbS10eXBlczpyMX1TdHJpbmciLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAidmFsdWUiIDogIiIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJMaXRlcmFsIgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB9IF0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfSwgewogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAidHlwZSIgOiAiQ29hbGVzY2UiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAib3BlcmFuZCIgOiBbIHsKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjQ1OjE4MC00NToxOTgiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAicGF0aCIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAidHlwZSIgOiAiUHJvcGVydHkiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAic291cmNlIiA6IHsKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjQ1OjE4MC00NToxOTIiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAicGF0aCIgOiAiY29kZSIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJQcm9wZXJ0eSIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJzb3VyY2UiIDogewogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiNDU6MTgwLTQ1OjE4NyIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJxdWFudGl0eSIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJPcGVyYW5kUmVmIgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB9CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfSwgewogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAidmFsdWVUeXBlIiA6ICJ7dXJuOmhsNy1vcmc6ZWxtLXR5cGVzOnIxfVN0cmluZyIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJ2YWx1ZSIgOiAiIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInR5cGUiIDogIkxpdGVyYWwiCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIH0gXQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICB9IF0KICAgICAgICAgICAgICAgICAgICAgICAgICAgfSwgewogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAidmFsdWVUeXBlIiA6ICJ7dXJuOmhsNy1vcmc6ZWxtLXR5cGVzOnIxfVN0cmluZyIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJ2YWx1ZSIgOiAiIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInR5cGUiIDogIkxpdGVyYWwiCiAgICAgICAgICAgICAgICAgICAgICAgICAgIH0gXQogICAgICAgICAgICAgICAgICAgICAgICB9LCB7CiAgICAgICAgICAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJDb2FsZXNjZSIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICJvcGVyYW5kIiA6IFsgewogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiNDU6MjAyLTQ1OjIwNCIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJ2YWx1ZVR5cGUiIDogInt1cm46aGw3LW9yZzplbG0tdHlwZXM6cjF9U3RyaW5nIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInZhbHVlIiA6ICIpIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInR5cGUiIDogIkxpdGVyYWwiCiAgICAgICAgICAgICAgICAgICAgICAgICAgIH0sIHsKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInZhbHVlVHlwZSIgOiAie3VybjpobDctb3JnOmVsbS10eXBlczpyMX1TdHJpbmciLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAidmFsdWUiIDogIiIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJMaXRlcmFsIgogICAgICAgICAgICAgICAgICAgICAgICAgICB9IF0KICAgICAgICAgICAgICAgICAgICAgICAgfSBdCiAgICAgICAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgICAgICAgfQogICAgICAgICAgICAgICB9CiAgICAgICAgICAgIH0sCiAgICAgICAgICAgICJvcGVyYW5kIiA6IFsgewogICAgICAgICAgICAgICAibmFtZSIgOiAicXVhbnRpdHkiLAogICAgICAgICAgICAgICAib3BlcmFuZFR5cGVTcGVjaWZpZXIiIDogewogICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMzU6MzctMzU6NDkiLAogICAgICAgICAgICAgICAgICAibmFtZSIgOiAie2h0dHA6Ly9obDcub3JnL2ZoaXJ9UXVhbnRpdHkiLAogICAgICAgICAgICAgICAgICAidHlwZSIgOiAiTmFtZWRUeXBlU3BlY2lmaWVyIgogICAgICAgICAgICAgICB9CiAgICAgICAgICAgIH0gXQogICAgICAgICB9LCB7CiAgICAgICAgICAgICJsb2NhdG9yIiA6ICI0ODoxLTU3OjciLAogICAgICAgICAgICAibmFtZSIgOiAiVG9RdWFudGl0eUlnbm9yaW5nQ29tcGFyYXRvciIsCiAgICAgICAgICAgICJjb250ZXh0IiA6ICJQYXRpZW50IiwKICAgICAgICAgICAgImFjY2Vzc0xldmVsIiA6ICJQdWJsaWMiLAogICAgICAgICAgICAidHlwZSIgOiAiRnVuY3Rpb25EZWYiLAogICAgICAgICAgICAiZXhwcmVzc2lvbiIgOiB7CiAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICI0OTo1LTU3OjciLAogICAgICAgICAgICAgICAidHlwZSIgOiAiQ2FzZSIsCiAgICAgICAgICAgICAgICJjYXNlSXRlbSIgOiBbIHsKICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjUwOjktNTA6MzkiLAogICAgICAgICAgICAgICAgICAid2hlbiIgOiB7CiAgICAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICI1MDoxNC01MDoyOSIsCiAgICAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJJc051bGwiLAogICAgICAgICAgICAgICAgICAgICAib3BlcmFuZCIgOiB7CiAgICAgICAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICI1MDoxNC01MDoyMSIsCiAgICAgICAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJxdWFudGl0eSIsCiAgICAgICAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJPcGVyYW5kUmVmIgogICAgICAgICAgICAgICAgICAgICB9CiAgICAgICAgICAgICAgICAgIH0sCiAgICAgICAgICAgICAgICAgICJ0aGVuIiA6IHsKICAgICAgICAgICAgICAgICAgICAgImFzVHlwZSIgOiAie3VybjpobDctb3JnOmVsbS10eXBlczpyMX1RdWFudGl0eSIsCiAgICAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJBcyIsCiAgICAgICAgICAgICAgICAgICAgICJvcGVyYW5kIiA6IHsKICAgICAgICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjUwOjM2LTUwOjM5IiwKICAgICAgICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk51bGwiCiAgICAgICAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgICAgICAgfQogICAgICAgICAgICAgICB9LCB7CiAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICI1MTo5LTUxOjQ1IiwKICAgICAgICAgICAgICAgICAgIndoZW4iIDogewogICAgICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiNTE6MTQtNTE6MzUiLAogICAgICAgICAgICAgICAgICAgICAidHlwZSIgOiAiSXNOdWxsIiwKICAgICAgICAgICAgICAgICAgICAgIm9wZXJhbmQiIDogewogICAgICAgICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiNTE6MTQtNTE6MjciLAogICAgICAgICAgICAgICAgICAgICAgICAicGF0aCIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAgICAgICAgICAidHlwZSIgOiAiUHJvcGVydHkiLAogICAgICAgICAgICAgICAgICAgICAgICAic291cmNlIiA6IHsKICAgICAgICAgICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjUxOjE0LTUxOjIxIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgIm5hbWUiIDogInF1YW50aXR5IiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk9wZXJhbmRSZWYiCiAgICAgICAgICAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgICAgICAgICAgfQogICAgICAgICAgICAgICAgICB9LAogICAgICAgICAgICAgICAgICAidGhlbiIgOiB7CiAgICAgICAgICAgICAgICAgICAgICJhc1R5cGUiIDogInt1cm46aGw3LW9yZzplbG0tdHlwZXM6cjF9UXVhbnRpdHkiLAogICAgICAgICAgICAgICAgICAgICAidHlwZSIgOiAiQXMiLAogICAgICAgICAgICAgICAgICAgICAib3BlcmFuZCIgOiB7CiAgICAgICAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICI1MTo0Mi01MTo0NSIsCiAgICAgICAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJOdWxsIgogICAgICAgICAgICAgICAgICAgICB9CiAgICAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgICAgfSwgewogICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiNTI6OS01NDoxMzgiLAogICAgICAgICAgICAgICAgICAid2hlbiIgOiB7CiAgICAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICI1MjoxNC01Mzo5MiIsCiAgICAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJPciIsCiAgICAgICAgICAgICAgICAgICAgICJvcGVyYW5kIiA6IFsgewogICAgICAgICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiNTI6MTQtNTI6OTEiLAogICAgICAgICAgICAgICAgICAgICAgICAidHlwZSIgOiAiT3IiLAogICAgICAgICAgICAgICAgICAgICAgICAib3BlcmFuZCIgOiBbIHsKICAgICAgICAgICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjUyOjE0LTUyOjM2IiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgInR5cGUiIDogIklzTnVsbCIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICJvcGVyYW5kIiA6IHsKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjUyOjE0LTUyOjI4IiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInBhdGgiIDogInN5c3RlbSIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJQcm9wZXJ0eSIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJzb3VyY2UiIDogewogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiNTI6MTQtNTI6MjEiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAibmFtZSIgOiAicXVhbnRpdHkiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAidHlwZSIgOiAiT3BlcmFuZFJlZiIKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfQogICAgICAgICAgICAgICAgICAgICAgICAgICB9CiAgICAgICAgICAgICAgICAgICAgICAgIH0sIHsKICAgICAgICAgICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjUyOjQxLTUyOjkxIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgInR5cGUiIDogIkVxdWFsIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgIm9wZXJhbmQiIDogWyB7CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICI1Mjo0MS01Mjo2MSIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJwYXRoIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJQcm9wZXJ0eSIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJzb3VyY2UiIDogewogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiNTI6NDEtNTI6NTUiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAicGF0aCIgOiAic3lzdGVtIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInR5cGUiIDogIlByb3BlcnR5IiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInNvdXJjZSIgOiB7CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICI1Mjo0MS01Mjo0OCIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJxdWFudGl0eSIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJPcGVyYW5kUmVmIgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB9CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgICAgICAgICAgICAgICAgfSwgewogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiNTI6NjUtNTI6OTEiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAidmFsdWVUeXBlIiA6ICJ7dXJuOmhsNy1vcmc6ZWxtLXR5cGVzOnIxfVN0cmluZyIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJ2YWx1ZSIgOiAiaHR0cDovL3VuaXRzb2ZtZWFzdXJlLm9yZyIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJMaXRlcmFsIgogICAgICAgICAgICAgICAgICAgICAgICAgICB9IF0KICAgICAgICAgICAgICAgICAgICAgICAgfSBdCiAgICAgICAgICAgICAgICAgICAgIH0sIHsKICAgICAgICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjUzOjE4LTUzOjkyIiwKICAgICAgICAgICAgICAgICAgICAgICAgInR5cGUiIDogIkVxdWFsIiwKICAgICAgICAgICAgICAgICAgICAgICAgIm9wZXJhbmQiIDogWyB7CiAgICAgICAgICAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICI1MzoxOC01MzozOCIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICJwYXRoIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJQcm9wZXJ0eSIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICJzb3VyY2UiIDogewogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiNTM6MTgtNTM6MzIiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAicGF0aCIgOiAic3lzdGVtIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInR5cGUiIDogIlByb3BlcnR5IiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInNvdXJjZSIgOiB7CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICI1MzoxOC01MzoyNSIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJxdWFudGl0eSIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJPcGVyYW5kUmVmIgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICB9CiAgICAgICAgICAgICAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgICAgICAgICAgICAgfSwgewogICAgICAgICAgICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiNTM6NDItNTM6OTIiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAidmFsdWVUeXBlIiA6ICJ7dXJuOmhsNy1vcmc6ZWxtLXR5cGVzOnIxfVN0cmluZyIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICJ2YWx1ZSIgOiAiaHR0cDovL2hsNy5vcmcvZmhpcnBhdGgvQ29kZVN5c3RlbS9jYWxlbmRhci11bml0cyIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJMaXRlcmFsIgogICAgICAgICAgICAgICAgICAgICAgICB9IF0KICAgICAgICAgICAgICAgICAgICAgfSBdCiAgICAgICAgICAgICAgICAgIH0sCiAgICAgICAgICAgICAgICAgICJ0aGVuIiA6IHsKICAgICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjU0OjEzLTU0OjEzOCIsCiAgICAgICAgICAgICAgICAgICAgICJjbGFzc1R5cGUiIDogInt1cm46aGw3LW9yZzplbG0tdHlwZXM6cjF9UXVhbnRpdHkiLAogICAgICAgICAgICAgICAgICAgICAidHlwZSIgOiAiSW5zdGFuY2UiLAogICAgICAgICAgICAgICAgICAgICAiZWxlbWVudCIgOiBbIHsKICAgICAgICAgICAgICAgICAgICAgICAgIm5hbWUiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgICAgICAgICAgInZhbHVlIiA6IHsKICAgICAgICAgICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjU0OjM4LTU0OjU3IiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgInBhdGgiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgInR5cGUiIDogIlByb3BlcnR5IiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgInNvdXJjZSIgOiB7CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICI1NDozOC01NDo1MSIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJwYXRoIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJQcm9wZXJ0eSIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJzb3VyY2UiIDogewogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiNTQ6MzgtNTQ6NDUiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAibmFtZSIgOiAicXVhbnRpdHkiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAidHlwZSIgOiAiT3BlcmFuZFJlZiIKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfQogICAgICAgICAgICAgICAgICAgICAgICAgICB9CiAgICAgICAgICAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgICAgICAgICAgfSwgewogICAgICAgICAgICAgICAgICAgICAgICAibmFtZSIgOiAidW5pdCIsCiAgICAgICAgICAgICAgICAgICAgICAgICJ2YWx1ZSIgOiB7CiAgICAgICAgICAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICI1NDo2Ni01NDoxMzYiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAibmFtZSIgOiAiVG9DYWxlbmRhclVuaXQiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAidHlwZSIgOiAiRnVuY3Rpb25SZWYiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAib3BlcmFuZCIgOiBbIHsKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjU0OjgxLTU0OjEzNSIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJDb2FsZXNjZSIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJvcGVyYW5kIiA6IFsgewogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiNTQ6OTAtNTQ6MTA4IiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInBhdGgiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInR5cGUiIDogIlByb3BlcnR5IiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInNvdXJjZSIgOiB7CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICI1NDo5MC01NDoxMDIiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAicGF0aCIgOiAiY29kZSIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJQcm9wZXJ0eSIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJzb3VyY2UiIDogewogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiNTQ6OTAtNTQ6OTciLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAibmFtZSIgOiAicXVhbnRpdHkiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAidHlwZSIgOiAiT3BlcmFuZFJlZiIKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB9CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIH0sIHsKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjU0OjExMS01NDoxMjkiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAicGF0aCIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAidHlwZSIgOiAiUHJvcGVydHkiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAic291cmNlIiA6IHsKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjU0OjExMS01NDoxMjMiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAicGF0aCIgOiAidW5pdCIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJQcm9wZXJ0eSIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJzb3VyY2UiIDogewogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiNTQ6MTExLTU0OjExOCIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJxdWFudGl0eSIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJPcGVyYW5kUmVmIgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB9CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfSwgewogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiNTQ6MTMyLTU0OjEzNCIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJ2YWx1ZVR5cGUiIDogInt1cm46aGw3LW9yZzplbG0tdHlwZXM6cjF9U3RyaW5nIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInZhbHVlIiA6ICIxIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInR5cGUiIDogIkxpdGVyYWwiCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIH0gXQogICAgICAgICAgICAgICAgICAgICAgICAgICB9IF0KICAgICAgICAgICAgICAgICAgICAgICAgfQogICAgICAgICAgICAgICAgICAgICB9IF0KICAgICAgICAgICAgICAgICAgfQogICAgICAgICAgICAgICB9IF0sCiAgICAgICAgICAgICAgICJlbHNlIiA6IHsKICAgICAgICAgICAgICAgICAgImFzVHlwZSIgOiAie3VybjpobDctb3JnOmVsbS10eXBlczpyMX1RdWFudGl0eSIsCiAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJBcyIsCiAgICAgICAgICAgICAgICAgICJvcGVyYW5kIiA6IHsKICAgICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjU2OjEzLTU2OjIwNSIsCiAgICAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJNZXNzYWdlIiwKICAgICAgICAgICAgICAgICAgICAgInNvdXJjZSIgOiB7CiAgICAgICAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICI1NjoyMS01NjoyNCIsCiAgICAgICAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJOdWxsIgogICAgICAgICAgICAgICAgICAgICB9LAogICAgICAgICAgICAgICAgICAgICAiY29uZGl0aW9uIiA6IHsKICAgICAgICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjU2OjI3LTU2OjMwIiwKICAgICAgICAgICAgICAgICAgICAgICAgInZhbHVlVHlwZSIgOiAie3VybjpobDctb3JnOmVsbS10eXBlczpyMX1Cb29sZWFuIiwKICAgICAgICAgICAgICAgICAgICAgICAgInZhbHVlIiA6ICJ0cnVlIiwKICAgICAgICAgICAgICAgICAgICAgICAgInR5cGUiIDogIkxpdGVyYWwiCiAgICAgICAgICAgICAgICAgICAgIH0sCiAgICAgICAgICAgICAgICAgICAgICJjb2RlIiA6IHsKICAgICAgICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjU2OjMzLTU2Ojc2IiwKICAgICAgICAgICAgICAgICAgICAgICAgInZhbHVlVHlwZSIgOiAie3VybjpobDctb3JnOmVsbS10eXBlczpyMX1TdHJpbmciLAogICAgICAgICAgICAgICAgICAgICAgICAidmFsdWUiIDogIkZISVJIZWxwZXJzLlRvUXVhbnRpdHkuSW52YWxpZEZISVJRdWFudGl0eSIsCiAgICAgICAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJMaXRlcmFsIgogICAgICAgICAgICAgICAgICAgICB9LAogICAgICAgICAgICAgICAgICAgICAic2V2ZXJpdHkiIDogewogICAgICAgICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiNTY6NzktNTY6ODUiLAogICAgICAgICAgICAgICAgICAgICAgICAidmFsdWVUeXBlIiA6ICJ7dXJuOmhsNy1vcmc6ZWxtLXR5cGVzOnIxfVN0cmluZyIsCiAgICAgICAgICAgICAgICAgICAgICAgICJ2YWx1ZSIgOiAiRXJyb3IiLAogICAgICAgICAgICAgICAgICAgICAgICAidHlwZSIgOiAiTGl0ZXJhbCIKICAgICAgICAgICAgICAgICAgICAgfSwKICAgICAgICAgICAgICAgICAgICAgIm1lc3NhZ2UiIDogewogICAgICAgICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiNTY6ODgtNTY6MjA0IiwKICAgICAgICAgICAgICAgICAgICAgICAgInR5cGUiIDogIkNvbmNhdGVuYXRlIiwKICAgICAgICAgICAgICAgICAgICAgICAgIm9wZXJhbmQiIDogWyB7CiAgICAgICAgICAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJDb2FsZXNjZSIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICJvcGVyYW5kIiA6IFsgewogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiNTY6ODgtNTY6MTk4IiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInR5cGUiIDogIkNvbmNhdGVuYXRlIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIm9wZXJhbmQiIDogWyB7CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJDb2FsZXNjZSIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJvcGVyYW5kIiA6IFsgewogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiNTY6ODgtNTY6MTc2IiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInR5cGUiIDogIkNvbmNhdGVuYXRlIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIm9wZXJhbmQiIDogWyB7CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJDb2FsZXNjZSIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJvcGVyYW5kIiA6IFsgewogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiNTY6ODgtNTY6MTcwIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInR5cGUiIDogIkNvbmNhdGVuYXRlIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIm9wZXJhbmQiIDogWyB7CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJDb2FsZXNjZSIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJvcGVyYW5kIiA6IFsgewogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiNTY6ODgtNTY6MTQ2IiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInR5cGUiIDogIkNvbmNhdGVuYXRlIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIm9wZXJhbmQiIDogWyB7CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJDb2FsZXNjZSIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJvcGVyYW5kIiA6IFsgewogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiNTY6ODgtNTY6MTM5IiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInR5cGUiIDogIkNvbmNhdGVuYXRlIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIm9wZXJhbmQiIDogWyB7CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJDb2FsZXNjZSIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJvcGVyYW5kIiA6IFsgewogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiNTY6ODgtNTY6MTE3IiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInZhbHVlVHlwZSIgOiAie3VybjpobDctb3JnOmVsbS10eXBlczpyMX1TdHJpbmciLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAidmFsdWUiIDogIkludmFsaWQgRkhJUiBRdWFudGl0eSBjb2RlOiAiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAidHlwZSIgOiAiTGl0ZXJhbCIKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfSwgewogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAidmFsdWVUeXBlIiA6ICJ7dXJuOmhsNy1vcmc6ZWxtLXR5cGVzOnIxfVN0cmluZyIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJ2YWx1ZSIgOiAiIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInR5cGUiIDogIkxpdGVyYWwiCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIH0gXQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB9LCB7CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJDb2FsZXNjZSIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJvcGVyYW5kIiA6IFsgewogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiNTY6MTIxLTU2OjEzOSIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJwYXRoIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJQcm9wZXJ0eSIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJzb3VyY2UiIDogewogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiNTY6MTIxLTU2OjEzMyIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJwYXRoIiA6ICJ1bml0IiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInR5cGUiIDogIlByb3BlcnR5IiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInNvdXJjZSIgOiB7CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICI1NjoxMjEtNTY6MTI4IiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIm5hbWUiIDogInF1YW50aXR5IiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk9wZXJhbmRSZWYiCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB9LCB7CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJ2YWx1ZVR5cGUiIDogInt1cm46aGw3LW9yZzplbG0tdHlwZXM6cjF9U3RyaW5nIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInZhbHVlIiA6ICIiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAidHlwZSIgOiAiTGl0ZXJhbCIKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfSBdCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIH0gXQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB9LCB7CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJ2YWx1ZVR5cGUiIDogInt1cm46aGw3LW9yZzplbG0tdHlwZXM6cjF9U3RyaW5nIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInZhbHVlIiA6ICIiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAidHlwZSIgOiAiTGl0ZXJhbCIKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfSBdCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIH0sIHsKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInR5cGUiIDogIkNvYWxlc2NlIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIm9wZXJhbmQiIDogWyB7CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICI1NjoxNDMtNTY6MTQ2IiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInZhbHVlVHlwZSIgOiAie3VybjpobDctb3JnOmVsbS10eXBlczpyMX1TdHJpbmciLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAidmFsdWUiIDogIiAoIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInR5cGUiIDogIkxpdGVyYWwiCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIH0sIHsKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInZhbHVlVHlwZSIgOiAie3VybjpobDctb3JnOmVsbS10eXBlczpyMX1TdHJpbmciLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAidmFsdWUiIDogIiIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJMaXRlcmFsIgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB9IF0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfSBdCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIH0sIHsKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInZhbHVlVHlwZSIgOiAie3VybjpobDctb3JnOmVsbS10eXBlczpyMX1TdHJpbmciLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAidmFsdWUiIDogIiIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJMaXRlcmFsIgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB9IF0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfSwgewogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAidHlwZSIgOiAiQ29hbGVzY2UiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAib3BlcmFuZCIgOiBbIHsKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjU2OjE1MC01NjoxNzAiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAicGF0aCIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAidHlwZSIgOiAiUHJvcGVydHkiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAic291cmNlIiA6IHsKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjU2OjE1MC01NjoxNjQiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAicGF0aCIgOiAic3lzdGVtIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInR5cGUiIDogIlByb3BlcnR5IiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInNvdXJjZSIgOiB7CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICI1NjoxNTAtNTY6MTU3IiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIm5hbWUiIDogInF1YW50aXR5IiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk9wZXJhbmRSZWYiCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB9LCB7CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJ2YWx1ZVR5cGUiIDogInt1cm46aGw3LW9yZzplbG0tdHlwZXM6cjF9U3RyaW5nIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInZhbHVlIiA6ICIiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAidHlwZSIgOiAiTGl0ZXJhbCIKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfSBdCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIH0gXQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB9LCB7CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJ2YWx1ZVR5cGUiIDogInt1cm46aGw3LW9yZzplbG0tdHlwZXM6cjF9U3RyaW5nIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInZhbHVlIiA6ICIiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAidHlwZSIgOiAiTGl0ZXJhbCIKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfSBdCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIH0sIHsKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInR5cGUiIDogIkNvYWxlc2NlIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIm9wZXJhbmQiIDogWyB7CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICI1NjoxNzQtNTY6MTc2IiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInZhbHVlVHlwZSIgOiAie3VybjpobDctb3JnOmVsbS10eXBlczpyMX1TdHJpbmciLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAidmFsdWUiIDogInwiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAidHlwZSIgOiAiTGl0ZXJhbCIKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfSwgewogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAidmFsdWVUeXBlIiA6ICJ7dXJuOmhsNy1vcmc6ZWxtLXR5cGVzOnIxfVN0cmluZyIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJ2YWx1ZSIgOiAiIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInR5cGUiIDogIkxpdGVyYWwiCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIH0gXQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB9IF0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfSwgewogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAidmFsdWVUeXBlIiA6ICJ7dXJuOmhsNy1vcmc6ZWxtLXR5cGVzOnIxfVN0cmluZyIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJ2YWx1ZSIgOiAiIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInR5cGUiIDogIkxpdGVyYWwiCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIH0gXQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICB9LCB7CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJDb2FsZXNjZSIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJvcGVyYW5kIiA6IFsgewogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiNTY6MTgwLTU2OjE5OCIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJwYXRoIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJQcm9wZXJ0eSIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJzb3VyY2UiIDogewogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiNTY6MTgwLTU2OjE5MiIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJwYXRoIiA6ICJjb2RlIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInR5cGUiIDogIlByb3BlcnR5IiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInNvdXJjZSIgOiB7CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICI1NjoxODAtNTY6MTg3IiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIm5hbWUiIDogInF1YW50aXR5IiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk9wZXJhbmRSZWYiCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB9LCB7CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJ2YWx1ZVR5cGUiIDogInt1cm46aGw3LW9yZzplbG0tdHlwZXM6cjF9U3RyaW5nIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInZhbHVlIiA6ICIiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAidHlwZSIgOiAiTGl0ZXJhbCIKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfSBdCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIH0gXQogICAgICAgICAgICAgICAgICAgICAgICAgICB9LCB7CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJ2YWx1ZVR5cGUiIDogInt1cm46aGw3LW9yZzplbG0tdHlwZXM6cjF9U3RyaW5nIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInZhbHVlIiA6ICIiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAidHlwZSIgOiAiTGl0ZXJhbCIKICAgICAgICAgICAgICAgICAgICAgICAgICAgfSBdCiAgICAgICAgICAgICAgICAgICAgICAgIH0sIHsKICAgICAgICAgICAgICAgICAgICAgICAgICAgInR5cGUiIDogIkNvYWxlc2NlIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgIm9wZXJhbmQiIDogWyB7CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICI1NjoyMDItNTY6MjA0IiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInZhbHVlVHlwZSIgOiAie3VybjpobDctb3JnOmVsbS10eXBlczpyMX1TdHJpbmciLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAidmFsdWUiIDogIikiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAidHlwZSIgOiAiTGl0ZXJhbCIKICAgICAgICAgICAgICAgICAgICAgICAgICAgfSwgewogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAidmFsdWVUeXBlIiA6ICJ7dXJuOmhsNy1vcmc6ZWxtLXR5cGVzOnIxfVN0cmluZyIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJ2YWx1ZSIgOiAiIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInR5cGUiIDogIkxpdGVyYWwiCiAgICAgICAgICAgICAgICAgICAgICAgICAgIH0gXQogICAgICAgICAgICAgICAgICAgICAgICB9IF0KICAgICAgICAgICAgICAgICAgICAgfQogICAgICAgICAgICAgICAgICB9CiAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgfSwKICAgICAgICAgICAgIm9wZXJhbmQiIDogWyB7CiAgICAgICAgICAgICAgICJuYW1lIiA6ICJxdWFudGl0eSIsCiAgICAgICAgICAgICAgICJvcGVyYW5kVHlwZVNwZWNpZmllciIgOiB7CiAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICI0ODo1NS00ODo2NyIsCiAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJ7aHR0cDovL2hsNy5vcmcvZmhpcn1RdWFudGl0eSIsCiAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJOYW1lZFR5cGVTcGVjaWZpZXIiCiAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgfSBdCiAgICAgICAgIH0sIHsKICAgICAgICAgICAgImxvY2F0b3IiIDogIjU5OjEtODQ6MTEiLAogICAgICAgICAgICAibmFtZSIgOiAiVG9JbnRlcnZhbCIsCiAgICAgICAgICAgICJjb250ZXh0IiA6ICJQYXRpZW50IiwKICAgICAgICAgICAgImFjY2Vzc0xldmVsIiA6ICJQdWJsaWMiLAogICAgICAgICAgICAidHlwZSIgOiAiRnVuY3Rpb25EZWYiLAogICAgICAgICAgICAiZXhwcmVzc2lvbiIgOiB7CiAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICI2MDo1LTg0OjExIiwKICAgICAgICAgICAgICAgInR5cGUiIDogIklmIiwKICAgICAgICAgICAgICAgImNvbmRpdGlvbiIgOiB7CiAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICI2MDo4LTYwOjIzIiwKICAgICAgICAgICAgICAgICAgInR5cGUiIDogIklzTnVsbCIsCiAgICAgICAgICAgICAgICAgICJvcGVyYW5kIiA6IHsKICAgICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjYwOjgtNjA6MTUiLAogICAgICAgICAgICAgICAgICAgICAibmFtZSIgOiAicXVhbnRpdHkiLAogICAgICAgICAgICAgICAgICAgICAidHlwZSIgOiAiT3BlcmFuZFJlZiIKICAgICAgICAgICAgICAgICAgfQogICAgICAgICAgICAgICB9LAogICAgICAgICAgICAgICAidGhlbiIgOiB7CiAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJBcyIsCiAgICAgICAgICAgICAgICAgICJvcGVyYW5kIiA6IHsKICAgICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjYwOjMwLTYwOjMzIiwKICAgICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk51bGwiCiAgICAgICAgICAgICAgICAgIH0sCiAgICAgICAgICAgICAgICAgICJhc1R5cGVTcGVjaWZpZXIiIDogewogICAgICAgICAgICAgICAgICAgICAidHlwZSIgOiAiSW50ZXJ2YWxUeXBlU3BlY2lmaWVyIiwKICAgICAgICAgICAgICAgICAgICAgInBvaW50VHlwZSIgOiB7CiAgICAgICAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJ7dXJuOmhsNy1vcmc6ZWxtLXR5cGVzOnIxfVF1YW50aXR5IiwKICAgICAgICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk5hbWVkVHlwZVNwZWNpZmllciIKICAgICAgICAgICAgICAgICAgICAgfQogICAgICAgICAgICAgICAgICB9CiAgICAgICAgICAgICAgIH0sCiAgICAgICAgICAgICAgICJlbHNlIiA6IHsKICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjYxOjktODQ6MTEiLAogICAgICAgICAgICAgICAgICAidHlwZSIgOiAiQ2FzZSIsCiAgICAgICAgICAgICAgICAgICJjb21wYXJhbmQiIDogewogICAgICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiNjE6MTQtNjE6MzgiLAogICAgICAgICAgICAgICAgICAgICAicGF0aCIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAgICAgICAidHlwZSIgOiAiUHJvcGVydHkiLAogICAgICAgICAgICAgICAgICAgICAic291cmNlIiA6IHsKICAgICAgICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjYxOjE0LTYxOjMyIiwKICAgICAgICAgICAgICAgICAgICAgICAgInBhdGgiIDogImNvbXBhcmF0b3IiLAogICAgICAgICAgICAgICAgICAgICAgICAidHlwZSIgOiAiUHJvcGVydHkiLAogICAgICAgICAgICAgICAgICAgICAgICAic291cmNlIiA6IHsKICAgICAgICAgICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjYxOjE0LTYxOjIxIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgIm5hbWUiIDogInF1YW50aXR5IiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk9wZXJhbmRSZWYiCiAgICAgICAgICAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgICAgICAgICAgfQogICAgICAgICAgICAgICAgICB9LAogICAgICAgICAgICAgICAgICAiY2FzZUl0ZW0iIDogWyB7CiAgICAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICI2MjoxMy02NjoxNyIsCiAgICAgICAgICAgICAgICAgICAgICJ3aGVuIiA6IHsKICAgICAgICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjYyOjE4LTYyOjIwIiwKICAgICAgICAgICAgICAgICAgICAgICAgInZhbHVlVHlwZSIgOiAie3VybjpobDctb3JnOmVsbS10eXBlczpyMX1TdHJpbmciLAogICAgICAgICAgICAgICAgICAgICAgICAidmFsdWUiIDogIjwiLAogICAgICAgICAgICAgICAgICAgICAgICAidHlwZSIgOiAiTGl0ZXJhbCIKICAgICAgICAgICAgICAgICAgICAgfSwKICAgICAgICAgICAgICAgICAgICAgInRoZW4iIDogewogICAgICAgICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiNjM6MTctNjY6MTciLAogICAgICAgICAgICAgICAgICAgICAgICAibG93Q2xvc2VkIiA6IHRydWUsCiAgICAgICAgICAgICAgICAgICAgICAgICJoaWdoQ2xvc2VkIiA6IGZhbHNlLAogICAgICAgICAgICAgICAgICAgICAgICAidHlwZSIgOiAiSW50ZXJ2YWwiLAogICAgICAgICAgICAgICAgICAgICAgICAibG93IiA6IHsKICAgICAgICAgICAgICAgICAgICAgICAgICAgImFzVHlwZSIgOiAie3VybjpobDctb3JnOmVsbS10eXBlczpyMX1RdWFudGl0eSIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJBcyIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICJvcGVyYW5kIiA6IHsKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjY0OjIxLTY0OjI0IiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk51bGwiCiAgICAgICAgICAgICAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgICAgICAgICAgICAgfSwKICAgICAgICAgICAgICAgICAgICAgICAgImhpZ2giIDogewogICAgICAgICAgICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiNjU6MjEtNjU6NTgiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAibmFtZSIgOiAiVG9RdWFudGl0eUlnbm9yaW5nQ29tcGFyYXRvciIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJGdW5jdGlvblJlZiIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICJvcGVyYW5kIiA6IFsgewogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiNjU6NTAtNjU6NTciLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAibmFtZSIgOiAicXVhbnRpdHkiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAidHlwZSIgOiAiT3BlcmFuZFJlZiIKICAgICAgICAgICAgICAgICAgICAgICAgICAgfSBdCiAgICAgICAgICAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgICAgICAgICAgfQogICAgICAgICAgICAgICAgICB9LCB7CiAgICAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICI2NzoxMy03MToxNyIsCiAgICAgICAgICAgICAgICAgICAgICJ3aGVuIiA6IHsKICAgICAgICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjY3OjE4LTY3OjIxIiwKICAgICAgICAgICAgICAgICAgICAgICAgInZhbHVlVHlwZSIgOiAie3VybjpobDctb3JnOmVsbS10eXBlczpyMX1TdHJpbmciLAogICAgICAgICAgICAgICAgICAgICAgICAidmFsdWUiIDogIjw9IiwKICAgICAgICAgICAgICAgICAgICAgICAgInR5cGUiIDogIkxpdGVyYWwiCiAgICAgICAgICAgICAgICAgICAgIH0sCiAgICAgICAgICAgICAgICAgICAgICJ0aGVuIiA6IHsKICAgICAgICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjY4OjE3LTcxOjE3IiwKICAgICAgICAgICAgICAgICAgICAgICAgImxvd0Nsb3NlZCIgOiB0cnVlLAogICAgICAgICAgICAgICAgICAgICAgICAiaGlnaENsb3NlZCIgOiB0cnVlLAogICAgICAgICAgICAgICAgICAgICAgICAidHlwZSIgOiAiSW50ZXJ2YWwiLAogICAgICAgICAgICAgICAgICAgICAgICAibG93IiA6IHsKICAgICAgICAgICAgICAgICAgICAgICAgICAgImFzVHlwZSIgOiAie3VybjpobDctb3JnOmVsbS10eXBlczpyMX1RdWFudGl0eSIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJBcyIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICJvcGVyYW5kIiA6IHsKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjY5OjIxLTY5OjI0IiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk51bGwiCiAgICAgICAgICAgICAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgICAgICAgICAgICAgfSwKICAgICAgICAgICAgICAgICAgICAgICAgImhpZ2giIDogewogICAgICAgICAgICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiNzA6MjEtNzA6NTgiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAibmFtZSIgOiAiVG9RdWFudGl0eUlnbm9yaW5nQ29tcGFyYXRvciIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJGdW5jdGlvblJlZiIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICJvcGVyYW5kIiA6IFsgewogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiNzA6NTAtNzA6NTciLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAibmFtZSIgOiAicXVhbnRpdHkiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAidHlwZSIgOiAiT3BlcmFuZFJlZiIKICAgICAgICAgICAgICAgICAgICAgICAgICAgfSBdCiAgICAgICAgICAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgICAgICAgICAgfQogICAgICAgICAgICAgICAgICB9LCB7CiAgICAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICI3MjoxMy03NjoxNyIsCiAgICAgICAgICAgICAgICAgICAgICJ3aGVuIiA6IHsKICAgICAgICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjcyOjE4LTcyOjIxIiwKICAgICAgICAgICAgICAgICAgICAgICAgInZhbHVlVHlwZSIgOiAie3VybjpobDctb3JnOmVsbS10eXBlczpyMX1TdHJpbmciLAogICAgICAgICAgICAgICAgICAgICAgICAidmFsdWUiIDogIj49IiwKICAgICAgICAgICAgICAgICAgICAgICAgInR5cGUiIDogIkxpdGVyYWwiCiAgICAgICAgICAgICAgICAgICAgIH0sCiAgICAgICAgICAgICAgICAgICAgICJ0aGVuIiA6IHsKICAgICAgICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjczOjE3LTc2OjE3IiwKICAgICAgICAgICAgICAgICAgICAgICAgImxvd0Nsb3NlZCIgOiB0cnVlLAogICAgICAgICAgICAgICAgICAgICAgICAiaGlnaENsb3NlZCIgOiB0cnVlLAogICAgICAgICAgICAgICAgICAgICAgICAidHlwZSIgOiAiSW50ZXJ2YWwiLAogICAgICAgICAgICAgICAgICAgICAgICAibG93IiA6IHsKICAgICAgICAgICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjc0OjIxLTc0OjU4IiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgIm5hbWUiIDogIlRvUXVhbnRpdHlJZ25vcmluZ0NvbXBhcmF0b3IiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAidHlwZSIgOiAiRnVuY3Rpb25SZWYiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAib3BlcmFuZCIgOiBbIHsKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjc0OjUwLTc0OjU3IiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIm5hbWUiIDogInF1YW50aXR5IiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk9wZXJhbmRSZWYiCiAgICAgICAgICAgICAgICAgICAgICAgICAgIH0gXQogICAgICAgICAgICAgICAgICAgICAgICB9LAogICAgICAgICAgICAgICAgICAgICAgICAiaGlnaCIgOiB7CiAgICAgICAgICAgICAgICAgICAgICAgICAgICJhc1R5cGUiIDogInt1cm46aGw3LW9yZzplbG0tdHlwZXM6cjF9UXVhbnRpdHkiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAidHlwZSIgOiAiQXMiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAib3BlcmFuZCIgOiB7CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICI3NToyMS03NToyNCIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJOdWxsIgogICAgICAgICAgICAgICAgICAgICAgICAgICB9CiAgICAgICAgICAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgICAgICAgICAgfQogICAgICAgICAgICAgICAgICB9LCB7CiAgICAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICI3NzoxMy04MToxNyIsCiAgICAgICAgICAgICAgICAgICAgICJ3aGVuIiA6IHsKICAgICAgICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjc3OjE4LTc3OjIwIiwKICAgICAgICAgICAgICAgICAgICAgICAgInZhbHVlVHlwZSIgOiAie3VybjpobDctb3JnOmVsbS10eXBlczpyMX1TdHJpbmciLAogICAgICAgICAgICAgICAgICAgICAgICAidmFsdWUiIDogIj4iLAogICAgICAgICAgICAgICAgICAgICAgICAidHlwZSIgOiAiTGl0ZXJhbCIKICAgICAgICAgICAgICAgICAgICAgfSwKICAgICAgICAgICAgICAgICAgICAgInRoZW4iIDogewogICAgICAgICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiNzg6MTctODE6MTciLAogICAgICAgICAgICAgICAgICAgICAgICAibG93Q2xvc2VkIiA6IGZhbHNlLAogICAgICAgICAgICAgICAgICAgICAgICAiaGlnaENsb3NlZCIgOiB0cnVlLAogICAgICAgICAgICAgICAgICAgICAgICAidHlwZSIgOiAiSW50ZXJ2YWwiLAogICAgICAgICAgICAgICAgICAgICAgICAibG93IiA6IHsKICAgICAgICAgICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjc5OjIxLTc5OjU4IiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgIm5hbWUiIDogIlRvUXVhbnRpdHlJZ25vcmluZ0NvbXBhcmF0b3IiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAidHlwZSIgOiAiRnVuY3Rpb25SZWYiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAib3BlcmFuZCIgOiBbIHsKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjc5OjUwLTc5OjU3IiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIm5hbWUiIDogInF1YW50aXR5IiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk9wZXJhbmRSZWYiCiAgICAgICAgICAgICAgICAgICAgICAgICAgIH0gXQogICAgICAgICAgICAgICAgICAgICAgICB9LAogICAgICAgICAgICAgICAgICAgICAgICAiaGlnaCIgOiB7CiAgICAgICAgICAgICAgICAgICAgICAgICAgICJhc1R5cGUiIDogInt1cm46aGw3LW9yZzplbG0tdHlwZXM6cjF9UXVhbnRpdHkiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAidHlwZSIgOiAiQXMiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAib3BlcmFuZCIgOiB7CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICI4MDoyMS04MDoyNCIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJOdWxsIgogICAgICAgICAgICAgICAgICAgICAgICAgICB9CiAgICAgICAgICAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgICAgICAgICAgfQogICAgICAgICAgICAgICAgICB9IF0sCiAgICAgICAgICAgICAgICAgICJlbHNlIiA6IHsKICAgICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjgzOjE3LTgzOjY4IiwKICAgICAgICAgICAgICAgICAgICAgImxvd0Nsb3NlZCIgOiB0cnVlLAogICAgICAgICAgICAgICAgICAgICAiaGlnaENsb3NlZCIgOiB0cnVlLAogICAgICAgICAgICAgICAgICAgICAidHlwZSIgOiAiSW50ZXJ2YWwiLAogICAgICAgICAgICAgICAgICAgICAibG93IiA6IHsKICAgICAgICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjgzOjI2LTgzOjQ1IiwKICAgICAgICAgICAgICAgICAgICAgICAgIm5hbWUiIDogIlRvUXVhbnRpdHkiLAogICAgICAgICAgICAgICAgICAgICAgICAidHlwZSIgOiAiRnVuY3Rpb25SZWYiLAogICAgICAgICAgICAgICAgICAgICAgICAib3BlcmFuZCIgOiBbIHsKICAgICAgICAgICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjgzOjM3LTgzOjQ0IiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgIm5hbWUiIDogInF1YW50aXR5IiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk9wZXJhbmRSZWYiCiAgICAgICAgICAgICAgICAgICAgICAgIH0gXQogICAgICAgICAgICAgICAgICAgICB9LAogICAgICAgICAgICAgICAgICAgICAiaGlnaCIgOiB7CiAgICAgICAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICI4Mzo0OC04Mzo2NyIsCiAgICAgICAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJUb1F1YW50aXR5IiwKICAgICAgICAgICAgICAgICAgICAgICAgInR5cGUiIDogIkZ1bmN0aW9uUmVmIiwKICAgICAgICAgICAgICAgICAgICAgICAgIm9wZXJhbmQiIDogWyB7CiAgICAgICAgICAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICI4Mzo1OS04Mzo2NiIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJxdWFudGl0eSIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJPcGVyYW5kUmVmIgogICAgICAgICAgICAgICAgICAgICAgICB9IF0KICAgICAgICAgICAgICAgICAgICAgfQogICAgICAgICAgICAgICAgICB9CiAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgfSwKICAgICAgICAgICAgIm9wZXJhbmQiIDogWyB7CiAgICAgICAgICAgICAgICJuYW1lIiA6ICJxdWFudGl0eSIsCiAgICAgICAgICAgICAgICJvcGVyYW5kVHlwZVNwZWNpZmllciIgOiB7CiAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICI1OTozNy01OTo0OSIsCiAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJ7aHR0cDovL2hsNy5vcmcvZmhpcn1RdWFudGl0eSIsCiAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJOYW1lZFR5cGVTcGVjaWZpZXIiCiAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgfSBdCiAgICAgICAgIH0sIHsKICAgICAgICAgICAgImxvY2F0b3IiIDogIjg2OjEtOTA6MTA3IiwKICAgICAgICAgICAgIm5hbWUiIDogIlRvUmF0aW8iLAogICAgICAgICAgICAiY29udGV4dCIgOiAiUGF0aWVudCIsCiAgICAgICAgICAgICJhY2Nlc3NMZXZlbCIgOiAiUHVibGljIiwKICAgICAgICAgICAgInR5cGUiIDogIkZ1bmN0aW9uRGVmIiwKICAgICAgICAgICAgImV4cHJlc3Npb24iIDogewogICAgICAgICAgICAgICAibG9jYXRvciIgOiAiODc6NS05MDoxMDciLAogICAgICAgICAgICAgICAidHlwZSIgOiAiSWYiLAogICAgICAgICAgICAgICAiY29uZGl0aW9uIiA6IHsKICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjg3OjgtODc6MjAiLAogICAgICAgICAgICAgICAgICAidHlwZSIgOiAiSXNOdWxsIiwKICAgICAgICAgICAgICAgICAgIm9wZXJhbmQiIDogewogICAgICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiODc6OC04NzoxMiIsCiAgICAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJyYXRpbyIsCiAgICAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJPcGVyYW5kUmVmIgogICAgICAgICAgICAgICAgICB9CiAgICAgICAgICAgICAgIH0sCiAgICAgICAgICAgICAgICJ0aGVuIiA6IHsKICAgICAgICAgICAgICAgICAgImFzVHlwZSIgOiAie3VybjpobDctb3JnOmVsbS10eXBlczpyMX1SYXRpbyIsCiAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJBcyIsCiAgICAgICAgICAgICAgICAgICJvcGVyYW5kIiA6IHsKICAgICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjg4OjktODg6MTIiLAogICAgICAgICAgICAgICAgICAgICAidHlwZSIgOiAiTnVsbCIKICAgICAgICAgICAgICAgICAgfQogICAgICAgICAgICAgICB9LAogICAgICAgICAgICAgICAiZWxzZSIgOiB7CiAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICI5MDo5LTkwOjEwNyIsCiAgICAgICAgICAgICAgICAgICJjbGFzc1R5cGUiIDogInt1cm46aGw3LW9yZzplbG0tdHlwZXM6cjF9UmF0aW8iLAogICAgICAgICAgICAgICAgICAidHlwZSIgOiAiSW5zdGFuY2UiLAogICAgICAgICAgICAgICAgICAiZWxlbWVudCIgOiBbIHsKICAgICAgICAgICAgICAgICAgICAgIm5hbWUiIDogIm51bWVyYXRvciIsCiAgICAgICAgICAgICAgICAgICAgICJ2YWx1ZSIgOiB7CiAgICAgICAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICI5MDozNS05MDo2MSIsCiAgICAgICAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJUb1F1YW50aXR5IiwKICAgICAgICAgICAgICAgICAgICAgICAgInR5cGUiIDogIkZ1bmN0aW9uUmVmIiwKICAgICAgICAgICAgICAgICAgICAgICAgIm9wZXJhbmQiIDogWyB7CiAgICAgICAgICAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICI5MDo0Ni05MDo2MCIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICJwYXRoIiA6ICJudW1lcmF0b3IiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAidHlwZSIgOiAiUHJvcGVydHkiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAic291cmNlIiA6IHsKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjkwOjQ2LTkwOjUwIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIm5hbWUiIDogInJhdGlvIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk9wZXJhbmRSZWYiCiAgICAgICAgICAgICAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgICAgICAgICAgICAgfSBdCiAgICAgICAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgICAgICAgfSwgewogICAgICAgICAgICAgICAgICAgICAibmFtZSIgOiAiZGVub21pbmF0b3IiLAogICAgICAgICAgICAgICAgICAgICAidmFsdWUiIDogewogICAgICAgICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiOTA6NzctOTA6MTA1IiwKICAgICAgICAgICAgICAgICAgICAgICAgIm5hbWUiIDogIlRvUXVhbnRpdHkiLAogICAgICAgICAgICAgICAgICAgICAgICAidHlwZSIgOiAiRnVuY3Rpb25SZWYiLAogICAgICAgICAgICAgICAgICAgICAgICAib3BlcmFuZCIgOiBbIHsKICAgICAgICAgICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjkwOjg4LTkwOjEwNCIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICJwYXRoIiA6ICJkZW5vbWluYXRvciIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJQcm9wZXJ0eSIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICJzb3VyY2UiIDogewogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiOTA6ODgtOTA6OTIiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAibmFtZSIgOiAicmF0aW8iLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAidHlwZSIgOiAiT3BlcmFuZFJlZiIKICAgICAgICAgICAgICAgICAgICAgICAgICAgfQogICAgICAgICAgICAgICAgICAgICAgICB9IF0KICAgICAgICAgICAgICAgICAgICAgfQogICAgICAgICAgICAgICAgICB9IF0KICAgICAgICAgICAgICAgfQogICAgICAgICAgICB9LAogICAgICAgICAgICAib3BlcmFuZCIgOiBbIHsKICAgICAgICAgICAgICAgIm5hbWUiIDogInJhdGlvIiwKICAgICAgICAgICAgICAgIm9wZXJhbmRUeXBlU3BlY2lmaWVyIiA6IHsKICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjg2OjMxLTg2OjQwIiwKICAgICAgICAgICAgICAgICAgIm5hbWUiIDogIntodHRwOi8vaGw3Lm9yZy9maGlyfVJhdGlvIiwKICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk5hbWVkVHlwZVNwZWNpZmllciIKICAgICAgICAgICAgICAgfQogICAgICAgICAgICB9IF0KICAgICAgICAgfSwgewogICAgICAgICAgICAibG9jYXRvciIgOiAiOTI6MS05Njo2MyIsCiAgICAgICAgICAgICJuYW1lIiA6ICJUb0ludGVydmFsIiwKICAgICAgICAgICAgImNvbnRleHQiIDogIlBhdGllbnQiLAogICAgICAgICAgICAiYWNjZXNzTGV2ZWwiIDogIlB1YmxpYyIsCiAgICAgICAgICAgICJ0eXBlIiA6ICJGdW5jdGlvbkRlZiIsCiAgICAgICAgICAgICJleHByZXNzaW9uIiA6IHsKICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjkzOjUtOTY6NjMiLAogICAgICAgICAgICAgICAidHlwZSIgOiAiSWYiLAogICAgICAgICAgICAgICAiY29uZGl0aW9uIiA6IHsKICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjkzOjgtOTM6MjAiLAogICAgICAgICAgICAgICAgICAidHlwZSIgOiAiSXNOdWxsIiwKICAgICAgICAgICAgICAgICAgIm9wZXJhbmQiIDogewogICAgICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiOTM6OC05MzoxMiIsCiAgICAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJyYW5nZSIsCiAgICAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJPcGVyYW5kUmVmIgogICAgICAgICAgICAgICAgICB9CiAgICAgICAgICAgICAgIH0sCiAgICAgICAgICAgICAgICJ0aGVuIiA6IHsKICAgICAgICAgICAgICAgICAgInR5cGUiIDogIkFzIiwKICAgICAgICAgICAgICAgICAgIm9wZXJhbmQiIDogewogICAgICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiOTQ6OS05NDoxMiIsCiAgICAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJOdWxsIgogICAgICAgICAgICAgICAgICB9LAogICAgICAgICAgICAgICAgICAiYXNUeXBlU3BlY2lmaWVyIiA6IHsKICAgICAgICAgICAgICAgICAgICAgInR5cGUiIDogIkludGVydmFsVHlwZVNwZWNpZmllciIsCiAgICAgICAgICAgICAgICAgICAgICJwb2ludFR5cGUiIDogewogICAgICAgICAgICAgICAgICAgICAgICAibmFtZSIgOiAie3VybjpobDctb3JnOmVsbS10eXBlczpyMX1RdWFudGl0eSIsCiAgICAgICAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJOYW1lZFR5cGVTcGVjaWZpZXIiCiAgICAgICAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgICAgICAgfQogICAgICAgICAgICAgICB9LAogICAgICAgICAgICAgICAiZWxzZSIgOiB7CiAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICI5Njo5LTk2OjYzIiwKICAgICAgICAgICAgICAgICAgImxvd0Nsb3NlZCIgOiB0cnVlLAogICAgICAgICAgICAgICAgICAiaGlnaENsb3NlZCIgOiB0cnVlLAogICAgICAgICAgICAgICAgICAidHlwZSIgOiAiSW50ZXJ2YWwiLAogICAgICAgICAgICAgICAgICAibG93IiA6IHsKICAgICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjk2OjE4LTk2OjM4IiwKICAgICAgICAgICAgICAgICAgICAgIm5hbWUiIDogIlRvUXVhbnRpdHkiLAogICAgICAgICAgICAgICAgICAgICAidHlwZSIgOiAiRnVuY3Rpb25SZWYiLAogICAgICAgICAgICAgICAgICAgICAib3BlcmFuZCIgOiBbIHsKICAgICAgICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjk2OjI5LTk2OjM3IiwKICAgICAgICAgICAgICAgICAgICAgICAgInBhdGgiIDogImxvdyIsCiAgICAgICAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJQcm9wZXJ0eSIsCiAgICAgICAgICAgICAgICAgICAgICAgICJzb3VyY2UiIDogewogICAgICAgICAgICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiOTY6MjktOTY6MzMiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAibmFtZSIgOiAicmFuZ2UiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAidHlwZSIgOiAiT3BlcmFuZFJlZiIKICAgICAgICAgICAgICAgICAgICAgICAgfQogICAgICAgICAgICAgICAgICAgICB9IF0KICAgICAgICAgICAgICAgICAgfSwKICAgICAgICAgICAgICAgICAgImhpZ2giIDogewogICAgICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiOTY6NDEtOTY6NjIiLAogICAgICAgICAgICAgICAgICAgICAibmFtZSIgOiAiVG9RdWFudGl0eSIsCiAgICAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJGdW5jdGlvblJlZiIsCiAgICAgICAgICAgICAgICAgICAgICJvcGVyYW5kIiA6IFsgewogICAgICAgICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiOTY6NTItOTY6NjEiLAogICAgICAgICAgICAgICAgICAgICAgICAicGF0aCIgOiAiaGlnaCIsCiAgICAgICAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJQcm9wZXJ0eSIsCiAgICAgICAgICAgICAgICAgICAgICAgICJzb3VyY2UiIDogewogICAgICAgICAgICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiOTY6NTItOTY6NTYiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAibmFtZSIgOiAicmFuZ2UiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAidHlwZSIgOiAiT3BlcmFuZFJlZiIKICAgICAgICAgICAgICAgICAgICAgICAgfQogICAgICAgICAgICAgICAgICAgICB9IF0KICAgICAgICAgICAgICAgICAgfQogICAgICAgICAgICAgICB9CiAgICAgICAgICAgIH0sCiAgICAgICAgICAgICJvcGVyYW5kIiA6IFsgewogICAgICAgICAgICAgICAibmFtZSIgOiAicmFuZ2UiLAogICAgICAgICAgICAgICAib3BlcmFuZFR5cGVTcGVjaWZpZXIiIDogewogICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiOTI6MzQtOTI6NDMiLAogICAgICAgICAgICAgICAgICAibmFtZSIgOiAie2h0dHA6Ly9obDcub3JnL2ZoaXJ9UmFuZ2UiLAogICAgICAgICAgICAgICAgICAidHlwZSIgOiAiTmFtZWRUeXBlU3BlY2lmaWVyIgogICAgICAgICAgICAgICB9CiAgICAgICAgICAgIH0gXQogICAgICAgICB9LCB7CiAgICAgICAgICAgICJsb2NhdG9yIiA6ICI5ODoxLTEwNzo5IiwKICAgICAgICAgICAgIm5hbWUiIDogIlRvQ29kZSIsCiAgICAgICAgICAgICJjb250ZXh0IiA6ICJQYXRpZW50IiwKICAgICAgICAgICAgImFjY2Vzc0xldmVsIiA6ICJQdWJsaWMiLAogICAgICAgICAgICAidHlwZSIgOiAiRnVuY3Rpb25EZWYiLAogICAgICAgICAgICAiZXhwcmVzc2lvbiIgOiB7CiAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICI5OTo1LTEwNzo5IiwKICAgICAgICAgICAgICAgInR5cGUiIDogIklmIiwKICAgICAgICAgICAgICAgImNvbmRpdGlvbiIgOiB7CiAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICI5OTo4LTk5OjIxIiwKICAgICAgICAgICAgICAgICAgInR5cGUiIDogIklzTnVsbCIsCiAgICAgICAgICAgICAgICAgICJvcGVyYW5kIiA6IHsKICAgICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjk5OjgtOTk6MTMiLAogICAgICAgICAgICAgICAgICAgICAibmFtZSIgOiAiY29kaW5nIiwKICAgICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk9wZXJhbmRSZWYiCiAgICAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgICAgfSwKICAgICAgICAgICAgICAgInRoZW4iIDogewogICAgICAgICAgICAgICAgICAiYXNUeXBlIiA6ICJ7dXJuOmhsNy1vcmc6ZWxtLXR5cGVzOnIxfUNvZGUiLAogICAgICAgICAgICAgICAgICAidHlwZSIgOiAiQXMiLAogICAgICAgICAgICAgICAgICAib3BlcmFuZCIgOiB7CiAgICAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIxMDA6OS0xMDA6MTIiLAogICAgICAgICAgICAgICAgICAgICAidHlwZSIgOiAiTnVsbCIKICAgICAgICAgICAgICAgICAgfQogICAgICAgICAgICAgICB9LAogICAgICAgICAgICAgICAiZWxzZSIgOiB7CiAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIxMDI6OS0xMDc6OSIsCiAgICAgICAgICAgICAgICAgICJjbGFzc1R5cGUiIDogInt1cm46aGw3LW9yZzplbG0tdHlwZXM6cjF9Q29kZSIsCiAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJJbnN0YW5jZSIsCiAgICAgICAgICAgICAgICAgICJlbGVtZW50IiA6IFsgewogICAgICAgICAgICAgICAgICAgICAibmFtZSIgOiAiY29kZSIsCiAgICAgICAgICAgICAgICAgICAgICJ2YWx1ZSIgOiB7CiAgICAgICAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIxMDM6MTctMTAzOjMzIiwKICAgICAgICAgICAgICAgICAgICAgICAgInBhdGgiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgICAgICAgICAgInR5cGUiIDogIlByb3BlcnR5IiwKICAgICAgICAgICAgICAgICAgICAgICAgInNvdXJjZSIgOiB7CiAgICAgICAgICAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIxMDM6MTctMTAzOjI3IiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgInBhdGgiIDogImNvZGUiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAidHlwZSIgOiAiUHJvcGVydHkiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAic291cmNlIiA6IHsKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjEwMzoxNy0xMDM6MjIiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAibmFtZSIgOiAiY29kaW5nIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk9wZXJhbmRSZWYiCiAgICAgICAgICAgICAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgICAgICAgICAgICAgfQogICAgICAgICAgICAgICAgICAgICB9CiAgICAgICAgICAgICAgICAgIH0sIHsKICAgICAgICAgICAgICAgICAgICAgIm5hbWUiIDogInN5c3RlbSIsCiAgICAgICAgICAgICAgICAgICAgICJ2YWx1ZSIgOiB7CiAgICAgICAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIxMDQ6MTktMTA0OjM3IiwKICAgICAgICAgICAgICAgICAgICAgICAgInBhdGgiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgICAgICAgICAgInR5cGUiIDogIlByb3BlcnR5IiwKICAgICAgICAgICAgICAgICAgICAgICAgInNvdXJjZSIgOiB7CiAgICAgICAgICAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIxMDQ6MTktMTA0OjMxIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgInBhdGgiIDogInN5c3RlbSIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJQcm9wZXJ0eSIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICJzb3VyY2UiIDogewogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMTA0OjE5LTEwNDoyNCIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJjb2RpbmciLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAidHlwZSIgOiAiT3BlcmFuZFJlZiIKICAgICAgICAgICAgICAgICAgICAgICAgICAgfQogICAgICAgICAgICAgICAgICAgICAgICB9CiAgICAgICAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgICAgICAgfSwgewogICAgICAgICAgICAgICAgICAgICAibmFtZSIgOiAidmVyc2lvbiIsCiAgICAgICAgICAgICAgICAgICAgICJ2YWx1ZSIgOiB7CiAgICAgICAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIxMDU6MjAtMTA1OjM5IiwKICAgICAgICAgICAgICAgICAgICAgICAgInBhdGgiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgICAgICAgICAgInR5cGUiIDogIlByb3BlcnR5IiwKICAgICAgICAgICAgICAgICAgICAgICAgInNvdXJjZSIgOiB7CiAgICAgICAgICAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIxMDU6MjAtMTA1OjMzIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgInBhdGgiIDogInZlcnNpb24iLAogICAgICAgICAgICAgICAgICAgICAgICAgICAidHlwZSIgOiAiUHJvcGVydHkiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAic291cmNlIiA6IHsKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjEwNToyMC0xMDU6MjUiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAibmFtZSIgOiAiY29kaW5nIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk9wZXJhbmRSZWYiCiAgICAgICAgICAgICAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgICAgICAgICAgICAgfQogICAgICAgICAgICAgICAgICAgICB9CiAgICAgICAgICAgICAgICAgIH0sIHsKICAgICAgICAgICAgICAgICAgICAgIm5hbWUiIDogImRpc3BsYXkiLAogICAgICAgICAgICAgICAgICAgICAidmFsdWUiIDogewogICAgICAgICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMTA2OjIwLTEwNjozOSIsCiAgICAgICAgICAgICAgICAgICAgICAgICJwYXRoIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJQcm9wZXJ0eSIsCiAgICAgICAgICAgICAgICAgICAgICAgICJzb3VyY2UiIDogewogICAgICAgICAgICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMTA2OjIwLTEwNjozMyIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICJwYXRoIiA6ICJkaXNwbGF5IiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgInR5cGUiIDogIlByb3BlcnR5IiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgInNvdXJjZSIgOiB7CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIxMDY6MjAtMTA2OjI1IiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIm5hbWUiIDogImNvZGluZyIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJPcGVyYW5kUmVmIgogICAgICAgICAgICAgICAgICAgICAgICAgICB9CiAgICAgICAgICAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgICAgICAgICAgfQogICAgICAgICAgICAgICAgICB9IF0KICAgICAgICAgICAgICAgfQogICAgICAgICAgICB9LAogICAgICAgICAgICAib3BlcmFuZCIgOiBbIHsKICAgICAgICAgICAgICAgIm5hbWUiIDogImNvZGluZyIsCiAgICAgICAgICAgICAgICJvcGVyYW5kVHlwZVNwZWNpZmllciIgOiB7CiAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICI5ODozMS05ODo0MSIsCiAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJ7aHR0cDovL2hsNy5vcmcvZmhpcn1Db2RpbmciLAogICAgICAgICAgICAgICAgICAidHlwZSIgOiAiTmFtZWRUeXBlU3BlY2lmaWVyIgogICAgICAgICAgICAgICB9CiAgICAgICAgICAgIH0gXQogICAgICAgICB9LCB7CiAgICAgICAgICAgICJsb2NhdG9yIiA6ICIxMDk6MS0xMTY6OSIsCiAgICAgICAgICAgICJuYW1lIiA6ICJUb0NvbmNlcHQiLAogICAgICAgICAgICAiY29udGV4dCIgOiAiUGF0aWVudCIsCiAgICAgICAgICAgICJhY2Nlc3NMZXZlbCIgOiAiUHVibGljIiwKICAgICAgICAgICAgInR5cGUiIDogIkZ1bmN0aW9uRGVmIiwKICAgICAgICAgICAgImV4cHJlc3Npb24iIDogewogICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMTEwOjUtMTE2OjkiLAogICAgICAgICAgICAgICAidHlwZSIgOiAiSWYiLAogICAgICAgICAgICAgICAiY29uZGl0aW9uIiA6IHsKICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjExMDo4LTExMDoyMiIsCiAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJJc051bGwiLAogICAgICAgICAgICAgICAgICAib3BlcmFuZCIgOiB7CiAgICAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIxMTA6OC0xMTA6MTQiLAogICAgICAgICAgICAgICAgICAgICAibmFtZSIgOiAiY29uY2VwdCIsCiAgICAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJPcGVyYW5kUmVmIgogICAgICAgICAgICAgICAgICB9CiAgICAgICAgICAgICAgIH0sCiAgICAgICAgICAgICAgICJ0aGVuIiA6IHsKICAgICAgICAgICAgICAgICAgImFzVHlwZSIgOiAie3VybjpobDctb3JnOmVsbS10eXBlczpyMX1Db25jZXB0IiwKICAgICAgICAgICAgICAgICAgInR5cGUiIDogIkFzIiwKICAgICAgICAgICAgICAgICAgIm9wZXJhbmQiIDogewogICAgICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMTExOjktMTExOjEyIiwKICAgICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk51bGwiCiAgICAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgICAgfSwKICAgICAgICAgICAgICAgImVsc2UiIDogewogICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMTEzOjktMTE2OjkiLAogICAgICAgICAgICAgICAgICAiY2xhc3NUeXBlIiA6ICJ7dXJuOmhsNy1vcmc6ZWxtLXR5cGVzOnIxfUNvbmNlcHQiLAogICAgICAgICAgICAgICAgICAidHlwZSIgOiAiSW5zdGFuY2UiLAogICAgICAgICAgICAgICAgICAiZWxlbWVudCIgOiBbIHsKICAgICAgICAgICAgICAgICAgICAgIm5hbWUiIDogImNvZGVzIiwKICAgICAgICAgICAgICAgICAgICAgInZhbHVlIiA6IHsKICAgICAgICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjExNDoyMC0xMTQ6NTIiLAogICAgICAgICAgICAgICAgICAgICAgICAidHlwZSIgOiAiUXVlcnkiLAogICAgICAgICAgICAgICAgICAgICAgICAic291cmNlIiA6IFsgewogICAgICAgICAgICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMTE0OjIwLTExNDozNSIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICJhbGlhcyIgOiAiQyIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICJleHByZXNzaW9uIiA6IHsKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjExNDoyMC0xMTQ6MzMiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAicGF0aCIgOiAiY29kaW5nIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInR5cGUiIDogIlByb3BlcnR5IiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInNvdXJjZSIgOiB7CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJjb25jZXB0IiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk9wZXJhbmRSZWYiCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgICAgICAgICAgICAgICAgfQogICAgICAgICAgICAgICAgICAgICAgICB9IF0sCiAgICAgICAgICAgICAgICAgICAgICAgICJyZWxhdGlvbnNoaXAiIDogWyBdLAogICAgICAgICAgICAgICAgICAgICAgICAicmV0dXJuIiA6IHsKICAgICAgICAgICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjExNDozNy0xMTQ6NTIiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAiZXhwcmVzc2lvbiIgOiB7CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIxMTQ6NDQtMTE0OjUyIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIm5hbWUiIDogIlRvQ29kZSIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJGdW5jdGlvblJlZiIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJvcGVyYW5kIiA6IFsgewogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMTE0OjUxIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIm5hbWUiIDogIkMiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAidHlwZSIgOiAiQWxpYXNSZWYiCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIH0gXQogICAgICAgICAgICAgICAgICAgICAgICAgICB9CiAgICAgICAgICAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgICAgICAgICAgfQogICAgICAgICAgICAgICAgICB9LCB7CiAgICAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJkaXNwbGF5IiwKICAgICAgICAgICAgICAgICAgICAgInZhbHVlIiA6IHsKICAgICAgICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjExNToyMi0xMTU6MzkiLAogICAgICAgICAgICAgICAgICAgICAgICAicGF0aCIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAgICAgICAgICAidHlwZSIgOiAiUHJvcGVydHkiLAogICAgICAgICAgICAgICAgICAgICAgICAic291cmNlIiA6IHsKICAgICAgICAgICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjExNToyMi0xMTU6MzMiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAicGF0aCIgOiAidGV4dCIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJQcm9wZXJ0eSIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICJzb3VyY2UiIDogewogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMTE1OjIyLTExNToyOCIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJjb25jZXB0IiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk9wZXJhbmRSZWYiCiAgICAgICAgICAgICAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgICAgICAgICAgICAgfQogICAgICAgICAgICAgICAgICAgICB9CiAgICAgICAgICAgICAgICAgIH0gXQogICAgICAgICAgICAgICB9CiAgICAgICAgICAgIH0sCiAgICAgICAgICAgICJvcGVyYW5kIiA6IFsgewogICAgICAgICAgICAgICAibmFtZSIgOiAiY29uY2VwdCIsCiAgICAgICAgICAgICAgICJvcGVyYW5kVHlwZVNwZWNpZmllciIgOiB7CiAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIxMDk6MzUtMTA5OjU0IiwKICAgICAgICAgICAgICAgICAgIm5hbWUiIDogIntodHRwOi8vaGw3Lm9yZy9maGlyfUNvZGVhYmxlQ29uY2VwdCIsCiAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJOYW1lZFR5cGVTcGVjaWZpZXIiCiAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgfSBdCiAgICAgICAgIH0sIHsKICAgICAgICAgICAgImxvY2F0b3IiIDogIjExODoxLTEyMjo2MCIsCiAgICAgICAgICAgICJuYW1lIiA6ICJyZWZlcmVuY2UiLAogICAgICAgICAgICAiY29udGV4dCIgOiAiUGF0aWVudCIsCiAgICAgICAgICAgICJhY2Nlc3NMZXZlbCIgOiAiUHVibGljIiwKICAgICAgICAgICAgInR5cGUiIDogIkZ1bmN0aW9uRGVmIiwKICAgICAgICAgICAgImV4cHJlc3Npb24iIDogewogICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMTE5OjUtMTIyOjYwIiwKICAgICAgICAgICAgICAgInR5cGUiIDogIklmIiwKICAgICAgICAgICAgICAgImNvbmRpdGlvbiIgOiB7CiAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIxMTk6OC0xMTk6MjQiLAogICAgICAgICAgICAgICAgICAidHlwZSIgOiAiSXNOdWxsIiwKICAgICAgICAgICAgICAgICAgIm9wZXJhbmQiIDogewogICAgICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMTE5OjgtMTE5OjE2IiwKICAgICAgICAgICAgICAgICAgICAgIm5hbWUiIDogInJlZmVyZW5jZSIsCiAgICAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJPcGVyYW5kUmVmIgogICAgICAgICAgICAgICAgICB9CiAgICAgICAgICAgICAgIH0sCiAgICAgICAgICAgICAgICJ0aGVuIiA6IHsKICAgICAgICAgICAgICAgICAgImFzVHlwZSIgOiAie2h0dHA6Ly9obDcub3JnL2ZoaXJ9UmVmZXJlbmNlIiwKICAgICAgICAgICAgICAgICAgInR5cGUiIDogIkFzIiwKICAgICAgICAgICAgICAgICAgIm9wZXJhbmQiIDogewogICAgICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMTIwOjktMTIwOjEyIiwKICAgICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk51bGwiCiAgICAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgICAgfSwKICAgICAgICAgICAgICAgImVsc2UiIDogewogICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMTIyOjktMTIyOjYwIiwKICAgICAgICAgICAgICAgICAgImNsYXNzVHlwZSIgOiAie2h0dHA6Ly9obDcub3JnL2ZoaXJ9UmVmZXJlbmNlIiwKICAgICAgICAgICAgICAgICAgInR5cGUiIDogIkluc3RhbmNlIiwKICAgICAgICAgICAgICAgICAgImVsZW1lbnQiIDogWyB7CiAgICAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJyZWZlcmVuY2UiLAogICAgICAgICAgICAgICAgICAgICAidmFsdWUiIDogewogICAgICAgICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMTIyOjMyLTEyMjo1OCIsCiAgICAgICAgICAgICAgICAgICAgICAgICJjbGFzc1R5cGUiIDogIntodHRwOi8vaGw3Lm9yZy9maGlyfXN0cmluZyIsCiAgICAgICAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJJbnN0YW5jZSIsCiAgICAgICAgICAgICAgICAgICAgICAgICJlbGVtZW50IiA6IFsgewogICAgICAgICAgICAgICAgICAgICAgICAgICAibmFtZSIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAidmFsdWUiIDogewogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMTIyOjQ4LTEyMjo1NiIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJyZWZlcmVuY2UiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAidHlwZSIgOiAiT3BlcmFuZFJlZiIKICAgICAgICAgICAgICAgICAgICAgICAgICAgfQogICAgICAgICAgICAgICAgICAgICAgICB9IF0KICAgICAgICAgICAgICAgICAgICAgfQogICAgICAgICAgICAgICAgICB9IF0KICAgICAgICAgICAgICAgfQogICAgICAgICAgICB9LAogICAgICAgICAgICAib3BlcmFuZCIgOiBbIHsKICAgICAgICAgICAgICAgIm5hbWUiIDogInJlZmVyZW5jZSIsCiAgICAgICAgICAgICAgICJvcGVyYW5kVHlwZVNwZWNpZmllciIgOiB7CiAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIxMTg6MzctMTE4OjQyIiwKICAgICAgICAgICAgICAgICAgIm5hbWUiIDogInt1cm46aGw3LW9yZzplbG0tdHlwZXM6cjF9U3RyaW5nIiwKICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk5hbWVkVHlwZVNwZWNpZmllciIKICAgICAgICAgICAgICAgfQogICAgICAgICAgICB9IF0KICAgICAgICAgfSwgewogICAgICAgICAgICAibG9jYXRvciIgOiAiMTI0OjEtMTI0OjY4IiwKICAgICAgICAgICAgIm5hbWUiIDogInJlc29sdmUiLAogICAgICAgICAgICAiY29udGV4dCIgOiAiUGF0aWVudCIsCiAgICAgICAgICAgICJhY2Nlc3NMZXZlbCIgOiAiUHVibGljIiwKICAgICAgICAgICAgImV4dGVybmFsIiA6IHRydWUsCiAgICAgICAgICAgICJ0eXBlIiA6ICJGdW5jdGlvbkRlZiIsCiAgICAgICAgICAgICJvcGVyYW5kIiA6IFsgewogICAgICAgICAgICAgICAibmFtZSIgOiAicmVmZXJlbmNlIiwKICAgICAgICAgICAgICAgIm9wZXJhbmRUeXBlU3BlY2lmaWVyIiA6IHsKICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjEyNDozNS0xMjQ6NDAiLAogICAgICAgICAgICAgICAgICAibmFtZSIgOiAie3VybjpobDctb3JnOmVsbS10eXBlczpyMX1TdHJpbmciLAogICAgICAgICAgICAgICAgICAidHlwZSIgOiAiTmFtZWRUeXBlU3BlY2lmaWVyIgogICAgICAgICAgICAgICB9CiAgICAgICAgICAgIH0gXQogICAgICAgICB9LCB7CiAgICAgICAgICAgICJsb2NhdG9yIiA6ICIxMjU6MS0xMjU6NzEiLAogICAgICAgICAgICAibmFtZSIgOiAicmVzb2x2ZSIsCiAgICAgICAgICAgICJjb250ZXh0IiA6ICJQYXRpZW50IiwKICAgICAgICAgICAgImFjY2Vzc0xldmVsIiA6ICJQdWJsaWMiLAogICAgICAgICAgICAiZXh0ZXJuYWwiIDogdHJ1ZSwKICAgICAgICAgICAgInR5cGUiIDogIkZ1bmN0aW9uRGVmIiwKICAgICAgICAgICAgIm9wZXJhbmQiIDogWyB7CiAgICAgICAgICAgICAgICJuYW1lIiA6ICJyZWZlcmVuY2UiLAogICAgICAgICAgICAgICAib3BlcmFuZFR5cGVTcGVjaWZpZXIiIDogewogICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMTI1OjM1LTEyNTo0MyIsCiAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJ7aHR0cDovL2hsNy5vcmcvZmhpcn1SZWZlcmVuY2UiLAogICAgICAgICAgICAgICAgICAidHlwZSIgOiAiTmFtZWRUeXBlU3BlY2lmaWVyIgogICAgICAgICAgICAgICB9CiAgICAgICAgICAgIH0gXQogICAgICAgICB9LCB7CiAgICAgICAgICAgICJsb2NhdG9yIiA6ICIxMjY6MS0xMjY6NzIiLAogICAgICAgICAgICAibmFtZSIgOiAicmVmZXJlbmNlIiwKICAgICAgICAgICAgImNvbnRleHQiIDogIlBhdGllbnQiLAogICAgICAgICAgICAiYWNjZXNzTGV2ZWwiIDogIlB1YmxpYyIsCiAgICAgICAgICAgICJleHRlcm5hbCIgOiB0cnVlLAogICAgICAgICAgICAidHlwZSIgOiAiRnVuY3Rpb25EZWYiLAogICAgICAgICAgICAib3BlcmFuZCIgOiBbIHsKICAgICAgICAgICAgICAgIm5hbWUiIDogInJlc291cmNlIiwKICAgICAgICAgICAgICAgIm9wZXJhbmRUeXBlU3BlY2lmaWVyIiA6IHsKICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjEyNjozNi0xMjY6NDMiLAogICAgICAgICAgICAgICAgICAibmFtZSIgOiAie2h0dHA6Ly9obDcub3JnL2ZoaXJ9UmVzb3VyY2UiLAogICAgICAgICAgICAgICAgICAidHlwZSIgOiAiTmFtZWRUeXBlU3BlY2lmaWVyIgogICAgICAgICAgICAgICB9CiAgICAgICAgICAgIH0gXQogICAgICAgICB9LCB7CiAgICAgICAgICAgICJsb2NhdG9yIiA6ICIxMjc6MS0xMjc6ODYiLAogICAgICAgICAgICAibmFtZSIgOiAiZXh0ZW5zaW9uIiwKICAgICAgICAgICAgImNvbnRleHQiIDogIlBhdGllbnQiLAogICAgICAgICAgICAiYWNjZXNzTGV2ZWwiIDogIlB1YmxpYyIsCiAgICAgICAgICAgICJleHRlcm5hbCIgOiB0cnVlLAogICAgICAgICAgICAidHlwZSIgOiAiRnVuY3Rpb25EZWYiLAogICAgICAgICAgICAib3BlcmFuZCIgOiBbIHsKICAgICAgICAgICAgICAgIm5hbWUiIDogImVsZW1lbnQiLAogICAgICAgICAgICAgICAib3BlcmFuZFR5cGVTcGVjaWZpZXIiIDogewogICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMTI3OjM1LTEyNzo0MSIsCiAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJ7aHR0cDovL2hsNy5vcmcvZmhpcn1FbGVtZW50IiwKICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk5hbWVkVHlwZVNwZWNpZmllciIKICAgICAgICAgICAgICAgfQogICAgICAgICAgICB9LCB7CiAgICAgICAgICAgICAgICJuYW1lIiA6ICJ1cmwiLAogICAgICAgICAgICAgICAib3BlcmFuZFR5cGVTcGVjaWZpZXIiIDogewogICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMTI3OjQ4LTEyNzo1MyIsCiAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJ7dXJuOmhsNy1vcmc6ZWxtLXR5cGVzOnIxfVN0cmluZyIsCiAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJOYW1lZFR5cGVTcGVjaWZpZXIiCiAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgfSBdCiAgICAgICAgIH0sIHsKICAgICAgICAgICAgImxvY2F0b3IiIDogIjEyODoxLTEyODo4OCIsCiAgICAgICAgICAgICJuYW1lIiA6ICJleHRlbnNpb24iLAogICAgICAgICAgICAiY29udGV4dCIgOiAiUGF0aWVudCIsCiAgICAgICAgICAgICJhY2Nlc3NMZXZlbCIgOiAiUHVibGljIiwKICAgICAgICAgICAgImV4dGVybmFsIiA6IHRydWUsCiAgICAgICAgICAgICJ0eXBlIiA6ICJGdW5jdGlvbkRlZiIsCiAgICAgICAgICAgICJvcGVyYW5kIiA6IFsgewogICAgICAgICAgICAgICAibmFtZSIgOiAicmVzb3VyY2UiLAogICAgICAgICAgICAgICAib3BlcmFuZFR5cGVTcGVjaWZpZXIiIDogewogICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMTI4OjM2LTEyODo0MyIsCiAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJ7aHR0cDovL2hsNy5vcmcvZmhpcn1SZXNvdXJjZSIsCiAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJOYW1lZFR5cGVTcGVjaWZpZXIiCiAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgfSwgewogICAgICAgICAgICAgICAibmFtZSIgOiAidXJsIiwKICAgICAgICAgICAgICAgIm9wZXJhbmRUeXBlU3BlY2lmaWVyIiA6IHsKICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjEyODo1MC0xMjg6NTUiLAogICAgICAgICAgICAgICAgICAibmFtZSIgOiAie3VybjpobDctb3JnOmVsbS10eXBlczpyMX1TdHJpbmciLAogICAgICAgICAgICAgICAgICAidHlwZSIgOiAiTmFtZWRUeXBlU3BlY2lmaWVyIgogICAgICAgICAgICAgICB9CiAgICAgICAgICAgIH0gXQogICAgICAgICB9LCB7CiAgICAgICAgICAgICJsb2NhdG9yIiA6ICIxMjk6MS0xMjk6NjciLAogICAgICAgICAgICAibmFtZSIgOiAiaGFzVmFsdWUiLAogICAgICAgICAgICAiY29udGV4dCIgOiAiUGF0aWVudCIsCiAgICAgICAgICAgICJhY2Nlc3NMZXZlbCIgOiAiUHVibGljIiwKICAgICAgICAgICAgImV4dGVybmFsIiA6IHRydWUsCiAgICAgICAgICAgICJ0eXBlIiA6ICJGdW5jdGlvbkRlZiIsCiAgICAgICAgICAgICJvcGVyYW5kIiA6IFsgewogICAgICAgICAgICAgICAibmFtZSIgOiAiZWxlbWVudCIsCiAgICAgICAgICAgICAgICJvcGVyYW5kVHlwZVNwZWNpZmllciIgOiB7CiAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIxMjk6MzQtMTI5OjQwIiwKICAgICAgICAgICAgICAgICAgIm5hbWUiIDogIntodHRwOi8vaGw3Lm9yZy9maGlyfUVsZW1lbnQiLAogICAgICAgICAgICAgICAgICAidHlwZSIgOiAiTmFtZWRUeXBlU3BlY2lmaWVyIgogICAgICAgICAgICAgICB9CiAgICAgICAgICAgIH0gXQogICAgICAgICB9LCB7CiAgICAgICAgICAgICJsb2NhdG9yIiA6ICIxMzA6MS0xMzA6NjMiLAogICAgICAgICAgICAibmFtZSIgOiAiZ2V0VmFsdWUiLAogICAgICAgICAgICAiY29udGV4dCIgOiAiUGF0aWVudCIsCiAgICAgICAgICAgICJhY2Nlc3NMZXZlbCIgOiAiUHVibGljIiwKICAgICAgICAgICAgImV4dGVybmFsIiA6IHRydWUsCiAgICAgICAgICAgICJ0eXBlIiA6ICJGdW5jdGlvbkRlZiIsCiAgICAgICAgICAgICJvcGVyYW5kIiA6IFsgewogICAgICAgICAgICAgICAibmFtZSIgOiAiZWxlbWVudCIsCiAgICAgICAgICAgICAgICJvcGVyYW5kVHlwZVNwZWNpZmllciIgOiB7CiAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIxMzA6MzQtMTMwOjQwIiwKICAgICAgICAgICAgICAgICAgIm5hbWUiIDogIntodHRwOi8vaGw3Lm9yZy9maGlyfUVsZW1lbnQiLAogICAgICAgICAgICAgICAgICAidHlwZSIgOiAiTmFtZWRUeXBlU3BlY2lmaWVyIgogICAgICAgICAgICAgICB9CiAgICAgICAgICAgIH0gXQogICAgICAgICB9LCB7CiAgICAgICAgICAgICJsb2NhdG9yIiA6ICIxMzE6MS0xMzE6NjkiLAogICAgICAgICAgICAibmFtZSIgOiAib2ZUeXBlIiwKICAgICAgICAgICAgImNvbnRleHQiIDogIlBhdGllbnQiLAogICAgICAgICAgICAiYWNjZXNzTGV2ZWwiIDogIlB1YmxpYyIsCiAgICAgICAgICAgICJleHRlcm5hbCIgOiB0cnVlLAogICAgICAgICAgICAidHlwZSIgOiAiRnVuY3Rpb25EZWYiLAogICAgICAgICAgICAib3BlcmFuZCIgOiBbIHsKICAgICAgICAgICAgICAgIm5hbWUiIDogImlkZW50aWZpZXIiLAogICAgICAgICAgICAgICAib3BlcmFuZFR5cGVTcGVjaWZpZXIiIDogewogICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMTMxOjM1LTEzMTo0MCIsCiAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJ7dXJuOmhsNy1vcmc6ZWxtLXR5cGVzOnIxfVN0cmluZyIsCiAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJOYW1lZFR5cGVTcGVjaWZpZXIiCiAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgfSBdCiAgICAgICAgIH0sIHsKICAgICAgICAgICAgImxvY2F0b3IiIDogIjEzMjoxLTEzMjo2MyIsCiAgICAgICAgICAgICJuYW1lIiA6ICJpcyIsCiAgICAgICAgICAgICJjb250ZXh0IiA6ICJQYXRpZW50IiwKICAgICAgICAgICAgImFjY2Vzc0xldmVsIiA6ICJQdWJsaWMiLAogICAgICAgICAgICAiZXh0ZXJuYWwiIDogdHJ1ZSwKICAgICAgICAgICAgInR5cGUiIDogIkZ1bmN0aW9uRGVmIiwKICAgICAgICAgICAgIm9wZXJhbmQiIDogWyB7CiAgICAgICAgICAgICAgICJuYW1lIiA6ICJpZGVudGlmaWVyIiwKICAgICAgICAgICAgICAgIm9wZXJhbmRUeXBlU3BlY2lmaWVyIiA6IHsKICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjEzMjozMS0xMzI6MzYiLAogICAgICAgICAgICAgICAgICAibmFtZSIgOiAie3VybjpobDctb3JnOmVsbS10eXBlczpyMX1TdHJpbmciLAogICAgICAgICAgICAgICAgICAidHlwZSIgOiAiTmFtZWRUeXBlU3BlY2lmaWVyIgogICAgICAgICAgICAgICB9CiAgICAgICAgICAgIH0gXQogICAgICAgICB9LCB7CiAgICAgICAgICAgICJsb2NhdG9yIiA6ICIxMzM6MS0xMzM6NTkiLAogICAgICAgICAgICAibmFtZSIgOiAiYXMiLAogICAgICAgICAgICAiY29udGV4dCIgOiAiUGF0aWVudCIsCiAgICAgICAgICAgICJhY2Nlc3NMZXZlbCIgOiAiUHVibGljIiwKICAgICAgICAgICAgImV4dGVybmFsIiA6IHRydWUsCiAgICAgICAgICAgICJ0eXBlIiA6ICJGdW5jdGlvbkRlZiIsCiAgICAgICAgICAgICJvcGVyYW5kIiA6IFsgewogICAgICAgICAgICAgICAibmFtZSIgOiAiaWRlbnRpZmllciIsCiAgICAgICAgICAgICAgICJvcGVyYW5kVHlwZVNwZWNpZmllciIgOiB7CiAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIxMzM6MzEtMTMzOjM2IiwKICAgICAgICAgICAgICAgICAgIm5hbWUiIDogInt1cm46aGw3LW9yZzplbG0tdHlwZXM6cjF9U3RyaW5nIiwKICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk5hbWVkVHlwZVNwZWNpZmllciIKICAgICAgICAgICAgICAgfQogICAgICAgICAgICB9IF0KICAgICAgICAgfSwgewogICAgICAgICAgICAibG9jYXRvciIgOiAiMTM0OjEtMTM0Ojg2IiwKICAgICAgICAgICAgIm5hbWUiIDogImVsZW1lbnREZWZpbml0aW9uIiwKICAgICAgICAgICAgImNvbnRleHQiIDogIlBhdGllbnQiLAogICAgICAgICAgICAiYWNjZXNzTGV2ZWwiIDogIlB1YmxpYyIsCiAgICAgICAgICAgICJleHRlcm5hbCIgOiB0cnVlLAogICAgICAgICAgICAidHlwZSIgOiAiRnVuY3Rpb25EZWYiLAogICAgICAgICAgICAib3BlcmFuZCIgOiBbIHsKICAgICAgICAgICAgICAgIm5hbWUiIDogImVsZW1lbnQiLAogICAgICAgICAgICAgICAib3BlcmFuZFR5cGVTcGVjaWZpZXIiIDogewogICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMTM0OjQzLTEzNDo0OSIsCiAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJ7aHR0cDovL2hsNy5vcmcvZmhpcn1FbGVtZW50IiwKICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk5hbWVkVHlwZVNwZWNpZmllciIKICAgICAgICAgICAgICAgfQogICAgICAgICAgICB9IF0KICAgICAgICAgfSwgewogICAgICAgICAgICAibG9jYXRvciIgOiAiMTM1OjEtMTM1Ojk1IiwKICAgICAgICAgICAgIm5hbWUiIDogInNsaWNlIiwKICAgICAgICAgICAgImNvbnRleHQiIDogIlBhdGllbnQiLAogICAgICAgICAgICAiYWNjZXNzTGV2ZWwiIDogIlB1YmxpYyIsCiAgICAgICAgICAgICJleHRlcm5hbCIgOiB0cnVlLAogICAgICAgICAgICAidHlwZSIgOiAiRnVuY3Rpb25EZWYiLAogICAgICAgICAgICAib3BlcmFuZCIgOiBbIHsKICAgICAgICAgICAgICAgIm5hbWUiIDogImVsZW1lbnQiLAogICAgICAgICAgICAgICAib3BlcmFuZFR5cGVTcGVjaWZpZXIiIDogewogICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMTM1OjMxLTEzNTozNyIsCiAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJ7aHR0cDovL2hsNy5vcmcvZmhpcn1FbGVtZW50IiwKICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk5hbWVkVHlwZVNwZWNpZmllciIKICAgICAgICAgICAgICAgfQogICAgICAgICAgICB9LCB7CiAgICAgICAgICAgICAgICJuYW1lIiA6ICJ1cmwiLAogICAgICAgICAgICAgICAib3BlcmFuZFR5cGVTcGVjaWZpZXIiIDogewogICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMTM1OjQ0LTEzNTo0OSIsCiAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJ7dXJuOmhsNy1vcmc6ZWxtLXR5cGVzOnIxfVN0cmluZyIsCiAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJOYW1lZFR5cGVTcGVjaWZpZXIiCiAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgfSwgewogICAgICAgICAgICAgICAibmFtZSIgOiAibmFtZSIsCiAgICAgICAgICAgICAgICJvcGVyYW5kVHlwZVNwZWNpZmllciIgOiB7CiAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIxMzU6NTctMTM1OjYyIiwKICAgICAgICAgICAgICAgICAgIm5hbWUiIDogInt1cm46aGw3LW9yZzplbG0tdHlwZXM6cjF9U3RyaW5nIiwKICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk5hbWVkVHlwZVNwZWNpZmllciIKICAgICAgICAgICAgICAgfQogICAgICAgICAgICB9IF0KICAgICAgICAgfSwgewogICAgICAgICAgICAibG9jYXRvciIgOiAiMTM2OjEtMTM2Ojc2IiwKICAgICAgICAgICAgIm5hbWUiIDogImNoZWNrTW9kaWZpZXJzIiwKICAgICAgICAgICAgImNvbnRleHQiIDogIlBhdGllbnQiLAogICAgICAgICAgICAiYWNjZXNzTGV2ZWwiIDogIlB1YmxpYyIsCiAgICAgICAgICAgICJleHRlcm5hbCIgOiB0cnVlLAogICAgICAgICAgICAidHlwZSIgOiAiRnVuY3Rpb25EZWYiLAogICAgICAgICAgICAib3BlcmFuZCIgOiBbIHsKICAgICAgICAgICAgICAgIm5hbWUiIDogInJlc291cmNlIiwKICAgICAgICAgICAgICAgIm9wZXJhbmRUeXBlU3BlY2lmaWVyIiA6IHsKICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjEzNjo0MS0xMzY6NDgiLAogICAgICAgICAgICAgICAgICAibmFtZSIgOiAie2h0dHA6Ly9obDcub3JnL2ZoaXJ9UmVzb3VyY2UiLAogICAgICAgICAgICAgICAgICAidHlwZSIgOiAiTmFtZWRUeXBlU3BlY2lmaWVyIgogICAgICAgICAgICAgICB9CiAgICAgICAgICAgIH0gXQogICAgICAgICB9LCB7CiAgICAgICAgICAgICJsb2NhdG9yIiA6ICIxMzc6MS0xMzc6OTMiLAogICAgICAgICAgICAibmFtZSIgOiAiY2hlY2tNb2RpZmllcnMiLAogICAgICAgICAgICAiY29udGV4dCIgOiAiUGF0aWVudCIsCiAgICAgICAgICAgICJhY2Nlc3NMZXZlbCIgOiAiUHVibGljIiwKICAgICAgICAgICAgImV4dGVybmFsIiA6IHRydWUsCiAgICAgICAgICAgICJ0eXBlIiA6ICJGdW5jdGlvbkRlZiIsCiAgICAgICAgICAgICJvcGVyYW5kIiA6IFsgewogICAgICAgICAgICAgICAibmFtZSIgOiAicmVzb3VyY2UiLAogICAgICAgICAgICAgICAib3BlcmFuZFR5cGVTcGVjaWZpZXIiIDogewogICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMTM3OjQxLTEzNzo0OCIsCiAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJ7aHR0cDovL2hsNy5vcmcvZmhpcn1SZXNvdXJjZSIsCiAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJOYW1lZFR5cGVTcGVjaWZpZXIiCiAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgfSwgewogICAgICAgICAgICAgICAibmFtZSIgOiAibW9kaWZpZXIiLAogICAgICAgICAgICAgICAib3BlcmFuZFR5cGVTcGVjaWZpZXIiIDogewogICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMTM3OjYwLTEzNzo2NSIsCiAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJ7dXJuOmhsNy1vcmc6ZWxtLXR5cGVzOnIxfVN0cmluZyIsCiAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJOYW1lZFR5cGVTcGVjaWZpZXIiCiAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgfSBdCiAgICAgICAgIH0sIHsKICAgICAgICAgICAgImxvY2F0b3IiIDogIjEzODoxLTEzODo3MyIsCiAgICAgICAgICAgICJuYW1lIiA6ICJjaGVja01vZGlmaWVycyIsCiAgICAgICAgICAgICJjb250ZXh0IiA6ICJQYXRpZW50IiwKICAgICAgICAgICAgImFjY2Vzc0xldmVsIiA6ICJQdWJsaWMiLAogICAgICAgICAgICAiZXh0ZXJuYWwiIDogdHJ1ZSwKICAgICAgICAgICAgInR5cGUiIDogIkZ1bmN0aW9uRGVmIiwKICAgICAgICAgICAgIm9wZXJhbmQiIDogWyB7CiAgICAgICAgICAgICAgICJuYW1lIiA6ICJlbGVtZW50IiwKICAgICAgICAgICAgICAgIm9wZXJhbmRUeXBlU3BlY2lmaWVyIiA6IHsKICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjEzODo0MC0xMzg6NDYiLAogICAgICAgICAgICAgICAgICAibmFtZSIgOiAie2h0dHA6Ly9obDcub3JnL2ZoaXJ9RWxlbWVudCIsCiAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJOYW1lZFR5cGVTcGVjaWZpZXIiCiAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgfSBdCiAgICAgICAgIH0sIHsKICAgICAgICAgICAgImxvY2F0b3IiIDogIjEzOToxLTEzOTo5MCIsCiAgICAgICAgICAgICJuYW1lIiA6ICJjaGVja01vZGlmaWVycyIsCiAgICAgICAgICAgICJjb250ZXh0IiA6ICJQYXRpZW50IiwKICAgICAgICAgICAgImFjY2Vzc0xldmVsIiA6ICJQdWJsaWMiLAogICAgICAgICAgICAiZXh0ZXJuYWwiIDogdHJ1ZSwKICAgICAgICAgICAgInR5cGUiIDogIkZ1bmN0aW9uRGVmIiwKICAgICAgICAgICAgIm9wZXJhbmQiIDogWyB7CiAgICAgICAgICAgICAgICJuYW1lIiA6ICJlbGVtZW50IiwKICAgICAgICAgICAgICAgIm9wZXJhbmRUeXBlU3BlY2lmaWVyIiA6IHsKICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjEzOTo0MC0xMzk6NDYiLAogICAgICAgICAgICAgICAgICAibmFtZSIgOiAie2h0dHA6Ly9obDcub3JnL2ZoaXJ9RWxlbWVudCIsCiAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJOYW1lZFR5cGVTcGVjaWZpZXIiCiAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgfSwgewogICAgICAgICAgICAgICAibmFtZSIgOiAibW9kaWZpZXIiLAogICAgICAgICAgICAgICAib3BlcmFuZFR5cGVTcGVjaWZpZXIiIDogewogICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMTM5OjU4LTEzOTo2MyIsCiAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJ7dXJuOmhsNy1vcmc6ZWxtLXR5cGVzOnIxfVN0cmluZyIsCiAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJOYW1lZFR5cGVTcGVjaWZpZXIiCiAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgfSBdCiAgICAgICAgIH0sIHsKICAgICAgICAgICAgImxvY2F0b3IiIDogIjE0MDoxLTE0MDo4OSIsCiAgICAgICAgICAgICJuYW1lIiA6ICJjb25mb3Jtc1RvIiwKICAgICAgICAgICAgImNvbnRleHQiIDogIlBhdGllbnQiLAogICAgICAgICAgICAiYWNjZXNzTGV2ZWwiIDogIlB1YmxpYyIsCiAgICAgICAgICAgICJleHRlcm5hbCIgOiB0cnVlLAogICAgICAgICAgICAidHlwZSIgOiAiRnVuY3Rpb25EZWYiLAogICAgICAgICAgICAib3BlcmFuZCIgOiBbIHsKICAgICAgICAgICAgICAgIm5hbWUiIDogInJlc291cmNlIiwKICAgICAgICAgICAgICAgIm9wZXJhbmRUeXBlU3BlY2lmaWVyIiA6IHsKICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjE0MDozNy0xNDA6NDQiLAogICAgICAgICAgICAgICAgICAibmFtZSIgOiAie2h0dHA6Ly9obDcub3JnL2ZoaXJ9UmVzb3VyY2UiLAogICAgICAgICAgICAgICAgICAidHlwZSIgOiAiTmFtZWRUeXBlU3BlY2lmaWVyIgogICAgICAgICAgICAgICB9CiAgICAgICAgICAgIH0sIHsKICAgICAgICAgICAgICAgIm5hbWUiIDogInN0cnVjdHVyZSIsCiAgICAgICAgICAgICAgICJvcGVyYW5kVHlwZVNwZWNpZmllciIgOiB7CiAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIxNDA6NTctMTQwOjYyIiwKICAgICAgICAgICAgICAgICAgIm5hbWUiIDogInt1cm46aGw3LW9yZzplbG0tdHlwZXM6cjF9U3RyaW5nIiwKICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk5hbWVkVHlwZVNwZWNpZmllciIKICAgICAgICAgICAgICAgfQogICAgICAgICAgICB9IF0KICAgICAgICAgfSwgewogICAgICAgICAgICAibG9jYXRvciIgOiAiMTQxOjEtMTQxOjc4IiwKICAgICAgICAgICAgIm5hbWUiIDogIm1lbWJlck9mIiwKICAgICAgICAgICAgImNvbnRleHQiIDogIlBhdGllbnQiLAogICAgICAgICAgICAiYWNjZXNzTGV2ZWwiIDogIlB1YmxpYyIsCiAgICAgICAgICAgICJleHRlcm5hbCIgOiB0cnVlLAogICAgICAgICAgICAidHlwZSIgOiAiRnVuY3Rpb25EZWYiLAogICAgICAgICAgICAib3BlcmFuZCIgOiBbIHsKICAgICAgICAgICAgICAgIm5hbWUiIDogImNvZGUiLAogICAgICAgICAgICAgICAib3BlcmFuZFR5cGVTcGVjaWZpZXIiIDogewogICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMTQxOjMxLTE0MTozNCIsCiAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJ7aHR0cDovL2hsNy5vcmcvZmhpcn1jb2RlIiwKICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk5hbWVkVHlwZVNwZWNpZmllciIKICAgICAgICAgICAgICAgfQogICAgICAgICAgICB9LCB7CiAgICAgICAgICAgICAgICJuYW1lIiA6ICJ2YWx1ZVNldCIsCiAgICAgICAgICAgICAgICJvcGVyYW5kVHlwZVNwZWNpZmllciIgOiB7CiAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIxNDE6NDYtMTQxOjUxIiwKICAgICAgICAgICAgICAgICAgIm5hbWUiIDogInt1cm46aGw3LW9yZzplbG0tdHlwZXM6cjF9U3RyaW5nIiwKICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk5hbWVkVHlwZVNwZWNpZmllciIKICAgICAgICAgICAgICAgfQogICAgICAgICAgICB9IF0KICAgICAgICAgfSwgewogICAgICAgICAgICAibG9jYXRvciIgOiAiMTQyOjEtMTQyOjgyIiwKICAgICAgICAgICAgIm5hbWUiIDogIm1lbWJlck9mIiwKICAgICAgICAgICAgImNvbnRleHQiIDogIlBhdGllbnQiLAogICAgICAgICAgICAiYWNjZXNzTGV2ZWwiIDogIlB1YmxpYyIsCiAgICAgICAgICAgICJleHRlcm5hbCIgOiB0cnVlLAogICAgICAgICAgICAidHlwZSIgOiAiRnVuY3Rpb25EZWYiLAogICAgICAgICAgICAib3BlcmFuZCIgOiBbIHsKICAgICAgICAgICAgICAgIm5hbWUiIDogImNvZGluZyIsCiAgICAgICAgICAgICAgICJvcGVyYW5kVHlwZVNwZWNpZmllciIgOiB7CiAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIxNDI6MzMtMTQyOjM4IiwKICAgICAgICAgICAgICAgICAgIm5hbWUiIDogIntodHRwOi8vaGw3Lm9yZy9maGlyfUNvZGluZyIsCiAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJOYW1lZFR5cGVTcGVjaWZpZXIiCiAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgfSwgewogICAgICAgICAgICAgICAibmFtZSIgOiAidmFsdWVTZXQiLAogICAgICAgICAgICAgICAib3BlcmFuZFR5cGVTcGVjaWZpZXIiIDogewogICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMTQyOjUwLTE0Mjo1NSIsCiAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJ7dXJuOmhsNy1vcmc6ZWxtLXR5cGVzOnIxfVN0cmluZyIsCiAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJOYW1lZFR5cGVTcGVjaWZpZXIiCiAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgfSBdCiAgICAgICAgIH0sIHsKICAgICAgICAgICAgImxvY2F0b3IiIDogIjE0MzoxLTE0Mzo5MiIsCiAgICAgICAgICAgICJuYW1lIiA6ICJtZW1iZXJPZiIsCiAgICAgICAgICAgICJjb250ZXh0IiA6ICJQYXRpZW50IiwKICAgICAgICAgICAgImFjY2Vzc0xldmVsIiA6ICJQdWJsaWMiLAogICAgICAgICAgICAiZXh0ZXJuYWwiIDogdHJ1ZSwKICAgICAgICAgICAgInR5cGUiIDogIkZ1bmN0aW9uRGVmIiwKICAgICAgICAgICAgIm9wZXJhbmQiIDogWyB7CiAgICAgICAgICAgICAgICJuYW1lIiA6ICJjb25jZXB0IiwKICAgICAgICAgICAgICAgIm9wZXJhbmRUeXBlU3BlY2lmaWVyIiA6IHsKICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjE0MzozNC0xNDM6NDgiLAogICAgICAgICAgICAgICAgICAibmFtZSIgOiAie2h0dHA6Ly9obDcub3JnL2ZoaXJ9Q29kZWFibGVDb25jZXB0IiwKICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk5hbWVkVHlwZVNwZWNpZmllciIKICAgICAgICAgICAgICAgfQogICAgICAgICAgICB9LCB7CiAgICAgICAgICAgICAgICJuYW1lIiA6ICJ2YWx1ZVNldCIsCiAgICAgICAgICAgICAgICJvcGVyYW5kVHlwZVNwZWNpZmllciIgOiB7CiAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIxNDM6NjAtMTQzOjY1IiwKICAgICAgICAgICAgICAgICAgIm5hbWUiIDogInt1cm46aGw3LW9yZzplbG0tdHlwZXM6cjF9U3RyaW5nIiwKICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk5hbWVkVHlwZVNwZWNpZmllciIKICAgICAgICAgICAgICAgfQogICAgICAgICAgICB9IF0KICAgICAgICAgfSwgewogICAgICAgICAgICAibG9jYXRvciIgOiAiMTQ0OjEtMTQ0Ojg4IiwKICAgICAgICAgICAgIm5hbWUiIDogInN1YnN1bWVzIiwKICAgICAgICAgICAgImNvbnRleHQiIDogIlBhdGllbnQiLAogICAgICAgICAgICAiYWNjZXNzTGV2ZWwiIDogIlB1YmxpYyIsCiAgICAgICAgICAgICJleHRlcm5hbCIgOiB0cnVlLAogICAgICAgICAgICAidHlwZSIgOiAiRnVuY3Rpb25EZWYiLAogICAgICAgICAgICAib3BlcmFuZCIgOiBbIHsKICAgICAgICAgICAgICAgIm5hbWUiIDogImNvZGluZyIsCiAgICAgICAgICAgICAgICJvcGVyYW5kVHlwZVNwZWNpZmllciIgOiB7CiAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIxNDQ6MzMtMTQ0OjM4IiwKICAgICAgICAgICAgICAgICAgIm5hbWUiIDogIntodHRwOi8vaGw3Lm9yZy9maGlyfUNvZGluZyIsCiAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJOYW1lZFR5cGVTcGVjaWZpZXIiCiAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgfSwgewogICAgICAgICAgICAgICAibmFtZSIgOiAic3Vic3VtZWRDb2RpbmciLAogICAgICAgICAgICAgICAib3BlcmFuZFR5cGVTcGVjaWZpZXIiIDogewogICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMTQ0OjU2LTE0NDo2MSIsCiAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJ7aHR0cDovL2hsNy5vcmcvZmhpcn1Db2RpbmciLAogICAgICAgICAgICAgICAgICAidHlwZSIgOiAiTmFtZWRUeXBlU3BlY2lmaWVyIgogICAgICAgICAgICAgICB9CiAgICAgICAgICAgIH0gXQogICAgICAgICB9LCB7CiAgICAgICAgICAgICJsb2NhdG9yIiA6ICIxNDU6MS0xNDU6MTA4IiwKICAgICAgICAgICAgIm5hbWUiIDogInN1YnN1bWVzIiwKICAgICAgICAgICAgImNvbnRleHQiIDogIlBhdGllbnQiLAogICAgICAgICAgICAiYWNjZXNzTGV2ZWwiIDogIlB1YmxpYyIsCiAgICAgICAgICAgICJleHRlcm5hbCIgOiB0cnVlLAogICAgICAgICAgICAidHlwZSIgOiAiRnVuY3Rpb25EZWYiLAogICAgICAgICAgICAib3BlcmFuZCIgOiBbIHsKICAgICAgICAgICAgICAgIm5hbWUiIDogImNvbmNlcHQiLAogICAgICAgICAgICAgICAib3BlcmFuZFR5cGVTcGVjaWZpZXIiIDogewogICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMTQ1OjM0LTE0NTo0OCIsCiAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJ7aHR0cDovL2hsNy5vcmcvZmhpcn1Db2RlYWJsZUNvbmNlcHQiLAogICAgICAgICAgICAgICAgICAidHlwZSIgOiAiTmFtZWRUeXBlU3BlY2lmaWVyIgogICAgICAgICAgICAgICB9CiAgICAgICAgICAgIH0sIHsKICAgICAgICAgICAgICAgIm5hbWUiIDogInN1YnN1bWVkQ29uY2VwdCIsCiAgICAgICAgICAgICAgICJvcGVyYW5kVHlwZVNwZWNpZmllciIgOiB7CiAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIxNDU6NjctMTQ1OjgxIiwKICAgICAgICAgICAgICAgICAgIm5hbWUiIDogIntodHRwOi8vaGw3Lm9yZy9maGlyfUNvZGVhYmxlQ29uY2VwdCIsCiAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJOYW1lZFR5cGVTcGVjaWZpZXIiCiAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgfSBdCiAgICAgICAgIH0sIHsKICAgICAgICAgICAgImxvY2F0b3IiIDogIjE0NjoxLTE0Njo5MSIsCiAgICAgICAgICAgICJuYW1lIiA6ICJzdWJzdW1lZEJ5IiwKICAgICAgICAgICAgImNvbnRleHQiIDogIlBhdGllbnQiLAogICAgICAgICAgICAiYWNjZXNzTGV2ZWwiIDogIlB1YmxpYyIsCiAgICAgICAgICAgICJleHRlcm5hbCIgOiB0cnVlLAogICAgICAgICAgICAidHlwZSIgOiAiRnVuY3Rpb25EZWYiLAogICAgICAgICAgICAib3BlcmFuZCIgOiBbIHsKICAgICAgICAgICAgICAgIm5hbWUiIDogImNvZGluZyIsCiAgICAgICAgICAgICAgICJvcGVyYW5kVHlwZVNwZWNpZmllciIgOiB7CiAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIxNDY6MzUtMTQ2OjQwIiwKICAgICAgICAgICAgICAgICAgIm5hbWUiIDogIntodHRwOi8vaGw3Lm9yZy9maGlyfUNvZGluZyIsCiAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJOYW1lZFR5cGVTcGVjaWZpZXIiCiAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgfSwgewogICAgICAgICAgICAgICAibmFtZSIgOiAic3Vic3VtaW5nQ29kaW5nIiwKICAgICAgICAgICAgICAgIm9wZXJhbmRUeXBlU3BlY2lmaWVyIiA6IHsKICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjE0Njo1OS0xNDY6NjQiLAogICAgICAgICAgICAgICAgICAibmFtZSIgOiAie2h0dHA6Ly9obDcub3JnL2ZoaXJ9Q29kaW5nIiwKICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk5hbWVkVHlwZVNwZWNpZmllciIKICAgICAgICAgICAgICAgfQogICAgICAgICAgICB9IF0KICAgICAgICAgfSwgewogICAgICAgICAgICAibG9jYXRvciIgOiAiMTQ3OjEtMTQ3OjExMSIsCiAgICAgICAgICAgICJuYW1lIiA6ICJzdWJzdW1lZEJ5IiwKICAgICAgICAgICAgImNvbnRleHQiIDogIlBhdGllbnQiLAogICAgICAgICAgICAiYWNjZXNzTGV2ZWwiIDogIlB1YmxpYyIsCiAgICAgICAgICAgICJleHRlcm5hbCIgOiB0cnVlLAogICAgICAgICAgICAidHlwZSIgOiAiRnVuY3Rpb25EZWYiLAogICAgICAgICAgICAib3BlcmFuZCIgOiBbIHsKICAgICAgICAgICAgICAgIm5hbWUiIDogImNvbmNlcHQiLAogICAgICAgICAgICAgICAib3BlcmFuZFR5cGVTcGVjaWZpZXIiIDogewogICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMTQ3OjM2LTE0Nzo1MCIsCiAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJ7aHR0cDovL2hsNy5vcmcvZmhpcn1Db2RlYWJsZUNvbmNlcHQiLAogICAgICAgICAgICAgICAgICAidHlwZSIgOiAiTmFtZWRUeXBlU3BlY2lmaWVyIgogICAgICAgICAgICAgICB9CiAgICAgICAgICAgIH0sIHsKICAgICAgICAgICAgICAgIm5hbWUiIDogInN1YnN1bWluZ0NvbmNlcHQiLAogICAgICAgICAgICAgICAib3BlcmFuZFR5cGVTcGVjaWZpZXIiIDogewogICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMTQ3OjcwLTE0Nzo4NCIsCiAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJ7aHR0cDovL2hsNy5vcmcvZmhpcn1Db2RlYWJsZUNvbmNlcHQiLAogICAgICAgICAgICAgICAgICAidHlwZSIgOiAiTmFtZWRUeXBlU3BlY2lmaWVyIgogICAgICAgICAgICAgICB9CiAgICAgICAgICAgIH0gXQogICAgICAgICB9LCB7CiAgICAgICAgICAgICJsb2NhdG9yIiA6ICIxNDg6MS0xNDg6NjkiLAogICAgICAgICAgICAibmFtZSIgOiAiaHRtbENoZWNrcyIsCiAgICAgICAgICAgICJjb250ZXh0IiA6ICJQYXRpZW50IiwKICAgICAgICAgICAgImFjY2Vzc0xldmVsIiA6ICJQdWJsaWMiLAogICAgICAgICAgICAiZXh0ZXJuYWwiIDogdHJ1ZSwKICAgICAgICAgICAgInR5cGUiIDogIkZ1bmN0aW9uRGVmIiwKICAgICAgICAgICAgIm9wZXJhbmQiIDogWyB7CiAgICAgICAgICAgICAgICJuYW1lIiA6ICJlbGVtZW50IiwKICAgICAgICAgICAgICAgIm9wZXJhbmRUeXBlU3BlY2lmaWVyIiA6IHsKICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjE0ODozNi0xNDg6NDIiLAogICAgICAgICAgICAgICAgICAibmFtZSIgOiAie2h0dHA6Ly9obDcub3JnL2ZoaXJ9RWxlbWVudCIsCiAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJOYW1lZFR5cGVTcGVjaWZpZXIiCiAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgfSBdCiAgICAgICAgIH0sIHsKICAgICAgICAgICAgImxvY2F0b3IiIDogIjE1MDoxLTE1MDo1OCIsCiAgICAgICAgICAgICJuYW1lIiA6ICJUb1N0cmluZyIsCiAgICAgICAgICAgICJjb250ZXh0IiA6ICJQYXRpZW50IiwKICAgICAgICAgICAgImFjY2Vzc0xldmVsIiA6ICJQdWJsaWMiLAogICAgICAgICAgICAidHlwZSIgOiAiRnVuY3Rpb25EZWYiLAogICAgICAgICAgICAiZXhwcmVzc2lvbiIgOiB7CiAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIxNTA6NDgtMTUwOjU4IiwKICAgICAgICAgICAgICAgInBhdGgiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgInR5cGUiIDogIlByb3BlcnR5IiwKICAgICAgICAgICAgICAgInNvdXJjZSIgOiB7CiAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIxNTA6NDgtMTUwOjUyIiwKICAgICAgICAgICAgICAgICAgIm5hbWUiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk9wZXJhbmRSZWYiCiAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgfSwKICAgICAgICAgICAgIm9wZXJhbmQiIDogWyB7CiAgICAgICAgICAgICAgICJuYW1lIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICJvcGVyYW5kVHlwZVNwZWNpZmllciIgOiB7CiAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIxNTA6MzItMTUwOjQ0IiwKICAgICAgICAgICAgICAgICAgIm5hbWUiIDogIntodHRwOi8vaGw3Lm9yZy9maGlyfUFjY291bnRTdGF0dXMiLAogICAgICAgICAgICAgICAgICAidHlwZSIgOiAiTmFtZWRUeXBlU3BlY2lmaWVyIgogICAgICAgICAgICAgICB9CiAgICAgICAgICAgIH0gXQogICAgICAgICB9LCB7CiAgICAgICAgICAgICJsb2NhdG9yIiA6ICIxNTE6MS0xNTE6NzAiLAogICAgICAgICAgICAibmFtZSIgOiAiVG9TdHJpbmciLAogICAgICAgICAgICAiY29udGV4dCIgOiAiUGF0aWVudCIsCiAgICAgICAgICAgICJhY2Nlc3NMZXZlbCIgOiAiUHVibGljIiwKICAgICAgICAgICAgInR5cGUiIDogIkZ1bmN0aW9uRGVmIiwKICAgICAgICAgICAgImV4cHJlc3Npb24iIDogewogICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMTUxOjYwLTE1MTo3MCIsCiAgICAgICAgICAgICAgICJwYXRoIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICJ0eXBlIiA6ICJQcm9wZXJ0eSIsCiAgICAgICAgICAgICAgICJzb3VyY2UiIDogewogICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMTUxOjYwLTE1MTo2NCIsCiAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJPcGVyYW5kUmVmIgogICAgICAgICAgICAgICB9CiAgICAgICAgICAgIH0sCiAgICAgICAgICAgICJvcGVyYW5kIiA6IFsgewogICAgICAgICAgICAgICAibmFtZSIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAib3BlcmFuZFR5cGVTcGVjaWZpZXIiIDogewogICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMTUxOjMyLTE1MTo1NiIsCiAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJ7aHR0cDovL2hsNy5vcmcvZmhpcn1BY3Rpb25DYXJkaW5hbGl0eUJlaGF2aW9yIiwKICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk5hbWVkVHlwZVNwZWNpZmllciIKICAgICAgICAgICAgICAgfQogICAgICAgICAgICB9IF0KICAgICAgICAgfSwgewogICAgICAgICAgICAibG9jYXRvciIgOiAiMTUyOjEtMTUyOjY0IiwKICAgICAgICAgICAgIm5hbWUiIDogIlRvU3RyaW5nIiwKICAgICAgICAgICAgImNvbnRleHQiIDogIlBhdGllbnQiLAogICAgICAgICAgICAiYWNjZXNzTGV2ZWwiIDogIlB1YmxpYyIsCiAgICAgICAgICAgICJ0eXBlIiA6ICJGdW5jdGlvbkRlZiIsCiAgICAgICAgICAgICJleHByZXNzaW9uIiA6IHsKICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjE1Mjo1NC0xNTI6NjQiLAogICAgICAgICAgICAgICAicGF0aCIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAidHlwZSIgOiAiUHJvcGVydHkiLAogICAgICAgICAgICAgICAic291cmNlIiA6IHsKICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjE1Mjo1NC0xNTI6NTgiLAogICAgICAgICAgICAgICAgICAibmFtZSIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAgICAidHlwZSIgOiAiT3BlcmFuZFJlZiIKICAgICAgICAgICAgICAgfQogICAgICAgICAgICB9LAogICAgICAgICAgICAib3BlcmFuZCIgOiBbIHsKICAgICAgICAgICAgICAgIm5hbWUiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgIm9wZXJhbmRUeXBlU3BlY2lmaWVyIiA6IHsKICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjE1MjozMi0xNTI6NTAiLAogICAgICAgICAgICAgICAgICAibmFtZSIgOiAie2h0dHA6Ly9obDcub3JnL2ZoaXJ9QWN0aW9uQ29uZGl0aW9uS2luZCIsCiAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJOYW1lZFR5cGVTcGVjaWZpZXIiCiAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgfSBdCiAgICAgICAgIH0sIHsKICAgICAgICAgICAgImxvY2F0b3IiIDogIjE1MzoxLTE1Mzo2NyIsCiAgICAgICAgICAgICJuYW1lIiA6ICJUb1N0cmluZyIsCiAgICAgICAgICAgICJjb250ZXh0IiA6ICJQYXRpZW50IiwKICAgICAgICAgICAgImFjY2Vzc0xldmVsIiA6ICJQdWJsaWMiLAogICAgICAgICAgICAidHlwZSIgOiAiRnVuY3Rpb25EZWYiLAogICAgICAgICAgICAiZXhwcmVzc2lvbiIgOiB7CiAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIxNTM6NTctMTUzOjY3IiwKICAgICAgICAgICAgICAgInBhdGgiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgInR5cGUiIDogIlByb3BlcnR5IiwKICAgICAgICAgICAgICAgInNvdXJjZSIgOiB7CiAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIxNTM6NTctMTUzOjYxIiwKICAgICAgICAgICAgICAgICAgIm5hbWUiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk9wZXJhbmRSZWYiCiAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgfSwKICAgICAgICAgICAgIm9wZXJhbmQiIDogWyB7CiAgICAgICAgICAgICAgICJuYW1lIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICJvcGVyYW5kVHlwZVNwZWNpZmllciIgOiB7CiAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIxNTM6MzItMTUzOjUzIiwKICAgICAgICAgICAgICAgICAgIm5hbWUiIDogIntodHRwOi8vaGw3Lm9yZy9maGlyfUFjdGlvbkdyb3VwaW5nQmVoYXZpb3IiLAogICAgICAgICAgICAgICAgICAidHlwZSIgOiAiTmFtZWRUeXBlU3BlY2lmaWVyIgogICAgICAgICAgICAgICB9CiAgICAgICAgICAgIH0gXQogICAgICAgICB9LCB7CiAgICAgICAgICAgICJsb2NhdG9yIiA6ICIxNTQ6MS0xNTQ6NjYiLAogICAgICAgICAgICAibmFtZSIgOiAiVG9TdHJpbmciLAogICAgICAgICAgICAiY29udGV4dCIgOiAiUGF0aWVudCIsCiAgICAgICAgICAgICJhY2Nlc3NMZXZlbCIgOiAiUHVibGljIiwKICAgICAgICAgICAgInR5cGUiIDogIkZ1bmN0aW9uRGVmIiwKICAgICAgICAgICAgImV4cHJlc3Npb24iIDogewogICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMTU0OjU2LTE1NDo2NiIsCiAgICAgICAgICAgICAgICJwYXRoIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICJ0eXBlIiA6ICJQcm9wZXJ0eSIsCiAgICAgICAgICAgICAgICJzb3VyY2UiIDogewogICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMTU0OjU2LTE1NDo2MCIsCiAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJPcGVyYW5kUmVmIgogICAgICAgICAgICAgICB9CiAgICAgICAgICAgIH0sCiAgICAgICAgICAgICJvcGVyYW5kIiA6IFsgewogICAgICAgICAgICAgICAibmFtZSIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAib3BlcmFuZFR5cGVTcGVjaWZpZXIiIDogewogICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMTU0OjMyLTE1NDo1MiIsCiAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJ7aHR0cDovL2hsNy5vcmcvZmhpcn1BY3Rpb25QYXJ0aWNpcGFudFR5cGUiLAogICAgICAgICAgICAgICAgICAidHlwZSIgOiAiTmFtZWRUeXBlU3BlY2lmaWVyIgogICAgICAgICAgICAgICB9CiAgICAgICAgICAgIH0gXQogICAgICAgICB9LCB7CiAgICAgICAgICAgICJsb2NhdG9yIiA6ICIxNTU6MS0xNTU6NjciLAogICAgICAgICAgICAibmFtZSIgOiAiVG9TdHJpbmciLAogICAgICAgICAgICAiY29udGV4dCIgOiAiUGF0aWVudCIsCiAgICAgICAgICAgICJhY2Nlc3NMZXZlbCIgOiAiUHVibGljIiwKICAgICAgICAgICAgInR5cGUiIDogIkZ1bmN0aW9uRGVmIiwKICAgICAgICAgICAgImV4cHJlc3Npb24iIDogewogICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMTU1OjU3LTE1NTo2NyIsCiAgICAgICAgICAgICAgICJwYXRoIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICJ0eXBlIiA6ICJQcm9wZXJ0eSIsCiAgICAgICAgICAgICAgICJzb3VyY2UiIDogewogICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMTU1OjU3LTE1NTo2MSIsCiAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJPcGVyYW5kUmVmIgogICAgICAgICAgICAgICB9CiAgICAgICAgICAgIH0sCiAgICAgICAgICAgICJvcGVyYW5kIiA6IFsgewogICAgICAgICAgICAgICAibmFtZSIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAib3BlcmFuZFR5cGVTcGVjaWZpZXIiIDogewogICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMTU1OjMyLTE1NTo1MyIsCiAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJ7aHR0cDovL2hsNy5vcmcvZmhpcn1BY3Rpb25QcmVjaGVja0JlaGF2aW9yIiwKICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk5hbWVkVHlwZVNwZWNpZmllciIKICAgICAgICAgICAgICAgfQogICAgICAgICAgICB9IF0KICAgICAgICAgfSwgewogICAgICAgICAgICAibG9jYXRvciIgOiAiMTU2OjEtMTU2OjY3IiwKICAgICAgICAgICAgIm5hbWUiIDogIlRvU3RyaW5nIiwKICAgICAgICAgICAgImNvbnRleHQiIDogIlBhdGllbnQiLAogICAgICAgICAgICAiYWNjZXNzTGV2ZWwiIDogIlB1YmxpYyIsCiAgICAgICAgICAgICJ0eXBlIiA6ICJGdW5jdGlvbkRlZiIsCiAgICAgICAgICAgICJleHByZXNzaW9uIiA6IHsKICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjE1Njo1Ny0xNTY6NjciLAogICAgICAgICAgICAgICAicGF0aCIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAidHlwZSIgOiAiUHJvcGVydHkiLAogICAgICAgICAgICAgICAic291cmNlIiA6IHsKICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjE1Njo1Ny0xNTY6NjEiLAogICAgICAgICAgICAgICAgICAibmFtZSIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAgICAidHlwZSIgOiAiT3BlcmFuZFJlZiIKICAgICAgICAgICAgICAgfQogICAgICAgICAgICB9LAogICAgICAgICAgICAib3BlcmFuZCIgOiBbIHsKICAgICAgICAgICAgICAgIm5hbWUiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgIm9wZXJhbmRUeXBlU3BlY2lmaWVyIiA6IHsKICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjE1NjozMi0xNTY6NTMiLAogICAgICAgICAgICAgICAgICAibmFtZSIgOiAie2h0dHA6Ly9obDcub3JnL2ZoaXJ9QWN0aW9uUmVsYXRpb25zaGlwVHlwZSIsCiAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJOYW1lZFR5cGVTcGVjaWZpZXIiCiAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgfSBdCiAgICAgICAgIH0sIHsKICAgICAgICAgICAgImxvY2F0b3IiIDogIjE1NzoxLTE1Nzo2NyIsCiAgICAgICAgICAgICJuYW1lIiA6ICJUb1N0cmluZyIsCiAgICAgICAgICAgICJjb250ZXh0IiA6ICJQYXRpZW50IiwKICAgICAgICAgICAgImFjY2Vzc0xldmVsIiA6ICJQdWJsaWMiLAogICAgICAgICAgICAidHlwZSIgOiAiRnVuY3Rpb25EZWYiLAogICAgICAgICAgICAiZXhwcmVzc2lvbiIgOiB7CiAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIxNTc6NTctMTU3OjY3IiwKICAgICAgICAgICAgICAgInBhdGgiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgInR5cGUiIDogIlByb3BlcnR5IiwKICAgICAgICAgICAgICAgInNvdXJjZSIgOiB7CiAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIxNTc6NTctMTU3OjYxIiwKICAgICAgICAgICAgICAgICAgIm5hbWUiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk9wZXJhbmRSZWYiCiAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgfSwKICAgICAgICAgICAgIm9wZXJhbmQiIDogWyB7CiAgICAgICAgICAgICAgICJuYW1lIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICJvcGVyYW5kVHlwZVNwZWNpZmllciIgOiB7CiAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIxNTc6MzItMTU3OjUzIiwKICAgICAgICAgICAgICAgICAgIm5hbWUiIDogIntodHRwOi8vaGw3Lm9yZy9maGlyfUFjdGlvblJlcXVpcmVkQmVoYXZpb3IiLAogICAgICAgICAgICAgICAgICAidHlwZSIgOiAiTmFtZWRUeXBlU3BlY2lmaWVyIgogICAgICAgICAgICAgICB9CiAgICAgICAgICAgIH0gXQogICAgICAgICB9LCB7CiAgICAgICAgICAgICJsb2NhdG9yIiA6ICIxNTg6MS0xNTg6NjgiLAogICAgICAgICAgICAibmFtZSIgOiAiVG9TdHJpbmciLAogICAgICAgICAgICAiY29udGV4dCIgOiAiUGF0aWVudCIsCiAgICAgICAgICAgICJhY2Nlc3NMZXZlbCIgOiAiUHVibGljIiwKICAgICAgICAgICAgInR5cGUiIDogIkZ1bmN0aW9uRGVmIiwKICAgICAgICAgICAgImV4cHJlc3Npb24iIDogewogICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMTU4OjU4LTE1ODo2OCIsCiAgICAgICAgICAgICAgICJwYXRoIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICJ0eXBlIiA6ICJQcm9wZXJ0eSIsCiAgICAgICAgICAgICAgICJzb3VyY2UiIDogewogICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMTU4OjU4LTE1ODo2MiIsCiAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJPcGVyYW5kUmVmIgogICAgICAgICAgICAgICB9CiAgICAgICAgICAgIH0sCiAgICAgICAgICAgICJvcGVyYW5kIiA6IFsgewogICAgICAgICAgICAgICAibmFtZSIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAib3BlcmFuZFR5cGVTcGVjaWZpZXIiIDogewogICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMTU4OjMyLTE1ODo1NCIsCiAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJ7aHR0cDovL2hsNy5vcmcvZmhpcn1BY3Rpb25TZWxlY3Rpb25CZWhhdmlvciIsCiAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJOYW1lZFR5cGVTcGVjaWZpZXIiCiAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgfSBdCiAgICAgICAgIH0sIHsKICAgICAgICAgICAgImxvY2F0b3IiIDogIjE1OToxLTE1OTo2NyIsCiAgICAgICAgICAgICJuYW1lIiA6ICJUb1N0cmluZyIsCiAgICAgICAgICAgICJjb250ZXh0IiA6ICJQYXRpZW50IiwKICAgICAgICAgICAgImFjY2Vzc0xldmVsIiA6ICJQdWJsaWMiLAogICAgICAgICAgICAidHlwZSIgOiAiRnVuY3Rpb25EZWYiLAogICAgICAgICAgICAiZXhwcmVzc2lvbiIgOiB7CiAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIxNTk6NTctMTU5OjY3IiwKICAgICAgICAgICAgICAgInBhdGgiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgInR5cGUiIDogIlByb3BlcnR5IiwKICAgICAgICAgICAgICAgInNvdXJjZSIgOiB7CiAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIxNTk6NTctMTU5OjYxIiwKICAgICAgICAgICAgICAgICAgIm5hbWUiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk9wZXJhbmRSZWYiCiAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgfSwKICAgICAgICAgICAgIm9wZXJhbmQiIDogWyB7CiAgICAgICAgICAgICAgICJuYW1lIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICJvcGVyYW5kVHlwZVNwZWNpZmllciIgOiB7CiAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIxNTk6MzItMTU5OjUzIiwKICAgICAgICAgICAgICAgICAgIm5hbWUiIDogIntodHRwOi8vaGw3Lm9yZy9maGlyfUFjdGl2aXR5RGVmaW5pdGlvbktpbmQiLAogICAgICAgICAgICAgICAgICAidHlwZSIgOiAiTmFtZWRUeXBlU3BlY2lmaWVyIgogICAgICAgICAgICAgICB9CiAgICAgICAgICAgIH0gXQogICAgICAgICB9LCB7CiAgICAgICAgICAgICJsb2NhdG9yIiA6ICIxNjA6MS0xNjA6NjgiLAogICAgICAgICAgICAibmFtZSIgOiAiVG9TdHJpbmciLAogICAgICAgICAgICAiY29udGV4dCIgOiAiUGF0aWVudCIsCiAgICAgICAgICAgICJhY2Nlc3NMZXZlbCIgOiAiUHVibGljIiwKICAgICAgICAgICAgInR5cGUiIDogIkZ1bmN0aW9uRGVmIiwKICAgICAgICAgICAgImV4cHJlc3Npb24iIDogewogICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMTYwOjU4LTE2MDo2OCIsCiAgICAgICAgICAgICAgICJwYXRoIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICJ0eXBlIiA6ICJQcm9wZXJ0eSIsCiAgICAgICAgICAgICAgICJzb3VyY2UiIDogewogICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMTYwOjU4LTE2MDo2MiIsCiAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJPcGVyYW5kUmVmIgogICAgICAgICAgICAgICB9CiAgICAgICAgICAgIH0sCiAgICAgICAgICAgICJvcGVyYW5kIiA6IFsgewogICAgICAgICAgICAgICAibmFtZSIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAib3BlcmFuZFR5cGVTcGVjaWZpZXIiIDogewogICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMTYwOjMyLTE2MDo1NCIsCiAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJ7aHR0cDovL2hsNy5vcmcvZmhpcn1BY3Rpdml0eVBhcnRpY2lwYW50VHlwZSIsCiAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJOYW1lZFR5cGVTcGVjaWZpZXIiCiAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgfSBdCiAgICAgICAgIH0sIHsKICAgICAgICAgICAgImxvY2F0b3IiIDogIjE2MToxLTE2MTo1NiIsCiAgICAgICAgICAgICJuYW1lIiA6ICJUb1N0cmluZyIsCiAgICAgICAgICAgICJjb250ZXh0IiA6ICJQYXRpZW50IiwKICAgICAgICAgICAgImFjY2Vzc0xldmVsIiA6ICJQdWJsaWMiLAogICAgICAgICAgICAidHlwZSIgOiAiRnVuY3Rpb25EZWYiLAogICAgICAgICAgICAiZXhwcmVzc2lvbiIgOiB7CiAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIxNjE6NDYtMTYxOjU2IiwKICAgICAgICAgICAgICAgInBhdGgiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgInR5cGUiIDogIlByb3BlcnR5IiwKICAgICAgICAgICAgICAgInNvdXJjZSIgOiB7CiAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIxNjE6NDYtMTYxOjUwIiwKICAgICAgICAgICAgICAgICAgIm5hbWUiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk9wZXJhbmRSZWYiCiAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgfSwKICAgICAgICAgICAgIm9wZXJhbmQiIDogWyB7CiAgICAgICAgICAgICAgICJuYW1lIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICJvcGVyYW5kVHlwZVNwZWNpZmllciIgOiB7CiAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIxNjE6MzItMTYxOjQyIiwKICAgICAgICAgICAgICAgICAgIm5hbWUiIDogIntodHRwOi8vaGw3Lm9yZy9maGlyfUFkZHJlc3NUeXBlIiwKICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk5hbWVkVHlwZVNwZWNpZmllciIKICAgICAgICAgICAgICAgfQogICAgICAgICAgICB9IF0KICAgICAgICAgfSwgewogICAgICAgICAgICAibG9jYXRvciIgOiAiMTYyOjEtMTYyOjU1IiwKICAgICAgICAgICAgIm5hbWUiIDogIlRvU3RyaW5nIiwKICAgICAgICAgICAgImNvbnRleHQiIDogIlBhdGllbnQiLAogICAgICAgICAgICAiYWNjZXNzTGV2ZWwiIDogIlB1YmxpYyIsCiAgICAgICAgICAgICJ0eXBlIiA6ICJGdW5jdGlvbkRlZiIsCiAgICAgICAgICAgICJleHByZXNzaW9uIiA6IHsKICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjE2Mjo0NS0xNjI6NTUiLAogICAgICAgICAgICAgICAicGF0aCIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAidHlwZSIgOiAiUHJvcGVydHkiLAogICAgICAgICAgICAgICAic291cmNlIiA6IHsKICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjE2Mjo0NS0xNjI6NDkiLAogICAgICAgICAgICAgICAgICAibmFtZSIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAgICAidHlwZSIgOiAiT3BlcmFuZFJlZiIKICAgICAgICAgICAgICAgfQogICAgICAgICAgICB9LAogICAgICAgICAgICAib3BlcmFuZCIgOiBbIHsKICAgICAgICAgICAgICAgIm5hbWUiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgIm9wZXJhbmRUeXBlU3BlY2lmaWVyIiA6IHsKICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjE2MjozMi0xNjI6NDEiLAogICAgICAgICAgICAgICAgICAibmFtZSIgOiAie2h0dHA6Ly9obDcub3JnL2ZoaXJ9QWRkcmVzc1VzZSIsCiAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJOYW1lZFR5cGVTcGVjaWZpZXIiCiAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgfSBdCiAgICAgICAgIH0sIHsKICAgICAgICAgICAgImxvY2F0b3IiIDogIjE2MzoxLTE2Mzo2NSIsCiAgICAgICAgICAgICJuYW1lIiA6ICJUb1N0cmluZyIsCiAgICAgICAgICAgICJjb250ZXh0IiA6ICJQYXRpZW50IiwKICAgICAgICAgICAgImFjY2Vzc0xldmVsIiA6ICJQdWJsaWMiLAogICAgICAgICAgICAidHlwZSIgOiAiRnVuY3Rpb25EZWYiLAogICAgICAgICAgICAiZXhwcmVzc2lvbiIgOiB7CiAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIxNjM6NTUtMTYzOjY1IiwKICAgICAgICAgICAgICAgInBhdGgiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgInR5cGUiIDogIlByb3BlcnR5IiwKICAgICAgICAgICAgICAgInNvdXJjZSIgOiB7CiAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIxNjM6NTUtMTYzOjU5IiwKICAgICAgICAgICAgICAgICAgIm5hbWUiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk9wZXJhbmRSZWYiCiAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgfSwKICAgICAgICAgICAgIm9wZXJhbmQiIDogWyB7CiAgICAgICAgICAgICAgICJuYW1lIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICJvcGVyYW5kVHlwZVNwZWNpZmllciIgOiB7CiAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIxNjM6MzItMTYzOjUxIiwKICAgICAgICAgICAgICAgICAgIm5hbWUiIDogIntodHRwOi8vaGw3Lm9yZy9maGlyfUFkbWluaXN0cmF0aXZlR2VuZGVyIiwKICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk5hbWVkVHlwZVNwZWNpZmllciIKICAgICAgICAgICAgICAgfQogICAgICAgICAgICB9IF0KICAgICAgICAgfSwgewogICAgICAgICAgICAibG9jYXRvciIgOiAiMTY0OjEtMTY0OjY2IiwKICAgICAgICAgICAgIm5hbWUiIDogIlRvU3RyaW5nIiwKICAgICAgICAgICAgImNvbnRleHQiIDogIlBhdGllbnQiLAogICAgICAgICAgICAiYWNjZXNzTGV2ZWwiIDogIlB1YmxpYyIsCiAgICAgICAgICAgICJ0eXBlIiA6ICJGdW5jdGlvbkRlZiIsCiAgICAgICAgICAgICJleHByZXNzaW9uIiA6IHsKICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjE2NDo1Ni0xNjQ6NjYiLAogICAgICAgICAgICAgICAicGF0aCIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAidHlwZSIgOiAiUHJvcGVydHkiLAogICAgICAgICAgICAgICAic291cmNlIiA6IHsKICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjE2NDo1Ni0xNjQ6NjAiLAogICAgICAgICAgICAgICAgICAibmFtZSIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAgICAidHlwZSIgOiAiT3BlcmFuZFJlZiIKICAgICAgICAgICAgICAgfQogICAgICAgICAgICB9LAogICAgICAgICAgICAib3BlcmFuZCIgOiBbIHsKICAgICAgICAgICAgICAgIm5hbWUiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgIm9wZXJhbmRUeXBlU3BlY2lmaWVyIiA6IHsKICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjE2NDozMi0xNjQ6NTIiLAogICAgICAgICAgICAgICAgICAibmFtZSIgOiAie2h0dHA6Ly9obDcub3JnL2ZoaXJ9QWR2ZXJzZUV2ZW50QWN0dWFsaXR5IiwKICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk5hbWVkVHlwZVNwZWNpZmllciIKICAgICAgICAgICAgICAgfQogICAgICAgICAgICB9IF0KICAgICAgICAgfSwgewogICAgICAgICAgICAibG9jYXRvciIgOiAiMTY1OjEtMTY1OjYwIiwKICAgICAgICAgICAgIm5hbWUiIDogIlRvU3RyaW5nIiwKICAgICAgICAgICAgImNvbnRleHQiIDogIlBhdGllbnQiLAogICAgICAgICAgICAiYWNjZXNzTGV2ZWwiIDogIlB1YmxpYyIsCiAgICAgICAgICAgICJ0eXBlIiA6ICJGdW5jdGlvbkRlZiIsCiAgICAgICAgICAgICJleHByZXNzaW9uIiA6IHsKICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjE2NTo1MC0xNjU6NjAiLAogICAgICAgICAgICAgICAicGF0aCIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAidHlwZSIgOiAiUHJvcGVydHkiLAogICAgICAgICAgICAgICAic291cmNlIiA6IHsKICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjE2NTo1MC0xNjU6NTQiLAogICAgICAgICAgICAgICAgICAibmFtZSIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAgICAidHlwZSIgOiAiT3BlcmFuZFJlZiIKICAgICAgICAgICAgICAgfQogICAgICAgICAgICB9LAogICAgICAgICAgICAib3BlcmFuZCIgOiBbIHsKICAgICAgICAgICAgICAgIm5hbWUiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgIm9wZXJhbmRUeXBlU3BlY2lmaWVyIiA6IHsKICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjE2NTozMi0xNjU6NDYiLAogICAgICAgICAgICAgICAgICAibmFtZSIgOiAie2h0dHA6Ly9obDcub3JnL2ZoaXJ9QWdncmVnYXRpb25Nb2RlIiwKICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk5hbWVkVHlwZVNwZWNpZmllciIKICAgICAgICAgICAgICAgfQogICAgICAgICAgICB9IF0KICAgICAgICAgfSwgewogICAgICAgICAgICAibG9jYXRvciIgOiAiMTY2OjEtMTY2OjcxIiwKICAgICAgICAgICAgIm5hbWUiIDogIlRvU3RyaW5nIiwKICAgICAgICAgICAgImNvbnRleHQiIDogIlBhdGllbnQiLAogICAgICAgICAgICAiYWNjZXNzTGV2ZWwiIDogIlB1YmxpYyIsCiAgICAgICAgICAgICJ0eXBlIiA6ICJGdW5jdGlvbkRlZiIsCiAgICAgICAgICAgICJleHByZXNzaW9uIiA6IHsKICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjE2Njo2MS0xNjY6NzEiLAogICAgICAgICAgICAgICAicGF0aCIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAidHlwZSIgOiAiUHJvcGVydHkiLAogICAgICAgICAgICAgICAic291cmNlIiA6IHsKICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjE2Njo2MS0xNjY6NjUiLAogICAgICAgICAgICAgICAgICAibmFtZSIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAgICAidHlwZSIgOiAiT3BlcmFuZFJlZiIKICAgICAgICAgICAgICAgfQogICAgICAgICAgICB9LAogICAgICAgICAgICAib3BlcmFuZCIgOiBbIHsKICAgICAgICAgICAgICAgIm5hbWUiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgIm9wZXJhbmRUeXBlU3BlY2lmaWVyIiA6IHsKICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjE2NjozMi0xNjY6NTciLAogICAgICAgICAgICAgICAgICAibmFtZSIgOiAie2h0dHA6Ly9obDcub3JnL2ZoaXJ9QWxsZXJneUludG9sZXJhbmNlQ2F0ZWdvcnkiLAogICAgICAgICAgICAgICAgICAidHlwZSIgOiAiTmFtZWRUeXBlU3BlY2lmaWVyIgogICAgICAgICAgICAgICB9CiAgICAgICAgICAgIH0gXQogICAgICAgICB9LCB7CiAgICAgICAgICAgICJsb2NhdG9yIiA6ICIxNjc6MS0xNjc6NzQiLAogICAgICAgICAgICAibmFtZSIgOiAiVG9TdHJpbmciLAogICAgICAgICAgICAiY29udGV4dCIgOiAiUGF0aWVudCIsCiAgICAgICAgICAgICJhY2Nlc3NMZXZlbCIgOiAiUHVibGljIiwKICAgICAgICAgICAgInR5cGUiIDogIkZ1bmN0aW9uRGVmIiwKICAgICAgICAgICAgImV4cHJlc3Npb24iIDogewogICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMTY3OjY0LTE2Nzo3NCIsCiAgICAgICAgICAgICAgICJwYXRoIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICJ0eXBlIiA6ICJQcm9wZXJ0eSIsCiAgICAgICAgICAgICAgICJzb3VyY2UiIDogewogICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMTY3OjY0LTE2Nzo2OCIsCiAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJPcGVyYW5kUmVmIgogICAgICAgICAgICAgICB9CiAgICAgICAgICAgIH0sCiAgICAgICAgICAgICJvcGVyYW5kIiA6IFsgewogICAgICAgICAgICAgICAibmFtZSIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAib3BlcmFuZFR5cGVTcGVjaWZpZXIiIDogewogICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMTY3OjMyLTE2Nzo2MCIsCiAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJ7aHR0cDovL2hsNy5vcmcvZmhpcn1BbGxlcmd5SW50b2xlcmFuY2VDcml0aWNhbGl0eSIsCiAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJOYW1lZFR5cGVTcGVjaWZpZXIiCiAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgfSBdCiAgICAgICAgIH0sIHsKICAgICAgICAgICAgImxvY2F0b3IiIDogIjE2ODoxLTE2ODo3MSIsCiAgICAgICAgICAgICJuYW1lIiA6ICJUb1N0cmluZyIsCiAgICAgICAgICAgICJjb250ZXh0IiA6ICJQYXRpZW50IiwKICAgICAgICAgICAgImFjY2Vzc0xldmVsIiA6ICJQdWJsaWMiLAogICAgICAgICAgICAidHlwZSIgOiAiRnVuY3Rpb25EZWYiLAogICAgICAgICAgICAiZXhwcmVzc2lvbiIgOiB7CiAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIxNjg6NjEtMTY4OjcxIiwKICAgICAgICAgICAgICAgInBhdGgiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgInR5cGUiIDogIlByb3BlcnR5IiwKICAgICAgICAgICAgICAgInNvdXJjZSIgOiB7CiAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIxNjg6NjEtMTY4OjY1IiwKICAgICAgICAgICAgICAgICAgIm5hbWUiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk9wZXJhbmRSZWYiCiAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgfSwKICAgICAgICAgICAgIm9wZXJhbmQiIDogWyB7CiAgICAgICAgICAgICAgICJuYW1lIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICJvcGVyYW5kVHlwZVNwZWNpZmllciIgOiB7CiAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIxNjg6MzItMTY4OjU3IiwKICAgICAgICAgICAgICAgICAgIm5hbWUiIDogIntodHRwOi8vaGw3Lm9yZy9maGlyfUFsbGVyZ3lJbnRvbGVyYW5jZVNldmVyaXR5IiwKICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk5hbWVkVHlwZVNwZWNpZmllciIKICAgICAgICAgICAgICAgfQogICAgICAgICAgICB9IF0KICAgICAgICAgfSwgewogICAgICAgICAgICAibG9jYXRvciIgOiAiMTY5OjEtMTY5OjY3IiwKICAgICAgICAgICAgIm5hbWUiIDogIlRvU3RyaW5nIiwKICAgICAgICAgICAgImNvbnRleHQiIDogIlBhdGllbnQiLAogICAgICAgICAgICAiYWNjZXNzTGV2ZWwiIDogIlB1YmxpYyIsCiAgICAgICAgICAgICJ0eXBlIiA6ICJGdW5jdGlvbkRlZiIsCiAgICAgICAgICAgICJleHByZXNzaW9uIiA6IHsKICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjE2OTo1Ny0xNjk6NjciLAogICAgICAgICAgICAgICAicGF0aCIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAidHlwZSIgOiAiUHJvcGVydHkiLAogICAgICAgICAgICAgICAic291cmNlIiA6IHsKICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjE2OTo1Ny0xNjk6NjEiLAogICAgICAgICAgICAgICAgICAibmFtZSIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAgICAidHlwZSIgOiAiT3BlcmFuZFJlZiIKICAgICAgICAgICAgICAgfQogICAgICAgICAgICB9LAogICAgICAgICAgICAib3BlcmFuZCIgOiBbIHsKICAgICAgICAgICAgICAgIm5hbWUiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgIm9wZXJhbmRUeXBlU3BlY2lmaWVyIiA6IHsKICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjE2OTozMi0xNjk6NTMiLAogICAgICAgICAgICAgICAgICAibmFtZSIgOiAie2h0dHA6Ly9obDcub3JnL2ZoaXJ9QWxsZXJneUludG9sZXJhbmNlVHlwZSIsCiAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJOYW1lZFR5cGVTcGVjaWZpZXIiCiAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgfSBdCiAgICAgICAgIH0sIHsKICAgICAgICAgICAgImxvY2F0b3IiIDogIjE3MDoxLTE3MDo2MiIsCiAgICAgICAgICAgICJuYW1lIiA6ICJUb1N0cmluZyIsCiAgICAgICAgICAgICJjb250ZXh0IiA6ICJQYXRpZW50IiwKICAgICAgICAgICAgImFjY2Vzc0xldmVsIiA6ICJQdWJsaWMiLAogICAgICAgICAgICAidHlwZSIgOiAiRnVuY3Rpb25EZWYiLAogICAgICAgICAgICAiZXhwcmVzc2lvbiIgOiB7CiAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIxNzA6NTItMTcwOjYyIiwKICAgICAgICAgICAgICAgInBhdGgiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgInR5cGUiIDogIlByb3BlcnR5IiwKICAgICAgICAgICAgICAgInNvdXJjZSIgOiB7CiAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIxNzA6NTItMTcwOjU2IiwKICAgICAgICAgICAgICAgICAgIm5hbWUiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk9wZXJhbmRSZWYiCiAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgfSwKICAgICAgICAgICAgIm9wZXJhbmQiIDogWyB7CiAgICAgICAgICAgICAgICJuYW1lIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICJvcGVyYW5kVHlwZVNwZWNpZmllciIgOiB7CiAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIxNzA6MzItMTcwOjQ4IiwKICAgICAgICAgICAgICAgICAgIm5hbWUiIDogIntodHRwOi8vaGw3Lm9yZy9maGlyfUFwcG9pbnRtZW50U3RhdHVzIiwKICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk5hbWVkVHlwZVNwZWNpZmllciIKICAgICAgICAgICAgICAgfQogICAgICAgICAgICB9IF0KICAgICAgICAgfSwgewogICAgICAgICAgICAibG9jYXRvciIgOiAiMTcxOjEtMTcxOjY3IiwKICAgICAgICAgICAgIm5hbWUiIDogIlRvU3RyaW5nIiwKICAgICAgICAgICAgImNvbnRleHQiIDogIlBhdGllbnQiLAogICAgICAgICAgICAiYWNjZXNzTGV2ZWwiIDogIlB1YmxpYyIsCiAgICAgICAgICAgICJ0eXBlIiA6ICJGdW5jdGlvbkRlZiIsCiAgICAgICAgICAgICJleHByZXNzaW9uIiA6IHsKICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjE3MTo1Ny0xNzE6NjciLAogICAgICAgICAgICAgICAicGF0aCIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAidHlwZSIgOiAiUHJvcGVydHkiLAogICAgICAgICAgICAgICAic291cmNlIiA6IHsKICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjE3MTo1Ny0xNzE6NjEiLAogICAgICAgICAgICAgICAgICAibmFtZSIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAgICAidHlwZSIgOiAiT3BlcmFuZFJlZiIKICAgICAgICAgICAgICAgfQogICAgICAgICAgICB9LAogICAgICAgICAgICAib3BlcmFuZCIgOiBbIHsKICAgICAgICAgICAgICAgIm5hbWUiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgIm9wZXJhbmRUeXBlU3BlY2lmaWVyIiA6IHsKICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjE3MTozMi0xNzE6NTMiLAogICAgICAgICAgICAgICAgICAibmFtZSIgOiAie2h0dHA6Ly9obDcub3JnL2ZoaXJ9QXNzZXJ0aW9uRGlyZWN0aW9uVHlwZSIsCiAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJOYW1lZFR5cGVTcGVjaWZpZXIiCiAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgfSBdCiAgICAgICAgIH0sIHsKICAgICAgICAgICAgImxvY2F0b3IiIDogIjE3MjoxLTE3Mjo2NiIsCiAgICAgICAgICAgICJuYW1lIiA6ICJUb1N0cmluZyIsCiAgICAgICAgICAgICJjb250ZXh0IiA6ICJQYXRpZW50IiwKICAgICAgICAgICAgImFjY2Vzc0xldmVsIiA6ICJQdWJsaWMiLAogICAgICAgICAgICAidHlwZSIgOiAiRnVuY3Rpb25EZWYiLAogICAgICAgICAgICAiZXhwcmVzc2lvbiIgOiB7CiAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIxNzI6NTYtMTcyOjY2IiwKICAgICAgICAgICAgICAgInBhdGgiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgInR5cGUiIDogIlByb3BlcnR5IiwKICAgICAgICAgICAgICAgInNvdXJjZSIgOiB7CiAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIxNzI6NTYtMTcyOjYwIiwKICAgICAgICAgICAgICAgICAgIm5hbWUiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk9wZXJhbmRSZWYiCiAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgfSwKICAgICAgICAgICAgIm9wZXJhbmQiIDogWyB7CiAgICAgICAgICAgICAgICJuYW1lIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICJvcGVyYW5kVHlwZVNwZWNpZmllciIgOiB7CiAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIxNzI6MzItMTcyOjUyIiwKICAgICAgICAgICAgICAgICAgIm5hbWUiIDogIntodHRwOi8vaGw3Lm9yZy9maGlyfUFzc2VydGlvbk9wZXJhdG9yVHlwZSIsCiAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJOYW1lZFR5cGVTcGVjaWZpZXIiCiAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgfSBdCiAgICAgICAgIH0sIHsKICAgICAgICAgICAgImxvY2F0b3IiIDogIjE3MzoxLTE3Mzo2NyIsCiAgICAgICAgICAgICJuYW1lIiA6ICJUb1N0cmluZyIsCiAgICAgICAgICAgICJjb250ZXh0IiA6ICJQYXRpZW50IiwKICAgICAgICAgICAgImFjY2Vzc0xldmVsIiA6ICJQdWJsaWMiLAogICAgICAgICAgICAidHlwZSIgOiAiRnVuY3Rpb25EZWYiLAogICAgICAgICAgICAiZXhwcmVzc2lvbiIgOiB7CiAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIxNzM6NTctMTczOjY3IiwKICAgICAgICAgICAgICAgInBhdGgiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgInR5cGUiIDogIlByb3BlcnR5IiwKICAgICAgICAgICAgICAgInNvdXJjZSIgOiB7CiAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIxNzM6NTctMTczOjYxIiwKICAgICAgICAgICAgICAgICAgIm5hbWUiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk9wZXJhbmRSZWYiCiAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgfSwKICAgICAgICAgICAgIm9wZXJhbmQiIDogWyB7CiAgICAgICAgICAgICAgICJuYW1lIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICJvcGVyYW5kVHlwZVNwZWNpZmllciIgOiB7CiAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIxNzM6MzItMTczOjUzIiwKICAgICAgICAgICAgICAgICAgIm5hbWUiIDogIntodHRwOi8vaGw3Lm9yZy9maGlyfUFzc2VydGlvblJlc3BvbnNlVHlwZXMiLAogICAgICAgICAgICAgICAgICAidHlwZSIgOiAiTmFtZWRUeXBlU3BlY2lmaWVyIgogICAgICAgICAgICAgICB9CiAgICAgICAgICAgIH0gXQogICAgICAgICB9LCB7CiAgICAgICAgICAgICJsb2NhdG9yIiA6ICIxNzQ6MS0xNzQ6NjEiLAogICAgICAgICAgICAibmFtZSIgOiAiVG9TdHJpbmciLAogICAgICAgICAgICAiY29udGV4dCIgOiAiUGF0aWVudCIsCiAgICAgICAgICAgICJhY2Nlc3NMZXZlbCIgOiAiUHVibGljIiwKICAgICAgICAgICAgInR5cGUiIDogIkZ1bmN0aW9uRGVmIiwKICAgICAgICAgICAgImV4cHJlc3Npb24iIDogewogICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMTc0OjUxLTE3NDo2MSIsCiAgICAgICAgICAgICAgICJwYXRoIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICJ0eXBlIiA6ICJQcm9wZXJ0eSIsCiAgICAgICAgICAgICAgICJzb3VyY2UiIDogewogICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMTc0OjUxLTE3NDo1NSIsCiAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJPcGVyYW5kUmVmIgogICAgICAgICAgICAgICB9CiAgICAgICAgICAgIH0sCiAgICAgICAgICAgICJvcGVyYW5kIiA6IFsgewogICAgICAgICAgICAgICAibmFtZSIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAib3BlcmFuZFR5cGVTcGVjaWZpZXIiIDogewogICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMTc0OjMyLTE3NDo0NyIsCiAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJ7aHR0cDovL2hsNy5vcmcvZmhpcn1BdWRpdEV2ZW50QWN0aW9uIiwKICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk5hbWVkVHlwZVNwZWNpZmllciIKICAgICAgICAgICAgICAgfQogICAgICAgICAgICB9IF0KICAgICAgICAgfSwgewogICAgICAgICAgICAibG9jYXRvciIgOiAiMTc1OjEtMTc1OjcxIiwKICAgICAgICAgICAgIm5hbWUiIDogIlRvU3RyaW5nIiwKICAgICAgICAgICAgImNvbnRleHQiIDogIlBhdGllbnQiLAogICAgICAgICAgICAiYWNjZXNzTGV2ZWwiIDogIlB1YmxpYyIsCiAgICAgICAgICAgICJ0eXBlIiA6ICJGdW5jdGlvbkRlZiIsCiAgICAgICAgICAgICJleHByZXNzaW9uIiA6IHsKICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjE3NTo2MS0xNzU6NzEiLAogICAgICAgICAgICAgICAicGF0aCIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAidHlwZSIgOiAiUHJvcGVydHkiLAogICAgICAgICAgICAgICAic291cmNlIiA6IHsKICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjE3NTo2MS0xNzU6NjUiLAogICAgICAgICAgICAgICAgICAibmFtZSIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAgICAidHlwZSIgOiAiT3BlcmFuZFJlZiIKICAgICAgICAgICAgICAgfQogICAgICAgICAgICB9LAogICAgICAgICAgICAib3BlcmFuZCIgOiBbIHsKICAgICAgICAgICAgICAgIm5hbWUiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgIm9wZXJhbmRUeXBlU3BlY2lmaWVyIiA6IHsKICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjE3NTozMi0xNzU6NTciLAogICAgICAgICAgICAgICAgICAibmFtZSIgOiAie2h0dHA6Ly9obDcub3JnL2ZoaXJ9QXVkaXRFdmVudEFnZW50TmV0d29ya1R5cGUiLAogICAgICAgICAgICAgICAgICAidHlwZSIgOiAiTmFtZWRUeXBlU3BlY2lmaWVyIgogICAgICAgICAgICAgICB9CiAgICAgICAgICAgIH0gXQogICAgICAgICB9LCB7CiAgICAgICAgICAgICJsb2NhdG9yIiA6ICIxNzY6MS0xNzY6NjIiLAogICAgICAgICAgICAibmFtZSIgOiAiVG9TdHJpbmciLAogICAgICAgICAgICAiY29udGV4dCIgOiAiUGF0aWVudCIsCiAgICAgICAgICAgICJhY2Nlc3NMZXZlbCIgOiAiUHVibGljIiwKICAgICAgICAgICAgInR5cGUiIDogIkZ1bmN0aW9uRGVmIiwKICAgICAgICAgICAgImV4cHJlc3Npb24iIDogewogICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMTc2OjUyLTE3Njo2MiIsCiAgICAgICAgICAgICAgICJwYXRoIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICJ0eXBlIiA6ICJQcm9wZXJ0eSIsCiAgICAgICAgICAgICAgICJzb3VyY2UiIDogewogICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMTc2OjUyLTE3Njo1NiIsCiAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJPcGVyYW5kUmVmIgogICAgICAgICAgICAgICB9CiAgICAgICAgICAgIH0sCiAgICAgICAgICAgICJvcGVyYW5kIiA6IFsgewogICAgICAgICAgICAgICAibmFtZSIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAib3BlcmFuZFR5cGVTcGVjaWZpZXIiIDogewogICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMTc2OjMyLTE3Njo0OCIsCiAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJ7aHR0cDovL2hsNy5vcmcvZmhpcn1BdWRpdEV2ZW50T3V0Y29tZSIsCiAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJOYW1lZFR5cGVTcGVjaWZpZXIiCiAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgfSBdCiAgICAgICAgIH0sIHsKICAgICAgICAgICAgImxvY2F0b3IiIDogIjE3NzoxLTE3Nzo2MCIsCiAgICAgICAgICAgICJuYW1lIiA6ICJUb1N0cmluZyIsCiAgICAgICAgICAgICJjb250ZXh0IiA6ICJQYXRpZW50IiwKICAgICAgICAgICAgImFjY2Vzc0xldmVsIiA6ICJQdWJsaWMiLAogICAgICAgICAgICAidHlwZSIgOiAiRnVuY3Rpb25EZWYiLAogICAgICAgICAgICAiZXhwcmVzc2lvbiIgOiB7CiAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIxNzc6NTAtMTc3OjYwIiwKICAgICAgICAgICAgICAgInBhdGgiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgInR5cGUiIDogIlByb3BlcnR5IiwKICAgICAgICAgICAgICAgInNvdXJjZSIgOiB7CiAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIxNzc6NTAtMTc3OjU0IiwKICAgICAgICAgICAgICAgICAgIm5hbWUiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk9wZXJhbmRSZWYiCiAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgfSwKICAgICAgICAgICAgIm9wZXJhbmQiIDogWyB7CiAgICAgICAgICAgICAgICJuYW1lIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICJvcGVyYW5kVHlwZVNwZWNpZmllciIgOiB7CiAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIxNzc6MzItMTc3OjQ2IiwKICAgICAgICAgICAgICAgICAgIm5hbWUiIDogIntodHRwOi8vaGw3Lm9yZy9maGlyfUJpbmRpbmdTdHJlbmd0aCIsCiAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJOYW1lZFR5cGVTcGVjaWZpZXIiCiAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgfSBdCiAgICAgICAgIH0sIHsKICAgICAgICAgICAgImxvY2F0b3IiIDogIjE3ODoxLTE3ODo3OSIsCiAgICAgICAgICAgICJuYW1lIiA6ICJUb1N0cmluZyIsCiAgICAgICAgICAgICJjb250ZXh0IiA6ICJQYXRpZW50IiwKICAgICAgICAgICAgImFjY2Vzc0xldmVsIiA6ICJQdWJsaWMiLAogICAgICAgICAgICAidHlwZSIgOiAiRnVuY3Rpb25EZWYiLAogICAgICAgICAgICAiZXhwcmVzc2lvbiIgOiB7CiAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIxNzg6NjktMTc4Ojc5IiwKICAgICAgICAgICAgICAgInBhdGgiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgInR5cGUiIDogIlByb3BlcnR5IiwKICAgICAgICAgICAgICAgInNvdXJjZSIgOiB7CiAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIxNzg6NjktMTc4OjczIiwKICAgICAgICAgICAgICAgICAgIm5hbWUiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk9wZXJhbmRSZWYiCiAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgfSwKICAgICAgICAgICAgIm9wZXJhbmQiIDogWyB7CiAgICAgICAgICAgICAgICJuYW1lIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICJvcGVyYW5kVHlwZVNwZWNpZmllciIgOiB7CiAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIxNzg6MzItMTc4OjY1IiwKICAgICAgICAgICAgICAgICAgIm5hbWUiIDogIntodHRwOi8vaGw3Lm9yZy9maGlyfUJpb2xvZ2ljYWxseURlcml2ZWRQcm9kdWN0Q2F0ZWdvcnkiLAogICAgICAgICAgICAgICAgICAidHlwZSIgOiAiTmFtZWRUeXBlU3BlY2lmaWVyIgogICAgICAgICAgICAgICB9CiAgICAgICAgICAgIH0gXQogICAgICAgICB9LCB7CiAgICAgICAgICAgICJsb2NhdG9yIiA6ICIxNzk6MS0xNzk6NzciLAogICAgICAgICAgICAibmFtZSIgOiAiVG9TdHJpbmciLAogICAgICAgICAgICAiY29udGV4dCIgOiAiUGF0aWVudCIsCiAgICAgICAgICAgICJhY2Nlc3NMZXZlbCIgOiAiUHVibGljIiwKICAgICAgICAgICAgInR5cGUiIDogIkZ1bmN0aW9uRGVmIiwKICAgICAgICAgICAgImV4cHJlc3Npb24iIDogewogICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMTc5OjY3LTE3OTo3NyIsCiAgICAgICAgICAgICAgICJwYXRoIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICJ0eXBlIiA6ICJQcm9wZXJ0eSIsCiAgICAgICAgICAgICAgICJzb3VyY2UiIDogewogICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMTc5OjY3LTE3OTo3MSIsCiAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJPcGVyYW5kUmVmIgogICAgICAgICAgICAgICB9CiAgICAgICAgICAgIH0sCiAgICAgICAgICAgICJvcGVyYW5kIiA6IFsgewogICAgICAgICAgICAgICAibmFtZSIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAib3BlcmFuZFR5cGVTcGVjaWZpZXIiIDogewogICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMTc5OjMyLTE3OTo2MyIsCiAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJ7aHR0cDovL2hsNy5vcmcvZmhpcn1CaW9sb2dpY2FsbHlEZXJpdmVkUHJvZHVjdFN0YXR1cyIsCiAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJOYW1lZFR5cGVTcGVjaWZpZXIiCiAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgfSBdCiAgICAgICAgIH0sIHsKICAgICAgICAgICAgImxvY2F0b3IiIDogIjE4MDoxLTE4MDo4MyIsCiAgICAgICAgICAgICJuYW1lIiA6ICJUb1N0cmluZyIsCiAgICAgICAgICAgICJjb250ZXh0IiA6ICJQYXRpZW50IiwKICAgICAgICAgICAgImFjY2Vzc0xldmVsIiA6ICJQdWJsaWMiLAogICAgICAgICAgICAidHlwZSIgOiAiRnVuY3Rpb25EZWYiLAogICAgICAgICAgICAiZXhwcmVzc2lvbiIgOiB7CiAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIxODA6NzMtMTgwOjgzIiwKICAgICAgICAgICAgICAgInBhdGgiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgInR5cGUiIDogIlByb3BlcnR5IiwKICAgICAgICAgICAgICAgInNvdXJjZSIgOiB7CiAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIxODA6NzMtMTgwOjc3IiwKICAgICAgICAgICAgICAgICAgIm5hbWUiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk9wZXJhbmRSZWYiCiAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgfSwKICAgICAgICAgICAgIm9wZXJhbmQiIDogWyB7CiAgICAgICAgICAgICAgICJuYW1lIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICJvcGVyYW5kVHlwZVNwZWNpZmllciIgOiB7CiAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIxODA6MzItMTgwOjY5IiwKICAgICAgICAgICAgICAgICAgIm5hbWUiIDogIntodHRwOi8vaGw3Lm9yZy9maGlyfUJpb2xvZ2ljYWxseURlcml2ZWRQcm9kdWN0U3RvcmFnZVNjYWxlIiwKICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk5hbWVkVHlwZVNwZWNpZmllciIKICAgICAgICAgICAgICAgfQogICAgICAgICAgICB9IF0KICAgICAgICAgfSwgewogICAgICAgICAgICAibG9jYXRvciIgOiAiMTgxOjEtMTgxOjU1IiwKICAgICAgICAgICAgIm5hbWUiIDogIlRvU3RyaW5nIiwKICAgICAgICAgICAgImNvbnRleHQiIDogIlBhdGllbnQiLAogICAgICAgICAgICAiYWNjZXNzTGV2ZWwiIDogIlB1YmxpYyIsCiAgICAgICAgICAgICJ0eXBlIiA6ICJGdW5jdGlvbkRlZiIsCiAgICAgICAgICAgICJleHByZXNzaW9uIiA6IHsKICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjE4MTo0NS0xODE6NTUiLAogICAgICAgICAgICAgICAicGF0aCIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAidHlwZSIgOiAiUHJvcGVydHkiLAogICAgICAgICAgICAgICAic291cmNlIiA6IHsKICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjE4MTo0NS0xODE6NDkiLAogICAgICAgICAgICAgICAgICAibmFtZSIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAgICAidHlwZSIgOiAiT3BlcmFuZFJlZiIKICAgICAgICAgICAgICAgfQogICAgICAgICAgICB9LAogICAgICAgICAgICAib3BlcmFuZCIgOiBbIHsKICAgICAgICAgICAgICAgIm5hbWUiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgIm9wZXJhbmRUeXBlU3BlY2lmaWVyIiA6IHsKICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjE4MTozMi0xODE6NDEiLAogICAgICAgICAgICAgICAgICAibmFtZSIgOiAie2h0dHA6Ly9obDcub3JnL2ZoaXJ9QnVuZGxlVHlwZSIsCiAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJOYW1lZFR5cGVTcGVjaWZpZXIiCiAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgfSBdCiAgICAgICAgIH0sIHsKICAgICAgICAgICAgImxvY2F0b3IiIDogIjE4MjoxLTE4Mjo2OCIsCiAgICAgICAgICAgICJuYW1lIiA6ICJUb1N0cmluZyIsCiAgICAgICAgICAgICJjb250ZXh0IiA6ICJQYXRpZW50IiwKICAgICAgICAgICAgImFjY2Vzc0xldmVsIiA6ICJQdWJsaWMiLAogICAgICAgICAgICAidHlwZSIgOiAiRnVuY3Rpb25EZWYiLAogICAgICAgICAgICAiZXhwcmVzc2lvbiIgOiB7CiAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIxODI6NTgtMTgyOjY4IiwKICAgICAgICAgICAgICAgInBhdGgiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgInR5cGUiIDogIlByb3BlcnR5IiwKICAgICAgICAgICAgICAgInNvdXJjZSIgOiB7CiAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIxODI6NTgtMTgyOjYyIiwKICAgICAgICAgICAgICAgICAgIm5hbWUiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk9wZXJhbmRSZWYiCiAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgfSwKICAgICAgICAgICAgIm9wZXJhbmQiIDogWyB7CiAgICAgICAgICAgICAgICJuYW1lIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICJvcGVyYW5kVHlwZVNwZWNpZmllciIgOiB7CiAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIxODI6MzItMTgyOjU0IiwKICAgICAgICAgICAgICAgICAgIm5hbWUiIDogIntodHRwOi8vaGw3Lm9yZy9maGlyfUNhcGFiaWxpdHlTdGF0ZW1lbnRLaW5kIiwKICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk5hbWVkVHlwZVNwZWNpZmllciIKICAgICAgICAgICAgICAgfQogICAgICAgICAgICB9IF0KICAgICAgICAgfSwgewogICAgICAgICAgICAibG9jYXRvciIgOiAiMTgzOjEtMTgzOjY1IiwKICAgICAgICAgICAgIm5hbWUiIDogIlRvU3RyaW5nIiwKICAgICAgICAgICAgImNvbnRleHQiIDogIlBhdGllbnQiLAogICAgICAgICAgICAiYWNjZXNzTGV2ZWwiIDogIlB1YmxpYyIsCiAgICAgICAgICAgICJ0eXBlIiA6ICJGdW5jdGlvbkRlZiIsCiAgICAgICAgICAgICJleHByZXNzaW9uIiA6IHsKICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjE4Mzo1NS0xODM6NjUiLAogICAgICAgICAgICAgICAicGF0aCIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAidHlwZSIgOiAiUHJvcGVydHkiLAogICAgICAgICAgICAgICAic291cmNlIiA6IHsKICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjE4Mzo1NS0xODM6NTkiLAogICAgICAgICAgICAgICAgICAibmFtZSIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAgICAidHlwZSIgOiAiT3BlcmFuZFJlZiIKICAgICAgICAgICAgICAgfQogICAgICAgICAgICB9LAogICAgICAgICAgICAib3BlcmFuZCIgOiBbIHsKICAgICAgICAgICAgICAgIm5hbWUiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgIm9wZXJhbmRUeXBlU3BlY2lmaWVyIiA6IHsKICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjE4MzozMi0xODM6NTEiLAogICAgICAgICAgICAgICAgICAibmFtZSIgOiAie2h0dHA6Ly9obDcub3JnL2ZoaXJ9Q2FyZVBsYW5BY3Rpdml0eUtpbmQiLAogICAgICAgICAgICAgICAgICAidHlwZSIgOiAiTmFtZWRUeXBlU3BlY2lmaWVyIgogICAgICAgICAgICAgICB9CiAgICAgICAgICAgIH0gXQogICAgICAgICB9LCB7CiAgICAgICAgICAgICJsb2NhdG9yIiA6ICIxODQ6MS0xODQ6NjciLAogICAgICAgICAgICAibmFtZSIgOiAiVG9TdHJpbmciLAogICAgICAgICAgICAiY29udGV4dCIgOiAiUGF0aWVudCIsCiAgICAgICAgICAgICJhY2Nlc3NMZXZlbCIgOiAiUHVibGljIiwKICAgICAgICAgICAgInR5cGUiIDogIkZ1bmN0aW9uRGVmIiwKICAgICAgICAgICAgImV4cHJlc3Npb24iIDogewogICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMTg0OjU3LTE4NDo2NyIsCiAgICAgICAgICAgICAgICJwYXRoIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICJ0eXBlIiA6ICJQcm9wZXJ0eSIsCiAgICAgICAgICAgICAgICJzb3VyY2UiIDogewogICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMTg0OjU3LTE4NDo2MSIsCiAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJPcGVyYW5kUmVmIgogICAgICAgICAgICAgICB9CiAgICAgICAgICAgIH0sCiAgICAgICAgICAgICJvcGVyYW5kIiA6IFsgewogICAgICAgICAgICAgICAibmFtZSIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAib3BlcmFuZFR5cGVTcGVjaWZpZXIiIDogewogICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMTg0OjMyLTE4NDo1MyIsCiAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJ7aHR0cDovL2hsNy5vcmcvZmhpcn1DYXJlUGxhbkFjdGl2aXR5U3RhdHVzIiwKICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk5hbWVkVHlwZVNwZWNpZmllciIKICAgICAgICAgICAgICAgfQogICAgICAgICAgICB9IF0KICAgICAgICAgfSwgewogICAgICAgICAgICAibG9jYXRvciIgOiAiMTg1OjEtMTg1OjU5IiwKICAgICAgICAgICAgIm5hbWUiIDogIlRvU3RyaW5nIiwKICAgICAgICAgICAgImNvbnRleHQiIDogIlBhdGllbnQiLAogICAgICAgICAgICAiYWNjZXNzTGV2ZWwiIDogIlB1YmxpYyIsCiAgICAgICAgICAgICJ0eXBlIiA6ICJGdW5jdGlvbkRlZiIsCiAgICAgICAgICAgICJleHByZXNzaW9uIiA6IHsKICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjE4NTo0OS0xODU6NTkiLAogICAgICAgICAgICAgICAicGF0aCIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAidHlwZSIgOiAiUHJvcGVydHkiLAogICAgICAgICAgICAgICAic291cmNlIiA6IHsKICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjE4NTo0OS0xODU6NTMiLAogICAgICAgICAgICAgICAgICAibmFtZSIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAgICAidHlwZSIgOiAiT3BlcmFuZFJlZiIKICAgICAgICAgICAgICAgfQogICAgICAgICAgICB9LAogICAgICAgICAgICAib3BlcmFuZCIgOiBbIHsKICAgICAgICAgICAgICAgIm5hbWUiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgIm9wZXJhbmRUeXBlU3BlY2lmaWVyIiA6IHsKICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjE4NTozMi0xODU6NDUiLAogICAgICAgICAgICAgICAgICAibmFtZSIgOiAie2h0dHA6Ly9obDcub3JnL2ZoaXJ9Q2FyZVBsYW5JbnRlbnQiLAogICAgICAgICAgICAgICAgICAidHlwZSIgOiAiTmFtZWRUeXBlU3BlY2lmaWVyIgogICAgICAgICAgICAgICB9CiAgICAgICAgICAgIH0gXQogICAgICAgICB9LCB7CiAgICAgICAgICAgICJsb2NhdG9yIiA6ICIxODY6MS0xODY6NTkiLAogICAgICAgICAgICAibmFtZSIgOiAiVG9TdHJpbmciLAogICAgICAgICAgICAiY29udGV4dCIgOiAiUGF0aWVudCIsCiAgICAgICAgICAgICJhY2Nlc3NMZXZlbCIgOiAiUHVibGljIiwKICAgICAgICAgICAgInR5cGUiIDogIkZ1bmN0aW9uRGVmIiwKICAgICAgICAgICAgImV4cHJlc3Npb24iIDogewogICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMTg2OjQ5LTE4Njo1OSIsCiAgICAgICAgICAgICAgICJwYXRoIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICJ0eXBlIiA6ICJQcm9wZXJ0eSIsCiAgICAgICAgICAgICAgICJzb3VyY2UiIDogewogICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMTg2OjQ5LTE4Njo1MyIsCiAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJPcGVyYW5kUmVmIgogICAgICAgICAgICAgICB9CiAgICAgICAgICAgIH0sCiAgICAgICAgICAgICJvcGVyYW5kIiA6IFsgewogICAgICAgICAgICAgICAibmFtZSIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAib3BlcmFuZFR5cGVTcGVjaWZpZXIiIDogewogICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMTg2OjMyLTE4Njo0NSIsCiAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJ7aHR0cDovL2hsNy5vcmcvZmhpcn1DYXJlUGxhblN0YXR1cyIsCiAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJOYW1lZFR5cGVTcGVjaWZpZXIiCiAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgfSBdCiAgICAgICAgIH0sIHsKICAgICAgICAgICAgImxvY2F0b3IiIDogIjE4NzoxLTE4Nzo1OSIsCiAgICAgICAgICAgICJuYW1lIiA6ICJUb1N0cmluZyIsCiAgICAgICAgICAgICJjb250ZXh0IiA6ICJQYXRpZW50IiwKICAgICAgICAgICAgImFjY2Vzc0xldmVsIiA6ICJQdWJsaWMiLAogICAgICAgICAgICAidHlwZSIgOiAiRnVuY3Rpb25EZWYiLAogICAgICAgICAgICAiZXhwcmVzc2lvbiIgOiB7CiAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIxODc6NDktMTg3OjU5IiwKICAgICAgICAgICAgICAgInBhdGgiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgInR5cGUiIDogIlByb3BlcnR5IiwKICAgICAgICAgICAgICAgInNvdXJjZSIgOiB7CiAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIxODc6NDktMTg3OjUzIiwKICAgICAgICAgICAgICAgICAgIm5hbWUiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk9wZXJhbmRSZWYiCiAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgfSwKICAgICAgICAgICAgIm9wZXJhbmQiIDogWyB7CiAgICAgICAgICAgICAgICJuYW1lIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICJvcGVyYW5kVHlwZVNwZWNpZmllciIgOiB7CiAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIxODc6MzItMTg3OjQ1IiwKICAgICAgICAgICAgICAgICAgIm5hbWUiIDogIntodHRwOi8vaGw3Lm9yZy9maGlyfUNhcmVUZWFtU3RhdHVzIiwKICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk5hbWVkVHlwZVNwZWNpZmllciIKICAgICAgICAgICAgICAgfQogICAgICAgICAgICB9IF0KICAgICAgICAgfSwgewogICAgICAgICAgICAibG9jYXRvciIgOiAiMTg4OjEtMTg4OjY5IiwKICAgICAgICAgICAgIm5hbWUiIDogIlRvU3RyaW5nIiwKICAgICAgICAgICAgImNvbnRleHQiIDogIlBhdGllbnQiLAogICAgICAgICAgICAiYWNjZXNzTGV2ZWwiIDogIlB1YmxpYyIsCiAgICAgICAgICAgICJ0eXBlIiA6ICJGdW5jdGlvbkRlZiIsCiAgICAgICAgICAgICJleHByZXNzaW9uIiA6IHsKICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjE4ODo1OS0xODg6NjkiLAogICAgICAgICAgICAgICAicGF0aCIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAidHlwZSIgOiAiUHJvcGVydHkiLAogICAgICAgICAgICAgICAic291cmNlIiA6IHsKICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjE4ODo1OS0xODg6NjMiLAogICAgICAgICAgICAgICAgICAibmFtZSIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAgICAidHlwZSIgOiAiT3BlcmFuZFJlZiIKICAgICAgICAgICAgICAgfQogICAgICAgICAgICB9LAogICAgICAgICAgICAib3BlcmFuZCIgOiBbIHsKICAgICAgICAgICAgICAgIm5hbWUiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgIm9wZXJhbmRUeXBlU3BlY2lmaWVyIiA6IHsKICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjE4ODozMi0xODg6NTUiLAogICAgICAgICAgICAgICAgICAibmFtZSIgOiAie2h0dHA6Ly9obDcub3JnL2ZoaXJ9Q2F0YWxvZ0VudHJ5UmVsYXRpb25UeXBlIiwKICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk5hbWVkVHlwZVNwZWNpZmllciIKICAgICAgICAgICAgICAgfQogICAgICAgICAgICB9IF0KICAgICAgICAgfSwgewogICAgICAgICAgICAibG9jYXRvciIgOiAiMTg5OjEtMTg5OjgzIiwKICAgICAgICAgICAgIm5hbWUiIDogIlRvU3RyaW5nIiwKICAgICAgICAgICAgImNvbnRleHQiIDogIlBhdGllbnQiLAogICAgICAgICAgICAiYWNjZXNzTGV2ZWwiIDogIlB1YmxpYyIsCiAgICAgICAgICAgICJ0eXBlIiA6ICJGdW5jdGlvbkRlZiIsCiAgICAgICAgICAgICJleHByZXNzaW9uIiA6IHsKICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjE4OTo3My0xODk6ODMiLAogICAgICAgICAgICAgICAicGF0aCIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAidHlwZSIgOiAiUHJvcGVydHkiLAogICAgICAgICAgICAgICAic291cmNlIiA6IHsKICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjE4OTo3My0xODk6NzciLAogICAgICAgICAgICAgICAgICAibmFtZSIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAgICAidHlwZSIgOiAiT3BlcmFuZFJlZiIKICAgICAgICAgICAgICAgfQogICAgICAgICAgICB9LAogICAgICAgICAgICAib3BlcmFuZCIgOiBbIHsKICAgICAgICAgICAgICAgIm5hbWUiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgIm9wZXJhbmRUeXBlU3BlY2lmaWVyIiA6IHsKICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjE4OTozMi0xODk6NjkiLAogICAgICAgICAgICAgICAgICAibmFtZSIgOiAie2h0dHA6Ly9obDcub3JnL2ZoaXJ9Q2hhcmdlSXRlbURlZmluaXRpb25QcmljZUNvbXBvbmVudFR5cGUiLAogICAgICAgICAgICAgICAgICAidHlwZSIgOiAiTmFtZWRUeXBlU3BlY2lmaWVyIgogICAgICAgICAgICAgICB9CiAgICAgICAgICAgIH0gXQogICAgICAgICB9LCB7CiAgICAgICAgICAgICJsb2NhdG9yIiA6ICIxOTA6MS0xOTA6NjEiLAogICAgICAgICAgICAibmFtZSIgOiAiVG9TdHJpbmciLAogICAgICAgICAgICAiY29udGV4dCIgOiAiUGF0aWVudCIsCiAgICAgICAgICAgICJhY2Nlc3NMZXZlbCIgOiAiUHVibGljIiwKICAgICAgICAgICAgInR5cGUiIDogIkZ1bmN0aW9uRGVmIiwKICAgICAgICAgICAgImV4cHJlc3Npb24iIDogewogICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMTkwOjUxLTE5MDo2MSIsCiAgICAgICAgICAgICAgICJwYXRoIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICJ0eXBlIiA6ICJQcm9wZXJ0eSIsCiAgICAgICAgICAgICAgICJzb3VyY2UiIDogewogICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMTkwOjUxLTE5MDo1NSIsCiAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJPcGVyYW5kUmVmIgogICAgICAgICAgICAgICB9CiAgICAgICAgICAgIH0sCiAgICAgICAgICAgICJvcGVyYW5kIiA6IFsgewogICAgICAgICAgICAgICAibmFtZSIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAib3BlcmFuZFR5cGVTcGVjaWZpZXIiIDogewogICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMTkwOjMyLTE5MDo0NyIsCiAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJ7aHR0cDovL2hsNy5vcmcvZmhpcn1DaGFyZ2VJdGVtU3RhdHVzIiwKICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk5hbWVkVHlwZVNwZWNpZmllciIKICAgICAgICAgICAgICAgfQogICAgICAgICAgICB9IF0KICAgICAgICAgfSwgewogICAgICAgICAgICAibG9jYXRvciIgOiAiMTkxOjEtMTkxOjY0IiwKICAgICAgICAgICAgIm5hbWUiIDogIlRvU3RyaW5nIiwKICAgICAgICAgICAgImNvbnRleHQiIDogIlBhdGllbnQiLAogICAgICAgICAgICAiYWNjZXNzTGV2ZWwiIDogIlB1YmxpYyIsCiAgICAgICAgICAgICJ0eXBlIiA6ICJGdW5jdGlvbkRlZiIsCiAgICAgICAgICAgICJleHByZXNzaW9uIiA6IHsKICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjE5MTo1NC0xOTE6NjQiLAogICAgICAgICAgICAgICAicGF0aCIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAidHlwZSIgOiAiUHJvcGVydHkiLAogICAgICAgICAgICAgICAic291cmNlIiA6IHsKICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjE5MTo1NC0xOTE6NTgiLAogICAgICAgICAgICAgICAgICAibmFtZSIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAgICAidHlwZSIgOiAiT3BlcmFuZFJlZiIKICAgICAgICAgICAgICAgfQogICAgICAgICAgICB9LAogICAgICAgICAgICAib3BlcmFuZCIgOiBbIHsKICAgICAgICAgICAgICAgIm5hbWUiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgIm9wZXJhbmRUeXBlU3BlY2lmaWVyIiA6IHsKICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjE5MTozMi0xOTE6NTAiLAogICAgICAgICAgICAgICAgICAibmFtZSIgOiAie2h0dHA6Ly9obDcub3JnL2ZoaXJ9Q2xhaW1SZXNwb25zZVN0YXR1cyIsCiAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJOYW1lZFR5cGVTcGVjaWZpZXIiCiAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgfSBdCiAgICAgICAgIH0sIHsKICAgICAgICAgICAgImxvY2F0b3IiIDogIjE5MjoxLTE5Mjo1NiIsCiAgICAgICAgICAgICJuYW1lIiA6ICJUb1N0cmluZyIsCiAgICAgICAgICAgICJjb250ZXh0IiA6ICJQYXRpZW50IiwKICAgICAgICAgICAgImFjY2Vzc0xldmVsIiA6ICJQdWJsaWMiLAogICAgICAgICAgICAidHlwZSIgOiAiRnVuY3Rpb25EZWYiLAogICAgICAgICAgICAiZXhwcmVzc2lvbiIgOiB7CiAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIxOTI6NDYtMTkyOjU2IiwKICAgICAgICAgICAgICAgInBhdGgiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgInR5cGUiIDogIlByb3BlcnR5IiwKICAgICAgICAgICAgICAgInNvdXJjZSIgOiB7CiAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIxOTI6NDYtMTkyOjUwIiwKICAgICAgICAgICAgICAgICAgIm5hbWUiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk9wZXJhbmRSZWYiCiAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgfSwKICAgICAgICAgICAgIm9wZXJhbmQiIDogWyB7CiAgICAgICAgICAgICAgICJuYW1lIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICJvcGVyYW5kVHlwZVNwZWNpZmllciIgOiB7CiAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIxOTI6MzItMTkyOjQyIiwKICAgICAgICAgICAgICAgICAgIm5hbWUiIDogIntodHRwOi8vaGw3Lm9yZy9maGlyfUNsYWltU3RhdHVzIiwKICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk5hbWVkVHlwZVNwZWNpZmllciIKICAgICAgICAgICAgICAgfQogICAgICAgICAgICB9IF0KICAgICAgICAgfSwgewogICAgICAgICAgICAibG9jYXRvciIgOiAiMTkzOjEtMTkzOjY5IiwKICAgICAgICAgICAgIm5hbWUiIDogIlRvU3RyaW5nIiwKICAgICAgICAgICAgImNvbnRleHQiIDogIlBhdGllbnQiLAogICAgICAgICAgICAiYWNjZXNzTGV2ZWwiIDogIlB1YmxpYyIsCiAgICAgICAgICAgICJ0eXBlIiA6ICJGdW5jdGlvbkRlZiIsCiAgICAgICAgICAgICJleHByZXNzaW9uIiA6IHsKICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjE5Mzo1OS0xOTM6NjkiLAogICAgICAgICAgICAgICAicGF0aCIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAidHlwZSIgOiAiUHJvcGVydHkiLAogICAgICAgICAgICAgICAic291cmNlIiA6IHsKICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjE5Mzo1OS0xOTM6NjMiLAogICAgICAgICAgICAgICAgICAibmFtZSIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAgICAidHlwZSIgOiAiT3BlcmFuZFJlZiIKICAgICAgICAgICAgICAgfQogICAgICAgICAgICB9LAogICAgICAgICAgICAib3BlcmFuZCIgOiBbIHsKICAgICAgICAgICAgICAgIm5hbWUiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgIm9wZXJhbmRUeXBlU3BlY2lmaWVyIiA6IHsKICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjE5MzozMi0xOTM6NTUiLAogICAgICAgICAgICAgICAgICAibmFtZSIgOiAie2h0dHA6Ly9obDcub3JnL2ZoaXJ9Q2xpbmljYWxJbXByZXNzaW9uU3RhdHVzIiwKICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk5hbWVkVHlwZVNwZWNpZmllciIKICAgICAgICAgICAgICAgfQogICAgICAgICAgICB9IF0KICAgICAgICAgfSwgewogICAgICAgICAgICAibG9jYXRvciIgOiAiMTk0OjEtMTk0OjYyIiwKICAgICAgICAgICAgIm5hbWUiIDogIlRvU3RyaW5nIiwKICAgICAgICAgICAgImNvbnRleHQiIDogIlBhdGllbnQiLAogICAgICAgICAgICAiYWNjZXNzTGV2ZWwiIDogIlB1YmxpYyIsCiAgICAgICAgICAgICJ0eXBlIiA6ICJGdW5jdGlvbkRlZiIsCiAgICAgICAgICAgICJleHByZXNzaW9uIiA6IHsKICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjE5NDo1Mi0xOTQ6NjIiLAogICAgICAgICAgICAgICAicGF0aCIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAidHlwZSIgOiAiUHJvcGVydHkiLAogICAgICAgICAgICAgICAic291cmNlIiA6IHsKICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjE5NDo1Mi0xOTQ6NTYiLAogICAgICAgICAgICAgICAgICAibmFtZSIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAgICAidHlwZSIgOiAiT3BlcmFuZFJlZiIKICAgICAgICAgICAgICAgfQogICAgICAgICAgICB9LAogICAgICAgICAgICAib3BlcmFuZCIgOiBbIHsKICAgICAgICAgICAgICAgIm5hbWUiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgIm9wZXJhbmRUeXBlU3BlY2lmaWVyIiA6IHsKICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjE5NDozMi0xOTQ6NDgiLAogICAgICAgICAgICAgICAgICAibmFtZSIgOiAie2h0dHA6Ly9obDcub3JnL2ZoaXJ9Q29kZVNlYXJjaFN1cHBvcnQiLAogICAgICAgICAgICAgICAgICAidHlwZSIgOiAiTmFtZWRUeXBlU3BlY2lmaWVyIgogICAgICAgICAgICAgICB9CiAgICAgICAgICAgIH0gXQogICAgICAgICB9LCB7CiAgICAgICAgICAgICJsb2NhdG9yIiA6ICIxOTU6MS0xOTU6NjYiLAogICAgICAgICAgICAibmFtZSIgOiAiVG9TdHJpbmciLAogICAgICAgICAgICAiY29udGV4dCIgOiAiUGF0aWVudCIsCiAgICAgICAgICAgICJhY2Nlc3NMZXZlbCIgOiAiUHVibGljIiwKICAgICAgICAgICAgInR5cGUiIDogIkZ1bmN0aW9uRGVmIiwKICAgICAgICAgICAgImV4cHJlc3Npb24iIDogewogICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMTk1OjU2LTE5NTo2NiIsCiAgICAgICAgICAgICAgICJwYXRoIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICJ0eXBlIiA6ICJQcm9wZXJ0eSIsCiAgICAgICAgICAgICAgICJzb3VyY2UiIDogewogICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMTk1OjU2LTE5NTo2MCIsCiAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJPcGVyYW5kUmVmIgogICAgICAgICAgICAgICB9CiAgICAgICAgICAgIH0sCiAgICAgICAgICAgICJvcGVyYW5kIiA6IFsgewogICAgICAgICAgICAgICAibmFtZSIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAib3BlcmFuZFR5cGVTcGVjaWZpZXIiIDogewogICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMTk1OjMyLTE5NTo1MiIsCiAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJ7aHR0cDovL2hsNy5vcmcvZmhpcn1Db2RlU3lzdGVtQ29udGVudE1vZGUiLAogICAgICAgICAgICAgICAgICAidHlwZSIgOiAiTmFtZWRUeXBlU3BlY2lmaWVyIgogICAgICAgICAgICAgICB9CiAgICAgICAgICAgIH0gXQogICAgICAgICB9LCB7CiAgICAgICAgICAgICJsb2NhdG9yIiA6ICIxOTY6MS0xOTY6NzEiLAogICAgICAgICAgICAibmFtZSIgOiAiVG9TdHJpbmciLAogICAgICAgICAgICAiY29udGV4dCIgOiAiUGF0aWVudCIsCiAgICAgICAgICAgICJhY2Nlc3NMZXZlbCIgOiAiUHVibGljIiwKICAgICAgICAgICAgInR5cGUiIDogIkZ1bmN0aW9uRGVmIiwKICAgICAgICAgICAgImV4cHJlc3Npb24iIDogewogICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMTk2OjYxLTE5Njo3MSIsCiAgICAgICAgICAgICAgICJwYXRoIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICJ0eXBlIiA6ICJQcm9wZXJ0eSIsCiAgICAgICAgICAgICAgICJzb3VyY2UiIDogewogICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMTk2OjYxLTE5Njo2NSIsCiAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJPcGVyYW5kUmVmIgogICAgICAgICAgICAgICB9CiAgICAgICAgICAgIH0sCiAgICAgICAgICAgICJvcGVyYW5kIiA6IFsgewogICAgICAgICAgICAgICAibmFtZSIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAib3BlcmFuZFR5cGVTcGVjaWZpZXIiIDogewogICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMTk2OjMyLTE5Njo1NyIsCiAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJ7aHR0cDovL2hsNy5vcmcvZmhpcn1Db2RlU3lzdGVtSGllcmFyY2h5TWVhbmluZyIsCiAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJOYW1lZFR5cGVTcGVjaWZpZXIiCiAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgfSBdCiAgICAgICAgIH0sIHsKICAgICAgICAgICAgImxvY2F0b3IiIDogIjE5NzoxLTE5Nzo2NiIsCiAgICAgICAgICAgICJuYW1lIiA6ICJUb1N0cmluZyIsCiAgICAgICAgICAgICJjb250ZXh0IiA6ICJQYXRpZW50IiwKICAgICAgICAgICAgImFjY2Vzc0xldmVsIiA6ICJQdWJsaWMiLAogICAgICAgICAgICAidHlwZSIgOiAiRnVuY3Rpb25EZWYiLAogICAgICAgICAgICAiZXhwcmVzc2lvbiIgOiB7CiAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIxOTc6NTYtMTk3OjY2IiwKICAgICAgICAgICAgICAgInBhdGgiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgInR5cGUiIDogIlByb3BlcnR5IiwKICAgICAgICAgICAgICAgInNvdXJjZSIgOiB7CiAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIxOTc6NTYtMTk3OjYwIiwKICAgICAgICAgICAgICAgICAgIm5hbWUiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk9wZXJhbmRSZWYiCiAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgfSwKICAgICAgICAgICAgIm9wZXJhbmQiIDogWyB7CiAgICAgICAgICAgICAgICJuYW1lIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICJvcGVyYW5kVHlwZVNwZWNpZmllciIgOiB7CiAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIxOTc6MzItMTk3OjUyIiwKICAgICAgICAgICAgICAgICAgIm5hbWUiIDogIntodHRwOi8vaGw3Lm9yZy9maGlyfUNvbW11bmljYXRpb25Qcmlvcml0eSIsCiAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJOYW1lZFR5cGVTcGVjaWZpZXIiCiAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgfSBdCiAgICAgICAgIH0sIHsKICAgICAgICAgICAgImxvY2F0b3IiIDogIjE5ODoxLTE5ODo3MSIsCiAgICAgICAgICAgICJuYW1lIiA6ICJUb1N0cmluZyIsCiAgICAgICAgICAgICJjb250ZXh0IiA6ICJQYXRpZW50IiwKICAgICAgICAgICAgImFjY2Vzc0xldmVsIiA6ICJQdWJsaWMiLAogICAgICAgICAgICAidHlwZSIgOiAiRnVuY3Rpb25EZWYiLAogICAgICAgICAgICAiZXhwcmVzc2lvbiIgOiB7CiAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIxOTg6NjEtMTk4OjcxIiwKICAgICAgICAgICAgICAgInBhdGgiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgInR5cGUiIDogIlByb3BlcnR5IiwKICAgICAgICAgICAgICAgInNvdXJjZSIgOiB7CiAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIxOTg6NjEtMTk4OjY1IiwKICAgICAgICAgICAgICAgICAgIm5hbWUiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk9wZXJhbmRSZWYiCiAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgfSwKICAgICAgICAgICAgIm9wZXJhbmQiIDogWyB7CiAgICAgICAgICAgICAgICJuYW1lIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICJvcGVyYW5kVHlwZVNwZWNpZmllciIgOiB7CiAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIxOTg6MzItMTk4OjU3IiwKICAgICAgICAgICAgICAgICAgIm5hbWUiIDogIntodHRwOi8vaGw3Lm9yZy9maGlyfUNvbW11bmljYXRpb25SZXF1ZXN0U3RhdHVzIiwKICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk5hbWVkVHlwZVNwZWNpZmllciIKICAgICAgICAgICAgICAgfQogICAgICAgICAgICB9IF0KICAgICAgICAgfSwgewogICAgICAgICAgICAibG9jYXRvciIgOiAiMTk5OjEtMTk5OjY0IiwKICAgICAgICAgICAgIm5hbWUiIDogIlRvU3RyaW5nIiwKICAgICAgICAgICAgImNvbnRleHQiIDogIlBhdGllbnQiLAogICAgICAgICAgICAiYWNjZXNzTGV2ZWwiIDogIlB1YmxpYyIsCiAgICAgICAgICAgICJ0eXBlIiA6ICJGdW5jdGlvbkRlZiIsCiAgICAgICAgICAgICJleHByZXNzaW9uIiA6IHsKICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjE5OTo1NC0xOTk6NjQiLAogICAgICAgICAgICAgICAicGF0aCIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAidHlwZSIgOiAiUHJvcGVydHkiLAogICAgICAgICAgICAgICAic291cmNlIiA6IHsKICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjE5OTo1NC0xOTk6NTgiLAogICAgICAgICAgICAgICAgICAibmFtZSIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAgICAidHlwZSIgOiAiT3BlcmFuZFJlZiIKICAgICAgICAgICAgICAgfQogICAgICAgICAgICB9LAogICAgICAgICAgICAib3BlcmFuZCIgOiBbIHsKICAgICAgICAgICAgICAgIm5hbWUiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgIm9wZXJhbmRUeXBlU3BlY2lmaWVyIiA6IHsKICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjE5OTozMi0xOTk6NTAiLAogICAgICAgICAgICAgICAgICAibmFtZSIgOiAie2h0dHA6Ly9obDcub3JnL2ZoaXJ9Q29tbXVuaWNhdGlvblN0YXR1cyIsCiAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJOYW1lZFR5cGVTcGVjaWZpZXIiCiAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgfSBdCiAgICAgICAgIH0sIHsKICAgICAgICAgICAgImxvY2F0b3IiIDogIjIwMDoxLTIwMDo2MCIsCiAgICAgICAgICAgICJuYW1lIiA6ICJUb1N0cmluZyIsCiAgICAgICAgICAgICJjb250ZXh0IiA6ICJQYXRpZW50IiwKICAgICAgICAgICAgImFjY2Vzc0xldmVsIiA6ICJQdWJsaWMiLAogICAgICAgICAgICAidHlwZSIgOiAiRnVuY3Rpb25EZWYiLAogICAgICAgICAgICAiZXhwcmVzc2lvbiIgOiB7CiAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIyMDA6NTAtMjAwOjYwIiwKICAgICAgICAgICAgICAgInBhdGgiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgInR5cGUiIDogIlByb3BlcnR5IiwKICAgICAgICAgICAgICAgInNvdXJjZSIgOiB7CiAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIyMDA6NTAtMjAwOjU0IiwKICAgICAgICAgICAgICAgICAgIm5hbWUiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk9wZXJhbmRSZWYiCiAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgfSwKICAgICAgICAgICAgIm9wZXJhbmQiIDogWyB7CiAgICAgICAgICAgICAgICJuYW1lIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICJvcGVyYW5kVHlwZVNwZWNpZmllciIgOiB7CiAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIyMDA6MzItMjAwOjQ2IiwKICAgICAgICAgICAgICAgICAgIm5hbWUiIDogIntodHRwOi8vaGw3Lm9yZy9maGlyfUNvbXBhcnRtZW50Q29kZSIsCiAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJOYW1lZFR5cGVTcGVjaWZpZXIiCiAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgfSBdCiAgICAgICAgIH0sIHsKICAgICAgICAgICAgImxvY2F0b3IiIDogIjIwMToxLTIwMTo2MCIsCiAgICAgICAgICAgICJuYW1lIiA6ICJUb1N0cmluZyIsCiAgICAgICAgICAgICJjb250ZXh0IiA6ICJQYXRpZW50IiwKICAgICAgICAgICAgImFjY2Vzc0xldmVsIiA6ICJQdWJsaWMiLAogICAgICAgICAgICAidHlwZSIgOiAiRnVuY3Rpb25EZWYiLAogICAgICAgICAgICAiZXhwcmVzc2lvbiIgOiB7CiAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIyMDE6NTAtMjAxOjYwIiwKICAgICAgICAgICAgICAgInBhdGgiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgInR5cGUiIDogIlByb3BlcnR5IiwKICAgICAgICAgICAgICAgInNvdXJjZSIgOiB7CiAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIyMDE6NTAtMjAxOjU0IiwKICAgICAgICAgICAgICAgICAgIm5hbWUiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk9wZXJhbmRSZWYiCiAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgfSwKICAgICAgICAgICAgIm9wZXJhbmQiIDogWyB7CiAgICAgICAgICAgICAgICJuYW1lIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICJvcGVyYW5kVHlwZVNwZWNpZmllciIgOiB7CiAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIyMDE6MzItMjAxOjQ2IiwKICAgICAgICAgICAgICAgICAgIm5hbWUiIDogIntodHRwOi8vaGw3Lm9yZy9maGlyfUNvbXBhcnRtZW50VHlwZSIsCiAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJOYW1lZFR5cGVTcGVjaWZpZXIiCiAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgfSBdCiAgICAgICAgIH0sIHsKICAgICAgICAgICAgImxvY2F0b3IiIDogIjIwMjoxLTIwMjo3MSIsCiAgICAgICAgICAgICJuYW1lIiA6ICJUb1N0cmluZyIsCiAgICAgICAgICAgICJjb250ZXh0IiA6ICJQYXRpZW50IiwKICAgICAgICAgICAgImFjY2Vzc0xldmVsIiA6ICJQdWJsaWMiLAogICAgICAgICAgICAidHlwZSIgOiAiRnVuY3Rpb25EZWYiLAogICAgICAgICAgICAiZXhwcmVzc2lvbiIgOiB7CiAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIyMDI6NjEtMjAyOjcxIiwKICAgICAgICAgICAgICAgInBhdGgiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgInR5cGUiIDogIlByb3BlcnR5IiwKICAgICAgICAgICAgICAgInNvdXJjZSIgOiB7CiAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIyMDI6NjEtMjAyOjY1IiwKICAgICAgICAgICAgICAgICAgIm5hbWUiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk9wZXJhbmRSZWYiCiAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgfSwKICAgICAgICAgICAgIm9wZXJhbmQiIDogWyB7CiAgICAgICAgICAgICAgICJuYW1lIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICJvcGVyYW5kVHlwZVNwZWNpZmllciIgOiB7CiAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIyMDI6MzItMjAyOjU3IiwKICAgICAgICAgICAgICAgICAgIm5hbWUiIDogIntodHRwOi8vaGw3Lm9yZy9maGlyfUNvbXBvc2l0aW9uQXR0ZXN0YXRpb25Nb2RlIiwKICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk5hbWVkVHlwZVNwZWNpZmllciIKICAgICAgICAgICAgICAgfQogICAgICAgICAgICB9IF0KICAgICAgICAgfSwgewogICAgICAgICAgICAibG9jYXRvciIgOiAiMjAzOjEtMjAzOjYyIiwKICAgICAgICAgICAgIm5hbWUiIDogIlRvU3RyaW5nIiwKICAgICAgICAgICAgImNvbnRleHQiIDogIlBhdGllbnQiLAogICAgICAgICAgICAiYWNjZXNzTGV2ZWwiIDogIlB1YmxpYyIsCiAgICAgICAgICAgICJ0eXBlIiA6ICJGdW5jdGlvbkRlZiIsCiAgICAgICAgICAgICJleHByZXNzaW9uIiA6IHsKICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjIwMzo1Mi0yMDM6NjIiLAogICAgICAgICAgICAgICAicGF0aCIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAidHlwZSIgOiAiUHJvcGVydHkiLAogICAgICAgICAgICAgICAic291cmNlIiA6IHsKICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjIwMzo1Mi0yMDM6NTYiLAogICAgICAgICAgICAgICAgICAibmFtZSIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAgICAidHlwZSIgOiAiT3BlcmFuZFJlZiIKICAgICAgICAgICAgICAgfQogICAgICAgICAgICB9LAogICAgICAgICAgICAib3BlcmFuZCIgOiBbIHsKICAgICAgICAgICAgICAgIm5hbWUiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgIm9wZXJhbmRUeXBlU3BlY2lmaWVyIiA6IHsKICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjIwMzozMi0yMDM6NDgiLAogICAgICAgICAgICAgICAgICAibmFtZSIgOiAie2h0dHA6Ly9obDcub3JnL2ZoaXJ9Q29tcG9zaXRpb25TdGF0dXMiLAogICAgICAgICAgICAgICAgICAidHlwZSIgOiAiTmFtZWRUeXBlU3BlY2lmaWVyIgogICAgICAgICAgICAgICB9CiAgICAgICAgICAgIH0gXQogICAgICAgICB9LCB7CiAgICAgICAgICAgICJsb2NhdG9yIiA6ICIyMDQ6MS0yMDQ6NjYiLAogICAgICAgICAgICAibmFtZSIgOiAiVG9TdHJpbmciLAogICAgICAgICAgICAiY29udGV4dCIgOiAiUGF0aWVudCIsCiAgICAgICAgICAgICJhY2Nlc3NMZXZlbCIgOiAiUHVibGljIiwKICAgICAgICAgICAgInR5cGUiIDogIkZ1bmN0aW9uRGVmIiwKICAgICAgICAgICAgImV4cHJlc3Npb24iIDogewogICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMjA0OjU2LTIwNDo2NiIsCiAgICAgICAgICAgICAgICJwYXRoIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICJ0eXBlIiA6ICJQcm9wZXJ0eSIsCiAgICAgICAgICAgICAgICJzb3VyY2UiIDogewogICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMjA0OjU2LTIwNDo2MCIsCiAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJPcGVyYW5kUmVmIgogICAgICAgICAgICAgICB9CiAgICAgICAgICAgIH0sCiAgICAgICAgICAgICJvcGVyYW5kIiA6IFsgewogICAgICAgICAgICAgICAibmFtZSIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAib3BlcmFuZFR5cGVTcGVjaWZpZXIiIDogewogICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMjA0OjMyLTIwNDo1MiIsCiAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJ7aHR0cDovL2hsNy5vcmcvZmhpcn1Db25jZXB0TWFwRXF1aXZhbGVuY2UiLAogICAgICAgICAgICAgICAgICAidHlwZSIgOiAiTmFtZWRUeXBlU3BlY2lmaWVyIgogICAgICAgICAgICAgICB9CiAgICAgICAgICAgIH0gXQogICAgICAgICB9LCB7CiAgICAgICAgICAgICJsb2NhdG9yIiA6ICIyMDU6MS0yMDU6NzIiLAogICAgICAgICAgICAibmFtZSIgOiAiVG9TdHJpbmciLAogICAgICAgICAgICAiY29udGV4dCIgOiAiUGF0aWVudCIsCiAgICAgICAgICAgICJhY2Nlc3NMZXZlbCIgOiAiUHVibGljIiwKICAgICAgICAgICAgInR5cGUiIDogIkZ1bmN0aW9uRGVmIiwKICAgICAgICAgICAgImV4cHJlc3Npb24iIDogewogICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMjA1OjYyLTIwNTo3MiIsCiAgICAgICAgICAgICAgICJwYXRoIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICJ0eXBlIiA6ICJQcm9wZXJ0eSIsCiAgICAgICAgICAgICAgICJzb3VyY2UiIDogewogICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMjA1OjYyLTIwNTo2NiIsCiAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJPcGVyYW5kUmVmIgogICAgICAgICAgICAgICB9CiAgICAgICAgICAgIH0sCiAgICAgICAgICAgICJvcGVyYW5kIiA6IFsgewogICAgICAgICAgICAgICAibmFtZSIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAib3BlcmFuZFR5cGVTcGVjaWZpZXIiIDogewogICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMjA1OjMyLTIwNTo1OCIsCiAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJ7aHR0cDovL2hsNy5vcmcvZmhpcn1Db25jZXB0TWFwR3JvdXBVbm1hcHBlZE1vZGUiLAogICAgICAgICAgICAgICAgICAidHlwZSIgOiAiTmFtZWRUeXBlU3BlY2lmaWVyIgogICAgICAgICAgICAgICB9CiAgICAgICAgICAgIH0gXQogICAgICAgICB9LCB7CiAgICAgICAgICAgICJsb2NhdG9yIiA6ICIyMDY6MS0yMDY6NjgiLAogICAgICAgICAgICAibmFtZSIgOiAiVG9TdHJpbmciLAogICAgICAgICAgICAiY29udGV4dCIgOiAiUGF0aWVudCIsCiAgICAgICAgICAgICJhY2Nlc3NMZXZlbCIgOiAiUHVibGljIiwKICAgICAgICAgICAgInR5cGUiIDogIkZ1bmN0aW9uRGVmIiwKICAgICAgICAgICAgImV4cHJlc3Npb24iIDogewogICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMjA2OjU4LTIwNjo2OCIsCiAgICAgICAgICAgICAgICJwYXRoIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICJ0eXBlIiA6ICJQcm9wZXJ0eSIsCiAgICAgICAgICAgICAgICJzb3VyY2UiIDogewogICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMjA2OjU4LTIwNjo2MiIsCiAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJPcGVyYW5kUmVmIgogICAgICAgICAgICAgICB9CiAgICAgICAgICAgIH0sCiAgICAgICAgICAgICJvcGVyYW5kIiA6IFsgewogICAgICAgICAgICAgICAibmFtZSIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAib3BlcmFuZFR5cGVTcGVjaWZpZXIiIDogewogICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMjA2OjMyLTIwNjo1NCIsCiAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJ7aHR0cDovL2hsNy5vcmcvZmhpcn1Db25kaXRpb25hbERlbGV0ZVN0YXR1cyIsCiAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJOYW1lZFR5cGVTcGVjaWZpZXIiCiAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgfSBdCiAgICAgICAgIH0sIHsKICAgICAgICAgICAgImxvY2F0b3IiIDogIjIwNzoxLTIwNzo2NiIsCiAgICAgICAgICAgICJuYW1lIiA6ICJUb1N0cmluZyIsCiAgICAgICAgICAgICJjb250ZXh0IiA6ICJQYXRpZW50IiwKICAgICAgICAgICAgImFjY2Vzc0xldmVsIiA6ICJQdWJsaWMiLAogICAgICAgICAgICAidHlwZSIgOiAiRnVuY3Rpb25EZWYiLAogICAgICAgICAgICAiZXhwcmVzc2lvbiIgOiB7CiAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIyMDc6NTYtMjA3OjY2IiwKICAgICAgICAgICAgICAgInBhdGgiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgInR5cGUiIDogIlByb3BlcnR5IiwKICAgICAgICAgICAgICAgInNvdXJjZSIgOiB7CiAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIyMDc6NTYtMjA3OjYwIiwKICAgICAgICAgICAgICAgICAgIm5hbWUiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk9wZXJhbmRSZWYiCiAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgfSwKICAgICAgICAgICAgIm9wZXJhbmQiIDogWyB7CiAgICAgICAgICAgICAgICJuYW1lIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICJvcGVyYW5kVHlwZVNwZWNpZmllciIgOiB7CiAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIyMDc6MzItMjA3OjUyIiwKICAgICAgICAgICAgICAgICAgIm5hbWUiIDogIntodHRwOi8vaGw3Lm9yZy9maGlyfUNvbmRpdGlvbmFsUmVhZFN0YXR1cyIsCiAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJOYW1lZFR5cGVTcGVjaWZpZXIiCiAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgfSBdCiAgICAgICAgIH0sIHsKICAgICAgICAgICAgImxvY2F0b3IiIDogIjIwODoxLTIwODo2MyIsCiAgICAgICAgICAgICJuYW1lIiA6ICJUb1N0cmluZyIsCiAgICAgICAgICAgICJjb250ZXh0IiA6ICJQYXRpZW50IiwKICAgICAgICAgICAgImFjY2Vzc0xldmVsIiA6ICJQdWJsaWMiLAogICAgICAgICAgICAidHlwZSIgOiAiRnVuY3Rpb25EZWYiLAogICAgICAgICAgICAiZXhwcmVzc2lvbiIgOiB7CiAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIyMDg6NTMtMjA4OjYzIiwKICAgICAgICAgICAgICAgInBhdGgiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgInR5cGUiIDogIlByb3BlcnR5IiwKICAgICAgICAgICAgICAgInNvdXJjZSIgOiB7CiAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIyMDg6NTMtMjA4OjU3IiwKICAgICAgICAgICAgICAgICAgIm5hbWUiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk9wZXJhbmRSZWYiCiAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgfSwKICAgICAgICAgICAgIm9wZXJhbmQiIDogWyB7CiAgICAgICAgICAgICAgICJuYW1lIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICJvcGVyYW5kVHlwZVNwZWNpZmllciIgOiB7CiAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIyMDg6MzItMjA4OjQ5IiwKICAgICAgICAgICAgICAgICAgIm5hbWUiIDogIntodHRwOi8vaGw3Lm9yZy9maGlyfUNvbnNlbnREYXRhTWVhbmluZyIsCiAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJOYW1lZFR5cGVTcGVjaWZpZXIiCiAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgfSBdCiAgICAgICAgIH0sIHsKICAgICAgICAgICAgImxvY2F0b3IiIDogIjIwOToxLTIwOTo2NSIsCiAgICAgICAgICAgICJuYW1lIiA6ICJUb1N0cmluZyIsCiAgICAgICAgICAgICJjb250ZXh0IiA6ICJQYXRpZW50IiwKICAgICAgICAgICAgImFjY2Vzc0xldmVsIiA6ICJQdWJsaWMiLAogICAgICAgICAgICAidHlwZSIgOiAiRnVuY3Rpb25EZWYiLAogICAgICAgICAgICAiZXhwcmVzc2lvbiIgOiB7CiAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIyMDk6NTUtMjA5OjY1IiwKICAgICAgICAgICAgICAgInBhdGgiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgInR5cGUiIDogIlByb3BlcnR5IiwKICAgICAgICAgICAgICAgInNvdXJjZSIgOiB7CiAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIyMDk6NTUtMjA5OjU5IiwKICAgICAgICAgICAgICAgICAgIm5hbWUiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk9wZXJhbmRSZWYiCiAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgfSwKICAgICAgICAgICAgIm9wZXJhbmQiIDogWyB7CiAgICAgICAgICAgICAgICJuYW1lIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICJvcGVyYW5kVHlwZVNwZWNpZmllciIgOiB7CiAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIyMDk6MzItMjA5OjUxIiwKICAgICAgICAgICAgICAgICAgIm5hbWUiIDogIntodHRwOi8vaGw3Lm9yZy9maGlyfUNvbnNlbnRQcm92aXNpb25UeXBlIiwKICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk5hbWVkVHlwZVNwZWNpZmllciIKICAgICAgICAgICAgICAgfQogICAgICAgICAgICB9IF0KICAgICAgICAgfSwgewogICAgICAgICAgICAibG9jYXRvciIgOiAiMjEwOjEtMjEwOjU3IiwKICAgICAgICAgICAgIm5hbWUiIDogIlRvU3RyaW5nIiwKICAgICAgICAgICAgImNvbnRleHQiIDogIlBhdGllbnQiLAogICAgICAgICAgICAiYWNjZXNzTGV2ZWwiIDogIlB1YmxpYyIsCiAgICAgICAgICAgICJ0eXBlIiA6ICJGdW5jdGlvbkRlZiIsCiAgICAgICAgICAgICJleHByZXNzaW9uIiA6IHsKICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjIxMDo0Ny0yMTA6NTciLAogICAgICAgICAgICAgICAicGF0aCIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAidHlwZSIgOiAiUHJvcGVydHkiLAogICAgICAgICAgICAgICAic291cmNlIiA6IHsKICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjIxMDo0Ny0yMTA6NTEiLAogICAgICAgICAgICAgICAgICAibmFtZSIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAgICAidHlwZSIgOiAiT3BlcmFuZFJlZiIKICAgICAgICAgICAgICAgfQogICAgICAgICAgICB9LAogICAgICAgICAgICAib3BlcmFuZCIgOiBbIHsKICAgICAgICAgICAgICAgIm5hbWUiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgIm9wZXJhbmRUeXBlU3BlY2lmaWVyIiA6IHsKICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjIxMDozMi0yMTA6NDMiLAogICAgICAgICAgICAgICAgICAibmFtZSIgOiAie2h0dHA6Ly9obDcub3JnL2ZoaXJ9Q29uc2VudFN0YXRlIiwKICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk5hbWVkVHlwZVNwZWNpZmllciIKICAgICAgICAgICAgICAgfQogICAgICAgICAgICB9IF0KICAgICAgICAgfSwgewogICAgICAgICAgICAibG9jYXRvciIgOiAiMjExOjEtMjExOjYzIiwKICAgICAgICAgICAgIm5hbWUiIDogIlRvU3RyaW5nIiwKICAgICAgICAgICAgImNvbnRleHQiIDogIlBhdGllbnQiLAogICAgICAgICAgICAiYWNjZXNzTGV2ZWwiIDogIlB1YmxpYyIsCiAgICAgICAgICAgICJ0eXBlIiA6ICJGdW5jdGlvbkRlZiIsCiAgICAgICAgICAgICJleHByZXNzaW9uIiA6IHsKICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjIxMTo1My0yMTE6NjMiLAogICAgICAgICAgICAgICAicGF0aCIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAidHlwZSIgOiAiUHJvcGVydHkiLAogICAgICAgICAgICAgICAic291cmNlIiA6IHsKICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjIxMTo1My0yMTE6NTciLAogICAgICAgICAgICAgICAgICAibmFtZSIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAgICAidHlwZSIgOiAiT3BlcmFuZFJlZiIKICAgICAgICAgICAgICAgfQogICAgICAgICAgICB9LAogICAgICAgICAgICAib3BlcmFuZCIgOiBbIHsKICAgICAgICAgICAgICAgIm5hbWUiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgIm9wZXJhbmRUeXBlU3BlY2lmaWVyIiA6IHsKICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjIxMTozMi0yMTE6NDkiLAogICAgICAgICAgICAgICAgICAibmFtZSIgOiAie2h0dHA6Ly9obDcub3JnL2ZoaXJ9Q29uc3RyYWludFNldmVyaXR5IiwKICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk5hbWVkVHlwZVNwZWNpZmllciIKICAgICAgICAgICAgICAgfQogICAgICAgICAgICB9IF0KICAgICAgICAgfSwgewogICAgICAgICAgICAibG9jYXRvciIgOiAiMjEyOjEtMjEyOjYzIiwKICAgICAgICAgICAgIm5hbWUiIDogIlRvU3RyaW5nIiwKICAgICAgICAgICAgImNvbnRleHQiIDogIlBhdGllbnQiLAogICAgICAgICAgICAiYWNjZXNzTGV2ZWwiIDogIlB1YmxpYyIsCiAgICAgICAgICAgICJ0eXBlIiA6ICJGdW5jdGlvbkRlZiIsCiAgICAgICAgICAgICJleHByZXNzaW9uIiA6IHsKICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjIxMjo1My0yMTI6NjMiLAogICAgICAgICAgICAgICAicGF0aCIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAidHlwZSIgOiAiUHJvcGVydHkiLAogICAgICAgICAgICAgICAic291cmNlIiA6IHsKICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjIxMjo1My0yMTI6NTciLAogICAgICAgICAgICAgICAgICAibmFtZSIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAgICAidHlwZSIgOiAiT3BlcmFuZFJlZiIKICAgICAgICAgICAgICAgfQogICAgICAgICAgICB9LAogICAgICAgICAgICAib3BlcmFuZCIgOiBbIHsKICAgICAgICAgICAgICAgIm5hbWUiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgIm9wZXJhbmRUeXBlU3BlY2lmaWVyIiA6IHsKICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjIxMjozMi0yMTI6NDkiLAogICAgICAgICAgICAgICAgICAibmFtZSIgOiAie2h0dHA6Ly9obDcub3JnL2ZoaXJ9Q29udGFjdFBvaW50U3lzdGVtIiwKICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk5hbWVkVHlwZVNwZWNpZmllciIKICAgICAgICAgICAgICAgfQogICAgICAgICAgICB9IF0KICAgICAgICAgfSwgewogICAgICAgICAgICAibG9jYXRvciIgOiAiMjEzOjEtMjEzOjYwIiwKICAgICAgICAgICAgIm5hbWUiIDogIlRvU3RyaW5nIiwKICAgICAgICAgICAgImNvbnRleHQiIDogIlBhdGllbnQiLAogICAgICAgICAgICAiYWNjZXNzTGV2ZWwiIDogIlB1YmxpYyIsCiAgICAgICAgICAgICJ0eXBlIiA6ICJGdW5jdGlvbkRlZiIsCiAgICAgICAgICAgICJleHByZXNzaW9uIiA6IHsKICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjIxMzo1MC0yMTM6NjAiLAogICAgICAgICAgICAgICAicGF0aCIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAidHlwZSIgOiAiUHJvcGVydHkiLAogICAgICAgICAgICAgICAic291cmNlIiA6IHsKICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjIxMzo1MC0yMTM6NTQiLAogICAgICAgICAgICAgICAgICAibmFtZSIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAgICAidHlwZSIgOiAiT3BlcmFuZFJlZiIKICAgICAgICAgICAgICAgfQogICAgICAgICAgICB9LAogICAgICAgICAgICAib3BlcmFuZCIgOiBbIHsKICAgICAgICAgICAgICAgIm5hbWUiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgIm9wZXJhbmRUeXBlU3BlY2lmaWVyIiA6IHsKICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjIxMzozMi0yMTM6NDYiLAogICAgICAgICAgICAgICAgICAibmFtZSIgOiAie2h0dHA6Ly9obDcub3JnL2ZoaXJ9Q29udGFjdFBvaW50VXNlIiwKICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk5hbWVkVHlwZVNwZWNpZmllciIKICAgICAgICAgICAgICAgfQogICAgICAgICAgICB9IF0KICAgICAgICAgfSwgewogICAgICAgICAgICAibG9jYXRvciIgOiAiMjE0OjEtMjE0OjcwIiwKICAgICAgICAgICAgIm5hbWUiIDogIlRvU3RyaW5nIiwKICAgICAgICAgICAgImNvbnRleHQiIDogIlBhdGllbnQiLAogICAgICAgICAgICAiYWNjZXNzTGV2ZWwiIDogIlB1YmxpYyIsCiAgICAgICAgICAgICJ0eXBlIiA6ICJGdW5jdGlvbkRlZiIsCiAgICAgICAgICAgICJleHByZXNzaW9uIiA6IHsKICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjIxNDo2MC0yMTQ6NzAiLAogICAgICAgICAgICAgICAicGF0aCIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAidHlwZSIgOiAiUHJvcGVydHkiLAogICAgICAgICAgICAgICAic291cmNlIiA6IHsKICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjIxNDo2MC0yMTQ6NjQiLAogICAgICAgICAgICAgICAgICAibmFtZSIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAgICAidHlwZSIgOiAiT3BlcmFuZFJlZiIKICAgICAgICAgICAgICAgfQogICAgICAgICAgICB9LAogICAgICAgICAgICAib3BlcmFuZCIgOiBbIHsKICAgICAgICAgICAgICAgIm5hbWUiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgIm9wZXJhbmRUeXBlU3BlY2lmaWVyIiA6IHsKICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjIxNDozMi0yMTQ6NTYiLAogICAgICAgICAgICAgICAgICAibmFtZSIgOiAie2h0dHA6Ly9obDcub3JnL2ZoaXJ9Q29udHJhY3RQdWJsaWNhdGlvblN0YXR1cyIsCiAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJOYW1lZFR5cGVTcGVjaWZpZXIiCiAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgfSBdCiAgICAgICAgIH0sIHsKICAgICAgICAgICAgImxvY2F0b3IiIDogIjIxNToxLTIxNTo1OSIsCiAgICAgICAgICAgICJuYW1lIiA6ICJUb1N0cmluZyIsCiAgICAgICAgICAgICJjb250ZXh0IiA6ICJQYXRpZW50IiwKICAgICAgICAgICAgImFjY2Vzc0xldmVsIiA6ICJQdWJsaWMiLAogICAgICAgICAgICAidHlwZSIgOiAiRnVuY3Rpb25EZWYiLAogICAgICAgICAgICAiZXhwcmVzc2lvbiIgOiB7CiAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIyMTU6NDktMjE1OjU5IiwKICAgICAgICAgICAgICAgInBhdGgiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgInR5cGUiIDogIlByb3BlcnR5IiwKICAgICAgICAgICAgICAgInNvdXJjZSIgOiB7CiAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIyMTU6NDktMjE1OjUzIiwKICAgICAgICAgICAgICAgICAgIm5hbWUiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk9wZXJhbmRSZWYiCiAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgfSwKICAgICAgICAgICAgIm9wZXJhbmQiIDogWyB7CiAgICAgICAgICAgICAgICJuYW1lIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICJvcGVyYW5kVHlwZVNwZWNpZmllciIgOiB7CiAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIyMTU6MzItMjE1OjQ1IiwKICAgICAgICAgICAgICAgICAgIm5hbWUiIDogIntodHRwOi8vaGw3Lm9yZy9maGlyfUNvbnRyYWN0U3RhdHVzIiwKICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk5hbWVkVHlwZVNwZWNpZmllciIKICAgICAgICAgICAgICAgfQogICAgICAgICAgICB9IF0KICAgICAgICAgfSwgewogICAgICAgICAgICAibG9jYXRvciIgOiAiMjE2OjEtMjE2OjYwIiwKICAgICAgICAgICAgIm5hbWUiIDogIlRvU3RyaW5nIiwKICAgICAgICAgICAgImNvbnRleHQiIDogIlBhdGllbnQiLAogICAgICAgICAgICAiYWNjZXNzTGV2ZWwiIDogIlB1YmxpYyIsCiAgICAgICAgICAgICJ0eXBlIiA6ICJGdW5jdGlvbkRlZiIsCiAgICAgICAgICAgICJleHByZXNzaW9uIiA6IHsKICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjIxNjo1MC0yMTY6NjAiLAogICAgICAgICAgICAgICAicGF0aCIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAidHlwZSIgOiAiUHJvcGVydHkiLAogICAgICAgICAgICAgICAic291cmNlIiA6IHsKICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjIxNjo1MC0yMTY6NTQiLAogICAgICAgICAgICAgICAgICAibmFtZSIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAgICAidHlwZSIgOiAiT3BlcmFuZFJlZiIKICAgICAgICAgICAgICAgfQogICAgICAgICAgICB9LAogICAgICAgICAgICAib3BlcmFuZCIgOiBbIHsKICAgICAgICAgICAgICAgIm5hbWUiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgIm9wZXJhbmRUeXBlU3BlY2lmaWVyIiA6IHsKICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjIxNjozMi0yMTY6NDYiLAogICAgICAgICAgICAgICAgICAibmFtZSIgOiAie2h0dHA6Ly9obDcub3JnL2ZoaXJ9Q29udHJpYnV0b3JUeXBlIiwKICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk5hbWVkVHlwZVNwZWNpZmllciIKICAgICAgICAgICAgICAgfQogICAgICAgICAgICB9IF0KICAgICAgICAgfSwgewogICAgICAgICAgICAibG9jYXRvciIgOiAiMjE3OjEtMjE3OjU5IiwKICAgICAgICAgICAgIm5hbWUiIDogIlRvU3RyaW5nIiwKICAgICAgICAgICAgImNvbnRleHQiIDogIlBhdGllbnQiLAogICAgICAgICAgICAiYWNjZXNzTGV2ZWwiIDogIlB1YmxpYyIsCiAgICAgICAgICAgICJ0eXBlIiA6ICJGdW5jdGlvbkRlZiIsCiAgICAgICAgICAgICJleHByZXNzaW9uIiA6IHsKICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjIxNzo0OS0yMTc6NTkiLAogICAgICAgICAgICAgICAicGF0aCIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAidHlwZSIgOiAiUHJvcGVydHkiLAogICAgICAgICAgICAgICAic291cmNlIiA6IHsKICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjIxNzo0OS0yMTc6NTMiLAogICAgICAgICAgICAgICAgICAibmFtZSIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAgICAidHlwZSIgOiAiT3BlcmFuZFJlZiIKICAgICAgICAgICAgICAgfQogICAgICAgICAgICB9LAogICAgICAgICAgICAib3BlcmFuZCIgOiBbIHsKICAgICAgICAgICAgICAgIm5hbWUiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgIm9wZXJhbmRUeXBlU3BlY2lmaWVyIiA6IHsKICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjIxNzozMi0yMTc6NDUiLAogICAgICAgICAgICAgICAgICAibmFtZSIgOiAie2h0dHA6Ly9obDcub3JnL2ZoaXJ9Q292ZXJhZ2VTdGF0dXMiLAogICAgICAgICAgICAgICAgICAidHlwZSIgOiAiTmFtZWRUeXBlU3BlY2lmaWVyIgogICAgICAgICAgICAgICB9CiAgICAgICAgICAgIH0gXQogICAgICAgICB9LCB7CiAgICAgICAgICAgICJsb2NhdG9yIiA6ICIyMTg6MS0yMTg6NTciLAogICAgICAgICAgICAibmFtZSIgOiAiVG9TdHJpbmciLAogICAgICAgICAgICAiY29udGV4dCIgOiAiUGF0aWVudCIsCiAgICAgICAgICAgICJhY2Nlc3NMZXZlbCIgOiAiUHVibGljIiwKICAgICAgICAgICAgInR5cGUiIDogIkZ1bmN0aW9uRGVmIiwKICAgICAgICAgICAgImV4cHJlc3Npb24iIDogewogICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMjE4OjQ3LTIxODo1NyIsCiAgICAgICAgICAgICAgICJwYXRoIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICJ0eXBlIiA6ICJQcm9wZXJ0eSIsCiAgICAgICAgICAgICAgICJzb3VyY2UiIDogewogICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMjE4OjQ3LTIxODo1MSIsCiAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJPcGVyYW5kUmVmIgogICAgICAgICAgICAgICB9CiAgICAgICAgICAgIH0sCiAgICAgICAgICAgICJvcGVyYW5kIiA6IFsgewogICAgICAgICAgICAgICAibmFtZSIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAib3BlcmFuZFR5cGVTcGVjaWZpZXIiIDogewogICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMjE4OjMyLTIxODo0MyIsCiAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJ7aHR0cDovL2hsNy5vcmcvZmhpcn1DdXJyZW5jeUNvZGUiLAogICAgICAgICAgICAgICAgICAidHlwZSIgOiAiTmFtZWRUeXBlU3BlY2lmaWVyIgogICAgICAgICAgICAgICB9CiAgICAgICAgICAgIH0gXQogICAgICAgICB9LCB7CiAgICAgICAgICAgICJsb2NhdG9yIiA6ICIyMTk6MS0yMTk6NTQiLAogICAgICAgICAgICAibmFtZSIgOiAiVG9TdHJpbmciLAogICAgICAgICAgICAiY29udGV4dCIgOiAiUGF0aWVudCIsCiAgICAgICAgICAgICJhY2Nlc3NMZXZlbCIgOiAiUHVibGljIiwKICAgICAgICAgICAgInR5cGUiIDogIkZ1bmN0aW9uRGVmIiwKICAgICAgICAgICAgImV4cHJlc3Npb24iIDogewogICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMjE5OjQ0LTIxOTo1NCIsCiAgICAgICAgICAgICAgICJwYXRoIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICJ0eXBlIiA6ICJQcm9wZXJ0eSIsCiAgICAgICAgICAgICAgICJzb3VyY2UiIDogewogICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMjE5OjQ0LTIxOTo0OCIsCiAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJPcGVyYW5kUmVmIgogICAgICAgICAgICAgICB9CiAgICAgICAgICAgIH0sCiAgICAgICAgICAgICJvcGVyYW5kIiA6IFsgewogICAgICAgICAgICAgICAibmFtZSIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAib3BlcmFuZFR5cGVTcGVjaWZpZXIiIDogewogICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMjE5OjMyLTIxOTo0MCIsCiAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJ7aHR0cDovL2hsNy5vcmcvZmhpcn1EYXlPZldlZWsiLAogICAgICAgICAgICAgICAgICAidHlwZSIgOiAiTmFtZWRUeXBlU3BlY2lmaWVyIgogICAgICAgICAgICAgICB9CiAgICAgICAgICAgIH0gXQogICAgICAgICB9LCB7CiAgICAgICAgICAgICJsb2NhdG9yIiA6ICIyMjA6MS0yMjA6NTUiLAogICAgICAgICAgICAibmFtZSIgOiAiVG9TdHJpbmciLAogICAgICAgICAgICAiY29udGV4dCIgOiAiUGF0aWVudCIsCiAgICAgICAgICAgICJhY2Nlc3NMZXZlbCIgOiAiUHVibGljIiwKICAgICAgICAgICAgInR5cGUiIDogIkZ1bmN0aW9uRGVmIiwKICAgICAgICAgICAgImV4cHJlc3Npb24iIDogewogICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMjIwOjQ1LTIyMDo1NSIsCiAgICAgICAgICAgICAgICJwYXRoIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICJ0eXBlIiA6ICJQcm9wZXJ0eSIsCiAgICAgICAgICAgICAgICJzb3VyY2UiIDogewogICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMjIwOjQ1LTIyMDo0OSIsCiAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJPcGVyYW5kUmVmIgogICAgICAgICAgICAgICB9CiAgICAgICAgICAgIH0sCiAgICAgICAgICAgICJvcGVyYW5kIiA6IFsgewogICAgICAgICAgICAgICAibmFtZSIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAib3BlcmFuZFR5cGVTcGVjaWZpZXIiIDogewogICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMjIwOjMyLTIyMDo0MSIsCiAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJ7aHR0cDovL2hsNy5vcmcvZmhpcn1EYXlzT2ZXZWVrIiwKICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk5hbWVkVHlwZVNwZWNpZmllciIKICAgICAgICAgICAgICAgfQogICAgICAgICAgICB9IF0KICAgICAgICAgfSwgewogICAgICAgICAgICAibG9jYXRvciIgOiAiMjIxOjEtMjIxOjY2IiwKICAgICAgICAgICAgIm5hbWUiIDogIlRvU3RyaW5nIiwKICAgICAgICAgICAgImNvbnRleHQiIDogIlBhdGllbnQiLAogICAgICAgICAgICAiYWNjZXNzTGV2ZWwiIDogIlB1YmxpYyIsCiAgICAgICAgICAgICJ0eXBlIiA6ICJGdW5jdGlvbkRlZiIsCiAgICAgICAgICAgICJleHByZXNzaW9uIiA6IHsKICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjIyMTo1Ni0yMjE6NjYiLAogICAgICAgICAgICAgICAicGF0aCIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAidHlwZSIgOiAiUHJvcGVydHkiLAogICAgICAgICAgICAgICAic291cmNlIiA6IHsKICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjIyMTo1Ni0yMjE6NjAiLAogICAgICAgICAgICAgICAgICAibmFtZSIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAgICAidHlwZSIgOiAiT3BlcmFuZFJlZiIKICAgICAgICAgICAgICAgfQogICAgICAgICAgICB9LAogICAgICAgICAgICAib3BlcmFuZCIgOiBbIHsKICAgICAgICAgICAgICAgIm5hbWUiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgIm9wZXJhbmRUeXBlU3BlY2lmaWVyIiA6IHsKICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjIyMTozMi0yMjE6NTIiLAogICAgICAgICAgICAgICAgICAibmFtZSIgOiAie2h0dHA6Ly9obDcub3JnL2ZoaXJ9RGV0ZWN0ZWRJc3N1ZVNldmVyaXR5IiwKICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk5hbWVkVHlwZVNwZWNpZmllciIKICAgICAgICAgICAgICAgfQogICAgICAgICAgICB9IF0KICAgICAgICAgfSwgewogICAgICAgICAgICAibG9jYXRvciIgOiAiMjIyOjEtMjIyOjY0IiwKICAgICAgICAgICAgIm5hbWUiIDogIlRvU3RyaW5nIiwKICAgICAgICAgICAgImNvbnRleHQiIDogIlBhdGllbnQiLAogICAgICAgICAgICAiYWNjZXNzTGV2ZWwiIDogIlB1YmxpYyIsCiAgICAgICAgICAgICJ0eXBlIiA6ICJGdW5jdGlvbkRlZiIsCiAgICAgICAgICAgICJleHByZXNzaW9uIiA6IHsKICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjIyMjo1NC0yMjI6NjQiLAogICAgICAgICAgICAgICAicGF0aCIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAidHlwZSIgOiAiUHJvcGVydHkiLAogICAgICAgICAgICAgICAic291cmNlIiA6IHsKICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjIyMjo1NC0yMjI6NTgiLAogICAgICAgICAgICAgICAgICAibmFtZSIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAgICAidHlwZSIgOiAiT3BlcmFuZFJlZiIKICAgICAgICAgICAgICAgfQogICAgICAgICAgICB9LAogICAgICAgICAgICAib3BlcmFuZCIgOiBbIHsKICAgICAgICAgICAgICAgIm5hbWUiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgIm9wZXJhbmRUeXBlU3BlY2lmaWVyIiA6IHsKICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjIyMjozMi0yMjI6NTAiLAogICAgICAgICAgICAgICAgICAibmFtZSIgOiAie2h0dHA6Ly9obDcub3JnL2ZoaXJ9RGV0ZWN0ZWRJc3N1ZVN0YXR1cyIsCiAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJOYW1lZFR5cGVTcGVjaWZpZXIiCiAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgfSBdCiAgICAgICAgIH0sIHsKICAgICAgICAgICAgImxvY2F0b3IiIDogIjIyMzoxLTIyMzo3MyIsCiAgICAgICAgICAgICJuYW1lIiA6ICJUb1N0cmluZyIsCiAgICAgICAgICAgICJjb250ZXh0IiA6ICJQYXRpZW50IiwKICAgICAgICAgICAgImFjY2Vzc0xldmVsIiA6ICJQdWJsaWMiLAogICAgICAgICAgICAidHlwZSIgOiAiRnVuY3Rpb25EZWYiLAogICAgICAgICAgICAiZXhwcmVzc2lvbiIgOiB7CiAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIyMjM6NjMtMjIzOjczIiwKICAgICAgICAgICAgICAgInBhdGgiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgInR5cGUiIDogIlByb3BlcnR5IiwKICAgICAgICAgICAgICAgInNvdXJjZSIgOiB7CiAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIyMjM6NjMtMjIzOjY3IiwKICAgICAgICAgICAgICAgICAgIm5hbWUiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk9wZXJhbmRSZWYiCiAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgfSwKICAgICAgICAgICAgIm9wZXJhbmQiIDogWyB7CiAgICAgICAgICAgICAgICJuYW1lIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICJvcGVyYW5kVHlwZVNwZWNpZmllciIgOiB7CiAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIyMjM6MzItMjIzOjU5IiwKICAgICAgICAgICAgICAgICAgIm5hbWUiIDogIntodHRwOi8vaGw3Lm9yZy9maGlyfURldmljZU1ldHJpY0NhbGlicmF0aW9uU3RhdGUiLAogICAgICAgICAgICAgICAgICAidHlwZSIgOiAiTmFtZWRUeXBlU3BlY2lmaWVyIgogICAgICAgICAgICAgICB9CiAgICAgICAgICAgIH0gXQogICAgICAgICB9LCB7CiAgICAgICAgICAgICJsb2NhdG9yIiA6ICIyMjQ6MS0yMjQ6NzIiLAogICAgICAgICAgICAibmFtZSIgOiAiVG9TdHJpbmciLAogICAgICAgICAgICAiY29udGV4dCIgOiAiUGF0aWVudCIsCiAgICAgICAgICAgICJhY2Nlc3NMZXZlbCIgOiAiUHVibGljIiwKICAgICAgICAgICAgInR5cGUiIDogIkZ1bmN0aW9uRGVmIiwKICAgICAgICAgICAgImV4cHJlc3Npb24iIDogewogICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMjI0OjYyLTIyNDo3MiIsCiAgICAgICAgICAgICAgICJwYXRoIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICJ0eXBlIiA6ICJQcm9wZXJ0eSIsCiAgICAgICAgICAgICAgICJzb3VyY2UiIDogewogICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMjI0OjYyLTIyNDo2NiIsCiAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJPcGVyYW5kUmVmIgogICAgICAgICAgICAgICB9CiAgICAgICAgICAgIH0sCiAgICAgICAgICAgICJvcGVyYW5kIiA6IFsgewogICAgICAgICAgICAgICAibmFtZSIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAib3BlcmFuZFR5cGVTcGVjaWZpZXIiIDogewogICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMjI0OjMyLTIyNDo1OCIsCiAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJ7aHR0cDovL2hsNy5vcmcvZmhpcn1EZXZpY2VNZXRyaWNDYWxpYnJhdGlvblR5cGUiLAogICAgICAgICAgICAgICAgICAidHlwZSIgOiAiTmFtZWRUeXBlU3BlY2lmaWVyIgogICAgICAgICAgICAgICB9CiAgICAgICAgICAgIH0gXQogICAgICAgICB9LCB7CiAgICAgICAgICAgICJsb2NhdG9yIiA6ICIyMjU6MS0yMjU6NjUiLAogICAgICAgICAgICAibmFtZSIgOiAiVG9TdHJpbmciLAogICAgICAgICAgICAiY29udGV4dCIgOiAiUGF0aWVudCIsCiAgICAgICAgICAgICJhY2Nlc3NMZXZlbCIgOiAiUHVibGljIiwKICAgICAgICAgICAgInR5cGUiIDogIkZ1bmN0aW9uRGVmIiwKICAgICAgICAgICAgImV4cHJlc3Npb24iIDogewogICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMjI1OjU1LTIyNTo2NSIsCiAgICAgICAgICAgICAgICJwYXRoIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICJ0eXBlIiA6ICJQcm9wZXJ0eSIsCiAgICAgICAgICAgICAgICJzb3VyY2UiIDogewogICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMjI1OjU1LTIyNTo1OSIsCiAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJPcGVyYW5kUmVmIgogICAgICAgICAgICAgICB9CiAgICAgICAgICAgIH0sCiAgICAgICAgICAgICJvcGVyYW5kIiA6IFsgewogICAgICAgICAgICAgICAibmFtZSIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAib3BlcmFuZFR5cGVTcGVjaWZpZXIiIDogewogICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMjI1OjMyLTIyNTo1MSIsCiAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJ7aHR0cDovL2hsNy5vcmcvZmhpcn1EZXZpY2VNZXRyaWNDYXRlZ29yeSIsCiAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJOYW1lZFR5cGVTcGVjaWZpZXIiCiAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgfSBdCiAgICAgICAgIH0sIHsKICAgICAgICAgICAgImxvY2F0b3IiIDogIjIyNjoxLTIyNjo2MiIsCiAgICAgICAgICAgICJuYW1lIiA6ICJUb1N0cmluZyIsCiAgICAgICAgICAgICJjb250ZXh0IiA6ICJQYXRpZW50IiwKICAgICAgICAgICAgImFjY2Vzc0xldmVsIiA6ICJQdWJsaWMiLAogICAgICAgICAgICAidHlwZSIgOiAiRnVuY3Rpb25EZWYiLAogICAgICAgICAgICAiZXhwcmVzc2lvbiIgOiB7CiAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIyMjY6NTItMjI2OjYyIiwKICAgICAgICAgICAgICAgInBhdGgiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgInR5cGUiIDogIlByb3BlcnR5IiwKICAgICAgICAgICAgICAgInNvdXJjZSIgOiB7CiAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIyMjY6NTItMjI2OjU2IiwKICAgICAgICAgICAgICAgICAgIm5hbWUiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk9wZXJhbmRSZWYiCiAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgfSwKICAgICAgICAgICAgIm9wZXJhbmQiIDogWyB7CiAgICAgICAgICAgICAgICJuYW1lIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICJvcGVyYW5kVHlwZVNwZWNpZmllciIgOiB7CiAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIyMjY6MzItMjI2OjQ4IiwKICAgICAgICAgICAgICAgICAgIm5hbWUiIDogIntodHRwOi8vaGw3Lm9yZy9maGlyfURldmljZU1ldHJpY0NvbG9yIiwKICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk5hbWVkVHlwZVNwZWNpZmllciIKICAgICAgICAgICAgICAgfQogICAgICAgICAgICB9IF0KICAgICAgICAgfSwgewogICAgICAgICAgICAibG9jYXRvciIgOiAiMjI3OjEtMjI3Ojc0IiwKICAgICAgICAgICAgIm5hbWUiIDogIlRvU3RyaW5nIiwKICAgICAgICAgICAgImNvbnRleHQiIDogIlBhdGllbnQiLAogICAgICAgICAgICAiYWNjZXNzTGV2ZWwiIDogIlB1YmxpYyIsCiAgICAgICAgICAgICJ0eXBlIiA6ICJGdW5jdGlvbkRlZiIsCiAgICAgICAgICAgICJleHByZXNzaW9uIiA6IHsKICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjIyNzo2NC0yMjc6NzQiLAogICAgICAgICAgICAgICAicGF0aCIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAidHlwZSIgOiAiUHJvcGVydHkiLAogICAgICAgICAgICAgICAic291cmNlIiA6IHsKICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjIyNzo2NC0yMjc6NjgiLAogICAgICAgICAgICAgICAgICAibmFtZSIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAgICAidHlwZSIgOiAiT3BlcmFuZFJlZiIKICAgICAgICAgICAgICAgfQogICAgICAgICAgICB9LAogICAgICAgICAgICAib3BlcmFuZCIgOiBbIHsKICAgICAgICAgICAgICAgIm5hbWUiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgIm9wZXJhbmRUeXBlU3BlY2lmaWVyIiA6IHsKICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjIyNzozMi0yMjc6NjAiLAogICAgICAgICAgICAgICAgICAibmFtZSIgOiAie2h0dHA6Ly9obDcub3JnL2ZoaXJ9RGV2aWNlTWV0cmljT3BlcmF0aW9uYWxTdGF0dXMiLAogICAgICAgICAgICAgICAgICAidHlwZSIgOiAiTmFtZWRUeXBlU3BlY2lmaWVyIgogICAgICAgICAgICAgICB9CiAgICAgICAgICAgIH0gXQogICAgICAgICB9LCB7CiAgICAgICAgICAgICJsb2NhdG9yIiA6ICIyMjg6MS0yMjg6NTkiLAogICAgICAgICAgICAibmFtZSIgOiAiVG9TdHJpbmciLAogICAgICAgICAgICAiY29udGV4dCIgOiAiUGF0aWVudCIsCiAgICAgICAgICAgICJhY2Nlc3NMZXZlbCIgOiAiUHVibGljIiwKICAgICAgICAgICAgInR5cGUiIDogIkZ1bmN0aW9uRGVmIiwKICAgICAgICAgICAgImV4cHJlc3Npb24iIDogewogICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMjI4OjQ5LTIyODo1OSIsCiAgICAgICAgICAgICAgICJwYXRoIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICJ0eXBlIiA6ICJQcm9wZXJ0eSIsCiAgICAgICAgICAgICAgICJzb3VyY2UiIDogewogICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMjI4OjQ5LTIyODo1MyIsCiAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJPcGVyYW5kUmVmIgogICAgICAgICAgICAgICB9CiAgICAgICAgICAgIH0sCiAgICAgICAgICAgICJvcGVyYW5kIiA6IFsgewogICAgICAgICAgICAgICAibmFtZSIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAib3BlcmFuZFR5cGVTcGVjaWZpZXIiIDogewogICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMjI4OjMyLTIyODo0NSIsCiAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJ7aHR0cDovL2hsNy5vcmcvZmhpcn1EZXZpY2VOYW1lVHlwZSIsCiAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJOYW1lZFR5cGVTcGVjaWZpZXIiCiAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgfSBdCiAgICAgICAgIH0sIHsKICAgICAgICAgICAgImxvY2F0b3IiIDogIjIyOToxLTIyOTo2NCIsCiAgICAgICAgICAgICJuYW1lIiA6ICJUb1N0cmluZyIsCiAgICAgICAgICAgICJjb250ZXh0IiA6ICJQYXRpZW50IiwKICAgICAgICAgICAgImFjY2Vzc0xldmVsIiA6ICJQdWJsaWMiLAogICAgICAgICAgICAidHlwZSIgOiAiRnVuY3Rpb25EZWYiLAogICAgICAgICAgICAiZXhwcmVzc2lvbiIgOiB7CiAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIyMjk6NTQtMjI5OjY0IiwKICAgICAgICAgICAgICAgInBhdGgiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgInR5cGUiIDogIlByb3BlcnR5IiwKICAgICAgICAgICAgICAgInNvdXJjZSIgOiB7CiAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIyMjk6NTQtMjI5OjU4IiwKICAgICAgICAgICAgICAgICAgIm5hbWUiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk9wZXJhbmRSZWYiCiAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgfSwKICAgICAgICAgICAgIm9wZXJhbmQiIDogWyB7CiAgICAgICAgICAgICAgICJuYW1lIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICJvcGVyYW5kVHlwZVNwZWNpZmllciIgOiB7CiAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIyMjk6MzItMjI5OjUwIiwKICAgICAgICAgICAgICAgICAgIm5hbWUiIDogIntodHRwOi8vaGw3Lm9yZy9maGlyfURldmljZVJlcXVlc3RTdGF0dXMiLAogICAgICAgICAgICAgICAgICAidHlwZSIgOiAiTmFtZWRUeXBlU3BlY2lmaWVyIgogICAgICAgICAgICAgICB9CiAgICAgICAgICAgIH0gXQogICAgICAgICB9LCB7CiAgICAgICAgICAgICJsb2NhdG9yIiA6ICIyMzA6MS0yMzA6NjkiLAogICAgICAgICAgICAibmFtZSIgOiAiVG9TdHJpbmciLAogICAgICAgICAgICAiY29udGV4dCIgOiAiUGF0aWVudCIsCiAgICAgICAgICAgICJhY2Nlc3NMZXZlbCIgOiAiUHVibGljIiwKICAgICAgICAgICAgInR5cGUiIDogIkZ1bmN0aW9uRGVmIiwKICAgICAgICAgICAgImV4cHJlc3Npb24iIDogewogICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMjMwOjU5LTIzMDo2OSIsCiAgICAgICAgICAgICAgICJwYXRoIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICJ0eXBlIiA6ICJQcm9wZXJ0eSIsCiAgICAgICAgICAgICAgICJzb3VyY2UiIDogewogICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMjMwOjU5LTIzMDo2MyIsCiAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJPcGVyYW5kUmVmIgogICAgICAgICAgICAgICB9CiAgICAgICAgICAgIH0sCiAgICAgICAgICAgICJvcGVyYW5kIiA6IFsgewogICAgICAgICAgICAgICAibmFtZSIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAib3BlcmFuZFR5cGVTcGVjaWZpZXIiIDogewogICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMjMwOjMyLTIzMDo1NSIsCiAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJ7aHR0cDovL2hsNy5vcmcvZmhpcn1EZXZpY2VVc2VTdGF0ZW1lbnRTdGF0dXMiLAogICAgICAgICAgICAgICAgICAidHlwZSIgOiAiTmFtZWRUeXBlU3BlY2lmaWVyIgogICAgICAgICAgICAgICB9CiAgICAgICAgICAgIH0gXQogICAgICAgICB9LCB7CiAgICAgICAgICAgICJsb2NhdG9yIiA6ICIyMzE6MS0yMzE6NjciLAogICAgICAgICAgICAibmFtZSIgOiAiVG9TdHJpbmciLAogICAgICAgICAgICAiY29udGV4dCIgOiAiUGF0aWVudCIsCiAgICAgICAgICAgICJhY2Nlc3NMZXZlbCIgOiAiUHVibGljIiwKICAgICAgICAgICAgInR5cGUiIDogIkZ1bmN0aW9uRGVmIiwKICAgICAgICAgICAgImV4cHJlc3Npb24iIDogewogICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMjMxOjU3LTIzMTo2NyIsCiAgICAgICAgICAgICAgICJwYXRoIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICJ0eXBlIiA6ICJQcm9wZXJ0eSIsCiAgICAgICAgICAgICAgICJzb3VyY2UiIDogewogICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMjMxOjU3LTIzMTo2MSIsCiAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJPcGVyYW5kUmVmIgogICAgICAgICAgICAgICB9CiAgICAgICAgICAgIH0sCiAgICAgICAgICAgICJvcGVyYW5kIiA6IFsgewogICAgICAgICAgICAgICAibmFtZSIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAib3BlcmFuZFR5cGVTcGVjaWZpZXIiIDogewogICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMjMxOjMyLTIzMTo1MyIsCiAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJ7aHR0cDovL2hsNy5vcmcvZmhpcn1EaWFnbm9zdGljUmVwb3J0U3RhdHVzIiwKICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk5hbWVkVHlwZVNwZWNpZmllciIKICAgICAgICAgICAgICAgfQogICAgICAgICAgICB9IF0KICAgICAgICAgfSwgewogICAgICAgICAgICAibG9jYXRvciIgOiAiMjMyOjEtMjMyOjYyIiwKICAgICAgICAgICAgIm5hbWUiIDogIlRvU3RyaW5nIiwKICAgICAgICAgICAgImNvbnRleHQiIDogIlBhdGllbnQiLAogICAgICAgICAgICAiYWNjZXNzTGV2ZWwiIDogIlB1YmxpYyIsCiAgICAgICAgICAgICJ0eXBlIiA6ICJGdW5jdGlvbkRlZiIsCiAgICAgICAgICAgICJleHByZXNzaW9uIiA6IHsKICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjIzMjo1Mi0yMzI6NjIiLAogICAgICAgICAgICAgICAicGF0aCIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAidHlwZSIgOiAiUHJvcGVydHkiLAogICAgICAgICAgICAgICAic291cmNlIiA6IHsKICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjIzMjo1Mi0yMzI6NTYiLAogICAgICAgICAgICAgICAgICAibmFtZSIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAgICAidHlwZSIgOiAiT3BlcmFuZFJlZiIKICAgICAgICAgICAgICAgfQogICAgICAgICAgICB9LAogICAgICAgICAgICAib3BlcmFuZCIgOiBbIHsKICAgICAgICAgICAgICAgIm5hbWUiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgIm9wZXJhbmRUeXBlU3BlY2lmaWVyIiA6IHsKICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjIzMjozMi0yMzI6NDgiLAogICAgICAgICAgICAgICAgICAibmFtZSIgOiAie2h0dHA6Ly9obDcub3JnL2ZoaXJ9RGlzY3JpbWluYXRvclR5cGUiLAogICAgICAgICAgICAgICAgICAidHlwZSIgOiAiTmFtZWRUeXBlU3BlY2lmaWVyIgogICAgICAgICAgICAgICB9CiAgICAgICAgICAgIH0gXQogICAgICAgICB9LCB7CiAgICAgICAgICAgICJsb2NhdG9yIiA6ICIyMzM6MS0yMzM6NjgiLAogICAgICAgICAgICAibmFtZSIgOiAiVG9TdHJpbmciLAogICAgICAgICAgICAiY29udGV4dCIgOiAiUGF0aWVudCIsCiAgICAgICAgICAgICJhY2Nlc3NMZXZlbCIgOiAiUHVibGljIiwKICAgICAgICAgICAgInR5cGUiIDogIkZ1bmN0aW9uRGVmIiwKICAgICAgICAgICAgImV4cHJlc3Npb24iIDogewogICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMjMzOjU4LTIzMzo2OCIsCiAgICAgICAgICAgICAgICJwYXRoIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICJ0eXBlIiA6ICJQcm9wZXJ0eSIsCiAgICAgICAgICAgICAgICJzb3VyY2UiIDogewogICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMjMzOjU4LTIzMzo2MiIsCiAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJPcGVyYW5kUmVmIgogICAgICAgICAgICAgICB9CiAgICAgICAgICAgIH0sCiAgICAgICAgICAgICJvcGVyYW5kIiA6IFsgewogICAgICAgICAgICAgICAibmFtZSIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAib3BlcmFuZFR5cGVTcGVjaWZpZXIiIDogewogICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMjMzOjMyLTIzMzo1NCIsCiAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJ7aHR0cDovL2hsNy5vcmcvZmhpcn1Eb2N1bWVudENvbmZpZGVudGlhbGl0eSIsCiAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJOYW1lZFR5cGVTcGVjaWZpZXIiCiAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgfSBdCiAgICAgICAgIH0sIHsKICAgICAgICAgICAgImxvY2F0b3IiIDogIjIzNDoxLTIzNDo1NyIsCiAgICAgICAgICAgICJuYW1lIiA6ICJUb1N0cmluZyIsCiAgICAgICAgICAgICJjb250ZXh0IiA6ICJQYXRpZW50IiwKICAgICAgICAgICAgImFjY2Vzc0xldmVsIiA6ICJQdWJsaWMiLAogICAgICAgICAgICAidHlwZSIgOiAiRnVuY3Rpb25EZWYiLAogICAgICAgICAgICAiZXhwcmVzc2lvbiIgOiB7CiAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIyMzQ6NDctMjM0OjU3IiwKICAgICAgICAgICAgICAgInBhdGgiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgInR5cGUiIDogIlByb3BlcnR5IiwKICAgICAgICAgICAgICAgInNvdXJjZSIgOiB7CiAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIyMzQ6NDctMjM0OjUxIiwKICAgICAgICAgICAgICAgICAgIm5hbWUiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk9wZXJhbmRSZWYiCiAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgfSwKICAgICAgICAgICAgIm9wZXJhbmQiIDogWyB7CiAgICAgICAgICAgICAgICJuYW1lIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICJvcGVyYW5kVHlwZVNwZWNpZmllciIgOiB7CiAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIyMzQ6MzItMjM0OjQzIiwKICAgICAgICAgICAgICAgICAgIm5hbWUiIDogIntodHRwOi8vaGw3Lm9yZy9maGlyfURvY3VtZW50TW9kZSIsCiAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJOYW1lZFR5cGVTcGVjaWZpZXIiCiAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgfSBdCiAgICAgICAgIH0sIHsKICAgICAgICAgICAgImxvY2F0b3IiIDogIjIzNToxLTIzNTo2OCIsCiAgICAgICAgICAgICJuYW1lIiA6ICJUb1N0cmluZyIsCiAgICAgICAgICAgICJjb250ZXh0IiA6ICJQYXRpZW50IiwKICAgICAgICAgICAgImFjY2Vzc0xldmVsIiA6ICJQdWJsaWMiLAogICAgICAgICAgICAidHlwZSIgOiAiRnVuY3Rpb25EZWYiLAogICAgICAgICAgICAiZXhwcmVzc2lvbiIgOiB7CiAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIyMzU6NTgtMjM1OjY4IiwKICAgICAgICAgICAgICAgInBhdGgiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgInR5cGUiIDogIlByb3BlcnR5IiwKICAgICAgICAgICAgICAgInNvdXJjZSIgOiB7CiAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIyMzU6NTgtMjM1OjYyIiwKICAgICAgICAgICAgICAgICAgIm5hbWUiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk9wZXJhbmRSZWYiCiAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgfSwKICAgICAgICAgICAgIm9wZXJhbmQiIDogWyB7CiAgICAgICAgICAgICAgICJuYW1lIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICJvcGVyYW5kVHlwZVNwZWNpZmllciIgOiB7CiAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIyMzU6MzItMjM1OjU0IiwKICAgICAgICAgICAgICAgICAgIm5hbWUiIDogIntodHRwOi8vaGw3Lm9yZy9maGlyfURvY3VtZW50UmVmZXJlbmNlU3RhdHVzIiwKICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk5hbWVkVHlwZVNwZWNpZmllciIKICAgICAgICAgICAgICAgfQogICAgICAgICAgICB9IF0KICAgICAgICAgfSwgewogICAgICAgICAgICAibG9jYXRvciIgOiAiMjM2OjEtMjM2OjY5IiwKICAgICAgICAgICAgIm5hbWUiIDogIlRvU3RyaW5nIiwKICAgICAgICAgICAgImNvbnRleHQiIDogIlBhdGllbnQiLAogICAgICAgICAgICAiYWNjZXNzTGV2ZWwiIDogIlB1YmxpYyIsCiAgICAgICAgICAgICJ0eXBlIiA6ICJGdW5jdGlvbkRlZiIsCiAgICAgICAgICAgICJleHByZXNzaW9uIiA6IHsKICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjIzNjo1OS0yMzY6NjkiLAogICAgICAgICAgICAgICAicGF0aCIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAidHlwZSIgOiAiUHJvcGVydHkiLAogICAgICAgICAgICAgICAic291cmNlIiA6IHsKICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjIzNjo1OS0yMzY6NjMiLAogICAgICAgICAgICAgICAgICAibmFtZSIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAgICAidHlwZSIgOiAiT3BlcmFuZFJlZiIKICAgICAgICAgICAgICAgfQogICAgICAgICAgICB9LAogICAgICAgICAgICAib3BlcmFuZCIgOiBbIHsKICAgICAgICAgICAgICAgIm5hbWUiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgIm9wZXJhbmRUeXBlU3BlY2lmaWVyIiA6IHsKICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjIzNjozMi0yMzY6NTUiLAogICAgICAgICAgICAgICAgICAibmFtZSIgOiAie2h0dHA6Ly9obDcub3JnL2ZoaXJ9RG9jdW1lbnRSZWxhdGlvbnNoaXBUeXBlIiwKICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk5hbWVkVHlwZVNwZWNpZmllciIKICAgICAgICAgICAgICAgfQogICAgICAgICAgICB9IF0KICAgICAgICAgfSwgewogICAgICAgICAgICAibG9jYXRvciIgOiAiMjM3OjEtMjM3OjcwIiwKICAgICAgICAgICAgIm5hbWUiIDogIlRvU3RyaW5nIiwKICAgICAgICAgICAgImNvbnRleHQiIDogIlBhdGllbnQiLAogICAgICAgICAgICAiYWNjZXNzTGV2ZWwiIDogIlB1YmxpYyIsCiAgICAgICAgICAgICJ0eXBlIiA6ICJGdW5jdGlvbkRlZiIsCiAgICAgICAgICAgICJleHByZXNzaW9uIiA6IHsKICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjIzNzo2MC0yMzc6NzAiLAogICAgICAgICAgICAgICAicGF0aCIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAidHlwZSIgOiAiUHJvcGVydHkiLAogICAgICAgICAgICAgICAic291cmNlIiA6IHsKICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjIzNzo2MC0yMzc6NjQiLAogICAgICAgICAgICAgICAgICAibmFtZSIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAgICAidHlwZSIgOiAiT3BlcmFuZFJlZiIKICAgICAgICAgICAgICAgfQogICAgICAgICAgICB9LAogICAgICAgICAgICAib3BlcmFuZCIgOiBbIHsKICAgICAgICAgICAgICAgIm5hbWUiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgIm9wZXJhbmRUeXBlU3BlY2lmaWVyIiA6IHsKICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjIzNzozMi0yMzc6NTYiLAogICAgICAgICAgICAgICAgICAibmFtZSIgOiAie2h0dHA6Ly9obDcub3JnL2ZoaXJ9RWxpZ2liaWxpdHlSZXF1ZXN0UHVycG9zZSIsCiAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJOYW1lZFR5cGVTcGVjaWZpZXIiCiAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgfSBdCiAgICAgICAgIH0sIHsKICAgICAgICAgICAgImxvY2F0b3IiIDogIjIzODoxLTIzODo2OSIsCiAgICAgICAgICAgICJuYW1lIiA6ICJUb1N0cmluZyIsCiAgICAgICAgICAgICJjb250ZXh0IiA6ICJQYXRpZW50IiwKICAgICAgICAgICAgImFjY2Vzc0xldmVsIiA6ICJQdWJsaWMiLAogICAgICAgICAgICAidHlwZSIgOiAiRnVuY3Rpb25EZWYiLAogICAgICAgICAgICAiZXhwcmVzc2lvbiIgOiB7CiAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIyMzg6NTktMjM4OjY5IiwKICAgICAgICAgICAgICAgInBhdGgiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgInR5cGUiIDogIlByb3BlcnR5IiwKICAgICAgICAgICAgICAgInNvdXJjZSIgOiB7CiAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIyMzg6NTktMjM4OjYzIiwKICAgICAgICAgICAgICAgICAgIm5hbWUiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk9wZXJhbmRSZWYiCiAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgfSwKICAgICAgICAgICAgIm9wZXJhbmQiIDogWyB7CiAgICAgICAgICAgICAgICJuYW1lIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICJvcGVyYW5kVHlwZVNwZWNpZmllciIgOiB7CiAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIyMzg6MzItMjM4OjU1IiwKICAgICAgICAgICAgICAgICAgIm5hbWUiIDogIntodHRwOi8vaGw3Lm9yZy9maGlyfUVsaWdpYmlsaXR5UmVxdWVzdFN0YXR1cyIsCiAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJOYW1lZFR5cGVTcGVjaWZpZXIiCiAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgfSBdCiAgICAgICAgIH0sIHsKICAgICAgICAgICAgImxvY2F0b3IiIDogIjIzOToxLTIzOTo3MSIsCiAgICAgICAgICAgICJuYW1lIiA6ICJUb1N0cmluZyIsCiAgICAgICAgICAgICJjb250ZXh0IiA6ICJQYXRpZW50IiwKICAgICAgICAgICAgImFjY2Vzc0xldmVsIiA6ICJQdWJsaWMiLAogICAgICAgICAgICAidHlwZSIgOiAiRnVuY3Rpb25EZWYiLAogICAgICAgICAgICAiZXhwcmVzc2lvbiIgOiB7CiAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIyMzk6NjEtMjM5OjcxIiwKICAgICAgICAgICAgICAgInBhdGgiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgInR5cGUiIDogIlByb3BlcnR5IiwKICAgICAgICAgICAgICAgInNvdXJjZSIgOiB7CiAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIyMzk6NjEtMjM5OjY1IiwKICAgICAgICAgICAgICAgICAgIm5hbWUiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk9wZXJhbmRSZWYiCiAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgfSwKICAgICAgICAgICAgIm9wZXJhbmQiIDogWyB7CiAgICAgICAgICAgICAgICJuYW1lIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICJvcGVyYW5kVHlwZVNwZWNpZmllciIgOiB7CiAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIyMzk6MzItMjM5OjU3IiwKICAgICAgICAgICAgICAgICAgIm5hbWUiIDogIntodHRwOi8vaGw3Lm9yZy9maGlyfUVsaWdpYmlsaXR5UmVzcG9uc2VQdXJwb3NlIiwKICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk5hbWVkVHlwZVNwZWNpZmllciIKICAgICAgICAgICAgICAgfQogICAgICAgICAgICB9IF0KICAgICAgICAgfSwgewogICAgICAgICAgICAibG9jYXRvciIgOiAiMjQwOjEtMjQwOjcwIiwKICAgICAgICAgICAgIm5hbWUiIDogIlRvU3RyaW5nIiwKICAgICAgICAgICAgImNvbnRleHQiIDogIlBhdGllbnQiLAogICAgICAgICAgICAiYWNjZXNzTGV2ZWwiIDogIlB1YmxpYyIsCiAgICAgICAgICAgICJ0eXBlIiA6ICJGdW5jdGlvbkRlZiIsCiAgICAgICAgICAgICJleHByZXNzaW9uIiA6IHsKICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjI0MDo2MC0yNDA6NzAiLAogICAgICAgICAgICAgICAicGF0aCIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAidHlwZSIgOiAiUHJvcGVydHkiLAogICAgICAgICAgICAgICAic291cmNlIiA6IHsKICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjI0MDo2MC0yNDA6NjQiLAogICAgICAgICAgICAgICAgICAibmFtZSIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAgICAidHlwZSIgOiAiT3BlcmFuZFJlZiIKICAgICAgICAgICAgICAgfQogICAgICAgICAgICB9LAogICAgICAgICAgICAib3BlcmFuZCIgOiBbIHsKICAgICAgICAgICAgICAgIm5hbWUiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgIm9wZXJhbmRUeXBlU3BlY2lmaWVyIiA6IHsKICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjI0MDozMi0yNDA6NTYiLAogICAgICAgICAgICAgICAgICAibmFtZSIgOiAie2h0dHA6Ly9obDcub3JnL2ZoaXJ9RWxpZ2liaWxpdHlSZXNwb25zZVN0YXR1cyIsCiAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJOYW1lZFR5cGVTcGVjaWZpZXIiCiAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgfSBdCiAgICAgICAgIH0sIHsKICAgICAgICAgICAgImxvY2F0b3IiIDogIjI0MToxLTI0MTo2MyIsCiAgICAgICAgICAgICJuYW1lIiA6ICJUb1N0cmluZyIsCiAgICAgICAgICAgICJjb250ZXh0IiA6ICJQYXRpZW50IiwKICAgICAgICAgICAgImFjY2Vzc0xldmVsIiA6ICJQdWJsaWMiLAogICAgICAgICAgICAidHlwZSIgOiAiRnVuY3Rpb25EZWYiLAogICAgICAgICAgICAiZXhwcmVzc2lvbiIgOiB7CiAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIyNDE6NTMtMjQxOjYzIiwKICAgICAgICAgICAgICAgInBhdGgiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgInR5cGUiIDogIlByb3BlcnR5IiwKICAgICAgICAgICAgICAgInNvdXJjZSIgOiB7CiAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIyNDE6NTMtMjQxOjU3IiwKICAgICAgICAgICAgICAgICAgIm5hbWUiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk9wZXJhbmRSZWYiCiAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgfSwKICAgICAgICAgICAgIm9wZXJhbmQiIDogWyB7CiAgICAgICAgICAgICAgICJuYW1lIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICJvcGVyYW5kVHlwZVNwZWNpZmllciIgOiB7CiAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIyNDE6MzItMjQxOjQ5IiwKICAgICAgICAgICAgICAgICAgIm5hbWUiIDogIntodHRwOi8vaGw3Lm9yZy9maGlyfUVuYWJsZVdoZW5CZWhhdmlvciIsCiAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJOYW1lZFR5cGVTcGVjaWZpZXIiCiAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgfSBdCiAgICAgICAgIH0sIHsKICAgICAgICAgICAgImxvY2F0b3IiIDogIjI0MjoxLTI0Mjo2OCIsCiAgICAgICAgICAgICJuYW1lIiA6ICJUb1N0cmluZyIsCiAgICAgICAgICAgICJjb250ZXh0IiA6ICJQYXRpZW50IiwKICAgICAgICAgICAgImFjY2Vzc0xldmVsIiA6ICJQdWJsaWMiLAogICAgICAgICAgICAidHlwZSIgOiAiRnVuY3Rpb25EZWYiLAogICAgICAgICAgICAiZXhwcmVzc2lvbiIgOiB7CiAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIyNDI6NTgtMjQyOjY4IiwKICAgICAgICAgICAgICAgInBhdGgiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgInR5cGUiIDogIlByb3BlcnR5IiwKICAgICAgICAgICAgICAgInNvdXJjZSIgOiB7CiAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIyNDI6NTgtMjQyOjYyIiwKICAgICAgICAgICAgICAgICAgIm5hbWUiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk9wZXJhbmRSZWYiCiAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgfSwKICAgICAgICAgICAgIm9wZXJhbmQiIDogWyB7CiAgICAgICAgICAgICAgICJuYW1lIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICJvcGVyYW5kVHlwZVNwZWNpZmllciIgOiB7CiAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIyNDI6MzItMjQyOjU0IiwKICAgICAgICAgICAgICAgICAgIm5hbWUiIDogIntodHRwOi8vaGw3Lm9yZy9maGlyfUVuY291bnRlckxvY2F0aW9uU3RhdHVzIiwKICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk5hbWVkVHlwZVNwZWNpZmllciIKICAgICAgICAgICAgICAgfQogICAgICAgICAgICB9IF0KICAgICAgICAgfSwgewogICAgICAgICAgICAibG9jYXRvciIgOiAiMjQzOjEtMjQzOjYwIiwKICAgICAgICAgICAgIm5hbWUiIDogIlRvU3RyaW5nIiwKICAgICAgICAgICAgImNvbnRleHQiIDogIlBhdGllbnQiLAogICAgICAgICAgICAiYWNjZXNzTGV2ZWwiIDogIlB1YmxpYyIsCiAgICAgICAgICAgICJ0eXBlIiA6ICJGdW5jdGlvbkRlZiIsCiAgICAgICAgICAgICJleHByZXNzaW9uIiA6IHsKICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjI0Mzo1MC0yNDM6NjAiLAogICAgICAgICAgICAgICAicGF0aCIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAidHlwZSIgOiAiUHJvcGVydHkiLAogICAgICAgICAgICAgICAic291cmNlIiA6IHsKICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjI0Mzo1MC0yNDM6NTQiLAogICAgICAgICAgICAgICAgICAibmFtZSIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAgICAidHlwZSIgOiAiT3BlcmFuZFJlZiIKICAgICAgICAgICAgICAgfQogICAgICAgICAgICB9LAogICAgICAgICAgICAib3BlcmFuZCIgOiBbIHsKICAgICAgICAgICAgICAgIm5hbWUiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgIm9wZXJhbmRUeXBlU3BlY2lmaWVyIiA6IHsKICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjI0MzozMi0yNDM6NDYiLAogICAgICAgICAgICAgICAgICAibmFtZSIgOiAie2h0dHA6Ly9obDcub3JnL2ZoaXJ9RW5jb3VudGVyU3RhdHVzIiwKICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk5hbWVkVHlwZVNwZWNpZmllciIKICAgICAgICAgICAgICAgfQogICAgICAgICAgICB9IF0KICAgICAgICAgfSwgewogICAgICAgICAgICAibG9jYXRvciIgOiAiMjQ0OjEtMjQ0OjU5IiwKICAgICAgICAgICAgIm5hbWUiIDogIlRvU3RyaW5nIiwKICAgICAgICAgICAgImNvbnRleHQiIDogIlBhdGllbnQiLAogICAgICAgICAgICAiYWNjZXNzTGV2ZWwiIDogIlB1YmxpYyIsCiAgICAgICAgICAgICJ0eXBlIiA6ICJGdW5jdGlvbkRlZiIsCiAgICAgICAgICAgICJleHByZXNzaW9uIiA6IHsKICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjI0NDo0OS0yNDQ6NTkiLAogICAgICAgICAgICAgICAicGF0aCIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAidHlwZSIgOiAiUHJvcGVydHkiLAogICAgICAgICAgICAgICAic291cmNlIiA6IHsKICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjI0NDo0OS0yNDQ6NTMiLAogICAgICAgICAgICAgICAgICAibmFtZSIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAgICAidHlwZSIgOiAiT3BlcmFuZFJlZiIKICAgICAgICAgICAgICAgfQogICAgICAgICAgICB9LAogICAgICAgICAgICAib3BlcmFuZCIgOiBbIHsKICAgICAgICAgICAgICAgIm5hbWUiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgIm9wZXJhbmRUeXBlU3BlY2lmaWVyIiA6IHsKICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjI0NDozMi0yNDQ6NDUiLAogICAgICAgICAgICAgICAgICAibmFtZSIgOiAie2h0dHA6Ly9obDcub3JnL2ZoaXJ9RW5kcG9pbnRTdGF0dXMiLAogICAgICAgICAgICAgICAgICAidHlwZSIgOiAiTmFtZWRUeXBlU3BlY2lmaWVyIgogICAgICAgICAgICAgICB9CiAgICAgICAgICAgIH0gXQogICAgICAgICB9LCB7CiAgICAgICAgICAgICJsb2NhdG9yIiA6ICIyNDU6MS0yNDU6NjgiLAogICAgICAgICAgICAibmFtZSIgOiAiVG9TdHJpbmciLAogICAgICAgICAgICAiY29udGV4dCIgOiAiUGF0aWVudCIsCiAgICAgICAgICAgICJhY2Nlc3NMZXZlbCIgOiAiUHVibGljIiwKICAgICAgICAgICAgInR5cGUiIDogIkZ1bmN0aW9uRGVmIiwKICAgICAgICAgICAgImV4cHJlc3Npb24iIDogewogICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMjQ1OjU4LTI0NTo2OCIsCiAgICAgICAgICAgICAgICJwYXRoIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICJ0eXBlIiA6ICJQcm9wZXJ0eSIsCiAgICAgICAgICAgICAgICJzb3VyY2UiIDogewogICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMjQ1OjU4LTI0NTo2MiIsCiAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJPcGVyYW5kUmVmIgogICAgICAgICAgICAgICB9CiAgICAgICAgICAgIH0sCiAgICAgICAgICAgICJvcGVyYW5kIiA6IFsgewogICAgICAgICAgICAgICAibmFtZSIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAib3BlcmFuZFR5cGVTcGVjaWZpZXIiIDogewogICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMjQ1OjMyLTI0NTo1NCIsCiAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJ7aHR0cDovL2hsNy5vcmcvZmhpcn1FbnJvbGxtZW50UmVxdWVzdFN0YXR1cyIsCiAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJOYW1lZFR5cGVTcGVjaWZpZXIiCiAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgfSBdCiAgICAgICAgIH0sIHsKICAgICAgICAgICAgImxvY2F0b3IiIDogIjI0NjoxLTI0Njo2OSIsCiAgICAgICAgICAgICJuYW1lIiA6ICJUb1N0cmluZyIsCiAgICAgICAgICAgICJjb250ZXh0IiA6ICJQYXRpZW50IiwKICAgICAgICAgICAgImFjY2Vzc0xldmVsIiA6ICJQdWJsaWMiLAogICAgICAgICAgICAidHlwZSIgOiAiRnVuY3Rpb25EZWYiLAogICAgICAgICAgICAiZXhwcmVzc2lvbiIgOiB7CiAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIyNDY6NTktMjQ2OjY5IiwKICAgICAgICAgICAgICAgInBhdGgiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgInR5cGUiIDogIlByb3BlcnR5IiwKICAgICAgICAgICAgICAgInNvdXJjZSIgOiB7CiAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIyNDY6NTktMjQ2OjYzIiwKICAgICAgICAgICAgICAgICAgIm5hbWUiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk9wZXJhbmRSZWYiCiAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgfSwKICAgICAgICAgICAgIm9wZXJhbmQiIDogWyB7CiAgICAgICAgICAgICAgICJuYW1lIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICJvcGVyYW5kVHlwZVNwZWNpZmllciIgOiB7CiAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIyNDY6MzItMjQ2OjU1IiwKICAgICAgICAgICAgICAgICAgIm5hbWUiIDogIntodHRwOi8vaGw3Lm9yZy9maGlyfUVucm9sbG1lbnRSZXNwb25zZVN0YXR1cyIsCiAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJOYW1lZFR5cGVTcGVjaWZpZXIiCiAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgfSBdCiAgICAgICAgIH0sIHsKICAgICAgICAgICAgImxvY2F0b3IiIDogIjI0NzoxLTI0Nzo2NCIsCiAgICAgICAgICAgICJuYW1lIiA6ICJUb1N0cmluZyIsCiAgICAgICAgICAgICJjb250ZXh0IiA6ICJQYXRpZW50IiwKICAgICAgICAgICAgImFjY2Vzc0xldmVsIiA6ICJQdWJsaWMiLAogICAgICAgICAgICAidHlwZSIgOiAiRnVuY3Rpb25EZWYiLAogICAgICAgICAgICAiZXhwcmVzc2lvbiIgOiB7CiAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIyNDc6NTQtMjQ3OjY0IiwKICAgICAgICAgICAgICAgInBhdGgiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgInR5cGUiIDogIlByb3BlcnR5IiwKICAgICAgICAgICAgICAgInNvdXJjZSIgOiB7CiAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIyNDc6NTQtMjQ3OjU4IiwKICAgICAgICAgICAgICAgICAgIm5hbWUiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk9wZXJhbmRSZWYiCiAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgfSwKICAgICAgICAgICAgIm9wZXJhbmQiIDogWyB7CiAgICAgICAgICAgICAgICJuYW1lIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICJvcGVyYW5kVHlwZVNwZWNpZmllciIgOiB7CiAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIyNDc6MzItMjQ3OjUwIiwKICAgICAgICAgICAgICAgICAgIm5hbWUiIDogIntodHRwOi8vaGw3Lm9yZy9maGlyfUVwaXNvZGVPZkNhcmVTdGF0dXMiLAogICAgICAgICAgICAgICAgICAidHlwZSIgOiAiTmFtZWRUeXBlU3BlY2lmaWVyIgogICAgICAgICAgICAgICB9CiAgICAgICAgICAgIH0gXQogICAgICAgICB9LCB7CiAgICAgICAgICAgICJsb2NhdG9yIiA6ICIyNDg6MS0yNDg6NjQiLAogICAgICAgICAgICAibmFtZSIgOiAiVG9TdHJpbmciLAogICAgICAgICAgICAiY29udGV4dCIgOiAiUGF0aWVudCIsCiAgICAgICAgICAgICJhY2Nlc3NMZXZlbCIgOiAiUHVibGljIiwKICAgICAgICAgICAgInR5cGUiIDogIkZ1bmN0aW9uRGVmIiwKICAgICAgICAgICAgImV4cHJlc3Npb24iIDogewogICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMjQ4OjU0LTI0ODo2NCIsCiAgICAgICAgICAgICAgICJwYXRoIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICJ0eXBlIiA6ICJQcm9wZXJ0eSIsCiAgICAgICAgICAgICAgICJzb3VyY2UiIDogewogICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMjQ4OjU0LTI0ODo1OCIsCiAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJPcGVyYW5kUmVmIgogICAgICAgICAgICAgICB9CiAgICAgICAgICAgIH0sCiAgICAgICAgICAgICJvcGVyYW5kIiA6IFsgewogICAgICAgICAgICAgICAibmFtZSIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAib3BlcmFuZFR5cGVTcGVjaWZpZXIiIDogewogICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMjQ4OjMyLTI0ODo1MCIsCiAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJ7aHR0cDovL2hsNy5vcmcvZmhpcn1FdmVudENhcGFiaWxpdHlNb2RlIiwKICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk5hbWVkVHlwZVNwZWNpZmllciIKICAgICAgICAgICAgICAgfQogICAgICAgICAgICB9IF0KICAgICAgICAgfSwgewogICAgICAgICAgICAibG9jYXRvciIgOiAiMjQ5OjEtMjQ5OjU2IiwKICAgICAgICAgICAgIm5hbWUiIDogIlRvU3RyaW5nIiwKICAgICAgICAgICAgImNvbnRleHQiIDogIlBhdGllbnQiLAogICAgICAgICAgICAiYWNjZXNzTGV2ZWwiIDogIlB1YmxpYyIsCiAgICAgICAgICAgICJ0eXBlIiA6ICJGdW5jdGlvbkRlZiIsCiAgICAgICAgICAgICJleHByZXNzaW9uIiA6IHsKICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjI0OTo0Ni0yNDk6NTYiLAogICAgICAgICAgICAgICAicGF0aCIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAidHlwZSIgOiAiUHJvcGVydHkiLAogICAgICAgICAgICAgICAic291cmNlIiA6IHsKICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjI0OTo0Ni0yNDk6NTAiLAogICAgICAgICAgICAgICAgICAibmFtZSIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAgICAidHlwZSIgOiAiT3BlcmFuZFJlZiIKICAgICAgICAgICAgICAgfQogICAgICAgICAgICB9LAogICAgICAgICAgICAib3BlcmFuZCIgOiBbIHsKICAgICAgICAgICAgICAgIm5hbWUiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgIm9wZXJhbmRUeXBlU3BlY2lmaWVyIiA6IHsKICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjI0OTozMi0yNDk6NDIiLAogICAgICAgICAgICAgICAgICAibmFtZSIgOiAie2h0dHA6Ly9obDcub3JnL2ZoaXJ9RXZlbnRUaW1pbmciLAogICAgICAgICAgICAgICAgICAidHlwZSIgOiAiTmFtZWRUeXBlU3BlY2lmaWVyIgogICAgICAgICAgICAgICB9CiAgICAgICAgICAgIH0gXQogICAgICAgICB9LCB7CiAgICAgICAgICAgICJsb2NhdG9yIiA6ICIyNTA6MS0yNTA6NjUiLAogICAgICAgICAgICAibmFtZSIgOiAiVG9TdHJpbmciLAogICAgICAgICAgICAiY29udGV4dCIgOiAiUGF0aWVudCIsCiAgICAgICAgICAgICJhY2Nlc3NMZXZlbCIgOiAiUHVibGljIiwKICAgICAgICAgICAgInR5cGUiIDogIkZ1bmN0aW9uRGVmIiwKICAgICAgICAgICAgImV4cHJlc3Npb24iIDogewogICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMjUwOjU1LTI1MDo2NSIsCiAgICAgICAgICAgICAgICJwYXRoIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICJ0eXBlIiA6ICJQcm9wZXJ0eSIsCiAgICAgICAgICAgICAgICJzb3VyY2UiIDogewogICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMjUwOjU1LTI1MDo1OSIsCiAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJPcGVyYW5kUmVmIgogICAgICAgICAgICAgICB9CiAgICAgICAgICAgIH0sCiAgICAgICAgICAgICJvcGVyYW5kIiA6IFsgewogICAgICAgICAgICAgICAibmFtZSIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAib3BlcmFuZFR5cGVTcGVjaWZpZXIiIDogewogICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMjUwOjMyLTI1MDo1MSIsCiAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJ7aHR0cDovL2hsNy5vcmcvZmhpcn1FdmlkZW5jZVZhcmlhYmxlVHlwZSIsCiAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJOYW1lZFR5cGVTcGVjaWZpZXIiCiAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgfSBdCiAgICAgICAgIH0sIHsKICAgICAgICAgICAgImxvY2F0b3IiIDogIjI1MToxLTI1MTo2OSIsCiAgICAgICAgICAgICJuYW1lIiA6ICJUb1N0cmluZyIsCiAgICAgICAgICAgICJjb250ZXh0IiA6ICJQYXRpZW50IiwKICAgICAgICAgICAgImFjY2Vzc0xldmVsIiA6ICJQdWJsaWMiLAogICAgICAgICAgICAidHlwZSIgOiAiRnVuY3Rpb25EZWYiLAogICAgICAgICAgICAiZXhwcmVzc2lvbiIgOiB7CiAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIyNTE6NTktMjUxOjY5IiwKICAgICAgICAgICAgICAgInBhdGgiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgInR5cGUiIDogIlByb3BlcnR5IiwKICAgICAgICAgICAgICAgInNvdXJjZSIgOiB7CiAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIyNTE6NTktMjUxOjYzIiwKICAgICAgICAgICAgICAgICAgIm5hbWUiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk9wZXJhbmRSZWYiCiAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgfSwKICAgICAgICAgICAgIm9wZXJhbmQiIDogWyB7CiAgICAgICAgICAgICAgICJuYW1lIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICJvcGVyYW5kVHlwZVNwZWNpZmllciIgOiB7CiAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIyNTE6MzItMjUxOjU1IiwKICAgICAgICAgICAgICAgICAgIm5hbWUiIDogIntodHRwOi8vaGw3Lm9yZy9maGlyfUV4YW1wbGVTY2VuYXJpb0FjdG9yVHlwZSIsCiAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJOYW1lZFR5cGVTcGVjaWZpZXIiCiAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgfSBdCiAgICAgICAgIH0sIHsKICAgICAgICAgICAgImxvY2F0b3IiIDogIjI1MjoxLTI1Mjo3MSIsCiAgICAgICAgICAgICJuYW1lIiA6ICJUb1N0cmluZyIsCiAgICAgICAgICAgICJjb250ZXh0IiA6ICJQYXRpZW50IiwKICAgICAgICAgICAgImFjY2Vzc0xldmVsIiA6ICJQdWJsaWMiLAogICAgICAgICAgICAidHlwZSIgOiAiRnVuY3Rpb25EZWYiLAogICAgICAgICAgICAiZXhwcmVzc2lvbiIgOiB7CiAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIyNTI6NjEtMjUyOjcxIiwKICAgICAgICAgICAgICAgInBhdGgiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgInR5cGUiIDogIlByb3BlcnR5IiwKICAgICAgICAgICAgICAgInNvdXJjZSIgOiB7CiAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIyNTI6NjEtMjUyOjY1IiwKICAgICAgICAgICAgICAgICAgIm5hbWUiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk9wZXJhbmRSZWYiCiAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgfSwKICAgICAgICAgICAgIm9wZXJhbmQiIDogWyB7CiAgICAgICAgICAgICAgICJuYW1lIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICJvcGVyYW5kVHlwZVNwZWNpZmllciIgOiB7CiAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIyNTI6MzItMjUyOjU3IiwKICAgICAgICAgICAgICAgICAgIm5hbWUiIDogIntodHRwOi8vaGw3Lm9yZy9maGlyfUV4cGxhbmF0aW9uT2ZCZW5lZml0U3RhdHVzIiwKICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk5hbWVkVHlwZVNwZWNpZmllciIKICAgICAgICAgICAgICAgfQogICAgICAgICAgICB9IF0KICAgICAgICAgfSwgewogICAgICAgICAgICAibG9jYXRvciIgOiAiMjUzOjEtMjUzOjU4IiwKICAgICAgICAgICAgIm5hbWUiIDogIlRvU3RyaW5nIiwKICAgICAgICAgICAgImNvbnRleHQiIDogIlBhdGllbnQiLAogICAgICAgICAgICAiYWNjZXNzTGV2ZWwiIDogIlB1YmxpYyIsCiAgICAgICAgICAgICJ0eXBlIiA6ICJGdW5jdGlvbkRlZiIsCiAgICAgICAgICAgICJleHByZXNzaW9uIiA6IHsKICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjI1Mzo0OC0yNTM6NTgiLAogICAgICAgICAgICAgICAicGF0aCIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAidHlwZSIgOiAiUHJvcGVydHkiLAogICAgICAgICAgICAgICAic291cmNlIiA6IHsKICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjI1Mzo0OC0yNTM6NTIiLAogICAgICAgICAgICAgICAgICAibmFtZSIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAgICAidHlwZSIgOiAiT3BlcmFuZFJlZiIKICAgICAgICAgICAgICAgfQogICAgICAgICAgICB9LAogICAgICAgICAgICAib3BlcmFuZCIgOiBbIHsKICAgICAgICAgICAgICAgIm5hbWUiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgIm9wZXJhbmRUeXBlU3BlY2lmaWVyIiA6IHsKICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjI1MzozMi0yNTM6NDQiLAogICAgICAgICAgICAgICAgICAibmFtZSIgOiAie2h0dHA6Ly9obDcub3JnL2ZoaXJ9RXhwb3N1cmVTdGF0ZSIsCiAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJOYW1lZFR5cGVTcGVjaWZpZXIiCiAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgfSBdCiAgICAgICAgIH0sIHsKICAgICAgICAgICAgImxvY2F0b3IiIDogIjI1NDoxLTI1NDo2NSIsCiAgICAgICAgICAgICJuYW1lIiA6ICJUb1N0cmluZyIsCiAgICAgICAgICAgICJjb250ZXh0IiA6ICJQYXRpZW50IiwKICAgICAgICAgICAgImFjY2Vzc0xldmVsIiA6ICJQdWJsaWMiLAogICAgICAgICAgICAidHlwZSIgOiAiRnVuY3Rpb25EZWYiLAogICAgICAgICAgICAiZXhwcmVzc2lvbiIgOiB7CiAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIyNTQ6NTUtMjU0OjY1IiwKICAgICAgICAgICAgICAgInBhdGgiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgInR5cGUiIDogIlByb3BlcnR5IiwKICAgICAgICAgICAgICAgInNvdXJjZSIgOiB7CiAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIyNTQ6NTUtMjU0OjU5IiwKICAgICAgICAgICAgICAgICAgIm5hbWUiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk9wZXJhbmRSZWYiCiAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgfSwKICAgICAgICAgICAgIm9wZXJhbmQiIDogWyB7CiAgICAgICAgICAgICAgICJuYW1lIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICJvcGVyYW5kVHlwZVNwZWNpZmllciIgOiB7CiAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIyNTQ6MzItMjU0OjUxIiwKICAgICAgICAgICAgICAgICAgIm5hbWUiIDogIntodHRwOi8vaGw3Lm9yZy9maGlyfUV4dGVuc2lvbkNvbnRleHRUeXBlIiwKICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk5hbWVkVHlwZVNwZWNpZmllciIKICAgICAgICAgICAgICAgfQogICAgICAgICAgICB9IF0KICAgICAgICAgfSwgewogICAgICAgICAgICAibG9jYXRvciIgOiAiMjU1OjEtMjU1OjU3IiwKICAgICAgICAgICAgIm5hbWUiIDogIlRvU3RyaW5nIiwKICAgICAgICAgICAgImNvbnRleHQiIDogIlBhdGllbnQiLAogICAgICAgICAgICAiYWNjZXNzTGV2ZWwiIDogIlB1YmxpYyIsCiAgICAgICAgICAgICJ0eXBlIiA6ICJGdW5jdGlvbkRlZiIsCiAgICAgICAgICAgICJleHByZXNzaW9uIiA6IHsKICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjI1NTo0Ny0yNTU6NTciLAogICAgICAgICAgICAgICAicGF0aCIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAidHlwZSIgOiAiUHJvcGVydHkiLAogICAgICAgICAgICAgICAic291cmNlIiA6IHsKICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjI1NTo0Ny0yNTU6NTEiLAogICAgICAgICAgICAgICAgICAibmFtZSIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAgICAidHlwZSIgOiAiT3BlcmFuZFJlZiIKICAgICAgICAgICAgICAgfQogICAgICAgICAgICB9LAogICAgICAgICAgICAib3BlcmFuZCIgOiBbIHsKICAgICAgICAgICAgICAgIm5hbWUiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgIm9wZXJhbmRUeXBlU3BlY2lmaWVyIiA6IHsKICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjI1NTozMi0yNTU6NDMiLAogICAgICAgICAgICAgICAgICAibmFtZSIgOiAie2h0dHA6Ly9obDcub3JnL2ZoaXJ9RkhJUkFsbFR5cGVzIiwKICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk5hbWVkVHlwZVNwZWNpZmllciIKICAgICAgICAgICAgICAgfQogICAgICAgICAgICB9IF0KICAgICAgICAgfSwgewogICAgICAgICAgICAibG9jYXRvciIgOiAiMjU2OjEtMjU2OjYwIiwKICAgICAgICAgICAgIm5hbWUiIDogIlRvU3RyaW5nIiwKICAgICAgICAgICAgImNvbnRleHQiIDogIlBhdGllbnQiLAogICAgICAgICAgICAiYWNjZXNzTGV2ZWwiIDogIlB1YmxpYyIsCiAgICAgICAgICAgICJ0eXBlIiA6ICJGdW5jdGlvbkRlZiIsCiAgICAgICAgICAgICJleHByZXNzaW9uIiA6IHsKICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjI1Njo1MC0yNTY6NjAiLAogICAgICAgICAgICAgICAicGF0aCIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAidHlwZSIgOiAiUHJvcGVydHkiLAogICAgICAgICAgICAgICAic291cmNlIiA6IHsKICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjI1Njo1MC0yNTY6NTQiLAogICAgICAgICAgICAgICAgICAibmFtZSIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAgICAidHlwZSIgOiAiT3BlcmFuZFJlZiIKICAgICAgICAgICAgICAgfQogICAgICAgICAgICB9LAogICAgICAgICAgICAib3BlcmFuZCIgOiBbIHsKICAgICAgICAgICAgICAgIm5hbWUiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgIm9wZXJhbmRUeXBlU3BlY2lmaWVyIiA6IHsKICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjI1NjozMi0yNTY6NDYiLAogICAgICAgICAgICAgICAgICAibmFtZSIgOiAie2h0dHA6Ly9obDcub3JnL2ZoaXJ9RkhJUkRlZmluZWRUeXBlIiwKICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk5hbWVkVHlwZVNwZWNpZmllciIKICAgICAgICAgICAgICAgfQogICAgICAgICAgICB9IF0KICAgICAgICAgfSwgewogICAgICAgICAgICAibG9jYXRvciIgOiAiMjU3OjEtMjU3OjYxIiwKICAgICAgICAgICAgIm5hbWUiIDogIlRvU3RyaW5nIiwKICAgICAgICAgICAgImNvbnRleHQiIDogIlBhdGllbnQiLAogICAgICAgICAgICAiYWNjZXNzTGV2ZWwiIDogIlB1YmxpYyIsCiAgICAgICAgICAgICJ0eXBlIiA6ICJGdW5jdGlvbkRlZiIsCiAgICAgICAgICAgICJleHByZXNzaW9uIiA6IHsKICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjI1Nzo1MS0yNTc6NjEiLAogICAgICAgICAgICAgICAicGF0aCIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAidHlwZSIgOiAiUHJvcGVydHkiLAogICAgICAgICAgICAgICAic291cmNlIiA6IHsKICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjI1Nzo1MS0yNTc6NTUiLAogICAgICAgICAgICAgICAgICAibmFtZSIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAgICAidHlwZSIgOiAiT3BlcmFuZFJlZiIKICAgICAgICAgICAgICAgfQogICAgICAgICAgICB9LAogICAgICAgICAgICAib3BlcmFuZCIgOiBbIHsKICAgICAgICAgICAgICAgIm5hbWUiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgIm9wZXJhbmRUeXBlU3BlY2lmaWVyIiA6IHsKICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjI1NzozMi0yNTc6NDciLAogICAgICAgICAgICAgICAgICAibmFtZSIgOiAie2h0dHA6Ly9obDcub3JnL2ZoaXJ9RkhJUkRldmljZVN0YXR1cyIsCiAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJOYW1lZFR5cGVTcGVjaWZpZXIiCiAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgfSBdCiAgICAgICAgIH0sIHsKICAgICAgICAgICAgImxvY2F0b3IiIDogIjI1ODoxLTI1ODo2MSIsCiAgICAgICAgICAgICJuYW1lIiA6ICJUb1N0cmluZyIsCiAgICAgICAgICAgICJjb250ZXh0IiA6ICJQYXRpZW50IiwKICAgICAgICAgICAgImFjY2Vzc0xldmVsIiA6ICJQdWJsaWMiLAogICAgICAgICAgICAidHlwZSIgOiAiRnVuY3Rpb25EZWYiLAogICAgICAgICAgICAiZXhwcmVzc2lvbiIgOiB7CiAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIyNTg6NTEtMjU4OjYxIiwKICAgICAgICAgICAgICAgInBhdGgiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgInR5cGUiIDogIlByb3BlcnR5IiwKICAgICAgICAgICAgICAgInNvdXJjZSIgOiB7CiAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIyNTg6NTEtMjU4OjU1IiwKICAgICAgICAgICAgICAgICAgIm5hbWUiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk9wZXJhbmRSZWYiCiAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgfSwKICAgICAgICAgICAgIm9wZXJhbmQiIDogWyB7CiAgICAgICAgICAgICAgICJuYW1lIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICJvcGVyYW5kVHlwZVNwZWNpZmllciIgOiB7CiAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIyNTg6MzItMjU4OjQ3IiwKICAgICAgICAgICAgICAgICAgIm5hbWUiIDogIntodHRwOi8vaGw3Lm9yZy9maGlyfUZISVJSZXNvdXJjZVR5cGUiLAogICAgICAgICAgICAgICAgICAidHlwZSIgOiAiTmFtZWRUeXBlU3BlY2lmaWVyIgogICAgICAgICAgICAgICB9CiAgICAgICAgICAgIH0gXQogICAgICAgICB9LCB7CiAgICAgICAgICAgICJsb2NhdG9yIiA6ICIyNTk6MS0yNTk6NjQiLAogICAgICAgICAgICAibmFtZSIgOiAiVG9TdHJpbmciLAogICAgICAgICAgICAiY29udGV4dCIgOiAiUGF0aWVudCIsCiAgICAgICAgICAgICJhY2Nlc3NMZXZlbCIgOiAiUHVibGljIiwKICAgICAgICAgICAgInR5cGUiIDogIkZ1bmN0aW9uRGVmIiwKICAgICAgICAgICAgImV4cHJlc3Npb24iIDogewogICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMjU5OjU0LTI1OTo2NCIsCiAgICAgICAgICAgICAgICJwYXRoIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICJ0eXBlIiA6ICJQcm9wZXJ0eSIsCiAgICAgICAgICAgICAgICJzb3VyY2UiIDogewogICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMjU5OjU0LTI1OTo1OCIsCiAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJPcGVyYW5kUmVmIgogICAgICAgICAgICAgICB9CiAgICAgICAgICAgIH0sCiAgICAgICAgICAgICJvcGVyYW5kIiA6IFsgewogICAgICAgICAgICAgICAibmFtZSIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAib3BlcmFuZFR5cGVTcGVjaWZpZXIiIDogewogICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMjU5OjMyLTI1OTo1MCIsCiAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJ7aHR0cDovL2hsNy5vcmcvZmhpcn1GSElSU3Vic3RhbmNlU3RhdHVzIiwKICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk5hbWVkVHlwZVNwZWNpZmllciIKICAgICAgICAgICAgICAgfQogICAgICAgICAgICB9IF0KICAgICAgICAgfSwgewogICAgICAgICAgICAibG9jYXRvciIgOiAiMjYwOjEtMjYwOjU2IiwKICAgICAgICAgICAgIm5hbWUiIDogIlRvU3RyaW5nIiwKICAgICAgICAgICAgImNvbnRleHQiIDogIlBhdGllbnQiLAogICAgICAgICAgICAiYWNjZXNzTGV2ZWwiIDogIlB1YmxpYyIsCiAgICAgICAgICAgICJ0eXBlIiA6ICJGdW5jdGlvbkRlZiIsCiAgICAgICAgICAgICJleHByZXNzaW9uIiA6IHsKICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjI2MDo0Ni0yNjA6NTYiLAogICAgICAgICAgICAgICAicGF0aCIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAidHlwZSIgOiAiUHJvcGVydHkiLAogICAgICAgICAgICAgICAic291cmNlIiA6IHsKICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjI2MDo0Ni0yNjA6NTAiLAogICAgICAgICAgICAgICAgICAibmFtZSIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAgICAidHlwZSIgOiAiT3BlcmFuZFJlZiIKICAgICAgICAgICAgICAgfQogICAgICAgICAgICB9LAogICAgICAgICAgICAib3BlcmFuZCIgOiBbIHsKICAgICAgICAgICAgICAgIm5hbWUiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgIm9wZXJhbmRUeXBlU3BlY2lmaWVyIiA6IHsKICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjI2MDozMi0yNjA6NDIiLAogICAgICAgICAgICAgICAgICAibmFtZSIgOiAie2h0dHA6Ly9obDcub3JnL2ZoaXJ9RkhJUlZlcnNpb24iLAogICAgICAgICAgICAgICAgICAidHlwZSIgOiAiTmFtZWRUeXBlU3BlY2lmaWVyIgogICAgICAgICAgICAgICB9CiAgICAgICAgICAgIH0gXQogICAgICAgICB9LCB7CiAgICAgICAgICAgICJsb2NhdG9yIiA6ICIyNjE6MS0yNjE6NjQiLAogICAgICAgICAgICAibmFtZSIgOiAiVG9TdHJpbmciLAogICAgICAgICAgICAiY29udGV4dCIgOiAiUGF0aWVudCIsCiAgICAgICAgICAgICJhY2Nlc3NMZXZlbCIgOiAiUHVibGljIiwKICAgICAgICAgICAgInR5cGUiIDogIkZ1bmN0aW9uRGVmIiwKICAgICAgICAgICAgImV4cHJlc3Npb24iIDogewogICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMjYxOjU0LTI2MTo2NCIsCiAgICAgICAgICAgICAgICJwYXRoIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICJ0eXBlIiA6ICJQcm9wZXJ0eSIsCiAgICAgICAgICAgICAgICJzb3VyY2UiIDogewogICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMjYxOjU0LTI2MTo1OCIsCiAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJPcGVyYW5kUmVmIgogICAgICAgICAgICAgICB9CiAgICAgICAgICAgIH0sCiAgICAgICAgICAgICJvcGVyYW5kIiA6IFsgewogICAgICAgICAgICAgICAibmFtZSIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAib3BlcmFuZFR5cGVTcGVjaWZpZXIiIDogewogICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMjYxOjMyLTI2MTo1MCIsCiAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJ7aHR0cDovL2hsNy5vcmcvZmhpcn1GYW1pbHlIaXN0b3J5U3RhdHVzIiwKICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk5hbWVkVHlwZVNwZWNpZmllciIKICAgICAgICAgICAgICAgfQogICAgICAgICAgICB9IF0KICAgICAgICAgfSwgewogICAgICAgICAgICAibG9jYXRvciIgOiAiMjYyOjEtMjYyOjU5IiwKICAgICAgICAgICAgIm5hbWUiIDogIlRvU3RyaW5nIiwKICAgICAgICAgICAgImNvbnRleHQiIDogIlBhdGllbnQiLAogICAgICAgICAgICAiYWNjZXNzTGV2ZWwiIDogIlB1YmxpYyIsCiAgICAgICAgICAgICJ0eXBlIiA6ICJGdW5jdGlvbkRlZiIsCiAgICAgICAgICAgICJleHByZXNzaW9uIiA6IHsKICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjI2Mjo0OS0yNjI6NTkiLAogICAgICAgICAgICAgICAicGF0aCIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAidHlwZSIgOiAiUHJvcGVydHkiLAogICAgICAgICAgICAgICAic291cmNlIiA6IHsKICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjI2Mjo0OS0yNjI6NTMiLAogICAgICAgICAgICAgICAgICAibmFtZSIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAgICAidHlwZSIgOiAiT3BlcmFuZFJlZiIKICAgICAgICAgICAgICAgfQogICAgICAgICAgICB9LAogICAgICAgICAgICAib3BlcmFuZCIgOiBbIHsKICAgICAgICAgICAgICAgIm5hbWUiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgIm9wZXJhbmRUeXBlU3BlY2lmaWVyIiA6IHsKICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjI2MjozMi0yNjI6NDUiLAogICAgICAgICAgICAgICAgICAibmFtZSIgOiAie2h0dHA6Ly9obDcub3JnL2ZoaXJ9RmlsdGVyT3BlcmF0b3IiLAogICAgICAgICAgICAgICAgICAidHlwZSIgOiAiTmFtZWRUeXBlU3BlY2lmaWVyIgogICAgICAgICAgICAgICB9CiAgICAgICAgICAgIH0gXQogICAgICAgICB9LCB7CiAgICAgICAgICAgICJsb2NhdG9yIiA6ICIyNjM6MS0yNjM6NTUiLAogICAgICAgICAgICAibmFtZSIgOiAiVG9TdHJpbmciLAogICAgICAgICAgICAiY29udGV4dCIgOiAiUGF0aWVudCIsCiAgICAgICAgICAgICJhY2Nlc3NMZXZlbCIgOiAiUHVibGljIiwKICAgICAgICAgICAgInR5cGUiIDogIkZ1bmN0aW9uRGVmIiwKICAgICAgICAgICAgImV4cHJlc3Npb24iIDogewogICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMjYzOjQ1LTI2Mzo1NSIsCiAgICAgICAgICAgICAgICJwYXRoIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICJ0eXBlIiA6ICJQcm9wZXJ0eSIsCiAgICAgICAgICAgICAgICJzb3VyY2UiIDogewogICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMjYzOjQ1LTI2Mzo0OSIsCiAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJPcGVyYW5kUmVmIgogICAgICAgICAgICAgICB9CiAgICAgICAgICAgIH0sCiAgICAgICAgICAgICJvcGVyYW5kIiA6IFsgewogICAgICAgICAgICAgICAibmFtZSIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAib3BlcmFuZFR5cGVTcGVjaWZpZXIiIDogewogICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMjYzOjMyLTI2Mzo0MSIsCiAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJ7aHR0cDovL2hsNy5vcmcvZmhpcn1GbGFnU3RhdHVzIiwKICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk5hbWVkVHlwZVNwZWNpZmllciIKICAgICAgICAgICAgICAgfQogICAgICAgICAgICB9IF0KICAgICAgICAgfSwgewogICAgICAgICAgICAibG9jYXRvciIgOiAiMjY0OjEtMjY0OjY0IiwKICAgICAgICAgICAgIm5hbWUiIDogIlRvU3RyaW5nIiwKICAgICAgICAgICAgImNvbnRleHQiIDogIlBhdGllbnQiLAogICAgICAgICAgICAiYWNjZXNzTGV2ZWwiIDogIlB1YmxpYyIsCiAgICAgICAgICAgICJ0eXBlIiA6ICJGdW5jdGlvbkRlZiIsCiAgICAgICAgICAgICJleHByZXNzaW9uIiA6IHsKICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjI2NDo1NC0yNjQ6NjQiLAogICAgICAgICAgICAgICAicGF0aCIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAidHlwZSIgOiAiUHJvcGVydHkiLAogICAgICAgICAgICAgICAic291cmNlIiA6IHsKICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjI2NDo1NC0yNjQ6NTgiLAogICAgICAgICAgICAgICAgICAibmFtZSIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAgICAidHlwZSIgOiAiT3BlcmFuZFJlZiIKICAgICAgICAgICAgICAgfQogICAgICAgICAgICB9LAogICAgICAgICAgICAib3BlcmFuZCIgOiBbIHsKICAgICAgICAgICAgICAgIm5hbWUiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgIm9wZXJhbmRUeXBlU3BlY2lmaWVyIiA6IHsKICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjI2NDozMi0yNjQ6NTAiLAogICAgICAgICAgICAgICAgICAibmFtZSIgOiAie2h0dHA6Ly9obDcub3JnL2ZoaXJ9R29hbExpZmVjeWNsZVN0YXR1cyIsCiAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJOYW1lZFR5cGVTcGVjaWZpZXIiCiAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgfSBdCiAgICAgICAgIH0sIHsKICAgICAgICAgICAgImxvY2F0b3IiIDogIjI2NToxLTI2NTo2NSIsCiAgICAgICAgICAgICJuYW1lIiA6ICJUb1N0cmluZyIsCiAgICAgICAgICAgICJjb250ZXh0IiA6ICJQYXRpZW50IiwKICAgICAgICAgICAgImFjY2Vzc0xldmVsIiA6ICJQdWJsaWMiLAogICAgICAgICAgICAidHlwZSIgOiAiRnVuY3Rpb25EZWYiLAogICAgICAgICAgICAiZXhwcmVzc2lvbiIgOiB7CiAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIyNjU6NTUtMjY1OjY1IiwKICAgICAgICAgICAgICAgInBhdGgiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgInR5cGUiIDogIlByb3BlcnR5IiwKICAgICAgICAgICAgICAgInNvdXJjZSIgOiB7CiAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIyNjU6NTUtMjY1OjU5IiwKICAgICAgICAgICAgICAgICAgIm5hbWUiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk9wZXJhbmRSZWYiCiAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgfSwKICAgICAgICAgICAgIm9wZXJhbmQiIDogWyB7CiAgICAgICAgICAgICAgICJuYW1lIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICJvcGVyYW5kVHlwZVNwZWNpZmllciIgOiB7CiAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIyNjU6MzItMjY1OjUxIiwKICAgICAgICAgICAgICAgICAgIm5hbWUiIDogIntodHRwOi8vaGw3Lm9yZy9maGlyfUdyYXBoQ29tcGFydG1lbnRSdWxlIiwKICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk5hbWVkVHlwZVNwZWNpZmllciIKICAgICAgICAgICAgICAgfQogICAgICAgICAgICB9IF0KICAgICAgICAgfSwgewogICAgICAgICAgICAibG9jYXRvciIgOiAiMjY2OjEtMjY2OjY0IiwKICAgICAgICAgICAgIm5hbWUiIDogIlRvU3RyaW5nIiwKICAgICAgICAgICAgImNvbnRleHQiIDogIlBhdGllbnQiLAogICAgICAgICAgICAiYWNjZXNzTGV2ZWwiIDogIlB1YmxpYyIsCiAgICAgICAgICAgICJ0eXBlIiA6ICJGdW5jdGlvbkRlZiIsCiAgICAgICAgICAgICJleHByZXNzaW9uIiA6IHsKICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjI2Njo1NC0yNjY6NjQiLAogICAgICAgICAgICAgICAicGF0aCIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAidHlwZSIgOiAiUHJvcGVydHkiLAogICAgICAgICAgICAgICAic291cmNlIiA6IHsKICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjI2Njo1NC0yNjY6NTgiLAogICAgICAgICAgICAgICAgICAibmFtZSIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAgICAidHlwZSIgOiAiT3BlcmFuZFJlZiIKICAgICAgICAgICAgICAgfQogICAgICAgICAgICB9LAogICAgICAgICAgICAib3BlcmFuZCIgOiBbIHsKICAgICAgICAgICAgICAgIm5hbWUiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgIm9wZXJhbmRUeXBlU3BlY2lmaWVyIiA6IHsKICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjI2NjozMi0yNjY6NTAiLAogICAgICAgICAgICAgICAgICAibmFtZSIgOiAie2h0dHA6Ly9obDcub3JnL2ZoaXJ9R3JhcGhDb21wYXJ0bWVudFVzZSIsCiAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJOYW1lZFR5cGVTcGVjaWZpZXIiCiAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgfSBdCiAgICAgICAgIH0sIHsKICAgICAgICAgICAgImxvY2F0b3IiIDogIjI2NzoxLTI2Nzo1NyIsCiAgICAgICAgICAgICJuYW1lIiA6ICJUb1N0cmluZyIsCiAgICAgICAgICAgICJjb250ZXh0IiA6ICJQYXRpZW50IiwKICAgICAgICAgICAgImFjY2Vzc0xldmVsIiA6ICJQdWJsaWMiLAogICAgICAgICAgICAidHlwZSIgOiAiRnVuY3Rpb25EZWYiLAogICAgICAgICAgICAiZXhwcmVzc2lvbiIgOiB7CiAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIyNjc6NDctMjY3OjU3IiwKICAgICAgICAgICAgICAgInBhdGgiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgInR5cGUiIDogIlByb3BlcnR5IiwKICAgICAgICAgICAgICAgInNvdXJjZSIgOiB7CiAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIyNjc6NDctMjY3OjUxIiwKICAgICAgICAgICAgICAgICAgIm5hbWUiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk9wZXJhbmRSZWYiCiAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgfSwKICAgICAgICAgICAgIm9wZXJhbmQiIDogWyB7CiAgICAgICAgICAgICAgICJuYW1lIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICJvcGVyYW5kVHlwZVNwZWNpZmllciIgOiB7CiAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIyNjc6MzItMjY3OjQzIiwKICAgICAgICAgICAgICAgICAgIm5hbWUiIDogIntodHRwOi8vaGw3Lm9yZy9maGlyfUdyb3VwTWVhc3VyZSIsCiAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJOYW1lZFR5cGVTcGVjaWZpZXIiCiAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgfSBdCiAgICAgICAgIH0sIHsKICAgICAgICAgICAgImxvY2F0b3IiIDogIjI2ODoxLTI2ODo1NCIsCiAgICAgICAgICAgICJuYW1lIiA6ICJUb1N0cmluZyIsCiAgICAgICAgICAgICJjb250ZXh0IiA6ICJQYXRpZW50IiwKICAgICAgICAgICAgImFjY2Vzc0xldmVsIiA6ICJQdWJsaWMiLAogICAgICAgICAgICAidHlwZSIgOiAiRnVuY3Rpb25EZWYiLAogICAgICAgICAgICAiZXhwcmVzc2lvbiIgOiB7CiAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIyNjg6NDQtMjY4OjU0IiwKICAgICAgICAgICAgICAgInBhdGgiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgInR5cGUiIDogIlByb3BlcnR5IiwKICAgICAgICAgICAgICAgInNvdXJjZSIgOiB7CiAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIyNjg6NDQtMjY4OjQ4IiwKICAgICAgICAgICAgICAgICAgIm5hbWUiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk9wZXJhbmRSZWYiCiAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgfSwKICAgICAgICAgICAgIm9wZXJhbmQiIDogWyB7CiAgICAgICAgICAgICAgICJuYW1lIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICJvcGVyYW5kVHlwZVNwZWNpZmllciIgOiB7CiAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIyNjg6MzItMjY4OjQwIiwKICAgICAgICAgICAgICAgICAgIm5hbWUiIDogIntodHRwOi8vaGw3Lm9yZy9maGlyfUdyb3VwVHlwZSIsCiAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJOYW1lZFR5cGVTcGVjaWZpZXIiCiAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgfSBdCiAgICAgICAgIH0sIHsKICAgICAgICAgICAgImxvY2F0b3IiIDogIjI2OToxLTI2OTo2NyIsCiAgICAgICAgICAgICJuYW1lIiA6ICJUb1N0cmluZyIsCiAgICAgICAgICAgICJjb250ZXh0IiA6ICJQYXRpZW50IiwKICAgICAgICAgICAgImFjY2Vzc0xldmVsIiA6ICJQdWJsaWMiLAogICAgICAgICAgICAidHlwZSIgOiAiRnVuY3Rpb25EZWYiLAogICAgICAgICAgICAiZXhwcmVzc2lvbiIgOiB7CiAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIyNjk6NTctMjY5OjY3IiwKICAgICAgICAgICAgICAgInBhdGgiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgInR5cGUiIDogIlByb3BlcnR5IiwKICAgICAgICAgICAgICAgInNvdXJjZSIgOiB7CiAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIyNjk6NTctMjY5OjYxIiwKICAgICAgICAgICAgICAgICAgIm5hbWUiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk9wZXJhbmRSZWYiCiAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgfSwKICAgICAgICAgICAgIm9wZXJhbmQiIDogWyB7CiAgICAgICAgICAgICAgICJuYW1lIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICJvcGVyYW5kVHlwZVNwZWNpZmllciIgOiB7CiAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIyNjk6MzItMjY5OjUzIiwKICAgICAgICAgICAgICAgICAgIm5hbWUiIDogIntodHRwOi8vaGw3Lm9yZy9maGlyfUd1aWRhbmNlUmVzcG9uc2VTdGF0dXMiLAogICAgICAgICAgICAgICAgICAidHlwZSIgOiAiTmFtZWRUeXBlU3BlY2lmaWVyIgogICAgICAgICAgICAgICB9CiAgICAgICAgICAgIH0gXQogICAgICAgICB9LCB7CiAgICAgICAgICAgICJsb2NhdG9yIiA6ICIyNzA6MS0yNzA6NjQiLAogICAgICAgICAgICAibmFtZSIgOiAiVG9TdHJpbmciLAogICAgICAgICAgICAiY29udGV4dCIgOiAiUGF0aWVudCIsCiAgICAgICAgICAgICJhY2Nlc3NMZXZlbCIgOiAiUHVibGljIiwKICAgICAgICAgICAgInR5cGUiIDogIkZ1bmN0aW9uRGVmIiwKICAgICAgICAgICAgImV4cHJlc3Npb24iIDogewogICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMjcwOjU0LTI3MDo2NCIsCiAgICAgICAgICAgICAgICJwYXRoIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICJ0eXBlIiA6ICJQcm9wZXJ0eSIsCiAgICAgICAgICAgICAgICJzb3VyY2UiIDogewogICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMjcwOjU0LTI3MDo1OCIsCiAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJPcGVyYW5kUmVmIgogICAgICAgICAgICAgICB9CiAgICAgICAgICAgIH0sCiAgICAgICAgICAgICJvcGVyYW5kIiA6IFsgewogICAgICAgICAgICAgICAibmFtZSIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAib3BlcmFuZFR5cGVTcGVjaWZpZXIiIDogewogICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMjcwOjMyLTI3MDo1MCIsCiAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJ7aHR0cDovL2hsNy5vcmcvZmhpcn1HdWlkZVBhZ2VHZW5lcmF0aW9uIiwKICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk5hbWVkVHlwZVNwZWNpZmllciIKICAgICAgICAgICAgICAgfQogICAgICAgICAgICB9IF0KICAgICAgICAgfSwgewogICAgICAgICAgICAibG9jYXRvciIgOiAiMjcxOjEtMjcxOjYzIiwKICAgICAgICAgICAgIm5hbWUiIDogIlRvU3RyaW5nIiwKICAgICAgICAgICAgImNvbnRleHQiIDogIlBhdGllbnQiLAogICAgICAgICAgICAiYWNjZXNzTGV2ZWwiIDogIlB1YmxpYyIsCiAgICAgICAgICAgICJ0eXBlIiA6ICJGdW5jdGlvbkRlZiIsCiAgICAgICAgICAgICJleHByZXNzaW9uIiA6IHsKICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjI3MTo1My0yNzE6NjMiLAogICAgICAgICAgICAgICAicGF0aCIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAidHlwZSIgOiAiUHJvcGVydHkiLAogICAgICAgICAgICAgICAic291cmNlIiA6IHsKICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjI3MTo1My0yNzE6NTciLAogICAgICAgICAgICAgICAgICAibmFtZSIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAgICAidHlwZSIgOiAiT3BlcmFuZFJlZiIKICAgICAgICAgICAgICAgfQogICAgICAgICAgICB9LAogICAgICAgICAgICAib3BlcmFuZCIgOiBbIHsKICAgICAgICAgICAgICAgIm5hbWUiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgIm9wZXJhbmRUeXBlU3BlY2lmaWVyIiA6IHsKICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjI3MTozMi0yNzE6NDkiLAogICAgICAgICAgICAgICAgICAibmFtZSIgOiAie2h0dHA6Ly9obDcub3JnL2ZoaXJ9R3VpZGVQYXJhbWV0ZXJDb2RlIiwKICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk5hbWVkVHlwZVNwZWNpZmllciIKICAgICAgICAgICAgICAgfQogICAgICAgICAgICB9IF0KICAgICAgICAgfSwgewogICAgICAgICAgICAibG9jYXRvciIgOiAiMjcyOjEtMjcyOjUzIiwKICAgICAgICAgICAgIm5hbWUiIDogIlRvU3RyaW5nIiwKICAgICAgICAgICAgImNvbnRleHQiIDogIlBhdGllbnQiLAogICAgICAgICAgICAiYWNjZXNzTGV2ZWwiIDogIlB1YmxpYyIsCiAgICAgICAgICAgICJ0eXBlIiA6ICJGdW5jdGlvbkRlZiIsCiAgICAgICAgICAgICJleHByZXNzaW9uIiA6IHsKICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjI3Mjo0My0yNzI6NTMiLAogICAgICAgICAgICAgICAicGF0aCIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAidHlwZSIgOiAiUHJvcGVydHkiLAogICAgICAgICAgICAgICAic291cmNlIiA6IHsKICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjI3Mjo0My0yNzI6NDciLAogICAgICAgICAgICAgICAgICAibmFtZSIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAgICAidHlwZSIgOiAiT3BlcmFuZFJlZiIKICAgICAgICAgICAgICAgfQogICAgICAgICAgICB9LAogICAgICAgICAgICAib3BlcmFuZCIgOiBbIHsKICAgICAgICAgICAgICAgIm5hbWUiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgIm9wZXJhbmRUeXBlU3BlY2lmaWVyIiA6IHsKICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjI3MjozMi0yNzI6MzkiLAogICAgICAgICAgICAgICAgICAibmFtZSIgOiAie2h0dHA6Ly9obDcub3JnL2ZoaXJ9SFRUUFZlcmIiLAogICAgICAgICAgICAgICAgICAidHlwZSIgOiAiTmFtZWRUeXBlU3BlY2lmaWVyIgogICAgICAgICAgICAgICB9CiAgICAgICAgICAgIH0gXQogICAgICAgICB9LCB7CiAgICAgICAgICAgICJsb2NhdG9yIiA6ICIyNzM6MS0yNzM6NTgiLAogICAgICAgICAgICAibmFtZSIgOiAiVG9TdHJpbmciLAogICAgICAgICAgICAiY29udGV4dCIgOiAiUGF0aWVudCIsCiAgICAgICAgICAgICJhY2Nlc3NMZXZlbCIgOiAiUHVibGljIiwKICAgICAgICAgICAgInR5cGUiIDogIkZ1bmN0aW9uRGVmIiwKICAgICAgICAgICAgImV4cHJlc3Npb24iIDogewogICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMjczOjQ4LTI3Mzo1OCIsCiAgICAgICAgICAgICAgICJwYXRoIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICJ0eXBlIiA6ICJQcm9wZXJ0eSIsCiAgICAgICAgICAgICAgICJzb3VyY2UiIDogewogICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMjczOjQ4LTI3Mzo1MiIsCiAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJPcGVyYW5kUmVmIgogICAgICAgICAgICAgICB9CiAgICAgICAgICAgIH0sCiAgICAgICAgICAgICJvcGVyYW5kIiA6IFsgewogICAgICAgICAgICAgICAibmFtZSIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAib3BlcmFuZFR5cGVTcGVjaWZpZXIiIDogewogICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMjczOjMyLTI3Mzo0NCIsCiAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJ7aHR0cDovL2hsNy5vcmcvZmhpcn1JZGVudGlmaWVyVXNlIiwKICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk5hbWVkVHlwZVNwZWNpZmllciIKICAgICAgICAgICAgICAgfQogICAgICAgICAgICB9IF0KICAgICAgICAgfSwgewogICAgICAgICAgICAibG9jYXRvciIgOiAiMjc0OjEtMjc0OjY3IiwKICAgICAgICAgICAgIm5hbWUiIDogIlRvU3RyaW5nIiwKICAgICAgICAgICAgImNvbnRleHQiIDogIlBhdGllbnQiLAogICAgICAgICAgICAiYWNjZXNzTGV2ZWwiIDogIlB1YmxpYyIsCiAgICAgICAgICAgICJ0eXBlIiA6ICJGdW5jdGlvbkRlZiIsCiAgICAgICAgICAgICJleHByZXNzaW9uIiA6IHsKICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjI3NDo1Ny0yNzQ6NjciLAogICAgICAgICAgICAgICAicGF0aCIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAidHlwZSIgOiAiUHJvcGVydHkiLAogICAgICAgICAgICAgICAic291cmNlIiA6IHsKICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjI3NDo1Ny0yNzQ6NjEiLAogICAgICAgICAgICAgICAgICAibmFtZSIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAgICAidHlwZSIgOiAiT3BlcmFuZFJlZiIKICAgICAgICAgICAgICAgfQogICAgICAgICAgICB9LAogICAgICAgICAgICAib3BlcmFuZCIgOiBbIHsKICAgICAgICAgICAgICAgIm5hbWUiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgIm9wZXJhbmRUeXBlU3BlY2lmaWVyIiA6IHsKICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjI3NDozMi0yNzQ6NTMiLAogICAgICAgICAgICAgICAgICAibmFtZSIgOiAie2h0dHA6Ly9obDcub3JnL2ZoaXJ9SWRlbnRpdHlBc3N1cmFuY2VMZXZlbCIsCiAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJOYW1lZFR5cGVTcGVjaWZpZXIiCiAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgfSBdCiAgICAgICAgIH0sIHsKICAgICAgICAgICAgImxvY2F0b3IiIDogIjI3NToxLTI3NTo2MyIsCiAgICAgICAgICAgICJuYW1lIiA6ICJUb1N0cmluZyIsCiAgICAgICAgICAgICJjb250ZXh0IiA6ICJQYXRpZW50IiwKICAgICAgICAgICAgImFjY2Vzc0xldmVsIiA6ICJQdWJsaWMiLAogICAgICAgICAgICAidHlwZSIgOiAiRnVuY3Rpb25EZWYiLAogICAgICAgICAgICAiZXhwcmVzc2lvbiIgOiB7CiAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIyNzU6NTMtMjc1OjYzIiwKICAgICAgICAgICAgICAgInBhdGgiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgInR5cGUiIDogIlByb3BlcnR5IiwKICAgICAgICAgICAgICAgInNvdXJjZSIgOiB7CiAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIyNzU6NTMtMjc1OjU3IiwKICAgICAgICAgICAgICAgICAgIm5hbWUiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk9wZXJhbmRSZWYiCiAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgfSwKICAgICAgICAgICAgIm9wZXJhbmQiIDogWyB7CiAgICAgICAgICAgICAgICJuYW1lIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICJvcGVyYW5kVHlwZVNwZWNpZmllciIgOiB7CiAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIyNzU6MzItMjc1OjQ5IiwKICAgICAgICAgICAgICAgICAgIm5hbWUiIDogIntodHRwOi8vaGw3Lm9yZy9maGlyfUltYWdpbmdTdHVkeVN0YXR1cyIsCiAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJOYW1lZFR5cGVTcGVjaWZpZXIiCiAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgfSBdCiAgICAgICAgIH0sIHsKICAgICAgICAgICAgImxvY2F0b3IiIDogIjI3NjoxLTI3Njo3MyIsCiAgICAgICAgICAgICJuYW1lIiA6ICJUb1N0cmluZyIsCiAgICAgICAgICAgICJjb250ZXh0IiA6ICJQYXRpZW50IiwKICAgICAgICAgICAgImFjY2Vzc0xldmVsIiA6ICJQdWJsaWMiLAogICAgICAgICAgICAidHlwZSIgOiAiRnVuY3Rpb25EZWYiLAogICAgICAgICAgICAiZXhwcmVzc2lvbiIgOiB7CiAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIyNzY6NjMtMjc2OjczIiwKICAgICAgICAgICAgICAgInBhdGgiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgInR5cGUiIDogIlByb3BlcnR5IiwKICAgICAgICAgICAgICAgInNvdXJjZSIgOiB7CiAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIyNzY6NjMtMjc2OjY3IiwKICAgICAgICAgICAgICAgICAgIm5hbWUiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk9wZXJhbmRSZWYiCiAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgfSwKICAgICAgICAgICAgIm9wZXJhbmQiIDogWyB7CiAgICAgICAgICAgICAgICJuYW1lIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICJvcGVyYW5kVHlwZVNwZWNpZmllciIgOiB7CiAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIyNzY6MzItMjc2OjU5IiwKICAgICAgICAgICAgICAgICAgIm5hbWUiIDogIntodHRwOi8vaGw3Lm9yZy9maGlyfUltbXVuaXphdGlvbkV2YWx1YXRpb25TdGF0dXMiLAogICAgICAgICAgICAgICAgICAidHlwZSIgOiAiTmFtZWRUeXBlU3BlY2lmaWVyIgogICAgICAgICAgICAgICB9CiAgICAgICAgICAgIH0gXQogICAgICAgICB9LCB7CiAgICAgICAgICAgICJsb2NhdG9yIiA6ICIyNzc6MS0yNzc6NjMiLAogICAgICAgICAgICAibmFtZSIgOiAiVG9TdHJpbmciLAogICAgICAgICAgICAiY29udGV4dCIgOiAiUGF0aWVudCIsCiAgICAgICAgICAgICJhY2Nlc3NMZXZlbCIgOiAiUHVibGljIiwKICAgICAgICAgICAgInR5cGUiIDogIkZ1bmN0aW9uRGVmIiwKICAgICAgICAgICAgImV4cHJlc3Npb24iIDogewogICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMjc3OjUzLTI3Nzo2MyIsCiAgICAgICAgICAgICAgICJwYXRoIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICJ0eXBlIiA6ICJQcm9wZXJ0eSIsCiAgICAgICAgICAgICAgICJzb3VyY2UiIDogewogICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMjc3OjUzLTI3Nzo1NyIsCiAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJPcGVyYW5kUmVmIgogICAgICAgICAgICAgICB9CiAgICAgICAgICAgIH0sCiAgICAgICAgICAgICJvcGVyYW5kIiA6IFsgewogICAgICAgICAgICAgICAibmFtZSIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAib3BlcmFuZFR5cGVTcGVjaWZpZXIiIDogewogICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMjc3OjMyLTI3Nzo0OSIsCiAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJ7aHR0cDovL2hsNy5vcmcvZmhpcn1JbW11bml6YXRpb25TdGF0dXMiLAogICAgICAgICAgICAgICAgICAidHlwZSIgOiAiTmFtZWRUeXBlU3BlY2lmaWVyIgogICAgICAgICAgICAgICB9CiAgICAgICAgICAgIH0gXQogICAgICAgICB9LCB7CiAgICAgICAgICAgICJsb2NhdG9yIiA6ICIyNzg6MS0yNzg6NzAiLAogICAgICAgICAgICAibmFtZSIgOiAiVG9TdHJpbmciLAogICAgICAgICAgICAiY29udGV4dCIgOiAiUGF0aWVudCIsCiAgICAgICAgICAgICJhY2Nlc3NMZXZlbCIgOiAiUHVibGljIiwKICAgICAgICAgICAgInR5cGUiIDogIkZ1bmN0aW9uRGVmIiwKICAgICAgICAgICAgImV4cHJlc3Npb24iIDogewogICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMjc4OjYwLTI3ODo3MCIsCiAgICAgICAgICAgICAgICJwYXRoIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICJ0eXBlIiA6ICJQcm9wZXJ0eSIsCiAgICAgICAgICAgICAgICJzb3VyY2UiIDogewogICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMjc4OjYwLTI3ODo2NCIsCiAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJPcGVyYW5kUmVmIgogICAgICAgICAgICAgICB9CiAgICAgICAgICAgIH0sCiAgICAgICAgICAgICJvcGVyYW5kIiA6IFsgewogICAgICAgICAgICAgICAibmFtZSIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAib3BlcmFuZFR5cGVTcGVjaWZpZXIiIDogewogICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMjc4OjMyLTI3ODo1NiIsCiAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJ7aHR0cDovL2hsNy5vcmcvZmhpcn1JbnZvaWNlUHJpY2VDb21wb25lbnRUeXBlIiwKICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk5hbWVkVHlwZVNwZWNpZmllciIKICAgICAgICAgICAgICAgfQogICAgICAgICAgICB9IF0KICAgICAgICAgfSwgewogICAgICAgICAgICAibG9jYXRvciIgOiAiMjc5OjEtMjc5OjU4IiwKICAgICAgICAgICAgIm5hbWUiIDogIlRvU3RyaW5nIiwKICAgICAgICAgICAgImNvbnRleHQiIDogIlBhdGllbnQiLAogICAgICAgICAgICAiYWNjZXNzTGV2ZWwiIDogIlB1YmxpYyIsCiAgICAgICAgICAgICJ0eXBlIiA6ICJGdW5jdGlvbkRlZiIsCiAgICAgICAgICAgICJleHByZXNzaW9uIiA6IHsKICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjI3OTo0OC0yNzk6NTgiLAogICAgICAgICAgICAgICAicGF0aCIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAidHlwZSIgOiAiUHJvcGVydHkiLAogICAgICAgICAgICAgICAic291cmNlIiA6IHsKICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjI3OTo0OC0yNzk6NTIiLAogICAgICAgICAgICAgICAgICAibmFtZSIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAgICAidHlwZSIgOiAiT3BlcmFuZFJlZiIKICAgICAgICAgICAgICAgfQogICAgICAgICAgICB9LAogICAgICAgICAgICAib3BlcmFuZCIgOiBbIHsKICAgICAgICAgICAgICAgIm5hbWUiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgIm9wZXJhbmRUeXBlU3BlY2lmaWVyIiA6IHsKICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjI3OTozMi0yNzk6NDQiLAogICAgICAgICAgICAgICAgICAibmFtZSIgOiAie2h0dHA6Ly9obDcub3JnL2ZoaXJ9SW52b2ljZVN0YXR1cyIsCiAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJOYW1lZFR5cGVTcGVjaWZpZXIiCiAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgfSBdCiAgICAgICAgIH0sIHsKICAgICAgICAgICAgImxvY2F0b3IiIDogIjI4MDoxLTI4MDo1OCIsCiAgICAgICAgICAgICJuYW1lIiA6ICJUb1N0cmluZyIsCiAgICAgICAgICAgICJjb250ZXh0IiA6ICJQYXRpZW50IiwKICAgICAgICAgICAgImFjY2Vzc0xldmVsIiA6ICJQdWJsaWMiLAogICAgICAgICAgICAidHlwZSIgOiAiRnVuY3Rpb25EZWYiLAogICAgICAgICAgICAiZXhwcmVzc2lvbiIgOiB7CiAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIyODA6NDgtMjgwOjU4IiwKICAgICAgICAgICAgICAgInBhdGgiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgInR5cGUiIDogIlByb3BlcnR5IiwKICAgICAgICAgICAgICAgInNvdXJjZSIgOiB7CiAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIyODA6NDgtMjgwOjUyIiwKICAgICAgICAgICAgICAgICAgIm5hbWUiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk9wZXJhbmRSZWYiCiAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgfSwKICAgICAgICAgICAgIm9wZXJhbmQiIDogWyB7CiAgICAgICAgICAgICAgICJuYW1lIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICJvcGVyYW5kVHlwZVNwZWNpZmllciIgOiB7CiAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIyODA6MzItMjgwOjQ0IiwKICAgICAgICAgICAgICAgICAgIm5hbWUiIDogIntodHRwOi8vaGw3Lm9yZy9maGlyfUlzc3VlU2V2ZXJpdHkiLAogICAgICAgICAgICAgICAgICAidHlwZSIgOiAiTmFtZWRUeXBlU3BlY2lmaWVyIgogICAgICAgICAgICAgICB9CiAgICAgICAgICAgIH0gXQogICAgICAgICB9LCB7CiAgICAgICAgICAgICJsb2NhdG9yIiA6ICIyODE6MS0yODE6NTQiLAogICAgICAgICAgICAibmFtZSIgOiAiVG9TdHJpbmciLAogICAgICAgICAgICAiY29udGV4dCIgOiAiUGF0aWVudCIsCiAgICAgICAgICAgICJhY2Nlc3NMZXZlbCIgOiAiUHVibGljIiwKICAgICAgICAgICAgInR5cGUiIDogIkZ1bmN0aW9uRGVmIiwKICAgICAgICAgICAgImV4cHJlc3Npb24iIDogewogICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMjgxOjQ0LTI4MTo1NCIsCiAgICAgICAgICAgICAgICJwYXRoIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICJ0eXBlIiA6ICJQcm9wZXJ0eSIsCiAgICAgICAgICAgICAgICJzb3VyY2UiIDogewogICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMjgxOjQ0LTI4MTo0OCIsCiAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJPcGVyYW5kUmVmIgogICAgICAgICAgICAgICB9CiAgICAgICAgICAgIH0sCiAgICAgICAgICAgICJvcGVyYW5kIiA6IFsgewogICAgICAgICAgICAgICAibmFtZSIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAib3BlcmFuZFR5cGVTcGVjaWZpZXIiIDogewogICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMjgxOjMyLTI4MTo0MCIsCiAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJ7aHR0cDovL2hsNy5vcmcvZmhpcn1Jc3N1ZVR5cGUiLAogICAgICAgICAgICAgICAgICAidHlwZSIgOiAiTmFtZWRUeXBlU3BlY2lmaWVyIgogICAgICAgICAgICAgICB9CiAgICAgICAgICAgIH0gXQogICAgICAgICB9LCB7CiAgICAgICAgICAgICJsb2NhdG9yIiA6ICIyODI6MS0yODI6NTMiLAogICAgICAgICAgICAibmFtZSIgOiAiVG9TdHJpbmciLAogICAgICAgICAgICAiY29udGV4dCIgOiAiUGF0aWVudCIsCiAgICAgICAgICAgICJhY2Nlc3NMZXZlbCIgOiAiUHVibGljIiwKICAgICAgICAgICAgInR5cGUiIDogIkZ1bmN0aW9uRGVmIiwKICAgICAgICAgICAgImV4cHJlc3Npb24iIDogewogICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMjgyOjQzLTI4Mjo1MyIsCiAgICAgICAgICAgICAgICJwYXRoIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICJ0eXBlIiA6ICJQcm9wZXJ0eSIsCiAgICAgICAgICAgICAgICJzb3VyY2UiIDogewogICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMjgyOjQzLTI4Mjo0NyIsCiAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJPcGVyYW5kUmVmIgogICAgICAgICAgICAgICB9CiAgICAgICAgICAgIH0sCiAgICAgICAgICAgICJvcGVyYW5kIiA6IFsgewogICAgICAgICAgICAgICAibmFtZSIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAib3BlcmFuZFR5cGVTcGVjaWZpZXIiIDogewogICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMjgyOjMyLTI4MjozOSIsCiAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJ7aHR0cDovL2hsNy5vcmcvZmhpcn1MaW5rVHlwZSIsCiAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJOYW1lZFR5cGVTcGVjaWZpZXIiCiAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgfSBdCiAgICAgICAgIH0sIHsKICAgICAgICAgICAgImxvY2F0b3IiIDogIjI4MzoxLTI4Mzo1NiIsCiAgICAgICAgICAgICJuYW1lIiA6ICJUb1N0cmluZyIsCiAgICAgICAgICAgICJjb250ZXh0IiA6ICJQYXRpZW50IiwKICAgICAgICAgICAgImFjY2Vzc0xldmVsIiA6ICJQdWJsaWMiLAogICAgICAgICAgICAidHlwZSIgOiAiRnVuY3Rpb25EZWYiLAogICAgICAgICAgICAiZXhwcmVzc2lvbiIgOiB7CiAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIyODM6NDYtMjgzOjU2IiwKICAgICAgICAgICAgICAgInBhdGgiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgInR5cGUiIDogIlByb3BlcnR5IiwKICAgICAgICAgICAgICAgInNvdXJjZSIgOiB7CiAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIyODM6NDYtMjgzOjUwIiwKICAgICAgICAgICAgICAgICAgIm5hbWUiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk9wZXJhbmRSZWYiCiAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgfSwKICAgICAgICAgICAgIm9wZXJhbmQiIDogWyB7CiAgICAgICAgICAgICAgICJuYW1lIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICJvcGVyYW5kVHlwZVNwZWNpZmllciIgOiB7CiAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIyODM6MzItMjgzOjQyIiwKICAgICAgICAgICAgICAgICAgIm5hbWUiIDogIntodHRwOi8vaGw3Lm9yZy9maGlyfUxpbmthZ2VUeXBlIiwKICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk5hbWVkVHlwZVNwZWNpZmllciIKICAgICAgICAgICAgICAgfQogICAgICAgICAgICB9IF0KICAgICAgICAgfSwgewogICAgICAgICAgICAibG9jYXRvciIgOiAiMjg0OjEtMjg0OjUzIiwKICAgICAgICAgICAgIm5hbWUiIDogIlRvU3RyaW5nIiwKICAgICAgICAgICAgImNvbnRleHQiIDogIlBhdGllbnQiLAogICAgICAgICAgICAiYWNjZXNzTGV2ZWwiIDogIlB1YmxpYyIsCiAgICAgICAgICAgICJ0eXBlIiA6ICJGdW5jdGlvbkRlZiIsCiAgICAgICAgICAgICJleHByZXNzaW9uIiA6IHsKICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjI4NDo0My0yODQ6NTMiLAogICAgICAgICAgICAgICAicGF0aCIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAidHlwZSIgOiAiUHJvcGVydHkiLAogICAgICAgICAgICAgICAic291cmNlIiA6IHsKICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjI4NDo0My0yODQ6NDciLAogICAgICAgICAgICAgICAgICAibmFtZSIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAgICAidHlwZSIgOiAiT3BlcmFuZFJlZiIKICAgICAgICAgICAgICAgfQogICAgICAgICAgICB9LAogICAgICAgICAgICAib3BlcmFuZCIgOiBbIHsKICAgICAgICAgICAgICAgIm5hbWUiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgIm9wZXJhbmRUeXBlU3BlY2lmaWVyIiA6IHsKICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjI4NDozMi0yODQ6MzkiLAogICAgICAgICAgICAgICAgICAibmFtZSIgOiAie2h0dHA6Ly9obDcub3JnL2ZoaXJ9TGlzdE1vZGUiLAogICAgICAgICAgICAgICAgICAidHlwZSIgOiAiTmFtZWRUeXBlU3BlY2lmaWVyIgogICAgICAgICAgICAgICB9CiAgICAgICAgICAgIH0gXQogICAgICAgICB9LCB7CiAgICAgICAgICAgICJsb2NhdG9yIiA6ICIyODU6MS0yODU6NTUiLAogICAgICAgICAgICAibmFtZSIgOiAiVG9TdHJpbmciLAogICAgICAgICAgICAiY29udGV4dCIgOiAiUGF0aWVudCIsCiAgICAgICAgICAgICJhY2Nlc3NMZXZlbCIgOiAiUHVibGljIiwKICAgICAgICAgICAgInR5cGUiIDogIkZ1bmN0aW9uRGVmIiwKICAgICAgICAgICAgImV4cHJlc3Npb24iIDogewogICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMjg1OjQ1LTI4NTo1NSIsCiAgICAgICAgICAgICAgICJwYXRoIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICJ0eXBlIiA6ICJQcm9wZXJ0eSIsCiAgICAgICAgICAgICAgICJzb3VyY2UiIDogewogICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMjg1OjQ1LTI4NTo0OSIsCiAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJPcGVyYW5kUmVmIgogICAgICAgICAgICAgICB9CiAgICAgICAgICAgIH0sCiAgICAgICAgICAgICJvcGVyYW5kIiA6IFsgewogICAgICAgICAgICAgICAibmFtZSIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAib3BlcmFuZFR5cGVTcGVjaWZpZXIiIDogewogICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMjg1OjMyLTI4NTo0MSIsCiAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJ7aHR0cDovL2hsNy5vcmcvZmhpcn1MaXN0U3RhdHVzIiwKICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk5hbWVkVHlwZVNwZWNpZmllciIKICAgICAgICAgICAgICAgfQogICAgICAgICAgICB9IF0KICAgICAgICAgfSwgewogICAgICAgICAgICAibG9jYXRvciIgOiAiMjg2OjEtMjg2OjU3IiwKICAgICAgICAgICAgIm5hbWUiIDogIlRvU3RyaW5nIiwKICAgICAgICAgICAgImNvbnRleHQiIDogIlBhdGllbnQiLAogICAgICAgICAgICAiYWNjZXNzTGV2ZWwiIDogIlB1YmxpYyIsCiAgICAgICAgICAgICJ0eXBlIiA6ICJGdW5jdGlvbkRlZiIsCiAgICAgICAgICAgICJleHByZXNzaW9uIiA6IHsKICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjI4Njo0Ny0yODY6NTciLAogICAgICAgICAgICAgICAicGF0aCIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAidHlwZSIgOiAiUHJvcGVydHkiLAogICAgICAgICAgICAgICAic291cmNlIiA6IHsKICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjI4Njo0Ny0yODY6NTEiLAogICAgICAgICAgICAgICAgICAibmFtZSIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAgICAidHlwZSIgOiAiT3BlcmFuZFJlZiIKICAgICAgICAgICAgICAgfQogICAgICAgICAgICB9LAogICAgICAgICAgICAib3BlcmFuZCIgOiBbIHsKICAgICAgICAgICAgICAgIm5hbWUiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgIm9wZXJhbmRUeXBlU3BlY2lmaWVyIiA6IHsKICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjI4NjozMi0yODY6NDMiLAogICAgICAgICAgICAgICAgICAibmFtZSIgOiAie2h0dHA6Ly9obDcub3JnL2ZoaXJ9TG9jYXRpb25Nb2RlIiwKICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk5hbWVkVHlwZVNwZWNpZmllciIKICAgICAgICAgICAgICAgfQogICAgICAgICAgICB9IF0KICAgICAgICAgfSwgewogICAgICAgICAgICAibG9jYXRvciIgOiAiMjg3OjEtMjg3OjU5IiwKICAgICAgICAgICAgIm5hbWUiIDogIlRvU3RyaW5nIiwKICAgICAgICAgICAgImNvbnRleHQiIDogIlBhdGllbnQiLAogICAgICAgICAgICAiYWNjZXNzTGV2ZWwiIDogIlB1YmxpYyIsCiAgICAgICAgICAgICJ0eXBlIiA6ICJGdW5jdGlvbkRlZiIsCiAgICAgICAgICAgICJleHByZXNzaW9uIiA6IHsKICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjI4Nzo0OS0yODc6NTkiLAogICAgICAgICAgICAgICAicGF0aCIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAidHlwZSIgOiAiUHJvcGVydHkiLAogICAgICAgICAgICAgICAic291cmNlIiA6IHsKICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjI4Nzo0OS0yODc6NTMiLAogICAgICAgICAgICAgICAgICAibmFtZSIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAgICAidHlwZSIgOiAiT3BlcmFuZFJlZiIKICAgICAgICAgICAgICAgfQogICAgICAgICAgICB9LAogICAgICAgICAgICAib3BlcmFuZCIgOiBbIHsKICAgICAgICAgICAgICAgIm5hbWUiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgIm9wZXJhbmRUeXBlU3BlY2lmaWVyIiA6IHsKICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjI4NzozMi0yODc6NDUiLAogICAgICAgICAgICAgICAgICAibmFtZSIgOiAie2h0dHA6Ly9obDcub3JnL2ZoaXJ9TG9jYXRpb25TdGF0dXMiLAogICAgICAgICAgICAgICAgICAidHlwZSIgOiAiTmFtZWRUeXBlU3BlY2lmaWVyIgogICAgICAgICAgICAgICB9CiAgICAgICAgICAgIH0gXQogICAgICAgICB9LCB7CiAgICAgICAgICAgICJsb2NhdG9yIiA6ICIyODg6MS0yODg6NjQiLAogICAgICAgICAgICAibmFtZSIgOiAiVG9TdHJpbmciLAogICAgICAgICAgICAiY29udGV4dCIgOiAiUGF0aWVudCIsCiAgICAgICAgICAgICJhY2Nlc3NMZXZlbCIgOiAiUHVibGljIiwKICAgICAgICAgICAgInR5cGUiIDogIkZ1bmN0aW9uRGVmIiwKICAgICAgICAgICAgImV4cHJlc3Npb24iIDogewogICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMjg4OjU0LTI4ODo2NCIsCiAgICAgICAgICAgICAgICJwYXRoIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICJ0eXBlIiA6ICJQcm9wZXJ0eSIsCiAgICAgICAgICAgICAgICJzb3VyY2UiIDogewogICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMjg4OjU0LTI4ODo1OCIsCiAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJPcGVyYW5kUmVmIgogICAgICAgICAgICAgICB9CiAgICAgICAgICAgIH0sCiAgICAgICAgICAgICJvcGVyYW5kIiA6IFsgewogICAgICAgICAgICAgICAibmFtZSIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAib3BlcmFuZFR5cGVTcGVjaWZpZXIiIDogewogICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMjg4OjMyLTI4ODo1MCIsCiAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJ7aHR0cDovL2hsNy5vcmcvZmhpcn1NZWFzdXJlUmVwb3J0U3RhdHVzIiwKICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk5hbWVkVHlwZVNwZWNpZmllciIKICAgICAgICAgICAgICAgfQogICAgICAgICAgICB9IF0KICAgICAgICAgfSwgewogICAgICAgICAgICAibG9jYXRvciIgOiAiMjg5OjEtMjg5OjYyIiwKICAgICAgICAgICAgIm5hbWUiIDogIlRvU3RyaW5nIiwKICAgICAgICAgICAgImNvbnRleHQiIDogIlBhdGllbnQiLAogICAgICAgICAgICAiYWNjZXNzTGV2ZWwiIDogIlB1YmxpYyIsCiAgICAgICAgICAgICJ0eXBlIiA6ICJGdW5jdGlvbkRlZiIsCiAgICAgICAgICAgICJleHByZXNzaW9uIiA6IHsKICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjI4OTo1Mi0yODk6NjIiLAogICAgICAgICAgICAgICAicGF0aCIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAidHlwZSIgOiAiUHJvcGVydHkiLAogICAgICAgICAgICAgICAic291cmNlIiA6IHsKICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjI4OTo1Mi0yODk6NTYiLAogICAgICAgICAgICAgICAgICAibmFtZSIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAgICAidHlwZSIgOiAiT3BlcmFuZFJlZiIKICAgICAgICAgICAgICAgfQogICAgICAgICAgICB9LAogICAgICAgICAgICAib3BlcmFuZCIgOiBbIHsKICAgICAgICAgICAgICAgIm5hbWUiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgIm9wZXJhbmRUeXBlU3BlY2lmaWVyIiA6IHsKICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjI4OTozMi0yODk6NDgiLAogICAgICAgICAgICAgICAgICAibmFtZSIgOiAie2h0dHA6Ly9obDcub3JnL2ZoaXJ9TWVhc3VyZVJlcG9ydFR5cGUiLAogICAgICAgICAgICAgICAgICAidHlwZSIgOiAiTmFtZWRUeXBlU3BlY2lmaWVyIgogICAgICAgICAgICAgICB9CiAgICAgICAgICAgIH0gXQogICAgICAgICB9LCB7CiAgICAgICAgICAgICJsb2NhdG9yIiA6ICIyOTA6MS0yOTA6NTYiLAogICAgICAgICAgICAibmFtZSIgOiAiVG9TdHJpbmciLAogICAgICAgICAgICAiY29udGV4dCIgOiAiUGF0aWVudCIsCiAgICAgICAgICAgICJhY2Nlc3NMZXZlbCIgOiAiUHVibGljIiwKICAgICAgICAgICAgInR5cGUiIDogIkZ1bmN0aW9uRGVmIiwKICAgICAgICAgICAgImV4cHJlc3Npb24iIDogewogICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMjkwOjQ2LTI5MDo1NiIsCiAgICAgICAgICAgICAgICJwYXRoIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICJ0eXBlIiA6ICJQcm9wZXJ0eSIsCiAgICAgICAgICAgICAgICJzb3VyY2UiIDogewogICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMjkwOjQ2LTI5MDo1MCIsCiAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJPcGVyYW5kUmVmIgogICAgICAgICAgICAgICB9CiAgICAgICAgICAgIH0sCiAgICAgICAgICAgICJvcGVyYW5kIiA6IFsgewogICAgICAgICAgICAgICAibmFtZSIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAib3BlcmFuZFR5cGVTcGVjaWZpZXIiIDogewogICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMjkwOjMyLTI5MDo0MiIsCiAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJ7aHR0cDovL2hsNy5vcmcvZmhpcn1NZWRpYVN0YXR1cyIsCiAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJOYW1lZFR5cGVTcGVjaWZpZXIiCiAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgfSBdCiAgICAgICAgIH0sIHsKICAgICAgICAgICAgImxvY2F0b3IiIDogIjI5MToxLTI5MTo3NSIsCiAgICAgICAgICAgICJuYW1lIiA6ICJUb1N0cmluZyIsCiAgICAgICAgICAgICJjb250ZXh0IiA6ICJQYXRpZW50IiwKICAgICAgICAgICAgImFjY2Vzc0xldmVsIiA6ICJQdWJsaWMiLAogICAgICAgICAgICAidHlwZSIgOiAiRnVuY3Rpb25EZWYiLAogICAgICAgICAgICAiZXhwcmVzc2lvbiIgOiB7CiAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIyOTE6NjUtMjkxOjc1IiwKICAgICAgICAgICAgICAgInBhdGgiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgInR5cGUiIDogIlByb3BlcnR5IiwKICAgICAgICAgICAgICAgInNvdXJjZSIgOiB7CiAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIyOTE6NjUtMjkxOjY5IiwKICAgICAgICAgICAgICAgICAgIm5hbWUiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk9wZXJhbmRSZWYiCiAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgfSwKICAgICAgICAgICAgIm9wZXJhbmQiIDogWyB7CiAgICAgICAgICAgICAgICJuYW1lIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICJvcGVyYW5kVHlwZVNwZWNpZmllciIgOiB7CiAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIyOTE6MzItMjkxOjYxIiwKICAgICAgICAgICAgICAgICAgIm5hbWUiIDogIntodHRwOi8vaGw3Lm9yZy9maGlyfU1lZGljYXRpb25BZG1pbmlzdHJhdGlvblN0YXR1cyIsCiAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJOYW1lZFR5cGVTcGVjaWZpZXIiCiAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgfSBdCiAgICAgICAgIH0sIHsKICAgICAgICAgICAgImxvY2F0b3IiIDogIjI5MjoxLTI5Mjo2OSIsCiAgICAgICAgICAgICJuYW1lIiA6ICJUb1N0cmluZyIsCiAgICAgICAgICAgICJjb250ZXh0IiA6ICJQYXRpZW50IiwKICAgICAgICAgICAgImFjY2Vzc0xldmVsIiA6ICJQdWJsaWMiLAogICAgICAgICAgICAidHlwZSIgOiAiRnVuY3Rpb25EZWYiLAogICAgICAgICAgICAiZXhwcmVzc2lvbiIgOiB7CiAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIyOTI6NTktMjkyOjY5IiwKICAgICAgICAgICAgICAgInBhdGgiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgInR5cGUiIDogIlByb3BlcnR5IiwKICAgICAgICAgICAgICAgInNvdXJjZSIgOiB7CiAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIyOTI6NTktMjkyOjYzIiwKICAgICAgICAgICAgICAgICAgIm5hbWUiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk9wZXJhbmRSZWYiCiAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgfSwKICAgICAgICAgICAgIm9wZXJhbmQiIDogWyB7CiAgICAgICAgICAgICAgICJuYW1lIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICJvcGVyYW5kVHlwZVNwZWNpZmllciIgOiB7CiAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIyOTI6MzItMjkyOjU1IiwKICAgICAgICAgICAgICAgICAgIm5hbWUiIDogIntodHRwOi8vaGw3Lm9yZy9maGlyfU1lZGljYXRpb25EaXNwZW5zZVN0YXR1cyIsCiAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJOYW1lZFR5cGVTcGVjaWZpZXIiCiAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgfSBdCiAgICAgICAgIH0sIHsKICAgICAgICAgICAgImxvY2F0b3IiIDogIjI5MzoxLTI5Mzo3MCIsCiAgICAgICAgICAgICJuYW1lIiA6ICJUb1N0cmluZyIsCiAgICAgICAgICAgICJjb250ZXh0IiA6ICJQYXRpZW50IiwKICAgICAgICAgICAgImFjY2Vzc0xldmVsIiA6ICJQdWJsaWMiLAogICAgICAgICAgICAidHlwZSIgOiAiRnVuY3Rpb25EZWYiLAogICAgICAgICAgICAiZXhwcmVzc2lvbiIgOiB7CiAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIyOTM6NjAtMjkzOjcwIiwKICAgICAgICAgICAgICAgInBhdGgiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgInR5cGUiIDogIlByb3BlcnR5IiwKICAgICAgICAgICAgICAgInNvdXJjZSIgOiB7CiAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIyOTM6NjAtMjkzOjY0IiwKICAgICAgICAgICAgICAgICAgIm5hbWUiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk9wZXJhbmRSZWYiCiAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgfSwKICAgICAgICAgICAgIm9wZXJhbmQiIDogWyB7CiAgICAgICAgICAgICAgICJuYW1lIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICJvcGVyYW5kVHlwZVNwZWNpZmllciIgOiB7CiAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIyOTM6MzItMjkzOjU2IiwKICAgICAgICAgICAgICAgICAgIm5hbWUiIDogIntodHRwOi8vaGw3Lm9yZy9maGlyfU1lZGljYXRpb25Lbm93bGVkZ2VTdGF0dXMiLAogICAgICAgICAgICAgICAgICAidHlwZSIgOiAiTmFtZWRUeXBlU3BlY2lmaWVyIgogICAgICAgICAgICAgICB9CiAgICAgICAgICAgIH0gXQogICAgICAgICB9LCB7CiAgICAgICAgICAgICJsb2NhdG9yIiA6ICIyOTQ6MS0yOTQ6NjgiLAogICAgICAgICAgICAibmFtZSIgOiAiVG9TdHJpbmciLAogICAgICAgICAgICAiY29udGV4dCIgOiAiUGF0aWVudCIsCiAgICAgICAgICAgICJhY2Nlc3NMZXZlbCIgOiAiUHVibGljIiwKICAgICAgICAgICAgInR5cGUiIDogIkZ1bmN0aW9uRGVmIiwKICAgICAgICAgICAgImV4cHJlc3Npb24iIDogewogICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMjk0OjU4LTI5NDo2OCIsCiAgICAgICAgICAgICAgICJwYXRoIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICJ0eXBlIiA6ICJQcm9wZXJ0eSIsCiAgICAgICAgICAgICAgICJzb3VyY2UiIDogewogICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMjk0OjU4LTI5NDo2MiIsCiAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJPcGVyYW5kUmVmIgogICAgICAgICAgICAgICB9CiAgICAgICAgICAgIH0sCiAgICAgICAgICAgICJvcGVyYW5kIiA6IFsgewogICAgICAgICAgICAgICAibmFtZSIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAib3BlcmFuZFR5cGVTcGVjaWZpZXIiIDogewogICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMjk0OjMyLTI5NDo1NCIsCiAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJ7aHR0cDovL2hsNy5vcmcvZmhpcn1NZWRpY2F0aW9uUmVxdWVzdEludGVudCIsCiAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJOYW1lZFR5cGVTcGVjaWZpZXIiCiAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgfSBdCiAgICAgICAgIH0sIHsKICAgICAgICAgICAgImxvY2F0b3IiIDogIjI5NToxLTI5NTo3MCIsCiAgICAgICAgICAgICJuYW1lIiA6ICJUb1N0cmluZyIsCiAgICAgICAgICAgICJjb250ZXh0IiA6ICJQYXRpZW50IiwKICAgICAgICAgICAgImFjY2Vzc0xldmVsIiA6ICJQdWJsaWMiLAogICAgICAgICAgICAidHlwZSIgOiAiRnVuY3Rpb25EZWYiLAogICAgICAgICAgICAiZXhwcmVzc2lvbiIgOiB7CiAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIyOTU6NjAtMjk1OjcwIiwKICAgICAgICAgICAgICAgInBhdGgiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgInR5cGUiIDogIlByb3BlcnR5IiwKICAgICAgICAgICAgICAgInNvdXJjZSIgOiB7CiAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIyOTU6NjAtMjk1OjY0IiwKICAgICAgICAgICAgICAgICAgIm5hbWUiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk9wZXJhbmRSZWYiCiAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgfSwKICAgICAgICAgICAgIm9wZXJhbmQiIDogWyB7CiAgICAgICAgICAgICAgICJuYW1lIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICJvcGVyYW5kVHlwZVNwZWNpZmllciIgOiB7CiAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIyOTU6MzItMjk1OjU2IiwKICAgICAgICAgICAgICAgICAgIm5hbWUiIDogIntodHRwOi8vaGw3Lm9yZy9maGlyfU1lZGljYXRpb25SZXF1ZXN0UHJpb3JpdHkiLAogICAgICAgICAgICAgICAgICAidHlwZSIgOiAiTmFtZWRUeXBlU3BlY2lmaWVyIgogICAgICAgICAgICAgICB9CiAgICAgICAgICAgIH0gXQogICAgICAgICB9LCB7CiAgICAgICAgICAgICJsb2NhdG9yIiA6ICIyOTY6MS0yOTY6NjgiLAogICAgICAgICAgICAibmFtZSIgOiAiVG9TdHJpbmciLAogICAgICAgICAgICAiY29udGV4dCIgOiAiUGF0aWVudCIsCiAgICAgICAgICAgICJhY2Nlc3NMZXZlbCIgOiAiUHVibGljIiwKICAgICAgICAgICAgInR5cGUiIDogIkZ1bmN0aW9uRGVmIiwKICAgICAgICAgICAgImV4cHJlc3Npb24iIDogewogICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMjk2OjU4LTI5Njo2OCIsCiAgICAgICAgICAgICAgICJwYXRoIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICJ0eXBlIiA6ICJQcm9wZXJ0eSIsCiAgICAgICAgICAgICAgICJzb3VyY2UiIDogewogICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMjk2OjU4LTI5Njo2MiIsCiAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJPcGVyYW5kUmVmIgogICAgICAgICAgICAgICB9CiAgICAgICAgICAgIH0sCiAgICAgICAgICAgICJvcGVyYW5kIiA6IFsgewogICAgICAgICAgICAgICAibmFtZSIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAib3BlcmFuZFR5cGVTcGVjaWZpZXIiIDogewogICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMjk2OjMyLTI5Njo1NCIsCiAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJ7aHR0cDovL2hsNy5vcmcvZmhpcn1NZWRpY2F0aW9uUmVxdWVzdFN0YXR1cyIsCiAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJOYW1lZFR5cGVTcGVjaWZpZXIiCiAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgfSBdCiAgICAgICAgIH0sIHsKICAgICAgICAgICAgImxvY2F0b3IiIDogIjI5NzoxLTI5Nzo3MCIsCiAgICAgICAgICAgICJuYW1lIiA6ICJUb1N0cmluZyIsCiAgICAgICAgICAgICJjb250ZXh0IiA6ICJQYXRpZW50IiwKICAgICAgICAgICAgImFjY2Vzc0xldmVsIiA6ICJQdWJsaWMiLAogICAgICAgICAgICAidHlwZSIgOiAiRnVuY3Rpb25EZWYiLAogICAgICAgICAgICAiZXhwcmVzc2lvbiIgOiB7CiAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIyOTc6NjAtMjk3OjcwIiwKICAgICAgICAgICAgICAgInBhdGgiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgInR5cGUiIDogIlByb3BlcnR5IiwKICAgICAgICAgICAgICAgInNvdXJjZSIgOiB7CiAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIyOTc6NjAtMjk3OjY0IiwKICAgICAgICAgICAgICAgICAgIm5hbWUiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk9wZXJhbmRSZWYiCiAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgfSwKICAgICAgICAgICAgIm9wZXJhbmQiIDogWyB7CiAgICAgICAgICAgICAgICJuYW1lIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICJvcGVyYW5kVHlwZVNwZWNpZmllciIgOiB7CiAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIyOTc6MzItMjk3OjU2IiwKICAgICAgICAgICAgICAgICAgIm5hbWUiIDogIntodHRwOi8vaGw3Lm9yZy9maGlyfU1lZGljYXRpb25TdGF0ZW1lbnRTdGF0dXMiLAogICAgICAgICAgICAgICAgICAidHlwZSIgOiAiTmFtZWRUeXBlU3BlY2lmaWVyIgogICAgICAgICAgICAgICB9CiAgICAgICAgICAgIH0gXQogICAgICAgICB9LCB7CiAgICAgICAgICAgICJsb2NhdG9yIiA6ICIyOTg6MS0yOTg6NjEiLAogICAgICAgICAgICAibmFtZSIgOiAiVG9TdHJpbmciLAogICAgICAgICAgICAiY29udGV4dCIgOiAiUGF0aWVudCIsCiAgICAgICAgICAgICJhY2Nlc3NMZXZlbCIgOiAiUHVibGljIiwKICAgICAgICAgICAgInR5cGUiIDogIkZ1bmN0aW9uRGVmIiwKICAgICAgICAgICAgImV4cHJlc3Npb24iIDogewogICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMjk4OjUxLTI5ODo2MSIsCiAgICAgICAgICAgICAgICJwYXRoIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICJ0eXBlIiA6ICJQcm9wZXJ0eSIsCiAgICAgICAgICAgICAgICJzb3VyY2UiIDogewogICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMjk4OjUxLTI5ODo1NSIsCiAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJPcGVyYW5kUmVmIgogICAgICAgICAgICAgICB9CiAgICAgICAgICAgIH0sCiAgICAgICAgICAgICJvcGVyYW5kIiA6IFsgewogICAgICAgICAgICAgICAibmFtZSIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAib3BlcmFuZFR5cGVTcGVjaWZpZXIiIDogewogICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMjk4OjMyLTI5ODo0NyIsCiAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJ7aHR0cDovL2hsNy5vcmcvZmhpcn1NZWRpY2F0aW9uU3RhdHVzIiwKICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk5hbWVkVHlwZVNwZWNpZmllciIKICAgICAgICAgICAgICAgfQogICAgICAgICAgICB9IF0KICAgICAgICAgfSwgewogICAgICAgICAgICAibG9jYXRvciIgOiAiMjk5OjEtMjk5OjcyIiwKICAgICAgICAgICAgIm5hbWUiIDogIlRvU3RyaW5nIiwKICAgICAgICAgICAgImNvbnRleHQiIDogIlBhdGllbnQiLAogICAgICAgICAgICAiYWNjZXNzTGV2ZWwiIDogIlB1YmxpYyIsCiAgICAgICAgICAgICJ0eXBlIiA6ICJGdW5jdGlvbkRlZiIsCiAgICAgICAgICAgICJleHByZXNzaW9uIiA6IHsKICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjI5OTo2Mi0yOTk6NzIiLAogICAgICAgICAgICAgICAicGF0aCIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAidHlwZSIgOiAiUHJvcGVydHkiLAogICAgICAgICAgICAgICAic291cmNlIiA6IHsKICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjI5OTo2Mi0yOTk6NjYiLAogICAgICAgICAgICAgICAgICAibmFtZSIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAgICAidHlwZSIgOiAiT3BlcmFuZFJlZiIKICAgICAgICAgICAgICAgfQogICAgICAgICAgICB9LAogICAgICAgICAgICAib3BlcmFuZCIgOiBbIHsKICAgICAgICAgICAgICAgIm5hbWUiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgIm9wZXJhbmRUeXBlU3BlY2lmaWVyIiA6IHsKICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjI5OTozMi0yOTk6NTgiLAogICAgICAgICAgICAgICAgICAibmFtZSIgOiAie2h0dHA6Ly9obDcub3JnL2ZoaXJ9TWVzc2FnZVNpZ25pZmljYW5jZUNhdGVnb3J5IiwKICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk5hbWVkVHlwZVNwZWNpZmllciIKICAgICAgICAgICAgICAgfQogICAgICAgICAgICB9IF0KICAgICAgICAgfSwgewogICAgICAgICAgICAibG9jYXRvciIgOiAiMzAwOjEtMzAwOjc1IiwKICAgICAgICAgICAgIm5hbWUiIDogIlRvU3RyaW5nIiwKICAgICAgICAgICAgImNvbnRleHQiIDogIlBhdGllbnQiLAogICAgICAgICAgICAiYWNjZXNzTGV2ZWwiIDogIlB1YmxpYyIsCiAgICAgICAgICAgICJ0eXBlIiA6ICJGdW5jdGlvbkRlZiIsCiAgICAgICAgICAgICJleHByZXNzaW9uIiA6IHsKICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjMwMDo2NS0zMDA6NzUiLAogICAgICAgICAgICAgICAicGF0aCIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAidHlwZSIgOiAiUHJvcGVydHkiLAogICAgICAgICAgICAgICAic291cmNlIiA6IHsKICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjMwMDo2NS0zMDA6NjkiLAogICAgICAgICAgICAgICAgICAibmFtZSIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAgICAidHlwZSIgOiAiT3BlcmFuZFJlZiIKICAgICAgICAgICAgICAgfQogICAgICAgICAgICB9LAogICAgICAgICAgICAib3BlcmFuZCIgOiBbIHsKICAgICAgICAgICAgICAgIm5hbWUiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgIm9wZXJhbmRUeXBlU3BlY2lmaWVyIiA6IHsKICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjMwMDozMi0zMDA6NjEiLAogICAgICAgICAgICAgICAgICAibmFtZSIgOiAie2h0dHA6Ly9obDcub3JnL2ZoaXJ9TWVzc2FnZWhlYWRlcl9SZXNwb25zZV9SZXF1ZXN0IiwKICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk5hbWVkVHlwZVNwZWNpZmllciIKICAgICAgICAgICAgICAgfQogICAgICAgICAgICB9IF0KICAgICAgICAgfSwgewogICAgICAgICAgICAibG9jYXRvciIgOiAiMzAxOjEtMzAxOjUzIiwKICAgICAgICAgICAgIm5hbWUiIDogIlRvU3RyaW5nIiwKICAgICAgICAgICAgImNvbnRleHQiIDogIlBhdGllbnQiLAogICAgICAgICAgICAiYWNjZXNzTGV2ZWwiIDogIlB1YmxpYyIsCiAgICAgICAgICAgICJ0eXBlIiA6ICJGdW5jdGlvbkRlZiIsCiAgICAgICAgICAgICJleHByZXNzaW9uIiA6IHsKICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjMwMTo0My0zMDE6NTMiLAogICAgICAgICAgICAgICAicGF0aCIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAidHlwZSIgOiAiUHJvcGVydHkiLAogICAgICAgICAgICAgICAic291cmNlIiA6IHsKICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjMwMTo0My0zMDE6NDciLAogICAgICAgICAgICAgICAgICAibmFtZSIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAgICAidHlwZSIgOiAiT3BlcmFuZFJlZiIKICAgICAgICAgICAgICAgfQogICAgICAgICAgICB9LAogICAgICAgICAgICAib3BlcmFuZCIgOiBbIHsKICAgICAgICAgICAgICAgIm5hbWUiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgIm9wZXJhbmRUeXBlU3BlY2lmaWVyIiA6IHsKICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjMwMTozMi0zMDE6MzkiLAogICAgICAgICAgICAgICAgICAibmFtZSIgOiAie2h0dHA6Ly9obDcub3JnL2ZoaXJ9TWltZVR5cGUiLAogICAgICAgICAgICAgICAgICAidHlwZSIgOiAiTmFtZWRUeXBlU3BlY2lmaWVyIgogICAgICAgICAgICAgICB9CiAgICAgICAgICAgIH0gXQogICAgICAgICB9LCB7CiAgICAgICAgICAgICJsb2NhdG9yIiA6ICIzMDI6MS0zMDI6NTIiLAogICAgICAgICAgICAibmFtZSIgOiAiVG9TdHJpbmciLAogICAgICAgICAgICAiY29udGV4dCIgOiAiUGF0aWVudCIsCiAgICAgICAgICAgICJhY2Nlc3NMZXZlbCIgOiAiUHVibGljIiwKICAgICAgICAgICAgInR5cGUiIDogIkZ1bmN0aW9uRGVmIiwKICAgICAgICAgICAgImV4cHJlc3Npb24iIDogewogICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMzAyOjQyLTMwMjo1MiIsCiAgICAgICAgICAgICAgICJwYXRoIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICJ0eXBlIiA6ICJQcm9wZXJ0eSIsCiAgICAgICAgICAgICAgICJzb3VyY2UiIDogewogICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMzAyOjQyLTMwMjo0NiIsCiAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJPcGVyYW5kUmVmIgogICAgICAgICAgICAgICB9CiAgICAgICAgICAgIH0sCiAgICAgICAgICAgICJvcGVyYW5kIiA6IFsgewogICAgICAgICAgICAgICAibmFtZSIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAib3BlcmFuZFR5cGVTcGVjaWZpZXIiIDogewogICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMzAyOjMyLTMwMjozOCIsCiAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJ7aHR0cDovL2hsNy5vcmcvZmhpcn1OYW1lVXNlIiwKICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk5hbWVkVHlwZVNwZWNpZmllciIKICAgICAgICAgICAgICAgfQogICAgICAgICAgICB9IF0KICAgICAgICAgfSwgewogICAgICAgICAgICAibG9jYXRvciIgOiAiMzAzOjEtMzAzOjcxIiwKICAgICAgICAgICAgIm5hbWUiIDogIlRvU3RyaW5nIiwKICAgICAgICAgICAgImNvbnRleHQiIDogIlBhdGllbnQiLAogICAgICAgICAgICAiYWNjZXNzTGV2ZWwiIDogIlB1YmxpYyIsCiAgICAgICAgICAgICJ0eXBlIiA6ICJGdW5jdGlvbkRlZiIsCiAgICAgICAgICAgICJleHByZXNzaW9uIiA6IHsKICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjMwMzo2MS0zMDM6NzEiLAogICAgICAgICAgICAgICAicGF0aCIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAidHlwZSIgOiAiUHJvcGVydHkiLAogICAgICAgICAgICAgICAic291cmNlIiA6IHsKICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjMwMzo2MS0zMDM6NjUiLAogICAgICAgICAgICAgICAgICAibmFtZSIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAgICAidHlwZSIgOiAiT3BlcmFuZFJlZiIKICAgICAgICAgICAgICAgfQogICAgICAgICAgICB9LAogICAgICAgICAgICAib3BlcmFuZCIgOiBbIHsKICAgICAgICAgICAgICAgIm5hbWUiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgIm9wZXJhbmRUeXBlU3BlY2lmaWVyIiA6IHsKICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjMwMzozMi0zMDM6NTciLAogICAgICAgICAgICAgICAgICAibmFtZSIgOiAie2h0dHA6Ly9obDcub3JnL2ZoaXJ9TmFtaW5nU3lzdGVtSWRlbnRpZmllclR5cGUiLAogICAgICAgICAgICAgICAgICAidHlwZSIgOiAiTmFtZWRUeXBlU3BlY2lmaWVyIgogICAgICAgICAgICAgICB9CiAgICAgICAgICAgIH0gXQogICAgICAgICB9LCB7CiAgICAgICAgICAgICJsb2NhdG9yIiA6ICIzMDQ6MS0zMDQ6NjEiLAogICAgICAgICAgICAibmFtZSIgOiAiVG9TdHJpbmciLAogICAgICAgICAgICAiY29udGV4dCIgOiAiUGF0aWVudCIsCiAgICAgICAgICAgICJhY2Nlc3NMZXZlbCIgOiAiUHVibGljIiwKICAgICAgICAgICAgInR5cGUiIDogIkZ1bmN0aW9uRGVmIiwKICAgICAgICAgICAgImV4cHJlc3Npb24iIDogewogICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMzA0OjUxLTMwNDo2MSIsCiAgICAgICAgICAgICAgICJwYXRoIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICJ0eXBlIiA6ICJQcm9wZXJ0eSIsCiAgICAgICAgICAgICAgICJzb3VyY2UiIDogewogICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMzA0OjUxLTMwNDo1NSIsCiAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJPcGVyYW5kUmVmIgogICAgICAgICAgICAgICB9CiAgICAgICAgICAgIH0sCiAgICAgICAgICAgICJvcGVyYW5kIiA6IFsgewogICAgICAgICAgICAgICAibmFtZSIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAib3BlcmFuZFR5cGVTcGVjaWZpZXIiIDogewogICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMzA0OjMyLTMwNDo0NyIsCiAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJ7aHR0cDovL2hsNy5vcmcvZmhpcn1OYW1pbmdTeXN0ZW1UeXBlIiwKICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk5hbWVkVHlwZVNwZWNpZmllciIKICAgICAgICAgICAgICAgfQogICAgICAgICAgICB9IF0KICAgICAgICAgfSwgewogICAgICAgICAgICAibG9jYXRvciIgOiAiMzA1OjEtMzA1OjYwIiwKICAgICAgICAgICAgIm5hbWUiIDogIlRvU3RyaW5nIiwKICAgICAgICAgICAgImNvbnRleHQiIDogIlBhdGllbnQiLAogICAgICAgICAgICAiYWNjZXNzTGV2ZWwiIDogIlB1YmxpYyIsCiAgICAgICAgICAgICJ0eXBlIiA6ICJGdW5jdGlvbkRlZiIsCiAgICAgICAgICAgICJleHByZXNzaW9uIiA6IHsKICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjMwNTo1MC0zMDU6NjAiLAogICAgICAgICAgICAgICAicGF0aCIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAidHlwZSIgOiAiUHJvcGVydHkiLAogICAgICAgICAgICAgICAic291cmNlIiA6IHsKICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjMwNTo1MC0zMDU6NTQiLAogICAgICAgICAgICAgICAgICAibmFtZSIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAgICAidHlwZSIgOiAiT3BlcmFuZFJlZiIKICAgICAgICAgICAgICAgfQogICAgICAgICAgICB9LAogICAgICAgICAgICAib3BlcmFuZCIgOiBbIHsKICAgICAgICAgICAgICAgIm5hbWUiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgIm9wZXJhbmRUeXBlU3BlY2lmaWVyIiA6IHsKICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjMwNTozMi0zMDU6NDYiLAogICAgICAgICAgICAgICAgICAibmFtZSIgOiAie2h0dHA6Ly9obDcub3JnL2ZoaXJ9TmFycmF0aXZlU3RhdHVzIiwKICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk5hbWVkVHlwZVNwZWNpZmllciIKICAgICAgICAgICAgICAgfQogICAgICAgICAgICB9IF0KICAgICAgICAgfSwgewogICAgICAgICAgICAibG9jYXRvciIgOiAiMzA2OjEtMzA2OjUzIiwKICAgICAgICAgICAgIm5hbWUiIDogIlRvU3RyaW5nIiwKICAgICAgICAgICAgImNvbnRleHQiIDogIlBhdGllbnQiLAogICAgICAgICAgICAiYWNjZXNzTGV2ZWwiIDogIlB1YmxpYyIsCiAgICAgICAgICAgICJ0eXBlIiA6ICJGdW5jdGlvbkRlZiIsCiAgICAgICAgICAgICJleHByZXNzaW9uIiA6IHsKICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjMwNjo0My0zMDY6NTMiLAogICAgICAgICAgICAgICAicGF0aCIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAidHlwZSIgOiAiUHJvcGVydHkiLAogICAgICAgICAgICAgICAic291cmNlIiA6IHsKICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjMwNjo0My0zMDY6NDciLAogICAgICAgICAgICAgICAgICAibmFtZSIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAgICAidHlwZSIgOiAiT3BlcmFuZFJlZiIKICAgICAgICAgICAgICAgfQogICAgICAgICAgICB9LAogICAgICAgICAgICAib3BlcmFuZCIgOiBbIHsKICAgICAgICAgICAgICAgIm5hbWUiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgIm9wZXJhbmRUeXBlU3BlY2lmaWVyIiA6IHsKICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjMwNjozMi0zMDY6MzkiLAogICAgICAgICAgICAgICAgICAibmFtZSIgOiAie2h0dHA6Ly9obDcub3JnL2ZoaXJ9Tm90ZVR5cGUiLAogICAgICAgICAgICAgICAgICAidHlwZSIgOiAiTmFtZWRUeXBlU3BlY2lmaWVyIgogICAgICAgICAgICAgICB9CiAgICAgICAgICAgIH0gXQogICAgICAgICB9LCB7CiAgICAgICAgICAgICJsb2NhdG9yIiA6ICIzMDc6MS0zMDc6NjYiLAogICAgICAgICAgICAibmFtZSIgOiAiVG9TdHJpbmciLAogICAgICAgICAgICAiY29udGV4dCIgOiAiUGF0aWVudCIsCiAgICAgICAgICAgICJhY2Nlc3NMZXZlbCIgOiAiUHVibGljIiwKICAgICAgICAgICAgInR5cGUiIDogIkZ1bmN0aW9uRGVmIiwKICAgICAgICAgICAgImV4cHJlc3Npb24iIDogewogICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMzA3OjU2LTMwNzo2NiIsCiAgICAgICAgICAgICAgICJwYXRoIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICJ0eXBlIiA6ICJQcm9wZXJ0eSIsCiAgICAgICAgICAgICAgICJzb3VyY2UiIDogewogICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMzA3OjU2LTMwNzo2MCIsCiAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJPcGVyYW5kUmVmIgogICAgICAgICAgICAgICB9CiAgICAgICAgICAgIH0sCiAgICAgICAgICAgICJvcGVyYW5kIiA6IFsgewogICAgICAgICAgICAgICAibmFtZSIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAib3BlcmFuZFR5cGVTcGVjaWZpZXIiIDogewogICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMzA3OjMyLTMwNzo1MiIsCiAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJ7aHR0cDovL2hsNy5vcmcvZmhpcn1OdXRyaXRpaW9uT3JkZXJJbnRlbnQiLAogICAgICAgICAgICAgICAgICAidHlwZSIgOiAiTmFtZWRUeXBlU3BlY2lmaWVyIgogICAgICAgICAgICAgICB9CiAgICAgICAgICAgIH0gXQogICAgICAgICB9LCB7CiAgICAgICAgICAgICJsb2NhdG9yIiA6ICIzMDg6MS0zMDg6NjUiLAogICAgICAgICAgICAibmFtZSIgOiAiVG9TdHJpbmciLAogICAgICAgICAgICAiY29udGV4dCIgOiAiUGF0aWVudCIsCiAgICAgICAgICAgICJhY2Nlc3NMZXZlbCIgOiAiUHVibGljIiwKICAgICAgICAgICAgInR5cGUiIDogIkZ1bmN0aW9uRGVmIiwKICAgICAgICAgICAgImV4cHJlc3Npb24iIDogewogICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMzA4OjU1LTMwODo2NSIsCiAgICAgICAgICAgICAgICJwYXRoIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICJ0eXBlIiA6ICJQcm9wZXJ0eSIsCiAgICAgICAgICAgICAgICJzb3VyY2UiIDogewogICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMzA4OjU1LTMwODo1OSIsCiAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJPcGVyYW5kUmVmIgogICAgICAgICAgICAgICB9CiAgICAgICAgICAgIH0sCiAgICAgICAgICAgICJvcGVyYW5kIiA6IFsgewogICAgICAgICAgICAgICAibmFtZSIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAib3BlcmFuZFR5cGVTcGVjaWZpZXIiIDogewogICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMzA4OjMyLTMwODo1MSIsCiAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJ7aHR0cDovL2hsNy5vcmcvZmhpcn1OdXRyaXRpb25PcmRlclN0YXR1cyIsCiAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJOYW1lZFR5cGVTcGVjaWZpZXIiCiAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgfSBdCiAgICAgICAgIH0sIHsKICAgICAgICAgICAgImxvY2F0b3IiIDogIjMwOToxLTMwOTo2NCIsCiAgICAgICAgICAgICJuYW1lIiA6ICJUb1N0cmluZyIsCiAgICAgICAgICAgICJjb250ZXh0IiA6ICJQYXRpZW50IiwKICAgICAgICAgICAgImFjY2Vzc0xldmVsIiA6ICJQdWJsaWMiLAogICAgICAgICAgICAidHlwZSIgOiAiRnVuY3Rpb25EZWYiLAogICAgICAgICAgICAiZXhwcmVzc2lvbiIgOiB7CiAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIzMDk6NTQtMzA5OjY0IiwKICAgICAgICAgICAgICAgInBhdGgiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgInR5cGUiIDogIlByb3BlcnR5IiwKICAgICAgICAgICAgICAgInNvdXJjZSIgOiB7CiAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIzMDk6NTQtMzA5OjU4IiwKICAgICAgICAgICAgICAgICAgIm5hbWUiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk9wZXJhbmRSZWYiCiAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgfSwKICAgICAgICAgICAgIm9wZXJhbmQiIDogWyB7CiAgICAgICAgICAgICAgICJuYW1lIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICJvcGVyYW5kVHlwZVNwZWNpZmllciIgOiB7CiAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIzMDk6MzItMzA5OjUwIiwKICAgICAgICAgICAgICAgICAgIm5hbWUiIDogIntodHRwOi8vaGw3Lm9yZy9maGlyfU9ic2VydmF0aW9uRGF0YVR5cGUiLAogICAgICAgICAgICAgICAgICAidHlwZSIgOiAiTmFtZWRUeXBlU3BlY2lmaWVyIgogICAgICAgICAgICAgICB9CiAgICAgICAgICAgIH0gXQogICAgICAgICB9LCB7CiAgICAgICAgICAgICJsb2NhdG9yIiA6ICIzMTA6MS0zMTA6NjkiLAogICAgICAgICAgICAibmFtZSIgOiAiVG9TdHJpbmciLAogICAgICAgICAgICAiY29udGV4dCIgOiAiUGF0aWVudCIsCiAgICAgICAgICAgICJhY2Nlc3NMZXZlbCIgOiAiUHVibGljIiwKICAgICAgICAgICAgInR5cGUiIDogIkZ1bmN0aW9uRGVmIiwKICAgICAgICAgICAgImV4cHJlc3Npb24iIDogewogICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMzEwOjU5LTMxMDo2OSIsCiAgICAgICAgICAgICAgICJwYXRoIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICJ0eXBlIiA6ICJQcm9wZXJ0eSIsCiAgICAgICAgICAgICAgICJzb3VyY2UiIDogewogICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMzEwOjU5LTMxMDo2MyIsCiAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJPcGVyYW5kUmVmIgogICAgICAgICAgICAgICB9CiAgICAgICAgICAgIH0sCiAgICAgICAgICAgICJvcGVyYW5kIiA6IFsgewogICAgICAgICAgICAgICAibmFtZSIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAib3BlcmFuZFR5cGVTcGVjaWZpZXIiIDogewogICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMzEwOjMyLTMxMDo1NSIsCiAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJ7aHR0cDovL2hsNy5vcmcvZmhpcn1PYnNlcnZhdGlvblJhbmdlQ2F0ZWdvcnkiLAogICAgICAgICAgICAgICAgICAidHlwZSIgOiAiTmFtZWRUeXBlU3BlY2lmaWVyIgogICAgICAgICAgICAgICB9CiAgICAgICAgICAgIH0gXQogICAgICAgICB9LCB7CiAgICAgICAgICAgICJsb2NhdG9yIiA6ICIzMTE6MS0zMTE6NjIiLAogICAgICAgICAgICAibmFtZSIgOiAiVG9TdHJpbmciLAogICAgICAgICAgICAiY29udGV4dCIgOiAiUGF0aWVudCIsCiAgICAgICAgICAgICJhY2Nlc3NMZXZlbCIgOiAiUHVibGljIiwKICAgICAgICAgICAgInR5cGUiIDogIkZ1bmN0aW9uRGVmIiwKICAgICAgICAgICAgImV4cHJlc3Npb24iIDogewogICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMzExOjUyLTMxMTo2MiIsCiAgICAgICAgICAgICAgICJwYXRoIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICJ0eXBlIiA6ICJQcm9wZXJ0eSIsCiAgICAgICAgICAgICAgICJzb3VyY2UiIDogewogICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMzExOjUyLTMxMTo1NiIsCiAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJPcGVyYW5kUmVmIgogICAgICAgICAgICAgICB9CiAgICAgICAgICAgIH0sCiAgICAgICAgICAgICJvcGVyYW5kIiA6IFsgewogICAgICAgICAgICAgICAibmFtZSIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAib3BlcmFuZFR5cGVTcGVjaWZpZXIiIDogewogICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMzExOjMyLTMxMTo0OCIsCiAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJ7aHR0cDovL2hsNy5vcmcvZmhpcn1PYnNlcnZhdGlvblN0YXR1cyIsCiAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJOYW1lZFR5cGVTcGVjaWZpZXIiCiAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgfSBdCiAgICAgICAgIH0sIHsKICAgICAgICAgICAgImxvY2F0b3IiIDogIjMxMjoxLTMxMjo1OCIsCiAgICAgICAgICAgICJuYW1lIiA6ICJUb1N0cmluZyIsCiAgICAgICAgICAgICJjb250ZXh0IiA6ICJQYXRpZW50IiwKICAgICAgICAgICAgImFjY2Vzc0xldmVsIiA6ICJQdWJsaWMiLAogICAgICAgICAgICAidHlwZSIgOiAiRnVuY3Rpb25EZWYiLAogICAgICAgICAgICAiZXhwcmVzc2lvbiIgOiB7CiAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIzMTI6NDgtMzEyOjU4IiwKICAgICAgICAgICAgICAgInBhdGgiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgInR5cGUiIDogIlByb3BlcnR5IiwKICAgICAgICAgICAgICAgInNvdXJjZSIgOiB7CiAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIzMTI6NDgtMzEyOjUyIiwKICAgICAgICAgICAgICAgICAgIm5hbWUiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk9wZXJhbmRSZWYiCiAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgfSwKICAgICAgICAgICAgIm9wZXJhbmQiIDogWyB7CiAgICAgICAgICAgICAgICJuYW1lIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICJvcGVyYW5kVHlwZVNwZWNpZmllciIgOiB7CiAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIzMTI6MzItMzEyOjQ0IiwKICAgICAgICAgICAgICAgICAgIm5hbWUiIDogIntodHRwOi8vaGw3Lm9yZy9maGlyfU9wZXJhdGlvbktpbmQiLAogICAgICAgICAgICAgICAgICAidHlwZSIgOiAiTmFtZWRUeXBlU3BlY2lmaWVyIgogICAgICAgICAgICAgICB9CiAgICAgICAgICAgIH0gXQogICAgICAgICB9LCB7CiAgICAgICAgICAgICJsb2NhdG9yIiA6ICIzMTM6MS0zMTM6NjYiLAogICAgICAgICAgICAibmFtZSIgOiAiVG9TdHJpbmciLAogICAgICAgICAgICAiY29udGV4dCIgOiAiUGF0aWVudCIsCiAgICAgICAgICAgICJhY2Nlc3NMZXZlbCIgOiAiUHVibGljIiwKICAgICAgICAgICAgInR5cGUiIDogIkZ1bmN0aW9uRGVmIiwKICAgICAgICAgICAgImV4cHJlc3Npb24iIDogewogICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMzEzOjU2LTMxMzo2NiIsCiAgICAgICAgICAgICAgICJwYXRoIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICJ0eXBlIiA6ICJQcm9wZXJ0eSIsCiAgICAgICAgICAgICAgICJzb3VyY2UiIDogewogICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMzEzOjU2LTMxMzo2MCIsCiAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJPcGVyYW5kUmVmIgogICAgICAgICAgICAgICB9CiAgICAgICAgICAgIH0sCiAgICAgICAgICAgICJvcGVyYW5kIiA6IFsgewogICAgICAgICAgICAgICAibmFtZSIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAib3BlcmFuZFR5cGVTcGVjaWZpZXIiIDogewogICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMzEzOjMyLTMxMzo1MiIsCiAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJ7aHR0cDovL2hsNy5vcmcvZmhpcn1PcGVyYXRpb25QYXJhbWV0ZXJVc2UiLAogICAgICAgICAgICAgICAgICAidHlwZSIgOiAiTmFtZWRUeXBlU3BlY2lmaWVyIgogICAgICAgICAgICAgICB9CiAgICAgICAgICAgIH0gXQogICAgICAgICB9LCB7CiAgICAgICAgICAgICJsb2NhdG9yIiA6ICIzMTQ6MS0zMTQ6NjAiLAogICAgICAgICAgICAibmFtZSIgOiAiVG9TdHJpbmciLAogICAgICAgICAgICAiY29udGV4dCIgOiAiUGF0aWVudCIsCiAgICAgICAgICAgICJhY2Nlc3NMZXZlbCIgOiAiUHVibGljIiwKICAgICAgICAgICAgInR5cGUiIDogIkZ1bmN0aW9uRGVmIiwKICAgICAgICAgICAgImV4cHJlc3Npb24iIDogewogICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMzE0OjUwLTMxNDo2MCIsCiAgICAgICAgICAgICAgICJwYXRoIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICJ0eXBlIiA6ICJQcm9wZXJ0eSIsCiAgICAgICAgICAgICAgICJzb3VyY2UiIDogewogICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMzE0OjUwLTMxNDo1NCIsCiAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJPcGVyYW5kUmVmIgogICAgICAgICAgICAgICB9CiAgICAgICAgICAgIH0sCiAgICAgICAgICAgICJvcGVyYW5kIiA6IFsgewogICAgICAgICAgICAgICAibmFtZSIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAib3BlcmFuZFR5cGVTcGVjaWZpZXIiIDogewogICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMzE0OjMyLTMxNDo0NiIsCiAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJ7aHR0cDovL2hsNy5vcmcvZmhpcn1PcmllbnRhdGlvblR5cGUiLAogICAgICAgICAgICAgICAgICAidHlwZSIgOiAiTmFtZWRUeXBlU3BlY2lmaWVyIgogICAgICAgICAgICAgICB9CiAgICAgICAgICAgIH0gXQogICAgICAgICB9LCB7CiAgICAgICAgICAgICJsb2NhdG9yIiA6ICIzMTU6MS0zMTU6NTciLAogICAgICAgICAgICAibmFtZSIgOiAiVG9TdHJpbmciLAogICAgICAgICAgICAiY29udGV4dCIgOiAiUGF0aWVudCIsCiAgICAgICAgICAgICJhY2Nlc3NMZXZlbCIgOiAiUHVibGljIiwKICAgICAgICAgICAgInR5cGUiIDogIkZ1bmN0aW9uRGVmIiwKICAgICAgICAgICAgImV4cHJlc3Npb24iIDogewogICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMzE1OjQ3LTMxNTo1NyIsCiAgICAgICAgICAgICAgICJwYXRoIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICJ0eXBlIiA6ICJQcm9wZXJ0eSIsCiAgICAgICAgICAgICAgICJzb3VyY2UiIDogewogICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMzE1OjQ3LTMxNTo1MSIsCiAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJPcGVyYW5kUmVmIgogICAgICAgICAgICAgICB9CiAgICAgICAgICAgIH0sCiAgICAgICAgICAgICJvcGVyYW5kIiA6IFsgewogICAgICAgICAgICAgICAibmFtZSIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAib3BlcmFuZFR5cGVTcGVjaWZpZXIiIDogewogICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMzE1OjMyLTMxNTo0MyIsCiAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJ7aHR0cDovL2hsNy5vcmcvZmhpcn1QYXJhbWV0ZXJVc2UiLAogICAgICAgICAgICAgICAgICAidHlwZSIgOiAiTmFtZWRUeXBlU3BlY2lmaWVyIgogICAgICAgICAgICAgICB9CiAgICAgICAgICAgIH0gXQogICAgICAgICB9LCB7CiAgICAgICAgICAgICJsb2NhdG9yIiA6ICIzMTY6MS0zMTY6NjQiLAogICAgICAgICAgICAibmFtZSIgOiAiVG9TdHJpbmciLAogICAgICAgICAgICAiY29udGV4dCIgOiAiUGF0aWVudCIsCiAgICAgICAgICAgICJhY2Nlc3NMZXZlbCIgOiAiUHVibGljIiwKICAgICAgICAgICAgInR5cGUiIDogIkZ1bmN0aW9uRGVmIiwKICAgICAgICAgICAgImV4cHJlc3Npb24iIDogewogICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMzE2OjU0LTMxNjo2NCIsCiAgICAgICAgICAgICAgICJwYXRoIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICJ0eXBlIiA6ICJQcm9wZXJ0eSIsCiAgICAgICAgICAgICAgICJzb3VyY2UiIDogewogICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMzE2OjU0LTMxNjo1OCIsCiAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJPcGVyYW5kUmVmIgogICAgICAgICAgICAgICB9CiAgICAgICAgICAgIH0sCiAgICAgICAgICAgICJvcGVyYW5kIiA6IFsgewogICAgICAgICAgICAgICAibmFtZSIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAib3BlcmFuZFR5cGVTcGVjaWZpZXIiIDogewogICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMzE2OjMyLTMxNjo1MCIsCiAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJ7aHR0cDovL2hsNy5vcmcvZmhpcn1QYXJ0aWNpcGFudFJlcXVpcmVkIiwKICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk5hbWVkVHlwZVNwZWNpZmllciIKICAgICAgICAgICAgICAgfQogICAgICAgICAgICB9IF0KICAgICAgICAgfSwgewogICAgICAgICAgICAibG9jYXRvciIgOiAiMzE3OjEtMzE3OjYyIiwKICAgICAgICAgICAgIm5hbWUiIDogIlRvU3RyaW5nIiwKICAgICAgICAgICAgImNvbnRleHQiIDogIlBhdGllbnQiLAogICAgICAgICAgICAiYWNjZXNzTGV2ZWwiIDogIlB1YmxpYyIsCiAgICAgICAgICAgICJ0eXBlIiA6ICJGdW5jdGlvbkRlZiIsCiAgICAgICAgICAgICJleHByZXNzaW9uIiA6IHsKICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjMxNzo1Mi0zMTc6NjIiLAogICAgICAgICAgICAgICAicGF0aCIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAidHlwZSIgOiAiUHJvcGVydHkiLAogICAgICAgICAgICAgICAic291cmNlIiA6IHsKICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjMxNzo1Mi0zMTc6NTYiLAogICAgICAgICAgICAgICAgICAibmFtZSIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAgICAidHlwZSIgOiAiT3BlcmFuZFJlZiIKICAgICAgICAgICAgICAgfQogICAgICAgICAgICB9LAogICAgICAgICAgICAib3BlcmFuZCIgOiBbIHsKICAgICAgICAgICAgICAgIm5hbWUiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgIm9wZXJhbmRUeXBlU3BlY2lmaWVyIiA6IHsKICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjMxNzozMi0zMTc6NDgiLAogICAgICAgICAgICAgICAgICAibmFtZSIgOiAie2h0dHA6Ly9obDcub3JnL2ZoaXJ9UGFydGljaXBhbnRTdGF0dXMiLAogICAgICAgICAgICAgICAgICAidHlwZSIgOiAiTmFtZWRUeXBlU3BlY2lmaWVyIgogICAgICAgICAgICAgICB9CiAgICAgICAgICAgIH0gXQogICAgICAgICB9LCB7CiAgICAgICAgICAgICJsb2NhdG9yIiA6ICIzMTg6MS0zMTg6NjQiLAogICAgICAgICAgICAibmFtZSIgOiAiVG9TdHJpbmciLAogICAgICAgICAgICAiY29udGV4dCIgOiAiUGF0aWVudCIsCiAgICAgICAgICAgICJhY2Nlc3NMZXZlbCIgOiAiUHVibGljIiwKICAgICAgICAgICAgInR5cGUiIDogIkZ1bmN0aW9uRGVmIiwKICAgICAgICAgICAgImV4cHJlc3Npb24iIDogewogICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMzE4OjU0LTMxODo2NCIsCiAgICAgICAgICAgICAgICJwYXRoIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICJ0eXBlIiA6ICJQcm9wZXJ0eSIsCiAgICAgICAgICAgICAgICJzb3VyY2UiIDogewogICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMzE4OjU0LTMxODo1OCIsCiAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJPcGVyYW5kUmVmIgogICAgICAgICAgICAgICB9CiAgICAgICAgICAgIH0sCiAgICAgICAgICAgICJvcGVyYW5kIiA6IFsgewogICAgICAgICAgICAgICAibmFtZSIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAib3BlcmFuZFR5cGVTcGVjaWZpZXIiIDogewogICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMzE4OjMyLTMxODo1MCIsCiAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJ7aHR0cDovL2hsNy5vcmcvZmhpcn1QYXJ0aWNpcGF0aW9uU3RhdHVzIiwKICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk5hbWVkVHlwZVNwZWNpZmllciIKICAgICAgICAgICAgICAgfQogICAgICAgICAgICB9IF0KICAgICAgICAgfSwgewogICAgICAgICAgICAibG9jYXRvciIgOiAiMzE5OjEtMzE5OjY0IiwKICAgICAgICAgICAgIm5hbWUiIDogIlRvU3RyaW5nIiwKICAgICAgICAgICAgImNvbnRleHQiIDogIlBhdGllbnQiLAogICAgICAgICAgICAiYWNjZXNzTGV2ZWwiIDogIlB1YmxpYyIsCiAgICAgICAgICAgICJ0eXBlIiA6ICJGdW5jdGlvbkRlZiIsCiAgICAgICAgICAgICJleHByZXNzaW9uIiA6IHsKICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjMxOTo1NC0zMTk6NjQiLAogICAgICAgICAgICAgICAicGF0aCIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAidHlwZSIgOiAiUHJvcGVydHkiLAogICAgICAgICAgICAgICAic291cmNlIiA6IHsKICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjMxOTo1NC0zMTk6NTgiLAogICAgICAgICAgICAgICAgICAibmFtZSIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAgICAidHlwZSIgOiAiT3BlcmFuZFJlZiIKICAgICAgICAgICAgICAgfQogICAgICAgICAgICB9LAogICAgICAgICAgICAib3BlcmFuZCIgOiBbIHsKICAgICAgICAgICAgICAgIm5hbWUiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgIm9wZXJhbmRUeXBlU3BlY2lmaWVyIiA6IHsKICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjMxOTozMi0zMTk6NTAiLAogICAgICAgICAgICAgICAgICAibmFtZSIgOiAie2h0dHA6Ly9obDcub3JnL2ZoaXJ9UGF5bWVudE5vdGljZVN0YXR1cyIsCiAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJOYW1lZFR5cGVTcGVjaWZpZXIiCiAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgfSBdCiAgICAgICAgIH0sIHsKICAgICAgICAgICAgImxvY2F0b3IiIDogIjMyMDoxLTMyMDo3MiIsCiAgICAgICAgICAgICJuYW1lIiA6ICJUb1N0cmluZyIsCiAgICAgICAgICAgICJjb250ZXh0IiA6ICJQYXRpZW50IiwKICAgICAgICAgICAgImFjY2Vzc0xldmVsIiA6ICJQdWJsaWMiLAogICAgICAgICAgICAidHlwZSIgOiAiRnVuY3Rpb25EZWYiLAogICAgICAgICAgICAiZXhwcmVzc2lvbiIgOiB7CiAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIzMjA6NjItMzIwOjcyIiwKICAgICAgICAgICAgICAgInBhdGgiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgInR5cGUiIDogIlByb3BlcnR5IiwKICAgICAgICAgICAgICAgInNvdXJjZSIgOiB7CiAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIzMjA6NjItMzIwOjY2IiwKICAgICAgICAgICAgICAgICAgIm5hbWUiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk9wZXJhbmRSZWYiCiAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgfSwKICAgICAgICAgICAgIm9wZXJhbmQiIDogWyB7CiAgICAgICAgICAgICAgICJuYW1lIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICJvcGVyYW5kVHlwZVNwZWNpZmllciIgOiB7CiAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIzMjA6MzItMzIwOjU4IiwKICAgICAgICAgICAgICAgICAgIm5hbWUiIDogIntodHRwOi8vaGw3Lm9yZy9maGlyfVBheW1lbnRSZWNvbmNpbGlhdGlvblN0YXR1cyIsCiAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJOYW1lZFR5cGVTcGVjaWZpZXIiCiAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgfSBdCiAgICAgICAgIH0sIHsKICAgICAgICAgICAgImxvY2F0b3IiIDogIjMyMToxLTMyMTo2MCIsCiAgICAgICAgICAgICJuYW1lIiA6ICJUb1N0cmluZyIsCiAgICAgICAgICAgICJjb250ZXh0IiA6ICJQYXRpZW50IiwKICAgICAgICAgICAgImFjY2Vzc0xldmVsIiA6ICJQdWJsaWMiLAogICAgICAgICAgICAidHlwZSIgOiAiRnVuY3Rpb25EZWYiLAogICAgICAgICAgICAiZXhwcmVzc2lvbiIgOiB7CiAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIzMjE6NTAtMzIxOjYwIiwKICAgICAgICAgICAgICAgInBhdGgiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgInR5cGUiIDogIlByb3BlcnR5IiwKICAgICAgICAgICAgICAgInNvdXJjZSIgOiB7CiAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIzMjE6NTAtMzIxOjU0IiwKICAgICAgICAgICAgICAgICAgIm5hbWUiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk9wZXJhbmRSZWYiCiAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgfSwKICAgICAgICAgICAgIm9wZXJhbmQiIDogWyB7CiAgICAgICAgICAgICAgICJuYW1lIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICJvcGVyYW5kVHlwZVNwZWNpZmllciIgOiB7CiAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIzMjE6MzItMzIxOjQ2IiwKICAgICAgICAgICAgICAgICAgIm5hbWUiIDogIntodHRwOi8vaGw3Lm9yZy9maGlyfVByb2NlZHVyZVN0YXR1cyIsCiAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJOYW1lZFR5cGVTcGVjaWZpZXIiCiAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgfSBdCiAgICAgICAgIH0sIHsKICAgICAgICAgICAgImxvY2F0b3IiIDogIjMyMjoxLTMyMjo2NyIsCiAgICAgICAgICAgICJuYW1lIiA6ICJUb1N0cmluZyIsCiAgICAgICAgICAgICJjb250ZXh0IiA6ICJQYXRpZW50IiwKICAgICAgICAgICAgImFjY2Vzc0xldmVsIiA6ICJQdWJsaWMiLAogICAgICAgICAgICAidHlwZSIgOiAiRnVuY3Rpb25EZWYiLAogICAgICAgICAgICAiZXhwcmVzc2lvbiIgOiB7CiAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIzMjI6NTctMzIyOjY3IiwKICAgICAgICAgICAgICAgInBhdGgiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgInR5cGUiIDogIlByb3BlcnR5IiwKICAgICAgICAgICAgICAgInNvdXJjZSIgOiB7CiAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIzMjI6NTctMzIyOjYxIiwKICAgICAgICAgICAgICAgICAgIm5hbWUiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk9wZXJhbmRSZWYiCiAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgfSwKICAgICAgICAgICAgIm9wZXJhbmQiIDogWyB7CiAgICAgICAgICAgICAgICJuYW1lIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICJvcGVyYW5kVHlwZVNwZWNpZmllciIgOiB7CiAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIzMjI6MzItMzIyOjUzIiwKICAgICAgICAgICAgICAgICAgIm5hbWUiIDogIntodHRwOi8vaGw3Lm9yZy9maGlyfVByb3BlcnR5UmVwcmVzZW50YXRpb24iLAogICAgICAgICAgICAgICAgICAidHlwZSIgOiAiTmFtZWRUeXBlU3BlY2lmaWVyIgogICAgICAgICAgICAgICB9CiAgICAgICAgICAgIH0gXQogICAgICAgICB9LCB7CiAgICAgICAgICAgICJsb2NhdG9yIiA6ICIzMjM6MS0zMjM6NTciLAogICAgICAgICAgICAibmFtZSIgOiAiVG9TdHJpbmciLAogICAgICAgICAgICAiY29udGV4dCIgOiAiUGF0aWVudCIsCiAgICAgICAgICAgICJhY2Nlc3NMZXZlbCIgOiAiUHVibGljIiwKICAgICAgICAgICAgInR5cGUiIDogIkZ1bmN0aW9uRGVmIiwKICAgICAgICAgICAgImV4cHJlc3Npb24iIDogewogICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMzIzOjQ3LTMyMzo1NyIsCiAgICAgICAgICAgICAgICJwYXRoIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICJ0eXBlIiA6ICJQcm9wZXJ0eSIsCiAgICAgICAgICAgICAgICJzb3VyY2UiIDogewogICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMzIzOjQ3LTMyMzo1MSIsCiAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJPcGVyYW5kUmVmIgogICAgICAgICAgICAgICB9CiAgICAgICAgICAgIH0sCiAgICAgICAgICAgICJvcGVyYW5kIiA6IFsgewogICAgICAgICAgICAgICAibmFtZSIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAib3BlcmFuZFR5cGVTcGVjaWZpZXIiIDogewogICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMzIzOjMyLTMyMzo0MyIsCiAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJ7aHR0cDovL2hsNy5vcmcvZmhpcn1Qcm9wZXJ0eVR5cGUiLAogICAgICAgICAgICAgICAgICAidHlwZSIgOiAiTmFtZWRUeXBlU3BlY2lmaWVyIgogICAgICAgICAgICAgICB9CiAgICAgICAgICAgIH0gXQogICAgICAgICB9LCB7CiAgICAgICAgICAgICJsb2NhdG9yIiA6ICIzMjQ6MS0zMjQ6NjUiLAogICAgICAgICAgICAibmFtZSIgOiAiVG9TdHJpbmciLAogICAgICAgICAgICAiY29udGV4dCIgOiAiUGF0aWVudCIsCiAgICAgICAgICAgICJhY2Nlc3NMZXZlbCIgOiAiUHVibGljIiwKICAgICAgICAgICAgInR5cGUiIDogIkZ1bmN0aW9uRGVmIiwKICAgICAgICAgICAgImV4cHJlc3Npb24iIDogewogICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMzI0OjU1LTMyNDo2NSIsCiAgICAgICAgICAgICAgICJwYXRoIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICJ0eXBlIiA6ICJQcm9wZXJ0eSIsCiAgICAgICAgICAgICAgICJzb3VyY2UiIDogewogICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMzI0OjU1LTMyNDo1OSIsCiAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJPcGVyYW5kUmVmIgogICAgICAgICAgICAgICB9CiAgICAgICAgICAgIH0sCiAgICAgICAgICAgICJvcGVyYW5kIiA6IFsgewogICAgICAgICAgICAgICAibmFtZSIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAib3BlcmFuZFR5cGVTcGVjaWZpZXIiIDogewogICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMzI0OjMyLTMyNDo1MSIsCiAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJ7aHR0cDovL2hsNy5vcmcvZmhpcn1Qcm92ZW5hbmNlRW50aXR5Um9sZSIsCiAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJOYW1lZFR5cGVTcGVjaWZpZXIiCiAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgfSBdCiAgICAgICAgIH0sIHsKICAgICAgICAgICAgImxvY2F0b3IiIDogIjMyNToxLTMyNTo2MiIsCiAgICAgICAgICAgICJuYW1lIiA6ICJUb1N0cmluZyIsCiAgICAgICAgICAgICJjb250ZXh0IiA6ICJQYXRpZW50IiwKICAgICAgICAgICAgImFjY2Vzc0xldmVsIiA6ICJQdWJsaWMiLAogICAgICAgICAgICAidHlwZSIgOiAiRnVuY3Rpb25EZWYiLAogICAgICAgICAgICAiZXhwcmVzc2lvbiIgOiB7CiAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIzMjU6NTItMzI1OjYyIiwKICAgICAgICAgICAgICAgInBhdGgiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgInR5cGUiIDogIlByb3BlcnR5IiwKICAgICAgICAgICAgICAgInNvdXJjZSIgOiB7CiAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIzMjU6NTItMzI1OjU2IiwKICAgICAgICAgICAgICAgICAgIm5hbWUiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk9wZXJhbmRSZWYiCiAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgfSwKICAgICAgICAgICAgIm9wZXJhbmQiIDogWyB7CiAgICAgICAgICAgICAgICJuYW1lIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICJvcGVyYW5kVHlwZVNwZWNpZmllciIgOiB7CiAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIzMjU6MzItMzI1OjQ4IiwKICAgICAgICAgICAgICAgICAgIm5hbWUiIDogIntodHRwOi8vaGw3Lm9yZy9maGlyfVB1YmxpY2F0aW9uU3RhdHVzIiwKICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk5hbWVkVHlwZVNwZWNpZmllciIKICAgICAgICAgICAgICAgfQogICAgICAgICAgICB9IF0KICAgICAgICAgfSwgewogICAgICAgICAgICAibG9jYXRvciIgOiAiMzI2OjEtMzI2OjU2IiwKICAgICAgICAgICAgIm5hbWUiIDogIlRvU3RyaW5nIiwKICAgICAgICAgICAgImNvbnRleHQiIDogIlBhdGllbnQiLAogICAgICAgICAgICAiYWNjZXNzTGV2ZWwiIDogIlB1YmxpYyIsCiAgICAgICAgICAgICJ0eXBlIiA6ICJGdW5jdGlvbkRlZiIsCiAgICAgICAgICAgICJleHByZXNzaW9uIiA6IHsKICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjMyNjo0Ni0zMjY6NTYiLAogICAgICAgICAgICAgICAicGF0aCIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAidHlwZSIgOiAiUHJvcGVydHkiLAogICAgICAgICAgICAgICAic291cmNlIiA6IHsKICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjMyNjo0Ni0zMjY6NTAiLAogICAgICAgICAgICAgICAgICAibmFtZSIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAgICAidHlwZSIgOiAiT3BlcmFuZFJlZiIKICAgICAgICAgICAgICAgfQogICAgICAgICAgICB9LAogICAgICAgICAgICAib3BlcmFuZCIgOiBbIHsKICAgICAgICAgICAgICAgIm5hbWUiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgIm9wZXJhbmRUeXBlU3BlY2lmaWVyIiA6IHsKICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjMyNjozMi0zMjY6NDIiLAogICAgICAgICAgICAgICAgICAibmFtZSIgOiAie2h0dHA6Ly9obDcub3JnL2ZoaXJ9UXVhbGl0eVR5cGUiLAogICAgICAgICAgICAgICAgICAidHlwZSIgOiAiTmFtZWRUeXBlU3BlY2lmaWVyIgogICAgICAgICAgICAgICB9CiAgICAgICAgICAgIH0gXQogICAgICAgICB9LCB7CiAgICAgICAgICAgICJsb2NhdG9yIiA6ICIzMjc6MS0zMjc6NjMiLAogICAgICAgICAgICAibmFtZSIgOiAiVG9TdHJpbmciLAogICAgICAgICAgICAiY29udGV4dCIgOiAiUGF0aWVudCIsCiAgICAgICAgICAgICJhY2Nlc3NMZXZlbCIgOiAiUHVibGljIiwKICAgICAgICAgICAgInR5cGUiIDogIkZ1bmN0aW9uRGVmIiwKICAgICAgICAgICAgImV4cHJlc3Npb24iIDogewogICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMzI3OjUzLTMyNzo2MyIsCiAgICAgICAgICAgICAgICJwYXRoIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICJ0eXBlIiA6ICJQcm9wZXJ0eSIsCiAgICAgICAgICAgICAgICJzb3VyY2UiIDogewogICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMzI3OjUzLTMyNzo1NyIsCiAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJPcGVyYW5kUmVmIgogICAgICAgICAgICAgICB9CiAgICAgICAgICAgIH0sCiAgICAgICAgICAgICJvcGVyYW5kIiA6IFsgewogICAgICAgICAgICAgICAibmFtZSIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAib3BlcmFuZFR5cGVTcGVjaWZpZXIiIDogewogICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMzI3OjMyLTMyNzo0OSIsCiAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJ7aHR0cDovL2hsNy5vcmcvZmhpcn1RdWFudGl0eUNvbXBhcmF0b3IiLAogICAgICAgICAgICAgICAgICAidHlwZSIgOiAiTmFtZWRUeXBlU3BlY2lmaWVyIgogICAgICAgICAgICAgICB9CiAgICAgICAgICAgIH0gXQogICAgICAgICB9LCB7CiAgICAgICAgICAgICJsb2NhdG9yIiA6ICIzMjg6MS0zMjg6NzAiLAogICAgICAgICAgICAibmFtZSIgOiAiVG9TdHJpbmciLAogICAgICAgICAgICAiY29udGV4dCIgOiAiUGF0aWVudCIsCiAgICAgICAgICAgICJhY2Nlc3NMZXZlbCIgOiAiUHVibGljIiwKICAgICAgICAgICAgInR5cGUiIDogIkZ1bmN0aW9uRGVmIiwKICAgICAgICAgICAgImV4cHJlc3Npb24iIDogewogICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMzI4OjYwLTMyODo3MCIsCiAgICAgICAgICAgICAgICJwYXRoIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICJ0eXBlIiA6ICJQcm9wZXJ0eSIsCiAgICAgICAgICAgICAgICJzb3VyY2UiIDogewogICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMzI4OjYwLTMyODo2NCIsCiAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJPcGVyYW5kUmVmIgogICAgICAgICAgICAgICB9CiAgICAgICAgICAgIH0sCiAgICAgICAgICAgICJvcGVyYW5kIiA6IFsgewogICAgICAgICAgICAgICAibmFtZSIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAib3BlcmFuZFR5cGVTcGVjaWZpZXIiIDogewogICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMzI4OjMyLTMyODo1NiIsCiAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJ7aHR0cDovL2hsNy5vcmcvZmhpcn1RdWVzdGlvbm5haXJlSXRlbU9wZXJhdG9yIiwKICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk5hbWVkVHlwZVNwZWNpZmllciIKICAgICAgICAgICAgICAgfQogICAgICAgICAgICB9IF0KICAgICAgICAgfSwgewogICAgICAgICAgICAibG9jYXRvciIgOiAiMzI5OjEtMzI5OjY2IiwKICAgICAgICAgICAgIm5hbWUiIDogIlRvU3RyaW5nIiwKICAgICAgICAgICAgImNvbnRleHQiIDogIlBhdGllbnQiLAogICAgICAgICAgICAiYWNjZXNzTGV2ZWwiIDogIlB1YmxpYyIsCiAgICAgICAgICAgICJ0eXBlIiA6ICJGdW5jdGlvbkRlZiIsCiAgICAgICAgICAgICJleHByZXNzaW9uIiA6IHsKICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjMyOTo1Ni0zMjk6NjYiLAogICAgICAgICAgICAgICAicGF0aCIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAidHlwZSIgOiAiUHJvcGVydHkiLAogICAgICAgICAgICAgICAic291cmNlIiA6IHsKICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjMyOTo1Ni0zMjk6NjAiLAogICAgICAgICAgICAgICAgICAibmFtZSIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAgICAidHlwZSIgOiAiT3BlcmFuZFJlZiIKICAgICAgICAgICAgICAgfQogICAgICAgICAgICB9LAogICAgICAgICAgICAib3BlcmFuZCIgOiBbIHsKICAgICAgICAgICAgICAgIm5hbWUiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgIm9wZXJhbmRUeXBlU3BlY2lmaWVyIiA6IHsKICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjMyOTozMi0zMjk6NTIiLAogICAgICAgICAgICAgICAgICAibmFtZSIgOiAie2h0dHA6Ly9obDcub3JnL2ZoaXJ9UXVlc3Rpb25uYWlyZUl0ZW1UeXBlIiwKICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk5hbWVkVHlwZVNwZWNpZmllciIKICAgICAgICAgICAgICAgfQogICAgICAgICAgICB9IF0KICAgICAgICAgfSwgewogICAgICAgICAgICAibG9jYXRvciIgOiAiMzMwOjEtMzMwOjcyIiwKICAgICAgICAgICAgIm5hbWUiIDogIlRvU3RyaW5nIiwKICAgICAgICAgICAgImNvbnRleHQiIDogIlBhdGllbnQiLAogICAgICAgICAgICAiYWNjZXNzTGV2ZWwiIDogIlB1YmxpYyIsCiAgICAgICAgICAgICJ0eXBlIiA6ICJGdW5jdGlvbkRlZiIsCiAgICAgICAgICAgICJleHByZXNzaW9uIiA6IHsKICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjMzMDo2Mi0zMzA6NzIiLAogICAgICAgICAgICAgICAicGF0aCIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAidHlwZSIgOiAiUHJvcGVydHkiLAogICAgICAgICAgICAgICAic291cmNlIiA6IHsKICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjMzMDo2Mi0zMzA6NjYiLAogICAgICAgICAgICAgICAgICAibmFtZSIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAgICAidHlwZSIgOiAiT3BlcmFuZFJlZiIKICAgICAgICAgICAgICAgfQogICAgICAgICAgICB9LAogICAgICAgICAgICAib3BlcmFuZCIgOiBbIHsKICAgICAgICAgICAgICAgIm5hbWUiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgIm9wZXJhbmRUeXBlU3BlY2lmaWVyIiA6IHsKICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjMzMDozMi0zMzA6NTgiLAogICAgICAgICAgICAgICAgICAibmFtZSIgOiAie2h0dHA6Ly9obDcub3JnL2ZoaXJ9UXVlc3Rpb25uYWlyZVJlc3BvbnNlU3RhdHVzIiwKICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk5hbWVkVHlwZVNwZWNpZmllciIKICAgICAgICAgICAgICAgfQogICAgICAgICAgICB9IF0KICAgICAgICAgfSwgewogICAgICAgICAgICAibG9jYXRvciIgOiAiMzMxOjEtMzMxOjY4IiwKICAgICAgICAgICAgIm5hbWUiIDogIlRvU3RyaW5nIiwKICAgICAgICAgICAgImNvbnRleHQiIDogIlBhdGllbnQiLAogICAgICAgICAgICAiYWNjZXNzTGV2ZWwiIDogIlB1YmxpYyIsCiAgICAgICAgICAgICJ0eXBlIiA6ICJGdW5jdGlvbkRlZiIsCiAgICAgICAgICAgICJleHByZXNzaW9uIiA6IHsKICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjMzMTo1OC0zMzE6NjgiLAogICAgICAgICAgICAgICAicGF0aCIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAidHlwZSIgOiAiUHJvcGVydHkiLAogICAgICAgICAgICAgICAic291cmNlIiA6IHsKICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjMzMTo1OC0zMzE6NjIiLAogICAgICAgICAgICAgICAgICAibmFtZSIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAgICAidHlwZSIgOiAiT3BlcmFuZFJlZiIKICAgICAgICAgICAgICAgfQogICAgICAgICAgICB9LAogICAgICAgICAgICAib3BlcmFuZCIgOiBbIHsKICAgICAgICAgICAgICAgIm5hbWUiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgIm9wZXJhbmRUeXBlU3BlY2lmaWVyIiA6IHsKICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjMzMTozMi0zMzE6NTQiLAogICAgICAgICAgICAgICAgICAibmFtZSIgOiAie2h0dHA6Ly9obDcub3JnL2ZoaXJ9UmVmZXJlbmNlSGFuZGxpbmdQb2xpY3kiLAogICAgICAgICAgICAgICAgICAidHlwZSIgOiAiTmFtZWRUeXBlU3BlY2lmaWVyIgogICAgICAgICAgICAgICB9CiAgICAgICAgICAgIH0gXQogICAgICAgICB9LCB7CiAgICAgICAgICAgICJsb2NhdG9yIiA6ICIzMzI6MS0zMzI6NjYiLAogICAgICAgICAgICAibmFtZSIgOiAiVG9TdHJpbmciLAogICAgICAgICAgICAiY29udGV4dCIgOiAiUGF0aWVudCIsCiAgICAgICAgICAgICJhY2Nlc3NMZXZlbCIgOiAiUHVibGljIiwKICAgICAgICAgICAgInR5cGUiIDogIkZ1bmN0aW9uRGVmIiwKICAgICAgICAgICAgImV4cHJlc3Npb24iIDogewogICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMzMyOjU2LTMzMjo2NiIsCiAgICAgICAgICAgICAgICJwYXRoIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICJ0eXBlIiA6ICJQcm9wZXJ0eSIsCiAgICAgICAgICAgICAgICJzb3VyY2UiIDogewogICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMzMyOjU2LTMzMjo2MCIsCiAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJPcGVyYW5kUmVmIgogICAgICAgICAgICAgICB9CiAgICAgICAgICAgIH0sCiAgICAgICAgICAgICJvcGVyYW5kIiA6IFsgewogICAgICAgICAgICAgICAibmFtZSIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAib3BlcmFuZFR5cGVTcGVjaWZpZXIiIDogewogICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMzMyOjMyLTMzMjo1MiIsCiAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJ7aHR0cDovL2hsNy5vcmcvZmhpcn1SZWZlcmVuY2VWZXJzaW9uUnVsZXMiLAogICAgICAgICAgICAgICAgICAidHlwZSIgOiAiTmFtZWRUeXBlU3BlY2lmaWVyIgogICAgICAgICAgICAgICB9CiAgICAgICAgICAgIH0gXQogICAgICAgICB9LCB7CiAgICAgICAgICAgICJsb2NhdG9yIiA6ICIzMzM6MS0zMzM6NjciLAogICAgICAgICAgICAibmFtZSIgOiAiVG9TdHJpbmciLAogICAgICAgICAgICAiY29udGV4dCIgOiAiUGF0aWVudCIsCiAgICAgICAgICAgICJhY2Nlc3NMZXZlbCIgOiAiUHVibGljIiwKICAgICAgICAgICAgInR5cGUiIDogIkZ1bmN0aW9uRGVmIiwKICAgICAgICAgICAgImV4cHJlc3Npb24iIDogewogICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMzMzOjU3LTMzMzo2NyIsCiAgICAgICAgICAgICAgICJwYXRoIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICJ0eXBlIiA6ICJQcm9wZXJ0eSIsCiAgICAgICAgICAgICAgICJzb3VyY2UiIDogewogICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMzMzOjU3LTMzMzo2MSIsCiAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJPcGVyYW5kUmVmIgogICAgICAgICAgICAgICB9CiAgICAgICAgICAgIH0sCiAgICAgICAgICAgICJvcGVyYW5kIiA6IFsgewogICAgICAgICAgICAgICAibmFtZSIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAib3BlcmFuZFR5cGVTcGVjaWZpZXIiIDogewogICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMzMzOjMyLTMzMzo1MyIsCiAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJ7aHR0cDovL2hsNy5vcmcvZmhpcn1SZWZlcnJlZERvY3VtZW50U3RhdHVzIiwKICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk5hbWVkVHlwZVNwZWNpZmllciIKICAgICAgICAgICAgICAgfQogICAgICAgICAgICB9IF0KICAgICAgICAgfSwgewogICAgICAgICAgICAibG9jYXRvciIgOiAiMzM0OjEtMzM0OjY0IiwKICAgICAgICAgICAgIm5hbWUiIDogIlRvU3RyaW5nIiwKICAgICAgICAgICAgImNvbnRleHQiIDogIlBhdGllbnQiLAogICAgICAgICAgICAiYWNjZXNzTGV2ZWwiIDogIlB1YmxpYyIsCiAgICAgICAgICAgICJ0eXBlIiA6ICJGdW5jdGlvbkRlZiIsCiAgICAgICAgICAgICJleHByZXNzaW9uIiA6IHsKICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjMzNDo1NC0zMzQ6NjQiLAogICAgICAgICAgICAgICAicGF0aCIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAidHlwZSIgOiAiUHJvcGVydHkiLAogICAgICAgICAgICAgICAic291cmNlIiA6IHsKICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjMzNDo1NC0zMzQ6NTgiLAogICAgICAgICAgICAgICAgICAibmFtZSIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAgICAidHlwZSIgOiAiT3BlcmFuZFJlZiIKICAgICAgICAgICAgICAgfQogICAgICAgICAgICB9LAogICAgICAgICAgICAib3BlcmFuZCIgOiBbIHsKICAgICAgICAgICAgICAgIm5hbWUiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgIm9wZXJhbmRUeXBlU3BlY2lmaWVyIiA6IHsKICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjMzNDozMi0zMzQ6NTAiLAogICAgICAgICAgICAgICAgICAibmFtZSIgOiAie2h0dHA6Ly9obDcub3JnL2ZoaXJ9UmVsYXRlZEFydGlmYWN0VHlwZSIsCiAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJOYW1lZFR5cGVTcGVjaWZpZXIiCiAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgfSBdCiAgICAgICAgIH0sIHsKICAgICAgICAgICAgImxvY2F0b3IiIDogIjMzNToxLTMzNTo2MiIsCiAgICAgICAgICAgICJuYW1lIiA6ICJUb1N0cmluZyIsCiAgICAgICAgICAgICJjb250ZXh0IiA6ICJQYXRpZW50IiwKICAgICAgICAgICAgImFjY2Vzc0xldmVsIiA6ICJQdWJsaWMiLAogICAgICAgICAgICAidHlwZSIgOiAiRnVuY3Rpb25EZWYiLAogICAgICAgICAgICAiZXhwcmVzc2lvbiIgOiB7CiAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIzMzU6NTItMzM1OjYyIiwKICAgICAgICAgICAgICAgInBhdGgiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgInR5cGUiIDogIlByb3BlcnR5IiwKICAgICAgICAgICAgICAgInNvdXJjZSIgOiB7CiAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIzMzU6NTItMzM1OjU2IiwKICAgICAgICAgICAgICAgICAgIm5hbWUiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk9wZXJhbmRSZWYiCiAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgfSwKICAgICAgICAgICAgIm9wZXJhbmQiIDogWyB7CiAgICAgICAgICAgICAgICJuYW1lIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICJvcGVyYW5kVHlwZVNwZWNpZmllciIgOiB7CiAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIzMzU6MzItMzM1OjQ4IiwKICAgICAgICAgICAgICAgICAgIm5hbWUiIDogIntodHRwOi8vaGw3Lm9yZy9maGlyfVJlbWl0dGFuY2VPdXRjb21lIiwKICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk5hbWVkVHlwZVNwZWNpZmllciIKICAgICAgICAgICAgICAgfQogICAgICAgICAgICB9IF0KICAgICAgICAgfSwgewogICAgICAgICAgICAibG9jYXRvciIgOiAiMzM2OjEtMzM2OjU5IiwKICAgICAgICAgICAgIm5hbWUiIDogIlRvU3RyaW5nIiwKICAgICAgICAgICAgImNvbnRleHQiIDogIlBhdGllbnQiLAogICAgICAgICAgICAiYWNjZXNzTGV2ZWwiIDogIlB1YmxpYyIsCiAgICAgICAgICAgICJ0eXBlIiA6ICJGdW5jdGlvbkRlZiIsCiAgICAgICAgICAgICJleHByZXNzaW9uIiA6IHsKICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjMzNjo0OS0zMzY6NTkiLAogICAgICAgICAgICAgICAicGF0aCIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAidHlwZSIgOiAiUHJvcGVydHkiLAogICAgICAgICAgICAgICAic291cmNlIiA6IHsKICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjMzNjo0OS0zMzY6NTMiLAogICAgICAgICAgICAgICAgICAibmFtZSIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAgICAidHlwZSIgOiAiT3BlcmFuZFJlZiIKICAgICAgICAgICAgICAgfQogICAgICAgICAgICB9LAogICAgICAgICAgICAib3BlcmFuZCIgOiBbIHsKICAgICAgICAgICAgICAgIm5hbWUiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgIm9wZXJhbmRUeXBlU3BlY2lmaWVyIiA6IHsKICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjMzNjozMi0zMzY6NDUiLAogICAgICAgICAgICAgICAgICAibmFtZSIgOiAie2h0dHA6Ly9obDcub3JnL2ZoaXJ9UmVwb3NpdG9yeVR5cGUiLAogICAgICAgICAgICAgICAgICAidHlwZSIgOiAiTmFtZWRUeXBlU3BlY2lmaWVyIgogICAgICAgICAgICAgICB9CiAgICAgICAgICAgIH0gXQogICAgICAgICB9LCB7CiAgICAgICAgICAgICJsb2NhdG9yIiA6ICIzMzc6MS0zMzc6NTgiLAogICAgICAgICAgICAibmFtZSIgOiAiVG9TdHJpbmciLAogICAgICAgICAgICAiY29udGV4dCIgOiAiUGF0aWVudCIsCiAgICAgICAgICAgICJhY2Nlc3NMZXZlbCIgOiAiUHVibGljIiwKICAgICAgICAgICAgInR5cGUiIDogIkZ1bmN0aW9uRGVmIiwKICAgICAgICAgICAgImV4cHJlc3Npb24iIDogewogICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMzM3OjQ4LTMzNzo1OCIsCiAgICAgICAgICAgICAgICJwYXRoIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICJ0eXBlIiA6ICJQcm9wZXJ0eSIsCiAgICAgICAgICAgICAgICJzb3VyY2UiIDogewogICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMzM3OjQ4LTMzNzo1MiIsCiAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJPcGVyYW5kUmVmIgogICAgICAgICAgICAgICB9CiAgICAgICAgICAgIH0sCiAgICAgICAgICAgICJvcGVyYW5kIiA6IFsgewogICAgICAgICAgICAgICAibmFtZSIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAib3BlcmFuZFR5cGVTcGVjaWZpZXIiIDogewogICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMzM3OjMyLTMzNzo0NCIsCiAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJ7aHR0cDovL2hsNy5vcmcvZmhpcn1SZXF1ZXN0SW50ZW50IiwKICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk5hbWVkVHlwZVNwZWNpZmllciIKICAgICAgICAgICAgICAgfQogICAgICAgICAgICB9IF0KICAgICAgICAgfSwgewogICAgICAgICAgICAibG9jYXRvciIgOiAiMzM4OjEtMzM4OjYwIiwKICAgICAgICAgICAgIm5hbWUiIDogIlRvU3RyaW5nIiwKICAgICAgICAgICAgImNvbnRleHQiIDogIlBhdGllbnQiLAogICAgICAgICAgICAiYWNjZXNzTGV2ZWwiIDogIlB1YmxpYyIsCiAgICAgICAgICAgICJ0eXBlIiA6ICJGdW5jdGlvbkRlZiIsCiAgICAgICAgICAgICJleHByZXNzaW9uIiA6IHsKICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjMzODo1MC0zMzg6NjAiLAogICAgICAgICAgICAgICAicGF0aCIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAidHlwZSIgOiAiUHJvcGVydHkiLAogICAgICAgICAgICAgICAic291cmNlIiA6IHsKICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjMzODo1MC0zMzg6NTQiLAogICAgICAgICAgICAgICAgICAibmFtZSIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAgICAidHlwZSIgOiAiT3BlcmFuZFJlZiIKICAgICAgICAgICAgICAgfQogICAgICAgICAgICB9LAogICAgICAgICAgICAib3BlcmFuZCIgOiBbIHsKICAgICAgICAgICAgICAgIm5hbWUiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgIm9wZXJhbmRUeXBlU3BlY2lmaWVyIiA6IHsKICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjMzODozMi0zMzg6NDYiLAogICAgICAgICAgICAgICAgICAibmFtZSIgOiAie2h0dHA6Ly9obDcub3JnL2ZoaXJ9UmVxdWVzdFByaW9yaXR5IiwKICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk5hbWVkVHlwZVNwZWNpZmllciIKICAgICAgICAgICAgICAgfQogICAgICAgICAgICB9IF0KICAgICAgICAgfSwgewogICAgICAgICAgICAibG9jYXRvciIgOiAiMzM5OjEtMzM5OjU4IiwKICAgICAgICAgICAgIm5hbWUiIDogIlRvU3RyaW5nIiwKICAgICAgICAgICAgImNvbnRleHQiIDogIlBhdGllbnQiLAogICAgICAgICAgICAiYWNjZXNzTGV2ZWwiIDogIlB1YmxpYyIsCiAgICAgICAgICAgICJ0eXBlIiA6ICJGdW5jdGlvbkRlZiIsCiAgICAgICAgICAgICJleHByZXNzaW9uIiA6IHsKICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjMzOTo0OC0zMzk6NTgiLAogICAgICAgICAgICAgICAicGF0aCIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAidHlwZSIgOiAiUHJvcGVydHkiLAogICAgICAgICAgICAgICAic291cmNlIiA6IHsKICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjMzOTo0OC0zMzk6NTIiLAogICAgICAgICAgICAgICAgICAibmFtZSIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAgICAidHlwZSIgOiAiT3BlcmFuZFJlZiIKICAgICAgICAgICAgICAgfQogICAgICAgICAgICB9LAogICAgICAgICAgICAib3BlcmFuZCIgOiBbIHsKICAgICAgICAgICAgICAgIm5hbWUiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgIm9wZXJhbmRUeXBlU3BlY2lmaWVyIiA6IHsKICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjMzOTozMi0zMzk6NDQiLAogICAgICAgICAgICAgICAgICAibmFtZSIgOiAie2h0dHA6Ly9obDcub3JnL2ZoaXJ9UmVxdWVzdFN0YXR1cyIsCiAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJOYW1lZFR5cGVTcGVjaWZpZXIiCiAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgfSBdCiAgICAgICAgIH0sIHsKICAgICAgICAgICAgImxvY2F0b3IiIDogIjM0MDoxLTM0MDo2NCIsCiAgICAgICAgICAgICJuYW1lIiA6ICJUb1N0cmluZyIsCiAgICAgICAgICAgICJjb250ZXh0IiA6ICJQYXRpZW50IiwKICAgICAgICAgICAgImFjY2Vzc0xldmVsIiA6ICJQdWJsaWMiLAogICAgICAgICAgICAidHlwZSIgOiAiRnVuY3Rpb25EZWYiLAogICAgICAgICAgICAiZXhwcmVzc2lvbiIgOiB7CiAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIzNDA6NTQtMzQwOjY0IiwKICAgICAgICAgICAgICAgInBhdGgiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgInR5cGUiIDogIlByb3BlcnR5IiwKICAgICAgICAgICAgICAgInNvdXJjZSIgOiB7CiAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIzNDA6NTQtMzQwOjU4IiwKICAgICAgICAgICAgICAgICAgIm5hbWUiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk9wZXJhbmRSZWYiCiAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgfSwKICAgICAgICAgICAgIm9wZXJhbmQiIDogWyB7CiAgICAgICAgICAgICAgICJuYW1lIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICJvcGVyYW5kVHlwZVNwZWNpZmllciIgOiB7CiAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIzNDA6MzItMzQwOjUwIiwKICAgICAgICAgICAgICAgICAgIm5hbWUiIDogIntodHRwOi8vaGw3Lm9yZy9maGlyfVJlc2VhcmNoRWxlbWVudFR5cGUiLAogICAgICAgICAgICAgICAgICAidHlwZSIgOiAiTmFtZWRUeXBlU3BlY2lmaWVyIgogICAgICAgICAgICAgICB9CiAgICAgICAgICAgIH0gXQogICAgICAgICB9LCB7CiAgICAgICAgICAgICJsb2NhdG9yIiA6ICIzNDE6MS0zNDE6NjQiLAogICAgICAgICAgICAibmFtZSIgOiAiVG9TdHJpbmciLAogICAgICAgICAgICAiY29udGV4dCIgOiAiUGF0aWVudCIsCiAgICAgICAgICAgICJhY2Nlc3NMZXZlbCIgOiAiUHVibGljIiwKICAgICAgICAgICAgInR5cGUiIDogIkZ1bmN0aW9uRGVmIiwKICAgICAgICAgICAgImV4cHJlc3Npb24iIDogewogICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMzQxOjU0LTM0MTo2NCIsCiAgICAgICAgICAgICAgICJwYXRoIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICJ0eXBlIiA6ICJQcm9wZXJ0eSIsCiAgICAgICAgICAgICAgICJzb3VyY2UiIDogewogICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMzQxOjU0LTM0MTo1OCIsCiAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJPcGVyYW5kUmVmIgogICAgICAgICAgICAgICB9CiAgICAgICAgICAgIH0sCiAgICAgICAgICAgICJvcGVyYW5kIiA6IFsgewogICAgICAgICAgICAgICAibmFtZSIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAib3BlcmFuZFR5cGVTcGVjaWZpZXIiIDogewogICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMzQxOjMyLTM0MTo1MCIsCiAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJ7aHR0cDovL2hsNy5vcmcvZmhpcn1SZXNlYXJjaFN0dWR5U3RhdHVzIiwKICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk5hbWVkVHlwZVNwZWNpZmllciIKICAgICAgICAgICAgICAgfQogICAgICAgICAgICB9IF0KICAgICAgICAgfSwgewogICAgICAgICAgICAibG9jYXRvciIgOiAiMzQyOjEtMzQyOjY2IiwKICAgICAgICAgICAgIm5hbWUiIDogIlRvU3RyaW5nIiwKICAgICAgICAgICAgImNvbnRleHQiIDogIlBhdGllbnQiLAogICAgICAgICAgICAiYWNjZXNzTGV2ZWwiIDogIlB1YmxpYyIsCiAgICAgICAgICAgICJ0eXBlIiA6ICJGdW5jdGlvbkRlZiIsCiAgICAgICAgICAgICJleHByZXNzaW9uIiA6IHsKICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjM0Mjo1Ni0zNDI6NjYiLAogICAgICAgICAgICAgICAicGF0aCIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAidHlwZSIgOiAiUHJvcGVydHkiLAogICAgICAgICAgICAgICAic291cmNlIiA6IHsKICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjM0Mjo1Ni0zNDI6NjAiLAogICAgICAgICAgICAgICAgICAibmFtZSIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAgICAidHlwZSIgOiAiT3BlcmFuZFJlZiIKICAgICAgICAgICAgICAgfQogICAgICAgICAgICB9LAogICAgICAgICAgICAib3BlcmFuZCIgOiBbIHsKICAgICAgICAgICAgICAgIm5hbWUiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgIm9wZXJhbmRUeXBlU3BlY2lmaWVyIiA6IHsKICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjM0MjozMi0zNDI6NTIiLAogICAgICAgICAgICAgICAgICAibmFtZSIgOiAie2h0dHA6Ly9obDcub3JnL2ZoaXJ9UmVzZWFyY2hTdWJqZWN0U3RhdHVzIiwKICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk5hbWVkVHlwZVNwZWNpZmllciIKICAgICAgICAgICAgICAgfQogICAgICAgICAgICB9IF0KICAgICAgICAgfSwgewogICAgICAgICAgICAibG9jYXRvciIgOiAiMzQzOjEtMzQzOjU3IiwKICAgICAgICAgICAgIm5hbWUiIDogIlRvU3RyaW5nIiwKICAgICAgICAgICAgImNvbnRleHQiIDogIlBhdGllbnQiLAogICAgICAgICAgICAiYWNjZXNzTGV2ZWwiIDogIlB1YmxpYyIsCiAgICAgICAgICAgICJ0eXBlIiA6ICJGdW5jdGlvbkRlZiIsCiAgICAgICAgICAgICJleHByZXNzaW9uIiA6IHsKICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjM0Mzo0Ny0zNDM6NTciLAogICAgICAgICAgICAgICAicGF0aCIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAidHlwZSIgOiAiUHJvcGVydHkiLAogICAgICAgICAgICAgICAic291cmNlIiA6IHsKICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjM0Mzo0Ny0zNDM6NTEiLAogICAgICAgICAgICAgICAgICAibmFtZSIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAgICAidHlwZSIgOiAiT3BlcmFuZFJlZiIKICAgICAgICAgICAgICAgfQogICAgICAgICAgICB9LAogICAgICAgICAgICAib3BlcmFuZCIgOiBbIHsKICAgICAgICAgICAgICAgIm5hbWUiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgIm9wZXJhbmRUeXBlU3BlY2lmaWVyIiA6IHsKICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjM0MzozMi0zNDM6NDMiLAogICAgICAgICAgICAgICAgICAibmFtZSIgOiAie2h0dHA6Ly9obDcub3JnL2ZoaXJ9UmVzb3VyY2VUeXBlIiwKICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk5hbWVkVHlwZVNwZWNpZmllciIKICAgICAgICAgICAgICAgfQogICAgICAgICAgICB9IF0KICAgICAgICAgfSwgewogICAgICAgICAgICAibG9jYXRvciIgOiAiMzQ0OjEtMzQ0OjY2IiwKICAgICAgICAgICAgIm5hbWUiIDogIlRvU3RyaW5nIiwKICAgICAgICAgICAgImNvbnRleHQiIDogIlBhdGllbnQiLAogICAgICAgICAgICAiYWNjZXNzTGV2ZWwiIDogIlB1YmxpYyIsCiAgICAgICAgICAgICJ0eXBlIiA6ICJGdW5jdGlvbkRlZiIsCiAgICAgICAgICAgICJleHByZXNzaW9uIiA6IHsKICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjM0NDo1Ni0zNDQ6NjYiLAogICAgICAgICAgICAgICAicGF0aCIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAidHlwZSIgOiAiUHJvcGVydHkiLAogICAgICAgICAgICAgICAic291cmNlIiA6IHsKICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjM0NDo1Ni0zNDQ6NjAiLAogICAgICAgICAgICAgICAgICAibmFtZSIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAgICAidHlwZSIgOiAiT3BlcmFuZFJlZiIKICAgICAgICAgICAgICAgfQogICAgICAgICAgICB9LAogICAgICAgICAgICAib3BlcmFuZCIgOiBbIHsKICAgICAgICAgICAgICAgIm5hbWUiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgIm9wZXJhbmRUeXBlU3BlY2lmaWVyIiA6IHsKICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjM0NDozMi0zNDQ6NTIiLAogICAgICAgICAgICAgICAgICAibmFtZSIgOiAie2h0dHA6Ly9obDcub3JnL2ZoaXJ9UmVzb3VyY2VWZXJzaW9uUG9saWN5IiwKICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk5hbWVkVHlwZVNwZWNpZmllciIKICAgICAgICAgICAgICAgfQogICAgICAgICAgICB9IF0KICAgICAgICAgfSwgewogICAgICAgICAgICAibG9jYXRvciIgOiAiMzQ1OjEtMzQ1OjU3IiwKICAgICAgICAgICAgIm5hbWUiIDogIlRvU3RyaW5nIiwKICAgICAgICAgICAgImNvbnRleHQiIDogIlBhdGllbnQiLAogICAgICAgICAgICAiYWNjZXNzTGV2ZWwiIDogIlB1YmxpYyIsCiAgICAgICAgICAgICJ0eXBlIiA6ICJGdW5jdGlvbkRlZiIsCiAgICAgICAgICAgICJleHByZXNzaW9uIiA6IHsKICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjM0NTo0Ny0zNDU6NTciLAogICAgICAgICAgICAgICAicGF0aCIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAidHlwZSIgOiAiUHJvcGVydHkiLAogICAgICAgICAgICAgICAic291cmNlIiA6IHsKICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjM0NTo0Ny0zNDU6NTEiLAogICAgICAgICAgICAgICAgICAibmFtZSIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAgICAidHlwZSIgOiAiT3BlcmFuZFJlZiIKICAgICAgICAgICAgICAgfQogICAgICAgICAgICB9LAogICAgICAgICAgICAib3BlcmFuZCIgOiBbIHsKICAgICAgICAgICAgICAgIm5hbWUiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgIm9wZXJhbmRUeXBlU3BlY2lmaWVyIiA6IHsKICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjM0NTozMi0zNDU6NDMiLAogICAgICAgICAgICAgICAgICAibmFtZSIgOiAie2h0dHA6Ly9obDcub3JnL2ZoaXJ9UmVzcG9uc2VUeXBlIiwKICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk5hbWVkVHlwZVNwZWNpZmllciIKICAgICAgICAgICAgICAgfQogICAgICAgICAgICB9IF0KICAgICAgICAgfSwgewogICAgICAgICAgICAibG9jYXRvciIgOiAiMzQ2OjEtMzQ2OjY2IiwKICAgICAgICAgICAgIm5hbWUiIDogIlRvU3RyaW5nIiwKICAgICAgICAgICAgImNvbnRleHQiIDogIlBhdGllbnQiLAogICAgICAgICAgICAiYWNjZXNzTGV2ZWwiIDogIlB1YmxpYyIsCiAgICAgICAgICAgICJ0eXBlIiA6ICJGdW5jdGlvbkRlZiIsCiAgICAgICAgICAgICJleHByZXNzaW9uIiA6IHsKICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjM0Njo1Ni0zNDY6NjYiLAogICAgICAgICAgICAgICAicGF0aCIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAidHlwZSIgOiAiUHJvcGVydHkiLAogICAgICAgICAgICAgICAic291cmNlIiA6IHsKICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjM0Njo1Ni0zNDY6NjAiLAogICAgICAgICAgICAgICAgICAibmFtZSIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAgICAidHlwZSIgOiAiT3BlcmFuZFJlZiIKICAgICAgICAgICAgICAgfQogICAgICAgICAgICB9LAogICAgICAgICAgICAib3BlcmFuZCIgOiBbIHsKICAgICAgICAgICAgICAgIm5hbWUiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgIm9wZXJhbmRUeXBlU3BlY2lmaWVyIiA6IHsKICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjM0NjozMi0zNDY6NTIiLAogICAgICAgICAgICAgICAgICAibmFtZSIgOiAie2h0dHA6Ly9obDcub3JnL2ZoaXJ9UmVzdGZ1bENhcGFiaWxpdHlNb2RlIiwKICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk5hbWVkVHlwZVNwZWNpZmllciIKICAgICAgICAgICAgICAgfQogICAgICAgICAgICB9IF0KICAgICAgICAgfSwgewogICAgICAgICAgICAibG9jYXRvciIgOiAiMzQ3OjEtMzQ3OjY1IiwKICAgICAgICAgICAgIm5hbWUiIDogIlRvU3RyaW5nIiwKICAgICAgICAgICAgImNvbnRleHQiIDogIlBhdGllbnQiLAogICAgICAgICAgICAiYWNjZXNzTGV2ZWwiIDogIlB1YmxpYyIsCiAgICAgICAgICAgICJ0eXBlIiA6ICJGdW5jdGlvbkRlZiIsCiAgICAgICAgICAgICJleHByZXNzaW9uIiA6IHsKICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjM0Nzo1NS0zNDc6NjUiLAogICAgICAgICAgICAgICAicGF0aCIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAidHlwZSIgOiAiUHJvcGVydHkiLAogICAgICAgICAgICAgICAic291cmNlIiA6IHsKICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjM0Nzo1NS0zNDc6NTkiLAogICAgICAgICAgICAgICAgICAibmFtZSIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAgICAidHlwZSIgOiAiT3BlcmFuZFJlZiIKICAgICAgICAgICAgICAgfQogICAgICAgICAgICB9LAogICAgICAgICAgICAib3BlcmFuZCIgOiBbIHsKICAgICAgICAgICAgICAgIm5hbWUiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgIm9wZXJhbmRUeXBlU3BlY2lmaWVyIiA6IHsKICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjM0NzozMi0zNDc6NTEiLAogICAgICAgICAgICAgICAgICAibmFtZSIgOiAie2h0dHA6Ly9obDcub3JnL2ZoaXJ9Umlza0Fzc2Vzc21lbnRTdGF0dXMiLAogICAgICAgICAgICAgICAgICAidHlwZSIgOiAiTmFtZWRUeXBlU3BlY2lmaWVyIgogICAgICAgICAgICAgICB9CiAgICAgICAgICAgIH0gXQogICAgICAgICB9LCB7CiAgICAgICAgICAgICJsb2NhdG9yIiA6ICIzNDg6MS0zNDg6NTYiLAogICAgICAgICAgICAibmFtZSIgOiAiVG9TdHJpbmciLAogICAgICAgICAgICAiY29udGV4dCIgOiAiUGF0aWVudCIsCiAgICAgICAgICAgICJhY2Nlc3NMZXZlbCIgOiAiUHVibGljIiwKICAgICAgICAgICAgInR5cGUiIDogIkZ1bmN0aW9uRGVmIiwKICAgICAgICAgICAgImV4cHJlc3Npb24iIDogewogICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMzQ4OjQ2LTM0ODo1NiIsCiAgICAgICAgICAgICAgICJwYXRoIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICJ0eXBlIiA6ICJQcm9wZXJ0eSIsCiAgICAgICAgICAgICAgICJzb3VyY2UiIDogewogICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMzQ4OjQ2LTM0ODo1MCIsCiAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJPcGVyYW5kUmVmIgogICAgICAgICAgICAgICB9CiAgICAgICAgICAgIH0sCiAgICAgICAgICAgICJvcGVyYW5kIiA6IFsgewogICAgICAgICAgICAgICAibmFtZSIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAib3BlcmFuZFR5cGVTcGVjaWZpZXIiIDogewogICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMzQ4OjMyLTM0ODo0MiIsCiAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJ7aHR0cDovL2hsNy5vcmcvZmhpcn1TUERYTGljZW5zZSIsCiAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJOYW1lZFR5cGVTcGVjaWZpZXIiCiAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgfSBdCiAgICAgICAgIH0sIHsKICAgICAgICAgICAgImxvY2F0b3IiIDogIjM0OToxLTM0OTo2MSIsCiAgICAgICAgICAgICJuYW1lIiA6ICJUb1N0cmluZyIsCiAgICAgICAgICAgICJjb250ZXh0IiA6ICJQYXRpZW50IiwKICAgICAgICAgICAgImFjY2Vzc0xldmVsIiA6ICJQdWJsaWMiLAogICAgICAgICAgICAidHlwZSIgOiAiRnVuY3Rpb25EZWYiLAogICAgICAgICAgICAiZXhwcmVzc2lvbiIgOiB7CiAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIzNDk6NTEtMzQ5OjYxIiwKICAgICAgICAgICAgICAgInBhdGgiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgInR5cGUiIDogIlByb3BlcnR5IiwKICAgICAgICAgICAgICAgInNvdXJjZSIgOiB7CiAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIzNDk6NTEtMzQ5OjU1IiwKICAgICAgICAgICAgICAgICAgIm5hbWUiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk9wZXJhbmRSZWYiCiAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgfSwKICAgICAgICAgICAgIm9wZXJhbmQiIDogWyB7CiAgICAgICAgICAgICAgICJuYW1lIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICJvcGVyYW5kVHlwZVNwZWNpZmllciIgOiB7CiAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIzNDk6MzItMzQ5OjQ3IiwKICAgICAgICAgICAgICAgICAgIm5hbWUiIDogIntodHRwOi8vaGw3Lm9yZy9maGlyfVNlYXJjaENvbXBhcmF0b3IiLAogICAgICAgICAgICAgICAgICAidHlwZSIgOiAiTmFtZWRUeXBlU3BlY2lmaWVyIgogICAgICAgICAgICAgICB9CiAgICAgICAgICAgIH0gXQogICAgICAgICB9LCB7CiAgICAgICAgICAgICJsb2NhdG9yIiA6ICIzNTA6MS0zNTA6NjAiLAogICAgICAgICAgICAibmFtZSIgOiAiVG9TdHJpbmciLAogICAgICAgICAgICAiY29udGV4dCIgOiAiUGF0aWVudCIsCiAgICAgICAgICAgICJhY2Nlc3NMZXZlbCIgOiAiUHVibGljIiwKICAgICAgICAgICAgInR5cGUiIDogIkZ1bmN0aW9uRGVmIiwKICAgICAgICAgICAgImV4cHJlc3Npb24iIDogewogICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMzUwOjUwLTM1MDo2MCIsCiAgICAgICAgICAgICAgICJwYXRoIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICJ0eXBlIiA6ICJQcm9wZXJ0eSIsCiAgICAgICAgICAgICAgICJzb3VyY2UiIDogewogICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMzUwOjUwLTM1MDo1NCIsCiAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJPcGVyYW5kUmVmIgogICAgICAgICAgICAgICB9CiAgICAgICAgICAgIH0sCiAgICAgICAgICAgICJvcGVyYW5kIiA6IFsgewogICAgICAgICAgICAgICAibmFtZSIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAib3BlcmFuZFR5cGVTcGVjaWZpZXIiIDogewogICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMzUwOjMyLTM1MDo0NiIsCiAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJ7aHR0cDovL2hsNy5vcmcvZmhpcn1TZWFyY2hFbnRyeU1vZGUiLAogICAgICAgICAgICAgICAgICAidHlwZSIgOiAiTmFtZWRUeXBlU3BlY2lmaWVyIgogICAgICAgICAgICAgICB9CiAgICAgICAgICAgIH0gXQogICAgICAgICB9LCB7CiAgICAgICAgICAgICJsb2NhdG9yIiA6ICIzNTE6MS0zNTE6NjMiLAogICAgICAgICAgICAibmFtZSIgOiAiVG9TdHJpbmciLAogICAgICAgICAgICAiY29udGV4dCIgOiAiUGF0aWVudCIsCiAgICAgICAgICAgICJhY2Nlc3NMZXZlbCIgOiAiUHVibGljIiwKICAgICAgICAgICAgInR5cGUiIDogIkZ1bmN0aW9uRGVmIiwKICAgICAgICAgICAgImV4cHJlc3Npb24iIDogewogICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMzUxOjUzLTM1MTo2MyIsCiAgICAgICAgICAgICAgICJwYXRoIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICJ0eXBlIiA6ICJQcm9wZXJ0eSIsCiAgICAgICAgICAgICAgICJzb3VyY2UiIDogewogICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMzUxOjUzLTM1MTo1NyIsCiAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJPcGVyYW5kUmVmIgogICAgICAgICAgICAgICB9CiAgICAgICAgICAgIH0sCiAgICAgICAgICAgICJvcGVyYW5kIiA6IFsgewogICAgICAgICAgICAgICAibmFtZSIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAib3BlcmFuZFR5cGVTcGVjaWZpZXIiIDogewogICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMzUxOjMyLTM1MTo0OSIsCiAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJ7aHR0cDovL2hsNy5vcmcvZmhpcn1TZWFyY2hNb2RpZmllckNvZGUiLAogICAgICAgICAgICAgICAgICAidHlwZSIgOiAiTmFtZWRUeXBlU3BlY2lmaWVyIgogICAgICAgICAgICAgICB9CiAgICAgICAgICAgIH0gXQogICAgICAgICB9LCB7CiAgICAgICAgICAgICJsb2NhdG9yIiA6ICIzNTI6MS0zNTI6NjAiLAogICAgICAgICAgICAibmFtZSIgOiAiVG9TdHJpbmciLAogICAgICAgICAgICAiY29udGV4dCIgOiAiUGF0aWVudCIsCiAgICAgICAgICAgICJhY2Nlc3NMZXZlbCIgOiAiUHVibGljIiwKICAgICAgICAgICAgInR5cGUiIDogIkZ1bmN0aW9uRGVmIiwKICAgICAgICAgICAgImV4cHJlc3Npb24iIDogewogICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMzUyOjUwLTM1Mjo2MCIsCiAgICAgICAgICAgICAgICJwYXRoIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICJ0eXBlIiA6ICJQcm9wZXJ0eSIsCiAgICAgICAgICAgICAgICJzb3VyY2UiIDogewogICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMzUyOjUwLTM1Mjo1NCIsCiAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJPcGVyYW5kUmVmIgogICAgICAgICAgICAgICB9CiAgICAgICAgICAgIH0sCiAgICAgICAgICAgICJvcGVyYW5kIiA6IFsgewogICAgICAgICAgICAgICAibmFtZSIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAib3BlcmFuZFR5cGVTcGVjaWZpZXIiIDogewogICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMzUyOjMyLTM1Mjo0NiIsCiAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJ7aHR0cDovL2hsNy5vcmcvZmhpcn1TZWFyY2hQYXJhbVR5cGUiLAogICAgICAgICAgICAgICAgICAidHlwZSIgOiAiTmFtZWRUeXBlU3BlY2lmaWVyIgogICAgICAgICAgICAgICB9CiAgICAgICAgICAgIH0gXQogICAgICAgICB9LCB7CiAgICAgICAgICAgICJsb2NhdG9yIiA6ICIzNTM6MS0zNTM6NTYiLAogICAgICAgICAgICAibmFtZSIgOiAiVG9TdHJpbmciLAogICAgICAgICAgICAiY29udGV4dCIgOiAiUGF0aWVudCIsCiAgICAgICAgICAgICJhY2Nlc3NMZXZlbCIgOiAiUHVibGljIiwKICAgICAgICAgICAgInR5cGUiIDogIkZ1bmN0aW9uRGVmIiwKICAgICAgICAgICAgImV4cHJlc3Npb24iIDogewogICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMzUzOjQ2LTM1Mzo1NiIsCiAgICAgICAgICAgICAgICJwYXRoIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICJ0eXBlIiA6ICJQcm9wZXJ0eSIsCiAgICAgICAgICAgICAgICJzb3VyY2UiIDogewogICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMzUzOjQ2LTM1Mzo1MCIsCiAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJPcGVyYW5kUmVmIgogICAgICAgICAgICAgICB9CiAgICAgICAgICAgIH0sCiAgICAgICAgICAgICJvcGVyYW5kIiA6IFsgewogICAgICAgICAgICAgICAibmFtZSIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAib3BlcmFuZFR5cGVTcGVjaWZpZXIiIDogewogICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMzUzOjMyLTM1Mzo0MiIsCiAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJ7aHR0cDovL2hsNy5vcmcvZmhpcn1TZWN0aW9uTW9kZSIsCiAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJOYW1lZFR5cGVTcGVjaWZpZXIiCiAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgfSBdCiAgICAgICAgIH0sIHsKICAgICAgICAgICAgImxvY2F0b3IiIDogIjM1NDoxLTM1NDo1NyIsCiAgICAgICAgICAgICJuYW1lIiA6ICJUb1N0cmluZyIsCiAgICAgICAgICAgICJjb250ZXh0IiA6ICJQYXRpZW50IiwKICAgICAgICAgICAgImFjY2Vzc0xldmVsIiA6ICJQdWJsaWMiLAogICAgICAgICAgICAidHlwZSIgOiAiRnVuY3Rpb25EZWYiLAogICAgICAgICAgICAiZXhwcmVzc2lvbiIgOiB7CiAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIzNTQ6NDctMzU0OjU3IiwKICAgICAgICAgICAgICAgInBhdGgiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgInR5cGUiIDogIlByb3BlcnR5IiwKICAgICAgICAgICAgICAgInNvdXJjZSIgOiB7CiAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIzNTQ6NDctMzU0OjUxIiwKICAgICAgICAgICAgICAgICAgIm5hbWUiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk9wZXJhbmRSZWYiCiAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgfSwKICAgICAgICAgICAgIm9wZXJhbmQiIDogWyB7CiAgICAgICAgICAgICAgICJuYW1lIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICJvcGVyYW5kVHlwZVNwZWNpZmllciIgOiB7CiAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIzNTQ6MzItMzU0OjQzIiwKICAgICAgICAgICAgICAgICAgIm5hbWUiIDogIntodHRwOi8vaGw3Lm9yZy9maGlyfVNlcXVlbmNlVHlwZSIsCiAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJOYW1lZFR5cGVTcGVjaWZpZXIiCiAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgfSBdCiAgICAgICAgIH0sIHsKICAgICAgICAgICAgImxvY2F0b3IiIDogIjM1NToxLTM1NTo2NSIsCiAgICAgICAgICAgICJuYW1lIiA6ICJUb1N0cmluZyIsCiAgICAgICAgICAgICJjb250ZXh0IiA6ICJQYXRpZW50IiwKICAgICAgICAgICAgImFjY2Vzc0xldmVsIiA6ICJQdWJsaWMiLAogICAgICAgICAgICAidHlwZSIgOiAiRnVuY3Rpb25EZWYiLAogICAgICAgICAgICAiZXhwcmVzc2lvbiIgOiB7CiAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIzNTU6NTUtMzU1OjY1IiwKICAgICAgICAgICAgICAgInBhdGgiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgInR5cGUiIDogIlByb3BlcnR5IiwKICAgICAgICAgICAgICAgInNvdXJjZSIgOiB7CiAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIzNTU6NTUtMzU1OjU5IiwKICAgICAgICAgICAgICAgICAgIm5hbWUiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk9wZXJhbmRSZWYiCiAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgfSwKICAgICAgICAgICAgIm9wZXJhbmQiIDogWyB7CiAgICAgICAgICAgICAgICJuYW1lIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICJvcGVyYW5kVHlwZVNwZWNpZmllciIgOiB7CiAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIzNTU6MzItMzU1OjUxIiwKICAgICAgICAgICAgICAgICAgIm5hbWUiIDogIntodHRwOi8vaGw3Lm9yZy9maGlyfVNlcnZpY2VSZXF1ZXN0SW50ZW50IiwKICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk5hbWVkVHlwZVNwZWNpZmllciIKICAgICAgICAgICAgICAgfQogICAgICAgICAgICB9IF0KICAgICAgICAgfSwgewogICAgICAgICAgICAibG9jYXRvciIgOiAiMzU2OjEtMzU2OjY3IiwKICAgICAgICAgICAgIm5hbWUiIDogIlRvU3RyaW5nIiwKICAgICAgICAgICAgImNvbnRleHQiIDogIlBhdGllbnQiLAogICAgICAgICAgICAiYWNjZXNzTGV2ZWwiIDogIlB1YmxpYyIsCiAgICAgICAgICAgICJ0eXBlIiA6ICJGdW5jdGlvbkRlZiIsCiAgICAgICAgICAgICJleHByZXNzaW9uIiA6IHsKICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjM1Njo1Ny0zNTY6NjciLAogICAgICAgICAgICAgICAicGF0aCIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAidHlwZSIgOiAiUHJvcGVydHkiLAogICAgICAgICAgICAgICAic291cmNlIiA6IHsKICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjM1Njo1Ny0zNTY6NjEiLAogICAgICAgICAgICAgICAgICAibmFtZSIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAgICAidHlwZSIgOiAiT3BlcmFuZFJlZiIKICAgICAgICAgICAgICAgfQogICAgICAgICAgICB9LAogICAgICAgICAgICAib3BlcmFuZCIgOiBbIHsKICAgICAgICAgICAgICAgIm5hbWUiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgIm9wZXJhbmRUeXBlU3BlY2lmaWVyIiA6IHsKICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjM1NjozMi0zNTY6NTMiLAogICAgICAgICAgICAgICAgICAibmFtZSIgOiAie2h0dHA6Ly9obDcub3JnL2ZoaXJ9U2VydmljZVJlcXVlc3RQcmlvcml0eSIsCiAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJOYW1lZFR5cGVTcGVjaWZpZXIiCiAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgfSBdCiAgICAgICAgIH0sIHsKICAgICAgICAgICAgImxvY2F0b3IiIDogIjM1NzoxLTM1Nzo2NSIsCiAgICAgICAgICAgICJuYW1lIiA6ICJUb1N0cmluZyIsCiAgICAgICAgICAgICJjb250ZXh0IiA6ICJQYXRpZW50IiwKICAgICAgICAgICAgImFjY2Vzc0xldmVsIiA6ICJQdWJsaWMiLAogICAgICAgICAgICAidHlwZSIgOiAiRnVuY3Rpb25EZWYiLAogICAgICAgICAgICAiZXhwcmVzc2lvbiIgOiB7CiAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIzNTc6NTUtMzU3OjY1IiwKICAgICAgICAgICAgICAgInBhdGgiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgInR5cGUiIDogIlByb3BlcnR5IiwKICAgICAgICAgICAgICAgInNvdXJjZSIgOiB7CiAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIzNTc6NTUtMzU3OjU5IiwKICAgICAgICAgICAgICAgICAgIm5hbWUiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk9wZXJhbmRSZWYiCiAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgfSwKICAgICAgICAgICAgIm9wZXJhbmQiIDogWyB7CiAgICAgICAgICAgICAgICJuYW1lIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICJvcGVyYW5kVHlwZVNwZWNpZmllciIgOiB7CiAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIzNTc6MzItMzU3OjUxIiwKICAgICAgICAgICAgICAgICAgIm5hbWUiIDogIntodHRwOi8vaGw3Lm9yZy9maGlyfVNlcnZpY2VSZXF1ZXN0U3RhdHVzIiwKICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk5hbWVkVHlwZVNwZWNpZmllciIKICAgICAgICAgICAgICAgfQogICAgICAgICAgICB9IF0KICAgICAgICAgfSwgewogICAgICAgICAgICAibG9jYXRvciIgOiAiMzU4OjEtMzU4OjU3IiwKICAgICAgICAgICAgIm5hbWUiIDogIlRvU3RyaW5nIiwKICAgICAgICAgICAgImNvbnRleHQiIDogIlBhdGllbnQiLAogICAgICAgICAgICAiYWNjZXNzTGV2ZWwiIDogIlB1YmxpYyIsCiAgICAgICAgICAgICJ0eXBlIiA6ICJGdW5jdGlvbkRlZiIsCiAgICAgICAgICAgICJleHByZXNzaW9uIiA6IHsKICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjM1ODo0Ny0zNTg6NTciLAogICAgICAgICAgICAgICAicGF0aCIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAidHlwZSIgOiAiUHJvcGVydHkiLAogICAgICAgICAgICAgICAic291cmNlIiA6IHsKICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjM1ODo0Ny0zNTg6NTEiLAogICAgICAgICAgICAgICAgICAibmFtZSIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAgICAidHlwZSIgOiAiT3BlcmFuZFJlZiIKICAgICAgICAgICAgICAgfQogICAgICAgICAgICB9LAogICAgICAgICAgICAib3BlcmFuZCIgOiBbIHsKICAgICAgICAgICAgICAgIm5hbWUiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgIm9wZXJhbmRUeXBlU3BlY2lmaWVyIiA6IHsKICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjM1ODozMi0zNTg6NDMiLAogICAgICAgICAgICAgICAgICAibmFtZSIgOiAie2h0dHA6Ly9obDcub3JnL2ZoaXJ9U2xpY2luZ1J1bGVzIiwKICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk5hbWVkVHlwZVNwZWNpZmllciIKICAgICAgICAgICAgICAgfQogICAgICAgICAgICB9IF0KICAgICAgICAgfSwgewogICAgICAgICAgICAibG9jYXRvciIgOiAiMzU5OjEtMzU5OjU1IiwKICAgICAgICAgICAgIm5hbWUiIDogIlRvU3RyaW5nIiwKICAgICAgICAgICAgImNvbnRleHQiIDogIlBhdGllbnQiLAogICAgICAgICAgICAiYWNjZXNzTGV2ZWwiIDogIlB1YmxpYyIsCiAgICAgICAgICAgICJ0eXBlIiA6ICJGdW5jdGlvbkRlZiIsCiAgICAgICAgICAgICJleHByZXNzaW9uIiA6IHsKICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjM1OTo0NS0zNTk6NTUiLAogICAgICAgICAgICAgICAicGF0aCIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAidHlwZSIgOiAiUHJvcGVydHkiLAogICAgICAgICAgICAgICAic291cmNlIiA6IHsKICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjM1OTo0NS0zNTk6NDkiLAogICAgICAgICAgICAgICAgICAibmFtZSIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAgICAidHlwZSIgOiAiT3BlcmFuZFJlZiIKICAgICAgICAgICAgICAgfQogICAgICAgICAgICB9LAogICAgICAgICAgICAib3BlcmFuZCIgOiBbIHsKICAgICAgICAgICAgICAgIm5hbWUiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgIm9wZXJhbmRUeXBlU3BlY2lmaWVyIiA6IHsKICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjM1OTozMi0zNTk6NDEiLAogICAgICAgICAgICAgICAgICAibmFtZSIgOiAie2h0dHA6Ly9obDcub3JnL2ZoaXJ9U2xvdFN0YXR1cyIsCiAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJOYW1lZFR5cGVTcGVjaWZpZXIiCiAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgfSBdCiAgICAgICAgIH0sIHsKICAgICAgICAgICAgImxvY2F0b3IiIDogIjM2MDoxLTM2MDo1OCIsCiAgICAgICAgICAgICJuYW1lIiA6ICJUb1N0cmluZyIsCiAgICAgICAgICAgICJjb250ZXh0IiA6ICJQYXRpZW50IiwKICAgICAgICAgICAgImFjY2Vzc0xldmVsIiA6ICJQdWJsaWMiLAogICAgICAgICAgICAidHlwZSIgOiAiRnVuY3Rpb25EZWYiLAogICAgICAgICAgICAiZXhwcmVzc2lvbiIgOiB7CiAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIzNjA6NDgtMzYwOjU4IiwKICAgICAgICAgICAgICAgInBhdGgiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgInR5cGUiIDogIlByb3BlcnR5IiwKICAgICAgICAgICAgICAgInNvdXJjZSIgOiB7CiAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIzNjA6NDgtMzYwOjUyIiwKICAgICAgICAgICAgICAgICAgIm5hbWUiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk9wZXJhbmRSZWYiCiAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgfSwKICAgICAgICAgICAgIm9wZXJhbmQiIDogWyB7CiAgICAgICAgICAgICAgICJuYW1lIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICJvcGVyYW5kVHlwZVNwZWNpZmllciIgOiB7CiAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIzNjA6MzItMzYwOjQ0IiwKICAgICAgICAgICAgICAgICAgIm5hbWUiIDogIntodHRwOi8vaGw3Lm9yZy9maGlyfVNvcnREaXJlY3Rpb24iLAogICAgICAgICAgICAgICAgICAidHlwZSIgOiAiTmFtZWRUeXBlU3BlY2lmaWVyIgogICAgICAgICAgICAgICB9CiAgICAgICAgICAgIH0gXQogICAgICAgICB9LCB7CiAgICAgICAgICAgICJsb2NhdG9yIiA6ICIzNjE6MS0zNjE6NzIiLAogICAgICAgICAgICAibmFtZSIgOiAiVG9TdHJpbmciLAogICAgICAgICAgICAiY29udGV4dCIgOiAiUGF0aWVudCIsCiAgICAgICAgICAgICJhY2Nlc3NMZXZlbCIgOiAiUHVibGljIiwKICAgICAgICAgICAgInR5cGUiIDogIkZ1bmN0aW9uRGVmIiwKICAgICAgICAgICAgImV4cHJlc3Npb24iIDogewogICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMzYxOjYyLTM2MTo3MiIsCiAgICAgICAgICAgICAgICJwYXRoIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICJ0eXBlIiA6ICJQcm9wZXJ0eSIsCiAgICAgICAgICAgICAgICJzb3VyY2UiIDogewogICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMzYxOjYyLTM2MTo2NiIsCiAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJPcGVyYW5kUmVmIgogICAgICAgICAgICAgICB9CiAgICAgICAgICAgIH0sCiAgICAgICAgICAgICJvcGVyYW5kIiA6IFsgewogICAgICAgICAgICAgICAibmFtZSIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAib3BlcmFuZFR5cGVTcGVjaWZpZXIiIDogewogICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMzYxOjMyLTM2MTo1OCIsCiAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJ7aHR0cDovL2hsNy5vcmcvZmhpcn1TcGVjaW1lbkNvbnRhaW5lZFByZWZlcmVuY2UiLAogICAgICAgICAgICAgICAgICAidHlwZSIgOiAiTmFtZWRUeXBlU3BlY2lmaWVyIgogICAgICAgICAgICAgICB9CiAgICAgICAgICAgIH0gXQogICAgICAgICB9LCB7CiAgICAgICAgICAgICJsb2NhdG9yIiA6ICIzNjI6MS0zNjI6NTkiLAogICAgICAgICAgICAibmFtZSIgOiAiVG9TdHJpbmciLAogICAgICAgICAgICAiY29udGV4dCIgOiAiUGF0aWVudCIsCiAgICAgICAgICAgICJhY2Nlc3NMZXZlbCIgOiAiUHVibGljIiwKICAgICAgICAgICAgInR5cGUiIDogIkZ1bmN0aW9uRGVmIiwKICAgICAgICAgICAgImV4cHJlc3Npb24iIDogewogICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMzYyOjQ5LTM2Mjo1OSIsCiAgICAgICAgICAgICAgICJwYXRoIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICJ0eXBlIiA6ICJQcm9wZXJ0eSIsCiAgICAgICAgICAgICAgICJzb3VyY2UiIDogewogICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMzYyOjQ5LTM2Mjo1MyIsCiAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJPcGVyYW5kUmVmIgogICAgICAgICAgICAgICB9CiAgICAgICAgICAgIH0sCiAgICAgICAgICAgICJvcGVyYW5kIiA6IFsgewogICAgICAgICAgICAgICAibmFtZSIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAib3BlcmFuZFR5cGVTcGVjaWZpZXIiIDogewogICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMzYyOjMyLTM2Mjo0NSIsCiAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJ7aHR0cDovL2hsNy5vcmcvZmhpcn1TcGVjaW1lblN0YXR1cyIsCiAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJOYW1lZFR5cGVTcGVjaWZpZXIiCiAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgfSBdCiAgICAgICAgIH0sIHsKICAgICAgICAgICAgImxvY2F0b3IiIDogIjM2MzoxLTM2Mzo1MSIsCiAgICAgICAgICAgICJuYW1lIiA6ICJUb1N0cmluZyIsCiAgICAgICAgICAgICJjb250ZXh0IiA6ICJQYXRpZW50IiwKICAgICAgICAgICAgImFjY2Vzc0xldmVsIiA6ICJQdWJsaWMiLAogICAgICAgICAgICAidHlwZSIgOiAiRnVuY3Rpb25EZWYiLAogICAgICAgICAgICAiZXhwcmVzc2lvbiIgOiB7CiAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIzNjM6NDEtMzYzOjUxIiwKICAgICAgICAgICAgICAgInBhdGgiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgInR5cGUiIDogIlByb3BlcnR5IiwKICAgICAgICAgICAgICAgInNvdXJjZSIgOiB7CiAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIzNjM6NDEtMzYzOjQ1IiwKICAgICAgICAgICAgICAgICAgIm5hbWUiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk9wZXJhbmRSZWYiCiAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgfSwKICAgICAgICAgICAgIm9wZXJhbmQiIDogWyB7CiAgICAgICAgICAgICAgICJuYW1lIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICJvcGVyYW5kVHlwZVNwZWNpZmllciIgOiB7CiAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIzNjM6MzItMzYzOjM3IiwKICAgICAgICAgICAgICAgICAgIm5hbWUiIDogIntodHRwOi8vaGw3Lm9yZy9maGlyfVN0YXR1cyIsCiAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJOYW1lZFR5cGVTcGVjaWZpZXIiCiAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgfSBdCiAgICAgICAgIH0sIHsKICAgICAgICAgICAgImxvY2F0b3IiIDogIjM2NDoxLTM2NDo1NSIsCiAgICAgICAgICAgICJuYW1lIiA6ICJUb1N0cmluZyIsCiAgICAgICAgICAgICJjb250ZXh0IiA6ICJQYXRpZW50IiwKICAgICAgICAgICAgImFjY2Vzc0xldmVsIiA6ICJQdWJsaWMiLAogICAgICAgICAgICAidHlwZSIgOiAiRnVuY3Rpb25EZWYiLAogICAgICAgICAgICAiZXhwcmVzc2lvbiIgOiB7CiAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIzNjQ6NDUtMzY0OjU1IiwKICAgICAgICAgICAgICAgInBhdGgiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgInR5cGUiIDogIlByb3BlcnR5IiwKICAgICAgICAgICAgICAgInNvdXJjZSIgOiB7CiAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIzNjQ6NDUtMzY0OjQ5IiwKICAgICAgICAgICAgICAgICAgIm5hbWUiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk9wZXJhbmRSZWYiCiAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgfSwKICAgICAgICAgICAgIm9wZXJhbmQiIDogWyB7CiAgICAgICAgICAgICAgICJuYW1lIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICJvcGVyYW5kVHlwZVNwZWNpZmllciIgOiB7CiAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIzNjQ6MzItMzY0OjQxIiwKICAgICAgICAgICAgICAgICAgIm5hbWUiIDogIntodHRwOi8vaGw3Lm9yZy9maGlyfVN0cmFuZFR5cGUiLAogICAgICAgICAgICAgICAgICAidHlwZSIgOiAiTmFtZWRUeXBlU3BlY2lmaWVyIgogICAgICAgICAgICAgICB9CiAgICAgICAgICAgIH0gXQogICAgICAgICB9LCB7CiAgICAgICAgICAgICJsb2NhdG9yIiA6ICIzNjU6MS0zNjU6NjgiLAogICAgICAgICAgICAibmFtZSIgOiAiVG9TdHJpbmciLAogICAgICAgICAgICAiY29udGV4dCIgOiAiUGF0aWVudCIsCiAgICAgICAgICAgICJhY2Nlc3NMZXZlbCIgOiAiUHVibGljIiwKICAgICAgICAgICAgInR5cGUiIDogIkZ1bmN0aW9uRGVmIiwKICAgICAgICAgICAgImV4cHJlc3Npb24iIDogewogICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMzY1OjU4LTM2NTo2OCIsCiAgICAgICAgICAgICAgICJwYXRoIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICJ0eXBlIiA6ICJQcm9wZXJ0eSIsCiAgICAgICAgICAgICAgICJzb3VyY2UiIDogewogICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMzY1OjU4LTM2NTo2MiIsCiAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJPcGVyYW5kUmVmIgogICAgICAgICAgICAgICB9CiAgICAgICAgICAgIH0sCiAgICAgICAgICAgICJvcGVyYW5kIiA6IFsgewogICAgICAgICAgICAgICAibmFtZSIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAib3BlcmFuZFR5cGVTcGVjaWZpZXIiIDogewogICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMzY1OjMyLTM2NTo1NCIsCiAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJ7aHR0cDovL2hsNy5vcmcvZmhpcn1TdHJ1Y3R1cmVEZWZpbml0aW9uS2luZCIsCiAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJOYW1lZFR5cGVTcGVjaWZpZXIiCiAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgfSBdCiAgICAgICAgIH0sIHsKICAgICAgICAgICAgImxvY2F0b3IiIDogIjM2NjoxLTM2Njo2OCIsCiAgICAgICAgICAgICJuYW1lIiA6ICJUb1N0cmluZyIsCiAgICAgICAgICAgICJjb250ZXh0IiA6ICJQYXRpZW50IiwKICAgICAgICAgICAgImFjY2Vzc0xldmVsIiA6ICJQdWJsaWMiLAogICAgICAgICAgICAidHlwZSIgOiAiRnVuY3Rpb25EZWYiLAogICAgICAgICAgICAiZXhwcmVzc2lvbiIgOiB7CiAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIzNjY6NTgtMzY2OjY4IiwKICAgICAgICAgICAgICAgInBhdGgiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgInR5cGUiIDogIlByb3BlcnR5IiwKICAgICAgICAgICAgICAgInNvdXJjZSIgOiB7CiAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIzNjY6NTgtMzY2OjYyIiwKICAgICAgICAgICAgICAgICAgIm5hbWUiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk9wZXJhbmRSZWYiCiAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgfSwKICAgICAgICAgICAgIm9wZXJhbmQiIDogWyB7CiAgICAgICAgICAgICAgICJuYW1lIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICJvcGVyYW5kVHlwZVNwZWNpZmllciIgOiB7CiAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIzNjY6MzItMzY2OjU0IiwKICAgICAgICAgICAgICAgICAgIm5hbWUiIDogIntodHRwOi8vaGw3Lm9yZy9maGlyfVN0cnVjdHVyZU1hcENvbnRleHRUeXBlIiwKICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk5hbWVkVHlwZVNwZWNpZmllciIKICAgICAgICAgICAgICAgfQogICAgICAgICAgICB9IF0KICAgICAgICAgfSwgewogICAgICAgICAgICAibG9jYXRvciIgOiAiMzY3OjEtMzY3OjcwIiwKICAgICAgICAgICAgIm5hbWUiIDogIlRvU3RyaW5nIiwKICAgICAgICAgICAgImNvbnRleHQiIDogIlBhdGllbnQiLAogICAgICAgICAgICAiYWNjZXNzTGV2ZWwiIDogIlB1YmxpYyIsCiAgICAgICAgICAgICJ0eXBlIiA6ICJGdW5jdGlvbkRlZiIsCiAgICAgICAgICAgICJleHByZXNzaW9uIiA6IHsKICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjM2Nzo2MC0zNjc6NzAiLAogICAgICAgICAgICAgICAicGF0aCIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAidHlwZSIgOiAiUHJvcGVydHkiLAogICAgICAgICAgICAgICAic291cmNlIiA6IHsKICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjM2Nzo2MC0zNjc6NjQiLAogICAgICAgICAgICAgICAgICAibmFtZSIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAgICAidHlwZSIgOiAiT3BlcmFuZFJlZiIKICAgICAgICAgICAgICAgfQogICAgICAgICAgICB9LAogICAgICAgICAgICAib3BlcmFuZCIgOiBbIHsKICAgICAgICAgICAgICAgIm5hbWUiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgIm9wZXJhbmRUeXBlU3BlY2lmaWVyIiA6IHsKICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjM2NzozMi0zNjc6NTYiLAogICAgICAgICAgICAgICAgICAibmFtZSIgOiAie2h0dHA6Ly9obDcub3JnL2ZoaXJ9U3RydWN0dXJlTWFwR3JvdXBUeXBlTW9kZSIsCiAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJOYW1lZFR5cGVTcGVjaWZpZXIiCiAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgfSBdCiAgICAgICAgIH0sIHsKICAgICAgICAgICAgImxvY2F0b3IiIDogIjM2ODoxLTM2ODo2NiIsCiAgICAgICAgICAgICJuYW1lIiA6ICJUb1N0cmluZyIsCiAgICAgICAgICAgICJjb250ZXh0IiA6ICJQYXRpZW50IiwKICAgICAgICAgICAgImFjY2Vzc0xldmVsIiA6ICJQdWJsaWMiLAogICAgICAgICAgICAidHlwZSIgOiAiRnVuY3Rpb25EZWYiLAogICAgICAgICAgICAiZXhwcmVzc2lvbiIgOiB7CiAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIzNjg6NTYtMzY4OjY2IiwKICAgICAgICAgICAgICAgInBhdGgiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgInR5cGUiIDogIlByb3BlcnR5IiwKICAgICAgICAgICAgICAgInNvdXJjZSIgOiB7CiAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIzNjg6NTYtMzY4OjYwIiwKICAgICAgICAgICAgICAgICAgIm5hbWUiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk9wZXJhbmRSZWYiCiAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgfSwKICAgICAgICAgICAgIm9wZXJhbmQiIDogWyB7CiAgICAgICAgICAgICAgICJuYW1lIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICJvcGVyYW5kVHlwZVNwZWNpZmllciIgOiB7CiAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIzNjg6MzItMzY4OjUyIiwKICAgICAgICAgICAgICAgICAgIm5hbWUiIDogIntodHRwOi8vaGw3Lm9yZy9maGlyfVN0cnVjdHVyZU1hcElucHV0TW9kZSIsCiAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJOYW1lZFR5cGVTcGVjaWZpZXIiCiAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgfSBdCiAgICAgICAgIH0sIHsKICAgICAgICAgICAgImxvY2F0b3IiIDogIjM2OToxLTM2OTo2NiIsCiAgICAgICAgICAgICJuYW1lIiA6ICJUb1N0cmluZyIsCiAgICAgICAgICAgICJjb250ZXh0IiA6ICJQYXRpZW50IiwKICAgICAgICAgICAgImFjY2Vzc0xldmVsIiA6ICJQdWJsaWMiLAogICAgICAgICAgICAidHlwZSIgOiAiRnVuY3Rpb25EZWYiLAogICAgICAgICAgICAiZXhwcmVzc2lvbiIgOiB7CiAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIzNjk6NTYtMzY5OjY2IiwKICAgICAgICAgICAgICAgInBhdGgiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgInR5cGUiIDogIlByb3BlcnR5IiwKICAgICAgICAgICAgICAgInNvdXJjZSIgOiB7CiAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIzNjk6NTYtMzY5OjYwIiwKICAgICAgICAgICAgICAgICAgIm5hbWUiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk9wZXJhbmRSZWYiCiAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgfSwKICAgICAgICAgICAgIm9wZXJhbmQiIDogWyB7CiAgICAgICAgICAgICAgICJuYW1lIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICJvcGVyYW5kVHlwZVNwZWNpZmllciIgOiB7CiAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIzNjk6MzItMzY5OjUyIiwKICAgICAgICAgICAgICAgICAgIm5hbWUiIDogIntodHRwOi8vaGw3Lm9yZy9maGlyfVN0cnVjdHVyZU1hcE1vZGVsTW9kZSIsCiAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJOYW1lZFR5cGVTcGVjaWZpZXIiCiAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgfSBdCiAgICAgICAgIH0sIHsKICAgICAgICAgICAgImxvY2F0b3IiIDogIjM3MDoxLTM3MDo3MSIsCiAgICAgICAgICAgICJuYW1lIiA6ICJUb1N0cmluZyIsCiAgICAgICAgICAgICJjb250ZXh0IiA6ICJQYXRpZW50IiwKICAgICAgICAgICAgImFjY2Vzc0xldmVsIiA6ICJQdWJsaWMiLAogICAgICAgICAgICAidHlwZSIgOiAiRnVuY3Rpb25EZWYiLAogICAgICAgICAgICAiZXhwcmVzc2lvbiIgOiB7CiAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIzNzA6NjEtMzcwOjcxIiwKICAgICAgICAgICAgICAgInBhdGgiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgInR5cGUiIDogIlByb3BlcnR5IiwKICAgICAgICAgICAgICAgInNvdXJjZSIgOiB7CiAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIzNzA6NjEtMzcwOjY1IiwKICAgICAgICAgICAgICAgICAgIm5hbWUiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk9wZXJhbmRSZWYiCiAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgfSwKICAgICAgICAgICAgIm9wZXJhbmQiIDogWyB7CiAgICAgICAgICAgICAgICJuYW1lIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICJvcGVyYW5kVHlwZVNwZWNpZmllciIgOiB7CiAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIzNzA6MzItMzcwOjU3IiwKICAgICAgICAgICAgICAgICAgIm5hbWUiIDogIntodHRwOi8vaGw3Lm9yZy9maGlyfVN0cnVjdHVyZU1hcFNvdXJjZUxpc3RNb2RlIiwKICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk5hbWVkVHlwZVNwZWNpZmllciIKICAgICAgICAgICAgICAgfQogICAgICAgICAgICB9IF0KICAgICAgICAgfSwgewogICAgICAgICAgICAibG9jYXRvciIgOiAiMzcxOjEtMzcxOjcxIiwKICAgICAgICAgICAgIm5hbWUiIDogIlRvU3RyaW5nIiwKICAgICAgICAgICAgImNvbnRleHQiIDogIlBhdGllbnQiLAogICAgICAgICAgICAiYWNjZXNzTGV2ZWwiIDogIlB1YmxpYyIsCiAgICAgICAgICAgICJ0eXBlIiA6ICJGdW5jdGlvbkRlZiIsCiAgICAgICAgICAgICJleHByZXNzaW9uIiA6IHsKICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjM3MTo2MS0zNzE6NzEiLAogICAgICAgICAgICAgICAicGF0aCIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAidHlwZSIgOiAiUHJvcGVydHkiLAogICAgICAgICAgICAgICAic291cmNlIiA6IHsKICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjM3MTo2MS0zNzE6NjUiLAogICAgICAgICAgICAgICAgICAibmFtZSIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAgICAidHlwZSIgOiAiT3BlcmFuZFJlZiIKICAgICAgICAgICAgICAgfQogICAgICAgICAgICB9LAogICAgICAgICAgICAib3BlcmFuZCIgOiBbIHsKICAgICAgICAgICAgICAgIm5hbWUiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgIm9wZXJhbmRUeXBlU3BlY2lmaWVyIiA6IHsKICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjM3MTozMi0zNzE6NTciLAogICAgICAgICAgICAgICAgICAibmFtZSIgOiAie2h0dHA6Ly9obDcub3JnL2ZoaXJ9U3RydWN0dXJlTWFwVGFyZ2V0TGlzdE1vZGUiLAogICAgICAgICAgICAgICAgICAidHlwZSIgOiAiTmFtZWRUeXBlU3BlY2lmaWVyIgogICAgICAgICAgICAgICB9CiAgICAgICAgICAgIH0gXQogICAgICAgICB9LCB7CiAgICAgICAgICAgICJsb2NhdG9yIiA6ICIzNzI6MS0zNzI6NjYiLAogICAgICAgICAgICAibmFtZSIgOiAiVG9TdHJpbmciLAogICAgICAgICAgICAiY29udGV4dCIgOiAiUGF0aWVudCIsCiAgICAgICAgICAgICJhY2Nlc3NMZXZlbCIgOiAiUHVibGljIiwKICAgICAgICAgICAgInR5cGUiIDogIkZ1bmN0aW9uRGVmIiwKICAgICAgICAgICAgImV4cHJlc3Npb24iIDogewogICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMzcyOjU2LTM3Mjo2NiIsCiAgICAgICAgICAgICAgICJwYXRoIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICJ0eXBlIiA6ICJQcm9wZXJ0eSIsCiAgICAgICAgICAgICAgICJzb3VyY2UiIDogewogICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMzcyOjU2LTM3Mjo2MCIsCiAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJPcGVyYW5kUmVmIgogICAgICAgICAgICAgICB9CiAgICAgICAgICAgIH0sCiAgICAgICAgICAgICJvcGVyYW5kIiA6IFsgewogICAgICAgICAgICAgICAibmFtZSIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAib3BlcmFuZFR5cGVTcGVjaWZpZXIiIDogewogICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMzcyOjMyLTM3Mjo1MiIsCiAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJ7aHR0cDovL2hsNy5vcmcvZmhpcn1TdHJ1Y3R1cmVNYXBUcmFuc2Zvcm0iLAogICAgICAgICAgICAgICAgICAidHlwZSIgOiAiTmFtZWRUeXBlU3BlY2lmaWVyIgogICAgICAgICAgICAgICB9CiAgICAgICAgICAgIH0gXQogICAgICAgICB9LCB7CiAgICAgICAgICAgICJsb2NhdG9yIiA6ICIzNzM6MS0zNzM6NjgiLAogICAgICAgICAgICAibmFtZSIgOiAiVG9TdHJpbmciLAogICAgICAgICAgICAiY29udGV4dCIgOiAiUGF0aWVudCIsCiAgICAgICAgICAgICJhY2Nlc3NMZXZlbCIgOiAiUHVibGljIiwKICAgICAgICAgICAgInR5cGUiIDogIkZ1bmN0aW9uRGVmIiwKICAgICAgICAgICAgImV4cHJlc3Npb24iIDogewogICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMzczOjU4LTM3Mzo2OCIsCiAgICAgICAgICAgICAgICJwYXRoIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICJ0eXBlIiA6ICJQcm9wZXJ0eSIsCiAgICAgICAgICAgICAgICJzb3VyY2UiIDogewogICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMzczOjU4LTM3Mzo2MiIsCiAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJPcGVyYW5kUmVmIgogICAgICAgICAgICAgICB9CiAgICAgICAgICAgIH0sCiAgICAgICAgICAgICJvcGVyYW5kIiA6IFsgewogICAgICAgICAgICAgICAibmFtZSIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAib3BlcmFuZFR5cGVTcGVjaWZpZXIiIDogewogICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMzczOjMyLTM3Mzo1NCIsCiAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJ7aHR0cDovL2hsNy5vcmcvZmhpcn1TdWJzY3JpcHRpb25DaGFubmVsVHlwZSIsCiAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJOYW1lZFR5cGVTcGVjaWZpZXIiCiAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgfSBdCiAgICAgICAgIH0sIHsKICAgICAgICAgICAgImxvY2F0b3IiIDogIjM3NDoxLTM3NDo2MyIsCiAgICAgICAgICAgICJuYW1lIiA6ICJUb1N0cmluZyIsCiAgICAgICAgICAgICJjb250ZXh0IiA6ICJQYXRpZW50IiwKICAgICAgICAgICAgImFjY2Vzc0xldmVsIiA6ICJQdWJsaWMiLAogICAgICAgICAgICAidHlwZSIgOiAiRnVuY3Rpb25EZWYiLAogICAgICAgICAgICAiZXhwcmVzc2lvbiIgOiB7CiAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIzNzQ6NTMtMzc0OjYzIiwKICAgICAgICAgICAgICAgInBhdGgiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgInR5cGUiIDogIlByb3BlcnR5IiwKICAgICAgICAgICAgICAgInNvdXJjZSIgOiB7CiAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIzNzQ6NTMtMzc0OjU3IiwKICAgICAgICAgICAgICAgICAgIm5hbWUiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk9wZXJhbmRSZWYiCiAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgfSwKICAgICAgICAgICAgIm9wZXJhbmQiIDogWyB7CiAgICAgICAgICAgICAgICJuYW1lIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICJvcGVyYW5kVHlwZVNwZWNpZmllciIgOiB7CiAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIzNzQ6MzItMzc0OjQ5IiwKICAgICAgICAgICAgICAgICAgIm5hbWUiIDogIntodHRwOi8vaGw3Lm9yZy9maGlyfVN1YnNjcmlwdGlvblN0YXR1cyIsCiAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJOYW1lZFR5cGVTcGVjaWZpZXIiCiAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgfSBdCiAgICAgICAgIH0sIHsKICAgICAgICAgICAgImxvY2F0b3IiIDogIjM3NToxLTM3NTo2NSIsCiAgICAgICAgICAgICJuYW1lIiA6ICJUb1N0cmluZyIsCiAgICAgICAgICAgICJjb250ZXh0IiA6ICJQYXRpZW50IiwKICAgICAgICAgICAgImFjY2Vzc0xldmVsIiA6ICJQdWJsaWMiLAogICAgICAgICAgICAidHlwZSIgOiAiRnVuY3Rpb25EZWYiLAogICAgICAgICAgICAiZXhwcmVzc2lvbiIgOiB7CiAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIzNzU6NTUtMzc1OjY1IiwKICAgICAgICAgICAgICAgInBhdGgiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgInR5cGUiIDogIlByb3BlcnR5IiwKICAgICAgICAgICAgICAgInNvdXJjZSIgOiB7CiAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIzNzU6NTUtMzc1OjU5IiwKICAgICAgICAgICAgICAgICAgIm5hbWUiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk9wZXJhbmRSZWYiCiAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgfSwKICAgICAgICAgICAgIm9wZXJhbmQiIDogWyB7CiAgICAgICAgICAgICAgICJuYW1lIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICJvcGVyYW5kVHlwZVNwZWNpZmllciIgOiB7CiAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIzNzU6MzItMzc1OjUxIiwKICAgICAgICAgICAgICAgICAgIm5hbWUiIDogIntodHRwOi8vaGw3Lm9yZy9maGlyfVN1cHBseURlbGl2ZXJ5U3RhdHVzIiwKICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk5hbWVkVHlwZVNwZWNpZmllciIKICAgICAgICAgICAgICAgfQogICAgICAgICAgICB9IF0KICAgICAgICAgfSwgewogICAgICAgICAgICAibG9jYXRvciIgOiAiMzc2OjEtMzc2OjY0IiwKICAgICAgICAgICAgIm5hbWUiIDogIlRvU3RyaW5nIiwKICAgICAgICAgICAgImNvbnRleHQiIDogIlBhdGllbnQiLAogICAgICAgICAgICAiYWNjZXNzTGV2ZWwiIDogIlB1YmxpYyIsCiAgICAgICAgICAgICJ0eXBlIiA6ICJGdW5jdGlvbkRlZiIsCiAgICAgICAgICAgICJleHByZXNzaW9uIiA6IHsKICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjM3Njo1NC0zNzY6NjQiLAogICAgICAgICAgICAgICAicGF0aCIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAidHlwZSIgOiAiUHJvcGVydHkiLAogICAgICAgICAgICAgICAic291cmNlIiA6IHsKICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjM3Njo1NC0zNzY6NTgiLAogICAgICAgICAgICAgICAgICAibmFtZSIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAgICAidHlwZSIgOiAiT3BlcmFuZFJlZiIKICAgICAgICAgICAgICAgfQogICAgICAgICAgICB9LAogICAgICAgICAgICAib3BlcmFuZCIgOiBbIHsKICAgICAgICAgICAgICAgIm5hbWUiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgIm9wZXJhbmRUeXBlU3BlY2lmaWVyIiA6IHsKICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjM3NjozMi0zNzY6NTAiLAogICAgICAgICAgICAgICAgICAibmFtZSIgOiAie2h0dHA6Ly9obDcub3JnL2ZoaXJ9U3VwcGx5UmVxdWVzdFN0YXR1cyIsCiAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJOYW1lZFR5cGVTcGVjaWZpZXIiCiAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgfSBdCiAgICAgICAgIH0sIHsKICAgICAgICAgICAgImxvY2F0b3IiIDogIjM3NzoxLTM3Nzo2OSIsCiAgICAgICAgICAgICJuYW1lIiA6ICJUb1N0cmluZyIsCiAgICAgICAgICAgICJjb250ZXh0IiA6ICJQYXRpZW50IiwKICAgICAgICAgICAgImFjY2Vzc0xldmVsIiA6ICJQdWJsaWMiLAogICAgICAgICAgICAidHlwZSIgOiAiRnVuY3Rpb25EZWYiLAogICAgICAgICAgICAiZXhwcmVzc2lvbiIgOiB7CiAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIzNzc6NTktMzc3OjY5IiwKICAgICAgICAgICAgICAgInBhdGgiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgInR5cGUiIDogIlByb3BlcnR5IiwKICAgICAgICAgICAgICAgInNvdXJjZSIgOiB7CiAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIzNzc6NTktMzc3OjYzIiwKICAgICAgICAgICAgICAgICAgIm5hbWUiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk9wZXJhbmRSZWYiCiAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgfSwKICAgICAgICAgICAgIm9wZXJhbmQiIDogWyB7CiAgICAgICAgICAgICAgICJuYW1lIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICJvcGVyYW5kVHlwZVNwZWNpZmllciIgOiB7CiAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIzNzc6MzItMzc3OjU1IiwKICAgICAgICAgICAgICAgICAgIm5hbWUiIDogIntodHRwOi8vaGw3Lm9yZy9maGlyfVN5c3RlbVJlc3RmdWxJbnRlcmFjdGlvbiIsCiAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJOYW1lZFR5cGVTcGVjaWZpZXIiCiAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgfSBdCiAgICAgICAgIH0sIHsKICAgICAgICAgICAgImxvY2F0b3IiIDogIjM3ODoxLTM3ODo1NSIsCiAgICAgICAgICAgICJuYW1lIiA6ICJUb1N0cmluZyIsCiAgICAgICAgICAgICJjb250ZXh0IiA6ICJQYXRpZW50IiwKICAgICAgICAgICAgImFjY2Vzc0xldmVsIiA6ICJQdWJsaWMiLAogICAgICAgICAgICAidHlwZSIgOiAiRnVuY3Rpb25EZWYiLAogICAgICAgICAgICAiZXhwcmVzc2lvbiIgOiB7CiAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIzNzg6NDUtMzc4OjU1IiwKICAgICAgICAgICAgICAgInBhdGgiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgInR5cGUiIDogIlByb3BlcnR5IiwKICAgICAgICAgICAgICAgInNvdXJjZSIgOiB7CiAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIzNzg6NDUtMzc4OjQ5IiwKICAgICAgICAgICAgICAgICAgIm5hbWUiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk9wZXJhbmRSZWYiCiAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgfSwKICAgICAgICAgICAgIm9wZXJhbmQiIDogWyB7CiAgICAgICAgICAgICAgICJuYW1lIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICJvcGVyYW5kVHlwZVNwZWNpZmllciIgOiB7CiAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIzNzg6MzItMzc4OjQxIiwKICAgICAgICAgICAgICAgICAgIm5hbWUiIDogIntodHRwOi8vaGw3Lm9yZy9maGlyfVRhc2tJbnRlbnQiLAogICAgICAgICAgICAgICAgICAidHlwZSIgOiAiTmFtZWRUeXBlU3BlY2lmaWVyIgogICAgICAgICAgICAgICB9CiAgICAgICAgICAgIH0gXQogICAgICAgICB9LCB7CiAgICAgICAgICAgICJsb2NhdG9yIiA6ICIzNzk6MS0zNzk6NTciLAogICAgICAgICAgICAibmFtZSIgOiAiVG9TdHJpbmciLAogICAgICAgICAgICAiY29udGV4dCIgOiAiUGF0aWVudCIsCiAgICAgICAgICAgICJhY2Nlc3NMZXZlbCIgOiAiUHVibGljIiwKICAgICAgICAgICAgInR5cGUiIDogIkZ1bmN0aW9uRGVmIiwKICAgICAgICAgICAgImV4cHJlc3Npb24iIDogewogICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMzc5OjQ3LTM3OTo1NyIsCiAgICAgICAgICAgICAgICJwYXRoIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICJ0eXBlIiA6ICJQcm9wZXJ0eSIsCiAgICAgICAgICAgICAgICJzb3VyY2UiIDogewogICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMzc5OjQ3LTM3OTo1MSIsCiAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJPcGVyYW5kUmVmIgogICAgICAgICAgICAgICB9CiAgICAgICAgICAgIH0sCiAgICAgICAgICAgICJvcGVyYW5kIiA6IFsgewogICAgICAgICAgICAgICAibmFtZSIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAib3BlcmFuZFR5cGVTcGVjaWZpZXIiIDogewogICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMzc5OjMyLTM3OTo0MyIsCiAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJ7aHR0cDovL2hsNy5vcmcvZmhpcn1UYXNrUHJpb3JpdHkiLAogICAgICAgICAgICAgICAgICAidHlwZSIgOiAiTmFtZWRUeXBlU3BlY2lmaWVyIgogICAgICAgICAgICAgICB9CiAgICAgICAgICAgIH0gXQogICAgICAgICB9LCB7CiAgICAgICAgICAgICJsb2NhdG9yIiA6ICIzODA6MS0zODA6NTUiLAogICAgICAgICAgICAibmFtZSIgOiAiVG9TdHJpbmciLAogICAgICAgICAgICAiY29udGV4dCIgOiAiUGF0aWVudCIsCiAgICAgICAgICAgICJhY2Nlc3NMZXZlbCIgOiAiUHVibGljIiwKICAgICAgICAgICAgInR5cGUiIDogIkZ1bmN0aW9uRGVmIiwKICAgICAgICAgICAgImV4cHJlc3Npb24iIDogewogICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMzgwOjQ1LTM4MDo1NSIsCiAgICAgICAgICAgICAgICJwYXRoIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICJ0eXBlIiA6ICJQcm9wZXJ0eSIsCiAgICAgICAgICAgICAgICJzb3VyY2UiIDogewogICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMzgwOjQ1LTM4MDo0OSIsCiAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJPcGVyYW5kUmVmIgogICAgICAgICAgICAgICB9CiAgICAgICAgICAgIH0sCiAgICAgICAgICAgICJvcGVyYW5kIiA6IFsgewogICAgICAgICAgICAgICAibmFtZSIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAib3BlcmFuZFR5cGVTcGVjaWZpZXIiIDogewogICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMzgwOjMyLTM4MDo0MSIsCiAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJ7aHR0cDovL2hsNy5vcmcvZmhpcn1UYXNrU3RhdHVzIiwKICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk5hbWVkVHlwZVNwZWNpZmllciIKICAgICAgICAgICAgICAgfQogICAgICAgICAgICB9IF0KICAgICAgICAgfSwgewogICAgICAgICAgICAibG9jYXRvciIgOiAiMzgxOjEtMzgxOjY3IiwKICAgICAgICAgICAgIm5hbWUiIDogIlRvU3RyaW5nIiwKICAgICAgICAgICAgImNvbnRleHQiIDogIlBhdGllbnQiLAogICAgICAgICAgICAiYWNjZXNzTGV2ZWwiIDogIlB1YmxpYyIsCiAgICAgICAgICAgICJ0eXBlIiA6ICJGdW5jdGlvbkRlZiIsCiAgICAgICAgICAgICJleHByZXNzaW9uIiA6IHsKICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjM4MTo1Ny0zODE6NjciLAogICAgICAgICAgICAgICAicGF0aCIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAidHlwZSIgOiAiUHJvcGVydHkiLAogICAgICAgICAgICAgICAic291cmNlIiA6IHsKICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjM4MTo1Ny0zODE6NjEiLAogICAgICAgICAgICAgICAgICAibmFtZSIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAgICAidHlwZSIgOiAiT3BlcmFuZFJlZiIKICAgICAgICAgICAgICAgfQogICAgICAgICAgICB9LAogICAgICAgICAgICAib3BlcmFuZCIgOiBbIHsKICAgICAgICAgICAgICAgIm5hbWUiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgIm9wZXJhbmRUeXBlU3BlY2lmaWVyIiA6IHsKICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjM4MTozMi0zODE6NTMiLAogICAgICAgICAgICAgICAgICAibmFtZSIgOiAie2h0dHA6Ly9obDcub3JnL2ZoaXJ9VGVzdFJlcG9ydEFjdGlvblJlc3VsdCIsCiAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJOYW1lZFR5cGVTcGVjaWZpZXIiCiAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgfSBdCiAgICAgICAgIH0sIHsKICAgICAgICAgICAgImxvY2F0b3IiIDogIjM4MjoxLTM4Mjo3MCIsCiAgICAgICAgICAgICJuYW1lIiA6ICJUb1N0cmluZyIsCiAgICAgICAgICAgICJjb250ZXh0IiA6ICJQYXRpZW50IiwKICAgICAgICAgICAgImFjY2Vzc0xldmVsIiA6ICJQdWJsaWMiLAogICAgICAgICAgICAidHlwZSIgOiAiRnVuY3Rpb25EZWYiLAogICAgICAgICAgICAiZXhwcmVzc2lvbiIgOiB7CiAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIzODI6NjAtMzgyOjcwIiwKICAgICAgICAgICAgICAgInBhdGgiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgInR5cGUiIDogIlByb3BlcnR5IiwKICAgICAgICAgICAgICAgInNvdXJjZSIgOiB7CiAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIzODI6NjAtMzgyOjY0IiwKICAgICAgICAgICAgICAgICAgIm5hbWUiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk9wZXJhbmRSZWYiCiAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgfSwKICAgICAgICAgICAgIm9wZXJhbmQiIDogWyB7CiAgICAgICAgICAgICAgICJuYW1lIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICJvcGVyYW5kVHlwZVNwZWNpZmllciIgOiB7CiAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIzODI6MzItMzgyOjU2IiwKICAgICAgICAgICAgICAgICAgIm5hbWUiIDogIntodHRwOi8vaGw3Lm9yZy9maGlyfVRlc3RSZXBvcnRQYXJ0aWNpcGFudFR5cGUiLAogICAgICAgICAgICAgICAgICAidHlwZSIgOiAiTmFtZWRUeXBlU3BlY2lmaWVyIgogICAgICAgICAgICAgICB9CiAgICAgICAgICAgIH0gXQogICAgICAgICB9LCB7CiAgICAgICAgICAgICJsb2NhdG9yIiA6ICIzODM6MS0zODM6NjEiLAogICAgICAgICAgICAibmFtZSIgOiAiVG9TdHJpbmciLAogICAgICAgICAgICAiY29udGV4dCIgOiAiUGF0aWVudCIsCiAgICAgICAgICAgICJhY2Nlc3NMZXZlbCIgOiAiUHVibGljIiwKICAgICAgICAgICAgInR5cGUiIDogIkZ1bmN0aW9uRGVmIiwKICAgICAgICAgICAgImV4cHJlc3Npb24iIDogewogICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMzgzOjUxLTM4Mzo2MSIsCiAgICAgICAgICAgICAgICJwYXRoIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICJ0eXBlIiA6ICJQcm9wZXJ0eSIsCiAgICAgICAgICAgICAgICJzb3VyY2UiIDogewogICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMzgzOjUxLTM4Mzo1NSIsCiAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJPcGVyYW5kUmVmIgogICAgICAgICAgICAgICB9CiAgICAgICAgICAgIH0sCiAgICAgICAgICAgICJvcGVyYW5kIiA6IFsgewogICAgICAgICAgICAgICAibmFtZSIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAib3BlcmFuZFR5cGVTcGVjaWZpZXIiIDogewogICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMzgzOjMyLTM4Mzo0NyIsCiAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJ7aHR0cDovL2hsNy5vcmcvZmhpcn1UZXN0UmVwb3J0UmVzdWx0IiwKICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk5hbWVkVHlwZVNwZWNpZmllciIKICAgICAgICAgICAgICAgfQogICAgICAgICAgICB9IF0KICAgICAgICAgfSwgewogICAgICAgICAgICAibG9jYXRvciIgOiAiMzg0OjEtMzg0OjYxIiwKICAgICAgICAgICAgIm5hbWUiIDogIlRvU3RyaW5nIiwKICAgICAgICAgICAgImNvbnRleHQiIDogIlBhdGllbnQiLAogICAgICAgICAgICAiYWNjZXNzTGV2ZWwiIDogIlB1YmxpYyIsCiAgICAgICAgICAgICJ0eXBlIiA6ICJGdW5jdGlvbkRlZiIsCiAgICAgICAgICAgICJleHByZXNzaW9uIiA6IHsKICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjM4NDo1MS0zODQ6NjEiLAogICAgICAgICAgICAgICAicGF0aCIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAidHlwZSIgOiAiUHJvcGVydHkiLAogICAgICAgICAgICAgICAic291cmNlIiA6IHsKICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjM4NDo1MS0zODQ6NTUiLAogICAgICAgICAgICAgICAgICAibmFtZSIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAgICAidHlwZSIgOiAiT3BlcmFuZFJlZiIKICAgICAgICAgICAgICAgfQogICAgICAgICAgICB9LAogICAgICAgICAgICAib3BlcmFuZCIgOiBbIHsKICAgICAgICAgICAgICAgIm5hbWUiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgIm9wZXJhbmRUeXBlU3BlY2lmaWVyIiA6IHsKICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjM4NDozMi0zODQ6NDciLAogICAgICAgICAgICAgICAgICAibmFtZSIgOiAie2h0dHA6Ly9obDcub3JnL2ZoaXJ9VGVzdFJlcG9ydFN0YXR1cyIsCiAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJOYW1lZFR5cGVTcGVjaWZpZXIiCiAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgfSBdCiAgICAgICAgIH0sIHsKICAgICAgICAgICAgImxvY2F0b3IiIDogIjM4NToxLTM4NTo3MiIsCiAgICAgICAgICAgICJuYW1lIiA6ICJUb1N0cmluZyIsCiAgICAgICAgICAgICJjb250ZXh0IiA6ICJQYXRpZW50IiwKICAgICAgICAgICAgImFjY2Vzc0xldmVsIiA6ICJQdWJsaWMiLAogICAgICAgICAgICAidHlwZSIgOiAiRnVuY3Rpb25EZWYiLAogICAgICAgICAgICAiZXhwcmVzc2lvbiIgOiB7CiAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIzODU6NjItMzg1OjcyIiwKICAgICAgICAgICAgICAgInBhdGgiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgInR5cGUiIDogIlByb3BlcnR5IiwKICAgICAgICAgICAgICAgInNvdXJjZSIgOiB7CiAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIzODU6NjItMzg1OjY2IiwKICAgICAgICAgICAgICAgICAgIm5hbWUiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk9wZXJhbmRSZWYiCiAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgfSwKICAgICAgICAgICAgIm9wZXJhbmQiIDogWyB7CiAgICAgICAgICAgICAgICJuYW1lIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICJvcGVyYW5kVHlwZVNwZWNpZmllciIgOiB7CiAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIzODU6MzItMzg1OjU4IiwKICAgICAgICAgICAgICAgICAgIm5hbWUiIDogIntodHRwOi8vaGw3Lm9yZy9maGlyfVRlc3RTY3JpcHRSZXF1ZXN0TWV0aG9kQ29kZSIsCiAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJOYW1lZFR5cGVTcGVjaWZpZXIiCiAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgfSBdCiAgICAgICAgIH0sIHsKICAgICAgICAgICAgImxvY2F0b3IiIDogIjM4NjoxLTM4Njo1NiIsCiAgICAgICAgICAgICJuYW1lIiA6ICJUb1N0cmluZyIsCiAgICAgICAgICAgICJjb250ZXh0IiA6ICJQYXRpZW50IiwKICAgICAgICAgICAgImFjY2Vzc0xldmVsIiA6ICJQdWJsaWMiLAogICAgICAgICAgICAidHlwZSIgOiAiRnVuY3Rpb25EZWYiLAogICAgICAgICAgICAiZXhwcmVzc2lvbiIgOiB7CiAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIzODY6NDYtMzg2OjU2IiwKICAgICAgICAgICAgICAgInBhdGgiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgInR5cGUiIDogIlByb3BlcnR5IiwKICAgICAgICAgICAgICAgInNvdXJjZSIgOiB7CiAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIzODY6NDYtMzg2OjUwIiwKICAgICAgICAgICAgICAgICAgIm5hbWUiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk9wZXJhbmRSZWYiCiAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgfSwKICAgICAgICAgICAgIm9wZXJhbmQiIDogWyB7CiAgICAgICAgICAgICAgICJuYW1lIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICJvcGVyYW5kVHlwZVNwZWNpZmllciIgOiB7CiAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIzODY6MzItMzg2OjQyIiwKICAgICAgICAgICAgICAgICAgIm5hbWUiIDogIntodHRwOi8vaGw3Lm9yZy9maGlyfVRyaWdnZXJUeXBlIiwKICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk5hbWVkVHlwZVNwZWNpZmllciIKICAgICAgICAgICAgICAgfQogICAgICAgICAgICB9IF0KICAgICAgICAgfSwgewogICAgICAgICAgICAibG9jYXRvciIgOiAiMzg3OjEtMzg3OjYzIiwKICAgICAgICAgICAgIm5hbWUiIDogIlRvU3RyaW5nIiwKICAgICAgICAgICAgImNvbnRleHQiIDogIlBhdGllbnQiLAogICAgICAgICAgICAiYWNjZXNzTGV2ZWwiIDogIlB1YmxpYyIsCiAgICAgICAgICAgICJ0eXBlIiA6ICJGdW5jdGlvbkRlZiIsCiAgICAgICAgICAgICJleHByZXNzaW9uIiA6IHsKICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjM4Nzo1My0zODc6NjMiLAogICAgICAgICAgICAgICAicGF0aCIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAidHlwZSIgOiAiUHJvcGVydHkiLAogICAgICAgICAgICAgICAic291cmNlIiA6IHsKICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjM4Nzo1My0zODc6NTciLAogICAgICAgICAgICAgICAgICAibmFtZSIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAgICAidHlwZSIgOiAiT3BlcmFuZFJlZiIKICAgICAgICAgICAgICAgfQogICAgICAgICAgICB9LAogICAgICAgICAgICAib3BlcmFuZCIgOiBbIHsKICAgICAgICAgICAgICAgIm5hbWUiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgIm9wZXJhbmRUeXBlU3BlY2lmaWVyIiA6IHsKICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjM4NzozMi0zODc6NDkiLAogICAgICAgICAgICAgICAgICAibmFtZSIgOiAie2h0dHA6Ly9obDcub3JnL2ZoaXJ9VHlwZURlcml2YXRpb25SdWxlIiwKICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk5hbWVkVHlwZVNwZWNpZmllciIKICAgICAgICAgICAgICAgfQogICAgICAgICAgICB9IF0KICAgICAgICAgfSwgewogICAgICAgICAgICAibG9jYXRvciIgOiAiMzg4OjEtMzg4OjY3IiwKICAgICAgICAgICAgIm5hbWUiIDogIlRvU3RyaW5nIiwKICAgICAgICAgICAgImNvbnRleHQiIDogIlBhdGllbnQiLAogICAgICAgICAgICAiYWNjZXNzTGV2ZWwiIDogIlB1YmxpYyIsCiAgICAgICAgICAgICJ0eXBlIiA6ICJGdW5jdGlvbkRlZiIsCiAgICAgICAgICAgICJleHByZXNzaW9uIiA6IHsKICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjM4ODo1Ny0zODg6NjciLAogICAgICAgICAgICAgICAicGF0aCIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAidHlwZSIgOiAiUHJvcGVydHkiLAogICAgICAgICAgICAgICAic291cmNlIiA6IHsKICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjM4ODo1Ny0zODg6NjEiLAogICAgICAgICAgICAgICAgICAibmFtZSIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAgICAidHlwZSIgOiAiT3BlcmFuZFJlZiIKICAgICAgICAgICAgICAgfQogICAgICAgICAgICB9LAogICAgICAgICAgICAib3BlcmFuZCIgOiBbIHsKICAgICAgICAgICAgICAgIm5hbWUiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgIm9wZXJhbmRUeXBlU3BlY2lmaWVyIiA6IHsKICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjM4ODozMi0zODg6NTMiLAogICAgICAgICAgICAgICAgICAibmFtZSIgOiAie2h0dHA6Ly9obDcub3JnL2ZoaXJ9VHlwZVJlc3RmdWxJbnRlcmFjdGlvbiIsCiAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJOYW1lZFR5cGVTcGVjaWZpZXIiCiAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgfSBdCiAgICAgICAgIH0sIHsKICAgICAgICAgICAgImxvY2F0b3IiIDogIjM4OToxLTM4OTo1NyIsCiAgICAgICAgICAgICJuYW1lIiA6ICJUb1N0cmluZyIsCiAgICAgICAgICAgICJjb250ZXh0IiA6ICJQYXRpZW50IiwKICAgICAgICAgICAgImFjY2Vzc0xldmVsIiA6ICJQdWJsaWMiLAogICAgICAgICAgICAidHlwZSIgOiAiRnVuY3Rpb25EZWYiLAogICAgICAgICAgICAiZXhwcmVzc2lvbiIgOiB7CiAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIzODk6NDctMzg5OjU3IiwKICAgICAgICAgICAgICAgInBhdGgiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgInR5cGUiIDogIlByb3BlcnR5IiwKICAgICAgICAgICAgICAgInNvdXJjZSIgOiB7CiAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIzODk6NDctMzg5OjUxIiwKICAgICAgICAgICAgICAgICAgIm5hbWUiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk9wZXJhbmRSZWYiCiAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgfSwKICAgICAgICAgICAgIm9wZXJhbmQiIDogWyB7CiAgICAgICAgICAgICAgICJuYW1lIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICJvcGVyYW5kVHlwZVNwZWNpZmllciIgOiB7CiAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIzODk6MzItMzg5OjQzIiwKICAgICAgICAgICAgICAgICAgIm5hbWUiIDogIntodHRwOi8vaGw3Lm9yZy9maGlyfVVESUVudHJ5VHlwZSIsCiAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJOYW1lZFR5cGVTcGVjaWZpZXIiCiAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgfSBdCiAgICAgICAgIH0sIHsKICAgICAgICAgICAgImxvY2F0b3IiIDogIjM5MDoxLTM5MDo1NiIsCiAgICAgICAgICAgICJuYW1lIiA6ICJUb1N0cmluZyIsCiAgICAgICAgICAgICJjb250ZXh0IiA6ICJQYXRpZW50IiwKICAgICAgICAgICAgImFjY2Vzc0xldmVsIiA6ICJQdWJsaWMiLAogICAgICAgICAgICAidHlwZSIgOiAiRnVuY3Rpb25EZWYiLAogICAgICAgICAgICAiZXhwcmVzc2lvbiIgOiB7CiAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIzOTA6NDYtMzkwOjU2IiwKICAgICAgICAgICAgICAgInBhdGgiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgInR5cGUiIDogIlByb3BlcnR5IiwKICAgICAgICAgICAgICAgInNvdXJjZSIgOiB7CiAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIzOTA6NDYtMzkwOjUwIiwKICAgICAgICAgICAgICAgICAgIm5hbWUiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk9wZXJhbmRSZWYiCiAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgfSwKICAgICAgICAgICAgIm9wZXJhbmQiIDogWyB7CiAgICAgICAgICAgICAgICJuYW1lIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICJvcGVyYW5kVHlwZVNwZWNpZmllciIgOiB7CiAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIzOTA6MzItMzkwOjQyIiwKICAgICAgICAgICAgICAgICAgIm5hbWUiIDogIntodHRwOi8vaGw3Lm9yZy9maGlyfVVuaXRzT2ZUaW1lIiwKICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk5hbWVkVHlwZVNwZWNpZmllciIKICAgICAgICAgICAgICAgfQogICAgICAgICAgICB9IF0KICAgICAgICAgfSwgewogICAgICAgICAgICAibG9jYXRvciIgOiAiMzkxOjEtMzkxOjQ4IiwKICAgICAgICAgICAgIm5hbWUiIDogIlRvU3RyaW5nIiwKICAgICAgICAgICAgImNvbnRleHQiIDogIlBhdGllbnQiLAogICAgICAgICAgICAiYWNjZXNzTGV2ZWwiIDogIlB1YmxpYyIsCiAgICAgICAgICAgICJ0eXBlIiA6ICJGdW5jdGlvbkRlZiIsCiAgICAgICAgICAgICJleHByZXNzaW9uIiA6IHsKICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjM5MTozOC0zOTE6NDgiLAogICAgICAgICAgICAgICAicGF0aCIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAidHlwZSIgOiAiUHJvcGVydHkiLAogICAgICAgICAgICAgICAic291cmNlIiA6IHsKICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjM5MTozOC0zOTE6NDIiLAogICAgICAgICAgICAgICAgICAibmFtZSIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAgICAidHlwZSIgOiAiT3BlcmFuZFJlZiIKICAgICAgICAgICAgICAgfQogICAgICAgICAgICB9LAogICAgICAgICAgICAib3BlcmFuZCIgOiBbIHsKICAgICAgICAgICAgICAgIm5hbWUiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgIm9wZXJhbmRUeXBlU3BlY2lmaWVyIiA6IHsKICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjM5MTozMi0zOTE6MzQiLAogICAgICAgICAgICAgICAgICAibmFtZSIgOiAie2h0dHA6Ly9obDcub3JnL2ZoaXJ9VXNlIiwKICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk5hbWVkVHlwZVNwZWNpZmllciIKICAgICAgICAgICAgICAgfQogICAgICAgICAgICB9IF0KICAgICAgICAgfSwgewogICAgICAgICAgICAibG9jYXRvciIgOiAiMzkyOjEtMzkyOjU3IiwKICAgICAgICAgICAgIm5hbWUiIDogIlRvU3RyaW5nIiwKICAgICAgICAgICAgImNvbnRleHQiIDogIlBhdGllbnQiLAogICAgICAgICAgICAiYWNjZXNzTGV2ZWwiIDogIlB1YmxpYyIsCiAgICAgICAgICAgICJ0eXBlIiA6ICJGdW5jdGlvbkRlZiIsCiAgICAgICAgICAgICJleHByZXNzaW9uIiA6IHsKICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjM5Mjo0Ny0zOTI6NTciLAogICAgICAgICAgICAgICAicGF0aCIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAidHlwZSIgOiAiUHJvcGVydHkiLAogICAgICAgICAgICAgICAic291cmNlIiA6IHsKICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjM5Mjo0Ny0zOTI6NTEiLAogICAgICAgICAgICAgICAgICAibmFtZSIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAgICAidHlwZSIgOiAiT3BlcmFuZFJlZiIKICAgICAgICAgICAgICAgfQogICAgICAgICAgICB9LAogICAgICAgICAgICAib3BlcmFuZCIgOiBbIHsKICAgICAgICAgICAgICAgIm5hbWUiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgIm9wZXJhbmRUeXBlU3BlY2lmaWVyIiA6IHsKICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjM5MjozMi0zOTI6NDMiLAogICAgICAgICAgICAgICAgICAibmFtZSIgOiAie2h0dHA6Ly9obDcub3JnL2ZoaXJ9VmFyaWFibGVUeXBlIiwKICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk5hbWVkVHlwZVNwZWNpZmllciIKICAgICAgICAgICAgICAgfQogICAgICAgICAgICB9IF0KICAgICAgICAgfSwgewogICAgICAgICAgICAibG9jYXRvciIgOiAiMzkzOjEtMzkzOjU1IiwKICAgICAgICAgICAgIm5hbWUiIDogIlRvU3RyaW5nIiwKICAgICAgICAgICAgImNvbnRleHQiIDogIlBhdGllbnQiLAogICAgICAgICAgICAiYWNjZXNzTGV2ZWwiIDogIlB1YmxpYyIsCiAgICAgICAgICAgICJ0eXBlIiA6ICJGdW5jdGlvbkRlZiIsCiAgICAgICAgICAgICJleHByZXNzaW9uIiA6IHsKICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjM5Mzo0NS0zOTM6NTUiLAogICAgICAgICAgICAgICAicGF0aCIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAidHlwZSIgOiAiUHJvcGVydHkiLAogICAgICAgICAgICAgICAic291cmNlIiA6IHsKICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjM5Mzo0NS0zOTM6NDkiLAogICAgICAgICAgICAgICAgICAibmFtZSIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAgICAidHlwZSIgOiAiT3BlcmFuZFJlZiIKICAgICAgICAgICAgICAgfQogICAgICAgICAgICB9LAogICAgICAgICAgICAib3BlcmFuZCIgOiBbIHsKICAgICAgICAgICAgICAgIm5hbWUiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgIm9wZXJhbmRUeXBlU3BlY2lmaWVyIiA6IHsKICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjM5MzozMi0zOTM6NDEiLAogICAgICAgICAgICAgICAgICAibmFtZSIgOiAie2h0dHA6Ly9obDcub3JnL2ZoaXJ9VmlzaW9uQmFzZSIsCiAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJOYW1lZFR5cGVTcGVjaWZpZXIiCiAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgfSBdCiAgICAgICAgIH0sIHsKICAgICAgICAgICAgImxvY2F0b3IiIDogIjM5NDoxLTM5NDo1NSIsCiAgICAgICAgICAgICJuYW1lIiA6ICJUb1N0cmluZyIsCiAgICAgICAgICAgICJjb250ZXh0IiA6ICJQYXRpZW50IiwKICAgICAgICAgICAgImFjY2Vzc0xldmVsIiA6ICJQdWJsaWMiLAogICAgICAgICAgICAidHlwZSIgOiAiRnVuY3Rpb25EZWYiLAogICAgICAgICAgICAiZXhwcmVzc2lvbiIgOiB7CiAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIzOTQ6NDUtMzk0OjU1IiwKICAgICAgICAgICAgICAgInBhdGgiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgInR5cGUiIDogIlByb3BlcnR5IiwKICAgICAgICAgICAgICAgInNvdXJjZSIgOiB7CiAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIzOTQ6NDUtMzk0OjQ5IiwKICAgICAgICAgICAgICAgICAgIm5hbWUiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk9wZXJhbmRSZWYiCiAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgfSwKICAgICAgICAgICAgIm9wZXJhbmQiIDogWyB7CiAgICAgICAgICAgICAgICJuYW1lIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICJvcGVyYW5kVHlwZVNwZWNpZmllciIgOiB7CiAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIzOTQ6MzItMzk0OjQxIiwKICAgICAgICAgICAgICAgICAgIm5hbWUiIDogIntodHRwOi8vaGw3Lm9yZy9maGlyfVZpc2lvbkV5ZXMiLAogICAgICAgICAgICAgICAgICAidHlwZSIgOiAiTmFtZWRUeXBlU3BlY2lmaWVyIgogICAgICAgICAgICAgICB9CiAgICAgICAgICAgIH0gXQogICAgICAgICB9LCB7CiAgICAgICAgICAgICJsb2NhdG9yIiA6ICIzOTU6MS0zOTU6NTciLAogICAgICAgICAgICAibmFtZSIgOiAiVG9TdHJpbmciLAogICAgICAgICAgICAiY29udGV4dCIgOiAiUGF0aWVudCIsCiAgICAgICAgICAgICJhY2Nlc3NMZXZlbCIgOiAiUHVibGljIiwKICAgICAgICAgICAgInR5cGUiIDogIkZ1bmN0aW9uRGVmIiwKICAgICAgICAgICAgImV4cHJlc3Npb24iIDogewogICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMzk1OjQ3LTM5NTo1NyIsCiAgICAgICAgICAgICAgICJwYXRoIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICJ0eXBlIiA6ICJQcm9wZXJ0eSIsCiAgICAgICAgICAgICAgICJzb3VyY2UiIDogewogICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMzk1OjQ3LTM5NTo1MSIsCiAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJPcGVyYW5kUmVmIgogICAgICAgICAgICAgICB9CiAgICAgICAgICAgIH0sCiAgICAgICAgICAgICJvcGVyYW5kIiA6IFsgewogICAgICAgICAgICAgICAibmFtZSIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAib3BlcmFuZFR5cGVTcGVjaWZpZXIiIDogewogICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMzk1OjMyLTM5NTo0MyIsCiAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJ7aHR0cDovL2hsNy5vcmcvZmhpcn1WaXNpb25TdGF0dXMiLAogICAgICAgICAgICAgICAgICAidHlwZSIgOiAiTmFtZWRUeXBlU3BlY2lmaWVyIgogICAgICAgICAgICAgICB9CiAgICAgICAgICAgIH0gXQogICAgICAgICB9LCB7CiAgICAgICAgICAgICJsb2NhdG9yIiA6ICIzOTY6MS0zOTY6NTkiLAogICAgICAgICAgICAibmFtZSIgOiAiVG9TdHJpbmciLAogICAgICAgICAgICAiY29udGV4dCIgOiAiUGF0aWVudCIsCiAgICAgICAgICAgICJhY2Nlc3NMZXZlbCIgOiAiUHVibGljIiwKICAgICAgICAgICAgInR5cGUiIDogIkZ1bmN0aW9uRGVmIiwKICAgICAgICAgICAgImV4cHJlc3Npb24iIDogewogICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMzk2OjQ5LTM5Njo1OSIsCiAgICAgICAgICAgICAgICJwYXRoIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICJ0eXBlIiA6ICJQcm9wZXJ0eSIsCiAgICAgICAgICAgICAgICJzb3VyY2UiIDogewogICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMzk2OjQ5LTM5Njo1MyIsCiAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJPcGVyYW5kUmVmIgogICAgICAgICAgICAgICB9CiAgICAgICAgICAgIH0sCiAgICAgICAgICAgICJvcGVyYW5kIiA6IFsgewogICAgICAgICAgICAgICAibmFtZSIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAib3BlcmFuZFR5cGVTcGVjaWZpZXIiIDogewogICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMzk2OjMyLTM5Njo0NSIsCiAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJ7aHR0cDovL2hsNy5vcmcvZmhpcn1YUGF0aFVzYWdlVHlwZSIsCiAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJOYW1lZFR5cGVTcGVjaWZpZXIiCiAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgfSBdCiAgICAgICAgIH0sIHsKICAgICAgICAgICAgImxvY2F0b3IiIDogIjM5NzoxLTM5Nzo1NyIsCiAgICAgICAgICAgICJuYW1lIiA6ICJUb1N0cmluZyIsCiAgICAgICAgICAgICJjb250ZXh0IiA6ICJQYXRpZW50IiwKICAgICAgICAgICAgImFjY2Vzc0xldmVsIiA6ICJQdWJsaWMiLAogICAgICAgICAgICAidHlwZSIgOiAiRnVuY3Rpb25EZWYiLAogICAgICAgICAgICAiZXhwcmVzc2lvbiIgOiB7CiAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIzOTc6NDctMzk3OjU3IiwKICAgICAgICAgICAgICAgInBhdGgiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgInR5cGUiIDogIlByb3BlcnR5IiwKICAgICAgICAgICAgICAgInNvdXJjZSIgOiB7CiAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIzOTc6NDctMzk3OjUxIiwKICAgICAgICAgICAgICAgICAgIm5hbWUiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk9wZXJhbmRSZWYiCiAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgfSwKICAgICAgICAgICAgIm9wZXJhbmQiIDogWyB7CiAgICAgICAgICAgICAgICJuYW1lIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICJvcGVyYW5kVHlwZVNwZWNpZmllciIgOiB7CiAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIzOTc6MzItMzk3OjQzIiwKICAgICAgICAgICAgICAgICAgIm5hbWUiIDogIntodHRwOi8vaGw3Lm9yZy9maGlyfWJhc2U2NEJpbmFyeSIsCiAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJOYW1lZFR5cGVTcGVjaWZpZXIiCiAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgfSBdCiAgICAgICAgIH0sIHsKICAgICAgICAgICAgImxvY2F0b3IiIDogIjM5ODoxLTM5ODo1MyIsCiAgICAgICAgICAgICJuYW1lIiA6ICJUb0Jvb2xlYW4iLAogICAgICAgICAgICAiY29udGV4dCIgOiAiUGF0aWVudCIsCiAgICAgICAgICAgICJhY2Nlc3NMZXZlbCIgOiAiUHVibGljIiwKICAgICAgICAgICAgInR5cGUiIDogIkZ1bmN0aW9uRGVmIiwKICAgICAgICAgICAgImV4cHJlc3Npb24iIDogewogICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMzk4OjQzLTM5ODo1MyIsCiAgICAgICAgICAgICAgICJwYXRoIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICJ0eXBlIiA6ICJQcm9wZXJ0eSIsCiAgICAgICAgICAgICAgICJzb3VyY2UiIDogewogICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMzk4OjQzLTM5ODo0NyIsCiAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJPcGVyYW5kUmVmIgogICAgICAgICAgICAgICB9CiAgICAgICAgICAgIH0sCiAgICAgICAgICAgICJvcGVyYW5kIiA6IFsgewogICAgICAgICAgICAgICAibmFtZSIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAib3BlcmFuZFR5cGVTcGVjaWZpZXIiIDogewogICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMzk4OjMzLTM5ODozOSIsCiAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJ7aHR0cDovL2hsNy5vcmcvZmhpcn1ib29sZWFuIiwKICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk5hbWVkVHlwZVNwZWNpZmllciIKICAgICAgICAgICAgICAgfQogICAgICAgICAgICB9IF0KICAgICAgICAgfSwgewogICAgICAgICAgICAibG9jYXRvciIgOiAiMzk5OjEtMzk5OjQ3IiwKICAgICAgICAgICAgIm5hbWUiIDogIlRvRGF0ZSIsCiAgICAgICAgICAgICJjb250ZXh0IiA6ICJQYXRpZW50IiwKICAgICAgICAgICAgImFjY2Vzc0xldmVsIiA6ICJQdWJsaWMiLAogICAgICAgICAgICAidHlwZSIgOiAiRnVuY3Rpb25EZWYiLAogICAgICAgICAgICAiZXhwcmVzc2lvbiIgOiB7CiAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIzOTk6MzctMzk5OjQ3IiwKICAgICAgICAgICAgICAgInBhdGgiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgInR5cGUiIDogIlByb3BlcnR5IiwKICAgICAgICAgICAgICAgInNvdXJjZSIgOiB7CiAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIzOTk6MzctMzk5OjQxIiwKICAgICAgICAgICAgICAgICAgIm5hbWUiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk9wZXJhbmRSZWYiCiAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgfSwKICAgICAgICAgICAgIm9wZXJhbmQiIDogWyB7CiAgICAgICAgICAgICAgICJuYW1lIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICJvcGVyYW5kVHlwZVNwZWNpZmllciIgOiB7CiAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIzOTk6MzAtMzk5OjMzIiwKICAgICAgICAgICAgICAgICAgIm5hbWUiIDogIntodHRwOi8vaGw3Lm9yZy9maGlyfWRhdGUiLAogICAgICAgICAgICAgICAgICAidHlwZSIgOiAiTmFtZWRUeXBlU3BlY2lmaWVyIgogICAgICAgICAgICAgICB9CiAgICAgICAgICAgIH0gXQogICAgICAgICB9LCB7CiAgICAgICAgICAgICJsb2NhdG9yIiA6ICI0MDA6MS00MDA6NTUiLAogICAgICAgICAgICAibmFtZSIgOiAiVG9EYXRlVGltZSIsCiAgICAgICAgICAgICJjb250ZXh0IiA6ICJQYXRpZW50IiwKICAgICAgICAgICAgImFjY2Vzc0xldmVsIiA6ICJQdWJsaWMiLAogICAgICAgICAgICAidHlwZSIgOiAiRnVuY3Rpb25EZWYiLAogICAgICAgICAgICAiZXhwcmVzc2lvbiIgOiB7CiAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICI0MDA6NDUtNDAwOjU1IiwKICAgICAgICAgICAgICAgInBhdGgiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgInR5cGUiIDogIlByb3BlcnR5IiwKICAgICAgICAgICAgICAgInNvdXJjZSIgOiB7CiAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICI0MDA6NDUtNDAwOjQ5IiwKICAgICAgICAgICAgICAgICAgIm5hbWUiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk9wZXJhbmRSZWYiCiAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgfSwKICAgICAgICAgICAgIm9wZXJhbmQiIDogWyB7CiAgICAgICAgICAgICAgICJuYW1lIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICJvcGVyYW5kVHlwZVNwZWNpZmllciIgOiB7CiAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICI0MDA6MzQtNDAwOjQxIiwKICAgICAgICAgICAgICAgICAgIm5hbWUiIDogIntodHRwOi8vaGw3Lm9yZy9maGlyfWRhdGVUaW1lIiwKICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk5hbWVkVHlwZVNwZWNpZmllciIKICAgICAgICAgICAgICAgfQogICAgICAgICAgICB9IF0KICAgICAgICAgfSwgewogICAgICAgICAgICAibG9jYXRvciIgOiAiNDAxOjEtNDAxOjUzIiwKICAgICAgICAgICAgIm5hbWUiIDogIlRvRGVjaW1hbCIsCiAgICAgICAgICAgICJjb250ZXh0IiA6ICJQYXRpZW50IiwKICAgICAgICAgICAgImFjY2Vzc0xldmVsIiA6ICJQdWJsaWMiLAogICAgICAgICAgICAidHlwZSIgOiAiRnVuY3Rpb25EZWYiLAogICAgICAgICAgICAiZXhwcmVzc2lvbiIgOiB7CiAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICI0MDE6NDMtNDAxOjUzIiwKICAgICAgICAgICAgICAgInBhdGgiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgInR5cGUiIDogIlByb3BlcnR5IiwKICAgICAgICAgICAgICAgInNvdXJjZSIgOiB7CiAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICI0MDE6NDMtNDAxOjQ3IiwKICAgICAgICAgICAgICAgICAgIm5hbWUiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk9wZXJhbmRSZWYiCiAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgfSwKICAgICAgICAgICAgIm9wZXJhbmQiIDogWyB7CiAgICAgICAgICAgICAgICJuYW1lIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICJvcGVyYW5kVHlwZVNwZWNpZmllciIgOiB7CiAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICI0MDE6MzMtNDAxOjM5IiwKICAgICAgICAgICAgICAgICAgIm5hbWUiIDogIntodHRwOi8vaGw3Lm9yZy9maGlyfWRlY2ltYWwiLAogICAgICAgICAgICAgICAgICAidHlwZSIgOiAiTmFtZWRUeXBlU3BlY2lmaWVyIgogICAgICAgICAgICAgICB9CiAgICAgICAgICAgIH0gXQogICAgICAgICB9LCB7CiAgICAgICAgICAgICJsb2NhdG9yIiA6ICI0MDI6MS00MDI6NTQiLAogICAgICAgICAgICAibmFtZSIgOiAiVG9EYXRlVGltZSIsCiAgICAgICAgICAgICJjb250ZXh0IiA6ICJQYXRpZW50IiwKICAgICAgICAgICAgImFjY2Vzc0xldmVsIiA6ICJQdWJsaWMiLAogICAgICAgICAgICAidHlwZSIgOiAiRnVuY3Rpb25EZWYiLAogICAgICAgICAgICAiZXhwcmVzc2lvbiIgOiB7CiAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICI0MDI6NDQtNDAyOjU0IiwKICAgICAgICAgICAgICAgInBhdGgiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgInR5cGUiIDogIlByb3BlcnR5IiwKICAgICAgICAgICAgICAgInNvdXJjZSIgOiB7CiAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICI0MDI6NDQtNDAyOjQ4IiwKICAgICAgICAgICAgICAgICAgIm5hbWUiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk9wZXJhbmRSZWYiCiAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgfSwKICAgICAgICAgICAgIm9wZXJhbmQiIDogWyB7CiAgICAgICAgICAgICAgICJuYW1lIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICJvcGVyYW5kVHlwZVNwZWNpZmllciIgOiB7CiAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICI0MDI6MzQtNDAyOjQwIiwKICAgICAgICAgICAgICAgICAgIm5hbWUiIDogIntodHRwOi8vaGw3Lm9yZy9maGlyfWluc3RhbnQiLAogICAgICAgICAgICAgICAgICAidHlwZSIgOiAiTmFtZWRUeXBlU3BlY2lmaWVyIgogICAgICAgICAgICAgICB9CiAgICAgICAgICAgIH0gXQogICAgICAgICB9LCB7CiAgICAgICAgICAgICJsb2NhdG9yIiA6ICI0MDM6MS00MDM6NTMiLAogICAgICAgICAgICAibmFtZSIgOiAiVG9JbnRlZ2VyIiwKICAgICAgICAgICAgImNvbnRleHQiIDogIlBhdGllbnQiLAogICAgICAgICAgICAiYWNjZXNzTGV2ZWwiIDogIlB1YmxpYyIsCiAgICAgICAgICAgICJ0eXBlIiA6ICJGdW5jdGlvbkRlZiIsCiAgICAgICAgICAgICJleHByZXNzaW9uIiA6IHsKICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjQwMzo0My00MDM6NTMiLAogICAgICAgICAgICAgICAicGF0aCIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAidHlwZSIgOiAiUHJvcGVydHkiLAogICAgICAgICAgICAgICAic291cmNlIiA6IHsKICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjQwMzo0My00MDM6NDciLAogICAgICAgICAgICAgICAgICAibmFtZSIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAgICAidHlwZSIgOiAiT3BlcmFuZFJlZiIKICAgICAgICAgICAgICAgfQogICAgICAgICAgICB9LAogICAgICAgICAgICAib3BlcmFuZCIgOiBbIHsKICAgICAgICAgICAgICAgIm5hbWUiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgIm9wZXJhbmRUeXBlU3BlY2lmaWVyIiA6IHsKICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjQwMzozMy00MDM6MzkiLAogICAgICAgICAgICAgICAgICAibmFtZSIgOiAie2h0dHA6Ly9obDcub3JnL2ZoaXJ9aW50ZWdlciIsCiAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJOYW1lZFR5cGVTcGVjaWZpZXIiCiAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgfSBdCiAgICAgICAgIH0sIHsKICAgICAgICAgICAgImxvY2F0b3IiIDogIjQwNDoxLTQwNDo1MSIsCiAgICAgICAgICAgICJuYW1lIiA6ICJUb1N0cmluZyIsCiAgICAgICAgICAgICJjb250ZXh0IiA6ICJQYXRpZW50IiwKICAgICAgICAgICAgImFjY2Vzc0xldmVsIiA6ICJQdWJsaWMiLAogICAgICAgICAgICAidHlwZSIgOiAiRnVuY3Rpb25EZWYiLAogICAgICAgICAgICAiZXhwcmVzc2lvbiIgOiB7CiAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICI0MDQ6NDEtNDA0OjUxIiwKICAgICAgICAgICAgICAgInBhdGgiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgInR5cGUiIDogIlByb3BlcnR5IiwKICAgICAgICAgICAgICAgInNvdXJjZSIgOiB7CiAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICI0MDQ6NDEtNDA0OjQ1IiwKICAgICAgICAgICAgICAgICAgIm5hbWUiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk9wZXJhbmRSZWYiCiAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgfSwKICAgICAgICAgICAgIm9wZXJhbmQiIDogWyB7CiAgICAgICAgICAgICAgICJuYW1lIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICJvcGVyYW5kVHlwZVNwZWNpZmllciIgOiB7CiAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICI0MDQ6MzItNDA0OjM3IiwKICAgICAgICAgICAgICAgICAgIm5hbWUiIDogIntodHRwOi8vaGw3Lm9yZy9maGlyfXN0cmluZyIsCiAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJOYW1lZFR5cGVTcGVjaWZpZXIiCiAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgfSBdCiAgICAgICAgIH0sIHsKICAgICAgICAgICAgImxvY2F0b3IiIDogIjQwNToxLTQwNTo0NyIsCiAgICAgICAgICAgICJuYW1lIiA6ICJUb1RpbWUiLAogICAgICAgICAgICAiY29udGV4dCIgOiAiUGF0aWVudCIsCiAgICAgICAgICAgICJhY2Nlc3NMZXZlbCIgOiAiUHVibGljIiwKICAgICAgICAgICAgInR5cGUiIDogIkZ1bmN0aW9uRGVmIiwKICAgICAgICAgICAgImV4cHJlc3Npb24iIDogewogICAgICAgICAgICAgICAibG9jYXRvciIgOiAiNDA1OjM3LTQwNTo0NyIsCiAgICAgICAgICAgICAgICJwYXRoIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICJ0eXBlIiA6ICJQcm9wZXJ0eSIsCiAgICAgICAgICAgICAgICJzb3VyY2UiIDogewogICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiNDA1OjM3LTQwNTo0MSIsCiAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJPcGVyYW5kUmVmIgogICAgICAgICAgICAgICB9CiAgICAgICAgICAgIH0sCiAgICAgICAgICAgICJvcGVyYW5kIiA6IFsgewogICAgICAgICAgICAgICAibmFtZSIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAib3BlcmFuZFR5cGVTcGVjaWZpZXIiIDogewogICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiNDA1OjMwLTQwNTozMyIsCiAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJ7aHR0cDovL2hsNy5vcmcvZmhpcn10aW1lIiwKICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk5hbWVkVHlwZVNwZWNpZmllciIKICAgICAgICAgICAgICAgfQogICAgICAgICAgICB9IF0KICAgICAgICAgfSwgewogICAgICAgICAgICAibG9jYXRvciIgOiAiNDA2OjEtNDA2OjQ4IiwKICAgICAgICAgICAgIm5hbWUiIDogIlRvU3RyaW5nIiwKICAgICAgICAgICAgImNvbnRleHQiIDogIlBhdGllbnQiLAogICAgICAgICAgICAiYWNjZXNzTGV2ZWwiIDogIlB1YmxpYyIsCiAgICAgICAgICAgICJ0eXBlIiA6ICJGdW5jdGlvbkRlZiIsCiAgICAgICAgICAgICJleHByZXNzaW9uIiA6IHsKICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjQwNjozOC00MDY6NDgiLAogICAgICAgICAgICAgICAicGF0aCIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAidHlwZSIgOiAiUHJvcGVydHkiLAogICAgICAgICAgICAgICAic291cmNlIiA6IHsKICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjQwNjozOC00MDY6NDIiLAogICAgICAgICAgICAgICAgICAibmFtZSIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAgICAidHlwZSIgOiAiT3BlcmFuZFJlZiIKICAgICAgICAgICAgICAgfQogICAgICAgICAgICB9LAogICAgICAgICAgICAib3BlcmFuZCIgOiBbIHsKICAgICAgICAgICAgICAgIm5hbWUiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgIm9wZXJhbmRUeXBlU3BlY2lmaWVyIiA6IHsKICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjQwNjozMi00MDY6MzQiLAogICAgICAgICAgICAgICAgICAibmFtZSIgOiAie2h0dHA6Ly9obDcub3JnL2ZoaXJ9dXJpIiwKICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk5hbWVkVHlwZVNwZWNpZmllciIKICAgICAgICAgICAgICAgfQogICAgICAgICAgICB9IF0KICAgICAgICAgfSwgewogICAgICAgICAgICAibG9jYXRvciIgOiAiNDA3OjEtNDA3OjUwIiwKICAgICAgICAgICAgIm5hbWUiIDogIlRvU3RyaW5nIiwKICAgICAgICAgICAgImNvbnRleHQiIDogIlBhdGllbnQiLAogICAgICAgICAgICAiYWNjZXNzTGV2ZWwiIDogIlB1YmxpYyIsCiAgICAgICAgICAgICJ0eXBlIiA6ICJGdW5jdGlvbkRlZiIsCiAgICAgICAgICAgICJleHByZXNzaW9uIiA6IHsKICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjQwNzo0MC00MDc6NTAiLAogICAgICAgICAgICAgICAicGF0aCIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAidHlwZSIgOiAiUHJvcGVydHkiLAogICAgICAgICAgICAgICAic291cmNlIiA6IHsKICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjQwNzo0MC00MDc6NDQiLAogICAgICAgICAgICAgICAgICAibmFtZSIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAgICAidHlwZSIgOiAiT3BlcmFuZFJlZiIKICAgICAgICAgICAgICAgfQogICAgICAgICAgICB9LAogICAgICAgICAgICAib3BlcmFuZCIgOiBbIHsKICAgICAgICAgICAgICAgIm5hbWUiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgIm9wZXJhbmRUeXBlU3BlY2lmaWVyIiA6IHsKICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjQwNzozMi00MDc6MzYiLAogICAgICAgICAgICAgICAgICAibmFtZSIgOiAie2h0dHA6Ly9obDcub3JnL2ZoaXJ9eGh0bWwiLAogICAgICAgICAgICAgICAgICAidHlwZSIgOiAiTmFtZWRUeXBlU3BlY2lmaWVyIgogICAgICAgICAgICAgICB9CiAgICAgICAgICAgIH0gXQogICAgICAgICB9IF0KICAgICAgfQogICB9Cn0=" - } - ] - }, - "request": { - "method": "PUT", - "url": "Library/FHIRHelpers" - } - }, - { - "resource": { - "resourceType": "Patient", - "id": "CMSTest-patient-2", - "meta": { - "profile": [ - "http://hl7.org/fhir/us/core/StructureDefinition/us-core-patient" - ] - }, - "extension": [ - { - "url": "http://hl7.org/fhir/us/core/StructureDefinition/us-core-race", - "extension": [ - { - "url": "ombCategory", - "valueCoding": { - "system": "urn:oid:2.16.840.1.113883.6.238", - "code": "2028-9", - "display": "Asian" - } - } - ] - }, - { - "url": "http://hl7.org/fhir/us/core/StructureDefinition/us-core-ethnicity", - "extension": [ - { - "url": "ombCategory", - "valueCoding": { - "system": "urn:oid:2.16.840.1.113883.6.238", - "code": "2135-2", - "display": "Hispanic or Latino" - } - } - ] - } - ], - "identifier": [ - { - "use": "usual", - "type": { - "coding": [ - { - "system": "http://terminology.hl7.org/CodeSystem/v2-0203", - "code": "MR", - "display": "Medical Record Number" - } - ] - }, - "system": "http://hospital.smarthealthit.org", - "value": "999999999" - } - ], - "name": [ - { - "family": "Dere", - "given": [ - "Ben" - ] - } - ], - "gender": "female", - "birthDate": "2000-03-01" - }, - "request": { - "method": "PUT", - "url": "Patient/CMSTest-patient-2" - } - }, - { - "resource": { - "resourceType": "Library", - "id": "SDE", - "extension": [ - { - "url": "http://hl7.org/fhir/us/cqfmeasures/StructureDefinition/cqfm-softwaresystem", - "valueReference": { - "reference": "Device/cqf-tooling" - } - } - ], - "url": "http://content.alphora.com/fhir/dqm/Library/SDE", - "name": "SDE", - "title": "Library - Supplemental Data Elements", - "type": { - "coding": [ - { - "system": "http://terminology.hl7.org/CodeSystem/library-type", - "code": "logic-library" - } - ] - }, - "description": "A Shared library encapsulating valuable common functions related to the use of Supplemental Data Elements used in Alphora dQM and QICore-based CQL artifacts.", - "jurisdiction": [ - { - "coding": [ - { - "system": "urn:iso:std:iso:3166", - "version": "4.0.1", - "code": "US", - "display": "United States of America" - } - ], - "text": "United States of America" - } - ], - "relatedArtifact": [ - { - "type": "depends-on", - "display": "FHIR model information", - "resource": "http://fhir.org/guides/cqf/common/Library/FHIR-ModelInfo|4.0.1" - }, - { - "type": "depends-on", - "display": "Library FHIRHelpers", - "resource": "http://content.alphora.com/fhir/dqm/Library/DQMFHIRHelpers" - }, - { - "type": "depends-on", - "display": "Value set Ethnicity", - "resource": "http://cts.nlm.nih.gov/fhir/ValueSet/2.16.840.1.114222.4.11.837" - }, - { - "type": "depends-on", - "display": "Value set ONC Administrative Sex", - "resource": "http://cts.nlm.nih.gov/fhir/ValueSet/2.16.840.1.113762.1.4.1" - }, - { - "type": "depends-on", - "display": "Value set Payer", - "resource": "http://cts.nlm.nih.gov/fhir/ValueSet/2.16.840.1.114222.4.11.3591" - }, - { - "type": "depends-on", - "display": "Value set Race", - "resource": "http://cts.nlm.nih.gov/fhir/ValueSet/2.16.840.1.114222.4.11.836" - } - ], - "parameter": [ - { - "name": "Measurement Period", - "use": "in", - "min": 0, - "max": "1", - "type": "Period" - }, - { - "name": "Patient", - "use": "out", - "min": 0, - "max": "1", - "type": "Patient" - }, - { - "name": "SDE Ethnicity", - "use": "out", - "min": 0, - "max": "*", - "type": "Coding" - }, - { - "name": "SDE Payer", - "use": "out", - "min": 0, - "max": "*", - "type": "Coding" - }, - { - "name": "SDE Race", - "use": "out", - "min": 0, - "max": "*", - "type": "Coding" - }, - { - "name": "SDE Sex", - "use": "out", - "min": 0, - "max": "1", - "type": "Coding" - } - ], - "dataRequirement": [ - { - "type": "Patient", - "profile": [ - "http://hl7.org/fhir/StructureDefinition/Patient" - ] - }, - { - "type": "Patient", - "profile": [ - "http://hl7.org/fhir/StructureDefinition/Patient" - ], - "mustSupport": [ - "url", - "extension", - "value" - ] - }, - { - "type": "Patient", - "profile": [ - "http://hl7.org/fhir/StructureDefinition/Patient" - ], - "mustSupport": [ - "url", - "extension", - "value" - ] - }, - { - "type": "Coverage", - "profile": [ - "http://hl7.org/fhir/StructureDefinition/Coverage" - ], - "mustSupport": [ - "type", - "period", - "type.coding" - ], - "codeFilter": [ - { - "path": "type", - "valueSet": "http://cts.nlm.nih.gov/fhir/ValueSet/2.16.840.1.114222.4.11.3591" - } - ] - }, - { - "type": "Patient", - "profile": [ - "http://hl7.org/fhir/StructureDefinition/Patient" - ], - "mustSupport": [ - "url", - "extension", - "value" - ] - }, - { - "type": "Patient", - "profile": [ - "http://hl7.org/fhir/StructureDefinition/Patient" - ], - "mustSupport": [ - "url", - "extension", - "value" - ] - } - ], - "content": [ - { - "contentType": "text/cql", - "data": "bGlicmFyeSBTREUKCi8qQHVwZGF0ZTogQEBCVFIgMjAyMC0wMy0zMSAtPgpJbmNyZW1lbnRlZCB2ZXJzaW9uIHRvIDIuMC4wClVwZGF0ZWQgRkhJUiB2ZXJzaW9uIHRvIDQuMC4xCkBAQCovCgp1c2luZyBGSElSIHZlcnNpb24gJzQuMC4xJwoKaW5jbHVkZSBEUU1GSElSSGVscGVycyBjYWxsZWQgRkhJUkhlbHBlcnMKCnZhbHVlc2V0ICJFdGhuaWNpdHkiOiAnaHR0cDovL2N0cy5ubG0ubmloLmdvdi9maGlyL1ZhbHVlU2V0LzIuMTYuODQwLjEuMTE0MjIyLjQuMTEuODM3Jwp2YWx1ZXNldCAiT05DIEFkbWluaXN0cmF0aXZlIFNleCI6ICdodHRwOi8vY3RzLm5sbS5uaWguZ292L2ZoaXIvVmFsdWVTZXQvMi4xNi44NDAuMS4xMTM3NjIuMS40LjEnCnZhbHVlc2V0ICJQYXllciI6ICdodHRwOi8vY3RzLm5sbS5uaWguZ292L2ZoaXIvVmFsdWVTZXQvMi4xNi44NDAuMS4xMTQyMjIuNC4xMS4zNTkxJwp2YWx1ZXNldCAiUmFjZSI6ICdodHRwOi8vY3RzLm5sbS5uaWguZ292L2ZoaXIvVmFsdWVTZXQvMi4xNi44NDAuMS4xMTQyMjIuNC4xMS44MzYnCgpwYXJhbWV0ZXIgIk1lYXN1cmVtZW50IFBlcmlvZCIgZGVmYXVsdCBJbnRlcnZhbFtAMjAyMC0wMS0wMVQwMDowMDowMC4wMDAsIEAyMDIxLTAxLTAxVDAwOjAwOjAwLjAwMCkKY29udGV4dCBQYXRpZW50CgpkZWZpbmUgIlNERSBFdGhuaWNpdHkiOgogIChmbGF0dGVuICgKICAgICAgUGF0aWVudC5leHRlbnNpb24gRXh0ZW5zaW9uCiAgICAgICAgd2hlcmUgRXh0ZW5zaW9uLnVybCA9ICdodHRwOi8vaGw3Lm9yZy9maGlyL3VzL2NvcmUvU3RydWN0dXJlRGVmaW5pdGlvbi91cy1jb3JlLWV0aG5pY2l0eScKICAgICAgICAgIHJldHVybiBFeHRlbnNpb24uZXh0ZW5zaW9uCiAgICApKSBFCiAgICAgIHdoZXJlIEUudXJsID0gJ29tYkNhdGVnb3J5JwogICAgICAgIG9yIEUudXJsID0gJ2RldGFpbGVkJwogICAgICByZXR1cm4gRkhJUkhlbHBlcnMuVG9Db2RlKEUudmFsdWUpCgpkZWZpbmUgIlNERSBQYXllciI6CiAgZmxhdHRlbihbQ292ZXJhZ2U6IHR5cGUgaW4gIlBheWVyIl0gUGF5ZXIKICAgIHdoZXJlIFBheWVyLnBlcmlvZCBvdmVybGFwcyAiTWVhc3VyZW1lbnQgUGVyaW9kIgogICAgICByZXR1cm4gKAogICAgICAgIFBheWVyLnR5cGUuY29kaW5nIGMKICAgICAgICAgIHJldHVybiBGSElSSGVscGVycy5Ub0NvZGUoYykKICAgICAgKQogICkKCmRlZmluZSAiU0RFIFJhY2UiOgogIChmbGF0dGVuICgKICAgICAgUGF0aWVudC5leHRlbnNpb24gRXh0ZW5zaW9uCiAgICAgICAgd2hlcmUgRXh0ZW5zaW9uLnVybCA9ICdodHRwOi8vaGw3Lm9yZy9maGlyL3VzL2NvcmUvU3RydWN0dXJlRGVmaW5pdGlvbi91cy1jb3JlLXJhY2UnCiAgICAgICAgICByZXR1cm4gRXh0ZW5zaW9uLmV4dGVuc2lvbgogICAgKSkgRQogICAgICB3aGVyZSBFLnVybCA9ICdvbWJDYXRlZ29yeScKICAgICAgICBvciBFLnVybCA9ICdkZXRhaWxlZCcKICAgICAgcmV0dXJuIEZISVJIZWxwZXJzLlRvQ29kZShFLnZhbHVlKQoKZGVmaW5lICJTREUgU2V4IjoKICBjYXNlCiAgICAgIHdoZW4gUGF0aWVudC5nZW5kZXIgPSAnbWFsZScgdGhlbiBDb2RlIHsgY29kZTogJ00nLCBzeXN0ZW06ICdodHRwOi8vaGw3Lm9yZy9maGlyL3YzL0FkbWluaXN0cmF0aXZlR2VuZGVyJywgZGlzcGxheTogJ01hbGUnIH0KICAgICAgd2hlbiBQYXRpZW50LmdlbmRlciA9ICdmZW1hbGUnIHRoZW4gQ29kZSB7IGNvZGU6ICdGJywgc3lzdGVtOiAnaHR0cDovL2hsNy5vcmcvZmhpci92My9BZG1pbmlzdHJhdGl2ZUdlbmRlcicsIGRpc3BsYXk6ICdGZW1hbGUnIH0KICAgICAgZWxzZSBudWxsCiAgICBlbmQK" - }, - { - "contentType": "application/elm+xml", - "data": "PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0iVVRGLTgiPz4KPGxpYnJhcnkgeG1sbnM9InVybjpobDctb3JnOmVsbTpyMSIgeG1sbnM6dD0idXJuOmhsNy1vcmc6ZWxtLXR5cGVzOnIxIiB4bWxuczp4c2k9Imh0dHA6Ly93d3cudzMub3JnLzIwMDEvWE1MU2NoZW1hLWluc3RhbmNlIiB4bWxuczp4c2Q9Imh0dHA6Ly93d3cudzMub3JnLzIwMDEvWE1MU2NoZW1hIiB4bWxuczpmaGlyPSJodHRwOi8vaGw3Lm9yZy9maGlyIiB4bWxuczpxZG00Mz0idXJuOmhlYWx0aGl0LWdvdjpxZG06djRfMyIgeG1sbnM6cWRtNTM9InVybjpoZWFsdGhpdC1nb3Y6cWRtOnY1XzMiIHhtbG5zOmE9InVybjpobDctb3JnOmNxbC1hbm5vdGF0aW9uczpyMSI+CiAgIDxhbm5vdGF0aW9uIHRyYW5zbGF0b3JWZXJzaW9uPSIyLjcuMCIgdHJhbnNsYXRvck9wdGlvbnM9IkVuYWJsZUxvY2F0b3JzLERpc2FibGVMaXN0RGVtb3Rpb24sRGlzYWJsZUxpc3RQcm9tb3Rpb24iIHhzaTp0eXBlPSJhOkNxbFRvRWxtSW5mbyIvPgogICA8aWRlbnRpZmllciBpZD0iU0RFIiBzeXN0ZW09Imh0dHA6Ly9jb250ZW50LmFscGhvcmEuY29tL2ZoaXIvZHFtIi8+CiAgIDxzY2hlbWFJZGVudGlmaWVyIGlkPSJ1cm46aGw3LW9yZzplbG0iIHZlcnNpb249InIxIi8+CiAgIDx1c2luZ3M+CiAgICAgIDxkZWYgbG9jYWxJZGVudGlmaWVyPSJTeXN0ZW0iIHVyaT0idXJuOmhsNy1vcmc6ZWxtLXR5cGVzOnIxIi8+CiAgICAgIDxkZWYgbG9jYXRvcj0iODoxLTg6MjYiIGxvY2FsSWRlbnRpZmllcj0iRkhJUiIgdXJpPSJodHRwOi8vaGw3Lm9yZy9maGlyIiB2ZXJzaW9uPSI0LjAuMSI+CiAgICAgICAgIDxhbm5vdGF0aW9uIHhzaTp0eXBlPSJhOkFubm90YXRpb24iPgogICAgICAgICAgICA8YTp0IG5hbWU9InVwZGF0ZSIvPgogICAgICAgICA8L2Fubm90YXRpb24+CiAgICAgIDwvZGVmPgogICA8L3VzaW5ncz4KICAgPGluY2x1ZGVzPgogICAgICA8ZGVmIGxvY2F0b3I9IjEwOjEtMTA6NDEiIGxvY2FsSWRlbnRpZmllcj0iRkhJUkhlbHBlcnMiIHBhdGg9Imh0dHA6Ly9jb250ZW50LmFscGhvcmEuY29tL2ZoaXIvZHFtL0RRTUZISVJIZWxwZXJzIi8+CiAgIDwvaW5jbHVkZXM+CiAgIDxwYXJhbWV0ZXJzPgogICAgICA8ZGVmIGxvY2F0b3I9IjE3OjEtMTc6OTkiIG5hbWU9Ik1lYXN1cmVtZW50IFBlcmlvZCIgYWNjZXNzTGV2ZWw9IlB1YmxpYyI+CiAgICAgICAgIDxkZWZhdWx0IGxvY2F0b3I9IjE3OjQwLTE3Ojk5IiBsb3dDbG9zZWQ9InRydWUiIGhpZ2hDbG9zZWQ9ImZhbHNlIiB4c2k6dHlwZT0iSW50ZXJ2YWwiPgogICAgICAgICAgICA8bG93IGxvY2F0b3I9IjE3OjQ5LTE3OjcyIiB4c2k6dHlwZT0iRGF0ZVRpbWUiPgogICAgICAgICAgICAgICA8eWVhciB2YWx1ZVR5cGU9InQ6SW50ZWdlciIgdmFsdWU9IjIwMjAiIHhzaTp0eXBlPSJMaXRlcmFsIi8+CiAgICAgICAgICAgICAgIDxtb250aCB2YWx1ZVR5cGU9InQ6SW50ZWdlciIgdmFsdWU9IjEiIHhzaTp0eXBlPSJMaXRlcmFsIi8+CiAgICAgICAgICAgICAgIDxkYXkgdmFsdWVUeXBlPSJ0OkludGVnZXIiIHZhbHVlPSIxIiB4c2k6dHlwZT0iTGl0ZXJhbCIvPgogICAgICAgICAgICAgICA8aG91ciB2YWx1ZVR5cGU9InQ6SW50ZWdlciIgdmFsdWU9IjAiIHhzaTp0eXBlPSJMaXRlcmFsIi8+CiAgICAgICAgICAgICAgIDxtaW51dGUgdmFsdWVUeXBlPSJ0OkludGVnZXIiIHZhbHVlPSIwIiB4c2k6dHlwZT0iTGl0ZXJhbCIvPgogICAgICAgICAgICAgICA8c2Vjb25kIHZhbHVlVHlwZT0idDpJbnRlZ2VyIiB2YWx1ZT0iMCIgeHNpOnR5cGU9IkxpdGVyYWwiLz4KICAgICAgICAgICAgICAgPG1pbGxpc2Vjb25kIHZhbHVlVHlwZT0idDpJbnRlZ2VyIiB2YWx1ZT0iMCIgeHNpOnR5cGU9IkxpdGVyYWwiLz4KICAgICAgICAgICAgPC9sb3c+CiAgICAgICAgICAgIDxoaWdoIGxvY2F0b3I9IjE3Ojc1LTE3Ojk4IiB4c2k6dHlwZT0iRGF0ZVRpbWUiPgogICAgICAgICAgICAgICA8eWVhciB2YWx1ZVR5cGU9InQ6SW50ZWdlciIgdmFsdWU9IjIwMjEiIHhzaTp0eXBlPSJMaXRlcmFsIi8+CiAgICAgICAgICAgICAgIDxtb250aCB2YWx1ZVR5cGU9InQ6SW50ZWdlciIgdmFsdWU9IjEiIHhzaTp0eXBlPSJMaXRlcmFsIi8+CiAgICAgICAgICAgICAgIDxkYXkgdmFsdWVUeXBlPSJ0OkludGVnZXIiIHZhbHVlPSIxIiB4c2k6dHlwZT0iTGl0ZXJhbCIvPgogICAgICAgICAgICAgICA8aG91ciB2YWx1ZVR5cGU9InQ6SW50ZWdlciIgdmFsdWU9IjAiIHhzaTp0eXBlPSJMaXRlcmFsIi8+CiAgICAgICAgICAgICAgIDxtaW51dGUgdmFsdWVUeXBlPSJ0OkludGVnZXIiIHZhbHVlPSIwIiB4c2k6dHlwZT0iTGl0ZXJhbCIvPgogICAgICAgICAgICAgICA8c2Vjb25kIHZhbHVlVHlwZT0idDpJbnRlZ2VyIiB2YWx1ZT0iMCIgeHNpOnR5cGU9IkxpdGVyYWwiLz4KICAgICAgICAgICAgICAgPG1pbGxpc2Vjb25kIHZhbHVlVHlwZT0idDpJbnRlZ2VyIiB2YWx1ZT0iMCIgeHNpOnR5cGU9IkxpdGVyYWwiLz4KICAgICAgICAgICAgPC9oaWdoPgogICAgICAgICA8L2RlZmF1bHQ+CiAgICAgIDwvZGVmPgogICA8L3BhcmFtZXRlcnM+CiAgIDx2YWx1ZVNldHM+CiAgICAgIDxkZWYgbG9jYXRvcj0iMTI6MS0xMjo4NyIgbmFtZT0iRXRobmljaXR5IiBpZD0iaHR0cDovL2N0cy5ubG0ubmloLmdvdi9maGlyL1ZhbHVlU2V0LzIuMTYuODQwLjEuMTE0MjIyLjQuMTEuODM3IiBhY2Nlc3NMZXZlbD0iUHVibGljIi8+CiAgICAgIDxkZWYgbG9jYXRvcj0iMTM6MS0xMzo5NyIgbmFtZT0iT05DIEFkbWluaXN0cmF0aXZlIFNleCIgaWQ9Imh0dHA6Ly9jdHMubmxtLm5paC5nb3YvZmhpci9WYWx1ZVNldC8yLjE2Ljg0MC4xLjExMzc2Mi4xLjQuMSIgYWNjZXNzTGV2ZWw9IlB1YmxpYyIvPgogICAgICA8ZGVmIGxvY2F0b3I9IjE0OjEtMTQ6ODQiIG5hbWU9IlBheWVyIiBpZD0iaHR0cDovL2N0cy5ubG0ubmloLmdvdi9maGlyL1ZhbHVlU2V0LzIuMTYuODQwLjEuMTE0MjIyLjQuMTEuMzU5MSIgYWNjZXNzTGV2ZWw9IlB1YmxpYyIvPgogICAgICA8ZGVmIGxvY2F0b3I9IjE1OjEtMTU6ODIiIG5hbWU9IlJhY2UiIGlkPSJodHRwOi8vY3RzLm5sbS5uaWguZ292L2ZoaXIvVmFsdWVTZXQvMi4xNi44NDAuMS4xMTQyMjIuNC4xMS44MzYiIGFjY2Vzc0xldmVsPSJQdWJsaWMiLz4KICAgPC92YWx1ZVNldHM+CiAgIDxjb250ZXh0cz4KICAgICAgPGRlZiBsb2NhdG9yPSIxODoxLTE4OjE1IiBuYW1lPSJQYXRpZW50Ii8+CiAgIDwvY29udGV4dHM+CiAgIDxzdGF0ZW1lbnRzPgogICAgICA8ZGVmIGxvY2F0b3I9IjE4OjEtMTg6MTUiIG5hbWU9IlBhdGllbnQiIGNvbnRleHQ9IlBhdGllbnQiPgogICAgICAgICA8ZXhwcmVzc2lvbiB4c2k6dHlwZT0iU2luZ2xldG9uRnJvbSI+CiAgICAgICAgICAgIDxvcGVyYW5kIGxvY2F0b3I9IjE4OjEtMTg6MTUiIGRhdGFUeXBlPSJmaGlyOlBhdGllbnQiIHRlbXBsYXRlSWQ9Imh0dHA6Ly9obDcub3JnL2ZoaXIvU3RydWN0dXJlRGVmaW5pdGlvbi9QYXRpZW50IiB4c2k6dHlwZT0iUmV0cmlldmUiLz4KICAgICAgICAgPC9leHByZXNzaW9uPgogICAgICA8L2RlZj4KICAgICAgPGRlZiBsb2NhdG9yPSIyMDoxLTI4OjQwIiBuYW1lPSJTREUgRXRobmljaXR5IiBjb250ZXh0PSJQYXRpZW50IiBhY2Nlc3NMZXZlbD0iUHVibGljIj4KICAgICAgICAgPGV4cHJlc3Npb24gbG9jYXRvcj0iMjE6My0yODo0MCIgeHNpOnR5cGU9IlF1ZXJ5Ij4KICAgICAgICAgICAgPHNvdXJjZSBsb2NhdG9yPSIyMTozLTI1OjgiIGFsaWFzPSJFIj4KICAgICAgICAgICAgICAgPGV4cHJlc3Npb24gbG9jYXRvcj0iMjE6My0yNTo2IiB4c2k6dHlwZT0iRmxhdHRlbiI+CiAgICAgICAgICAgICAgICAgIDxvcGVyYW5kIGxvY2F0b3I9IjIxOjEyLTI1OjUiIHhzaTp0eXBlPSJRdWVyeSI+CiAgICAgICAgICAgICAgICAgICAgIDxzb3VyY2UgbG9jYXRvcj0iMjI6Ny0yMjozMyIgYWxpYXM9IkV4dGVuc2lvbiI+CiAgICAgICAgICAgICAgICAgICAgICAgIDxleHByZXNzaW9uIGxvY2F0b3I9IjIyOjctMjI6MjMiIHBhdGg9ImV4dGVuc2lvbiIgeHNpOnR5cGU9IlByb3BlcnR5Ij4KICAgICAgICAgICAgICAgICAgICAgICAgICAgPHNvdXJjZSBuYW1lPSJQYXRpZW50IiB4c2k6dHlwZT0iRXhwcmVzc2lvblJlZiIvPgogICAgICAgICAgICAgICAgICAgICAgICA8L2V4cHJlc3Npb24+CiAgICAgICAgICAgICAgICAgICAgIDwvc291cmNlPgogICAgICAgICAgICAgICAgICAgICA8d2hlcmUgbG9jYXRvcj0iMjM6OS0yMzo5NyIgeHNpOnR5cGU9IkVxdWFsIj4KICAgICAgICAgICAgICAgICAgICAgICAgPG9wZXJhbmQgbmFtZT0iVG9TdHJpbmciIGxpYnJhcnlOYW1lPSJGSElSSGVscGVycyIgeHNpOnR5cGU9IkZ1bmN0aW9uUmVmIj4KICAgICAgICAgICAgICAgICAgICAgICAgICAgPG9wZXJhbmQgbG9jYXRvcj0iMjM6MTUtMjM6MjciIHBhdGg9InVybCIgc2NvcGU9IkV4dGVuc2lvbiIgeHNpOnR5cGU9IlByb3BlcnR5Ii8+CiAgICAgICAgICAgICAgICAgICAgICAgIDwvb3BlcmFuZD4KICAgICAgICAgICAgICAgICAgICAgICAgPG9wZXJhbmQgbG9jYXRvcj0iMjM6MzEtMjM6OTciIHZhbHVlVHlwZT0idDpTdHJpbmciIHZhbHVlPSJodHRwOi8vaGw3Lm9yZy9maGlyL3VzL2NvcmUvU3RydWN0dXJlRGVmaW5pdGlvbi91cy1jb3JlLWV0aG5pY2l0eSIgeHNpOnR5cGU9IkxpdGVyYWwiLz4KICAgICAgICAgICAgICAgICAgICAgPC93aGVyZT4KICAgICAgICAgICAgICAgICAgICAgPHJldHVybiBsb2NhdG9yPSIyNDoxMS0yNDozNiI+CiAgICAgICAgICAgICAgICAgICAgICAgIDxleHByZXNzaW9uIGxvY2F0b3I9IjI0OjE4LTI0OjM2IiBwYXRoPSJleHRlbnNpb24iIHNjb3BlPSJFeHRlbnNpb24iIHhzaTp0eXBlPSJQcm9wZXJ0eSIvPgogICAgICAgICAgICAgICAgICAgICA8L3JldHVybj4KICAgICAgICAgICAgICAgICAgPC9vcGVyYW5kPgogICAgICAgICAgICAgICA8L2V4cHJlc3Npb24+CiAgICAgICAgICAgIDwvc291cmNlPgogICAgICAgICAgICA8d2hlcmUgbG9jYXRvcj0iMjY6Ny0yNzoyOSIgeHNpOnR5cGU9Ik9yIj4KICAgICAgICAgICAgICAgPG9wZXJhbmQgbG9jYXRvcj0iMjY6MTMtMjY6MzMiIHhzaTp0eXBlPSJFcXVhbCI+CiAgICAgICAgICAgICAgICAgIDxvcGVyYW5kIG5hbWU9IlRvU3RyaW5nIiBsaWJyYXJ5TmFtZT0iRkhJUkhlbHBlcnMiIHhzaTp0eXBlPSJGdW5jdGlvblJlZiI+CiAgICAgICAgICAgICAgICAgICAgIDxvcGVyYW5kIGxvY2F0b3I9IjI2OjEzLTI2OjE3IiBwYXRoPSJ1cmwiIHNjb3BlPSJFIiB4c2k6dHlwZT0iUHJvcGVydHkiLz4KICAgICAgICAgICAgICAgICAgPC9vcGVyYW5kPgogICAgICAgICAgICAgICAgICA8b3BlcmFuZCBsb2NhdG9yPSIyNjoyMS0yNjozMyIgdmFsdWVUeXBlPSJ0OlN0cmluZyIgdmFsdWU9Im9tYkNhdGVnb3J5IiB4c2k6dHlwZT0iTGl0ZXJhbCIvPgogICAgICAgICAgICAgICA8L29wZXJhbmQ+CiAgICAgICAgICAgICAgIDxvcGVyYW5kIGxvY2F0b3I9IjI3OjEyLTI3OjI5IiB4c2k6dHlwZT0iRXF1YWwiPgogICAgICAgICAgICAgICAgICA8b3BlcmFuZCBuYW1lPSJUb1N0cmluZyIgbGlicmFyeU5hbWU9IkZISVJIZWxwZXJzIiB4c2k6dHlwZT0iRnVuY3Rpb25SZWYiPgogICAgICAgICAgICAgICAgICAgICA8b3BlcmFuZCBsb2NhdG9yPSIyNzoxMi0yNzoxNiIgcGF0aD0idXJsIiBzY29wZT0iRSIgeHNpOnR5cGU9IlByb3BlcnR5Ii8+CiAgICAgICAgICAgICAgICAgIDwvb3BlcmFuZD4KICAgICAgICAgICAgICAgICAgPG9wZXJhbmQgbG9jYXRvcj0iMjc6MjAtMjc6MjkiIHZhbHVlVHlwZT0idDpTdHJpbmciIHZhbHVlPSJkZXRhaWxlZCIgeHNpOnR5cGU9IkxpdGVyYWwiLz4KICAgICAgICAgICAgICAgPC9vcGVyYW5kPgogICAgICAgICAgICA8L3doZXJlPgogICAgICAgICAgICA8cmV0dXJuIGxvY2F0b3I9IjI4OjctMjg6NDAiPgogICAgICAgICAgICAgICA8ZXhwcmVzc2lvbiBsb2NhdG9yPSIyODoxNC0yODo0MCIgbmFtZT0iVG9Db2RlIiBsaWJyYXJ5TmFtZT0iRkhJUkhlbHBlcnMiIHhzaTp0eXBlPSJGdW5jdGlvblJlZiI+CiAgICAgICAgICAgICAgICAgIDxvcGVyYW5kIGFzVHlwZT0iZmhpcjpDb2RpbmciIHhzaTp0eXBlPSJBcyI+CiAgICAgICAgICAgICAgICAgICAgIDxvcGVyYW5kIGxvY2F0b3I9IjI4OjMzLTI4OjM5IiBwYXRoPSJ2YWx1ZSIgc2NvcGU9IkUiIHhzaTp0eXBlPSJQcm9wZXJ0eSIvPgogICAgICAgICAgICAgICAgICA8L29wZXJhbmQ+CiAgICAgICAgICAgICAgIDwvZXhwcmVzc2lvbj4KICAgICAgICAgICAgPC9yZXR1cm4+CiAgICAgICAgIDwvZXhwcmVzc2lvbj4KICAgICAgPC9kZWY+CiAgICAgIDxkZWYgbG9jYXRvcj0iMzA6MS0zNzozIiBuYW1lPSJTREUgUGF5ZXIiIGNvbnRleHQ9IlBhdGllbnQiIGFjY2Vzc0xldmVsPSJQdWJsaWMiPgogICAgICAgICA8ZXhwcmVzc2lvbiBsb2NhdG9yPSIzMTozLTM3OjMiIHhzaTp0eXBlPSJGbGF0dGVuIj4KICAgICAgICAgICAgPG9wZXJhbmQgbG9jYXRvcj0iMzE6MTAtMzc6MyIgeHNpOnR5cGU9IlF1ZXJ5Ij4KICAgICAgICAgICAgICAgPHNvdXJjZSBsb2NhdG9yPSIzMToxMS0zMTo0MyIgYWxpYXM9IlBheWVyIj4KICAgICAgICAgICAgICAgICAgPGV4cHJlc3Npb24gbG9jYXRvcj0iMzE6MTEtMzE6MzciIGRhdGFUeXBlPSJmaGlyOkNvdmVyYWdlIiB0ZW1wbGF0ZUlkPSJodHRwOi8vaGw3Lm9yZy9maGlyL1N0cnVjdHVyZURlZmluaXRpb24vQ292ZXJhZ2UiIGNvZGVQcm9wZXJ0eT0idHlwZSIgY29kZUNvbXBhcmF0b3I9ImluIiB4c2k6dHlwZT0iUmV0cmlldmUiPgogICAgICAgICAgICAgICAgICAgICA8Y29kZXMgbG9jYXRvcj0iMzE6MzAtMzE6MzYiIG5hbWU9IlBheWVyIiBwcmVzZXJ2ZT0idHJ1ZSIgeHNpOnR5cGU9IlZhbHVlU2V0UmVmIi8+CiAgICAgICAgICAgICAgICAgIDwvZXhwcmVzc2lvbj4KICAgICAgICAgICAgICAgPC9zb3VyY2U+CiAgICAgICAgICAgICAgIDx3aGVyZSBsb2NhdG9yPSIzMjo1LTMyOjUyIiB4c2k6dHlwZT0iT3ZlcmxhcHMiPgogICAgICAgICAgICAgICAgICA8b3BlcmFuZCBuYW1lPSJUb0ludGVydmFsIiBsaWJyYXJ5TmFtZT0iRkhJUkhlbHBlcnMiIHhzaTp0eXBlPSJGdW5jdGlvblJlZiI+CiAgICAgICAgICAgICAgICAgICAgIDxvcGVyYW5kIGxvY2F0b3I9IjMyOjExLTMyOjIyIiBwYXRoPSJwZXJpb2QiIHNjb3BlPSJQYXllciIgeHNpOnR5cGU9IlByb3BlcnR5Ii8+CiAgICAgICAgICAgICAgICAgIDwvb3BlcmFuZD4KICAgICAgICAgICAgICAgICAgPG9wZXJhbmQgbG9jYXRvcj0iMzI6MzMtMzI6NTIiIG5hbWU9Ik1lYXN1cmVtZW50IFBlcmlvZCIgeHNpOnR5cGU9IlBhcmFtZXRlclJlZiIvPgogICAgICAgICAgICAgICA8L3doZXJlPgogICAgICAgICAgICAgICA8cmV0dXJuIGxvY2F0b3I9IjMzOjctMzY6NyI+CiAgICAgICAgICAgICAgICAgIDxleHByZXNzaW9uIGxvY2F0b3I9IjMzOjE0LTM2OjciIHhzaTp0eXBlPSJRdWVyeSI+CiAgICAgICAgICAgICAgICAgICAgIDxzb3VyY2UgbG9jYXRvcj0iMzQ6OS0zNDoyNyIgYWxpYXM9ImMiPgogICAgICAgICAgICAgICAgICAgICAgICA8ZXhwcmVzc2lvbiBsb2NhdG9yPSIzNDo5LTM0OjI1IiBwYXRoPSJjb2RpbmciIHhzaTp0eXBlPSJQcm9wZXJ0eSI+CiAgICAgICAgICAgICAgICAgICAgICAgICAgIDxzb3VyY2UgcGF0aD0idHlwZSIgc2NvcGU9IlBheWVyIiB4c2k6dHlwZT0iUHJvcGVydHkiLz4KICAgICAgICAgICAgICAgICAgICAgICAgPC9leHByZXNzaW9uPgogICAgICAgICAgICAgICAgICAgICA8L3NvdXJjZT4KICAgICAgICAgICAgICAgICAgICAgPHJldHVybiBsb2NhdG9yPSIzNToxMS0zNTozOCI+CiAgICAgICAgICAgICAgICAgICAgICAgIDxleHByZXNzaW9uIGxvY2F0b3I9IjM1OjE4LTM1OjM4IiBuYW1lPSJUb0NvZGUiIGxpYnJhcnlOYW1lPSJGSElSSGVscGVycyIgeHNpOnR5cGU9IkZ1bmN0aW9uUmVmIj4KICAgICAgICAgICAgICAgICAgICAgICAgICAgPG9wZXJhbmQgbG9jYXRvcj0iMzU6MzciIG5hbWU9ImMiIHhzaTp0eXBlPSJBbGlhc1JlZiIvPgogICAgICAgICAgICAgICAgICAgICAgICA8L2V4cHJlc3Npb24+CiAgICAgICAgICAgICAgICAgICAgIDwvcmV0dXJuPgogICAgICAgICAgICAgICAgICA8L2V4cHJlc3Npb24+CiAgICAgICAgICAgICAgIDwvcmV0dXJuPgogICAgICAgICAgICA8L29wZXJhbmQ+CiAgICAgICAgIDwvZXhwcmVzc2lvbj4KICAgICAgPC9kZWY+CiAgICAgIDxkZWYgbG9jYXRvcj0iMzk6MS00Nzo0MCIgbmFtZT0iU0RFIFJhY2UiIGNvbnRleHQ9IlBhdGllbnQiIGFjY2Vzc0xldmVsPSJQdWJsaWMiPgogICAgICAgICA8ZXhwcmVzc2lvbiBsb2NhdG9yPSI0MDozLTQ3OjQwIiB4c2k6dHlwZT0iUXVlcnkiPgogICAgICAgICAgICA8c291cmNlIGxvY2F0b3I9IjQwOjMtNDQ6OCIgYWxpYXM9IkUiPgogICAgICAgICAgICAgICA8ZXhwcmVzc2lvbiBsb2NhdG9yPSI0MDozLTQ0OjYiIHhzaTp0eXBlPSJGbGF0dGVuIj4KICAgICAgICAgICAgICAgICAgPG9wZXJhbmQgbG9jYXRvcj0iNDA6MTItNDQ6NSIgeHNpOnR5cGU9IlF1ZXJ5Ij4KICAgICAgICAgICAgICAgICAgICAgPHNvdXJjZSBsb2NhdG9yPSI0MTo3LTQxOjMzIiBhbGlhcz0iRXh0ZW5zaW9uIj4KICAgICAgICAgICAgICAgICAgICAgICAgPGV4cHJlc3Npb24gbG9jYXRvcj0iNDE6Ny00MToyMyIgcGF0aD0iZXh0ZW5zaW9uIiB4c2k6dHlwZT0iUHJvcGVydHkiPgogICAgICAgICAgICAgICAgICAgICAgICAgICA8c291cmNlIG5hbWU9IlBhdGllbnQiIHhzaTp0eXBlPSJFeHByZXNzaW9uUmVmIi8+CiAgICAgICAgICAgICAgICAgICAgICAgIDwvZXhwcmVzc2lvbj4KICAgICAgICAgICAgICAgICAgICAgPC9zb3VyY2U+CiAgICAgICAgICAgICAgICAgICAgIDx3aGVyZSBsb2NhdG9yPSI0Mjo5LTQyOjkyIiB4c2k6dHlwZT0iRXF1YWwiPgogICAgICAgICAgICAgICAgICAgICAgICA8b3BlcmFuZCBuYW1lPSJUb1N0cmluZyIgbGlicmFyeU5hbWU9IkZISVJIZWxwZXJzIiB4c2k6dHlwZT0iRnVuY3Rpb25SZWYiPgogICAgICAgICAgICAgICAgICAgICAgICAgICA8b3BlcmFuZCBsb2NhdG9yPSI0MjoxNS00MjoyNyIgcGF0aD0idXJsIiBzY29wZT0iRXh0ZW5zaW9uIiB4c2k6dHlwZT0iUHJvcGVydHkiLz4KICAgICAgICAgICAgICAgICAgICAgICAgPC9vcGVyYW5kPgogICAgICAgICAgICAgICAgICAgICAgICA8b3BlcmFuZCBsb2NhdG9yPSI0MjozMS00Mjo5MiIgdmFsdWVUeXBlPSJ0OlN0cmluZyIgdmFsdWU9Imh0dHA6Ly9obDcub3JnL2ZoaXIvdXMvY29yZS9TdHJ1Y3R1cmVEZWZpbml0aW9uL3VzLWNvcmUtcmFjZSIgeHNpOnR5cGU9IkxpdGVyYWwiLz4KICAgICAgICAgICAgICAgICAgICAgPC93aGVyZT4KICAgICAgICAgICAgICAgICAgICAgPHJldHVybiBsb2NhdG9yPSI0MzoxMS00MzozNiI+CiAgICAgICAgICAgICAgICAgICAgICAgIDxleHByZXNzaW9uIGxvY2F0b3I9IjQzOjE4LTQzOjM2IiBwYXRoPSJleHRlbnNpb24iIHNjb3BlPSJFeHRlbnNpb24iIHhzaTp0eXBlPSJQcm9wZXJ0eSIvPgogICAgICAgICAgICAgICAgICAgICA8L3JldHVybj4KICAgICAgICAgICAgICAgICAgPC9vcGVyYW5kPgogICAgICAgICAgICAgICA8L2V4cHJlc3Npb24+CiAgICAgICAgICAgIDwvc291cmNlPgogICAgICAgICAgICA8d2hlcmUgbG9jYXRvcj0iNDU6Ny00NjoyOSIgeHNpOnR5cGU9Ik9yIj4KICAgICAgICAgICAgICAgPG9wZXJhbmQgbG9jYXRvcj0iNDU6MTMtNDU6MzMiIHhzaTp0eXBlPSJFcXVhbCI+CiAgICAgICAgICAgICAgICAgIDxvcGVyYW5kIG5hbWU9IlRvU3RyaW5nIiBsaWJyYXJ5TmFtZT0iRkhJUkhlbHBlcnMiIHhzaTp0eXBlPSJGdW5jdGlvblJlZiI+CiAgICAgICAgICAgICAgICAgICAgIDxvcGVyYW5kIGxvY2F0b3I9IjQ1OjEzLTQ1OjE3IiBwYXRoPSJ1cmwiIHNjb3BlPSJFIiB4c2k6dHlwZT0iUHJvcGVydHkiLz4KICAgICAgICAgICAgICAgICAgPC9vcGVyYW5kPgogICAgICAgICAgICAgICAgICA8b3BlcmFuZCBsb2NhdG9yPSI0NToyMS00NTozMyIgdmFsdWVUeXBlPSJ0OlN0cmluZyIgdmFsdWU9Im9tYkNhdGVnb3J5IiB4c2k6dHlwZT0iTGl0ZXJhbCIvPgogICAgICAgICAgICAgICA8L29wZXJhbmQ+CiAgICAgICAgICAgICAgIDxvcGVyYW5kIGxvY2F0b3I9IjQ2OjEyLTQ2OjI5IiB4c2k6dHlwZT0iRXF1YWwiPgogICAgICAgICAgICAgICAgICA8b3BlcmFuZCBuYW1lPSJUb1N0cmluZyIgbGlicmFyeU5hbWU9IkZISVJIZWxwZXJzIiB4c2k6dHlwZT0iRnVuY3Rpb25SZWYiPgogICAgICAgICAgICAgICAgICAgICA8b3BlcmFuZCBsb2NhdG9yPSI0NjoxMi00NjoxNiIgcGF0aD0idXJsIiBzY29wZT0iRSIgeHNpOnR5cGU9IlByb3BlcnR5Ii8+CiAgICAgICAgICAgICAgICAgIDwvb3BlcmFuZD4KICAgICAgICAgICAgICAgICAgPG9wZXJhbmQgbG9jYXRvcj0iNDY6MjAtNDY6MjkiIHZhbHVlVHlwZT0idDpTdHJpbmciIHZhbHVlPSJkZXRhaWxlZCIgeHNpOnR5cGU9IkxpdGVyYWwiLz4KICAgICAgICAgICAgICAgPC9vcGVyYW5kPgogICAgICAgICAgICA8L3doZXJlPgogICAgICAgICAgICA8cmV0dXJuIGxvY2F0b3I9IjQ3OjctNDc6NDAiPgogICAgICAgICAgICAgICA8ZXhwcmVzc2lvbiBsb2NhdG9yPSI0NzoxNC00Nzo0MCIgbmFtZT0iVG9Db2RlIiBsaWJyYXJ5TmFtZT0iRkhJUkhlbHBlcnMiIHhzaTp0eXBlPSJGdW5jdGlvblJlZiI+CiAgICAgICAgICAgICAgICAgIDxvcGVyYW5kIGFzVHlwZT0iZmhpcjpDb2RpbmciIHhzaTp0eXBlPSJBcyI+CiAgICAgICAgICAgICAgICAgICAgIDxvcGVyYW5kIGxvY2F0b3I9IjQ3OjMzLTQ3OjM5IiBwYXRoPSJ2YWx1ZSIgc2NvcGU9IkUiIHhzaTp0eXBlPSJQcm9wZXJ0eSIvPgogICAgICAgICAgICAgICAgICA8L29wZXJhbmQ+CiAgICAgICAgICAgICAgIDwvZXhwcmVzc2lvbj4KICAgICAgICAgICAgPC9yZXR1cm4+CiAgICAgICAgIDwvZXhwcmVzc2lvbj4KICAgICAgPC9kZWY+CiAgICAgIDxkZWYgbG9jYXRvcj0iNDk6MS01NDo3IiBuYW1lPSJTREUgU2V4IiBjb250ZXh0PSJQYXRpZW50IiBhY2Nlc3NMZXZlbD0iUHVibGljIj4KICAgICAgICAgPGV4cHJlc3Npb24gbG9jYXRvcj0iNTA6My01NDo3IiB4c2k6dHlwZT0iQ2FzZSI+CiAgICAgICAgICAgIDxjYXNlSXRlbSBsb2NhdG9yPSI1MTo3LTUxOjEzMCI+CiAgICAgICAgICAgICAgIDx3aGVuIGxvY2F0b3I9IjUxOjEyLTUxOjM0IiB4c2k6dHlwZT0iRXF1YWwiPgogICAgICAgICAgICAgICAgICA8b3BlcmFuZCBuYW1lPSJUb1N0cmluZyIgbGlicmFyeU5hbWU9IkZISVJIZWxwZXJzIiB4c2k6dHlwZT0iRnVuY3Rpb25SZWYiPgogICAgICAgICAgICAgICAgICAgICA8b3BlcmFuZCBsb2NhdG9yPSI1MToxMi01MToyNSIgcGF0aD0iZ2VuZGVyIiB4c2k6dHlwZT0iUHJvcGVydHkiPgogICAgICAgICAgICAgICAgICAgICAgICA8c291cmNlIGxvY2F0b3I9IjUxOjEyLTUxOjE4IiBuYW1lPSJQYXRpZW50IiB4c2k6dHlwZT0iRXhwcmVzc2lvblJlZiIvPgogICAgICAgICAgICAgICAgICAgICA8L29wZXJhbmQ+CiAgICAgICAgICAgICAgICAgIDwvb3BlcmFuZD4KICAgICAgICAgICAgICAgICAgPG9wZXJhbmQgbG9jYXRvcj0iNTE6MjktNTE6MzQiIHZhbHVlVHlwZT0idDpTdHJpbmciIHZhbHVlPSJtYWxlIiB4c2k6dHlwZT0iTGl0ZXJhbCIvPgogICAgICAgICAgICAgICA8L3doZW4+CiAgICAgICAgICAgICAgIDx0aGVuIGxvY2F0b3I9IjUxOjQxLTUxOjEzMCIgY2xhc3NUeXBlPSJ0OkNvZGUiIHhzaTp0eXBlPSJJbnN0YW5jZSI+CiAgICAgICAgICAgICAgICAgIDxlbGVtZW50IG5hbWU9ImNvZGUiPgogICAgICAgICAgICAgICAgICAgICA8dmFsdWUgbG9jYXRvcj0iNTE6NTQtNTE6NTYiIHZhbHVlVHlwZT0idDpTdHJpbmciIHZhbHVlPSJNIiB4c2k6dHlwZT0iTGl0ZXJhbCIvPgogICAgICAgICAgICAgICAgICA8L2VsZW1lbnQ+CiAgICAgICAgICAgICAgICAgIDxlbGVtZW50IG5hbWU9InN5c3RlbSI+CiAgICAgICAgICAgICAgICAgICAgIDx2YWx1ZSBsb2NhdG9yPSI1MTo2Ny01MToxMTEiIHZhbHVlVHlwZT0idDpTdHJpbmciIHZhbHVlPSJodHRwOi8vaGw3Lm9yZy9maGlyL3YzL0FkbWluaXN0cmF0aXZlR2VuZGVyIiB4c2k6dHlwZT0iTGl0ZXJhbCIvPgogICAgICAgICAgICAgICAgICA8L2VsZW1lbnQ+CiAgICAgICAgICAgICAgICAgIDxlbGVtZW50IG5hbWU9ImRpc3BsYXkiPgogICAgICAgICAgICAgICAgICAgICA8dmFsdWUgbG9jYXRvcj0iNTE6MTIzLTUxOjEyOCIgdmFsdWVUeXBlPSJ0OlN0cmluZyIgdmFsdWU9Ik1hbGUiIHhzaTp0eXBlPSJMaXRlcmFsIi8+CiAgICAgICAgICAgICAgICAgIDwvZWxlbWVudD4KICAgICAgICAgICAgICAgPC90aGVuPgogICAgICAgICAgICA8L2Nhc2VJdGVtPgogICAgICAgICAgICA8Y2FzZUl0ZW0gbG9jYXRvcj0iNTI6Ny01MjoxMzQiPgogICAgICAgICAgICAgICA8d2hlbiBsb2NhdG9yPSI1MjoxMi01MjozNiIgeHNpOnR5cGU9IkVxdWFsIj4KICAgICAgICAgICAgICAgICAgPG9wZXJhbmQgbmFtZT0iVG9TdHJpbmciIGxpYnJhcnlOYW1lPSJGSElSSGVscGVycyIgeHNpOnR5cGU9IkZ1bmN0aW9uUmVmIj4KICAgICAgICAgICAgICAgICAgICAgPG9wZXJhbmQgbG9jYXRvcj0iNTI6MTItNTI6MjUiIHBhdGg9ImdlbmRlciIgeHNpOnR5cGU9IlByb3BlcnR5Ij4KICAgICAgICAgICAgICAgICAgICAgICAgPHNvdXJjZSBsb2NhdG9yPSI1MjoxMi01MjoxOCIgbmFtZT0iUGF0aWVudCIgeHNpOnR5cGU9IkV4cHJlc3Npb25SZWYiLz4KICAgICAgICAgICAgICAgICAgICAgPC9vcGVyYW5kPgogICAgICAgICAgICAgICAgICA8L29wZXJhbmQ+CiAgICAgICAgICAgICAgICAgIDxvcGVyYW5kIGxvY2F0b3I9IjUyOjI5LTUyOjM2IiB2YWx1ZVR5cGU9InQ6U3RyaW5nIiB2YWx1ZT0iZmVtYWxlIiB4c2k6dHlwZT0iTGl0ZXJhbCIvPgogICAgICAgICAgICAgICA8L3doZW4+CiAgICAgICAgICAgICAgIDx0aGVuIGxvY2F0b3I9IjUyOjQzLTUyOjEzNCIgY2xhc3NUeXBlPSJ0OkNvZGUiIHhzaTp0eXBlPSJJbnN0YW5jZSI+CiAgICAgICAgICAgICAgICAgIDxlbGVtZW50IG5hbWU9ImNvZGUiPgogICAgICAgICAgICAgICAgICAgICA8dmFsdWUgbG9jYXRvcj0iNTI6NTYtNTI6NTgiIHZhbHVlVHlwZT0idDpTdHJpbmciIHZhbHVlPSJGIiB4c2k6dHlwZT0iTGl0ZXJhbCIvPgogICAgICAgICAgICAgICAgICA8L2VsZW1lbnQ+CiAgICAgICAgICAgICAgICAgIDxlbGVtZW50IG5hbWU9InN5c3RlbSI+CiAgICAgICAgICAgICAgICAgICAgIDx2YWx1ZSBsb2NhdG9yPSI1Mjo2OS01MjoxMTMiIHZhbHVlVHlwZT0idDpTdHJpbmciIHZhbHVlPSJodHRwOi8vaGw3Lm9yZy9maGlyL3YzL0FkbWluaXN0cmF0aXZlR2VuZGVyIiB4c2k6dHlwZT0iTGl0ZXJhbCIvPgogICAgICAgICAgICAgICAgICA8L2VsZW1lbnQ+CiAgICAgICAgICAgICAgICAgIDxlbGVtZW50IG5hbWU9ImRpc3BsYXkiPgogICAgICAgICAgICAgICAgICAgICA8dmFsdWUgbG9jYXRvcj0iNTI6MTI1LTUyOjEzMiIgdmFsdWVUeXBlPSJ0OlN0cmluZyIgdmFsdWU9IkZlbWFsZSIgeHNpOnR5cGU9IkxpdGVyYWwiLz4KICAgICAgICAgICAgICAgICAgPC9lbGVtZW50PgogICAgICAgICAgICAgICA8L3RoZW4+CiAgICAgICAgICAgIDwvY2FzZUl0ZW0+CiAgICAgICAgICAgIDxlbHNlIGFzVHlwZT0idDpDb2RlIiB4c2k6dHlwZT0iQXMiPgogICAgICAgICAgICAgICA8b3BlcmFuZCBsb2NhdG9yPSI1MzoxMi01MzoxNSIgeHNpOnR5cGU9Ik51bGwiLz4KICAgICAgICAgICAgPC9lbHNlPgogICAgICAgICA8L2V4cHJlc3Npb24+CiAgICAgIDwvZGVmPgogICA8L3N0YXRlbWVudHM+CjwvbGlicmFyeT4K" - }, - { - "contentType": "application/elm+json", - "data": "ewogICAibGlicmFyeSIgOiB7CiAgICAgICJhbm5vdGF0aW9uIiA6IFsgewogICAgICAgICAidHJhbnNsYXRvclZlcnNpb24iIDogIjIuNy4wIiwKICAgICAgICAgInRyYW5zbGF0b3JPcHRpb25zIiA6ICJFbmFibGVMb2NhdG9ycyxEaXNhYmxlTGlzdERlbW90aW9uLERpc2FibGVMaXN0UHJvbW90aW9uIiwKICAgICAgICAgInR5cGUiIDogIkNxbFRvRWxtSW5mbyIKICAgICAgfSBdLAogICAgICAiaWRlbnRpZmllciIgOiB7CiAgICAgICAgICJpZCIgOiAiU0RFIiwKICAgICAgICAgInN5c3RlbSIgOiAiaHR0cDovL2NvbnRlbnQuYWxwaG9yYS5jb20vZmhpci9kcW0iCiAgICAgIH0sCiAgICAgICJzY2hlbWFJZGVudGlmaWVyIiA6IHsKICAgICAgICAgImlkIiA6ICJ1cm46aGw3LW9yZzplbG0iLAogICAgICAgICAidmVyc2lvbiIgOiAicjEiCiAgICAgIH0sCiAgICAgICJ1c2luZ3MiIDogewogICAgICAgICAiZGVmIiA6IFsgewogICAgICAgICAgICAibG9jYWxJZGVudGlmaWVyIiA6ICJTeXN0ZW0iLAogICAgICAgICAgICAidXJpIiA6ICJ1cm46aGw3LW9yZzplbG0tdHlwZXM6cjEiCiAgICAgICAgIH0sIHsKICAgICAgICAgICAgImxvY2F0b3IiIDogIjg6MS04OjI2IiwKICAgICAgICAgICAgImxvY2FsSWRlbnRpZmllciIgOiAiRkhJUiIsCiAgICAgICAgICAgICJ1cmkiIDogImh0dHA6Ly9obDcub3JnL2ZoaXIiLAogICAgICAgICAgICAidmVyc2lvbiIgOiAiNC4wLjEiLAogICAgICAgICAgICAiYW5ub3RhdGlvbiIgOiBbIHsKICAgICAgICAgICAgICAgInR5cGUiIDogIkFubm90YXRpb24iLAogICAgICAgICAgICAgICAidCIgOiBbIHsKICAgICAgICAgICAgICAgICAgIm5hbWUiIDogInVwZGF0ZSIKICAgICAgICAgICAgICAgfSBdCiAgICAgICAgICAgIH0gXQogICAgICAgICB9IF0KICAgICAgfSwKICAgICAgImluY2x1ZGVzIiA6IHsKICAgICAgICAgImRlZiIgOiBbIHsKICAgICAgICAgICAgImxvY2F0b3IiIDogIjEwOjEtMTA6NDEiLAogICAgICAgICAgICAibG9jYWxJZGVudGlmaWVyIiA6ICJGSElSSGVscGVycyIsCiAgICAgICAgICAgICJwYXRoIiA6ICJodHRwOi8vY29udGVudC5hbHBob3JhLmNvbS9maGlyL2RxbS9EUU1GSElSSGVscGVycyIKICAgICAgICAgfSBdCiAgICAgIH0sCiAgICAgICJwYXJhbWV0ZXJzIiA6IHsKICAgICAgICAgImRlZiIgOiBbIHsKICAgICAgICAgICAgImxvY2F0b3IiIDogIjE3OjEtMTc6OTkiLAogICAgICAgICAgICAibmFtZSIgOiAiTWVhc3VyZW1lbnQgUGVyaW9kIiwKICAgICAgICAgICAgImFjY2Vzc0xldmVsIiA6ICJQdWJsaWMiLAogICAgICAgICAgICAiZGVmYXVsdCIgOiB7CiAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIxNzo0MC0xNzo5OSIsCiAgICAgICAgICAgICAgICJsb3dDbG9zZWQiIDogdHJ1ZSwKICAgICAgICAgICAgICAgImhpZ2hDbG9zZWQiIDogZmFsc2UsCiAgICAgICAgICAgICAgICJ0eXBlIiA6ICJJbnRlcnZhbCIsCiAgICAgICAgICAgICAgICJsb3ciIDogewogICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMTc6NDktMTc6NzIiLAogICAgICAgICAgICAgICAgICAidHlwZSIgOiAiRGF0ZVRpbWUiLAogICAgICAgICAgICAgICAgICAieWVhciIgOiB7CiAgICAgICAgICAgICAgICAgICAgICJ2YWx1ZVR5cGUiIDogInt1cm46aGw3LW9yZzplbG0tdHlwZXM6cjF9SW50ZWdlciIsCiAgICAgICAgICAgICAgICAgICAgICJ2YWx1ZSIgOiAiMjAyMCIsCiAgICAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJMaXRlcmFsIgogICAgICAgICAgICAgICAgICB9LAogICAgICAgICAgICAgICAgICAibW9udGgiIDogewogICAgICAgICAgICAgICAgICAgICAidmFsdWVUeXBlIiA6ICJ7dXJuOmhsNy1vcmc6ZWxtLXR5cGVzOnIxfUludGVnZXIiLAogICAgICAgICAgICAgICAgICAgICAidmFsdWUiIDogIjEiLAogICAgICAgICAgICAgICAgICAgICAidHlwZSIgOiAiTGl0ZXJhbCIKICAgICAgICAgICAgICAgICAgfSwKICAgICAgICAgICAgICAgICAgImRheSIgOiB7CiAgICAgICAgICAgICAgICAgICAgICJ2YWx1ZVR5cGUiIDogInt1cm46aGw3LW9yZzplbG0tdHlwZXM6cjF9SW50ZWdlciIsCiAgICAgICAgICAgICAgICAgICAgICJ2YWx1ZSIgOiAiMSIsCiAgICAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJMaXRlcmFsIgogICAgICAgICAgICAgICAgICB9LAogICAgICAgICAgICAgICAgICAiaG91ciIgOiB7CiAgICAgICAgICAgICAgICAgICAgICJ2YWx1ZVR5cGUiIDogInt1cm46aGw3LW9yZzplbG0tdHlwZXM6cjF9SW50ZWdlciIsCiAgICAgICAgICAgICAgICAgICAgICJ2YWx1ZSIgOiAiMCIsCiAgICAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJMaXRlcmFsIgogICAgICAgICAgICAgICAgICB9LAogICAgICAgICAgICAgICAgICAibWludXRlIiA6IHsKICAgICAgICAgICAgICAgICAgICAgInZhbHVlVHlwZSIgOiAie3VybjpobDctb3JnOmVsbS10eXBlczpyMX1JbnRlZ2VyIiwKICAgICAgICAgICAgICAgICAgICAgInZhbHVlIiA6ICIwIiwKICAgICAgICAgICAgICAgICAgICAgInR5cGUiIDogIkxpdGVyYWwiCiAgICAgICAgICAgICAgICAgIH0sCiAgICAgICAgICAgICAgICAgICJzZWNvbmQiIDogewogICAgICAgICAgICAgICAgICAgICAidmFsdWVUeXBlIiA6ICJ7dXJuOmhsNy1vcmc6ZWxtLXR5cGVzOnIxfUludGVnZXIiLAogICAgICAgICAgICAgICAgICAgICAidmFsdWUiIDogIjAiLAogICAgICAgICAgICAgICAgICAgICAidHlwZSIgOiAiTGl0ZXJhbCIKICAgICAgICAgICAgICAgICAgfSwKICAgICAgICAgICAgICAgICAgIm1pbGxpc2Vjb25kIiA6IHsKICAgICAgICAgICAgICAgICAgICAgInZhbHVlVHlwZSIgOiAie3VybjpobDctb3JnOmVsbS10eXBlczpyMX1JbnRlZ2VyIiwKICAgICAgICAgICAgICAgICAgICAgInZhbHVlIiA6ICIwIiwKICAgICAgICAgICAgICAgICAgICAgInR5cGUiIDogIkxpdGVyYWwiCiAgICAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgICAgfSwKICAgICAgICAgICAgICAgImhpZ2giIDogewogICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMTc6NzUtMTc6OTgiLAogICAgICAgICAgICAgICAgICAidHlwZSIgOiAiRGF0ZVRpbWUiLAogICAgICAgICAgICAgICAgICAieWVhciIgOiB7CiAgICAgICAgICAgICAgICAgICAgICJ2YWx1ZVR5cGUiIDogInt1cm46aGw3LW9yZzplbG0tdHlwZXM6cjF9SW50ZWdlciIsCiAgICAgICAgICAgICAgICAgICAgICJ2YWx1ZSIgOiAiMjAyMSIsCiAgICAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJMaXRlcmFsIgogICAgICAgICAgICAgICAgICB9LAogICAgICAgICAgICAgICAgICAibW9udGgiIDogewogICAgICAgICAgICAgICAgICAgICAidmFsdWVUeXBlIiA6ICJ7dXJuOmhsNy1vcmc6ZWxtLXR5cGVzOnIxfUludGVnZXIiLAogICAgICAgICAgICAgICAgICAgICAidmFsdWUiIDogIjEiLAogICAgICAgICAgICAgICAgICAgICAidHlwZSIgOiAiTGl0ZXJhbCIKICAgICAgICAgICAgICAgICAgfSwKICAgICAgICAgICAgICAgICAgImRheSIgOiB7CiAgICAgICAgICAgICAgICAgICAgICJ2YWx1ZVR5cGUiIDogInt1cm46aGw3LW9yZzplbG0tdHlwZXM6cjF9SW50ZWdlciIsCiAgICAgICAgICAgICAgICAgICAgICJ2YWx1ZSIgOiAiMSIsCiAgICAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJMaXRlcmFsIgogICAgICAgICAgICAgICAgICB9LAogICAgICAgICAgICAgICAgICAiaG91ciIgOiB7CiAgICAgICAgICAgICAgICAgICAgICJ2YWx1ZVR5cGUiIDogInt1cm46aGw3LW9yZzplbG0tdHlwZXM6cjF9SW50ZWdlciIsCiAgICAgICAgICAgICAgICAgICAgICJ2YWx1ZSIgOiAiMCIsCiAgICAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJMaXRlcmFsIgogICAgICAgICAgICAgICAgICB9LAogICAgICAgICAgICAgICAgICAibWludXRlIiA6IHsKICAgICAgICAgICAgICAgICAgICAgInZhbHVlVHlwZSIgOiAie3VybjpobDctb3JnOmVsbS10eXBlczpyMX1JbnRlZ2VyIiwKICAgICAgICAgICAgICAgICAgICAgInZhbHVlIiA6ICIwIiwKICAgICAgICAgICAgICAgICAgICAgInR5cGUiIDogIkxpdGVyYWwiCiAgICAgICAgICAgICAgICAgIH0sCiAgICAgICAgICAgICAgICAgICJzZWNvbmQiIDogewogICAgICAgICAgICAgICAgICAgICAidmFsdWVUeXBlIiA6ICJ7dXJuOmhsNy1vcmc6ZWxtLXR5cGVzOnIxfUludGVnZXIiLAogICAgICAgICAgICAgICAgICAgICAidmFsdWUiIDogIjAiLAogICAgICAgICAgICAgICAgICAgICAidHlwZSIgOiAiTGl0ZXJhbCIKICAgICAgICAgICAgICAgICAgfSwKICAgICAgICAgICAgICAgICAgIm1pbGxpc2Vjb25kIiA6IHsKICAgICAgICAgICAgICAgICAgICAgInZhbHVlVHlwZSIgOiAie3VybjpobDctb3JnOmVsbS10eXBlczpyMX1JbnRlZ2VyIiwKICAgICAgICAgICAgICAgICAgICAgInZhbHVlIiA6ICIwIiwKICAgICAgICAgICAgICAgICAgICAgInR5cGUiIDogIkxpdGVyYWwiCiAgICAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgICAgfQogICAgICAgICAgICB9CiAgICAgICAgIH0gXQogICAgICB9LAogICAgICAidmFsdWVTZXRzIiA6IHsKICAgICAgICAgImRlZiIgOiBbIHsKICAgICAgICAgICAgImxvY2F0b3IiIDogIjEyOjEtMTI6ODciLAogICAgICAgICAgICAibmFtZSIgOiAiRXRobmljaXR5IiwKICAgICAgICAgICAgImlkIiA6ICJodHRwOi8vY3RzLm5sbS5uaWguZ292L2ZoaXIvVmFsdWVTZXQvMi4xNi44NDAuMS4xMTQyMjIuNC4xMS44MzciLAogICAgICAgICAgICAiYWNjZXNzTGV2ZWwiIDogIlB1YmxpYyIKICAgICAgICAgfSwgewogICAgICAgICAgICAibG9jYXRvciIgOiAiMTM6MS0xMzo5NyIsCiAgICAgICAgICAgICJuYW1lIiA6ICJPTkMgQWRtaW5pc3RyYXRpdmUgU2V4IiwKICAgICAgICAgICAgImlkIiA6ICJodHRwOi8vY3RzLm5sbS5uaWguZ292L2ZoaXIvVmFsdWVTZXQvMi4xNi44NDAuMS4xMTM3NjIuMS40LjEiLAogICAgICAgICAgICAiYWNjZXNzTGV2ZWwiIDogIlB1YmxpYyIKICAgICAgICAgfSwgewogICAgICAgICAgICAibG9jYXRvciIgOiAiMTQ6MS0xNDo4NCIsCiAgICAgICAgICAgICJuYW1lIiA6ICJQYXllciIsCiAgICAgICAgICAgICJpZCIgOiAiaHR0cDovL2N0cy5ubG0ubmloLmdvdi9maGlyL1ZhbHVlU2V0LzIuMTYuODQwLjEuMTE0MjIyLjQuMTEuMzU5MSIsCiAgICAgICAgICAgICJhY2Nlc3NMZXZlbCIgOiAiUHVibGljIgogICAgICAgICB9LCB7CiAgICAgICAgICAgICJsb2NhdG9yIiA6ICIxNToxLTE1OjgyIiwKICAgICAgICAgICAgIm5hbWUiIDogIlJhY2UiLAogICAgICAgICAgICAiaWQiIDogImh0dHA6Ly9jdHMubmxtLm5paC5nb3YvZmhpci9WYWx1ZVNldC8yLjE2Ljg0MC4xLjExNDIyMi40LjExLjgzNiIsCiAgICAgICAgICAgICJhY2Nlc3NMZXZlbCIgOiAiUHVibGljIgogICAgICAgICB9IF0KICAgICAgfSwKICAgICAgImNvbnRleHRzIiA6IHsKICAgICAgICAgImRlZiIgOiBbIHsKICAgICAgICAgICAgImxvY2F0b3IiIDogIjE4OjEtMTg6MTUiLAogICAgICAgICAgICAibmFtZSIgOiAiUGF0aWVudCIKICAgICAgICAgfSBdCiAgICAgIH0sCiAgICAgICJzdGF0ZW1lbnRzIiA6IHsKICAgICAgICAgImRlZiIgOiBbIHsKICAgICAgICAgICAgImxvY2F0b3IiIDogIjE4OjEtMTg6MTUiLAogICAgICAgICAgICAibmFtZSIgOiAiUGF0aWVudCIsCiAgICAgICAgICAgICJjb250ZXh0IiA6ICJQYXRpZW50IiwKICAgICAgICAgICAgImV4cHJlc3Npb24iIDogewogICAgICAgICAgICAgICAidHlwZSIgOiAiU2luZ2xldG9uRnJvbSIsCiAgICAgICAgICAgICAgICJvcGVyYW5kIiA6IHsKICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjE4OjEtMTg6MTUiLAogICAgICAgICAgICAgICAgICAiZGF0YVR5cGUiIDogIntodHRwOi8vaGw3Lm9yZy9maGlyfVBhdGllbnQiLAogICAgICAgICAgICAgICAgICAidGVtcGxhdGVJZCIgOiAiaHR0cDovL2hsNy5vcmcvZmhpci9TdHJ1Y3R1cmVEZWZpbml0aW9uL1BhdGllbnQiLAogICAgICAgICAgICAgICAgICAidHlwZSIgOiAiUmV0cmlldmUiCiAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgfQogICAgICAgICB9LCB7CiAgICAgICAgICAgICJsb2NhdG9yIiA6ICIyMDoxLTI4OjQwIiwKICAgICAgICAgICAgIm5hbWUiIDogIlNERSBFdGhuaWNpdHkiLAogICAgICAgICAgICAiY29udGV4dCIgOiAiUGF0aWVudCIsCiAgICAgICAgICAgICJhY2Nlc3NMZXZlbCIgOiAiUHVibGljIiwKICAgICAgICAgICAgImV4cHJlc3Npb24iIDogewogICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMjE6My0yODo0MCIsCiAgICAgICAgICAgICAgICJ0eXBlIiA6ICJRdWVyeSIsCiAgICAgICAgICAgICAgICJzb3VyY2UiIDogWyB7CiAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIyMTozLTI1OjgiLAogICAgICAgICAgICAgICAgICAiYWxpYXMiIDogIkUiLAogICAgICAgICAgICAgICAgICAiZXhwcmVzc2lvbiIgOiB7CiAgICAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIyMTozLTI1OjYiLAogICAgICAgICAgICAgICAgICAgICAidHlwZSIgOiAiRmxhdHRlbiIsCiAgICAgICAgICAgICAgICAgICAgICJvcGVyYW5kIiA6IHsKICAgICAgICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjIxOjEyLTI1OjUiLAogICAgICAgICAgICAgICAgICAgICAgICAidHlwZSIgOiAiUXVlcnkiLAogICAgICAgICAgICAgICAgICAgICAgICAic291cmNlIiA6IFsgewogICAgICAgICAgICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMjI6Ny0yMjozMyIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICJhbGlhcyIgOiAiRXh0ZW5zaW9uIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgImV4cHJlc3Npb24iIDogewogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMjI6Ny0yMjoyMyIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJwYXRoIiA6ICJleHRlbnNpb24iLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAidHlwZSIgOiAiUHJvcGVydHkiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAic291cmNlIiA6IHsKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIm5hbWUiIDogIlBhdGllbnQiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAidHlwZSIgOiAiRXhwcmVzc2lvblJlZiIKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfQogICAgICAgICAgICAgICAgICAgICAgICAgICB9CiAgICAgICAgICAgICAgICAgICAgICAgIH0gXSwKICAgICAgICAgICAgICAgICAgICAgICAgInJlbGF0aW9uc2hpcCIgOiBbIF0sCiAgICAgICAgICAgICAgICAgICAgICAgICJ3aGVyZSIgOiB7CiAgICAgICAgICAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIyMzo5LTIzOjk3IiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgInR5cGUiIDogIkVxdWFsIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgIm9wZXJhbmQiIDogWyB7CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJUb1N0cmluZyIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJsaWJyYXJ5TmFtZSIgOiAiRkhJUkhlbHBlcnMiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAidHlwZSIgOiAiRnVuY3Rpb25SZWYiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAib3BlcmFuZCIgOiBbIHsKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjIzOjE1LTIzOjI3IiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInBhdGgiIDogInVybCIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJzY29wZSIgOiAiRXh0ZW5zaW9uIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInR5cGUiIDogIlByb3BlcnR5IgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICB9IF0KICAgICAgICAgICAgICAgICAgICAgICAgICAgfSwgewogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMjM6MzEtMjM6OTciLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAidmFsdWVUeXBlIiA6ICJ7dXJuOmhsNy1vcmc6ZWxtLXR5cGVzOnIxfVN0cmluZyIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJ2YWx1ZSIgOiAiaHR0cDovL2hsNy5vcmcvZmhpci91cy9jb3JlL1N0cnVjdHVyZURlZmluaXRpb24vdXMtY29yZS1ldGhuaWNpdHkiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAidHlwZSIgOiAiTGl0ZXJhbCIKICAgICAgICAgICAgICAgICAgICAgICAgICAgfSBdCiAgICAgICAgICAgICAgICAgICAgICAgIH0sCiAgICAgICAgICAgICAgICAgICAgICAgICJyZXR1cm4iIDogewogICAgICAgICAgICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMjQ6MTEtMjQ6MzYiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAiZXhwcmVzc2lvbiIgOiB7CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIyNDoxOC0yNDozNiIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJwYXRoIiA6ICJleHRlbnNpb24iLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAic2NvcGUiIDogIkV4dGVuc2lvbiIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJQcm9wZXJ0eSIKICAgICAgICAgICAgICAgICAgICAgICAgICAgfQogICAgICAgICAgICAgICAgICAgICAgICB9CiAgICAgICAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgICAgICAgfQogICAgICAgICAgICAgICB9IF0sCiAgICAgICAgICAgICAgICJyZWxhdGlvbnNoaXAiIDogWyBdLAogICAgICAgICAgICAgICAid2hlcmUiIDogewogICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMjY6Ny0yNzoyOSIsCiAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJPciIsCiAgICAgICAgICAgICAgICAgICJvcGVyYW5kIiA6IFsgewogICAgICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMjY6MTMtMjY6MzMiLAogICAgICAgICAgICAgICAgICAgICAidHlwZSIgOiAiRXF1YWwiLAogICAgICAgICAgICAgICAgICAgICAib3BlcmFuZCIgOiBbIHsKICAgICAgICAgICAgICAgICAgICAgICAgIm5hbWUiIDogIlRvU3RyaW5nIiwKICAgICAgICAgICAgICAgICAgICAgICAgImxpYnJhcnlOYW1lIiA6ICJGSElSSGVscGVycyIsCiAgICAgICAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJGdW5jdGlvblJlZiIsCiAgICAgICAgICAgICAgICAgICAgICAgICJvcGVyYW5kIiA6IFsgewogICAgICAgICAgICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMjY6MTMtMjY6MTciLAogICAgICAgICAgICAgICAgICAgICAgICAgICAicGF0aCIgOiAidXJsIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgInNjb3BlIiA6ICJFIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgInR5cGUiIDogIlByb3BlcnR5IgogICAgICAgICAgICAgICAgICAgICAgICB9IF0KICAgICAgICAgICAgICAgICAgICAgfSwgewogICAgICAgICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMjY6MjEtMjY6MzMiLAogICAgICAgICAgICAgICAgICAgICAgICAidmFsdWVUeXBlIiA6ICJ7dXJuOmhsNy1vcmc6ZWxtLXR5cGVzOnIxfVN0cmluZyIsCiAgICAgICAgICAgICAgICAgICAgICAgICJ2YWx1ZSIgOiAib21iQ2F0ZWdvcnkiLAogICAgICAgICAgICAgICAgICAgICAgICAidHlwZSIgOiAiTGl0ZXJhbCIKICAgICAgICAgICAgICAgICAgICAgfSBdCiAgICAgICAgICAgICAgICAgIH0sIHsKICAgICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjI3OjEyLTI3OjI5IiwKICAgICAgICAgICAgICAgICAgICAgInR5cGUiIDogIkVxdWFsIiwKICAgICAgICAgICAgICAgICAgICAgIm9wZXJhbmQiIDogWyB7CiAgICAgICAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJUb1N0cmluZyIsCiAgICAgICAgICAgICAgICAgICAgICAgICJsaWJyYXJ5TmFtZSIgOiAiRkhJUkhlbHBlcnMiLAogICAgICAgICAgICAgICAgICAgICAgICAidHlwZSIgOiAiRnVuY3Rpb25SZWYiLAogICAgICAgICAgICAgICAgICAgICAgICAib3BlcmFuZCIgOiBbIHsKICAgICAgICAgICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjI3OjEyLTI3OjE2IiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgInBhdGgiIDogInVybCIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICJzY29wZSIgOiAiRSIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJQcm9wZXJ0eSIKICAgICAgICAgICAgICAgICAgICAgICAgfSBdCiAgICAgICAgICAgICAgICAgICAgIH0sIHsKICAgICAgICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjI3OjIwLTI3OjI5IiwKICAgICAgICAgICAgICAgICAgICAgICAgInZhbHVlVHlwZSIgOiAie3VybjpobDctb3JnOmVsbS10eXBlczpyMX1TdHJpbmciLAogICAgICAgICAgICAgICAgICAgICAgICAidmFsdWUiIDogImRldGFpbGVkIiwKICAgICAgICAgICAgICAgICAgICAgICAgInR5cGUiIDogIkxpdGVyYWwiCiAgICAgICAgICAgICAgICAgICAgIH0gXQogICAgICAgICAgICAgICAgICB9IF0KICAgICAgICAgICAgICAgfSwKICAgICAgICAgICAgICAgInJldHVybiIgOiB7CiAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIyODo3LTI4OjQwIiwKICAgICAgICAgICAgICAgICAgImV4cHJlc3Npb24iIDogewogICAgICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMjg6MTQtMjg6NDAiLAogICAgICAgICAgICAgICAgICAgICAibmFtZSIgOiAiVG9Db2RlIiwKICAgICAgICAgICAgICAgICAgICAgImxpYnJhcnlOYW1lIiA6ICJGSElSSGVscGVycyIsCiAgICAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJGdW5jdGlvblJlZiIsCiAgICAgICAgICAgICAgICAgICAgICJvcGVyYW5kIiA6IFsgewogICAgICAgICAgICAgICAgICAgICAgICAiYXNUeXBlIiA6ICJ7aHR0cDovL2hsNy5vcmcvZmhpcn1Db2RpbmciLAogICAgICAgICAgICAgICAgICAgICAgICAidHlwZSIgOiAiQXMiLAogICAgICAgICAgICAgICAgICAgICAgICAib3BlcmFuZCIgOiB7CiAgICAgICAgICAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIyODozMy0yODozOSIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICJwYXRoIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICJzY29wZSIgOiAiRSIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJQcm9wZXJ0eSIKICAgICAgICAgICAgICAgICAgICAgICAgfQogICAgICAgICAgICAgICAgICAgICB9IF0KICAgICAgICAgICAgICAgICAgfQogICAgICAgICAgICAgICB9CiAgICAgICAgICAgIH0KICAgICAgICAgfSwgewogICAgICAgICAgICAibG9jYXRvciIgOiAiMzA6MS0zNzozIiwKICAgICAgICAgICAgIm5hbWUiIDogIlNERSBQYXllciIsCiAgICAgICAgICAgICJjb250ZXh0IiA6ICJQYXRpZW50IiwKICAgICAgICAgICAgImFjY2Vzc0xldmVsIiA6ICJQdWJsaWMiLAogICAgICAgICAgICAiZXhwcmVzc2lvbiIgOiB7CiAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIzMTozLTM3OjMiLAogICAgICAgICAgICAgICAidHlwZSIgOiAiRmxhdHRlbiIsCiAgICAgICAgICAgICAgICJvcGVyYW5kIiA6IHsKICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjMxOjEwLTM3OjMiLAogICAgICAgICAgICAgICAgICAidHlwZSIgOiAiUXVlcnkiLAogICAgICAgICAgICAgICAgICAic291cmNlIiA6IFsgewogICAgICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMzE6MTEtMzE6NDMiLAogICAgICAgICAgICAgICAgICAgICAiYWxpYXMiIDogIlBheWVyIiwKICAgICAgICAgICAgICAgICAgICAgImV4cHJlc3Npb24iIDogewogICAgICAgICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMzE6MTEtMzE6MzciLAogICAgICAgICAgICAgICAgICAgICAgICAiZGF0YVR5cGUiIDogIntodHRwOi8vaGw3Lm9yZy9maGlyfUNvdmVyYWdlIiwKICAgICAgICAgICAgICAgICAgICAgICAgInRlbXBsYXRlSWQiIDogImh0dHA6Ly9obDcub3JnL2ZoaXIvU3RydWN0dXJlRGVmaW5pdGlvbi9Db3ZlcmFnZSIsCiAgICAgICAgICAgICAgICAgICAgICAgICJjb2RlUHJvcGVydHkiIDogInR5cGUiLAogICAgICAgICAgICAgICAgICAgICAgICAiY29kZUNvbXBhcmF0b3IiIDogImluIiwKICAgICAgICAgICAgICAgICAgICAgICAgInR5cGUiIDogIlJldHJpZXZlIiwKICAgICAgICAgICAgICAgICAgICAgICAgImNvZGVzIiA6IHsKICAgICAgICAgICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjMxOjMwLTMxOjM2IiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgIm5hbWUiIDogIlBheWVyIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgInByZXNlcnZlIiA6IHRydWUsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJWYWx1ZVNldFJlZiIKICAgICAgICAgICAgICAgICAgICAgICAgfQogICAgICAgICAgICAgICAgICAgICB9CiAgICAgICAgICAgICAgICAgIH0gXSwKICAgICAgICAgICAgICAgICAgInJlbGF0aW9uc2hpcCIgOiBbIF0sCiAgICAgICAgICAgICAgICAgICJ3aGVyZSIgOiB7CiAgICAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIzMjo1LTMyOjUyIiwKICAgICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk92ZXJsYXBzIiwKICAgICAgICAgICAgICAgICAgICAgIm9wZXJhbmQiIDogWyB7CiAgICAgICAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJUb0ludGVydmFsIiwKICAgICAgICAgICAgICAgICAgICAgICAgImxpYnJhcnlOYW1lIiA6ICJGSElSSGVscGVycyIsCiAgICAgICAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJGdW5jdGlvblJlZiIsCiAgICAgICAgICAgICAgICAgICAgICAgICJvcGVyYW5kIiA6IFsgewogICAgICAgICAgICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMzI6MTEtMzI6MjIiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAicGF0aCIgOiAicGVyaW9kIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgInNjb3BlIiA6ICJQYXllciIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJQcm9wZXJ0eSIKICAgICAgICAgICAgICAgICAgICAgICAgfSBdCiAgICAgICAgICAgICAgICAgICAgIH0sIHsKICAgICAgICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjMyOjMzLTMyOjUyIiwKICAgICAgICAgICAgICAgICAgICAgICAgIm5hbWUiIDogIk1lYXN1cmVtZW50IFBlcmlvZCIsCiAgICAgICAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJQYXJhbWV0ZXJSZWYiCiAgICAgICAgICAgICAgICAgICAgIH0gXQogICAgICAgICAgICAgICAgICB9LAogICAgICAgICAgICAgICAgICAicmV0dXJuIiA6IHsKICAgICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjMzOjctMzY6NyIsCiAgICAgICAgICAgICAgICAgICAgICJleHByZXNzaW9uIiA6IHsKICAgICAgICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjMzOjE0LTM2OjciLAogICAgICAgICAgICAgICAgICAgICAgICAidHlwZSIgOiAiUXVlcnkiLAogICAgICAgICAgICAgICAgICAgICAgICAic291cmNlIiA6IFsgewogICAgICAgICAgICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMzQ6OS0zNDoyNyIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICJhbGlhcyIgOiAiYyIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICJleHByZXNzaW9uIiA6IHsKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjM0OjktMzQ6MjUiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAicGF0aCIgOiAiY29kaW5nIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInR5cGUiIDogIlByb3BlcnR5IiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInNvdXJjZSIgOiB7CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJwYXRoIiA6ICJ0eXBlIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInNjb3BlIiA6ICJQYXllciIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJQcm9wZXJ0eSIKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfQogICAgICAgICAgICAgICAgICAgICAgICAgICB9CiAgICAgICAgICAgICAgICAgICAgICAgIH0gXSwKICAgICAgICAgICAgICAgICAgICAgICAgInJlbGF0aW9uc2hpcCIgOiBbIF0sCiAgICAgICAgICAgICAgICAgICAgICAgICJyZXR1cm4iIDogewogICAgICAgICAgICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMzU6MTEtMzU6MzgiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAiZXhwcmVzc2lvbiIgOiB7CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIzNToxOC0zNTozOCIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJUb0NvZGUiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAibGlicmFyeU5hbWUiIDogIkZISVJIZWxwZXJzIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInR5cGUiIDogIkZ1bmN0aW9uUmVmIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIm9wZXJhbmQiIDogWyB7CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIzNTozNyIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJjIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInR5cGUiIDogIkFsaWFzUmVmIgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICB9IF0KICAgICAgICAgICAgICAgICAgICAgICAgICAgfQogICAgICAgICAgICAgICAgICAgICAgICB9CiAgICAgICAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgICAgICAgfQogICAgICAgICAgICAgICB9CiAgICAgICAgICAgIH0KICAgICAgICAgfSwgewogICAgICAgICAgICAibG9jYXRvciIgOiAiMzk6MS00Nzo0MCIsCiAgICAgICAgICAgICJuYW1lIiA6ICJTREUgUmFjZSIsCiAgICAgICAgICAgICJjb250ZXh0IiA6ICJQYXRpZW50IiwKICAgICAgICAgICAgImFjY2Vzc0xldmVsIiA6ICJQdWJsaWMiLAogICAgICAgICAgICAiZXhwcmVzc2lvbiIgOiB7CiAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICI0MDozLTQ3OjQwIiwKICAgICAgICAgICAgICAgInR5cGUiIDogIlF1ZXJ5IiwKICAgICAgICAgICAgICAgInNvdXJjZSIgOiBbIHsKICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjQwOjMtNDQ6OCIsCiAgICAgICAgICAgICAgICAgICJhbGlhcyIgOiAiRSIsCiAgICAgICAgICAgICAgICAgICJleHByZXNzaW9uIiA6IHsKICAgICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjQwOjMtNDQ6NiIsCiAgICAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJGbGF0dGVuIiwKICAgICAgICAgICAgICAgICAgICAgIm9wZXJhbmQiIDogewogICAgICAgICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiNDA6MTItNDQ6NSIsCiAgICAgICAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJRdWVyeSIsCiAgICAgICAgICAgICAgICAgICAgICAgICJzb3VyY2UiIDogWyB7CiAgICAgICAgICAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICI0MTo3LTQxOjMzIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgImFsaWFzIiA6ICJFeHRlbnNpb24iLAogICAgICAgICAgICAgICAgICAgICAgICAgICAiZXhwcmVzc2lvbiIgOiB7CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICI0MTo3LTQxOjIzIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInBhdGgiIDogImV4dGVuc2lvbiIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJQcm9wZXJ0eSIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJzb3VyY2UiIDogewogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAibmFtZSIgOiAiUGF0aWVudCIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJFeHByZXNzaW9uUmVmIgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICB9CiAgICAgICAgICAgICAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgICAgICAgICAgICAgfSBdLAogICAgICAgICAgICAgICAgICAgICAgICAicmVsYXRpb25zaGlwIiA6IFsgXSwKICAgICAgICAgICAgICAgICAgICAgICAgIndoZXJlIiA6IHsKICAgICAgICAgICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjQyOjktNDI6OTIiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAidHlwZSIgOiAiRXF1YWwiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAib3BlcmFuZCIgOiBbIHsKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIm5hbWUiIDogIlRvU3RyaW5nIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgImxpYnJhcnlOYW1lIiA6ICJGSElSSGVscGVycyIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJGdW5jdGlvblJlZiIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJvcGVyYW5kIiA6IFsgewogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiNDI6MTUtNDI6MjciLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAicGF0aCIgOiAidXJsIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInNjb3BlIiA6ICJFeHRlbnNpb24iLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAidHlwZSIgOiAiUHJvcGVydHkiCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIH0gXQogICAgICAgICAgICAgICAgICAgICAgICAgICB9LCB7CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICI0MjozMS00Mjo5MiIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJ2YWx1ZVR5cGUiIDogInt1cm46aGw3LW9yZzplbG0tdHlwZXM6cjF9U3RyaW5nIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInZhbHVlIiA6ICJodHRwOi8vaGw3Lm9yZy9maGlyL3VzL2NvcmUvU3RydWN0dXJlRGVmaW5pdGlvbi91cy1jb3JlLXJhY2UiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAidHlwZSIgOiAiTGl0ZXJhbCIKICAgICAgICAgICAgICAgICAgICAgICAgICAgfSBdCiAgICAgICAgICAgICAgICAgICAgICAgIH0sCiAgICAgICAgICAgICAgICAgICAgICAgICJyZXR1cm4iIDogewogICAgICAgICAgICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiNDM6MTEtNDM6MzYiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAiZXhwcmVzc2lvbiIgOiB7CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICI0MzoxOC00MzozNiIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJwYXRoIiA6ICJleHRlbnNpb24iLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAic2NvcGUiIDogIkV4dGVuc2lvbiIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJQcm9wZXJ0eSIKICAgICAgICAgICAgICAgICAgICAgICAgICAgfQogICAgICAgICAgICAgICAgICAgICAgICB9CiAgICAgICAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgICAgICAgfQogICAgICAgICAgICAgICB9IF0sCiAgICAgICAgICAgICAgICJyZWxhdGlvbnNoaXAiIDogWyBdLAogICAgICAgICAgICAgICAid2hlcmUiIDogewogICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiNDU6Ny00NjoyOSIsCiAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJPciIsCiAgICAgICAgICAgICAgICAgICJvcGVyYW5kIiA6IFsgewogICAgICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiNDU6MTMtNDU6MzMiLAogICAgICAgICAgICAgICAgICAgICAidHlwZSIgOiAiRXF1YWwiLAogICAgICAgICAgICAgICAgICAgICAib3BlcmFuZCIgOiBbIHsKICAgICAgICAgICAgICAgICAgICAgICAgIm5hbWUiIDogIlRvU3RyaW5nIiwKICAgICAgICAgICAgICAgICAgICAgICAgImxpYnJhcnlOYW1lIiA6ICJGSElSSGVscGVycyIsCiAgICAgICAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJGdW5jdGlvblJlZiIsCiAgICAgICAgICAgICAgICAgICAgICAgICJvcGVyYW5kIiA6IFsgewogICAgICAgICAgICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiNDU6MTMtNDU6MTciLAogICAgICAgICAgICAgICAgICAgICAgICAgICAicGF0aCIgOiAidXJsIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgInNjb3BlIiA6ICJFIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgInR5cGUiIDogIlByb3BlcnR5IgogICAgICAgICAgICAgICAgICAgICAgICB9IF0KICAgICAgICAgICAgICAgICAgICAgfSwgewogICAgICAgICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiNDU6MjEtNDU6MzMiLAogICAgICAgICAgICAgICAgICAgICAgICAidmFsdWVUeXBlIiA6ICJ7dXJuOmhsNy1vcmc6ZWxtLXR5cGVzOnIxfVN0cmluZyIsCiAgICAgICAgICAgICAgICAgICAgICAgICJ2YWx1ZSIgOiAib21iQ2F0ZWdvcnkiLAogICAgICAgICAgICAgICAgICAgICAgICAidHlwZSIgOiAiTGl0ZXJhbCIKICAgICAgICAgICAgICAgICAgICAgfSBdCiAgICAgICAgICAgICAgICAgIH0sIHsKICAgICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjQ2OjEyLTQ2OjI5IiwKICAgICAgICAgICAgICAgICAgICAgInR5cGUiIDogIkVxdWFsIiwKICAgICAgICAgICAgICAgICAgICAgIm9wZXJhbmQiIDogWyB7CiAgICAgICAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJUb1N0cmluZyIsCiAgICAgICAgICAgICAgICAgICAgICAgICJsaWJyYXJ5TmFtZSIgOiAiRkhJUkhlbHBlcnMiLAogICAgICAgICAgICAgICAgICAgICAgICAidHlwZSIgOiAiRnVuY3Rpb25SZWYiLAogICAgICAgICAgICAgICAgICAgICAgICAib3BlcmFuZCIgOiBbIHsKICAgICAgICAgICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjQ2OjEyLTQ2OjE2IiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgInBhdGgiIDogInVybCIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICJzY29wZSIgOiAiRSIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJQcm9wZXJ0eSIKICAgICAgICAgICAgICAgICAgICAgICAgfSBdCiAgICAgICAgICAgICAgICAgICAgIH0sIHsKICAgICAgICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjQ2OjIwLTQ2OjI5IiwKICAgICAgICAgICAgICAgICAgICAgICAgInZhbHVlVHlwZSIgOiAie3VybjpobDctb3JnOmVsbS10eXBlczpyMX1TdHJpbmciLAogICAgICAgICAgICAgICAgICAgICAgICAidmFsdWUiIDogImRldGFpbGVkIiwKICAgICAgICAgICAgICAgICAgICAgICAgInR5cGUiIDogIkxpdGVyYWwiCiAgICAgICAgICAgICAgICAgICAgIH0gXQogICAgICAgICAgICAgICAgICB9IF0KICAgICAgICAgICAgICAgfSwKICAgICAgICAgICAgICAgInJldHVybiIgOiB7CiAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICI0Nzo3LTQ3OjQwIiwKICAgICAgICAgICAgICAgICAgImV4cHJlc3Npb24iIDogewogICAgICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiNDc6MTQtNDc6NDAiLAogICAgICAgICAgICAgICAgICAgICAibmFtZSIgOiAiVG9Db2RlIiwKICAgICAgICAgICAgICAgICAgICAgImxpYnJhcnlOYW1lIiA6ICJGSElSSGVscGVycyIsCiAgICAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJGdW5jdGlvblJlZiIsCiAgICAgICAgICAgICAgICAgICAgICJvcGVyYW5kIiA6IFsgewogICAgICAgICAgICAgICAgICAgICAgICAiYXNUeXBlIiA6ICJ7aHR0cDovL2hsNy5vcmcvZmhpcn1Db2RpbmciLAogICAgICAgICAgICAgICAgICAgICAgICAidHlwZSIgOiAiQXMiLAogICAgICAgICAgICAgICAgICAgICAgICAib3BlcmFuZCIgOiB7CiAgICAgICAgICAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICI0NzozMy00NzozOSIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICJwYXRoIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICJzY29wZSIgOiAiRSIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJQcm9wZXJ0eSIKICAgICAgICAgICAgICAgICAgICAgICAgfQogICAgICAgICAgICAgICAgICAgICB9IF0KICAgICAgICAgICAgICAgICAgfQogICAgICAgICAgICAgICB9CiAgICAgICAgICAgIH0KICAgICAgICAgfSwgewogICAgICAgICAgICAibG9jYXRvciIgOiAiNDk6MS01NDo3IiwKICAgICAgICAgICAgIm5hbWUiIDogIlNERSBTZXgiLAogICAgICAgICAgICAiY29udGV4dCIgOiAiUGF0aWVudCIsCiAgICAgICAgICAgICJhY2Nlc3NMZXZlbCIgOiAiUHVibGljIiwKICAgICAgICAgICAgImV4cHJlc3Npb24iIDogewogICAgICAgICAgICAgICAibG9jYXRvciIgOiAiNTA6My01NDo3IiwKICAgICAgICAgICAgICAgInR5cGUiIDogIkNhc2UiLAogICAgICAgICAgICAgICAiY2FzZUl0ZW0iIDogWyB7CiAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICI1MTo3LTUxOjEzMCIsCiAgICAgICAgICAgICAgICAgICJ3aGVuIiA6IHsKICAgICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjUxOjEyLTUxOjM0IiwKICAgICAgICAgICAgICAgICAgICAgInR5cGUiIDogIkVxdWFsIiwKICAgICAgICAgICAgICAgICAgICAgIm9wZXJhbmQiIDogWyB7CiAgICAgICAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJUb1N0cmluZyIsCiAgICAgICAgICAgICAgICAgICAgICAgICJsaWJyYXJ5TmFtZSIgOiAiRkhJUkhlbHBlcnMiLAogICAgICAgICAgICAgICAgICAgICAgICAidHlwZSIgOiAiRnVuY3Rpb25SZWYiLAogICAgICAgICAgICAgICAgICAgICAgICAib3BlcmFuZCIgOiBbIHsKICAgICAgICAgICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjUxOjEyLTUxOjI1IiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgInBhdGgiIDogImdlbmRlciIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJQcm9wZXJ0eSIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICJzb3VyY2UiIDogewogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiNTE6MTItNTE6MTgiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAibmFtZSIgOiAiUGF0aWVudCIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJFeHByZXNzaW9uUmVmIgogICAgICAgICAgICAgICAgICAgICAgICAgICB9CiAgICAgICAgICAgICAgICAgICAgICAgIH0gXQogICAgICAgICAgICAgICAgICAgICB9LCB7CiAgICAgICAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICI1MToyOS01MTozNCIsCiAgICAgICAgICAgICAgICAgICAgICAgICJ2YWx1ZVR5cGUiIDogInt1cm46aGw3LW9yZzplbG0tdHlwZXM6cjF9U3RyaW5nIiwKICAgICAgICAgICAgICAgICAgICAgICAgInZhbHVlIiA6ICJtYWxlIiwKICAgICAgICAgICAgICAgICAgICAgICAgInR5cGUiIDogIkxpdGVyYWwiCiAgICAgICAgICAgICAgICAgICAgIH0gXQogICAgICAgICAgICAgICAgICB9LAogICAgICAgICAgICAgICAgICAidGhlbiIgOiB7CiAgICAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICI1MTo0MS01MToxMzAiLAogICAgICAgICAgICAgICAgICAgICAiY2xhc3NUeXBlIiA6ICJ7dXJuOmhsNy1vcmc6ZWxtLXR5cGVzOnIxfUNvZGUiLAogICAgICAgICAgICAgICAgICAgICAidHlwZSIgOiAiSW5zdGFuY2UiLAogICAgICAgICAgICAgICAgICAgICAiZWxlbWVudCIgOiBbIHsKICAgICAgICAgICAgICAgICAgICAgICAgIm5hbWUiIDogImNvZGUiLAogICAgICAgICAgICAgICAgICAgICAgICAidmFsdWUiIDogewogICAgICAgICAgICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiNTE6NTQtNTE6NTYiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAidmFsdWVUeXBlIiA6ICJ7dXJuOmhsNy1vcmc6ZWxtLXR5cGVzOnIxfVN0cmluZyIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICJ2YWx1ZSIgOiAiTSIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJMaXRlcmFsIgogICAgICAgICAgICAgICAgICAgICAgICB9CiAgICAgICAgICAgICAgICAgICAgIH0sIHsKICAgICAgICAgICAgICAgICAgICAgICAgIm5hbWUiIDogInN5c3RlbSIsCiAgICAgICAgICAgICAgICAgICAgICAgICJ2YWx1ZSIgOiB7CiAgICAgICAgICAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICI1MTo2Ny01MToxMTEiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAidmFsdWVUeXBlIiA6ICJ7dXJuOmhsNy1vcmc6ZWxtLXR5cGVzOnIxfVN0cmluZyIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICJ2YWx1ZSIgOiAiaHR0cDovL2hsNy5vcmcvZmhpci92My9BZG1pbmlzdHJhdGl2ZUdlbmRlciIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJMaXRlcmFsIgogICAgICAgICAgICAgICAgICAgICAgICB9CiAgICAgICAgICAgICAgICAgICAgIH0sIHsKICAgICAgICAgICAgICAgICAgICAgICAgIm5hbWUiIDogImRpc3BsYXkiLAogICAgICAgICAgICAgICAgICAgICAgICAidmFsdWUiIDogewogICAgICAgICAgICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiNTE6MTIzLTUxOjEyOCIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICJ2YWx1ZVR5cGUiIDogInt1cm46aGw3LW9yZzplbG0tdHlwZXM6cjF9U3RyaW5nIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgInZhbHVlIiA6ICJNYWxlIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgInR5cGUiIDogIkxpdGVyYWwiCiAgICAgICAgICAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgICAgICAgICAgfSBdCiAgICAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgICAgfSwgewogICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiNTI6Ny01MjoxMzQiLAogICAgICAgICAgICAgICAgICAid2hlbiIgOiB7CiAgICAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICI1MjoxMi01MjozNiIsCiAgICAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJFcXVhbCIsCiAgICAgICAgICAgICAgICAgICAgICJvcGVyYW5kIiA6IFsgewogICAgICAgICAgICAgICAgICAgICAgICAibmFtZSIgOiAiVG9TdHJpbmciLAogICAgICAgICAgICAgICAgICAgICAgICAibGlicmFyeU5hbWUiIDogIkZISVJIZWxwZXJzIiwKICAgICAgICAgICAgICAgICAgICAgICAgInR5cGUiIDogIkZ1bmN0aW9uUmVmIiwKICAgICAgICAgICAgICAgICAgICAgICAgIm9wZXJhbmQiIDogWyB7CiAgICAgICAgICAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICI1MjoxMi01MjoyNSIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICJwYXRoIiA6ICJnZW5kZXIiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAidHlwZSIgOiAiUHJvcGVydHkiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAic291cmNlIiA6IHsKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjUyOjEyLTUyOjE4IiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIm5hbWUiIDogIlBhdGllbnQiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAidHlwZSIgOiAiRXhwcmVzc2lvblJlZiIKICAgICAgICAgICAgICAgICAgICAgICAgICAgfQogICAgICAgICAgICAgICAgICAgICAgICB9IF0KICAgICAgICAgICAgICAgICAgICAgfSwgewogICAgICAgICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiNTI6MjktNTI6MzYiLAogICAgICAgICAgICAgICAgICAgICAgICAidmFsdWVUeXBlIiA6ICJ7dXJuOmhsNy1vcmc6ZWxtLXR5cGVzOnIxfVN0cmluZyIsCiAgICAgICAgICAgICAgICAgICAgICAgICJ2YWx1ZSIgOiAiZmVtYWxlIiwKICAgICAgICAgICAgICAgICAgICAgICAgInR5cGUiIDogIkxpdGVyYWwiCiAgICAgICAgICAgICAgICAgICAgIH0gXQogICAgICAgICAgICAgICAgICB9LAogICAgICAgICAgICAgICAgICAidGhlbiIgOiB7CiAgICAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICI1Mjo0My01MjoxMzQiLAogICAgICAgICAgICAgICAgICAgICAiY2xhc3NUeXBlIiA6ICJ7dXJuOmhsNy1vcmc6ZWxtLXR5cGVzOnIxfUNvZGUiLAogICAgICAgICAgICAgICAgICAgICAidHlwZSIgOiAiSW5zdGFuY2UiLAogICAgICAgICAgICAgICAgICAgICAiZWxlbWVudCIgOiBbIHsKICAgICAgICAgICAgICAgICAgICAgICAgIm5hbWUiIDogImNvZGUiLAogICAgICAgICAgICAgICAgICAgICAgICAidmFsdWUiIDogewogICAgICAgICAgICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiNTI6NTYtNTI6NTgiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAidmFsdWVUeXBlIiA6ICJ7dXJuOmhsNy1vcmc6ZWxtLXR5cGVzOnIxfVN0cmluZyIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICJ2YWx1ZSIgOiAiRiIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJMaXRlcmFsIgogICAgICAgICAgICAgICAgICAgICAgICB9CiAgICAgICAgICAgICAgICAgICAgIH0sIHsKICAgICAgICAgICAgICAgICAgICAgICAgIm5hbWUiIDogInN5c3RlbSIsCiAgICAgICAgICAgICAgICAgICAgICAgICJ2YWx1ZSIgOiB7CiAgICAgICAgICAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICI1Mjo2OS01MjoxMTMiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAidmFsdWVUeXBlIiA6ICJ7dXJuOmhsNy1vcmc6ZWxtLXR5cGVzOnIxfVN0cmluZyIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICJ2YWx1ZSIgOiAiaHR0cDovL2hsNy5vcmcvZmhpci92My9BZG1pbmlzdHJhdGl2ZUdlbmRlciIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJMaXRlcmFsIgogICAgICAgICAgICAgICAgICAgICAgICB9CiAgICAgICAgICAgICAgICAgICAgIH0sIHsKICAgICAgICAgICAgICAgICAgICAgICAgIm5hbWUiIDogImRpc3BsYXkiLAogICAgICAgICAgICAgICAgICAgICAgICAidmFsdWUiIDogewogICAgICAgICAgICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiNTI6MTI1LTUyOjEzMiIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICJ2YWx1ZVR5cGUiIDogInt1cm46aGw3LW9yZzplbG0tdHlwZXM6cjF9U3RyaW5nIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgInZhbHVlIiA6ICJGZW1hbGUiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAidHlwZSIgOiAiTGl0ZXJhbCIKICAgICAgICAgICAgICAgICAgICAgICAgfQogICAgICAgICAgICAgICAgICAgICB9IF0KICAgICAgICAgICAgICAgICAgfQogICAgICAgICAgICAgICB9IF0sCiAgICAgICAgICAgICAgICJlbHNlIiA6IHsKICAgICAgICAgICAgICAgICAgImFzVHlwZSIgOiAie3VybjpobDctb3JnOmVsbS10eXBlczpyMX1Db2RlIiwKICAgICAgICAgICAgICAgICAgInR5cGUiIDogIkFzIiwKICAgICAgICAgICAgICAgICAgIm9wZXJhbmQiIDogewogICAgICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiNTM6MTItNTM6MTUiLAogICAgICAgICAgICAgICAgICAgICAidHlwZSIgOiAiTnVsbCIKICAgICAgICAgICAgICAgICAgfQogICAgICAgICAgICAgICB9CiAgICAgICAgICAgIH0KICAgICAgICAgfSBdCiAgICAgIH0KICAgfQp9" - } - ] - }, - "request": { - "method": "PUT", - "url": "Library/SDE" - } - }, - { - "resource": { - "resourceType": "ValueSet", - "id": "2.16.840.1.113883.3.526.3.378", - "meta": { - "profile": [ - "http://hl7.org/fhir/uv/cpg/StructureDefinition/cpg-shareablevalueset", - "http://hl7.org/fhir/uv/cpg/StructureDefinition/cpg-publishablevalueset", - "http://hl7.org/fhir/uv/cpg/StructureDefinition/cpg-executablevalueset" - ] - }, - "extension": [ - { - "url": "http://hl7.org/fhir/uv/cpg/StructureDefinition/cpg-knowledgeCapability", - "valueCode": "shareable" - }, - { - "url": "http://hl7.org/fhir/uv/cpg/StructureDefinition/cpg-knowledgeRepresentationLevel", - "valueCode": "narrative" - }, - { - "url": "http://hl7.org/fhir/uv/cpg/StructureDefinition/cpg-knowledgeCapability", - "valueCode": "publishable" - }, - { - "url": "http://fhir.org/guides/cdc/opioid-cds/StructureDefinition/cdc-valueset-inclusion", - "valueString": "Includes concepts that represent a diagnosis of pregnancy." - }, - { - "url": "http://fhir.org/guides/cdc/opioid-cds/StructureDefinition/cdc-valueset-exclusion", - "valueString": "No exclusions." - }, - { - "url": "http://hl7.org/fhir/uv/cpg/StructureDefinition/cpg-knowledgeCapability", - "valueCode": "executable" - }, - { - "url": "http://hl7.org/fhir/uv/cpg/StructureDefinition/cpg-knowledgeRepresentationLevel", - "valueCode": "executable" - }, - { - "url": "http://hl7.org/fhir/uv/cpg/StructureDefinition/cpg-usageWarning", - "valueString": "This value set contains a point-in-time expansion enumerating the codes that meet the value set intent. As new versions of the code systems used by the value set are released, the contents of this expansion will need to be updated to incorporate newly defined codes that meet the value set intent. Before, and periodically during production use, the value set expansion contents SHOULD be updated. The value set expansion specifies the timestamp when the expansion was produced, SHOULD contain the parameters used for the expansion, and SHALL contain the codes that are obtained by evaluating the value set definition. If this is ONLY an executable value set, a distributable definition of the value set must be obtained to compute the updated expansion." - } - ], - "url": "http://cts.nlm.nih.gov/fhir/ValueSet/2.16.840.1.113883.3.526.3.378", - "version": "20210218", - "title": "Pregnancy", - "status": "active", - "experimental": false, - "publisher": "American Heart Association, Inc.", - "description": "The purpose of this value set is to represent concepts for diagnoses of pregnancy.", - "purpose": "This value set may use a model element related to Diagnosis.", - "expansion": { - "timestamp": "2023-01-02T15:36:29-07:00", - "contains": [ - { - "system": "http://snomed.info/sct", - "version": "2022-09", - "code": "10231000132102", - "display": "In-vitro fertilization pregnancy (finding)" - }, - { - "system": "http://snomed.info/sct", - "version": "2022-09", - "code": "102872000", - "display": "Pregnancy on oral contraceptive (finding)" - }, - { - "system": "http://snomed.info/sct", - "version": "2022-09", - "code": "102875003", - "display": "Surrogate pregnancy (finding)" - }, - { - "system": "http://snomed.info/sct", - "version": "2022-09", - "code": "11082009", - "display": "Abnormal pregnancy (finding)" - }, - { - "system": "http://snomed.info/sct", - "version": "2022-09", - "code": "127364007", - "display": "Primigravida (finding)" - }, - { - "system": "http://snomed.info/sct", - "version": "2022-09", - "code": "134781000119106", - "display": "High risk pregnancy due to recurrent miscarriage (finding)" - }, - { - "system": "http://snomed.info/sct", - "version": "2022-09", - "code": "14080002", - "display": "Illegitimate pregnancy, life event (finding)" - }, - { - "system": "http://snomed.info/sct", - "version": "2022-09", - "code": "14418008", - "display": "Precocious pregnancy (finding)" - }, - { - "system": "http://snomed.info/sct", - "version": "2022-09", - "code": "16356006", - "display": "Multiple pregnancy (disorder)" - }, - { - "system": "http://snomed.info/sct", - "version": "2022-09", - "code": "169561007", - "display": "Pregnant - blood test confirms (finding)" - }, - { - "system": "http://snomed.info/sct", - "version": "2022-09", - "code": "169562000", - "display": "Pregnant - vaginal examination confirms (finding)" - }, - { - "system": "http://snomed.info/sct", - "version": "2022-09", - "code": "169563005", - "display": "Pregnant - on history (finding)" - }, - { - "system": "http://snomed.info/sct", - "version": "2022-09", - "code": "169564004", - "display": "Pregnant - on abdominal palpation (finding)" - }, - { - "system": "http://snomed.info/sct", - "version": "2022-09", - "code": "169565003", - "display": "Pregnant - planned (finding)" - }, - { - "system": "http://snomed.info/sct", - "version": "2022-09", - "code": "169566002", - "display": "Pregnancy unplanned but wanted (finding)" - }, - { - "system": "http://snomed.info/sct", - "version": "2022-09", - "code": "169567006", - "display": "Pregnancy unplanned and unwanted (finding)" - }, - { - "system": "http://snomed.info/sct", - "version": "2022-09", - "code": "169568001", - "display": "Unplanned pregnancy unknown if child is wanted (finding)" - }, - { - "system": "http://snomed.info/sct", - "version": "2022-09", - "code": "199064003", - "display": "Post-term pregnancy - not delivered (disorder)" - }, - { - "system": "http://snomed.info/sct", - "version": "2022-09", - "code": "199306007", - "display": "Continuing pregnancy after abortion of one fetus or more (disorder)" - }, - { - "system": "http://snomed.info/sct", - "version": "2022-09", - "code": "22281000119101", - "display": "Post-term pregnancy of 40 to 42 weeks (finding)" - }, - { - "system": "http://snomed.info/sct", - "version": "2022-09", - "code": "237233002", - "display": "Concealed pregnancy (finding)" - }, - { - "system": "http://snomed.info/sct", - "version": "2022-09", - "code": "237238006", - "display": "Pregnancy with uncertain dates (finding)" - }, - { - "system": "http://snomed.info/sct", - "version": "2022-09", - "code": "237239003", - "display": "Low risk pregnancy (finding)" - }, - { - "system": "http://snomed.info/sct", - "version": "2022-09", - "code": "237240001", - "display": "Teenage pregnancy (finding)" - }, - { - "system": "http://snomed.info/sct", - "version": "2022-09", - "code": "237241002", - "display": "Viable pregnancy (finding)" - }, - { - "system": "http://snomed.info/sct", - "version": "2022-09", - "code": "237244005", - "display": "Single pregnancy (finding)" - }, - { - "system": "http://snomed.info/sct", - "version": "2022-09", - "code": "248985009", - "display": "Presentation of pregnancy (finding)" - }, - { - "system": "http://snomed.info/sct", - "version": "2022-09", - "code": "276367008", - "display": "Wanted pregnancy (finding)" - }, - { - "system": "http://snomed.info/sct", - "version": "2022-09", - "code": "281307002", - "display": "Uncertain viability of pregnancy (finding)" - }, - { - "system": "http://snomed.info/sct", - "version": "2022-09", - "code": "314204000", - "display": "Early stage of pregnancy (finding)" - }, - { - "system": "http://snomed.info/sct", - "version": "2022-09", - "code": "35381000119101", - "display": "Quadruplet pregnancy with loss of one or more fetuses (disorder)" - }, - { - "system": "http://snomed.info/sct", - "version": "2022-09", - "code": "36801000119105", - "display": "Continuing triplet pregnancy after spontaneous abortion of one or more fetuses (disorder)" - }, - { - "system": "http://snomed.info/sct", - "version": "2022-09", - "code": "38720006", - "display": "Septuplet pregnancy (disorder)" - }, - { - "system": "http://snomed.info/sct", - "version": "2022-09", - "code": "41587001", - "display": "Third trimester pregnancy (finding)" - }, - { - "system": "http://snomed.info/sct", - "version": "2022-09", - "code": "41991004", - "display": "Angiectasis pregnancy (disorder)" - }, - { - "system": "http://snomed.info/sct", - "version": "2022-09", - "code": "429187001", - "display": "Continuing pregnancy after intrauterine death of twin fetus (disorder)" - }, - { - "system": "http://snomed.info/sct", - "version": "2022-09", - "code": "43990006", - "display": "Sextuplet pregnancy (disorder)" - }, - { - "system": "http://snomed.info/sct", - "version": "2022-09", - "code": "442478007", - "display": "Multiple pregnancy involving intrauterine pregnancy and tubal pregnancy (disorder)" - }, - { - "system": "http://snomed.info/sct", - "version": "2022-09", - "code": "444661007", - "display": "High risk pregnancy due to history of preterm labor (finding)" - }, - { - "system": "http://snomed.info/sct", - "version": "2022-09", - "code": "45307008", - "display": "Extrachorial pregnancy (finding)" - }, - { - "system": "http://snomed.info/sct", - "version": "2022-09", - "code": "457811000124103", - "display": "Normal pregnancy in primigravida (finding)" - }, - { - "system": "http://snomed.info/sct", - "version": "2022-09", - "code": "457821000124106", - "display": "Normal pregnancy in multigravida (finding)" - }, - { - "system": "http://snomed.info/sct", - "version": "2022-09", - "code": "459167000", - "display": "Monochorionic twin pregnancy (disorder)" - }, - { - "system": "http://snomed.info/sct", - "version": "2022-09", - "code": "459169002", - "display": "Monochorionic diamniotic twin pregnancy with similar amniotic fluid volumes (disorder)" - }, - { - "system": "http://snomed.info/sct", - "version": "2022-09", - "code": "459170001", - "display": "Monochorionic diamniotic twin pregnancy with dissimilar amniotic fluid volumes (disorder)" - }, - { - "system": "http://snomed.info/sct", - "version": "2022-09", - "code": "47200007", - "display": "High risk pregnancy (finding)" - }, - { - "system": "http://snomed.info/sct", - "version": "2022-09", - "code": "472321009", - "display": "Continuing pregnancy after intrauterine death of one twin with intrauterine retention of dead twin (disorder)" - }, - { - "system": "http://snomed.info/sct", - "version": "2022-09", - "code": "57630001", - "display": "First trimester pregnancy (finding)" - }, - { - "system": "http://snomed.info/sct", - "version": "2022-09", - "code": "58532003", - "display": "Unwanted pregnancy (finding)" - }, - { - "system": "http://snomed.info/sct", - "version": "2022-09", - "code": "59466002", - "display": "Second trimester pregnancy (finding)" - }, - { - "system": "http://snomed.info/sct", - "version": "2022-09", - "code": "60810003", - "display": "Quadruplet pregnancy (disorder)" - }, - { - "system": "http://snomed.info/sct", - "version": "2022-09", - "code": "64254006", - "display": "Triplet pregnancy (disorder)" - }, - { - "system": "http://snomed.info/sct", - "version": "2022-09", - "code": "65147003", - "display": "Twin pregnancy (disorder)" - }, - { - "system": "http://snomed.info/sct", - "version": "2022-09", - "code": "65727000", - "display": "Intrauterine pregnancy (finding)" - }, - { - "system": "http://snomed.info/sct", - "version": "2022-09", - "code": "713575004", - "display": "Dizygotic twin pregnancy (disorder)" - }, - { - "system": "http://snomed.info/sct", - "version": "2022-09", - "code": "713576003", - "display": "Monozygotic twin pregnancy (disorder)" - }, - { - "system": "http://snomed.info/sct", - "version": "2022-09", - "code": "72892002", - "display": "Normal pregnancy (finding)" - }, - { - "system": "http://snomed.info/sct", - "version": "2022-09", - "code": "77386006", - "display": "Pregnancy (finding)" - }, - { - "system": "http://snomed.info/sct", - "version": "2022-09", - "code": "80997009", - "display": "Quintuplet pregnancy (disorder)" - }, - { - "system": "http://snomed.info/sct", - "version": "2022-09", - "code": "83074005", - "display": "Unplanned pregnancy (finding)" - }, - { - "system": "http://snomed.info/sct", - "version": "2022-09", - "code": "87527008", - "display": "Term pregnancy (finding)" - }, - { - "system": "http://snomed.info/sct", - "version": "2022-09", - "code": "90968009", - "display": "Prolonged pregnancy (disorder)" - }, - { - "system": "http://snomed.info/sct", - "version": "2022-09", - "code": "9279009", - "display": "Extra-amniotic pregnancy (finding)" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O09.00", - "display": "Supervision of pregnancy with history of infertility, unspecified trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O09.01", - "display": "Supervision of pregnancy with history of infertility, first trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O09.02", - "display": "Supervision of pregnancy with history of infertility, second trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O09.03", - "display": "Supervision of pregnancy with history of infertility, third trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O09.10", - "display": "Supervision of pregnancy with history of ectopic pregnancy, unspecified trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O09.11", - "display": "Supervision of pregnancy with history of ectopic pregnancy, first trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O09.12", - "display": "Supervision of pregnancy with history of ectopic pregnancy, second trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O09.13", - "display": "Supervision of pregnancy with history of ectopic pregnancy, third trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O09.211", - "display": "Supervision of pregnancy with history of pre-term labor, first trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O09.212", - "display": "Supervision of pregnancy with history of pre-term labor, second trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O09.213", - "display": "Supervision of pregnancy with history of pre-term labor, third trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O09.219", - "display": "Supervision of pregnancy with history of pre-term labor, unspecified trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O09.291", - "display": "Supervision of pregnancy with other poor reproductive or obstetric history, first trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O09.292", - "display": "Supervision of pregnancy with other poor reproductive or obstetric history, second trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O09.293", - "display": "Supervision of pregnancy with other poor reproductive or obstetric history, third trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O09.299", - "display": "Supervision of pregnancy with other poor reproductive or obstetric history, unspecified trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O09.30", - "display": "Supervision of pregnancy with insufficient antenatal care, unspecified trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O09.31", - "display": "Supervision of pregnancy with insufficient antenatal care, first trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O09.32", - "display": "Supervision of pregnancy with insufficient antenatal care, second trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O09.33", - "display": "Supervision of pregnancy with insufficient antenatal care, third trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O09.40", - "display": "Supervision of pregnancy with grand multiparity, unspecified trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O09.41", - "display": "Supervision of pregnancy with grand multiparity, first trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O09.42", - "display": "Supervision of pregnancy with grand multiparity, second trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O09.43", - "display": "Supervision of pregnancy with grand multiparity, third trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O09.511", - "display": "Supervision of elderly primigravida, first trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O09.512", - "display": "Supervision of elderly primigravida, second trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O09.513", - "display": "Supervision of elderly primigravida, third trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O09.519", - "display": "Supervision of elderly primigravida, unspecified trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O09.521", - "display": "Supervision of elderly multigravida, first trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O09.522", - "display": "Supervision of elderly multigravida, second trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O09.523", - "display": "Supervision of elderly multigravida, third trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O09.529", - "display": "Supervision of elderly multigravida, unspecified trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O09.611", - "display": "Supervision of young primigravida, first trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O09.612", - "display": "Supervision of young primigravida, second trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O09.613", - "display": "Supervision of young primigravida, third trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O09.619", - "display": "Supervision of young primigravida, unspecified trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O09.621", - "display": "Supervision of young multigravida, first trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O09.622", - "display": "Supervision of young multigravida, second trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O09.623", - "display": "Supervision of young multigravida, third trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O09.629", - "display": "Supervision of young multigravida, unspecified trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O09.70", - "display": "Supervision of high risk pregnancy due to social problems, unspecified trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O09.71", - "display": "Supervision of high risk pregnancy due to social problems, first trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O09.72", - "display": "Supervision of high risk pregnancy due to social problems, second trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O09.73", - "display": "Supervision of high risk pregnancy due to social problems, third trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O09.811", - "display": "Supervision of pregnancy resulting from assisted reproductive technology, first trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O09.812", - "display": "Supervision of pregnancy resulting from assisted reproductive technology, second trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O09.813", - "display": "Supervision of pregnancy resulting from assisted reproductive technology, third trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O09.819", - "display": "Supervision of pregnancy resulting from assisted reproductive technology, unspecified trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O09.821", - "display": "Supervision of pregnancy with history of in utero procedure during previous pregnancy, first trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O09.822", - "display": "Supervision of pregnancy with history of in utero procedure during previous pregnancy, second trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O09.823", - "display": "Supervision of pregnancy with history of in utero procedure during previous pregnancy, third trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O09.829", - "display": "Supervision of pregnancy with history of in utero procedure during previous pregnancy, unspecified trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O09.891", - "display": "Supervision of other high risk pregnancies, first trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O09.892", - "display": "Supervision of other high risk pregnancies, second trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O09.893", - "display": "Supervision of other high risk pregnancies, third trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O09.899", - "display": "Supervision of other high risk pregnancies, unspecified trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O09.90", - "display": "Supervision of high risk pregnancy, unspecified, unspecified trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O09.91", - "display": "Supervision of high risk pregnancy, unspecified, first trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O09.92", - "display": "Supervision of high risk pregnancy, unspecified, second trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O09.93", - "display": "Supervision of high risk pregnancy, unspecified, third trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O09.A0", - "display": "Supervision of pregnancy with history of molar pregnancy, unspecified trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O09.A1", - "display": "Supervision of pregnancy with history of molar pregnancy, first trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O09.A2", - "display": "Supervision of pregnancy with history of molar pregnancy, second trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O09.A3", - "display": "Supervision of pregnancy with history of molar pregnancy, third trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O10.011", - "display": "Pre-existing essential hypertension complicating pregnancy, first trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O10.012", - "display": "Pre-existing essential hypertension complicating pregnancy, second trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O10.013", - "display": "Pre-existing essential hypertension complicating pregnancy, third trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O10.019", - "display": "Pre-existing essential hypertension complicating pregnancy, unspecified trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O10.111", - "display": "Pre-existing hypertensive heart disease complicating pregnancy, first trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O10.112", - "display": "Pre-existing hypertensive heart disease complicating pregnancy, second trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O10.113", - "display": "Pre-existing hypertensive heart disease complicating pregnancy, third trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O10.119", - "display": "Pre-existing hypertensive heart disease complicating pregnancy, unspecified trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O10.211", - "display": "Pre-existing hypertensive chronic kidney disease complicating pregnancy, first trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O10.212", - "display": "Pre-existing hypertensive chronic kidney disease complicating pregnancy, second trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O10.213", - "display": "Pre-existing hypertensive chronic kidney disease complicating pregnancy, third trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O10.219", - "display": "Pre-existing hypertensive chronic kidney disease complicating pregnancy, unspecified trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O10.311", - "display": "Pre-existing hypertensive heart and chronic kidney disease complicating pregnancy, first trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O10.312", - "display": "Pre-existing hypertensive heart and chronic kidney disease complicating pregnancy, second trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O10.313", - "display": "Pre-existing hypertensive heart and chronic kidney disease complicating pregnancy, third trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O10.319", - "display": "Pre-existing hypertensive heart and chronic kidney disease complicating pregnancy, unspecified trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O10.411", - "display": "Pre-existing secondary hypertension complicating pregnancy, first trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O10.412", - "display": "Pre-existing secondary hypertension complicating pregnancy, second trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O10.413", - "display": "Pre-existing secondary hypertension complicating pregnancy, third trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O10.419", - "display": "Pre-existing secondary hypertension complicating pregnancy, unspecified trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O10.911", - "display": "Unspecified pre-existing hypertension complicating pregnancy, first trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O10.912", - "display": "Unspecified pre-existing hypertension complicating pregnancy, second trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O10.913", - "display": "Unspecified pre-existing hypertension complicating pregnancy, third trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O10.919", - "display": "Unspecified pre-existing hypertension complicating pregnancy, unspecified trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O11.1", - "display": "Pre-existing hypertension with pre-eclampsia, first trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O11.2", - "display": "Pre-existing hypertension with pre-eclampsia, second trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O11.3", - "display": "Pre-existing hypertension with pre-eclampsia, third trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O11.9", - "display": "Pre-existing hypertension with pre-eclampsia, unspecified trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O12.00", - "display": "Gestational edema, unspecified trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O12.01", - "display": "Gestational edema, first trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O12.02", - "display": "Gestational edema, second trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O12.03", - "display": "Gestational edema, third trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O12.10", - "display": "Gestational proteinuria, unspecified trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O12.11", - "display": "Gestational proteinuria, first trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O12.12", - "display": "Gestational proteinuria, second trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O12.13", - "display": "Gestational proteinuria, third trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O12.20", - "display": "Gestational edema with proteinuria, unspecified trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O12.21", - "display": "Gestational edema with proteinuria, first trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O12.22", - "display": "Gestational edema with proteinuria, second trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O12.23", - "display": "Gestational edema with proteinuria, third trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O13.1", - "display": "Gestational [pregnancy-induced] hypertension without significant proteinuria, first trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O13.2", - "display": "Gestational [pregnancy-induced] hypertension without significant proteinuria, second trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O13.3", - "display": "Gestational [pregnancy-induced] hypertension without significant proteinuria, third trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O13.9", - "display": "Gestational [pregnancy-induced] hypertension without significant proteinuria, unspecified trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O14.00", - "display": "Mild to moderate pre-eclampsia, unspecified trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O14.02", - "display": "Mild to moderate pre-eclampsia, second trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O14.03", - "display": "Mild to moderate pre-eclampsia, third trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O14.10", - "display": "Severe pre-eclampsia, unspecified trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O14.12", - "display": "Severe pre-eclampsia, second trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O14.13", - "display": "Severe pre-eclampsia, third trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O14.20", - "display": "HELLP syndrome (HELLP), unspecified trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O14.22", - "display": "HELLP syndrome (HELLP), second trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O14.23", - "display": "HELLP syndrome (HELLP), third trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O14.90", - "display": "Unspecified pre-eclampsia, unspecified trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O14.92", - "display": "Unspecified pre-eclampsia, second trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O14.93", - "display": "Unspecified pre-eclampsia, third trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O15.00", - "display": "Eclampsia complicating pregnancy, unspecified trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O15.02", - "display": "Eclampsia complicating pregnancy, second trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O15.03", - "display": "Eclampsia complicating pregnancy, third trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O16.1", - "display": "Unspecified maternal hypertension, first trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O16.2", - "display": "Unspecified maternal hypertension, second trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O16.3", - "display": "Unspecified maternal hypertension, third trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O16.9", - "display": "Unspecified maternal hypertension, unspecified trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O20.0", - "display": "Threatened abortion" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O20.8", - "display": "Other hemorrhage in early pregnancy" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O20.9", - "display": "Hemorrhage in early pregnancy, unspecified" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O21.0", - "display": "Mild hyperemesis gravidarum" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O21.1", - "display": "Hyperemesis gravidarum with metabolic disturbance" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O21.2", - "display": "Late vomiting of pregnancy" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O21.8", - "display": "Other vomiting complicating pregnancy" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O21.9", - "display": "Vomiting of pregnancy, unspecified" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O22.00", - "display": "Varicose veins of lower extremity in pregnancy, unspecified trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O22.01", - "display": "Varicose veins of lower extremity in pregnancy, first trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O22.02", - "display": "Varicose veins of lower extremity in pregnancy, second trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O22.03", - "display": "Varicose veins of lower extremity in pregnancy, third trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O22.10", - "display": "Genital varices in pregnancy, unspecified trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O22.11", - "display": "Genital varices in pregnancy, first trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O22.12", - "display": "Genital varices in pregnancy, second trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O22.13", - "display": "Genital varices in pregnancy, third trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O22.20", - "display": "Superficial thrombophlebitis in pregnancy, unspecified trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O22.21", - "display": "Superficial thrombophlebitis in pregnancy, first trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O22.22", - "display": "Superficial thrombophlebitis in pregnancy, second trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O22.23", - "display": "Superficial thrombophlebitis in pregnancy, third trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O22.30", - "display": "Deep phlebothrombosis in pregnancy, unspecified trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O22.31", - "display": "Deep phlebothrombosis in pregnancy, first trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O22.32", - "display": "Deep phlebothrombosis in pregnancy, second trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O22.33", - "display": "Deep phlebothrombosis in pregnancy, third trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O22.40", - "display": "Hemorrhoids in pregnancy, unspecified trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O22.41", - "display": "Hemorrhoids in pregnancy, first trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O22.42", - "display": "Hemorrhoids in pregnancy, second trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O22.43", - "display": "Hemorrhoids in pregnancy, third trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O22.50", - "display": "Cerebral venous thrombosis in pregnancy, unspecified trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O22.51", - "display": "Cerebral venous thrombosis in pregnancy, first trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O22.52", - "display": "Cerebral venous thrombosis in pregnancy, second trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O22.53", - "display": "Cerebral venous thrombosis in pregnancy, third trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O22.8X1", - "display": "Other venous complications in pregnancy, first trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O22.8X2", - "display": "Other venous complications in pregnancy, second trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O22.8X3", - "display": "Other venous complications in pregnancy, third trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O22.8X9", - "display": "Other venous complications in pregnancy, unspecified trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O22.90", - "display": "Venous complication in pregnancy, unspecified, unspecified trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O22.91", - "display": "Venous complication in pregnancy, unspecified, first trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O22.92", - "display": "Venous complication in pregnancy, unspecified, second trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O22.93", - "display": "Venous complication in pregnancy, unspecified, third trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O23.00", - "display": "Infections of kidney in pregnancy, unspecified trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O23.01", - "display": "Infections of kidney in pregnancy, first trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O23.02", - "display": "Infections of kidney in pregnancy, second trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O23.03", - "display": "Infections of kidney in pregnancy, third trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O23.10", - "display": "Infections of bladder in pregnancy, unspecified trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O23.11", - "display": "Infections of bladder in pregnancy, first trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O23.12", - "display": "Infections of bladder in pregnancy, second trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O23.13", - "display": "Infections of bladder in pregnancy, third trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O23.20", - "display": "Infections of urethra in pregnancy, unspecified trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O23.21", - "display": "Infections of urethra in pregnancy, first trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O23.22", - "display": "Infections of urethra in pregnancy, second trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O23.23", - "display": "Infections of urethra in pregnancy, third trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O23.30", - "display": "Infections of other parts of urinary tract in pregnancy, unspecified trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O23.31", - "display": "Infections of other parts of urinary tract in pregnancy, first trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O23.32", - "display": "Infections of other parts of urinary tract in pregnancy, second trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O23.33", - "display": "Infections of other parts of urinary tract in pregnancy, third trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O23.40", - "display": "Unspecified infection of urinary tract in pregnancy, unspecified trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O23.41", - "display": "Unspecified infection of urinary tract in pregnancy, first trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O23.42", - "display": "Unspecified infection of urinary tract in pregnancy, second trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O23.43", - "display": "Unspecified infection of urinary tract in pregnancy, third trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O23.511", - "display": "Infections of cervix in pregnancy, first trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O23.512", - "display": "Infections of cervix in pregnancy, second trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O23.513", - "display": "Infections of cervix in pregnancy, third trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O23.519", - "display": "Infections of cervix in pregnancy, unspecified trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O23.521", - "display": "Salpingo-oophoritis in pregnancy, first trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O23.522", - "display": "Salpingo-oophoritis in pregnancy, second trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O23.523", - "display": "Salpingo-oophoritis in pregnancy, third trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O23.529", - "display": "Salpingo-oophoritis in pregnancy, unspecified trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O23.591", - "display": "Infection of other part of genital tract in pregnancy, first trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O23.592", - "display": "Infection of other part of genital tract in pregnancy, second trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O23.593", - "display": "Infection of other part of genital tract in pregnancy, third trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O23.599", - "display": "Infection of other part of genital tract in pregnancy, unspecified trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O23.90", - "display": "Unspecified genitourinary tract infection in pregnancy, unspecified trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O23.91", - "display": "Unspecified genitourinary tract infection in pregnancy, first trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O23.92", - "display": "Unspecified genitourinary tract infection in pregnancy, second trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O23.93", - "display": "Unspecified genitourinary tract infection in pregnancy, third trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O24.011", - "display": "Pre-existing type 1 diabetes mellitus, in pregnancy, first trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O24.012", - "display": "Pre-existing type 1 diabetes mellitus, in pregnancy, second trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O24.013", - "display": "Pre-existing type 1 diabetes mellitus, in pregnancy, third trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O24.019", - "display": "Pre-existing type 1 diabetes mellitus, in pregnancy, unspecified trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O24.111", - "display": "Pre-existing type 2 diabetes mellitus, in pregnancy, first trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O24.112", - "display": "Pre-existing type 2 diabetes mellitus, in pregnancy, second trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O24.113", - "display": "Pre-existing type 2 diabetes mellitus, in pregnancy, third trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O24.119", - "display": "Pre-existing type 2 diabetes mellitus, in pregnancy, unspecified trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O24.311", - "display": "Unspecified pre-existing diabetes mellitus in pregnancy, first trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O24.312", - "display": "Unspecified pre-existing diabetes mellitus in pregnancy, second trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O24.313", - "display": "Unspecified pre-existing diabetes mellitus in pregnancy, third trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O24.319", - "display": "Unspecified pre-existing diabetes mellitus in pregnancy, unspecified trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O24.410", - "display": "Gestational diabetes mellitus in pregnancy, diet controlled" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O24.414", - "display": "Gestational diabetes mellitus in pregnancy, insulin controlled" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O24.415", - "display": "Gestational diabetes mellitus in pregnancy, controlled by oral hypoglycemic drugs" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O24.419", - "display": "Gestational diabetes mellitus in pregnancy, unspecified control" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O24.811", - "display": "Other pre-existing diabetes mellitus in pregnancy, first trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O24.812", - "display": "Other pre-existing diabetes mellitus in pregnancy, second trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O24.813", - "display": "Other pre-existing diabetes mellitus in pregnancy, third trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O24.819", - "display": "Other pre-existing diabetes mellitus in pregnancy, unspecified trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O24.911", - "display": "Unspecified diabetes mellitus in pregnancy, first trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O24.912", - "display": "Unspecified diabetes mellitus in pregnancy, second trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O24.913", - "display": "Unspecified diabetes mellitus in pregnancy, third trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O24.919", - "display": "Unspecified diabetes mellitus in pregnancy, unspecified trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O25.10", - "display": "Malnutrition in pregnancy, unspecified trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O25.11", - "display": "Malnutrition in pregnancy, first trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O25.12", - "display": "Malnutrition in pregnancy, second trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O25.13", - "display": "Malnutrition in pregnancy, third trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O26.00", - "display": "Excessive weight gain in pregnancy, unspecified trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O26.01", - "display": "Excessive weight gain in pregnancy, first trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O26.02", - "display": "Excessive weight gain in pregnancy, second trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O26.03", - "display": "Excessive weight gain in pregnancy, third trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O26.10", - "display": "Low weight gain in pregnancy, unspecified trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O26.11", - "display": "Low weight gain in pregnancy, first trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O26.12", - "display": "Low weight gain in pregnancy, second trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O26.13", - "display": "Low weight gain in pregnancy, third trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O26.20", - "display": "Pregnancy care for patient with recurrent pregnancy loss, unspecified trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O26.21", - "display": "Pregnancy care for patient with recurrent pregnancy loss, first trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O26.22", - "display": "Pregnancy care for patient with recurrent pregnancy loss, second trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O26.23", - "display": "Pregnancy care for patient with recurrent pregnancy loss, third trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O26.30", - "display": "Retained intrauterine contraceptive device in pregnancy, unspecified trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O26.31", - "display": "Retained intrauterine contraceptive device in pregnancy, first trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O26.32", - "display": "Retained intrauterine contraceptive device in pregnancy, second trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O26.33", - "display": "Retained intrauterine contraceptive device in pregnancy, third trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O26.40", - "display": "Herpes gestationis, unspecified trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O26.41", - "display": "Herpes gestationis, first trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O26.42", - "display": "Herpes gestationis, second trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O26.43", - "display": "Herpes gestationis, third trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O26.50", - "display": "Maternal hypotension syndrome, unspecified trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O26.51", - "display": "Maternal hypotension syndrome, first trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O26.52", - "display": "Maternal hypotension syndrome, second trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O26.53", - "display": "Maternal hypotension syndrome, third trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O26.611", - "display": "Liver and biliary tract disorders in pregnancy, first trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O26.612", - "display": "Liver and biliary tract disorders in pregnancy, second trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O26.613", - "display": "Liver and biliary tract disorders in pregnancy, third trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O26.619", - "display": "Liver and biliary tract disorders in pregnancy, unspecified trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O26.711", - "display": "Subluxation of symphysis (pubis) in pregnancy, first trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O26.712", - "display": "Subluxation of symphysis (pubis) in pregnancy, second trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O26.713", - "display": "Subluxation of symphysis (pubis) in pregnancy, third trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O26.719", - "display": "Subluxation of symphysis (pubis) in pregnancy, unspecified trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O26.811", - "display": "Pregnancy related exhaustion and fatigue, first trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O26.812", - "display": "Pregnancy related exhaustion and fatigue, second trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O26.813", - "display": "Pregnancy related exhaustion and fatigue, third trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O26.819", - "display": "Pregnancy related exhaustion and fatigue, unspecified trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O26.821", - "display": "Pregnancy related peripheral neuritis, first trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O26.822", - "display": "Pregnancy related peripheral neuritis, second trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O26.823", - "display": "Pregnancy related peripheral neuritis, third trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O26.829", - "display": "Pregnancy related peripheral neuritis, unspecified trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O26.831", - "display": "Pregnancy related renal disease, first trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O26.832", - "display": "Pregnancy related renal disease, second trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O26.833", - "display": "Pregnancy related renal disease, third trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O26.839", - "display": "Pregnancy related renal disease, unspecified trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O26.841", - "display": "Uterine size-date discrepancy, first trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O26.842", - "display": "Uterine size-date discrepancy, second trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O26.843", - "display": "Uterine size-date discrepancy, third trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O26.849", - "display": "Uterine size-date discrepancy, unspecified trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O26.851", - "display": "Spotting complicating pregnancy, first trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O26.852", - "display": "Spotting complicating pregnancy, second trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O26.853", - "display": "Spotting complicating pregnancy, third trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O26.859", - "display": "Spotting complicating pregnancy, unspecified trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O26.86", - "display": "Pruritic urticarial papules and plaques of pregnancy (PUPPP)" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O26.872", - "display": "Cervical shortening, second trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O26.873", - "display": "Cervical shortening, third trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O26.879", - "display": "Cervical shortening, unspecified trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O26.891", - "display": "Other specified pregnancy related conditions, first trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O26.892", - "display": "Other specified pregnancy related conditions, second trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O26.893", - "display": "Other specified pregnancy related conditions, third trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O26.899", - "display": "Other specified pregnancy related conditions, unspecified trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O26.90", - "display": "Pregnancy related conditions, unspecified, unspecified trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O26.91", - "display": "Pregnancy related conditions, unspecified, first trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O26.92", - "display": "Pregnancy related conditions, unspecified, second trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O26.93", - "display": "Pregnancy related conditions, unspecified, third trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O28.0", - "display": "Abnormal hematological finding on antenatal screening of mother" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O28.1", - "display": "Abnormal biochemical finding on antenatal screening of mother" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O28.2", - "display": "Abnormal cytological finding on antenatal screening of mother" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O28.3", - "display": "Abnormal ultrasonic finding on antenatal screening of mother" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O28.4", - "display": "Abnormal radiological finding on antenatal screening of mother" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O28.5", - "display": "Abnormal chromosomal and genetic finding on antenatal screening of mother" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O28.8", - "display": "Other abnormal findings on antenatal screening of mother" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O28.9", - "display": "Unspecified abnormal findings on antenatal screening of mother" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O29.011", - "display": "Aspiration pneumonitis due to anesthesia during pregnancy, first trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O29.012", - "display": "Aspiration pneumonitis due to anesthesia during pregnancy, second trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O29.013", - "display": "Aspiration pneumonitis due to anesthesia during pregnancy, third trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O29.019", - "display": "Aspiration pneumonitis due to anesthesia during pregnancy, unspecified trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O29.021", - "display": "Pressure collapse of lung due to anesthesia during pregnancy, first trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O29.022", - "display": "Pressure collapse of lung due to anesthesia during pregnancy, second trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O29.023", - "display": "Pressure collapse of lung due to anesthesia during pregnancy, third trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O29.029", - "display": "Pressure collapse of lung due to anesthesia during pregnancy, unspecified trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O29.091", - "display": "Other pulmonary complications of anesthesia during pregnancy, first trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O29.092", - "display": "Other pulmonary complications of anesthesia during pregnancy, second trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O29.093", - "display": "Other pulmonary complications of anesthesia during pregnancy, third trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O29.099", - "display": "Other pulmonary complications of anesthesia during pregnancy, unspecified trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O29.111", - "display": "Cardiac arrest due to anesthesia during pregnancy, first trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O29.112", - "display": "Cardiac arrest due to anesthesia during pregnancy, second trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O29.113", - "display": "Cardiac arrest due to anesthesia during pregnancy, third trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O29.119", - "display": "Cardiac arrest due to anesthesia during pregnancy, unspecified trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O29.121", - "display": "Cardiac failure due to anesthesia during pregnancy, first trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O29.122", - "display": "Cardiac failure due to anesthesia during pregnancy, second trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O29.123", - "display": "Cardiac failure due to anesthesia during pregnancy, third trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O29.129", - "display": "Cardiac failure due to anesthesia during pregnancy, unspecified trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O29.191", - "display": "Other cardiac complications of anesthesia during pregnancy, first trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O29.192", - "display": "Other cardiac complications of anesthesia during pregnancy, second trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O29.193", - "display": "Other cardiac complications of anesthesia during pregnancy, third trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O29.199", - "display": "Other cardiac complications of anesthesia during pregnancy, unspecified trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O29.211", - "display": "Cerebral anoxia due to anesthesia during pregnancy, first trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O29.212", - "display": "Cerebral anoxia due to anesthesia during pregnancy, second trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O29.213", - "display": "Cerebral anoxia due to anesthesia during pregnancy, third trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O29.219", - "display": "Cerebral anoxia due to anesthesia during pregnancy, unspecified trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O29.291", - "display": "Other central nervous system complications of anesthesia during pregnancy, first trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O29.292", - "display": "Other central nervous system complications of anesthesia during pregnancy, second trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O29.293", - "display": "Other central nervous system complications of anesthesia during pregnancy, third trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O29.299", - "display": "Other central nervous system complications of anesthesia during pregnancy, unspecified trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O29.3X1", - "display": "Toxic reaction to local anesthesia during pregnancy, first trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O29.3X2", - "display": "Toxic reaction to local anesthesia during pregnancy, second trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O29.3X3", - "display": "Toxic reaction to local anesthesia during pregnancy, third trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O29.3X9", - "display": "Toxic reaction to local anesthesia during pregnancy, unspecified trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O29.40", - "display": "Spinal and epidural anesthesia induced headache during pregnancy, unspecified trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O29.41", - "display": "Spinal and epidural anesthesia induced headache during pregnancy, first trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O29.42", - "display": "Spinal and epidural anesthesia induced headache during pregnancy, second trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O29.43", - "display": "Spinal and epidural anesthesia induced headache during pregnancy, third trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O29.5X1", - "display": "Other complications of spinal and epidural anesthesia during pregnancy, first trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O29.5X2", - "display": "Other complications of spinal and epidural anesthesia during pregnancy, second trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O29.5X3", - "display": "Other complications of spinal and epidural anesthesia during pregnancy, third trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O29.5X9", - "display": "Other complications of spinal and epidural anesthesia during pregnancy, unspecified trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O29.60", - "display": "Failed or difficult intubation for anesthesia during pregnancy, unspecified trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O29.61", - "display": "Failed or difficult intubation for anesthesia during pregnancy, first trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O29.62", - "display": "Failed or difficult intubation for anesthesia during pregnancy, second trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O29.63", - "display": "Failed or difficult intubation for anesthesia during pregnancy, third trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O29.8X1", - "display": "Other complications of anesthesia during pregnancy, first trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O29.8X2", - "display": "Other complications of anesthesia during pregnancy, second trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O29.8X3", - "display": "Other complications of anesthesia during pregnancy, third trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O29.8X9", - "display": "Other complications of anesthesia during pregnancy, unspecified trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O29.90", - "display": "Unspecified complication of anesthesia during pregnancy, unspecified trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O29.91", - "display": "Unspecified complication of anesthesia during pregnancy, first trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O29.92", - "display": "Unspecified complication of anesthesia during pregnancy, second trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O29.93", - "display": "Unspecified complication of anesthesia during pregnancy, third trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O30.001", - "display": "Twin pregnancy, unspecified number of placenta and unspecified number of amniotic sacs, first trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O30.002", - "display": "Twin pregnancy, unspecified number of placenta and unspecified number of amniotic sacs, second trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O30.003", - "display": "Twin pregnancy, unspecified number of placenta and unspecified number of amniotic sacs, third trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O30.009", - "display": "Twin pregnancy, unspecified number of placenta and unspecified number of amniotic sacs, unspecified trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O30.011", - "display": "Twin pregnancy, monochorionic/monoamniotic, first trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O30.012", - "display": "Twin pregnancy, monochorionic/monoamniotic, second trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O30.013", - "display": "Twin pregnancy, monochorionic/monoamniotic, third trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O30.019", - "display": "Twin pregnancy, monochorionic/monoamniotic, unspecified trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O30.021", - "display": "Conjoined twin pregnancy, first trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O30.022", - "display": "Conjoined twin pregnancy, second trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O30.023", - "display": "Conjoined twin pregnancy, third trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O30.029", - "display": "Conjoined twin pregnancy, unspecified trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O30.031", - "display": "Twin pregnancy, monochorionic/diamniotic, first trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O30.032", - "display": "Twin pregnancy, monochorionic/diamniotic, second trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O30.033", - "display": "Twin pregnancy, monochorionic/diamniotic, third trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O30.039", - "display": "Twin pregnancy, monochorionic/diamniotic, unspecified trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O30.041", - "display": "Twin pregnancy, dichorionic/diamniotic, first trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O30.042", - "display": "Twin pregnancy, dichorionic/diamniotic, second trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O30.043", - "display": "Twin pregnancy, dichorionic/diamniotic, third trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O30.049", - "display": "Twin pregnancy, dichorionic/diamniotic, unspecified trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O30.091", - "display": "Twin pregnancy, unable to determine number of placenta and number of amniotic sacs, first trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O30.092", - "display": "Twin pregnancy, unable to determine number of placenta and number of amniotic sacs, second trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O30.093", - "display": "Twin pregnancy, unable to determine number of placenta and number of amniotic sacs, third trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O30.099", - "display": "Twin pregnancy, unable to determine number of placenta and number of amniotic sacs, unspecified trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O30.101", - "display": "Triplet pregnancy, unspecified number of placenta and unspecified number of amniotic sacs, first trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O30.102", - "display": "Triplet pregnancy, unspecified number of placenta and unspecified number of amniotic sacs, second trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O30.103", - "display": "Triplet pregnancy, unspecified number of placenta and unspecified number of amniotic sacs, third trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O30.109", - "display": "Triplet pregnancy, unspecified number of placenta and unspecified number of amniotic sacs, unspecified trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O30.111", - "display": "Triplet pregnancy with two or more monochorionic fetuses, first trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O30.112", - "display": "Triplet pregnancy with two or more monochorionic fetuses, second trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O30.113", - "display": "Triplet pregnancy with two or more monochorionic fetuses, third trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O30.119", - "display": "Triplet pregnancy with two or more monochorionic fetuses, unspecified trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O30.121", - "display": "Triplet pregnancy with two or more monoamniotic fetuses, first trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O30.122", - "display": "Triplet pregnancy with two or more monoamniotic fetuses, second trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O30.123", - "display": "Triplet pregnancy with two or more monoamniotic fetuses, third trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O30.129", - "display": "Triplet pregnancy with two or more monoamniotic fetuses, unspecified trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O30.131", - "display": "Triplet pregnancy, trichorionic/triamniotic, first trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O30.132", - "display": "Triplet pregnancy, trichorionic/triamniotic, second trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O30.133", - "display": "Triplet pregnancy, trichorionic/triamniotic, third trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O30.139", - "display": "Triplet pregnancy, trichorionic/triamniotic, unspecified trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O30.191", - "display": "Triplet pregnancy, unable to determine number of placenta and number of amniotic sacs, first trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O30.192", - "display": "Triplet pregnancy, unable to determine number of placenta and number of amniotic sacs, second trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O30.193", - "display": "Triplet pregnancy, unable to determine number of placenta and number of amniotic sacs, third trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O30.199", - "display": "Triplet pregnancy, unable to determine number of placenta and number of amniotic sacs, unspecified trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O30.201", - "display": "Quadruplet pregnancy, unspecified number of placenta and unspecified number of amniotic sacs, first trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O30.202", - "display": "Quadruplet pregnancy, unspecified number of placenta and unspecified number of amniotic sacs, second trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O30.203", - "display": "Quadruplet pregnancy, unspecified number of placenta and unspecified number of amniotic sacs, third trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O30.209", - "display": "Quadruplet pregnancy, unspecified number of placenta and unspecified number of amniotic sacs, unspecified trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O30.211", - "display": "Quadruplet pregnancy with two or more monochorionic fetuses, first trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O30.212", - "display": "Quadruplet pregnancy with two or more monochorionic fetuses, second trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O30.213", - "display": "Quadruplet pregnancy with two or more monochorionic fetuses, third trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O30.219", - "display": "Quadruplet pregnancy with two or more monochorionic fetuses, unspecified trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O30.221", - "display": "Quadruplet pregnancy with two or more monoamniotic fetuses, first trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O30.222", - "display": "Quadruplet pregnancy with two or more monoamniotic fetuses, second trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O30.223", - "display": "Quadruplet pregnancy with two or more monoamniotic fetuses, third trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O30.229", - "display": "Quadruplet pregnancy with two or more monoamniotic fetuses, unspecified trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O30.231", - "display": "Quadruplet pregnancy, quadrachorionic/quadra-amniotic, first trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O30.232", - "display": "Quadruplet pregnancy, quadrachorionic/quadra-amniotic, second trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O30.233", - "display": "Quadruplet pregnancy, quadrachorionic/quadra-amniotic, third trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O30.239", - "display": "Quadruplet pregnancy, quadrachorionic/quadra-amniotic, unspecified trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O30.291", - "display": "Quadruplet pregnancy, unable to determine number of placenta and number of amniotic sacs, first trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O30.292", - "display": "Quadruplet pregnancy, unable to determine number of placenta and number of amniotic sacs, second trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O30.293", - "display": "Quadruplet pregnancy, unable to determine number of placenta and number of amniotic sacs, third trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O30.299", - "display": "Quadruplet pregnancy, unable to determine number of placenta and number of amniotic sacs, unspecified trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O30.801", - "display": "Other specified multiple gestation, unspecified number of placenta and unspecified number of amniotic sacs, first trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O30.802", - "display": "Other specified multiple gestation, unspecified number of placenta and unspecified number of amniotic sacs, second trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O30.803", - "display": "Other specified multiple gestation, unspecified number of placenta and unspecified number of amniotic sacs, third trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O30.809", - "display": "Other specified multiple gestation, unspecified number of placenta and unspecified number of amniotic sacs, unspecified trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O30.811", - "display": "Other specified multiple gestation with two or more monochorionic fetuses, first trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O30.812", - "display": "Other specified multiple gestation with two or more monochorionic fetuses, second trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O30.813", - "display": "Other specified multiple gestation with two or more monochorionic fetuses, third trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O30.819", - "display": "Other specified multiple gestation with two or more monochorionic fetuses, unspecified trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O30.821", - "display": "Other specified multiple gestation with two or more monoamniotic fetuses, first trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O30.822", - "display": "Other specified multiple gestation with two or more monoamniotic fetuses, second trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O30.823", - "display": "Other specified multiple gestation with two or more monoamniotic fetuses, third trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O30.829", - "display": "Other specified multiple gestation with two or more monoamniotic fetuses, unspecified trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O30.831", - "display": "Other specified multiple gestation, number of chorions and amnions are both equal to the number of fetuses, first trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O30.832", - "display": "Other specified multiple gestation, number of chorions and amnions are both equal to the number of fetuses, second trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O30.833", - "display": "Other specified multiple gestation, number of chorions and amnions are both equal to the number of fetuses, third trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O30.839", - "display": "Other specified multiple gestation, number of chorions and amnions are both equal to the number of fetuses, unspecified trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O30.891", - "display": "Other specified multiple gestation, unable to determine number of placenta and number of amniotic sacs, first trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O30.892", - "display": "Other specified multiple gestation, unable to determine number of placenta and number of amniotic sacs, second trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O30.893", - "display": "Other specified multiple gestation, unable to determine number of placenta and number of amniotic sacs, third trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O30.899", - "display": "Other specified multiple gestation, unable to determine number of placenta and number of amniotic sacs, unspecified trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O30.90", - "display": "Multiple gestation, unspecified, unspecified trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O30.91", - "display": "Multiple gestation, unspecified, first trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O30.92", - "display": "Multiple gestation, unspecified, second trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O30.93", - "display": "Multiple gestation, unspecified, third trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O31.00X0", - "display": "Papyraceous fetus, unspecified trimester, not applicable or unspecified" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O31.00X1", - "display": "Papyraceous fetus, unspecified trimester, fetus 1" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O31.00X2", - "display": "Papyraceous fetus, unspecified trimester, fetus 2" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O31.00X3", - "display": "Papyraceous fetus, unspecified trimester, fetus 3" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O31.00X4", - "display": "Papyraceous fetus, unspecified trimester, fetus 4" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O31.00X5", - "display": "Papyraceous fetus, unspecified trimester, fetus 5" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O31.00X9", - "display": "Papyraceous fetus, unspecified trimester, other fetus" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O31.01X0", - "display": "Papyraceous fetus, first trimester, not applicable or unspecified" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O31.01X1", - "display": "Papyraceous fetus, first trimester, fetus 1" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O31.01X2", - "display": "Papyraceous fetus, first trimester, fetus 2" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O31.01X3", - "display": "Papyraceous fetus, first trimester, fetus 3" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O31.01X4", - "display": "Papyraceous fetus, first trimester, fetus 4" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O31.01X5", - "display": "Papyraceous fetus, first trimester, fetus 5" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O31.01X9", - "display": "Papyraceous fetus, first trimester, other fetus" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O31.02X0", - "display": "Papyraceous fetus, second trimester, not applicable or unspecified" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O31.02X1", - "display": "Papyraceous fetus, second trimester, fetus 1" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O31.02X2", - "display": "Papyraceous fetus, second trimester, fetus 2" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O31.02X3", - "display": "Papyraceous fetus, second trimester, fetus 3" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O31.02X4", - "display": "Papyraceous fetus, second trimester, fetus 4" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O31.02X5", - "display": "Papyraceous fetus, second trimester, fetus 5" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O31.02X9", - "display": "Papyraceous fetus, second trimester, other fetus" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O31.03X0", - "display": "Papyraceous fetus, third trimester, not applicable or unspecified" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O31.03X1", - "display": "Papyraceous fetus, third trimester, fetus 1" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O31.03X2", - "display": "Papyraceous fetus, third trimester, fetus 2" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O31.03X3", - "display": "Papyraceous fetus, third trimester, fetus 3" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O31.03X4", - "display": "Papyraceous fetus, third trimester, fetus 4" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O31.03X5", - "display": "Papyraceous fetus, third trimester, fetus 5" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O31.03X9", - "display": "Papyraceous fetus, third trimester, other fetus" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O31.10X0", - "display": "Continuing pregnancy after spontaneous abortion of one fetus or more, unspecified trimester, not applicable or unspecified" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O31.10X1", - "display": "Continuing pregnancy after spontaneous abortion of one fetus or more, unspecified trimester, fetus 1" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O31.10X2", - "display": "Continuing pregnancy after spontaneous abortion of one fetus or more, unspecified trimester, fetus 2" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O31.10X3", - "display": "Continuing pregnancy after spontaneous abortion of one fetus or more, unspecified trimester, fetus 3" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O31.10X4", - "display": "Continuing pregnancy after spontaneous abortion of one fetus or more, unspecified trimester, fetus 4" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O31.10X5", - "display": "Continuing pregnancy after spontaneous abortion of one fetus or more, unspecified trimester, fetus 5" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O31.10X9", - "display": "Continuing pregnancy after spontaneous abortion of one fetus or more, unspecified trimester, other fetus" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O31.11X0", - "display": "Continuing pregnancy after spontaneous abortion of one fetus or more, first trimester, not applicable or unspecified" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O31.11X1", - "display": "Continuing pregnancy after spontaneous abortion of one fetus or more, first trimester, fetus 1" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O31.11X2", - "display": "Continuing pregnancy after spontaneous abortion of one fetus or more, first trimester, fetus 2" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O31.11X3", - "display": "Continuing pregnancy after spontaneous abortion of one fetus or more, first trimester, fetus 3" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O31.11X4", - "display": "Continuing pregnancy after spontaneous abortion of one fetus or more, first trimester, fetus 4" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O31.11X5", - "display": "Continuing pregnancy after spontaneous abortion of one fetus or more, first trimester, fetus 5" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O31.11X9", - "display": "Continuing pregnancy after spontaneous abortion of one fetus or more, first trimester, other fetus" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O31.12X0", - "display": "Continuing pregnancy after spontaneous abortion of one fetus or more, second trimester, not applicable or unspecified" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O31.12X1", - "display": "Continuing pregnancy after spontaneous abortion of one fetus or more, second trimester, fetus 1" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O31.12X2", - "display": "Continuing pregnancy after spontaneous abortion of one fetus or more, second trimester, fetus 2" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O31.12X3", - "display": "Continuing pregnancy after spontaneous abortion of one fetus or more, second trimester, fetus 3" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O31.12X4", - "display": "Continuing pregnancy after spontaneous abortion of one fetus or more, second trimester, fetus 4" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O31.12X5", - "display": "Continuing pregnancy after spontaneous abortion of one fetus or more, second trimester, fetus 5" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O31.12X9", - "display": "Continuing pregnancy after spontaneous abortion of one fetus or more, second trimester, other fetus" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O31.13X0", - "display": "Continuing pregnancy after spontaneous abortion of one fetus or more, third trimester, not applicable or unspecified" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O31.13X1", - "display": "Continuing pregnancy after spontaneous abortion of one fetus or more, third trimester, fetus 1" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O31.13X2", - "display": "Continuing pregnancy after spontaneous abortion of one fetus or more, third trimester, fetus 2" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O31.13X3", - "display": "Continuing pregnancy after spontaneous abortion of one fetus or more, third trimester, fetus 3" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O31.13X4", - "display": "Continuing pregnancy after spontaneous abortion of one fetus or more, third trimester, fetus 4" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O31.13X5", - "display": "Continuing pregnancy after spontaneous abortion of one fetus or more, third trimester, fetus 5" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O31.13X9", - "display": "Continuing pregnancy after spontaneous abortion of one fetus or more, third trimester, other fetus" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O31.20X0", - "display": "Continuing pregnancy after intrauterine death of one fetus or more, unspecified trimester, not applicable or unspecified" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O31.20X1", - "display": "Continuing pregnancy after intrauterine death of one fetus or more, unspecified trimester, fetus 1" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O31.20X2", - "display": "Continuing pregnancy after intrauterine death of one fetus or more, unspecified trimester, fetus 2" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O31.20X3", - "display": "Continuing pregnancy after intrauterine death of one fetus or more, unspecified trimester, fetus 3" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O31.20X4", - "display": "Continuing pregnancy after intrauterine death of one fetus or more, unspecified trimester, fetus 4" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O31.20X5", - "display": "Continuing pregnancy after intrauterine death of one fetus or more, unspecified trimester, fetus 5" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O31.20X9", - "display": "Continuing pregnancy after intrauterine death of one fetus or more, unspecified trimester, other fetus" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O31.21X0", - "display": "Continuing pregnancy after intrauterine death of one fetus or more, first trimester, not applicable or unspecified" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O31.21X1", - "display": "Continuing pregnancy after intrauterine death of one fetus or more, first trimester, fetus 1" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O31.21X2", - "display": "Continuing pregnancy after intrauterine death of one fetus or more, first trimester, fetus 2" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O31.21X3", - "display": "Continuing pregnancy after intrauterine death of one fetus or more, first trimester, fetus 3" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O31.21X4", - "display": "Continuing pregnancy after intrauterine death of one fetus or more, first trimester, fetus 4" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O31.21X5", - "display": "Continuing pregnancy after intrauterine death of one fetus or more, first trimester, fetus 5" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O31.21X9", - "display": "Continuing pregnancy after intrauterine death of one fetus or more, first trimester, other fetus" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O31.22X0", - "display": "Continuing pregnancy after intrauterine death of one fetus or more, second trimester, not applicable or unspecified" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O31.22X1", - "display": "Continuing pregnancy after intrauterine death of one fetus or more, second trimester, fetus 1" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O31.22X2", - "display": "Continuing pregnancy after intrauterine death of one fetus or more, second trimester, fetus 2" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O31.22X3", - "display": "Continuing pregnancy after intrauterine death of one fetus or more, second trimester, fetus 3" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O31.22X4", - "display": "Continuing pregnancy after intrauterine death of one fetus or more, second trimester, fetus 4" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O31.22X5", - "display": "Continuing pregnancy after intrauterine death of one fetus or more, second trimester, fetus 5" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O31.22X9", - "display": "Continuing pregnancy after intrauterine death of one fetus or more, second trimester, other fetus" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O31.23X0", - "display": "Continuing pregnancy after intrauterine death of one fetus or more, third trimester, not applicable or unspecified" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O31.23X1", - "display": "Continuing pregnancy after intrauterine death of one fetus or more, third trimester, fetus 1" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O31.23X2", - "display": "Continuing pregnancy after intrauterine death of one fetus or more, third trimester, fetus 2" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O31.23X3", - "display": "Continuing pregnancy after intrauterine death of one fetus or more, third trimester, fetus 3" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O31.23X4", - "display": "Continuing pregnancy after intrauterine death of one fetus or more, third trimester, fetus 4" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O31.23X5", - "display": "Continuing pregnancy after intrauterine death of one fetus or more, third trimester, fetus 5" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O31.23X9", - "display": "Continuing pregnancy after intrauterine death of one fetus or more, third trimester, other fetus" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O31.30X0", - "display": "Continuing pregnancy after elective fetal reduction of one fetus or more, unspecified trimester, not applicable or unspecified" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O31.30X1", - "display": "Continuing pregnancy after elective fetal reduction of one fetus or more, unspecified trimester, fetus 1" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O31.30X2", - "display": "Continuing pregnancy after elective fetal reduction of one fetus or more, unspecified trimester, fetus 2" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O31.30X3", - "display": "Continuing pregnancy after elective fetal reduction of one fetus or more, unspecified trimester, fetus 3" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O31.30X4", - "display": "Continuing pregnancy after elective fetal reduction of one fetus or more, unspecified trimester, fetus 4" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O31.30X5", - "display": "Continuing pregnancy after elective fetal reduction of one fetus or more, unspecified trimester, fetus 5" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O31.30X9", - "display": "Continuing pregnancy after elective fetal reduction of one fetus or more, unspecified trimester, other fetus" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O31.31X0", - "display": "Continuing pregnancy after elective fetal reduction of one fetus or more, first trimester, not applicable or unspecified" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O31.31X1", - "display": "Continuing pregnancy after elective fetal reduction of one fetus or more, first trimester, fetus 1" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O31.31X2", - "display": "Continuing pregnancy after elective fetal reduction of one fetus or more, first trimester, fetus 2" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O31.31X3", - "display": "Continuing pregnancy after elective fetal reduction of one fetus or more, first trimester, fetus 3" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O31.31X4", - "display": "Continuing pregnancy after elective fetal reduction of one fetus or more, first trimester, fetus 4" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O31.31X5", - "display": "Continuing pregnancy after elective fetal reduction of one fetus or more, first trimester, fetus 5" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O31.31X9", - "display": "Continuing pregnancy after elective fetal reduction of one fetus or more, first trimester, other fetus" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O31.32X0", - "display": "Continuing pregnancy after elective fetal reduction of one fetus or more, second trimester, not applicable or unspecified" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O31.32X1", - "display": "Continuing pregnancy after elective fetal reduction of one fetus or more, second trimester, fetus 1" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O31.32X2", - "display": "Continuing pregnancy after elective fetal reduction of one fetus or more, second trimester, fetus 2" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O31.32X3", - "display": "Continuing pregnancy after elective fetal reduction of one fetus or more, second trimester, fetus 3" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O31.32X4", - "display": "Continuing pregnancy after elective fetal reduction of one fetus or more, second trimester, fetus 4" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O31.32X5", - "display": "Continuing pregnancy after elective fetal reduction of one fetus or more, second trimester, fetus 5" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O31.32X9", - "display": "Continuing pregnancy after elective fetal reduction of one fetus or more, second trimester, other fetus" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O31.33X0", - "display": "Continuing pregnancy after elective fetal reduction of one fetus or more, third trimester, not applicable or unspecified" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O31.33X1", - "display": "Continuing pregnancy after elective fetal reduction of one fetus or more, third trimester, fetus 1" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O31.33X2", - "display": "Continuing pregnancy after elective fetal reduction of one fetus or more, third trimester, fetus 2" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O31.33X3", - "display": "Continuing pregnancy after elective fetal reduction of one fetus or more, third trimester, fetus 3" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O31.33X4", - "display": "Continuing pregnancy after elective fetal reduction of one fetus or more, third trimester, fetus 4" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O31.33X5", - "display": "Continuing pregnancy after elective fetal reduction of one fetus or more, third trimester, fetus 5" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O31.33X9", - "display": "Continuing pregnancy after elective fetal reduction of one fetus or more, third trimester, other fetus" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O31.8X10", - "display": "Other complications specific to multiple gestation, first trimester, not applicable or unspecified" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O31.8X11", - "display": "Other complications specific to multiple gestation, first trimester, fetus 1" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O31.8X12", - "display": "Other complications specific to multiple gestation, first trimester, fetus 2" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O31.8X13", - "display": "Other complications specific to multiple gestation, first trimester, fetus 3" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O31.8X14", - "display": "Other complications specific to multiple gestation, first trimester, fetus 4" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O31.8X15", - "display": "Other complications specific to multiple gestation, first trimester, fetus 5" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O31.8X19", - "display": "Other complications specific to multiple gestation, first trimester, other fetus" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O31.8X20", - "display": "Other complications specific to multiple gestation, second trimester, not applicable or unspecified" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O31.8X21", - "display": "Other complications specific to multiple gestation, second trimester, fetus 1" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O31.8X22", - "display": "Other complications specific to multiple gestation, second trimester, fetus 2" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O31.8X23", - "display": "Other complications specific to multiple gestation, second trimester, fetus 3" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O31.8X24", - "display": "Other complications specific to multiple gestation, second trimester, fetus 4" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O31.8X25", - "display": "Other complications specific to multiple gestation, second trimester, fetus 5" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O31.8X29", - "display": "Other complications specific to multiple gestation, second trimester, other fetus" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O31.8X30", - "display": "Other complications specific to multiple gestation, third trimester, not applicable or unspecified" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O31.8X31", - "display": "Other complications specific to multiple gestation, third trimester, fetus 1" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O31.8X32", - "display": "Other complications specific to multiple gestation, third trimester, fetus 2" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O31.8X33", - "display": "Other complications specific to multiple gestation, third trimester, fetus 3" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O31.8X34", - "display": "Other complications specific to multiple gestation, third trimester, fetus 4" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O31.8X35", - "display": "Other complications specific to multiple gestation, third trimester, fetus 5" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O31.8X39", - "display": "Other complications specific to multiple gestation, third trimester, other fetus" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O31.8X90", - "display": "Other complications specific to multiple gestation, unspecified trimester, not applicable or unspecified" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O31.8X91", - "display": "Other complications specific to multiple gestation, unspecified trimester, fetus 1" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O31.8X92", - "display": "Other complications specific to multiple gestation, unspecified trimester, fetus 2" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O31.8X93", - "display": "Other complications specific to multiple gestation, unspecified trimester, fetus 3" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O31.8X94", - "display": "Other complications specific to multiple gestation, unspecified trimester, fetus 4" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O31.8X95", - "display": "Other complications specific to multiple gestation, unspecified trimester, fetus 5" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O31.8X99", - "display": "Other complications specific to multiple gestation, unspecified trimester, other fetus" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O32.0XX0", - "display": "Maternal care for unstable lie, not applicable or unspecified" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O32.0XX1", - "display": "Maternal care for unstable lie, fetus 1" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O32.0XX2", - "display": "Maternal care for unstable lie, fetus 2" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O32.0XX3", - "display": "Maternal care for unstable lie, fetus 3" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O32.0XX4", - "display": "Maternal care for unstable lie, fetus 4" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O32.0XX5", - "display": "Maternal care for unstable lie, fetus 5" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O32.0XX9", - "display": "Maternal care for unstable lie, other fetus" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O32.1XX0", - "display": "Maternal care for breech presentation, not applicable or unspecified" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O32.1XX1", - "display": "Maternal care for breech presentation, fetus 1" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O32.1XX2", - "display": "Maternal care for breech presentation, fetus 2" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O32.1XX3", - "display": "Maternal care for breech presentation, fetus 3" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O32.1XX4", - "display": "Maternal care for breech presentation, fetus 4" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O32.1XX5", - "display": "Maternal care for breech presentation, fetus 5" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O32.1XX9", - "display": "Maternal care for breech presentation, other fetus" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O32.2XX0", - "display": "Maternal care for transverse and oblique lie, not applicable or unspecified" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O32.2XX1", - "display": "Maternal care for transverse and oblique lie, fetus 1" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O32.2XX2", - "display": "Maternal care for transverse and oblique lie, fetus 2" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O32.2XX3", - "display": "Maternal care for transverse and oblique lie, fetus 3" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O32.2XX4", - "display": "Maternal care for transverse and oblique lie, fetus 4" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O32.2XX5", - "display": "Maternal care for transverse and oblique lie, fetus 5" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O32.2XX9", - "display": "Maternal care for transverse and oblique lie, other fetus" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O32.3XX0", - "display": "Maternal care for face, brow and chin presentation, not applicable or unspecified" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O32.3XX1", - "display": "Maternal care for face, brow and chin presentation, fetus 1" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O32.3XX2", - "display": "Maternal care for face, brow and chin presentation, fetus 2" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O32.3XX3", - "display": "Maternal care for face, brow and chin presentation, fetus 3" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O32.3XX4", - "display": "Maternal care for face, brow and chin presentation, fetus 4" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O32.3XX5", - "display": "Maternal care for face, brow and chin presentation, fetus 5" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O32.3XX9", - "display": "Maternal care for face, brow and chin presentation, other fetus" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O32.4XX0", - "display": "Maternal care for high head at term, not applicable or unspecified" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O32.4XX1", - "display": "Maternal care for high head at term, fetus 1" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O32.4XX2", - "display": "Maternal care for high head at term, fetus 2" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O32.4XX3", - "display": "Maternal care for high head at term, fetus 3" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O32.4XX4", - "display": "Maternal care for high head at term, fetus 4" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O32.4XX5", - "display": "Maternal care for high head at term, fetus 5" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O32.4XX9", - "display": "Maternal care for high head at term, other fetus" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O32.6XX0", - "display": "Maternal care for compound presentation, not applicable or unspecified" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O32.6XX1", - "display": "Maternal care for compound presentation, fetus 1" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O32.6XX2", - "display": "Maternal care for compound presentation, fetus 2" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O32.6XX3", - "display": "Maternal care for compound presentation, fetus 3" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O32.6XX4", - "display": "Maternal care for compound presentation, fetus 4" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O32.6XX5", - "display": "Maternal care for compound presentation, fetus 5" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O32.6XX9", - "display": "Maternal care for compound presentation, other fetus" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O32.8XX0", - "display": "Maternal care for other malpresentation of fetus, not applicable or unspecified" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O32.8XX1", - "display": "Maternal care for other malpresentation of fetus, fetus 1" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O32.8XX2", - "display": "Maternal care for other malpresentation of fetus, fetus 2" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O32.8XX3", - "display": "Maternal care for other malpresentation of fetus, fetus 3" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O32.8XX4", - "display": "Maternal care for other malpresentation of fetus, fetus 4" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O32.8XX5", - "display": "Maternal care for other malpresentation of fetus, fetus 5" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O32.8XX9", - "display": "Maternal care for other malpresentation of fetus, other fetus" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O32.9XX0", - "display": "Maternal care for malpresentation of fetus, unspecified, not applicable or unspecified" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O32.9XX1", - "display": "Maternal care for malpresentation of fetus, unspecified, fetus 1" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O32.9XX2", - "display": "Maternal care for malpresentation of fetus, unspecified, fetus 2" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O32.9XX3", - "display": "Maternal care for malpresentation of fetus, unspecified, fetus 3" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O32.9XX4", - "display": "Maternal care for malpresentation of fetus, unspecified, fetus 4" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O32.9XX5", - "display": "Maternal care for malpresentation of fetus, unspecified, fetus 5" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O32.9XX9", - "display": "Maternal care for malpresentation of fetus, unspecified, other fetus" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O33.0", - "display": "Maternal care for disproportion due to deformity of maternal pelvic bones" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O33.1", - "display": "Maternal care for disproportion due to generally contracted pelvis" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O33.2", - "display": "Maternal care for disproportion due to inlet contraction of pelvis" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O33.3XX0", - "display": "Maternal care for disproportion due to outlet contraction of pelvis, not applicable or unspecified" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O33.3XX1", - "display": "Maternal care for disproportion due to outlet contraction of pelvis, fetus 1" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O33.3XX2", - "display": "Maternal care for disproportion due to outlet contraction of pelvis, fetus 2" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O33.3XX3", - "display": "Maternal care for disproportion due to outlet contraction of pelvis, fetus 3" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O33.3XX4", - "display": "Maternal care for disproportion due to outlet contraction of pelvis, fetus 4" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O33.3XX5", - "display": "Maternal care for disproportion due to outlet contraction of pelvis, fetus 5" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O33.3XX9", - "display": "Maternal care for disproportion due to outlet contraction of pelvis, other fetus" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O33.4XX0", - "display": "Maternal care for disproportion of mixed maternal and fetal origin, not applicable or unspecified" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O33.4XX1", - "display": "Maternal care for disproportion of mixed maternal and fetal origin, fetus 1" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O33.4XX2", - "display": "Maternal care for disproportion of mixed maternal and fetal origin, fetus 2" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O33.4XX3", - "display": "Maternal care for disproportion of mixed maternal and fetal origin, fetus 3" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O33.4XX4", - "display": "Maternal care for disproportion of mixed maternal and fetal origin, fetus 4" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O33.4XX5", - "display": "Maternal care for disproportion of mixed maternal and fetal origin, fetus 5" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O33.4XX9", - "display": "Maternal care for disproportion of mixed maternal and fetal origin, other fetus" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O33.5XX0", - "display": "Maternal care for disproportion due to unusually large fetus, not applicable or unspecified" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O33.5XX1", - "display": "Maternal care for disproportion due to unusually large fetus, fetus 1" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O33.5XX2", - "display": "Maternal care for disproportion due to unusually large fetus, fetus 2" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O33.5XX3", - "display": "Maternal care for disproportion due to unusually large fetus, fetus 3" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O33.5XX4", - "display": "Maternal care for disproportion due to unusually large fetus, fetus 4" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O33.5XX5", - "display": "Maternal care for disproportion due to unusually large fetus, fetus 5" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O33.5XX9", - "display": "Maternal care for disproportion due to unusually large fetus, other fetus" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O33.6XX0", - "display": "Maternal care for disproportion due to hydrocephalic fetus, not applicable or unspecified" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O33.6XX1", - "display": "Maternal care for disproportion due to hydrocephalic fetus, fetus 1" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O33.6XX2", - "display": "Maternal care for disproportion due to hydrocephalic fetus, fetus 2" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O33.6XX3", - "display": "Maternal care for disproportion due to hydrocephalic fetus, fetus 3" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O33.6XX4", - "display": "Maternal care for disproportion due to hydrocephalic fetus, fetus 4" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O33.6XX5", - "display": "Maternal care for disproportion due to hydrocephalic fetus, fetus 5" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O33.6XX9", - "display": "Maternal care for disproportion due to hydrocephalic fetus, other fetus" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O33.7XX0", - "display": "Maternal care for disproportion due to other fetal deformities, not applicable or unspecified" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O33.7XX1", - "display": "Maternal care for disproportion due to other fetal deformities, fetus 1" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O33.7XX2", - "display": "Maternal care for disproportion due to other fetal deformities, fetus 2" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O33.7XX3", - "display": "Maternal care for disproportion due to other fetal deformities, fetus 3" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O33.7XX4", - "display": "Maternal care for disproportion due to other fetal deformities, fetus 4" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O33.7XX5", - "display": "Maternal care for disproportion due to other fetal deformities, fetus 5" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O33.7XX9", - "display": "Maternal care for disproportion due to other fetal deformities, other fetus" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O33.8", - "display": "Maternal care for disproportion of other origin" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O33.9", - "display": "Maternal care for disproportion, unspecified" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O34.00", - "display": "Maternal care for unspecified congenital malformation of uterus, unspecified trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O34.01", - "display": "Maternal care for unspecified congenital malformation of uterus, first trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O34.02", - "display": "Maternal care for unspecified congenital malformation of uterus, second trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O34.03", - "display": "Maternal care for unspecified congenital malformation of uterus, third trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O34.10", - "display": "Maternal care for benign tumor of corpus uteri, unspecified trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O34.11", - "display": "Maternal care for benign tumor of corpus uteri, first trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O34.12", - "display": "Maternal care for benign tumor of corpus uteri, second trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O34.13", - "display": "Maternal care for benign tumor of corpus uteri, third trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O34.211", - "display": "Maternal care for low transverse scar from previous cesarean delivery" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O34.212", - "display": "Maternal care for vertical scar from previous cesarean delivery" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O34.219", - "display": "Maternal care for unspecified type scar from previous cesarean delivery" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O34.29", - "display": "Maternal care due to uterine scar from other previous surgery" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O34.30", - "display": "Maternal care for cervical incompetence, unspecified trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O34.31", - "display": "Maternal care for cervical incompetence, first trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O34.32", - "display": "Maternal care for cervical incompetence, second trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O34.33", - "display": "Maternal care for cervical incompetence, third trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O34.40", - "display": "Maternal care for other abnormalities of cervix, unspecified trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O34.41", - "display": "Maternal care for other abnormalities of cervix, first trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O34.42", - "display": "Maternal care for other abnormalities of cervix, second trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O34.43", - "display": "Maternal care for other abnormalities of cervix, third trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O34.511", - "display": "Maternal care for incarceration of gravid uterus, first trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O34.512", - "display": "Maternal care for incarceration of gravid uterus, second trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O34.513", - "display": "Maternal care for incarceration of gravid uterus, third trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O34.519", - "display": "Maternal care for incarceration of gravid uterus, unspecified trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O34.521", - "display": "Maternal care for prolapse of gravid uterus, first trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O34.522", - "display": "Maternal care for prolapse of gravid uterus, second trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O34.523", - "display": "Maternal care for prolapse of gravid uterus, third trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O34.529", - "display": "Maternal care for prolapse of gravid uterus, unspecified trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O34.531", - "display": "Maternal care for retroversion of gravid uterus, first trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O34.532", - "display": "Maternal care for retroversion of gravid uterus, second trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O34.533", - "display": "Maternal care for retroversion of gravid uterus, third trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O34.539", - "display": "Maternal care for retroversion of gravid uterus, unspecified trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O34.591", - "display": "Maternal care for other abnormalities of gravid uterus, first trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O34.592", - "display": "Maternal care for other abnormalities of gravid uterus, second trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O34.593", - "display": "Maternal care for other abnormalities of gravid uterus, third trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O34.599", - "display": "Maternal care for other abnormalities of gravid uterus, unspecified trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O34.60", - "display": "Maternal care for abnormality of vagina, unspecified trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O34.61", - "display": "Maternal care for abnormality of vagina, first trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O34.62", - "display": "Maternal care for abnormality of vagina, second trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O34.63", - "display": "Maternal care for abnormality of vagina, third trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O34.70", - "display": "Maternal care for abnormality of vulva and perineum, unspecified trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O34.71", - "display": "Maternal care for abnormality of vulva and perineum, first trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O34.72", - "display": "Maternal care for abnormality of vulva and perineum, second trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O34.73", - "display": "Maternal care for abnormality of vulva and perineum, third trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O34.80", - "display": "Maternal care for other abnormalities of pelvic organs, unspecified trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O34.81", - "display": "Maternal care for other abnormalities of pelvic organs, first trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O34.82", - "display": "Maternal care for other abnormalities of pelvic organs, second trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O34.83", - "display": "Maternal care for other abnormalities of pelvic organs, third trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O34.90", - "display": "Maternal care for abnormality of pelvic organ, unspecified, unspecified trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O34.91", - "display": "Maternal care for abnormality of pelvic organ, unspecified, first trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O34.92", - "display": "Maternal care for abnormality of pelvic organ, unspecified, second trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O34.93", - "display": "Maternal care for abnormality of pelvic organ, unspecified, third trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O35.2XX0", - "display": "Maternal care for (suspected) hereditary disease in fetus, not applicable or unspecified" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O35.2XX1", - "display": "Maternal care for (suspected) hereditary disease in fetus, fetus 1" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O35.2XX2", - "display": "Maternal care for (suspected) hereditary disease in fetus, fetus 2" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O35.2XX3", - "display": "Maternal care for (suspected) hereditary disease in fetus, fetus 3" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O35.2XX4", - "display": "Maternal care for (suspected) hereditary disease in fetus, fetus 4" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O35.2XX5", - "display": "Maternal care for (suspected) hereditary disease in fetus, fetus 5" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O35.2XX9", - "display": "Maternal care for (suspected) hereditary disease in fetus, other fetus" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O35.3XX0", - "display": "Maternal care for (suspected) damage to fetus from viral disease in mother, not applicable or unspecified" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O35.3XX1", - "display": "Maternal care for (suspected) damage to fetus from viral disease in mother, fetus 1" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O35.3XX2", - "display": "Maternal care for (suspected) damage to fetus from viral disease in mother, fetus 2" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O35.3XX3", - "display": "Maternal care for (suspected) damage to fetus from viral disease in mother, fetus 3" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O35.3XX4", - "display": "Maternal care for (suspected) damage to fetus from viral disease in mother, fetus 4" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O35.3XX5", - "display": "Maternal care for (suspected) damage to fetus from viral disease in mother, fetus 5" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O35.3XX9", - "display": "Maternal care for (suspected) damage to fetus from viral disease in mother, other fetus" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O35.4XX0", - "display": "Maternal care for (suspected) damage to fetus from alcohol, not applicable or unspecified" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O35.4XX1", - "display": "Maternal care for (suspected) damage to fetus from alcohol, fetus 1" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O35.4XX2", - "display": "Maternal care for (suspected) damage to fetus from alcohol, fetus 2" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O35.4XX3", - "display": "Maternal care for (suspected) damage to fetus from alcohol, fetus 3" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O35.4XX4", - "display": "Maternal care for (suspected) damage to fetus from alcohol, fetus 4" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O35.4XX5", - "display": "Maternal care for (suspected) damage to fetus from alcohol, fetus 5" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O35.4XX9", - "display": "Maternal care for (suspected) damage to fetus from alcohol, other fetus" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O35.5XX0", - "display": "Maternal care for (suspected) damage to fetus by drugs, not applicable or unspecified" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O35.5XX1", - "display": "Maternal care for (suspected) damage to fetus by drugs, fetus 1" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O35.5XX2", - "display": "Maternal care for (suspected) damage to fetus by drugs, fetus 2" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O35.5XX3", - "display": "Maternal care for (suspected) damage to fetus by drugs, fetus 3" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O35.5XX4", - "display": "Maternal care for (suspected) damage to fetus by drugs, fetus 4" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O35.5XX5", - "display": "Maternal care for (suspected) damage to fetus by drugs, fetus 5" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O35.5XX9", - "display": "Maternal care for (suspected) damage to fetus by drugs, other fetus" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O35.6XX0", - "display": "Maternal care for (suspected) damage to fetus by radiation, not applicable or unspecified" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O35.6XX1", - "display": "Maternal care for (suspected) damage to fetus by radiation, fetus 1" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O35.6XX2", - "display": "Maternal care for (suspected) damage to fetus by radiation, fetus 2" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O35.6XX3", - "display": "Maternal care for (suspected) damage to fetus by radiation, fetus 3" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O35.6XX4", - "display": "Maternal care for (suspected) damage to fetus by radiation, fetus 4" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O35.6XX5", - "display": "Maternal care for (suspected) damage to fetus by radiation, fetus 5" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O35.6XX9", - "display": "Maternal care for (suspected) damage to fetus by radiation, other fetus" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O35.7XX0", - "display": "Maternal care for (suspected) damage to fetus by other medical procedures, not applicable or unspecified" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O35.7XX1", - "display": "Maternal care for (suspected) damage to fetus by other medical procedures, fetus 1" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O35.7XX2", - "display": "Maternal care for (suspected) damage to fetus by other medical procedures, fetus 2" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O35.7XX3", - "display": "Maternal care for (suspected) damage to fetus by other medical procedures, fetus 3" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O35.7XX4", - "display": "Maternal care for (suspected) damage to fetus by other medical procedures, fetus 4" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O35.7XX5", - "display": "Maternal care for (suspected) damage to fetus by other medical procedures, fetus 5" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O35.7XX9", - "display": "Maternal care for (suspected) damage to fetus by other medical procedures, other fetus" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O35.8XX0", - "display": "Maternal care for other (suspected) fetal abnormality and damage, not applicable or unspecified" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O35.8XX1", - "display": "Maternal care for other (suspected) fetal abnormality and damage, fetus 1" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O35.8XX2", - "display": "Maternal care for other (suspected) fetal abnormality and damage, fetus 2" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O35.8XX3", - "display": "Maternal care for other (suspected) fetal abnormality and damage, fetus 3" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O35.8XX4", - "display": "Maternal care for other (suspected) fetal abnormality and damage, fetus 4" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O35.8XX5", - "display": "Maternal care for other (suspected) fetal abnormality and damage, fetus 5" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O35.8XX9", - "display": "Maternal care for other (suspected) fetal abnormality and damage, other fetus" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O35.9XX0", - "display": "Maternal care for (suspected) fetal abnormality and damage, unspecified, not applicable or unspecified" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O35.9XX1", - "display": "Maternal care for (suspected) fetal abnormality and damage, unspecified, fetus 1" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O35.9XX2", - "display": "Maternal care for (suspected) fetal abnormality and damage, unspecified, fetus 2" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O35.9XX3", - "display": "Maternal care for (suspected) fetal abnormality and damage, unspecified, fetus 3" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O35.9XX4", - "display": "Maternal care for (suspected) fetal abnormality and damage, unspecified, fetus 4" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O35.9XX5", - "display": "Maternal care for (suspected) fetal abnormality and damage, unspecified, fetus 5" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O35.9XX9", - "display": "Maternal care for (suspected) fetal abnormality and damage, unspecified, other fetus" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O36.0110", - "display": "Maternal care for anti-D [Rh] antibodies, first trimester, not applicable or unspecified" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O36.0111", - "display": "Maternal care for anti-D [Rh] antibodies, first trimester, fetus 1" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O36.0112", - "display": "Maternal care for anti-D [Rh] antibodies, first trimester, fetus 2" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O36.0113", - "display": "Maternal care for anti-D [Rh] antibodies, first trimester, fetus 3" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O36.0114", - "display": "Maternal care for anti-D [Rh] antibodies, first trimester, fetus 4" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O36.0115", - "display": "Maternal care for anti-D [Rh] antibodies, first trimester, fetus 5" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O36.0119", - "display": "Maternal care for anti-D [Rh] antibodies, first trimester, other fetus" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O36.0120", - "display": "Maternal care for anti-D [Rh] antibodies, second trimester, not applicable or unspecified" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O36.0121", - "display": "Maternal care for anti-D [Rh] antibodies, second trimester, fetus 1" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O36.0122", - "display": "Maternal care for anti-D [Rh] antibodies, second trimester, fetus 2" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O36.0123", - "display": "Maternal care for anti-D [Rh] antibodies, second trimester, fetus 3" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O36.0124", - "display": "Maternal care for anti-D [Rh] antibodies, second trimester, fetus 4" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O36.0125", - "display": "Maternal care for anti-D [Rh] antibodies, second trimester, fetus 5" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O36.0129", - "display": "Maternal care for anti-D [Rh] antibodies, second trimester, other fetus" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O36.0130", - "display": "Maternal care for anti-D [Rh] antibodies, third trimester, not applicable or unspecified" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O36.0131", - "display": "Maternal care for anti-D [Rh] antibodies, third trimester, fetus 1" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O36.0132", - "display": "Maternal care for anti-D [Rh] antibodies, third trimester, fetus 2" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O36.0133", - "display": "Maternal care for anti-D [Rh] antibodies, third trimester, fetus 3" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O36.0134", - "display": "Maternal care for anti-D [Rh] antibodies, third trimester, fetus 4" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O36.0135", - "display": "Maternal care for anti-D [Rh] antibodies, third trimester, fetus 5" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O36.0139", - "display": "Maternal care for anti-D [Rh] antibodies, third trimester, other fetus" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O36.0190", - "display": "Maternal care for anti-D [Rh] antibodies, unspecified trimester, not applicable or unspecified" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O36.0191", - "display": "Maternal care for anti-D [Rh] antibodies, unspecified trimester, fetus 1" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O36.0192", - "display": "Maternal care for anti-D [Rh] antibodies, unspecified trimester, fetus 2" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O36.0193", - "display": "Maternal care for anti-D [Rh] antibodies, unspecified trimester, fetus 3" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O36.0194", - "display": "Maternal care for anti-D [Rh] antibodies, unspecified trimester, fetus 4" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O36.0195", - "display": "Maternal care for anti-D [Rh] antibodies, unspecified trimester, fetus 5" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O36.0199", - "display": "Maternal care for anti-D [Rh] antibodies, unspecified trimester, other fetus" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O36.0910", - "display": "Maternal care for other rhesus isoimmunization, first trimester, not applicable or unspecified" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O36.0911", - "display": "Maternal care for other rhesus isoimmunization, first trimester, fetus 1" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O36.0912", - "display": "Maternal care for other rhesus isoimmunization, first trimester, fetus 2" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O36.0913", - "display": "Maternal care for other rhesus isoimmunization, first trimester, fetus 3" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O36.0914", - "display": "Maternal care for other rhesus isoimmunization, first trimester, fetus 4" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O36.0915", - "display": "Maternal care for other rhesus isoimmunization, first trimester, fetus 5" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O36.0919", - "display": "Maternal care for other rhesus isoimmunization, first trimester, other fetus" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O36.0920", - "display": "Maternal care for other rhesus isoimmunization, second trimester, not applicable or unspecified" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O36.0921", - "display": "Maternal care for other rhesus isoimmunization, second trimester, fetus 1" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O36.0922", - "display": "Maternal care for other rhesus isoimmunization, second trimester, fetus 2" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O36.0923", - "display": "Maternal care for other rhesus isoimmunization, second trimester, fetus 3" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O36.0924", - "display": "Maternal care for other rhesus isoimmunization, second trimester, fetus 4" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O36.0925", - "display": "Maternal care for other rhesus isoimmunization, second trimester, fetus 5" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O36.0929", - "display": "Maternal care for other rhesus isoimmunization, second trimester, other fetus" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O36.0930", - "display": "Maternal care for other rhesus isoimmunization, third trimester, not applicable or unspecified" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O36.0931", - "display": "Maternal care for other rhesus isoimmunization, third trimester, fetus 1" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O36.0932", - "display": "Maternal care for other rhesus isoimmunization, third trimester, fetus 2" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O36.0933", - "display": "Maternal care for other rhesus isoimmunization, third trimester, fetus 3" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O36.0934", - "display": "Maternal care for other rhesus isoimmunization, third trimester, fetus 4" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O36.0935", - "display": "Maternal care for other rhesus isoimmunization, third trimester, fetus 5" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O36.0939", - "display": "Maternal care for other rhesus isoimmunization, third trimester, other fetus" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O36.0990", - "display": "Maternal care for other rhesus isoimmunization, unspecified trimester, not applicable or unspecified" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O36.0991", - "display": "Maternal care for other rhesus isoimmunization, unspecified trimester, fetus 1" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O36.0992", - "display": "Maternal care for other rhesus isoimmunization, unspecified trimester, fetus 2" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O36.0993", - "display": "Maternal care for other rhesus isoimmunization, unspecified trimester, fetus 3" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O36.0994", - "display": "Maternal care for other rhesus isoimmunization, unspecified trimester, fetus 4" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O36.0995", - "display": "Maternal care for other rhesus isoimmunization, unspecified trimester, fetus 5" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O36.0999", - "display": "Maternal care for other rhesus isoimmunization, unspecified trimester, other fetus" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O36.1110", - "display": "Maternal care for Anti-A sensitization, first trimester, not applicable or unspecified" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O36.1111", - "display": "Maternal care for Anti-A sensitization, first trimester, fetus 1" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O36.1112", - "display": "Maternal care for Anti-A sensitization, first trimester, fetus 2" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O36.1113", - "display": "Maternal care for Anti-A sensitization, first trimester, fetus 3" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O36.1114", - "display": "Maternal care for Anti-A sensitization, first trimester, fetus 4" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O36.1115", - "display": "Maternal care for Anti-A sensitization, first trimester, fetus 5" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O36.1119", - "display": "Maternal care for Anti-A sensitization, first trimester, other fetus" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O36.1120", - "display": "Maternal care for Anti-A sensitization, second trimester, not applicable or unspecified" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O36.1121", - "display": "Maternal care for Anti-A sensitization, second trimester, fetus 1" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O36.1122", - "display": "Maternal care for Anti-A sensitization, second trimester, fetus 2" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O36.1123", - "display": "Maternal care for Anti-A sensitization, second trimester, fetus 3" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O36.1124", - "display": "Maternal care for Anti-A sensitization, second trimester, fetus 4" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O36.1125", - "display": "Maternal care for Anti-A sensitization, second trimester, fetus 5" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O36.1129", - "display": "Maternal care for Anti-A sensitization, second trimester, other fetus" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O36.1130", - "display": "Maternal care for Anti-A sensitization, third trimester, not applicable or unspecified" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O36.1131", - "display": "Maternal care for Anti-A sensitization, third trimester, fetus 1" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O36.1132", - "display": "Maternal care for Anti-A sensitization, third trimester, fetus 2" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O36.1133", - "display": "Maternal care for Anti-A sensitization, third trimester, fetus 3" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O36.1134", - "display": "Maternal care for Anti-A sensitization, third trimester, fetus 4" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O36.1135", - "display": "Maternal care for Anti-A sensitization, third trimester, fetus 5" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O36.1139", - "display": "Maternal care for Anti-A sensitization, third trimester, other fetus" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O36.1190", - "display": "Maternal care for Anti-A sensitization, unspecified trimester, not applicable or unspecified" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O36.1191", - "display": "Maternal care for Anti-A sensitization, unspecified trimester, fetus 1" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O36.1192", - "display": "Maternal care for Anti-A sensitization, unspecified trimester, fetus 2" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O36.1193", - "display": "Maternal care for Anti-A sensitization, unspecified trimester, fetus 3" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O36.1194", - "display": "Maternal care for Anti-A sensitization, unspecified trimester, fetus 4" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O36.1195", - "display": "Maternal care for Anti-A sensitization, unspecified trimester, fetus 5" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O36.1199", - "display": "Maternal care for Anti-A sensitization, unspecified trimester, other fetus" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O36.1910", - "display": "Maternal care for other isoimmunization, first trimester, not applicable or unspecified" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O36.1911", - "display": "Maternal care for other isoimmunization, first trimester, fetus 1" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O36.1912", - "display": "Maternal care for other isoimmunization, first trimester, fetus 2" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O36.1913", - "display": "Maternal care for other isoimmunization, first trimester, fetus 3" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O36.1914", - "display": "Maternal care for other isoimmunization, first trimester, fetus 4" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O36.1915", - "display": "Maternal care for other isoimmunization, first trimester, fetus 5" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O36.1919", - "display": "Maternal care for other isoimmunization, first trimester, other fetus" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O36.1920", - "display": "Maternal care for other isoimmunization, second trimester, not applicable or unspecified" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O36.1921", - "display": "Maternal care for other isoimmunization, second trimester, fetus 1" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O36.1922", - "display": "Maternal care for other isoimmunization, second trimester, fetus 2" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O36.1923", - "display": "Maternal care for other isoimmunization, second trimester, fetus 3" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O36.1924", - "display": "Maternal care for other isoimmunization, second trimester, fetus 4" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O36.1925", - "display": "Maternal care for other isoimmunization, second trimester, fetus 5" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O36.1929", - "display": "Maternal care for other isoimmunization, second trimester, other fetus" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O36.1930", - "display": "Maternal care for other isoimmunization, third trimester, not applicable or unspecified" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O36.1931", - "display": "Maternal care for other isoimmunization, third trimester, fetus 1" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O36.1932", - "display": "Maternal care for other isoimmunization, third trimester, fetus 2" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O36.1933", - "display": "Maternal care for other isoimmunization, third trimester, fetus 3" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O36.1934", - "display": "Maternal care for other isoimmunization, third trimester, fetus 4" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O36.1935", - "display": "Maternal care for other isoimmunization, third trimester, fetus 5" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O36.1939", - "display": "Maternal care for other isoimmunization, third trimester, other fetus" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O36.1990", - "display": "Maternal care for other isoimmunization, unspecified trimester, not applicable or unspecified" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O36.1991", - "display": "Maternal care for other isoimmunization, unspecified trimester, fetus 1" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O36.1992", - "display": "Maternal care for other isoimmunization, unspecified trimester, fetus 2" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O36.1993", - "display": "Maternal care for other isoimmunization, unspecified trimester, fetus 3" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O36.1994", - "display": "Maternal care for other isoimmunization, unspecified trimester, fetus 4" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O36.1995", - "display": "Maternal care for other isoimmunization, unspecified trimester, fetus 5" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O36.1999", - "display": "Maternal care for other isoimmunization, unspecified trimester, other fetus" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O36.20X0", - "display": "Maternal care for hydrops fetalis, unspecified trimester, not applicable or unspecified" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O36.20X1", - "display": "Maternal care for hydrops fetalis, unspecified trimester, fetus 1" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O36.20X2", - "display": "Maternal care for hydrops fetalis, unspecified trimester, fetus 2" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O36.20X3", - "display": "Maternal care for hydrops fetalis, unspecified trimester, fetus 3" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O36.20X4", - "display": "Maternal care for hydrops fetalis, unspecified trimester, fetus 4" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O36.20X5", - "display": "Maternal care for hydrops fetalis, unspecified trimester, fetus 5" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O36.20X9", - "display": "Maternal care for hydrops fetalis, unspecified trimester, other fetus" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O36.21X0", - "display": "Maternal care for hydrops fetalis, first trimester, not applicable or unspecified" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O36.21X1", - "display": "Maternal care for hydrops fetalis, first trimester, fetus 1" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O36.21X2", - "display": "Maternal care for hydrops fetalis, first trimester, fetus 2" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O36.21X3", - "display": "Maternal care for hydrops fetalis, first trimester, fetus 3" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O36.21X4", - "display": "Maternal care for hydrops fetalis, first trimester, fetus 4" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O36.21X5", - "display": "Maternal care for hydrops fetalis, first trimester, fetus 5" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O36.21X9", - "display": "Maternal care for hydrops fetalis, first trimester, other fetus" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O36.22X0", - "display": "Maternal care for hydrops fetalis, second trimester, not applicable or unspecified" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O36.22X1", - "display": "Maternal care for hydrops fetalis, second trimester, fetus 1" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O36.22X2", - "display": "Maternal care for hydrops fetalis, second trimester, fetus 2" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O36.22X3", - "display": "Maternal care for hydrops fetalis, second trimester, fetus 3" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O36.22X4", - "display": "Maternal care for hydrops fetalis, second trimester, fetus 4" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O36.22X5", - "display": "Maternal care for hydrops fetalis, second trimester, fetus 5" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O36.22X9", - "display": "Maternal care for hydrops fetalis, second trimester, other fetus" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O36.23X0", - "display": "Maternal care for hydrops fetalis, third trimester, not applicable or unspecified" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O36.23X1", - "display": "Maternal care for hydrops fetalis, third trimester, fetus 1" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O36.23X2", - "display": "Maternal care for hydrops fetalis, third trimester, fetus 2" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O36.23X3", - "display": "Maternal care for hydrops fetalis, third trimester, fetus 3" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O36.23X4", - "display": "Maternal care for hydrops fetalis, third trimester, fetus 4" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O36.23X5", - "display": "Maternal care for hydrops fetalis, third trimester, fetus 5" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O36.23X9", - "display": "Maternal care for hydrops fetalis, third trimester, other fetus" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O36.4XX0", - "display": "Maternal care for intrauterine death, not applicable or unspecified" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O36.4XX1", - "display": "Maternal care for intrauterine death, fetus 1" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O36.4XX2", - "display": "Maternal care for intrauterine death, fetus 2" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O36.4XX3", - "display": "Maternal care for intrauterine death, fetus 3" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O36.4XX4", - "display": "Maternal care for intrauterine death, fetus 4" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O36.4XX5", - "display": "Maternal care for intrauterine death, fetus 5" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O36.4XX9", - "display": "Maternal care for intrauterine death, other fetus" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O36.5110", - "display": "Maternal care for known or suspected placental insufficiency, first trimester, not applicable or unspecified" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O36.5111", - "display": "Maternal care for known or suspected placental insufficiency, first trimester, fetus 1" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O36.5112", - "display": "Maternal care for known or suspected placental insufficiency, first trimester, fetus 2" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O36.5113", - "display": "Maternal care for known or suspected placental insufficiency, first trimester, fetus 3" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O36.5114", - "display": "Maternal care for known or suspected placental insufficiency, first trimester, fetus 4" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O36.5115", - "display": "Maternal care for known or suspected placental insufficiency, first trimester, fetus 5" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O36.5119", - "display": "Maternal care for known or suspected placental insufficiency, first trimester, other fetus" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O36.5120", - "display": "Maternal care for known or suspected placental insufficiency, second trimester, not applicable or unspecified" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O36.5121", - "display": "Maternal care for known or suspected placental insufficiency, second trimester, fetus 1" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O36.5122", - "display": "Maternal care for known or suspected placental insufficiency, second trimester, fetus 2" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O36.5123", - "display": "Maternal care for known or suspected placental insufficiency, second trimester, fetus 3" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O36.5124", - "display": "Maternal care for known or suspected placental insufficiency, second trimester, fetus 4" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O36.5125", - "display": "Maternal care for known or suspected placental insufficiency, second trimester, fetus 5" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O36.5129", - "display": "Maternal care for known or suspected placental insufficiency, second trimester, other fetus" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O36.5130", - "display": "Maternal care for known or suspected placental insufficiency, third trimester, not applicable or unspecified" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O36.5131", - "display": "Maternal care for known or suspected placental insufficiency, third trimester, fetus 1" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O36.5132", - "display": "Maternal care for known or suspected placental insufficiency, third trimester, fetus 2" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O36.5133", - "display": "Maternal care for known or suspected placental insufficiency, third trimester, fetus 3" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O36.5134", - "display": "Maternal care for known or suspected placental insufficiency, third trimester, fetus 4" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O36.5135", - "display": "Maternal care for known or suspected placental insufficiency, third trimester, fetus 5" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O36.5139", - "display": "Maternal care for known or suspected placental insufficiency, third trimester, other fetus" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O36.5190", - "display": "Maternal care for known or suspected placental insufficiency, unspecified trimester, not applicable or unspecified" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O36.5191", - "display": "Maternal care for known or suspected placental insufficiency, unspecified trimester, fetus 1" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O36.5192", - "display": "Maternal care for known or suspected placental insufficiency, unspecified trimester, fetus 2" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O36.5193", - "display": "Maternal care for known or suspected placental insufficiency, unspecified trimester, fetus 3" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O36.5194", - "display": "Maternal care for known or suspected placental insufficiency, unspecified trimester, fetus 4" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O36.5195", - "display": "Maternal care for known or suspected placental insufficiency, unspecified trimester, fetus 5" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O36.5199", - "display": "Maternal care for known or suspected placental insufficiency, unspecified trimester, other fetus" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O36.5910", - "display": "Maternal care for other known or suspected poor fetal growth, first trimester, not applicable or unspecified" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O36.5911", - "display": "Maternal care for other known or suspected poor fetal growth, first trimester, fetus 1" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O36.5912", - "display": "Maternal care for other known or suspected poor fetal growth, first trimester, fetus 2" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O36.5913", - "display": "Maternal care for other known or suspected poor fetal growth, first trimester, fetus 3" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O36.5914", - "display": "Maternal care for other known or suspected poor fetal growth, first trimester, fetus 4" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O36.5915", - "display": "Maternal care for other known or suspected poor fetal growth, first trimester, fetus 5" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O36.5919", - "display": "Maternal care for other known or suspected poor fetal growth, first trimester, other fetus" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O36.5920", - "display": "Maternal care for other known or suspected poor fetal growth, second trimester, not applicable or unspecified" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O36.5921", - "display": "Maternal care for other known or suspected poor fetal growth, second trimester, fetus 1" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O36.5922", - "display": "Maternal care for other known or suspected poor fetal growth, second trimester, fetus 2" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O36.5923", - "display": "Maternal care for other known or suspected poor fetal growth, second trimester, fetus 3" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O36.5924", - "display": "Maternal care for other known or suspected poor fetal growth, second trimester, fetus 4" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O36.5925", - "display": "Maternal care for other known or suspected poor fetal growth, second trimester, fetus 5" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O36.5929", - "display": "Maternal care for other known or suspected poor fetal growth, second trimester, other fetus" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O36.5930", - "display": "Maternal care for other known or suspected poor fetal growth, third trimester, not applicable or unspecified" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O36.5931", - "display": "Maternal care for other known or suspected poor fetal growth, third trimester, fetus 1" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O36.5932", - "display": "Maternal care for other known or suspected poor fetal growth, third trimester, fetus 2" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O36.5933", - "display": "Maternal care for other known or suspected poor fetal growth, third trimester, fetus 3" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O36.5934", - "display": "Maternal care for other known or suspected poor fetal growth, third trimester, fetus 4" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O36.5935", - "display": "Maternal care for other known or suspected poor fetal growth, third trimester, fetus 5" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O36.5939", - "display": "Maternal care for other known or suspected poor fetal growth, third trimester, other fetus" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O36.5990", - "display": "Maternal care for other known or suspected poor fetal growth, unspecified trimester, not applicable or unspecified" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O36.5991", - "display": "Maternal care for other known or suspected poor fetal growth, unspecified trimester, fetus 1" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O36.5992", - "display": "Maternal care for other known or suspected poor fetal growth, unspecified trimester, fetus 2" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O36.5993", - "display": "Maternal care for other known or suspected poor fetal growth, unspecified trimester, fetus 3" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O36.5994", - "display": "Maternal care for other known or suspected poor fetal growth, unspecified trimester, fetus 4" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O36.5995", - "display": "Maternal care for other known or suspected poor fetal growth, unspecified trimester, fetus 5" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O36.5999", - "display": "Maternal care for other known or suspected poor fetal growth, unspecified trimester, other fetus" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O36.60X0", - "display": "Maternal care for excessive fetal growth, unspecified trimester, not applicable or unspecified" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O36.60X1", - "display": "Maternal care for excessive fetal growth, unspecified trimester, fetus 1" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O36.60X2", - "display": "Maternal care for excessive fetal growth, unspecified trimester, fetus 2" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O36.60X3", - "display": "Maternal care for excessive fetal growth, unspecified trimester, fetus 3" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O36.60X4", - "display": "Maternal care for excessive fetal growth, unspecified trimester, fetus 4" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O36.60X5", - "display": "Maternal care for excessive fetal growth, unspecified trimester, fetus 5" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O36.60X9", - "display": "Maternal care for excessive fetal growth, unspecified trimester, other fetus" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O36.61X0", - "display": "Maternal care for excessive fetal growth, first trimester, not applicable or unspecified" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O36.61X1", - "display": "Maternal care for excessive fetal growth, first trimester, fetus 1" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O36.61X2", - "display": "Maternal care for excessive fetal growth, first trimester, fetus 2" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O36.61X3", - "display": "Maternal care for excessive fetal growth, first trimester, fetus 3" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O36.61X4", - "display": "Maternal care for excessive fetal growth, first trimester, fetus 4" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O36.61X5", - "display": "Maternal care for excessive fetal growth, first trimester, fetus 5" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O36.61X9", - "display": "Maternal care for excessive fetal growth, first trimester, other fetus" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O36.62X0", - "display": "Maternal care for excessive fetal growth, second trimester, not applicable or unspecified" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O36.62X1", - "display": "Maternal care for excessive fetal growth, second trimester, fetus 1" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O36.62X2", - "display": "Maternal care for excessive fetal growth, second trimester, fetus 2" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O36.62X3", - "display": "Maternal care for excessive fetal growth, second trimester, fetus 3" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O36.62X4", - "display": "Maternal care for excessive fetal growth, second trimester, fetus 4" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O36.62X5", - "display": "Maternal care for excessive fetal growth, second trimester, fetus 5" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O36.62X9", - "display": "Maternal care for excessive fetal growth, second trimester, other fetus" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O36.63X0", - "display": "Maternal care for excessive fetal growth, third trimester, not applicable or unspecified" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O36.63X1", - "display": "Maternal care for excessive fetal growth, third trimester, fetus 1" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O36.63X2", - "display": "Maternal care for excessive fetal growth, third trimester, fetus 2" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O36.63X3", - "display": "Maternal care for excessive fetal growth, third trimester, fetus 3" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O36.63X4", - "display": "Maternal care for excessive fetal growth, third trimester, fetus 4" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O36.63X5", - "display": "Maternal care for excessive fetal growth, third trimester, fetus 5" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O36.63X9", - "display": "Maternal care for excessive fetal growth, third trimester, other fetus" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O36.70X0", - "display": "Maternal care for viable fetus in abdominal pregnancy, unspecified trimester, not applicable or unspecified" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O36.70X1", - "display": "Maternal care for viable fetus in abdominal pregnancy, unspecified trimester, fetus 1" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O36.70X2", - "display": "Maternal care for viable fetus in abdominal pregnancy, unspecified trimester, fetus 2" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O36.70X3", - "display": "Maternal care for viable fetus in abdominal pregnancy, unspecified trimester, fetus 3" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O36.70X4", - "display": "Maternal care for viable fetus in abdominal pregnancy, unspecified trimester, fetus 4" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O36.70X5", - "display": "Maternal care for viable fetus in abdominal pregnancy, unspecified trimester, fetus 5" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O36.70X9", - "display": "Maternal care for viable fetus in abdominal pregnancy, unspecified trimester, other fetus" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O36.71X0", - "display": "Maternal care for viable fetus in abdominal pregnancy, first trimester, not applicable or unspecified" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O36.71X1", - "display": "Maternal care for viable fetus in abdominal pregnancy, first trimester, fetus 1" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O36.71X2", - "display": "Maternal care for viable fetus in abdominal pregnancy, first trimester, fetus 2" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O36.71X3", - "display": "Maternal care for viable fetus in abdominal pregnancy, first trimester, fetus 3" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O36.71X4", - "display": "Maternal care for viable fetus in abdominal pregnancy, first trimester, fetus 4" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O36.71X5", - "display": "Maternal care for viable fetus in abdominal pregnancy, first trimester, fetus 5" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O36.71X9", - "display": "Maternal care for viable fetus in abdominal pregnancy, first trimester, other fetus" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O36.72X0", - "display": "Maternal care for viable fetus in abdominal pregnancy, second trimester, not applicable or unspecified" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O36.72X1", - "display": "Maternal care for viable fetus in abdominal pregnancy, second trimester, fetus 1" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O36.72X2", - "display": "Maternal care for viable fetus in abdominal pregnancy, second trimester, fetus 2" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O36.72X3", - "display": "Maternal care for viable fetus in abdominal pregnancy, second trimester, fetus 3" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O36.72X4", - "display": "Maternal care for viable fetus in abdominal pregnancy, second trimester, fetus 4" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O36.72X5", - "display": "Maternal care for viable fetus in abdominal pregnancy, second trimester, fetus 5" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O36.72X9", - "display": "Maternal care for viable fetus in abdominal pregnancy, second trimester, other fetus" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O36.73X0", - "display": "Maternal care for viable fetus in abdominal pregnancy, third trimester, not applicable or unspecified" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O36.73X1", - "display": "Maternal care for viable fetus in abdominal pregnancy, third trimester, fetus 1" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O36.73X2", - "display": "Maternal care for viable fetus in abdominal pregnancy, third trimester, fetus 2" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O36.73X3", - "display": "Maternal care for viable fetus in abdominal pregnancy, third trimester, fetus 3" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O36.73X4", - "display": "Maternal care for viable fetus in abdominal pregnancy, third trimester, fetus 4" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O36.73X5", - "display": "Maternal care for viable fetus in abdominal pregnancy, third trimester, fetus 5" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O36.73X9", - "display": "Maternal care for viable fetus in abdominal pregnancy, third trimester, other fetus" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O36.8120", - "display": "Decreased fetal movements, second trimester, not applicable or unspecified" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O36.8121", - "display": "Decreased fetal movements, second trimester, fetus 1" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O36.8122", - "display": "Decreased fetal movements, second trimester, fetus 2" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O36.8123", - "display": "Decreased fetal movements, second trimester, fetus 3" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O36.8124", - "display": "Decreased fetal movements, second trimester, fetus 4" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O36.8125", - "display": "Decreased fetal movements, second trimester, fetus 5" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O36.8129", - "display": "Decreased fetal movements, second trimester, other fetus" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O36.8130", - "display": "Decreased fetal movements, third trimester, not applicable or unspecified" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O36.8131", - "display": "Decreased fetal movements, third trimester, fetus 1" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O36.8132", - "display": "Decreased fetal movements, third trimester, fetus 2" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O36.8133", - "display": "Decreased fetal movements, third trimester, fetus 3" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O36.8134", - "display": "Decreased fetal movements, third trimester, fetus 4" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O36.8135", - "display": "Decreased fetal movements, third trimester, fetus 5" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O36.8139", - "display": "Decreased fetal movements, third trimester, other fetus" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O36.8190", - "display": "Decreased fetal movements, unspecified trimester, not applicable or unspecified" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O36.8191", - "display": "Decreased fetal movements, unspecified trimester, fetus 1" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O36.8192", - "display": "Decreased fetal movements, unspecified trimester, fetus 2" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O36.8193", - "display": "Decreased fetal movements, unspecified trimester, fetus 3" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O36.8194", - "display": "Decreased fetal movements, unspecified trimester, fetus 4" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O36.8195", - "display": "Decreased fetal movements, unspecified trimester, fetus 5" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O36.8199", - "display": "Decreased fetal movements, unspecified trimester, other fetus" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O36.8210", - "display": "Fetal anemia and thrombocytopenia, first trimester, not applicable or unspecified" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O36.8211", - "display": "Fetal anemia and thrombocytopenia, first trimester, fetus 1" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O36.8212", - "display": "Fetal anemia and thrombocytopenia, first trimester, fetus 2" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O36.8213", - "display": "Fetal anemia and thrombocytopenia, first trimester, fetus 3" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O36.8214", - "display": "Fetal anemia and thrombocytopenia, first trimester, fetus 4" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O36.8215", - "display": "Fetal anemia and thrombocytopenia, first trimester, fetus 5" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O36.8219", - "display": "Fetal anemia and thrombocytopenia, first trimester, other fetus" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O36.8220", - "display": "Fetal anemia and thrombocytopenia, second trimester, not applicable or unspecified" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O36.8221", - "display": "Fetal anemia and thrombocytopenia, second trimester, fetus 1" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O36.8222", - "display": "Fetal anemia and thrombocytopenia, second trimester, fetus 2" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O36.8223", - "display": "Fetal anemia and thrombocytopenia, second trimester, fetus 3" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O36.8224", - "display": "Fetal anemia and thrombocytopenia, second trimester, fetus 4" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O36.8225", - "display": "Fetal anemia and thrombocytopenia, second trimester, fetus 5" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O36.8229", - "display": "Fetal anemia and thrombocytopenia, second trimester, other fetus" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O36.8230", - "display": "Fetal anemia and thrombocytopenia, third trimester, not applicable or unspecified" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O36.8231", - "display": "Fetal anemia and thrombocytopenia, third trimester, fetus 1" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O36.8232", - "display": "Fetal anemia and thrombocytopenia, third trimester, fetus 2" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O36.8233", - "display": "Fetal anemia and thrombocytopenia, third trimester, fetus 3" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O36.8234", - "display": "Fetal anemia and thrombocytopenia, third trimester, fetus 4" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O36.8235", - "display": "Fetal anemia and thrombocytopenia, third trimester, fetus 5" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O36.8239", - "display": "Fetal anemia and thrombocytopenia, third trimester, other fetus" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O36.8290", - "display": "Fetal anemia and thrombocytopenia, unspecified trimester, not applicable or unspecified" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O36.8291", - "display": "Fetal anemia and thrombocytopenia, unspecified trimester, fetus 1" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O36.8292", - "display": "Fetal anemia and thrombocytopenia, unspecified trimester, fetus 2" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O36.8293", - "display": "Fetal anemia and thrombocytopenia, unspecified trimester, fetus 3" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O36.8294", - "display": "Fetal anemia and thrombocytopenia, unspecified trimester, fetus 4" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O36.8295", - "display": "Fetal anemia and thrombocytopenia, unspecified trimester, fetus 5" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O36.8299", - "display": "Fetal anemia and thrombocytopenia, unspecified trimester, other fetus" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O36.8310", - "display": "Maternal care for abnormalities of the fetal heart rate or rhythm, first trimester, not applicable or unspecified" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O36.8311", - "display": "Maternal care for abnormalities of the fetal heart rate or rhythm, first trimester, fetus 1" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O36.8312", - "display": "Maternal care for abnormalities of the fetal heart rate or rhythm, first trimester, fetus 2" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O36.8313", - "display": "Maternal care for abnormalities of the fetal heart rate or rhythm, first trimester, fetus 3" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O36.8314", - "display": "Maternal care for abnormalities of the fetal heart rate or rhythm, first trimester, fetus 4" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O36.8315", - "display": "Maternal care for abnormalities of the fetal heart rate or rhythm, first trimester, fetus 5" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O36.8319", - "display": "Maternal care for abnormalities of the fetal heart rate or rhythm, first trimester, other fetus" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O36.8320", - "display": "Maternal care for abnormalities of the fetal heart rate or rhythm, second trimester, not applicable or unspecified" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O36.8321", - "display": "Maternal care for abnormalities of the fetal heart rate or rhythm, second trimester, fetus 1" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O36.8322", - "display": "Maternal care for abnormalities of the fetal heart rate or rhythm, second trimester, fetus 2" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O36.8323", - "display": "Maternal care for abnormalities of the fetal heart rate or rhythm, second trimester, fetus 3" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O36.8324", - "display": "Maternal care for abnormalities of the fetal heart rate or rhythm, second trimester, fetus 4" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O36.8325", - "display": "Maternal care for abnormalities of the fetal heart rate or rhythm, second trimester, fetus 5" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O36.8329", - "display": "Maternal care for abnormalities of the fetal heart rate or rhythm, second trimester, other fetus" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O36.8330", - "display": "Maternal care for abnormalities of the fetal heart rate or rhythm, third trimester, not applicable or unspecified" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O36.8331", - "display": "Maternal care for abnormalities of the fetal heart rate or rhythm, third trimester, fetus 1" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O36.8332", - "display": "Maternal care for abnormalities of the fetal heart rate or rhythm, third trimester, fetus 2" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O36.8333", - "display": "Maternal care for abnormalities of the fetal heart rate or rhythm, third trimester, fetus 3" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O36.8334", - "display": "Maternal care for abnormalities of the fetal heart rate or rhythm, third trimester, fetus 4" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O36.8335", - "display": "Maternal care for abnormalities of the fetal heart rate or rhythm, third trimester, fetus 5" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O36.8339", - "display": "Maternal care for abnormalities of the fetal heart rate or rhythm, third trimester, other fetus" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O36.8390", - "display": "Maternal care for abnormalities of the fetal heart rate or rhythm, unspecified trimester, not applicable or unspecified" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O36.8391", - "display": "Maternal care for abnormalities of the fetal heart rate or rhythm, unspecified trimester, fetus 1" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O36.8392", - "display": "Maternal care for abnormalities of the fetal heart rate or rhythm, unspecified trimester, fetus 2" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O36.8393", - "display": "Maternal care for abnormalities of the fetal heart rate or rhythm, unspecified trimester, fetus 3" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O36.8394", - "display": "Maternal care for abnormalities of the fetal heart rate or rhythm, unspecified trimester, fetus 4" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O36.8395", - "display": "Maternal care for abnormalities of the fetal heart rate or rhythm, unspecified trimester, fetus 5" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O36.8399", - "display": "Maternal care for abnormalities of the fetal heart rate or rhythm, unspecified trimester, other fetus" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O36.8910", - "display": "Maternal care for other specified fetal problems, first trimester, not applicable or unspecified" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O36.8911", - "display": "Maternal care for other specified fetal problems, first trimester, fetus 1" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O36.8912", - "display": "Maternal care for other specified fetal problems, first trimester, fetus 2" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O36.8913", - "display": "Maternal care for other specified fetal problems, first trimester, fetus 3" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O36.8914", - "display": "Maternal care for other specified fetal problems, first trimester, fetus 4" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O36.8915", - "display": "Maternal care for other specified fetal problems, first trimester, fetus 5" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O36.8919", - "display": "Maternal care for other specified fetal problems, first trimester, other fetus" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O36.8920", - "display": "Maternal care for other specified fetal problems, second trimester, not applicable or unspecified" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O36.8921", - "display": "Maternal care for other specified fetal problems, second trimester, fetus 1" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O36.8922", - "display": "Maternal care for other specified fetal problems, second trimester, fetus 2" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O36.8923", - "display": "Maternal care for other specified fetal problems, second trimester, fetus 3" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O36.8924", - "display": "Maternal care for other specified fetal problems, second trimester, fetus 4" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O36.8925", - "display": "Maternal care for other specified fetal problems, second trimester, fetus 5" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O36.8929", - "display": "Maternal care for other specified fetal problems, second trimester, other fetus" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O36.8930", - "display": "Maternal care for other specified fetal problems, third trimester, not applicable or unspecified" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O36.8931", - "display": "Maternal care for other specified fetal problems, third trimester, fetus 1" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O36.8932", - "display": "Maternal care for other specified fetal problems, third trimester, fetus 2" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O36.8933", - "display": "Maternal care for other specified fetal problems, third trimester, fetus 3" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O36.8934", - "display": "Maternal care for other specified fetal problems, third trimester, fetus 4" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O36.8935", - "display": "Maternal care for other specified fetal problems, third trimester, fetus 5" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O36.8939", - "display": "Maternal care for other specified fetal problems, third trimester, other fetus" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O36.8990", - "display": "Maternal care for other specified fetal problems, unspecified trimester, not applicable or unspecified" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O36.8991", - "display": "Maternal care for other specified fetal problems, unspecified trimester, fetus 1" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O36.8992", - "display": "Maternal care for other specified fetal problems, unspecified trimester, fetus 2" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O36.8993", - "display": "Maternal care for other specified fetal problems, unspecified trimester, fetus 3" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O36.8994", - "display": "Maternal care for other specified fetal problems, unspecified trimester, fetus 4" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O36.8995", - "display": "Maternal care for other specified fetal problems, unspecified trimester, fetus 5" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O36.8999", - "display": "Maternal care for other specified fetal problems, unspecified trimester, other fetus" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O36.90X0", - "display": "Maternal care for fetal problem, unspecified, unspecified trimester, not applicable or unspecified" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O36.90X1", - "display": "Maternal care for fetal problem, unspecified, unspecified trimester, fetus 1" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O36.90X2", - "display": "Maternal care for fetal problem, unspecified, unspecified trimester, fetus 2" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O36.90X3", - "display": "Maternal care for fetal problem, unspecified, unspecified trimester, fetus 3" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O36.90X4", - "display": "Maternal care for fetal problem, unspecified, unspecified trimester, fetus 4" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O36.90X5", - "display": "Maternal care for fetal problem, unspecified, unspecified trimester, fetus 5" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O36.90X9", - "display": "Maternal care for fetal problem, unspecified, unspecified trimester, other fetus" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O36.91X0", - "display": "Maternal care for fetal problem, unspecified, first trimester, not applicable or unspecified" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O36.91X1", - "display": "Maternal care for fetal problem, unspecified, first trimester, fetus 1" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O36.91X2", - "display": "Maternal care for fetal problem, unspecified, first trimester, fetus 2" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O36.91X3", - "display": "Maternal care for fetal problem, unspecified, first trimester, fetus 3" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O36.91X4", - "display": "Maternal care for fetal problem, unspecified, first trimester, fetus 4" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O36.91X5", - "display": "Maternal care for fetal problem, unspecified, first trimester, fetus 5" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O36.91X9", - "display": "Maternal care for fetal problem, unspecified, first trimester, other fetus" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O36.92X0", - "display": "Maternal care for fetal problem, unspecified, second trimester, not applicable or unspecified" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O36.92X1", - "display": "Maternal care for fetal problem, unspecified, second trimester, fetus 1" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O36.92X2", - "display": "Maternal care for fetal problem, unspecified, second trimester, fetus 2" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O36.92X3", - "display": "Maternal care for fetal problem, unspecified, second trimester, fetus 3" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O36.92X4", - "display": "Maternal care for fetal problem, unspecified, second trimester, fetus 4" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O36.92X5", - "display": "Maternal care for fetal problem, unspecified, second trimester, fetus 5" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O36.92X9", - "display": "Maternal care for fetal problem, unspecified, second trimester, other fetus" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O36.93X0", - "display": "Maternal care for fetal problem, unspecified, third trimester, not applicable or unspecified" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O36.93X1", - "display": "Maternal care for fetal problem, unspecified, third trimester, fetus 1" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O36.93X2", - "display": "Maternal care for fetal problem, unspecified, third trimester, fetus 2" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O36.93X3", - "display": "Maternal care for fetal problem, unspecified, third trimester, fetus 3" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O36.93X4", - "display": "Maternal care for fetal problem, unspecified, third trimester, fetus 4" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O36.93X5", - "display": "Maternal care for fetal problem, unspecified, third trimester, fetus 5" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O36.93X9", - "display": "Maternal care for fetal problem, unspecified, third trimester, other fetus" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O40.1XX0", - "display": "Polyhydramnios, first trimester, not applicable or unspecified" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O40.1XX1", - "display": "Polyhydramnios, first trimester, fetus 1" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O40.1XX2", - "display": "Polyhydramnios, first trimester, fetus 2" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O40.1XX3", - "display": "Polyhydramnios, first trimester, fetus 3" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O40.1XX4", - "display": "Polyhydramnios, first trimester, fetus 4" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O40.1XX5", - "display": "Polyhydramnios, first trimester, fetus 5" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O40.1XX9", - "display": "Polyhydramnios, first trimester, other fetus" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O40.2XX0", - "display": "Polyhydramnios, second trimester, not applicable or unspecified" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O40.2XX1", - "display": "Polyhydramnios, second trimester, fetus 1" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O40.2XX2", - "display": "Polyhydramnios, second trimester, fetus 2" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O40.2XX3", - "display": "Polyhydramnios, second trimester, fetus 3" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O40.2XX4", - "display": "Polyhydramnios, second trimester, fetus 4" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O40.2XX5", - "display": "Polyhydramnios, second trimester, fetus 5" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O40.2XX9", - "display": "Polyhydramnios, second trimester, other fetus" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O40.3XX0", - "display": "Polyhydramnios, third trimester, not applicable or unspecified" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O40.3XX1", - "display": "Polyhydramnios, third trimester, fetus 1" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O40.3XX2", - "display": "Polyhydramnios, third trimester, fetus 2" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O40.3XX3", - "display": "Polyhydramnios, third trimester, fetus 3" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O40.3XX4", - "display": "Polyhydramnios, third trimester, fetus 4" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O40.3XX5", - "display": "Polyhydramnios, third trimester, fetus 5" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O40.3XX9", - "display": "Polyhydramnios, third trimester, other fetus" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O40.9XX0", - "display": "Polyhydramnios, unspecified trimester, not applicable or unspecified" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O40.9XX1", - "display": "Polyhydramnios, unspecified trimester, fetus 1" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O40.9XX2", - "display": "Polyhydramnios, unspecified trimester, fetus 2" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O40.9XX3", - "display": "Polyhydramnios, unspecified trimester, fetus 3" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O40.9XX4", - "display": "Polyhydramnios, unspecified trimester, fetus 4" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O40.9XX5", - "display": "Polyhydramnios, unspecified trimester, fetus 5" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O40.9XX9", - "display": "Polyhydramnios, unspecified trimester, other fetus" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O41.00X0", - "display": "Oligohydramnios, unspecified trimester, not applicable or unspecified" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O41.00X1", - "display": "Oligohydramnios, unspecified trimester, fetus 1" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O41.00X2", - "display": "Oligohydramnios, unspecified trimester, fetus 2" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O41.00X3", - "display": "Oligohydramnios, unspecified trimester, fetus 3" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O41.00X4", - "display": "Oligohydramnios, unspecified trimester, fetus 4" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O41.00X5", - "display": "Oligohydramnios, unspecified trimester, fetus 5" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O41.00X9", - "display": "Oligohydramnios, unspecified trimester, other fetus" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O41.01X0", - "display": "Oligohydramnios, first trimester, not applicable or unspecified" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O41.01X1", - "display": "Oligohydramnios, first trimester, fetus 1" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O41.01X2", - "display": "Oligohydramnios, first trimester, fetus 2" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O41.01X3", - "display": "Oligohydramnios, first trimester, fetus 3" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O41.01X4", - "display": "Oligohydramnios, first trimester, fetus 4" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O41.01X5", - "display": "Oligohydramnios, first trimester, fetus 5" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O41.01X9", - "display": "Oligohydramnios, first trimester, other fetus" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O41.02X0", - "display": "Oligohydramnios, second trimester, not applicable or unspecified" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O41.02X1", - "display": "Oligohydramnios, second trimester, fetus 1" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O41.02X2", - "display": "Oligohydramnios, second trimester, fetus 2" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O41.02X3", - "display": "Oligohydramnios, second trimester, fetus 3" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O41.02X4", - "display": "Oligohydramnios, second trimester, fetus 4" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O41.02X5", - "display": "Oligohydramnios, second trimester, fetus 5" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O41.02X9", - "display": "Oligohydramnios, second trimester, other fetus" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O41.03X0", - "display": "Oligohydramnios, third trimester, not applicable or unspecified" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O41.03X1", - "display": "Oligohydramnios, third trimester, fetus 1" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O41.03X2", - "display": "Oligohydramnios, third trimester, fetus 2" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O41.03X3", - "display": "Oligohydramnios, third trimester, fetus 3" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O41.03X4", - "display": "Oligohydramnios, third trimester, fetus 4" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O41.03X5", - "display": "Oligohydramnios, third trimester, fetus 5" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O41.03X9", - "display": "Oligohydramnios, third trimester, other fetus" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O41.1010", - "display": "Infection of amniotic sac and membranes, unspecified, first trimester, not applicable or unspecified" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O41.1011", - "display": "Infection of amniotic sac and membranes, unspecified, first trimester, fetus 1" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O41.1012", - "display": "Infection of amniotic sac and membranes, unspecified, first trimester, fetus 2" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O41.1013", - "display": "Infection of amniotic sac and membranes, unspecified, first trimester, fetus 3" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O41.1014", - "display": "Infection of amniotic sac and membranes, unspecified, first trimester, fetus 4" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O41.1015", - "display": "Infection of amniotic sac and membranes, unspecified, first trimester, fetus 5" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O41.1019", - "display": "Infection of amniotic sac and membranes, unspecified, first trimester, other fetus" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O41.1020", - "display": "Infection of amniotic sac and membranes, unspecified, second trimester, not applicable or unspecified" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O41.1021", - "display": "Infection of amniotic sac and membranes, unspecified, second trimester, fetus 1" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O41.1022", - "display": "Infection of amniotic sac and membranes, unspecified, second trimester, fetus 2" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O41.1023", - "display": "Infection of amniotic sac and membranes, unspecified, second trimester, fetus 3" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O41.1024", - "display": "Infection of amniotic sac and membranes, unspecified, second trimester, fetus 4" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O41.1025", - "display": "Infection of amniotic sac and membranes, unspecified, second trimester, fetus 5" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O41.1029", - "display": "Infection of amniotic sac and membranes, unspecified, second trimester, other fetus" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O41.1030", - "display": "Infection of amniotic sac and membranes, unspecified, third trimester, not applicable or unspecified" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O41.1031", - "display": "Infection of amniotic sac and membranes, unspecified, third trimester, fetus 1" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O41.1032", - "display": "Infection of amniotic sac and membranes, unspecified, third trimester, fetus 2" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O41.1033", - "display": "Infection of amniotic sac and membranes, unspecified, third trimester, fetus 3" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O41.1034", - "display": "Infection of amniotic sac and membranes, unspecified, third trimester, fetus 4" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O41.1035", - "display": "Infection of amniotic sac and membranes, unspecified, third trimester, fetus 5" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O41.1039", - "display": "Infection of amniotic sac and membranes, unspecified, third trimester, other fetus" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O41.1090", - "display": "Infection of amniotic sac and membranes, unspecified, unspecified trimester, not applicable or unspecified" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O41.1091", - "display": "Infection of amniotic sac and membranes, unspecified, unspecified trimester, fetus 1" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O41.1092", - "display": "Infection of amniotic sac and membranes, unspecified, unspecified trimester, fetus 2" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O41.1093", - "display": "Infection of amniotic sac and membranes, unspecified, unspecified trimester, fetus 3" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O41.1094", - "display": "Infection of amniotic sac and membranes, unspecified, unspecified trimester, fetus 4" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O41.1095", - "display": "Infection of amniotic sac and membranes, unspecified, unspecified trimester, fetus 5" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O41.1099", - "display": "Infection of amniotic sac and membranes, unspecified, unspecified trimester, other fetus" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O41.1210", - "display": "Chorioamnionitis, first trimester, not applicable or unspecified" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O41.1211", - "display": "Chorioamnionitis, first trimester, fetus 1" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O41.1212", - "display": "Chorioamnionitis, first trimester, fetus 2" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O41.1213", - "display": "Chorioamnionitis, first trimester, fetus 3" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O41.1214", - "display": "Chorioamnionitis, first trimester, fetus 4" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O41.1215", - "display": "Chorioamnionitis, first trimester, fetus 5" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O41.1219", - "display": "Chorioamnionitis, first trimester, other fetus" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O41.1220", - "display": "Chorioamnionitis, second trimester, not applicable or unspecified" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O41.1221", - "display": "Chorioamnionitis, second trimester, fetus 1" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O41.1222", - "display": "Chorioamnionitis, second trimester, fetus 2" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O41.1223", - "display": "Chorioamnionitis, second trimester, fetus 3" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O41.1224", - "display": "Chorioamnionitis, second trimester, fetus 4" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O41.1225", - "display": "Chorioamnionitis, second trimester, fetus 5" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O41.1229", - "display": "Chorioamnionitis, second trimester, other fetus" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O41.1230", - "display": "Chorioamnionitis, third trimester, not applicable or unspecified" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O41.1231", - "display": "Chorioamnionitis, third trimester, fetus 1" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O41.1232", - "display": "Chorioamnionitis, third trimester, fetus 2" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O41.1233", - "display": "Chorioamnionitis, third trimester, fetus 3" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O41.1234", - "display": "Chorioamnionitis, third trimester, fetus 4" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O41.1235", - "display": "Chorioamnionitis, third trimester, fetus 5" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O41.1239", - "display": "Chorioamnionitis, third trimester, other fetus" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O41.1290", - "display": "Chorioamnionitis, unspecified trimester, not applicable or unspecified" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O41.1291", - "display": "Chorioamnionitis, unspecified trimester, fetus 1" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O41.1292", - "display": "Chorioamnionitis, unspecified trimester, fetus 2" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O41.1293", - "display": "Chorioamnionitis, unspecified trimester, fetus 3" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O41.1294", - "display": "Chorioamnionitis, unspecified trimester, fetus 4" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O41.1295", - "display": "Chorioamnionitis, unspecified trimester, fetus 5" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O41.1299", - "display": "Chorioamnionitis, unspecified trimester, other fetus" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O41.1410", - "display": "Placentitis, first trimester, not applicable or unspecified" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O41.1411", - "display": "Placentitis, first trimester, fetus 1" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O41.1412", - "display": "Placentitis, first trimester, fetus 2" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O41.1413", - "display": "Placentitis, first trimester, fetus 3" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O41.1414", - "display": "Placentitis, first trimester, fetus 4" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O41.1415", - "display": "Placentitis, first trimester, fetus 5" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O41.1419", - "display": "Placentitis, first trimester, other fetus" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O41.1420", - "display": "Placentitis, second trimester, not applicable or unspecified" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O41.1421", - "display": "Placentitis, second trimester, fetus 1" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O41.1422", - "display": "Placentitis, second trimester, fetus 2" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O41.1423", - "display": "Placentitis, second trimester, fetus 3" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O41.1424", - "display": "Placentitis, second trimester, fetus 4" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O41.1425", - "display": "Placentitis, second trimester, fetus 5" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O41.1429", - "display": "Placentitis, second trimester, other fetus" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O41.1430", - "display": "Placentitis, third trimester, not applicable or unspecified" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O41.1431", - "display": "Placentitis, third trimester, fetus 1" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O41.1432", - "display": "Placentitis, third trimester, fetus 2" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O41.1433", - "display": "Placentitis, third trimester, fetus 3" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O41.1434", - "display": "Placentitis, third trimester, fetus 4" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O41.1435", - "display": "Placentitis, third trimester, fetus 5" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O41.1439", - "display": "Placentitis, third trimester, other fetus" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O41.1490", - "display": "Placentitis, unspecified trimester, not applicable or unspecified" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O41.1491", - "display": "Placentitis, unspecified trimester, fetus 1" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O41.1492", - "display": "Placentitis, unspecified trimester, fetus 2" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O41.1493", - "display": "Placentitis, unspecified trimester, fetus 3" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O41.1494", - "display": "Placentitis, unspecified trimester, fetus 4" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O41.1495", - "display": "Placentitis, unspecified trimester, fetus 5" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O41.1499", - "display": "Placentitis, unspecified trimester, other fetus" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O41.8X10", - "display": "Other specified disorders of amniotic fluid and membranes, first trimester, not applicable or unspecified" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O41.8X11", - "display": "Other specified disorders of amniotic fluid and membranes, first trimester, fetus 1" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O41.8X12", - "display": "Other specified disorders of amniotic fluid and membranes, first trimester, fetus 2" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O41.8X13", - "display": "Other specified disorders of amniotic fluid and membranes, first trimester, fetus 3" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O41.8X14", - "display": "Other specified disorders of amniotic fluid and membranes, first trimester, fetus 4" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O41.8X15", - "display": "Other specified disorders of amniotic fluid and membranes, first trimester, fetus 5" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O41.8X19", - "display": "Other specified disorders of amniotic fluid and membranes, first trimester, other fetus" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O41.8X20", - "display": "Other specified disorders of amniotic fluid and membranes, second trimester, not applicable or unspecified" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O41.8X21", - "display": "Other specified disorders of amniotic fluid and membranes, second trimester, fetus 1" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O41.8X22", - "display": "Other specified disorders of amniotic fluid and membranes, second trimester, fetus 2" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O41.8X23", - "display": "Other specified disorders of amniotic fluid and membranes, second trimester, fetus 3" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O41.8X24", - "display": "Other specified disorders of amniotic fluid and membranes, second trimester, fetus 4" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O41.8X25", - "display": "Other specified disorders of amniotic fluid and membranes, second trimester, fetus 5" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O41.8X29", - "display": "Other specified disorders of amniotic fluid and membranes, second trimester, other fetus" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O41.8X30", - "display": "Other specified disorders of amniotic fluid and membranes, third trimester, not applicable or unspecified" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O41.8X31", - "display": "Other specified disorders of amniotic fluid and membranes, third trimester, fetus 1" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O41.8X32", - "display": "Other specified disorders of amniotic fluid and membranes, third trimester, fetus 2" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O41.8X33", - "display": "Other specified disorders of amniotic fluid and membranes, third trimester, fetus 3" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O41.8X34", - "display": "Other specified disorders of amniotic fluid and membranes, third trimester, fetus 4" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O41.8X35", - "display": "Other specified disorders of amniotic fluid and membranes, third trimester, fetus 5" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O41.8X39", - "display": "Other specified disorders of amniotic fluid and membranes, third trimester, other fetus" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O41.8X90", - "display": "Other specified disorders of amniotic fluid and membranes, unspecified trimester, not applicable or unspecified" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O41.8X91", - "display": "Other specified disorders of amniotic fluid and membranes, unspecified trimester, fetus 1" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O41.8X92", - "display": "Other specified disorders of amniotic fluid and membranes, unspecified trimester, fetus 2" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O41.8X93", - "display": "Other specified disorders of amniotic fluid and membranes, unspecified trimester, fetus 3" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O41.8X94", - "display": "Other specified disorders of amniotic fluid and membranes, unspecified trimester, fetus 4" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O41.8X95", - "display": "Other specified disorders of amniotic fluid and membranes, unspecified trimester, fetus 5" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O41.8X99", - "display": "Other specified disorders of amniotic fluid and membranes, unspecified trimester, other fetus" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O41.90X0", - "display": "Disorder of amniotic fluid and membranes, unspecified, unspecified trimester, not applicable or unspecified" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O41.90X1", - "display": "Disorder of amniotic fluid and membranes, unspecified, unspecified trimester, fetus 1" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O41.90X2", - "display": "Disorder of amniotic fluid and membranes, unspecified, unspecified trimester, fetus 2" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O41.90X3", - "display": "Disorder of amniotic fluid and membranes, unspecified, unspecified trimester, fetus 3" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O41.90X4", - "display": "Disorder of amniotic fluid and membranes, unspecified, unspecified trimester, fetus 4" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O41.90X5", - "display": "Disorder of amniotic fluid and membranes, unspecified, unspecified trimester, fetus 5" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O41.90X9", - "display": "Disorder of amniotic fluid and membranes, unspecified, unspecified trimester, other fetus" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O41.91X0", - "display": "Disorder of amniotic fluid and membranes, unspecified, first trimester, not applicable or unspecified" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O41.91X1", - "display": "Disorder of amniotic fluid and membranes, unspecified, first trimester, fetus 1" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O41.91X2", - "display": "Disorder of amniotic fluid and membranes, unspecified, first trimester, fetus 2" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O41.91X3", - "display": "Disorder of amniotic fluid and membranes, unspecified, first trimester, fetus 3" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O41.91X4", - "display": "Disorder of amniotic fluid and membranes, unspecified, first trimester, fetus 4" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O41.91X5", - "display": "Disorder of amniotic fluid and membranes, unspecified, first trimester, fetus 5" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O41.91X9", - "display": "Disorder of amniotic fluid and membranes, unspecified, first trimester, other fetus" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O41.92X0", - "display": "Disorder of amniotic fluid and membranes, unspecified, second trimester, not applicable or unspecified" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O41.92X1", - "display": "Disorder of amniotic fluid and membranes, unspecified, second trimester, fetus 1" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O41.92X2", - "display": "Disorder of amniotic fluid and membranes, unspecified, second trimester, fetus 2" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O41.92X3", - "display": "Disorder of amniotic fluid and membranes, unspecified, second trimester, fetus 3" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O41.92X4", - "display": "Disorder of amniotic fluid and membranes, unspecified, second trimester, fetus 4" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O41.92X5", - "display": "Disorder of amniotic fluid and membranes, unspecified, second trimester, fetus 5" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O41.92X9", - "display": "Disorder of amniotic fluid and membranes, unspecified, second trimester, other fetus" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O41.93X0", - "display": "Disorder of amniotic fluid and membranes, unspecified, third trimester, not applicable or unspecified" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O41.93X1", - "display": "Disorder of amniotic fluid and membranes, unspecified, third trimester, fetus 1" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O41.93X2", - "display": "Disorder of amniotic fluid and membranes, unspecified, third trimester, fetus 2" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O41.93X3", - "display": "Disorder of amniotic fluid and membranes, unspecified, third trimester, fetus 3" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O41.93X4", - "display": "Disorder of amniotic fluid and membranes, unspecified, third trimester, fetus 4" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O41.93X5", - "display": "Disorder of amniotic fluid and membranes, unspecified, third trimester, fetus 5" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O41.93X9", - "display": "Disorder of amniotic fluid and membranes, unspecified, third trimester, other fetus" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O42.00", - "display": "Premature rupture of membranes, onset of labor within 24 hours of rupture, unspecified weeks of gestation" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O42.011", - "display": "Preterm premature rupture of membranes, onset of labor within 24 hours of rupture, first trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O42.012", - "display": "Preterm premature rupture of membranes, onset of labor within 24 hours of rupture, second trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O42.013", - "display": "Preterm premature rupture of membranes, onset of labor within 24 hours of rupture, third trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O42.019", - "display": "Preterm premature rupture of membranes, onset of labor within 24 hours of rupture, unspecified trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O42.02", - "display": "Full-term premature rupture of membranes, onset of labor within 24 hours of rupture" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O42.10", - "display": "Premature rupture of membranes, onset of labor more than 24 hours following rupture, unspecified weeks of gestation" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O42.111", - "display": "Preterm premature rupture of membranes, onset of labor more than 24 hours following rupture, first trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O42.112", - "display": "Preterm premature rupture of membranes, onset of labor more than 24 hours following rupture, second trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O42.113", - "display": "Preterm premature rupture of membranes, onset of labor more than 24 hours following rupture, third trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O42.119", - "display": "Preterm premature rupture of membranes, onset of labor more than 24 hours following rupture, unspecified trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O42.12", - "display": "Full-term premature rupture of membranes, onset of labor more than 24 hours following rupture" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O42.90", - "display": "Premature rupture of membranes, unspecified as to length of time between rupture and onset of labor, unspecified weeks of gestation" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O42.911", - "display": "Preterm premature rupture of membranes, unspecified as to length of time between rupture and onset of labor, first trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O42.912", - "display": "Preterm premature rupture of membranes, unspecified as to length of time between rupture and onset of labor, second trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O42.913", - "display": "Preterm premature rupture of membranes, unspecified as to length of time between rupture and onset of labor, third trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O42.919", - "display": "Preterm premature rupture of membranes, unspecified as to length of time between rupture and onset of labor, unspecified trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O42.92", - "display": "Full-term premature rupture of membranes, unspecified as to length of time between rupture and onset of labor" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O43.011", - "display": "Fetomaternal placental transfusion syndrome, first trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O43.012", - "display": "Fetomaternal placental transfusion syndrome, second trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O43.013", - "display": "Fetomaternal placental transfusion syndrome, third trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O43.019", - "display": "Fetomaternal placental transfusion syndrome, unspecified trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O43.021", - "display": "Fetus-to-fetus placental transfusion syndrome, first trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O43.022", - "display": "Fetus-to-fetus placental transfusion syndrome, second trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O43.023", - "display": "Fetus-to-fetus placental transfusion syndrome, third trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O43.029", - "display": "Fetus-to-fetus placental transfusion syndrome, unspecified trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O43.101", - "display": "Malformation of placenta, unspecified, first trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O43.102", - "display": "Malformation of placenta, unspecified, second trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O43.103", - "display": "Malformation of placenta, unspecified, third trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O43.109", - "display": "Malformation of placenta, unspecified, unspecified trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O43.111", - "display": "Circumvallate placenta, first trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O43.112", - "display": "Circumvallate placenta, second trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O43.113", - "display": "Circumvallate placenta, third trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O43.119", - "display": "Circumvallate placenta, unspecified trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O43.121", - "display": "Velamentous insertion of umbilical cord, first trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O43.122", - "display": "Velamentous insertion of umbilical cord, second trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O43.123", - "display": "Velamentous insertion of umbilical cord, third trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O43.129", - "display": "Velamentous insertion of umbilical cord, unspecified trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O43.191", - "display": "Other malformation of placenta, first trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O43.192", - "display": "Other malformation of placenta, second trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O43.193", - "display": "Other malformation of placenta, third trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O43.199", - "display": "Other malformation of placenta, unspecified trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O43.211", - "display": "Placenta accreta, first trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O43.212", - "display": "Placenta accreta, second trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O43.213", - "display": "Placenta accreta, third trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O43.219", - "display": "Placenta accreta, unspecified trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O43.221", - "display": "Placenta increta, first trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O43.222", - "display": "Placenta increta, second trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O43.223", - "display": "Placenta increta, third trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O43.229", - "display": "Placenta increta, unspecified trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O43.231", - "display": "Placenta percreta, first trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O43.232", - "display": "Placenta percreta, second trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O43.233", - "display": "Placenta percreta, third trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O43.239", - "display": "Placenta percreta, unspecified trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O43.811", - "display": "Placental infarction, first trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O43.812", - "display": "Placental infarction, second trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O43.813", - "display": "Placental infarction, third trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O43.819", - "display": "Placental infarction, unspecified trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O43.891", - "display": "Other placental disorders, first trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O43.892", - "display": "Other placental disorders, second trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O43.893", - "display": "Other placental disorders, third trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O43.899", - "display": "Other placental disorders, unspecified trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O43.90", - "display": "Unspecified placental disorder, unspecified trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O43.91", - "display": "Unspecified placental disorder, first trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O43.92", - "display": "Unspecified placental disorder, second trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O43.93", - "display": "Unspecified placental disorder, third trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O44.00", - "display": "Complete placenta previa NOS or without hemorrhage, unspecified trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O44.01", - "display": "Complete placenta previa NOS or without hemorrhage, first trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O44.02", - "display": "Complete placenta previa NOS or without hemorrhage, second trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O44.03", - "display": "Complete placenta previa NOS or without hemorrhage, third trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O44.10", - "display": "Complete placenta previa with hemorrhage, unspecified trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O44.11", - "display": "Complete placenta previa with hemorrhage, first trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O44.12", - "display": "Complete placenta previa with hemorrhage, second trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O44.13", - "display": "Complete placenta previa with hemorrhage, third trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O44.20", - "display": "Partial placenta previa NOS or without hemorrhage, unspecified trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O44.21", - "display": "Partial placenta previa NOS or without hemorrhage, first trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O44.22", - "display": "Partial placenta previa NOS or without hemorrhage, second trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O44.23", - "display": "Partial placenta previa NOS or without hemorrhage, third trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O44.30", - "display": "Partial placenta previa with hemorrhage, unspecified trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O44.31", - "display": "Partial placenta previa with hemorrhage, first trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O44.32", - "display": "Partial placenta previa with hemorrhage, second trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O44.33", - "display": "Partial placenta previa with hemorrhage, third trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O44.40", - "display": "Low lying placenta NOS or without hemorrhage, unspecified trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O44.41", - "display": "Low lying placenta NOS or without hemorrhage, first trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O44.42", - "display": "Low lying placenta NOS or without hemorrhage, second trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O44.43", - "display": "Low lying placenta NOS or without hemorrhage, third trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O44.50", - "display": "Low lying placenta with hemorrhage, unspecified trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O44.51", - "display": "Low lying placenta with hemorrhage, first trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O44.52", - "display": "Low lying placenta with hemorrhage, second trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O44.53", - "display": "Low lying placenta with hemorrhage, third trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O45.001", - "display": "Premature separation of placenta with coagulation defect, unspecified, first trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O45.002", - "display": "Premature separation of placenta with coagulation defect, unspecified, second trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O45.003", - "display": "Premature separation of placenta with coagulation defect, unspecified, third trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O45.009", - "display": "Premature separation of placenta with coagulation defect, unspecified, unspecified trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O45.011", - "display": "Premature separation of placenta with afibrinogenemia, first trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O45.012", - "display": "Premature separation of placenta with afibrinogenemia, second trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O45.013", - "display": "Premature separation of placenta with afibrinogenemia, third trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O45.019", - "display": "Premature separation of placenta with afibrinogenemia, unspecified trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O45.021", - "display": "Premature separation of placenta with disseminated intravascular coagulation, first trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O45.022", - "display": "Premature separation of placenta with disseminated intravascular coagulation, second trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O45.023", - "display": "Premature separation of placenta with disseminated intravascular coagulation, third trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O45.029", - "display": "Premature separation of placenta with disseminated intravascular coagulation, unspecified trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O45.091", - "display": "Premature separation of placenta with other coagulation defect, first trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O45.092", - "display": "Premature separation of placenta with other coagulation defect, second trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O45.093", - "display": "Premature separation of placenta with other coagulation defect, third trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O45.099", - "display": "Premature separation of placenta with other coagulation defect, unspecified trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O45.8X1", - "display": "Other premature separation of placenta, first trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O45.8X2", - "display": "Other premature separation of placenta, second trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O45.8X3", - "display": "Other premature separation of placenta, third trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O45.8X9", - "display": "Other premature separation of placenta, unspecified trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O45.90", - "display": "Premature separation of placenta, unspecified, unspecified trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O45.91", - "display": "Premature separation of placenta, unspecified, first trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O45.92", - "display": "Premature separation of placenta, unspecified, second trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O45.93", - "display": "Premature separation of placenta, unspecified, third trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O46.001", - "display": "Antepartum hemorrhage with coagulation defect, unspecified, first trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O46.002", - "display": "Antepartum hemorrhage with coagulation defect, unspecified, second trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O46.003", - "display": "Antepartum hemorrhage with coagulation defect, unspecified, third trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O46.009", - "display": "Antepartum hemorrhage with coagulation defect, unspecified, unspecified trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O46.011", - "display": "Antepartum hemorrhage with afibrinogenemia, first trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O46.012", - "display": "Antepartum hemorrhage with afibrinogenemia, second trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O46.013", - "display": "Antepartum hemorrhage with afibrinogenemia, third trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O46.019", - "display": "Antepartum hemorrhage with afibrinogenemia, unspecified trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O46.021", - "display": "Antepartum hemorrhage with disseminated intravascular coagulation, first trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O46.022", - "display": "Antepartum hemorrhage with disseminated intravascular coagulation, second trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O46.023", - "display": "Antepartum hemorrhage with disseminated intravascular coagulation, third trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O46.029", - "display": "Antepartum hemorrhage with disseminated intravascular coagulation, unspecified trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O46.091", - "display": "Antepartum hemorrhage with other coagulation defect, first trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O46.092", - "display": "Antepartum hemorrhage with other coagulation defect, second trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O46.093", - "display": "Antepartum hemorrhage with other coagulation defect, third trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O46.099", - "display": "Antepartum hemorrhage with other coagulation defect, unspecified trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O46.8X1", - "display": "Other antepartum hemorrhage, first trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O46.8X2", - "display": "Other antepartum hemorrhage, second trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O46.8X3", - "display": "Other antepartum hemorrhage, third trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O46.8X9", - "display": "Other antepartum hemorrhage, unspecified trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O46.90", - "display": "Antepartum hemorrhage, unspecified, unspecified trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O46.91", - "display": "Antepartum hemorrhage, unspecified, first trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O46.92", - "display": "Antepartum hemorrhage, unspecified, second trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O46.93", - "display": "Antepartum hemorrhage, unspecified, third trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O47.00", - "display": "False labor before 37 completed weeks of gestation, unspecified trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O47.02", - "display": "False labor before 37 completed weeks of gestation, second trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O47.03", - "display": "False labor before 37 completed weeks of gestation, third trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O47.1", - "display": "False labor at or after 37 completed weeks of gestation" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O47.9", - "display": "False labor, unspecified" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O48.0", - "display": "Post-term pregnancy" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O48.1", - "display": "Prolonged pregnancy" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O60.00", - "display": "Preterm labor without delivery, unspecified trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O60.02", - "display": "Preterm labor without delivery, second trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O60.03", - "display": "Preterm labor without delivery, third trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O71.00", - "display": "Rupture of uterus before onset of labor, unspecified trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O71.02", - "display": "Rupture of uterus before onset of labor, second trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O71.03", - "display": "Rupture of uterus before onset of labor, third trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O88.011", - "display": "Air embolism in pregnancy, first trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O88.012", - "display": "Air embolism in pregnancy, second trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O88.013", - "display": "Air embolism in pregnancy, third trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O88.019", - "display": "Air embolism in pregnancy, unspecified trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O88.111", - "display": "Amniotic fluid embolism in pregnancy, first trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O88.112", - "display": "Amniotic fluid embolism in pregnancy, second trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O88.113", - "display": "Amniotic fluid embolism in pregnancy, third trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O88.119", - "display": "Amniotic fluid embolism in pregnancy, unspecified trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O88.211", - "display": "Thromboembolism in pregnancy, first trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O88.212", - "display": "Thromboembolism in pregnancy, second trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O88.213", - "display": "Thromboembolism in pregnancy, third trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O88.219", - "display": "Thromboembolism in pregnancy, unspecified trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O88.311", - "display": "Pyemic and septic embolism in pregnancy, first trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O88.312", - "display": "Pyemic and septic embolism in pregnancy, second trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O88.313", - "display": "Pyemic and septic embolism in pregnancy, third trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O88.319", - "display": "Pyemic and septic embolism in pregnancy, unspecified trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O88.811", - "display": "Other embolism in pregnancy, first trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O88.812", - "display": "Other embolism in pregnancy, second trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O88.813", - "display": "Other embolism in pregnancy, third trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O88.819", - "display": "Other embolism in pregnancy, unspecified trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O90.3", - "display": "Peripartum cardiomyopathy" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O91.011", - "display": "Infection of nipple associated with pregnancy, first trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O91.012", - "display": "Infection of nipple associated with pregnancy, second trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O91.013", - "display": "Infection of nipple associated with pregnancy, third trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O91.019", - "display": "Infection of nipple associated with pregnancy, unspecified trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O91.111", - "display": "Abscess of breast associated with pregnancy, first trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O91.112", - "display": "Abscess of breast associated with pregnancy, second trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O91.113", - "display": "Abscess of breast associated with pregnancy, third trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O91.119", - "display": "Abscess of breast associated with pregnancy, unspecified trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O91.211", - "display": "Nonpurulent mastitis associated with pregnancy, first trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O91.212", - "display": "Nonpurulent mastitis associated with pregnancy, second trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O91.213", - "display": "Nonpurulent mastitis associated with pregnancy, third trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O91.219", - "display": "Nonpurulent mastitis associated with pregnancy, unspecified trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O92.011", - "display": "Retracted nipple associated with pregnancy, first trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O92.012", - "display": "Retracted nipple associated with pregnancy, second trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O92.013", - "display": "Retracted nipple associated with pregnancy, third trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O92.019", - "display": "Retracted nipple associated with pregnancy, unspecified trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O92.111", - "display": "Cracked nipple associated with pregnancy, first trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O92.112", - "display": "Cracked nipple associated with pregnancy, second trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O92.113", - "display": "Cracked nipple associated with pregnancy, third trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O92.119", - "display": "Cracked nipple associated with pregnancy, unspecified trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O92.20", - "display": "Unspecified disorder of breast associated with pregnancy and the puerperium" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O92.29", - "display": "Other disorders of breast associated with pregnancy and the puerperium" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O98.011", - "display": "Tuberculosis complicating pregnancy, first trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O98.012", - "display": "Tuberculosis complicating pregnancy, second trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O98.013", - "display": "Tuberculosis complicating pregnancy, third trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O98.019", - "display": "Tuberculosis complicating pregnancy, unspecified trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O98.111", - "display": "Syphilis complicating pregnancy, first trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O98.112", - "display": "Syphilis complicating pregnancy, second trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O98.113", - "display": "Syphilis complicating pregnancy, third trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O98.119", - "display": "Syphilis complicating pregnancy, unspecified trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O98.211", - "display": "Gonorrhea complicating pregnancy, first trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O98.212", - "display": "Gonorrhea complicating pregnancy, second trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O98.213", - "display": "Gonorrhea complicating pregnancy, third trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O98.219", - "display": "Gonorrhea complicating pregnancy, unspecified trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O98.311", - "display": "Other infections with a predominantly sexual mode of transmission complicating pregnancy, first trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O98.312", - "display": "Other infections with a predominantly sexual mode of transmission complicating pregnancy, second trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O98.313", - "display": "Other infections with a predominantly sexual mode of transmission complicating pregnancy, third trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O98.319", - "display": "Other infections with a predominantly sexual mode of transmission complicating pregnancy, unspecified trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O98.411", - "display": "Viral hepatitis complicating pregnancy, first trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O98.412", - "display": "Viral hepatitis complicating pregnancy, second trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O98.413", - "display": "Viral hepatitis complicating pregnancy, third trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O98.419", - "display": "Viral hepatitis complicating pregnancy, unspecified trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O98.511", - "display": "Other viral diseases complicating pregnancy, first trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O98.512", - "display": "Other viral diseases complicating pregnancy, second trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O98.513", - "display": "Other viral diseases complicating pregnancy, third trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O98.519", - "display": "Other viral diseases complicating pregnancy, unspecified trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O98.611", - "display": "Protozoal diseases complicating pregnancy, first trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O98.612", - "display": "Protozoal diseases complicating pregnancy, second trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O98.613", - "display": "Protozoal diseases complicating pregnancy, third trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O98.619", - "display": "Protozoal diseases complicating pregnancy, unspecified trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O98.711", - "display": "Human immunodeficiency virus [HIV] disease complicating pregnancy, first trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O98.712", - "display": "Human immunodeficiency virus [HIV] disease complicating pregnancy, second trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O98.713", - "display": "Human immunodeficiency virus [HIV] disease complicating pregnancy, third trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O98.719", - "display": "Human immunodeficiency virus [HIV] disease complicating pregnancy, unspecified trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O98.811", - "display": "Other maternal infectious and parasitic diseases complicating pregnancy, first trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O98.812", - "display": "Other maternal infectious and parasitic diseases complicating pregnancy, second trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O98.813", - "display": "Other maternal infectious and parasitic diseases complicating pregnancy, third trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O98.819", - "display": "Other maternal infectious and parasitic diseases complicating pregnancy, unspecified trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O98.911", - "display": "Unspecified maternal infectious and parasitic disease complicating pregnancy, first trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O98.912", - "display": "Unspecified maternal infectious and parasitic disease complicating pregnancy, second trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O98.913", - "display": "Unspecified maternal infectious and parasitic disease complicating pregnancy, third trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O98.919", - "display": "Unspecified maternal infectious and parasitic disease complicating pregnancy, unspecified trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O99.011", - "display": "Anemia complicating pregnancy, first trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O99.012", - "display": "Anemia complicating pregnancy, second trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O99.013", - "display": "Anemia complicating pregnancy, third trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O99.019", - "display": "Anemia complicating pregnancy, unspecified trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O99.111", - "display": "Other diseases of the blood and blood-forming organs and certain disorders involving the immune mechanism complicating pregnancy, first trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O99.112", - "display": "Other diseases of the blood and blood-forming organs and certain disorders involving the immune mechanism complicating pregnancy, second trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O99.113", - "display": "Other diseases of the blood and blood-forming organs and certain disorders involving the immune mechanism complicating pregnancy, third trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O99.119", - "display": "Other diseases of the blood and blood-forming organs and certain disorders involving the immune mechanism complicating pregnancy, unspecified trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O99.210", - "display": "Obesity complicating pregnancy, unspecified trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O99.211", - "display": "Obesity complicating pregnancy, first trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O99.212", - "display": "Obesity complicating pregnancy, second trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O99.213", - "display": "Obesity complicating pregnancy, third trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O99.280", - "display": "Endocrine, nutritional and metabolic diseases complicating pregnancy, unspecified trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O99.281", - "display": "Endocrine, nutritional and metabolic diseases complicating pregnancy, first trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O99.282", - "display": "Endocrine, nutritional and metabolic diseases complicating pregnancy, second trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O99.283", - "display": "Endocrine, nutritional and metabolic diseases complicating pregnancy, third trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O99.310", - "display": "Alcohol use complicating pregnancy, unspecified trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O99.311", - "display": "Alcohol use complicating pregnancy, first trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O99.312", - "display": "Alcohol use complicating pregnancy, second trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O99.313", - "display": "Alcohol use complicating pregnancy, third trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O99.320", - "display": "Drug use complicating pregnancy, unspecified trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O99.321", - "display": "Drug use complicating pregnancy, first trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O99.322", - "display": "Drug use complicating pregnancy, second trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O99.323", - "display": "Drug use complicating pregnancy, third trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O99.330", - "display": "Smoking (tobacco) complicating pregnancy, unspecified trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O99.331", - "display": "Smoking (tobacco) complicating pregnancy, first trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O99.332", - "display": "Smoking (tobacco) complicating pregnancy, second trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O99.333", - "display": "Smoking (tobacco) complicating pregnancy, third trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O99.340", - "display": "Other mental disorders complicating pregnancy, unspecified trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O99.341", - "display": "Other mental disorders complicating pregnancy, first trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O99.342", - "display": "Other mental disorders complicating pregnancy, second trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O99.343", - "display": "Other mental disorders complicating pregnancy, third trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O99.350", - "display": "Diseases of the nervous system complicating pregnancy, unspecified trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O99.351", - "display": "Diseases of the nervous system complicating pregnancy, first trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O99.352", - "display": "Diseases of the nervous system complicating pregnancy, second trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O99.353", - "display": "Diseases of the nervous system complicating pregnancy, third trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O99.411", - "display": "Diseases of the circulatory system complicating pregnancy, first trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O99.412", - "display": "Diseases of the circulatory system complicating pregnancy, second trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O99.413", - "display": "Diseases of the circulatory system complicating pregnancy, third trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O99.419", - "display": "Diseases of the circulatory system complicating pregnancy, unspecified trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O99.511", - "display": "Diseases of the respiratory system complicating pregnancy, first trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O99.512", - "display": "Diseases of the respiratory system complicating pregnancy, second trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O99.513", - "display": "Diseases of the respiratory system complicating pregnancy, third trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O99.519", - "display": "Diseases of the respiratory system complicating pregnancy, unspecified trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O99.611", - "display": "Diseases of the digestive system complicating pregnancy, first trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O99.612", - "display": "Diseases of the digestive system complicating pregnancy, second trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O99.613", - "display": "Diseases of the digestive system complicating pregnancy, third trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O99.619", - "display": "Diseases of the digestive system complicating pregnancy, unspecified trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O99.711", - "display": "Diseases of the skin and subcutaneous tissue complicating pregnancy, first trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O99.712", - "display": "Diseases of the skin and subcutaneous tissue complicating pregnancy, second trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O99.713", - "display": "Diseases of the skin and subcutaneous tissue complicating pregnancy, third trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O99.719", - "display": "Diseases of the skin and subcutaneous tissue complicating pregnancy, unspecified trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O99.810", - "display": "Abnormal glucose complicating pregnancy" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O99.820", - "display": "Streptococcus B carrier state complicating pregnancy" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O99.830", - "display": "Other infection carrier state complicating pregnancy" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O99.840", - "display": "Bariatric surgery status complicating pregnancy, unspecified trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O99.841", - "display": "Bariatric surgery status complicating pregnancy, first trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O99.842", - "display": "Bariatric surgery status complicating pregnancy, second trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O99.843", - "display": "Bariatric surgery status complicating pregnancy, third trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O9A.111", - "display": "Malignant neoplasm complicating pregnancy, first trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O9A.112", - "display": "Malignant neoplasm complicating pregnancy, second trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O9A.113", - "display": "Malignant neoplasm complicating pregnancy, third trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O9A.119", - "display": "Malignant neoplasm complicating pregnancy, unspecified trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O9A.211", - "display": "Injury, poisoning and certain other consequences of external causes complicating pregnancy, first trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O9A.212", - "display": "Injury, poisoning and certain other consequences of external causes complicating pregnancy, second trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O9A.213", - "display": "Injury, poisoning and certain other consequences of external causes complicating pregnancy, third trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O9A.219", - "display": "Injury, poisoning and certain other consequences of external causes complicating pregnancy, unspecified trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O9A.311", - "display": "Physical abuse complicating pregnancy, first trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O9A.312", - "display": "Physical abuse complicating pregnancy, second trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O9A.313", - "display": "Physical abuse complicating pregnancy, third trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O9A.319", - "display": "Physical abuse complicating pregnancy, unspecified trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O9A.411", - "display": "Sexual abuse complicating pregnancy, first trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O9A.412", - "display": "Sexual abuse complicating pregnancy, second trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O9A.413", - "display": "Sexual abuse complicating pregnancy, third trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O9A.419", - "display": "Sexual abuse complicating pregnancy, unspecified trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O9A.511", - "display": "Psychological abuse complicating pregnancy, first trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O9A.512", - "display": "Psychological abuse complicating pregnancy, second trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O9A.513", - "display": "Psychological abuse complicating pregnancy, third trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O9A.519", - "display": "Psychological abuse complicating pregnancy, unspecified trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "Z33.1", - "display": "Pregnant state, incidental" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "Z33.3", - "display": "Pregnant state, gestational carrier" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "Z34.00", - "display": "Encounter for supervision of normal first pregnancy, unspecified trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "Z34.01", - "display": "Encounter for supervision of normal first pregnancy, first trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "Z34.02", - "display": "Encounter for supervision of normal first pregnancy, second trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "Z34.03", - "display": "Encounter for supervision of normal first pregnancy, third trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "Z34.80", - "display": "Encounter for supervision of other normal pregnancy, unspecified trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "Z34.81", - "display": "Encounter for supervision of other normal pregnancy, first trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "Z34.82", - "display": "Encounter for supervision of other normal pregnancy, second trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "Z34.83", - "display": "Encounter for supervision of other normal pregnancy, third trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "Z34.90", - "display": "Encounter for supervision of normal pregnancy, unspecified, unspecified trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "Z34.91", - "display": "Encounter for supervision of normal pregnancy, unspecified, first trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "Z34.92", - "display": "Encounter for supervision of normal pregnancy, unspecified, second trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "Z34.93", - "display": "Encounter for supervision of normal pregnancy, unspecified, third trimester" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "Z3A.01", - "display": "Less than 8 weeks gestation of pregnancy" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "Z3A.08", - "display": "8 weeks gestation of pregnancy" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "Z3A.09", - "display": "9 weeks gestation of pregnancy" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "Z3A.10", - "display": "10 weeks gestation of pregnancy" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "Z3A.11", - "display": "11 weeks gestation of pregnancy" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "Z3A.12", - "display": "12 weeks gestation of pregnancy" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "Z3A.13", - "display": "13 weeks gestation of pregnancy" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "Z3A.14", - "display": "14 weeks gestation of pregnancy" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "Z3A.15", - "display": "15 weeks gestation of pregnancy" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "Z3A.16", - "display": "16 weeks gestation of pregnancy" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "Z3A.17", - "display": "17 weeks gestation of pregnancy" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "Z3A.18", - "display": "18 weeks gestation of pregnancy" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "Z3A.19", - "display": "19 weeks gestation of pregnancy" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "Z3A.20", - "display": "20 weeks gestation of pregnancy" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "Z3A.21", - "display": "21 weeks gestation of pregnancy" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "Z3A.22", - "display": "22 weeks gestation of pregnancy" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "Z3A.23", - "display": "23 weeks gestation of pregnancy" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "Z3A.24", - "display": "24 weeks gestation of pregnancy" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "Z3A.25", - "display": "25 weeks gestation of pregnancy" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "Z3A.26", - "display": "26 weeks gestation of pregnancy" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "Z3A.27", - "display": "27 weeks gestation of pregnancy" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "Z3A.28", - "display": "28 weeks gestation of pregnancy" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "Z3A.29", - "display": "29 weeks gestation of pregnancy" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "Z3A.30", - "display": "30 weeks gestation of pregnancy" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "Z3A.31", - "display": "31 weeks gestation of pregnancy" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "Z3A.32", - "display": "32 weeks gestation of pregnancy" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "Z3A.33", - "display": "33 weeks gestation of pregnancy" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "Z3A.34", - "display": "34 weeks gestation of pregnancy" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "Z3A.35", - "display": "35 weeks gestation of pregnancy" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "Z3A.36", - "display": "36 weeks gestation of pregnancy" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "Z3A.37", - "display": "37 weeks gestation of pregnancy" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "Z3A.38", - "display": "38 weeks gestation of pregnancy" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "Z3A.39", - "display": "39 weeks gestation of pregnancy" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "Z3A.40", - "display": "40 weeks gestation of pregnancy" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "Z3A.41", - "display": "41 weeks gestation of pregnancy" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "Z3A.42", - "display": "42 weeks gestation of pregnancy" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "Z3A.49", - "display": "Greater than 42 weeks gestation of pregnancy" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O0000", - "display": "Abdominal pregnancy without intrauterine pregnancy" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O0001", - "display": "Abdominal pregnancy with intrauterine pregnancy" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O00101", - "display": "Right tubal pregnancy without intrauterine pregnancy" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O00102", - "display": "Left tubal pregnancy without intrauterine pregnancy" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O00109", - "display": "Unspecified tubal pregnancy without intrauterine pregnancy" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O00111", - "display": "Right tubal pregnancy with intrauterine pregnancy" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O00112", - "display": "Left tubal pregnancy with intrauterine pregnancy" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O00119", - "display": "Unspecified tubal pregnancy with intrauterine pregnancy" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O00201", - "display": "Right ovarian pregnancy without intrauterine pregnancy" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O00202", - "display": "Left ovarian pregnancy without intrauterine pregnancy" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O00209", - "display": "Unspecified ovarian pregnancy without intrauterine pregnancy" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O00211", - "display": "Right ovarian pregnancy with intrauterine pregnancy" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O00212", - "display": "Left ovarian pregnancy with intrauterine pregnancy" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O00219", - "display": "Unspecified ovarian pregnancy with intrauterine pregnancy" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O0080", - "display": "Other ectopic pregnancy without intrauterine pregnancy" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O0081", - "display": "Other ectopic pregnancy with intrauterine pregnancy" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O0090", - "display": "Unspecified ectopic pregnancy without intrauterine pregnancy" - }, - { - "system": "http://hl7.org/fhir/sid/icd-10-cm", - "version": "2023", - "code": "O0091", - "display": "Unspecified ectopic pregnancy with intrauterine pregnancy" - } - ] - } - }, - "request": { - "method": "PUT", - "url": "ValueSet/2.16.840.1.113883.3.526.3.378" - } - }, - { - "resource": { - "resourceType": "Library", - "id": "DQMFHIRHelpers", - "extension": [ - { - "url": "http://hl7.org/fhir/us/cqfmeasures/StructureDefinition/cqfm-softwaresystem", - "valueReference": { - "reference": "Device/cqf-tooling" - } - } - ], - "url": "http://content.alphora.com/fhir/dqm/Library/DQMFHIRHelpers", - "version": "4.0.1", - "name": "DQMFHIRHelpers", - "relatedArtifact": [ - { - "type": "depends-on", - "display": "FHIR model information", - "resource": "http://fhir.org/guides/cqf/common/Library/FHIR-ModelInfo|4.0.1" - } - ], - "content": [ - { - "contentType": "text/cql", - "data": "bGlicmFyeSBEUU1GSElSSGVscGVycyB2ZXJzaW9uICc0LjAuMScKCnVzaW5nIEZISVIgdmVyc2lvbiAnNC4wLjEnCgpkZWZpbmUgZnVuY3Rpb24gVG9JbnRlcnZhbChwZXJpb2QgRkhJUi5QZXJpb2QpOgogICAgaWYgcGVyaW9kIGlzIG51bGwgdGhlbgogICAgICAgIG51bGwKICAgIGVsc2UKICAgICAgICBJbnRlcnZhbFtwZXJpb2QuInN0YXJ0Ii52YWx1ZSwgcGVyaW9kLiJlbmQiLnZhbHVlXQoKZGVmaW5lIGZ1bmN0aW9uIFRvUXVhbnRpdHkocXVhbnRpdHkgRkhJUi5RdWFudGl0eSk6CiAgICBpZiBxdWFudGl0eSBpcyBudWxsIHRoZW4KICAgICAgICBudWxsCiAgICBlbHNlCiAgICAgICAgU3lzdGVtLlF1YW50aXR5IHsgdmFsdWU6IHF1YW50aXR5LnZhbHVlLnZhbHVlLCB1bml0OiBxdWFudGl0eS51bml0LnZhbHVlIH0KCmRlZmluZSBmdW5jdGlvbiBUb1JhdGlvKHJhdGlvIEZISVIuUmF0aW8pOgogICAgaWYgcmF0aW8gaXMgbnVsbCB0aGVuCiAgICAgICAgbnVsbAogICAgZWxzZQogICAgICAgIFN5c3RlbS5SYXRpbyB7IG51bWVyYXRvcjogVG9RdWFudGl0eShyYXRpby5udW1lcmF0b3IpLCBkZW5vbWluYXRvcjogVG9RdWFudGl0eShyYXRpby5kZW5vbWluYXRvcikgfQoKZGVmaW5lIGZ1bmN0aW9uIFRvSW50ZXJ2YWwocmFuZ2UgRkhJUi5SYW5nZSk6CiAgICBpZiByYW5nZSBpcyBudWxsIHRoZW4KICAgICAgICBudWxsCiAgICBlbHNlCiAgICAgICAgSW50ZXJ2YWxbVG9RdWFudGl0eShyYW5nZS5sb3cpLCBUb1F1YW50aXR5KHJhbmdlLmhpZ2gpXQoKZGVmaW5lIGZ1bmN0aW9uIFRvQ29kZShjb2RpbmcgRkhJUi5Db2RpbmcpOgogICAgaWYgY29kaW5nIGlzIG51bGwgdGhlbgogICAgICAgIG51bGwKICAgIGVsc2UKICAgICAgICBTeXN0ZW0uQ29kZSB7CiAgICAgICAgICBjb2RlOiBjb2RpbmcuY29kZS52YWx1ZSwKICAgICAgICAgIHN5c3RlbTogY29kaW5nLnN5c3RlbS52YWx1ZSwKICAgICAgICAgIHZlcnNpb246IGNvZGluZy52ZXJzaW9uLnZhbHVlLAogICAgICAgICAgZGlzcGxheTogY29kaW5nLmRpc3BsYXkudmFsdWUKICAgICAgICB9CgpkZWZpbmUgZnVuY3Rpb24gVG9Db25jZXB0KGNvbmNlcHQgRkhJUi5Db2RlYWJsZUNvbmNlcHQpOgogICAgaWYgY29uY2VwdCBpcyBudWxsIHRoZW4KICAgICAgICBudWxsCiAgICBlbHNlCiAgICAgICAgU3lzdGVtLkNvbmNlcHQgewogICAgICAgICAgICBjb2RlczogY29uY2VwdC5jb2RpbmcgQyByZXR1cm4gVG9Db2RlKEMpLAogICAgICAgICAgICBkaXNwbGF5OiBjb25jZXB0LnRleHQudmFsdWUKICAgICAgICB9CgoKZGVmaW5lIGZ1bmN0aW9uIFRvU3RyaW5nKHZhbHVlIEFjY291bnRTdGF0dXMpOiB2YWx1ZS52YWx1ZQpkZWZpbmUgZnVuY3Rpb24gVG9TdHJpbmcodmFsdWUgQWN0aW9uQ2FyZGluYWxpdHlCZWhhdmlvcik6IHZhbHVlLnZhbHVlCmRlZmluZSBmdW5jdGlvbiBUb1N0cmluZyh2YWx1ZSBBY3Rpb25Db25kaXRpb25LaW5kKTogdmFsdWUudmFsdWUKZGVmaW5lIGZ1bmN0aW9uIFRvU3RyaW5nKHZhbHVlIEFjdGlvbkdyb3VwaW5nQmVoYXZpb3IpOiB2YWx1ZS52YWx1ZQpkZWZpbmUgZnVuY3Rpb24gVG9TdHJpbmcodmFsdWUgQWN0aW9uUGFydGljaXBhbnRUeXBlKTogdmFsdWUudmFsdWUKZGVmaW5lIGZ1bmN0aW9uIFRvU3RyaW5nKHZhbHVlIEFjdGlvblByZWNoZWNrQmVoYXZpb3IpOiB2YWx1ZS52YWx1ZQpkZWZpbmUgZnVuY3Rpb24gVG9TdHJpbmcodmFsdWUgQWN0aW9uUmVsYXRpb25zaGlwVHlwZSk6IHZhbHVlLnZhbHVlCmRlZmluZSBmdW5jdGlvbiBUb1N0cmluZyh2YWx1ZSBBY3Rpb25SZXF1aXJlZEJlaGF2aW9yKTogdmFsdWUudmFsdWUKZGVmaW5lIGZ1bmN0aW9uIFRvU3RyaW5nKHZhbHVlIEFjdGlvblNlbGVjdGlvbkJlaGF2aW9yKTogdmFsdWUudmFsdWUKZGVmaW5lIGZ1bmN0aW9uIFRvU3RyaW5nKHZhbHVlIEFjdGl2aXR5RGVmaW5pdGlvbktpbmQpOiB2YWx1ZS52YWx1ZQpkZWZpbmUgZnVuY3Rpb24gVG9TdHJpbmcodmFsdWUgQWN0aXZpdHlQYXJ0aWNpcGFudFR5cGUpOiB2YWx1ZS52YWx1ZQpkZWZpbmUgZnVuY3Rpb24gVG9TdHJpbmcodmFsdWUgQWRkcmVzc1R5cGUpOiB2YWx1ZS52YWx1ZQpkZWZpbmUgZnVuY3Rpb24gVG9TdHJpbmcodmFsdWUgQWRkcmVzc1VzZSk6IHZhbHVlLnZhbHVlCmRlZmluZSBmdW5jdGlvbiBUb1N0cmluZyh2YWx1ZSBBZG1pbmlzdHJhdGl2ZUdlbmRlcik6IHZhbHVlLnZhbHVlCmRlZmluZSBmdW5jdGlvbiBUb1N0cmluZyh2YWx1ZSBBZHZlcnNlRXZlbnRBY3R1YWxpdHkpOiB2YWx1ZS52YWx1ZQpkZWZpbmUgZnVuY3Rpb24gVG9TdHJpbmcodmFsdWUgQWdncmVnYXRpb25Nb2RlKTogdmFsdWUudmFsdWUKZGVmaW5lIGZ1bmN0aW9uIFRvU3RyaW5nKHZhbHVlIEFsbGVyZ3lJbnRvbGVyYW5jZUNhdGVnb3J5KTogdmFsdWUudmFsdWUKZGVmaW5lIGZ1bmN0aW9uIFRvU3RyaW5nKHZhbHVlIEFsbGVyZ3lJbnRvbGVyYW5jZUNyaXRpY2FsaXR5KTogdmFsdWUudmFsdWUKZGVmaW5lIGZ1bmN0aW9uIFRvU3RyaW5nKHZhbHVlIEFsbGVyZ3lJbnRvbGVyYW5jZVNldmVyaXR5KTogdmFsdWUudmFsdWUKZGVmaW5lIGZ1bmN0aW9uIFRvU3RyaW5nKHZhbHVlIEFsbGVyZ3lJbnRvbGVyYW5jZVR5cGUpOiB2YWx1ZS52YWx1ZQpkZWZpbmUgZnVuY3Rpb24gVG9TdHJpbmcodmFsdWUgQXBwb2ludG1lbnRTdGF0dXMpOiB2YWx1ZS52YWx1ZQpkZWZpbmUgZnVuY3Rpb24gVG9TdHJpbmcodmFsdWUgQXNzZXJ0aW9uRGlyZWN0aW9uVHlwZSk6IHZhbHVlLnZhbHVlCmRlZmluZSBmdW5jdGlvbiBUb1N0cmluZyh2YWx1ZSBBc3NlcnRpb25PcGVyYXRvclR5cGUpOiB2YWx1ZS52YWx1ZQpkZWZpbmUgZnVuY3Rpb24gVG9TdHJpbmcodmFsdWUgQXNzZXJ0aW9uUmVzcG9uc2VUeXBlcyk6IHZhbHVlLnZhbHVlCmRlZmluZSBmdW5jdGlvbiBUb1N0cmluZyh2YWx1ZSBBdWRpdEV2ZW50QWN0aW9uKTogdmFsdWUudmFsdWUKZGVmaW5lIGZ1bmN0aW9uIFRvU3RyaW5nKHZhbHVlIEF1ZGl0RXZlbnRBZ2VudE5ldHdvcmtUeXBlKTogdmFsdWUudmFsdWUKZGVmaW5lIGZ1bmN0aW9uIFRvU3RyaW5nKHZhbHVlIEF1ZGl0RXZlbnRPdXRjb21lKTogdmFsdWUudmFsdWUKZGVmaW5lIGZ1bmN0aW9uIFRvU3RyaW5nKHZhbHVlIEJpbmRpbmdTdHJlbmd0aCk6IHZhbHVlLnZhbHVlCmRlZmluZSBmdW5jdGlvbiBUb1N0cmluZyh2YWx1ZSBCaW9sb2dpY2FsbHlEZXJpdmVkUHJvZHVjdENhdGVnb3J5KTogdmFsdWUudmFsdWUKZGVmaW5lIGZ1bmN0aW9uIFRvU3RyaW5nKHZhbHVlIEJpb2xvZ2ljYWxseURlcml2ZWRQcm9kdWN0U3RhdHVzKTogdmFsdWUudmFsdWUKZGVmaW5lIGZ1bmN0aW9uIFRvU3RyaW5nKHZhbHVlIEJpb2xvZ2ljYWxseURlcml2ZWRQcm9kdWN0U3RvcmFnZVNjYWxlKTogdmFsdWUudmFsdWUKZGVmaW5lIGZ1bmN0aW9uIFRvU3RyaW5nKHZhbHVlIEJ1bmRsZVR5cGUpOiB2YWx1ZS52YWx1ZQpkZWZpbmUgZnVuY3Rpb24gVG9TdHJpbmcodmFsdWUgQ2FwYWJpbGl0eVN0YXRlbWVudEtpbmQpOiB2YWx1ZS52YWx1ZQpkZWZpbmUgZnVuY3Rpb24gVG9TdHJpbmcodmFsdWUgQ2FyZVBsYW5BY3Rpdml0eUtpbmQpOiB2YWx1ZS52YWx1ZQpkZWZpbmUgZnVuY3Rpb24gVG9TdHJpbmcodmFsdWUgQ2FyZVBsYW5BY3Rpdml0eVN0YXR1cyk6IHZhbHVlLnZhbHVlCmRlZmluZSBmdW5jdGlvbiBUb1N0cmluZyh2YWx1ZSBDYXJlUGxhbkludGVudCk6IHZhbHVlLnZhbHVlCmRlZmluZSBmdW5jdGlvbiBUb1N0cmluZyh2YWx1ZSBDYXJlUGxhblN0YXR1cyk6IHZhbHVlLnZhbHVlCmRlZmluZSBmdW5jdGlvbiBUb1N0cmluZyh2YWx1ZSBDYXJlVGVhbVN0YXR1cyk6IHZhbHVlLnZhbHVlCmRlZmluZSBmdW5jdGlvbiBUb1N0cmluZyh2YWx1ZSBDYXRhbG9nRW50cnlSZWxhdGlvblR5cGUpOiB2YWx1ZS52YWx1ZQpkZWZpbmUgZnVuY3Rpb24gVG9TdHJpbmcodmFsdWUgQ2hhcmdlSXRlbURlZmluaXRpb25QcmljZUNvbXBvbmVudFR5cGUpOiB2YWx1ZS52YWx1ZQpkZWZpbmUgZnVuY3Rpb24gVG9TdHJpbmcodmFsdWUgQ2hhcmdlSXRlbVN0YXR1cyk6IHZhbHVlLnZhbHVlCmRlZmluZSBmdW5jdGlvbiBUb1N0cmluZyh2YWx1ZSBDbGFpbVJlc3BvbnNlU3RhdHVzKTogdmFsdWUudmFsdWUKZGVmaW5lIGZ1bmN0aW9uIFRvU3RyaW5nKHZhbHVlIENsYWltU3RhdHVzKTogdmFsdWUudmFsdWUKZGVmaW5lIGZ1bmN0aW9uIFRvU3RyaW5nKHZhbHVlIENsaW5pY2FsSW1wcmVzc2lvblN0YXR1cyk6IHZhbHVlLnZhbHVlCmRlZmluZSBmdW5jdGlvbiBUb1N0cmluZyh2YWx1ZSBDb2RlU2VhcmNoU3VwcG9ydCk6IHZhbHVlLnZhbHVlCmRlZmluZSBmdW5jdGlvbiBUb1N0cmluZyh2YWx1ZSBDb2RlU3lzdGVtQ29udGVudE1vZGUpOiB2YWx1ZS52YWx1ZQpkZWZpbmUgZnVuY3Rpb24gVG9TdHJpbmcodmFsdWUgQ29kZVN5c3RlbUhpZXJhcmNoeU1lYW5pbmcpOiB2YWx1ZS52YWx1ZQpkZWZpbmUgZnVuY3Rpb24gVG9TdHJpbmcodmFsdWUgQ29tbXVuaWNhdGlvblByaW9yaXR5KTogdmFsdWUudmFsdWUKZGVmaW5lIGZ1bmN0aW9uIFRvU3RyaW5nKHZhbHVlIENvbW11bmljYXRpb25SZXF1ZXN0U3RhdHVzKTogdmFsdWUudmFsdWUKZGVmaW5lIGZ1bmN0aW9uIFRvU3RyaW5nKHZhbHVlIENvbW11bmljYXRpb25TdGF0dXMpOiB2YWx1ZS52YWx1ZQpkZWZpbmUgZnVuY3Rpb24gVG9TdHJpbmcodmFsdWUgQ29tcGFydG1lbnRDb2RlKTogdmFsdWUudmFsdWUKZGVmaW5lIGZ1bmN0aW9uIFRvU3RyaW5nKHZhbHVlIENvbXBhcnRtZW50VHlwZSk6IHZhbHVlLnZhbHVlCmRlZmluZSBmdW5jdGlvbiBUb1N0cmluZyh2YWx1ZSBDb21wb3NpdGlvbkF0dGVzdGF0aW9uTW9kZSk6IHZhbHVlLnZhbHVlCmRlZmluZSBmdW5jdGlvbiBUb1N0cmluZyh2YWx1ZSBDb21wb3NpdGlvblN0YXR1cyk6IHZhbHVlLnZhbHVlCmRlZmluZSBmdW5jdGlvbiBUb1N0cmluZyh2YWx1ZSBDb25jZXB0TWFwRXF1aXZhbGVuY2UpOiB2YWx1ZS52YWx1ZQpkZWZpbmUgZnVuY3Rpb24gVG9TdHJpbmcodmFsdWUgQ29uY2VwdE1hcEdyb3VwVW5tYXBwZWRNb2RlKTogdmFsdWUudmFsdWUKZGVmaW5lIGZ1bmN0aW9uIFRvU3RyaW5nKHZhbHVlIENvbmRpdGlvbmFsRGVsZXRlU3RhdHVzKTogdmFsdWUudmFsdWUKZGVmaW5lIGZ1bmN0aW9uIFRvU3RyaW5nKHZhbHVlIENvbmRpdGlvbmFsUmVhZFN0YXR1cyk6IHZhbHVlLnZhbHVlCmRlZmluZSBmdW5jdGlvbiBUb1N0cmluZyh2YWx1ZSBDb25zZW50RGF0YU1lYW5pbmcpOiB2YWx1ZS52YWx1ZQpkZWZpbmUgZnVuY3Rpb24gVG9TdHJpbmcodmFsdWUgQ29uc2VudFByb3Zpc2lvblR5cGUpOiB2YWx1ZS52YWx1ZQpkZWZpbmUgZnVuY3Rpb24gVG9TdHJpbmcodmFsdWUgQ29uc2VudFN0YXRlKTogdmFsdWUudmFsdWUKZGVmaW5lIGZ1bmN0aW9uIFRvU3RyaW5nKHZhbHVlIENvbnN0cmFpbnRTZXZlcml0eSk6IHZhbHVlLnZhbHVlCmRlZmluZSBmdW5jdGlvbiBUb1N0cmluZyh2YWx1ZSBDb250YWN0UG9pbnRTeXN0ZW0pOiB2YWx1ZS52YWx1ZQpkZWZpbmUgZnVuY3Rpb24gVG9TdHJpbmcodmFsdWUgQ29udGFjdFBvaW50VXNlKTogdmFsdWUudmFsdWUKZGVmaW5lIGZ1bmN0aW9uIFRvU3RyaW5nKHZhbHVlIENvbnRyYWN0UHVibGljYXRpb25TdGF0dXMpOiB2YWx1ZS52YWx1ZQpkZWZpbmUgZnVuY3Rpb24gVG9TdHJpbmcodmFsdWUgQ29udHJhY3RTdGF0dXMpOiB2YWx1ZS52YWx1ZQpkZWZpbmUgZnVuY3Rpb24gVG9TdHJpbmcodmFsdWUgQ29udHJpYnV0b3JUeXBlKTogdmFsdWUudmFsdWUKZGVmaW5lIGZ1bmN0aW9uIFRvU3RyaW5nKHZhbHVlIENvdmVyYWdlU3RhdHVzKTogdmFsdWUudmFsdWUKZGVmaW5lIGZ1bmN0aW9uIFRvU3RyaW5nKHZhbHVlIEN1cnJlbmN5Q29kZSk6IHZhbHVlLnZhbHVlCmRlZmluZSBmdW5jdGlvbiBUb1N0cmluZyh2YWx1ZSBEYXlPZldlZWspOiB2YWx1ZS52YWx1ZQpkZWZpbmUgZnVuY3Rpb24gVG9TdHJpbmcodmFsdWUgRGF5c09mV2Vlayk6IHZhbHVlLnZhbHVlCmRlZmluZSBmdW5jdGlvbiBUb1N0cmluZyh2YWx1ZSBEZXRlY3RlZElzc3VlU2V2ZXJpdHkpOiB2YWx1ZS52YWx1ZQpkZWZpbmUgZnVuY3Rpb24gVG9TdHJpbmcodmFsdWUgRGV0ZWN0ZWRJc3N1ZVN0YXR1cyk6IHZhbHVlLnZhbHVlCmRlZmluZSBmdW5jdGlvbiBUb1N0cmluZyh2YWx1ZSBEZXZpY2VNZXRyaWNDYWxpYnJhdGlvblN0YXRlKTogdmFsdWUudmFsdWUKZGVmaW5lIGZ1bmN0aW9uIFRvU3RyaW5nKHZhbHVlIERldmljZU1ldHJpY0NhbGlicmF0aW9uVHlwZSk6IHZhbHVlLnZhbHVlCmRlZmluZSBmdW5jdGlvbiBUb1N0cmluZyh2YWx1ZSBEZXZpY2VNZXRyaWNDYXRlZ29yeSk6IHZhbHVlLnZhbHVlCmRlZmluZSBmdW5jdGlvbiBUb1N0cmluZyh2YWx1ZSBEZXZpY2VNZXRyaWNDb2xvcik6IHZhbHVlLnZhbHVlCmRlZmluZSBmdW5jdGlvbiBUb1N0cmluZyh2YWx1ZSBEZXZpY2VNZXRyaWNPcGVyYXRpb25hbFN0YXR1cyk6IHZhbHVlLnZhbHVlCmRlZmluZSBmdW5jdGlvbiBUb1N0cmluZyh2YWx1ZSBEZXZpY2VOYW1lVHlwZSk6IHZhbHVlLnZhbHVlCmRlZmluZSBmdW5jdGlvbiBUb1N0cmluZyh2YWx1ZSBEZXZpY2VSZXF1ZXN0U3RhdHVzKTogdmFsdWUudmFsdWUKZGVmaW5lIGZ1bmN0aW9uIFRvU3RyaW5nKHZhbHVlIERldmljZVVzZVN0YXRlbWVudFN0YXR1cyk6IHZhbHVlLnZhbHVlCmRlZmluZSBmdW5jdGlvbiBUb1N0cmluZyh2YWx1ZSBEaWFnbm9zdGljUmVwb3J0U3RhdHVzKTogdmFsdWUudmFsdWUKZGVmaW5lIGZ1bmN0aW9uIFRvU3RyaW5nKHZhbHVlIERpc2NyaW1pbmF0b3JUeXBlKTogdmFsdWUudmFsdWUKZGVmaW5lIGZ1bmN0aW9uIFRvU3RyaW5nKHZhbHVlIERvY3VtZW50Q29uZmlkZW50aWFsaXR5KTogdmFsdWUudmFsdWUKZGVmaW5lIGZ1bmN0aW9uIFRvU3RyaW5nKHZhbHVlIERvY3VtZW50TW9kZSk6IHZhbHVlLnZhbHVlCmRlZmluZSBmdW5jdGlvbiBUb1N0cmluZyh2YWx1ZSBEb2N1bWVudFJlZmVyZW5jZVN0YXR1cyk6IHZhbHVlLnZhbHVlCmRlZmluZSBmdW5jdGlvbiBUb1N0cmluZyh2YWx1ZSBEb2N1bWVudFJlbGF0aW9uc2hpcFR5cGUpOiB2YWx1ZS52YWx1ZQpkZWZpbmUgZnVuY3Rpb24gVG9TdHJpbmcodmFsdWUgRWxpZ2liaWxpdHlSZXF1ZXN0UHVycG9zZSk6IHZhbHVlLnZhbHVlCmRlZmluZSBmdW5jdGlvbiBUb1N0cmluZyh2YWx1ZSBFbGlnaWJpbGl0eVJlcXVlc3RTdGF0dXMpOiB2YWx1ZS52YWx1ZQpkZWZpbmUgZnVuY3Rpb24gVG9TdHJpbmcodmFsdWUgRWxpZ2liaWxpdHlSZXNwb25zZVB1cnBvc2UpOiB2YWx1ZS52YWx1ZQpkZWZpbmUgZnVuY3Rpb24gVG9TdHJpbmcodmFsdWUgRWxpZ2liaWxpdHlSZXNwb25zZVN0YXR1cyk6IHZhbHVlLnZhbHVlCmRlZmluZSBmdW5jdGlvbiBUb1N0cmluZyh2YWx1ZSBFbmFibGVXaGVuQmVoYXZpb3IpOiB2YWx1ZS52YWx1ZQpkZWZpbmUgZnVuY3Rpb24gVG9TdHJpbmcodmFsdWUgRW5jb3VudGVyTG9jYXRpb25TdGF0dXMpOiB2YWx1ZS52YWx1ZQpkZWZpbmUgZnVuY3Rpb24gVG9TdHJpbmcodmFsdWUgRW5jb3VudGVyU3RhdHVzKTogdmFsdWUudmFsdWUKZGVmaW5lIGZ1bmN0aW9uIFRvU3RyaW5nKHZhbHVlIEVuZHBvaW50U3RhdHVzKTogdmFsdWUudmFsdWUKZGVmaW5lIGZ1bmN0aW9uIFRvU3RyaW5nKHZhbHVlIEVucm9sbG1lbnRSZXF1ZXN0U3RhdHVzKTogdmFsdWUudmFsdWUKZGVmaW5lIGZ1bmN0aW9uIFRvU3RyaW5nKHZhbHVlIEVucm9sbG1lbnRSZXNwb25zZVN0YXR1cyk6IHZhbHVlLnZhbHVlCmRlZmluZSBmdW5jdGlvbiBUb1N0cmluZyh2YWx1ZSBFcGlzb2RlT2ZDYXJlU3RhdHVzKTogdmFsdWUudmFsdWUKZGVmaW5lIGZ1bmN0aW9uIFRvU3RyaW5nKHZhbHVlIEV2ZW50Q2FwYWJpbGl0eU1vZGUpOiB2YWx1ZS52YWx1ZQpkZWZpbmUgZnVuY3Rpb24gVG9TdHJpbmcodmFsdWUgRXZlbnRUaW1pbmcpOiB2YWx1ZS52YWx1ZQpkZWZpbmUgZnVuY3Rpb24gVG9TdHJpbmcodmFsdWUgRXZpZGVuY2VWYXJpYWJsZVR5cGUpOiB2YWx1ZS52YWx1ZQpkZWZpbmUgZnVuY3Rpb24gVG9TdHJpbmcodmFsdWUgRXhhbXBsZVNjZW5hcmlvQWN0b3JUeXBlKTogdmFsdWUudmFsdWUKZGVmaW5lIGZ1bmN0aW9uIFRvU3RyaW5nKHZhbHVlIEV4cGxhbmF0aW9uT2ZCZW5lZml0U3RhdHVzKTogdmFsdWUudmFsdWUKZGVmaW5lIGZ1bmN0aW9uIFRvU3RyaW5nKHZhbHVlIEV4cG9zdXJlU3RhdGUpOiB2YWx1ZS52YWx1ZQpkZWZpbmUgZnVuY3Rpb24gVG9TdHJpbmcodmFsdWUgRXh0ZW5zaW9uQ29udGV4dFR5cGUpOiB2YWx1ZS52YWx1ZQpkZWZpbmUgZnVuY3Rpb24gVG9TdHJpbmcodmFsdWUgRkhJUkFsbFR5cGVzKTogdmFsdWUudmFsdWUKZGVmaW5lIGZ1bmN0aW9uIFRvU3RyaW5nKHZhbHVlIEZISVJEZWZpbmVkVHlwZSk6IHZhbHVlLnZhbHVlCmRlZmluZSBmdW5jdGlvbiBUb1N0cmluZyh2YWx1ZSBGSElSRGV2aWNlU3RhdHVzKTogdmFsdWUudmFsdWUKZGVmaW5lIGZ1bmN0aW9uIFRvU3RyaW5nKHZhbHVlIEZISVJSZXNvdXJjZVR5cGUpOiB2YWx1ZS52YWx1ZQpkZWZpbmUgZnVuY3Rpb24gVG9TdHJpbmcodmFsdWUgRkhJUlN1YnN0YW5jZVN0YXR1cyk6IHZhbHVlLnZhbHVlCmRlZmluZSBmdW5jdGlvbiBUb1N0cmluZyh2YWx1ZSBGSElSVmVyc2lvbik6IHZhbHVlLnZhbHVlCmRlZmluZSBmdW5jdGlvbiBUb1N0cmluZyh2YWx1ZSBGYW1pbHlIaXN0b3J5U3RhdHVzKTogdmFsdWUudmFsdWUKZGVmaW5lIGZ1bmN0aW9uIFRvU3RyaW5nKHZhbHVlIEZpbHRlck9wZXJhdG9yKTogdmFsdWUudmFsdWUKZGVmaW5lIGZ1bmN0aW9uIFRvU3RyaW5nKHZhbHVlIEZsYWdTdGF0dXMpOiB2YWx1ZS52YWx1ZQpkZWZpbmUgZnVuY3Rpb24gVG9TdHJpbmcodmFsdWUgR29hbExpZmVjeWNsZVN0YXR1cyk6IHZhbHVlLnZhbHVlCmRlZmluZSBmdW5jdGlvbiBUb1N0cmluZyh2YWx1ZSBHcmFwaENvbXBhcnRtZW50UnVsZSk6IHZhbHVlLnZhbHVlCmRlZmluZSBmdW5jdGlvbiBUb1N0cmluZyh2YWx1ZSBHcmFwaENvbXBhcnRtZW50VXNlKTogdmFsdWUudmFsdWUKZGVmaW5lIGZ1bmN0aW9uIFRvU3RyaW5nKHZhbHVlIEdyb3VwTWVhc3VyZSk6IHZhbHVlLnZhbHVlCmRlZmluZSBmdW5jdGlvbiBUb1N0cmluZyh2YWx1ZSBHcm91cFR5cGUpOiB2YWx1ZS52YWx1ZQpkZWZpbmUgZnVuY3Rpb24gVG9TdHJpbmcodmFsdWUgR3VpZGFuY2VSZXNwb25zZVN0YXR1cyk6IHZhbHVlLnZhbHVlCmRlZmluZSBmdW5jdGlvbiBUb1N0cmluZyh2YWx1ZSBHdWlkZVBhZ2VHZW5lcmF0aW9uKTogdmFsdWUudmFsdWUKZGVmaW5lIGZ1bmN0aW9uIFRvU3RyaW5nKHZhbHVlIEd1aWRlUGFyYW1ldGVyQ29kZSk6IHZhbHVlLnZhbHVlCmRlZmluZSBmdW5jdGlvbiBUb1N0cmluZyh2YWx1ZSBIVFRQVmVyYik6IHZhbHVlLnZhbHVlCmRlZmluZSBmdW5jdGlvbiBUb1N0cmluZyh2YWx1ZSBJZGVudGlmaWVyVXNlKTogdmFsdWUudmFsdWUKZGVmaW5lIGZ1bmN0aW9uIFRvU3RyaW5nKHZhbHVlIElkZW50aXR5QXNzdXJhbmNlTGV2ZWwpOiB2YWx1ZS52YWx1ZQpkZWZpbmUgZnVuY3Rpb24gVG9TdHJpbmcodmFsdWUgSW1hZ2luZ1N0dWR5U3RhdHVzKTogdmFsdWUudmFsdWUKZGVmaW5lIGZ1bmN0aW9uIFRvU3RyaW5nKHZhbHVlIEltbXVuaXphdGlvbkV2YWx1YXRpb25TdGF0dXMpOiB2YWx1ZS52YWx1ZQpkZWZpbmUgZnVuY3Rpb24gVG9TdHJpbmcodmFsdWUgSW1tdW5pemF0aW9uU3RhdHVzKTogdmFsdWUudmFsdWUKZGVmaW5lIGZ1bmN0aW9uIFRvU3RyaW5nKHZhbHVlIEludm9pY2VQcmljZUNvbXBvbmVudFR5cGUpOiB2YWx1ZS52YWx1ZQpkZWZpbmUgZnVuY3Rpb24gVG9TdHJpbmcodmFsdWUgSW52b2ljZVN0YXR1cyk6IHZhbHVlLnZhbHVlCmRlZmluZSBmdW5jdGlvbiBUb1N0cmluZyh2YWx1ZSBJc3N1ZVNldmVyaXR5KTogdmFsdWUudmFsdWUKZGVmaW5lIGZ1bmN0aW9uIFRvU3RyaW5nKHZhbHVlIElzc3VlVHlwZSk6IHZhbHVlLnZhbHVlCmRlZmluZSBmdW5jdGlvbiBUb1N0cmluZyh2YWx1ZSBMaW5rVHlwZSk6IHZhbHVlLnZhbHVlCmRlZmluZSBmdW5jdGlvbiBUb1N0cmluZyh2YWx1ZSBMaW5rYWdlVHlwZSk6IHZhbHVlLnZhbHVlCmRlZmluZSBmdW5jdGlvbiBUb1N0cmluZyh2YWx1ZSBMaXN0TW9kZSk6IHZhbHVlLnZhbHVlCmRlZmluZSBmdW5jdGlvbiBUb1N0cmluZyh2YWx1ZSBMaXN0U3RhdHVzKTogdmFsdWUudmFsdWUKZGVmaW5lIGZ1bmN0aW9uIFRvU3RyaW5nKHZhbHVlIExvY2F0aW9uTW9kZSk6IHZhbHVlLnZhbHVlCmRlZmluZSBmdW5jdGlvbiBUb1N0cmluZyh2YWx1ZSBMb2NhdGlvblN0YXR1cyk6IHZhbHVlLnZhbHVlCmRlZmluZSBmdW5jdGlvbiBUb1N0cmluZyh2YWx1ZSBNZWFzdXJlUmVwb3J0U3RhdHVzKTogdmFsdWUudmFsdWUKZGVmaW5lIGZ1bmN0aW9uIFRvU3RyaW5nKHZhbHVlIE1lYXN1cmVSZXBvcnRUeXBlKTogdmFsdWUudmFsdWUKZGVmaW5lIGZ1bmN0aW9uIFRvU3RyaW5nKHZhbHVlIE1lZGlhU3RhdHVzKTogdmFsdWUudmFsdWUKZGVmaW5lIGZ1bmN0aW9uIFRvU3RyaW5nKHZhbHVlIE1lZGljYXRpb25BZG1pbmlzdHJhdGlvblN0YXR1cyk6IHZhbHVlLnZhbHVlCmRlZmluZSBmdW5jdGlvbiBUb1N0cmluZyh2YWx1ZSBNZWRpY2F0aW9uRGlzcGVuc2VTdGF0dXMpOiB2YWx1ZS52YWx1ZQpkZWZpbmUgZnVuY3Rpb24gVG9TdHJpbmcodmFsdWUgTWVkaWNhdGlvbktub3dsZWRnZVN0YXR1cyk6IHZhbHVlLnZhbHVlCmRlZmluZSBmdW5jdGlvbiBUb1N0cmluZyh2YWx1ZSBNZWRpY2F0aW9uUmVxdWVzdEludGVudCk6IHZhbHVlLnZhbHVlCmRlZmluZSBmdW5jdGlvbiBUb1N0cmluZyh2YWx1ZSBNZWRpY2F0aW9uUmVxdWVzdFByaW9yaXR5KTogdmFsdWUudmFsdWUKZGVmaW5lIGZ1bmN0aW9uIFRvU3RyaW5nKHZhbHVlIE1lZGljYXRpb25SZXF1ZXN0U3RhdHVzKTogdmFsdWUudmFsdWUKZGVmaW5lIGZ1bmN0aW9uIFRvU3RyaW5nKHZhbHVlIE1lZGljYXRpb25TdGF0ZW1lbnRTdGF0dXMpOiB2YWx1ZS52YWx1ZQpkZWZpbmUgZnVuY3Rpb24gVG9TdHJpbmcodmFsdWUgTWVkaWNhdGlvblN0YXR1cyk6IHZhbHVlLnZhbHVlCmRlZmluZSBmdW5jdGlvbiBUb1N0cmluZyh2YWx1ZSBNZXNzYWdlU2lnbmlmaWNhbmNlQ2F0ZWdvcnkpOiB2YWx1ZS52YWx1ZQpkZWZpbmUgZnVuY3Rpb24gVG9TdHJpbmcodmFsdWUgTWVzc2FnZWhlYWRlcl9SZXNwb25zZV9SZXF1ZXN0KTogdmFsdWUudmFsdWUKZGVmaW5lIGZ1bmN0aW9uIFRvU3RyaW5nKHZhbHVlIE1pbWVUeXBlKTogdmFsdWUudmFsdWUKZGVmaW5lIGZ1bmN0aW9uIFRvU3RyaW5nKHZhbHVlIE5hbWVVc2UpOiB2YWx1ZS52YWx1ZQpkZWZpbmUgZnVuY3Rpb24gVG9TdHJpbmcodmFsdWUgTmFtaW5nU3lzdGVtSWRlbnRpZmllclR5cGUpOiB2YWx1ZS52YWx1ZQpkZWZpbmUgZnVuY3Rpb24gVG9TdHJpbmcodmFsdWUgTmFtaW5nU3lzdGVtVHlwZSk6IHZhbHVlLnZhbHVlCmRlZmluZSBmdW5jdGlvbiBUb1N0cmluZyh2YWx1ZSBOYXJyYXRpdmVTdGF0dXMpOiB2YWx1ZS52YWx1ZQpkZWZpbmUgZnVuY3Rpb24gVG9TdHJpbmcodmFsdWUgTm90ZVR5cGUpOiB2YWx1ZS52YWx1ZQpkZWZpbmUgZnVuY3Rpb24gVG9TdHJpbmcodmFsdWUgTnV0cml0aWlvbk9yZGVySW50ZW50KTogdmFsdWUudmFsdWUKZGVmaW5lIGZ1bmN0aW9uIFRvU3RyaW5nKHZhbHVlIE51dHJpdGlvbk9yZGVyU3RhdHVzKTogdmFsdWUudmFsdWUKZGVmaW5lIGZ1bmN0aW9uIFRvU3RyaW5nKHZhbHVlIE9ic2VydmF0aW9uRGF0YVR5cGUpOiB2YWx1ZS52YWx1ZQpkZWZpbmUgZnVuY3Rpb24gVG9TdHJpbmcodmFsdWUgT2JzZXJ2YXRpb25SYW5nZUNhdGVnb3J5KTogdmFsdWUudmFsdWUKZGVmaW5lIGZ1bmN0aW9uIFRvU3RyaW5nKHZhbHVlIE9ic2VydmF0aW9uU3RhdHVzKTogdmFsdWUudmFsdWUKZGVmaW5lIGZ1bmN0aW9uIFRvU3RyaW5nKHZhbHVlIE9wZXJhdGlvbktpbmQpOiB2YWx1ZS52YWx1ZQpkZWZpbmUgZnVuY3Rpb24gVG9TdHJpbmcodmFsdWUgT3BlcmF0aW9uUGFyYW1ldGVyVXNlKTogdmFsdWUudmFsdWUKZGVmaW5lIGZ1bmN0aW9uIFRvU3RyaW5nKHZhbHVlIE9yaWVudGF0aW9uVHlwZSk6IHZhbHVlLnZhbHVlCmRlZmluZSBmdW5jdGlvbiBUb1N0cmluZyh2YWx1ZSBQYXJhbWV0ZXJVc2UpOiB2YWx1ZS52YWx1ZQpkZWZpbmUgZnVuY3Rpb24gVG9TdHJpbmcodmFsdWUgUGFydGljaXBhbnRSZXF1aXJlZCk6IHZhbHVlLnZhbHVlCmRlZmluZSBmdW5jdGlvbiBUb1N0cmluZyh2YWx1ZSBQYXJ0aWNpcGFudFN0YXR1cyk6IHZhbHVlLnZhbHVlCmRlZmluZSBmdW5jdGlvbiBUb1N0cmluZyh2YWx1ZSBQYXJ0aWNpcGF0aW9uU3RhdHVzKTogdmFsdWUudmFsdWUKZGVmaW5lIGZ1bmN0aW9uIFRvU3RyaW5nKHZhbHVlIFBheW1lbnROb3RpY2VTdGF0dXMpOiB2YWx1ZS52YWx1ZQpkZWZpbmUgZnVuY3Rpb24gVG9TdHJpbmcodmFsdWUgUGF5bWVudFJlY29uY2lsaWF0aW9uU3RhdHVzKTogdmFsdWUudmFsdWUKZGVmaW5lIGZ1bmN0aW9uIFRvU3RyaW5nKHZhbHVlIFByb2NlZHVyZVN0YXR1cyk6IHZhbHVlLnZhbHVlCmRlZmluZSBmdW5jdGlvbiBUb1N0cmluZyh2YWx1ZSBQcm9wZXJ0eVJlcHJlc2VudGF0aW9uKTogdmFsdWUudmFsdWUKZGVmaW5lIGZ1bmN0aW9uIFRvU3RyaW5nKHZhbHVlIFByb3BlcnR5VHlwZSk6IHZhbHVlLnZhbHVlCmRlZmluZSBmdW5jdGlvbiBUb1N0cmluZyh2YWx1ZSBQcm92ZW5hbmNlRW50aXR5Um9sZSk6IHZhbHVlLnZhbHVlCmRlZmluZSBmdW5jdGlvbiBUb1N0cmluZyh2YWx1ZSBQdWJsaWNhdGlvblN0YXR1cyk6IHZhbHVlLnZhbHVlCmRlZmluZSBmdW5jdGlvbiBUb1N0cmluZyh2YWx1ZSBRdWFsaXR5VHlwZSk6IHZhbHVlLnZhbHVlCmRlZmluZSBmdW5jdGlvbiBUb1N0cmluZyh2YWx1ZSBRdWFudGl0eUNvbXBhcmF0b3IpOiB2YWx1ZS52YWx1ZQpkZWZpbmUgZnVuY3Rpb24gVG9TdHJpbmcodmFsdWUgUXVlc3Rpb25uYWlyZUl0ZW1PcGVyYXRvcik6IHZhbHVlLnZhbHVlCmRlZmluZSBmdW5jdGlvbiBUb1N0cmluZyh2YWx1ZSBRdWVzdGlvbm5haXJlSXRlbVR5cGUpOiB2YWx1ZS52YWx1ZQpkZWZpbmUgZnVuY3Rpb24gVG9TdHJpbmcodmFsdWUgUXVlc3Rpb25uYWlyZVJlc3BvbnNlU3RhdHVzKTogdmFsdWUudmFsdWUKZGVmaW5lIGZ1bmN0aW9uIFRvU3RyaW5nKHZhbHVlIFJlZmVyZW5jZUhhbmRsaW5nUG9saWN5KTogdmFsdWUudmFsdWUKZGVmaW5lIGZ1bmN0aW9uIFRvU3RyaW5nKHZhbHVlIFJlZmVyZW5jZVZlcnNpb25SdWxlcyk6IHZhbHVlLnZhbHVlCmRlZmluZSBmdW5jdGlvbiBUb1N0cmluZyh2YWx1ZSBSZWZlcnJlZERvY3VtZW50U3RhdHVzKTogdmFsdWUudmFsdWUKZGVmaW5lIGZ1bmN0aW9uIFRvU3RyaW5nKHZhbHVlIFJlbGF0ZWRBcnRpZmFjdFR5cGUpOiB2YWx1ZS52YWx1ZQpkZWZpbmUgZnVuY3Rpb24gVG9TdHJpbmcodmFsdWUgUmVtaXR0YW5jZU91dGNvbWUpOiB2YWx1ZS52YWx1ZQpkZWZpbmUgZnVuY3Rpb24gVG9TdHJpbmcodmFsdWUgUmVwb3NpdG9yeVR5cGUpOiB2YWx1ZS52YWx1ZQpkZWZpbmUgZnVuY3Rpb24gVG9TdHJpbmcodmFsdWUgUmVxdWVzdEludGVudCk6IHZhbHVlLnZhbHVlCmRlZmluZSBmdW5jdGlvbiBUb1N0cmluZyh2YWx1ZSBSZXF1ZXN0UHJpb3JpdHkpOiB2YWx1ZS52YWx1ZQpkZWZpbmUgZnVuY3Rpb24gVG9TdHJpbmcodmFsdWUgUmVxdWVzdFN0YXR1cyk6IHZhbHVlLnZhbHVlCmRlZmluZSBmdW5jdGlvbiBUb1N0cmluZyh2YWx1ZSBSZXNlYXJjaEVsZW1lbnRUeXBlKTogdmFsdWUudmFsdWUKZGVmaW5lIGZ1bmN0aW9uIFRvU3RyaW5nKHZhbHVlIFJlc2VhcmNoU3R1ZHlTdGF0dXMpOiB2YWx1ZS52YWx1ZQpkZWZpbmUgZnVuY3Rpb24gVG9TdHJpbmcodmFsdWUgUmVzZWFyY2hTdWJqZWN0U3RhdHVzKTogdmFsdWUudmFsdWUKZGVmaW5lIGZ1bmN0aW9uIFRvU3RyaW5nKHZhbHVlIFJlc291cmNlVHlwZSk6IHZhbHVlLnZhbHVlCmRlZmluZSBmdW5jdGlvbiBUb1N0cmluZyh2YWx1ZSBSZXNvdXJjZVZlcnNpb25Qb2xpY3kpOiB2YWx1ZS52YWx1ZQpkZWZpbmUgZnVuY3Rpb24gVG9TdHJpbmcodmFsdWUgUmVzcG9uc2VUeXBlKTogdmFsdWUudmFsdWUKZGVmaW5lIGZ1bmN0aW9uIFRvU3RyaW5nKHZhbHVlIFJlc3RmdWxDYXBhYmlsaXR5TW9kZSk6IHZhbHVlLnZhbHVlCmRlZmluZSBmdW5jdGlvbiBUb1N0cmluZyh2YWx1ZSBSaXNrQXNzZXNzbWVudFN0YXR1cyk6IHZhbHVlLnZhbHVlCmRlZmluZSBmdW5jdGlvbiBUb1N0cmluZyh2YWx1ZSBTUERYTGljZW5zZSk6IHZhbHVlLnZhbHVlCmRlZmluZSBmdW5jdGlvbiBUb1N0cmluZyh2YWx1ZSBTZWFyY2hDb21wYXJhdG9yKTogdmFsdWUudmFsdWUKZGVmaW5lIGZ1bmN0aW9uIFRvU3RyaW5nKHZhbHVlIFNlYXJjaEVudHJ5TW9kZSk6IHZhbHVlLnZhbHVlCmRlZmluZSBmdW5jdGlvbiBUb1N0cmluZyh2YWx1ZSBTZWFyY2hNb2RpZmllckNvZGUpOiB2YWx1ZS52YWx1ZQpkZWZpbmUgZnVuY3Rpb24gVG9TdHJpbmcodmFsdWUgU2VhcmNoUGFyYW1UeXBlKTogdmFsdWUudmFsdWUKZGVmaW5lIGZ1bmN0aW9uIFRvU3RyaW5nKHZhbHVlIFNlY3Rpb25Nb2RlKTogdmFsdWUudmFsdWUKZGVmaW5lIGZ1bmN0aW9uIFRvU3RyaW5nKHZhbHVlIFNlcXVlbmNlVHlwZSk6IHZhbHVlLnZhbHVlCmRlZmluZSBmdW5jdGlvbiBUb1N0cmluZyh2YWx1ZSBTZXJ2aWNlUmVxdWVzdEludGVudCk6IHZhbHVlLnZhbHVlCmRlZmluZSBmdW5jdGlvbiBUb1N0cmluZyh2YWx1ZSBTZXJ2aWNlUmVxdWVzdFByaW9yaXR5KTogdmFsdWUudmFsdWUKZGVmaW5lIGZ1bmN0aW9uIFRvU3RyaW5nKHZhbHVlIFNlcnZpY2VSZXF1ZXN0U3RhdHVzKTogdmFsdWUudmFsdWUKZGVmaW5lIGZ1bmN0aW9uIFRvU3RyaW5nKHZhbHVlIFNsaWNpbmdSdWxlcyk6IHZhbHVlLnZhbHVlCmRlZmluZSBmdW5jdGlvbiBUb1N0cmluZyh2YWx1ZSBTbG90U3RhdHVzKTogdmFsdWUudmFsdWUKZGVmaW5lIGZ1bmN0aW9uIFRvU3RyaW5nKHZhbHVlIFNvcnREaXJlY3Rpb24pOiB2YWx1ZS52YWx1ZQpkZWZpbmUgZnVuY3Rpb24gVG9TdHJpbmcodmFsdWUgU3BlY2ltZW5Db250YWluZWRQcmVmZXJlbmNlKTogdmFsdWUudmFsdWUKZGVmaW5lIGZ1bmN0aW9uIFRvU3RyaW5nKHZhbHVlIFNwZWNpbWVuU3RhdHVzKTogdmFsdWUudmFsdWUKZGVmaW5lIGZ1bmN0aW9uIFRvU3RyaW5nKHZhbHVlIFN0YXR1cyk6IHZhbHVlLnZhbHVlCmRlZmluZSBmdW5jdGlvbiBUb1N0cmluZyh2YWx1ZSBTdHJhbmRUeXBlKTogdmFsdWUudmFsdWUKZGVmaW5lIGZ1bmN0aW9uIFRvU3RyaW5nKHZhbHVlIFN0cnVjdHVyZURlZmluaXRpb25LaW5kKTogdmFsdWUudmFsdWUKZGVmaW5lIGZ1bmN0aW9uIFRvU3RyaW5nKHZhbHVlIFN0cnVjdHVyZU1hcENvbnRleHRUeXBlKTogdmFsdWUudmFsdWUKZGVmaW5lIGZ1bmN0aW9uIFRvU3RyaW5nKHZhbHVlIFN0cnVjdHVyZU1hcEdyb3VwVHlwZU1vZGUpOiB2YWx1ZS52YWx1ZQpkZWZpbmUgZnVuY3Rpb24gVG9TdHJpbmcodmFsdWUgU3RydWN0dXJlTWFwSW5wdXRNb2RlKTogdmFsdWUudmFsdWUKZGVmaW5lIGZ1bmN0aW9uIFRvU3RyaW5nKHZhbHVlIFN0cnVjdHVyZU1hcE1vZGVsTW9kZSk6IHZhbHVlLnZhbHVlCmRlZmluZSBmdW5jdGlvbiBUb1N0cmluZyh2YWx1ZSBTdHJ1Y3R1cmVNYXBTb3VyY2VMaXN0TW9kZSk6IHZhbHVlLnZhbHVlCmRlZmluZSBmdW5jdGlvbiBUb1N0cmluZyh2YWx1ZSBTdHJ1Y3R1cmVNYXBUYXJnZXRMaXN0TW9kZSk6IHZhbHVlLnZhbHVlCmRlZmluZSBmdW5jdGlvbiBUb1N0cmluZyh2YWx1ZSBTdHJ1Y3R1cmVNYXBUcmFuc2Zvcm0pOiB2YWx1ZS52YWx1ZQpkZWZpbmUgZnVuY3Rpb24gVG9TdHJpbmcodmFsdWUgU3Vic2NyaXB0aW9uQ2hhbm5lbFR5cGUpOiB2YWx1ZS52YWx1ZQpkZWZpbmUgZnVuY3Rpb24gVG9TdHJpbmcodmFsdWUgU3Vic2NyaXB0aW9uU3RhdHVzKTogdmFsdWUudmFsdWUKZGVmaW5lIGZ1bmN0aW9uIFRvU3RyaW5nKHZhbHVlIFN1cHBseURlbGl2ZXJ5U3RhdHVzKTogdmFsdWUudmFsdWUKZGVmaW5lIGZ1bmN0aW9uIFRvU3RyaW5nKHZhbHVlIFN1cHBseVJlcXVlc3RTdGF0dXMpOiB2YWx1ZS52YWx1ZQpkZWZpbmUgZnVuY3Rpb24gVG9TdHJpbmcodmFsdWUgU3lzdGVtUmVzdGZ1bEludGVyYWN0aW9uKTogdmFsdWUudmFsdWUKZGVmaW5lIGZ1bmN0aW9uIFRvU3RyaW5nKHZhbHVlIFRhc2tJbnRlbnQpOiB2YWx1ZS52YWx1ZQpkZWZpbmUgZnVuY3Rpb24gVG9TdHJpbmcodmFsdWUgVGFza1ByaW9yaXR5KTogdmFsdWUudmFsdWUKZGVmaW5lIGZ1bmN0aW9uIFRvU3RyaW5nKHZhbHVlIFRhc2tTdGF0dXMpOiB2YWx1ZS52YWx1ZQpkZWZpbmUgZnVuY3Rpb24gVG9TdHJpbmcodmFsdWUgVGVzdFJlcG9ydEFjdGlvblJlc3VsdCk6IHZhbHVlLnZhbHVlCmRlZmluZSBmdW5jdGlvbiBUb1N0cmluZyh2YWx1ZSBUZXN0UmVwb3J0UGFydGljaXBhbnRUeXBlKTogdmFsdWUudmFsdWUKZGVmaW5lIGZ1bmN0aW9uIFRvU3RyaW5nKHZhbHVlIFRlc3RSZXBvcnRSZXN1bHQpOiB2YWx1ZS52YWx1ZQpkZWZpbmUgZnVuY3Rpb24gVG9TdHJpbmcodmFsdWUgVGVzdFJlcG9ydFN0YXR1cyk6IHZhbHVlLnZhbHVlCmRlZmluZSBmdW5jdGlvbiBUb1N0cmluZyh2YWx1ZSBUZXN0U2NyaXB0UmVxdWVzdE1ldGhvZENvZGUpOiB2YWx1ZS52YWx1ZQpkZWZpbmUgZnVuY3Rpb24gVG9TdHJpbmcodmFsdWUgVHJpZ2dlclR5cGUpOiB2YWx1ZS52YWx1ZQpkZWZpbmUgZnVuY3Rpb24gVG9TdHJpbmcodmFsdWUgVHlwZURlcml2YXRpb25SdWxlKTogdmFsdWUudmFsdWUKZGVmaW5lIGZ1bmN0aW9uIFRvU3RyaW5nKHZhbHVlIFR5cGVSZXN0ZnVsSW50ZXJhY3Rpb24pOiB2YWx1ZS52YWx1ZQpkZWZpbmUgZnVuY3Rpb24gVG9TdHJpbmcodmFsdWUgVURJRW50cnlUeXBlKTogdmFsdWUudmFsdWUKZGVmaW5lIGZ1bmN0aW9uIFRvU3RyaW5nKHZhbHVlIFVuaXRzT2ZUaW1lKTogdmFsdWUudmFsdWUKZGVmaW5lIGZ1bmN0aW9uIFRvU3RyaW5nKHZhbHVlIFVzZSk6IHZhbHVlLnZhbHVlCmRlZmluZSBmdW5jdGlvbiBUb1N0cmluZyh2YWx1ZSBWYXJpYWJsZVR5cGUpOiB2YWx1ZS52YWx1ZQpkZWZpbmUgZnVuY3Rpb24gVG9TdHJpbmcodmFsdWUgVmlzaW9uQmFzZSk6IHZhbHVlLnZhbHVlCmRlZmluZSBmdW5jdGlvbiBUb1N0cmluZyh2YWx1ZSBWaXNpb25FeWVzKTogdmFsdWUudmFsdWUKZGVmaW5lIGZ1bmN0aW9uIFRvU3RyaW5nKHZhbHVlIFZpc2lvblN0YXR1cyk6IHZhbHVlLnZhbHVlCmRlZmluZSBmdW5jdGlvbiBUb1N0cmluZyh2YWx1ZSBYUGF0aFVzYWdlVHlwZSk6IHZhbHVlLnZhbHVlCmRlZmluZSBmdW5jdGlvbiBUb1N0cmluZyh2YWx1ZSBiYXNlNjRCaW5hcnkpOiB2YWx1ZS52YWx1ZQpkZWZpbmUgZnVuY3Rpb24gVG9TdHJpbmcodmFsdWUgaWQpOiB2YWx1ZS52YWx1ZQpkZWZpbmUgZnVuY3Rpb24gVG9Cb29sZWFuKHZhbHVlIGJvb2xlYW4pOiB2YWx1ZS52YWx1ZQpkZWZpbmUgZnVuY3Rpb24gVG9EYXRlKHZhbHVlIGRhdGUpOiB2YWx1ZS52YWx1ZQpkZWZpbmUgZnVuY3Rpb24gVG9EYXRlVGltZSh2YWx1ZSBkYXRlVGltZSk6IHZhbHVlLnZhbHVlCmRlZmluZSBmdW5jdGlvbiBUb0RlY2ltYWwodmFsdWUgZGVjaW1hbCk6IHZhbHVlLnZhbHVlCmRlZmluZSBmdW5jdGlvbiBUb0RhdGVUaW1lKHZhbHVlIGluc3RhbnQpOiB2YWx1ZS52YWx1ZQpkZWZpbmUgZnVuY3Rpb24gVG9JbnRlZ2VyKHZhbHVlIGludGVnZXIpOiB2YWx1ZS52YWx1ZQpkZWZpbmUgZnVuY3Rpb24gVG9TdHJpbmcodmFsdWUgc3RyaW5nKTogdmFsdWUudmFsdWUKZGVmaW5lIGZ1bmN0aW9uIFRvVGltZSh2YWx1ZSB0aW1lKTogdmFsdWUudmFsdWUKZGVmaW5lIGZ1bmN0aW9uIFRvU3RyaW5nKHZhbHVlIHVyaSk6IHZhbHVlLnZhbHVlCmRlZmluZSBmdW5jdGlvbiBUb1N0cmluZyh2YWx1ZSB4aHRtbCk6IHZhbHVlLnZhbHVlCg==" - }, - { - "contentType": "application/elm+xml", - "data": "PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0iVVRGLTgiPz4KPGxpYnJhcnkgeG1sbnM9InVybjpobDctb3JnOmVsbTpyMSIgeG1sbnM6dD0idXJuOmhsNy1vcmc6ZWxtLXR5cGVzOnIxIiB4bWxuczp4c2k9Imh0dHA6Ly93d3cudzMub3JnLzIwMDEvWE1MU2NoZW1hLWluc3RhbmNlIiB4bWxuczp4c2Q9Imh0dHA6Ly93d3cudzMub3JnLzIwMDEvWE1MU2NoZW1hIiB4bWxuczpmaGlyPSJodHRwOi8vaGw3Lm9yZy9maGlyIiB4bWxuczpxZG00Mz0idXJuOmhlYWx0aGl0LWdvdjpxZG06djRfMyIgeG1sbnM6cWRtNTM9InVybjpoZWFsdGhpdC1nb3Y6cWRtOnY1XzMiIHhtbG5zOmE9InVybjpobDctb3JnOmNxbC1hbm5vdGF0aW9uczpyMSI+CiAgIDxhbm5vdGF0aW9uIHRyYW5zbGF0b3JWZXJzaW9uPSIyLjcuMCIgdHJhbnNsYXRvck9wdGlvbnM9IkVuYWJsZUxvY2F0b3JzLERpc2FibGVMaXN0RGVtb3Rpb24sRGlzYWJsZUxpc3RQcm9tb3Rpb24iIHhzaTp0eXBlPSJhOkNxbFRvRWxtSW5mbyIvPgogICA8aWRlbnRpZmllciBpZD0iRFFNRkhJUkhlbHBlcnMiIHN5c3RlbT0iaHR0cDovL2NvbnRlbnQuYWxwaG9yYS5jb20vZmhpci9kcW0iIHZlcnNpb249IjQuMC4xIi8+CiAgIDxzY2hlbWFJZGVudGlmaWVyIGlkPSJ1cm46aGw3LW9yZzplbG0iIHZlcnNpb249InIxIi8+CiAgIDx1c2luZ3M+CiAgICAgIDxkZWYgbG9jYWxJZGVudGlmaWVyPSJTeXN0ZW0iIHVyaT0idXJuOmhsNy1vcmc6ZWxtLXR5cGVzOnIxIi8+CiAgICAgIDxkZWYgbG9jYXRvcj0iMzoxLTM6MjYiIGxvY2FsSWRlbnRpZmllcj0iRkhJUiIgdXJpPSJodHRwOi8vaGw3Lm9yZy9maGlyIiB2ZXJzaW9uPSI0LjAuMSIvPgogICA8L3VzaW5ncz4KICAgPHN0YXRlbWVudHM+CiAgICAgIDxkZWYgbG9jYXRvcj0iNToxLTk6NTgiIG5hbWU9IlRvSW50ZXJ2YWwiIGNvbnRleHQ9IlBhdGllbnQiIGFjY2Vzc0xldmVsPSJQdWJsaWMiIHhzaTp0eXBlPSJGdW5jdGlvbkRlZiI+CiAgICAgICAgIDxleHByZXNzaW9uIGxvY2F0b3I9IjY6NS05OjU4IiB4c2k6dHlwZT0iSWYiPgogICAgICAgICAgICA8Y29uZGl0aW9uIGxvY2F0b3I9IjY6OC02OjIxIiB4c2k6dHlwZT0iSXNOdWxsIj4KICAgICAgICAgICAgICAgPG9wZXJhbmQgbG9jYXRvcj0iNjo4LTY6MTMiIG5hbWU9InBlcmlvZCIgeHNpOnR5cGU9Ik9wZXJhbmRSZWYiLz4KICAgICAgICAgICAgPC9jb25kaXRpb24+CiAgICAgICAgICAgIDx0aGVuIHhzaTp0eXBlPSJBcyI+CiAgICAgICAgICAgICAgIDxvcGVyYW5kIGxvY2F0b3I9Ijc6OS03OjEyIiB4c2k6dHlwZT0iTnVsbCIvPgogICAgICAgICAgICAgICA8YXNUeXBlU3BlY2lmaWVyIHhzaTp0eXBlPSJJbnRlcnZhbFR5cGVTcGVjaWZpZXIiPgogICAgICAgICAgICAgICAgICA8cG9pbnRUeXBlIG5hbWU9InQ6RGF0ZVRpbWUiIHhzaTp0eXBlPSJOYW1lZFR5cGVTcGVjaWZpZXIiLz4KICAgICAgICAgICAgICAgPC9hc1R5cGVTcGVjaWZpZXI+CiAgICAgICAgICAgIDwvdGhlbj4KICAgICAgICAgICAgPGVsc2UgbG9jYXRvcj0iOTo5LTk6NTgiIGxvd0Nsb3NlZD0idHJ1ZSIgaGlnaENsb3NlZD0idHJ1ZSIgeHNpOnR5cGU9IkludGVydmFsIj4KICAgICAgICAgICAgICAgPGxvdyBsb2NhdG9yPSI5OjE4LTk6MzciIHBhdGg9InZhbHVlIiB4c2k6dHlwZT0iUHJvcGVydHkiPgogICAgICAgICAgICAgICAgICA8c291cmNlIGxvY2F0b3I9Ijk6MTgtOTozMSIgcGF0aD0ic3RhcnQiIHhzaTp0eXBlPSJQcm9wZXJ0eSI+CiAgICAgICAgICAgICAgICAgICAgIDxzb3VyY2UgbG9jYXRvcj0iOToxOC05OjIzIiBuYW1lPSJwZXJpb2QiIHhzaTp0eXBlPSJPcGVyYW5kUmVmIi8+CiAgICAgICAgICAgICAgICAgIDwvc291cmNlPgogICAgICAgICAgICAgICA8L2xvdz4KICAgICAgICAgICAgICAgPGhpZ2ggbG9jYXRvcj0iOTo0MC05OjU3IiBwYXRoPSJ2YWx1ZSIgeHNpOnR5cGU9IlByb3BlcnR5Ij4KICAgICAgICAgICAgICAgICAgPHNvdXJjZSBsb2NhdG9yPSI5OjQwLTk6NTEiIHBhdGg9ImVuZCIgeHNpOnR5cGU9IlByb3BlcnR5Ij4KICAgICAgICAgICAgICAgICAgICAgPHNvdXJjZSBsb2NhdG9yPSI5OjQwLTk6NDUiIG5hbWU9InBlcmlvZCIgeHNpOnR5cGU9Ik9wZXJhbmRSZWYiLz4KICAgICAgICAgICAgICAgICAgPC9zb3VyY2U+CiAgICAgICAgICAgICAgIDwvaGlnaD4KICAgICAgICAgICAgPC9lbHNlPgogICAgICAgICA8L2V4cHJlc3Npb24+CiAgICAgICAgIDxvcGVyYW5kIG5hbWU9InBlcmlvZCI+CiAgICAgICAgICAgIDxvcGVyYW5kVHlwZVNwZWNpZmllciBsb2NhdG9yPSI1OjM1LTU6NDUiIG5hbWU9ImZoaXI6UGVyaW9kIiB4c2k6dHlwZT0iTmFtZWRUeXBlU3BlY2lmaWVyIi8+CiAgICAgICAgIDwvb3BlcmFuZD4KICAgICAgPC9kZWY+CiAgICAgIDxkZWYgbG9jYXRvcj0iMTE6MS0xNTo4MiIgbmFtZT0iVG9RdWFudGl0eSIgY29udGV4dD0iUGF0aWVudCIgYWNjZXNzTGV2ZWw9IlB1YmxpYyIgeHNpOnR5cGU9IkZ1bmN0aW9uRGVmIj4KICAgICAgICAgPGV4cHJlc3Npb24gbG9jYXRvcj0iMTI6NS0xNTo4MiIgeHNpOnR5cGU9IklmIj4KICAgICAgICAgICAgPGNvbmRpdGlvbiBsb2NhdG9yPSIxMjo4LTEyOjIzIiB4c2k6dHlwZT0iSXNOdWxsIj4KICAgICAgICAgICAgICAgPG9wZXJhbmQgbG9jYXRvcj0iMTI6OC0xMjoxNSIgbmFtZT0icXVhbnRpdHkiIHhzaTp0eXBlPSJPcGVyYW5kUmVmIi8+CiAgICAgICAgICAgIDwvY29uZGl0aW9uPgogICAgICAgICAgICA8dGhlbiBhc1R5cGU9InQ6UXVhbnRpdHkiIHhzaTp0eXBlPSJBcyI+CiAgICAgICAgICAgICAgIDxvcGVyYW5kIGxvY2F0b3I9IjEzOjktMTM6MTIiIHhzaTp0eXBlPSJOdWxsIi8+CiAgICAgICAgICAgIDwvdGhlbj4KICAgICAgICAgICAgPGVsc2UgbG9jYXRvcj0iMTU6OS0xNTo4MiIgY2xhc3NUeXBlPSJ0OlF1YW50aXR5IiB4c2k6dHlwZT0iSW5zdGFuY2UiPgogICAgICAgICAgICAgICA8ZWxlbWVudCBuYW1lPSJ2YWx1ZSI+CiAgICAgICAgICAgICAgICAgIDx2YWx1ZSBsb2NhdG9yPSIxNTozNC0xNTo1MyIgcGF0aD0idmFsdWUiIHhzaTp0eXBlPSJQcm9wZXJ0eSI+CiAgICAgICAgICAgICAgICAgICAgIDxzb3VyY2UgbG9jYXRvcj0iMTU6MzQtMTU6NDciIHBhdGg9InZhbHVlIiB4c2k6dHlwZT0iUHJvcGVydHkiPgogICAgICAgICAgICAgICAgICAgICAgICA8c291cmNlIGxvY2F0b3I9IjE1OjM0LTE1OjQxIiBuYW1lPSJxdWFudGl0eSIgeHNpOnR5cGU9Ik9wZXJhbmRSZWYiLz4KICAgICAgICAgICAgICAgICAgICAgPC9zb3VyY2U+CiAgICAgICAgICAgICAgICAgIDwvdmFsdWU+CiAgICAgICAgICAgICAgIDwvZWxlbWVudD4KICAgICAgICAgICAgICAgPGVsZW1lbnQgbmFtZT0idW5pdCI+CiAgICAgICAgICAgICAgICAgIDx2YWx1ZSBsb2NhdG9yPSIxNTo2Mi0xNTo4MCIgcGF0aD0idmFsdWUiIHhzaTp0eXBlPSJQcm9wZXJ0eSI+CiAgICAgICAgICAgICAgICAgICAgIDxzb3VyY2UgbG9jYXRvcj0iMTU6NjItMTU6NzQiIHBhdGg9InVuaXQiIHhzaTp0eXBlPSJQcm9wZXJ0eSI+CiAgICAgICAgICAgICAgICAgICAgICAgIDxzb3VyY2UgbG9jYXRvcj0iMTU6NjItMTU6NjkiIG5hbWU9InF1YW50aXR5IiB4c2k6dHlwZT0iT3BlcmFuZFJlZiIvPgogICAgICAgICAgICAgICAgICAgICA8L3NvdXJjZT4KICAgICAgICAgICAgICAgICAgPC92YWx1ZT4KICAgICAgICAgICAgICAgPC9lbGVtZW50PgogICAgICAgICAgICA8L2Vsc2U+CiAgICAgICAgIDwvZXhwcmVzc2lvbj4KICAgICAgICAgPG9wZXJhbmQgbmFtZT0icXVhbnRpdHkiPgogICAgICAgICAgICA8b3BlcmFuZFR5cGVTcGVjaWZpZXIgbG9jYXRvcj0iMTE6MzctMTE6NDkiIG5hbWU9ImZoaXI6UXVhbnRpdHkiIHhzaTp0eXBlPSJOYW1lZFR5cGVTcGVjaWZpZXIiLz4KICAgICAgICAgPC9vcGVyYW5kPgogICAgICA8L2RlZj4KICAgICAgPGRlZiBsb2NhdG9yPSIxNzoxLTIxOjEwNyIgbmFtZT0iVG9SYXRpbyIgY29udGV4dD0iUGF0aWVudCIgYWNjZXNzTGV2ZWw9IlB1YmxpYyIgeHNpOnR5cGU9IkZ1bmN0aW9uRGVmIj4KICAgICAgICAgPGV4cHJlc3Npb24gbG9jYXRvcj0iMTg6NS0yMToxMDciIHhzaTp0eXBlPSJJZiI+CiAgICAgICAgICAgIDxjb25kaXRpb24gbG9jYXRvcj0iMTg6OC0xODoyMCIgeHNpOnR5cGU9IklzTnVsbCI+CiAgICAgICAgICAgICAgIDxvcGVyYW5kIGxvY2F0b3I9IjE4OjgtMTg6MTIiIG5hbWU9InJhdGlvIiB4c2k6dHlwZT0iT3BlcmFuZFJlZiIvPgogICAgICAgICAgICA8L2NvbmRpdGlvbj4KICAgICAgICAgICAgPHRoZW4gYXNUeXBlPSJ0OlJhdGlvIiB4c2k6dHlwZT0iQXMiPgogICAgICAgICAgICAgICA8b3BlcmFuZCBsb2NhdG9yPSIxOTo5LTE5OjEyIiB4c2k6dHlwZT0iTnVsbCIvPgogICAgICAgICAgICA8L3RoZW4+CiAgICAgICAgICAgIDxlbHNlIGxvY2F0b3I9IjIxOjktMjE6MTA3IiBjbGFzc1R5cGU9InQ6UmF0aW8iIHhzaTp0eXBlPSJJbnN0YW5jZSI+CiAgICAgICAgICAgICAgIDxlbGVtZW50IG5hbWU9Im51bWVyYXRvciI+CiAgICAgICAgICAgICAgICAgIDx2YWx1ZSBsb2NhdG9yPSIyMTozNS0yMTo2MSIgbmFtZT0iVG9RdWFudGl0eSIgeHNpOnR5cGU9IkZ1bmN0aW9uUmVmIj4KICAgICAgICAgICAgICAgICAgICAgPG9wZXJhbmQgbG9jYXRvcj0iMjE6NDYtMjE6NjAiIHBhdGg9Im51bWVyYXRvciIgeHNpOnR5cGU9IlByb3BlcnR5Ij4KICAgICAgICAgICAgICAgICAgICAgICAgPHNvdXJjZSBsb2NhdG9yPSIyMTo0Ni0yMTo1MCIgbmFtZT0icmF0aW8iIHhzaTp0eXBlPSJPcGVyYW5kUmVmIi8+CiAgICAgICAgICAgICAgICAgICAgIDwvb3BlcmFuZD4KICAgICAgICAgICAgICAgICAgPC92YWx1ZT4KICAgICAgICAgICAgICAgPC9lbGVtZW50PgogICAgICAgICAgICAgICA8ZWxlbWVudCBuYW1lPSJkZW5vbWluYXRvciI+CiAgICAgICAgICAgICAgICAgIDx2YWx1ZSBsb2NhdG9yPSIyMTo3Ny0yMToxMDUiIG5hbWU9IlRvUXVhbnRpdHkiIHhzaTp0eXBlPSJGdW5jdGlvblJlZiI+CiAgICAgICAgICAgICAgICAgICAgIDxvcGVyYW5kIGxvY2F0b3I9IjIxOjg4LTIxOjEwNCIgcGF0aD0iZGVub21pbmF0b3IiIHhzaTp0eXBlPSJQcm9wZXJ0eSI+CiAgICAgICAgICAgICAgICAgICAgICAgIDxzb3VyY2UgbG9jYXRvcj0iMjE6ODgtMjE6OTIiIG5hbWU9InJhdGlvIiB4c2k6dHlwZT0iT3BlcmFuZFJlZiIvPgogICAgICAgICAgICAgICAgICAgICA8L29wZXJhbmQ+CiAgICAgICAgICAgICAgICAgIDwvdmFsdWU+CiAgICAgICAgICAgICAgIDwvZWxlbWVudD4KICAgICAgICAgICAgPC9lbHNlPgogICAgICAgICA8L2V4cHJlc3Npb24+CiAgICAgICAgIDxvcGVyYW5kIG5hbWU9InJhdGlvIj4KICAgICAgICAgICAgPG9wZXJhbmRUeXBlU3BlY2lmaWVyIGxvY2F0b3I9IjE3OjMxLTE3OjQwIiBuYW1lPSJmaGlyOlJhdGlvIiB4c2k6dHlwZT0iTmFtZWRUeXBlU3BlY2lmaWVyIi8+CiAgICAgICAgIDwvb3BlcmFuZD4KICAgICAgPC9kZWY+CiAgICAgIDxkZWYgbG9jYXRvcj0iMjM6MS0yNzo2MyIgbmFtZT0iVG9JbnRlcnZhbCIgY29udGV4dD0iUGF0aWVudCIgYWNjZXNzTGV2ZWw9IlB1YmxpYyIgeHNpOnR5cGU9IkZ1bmN0aW9uRGVmIj4KICAgICAgICAgPGV4cHJlc3Npb24gbG9jYXRvcj0iMjQ6NS0yNzo2MyIgeHNpOnR5cGU9IklmIj4KICAgICAgICAgICAgPGNvbmRpdGlvbiBsb2NhdG9yPSIyNDo4LTI0OjIwIiB4c2k6dHlwZT0iSXNOdWxsIj4KICAgICAgICAgICAgICAgPG9wZXJhbmQgbG9jYXRvcj0iMjQ6OC0yNDoxMiIgbmFtZT0icmFuZ2UiIHhzaTp0eXBlPSJPcGVyYW5kUmVmIi8+CiAgICAgICAgICAgIDwvY29uZGl0aW9uPgogICAgICAgICAgICA8dGhlbiB4c2k6dHlwZT0iQXMiPgogICAgICAgICAgICAgICA8b3BlcmFuZCBsb2NhdG9yPSIyNTo5LTI1OjEyIiB4c2k6dHlwZT0iTnVsbCIvPgogICAgICAgICAgICAgICA8YXNUeXBlU3BlY2lmaWVyIHhzaTp0eXBlPSJJbnRlcnZhbFR5cGVTcGVjaWZpZXIiPgogICAgICAgICAgICAgICAgICA8cG9pbnRUeXBlIG5hbWU9InQ6UXVhbnRpdHkiIHhzaTp0eXBlPSJOYW1lZFR5cGVTcGVjaWZpZXIiLz4KICAgICAgICAgICAgICAgPC9hc1R5cGVTcGVjaWZpZXI+CiAgICAgICAgICAgIDwvdGhlbj4KICAgICAgICAgICAgPGVsc2UgbG9jYXRvcj0iMjc6OS0yNzo2MyIgbG93Q2xvc2VkPSJ0cnVlIiBoaWdoQ2xvc2VkPSJ0cnVlIiB4c2k6dHlwZT0iSW50ZXJ2YWwiPgogICAgICAgICAgICAgICA8bG93IGxvY2F0b3I9IjI3OjE4LTI3OjM4IiBuYW1lPSJUb1F1YW50aXR5IiB4c2k6dHlwZT0iRnVuY3Rpb25SZWYiPgogICAgICAgICAgICAgICAgICA8b3BlcmFuZCBsb2NhdG9yPSIyNzoyOS0yNzozNyIgcGF0aD0ibG93IiB4c2k6dHlwZT0iUHJvcGVydHkiPgogICAgICAgICAgICAgICAgICAgICA8c291cmNlIGxvY2F0b3I9IjI3OjI5LTI3OjMzIiBuYW1lPSJyYW5nZSIgeHNpOnR5cGU9Ik9wZXJhbmRSZWYiLz4KICAgICAgICAgICAgICAgICAgPC9vcGVyYW5kPgogICAgICAgICAgICAgICA8L2xvdz4KICAgICAgICAgICAgICAgPGhpZ2ggbG9jYXRvcj0iMjc6NDEtMjc6NjIiIG5hbWU9IlRvUXVhbnRpdHkiIHhzaTp0eXBlPSJGdW5jdGlvblJlZiI+CiAgICAgICAgICAgICAgICAgIDxvcGVyYW5kIGxvY2F0b3I9IjI3OjUyLTI3OjYxIiBwYXRoPSJoaWdoIiB4c2k6dHlwZT0iUHJvcGVydHkiPgogICAgICAgICAgICAgICAgICAgICA8c291cmNlIGxvY2F0b3I9IjI3OjUyLTI3OjU2IiBuYW1lPSJyYW5nZSIgeHNpOnR5cGU9Ik9wZXJhbmRSZWYiLz4KICAgICAgICAgICAgICAgICAgPC9vcGVyYW5kPgogICAgICAgICAgICAgICA8L2hpZ2g+CiAgICAgICAgICAgIDwvZWxzZT4KICAgICAgICAgPC9leHByZXNzaW9uPgogICAgICAgICA8b3BlcmFuZCBuYW1lPSJyYW5nZSI+CiAgICAgICAgICAgIDxvcGVyYW5kVHlwZVNwZWNpZmllciBsb2NhdG9yPSIyMzozNC0yMzo0MyIgbmFtZT0iZmhpcjpSYW5nZSIgeHNpOnR5cGU9Ik5hbWVkVHlwZVNwZWNpZmllciIvPgogICAgICAgICA8L29wZXJhbmQ+CiAgICAgIDwvZGVmPgogICAgICA8ZGVmIGxvY2F0b3I9IjI5OjEtMzg6OSIgbmFtZT0iVG9Db2RlIiBjb250ZXh0PSJQYXRpZW50IiBhY2Nlc3NMZXZlbD0iUHVibGljIiB4c2k6dHlwZT0iRnVuY3Rpb25EZWYiPgogICAgICAgICA8ZXhwcmVzc2lvbiBsb2NhdG9yPSIzMDo1LTM4OjkiIHhzaTp0eXBlPSJJZiI+CiAgICAgICAgICAgIDxjb25kaXRpb24gbG9jYXRvcj0iMzA6OC0zMDoyMSIgeHNpOnR5cGU9IklzTnVsbCI+CiAgICAgICAgICAgICAgIDxvcGVyYW5kIGxvY2F0b3I9IjMwOjgtMzA6MTMiIG5hbWU9ImNvZGluZyIgeHNpOnR5cGU9Ik9wZXJhbmRSZWYiLz4KICAgICAgICAgICAgPC9jb25kaXRpb24+CiAgICAgICAgICAgIDx0aGVuIGFzVHlwZT0idDpDb2RlIiB4c2k6dHlwZT0iQXMiPgogICAgICAgICAgICAgICA8b3BlcmFuZCBsb2NhdG9yPSIzMTo5LTMxOjEyIiB4c2k6dHlwZT0iTnVsbCIvPgogICAgICAgICAgICA8L3RoZW4+CiAgICAgICAgICAgIDxlbHNlIGxvY2F0b3I9IjMzOjktMzg6OSIgY2xhc3NUeXBlPSJ0OkNvZGUiIHhzaTp0eXBlPSJJbnN0YW5jZSI+CiAgICAgICAgICAgICAgIDxlbGVtZW50IG5hbWU9ImNvZGUiPgogICAgICAgICAgICAgICAgICA8dmFsdWUgbG9jYXRvcj0iMzQ6MTctMzQ6MzMiIHBhdGg9InZhbHVlIiB4c2k6dHlwZT0iUHJvcGVydHkiPgogICAgICAgICAgICAgICAgICAgICA8c291cmNlIGxvY2F0b3I9IjM0OjE3LTM0OjI3IiBwYXRoPSJjb2RlIiB4c2k6dHlwZT0iUHJvcGVydHkiPgogICAgICAgICAgICAgICAgICAgICAgICA8c291cmNlIGxvY2F0b3I9IjM0OjE3LTM0OjIyIiBuYW1lPSJjb2RpbmciIHhzaTp0eXBlPSJPcGVyYW5kUmVmIi8+CiAgICAgICAgICAgICAgICAgICAgIDwvc291cmNlPgogICAgICAgICAgICAgICAgICA8L3ZhbHVlPgogICAgICAgICAgICAgICA8L2VsZW1lbnQ+CiAgICAgICAgICAgICAgIDxlbGVtZW50IG5hbWU9InN5c3RlbSI+CiAgICAgICAgICAgICAgICAgIDx2YWx1ZSBsb2NhdG9yPSIzNToxOS0zNTozNyIgcGF0aD0idmFsdWUiIHhzaTp0eXBlPSJQcm9wZXJ0eSI+CiAgICAgICAgICAgICAgICAgICAgIDxzb3VyY2UgbG9jYXRvcj0iMzU6MTktMzU6MzEiIHBhdGg9InN5c3RlbSIgeHNpOnR5cGU9IlByb3BlcnR5Ij4KICAgICAgICAgICAgICAgICAgICAgICAgPHNvdXJjZSBsb2NhdG9yPSIzNToxOS0zNToyNCIgbmFtZT0iY29kaW5nIiB4c2k6dHlwZT0iT3BlcmFuZFJlZiIvPgogICAgICAgICAgICAgICAgICAgICA8L3NvdXJjZT4KICAgICAgICAgICAgICAgICAgPC92YWx1ZT4KICAgICAgICAgICAgICAgPC9lbGVtZW50PgogICAgICAgICAgICAgICA8ZWxlbWVudCBuYW1lPSJ2ZXJzaW9uIj4KICAgICAgICAgICAgICAgICAgPHZhbHVlIGxvY2F0b3I9IjM2OjIwLTM2OjM5IiBwYXRoPSJ2YWx1ZSIgeHNpOnR5cGU9IlByb3BlcnR5Ij4KICAgICAgICAgICAgICAgICAgICAgPHNvdXJjZSBsb2NhdG9yPSIzNjoyMC0zNjozMyIgcGF0aD0idmVyc2lvbiIgeHNpOnR5cGU9IlByb3BlcnR5Ij4KICAgICAgICAgICAgICAgICAgICAgICAgPHNvdXJjZSBsb2NhdG9yPSIzNjoyMC0zNjoyNSIgbmFtZT0iY29kaW5nIiB4c2k6dHlwZT0iT3BlcmFuZFJlZiIvPgogICAgICAgICAgICAgICAgICAgICA8L3NvdXJjZT4KICAgICAgICAgICAgICAgICAgPC92YWx1ZT4KICAgICAgICAgICAgICAgPC9lbGVtZW50PgogICAgICAgICAgICAgICA8ZWxlbWVudCBuYW1lPSJkaXNwbGF5Ij4KICAgICAgICAgICAgICAgICAgPHZhbHVlIGxvY2F0b3I9IjM3OjIwLTM3OjM5IiBwYXRoPSJ2YWx1ZSIgeHNpOnR5cGU9IlByb3BlcnR5Ij4KICAgICAgICAgICAgICAgICAgICAgPHNvdXJjZSBsb2NhdG9yPSIzNzoyMC0zNzozMyIgcGF0aD0iZGlzcGxheSIgeHNpOnR5cGU9IlByb3BlcnR5Ij4KICAgICAgICAgICAgICAgICAgICAgICAgPHNvdXJjZSBsb2NhdG9yPSIzNzoyMC0zNzoyNSIgbmFtZT0iY29kaW5nIiB4c2k6dHlwZT0iT3BlcmFuZFJlZiIvPgogICAgICAgICAgICAgICAgICAgICA8L3NvdXJjZT4KICAgICAgICAgICAgICAgICAgPC92YWx1ZT4KICAgICAgICAgICAgICAgPC9lbGVtZW50PgogICAgICAgICAgICA8L2Vsc2U+CiAgICAgICAgIDwvZXhwcmVzc2lvbj4KICAgICAgICAgPG9wZXJhbmQgbmFtZT0iY29kaW5nIj4KICAgICAgICAgICAgPG9wZXJhbmRUeXBlU3BlY2lmaWVyIGxvY2F0b3I9IjI5OjMxLTI5OjQxIiBuYW1lPSJmaGlyOkNvZGluZyIgeHNpOnR5cGU9Ik5hbWVkVHlwZVNwZWNpZmllciIvPgogICAgICAgICA8L29wZXJhbmQ+CiAgICAgIDwvZGVmPgogICAgICA8ZGVmIGxvY2F0b3I9IjQwOjEtNDc6OSIgbmFtZT0iVG9Db25jZXB0IiBjb250ZXh0PSJQYXRpZW50IiBhY2Nlc3NMZXZlbD0iUHVibGljIiB4c2k6dHlwZT0iRnVuY3Rpb25EZWYiPgogICAgICAgICA8ZXhwcmVzc2lvbiBsb2NhdG9yPSI0MTo1LTQ3OjkiIHhzaTp0eXBlPSJJZiI+CiAgICAgICAgICAgIDxjb25kaXRpb24gbG9jYXRvcj0iNDE6OC00MToyMiIgeHNpOnR5cGU9IklzTnVsbCI+CiAgICAgICAgICAgICAgIDxvcGVyYW5kIGxvY2F0b3I9IjQxOjgtNDE6MTQiIG5hbWU9ImNvbmNlcHQiIHhzaTp0eXBlPSJPcGVyYW5kUmVmIi8+CiAgICAgICAgICAgIDwvY29uZGl0aW9uPgogICAgICAgICAgICA8dGhlbiBhc1R5cGU9InQ6Q29uY2VwdCIgeHNpOnR5cGU9IkFzIj4KICAgICAgICAgICAgICAgPG9wZXJhbmQgbG9jYXRvcj0iNDI6OS00MjoxMiIgeHNpOnR5cGU9Ik51bGwiLz4KICAgICAgICAgICAgPC90aGVuPgogICAgICAgICAgICA8ZWxzZSBsb2NhdG9yPSI0NDo5LTQ3OjkiIGNsYXNzVHlwZT0idDpDb25jZXB0IiB4c2k6dHlwZT0iSW5zdGFuY2UiPgogICAgICAgICAgICAgICA8ZWxlbWVudCBuYW1lPSJjb2RlcyI+CiAgICAgICAgICAgICAgICAgIDx2YWx1ZSBsb2NhdG9yPSI0NToyMC00NTo1MiIgeHNpOnR5cGU9IlF1ZXJ5Ij4KICAgICAgICAgICAgICAgICAgICAgPHNvdXJjZSBsb2NhdG9yPSI0NToyMC00NTozNSIgYWxpYXM9IkMiPgogICAgICAgICAgICAgICAgICAgICAgICA8ZXhwcmVzc2lvbiBsb2NhdG9yPSI0NToyMC00NTozMyIgcGF0aD0iY29kaW5nIiB4c2k6dHlwZT0iUHJvcGVydHkiPgogICAgICAgICAgICAgICAgICAgICAgICAgICA8c291cmNlIG5hbWU9ImNvbmNlcHQiIHhzaTp0eXBlPSJPcGVyYW5kUmVmIi8+CiAgICAgICAgICAgICAgICAgICAgICAgIDwvZXhwcmVzc2lvbj4KICAgICAgICAgICAgICAgICAgICAgPC9zb3VyY2U+CiAgICAgICAgICAgICAgICAgICAgIDxyZXR1cm4gbG9jYXRvcj0iNDU6MzctNDU6NTIiPgogICAgICAgICAgICAgICAgICAgICAgICA8ZXhwcmVzc2lvbiBsb2NhdG9yPSI0NTo0NC00NTo1MiIgbmFtZT0iVG9Db2RlIiB4c2k6dHlwZT0iRnVuY3Rpb25SZWYiPgogICAgICAgICAgICAgICAgICAgICAgICAgICA8b3BlcmFuZCBsb2NhdG9yPSI0NTo1MSIgbmFtZT0iQyIgeHNpOnR5cGU9IkFsaWFzUmVmIi8+CiAgICAgICAgICAgICAgICAgICAgICAgIDwvZXhwcmVzc2lvbj4KICAgICAgICAgICAgICAgICAgICAgPC9yZXR1cm4+CiAgICAgICAgICAgICAgICAgIDwvdmFsdWU+CiAgICAgICAgICAgICAgIDwvZWxlbWVudD4KICAgICAgICAgICAgICAgPGVsZW1lbnQgbmFtZT0iZGlzcGxheSI+CiAgICAgICAgICAgICAgICAgIDx2YWx1ZSBsb2NhdG9yPSI0NjoyMi00NjozOSIgcGF0aD0idmFsdWUiIHhzaTp0eXBlPSJQcm9wZXJ0eSI+CiAgICAgICAgICAgICAgICAgICAgIDxzb3VyY2UgbG9jYXRvcj0iNDY6MjItNDY6MzMiIHBhdGg9InRleHQiIHhzaTp0eXBlPSJQcm9wZXJ0eSI+CiAgICAgICAgICAgICAgICAgICAgICAgIDxzb3VyY2UgbG9jYXRvcj0iNDY6MjItNDY6MjgiIG5hbWU9ImNvbmNlcHQiIHhzaTp0eXBlPSJPcGVyYW5kUmVmIi8+CiAgICAgICAgICAgICAgICAgICAgIDwvc291cmNlPgogICAgICAgICAgICAgICAgICA8L3ZhbHVlPgogICAgICAgICAgICAgICA8L2VsZW1lbnQ+CiAgICAgICAgICAgIDwvZWxzZT4KICAgICAgICAgPC9leHByZXNzaW9uPgogICAgICAgICA8b3BlcmFuZCBuYW1lPSJjb25jZXB0Ij4KICAgICAgICAgICAgPG9wZXJhbmRUeXBlU3BlY2lmaWVyIGxvY2F0b3I9IjQwOjM1LTQwOjU0IiBuYW1lPSJmaGlyOkNvZGVhYmxlQ29uY2VwdCIgeHNpOnR5cGU9Ik5hbWVkVHlwZVNwZWNpZmllciIvPgogICAgICAgICA8L29wZXJhbmQ+CiAgICAgIDwvZGVmPgogICAgICA8ZGVmIGxvY2F0b3I9IjUwOjEtNTA6NTgiIG5hbWU9IlRvU3RyaW5nIiBjb250ZXh0PSJQYXRpZW50IiBhY2Nlc3NMZXZlbD0iUHVibGljIiB4c2k6dHlwZT0iRnVuY3Rpb25EZWYiPgogICAgICAgICA8ZXhwcmVzc2lvbiBsb2NhdG9yPSI1MDo0OC01MDo1OCIgcGF0aD0idmFsdWUiIHhzaTp0eXBlPSJQcm9wZXJ0eSI+CiAgICAgICAgICAgIDxzb3VyY2UgbG9jYXRvcj0iNTA6NDgtNTA6NTIiIG5hbWU9InZhbHVlIiB4c2k6dHlwZT0iT3BlcmFuZFJlZiIvPgogICAgICAgICA8L2V4cHJlc3Npb24+CiAgICAgICAgIDxvcGVyYW5kIG5hbWU9InZhbHVlIj4KICAgICAgICAgICAgPG9wZXJhbmRUeXBlU3BlY2lmaWVyIGxvY2F0b3I9IjUwOjMyLTUwOjQ0IiBuYW1lPSJmaGlyOkFjY291bnRTdGF0dXMiIHhzaTp0eXBlPSJOYW1lZFR5cGVTcGVjaWZpZXIiLz4KICAgICAgICAgPC9vcGVyYW5kPgogICAgICA8L2RlZj4KICAgICAgPGRlZiBsb2NhdG9yPSI1MToxLTUxOjcwIiBuYW1lPSJUb1N0cmluZyIgY29udGV4dD0iUGF0aWVudCIgYWNjZXNzTGV2ZWw9IlB1YmxpYyIgeHNpOnR5cGU9IkZ1bmN0aW9uRGVmIj4KICAgICAgICAgPGV4cHJlc3Npb24gbG9jYXRvcj0iNTE6NjAtNTE6NzAiIHBhdGg9InZhbHVlIiB4c2k6dHlwZT0iUHJvcGVydHkiPgogICAgICAgICAgICA8c291cmNlIGxvY2F0b3I9IjUxOjYwLTUxOjY0IiBuYW1lPSJ2YWx1ZSIgeHNpOnR5cGU9Ik9wZXJhbmRSZWYiLz4KICAgICAgICAgPC9leHByZXNzaW9uPgogICAgICAgICA8b3BlcmFuZCBuYW1lPSJ2YWx1ZSI+CiAgICAgICAgICAgIDxvcGVyYW5kVHlwZVNwZWNpZmllciBsb2NhdG9yPSI1MTozMi01MTo1NiIgbmFtZT0iZmhpcjpBY3Rpb25DYXJkaW5hbGl0eUJlaGF2aW9yIiB4c2k6dHlwZT0iTmFtZWRUeXBlU3BlY2lmaWVyIi8+CiAgICAgICAgIDwvb3BlcmFuZD4KICAgICAgPC9kZWY+CiAgICAgIDxkZWYgbG9jYXRvcj0iNTI6MS01Mjo2NCIgbmFtZT0iVG9TdHJpbmciIGNvbnRleHQ9IlBhdGllbnQiIGFjY2Vzc0xldmVsPSJQdWJsaWMiIHhzaTp0eXBlPSJGdW5jdGlvbkRlZiI+CiAgICAgICAgIDxleHByZXNzaW9uIGxvY2F0b3I9IjUyOjU0LTUyOjY0IiBwYXRoPSJ2YWx1ZSIgeHNpOnR5cGU9IlByb3BlcnR5Ij4KICAgICAgICAgICAgPHNvdXJjZSBsb2NhdG9yPSI1Mjo1NC01Mjo1OCIgbmFtZT0idmFsdWUiIHhzaTp0eXBlPSJPcGVyYW5kUmVmIi8+CiAgICAgICAgIDwvZXhwcmVzc2lvbj4KICAgICAgICAgPG9wZXJhbmQgbmFtZT0idmFsdWUiPgogICAgICAgICAgICA8b3BlcmFuZFR5cGVTcGVjaWZpZXIgbG9jYXRvcj0iNTI6MzItNTI6NTAiIG5hbWU9ImZoaXI6QWN0aW9uQ29uZGl0aW9uS2luZCIgeHNpOnR5cGU9Ik5hbWVkVHlwZVNwZWNpZmllciIvPgogICAgICAgICA8L29wZXJhbmQ+CiAgICAgIDwvZGVmPgogICAgICA8ZGVmIGxvY2F0b3I9IjUzOjEtNTM6NjciIG5hbWU9IlRvU3RyaW5nIiBjb250ZXh0PSJQYXRpZW50IiBhY2Nlc3NMZXZlbD0iUHVibGljIiB4c2k6dHlwZT0iRnVuY3Rpb25EZWYiPgogICAgICAgICA8ZXhwcmVzc2lvbiBsb2NhdG9yPSI1Mzo1Ny01Mzo2NyIgcGF0aD0idmFsdWUiIHhzaTp0eXBlPSJQcm9wZXJ0eSI+CiAgICAgICAgICAgIDxzb3VyY2UgbG9jYXRvcj0iNTM6NTctNTM6NjEiIG5hbWU9InZhbHVlIiB4c2k6dHlwZT0iT3BlcmFuZFJlZiIvPgogICAgICAgICA8L2V4cHJlc3Npb24+CiAgICAgICAgIDxvcGVyYW5kIG5hbWU9InZhbHVlIj4KICAgICAgICAgICAgPG9wZXJhbmRUeXBlU3BlY2lmaWVyIGxvY2F0b3I9IjUzOjMyLTUzOjUzIiBuYW1lPSJmaGlyOkFjdGlvbkdyb3VwaW5nQmVoYXZpb3IiIHhzaTp0eXBlPSJOYW1lZFR5cGVTcGVjaWZpZXIiLz4KICAgICAgICAgPC9vcGVyYW5kPgogICAgICA8L2RlZj4KICAgICAgPGRlZiBsb2NhdG9yPSI1NDoxLTU0OjY2IiBuYW1lPSJUb1N0cmluZyIgY29udGV4dD0iUGF0aWVudCIgYWNjZXNzTGV2ZWw9IlB1YmxpYyIgeHNpOnR5cGU9IkZ1bmN0aW9uRGVmIj4KICAgICAgICAgPGV4cHJlc3Npb24gbG9jYXRvcj0iNTQ6NTYtNTQ6NjYiIHBhdGg9InZhbHVlIiB4c2k6dHlwZT0iUHJvcGVydHkiPgogICAgICAgICAgICA8c291cmNlIGxvY2F0b3I9IjU0OjU2LTU0OjYwIiBuYW1lPSJ2YWx1ZSIgeHNpOnR5cGU9Ik9wZXJhbmRSZWYiLz4KICAgICAgICAgPC9leHByZXNzaW9uPgogICAgICAgICA8b3BlcmFuZCBuYW1lPSJ2YWx1ZSI+CiAgICAgICAgICAgIDxvcGVyYW5kVHlwZVNwZWNpZmllciBsb2NhdG9yPSI1NDozMi01NDo1MiIgbmFtZT0iZmhpcjpBY3Rpb25QYXJ0aWNpcGFudFR5cGUiIHhzaTp0eXBlPSJOYW1lZFR5cGVTcGVjaWZpZXIiLz4KICAgICAgICAgPC9vcGVyYW5kPgogICAgICA8L2RlZj4KICAgICAgPGRlZiBsb2NhdG9yPSI1NToxLTU1OjY3IiBuYW1lPSJUb1N0cmluZyIgY29udGV4dD0iUGF0aWVudCIgYWNjZXNzTGV2ZWw9IlB1YmxpYyIgeHNpOnR5cGU9IkZ1bmN0aW9uRGVmIj4KICAgICAgICAgPGV4cHJlc3Npb24gbG9jYXRvcj0iNTU6NTctNTU6NjciIHBhdGg9InZhbHVlIiB4c2k6dHlwZT0iUHJvcGVydHkiPgogICAgICAgICAgICA8c291cmNlIGxvY2F0b3I9IjU1OjU3LTU1OjYxIiBuYW1lPSJ2YWx1ZSIgeHNpOnR5cGU9Ik9wZXJhbmRSZWYiLz4KICAgICAgICAgPC9leHByZXNzaW9uPgogICAgICAgICA8b3BlcmFuZCBuYW1lPSJ2YWx1ZSI+CiAgICAgICAgICAgIDxvcGVyYW5kVHlwZVNwZWNpZmllciBsb2NhdG9yPSI1NTozMi01NTo1MyIgbmFtZT0iZmhpcjpBY3Rpb25QcmVjaGVja0JlaGF2aW9yIiB4c2k6dHlwZT0iTmFtZWRUeXBlU3BlY2lmaWVyIi8+CiAgICAgICAgIDwvb3BlcmFuZD4KICAgICAgPC9kZWY+CiAgICAgIDxkZWYgbG9jYXRvcj0iNTY6MS01Njo2NyIgbmFtZT0iVG9TdHJpbmciIGNvbnRleHQ9IlBhdGllbnQiIGFjY2Vzc0xldmVsPSJQdWJsaWMiIHhzaTp0eXBlPSJGdW5jdGlvbkRlZiI+CiAgICAgICAgIDxleHByZXNzaW9uIGxvY2F0b3I9IjU2OjU3LTU2OjY3IiBwYXRoPSJ2YWx1ZSIgeHNpOnR5cGU9IlByb3BlcnR5Ij4KICAgICAgICAgICAgPHNvdXJjZSBsb2NhdG9yPSI1Njo1Ny01Njo2MSIgbmFtZT0idmFsdWUiIHhzaTp0eXBlPSJPcGVyYW5kUmVmIi8+CiAgICAgICAgIDwvZXhwcmVzc2lvbj4KICAgICAgICAgPG9wZXJhbmQgbmFtZT0idmFsdWUiPgogICAgICAgICAgICA8b3BlcmFuZFR5cGVTcGVjaWZpZXIgbG9jYXRvcj0iNTY6MzItNTY6NTMiIG5hbWU9ImZoaXI6QWN0aW9uUmVsYXRpb25zaGlwVHlwZSIgeHNpOnR5cGU9Ik5hbWVkVHlwZVNwZWNpZmllciIvPgogICAgICAgICA8L29wZXJhbmQ+CiAgICAgIDwvZGVmPgogICAgICA8ZGVmIGxvY2F0b3I9IjU3OjEtNTc6NjciIG5hbWU9IlRvU3RyaW5nIiBjb250ZXh0PSJQYXRpZW50IiBhY2Nlc3NMZXZlbD0iUHVibGljIiB4c2k6dHlwZT0iRnVuY3Rpb25EZWYiPgogICAgICAgICA8ZXhwcmVzc2lvbiBsb2NhdG9yPSI1Nzo1Ny01Nzo2NyIgcGF0aD0idmFsdWUiIHhzaTp0eXBlPSJQcm9wZXJ0eSI+CiAgICAgICAgICAgIDxzb3VyY2UgbG9jYXRvcj0iNTc6NTctNTc6NjEiIG5hbWU9InZhbHVlIiB4c2k6dHlwZT0iT3BlcmFuZFJlZiIvPgogICAgICAgICA8L2V4cHJlc3Npb24+CiAgICAgICAgIDxvcGVyYW5kIG5hbWU9InZhbHVlIj4KICAgICAgICAgICAgPG9wZXJhbmRUeXBlU3BlY2lmaWVyIGxvY2F0b3I9IjU3OjMyLTU3OjUzIiBuYW1lPSJmaGlyOkFjdGlvblJlcXVpcmVkQmVoYXZpb3IiIHhzaTp0eXBlPSJOYW1lZFR5cGVTcGVjaWZpZXIiLz4KICAgICAgICAgPC9vcGVyYW5kPgogICAgICA8L2RlZj4KICAgICAgPGRlZiBsb2NhdG9yPSI1ODoxLTU4OjY4IiBuYW1lPSJUb1N0cmluZyIgY29udGV4dD0iUGF0aWVudCIgYWNjZXNzTGV2ZWw9IlB1YmxpYyIgeHNpOnR5cGU9IkZ1bmN0aW9uRGVmIj4KICAgICAgICAgPGV4cHJlc3Npb24gbG9jYXRvcj0iNTg6NTgtNTg6NjgiIHBhdGg9InZhbHVlIiB4c2k6dHlwZT0iUHJvcGVydHkiPgogICAgICAgICAgICA8c291cmNlIGxvY2F0b3I9IjU4OjU4LTU4OjYyIiBuYW1lPSJ2YWx1ZSIgeHNpOnR5cGU9Ik9wZXJhbmRSZWYiLz4KICAgICAgICAgPC9leHByZXNzaW9uPgogICAgICAgICA8b3BlcmFuZCBuYW1lPSJ2YWx1ZSI+CiAgICAgICAgICAgIDxvcGVyYW5kVHlwZVNwZWNpZmllciBsb2NhdG9yPSI1ODozMi01ODo1NCIgbmFtZT0iZmhpcjpBY3Rpb25TZWxlY3Rpb25CZWhhdmlvciIgeHNpOnR5cGU9Ik5hbWVkVHlwZVNwZWNpZmllciIvPgogICAgICAgICA8L29wZXJhbmQ+CiAgICAgIDwvZGVmPgogICAgICA8ZGVmIGxvY2F0b3I9IjU5OjEtNTk6NjciIG5hbWU9IlRvU3RyaW5nIiBjb250ZXh0PSJQYXRpZW50IiBhY2Nlc3NMZXZlbD0iUHVibGljIiB4c2k6dHlwZT0iRnVuY3Rpb25EZWYiPgogICAgICAgICA8ZXhwcmVzc2lvbiBsb2NhdG9yPSI1OTo1Ny01OTo2NyIgcGF0aD0idmFsdWUiIHhzaTp0eXBlPSJQcm9wZXJ0eSI+CiAgICAgICAgICAgIDxzb3VyY2UgbG9jYXRvcj0iNTk6NTctNTk6NjEiIG5hbWU9InZhbHVlIiB4c2k6dHlwZT0iT3BlcmFuZFJlZiIvPgogICAgICAgICA8L2V4cHJlc3Npb24+CiAgICAgICAgIDxvcGVyYW5kIG5hbWU9InZhbHVlIj4KICAgICAgICAgICAgPG9wZXJhbmRUeXBlU3BlY2lmaWVyIGxvY2F0b3I9IjU5OjMyLTU5OjUzIiBuYW1lPSJmaGlyOkFjdGl2aXR5RGVmaW5pdGlvbktpbmQiIHhzaTp0eXBlPSJOYW1lZFR5cGVTcGVjaWZpZXIiLz4KICAgICAgICAgPC9vcGVyYW5kPgogICAgICA8L2RlZj4KICAgICAgPGRlZiBsb2NhdG9yPSI2MDoxLTYwOjY4IiBuYW1lPSJUb1N0cmluZyIgY29udGV4dD0iUGF0aWVudCIgYWNjZXNzTGV2ZWw9IlB1YmxpYyIgeHNpOnR5cGU9IkZ1bmN0aW9uRGVmIj4KICAgICAgICAgPGV4cHJlc3Npb24gbG9jYXRvcj0iNjA6NTgtNjA6NjgiIHBhdGg9InZhbHVlIiB4c2k6dHlwZT0iUHJvcGVydHkiPgogICAgICAgICAgICA8c291cmNlIGxvY2F0b3I9IjYwOjU4LTYwOjYyIiBuYW1lPSJ2YWx1ZSIgeHNpOnR5cGU9Ik9wZXJhbmRSZWYiLz4KICAgICAgICAgPC9leHByZXNzaW9uPgogICAgICAgICA8b3BlcmFuZCBuYW1lPSJ2YWx1ZSI+CiAgICAgICAgICAgIDxvcGVyYW5kVHlwZVNwZWNpZmllciBsb2NhdG9yPSI2MDozMi02MDo1NCIgbmFtZT0iZmhpcjpBY3Rpdml0eVBhcnRpY2lwYW50VHlwZSIgeHNpOnR5cGU9Ik5hbWVkVHlwZVNwZWNpZmllciIvPgogICAgICAgICA8L29wZXJhbmQ+CiAgICAgIDwvZGVmPgogICAgICA8ZGVmIGxvY2F0b3I9IjYxOjEtNjE6NTYiIG5hbWU9IlRvU3RyaW5nIiBjb250ZXh0PSJQYXRpZW50IiBhY2Nlc3NMZXZlbD0iUHVibGljIiB4c2k6dHlwZT0iRnVuY3Rpb25EZWYiPgogICAgICAgICA8ZXhwcmVzc2lvbiBsb2NhdG9yPSI2MTo0Ni02MTo1NiIgcGF0aD0idmFsdWUiIHhzaTp0eXBlPSJQcm9wZXJ0eSI+CiAgICAgICAgICAgIDxzb3VyY2UgbG9jYXRvcj0iNjE6NDYtNjE6NTAiIG5hbWU9InZhbHVlIiB4c2k6dHlwZT0iT3BlcmFuZFJlZiIvPgogICAgICAgICA8L2V4cHJlc3Npb24+CiAgICAgICAgIDxvcGVyYW5kIG5hbWU9InZhbHVlIj4KICAgICAgICAgICAgPG9wZXJhbmRUeXBlU3BlY2lmaWVyIGxvY2F0b3I9IjYxOjMyLTYxOjQyIiBuYW1lPSJmaGlyOkFkZHJlc3NUeXBlIiB4c2k6dHlwZT0iTmFtZWRUeXBlU3BlY2lmaWVyIi8+CiAgICAgICAgIDwvb3BlcmFuZD4KICAgICAgPC9kZWY+CiAgICAgIDxkZWYgbG9jYXRvcj0iNjI6MS02Mjo1NSIgbmFtZT0iVG9TdHJpbmciIGNvbnRleHQ9IlBhdGllbnQiIGFjY2Vzc0xldmVsPSJQdWJsaWMiIHhzaTp0eXBlPSJGdW5jdGlvbkRlZiI+CiAgICAgICAgIDxleHByZXNzaW9uIGxvY2F0b3I9IjYyOjQ1LTYyOjU1IiBwYXRoPSJ2YWx1ZSIgeHNpOnR5cGU9IlByb3BlcnR5Ij4KICAgICAgICAgICAgPHNvdXJjZSBsb2NhdG9yPSI2Mjo0NS02Mjo0OSIgbmFtZT0idmFsdWUiIHhzaTp0eXBlPSJPcGVyYW5kUmVmIi8+CiAgICAgICAgIDwvZXhwcmVzc2lvbj4KICAgICAgICAgPG9wZXJhbmQgbmFtZT0idmFsdWUiPgogICAgICAgICAgICA8b3BlcmFuZFR5cGVTcGVjaWZpZXIgbG9jYXRvcj0iNjI6MzItNjI6NDEiIG5hbWU9ImZoaXI6QWRkcmVzc1VzZSIgeHNpOnR5cGU9Ik5hbWVkVHlwZVNwZWNpZmllciIvPgogICAgICAgICA8L29wZXJhbmQ+CiAgICAgIDwvZGVmPgogICAgICA8ZGVmIGxvY2F0b3I9IjYzOjEtNjM6NjUiIG5hbWU9IlRvU3RyaW5nIiBjb250ZXh0PSJQYXRpZW50IiBhY2Nlc3NMZXZlbD0iUHVibGljIiB4c2k6dHlwZT0iRnVuY3Rpb25EZWYiPgogICAgICAgICA8ZXhwcmVzc2lvbiBsb2NhdG9yPSI2Mzo1NS02Mzo2NSIgcGF0aD0idmFsdWUiIHhzaTp0eXBlPSJQcm9wZXJ0eSI+CiAgICAgICAgICAgIDxzb3VyY2UgbG9jYXRvcj0iNjM6NTUtNjM6NTkiIG5hbWU9InZhbHVlIiB4c2k6dHlwZT0iT3BlcmFuZFJlZiIvPgogICAgICAgICA8L2V4cHJlc3Npb24+CiAgICAgICAgIDxvcGVyYW5kIG5hbWU9InZhbHVlIj4KICAgICAgICAgICAgPG9wZXJhbmRUeXBlU3BlY2lmaWVyIGxvY2F0b3I9IjYzOjMyLTYzOjUxIiBuYW1lPSJmaGlyOkFkbWluaXN0cmF0aXZlR2VuZGVyIiB4c2k6dHlwZT0iTmFtZWRUeXBlU3BlY2lmaWVyIi8+CiAgICAgICAgIDwvb3BlcmFuZD4KICAgICAgPC9kZWY+CiAgICAgIDxkZWYgbG9jYXRvcj0iNjQ6MS02NDo2NiIgbmFtZT0iVG9TdHJpbmciIGNvbnRleHQ9IlBhdGllbnQiIGFjY2Vzc0xldmVsPSJQdWJsaWMiIHhzaTp0eXBlPSJGdW5jdGlvbkRlZiI+CiAgICAgICAgIDxleHByZXNzaW9uIGxvY2F0b3I9IjY0OjU2LTY0OjY2IiBwYXRoPSJ2YWx1ZSIgeHNpOnR5cGU9IlByb3BlcnR5Ij4KICAgICAgICAgICAgPHNvdXJjZSBsb2NhdG9yPSI2NDo1Ni02NDo2MCIgbmFtZT0idmFsdWUiIHhzaTp0eXBlPSJPcGVyYW5kUmVmIi8+CiAgICAgICAgIDwvZXhwcmVzc2lvbj4KICAgICAgICAgPG9wZXJhbmQgbmFtZT0idmFsdWUiPgogICAgICAgICAgICA8b3BlcmFuZFR5cGVTcGVjaWZpZXIgbG9jYXRvcj0iNjQ6MzItNjQ6NTIiIG5hbWU9ImZoaXI6QWR2ZXJzZUV2ZW50QWN0dWFsaXR5IiB4c2k6dHlwZT0iTmFtZWRUeXBlU3BlY2lmaWVyIi8+CiAgICAgICAgIDwvb3BlcmFuZD4KICAgICAgPC9kZWY+CiAgICAgIDxkZWYgbG9jYXRvcj0iNjU6MS02NTo2MCIgbmFtZT0iVG9TdHJpbmciIGNvbnRleHQ9IlBhdGllbnQiIGFjY2Vzc0xldmVsPSJQdWJsaWMiIHhzaTp0eXBlPSJGdW5jdGlvbkRlZiI+CiAgICAgICAgIDxleHByZXNzaW9uIGxvY2F0b3I9IjY1OjUwLTY1OjYwIiBwYXRoPSJ2YWx1ZSIgeHNpOnR5cGU9IlByb3BlcnR5Ij4KICAgICAgICAgICAgPHNvdXJjZSBsb2NhdG9yPSI2NTo1MC02NTo1NCIgbmFtZT0idmFsdWUiIHhzaTp0eXBlPSJPcGVyYW5kUmVmIi8+CiAgICAgICAgIDwvZXhwcmVzc2lvbj4KICAgICAgICAgPG9wZXJhbmQgbmFtZT0idmFsdWUiPgogICAgICAgICAgICA8b3BlcmFuZFR5cGVTcGVjaWZpZXIgbG9jYXRvcj0iNjU6MzItNjU6NDYiIG5hbWU9ImZoaXI6QWdncmVnYXRpb25Nb2RlIiB4c2k6dHlwZT0iTmFtZWRUeXBlU3BlY2lmaWVyIi8+CiAgICAgICAgIDwvb3BlcmFuZD4KICAgICAgPC9kZWY+CiAgICAgIDxkZWYgbG9jYXRvcj0iNjY6MS02Njo3MSIgbmFtZT0iVG9TdHJpbmciIGNvbnRleHQ9IlBhdGllbnQiIGFjY2Vzc0xldmVsPSJQdWJsaWMiIHhzaTp0eXBlPSJGdW5jdGlvbkRlZiI+CiAgICAgICAgIDxleHByZXNzaW9uIGxvY2F0b3I9IjY2OjYxLTY2OjcxIiBwYXRoPSJ2YWx1ZSIgeHNpOnR5cGU9IlByb3BlcnR5Ij4KICAgICAgICAgICAgPHNvdXJjZSBsb2NhdG9yPSI2Njo2MS02Njo2NSIgbmFtZT0idmFsdWUiIHhzaTp0eXBlPSJPcGVyYW5kUmVmIi8+CiAgICAgICAgIDwvZXhwcmVzc2lvbj4KICAgICAgICAgPG9wZXJhbmQgbmFtZT0idmFsdWUiPgogICAgICAgICAgICA8b3BlcmFuZFR5cGVTcGVjaWZpZXIgbG9jYXRvcj0iNjY6MzItNjY6NTciIG5hbWU9ImZoaXI6QWxsZXJneUludG9sZXJhbmNlQ2F0ZWdvcnkiIHhzaTp0eXBlPSJOYW1lZFR5cGVTcGVjaWZpZXIiLz4KICAgICAgICAgPC9vcGVyYW5kPgogICAgICA8L2RlZj4KICAgICAgPGRlZiBsb2NhdG9yPSI2NzoxLTY3Ojc0IiBuYW1lPSJUb1N0cmluZyIgY29udGV4dD0iUGF0aWVudCIgYWNjZXNzTGV2ZWw9IlB1YmxpYyIgeHNpOnR5cGU9IkZ1bmN0aW9uRGVmIj4KICAgICAgICAgPGV4cHJlc3Npb24gbG9jYXRvcj0iNjc6NjQtNjc6NzQiIHBhdGg9InZhbHVlIiB4c2k6dHlwZT0iUHJvcGVydHkiPgogICAgICAgICAgICA8c291cmNlIGxvY2F0b3I9IjY3OjY0LTY3OjY4IiBuYW1lPSJ2YWx1ZSIgeHNpOnR5cGU9Ik9wZXJhbmRSZWYiLz4KICAgICAgICAgPC9leHByZXNzaW9uPgogICAgICAgICA8b3BlcmFuZCBuYW1lPSJ2YWx1ZSI+CiAgICAgICAgICAgIDxvcGVyYW5kVHlwZVNwZWNpZmllciBsb2NhdG9yPSI2NzozMi02Nzo2MCIgbmFtZT0iZmhpcjpBbGxlcmd5SW50b2xlcmFuY2VDcml0aWNhbGl0eSIgeHNpOnR5cGU9Ik5hbWVkVHlwZVNwZWNpZmllciIvPgogICAgICAgICA8L29wZXJhbmQ+CiAgICAgIDwvZGVmPgogICAgICA8ZGVmIGxvY2F0b3I9IjY4OjEtNjg6NzEiIG5hbWU9IlRvU3RyaW5nIiBjb250ZXh0PSJQYXRpZW50IiBhY2Nlc3NMZXZlbD0iUHVibGljIiB4c2k6dHlwZT0iRnVuY3Rpb25EZWYiPgogICAgICAgICA8ZXhwcmVzc2lvbiBsb2NhdG9yPSI2ODo2MS02ODo3MSIgcGF0aD0idmFsdWUiIHhzaTp0eXBlPSJQcm9wZXJ0eSI+CiAgICAgICAgICAgIDxzb3VyY2UgbG9jYXRvcj0iNjg6NjEtNjg6NjUiIG5hbWU9InZhbHVlIiB4c2k6dHlwZT0iT3BlcmFuZFJlZiIvPgogICAgICAgICA8L2V4cHJlc3Npb24+CiAgICAgICAgIDxvcGVyYW5kIG5hbWU9InZhbHVlIj4KICAgICAgICAgICAgPG9wZXJhbmRUeXBlU3BlY2lmaWVyIGxvY2F0b3I9IjY4OjMyLTY4OjU3IiBuYW1lPSJmaGlyOkFsbGVyZ3lJbnRvbGVyYW5jZVNldmVyaXR5IiB4c2k6dHlwZT0iTmFtZWRUeXBlU3BlY2lmaWVyIi8+CiAgICAgICAgIDwvb3BlcmFuZD4KICAgICAgPC9kZWY+CiAgICAgIDxkZWYgbG9jYXRvcj0iNjk6MS02OTo2NyIgbmFtZT0iVG9TdHJpbmciIGNvbnRleHQ9IlBhdGllbnQiIGFjY2Vzc0xldmVsPSJQdWJsaWMiIHhzaTp0eXBlPSJGdW5jdGlvbkRlZiI+CiAgICAgICAgIDxleHByZXNzaW9uIGxvY2F0b3I9IjY5OjU3LTY5OjY3IiBwYXRoPSJ2YWx1ZSIgeHNpOnR5cGU9IlByb3BlcnR5Ij4KICAgICAgICAgICAgPHNvdXJjZSBsb2NhdG9yPSI2OTo1Ny02OTo2MSIgbmFtZT0idmFsdWUiIHhzaTp0eXBlPSJPcGVyYW5kUmVmIi8+CiAgICAgICAgIDwvZXhwcmVzc2lvbj4KICAgICAgICAgPG9wZXJhbmQgbmFtZT0idmFsdWUiPgogICAgICAgICAgICA8b3BlcmFuZFR5cGVTcGVjaWZpZXIgbG9jYXRvcj0iNjk6MzItNjk6NTMiIG5hbWU9ImZoaXI6QWxsZXJneUludG9sZXJhbmNlVHlwZSIgeHNpOnR5cGU9Ik5hbWVkVHlwZVNwZWNpZmllciIvPgogICAgICAgICA8L29wZXJhbmQ+CiAgICAgIDwvZGVmPgogICAgICA8ZGVmIGxvY2F0b3I9IjcwOjEtNzA6NjIiIG5hbWU9IlRvU3RyaW5nIiBjb250ZXh0PSJQYXRpZW50IiBhY2Nlc3NMZXZlbD0iUHVibGljIiB4c2k6dHlwZT0iRnVuY3Rpb25EZWYiPgogICAgICAgICA8ZXhwcmVzc2lvbiBsb2NhdG9yPSI3MDo1Mi03MDo2MiIgcGF0aD0idmFsdWUiIHhzaTp0eXBlPSJQcm9wZXJ0eSI+CiAgICAgICAgICAgIDxzb3VyY2UgbG9jYXRvcj0iNzA6NTItNzA6NTYiIG5hbWU9InZhbHVlIiB4c2k6dHlwZT0iT3BlcmFuZFJlZiIvPgogICAgICAgICA8L2V4cHJlc3Npb24+CiAgICAgICAgIDxvcGVyYW5kIG5hbWU9InZhbHVlIj4KICAgICAgICAgICAgPG9wZXJhbmRUeXBlU3BlY2lmaWVyIGxvY2F0b3I9IjcwOjMyLTcwOjQ4IiBuYW1lPSJmaGlyOkFwcG9pbnRtZW50U3RhdHVzIiB4c2k6dHlwZT0iTmFtZWRUeXBlU3BlY2lmaWVyIi8+CiAgICAgICAgIDwvb3BlcmFuZD4KICAgICAgPC9kZWY+CiAgICAgIDxkZWYgbG9jYXRvcj0iNzE6MS03MTo2NyIgbmFtZT0iVG9TdHJpbmciIGNvbnRleHQ9IlBhdGllbnQiIGFjY2Vzc0xldmVsPSJQdWJsaWMiIHhzaTp0eXBlPSJGdW5jdGlvbkRlZiI+CiAgICAgICAgIDxleHByZXNzaW9uIGxvY2F0b3I9IjcxOjU3LTcxOjY3IiBwYXRoPSJ2YWx1ZSIgeHNpOnR5cGU9IlByb3BlcnR5Ij4KICAgICAgICAgICAgPHNvdXJjZSBsb2NhdG9yPSI3MTo1Ny03MTo2MSIgbmFtZT0idmFsdWUiIHhzaTp0eXBlPSJPcGVyYW5kUmVmIi8+CiAgICAgICAgIDwvZXhwcmVzc2lvbj4KICAgICAgICAgPG9wZXJhbmQgbmFtZT0idmFsdWUiPgogICAgICAgICAgICA8b3BlcmFuZFR5cGVTcGVjaWZpZXIgbG9jYXRvcj0iNzE6MzItNzE6NTMiIG5hbWU9ImZoaXI6QXNzZXJ0aW9uRGlyZWN0aW9uVHlwZSIgeHNpOnR5cGU9Ik5hbWVkVHlwZVNwZWNpZmllciIvPgogICAgICAgICA8L29wZXJhbmQ+CiAgICAgIDwvZGVmPgogICAgICA8ZGVmIGxvY2F0b3I9IjcyOjEtNzI6NjYiIG5hbWU9IlRvU3RyaW5nIiBjb250ZXh0PSJQYXRpZW50IiBhY2Nlc3NMZXZlbD0iUHVibGljIiB4c2k6dHlwZT0iRnVuY3Rpb25EZWYiPgogICAgICAgICA8ZXhwcmVzc2lvbiBsb2NhdG9yPSI3Mjo1Ni03Mjo2NiIgcGF0aD0idmFsdWUiIHhzaTp0eXBlPSJQcm9wZXJ0eSI+CiAgICAgICAgICAgIDxzb3VyY2UgbG9jYXRvcj0iNzI6NTYtNzI6NjAiIG5hbWU9InZhbHVlIiB4c2k6dHlwZT0iT3BlcmFuZFJlZiIvPgogICAgICAgICA8L2V4cHJlc3Npb24+CiAgICAgICAgIDxvcGVyYW5kIG5hbWU9InZhbHVlIj4KICAgICAgICAgICAgPG9wZXJhbmRUeXBlU3BlY2lmaWVyIGxvY2F0b3I9IjcyOjMyLTcyOjUyIiBuYW1lPSJmaGlyOkFzc2VydGlvbk9wZXJhdG9yVHlwZSIgeHNpOnR5cGU9Ik5hbWVkVHlwZVNwZWNpZmllciIvPgogICAgICAgICA8L29wZXJhbmQ+CiAgICAgIDwvZGVmPgogICAgICA8ZGVmIGxvY2F0b3I9IjczOjEtNzM6NjciIG5hbWU9IlRvU3RyaW5nIiBjb250ZXh0PSJQYXRpZW50IiBhY2Nlc3NMZXZlbD0iUHVibGljIiB4c2k6dHlwZT0iRnVuY3Rpb25EZWYiPgogICAgICAgICA8ZXhwcmVzc2lvbiBsb2NhdG9yPSI3Mzo1Ny03Mzo2NyIgcGF0aD0idmFsdWUiIHhzaTp0eXBlPSJQcm9wZXJ0eSI+CiAgICAgICAgICAgIDxzb3VyY2UgbG9jYXRvcj0iNzM6NTctNzM6NjEiIG5hbWU9InZhbHVlIiB4c2k6dHlwZT0iT3BlcmFuZFJlZiIvPgogICAgICAgICA8L2V4cHJlc3Npb24+CiAgICAgICAgIDxvcGVyYW5kIG5hbWU9InZhbHVlIj4KICAgICAgICAgICAgPG9wZXJhbmRUeXBlU3BlY2lmaWVyIGxvY2F0b3I9IjczOjMyLTczOjUzIiBuYW1lPSJmaGlyOkFzc2VydGlvblJlc3BvbnNlVHlwZXMiIHhzaTp0eXBlPSJOYW1lZFR5cGVTcGVjaWZpZXIiLz4KICAgICAgICAgPC9vcGVyYW5kPgogICAgICA8L2RlZj4KICAgICAgPGRlZiBsb2NhdG9yPSI3NDoxLTc0OjYxIiBuYW1lPSJUb1N0cmluZyIgY29udGV4dD0iUGF0aWVudCIgYWNjZXNzTGV2ZWw9IlB1YmxpYyIgeHNpOnR5cGU9IkZ1bmN0aW9uRGVmIj4KICAgICAgICAgPGV4cHJlc3Npb24gbG9jYXRvcj0iNzQ6NTEtNzQ6NjEiIHBhdGg9InZhbHVlIiB4c2k6dHlwZT0iUHJvcGVydHkiPgogICAgICAgICAgICA8c291cmNlIGxvY2F0b3I9Ijc0OjUxLTc0OjU1IiBuYW1lPSJ2YWx1ZSIgeHNpOnR5cGU9Ik9wZXJhbmRSZWYiLz4KICAgICAgICAgPC9leHByZXNzaW9uPgogICAgICAgICA8b3BlcmFuZCBuYW1lPSJ2YWx1ZSI+CiAgICAgICAgICAgIDxvcGVyYW5kVHlwZVNwZWNpZmllciBsb2NhdG9yPSI3NDozMi03NDo0NyIgbmFtZT0iZmhpcjpBdWRpdEV2ZW50QWN0aW9uIiB4c2k6dHlwZT0iTmFtZWRUeXBlU3BlY2lmaWVyIi8+CiAgICAgICAgIDwvb3BlcmFuZD4KICAgICAgPC9kZWY+CiAgICAgIDxkZWYgbG9jYXRvcj0iNzU6MS03NTo3MSIgbmFtZT0iVG9TdHJpbmciIGNvbnRleHQ9IlBhdGllbnQiIGFjY2Vzc0xldmVsPSJQdWJsaWMiIHhzaTp0eXBlPSJGdW5jdGlvbkRlZiI+CiAgICAgICAgIDxleHByZXNzaW9uIGxvY2F0b3I9Ijc1OjYxLTc1OjcxIiBwYXRoPSJ2YWx1ZSIgeHNpOnR5cGU9IlByb3BlcnR5Ij4KICAgICAgICAgICAgPHNvdXJjZSBsb2NhdG9yPSI3NTo2MS03NTo2NSIgbmFtZT0idmFsdWUiIHhzaTp0eXBlPSJPcGVyYW5kUmVmIi8+CiAgICAgICAgIDwvZXhwcmVzc2lvbj4KICAgICAgICAgPG9wZXJhbmQgbmFtZT0idmFsdWUiPgogICAgICAgICAgICA8b3BlcmFuZFR5cGVTcGVjaWZpZXIgbG9jYXRvcj0iNzU6MzItNzU6NTciIG5hbWU9ImZoaXI6QXVkaXRFdmVudEFnZW50TmV0d29ya1R5cGUiIHhzaTp0eXBlPSJOYW1lZFR5cGVTcGVjaWZpZXIiLz4KICAgICAgICAgPC9vcGVyYW5kPgogICAgICA8L2RlZj4KICAgICAgPGRlZiBsb2NhdG9yPSI3NjoxLTc2OjYyIiBuYW1lPSJUb1N0cmluZyIgY29udGV4dD0iUGF0aWVudCIgYWNjZXNzTGV2ZWw9IlB1YmxpYyIgeHNpOnR5cGU9IkZ1bmN0aW9uRGVmIj4KICAgICAgICAgPGV4cHJlc3Npb24gbG9jYXRvcj0iNzY6NTItNzY6NjIiIHBhdGg9InZhbHVlIiB4c2k6dHlwZT0iUHJvcGVydHkiPgogICAgICAgICAgICA8c291cmNlIGxvY2F0b3I9Ijc2OjUyLTc2OjU2IiBuYW1lPSJ2YWx1ZSIgeHNpOnR5cGU9Ik9wZXJhbmRSZWYiLz4KICAgICAgICAgPC9leHByZXNzaW9uPgogICAgICAgICA8b3BlcmFuZCBuYW1lPSJ2YWx1ZSI+CiAgICAgICAgICAgIDxvcGVyYW5kVHlwZVNwZWNpZmllciBsb2NhdG9yPSI3NjozMi03Njo0OCIgbmFtZT0iZmhpcjpBdWRpdEV2ZW50T3V0Y29tZSIgeHNpOnR5cGU9Ik5hbWVkVHlwZVNwZWNpZmllciIvPgogICAgICAgICA8L29wZXJhbmQ+CiAgICAgIDwvZGVmPgogICAgICA8ZGVmIGxvY2F0b3I9Ijc3OjEtNzc6NjAiIG5hbWU9IlRvU3RyaW5nIiBjb250ZXh0PSJQYXRpZW50IiBhY2Nlc3NMZXZlbD0iUHVibGljIiB4c2k6dHlwZT0iRnVuY3Rpb25EZWYiPgogICAgICAgICA8ZXhwcmVzc2lvbiBsb2NhdG9yPSI3Nzo1MC03Nzo2MCIgcGF0aD0idmFsdWUiIHhzaTp0eXBlPSJQcm9wZXJ0eSI+CiAgICAgICAgICAgIDxzb3VyY2UgbG9jYXRvcj0iNzc6NTAtNzc6NTQiIG5hbWU9InZhbHVlIiB4c2k6dHlwZT0iT3BlcmFuZFJlZiIvPgogICAgICAgICA8L2V4cHJlc3Npb24+CiAgICAgICAgIDxvcGVyYW5kIG5hbWU9InZhbHVlIj4KICAgICAgICAgICAgPG9wZXJhbmRUeXBlU3BlY2lmaWVyIGxvY2F0b3I9Ijc3OjMyLTc3OjQ2IiBuYW1lPSJmaGlyOkJpbmRpbmdTdHJlbmd0aCIgeHNpOnR5cGU9Ik5hbWVkVHlwZVNwZWNpZmllciIvPgogICAgICAgICA8L29wZXJhbmQ+CiAgICAgIDwvZGVmPgogICAgICA8ZGVmIGxvY2F0b3I9Ijc4OjEtNzg6NzkiIG5hbWU9IlRvU3RyaW5nIiBjb250ZXh0PSJQYXRpZW50IiBhY2Nlc3NMZXZlbD0iUHVibGljIiB4c2k6dHlwZT0iRnVuY3Rpb25EZWYiPgogICAgICAgICA8ZXhwcmVzc2lvbiBsb2NhdG9yPSI3ODo2OS03ODo3OSIgcGF0aD0idmFsdWUiIHhzaTp0eXBlPSJQcm9wZXJ0eSI+CiAgICAgICAgICAgIDxzb3VyY2UgbG9jYXRvcj0iNzg6NjktNzg6NzMiIG5hbWU9InZhbHVlIiB4c2k6dHlwZT0iT3BlcmFuZFJlZiIvPgogICAgICAgICA8L2V4cHJlc3Npb24+CiAgICAgICAgIDxvcGVyYW5kIG5hbWU9InZhbHVlIj4KICAgICAgICAgICAgPG9wZXJhbmRUeXBlU3BlY2lmaWVyIGxvY2F0b3I9Ijc4OjMyLTc4OjY1IiBuYW1lPSJmaGlyOkJpb2xvZ2ljYWxseURlcml2ZWRQcm9kdWN0Q2F0ZWdvcnkiIHhzaTp0eXBlPSJOYW1lZFR5cGVTcGVjaWZpZXIiLz4KICAgICAgICAgPC9vcGVyYW5kPgogICAgICA8L2RlZj4KICAgICAgPGRlZiBsb2NhdG9yPSI3OToxLTc5Ojc3IiBuYW1lPSJUb1N0cmluZyIgY29udGV4dD0iUGF0aWVudCIgYWNjZXNzTGV2ZWw9IlB1YmxpYyIgeHNpOnR5cGU9IkZ1bmN0aW9uRGVmIj4KICAgICAgICAgPGV4cHJlc3Npb24gbG9jYXRvcj0iNzk6NjctNzk6NzciIHBhdGg9InZhbHVlIiB4c2k6dHlwZT0iUHJvcGVydHkiPgogICAgICAgICAgICA8c291cmNlIGxvY2F0b3I9Ijc5OjY3LTc5OjcxIiBuYW1lPSJ2YWx1ZSIgeHNpOnR5cGU9Ik9wZXJhbmRSZWYiLz4KICAgICAgICAgPC9leHByZXNzaW9uPgogICAgICAgICA8b3BlcmFuZCBuYW1lPSJ2YWx1ZSI+CiAgICAgICAgICAgIDxvcGVyYW5kVHlwZVNwZWNpZmllciBsb2NhdG9yPSI3OTozMi03OTo2MyIgbmFtZT0iZmhpcjpCaW9sb2dpY2FsbHlEZXJpdmVkUHJvZHVjdFN0YXR1cyIgeHNpOnR5cGU9Ik5hbWVkVHlwZVNwZWNpZmllciIvPgogICAgICAgICA8L29wZXJhbmQ+CiAgICAgIDwvZGVmPgogICAgICA8ZGVmIGxvY2F0b3I9IjgwOjEtODA6ODMiIG5hbWU9IlRvU3RyaW5nIiBjb250ZXh0PSJQYXRpZW50IiBhY2Nlc3NMZXZlbD0iUHVibGljIiB4c2k6dHlwZT0iRnVuY3Rpb25EZWYiPgogICAgICAgICA8ZXhwcmVzc2lvbiBsb2NhdG9yPSI4MDo3My04MDo4MyIgcGF0aD0idmFsdWUiIHhzaTp0eXBlPSJQcm9wZXJ0eSI+CiAgICAgICAgICAgIDxzb3VyY2UgbG9jYXRvcj0iODA6NzMtODA6NzciIG5hbWU9InZhbHVlIiB4c2k6dHlwZT0iT3BlcmFuZFJlZiIvPgogICAgICAgICA8L2V4cHJlc3Npb24+CiAgICAgICAgIDxvcGVyYW5kIG5hbWU9InZhbHVlIj4KICAgICAgICAgICAgPG9wZXJhbmRUeXBlU3BlY2lmaWVyIGxvY2F0b3I9IjgwOjMyLTgwOjY5IiBuYW1lPSJmaGlyOkJpb2xvZ2ljYWxseURlcml2ZWRQcm9kdWN0U3RvcmFnZVNjYWxlIiB4c2k6dHlwZT0iTmFtZWRUeXBlU3BlY2lmaWVyIi8+CiAgICAgICAgIDwvb3BlcmFuZD4KICAgICAgPC9kZWY+CiAgICAgIDxkZWYgbG9jYXRvcj0iODE6MS04MTo1NSIgbmFtZT0iVG9TdHJpbmciIGNvbnRleHQ9IlBhdGllbnQiIGFjY2Vzc0xldmVsPSJQdWJsaWMiIHhzaTp0eXBlPSJGdW5jdGlvbkRlZiI+CiAgICAgICAgIDxleHByZXNzaW9uIGxvY2F0b3I9IjgxOjQ1LTgxOjU1IiBwYXRoPSJ2YWx1ZSIgeHNpOnR5cGU9IlByb3BlcnR5Ij4KICAgICAgICAgICAgPHNvdXJjZSBsb2NhdG9yPSI4MTo0NS04MTo0OSIgbmFtZT0idmFsdWUiIHhzaTp0eXBlPSJPcGVyYW5kUmVmIi8+CiAgICAgICAgIDwvZXhwcmVzc2lvbj4KICAgICAgICAgPG9wZXJhbmQgbmFtZT0idmFsdWUiPgogICAgICAgICAgICA8b3BlcmFuZFR5cGVTcGVjaWZpZXIgbG9jYXRvcj0iODE6MzItODE6NDEiIG5hbWU9ImZoaXI6QnVuZGxlVHlwZSIgeHNpOnR5cGU9Ik5hbWVkVHlwZVNwZWNpZmllciIvPgogICAgICAgICA8L29wZXJhbmQ+CiAgICAgIDwvZGVmPgogICAgICA8ZGVmIGxvY2F0b3I9IjgyOjEtODI6NjgiIG5hbWU9IlRvU3RyaW5nIiBjb250ZXh0PSJQYXRpZW50IiBhY2Nlc3NMZXZlbD0iUHVibGljIiB4c2k6dHlwZT0iRnVuY3Rpb25EZWYiPgogICAgICAgICA8ZXhwcmVzc2lvbiBsb2NhdG9yPSI4Mjo1OC04Mjo2OCIgcGF0aD0idmFsdWUiIHhzaTp0eXBlPSJQcm9wZXJ0eSI+CiAgICAgICAgICAgIDxzb3VyY2UgbG9jYXRvcj0iODI6NTgtODI6NjIiIG5hbWU9InZhbHVlIiB4c2k6dHlwZT0iT3BlcmFuZFJlZiIvPgogICAgICAgICA8L2V4cHJlc3Npb24+CiAgICAgICAgIDxvcGVyYW5kIG5hbWU9InZhbHVlIj4KICAgICAgICAgICAgPG9wZXJhbmRUeXBlU3BlY2lmaWVyIGxvY2F0b3I9IjgyOjMyLTgyOjU0IiBuYW1lPSJmaGlyOkNhcGFiaWxpdHlTdGF0ZW1lbnRLaW5kIiB4c2k6dHlwZT0iTmFtZWRUeXBlU3BlY2lmaWVyIi8+CiAgICAgICAgIDwvb3BlcmFuZD4KICAgICAgPC9kZWY+CiAgICAgIDxkZWYgbG9jYXRvcj0iODM6MS04Mzo2NSIgbmFtZT0iVG9TdHJpbmciIGNvbnRleHQ9IlBhdGllbnQiIGFjY2Vzc0xldmVsPSJQdWJsaWMiIHhzaTp0eXBlPSJGdW5jdGlvbkRlZiI+CiAgICAgICAgIDxleHByZXNzaW9uIGxvY2F0b3I9IjgzOjU1LTgzOjY1IiBwYXRoPSJ2YWx1ZSIgeHNpOnR5cGU9IlByb3BlcnR5Ij4KICAgICAgICAgICAgPHNvdXJjZSBsb2NhdG9yPSI4Mzo1NS04Mzo1OSIgbmFtZT0idmFsdWUiIHhzaTp0eXBlPSJPcGVyYW5kUmVmIi8+CiAgICAgICAgIDwvZXhwcmVzc2lvbj4KICAgICAgICAgPG9wZXJhbmQgbmFtZT0idmFsdWUiPgogICAgICAgICAgICA8b3BlcmFuZFR5cGVTcGVjaWZpZXIgbG9jYXRvcj0iODM6MzItODM6NTEiIG5hbWU9ImZoaXI6Q2FyZVBsYW5BY3Rpdml0eUtpbmQiIHhzaTp0eXBlPSJOYW1lZFR5cGVTcGVjaWZpZXIiLz4KICAgICAgICAgPC9vcGVyYW5kPgogICAgICA8L2RlZj4KICAgICAgPGRlZiBsb2NhdG9yPSI4NDoxLTg0OjY3IiBuYW1lPSJUb1N0cmluZyIgY29udGV4dD0iUGF0aWVudCIgYWNjZXNzTGV2ZWw9IlB1YmxpYyIgeHNpOnR5cGU9IkZ1bmN0aW9uRGVmIj4KICAgICAgICAgPGV4cHJlc3Npb24gbG9jYXRvcj0iODQ6NTctODQ6NjciIHBhdGg9InZhbHVlIiB4c2k6dHlwZT0iUHJvcGVydHkiPgogICAgICAgICAgICA8c291cmNlIGxvY2F0b3I9Ijg0OjU3LTg0OjYxIiBuYW1lPSJ2YWx1ZSIgeHNpOnR5cGU9Ik9wZXJhbmRSZWYiLz4KICAgICAgICAgPC9leHByZXNzaW9uPgogICAgICAgICA8b3BlcmFuZCBuYW1lPSJ2YWx1ZSI+CiAgICAgICAgICAgIDxvcGVyYW5kVHlwZVNwZWNpZmllciBsb2NhdG9yPSI4NDozMi04NDo1MyIgbmFtZT0iZmhpcjpDYXJlUGxhbkFjdGl2aXR5U3RhdHVzIiB4c2k6dHlwZT0iTmFtZWRUeXBlU3BlY2lmaWVyIi8+CiAgICAgICAgIDwvb3BlcmFuZD4KICAgICAgPC9kZWY+CiAgICAgIDxkZWYgbG9jYXRvcj0iODU6MS04NTo1OSIgbmFtZT0iVG9TdHJpbmciIGNvbnRleHQ9IlBhdGllbnQiIGFjY2Vzc0xldmVsPSJQdWJsaWMiIHhzaTp0eXBlPSJGdW5jdGlvbkRlZiI+CiAgICAgICAgIDxleHByZXNzaW9uIGxvY2F0b3I9Ijg1OjQ5LTg1OjU5IiBwYXRoPSJ2YWx1ZSIgeHNpOnR5cGU9IlByb3BlcnR5Ij4KICAgICAgICAgICAgPHNvdXJjZSBsb2NhdG9yPSI4NTo0OS04NTo1MyIgbmFtZT0idmFsdWUiIHhzaTp0eXBlPSJPcGVyYW5kUmVmIi8+CiAgICAgICAgIDwvZXhwcmVzc2lvbj4KICAgICAgICAgPG9wZXJhbmQgbmFtZT0idmFsdWUiPgogICAgICAgICAgICA8b3BlcmFuZFR5cGVTcGVjaWZpZXIgbG9jYXRvcj0iODU6MzItODU6NDUiIG5hbWU9ImZoaXI6Q2FyZVBsYW5JbnRlbnQiIHhzaTp0eXBlPSJOYW1lZFR5cGVTcGVjaWZpZXIiLz4KICAgICAgICAgPC9vcGVyYW5kPgogICAgICA8L2RlZj4KICAgICAgPGRlZiBsb2NhdG9yPSI4NjoxLTg2OjU5IiBuYW1lPSJUb1N0cmluZyIgY29udGV4dD0iUGF0aWVudCIgYWNjZXNzTGV2ZWw9IlB1YmxpYyIgeHNpOnR5cGU9IkZ1bmN0aW9uRGVmIj4KICAgICAgICAgPGV4cHJlc3Npb24gbG9jYXRvcj0iODY6NDktODY6NTkiIHBhdGg9InZhbHVlIiB4c2k6dHlwZT0iUHJvcGVydHkiPgogICAgICAgICAgICA8c291cmNlIGxvY2F0b3I9Ijg2OjQ5LTg2OjUzIiBuYW1lPSJ2YWx1ZSIgeHNpOnR5cGU9Ik9wZXJhbmRSZWYiLz4KICAgICAgICAgPC9leHByZXNzaW9uPgogICAgICAgICA8b3BlcmFuZCBuYW1lPSJ2YWx1ZSI+CiAgICAgICAgICAgIDxvcGVyYW5kVHlwZVNwZWNpZmllciBsb2NhdG9yPSI4NjozMi04Njo0NSIgbmFtZT0iZmhpcjpDYXJlUGxhblN0YXR1cyIgeHNpOnR5cGU9Ik5hbWVkVHlwZVNwZWNpZmllciIvPgogICAgICAgICA8L29wZXJhbmQ+CiAgICAgIDwvZGVmPgogICAgICA8ZGVmIGxvY2F0b3I9Ijg3OjEtODc6NTkiIG5hbWU9IlRvU3RyaW5nIiBjb250ZXh0PSJQYXRpZW50IiBhY2Nlc3NMZXZlbD0iUHVibGljIiB4c2k6dHlwZT0iRnVuY3Rpb25EZWYiPgogICAgICAgICA8ZXhwcmVzc2lvbiBsb2NhdG9yPSI4Nzo0OS04Nzo1OSIgcGF0aD0idmFsdWUiIHhzaTp0eXBlPSJQcm9wZXJ0eSI+CiAgICAgICAgICAgIDxzb3VyY2UgbG9jYXRvcj0iODc6NDktODc6NTMiIG5hbWU9InZhbHVlIiB4c2k6dHlwZT0iT3BlcmFuZFJlZiIvPgogICAgICAgICA8L2V4cHJlc3Npb24+CiAgICAgICAgIDxvcGVyYW5kIG5hbWU9InZhbHVlIj4KICAgICAgICAgICAgPG9wZXJhbmRUeXBlU3BlY2lmaWVyIGxvY2F0b3I9Ijg3OjMyLTg3OjQ1IiBuYW1lPSJmaGlyOkNhcmVUZWFtU3RhdHVzIiB4c2k6dHlwZT0iTmFtZWRUeXBlU3BlY2lmaWVyIi8+CiAgICAgICAgIDwvb3BlcmFuZD4KICAgICAgPC9kZWY+CiAgICAgIDxkZWYgbG9jYXRvcj0iODg6MS04ODo2OSIgbmFtZT0iVG9TdHJpbmciIGNvbnRleHQ9IlBhdGllbnQiIGFjY2Vzc0xldmVsPSJQdWJsaWMiIHhzaTp0eXBlPSJGdW5jdGlvbkRlZiI+CiAgICAgICAgIDxleHByZXNzaW9uIGxvY2F0b3I9Ijg4OjU5LTg4OjY5IiBwYXRoPSJ2YWx1ZSIgeHNpOnR5cGU9IlByb3BlcnR5Ij4KICAgICAgICAgICAgPHNvdXJjZSBsb2NhdG9yPSI4ODo1OS04ODo2MyIgbmFtZT0idmFsdWUiIHhzaTp0eXBlPSJPcGVyYW5kUmVmIi8+CiAgICAgICAgIDwvZXhwcmVzc2lvbj4KICAgICAgICAgPG9wZXJhbmQgbmFtZT0idmFsdWUiPgogICAgICAgICAgICA8b3BlcmFuZFR5cGVTcGVjaWZpZXIgbG9jYXRvcj0iODg6MzItODg6NTUiIG5hbWU9ImZoaXI6Q2F0YWxvZ0VudHJ5UmVsYXRpb25UeXBlIiB4c2k6dHlwZT0iTmFtZWRUeXBlU3BlY2lmaWVyIi8+CiAgICAgICAgIDwvb3BlcmFuZD4KICAgICAgPC9kZWY+CiAgICAgIDxkZWYgbG9jYXRvcj0iODk6MS04OTo4MyIgbmFtZT0iVG9TdHJpbmciIGNvbnRleHQ9IlBhdGllbnQiIGFjY2Vzc0xldmVsPSJQdWJsaWMiIHhzaTp0eXBlPSJGdW5jdGlvbkRlZiI+CiAgICAgICAgIDxleHByZXNzaW9uIGxvY2F0b3I9Ijg5OjczLTg5OjgzIiBwYXRoPSJ2YWx1ZSIgeHNpOnR5cGU9IlByb3BlcnR5Ij4KICAgICAgICAgICAgPHNvdXJjZSBsb2NhdG9yPSI4OTo3My04OTo3NyIgbmFtZT0idmFsdWUiIHhzaTp0eXBlPSJPcGVyYW5kUmVmIi8+CiAgICAgICAgIDwvZXhwcmVzc2lvbj4KICAgICAgICAgPG9wZXJhbmQgbmFtZT0idmFsdWUiPgogICAgICAgICAgICA8b3BlcmFuZFR5cGVTcGVjaWZpZXIgbG9jYXRvcj0iODk6MzItODk6NjkiIG5hbWU9ImZoaXI6Q2hhcmdlSXRlbURlZmluaXRpb25QcmljZUNvbXBvbmVudFR5cGUiIHhzaTp0eXBlPSJOYW1lZFR5cGVTcGVjaWZpZXIiLz4KICAgICAgICAgPC9vcGVyYW5kPgogICAgICA8L2RlZj4KICAgICAgPGRlZiBsb2NhdG9yPSI5MDoxLTkwOjYxIiBuYW1lPSJUb1N0cmluZyIgY29udGV4dD0iUGF0aWVudCIgYWNjZXNzTGV2ZWw9IlB1YmxpYyIgeHNpOnR5cGU9IkZ1bmN0aW9uRGVmIj4KICAgICAgICAgPGV4cHJlc3Npb24gbG9jYXRvcj0iOTA6NTEtOTA6NjEiIHBhdGg9InZhbHVlIiB4c2k6dHlwZT0iUHJvcGVydHkiPgogICAgICAgICAgICA8c291cmNlIGxvY2F0b3I9IjkwOjUxLTkwOjU1IiBuYW1lPSJ2YWx1ZSIgeHNpOnR5cGU9Ik9wZXJhbmRSZWYiLz4KICAgICAgICAgPC9leHByZXNzaW9uPgogICAgICAgICA8b3BlcmFuZCBuYW1lPSJ2YWx1ZSI+CiAgICAgICAgICAgIDxvcGVyYW5kVHlwZVNwZWNpZmllciBsb2NhdG9yPSI5MDozMi05MDo0NyIgbmFtZT0iZmhpcjpDaGFyZ2VJdGVtU3RhdHVzIiB4c2k6dHlwZT0iTmFtZWRUeXBlU3BlY2lmaWVyIi8+CiAgICAgICAgIDwvb3BlcmFuZD4KICAgICAgPC9kZWY+CiAgICAgIDxkZWYgbG9jYXRvcj0iOTE6MS05MTo2NCIgbmFtZT0iVG9TdHJpbmciIGNvbnRleHQ9IlBhdGllbnQiIGFjY2Vzc0xldmVsPSJQdWJsaWMiIHhzaTp0eXBlPSJGdW5jdGlvbkRlZiI+CiAgICAgICAgIDxleHByZXNzaW9uIGxvY2F0b3I9IjkxOjU0LTkxOjY0IiBwYXRoPSJ2YWx1ZSIgeHNpOnR5cGU9IlByb3BlcnR5Ij4KICAgICAgICAgICAgPHNvdXJjZSBsb2NhdG9yPSI5MTo1NC05MTo1OCIgbmFtZT0idmFsdWUiIHhzaTp0eXBlPSJPcGVyYW5kUmVmIi8+CiAgICAgICAgIDwvZXhwcmVzc2lvbj4KICAgICAgICAgPG9wZXJhbmQgbmFtZT0idmFsdWUiPgogICAgICAgICAgICA8b3BlcmFuZFR5cGVTcGVjaWZpZXIgbG9jYXRvcj0iOTE6MzItOTE6NTAiIG5hbWU9ImZoaXI6Q2xhaW1SZXNwb25zZVN0YXR1cyIgeHNpOnR5cGU9Ik5hbWVkVHlwZVNwZWNpZmllciIvPgogICAgICAgICA8L29wZXJhbmQ+CiAgICAgIDwvZGVmPgogICAgICA8ZGVmIGxvY2F0b3I9IjkyOjEtOTI6NTYiIG5hbWU9IlRvU3RyaW5nIiBjb250ZXh0PSJQYXRpZW50IiBhY2Nlc3NMZXZlbD0iUHVibGljIiB4c2k6dHlwZT0iRnVuY3Rpb25EZWYiPgogICAgICAgICA8ZXhwcmVzc2lvbiBsb2NhdG9yPSI5Mjo0Ni05Mjo1NiIgcGF0aD0idmFsdWUiIHhzaTp0eXBlPSJQcm9wZXJ0eSI+CiAgICAgICAgICAgIDxzb3VyY2UgbG9jYXRvcj0iOTI6NDYtOTI6NTAiIG5hbWU9InZhbHVlIiB4c2k6dHlwZT0iT3BlcmFuZFJlZiIvPgogICAgICAgICA8L2V4cHJlc3Npb24+CiAgICAgICAgIDxvcGVyYW5kIG5hbWU9InZhbHVlIj4KICAgICAgICAgICAgPG9wZXJhbmRUeXBlU3BlY2lmaWVyIGxvY2F0b3I9IjkyOjMyLTkyOjQyIiBuYW1lPSJmaGlyOkNsYWltU3RhdHVzIiB4c2k6dHlwZT0iTmFtZWRUeXBlU3BlY2lmaWVyIi8+CiAgICAgICAgIDwvb3BlcmFuZD4KICAgICAgPC9kZWY+CiAgICAgIDxkZWYgbG9jYXRvcj0iOTM6MS05Mzo2OSIgbmFtZT0iVG9TdHJpbmciIGNvbnRleHQ9IlBhdGllbnQiIGFjY2Vzc0xldmVsPSJQdWJsaWMiIHhzaTp0eXBlPSJGdW5jdGlvbkRlZiI+CiAgICAgICAgIDxleHByZXNzaW9uIGxvY2F0b3I9IjkzOjU5LTkzOjY5IiBwYXRoPSJ2YWx1ZSIgeHNpOnR5cGU9IlByb3BlcnR5Ij4KICAgICAgICAgICAgPHNvdXJjZSBsb2NhdG9yPSI5Mzo1OS05Mzo2MyIgbmFtZT0idmFsdWUiIHhzaTp0eXBlPSJPcGVyYW5kUmVmIi8+CiAgICAgICAgIDwvZXhwcmVzc2lvbj4KICAgICAgICAgPG9wZXJhbmQgbmFtZT0idmFsdWUiPgogICAgICAgICAgICA8b3BlcmFuZFR5cGVTcGVjaWZpZXIgbG9jYXRvcj0iOTM6MzItOTM6NTUiIG5hbWU9ImZoaXI6Q2xpbmljYWxJbXByZXNzaW9uU3RhdHVzIiB4c2k6dHlwZT0iTmFtZWRUeXBlU3BlY2lmaWVyIi8+CiAgICAgICAgIDwvb3BlcmFuZD4KICAgICAgPC9kZWY+CiAgICAgIDxkZWYgbG9jYXRvcj0iOTQ6MS05NDo2MiIgbmFtZT0iVG9TdHJpbmciIGNvbnRleHQ9IlBhdGllbnQiIGFjY2Vzc0xldmVsPSJQdWJsaWMiIHhzaTp0eXBlPSJGdW5jdGlvbkRlZiI+CiAgICAgICAgIDxleHByZXNzaW9uIGxvY2F0b3I9Ijk0OjUyLTk0OjYyIiBwYXRoPSJ2YWx1ZSIgeHNpOnR5cGU9IlByb3BlcnR5Ij4KICAgICAgICAgICAgPHNvdXJjZSBsb2NhdG9yPSI5NDo1Mi05NDo1NiIgbmFtZT0idmFsdWUiIHhzaTp0eXBlPSJPcGVyYW5kUmVmIi8+CiAgICAgICAgIDwvZXhwcmVzc2lvbj4KICAgICAgICAgPG9wZXJhbmQgbmFtZT0idmFsdWUiPgogICAgICAgICAgICA8b3BlcmFuZFR5cGVTcGVjaWZpZXIgbG9jYXRvcj0iOTQ6MzItOTQ6NDgiIG5hbWU9ImZoaXI6Q29kZVNlYXJjaFN1cHBvcnQiIHhzaTp0eXBlPSJOYW1lZFR5cGVTcGVjaWZpZXIiLz4KICAgICAgICAgPC9vcGVyYW5kPgogICAgICA8L2RlZj4KICAgICAgPGRlZiBsb2NhdG9yPSI5NToxLTk1OjY2IiBuYW1lPSJUb1N0cmluZyIgY29udGV4dD0iUGF0aWVudCIgYWNjZXNzTGV2ZWw9IlB1YmxpYyIgeHNpOnR5cGU9IkZ1bmN0aW9uRGVmIj4KICAgICAgICAgPGV4cHJlc3Npb24gbG9jYXRvcj0iOTU6NTYtOTU6NjYiIHBhdGg9InZhbHVlIiB4c2k6dHlwZT0iUHJvcGVydHkiPgogICAgICAgICAgICA8c291cmNlIGxvY2F0b3I9Ijk1OjU2LTk1OjYwIiBuYW1lPSJ2YWx1ZSIgeHNpOnR5cGU9Ik9wZXJhbmRSZWYiLz4KICAgICAgICAgPC9leHByZXNzaW9uPgogICAgICAgICA8b3BlcmFuZCBuYW1lPSJ2YWx1ZSI+CiAgICAgICAgICAgIDxvcGVyYW5kVHlwZVNwZWNpZmllciBsb2NhdG9yPSI5NTozMi05NTo1MiIgbmFtZT0iZmhpcjpDb2RlU3lzdGVtQ29udGVudE1vZGUiIHhzaTp0eXBlPSJOYW1lZFR5cGVTcGVjaWZpZXIiLz4KICAgICAgICAgPC9vcGVyYW5kPgogICAgICA8L2RlZj4KICAgICAgPGRlZiBsb2NhdG9yPSI5NjoxLTk2OjcxIiBuYW1lPSJUb1N0cmluZyIgY29udGV4dD0iUGF0aWVudCIgYWNjZXNzTGV2ZWw9IlB1YmxpYyIgeHNpOnR5cGU9IkZ1bmN0aW9uRGVmIj4KICAgICAgICAgPGV4cHJlc3Npb24gbG9jYXRvcj0iOTY6NjEtOTY6NzEiIHBhdGg9InZhbHVlIiB4c2k6dHlwZT0iUHJvcGVydHkiPgogICAgICAgICAgICA8c291cmNlIGxvY2F0b3I9Ijk2OjYxLTk2OjY1IiBuYW1lPSJ2YWx1ZSIgeHNpOnR5cGU9Ik9wZXJhbmRSZWYiLz4KICAgICAgICAgPC9leHByZXNzaW9uPgogICAgICAgICA8b3BlcmFuZCBuYW1lPSJ2YWx1ZSI+CiAgICAgICAgICAgIDxvcGVyYW5kVHlwZVNwZWNpZmllciBsb2NhdG9yPSI5NjozMi05Njo1NyIgbmFtZT0iZmhpcjpDb2RlU3lzdGVtSGllcmFyY2h5TWVhbmluZyIgeHNpOnR5cGU9Ik5hbWVkVHlwZVNwZWNpZmllciIvPgogICAgICAgICA8L29wZXJhbmQ+CiAgICAgIDwvZGVmPgogICAgICA8ZGVmIGxvY2F0b3I9Ijk3OjEtOTc6NjYiIG5hbWU9IlRvU3RyaW5nIiBjb250ZXh0PSJQYXRpZW50IiBhY2Nlc3NMZXZlbD0iUHVibGljIiB4c2k6dHlwZT0iRnVuY3Rpb25EZWYiPgogICAgICAgICA8ZXhwcmVzc2lvbiBsb2NhdG9yPSI5Nzo1Ni05Nzo2NiIgcGF0aD0idmFsdWUiIHhzaTp0eXBlPSJQcm9wZXJ0eSI+CiAgICAgICAgICAgIDxzb3VyY2UgbG9jYXRvcj0iOTc6NTYtOTc6NjAiIG5hbWU9InZhbHVlIiB4c2k6dHlwZT0iT3BlcmFuZFJlZiIvPgogICAgICAgICA8L2V4cHJlc3Npb24+CiAgICAgICAgIDxvcGVyYW5kIG5hbWU9InZhbHVlIj4KICAgICAgICAgICAgPG9wZXJhbmRUeXBlU3BlY2lmaWVyIGxvY2F0b3I9Ijk3OjMyLTk3OjUyIiBuYW1lPSJmaGlyOkNvbW11bmljYXRpb25Qcmlvcml0eSIgeHNpOnR5cGU9Ik5hbWVkVHlwZVNwZWNpZmllciIvPgogICAgICAgICA8L29wZXJhbmQ+CiAgICAgIDwvZGVmPgogICAgICA8ZGVmIGxvY2F0b3I9Ijk4OjEtOTg6NzEiIG5hbWU9IlRvU3RyaW5nIiBjb250ZXh0PSJQYXRpZW50IiBhY2Nlc3NMZXZlbD0iUHVibGljIiB4c2k6dHlwZT0iRnVuY3Rpb25EZWYiPgogICAgICAgICA8ZXhwcmVzc2lvbiBsb2NhdG9yPSI5ODo2MS05ODo3MSIgcGF0aD0idmFsdWUiIHhzaTp0eXBlPSJQcm9wZXJ0eSI+CiAgICAgICAgICAgIDxzb3VyY2UgbG9jYXRvcj0iOTg6NjEtOTg6NjUiIG5hbWU9InZhbHVlIiB4c2k6dHlwZT0iT3BlcmFuZFJlZiIvPgogICAgICAgICA8L2V4cHJlc3Npb24+CiAgICAgICAgIDxvcGVyYW5kIG5hbWU9InZhbHVlIj4KICAgICAgICAgICAgPG9wZXJhbmRUeXBlU3BlY2lmaWVyIGxvY2F0b3I9Ijk4OjMyLTk4OjU3IiBuYW1lPSJmaGlyOkNvbW11bmljYXRpb25SZXF1ZXN0U3RhdHVzIiB4c2k6dHlwZT0iTmFtZWRUeXBlU3BlY2lmaWVyIi8+CiAgICAgICAgIDwvb3BlcmFuZD4KICAgICAgPC9kZWY+CiAgICAgIDxkZWYgbG9jYXRvcj0iOTk6MS05OTo2NCIgbmFtZT0iVG9TdHJpbmciIGNvbnRleHQ9IlBhdGllbnQiIGFjY2Vzc0xldmVsPSJQdWJsaWMiIHhzaTp0eXBlPSJGdW5jdGlvbkRlZiI+CiAgICAgICAgIDxleHByZXNzaW9uIGxvY2F0b3I9Ijk5OjU0LTk5OjY0IiBwYXRoPSJ2YWx1ZSIgeHNpOnR5cGU9IlByb3BlcnR5Ij4KICAgICAgICAgICAgPHNvdXJjZSBsb2NhdG9yPSI5OTo1NC05OTo1OCIgbmFtZT0idmFsdWUiIHhzaTp0eXBlPSJPcGVyYW5kUmVmIi8+CiAgICAgICAgIDwvZXhwcmVzc2lvbj4KICAgICAgICAgPG9wZXJhbmQgbmFtZT0idmFsdWUiPgogICAgICAgICAgICA8b3BlcmFuZFR5cGVTcGVjaWZpZXIgbG9jYXRvcj0iOTk6MzItOTk6NTAiIG5hbWU9ImZoaXI6Q29tbXVuaWNhdGlvblN0YXR1cyIgeHNpOnR5cGU9Ik5hbWVkVHlwZVNwZWNpZmllciIvPgogICAgICAgICA8L29wZXJhbmQ+CiAgICAgIDwvZGVmPgogICAgICA8ZGVmIGxvY2F0b3I9IjEwMDoxLTEwMDo2MCIgbmFtZT0iVG9TdHJpbmciIGNvbnRleHQ9IlBhdGllbnQiIGFjY2Vzc0xldmVsPSJQdWJsaWMiIHhzaTp0eXBlPSJGdW5jdGlvbkRlZiI+CiAgICAgICAgIDxleHByZXNzaW9uIGxvY2F0b3I9IjEwMDo1MC0xMDA6NjAiIHBhdGg9InZhbHVlIiB4c2k6dHlwZT0iUHJvcGVydHkiPgogICAgICAgICAgICA8c291cmNlIGxvY2F0b3I9IjEwMDo1MC0xMDA6NTQiIG5hbWU9InZhbHVlIiB4c2k6dHlwZT0iT3BlcmFuZFJlZiIvPgogICAgICAgICA8L2V4cHJlc3Npb24+CiAgICAgICAgIDxvcGVyYW5kIG5hbWU9InZhbHVlIj4KICAgICAgICAgICAgPG9wZXJhbmRUeXBlU3BlY2lmaWVyIGxvY2F0b3I9IjEwMDozMi0xMDA6NDYiIG5hbWU9ImZoaXI6Q29tcGFydG1lbnRDb2RlIiB4c2k6dHlwZT0iTmFtZWRUeXBlU3BlY2lmaWVyIi8+CiAgICAgICAgIDwvb3BlcmFuZD4KICAgICAgPC9kZWY+CiAgICAgIDxkZWYgbG9jYXRvcj0iMTAxOjEtMTAxOjYwIiBuYW1lPSJUb1N0cmluZyIgY29udGV4dD0iUGF0aWVudCIgYWNjZXNzTGV2ZWw9IlB1YmxpYyIgeHNpOnR5cGU9IkZ1bmN0aW9uRGVmIj4KICAgICAgICAgPGV4cHJlc3Npb24gbG9jYXRvcj0iMTAxOjUwLTEwMTo2MCIgcGF0aD0idmFsdWUiIHhzaTp0eXBlPSJQcm9wZXJ0eSI+CiAgICAgICAgICAgIDxzb3VyY2UgbG9jYXRvcj0iMTAxOjUwLTEwMTo1NCIgbmFtZT0idmFsdWUiIHhzaTp0eXBlPSJPcGVyYW5kUmVmIi8+CiAgICAgICAgIDwvZXhwcmVzc2lvbj4KICAgICAgICAgPG9wZXJhbmQgbmFtZT0idmFsdWUiPgogICAgICAgICAgICA8b3BlcmFuZFR5cGVTcGVjaWZpZXIgbG9jYXRvcj0iMTAxOjMyLTEwMTo0NiIgbmFtZT0iZmhpcjpDb21wYXJ0bWVudFR5cGUiIHhzaTp0eXBlPSJOYW1lZFR5cGVTcGVjaWZpZXIiLz4KICAgICAgICAgPC9vcGVyYW5kPgogICAgICA8L2RlZj4KICAgICAgPGRlZiBsb2NhdG9yPSIxMDI6MS0xMDI6NzEiIG5hbWU9IlRvU3RyaW5nIiBjb250ZXh0PSJQYXRpZW50IiBhY2Nlc3NMZXZlbD0iUHVibGljIiB4c2k6dHlwZT0iRnVuY3Rpb25EZWYiPgogICAgICAgICA8ZXhwcmVzc2lvbiBsb2NhdG9yPSIxMDI6NjEtMTAyOjcxIiBwYXRoPSJ2YWx1ZSIgeHNpOnR5cGU9IlByb3BlcnR5Ij4KICAgICAgICAgICAgPHNvdXJjZSBsb2NhdG9yPSIxMDI6NjEtMTAyOjY1IiBuYW1lPSJ2YWx1ZSIgeHNpOnR5cGU9Ik9wZXJhbmRSZWYiLz4KICAgICAgICAgPC9leHByZXNzaW9uPgogICAgICAgICA8b3BlcmFuZCBuYW1lPSJ2YWx1ZSI+CiAgICAgICAgICAgIDxvcGVyYW5kVHlwZVNwZWNpZmllciBsb2NhdG9yPSIxMDI6MzItMTAyOjU3IiBuYW1lPSJmaGlyOkNvbXBvc2l0aW9uQXR0ZXN0YXRpb25Nb2RlIiB4c2k6dHlwZT0iTmFtZWRUeXBlU3BlY2lmaWVyIi8+CiAgICAgICAgIDwvb3BlcmFuZD4KICAgICAgPC9kZWY+CiAgICAgIDxkZWYgbG9jYXRvcj0iMTAzOjEtMTAzOjYyIiBuYW1lPSJUb1N0cmluZyIgY29udGV4dD0iUGF0aWVudCIgYWNjZXNzTGV2ZWw9IlB1YmxpYyIgeHNpOnR5cGU9IkZ1bmN0aW9uRGVmIj4KICAgICAgICAgPGV4cHJlc3Npb24gbG9jYXRvcj0iMTAzOjUyLTEwMzo2MiIgcGF0aD0idmFsdWUiIHhzaTp0eXBlPSJQcm9wZXJ0eSI+CiAgICAgICAgICAgIDxzb3VyY2UgbG9jYXRvcj0iMTAzOjUyLTEwMzo1NiIgbmFtZT0idmFsdWUiIHhzaTp0eXBlPSJPcGVyYW5kUmVmIi8+CiAgICAgICAgIDwvZXhwcmVzc2lvbj4KICAgICAgICAgPG9wZXJhbmQgbmFtZT0idmFsdWUiPgogICAgICAgICAgICA8b3BlcmFuZFR5cGVTcGVjaWZpZXIgbG9jYXRvcj0iMTAzOjMyLTEwMzo0OCIgbmFtZT0iZmhpcjpDb21wb3NpdGlvblN0YXR1cyIgeHNpOnR5cGU9Ik5hbWVkVHlwZVNwZWNpZmllciIvPgogICAgICAgICA8L29wZXJhbmQ+CiAgICAgIDwvZGVmPgogICAgICA8ZGVmIGxvY2F0b3I9IjEwNDoxLTEwNDo2NiIgbmFtZT0iVG9TdHJpbmciIGNvbnRleHQ9IlBhdGllbnQiIGFjY2Vzc0xldmVsPSJQdWJsaWMiIHhzaTp0eXBlPSJGdW5jdGlvbkRlZiI+CiAgICAgICAgIDxleHByZXNzaW9uIGxvY2F0b3I9IjEwNDo1Ni0xMDQ6NjYiIHBhdGg9InZhbHVlIiB4c2k6dHlwZT0iUHJvcGVydHkiPgogICAgICAgICAgICA8c291cmNlIGxvY2F0b3I9IjEwNDo1Ni0xMDQ6NjAiIG5hbWU9InZhbHVlIiB4c2k6dHlwZT0iT3BlcmFuZFJlZiIvPgogICAgICAgICA8L2V4cHJlc3Npb24+CiAgICAgICAgIDxvcGVyYW5kIG5hbWU9InZhbHVlIj4KICAgICAgICAgICAgPG9wZXJhbmRUeXBlU3BlY2lmaWVyIGxvY2F0b3I9IjEwNDozMi0xMDQ6NTIiIG5hbWU9ImZoaXI6Q29uY2VwdE1hcEVxdWl2YWxlbmNlIiB4c2k6dHlwZT0iTmFtZWRUeXBlU3BlY2lmaWVyIi8+CiAgICAgICAgIDwvb3BlcmFuZD4KICAgICAgPC9kZWY+CiAgICAgIDxkZWYgbG9jYXRvcj0iMTA1OjEtMTA1OjcyIiBuYW1lPSJUb1N0cmluZyIgY29udGV4dD0iUGF0aWVudCIgYWNjZXNzTGV2ZWw9IlB1YmxpYyIgeHNpOnR5cGU9IkZ1bmN0aW9uRGVmIj4KICAgICAgICAgPGV4cHJlc3Npb24gbG9jYXRvcj0iMTA1OjYyLTEwNTo3MiIgcGF0aD0idmFsdWUiIHhzaTp0eXBlPSJQcm9wZXJ0eSI+CiAgICAgICAgICAgIDxzb3VyY2UgbG9jYXRvcj0iMTA1OjYyLTEwNTo2NiIgbmFtZT0idmFsdWUiIHhzaTp0eXBlPSJPcGVyYW5kUmVmIi8+CiAgICAgICAgIDwvZXhwcmVzc2lvbj4KICAgICAgICAgPG9wZXJhbmQgbmFtZT0idmFsdWUiPgogICAgICAgICAgICA8b3BlcmFuZFR5cGVTcGVjaWZpZXIgbG9jYXRvcj0iMTA1OjMyLTEwNTo1OCIgbmFtZT0iZmhpcjpDb25jZXB0TWFwR3JvdXBVbm1hcHBlZE1vZGUiIHhzaTp0eXBlPSJOYW1lZFR5cGVTcGVjaWZpZXIiLz4KICAgICAgICAgPC9vcGVyYW5kPgogICAgICA8L2RlZj4KICAgICAgPGRlZiBsb2NhdG9yPSIxMDY6MS0xMDY6NjgiIG5hbWU9IlRvU3RyaW5nIiBjb250ZXh0PSJQYXRpZW50IiBhY2Nlc3NMZXZlbD0iUHVibGljIiB4c2k6dHlwZT0iRnVuY3Rpb25EZWYiPgogICAgICAgICA8ZXhwcmVzc2lvbiBsb2NhdG9yPSIxMDY6NTgtMTA2OjY4IiBwYXRoPSJ2YWx1ZSIgeHNpOnR5cGU9IlByb3BlcnR5Ij4KICAgICAgICAgICAgPHNvdXJjZSBsb2NhdG9yPSIxMDY6NTgtMTA2OjYyIiBuYW1lPSJ2YWx1ZSIgeHNpOnR5cGU9Ik9wZXJhbmRSZWYiLz4KICAgICAgICAgPC9leHByZXNzaW9uPgogICAgICAgICA8b3BlcmFuZCBuYW1lPSJ2YWx1ZSI+CiAgICAgICAgICAgIDxvcGVyYW5kVHlwZVNwZWNpZmllciBsb2NhdG9yPSIxMDY6MzItMTA2OjU0IiBuYW1lPSJmaGlyOkNvbmRpdGlvbmFsRGVsZXRlU3RhdHVzIiB4c2k6dHlwZT0iTmFtZWRUeXBlU3BlY2lmaWVyIi8+CiAgICAgICAgIDwvb3BlcmFuZD4KICAgICAgPC9kZWY+CiAgICAgIDxkZWYgbG9jYXRvcj0iMTA3OjEtMTA3OjY2IiBuYW1lPSJUb1N0cmluZyIgY29udGV4dD0iUGF0aWVudCIgYWNjZXNzTGV2ZWw9IlB1YmxpYyIgeHNpOnR5cGU9IkZ1bmN0aW9uRGVmIj4KICAgICAgICAgPGV4cHJlc3Npb24gbG9jYXRvcj0iMTA3OjU2LTEwNzo2NiIgcGF0aD0idmFsdWUiIHhzaTp0eXBlPSJQcm9wZXJ0eSI+CiAgICAgICAgICAgIDxzb3VyY2UgbG9jYXRvcj0iMTA3OjU2LTEwNzo2MCIgbmFtZT0idmFsdWUiIHhzaTp0eXBlPSJPcGVyYW5kUmVmIi8+CiAgICAgICAgIDwvZXhwcmVzc2lvbj4KICAgICAgICAgPG9wZXJhbmQgbmFtZT0idmFsdWUiPgogICAgICAgICAgICA8b3BlcmFuZFR5cGVTcGVjaWZpZXIgbG9jYXRvcj0iMTA3OjMyLTEwNzo1MiIgbmFtZT0iZmhpcjpDb25kaXRpb25hbFJlYWRTdGF0dXMiIHhzaTp0eXBlPSJOYW1lZFR5cGVTcGVjaWZpZXIiLz4KICAgICAgICAgPC9vcGVyYW5kPgogICAgICA8L2RlZj4KICAgICAgPGRlZiBsb2NhdG9yPSIxMDg6MS0xMDg6NjMiIG5hbWU9IlRvU3RyaW5nIiBjb250ZXh0PSJQYXRpZW50IiBhY2Nlc3NMZXZlbD0iUHVibGljIiB4c2k6dHlwZT0iRnVuY3Rpb25EZWYiPgogICAgICAgICA8ZXhwcmVzc2lvbiBsb2NhdG9yPSIxMDg6NTMtMTA4OjYzIiBwYXRoPSJ2YWx1ZSIgeHNpOnR5cGU9IlByb3BlcnR5Ij4KICAgICAgICAgICAgPHNvdXJjZSBsb2NhdG9yPSIxMDg6NTMtMTA4OjU3IiBuYW1lPSJ2YWx1ZSIgeHNpOnR5cGU9Ik9wZXJhbmRSZWYiLz4KICAgICAgICAgPC9leHByZXNzaW9uPgogICAgICAgICA8b3BlcmFuZCBuYW1lPSJ2YWx1ZSI+CiAgICAgICAgICAgIDxvcGVyYW5kVHlwZVNwZWNpZmllciBsb2NhdG9yPSIxMDg6MzItMTA4OjQ5IiBuYW1lPSJmaGlyOkNvbnNlbnREYXRhTWVhbmluZyIgeHNpOnR5cGU9Ik5hbWVkVHlwZVNwZWNpZmllciIvPgogICAgICAgICA8L29wZXJhbmQ+CiAgICAgIDwvZGVmPgogICAgICA8ZGVmIGxvY2F0b3I9IjEwOToxLTEwOTo2NSIgbmFtZT0iVG9TdHJpbmciIGNvbnRleHQ9IlBhdGllbnQiIGFjY2Vzc0xldmVsPSJQdWJsaWMiIHhzaTp0eXBlPSJGdW5jdGlvbkRlZiI+CiAgICAgICAgIDxleHByZXNzaW9uIGxvY2F0b3I9IjEwOTo1NS0xMDk6NjUiIHBhdGg9InZhbHVlIiB4c2k6dHlwZT0iUHJvcGVydHkiPgogICAgICAgICAgICA8c291cmNlIGxvY2F0b3I9IjEwOTo1NS0xMDk6NTkiIG5hbWU9InZhbHVlIiB4c2k6dHlwZT0iT3BlcmFuZFJlZiIvPgogICAgICAgICA8L2V4cHJlc3Npb24+CiAgICAgICAgIDxvcGVyYW5kIG5hbWU9InZhbHVlIj4KICAgICAgICAgICAgPG9wZXJhbmRUeXBlU3BlY2lmaWVyIGxvY2F0b3I9IjEwOTozMi0xMDk6NTEiIG5hbWU9ImZoaXI6Q29uc2VudFByb3Zpc2lvblR5cGUiIHhzaTp0eXBlPSJOYW1lZFR5cGVTcGVjaWZpZXIiLz4KICAgICAgICAgPC9vcGVyYW5kPgogICAgICA8L2RlZj4KICAgICAgPGRlZiBsb2NhdG9yPSIxMTA6MS0xMTA6NTciIG5hbWU9IlRvU3RyaW5nIiBjb250ZXh0PSJQYXRpZW50IiBhY2Nlc3NMZXZlbD0iUHVibGljIiB4c2k6dHlwZT0iRnVuY3Rpb25EZWYiPgogICAgICAgICA8ZXhwcmVzc2lvbiBsb2NhdG9yPSIxMTA6NDctMTEwOjU3IiBwYXRoPSJ2YWx1ZSIgeHNpOnR5cGU9IlByb3BlcnR5Ij4KICAgICAgICAgICAgPHNvdXJjZSBsb2NhdG9yPSIxMTA6NDctMTEwOjUxIiBuYW1lPSJ2YWx1ZSIgeHNpOnR5cGU9Ik9wZXJhbmRSZWYiLz4KICAgICAgICAgPC9leHByZXNzaW9uPgogICAgICAgICA8b3BlcmFuZCBuYW1lPSJ2YWx1ZSI+CiAgICAgICAgICAgIDxvcGVyYW5kVHlwZVNwZWNpZmllciBsb2NhdG9yPSIxMTA6MzItMTEwOjQzIiBuYW1lPSJmaGlyOkNvbnNlbnRTdGF0ZSIgeHNpOnR5cGU9Ik5hbWVkVHlwZVNwZWNpZmllciIvPgogICAgICAgICA8L29wZXJhbmQ+CiAgICAgIDwvZGVmPgogICAgICA8ZGVmIGxvY2F0b3I9IjExMToxLTExMTo2MyIgbmFtZT0iVG9TdHJpbmciIGNvbnRleHQ9IlBhdGllbnQiIGFjY2Vzc0xldmVsPSJQdWJsaWMiIHhzaTp0eXBlPSJGdW5jdGlvbkRlZiI+CiAgICAgICAgIDxleHByZXNzaW9uIGxvY2F0b3I9IjExMTo1My0xMTE6NjMiIHBhdGg9InZhbHVlIiB4c2k6dHlwZT0iUHJvcGVydHkiPgogICAgICAgICAgICA8c291cmNlIGxvY2F0b3I9IjExMTo1My0xMTE6NTciIG5hbWU9InZhbHVlIiB4c2k6dHlwZT0iT3BlcmFuZFJlZiIvPgogICAgICAgICA8L2V4cHJlc3Npb24+CiAgICAgICAgIDxvcGVyYW5kIG5hbWU9InZhbHVlIj4KICAgICAgICAgICAgPG9wZXJhbmRUeXBlU3BlY2lmaWVyIGxvY2F0b3I9IjExMTozMi0xMTE6NDkiIG5hbWU9ImZoaXI6Q29uc3RyYWludFNldmVyaXR5IiB4c2k6dHlwZT0iTmFtZWRUeXBlU3BlY2lmaWVyIi8+CiAgICAgICAgIDwvb3BlcmFuZD4KICAgICAgPC9kZWY+CiAgICAgIDxkZWYgbG9jYXRvcj0iMTEyOjEtMTEyOjYzIiBuYW1lPSJUb1N0cmluZyIgY29udGV4dD0iUGF0aWVudCIgYWNjZXNzTGV2ZWw9IlB1YmxpYyIgeHNpOnR5cGU9IkZ1bmN0aW9uRGVmIj4KICAgICAgICAgPGV4cHJlc3Npb24gbG9jYXRvcj0iMTEyOjUzLTExMjo2MyIgcGF0aD0idmFsdWUiIHhzaTp0eXBlPSJQcm9wZXJ0eSI+CiAgICAgICAgICAgIDxzb3VyY2UgbG9jYXRvcj0iMTEyOjUzLTExMjo1NyIgbmFtZT0idmFsdWUiIHhzaTp0eXBlPSJPcGVyYW5kUmVmIi8+CiAgICAgICAgIDwvZXhwcmVzc2lvbj4KICAgICAgICAgPG9wZXJhbmQgbmFtZT0idmFsdWUiPgogICAgICAgICAgICA8b3BlcmFuZFR5cGVTcGVjaWZpZXIgbG9jYXRvcj0iMTEyOjMyLTExMjo0OSIgbmFtZT0iZmhpcjpDb250YWN0UG9pbnRTeXN0ZW0iIHhzaTp0eXBlPSJOYW1lZFR5cGVTcGVjaWZpZXIiLz4KICAgICAgICAgPC9vcGVyYW5kPgogICAgICA8L2RlZj4KICAgICAgPGRlZiBsb2NhdG9yPSIxMTM6MS0xMTM6NjAiIG5hbWU9IlRvU3RyaW5nIiBjb250ZXh0PSJQYXRpZW50IiBhY2Nlc3NMZXZlbD0iUHVibGljIiB4c2k6dHlwZT0iRnVuY3Rpb25EZWYiPgogICAgICAgICA8ZXhwcmVzc2lvbiBsb2NhdG9yPSIxMTM6NTAtMTEzOjYwIiBwYXRoPSJ2YWx1ZSIgeHNpOnR5cGU9IlByb3BlcnR5Ij4KICAgICAgICAgICAgPHNvdXJjZSBsb2NhdG9yPSIxMTM6NTAtMTEzOjU0IiBuYW1lPSJ2YWx1ZSIgeHNpOnR5cGU9Ik9wZXJhbmRSZWYiLz4KICAgICAgICAgPC9leHByZXNzaW9uPgogICAgICAgICA8b3BlcmFuZCBuYW1lPSJ2YWx1ZSI+CiAgICAgICAgICAgIDxvcGVyYW5kVHlwZVNwZWNpZmllciBsb2NhdG9yPSIxMTM6MzItMTEzOjQ2IiBuYW1lPSJmaGlyOkNvbnRhY3RQb2ludFVzZSIgeHNpOnR5cGU9Ik5hbWVkVHlwZVNwZWNpZmllciIvPgogICAgICAgICA8L29wZXJhbmQ+CiAgICAgIDwvZGVmPgogICAgICA8ZGVmIGxvY2F0b3I9IjExNDoxLTExNDo3MCIgbmFtZT0iVG9TdHJpbmciIGNvbnRleHQ9IlBhdGllbnQiIGFjY2Vzc0xldmVsPSJQdWJsaWMiIHhzaTp0eXBlPSJGdW5jdGlvbkRlZiI+CiAgICAgICAgIDxleHByZXNzaW9uIGxvY2F0b3I9IjExNDo2MC0xMTQ6NzAiIHBhdGg9InZhbHVlIiB4c2k6dHlwZT0iUHJvcGVydHkiPgogICAgICAgICAgICA8c291cmNlIGxvY2F0b3I9IjExNDo2MC0xMTQ6NjQiIG5hbWU9InZhbHVlIiB4c2k6dHlwZT0iT3BlcmFuZFJlZiIvPgogICAgICAgICA8L2V4cHJlc3Npb24+CiAgICAgICAgIDxvcGVyYW5kIG5hbWU9InZhbHVlIj4KICAgICAgICAgICAgPG9wZXJhbmRUeXBlU3BlY2lmaWVyIGxvY2F0b3I9IjExNDozMi0xMTQ6NTYiIG5hbWU9ImZoaXI6Q29udHJhY3RQdWJsaWNhdGlvblN0YXR1cyIgeHNpOnR5cGU9Ik5hbWVkVHlwZVNwZWNpZmllciIvPgogICAgICAgICA8L29wZXJhbmQ+CiAgICAgIDwvZGVmPgogICAgICA8ZGVmIGxvY2F0b3I9IjExNToxLTExNTo1OSIgbmFtZT0iVG9TdHJpbmciIGNvbnRleHQ9IlBhdGllbnQiIGFjY2Vzc0xldmVsPSJQdWJsaWMiIHhzaTp0eXBlPSJGdW5jdGlvbkRlZiI+CiAgICAgICAgIDxleHByZXNzaW9uIGxvY2F0b3I9IjExNTo0OS0xMTU6NTkiIHBhdGg9InZhbHVlIiB4c2k6dHlwZT0iUHJvcGVydHkiPgogICAgICAgICAgICA8c291cmNlIGxvY2F0b3I9IjExNTo0OS0xMTU6NTMiIG5hbWU9InZhbHVlIiB4c2k6dHlwZT0iT3BlcmFuZFJlZiIvPgogICAgICAgICA8L2V4cHJlc3Npb24+CiAgICAgICAgIDxvcGVyYW5kIG5hbWU9InZhbHVlIj4KICAgICAgICAgICAgPG9wZXJhbmRUeXBlU3BlY2lmaWVyIGxvY2F0b3I9IjExNTozMi0xMTU6NDUiIG5hbWU9ImZoaXI6Q29udHJhY3RTdGF0dXMiIHhzaTp0eXBlPSJOYW1lZFR5cGVTcGVjaWZpZXIiLz4KICAgICAgICAgPC9vcGVyYW5kPgogICAgICA8L2RlZj4KICAgICAgPGRlZiBsb2NhdG9yPSIxMTY6MS0xMTY6NjAiIG5hbWU9IlRvU3RyaW5nIiBjb250ZXh0PSJQYXRpZW50IiBhY2Nlc3NMZXZlbD0iUHVibGljIiB4c2k6dHlwZT0iRnVuY3Rpb25EZWYiPgogICAgICAgICA8ZXhwcmVzc2lvbiBsb2NhdG9yPSIxMTY6NTAtMTE2OjYwIiBwYXRoPSJ2YWx1ZSIgeHNpOnR5cGU9IlByb3BlcnR5Ij4KICAgICAgICAgICAgPHNvdXJjZSBsb2NhdG9yPSIxMTY6NTAtMTE2OjU0IiBuYW1lPSJ2YWx1ZSIgeHNpOnR5cGU9Ik9wZXJhbmRSZWYiLz4KICAgICAgICAgPC9leHByZXNzaW9uPgogICAgICAgICA8b3BlcmFuZCBuYW1lPSJ2YWx1ZSI+CiAgICAgICAgICAgIDxvcGVyYW5kVHlwZVNwZWNpZmllciBsb2NhdG9yPSIxMTY6MzItMTE2OjQ2IiBuYW1lPSJmaGlyOkNvbnRyaWJ1dG9yVHlwZSIgeHNpOnR5cGU9Ik5hbWVkVHlwZVNwZWNpZmllciIvPgogICAgICAgICA8L29wZXJhbmQ+CiAgICAgIDwvZGVmPgogICAgICA8ZGVmIGxvY2F0b3I9IjExNzoxLTExNzo1OSIgbmFtZT0iVG9TdHJpbmciIGNvbnRleHQ9IlBhdGllbnQiIGFjY2Vzc0xldmVsPSJQdWJsaWMiIHhzaTp0eXBlPSJGdW5jdGlvbkRlZiI+CiAgICAgICAgIDxleHByZXNzaW9uIGxvY2F0b3I9IjExNzo0OS0xMTc6NTkiIHBhdGg9InZhbHVlIiB4c2k6dHlwZT0iUHJvcGVydHkiPgogICAgICAgICAgICA8c291cmNlIGxvY2F0b3I9IjExNzo0OS0xMTc6NTMiIG5hbWU9InZhbHVlIiB4c2k6dHlwZT0iT3BlcmFuZFJlZiIvPgogICAgICAgICA8L2V4cHJlc3Npb24+CiAgICAgICAgIDxvcGVyYW5kIG5hbWU9InZhbHVlIj4KICAgICAgICAgICAgPG9wZXJhbmRUeXBlU3BlY2lmaWVyIGxvY2F0b3I9IjExNzozMi0xMTc6NDUiIG5hbWU9ImZoaXI6Q292ZXJhZ2VTdGF0dXMiIHhzaTp0eXBlPSJOYW1lZFR5cGVTcGVjaWZpZXIiLz4KICAgICAgICAgPC9vcGVyYW5kPgogICAgICA8L2RlZj4KICAgICAgPGRlZiBsb2NhdG9yPSIxMTg6MS0xMTg6NTciIG5hbWU9IlRvU3RyaW5nIiBjb250ZXh0PSJQYXRpZW50IiBhY2Nlc3NMZXZlbD0iUHVibGljIiB4c2k6dHlwZT0iRnVuY3Rpb25EZWYiPgogICAgICAgICA8ZXhwcmVzc2lvbiBsb2NhdG9yPSIxMTg6NDctMTE4OjU3IiBwYXRoPSJ2YWx1ZSIgeHNpOnR5cGU9IlByb3BlcnR5Ij4KICAgICAgICAgICAgPHNvdXJjZSBsb2NhdG9yPSIxMTg6NDctMTE4OjUxIiBuYW1lPSJ2YWx1ZSIgeHNpOnR5cGU9Ik9wZXJhbmRSZWYiLz4KICAgICAgICAgPC9leHByZXNzaW9uPgogICAgICAgICA8b3BlcmFuZCBuYW1lPSJ2YWx1ZSI+CiAgICAgICAgICAgIDxvcGVyYW5kVHlwZVNwZWNpZmllciBsb2NhdG9yPSIxMTg6MzItMTE4OjQzIiBuYW1lPSJmaGlyOkN1cnJlbmN5Q29kZSIgeHNpOnR5cGU9Ik5hbWVkVHlwZVNwZWNpZmllciIvPgogICAgICAgICA8L29wZXJhbmQ+CiAgICAgIDwvZGVmPgogICAgICA8ZGVmIGxvY2F0b3I9IjExOToxLTExOTo1NCIgbmFtZT0iVG9TdHJpbmciIGNvbnRleHQ9IlBhdGllbnQiIGFjY2Vzc0xldmVsPSJQdWJsaWMiIHhzaTp0eXBlPSJGdW5jdGlvbkRlZiI+CiAgICAgICAgIDxleHByZXNzaW9uIGxvY2F0b3I9IjExOTo0NC0xMTk6NTQiIHBhdGg9InZhbHVlIiB4c2k6dHlwZT0iUHJvcGVydHkiPgogICAgICAgICAgICA8c291cmNlIGxvY2F0b3I9IjExOTo0NC0xMTk6NDgiIG5hbWU9InZhbHVlIiB4c2k6dHlwZT0iT3BlcmFuZFJlZiIvPgogICAgICAgICA8L2V4cHJlc3Npb24+CiAgICAgICAgIDxvcGVyYW5kIG5hbWU9InZhbHVlIj4KICAgICAgICAgICAgPG9wZXJhbmRUeXBlU3BlY2lmaWVyIGxvY2F0b3I9IjExOTozMi0xMTk6NDAiIG5hbWU9ImZoaXI6RGF5T2ZXZWVrIiB4c2k6dHlwZT0iTmFtZWRUeXBlU3BlY2lmaWVyIi8+CiAgICAgICAgIDwvb3BlcmFuZD4KICAgICAgPC9kZWY+CiAgICAgIDxkZWYgbG9jYXRvcj0iMTIwOjEtMTIwOjU1IiBuYW1lPSJUb1N0cmluZyIgY29udGV4dD0iUGF0aWVudCIgYWNjZXNzTGV2ZWw9IlB1YmxpYyIgeHNpOnR5cGU9IkZ1bmN0aW9uRGVmIj4KICAgICAgICAgPGV4cHJlc3Npb24gbG9jYXRvcj0iMTIwOjQ1LTEyMDo1NSIgcGF0aD0idmFsdWUiIHhzaTp0eXBlPSJQcm9wZXJ0eSI+CiAgICAgICAgICAgIDxzb3VyY2UgbG9jYXRvcj0iMTIwOjQ1LTEyMDo0OSIgbmFtZT0idmFsdWUiIHhzaTp0eXBlPSJPcGVyYW5kUmVmIi8+CiAgICAgICAgIDwvZXhwcmVzc2lvbj4KICAgICAgICAgPG9wZXJhbmQgbmFtZT0idmFsdWUiPgogICAgICAgICAgICA8b3BlcmFuZFR5cGVTcGVjaWZpZXIgbG9jYXRvcj0iMTIwOjMyLTEyMDo0MSIgbmFtZT0iZmhpcjpEYXlzT2ZXZWVrIiB4c2k6dHlwZT0iTmFtZWRUeXBlU3BlY2lmaWVyIi8+CiAgICAgICAgIDwvb3BlcmFuZD4KICAgICAgPC9kZWY+CiAgICAgIDxkZWYgbG9jYXRvcj0iMTIxOjEtMTIxOjY2IiBuYW1lPSJUb1N0cmluZyIgY29udGV4dD0iUGF0aWVudCIgYWNjZXNzTGV2ZWw9IlB1YmxpYyIgeHNpOnR5cGU9IkZ1bmN0aW9uRGVmIj4KICAgICAgICAgPGV4cHJlc3Npb24gbG9jYXRvcj0iMTIxOjU2LTEyMTo2NiIgcGF0aD0idmFsdWUiIHhzaTp0eXBlPSJQcm9wZXJ0eSI+CiAgICAgICAgICAgIDxzb3VyY2UgbG9jYXRvcj0iMTIxOjU2LTEyMTo2MCIgbmFtZT0idmFsdWUiIHhzaTp0eXBlPSJPcGVyYW5kUmVmIi8+CiAgICAgICAgIDwvZXhwcmVzc2lvbj4KICAgICAgICAgPG9wZXJhbmQgbmFtZT0idmFsdWUiPgogICAgICAgICAgICA8b3BlcmFuZFR5cGVTcGVjaWZpZXIgbG9jYXRvcj0iMTIxOjMyLTEyMTo1MiIgbmFtZT0iZmhpcjpEZXRlY3RlZElzc3VlU2V2ZXJpdHkiIHhzaTp0eXBlPSJOYW1lZFR5cGVTcGVjaWZpZXIiLz4KICAgICAgICAgPC9vcGVyYW5kPgogICAgICA8L2RlZj4KICAgICAgPGRlZiBsb2NhdG9yPSIxMjI6MS0xMjI6NjQiIG5hbWU9IlRvU3RyaW5nIiBjb250ZXh0PSJQYXRpZW50IiBhY2Nlc3NMZXZlbD0iUHVibGljIiB4c2k6dHlwZT0iRnVuY3Rpb25EZWYiPgogICAgICAgICA8ZXhwcmVzc2lvbiBsb2NhdG9yPSIxMjI6NTQtMTIyOjY0IiBwYXRoPSJ2YWx1ZSIgeHNpOnR5cGU9IlByb3BlcnR5Ij4KICAgICAgICAgICAgPHNvdXJjZSBsb2NhdG9yPSIxMjI6NTQtMTIyOjU4IiBuYW1lPSJ2YWx1ZSIgeHNpOnR5cGU9Ik9wZXJhbmRSZWYiLz4KICAgICAgICAgPC9leHByZXNzaW9uPgogICAgICAgICA8b3BlcmFuZCBuYW1lPSJ2YWx1ZSI+CiAgICAgICAgICAgIDxvcGVyYW5kVHlwZVNwZWNpZmllciBsb2NhdG9yPSIxMjI6MzItMTIyOjUwIiBuYW1lPSJmaGlyOkRldGVjdGVkSXNzdWVTdGF0dXMiIHhzaTp0eXBlPSJOYW1lZFR5cGVTcGVjaWZpZXIiLz4KICAgICAgICAgPC9vcGVyYW5kPgogICAgICA8L2RlZj4KICAgICAgPGRlZiBsb2NhdG9yPSIxMjM6MS0xMjM6NzMiIG5hbWU9IlRvU3RyaW5nIiBjb250ZXh0PSJQYXRpZW50IiBhY2Nlc3NMZXZlbD0iUHVibGljIiB4c2k6dHlwZT0iRnVuY3Rpb25EZWYiPgogICAgICAgICA8ZXhwcmVzc2lvbiBsb2NhdG9yPSIxMjM6NjMtMTIzOjczIiBwYXRoPSJ2YWx1ZSIgeHNpOnR5cGU9IlByb3BlcnR5Ij4KICAgICAgICAgICAgPHNvdXJjZSBsb2NhdG9yPSIxMjM6NjMtMTIzOjY3IiBuYW1lPSJ2YWx1ZSIgeHNpOnR5cGU9Ik9wZXJhbmRSZWYiLz4KICAgICAgICAgPC9leHByZXNzaW9uPgogICAgICAgICA8b3BlcmFuZCBuYW1lPSJ2YWx1ZSI+CiAgICAgICAgICAgIDxvcGVyYW5kVHlwZVNwZWNpZmllciBsb2NhdG9yPSIxMjM6MzItMTIzOjU5IiBuYW1lPSJmaGlyOkRldmljZU1ldHJpY0NhbGlicmF0aW9uU3RhdGUiIHhzaTp0eXBlPSJOYW1lZFR5cGVTcGVjaWZpZXIiLz4KICAgICAgICAgPC9vcGVyYW5kPgogICAgICA8L2RlZj4KICAgICAgPGRlZiBsb2NhdG9yPSIxMjQ6MS0xMjQ6NzIiIG5hbWU9IlRvU3RyaW5nIiBjb250ZXh0PSJQYXRpZW50IiBhY2Nlc3NMZXZlbD0iUHVibGljIiB4c2k6dHlwZT0iRnVuY3Rpb25EZWYiPgogICAgICAgICA8ZXhwcmVzc2lvbiBsb2NhdG9yPSIxMjQ6NjItMTI0OjcyIiBwYXRoPSJ2YWx1ZSIgeHNpOnR5cGU9IlByb3BlcnR5Ij4KICAgICAgICAgICAgPHNvdXJjZSBsb2NhdG9yPSIxMjQ6NjItMTI0OjY2IiBuYW1lPSJ2YWx1ZSIgeHNpOnR5cGU9Ik9wZXJhbmRSZWYiLz4KICAgICAgICAgPC9leHByZXNzaW9uPgogICAgICAgICA8b3BlcmFuZCBuYW1lPSJ2YWx1ZSI+CiAgICAgICAgICAgIDxvcGVyYW5kVHlwZVNwZWNpZmllciBsb2NhdG9yPSIxMjQ6MzItMTI0OjU4IiBuYW1lPSJmaGlyOkRldmljZU1ldHJpY0NhbGlicmF0aW9uVHlwZSIgeHNpOnR5cGU9Ik5hbWVkVHlwZVNwZWNpZmllciIvPgogICAgICAgICA8L29wZXJhbmQ+CiAgICAgIDwvZGVmPgogICAgICA8ZGVmIGxvY2F0b3I9IjEyNToxLTEyNTo2NSIgbmFtZT0iVG9TdHJpbmciIGNvbnRleHQ9IlBhdGllbnQiIGFjY2Vzc0xldmVsPSJQdWJsaWMiIHhzaTp0eXBlPSJGdW5jdGlvbkRlZiI+CiAgICAgICAgIDxleHByZXNzaW9uIGxvY2F0b3I9IjEyNTo1NS0xMjU6NjUiIHBhdGg9InZhbHVlIiB4c2k6dHlwZT0iUHJvcGVydHkiPgogICAgICAgICAgICA8c291cmNlIGxvY2F0b3I9IjEyNTo1NS0xMjU6NTkiIG5hbWU9InZhbHVlIiB4c2k6dHlwZT0iT3BlcmFuZFJlZiIvPgogICAgICAgICA8L2V4cHJlc3Npb24+CiAgICAgICAgIDxvcGVyYW5kIG5hbWU9InZhbHVlIj4KICAgICAgICAgICAgPG9wZXJhbmRUeXBlU3BlY2lmaWVyIGxvY2F0b3I9IjEyNTozMi0xMjU6NTEiIG5hbWU9ImZoaXI6RGV2aWNlTWV0cmljQ2F0ZWdvcnkiIHhzaTp0eXBlPSJOYW1lZFR5cGVTcGVjaWZpZXIiLz4KICAgICAgICAgPC9vcGVyYW5kPgogICAgICA8L2RlZj4KICAgICAgPGRlZiBsb2NhdG9yPSIxMjY6MS0xMjY6NjIiIG5hbWU9IlRvU3RyaW5nIiBjb250ZXh0PSJQYXRpZW50IiBhY2Nlc3NMZXZlbD0iUHVibGljIiB4c2k6dHlwZT0iRnVuY3Rpb25EZWYiPgogICAgICAgICA8ZXhwcmVzc2lvbiBsb2NhdG9yPSIxMjY6NTItMTI2OjYyIiBwYXRoPSJ2YWx1ZSIgeHNpOnR5cGU9IlByb3BlcnR5Ij4KICAgICAgICAgICAgPHNvdXJjZSBsb2NhdG9yPSIxMjY6NTItMTI2OjU2IiBuYW1lPSJ2YWx1ZSIgeHNpOnR5cGU9Ik9wZXJhbmRSZWYiLz4KICAgICAgICAgPC9leHByZXNzaW9uPgogICAgICAgICA8b3BlcmFuZCBuYW1lPSJ2YWx1ZSI+CiAgICAgICAgICAgIDxvcGVyYW5kVHlwZVNwZWNpZmllciBsb2NhdG9yPSIxMjY6MzItMTI2OjQ4IiBuYW1lPSJmaGlyOkRldmljZU1ldHJpY0NvbG9yIiB4c2k6dHlwZT0iTmFtZWRUeXBlU3BlY2lmaWVyIi8+CiAgICAgICAgIDwvb3BlcmFuZD4KICAgICAgPC9kZWY+CiAgICAgIDxkZWYgbG9jYXRvcj0iMTI3OjEtMTI3Ojc0IiBuYW1lPSJUb1N0cmluZyIgY29udGV4dD0iUGF0aWVudCIgYWNjZXNzTGV2ZWw9IlB1YmxpYyIgeHNpOnR5cGU9IkZ1bmN0aW9uRGVmIj4KICAgICAgICAgPGV4cHJlc3Npb24gbG9jYXRvcj0iMTI3OjY0LTEyNzo3NCIgcGF0aD0idmFsdWUiIHhzaTp0eXBlPSJQcm9wZXJ0eSI+CiAgICAgICAgICAgIDxzb3VyY2UgbG9jYXRvcj0iMTI3OjY0LTEyNzo2OCIgbmFtZT0idmFsdWUiIHhzaTp0eXBlPSJPcGVyYW5kUmVmIi8+CiAgICAgICAgIDwvZXhwcmVzc2lvbj4KICAgICAgICAgPG9wZXJhbmQgbmFtZT0idmFsdWUiPgogICAgICAgICAgICA8b3BlcmFuZFR5cGVTcGVjaWZpZXIgbG9jYXRvcj0iMTI3OjMyLTEyNzo2MCIgbmFtZT0iZmhpcjpEZXZpY2VNZXRyaWNPcGVyYXRpb25hbFN0YXR1cyIgeHNpOnR5cGU9Ik5hbWVkVHlwZVNwZWNpZmllciIvPgogICAgICAgICA8L29wZXJhbmQ+CiAgICAgIDwvZGVmPgogICAgICA8ZGVmIGxvY2F0b3I9IjEyODoxLTEyODo1OSIgbmFtZT0iVG9TdHJpbmciIGNvbnRleHQ9IlBhdGllbnQiIGFjY2Vzc0xldmVsPSJQdWJsaWMiIHhzaTp0eXBlPSJGdW5jdGlvbkRlZiI+CiAgICAgICAgIDxleHByZXNzaW9uIGxvY2F0b3I9IjEyODo0OS0xMjg6NTkiIHBhdGg9InZhbHVlIiB4c2k6dHlwZT0iUHJvcGVydHkiPgogICAgICAgICAgICA8c291cmNlIGxvY2F0b3I9IjEyODo0OS0xMjg6NTMiIG5hbWU9InZhbHVlIiB4c2k6dHlwZT0iT3BlcmFuZFJlZiIvPgogICAgICAgICA8L2V4cHJlc3Npb24+CiAgICAgICAgIDxvcGVyYW5kIG5hbWU9InZhbHVlIj4KICAgICAgICAgICAgPG9wZXJhbmRUeXBlU3BlY2lmaWVyIGxvY2F0b3I9IjEyODozMi0xMjg6NDUiIG5hbWU9ImZoaXI6RGV2aWNlTmFtZVR5cGUiIHhzaTp0eXBlPSJOYW1lZFR5cGVTcGVjaWZpZXIiLz4KICAgICAgICAgPC9vcGVyYW5kPgogICAgICA8L2RlZj4KICAgICAgPGRlZiBsb2NhdG9yPSIxMjk6MS0xMjk6NjQiIG5hbWU9IlRvU3RyaW5nIiBjb250ZXh0PSJQYXRpZW50IiBhY2Nlc3NMZXZlbD0iUHVibGljIiB4c2k6dHlwZT0iRnVuY3Rpb25EZWYiPgogICAgICAgICA8ZXhwcmVzc2lvbiBsb2NhdG9yPSIxMjk6NTQtMTI5OjY0IiBwYXRoPSJ2YWx1ZSIgeHNpOnR5cGU9IlByb3BlcnR5Ij4KICAgICAgICAgICAgPHNvdXJjZSBsb2NhdG9yPSIxMjk6NTQtMTI5OjU4IiBuYW1lPSJ2YWx1ZSIgeHNpOnR5cGU9Ik9wZXJhbmRSZWYiLz4KICAgICAgICAgPC9leHByZXNzaW9uPgogICAgICAgICA8b3BlcmFuZCBuYW1lPSJ2YWx1ZSI+CiAgICAgICAgICAgIDxvcGVyYW5kVHlwZVNwZWNpZmllciBsb2NhdG9yPSIxMjk6MzItMTI5OjUwIiBuYW1lPSJmaGlyOkRldmljZVJlcXVlc3RTdGF0dXMiIHhzaTp0eXBlPSJOYW1lZFR5cGVTcGVjaWZpZXIiLz4KICAgICAgICAgPC9vcGVyYW5kPgogICAgICA8L2RlZj4KICAgICAgPGRlZiBsb2NhdG9yPSIxMzA6MS0xMzA6NjkiIG5hbWU9IlRvU3RyaW5nIiBjb250ZXh0PSJQYXRpZW50IiBhY2Nlc3NMZXZlbD0iUHVibGljIiB4c2k6dHlwZT0iRnVuY3Rpb25EZWYiPgogICAgICAgICA8ZXhwcmVzc2lvbiBsb2NhdG9yPSIxMzA6NTktMTMwOjY5IiBwYXRoPSJ2YWx1ZSIgeHNpOnR5cGU9IlByb3BlcnR5Ij4KICAgICAgICAgICAgPHNvdXJjZSBsb2NhdG9yPSIxMzA6NTktMTMwOjYzIiBuYW1lPSJ2YWx1ZSIgeHNpOnR5cGU9Ik9wZXJhbmRSZWYiLz4KICAgICAgICAgPC9leHByZXNzaW9uPgogICAgICAgICA8b3BlcmFuZCBuYW1lPSJ2YWx1ZSI+CiAgICAgICAgICAgIDxvcGVyYW5kVHlwZVNwZWNpZmllciBsb2NhdG9yPSIxMzA6MzItMTMwOjU1IiBuYW1lPSJmaGlyOkRldmljZVVzZVN0YXRlbWVudFN0YXR1cyIgeHNpOnR5cGU9Ik5hbWVkVHlwZVNwZWNpZmllciIvPgogICAgICAgICA8L29wZXJhbmQ+CiAgICAgIDwvZGVmPgogICAgICA8ZGVmIGxvY2F0b3I9IjEzMToxLTEzMTo2NyIgbmFtZT0iVG9TdHJpbmciIGNvbnRleHQ9IlBhdGllbnQiIGFjY2Vzc0xldmVsPSJQdWJsaWMiIHhzaTp0eXBlPSJGdW5jdGlvbkRlZiI+CiAgICAgICAgIDxleHByZXNzaW9uIGxvY2F0b3I9IjEzMTo1Ny0xMzE6NjciIHBhdGg9InZhbHVlIiB4c2k6dHlwZT0iUHJvcGVydHkiPgogICAgICAgICAgICA8c291cmNlIGxvY2F0b3I9IjEzMTo1Ny0xMzE6NjEiIG5hbWU9InZhbHVlIiB4c2k6dHlwZT0iT3BlcmFuZFJlZiIvPgogICAgICAgICA8L2V4cHJlc3Npb24+CiAgICAgICAgIDxvcGVyYW5kIG5hbWU9InZhbHVlIj4KICAgICAgICAgICAgPG9wZXJhbmRUeXBlU3BlY2lmaWVyIGxvY2F0b3I9IjEzMTozMi0xMzE6NTMiIG5hbWU9ImZoaXI6RGlhZ25vc3RpY1JlcG9ydFN0YXR1cyIgeHNpOnR5cGU9Ik5hbWVkVHlwZVNwZWNpZmllciIvPgogICAgICAgICA8L29wZXJhbmQ+CiAgICAgIDwvZGVmPgogICAgICA8ZGVmIGxvY2F0b3I9IjEzMjoxLTEzMjo2MiIgbmFtZT0iVG9TdHJpbmciIGNvbnRleHQ9IlBhdGllbnQiIGFjY2Vzc0xldmVsPSJQdWJsaWMiIHhzaTp0eXBlPSJGdW5jdGlvbkRlZiI+CiAgICAgICAgIDxleHByZXNzaW9uIGxvY2F0b3I9IjEzMjo1Mi0xMzI6NjIiIHBhdGg9InZhbHVlIiB4c2k6dHlwZT0iUHJvcGVydHkiPgogICAgICAgICAgICA8c291cmNlIGxvY2F0b3I9IjEzMjo1Mi0xMzI6NTYiIG5hbWU9InZhbHVlIiB4c2k6dHlwZT0iT3BlcmFuZFJlZiIvPgogICAgICAgICA8L2V4cHJlc3Npb24+CiAgICAgICAgIDxvcGVyYW5kIG5hbWU9InZhbHVlIj4KICAgICAgICAgICAgPG9wZXJhbmRUeXBlU3BlY2lmaWVyIGxvY2F0b3I9IjEzMjozMi0xMzI6NDgiIG5hbWU9ImZoaXI6RGlzY3JpbWluYXRvclR5cGUiIHhzaTp0eXBlPSJOYW1lZFR5cGVTcGVjaWZpZXIiLz4KICAgICAgICAgPC9vcGVyYW5kPgogICAgICA8L2RlZj4KICAgICAgPGRlZiBsb2NhdG9yPSIxMzM6MS0xMzM6NjgiIG5hbWU9IlRvU3RyaW5nIiBjb250ZXh0PSJQYXRpZW50IiBhY2Nlc3NMZXZlbD0iUHVibGljIiB4c2k6dHlwZT0iRnVuY3Rpb25EZWYiPgogICAgICAgICA8ZXhwcmVzc2lvbiBsb2NhdG9yPSIxMzM6NTgtMTMzOjY4IiBwYXRoPSJ2YWx1ZSIgeHNpOnR5cGU9IlByb3BlcnR5Ij4KICAgICAgICAgICAgPHNvdXJjZSBsb2NhdG9yPSIxMzM6NTgtMTMzOjYyIiBuYW1lPSJ2YWx1ZSIgeHNpOnR5cGU9Ik9wZXJhbmRSZWYiLz4KICAgICAgICAgPC9leHByZXNzaW9uPgogICAgICAgICA8b3BlcmFuZCBuYW1lPSJ2YWx1ZSI+CiAgICAgICAgICAgIDxvcGVyYW5kVHlwZVNwZWNpZmllciBsb2NhdG9yPSIxMzM6MzItMTMzOjU0IiBuYW1lPSJmaGlyOkRvY3VtZW50Q29uZmlkZW50aWFsaXR5IiB4c2k6dHlwZT0iTmFtZWRUeXBlU3BlY2lmaWVyIi8+CiAgICAgICAgIDwvb3BlcmFuZD4KICAgICAgPC9kZWY+CiAgICAgIDxkZWYgbG9jYXRvcj0iMTM0OjEtMTM0OjU3IiBuYW1lPSJUb1N0cmluZyIgY29udGV4dD0iUGF0aWVudCIgYWNjZXNzTGV2ZWw9IlB1YmxpYyIgeHNpOnR5cGU9IkZ1bmN0aW9uRGVmIj4KICAgICAgICAgPGV4cHJlc3Npb24gbG9jYXRvcj0iMTM0OjQ3LTEzNDo1NyIgcGF0aD0idmFsdWUiIHhzaTp0eXBlPSJQcm9wZXJ0eSI+CiAgICAgICAgICAgIDxzb3VyY2UgbG9jYXRvcj0iMTM0OjQ3LTEzNDo1MSIgbmFtZT0idmFsdWUiIHhzaTp0eXBlPSJPcGVyYW5kUmVmIi8+CiAgICAgICAgIDwvZXhwcmVzc2lvbj4KICAgICAgICAgPG9wZXJhbmQgbmFtZT0idmFsdWUiPgogICAgICAgICAgICA8b3BlcmFuZFR5cGVTcGVjaWZpZXIgbG9jYXRvcj0iMTM0OjMyLTEzNDo0MyIgbmFtZT0iZmhpcjpEb2N1bWVudE1vZGUiIHhzaTp0eXBlPSJOYW1lZFR5cGVTcGVjaWZpZXIiLz4KICAgICAgICAgPC9vcGVyYW5kPgogICAgICA8L2RlZj4KICAgICAgPGRlZiBsb2NhdG9yPSIxMzU6MS0xMzU6NjgiIG5hbWU9IlRvU3RyaW5nIiBjb250ZXh0PSJQYXRpZW50IiBhY2Nlc3NMZXZlbD0iUHVibGljIiB4c2k6dHlwZT0iRnVuY3Rpb25EZWYiPgogICAgICAgICA8ZXhwcmVzc2lvbiBsb2NhdG9yPSIxMzU6NTgtMTM1OjY4IiBwYXRoPSJ2YWx1ZSIgeHNpOnR5cGU9IlByb3BlcnR5Ij4KICAgICAgICAgICAgPHNvdXJjZSBsb2NhdG9yPSIxMzU6NTgtMTM1OjYyIiBuYW1lPSJ2YWx1ZSIgeHNpOnR5cGU9Ik9wZXJhbmRSZWYiLz4KICAgICAgICAgPC9leHByZXNzaW9uPgogICAgICAgICA8b3BlcmFuZCBuYW1lPSJ2YWx1ZSI+CiAgICAgICAgICAgIDxvcGVyYW5kVHlwZVNwZWNpZmllciBsb2NhdG9yPSIxMzU6MzItMTM1OjU0IiBuYW1lPSJmaGlyOkRvY3VtZW50UmVmZXJlbmNlU3RhdHVzIiB4c2k6dHlwZT0iTmFtZWRUeXBlU3BlY2lmaWVyIi8+CiAgICAgICAgIDwvb3BlcmFuZD4KICAgICAgPC9kZWY+CiAgICAgIDxkZWYgbG9jYXRvcj0iMTM2OjEtMTM2OjY5IiBuYW1lPSJUb1N0cmluZyIgY29udGV4dD0iUGF0aWVudCIgYWNjZXNzTGV2ZWw9IlB1YmxpYyIgeHNpOnR5cGU9IkZ1bmN0aW9uRGVmIj4KICAgICAgICAgPGV4cHJlc3Npb24gbG9jYXRvcj0iMTM2OjU5LTEzNjo2OSIgcGF0aD0idmFsdWUiIHhzaTp0eXBlPSJQcm9wZXJ0eSI+CiAgICAgICAgICAgIDxzb3VyY2UgbG9jYXRvcj0iMTM2OjU5LTEzNjo2MyIgbmFtZT0idmFsdWUiIHhzaTp0eXBlPSJPcGVyYW5kUmVmIi8+CiAgICAgICAgIDwvZXhwcmVzc2lvbj4KICAgICAgICAgPG9wZXJhbmQgbmFtZT0idmFsdWUiPgogICAgICAgICAgICA8b3BlcmFuZFR5cGVTcGVjaWZpZXIgbG9jYXRvcj0iMTM2OjMyLTEzNjo1NSIgbmFtZT0iZmhpcjpEb2N1bWVudFJlbGF0aW9uc2hpcFR5cGUiIHhzaTp0eXBlPSJOYW1lZFR5cGVTcGVjaWZpZXIiLz4KICAgICAgICAgPC9vcGVyYW5kPgogICAgICA8L2RlZj4KICAgICAgPGRlZiBsb2NhdG9yPSIxMzc6MS0xMzc6NzAiIG5hbWU9IlRvU3RyaW5nIiBjb250ZXh0PSJQYXRpZW50IiBhY2Nlc3NMZXZlbD0iUHVibGljIiB4c2k6dHlwZT0iRnVuY3Rpb25EZWYiPgogICAgICAgICA8ZXhwcmVzc2lvbiBsb2NhdG9yPSIxMzc6NjAtMTM3OjcwIiBwYXRoPSJ2YWx1ZSIgeHNpOnR5cGU9IlByb3BlcnR5Ij4KICAgICAgICAgICAgPHNvdXJjZSBsb2NhdG9yPSIxMzc6NjAtMTM3OjY0IiBuYW1lPSJ2YWx1ZSIgeHNpOnR5cGU9Ik9wZXJhbmRSZWYiLz4KICAgICAgICAgPC9leHByZXNzaW9uPgogICAgICAgICA8b3BlcmFuZCBuYW1lPSJ2YWx1ZSI+CiAgICAgICAgICAgIDxvcGVyYW5kVHlwZVNwZWNpZmllciBsb2NhdG9yPSIxMzc6MzItMTM3OjU2IiBuYW1lPSJmaGlyOkVsaWdpYmlsaXR5UmVxdWVzdFB1cnBvc2UiIHhzaTp0eXBlPSJOYW1lZFR5cGVTcGVjaWZpZXIiLz4KICAgICAgICAgPC9vcGVyYW5kPgogICAgICA8L2RlZj4KICAgICAgPGRlZiBsb2NhdG9yPSIxMzg6MS0xMzg6NjkiIG5hbWU9IlRvU3RyaW5nIiBjb250ZXh0PSJQYXRpZW50IiBhY2Nlc3NMZXZlbD0iUHVibGljIiB4c2k6dHlwZT0iRnVuY3Rpb25EZWYiPgogICAgICAgICA8ZXhwcmVzc2lvbiBsb2NhdG9yPSIxMzg6NTktMTM4OjY5IiBwYXRoPSJ2YWx1ZSIgeHNpOnR5cGU9IlByb3BlcnR5Ij4KICAgICAgICAgICAgPHNvdXJjZSBsb2NhdG9yPSIxMzg6NTktMTM4OjYzIiBuYW1lPSJ2YWx1ZSIgeHNpOnR5cGU9Ik9wZXJhbmRSZWYiLz4KICAgICAgICAgPC9leHByZXNzaW9uPgogICAgICAgICA8b3BlcmFuZCBuYW1lPSJ2YWx1ZSI+CiAgICAgICAgICAgIDxvcGVyYW5kVHlwZVNwZWNpZmllciBsb2NhdG9yPSIxMzg6MzItMTM4OjU1IiBuYW1lPSJmaGlyOkVsaWdpYmlsaXR5UmVxdWVzdFN0YXR1cyIgeHNpOnR5cGU9Ik5hbWVkVHlwZVNwZWNpZmllciIvPgogICAgICAgICA8L29wZXJhbmQ+CiAgICAgIDwvZGVmPgogICAgICA8ZGVmIGxvY2F0b3I9IjEzOToxLTEzOTo3MSIgbmFtZT0iVG9TdHJpbmciIGNvbnRleHQ9IlBhdGllbnQiIGFjY2Vzc0xldmVsPSJQdWJsaWMiIHhzaTp0eXBlPSJGdW5jdGlvbkRlZiI+CiAgICAgICAgIDxleHByZXNzaW9uIGxvY2F0b3I9IjEzOTo2MS0xMzk6NzEiIHBhdGg9InZhbHVlIiB4c2k6dHlwZT0iUHJvcGVydHkiPgogICAgICAgICAgICA8c291cmNlIGxvY2F0b3I9IjEzOTo2MS0xMzk6NjUiIG5hbWU9InZhbHVlIiB4c2k6dHlwZT0iT3BlcmFuZFJlZiIvPgogICAgICAgICA8L2V4cHJlc3Npb24+CiAgICAgICAgIDxvcGVyYW5kIG5hbWU9InZhbHVlIj4KICAgICAgICAgICAgPG9wZXJhbmRUeXBlU3BlY2lmaWVyIGxvY2F0b3I9IjEzOTozMi0xMzk6NTciIG5hbWU9ImZoaXI6RWxpZ2liaWxpdHlSZXNwb25zZVB1cnBvc2UiIHhzaTp0eXBlPSJOYW1lZFR5cGVTcGVjaWZpZXIiLz4KICAgICAgICAgPC9vcGVyYW5kPgogICAgICA8L2RlZj4KICAgICAgPGRlZiBsb2NhdG9yPSIxNDA6MS0xNDA6NzAiIG5hbWU9IlRvU3RyaW5nIiBjb250ZXh0PSJQYXRpZW50IiBhY2Nlc3NMZXZlbD0iUHVibGljIiB4c2k6dHlwZT0iRnVuY3Rpb25EZWYiPgogICAgICAgICA8ZXhwcmVzc2lvbiBsb2NhdG9yPSIxNDA6NjAtMTQwOjcwIiBwYXRoPSJ2YWx1ZSIgeHNpOnR5cGU9IlByb3BlcnR5Ij4KICAgICAgICAgICAgPHNvdXJjZSBsb2NhdG9yPSIxNDA6NjAtMTQwOjY0IiBuYW1lPSJ2YWx1ZSIgeHNpOnR5cGU9Ik9wZXJhbmRSZWYiLz4KICAgICAgICAgPC9leHByZXNzaW9uPgogICAgICAgICA8b3BlcmFuZCBuYW1lPSJ2YWx1ZSI+CiAgICAgICAgICAgIDxvcGVyYW5kVHlwZVNwZWNpZmllciBsb2NhdG9yPSIxNDA6MzItMTQwOjU2IiBuYW1lPSJmaGlyOkVsaWdpYmlsaXR5UmVzcG9uc2VTdGF0dXMiIHhzaTp0eXBlPSJOYW1lZFR5cGVTcGVjaWZpZXIiLz4KICAgICAgICAgPC9vcGVyYW5kPgogICAgICA8L2RlZj4KICAgICAgPGRlZiBsb2NhdG9yPSIxNDE6MS0xNDE6NjMiIG5hbWU9IlRvU3RyaW5nIiBjb250ZXh0PSJQYXRpZW50IiBhY2Nlc3NMZXZlbD0iUHVibGljIiB4c2k6dHlwZT0iRnVuY3Rpb25EZWYiPgogICAgICAgICA8ZXhwcmVzc2lvbiBsb2NhdG9yPSIxNDE6NTMtMTQxOjYzIiBwYXRoPSJ2YWx1ZSIgeHNpOnR5cGU9IlByb3BlcnR5Ij4KICAgICAgICAgICAgPHNvdXJjZSBsb2NhdG9yPSIxNDE6NTMtMTQxOjU3IiBuYW1lPSJ2YWx1ZSIgeHNpOnR5cGU9Ik9wZXJhbmRSZWYiLz4KICAgICAgICAgPC9leHByZXNzaW9uPgogICAgICAgICA8b3BlcmFuZCBuYW1lPSJ2YWx1ZSI+CiAgICAgICAgICAgIDxvcGVyYW5kVHlwZVNwZWNpZmllciBsb2NhdG9yPSIxNDE6MzItMTQxOjQ5IiBuYW1lPSJmaGlyOkVuYWJsZVdoZW5CZWhhdmlvciIgeHNpOnR5cGU9Ik5hbWVkVHlwZVNwZWNpZmllciIvPgogICAgICAgICA8L29wZXJhbmQ+CiAgICAgIDwvZGVmPgogICAgICA8ZGVmIGxvY2F0b3I9IjE0MjoxLTE0Mjo2OCIgbmFtZT0iVG9TdHJpbmciIGNvbnRleHQ9IlBhdGllbnQiIGFjY2Vzc0xldmVsPSJQdWJsaWMiIHhzaTp0eXBlPSJGdW5jdGlvbkRlZiI+CiAgICAgICAgIDxleHByZXNzaW9uIGxvY2F0b3I9IjE0Mjo1OC0xNDI6NjgiIHBhdGg9InZhbHVlIiB4c2k6dHlwZT0iUHJvcGVydHkiPgogICAgICAgICAgICA8c291cmNlIGxvY2F0b3I9IjE0Mjo1OC0xNDI6NjIiIG5hbWU9InZhbHVlIiB4c2k6dHlwZT0iT3BlcmFuZFJlZiIvPgogICAgICAgICA8L2V4cHJlc3Npb24+CiAgICAgICAgIDxvcGVyYW5kIG5hbWU9InZhbHVlIj4KICAgICAgICAgICAgPG9wZXJhbmRUeXBlU3BlY2lmaWVyIGxvY2F0b3I9IjE0MjozMi0xNDI6NTQiIG5hbWU9ImZoaXI6RW5jb3VudGVyTG9jYXRpb25TdGF0dXMiIHhzaTp0eXBlPSJOYW1lZFR5cGVTcGVjaWZpZXIiLz4KICAgICAgICAgPC9vcGVyYW5kPgogICAgICA8L2RlZj4KICAgICAgPGRlZiBsb2NhdG9yPSIxNDM6MS0xNDM6NjAiIG5hbWU9IlRvU3RyaW5nIiBjb250ZXh0PSJQYXRpZW50IiBhY2Nlc3NMZXZlbD0iUHVibGljIiB4c2k6dHlwZT0iRnVuY3Rpb25EZWYiPgogICAgICAgICA8ZXhwcmVzc2lvbiBsb2NhdG9yPSIxNDM6NTAtMTQzOjYwIiBwYXRoPSJ2YWx1ZSIgeHNpOnR5cGU9IlByb3BlcnR5Ij4KICAgICAgICAgICAgPHNvdXJjZSBsb2NhdG9yPSIxNDM6NTAtMTQzOjU0IiBuYW1lPSJ2YWx1ZSIgeHNpOnR5cGU9Ik9wZXJhbmRSZWYiLz4KICAgICAgICAgPC9leHByZXNzaW9uPgogICAgICAgICA8b3BlcmFuZCBuYW1lPSJ2YWx1ZSI+CiAgICAgICAgICAgIDxvcGVyYW5kVHlwZVNwZWNpZmllciBsb2NhdG9yPSIxNDM6MzItMTQzOjQ2IiBuYW1lPSJmaGlyOkVuY291bnRlclN0YXR1cyIgeHNpOnR5cGU9Ik5hbWVkVHlwZVNwZWNpZmllciIvPgogICAgICAgICA8L29wZXJhbmQ+CiAgICAgIDwvZGVmPgogICAgICA8ZGVmIGxvY2F0b3I9IjE0NDoxLTE0NDo1OSIgbmFtZT0iVG9TdHJpbmciIGNvbnRleHQ9IlBhdGllbnQiIGFjY2Vzc0xldmVsPSJQdWJsaWMiIHhzaTp0eXBlPSJGdW5jdGlvbkRlZiI+CiAgICAgICAgIDxleHByZXNzaW9uIGxvY2F0b3I9IjE0NDo0OS0xNDQ6NTkiIHBhdGg9InZhbHVlIiB4c2k6dHlwZT0iUHJvcGVydHkiPgogICAgICAgICAgICA8c291cmNlIGxvY2F0b3I9IjE0NDo0OS0xNDQ6NTMiIG5hbWU9InZhbHVlIiB4c2k6dHlwZT0iT3BlcmFuZFJlZiIvPgogICAgICAgICA8L2V4cHJlc3Npb24+CiAgICAgICAgIDxvcGVyYW5kIG5hbWU9InZhbHVlIj4KICAgICAgICAgICAgPG9wZXJhbmRUeXBlU3BlY2lmaWVyIGxvY2F0b3I9IjE0NDozMi0xNDQ6NDUiIG5hbWU9ImZoaXI6RW5kcG9pbnRTdGF0dXMiIHhzaTp0eXBlPSJOYW1lZFR5cGVTcGVjaWZpZXIiLz4KICAgICAgICAgPC9vcGVyYW5kPgogICAgICA8L2RlZj4KICAgICAgPGRlZiBsb2NhdG9yPSIxNDU6MS0xNDU6NjgiIG5hbWU9IlRvU3RyaW5nIiBjb250ZXh0PSJQYXRpZW50IiBhY2Nlc3NMZXZlbD0iUHVibGljIiB4c2k6dHlwZT0iRnVuY3Rpb25EZWYiPgogICAgICAgICA8ZXhwcmVzc2lvbiBsb2NhdG9yPSIxNDU6NTgtMTQ1OjY4IiBwYXRoPSJ2YWx1ZSIgeHNpOnR5cGU9IlByb3BlcnR5Ij4KICAgICAgICAgICAgPHNvdXJjZSBsb2NhdG9yPSIxNDU6NTgtMTQ1OjYyIiBuYW1lPSJ2YWx1ZSIgeHNpOnR5cGU9Ik9wZXJhbmRSZWYiLz4KICAgICAgICAgPC9leHByZXNzaW9uPgogICAgICAgICA8b3BlcmFuZCBuYW1lPSJ2YWx1ZSI+CiAgICAgICAgICAgIDxvcGVyYW5kVHlwZVNwZWNpZmllciBsb2NhdG9yPSIxNDU6MzItMTQ1OjU0IiBuYW1lPSJmaGlyOkVucm9sbG1lbnRSZXF1ZXN0U3RhdHVzIiB4c2k6dHlwZT0iTmFtZWRUeXBlU3BlY2lmaWVyIi8+CiAgICAgICAgIDwvb3BlcmFuZD4KICAgICAgPC9kZWY+CiAgICAgIDxkZWYgbG9jYXRvcj0iMTQ2OjEtMTQ2OjY5IiBuYW1lPSJUb1N0cmluZyIgY29udGV4dD0iUGF0aWVudCIgYWNjZXNzTGV2ZWw9IlB1YmxpYyIgeHNpOnR5cGU9IkZ1bmN0aW9uRGVmIj4KICAgICAgICAgPGV4cHJlc3Npb24gbG9jYXRvcj0iMTQ2OjU5LTE0Njo2OSIgcGF0aD0idmFsdWUiIHhzaTp0eXBlPSJQcm9wZXJ0eSI+CiAgICAgICAgICAgIDxzb3VyY2UgbG9jYXRvcj0iMTQ2OjU5LTE0Njo2MyIgbmFtZT0idmFsdWUiIHhzaTp0eXBlPSJPcGVyYW5kUmVmIi8+CiAgICAgICAgIDwvZXhwcmVzc2lvbj4KICAgICAgICAgPG9wZXJhbmQgbmFtZT0idmFsdWUiPgogICAgICAgICAgICA8b3BlcmFuZFR5cGVTcGVjaWZpZXIgbG9jYXRvcj0iMTQ2OjMyLTE0Njo1NSIgbmFtZT0iZmhpcjpFbnJvbGxtZW50UmVzcG9uc2VTdGF0dXMiIHhzaTp0eXBlPSJOYW1lZFR5cGVTcGVjaWZpZXIiLz4KICAgICAgICAgPC9vcGVyYW5kPgogICAgICA8L2RlZj4KICAgICAgPGRlZiBsb2NhdG9yPSIxNDc6MS0xNDc6NjQiIG5hbWU9IlRvU3RyaW5nIiBjb250ZXh0PSJQYXRpZW50IiBhY2Nlc3NMZXZlbD0iUHVibGljIiB4c2k6dHlwZT0iRnVuY3Rpb25EZWYiPgogICAgICAgICA8ZXhwcmVzc2lvbiBsb2NhdG9yPSIxNDc6NTQtMTQ3OjY0IiBwYXRoPSJ2YWx1ZSIgeHNpOnR5cGU9IlByb3BlcnR5Ij4KICAgICAgICAgICAgPHNvdXJjZSBsb2NhdG9yPSIxNDc6NTQtMTQ3OjU4IiBuYW1lPSJ2YWx1ZSIgeHNpOnR5cGU9Ik9wZXJhbmRSZWYiLz4KICAgICAgICAgPC9leHByZXNzaW9uPgogICAgICAgICA8b3BlcmFuZCBuYW1lPSJ2YWx1ZSI+CiAgICAgICAgICAgIDxvcGVyYW5kVHlwZVNwZWNpZmllciBsb2NhdG9yPSIxNDc6MzItMTQ3OjUwIiBuYW1lPSJmaGlyOkVwaXNvZGVPZkNhcmVTdGF0dXMiIHhzaTp0eXBlPSJOYW1lZFR5cGVTcGVjaWZpZXIiLz4KICAgICAgICAgPC9vcGVyYW5kPgogICAgICA8L2RlZj4KICAgICAgPGRlZiBsb2NhdG9yPSIxNDg6MS0xNDg6NjQiIG5hbWU9IlRvU3RyaW5nIiBjb250ZXh0PSJQYXRpZW50IiBhY2Nlc3NMZXZlbD0iUHVibGljIiB4c2k6dHlwZT0iRnVuY3Rpb25EZWYiPgogICAgICAgICA8ZXhwcmVzc2lvbiBsb2NhdG9yPSIxNDg6NTQtMTQ4OjY0IiBwYXRoPSJ2YWx1ZSIgeHNpOnR5cGU9IlByb3BlcnR5Ij4KICAgICAgICAgICAgPHNvdXJjZSBsb2NhdG9yPSIxNDg6NTQtMTQ4OjU4IiBuYW1lPSJ2YWx1ZSIgeHNpOnR5cGU9Ik9wZXJhbmRSZWYiLz4KICAgICAgICAgPC9leHByZXNzaW9uPgogICAgICAgICA8b3BlcmFuZCBuYW1lPSJ2YWx1ZSI+CiAgICAgICAgICAgIDxvcGVyYW5kVHlwZVNwZWNpZmllciBsb2NhdG9yPSIxNDg6MzItMTQ4OjUwIiBuYW1lPSJmaGlyOkV2ZW50Q2FwYWJpbGl0eU1vZGUiIHhzaTp0eXBlPSJOYW1lZFR5cGVTcGVjaWZpZXIiLz4KICAgICAgICAgPC9vcGVyYW5kPgogICAgICA8L2RlZj4KICAgICAgPGRlZiBsb2NhdG9yPSIxNDk6MS0xNDk6NTYiIG5hbWU9IlRvU3RyaW5nIiBjb250ZXh0PSJQYXRpZW50IiBhY2Nlc3NMZXZlbD0iUHVibGljIiB4c2k6dHlwZT0iRnVuY3Rpb25EZWYiPgogICAgICAgICA8ZXhwcmVzc2lvbiBsb2NhdG9yPSIxNDk6NDYtMTQ5OjU2IiBwYXRoPSJ2YWx1ZSIgeHNpOnR5cGU9IlByb3BlcnR5Ij4KICAgICAgICAgICAgPHNvdXJjZSBsb2NhdG9yPSIxNDk6NDYtMTQ5OjUwIiBuYW1lPSJ2YWx1ZSIgeHNpOnR5cGU9Ik9wZXJhbmRSZWYiLz4KICAgICAgICAgPC9leHByZXNzaW9uPgogICAgICAgICA8b3BlcmFuZCBuYW1lPSJ2YWx1ZSI+CiAgICAgICAgICAgIDxvcGVyYW5kVHlwZVNwZWNpZmllciBsb2NhdG9yPSIxNDk6MzItMTQ5OjQyIiBuYW1lPSJmaGlyOkV2ZW50VGltaW5nIiB4c2k6dHlwZT0iTmFtZWRUeXBlU3BlY2lmaWVyIi8+CiAgICAgICAgIDwvb3BlcmFuZD4KICAgICAgPC9kZWY+CiAgICAgIDxkZWYgbG9jYXRvcj0iMTUwOjEtMTUwOjY1IiBuYW1lPSJUb1N0cmluZyIgY29udGV4dD0iUGF0aWVudCIgYWNjZXNzTGV2ZWw9IlB1YmxpYyIgeHNpOnR5cGU9IkZ1bmN0aW9uRGVmIj4KICAgICAgICAgPGV4cHJlc3Npb24gbG9jYXRvcj0iMTUwOjU1LTE1MDo2NSIgcGF0aD0idmFsdWUiIHhzaTp0eXBlPSJQcm9wZXJ0eSI+CiAgICAgICAgICAgIDxzb3VyY2UgbG9jYXRvcj0iMTUwOjU1LTE1MDo1OSIgbmFtZT0idmFsdWUiIHhzaTp0eXBlPSJPcGVyYW5kUmVmIi8+CiAgICAgICAgIDwvZXhwcmVzc2lvbj4KICAgICAgICAgPG9wZXJhbmQgbmFtZT0idmFsdWUiPgogICAgICAgICAgICA8b3BlcmFuZFR5cGVTcGVjaWZpZXIgbG9jYXRvcj0iMTUwOjMyLTE1MDo1MSIgbmFtZT0iZmhpcjpFdmlkZW5jZVZhcmlhYmxlVHlwZSIgeHNpOnR5cGU9Ik5hbWVkVHlwZVNwZWNpZmllciIvPgogICAgICAgICA8L29wZXJhbmQ+CiAgICAgIDwvZGVmPgogICAgICA8ZGVmIGxvY2F0b3I9IjE1MToxLTE1MTo2OSIgbmFtZT0iVG9TdHJpbmciIGNvbnRleHQ9IlBhdGllbnQiIGFjY2Vzc0xldmVsPSJQdWJsaWMiIHhzaTp0eXBlPSJGdW5jdGlvbkRlZiI+CiAgICAgICAgIDxleHByZXNzaW9uIGxvY2F0b3I9IjE1MTo1OS0xNTE6NjkiIHBhdGg9InZhbHVlIiB4c2k6dHlwZT0iUHJvcGVydHkiPgogICAgICAgICAgICA8c291cmNlIGxvY2F0b3I9IjE1MTo1OS0xNTE6NjMiIG5hbWU9InZhbHVlIiB4c2k6dHlwZT0iT3BlcmFuZFJlZiIvPgogICAgICAgICA8L2V4cHJlc3Npb24+CiAgICAgICAgIDxvcGVyYW5kIG5hbWU9InZhbHVlIj4KICAgICAgICAgICAgPG9wZXJhbmRUeXBlU3BlY2lmaWVyIGxvY2F0b3I9IjE1MTozMi0xNTE6NTUiIG5hbWU9ImZoaXI6RXhhbXBsZVNjZW5hcmlvQWN0b3JUeXBlIiB4c2k6dHlwZT0iTmFtZWRUeXBlU3BlY2lmaWVyIi8+CiAgICAgICAgIDwvb3BlcmFuZD4KICAgICAgPC9kZWY+CiAgICAgIDxkZWYgbG9jYXRvcj0iMTUyOjEtMTUyOjcxIiBuYW1lPSJUb1N0cmluZyIgY29udGV4dD0iUGF0aWVudCIgYWNjZXNzTGV2ZWw9IlB1YmxpYyIgeHNpOnR5cGU9IkZ1bmN0aW9uRGVmIj4KICAgICAgICAgPGV4cHJlc3Npb24gbG9jYXRvcj0iMTUyOjYxLTE1Mjo3MSIgcGF0aD0idmFsdWUiIHhzaTp0eXBlPSJQcm9wZXJ0eSI+CiAgICAgICAgICAgIDxzb3VyY2UgbG9jYXRvcj0iMTUyOjYxLTE1Mjo2NSIgbmFtZT0idmFsdWUiIHhzaTp0eXBlPSJPcGVyYW5kUmVmIi8+CiAgICAgICAgIDwvZXhwcmVzc2lvbj4KICAgICAgICAgPG9wZXJhbmQgbmFtZT0idmFsdWUiPgogICAgICAgICAgICA8b3BlcmFuZFR5cGVTcGVjaWZpZXIgbG9jYXRvcj0iMTUyOjMyLTE1Mjo1NyIgbmFtZT0iZmhpcjpFeHBsYW5hdGlvbk9mQmVuZWZpdFN0YXR1cyIgeHNpOnR5cGU9Ik5hbWVkVHlwZVNwZWNpZmllciIvPgogICAgICAgICA8L29wZXJhbmQ+CiAgICAgIDwvZGVmPgogICAgICA8ZGVmIGxvY2F0b3I9IjE1MzoxLTE1Mzo1OCIgbmFtZT0iVG9TdHJpbmciIGNvbnRleHQ9IlBhdGllbnQiIGFjY2Vzc0xldmVsPSJQdWJsaWMiIHhzaTp0eXBlPSJGdW5jdGlvbkRlZiI+CiAgICAgICAgIDxleHByZXNzaW9uIGxvY2F0b3I9IjE1Mzo0OC0xNTM6NTgiIHBhdGg9InZhbHVlIiB4c2k6dHlwZT0iUHJvcGVydHkiPgogICAgICAgICAgICA8c291cmNlIGxvY2F0b3I9IjE1Mzo0OC0xNTM6NTIiIG5hbWU9InZhbHVlIiB4c2k6dHlwZT0iT3BlcmFuZFJlZiIvPgogICAgICAgICA8L2V4cHJlc3Npb24+CiAgICAgICAgIDxvcGVyYW5kIG5hbWU9InZhbHVlIj4KICAgICAgICAgICAgPG9wZXJhbmRUeXBlU3BlY2lmaWVyIGxvY2F0b3I9IjE1MzozMi0xNTM6NDQiIG5hbWU9ImZoaXI6RXhwb3N1cmVTdGF0ZSIgeHNpOnR5cGU9Ik5hbWVkVHlwZVNwZWNpZmllciIvPgogICAgICAgICA8L29wZXJhbmQ+CiAgICAgIDwvZGVmPgogICAgICA8ZGVmIGxvY2F0b3I9IjE1NDoxLTE1NDo2NSIgbmFtZT0iVG9TdHJpbmciIGNvbnRleHQ9IlBhdGllbnQiIGFjY2Vzc0xldmVsPSJQdWJsaWMiIHhzaTp0eXBlPSJGdW5jdGlvbkRlZiI+CiAgICAgICAgIDxleHByZXNzaW9uIGxvY2F0b3I9IjE1NDo1NS0xNTQ6NjUiIHBhdGg9InZhbHVlIiB4c2k6dHlwZT0iUHJvcGVydHkiPgogICAgICAgICAgICA8c291cmNlIGxvY2F0b3I9IjE1NDo1NS0xNTQ6NTkiIG5hbWU9InZhbHVlIiB4c2k6dHlwZT0iT3BlcmFuZFJlZiIvPgogICAgICAgICA8L2V4cHJlc3Npb24+CiAgICAgICAgIDxvcGVyYW5kIG5hbWU9InZhbHVlIj4KICAgICAgICAgICAgPG9wZXJhbmRUeXBlU3BlY2lmaWVyIGxvY2F0b3I9IjE1NDozMi0xNTQ6NTEiIG5hbWU9ImZoaXI6RXh0ZW5zaW9uQ29udGV4dFR5cGUiIHhzaTp0eXBlPSJOYW1lZFR5cGVTcGVjaWZpZXIiLz4KICAgICAgICAgPC9vcGVyYW5kPgogICAgICA8L2RlZj4KICAgICAgPGRlZiBsb2NhdG9yPSIxNTU6MS0xNTU6NTciIG5hbWU9IlRvU3RyaW5nIiBjb250ZXh0PSJQYXRpZW50IiBhY2Nlc3NMZXZlbD0iUHVibGljIiB4c2k6dHlwZT0iRnVuY3Rpb25EZWYiPgogICAgICAgICA8ZXhwcmVzc2lvbiBsb2NhdG9yPSIxNTU6NDctMTU1OjU3IiBwYXRoPSJ2YWx1ZSIgeHNpOnR5cGU9IlByb3BlcnR5Ij4KICAgICAgICAgICAgPHNvdXJjZSBsb2NhdG9yPSIxNTU6NDctMTU1OjUxIiBuYW1lPSJ2YWx1ZSIgeHNpOnR5cGU9Ik9wZXJhbmRSZWYiLz4KICAgICAgICAgPC9leHByZXNzaW9uPgogICAgICAgICA8b3BlcmFuZCBuYW1lPSJ2YWx1ZSI+CiAgICAgICAgICAgIDxvcGVyYW5kVHlwZVNwZWNpZmllciBsb2NhdG9yPSIxNTU6MzItMTU1OjQzIiBuYW1lPSJmaGlyOkZISVJBbGxUeXBlcyIgeHNpOnR5cGU9Ik5hbWVkVHlwZVNwZWNpZmllciIvPgogICAgICAgICA8L29wZXJhbmQ+CiAgICAgIDwvZGVmPgogICAgICA8ZGVmIGxvY2F0b3I9IjE1NjoxLTE1Njo2MCIgbmFtZT0iVG9TdHJpbmciIGNvbnRleHQ9IlBhdGllbnQiIGFjY2Vzc0xldmVsPSJQdWJsaWMiIHhzaTp0eXBlPSJGdW5jdGlvbkRlZiI+CiAgICAgICAgIDxleHByZXNzaW9uIGxvY2F0b3I9IjE1Njo1MC0xNTY6NjAiIHBhdGg9InZhbHVlIiB4c2k6dHlwZT0iUHJvcGVydHkiPgogICAgICAgICAgICA8c291cmNlIGxvY2F0b3I9IjE1Njo1MC0xNTY6NTQiIG5hbWU9InZhbHVlIiB4c2k6dHlwZT0iT3BlcmFuZFJlZiIvPgogICAgICAgICA8L2V4cHJlc3Npb24+CiAgICAgICAgIDxvcGVyYW5kIG5hbWU9InZhbHVlIj4KICAgICAgICAgICAgPG9wZXJhbmRUeXBlU3BlY2lmaWVyIGxvY2F0b3I9IjE1NjozMi0xNTY6NDYiIG5hbWU9ImZoaXI6RkhJUkRlZmluZWRUeXBlIiB4c2k6dHlwZT0iTmFtZWRUeXBlU3BlY2lmaWVyIi8+CiAgICAgICAgIDwvb3BlcmFuZD4KICAgICAgPC9kZWY+CiAgICAgIDxkZWYgbG9jYXRvcj0iMTU3OjEtMTU3OjYxIiBuYW1lPSJUb1N0cmluZyIgY29udGV4dD0iUGF0aWVudCIgYWNjZXNzTGV2ZWw9IlB1YmxpYyIgeHNpOnR5cGU9IkZ1bmN0aW9uRGVmIj4KICAgICAgICAgPGV4cHJlc3Npb24gbG9jYXRvcj0iMTU3OjUxLTE1Nzo2MSIgcGF0aD0idmFsdWUiIHhzaTp0eXBlPSJQcm9wZXJ0eSI+CiAgICAgICAgICAgIDxzb3VyY2UgbG9jYXRvcj0iMTU3OjUxLTE1Nzo1NSIgbmFtZT0idmFsdWUiIHhzaTp0eXBlPSJPcGVyYW5kUmVmIi8+CiAgICAgICAgIDwvZXhwcmVzc2lvbj4KICAgICAgICAgPG9wZXJhbmQgbmFtZT0idmFsdWUiPgogICAgICAgICAgICA8b3BlcmFuZFR5cGVTcGVjaWZpZXIgbG9jYXRvcj0iMTU3OjMyLTE1Nzo0NyIgbmFtZT0iZmhpcjpGSElSRGV2aWNlU3RhdHVzIiB4c2k6dHlwZT0iTmFtZWRUeXBlU3BlY2lmaWVyIi8+CiAgICAgICAgIDwvb3BlcmFuZD4KICAgICAgPC9kZWY+CiAgICAgIDxkZWYgbG9jYXRvcj0iMTU4OjEtMTU4OjYxIiBuYW1lPSJUb1N0cmluZyIgY29udGV4dD0iUGF0aWVudCIgYWNjZXNzTGV2ZWw9IlB1YmxpYyIgeHNpOnR5cGU9IkZ1bmN0aW9uRGVmIj4KICAgICAgICAgPGV4cHJlc3Npb24gbG9jYXRvcj0iMTU4OjUxLTE1ODo2MSIgcGF0aD0idmFsdWUiIHhzaTp0eXBlPSJQcm9wZXJ0eSI+CiAgICAgICAgICAgIDxzb3VyY2UgbG9jYXRvcj0iMTU4OjUxLTE1ODo1NSIgbmFtZT0idmFsdWUiIHhzaTp0eXBlPSJPcGVyYW5kUmVmIi8+CiAgICAgICAgIDwvZXhwcmVzc2lvbj4KICAgICAgICAgPG9wZXJhbmQgbmFtZT0idmFsdWUiPgogICAgICAgICAgICA8b3BlcmFuZFR5cGVTcGVjaWZpZXIgbG9jYXRvcj0iMTU4OjMyLTE1ODo0NyIgbmFtZT0iZmhpcjpGSElSUmVzb3VyY2VUeXBlIiB4c2k6dHlwZT0iTmFtZWRUeXBlU3BlY2lmaWVyIi8+CiAgICAgICAgIDwvb3BlcmFuZD4KICAgICAgPC9kZWY+CiAgICAgIDxkZWYgbG9jYXRvcj0iMTU5OjEtMTU5OjY0IiBuYW1lPSJUb1N0cmluZyIgY29udGV4dD0iUGF0aWVudCIgYWNjZXNzTGV2ZWw9IlB1YmxpYyIgeHNpOnR5cGU9IkZ1bmN0aW9uRGVmIj4KICAgICAgICAgPGV4cHJlc3Npb24gbG9jYXRvcj0iMTU5OjU0LTE1OTo2NCIgcGF0aD0idmFsdWUiIHhzaTp0eXBlPSJQcm9wZXJ0eSI+CiAgICAgICAgICAgIDxzb3VyY2UgbG9jYXRvcj0iMTU5OjU0LTE1OTo1OCIgbmFtZT0idmFsdWUiIHhzaTp0eXBlPSJPcGVyYW5kUmVmIi8+CiAgICAgICAgIDwvZXhwcmVzc2lvbj4KICAgICAgICAgPG9wZXJhbmQgbmFtZT0idmFsdWUiPgogICAgICAgICAgICA8b3BlcmFuZFR5cGVTcGVjaWZpZXIgbG9jYXRvcj0iMTU5OjMyLTE1OTo1MCIgbmFtZT0iZmhpcjpGSElSU3Vic3RhbmNlU3RhdHVzIiB4c2k6dHlwZT0iTmFtZWRUeXBlU3BlY2lmaWVyIi8+CiAgICAgICAgIDwvb3BlcmFuZD4KICAgICAgPC9kZWY+CiAgICAgIDxkZWYgbG9jYXRvcj0iMTYwOjEtMTYwOjU2IiBuYW1lPSJUb1N0cmluZyIgY29udGV4dD0iUGF0aWVudCIgYWNjZXNzTGV2ZWw9IlB1YmxpYyIgeHNpOnR5cGU9IkZ1bmN0aW9uRGVmIj4KICAgICAgICAgPGV4cHJlc3Npb24gbG9jYXRvcj0iMTYwOjQ2LTE2MDo1NiIgcGF0aD0idmFsdWUiIHhzaTp0eXBlPSJQcm9wZXJ0eSI+CiAgICAgICAgICAgIDxzb3VyY2UgbG9jYXRvcj0iMTYwOjQ2LTE2MDo1MCIgbmFtZT0idmFsdWUiIHhzaTp0eXBlPSJPcGVyYW5kUmVmIi8+CiAgICAgICAgIDwvZXhwcmVzc2lvbj4KICAgICAgICAgPG9wZXJhbmQgbmFtZT0idmFsdWUiPgogICAgICAgICAgICA8b3BlcmFuZFR5cGVTcGVjaWZpZXIgbG9jYXRvcj0iMTYwOjMyLTE2MDo0MiIgbmFtZT0iZmhpcjpGSElSVmVyc2lvbiIgeHNpOnR5cGU9Ik5hbWVkVHlwZVNwZWNpZmllciIvPgogICAgICAgICA8L29wZXJhbmQ+CiAgICAgIDwvZGVmPgogICAgICA8ZGVmIGxvY2F0b3I9IjE2MToxLTE2MTo2NCIgbmFtZT0iVG9TdHJpbmciIGNvbnRleHQ9IlBhdGllbnQiIGFjY2Vzc0xldmVsPSJQdWJsaWMiIHhzaTp0eXBlPSJGdW5jdGlvbkRlZiI+CiAgICAgICAgIDxleHByZXNzaW9uIGxvY2F0b3I9IjE2MTo1NC0xNjE6NjQiIHBhdGg9InZhbHVlIiB4c2k6dHlwZT0iUHJvcGVydHkiPgogICAgICAgICAgICA8c291cmNlIGxvY2F0b3I9IjE2MTo1NC0xNjE6NTgiIG5hbWU9InZhbHVlIiB4c2k6dHlwZT0iT3BlcmFuZFJlZiIvPgogICAgICAgICA8L2V4cHJlc3Npb24+CiAgICAgICAgIDxvcGVyYW5kIG5hbWU9InZhbHVlIj4KICAgICAgICAgICAgPG9wZXJhbmRUeXBlU3BlY2lmaWVyIGxvY2F0b3I9IjE2MTozMi0xNjE6NTAiIG5hbWU9ImZoaXI6RmFtaWx5SGlzdG9yeVN0YXR1cyIgeHNpOnR5cGU9Ik5hbWVkVHlwZVNwZWNpZmllciIvPgogICAgICAgICA8L29wZXJhbmQ+CiAgICAgIDwvZGVmPgogICAgICA8ZGVmIGxvY2F0b3I9IjE2MjoxLTE2Mjo1OSIgbmFtZT0iVG9TdHJpbmciIGNvbnRleHQ9IlBhdGllbnQiIGFjY2Vzc0xldmVsPSJQdWJsaWMiIHhzaTp0eXBlPSJGdW5jdGlvbkRlZiI+CiAgICAgICAgIDxleHByZXNzaW9uIGxvY2F0b3I9IjE2Mjo0OS0xNjI6NTkiIHBhdGg9InZhbHVlIiB4c2k6dHlwZT0iUHJvcGVydHkiPgogICAgICAgICAgICA8c291cmNlIGxvY2F0b3I9IjE2Mjo0OS0xNjI6NTMiIG5hbWU9InZhbHVlIiB4c2k6dHlwZT0iT3BlcmFuZFJlZiIvPgogICAgICAgICA8L2V4cHJlc3Npb24+CiAgICAgICAgIDxvcGVyYW5kIG5hbWU9InZhbHVlIj4KICAgICAgICAgICAgPG9wZXJhbmRUeXBlU3BlY2lmaWVyIGxvY2F0b3I9IjE2MjozMi0xNjI6NDUiIG5hbWU9ImZoaXI6RmlsdGVyT3BlcmF0b3IiIHhzaTp0eXBlPSJOYW1lZFR5cGVTcGVjaWZpZXIiLz4KICAgICAgICAgPC9vcGVyYW5kPgogICAgICA8L2RlZj4KICAgICAgPGRlZiBsb2NhdG9yPSIxNjM6MS0xNjM6NTUiIG5hbWU9IlRvU3RyaW5nIiBjb250ZXh0PSJQYXRpZW50IiBhY2Nlc3NMZXZlbD0iUHVibGljIiB4c2k6dHlwZT0iRnVuY3Rpb25EZWYiPgogICAgICAgICA8ZXhwcmVzc2lvbiBsb2NhdG9yPSIxNjM6NDUtMTYzOjU1IiBwYXRoPSJ2YWx1ZSIgeHNpOnR5cGU9IlByb3BlcnR5Ij4KICAgICAgICAgICAgPHNvdXJjZSBsb2NhdG9yPSIxNjM6NDUtMTYzOjQ5IiBuYW1lPSJ2YWx1ZSIgeHNpOnR5cGU9Ik9wZXJhbmRSZWYiLz4KICAgICAgICAgPC9leHByZXNzaW9uPgogICAgICAgICA8b3BlcmFuZCBuYW1lPSJ2YWx1ZSI+CiAgICAgICAgICAgIDxvcGVyYW5kVHlwZVNwZWNpZmllciBsb2NhdG9yPSIxNjM6MzItMTYzOjQxIiBuYW1lPSJmaGlyOkZsYWdTdGF0dXMiIHhzaTp0eXBlPSJOYW1lZFR5cGVTcGVjaWZpZXIiLz4KICAgICAgICAgPC9vcGVyYW5kPgogICAgICA8L2RlZj4KICAgICAgPGRlZiBsb2NhdG9yPSIxNjQ6MS0xNjQ6NjQiIG5hbWU9IlRvU3RyaW5nIiBjb250ZXh0PSJQYXRpZW50IiBhY2Nlc3NMZXZlbD0iUHVibGljIiB4c2k6dHlwZT0iRnVuY3Rpb25EZWYiPgogICAgICAgICA8ZXhwcmVzc2lvbiBsb2NhdG9yPSIxNjQ6NTQtMTY0OjY0IiBwYXRoPSJ2YWx1ZSIgeHNpOnR5cGU9IlByb3BlcnR5Ij4KICAgICAgICAgICAgPHNvdXJjZSBsb2NhdG9yPSIxNjQ6NTQtMTY0OjU4IiBuYW1lPSJ2YWx1ZSIgeHNpOnR5cGU9Ik9wZXJhbmRSZWYiLz4KICAgICAgICAgPC9leHByZXNzaW9uPgogICAgICAgICA8b3BlcmFuZCBuYW1lPSJ2YWx1ZSI+CiAgICAgICAgICAgIDxvcGVyYW5kVHlwZVNwZWNpZmllciBsb2NhdG9yPSIxNjQ6MzItMTY0OjUwIiBuYW1lPSJmaGlyOkdvYWxMaWZlY3ljbGVTdGF0dXMiIHhzaTp0eXBlPSJOYW1lZFR5cGVTcGVjaWZpZXIiLz4KICAgICAgICAgPC9vcGVyYW5kPgogICAgICA8L2RlZj4KICAgICAgPGRlZiBsb2NhdG9yPSIxNjU6MS0xNjU6NjUiIG5hbWU9IlRvU3RyaW5nIiBjb250ZXh0PSJQYXRpZW50IiBhY2Nlc3NMZXZlbD0iUHVibGljIiB4c2k6dHlwZT0iRnVuY3Rpb25EZWYiPgogICAgICAgICA8ZXhwcmVzc2lvbiBsb2NhdG9yPSIxNjU6NTUtMTY1OjY1IiBwYXRoPSJ2YWx1ZSIgeHNpOnR5cGU9IlByb3BlcnR5Ij4KICAgICAgICAgICAgPHNvdXJjZSBsb2NhdG9yPSIxNjU6NTUtMTY1OjU5IiBuYW1lPSJ2YWx1ZSIgeHNpOnR5cGU9Ik9wZXJhbmRSZWYiLz4KICAgICAgICAgPC9leHByZXNzaW9uPgogICAgICAgICA8b3BlcmFuZCBuYW1lPSJ2YWx1ZSI+CiAgICAgICAgICAgIDxvcGVyYW5kVHlwZVNwZWNpZmllciBsb2NhdG9yPSIxNjU6MzItMTY1OjUxIiBuYW1lPSJmaGlyOkdyYXBoQ29tcGFydG1lbnRSdWxlIiB4c2k6dHlwZT0iTmFtZWRUeXBlU3BlY2lmaWVyIi8+CiAgICAgICAgIDwvb3BlcmFuZD4KICAgICAgPC9kZWY+CiAgICAgIDxkZWYgbG9jYXRvcj0iMTY2OjEtMTY2OjY0IiBuYW1lPSJUb1N0cmluZyIgY29udGV4dD0iUGF0aWVudCIgYWNjZXNzTGV2ZWw9IlB1YmxpYyIgeHNpOnR5cGU9IkZ1bmN0aW9uRGVmIj4KICAgICAgICAgPGV4cHJlc3Npb24gbG9jYXRvcj0iMTY2OjU0LTE2Njo2NCIgcGF0aD0idmFsdWUiIHhzaTp0eXBlPSJQcm9wZXJ0eSI+CiAgICAgICAgICAgIDxzb3VyY2UgbG9jYXRvcj0iMTY2OjU0LTE2Njo1OCIgbmFtZT0idmFsdWUiIHhzaTp0eXBlPSJPcGVyYW5kUmVmIi8+CiAgICAgICAgIDwvZXhwcmVzc2lvbj4KICAgICAgICAgPG9wZXJhbmQgbmFtZT0idmFsdWUiPgogICAgICAgICAgICA8b3BlcmFuZFR5cGVTcGVjaWZpZXIgbG9jYXRvcj0iMTY2OjMyLTE2Njo1MCIgbmFtZT0iZmhpcjpHcmFwaENvbXBhcnRtZW50VXNlIiB4c2k6dHlwZT0iTmFtZWRUeXBlU3BlY2lmaWVyIi8+CiAgICAgICAgIDwvb3BlcmFuZD4KICAgICAgPC9kZWY+CiAgICAgIDxkZWYgbG9jYXRvcj0iMTY3OjEtMTY3OjU3IiBuYW1lPSJUb1N0cmluZyIgY29udGV4dD0iUGF0aWVudCIgYWNjZXNzTGV2ZWw9IlB1YmxpYyIgeHNpOnR5cGU9IkZ1bmN0aW9uRGVmIj4KICAgICAgICAgPGV4cHJlc3Npb24gbG9jYXRvcj0iMTY3OjQ3LTE2Nzo1NyIgcGF0aD0idmFsdWUiIHhzaTp0eXBlPSJQcm9wZXJ0eSI+CiAgICAgICAgICAgIDxzb3VyY2UgbG9jYXRvcj0iMTY3OjQ3LTE2Nzo1MSIgbmFtZT0idmFsdWUiIHhzaTp0eXBlPSJPcGVyYW5kUmVmIi8+CiAgICAgICAgIDwvZXhwcmVzc2lvbj4KICAgICAgICAgPG9wZXJhbmQgbmFtZT0idmFsdWUiPgogICAgICAgICAgICA8b3BlcmFuZFR5cGVTcGVjaWZpZXIgbG9jYXRvcj0iMTY3OjMyLTE2Nzo0MyIgbmFtZT0iZmhpcjpHcm91cE1lYXN1cmUiIHhzaTp0eXBlPSJOYW1lZFR5cGVTcGVjaWZpZXIiLz4KICAgICAgICAgPC9vcGVyYW5kPgogICAgICA8L2RlZj4KICAgICAgPGRlZiBsb2NhdG9yPSIxNjg6MS0xNjg6NTQiIG5hbWU9IlRvU3RyaW5nIiBjb250ZXh0PSJQYXRpZW50IiBhY2Nlc3NMZXZlbD0iUHVibGljIiB4c2k6dHlwZT0iRnVuY3Rpb25EZWYiPgogICAgICAgICA8ZXhwcmVzc2lvbiBsb2NhdG9yPSIxNjg6NDQtMTY4OjU0IiBwYXRoPSJ2YWx1ZSIgeHNpOnR5cGU9IlByb3BlcnR5Ij4KICAgICAgICAgICAgPHNvdXJjZSBsb2NhdG9yPSIxNjg6NDQtMTY4OjQ4IiBuYW1lPSJ2YWx1ZSIgeHNpOnR5cGU9Ik9wZXJhbmRSZWYiLz4KICAgICAgICAgPC9leHByZXNzaW9uPgogICAgICAgICA8b3BlcmFuZCBuYW1lPSJ2YWx1ZSI+CiAgICAgICAgICAgIDxvcGVyYW5kVHlwZVNwZWNpZmllciBsb2NhdG9yPSIxNjg6MzItMTY4OjQwIiBuYW1lPSJmaGlyOkdyb3VwVHlwZSIgeHNpOnR5cGU9Ik5hbWVkVHlwZVNwZWNpZmllciIvPgogICAgICAgICA8L29wZXJhbmQ+CiAgICAgIDwvZGVmPgogICAgICA8ZGVmIGxvY2F0b3I9IjE2OToxLTE2OTo2NyIgbmFtZT0iVG9TdHJpbmciIGNvbnRleHQ9IlBhdGllbnQiIGFjY2Vzc0xldmVsPSJQdWJsaWMiIHhzaTp0eXBlPSJGdW5jdGlvbkRlZiI+CiAgICAgICAgIDxleHByZXNzaW9uIGxvY2F0b3I9IjE2OTo1Ny0xNjk6NjciIHBhdGg9InZhbHVlIiB4c2k6dHlwZT0iUHJvcGVydHkiPgogICAgICAgICAgICA8c291cmNlIGxvY2F0b3I9IjE2OTo1Ny0xNjk6NjEiIG5hbWU9InZhbHVlIiB4c2k6dHlwZT0iT3BlcmFuZFJlZiIvPgogICAgICAgICA8L2V4cHJlc3Npb24+CiAgICAgICAgIDxvcGVyYW5kIG5hbWU9InZhbHVlIj4KICAgICAgICAgICAgPG9wZXJhbmRUeXBlU3BlY2lmaWVyIGxvY2F0b3I9IjE2OTozMi0xNjk6NTMiIG5hbWU9ImZoaXI6R3VpZGFuY2VSZXNwb25zZVN0YXR1cyIgeHNpOnR5cGU9Ik5hbWVkVHlwZVNwZWNpZmllciIvPgogICAgICAgICA8L29wZXJhbmQ+CiAgICAgIDwvZGVmPgogICAgICA8ZGVmIGxvY2F0b3I9IjE3MDoxLTE3MDo2NCIgbmFtZT0iVG9TdHJpbmciIGNvbnRleHQ9IlBhdGllbnQiIGFjY2Vzc0xldmVsPSJQdWJsaWMiIHhzaTp0eXBlPSJGdW5jdGlvbkRlZiI+CiAgICAgICAgIDxleHByZXNzaW9uIGxvY2F0b3I9IjE3MDo1NC0xNzA6NjQiIHBhdGg9InZhbHVlIiB4c2k6dHlwZT0iUHJvcGVydHkiPgogICAgICAgICAgICA8c291cmNlIGxvY2F0b3I9IjE3MDo1NC0xNzA6NTgiIG5hbWU9InZhbHVlIiB4c2k6dHlwZT0iT3BlcmFuZFJlZiIvPgogICAgICAgICA8L2V4cHJlc3Npb24+CiAgICAgICAgIDxvcGVyYW5kIG5hbWU9InZhbHVlIj4KICAgICAgICAgICAgPG9wZXJhbmRUeXBlU3BlY2lmaWVyIGxvY2F0b3I9IjE3MDozMi0xNzA6NTAiIG5hbWU9ImZoaXI6R3VpZGVQYWdlR2VuZXJhdGlvbiIgeHNpOnR5cGU9Ik5hbWVkVHlwZVNwZWNpZmllciIvPgogICAgICAgICA8L29wZXJhbmQ+CiAgICAgIDwvZGVmPgogICAgICA8ZGVmIGxvY2F0b3I9IjE3MToxLTE3MTo2MyIgbmFtZT0iVG9TdHJpbmciIGNvbnRleHQ9IlBhdGllbnQiIGFjY2Vzc0xldmVsPSJQdWJsaWMiIHhzaTp0eXBlPSJGdW5jdGlvbkRlZiI+CiAgICAgICAgIDxleHByZXNzaW9uIGxvY2F0b3I9IjE3MTo1My0xNzE6NjMiIHBhdGg9InZhbHVlIiB4c2k6dHlwZT0iUHJvcGVydHkiPgogICAgICAgICAgICA8c291cmNlIGxvY2F0b3I9IjE3MTo1My0xNzE6NTciIG5hbWU9InZhbHVlIiB4c2k6dHlwZT0iT3BlcmFuZFJlZiIvPgogICAgICAgICA8L2V4cHJlc3Npb24+CiAgICAgICAgIDxvcGVyYW5kIG5hbWU9InZhbHVlIj4KICAgICAgICAgICAgPG9wZXJhbmRUeXBlU3BlY2lmaWVyIGxvY2F0b3I9IjE3MTozMi0xNzE6NDkiIG5hbWU9ImZoaXI6R3VpZGVQYXJhbWV0ZXJDb2RlIiB4c2k6dHlwZT0iTmFtZWRUeXBlU3BlY2lmaWVyIi8+CiAgICAgICAgIDwvb3BlcmFuZD4KICAgICAgPC9kZWY+CiAgICAgIDxkZWYgbG9jYXRvcj0iMTcyOjEtMTcyOjUzIiBuYW1lPSJUb1N0cmluZyIgY29udGV4dD0iUGF0aWVudCIgYWNjZXNzTGV2ZWw9IlB1YmxpYyIgeHNpOnR5cGU9IkZ1bmN0aW9uRGVmIj4KICAgICAgICAgPGV4cHJlc3Npb24gbG9jYXRvcj0iMTcyOjQzLTE3Mjo1MyIgcGF0aD0idmFsdWUiIHhzaTp0eXBlPSJQcm9wZXJ0eSI+CiAgICAgICAgICAgIDxzb3VyY2UgbG9jYXRvcj0iMTcyOjQzLTE3Mjo0NyIgbmFtZT0idmFsdWUiIHhzaTp0eXBlPSJPcGVyYW5kUmVmIi8+CiAgICAgICAgIDwvZXhwcmVzc2lvbj4KICAgICAgICAgPG9wZXJhbmQgbmFtZT0idmFsdWUiPgogICAgICAgICAgICA8b3BlcmFuZFR5cGVTcGVjaWZpZXIgbG9jYXRvcj0iMTcyOjMyLTE3MjozOSIgbmFtZT0iZmhpcjpIVFRQVmVyYiIgeHNpOnR5cGU9Ik5hbWVkVHlwZVNwZWNpZmllciIvPgogICAgICAgICA8L29wZXJhbmQ+CiAgICAgIDwvZGVmPgogICAgICA8ZGVmIGxvY2F0b3I9IjE3MzoxLTE3Mzo1OCIgbmFtZT0iVG9TdHJpbmciIGNvbnRleHQ9IlBhdGllbnQiIGFjY2Vzc0xldmVsPSJQdWJsaWMiIHhzaTp0eXBlPSJGdW5jdGlvbkRlZiI+CiAgICAgICAgIDxleHByZXNzaW9uIGxvY2F0b3I9IjE3Mzo0OC0xNzM6NTgiIHBhdGg9InZhbHVlIiB4c2k6dHlwZT0iUHJvcGVydHkiPgogICAgICAgICAgICA8c291cmNlIGxvY2F0b3I9IjE3Mzo0OC0xNzM6NTIiIG5hbWU9InZhbHVlIiB4c2k6dHlwZT0iT3BlcmFuZFJlZiIvPgogICAgICAgICA8L2V4cHJlc3Npb24+CiAgICAgICAgIDxvcGVyYW5kIG5hbWU9InZhbHVlIj4KICAgICAgICAgICAgPG9wZXJhbmRUeXBlU3BlY2lmaWVyIGxvY2F0b3I9IjE3MzozMi0xNzM6NDQiIG5hbWU9ImZoaXI6SWRlbnRpZmllclVzZSIgeHNpOnR5cGU9Ik5hbWVkVHlwZVNwZWNpZmllciIvPgogICAgICAgICA8L29wZXJhbmQ+CiAgICAgIDwvZGVmPgogICAgICA8ZGVmIGxvY2F0b3I9IjE3NDoxLTE3NDo2NyIgbmFtZT0iVG9TdHJpbmciIGNvbnRleHQ9IlBhdGllbnQiIGFjY2Vzc0xldmVsPSJQdWJsaWMiIHhzaTp0eXBlPSJGdW5jdGlvbkRlZiI+CiAgICAgICAgIDxleHByZXNzaW9uIGxvY2F0b3I9IjE3NDo1Ny0xNzQ6NjciIHBhdGg9InZhbHVlIiB4c2k6dHlwZT0iUHJvcGVydHkiPgogICAgICAgICAgICA8c291cmNlIGxvY2F0b3I9IjE3NDo1Ny0xNzQ6NjEiIG5hbWU9InZhbHVlIiB4c2k6dHlwZT0iT3BlcmFuZFJlZiIvPgogICAgICAgICA8L2V4cHJlc3Npb24+CiAgICAgICAgIDxvcGVyYW5kIG5hbWU9InZhbHVlIj4KICAgICAgICAgICAgPG9wZXJhbmRUeXBlU3BlY2lmaWVyIGxvY2F0b3I9IjE3NDozMi0xNzQ6NTMiIG5hbWU9ImZoaXI6SWRlbnRpdHlBc3N1cmFuY2VMZXZlbCIgeHNpOnR5cGU9Ik5hbWVkVHlwZVNwZWNpZmllciIvPgogICAgICAgICA8L29wZXJhbmQ+CiAgICAgIDwvZGVmPgogICAgICA8ZGVmIGxvY2F0b3I9IjE3NToxLTE3NTo2MyIgbmFtZT0iVG9TdHJpbmciIGNvbnRleHQ9IlBhdGllbnQiIGFjY2Vzc0xldmVsPSJQdWJsaWMiIHhzaTp0eXBlPSJGdW5jdGlvbkRlZiI+CiAgICAgICAgIDxleHByZXNzaW9uIGxvY2F0b3I9IjE3NTo1My0xNzU6NjMiIHBhdGg9InZhbHVlIiB4c2k6dHlwZT0iUHJvcGVydHkiPgogICAgICAgICAgICA8c291cmNlIGxvY2F0b3I9IjE3NTo1My0xNzU6NTciIG5hbWU9InZhbHVlIiB4c2k6dHlwZT0iT3BlcmFuZFJlZiIvPgogICAgICAgICA8L2V4cHJlc3Npb24+CiAgICAgICAgIDxvcGVyYW5kIG5hbWU9InZhbHVlIj4KICAgICAgICAgICAgPG9wZXJhbmRUeXBlU3BlY2lmaWVyIGxvY2F0b3I9IjE3NTozMi0xNzU6NDkiIG5hbWU9ImZoaXI6SW1hZ2luZ1N0dWR5U3RhdHVzIiB4c2k6dHlwZT0iTmFtZWRUeXBlU3BlY2lmaWVyIi8+CiAgICAgICAgIDwvb3BlcmFuZD4KICAgICAgPC9kZWY+CiAgICAgIDxkZWYgbG9jYXRvcj0iMTc2OjEtMTc2OjczIiBuYW1lPSJUb1N0cmluZyIgY29udGV4dD0iUGF0aWVudCIgYWNjZXNzTGV2ZWw9IlB1YmxpYyIgeHNpOnR5cGU9IkZ1bmN0aW9uRGVmIj4KICAgICAgICAgPGV4cHJlc3Npb24gbG9jYXRvcj0iMTc2OjYzLTE3Njo3MyIgcGF0aD0idmFsdWUiIHhzaTp0eXBlPSJQcm9wZXJ0eSI+CiAgICAgICAgICAgIDxzb3VyY2UgbG9jYXRvcj0iMTc2OjYzLTE3Njo2NyIgbmFtZT0idmFsdWUiIHhzaTp0eXBlPSJPcGVyYW5kUmVmIi8+CiAgICAgICAgIDwvZXhwcmVzc2lvbj4KICAgICAgICAgPG9wZXJhbmQgbmFtZT0idmFsdWUiPgogICAgICAgICAgICA8b3BlcmFuZFR5cGVTcGVjaWZpZXIgbG9jYXRvcj0iMTc2OjMyLTE3Njo1OSIgbmFtZT0iZmhpcjpJbW11bml6YXRpb25FdmFsdWF0aW9uU3RhdHVzIiB4c2k6dHlwZT0iTmFtZWRUeXBlU3BlY2lmaWVyIi8+CiAgICAgICAgIDwvb3BlcmFuZD4KICAgICAgPC9kZWY+CiAgICAgIDxkZWYgbG9jYXRvcj0iMTc3OjEtMTc3OjYzIiBuYW1lPSJUb1N0cmluZyIgY29udGV4dD0iUGF0aWVudCIgYWNjZXNzTGV2ZWw9IlB1YmxpYyIgeHNpOnR5cGU9IkZ1bmN0aW9uRGVmIj4KICAgICAgICAgPGV4cHJlc3Npb24gbG9jYXRvcj0iMTc3OjUzLTE3Nzo2MyIgcGF0aD0idmFsdWUiIHhzaTp0eXBlPSJQcm9wZXJ0eSI+CiAgICAgICAgICAgIDxzb3VyY2UgbG9jYXRvcj0iMTc3OjUzLTE3Nzo1NyIgbmFtZT0idmFsdWUiIHhzaTp0eXBlPSJPcGVyYW5kUmVmIi8+CiAgICAgICAgIDwvZXhwcmVzc2lvbj4KICAgICAgICAgPG9wZXJhbmQgbmFtZT0idmFsdWUiPgogICAgICAgICAgICA8b3BlcmFuZFR5cGVTcGVjaWZpZXIgbG9jYXRvcj0iMTc3OjMyLTE3Nzo0OSIgbmFtZT0iZmhpcjpJbW11bml6YXRpb25TdGF0dXMiIHhzaTp0eXBlPSJOYW1lZFR5cGVTcGVjaWZpZXIiLz4KICAgICAgICAgPC9vcGVyYW5kPgogICAgICA8L2RlZj4KICAgICAgPGRlZiBsb2NhdG9yPSIxNzg6MS0xNzg6NzAiIG5hbWU9IlRvU3RyaW5nIiBjb250ZXh0PSJQYXRpZW50IiBhY2Nlc3NMZXZlbD0iUHVibGljIiB4c2k6dHlwZT0iRnVuY3Rpb25EZWYiPgogICAgICAgICA8ZXhwcmVzc2lvbiBsb2NhdG9yPSIxNzg6NjAtMTc4OjcwIiBwYXRoPSJ2YWx1ZSIgeHNpOnR5cGU9IlByb3BlcnR5Ij4KICAgICAgICAgICAgPHNvdXJjZSBsb2NhdG9yPSIxNzg6NjAtMTc4OjY0IiBuYW1lPSJ2YWx1ZSIgeHNpOnR5cGU9Ik9wZXJhbmRSZWYiLz4KICAgICAgICAgPC9leHByZXNzaW9uPgogICAgICAgICA8b3BlcmFuZCBuYW1lPSJ2YWx1ZSI+CiAgICAgICAgICAgIDxvcGVyYW5kVHlwZVNwZWNpZmllciBsb2NhdG9yPSIxNzg6MzItMTc4OjU2IiBuYW1lPSJmaGlyOkludm9pY2VQcmljZUNvbXBvbmVudFR5cGUiIHhzaTp0eXBlPSJOYW1lZFR5cGVTcGVjaWZpZXIiLz4KICAgICAgICAgPC9vcGVyYW5kPgogICAgICA8L2RlZj4KICAgICAgPGRlZiBsb2NhdG9yPSIxNzk6MS0xNzk6NTgiIG5hbWU9IlRvU3RyaW5nIiBjb250ZXh0PSJQYXRpZW50IiBhY2Nlc3NMZXZlbD0iUHVibGljIiB4c2k6dHlwZT0iRnVuY3Rpb25EZWYiPgogICAgICAgICA8ZXhwcmVzc2lvbiBsb2NhdG9yPSIxNzk6NDgtMTc5OjU4IiBwYXRoPSJ2YWx1ZSIgeHNpOnR5cGU9IlByb3BlcnR5Ij4KICAgICAgICAgICAgPHNvdXJjZSBsb2NhdG9yPSIxNzk6NDgtMTc5OjUyIiBuYW1lPSJ2YWx1ZSIgeHNpOnR5cGU9Ik9wZXJhbmRSZWYiLz4KICAgICAgICAgPC9leHByZXNzaW9uPgogICAgICAgICA8b3BlcmFuZCBuYW1lPSJ2YWx1ZSI+CiAgICAgICAgICAgIDxvcGVyYW5kVHlwZVNwZWNpZmllciBsb2NhdG9yPSIxNzk6MzItMTc5OjQ0IiBuYW1lPSJmaGlyOkludm9pY2VTdGF0dXMiIHhzaTp0eXBlPSJOYW1lZFR5cGVTcGVjaWZpZXIiLz4KICAgICAgICAgPC9vcGVyYW5kPgogICAgICA8L2RlZj4KICAgICAgPGRlZiBsb2NhdG9yPSIxODA6MS0xODA6NTgiIG5hbWU9IlRvU3RyaW5nIiBjb250ZXh0PSJQYXRpZW50IiBhY2Nlc3NMZXZlbD0iUHVibGljIiB4c2k6dHlwZT0iRnVuY3Rpb25EZWYiPgogICAgICAgICA8ZXhwcmVzc2lvbiBsb2NhdG9yPSIxODA6NDgtMTgwOjU4IiBwYXRoPSJ2YWx1ZSIgeHNpOnR5cGU9IlByb3BlcnR5Ij4KICAgICAgICAgICAgPHNvdXJjZSBsb2NhdG9yPSIxODA6NDgtMTgwOjUyIiBuYW1lPSJ2YWx1ZSIgeHNpOnR5cGU9Ik9wZXJhbmRSZWYiLz4KICAgICAgICAgPC9leHByZXNzaW9uPgogICAgICAgICA8b3BlcmFuZCBuYW1lPSJ2YWx1ZSI+CiAgICAgICAgICAgIDxvcGVyYW5kVHlwZVNwZWNpZmllciBsb2NhdG9yPSIxODA6MzItMTgwOjQ0IiBuYW1lPSJmaGlyOklzc3VlU2V2ZXJpdHkiIHhzaTp0eXBlPSJOYW1lZFR5cGVTcGVjaWZpZXIiLz4KICAgICAgICAgPC9vcGVyYW5kPgogICAgICA8L2RlZj4KICAgICAgPGRlZiBsb2NhdG9yPSIxODE6MS0xODE6NTQiIG5hbWU9IlRvU3RyaW5nIiBjb250ZXh0PSJQYXRpZW50IiBhY2Nlc3NMZXZlbD0iUHVibGljIiB4c2k6dHlwZT0iRnVuY3Rpb25EZWYiPgogICAgICAgICA8ZXhwcmVzc2lvbiBsb2NhdG9yPSIxODE6NDQtMTgxOjU0IiBwYXRoPSJ2YWx1ZSIgeHNpOnR5cGU9IlByb3BlcnR5Ij4KICAgICAgICAgICAgPHNvdXJjZSBsb2NhdG9yPSIxODE6NDQtMTgxOjQ4IiBuYW1lPSJ2YWx1ZSIgeHNpOnR5cGU9Ik9wZXJhbmRSZWYiLz4KICAgICAgICAgPC9leHByZXNzaW9uPgogICAgICAgICA8b3BlcmFuZCBuYW1lPSJ2YWx1ZSI+CiAgICAgICAgICAgIDxvcGVyYW5kVHlwZVNwZWNpZmllciBsb2NhdG9yPSIxODE6MzItMTgxOjQwIiBuYW1lPSJmaGlyOklzc3VlVHlwZSIgeHNpOnR5cGU9Ik5hbWVkVHlwZVNwZWNpZmllciIvPgogICAgICAgICA8L29wZXJhbmQ+CiAgICAgIDwvZGVmPgogICAgICA8ZGVmIGxvY2F0b3I9IjE4MjoxLTE4Mjo1MyIgbmFtZT0iVG9TdHJpbmciIGNvbnRleHQ9IlBhdGllbnQiIGFjY2Vzc0xldmVsPSJQdWJsaWMiIHhzaTp0eXBlPSJGdW5jdGlvbkRlZiI+CiAgICAgICAgIDxleHByZXNzaW9uIGxvY2F0b3I9IjE4Mjo0My0xODI6NTMiIHBhdGg9InZhbHVlIiB4c2k6dHlwZT0iUHJvcGVydHkiPgogICAgICAgICAgICA8c291cmNlIGxvY2F0b3I9IjE4Mjo0My0xODI6NDciIG5hbWU9InZhbHVlIiB4c2k6dHlwZT0iT3BlcmFuZFJlZiIvPgogICAgICAgICA8L2V4cHJlc3Npb24+CiAgICAgICAgIDxvcGVyYW5kIG5hbWU9InZhbHVlIj4KICAgICAgICAgICAgPG9wZXJhbmRUeXBlU3BlY2lmaWVyIGxvY2F0b3I9IjE4MjozMi0xODI6MzkiIG5hbWU9ImZoaXI6TGlua1R5cGUiIHhzaTp0eXBlPSJOYW1lZFR5cGVTcGVjaWZpZXIiLz4KICAgICAgICAgPC9vcGVyYW5kPgogICAgICA8L2RlZj4KICAgICAgPGRlZiBsb2NhdG9yPSIxODM6MS0xODM6NTYiIG5hbWU9IlRvU3RyaW5nIiBjb250ZXh0PSJQYXRpZW50IiBhY2Nlc3NMZXZlbD0iUHVibGljIiB4c2k6dHlwZT0iRnVuY3Rpb25EZWYiPgogICAgICAgICA8ZXhwcmVzc2lvbiBsb2NhdG9yPSIxODM6NDYtMTgzOjU2IiBwYXRoPSJ2YWx1ZSIgeHNpOnR5cGU9IlByb3BlcnR5Ij4KICAgICAgICAgICAgPHNvdXJjZSBsb2NhdG9yPSIxODM6NDYtMTgzOjUwIiBuYW1lPSJ2YWx1ZSIgeHNpOnR5cGU9Ik9wZXJhbmRSZWYiLz4KICAgICAgICAgPC9leHByZXNzaW9uPgogICAgICAgICA8b3BlcmFuZCBuYW1lPSJ2YWx1ZSI+CiAgICAgICAgICAgIDxvcGVyYW5kVHlwZVNwZWNpZmllciBsb2NhdG9yPSIxODM6MzItMTgzOjQyIiBuYW1lPSJmaGlyOkxpbmthZ2VUeXBlIiB4c2k6dHlwZT0iTmFtZWRUeXBlU3BlY2lmaWVyIi8+CiAgICAgICAgIDwvb3BlcmFuZD4KICAgICAgPC9kZWY+CiAgICAgIDxkZWYgbG9jYXRvcj0iMTg0OjEtMTg0OjUzIiBuYW1lPSJUb1N0cmluZyIgY29udGV4dD0iUGF0aWVudCIgYWNjZXNzTGV2ZWw9IlB1YmxpYyIgeHNpOnR5cGU9IkZ1bmN0aW9uRGVmIj4KICAgICAgICAgPGV4cHJlc3Npb24gbG9jYXRvcj0iMTg0OjQzLTE4NDo1MyIgcGF0aD0idmFsdWUiIHhzaTp0eXBlPSJQcm9wZXJ0eSI+CiAgICAgICAgICAgIDxzb3VyY2UgbG9jYXRvcj0iMTg0OjQzLTE4NDo0NyIgbmFtZT0idmFsdWUiIHhzaTp0eXBlPSJPcGVyYW5kUmVmIi8+CiAgICAgICAgIDwvZXhwcmVzc2lvbj4KICAgICAgICAgPG9wZXJhbmQgbmFtZT0idmFsdWUiPgogICAgICAgICAgICA8b3BlcmFuZFR5cGVTcGVjaWZpZXIgbG9jYXRvcj0iMTg0OjMyLTE4NDozOSIgbmFtZT0iZmhpcjpMaXN0TW9kZSIgeHNpOnR5cGU9Ik5hbWVkVHlwZVNwZWNpZmllciIvPgogICAgICAgICA8L29wZXJhbmQ+CiAgICAgIDwvZGVmPgogICAgICA8ZGVmIGxvY2F0b3I9IjE4NToxLTE4NTo1NSIgbmFtZT0iVG9TdHJpbmciIGNvbnRleHQ9IlBhdGllbnQiIGFjY2Vzc0xldmVsPSJQdWJsaWMiIHhzaTp0eXBlPSJGdW5jdGlvbkRlZiI+CiAgICAgICAgIDxleHByZXNzaW9uIGxvY2F0b3I9IjE4NTo0NS0xODU6NTUiIHBhdGg9InZhbHVlIiB4c2k6dHlwZT0iUHJvcGVydHkiPgogICAgICAgICAgICA8c291cmNlIGxvY2F0b3I9IjE4NTo0NS0xODU6NDkiIG5hbWU9InZhbHVlIiB4c2k6dHlwZT0iT3BlcmFuZFJlZiIvPgogICAgICAgICA8L2V4cHJlc3Npb24+CiAgICAgICAgIDxvcGVyYW5kIG5hbWU9InZhbHVlIj4KICAgICAgICAgICAgPG9wZXJhbmRUeXBlU3BlY2lmaWVyIGxvY2F0b3I9IjE4NTozMi0xODU6NDEiIG5hbWU9ImZoaXI6TGlzdFN0YXR1cyIgeHNpOnR5cGU9Ik5hbWVkVHlwZVNwZWNpZmllciIvPgogICAgICAgICA8L29wZXJhbmQ+CiAgICAgIDwvZGVmPgogICAgICA8ZGVmIGxvY2F0b3I9IjE4NjoxLTE4Njo1NyIgbmFtZT0iVG9TdHJpbmciIGNvbnRleHQ9IlBhdGllbnQiIGFjY2Vzc0xldmVsPSJQdWJsaWMiIHhzaTp0eXBlPSJGdW5jdGlvbkRlZiI+CiAgICAgICAgIDxleHByZXNzaW9uIGxvY2F0b3I9IjE4Njo0Ny0xODY6NTciIHBhdGg9InZhbHVlIiB4c2k6dHlwZT0iUHJvcGVydHkiPgogICAgICAgICAgICA8c291cmNlIGxvY2F0b3I9IjE4Njo0Ny0xODY6NTEiIG5hbWU9InZhbHVlIiB4c2k6dHlwZT0iT3BlcmFuZFJlZiIvPgogICAgICAgICA8L2V4cHJlc3Npb24+CiAgICAgICAgIDxvcGVyYW5kIG5hbWU9InZhbHVlIj4KICAgICAgICAgICAgPG9wZXJhbmRUeXBlU3BlY2lmaWVyIGxvY2F0b3I9IjE4NjozMi0xODY6NDMiIG5hbWU9ImZoaXI6TG9jYXRpb25Nb2RlIiB4c2k6dHlwZT0iTmFtZWRUeXBlU3BlY2lmaWVyIi8+CiAgICAgICAgIDwvb3BlcmFuZD4KICAgICAgPC9kZWY+CiAgICAgIDxkZWYgbG9jYXRvcj0iMTg3OjEtMTg3OjU5IiBuYW1lPSJUb1N0cmluZyIgY29udGV4dD0iUGF0aWVudCIgYWNjZXNzTGV2ZWw9IlB1YmxpYyIgeHNpOnR5cGU9IkZ1bmN0aW9uRGVmIj4KICAgICAgICAgPGV4cHJlc3Npb24gbG9jYXRvcj0iMTg3OjQ5LTE4Nzo1OSIgcGF0aD0idmFsdWUiIHhzaTp0eXBlPSJQcm9wZXJ0eSI+CiAgICAgICAgICAgIDxzb3VyY2UgbG9jYXRvcj0iMTg3OjQ5LTE4Nzo1MyIgbmFtZT0idmFsdWUiIHhzaTp0eXBlPSJPcGVyYW5kUmVmIi8+CiAgICAgICAgIDwvZXhwcmVzc2lvbj4KICAgICAgICAgPG9wZXJhbmQgbmFtZT0idmFsdWUiPgogICAgICAgICAgICA8b3BlcmFuZFR5cGVTcGVjaWZpZXIgbG9jYXRvcj0iMTg3OjMyLTE4Nzo0NSIgbmFtZT0iZmhpcjpMb2NhdGlvblN0YXR1cyIgeHNpOnR5cGU9Ik5hbWVkVHlwZVNwZWNpZmllciIvPgogICAgICAgICA8L29wZXJhbmQ+CiAgICAgIDwvZGVmPgogICAgICA8ZGVmIGxvY2F0b3I9IjE4ODoxLTE4ODo2NCIgbmFtZT0iVG9TdHJpbmciIGNvbnRleHQ9IlBhdGllbnQiIGFjY2Vzc0xldmVsPSJQdWJsaWMiIHhzaTp0eXBlPSJGdW5jdGlvbkRlZiI+CiAgICAgICAgIDxleHByZXNzaW9uIGxvY2F0b3I9IjE4ODo1NC0xODg6NjQiIHBhdGg9InZhbHVlIiB4c2k6dHlwZT0iUHJvcGVydHkiPgogICAgICAgICAgICA8c291cmNlIGxvY2F0b3I9IjE4ODo1NC0xODg6NTgiIG5hbWU9InZhbHVlIiB4c2k6dHlwZT0iT3BlcmFuZFJlZiIvPgogICAgICAgICA8L2V4cHJlc3Npb24+CiAgICAgICAgIDxvcGVyYW5kIG5hbWU9InZhbHVlIj4KICAgICAgICAgICAgPG9wZXJhbmRUeXBlU3BlY2lmaWVyIGxvY2F0b3I9IjE4ODozMi0xODg6NTAiIG5hbWU9ImZoaXI6TWVhc3VyZVJlcG9ydFN0YXR1cyIgeHNpOnR5cGU9Ik5hbWVkVHlwZVNwZWNpZmllciIvPgogICAgICAgICA8L29wZXJhbmQ+CiAgICAgIDwvZGVmPgogICAgICA8ZGVmIGxvY2F0b3I9IjE4OToxLTE4OTo2MiIgbmFtZT0iVG9TdHJpbmciIGNvbnRleHQ9IlBhdGllbnQiIGFjY2Vzc0xldmVsPSJQdWJsaWMiIHhzaTp0eXBlPSJGdW5jdGlvbkRlZiI+CiAgICAgICAgIDxleHByZXNzaW9uIGxvY2F0b3I9IjE4OTo1Mi0xODk6NjIiIHBhdGg9InZhbHVlIiB4c2k6dHlwZT0iUHJvcGVydHkiPgogICAgICAgICAgICA8c291cmNlIGxvY2F0b3I9IjE4OTo1Mi0xODk6NTYiIG5hbWU9InZhbHVlIiB4c2k6dHlwZT0iT3BlcmFuZFJlZiIvPgogICAgICAgICA8L2V4cHJlc3Npb24+CiAgICAgICAgIDxvcGVyYW5kIG5hbWU9InZhbHVlIj4KICAgICAgICAgICAgPG9wZXJhbmRUeXBlU3BlY2lmaWVyIGxvY2F0b3I9IjE4OTozMi0xODk6NDgiIG5hbWU9ImZoaXI6TWVhc3VyZVJlcG9ydFR5cGUiIHhzaTp0eXBlPSJOYW1lZFR5cGVTcGVjaWZpZXIiLz4KICAgICAgICAgPC9vcGVyYW5kPgogICAgICA8L2RlZj4KICAgICAgPGRlZiBsb2NhdG9yPSIxOTA6MS0xOTA6NTYiIG5hbWU9IlRvU3RyaW5nIiBjb250ZXh0PSJQYXRpZW50IiBhY2Nlc3NMZXZlbD0iUHVibGljIiB4c2k6dHlwZT0iRnVuY3Rpb25EZWYiPgogICAgICAgICA8ZXhwcmVzc2lvbiBsb2NhdG9yPSIxOTA6NDYtMTkwOjU2IiBwYXRoPSJ2YWx1ZSIgeHNpOnR5cGU9IlByb3BlcnR5Ij4KICAgICAgICAgICAgPHNvdXJjZSBsb2NhdG9yPSIxOTA6NDYtMTkwOjUwIiBuYW1lPSJ2YWx1ZSIgeHNpOnR5cGU9Ik9wZXJhbmRSZWYiLz4KICAgICAgICAgPC9leHByZXNzaW9uPgogICAgICAgICA8b3BlcmFuZCBuYW1lPSJ2YWx1ZSI+CiAgICAgICAgICAgIDxvcGVyYW5kVHlwZVNwZWNpZmllciBsb2NhdG9yPSIxOTA6MzItMTkwOjQyIiBuYW1lPSJmaGlyOk1lZGlhU3RhdHVzIiB4c2k6dHlwZT0iTmFtZWRUeXBlU3BlY2lmaWVyIi8+CiAgICAgICAgIDwvb3BlcmFuZD4KICAgICAgPC9kZWY+CiAgICAgIDxkZWYgbG9jYXRvcj0iMTkxOjEtMTkxOjc1IiBuYW1lPSJUb1N0cmluZyIgY29udGV4dD0iUGF0aWVudCIgYWNjZXNzTGV2ZWw9IlB1YmxpYyIgeHNpOnR5cGU9IkZ1bmN0aW9uRGVmIj4KICAgICAgICAgPGV4cHJlc3Npb24gbG9jYXRvcj0iMTkxOjY1LTE5MTo3NSIgcGF0aD0idmFsdWUiIHhzaTp0eXBlPSJQcm9wZXJ0eSI+CiAgICAgICAgICAgIDxzb3VyY2UgbG9jYXRvcj0iMTkxOjY1LTE5MTo2OSIgbmFtZT0idmFsdWUiIHhzaTp0eXBlPSJPcGVyYW5kUmVmIi8+CiAgICAgICAgIDwvZXhwcmVzc2lvbj4KICAgICAgICAgPG9wZXJhbmQgbmFtZT0idmFsdWUiPgogICAgICAgICAgICA8b3BlcmFuZFR5cGVTcGVjaWZpZXIgbG9jYXRvcj0iMTkxOjMyLTE5MTo2MSIgbmFtZT0iZmhpcjpNZWRpY2F0aW9uQWRtaW5pc3RyYXRpb25TdGF0dXMiIHhzaTp0eXBlPSJOYW1lZFR5cGVTcGVjaWZpZXIiLz4KICAgICAgICAgPC9vcGVyYW5kPgogICAgICA8L2RlZj4KICAgICAgPGRlZiBsb2NhdG9yPSIxOTI6MS0xOTI6NjkiIG5hbWU9IlRvU3RyaW5nIiBjb250ZXh0PSJQYXRpZW50IiBhY2Nlc3NMZXZlbD0iUHVibGljIiB4c2k6dHlwZT0iRnVuY3Rpb25EZWYiPgogICAgICAgICA8ZXhwcmVzc2lvbiBsb2NhdG9yPSIxOTI6NTktMTkyOjY5IiBwYXRoPSJ2YWx1ZSIgeHNpOnR5cGU9IlByb3BlcnR5Ij4KICAgICAgICAgICAgPHNvdXJjZSBsb2NhdG9yPSIxOTI6NTktMTkyOjYzIiBuYW1lPSJ2YWx1ZSIgeHNpOnR5cGU9Ik9wZXJhbmRSZWYiLz4KICAgICAgICAgPC9leHByZXNzaW9uPgogICAgICAgICA8b3BlcmFuZCBuYW1lPSJ2YWx1ZSI+CiAgICAgICAgICAgIDxvcGVyYW5kVHlwZVNwZWNpZmllciBsb2NhdG9yPSIxOTI6MzItMTkyOjU1IiBuYW1lPSJmaGlyOk1lZGljYXRpb25EaXNwZW5zZVN0YXR1cyIgeHNpOnR5cGU9Ik5hbWVkVHlwZVNwZWNpZmllciIvPgogICAgICAgICA8L29wZXJhbmQ+CiAgICAgIDwvZGVmPgogICAgICA8ZGVmIGxvY2F0b3I9IjE5MzoxLTE5Mzo3MCIgbmFtZT0iVG9TdHJpbmciIGNvbnRleHQ9IlBhdGllbnQiIGFjY2Vzc0xldmVsPSJQdWJsaWMiIHhzaTp0eXBlPSJGdW5jdGlvbkRlZiI+CiAgICAgICAgIDxleHByZXNzaW9uIGxvY2F0b3I9IjE5Mzo2MC0xOTM6NzAiIHBhdGg9InZhbHVlIiB4c2k6dHlwZT0iUHJvcGVydHkiPgogICAgICAgICAgICA8c291cmNlIGxvY2F0b3I9IjE5Mzo2MC0xOTM6NjQiIG5hbWU9InZhbHVlIiB4c2k6dHlwZT0iT3BlcmFuZFJlZiIvPgogICAgICAgICA8L2V4cHJlc3Npb24+CiAgICAgICAgIDxvcGVyYW5kIG5hbWU9InZhbHVlIj4KICAgICAgICAgICAgPG9wZXJhbmRUeXBlU3BlY2lmaWVyIGxvY2F0b3I9IjE5MzozMi0xOTM6NTYiIG5hbWU9ImZoaXI6TWVkaWNhdGlvbktub3dsZWRnZVN0YXR1cyIgeHNpOnR5cGU9Ik5hbWVkVHlwZVNwZWNpZmllciIvPgogICAgICAgICA8L29wZXJhbmQ+CiAgICAgIDwvZGVmPgogICAgICA8ZGVmIGxvY2F0b3I9IjE5NDoxLTE5NDo2OCIgbmFtZT0iVG9TdHJpbmciIGNvbnRleHQ9IlBhdGllbnQiIGFjY2Vzc0xldmVsPSJQdWJsaWMiIHhzaTp0eXBlPSJGdW5jdGlvbkRlZiI+CiAgICAgICAgIDxleHByZXNzaW9uIGxvY2F0b3I9IjE5NDo1OC0xOTQ6NjgiIHBhdGg9InZhbHVlIiB4c2k6dHlwZT0iUHJvcGVydHkiPgogICAgICAgICAgICA8c291cmNlIGxvY2F0b3I9IjE5NDo1OC0xOTQ6NjIiIG5hbWU9InZhbHVlIiB4c2k6dHlwZT0iT3BlcmFuZFJlZiIvPgogICAgICAgICA8L2V4cHJlc3Npb24+CiAgICAgICAgIDxvcGVyYW5kIG5hbWU9InZhbHVlIj4KICAgICAgICAgICAgPG9wZXJhbmRUeXBlU3BlY2lmaWVyIGxvY2F0b3I9IjE5NDozMi0xOTQ6NTQiIG5hbWU9ImZoaXI6TWVkaWNhdGlvblJlcXVlc3RJbnRlbnQiIHhzaTp0eXBlPSJOYW1lZFR5cGVTcGVjaWZpZXIiLz4KICAgICAgICAgPC9vcGVyYW5kPgogICAgICA8L2RlZj4KICAgICAgPGRlZiBsb2NhdG9yPSIxOTU6MS0xOTU6NzAiIG5hbWU9IlRvU3RyaW5nIiBjb250ZXh0PSJQYXRpZW50IiBhY2Nlc3NMZXZlbD0iUHVibGljIiB4c2k6dHlwZT0iRnVuY3Rpb25EZWYiPgogICAgICAgICA8ZXhwcmVzc2lvbiBsb2NhdG9yPSIxOTU6NjAtMTk1OjcwIiBwYXRoPSJ2YWx1ZSIgeHNpOnR5cGU9IlByb3BlcnR5Ij4KICAgICAgICAgICAgPHNvdXJjZSBsb2NhdG9yPSIxOTU6NjAtMTk1OjY0IiBuYW1lPSJ2YWx1ZSIgeHNpOnR5cGU9Ik9wZXJhbmRSZWYiLz4KICAgICAgICAgPC9leHByZXNzaW9uPgogICAgICAgICA8b3BlcmFuZCBuYW1lPSJ2YWx1ZSI+CiAgICAgICAgICAgIDxvcGVyYW5kVHlwZVNwZWNpZmllciBsb2NhdG9yPSIxOTU6MzItMTk1OjU2IiBuYW1lPSJmaGlyOk1lZGljYXRpb25SZXF1ZXN0UHJpb3JpdHkiIHhzaTp0eXBlPSJOYW1lZFR5cGVTcGVjaWZpZXIiLz4KICAgICAgICAgPC9vcGVyYW5kPgogICAgICA8L2RlZj4KICAgICAgPGRlZiBsb2NhdG9yPSIxOTY6MS0xOTY6NjgiIG5hbWU9IlRvU3RyaW5nIiBjb250ZXh0PSJQYXRpZW50IiBhY2Nlc3NMZXZlbD0iUHVibGljIiB4c2k6dHlwZT0iRnVuY3Rpb25EZWYiPgogICAgICAgICA8ZXhwcmVzc2lvbiBsb2NhdG9yPSIxOTY6NTgtMTk2OjY4IiBwYXRoPSJ2YWx1ZSIgeHNpOnR5cGU9IlByb3BlcnR5Ij4KICAgICAgICAgICAgPHNvdXJjZSBsb2NhdG9yPSIxOTY6NTgtMTk2OjYyIiBuYW1lPSJ2YWx1ZSIgeHNpOnR5cGU9Ik9wZXJhbmRSZWYiLz4KICAgICAgICAgPC9leHByZXNzaW9uPgogICAgICAgICA8b3BlcmFuZCBuYW1lPSJ2YWx1ZSI+CiAgICAgICAgICAgIDxvcGVyYW5kVHlwZVNwZWNpZmllciBsb2NhdG9yPSIxOTY6MzItMTk2OjU0IiBuYW1lPSJmaGlyOk1lZGljYXRpb25SZXF1ZXN0U3RhdHVzIiB4c2k6dHlwZT0iTmFtZWRUeXBlU3BlY2lmaWVyIi8+CiAgICAgICAgIDwvb3BlcmFuZD4KICAgICAgPC9kZWY+CiAgICAgIDxkZWYgbG9jYXRvcj0iMTk3OjEtMTk3OjcwIiBuYW1lPSJUb1N0cmluZyIgY29udGV4dD0iUGF0aWVudCIgYWNjZXNzTGV2ZWw9IlB1YmxpYyIgeHNpOnR5cGU9IkZ1bmN0aW9uRGVmIj4KICAgICAgICAgPGV4cHJlc3Npb24gbG9jYXRvcj0iMTk3OjYwLTE5Nzo3MCIgcGF0aD0idmFsdWUiIHhzaTp0eXBlPSJQcm9wZXJ0eSI+CiAgICAgICAgICAgIDxzb3VyY2UgbG9jYXRvcj0iMTk3OjYwLTE5Nzo2NCIgbmFtZT0idmFsdWUiIHhzaTp0eXBlPSJPcGVyYW5kUmVmIi8+CiAgICAgICAgIDwvZXhwcmVzc2lvbj4KICAgICAgICAgPG9wZXJhbmQgbmFtZT0idmFsdWUiPgogICAgICAgICAgICA8b3BlcmFuZFR5cGVTcGVjaWZpZXIgbG9jYXRvcj0iMTk3OjMyLTE5Nzo1NiIgbmFtZT0iZmhpcjpNZWRpY2F0aW9uU3RhdGVtZW50U3RhdHVzIiB4c2k6dHlwZT0iTmFtZWRUeXBlU3BlY2lmaWVyIi8+CiAgICAgICAgIDwvb3BlcmFuZD4KICAgICAgPC9kZWY+CiAgICAgIDxkZWYgbG9jYXRvcj0iMTk4OjEtMTk4OjYxIiBuYW1lPSJUb1N0cmluZyIgY29udGV4dD0iUGF0aWVudCIgYWNjZXNzTGV2ZWw9IlB1YmxpYyIgeHNpOnR5cGU9IkZ1bmN0aW9uRGVmIj4KICAgICAgICAgPGV4cHJlc3Npb24gbG9jYXRvcj0iMTk4OjUxLTE5ODo2MSIgcGF0aD0idmFsdWUiIHhzaTp0eXBlPSJQcm9wZXJ0eSI+CiAgICAgICAgICAgIDxzb3VyY2UgbG9jYXRvcj0iMTk4OjUxLTE5ODo1NSIgbmFtZT0idmFsdWUiIHhzaTp0eXBlPSJPcGVyYW5kUmVmIi8+CiAgICAgICAgIDwvZXhwcmVzc2lvbj4KICAgICAgICAgPG9wZXJhbmQgbmFtZT0idmFsdWUiPgogICAgICAgICAgICA8b3BlcmFuZFR5cGVTcGVjaWZpZXIgbG9jYXRvcj0iMTk4OjMyLTE5ODo0NyIgbmFtZT0iZmhpcjpNZWRpY2F0aW9uU3RhdHVzIiB4c2k6dHlwZT0iTmFtZWRUeXBlU3BlY2lmaWVyIi8+CiAgICAgICAgIDwvb3BlcmFuZD4KICAgICAgPC9kZWY+CiAgICAgIDxkZWYgbG9jYXRvcj0iMTk5OjEtMTk5OjcyIiBuYW1lPSJUb1N0cmluZyIgY29udGV4dD0iUGF0aWVudCIgYWNjZXNzTGV2ZWw9IlB1YmxpYyIgeHNpOnR5cGU9IkZ1bmN0aW9uRGVmIj4KICAgICAgICAgPGV4cHJlc3Npb24gbG9jYXRvcj0iMTk5OjYyLTE5OTo3MiIgcGF0aD0idmFsdWUiIHhzaTp0eXBlPSJQcm9wZXJ0eSI+CiAgICAgICAgICAgIDxzb3VyY2UgbG9jYXRvcj0iMTk5OjYyLTE5OTo2NiIgbmFtZT0idmFsdWUiIHhzaTp0eXBlPSJPcGVyYW5kUmVmIi8+CiAgICAgICAgIDwvZXhwcmVzc2lvbj4KICAgICAgICAgPG9wZXJhbmQgbmFtZT0idmFsdWUiPgogICAgICAgICAgICA8b3BlcmFuZFR5cGVTcGVjaWZpZXIgbG9jYXRvcj0iMTk5OjMyLTE5OTo1OCIgbmFtZT0iZmhpcjpNZXNzYWdlU2lnbmlmaWNhbmNlQ2F0ZWdvcnkiIHhzaTp0eXBlPSJOYW1lZFR5cGVTcGVjaWZpZXIiLz4KICAgICAgICAgPC9vcGVyYW5kPgogICAgICA8L2RlZj4KICAgICAgPGRlZiBsb2NhdG9yPSIyMDA6MS0yMDA6NzUiIG5hbWU9IlRvU3RyaW5nIiBjb250ZXh0PSJQYXRpZW50IiBhY2Nlc3NMZXZlbD0iUHVibGljIiB4c2k6dHlwZT0iRnVuY3Rpb25EZWYiPgogICAgICAgICA8ZXhwcmVzc2lvbiBsb2NhdG9yPSIyMDA6NjUtMjAwOjc1IiBwYXRoPSJ2YWx1ZSIgeHNpOnR5cGU9IlByb3BlcnR5Ij4KICAgICAgICAgICAgPHNvdXJjZSBsb2NhdG9yPSIyMDA6NjUtMjAwOjY5IiBuYW1lPSJ2YWx1ZSIgeHNpOnR5cGU9Ik9wZXJhbmRSZWYiLz4KICAgICAgICAgPC9leHByZXNzaW9uPgogICAgICAgICA8b3BlcmFuZCBuYW1lPSJ2YWx1ZSI+CiAgICAgICAgICAgIDxvcGVyYW5kVHlwZVNwZWNpZmllciBsb2NhdG9yPSIyMDA6MzItMjAwOjYxIiBuYW1lPSJmaGlyOk1lc3NhZ2VoZWFkZXJfUmVzcG9uc2VfUmVxdWVzdCIgeHNpOnR5cGU9Ik5hbWVkVHlwZVNwZWNpZmllciIvPgogICAgICAgICA8L29wZXJhbmQ+CiAgICAgIDwvZGVmPgogICAgICA8ZGVmIGxvY2F0b3I9IjIwMToxLTIwMTo1MyIgbmFtZT0iVG9TdHJpbmciIGNvbnRleHQ9IlBhdGllbnQiIGFjY2Vzc0xldmVsPSJQdWJsaWMiIHhzaTp0eXBlPSJGdW5jdGlvbkRlZiI+CiAgICAgICAgIDxleHByZXNzaW9uIGxvY2F0b3I9IjIwMTo0My0yMDE6NTMiIHBhdGg9InZhbHVlIiB4c2k6dHlwZT0iUHJvcGVydHkiPgogICAgICAgICAgICA8c291cmNlIGxvY2F0b3I9IjIwMTo0My0yMDE6NDciIG5hbWU9InZhbHVlIiB4c2k6dHlwZT0iT3BlcmFuZFJlZiIvPgogICAgICAgICA8L2V4cHJlc3Npb24+CiAgICAgICAgIDxvcGVyYW5kIG5hbWU9InZhbHVlIj4KICAgICAgICAgICAgPG9wZXJhbmRUeXBlU3BlY2lmaWVyIGxvY2F0b3I9IjIwMTozMi0yMDE6MzkiIG5hbWU9ImZoaXI6TWltZVR5cGUiIHhzaTp0eXBlPSJOYW1lZFR5cGVTcGVjaWZpZXIiLz4KICAgICAgICAgPC9vcGVyYW5kPgogICAgICA8L2RlZj4KICAgICAgPGRlZiBsb2NhdG9yPSIyMDI6MS0yMDI6NTIiIG5hbWU9IlRvU3RyaW5nIiBjb250ZXh0PSJQYXRpZW50IiBhY2Nlc3NMZXZlbD0iUHVibGljIiB4c2k6dHlwZT0iRnVuY3Rpb25EZWYiPgogICAgICAgICA8ZXhwcmVzc2lvbiBsb2NhdG9yPSIyMDI6NDItMjAyOjUyIiBwYXRoPSJ2YWx1ZSIgeHNpOnR5cGU9IlByb3BlcnR5Ij4KICAgICAgICAgICAgPHNvdXJjZSBsb2NhdG9yPSIyMDI6NDItMjAyOjQ2IiBuYW1lPSJ2YWx1ZSIgeHNpOnR5cGU9Ik9wZXJhbmRSZWYiLz4KICAgICAgICAgPC9leHByZXNzaW9uPgogICAgICAgICA8b3BlcmFuZCBuYW1lPSJ2YWx1ZSI+CiAgICAgICAgICAgIDxvcGVyYW5kVHlwZVNwZWNpZmllciBsb2NhdG9yPSIyMDI6MzItMjAyOjM4IiBuYW1lPSJmaGlyOk5hbWVVc2UiIHhzaTp0eXBlPSJOYW1lZFR5cGVTcGVjaWZpZXIiLz4KICAgICAgICAgPC9vcGVyYW5kPgogICAgICA8L2RlZj4KICAgICAgPGRlZiBsb2NhdG9yPSIyMDM6MS0yMDM6NzEiIG5hbWU9IlRvU3RyaW5nIiBjb250ZXh0PSJQYXRpZW50IiBhY2Nlc3NMZXZlbD0iUHVibGljIiB4c2k6dHlwZT0iRnVuY3Rpb25EZWYiPgogICAgICAgICA8ZXhwcmVzc2lvbiBsb2NhdG9yPSIyMDM6NjEtMjAzOjcxIiBwYXRoPSJ2YWx1ZSIgeHNpOnR5cGU9IlByb3BlcnR5Ij4KICAgICAgICAgICAgPHNvdXJjZSBsb2NhdG9yPSIyMDM6NjEtMjAzOjY1IiBuYW1lPSJ2YWx1ZSIgeHNpOnR5cGU9Ik9wZXJhbmRSZWYiLz4KICAgICAgICAgPC9leHByZXNzaW9uPgogICAgICAgICA8b3BlcmFuZCBuYW1lPSJ2YWx1ZSI+CiAgICAgICAgICAgIDxvcGVyYW5kVHlwZVNwZWNpZmllciBsb2NhdG9yPSIyMDM6MzItMjAzOjU3IiBuYW1lPSJmaGlyOk5hbWluZ1N5c3RlbUlkZW50aWZpZXJUeXBlIiB4c2k6dHlwZT0iTmFtZWRUeXBlU3BlY2lmaWVyIi8+CiAgICAgICAgIDwvb3BlcmFuZD4KICAgICAgPC9kZWY+CiAgICAgIDxkZWYgbG9jYXRvcj0iMjA0OjEtMjA0OjYxIiBuYW1lPSJUb1N0cmluZyIgY29udGV4dD0iUGF0aWVudCIgYWNjZXNzTGV2ZWw9IlB1YmxpYyIgeHNpOnR5cGU9IkZ1bmN0aW9uRGVmIj4KICAgICAgICAgPGV4cHJlc3Npb24gbG9jYXRvcj0iMjA0OjUxLTIwNDo2MSIgcGF0aD0idmFsdWUiIHhzaTp0eXBlPSJQcm9wZXJ0eSI+CiAgICAgICAgICAgIDxzb3VyY2UgbG9jYXRvcj0iMjA0OjUxLTIwNDo1NSIgbmFtZT0idmFsdWUiIHhzaTp0eXBlPSJPcGVyYW5kUmVmIi8+CiAgICAgICAgIDwvZXhwcmVzc2lvbj4KICAgICAgICAgPG9wZXJhbmQgbmFtZT0idmFsdWUiPgogICAgICAgICAgICA8b3BlcmFuZFR5cGVTcGVjaWZpZXIgbG9jYXRvcj0iMjA0OjMyLTIwNDo0NyIgbmFtZT0iZmhpcjpOYW1pbmdTeXN0ZW1UeXBlIiB4c2k6dHlwZT0iTmFtZWRUeXBlU3BlY2lmaWVyIi8+CiAgICAgICAgIDwvb3BlcmFuZD4KICAgICAgPC9kZWY+CiAgICAgIDxkZWYgbG9jYXRvcj0iMjA1OjEtMjA1OjYwIiBuYW1lPSJUb1N0cmluZyIgY29udGV4dD0iUGF0aWVudCIgYWNjZXNzTGV2ZWw9IlB1YmxpYyIgeHNpOnR5cGU9IkZ1bmN0aW9uRGVmIj4KICAgICAgICAgPGV4cHJlc3Npb24gbG9jYXRvcj0iMjA1OjUwLTIwNTo2MCIgcGF0aD0idmFsdWUiIHhzaTp0eXBlPSJQcm9wZXJ0eSI+CiAgICAgICAgICAgIDxzb3VyY2UgbG9jYXRvcj0iMjA1OjUwLTIwNTo1NCIgbmFtZT0idmFsdWUiIHhzaTp0eXBlPSJPcGVyYW5kUmVmIi8+CiAgICAgICAgIDwvZXhwcmVzc2lvbj4KICAgICAgICAgPG9wZXJhbmQgbmFtZT0idmFsdWUiPgogICAgICAgICAgICA8b3BlcmFuZFR5cGVTcGVjaWZpZXIgbG9jYXRvcj0iMjA1OjMyLTIwNTo0NiIgbmFtZT0iZmhpcjpOYXJyYXRpdmVTdGF0dXMiIHhzaTp0eXBlPSJOYW1lZFR5cGVTcGVjaWZpZXIiLz4KICAgICAgICAgPC9vcGVyYW5kPgogICAgICA8L2RlZj4KICAgICAgPGRlZiBsb2NhdG9yPSIyMDY6MS0yMDY6NTMiIG5hbWU9IlRvU3RyaW5nIiBjb250ZXh0PSJQYXRpZW50IiBhY2Nlc3NMZXZlbD0iUHVibGljIiB4c2k6dHlwZT0iRnVuY3Rpb25EZWYiPgogICAgICAgICA8ZXhwcmVzc2lvbiBsb2NhdG9yPSIyMDY6NDMtMjA2OjUzIiBwYXRoPSJ2YWx1ZSIgeHNpOnR5cGU9IlByb3BlcnR5Ij4KICAgICAgICAgICAgPHNvdXJjZSBsb2NhdG9yPSIyMDY6NDMtMjA2OjQ3IiBuYW1lPSJ2YWx1ZSIgeHNpOnR5cGU9Ik9wZXJhbmRSZWYiLz4KICAgICAgICAgPC9leHByZXNzaW9uPgogICAgICAgICA8b3BlcmFuZCBuYW1lPSJ2YWx1ZSI+CiAgICAgICAgICAgIDxvcGVyYW5kVHlwZVNwZWNpZmllciBsb2NhdG9yPSIyMDY6MzItMjA2OjM5IiBuYW1lPSJmaGlyOk5vdGVUeXBlIiB4c2k6dHlwZT0iTmFtZWRUeXBlU3BlY2lmaWVyIi8+CiAgICAgICAgIDwvb3BlcmFuZD4KICAgICAgPC9kZWY+CiAgICAgIDxkZWYgbG9jYXRvcj0iMjA3OjEtMjA3OjY2IiBuYW1lPSJUb1N0cmluZyIgY29udGV4dD0iUGF0aWVudCIgYWNjZXNzTGV2ZWw9IlB1YmxpYyIgeHNpOnR5cGU9IkZ1bmN0aW9uRGVmIj4KICAgICAgICAgPGV4cHJlc3Npb24gbG9jYXRvcj0iMjA3OjU2LTIwNzo2NiIgcGF0aD0idmFsdWUiIHhzaTp0eXBlPSJQcm9wZXJ0eSI+CiAgICAgICAgICAgIDxzb3VyY2UgbG9jYXRvcj0iMjA3OjU2LTIwNzo2MCIgbmFtZT0idmFsdWUiIHhzaTp0eXBlPSJPcGVyYW5kUmVmIi8+CiAgICAgICAgIDwvZXhwcmVzc2lvbj4KICAgICAgICAgPG9wZXJhbmQgbmFtZT0idmFsdWUiPgogICAgICAgICAgICA8b3BlcmFuZFR5cGVTcGVjaWZpZXIgbG9jYXRvcj0iMjA3OjMyLTIwNzo1MiIgbmFtZT0iZmhpcjpOdXRyaXRpaW9uT3JkZXJJbnRlbnQiIHhzaTp0eXBlPSJOYW1lZFR5cGVTcGVjaWZpZXIiLz4KICAgICAgICAgPC9vcGVyYW5kPgogICAgICA8L2RlZj4KICAgICAgPGRlZiBsb2NhdG9yPSIyMDg6MS0yMDg6NjUiIG5hbWU9IlRvU3RyaW5nIiBjb250ZXh0PSJQYXRpZW50IiBhY2Nlc3NMZXZlbD0iUHVibGljIiB4c2k6dHlwZT0iRnVuY3Rpb25EZWYiPgogICAgICAgICA8ZXhwcmVzc2lvbiBsb2NhdG9yPSIyMDg6NTUtMjA4OjY1IiBwYXRoPSJ2YWx1ZSIgeHNpOnR5cGU9IlByb3BlcnR5Ij4KICAgICAgICAgICAgPHNvdXJjZSBsb2NhdG9yPSIyMDg6NTUtMjA4OjU5IiBuYW1lPSJ2YWx1ZSIgeHNpOnR5cGU9Ik9wZXJhbmRSZWYiLz4KICAgICAgICAgPC9leHByZXNzaW9uPgogICAgICAgICA8b3BlcmFuZCBuYW1lPSJ2YWx1ZSI+CiAgICAgICAgICAgIDxvcGVyYW5kVHlwZVNwZWNpZmllciBsb2NhdG9yPSIyMDg6MzItMjA4OjUxIiBuYW1lPSJmaGlyOk51dHJpdGlvbk9yZGVyU3RhdHVzIiB4c2k6dHlwZT0iTmFtZWRUeXBlU3BlY2lmaWVyIi8+CiAgICAgICAgIDwvb3BlcmFuZD4KICAgICAgPC9kZWY+CiAgICAgIDxkZWYgbG9jYXRvcj0iMjA5OjEtMjA5OjY0IiBuYW1lPSJUb1N0cmluZyIgY29udGV4dD0iUGF0aWVudCIgYWNjZXNzTGV2ZWw9IlB1YmxpYyIgeHNpOnR5cGU9IkZ1bmN0aW9uRGVmIj4KICAgICAgICAgPGV4cHJlc3Npb24gbG9jYXRvcj0iMjA5OjU0LTIwOTo2NCIgcGF0aD0idmFsdWUiIHhzaTp0eXBlPSJQcm9wZXJ0eSI+CiAgICAgICAgICAgIDxzb3VyY2UgbG9jYXRvcj0iMjA5OjU0LTIwOTo1OCIgbmFtZT0idmFsdWUiIHhzaTp0eXBlPSJPcGVyYW5kUmVmIi8+CiAgICAgICAgIDwvZXhwcmVzc2lvbj4KICAgICAgICAgPG9wZXJhbmQgbmFtZT0idmFsdWUiPgogICAgICAgICAgICA8b3BlcmFuZFR5cGVTcGVjaWZpZXIgbG9jYXRvcj0iMjA5OjMyLTIwOTo1MCIgbmFtZT0iZmhpcjpPYnNlcnZhdGlvbkRhdGFUeXBlIiB4c2k6dHlwZT0iTmFtZWRUeXBlU3BlY2lmaWVyIi8+CiAgICAgICAgIDwvb3BlcmFuZD4KICAgICAgPC9kZWY+CiAgICAgIDxkZWYgbG9jYXRvcj0iMjEwOjEtMjEwOjY5IiBuYW1lPSJUb1N0cmluZyIgY29udGV4dD0iUGF0aWVudCIgYWNjZXNzTGV2ZWw9IlB1YmxpYyIgeHNpOnR5cGU9IkZ1bmN0aW9uRGVmIj4KICAgICAgICAgPGV4cHJlc3Npb24gbG9jYXRvcj0iMjEwOjU5LTIxMDo2OSIgcGF0aD0idmFsdWUiIHhzaTp0eXBlPSJQcm9wZXJ0eSI+CiAgICAgICAgICAgIDxzb3VyY2UgbG9jYXRvcj0iMjEwOjU5LTIxMDo2MyIgbmFtZT0idmFsdWUiIHhzaTp0eXBlPSJPcGVyYW5kUmVmIi8+CiAgICAgICAgIDwvZXhwcmVzc2lvbj4KICAgICAgICAgPG9wZXJhbmQgbmFtZT0idmFsdWUiPgogICAgICAgICAgICA8b3BlcmFuZFR5cGVTcGVjaWZpZXIgbG9jYXRvcj0iMjEwOjMyLTIxMDo1NSIgbmFtZT0iZmhpcjpPYnNlcnZhdGlvblJhbmdlQ2F0ZWdvcnkiIHhzaTp0eXBlPSJOYW1lZFR5cGVTcGVjaWZpZXIiLz4KICAgICAgICAgPC9vcGVyYW5kPgogICAgICA8L2RlZj4KICAgICAgPGRlZiBsb2NhdG9yPSIyMTE6MS0yMTE6NjIiIG5hbWU9IlRvU3RyaW5nIiBjb250ZXh0PSJQYXRpZW50IiBhY2Nlc3NMZXZlbD0iUHVibGljIiB4c2k6dHlwZT0iRnVuY3Rpb25EZWYiPgogICAgICAgICA8ZXhwcmVzc2lvbiBsb2NhdG9yPSIyMTE6NTItMjExOjYyIiBwYXRoPSJ2YWx1ZSIgeHNpOnR5cGU9IlByb3BlcnR5Ij4KICAgICAgICAgICAgPHNvdXJjZSBsb2NhdG9yPSIyMTE6NTItMjExOjU2IiBuYW1lPSJ2YWx1ZSIgeHNpOnR5cGU9Ik9wZXJhbmRSZWYiLz4KICAgICAgICAgPC9leHByZXNzaW9uPgogICAgICAgICA8b3BlcmFuZCBuYW1lPSJ2YWx1ZSI+CiAgICAgICAgICAgIDxvcGVyYW5kVHlwZVNwZWNpZmllciBsb2NhdG9yPSIyMTE6MzItMjExOjQ4IiBuYW1lPSJmaGlyOk9ic2VydmF0aW9uU3RhdHVzIiB4c2k6dHlwZT0iTmFtZWRUeXBlU3BlY2lmaWVyIi8+CiAgICAgICAgIDwvb3BlcmFuZD4KICAgICAgPC9kZWY+CiAgICAgIDxkZWYgbG9jYXRvcj0iMjEyOjEtMjEyOjU4IiBuYW1lPSJUb1N0cmluZyIgY29udGV4dD0iUGF0aWVudCIgYWNjZXNzTGV2ZWw9IlB1YmxpYyIgeHNpOnR5cGU9IkZ1bmN0aW9uRGVmIj4KICAgICAgICAgPGV4cHJlc3Npb24gbG9jYXRvcj0iMjEyOjQ4LTIxMjo1OCIgcGF0aD0idmFsdWUiIHhzaTp0eXBlPSJQcm9wZXJ0eSI+CiAgICAgICAgICAgIDxzb3VyY2UgbG9jYXRvcj0iMjEyOjQ4LTIxMjo1MiIgbmFtZT0idmFsdWUiIHhzaTp0eXBlPSJPcGVyYW5kUmVmIi8+CiAgICAgICAgIDwvZXhwcmVzc2lvbj4KICAgICAgICAgPG9wZXJhbmQgbmFtZT0idmFsdWUiPgogICAgICAgICAgICA8b3BlcmFuZFR5cGVTcGVjaWZpZXIgbG9jYXRvcj0iMjEyOjMyLTIxMjo0NCIgbmFtZT0iZmhpcjpPcGVyYXRpb25LaW5kIiB4c2k6dHlwZT0iTmFtZWRUeXBlU3BlY2lmaWVyIi8+CiAgICAgICAgIDwvb3BlcmFuZD4KICAgICAgPC9kZWY+CiAgICAgIDxkZWYgbG9jYXRvcj0iMjEzOjEtMjEzOjY2IiBuYW1lPSJUb1N0cmluZyIgY29udGV4dD0iUGF0aWVudCIgYWNjZXNzTGV2ZWw9IlB1YmxpYyIgeHNpOnR5cGU9IkZ1bmN0aW9uRGVmIj4KICAgICAgICAgPGV4cHJlc3Npb24gbG9jYXRvcj0iMjEzOjU2LTIxMzo2NiIgcGF0aD0idmFsdWUiIHhzaTp0eXBlPSJQcm9wZXJ0eSI+CiAgICAgICAgICAgIDxzb3VyY2UgbG9jYXRvcj0iMjEzOjU2LTIxMzo2MCIgbmFtZT0idmFsdWUiIHhzaTp0eXBlPSJPcGVyYW5kUmVmIi8+CiAgICAgICAgIDwvZXhwcmVzc2lvbj4KICAgICAgICAgPG9wZXJhbmQgbmFtZT0idmFsdWUiPgogICAgICAgICAgICA8b3BlcmFuZFR5cGVTcGVjaWZpZXIgbG9jYXRvcj0iMjEzOjMyLTIxMzo1MiIgbmFtZT0iZmhpcjpPcGVyYXRpb25QYXJhbWV0ZXJVc2UiIHhzaTp0eXBlPSJOYW1lZFR5cGVTcGVjaWZpZXIiLz4KICAgICAgICAgPC9vcGVyYW5kPgogICAgICA8L2RlZj4KICAgICAgPGRlZiBsb2NhdG9yPSIyMTQ6MS0yMTQ6NjAiIG5hbWU9IlRvU3RyaW5nIiBjb250ZXh0PSJQYXRpZW50IiBhY2Nlc3NMZXZlbD0iUHVibGljIiB4c2k6dHlwZT0iRnVuY3Rpb25EZWYiPgogICAgICAgICA8ZXhwcmVzc2lvbiBsb2NhdG9yPSIyMTQ6NTAtMjE0OjYwIiBwYXRoPSJ2YWx1ZSIgeHNpOnR5cGU9IlByb3BlcnR5Ij4KICAgICAgICAgICAgPHNvdXJjZSBsb2NhdG9yPSIyMTQ6NTAtMjE0OjU0IiBuYW1lPSJ2YWx1ZSIgeHNpOnR5cGU9Ik9wZXJhbmRSZWYiLz4KICAgICAgICAgPC9leHByZXNzaW9uPgogICAgICAgICA8b3BlcmFuZCBuYW1lPSJ2YWx1ZSI+CiAgICAgICAgICAgIDxvcGVyYW5kVHlwZVNwZWNpZmllciBsb2NhdG9yPSIyMTQ6MzItMjE0OjQ2IiBuYW1lPSJmaGlyOk9yaWVudGF0aW9uVHlwZSIgeHNpOnR5cGU9Ik5hbWVkVHlwZVNwZWNpZmllciIvPgogICAgICAgICA8L29wZXJhbmQ+CiAgICAgIDwvZGVmPgogICAgICA8ZGVmIGxvY2F0b3I9IjIxNToxLTIxNTo1NyIgbmFtZT0iVG9TdHJpbmciIGNvbnRleHQ9IlBhdGllbnQiIGFjY2Vzc0xldmVsPSJQdWJsaWMiIHhzaTp0eXBlPSJGdW5jdGlvbkRlZiI+CiAgICAgICAgIDxleHByZXNzaW9uIGxvY2F0b3I9IjIxNTo0Ny0yMTU6NTciIHBhdGg9InZhbHVlIiB4c2k6dHlwZT0iUHJvcGVydHkiPgogICAgICAgICAgICA8c291cmNlIGxvY2F0b3I9IjIxNTo0Ny0yMTU6NTEiIG5hbWU9InZhbHVlIiB4c2k6dHlwZT0iT3BlcmFuZFJlZiIvPgogICAgICAgICA8L2V4cHJlc3Npb24+CiAgICAgICAgIDxvcGVyYW5kIG5hbWU9InZhbHVlIj4KICAgICAgICAgICAgPG9wZXJhbmRUeXBlU3BlY2lmaWVyIGxvY2F0b3I9IjIxNTozMi0yMTU6NDMiIG5hbWU9ImZoaXI6UGFyYW1ldGVyVXNlIiB4c2k6dHlwZT0iTmFtZWRUeXBlU3BlY2lmaWVyIi8+CiAgICAgICAgIDwvb3BlcmFuZD4KICAgICAgPC9kZWY+CiAgICAgIDxkZWYgbG9jYXRvcj0iMjE2OjEtMjE2OjY0IiBuYW1lPSJUb1N0cmluZyIgY29udGV4dD0iUGF0aWVudCIgYWNjZXNzTGV2ZWw9IlB1YmxpYyIgeHNpOnR5cGU9IkZ1bmN0aW9uRGVmIj4KICAgICAgICAgPGV4cHJlc3Npb24gbG9jYXRvcj0iMjE2OjU0LTIxNjo2NCIgcGF0aD0idmFsdWUiIHhzaTp0eXBlPSJQcm9wZXJ0eSI+CiAgICAgICAgICAgIDxzb3VyY2UgbG9jYXRvcj0iMjE2OjU0LTIxNjo1OCIgbmFtZT0idmFsdWUiIHhzaTp0eXBlPSJPcGVyYW5kUmVmIi8+CiAgICAgICAgIDwvZXhwcmVzc2lvbj4KICAgICAgICAgPG9wZXJhbmQgbmFtZT0idmFsdWUiPgogICAgICAgICAgICA8b3BlcmFuZFR5cGVTcGVjaWZpZXIgbG9jYXRvcj0iMjE2OjMyLTIxNjo1MCIgbmFtZT0iZmhpcjpQYXJ0aWNpcGFudFJlcXVpcmVkIiB4c2k6dHlwZT0iTmFtZWRUeXBlU3BlY2lmaWVyIi8+CiAgICAgICAgIDwvb3BlcmFuZD4KICAgICAgPC9kZWY+CiAgICAgIDxkZWYgbG9jYXRvcj0iMjE3OjEtMjE3OjYyIiBuYW1lPSJUb1N0cmluZyIgY29udGV4dD0iUGF0aWVudCIgYWNjZXNzTGV2ZWw9IlB1YmxpYyIgeHNpOnR5cGU9IkZ1bmN0aW9uRGVmIj4KICAgICAgICAgPGV4cHJlc3Npb24gbG9jYXRvcj0iMjE3OjUyLTIxNzo2MiIgcGF0aD0idmFsdWUiIHhzaTp0eXBlPSJQcm9wZXJ0eSI+CiAgICAgICAgICAgIDxzb3VyY2UgbG9jYXRvcj0iMjE3OjUyLTIxNzo1NiIgbmFtZT0idmFsdWUiIHhzaTp0eXBlPSJPcGVyYW5kUmVmIi8+CiAgICAgICAgIDwvZXhwcmVzc2lvbj4KICAgICAgICAgPG9wZXJhbmQgbmFtZT0idmFsdWUiPgogICAgICAgICAgICA8b3BlcmFuZFR5cGVTcGVjaWZpZXIgbG9jYXRvcj0iMjE3OjMyLTIxNzo0OCIgbmFtZT0iZmhpcjpQYXJ0aWNpcGFudFN0YXR1cyIgeHNpOnR5cGU9Ik5hbWVkVHlwZVNwZWNpZmllciIvPgogICAgICAgICA8L29wZXJhbmQ+CiAgICAgIDwvZGVmPgogICAgICA8ZGVmIGxvY2F0b3I9IjIxODoxLTIxODo2NCIgbmFtZT0iVG9TdHJpbmciIGNvbnRleHQ9IlBhdGllbnQiIGFjY2Vzc0xldmVsPSJQdWJsaWMiIHhzaTp0eXBlPSJGdW5jdGlvbkRlZiI+CiAgICAgICAgIDxleHByZXNzaW9uIGxvY2F0b3I9IjIxODo1NC0yMTg6NjQiIHBhdGg9InZhbHVlIiB4c2k6dHlwZT0iUHJvcGVydHkiPgogICAgICAgICAgICA8c291cmNlIGxvY2F0b3I9IjIxODo1NC0yMTg6NTgiIG5hbWU9InZhbHVlIiB4c2k6dHlwZT0iT3BlcmFuZFJlZiIvPgogICAgICAgICA8L2V4cHJlc3Npb24+CiAgICAgICAgIDxvcGVyYW5kIG5hbWU9InZhbHVlIj4KICAgICAgICAgICAgPG9wZXJhbmRUeXBlU3BlY2lmaWVyIGxvY2F0b3I9IjIxODozMi0yMTg6NTAiIG5hbWU9ImZoaXI6UGFydGljaXBhdGlvblN0YXR1cyIgeHNpOnR5cGU9Ik5hbWVkVHlwZVNwZWNpZmllciIvPgogICAgICAgICA8L29wZXJhbmQ+CiAgICAgIDwvZGVmPgogICAgICA8ZGVmIGxvY2F0b3I9IjIxOToxLTIxOTo2NCIgbmFtZT0iVG9TdHJpbmciIGNvbnRleHQ9IlBhdGllbnQiIGFjY2Vzc0xldmVsPSJQdWJsaWMiIHhzaTp0eXBlPSJGdW5jdGlvbkRlZiI+CiAgICAgICAgIDxleHByZXNzaW9uIGxvY2F0b3I9IjIxOTo1NC0yMTk6NjQiIHBhdGg9InZhbHVlIiB4c2k6dHlwZT0iUHJvcGVydHkiPgogICAgICAgICAgICA8c291cmNlIGxvY2F0b3I9IjIxOTo1NC0yMTk6NTgiIG5hbWU9InZhbHVlIiB4c2k6dHlwZT0iT3BlcmFuZFJlZiIvPgogICAgICAgICA8L2V4cHJlc3Npb24+CiAgICAgICAgIDxvcGVyYW5kIG5hbWU9InZhbHVlIj4KICAgICAgICAgICAgPG9wZXJhbmRUeXBlU3BlY2lmaWVyIGxvY2F0b3I9IjIxOTozMi0yMTk6NTAiIG5hbWU9ImZoaXI6UGF5bWVudE5vdGljZVN0YXR1cyIgeHNpOnR5cGU9Ik5hbWVkVHlwZVNwZWNpZmllciIvPgogICAgICAgICA8L29wZXJhbmQ+CiAgICAgIDwvZGVmPgogICAgICA8ZGVmIGxvY2F0b3I9IjIyMDoxLTIyMDo3MiIgbmFtZT0iVG9TdHJpbmciIGNvbnRleHQ9IlBhdGllbnQiIGFjY2Vzc0xldmVsPSJQdWJsaWMiIHhzaTp0eXBlPSJGdW5jdGlvbkRlZiI+CiAgICAgICAgIDxleHByZXNzaW9uIGxvY2F0b3I9IjIyMDo2Mi0yMjA6NzIiIHBhdGg9InZhbHVlIiB4c2k6dHlwZT0iUHJvcGVydHkiPgogICAgICAgICAgICA8c291cmNlIGxvY2F0b3I9IjIyMDo2Mi0yMjA6NjYiIG5hbWU9InZhbHVlIiB4c2k6dHlwZT0iT3BlcmFuZFJlZiIvPgogICAgICAgICA8L2V4cHJlc3Npb24+CiAgICAgICAgIDxvcGVyYW5kIG5hbWU9InZhbHVlIj4KICAgICAgICAgICAgPG9wZXJhbmRUeXBlU3BlY2lmaWVyIGxvY2F0b3I9IjIyMDozMi0yMjA6NTgiIG5hbWU9ImZoaXI6UGF5bWVudFJlY29uY2lsaWF0aW9uU3RhdHVzIiB4c2k6dHlwZT0iTmFtZWRUeXBlU3BlY2lmaWVyIi8+CiAgICAgICAgIDwvb3BlcmFuZD4KICAgICAgPC9kZWY+CiAgICAgIDxkZWYgbG9jYXRvcj0iMjIxOjEtMjIxOjYwIiBuYW1lPSJUb1N0cmluZyIgY29udGV4dD0iUGF0aWVudCIgYWNjZXNzTGV2ZWw9IlB1YmxpYyIgeHNpOnR5cGU9IkZ1bmN0aW9uRGVmIj4KICAgICAgICAgPGV4cHJlc3Npb24gbG9jYXRvcj0iMjIxOjUwLTIyMTo2MCIgcGF0aD0idmFsdWUiIHhzaTp0eXBlPSJQcm9wZXJ0eSI+CiAgICAgICAgICAgIDxzb3VyY2UgbG9jYXRvcj0iMjIxOjUwLTIyMTo1NCIgbmFtZT0idmFsdWUiIHhzaTp0eXBlPSJPcGVyYW5kUmVmIi8+CiAgICAgICAgIDwvZXhwcmVzc2lvbj4KICAgICAgICAgPG9wZXJhbmQgbmFtZT0idmFsdWUiPgogICAgICAgICAgICA8b3BlcmFuZFR5cGVTcGVjaWZpZXIgbG9jYXRvcj0iMjIxOjMyLTIyMTo0NiIgbmFtZT0iZmhpcjpQcm9jZWR1cmVTdGF0dXMiIHhzaTp0eXBlPSJOYW1lZFR5cGVTcGVjaWZpZXIiLz4KICAgICAgICAgPC9vcGVyYW5kPgogICAgICA8L2RlZj4KICAgICAgPGRlZiBsb2NhdG9yPSIyMjI6MS0yMjI6NjciIG5hbWU9IlRvU3RyaW5nIiBjb250ZXh0PSJQYXRpZW50IiBhY2Nlc3NMZXZlbD0iUHVibGljIiB4c2k6dHlwZT0iRnVuY3Rpb25EZWYiPgogICAgICAgICA8ZXhwcmVzc2lvbiBsb2NhdG9yPSIyMjI6NTctMjIyOjY3IiBwYXRoPSJ2YWx1ZSIgeHNpOnR5cGU9IlByb3BlcnR5Ij4KICAgICAgICAgICAgPHNvdXJjZSBsb2NhdG9yPSIyMjI6NTctMjIyOjYxIiBuYW1lPSJ2YWx1ZSIgeHNpOnR5cGU9Ik9wZXJhbmRSZWYiLz4KICAgICAgICAgPC9leHByZXNzaW9uPgogICAgICAgICA8b3BlcmFuZCBuYW1lPSJ2YWx1ZSI+CiAgICAgICAgICAgIDxvcGVyYW5kVHlwZVNwZWNpZmllciBsb2NhdG9yPSIyMjI6MzItMjIyOjUzIiBuYW1lPSJmaGlyOlByb3BlcnR5UmVwcmVzZW50YXRpb24iIHhzaTp0eXBlPSJOYW1lZFR5cGVTcGVjaWZpZXIiLz4KICAgICAgICAgPC9vcGVyYW5kPgogICAgICA8L2RlZj4KICAgICAgPGRlZiBsb2NhdG9yPSIyMjM6MS0yMjM6NTciIG5hbWU9IlRvU3RyaW5nIiBjb250ZXh0PSJQYXRpZW50IiBhY2Nlc3NMZXZlbD0iUHVibGljIiB4c2k6dHlwZT0iRnVuY3Rpb25EZWYiPgogICAgICAgICA8ZXhwcmVzc2lvbiBsb2NhdG9yPSIyMjM6NDctMjIzOjU3IiBwYXRoPSJ2YWx1ZSIgeHNpOnR5cGU9IlByb3BlcnR5Ij4KICAgICAgICAgICAgPHNvdXJjZSBsb2NhdG9yPSIyMjM6NDctMjIzOjUxIiBuYW1lPSJ2YWx1ZSIgeHNpOnR5cGU9Ik9wZXJhbmRSZWYiLz4KICAgICAgICAgPC9leHByZXNzaW9uPgogICAgICAgICA8b3BlcmFuZCBuYW1lPSJ2YWx1ZSI+CiAgICAgICAgICAgIDxvcGVyYW5kVHlwZVNwZWNpZmllciBsb2NhdG9yPSIyMjM6MzItMjIzOjQzIiBuYW1lPSJmaGlyOlByb3BlcnR5VHlwZSIgeHNpOnR5cGU9Ik5hbWVkVHlwZVNwZWNpZmllciIvPgogICAgICAgICA8L29wZXJhbmQ+CiAgICAgIDwvZGVmPgogICAgICA8ZGVmIGxvY2F0b3I9IjIyNDoxLTIyNDo2NSIgbmFtZT0iVG9TdHJpbmciIGNvbnRleHQ9IlBhdGllbnQiIGFjY2Vzc0xldmVsPSJQdWJsaWMiIHhzaTp0eXBlPSJGdW5jdGlvbkRlZiI+CiAgICAgICAgIDxleHByZXNzaW9uIGxvY2F0b3I9IjIyNDo1NS0yMjQ6NjUiIHBhdGg9InZhbHVlIiB4c2k6dHlwZT0iUHJvcGVydHkiPgogICAgICAgICAgICA8c291cmNlIGxvY2F0b3I9IjIyNDo1NS0yMjQ6NTkiIG5hbWU9InZhbHVlIiB4c2k6dHlwZT0iT3BlcmFuZFJlZiIvPgogICAgICAgICA8L2V4cHJlc3Npb24+CiAgICAgICAgIDxvcGVyYW5kIG5hbWU9InZhbHVlIj4KICAgICAgICAgICAgPG9wZXJhbmRUeXBlU3BlY2lmaWVyIGxvY2F0b3I9IjIyNDozMi0yMjQ6NTEiIG5hbWU9ImZoaXI6UHJvdmVuYW5jZUVudGl0eVJvbGUiIHhzaTp0eXBlPSJOYW1lZFR5cGVTcGVjaWZpZXIiLz4KICAgICAgICAgPC9vcGVyYW5kPgogICAgICA8L2RlZj4KICAgICAgPGRlZiBsb2NhdG9yPSIyMjU6MS0yMjU6NjIiIG5hbWU9IlRvU3RyaW5nIiBjb250ZXh0PSJQYXRpZW50IiBhY2Nlc3NMZXZlbD0iUHVibGljIiB4c2k6dHlwZT0iRnVuY3Rpb25EZWYiPgogICAgICAgICA8ZXhwcmVzc2lvbiBsb2NhdG9yPSIyMjU6NTItMjI1OjYyIiBwYXRoPSJ2YWx1ZSIgeHNpOnR5cGU9IlByb3BlcnR5Ij4KICAgICAgICAgICAgPHNvdXJjZSBsb2NhdG9yPSIyMjU6NTItMjI1OjU2IiBuYW1lPSJ2YWx1ZSIgeHNpOnR5cGU9Ik9wZXJhbmRSZWYiLz4KICAgICAgICAgPC9leHByZXNzaW9uPgogICAgICAgICA8b3BlcmFuZCBuYW1lPSJ2YWx1ZSI+CiAgICAgICAgICAgIDxvcGVyYW5kVHlwZVNwZWNpZmllciBsb2NhdG9yPSIyMjU6MzItMjI1OjQ4IiBuYW1lPSJmaGlyOlB1YmxpY2F0aW9uU3RhdHVzIiB4c2k6dHlwZT0iTmFtZWRUeXBlU3BlY2lmaWVyIi8+CiAgICAgICAgIDwvb3BlcmFuZD4KICAgICAgPC9kZWY+CiAgICAgIDxkZWYgbG9jYXRvcj0iMjI2OjEtMjI2OjU2IiBuYW1lPSJUb1N0cmluZyIgY29udGV4dD0iUGF0aWVudCIgYWNjZXNzTGV2ZWw9IlB1YmxpYyIgeHNpOnR5cGU9IkZ1bmN0aW9uRGVmIj4KICAgICAgICAgPGV4cHJlc3Npb24gbG9jYXRvcj0iMjI2OjQ2LTIyNjo1NiIgcGF0aD0idmFsdWUiIHhzaTp0eXBlPSJQcm9wZXJ0eSI+CiAgICAgICAgICAgIDxzb3VyY2UgbG9jYXRvcj0iMjI2OjQ2LTIyNjo1MCIgbmFtZT0idmFsdWUiIHhzaTp0eXBlPSJPcGVyYW5kUmVmIi8+CiAgICAgICAgIDwvZXhwcmVzc2lvbj4KICAgICAgICAgPG9wZXJhbmQgbmFtZT0idmFsdWUiPgogICAgICAgICAgICA8b3BlcmFuZFR5cGVTcGVjaWZpZXIgbG9jYXRvcj0iMjI2OjMyLTIyNjo0MiIgbmFtZT0iZmhpcjpRdWFsaXR5VHlwZSIgeHNpOnR5cGU9Ik5hbWVkVHlwZVNwZWNpZmllciIvPgogICAgICAgICA8L29wZXJhbmQ+CiAgICAgIDwvZGVmPgogICAgICA8ZGVmIGxvY2F0b3I9IjIyNzoxLTIyNzo2MyIgbmFtZT0iVG9TdHJpbmciIGNvbnRleHQ9IlBhdGllbnQiIGFjY2Vzc0xldmVsPSJQdWJsaWMiIHhzaTp0eXBlPSJGdW5jdGlvbkRlZiI+CiAgICAgICAgIDxleHByZXNzaW9uIGxvY2F0b3I9IjIyNzo1My0yMjc6NjMiIHBhdGg9InZhbHVlIiB4c2k6dHlwZT0iUHJvcGVydHkiPgogICAgICAgICAgICA8c291cmNlIGxvY2F0b3I9IjIyNzo1My0yMjc6NTciIG5hbWU9InZhbHVlIiB4c2k6dHlwZT0iT3BlcmFuZFJlZiIvPgogICAgICAgICA8L2V4cHJlc3Npb24+CiAgICAgICAgIDxvcGVyYW5kIG5hbWU9InZhbHVlIj4KICAgICAgICAgICAgPG9wZXJhbmRUeXBlU3BlY2lmaWVyIGxvY2F0b3I9IjIyNzozMi0yMjc6NDkiIG5hbWU9ImZoaXI6UXVhbnRpdHlDb21wYXJhdG9yIiB4c2k6dHlwZT0iTmFtZWRUeXBlU3BlY2lmaWVyIi8+CiAgICAgICAgIDwvb3BlcmFuZD4KICAgICAgPC9kZWY+CiAgICAgIDxkZWYgbG9jYXRvcj0iMjI4OjEtMjI4OjcwIiBuYW1lPSJUb1N0cmluZyIgY29udGV4dD0iUGF0aWVudCIgYWNjZXNzTGV2ZWw9IlB1YmxpYyIgeHNpOnR5cGU9IkZ1bmN0aW9uRGVmIj4KICAgICAgICAgPGV4cHJlc3Npb24gbG9jYXRvcj0iMjI4OjYwLTIyODo3MCIgcGF0aD0idmFsdWUiIHhzaTp0eXBlPSJQcm9wZXJ0eSI+CiAgICAgICAgICAgIDxzb3VyY2UgbG9jYXRvcj0iMjI4OjYwLTIyODo2NCIgbmFtZT0idmFsdWUiIHhzaTp0eXBlPSJPcGVyYW5kUmVmIi8+CiAgICAgICAgIDwvZXhwcmVzc2lvbj4KICAgICAgICAgPG9wZXJhbmQgbmFtZT0idmFsdWUiPgogICAgICAgICAgICA8b3BlcmFuZFR5cGVTcGVjaWZpZXIgbG9jYXRvcj0iMjI4OjMyLTIyODo1NiIgbmFtZT0iZmhpcjpRdWVzdGlvbm5haXJlSXRlbU9wZXJhdG9yIiB4c2k6dHlwZT0iTmFtZWRUeXBlU3BlY2lmaWVyIi8+CiAgICAgICAgIDwvb3BlcmFuZD4KICAgICAgPC9kZWY+CiAgICAgIDxkZWYgbG9jYXRvcj0iMjI5OjEtMjI5OjY2IiBuYW1lPSJUb1N0cmluZyIgY29udGV4dD0iUGF0aWVudCIgYWNjZXNzTGV2ZWw9IlB1YmxpYyIgeHNpOnR5cGU9IkZ1bmN0aW9uRGVmIj4KICAgICAgICAgPGV4cHJlc3Npb24gbG9jYXRvcj0iMjI5OjU2LTIyOTo2NiIgcGF0aD0idmFsdWUiIHhzaTp0eXBlPSJQcm9wZXJ0eSI+CiAgICAgICAgICAgIDxzb3VyY2UgbG9jYXRvcj0iMjI5OjU2LTIyOTo2MCIgbmFtZT0idmFsdWUiIHhzaTp0eXBlPSJPcGVyYW5kUmVmIi8+CiAgICAgICAgIDwvZXhwcmVzc2lvbj4KICAgICAgICAgPG9wZXJhbmQgbmFtZT0idmFsdWUiPgogICAgICAgICAgICA8b3BlcmFuZFR5cGVTcGVjaWZpZXIgbG9jYXRvcj0iMjI5OjMyLTIyOTo1MiIgbmFtZT0iZmhpcjpRdWVzdGlvbm5haXJlSXRlbVR5cGUiIHhzaTp0eXBlPSJOYW1lZFR5cGVTcGVjaWZpZXIiLz4KICAgICAgICAgPC9vcGVyYW5kPgogICAgICA8L2RlZj4KICAgICAgPGRlZiBsb2NhdG9yPSIyMzA6MS0yMzA6NzIiIG5hbWU9IlRvU3RyaW5nIiBjb250ZXh0PSJQYXRpZW50IiBhY2Nlc3NMZXZlbD0iUHVibGljIiB4c2k6dHlwZT0iRnVuY3Rpb25EZWYiPgogICAgICAgICA8ZXhwcmVzc2lvbiBsb2NhdG9yPSIyMzA6NjItMjMwOjcyIiBwYXRoPSJ2YWx1ZSIgeHNpOnR5cGU9IlByb3BlcnR5Ij4KICAgICAgICAgICAgPHNvdXJjZSBsb2NhdG9yPSIyMzA6NjItMjMwOjY2IiBuYW1lPSJ2YWx1ZSIgeHNpOnR5cGU9Ik9wZXJhbmRSZWYiLz4KICAgICAgICAgPC9leHByZXNzaW9uPgogICAgICAgICA8b3BlcmFuZCBuYW1lPSJ2YWx1ZSI+CiAgICAgICAgICAgIDxvcGVyYW5kVHlwZVNwZWNpZmllciBsb2NhdG9yPSIyMzA6MzItMjMwOjU4IiBuYW1lPSJmaGlyOlF1ZXN0aW9ubmFpcmVSZXNwb25zZVN0YXR1cyIgeHNpOnR5cGU9Ik5hbWVkVHlwZVNwZWNpZmllciIvPgogICAgICAgICA8L29wZXJhbmQ+CiAgICAgIDwvZGVmPgogICAgICA8ZGVmIGxvY2F0b3I9IjIzMToxLTIzMTo2OCIgbmFtZT0iVG9TdHJpbmciIGNvbnRleHQ9IlBhdGllbnQiIGFjY2Vzc0xldmVsPSJQdWJsaWMiIHhzaTp0eXBlPSJGdW5jdGlvbkRlZiI+CiAgICAgICAgIDxleHByZXNzaW9uIGxvY2F0b3I9IjIzMTo1OC0yMzE6NjgiIHBhdGg9InZhbHVlIiB4c2k6dHlwZT0iUHJvcGVydHkiPgogICAgICAgICAgICA8c291cmNlIGxvY2F0b3I9IjIzMTo1OC0yMzE6NjIiIG5hbWU9InZhbHVlIiB4c2k6dHlwZT0iT3BlcmFuZFJlZiIvPgogICAgICAgICA8L2V4cHJlc3Npb24+CiAgICAgICAgIDxvcGVyYW5kIG5hbWU9InZhbHVlIj4KICAgICAgICAgICAgPG9wZXJhbmRUeXBlU3BlY2lmaWVyIGxvY2F0b3I9IjIzMTozMi0yMzE6NTQiIG5hbWU9ImZoaXI6UmVmZXJlbmNlSGFuZGxpbmdQb2xpY3kiIHhzaTp0eXBlPSJOYW1lZFR5cGVTcGVjaWZpZXIiLz4KICAgICAgICAgPC9vcGVyYW5kPgogICAgICA8L2RlZj4KICAgICAgPGRlZiBsb2NhdG9yPSIyMzI6MS0yMzI6NjYiIG5hbWU9IlRvU3RyaW5nIiBjb250ZXh0PSJQYXRpZW50IiBhY2Nlc3NMZXZlbD0iUHVibGljIiB4c2k6dHlwZT0iRnVuY3Rpb25EZWYiPgogICAgICAgICA8ZXhwcmVzc2lvbiBsb2NhdG9yPSIyMzI6NTYtMjMyOjY2IiBwYXRoPSJ2YWx1ZSIgeHNpOnR5cGU9IlByb3BlcnR5Ij4KICAgICAgICAgICAgPHNvdXJjZSBsb2NhdG9yPSIyMzI6NTYtMjMyOjYwIiBuYW1lPSJ2YWx1ZSIgeHNpOnR5cGU9Ik9wZXJhbmRSZWYiLz4KICAgICAgICAgPC9leHByZXNzaW9uPgogICAgICAgICA8b3BlcmFuZCBuYW1lPSJ2YWx1ZSI+CiAgICAgICAgICAgIDxvcGVyYW5kVHlwZVNwZWNpZmllciBsb2NhdG9yPSIyMzI6MzItMjMyOjUyIiBuYW1lPSJmaGlyOlJlZmVyZW5jZVZlcnNpb25SdWxlcyIgeHNpOnR5cGU9Ik5hbWVkVHlwZVNwZWNpZmllciIvPgogICAgICAgICA8L29wZXJhbmQ+CiAgICAgIDwvZGVmPgogICAgICA8ZGVmIGxvY2F0b3I9IjIzMzoxLTIzMzo2NyIgbmFtZT0iVG9TdHJpbmciIGNvbnRleHQ9IlBhdGllbnQiIGFjY2Vzc0xldmVsPSJQdWJsaWMiIHhzaTp0eXBlPSJGdW5jdGlvbkRlZiI+CiAgICAgICAgIDxleHByZXNzaW9uIGxvY2F0b3I9IjIzMzo1Ny0yMzM6NjciIHBhdGg9InZhbHVlIiB4c2k6dHlwZT0iUHJvcGVydHkiPgogICAgICAgICAgICA8c291cmNlIGxvY2F0b3I9IjIzMzo1Ny0yMzM6NjEiIG5hbWU9InZhbHVlIiB4c2k6dHlwZT0iT3BlcmFuZFJlZiIvPgogICAgICAgICA8L2V4cHJlc3Npb24+CiAgICAgICAgIDxvcGVyYW5kIG5hbWU9InZhbHVlIj4KICAgICAgICAgICAgPG9wZXJhbmRUeXBlU3BlY2lmaWVyIGxvY2F0b3I9IjIzMzozMi0yMzM6NTMiIG5hbWU9ImZoaXI6UmVmZXJyZWREb2N1bWVudFN0YXR1cyIgeHNpOnR5cGU9Ik5hbWVkVHlwZVNwZWNpZmllciIvPgogICAgICAgICA8L29wZXJhbmQ+CiAgICAgIDwvZGVmPgogICAgICA8ZGVmIGxvY2F0b3I9IjIzNDoxLTIzNDo2NCIgbmFtZT0iVG9TdHJpbmciIGNvbnRleHQ9IlBhdGllbnQiIGFjY2Vzc0xldmVsPSJQdWJsaWMiIHhzaTp0eXBlPSJGdW5jdGlvbkRlZiI+CiAgICAgICAgIDxleHByZXNzaW9uIGxvY2F0b3I9IjIzNDo1NC0yMzQ6NjQiIHBhdGg9InZhbHVlIiB4c2k6dHlwZT0iUHJvcGVydHkiPgogICAgICAgICAgICA8c291cmNlIGxvY2F0b3I9IjIzNDo1NC0yMzQ6NTgiIG5hbWU9InZhbHVlIiB4c2k6dHlwZT0iT3BlcmFuZFJlZiIvPgogICAgICAgICA8L2V4cHJlc3Npb24+CiAgICAgICAgIDxvcGVyYW5kIG5hbWU9InZhbHVlIj4KICAgICAgICAgICAgPG9wZXJhbmRUeXBlU3BlY2lmaWVyIGxvY2F0b3I9IjIzNDozMi0yMzQ6NTAiIG5hbWU9ImZoaXI6UmVsYXRlZEFydGlmYWN0VHlwZSIgeHNpOnR5cGU9Ik5hbWVkVHlwZVNwZWNpZmllciIvPgogICAgICAgICA8L29wZXJhbmQ+CiAgICAgIDwvZGVmPgogICAgICA8ZGVmIGxvY2F0b3I9IjIzNToxLTIzNTo2MiIgbmFtZT0iVG9TdHJpbmciIGNvbnRleHQ9IlBhdGllbnQiIGFjY2Vzc0xldmVsPSJQdWJsaWMiIHhzaTp0eXBlPSJGdW5jdGlvbkRlZiI+CiAgICAgICAgIDxleHByZXNzaW9uIGxvY2F0b3I9IjIzNTo1Mi0yMzU6NjIiIHBhdGg9InZhbHVlIiB4c2k6dHlwZT0iUHJvcGVydHkiPgogICAgICAgICAgICA8c291cmNlIGxvY2F0b3I9IjIzNTo1Mi0yMzU6NTYiIG5hbWU9InZhbHVlIiB4c2k6dHlwZT0iT3BlcmFuZFJlZiIvPgogICAgICAgICA8L2V4cHJlc3Npb24+CiAgICAgICAgIDxvcGVyYW5kIG5hbWU9InZhbHVlIj4KICAgICAgICAgICAgPG9wZXJhbmRUeXBlU3BlY2lmaWVyIGxvY2F0b3I9IjIzNTozMi0yMzU6NDgiIG5hbWU9ImZoaXI6UmVtaXR0YW5jZU91dGNvbWUiIHhzaTp0eXBlPSJOYW1lZFR5cGVTcGVjaWZpZXIiLz4KICAgICAgICAgPC9vcGVyYW5kPgogICAgICA8L2RlZj4KICAgICAgPGRlZiBsb2NhdG9yPSIyMzY6MS0yMzY6NTkiIG5hbWU9IlRvU3RyaW5nIiBjb250ZXh0PSJQYXRpZW50IiBhY2Nlc3NMZXZlbD0iUHVibGljIiB4c2k6dHlwZT0iRnVuY3Rpb25EZWYiPgogICAgICAgICA8ZXhwcmVzc2lvbiBsb2NhdG9yPSIyMzY6NDktMjM2OjU5IiBwYXRoPSJ2YWx1ZSIgeHNpOnR5cGU9IlByb3BlcnR5Ij4KICAgICAgICAgICAgPHNvdXJjZSBsb2NhdG9yPSIyMzY6NDktMjM2OjUzIiBuYW1lPSJ2YWx1ZSIgeHNpOnR5cGU9Ik9wZXJhbmRSZWYiLz4KICAgICAgICAgPC9leHByZXNzaW9uPgogICAgICAgICA8b3BlcmFuZCBuYW1lPSJ2YWx1ZSI+CiAgICAgICAgICAgIDxvcGVyYW5kVHlwZVNwZWNpZmllciBsb2NhdG9yPSIyMzY6MzItMjM2OjQ1IiBuYW1lPSJmaGlyOlJlcG9zaXRvcnlUeXBlIiB4c2k6dHlwZT0iTmFtZWRUeXBlU3BlY2lmaWVyIi8+CiAgICAgICAgIDwvb3BlcmFuZD4KICAgICAgPC9kZWY+CiAgICAgIDxkZWYgbG9jYXRvcj0iMjM3OjEtMjM3OjU4IiBuYW1lPSJUb1N0cmluZyIgY29udGV4dD0iUGF0aWVudCIgYWNjZXNzTGV2ZWw9IlB1YmxpYyIgeHNpOnR5cGU9IkZ1bmN0aW9uRGVmIj4KICAgICAgICAgPGV4cHJlc3Npb24gbG9jYXRvcj0iMjM3OjQ4LTIzNzo1OCIgcGF0aD0idmFsdWUiIHhzaTp0eXBlPSJQcm9wZXJ0eSI+CiAgICAgICAgICAgIDxzb3VyY2UgbG9jYXRvcj0iMjM3OjQ4LTIzNzo1MiIgbmFtZT0idmFsdWUiIHhzaTp0eXBlPSJPcGVyYW5kUmVmIi8+CiAgICAgICAgIDwvZXhwcmVzc2lvbj4KICAgICAgICAgPG9wZXJhbmQgbmFtZT0idmFsdWUiPgogICAgICAgICAgICA8b3BlcmFuZFR5cGVTcGVjaWZpZXIgbG9jYXRvcj0iMjM3OjMyLTIzNzo0NCIgbmFtZT0iZmhpcjpSZXF1ZXN0SW50ZW50IiB4c2k6dHlwZT0iTmFtZWRUeXBlU3BlY2lmaWVyIi8+CiAgICAgICAgIDwvb3BlcmFuZD4KICAgICAgPC9kZWY+CiAgICAgIDxkZWYgbG9jYXRvcj0iMjM4OjEtMjM4OjYwIiBuYW1lPSJUb1N0cmluZyIgY29udGV4dD0iUGF0aWVudCIgYWNjZXNzTGV2ZWw9IlB1YmxpYyIgeHNpOnR5cGU9IkZ1bmN0aW9uRGVmIj4KICAgICAgICAgPGV4cHJlc3Npb24gbG9jYXRvcj0iMjM4OjUwLTIzODo2MCIgcGF0aD0idmFsdWUiIHhzaTp0eXBlPSJQcm9wZXJ0eSI+CiAgICAgICAgICAgIDxzb3VyY2UgbG9jYXRvcj0iMjM4OjUwLTIzODo1NCIgbmFtZT0idmFsdWUiIHhzaTp0eXBlPSJPcGVyYW5kUmVmIi8+CiAgICAgICAgIDwvZXhwcmVzc2lvbj4KICAgICAgICAgPG9wZXJhbmQgbmFtZT0idmFsdWUiPgogICAgICAgICAgICA8b3BlcmFuZFR5cGVTcGVjaWZpZXIgbG9jYXRvcj0iMjM4OjMyLTIzODo0NiIgbmFtZT0iZmhpcjpSZXF1ZXN0UHJpb3JpdHkiIHhzaTp0eXBlPSJOYW1lZFR5cGVTcGVjaWZpZXIiLz4KICAgICAgICAgPC9vcGVyYW5kPgogICAgICA8L2RlZj4KICAgICAgPGRlZiBsb2NhdG9yPSIyMzk6MS0yMzk6NTgiIG5hbWU9IlRvU3RyaW5nIiBjb250ZXh0PSJQYXRpZW50IiBhY2Nlc3NMZXZlbD0iUHVibGljIiB4c2k6dHlwZT0iRnVuY3Rpb25EZWYiPgogICAgICAgICA8ZXhwcmVzc2lvbiBsb2NhdG9yPSIyMzk6NDgtMjM5OjU4IiBwYXRoPSJ2YWx1ZSIgeHNpOnR5cGU9IlByb3BlcnR5Ij4KICAgICAgICAgICAgPHNvdXJjZSBsb2NhdG9yPSIyMzk6NDgtMjM5OjUyIiBuYW1lPSJ2YWx1ZSIgeHNpOnR5cGU9Ik9wZXJhbmRSZWYiLz4KICAgICAgICAgPC9leHByZXNzaW9uPgogICAgICAgICA8b3BlcmFuZCBuYW1lPSJ2YWx1ZSI+CiAgICAgICAgICAgIDxvcGVyYW5kVHlwZVNwZWNpZmllciBsb2NhdG9yPSIyMzk6MzItMjM5OjQ0IiBuYW1lPSJmaGlyOlJlcXVlc3RTdGF0dXMiIHhzaTp0eXBlPSJOYW1lZFR5cGVTcGVjaWZpZXIiLz4KICAgICAgICAgPC9vcGVyYW5kPgogICAgICA8L2RlZj4KICAgICAgPGRlZiBsb2NhdG9yPSIyNDA6MS0yNDA6NjQiIG5hbWU9IlRvU3RyaW5nIiBjb250ZXh0PSJQYXRpZW50IiBhY2Nlc3NMZXZlbD0iUHVibGljIiB4c2k6dHlwZT0iRnVuY3Rpb25EZWYiPgogICAgICAgICA8ZXhwcmVzc2lvbiBsb2NhdG9yPSIyNDA6NTQtMjQwOjY0IiBwYXRoPSJ2YWx1ZSIgeHNpOnR5cGU9IlByb3BlcnR5Ij4KICAgICAgICAgICAgPHNvdXJjZSBsb2NhdG9yPSIyNDA6NTQtMjQwOjU4IiBuYW1lPSJ2YWx1ZSIgeHNpOnR5cGU9Ik9wZXJhbmRSZWYiLz4KICAgICAgICAgPC9leHByZXNzaW9uPgogICAgICAgICA8b3BlcmFuZCBuYW1lPSJ2YWx1ZSI+CiAgICAgICAgICAgIDxvcGVyYW5kVHlwZVNwZWNpZmllciBsb2NhdG9yPSIyNDA6MzItMjQwOjUwIiBuYW1lPSJmaGlyOlJlc2VhcmNoRWxlbWVudFR5cGUiIHhzaTp0eXBlPSJOYW1lZFR5cGVTcGVjaWZpZXIiLz4KICAgICAgICAgPC9vcGVyYW5kPgogICAgICA8L2RlZj4KICAgICAgPGRlZiBsb2NhdG9yPSIyNDE6MS0yNDE6NjQiIG5hbWU9IlRvU3RyaW5nIiBjb250ZXh0PSJQYXRpZW50IiBhY2Nlc3NMZXZlbD0iUHVibGljIiB4c2k6dHlwZT0iRnVuY3Rpb25EZWYiPgogICAgICAgICA8ZXhwcmVzc2lvbiBsb2NhdG9yPSIyNDE6NTQtMjQxOjY0IiBwYXRoPSJ2YWx1ZSIgeHNpOnR5cGU9IlByb3BlcnR5Ij4KICAgICAgICAgICAgPHNvdXJjZSBsb2NhdG9yPSIyNDE6NTQtMjQxOjU4IiBuYW1lPSJ2YWx1ZSIgeHNpOnR5cGU9Ik9wZXJhbmRSZWYiLz4KICAgICAgICAgPC9leHByZXNzaW9uPgogICAgICAgICA8b3BlcmFuZCBuYW1lPSJ2YWx1ZSI+CiAgICAgICAgICAgIDxvcGVyYW5kVHlwZVNwZWNpZmllciBsb2NhdG9yPSIyNDE6MzItMjQxOjUwIiBuYW1lPSJmaGlyOlJlc2VhcmNoU3R1ZHlTdGF0dXMiIHhzaTp0eXBlPSJOYW1lZFR5cGVTcGVjaWZpZXIiLz4KICAgICAgICAgPC9vcGVyYW5kPgogICAgICA8L2RlZj4KICAgICAgPGRlZiBsb2NhdG9yPSIyNDI6MS0yNDI6NjYiIG5hbWU9IlRvU3RyaW5nIiBjb250ZXh0PSJQYXRpZW50IiBhY2Nlc3NMZXZlbD0iUHVibGljIiB4c2k6dHlwZT0iRnVuY3Rpb25EZWYiPgogICAgICAgICA8ZXhwcmVzc2lvbiBsb2NhdG9yPSIyNDI6NTYtMjQyOjY2IiBwYXRoPSJ2YWx1ZSIgeHNpOnR5cGU9IlByb3BlcnR5Ij4KICAgICAgICAgICAgPHNvdXJjZSBsb2NhdG9yPSIyNDI6NTYtMjQyOjYwIiBuYW1lPSJ2YWx1ZSIgeHNpOnR5cGU9Ik9wZXJhbmRSZWYiLz4KICAgICAgICAgPC9leHByZXNzaW9uPgogICAgICAgICA8b3BlcmFuZCBuYW1lPSJ2YWx1ZSI+CiAgICAgICAgICAgIDxvcGVyYW5kVHlwZVNwZWNpZmllciBsb2NhdG9yPSIyNDI6MzItMjQyOjUyIiBuYW1lPSJmaGlyOlJlc2VhcmNoU3ViamVjdFN0YXR1cyIgeHNpOnR5cGU9Ik5hbWVkVHlwZVNwZWNpZmllciIvPgogICAgICAgICA8L29wZXJhbmQ+CiAgICAgIDwvZGVmPgogICAgICA8ZGVmIGxvY2F0b3I9IjI0MzoxLTI0Mzo1NyIgbmFtZT0iVG9TdHJpbmciIGNvbnRleHQ9IlBhdGllbnQiIGFjY2Vzc0xldmVsPSJQdWJsaWMiIHhzaTp0eXBlPSJGdW5jdGlvbkRlZiI+CiAgICAgICAgIDxleHByZXNzaW9uIGxvY2F0b3I9IjI0Mzo0Ny0yNDM6NTciIHBhdGg9InZhbHVlIiB4c2k6dHlwZT0iUHJvcGVydHkiPgogICAgICAgICAgICA8c291cmNlIGxvY2F0b3I9IjI0Mzo0Ny0yNDM6NTEiIG5hbWU9InZhbHVlIiB4c2k6dHlwZT0iT3BlcmFuZFJlZiIvPgogICAgICAgICA8L2V4cHJlc3Npb24+CiAgICAgICAgIDxvcGVyYW5kIG5hbWU9InZhbHVlIj4KICAgICAgICAgICAgPG9wZXJhbmRUeXBlU3BlY2lmaWVyIGxvY2F0b3I9IjI0MzozMi0yNDM6NDMiIG5hbWU9ImZoaXI6UmVzb3VyY2VUeXBlIiB4c2k6dHlwZT0iTmFtZWRUeXBlU3BlY2lmaWVyIi8+CiAgICAgICAgIDwvb3BlcmFuZD4KICAgICAgPC9kZWY+CiAgICAgIDxkZWYgbG9jYXRvcj0iMjQ0OjEtMjQ0OjY2IiBuYW1lPSJUb1N0cmluZyIgY29udGV4dD0iUGF0aWVudCIgYWNjZXNzTGV2ZWw9IlB1YmxpYyIgeHNpOnR5cGU9IkZ1bmN0aW9uRGVmIj4KICAgICAgICAgPGV4cHJlc3Npb24gbG9jYXRvcj0iMjQ0OjU2LTI0NDo2NiIgcGF0aD0idmFsdWUiIHhzaTp0eXBlPSJQcm9wZXJ0eSI+CiAgICAgICAgICAgIDxzb3VyY2UgbG9jYXRvcj0iMjQ0OjU2LTI0NDo2MCIgbmFtZT0idmFsdWUiIHhzaTp0eXBlPSJPcGVyYW5kUmVmIi8+CiAgICAgICAgIDwvZXhwcmVzc2lvbj4KICAgICAgICAgPG9wZXJhbmQgbmFtZT0idmFsdWUiPgogICAgICAgICAgICA8b3BlcmFuZFR5cGVTcGVjaWZpZXIgbG9jYXRvcj0iMjQ0OjMyLTI0NDo1MiIgbmFtZT0iZmhpcjpSZXNvdXJjZVZlcnNpb25Qb2xpY3kiIHhzaTp0eXBlPSJOYW1lZFR5cGVTcGVjaWZpZXIiLz4KICAgICAgICAgPC9vcGVyYW5kPgogICAgICA8L2RlZj4KICAgICAgPGRlZiBsb2NhdG9yPSIyNDU6MS0yNDU6NTciIG5hbWU9IlRvU3RyaW5nIiBjb250ZXh0PSJQYXRpZW50IiBhY2Nlc3NMZXZlbD0iUHVibGljIiB4c2k6dHlwZT0iRnVuY3Rpb25EZWYiPgogICAgICAgICA8ZXhwcmVzc2lvbiBsb2NhdG9yPSIyNDU6NDctMjQ1OjU3IiBwYXRoPSJ2YWx1ZSIgeHNpOnR5cGU9IlByb3BlcnR5Ij4KICAgICAgICAgICAgPHNvdXJjZSBsb2NhdG9yPSIyNDU6NDctMjQ1OjUxIiBuYW1lPSJ2YWx1ZSIgeHNpOnR5cGU9Ik9wZXJhbmRSZWYiLz4KICAgICAgICAgPC9leHByZXNzaW9uPgogICAgICAgICA8b3BlcmFuZCBuYW1lPSJ2YWx1ZSI+CiAgICAgICAgICAgIDxvcGVyYW5kVHlwZVNwZWNpZmllciBsb2NhdG9yPSIyNDU6MzItMjQ1OjQzIiBuYW1lPSJmaGlyOlJlc3BvbnNlVHlwZSIgeHNpOnR5cGU9Ik5hbWVkVHlwZVNwZWNpZmllciIvPgogICAgICAgICA8L29wZXJhbmQ+CiAgICAgIDwvZGVmPgogICAgICA8ZGVmIGxvY2F0b3I9IjI0NjoxLTI0Njo2NiIgbmFtZT0iVG9TdHJpbmciIGNvbnRleHQ9IlBhdGllbnQiIGFjY2Vzc0xldmVsPSJQdWJsaWMiIHhzaTp0eXBlPSJGdW5jdGlvbkRlZiI+CiAgICAgICAgIDxleHByZXNzaW9uIGxvY2F0b3I9IjI0Njo1Ni0yNDY6NjYiIHBhdGg9InZhbHVlIiB4c2k6dHlwZT0iUHJvcGVydHkiPgogICAgICAgICAgICA8c291cmNlIGxvY2F0b3I9IjI0Njo1Ni0yNDY6NjAiIG5hbWU9InZhbHVlIiB4c2k6dHlwZT0iT3BlcmFuZFJlZiIvPgogICAgICAgICA8L2V4cHJlc3Npb24+CiAgICAgICAgIDxvcGVyYW5kIG5hbWU9InZhbHVlIj4KICAgICAgICAgICAgPG9wZXJhbmRUeXBlU3BlY2lmaWVyIGxvY2F0b3I9IjI0NjozMi0yNDY6NTIiIG5hbWU9ImZoaXI6UmVzdGZ1bENhcGFiaWxpdHlNb2RlIiB4c2k6dHlwZT0iTmFtZWRUeXBlU3BlY2lmaWVyIi8+CiAgICAgICAgIDwvb3BlcmFuZD4KICAgICAgPC9kZWY+CiAgICAgIDxkZWYgbG9jYXRvcj0iMjQ3OjEtMjQ3OjY1IiBuYW1lPSJUb1N0cmluZyIgY29udGV4dD0iUGF0aWVudCIgYWNjZXNzTGV2ZWw9IlB1YmxpYyIgeHNpOnR5cGU9IkZ1bmN0aW9uRGVmIj4KICAgICAgICAgPGV4cHJlc3Npb24gbG9jYXRvcj0iMjQ3OjU1LTI0Nzo2NSIgcGF0aD0idmFsdWUiIHhzaTp0eXBlPSJQcm9wZXJ0eSI+CiAgICAgICAgICAgIDxzb3VyY2UgbG9jYXRvcj0iMjQ3OjU1LTI0Nzo1OSIgbmFtZT0idmFsdWUiIHhzaTp0eXBlPSJPcGVyYW5kUmVmIi8+CiAgICAgICAgIDwvZXhwcmVzc2lvbj4KICAgICAgICAgPG9wZXJhbmQgbmFtZT0idmFsdWUiPgogICAgICAgICAgICA8b3BlcmFuZFR5cGVTcGVjaWZpZXIgbG9jYXRvcj0iMjQ3OjMyLTI0Nzo1MSIgbmFtZT0iZmhpcjpSaXNrQXNzZXNzbWVudFN0YXR1cyIgeHNpOnR5cGU9Ik5hbWVkVHlwZVNwZWNpZmllciIvPgogICAgICAgICA8L29wZXJhbmQ+CiAgICAgIDwvZGVmPgogICAgICA8ZGVmIGxvY2F0b3I9IjI0ODoxLTI0ODo1NiIgbmFtZT0iVG9TdHJpbmciIGNvbnRleHQ9IlBhdGllbnQiIGFjY2Vzc0xldmVsPSJQdWJsaWMiIHhzaTp0eXBlPSJGdW5jdGlvbkRlZiI+CiAgICAgICAgIDxleHByZXNzaW9uIGxvY2F0b3I9IjI0ODo0Ni0yNDg6NTYiIHBhdGg9InZhbHVlIiB4c2k6dHlwZT0iUHJvcGVydHkiPgogICAgICAgICAgICA8c291cmNlIGxvY2F0b3I9IjI0ODo0Ni0yNDg6NTAiIG5hbWU9InZhbHVlIiB4c2k6dHlwZT0iT3BlcmFuZFJlZiIvPgogICAgICAgICA8L2V4cHJlc3Npb24+CiAgICAgICAgIDxvcGVyYW5kIG5hbWU9InZhbHVlIj4KICAgICAgICAgICAgPG9wZXJhbmRUeXBlU3BlY2lmaWVyIGxvY2F0b3I9IjI0ODozMi0yNDg6NDIiIG5hbWU9ImZoaXI6U1BEWExpY2Vuc2UiIHhzaTp0eXBlPSJOYW1lZFR5cGVTcGVjaWZpZXIiLz4KICAgICAgICAgPC9vcGVyYW5kPgogICAgICA8L2RlZj4KICAgICAgPGRlZiBsb2NhdG9yPSIyNDk6MS0yNDk6NjEiIG5hbWU9IlRvU3RyaW5nIiBjb250ZXh0PSJQYXRpZW50IiBhY2Nlc3NMZXZlbD0iUHVibGljIiB4c2k6dHlwZT0iRnVuY3Rpb25EZWYiPgogICAgICAgICA8ZXhwcmVzc2lvbiBsb2NhdG9yPSIyNDk6NTEtMjQ5OjYxIiBwYXRoPSJ2YWx1ZSIgeHNpOnR5cGU9IlByb3BlcnR5Ij4KICAgICAgICAgICAgPHNvdXJjZSBsb2NhdG9yPSIyNDk6NTEtMjQ5OjU1IiBuYW1lPSJ2YWx1ZSIgeHNpOnR5cGU9Ik9wZXJhbmRSZWYiLz4KICAgICAgICAgPC9leHByZXNzaW9uPgogICAgICAgICA8b3BlcmFuZCBuYW1lPSJ2YWx1ZSI+CiAgICAgICAgICAgIDxvcGVyYW5kVHlwZVNwZWNpZmllciBsb2NhdG9yPSIyNDk6MzItMjQ5OjQ3IiBuYW1lPSJmaGlyOlNlYXJjaENvbXBhcmF0b3IiIHhzaTp0eXBlPSJOYW1lZFR5cGVTcGVjaWZpZXIiLz4KICAgICAgICAgPC9vcGVyYW5kPgogICAgICA8L2RlZj4KICAgICAgPGRlZiBsb2NhdG9yPSIyNTA6MS0yNTA6NjAiIG5hbWU9IlRvU3RyaW5nIiBjb250ZXh0PSJQYXRpZW50IiBhY2Nlc3NMZXZlbD0iUHVibGljIiB4c2k6dHlwZT0iRnVuY3Rpb25EZWYiPgogICAgICAgICA8ZXhwcmVzc2lvbiBsb2NhdG9yPSIyNTA6NTAtMjUwOjYwIiBwYXRoPSJ2YWx1ZSIgeHNpOnR5cGU9IlByb3BlcnR5Ij4KICAgICAgICAgICAgPHNvdXJjZSBsb2NhdG9yPSIyNTA6NTAtMjUwOjU0IiBuYW1lPSJ2YWx1ZSIgeHNpOnR5cGU9Ik9wZXJhbmRSZWYiLz4KICAgICAgICAgPC9leHByZXNzaW9uPgogICAgICAgICA8b3BlcmFuZCBuYW1lPSJ2YWx1ZSI+CiAgICAgICAgICAgIDxvcGVyYW5kVHlwZVNwZWNpZmllciBsb2NhdG9yPSIyNTA6MzItMjUwOjQ2IiBuYW1lPSJmaGlyOlNlYXJjaEVudHJ5TW9kZSIgeHNpOnR5cGU9Ik5hbWVkVHlwZVNwZWNpZmllciIvPgogICAgICAgICA8L29wZXJhbmQ+CiAgICAgIDwvZGVmPgogICAgICA8ZGVmIGxvY2F0b3I9IjI1MToxLTI1MTo2MyIgbmFtZT0iVG9TdHJpbmciIGNvbnRleHQ9IlBhdGllbnQiIGFjY2Vzc0xldmVsPSJQdWJsaWMiIHhzaTp0eXBlPSJGdW5jdGlvbkRlZiI+CiAgICAgICAgIDxleHByZXNzaW9uIGxvY2F0b3I9IjI1MTo1My0yNTE6NjMiIHBhdGg9InZhbHVlIiB4c2k6dHlwZT0iUHJvcGVydHkiPgogICAgICAgICAgICA8c291cmNlIGxvY2F0b3I9IjI1MTo1My0yNTE6NTciIG5hbWU9InZhbHVlIiB4c2k6dHlwZT0iT3BlcmFuZFJlZiIvPgogICAgICAgICA8L2V4cHJlc3Npb24+CiAgICAgICAgIDxvcGVyYW5kIG5hbWU9InZhbHVlIj4KICAgICAgICAgICAgPG9wZXJhbmRUeXBlU3BlY2lmaWVyIGxvY2F0b3I9IjI1MTozMi0yNTE6NDkiIG5hbWU9ImZoaXI6U2VhcmNoTW9kaWZpZXJDb2RlIiB4c2k6dHlwZT0iTmFtZWRUeXBlU3BlY2lmaWVyIi8+CiAgICAgICAgIDwvb3BlcmFuZD4KICAgICAgPC9kZWY+CiAgICAgIDxkZWYgbG9jYXRvcj0iMjUyOjEtMjUyOjYwIiBuYW1lPSJUb1N0cmluZyIgY29udGV4dD0iUGF0aWVudCIgYWNjZXNzTGV2ZWw9IlB1YmxpYyIgeHNpOnR5cGU9IkZ1bmN0aW9uRGVmIj4KICAgICAgICAgPGV4cHJlc3Npb24gbG9jYXRvcj0iMjUyOjUwLTI1Mjo2MCIgcGF0aD0idmFsdWUiIHhzaTp0eXBlPSJQcm9wZXJ0eSI+CiAgICAgICAgICAgIDxzb3VyY2UgbG9jYXRvcj0iMjUyOjUwLTI1Mjo1NCIgbmFtZT0idmFsdWUiIHhzaTp0eXBlPSJPcGVyYW5kUmVmIi8+CiAgICAgICAgIDwvZXhwcmVzc2lvbj4KICAgICAgICAgPG9wZXJhbmQgbmFtZT0idmFsdWUiPgogICAgICAgICAgICA8b3BlcmFuZFR5cGVTcGVjaWZpZXIgbG9jYXRvcj0iMjUyOjMyLTI1Mjo0NiIgbmFtZT0iZmhpcjpTZWFyY2hQYXJhbVR5cGUiIHhzaTp0eXBlPSJOYW1lZFR5cGVTcGVjaWZpZXIiLz4KICAgICAgICAgPC9vcGVyYW5kPgogICAgICA8L2RlZj4KICAgICAgPGRlZiBsb2NhdG9yPSIyNTM6MS0yNTM6NTYiIG5hbWU9IlRvU3RyaW5nIiBjb250ZXh0PSJQYXRpZW50IiBhY2Nlc3NMZXZlbD0iUHVibGljIiB4c2k6dHlwZT0iRnVuY3Rpb25EZWYiPgogICAgICAgICA8ZXhwcmVzc2lvbiBsb2NhdG9yPSIyNTM6NDYtMjUzOjU2IiBwYXRoPSJ2YWx1ZSIgeHNpOnR5cGU9IlByb3BlcnR5Ij4KICAgICAgICAgICAgPHNvdXJjZSBsb2NhdG9yPSIyNTM6NDYtMjUzOjUwIiBuYW1lPSJ2YWx1ZSIgeHNpOnR5cGU9Ik9wZXJhbmRSZWYiLz4KICAgICAgICAgPC9leHByZXNzaW9uPgogICAgICAgICA8b3BlcmFuZCBuYW1lPSJ2YWx1ZSI+CiAgICAgICAgICAgIDxvcGVyYW5kVHlwZVNwZWNpZmllciBsb2NhdG9yPSIyNTM6MzItMjUzOjQyIiBuYW1lPSJmaGlyOlNlY3Rpb25Nb2RlIiB4c2k6dHlwZT0iTmFtZWRUeXBlU3BlY2lmaWVyIi8+CiAgICAgICAgIDwvb3BlcmFuZD4KICAgICAgPC9kZWY+CiAgICAgIDxkZWYgbG9jYXRvcj0iMjU0OjEtMjU0OjU3IiBuYW1lPSJUb1N0cmluZyIgY29udGV4dD0iUGF0aWVudCIgYWNjZXNzTGV2ZWw9IlB1YmxpYyIgeHNpOnR5cGU9IkZ1bmN0aW9uRGVmIj4KICAgICAgICAgPGV4cHJlc3Npb24gbG9jYXRvcj0iMjU0OjQ3LTI1NDo1NyIgcGF0aD0idmFsdWUiIHhzaTp0eXBlPSJQcm9wZXJ0eSI+CiAgICAgICAgICAgIDxzb3VyY2UgbG9jYXRvcj0iMjU0OjQ3LTI1NDo1MSIgbmFtZT0idmFsdWUiIHhzaTp0eXBlPSJPcGVyYW5kUmVmIi8+CiAgICAgICAgIDwvZXhwcmVzc2lvbj4KICAgICAgICAgPG9wZXJhbmQgbmFtZT0idmFsdWUiPgogICAgICAgICAgICA8b3BlcmFuZFR5cGVTcGVjaWZpZXIgbG9jYXRvcj0iMjU0OjMyLTI1NDo0MyIgbmFtZT0iZmhpcjpTZXF1ZW5jZVR5cGUiIHhzaTp0eXBlPSJOYW1lZFR5cGVTcGVjaWZpZXIiLz4KICAgICAgICAgPC9vcGVyYW5kPgogICAgICA8L2RlZj4KICAgICAgPGRlZiBsb2NhdG9yPSIyNTU6MS0yNTU6NjUiIG5hbWU9IlRvU3RyaW5nIiBjb250ZXh0PSJQYXRpZW50IiBhY2Nlc3NMZXZlbD0iUHVibGljIiB4c2k6dHlwZT0iRnVuY3Rpb25EZWYiPgogICAgICAgICA8ZXhwcmVzc2lvbiBsb2NhdG9yPSIyNTU6NTUtMjU1OjY1IiBwYXRoPSJ2YWx1ZSIgeHNpOnR5cGU9IlByb3BlcnR5Ij4KICAgICAgICAgICAgPHNvdXJjZSBsb2NhdG9yPSIyNTU6NTUtMjU1OjU5IiBuYW1lPSJ2YWx1ZSIgeHNpOnR5cGU9Ik9wZXJhbmRSZWYiLz4KICAgICAgICAgPC9leHByZXNzaW9uPgogICAgICAgICA8b3BlcmFuZCBuYW1lPSJ2YWx1ZSI+CiAgICAgICAgICAgIDxvcGVyYW5kVHlwZVNwZWNpZmllciBsb2NhdG9yPSIyNTU6MzItMjU1OjUxIiBuYW1lPSJmaGlyOlNlcnZpY2VSZXF1ZXN0SW50ZW50IiB4c2k6dHlwZT0iTmFtZWRUeXBlU3BlY2lmaWVyIi8+CiAgICAgICAgIDwvb3BlcmFuZD4KICAgICAgPC9kZWY+CiAgICAgIDxkZWYgbG9jYXRvcj0iMjU2OjEtMjU2OjY3IiBuYW1lPSJUb1N0cmluZyIgY29udGV4dD0iUGF0aWVudCIgYWNjZXNzTGV2ZWw9IlB1YmxpYyIgeHNpOnR5cGU9IkZ1bmN0aW9uRGVmIj4KICAgICAgICAgPGV4cHJlc3Npb24gbG9jYXRvcj0iMjU2OjU3LTI1Njo2NyIgcGF0aD0idmFsdWUiIHhzaTp0eXBlPSJQcm9wZXJ0eSI+CiAgICAgICAgICAgIDxzb3VyY2UgbG9jYXRvcj0iMjU2OjU3LTI1Njo2MSIgbmFtZT0idmFsdWUiIHhzaTp0eXBlPSJPcGVyYW5kUmVmIi8+CiAgICAgICAgIDwvZXhwcmVzc2lvbj4KICAgICAgICAgPG9wZXJhbmQgbmFtZT0idmFsdWUiPgogICAgICAgICAgICA8b3BlcmFuZFR5cGVTcGVjaWZpZXIgbG9jYXRvcj0iMjU2OjMyLTI1Njo1MyIgbmFtZT0iZmhpcjpTZXJ2aWNlUmVxdWVzdFByaW9yaXR5IiB4c2k6dHlwZT0iTmFtZWRUeXBlU3BlY2lmaWVyIi8+CiAgICAgICAgIDwvb3BlcmFuZD4KICAgICAgPC9kZWY+CiAgICAgIDxkZWYgbG9jYXRvcj0iMjU3OjEtMjU3OjY1IiBuYW1lPSJUb1N0cmluZyIgY29udGV4dD0iUGF0aWVudCIgYWNjZXNzTGV2ZWw9IlB1YmxpYyIgeHNpOnR5cGU9IkZ1bmN0aW9uRGVmIj4KICAgICAgICAgPGV4cHJlc3Npb24gbG9jYXRvcj0iMjU3OjU1LTI1Nzo2NSIgcGF0aD0idmFsdWUiIHhzaTp0eXBlPSJQcm9wZXJ0eSI+CiAgICAgICAgICAgIDxzb3VyY2UgbG9jYXRvcj0iMjU3OjU1LTI1Nzo1OSIgbmFtZT0idmFsdWUiIHhzaTp0eXBlPSJPcGVyYW5kUmVmIi8+CiAgICAgICAgIDwvZXhwcmVzc2lvbj4KICAgICAgICAgPG9wZXJhbmQgbmFtZT0idmFsdWUiPgogICAgICAgICAgICA8b3BlcmFuZFR5cGVTcGVjaWZpZXIgbG9jYXRvcj0iMjU3OjMyLTI1Nzo1MSIgbmFtZT0iZmhpcjpTZXJ2aWNlUmVxdWVzdFN0YXR1cyIgeHNpOnR5cGU9Ik5hbWVkVHlwZVNwZWNpZmllciIvPgogICAgICAgICA8L29wZXJhbmQ+CiAgICAgIDwvZGVmPgogICAgICA8ZGVmIGxvY2F0b3I9IjI1ODoxLTI1ODo1NyIgbmFtZT0iVG9TdHJpbmciIGNvbnRleHQ9IlBhdGllbnQiIGFjY2Vzc0xldmVsPSJQdWJsaWMiIHhzaTp0eXBlPSJGdW5jdGlvbkRlZiI+CiAgICAgICAgIDxleHByZXNzaW9uIGxvY2F0b3I9IjI1ODo0Ny0yNTg6NTciIHBhdGg9InZhbHVlIiB4c2k6dHlwZT0iUHJvcGVydHkiPgogICAgICAgICAgICA8c291cmNlIGxvY2F0b3I9IjI1ODo0Ny0yNTg6NTEiIG5hbWU9InZhbHVlIiB4c2k6dHlwZT0iT3BlcmFuZFJlZiIvPgogICAgICAgICA8L2V4cHJlc3Npb24+CiAgICAgICAgIDxvcGVyYW5kIG5hbWU9InZhbHVlIj4KICAgICAgICAgICAgPG9wZXJhbmRUeXBlU3BlY2lmaWVyIGxvY2F0b3I9IjI1ODozMi0yNTg6NDMiIG5hbWU9ImZoaXI6U2xpY2luZ1J1bGVzIiB4c2k6dHlwZT0iTmFtZWRUeXBlU3BlY2lmaWVyIi8+CiAgICAgICAgIDwvb3BlcmFuZD4KICAgICAgPC9kZWY+CiAgICAgIDxkZWYgbG9jYXRvcj0iMjU5OjEtMjU5OjU1IiBuYW1lPSJUb1N0cmluZyIgY29udGV4dD0iUGF0aWVudCIgYWNjZXNzTGV2ZWw9IlB1YmxpYyIgeHNpOnR5cGU9IkZ1bmN0aW9uRGVmIj4KICAgICAgICAgPGV4cHJlc3Npb24gbG9jYXRvcj0iMjU5OjQ1LTI1OTo1NSIgcGF0aD0idmFsdWUiIHhzaTp0eXBlPSJQcm9wZXJ0eSI+CiAgICAgICAgICAgIDxzb3VyY2UgbG9jYXRvcj0iMjU5OjQ1LTI1OTo0OSIgbmFtZT0idmFsdWUiIHhzaTp0eXBlPSJPcGVyYW5kUmVmIi8+CiAgICAgICAgIDwvZXhwcmVzc2lvbj4KICAgICAgICAgPG9wZXJhbmQgbmFtZT0idmFsdWUiPgogICAgICAgICAgICA8b3BlcmFuZFR5cGVTcGVjaWZpZXIgbG9jYXRvcj0iMjU5OjMyLTI1OTo0MSIgbmFtZT0iZmhpcjpTbG90U3RhdHVzIiB4c2k6dHlwZT0iTmFtZWRUeXBlU3BlY2lmaWVyIi8+CiAgICAgICAgIDwvb3BlcmFuZD4KICAgICAgPC9kZWY+CiAgICAgIDxkZWYgbG9jYXRvcj0iMjYwOjEtMjYwOjU4IiBuYW1lPSJUb1N0cmluZyIgY29udGV4dD0iUGF0aWVudCIgYWNjZXNzTGV2ZWw9IlB1YmxpYyIgeHNpOnR5cGU9IkZ1bmN0aW9uRGVmIj4KICAgICAgICAgPGV4cHJlc3Npb24gbG9jYXRvcj0iMjYwOjQ4LTI2MDo1OCIgcGF0aD0idmFsdWUiIHhzaTp0eXBlPSJQcm9wZXJ0eSI+CiAgICAgICAgICAgIDxzb3VyY2UgbG9jYXRvcj0iMjYwOjQ4LTI2MDo1MiIgbmFtZT0idmFsdWUiIHhzaTp0eXBlPSJPcGVyYW5kUmVmIi8+CiAgICAgICAgIDwvZXhwcmVzc2lvbj4KICAgICAgICAgPG9wZXJhbmQgbmFtZT0idmFsdWUiPgogICAgICAgICAgICA8b3BlcmFuZFR5cGVTcGVjaWZpZXIgbG9jYXRvcj0iMjYwOjMyLTI2MDo0NCIgbmFtZT0iZmhpcjpTb3J0RGlyZWN0aW9uIiB4c2k6dHlwZT0iTmFtZWRUeXBlU3BlY2lmaWVyIi8+CiAgICAgICAgIDwvb3BlcmFuZD4KICAgICAgPC9kZWY+CiAgICAgIDxkZWYgbG9jYXRvcj0iMjYxOjEtMjYxOjcyIiBuYW1lPSJUb1N0cmluZyIgY29udGV4dD0iUGF0aWVudCIgYWNjZXNzTGV2ZWw9IlB1YmxpYyIgeHNpOnR5cGU9IkZ1bmN0aW9uRGVmIj4KICAgICAgICAgPGV4cHJlc3Npb24gbG9jYXRvcj0iMjYxOjYyLTI2MTo3MiIgcGF0aD0idmFsdWUiIHhzaTp0eXBlPSJQcm9wZXJ0eSI+CiAgICAgICAgICAgIDxzb3VyY2UgbG9jYXRvcj0iMjYxOjYyLTI2MTo2NiIgbmFtZT0idmFsdWUiIHhzaTp0eXBlPSJPcGVyYW5kUmVmIi8+CiAgICAgICAgIDwvZXhwcmVzc2lvbj4KICAgICAgICAgPG9wZXJhbmQgbmFtZT0idmFsdWUiPgogICAgICAgICAgICA8b3BlcmFuZFR5cGVTcGVjaWZpZXIgbG9jYXRvcj0iMjYxOjMyLTI2MTo1OCIgbmFtZT0iZmhpcjpTcGVjaW1lbkNvbnRhaW5lZFByZWZlcmVuY2UiIHhzaTp0eXBlPSJOYW1lZFR5cGVTcGVjaWZpZXIiLz4KICAgICAgICAgPC9vcGVyYW5kPgogICAgICA8L2RlZj4KICAgICAgPGRlZiBsb2NhdG9yPSIyNjI6MS0yNjI6NTkiIG5hbWU9IlRvU3RyaW5nIiBjb250ZXh0PSJQYXRpZW50IiBhY2Nlc3NMZXZlbD0iUHVibGljIiB4c2k6dHlwZT0iRnVuY3Rpb25EZWYiPgogICAgICAgICA8ZXhwcmVzc2lvbiBsb2NhdG9yPSIyNjI6NDktMjYyOjU5IiBwYXRoPSJ2YWx1ZSIgeHNpOnR5cGU9IlByb3BlcnR5Ij4KICAgICAgICAgICAgPHNvdXJjZSBsb2NhdG9yPSIyNjI6NDktMjYyOjUzIiBuYW1lPSJ2YWx1ZSIgeHNpOnR5cGU9Ik9wZXJhbmRSZWYiLz4KICAgICAgICAgPC9leHByZXNzaW9uPgogICAgICAgICA8b3BlcmFuZCBuYW1lPSJ2YWx1ZSI+CiAgICAgICAgICAgIDxvcGVyYW5kVHlwZVNwZWNpZmllciBsb2NhdG9yPSIyNjI6MzItMjYyOjQ1IiBuYW1lPSJmaGlyOlNwZWNpbWVuU3RhdHVzIiB4c2k6dHlwZT0iTmFtZWRUeXBlU3BlY2lmaWVyIi8+CiAgICAgICAgIDwvb3BlcmFuZD4KICAgICAgPC9kZWY+CiAgICAgIDxkZWYgbG9jYXRvcj0iMjYzOjEtMjYzOjUxIiBuYW1lPSJUb1N0cmluZyIgY29udGV4dD0iUGF0aWVudCIgYWNjZXNzTGV2ZWw9IlB1YmxpYyIgeHNpOnR5cGU9IkZ1bmN0aW9uRGVmIj4KICAgICAgICAgPGV4cHJlc3Npb24gbG9jYXRvcj0iMjYzOjQxLTI2Mzo1MSIgcGF0aD0idmFsdWUiIHhzaTp0eXBlPSJQcm9wZXJ0eSI+CiAgICAgICAgICAgIDxzb3VyY2UgbG9jYXRvcj0iMjYzOjQxLTI2Mzo0NSIgbmFtZT0idmFsdWUiIHhzaTp0eXBlPSJPcGVyYW5kUmVmIi8+CiAgICAgICAgIDwvZXhwcmVzc2lvbj4KICAgICAgICAgPG9wZXJhbmQgbmFtZT0idmFsdWUiPgogICAgICAgICAgICA8b3BlcmFuZFR5cGVTcGVjaWZpZXIgbG9jYXRvcj0iMjYzOjMyLTI2MzozNyIgbmFtZT0iZmhpcjpTdGF0dXMiIHhzaTp0eXBlPSJOYW1lZFR5cGVTcGVjaWZpZXIiLz4KICAgICAgICAgPC9vcGVyYW5kPgogICAgICA8L2RlZj4KICAgICAgPGRlZiBsb2NhdG9yPSIyNjQ6MS0yNjQ6NTUiIG5hbWU9IlRvU3RyaW5nIiBjb250ZXh0PSJQYXRpZW50IiBhY2Nlc3NMZXZlbD0iUHVibGljIiB4c2k6dHlwZT0iRnVuY3Rpb25EZWYiPgogICAgICAgICA8ZXhwcmVzc2lvbiBsb2NhdG9yPSIyNjQ6NDUtMjY0OjU1IiBwYXRoPSJ2YWx1ZSIgeHNpOnR5cGU9IlByb3BlcnR5Ij4KICAgICAgICAgICAgPHNvdXJjZSBsb2NhdG9yPSIyNjQ6NDUtMjY0OjQ5IiBuYW1lPSJ2YWx1ZSIgeHNpOnR5cGU9Ik9wZXJhbmRSZWYiLz4KICAgICAgICAgPC9leHByZXNzaW9uPgogICAgICAgICA8b3BlcmFuZCBuYW1lPSJ2YWx1ZSI+CiAgICAgICAgICAgIDxvcGVyYW5kVHlwZVNwZWNpZmllciBsb2NhdG9yPSIyNjQ6MzItMjY0OjQxIiBuYW1lPSJmaGlyOlN0cmFuZFR5cGUiIHhzaTp0eXBlPSJOYW1lZFR5cGVTcGVjaWZpZXIiLz4KICAgICAgICAgPC9vcGVyYW5kPgogICAgICA8L2RlZj4KICAgICAgPGRlZiBsb2NhdG9yPSIyNjU6MS0yNjU6NjgiIG5hbWU9IlRvU3RyaW5nIiBjb250ZXh0PSJQYXRpZW50IiBhY2Nlc3NMZXZlbD0iUHVibGljIiB4c2k6dHlwZT0iRnVuY3Rpb25EZWYiPgogICAgICAgICA8ZXhwcmVzc2lvbiBsb2NhdG9yPSIyNjU6NTgtMjY1OjY4IiBwYXRoPSJ2YWx1ZSIgeHNpOnR5cGU9IlByb3BlcnR5Ij4KICAgICAgICAgICAgPHNvdXJjZSBsb2NhdG9yPSIyNjU6NTgtMjY1OjYyIiBuYW1lPSJ2YWx1ZSIgeHNpOnR5cGU9Ik9wZXJhbmRSZWYiLz4KICAgICAgICAgPC9leHByZXNzaW9uPgogICAgICAgICA8b3BlcmFuZCBuYW1lPSJ2YWx1ZSI+CiAgICAgICAgICAgIDxvcGVyYW5kVHlwZVNwZWNpZmllciBsb2NhdG9yPSIyNjU6MzItMjY1OjU0IiBuYW1lPSJmaGlyOlN0cnVjdHVyZURlZmluaXRpb25LaW5kIiB4c2k6dHlwZT0iTmFtZWRUeXBlU3BlY2lmaWVyIi8+CiAgICAgICAgIDwvb3BlcmFuZD4KICAgICAgPC9kZWY+CiAgICAgIDxkZWYgbG9jYXRvcj0iMjY2OjEtMjY2OjY4IiBuYW1lPSJUb1N0cmluZyIgY29udGV4dD0iUGF0aWVudCIgYWNjZXNzTGV2ZWw9IlB1YmxpYyIgeHNpOnR5cGU9IkZ1bmN0aW9uRGVmIj4KICAgICAgICAgPGV4cHJlc3Npb24gbG9jYXRvcj0iMjY2OjU4LTI2Njo2OCIgcGF0aD0idmFsdWUiIHhzaTp0eXBlPSJQcm9wZXJ0eSI+CiAgICAgICAgICAgIDxzb3VyY2UgbG9jYXRvcj0iMjY2OjU4LTI2Njo2MiIgbmFtZT0idmFsdWUiIHhzaTp0eXBlPSJPcGVyYW5kUmVmIi8+CiAgICAgICAgIDwvZXhwcmVzc2lvbj4KICAgICAgICAgPG9wZXJhbmQgbmFtZT0idmFsdWUiPgogICAgICAgICAgICA8b3BlcmFuZFR5cGVTcGVjaWZpZXIgbG9jYXRvcj0iMjY2OjMyLTI2Njo1NCIgbmFtZT0iZmhpcjpTdHJ1Y3R1cmVNYXBDb250ZXh0VHlwZSIgeHNpOnR5cGU9Ik5hbWVkVHlwZVNwZWNpZmllciIvPgogICAgICAgICA8L29wZXJhbmQ+CiAgICAgIDwvZGVmPgogICAgICA8ZGVmIGxvY2F0b3I9IjI2NzoxLTI2Nzo3MCIgbmFtZT0iVG9TdHJpbmciIGNvbnRleHQ9IlBhdGllbnQiIGFjY2Vzc0xldmVsPSJQdWJsaWMiIHhzaTp0eXBlPSJGdW5jdGlvbkRlZiI+CiAgICAgICAgIDxleHByZXNzaW9uIGxvY2F0b3I9IjI2Nzo2MC0yNjc6NzAiIHBhdGg9InZhbHVlIiB4c2k6dHlwZT0iUHJvcGVydHkiPgogICAgICAgICAgICA8c291cmNlIGxvY2F0b3I9IjI2Nzo2MC0yNjc6NjQiIG5hbWU9InZhbHVlIiB4c2k6dHlwZT0iT3BlcmFuZFJlZiIvPgogICAgICAgICA8L2V4cHJlc3Npb24+CiAgICAgICAgIDxvcGVyYW5kIG5hbWU9InZhbHVlIj4KICAgICAgICAgICAgPG9wZXJhbmRUeXBlU3BlY2lmaWVyIGxvY2F0b3I9IjI2NzozMi0yNjc6NTYiIG5hbWU9ImZoaXI6U3RydWN0dXJlTWFwR3JvdXBUeXBlTW9kZSIgeHNpOnR5cGU9Ik5hbWVkVHlwZVNwZWNpZmllciIvPgogICAgICAgICA8L29wZXJhbmQ+CiAgICAgIDwvZGVmPgogICAgICA8ZGVmIGxvY2F0b3I9IjI2ODoxLTI2ODo2NiIgbmFtZT0iVG9TdHJpbmciIGNvbnRleHQ9IlBhdGllbnQiIGFjY2Vzc0xldmVsPSJQdWJsaWMiIHhzaTp0eXBlPSJGdW5jdGlvbkRlZiI+CiAgICAgICAgIDxleHByZXNzaW9uIGxvY2F0b3I9IjI2ODo1Ni0yNjg6NjYiIHBhdGg9InZhbHVlIiB4c2k6dHlwZT0iUHJvcGVydHkiPgogICAgICAgICAgICA8c291cmNlIGxvY2F0b3I9IjI2ODo1Ni0yNjg6NjAiIG5hbWU9InZhbHVlIiB4c2k6dHlwZT0iT3BlcmFuZFJlZiIvPgogICAgICAgICA8L2V4cHJlc3Npb24+CiAgICAgICAgIDxvcGVyYW5kIG5hbWU9InZhbHVlIj4KICAgICAgICAgICAgPG9wZXJhbmRUeXBlU3BlY2lmaWVyIGxvY2F0b3I9IjI2ODozMi0yNjg6NTIiIG5hbWU9ImZoaXI6U3RydWN0dXJlTWFwSW5wdXRNb2RlIiB4c2k6dHlwZT0iTmFtZWRUeXBlU3BlY2lmaWVyIi8+CiAgICAgICAgIDwvb3BlcmFuZD4KICAgICAgPC9kZWY+CiAgICAgIDxkZWYgbG9jYXRvcj0iMjY5OjEtMjY5OjY2IiBuYW1lPSJUb1N0cmluZyIgY29udGV4dD0iUGF0aWVudCIgYWNjZXNzTGV2ZWw9IlB1YmxpYyIgeHNpOnR5cGU9IkZ1bmN0aW9uRGVmIj4KICAgICAgICAgPGV4cHJlc3Npb24gbG9jYXRvcj0iMjY5OjU2LTI2OTo2NiIgcGF0aD0idmFsdWUiIHhzaTp0eXBlPSJQcm9wZXJ0eSI+CiAgICAgICAgICAgIDxzb3VyY2UgbG9jYXRvcj0iMjY5OjU2LTI2OTo2MCIgbmFtZT0idmFsdWUiIHhzaTp0eXBlPSJPcGVyYW5kUmVmIi8+CiAgICAgICAgIDwvZXhwcmVzc2lvbj4KICAgICAgICAgPG9wZXJhbmQgbmFtZT0idmFsdWUiPgogICAgICAgICAgICA8b3BlcmFuZFR5cGVTcGVjaWZpZXIgbG9jYXRvcj0iMjY5OjMyLTI2OTo1MiIgbmFtZT0iZmhpcjpTdHJ1Y3R1cmVNYXBNb2RlbE1vZGUiIHhzaTp0eXBlPSJOYW1lZFR5cGVTcGVjaWZpZXIiLz4KICAgICAgICAgPC9vcGVyYW5kPgogICAgICA8L2RlZj4KICAgICAgPGRlZiBsb2NhdG9yPSIyNzA6MS0yNzA6NzEiIG5hbWU9IlRvU3RyaW5nIiBjb250ZXh0PSJQYXRpZW50IiBhY2Nlc3NMZXZlbD0iUHVibGljIiB4c2k6dHlwZT0iRnVuY3Rpb25EZWYiPgogICAgICAgICA8ZXhwcmVzc2lvbiBsb2NhdG9yPSIyNzA6NjEtMjcwOjcxIiBwYXRoPSJ2YWx1ZSIgeHNpOnR5cGU9IlByb3BlcnR5Ij4KICAgICAgICAgICAgPHNvdXJjZSBsb2NhdG9yPSIyNzA6NjEtMjcwOjY1IiBuYW1lPSJ2YWx1ZSIgeHNpOnR5cGU9Ik9wZXJhbmRSZWYiLz4KICAgICAgICAgPC9leHByZXNzaW9uPgogICAgICAgICA8b3BlcmFuZCBuYW1lPSJ2YWx1ZSI+CiAgICAgICAgICAgIDxvcGVyYW5kVHlwZVNwZWNpZmllciBsb2NhdG9yPSIyNzA6MzItMjcwOjU3IiBuYW1lPSJmaGlyOlN0cnVjdHVyZU1hcFNvdXJjZUxpc3RNb2RlIiB4c2k6dHlwZT0iTmFtZWRUeXBlU3BlY2lmaWVyIi8+CiAgICAgICAgIDwvb3BlcmFuZD4KICAgICAgPC9kZWY+CiAgICAgIDxkZWYgbG9jYXRvcj0iMjcxOjEtMjcxOjcxIiBuYW1lPSJUb1N0cmluZyIgY29udGV4dD0iUGF0aWVudCIgYWNjZXNzTGV2ZWw9IlB1YmxpYyIgeHNpOnR5cGU9IkZ1bmN0aW9uRGVmIj4KICAgICAgICAgPGV4cHJlc3Npb24gbG9jYXRvcj0iMjcxOjYxLTI3MTo3MSIgcGF0aD0idmFsdWUiIHhzaTp0eXBlPSJQcm9wZXJ0eSI+CiAgICAgICAgICAgIDxzb3VyY2UgbG9jYXRvcj0iMjcxOjYxLTI3MTo2NSIgbmFtZT0idmFsdWUiIHhzaTp0eXBlPSJPcGVyYW5kUmVmIi8+CiAgICAgICAgIDwvZXhwcmVzc2lvbj4KICAgICAgICAgPG9wZXJhbmQgbmFtZT0idmFsdWUiPgogICAgICAgICAgICA8b3BlcmFuZFR5cGVTcGVjaWZpZXIgbG9jYXRvcj0iMjcxOjMyLTI3MTo1NyIgbmFtZT0iZmhpcjpTdHJ1Y3R1cmVNYXBUYXJnZXRMaXN0TW9kZSIgeHNpOnR5cGU9Ik5hbWVkVHlwZVNwZWNpZmllciIvPgogICAgICAgICA8L29wZXJhbmQ+CiAgICAgIDwvZGVmPgogICAgICA8ZGVmIGxvY2F0b3I9IjI3MjoxLTI3Mjo2NiIgbmFtZT0iVG9TdHJpbmciIGNvbnRleHQ9IlBhdGllbnQiIGFjY2Vzc0xldmVsPSJQdWJsaWMiIHhzaTp0eXBlPSJGdW5jdGlvbkRlZiI+CiAgICAgICAgIDxleHByZXNzaW9uIGxvY2F0b3I9IjI3Mjo1Ni0yNzI6NjYiIHBhdGg9InZhbHVlIiB4c2k6dHlwZT0iUHJvcGVydHkiPgogICAgICAgICAgICA8c291cmNlIGxvY2F0b3I9IjI3Mjo1Ni0yNzI6NjAiIG5hbWU9InZhbHVlIiB4c2k6dHlwZT0iT3BlcmFuZFJlZiIvPgogICAgICAgICA8L2V4cHJlc3Npb24+CiAgICAgICAgIDxvcGVyYW5kIG5hbWU9InZhbHVlIj4KICAgICAgICAgICAgPG9wZXJhbmRUeXBlU3BlY2lmaWVyIGxvY2F0b3I9IjI3MjozMi0yNzI6NTIiIG5hbWU9ImZoaXI6U3RydWN0dXJlTWFwVHJhbnNmb3JtIiB4c2k6dHlwZT0iTmFtZWRUeXBlU3BlY2lmaWVyIi8+CiAgICAgICAgIDwvb3BlcmFuZD4KICAgICAgPC9kZWY+CiAgICAgIDxkZWYgbG9jYXRvcj0iMjczOjEtMjczOjY4IiBuYW1lPSJUb1N0cmluZyIgY29udGV4dD0iUGF0aWVudCIgYWNjZXNzTGV2ZWw9IlB1YmxpYyIgeHNpOnR5cGU9IkZ1bmN0aW9uRGVmIj4KICAgICAgICAgPGV4cHJlc3Npb24gbG9jYXRvcj0iMjczOjU4LTI3Mzo2OCIgcGF0aD0idmFsdWUiIHhzaTp0eXBlPSJQcm9wZXJ0eSI+CiAgICAgICAgICAgIDxzb3VyY2UgbG9jYXRvcj0iMjczOjU4LTI3Mzo2MiIgbmFtZT0idmFsdWUiIHhzaTp0eXBlPSJPcGVyYW5kUmVmIi8+CiAgICAgICAgIDwvZXhwcmVzc2lvbj4KICAgICAgICAgPG9wZXJhbmQgbmFtZT0idmFsdWUiPgogICAgICAgICAgICA8b3BlcmFuZFR5cGVTcGVjaWZpZXIgbG9jYXRvcj0iMjczOjMyLTI3Mzo1NCIgbmFtZT0iZmhpcjpTdWJzY3JpcHRpb25DaGFubmVsVHlwZSIgeHNpOnR5cGU9Ik5hbWVkVHlwZVNwZWNpZmllciIvPgogICAgICAgICA8L29wZXJhbmQ+CiAgICAgIDwvZGVmPgogICAgICA8ZGVmIGxvY2F0b3I9IjI3NDoxLTI3NDo2MyIgbmFtZT0iVG9TdHJpbmciIGNvbnRleHQ9IlBhdGllbnQiIGFjY2Vzc0xldmVsPSJQdWJsaWMiIHhzaTp0eXBlPSJGdW5jdGlvbkRlZiI+CiAgICAgICAgIDxleHByZXNzaW9uIGxvY2F0b3I9IjI3NDo1My0yNzQ6NjMiIHBhdGg9InZhbHVlIiB4c2k6dHlwZT0iUHJvcGVydHkiPgogICAgICAgICAgICA8c291cmNlIGxvY2F0b3I9IjI3NDo1My0yNzQ6NTciIG5hbWU9InZhbHVlIiB4c2k6dHlwZT0iT3BlcmFuZFJlZiIvPgogICAgICAgICA8L2V4cHJlc3Npb24+CiAgICAgICAgIDxvcGVyYW5kIG5hbWU9InZhbHVlIj4KICAgICAgICAgICAgPG9wZXJhbmRUeXBlU3BlY2lmaWVyIGxvY2F0b3I9IjI3NDozMi0yNzQ6NDkiIG5hbWU9ImZoaXI6U3Vic2NyaXB0aW9uU3RhdHVzIiB4c2k6dHlwZT0iTmFtZWRUeXBlU3BlY2lmaWVyIi8+CiAgICAgICAgIDwvb3BlcmFuZD4KICAgICAgPC9kZWY+CiAgICAgIDxkZWYgbG9jYXRvcj0iMjc1OjEtMjc1OjY1IiBuYW1lPSJUb1N0cmluZyIgY29udGV4dD0iUGF0aWVudCIgYWNjZXNzTGV2ZWw9IlB1YmxpYyIgeHNpOnR5cGU9IkZ1bmN0aW9uRGVmIj4KICAgICAgICAgPGV4cHJlc3Npb24gbG9jYXRvcj0iMjc1OjU1LTI3NTo2NSIgcGF0aD0idmFsdWUiIHhzaTp0eXBlPSJQcm9wZXJ0eSI+CiAgICAgICAgICAgIDxzb3VyY2UgbG9jYXRvcj0iMjc1OjU1LTI3NTo1OSIgbmFtZT0idmFsdWUiIHhzaTp0eXBlPSJPcGVyYW5kUmVmIi8+CiAgICAgICAgIDwvZXhwcmVzc2lvbj4KICAgICAgICAgPG9wZXJhbmQgbmFtZT0idmFsdWUiPgogICAgICAgICAgICA8b3BlcmFuZFR5cGVTcGVjaWZpZXIgbG9jYXRvcj0iMjc1OjMyLTI3NTo1MSIgbmFtZT0iZmhpcjpTdXBwbHlEZWxpdmVyeVN0YXR1cyIgeHNpOnR5cGU9Ik5hbWVkVHlwZVNwZWNpZmllciIvPgogICAgICAgICA8L29wZXJhbmQ+CiAgICAgIDwvZGVmPgogICAgICA8ZGVmIGxvY2F0b3I9IjI3NjoxLTI3Njo2NCIgbmFtZT0iVG9TdHJpbmciIGNvbnRleHQ9IlBhdGllbnQiIGFjY2Vzc0xldmVsPSJQdWJsaWMiIHhzaTp0eXBlPSJGdW5jdGlvbkRlZiI+CiAgICAgICAgIDxleHByZXNzaW9uIGxvY2F0b3I9IjI3Njo1NC0yNzY6NjQiIHBhdGg9InZhbHVlIiB4c2k6dHlwZT0iUHJvcGVydHkiPgogICAgICAgICAgICA8c291cmNlIGxvY2F0b3I9IjI3Njo1NC0yNzY6NTgiIG5hbWU9InZhbHVlIiB4c2k6dHlwZT0iT3BlcmFuZFJlZiIvPgogICAgICAgICA8L2V4cHJlc3Npb24+CiAgICAgICAgIDxvcGVyYW5kIG5hbWU9InZhbHVlIj4KICAgICAgICAgICAgPG9wZXJhbmRUeXBlU3BlY2lmaWVyIGxvY2F0b3I9IjI3NjozMi0yNzY6NTAiIG5hbWU9ImZoaXI6U3VwcGx5UmVxdWVzdFN0YXR1cyIgeHNpOnR5cGU9Ik5hbWVkVHlwZVNwZWNpZmllciIvPgogICAgICAgICA8L29wZXJhbmQ+CiAgICAgIDwvZGVmPgogICAgICA8ZGVmIGxvY2F0b3I9IjI3NzoxLTI3Nzo2OSIgbmFtZT0iVG9TdHJpbmciIGNvbnRleHQ9IlBhdGllbnQiIGFjY2Vzc0xldmVsPSJQdWJsaWMiIHhzaTp0eXBlPSJGdW5jdGlvbkRlZiI+CiAgICAgICAgIDxleHByZXNzaW9uIGxvY2F0b3I9IjI3Nzo1OS0yNzc6NjkiIHBhdGg9InZhbHVlIiB4c2k6dHlwZT0iUHJvcGVydHkiPgogICAgICAgICAgICA8c291cmNlIGxvY2F0b3I9IjI3Nzo1OS0yNzc6NjMiIG5hbWU9InZhbHVlIiB4c2k6dHlwZT0iT3BlcmFuZFJlZiIvPgogICAgICAgICA8L2V4cHJlc3Npb24+CiAgICAgICAgIDxvcGVyYW5kIG5hbWU9InZhbHVlIj4KICAgICAgICAgICAgPG9wZXJhbmRUeXBlU3BlY2lmaWVyIGxvY2F0b3I9IjI3NzozMi0yNzc6NTUiIG5hbWU9ImZoaXI6U3lzdGVtUmVzdGZ1bEludGVyYWN0aW9uIiB4c2k6dHlwZT0iTmFtZWRUeXBlU3BlY2lmaWVyIi8+CiAgICAgICAgIDwvb3BlcmFuZD4KICAgICAgPC9kZWY+CiAgICAgIDxkZWYgbG9jYXRvcj0iMjc4OjEtMjc4OjU1IiBuYW1lPSJUb1N0cmluZyIgY29udGV4dD0iUGF0aWVudCIgYWNjZXNzTGV2ZWw9IlB1YmxpYyIgeHNpOnR5cGU9IkZ1bmN0aW9uRGVmIj4KICAgICAgICAgPGV4cHJlc3Npb24gbG9jYXRvcj0iMjc4OjQ1LTI3ODo1NSIgcGF0aD0idmFsdWUiIHhzaTp0eXBlPSJQcm9wZXJ0eSI+CiAgICAgICAgICAgIDxzb3VyY2UgbG9jYXRvcj0iMjc4OjQ1LTI3ODo0OSIgbmFtZT0idmFsdWUiIHhzaTp0eXBlPSJPcGVyYW5kUmVmIi8+CiAgICAgICAgIDwvZXhwcmVzc2lvbj4KICAgICAgICAgPG9wZXJhbmQgbmFtZT0idmFsdWUiPgogICAgICAgICAgICA8b3BlcmFuZFR5cGVTcGVjaWZpZXIgbG9jYXRvcj0iMjc4OjMyLTI3ODo0MSIgbmFtZT0iZmhpcjpUYXNrSW50ZW50IiB4c2k6dHlwZT0iTmFtZWRUeXBlU3BlY2lmaWVyIi8+CiAgICAgICAgIDwvb3BlcmFuZD4KICAgICAgPC9kZWY+CiAgICAgIDxkZWYgbG9jYXRvcj0iMjc5OjEtMjc5OjU3IiBuYW1lPSJUb1N0cmluZyIgY29udGV4dD0iUGF0aWVudCIgYWNjZXNzTGV2ZWw9IlB1YmxpYyIgeHNpOnR5cGU9IkZ1bmN0aW9uRGVmIj4KICAgICAgICAgPGV4cHJlc3Npb24gbG9jYXRvcj0iMjc5OjQ3LTI3OTo1NyIgcGF0aD0idmFsdWUiIHhzaTp0eXBlPSJQcm9wZXJ0eSI+CiAgICAgICAgICAgIDxzb3VyY2UgbG9jYXRvcj0iMjc5OjQ3LTI3OTo1MSIgbmFtZT0idmFsdWUiIHhzaTp0eXBlPSJPcGVyYW5kUmVmIi8+CiAgICAgICAgIDwvZXhwcmVzc2lvbj4KICAgICAgICAgPG9wZXJhbmQgbmFtZT0idmFsdWUiPgogICAgICAgICAgICA8b3BlcmFuZFR5cGVTcGVjaWZpZXIgbG9jYXRvcj0iMjc5OjMyLTI3OTo0MyIgbmFtZT0iZmhpcjpUYXNrUHJpb3JpdHkiIHhzaTp0eXBlPSJOYW1lZFR5cGVTcGVjaWZpZXIiLz4KICAgICAgICAgPC9vcGVyYW5kPgogICAgICA8L2RlZj4KICAgICAgPGRlZiBsb2NhdG9yPSIyODA6MS0yODA6NTUiIG5hbWU9IlRvU3RyaW5nIiBjb250ZXh0PSJQYXRpZW50IiBhY2Nlc3NMZXZlbD0iUHVibGljIiB4c2k6dHlwZT0iRnVuY3Rpb25EZWYiPgogICAgICAgICA8ZXhwcmVzc2lvbiBsb2NhdG9yPSIyODA6NDUtMjgwOjU1IiBwYXRoPSJ2YWx1ZSIgeHNpOnR5cGU9IlByb3BlcnR5Ij4KICAgICAgICAgICAgPHNvdXJjZSBsb2NhdG9yPSIyODA6NDUtMjgwOjQ5IiBuYW1lPSJ2YWx1ZSIgeHNpOnR5cGU9Ik9wZXJhbmRSZWYiLz4KICAgICAgICAgPC9leHByZXNzaW9uPgogICAgICAgICA8b3BlcmFuZCBuYW1lPSJ2YWx1ZSI+CiAgICAgICAgICAgIDxvcGVyYW5kVHlwZVNwZWNpZmllciBsb2NhdG9yPSIyODA6MzItMjgwOjQxIiBuYW1lPSJmaGlyOlRhc2tTdGF0dXMiIHhzaTp0eXBlPSJOYW1lZFR5cGVTcGVjaWZpZXIiLz4KICAgICAgICAgPC9vcGVyYW5kPgogICAgICA8L2RlZj4KICAgICAgPGRlZiBsb2NhdG9yPSIyODE6MS0yODE6NjciIG5hbWU9IlRvU3RyaW5nIiBjb250ZXh0PSJQYXRpZW50IiBhY2Nlc3NMZXZlbD0iUHVibGljIiB4c2k6dHlwZT0iRnVuY3Rpb25EZWYiPgogICAgICAgICA8ZXhwcmVzc2lvbiBsb2NhdG9yPSIyODE6NTctMjgxOjY3IiBwYXRoPSJ2YWx1ZSIgeHNpOnR5cGU9IlByb3BlcnR5Ij4KICAgICAgICAgICAgPHNvdXJjZSBsb2NhdG9yPSIyODE6NTctMjgxOjYxIiBuYW1lPSJ2YWx1ZSIgeHNpOnR5cGU9Ik9wZXJhbmRSZWYiLz4KICAgICAgICAgPC9leHByZXNzaW9uPgogICAgICAgICA8b3BlcmFuZCBuYW1lPSJ2YWx1ZSI+CiAgICAgICAgICAgIDxvcGVyYW5kVHlwZVNwZWNpZmllciBsb2NhdG9yPSIyODE6MzItMjgxOjUzIiBuYW1lPSJmaGlyOlRlc3RSZXBvcnRBY3Rpb25SZXN1bHQiIHhzaTp0eXBlPSJOYW1lZFR5cGVTcGVjaWZpZXIiLz4KICAgICAgICAgPC9vcGVyYW5kPgogICAgICA8L2RlZj4KICAgICAgPGRlZiBsb2NhdG9yPSIyODI6MS0yODI6NzAiIG5hbWU9IlRvU3RyaW5nIiBjb250ZXh0PSJQYXRpZW50IiBhY2Nlc3NMZXZlbD0iUHVibGljIiB4c2k6dHlwZT0iRnVuY3Rpb25EZWYiPgogICAgICAgICA8ZXhwcmVzc2lvbiBsb2NhdG9yPSIyODI6NjAtMjgyOjcwIiBwYXRoPSJ2YWx1ZSIgeHNpOnR5cGU9IlByb3BlcnR5Ij4KICAgICAgICAgICAgPHNvdXJjZSBsb2NhdG9yPSIyODI6NjAtMjgyOjY0IiBuYW1lPSJ2YWx1ZSIgeHNpOnR5cGU9Ik9wZXJhbmRSZWYiLz4KICAgICAgICAgPC9leHByZXNzaW9uPgogICAgICAgICA8b3BlcmFuZCBuYW1lPSJ2YWx1ZSI+CiAgICAgICAgICAgIDxvcGVyYW5kVHlwZVNwZWNpZmllciBsb2NhdG9yPSIyODI6MzItMjgyOjU2IiBuYW1lPSJmaGlyOlRlc3RSZXBvcnRQYXJ0aWNpcGFudFR5cGUiIHhzaTp0eXBlPSJOYW1lZFR5cGVTcGVjaWZpZXIiLz4KICAgICAgICAgPC9vcGVyYW5kPgogICAgICA8L2RlZj4KICAgICAgPGRlZiBsb2NhdG9yPSIyODM6MS0yODM6NjEiIG5hbWU9IlRvU3RyaW5nIiBjb250ZXh0PSJQYXRpZW50IiBhY2Nlc3NMZXZlbD0iUHVibGljIiB4c2k6dHlwZT0iRnVuY3Rpb25EZWYiPgogICAgICAgICA8ZXhwcmVzc2lvbiBsb2NhdG9yPSIyODM6NTEtMjgzOjYxIiBwYXRoPSJ2YWx1ZSIgeHNpOnR5cGU9IlByb3BlcnR5Ij4KICAgICAgICAgICAgPHNvdXJjZSBsb2NhdG9yPSIyODM6NTEtMjgzOjU1IiBuYW1lPSJ2YWx1ZSIgeHNpOnR5cGU9Ik9wZXJhbmRSZWYiLz4KICAgICAgICAgPC9leHByZXNzaW9uPgogICAgICAgICA8b3BlcmFuZCBuYW1lPSJ2YWx1ZSI+CiAgICAgICAgICAgIDxvcGVyYW5kVHlwZVNwZWNpZmllciBsb2NhdG9yPSIyODM6MzItMjgzOjQ3IiBuYW1lPSJmaGlyOlRlc3RSZXBvcnRSZXN1bHQiIHhzaTp0eXBlPSJOYW1lZFR5cGVTcGVjaWZpZXIiLz4KICAgICAgICAgPC9vcGVyYW5kPgogICAgICA8L2RlZj4KICAgICAgPGRlZiBsb2NhdG9yPSIyODQ6MS0yODQ6NjEiIG5hbWU9IlRvU3RyaW5nIiBjb250ZXh0PSJQYXRpZW50IiBhY2Nlc3NMZXZlbD0iUHVibGljIiB4c2k6dHlwZT0iRnVuY3Rpb25EZWYiPgogICAgICAgICA8ZXhwcmVzc2lvbiBsb2NhdG9yPSIyODQ6NTEtMjg0OjYxIiBwYXRoPSJ2YWx1ZSIgeHNpOnR5cGU9IlByb3BlcnR5Ij4KICAgICAgICAgICAgPHNvdXJjZSBsb2NhdG9yPSIyODQ6NTEtMjg0OjU1IiBuYW1lPSJ2YWx1ZSIgeHNpOnR5cGU9Ik9wZXJhbmRSZWYiLz4KICAgICAgICAgPC9leHByZXNzaW9uPgogICAgICAgICA8b3BlcmFuZCBuYW1lPSJ2YWx1ZSI+CiAgICAgICAgICAgIDxvcGVyYW5kVHlwZVNwZWNpZmllciBsb2NhdG9yPSIyODQ6MzItMjg0OjQ3IiBuYW1lPSJmaGlyOlRlc3RSZXBvcnRTdGF0dXMiIHhzaTp0eXBlPSJOYW1lZFR5cGVTcGVjaWZpZXIiLz4KICAgICAgICAgPC9vcGVyYW5kPgogICAgICA8L2RlZj4KICAgICAgPGRlZiBsb2NhdG9yPSIyODU6MS0yODU6NzIiIG5hbWU9IlRvU3RyaW5nIiBjb250ZXh0PSJQYXRpZW50IiBhY2Nlc3NMZXZlbD0iUHVibGljIiB4c2k6dHlwZT0iRnVuY3Rpb25EZWYiPgogICAgICAgICA8ZXhwcmVzc2lvbiBsb2NhdG9yPSIyODU6NjItMjg1OjcyIiBwYXRoPSJ2YWx1ZSIgeHNpOnR5cGU9IlByb3BlcnR5Ij4KICAgICAgICAgICAgPHNvdXJjZSBsb2NhdG9yPSIyODU6NjItMjg1OjY2IiBuYW1lPSJ2YWx1ZSIgeHNpOnR5cGU9Ik9wZXJhbmRSZWYiLz4KICAgICAgICAgPC9leHByZXNzaW9uPgogICAgICAgICA8b3BlcmFuZCBuYW1lPSJ2YWx1ZSI+CiAgICAgICAgICAgIDxvcGVyYW5kVHlwZVNwZWNpZmllciBsb2NhdG9yPSIyODU6MzItMjg1OjU4IiBuYW1lPSJmaGlyOlRlc3RTY3JpcHRSZXF1ZXN0TWV0aG9kQ29kZSIgeHNpOnR5cGU9Ik5hbWVkVHlwZVNwZWNpZmllciIvPgogICAgICAgICA8L29wZXJhbmQ+CiAgICAgIDwvZGVmPgogICAgICA8ZGVmIGxvY2F0b3I9IjI4NjoxLTI4Njo1NiIgbmFtZT0iVG9TdHJpbmciIGNvbnRleHQ9IlBhdGllbnQiIGFjY2Vzc0xldmVsPSJQdWJsaWMiIHhzaTp0eXBlPSJGdW5jdGlvbkRlZiI+CiAgICAgICAgIDxleHByZXNzaW9uIGxvY2F0b3I9IjI4Njo0Ni0yODY6NTYiIHBhdGg9InZhbHVlIiB4c2k6dHlwZT0iUHJvcGVydHkiPgogICAgICAgICAgICA8c291cmNlIGxvY2F0b3I9IjI4Njo0Ni0yODY6NTAiIG5hbWU9InZhbHVlIiB4c2k6dHlwZT0iT3BlcmFuZFJlZiIvPgogICAgICAgICA8L2V4cHJlc3Npb24+CiAgICAgICAgIDxvcGVyYW5kIG5hbWU9InZhbHVlIj4KICAgICAgICAgICAgPG9wZXJhbmRUeXBlU3BlY2lmaWVyIGxvY2F0b3I9IjI4NjozMi0yODY6NDIiIG5hbWU9ImZoaXI6VHJpZ2dlclR5cGUiIHhzaTp0eXBlPSJOYW1lZFR5cGVTcGVjaWZpZXIiLz4KICAgICAgICAgPC9vcGVyYW5kPgogICAgICA8L2RlZj4KICAgICAgPGRlZiBsb2NhdG9yPSIyODc6MS0yODc6NjMiIG5hbWU9IlRvU3RyaW5nIiBjb250ZXh0PSJQYXRpZW50IiBhY2Nlc3NMZXZlbD0iUHVibGljIiB4c2k6dHlwZT0iRnVuY3Rpb25EZWYiPgogICAgICAgICA8ZXhwcmVzc2lvbiBsb2NhdG9yPSIyODc6NTMtMjg3OjYzIiBwYXRoPSJ2YWx1ZSIgeHNpOnR5cGU9IlByb3BlcnR5Ij4KICAgICAgICAgICAgPHNvdXJjZSBsb2NhdG9yPSIyODc6NTMtMjg3OjU3IiBuYW1lPSJ2YWx1ZSIgeHNpOnR5cGU9Ik9wZXJhbmRSZWYiLz4KICAgICAgICAgPC9leHByZXNzaW9uPgogICAgICAgICA8b3BlcmFuZCBuYW1lPSJ2YWx1ZSI+CiAgICAgICAgICAgIDxvcGVyYW5kVHlwZVNwZWNpZmllciBsb2NhdG9yPSIyODc6MzItMjg3OjQ5IiBuYW1lPSJmaGlyOlR5cGVEZXJpdmF0aW9uUnVsZSIgeHNpOnR5cGU9Ik5hbWVkVHlwZVNwZWNpZmllciIvPgogICAgICAgICA8L29wZXJhbmQ+CiAgICAgIDwvZGVmPgogICAgICA8ZGVmIGxvY2F0b3I9IjI4ODoxLTI4ODo2NyIgbmFtZT0iVG9TdHJpbmciIGNvbnRleHQ9IlBhdGllbnQiIGFjY2Vzc0xldmVsPSJQdWJsaWMiIHhzaTp0eXBlPSJGdW5jdGlvbkRlZiI+CiAgICAgICAgIDxleHByZXNzaW9uIGxvY2F0b3I9IjI4ODo1Ny0yODg6NjciIHBhdGg9InZhbHVlIiB4c2k6dHlwZT0iUHJvcGVydHkiPgogICAgICAgICAgICA8c291cmNlIGxvY2F0b3I9IjI4ODo1Ny0yODg6NjEiIG5hbWU9InZhbHVlIiB4c2k6dHlwZT0iT3BlcmFuZFJlZiIvPgogICAgICAgICA8L2V4cHJlc3Npb24+CiAgICAgICAgIDxvcGVyYW5kIG5hbWU9InZhbHVlIj4KICAgICAgICAgICAgPG9wZXJhbmRUeXBlU3BlY2lmaWVyIGxvY2F0b3I9IjI4ODozMi0yODg6NTMiIG5hbWU9ImZoaXI6VHlwZVJlc3RmdWxJbnRlcmFjdGlvbiIgeHNpOnR5cGU9Ik5hbWVkVHlwZVNwZWNpZmllciIvPgogICAgICAgICA8L29wZXJhbmQ+CiAgICAgIDwvZGVmPgogICAgICA8ZGVmIGxvY2F0b3I9IjI4OToxLTI4OTo1NyIgbmFtZT0iVG9TdHJpbmciIGNvbnRleHQ9IlBhdGllbnQiIGFjY2Vzc0xldmVsPSJQdWJsaWMiIHhzaTp0eXBlPSJGdW5jdGlvbkRlZiI+CiAgICAgICAgIDxleHByZXNzaW9uIGxvY2F0b3I9IjI4OTo0Ny0yODk6NTciIHBhdGg9InZhbHVlIiB4c2k6dHlwZT0iUHJvcGVydHkiPgogICAgICAgICAgICA8c291cmNlIGxvY2F0b3I9IjI4OTo0Ny0yODk6NTEiIG5hbWU9InZhbHVlIiB4c2k6dHlwZT0iT3BlcmFuZFJlZiIvPgogICAgICAgICA8L2V4cHJlc3Npb24+CiAgICAgICAgIDxvcGVyYW5kIG5hbWU9InZhbHVlIj4KICAgICAgICAgICAgPG9wZXJhbmRUeXBlU3BlY2lmaWVyIGxvY2F0b3I9IjI4OTozMi0yODk6NDMiIG5hbWU9ImZoaXI6VURJRW50cnlUeXBlIiB4c2k6dHlwZT0iTmFtZWRUeXBlU3BlY2lmaWVyIi8+CiAgICAgICAgIDwvb3BlcmFuZD4KICAgICAgPC9kZWY+CiAgICAgIDxkZWYgbG9jYXRvcj0iMjkwOjEtMjkwOjU2IiBuYW1lPSJUb1N0cmluZyIgY29udGV4dD0iUGF0aWVudCIgYWNjZXNzTGV2ZWw9IlB1YmxpYyIgeHNpOnR5cGU9IkZ1bmN0aW9uRGVmIj4KICAgICAgICAgPGV4cHJlc3Npb24gbG9jYXRvcj0iMjkwOjQ2LTI5MDo1NiIgcGF0aD0idmFsdWUiIHhzaTp0eXBlPSJQcm9wZXJ0eSI+CiAgICAgICAgICAgIDxzb3VyY2UgbG9jYXRvcj0iMjkwOjQ2LTI5MDo1MCIgbmFtZT0idmFsdWUiIHhzaTp0eXBlPSJPcGVyYW5kUmVmIi8+CiAgICAgICAgIDwvZXhwcmVzc2lvbj4KICAgICAgICAgPG9wZXJhbmQgbmFtZT0idmFsdWUiPgogICAgICAgICAgICA8b3BlcmFuZFR5cGVTcGVjaWZpZXIgbG9jYXRvcj0iMjkwOjMyLTI5MDo0MiIgbmFtZT0iZmhpcjpVbml0c09mVGltZSIgeHNpOnR5cGU9Ik5hbWVkVHlwZVNwZWNpZmllciIvPgogICAgICAgICA8L29wZXJhbmQ+CiAgICAgIDwvZGVmPgogICAgICA8ZGVmIGxvY2F0b3I9IjI5MToxLTI5MTo0OCIgbmFtZT0iVG9TdHJpbmciIGNvbnRleHQ9IlBhdGllbnQiIGFjY2Vzc0xldmVsPSJQdWJsaWMiIHhzaTp0eXBlPSJGdW5jdGlvbkRlZiI+CiAgICAgICAgIDxleHByZXNzaW9uIGxvY2F0b3I9IjI5MTozOC0yOTE6NDgiIHBhdGg9InZhbHVlIiB4c2k6dHlwZT0iUHJvcGVydHkiPgogICAgICAgICAgICA8c291cmNlIGxvY2F0b3I9IjI5MTozOC0yOTE6NDIiIG5hbWU9InZhbHVlIiB4c2k6dHlwZT0iT3BlcmFuZFJlZiIvPgogICAgICAgICA8L2V4cHJlc3Npb24+CiAgICAgICAgIDxvcGVyYW5kIG5hbWU9InZhbHVlIj4KICAgICAgICAgICAgPG9wZXJhbmRUeXBlU3BlY2lmaWVyIGxvY2F0b3I9IjI5MTozMi0yOTE6MzQiIG5hbWU9ImZoaXI6VXNlIiB4c2k6dHlwZT0iTmFtZWRUeXBlU3BlY2lmaWVyIi8+CiAgICAgICAgIDwvb3BlcmFuZD4KICAgICAgPC9kZWY+CiAgICAgIDxkZWYgbG9jYXRvcj0iMjkyOjEtMjkyOjU3IiBuYW1lPSJUb1N0cmluZyIgY29udGV4dD0iUGF0aWVudCIgYWNjZXNzTGV2ZWw9IlB1YmxpYyIgeHNpOnR5cGU9IkZ1bmN0aW9uRGVmIj4KICAgICAgICAgPGV4cHJlc3Npb24gbG9jYXRvcj0iMjkyOjQ3LTI5Mjo1NyIgcGF0aD0idmFsdWUiIHhzaTp0eXBlPSJQcm9wZXJ0eSI+CiAgICAgICAgICAgIDxzb3VyY2UgbG9jYXRvcj0iMjkyOjQ3LTI5Mjo1MSIgbmFtZT0idmFsdWUiIHhzaTp0eXBlPSJPcGVyYW5kUmVmIi8+CiAgICAgICAgIDwvZXhwcmVzc2lvbj4KICAgICAgICAgPG9wZXJhbmQgbmFtZT0idmFsdWUiPgogICAgICAgICAgICA8b3BlcmFuZFR5cGVTcGVjaWZpZXIgbG9jYXRvcj0iMjkyOjMyLTI5Mjo0MyIgbmFtZT0iZmhpcjpWYXJpYWJsZVR5cGUiIHhzaTp0eXBlPSJOYW1lZFR5cGVTcGVjaWZpZXIiLz4KICAgICAgICAgPC9vcGVyYW5kPgogICAgICA8L2RlZj4KICAgICAgPGRlZiBsb2NhdG9yPSIyOTM6MS0yOTM6NTUiIG5hbWU9IlRvU3RyaW5nIiBjb250ZXh0PSJQYXRpZW50IiBhY2Nlc3NMZXZlbD0iUHVibGljIiB4c2k6dHlwZT0iRnVuY3Rpb25EZWYiPgogICAgICAgICA8ZXhwcmVzc2lvbiBsb2NhdG9yPSIyOTM6NDUtMjkzOjU1IiBwYXRoPSJ2YWx1ZSIgeHNpOnR5cGU9IlByb3BlcnR5Ij4KICAgICAgICAgICAgPHNvdXJjZSBsb2NhdG9yPSIyOTM6NDUtMjkzOjQ5IiBuYW1lPSJ2YWx1ZSIgeHNpOnR5cGU9Ik9wZXJhbmRSZWYiLz4KICAgICAgICAgPC9leHByZXNzaW9uPgogICAgICAgICA8b3BlcmFuZCBuYW1lPSJ2YWx1ZSI+CiAgICAgICAgICAgIDxvcGVyYW5kVHlwZVNwZWNpZmllciBsb2NhdG9yPSIyOTM6MzItMjkzOjQxIiBuYW1lPSJmaGlyOlZpc2lvbkJhc2UiIHhzaTp0eXBlPSJOYW1lZFR5cGVTcGVjaWZpZXIiLz4KICAgICAgICAgPC9vcGVyYW5kPgogICAgICA8L2RlZj4KICAgICAgPGRlZiBsb2NhdG9yPSIyOTQ6MS0yOTQ6NTUiIG5hbWU9IlRvU3RyaW5nIiBjb250ZXh0PSJQYXRpZW50IiBhY2Nlc3NMZXZlbD0iUHVibGljIiB4c2k6dHlwZT0iRnVuY3Rpb25EZWYiPgogICAgICAgICA8ZXhwcmVzc2lvbiBsb2NhdG9yPSIyOTQ6NDUtMjk0OjU1IiBwYXRoPSJ2YWx1ZSIgeHNpOnR5cGU9IlByb3BlcnR5Ij4KICAgICAgICAgICAgPHNvdXJjZSBsb2NhdG9yPSIyOTQ6NDUtMjk0OjQ5IiBuYW1lPSJ2YWx1ZSIgeHNpOnR5cGU9Ik9wZXJhbmRSZWYiLz4KICAgICAgICAgPC9leHByZXNzaW9uPgogICAgICAgICA8b3BlcmFuZCBuYW1lPSJ2YWx1ZSI+CiAgICAgICAgICAgIDxvcGVyYW5kVHlwZVNwZWNpZmllciBsb2NhdG9yPSIyOTQ6MzItMjk0OjQxIiBuYW1lPSJmaGlyOlZpc2lvbkV5ZXMiIHhzaTp0eXBlPSJOYW1lZFR5cGVTcGVjaWZpZXIiLz4KICAgICAgICAgPC9vcGVyYW5kPgogICAgICA8L2RlZj4KICAgICAgPGRlZiBsb2NhdG9yPSIyOTU6MS0yOTU6NTciIG5hbWU9IlRvU3RyaW5nIiBjb250ZXh0PSJQYXRpZW50IiBhY2Nlc3NMZXZlbD0iUHVibGljIiB4c2k6dHlwZT0iRnVuY3Rpb25EZWYiPgogICAgICAgICA8ZXhwcmVzc2lvbiBsb2NhdG9yPSIyOTU6NDctMjk1OjU3IiBwYXRoPSJ2YWx1ZSIgeHNpOnR5cGU9IlByb3BlcnR5Ij4KICAgICAgICAgICAgPHNvdXJjZSBsb2NhdG9yPSIyOTU6NDctMjk1OjUxIiBuYW1lPSJ2YWx1ZSIgeHNpOnR5cGU9Ik9wZXJhbmRSZWYiLz4KICAgICAgICAgPC9leHByZXNzaW9uPgogICAgICAgICA8b3BlcmFuZCBuYW1lPSJ2YWx1ZSI+CiAgICAgICAgICAgIDxvcGVyYW5kVHlwZVNwZWNpZmllciBsb2NhdG9yPSIyOTU6MzItMjk1OjQzIiBuYW1lPSJmaGlyOlZpc2lvblN0YXR1cyIgeHNpOnR5cGU9Ik5hbWVkVHlwZVNwZWNpZmllciIvPgogICAgICAgICA8L29wZXJhbmQ+CiAgICAgIDwvZGVmPgogICAgICA8ZGVmIGxvY2F0b3I9IjI5NjoxLTI5Njo1OSIgbmFtZT0iVG9TdHJpbmciIGNvbnRleHQ9IlBhdGllbnQiIGFjY2Vzc0xldmVsPSJQdWJsaWMiIHhzaTp0eXBlPSJGdW5jdGlvbkRlZiI+CiAgICAgICAgIDxleHByZXNzaW9uIGxvY2F0b3I9IjI5Njo0OS0yOTY6NTkiIHBhdGg9InZhbHVlIiB4c2k6dHlwZT0iUHJvcGVydHkiPgogICAgICAgICAgICA8c291cmNlIGxvY2F0b3I9IjI5Njo0OS0yOTY6NTMiIG5hbWU9InZhbHVlIiB4c2k6dHlwZT0iT3BlcmFuZFJlZiIvPgogICAgICAgICA8L2V4cHJlc3Npb24+CiAgICAgICAgIDxvcGVyYW5kIG5hbWU9InZhbHVlIj4KICAgICAgICAgICAgPG9wZXJhbmRUeXBlU3BlY2lmaWVyIGxvY2F0b3I9IjI5NjozMi0yOTY6NDUiIG5hbWU9ImZoaXI6WFBhdGhVc2FnZVR5cGUiIHhzaTp0eXBlPSJOYW1lZFR5cGVTcGVjaWZpZXIiLz4KICAgICAgICAgPC9vcGVyYW5kPgogICAgICA8L2RlZj4KICAgICAgPGRlZiBsb2NhdG9yPSIyOTc6MS0yOTc6NTciIG5hbWU9IlRvU3RyaW5nIiBjb250ZXh0PSJQYXRpZW50IiBhY2Nlc3NMZXZlbD0iUHVibGljIiB4c2k6dHlwZT0iRnVuY3Rpb25EZWYiPgogICAgICAgICA8ZXhwcmVzc2lvbiBsb2NhdG9yPSIyOTc6NDctMjk3OjU3IiBwYXRoPSJ2YWx1ZSIgeHNpOnR5cGU9IlByb3BlcnR5Ij4KICAgICAgICAgICAgPHNvdXJjZSBsb2NhdG9yPSIyOTc6NDctMjk3OjUxIiBuYW1lPSJ2YWx1ZSIgeHNpOnR5cGU9Ik9wZXJhbmRSZWYiLz4KICAgICAgICAgPC9leHByZXNzaW9uPgogICAgICAgICA8b3BlcmFuZCBuYW1lPSJ2YWx1ZSI+CiAgICAgICAgICAgIDxvcGVyYW5kVHlwZVNwZWNpZmllciBsb2NhdG9yPSIyOTc6MzItMjk3OjQzIiBuYW1lPSJmaGlyOmJhc2U2NEJpbmFyeSIgeHNpOnR5cGU9Ik5hbWVkVHlwZVNwZWNpZmllciIvPgogICAgICAgICA8L29wZXJhbmQ+CiAgICAgIDwvZGVmPgogICAgICA8ZGVmIGxvY2F0b3I9IjI5ODoxLTI5ODo0NyIgbmFtZT0iVG9TdHJpbmciIGNvbnRleHQ9IlBhdGllbnQiIGFjY2Vzc0xldmVsPSJQdWJsaWMiIHhzaTp0eXBlPSJGdW5jdGlvbkRlZiI+CiAgICAgICAgIDxleHByZXNzaW9uIGxvY2F0b3I9IjI5ODozNy0yOTg6NDciIHBhdGg9InZhbHVlIiB4c2k6dHlwZT0iUHJvcGVydHkiPgogICAgICAgICAgICA8c291cmNlIGxvY2F0b3I9IjI5ODozNy0yOTg6NDEiIG5hbWU9InZhbHVlIiB4c2k6dHlwZT0iT3BlcmFuZFJlZiIvPgogICAgICAgICA8L2V4cHJlc3Npb24+CiAgICAgICAgIDxvcGVyYW5kIG5hbWU9InZhbHVlIj4KICAgICAgICAgICAgPG9wZXJhbmRUeXBlU3BlY2lmaWVyIGxvY2F0b3I9IjI5ODozMi0yOTg6MzMiIG5hbWU9ImZoaXI6aWQiIHhzaTp0eXBlPSJOYW1lZFR5cGVTcGVjaWZpZXIiLz4KICAgICAgICAgPC9vcGVyYW5kPgogICAgICA8L2RlZj4KICAgICAgPGRlZiBsb2NhdG9yPSIyOTk6MS0yOTk6NTMiIG5hbWU9IlRvQm9vbGVhbiIgY29udGV4dD0iUGF0aWVudCIgYWNjZXNzTGV2ZWw9IlB1YmxpYyIgeHNpOnR5cGU9IkZ1bmN0aW9uRGVmIj4KICAgICAgICAgPGV4cHJlc3Npb24gbG9jYXRvcj0iMjk5OjQzLTI5OTo1MyIgcGF0aD0idmFsdWUiIHhzaTp0eXBlPSJQcm9wZXJ0eSI+CiAgICAgICAgICAgIDxzb3VyY2UgbG9jYXRvcj0iMjk5OjQzLTI5OTo0NyIgbmFtZT0idmFsdWUiIHhzaTp0eXBlPSJPcGVyYW5kUmVmIi8+CiAgICAgICAgIDwvZXhwcmVzc2lvbj4KICAgICAgICAgPG9wZXJhbmQgbmFtZT0idmFsdWUiPgogICAgICAgICAgICA8b3BlcmFuZFR5cGVTcGVjaWZpZXIgbG9jYXRvcj0iMjk5OjMzLTI5OTozOSIgbmFtZT0iZmhpcjpib29sZWFuIiB4c2k6dHlwZT0iTmFtZWRUeXBlU3BlY2lmaWVyIi8+CiAgICAgICAgIDwvb3BlcmFuZD4KICAgICAgPC9kZWY+CiAgICAgIDxkZWYgbG9jYXRvcj0iMzAwOjEtMzAwOjQ3IiBuYW1lPSJUb0RhdGUiIGNvbnRleHQ9IlBhdGllbnQiIGFjY2Vzc0xldmVsPSJQdWJsaWMiIHhzaTp0eXBlPSJGdW5jdGlvbkRlZiI+CiAgICAgICAgIDxleHByZXNzaW9uIGxvY2F0b3I9IjMwMDozNy0zMDA6NDciIHBhdGg9InZhbHVlIiB4c2k6dHlwZT0iUHJvcGVydHkiPgogICAgICAgICAgICA8c291cmNlIGxvY2F0b3I9IjMwMDozNy0zMDA6NDEiIG5hbWU9InZhbHVlIiB4c2k6dHlwZT0iT3BlcmFuZFJlZiIvPgogICAgICAgICA8L2V4cHJlc3Npb24+CiAgICAgICAgIDxvcGVyYW5kIG5hbWU9InZhbHVlIj4KICAgICAgICAgICAgPG9wZXJhbmRUeXBlU3BlY2lmaWVyIGxvY2F0b3I9IjMwMDozMC0zMDA6MzMiIG5hbWU9ImZoaXI6ZGF0ZSIgeHNpOnR5cGU9Ik5hbWVkVHlwZVNwZWNpZmllciIvPgogICAgICAgICA8L29wZXJhbmQ+CiAgICAgIDwvZGVmPgogICAgICA8ZGVmIGxvY2F0b3I9IjMwMToxLTMwMTo1NSIgbmFtZT0iVG9EYXRlVGltZSIgY29udGV4dD0iUGF0aWVudCIgYWNjZXNzTGV2ZWw9IlB1YmxpYyIgeHNpOnR5cGU9IkZ1bmN0aW9uRGVmIj4KICAgICAgICAgPGV4cHJlc3Npb24gbG9jYXRvcj0iMzAxOjQ1LTMwMTo1NSIgcGF0aD0idmFsdWUiIHhzaTp0eXBlPSJQcm9wZXJ0eSI+CiAgICAgICAgICAgIDxzb3VyY2UgbG9jYXRvcj0iMzAxOjQ1LTMwMTo0OSIgbmFtZT0idmFsdWUiIHhzaTp0eXBlPSJPcGVyYW5kUmVmIi8+CiAgICAgICAgIDwvZXhwcmVzc2lvbj4KICAgICAgICAgPG9wZXJhbmQgbmFtZT0idmFsdWUiPgogICAgICAgICAgICA8b3BlcmFuZFR5cGVTcGVjaWZpZXIgbG9jYXRvcj0iMzAxOjM0LTMwMTo0MSIgbmFtZT0iZmhpcjpkYXRlVGltZSIgeHNpOnR5cGU9Ik5hbWVkVHlwZVNwZWNpZmllciIvPgogICAgICAgICA8L29wZXJhbmQ+CiAgICAgIDwvZGVmPgogICAgICA8ZGVmIGxvY2F0b3I9IjMwMjoxLTMwMjo1MyIgbmFtZT0iVG9EZWNpbWFsIiBjb250ZXh0PSJQYXRpZW50IiBhY2Nlc3NMZXZlbD0iUHVibGljIiB4c2k6dHlwZT0iRnVuY3Rpb25EZWYiPgogICAgICAgICA8ZXhwcmVzc2lvbiBsb2NhdG9yPSIzMDI6NDMtMzAyOjUzIiBwYXRoPSJ2YWx1ZSIgeHNpOnR5cGU9IlByb3BlcnR5Ij4KICAgICAgICAgICAgPHNvdXJjZSBsb2NhdG9yPSIzMDI6NDMtMzAyOjQ3IiBuYW1lPSJ2YWx1ZSIgeHNpOnR5cGU9Ik9wZXJhbmRSZWYiLz4KICAgICAgICAgPC9leHByZXNzaW9uPgogICAgICAgICA8b3BlcmFuZCBuYW1lPSJ2YWx1ZSI+CiAgICAgICAgICAgIDxvcGVyYW5kVHlwZVNwZWNpZmllciBsb2NhdG9yPSIzMDI6MzMtMzAyOjM5IiBuYW1lPSJmaGlyOmRlY2ltYWwiIHhzaTp0eXBlPSJOYW1lZFR5cGVTcGVjaWZpZXIiLz4KICAgICAgICAgPC9vcGVyYW5kPgogICAgICA8L2RlZj4KICAgICAgPGRlZiBsb2NhdG9yPSIzMDM6MS0zMDM6NTQiIG5hbWU9IlRvRGF0ZVRpbWUiIGNvbnRleHQ9IlBhdGllbnQiIGFjY2Vzc0xldmVsPSJQdWJsaWMiIHhzaTp0eXBlPSJGdW5jdGlvbkRlZiI+CiAgICAgICAgIDxleHByZXNzaW9uIGxvY2F0b3I9IjMwMzo0NC0zMDM6NTQiIHBhdGg9InZhbHVlIiB4c2k6dHlwZT0iUHJvcGVydHkiPgogICAgICAgICAgICA8c291cmNlIGxvY2F0b3I9IjMwMzo0NC0zMDM6NDgiIG5hbWU9InZhbHVlIiB4c2k6dHlwZT0iT3BlcmFuZFJlZiIvPgogICAgICAgICA8L2V4cHJlc3Npb24+CiAgICAgICAgIDxvcGVyYW5kIG5hbWU9InZhbHVlIj4KICAgICAgICAgICAgPG9wZXJhbmRUeXBlU3BlY2lmaWVyIGxvY2F0b3I9IjMwMzozNC0zMDM6NDAiIG5hbWU9ImZoaXI6aW5zdGFudCIgeHNpOnR5cGU9Ik5hbWVkVHlwZVNwZWNpZmllciIvPgogICAgICAgICA8L29wZXJhbmQ+CiAgICAgIDwvZGVmPgogICAgICA8ZGVmIGxvY2F0b3I9IjMwNDoxLTMwNDo1MyIgbmFtZT0iVG9JbnRlZ2VyIiBjb250ZXh0PSJQYXRpZW50IiBhY2Nlc3NMZXZlbD0iUHVibGljIiB4c2k6dHlwZT0iRnVuY3Rpb25EZWYiPgogICAgICAgICA8ZXhwcmVzc2lvbiBsb2NhdG9yPSIzMDQ6NDMtMzA0OjUzIiBwYXRoPSJ2YWx1ZSIgeHNpOnR5cGU9IlByb3BlcnR5Ij4KICAgICAgICAgICAgPHNvdXJjZSBsb2NhdG9yPSIzMDQ6NDMtMzA0OjQ3IiBuYW1lPSJ2YWx1ZSIgeHNpOnR5cGU9Ik9wZXJhbmRSZWYiLz4KICAgICAgICAgPC9leHByZXNzaW9uPgogICAgICAgICA8b3BlcmFuZCBuYW1lPSJ2YWx1ZSI+CiAgICAgICAgICAgIDxvcGVyYW5kVHlwZVNwZWNpZmllciBsb2NhdG9yPSIzMDQ6MzMtMzA0OjM5IiBuYW1lPSJmaGlyOmludGVnZXIiIHhzaTp0eXBlPSJOYW1lZFR5cGVTcGVjaWZpZXIiLz4KICAgICAgICAgPC9vcGVyYW5kPgogICAgICA8L2RlZj4KICAgICAgPGRlZiBsb2NhdG9yPSIzMDU6MS0zMDU6NTEiIG5hbWU9IlRvU3RyaW5nIiBjb250ZXh0PSJQYXRpZW50IiBhY2Nlc3NMZXZlbD0iUHVibGljIiB4c2k6dHlwZT0iRnVuY3Rpb25EZWYiPgogICAgICAgICA8ZXhwcmVzc2lvbiBsb2NhdG9yPSIzMDU6NDEtMzA1OjUxIiBwYXRoPSJ2YWx1ZSIgeHNpOnR5cGU9IlByb3BlcnR5Ij4KICAgICAgICAgICAgPHNvdXJjZSBsb2NhdG9yPSIzMDU6NDEtMzA1OjQ1IiBuYW1lPSJ2YWx1ZSIgeHNpOnR5cGU9Ik9wZXJhbmRSZWYiLz4KICAgICAgICAgPC9leHByZXNzaW9uPgogICAgICAgICA8b3BlcmFuZCBuYW1lPSJ2YWx1ZSI+CiAgICAgICAgICAgIDxvcGVyYW5kVHlwZVNwZWNpZmllciBsb2NhdG9yPSIzMDU6MzItMzA1OjM3IiBuYW1lPSJmaGlyOnN0cmluZyIgeHNpOnR5cGU9Ik5hbWVkVHlwZVNwZWNpZmllciIvPgogICAgICAgICA8L29wZXJhbmQ+CiAgICAgIDwvZGVmPgogICAgICA8ZGVmIGxvY2F0b3I9IjMwNjoxLTMwNjo0NyIgbmFtZT0iVG9UaW1lIiBjb250ZXh0PSJQYXRpZW50IiBhY2Nlc3NMZXZlbD0iUHVibGljIiB4c2k6dHlwZT0iRnVuY3Rpb25EZWYiPgogICAgICAgICA8ZXhwcmVzc2lvbiBsb2NhdG9yPSIzMDY6MzctMzA2OjQ3IiBwYXRoPSJ2YWx1ZSIgeHNpOnR5cGU9IlByb3BlcnR5Ij4KICAgICAgICAgICAgPHNvdXJjZSBsb2NhdG9yPSIzMDY6MzctMzA2OjQxIiBuYW1lPSJ2YWx1ZSIgeHNpOnR5cGU9Ik9wZXJhbmRSZWYiLz4KICAgICAgICAgPC9leHByZXNzaW9uPgogICAgICAgICA8b3BlcmFuZCBuYW1lPSJ2YWx1ZSI+CiAgICAgICAgICAgIDxvcGVyYW5kVHlwZVNwZWNpZmllciBsb2NhdG9yPSIzMDY6MzAtMzA2OjMzIiBuYW1lPSJmaGlyOnRpbWUiIHhzaTp0eXBlPSJOYW1lZFR5cGVTcGVjaWZpZXIiLz4KICAgICAgICAgPC9vcGVyYW5kPgogICAgICA8L2RlZj4KICAgICAgPGRlZiBsb2NhdG9yPSIzMDc6MS0zMDc6NDgiIG5hbWU9IlRvU3RyaW5nIiBjb250ZXh0PSJQYXRpZW50IiBhY2Nlc3NMZXZlbD0iUHVibGljIiB4c2k6dHlwZT0iRnVuY3Rpb25EZWYiPgogICAgICAgICA8ZXhwcmVzc2lvbiBsb2NhdG9yPSIzMDc6MzgtMzA3OjQ4IiBwYXRoPSJ2YWx1ZSIgeHNpOnR5cGU9IlByb3BlcnR5Ij4KICAgICAgICAgICAgPHNvdXJjZSBsb2NhdG9yPSIzMDc6MzgtMzA3OjQyIiBuYW1lPSJ2YWx1ZSIgeHNpOnR5cGU9Ik9wZXJhbmRSZWYiLz4KICAgICAgICAgPC9leHByZXNzaW9uPgogICAgICAgICA8b3BlcmFuZCBuYW1lPSJ2YWx1ZSI+CiAgICAgICAgICAgIDxvcGVyYW5kVHlwZVNwZWNpZmllciBsb2NhdG9yPSIzMDc6MzItMzA3OjM0IiBuYW1lPSJmaGlyOnVyaSIgeHNpOnR5cGU9Ik5hbWVkVHlwZVNwZWNpZmllciIvPgogICAgICAgICA8L29wZXJhbmQ+CiAgICAgIDwvZGVmPgogICAgICA8ZGVmIGxvY2F0b3I9IjMwODoxLTMwODo1MCIgbmFtZT0iVG9TdHJpbmciIGNvbnRleHQ9IlBhdGllbnQiIGFjY2Vzc0xldmVsPSJQdWJsaWMiIHhzaTp0eXBlPSJGdW5jdGlvbkRlZiI+CiAgICAgICAgIDxleHByZXNzaW9uIGxvY2F0b3I9IjMwODo0MC0zMDg6NTAiIHBhdGg9InZhbHVlIiB4c2k6dHlwZT0iUHJvcGVydHkiPgogICAgICAgICAgICA8c291cmNlIGxvY2F0b3I9IjMwODo0MC0zMDg6NDQiIG5hbWU9InZhbHVlIiB4c2k6dHlwZT0iT3BlcmFuZFJlZiIvPgogICAgICAgICA8L2V4cHJlc3Npb24+CiAgICAgICAgIDxvcGVyYW5kIG5hbWU9InZhbHVlIj4KICAgICAgICAgICAgPG9wZXJhbmRUeXBlU3BlY2lmaWVyIGxvY2F0b3I9IjMwODozMi0zMDg6MzYiIG5hbWU9ImZoaXI6eGh0bWwiIHhzaTp0eXBlPSJOYW1lZFR5cGVTcGVjaWZpZXIiLz4KICAgICAgICAgPC9vcGVyYW5kPgogICAgICA8L2RlZj4KICAgPC9zdGF0ZW1lbnRzPgo8L2xpYnJhcnk+Cg==" - }, - { - "contentType": "application/elm+json", - "data": "ewogICAibGlicmFyeSIgOiB7CiAgICAgICJhbm5vdGF0aW9uIiA6IFsgewogICAgICAgICAidHJhbnNsYXRvclZlcnNpb24iIDogIjIuNy4wIiwKICAgICAgICAgInRyYW5zbGF0b3JPcHRpb25zIiA6ICJFbmFibGVMb2NhdG9ycyxEaXNhYmxlTGlzdERlbW90aW9uLERpc2FibGVMaXN0UHJvbW90aW9uIiwKICAgICAgICAgInR5cGUiIDogIkNxbFRvRWxtSW5mbyIKICAgICAgfSBdLAogICAgICAiaWRlbnRpZmllciIgOiB7CiAgICAgICAgICJpZCIgOiAiRFFNRkhJUkhlbHBlcnMiLAogICAgICAgICAic3lzdGVtIiA6ICJodHRwOi8vY29udGVudC5hbHBob3JhLmNvbS9maGlyL2RxbSIsCiAgICAgICAgICJ2ZXJzaW9uIiA6ICI0LjAuMSIKICAgICAgfSwKICAgICAgInNjaGVtYUlkZW50aWZpZXIiIDogewogICAgICAgICAiaWQiIDogInVybjpobDctb3JnOmVsbSIsCiAgICAgICAgICJ2ZXJzaW9uIiA6ICJyMSIKICAgICAgfSwKICAgICAgInVzaW5ncyIgOiB7CiAgICAgICAgICJkZWYiIDogWyB7CiAgICAgICAgICAgICJsb2NhbElkZW50aWZpZXIiIDogIlN5c3RlbSIsCiAgICAgICAgICAgICJ1cmkiIDogInVybjpobDctb3JnOmVsbS10eXBlczpyMSIKICAgICAgICAgfSwgewogICAgICAgICAgICAibG9jYXRvciIgOiAiMzoxLTM6MjYiLAogICAgICAgICAgICAibG9jYWxJZGVudGlmaWVyIiA6ICJGSElSIiwKICAgICAgICAgICAgInVyaSIgOiAiaHR0cDovL2hsNy5vcmcvZmhpciIsCiAgICAgICAgICAgICJ2ZXJzaW9uIiA6ICI0LjAuMSIKICAgICAgICAgfSBdCiAgICAgIH0sCiAgICAgICJzdGF0ZW1lbnRzIiA6IHsKICAgICAgICAgImRlZiIgOiBbIHsKICAgICAgICAgICAgImxvY2F0b3IiIDogIjU6MS05OjU4IiwKICAgICAgICAgICAgIm5hbWUiIDogIlRvSW50ZXJ2YWwiLAogICAgICAgICAgICAiY29udGV4dCIgOiAiUGF0aWVudCIsCiAgICAgICAgICAgICJhY2Nlc3NMZXZlbCIgOiAiUHVibGljIiwKICAgICAgICAgICAgInR5cGUiIDogIkZ1bmN0aW9uRGVmIiwKICAgICAgICAgICAgImV4cHJlc3Npb24iIDogewogICAgICAgICAgICAgICAibG9jYXRvciIgOiAiNjo1LTk6NTgiLAogICAgICAgICAgICAgICAidHlwZSIgOiAiSWYiLAogICAgICAgICAgICAgICAiY29uZGl0aW9uIiA6IHsKICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjY6OC02OjIxIiwKICAgICAgICAgICAgICAgICAgInR5cGUiIDogIklzTnVsbCIsCiAgICAgICAgICAgICAgICAgICJvcGVyYW5kIiA6IHsKICAgICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjY6OC02OjEzIiwKICAgICAgICAgICAgICAgICAgICAgIm5hbWUiIDogInBlcmlvZCIsCiAgICAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJPcGVyYW5kUmVmIgogICAgICAgICAgICAgICAgICB9CiAgICAgICAgICAgICAgIH0sCiAgICAgICAgICAgICAgICJ0aGVuIiA6IHsKICAgICAgICAgICAgICAgICAgInR5cGUiIDogIkFzIiwKICAgICAgICAgICAgICAgICAgIm9wZXJhbmQiIDogewogICAgICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiNzo5LTc6MTIiLAogICAgICAgICAgICAgICAgICAgICAidHlwZSIgOiAiTnVsbCIKICAgICAgICAgICAgICAgICAgfSwKICAgICAgICAgICAgICAgICAgImFzVHlwZVNwZWNpZmllciIgOiB7CiAgICAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJJbnRlcnZhbFR5cGVTcGVjaWZpZXIiLAogICAgICAgICAgICAgICAgICAgICAicG9pbnRUeXBlIiA6IHsKICAgICAgICAgICAgICAgICAgICAgICAgIm5hbWUiIDogInt1cm46aGw3LW9yZzplbG0tdHlwZXM6cjF9RGF0ZVRpbWUiLAogICAgICAgICAgICAgICAgICAgICAgICAidHlwZSIgOiAiTmFtZWRUeXBlU3BlY2lmaWVyIgogICAgICAgICAgICAgICAgICAgICB9CiAgICAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgICAgfSwKICAgICAgICAgICAgICAgImVsc2UiIDogewogICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiOTo5LTk6NTgiLAogICAgICAgICAgICAgICAgICAibG93Q2xvc2VkIiA6IHRydWUsCiAgICAgICAgICAgICAgICAgICJoaWdoQ2xvc2VkIiA6IHRydWUsCiAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJJbnRlcnZhbCIsCiAgICAgICAgICAgICAgICAgICJsb3ciIDogewogICAgICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiOToxOC05OjM3IiwKICAgICAgICAgICAgICAgICAgICAgInBhdGgiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgICAgICAgInR5cGUiIDogIlByb3BlcnR5IiwKICAgICAgICAgICAgICAgICAgICAgInNvdXJjZSIgOiB7CiAgICAgICAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICI5OjE4LTk6MzEiLAogICAgICAgICAgICAgICAgICAgICAgICAicGF0aCIgOiAic3RhcnQiLAogICAgICAgICAgICAgICAgICAgICAgICAidHlwZSIgOiAiUHJvcGVydHkiLAogICAgICAgICAgICAgICAgICAgICAgICAic291cmNlIiA6IHsKICAgICAgICAgICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjk6MTgtOToyMyIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJwZXJpb2QiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAidHlwZSIgOiAiT3BlcmFuZFJlZiIKICAgICAgICAgICAgICAgICAgICAgICAgfQogICAgICAgICAgICAgICAgICAgICB9CiAgICAgICAgICAgICAgICAgIH0sCiAgICAgICAgICAgICAgICAgICJoaWdoIiA6IHsKICAgICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjk6NDAtOTo1NyIsCiAgICAgICAgICAgICAgICAgICAgICJwYXRoIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJQcm9wZXJ0eSIsCiAgICAgICAgICAgICAgICAgICAgICJzb3VyY2UiIDogewogICAgICAgICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiOTo0MC05OjUxIiwKICAgICAgICAgICAgICAgICAgICAgICAgInBhdGgiIDogImVuZCIsCiAgICAgICAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJQcm9wZXJ0eSIsCiAgICAgICAgICAgICAgICAgICAgICAgICJzb3VyY2UiIDogewogICAgICAgICAgICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiOTo0MC05OjQ1IiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgIm5hbWUiIDogInBlcmlvZCIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJPcGVyYW5kUmVmIgogICAgICAgICAgICAgICAgICAgICAgICB9CiAgICAgICAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgICAgICAgfQogICAgICAgICAgICAgICB9CiAgICAgICAgICAgIH0sCiAgICAgICAgICAgICJvcGVyYW5kIiA6IFsgewogICAgICAgICAgICAgICAibmFtZSIgOiAicGVyaW9kIiwKICAgICAgICAgICAgICAgIm9wZXJhbmRUeXBlU3BlY2lmaWVyIiA6IHsKICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjU6MzUtNTo0NSIsCiAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJ7aHR0cDovL2hsNy5vcmcvZmhpcn1QZXJpb2QiLAogICAgICAgICAgICAgICAgICAidHlwZSIgOiAiTmFtZWRUeXBlU3BlY2lmaWVyIgogICAgICAgICAgICAgICB9CiAgICAgICAgICAgIH0gXQogICAgICAgICB9LCB7CiAgICAgICAgICAgICJsb2NhdG9yIiA6ICIxMToxLTE1OjgyIiwKICAgICAgICAgICAgIm5hbWUiIDogIlRvUXVhbnRpdHkiLAogICAgICAgICAgICAiY29udGV4dCIgOiAiUGF0aWVudCIsCiAgICAgICAgICAgICJhY2Nlc3NMZXZlbCIgOiAiUHVibGljIiwKICAgICAgICAgICAgInR5cGUiIDogIkZ1bmN0aW9uRGVmIiwKICAgICAgICAgICAgImV4cHJlc3Npb24iIDogewogICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMTI6NS0xNTo4MiIsCiAgICAgICAgICAgICAgICJ0eXBlIiA6ICJJZiIsCiAgICAgICAgICAgICAgICJjb25kaXRpb24iIDogewogICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMTI6OC0xMjoyMyIsCiAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJJc051bGwiLAogICAgICAgICAgICAgICAgICAib3BlcmFuZCIgOiB7CiAgICAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIxMjo4LTEyOjE1IiwKICAgICAgICAgICAgICAgICAgICAgIm5hbWUiIDogInF1YW50aXR5IiwKICAgICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk9wZXJhbmRSZWYiCiAgICAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgICAgfSwKICAgICAgICAgICAgICAgInRoZW4iIDogewogICAgICAgICAgICAgICAgICAiYXNUeXBlIiA6ICJ7dXJuOmhsNy1vcmc6ZWxtLXR5cGVzOnIxfVF1YW50aXR5IiwKICAgICAgICAgICAgICAgICAgInR5cGUiIDogIkFzIiwKICAgICAgICAgICAgICAgICAgIm9wZXJhbmQiIDogewogICAgICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMTM6OS0xMzoxMiIsCiAgICAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJOdWxsIgogICAgICAgICAgICAgICAgICB9CiAgICAgICAgICAgICAgIH0sCiAgICAgICAgICAgICAgICJlbHNlIiA6IHsKICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjE1OjktMTU6ODIiLAogICAgICAgICAgICAgICAgICAiY2xhc3NUeXBlIiA6ICJ7dXJuOmhsNy1vcmc6ZWxtLXR5cGVzOnIxfVF1YW50aXR5IiwKICAgICAgICAgICAgICAgICAgInR5cGUiIDogIkluc3RhbmNlIiwKICAgICAgICAgICAgICAgICAgImVsZW1lbnQiIDogWyB7CiAgICAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICAgICAgICJ2YWx1ZSIgOiB7CiAgICAgICAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIxNTozNC0xNTo1MyIsCiAgICAgICAgICAgICAgICAgICAgICAgICJwYXRoIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJQcm9wZXJ0eSIsCiAgICAgICAgICAgICAgICAgICAgICAgICJzb3VyY2UiIDogewogICAgICAgICAgICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMTU6MzQtMTU6NDciLAogICAgICAgICAgICAgICAgICAgICAgICAgICAicGF0aCIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAidHlwZSIgOiAiUHJvcGVydHkiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAic291cmNlIiA6IHsKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjE1OjM0LTE1OjQxIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIm5hbWUiIDogInF1YW50aXR5IiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk9wZXJhbmRSZWYiCiAgICAgICAgICAgICAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgICAgICAgICAgICAgfQogICAgICAgICAgICAgICAgICAgICB9CiAgICAgICAgICAgICAgICAgIH0sIHsKICAgICAgICAgICAgICAgICAgICAgIm5hbWUiIDogInVuaXQiLAogICAgICAgICAgICAgICAgICAgICAidmFsdWUiIDogewogICAgICAgICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMTU6NjItMTU6ODAiLAogICAgICAgICAgICAgICAgICAgICAgICAicGF0aCIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAgICAgICAgICAidHlwZSIgOiAiUHJvcGVydHkiLAogICAgICAgICAgICAgICAgICAgICAgICAic291cmNlIiA6IHsKICAgICAgICAgICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjE1OjYyLTE1Ojc0IiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgInBhdGgiIDogInVuaXQiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAidHlwZSIgOiAiUHJvcGVydHkiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAic291cmNlIiA6IHsKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjE1OjYyLTE1OjY5IiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIm5hbWUiIDogInF1YW50aXR5IiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk9wZXJhbmRSZWYiCiAgICAgICAgICAgICAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgICAgICAgICAgICAgfQogICAgICAgICAgICAgICAgICAgICB9CiAgICAgICAgICAgICAgICAgIH0gXQogICAgICAgICAgICAgICB9CiAgICAgICAgICAgIH0sCiAgICAgICAgICAgICJvcGVyYW5kIiA6IFsgewogICAgICAgICAgICAgICAibmFtZSIgOiAicXVhbnRpdHkiLAogICAgICAgICAgICAgICAib3BlcmFuZFR5cGVTcGVjaWZpZXIiIDogewogICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMTE6MzctMTE6NDkiLAogICAgICAgICAgICAgICAgICAibmFtZSIgOiAie2h0dHA6Ly9obDcub3JnL2ZoaXJ9UXVhbnRpdHkiLAogICAgICAgICAgICAgICAgICAidHlwZSIgOiAiTmFtZWRUeXBlU3BlY2lmaWVyIgogICAgICAgICAgICAgICB9CiAgICAgICAgICAgIH0gXQogICAgICAgICB9LCB7CiAgICAgICAgICAgICJsb2NhdG9yIiA6ICIxNzoxLTIxOjEwNyIsCiAgICAgICAgICAgICJuYW1lIiA6ICJUb1JhdGlvIiwKICAgICAgICAgICAgImNvbnRleHQiIDogIlBhdGllbnQiLAogICAgICAgICAgICAiYWNjZXNzTGV2ZWwiIDogIlB1YmxpYyIsCiAgICAgICAgICAgICJ0eXBlIiA6ICJGdW5jdGlvbkRlZiIsCiAgICAgICAgICAgICJleHByZXNzaW9uIiA6IHsKICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjE4OjUtMjE6MTA3IiwKICAgICAgICAgICAgICAgInR5cGUiIDogIklmIiwKICAgICAgICAgICAgICAgImNvbmRpdGlvbiIgOiB7CiAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIxODo4LTE4OjIwIiwKICAgICAgICAgICAgICAgICAgInR5cGUiIDogIklzTnVsbCIsCiAgICAgICAgICAgICAgICAgICJvcGVyYW5kIiA6IHsKICAgICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjE4OjgtMTg6MTIiLAogICAgICAgICAgICAgICAgICAgICAibmFtZSIgOiAicmF0aW8iLAogICAgICAgICAgICAgICAgICAgICAidHlwZSIgOiAiT3BlcmFuZFJlZiIKICAgICAgICAgICAgICAgICAgfQogICAgICAgICAgICAgICB9LAogICAgICAgICAgICAgICAidGhlbiIgOiB7CiAgICAgICAgICAgICAgICAgICJhc1R5cGUiIDogInt1cm46aGw3LW9yZzplbG0tdHlwZXM6cjF9UmF0aW8iLAogICAgICAgICAgICAgICAgICAidHlwZSIgOiAiQXMiLAogICAgICAgICAgICAgICAgICAib3BlcmFuZCIgOiB7CiAgICAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIxOTo5LTE5OjEyIiwKICAgICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk51bGwiCiAgICAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgICAgfSwKICAgICAgICAgICAgICAgImVsc2UiIDogewogICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMjE6OS0yMToxMDciLAogICAgICAgICAgICAgICAgICAiY2xhc3NUeXBlIiA6ICJ7dXJuOmhsNy1vcmc6ZWxtLXR5cGVzOnIxfVJhdGlvIiwKICAgICAgICAgICAgICAgICAgInR5cGUiIDogIkluc3RhbmNlIiwKICAgICAgICAgICAgICAgICAgImVsZW1lbnQiIDogWyB7CiAgICAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJudW1lcmF0b3IiLAogICAgICAgICAgICAgICAgICAgICAidmFsdWUiIDogewogICAgICAgICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMjE6MzUtMjE6NjEiLAogICAgICAgICAgICAgICAgICAgICAgICAibmFtZSIgOiAiVG9RdWFudGl0eSIsCiAgICAgICAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJGdW5jdGlvblJlZiIsCiAgICAgICAgICAgICAgICAgICAgICAgICJvcGVyYW5kIiA6IFsgewogICAgICAgICAgICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMjE6NDYtMjE6NjAiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAicGF0aCIgOiAibnVtZXJhdG9yIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgInR5cGUiIDogIlByb3BlcnR5IiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgInNvdXJjZSIgOiB7CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIyMTo0Ni0yMTo1MCIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJyYXRpbyIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJPcGVyYW5kUmVmIgogICAgICAgICAgICAgICAgICAgICAgICAgICB9CiAgICAgICAgICAgICAgICAgICAgICAgIH0gXQogICAgICAgICAgICAgICAgICAgICB9CiAgICAgICAgICAgICAgICAgIH0sIHsKICAgICAgICAgICAgICAgICAgICAgIm5hbWUiIDogImRlbm9taW5hdG9yIiwKICAgICAgICAgICAgICAgICAgICAgInZhbHVlIiA6IHsKICAgICAgICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjIxOjc3LTIxOjEwNSIsCiAgICAgICAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJUb1F1YW50aXR5IiwKICAgICAgICAgICAgICAgICAgICAgICAgInR5cGUiIDogIkZ1bmN0aW9uUmVmIiwKICAgICAgICAgICAgICAgICAgICAgICAgIm9wZXJhbmQiIDogWyB7CiAgICAgICAgICAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIyMTo4OC0yMToxMDQiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAicGF0aCIgOiAiZGVub21pbmF0b3IiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAidHlwZSIgOiAiUHJvcGVydHkiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAic291cmNlIiA6IHsKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjIxOjg4LTIxOjkyIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIm5hbWUiIDogInJhdGlvIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk9wZXJhbmRSZWYiCiAgICAgICAgICAgICAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgICAgICAgICAgICAgfSBdCiAgICAgICAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgICAgICAgfSBdCiAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgfSwKICAgICAgICAgICAgIm9wZXJhbmQiIDogWyB7CiAgICAgICAgICAgICAgICJuYW1lIiA6ICJyYXRpbyIsCiAgICAgICAgICAgICAgICJvcGVyYW5kVHlwZVNwZWNpZmllciIgOiB7CiAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIxNzozMS0xNzo0MCIsCiAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJ7aHR0cDovL2hsNy5vcmcvZmhpcn1SYXRpbyIsCiAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJOYW1lZFR5cGVTcGVjaWZpZXIiCiAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgfSBdCiAgICAgICAgIH0sIHsKICAgICAgICAgICAgImxvY2F0b3IiIDogIjIzOjEtMjc6NjMiLAogICAgICAgICAgICAibmFtZSIgOiAiVG9JbnRlcnZhbCIsCiAgICAgICAgICAgICJjb250ZXh0IiA6ICJQYXRpZW50IiwKICAgICAgICAgICAgImFjY2Vzc0xldmVsIiA6ICJQdWJsaWMiLAogICAgICAgICAgICAidHlwZSIgOiAiRnVuY3Rpb25EZWYiLAogICAgICAgICAgICAiZXhwcmVzc2lvbiIgOiB7CiAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIyNDo1LTI3OjYzIiwKICAgICAgICAgICAgICAgInR5cGUiIDogIklmIiwKICAgICAgICAgICAgICAgImNvbmRpdGlvbiIgOiB7CiAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIyNDo4LTI0OjIwIiwKICAgICAgICAgICAgICAgICAgInR5cGUiIDogIklzTnVsbCIsCiAgICAgICAgICAgICAgICAgICJvcGVyYW5kIiA6IHsKICAgICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjI0OjgtMjQ6MTIiLAogICAgICAgICAgICAgICAgICAgICAibmFtZSIgOiAicmFuZ2UiLAogICAgICAgICAgICAgICAgICAgICAidHlwZSIgOiAiT3BlcmFuZFJlZiIKICAgICAgICAgICAgICAgICAgfQogICAgICAgICAgICAgICB9LAogICAgICAgICAgICAgICAidGhlbiIgOiB7CiAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJBcyIsCiAgICAgICAgICAgICAgICAgICJvcGVyYW5kIiA6IHsKICAgICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjI1OjktMjU6MTIiLAogICAgICAgICAgICAgICAgICAgICAidHlwZSIgOiAiTnVsbCIKICAgICAgICAgICAgICAgICAgfSwKICAgICAgICAgICAgICAgICAgImFzVHlwZVNwZWNpZmllciIgOiB7CiAgICAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJJbnRlcnZhbFR5cGVTcGVjaWZpZXIiLAogICAgICAgICAgICAgICAgICAgICAicG9pbnRUeXBlIiA6IHsKICAgICAgICAgICAgICAgICAgICAgICAgIm5hbWUiIDogInt1cm46aGw3LW9yZzplbG0tdHlwZXM6cjF9UXVhbnRpdHkiLAogICAgICAgICAgICAgICAgICAgICAgICAidHlwZSIgOiAiTmFtZWRUeXBlU3BlY2lmaWVyIgogICAgICAgICAgICAgICAgICAgICB9CiAgICAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgICAgfSwKICAgICAgICAgICAgICAgImVsc2UiIDogewogICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMjc6OS0yNzo2MyIsCiAgICAgICAgICAgICAgICAgICJsb3dDbG9zZWQiIDogdHJ1ZSwKICAgICAgICAgICAgICAgICAgImhpZ2hDbG9zZWQiIDogdHJ1ZSwKICAgICAgICAgICAgICAgICAgInR5cGUiIDogIkludGVydmFsIiwKICAgICAgICAgICAgICAgICAgImxvdyIgOiB7CiAgICAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIyNzoxOC0yNzozOCIsCiAgICAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJUb1F1YW50aXR5IiwKICAgICAgICAgICAgICAgICAgICAgInR5cGUiIDogIkZ1bmN0aW9uUmVmIiwKICAgICAgICAgICAgICAgICAgICAgIm9wZXJhbmQiIDogWyB7CiAgICAgICAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIyNzoyOS0yNzozNyIsCiAgICAgICAgICAgICAgICAgICAgICAgICJwYXRoIiA6ICJsb3ciLAogICAgICAgICAgICAgICAgICAgICAgICAidHlwZSIgOiAiUHJvcGVydHkiLAogICAgICAgICAgICAgICAgICAgICAgICAic291cmNlIiA6IHsKICAgICAgICAgICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjI3OjI5LTI3OjMzIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgIm5hbWUiIDogInJhbmdlIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk9wZXJhbmRSZWYiCiAgICAgICAgICAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgICAgICAgICAgfSBdCiAgICAgICAgICAgICAgICAgIH0sCiAgICAgICAgICAgICAgICAgICJoaWdoIiA6IHsKICAgICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjI3OjQxLTI3OjYyIiwKICAgICAgICAgICAgICAgICAgICAgIm5hbWUiIDogIlRvUXVhbnRpdHkiLAogICAgICAgICAgICAgICAgICAgICAidHlwZSIgOiAiRnVuY3Rpb25SZWYiLAogICAgICAgICAgICAgICAgICAgICAib3BlcmFuZCIgOiBbIHsKICAgICAgICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjI3OjUyLTI3OjYxIiwKICAgICAgICAgICAgICAgICAgICAgICAgInBhdGgiIDogImhpZ2giLAogICAgICAgICAgICAgICAgICAgICAgICAidHlwZSIgOiAiUHJvcGVydHkiLAogICAgICAgICAgICAgICAgICAgICAgICAic291cmNlIiA6IHsKICAgICAgICAgICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjI3OjUyLTI3OjU2IiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgIm5hbWUiIDogInJhbmdlIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk9wZXJhbmRSZWYiCiAgICAgICAgICAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgICAgICAgICAgfSBdCiAgICAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgICAgfQogICAgICAgICAgICB9LAogICAgICAgICAgICAib3BlcmFuZCIgOiBbIHsKICAgICAgICAgICAgICAgIm5hbWUiIDogInJhbmdlIiwKICAgICAgICAgICAgICAgIm9wZXJhbmRUeXBlU3BlY2lmaWVyIiA6IHsKICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjIzOjM0LTIzOjQzIiwKICAgICAgICAgICAgICAgICAgIm5hbWUiIDogIntodHRwOi8vaGw3Lm9yZy9maGlyfVJhbmdlIiwKICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk5hbWVkVHlwZVNwZWNpZmllciIKICAgICAgICAgICAgICAgfQogICAgICAgICAgICB9IF0KICAgICAgICAgfSwgewogICAgICAgICAgICAibG9jYXRvciIgOiAiMjk6MS0zODo5IiwKICAgICAgICAgICAgIm5hbWUiIDogIlRvQ29kZSIsCiAgICAgICAgICAgICJjb250ZXh0IiA6ICJQYXRpZW50IiwKICAgICAgICAgICAgImFjY2Vzc0xldmVsIiA6ICJQdWJsaWMiLAogICAgICAgICAgICAidHlwZSIgOiAiRnVuY3Rpb25EZWYiLAogICAgICAgICAgICAiZXhwcmVzc2lvbiIgOiB7CiAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIzMDo1LTM4OjkiLAogICAgICAgICAgICAgICAidHlwZSIgOiAiSWYiLAogICAgICAgICAgICAgICAiY29uZGl0aW9uIiA6IHsKICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjMwOjgtMzA6MjEiLAogICAgICAgICAgICAgICAgICAidHlwZSIgOiAiSXNOdWxsIiwKICAgICAgICAgICAgICAgICAgIm9wZXJhbmQiIDogewogICAgICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMzA6OC0zMDoxMyIsCiAgICAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJjb2RpbmciLAogICAgICAgICAgICAgICAgICAgICAidHlwZSIgOiAiT3BlcmFuZFJlZiIKICAgICAgICAgICAgICAgICAgfQogICAgICAgICAgICAgICB9LAogICAgICAgICAgICAgICAidGhlbiIgOiB7CiAgICAgICAgICAgICAgICAgICJhc1R5cGUiIDogInt1cm46aGw3LW9yZzplbG0tdHlwZXM6cjF9Q29kZSIsCiAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJBcyIsCiAgICAgICAgICAgICAgICAgICJvcGVyYW5kIiA6IHsKICAgICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjMxOjktMzE6MTIiLAogICAgICAgICAgICAgICAgICAgICAidHlwZSIgOiAiTnVsbCIKICAgICAgICAgICAgICAgICAgfQogICAgICAgICAgICAgICB9LAogICAgICAgICAgICAgICAiZWxzZSIgOiB7CiAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIzMzo5LTM4OjkiLAogICAgICAgICAgICAgICAgICAiY2xhc3NUeXBlIiA6ICJ7dXJuOmhsNy1vcmc6ZWxtLXR5cGVzOnIxfUNvZGUiLAogICAgICAgICAgICAgICAgICAidHlwZSIgOiAiSW5zdGFuY2UiLAogICAgICAgICAgICAgICAgICAiZWxlbWVudCIgOiBbIHsKICAgICAgICAgICAgICAgICAgICAgIm5hbWUiIDogImNvZGUiLAogICAgICAgICAgICAgICAgICAgICAidmFsdWUiIDogewogICAgICAgICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMzQ6MTctMzQ6MzMiLAogICAgICAgICAgICAgICAgICAgICAgICAicGF0aCIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAgICAgICAgICAidHlwZSIgOiAiUHJvcGVydHkiLAogICAgICAgICAgICAgICAgICAgICAgICAic291cmNlIiA6IHsKICAgICAgICAgICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjM0OjE3LTM0OjI3IiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgInBhdGgiIDogImNvZGUiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAidHlwZSIgOiAiUHJvcGVydHkiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAic291cmNlIiA6IHsKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjM0OjE3LTM0OjIyIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIm5hbWUiIDogImNvZGluZyIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJPcGVyYW5kUmVmIgogICAgICAgICAgICAgICAgICAgICAgICAgICB9CiAgICAgICAgICAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgICAgICAgICAgfQogICAgICAgICAgICAgICAgICB9LCB7CiAgICAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJzeXN0ZW0iLAogICAgICAgICAgICAgICAgICAgICAidmFsdWUiIDogewogICAgICAgICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMzU6MTktMzU6MzciLAogICAgICAgICAgICAgICAgICAgICAgICAicGF0aCIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAgICAgICAgICAidHlwZSIgOiAiUHJvcGVydHkiLAogICAgICAgICAgICAgICAgICAgICAgICAic291cmNlIiA6IHsKICAgICAgICAgICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjM1OjE5LTM1OjMxIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgInBhdGgiIDogInN5c3RlbSIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJQcm9wZXJ0eSIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICJzb3VyY2UiIDogewogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMzU6MTktMzU6MjQiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAibmFtZSIgOiAiY29kaW5nIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk9wZXJhbmRSZWYiCiAgICAgICAgICAgICAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgICAgICAgICAgICAgfQogICAgICAgICAgICAgICAgICAgICB9CiAgICAgICAgICAgICAgICAgIH0sIHsKICAgICAgICAgICAgICAgICAgICAgIm5hbWUiIDogInZlcnNpb24iLAogICAgICAgICAgICAgICAgICAgICAidmFsdWUiIDogewogICAgICAgICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMzY6MjAtMzY6MzkiLAogICAgICAgICAgICAgICAgICAgICAgICAicGF0aCIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAgICAgICAgICAidHlwZSIgOiAiUHJvcGVydHkiLAogICAgICAgICAgICAgICAgICAgICAgICAic291cmNlIiA6IHsKICAgICAgICAgICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjM2OjIwLTM2OjMzIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgInBhdGgiIDogInZlcnNpb24iLAogICAgICAgICAgICAgICAgICAgICAgICAgICAidHlwZSIgOiAiUHJvcGVydHkiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAic291cmNlIiA6IHsKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjM2OjIwLTM2OjI1IiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIm5hbWUiIDogImNvZGluZyIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJPcGVyYW5kUmVmIgogICAgICAgICAgICAgICAgICAgICAgICAgICB9CiAgICAgICAgICAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgICAgICAgICAgfQogICAgICAgICAgICAgICAgICB9LCB7CiAgICAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJkaXNwbGF5IiwKICAgICAgICAgICAgICAgICAgICAgInZhbHVlIiA6IHsKICAgICAgICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjM3OjIwLTM3OjM5IiwKICAgICAgICAgICAgICAgICAgICAgICAgInBhdGgiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgICAgICAgICAgInR5cGUiIDogIlByb3BlcnR5IiwKICAgICAgICAgICAgICAgICAgICAgICAgInNvdXJjZSIgOiB7CiAgICAgICAgICAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIzNzoyMC0zNzozMyIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICJwYXRoIiA6ICJkaXNwbGF5IiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgInR5cGUiIDogIlByb3BlcnR5IiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgInNvdXJjZSIgOiB7CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIzNzoyMC0zNzoyNSIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJjb2RpbmciLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAidHlwZSIgOiAiT3BlcmFuZFJlZiIKICAgICAgICAgICAgICAgICAgICAgICAgICAgfQogICAgICAgICAgICAgICAgICAgICAgICB9CiAgICAgICAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgICAgICAgfSBdCiAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgfSwKICAgICAgICAgICAgIm9wZXJhbmQiIDogWyB7CiAgICAgICAgICAgICAgICJuYW1lIiA6ICJjb2RpbmciLAogICAgICAgICAgICAgICAib3BlcmFuZFR5cGVTcGVjaWZpZXIiIDogewogICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMjk6MzEtMjk6NDEiLAogICAgICAgICAgICAgICAgICAibmFtZSIgOiAie2h0dHA6Ly9obDcub3JnL2ZoaXJ9Q29kaW5nIiwKICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk5hbWVkVHlwZVNwZWNpZmllciIKICAgICAgICAgICAgICAgfQogICAgICAgICAgICB9IF0KICAgICAgICAgfSwgewogICAgICAgICAgICAibG9jYXRvciIgOiAiNDA6MS00Nzo5IiwKICAgICAgICAgICAgIm5hbWUiIDogIlRvQ29uY2VwdCIsCiAgICAgICAgICAgICJjb250ZXh0IiA6ICJQYXRpZW50IiwKICAgICAgICAgICAgImFjY2Vzc0xldmVsIiA6ICJQdWJsaWMiLAogICAgICAgICAgICAidHlwZSIgOiAiRnVuY3Rpb25EZWYiLAogICAgICAgICAgICAiZXhwcmVzc2lvbiIgOiB7CiAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICI0MTo1LTQ3OjkiLAogICAgICAgICAgICAgICAidHlwZSIgOiAiSWYiLAogICAgICAgICAgICAgICAiY29uZGl0aW9uIiA6IHsKICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjQxOjgtNDE6MjIiLAogICAgICAgICAgICAgICAgICAidHlwZSIgOiAiSXNOdWxsIiwKICAgICAgICAgICAgICAgICAgIm9wZXJhbmQiIDogewogICAgICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiNDE6OC00MToxNCIsCiAgICAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJjb25jZXB0IiwKICAgICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk9wZXJhbmRSZWYiCiAgICAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgICAgfSwKICAgICAgICAgICAgICAgInRoZW4iIDogewogICAgICAgICAgICAgICAgICAiYXNUeXBlIiA6ICJ7dXJuOmhsNy1vcmc6ZWxtLXR5cGVzOnIxfUNvbmNlcHQiLAogICAgICAgICAgICAgICAgICAidHlwZSIgOiAiQXMiLAogICAgICAgICAgICAgICAgICAib3BlcmFuZCIgOiB7CiAgICAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICI0Mjo5LTQyOjEyIiwKICAgICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk51bGwiCiAgICAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgICAgfSwKICAgICAgICAgICAgICAgImVsc2UiIDogewogICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiNDQ6OS00Nzo5IiwKICAgICAgICAgICAgICAgICAgImNsYXNzVHlwZSIgOiAie3VybjpobDctb3JnOmVsbS10eXBlczpyMX1Db25jZXB0IiwKICAgICAgICAgICAgICAgICAgInR5cGUiIDogIkluc3RhbmNlIiwKICAgICAgICAgICAgICAgICAgImVsZW1lbnQiIDogWyB7CiAgICAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJjb2RlcyIsCiAgICAgICAgICAgICAgICAgICAgICJ2YWx1ZSIgOiB7CiAgICAgICAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICI0NToyMC00NTo1MiIsCiAgICAgICAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJRdWVyeSIsCiAgICAgICAgICAgICAgICAgICAgICAgICJzb3VyY2UiIDogWyB7CiAgICAgICAgICAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICI0NToyMC00NTozNSIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICJhbGlhcyIgOiAiQyIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICJleHByZXNzaW9uIiA6IHsKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjQ1OjIwLTQ1OjMzIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInBhdGgiIDogImNvZGluZyIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJQcm9wZXJ0eSIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJzb3VyY2UiIDogewogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAibmFtZSIgOiAiY29uY2VwdCIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJPcGVyYW5kUmVmIgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICB9CiAgICAgICAgICAgICAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgICAgICAgICAgICAgfSBdLAogICAgICAgICAgICAgICAgICAgICAgICAicmVsYXRpb25zaGlwIiA6IFsgXSwKICAgICAgICAgICAgICAgICAgICAgICAgInJldHVybiIgOiB7CiAgICAgICAgICAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICI0NTozNy00NTo1MiIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICJleHByZXNzaW9uIiA6IHsKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjQ1OjQ0LTQ1OjUyIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIm5hbWUiIDogIlRvQ29kZSIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJGdW5jdGlvblJlZiIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJvcGVyYW5kIiA6IFsgewogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiNDU6NTEiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAibmFtZSIgOiAiQyIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJBbGlhc1JlZiIKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfSBdCiAgICAgICAgICAgICAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgICAgICAgICAgICAgfQogICAgICAgICAgICAgICAgICAgICB9CiAgICAgICAgICAgICAgICAgIH0sIHsKICAgICAgICAgICAgICAgICAgICAgIm5hbWUiIDogImRpc3BsYXkiLAogICAgICAgICAgICAgICAgICAgICAidmFsdWUiIDogewogICAgICAgICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiNDY6MjItNDY6MzkiLAogICAgICAgICAgICAgICAgICAgICAgICAicGF0aCIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAgICAgICAgICAidHlwZSIgOiAiUHJvcGVydHkiLAogICAgICAgICAgICAgICAgICAgICAgICAic291cmNlIiA6IHsKICAgICAgICAgICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjQ2OjIyLTQ2OjMzIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgInBhdGgiIDogInRleHQiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAidHlwZSIgOiAiUHJvcGVydHkiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAic291cmNlIiA6IHsKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjQ2OjIyLTQ2OjI4IiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIm5hbWUiIDogImNvbmNlcHQiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAidHlwZSIgOiAiT3BlcmFuZFJlZiIKICAgICAgICAgICAgICAgICAgICAgICAgICAgfQogICAgICAgICAgICAgICAgICAgICAgICB9CiAgICAgICAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgICAgICAgfSBdCiAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgfSwKICAgICAgICAgICAgIm9wZXJhbmQiIDogWyB7CiAgICAgICAgICAgICAgICJuYW1lIiA6ICJjb25jZXB0IiwKICAgICAgICAgICAgICAgIm9wZXJhbmRUeXBlU3BlY2lmaWVyIiA6IHsKICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjQwOjM1LTQwOjU0IiwKICAgICAgICAgICAgICAgICAgIm5hbWUiIDogIntodHRwOi8vaGw3Lm9yZy9maGlyfUNvZGVhYmxlQ29uY2VwdCIsCiAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJOYW1lZFR5cGVTcGVjaWZpZXIiCiAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgfSBdCiAgICAgICAgIH0sIHsKICAgICAgICAgICAgImxvY2F0b3IiIDogIjUwOjEtNTA6NTgiLAogICAgICAgICAgICAibmFtZSIgOiAiVG9TdHJpbmciLAogICAgICAgICAgICAiY29udGV4dCIgOiAiUGF0aWVudCIsCiAgICAgICAgICAgICJhY2Nlc3NMZXZlbCIgOiAiUHVibGljIiwKICAgICAgICAgICAgInR5cGUiIDogIkZ1bmN0aW9uRGVmIiwKICAgICAgICAgICAgImV4cHJlc3Npb24iIDogewogICAgICAgICAgICAgICAibG9jYXRvciIgOiAiNTA6NDgtNTA6NTgiLAogICAgICAgICAgICAgICAicGF0aCIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAidHlwZSIgOiAiUHJvcGVydHkiLAogICAgICAgICAgICAgICAic291cmNlIiA6IHsKICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjUwOjQ4LTUwOjUyIiwKICAgICAgICAgICAgICAgICAgIm5hbWUiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk9wZXJhbmRSZWYiCiAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgfSwKICAgICAgICAgICAgIm9wZXJhbmQiIDogWyB7CiAgICAgICAgICAgICAgICJuYW1lIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICJvcGVyYW5kVHlwZVNwZWNpZmllciIgOiB7CiAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICI1MDozMi01MDo0NCIsCiAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJ7aHR0cDovL2hsNy5vcmcvZmhpcn1BY2NvdW50U3RhdHVzIiwKICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk5hbWVkVHlwZVNwZWNpZmllciIKICAgICAgICAgICAgICAgfQogICAgICAgICAgICB9IF0KICAgICAgICAgfSwgewogICAgICAgICAgICAibG9jYXRvciIgOiAiNTE6MS01MTo3MCIsCiAgICAgICAgICAgICJuYW1lIiA6ICJUb1N0cmluZyIsCiAgICAgICAgICAgICJjb250ZXh0IiA6ICJQYXRpZW50IiwKICAgICAgICAgICAgImFjY2Vzc0xldmVsIiA6ICJQdWJsaWMiLAogICAgICAgICAgICAidHlwZSIgOiAiRnVuY3Rpb25EZWYiLAogICAgICAgICAgICAiZXhwcmVzc2lvbiIgOiB7CiAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICI1MTo2MC01MTo3MCIsCiAgICAgICAgICAgICAgICJwYXRoIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICJ0eXBlIiA6ICJQcm9wZXJ0eSIsCiAgICAgICAgICAgICAgICJzb3VyY2UiIDogewogICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiNTE6NjAtNTE6NjQiLAogICAgICAgICAgICAgICAgICAibmFtZSIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAgICAidHlwZSIgOiAiT3BlcmFuZFJlZiIKICAgICAgICAgICAgICAgfQogICAgICAgICAgICB9LAogICAgICAgICAgICAib3BlcmFuZCIgOiBbIHsKICAgICAgICAgICAgICAgIm5hbWUiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgIm9wZXJhbmRUeXBlU3BlY2lmaWVyIiA6IHsKICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjUxOjMyLTUxOjU2IiwKICAgICAgICAgICAgICAgICAgIm5hbWUiIDogIntodHRwOi8vaGw3Lm9yZy9maGlyfUFjdGlvbkNhcmRpbmFsaXR5QmVoYXZpb3IiLAogICAgICAgICAgICAgICAgICAidHlwZSIgOiAiTmFtZWRUeXBlU3BlY2lmaWVyIgogICAgICAgICAgICAgICB9CiAgICAgICAgICAgIH0gXQogICAgICAgICB9LCB7CiAgICAgICAgICAgICJsb2NhdG9yIiA6ICI1MjoxLTUyOjY0IiwKICAgICAgICAgICAgIm5hbWUiIDogIlRvU3RyaW5nIiwKICAgICAgICAgICAgImNvbnRleHQiIDogIlBhdGllbnQiLAogICAgICAgICAgICAiYWNjZXNzTGV2ZWwiIDogIlB1YmxpYyIsCiAgICAgICAgICAgICJ0eXBlIiA6ICJGdW5jdGlvbkRlZiIsCiAgICAgICAgICAgICJleHByZXNzaW9uIiA6IHsKICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjUyOjU0LTUyOjY0IiwKICAgICAgICAgICAgICAgInBhdGgiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgInR5cGUiIDogIlByb3BlcnR5IiwKICAgICAgICAgICAgICAgInNvdXJjZSIgOiB7CiAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICI1Mjo1NC01Mjo1OCIsCiAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJPcGVyYW5kUmVmIgogICAgICAgICAgICAgICB9CiAgICAgICAgICAgIH0sCiAgICAgICAgICAgICJvcGVyYW5kIiA6IFsgewogICAgICAgICAgICAgICAibmFtZSIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAib3BlcmFuZFR5cGVTcGVjaWZpZXIiIDogewogICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiNTI6MzItNTI6NTAiLAogICAgICAgICAgICAgICAgICAibmFtZSIgOiAie2h0dHA6Ly9obDcub3JnL2ZoaXJ9QWN0aW9uQ29uZGl0aW9uS2luZCIsCiAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJOYW1lZFR5cGVTcGVjaWZpZXIiCiAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgfSBdCiAgICAgICAgIH0sIHsKICAgICAgICAgICAgImxvY2F0b3IiIDogIjUzOjEtNTM6NjciLAogICAgICAgICAgICAibmFtZSIgOiAiVG9TdHJpbmciLAogICAgICAgICAgICAiY29udGV4dCIgOiAiUGF0aWVudCIsCiAgICAgICAgICAgICJhY2Nlc3NMZXZlbCIgOiAiUHVibGljIiwKICAgICAgICAgICAgInR5cGUiIDogIkZ1bmN0aW9uRGVmIiwKICAgICAgICAgICAgImV4cHJlc3Npb24iIDogewogICAgICAgICAgICAgICAibG9jYXRvciIgOiAiNTM6NTctNTM6NjciLAogICAgICAgICAgICAgICAicGF0aCIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAidHlwZSIgOiAiUHJvcGVydHkiLAogICAgICAgICAgICAgICAic291cmNlIiA6IHsKICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjUzOjU3LTUzOjYxIiwKICAgICAgICAgICAgICAgICAgIm5hbWUiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk9wZXJhbmRSZWYiCiAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgfSwKICAgICAgICAgICAgIm9wZXJhbmQiIDogWyB7CiAgICAgICAgICAgICAgICJuYW1lIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICJvcGVyYW5kVHlwZVNwZWNpZmllciIgOiB7CiAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICI1MzozMi01Mzo1MyIsCiAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJ7aHR0cDovL2hsNy5vcmcvZmhpcn1BY3Rpb25Hcm91cGluZ0JlaGF2aW9yIiwKICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk5hbWVkVHlwZVNwZWNpZmllciIKICAgICAgICAgICAgICAgfQogICAgICAgICAgICB9IF0KICAgICAgICAgfSwgewogICAgICAgICAgICAibG9jYXRvciIgOiAiNTQ6MS01NDo2NiIsCiAgICAgICAgICAgICJuYW1lIiA6ICJUb1N0cmluZyIsCiAgICAgICAgICAgICJjb250ZXh0IiA6ICJQYXRpZW50IiwKICAgICAgICAgICAgImFjY2Vzc0xldmVsIiA6ICJQdWJsaWMiLAogICAgICAgICAgICAidHlwZSIgOiAiRnVuY3Rpb25EZWYiLAogICAgICAgICAgICAiZXhwcmVzc2lvbiIgOiB7CiAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICI1NDo1Ni01NDo2NiIsCiAgICAgICAgICAgICAgICJwYXRoIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICJ0eXBlIiA6ICJQcm9wZXJ0eSIsCiAgICAgICAgICAgICAgICJzb3VyY2UiIDogewogICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiNTQ6NTYtNTQ6NjAiLAogICAgICAgICAgICAgICAgICAibmFtZSIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAgICAidHlwZSIgOiAiT3BlcmFuZFJlZiIKICAgICAgICAgICAgICAgfQogICAgICAgICAgICB9LAogICAgICAgICAgICAib3BlcmFuZCIgOiBbIHsKICAgICAgICAgICAgICAgIm5hbWUiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgIm9wZXJhbmRUeXBlU3BlY2lmaWVyIiA6IHsKICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjU0OjMyLTU0OjUyIiwKICAgICAgICAgICAgICAgICAgIm5hbWUiIDogIntodHRwOi8vaGw3Lm9yZy9maGlyfUFjdGlvblBhcnRpY2lwYW50VHlwZSIsCiAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJOYW1lZFR5cGVTcGVjaWZpZXIiCiAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgfSBdCiAgICAgICAgIH0sIHsKICAgICAgICAgICAgImxvY2F0b3IiIDogIjU1OjEtNTU6NjciLAogICAgICAgICAgICAibmFtZSIgOiAiVG9TdHJpbmciLAogICAgICAgICAgICAiY29udGV4dCIgOiAiUGF0aWVudCIsCiAgICAgICAgICAgICJhY2Nlc3NMZXZlbCIgOiAiUHVibGljIiwKICAgICAgICAgICAgInR5cGUiIDogIkZ1bmN0aW9uRGVmIiwKICAgICAgICAgICAgImV4cHJlc3Npb24iIDogewogICAgICAgICAgICAgICAibG9jYXRvciIgOiAiNTU6NTctNTU6NjciLAogICAgICAgICAgICAgICAicGF0aCIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAidHlwZSIgOiAiUHJvcGVydHkiLAogICAgICAgICAgICAgICAic291cmNlIiA6IHsKICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjU1OjU3LTU1OjYxIiwKICAgICAgICAgICAgICAgICAgIm5hbWUiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk9wZXJhbmRSZWYiCiAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgfSwKICAgICAgICAgICAgIm9wZXJhbmQiIDogWyB7CiAgICAgICAgICAgICAgICJuYW1lIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICJvcGVyYW5kVHlwZVNwZWNpZmllciIgOiB7CiAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICI1NTozMi01NTo1MyIsCiAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJ7aHR0cDovL2hsNy5vcmcvZmhpcn1BY3Rpb25QcmVjaGVja0JlaGF2aW9yIiwKICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk5hbWVkVHlwZVNwZWNpZmllciIKICAgICAgICAgICAgICAgfQogICAgICAgICAgICB9IF0KICAgICAgICAgfSwgewogICAgICAgICAgICAibG9jYXRvciIgOiAiNTY6MS01Njo2NyIsCiAgICAgICAgICAgICJuYW1lIiA6ICJUb1N0cmluZyIsCiAgICAgICAgICAgICJjb250ZXh0IiA6ICJQYXRpZW50IiwKICAgICAgICAgICAgImFjY2Vzc0xldmVsIiA6ICJQdWJsaWMiLAogICAgICAgICAgICAidHlwZSIgOiAiRnVuY3Rpb25EZWYiLAogICAgICAgICAgICAiZXhwcmVzc2lvbiIgOiB7CiAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICI1Njo1Ny01Njo2NyIsCiAgICAgICAgICAgICAgICJwYXRoIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICJ0eXBlIiA6ICJQcm9wZXJ0eSIsCiAgICAgICAgICAgICAgICJzb3VyY2UiIDogewogICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiNTY6NTctNTY6NjEiLAogICAgICAgICAgICAgICAgICAibmFtZSIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAgICAidHlwZSIgOiAiT3BlcmFuZFJlZiIKICAgICAgICAgICAgICAgfQogICAgICAgICAgICB9LAogICAgICAgICAgICAib3BlcmFuZCIgOiBbIHsKICAgICAgICAgICAgICAgIm5hbWUiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgIm9wZXJhbmRUeXBlU3BlY2lmaWVyIiA6IHsKICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjU2OjMyLTU2OjUzIiwKICAgICAgICAgICAgICAgICAgIm5hbWUiIDogIntodHRwOi8vaGw3Lm9yZy9maGlyfUFjdGlvblJlbGF0aW9uc2hpcFR5cGUiLAogICAgICAgICAgICAgICAgICAidHlwZSIgOiAiTmFtZWRUeXBlU3BlY2lmaWVyIgogICAgICAgICAgICAgICB9CiAgICAgICAgICAgIH0gXQogICAgICAgICB9LCB7CiAgICAgICAgICAgICJsb2NhdG9yIiA6ICI1NzoxLTU3OjY3IiwKICAgICAgICAgICAgIm5hbWUiIDogIlRvU3RyaW5nIiwKICAgICAgICAgICAgImNvbnRleHQiIDogIlBhdGllbnQiLAogICAgICAgICAgICAiYWNjZXNzTGV2ZWwiIDogIlB1YmxpYyIsCiAgICAgICAgICAgICJ0eXBlIiA6ICJGdW5jdGlvbkRlZiIsCiAgICAgICAgICAgICJleHByZXNzaW9uIiA6IHsKICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjU3OjU3LTU3OjY3IiwKICAgICAgICAgICAgICAgInBhdGgiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgInR5cGUiIDogIlByb3BlcnR5IiwKICAgICAgICAgICAgICAgInNvdXJjZSIgOiB7CiAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICI1Nzo1Ny01Nzo2MSIsCiAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJPcGVyYW5kUmVmIgogICAgICAgICAgICAgICB9CiAgICAgICAgICAgIH0sCiAgICAgICAgICAgICJvcGVyYW5kIiA6IFsgewogICAgICAgICAgICAgICAibmFtZSIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAib3BlcmFuZFR5cGVTcGVjaWZpZXIiIDogewogICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiNTc6MzItNTc6NTMiLAogICAgICAgICAgICAgICAgICAibmFtZSIgOiAie2h0dHA6Ly9obDcub3JnL2ZoaXJ9QWN0aW9uUmVxdWlyZWRCZWhhdmlvciIsCiAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJOYW1lZFR5cGVTcGVjaWZpZXIiCiAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgfSBdCiAgICAgICAgIH0sIHsKICAgICAgICAgICAgImxvY2F0b3IiIDogIjU4OjEtNTg6NjgiLAogICAgICAgICAgICAibmFtZSIgOiAiVG9TdHJpbmciLAogICAgICAgICAgICAiY29udGV4dCIgOiAiUGF0aWVudCIsCiAgICAgICAgICAgICJhY2Nlc3NMZXZlbCIgOiAiUHVibGljIiwKICAgICAgICAgICAgInR5cGUiIDogIkZ1bmN0aW9uRGVmIiwKICAgICAgICAgICAgImV4cHJlc3Npb24iIDogewogICAgICAgICAgICAgICAibG9jYXRvciIgOiAiNTg6NTgtNTg6NjgiLAogICAgICAgICAgICAgICAicGF0aCIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAidHlwZSIgOiAiUHJvcGVydHkiLAogICAgICAgICAgICAgICAic291cmNlIiA6IHsKICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjU4OjU4LTU4OjYyIiwKICAgICAgICAgICAgICAgICAgIm5hbWUiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk9wZXJhbmRSZWYiCiAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgfSwKICAgICAgICAgICAgIm9wZXJhbmQiIDogWyB7CiAgICAgICAgICAgICAgICJuYW1lIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICJvcGVyYW5kVHlwZVNwZWNpZmllciIgOiB7CiAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICI1ODozMi01ODo1NCIsCiAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJ7aHR0cDovL2hsNy5vcmcvZmhpcn1BY3Rpb25TZWxlY3Rpb25CZWhhdmlvciIsCiAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJOYW1lZFR5cGVTcGVjaWZpZXIiCiAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgfSBdCiAgICAgICAgIH0sIHsKICAgICAgICAgICAgImxvY2F0b3IiIDogIjU5OjEtNTk6NjciLAogICAgICAgICAgICAibmFtZSIgOiAiVG9TdHJpbmciLAogICAgICAgICAgICAiY29udGV4dCIgOiAiUGF0aWVudCIsCiAgICAgICAgICAgICJhY2Nlc3NMZXZlbCIgOiAiUHVibGljIiwKICAgICAgICAgICAgInR5cGUiIDogIkZ1bmN0aW9uRGVmIiwKICAgICAgICAgICAgImV4cHJlc3Npb24iIDogewogICAgICAgICAgICAgICAibG9jYXRvciIgOiAiNTk6NTctNTk6NjciLAogICAgICAgICAgICAgICAicGF0aCIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAidHlwZSIgOiAiUHJvcGVydHkiLAogICAgICAgICAgICAgICAic291cmNlIiA6IHsKICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjU5OjU3LTU5OjYxIiwKICAgICAgICAgICAgICAgICAgIm5hbWUiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk9wZXJhbmRSZWYiCiAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgfSwKICAgICAgICAgICAgIm9wZXJhbmQiIDogWyB7CiAgICAgICAgICAgICAgICJuYW1lIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICJvcGVyYW5kVHlwZVNwZWNpZmllciIgOiB7CiAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICI1OTozMi01OTo1MyIsCiAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJ7aHR0cDovL2hsNy5vcmcvZmhpcn1BY3Rpdml0eURlZmluaXRpb25LaW5kIiwKICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk5hbWVkVHlwZVNwZWNpZmllciIKICAgICAgICAgICAgICAgfQogICAgICAgICAgICB9IF0KICAgICAgICAgfSwgewogICAgICAgICAgICAibG9jYXRvciIgOiAiNjA6MS02MDo2OCIsCiAgICAgICAgICAgICJuYW1lIiA6ICJUb1N0cmluZyIsCiAgICAgICAgICAgICJjb250ZXh0IiA6ICJQYXRpZW50IiwKICAgICAgICAgICAgImFjY2Vzc0xldmVsIiA6ICJQdWJsaWMiLAogICAgICAgICAgICAidHlwZSIgOiAiRnVuY3Rpb25EZWYiLAogICAgICAgICAgICAiZXhwcmVzc2lvbiIgOiB7CiAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICI2MDo1OC02MDo2OCIsCiAgICAgICAgICAgICAgICJwYXRoIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICJ0eXBlIiA6ICJQcm9wZXJ0eSIsCiAgICAgICAgICAgICAgICJzb3VyY2UiIDogewogICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiNjA6NTgtNjA6NjIiLAogICAgICAgICAgICAgICAgICAibmFtZSIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAgICAidHlwZSIgOiAiT3BlcmFuZFJlZiIKICAgICAgICAgICAgICAgfQogICAgICAgICAgICB9LAogICAgICAgICAgICAib3BlcmFuZCIgOiBbIHsKICAgICAgICAgICAgICAgIm5hbWUiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgIm9wZXJhbmRUeXBlU3BlY2lmaWVyIiA6IHsKICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjYwOjMyLTYwOjU0IiwKICAgICAgICAgICAgICAgICAgIm5hbWUiIDogIntodHRwOi8vaGw3Lm9yZy9maGlyfUFjdGl2aXR5UGFydGljaXBhbnRUeXBlIiwKICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk5hbWVkVHlwZVNwZWNpZmllciIKICAgICAgICAgICAgICAgfQogICAgICAgICAgICB9IF0KICAgICAgICAgfSwgewogICAgICAgICAgICAibG9jYXRvciIgOiAiNjE6MS02MTo1NiIsCiAgICAgICAgICAgICJuYW1lIiA6ICJUb1N0cmluZyIsCiAgICAgICAgICAgICJjb250ZXh0IiA6ICJQYXRpZW50IiwKICAgICAgICAgICAgImFjY2Vzc0xldmVsIiA6ICJQdWJsaWMiLAogICAgICAgICAgICAidHlwZSIgOiAiRnVuY3Rpb25EZWYiLAogICAgICAgICAgICAiZXhwcmVzc2lvbiIgOiB7CiAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICI2MTo0Ni02MTo1NiIsCiAgICAgICAgICAgICAgICJwYXRoIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICJ0eXBlIiA6ICJQcm9wZXJ0eSIsCiAgICAgICAgICAgICAgICJzb3VyY2UiIDogewogICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiNjE6NDYtNjE6NTAiLAogICAgICAgICAgICAgICAgICAibmFtZSIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAgICAidHlwZSIgOiAiT3BlcmFuZFJlZiIKICAgICAgICAgICAgICAgfQogICAgICAgICAgICB9LAogICAgICAgICAgICAib3BlcmFuZCIgOiBbIHsKICAgICAgICAgICAgICAgIm5hbWUiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgIm9wZXJhbmRUeXBlU3BlY2lmaWVyIiA6IHsKICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjYxOjMyLTYxOjQyIiwKICAgICAgICAgICAgICAgICAgIm5hbWUiIDogIntodHRwOi8vaGw3Lm9yZy9maGlyfUFkZHJlc3NUeXBlIiwKICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk5hbWVkVHlwZVNwZWNpZmllciIKICAgICAgICAgICAgICAgfQogICAgICAgICAgICB9IF0KICAgICAgICAgfSwgewogICAgICAgICAgICAibG9jYXRvciIgOiAiNjI6MS02Mjo1NSIsCiAgICAgICAgICAgICJuYW1lIiA6ICJUb1N0cmluZyIsCiAgICAgICAgICAgICJjb250ZXh0IiA6ICJQYXRpZW50IiwKICAgICAgICAgICAgImFjY2Vzc0xldmVsIiA6ICJQdWJsaWMiLAogICAgICAgICAgICAidHlwZSIgOiAiRnVuY3Rpb25EZWYiLAogICAgICAgICAgICAiZXhwcmVzc2lvbiIgOiB7CiAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICI2Mjo0NS02Mjo1NSIsCiAgICAgICAgICAgICAgICJwYXRoIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICJ0eXBlIiA6ICJQcm9wZXJ0eSIsCiAgICAgICAgICAgICAgICJzb3VyY2UiIDogewogICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiNjI6NDUtNjI6NDkiLAogICAgICAgICAgICAgICAgICAibmFtZSIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAgICAidHlwZSIgOiAiT3BlcmFuZFJlZiIKICAgICAgICAgICAgICAgfQogICAgICAgICAgICB9LAogICAgICAgICAgICAib3BlcmFuZCIgOiBbIHsKICAgICAgICAgICAgICAgIm5hbWUiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgIm9wZXJhbmRUeXBlU3BlY2lmaWVyIiA6IHsKICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjYyOjMyLTYyOjQxIiwKICAgICAgICAgICAgICAgICAgIm5hbWUiIDogIntodHRwOi8vaGw3Lm9yZy9maGlyfUFkZHJlc3NVc2UiLAogICAgICAgICAgICAgICAgICAidHlwZSIgOiAiTmFtZWRUeXBlU3BlY2lmaWVyIgogICAgICAgICAgICAgICB9CiAgICAgICAgICAgIH0gXQogICAgICAgICB9LCB7CiAgICAgICAgICAgICJsb2NhdG9yIiA6ICI2MzoxLTYzOjY1IiwKICAgICAgICAgICAgIm5hbWUiIDogIlRvU3RyaW5nIiwKICAgICAgICAgICAgImNvbnRleHQiIDogIlBhdGllbnQiLAogICAgICAgICAgICAiYWNjZXNzTGV2ZWwiIDogIlB1YmxpYyIsCiAgICAgICAgICAgICJ0eXBlIiA6ICJGdW5jdGlvbkRlZiIsCiAgICAgICAgICAgICJleHByZXNzaW9uIiA6IHsKICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjYzOjU1LTYzOjY1IiwKICAgICAgICAgICAgICAgInBhdGgiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgInR5cGUiIDogIlByb3BlcnR5IiwKICAgICAgICAgICAgICAgInNvdXJjZSIgOiB7CiAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICI2Mzo1NS02Mzo1OSIsCiAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJPcGVyYW5kUmVmIgogICAgICAgICAgICAgICB9CiAgICAgICAgICAgIH0sCiAgICAgICAgICAgICJvcGVyYW5kIiA6IFsgewogICAgICAgICAgICAgICAibmFtZSIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAib3BlcmFuZFR5cGVTcGVjaWZpZXIiIDogewogICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiNjM6MzItNjM6NTEiLAogICAgICAgICAgICAgICAgICAibmFtZSIgOiAie2h0dHA6Ly9obDcub3JnL2ZoaXJ9QWRtaW5pc3RyYXRpdmVHZW5kZXIiLAogICAgICAgICAgICAgICAgICAidHlwZSIgOiAiTmFtZWRUeXBlU3BlY2lmaWVyIgogICAgICAgICAgICAgICB9CiAgICAgICAgICAgIH0gXQogICAgICAgICB9LCB7CiAgICAgICAgICAgICJsb2NhdG9yIiA6ICI2NDoxLTY0OjY2IiwKICAgICAgICAgICAgIm5hbWUiIDogIlRvU3RyaW5nIiwKICAgICAgICAgICAgImNvbnRleHQiIDogIlBhdGllbnQiLAogICAgICAgICAgICAiYWNjZXNzTGV2ZWwiIDogIlB1YmxpYyIsCiAgICAgICAgICAgICJ0eXBlIiA6ICJGdW5jdGlvbkRlZiIsCiAgICAgICAgICAgICJleHByZXNzaW9uIiA6IHsKICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjY0OjU2LTY0OjY2IiwKICAgICAgICAgICAgICAgInBhdGgiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgInR5cGUiIDogIlByb3BlcnR5IiwKICAgICAgICAgICAgICAgInNvdXJjZSIgOiB7CiAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICI2NDo1Ni02NDo2MCIsCiAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJPcGVyYW5kUmVmIgogICAgICAgICAgICAgICB9CiAgICAgICAgICAgIH0sCiAgICAgICAgICAgICJvcGVyYW5kIiA6IFsgewogICAgICAgICAgICAgICAibmFtZSIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAib3BlcmFuZFR5cGVTcGVjaWZpZXIiIDogewogICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiNjQ6MzItNjQ6NTIiLAogICAgICAgICAgICAgICAgICAibmFtZSIgOiAie2h0dHA6Ly9obDcub3JnL2ZoaXJ9QWR2ZXJzZUV2ZW50QWN0dWFsaXR5IiwKICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk5hbWVkVHlwZVNwZWNpZmllciIKICAgICAgICAgICAgICAgfQogICAgICAgICAgICB9IF0KICAgICAgICAgfSwgewogICAgICAgICAgICAibG9jYXRvciIgOiAiNjU6MS02NTo2MCIsCiAgICAgICAgICAgICJuYW1lIiA6ICJUb1N0cmluZyIsCiAgICAgICAgICAgICJjb250ZXh0IiA6ICJQYXRpZW50IiwKICAgICAgICAgICAgImFjY2Vzc0xldmVsIiA6ICJQdWJsaWMiLAogICAgICAgICAgICAidHlwZSIgOiAiRnVuY3Rpb25EZWYiLAogICAgICAgICAgICAiZXhwcmVzc2lvbiIgOiB7CiAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICI2NTo1MC02NTo2MCIsCiAgICAgICAgICAgICAgICJwYXRoIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICJ0eXBlIiA6ICJQcm9wZXJ0eSIsCiAgICAgICAgICAgICAgICJzb3VyY2UiIDogewogICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiNjU6NTAtNjU6NTQiLAogICAgICAgICAgICAgICAgICAibmFtZSIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAgICAidHlwZSIgOiAiT3BlcmFuZFJlZiIKICAgICAgICAgICAgICAgfQogICAgICAgICAgICB9LAogICAgICAgICAgICAib3BlcmFuZCIgOiBbIHsKICAgICAgICAgICAgICAgIm5hbWUiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgIm9wZXJhbmRUeXBlU3BlY2lmaWVyIiA6IHsKICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjY1OjMyLTY1OjQ2IiwKICAgICAgICAgICAgICAgICAgIm5hbWUiIDogIntodHRwOi8vaGw3Lm9yZy9maGlyfUFnZ3JlZ2F0aW9uTW9kZSIsCiAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJOYW1lZFR5cGVTcGVjaWZpZXIiCiAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgfSBdCiAgICAgICAgIH0sIHsKICAgICAgICAgICAgImxvY2F0b3IiIDogIjY2OjEtNjY6NzEiLAogICAgICAgICAgICAibmFtZSIgOiAiVG9TdHJpbmciLAogICAgICAgICAgICAiY29udGV4dCIgOiAiUGF0aWVudCIsCiAgICAgICAgICAgICJhY2Nlc3NMZXZlbCIgOiAiUHVibGljIiwKICAgICAgICAgICAgInR5cGUiIDogIkZ1bmN0aW9uRGVmIiwKICAgICAgICAgICAgImV4cHJlc3Npb24iIDogewogICAgICAgICAgICAgICAibG9jYXRvciIgOiAiNjY6NjEtNjY6NzEiLAogICAgICAgICAgICAgICAicGF0aCIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAidHlwZSIgOiAiUHJvcGVydHkiLAogICAgICAgICAgICAgICAic291cmNlIiA6IHsKICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjY2OjYxLTY2OjY1IiwKICAgICAgICAgICAgICAgICAgIm5hbWUiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk9wZXJhbmRSZWYiCiAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgfSwKICAgICAgICAgICAgIm9wZXJhbmQiIDogWyB7CiAgICAgICAgICAgICAgICJuYW1lIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICJvcGVyYW5kVHlwZVNwZWNpZmllciIgOiB7CiAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICI2NjozMi02Njo1NyIsCiAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJ7aHR0cDovL2hsNy5vcmcvZmhpcn1BbGxlcmd5SW50b2xlcmFuY2VDYXRlZ29yeSIsCiAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJOYW1lZFR5cGVTcGVjaWZpZXIiCiAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgfSBdCiAgICAgICAgIH0sIHsKICAgICAgICAgICAgImxvY2F0b3IiIDogIjY3OjEtNjc6NzQiLAogICAgICAgICAgICAibmFtZSIgOiAiVG9TdHJpbmciLAogICAgICAgICAgICAiY29udGV4dCIgOiAiUGF0aWVudCIsCiAgICAgICAgICAgICJhY2Nlc3NMZXZlbCIgOiAiUHVibGljIiwKICAgICAgICAgICAgInR5cGUiIDogIkZ1bmN0aW9uRGVmIiwKICAgICAgICAgICAgImV4cHJlc3Npb24iIDogewogICAgICAgICAgICAgICAibG9jYXRvciIgOiAiNjc6NjQtNjc6NzQiLAogICAgICAgICAgICAgICAicGF0aCIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAidHlwZSIgOiAiUHJvcGVydHkiLAogICAgICAgICAgICAgICAic291cmNlIiA6IHsKICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjY3OjY0LTY3OjY4IiwKICAgICAgICAgICAgICAgICAgIm5hbWUiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk9wZXJhbmRSZWYiCiAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgfSwKICAgICAgICAgICAgIm9wZXJhbmQiIDogWyB7CiAgICAgICAgICAgICAgICJuYW1lIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICJvcGVyYW5kVHlwZVNwZWNpZmllciIgOiB7CiAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICI2NzozMi02Nzo2MCIsCiAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJ7aHR0cDovL2hsNy5vcmcvZmhpcn1BbGxlcmd5SW50b2xlcmFuY2VDcml0aWNhbGl0eSIsCiAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJOYW1lZFR5cGVTcGVjaWZpZXIiCiAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgfSBdCiAgICAgICAgIH0sIHsKICAgICAgICAgICAgImxvY2F0b3IiIDogIjY4OjEtNjg6NzEiLAogICAgICAgICAgICAibmFtZSIgOiAiVG9TdHJpbmciLAogICAgICAgICAgICAiY29udGV4dCIgOiAiUGF0aWVudCIsCiAgICAgICAgICAgICJhY2Nlc3NMZXZlbCIgOiAiUHVibGljIiwKICAgICAgICAgICAgInR5cGUiIDogIkZ1bmN0aW9uRGVmIiwKICAgICAgICAgICAgImV4cHJlc3Npb24iIDogewogICAgICAgICAgICAgICAibG9jYXRvciIgOiAiNjg6NjEtNjg6NzEiLAogICAgICAgICAgICAgICAicGF0aCIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAidHlwZSIgOiAiUHJvcGVydHkiLAogICAgICAgICAgICAgICAic291cmNlIiA6IHsKICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjY4OjYxLTY4OjY1IiwKICAgICAgICAgICAgICAgICAgIm5hbWUiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk9wZXJhbmRSZWYiCiAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgfSwKICAgICAgICAgICAgIm9wZXJhbmQiIDogWyB7CiAgICAgICAgICAgICAgICJuYW1lIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICJvcGVyYW5kVHlwZVNwZWNpZmllciIgOiB7CiAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICI2ODozMi02ODo1NyIsCiAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJ7aHR0cDovL2hsNy5vcmcvZmhpcn1BbGxlcmd5SW50b2xlcmFuY2VTZXZlcml0eSIsCiAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJOYW1lZFR5cGVTcGVjaWZpZXIiCiAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgfSBdCiAgICAgICAgIH0sIHsKICAgICAgICAgICAgImxvY2F0b3IiIDogIjY5OjEtNjk6NjciLAogICAgICAgICAgICAibmFtZSIgOiAiVG9TdHJpbmciLAogICAgICAgICAgICAiY29udGV4dCIgOiAiUGF0aWVudCIsCiAgICAgICAgICAgICJhY2Nlc3NMZXZlbCIgOiAiUHVibGljIiwKICAgICAgICAgICAgInR5cGUiIDogIkZ1bmN0aW9uRGVmIiwKICAgICAgICAgICAgImV4cHJlc3Npb24iIDogewogICAgICAgICAgICAgICAibG9jYXRvciIgOiAiNjk6NTctNjk6NjciLAogICAgICAgICAgICAgICAicGF0aCIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAidHlwZSIgOiAiUHJvcGVydHkiLAogICAgICAgICAgICAgICAic291cmNlIiA6IHsKICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjY5OjU3LTY5OjYxIiwKICAgICAgICAgICAgICAgICAgIm5hbWUiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk9wZXJhbmRSZWYiCiAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgfSwKICAgICAgICAgICAgIm9wZXJhbmQiIDogWyB7CiAgICAgICAgICAgICAgICJuYW1lIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICJvcGVyYW5kVHlwZVNwZWNpZmllciIgOiB7CiAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICI2OTozMi02OTo1MyIsCiAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJ7aHR0cDovL2hsNy5vcmcvZmhpcn1BbGxlcmd5SW50b2xlcmFuY2VUeXBlIiwKICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk5hbWVkVHlwZVNwZWNpZmllciIKICAgICAgICAgICAgICAgfQogICAgICAgICAgICB9IF0KICAgICAgICAgfSwgewogICAgICAgICAgICAibG9jYXRvciIgOiAiNzA6MS03MDo2MiIsCiAgICAgICAgICAgICJuYW1lIiA6ICJUb1N0cmluZyIsCiAgICAgICAgICAgICJjb250ZXh0IiA6ICJQYXRpZW50IiwKICAgICAgICAgICAgImFjY2Vzc0xldmVsIiA6ICJQdWJsaWMiLAogICAgICAgICAgICAidHlwZSIgOiAiRnVuY3Rpb25EZWYiLAogICAgICAgICAgICAiZXhwcmVzc2lvbiIgOiB7CiAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICI3MDo1Mi03MDo2MiIsCiAgICAgICAgICAgICAgICJwYXRoIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICJ0eXBlIiA6ICJQcm9wZXJ0eSIsCiAgICAgICAgICAgICAgICJzb3VyY2UiIDogewogICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiNzA6NTItNzA6NTYiLAogICAgICAgICAgICAgICAgICAibmFtZSIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAgICAidHlwZSIgOiAiT3BlcmFuZFJlZiIKICAgICAgICAgICAgICAgfQogICAgICAgICAgICB9LAogICAgICAgICAgICAib3BlcmFuZCIgOiBbIHsKICAgICAgICAgICAgICAgIm5hbWUiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgIm9wZXJhbmRUeXBlU3BlY2lmaWVyIiA6IHsKICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjcwOjMyLTcwOjQ4IiwKICAgICAgICAgICAgICAgICAgIm5hbWUiIDogIntodHRwOi8vaGw3Lm9yZy9maGlyfUFwcG9pbnRtZW50U3RhdHVzIiwKICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk5hbWVkVHlwZVNwZWNpZmllciIKICAgICAgICAgICAgICAgfQogICAgICAgICAgICB9IF0KICAgICAgICAgfSwgewogICAgICAgICAgICAibG9jYXRvciIgOiAiNzE6MS03MTo2NyIsCiAgICAgICAgICAgICJuYW1lIiA6ICJUb1N0cmluZyIsCiAgICAgICAgICAgICJjb250ZXh0IiA6ICJQYXRpZW50IiwKICAgICAgICAgICAgImFjY2Vzc0xldmVsIiA6ICJQdWJsaWMiLAogICAgICAgICAgICAidHlwZSIgOiAiRnVuY3Rpb25EZWYiLAogICAgICAgICAgICAiZXhwcmVzc2lvbiIgOiB7CiAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICI3MTo1Ny03MTo2NyIsCiAgICAgICAgICAgICAgICJwYXRoIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICJ0eXBlIiA6ICJQcm9wZXJ0eSIsCiAgICAgICAgICAgICAgICJzb3VyY2UiIDogewogICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiNzE6NTctNzE6NjEiLAogICAgICAgICAgICAgICAgICAibmFtZSIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAgICAidHlwZSIgOiAiT3BlcmFuZFJlZiIKICAgICAgICAgICAgICAgfQogICAgICAgICAgICB9LAogICAgICAgICAgICAib3BlcmFuZCIgOiBbIHsKICAgICAgICAgICAgICAgIm5hbWUiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgIm9wZXJhbmRUeXBlU3BlY2lmaWVyIiA6IHsKICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjcxOjMyLTcxOjUzIiwKICAgICAgICAgICAgICAgICAgIm5hbWUiIDogIntodHRwOi8vaGw3Lm9yZy9maGlyfUFzc2VydGlvbkRpcmVjdGlvblR5cGUiLAogICAgICAgICAgICAgICAgICAidHlwZSIgOiAiTmFtZWRUeXBlU3BlY2lmaWVyIgogICAgICAgICAgICAgICB9CiAgICAgICAgICAgIH0gXQogICAgICAgICB9LCB7CiAgICAgICAgICAgICJsb2NhdG9yIiA6ICI3MjoxLTcyOjY2IiwKICAgICAgICAgICAgIm5hbWUiIDogIlRvU3RyaW5nIiwKICAgICAgICAgICAgImNvbnRleHQiIDogIlBhdGllbnQiLAogICAgICAgICAgICAiYWNjZXNzTGV2ZWwiIDogIlB1YmxpYyIsCiAgICAgICAgICAgICJ0eXBlIiA6ICJGdW5jdGlvbkRlZiIsCiAgICAgICAgICAgICJleHByZXNzaW9uIiA6IHsKICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjcyOjU2LTcyOjY2IiwKICAgICAgICAgICAgICAgInBhdGgiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgInR5cGUiIDogIlByb3BlcnR5IiwKICAgICAgICAgICAgICAgInNvdXJjZSIgOiB7CiAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICI3Mjo1Ni03Mjo2MCIsCiAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJPcGVyYW5kUmVmIgogICAgICAgICAgICAgICB9CiAgICAgICAgICAgIH0sCiAgICAgICAgICAgICJvcGVyYW5kIiA6IFsgewogICAgICAgICAgICAgICAibmFtZSIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAib3BlcmFuZFR5cGVTcGVjaWZpZXIiIDogewogICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiNzI6MzItNzI6NTIiLAogICAgICAgICAgICAgICAgICAibmFtZSIgOiAie2h0dHA6Ly9obDcub3JnL2ZoaXJ9QXNzZXJ0aW9uT3BlcmF0b3JUeXBlIiwKICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk5hbWVkVHlwZVNwZWNpZmllciIKICAgICAgICAgICAgICAgfQogICAgICAgICAgICB9IF0KICAgICAgICAgfSwgewogICAgICAgICAgICAibG9jYXRvciIgOiAiNzM6MS03Mzo2NyIsCiAgICAgICAgICAgICJuYW1lIiA6ICJUb1N0cmluZyIsCiAgICAgICAgICAgICJjb250ZXh0IiA6ICJQYXRpZW50IiwKICAgICAgICAgICAgImFjY2Vzc0xldmVsIiA6ICJQdWJsaWMiLAogICAgICAgICAgICAidHlwZSIgOiAiRnVuY3Rpb25EZWYiLAogICAgICAgICAgICAiZXhwcmVzc2lvbiIgOiB7CiAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICI3Mzo1Ny03Mzo2NyIsCiAgICAgICAgICAgICAgICJwYXRoIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICJ0eXBlIiA6ICJQcm9wZXJ0eSIsCiAgICAgICAgICAgICAgICJzb3VyY2UiIDogewogICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiNzM6NTctNzM6NjEiLAogICAgICAgICAgICAgICAgICAibmFtZSIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAgICAidHlwZSIgOiAiT3BlcmFuZFJlZiIKICAgICAgICAgICAgICAgfQogICAgICAgICAgICB9LAogICAgICAgICAgICAib3BlcmFuZCIgOiBbIHsKICAgICAgICAgICAgICAgIm5hbWUiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgIm9wZXJhbmRUeXBlU3BlY2lmaWVyIiA6IHsKICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjczOjMyLTczOjUzIiwKICAgICAgICAgICAgICAgICAgIm5hbWUiIDogIntodHRwOi8vaGw3Lm9yZy9maGlyfUFzc2VydGlvblJlc3BvbnNlVHlwZXMiLAogICAgICAgICAgICAgICAgICAidHlwZSIgOiAiTmFtZWRUeXBlU3BlY2lmaWVyIgogICAgICAgICAgICAgICB9CiAgICAgICAgICAgIH0gXQogICAgICAgICB9LCB7CiAgICAgICAgICAgICJsb2NhdG9yIiA6ICI3NDoxLTc0OjYxIiwKICAgICAgICAgICAgIm5hbWUiIDogIlRvU3RyaW5nIiwKICAgICAgICAgICAgImNvbnRleHQiIDogIlBhdGllbnQiLAogICAgICAgICAgICAiYWNjZXNzTGV2ZWwiIDogIlB1YmxpYyIsCiAgICAgICAgICAgICJ0eXBlIiA6ICJGdW5jdGlvbkRlZiIsCiAgICAgICAgICAgICJleHByZXNzaW9uIiA6IHsKICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjc0OjUxLTc0OjYxIiwKICAgICAgICAgICAgICAgInBhdGgiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgInR5cGUiIDogIlByb3BlcnR5IiwKICAgICAgICAgICAgICAgInNvdXJjZSIgOiB7CiAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICI3NDo1MS03NDo1NSIsCiAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJPcGVyYW5kUmVmIgogICAgICAgICAgICAgICB9CiAgICAgICAgICAgIH0sCiAgICAgICAgICAgICJvcGVyYW5kIiA6IFsgewogICAgICAgICAgICAgICAibmFtZSIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAib3BlcmFuZFR5cGVTcGVjaWZpZXIiIDogewogICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiNzQ6MzItNzQ6NDciLAogICAgICAgICAgICAgICAgICAibmFtZSIgOiAie2h0dHA6Ly9obDcub3JnL2ZoaXJ9QXVkaXRFdmVudEFjdGlvbiIsCiAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJOYW1lZFR5cGVTcGVjaWZpZXIiCiAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgfSBdCiAgICAgICAgIH0sIHsKICAgICAgICAgICAgImxvY2F0b3IiIDogIjc1OjEtNzU6NzEiLAogICAgICAgICAgICAibmFtZSIgOiAiVG9TdHJpbmciLAogICAgICAgICAgICAiY29udGV4dCIgOiAiUGF0aWVudCIsCiAgICAgICAgICAgICJhY2Nlc3NMZXZlbCIgOiAiUHVibGljIiwKICAgICAgICAgICAgInR5cGUiIDogIkZ1bmN0aW9uRGVmIiwKICAgICAgICAgICAgImV4cHJlc3Npb24iIDogewogICAgICAgICAgICAgICAibG9jYXRvciIgOiAiNzU6NjEtNzU6NzEiLAogICAgICAgICAgICAgICAicGF0aCIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAidHlwZSIgOiAiUHJvcGVydHkiLAogICAgICAgICAgICAgICAic291cmNlIiA6IHsKICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjc1OjYxLTc1OjY1IiwKICAgICAgICAgICAgICAgICAgIm5hbWUiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk9wZXJhbmRSZWYiCiAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgfSwKICAgICAgICAgICAgIm9wZXJhbmQiIDogWyB7CiAgICAgICAgICAgICAgICJuYW1lIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICJvcGVyYW5kVHlwZVNwZWNpZmllciIgOiB7CiAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICI3NTozMi03NTo1NyIsCiAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJ7aHR0cDovL2hsNy5vcmcvZmhpcn1BdWRpdEV2ZW50QWdlbnROZXR3b3JrVHlwZSIsCiAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJOYW1lZFR5cGVTcGVjaWZpZXIiCiAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgfSBdCiAgICAgICAgIH0sIHsKICAgICAgICAgICAgImxvY2F0b3IiIDogIjc2OjEtNzY6NjIiLAogICAgICAgICAgICAibmFtZSIgOiAiVG9TdHJpbmciLAogICAgICAgICAgICAiY29udGV4dCIgOiAiUGF0aWVudCIsCiAgICAgICAgICAgICJhY2Nlc3NMZXZlbCIgOiAiUHVibGljIiwKICAgICAgICAgICAgInR5cGUiIDogIkZ1bmN0aW9uRGVmIiwKICAgICAgICAgICAgImV4cHJlc3Npb24iIDogewogICAgICAgICAgICAgICAibG9jYXRvciIgOiAiNzY6NTItNzY6NjIiLAogICAgICAgICAgICAgICAicGF0aCIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAidHlwZSIgOiAiUHJvcGVydHkiLAogICAgICAgICAgICAgICAic291cmNlIiA6IHsKICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjc2OjUyLTc2OjU2IiwKICAgICAgICAgICAgICAgICAgIm5hbWUiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk9wZXJhbmRSZWYiCiAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgfSwKICAgICAgICAgICAgIm9wZXJhbmQiIDogWyB7CiAgICAgICAgICAgICAgICJuYW1lIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICJvcGVyYW5kVHlwZVNwZWNpZmllciIgOiB7CiAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICI3NjozMi03Njo0OCIsCiAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJ7aHR0cDovL2hsNy5vcmcvZmhpcn1BdWRpdEV2ZW50T3V0Y29tZSIsCiAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJOYW1lZFR5cGVTcGVjaWZpZXIiCiAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgfSBdCiAgICAgICAgIH0sIHsKICAgICAgICAgICAgImxvY2F0b3IiIDogIjc3OjEtNzc6NjAiLAogICAgICAgICAgICAibmFtZSIgOiAiVG9TdHJpbmciLAogICAgICAgICAgICAiY29udGV4dCIgOiAiUGF0aWVudCIsCiAgICAgICAgICAgICJhY2Nlc3NMZXZlbCIgOiAiUHVibGljIiwKICAgICAgICAgICAgInR5cGUiIDogIkZ1bmN0aW9uRGVmIiwKICAgICAgICAgICAgImV4cHJlc3Npb24iIDogewogICAgICAgICAgICAgICAibG9jYXRvciIgOiAiNzc6NTAtNzc6NjAiLAogICAgICAgICAgICAgICAicGF0aCIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAidHlwZSIgOiAiUHJvcGVydHkiLAogICAgICAgICAgICAgICAic291cmNlIiA6IHsKICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjc3OjUwLTc3OjU0IiwKICAgICAgICAgICAgICAgICAgIm5hbWUiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk9wZXJhbmRSZWYiCiAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgfSwKICAgICAgICAgICAgIm9wZXJhbmQiIDogWyB7CiAgICAgICAgICAgICAgICJuYW1lIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICJvcGVyYW5kVHlwZVNwZWNpZmllciIgOiB7CiAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICI3NzozMi03Nzo0NiIsCiAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJ7aHR0cDovL2hsNy5vcmcvZmhpcn1CaW5kaW5nU3RyZW5ndGgiLAogICAgICAgICAgICAgICAgICAidHlwZSIgOiAiTmFtZWRUeXBlU3BlY2lmaWVyIgogICAgICAgICAgICAgICB9CiAgICAgICAgICAgIH0gXQogICAgICAgICB9LCB7CiAgICAgICAgICAgICJsb2NhdG9yIiA6ICI3ODoxLTc4Ojc5IiwKICAgICAgICAgICAgIm5hbWUiIDogIlRvU3RyaW5nIiwKICAgICAgICAgICAgImNvbnRleHQiIDogIlBhdGllbnQiLAogICAgICAgICAgICAiYWNjZXNzTGV2ZWwiIDogIlB1YmxpYyIsCiAgICAgICAgICAgICJ0eXBlIiA6ICJGdW5jdGlvbkRlZiIsCiAgICAgICAgICAgICJleHByZXNzaW9uIiA6IHsKICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjc4OjY5LTc4Ojc5IiwKICAgICAgICAgICAgICAgInBhdGgiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgInR5cGUiIDogIlByb3BlcnR5IiwKICAgICAgICAgICAgICAgInNvdXJjZSIgOiB7CiAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICI3ODo2OS03ODo3MyIsCiAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJPcGVyYW5kUmVmIgogICAgICAgICAgICAgICB9CiAgICAgICAgICAgIH0sCiAgICAgICAgICAgICJvcGVyYW5kIiA6IFsgewogICAgICAgICAgICAgICAibmFtZSIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAib3BlcmFuZFR5cGVTcGVjaWZpZXIiIDogewogICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiNzg6MzItNzg6NjUiLAogICAgICAgICAgICAgICAgICAibmFtZSIgOiAie2h0dHA6Ly9obDcub3JnL2ZoaXJ9QmlvbG9naWNhbGx5RGVyaXZlZFByb2R1Y3RDYXRlZ29yeSIsCiAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJOYW1lZFR5cGVTcGVjaWZpZXIiCiAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgfSBdCiAgICAgICAgIH0sIHsKICAgICAgICAgICAgImxvY2F0b3IiIDogIjc5OjEtNzk6NzciLAogICAgICAgICAgICAibmFtZSIgOiAiVG9TdHJpbmciLAogICAgICAgICAgICAiY29udGV4dCIgOiAiUGF0aWVudCIsCiAgICAgICAgICAgICJhY2Nlc3NMZXZlbCIgOiAiUHVibGljIiwKICAgICAgICAgICAgInR5cGUiIDogIkZ1bmN0aW9uRGVmIiwKICAgICAgICAgICAgImV4cHJlc3Npb24iIDogewogICAgICAgICAgICAgICAibG9jYXRvciIgOiAiNzk6NjctNzk6NzciLAogICAgICAgICAgICAgICAicGF0aCIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAidHlwZSIgOiAiUHJvcGVydHkiLAogICAgICAgICAgICAgICAic291cmNlIiA6IHsKICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjc5OjY3LTc5OjcxIiwKICAgICAgICAgICAgICAgICAgIm5hbWUiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk9wZXJhbmRSZWYiCiAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgfSwKICAgICAgICAgICAgIm9wZXJhbmQiIDogWyB7CiAgICAgICAgICAgICAgICJuYW1lIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICJvcGVyYW5kVHlwZVNwZWNpZmllciIgOiB7CiAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICI3OTozMi03OTo2MyIsCiAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJ7aHR0cDovL2hsNy5vcmcvZmhpcn1CaW9sb2dpY2FsbHlEZXJpdmVkUHJvZHVjdFN0YXR1cyIsCiAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJOYW1lZFR5cGVTcGVjaWZpZXIiCiAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgfSBdCiAgICAgICAgIH0sIHsKICAgICAgICAgICAgImxvY2F0b3IiIDogIjgwOjEtODA6ODMiLAogICAgICAgICAgICAibmFtZSIgOiAiVG9TdHJpbmciLAogICAgICAgICAgICAiY29udGV4dCIgOiAiUGF0aWVudCIsCiAgICAgICAgICAgICJhY2Nlc3NMZXZlbCIgOiAiUHVibGljIiwKICAgICAgICAgICAgInR5cGUiIDogIkZ1bmN0aW9uRGVmIiwKICAgICAgICAgICAgImV4cHJlc3Npb24iIDogewogICAgICAgICAgICAgICAibG9jYXRvciIgOiAiODA6NzMtODA6ODMiLAogICAgICAgICAgICAgICAicGF0aCIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAidHlwZSIgOiAiUHJvcGVydHkiLAogICAgICAgICAgICAgICAic291cmNlIiA6IHsKICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjgwOjczLTgwOjc3IiwKICAgICAgICAgICAgICAgICAgIm5hbWUiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk9wZXJhbmRSZWYiCiAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgfSwKICAgICAgICAgICAgIm9wZXJhbmQiIDogWyB7CiAgICAgICAgICAgICAgICJuYW1lIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICJvcGVyYW5kVHlwZVNwZWNpZmllciIgOiB7CiAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICI4MDozMi04MDo2OSIsCiAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJ7aHR0cDovL2hsNy5vcmcvZmhpcn1CaW9sb2dpY2FsbHlEZXJpdmVkUHJvZHVjdFN0b3JhZ2VTY2FsZSIsCiAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJOYW1lZFR5cGVTcGVjaWZpZXIiCiAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgfSBdCiAgICAgICAgIH0sIHsKICAgICAgICAgICAgImxvY2F0b3IiIDogIjgxOjEtODE6NTUiLAogICAgICAgICAgICAibmFtZSIgOiAiVG9TdHJpbmciLAogICAgICAgICAgICAiY29udGV4dCIgOiAiUGF0aWVudCIsCiAgICAgICAgICAgICJhY2Nlc3NMZXZlbCIgOiAiUHVibGljIiwKICAgICAgICAgICAgInR5cGUiIDogIkZ1bmN0aW9uRGVmIiwKICAgICAgICAgICAgImV4cHJlc3Npb24iIDogewogICAgICAgICAgICAgICAibG9jYXRvciIgOiAiODE6NDUtODE6NTUiLAogICAgICAgICAgICAgICAicGF0aCIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAidHlwZSIgOiAiUHJvcGVydHkiLAogICAgICAgICAgICAgICAic291cmNlIiA6IHsKICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjgxOjQ1LTgxOjQ5IiwKICAgICAgICAgICAgICAgICAgIm5hbWUiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk9wZXJhbmRSZWYiCiAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgfSwKICAgICAgICAgICAgIm9wZXJhbmQiIDogWyB7CiAgICAgICAgICAgICAgICJuYW1lIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICJvcGVyYW5kVHlwZVNwZWNpZmllciIgOiB7CiAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICI4MTozMi04MTo0MSIsCiAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJ7aHR0cDovL2hsNy5vcmcvZmhpcn1CdW5kbGVUeXBlIiwKICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk5hbWVkVHlwZVNwZWNpZmllciIKICAgICAgICAgICAgICAgfQogICAgICAgICAgICB9IF0KICAgICAgICAgfSwgewogICAgICAgICAgICAibG9jYXRvciIgOiAiODI6MS04Mjo2OCIsCiAgICAgICAgICAgICJuYW1lIiA6ICJUb1N0cmluZyIsCiAgICAgICAgICAgICJjb250ZXh0IiA6ICJQYXRpZW50IiwKICAgICAgICAgICAgImFjY2Vzc0xldmVsIiA6ICJQdWJsaWMiLAogICAgICAgICAgICAidHlwZSIgOiAiRnVuY3Rpb25EZWYiLAogICAgICAgICAgICAiZXhwcmVzc2lvbiIgOiB7CiAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICI4Mjo1OC04Mjo2OCIsCiAgICAgICAgICAgICAgICJwYXRoIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICJ0eXBlIiA6ICJQcm9wZXJ0eSIsCiAgICAgICAgICAgICAgICJzb3VyY2UiIDogewogICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiODI6NTgtODI6NjIiLAogICAgICAgICAgICAgICAgICAibmFtZSIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAgICAidHlwZSIgOiAiT3BlcmFuZFJlZiIKICAgICAgICAgICAgICAgfQogICAgICAgICAgICB9LAogICAgICAgICAgICAib3BlcmFuZCIgOiBbIHsKICAgICAgICAgICAgICAgIm5hbWUiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgIm9wZXJhbmRUeXBlU3BlY2lmaWVyIiA6IHsKICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjgyOjMyLTgyOjU0IiwKICAgICAgICAgICAgICAgICAgIm5hbWUiIDogIntodHRwOi8vaGw3Lm9yZy9maGlyfUNhcGFiaWxpdHlTdGF0ZW1lbnRLaW5kIiwKICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk5hbWVkVHlwZVNwZWNpZmllciIKICAgICAgICAgICAgICAgfQogICAgICAgICAgICB9IF0KICAgICAgICAgfSwgewogICAgICAgICAgICAibG9jYXRvciIgOiAiODM6MS04Mzo2NSIsCiAgICAgICAgICAgICJuYW1lIiA6ICJUb1N0cmluZyIsCiAgICAgICAgICAgICJjb250ZXh0IiA6ICJQYXRpZW50IiwKICAgICAgICAgICAgImFjY2Vzc0xldmVsIiA6ICJQdWJsaWMiLAogICAgICAgICAgICAidHlwZSIgOiAiRnVuY3Rpb25EZWYiLAogICAgICAgICAgICAiZXhwcmVzc2lvbiIgOiB7CiAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICI4Mzo1NS04Mzo2NSIsCiAgICAgICAgICAgICAgICJwYXRoIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICJ0eXBlIiA6ICJQcm9wZXJ0eSIsCiAgICAgICAgICAgICAgICJzb3VyY2UiIDogewogICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiODM6NTUtODM6NTkiLAogICAgICAgICAgICAgICAgICAibmFtZSIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAgICAidHlwZSIgOiAiT3BlcmFuZFJlZiIKICAgICAgICAgICAgICAgfQogICAgICAgICAgICB9LAogICAgICAgICAgICAib3BlcmFuZCIgOiBbIHsKICAgICAgICAgICAgICAgIm5hbWUiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgIm9wZXJhbmRUeXBlU3BlY2lmaWVyIiA6IHsKICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjgzOjMyLTgzOjUxIiwKICAgICAgICAgICAgICAgICAgIm5hbWUiIDogIntodHRwOi8vaGw3Lm9yZy9maGlyfUNhcmVQbGFuQWN0aXZpdHlLaW5kIiwKICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk5hbWVkVHlwZVNwZWNpZmllciIKICAgICAgICAgICAgICAgfQogICAgICAgICAgICB9IF0KICAgICAgICAgfSwgewogICAgICAgICAgICAibG9jYXRvciIgOiAiODQ6MS04NDo2NyIsCiAgICAgICAgICAgICJuYW1lIiA6ICJUb1N0cmluZyIsCiAgICAgICAgICAgICJjb250ZXh0IiA6ICJQYXRpZW50IiwKICAgICAgICAgICAgImFjY2Vzc0xldmVsIiA6ICJQdWJsaWMiLAogICAgICAgICAgICAidHlwZSIgOiAiRnVuY3Rpb25EZWYiLAogICAgICAgICAgICAiZXhwcmVzc2lvbiIgOiB7CiAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICI4NDo1Ny04NDo2NyIsCiAgICAgICAgICAgICAgICJwYXRoIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICJ0eXBlIiA6ICJQcm9wZXJ0eSIsCiAgICAgICAgICAgICAgICJzb3VyY2UiIDogewogICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiODQ6NTctODQ6NjEiLAogICAgICAgICAgICAgICAgICAibmFtZSIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAgICAidHlwZSIgOiAiT3BlcmFuZFJlZiIKICAgICAgICAgICAgICAgfQogICAgICAgICAgICB9LAogICAgICAgICAgICAib3BlcmFuZCIgOiBbIHsKICAgICAgICAgICAgICAgIm5hbWUiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgIm9wZXJhbmRUeXBlU3BlY2lmaWVyIiA6IHsKICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjg0OjMyLTg0OjUzIiwKICAgICAgICAgICAgICAgICAgIm5hbWUiIDogIntodHRwOi8vaGw3Lm9yZy9maGlyfUNhcmVQbGFuQWN0aXZpdHlTdGF0dXMiLAogICAgICAgICAgICAgICAgICAidHlwZSIgOiAiTmFtZWRUeXBlU3BlY2lmaWVyIgogICAgICAgICAgICAgICB9CiAgICAgICAgICAgIH0gXQogICAgICAgICB9LCB7CiAgICAgICAgICAgICJsb2NhdG9yIiA6ICI4NToxLTg1OjU5IiwKICAgICAgICAgICAgIm5hbWUiIDogIlRvU3RyaW5nIiwKICAgICAgICAgICAgImNvbnRleHQiIDogIlBhdGllbnQiLAogICAgICAgICAgICAiYWNjZXNzTGV2ZWwiIDogIlB1YmxpYyIsCiAgICAgICAgICAgICJ0eXBlIiA6ICJGdW5jdGlvbkRlZiIsCiAgICAgICAgICAgICJleHByZXNzaW9uIiA6IHsKICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjg1OjQ5LTg1OjU5IiwKICAgICAgICAgICAgICAgInBhdGgiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgInR5cGUiIDogIlByb3BlcnR5IiwKICAgICAgICAgICAgICAgInNvdXJjZSIgOiB7CiAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICI4NTo0OS04NTo1MyIsCiAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJPcGVyYW5kUmVmIgogICAgICAgICAgICAgICB9CiAgICAgICAgICAgIH0sCiAgICAgICAgICAgICJvcGVyYW5kIiA6IFsgewogICAgICAgICAgICAgICAibmFtZSIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAib3BlcmFuZFR5cGVTcGVjaWZpZXIiIDogewogICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiODU6MzItODU6NDUiLAogICAgICAgICAgICAgICAgICAibmFtZSIgOiAie2h0dHA6Ly9obDcub3JnL2ZoaXJ9Q2FyZVBsYW5JbnRlbnQiLAogICAgICAgICAgICAgICAgICAidHlwZSIgOiAiTmFtZWRUeXBlU3BlY2lmaWVyIgogICAgICAgICAgICAgICB9CiAgICAgICAgICAgIH0gXQogICAgICAgICB9LCB7CiAgICAgICAgICAgICJsb2NhdG9yIiA6ICI4NjoxLTg2OjU5IiwKICAgICAgICAgICAgIm5hbWUiIDogIlRvU3RyaW5nIiwKICAgICAgICAgICAgImNvbnRleHQiIDogIlBhdGllbnQiLAogICAgICAgICAgICAiYWNjZXNzTGV2ZWwiIDogIlB1YmxpYyIsCiAgICAgICAgICAgICJ0eXBlIiA6ICJGdW5jdGlvbkRlZiIsCiAgICAgICAgICAgICJleHByZXNzaW9uIiA6IHsKICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjg2OjQ5LTg2OjU5IiwKICAgICAgICAgICAgICAgInBhdGgiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgInR5cGUiIDogIlByb3BlcnR5IiwKICAgICAgICAgICAgICAgInNvdXJjZSIgOiB7CiAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICI4Njo0OS04Njo1MyIsCiAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJPcGVyYW5kUmVmIgogICAgICAgICAgICAgICB9CiAgICAgICAgICAgIH0sCiAgICAgICAgICAgICJvcGVyYW5kIiA6IFsgewogICAgICAgICAgICAgICAibmFtZSIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAib3BlcmFuZFR5cGVTcGVjaWZpZXIiIDogewogICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiODY6MzItODY6NDUiLAogICAgICAgICAgICAgICAgICAibmFtZSIgOiAie2h0dHA6Ly9obDcub3JnL2ZoaXJ9Q2FyZVBsYW5TdGF0dXMiLAogICAgICAgICAgICAgICAgICAidHlwZSIgOiAiTmFtZWRUeXBlU3BlY2lmaWVyIgogICAgICAgICAgICAgICB9CiAgICAgICAgICAgIH0gXQogICAgICAgICB9LCB7CiAgICAgICAgICAgICJsb2NhdG9yIiA6ICI4NzoxLTg3OjU5IiwKICAgICAgICAgICAgIm5hbWUiIDogIlRvU3RyaW5nIiwKICAgICAgICAgICAgImNvbnRleHQiIDogIlBhdGllbnQiLAogICAgICAgICAgICAiYWNjZXNzTGV2ZWwiIDogIlB1YmxpYyIsCiAgICAgICAgICAgICJ0eXBlIiA6ICJGdW5jdGlvbkRlZiIsCiAgICAgICAgICAgICJleHByZXNzaW9uIiA6IHsKICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjg3OjQ5LTg3OjU5IiwKICAgICAgICAgICAgICAgInBhdGgiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgInR5cGUiIDogIlByb3BlcnR5IiwKICAgICAgICAgICAgICAgInNvdXJjZSIgOiB7CiAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICI4Nzo0OS04Nzo1MyIsCiAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJPcGVyYW5kUmVmIgogICAgICAgICAgICAgICB9CiAgICAgICAgICAgIH0sCiAgICAgICAgICAgICJvcGVyYW5kIiA6IFsgewogICAgICAgICAgICAgICAibmFtZSIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAib3BlcmFuZFR5cGVTcGVjaWZpZXIiIDogewogICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiODc6MzItODc6NDUiLAogICAgICAgICAgICAgICAgICAibmFtZSIgOiAie2h0dHA6Ly9obDcub3JnL2ZoaXJ9Q2FyZVRlYW1TdGF0dXMiLAogICAgICAgICAgICAgICAgICAidHlwZSIgOiAiTmFtZWRUeXBlU3BlY2lmaWVyIgogICAgICAgICAgICAgICB9CiAgICAgICAgICAgIH0gXQogICAgICAgICB9LCB7CiAgICAgICAgICAgICJsb2NhdG9yIiA6ICI4ODoxLTg4OjY5IiwKICAgICAgICAgICAgIm5hbWUiIDogIlRvU3RyaW5nIiwKICAgICAgICAgICAgImNvbnRleHQiIDogIlBhdGllbnQiLAogICAgICAgICAgICAiYWNjZXNzTGV2ZWwiIDogIlB1YmxpYyIsCiAgICAgICAgICAgICJ0eXBlIiA6ICJGdW5jdGlvbkRlZiIsCiAgICAgICAgICAgICJleHByZXNzaW9uIiA6IHsKICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjg4OjU5LTg4OjY5IiwKICAgICAgICAgICAgICAgInBhdGgiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgInR5cGUiIDogIlByb3BlcnR5IiwKICAgICAgICAgICAgICAgInNvdXJjZSIgOiB7CiAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICI4ODo1OS04ODo2MyIsCiAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJPcGVyYW5kUmVmIgogICAgICAgICAgICAgICB9CiAgICAgICAgICAgIH0sCiAgICAgICAgICAgICJvcGVyYW5kIiA6IFsgewogICAgICAgICAgICAgICAibmFtZSIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAib3BlcmFuZFR5cGVTcGVjaWZpZXIiIDogewogICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiODg6MzItODg6NTUiLAogICAgICAgICAgICAgICAgICAibmFtZSIgOiAie2h0dHA6Ly9obDcub3JnL2ZoaXJ9Q2F0YWxvZ0VudHJ5UmVsYXRpb25UeXBlIiwKICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk5hbWVkVHlwZVNwZWNpZmllciIKICAgICAgICAgICAgICAgfQogICAgICAgICAgICB9IF0KICAgICAgICAgfSwgewogICAgICAgICAgICAibG9jYXRvciIgOiAiODk6MS04OTo4MyIsCiAgICAgICAgICAgICJuYW1lIiA6ICJUb1N0cmluZyIsCiAgICAgICAgICAgICJjb250ZXh0IiA6ICJQYXRpZW50IiwKICAgICAgICAgICAgImFjY2Vzc0xldmVsIiA6ICJQdWJsaWMiLAogICAgICAgICAgICAidHlwZSIgOiAiRnVuY3Rpb25EZWYiLAogICAgICAgICAgICAiZXhwcmVzc2lvbiIgOiB7CiAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICI4OTo3My04OTo4MyIsCiAgICAgICAgICAgICAgICJwYXRoIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICJ0eXBlIiA6ICJQcm9wZXJ0eSIsCiAgICAgICAgICAgICAgICJzb3VyY2UiIDogewogICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiODk6NzMtODk6NzciLAogICAgICAgICAgICAgICAgICAibmFtZSIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAgICAidHlwZSIgOiAiT3BlcmFuZFJlZiIKICAgICAgICAgICAgICAgfQogICAgICAgICAgICB9LAogICAgICAgICAgICAib3BlcmFuZCIgOiBbIHsKICAgICAgICAgICAgICAgIm5hbWUiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgIm9wZXJhbmRUeXBlU3BlY2lmaWVyIiA6IHsKICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjg5OjMyLTg5OjY5IiwKICAgICAgICAgICAgICAgICAgIm5hbWUiIDogIntodHRwOi8vaGw3Lm9yZy9maGlyfUNoYXJnZUl0ZW1EZWZpbml0aW9uUHJpY2VDb21wb25lbnRUeXBlIiwKICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk5hbWVkVHlwZVNwZWNpZmllciIKICAgICAgICAgICAgICAgfQogICAgICAgICAgICB9IF0KICAgICAgICAgfSwgewogICAgICAgICAgICAibG9jYXRvciIgOiAiOTA6MS05MDo2MSIsCiAgICAgICAgICAgICJuYW1lIiA6ICJUb1N0cmluZyIsCiAgICAgICAgICAgICJjb250ZXh0IiA6ICJQYXRpZW50IiwKICAgICAgICAgICAgImFjY2Vzc0xldmVsIiA6ICJQdWJsaWMiLAogICAgICAgICAgICAidHlwZSIgOiAiRnVuY3Rpb25EZWYiLAogICAgICAgICAgICAiZXhwcmVzc2lvbiIgOiB7CiAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICI5MDo1MS05MDo2MSIsCiAgICAgICAgICAgICAgICJwYXRoIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICJ0eXBlIiA6ICJQcm9wZXJ0eSIsCiAgICAgICAgICAgICAgICJzb3VyY2UiIDogewogICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiOTA6NTEtOTA6NTUiLAogICAgICAgICAgICAgICAgICAibmFtZSIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAgICAidHlwZSIgOiAiT3BlcmFuZFJlZiIKICAgICAgICAgICAgICAgfQogICAgICAgICAgICB9LAogICAgICAgICAgICAib3BlcmFuZCIgOiBbIHsKICAgICAgICAgICAgICAgIm5hbWUiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgIm9wZXJhbmRUeXBlU3BlY2lmaWVyIiA6IHsKICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjkwOjMyLTkwOjQ3IiwKICAgICAgICAgICAgICAgICAgIm5hbWUiIDogIntodHRwOi8vaGw3Lm9yZy9maGlyfUNoYXJnZUl0ZW1TdGF0dXMiLAogICAgICAgICAgICAgICAgICAidHlwZSIgOiAiTmFtZWRUeXBlU3BlY2lmaWVyIgogICAgICAgICAgICAgICB9CiAgICAgICAgICAgIH0gXQogICAgICAgICB9LCB7CiAgICAgICAgICAgICJsb2NhdG9yIiA6ICI5MToxLTkxOjY0IiwKICAgICAgICAgICAgIm5hbWUiIDogIlRvU3RyaW5nIiwKICAgICAgICAgICAgImNvbnRleHQiIDogIlBhdGllbnQiLAogICAgICAgICAgICAiYWNjZXNzTGV2ZWwiIDogIlB1YmxpYyIsCiAgICAgICAgICAgICJ0eXBlIiA6ICJGdW5jdGlvbkRlZiIsCiAgICAgICAgICAgICJleHByZXNzaW9uIiA6IHsKICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjkxOjU0LTkxOjY0IiwKICAgICAgICAgICAgICAgInBhdGgiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgInR5cGUiIDogIlByb3BlcnR5IiwKICAgICAgICAgICAgICAgInNvdXJjZSIgOiB7CiAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICI5MTo1NC05MTo1OCIsCiAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJPcGVyYW5kUmVmIgogICAgICAgICAgICAgICB9CiAgICAgICAgICAgIH0sCiAgICAgICAgICAgICJvcGVyYW5kIiA6IFsgewogICAgICAgICAgICAgICAibmFtZSIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAib3BlcmFuZFR5cGVTcGVjaWZpZXIiIDogewogICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiOTE6MzItOTE6NTAiLAogICAgICAgICAgICAgICAgICAibmFtZSIgOiAie2h0dHA6Ly9obDcub3JnL2ZoaXJ9Q2xhaW1SZXNwb25zZVN0YXR1cyIsCiAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJOYW1lZFR5cGVTcGVjaWZpZXIiCiAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgfSBdCiAgICAgICAgIH0sIHsKICAgICAgICAgICAgImxvY2F0b3IiIDogIjkyOjEtOTI6NTYiLAogICAgICAgICAgICAibmFtZSIgOiAiVG9TdHJpbmciLAogICAgICAgICAgICAiY29udGV4dCIgOiAiUGF0aWVudCIsCiAgICAgICAgICAgICJhY2Nlc3NMZXZlbCIgOiAiUHVibGljIiwKICAgICAgICAgICAgInR5cGUiIDogIkZ1bmN0aW9uRGVmIiwKICAgICAgICAgICAgImV4cHJlc3Npb24iIDogewogICAgICAgICAgICAgICAibG9jYXRvciIgOiAiOTI6NDYtOTI6NTYiLAogICAgICAgICAgICAgICAicGF0aCIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAidHlwZSIgOiAiUHJvcGVydHkiLAogICAgICAgICAgICAgICAic291cmNlIiA6IHsKICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjkyOjQ2LTkyOjUwIiwKICAgICAgICAgICAgICAgICAgIm5hbWUiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk9wZXJhbmRSZWYiCiAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgfSwKICAgICAgICAgICAgIm9wZXJhbmQiIDogWyB7CiAgICAgICAgICAgICAgICJuYW1lIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICJvcGVyYW5kVHlwZVNwZWNpZmllciIgOiB7CiAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICI5MjozMi05Mjo0MiIsCiAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJ7aHR0cDovL2hsNy5vcmcvZmhpcn1DbGFpbVN0YXR1cyIsCiAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJOYW1lZFR5cGVTcGVjaWZpZXIiCiAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgfSBdCiAgICAgICAgIH0sIHsKICAgICAgICAgICAgImxvY2F0b3IiIDogIjkzOjEtOTM6NjkiLAogICAgICAgICAgICAibmFtZSIgOiAiVG9TdHJpbmciLAogICAgICAgICAgICAiY29udGV4dCIgOiAiUGF0aWVudCIsCiAgICAgICAgICAgICJhY2Nlc3NMZXZlbCIgOiAiUHVibGljIiwKICAgICAgICAgICAgInR5cGUiIDogIkZ1bmN0aW9uRGVmIiwKICAgICAgICAgICAgImV4cHJlc3Npb24iIDogewogICAgICAgICAgICAgICAibG9jYXRvciIgOiAiOTM6NTktOTM6NjkiLAogICAgICAgICAgICAgICAicGF0aCIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAidHlwZSIgOiAiUHJvcGVydHkiLAogICAgICAgICAgICAgICAic291cmNlIiA6IHsKICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjkzOjU5LTkzOjYzIiwKICAgICAgICAgICAgICAgICAgIm5hbWUiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk9wZXJhbmRSZWYiCiAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgfSwKICAgICAgICAgICAgIm9wZXJhbmQiIDogWyB7CiAgICAgICAgICAgICAgICJuYW1lIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICJvcGVyYW5kVHlwZVNwZWNpZmllciIgOiB7CiAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICI5MzozMi05Mzo1NSIsCiAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJ7aHR0cDovL2hsNy5vcmcvZmhpcn1DbGluaWNhbEltcHJlc3Npb25TdGF0dXMiLAogICAgICAgICAgICAgICAgICAidHlwZSIgOiAiTmFtZWRUeXBlU3BlY2lmaWVyIgogICAgICAgICAgICAgICB9CiAgICAgICAgICAgIH0gXQogICAgICAgICB9LCB7CiAgICAgICAgICAgICJsb2NhdG9yIiA6ICI5NDoxLTk0OjYyIiwKICAgICAgICAgICAgIm5hbWUiIDogIlRvU3RyaW5nIiwKICAgICAgICAgICAgImNvbnRleHQiIDogIlBhdGllbnQiLAogICAgICAgICAgICAiYWNjZXNzTGV2ZWwiIDogIlB1YmxpYyIsCiAgICAgICAgICAgICJ0eXBlIiA6ICJGdW5jdGlvbkRlZiIsCiAgICAgICAgICAgICJleHByZXNzaW9uIiA6IHsKICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjk0OjUyLTk0OjYyIiwKICAgICAgICAgICAgICAgInBhdGgiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgInR5cGUiIDogIlByb3BlcnR5IiwKICAgICAgICAgICAgICAgInNvdXJjZSIgOiB7CiAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICI5NDo1Mi05NDo1NiIsCiAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJPcGVyYW5kUmVmIgogICAgICAgICAgICAgICB9CiAgICAgICAgICAgIH0sCiAgICAgICAgICAgICJvcGVyYW5kIiA6IFsgewogICAgICAgICAgICAgICAibmFtZSIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAib3BlcmFuZFR5cGVTcGVjaWZpZXIiIDogewogICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiOTQ6MzItOTQ6NDgiLAogICAgICAgICAgICAgICAgICAibmFtZSIgOiAie2h0dHA6Ly9obDcub3JnL2ZoaXJ9Q29kZVNlYXJjaFN1cHBvcnQiLAogICAgICAgICAgICAgICAgICAidHlwZSIgOiAiTmFtZWRUeXBlU3BlY2lmaWVyIgogICAgICAgICAgICAgICB9CiAgICAgICAgICAgIH0gXQogICAgICAgICB9LCB7CiAgICAgICAgICAgICJsb2NhdG9yIiA6ICI5NToxLTk1OjY2IiwKICAgICAgICAgICAgIm5hbWUiIDogIlRvU3RyaW5nIiwKICAgICAgICAgICAgImNvbnRleHQiIDogIlBhdGllbnQiLAogICAgICAgICAgICAiYWNjZXNzTGV2ZWwiIDogIlB1YmxpYyIsCiAgICAgICAgICAgICJ0eXBlIiA6ICJGdW5jdGlvbkRlZiIsCiAgICAgICAgICAgICJleHByZXNzaW9uIiA6IHsKICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjk1OjU2LTk1OjY2IiwKICAgICAgICAgICAgICAgInBhdGgiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgInR5cGUiIDogIlByb3BlcnR5IiwKICAgICAgICAgICAgICAgInNvdXJjZSIgOiB7CiAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICI5NTo1Ni05NTo2MCIsCiAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJPcGVyYW5kUmVmIgogICAgICAgICAgICAgICB9CiAgICAgICAgICAgIH0sCiAgICAgICAgICAgICJvcGVyYW5kIiA6IFsgewogICAgICAgICAgICAgICAibmFtZSIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAib3BlcmFuZFR5cGVTcGVjaWZpZXIiIDogewogICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiOTU6MzItOTU6NTIiLAogICAgICAgICAgICAgICAgICAibmFtZSIgOiAie2h0dHA6Ly9obDcub3JnL2ZoaXJ9Q29kZVN5c3RlbUNvbnRlbnRNb2RlIiwKICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk5hbWVkVHlwZVNwZWNpZmllciIKICAgICAgICAgICAgICAgfQogICAgICAgICAgICB9IF0KICAgICAgICAgfSwgewogICAgICAgICAgICAibG9jYXRvciIgOiAiOTY6MS05Njo3MSIsCiAgICAgICAgICAgICJuYW1lIiA6ICJUb1N0cmluZyIsCiAgICAgICAgICAgICJjb250ZXh0IiA6ICJQYXRpZW50IiwKICAgICAgICAgICAgImFjY2Vzc0xldmVsIiA6ICJQdWJsaWMiLAogICAgICAgICAgICAidHlwZSIgOiAiRnVuY3Rpb25EZWYiLAogICAgICAgICAgICAiZXhwcmVzc2lvbiIgOiB7CiAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICI5Njo2MS05Njo3MSIsCiAgICAgICAgICAgICAgICJwYXRoIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICJ0eXBlIiA6ICJQcm9wZXJ0eSIsCiAgICAgICAgICAgICAgICJzb3VyY2UiIDogewogICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiOTY6NjEtOTY6NjUiLAogICAgICAgICAgICAgICAgICAibmFtZSIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAgICAidHlwZSIgOiAiT3BlcmFuZFJlZiIKICAgICAgICAgICAgICAgfQogICAgICAgICAgICB9LAogICAgICAgICAgICAib3BlcmFuZCIgOiBbIHsKICAgICAgICAgICAgICAgIm5hbWUiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgIm9wZXJhbmRUeXBlU3BlY2lmaWVyIiA6IHsKICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjk2OjMyLTk2OjU3IiwKICAgICAgICAgICAgICAgICAgIm5hbWUiIDogIntodHRwOi8vaGw3Lm9yZy9maGlyfUNvZGVTeXN0ZW1IaWVyYXJjaHlNZWFuaW5nIiwKICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk5hbWVkVHlwZVNwZWNpZmllciIKICAgICAgICAgICAgICAgfQogICAgICAgICAgICB9IF0KICAgICAgICAgfSwgewogICAgICAgICAgICAibG9jYXRvciIgOiAiOTc6MS05Nzo2NiIsCiAgICAgICAgICAgICJuYW1lIiA6ICJUb1N0cmluZyIsCiAgICAgICAgICAgICJjb250ZXh0IiA6ICJQYXRpZW50IiwKICAgICAgICAgICAgImFjY2Vzc0xldmVsIiA6ICJQdWJsaWMiLAogICAgICAgICAgICAidHlwZSIgOiAiRnVuY3Rpb25EZWYiLAogICAgICAgICAgICAiZXhwcmVzc2lvbiIgOiB7CiAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICI5Nzo1Ni05Nzo2NiIsCiAgICAgICAgICAgICAgICJwYXRoIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICJ0eXBlIiA6ICJQcm9wZXJ0eSIsCiAgICAgICAgICAgICAgICJzb3VyY2UiIDogewogICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiOTc6NTYtOTc6NjAiLAogICAgICAgICAgICAgICAgICAibmFtZSIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAgICAidHlwZSIgOiAiT3BlcmFuZFJlZiIKICAgICAgICAgICAgICAgfQogICAgICAgICAgICB9LAogICAgICAgICAgICAib3BlcmFuZCIgOiBbIHsKICAgICAgICAgICAgICAgIm5hbWUiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgIm9wZXJhbmRUeXBlU3BlY2lmaWVyIiA6IHsKICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjk3OjMyLTk3OjUyIiwKICAgICAgICAgICAgICAgICAgIm5hbWUiIDogIntodHRwOi8vaGw3Lm9yZy9maGlyfUNvbW11bmljYXRpb25Qcmlvcml0eSIsCiAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJOYW1lZFR5cGVTcGVjaWZpZXIiCiAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgfSBdCiAgICAgICAgIH0sIHsKICAgICAgICAgICAgImxvY2F0b3IiIDogIjk4OjEtOTg6NzEiLAogICAgICAgICAgICAibmFtZSIgOiAiVG9TdHJpbmciLAogICAgICAgICAgICAiY29udGV4dCIgOiAiUGF0aWVudCIsCiAgICAgICAgICAgICJhY2Nlc3NMZXZlbCIgOiAiUHVibGljIiwKICAgICAgICAgICAgInR5cGUiIDogIkZ1bmN0aW9uRGVmIiwKICAgICAgICAgICAgImV4cHJlc3Npb24iIDogewogICAgICAgICAgICAgICAibG9jYXRvciIgOiAiOTg6NjEtOTg6NzEiLAogICAgICAgICAgICAgICAicGF0aCIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAidHlwZSIgOiAiUHJvcGVydHkiLAogICAgICAgICAgICAgICAic291cmNlIiA6IHsKICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjk4OjYxLTk4OjY1IiwKICAgICAgICAgICAgICAgICAgIm5hbWUiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk9wZXJhbmRSZWYiCiAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgfSwKICAgICAgICAgICAgIm9wZXJhbmQiIDogWyB7CiAgICAgICAgICAgICAgICJuYW1lIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICJvcGVyYW5kVHlwZVNwZWNpZmllciIgOiB7CiAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICI5ODozMi05ODo1NyIsCiAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJ7aHR0cDovL2hsNy5vcmcvZmhpcn1Db21tdW5pY2F0aW9uUmVxdWVzdFN0YXR1cyIsCiAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJOYW1lZFR5cGVTcGVjaWZpZXIiCiAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgfSBdCiAgICAgICAgIH0sIHsKICAgICAgICAgICAgImxvY2F0b3IiIDogIjk5OjEtOTk6NjQiLAogICAgICAgICAgICAibmFtZSIgOiAiVG9TdHJpbmciLAogICAgICAgICAgICAiY29udGV4dCIgOiAiUGF0aWVudCIsCiAgICAgICAgICAgICJhY2Nlc3NMZXZlbCIgOiAiUHVibGljIiwKICAgICAgICAgICAgInR5cGUiIDogIkZ1bmN0aW9uRGVmIiwKICAgICAgICAgICAgImV4cHJlc3Npb24iIDogewogICAgICAgICAgICAgICAibG9jYXRvciIgOiAiOTk6NTQtOTk6NjQiLAogICAgICAgICAgICAgICAicGF0aCIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAidHlwZSIgOiAiUHJvcGVydHkiLAogICAgICAgICAgICAgICAic291cmNlIiA6IHsKICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjk5OjU0LTk5OjU4IiwKICAgICAgICAgICAgICAgICAgIm5hbWUiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk9wZXJhbmRSZWYiCiAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgfSwKICAgICAgICAgICAgIm9wZXJhbmQiIDogWyB7CiAgICAgICAgICAgICAgICJuYW1lIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICJvcGVyYW5kVHlwZVNwZWNpZmllciIgOiB7CiAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICI5OTozMi05OTo1MCIsCiAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJ7aHR0cDovL2hsNy5vcmcvZmhpcn1Db21tdW5pY2F0aW9uU3RhdHVzIiwKICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk5hbWVkVHlwZVNwZWNpZmllciIKICAgICAgICAgICAgICAgfQogICAgICAgICAgICB9IF0KICAgICAgICAgfSwgewogICAgICAgICAgICAibG9jYXRvciIgOiAiMTAwOjEtMTAwOjYwIiwKICAgICAgICAgICAgIm5hbWUiIDogIlRvU3RyaW5nIiwKICAgICAgICAgICAgImNvbnRleHQiIDogIlBhdGllbnQiLAogICAgICAgICAgICAiYWNjZXNzTGV2ZWwiIDogIlB1YmxpYyIsCiAgICAgICAgICAgICJ0eXBlIiA6ICJGdW5jdGlvbkRlZiIsCiAgICAgICAgICAgICJleHByZXNzaW9uIiA6IHsKICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjEwMDo1MC0xMDA6NjAiLAogICAgICAgICAgICAgICAicGF0aCIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAidHlwZSIgOiAiUHJvcGVydHkiLAogICAgICAgICAgICAgICAic291cmNlIiA6IHsKICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjEwMDo1MC0xMDA6NTQiLAogICAgICAgICAgICAgICAgICAibmFtZSIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAgICAidHlwZSIgOiAiT3BlcmFuZFJlZiIKICAgICAgICAgICAgICAgfQogICAgICAgICAgICB9LAogICAgICAgICAgICAib3BlcmFuZCIgOiBbIHsKICAgICAgICAgICAgICAgIm5hbWUiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgIm9wZXJhbmRUeXBlU3BlY2lmaWVyIiA6IHsKICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjEwMDozMi0xMDA6NDYiLAogICAgICAgICAgICAgICAgICAibmFtZSIgOiAie2h0dHA6Ly9obDcub3JnL2ZoaXJ9Q29tcGFydG1lbnRDb2RlIiwKICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk5hbWVkVHlwZVNwZWNpZmllciIKICAgICAgICAgICAgICAgfQogICAgICAgICAgICB9IF0KICAgICAgICAgfSwgewogICAgICAgICAgICAibG9jYXRvciIgOiAiMTAxOjEtMTAxOjYwIiwKICAgICAgICAgICAgIm5hbWUiIDogIlRvU3RyaW5nIiwKICAgICAgICAgICAgImNvbnRleHQiIDogIlBhdGllbnQiLAogICAgICAgICAgICAiYWNjZXNzTGV2ZWwiIDogIlB1YmxpYyIsCiAgICAgICAgICAgICJ0eXBlIiA6ICJGdW5jdGlvbkRlZiIsCiAgICAgICAgICAgICJleHByZXNzaW9uIiA6IHsKICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjEwMTo1MC0xMDE6NjAiLAogICAgICAgICAgICAgICAicGF0aCIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAidHlwZSIgOiAiUHJvcGVydHkiLAogICAgICAgICAgICAgICAic291cmNlIiA6IHsKICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjEwMTo1MC0xMDE6NTQiLAogICAgICAgICAgICAgICAgICAibmFtZSIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAgICAidHlwZSIgOiAiT3BlcmFuZFJlZiIKICAgICAgICAgICAgICAgfQogICAgICAgICAgICB9LAogICAgICAgICAgICAib3BlcmFuZCIgOiBbIHsKICAgICAgICAgICAgICAgIm5hbWUiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgIm9wZXJhbmRUeXBlU3BlY2lmaWVyIiA6IHsKICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjEwMTozMi0xMDE6NDYiLAogICAgICAgICAgICAgICAgICAibmFtZSIgOiAie2h0dHA6Ly9obDcub3JnL2ZoaXJ9Q29tcGFydG1lbnRUeXBlIiwKICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk5hbWVkVHlwZVNwZWNpZmllciIKICAgICAgICAgICAgICAgfQogICAgICAgICAgICB9IF0KICAgICAgICAgfSwgewogICAgICAgICAgICAibG9jYXRvciIgOiAiMTAyOjEtMTAyOjcxIiwKICAgICAgICAgICAgIm5hbWUiIDogIlRvU3RyaW5nIiwKICAgICAgICAgICAgImNvbnRleHQiIDogIlBhdGllbnQiLAogICAgICAgICAgICAiYWNjZXNzTGV2ZWwiIDogIlB1YmxpYyIsCiAgICAgICAgICAgICJ0eXBlIiA6ICJGdW5jdGlvbkRlZiIsCiAgICAgICAgICAgICJleHByZXNzaW9uIiA6IHsKICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjEwMjo2MS0xMDI6NzEiLAogICAgICAgICAgICAgICAicGF0aCIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAidHlwZSIgOiAiUHJvcGVydHkiLAogICAgICAgICAgICAgICAic291cmNlIiA6IHsKICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjEwMjo2MS0xMDI6NjUiLAogICAgICAgICAgICAgICAgICAibmFtZSIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAgICAidHlwZSIgOiAiT3BlcmFuZFJlZiIKICAgICAgICAgICAgICAgfQogICAgICAgICAgICB9LAogICAgICAgICAgICAib3BlcmFuZCIgOiBbIHsKICAgICAgICAgICAgICAgIm5hbWUiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgIm9wZXJhbmRUeXBlU3BlY2lmaWVyIiA6IHsKICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjEwMjozMi0xMDI6NTciLAogICAgICAgICAgICAgICAgICAibmFtZSIgOiAie2h0dHA6Ly9obDcub3JnL2ZoaXJ9Q29tcG9zaXRpb25BdHRlc3RhdGlvbk1vZGUiLAogICAgICAgICAgICAgICAgICAidHlwZSIgOiAiTmFtZWRUeXBlU3BlY2lmaWVyIgogICAgICAgICAgICAgICB9CiAgICAgICAgICAgIH0gXQogICAgICAgICB9LCB7CiAgICAgICAgICAgICJsb2NhdG9yIiA6ICIxMDM6MS0xMDM6NjIiLAogICAgICAgICAgICAibmFtZSIgOiAiVG9TdHJpbmciLAogICAgICAgICAgICAiY29udGV4dCIgOiAiUGF0aWVudCIsCiAgICAgICAgICAgICJhY2Nlc3NMZXZlbCIgOiAiUHVibGljIiwKICAgICAgICAgICAgInR5cGUiIDogIkZ1bmN0aW9uRGVmIiwKICAgICAgICAgICAgImV4cHJlc3Npb24iIDogewogICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMTAzOjUyLTEwMzo2MiIsCiAgICAgICAgICAgICAgICJwYXRoIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICJ0eXBlIiA6ICJQcm9wZXJ0eSIsCiAgICAgICAgICAgICAgICJzb3VyY2UiIDogewogICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMTAzOjUyLTEwMzo1NiIsCiAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJPcGVyYW5kUmVmIgogICAgICAgICAgICAgICB9CiAgICAgICAgICAgIH0sCiAgICAgICAgICAgICJvcGVyYW5kIiA6IFsgewogICAgICAgICAgICAgICAibmFtZSIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAib3BlcmFuZFR5cGVTcGVjaWZpZXIiIDogewogICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMTAzOjMyLTEwMzo0OCIsCiAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJ7aHR0cDovL2hsNy5vcmcvZmhpcn1Db21wb3NpdGlvblN0YXR1cyIsCiAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJOYW1lZFR5cGVTcGVjaWZpZXIiCiAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgfSBdCiAgICAgICAgIH0sIHsKICAgICAgICAgICAgImxvY2F0b3IiIDogIjEwNDoxLTEwNDo2NiIsCiAgICAgICAgICAgICJuYW1lIiA6ICJUb1N0cmluZyIsCiAgICAgICAgICAgICJjb250ZXh0IiA6ICJQYXRpZW50IiwKICAgICAgICAgICAgImFjY2Vzc0xldmVsIiA6ICJQdWJsaWMiLAogICAgICAgICAgICAidHlwZSIgOiAiRnVuY3Rpb25EZWYiLAogICAgICAgICAgICAiZXhwcmVzc2lvbiIgOiB7CiAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIxMDQ6NTYtMTA0OjY2IiwKICAgICAgICAgICAgICAgInBhdGgiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgInR5cGUiIDogIlByb3BlcnR5IiwKICAgICAgICAgICAgICAgInNvdXJjZSIgOiB7CiAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIxMDQ6NTYtMTA0OjYwIiwKICAgICAgICAgICAgICAgICAgIm5hbWUiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk9wZXJhbmRSZWYiCiAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgfSwKICAgICAgICAgICAgIm9wZXJhbmQiIDogWyB7CiAgICAgICAgICAgICAgICJuYW1lIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICJvcGVyYW5kVHlwZVNwZWNpZmllciIgOiB7CiAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIxMDQ6MzItMTA0OjUyIiwKICAgICAgICAgICAgICAgICAgIm5hbWUiIDogIntodHRwOi8vaGw3Lm9yZy9maGlyfUNvbmNlcHRNYXBFcXVpdmFsZW5jZSIsCiAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJOYW1lZFR5cGVTcGVjaWZpZXIiCiAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgfSBdCiAgICAgICAgIH0sIHsKICAgICAgICAgICAgImxvY2F0b3IiIDogIjEwNToxLTEwNTo3MiIsCiAgICAgICAgICAgICJuYW1lIiA6ICJUb1N0cmluZyIsCiAgICAgICAgICAgICJjb250ZXh0IiA6ICJQYXRpZW50IiwKICAgICAgICAgICAgImFjY2Vzc0xldmVsIiA6ICJQdWJsaWMiLAogICAgICAgICAgICAidHlwZSIgOiAiRnVuY3Rpb25EZWYiLAogICAgICAgICAgICAiZXhwcmVzc2lvbiIgOiB7CiAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIxMDU6NjItMTA1OjcyIiwKICAgICAgICAgICAgICAgInBhdGgiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgInR5cGUiIDogIlByb3BlcnR5IiwKICAgICAgICAgICAgICAgInNvdXJjZSIgOiB7CiAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIxMDU6NjItMTA1OjY2IiwKICAgICAgICAgICAgICAgICAgIm5hbWUiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk9wZXJhbmRSZWYiCiAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgfSwKICAgICAgICAgICAgIm9wZXJhbmQiIDogWyB7CiAgICAgICAgICAgICAgICJuYW1lIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICJvcGVyYW5kVHlwZVNwZWNpZmllciIgOiB7CiAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIxMDU6MzItMTA1OjU4IiwKICAgICAgICAgICAgICAgICAgIm5hbWUiIDogIntodHRwOi8vaGw3Lm9yZy9maGlyfUNvbmNlcHRNYXBHcm91cFVubWFwcGVkTW9kZSIsCiAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJOYW1lZFR5cGVTcGVjaWZpZXIiCiAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgfSBdCiAgICAgICAgIH0sIHsKICAgICAgICAgICAgImxvY2F0b3IiIDogIjEwNjoxLTEwNjo2OCIsCiAgICAgICAgICAgICJuYW1lIiA6ICJUb1N0cmluZyIsCiAgICAgICAgICAgICJjb250ZXh0IiA6ICJQYXRpZW50IiwKICAgICAgICAgICAgImFjY2Vzc0xldmVsIiA6ICJQdWJsaWMiLAogICAgICAgICAgICAidHlwZSIgOiAiRnVuY3Rpb25EZWYiLAogICAgICAgICAgICAiZXhwcmVzc2lvbiIgOiB7CiAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIxMDY6NTgtMTA2OjY4IiwKICAgICAgICAgICAgICAgInBhdGgiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgInR5cGUiIDogIlByb3BlcnR5IiwKICAgICAgICAgICAgICAgInNvdXJjZSIgOiB7CiAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIxMDY6NTgtMTA2OjYyIiwKICAgICAgICAgICAgICAgICAgIm5hbWUiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk9wZXJhbmRSZWYiCiAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgfSwKICAgICAgICAgICAgIm9wZXJhbmQiIDogWyB7CiAgICAgICAgICAgICAgICJuYW1lIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICJvcGVyYW5kVHlwZVNwZWNpZmllciIgOiB7CiAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIxMDY6MzItMTA2OjU0IiwKICAgICAgICAgICAgICAgICAgIm5hbWUiIDogIntodHRwOi8vaGw3Lm9yZy9maGlyfUNvbmRpdGlvbmFsRGVsZXRlU3RhdHVzIiwKICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk5hbWVkVHlwZVNwZWNpZmllciIKICAgICAgICAgICAgICAgfQogICAgICAgICAgICB9IF0KICAgICAgICAgfSwgewogICAgICAgICAgICAibG9jYXRvciIgOiAiMTA3OjEtMTA3OjY2IiwKICAgICAgICAgICAgIm5hbWUiIDogIlRvU3RyaW5nIiwKICAgICAgICAgICAgImNvbnRleHQiIDogIlBhdGllbnQiLAogICAgICAgICAgICAiYWNjZXNzTGV2ZWwiIDogIlB1YmxpYyIsCiAgICAgICAgICAgICJ0eXBlIiA6ICJGdW5jdGlvbkRlZiIsCiAgICAgICAgICAgICJleHByZXNzaW9uIiA6IHsKICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjEwNzo1Ni0xMDc6NjYiLAogICAgICAgICAgICAgICAicGF0aCIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAidHlwZSIgOiAiUHJvcGVydHkiLAogICAgICAgICAgICAgICAic291cmNlIiA6IHsKICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjEwNzo1Ni0xMDc6NjAiLAogICAgICAgICAgICAgICAgICAibmFtZSIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAgICAidHlwZSIgOiAiT3BlcmFuZFJlZiIKICAgICAgICAgICAgICAgfQogICAgICAgICAgICB9LAogICAgICAgICAgICAib3BlcmFuZCIgOiBbIHsKICAgICAgICAgICAgICAgIm5hbWUiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgIm9wZXJhbmRUeXBlU3BlY2lmaWVyIiA6IHsKICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjEwNzozMi0xMDc6NTIiLAogICAgICAgICAgICAgICAgICAibmFtZSIgOiAie2h0dHA6Ly9obDcub3JnL2ZoaXJ9Q29uZGl0aW9uYWxSZWFkU3RhdHVzIiwKICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk5hbWVkVHlwZVNwZWNpZmllciIKICAgICAgICAgICAgICAgfQogICAgICAgICAgICB9IF0KICAgICAgICAgfSwgewogICAgICAgICAgICAibG9jYXRvciIgOiAiMTA4OjEtMTA4OjYzIiwKICAgICAgICAgICAgIm5hbWUiIDogIlRvU3RyaW5nIiwKICAgICAgICAgICAgImNvbnRleHQiIDogIlBhdGllbnQiLAogICAgICAgICAgICAiYWNjZXNzTGV2ZWwiIDogIlB1YmxpYyIsCiAgICAgICAgICAgICJ0eXBlIiA6ICJGdW5jdGlvbkRlZiIsCiAgICAgICAgICAgICJleHByZXNzaW9uIiA6IHsKICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjEwODo1My0xMDg6NjMiLAogICAgICAgICAgICAgICAicGF0aCIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAidHlwZSIgOiAiUHJvcGVydHkiLAogICAgICAgICAgICAgICAic291cmNlIiA6IHsKICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjEwODo1My0xMDg6NTciLAogICAgICAgICAgICAgICAgICAibmFtZSIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAgICAidHlwZSIgOiAiT3BlcmFuZFJlZiIKICAgICAgICAgICAgICAgfQogICAgICAgICAgICB9LAogICAgICAgICAgICAib3BlcmFuZCIgOiBbIHsKICAgICAgICAgICAgICAgIm5hbWUiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgIm9wZXJhbmRUeXBlU3BlY2lmaWVyIiA6IHsKICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjEwODozMi0xMDg6NDkiLAogICAgICAgICAgICAgICAgICAibmFtZSIgOiAie2h0dHA6Ly9obDcub3JnL2ZoaXJ9Q29uc2VudERhdGFNZWFuaW5nIiwKICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk5hbWVkVHlwZVNwZWNpZmllciIKICAgICAgICAgICAgICAgfQogICAgICAgICAgICB9IF0KICAgICAgICAgfSwgewogICAgICAgICAgICAibG9jYXRvciIgOiAiMTA5OjEtMTA5OjY1IiwKICAgICAgICAgICAgIm5hbWUiIDogIlRvU3RyaW5nIiwKICAgICAgICAgICAgImNvbnRleHQiIDogIlBhdGllbnQiLAogICAgICAgICAgICAiYWNjZXNzTGV2ZWwiIDogIlB1YmxpYyIsCiAgICAgICAgICAgICJ0eXBlIiA6ICJGdW5jdGlvbkRlZiIsCiAgICAgICAgICAgICJleHByZXNzaW9uIiA6IHsKICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjEwOTo1NS0xMDk6NjUiLAogICAgICAgICAgICAgICAicGF0aCIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAidHlwZSIgOiAiUHJvcGVydHkiLAogICAgICAgICAgICAgICAic291cmNlIiA6IHsKICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjEwOTo1NS0xMDk6NTkiLAogICAgICAgICAgICAgICAgICAibmFtZSIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAgICAidHlwZSIgOiAiT3BlcmFuZFJlZiIKICAgICAgICAgICAgICAgfQogICAgICAgICAgICB9LAogICAgICAgICAgICAib3BlcmFuZCIgOiBbIHsKICAgICAgICAgICAgICAgIm5hbWUiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgIm9wZXJhbmRUeXBlU3BlY2lmaWVyIiA6IHsKICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjEwOTozMi0xMDk6NTEiLAogICAgICAgICAgICAgICAgICAibmFtZSIgOiAie2h0dHA6Ly9obDcub3JnL2ZoaXJ9Q29uc2VudFByb3Zpc2lvblR5cGUiLAogICAgICAgICAgICAgICAgICAidHlwZSIgOiAiTmFtZWRUeXBlU3BlY2lmaWVyIgogICAgICAgICAgICAgICB9CiAgICAgICAgICAgIH0gXQogICAgICAgICB9LCB7CiAgICAgICAgICAgICJsb2NhdG9yIiA6ICIxMTA6MS0xMTA6NTciLAogICAgICAgICAgICAibmFtZSIgOiAiVG9TdHJpbmciLAogICAgICAgICAgICAiY29udGV4dCIgOiAiUGF0aWVudCIsCiAgICAgICAgICAgICJhY2Nlc3NMZXZlbCIgOiAiUHVibGljIiwKICAgICAgICAgICAgInR5cGUiIDogIkZ1bmN0aW9uRGVmIiwKICAgICAgICAgICAgImV4cHJlc3Npb24iIDogewogICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMTEwOjQ3LTExMDo1NyIsCiAgICAgICAgICAgICAgICJwYXRoIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICJ0eXBlIiA6ICJQcm9wZXJ0eSIsCiAgICAgICAgICAgICAgICJzb3VyY2UiIDogewogICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMTEwOjQ3LTExMDo1MSIsCiAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJPcGVyYW5kUmVmIgogICAgICAgICAgICAgICB9CiAgICAgICAgICAgIH0sCiAgICAgICAgICAgICJvcGVyYW5kIiA6IFsgewogICAgICAgICAgICAgICAibmFtZSIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAib3BlcmFuZFR5cGVTcGVjaWZpZXIiIDogewogICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMTEwOjMyLTExMDo0MyIsCiAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJ7aHR0cDovL2hsNy5vcmcvZmhpcn1Db25zZW50U3RhdGUiLAogICAgICAgICAgICAgICAgICAidHlwZSIgOiAiTmFtZWRUeXBlU3BlY2lmaWVyIgogICAgICAgICAgICAgICB9CiAgICAgICAgICAgIH0gXQogICAgICAgICB9LCB7CiAgICAgICAgICAgICJsb2NhdG9yIiA6ICIxMTE6MS0xMTE6NjMiLAogICAgICAgICAgICAibmFtZSIgOiAiVG9TdHJpbmciLAogICAgICAgICAgICAiY29udGV4dCIgOiAiUGF0aWVudCIsCiAgICAgICAgICAgICJhY2Nlc3NMZXZlbCIgOiAiUHVibGljIiwKICAgICAgICAgICAgInR5cGUiIDogIkZ1bmN0aW9uRGVmIiwKICAgICAgICAgICAgImV4cHJlc3Npb24iIDogewogICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMTExOjUzLTExMTo2MyIsCiAgICAgICAgICAgICAgICJwYXRoIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICJ0eXBlIiA6ICJQcm9wZXJ0eSIsCiAgICAgICAgICAgICAgICJzb3VyY2UiIDogewogICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMTExOjUzLTExMTo1NyIsCiAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJPcGVyYW5kUmVmIgogICAgICAgICAgICAgICB9CiAgICAgICAgICAgIH0sCiAgICAgICAgICAgICJvcGVyYW5kIiA6IFsgewogICAgICAgICAgICAgICAibmFtZSIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAib3BlcmFuZFR5cGVTcGVjaWZpZXIiIDogewogICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMTExOjMyLTExMTo0OSIsCiAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJ7aHR0cDovL2hsNy5vcmcvZmhpcn1Db25zdHJhaW50U2V2ZXJpdHkiLAogICAgICAgICAgICAgICAgICAidHlwZSIgOiAiTmFtZWRUeXBlU3BlY2lmaWVyIgogICAgICAgICAgICAgICB9CiAgICAgICAgICAgIH0gXQogICAgICAgICB9LCB7CiAgICAgICAgICAgICJsb2NhdG9yIiA6ICIxMTI6MS0xMTI6NjMiLAogICAgICAgICAgICAibmFtZSIgOiAiVG9TdHJpbmciLAogICAgICAgICAgICAiY29udGV4dCIgOiAiUGF0aWVudCIsCiAgICAgICAgICAgICJhY2Nlc3NMZXZlbCIgOiAiUHVibGljIiwKICAgICAgICAgICAgInR5cGUiIDogIkZ1bmN0aW9uRGVmIiwKICAgICAgICAgICAgImV4cHJlc3Npb24iIDogewogICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMTEyOjUzLTExMjo2MyIsCiAgICAgICAgICAgICAgICJwYXRoIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICJ0eXBlIiA6ICJQcm9wZXJ0eSIsCiAgICAgICAgICAgICAgICJzb3VyY2UiIDogewogICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMTEyOjUzLTExMjo1NyIsCiAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJPcGVyYW5kUmVmIgogICAgICAgICAgICAgICB9CiAgICAgICAgICAgIH0sCiAgICAgICAgICAgICJvcGVyYW5kIiA6IFsgewogICAgICAgICAgICAgICAibmFtZSIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAib3BlcmFuZFR5cGVTcGVjaWZpZXIiIDogewogICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMTEyOjMyLTExMjo0OSIsCiAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJ7aHR0cDovL2hsNy5vcmcvZmhpcn1Db250YWN0UG9pbnRTeXN0ZW0iLAogICAgICAgICAgICAgICAgICAidHlwZSIgOiAiTmFtZWRUeXBlU3BlY2lmaWVyIgogICAgICAgICAgICAgICB9CiAgICAgICAgICAgIH0gXQogICAgICAgICB9LCB7CiAgICAgICAgICAgICJsb2NhdG9yIiA6ICIxMTM6MS0xMTM6NjAiLAogICAgICAgICAgICAibmFtZSIgOiAiVG9TdHJpbmciLAogICAgICAgICAgICAiY29udGV4dCIgOiAiUGF0aWVudCIsCiAgICAgICAgICAgICJhY2Nlc3NMZXZlbCIgOiAiUHVibGljIiwKICAgICAgICAgICAgInR5cGUiIDogIkZ1bmN0aW9uRGVmIiwKICAgICAgICAgICAgImV4cHJlc3Npb24iIDogewogICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMTEzOjUwLTExMzo2MCIsCiAgICAgICAgICAgICAgICJwYXRoIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICJ0eXBlIiA6ICJQcm9wZXJ0eSIsCiAgICAgICAgICAgICAgICJzb3VyY2UiIDogewogICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMTEzOjUwLTExMzo1NCIsCiAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJPcGVyYW5kUmVmIgogICAgICAgICAgICAgICB9CiAgICAgICAgICAgIH0sCiAgICAgICAgICAgICJvcGVyYW5kIiA6IFsgewogICAgICAgICAgICAgICAibmFtZSIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAib3BlcmFuZFR5cGVTcGVjaWZpZXIiIDogewogICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMTEzOjMyLTExMzo0NiIsCiAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJ7aHR0cDovL2hsNy5vcmcvZmhpcn1Db250YWN0UG9pbnRVc2UiLAogICAgICAgICAgICAgICAgICAidHlwZSIgOiAiTmFtZWRUeXBlU3BlY2lmaWVyIgogICAgICAgICAgICAgICB9CiAgICAgICAgICAgIH0gXQogICAgICAgICB9LCB7CiAgICAgICAgICAgICJsb2NhdG9yIiA6ICIxMTQ6MS0xMTQ6NzAiLAogICAgICAgICAgICAibmFtZSIgOiAiVG9TdHJpbmciLAogICAgICAgICAgICAiY29udGV4dCIgOiAiUGF0aWVudCIsCiAgICAgICAgICAgICJhY2Nlc3NMZXZlbCIgOiAiUHVibGljIiwKICAgICAgICAgICAgInR5cGUiIDogIkZ1bmN0aW9uRGVmIiwKICAgICAgICAgICAgImV4cHJlc3Npb24iIDogewogICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMTE0OjYwLTExNDo3MCIsCiAgICAgICAgICAgICAgICJwYXRoIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICJ0eXBlIiA6ICJQcm9wZXJ0eSIsCiAgICAgICAgICAgICAgICJzb3VyY2UiIDogewogICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMTE0OjYwLTExNDo2NCIsCiAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJPcGVyYW5kUmVmIgogICAgICAgICAgICAgICB9CiAgICAgICAgICAgIH0sCiAgICAgICAgICAgICJvcGVyYW5kIiA6IFsgewogICAgICAgICAgICAgICAibmFtZSIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAib3BlcmFuZFR5cGVTcGVjaWZpZXIiIDogewogICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMTE0OjMyLTExNDo1NiIsCiAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJ7aHR0cDovL2hsNy5vcmcvZmhpcn1Db250cmFjdFB1YmxpY2F0aW9uU3RhdHVzIiwKICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk5hbWVkVHlwZVNwZWNpZmllciIKICAgICAgICAgICAgICAgfQogICAgICAgICAgICB9IF0KICAgICAgICAgfSwgewogICAgICAgICAgICAibG9jYXRvciIgOiAiMTE1OjEtMTE1OjU5IiwKICAgICAgICAgICAgIm5hbWUiIDogIlRvU3RyaW5nIiwKICAgICAgICAgICAgImNvbnRleHQiIDogIlBhdGllbnQiLAogICAgICAgICAgICAiYWNjZXNzTGV2ZWwiIDogIlB1YmxpYyIsCiAgICAgICAgICAgICJ0eXBlIiA6ICJGdW5jdGlvbkRlZiIsCiAgICAgICAgICAgICJleHByZXNzaW9uIiA6IHsKICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjExNTo0OS0xMTU6NTkiLAogICAgICAgICAgICAgICAicGF0aCIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAidHlwZSIgOiAiUHJvcGVydHkiLAogICAgICAgICAgICAgICAic291cmNlIiA6IHsKICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjExNTo0OS0xMTU6NTMiLAogICAgICAgICAgICAgICAgICAibmFtZSIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAgICAidHlwZSIgOiAiT3BlcmFuZFJlZiIKICAgICAgICAgICAgICAgfQogICAgICAgICAgICB9LAogICAgICAgICAgICAib3BlcmFuZCIgOiBbIHsKICAgICAgICAgICAgICAgIm5hbWUiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgIm9wZXJhbmRUeXBlU3BlY2lmaWVyIiA6IHsKICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjExNTozMi0xMTU6NDUiLAogICAgICAgICAgICAgICAgICAibmFtZSIgOiAie2h0dHA6Ly9obDcub3JnL2ZoaXJ9Q29udHJhY3RTdGF0dXMiLAogICAgICAgICAgICAgICAgICAidHlwZSIgOiAiTmFtZWRUeXBlU3BlY2lmaWVyIgogICAgICAgICAgICAgICB9CiAgICAgICAgICAgIH0gXQogICAgICAgICB9LCB7CiAgICAgICAgICAgICJsb2NhdG9yIiA6ICIxMTY6MS0xMTY6NjAiLAogICAgICAgICAgICAibmFtZSIgOiAiVG9TdHJpbmciLAogICAgICAgICAgICAiY29udGV4dCIgOiAiUGF0aWVudCIsCiAgICAgICAgICAgICJhY2Nlc3NMZXZlbCIgOiAiUHVibGljIiwKICAgICAgICAgICAgInR5cGUiIDogIkZ1bmN0aW9uRGVmIiwKICAgICAgICAgICAgImV4cHJlc3Npb24iIDogewogICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMTE2OjUwLTExNjo2MCIsCiAgICAgICAgICAgICAgICJwYXRoIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICJ0eXBlIiA6ICJQcm9wZXJ0eSIsCiAgICAgICAgICAgICAgICJzb3VyY2UiIDogewogICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMTE2OjUwLTExNjo1NCIsCiAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJPcGVyYW5kUmVmIgogICAgICAgICAgICAgICB9CiAgICAgICAgICAgIH0sCiAgICAgICAgICAgICJvcGVyYW5kIiA6IFsgewogICAgICAgICAgICAgICAibmFtZSIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAib3BlcmFuZFR5cGVTcGVjaWZpZXIiIDogewogICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMTE2OjMyLTExNjo0NiIsCiAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJ7aHR0cDovL2hsNy5vcmcvZmhpcn1Db250cmlidXRvclR5cGUiLAogICAgICAgICAgICAgICAgICAidHlwZSIgOiAiTmFtZWRUeXBlU3BlY2lmaWVyIgogICAgICAgICAgICAgICB9CiAgICAgICAgICAgIH0gXQogICAgICAgICB9LCB7CiAgICAgICAgICAgICJsb2NhdG9yIiA6ICIxMTc6MS0xMTc6NTkiLAogICAgICAgICAgICAibmFtZSIgOiAiVG9TdHJpbmciLAogICAgICAgICAgICAiY29udGV4dCIgOiAiUGF0aWVudCIsCiAgICAgICAgICAgICJhY2Nlc3NMZXZlbCIgOiAiUHVibGljIiwKICAgICAgICAgICAgInR5cGUiIDogIkZ1bmN0aW9uRGVmIiwKICAgICAgICAgICAgImV4cHJlc3Npb24iIDogewogICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMTE3OjQ5LTExNzo1OSIsCiAgICAgICAgICAgICAgICJwYXRoIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICJ0eXBlIiA6ICJQcm9wZXJ0eSIsCiAgICAgICAgICAgICAgICJzb3VyY2UiIDogewogICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMTE3OjQ5LTExNzo1MyIsCiAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJPcGVyYW5kUmVmIgogICAgICAgICAgICAgICB9CiAgICAgICAgICAgIH0sCiAgICAgICAgICAgICJvcGVyYW5kIiA6IFsgewogICAgICAgICAgICAgICAibmFtZSIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAib3BlcmFuZFR5cGVTcGVjaWZpZXIiIDogewogICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMTE3OjMyLTExNzo0NSIsCiAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJ7aHR0cDovL2hsNy5vcmcvZmhpcn1Db3ZlcmFnZVN0YXR1cyIsCiAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJOYW1lZFR5cGVTcGVjaWZpZXIiCiAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgfSBdCiAgICAgICAgIH0sIHsKICAgICAgICAgICAgImxvY2F0b3IiIDogIjExODoxLTExODo1NyIsCiAgICAgICAgICAgICJuYW1lIiA6ICJUb1N0cmluZyIsCiAgICAgICAgICAgICJjb250ZXh0IiA6ICJQYXRpZW50IiwKICAgICAgICAgICAgImFjY2Vzc0xldmVsIiA6ICJQdWJsaWMiLAogICAgICAgICAgICAidHlwZSIgOiAiRnVuY3Rpb25EZWYiLAogICAgICAgICAgICAiZXhwcmVzc2lvbiIgOiB7CiAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIxMTg6NDctMTE4OjU3IiwKICAgICAgICAgICAgICAgInBhdGgiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgInR5cGUiIDogIlByb3BlcnR5IiwKICAgICAgICAgICAgICAgInNvdXJjZSIgOiB7CiAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIxMTg6NDctMTE4OjUxIiwKICAgICAgICAgICAgICAgICAgIm5hbWUiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk9wZXJhbmRSZWYiCiAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgfSwKICAgICAgICAgICAgIm9wZXJhbmQiIDogWyB7CiAgICAgICAgICAgICAgICJuYW1lIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICJvcGVyYW5kVHlwZVNwZWNpZmllciIgOiB7CiAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIxMTg6MzItMTE4OjQzIiwKICAgICAgICAgICAgICAgICAgIm5hbWUiIDogIntodHRwOi8vaGw3Lm9yZy9maGlyfUN1cnJlbmN5Q29kZSIsCiAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJOYW1lZFR5cGVTcGVjaWZpZXIiCiAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgfSBdCiAgICAgICAgIH0sIHsKICAgICAgICAgICAgImxvY2F0b3IiIDogIjExOToxLTExOTo1NCIsCiAgICAgICAgICAgICJuYW1lIiA6ICJUb1N0cmluZyIsCiAgICAgICAgICAgICJjb250ZXh0IiA6ICJQYXRpZW50IiwKICAgICAgICAgICAgImFjY2Vzc0xldmVsIiA6ICJQdWJsaWMiLAogICAgICAgICAgICAidHlwZSIgOiAiRnVuY3Rpb25EZWYiLAogICAgICAgICAgICAiZXhwcmVzc2lvbiIgOiB7CiAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIxMTk6NDQtMTE5OjU0IiwKICAgICAgICAgICAgICAgInBhdGgiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgInR5cGUiIDogIlByb3BlcnR5IiwKICAgICAgICAgICAgICAgInNvdXJjZSIgOiB7CiAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIxMTk6NDQtMTE5OjQ4IiwKICAgICAgICAgICAgICAgICAgIm5hbWUiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk9wZXJhbmRSZWYiCiAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgfSwKICAgICAgICAgICAgIm9wZXJhbmQiIDogWyB7CiAgICAgICAgICAgICAgICJuYW1lIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICJvcGVyYW5kVHlwZVNwZWNpZmllciIgOiB7CiAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIxMTk6MzItMTE5OjQwIiwKICAgICAgICAgICAgICAgICAgIm5hbWUiIDogIntodHRwOi8vaGw3Lm9yZy9maGlyfURheU9mV2VlayIsCiAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJOYW1lZFR5cGVTcGVjaWZpZXIiCiAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgfSBdCiAgICAgICAgIH0sIHsKICAgICAgICAgICAgImxvY2F0b3IiIDogIjEyMDoxLTEyMDo1NSIsCiAgICAgICAgICAgICJuYW1lIiA6ICJUb1N0cmluZyIsCiAgICAgICAgICAgICJjb250ZXh0IiA6ICJQYXRpZW50IiwKICAgICAgICAgICAgImFjY2Vzc0xldmVsIiA6ICJQdWJsaWMiLAogICAgICAgICAgICAidHlwZSIgOiAiRnVuY3Rpb25EZWYiLAogICAgICAgICAgICAiZXhwcmVzc2lvbiIgOiB7CiAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIxMjA6NDUtMTIwOjU1IiwKICAgICAgICAgICAgICAgInBhdGgiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgInR5cGUiIDogIlByb3BlcnR5IiwKICAgICAgICAgICAgICAgInNvdXJjZSIgOiB7CiAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIxMjA6NDUtMTIwOjQ5IiwKICAgICAgICAgICAgICAgICAgIm5hbWUiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk9wZXJhbmRSZWYiCiAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgfSwKICAgICAgICAgICAgIm9wZXJhbmQiIDogWyB7CiAgICAgICAgICAgICAgICJuYW1lIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICJvcGVyYW5kVHlwZVNwZWNpZmllciIgOiB7CiAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIxMjA6MzItMTIwOjQxIiwKICAgICAgICAgICAgICAgICAgIm5hbWUiIDogIntodHRwOi8vaGw3Lm9yZy9maGlyfURheXNPZldlZWsiLAogICAgICAgICAgICAgICAgICAidHlwZSIgOiAiTmFtZWRUeXBlU3BlY2lmaWVyIgogICAgICAgICAgICAgICB9CiAgICAgICAgICAgIH0gXQogICAgICAgICB9LCB7CiAgICAgICAgICAgICJsb2NhdG9yIiA6ICIxMjE6MS0xMjE6NjYiLAogICAgICAgICAgICAibmFtZSIgOiAiVG9TdHJpbmciLAogICAgICAgICAgICAiY29udGV4dCIgOiAiUGF0aWVudCIsCiAgICAgICAgICAgICJhY2Nlc3NMZXZlbCIgOiAiUHVibGljIiwKICAgICAgICAgICAgInR5cGUiIDogIkZ1bmN0aW9uRGVmIiwKICAgICAgICAgICAgImV4cHJlc3Npb24iIDogewogICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMTIxOjU2LTEyMTo2NiIsCiAgICAgICAgICAgICAgICJwYXRoIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICJ0eXBlIiA6ICJQcm9wZXJ0eSIsCiAgICAgICAgICAgICAgICJzb3VyY2UiIDogewogICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMTIxOjU2LTEyMTo2MCIsCiAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJPcGVyYW5kUmVmIgogICAgICAgICAgICAgICB9CiAgICAgICAgICAgIH0sCiAgICAgICAgICAgICJvcGVyYW5kIiA6IFsgewogICAgICAgICAgICAgICAibmFtZSIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAib3BlcmFuZFR5cGVTcGVjaWZpZXIiIDogewogICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMTIxOjMyLTEyMTo1MiIsCiAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJ7aHR0cDovL2hsNy5vcmcvZmhpcn1EZXRlY3RlZElzc3VlU2V2ZXJpdHkiLAogICAgICAgICAgICAgICAgICAidHlwZSIgOiAiTmFtZWRUeXBlU3BlY2lmaWVyIgogICAgICAgICAgICAgICB9CiAgICAgICAgICAgIH0gXQogICAgICAgICB9LCB7CiAgICAgICAgICAgICJsb2NhdG9yIiA6ICIxMjI6MS0xMjI6NjQiLAogICAgICAgICAgICAibmFtZSIgOiAiVG9TdHJpbmciLAogICAgICAgICAgICAiY29udGV4dCIgOiAiUGF0aWVudCIsCiAgICAgICAgICAgICJhY2Nlc3NMZXZlbCIgOiAiUHVibGljIiwKICAgICAgICAgICAgInR5cGUiIDogIkZ1bmN0aW9uRGVmIiwKICAgICAgICAgICAgImV4cHJlc3Npb24iIDogewogICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMTIyOjU0LTEyMjo2NCIsCiAgICAgICAgICAgICAgICJwYXRoIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICJ0eXBlIiA6ICJQcm9wZXJ0eSIsCiAgICAgICAgICAgICAgICJzb3VyY2UiIDogewogICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMTIyOjU0LTEyMjo1OCIsCiAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJPcGVyYW5kUmVmIgogICAgICAgICAgICAgICB9CiAgICAgICAgICAgIH0sCiAgICAgICAgICAgICJvcGVyYW5kIiA6IFsgewogICAgICAgICAgICAgICAibmFtZSIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAib3BlcmFuZFR5cGVTcGVjaWZpZXIiIDogewogICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMTIyOjMyLTEyMjo1MCIsCiAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJ7aHR0cDovL2hsNy5vcmcvZmhpcn1EZXRlY3RlZElzc3VlU3RhdHVzIiwKICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk5hbWVkVHlwZVNwZWNpZmllciIKICAgICAgICAgICAgICAgfQogICAgICAgICAgICB9IF0KICAgICAgICAgfSwgewogICAgICAgICAgICAibG9jYXRvciIgOiAiMTIzOjEtMTIzOjczIiwKICAgICAgICAgICAgIm5hbWUiIDogIlRvU3RyaW5nIiwKICAgICAgICAgICAgImNvbnRleHQiIDogIlBhdGllbnQiLAogICAgICAgICAgICAiYWNjZXNzTGV2ZWwiIDogIlB1YmxpYyIsCiAgICAgICAgICAgICJ0eXBlIiA6ICJGdW5jdGlvbkRlZiIsCiAgICAgICAgICAgICJleHByZXNzaW9uIiA6IHsKICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjEyMzo2My0xMjM6NzMiLAogICAgICAgICAgICAgICAicGF0aCIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAidHlwZSIgOiAiUHJvcGVydHkiLAogICAgICAgICAgICAgICAic291cmNlIiA6IHsKICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjEyMzo2My0xMjM6NjciLAogICAgICAgICAgICAgICAgICAibmFtZSIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAgICAidHlwZSIgOiAiT3BlcmFuZFJlZiIKICAgICAgICAgICAgICAgfQogICAgICAgICAgICB9LAogICAgICAgICAgICAib3BlcmFuZCIgOiBbIHsKICAgICAgICAgICAgICAgIm5hbWUiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgIm9wZXJhbmRUeXBlU3BlY2lmaWVyIiA6IHsKICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjEyMzozMi0xMjM6NTkiLAogICAgICAgICAgICAgICAgICAibmFtZSIgOiAie2h0dHA6Ly9obDcub3JnL2ZoaXJ9RGV2aWNlTWV0cmljQ2FsaWJyYXRpb25TdGF0ZSIsCiAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJOYW1lZFR5cGVTcGVjaWZpZXIiCiAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgfSBdCiAgICAgICAgIH0sIHsKICAgICAgICAgICAgImxvY2F0b3IiIDogIjEyNDoxLTEyNDo3MiIsCiAgICAgICAgICAgICJuYW1lIiA6ICJUb1N0cmluZyIsCiAgICAgICAgICAgICJjb250ZXh0IiA6ICJQYXRpZW50IiwKICAgICAgICAgICAgImFjY2Vzc0xldmVsIiA6ICJQdWJsaWMiLAogICAgICAgICAgICAidHlwZSIgOiAiRnVuY3Rpb25EZWYiLAogICAgICAgICAgICAiZXhwcmVzc2lvbiIgOiB7CiAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIxMjQ6NjItMTI0OjcyIiwKICAgICAgICAgICAgICAgInBhdGgiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgInR5cGUiIDogIlByb3BlcnR5IiwKICAgICAgICAgICAgICAgInNvdXJjZSIgOiB7CiAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIxMjQ6NjItMTI0OjY2IiwKICAgICAgICAgICAgICAgICAgIm5hbWUiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk9wZXJhbmRSZWYiCiAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgfSwKICAgICAgICAgICAgIm9wZXJhbmQiIDogWyB7CiAgICAgICAgICAgICAgICJuYW1lIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICJvcGVyYW5kVHlwZVNwZWNpZmllciIgOiB7CiAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIxMjQ6MzItMTI0OjU4IiwKICAgICAgICAgICAgICAgICAgIm5hbWUiIDogIntodHRwOi8vaGw3Lm9yZy9maGlyfURldmljZU1ldHJpY0NhbGlicmF0aW9uVHlwZSIsCiAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJOYW1lZFR5cGVTcGVjaWZpZXIiCiAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgfSBdCiAgICAgICAgIH0sIHsKICAgICAgICAgICAgImxvY2F0b3IiIDogIjEyNToxLTEyNTo2NSIsCiAgICAgICAgICAgICJuYW1lIiA6ICJUb1N0cmluZyIsCiAgICAgICAgICAgICJjb250ZXh0IiA6ICJQYXRpZW50IiwKICAgICAgICAgICAgImFjY2Vzc0xldmVsIiA6ICJQdWJsaWMiLAogICAgICAgICAgICAidHlwZSIgOiAiRnVuY3Rpb25EZWYiLAogICAgICAgICAgICAiZXhwcmVzc2lvbiIgOiB7CiAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIxMjU6NTUtMTI1OjY1IiwKICAgICAgICAgICAgICAgInBhdGgiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgInR5cGUiIDogIlByb3BlcnR5IiwKICAgICAgICAgICAgICAgInNvdXJjZSIgOiB7CiAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIxMjU6NTUtMTI1OjU5IiwKICAgICAgICAgICAgICAgICAgIm5hbWUiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk9wZXJhbmRSZWYiCiAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgfSwKICAgICAgICAgICAgIm9wZXJhbmQiIDogWyB7CiAgICAgICAgICAgICAgICJuYW1lIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICJvcGVyYW5kVHlwZVNwZWNpZmllciIgOiB7CiAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIxMjU6MzItMTI1OjUxIiwKICAgICAgICAgICAgICAgICAgIm5hbWUiIDogIntodHRwOi8vaGw3Lm9yZy9maGlyfURldmljZU1ldHJpY0NhdGVnb3J5IiwKICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk5hbWVkVHlwZVNwZWNpZmllciIKICAgICAgICAgICAgICAgfQogICAgICAgICAgICB9IF0KICAgICAgICAgfSwgewogICAgICAgICAgICAibG9jYXRvciIgOiAiMTI2OjEtMTI2OjYyIiwKICAgICAgICAgICAgIm5hbWUiIDogIlRvU3RyaW5nIiwKICAgICAgICAgICAgImNvbnRleHQiIDogIlBhdGllbnQiLAogICAgICAgICAgICAiYWNjZXNzTGV2ZWwiIDogIlB1YmxpYyIsCiAgICAgICAgICAgICJ0eXBlIiA6ICJGdW5jdGlvbkRlZiIsCiAgICAgICAgICAgICJleHByZXNzaW9uIiA6IHsKICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjEyNjo1Mi0xMjY6NjIiLAogICAgICAgICAgICAgICAicGF0aCIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAidHlwZSIgOiAiUHJvcGVydHkiLAogICAgICAgICAgICAgICAic291cmNlIiA6IHsKICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjEyNjo1Mi0xMjY6NTYiLAogICAgICAgICAgICAgICAgICAibmFtZSIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAgICAidHlwZSIgOiAiT3BlcmFuZFJlZiIKICAgICAgICAgICAgICAgfQogICAgICAgICAgICB9LAogICAgICAgICAgICAib3BlcmFuZCIgOiBbIHsKICAgICAgICAgICAgICAgIm5hbWUiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgIm9wZXJhbmRUeXBlU3BlY2lmaWVyIiA6IHsKICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjEyNjozMi0xMjY6NDgiLAogICAgICAgICAgICAgICAgICAibmFtZSIgOiAie2h0dHA6Ly9obDcub3JnL2ZoaXJ9RGV2aWNlTWV0cmljQ29sb3IiLAogICAgICAgICAgICAgICAgICAidHlwZSIgOiAiTmFtZWRUeXBlU3BlY2lmaWVyIgogICAgICAgICAgICAgICB9CiAgICAgICAgICAgIH0gXQogICAgICAgICB9LCB7CiAgICAgICAgICAgICJsb2NhdG9yIiA6ICIxMjc6MS0xMjc6NzQiLAogICAgICAgICAgICAibmFtZSIgOiAiVG9TdHJpbmciLAogICAgICAgICAgICAiY29udGV4dCIgOiAiUGF0aWVudCIsCiAgICAgICAgICAgICJhY2Nlc3NMZXZlbCIgOiAiUHVibGljIiwKICAgICAgICAgICAgInR5cGUiIDogIkZ1bmN0aW9uRGVmIiwKICAgICAgICAgICAgImV4cHJlc3Npb24iIDogewogICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMTI3OjY0LTEyNzo3NCIsCiAgICAgICAgICAgICAgICJwYXRoIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICJ0eXBlIiA6ICJQcm9wZXJ0eSIsCiAgICAgICAgICAgICAgICJzb3VyY2UiIDogewogICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMTI3OjY0LTEyNzo2OCIsCiAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJPcGVyYW5kUmVmIgogICAgICAgICAgICAgICB9CiAgICAgICAgICAgIH0sCiAgICAgICAgICAgICJvcGVyYW5kIiA6IFsgewogICAgICAgICAgICAgICAibmFtZSIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAib3BlcmFuZFR5cGVTcGVjaWZpZXIiIDogewogICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMTI3OjMyLTEyNzo2MCIsCiAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJ7aHR0cDovL2hsNy5vcmcvZmhpcn1EZXZpY2VNZXRyaWNPcGVyYXRpb25hbFN0YXR1cyIsCiAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJOYW1lZFR5cGVTcGVjaWZpZXIiCiAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgfSBdCiAgICAgICAgIH0sIHsKICAgICAgICAgICAgImxvY2F0b3IiIDogIjEyODoxLTEyODo1OSIsCiAgICAgICAgICAgICJuYW1lIiA6ICJUb1N0cmluZyIsCiAgICAgICAgICAgICJjb250ZXh0IiA6ICJQYXRpZW50IiwKICAgICAgICAgICAgImFjY2Vzc0xldmVsIiA6ICJQdWJsaWMiLAogICAgICAgICAgICAidHlwZSIgOiAiRnVuY3Rpb25EZWYiLAogICAgICAgICAgICAiZXhwcmVzc2lvbiIgOiB7CiAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIxMjg6NDktMTI4OjU5IiwKICAgICAgICAgICAgICAgInBhdGgiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgInR5cGUiIDogIlByb3BlcnR5IiwKICAgICAgICAgICAgICAgInNvdXJjZSIgOiB7CiAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIxMjg6NDktMTI4OjUzIiwKICAgICAgICAgICAgICAgICAgIm5hbWUiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk9wZXJhbmRSZWYiCiAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgfSwKICAgICAgICAgICAgIm9wZXJhbmQiIDogWyB7CiAgICAgICAgICAgICAgICJuYW1lIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICJvcGVyYW5kVHlwZVNwZWNpZmllciIgOiB7CiAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIxMjg6MzItMTI4OjQ1IiwKICAgICAgICAgICAgICAgICAgIm5hbWUiIDogIntodHRwOi8vaGw3Lm9yZy9maGlyfURldmljZU5hbWVUeXBlIiwKICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk5hbWVkVHlwZVNwZWNpZmllciIKICAgICAgICAgICAgICAgfQogICAgICAgICAgICB9IF0KICAgICAgICAgfSwgewogICAgICAgICAgICAibG9jYXRvciIgOiAiMTI5OjEtMTI5OjY0IiwKICAgICAgICAgICAgIm5hbWUiIDogIlRvU3RyaW5nIiwKICAgICAgICAgICAgImNvbnRleHQiIDogIlBhdGllbnQiLAogICAgICAgICAgICAiYWNjZXNzTGV2ZWwiIDogIlB1YmxpYyIsCiAgICAgICAgICAgICJ0eXBlIiA6ICJGdW5jdGlvbkRlZiIsCiAgICAgICAgICAgICJleHByZXNzaW9uIiA6IHsKICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjEyOTo1NC0xMjk6NjQiLAogICAgICAgICAgICAgICAicGF0aCIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAidHlwZSIgOiAiUHJvcGVydHkiLAogICAgICAgICAgICAgICAic291cmNlIiA6IHsKICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjEyOTo1NC0xMjk6NTgiLAogICAgICAgICAgICAgICAgICAibmFtZSIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAgICAidHlwZSIgOiAiT3BlcmFuZFJlZiIKICAgICAgICAgICAgICAgfQogICAgICAgICAgICB9LAogICAgICAgICAgICAib3BlcmFuZCIgOiBbIHsKICAgICAgICAgICAgICAgIm5hbWUiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgIm9wZXJhbmRUeXBlU3BlY2lmaWVyIiA6IHsKICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjEyOTozMi0xMjk6NTAiLAogICAgICAgICAgICAgICAgICAibmFtZSIgOiAie2h0dHA6Ly9obDcub3JnL2ZoaXJ9RGV2aWNlUmVxdWVzdFN0YXR1cyIsCiAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJOYW1lZFR5cGVTcGVjaWZpZXIiCiAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgfSBdCiAgICAgICAgIH0sIHsKICAgICAgICAgICAgImxvY2F0b3IiIDogIjEzMDoxLTEzMDo2OSIsCiAgICAgICAgICAgICJuYW1lIiA6ICJUb1N0cmluZyIsCiAgICAgICAgICAgICJjb250ZXh0IiA6ICJQYXRpZW50IiwKICAgICAgICAgICAgImFjY2Vzc0xldmVsIiA6ICJQdWJsaWMiLAogICAgICAgICAgICAidHlwZSIgOiAiRnVuY3Rpb25EZWYiLAogICAgICAgICAgICAiZXhwcmVzc2lvbiIgOiB7CiAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIxMzA6NTktMTMwOjY5IiwKICAgICAgICAgICAgICAgInBhdGgiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgInR5cGUiIDogIlByb3BlcnR5IiwKICAgICAgICAgICAgICAgInNvdXJjZSIgOiB7CiAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIxMzA6NTktMTMwOjYzIiwKICAgICAgICAgICAgICAgICAgIm5hbWUiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk9wZXJhbmRSZWYiCiAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgfSwKICAgICAgICAgICAgIm9wZXJhbmQiIDogWyB7CiAgICAgICAgICAgICAgICJuYW1lIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICJvcGVyYW5kVHlwZVNwZWNpZmllciIgOiB7CiAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIxMzA6MzItMTMwOjU1IiwKICAgICAgICAgICAgICAgICAgIm5hbWUiIDogIntodHRwOi8vaGw3Lm9yZy9maGlyfURldmljZVVzZVN0YXRlbWVudFN0YXR1cyIsCiAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJOYW1lZFR5cGVTcGVjaWZpZXIiCiAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgfSBdCiAgICAgICAgIH0sIHsKICAgICAgICAgICAgImxvY2F0b3IiIDogIjEzMToxLTEzMTo2NyIsCiAgICAgICAgICAgICJuYW1lIiA6ICJUb1N0cmluZyIsCiAgICAgICAgICAgICJjb250ZXh0IiA6ICJQYXRpZW50IiwKICAgICAgICAgICAgImFjY2Vzc0xldmVsIiA6ICJQdWJsaWMiLAogICAgICAgICAgICAidHlwZSIgOiAiRnVuY3Rpb25EZWYiLAogICAgICAgICAgICAiZXhwcmVzc2lvbiIgOiB7CiAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIxMzE6NTctMTMxOjY3IiwKICAgICAgICAgICAgICAgInBhdGgiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgInR5cGUiIDogIlByb3BlcnR5IiwKICAgICAgICAgICAgICAgInNvdXJjZSIgOiB7CiAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIxMzE6NTctMTMxOjYxIiwKICAgICAgICAgICAgICAgICAgIm5hbWUiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk9wZXJhbmRSZWYiCiAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgfSwKICAgICAgICAgICAgIm9wZXJhbmQiIDogWyB7CiAgICAgICAgICAgICAgICJuYW1lIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICJvcGVyYW5kVHlwZVNwZWNpZmllciIgOiB7CiAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIxMzE6MzItMTMxOjUzIiwKICAgICAgICAgICAgICAgICAgIm5hbWUiIDogIntodHRwOi8vaGw3Lm9yZy9maGlyfURpYWdub3N0aWNSZXBvcnRTdGF0dXMiLAogICAgICAgICAgICAgICAgICAidHlwZSIgOiAiTmFtZWRUeXBlU3BlY2lmaWVyIgogICAgICAgICAgICAgICB9CiAgICAgICAgICAgIH0gXQogICAgICAgICB9LCB7CiAgICAgICAgICAgICJsb2NhdG9yIiA6ICIxMzI6MS0xMzI6NjIiLAogICAgICAgICAgICAibmFtZSIgOiAiVG9TdHJpbmciLAogICAgICAgICAgICAiY29udGV4dCIgOiAiUGF0aWVudCIsCiAgICAgICAgICAgICJhY2Nlc3NMZXZlbCIgOiAiUHVibGljIiwKICAgICAgICAgICAgInR5cGUiIDogIkZ1bmN0aW9uRGVmIiwKICAgICAgICAgICAgImV4cHJlc3Npb24iIDogewogICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMTMyOjUyLTEzMjo2MiIsCiAgICAgICAgICAgICAgICJwYXRoIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICJ0eXBlIiA6ICJQcm9wZXJ0eSIsCiAgICAgICAgICAgICAgICJzb3VyY2UiIDogewogICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMTMyOjUyLTEzMjo1NiIsCiAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJPcGVyYW5kUmVmIgogICAgICAgICAgICAgICB9CiAgICAgICAgICAgIH0sCiAgICAgICAgICAgICJvcGVyYW5kIiA6IFsgewogICAgICAgICAgICAgICAibmFtZSIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAib3BlcmFuZFR5cGVTcGVjaWZpZXIiIDogewogICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMTMyOjMyLTEzMjo0OCIsCiAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJ7aHR0cDovL2hsNy5vcmcvZmhpcn1EaXNjcmltaW5hdG9yVHlwZSIsCiAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJOYW1lZFR5cGVTcGVjaWZpZXIiCiAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgfSBdCiAgICAgICAgIH0sIHsKICAgICAgICAgICAgImxvY2F0b3IiIDogIjEzMzoxLTEzMzo2OCIsCiAgICAgICAgICAgICJuYW1lIiA6ICJUb1N0cmluZyIsCiAgICAgICAgICAgICJjb250ZXh0IiA6ICJQYXRpZW50IiwKICAgICAgICAgICAgImFjY2Vzc0xldmVsIiA6ICJQdWJsaWMiLAogICAgICAgICAgICAidHlwZSIgOiAiRnVuY3Rpb25EZWYiLAogICAgICAgICAgICAiZXhwcmVzc2lvbiIgOiB7CiAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIxMzM6NTgtMTMzOjY4IiwKICAgICAgICAgICAgICAgInBhdGgiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgInR5cGUiIDogIlByb3BlcnR5IiwKICAgICAgICAgICAgICAgInNvdXJjZSIgOiB7CiAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIxMzM6NTgtMTMzOjYyIiwKICAgICAgICAgICAgICAgICAgIm5hbWUiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk9wZXJhbmRSZWYiCiAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgfSwKICAgICAgICAgICAgIm9wZXJhbmQiIDogWyB7CiAgICAgICAgICAgICAgICJuYW1lIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICJvcGVyYW5kVHlwZVNwZWNpZmllciIgOiB7CiAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIxMzM6MzItMTMzOjU0IiwKICAgICAgICAgICAgICAgICAgIm5hbWUiIDogIntodHRwOi8vaGw3Lm9yZy9maGlyfURvY3VtZW50Q29uZmlkZW50aWFsaXR5IiwKICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk5hbWVkVHlwZVNwZWNpZmllciIKICAgICAgICAgICAgICAgfQogICAgICAgICAgICB9IF0KICAgICAgICAgfSwgewogICAgICAgICAgICAibG9jYXRvciIgOiAiMTM0OjEtMTM0OjU3IiwKICAgICAgICAgICAgIm5hbWUiIDogIlRvU3RyaW5nIiwKICAgICAgICAgICAgImNvbnRleHQiIDogIlBhdGllbnQiLAogICAgICAgICAgICAiYWNjZXNzTGV2ZWwiIDogIlB1YmxpYyIsCiAgICAgICAgICAgICJ0eXBlIiA6ICJGdW5jdGlvbkRlZiIsCiAgICAgICAgICAgICJleHByZXNzaW9uIiA6IHsKICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjEzNDo0Ny0xMzQ6NTciLAogICAgICAgICAgICAgICAicGF0aCIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAidHlwZSIgOiAiUHJvcGVydHkiLAogICAgICAgICAgICAgICAic291cmNlIiA6IHsKICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjEzNDo0Ny0xMzQ6NTEiLAogICAgICAgICAgICAgICAgICAibmFtZSIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAgICAidHlwZSIgOiAiT3BlcmFuZFJlZiIKICAgICAgICAgICAgICAgfQogICAgICAgICAgICB9LAogICAgICAgICAgICAib3BlcmFuZCIgOiBbIHsKICAgICAgICAgICAgICAgIm5hbWUiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgIm9wZXJhbmRUeXBlU3BlY2lmaWVyIiA6IHsKICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjEzNDozMi0xMzQ6NDMiLAogICAgICAgICAgICAgICAgICAibmFtZSIgOiAie2h0dHA6Ly9obDcub3JnL2ZoaXJ9RG9jdW1lbnRNb2RlIiwKICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk5hbWVkVHlwZVNwZWNpZmllciIKICAgICAgICAgICAgICAgfQogICAgICAgICAgICB9IF0KICAgICAgICAgfSwgewogICAgICAgICAgICAibG9jYXRvciIgOiAiMTM1OjEtMTM1OjY4IiwKICAgICAgICAgICAgIm5hbWUiIDogIlRvU3RyaW5nIiwKICAgICAgICAgICAgImNvbnRleHQiIDogIlBhdGllbnQiLAogICAgICAgICAgICAiYWNjZXNzTGV2ZWwiIDogIlB1YmxpYyIsCiAgICAgICAgICAgICJ0eXBlIiA6ICJGdW5jdGlvbkRlZiIsCiAgICAgICAgICAgICJleHByZXNzaW9uIiA6IHsKICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjEzNTo1OC0xMzU6NjgiLAogICAgICAgICAgICAgICAicGF0aCIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAidHlwZSIgOiAiUHJvcGVydHkiLAogICAgICAgICAgICAgICAic291cmNlIiA6IHsKICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjEzNTo1OC0xMzU6NjIiLAogICAgICAgICAgICAgICAgICAibmFtZSIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAgICAidHlwZSIgOiAiT3BlcmFuZFJlZiIKICAgICAgICAgICAgICAgfQogICAgICAgICAgICB9LAogICAgICAgICAgICAib3BlcmFuZCIgOiBbIHsKICAgICAgICAgICAgICAgIm5hbWUiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgIm9wZXJhbmRUeXBlU3BlY2lmaWVyIiA6IHsKICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjEzNTozMi0xMzU6NTQiLAogICAgICAgICAgICAgICAgICAibmFtZSIgOiAie2h0dHA6Ly9obDcub3JnL2ZoaXJ9RG9jdW1lbnRSZWZlcmVuY2VTdGF0dXMiLAogICAgICAgICAgICAgICAgICAidHlwZSIgOiAiTmFtZWRUeXBlU3BlY2lmaWVyIgogICAgICAgICAgICAgICB9CiAgICAgICAgICAgIH0gXQogICAgICAgICB9LCB7CiAgICAgICAgICAgICJsb2NhdG9yIiA6ICIxMzY6MS0xMzY6NjkiLAogICAgICAgICAgICAibmFtZSIgOiAiVG9TdHJpbmciLAogICAgICAgICAgICAiY29udGV4dCIgOiAiUGF0aWVudCIsCiAgICAgICAgICAgICJhY2Nlc3NMZXZlbCIgOiAiUHVibGljIiwKICAgICAgICAgICAgInR5cGUiIDogIkZ1bmN0aW9uRGVmIiwKICAgICAgICAgICAgImV4cHJlc3Npb24iIDogewogICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMTM2OjU5LTEzNjo2OSIsCiAgICAgICAgICAgICAgICJwYXRoIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICJ0eXBlIiA6ICJQcm9wZXJ0eSIsCiAgICAgICAgICAgICAgICJzb3VyY2UiIDogewogICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMTM2OjU5LTEzNjo2MyIsCiAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJPcGVyYW5kUmVmIgogICAgICAgICAgICAgICB9CiAgICAgICAgICAgIH0sCiAgICAgICAgICAgICJvcGVyYW5kIiA6IFsgewogICAgICAgICAgICAgICAibmFtZSIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAib3BlcmFuZFR5cGVTcGVjaWZpZXIiIDogewogICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMTM2OjMyLTEzNjo1NSIsCiAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJ7aHR0cDovL2hsNy5vcmcvZmhpcn1Eb2N1bWVudFJlbGF0aW9uc2hpcFR5cGUiLAogICAgICAgICAgICAgICAgICAidHlwZSIgOiAiTmFtZWRUeXBlU3BlY2lmaWVyIgogICAgICAgICAgICAgICB9CiAgICAgICAgICAgIH0gXQogICAgICAgICB9LCB7CiAgICAgICAgICAgICJsb2NhdG9yIiA6ICIxMzc6MS0xMzc6NzAiLAogICAgICAgICAgICAibmFtZSIgOiAiVG9TdHJpbmciLAogICAgICAgICAgICAiY29udGV4dCIgOiAiUGF0aWVudCIsCiAgICAgICAgICAgICJhY2Nlc3NMZXZlbCIgOiAiUHVibGljIiwKICAgICAgICAgICAgInR5cGUiIDogIkZ1bmN0aW9uRGVmIiwKICAgICAgICAgICAgImV4cHJlc3Npb24iIDogewogICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMTM3OjYwLTEzNzo3MCIsCiAgICAgICAgICAgICAgICJwYXRoIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICJ0eXBlIiA6ICJQcm9wZXJ0eSIsCiAgICAgICAgICAgICAgICJzb3VyY2UiIDogewogICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMTM3OjYwLTEzNzo2NCIsCiAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJPcGVyYW5kUmVmIgogICAgICAgICAgICAgICB9CiAgICAgICAgICAgIH0sCiAgICAgICAgICAgICJvcGVyYW5kIiA6IFsgewogICAgICAgICAgICAgICAibmFtZSIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAib3BlcmFuZFR5cGVTcGVjaWZpZXIiIDogewogICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMTM3OjMyLTEzNzo1NiIsCiAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJ7aHR0cDovL2hsNy5vcmcvZmhpcn1FbGlnaWJpbGl0eVJlcXVlc3RQdXJwb3NlIiwKICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk5hbWVkVHlwZVNwZWNpZmllciIKICAgICAgICAgICAgICAgfQogICAgICAgICAgICB9IF0KICAgICAgICAgfSwgewogICAgICAgICAgICAibG9jYXRvciIgOiAiMTM4OjEtMTM4OjY5IiwKICAgICAgICAgICAgIm5hbWUiIDogIlRvU3RyaW5nIiwKICAgICAgICAgICAgImNvbnRleHQiIDogIlBhdGllbnQiLAogICAgICAgICAgICAiYWNjZXNzTGV2ZWwiIDogIlB1YmxpYyIsCiAgICAgICAgICAgICJ0eXBlIiA6ICJGdW5jdGlvbkRlZiIsCiAgICAgICAgICAgICJleHByZXNzaW9uIiA6IHsKICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjEzODo1OS0xMzg6NjkiLAogICAgICAgICAgICAgICAicGF0aCIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAidHlwZSIgOiAiUHJvcGVydHkiLAogICAgICAgICAgICAgICAic291cmNlIiA6IHsKICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjEzODo1OS0xMzg6NjMiLAogICAgICAgICAgICAgICAgICAibmFtZSIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAgICAidHlwZSIgOiAiT3BlcmFuZFJlZiIKICAgICAgICAgICAgICAgfQogICAgICAgICAgICB9LAogICAgICAgICAgICAib3BlcmFuZCIgOiBbIHsKICAgICAgICAgICAgICAgIm5hbWUiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgIm9wZXJhbmRUeXBlU3BlY2lmaWVyIiA6IHsKICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjEzODozMi0xMzg6NTUiLAogICAgICAgICAgICAgICAgICAibmFtZSIgOiAie2h0dHA6Ly9obDcub3JnL2ZoaXJ9RWxpZ2liaWxpdHlSZXF1ZXN0U3RhdHVzIiwKICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk5hbWVkVHlwZVNwZWNpZmllciIKICAgICAgICAgICAgICAgfQogICAgICAgICAgICB9IF0KICAgICAgICAgfSwgewogICAgICAgICAgICAibG9jYXRvciIgOiAiMTM5OjEtMTM5OjcxIiwKICAgICAgICAgICAgIm5hbWUiIDogIlRvU3RyaW5nIiwKICAgICAgICAgICAgImNvbnRleHQiIDogIlBhdGllbnQiLAogICAgICAgICAgICAiYWNjZXNzTGV2ZWwiIDogIlB1YmxpYyIsCiAgICAgICAgICAgICJ0eXBlIiA6ICJGdW5jdGlvbkRlZiIsCiAgICAgICAgICAgICJleHByZXNzaW9uIiA6IHsKICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjEzOTo2MS0xMzk6NzEiLAogICAgICAgICAgICAgICAicGF0aCIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAidHlwZSIgOiAiUHJvcGVydHkiLAogICAgICAgICAgICAgICAic291cmNlIiA6IHsKICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjEzOTo2MS0xMzk6NjUiLAogICAgICAgICAgICAgICAgICAibmFtZSIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAgICAidHlwZSIgOiAiT3BlcmFuZFJlZiIKICAgICAgICAgICAgICAgfQogICAgICAgICAgICB9LAogICAgICAgICAgICAib3BlcmFuZCIgOiBbIHsKICAgICAgICAgICAgICAgIm5hbWUiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgIm9wZXJhbmRUeXBlU3BlY2lmaWVyIiA6IHsKICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjEzOTozMi0xMzk6NTciLAogICAgICAgICAgICAgICAgICAibmFtZSIgOiAie2h0dHA6Ly9obDcub3JnL2ZoaXJ9RWxpZ2liaWxpdHlSZXNwb25zZVB1cnBvc2UiLAogICAgICAgICAgICAgICAgICAidHlwZSIgOiAiTmFtZWRUeXBlU3BlY2lmaWVyIgogICAgICAgICAgICAgICB9CiAgICAgICAgICAgIH0gXQogICAgICAgICB9LCB7CiAgICAgICAgICAgICJsb2NhdG9yIiA6ICIxNDA6MS0xNDA6NzAiLAogICAgICAgICAgICAibmFtZSIgOiAiVG9TdHJpbmciLAogICAgICAgICAgICAiY29udGV4dCIgOiAiUGF0aWVudCIsCiAgICAgICAgICAgICJhY2Nlc3NMZXZlbCIgOiAiUHVibGljIiwKICAgICAgICAgICAgInR5cGUiIDogIkZ1bmN0aW9uRGVmIiwKICAgICAgICAgICAgImV4cHJlc3Npb24iIDogewogICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMTQwOjYwLTE0MDo3MCIsCiAgICAgICAgICAgICAgICJwYXRoIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICJ0eXBlIiA6ICJQcm9wZXJ0eSIsCiAgICAgICAgICAgICAgICJzb3VyY2UiIDogewogICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMTQwOjYwLTE0MDo2NCIsCiAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJPcGVyYW5kUmVmIgogICAgICAgICAgICAgICB9CiAgICAgICAgICAgIH0sCiAgICAgICAgICAgICJvcGVyYW5kIiA6IFsgewogICAgICAgICAgICAgICAibmFtZSIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAib3BlcmFuZFR5cGVTcGVjaWZpZXIiIDogewogICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMTQwOjMyLTE0MDo1NiIsCiAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJ7aHR0cDovL2hsNy5vcmcvZmhpcn1FbGlnaWJpbGl0eVJlc3BvbnNlU3RhdHVzIiwKICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk5hbWVkVHlwZVNwZWNpZmllciIKICAgICAgICAgICAgICAgfQogICAgICAgICAgICB9IF0KICAgICAgICAgfSwgewogICAgICAgICAgICAibG9jYXRvciIgOiAiMTQxOjEtMTQxOjYzIiwKICAgICAgICAgICAgIm5hbWUiIDogIlRvU3RyaW5nIiwKICAgICAgICAgICAgImNvbnRleHQiIDogIlBhdGllbnQiLAogICAgICAgICAgICAiYWNjZXNzTGV2ZWwiIDogIlB1YmxpYyIsCiAgICAgICAgICAgICJ0eXBlIiA6ICJGdW5jdGlvbkRlZiIsCiAgICAgICAgICAgICJleHByZXNzaW9uIiA6IHsKICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjE0MTo1My0xNDE6NjMiLAogICAgICAgICAgICAgICAicGF0aCIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAidHlwZSIgOiAiUHJvcGVydHkiLAogICAgICAgICAgICAgICAic291cmNlIiA6IHsKICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjE0MTo1My0xNDE6NTciLAogICAgICAgICAgICAgICAgICAibmFtZSIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAgICAidHlwZSIgOiAiT3BlcmFuZFJlZiIKICAgICAgICAgICAgICAgfQogICAgICAgICAgICB9LAogICAgICAgICAgICAib3BlcmFuZCIgOiBbIHsKICAgICAgICAgICAgICAgIm5hbWUiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgIm9wZXJhbmRUeXBlU3BlY2lmaWVyIiA6IHsKICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjE0MTozMi0xNDE6NDkiLAogICAgICAgICAgICAgICAgICAibmFtZSIgOiAie2h0dHA6Ly9obDcub3JnL2ZoaXJ9RW5hYmxlV2hlbkJlaGF2aW9yIiwKICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk5hbWVkVHlwZVNwZWNpZmllciIKICAgICAgICAgICAgICAgfQogICAgICAgICAgICB9IF0KICAgICAgICAgfSwgewogICAgICAgICAgICAibG9jYXRvciIgOiAiMTQyOjEtMTQyOjY4IiwKICAgICAgICAgICAgIm5hbWUiIDogIlRvU3RyaW5nIiwKICAgICAgICAgICAgImNvbnRleHQiIDogIlBhdGllbnQiLAogICAgICAgICAgICAiYWNjZXNzTGV2ZWwiIDogIlB1YmxpYyIsCiAgICAgICAgICAgICJ0eXBlIiA6ICJGdW5jdGlvbkRlZiIsCiAgICAgICAgICAgICJleHByZXNzaW9uIiA6IHsKICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjE0Mjo1OC0xNDI6NjgiLAogICAgICAgICAgICAgICAicGF0aCIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAidHlwZSIgOiAiUHJvcGVydHkiLAogICAgICAgICAgICAgICAic291cmNlIiA6IHsKICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjE0Mjo1OC0xNDI6NjIiLAogICAgICAgICAgICAgICAgICAibmFtZSIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAgICAidHlwZSIgOiAiT3BlcmFuZFJlZiIKICAgICAgICAgICAgICAgfQogICAgICAgICAgICB9LAogICAgICAgICAgICAib3BlcmFuZCIgOiBbIHsKICAgICAgICAgICAgICAgIm5hbWUiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgIm9wZXJhbmRUeXBlU3BlY2lmaWVyIiA6IHsKICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjE0MjozMi0xNDI6NTQiLAogICAgICAgICAgICAgICAgICAibmFtZSIgOiAie2h0dHA6Ly9obDcub3JnL2ZoaXJ9RW5jb3VudGVyTG9jYXRpb25TdGF0dXMiLAogICAgICAgICAgICAgICAgICAidHlwZSIgOiAiTmFtZWRUeXBlU3BlY2lmaWVyIgogICAgICAgICAgICAgICB9CiAgICAgICAgICAgIH0gXQogICAgICAgICB9LCB7CiAgICAgICAgICAgICJsb2NhdG9yIiA6ICIxNDM6MS0xNDM6NjAiLAogICAgICAgICAgICAibmFtZSIgOiAiVG9TdHJpbmciLAogICAgICAgICAgICAiY29udGV4dCIgOiAiUGF0aWVudCIsCiAgICAgICAgICAgICJhY2Nlc3NMZXZlbCIgOiAiUHVibGljIiwKICAgICAgICAgICAgInR5cGUiIDogIkZ1bmN0aW9uRGVmIiwKICAgICAgICAgICAgImV4cHJlc3Npb24iIDogewogICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMTQzOjUwLTE0Mzo2MCIsCiAgICAgICAgICAgICAgICJwYXRoIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICJ0eXBlIiA6ICJQcm9wZXJ0eSIsCiAgICAgICAgICAgICAgICJzb3VyY2UiIDogewogICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMTQzOjUwLTE0Mzo1NCIsCiAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJPcGVyYW5kUmVmIgogICAgICAgICAgICAgICB9CiAgICAgICAgICAgIH0sCiAgICAgICAgICAgICJvcGVyYW5kIiA6IFsgewogICAgICAgICAgICAgICAibmFtZSIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAib3BlcmFuZFR5cGVTcGVjaWZpZXIiIDogewogICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMTQzOjMyLTE0Mzo0NiIsCiAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJ7aHR0cDovL2hsNy5vcmcvZmhpcn1FbmNvdW50ZXJTdGF0dXMiLAogICAgICAgICAgICAgICAgICAidHlwZSIgOiAiTmFtZWRUeXBlU3BlY2lmaWVyIgogICAgICAgICAgICAgICB9CiAgICAgICAgICAgIH0gXQogICAgICAgICB9LCB7CiAgICAgICAgICAgICJsb2NhdG9yIiA6ICIxNDQ6MS0xNDQ6NTkiLAogICAgICAgICAgICAibmFtZSIgOiAiVG9TdHJpbmciLAogICAgICAgICAgICAiY29udGV4dCIgOiAiUGF0aWVudCIsCiAgICAgICAgICAgICJhY2Nlc3NMZXZlbCIgOiAiUHVibGljIiwKICAgICAgICAgICAgInR5cGUiIDogIkZ1bmN0aW9uRGVmIiwKICAgICAgICAgICAgImV4cHJlc3Npb24iIDogewogICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMTQ0OjQ5LTE0NDo1OSIsCiAgICAgICAgICAgICAgICJwYXRoIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICJ0eXBlIiA6ICJQcm9wZXJ0eSIsCiAgICAgICAgICAgICAgICJzb3VyY2UiIDogewogICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMTQ0OjQ5LTE0NDo1MyIsCiAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJPcGVyYW5kUmVmIgogICAgICAgICAgICAgICB9CiAgICAgICAgICAgIH0sCiAgICAgICAgICAgICJvcGVyYW5kIiA6IFsgewogICAgICAgICAgICAgICAibmFtZSIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAib3BlcmFuZFR5cGVTcGVjaWZpZXIiIDogewogICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMTQ0OjMyLTE0NDo0NSIsCiAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJ7aHR0cDovL2hsNy5vcmcvZmhpcn1FbmRwb2ludFN0YXR1cyIsCiAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJOYW1lZFR5cGVTcGVjaWZpZXIiCiAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgfSBdCiAgICAgICAgIH0sIHsKICAgICAgICAgICAgImxvY2F0b3IiIDogIjE0NToxLTE0NTo2OCIsCiAgICAgICAgICAgICJuYW1lIiA6ICJUb1N0cmluZyIsCiAgICAgICAgICAgICJjb250ZXh0IiA6ICJQYXRpZW50IiwKICAgICAgICAgICAgImFjY2Vzc0xldmVsIiA6ICJQdWJsaWMiLAogICAgICAgICAgICAidHlwZSIgOiAiRnVuY3Rpb25EZWYiLAogICAgICAgICAgICAiZXhwcmVzc2lvbiIgOiB7CiAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIxNDU6NTgtMTQ1OjY4IiwKICAgICAgICAgICAgICAgInBhdGgiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgInR5cGUiIDogIlByb3BlcnR5IiwKICAgICAgICAgICAgICAgInNvdXJjZSIgOiB7CiAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIxNDU6NTgtMTQ1OjYyIiwKICAgICAgICAgICAgICAgICAgIm5hbWUiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk9wZXJhbmRSZWYiCiAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgfSwKICAgICAgICAgICAgIm9wZXJhbmQiIDogWyB7CiAgICAgICAgICAgICAgICJuYW1lIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICJvcGVyYW5kVHlwZVNwZWNpZmllciIgOiB7CiAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIxNDU6MzItMTQ1OjU0IiwKICAgICAgICAgICAgICAgICAgIm5hbWUiIDogIntodHRwOi8vaGw3Lm9yZy9maGlyfUVucm9sbG1lbnRSZXF1ZXN0U3RhdHVzIiwKICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk5hbWVkVHlwZVNwZWNpZmllciIKICAgICAgICAgICAgICAgfQogICAgICAgICAgICB9IF0KICAgICAgICAgfSwgewogICAgICAgICAgICAibG9jYXRvciIgOiAiMTQ2OjEtMTQ2OjY5IiwKICAgICAgICAgICAgIm5hbWUiIDogIlRvU3RyaW5nIiwKICAgICAgICAgICAgImNvbnRleHQiIDogIlBhdGllbnQiLAogICAgICAgICAgICAiYWNjZXNzTGV2ZWwiIDogIlB1YmxpYyIsCiAgICAgICAgICAgICJ0eXBlIiA6ICJGdW5jdGlvbkRlZiIsCiAgICAgICAgICAgICJleHByZXNzaW9uIiA6IHsKICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjE0Njo1OS0xNDY6NjkiLAogICAgICAgICAgICAgICAicGF0aCIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAidHlwZSIgOiAiUHJvcGVydHkiLAogICAgICAgICAgICAgICAic291cmNlIiA6IHsKICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjE0Njo1OS0xNDY6NjMiLAogICAgICAgICAgICAgICAgICAibmFtZSIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAgICAidHlwZSIgOiAiT3BlcmFuZFJlZiIKICAgICAgICAgICAgICAgfQogICAgICAgICAgICB9LAogICAgICAgICAgICAib3BlcmFuZCIgOiBbIHsKICAgICAgICAgICAgICAgIm5hbWUiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgIm9wZXJhbmRUeXBlU3BlY2lmaWVyIiA6IHsKICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjE0NjozMi0xNDY6NTUiLAogICAgICAgICAgICAgICAgICAibmFtZSIgOiAie2h0dHA6Ly9obDcub3JnL2ZoaXJ9RW5yb2xsbWVudFJlc3BvbnNlU3RhdHVzIiwKICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk5hbWVkVHlwZVNwZWNpZmllciIKICAgICAgICAgICAgICAgfQogICAgICAgICAgICB9IF0KICAgICAgICAgfSwgewogICAgICAgICAgICAibG9jYXRvciIgOiAiMTQ3OjEtMTQ3OjY0IiwKICAgICAgICAgICAgIm5hbWUiIDogIlRvU3RyaW5nIiwKICAgICAgICAgICAgImNvbnRleHQiIDogIlBhdGllbnQiLAogICAgICAgICAgICAiYWNjZXNzTGV2ZWwiIDogIlB1YmxpYyIsCiAgICAgICAgICAgICJ0eXBlIiA6ICJGdW5jdGlvbkRlZiIsCiAgICAgICAgICAgICJleHByZXNzaW9uIiA6IHsKICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjE0Nzo1NC0xNDc6NjQiLAogICAgICAgICAgICAgICAicGF0aCIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAidHlwZSIgOiAiUHJvcGVydHkiLAogICAgICAgICAgICAgICAic291cmNlIiA6IHsKICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjE0Nzo1NC0xNDc6NTgiLAogICAgICAgICAgICAgICAgICAibmFtZSIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAgICAidHlwZSIgOiAiT3BlcmFuZFJlZiIKICAgICAgICAgICAgICAgfQogICAgICAgICAgICB9LAogICAgICAgICAgICAib3BlcmFuZCIgOiBbIHsKICAgICAgICAgICAgICAgIm5hbWUiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgIm9wZXJhbmRUeXBlU3BlY2lmaWVyIiA6IHsKICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjE0NzozMi0xNDc6NTAiLAogICAgICAgICAgICAgICAgICAibmFtZSIgOiAie2h0dHA6Ly9obDcub3JnL2ZoaXJ9RXBpc29kZU9mQ2FyZVN0YXR1cyIsCiAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJOYW1lZFR5cGVTcGVjaWZpZXIiCiAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgfSBdCiAgICAgICAgIH0sIHsKICAgICAgICAgICAgImxvY2F0b3IiIDogIjE0ODoxLTE0ODo2NCIsCiAgICAgICAgICAgICJuYW1lIiA6ICJUb1N0cmluZyIsCiAgICAgICAgICAgICJjb250ZXh0IiA6ICJQYXRpZW50IiwKICAgICAgICAgICAgImFjY2Vzc0xldmVsIiA6ICJQdWJsaWMiLAogICAgICAgICAgICAidHlwZSIgOiAiRnVuY3Rpb25EZWYiLAogICAgICAgICAgICAiZXhwcmVzc2lvbiIgOiB7CiAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIxNDg6NTQtMTQ4OjY0IiwKICAgICAgICAgICAgICAgInBhdGgiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgInR5cGUiIDogIlByb3BlcnR5IiwKICAgICAgICAgICAgICAgInNvdXJjZSIgOiB7CiAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIxNDg6NTQtMTQ4OjU4IiwKICAgICAgICAgICAgICAgICAgIm5hbWUiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk9wZXJhbmRSZWYiCiAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgfSwKICAgICAgICAgICAgIm9wZXJhbmQiIDogWyB7CiAgICAgICAgICAgICAgICJuYW1lIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICJvcGVyYW5kVHlwZVNwZWNpZmllciIgOiB7CiAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIxNDg6MzItMTQ4OjUwIiwKICAgICAgICAgICAgICAgICAgIm5hbWUiIDogIntodHRwOi8vaGw3Lm9yZy9maGlyfUV2ZW50Q2FwYWJpbGl0eU1vZGUiLAogICAgICAgICAgICAgICAgICAidHlwZSIgOiAiTmFtZWRUeXBlU3BlY2lmaWVyIgogICAgICAgICAgICAgICB9CiAgICAgICAgICAgIH0gXQogICAgICAgICB9LCB7CiAgICAgICAgICAgICJsb2NhdG9yIiA6ICIxNDk6MS0xNDk6NTYiLAogICAgICAgICAgICAibmFtZSIgOiAiVG9TdHJpbmciLAogICAgICAgICAgICAiY29udGV4dCIgOiAiUGF0aWVudCIsCiAgICAgICAgICAgICJhY2Nlc3NMZXZlbCIgOiAiUHVibGljIiwKICAgICAgICAgICAgInR5cGUiIDogIkZ1bmN0aW9uRGVmIiwKICAgICAgICAgICAgImV4cHJlc3Npb24iIDogewogICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMTQ5OjQ2LTE0OTo1NiIsCiAgICAgICAgICAgICAgICJwYXRoIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICJ0eXBlIiA6ICJQcm9wZXJ0eSIsCiAgICAgICAgICAgICAgICJzb3VyY2UiIDogewogICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMTQ5OjQ2LTE0OTo1MCIsCiAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJPcGVyYW5kUmVmIgogICAgICAgICAgICAgICB9CiAgICAgICAgICAgIH0sCiAgICAgICAgICAgICJvcGVyYW5kIiA6IFsgewogICAgICAgICAgICAgICAibmFtZSIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAib3BlcmFuZFR5cGVTcGVjaWZpZXIiIDogewogICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMTQ5OjMyLTE0OTo0MiIsCiAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJ7aHR0cDovL2hsNy5vcmcvZmhpcn1FdmVudFRpbWluZyIsCiAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJOYW1lZFR5cGVTcGVjaWZpZXIiCiAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgfSBdCiAgICAgICAgIH0sIHsKICAgICAgICAgICAgImxvY2F0b3IiIDogIjE1MDoxLTE1MDo2NSIsCiAgICAgICAgICAgICJuYW1lIiA6ICJUb1N0cmluZyIsCiAgICAgICAgICAgICJjb250ZXh0IiA6ICJQYXRpZW50IiwKICAgICAgICAgICAgImFjY2Vzc0xldmVsIiA6ICJQdWJsaWMiLAogICAgICAgICAgICAidHlwZSIgOiAiRnVuY3Rpb25EZWYiLAogICAgICAgICAgICAiZXhwcmVzc2lvbiIgOiB7CiAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIxNTA6NTUtMTUwOjY1IiwKICAgICAgICAgICAgICAgInBhdGgiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgInR5cGUiIDogIlByb3BlcnR5IiwKICAgICAgICAgICAgICAgInNvdXJjZSIgOiB7CiAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIxNTA6NTUtMTUwOjU5IiwKICAgICAgICAgICAgICAgICAgIm5hbWUiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk9wZXJhbmRSZWYiCiAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgfSwKICAgICAgICAgICAgIm9wZXJhbmQiIDogWyB7CiAgICAgICAgICAgICAgICJuYW1lIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICJvcGVyYW5kVHlwZVNwZWNpZmllciIgOiB7CiAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIxNTA6MzItMTUwOjUxIiwKICAgICAgICAgICAgICAgICAgIm5hbWUiIDogIntodHRwOi8vaGw3Lm9yZy9maGlyfUV2aWRlbmNlVmFyaWFibGVUeXBlIiwKICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk5hbWVkVHlwZVNwZWNpZmllciIKICAgICAgICAgICAgICAgfQogICAgICAgICAgICB9IF0KICAgICAgICAgfSwgewogICAgICAgICAgICAibG9jYXRvciIgOiAiMTUxOjEtMTUxOjY5IiwKICAgICAgICAgICAgIm5hbWUiIDogIlRvU3RyaW5nIiwKICAgICAgICAgICAgImNvbnRleHQiIDogIlBhdGllbnQiLAogICAgICAgICAgICAiYWNjZXNzTGV2ZWwiIDogIlB1YmxpYyIsCiAgICAgICAgICAgICJ0eXBlIiA6ICJGdW5jdGlvbkRlZiIsCiAgICAgICAgICAgICJleHByZXNzaW9uIiA6IHsKICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjE1MTo1OS0xNTE6NjkiLAogICAgICAgICAgICAgICAicGF0aCIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAidHlwZSIgOiAiUHJvcGVydHkiLAogICAgICAgICAgICAgICAic291cmNlIiA6IHsKICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjE1MTo1OS0xNTE6NjMiLAogICAgICAgICAgICAgICAgICAibmFtZSIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAgICAidHlwZSIgOiAiT3BlcmFuZFJlZiIKICAgICAgICAgICAgICAgfQogICAgICAgICAgICB9LAogICAgICAgICAgICAib3BlcmFuZCIgOiBbIHsKICAgICAgICAgICAgICAgIm5hbWUiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgIm9wZXJhbmRUeXBlU3BlY2lmaWVyIiA6IHsKICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjE1MTozMi0xNTE6NTUiLAogICAgICAgICAgICAgICAgICAibmFtZSIgOiAie2h0dHA6Ly9obDcub3JnL2ZoaXJ9RXhhbXBsZVNjZW5hcmlvQWN0b3JUeXBlIiwKICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk5hbWVkVHlwZVNwZWNpZmllciIKICAgICAgICAgICAgICAgfQogICAgICAgICAgICB9IF0KICAgICAgICAgfSwgewogICAgICAgICAgICAibG9jYXRvciIgOiAiMTUyOjEtMTUyOjcxIiwKICAgICAgICAgICAgIm5hbWUiIDogIlRvU3RyaW5nIiwKICAgICAgICAgICAgImNvbnRleHQiIDogIlBhdGllbnQiLAogICAgICAgICAgICAiYWNjZXNzTGV2ZWwiIDogIlB1YmxpYyIsCiAgICAgICAgICAgICJ0eXBlIiA6ICJGdW5jdGlvbkRlZiIsCiAgICAgICAgICAgICJleHByZXNzaW9uIiA6IHsKICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjE1Mjo2MS0xNTI6NzEiLAogICAgICAgICAgICAgICAicGF0aCIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAidHlwZSIgOiAiUHJvcGVydHkiLAogICAgICAgICAgICAgICAic291cmNlIiA6IHsKICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjE1Mjo2MS0xNTI6NjUiLAogICAgICAgICAgICAgICAgICAibmFtZSIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAgICAidHlwZSIgOiAiT3BlcmFuZFJlZiIKICAgICAgICAgICAgICAgfQogICAgICAgICAgICB9LAogICAgICAgICAgICAib3BlcmFuZCIgOiBbIHsKICAgICAgICAgICAgICAgIm5hbWUiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgIm9wZXJhbmRUeXBlU3BlY2lmaWVyIiA6IHsKICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjE1MjozMi0xNTI6NTciLAogICAgICAgICAgICAgICAgICAibmFtZSIgOiAie2h0dHA6Ly9obDcub3JnL2ZoaXJ9RXhwbGFuYXRpb25PZkJlbmVmaXRTdGF0dXMiLAogICAgICAgICAgICAgICAgICAidHlwZSIgOiAiTmFtZWRUeXBlU3BlY2lmaWVyIgogICAgICAgICAgICAgICB9CiAgICAgICAgICAgIH0gXQogICAgICAgICB9LCB7CiAgICAgICAgICAgICJsb2NhdG9yIiA6ICIxNTM6MS0xNTM6NTgiLAogICAgICAgICAgICAibmFtZSIgOiAiVG9TdHJpbmciLAogICAgICAgICAgICAiY29udGV4dCIgOiAiUGF0aWVudCIsCiAgICAgICAgICAgICJhY2Nlc3NMZXZlbCIgOiAiUHVibGljIiwKICAgICAgICAgICAgInR5cGUiIDogIkZ1bmN0aW9uRGVmIiwKICAgICAgICAgICAgImV4cHJlc3Npb24iIDogewogICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMTUzOjQ4LTE1Mzo1OCIsCiAgICAgICAgICAgICAgICJwYXRoIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICJ0eXBlIiA6ICJQcm9wZXJ0eSIsCiAgICAgICAgICAgICAgICJzb3VyY2UiIDogewogICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMTUzOjQ4LTE1Mzo1MiIsCiAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJPcGVyYW5kUmVmIgogICAgICAgICAgICAgICB9CiAgICAgICAgICAgIH0sCiAgICAgICAgICAgICJvcGVyYW5kIiA6IFsgewogICAgICAgICAgICAgICAibmFtZSIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAib3BlcmFuZFR5cGVTcGVjaWZpZXIiIDogewogICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMTUzOjMyLTE1Mzo0NCIsCiAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJ7aHR0cDovL2hsNy5vcmcvZmhpcn1FeHBvc3VyZVN0YXRlIiwKICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk5hbWVkVHlwZVNwZWNpZmllciIKICAgICAgICAgICAgICAgfQogICAgICAgICAgICB9IF0KICAgICAgICAgfSwgewogICAgICAgICAgICAibG9jYXRvciIgOiAiMTU0OjEtMTU0OjY1IiwKICAgICAgICAgICAgIm5hbWUiIDogIlRvU3RyaW5nIiwKICAgICAgICAgICAgImNvbnRleHQiIDogIlBhdGllbnQiLAogICAgICAgICAgICAiYWNjZXNzTGV2ZWwiIDogIlB1YmxpYyIsCiAgICAgICAgICAgICJ0eXBlIiA6ICJGdW5jdGlvbkRlZiIsCiAgICAgICAgICAgICJleHByZXNzaW9uIiA6IHsKICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjE1NDo1NS0xNTQ6NjUiLAogICAgICAgICAgICAgICAicGF0aCIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAidHlwZSIgOiAiUHJvcGVydHkiLAogICAgICAgICAgICAgICAic291cmNlIiA6IHsKICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjE1NDo1NS0xNTQ6NTkiLAogICAgICAgICAgICAgICAgICAibmFtZSIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAgICAidHlwZSIgOiAiT3BlcmFuZFJlZiIKICAgICAgICAgICAgICAgfQogICAgICAgICAgICB9LAogICAgICAgICAgICAib3BlcmFuZCIgOiBbIHsKICAgICAgICAgICAgICAgIm5hbWUiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgIm9wZXJhbmRUeXBlU3BlY2lmaWVyIiA6IHsKICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjE1NDozMi0xNTQ6NTEiLAogICAgICAgICAgICAgICAgICAibmFtZSIgOiAie2h0dHA6Ly9obDcub3JnL2ZoaXJ9RXh0ZW5zaW9uQ29udGV4dFR5cGUiLAogICAgICAgICAgICAgICAgICAidHlwZSIgOiAiTmFtZWRUeXBlU3BlY2lmaWVyIgogICAgICAgICAgICAgICB9CiAgICAgICAgICAgIH0gXQogICAgICAgICB9LCB7CiAgICAgICAgICAgICJsb2NhdG9yIiA6ICIxNTU6MS0xNTU6NTciLAogICAgICAgICAgICAibmFtZSIgOiAiVG9TdHJpbmciLAogICAgICAgICAgICAiY29udGV4dCIgOiAiUGF0aWVudCIsCiAgICAgICAgICAgICJhY2Nlc3NMZXZlbCIgOiAiUHVibGljIiwKICAgICAgICAgICAgInR5cGUiIDogIkZ1bmN0aW9uRGVmIiwKICAgICAgICAgICAgImV4cHJlc3Npb24iIDogewogICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMTU1OjQ3LTE1NTo1NyIsCiAgICAgICAgICAgICAgICJwYXRoIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICJ0eXBlIiA6ICJQcm9wZXJ0eSIsCiAgICAgICAgICAgICAgICJzb3VyY2UiIDogewogICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMTU1OjQ3LTE1NTo1MSIsCiAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJPcGVyYW5kUmVmIgogICAgICAgICAgICAgICB9CiAgICAgICAgICAgIH0sCiAgICAgICAgICAgICJvcGVyYW5kIiA6IFsgewogICAgICAgICAgICAgICAibmFtZSIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAib3BlcmFuZFR5cGVTcGVjaWZpZXIiIDogewogICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMTU1OjMyLTE1NTo0MyIsCiAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJ7aHR0cDovL2hsNy5vcmcvZmhpcn1GSElSQWxsVHlwZXMiLAogICAgICAgICAgICAgICAgICAidHlwZSIgOiAiTmFtZWRUeXBlU3BlY2lmaWVyIgogICAgICAgICAgICAgICB9CiAgICAgICAgICAgIH0gXQogICAgICAgICB9LCB7CiAgICAgICAgICAgICJsb2NhdG9yIiA6ICIxNTY6MS0xNTY6NjAiLAogICAgICAgICAgICAibmFtZSIgOiAiVG9TdHJpbmciLAogICAgICAgICAgICAiY29udGV4dCIgOiAiUGF0aWVudCIsCiAgICAgICAgICAgICJhY2Nlc3NMZXZlbCIgOiAiUHVibGljIiwKICAgICAgICAgICAgInR5cGUiIDogIkZ1bmN0aW9uRGVmIiwKICAgICAgICAgICAgImV4cHJlc3Npb24iIDogewogICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMTU2OjUwLTE1Njo2MCIsCiAgICAgICAgICAgICAgICJwYXRoIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICJ0eXBlIiA6ICJQcm9wZXJ0eSIsCiAgICAgICAgICAgICAgICJzb3VyY2UiIDogewogICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMTU2OjUwLTE1Njo1NCIsCiAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJPcGVyYW5kUmVmIgogICAgICAgICAgICAgICB9CiAgICAgICAgICAgIH0sCiAgICAgICAgICAgICJvcGVyYW5kIiA6IFsgewogICAgICAgICAgICAgICAibmFtZSIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAib3BlcmFuZFR5cGVTcGVjaWZpZXIiIDogewogICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMTU2OjMyLTE1Njo0NiIsCiAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJ7aHR0cDovL2hsNy5vcmcvZmhpcn1GSElSRGVmaW5lZFR5cGUiLAogICAgICAgICAgICAgICAgICAidHlwZSIgOiAiTmFtZWRUeXBlU3BlY2lmaWVyIgogICAgICAgICAgICAgICB9CiAgICAgICAgICAgIH0gXQogICAgICAgICB9LCB7CiAgICAgICAgICAgICJsb2NhdG9yIiA6ICIxNTc6MS0xNTc6NjEiLAogICAgICAgICAgICAibmFtZSIgOiAiVG9TdHJpbmciLAogICAgICAgICAgICAiY29udGV4dCIgOiAiUGF0aWVudCIsCiAgICAgICAgICAgICJhY2Nlc3NMZXZlbCIgOiAiUHVibGljIiwKICAgICAgICAgICAgInR5cGUiIDogIkZ1bmN0aW9uRGVmIiwKICAgICAgICAgICAgImV4cHJlc3Npb24iIDogewogICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMTU3OjUxLTE1Nzo2MSIsCiAgICAgICAgICAgICAgICJwYXRoIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICJ0eXBlIiA6ICJQcm9wZXJ0eSIsCiAgICAgICAgICAgICAgICJzb3VyY2UiIDogewogICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMTU3OjUxLTE1Nzo1NSIsCiAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJPcGVyYW5kUmVmIgogICAgICAgICAgICAgICB9CiAgICAgICAgICAgIH0sCiAgICAgICAgICAgICJvcGVyYW5kIiA6IFsgewogICAgICAgICAgICAgICAibmFtZSIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAib3BlcmFuZFR5cGVTcGVjaWZpZXIiIDogewogICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMTU3OjMyLTE1Nzo0NyIsCiAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJ7aHR0cDovL2hsNy5vcmcvZmhpcn1GSElSRGV2aWNlU3RhdHVzIiwKICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk5hbWVkVHlwZVNwZWNpZmllciIKICAgICAgICAgICAgICAgfQogICAgICAgICAgICB9IF0KICAgICAgICAgfSwgewogICAgICAgICAgICAibG9jYXRvciIgOiAiMTU4OjEtMTU4OjYxIiwKICAgICAgICAgICAgIm5hbWUiIDogIlRvU3RyaW5nIiwKICAgICAgICAgICAgImNvbnRleHQiIDogIlBhdGllbnQiLAogICAgICAgICAgICAiYWNjZXNzTGV2ZWwiIDogIlB1YmxpYyIsCiAgICAgICAgICAgICJ0eXBlIiA6ICJGdW5jdGlvbkRlZiIsCiAgICAgICAgICAgICJleHByZXNzaW9uIiA6IHsKICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjE1ODo1MS0xNTg6NjEiLAogICAgICAgICAgICAgICAicGF0aCIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAidHlwZSIgOiAiUHJvcGVydHkiLAogICAgICAgICAgICAgICAic291cmNlIiA6IHsKICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjE1ODo1MS0xNTg6NTUiLAogICAgICAgICAgICAgICAgICAibmFtZSIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAgICAidHlwZSIgOiAiT3BlcmFuZFJlZiIKICAgICAgICAgICAgICAgfQogICAgICAgICAgICB9LAogICAgICAgICAgICAib3BlcmFuZCIgOiBbIHsKICAgICAgICAgICAgICAgIm5hbWUiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgIm9wZXJhbmRUeXBlU3BlY2lmaWVyIiA6IHsKICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjE1ODozMi0xNTg6NDciLAogICAgICAgICAgICAgICAgICAibmFtZSIgOiAie2h0dHA6Ly9obDcub3JnL2ZoaXJ9RkhJUlJlc291cmNlVHlwZSIsCiAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJOYW1lZFR5cGVTcGVjaWZpZXIiCiAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgfSBdCiAgICAgICAgIH0sIHsKICAgICAgICAgICAgImxvY2F0b3IiIDogIjE1OToxLTE1OTo2NCIsCiAgICAgICAgICAgICJuYW1lIiA6ICJUb1N0cmluZyIsCiAgICAgICAgICAgICJjb250ZXh0IiA6ICJQYXRpZW50IiwKICAgICAgICAgICAgImFjY2Vzc0xldmVsIiA6ICJQdWJsaWMiLAogICAgICAgICAgICAidHlwZSIgOiAiRnVuY3Rpb25EZWYiLAogICAgICAgICAgICAiZXhwcmVzc2lvbiIgOiB7CiAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIxNTk6NTQtMTU5OjY0IiwKICAgICAgICAgICAgICAgInBhdGgiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgInR5cGUiIDogIlByb3BlcnR5IiwKICAgICAgICAgICAgICAgInNvdXJjZSIgOiB7CiAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIxNTk6NTQtMTU5OjU4IiwKICAgICAgICAgICAgICAgICAgIm5hbWUiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk9wZXJhbmRSZWYiCiAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgfSwKICAgICAgICAgICAgIm9wZXJhbmQiIDogWyB7CiAgICAgICAgICAgICAgICJuYW1lIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICJvcGVyYW5kVHlwZVNwZWNpZmllciIgOiB7CiAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIxNTk6MzItMTU5OjUwIiwKICAgICAgICAgICAgICAgICAgIm5hbWUiIDogIntodHRwOi8vaGw3Lm9yZy9maGlyfUZISVJTdWJzdGFuY2VTdGF0dXMiLAogICAgICAgICAgICAgICAgICAidHlwZSIgOiAiTmFtZWRUeXBlU3BlY2lmaWVyIgogICAgICAgICAgICAgICB9CiAgICAgICAgICAgIH0gXQogICAgICAgICB9LCB7CiAgICAgICAgICAgICJsb2NhdG9yIiA6ICIxNjA6MS0xNjA6NTYiLAogICAgICAgICAgICAibmFtZSIgOiAiVG9TdHJpbmciLAogICAgICAgICAgICAiY29udGV4dCIgOiAiUGF0aWVudCIsCiAgICAgICAgICAgICJhY2Nlc3NMZXZlbCIgOiAiUHVibGljIiwKICAgICAgICAgICAgInR5cGUiIDogIkZ1bmN0aW9uRGVmIiwKICAgICAgICAgICAgImV4cHJlc3Npb24iIDogewogICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMTYwOjQ2LTE2MDo1NiIsCiAgICAgICAgICAgICAgICJwYXRoIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICJ0eXBlIiA6ICJQcm9wZXJ0eSIsCiAgICAgICAgICAgICAgICJzb3VyY2UiIDogewogICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMTYwOjQ2LTE2MDo1MCIsCiAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJPcGVyYW5kUmVmIgogICAgICAgICAgICAgICB9CiAgICAgICAgICAgIH0sCiAgICAgICAgICAgICJvcGVyYW5kIiA6IFsgewogICAgICAgICAgICAgICAibmFtZSIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAib3BlcmFuZFR5cGVTcGVjaWZpZXIiIDogewogICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMTYwOjMyLTE2MDo0MiIsCiAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJ7aHR0cDovL2hsNy5vcmcvZmhpcn1GSElSVmVyc2lvbiIsCiAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJOYW1lZFR5cGVTcGVjaWZpZXIiCiAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgfSBdCiAgICAgICAgIH0sIHsKICAgICAgICAgICAgImxvY2F0b3IiIDogIjE2MToxLTE2MTo2NCIsCiAgICAgICAgICAgICJuYW1lIiA6ICJUb1N0cmluZyIsCiAgICAgICAgICAgICJjb250ZXh0IiA6ICJQYXRpZW50IiwKICAgICAgICAgICAgImFjY2Vzc0xldmVsIiA6ICJQdWJsaWMiLAogICAgICAgICAgICAidHlwZSIgOiAiRnVuY3Rpb25EZWYiLAogICAgICAgICAgICAiZXhwcmVzc2lvbiIgOiB7CiAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIxNjE6NTQtMTYxOjY0IiwKICAgICAgICAgICAgICAgInBhdGgiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgInR5cGUiIDogIlByb3BlcnR5IiwKICAgICAgICAgICAgICAgInNvdXJjZSIgOiB7CiAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIxNjE6NTQtMTYxOjU4IiwKICAgICAgICAgICAgICAgICAgIm5hbWUiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk9wZXJhbmRSZWYiCiAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgfSwKICAgICAgICAgICAgIm9wZXJhbmQiIDogWyB7CiAgICAgICAgICAgICAgICJuYW1lIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICJvcGVyYW5kVHlwZVNwZWNpZmllciIgOiB7CiAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIxNjE6MzItMTYxOjUwIiwKICAgICAgICAgICAgICAgICAgIm5hbWUiIDogIntodHRwOi8vaGw3Lm9yZy9maGlyfUZhbWlseUhpc3RvcnlTdGF0dXMiLAogICAgICAgICAgICAgICAgICAidHlwZSIgOiAiTmFtZWRUeXBlU3BlY2lmaWVyIgogICAgICAgICAgICAgICB9CiAgICAgICAgICAgIH0gXQogICAgICAgICB9LCB7CiAgICAgICAgICAgICJsb2NhdG9yIiA6ICIxNjI6MS0xNjI6NTkiLAogICAgICAgICAgICAibmFtZSIgOiAiVG9TdHJpbmciLAogICAgICAgICAgICAiY29udGV4dCIgOiAiUGF0aWVudCIsCiAgICAgICAgICAgICJhY2Nlc3NMZXZlbCIgOiAiUHVibGljIiwKICAgICAgICAgICAgInR5cGUiIDogIkZ1bmN0aW9uRGVmIiwKICAgICAgICAgICAgImV4cHJlc3Npb24iIDogewogICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMTYyOjQ5LTE2Mjo1OSIsCiAgICAgICAgICAgICAgICJwYXRoIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICJ0eXBlIiA6ICJQcm9wZXJ0eSIsCiAgICAgICAgICAgICAgICJzb3VyY2UiIDogewogICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMTYyOjQ5LTE2Mjo1MyIsCiAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJPcGVyYW5kUmVmIgogICAgICAgICAgICAgICB9CiAgICAgICAgICAgIH0sCiAgICAgICAgICAgICJvcGVyYW5kIiA6IFsgewogICAgICAgICAgICAgICAibmFtZSIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAib3BlcmFuZFR5cGVTcGVjaWZpZXIiIDogewogICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMTYyOjMyLTE2Mjo0NSIsCiAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJ7aHR0cDovL2hsNy5vcmcvZmhpcn1GaWx0ZXJPcGVyYXRvciIsCiAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJOYW1lZFR5cGVTcGVjaWZpZXIiCiAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgfSBdCiAgICAgICAgIH0sIHsKICAgICAgICAgICAgImxvY2F0b3IiIDogIjE2MzoxLTE2Mzo1NSIsCiAgICAgICAgICAgICJuYW1lIiA6ICJUb1N0cmluZyIsCiAgICAgICAgICAgICJjb250ZXh0IiA6ICJQYXRpZW50IiwKICAgICAgICAgICAgImFjY2Vzc0xldmVsIiA6ICJQdWJsaWMiLAogICAgICAgICAgICAidHlwZSIgOiAiRnVuY3Rpb25EZWYiLAogICAgICAgICAgICAiZXhwcmVzc2lvbiIgOiB7CiAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIxNjM6NDUtMTYzOjU1IiwKICAgICAgICAgICAgICAgInBhdGgiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgInR5cGUiIDogIlByb3BlcnR5IiwKICAgICAgICAgICAgICAgInNvdXJjZSIgOiB7CiAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIxNjM6NDUtMTYzOjQ5IiwKICAgICAgICAgICAgICAgICAgIm5hbWUiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk9wZXJhbmRSZWYiCiAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgfSwKICAgICAgICAgICAgIm9wZXJhbmQiIDogWyB7CiAgICAgICAgICAgICAgICJuYW1lIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICJvcGVyYW5kVHlwZVNwZWNpZmllciIgOiB7CiAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIxNjM6MzItMTYzOjQxIiwKICAgICAgICAgICAgICAgICAgIm5hbWUiIDogIntodHRwOi8vaGw3Lm9yZy9maGlyfUZsYWdTdGF0dXMiLAogICAgICAgICAgICAgICAgICAidHlwZSIgOiAiTmFtZWRUeXBlU3BlY2lmaWVyIgogICAgICAgICAgICAgICB9CiAgICAgICAgICAgIH0gXQogICAgICAgICB9LCB7CiAgICAgICAgICAgICJsb2NhdG9yIiA6ICIxNjQ6MS0xNjQ6NjQiLAogICAgICAgICAgICAibmFtZSIgOiAiVG9TdHJpbmciLAogICAgICAgICAgICAiY29udGV4dCIgOiAiUGF0aWVudCIsCiAgICAgICAgICAgICJhY2Nlc3NMZXZlbCIgOiAiUHVibGljIiwKICAgICAgICAgICAgInR5cGUiIDogIkZ1bmN0aW9uRGVmIiwKICAgICAgICAgICAgImV4cHJlc3Npb24iIDogewogICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMTY0OjU0LTE2NDo2NCIsCiAgICAgICAgICAgICAgICJwYXRoIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICJ0eXBlIiA6ICJQcm9wZXJ0eSIsCiAgICAgICAgICAgICAgICJzb3VyY2UiIDogewogICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMTY0OjU0LTE2NDo1OCIsCiAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJPcGVyYW5kUmVmIgogICAgICAgICAgICAgICB9CiAgICAgICAgICAgIH0sCiAgICAgICAgICAgICJvcGVyYW5kIiA6IFsgewogICAgICAgICAgICAgICAibmFtZSIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAib3BlcmFuZFR5cGVTcGVjaWZpZXIiIDogewogICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMTY0OjMyLTE2NDo1MCIsCiAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJ7aHR0cDovL2hsNy5vcmcvZmhpcn1Hb2FsTGlmZWN5Y2xlU3RhdHVzIiwKICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk5hbWVkVHlwZVNwZWNpZmllciIKICAgICAgICAgICAgICAgfQogICAgICAgICAgICB9IF0KICAgICAgICAgfSwgewogICAgICAgICAgICAibG9jYXRvciIgOiAiMTY1OjEtMTY1OjY1IiwKICAgICAgICAgICAgIm5hbWUiIDogIlRvU3RyaW5nIiwKICAgICAgICAgICAgImNvbnRleHQiIDogIlBhdGllbnQiLAogICAgICAgICAgICAiYWNjZXNzTGV2ZWwiIDogIlB1YmxpYyIsCiAgICAgICAgICAgICJ0eXBlIiA6ICJGdW5jdGlvbkRlZiIsCiAgICAgICAgICAgICJleHByZXNzaW9uIiA6IHsKICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjE2NTo1NS0xNjU6NjUiLAogICAgICAgICAgICAgICAicGF0aCIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAidHlwZSIgOiAiUHJvcGVydHkiLAogICAgICAgICAgICAgICAic291cmNlIiA6IHsKICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjE2NTo1NS0xNjU6NTkiLAogICAgICAgICAgICAgICAgICAibmFtZSIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAgICAidHlwZSIgOiAiT3BlcmFuZFJlZiIKICAgICAgICAgICAgICAgfQogICAgICAgICAgICB9LAogICAgICAgICAgICAib3BlcmFuZCIgOiBbIHsKICAgICAgICAgICAgICAgIm5hbWUiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgIm9wZXJhbmRUeXBlU3BlY2lmaWVyIiA6IHsKICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjE2NTozMi0xNjU6NTEiLAogICAgICAgICAgICAgICAgICAibmFtZSIgOiAie2h0dHA6Ly9obDcub3JnL2ZoaXJ9R3JhcGhDb21wYXJ0bWVudFJ1bGUiLAogICAgICAgICAgICAgICAgICAidHlwZSIgOiAiTmFtZWRUeXBlU3BlY2lmaWVyIgogICAgICAgICAgICAgICB9CiAgICAgICAgICAgIH0gXQogICAgICAgICB9LCB7CiAgICAgICAgICAgICJsb2NhdG9yIiA6ICIxNjY6MS0xNjY6NjQiLAogICAgICAgICAgICAibmFtZSIgOiAiVG9TdHJpbmciLAogICAgICAgICAgICAiY29udGV4dCIgOiAiUGF0aWVudCIsCiAgICAgICAgICAgICJhY2Nlc3NMZXZlbCIgOiAiUHVibGljIiwKICAgICAgICAgICAgInR5cGUiIDogIkZ1bmN0aW9uRGVmIiwKICAgICAgICAgICAgImV4cHJlc3Npb24iIDogewogICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMTY2OjU0LTE2Njo2NCIsCiAgICAgICAgICAgICAgICJwYXRoIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICJ0eXBlIiA6ICJQcm9wZXJ0eSIsCiAgICAgICAgICAgICAgICJzb3VyY2UiIDogewogICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMTY2OjU0LTE2Njo1OCIsCiAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJPcGVyYW5kUmVmIgogICAgICAgICAgICAgICB9CiAgICAgICAgICAgIH0sCiAgICAgICAgICAgICJvcGVyYW5kIiA6IFsgewogICAgICAgICAgICAgICAibmFtZSIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAib3BlcmFuZFR5cGVTcGVjaWZpZXIiIDogewogICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMTY2OjMyLTE2Njo1MCIsCiAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJ7aHR0cDovL2hsNy5vcmcvZmhpcn1HcmFwaENvbXBhcnRtZW50VXNlIiwKICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk5hbWVkVHlwZVNwZWNpZmllciIKICAgICAgICAgICAgICAgfQogICAgICAgICAgICB9IF0KICAgICAgICAgfSwgewogICAgICAgICAgICAibG9jYXRvciIgOiAiMTY3OjEtMTY3OjU3IiwKICAgICAgICAgICAgIm5hbWUiIDogIlRvU3RyaW5nIiwKICAgICAgICAgICAgImNvbnRleHQiIDogIlBhdGllbnQiLAogICAgICAgICAgICAiYWNjZXNzTGV2ZWwiIDogIlB1YmxpYyIsCiAgICAgICAgICAgICJ0eXBlIiA6ICJGdW5jdGlvbkRlZiIsCiAgICAgICAgICAgICJleHByZXNzaW9uIiA6IHsKICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjE2Nzo0Ny0xNjc6NTciLAogICAgICAgICAgICAgICAicGF0aCIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAidHlwZSIgOiAiUHJvcGVydHkiLAogICAgICAgICAgICAgICAic291cmNlIiA6IHsKICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjE2Nzo0Ny0xNjc6NTEiLAogICAgICAgICAgICAgICAgICAibmFtZSIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAgICAidHlwZSIgOiAiT3BlcmFuZFJlZiIKICAgICAgICAgICAgICAgfQogICAgICAgICAgICB9LAogICAgICAgICAgICAib3BlcmFuZCIgOiBbIHsKICAgICAgICAgICAgICAgIm5hbWUiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgIm9wZXJhbmRUeXBlU3BlY2lmaWVyIiA6IHsKICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjE2NzozMi0xNjc6NDMiLAogICAgICAgICAgICAgICAgICAibmFtZSIgOiAie2h0dHA6Ly9obDcub3JnL2ZoaXJ9R3JvdXBNZWFzdXJlIiwKICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk5hbWVkVHlwZVNwZWNpZmllciIKICAgICAgICAgICAgICAgfQogICAgICAgICAgICB9IF0KICAgICAgICAgfSwgewogICAgICAgICAgICAibG9jYXRvciIgOiAiMTY4OjEtMTY4OjU0IiwKICAgICAgICAgICAgIm5hbWUiIDogIlRvU3RyaW5nIiwKICAgICAgICAgICAgImNvbnRleHQiIDogIlBhdGllbnQiLAogICAgICAgICAgICAiYWNjZXNzTGV2ZWwiIDogIlB1YmxpYyIsCiAgICAgICAgICAgICJ0eXBlIiA6ICJGdW5jdGlvbkRlZiIsCiAgICAgICAgICAgICJleHByZXNzaW9uIiA6IHsKICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjE2ODo0NC0xNjg6NTQiLAogICAgICAgICAgICAgICAicGF0aCIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAidHlwZSIgOiAiUHJvcGVydHkiLAogICAgICAgICAgICAgICAic291cmNlIiA6IHsKICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjE2ODo0NC0xNjg6NDgiLAogICAgICAgICAgICAgICAgICAibmFtZSIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAgICAidHlwZSIgOiAiT3BlcmFuZFJlZiIKICAgICAgICAgICAgICAgfQogICAgICAgICAgICB9LAogICAgICAgICAgICAib3BlcmFuZCIgOiBbIHsKICAgICAgICAgICAgICAgIm5hbWUiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgIm9wZXJhbmRUeXBlU3BlY2lmaWVyIiA6IHsKICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjE2ODozMi0xNjg6NDAiLAogICAgICAgICAgICAgICAgICAibmFtZSIgOiAie2h0dHA6Ly9obDcub3JnL2ZoaXJ9R3JvdXBUeXBlIiwKICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk5hbWVkVHlwZVNwZWNpZmllciIKICAgICAgICAgICAgICAgfQogICAgICAgICAgICB9IF0KICAgICAgICAgfSwgewogICAgICAgICAgICAibG9jYXRvciIgOiAiMTY5OjEtMTY5OjY3IiwKICAgICAgICAgICAgIm5hbWUiIDogIlRvU3RyaW5nIiwKICAgICAgICAgICAgImNvbnRleHQiIDogIlBhdGllbnQiLAogICAgICAgICAgICAiYWNjZXNzTGV2ZWwiIDogIlB1YmxpYyIsCiAgICAgICAgICAgICJ0eXBlIiA6ICJGdW5jdGlvbkRlZiIsCiAgICAgICAgICAgICJleHByZXNzaW9uIiA6IHsKICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjE2OTo1Ny0xNjk6NjciLAogICAgICAgICAgICAgICAicGF0aCIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAidHlwZSIgOiAiUHJvcGVydHkiLAogICAgICAgICAgICAgICAic291cmNlIiA6IHsKICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjE2OTo1Ny0xNjk6NjEiLAogICAgICAgICAgICAgICAgICAibmFtZSIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAgICAidHlwZSIgOiAiT3BlcmFuZFJlZiIKICAgICAgICAgICAgICAgfQogICAgICAgICAgICB9LAogICAgICAgICAgICAib3BlcmFuZCIgOiBbIHsKICAgICAgICAgICAgICAgIm5hbWUiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgIm9wZXJhbmRUeXBlU3BlY2lmaWVyIiA6IHsKICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjE2OTozMi0xNjk6NTMiLAogICAgICAgICAgICAgICAgICAibmFtZSIgOiAie2h0dHA6Ly9obDcub3JnL2ZoaXJ9R3VpZGFuY2VSZXNwb25zZVN0YXR1cyIsCiAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJOYW1lZFR5cGVTcGVjaWZpZXIiCiAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgfSBdCiAgICAgICAgIH0sIHsKICAgICAgICAgICAgImxvY2F0b3IiIDogIjE3MDoxLTE3MDo2NCIsCiAgICAgICAgICAgICJuYW1lIiA6ICJUb1N0cmluZyIsCiAgICAgICAgICAgICJjb250ZXh0IiA6ICJQYXRpZW50IiwKICAgICAgICAgICAgImFjY2Vzc0xldmVsIiA6ICJQdWJsaWMiLAogICAgICAgICAgICAidHlwZSIgOiAiRnVuY3Rpb25EZWYiLAogICAgICAgICAgICAiZXhwcmVzc2lvbiIgOiB7CiAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIxNzA6NTQtMTcwOjY0IiwKICAgICAgICAgICAgICAgInBhdGgiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgInR5cGUiIDogIlByb3BlcnR5IiwKICAgICAgICAgICAgICAgInNvdXJjZSIgOiB7CiAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIxNzA6NTQtMTcwOjU4IiwKICAgICAgICAgICAgICAgICAgIm5hbWUiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk9wZXJhbmRSZWYiCiAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgfSwKICAgICAgICAgICAgIm9wZXJhbmQiIDogWyB7CiAgICAgICAgICAgICAgICJuYW1lIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICJvcGVyYW5kVHlwZVNwZWNpZmllciIgOiB7CiAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIxNzA6MzItMTcwOjUwIiwKICAgICAgICAgICAgICAgICAgIm5hbWUiIDogIntodHRwOi8vaGw3Lm9yZy9maGlyfUd1aWRlUGFnZUdlbmVyYXRpb24iLAogICAgICAgICAgICAgICAgICAidHlwZSIgOiAiTmFtZWRUeXBlU3BlY2lmaWVyIgogICAgICAgICAgICAgICB9CiAgICAgICAgICAgIH0gXQogICAgICAgICB9LCB7CiAgICAgICAgICAgICJsb2NhdG9yIiA6ICIxNzE6MS0xNzE6NjMiLAogICAgICAgICAgICAibmFtZSIgOiAiVG9TdHJpbmciLAogICAgICAgICAgICAiY29udGV4dCIgOiAiUGF0aWVudCIsCiAgICAgICAgICAgICJhY2Nlc3NMZXZlbCIgOiAiUHVibGljIiwKICAgICAgICAgICAgInR5cGUiIDogIkZ1bmN0aW9uRGVmIiwKICAgICAgICAgICAgImV4cHJlc3Npb24iIDogewogICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMTcxOjUzLTE3MTo2MyIsCiAgICAgICAgICAgICAgICJwYXRoIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICJ0eXBlIiA6ICJQcm9wZXJ0eSIsCiAgICAgICAgICAgICAgICJzb3VyY2UiIDogewogICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMTcxOjUzLTE3MTo1NyIsCiAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJPcGVyYW5kUmVmIgogICAgICAgICAgICAgICB9CiAgICAgICAgICAgIH0sCiAgICAgICAgICAgICJvcGVyYW5kIiA6IFsgewogICAgICAgICAgICAgICAibmFtZSIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAib3BlcmFuZFR5cGVTcGVjaWZpZXIiIDogewogICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMTcxOjMyLTE3MTo0OSIsCiAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJ7aHR0cDovL2hsNy5vcmcvZmhpcn1HdWlkZVBhcmFtZXRlckNvZGUiLAogICAgICAgICAgICAgICAgICAidHlwZSIgOiAiTmFtZWRUeXBlU3BlY2lmaWVyIgogICAgICAgICAgICAgICB9CiAgICAgICAgICAgIH0gXQogICAgICAgICB9LCB7CiAgICAgICAgICAgICJsb2NhdG9yIiA6ICIxNzI6MS0xNzI6NTMiLAogICAgICAgICAgICAibmFtZSIgOiAiVG9TdHJpbmciLAogICAgICAgICAgICAiY29udGV4dCIgOiAiUGF0aWVudCIsCiAgICAgICAgICAgICJhY2Nlc3NMZXZlbCIgOiAiUHVibGljIiwKICAgICAgICAgICAgInR5cGUiIDogIkZ1bmN0aW9uRGVmIiwKICAgICAgICAgICAgImV4cHJlc3Npb24iIDogewogICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMTcyOjQzLTE3Mjo1MyIsCiAgICAgICAgICAgICAgICJwYXRoIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICJ0eXBlIiA6ICJQcm9wZXJ0eSIsCiAgICAgICAgICAgICAgICJzb3VyY2UiIDogewogICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMTcyOjQzLTE3Mjo0NyIsCiAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJPcGVyYW5kUmVmIgogICAgICAgICAgICAgICB9CiAgICAgICAgICAgIH0sCiAgICAgICAgICAgICJvcGVyYW5kIiA6IFsgewogICAgICAgICAgICAgICAibmFtZSIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAib3BlcmFuZFR5cGVTcGVjaWZpZXIiIDogewogICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMTcyOjMyLTE3MjozOSIsCiAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJ7aHR0cDovL2hsNy5vcmcvZmhpcn1IVFRQVmVyYiIsCiAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJOYW1lZFR5cGVTcGVjaWZpZXIiCiAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgfSBdCiAgICAgICAgIH0sIHsKICAgICAgICAgICAgImxvY2F0b3IiIDogIjE3MzoxLTE3Mzo1OCIsCiAgICAgICAgICAgICJuYW1lIiA6ICJUb1N0cmluZyIsCiAgICAgICAgICAgICJjb250ZXh0IiA6ICJQYXRpZW50IiwKICAgICAgICAgICAgImFjY2Vzc0xldmVsIiA6ICJQdWJsaWMiLAogICAgICAgICAgICAidHlwZSIgOiAiRnVuY3Rpb25EZWYiLAogICAgICAgICAgICAiZXhwcmVzc2lvbiIgOiB7CiAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIxNzM6NDgtMTczOjU4IiwKICAgICAgICAgICAgICAgInBhdGgiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgInR5cGUiIDogIlByb3BlcnR5IiwKICAgICAgICAgICAgICAgInNvdXJjZSIgOiB7CiAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIxNzM6NDgtMTczOjUyIiwKICAgICAgICAgICAgICAgICAgIm5hbWUiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk9wZXJhbmRSZWYiCiAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgfSwKICAgICAgICAgICAgIm9wZXJhbmQiIDogWyB7CiAgICAgICAgICAgICAgICJuYW1lIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICJvcGVyYW5kVHlwZVNwZWNpZmllciIgOiB7CiAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIxNzM6MzItMTczOjQ0IiwKICAgICAgICAgICAgICAgICAgIm5hbWUiIDogIntodHRwOi8vaGw3Lm9yZy9maGlyfUlkZW50aWZpZXJVc2UiLAogICAgICAgICAgICAgICAgICAidHlwZSIgOiAiTmFtZWRUeXBlU3BlY2lmaWVyIgogICAgICAgICAgICAgICB9CiAgICAgICAgICAgIH0gXQogICAgICAgICB9LCB7CiAgICAgICAgICAgICJsb2NhdG9yIiA6ICIxNzQ6MS0xNzQ6NjciLAogICAgICAgICAgICAibmFtZSIgOiAiVG9TdHJpbmciLAogICAgICAgICAgICAiY29udGV4dCIgOiAiUGF0aWVudCIsCiAgICAgICAgICAgICJhY2Nlc3NMZXZlbCIgOiAiUHVibGljIiwKICAgICAgICAgICAgInR5cGUiIDogIkZ1bmN0aW9uRGVmIiwKICAgICAgICAgICAgImV4cHJlc3Npb24iIDogewogICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMTc0OjU3LTE3NDo2NyIsCiAgICAgICAgICAgICAgICJwYXRoIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICJ0eXBlIiA6ICJQcm9wZXJ0eSIsCiAgICAgICAgICAgICAgICJzb3VyY2UiIDogewogICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMTc0OjU3LTE3NDo2MSIsCiAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJPcGVyYW5kUmVmIgogICAgICAgICAgICAgICB9CiAgICAgICAgICAgIH0sCiAgICAgICAgICAgICJvcGVyYW5kIiA6IFsgewogICAgICAgICAgICAgICAibmFtZSIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAib3BlcmFuZFR5cGVTcGVjaWZpZXIiIDogewogICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMTc0OjMyLTE3NDo1MyIsCiAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJ7aHR0cDovL2hsNy5vcmcvZmhpcn1JZGVudGl0eUFzc3VyYW5jZUxldmVsIiwKICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk5hbWVkVHlwZVNwZWNpZmllciIKICAgICAgICAgICAgICAgfQogICAgICAgICAgICB9IF0KICAgICAgICAgfSwgewogICAgICAgICAgICAibG9jYXRvciIgOiAiMTc1OjEtMTc1OjYzIiwKICAgICAgICAgICAgIm5hbWUiIDogIlRvU3RyaW5nIiwKICAgICAgICAgICAgImNvbnRleHQiIDogIlBhdGllbnQiLAogICAgICAgICAgICAiYWNjZXNzTGV2ZWwiIDogIlB1YmxpYyIsCiAgICAgICAgICAgICJ0eXBlIiA6ICJGdW5jdGlvbkRlZiIsCiAgICAgICAgICAgICJleHByZXNzaW9uIiA6IHsKICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjE3NTo1My0xNzU6NjMiLAogICAgICAgICAgICAgICAicGF0aCIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAidHlwZSIgOiAiUHJvcGVydHkiLAogICAgICAgICAgICAgICAic291cmNlIiA6IHsKICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjE3NTo1My0xNzU6NTciLAogICAgICAgICAgICAgICAgICAibmFtZSIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAgICAidHlwZSIgOiAiT3BlcmFuZFJlZiIKICAgICAgICAgICAgICAgfQogICAgICAgICAgICB9LAogICAgICAgICAgICAib3BlcmFuZCIgOiBbIHsKICAgICAgICAgICAgICAgIm5hbWUiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgIm9wZXJhbmRUeXBlU3BlY2lmaWVyIiA6IHsKICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjE3NTozMi0xNzU6NDkiLAogICAgICAgICAgICAgICAgICAibmFtZSIgOiAie2h0dHA6Ly9obDcub3JnL2ZoaXJ9SW1hZ2luZ1N0dWR5U3RhdHVzIiwKICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk5hbWVkVHlwZVNwZWNpZmllciIKICAgICAgICAgICAgICAgfQogICAgICAgICAgICB9IF0KICAgICAgICAgfSwgewogICAgICAgICAgICAibG9jYXRvciIgOiAiMTc2OjEtMTc2OjczIiwKICAgICAgICAgICAgIm5hbWUiIDogIlRvU3RyaW5nIiwKICAgICAgICAgICAgImNvbnRleHQiIDogIlBhdGllbnQiLAogICAgICAgICAgICAiYWNjZXNzTGV2ZWwiIDogIlB1YmxpYyIsCiAgICAgICAgICAgICJ0eXBlIiA6ICJGdW5jdGlvbkRlZiIsCiAgICAgICAgICAgICJleHByZXNzaW9uIiA6IHsKICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjE3Njo2My0xNzY6NzMiLAogICAgICAgICAgICAgICAicGF0aCIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAidHlwZSIgOiAiUHJvcGVydHkiLAogICAgICAgICAgICAgICAic291cmNlIiA6IHsKICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjE3Njo2My0xNzY6NjciLAogICAgICAgICAgICAgICAgICAibmFtZSIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAgICAidHlwZSIgOiAiT3BlcmFuZFJlZiIKICAgICAgICAgICAgICAgfQogICAgICAgICAgICB9LAogICAgICAgICAgICAib3BlcmFuZCIgOiBbIHsKICAgICAgICAgICAgICAgIm5hbWUiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgIm9wZXJhbmRUeXBlU3BlY2lmaWVyIiA6IHsKICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjE3NjozMi0xNzY6NTkiLAogICAgICAgICAgICAgICAgICAibmFtZSIgOiAie2h0dHA6Ly9obDcub3JnL2ZoaXJ9SW1tdW5pemF0aW9uRXZhbHVhdGlvblN0YXR1cyIsCiAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJOYW1lZFR5cGVTcGVjaWZpZXIiCiAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgfSBdCiAgICAgICAgIH0sIHsKICAgICAgICAgICAgImxvY2F0b3IiIDogIjE3NzoxLTE3Nzo2MyIsCiAgICAgICAgICAgICJuYW1lIiA6ICJUb1N0cmluZyIsCiAgICAgICAgICAgICJjb250ZXh0IiA6ICJQYXRpZW50IiwKICAgICAgICAgICAgImFjY2Vzc0xldmVsIiA6ICJQdWJsaWMiLAogICAgICAgICAgICAidHlwZSIgOiAiRnVuY3Rpb25EZWYiLAogICAgICAgICAgICAiZXhwcmVzc2lvbiIgOiB7CiAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIxNzc6NTMtMTc3OjYzIiwKICAgICAgICAgICAgICAgInBhdGgiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgInR5cGUiIDogIlByb3BlcnR5IiwKICAgICAgICAgICAgICAgInNvdXJjZSIgOiB7CiAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIxNzc6NTMtMTc3OjU3IiwKICAgICAgICAgICAgICAgICAgIm5hbWUiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk9wZXJhbmRSZWYiCiAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgfSwKICAgICAgICAgICAgIm9wZXJhbmQiIDogWyB7CiAgICAgICAgICAgICAgICJuYW1lIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICJvcGVyYW5kVHlwZVNwZWNpZmllciIgOiB7CiAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIxNzc6MzItMTc3OjQ5IiwKICAgICAgICAgICAgICAgICAgIm5hbWUiIDogIntodHRwOi8vaGw3Lm9yZy9maGlyfUltbXVuaXphdGlvblN0YXR1cyIsCiAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJOYW1lZFR5cGVTcGVjaWZpZXIiCiAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgfSBdCiAgICAgICAgIH0sIHsKICAgICAgICAgICAgImxvY2F0b3IiIDogIjE3ODoxLTE3ODo3MCIsCiAgICAgICAgICAgICJuYW1lIiA6ICJUb1N0cmluZyIsCiAgICAgICAgICAgICJjb250ZXh0IiA6ICJQYXRpZW50IiwKICAgICAgICAgICAgImFjY2Vzc0xldmVsIiA6ICJQdWJsaWMiLAogICAgICAgICAgICAidHlwZSIgOiAiRnVuY3Rpb25EZWYiLAogICAgICAgICAgICAiZXhwcmVzc2lvbiIgOiB7CiAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIxNzg6NjAtMTc4OjcwIiwKICAgICAgICAgICAgICAgInBhdGgiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgInR5cGUiIDogIlByb3BlcnR5IiwKICAgICAgICAgICAgICAgInNvdXJjZSIgOiB7CiAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIxNzg6NjAtMTc4OjY0IiwKICAgICAgICAgICAgICAgICAgIm5hbWUiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk9wZXJhbmRSZWYiCiAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgfSwKICAgICAgICAgICAgIm9wZXJhbmQiIDogWyB7CiAgICAgICAgICAgICAgICJuYW1lIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICJvcGVyYW5kVHlwZVNwZWNpZmllciIgOiB7CiAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIxNzg6MzItMTc4OjU2IiwKICAgICAgICAgICAgICAgICAgIm5hbWUiIDogIntodHRwOi8vaGw3Lm9yZy9maGlyfUludm9pY2VQcmljZUNvbXBvbmVudFR5cGUiLAogICAgICAgICAgICAgICAgICAidHlwZSIgOiAiTmFtZWRUeXBlU3BlY2lmaWVyIgogICAgICAgICAgICAgICB9CiAgICAgICAgICAgIH0gXQogICAgICAgICB9LCB7CiAgICAgICAgICAgICJsb2NhdG9yIiA6ICIxNzk6MS0xNzk6NTgiLAogICAgICAgICAgICAibmFtZSIgOiAiVG9TdHJpbmciLAogICAgICAgICAgICAiY29udGV4dCIgOiAiUGF0aWVudCIsCiAgICAgICAgICAgICJhY2Nlc3NMZXZlbCIgOiAiUHVibGljIiwKICAgICAgICAgICAgInR5cGUiIDogIkZ1bmN0aW9uRGVmIiwKICAgICAgICAgICAgImV4cHJlc3Npb24iIDogewogICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMTc5OjQ4LTE3OTo1OCIsCiAgICAgICAgICAgICAgICJwYXRoIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICJ0eXBlIiA6ICJQcm9wZXJ0eSIsCiAgICAgICAgICAgICAgICJzb3VyY2UiIDogewogICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMTc5OjQ4LTE3OTo1MiIsCiAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJPcGVyYW5kUmVmIgogICAgICAgICAgICAgICB9CiAgICAgICAgICAgIH0sCiAgICAgICAgICAgICJvcGVyYW5kIiA6IFsgewogICAgICAgICAgICAgICAibmFtZSIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAib3BlcmFuZFR5cGVTcGVjaWZpZXIiIDogewogICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMTc5OjMyLTE3OTo0NCIsCiAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJ7aHR0cDovL2hsNy5vcmcvZmhpcn1JbnZvaWNlU3RhdHVzIiwKICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk5hbWVkVHlwZVNwZWNpZmllciIKICAgICAgICAgICAgICAgfQogICAgICAgICAgICB9IF0KICAgICAgICAgfSwgewogICAgICAgICAgICAibG9jYXRvciIgOiAiMTgwOjEtMTgwOjU4IiwKICAgICAgICAgICAgIm5hbWUiIDogIlRvU3RyaW5nIiwKICAgICAgICAgICAgImNvbnRleHQiIDogIlBhdGllbnQiLAogICAgICAgICAgICAiYWNjZXNzTGV2ZWwiIDogIlB1YmxpYyIsCiAgICAgICAgICAgICJ0eXBlIiA6ICJGdW5jdGlvbkRlZiIsCiAgICAgICAgICAgICJleHByZXNzaW9uIiA6IHsKICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjE4MDo0OC0xODA6NTgiLAogICAgICAgICAgICAgICAicGF0aCIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAidHlwZSIgOiAiUHJvcGVydHkiLAogICAgICAgICAgICAgICAic291cmNlIiA6IHsKICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjE4MDo0OC0xODA6NTIiLAogICAgICAgICAgICAgICAgICAibmFtZSIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAgICAidHlwZSIgOiAiT3BlcmFuZFJlZiIKICAgICAgICAgICAgICAgfQogICAgICAgICAgICB9LAogICAgICAgICAgICAib3BlcmFuZCIgOiBbIHsKICAgICAgICAgICAgICAgIm5hbWUiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgIm9wZXJhbmRUeXBlU3BlY2lmaWVyIiA6IHsKICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjE4MDozMi0xODA6NDQiLAogICAgICAgICAgICAgICAgICAibmFtZSIgOiAie2h0dHA6Ly9obDcub3JnL2ZoaXJ9SXNzdWVTZXZlcml0eSIsCiAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJOYW1lZFR5cGVTcGVjaWZpZXIiCiAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgfSBdCiAgICAgICAgIH0sIHsKICAgICAgICAgICAgImxvY2F0b3IiIDogIjE4MToxLTE4MTo1NCIsCiAgICAgICAgICAgICJuYW1lIiA6ICJUb1N0cmluZyIsCiAgICAgICAgICAgICJjb250ZXh0IiA6ICJQYXRpZW50IiwKICAgICAgICAgICAgImFjY2Vzc0xldmVsIiA6ICJQdWJsaWMiLAogICAgICAgICAgICAidHlwZSIgOiAiRnVuY3Rpb25EZWYiLAogICAgICAgICAgICAiZXhwcmVzc2lvbiIgOiB7CiAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIxODE6NDQtMTgxOjU0IiwKICAgICAgICAgICAgICAgInBhdGgiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgInR5cGUiIDogIlByb3BlcnR5IiwKICAgICAgICAgICAgICAgInNvdXJjZSIgOiB7CiAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIxODE6NDQtMTgxOjQ4IiwKICAgICAgICAgICAgICAgICAgIm5hbWUiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk9wZXJhbmRSZWYiCiAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgfSwKICAgICAgICAgICAgIm9wZXJhbmQiIDogWyB7CiAgICAgICAgICAgICAgICJuYW1lIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICJvcGVyYW5kVHlwZVNwZWNpZmllciIgOiB7CiAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIxODE6MzItMTgxOjQwIiwKICAgICAgICAgICAgICAgICAgIm5hbWUiIDogIntodHRwOi8vaGw3Lm9yZy9maGlyfUlzc3VlVHlwZSIsCiAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJOYW1lZFR5cGVTcGVjaWZpZXIiCiAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgfSBdCiAgICAgICAgIH0sIHsKICAgICAgICAgICAgImxvY2F0b3IiIDogIjE4MjoxLTE4Mjo1MyIsCiAgICAgICAgICAgICJuYW1lIiA6ICJUb1N0cmluZyIsCiAgICAgICAgICAgICJjb250ZXh0IiA6ICJQYXRpZW50IiwKICAgICAgICAgICAgImFjY2Vzc0xldmVsIiA6ICJQdWJsaWMiLAogICAgICAgICAgICAidHlwZSIgOiAiRnVuY3Rpb25EZWYiLAogICAgICAgICAgICAiZXhwcmVzc2lvbiIgOiB7CiAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIxODI6NDMtMTgyOjUzIiwKICAgICAgICAgICAgICAgInBhdGgiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgInR5cGUiIDogIlByb3BlcnR5IiwKICAgICAgICAgICAgICAgInNvdXJjZSIgOiB7CiAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIxODI6NDMtMTgyOjQ3IiwKICAgICAgICAgICAgICAgICAgIm5hbWUiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk9wZXJhbmRSZWYiCiAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgfSwKICAgICAgICAgICAgIm9wZXJhbmQiIDogWyB7CiAgICAgICAgICAgICAgICJuYW1lIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICJvcGVyYW5kVHlwZVNwZWNpZmllciIgOiB7CiAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIxODI6MzItMTgyOjM5IiwKICAgICAgICAgICAgICAgICAgIm5hbWUiIDogIntodHRwOi8vaGw3Lm9yZy9maGlyfUxpbmtUeXBlIiwKICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk5hbWVkVHlwZVNwZWNpZmllciIKICAgICAgICAgICAgICAgfQogICAgICAgICAgICB9IF0KICAgICAgICAgfSwgewogICAgICAgICAgICAibG9jYXRvciIgOiAiMTgzOjEtMTgzOjU2IiwKICAgICAgICAgICAgIm5hbWUiIDogIlRvU3RyaW5nIiwKICAgICAgICAgICAgImNvbnRleHQiIDogIlBhdGllbnQiLAogICAgICAgICAgICAiYWNjZXNzTGV2ZWwiIDogIlB1YmxpYyIsCiAgICAgICAgICAgICJ0eXBlIiA6ICJGdW5jdGlvbkRlZiIsCiAgICAgICAgICAgICJleHByZXNzaW9uIiA6IHsKICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjE4Mzo0Ni0xODM6NTYiLAogICAgICAgICAgICAgICAicGF0aCIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAidHlwZSIgOiAiUHJvcGVydHkiLAogICAgICAgICAgICAgICAic291cmNlIiA6IHsKICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjE4Mzo0Ni0xODM6NTAiLAogICAgICAgICAgICAgICAgICAibmFtZSIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAgICAidHlwZSIgOiAiT3BlcmFuZFJlZiIKICAgICAgICAgICAgICAgfQogICAgICAgICAgICB9LAogICAgICAgICAgICAib3BlcmFuZCIgOiBbIHsKICAgICAgICAgICAgICAgIm5hbWUiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgIm9wZXJhbmRUeXBlU3BlY2lmaWVyIiA6IHsKICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjE4MzozMi0xODM6NDIiLAogICAgICAgICAgICAgICAgICAibmFtZSIgOiAie2h0dHA6Ly9obDcub3JnL2ZoaXJ9TGlua2FnZVR5cGUiLAogICAgICAgICAgICAgICAgICAidHlwZSIgOiAiTmFtZWRUeXBlU3BlY2lmaWVyIgogICAgICAgICAgICAgICB9CiAgICAgICAgICAgIH0gXQogICAgICAgICB9LCB7CiAgICAgICAgICAgICJsb2NhdG9yIiA6ICIxODQ6MS0xODQ6NTMiLAogICAgICAgICAgICAibmFtZSIgOiAiVG9TdHJpbmciLAogICAgICAgICAgICAiY29udGV4dCIgOiAiUGF0aWVudCIsCiAgICAgICAgICAgICJhY2Nlc3NMZXZlbCIgOiAiUHVibGljIiwKICAgICAgICAgICAgInR5cGUiIDogIkZ1bmN0aW9uRGVmIiwKICAgICAgICAgICAgImV4cHJlc3Npb24iIDogewogICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMTg0OjQzLTE4NDo1MyIsCiAgICAgICAgICAgICAgICJwYXRoIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICJ0eXBlIiA6ICJQcm9wZXJ0eSIsCiAgICAgICAgICAgICAgICJzb3VyY2UiIDogewogICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMTg0OjQzLTE4NDo0NyIsCiAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJPcGVyYW5kUmVmIgogICAgICAgICAgICAgICB9CiAgICAgICAgICAgIH0sCiAgICAgICAgICAgICJvcGVyYW5kIiA6IFsgewogICAgICAgICAgICAgICAibmFtZSIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAib3BlcmFuZFR5cGVTcGVjaWZpZXIiIDogewogICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMTg0OjMyLTE4NDozOSIsCiAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJ7aHR0cDovL2hsNy5vcmcvZmhpcn1MaXN0TW9kZSIsCiAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJOYW1lZFR5cGVTcGVjaWZpZXIiCiAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgfSBdCiAgICAgICAgIH0sIHsKICAgICAgICAgICAgImxvY2F0b3IiIDogIjE4NToxLTE4NTo1NSIsCiAgICAgICAgICAgICJuYW1lIiA6ICJUb1N0cmluZyIsCiAgICAgICAgICAgICJjb250ZXh0IiA6ICJQYXRpZW50IiwKICAgICAgICAgICAgImFjY2Vzc0xldmVsIiA6ICJQdWJsaWMiLAogICAgICAgICAgICAidHlwZSIgOiAiRnVuY3Rpb25EZWYiLAogICAgICAgICAgICAiZXhwcmVzc2lvbiIgOiB7CiAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIxODU6NDUtMTg1OjU1IiwKICAgICAgICAgICAgICAgInBhdGgiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgInR5cGUiIDogIlByb3BlcnR5IiwKICAgICAgICAgICAgICAgInNvdXJjZSIgOiB7CiAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIxODU6NDUtMTg1OjQ5IiwKICAgICAgICAgICAgICAgICAgIm5hbWUiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk9wZXJhbmRSZWYiCiAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgfSwKICAgICAgICAgICAgIm9wZXJhbmQiIDogWyB7CiAgICAgICAgICAgICAgICJuYW1lIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICJvcGVyYW5kVHlwZVNwZWNpZmllciIgOiB7CiAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIxODU6MzItMTg1OjQxIiwKICAgICAgICAgICAgICAgICAgIm5hbWUiIDogIntodHRwOi8vaGw3Lm9yZy9maGlyfUxpc3RTdGF0dXMiLAogICAgICAgICAgICAgICAgICAidHlwZSIgOiAiTmFtZWRUeXBlU3BlY2lmaWVyIgogICAgICAgICAgICAgICB9CiAgICAgICAgICAgIH0gXQogICAgICAgICB9LCB7CiAgICAgICAgICAgICJsb2NhdG9yIiA6ICIxODY6MS0xODY6NTciLAogICAgICAgICAgICAibmFtZSIgOiAiVG9TdHJpbmciLAogICAgICAgICAgICAiY29udGV4dCIgOiAiUGF0aWVudCIsCiAgICAgICAgICAgICJhY2Nlc3NMZXZlbCIgOiAiUHVibGljIiwKICAgICAgICAgICAgInR5cGUiIDogIkZ1bmN0aW9uRGVmIiwKICAgICAgICAgICAgImV4cHJlc3Npb24iIDogewogICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMTg2OjQ3LTE4Njo1NyIsCiAgICAgICAgICAgICAgICJwYXRoIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICJ0eXBlIiA6ICJQcm9wZXJ0eSIsCiAgICAgICAgICAgICAgICJzb3VyY2UiIDogewogICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMTg2OjQ3LTE4Njo1MSIsCiAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJPcGVyYW5kUmVmIgogICAgICAgICAgICAgICB9CiAgICAgICAgICAgIH0sCiAgICAgICAgICAgICJvcGVyYW5kIiA6IFsgewogICAgICAgICAgICAgICAibmFtZSIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAib3BlcmFuZFR5cGVTcGVjaWZpZXIiIDogewogICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMTg2OjMyLTE4Njo0MyIsCiAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJ7aHR0cDovL2hsNy5vcmcvZmhpcn1Mb2NhdGlvbk1vZGUiLAogICAgICAgICAgICAgICAgICAidHlwZSIgOiAiTmFtZWRUeXBlU3BlY2lmaWVyIgogICAgICAgICAgICAgICB9CiAgICAgICAgICAgIH0gXQogICAgICAgICB9LCB7CiAgICAgICAgICAgICJsb2NhdG9yIiA6ICIxODc6MS0xODc6NTkiLAogICAgICAgICAgICAibmFtZSIgOiAiVG9TdHJpbmciLAogICAgICAgICAgICAiY29udGV4dCIgOiAiUGF0aWVudCIsCiAgICAgICAgICAgICJhY2Nlc3NMZXZlbCIgOiAiUHVibGljIiwKICAgICAgICAgICAgInR5cGUiIDogIkZ1bmN0aW9uRGVmIiwKICAgICAgICAgICAgImV4cHJlc3Npb24iIDogewogICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMTg3OjQ5LTE4Nzo1OSIsCiAgICAgICAgICAgICAgICJwYXRoIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICJ0eXBlIiA6ICJQcm9wZXJ0eSIsCiAgICAgICAgICAgICAgICJzb3VyY2UiIDogewogICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMTg3OjQ5LTE4Nzo1MyIsCiAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJPcGVyYW5kUmVmIgogICAgICAgICAgICAgICB9CiAgICAgICAgICAgIH0sCiAgICAgICAgICAgICJvcGVyYW5kIiA6IFsgewogICAgICAgICAgICAgICAibmFtZSIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAib3BlcmFuZFR5cGVTcGVjaWZpZXIiIDogewogICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMTg3OjMyLTE4Nzo0NSIsCiAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJ7aHR0cDovL2hsNy5vcmcvZmhpcn1Mb2NhdGlvblN0YXR1cyIsCiAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJOYW1lZFR5cGVTcGVjaWZpZXIiCiAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgfSBdCiAgICAgICAgIH0sIHsKICAgICAgICAgICAgImxvY2F0b3IiIDogIjE4ODoxLTE4ODo2NCIsCiAgICAgICAgICAgICJuYW1lIiA6ICJUb1N0cmluZyIsCiAgICAgICAgICAgICJjb250ZXh0IiA6ICJQYXRpZW50IiwKICAgICAgICAgICAgImFjY2Vzc0xldmVsIiA6ICJQdWJsaWMiLAogICAgICAgICAgICAidHlwZSIgOiAiRnVuY3Rpb25EZWYiLAogICAgICAgICAgICAiZXhwcmVzc2lvbiIgOiB7CiAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIxODg6NTQtMTg4OjY0IiwKICAgICAgICAgICAgICAgInBhdGgiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgInR5cGUiIDogIlByb3BlcnR5IiwKICAgICAgICAgICAgICAgInNvdXJjZSIgOiB7CiAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIxODg6NTQtMTg4OjU4IiwKICAgICAgICAgICAgICAgICAgIm5hbWUiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk9wZXJhbmRSZWYiCiAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgfSwKICAgICAgICAgICAgIm9wZXJhbmQiIDogWyB7CiAgICAgICAgICAgICAgICJuYW1lIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICJvcGVyYW5kVHlwZVNwZWNpZmllciIgOiB7CiAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIxODg6MzItMTg4OjUwIiwKICAgICAgICAgICAgICAgICAgIm5hbWUiIDogIntodHRwOi8vaGw3Lm9yZy9maGlyfU1lYXN1cmVSZXBvcnRTdGF0dXMiLAogICAgICAgICAgICAgICAgICAidHlwZSIgOiAiTmFtZWRUeXBlU3BlY2lmaWVyIgogICAgICAgICAgICAgICB9CiAgICAgICAgICAgIH0gXQogICAgICAgICB9LCB7CiAgICAgICAgICAgICJsb2NhdG9yIiA6ICIxODk6MS0xODk6NjIiLAogICAgICAgICAgICAibmFtZSIgOiAiVG9TdHJpbmciLAogICAgICAgICAgICAiY29udGV4dCIgOiAiUGF0aWVudCIsCiAgICAgICAgICAgICJhY2Nlc3NMZXZlbCIgOiAiUHVibGljIiwKICAgICAgICAgICAgInR5cGUiIDogIkZ1bmN0aW9uRGVmIiwKICAgICAgICAgICAgImV4cHJlc3Npb24iIDogewogICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMTg5OjUyLTE4OTo2MiIsCiAgICAgICAgICAgICAgICJwYXRoIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICJ0eXBlIiA6ICJQcm9wZXJ0eSIsCiAgICAgICAgICAgICAgICJzb3VyY2UiIDogewogICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMTg5OjUyLTE4OTo1NiIsCiAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJPcGVyYW5kUmVmIgogICAgICAgICAgICAgICB9CiAgICAgICAgICAgIH0sCiAgICAgICAgICAgICJvcGVyYW5kIiA6IFsgewogICAgICAgICAgICAgICAibmFtZSIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAib3BlcmFuZFR5cGVTcGVjaWZpZXIiIDogewogICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMTg5OjMyLTE4OTo0OCIsCiAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJ7aHR0cDovL2hsNy5vcmcvZmhpcn1NZWFzdXJlUmVwb3J0VHlwZSIsCiAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJOYW1lZFR5cGVTcGVjaWZpZXIiCiAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgfSBdCiAgICAgICAgIH0sIHsKICAgICAgICAgICAgImxvY2F0b3IiIDogIjE5MDoxLTE5MDo1NiIsCiAgICAgICAgICAgICJuYW1lIiA6ICJUb1N0cmluZyIsCiAgICAgICAgICAgICJjb250ZXh0IiA6ICJQYXRpZW50IiwKICAgICAgICAgICAgImFjY2Vzc0xldmVsIiA6ICJQdWJsaWMiLAogICAgICAgICAgICAidHlwZSIgOiAiRnVuY3Rpb25EZWYiLAogICAgICAgICAgICAiZXhwcmVzc2lvbiIgOiB7CiAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIxOTA6NDYtMTkwOjU2IiwKICAgICAgICAgICAgICAgInBhdGgiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgInR5cGUiIDogIlByb3BlcnR5IiwKICAgICAgICAgICAgICAgInNvdXJjZSIgOiB7CiAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIxOTA6NDYtMTkwOjUwIiwKICAgICAgICAgICAgICAgICAgIm5hbWUiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk9wZXJhbmRSZWYiCiAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgfSwKICAgICAgICAgICAgIm9wZXJhbmQiIDogWyB7CiAgICAgICAgICAgICAgICJuYW1lIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICJvcGVyYW5kVHlwZVNwZWNpZmllciIgOiB7CiAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIxOTA6MzItMTkwOjQyIiwKICAgICAgICAgICAgICAgICAgIm5hbWUiIDogIntodHRwOi8vaGw3Lm9yZy9maGlyfU1lZGlhU3RhdHVzIiwKICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk5hbWVkVHlwZVNwZWNpZmllciIKICAgICAgICAgICAgICAgfQogICAgICAgICAgICB9IF0KICAgICAgICAgfSwgewogICAgICAgICAgICAibG9jYXRvciIgOiAiMTkxOjEtMTkxOjc1IiwKICAgICAgICAgICAgIm5hbWUiIDogIlRvU3RyaW5nIiwKICAgICAgICAgICAgImNvbnRleHQiIDogIlBhdGllbnQiLAogICAgICAgICAgICAiYWNjZXNzTGV2ZWwiIDogIlB1YmxpYyIsCiAgICAgICAgICAgICJ0eXBlIiA6ICJGdW5jdGlvbkRlZiIsCiAgICAgICAgICAgICJleHByZXNzaW9uIiA6IHsKICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjE5MTo2NS0xOTE6NzUiLAogICAgICAgICAgICAgICAicGF0aCIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAidHlwZSIgOiAiUHJvcGVydHkiLAogICAgICAgICAgICAgICAic291cmNlIiA6IHsKICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjE5MTo2NS0xOTE6NjkiLAogICAgICAgICAgICAgICAgICAibmFtZSIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAgICAidHlwZSIgOiAiT3BlcmFuZFJlZiIKICAgICAgICAgICAgICAgfQogICAgICAgICAgICB9LAogICAgICAgICAgICAib3BlcmFuZCIgOiBbIHsKICAgICAgICAgICAgICAgIm5hbWUiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgIm9wZXJhbmRUeXBlU3BlY2lmaWVyIiA6IHsKICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjE5MTozMi0xOTE6NjEiLAogICAgICAgICAgICAgICAgICAibmFtZSIgOiAie2h0dHA6Ly9obDcub3JnL2ZoaXJ9TWVkaWNhdGlvbkFkbWluaXN0cmF0aW9uU3RhdHVzIiwKICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk5hbWVkVHlwZVNwZWNpZmllciIKICAgICAgICAgICAgICAgfQogICAgICAgICAgICB9IF0KICAgICAgICAgfSwgewogICAgICAgICAgICAibG9jYXRvciIgOiAiMTkyOjEtMTkyOjY5IiwKICAgICAgICAgICAgIm5hbWUiIDogIlRvU3RyaW5nIiwKICAgICAgICAgICAgImNvbnRleHQiIDogIlBhdGllbnQiLAogICAgICAgICAgICAiYWNjZXNzTGV2ZWwiIDogIlB1YmxpYyIsCiAgICAgICAgICAgICJ0eXBlIiA6ICJGdW5jdGlvbkRlZiIsCiAgICAgICAgICAgICJleHByZXNzaW9uIiA6IHsKICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjE5Mjo1OS0xOTI6NjkiLAogICAgICAgICAgICAgICAicGF0aCIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAidHlwZSIgOiAiUHJvcGVydHkiLAogICAgICAgICAgICAgICAic291cmNlIiA6IHsKICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjE5Mjo1OS0xOTI6NjMiLAogICAgICAgICAgICAgICAgICAibmFtZSIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAgICAidHlwZSIgOiAiT3BlcmFuZFJlZiIKICAgICAgICAgICAgICAgfQogICAgICAgICAgICB9LAogICAgICAgICAgICAib3BlcmFuZCIgOiBbIHsKICAgICAgICAgICAgICAgIm5hbWUiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgIm9wZXJhbmRUeXBlU3BlY2lmaWVyIiA6IHsKICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjE5MjozMi0xOTI6NTUiLAogICAgICAgICAgICAgICAgICAibmFtZSIgOiAie2h0dHA6Ly9obDcub3JnL2ZoaXJ9TWVkaWNhdGlvbkRpc3BlbnNlU3RhdHVzIiwKICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk5hbWVkVHlwZVNwZWNpZmllciIKICAgICAgICAgICAgICAgfQogICAgICAgICAgICB9IF0KICAgICAgICAgfSwgewogICAgICAgICAgICAibG9jYXRvciIgOiAiMTkzOjEtMTkzOjcwIiwKICAgICAgICAgICAgIm5hbWUiIDogIlRvU3RyaW5nIiwKICAgICAgICAgICAgImNvbnRleHQiIDogIlBhdGllbnQiLAogICAgICAgICAgICAiYWNjZXNzTGV2ZWwiIDogIlB1YmxpYyIsCiAgICAgICAgICAgICJ0eXBlIiA6ICJGdW5jdGlvbkRlZiIsCiAgICAgICAgICAgICJleHByZXNzaW9uIiA6IHsKICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjE5Mzo2MC0xOTM6NzAiLAogICAgICAgICAgICAgICAicGF0aCIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAidHlwZSIgOiAiUHJvcGVydHkiLAogICAgICAgICAgICAgICAic291cmNlIiA6IHsKICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjE5Mzo2MC0xOTM6NjQiLAogICAgICAgICAgICAgICAgICAibmFtZSIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAgICAidHlwZSIgOiAiT3BlcmFuZFJlZiIKICAgICAgICAgICAgICAgfQogICAgICAgICAgICB9LAogICAgICAgICAgICAib3BlcmFuZCIgOiBbIHsKICAgICAgICAgICAgICAgIm5hbWUiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgIm9wZXJhbmRUeXBlU3BlY2lmaWVyIiA6IHsKICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjE5MzozMi0xOTM6NTYiLAogICAgICAgICAgICAgICAgICAibmFtZSIgOiAie2h0dHA6Ly9obDcub3JnL2ZoaXJ9TWVkaWNhdGlvbktub3dsZWRnZVN0YXR1cyIsCiAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJOYW1lZFR5cGVTcGVjaWZpZXIiCiAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgfSBdCiAgICAgICAgIH0sIHsKICAgICAgICAgICAgImxvY2F0b3IiIDogIjE5NDoxLTE5NDo2OCIsCiAgICAgICAgICAgICJuYW1lIiA6ICJUb1N0cmluZyIsCiAgICAgICAgICAgICJjb250ZXh0IiA6ICJQYXRpZW50IiwKICAgICAgICAgICAgImFjY2Vzc0xldmVsIiA6ICJQdWJsaWMiLAogICAgICAgICAgICAidHlwZSIgOiAiRnVuY3Rpb25EZWYiLAogICAgICAgICAgICAiZXhwcmVzc2lvbiIgOiB7CiAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIxOTQ6NTgtMTk0OjY4IiwKICAgICAgICAgICAgICAgInBhdGgiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgInR5cGUiIDogIlByb3BlcnR5IiwKICAgICAgICAgICAgICAgInNvdXJjZSIgOiB7CiAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIxOTQ6NTgtMTk0OjYyIiwKICAgICAgICAgICAgICAgICAgIm5hbWUiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk9wZXJhbmRSZWYiCiAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgfSwKICAgICAgICAgICAgIm9wZXJhbmQiIDogWyB7CiAgICAgICAgICAgICAgICJuYW1lIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICJvcGVyYW5kVHlwZVNwZWNpZmllciIgOiB7CiAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIxOTQ6MzItMTk0OjU0IiwKICAgICAgICAgICAgICAgICAgIm5hbWUiIDogIntodHRwOi8vaGw3Lm9yZy9maGlyfU1lZGljYXRpb25SZXF1ZXN0SW50ZW50IiwKICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk5hbWVkVHlwZVNwZWNpZmllciIKICAgICAgICAgICAgICAgfQogICAgICAgICAgICB9IF0KICAgICAgICAgfSwgewogICAgICAgICAgICAibG9jYXRvciIgOiAiMTk1OjEtMTk1OjcwIiwKICAgICAgICAgICAgIm5hbWUiIDogIlRvU3RyaW5nIiwKICAgICAgICAgICAgImNvbnRleHQiIDogIlBhdGllbnQiLAogICAgICAgICAgICAiYWNjZXNzTGV2ZWwiIDogIlB1YmxpYyIsCiAgICAgICAgICAgICJ0eXBlIiA6ICJGdW5jdGlvbkRlZiIsCiAgICAgICAgICAgICJleHByZXNzaW9uIiA6IHsKICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjE5NTo2MC0xOTU6NzAiLAogICAgICAgICAgICAgICAicGF0aCIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAidHlwZSIgOiAiUHJvcGVydHkiLAogICAgICAgICAgICAgICAic291cmNlIiA6IHsKICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjE5NTo2MC0xOTU6NjQiLAogICAgICAgICAgICAgICAgICAibmFtZSIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAgICAidHlwZSIgOiAiT3BlcmFuZFJlZiIKICAgICAgICAgICAgICAgfQogICAgICAgICAgICB9LAogICAgICAgICAgICAib3BlcmFuZCIgOiBbIHsKICAgICAgICAgICAgICAgIm5hbWUiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgIm9wZXJhbmRUeXBlU3BlY2lmaWVyIiA6IHsKICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjE5NTozMi0xOTU6NTYiLAogICAgICAgICAgICAgICAgICAibmFtZSIgOiAie2h0dHA6Ly9obDcub3JnL2ZoaXJ9TWVkaWNhdGlvblJlcXVlc3RQcmlvcml0eSIsCiAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJOYW1lZFR5cGVTcGVjaWZpZXIiCiAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgfSBdCiAgICAgICAgIH0sIHsKICAgICAgICAgICAgImxvY2F0b3IiIDogIjE5NjoxLTE5Njo2OCIsCiAgICAgICAgICAgICJuYW1lIiA6ICJUb1N0cmluZyIsCiAgICAgICAgICAgICJjb250ZXh0IiA6ICJQYXRpZW50IiwKICAgICAgICAgICAgImFjY2Vzc0xldmVsIiA6ICJQdWJsaWMiLAogICAgICAgICAgICAidHlwZSIgOiAiRnVuY3Rpb25EZWYiLAogICAgICAgICAgICAiZXhwcmVzc2lvbiIgOiB7CiAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIxOTY6NTgtMTk2OjY4IiwKICAgICAgICAgICAgICAgInBhdGgiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgInR5cGUiIDogIlByb3BlcnR5IiwKICAgICAgICAgICAgICAgInNvdXJjZSIgOiB7CiAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIxOTY6NTgtMTk2OjYyIiwKICAgICAgICAgICAgICAgICAgIm5hbWUiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk9wZXJhbmRSZWYiCiAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgfSwKICAgICAgICAgICAgIm9wZXJhbmQiIDogWyB7CiAgICAgICAgICAgICAgICJuYW1lIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICJvcGVyYW5kVHlwZVNwZWNpZmllciIgOiB7CiAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIxOTY6MzItMTk2OjU0IiwKICAgICAgICAgICAgICAgICAgIm5hbWUiIDogIntodHRwOi8vaGw3Lm9yZy9maGlyfU1lZGljYXRpb25SZXF1ZXN0U3RhdHVzIiwKICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk5hbWVkVHlwZVNwZWNpZmllciIKICAgICAgICAgICAgICAgfQogICAgICAgICAgICB9IF0KICAgICAgICAgfSwgewogICAgICAgICAgICAibG9jYXRvciIgOiAiMTk3OjEtMTk3OjcwIiwKICAgICAgICAgICAgIm5hbWUiIDogIlRvU3RyaW5nIiwKICAgICAgICAgICAgImNvbnRleHQiIDogIlBhdGllbnQiLAogICAgICAgICAgICAiYWNjZXNzTGV2ZWwiIDogIlB1YmxpYyIsCiAgICAgICAgICAgICJ0eXBlIiA6ICJGdW5jdGlvbkRlZiIsCiAgICAgICAgICAgICJleHByZXNzaW9uIiA6IHsKICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjE5Nzo2MC0xOTc6NzAiLAogICAgICAgICAgICAgICAicGF0aCIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAidHlwZSIgOiAiUHJvcGVydHkiLAogICAgICAgICAgICAgICAic291cmNlIiA6IHsKICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjE5Nzo2MC0xOTc6NjQiLAogICAgICAgICAgICAgICAgICAibmFtZSIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAgICAidHlwZSIgOiAiT3BlcmFuZFJlZiIKICAgICAgICAgICAgICAgfQogICAgICAgICAgICB9LAogICAgICAgICAgICAib3BlcmFuZCIgOiBbIHsKICAgICAgICAgICAgICAgIm5hbWUiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgIm9wZXJhbmRUeXBlU3BlY2lmaWVyIiA6IHsKICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjE5NzozMi0xOTc6NTYiLAogICAgICAgICAgICAgICAgICAibmFtZSIgOiAie2h0dHA6Ly9obDcub3JnL2ZoaXJ9TWVkaWNhdGlvblN0YXRlbWVudFN0YXR1cyIsCiAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJOYW1lZFR5cGVTcGVjaWZpZXIiCiAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgfSBdCiAgICAgICAgIH0sIHsKICAgICAgICAgICAgImxvY2F0b3IiIDogIjE5ODoxLTE5ODo2MSIsCiAgICAgICAgICAgICJuYW1lIiA6ICJUb1N0cmluZyIsCiAgICAgICAgICAgICJjb250ZXh0IiA6ICJQYXRpZW50IiwKICAgICAgICAgICAgImFjY2Vzc0xldmVsIiA6ICJQdWJsaWMiLAogICAgICAgICAgICAidHlwZSIgOiAiRnVuY3Rpb25EZWYiLAogICAgICAgICAgICAiZXhwcmVzc2lvbiIgOiB7CiAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIxOTg6NTEtMTk4OjYxIiwKICAgICAgICAgICAgICAgInBhdGgiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgInR5cGUiIDogIlByb3BlcnR5IiwKICAgICAgICAgICAgICAgInNvdXJjZSIgOiB7CiAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIxOTg6NTEtMTk4OjU1IiwKICAgICAgICAgICAgICAgICAgIm5hbWUiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk9wZXJhbmRSZWYiCiAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgfSwKICAgICAgICAgICAgIm9wZXJhbmQiIDogWyB7CiAgICAgICAgICAgICAgICJuYW1lIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICJvcGVyYW5kVHlwZVNwZWNpZmllciIgOiB7CiAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIxOTg6MzItMTk4OjQ3IiwKICAgICAgICAgICAgICAgICAgIm5hbWUiIDogIntodHRwOi8vaGw3Lm9yZy9maGlyfU1lZGljYXRpb25TdGF0dXMiLAogICAgICAgICAgICAgICAgICAidHlwZSIgOiAiTmFtZWRUeXBlU3BlY2lmaWVyIgogICAgICAgICAgICAgICB9CiAgICAgICAgICAgIH0gXQogICAgICAgICB9LCB7CiAgICAgICAgICAgICJsb2NhdG9yIiA6ICIxOTk6MS0xOTk6NzIiLAogICAgICAgICAgICAibmFtZSIgOiAiVG9TdHJpbmciLAogICAgICAgICAgICAiY29udGV4dCIgOiAiUGF0aWVudCIsCiAgICAgICAgICAgICJhY2Nlc3NMZXZlbCIgOiAiUHVibGljIiwKICAgICAgICAgICAgInR5cGUiIDogIkZ1bmN0aW9uRGVmIiwKICAgICAgICAgICAgImV4cHJlc3Npb24iIDogewogICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMTk5OjYyLTE5OTo3MiIsCiAgICAgICAgICAgICAgICJwYXRoIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICJ0eXBlIiA6ICJQcm9wZXJ0eSIsCiAgICAgICAgICAgICAgICJzb3VyY2UiIDogewogICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMTk5OjYyLTE5OTo2NiIsCiAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJPcGVyYW5kUmVmIgogICAgICAgICAgICAgICB9CiAgICAgICAgICAgIH0sCiAgICAgICAgICAgICJvcGVyYW5kIiA6IFsgewogICAgICAgICAgICAgICAibmFtZSIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAib3BlcmFuZFR5cGVTcGVjaWZpZXIiIDogewogICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMTk5OjMyLTE5OTo1OCIsCiAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJ7aHR0cDovL2hsNy5vcmcvZmhpcn1NZXNzYWdlU2lnbmlmaWNhbmNlQ2F0ZWdvcnkiLAogICAgICAgICAgICAgICAgICAidHlwZSIgOiAiTmFtZWRUeXBlU3BlY2lmaWVyIgogICAgICAgICAgICAgICB9CiAgICAgICAgICAgIH0gXQogICAgICAgICB9LCB7CiAgICAgICAgICAgICJsb2NhdG9yIiA6ICIyMDA6MS0yMDA6NzUiLAogICAgICAgICAgICAibmFtZSIgOiAiVG9TdHJpbmciLAogICAgICAgICAgICAiY29udGV4dCIgOiAiUGF0aWVudCIsCiAgICAgICAgICAgICJhY2Nlc3NMZXZlbCIgOiAiUHVibGljIiwKICAgICAgICAgICAgInR5cGUiIDogIkZ1bmN0aW9uRGVmIiwKICAgICAgICAgICAgImV4cHJlc3Npb24iIDogewogICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMjAwOjY1LTIwMDo3NSIsCiAgICAgICAgICAgICAgICJwYXRoIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICJ0eXBlIiA6ICJQcm9wZXJ0eSIsCiAgICAgICAgICAgICAgICJzb3VyY2UiIDogewogICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMjAwOjY1LTIwMDo2OSIsCiAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJPcGVyYW5kUmVmIgogICAgICAgICAgICAgICB9CiAgICAgICAgICAgIH0sCiAgICAgICAgICAgICJvcGVyYW5kIiA6IFsgewogICAgICAgICAgICAgICAibmFtZSIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAib3BlcmFuZFR5cGVTcGVjaWZpZXIiIDogewogICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMjAwOjMyLTIwMDo2MSIsCiAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJ7aHR0cDovL2hsNy5vcmcvZmhpcn1NZXNzYWdlaGVhZGVyX1Jlc3BvbnNlX1JlcXVlc3QiLAogICAgICAgICAgICAgICAgICAidHlwZSIgOiAiTmFtZWRUeXBlU3BlY2lmaWVyIgogICAgICAgICAgICAgICB9CiAgICAgICAgICAgIH0gXQogICAgICAgICB9LCB7CiAgICAgICAgICAgICJsb2NhdG9yIiA6ICIyMDE6MS0yMDE6NTMiLAogICAgICAgICAgICAibmFtZSIgOiAiVG9TdHJpbmciLAogICAgICAgICAgICAiY29udGV4dCIgOiAiUGF0aWVudCIsCiAgICAgICAgICAgICJhY2Nlc3NMZXZlbCIgOiAiUHVibGljIiwKICAgICAgICAgICAgInR5cGUiIDogIkZ1bmN0aW9uRGVmIiwKICAgICAgICAgICAgImV4cHJlc3Npb24iIDogewogICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMjAxOjQzLTIwMTo1MyIsCiAgICAgICAgICAgICAgICJwYXRoIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICJ0eXBlIiA6ICJQcm9wZXJ0eSIsCiAgICAgICAgICAgICAgICJzb3VyY2UiIDogewogICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMjAxOjQzLTIwMTo0NyIsCiAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJPcGVyYW5kUmVmIgogICAgICAgICAgICAgICB9CiAgICAgICAgICAgIH0sCiAgICAgICAgICAgICJvcGVyYW5kIiA6IFsgewogICAgICAgICAgICAgICAibmFtZSIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAib3BlcmFuZFR5cGVTcGVjaWZpZXIiIDogewogICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMjAxOjMyLTIwMTozOSIsCiAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJ7aHR0cDovL2hsNy5vcmcvZmhpcn1NaW1lVHlwZSIsCiAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJOYW1lZFR5cGVTcGVjaWZpZXIiCiAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgfSBdCiAgICAgICAgIH0sIHsKICAgICAgICAgICAgImxvY2F0b3IiIDogIjIwMjoxLTIwMjo1MiIsCiAgICAgICAgICAgICJuYW1lIiA6ICJUb1N0cmluZyIsCiAgICAgICAgICAgICJjb250ZXh0IiA6ICJQYXRpZW50IiwKICAgICAgICAgICAgImFjY2Vzc0xldmVsIiA6ICJQdWJsaWMiLAogICAgICAgICAgICAidHlwZSIgOiAiRnVuY3Rpb25EZWYiLAogICAgICAgICAgICAiZXhwcmVzc2lvbiIgOiB7CiAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIyMDI6NDItMjAyOjUyIiwKICAgICAgICAgICAgICAgInBhdGgiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgInR5cGUiIDogIlByb3BlcnR5IiwKICAgICAgICAgICAgICAgInNvdXJjZSIgOiB7CiAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIyMDI6NDItMjAyOjQ2IiwKICAgICAgICAgICAgICAgICAgIm5hbWUiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk9wZXJhbmRSZWYiCiAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgfSwKICAgICAgICAgICAgIm9wZXJhbmQiIDogWyB7CiAgICAgICAgICAgICAgICJuYW1lIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICJvcGVyYW5kVHlwZVNwZWNpZmllciIgOiB7CiAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIyMDI6MzItMjAyOjM4IiwKICAgICAgICAgICAgICAgICAgIm5hbWUiIDogIntodHRwOi8vaGw3Lm9yZy9maGlyfU5hbWVVc2UiLAogICAgICAgICAgICAgICAgICAidHlwZSIgOiAiTmFtZWRUeXBlU3BlY2lmaWVyIgogICAgICAgICAgICAgICB9CiAgICAgICAgICAgIH0gXQogICAgICAgICB9LCB7CiAgICAgICAgICAgICJsb2NhdG9yIiA6ICIyMDM6MS0yMDM6NzEiLAogICAgICAgICAgICAibmFtZSIgOiAiVG9TdHJpbmciLAogICAgICAgICAgICAiY29udGV4dCIgOiAiUGF0aWVudCIsCiAgICAgICAgICAgICJhY2Nlc3NMZXZlbCIgOiAiUHVibGljIiwKICAgICAgICAgICAgInR5cGUiIDogIkZ1bmN0aW9uRGVmIiwKICAgICAgICAgICAgImV4cHJlc3Npb24iIDogewogICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMjAzOjYxLTIwMzo3MSIsCiAgICAgICAgICAgICAgICJwYXRoIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICJ0eXBlIiA6ICJQcm9wZXJ0eSIsCiAgICAgICAgICAgICAgICJzb3VyY2UiIDogewogICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMjAzOjYxLTIwMzo2NSIsCiAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJPcGVyYW5kUmVmIgogICAgICAgICAgICAgICB9CiAgICAgICAgICAgIH0sCiAgICAgICAgICAgICJvcGVyYW5kIiA6IFsgewogICAgICAgICAgICAgICAibmFtZSIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAib3BlcmFuZFR5cGVTcGVjaWZpZXIiIDogewogICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMjAzOjMyLTIwMzo1NyIsCiAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJ7aHR0cDovL2hsNy5vcmcvZmhpcn1OYW1pbmdTeXN0ZW1JZGVudGlmaWVyVHlwZSIsCiAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJOYW1lZFR5cGVTcGVjaWZpZXIiCiAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgfSBdCiAgICAgICAgIH0sIHsKICAgICAgICAgICAgImxvY2F0b3IiIDogIjIwNDoxLTIwNDo2MSIsCiAgICAgICAgICAgICJuYW1lIiA6ICJUb1N0cmluZyIsCiAgICAgICAgICAgICJjb250ZXh0IiA6ICJQYXRpZW50IiwKICAgICAgICAgICAgImFjY2Vzc0xldmVsIiA6ICJQdWJsaWMiLAogICAgICAgICAgICAidHlwZSIgOiAiRnVuY3Rpb25EZWYiLAogICAgICAgICAgICAiZXhwcmVzc2lvbiIgOiB7CiAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIyMDQ6NTEtMjA0OjYxIiwKICAgICAgICAgICAgICAgInBhdGgiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgInR5cGUiIDogIlByb3BlcnR5IiwKICAgICAgICAgICAgICAgInNvdXJjZSIgOiB7CiAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIyMDQ6NTEtMjA0OjU1IiwKICAgICAgICAgICAgICAgICAgIm5hbWUiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk9wZXJhbmRSZWYiCiAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgfSwKICAgICAgICAgICAgIm9wZXJhbmQiIDogWyB7CiAgICAgICAgICAgICAgICJuYW1lIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICJvcGVyYW5kVHlwZVNwZWNpZmllciIgOiB7CiAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIyMDQ6MzItMjA0OjQ3IiwKICAgICAgICAgICAgICAgICAgIm5hbWUiIDogIntodHRwOi8vaGw3Lm9yZy9maGlyfU5hbWluZ1N5c3RlbVR5cGUiLAogICAgICAgICAgICAgICAgICAidHlwZSIgOiAiTmFtZWRUeXBlU3BlY2lmaWVyIgogICAgICAgICAgICAgICB9CiAgICAgICAgICAgIH0gXQogICAgICAgICB9LCB7CiAgICAgICAgICAgICJsb2NhdG9yIiA6ICIyMDU6MS0yMDU6NjAiLAogICAgICAgICAgICAibmFtZSIgOiAiVG9TdHJpbmciLAogICAgICAgICAgICAiY29udGV4dCIgOiAiUGF0aWVudCIsCiAgICAgICAgICAgICJhY2Nlc3NMZXZlbCIgOiAiUHVibGljIiwKICAgICAgICAgICAgInR5cGUiIDogIkZ1bmN0aW9uRGVmIiwKICAgICAgICAgICAgImV4cHJlc3Npb24iIDogewogICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMjA1OjUwLTIwNTo2MCIsCiAgICAgICAgICAgICAgICJwYXRoIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICJ0eXBlIiA6ICJQcm9wZXJ0eSIsCiAgICAgICAgICAgICAgICJzb3VyY2UiIDogewogICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMjA1OjUwLTIwNTo1NCIsCiAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJPcGVyYW5kUmVmIgogICAgICAgICAgICAgICB9CiAgICAgICAgICAgIH0sCiAgICAgICAgICAgICJvcGVyYW5kIiA6IFsgewogICAgICAgICAgICAgICAibmFtZSIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAib3BlcmFuZFR5cGVTcGVjaWZpZXIiIDogewogICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMjA1OjMyLTIwNTo0NiIsCiAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJ7aHR0cDovL2hsNy5vcmcvZmhpcn1OYXJyYXRpdmVTdGF0dXMiLAogICAgICAgICAgICAgICAgICAidHlwZSIgOiAiTmFtZWRUeXBlU3BlY2lmaWVyIgogICAgICAgICAgICAgICB9CiAgICAgICAgICAgIH0gXQogICAgICAgICB9LCB7CiAgICAgICAgICAgICJsb2NhdG9yIiA6ICIyMDY6MS0yMDY6NTMiLAogICAgICAgICAgICAibmFtZSIgOiAiVG9TdHJpbmciLAogICAgICAgICAgICAiY29udGV4dCIgOiAiUGF0aWVudCIsCiAgICAgICAgICAgICJhY2Nlc3NMZXZlbCIgOiAiUHVibGljIiwKICAgICAgICAgICAgInR5cGUiIDogIkZ1bmN0aW9uRGVmIiwKICAgICAgICAgICAgImV4cHJlc3Npb24iIDogewogICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMjA2OjQzLTIwNjo1MyIsCiAgICAgICAgICAgICAgICJwYXRoIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICJ0eXBlIiA6ICJQcm9wZXJ0eSIsCiAgICAgICAgICAgICAgICJzb3VyY2UiIDogewogICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMjA2OjQzLTIwNjo0NyIsCiAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJPcGVyYW5kUmVmIgogICAgICAgICAgICAgICB9CiAgICAgICAgICAgIH0sCiAgICAgICAgICAgICJvcGVyYW5kIiA6IFsgewogICAgICAgICAgICAgICAibmFtZSIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAib3BlcmFuZFR5cGVTcGVjaWZpZXIiIDogewogICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMjA2OjMyLTIwNjozOSIsCiAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJ7aHR0cDovL2hsNy5vcmcvZmhpcn1Ob3RlVHlwZSIsCiAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJOYW1lZFR5cGVTcGVjaWZpZXIiCiAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgfSBdCiAgICAgICAgIH0sIHsKICAgICAgICAgICAgImxvY2F0b3IiIDogIjIwNzoxLTIwNzo2NiIsCiAgICAgICAgICAgICJuYW1lIiA6ICJUb1N0cmluZyIsCiAgICAgICAgICAgICJjb250ZXh0IiA6ICJQYXRpZW50IiwKICAgICAgICAgICAgImFjY2Vzc0xldmVsIiA6ICJQdWJsaWMiLAogICAgICAgICAgICAidHlwZSIgOiAiRnVuY3Rpb25EZWYiLAogICAgICAgICAgICAiZXhwcmVzc2lvbiIgOiB7CiAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIyMDc6NTYtMjA3OjY2IiwKICAgICAgICAgICAgICAgInBhdGgiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgInR5cGUiIDogIlByb3BlcnR5IiwKICAgICAgICAgICAgICAgInNvdXJjZSIgOiB7CiAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIyMDc6NTYtMjA3OjYwIiwKICAgICAgICAgICAgICAgICAgIm5hbWUiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk9wZXJhbmRSZWYiCiAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgfSwKICAgICAgICAgICAgIm9wZXJhbmQiIDogWyB7CiAgICAgICAgICAgICAgICJuYW1lIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICJvcGVyYW5kVHlwZVNwZWNpZmllciIgOiB7CiAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIyMDc6MzItMjA3OjUyIiwKICAgICAgICAgICAgICAgICAgIm5hbWUiIDogIntodHRwOi8vaGw3Lm9yZy9maGlyfU51dHJpdGlpb25PcmRlckludGVudCIsCiAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJOYW1lZFR5cGVTcGVjaWZpZXIiCiAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgfSBdCiAgICAgICAgIH0sIHsKICAgICAgICAgICAgImxvY2F0b3IiIDogIjIwODoxLTIwODo2NSIsCiAgICAgICAgICAgICJuYW1lIiA6ICJUb1N0cmluZyIsCiAgICAgICAgICAgICJjb250ZXh0IiA6ICJQYXRpZW50IiwKICAgICAgICAgICAgImFjY2Vzc0xldmVsIiA6ICJQdWJsaWMiLAogICAgICAgICAgICAidHlwZSIgOiAiRnVuY3Rpb25EZWYiLAogICAgICAgICAgICAiZXhwcmVzc2lvbiIgOiB7CiAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIyMDg6NTUtMjA4OjY1IiwKICAgICAgICAgICAgICAgInBhdGgiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgInR5cGUiIDogIlByb3BlcnR5IiwKICAgICAgICAgICAgICAgInNvdXJjZSIgOiB7CiAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIyMDg6NTUtMjA4OjU5IiwKICAgICAgICAgICAgICAgICAgIm5hbWUiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk9wZXJhbmRSZWYiCiAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgfSwKICAgICAgICAgICAgIm9wZXJhbmQiIDogWyB7CiAgICAgICAgICAgICAgICJuYW1lIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICJvcGVyYW5kVHlwZVNwZWNpZmllciIgOiB7CiAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIyMDg6MzItMjA4OjUxIiwKICAgICAgICAgICAgICAgICAgIm5hbWUiIDogIntodHRwOi8vaGw3Lm9yZy9maGlyfU51dHJpdGlvbk9yZGVyU3RhdHVzIiwKICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk5hbWVkVHlwZVNwZWNpZmllciIKICAgICAgICAgICAgICAgfQogICAgICAgICAgICB9IF0KICAgICAgICAgfSwgewogICAgICAgICAgICAibG9jYXRvciIgOiAiMjA5OjEtMjA5OjY0IiwKICAgICAgICAgICAgIm5hbWUiIDogIlRvU3RyaW5nIiwKICAgICAgICAgICAgImNvbnRleHQiIDogIlBhdGllbnQiLAogICAgICAgICAgICAiYWNjZXNzTGV2ZWwiIDogIlB1YmxpYyIsCiAgICAgICAgICAgICJ0eXBlIiA6ICJGdW5jdGlvbkRlZiIsCiAgICAgICAgICAgICJleHByZXNzaW9uIiA6IHsKICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjIwOTo1NC0yMDk6NjQiLAogICAgICAgICAgICAgICAicGF0aCIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAidHlwZSIgOiAiUHJvcGVydHkiLAogICAgICAgICAgICAgICAic291cmNlIiA6IHsKICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjIwOTo1NC0yMDk6NTgiLAogICAgICAgICAgICAgICAgICAibmFtZSIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAgICAidHlwZSIgOiAiT3BlcmFuZFJlZiIKICAgICAgICAgICAgICAgfQogICAgICAgICAgICB9LAogICAgICAgICAgICAib3BlcmFuZCIgOiBbIHsKICAgICAgICAgICAgICAgIm5hbWUiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgIm9wZXJhbmRUeXBlU3BlY2lmaWVyIiA6IHsKICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjIwOTozMi0yMDk6NTAiLAogICAgICAgICAgICAgICAgICAibmFtZSIgOiAie2h0dHA6Ly9obDcub3JnL2ZoaXJ9T2JzZXJ2YXRpb25EYXRhVHlwZSIsCiAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJOYW1lZFR5cGVTcGVjaWZpZXIiCiAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgfSBdCiAgICAgICAgIH0sIHsKICAgICAgICAgICAgImxvY2F0b3IiIDogIjIxMDoxLTIxMDo2OSIsCiAgICAgICAgICAgICJuYW1lIiA6ICJUb1N0cmluZyIsCiAgICAgICAgICAgICJjb250ZXh0IiA6ICJQYXRpZW50IiwKICAgICAgICAgICAgImFjY2Vzc0xldmVsIiA6ICJQdWJsaWMiLAogICAgICAgICAgICAidHlwZSIgOiAiRnVuY3Rpb25EZWYiLAogICAgICAgICAgICAiZXhwcmVzc2lvbiIgOiB7CiAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIyMTA6NTktMjEwOjY5IiwKICAgICAgICAgICAgICAgInBhdGgiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgInR5cGUiIDogIlByb3BlcnR5IiwKICAgICAgICAgICAgICAgInNvdXJjZSIgOiB7CiAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIyMTA6NTktMjEwOjYzIiwKICAgICAgICAgICAgICAgICAgIm5hbWUiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk9wZXJhbmRSZWYiCiAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgfSwKICAgICAgICAgICAgIm9wZXJhbmQiIDogWyB7CiAgICAgICAgICAgICAgICJuYW1lIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICJvcGVyYW5kVHlwZVNwZWNpZmllciIgOiB7CiAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIyMTA6MzItMjEwOjU1IiwKICAgICAgICAgICAgICAgICAgIm5hbWUiIDogIntodHRwOi8vaGw3Lm9yZy9maGlyfU9ic2VydmF0aW9uUmFuZ2VDYXRlZ29yeSIsCiAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJOYW1lZFR5cGVTcGVjaWZpZXIiCiAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgfSBdCiAgICAgICAgIH0sIHsKICAgICAgICAgICAgImxvY2F0b3IiIDogIjIxMToxLTIxMTo2MiIsCiAgICAgICAgICAgICJuYW1lIiA6ICJUb1N0cmluZyIsCiAgICAgICAgICAgICJjb250ZXh0IiA6ICJQYXRpZW50IiwKICAgICAgICAgICAgImFjY2Vzc0xldmVsIiA6ICJQdWJsaWMiLAogICAgICAgICAgICAidHlwZSIgOiAiRnVuY3Rpb25EZWYiLAogICAgICAgICAgICAiZXhwcmVzc2lvbiIgOiB7CiAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIyMTE6NTItMjExOjYyIiwKICAgICAgICAgICAgICAgInBhdGgiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgInR5cGUiIDogIlByb3BlcnR5IiwKICAgICAgICAgICAgICAgInNvdXJjZSIgOiB7CiAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIyMTE6NTItMjExOjU2IiwKICAgICAgICAgICAgICAgICAgIm5hbWUiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk9wZXJhbmRSZWYiCiAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgfSwKICAgICAgICAgICAgIm9wZXJhbmQiIDogWyB7CiAgICAgICAgICAgICAgICJuYW1lIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICJvcGVyYW5kVHlwZVNwZWNpZmllciIgOiB7CiAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIyMTE6MzItMjExOjQ4IiwKICAgICAgICAgICAgICAgICAgIm5hbWUiIDogIntodHRwOi8vaGw3Lm9yZy9maGlyfU9ic2VydmF0aW9uU3RhdHVzIiwKICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk5hbWVkVHlwZVNwZWNpZmllciIKICAgICAgICAgICAgICAgfQogICAgICAgICAgICB9IF0KICAgICAgICAgfSwgewogICAgICAgICAgICAibG9jYXRvciIgOiAiMjEyOjEtMjEyOjU4IiwKICAgICAgICAgICAgIm5hbWUiIDogIlRvU3RyaW5nIiwKICAgICAgICAgICAgImNvbnRleHQiIDogIlBhdGllbnQiLAogICAgICAgICAgICAiYWNjZXNzTGV2ZWwiIDogIlB1YmxpYyIsCiAgICAgICAgICAgICJ0eXBlIiA6ICJGdW5jdGlvbkRlZiIsCiAgICAgICAgICAgICJleHByZXNzaW9uIiA6IHsKICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjIxMjo0OC0yMTI6NTgiLAogICAgICAgICAgICAgICAicGF0aCIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAidHlwZSIgOiAiUHJvcGVydHkiLAogICAgICAgICAgICAgICAic291cmNlIiA6IHsKICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjIxMjo0OC0yMTI6NTIiLAogICAgICAgICAgICAgICAgICAibmFtZSIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAgICAidHlwZSIgOiAiT3BlcmFuZFJlZiIKICAgICAgICAgICAgICAgfQogICAgICAgICAgICB9LAogICAgICAgICAgICAib3BlcmFuZCIgOiBbIHsKICAgICAgICAgICAgICAgIm5hbWUiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgIm9wZXJhbmRUeXBlU3BlY2lmaWVyIiA6IHsKICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjIxMjozMi0yMTI6NDQiLAogICAgICAgICAgICAgICAgICAibmFtZSIgOiAie2h0dHA6Ly9obDcub3JnL2ZoaXJ9T3BlcmF0aW9uS2luZCIsCiAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJOYW1lZFR5cGVTcGVjaWZpZXIiCiAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgfSBdCiAgICAgICAgIH0sIHsKICAgICAgICAgICAgImxvY2F0b3IiIDogIjIxMzoxLTIxMzo2NiIsCiAgICAgICAgICAgICJuYW1lIiA6ICJUb1N0cmluZyIsCiAgICAgICAgICAgICJjb250ZXh0IiA6ICJQYXRpZW50IiwKICAgICAgICAgICAgImFjY2Vzc0xldmVsIiA6ICJQdWJsaWMiLAogICAgICAgICAgICAidHlwZSIgOiAiRnVuY3Rpb25EZWYiLAogICAgICAgICAgICAiZXhwcmVzc2lvbiIgOiB7CiAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIyMTM6NTYtMjEzOjY2IiwKICAgICAgICAgICAgICAgInBhdGgiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgInR5cGUiIDogIlByb3BlcnR5IiwKICAgICAgICAgICAgICAgInNvdXJjZSIgOiB7CiAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIyMTM6NTYtMjEzOjYwIiwKICAgICAgICAgICAgICAgICAgIm5hbWUiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk9wZXJhbmRSZWYiCiAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgfSwKICAgICAgICAgICAgIm9wZXJhbmQiIDogWyB7CiAgICAgICAgICAgICAgICJuYW1lIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICJvcGVyYW5kVHlwZVNwZWNpZmllciIgOiB7CiAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIyMTM6MzItMjEzOjUyIiwKICAgICAgICAgICAgICAgICAgIm5hbWUiIDogIntodHRwOi8vaGw3Lm9yZy9maGlyfU9wZXJhdGlvblBhcmFtZXRlclVzZSIsCiAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJOYW1lZFR5cGVTcGVjaWZpZXIiCiAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgfSBdCiAgICAgICAgIH0sIHsKICAgICAgICAgICAgImxvY2F0b3IiIDogIjIxNDoxLTIxNDo2MCIsCiAgICAgICAgICAgICJuYW1lIiA6ICJUb1N0cmluZyIsCiAgICAgICAgICAgICJjb250ZXh0IiA6ICJQYXRpZW50IiwKICAgICAgICAgICAgImFjY2Vzc0xldmVsIiA6ICJQdWJsaWMiLAogICAgICAgICAgICAidHlwZSIgOiAiRnVuY3Rpb25EZWYiLAogICAgICAgICAgICAiZXhwcmVzc2lvbiIgOiB7CiAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIyMTQ6NTAtMjE0OjYwIiwKICAgICAgICAgICAgICAgInBhdGgiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgInR5cGUiIDogIlByb3BlcnR5IiwKICAgICAgICAgICAgICAgInNvdXJjZSIgOiB7CiAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIyMTQ6NTAtMjE0OjU0IiwKICAgICAgICAgICAgICAgICAgIm5hbWUiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk9wZXJhbmRSZWYiCiAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgfSwKICAgICAgICAgICAgIm9wZXJhbmQiIDogWyB7CiAgICAgICAgICAgICAgICJuYW1lIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICJvcGVyYW5kVHlwZVNwZWNpZmllciIgOiB7CiAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIyMTQ6MzItMjE0OjQ2IiwKICAgICAgICAgICAgICAgICAgIm5hbWUiIDogIntodHRwOi8vaGw3Lm9yZy9maGlyfU9yaWVudGF0aW9uVHlwZSIsCiAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJOYW1lZFR5cGVTcGVjaWZpZXIiCiAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgfSBdCiAgICAgICAgIH0sIHsKICAgICAgICAgICAgImxvY2F0b3IiIDogIjIxNToxLTIxNTo1NyIsCiAgICAgICAgICAgICJuYW1lIiA6ICJUb1N0cmluZyIsCiAgICAgICAgICAgICJjb250ZXh0IiA6ICJQYXRpZW50IiwKICAgICAgICAgICAgImFjY2Vzc0xldmVsIiA6ICJQdWJsaWMiLAogICAgICAgICAgICAidHlwZSIgOiAiRnVuY3Rpb25EZWYiLAogICAgICAgICAgICAiZXhwcmVzc2lvbiIgOiB7CiAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIyMTU6NDctMjE1OjU3IiwKICAgICAgICAgICAgICAgInBhdGgiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgInR5cGUiIDogIlByb3BlcnR5IiwKICAgICAgICAgICAgICAgInNvdXJjZSIgOiB7CiAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIyMTU6NDctMjE1OjUxIiwKICAgICAgICAgICAgICAgICAgIm5hbWUiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk9wZXJhbmRSZWYiCiAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgfSwKICAgICAgICAgICAgIm9wZXJhbmQiIDogWyB7CiAgICAgICAgICAgICAgICJuYW1lIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICJvcGVyYW5kVHlwZVNwZWNpZmllciIgOiB7CiAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIyMTU6MzItMjE1OjQzIiwKICAgICAgICAgICAgICAgICAgIm5hbWUiIDogIntodHRwOi8vaGw3Lm9yZy9maGlyfVBhcmFtZXRlclVzZSIsCiAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJOYW1lZFR5cGVTcGVjaWZpZXIiCiAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgfSBdCiAgICAgICAgIH0sIHsKICAgICAgICAgICAgImxvY2F0b3IiIDogIjIxNjoxLTIxNjo2NCIsCiAgICAgICAgICAgICJuYW1lIiA6ICJUb1N0cmluZyIsCiAgICAgICAgICAgICJjb250ZXh0IiA6ICJQYXRpZW50IiwKICAgICAgICAgICAgImFjY2Vzc0xldmVsIiA6ICJQdWJsaWMiLAogICAgICAgICAgICAidHlwZSIgOiAiRnVuY3Rpb25EZWYiLAogICAgICAgICAgICAiZXhwcmVzc2lvbiIgOiB7CiAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIyMTY6NTQtMjE2OjY0IiwKICAgICAgICAgICAgICAgInBhdGgiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgInR5cGUiIDogIlByb3BlcnR5IiwKICAgICAgICAgICAgICAgInNvdXJjZSIgOiB7CiAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIyMTY6NTQtMjE2OjU4IiwKICAgICAgICAgICAgICAgICAgIm5hbWUiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk9wZXJhbmRSZWYiCiAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgfSwKICAgICAgICAgICAgIm9wZXJhbmQiIDogWyB7CiAgICAgICAgICAgICAgICJuYW1lIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICJvcGVyYW5kVHlwZVNwZWNpZmllciIgOiB7CiAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIyMTY6MzItMjE2OjUwIiwKICAgICAgICAgICAgICAgICAgIm5hbWUiIDogIntodHRwOi8vaGw3Lm9yZy9maGlyfVBhcnRpY2lwYW50UmVxdWlyZWQiLAogICAgICAgICAgICAgICAgICAidHlwZSIgOiAiTmFtZWRUeXBlU3BlY2lmaWVyIgogICAgICAgICAgICAgICB9CiAgICAgICAgICAgIH0gXQogICAgICAgICB9LCB7CiAgICAgICAgICAgICJsb2NhdG9yIiA6ICIyMTc6MS0yMTc6NjIiLAogICAgICAgICAgICAibmFtZSIgOiAiVG9TdHJpbmciLAogICAgICAgICAgICAiY29udGV4dCIgOiAiUGF0aWVudCIsCiAgICAgICAgICAgICJhY2Nlc3NMZXZlbCIgOiAiUHVibGljIiwKICAgICAgICAgICAgInR5cGUiIDogIkZ1bmN0aW9uRGVmIiwKICAgICAgICAgICAgImV4cHJlc3Npb24iIDogewogICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMjE3OjUyLTIxNzo2MiIsCiAgICAgICAgICAgICAgICJwYXRoIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICJ0eXBlIiA6ICJQcm9wZXJ0eSIsCiAgICAgICAgICAgICAgICJzb3VyY2UiIDogewogICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMjE3OjUyLTIxNzo1NiIsCiAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJPcGVyYW5kUmVmIgogICAgICAgICAgICAgICB9CiAgICAgICAgICAgIH0sCiAgICAgICAgICAgICJvcGVyYW5kIiA6IFsgewogICAgICAgICAgICAgICAibmFtZSIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAib3BlcmFuZFR5cGVTcGVjaWZpZXIiIDogewogICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMjE3OjMyLTIxNzo0OCIsCiAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJ7aHR0cDovL2hsNy5vcmcvZmhpcn1QYXJ0aWNpcGFudFN0YXR1cyIsCiAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJOYW1lZFR5cGVTcGVjaWZpZXIiCiAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgfSBdCiAgICAgICAgIH0sIHsKICAgICAgICAgICAgImxvY2F0b3IiIDogIjIxODoxLTIxODo2NCIsCiAgICAgICAgICAgICJuYW1lIiA6ICJUb1N0cmluZyIsCiAgICAgICAgICAgICJjb250ZXh0IiA6ICJQYXRpZW50IiwKICAgICAgICAgICAgImFjY2Vzc0xldmVsIiA6ICJQdWJsaWMiLAogICAgICAgICAgICAidHlwZSIgOiAiRnVuY3Rpb25EZWYiLAogICAgICAgICAgICAiZXhwcmVzc2lvbiIgOiB7CiAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIyMTg6NTQtMjE4OjY0IiwKICAgICAgICAgICAgICAgInBhdGgiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgInR5cGUiIDogIlByb3BlcnR5IiwKICAgICAgICAgICAgICAgInNvdXJjZSIgOiB7CiAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIyMTg6NTQtMjE4OjU4IiwKICAgICAgICAgICAgICAgICAgIm5hbWUiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk9wZXJhbmRSZWYiCiAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgfSwKICAgICAgICAgICAgIm9wZXJhbmQiIDogWyB7CiAgICAgICAgICAgICAgICJuYW1lIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICJvcGVyYW5kVHlwZVNwZWNpZmllciIgOiB7CiAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIyMTg6MzItMjE4OjUwIiwKICAgICAgICAgICAgICAgICAgIm5hbWUiIDogIntodHRwOi8vaGw3Lm9yZy9maGlyfVBhcnRpY2lwYXRpb25TdGF0dXMiLAogICAgICAgICAgICAgICAgICAidHlwZSIgOiAiTmFtZWRUeXBlU3BlY2lmaWVyIgogICAgICAgICAgICAgICB9CiAgICAgICAgICAgIH0gXQogICAgICAgICB9LCB7CiAgICAgICAgICAgICJsb2NhdG9yIiA6ICIyMTk6MS0yMTk6NjQiLAogICAgICAgICAgICAibmFtZSIgOiAiVG9TdHJpbmciLAogICAgICAgICAgICAiY29udGV4dCIgOiAiUGF0aWVudCIsCiAgICAgICAgICAgICJhY2Nlc3NMZXZlbCIgOiAiUHVibGljIiwKICAgICAgICAgICAgInR5cGUiIDogIkZ1bmN0aW9uRGVmIiwKICAgICAgICAgICAgImV4cHJlc3Npb24iIDogewogICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMjE5OjU0LTIxOTo2NCIsCiAgICAgICAgICAgICAgICJwYXRoIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICJ0eXBlIiA6ICJQcm9wZXJ0eSIsCiAgICAgICAgICAgICAgICJzb3VyY2UiIDogewogICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMjE5OjU0LTIxOTo1OCIsCiAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJPcGVyYW5kUmVmIgogICAgICAgICAgICAgICB9CiAgICAgICAgICAgIH0sCiAgICAgICAgICAgICJvcGVyYW5kIiA6IFsgewogICAgICAgICAgICAgICAibmFtZSIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAib3BlcmFuZFR5cGVTcGVjaWZpZXIiIDogewogICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMjE5OjMyLTIxOTo1MCIsCiAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJ7aHR0cDovL2hsNy5vcmcvZmhpcn1QYXltZW50Tm90aWNlU3RhdHVzIiwKICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk5hbWVkVHlwZVNwZWNpZmllciIKICAgICAgICAgICAgICAgfQogICAgICAgICAgICB9IF0KICAgICAgICAgfSwgewogICAgICAgICAgICAibG9jYXRvciIgOiAiMjIwOjEtMjIwOjcyIiwKICAgICAgICAgICAgIm5hbWUiIDogIlRvU3RyaW5nIiwKICAgICAgICAgICAgImNvbnRleHQiIDogIlBhdGllbnQiLAogICAgICAgICAgICAiYWNjZXNzTGV2ZWwiIDogIlB1YmxpYyIsCiAgICAgICAgICAgICJ0eXBlIiA6ICJGdW5jdGlvbkRlZiIsCiAgICAgICAgICAgICJleHByZXNzaW9uIiA6IHsKICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjIyMDo2Mi0yMjA6NzIiLAogICAgICAgICAgICAgICAicGF0aCIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAidHlwZSIgOiAiUHJvcGVydHkiLAogICAgICAgICAgICAgICAic291cmNlIiA6IHsKICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjIyMDo2Mi0yMjA6NjYiLAogICAgICAgICAgICAgICAgICAibmFtZSIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAgICAidHlwZSIgOiAiT3BlcmFuZFJlZiIKICAgICAgICAgICAgICAgfQogICAgICAgICAgICB9LAogICAgICAgICAgICAib3BlcmFuZCIgOiBbIHsKICAgICAgICAgICAgICAgIm5hbWUiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgIm9wZXJhbmRUeXBlU3BlY2lmaWVyIiA6IHsKICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjIyMDozMi0yMjA6NTgiLAogICAgICAgICAgICAgICAgICAibmFtZSIgOiAie2h0dHA6Ly9obDcub3JnL2ZoaXJ9UGF5bWVudFJlY29uY2lsaWF0aW9uU3RhdHVzIiwKICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk5hbWVkVHlwZVNwZWNpZmllciIKICAgICAgICAgICAgICAgfQogICAgICAgICAgICB9IF0KICAgICAgICAgfSwgewogICAgICAgICAgICAibG9jYXRvciIgOiAiMjIxOjEtMjIxOjYwIiwKICAgICAgICAgICAgIm5hbWUiIDogIlRvU3RyaW5nIiwKICAgICAgICAgICAgImNvbnRleHQiIDogIlBhdGllbnQiLAogICAgICAgICAgICAiYWNjZXNzTGV2ZWwiIDogIlB1YmxpYyIsCiAgICAgICAgICAgICJ0eXBlIiA6ICJGdW5jdGlvbkRlZiIsCiAgICAgICAgICAgICJleHByZXNzaW9uIiA6IHsKICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjIyMTo1MC0yMjE6NjAiLAogICAgICAgICAgICAgICAicGF0aCIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAidHlwZSIgOiAiUHJvcGVydHkiLAogICAgICAgICAgICAgICAic291cmNlIiA6IHsKICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjIyMTo1MC0yMjE6NTQiLAogICAgICAgICAgICAgICAgICAibmFtZSIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAgICAidHlwZSIgOiAiT3BlcmFuZFJlZiIKICAgICAgICAgICAgICAgfQogICAgICAgICAgICB9LAogICAgICAgICAgICAib3BlcmFuZCIgOiBbIHsKICAgICAgICAgICAgICAgIm5hbWUiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgIm9wZXJhbmRUeXBlU3BlY2lmaWVyIiA6IHsKICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjIyMTozMi0yMjE6NDYiLAogICAgICAgICAgICAgICAgICAibmFtZSIgOiAie2h0dHA6Ly9obDcub3JnL2ZoaXJ9UHJvY2VkdXJlU3RhdHVzIiwKICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk5hbWVkVHlwZVNwZWNpZmllciIKICAgICAgICAgICAgICAgfQogICAgICAgICAgICB9IF0KICAgICAgICAgfSwgewogICAgICAgICAgICAibG9jYXRvciIgOiAiMjIyOjEtMjIyOjY3IiwKICAgICAgICAgICAgIm5hbWUiIDogIlRvU3RyaW5nIiwKICAgICAgICAgICAgImNvbnRleHQiIDogIlBhdGllbnQiLAogICAgICAgICAgICAiYWNjZXNzTGV2ZWwiIDogIlB1YmxpYyIsCiAgICAgICAgICAgICJ0eXBlIiA6ICJGdW5jdGlvbkRlZiIsCiAgICAgICAgICAgICJleHByZXNzaW9uIiA6IHsKICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjIyMjo1Ny0yMjI6NjciLAogICAgICAgICAgICAgICAicGF0aCIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAidHlwZSIgOiAiUHJvcGVydHkiLAogICAgICAgICAgICAgICAic291cmNlIiA6IHsKICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjIyMjo1Ny0yMjI6NjEiLAogICAgICAgICAgICAgICAgICAibmFtZSIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAgICAidHlwZSIgOiAiT3BlcmFuZFJlZiIKICAgICAgICAgICAgICAgfQogICAgICAgICAgICB9LAogICAgICAgICAgICAib3BlcmFuZCIgOiBbIHsKICAgICAgICAgICAgICAgIm5hbWUiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgIm9wZXJhbmRUeXBlU3BlY2lmaWVyIiA6IHsKICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjIyMjozMi0yMjI6NTMiLAogICAgICAgICAgICAgICAgICAibmFtZSIgOiAie2h0dHA6Ly9obDcub3JnL2ZoaXJ9UHJvcGVydHlSZXByZXNlbnRhdGlvbiIsCiAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJOYW1lZFR5cGVTcGVjaWZpZXIiCiAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgfSBdCiAgICAgICAgIH0sIHsKICAgICAgICAgICAgImxvY2F0b3IiIDogIjIyMzoxLTIyMzo1NyIsCiAgICAgICAgICAgICJuYW1lIiA6ICJUb1N0cmluZyIsCiAgICAgICAgICAgICJjb250ZXh0IiA6ICJQYXRpZW50IiwKICAgICAgICAgICAgImFjY2Vzc0xldmVsIiA6ICJQdWJsaWMiLAogICAgICAgICAgICAidHlwZSIgOiAiRnVuY3Rpb25EZWYiLAogICAgICAgICAgICAiZXhwcmVzc2lvbiIgOiB7CiAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIyMjM6NDctMjIzOjU3IiwKICAgICAgICAgICAgICAgInBhdGgiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgInR5cGUiIDogIlByb3BlcnR5IiwKICAgICAgICAgICAgICAgInNvdXJjZSIgOiB7CiAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIyMjM6NDctMjIzOjUxIiwKICAgICAgICAgICAgICAgICAgIm5hbWUiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk9wZXJhbmRSZWYiCiAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgfSwKICAgICAgICAgICAgIm9wZXJhbmQiIDogWyB7CiAgICAgICAgICAgICAgICJuYW1lIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICJvcGVyYW5kVHlwZVNwZWNpZmllciIgOiB7CiAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIyMjM6MzItMjIzOjQzIiwKICAgICAgICAgICAgICAgICAgIm5hbWUiIDogIntodHRwOi8vaGw3Lm9yZy9maGlyfVByb3BlcnR5VHlwZSIsCiAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJOYW1lZFR5cGVTcGVjaWZpZXIiCiAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgfSBdCiAgICAgICAgIH0sIHsKICAgICAgICAgICAgImxvY2F0b3IiIDogIjIyNDoxLTIyNDo2NSIsCiAgICAgICAgICAgICJuYW1lIiA6ICJUb1N0cmluZyIsCiAgICAgICAgICAgICJjb250ZXh0IiA6ICJQYXRpZW50IiwKICAgICAgICAgICAgImFjY2Vzc0xldmVsIiA6ICJQdWJsaWMiLAogICAgICAgICAgICAidHlwZSIgOiAiRnVuY3Rpb25EZWYiLAogICAgICAgICAgICAiZXhwcmVzc2lvbiIgOiB7CiAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIyMjQ6NTUtMjI0OjY1IiwKICAgICAgICAgICAgICAgInBhdGgiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgInR5cGUiIDogIlByb3BlcnR5IiwKICAgICAgICAgICAgICAgInNvdXJjZSIgOiB7CiAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIyMjQ6NTUtMjI0OjU5IiwKICAgICAgICAgICAgICAgICAgIm5hbWUiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk9wZXJhbmRSZWYiCiAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgfSwKICAgICAgICAgICAgIm9wZXJhbmQiIDogWyB7CiAgICAgICAgICAgICAgICJuYW1lIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICJvcGVyYW5kVHlwZVNwZWNpZmllciIgOiB7CiAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIyMjQ6MzItMjI0OjUxIiwKICAgICAgICAgICAgICAgICAgIm5hbWUiIDogIntodHRwOi8vaGw3Lm9yZy9maGlyfVByb3ZlbmFuY2VFbnRpdHlSb2xlIiwKICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk5hbWVkVHlwZVNwZWNpZmllciIKICAgICAgICAgICAgICAgfQogICAgICAgICAgICB9IF0KICAgICAgICAgfSwgewogICAgICAgICAgICAibG9jYXRvciIgOiAiMjI1OjEtMjI1OjYyIiwKICAgICAgICAgICAgIm5hbWUiIDogIlRvU3RyaW5nIiwKICAgICAgICAgICAgImNvbnRleHQiIDogIlBhdGllbnQiLAogICAgICAgICAgICAiYWNjZXNzTGV2ZWwiIDogIlB1YmxpYyIsCiAgICAgICAgICAgICJ0eXBlIiA6ICJGdW5jdGlvbkRlZiIsCiAgICAgICAgICAgICJleHByZXNzaW9uIiA6IHsKICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjIyNTo1Mi0yMjU6NjIiLAogICAgICAgICAgICAgICAicGF0aCIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAidHlwZSIgOiAiUHJvcGVydHkiLAogICAgICAgICAgICAgICAic291cmNlIiA6IHsKICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjIyNTo1Mi0yMjU6NTYiLAogICAgICAgICAgICAgICAgICAibmFtZSIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAgICAidHlwZSIgOiAiT3BlcmFuZFJlZiIKICAgICAgICAgICAgICAgfQogICAgICAgICAgICB9LAogICAgICAgICAgICAib3BlcmFuZCIgOiBbIHsKICAgICAgICAgICAgICAgIm5hbWUiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgIm9wZXJhbmRUeXBlU3BlY2lmaWVyIiA6IHsKICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjIyNTozMi0yMjU6NDgiLAogICAgICAgICAgICAgICAgICAibmFtZSIgOiAie2h0dHA6Ly9obDcub3JnL2ZoaXJ9UHVibGljYXRpb25TdGF0dXMiLAogICAgICAgICAgICAgICAgICAidHlwZSIgOiAiTmFtZWRUeXBlU3BlY2lmaWVyIgogICAgICAgICAgICAgICB9CiAgICAgICAgICAgIH0gXQogICAgICAgICB9LCB7CiAgICAgICAgICAgICJsb2NhdG9yIiA6ICIyMjY6MS0yMjY6NTYiLAogICAgICAgICAgICAibmFtZSIgOiAiVG9TdHJpbmciLAogICAgICAgICAgICAiY29udGV4dCIgOiAiUGF0aWVudCIsCiAgICAgICAgICAgICJhY2Nlc3NMZXZlbCIgOiAiUHVibGljIiwKICAgICAgICAgICAgInR5cGUiIDogIkZ1bmN0aW9uRGVmIiwKICAgICAgICAgICAgImV4cHJlc3Npb24iIDogewogICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMjI2OjQ2LTIyNjo1NiIsCiAgICAgICAgICAgICAgICJwYXRoIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICJ0eXBlIiA6ICJQcm9wZXJ0eSIsCiAgICAgICAgICAgICAgICJzb3VyY2UiIDogewogICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMjI2OjQ2LTIyNjo1MCIsCiAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJPcGVyYW5kUmVmIgogICAgICAgICAgICAgICB9CiAgICAgICAgICAgIH0sCiAgICAgICAgICAgICJvcGVyYW5kIiA6IFsgewogICAgICAgICAgICAgICAibmFtZSIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAib3BlcmFuZFR5cGVTcGVjaWZpZXIiIDogewogICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMjI2OjMyLTIyNjo0MiIsCiAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJ7aHR0cDovL2hsNy5vcmcvZmhpcn1RdWFsaXR5VHlwZSIsCiAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJOYW1lZFR5cGVTcGVjaWZpZXIiCiAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgfSBdCiAgICAgICAgIH0sIHsKICAgICAgICAgICAgImxvY2F0b3IiIDogIjIyNzoxLTIyNzo2MyIsCiAgICAgICAgICAgICJuYW1lIiA6ICJUb1N0cmluZyIsCiAgICAgICAgICAgICJjb250ZXh0IiA6ICJQYXRpZW50IiwKICAgICAgICAgICAgImFjY2Vzc0xldmVsIiA6ICJQdWJsaWMiLAogICAgICAgICAgICAidHlwZSIgOiAiRnVuY3Rpb25EZWYiLAogICAgICAgICAgICAiZXhwcmVzc2lvbiIgOiB7CiAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIyMjc6NTMtMjI3OjYzIiwKICAgICAgICAgICAgICAgInBhdGgiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgInR5cGUiIDogIlByb3BlcnR5IiwKICAgICAgICAgICAgICAgInNvdXJjZSIgOiB7CiAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIyMjc6NTMtMjI3OjU3IiwKICAgICAgICAgICAgICAgICAgIm5hbWUiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk9wZXJhbmRSZWYiCiAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgfSwKICAgICAgICAgICAgIm9wZXJhbmQiIDogWyB7CiAgICAgICAgICAgICAgICJuYW1lIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICJvcGVyYW5kVHlwZVNwZWNpZmllciIgOiB7CiAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIyMjc6MzItMjI3OjQ5IiwKICAgICAgICAgICAgICAgICAgIm5hbWUiIDogIntodHRwOi8vaGw3Lm9yZy9maGlyfVF1YW50aXR5Q29tcGFyYXRvciIsCiAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJOYW1lZFR5cGVTcGVjaWZpZXIiCiAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgfSBdCiAgICAgICAgIH0sIHsKICAgICAgICAgICAgImxvY2F0b3IiIDogIjIyODoxLTIyODo3MCIsCiAgICAgICAgICAgICJuYW1lIiA6ICJUb1N0cmluZyIsCiAgICAgICAgICAgICJjb250ZXh0IiA6ICJQYXRpZW50IiwKICAgICAgICAgICAgImFjY2Vzc0xldmVsIiA6ICJQdWJsaWMiLAogICAgICAgICAgICAidHlwZSIgOiAiRnVuY3Rpb25EZWYiLAogICAgICAgICAgICAiZXhwcmVzc2lvbiIgOiB7CiAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIyMjg6NjAtMjI4OjcwIiwKICAgICAgICAgICAgICAgInBhdGgiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgInR5cGUiIDogIlByb3BlcnR5IiwKICAgICAgICAgICAgICAgInNvdXJjZSIgOiB7CiAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIyMjg6NjAtMjI4OjY0IiwKICAgICAgICAgICAgICAgICAgIm5hbWUiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk9wZXJhbmRSZWYiCiAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgfSwKICAgICAgICAgICAgIm9wZXJhbmQiIDogWyB7CiAgICAgICAgICAgICAgICJuYW1lIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICJvcGVyYW5kVHlwZVNwZWNpZmllciIgOiB7CiAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIyMjg6MzItMjI4OjU2IiwKICAgICAgICAgICAgICAgICAgIm5hbWUiIDogIntodHRwOi8vaGw3Lm9yZy9maGlyfVF1ZXN0aW9ubmFpcmVJdGVtT3BlcmF0b3IiLAogICAgICAgICAgICAgICAgICAidHlwZSIgOiAiTmFtZWRUeXBlU3BlY2lmaWVyIgogICAgICAgICAgICAgICB9CiAgICAgICAgICAgIH0gXQogICAgICAgICB9LCB7CiAgICAgICAgICAgICJsb2NhdG9yIiA6ICIyMjk6MS0yMjk6NjYiLAogICAgICAgICAgICAibmFtZSIgOiAiVG9TdHJpbmciLAogICAgICAgICAgICAiY29udGV4dCIgOiAiUGF0aWVudCIsCiAgICAgICAgICAgICJhY2Nlc3NMZXZlbCIgOiAiUHVibGljIiwKICAgICAgICAgICAgInR5cGUiIDogIkZ1bmN0aW9uRGVmIiwKICAgICAgICAgICAgImV4cHJlc3Npb24iIDogewogICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMjI5OjU2LTIyOTo2NiIsCiAgICAgICAgICAgICAgICJwYXRoIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICJ0eXBlIiA6ICJQcm9wZXJ0eSIsCiAgICAgICAgICAgICAgICJzb3VyY2UiIDogewogICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMjI5OjU2LTIyOTo2MCIsCiAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJPcGVyYW5kUmVmIgogICAgICAgICAgICAgICB9CiAgICAgICAgICAgIH0sCiAgICAgICAgICAgICJvcGVyYW5kIiA6IFsgewogICAgICAgICAgICAgICAibmFtZSIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAib3BlcmFuZFR5cGVTcGVjaWZpZXIiIDogewogICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMjI5OjMyLTIyOTo1MiIsCiAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJ7aHR0cDovL2hsNy5vcmcvZmhpcn1RdWVzdGlvbm5haXJlSXRlbVR5cGUiLAogICAgICAgICAgICAgICAgICAidHlwZSIgOiAiTmFtZWRUeXBlU3BlY2lmaWVyIgogICAgICAgICAgICAgICB9CiAgICAgICAgICAgIH0gXQogICAgICAgICB9LCB7CiAgICAgICAgICAgICJsb2NhdG9yIiA6ICIyMzA6MS0yMzA6NzIiLAogICAgICAgICAgICAibmFtZSIgOiAiVG9TdHJpbmciLAogICAgICAgICAgICAiY29udGV4dCIgOiAiUGF0aWVudCIsCiAgICAgICAgICAgICJhY2Nlc3NMZXZlbCIgOiAiUHVibGljIiwKICAgICAgICAgICAgInR5cGUiIDogIkZ1bmN0aW9uRGVmIiwKICAgICAgICAgICAgImV4cHJlc3Npb24iIDogewogICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMjMwOjYyLTIzMDo3MiIsCiAgICAgICAgICAgICAgICJwYXRoIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICJ0eXBlIiA6ICJQcm9wZXJ0eSIsCiAgICAgICAgICAgICAgICJzb3VyY2UiIDogewogICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMjMwOjYyLTIzMDo2NiIsCiAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJPcGVyYW5kUmVmIgogICAgICAgICAgICAgICB9CiAgICAgICAgICAgIH0sCiAgICAgICAgICAgICJvcGVyYW5kIiA6IFsgewogICAgICAgICAgICAgICAibmFtZSIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAib3BlcmFuZFR5cGVTcGVjaWZpZXIiIDogewogICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMjMwOjMyLTIzMDo1OCIsCiAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJ7aHR0cDovL2hsNy5vcmcvZmhpcn1RdWVzdGlvbm5haXJlUmVzcG9uc2VTdGF0dXMiLAogICAgICAgICAgICAgICAgICAidHlwZSIgOiAiTmFtZWRUeXBlU3BlY2lmaWVyIgogICAgICAgICAgICAgICB9CiAgICAgICAgICAgIH0gXQogICAgICAgICB9LCB7CiAgICAgICAgICAgICJsb2NhdG9yIiA6ICIyMzE6MS0yMzE6NjgiLAogICAgICAgICAgICAibmFtZSIgOiAiVG9TdHJpbmciLAogICAgICAgICAgICAiY29udGV4dCIgOiAiUGF0aWVudCIsCiAgICAgICAgICAgICJhY2Nlc3NMZXZlbCIgOiAiUHVibGljIiwKICAgICAgICAgICAgInR5cGUiIDogIkZ1bmN0aW9uRGVmIiwKICAgICAgICAgICAgImV4cHJlc3Npb24iIDogewogICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMjMxOjU4LTIzMTo2OCIsCiAgICAgICAgICAgICAgICJwYXRoIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICJ0eXBlIiA6ICJQcm9wZXJ0eSIsCiAgICAgICAgICAgICAgICJzb3VyY2UiIDogewogICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMjMxOjU4LTIzMTo2MiIsCiAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJPcGVyYW5kUmVmIgogICAgICAgICAgICAgICB9CiAgICAgICAgICAgIH0sCiAgICAgICAgICAgICJvcGVyYW5kIiA6IFsgewogICAgICAgICAgICAgICAibmFtZSIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAib3BlcmFuZFR5cGVTcGVjaWZpZXIiIDogewogICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMjMxOjMyLTIzMTo1NCIsCiAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJ7aHR0cDovL2hsNy5vcmcvZmhpcn1SZWZlcmVuY2VIYW5kbGluZ1BvbGljeSIsCiAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJOYW1lZFR5cGVTcGVjaWZpZXIiCiAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgfSBdCiAgICAgICAgIH0sIHsKICAgICAgICAgICAgImxvY2F0b3IiIDogIjIzMjoxLTIzMjo2NiIsCiAgICAgICAgICAgICJuYW1lIiA6ICJUb1N0cmluZyIsCiAgICAgICAgICAgICJjb250ZXh0IiA6ICJQYXRpZW50IiwKICAgICAgICAgICAgImFjY2Vzc0xldmVsIiA6ICJQdWJsaWMiLAogICAgICAgICAgICAidHlwZSIgOiAiRnVuY3Rpb25EZWYiLAogICAgICAgICAgICAiZXhwcmVzc2lvbiIgOiB7CiAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIyMzI6NTYtMjMyOjY2IiwKICAgICAgICAgICAgICAgInBhdGgiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgInR5cGUiIDogIlByb3BlcnR5IiwKICAgICAgICAgICAgICAgInNvdXJjZSIgOiB7CiAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIyMzI6NTYtMjMyOjYwIiwKICAgICAgICAgICAgICAgICAgIm5hbWUiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk9wZXJhbmRSZWYiCiAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgfSwKICAgICAgICAgICAgIm9wZXJhbmQiIDogWyB7CiAgICAgICAgICAgICAgICJuYW1lIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICJvcGVyYW5kVHlwZVNwZWNpZmllciIgOiB7CiAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIyMzI6MzItMjMyOjUyIiwKICAgICAgICAgICAgICAgICAgIm5hbWUiIDogIntodHRwOi8vaGw3Lm9yZy9maGlyfVJlZmVyZW5jZVZlcnNpb25SdWxlcyIsCiAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJOYW1lZFR5cGVTcGVjaWZpZXIiCiAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgfSBdCiAgICAgICAgIH0sIHsKICAgICAgICAgICAgImxvY2F0b3IiIDogIjIzMzoxLTIzMzo2NyIsCiAgICAgICAgICAgICJuYW1lIiA6ICJUb1N0cmluZyIsCiAgICAgICAgICAgICJjb250ZXh0IiA6ICJQYXRpZW50IiwKICAgICAgICAgICAgImFjY2Vzc0xldmVsIiA6ICJQdWJsaWMiLAogICAgICAgICAgICAidHlwZSIgOiAiRnVuY3Rpb25EZWYiLAogICAgICAgICAgICAiZXhwcmVzc2lvbiIgOiB7CiAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIyMzM6NTctMjMzOjY3IiwKICAgICAgICAgICAgICAgInBhdGgiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgInR5cGUiIDogIlByb3BlcnR5IiwKICAgICAgICAgICAgICAgInNvdXJjZSIgOiB7CiAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIyMzM6NTctMjMzOjYxIiwKICAgICAgICAgICAgICAgICAgIm5hbWUiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk9wZXJhbmRSZWYiCiAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgfSwKICAgICAgICAgICAgIm9wZXJhbmQiIDogWyB7CiAgICAgICAgICAgICAgICJuYW1lIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICJvcGVyYW5kVHlwZVNwZWNpZmllciIgOiB7CiAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIyMzM6MzItMjMzOjUzIiwKICAgICAgICAgICAgICAgICAgIm5hbWUiIDogIntodHRwOi8vaGw3Lm9yZy9maGlyfVJlZmVycmVkRG9jdW1lbnRTdGF0dXMiLAogICAgICAgICAgICAgICAgICAidHlwZSIgOiAiTmFtZWRUeXBlU3BlY2lmaWVyIgogICAgICAgICAgICAgICB9CiAgICAgICAgICAgIH0gXQogICAgICAgICB9LCB7CiAgICAgICAgICAgICJsb2NhdG9yIiA6ICIyMzQ6MS0yMzQ6NjQiLAogICAgICAgICAgICAibmFtZSIgOiAiVG9TdHJpbmciLAogICAgICAgICAgICAiY29udGV4dCIgOiAiUGF0aWVudCIsCiAgICAgICAgICAgICJhY2Nlc3NMZXZlbCIgOiAiUHVibGljIiwKICAgICAgICAgICAgInR5cGUiIDogIkZ1bmN0aW9uRGVmIiwKICAgICAgICAgICAgImV4cHJlc3Npb24iIDogewogICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMjM0OjU0LTIzNDo2NCIsCiAgICAgICAgICAgICAgICJwYXRoIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICJ0eXBlIiA6ICJQcm9wZXJ0eSIsCiAgICAgICAgICAgICAgICJzb3VyY2UiIDogewogICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMjM0OjU0LTIzNDo1OCIsCiAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJPcGVyYW5kUmVmIgogICAgICAgICAgICAgICB9CiAgICAgICAgICAgIH0sCiAgICAgICAgICAgICJvcGVyYW5kIiA6IFsgewogICAgICAgICAgICAgICAibmFtZSIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAib3BlcmFuZFR5cGVTcGVjaWZpZXIiIDogewogICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMjM0OjMyLTIzNDo1MCIsCiAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJ7aHR0cDovL2hsNy5vcmcvZmhpcn1SZWxhdGVkQXJ0aWZhY3RUeXBlIiwKICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk5hbWVkVHlwZVNwZWNpZmllciIKICAgICAgICAgICAgICAgfQogICAgICAgICAgICB9IF0KICAgICAgICAgfSwgewogICAgICAgICAgICAibG9jYXRvciIgOiAiMjM1OjEtMjM1OjYyIiwKICAgICAgICAgICAgIm5hbWUiIDogIlRvU3RyaW5nIiwKICAgICAgICAgICAgImNvbnRleHQiIDogIlBhdGllbnQiLAogICAgICAgICAgICAiYWNjZXNzTGV2ZWwiIDogIlB1YmxpYyIsCiAgICAgICAgICAgICJ0eXBlIiA6ICJGdW5jdGlvbkRlZiIsCiAgICAgICAgICAgICJleHByZXNzaW9uIiA6IHsKICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjIzNTo1Mi0yMzU6NjIiLAogICAgICAgICAgICAgICAicGF0aCIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAidHlwZSIgOiAiUHJvcGVydHkiLAogICAgICAgICAgICAgICAic291cmNlIiA6IHsKICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjIzNTo1Mi0yMzU6NTYiLAogICAgICAgICAgICAgICAgICAibmFtZSIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAgICAidHlwZSIgOiAiT3BlcmFuZFJlZiIKICAgICAgICAgICAgICAgfQogICAgICAgICAgICB9LAogICAgICAgICAgICAib3BlcmFuZCIgOiBbIHsKICAgICAgICAgICAgICAgIm5hbWUiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgIm9wZXJhbmRUeXBlU3BlY2lmaWVyIiA6IHsKICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjIzNTozMi0yMzU6NDgiLAogICAgICAgICAgICAgICAgICAibmFtZSIgOiAie2h0dHA6Ly9obDcub3JnL2ZoaXJ9UmVtaXR0YW5jZU91dGNvbWUiLAogICAgICAgICAgICAgICAgICAidHlwZSIgOiAiTmFtZWRUeXBlU3BlY2lmaWVyIgogICAgICAgICAgICAgICB9CiAgICAgICAgICAgIH0gXQogICAgICAgICB9LCB7CiAgICAgICAgICAgICJsb2NhdG9yIiA6ICIyMzY6MS0yMzY6NTkiLAogICAgICAgICAgICAibmFtZSIgOiAiVG9TdHJpbmciLAogICAgICAgICAgICAiY29udGV4dCIgOiAiUGF0aWVudCIsCiAgICAgICAgICAgICJhY2Nlc3NMZXZlbCIgOiAiUHVibGljIiwKICAgICAgICAgICAgInR5cGUiIDogIkZ1bmN0aW9uRGVmIiwKICAgICAgICAgICAgImV4cHJlc3Npb24iIDogewogICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMjM2OjQ5LTIzNjo1OSIsCiAgICAgICAgICAgICAgICJwYXRoIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICJ0eXBlIiA6ICJQcm9wZXJ0eSIsCiAgICAgICAgICAgICAgICJzb3VyY2UiIDogewogICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMjM2OjQ5LTIzNjo1MyIsCiAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJPcGVyYW5kUmVmIgogICAgICAgICAgICAgICB9CiAgICAgICAgICAgIH0sCiAgICAgICAgICAgICJvcGVyYW5kIiA6IFsgewogICAgICAgICAgICAgICAibmFtZSIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAib3BlcmFuZFR5cGVTcGVjaWZpZXIiIDogewogICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMjM2OjMyLTIzNjo0NSIsCiAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJ7aHR0cDovL2hsNy5vcmcvZmhpcn1SZXBvc2l0b3J5VHlwZSIsCiAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJOYW1lZFR5cGVTcGVjaWZpZXIiCiAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgfSBdCiAgICAgICAgIH0sIHsKICAgICAgICAgICAgImxvY2F0b3IiIDogIjIzNzoxLTIzNzo1OCIsCiAgICAgICAgICAgICJuYW1lIiA6ICJUb1N0cmluZyIsCiAgICAgICAgICAgICJjb250ZXh0IiA6ICJQYXRpZW50IiwKICAgICAgICAgICAgImFjY2Vzc0xldmVsIiA6ICJQdWJsaWMiLAogICAgICAgICAgICAidHlwZSIgOiAiRnVuY3Rpb25EZWYiLAogICAgICAgICAgICAiZXhwcmVzc2lvbiIgOiB7CiAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIyMzc6NDgtMjM3OjU4IiwKICAgICAgICAgICAgICAgInBhdGgiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgInR5cGUiIDogIlByb3BlcnR5IiwKICAgICAgICAgICAgICAgInNvdXJjZSIgOiB7CiAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIyMzc6NDgtMjM3OjUyIiwKICAgICAgICAgICAgICAgICAgIm5hbWUiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk9wZXJhbmRSZWYiCiAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgfSwKICAgICAgICAgICAgIm9wZXJhbmQiIDogWyB7CiAgICAgICAgICAgICAgICJuYW1lIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICJvcGVyYW5kVHlwZVNwZWNpZmllciIgOiB7CiAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIyMzc6MzItMjM3OjQ0IiwKICAgICAgICAgICAgICAgICAgIm5hbWUiIDogIntodHRwOi8vaGw3Lm9yZy9maGlyfVJlcXVlc3RJbnRlbnQiLAogICAgICAgICAgICAgICAgICAidHlwZSIgOiAiTmFtZWRUeXBlU3BlY2lmaWVyIgogICAgICAgICAgICAgICB9CiAgICAgICAgICAgIH0gXQogICAgICAgICB9LCB7CiAgICAgICAgICAgICJsb2NhdG9yIiA6ICIyMzg6MS0yMzg6NjAiLAogICAgICAgICAgICAibmFtZSIgOiAiVG9TdHJpbmciLAogICAgICAgICAgICAiY29udGV4dCIgOiAiUGF0aWVudCIsCiAgICAgICAgICAgICJhY2Nlc3NMZXZlbCIgOiAiUHVibGljIiwKICAgICAgICAgICAgInR5cGUiIDogIkZ1bmN0aW9uRGVmIiwKICAgICAgICAgICAgImV4cHJlc3Npb24iIDogewogICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMjM4OjUwLTIzODo2MCIsCiAgICAgICAgICAgICAgICJwYXRoIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICJ0eXBlIiA6ICJQcm9wZXJ0eSIsCiAgICAgICAgICAgICAgICJzb3VyY2UiIDogewogICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMjM4OjUwLTIzODo1NCIsCiAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJPcGVyYW5kUmVmIgogICAgICAgICAgICAgICB9CiAgICAgICAgICAgIH0sCiAgICAgICAgICAgICJvcGVyYW5kIiA6IFsgewogICAgICAgICAgICAgICAibmFtZSIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAib3BlcmFuZFR5cGVTcGVjaWZpZXIiIDogewogICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMjM4OjMyLTIzODo0NiIsCiAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJ7aHR0cDovL2hsNy5vcmcvZmhpcn1SZXF1ZXN0UHJpb3JpdHkiLAogICAgICAgICAgICAgICAgICAidHlwZSIgOiAiTmFtZWRUeXBlU3BlY2lmaWVyIgogICAgICAgICAgICAgICB9CiAgICAgICAgICAgIH0gXQogICAgICAgICB9LCB7CiAgICAgICAgICAgICJsb2NhdG9yIiA6ICIyMzk6MS0yMzk6NTgiLAogICAgICAgICAgICAibmFtZSIgOiAiVG9TdHJpbmciLAogICAgICAgICAgICAiY29udGV4dCIgOiAiUGF0aWVudCIsCiAgICAgICAgICAgICJhY2Nlc3NMZXZlbCIgOiAiUHVibGljIiwKICAgICAgICAgICAgInR5cGUiIDogIkZ1bmN0aW9uRGVmIiwKICAgICAgICAgICAgImV4cHJlc3Npb24iIDogewogICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMjM5OjQ4LTIzOTo1OCIsCiAgICAgICAgICAgICAgICJwYXRoIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICJ0eXBlIiA6ICJQcm9wZXJ0eSIsCiAgICAgICAgICAgICAgICJzb3VyY2UiIDogewogICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMjM5OjQ4LTIzOTo1MiIsCiAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJPcGVyYW5kUmVmIgogICAgICAgICAgICAgICB9CiAgICAgICAgICAgIH0sCiAgICAgICAgICAgICJvcGVyYW5kIiA6IFsgewogICAgICAgICAgICAgICAibmFtZSIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAib3BlcmFuZFR5cGVTcGVjaWZpZXIiIDogewogICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMjM5OjMyLTIzOTo0NCIsCiAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJ7aHR0cDovL2hsNy5vcmcvZmhpcn1SZXF1ZXN0U3RhdHVzIiwKICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk5hbWVkVHlwZVNwZWNpZmllciIKICAgICAgICAgICAgICAgfQogICAgICAgICAgICB9IF0KICAgICAgICAgfSwgewogICAgICAgICAgICAibG9jYXRvciIgOiAiMjQwOjEtMjQwOjY0IiwKICAgICAgICAgICAgIm5hbWUiIDogIlRvU3RyaW5nIiwKICAgICAgICAgICAgImNvbnRleHQiIDogIlBhdGllbnQiLAogICAgICAgICAgICAiYWNjZXNzTGV2ZWwiIDogIlB1YmxpYyIsCiAgICAgICAgICAgICJ0eXBlIiA6ICJGdW5jdGlvbkRlZiIsCiAgICAgICAgICAgICJleHByZXNzaW9uIiA6IHsKICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjI0MDo1NC0yNDA6NjQiLAogICAgICAgICAgICAgICAicGF0aCIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAidHlwZSIgOiAiUHJvcGVydHkiLAogICAgICAgICAgICAgICAic291cmNlIiA6IHsKICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjI0MDo1NC0yNDA6NTgiLAogICAgICAgICAgICAgICAgICAibmFtZSIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAgICAidHlwZSIgOiAiT3BlcmFuZFJlZiIKICAgICAgICAgICAgICAgfQogICAgICAgICAgICB9LAogICAgICAgICAgICAib3BlcmFuZCIgOiBbIHsKICAgICAgICAgICAgICAgIm5hbWUiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgIm9wZXJhbmRUeXBlU3BlY2lmaWVyIiA6IHsKICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjI0MDozMi0yNDA6NTAiLAogICAgICAgICAgICAgICAgICAibmFtZSIgOiAie2h0dHA6Ly9obDcub3JnL2ZoaXJ9UmVzZWFyY2hFbGVtZW50VHlwZSIsCiAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJOYW1lZFR5cGVTcGVjaWZpZXIiCiAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgfSBdCiAgICAgICAgIH0sIHsKICAgICAgICAgICAgImxvY2F0b3IiIDogIjI0MToxLTI0MTo2NCIsCiAgICAgICAgICAgICJuYW1lIiA6ICJUb1N0cmluZyIsCiAgICAgICAgICAgICJjb250ZXh0IiA6ICJQYXRpZW50IiwKICAgICAgICAgICAgImFjY2Vzc0xldmVsIiA6ICJQdWJsaWMiLAogICAgICAgICAgICAidHlwZSIgOiAiRnVuY3Rpb25EZWYiLAogICAgICAgICAgICAiZXhwcmVzc2lvbiIgOiB7CiAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIyNDE6NTQtMjQxOjY0IiwKICAgICAgICAgICAgICAgInBhdGgiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgInR5cGUiIDogIlByb3BlcnR5IiwKICAgICAgICAgICAgICAgInNvdXJjZSIgOiB7CiAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIyNDE6NTQtMjQxOjU4IiwKICAgICAgICAgICAgICAgICAgIm5hbWUiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk9wZXJhbmRSZWYiCiAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgfSwKICAgICAgICAgICAgIm9wZXJhbmQiIDogWyB7CiAgICAgICAgICAgICAgICJuYW1lIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICJvcGVyYW5kVHlwZVNwZWNpZmllciIgOiB7CiAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIyNDE6MzItMjQxOjUwIiwKICAgICAgICAgICAgICAgICAgIm5hbWUiIDogIntodHRwOi8vaGw3Lm9yZy9maGlyfVJlc2VhcmNoU3R1ZHlTdGF0dXMiLAogICAgICAgICAgICAgICAgICAidHlwZSIgOiAiTmFtZWRUeXBlU3BlY2lmaWVyIgogICAgICAgICAgICAgICB9CiAgICAgICAgICAgIH0gXQogICAgICAgICB9LCB7CiAgICAgICAgICAgICJsb2NhdG9yIiA6ICIyNDI6MS0yNDI6NjYiLAogICAgICAgICAgICAibmFtZSIgOiAiVG9TdHJpbmciLAogICAgICAgICAgICAiY29udGV4dCIgOiAiUGF0aWVudCIsCiAgICAgICAgICAgICJhY2Nlc3NMZXZlbCIgOiAiUHVibGljIiwKICAgICAgICAgICAgInR5cGUiIDogIkZ1bmN0aW9uRGVmIiwKICAgICAgICAgICAgImV4cHJlc3Npb24iIDogewogICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMjQyOjU2LTI0Mjo2NiIsCiAgICAgICAgICAgICAgICJwYXRoIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICJ0eXBlIiA6ICJQcm9wZXJ0eSIsCiAgICAgICAgICAgICAgICJzb3VyY2UiIDogewogICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMjQyOjU2LTI0Mjo2MCIsCiAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJPcGVyYW5kUmVmIgogICAgICAgICAgICAgICB9CiAgICAgICAgICAgIH0sCiAgICAgICAgICAgICJvcGVyYW5kIiA6IFsgewogICAgICAgICAgICAgICAibmFtZSIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAib3BlcmFuZFR5cGVTcGVjaWZpZXIiIDogewogICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMjQyOjMyLTI0Mjo1MiIsCiAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJ7aHR0cDovL2hsNy5vcmcvZmhpcn1SZXNlYXJjaFN1YmplY3RTdGF0dXMiLAogICAgICAgICAgICAgICAgICAidHlwZSIgOiAiTmFtZWRUeXBlU3BlY2lmaWVyIgogICAgICAgICAgICAgICB9CiAgICAgICAgICAgIH0gXQogICAgICAgICB9LCB7CiAgICAgICAgICAgICJsb2NhdG9yIiA6ICIyNDM6MS0yNDM6NTciLAogICAgICAgICAgICAibmFtZSIgOiAiVG9TdHJpbmciLAogICAgICAgICAgICAiY29udGV4dCIgOiAiUGF0aWVudCIsCiAgICAgICAgICAgICJhY2Nlc3NMZXZlbCIgOiAiUHVibGljIiwKICAgICAgICAgICAgInR5cGUiIDogIkZ1bmN0aW9uRGVmIiwKICAgICAgICAgICAgImV4cHJlc3Npb24iIDogewogICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMjQzOjQ3LTI0Mzo1NyIsCiAgICAgICAgICAgICAgICJwYXRoIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICJ0eXBlIiA6ICJQcm9wZXJ0eSIsCiAgICAgICAgICAgICAgICJzb3VyY2UiIDogewogICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMjQzOjQ3LTI0Mzo1MSIsCiAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJPcGVyYW5kUmVmIgogICAgICAgICAgICAgICB9CiAgICAgICAgICAgIH0sCiAgICAgICAgICAgICJvcGVyYW5kIiA6IFsgewogICAgICAgICAgICAgICAibmFtZSIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAib3BlcmFuZFR5cGVTcGVjaWZpZXIiIDogewogICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMjQzOjMyLTI0Mzo0MyIsCiAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJ7aHR0cDovL2hsNy5vcmcvZmhpcn1SZXNvdXJjZVR5cGUiLAogICAgICAgICAgICAgICAgICAidHlwZSIgOiAiTmFtZWRUeXBlU3BlY2lmaWVyIgogICAgICAgICAgICAgICB9CiAgICAgICAgICAgIH0gXQogICAgICAgICB9LCB7CiAgICAgICAgICAgICJsb2NhdG9yIiA6ICIyNDQ6MS0yNDQ6NjYiLAogICAgICAgICAgICAibmFtZSIgOiAiVG9TdHJpbmciLAogICAgICAgICAgICAiY29udGV4dCIgOiAiUGF0aWVudCIsCiAgICAgICAgICAgICJhY2Nlc3NMZXZlbCIgOiAiUHVibGljIiwKICAgICAgICAgICAgInR5cGUiIDogIkZ1bmN0aW9uRGVmIiwKICAgICAgICAgICAgImV4cHJlc3Npb24iIDogewogICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMjQ0OjU2LTI0NDo2NiIsCiAgICAgICAgICAgICAgICJwYXRoIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICJ0eXBlIiA6ICJQcm9wZXJ0eSIsCiAgICAgICAgICAgICAgICJzb3VyY2UiIDogewogICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMjQ0OjU2LTI0NDo2MCIsCiAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJPcGVyYW5kUmVmIgogICAgICAgICAgICAgICB9CiAgICAgICAgICAgIH0sCiAgICAgICAgICAgICJvcGVyYW5kIiA6IFsgewogICAgICAgICAgICAgICAibmFtZSIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAib3BlcmFuZFR5cGVTcGVjaWZpZXIiIDogewogICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMjQ0OjMyLTI0NDo1MiIsCiAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJ7aHR0cDovL2hsNy5vcmcvZmhpcn1SZXNvdXJjZVZlcnNpb25Qb2xpY3kiLAogICAgICAgICAgICAgICAgICAidHlwZSIgOiAiTmFtZWRUeXBlU3BlY2lmaWVyIgogICAgICAgICAgICAgICB9CiAgICAgICAgICAgIH0gXQogICAgICAgICB9LCB7CiAgICAgICAgICAgICJsb2NhdG9yIiA6ICIyNDU6MS0yNDU6NTciLAogICAgICAgICAgICAibmFtZSIgOiAiVG9TdHJpbmciLAogICAgICAgICAgICAiY29udGV4dCIgOiAiUGF0aWVudCIsCiAgICAgICAgICAgICJhY2Nlc3NMZXZlbCIgOiAiUHVibGljIiwKICAgICAgICAgICAgInR5cGUiIDogIkZ1bmN0aW9uRGVmIiwKICAgICAgICAgICAgImV4cHJlc3Npb24iIDogewogICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMjQ1OjQ3LTI0NTo1NyIsCiAgICAgICAgICAgICAgICJwYXRoIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICJ0eXBlIiA6ICJQcm9wZXJ0eSIsCiAgICAgICAgICAgICAgICJzb3VyY2UiIDogewogICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMjQ1OjQ3LTI0NTo1MSIsCiAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJPcGVyYW5kUmVmIgogICAgICAgICAgICAgICB9CiAgICAgICAgICAgIH0sCiAgICAgICAgICAgICJvcGVyYW5kIiA6IFsgewogICAgICAgICAgICAgICAibmFtZSIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAib3BlcmFuZFR5cGVTcGVjaWZpZXIiIDogewogICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMjQ1OjMyLTI0NTo0MyIsCiAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJ7aHR0cDovL2hsNy5vcmcvZmhpcn1SZXNwb25zZVR5cGUiLAogICAgICAgICAgICAgICAgICAidHlwZSIgOiAiTmFtZWRUeXBlU3BlY2lmaWVyIgogICAgICAgICAgICAgICB9CiAgICAgICAgICAgIH0gXQogICAgICAgICB9LCB7CiAgICAgICAgICAgICJsb2NhdG9yIiA6ICIyNDY6MS0yNDY6NjYiLAogICAgICAgICAgICAibmFtZSIgOiAiVG9TdHJpbmciLAogICAgICAgICAgICAiY29udGV4dCIgOiAiUGF0aWVudCIsCiAgICAgICAgICAgICJhY2Nlc3NMZXZlbCIgOiAiUHVibGljIiwKICAgICAgICAgICAgInR5cGUiIDogIkZ1bmN0aW9uRGVmIiwKICAgICAgICAgICAgImV4cHJlc3Npb24iIDogewogICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMjQ2OjU2LTI0Njo2NiIsCiAgICAgICAgICAgICAgICJwYXRoIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICJ0eXBlIiA6ICJQcm9wZXJ0eSIsCiAgICAgICAgICAgICAgICJzb3VyY2UiIDogewogICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMjQ2OjU2LTI0Njo2MCIsCiAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJPcGVyYW5kUmVmIgogICAgICAgICAgICAgICB9CiAgICAgICAgICAgIH0sCiAgICAgICAgICAgICJvcGVyYW5kIiA6IFsgewogICAgICAgICAgICAgICAibmFtZSIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAib3BlcmFuZFR5cGVTcGVjaWZpZXIiIDogewogICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMjQ2OjMyLTI0Njo1MiIsCiAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJ7aHR0cDovL2hsNy5vcmcvZmhpcn1SZXN0ZnVsQ2FwYWJpbGl0eU1vZGUiLAogICAgICAgICAgICAgICAgICAidHlwZSIgOiAiTmFtZWRUeXBlU3BlY2lmaWVyIgogICAgICAgICAgICAgICB9CiAgICAgICAgICAgIH0gXQogICAgICAgICB9LCB7CiAgICAgICAgICAgICJsb2NhdG9yIiA6ICIyNDc6MS0yNDc6NjUiLAogICAgICAgICAgICAibmFtZSIgOiAiVG9TdHJpbmciLAogICAgICAgICAgICAiY29udGV4dCIgOiAiUGF0aWVudCIsCiAgICAgICAgICAgICJhY2Nlc3NMZXZlbCIgOiAiUHVibGljIiwKICAgICAgICAgICAgInR5cGUiIDogIkZ1bmN0aW9uRGVmIiwKICAgICAgICAgICAgImV4cHJlc3Npb24iIDogewogICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMjQ3OjU1LTI0Nzo2NSIsCiAgICAgICAgICAgICAgICJwYXRoIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICJ0eXBlIiA6ICJQcm9wZXJ0eSIsCiAgICAgICAgICAgICAgICJzb3VyY2UiIDogewogICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMjQ3OjU1LTI0Nzo1OSIsCiAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJPcGVyYW5kUmVmIgogICAgICAgICAgICAgICB9CiAgICAgICAgICAgIH0sCiAgICAgICAgICAgICJvcGVyYW5kIiA6IFsgewogICAgICAgICAgICAgICAibmFtZSIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAib3BlcmFuZFR5cGVTcGVjaWZpZXIiIDogewogICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMjQ3OjMyLTI0Nzo1MSIsCiAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJ7aHR0cDovL2hsNy5vcmcvZmhpcn1SaXNrQXNzZXNzbWVudFN0YXR1cyIsCiAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJOYW1lZFR5cGVTcGVjaWZpZXIiCiAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgfSBdCiAgICAgICAgIH0sIHsKICAgICAgICAgICAgImxvY2F0b3IiIDogIjI0ODoxLTI0ODo1NiIsCiAgICAgICAgICAgICJuYW1lIiA6ICJUb1N0cmluZyIsCiAgICAgICAgICAgICJjb250ZXh0IiA6ICJQYXRpZW50IiwKICAgICAgICAgICAgImFjY2Vzc0xldmVsIiA6ICJQdWJsaWMiLAogICAgICAgICAgICAidHlwZSIgOiAiRnVuY3Rpb25EZWYiLAogICAgICAgICAgICAiZXhwcmVzc2lvbiIgOiB7CiAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIyNDg6NDYtMjQ4OjU2IiwKICAgICAgICAgICAgICAgInBhdGgiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgInR5cGUiIDogIlByb3BlcnR5IiwKICAgICAgICAgICAgICAgInNvdXJjZSIgOiB7CiAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIyNDg6NDYtMjQ4OjUwIiwKICAgICAgICAgICAgICAgICAgIm5hbWUiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk9wZXJhbmRSZWYiCiAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgfSwKICAgICAgICAgICAgIm9wZXJhbmQiIDogWyB7CiAgICAgICAgICAgICAgICJuYW1lIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICJvcGVyYW5kVHlwZVNwZWNpZmllciIgOiB7CiAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIyNDg6MzItMjQ4OjQyIiwKICAgICAgICAgICAgICAgICAgIm5hbWUiIDogIntodHRwOi8vaGw3Lm9yZy9maGlyfVNQRFhMaWNlbnNlIiwKICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk5hbWVkVHlwZVNwZWNpZmllciIKICAgICAgICAgICAgICAgfQogICAgICAgICAgICB9IF0KICAgICAgICAgfSwgewogICAgICAgICAgICAibG9jYXRvciIgOiAiMjQ5OjEtMjQ5OjYxIiwKICAgICAgICAgICAgIm5hbWUiIDogIlRvU3RyaW5nIiwKICAgICAgICAgICAgImNvbnRleHQiIDogIlBhdGllbnQiLAogICAgICAgICAgICAiYWNjZXNzTGV2ZWwiIDogIlB1YmxpYyIsCiAgICAgICAgICAgICJ0eXBlIiA6ICJGdW5jdGlvbkRlZiIsCiAgICAgICAgICAgICJleHByZXNzaW9uIiA6IHsKICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjI0OTo1MS0yNDk6NjEiLAogICAgICAgICAgICAgICAicGF0aCIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAidHlwZSIgOiAiUHJvcGVydHkiLAogICAgICAgICAgICAgICAic291cmNlIiA6IHsKICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjI0OTo1MS0yNDk6NTUiLAogICAgICAgICAgICAgICAgICAibmFtZSIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAgICAidHlwZSIgOiAiT3BlcmFuZFJlZiIKICAgICAgICAgICAgICAgfQogICAgICAgICAgICB9LAogICAgICAgICAgICAib3BlcmFuZCIgOiBbIHsKICAgICAgICAgICAgICAgIm5hbWUiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgIm9wZXJhbmRUeXBlU3BlY2lmaWVyIiA6IHsKICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjI0OTozMi0yNDk6NDciLAogICAgICAgICAgICAgICAgICAibmFtZSIgOiAie2h0dHA6Ly9obDcub3JnL2ZoaXJ9U2VhcmNoQ29tcGFyYXRvciIsCiAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJOYW1lZFR5cGVTcGVjaWZpZXIiCiAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgfSBdCiAgICAgICAgIH0sIHsKICAgICAgICAgICAgImxvY2F0b3IiIDogIjI1MDoxLTI1MDo2MCIsCiAgICAgICAgICAgICJuYW1lIiA6ICJUb1N0cmluZyIsCiAgICAgICAgICAgICJjb250ZXh0IiA6ICJQYXRpZW50IiwKICAgICAgICAgICAgImFjY2Vzc0xldmVsIiA6ICJQdWJsaWMiLAogICAgICAgICAgICAidHlwZSIgOiAiRnVuY3Rpb25EZWYiLAogICAgICAgICAgICAiZXhwcmVzc2lvbiIgOiB7CiAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIyNTA6NTAtMjUwOjYwIiwKICAgICAgICAgICAgICAgInBhdGgiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgInR5cGUiIDogIlByb3BlcnR5IiwKICAgICAgICAgICAgICAgInNvdXJjZSIgOiB7CiAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIyNTA6NTAtMjUwOjU0IiwKICAgICAgICAgICAgICAgICAgIm5hbWUiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk9wZXJhbmRSZWYiCiAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgfSwKICAgICAgICAgICAgIm9wZXJhbmQiIDogWyB7CiAgICAgICAgICAgICAgICJuYW1lIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICJvcGVyYW5kVHlwZVNwZWNpZmllciIgOiB7CiAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIyNTA6MzItMjUwOjQ2IiwKICAgICAgICAgICAgICAgICAgIm5hbWUiIDogIntodHRwOi8vaGw3Lm9yZy9maGlyfVNlYXJjaEVudHJ5TW9kZSIsCiAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJOYW1lZFR5cGVTcGVjaWZpZXIiCiAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgfSBdCiAgICAgICAgIH0sIHsKICAgICAgICAgICAgImxvY2F0b3IiIDogIjI1MToxLTI1MTo2MyIsCiAgICAgICAgICAgICJuYW1lIiA6ICJUb1N0cmluZyIsCiAgICAgICAgICAgICJjb250ZXh0IiA6ICJQYXRpZW50IiwKICAgICAgICAgICAgImFjY2Vzc0xldmVsIiA6ICJQdWJsaWMiLAogICAgICAgICAgICAidHlwZSIgOiAiRnVuY3Rpb25EZWYiLAogICAgICAgICAgICAiZXhwcmVzc2lvbiIgOiB7CiAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIyNTE6NTMtMjUxOjYzIiwKICAgICAgICAgICAgICAgInBhdGgiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgInR5cGUiIDogIlByb3BlcnR5IiwKICAgICAgICAgICAgICAgInNvdXJjZSIgOiB7CiAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIyNTE6NTMtMjUxOjU3IiwKICAgICAgICAgICAgICAgICAgIm5hbWUiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk9wZXJhbmRSZWYiCiAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgfSwKICAgICAgICAgICAgIm9wZXJhbmQiIDogWyB7CiAgICAgICAgICAgICAgICJuYW1lIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICJvcGVyYW5kVHlwZVNwZWNpZmllciIgOiB7CiAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIyNTE6MzItMjUxOjQ5IiwKICAgICAgICAgICAgICAgICAgIm5hbWUiIDogIntodHRwOi8vaGw3Lm9yZy9maGlyfVNlYXJjaE1vZGlmaWVyQ29kZSIsCiAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJOYW1lZFR5cGVTcGVjaWZpZXIiCiAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgfSBdCiAgICAgICAgIH0sIHsKICAgICAgICAgICAgImxvY2F0b3IiIDogIjI1MjoxLTI1Mjo2MCIsCiAgICAgICAgICAgICJuYW1lIiA6ICJUb1N0cmluZyIsCiAgICAgICAgICAgICJjb250ZXh0IiA6ICJQYXRpZW50IiwKICAgICAgICAgICAgImFjY2Vzc0xldmVsIiA6ICJQdWJsaWMiLAogICAgICAgICAgICAidHlwZSIgOiAiRnVuY3Rpb25EZWYiLAogICAgICAgICAgICAiZXhwcmVzc2lvbiIgOiB7CiAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIyNTI6NTAtMjUyOjYwIiwKICAgICAgICAgICAgICAgInBhdGgiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgInR5cGUiIDogIlByb3BlcnR5IiwKICAgICAgICAgICAgICAgInNvdXJjZSIgOiB7CiAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIyNTI6NTAtMjUyOjU0IiwKICAgICAgICAgICAgICAgICAgIm5hbWUiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk9wZXJhbmRSZWYiCiAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgfSwKICAgICAgICAgICAgIm9wZXJhbmQiIDogWyB7CiAgICAgICAgICAgICAgICJuYW1lIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICJvcGVyYW5kVHlwZVNwZWNpZmllciIgOiB7CiAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIyNTI6MzItMjUyOjQ2IiwKICAgICAgICAgICAgICAgICAgIm5hbWUiIDogIntodHRwOi8vaGw3Lm9yZy9maGlyfVNlYXJjaFBhcmFtVHlwZSIsCiAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJOYW1lZFR5cGVTcGVjaWZpZXIiCiAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgfSBdCiAgICAgICAgIH0sIHsKICAgICAgICAgICAgImxvY2F0b3IiIDogIjI1MzoxLTI1Mzo1NiIsCiAgICAgICAgICAgICJuYW1lIiA6ICJUb1N0cmluZyIsCiAgICAgICAgICAgICJjb250ZXh0IiA6ICJQYXRpZW50IiwKICAgICAgICAgICAgImFjY2Vzc0xldmVsIiA6ICJQdWJsaWMiLAogICAgICAgICAgICAidHlwZSIgOiAiRnVuY3Rpb25EZWYiLAogICAgICAgICAgICAiZXhwcmVzc2lvbiIgOiB7CiAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIyNTM6NDYtMjUzOjU2IiwKICAgICAgICAgICAgICAgInBhdGgiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgInR5cGUiIDogIlByb3BlcnR5IiwKICAgICAgICAgICAgICAgInNvdXJjZSIgOiB7CiAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIyNTM6NDYtMjUzOjUwIiwKICAgICAgICAgICAgICAgICAgIm5hbWUiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk9wZXJhbmRSZWYiCiAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgfSwKICAgICAgICAgICAgIm9wZXJhbmQiIDogWyB7CiAgICAgICAgICAgICAgICJuYW1lIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICJvcGVyYW5kVHlwZVNwZWNpZmllciIgOiB7CiAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIyNTM6MzItMjUzOjQyIiwKICAgICAgICAgICAgICAgICAgIm5hbWUiIDogIntodHRwOi8vaGw3Lm9yZy9maGlyfVNlY3Rpb25Nb2RlIiwKICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk5hbWVkVHlwZVNwZWNpZmllciIKICAgICAgICAgICAgICAgfQogICAgICAgICAgICB9IF0KICAgICAgICAgfSwgewogICAgICAgICAgICAibG9jYXRvciIgOiAiMjU0OjEtMjU0OjU3IiwKICAgICAgICAgICAgIm5hbWUiIDogIlRvU3RyaW5nIiwKICAgICAgICAgICAgImNvbnRleHQiIDogIlBhdGllbnQiLAogICAgICAgICAgICAiYWNjZXNzTGV2ZWwiIDogIlB1YmxpYyIsCiAgICAgICAgICAgICJ0eXBlIiA6ICJGdW5jdGlvbkRlZiIsCiAgICAgICAgICAgICJleHByZXNzaW9uIiA6IHsKICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjI1NDo0Ny0yNTQ6NTciLAogICAgICAgICAgICAgICAicGF0aCIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAidHlwZSIgOiAiUHJvcGVydHkiLAogICAgICAgICAgICAgICAic291cmNlIiA6IHsKICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjI1NDo0Ny0yNTQ6NTEiLAogICAgICAgICAgICAgICAgICAibmFtZSIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAgICAidHlwZSIgOiAiT3BlcmFuZFJlZiIKICAgICAgICAgICAgICAgfQogICAgICAgICAgICB9LAogICAgICAgICAgICAib3BlcmFuZCIgOiBbIHsKICAgICAgICAgICAgICAgIm5hbWUiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgIm9wZXJhbmRUeXBlU3BlY2lmaWVyIiA6IHsKICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjI1NDozMi0yNTQ6NDMiLAogICAgICAgICAgICAgICAgICAibmFtZSIgOiAie2h0dHA6Ly9obDcub3JnL2ZoaXJ9U2VxdWVuY2VUeXBlIiwKICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk5hbWVkVHlwZVNwZWNpZmllciIKICAgICAgICAgICAgICAgfQogICAgICAgICAgICB9IF0KICAgICAgICAgfSwgewogICAgICAgICAgICAibG9jYXRvciIgOiAiMjU1OjEtMjU1OjY1IiwKICAgICAgICAgICAgIm5hbWUiIDogIlRvU3RyaW5nIiwKICAgICAgICAgICAgImNvbnRleHQiIDogIlBhdGllbnQiLAogICAgICAgICAgICAiYWNjZXNzTGV2ZWwiIDogIlB1YmxpYyIsCiAgICAgICAgICAgICJ0eXBlIiA6ICJGdW5jdGlvbkRlZiIsCiAgICAgICAgICAgICJleHByZXNzaW9uIiA6IHsKICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjI1NTo1NS0yNTU6NjUiLAogICAgICAgICAgICAgICAicGF0aCIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAidHlwZSIgOiAiUHJvcGVydHkiLAogICAgICAgICAgICAgICAic291cmNlIiA6IHsKICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjI1NTo1NS0yNTU6NTkiLAogICAgICAgICAgICAgICAgICAibmFtZSIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAgICAidHlwZSIgOiAiT3BlcmFuZFJlZiIKICAgICAgICAgICAgICAgfQogICAgICAgICAgICB9LAogICAgICAgICAgICAib3BlcmFuZCIgOiBbIHsKICAgICAgICAgICAgICAgIm5hbWUiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgIm9wZXJhbmRUeXBlU3BlY2lmaWVyIiA6IHsKICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjI1NTozMi0yNTU6NTEiLAogICAgICAgICAgICAgICAgICAibmFtZSIgOiAie2h0dHA6Ly9obDcub3JnL2ZoaXJ9U2VydmljZVJlcXVlc3RJbnRlbnQiLAogICAgICAgICAgICAgICAgICAidHlwZSIgOiAiTmFtZWRUeXBlU3BlY2lmaWVyIgogICAgICAgICAgICAgICB9CiAgICAgICAgICAgIH0gXQogICAgICAgICB9LCB7CiAgICAgICAgICAgICJsb2NhdG9yIiA6ICIyNTY6MS0yNTY6NjciLAogICAgICAgICAgICAibmFtZSIgOiAiVG9TdHJpbmciLAogICAgICAgICAgICAiY29udGV4dCIgOiAiUGF0aWVudCIsCiAgICAgICAgICAgICJhY2Nlc3NMZXZlbCIgOiAiUHVibGljIiwKICAgICAgICAgICAgInR5cGUiIDogIkZ1bmN0aW9uRGVmIiwKICAgICAgICAgICAgImV4cHJlc3Npb24iIDogewogICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMjU2OjU3LTI1Njo2NyIsCiAgICAgICAgICAgICAgICJwYXRoIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICJ0eXBlIiA6ICJQcm9wZXJ0eSIsCiAgICAgICAgICAgICAgICJzb3VyY2UiIDogewogICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMjU2OjU3LTI1Njo2MSIsCiAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJPcGVyYW5kUmVmIgogICAgICAgICAgICAgICB9CiAgICAgICAgICAgIH0sCiAgICAgICAgICAgICJvcGVyYW5kIiA6IFsgewogICAgICAgICAgICAgICAibmFtZSIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAib3BlcmFuZFR5cGVTcGVjaWZpZXIiIDogewogICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMjU2OjMyLTI1Njo1MyIsCiAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJ7aHR0cDovL2hsNy5vcmcvZmhpcn1TZXJ2aWNlUmVxdWVzdFByaW9yaXR5IiwKICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk5hbWVkVHlwZVNwZWNpZmllciIKICAgICAgICAgICAgICAgfQogICAgICAgICAgICB9IF0KICAgICAgICAgfSwgewogICAgICAgICAgICAibG9jYXRvciIgOiAiMjU3OjEtMjU3OjY1IiwKICAgICAgICAgICAgIm5hbWUiIDogIlRvU3RyaW5nIiwKICAgICAgICAgICAgImNvbnRleHQiIDogIlBhdGllbnQiLAogICAgICAgICAgICAiYWNjZXNzTGV2ZWwiIDogIlB1YmxpYyIsCiAgICAgICAgICAgICJ0eXBlIiA6ICJGdW5jdGlvbkRlZiIsCiAgICAgICAgICAgICJleHByZXNzaW9uIiA6IHsKICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjI1Nzo1NS0yNTc6NjUiLAogICAgICAgICAgICAgICAicGF0aCIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAidHlwZSIgOiAiUHJvcGVydHkiLAogICAgICAgICAgICAgICAic291cmNlIiA6IHsKICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjI1Nzo1NS0yNTc6NTkiLAogICAgICAgICAgICAgICAgICAibmFtZSIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAgICAidHlwZSIgOiAiT3BlcmFuZFJlZiIKICAgICAgICAgICAgICAgfQogICAgICAgICAgICB9LAogICAgICAgICAgICAib3BlcmFuZCIgOiBbIHsKICAgICAgICAgICAgICAgIm5hbWUiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgIm9wZXJhbmRUeXBlU3BlY2lmaWVyIiA6IHsKICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjI1NzozMi0yNTc6NTEiLAogICAgICAgICAgICAgICAgICAibmFtZSIgOiAie2h0dHA6Ly9obDcub3JnL2ZoaXJ9U2VydmljZVJlcXVlc3RTdGF0dXMiLAogICAgICAgICAgICAgICAgICAidHlwZSIgOiAiTmFtZWRUeXBlU3BlY2lmaWVyIgogICAgICAgICAgICAgICB9CiAgICAgICAgICAgIH0gXQogICAgICAgICB9LCB7CiAgICAgICAgICAgICJsb2NhdG9yIiA6ICIyNTg6MS0yNTg6NTciLAogICAgICAgICAgICAibmFtZSIgOiAiVG9TdHJpbmciLAogICAgICAgICAgICAiY29udGV4dCIgOiAiUGF0aWVudCIsCiAgICAgICAgICAgICJhY2Nlc3NMZXZlbCIgOiAiUHVibGljIiwKICAgICAgICAgICAgInR5cGUiIDogIkZ1bmN0aW9uRGVmIiwKICAgICAgICAgICAgImV4cHJlc3Npb24iIDogewogICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMjU4OjQ3LTI1ODo1NyIsCiAgICAgICAgICAgICAgICJwYXRoIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICJ0eXBlIiA6ICJQcm9wZXJ0eSIsCiAgICAgICAgICAgICAgICJzb3VyY2UiIDogewogICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMjU4OjQ3LTI1ODo1MSIsCiAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJPcGVyYW5kUmVmIgogICAgICAgICAgICAgICB9CiAgICAgICAgICAgIH0sCiAgICAgICAgICAgICJvcGVyYW5kIiA6IFsgewogICAgICAgICAgICAgICAibmFtZSIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAib3BlcmFuZFR5cGVTcGVjaWZpZXIiIDogewogICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMjU4OjMyLTI1ODo0MyIsCiAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJ7aHR0cDovL2hsNy5vcmcvZmhpcn1TbGljaW5nUnVsZXMiLAogICAgICAgICAgICAgICAgICAidHlwZSIgOiAiTmFtZWRUeXBlU3BlY2lmaWVyIgogICAgICAgICAgICAgICB9CiAgICAgICAgICAgIH0gXQogICAgICAgICB9LCB7CiAgICAgICAgICAgICJsb2NhdG9yIiA6ICIyNTk6MS0yNTk6NTUiLAogICAgICAgICAgICAibmFtZSIgOiAiVG9TdHJpbmciLAogICAgICAgICAgICAiY29udGV4dCIgOiAiUGF0aWVudCIsCiAgICAgICAgICAgICJhY2Nlc3NMZXZlbCIgOiAiUHVibGljIiwKICAgICAgICAgICAgInR5cGUiIDogIkZ1bmN0aW9uRGVmIiwKICAgICAgICAgICAgImV4cHJlc3Npb24iIDogewogICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMjU5OjQ1LTI1OTo1NSIsCiAgICAgICAgICAgICAgICJwYXRoIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICJ0eXBlIiA6ICJQcm9wZXJ0eSIsCiAgICAgICAgICAgICAgICJzb3VyY2UiIDogewogICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMjU5OjQ1LTI1OTo0OSIsCiAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJPcGVyYW5kUmVmIgogICAgICAgICAgICAgICB9CiAgICAgICAgICAgIH0sCiAgICAgICAgICAgICJvcGVyYW5kIiA6IFsgewogICAgICAgICAgICAgICAibmFtZSIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAib3BlcmFuZFR5cGVTcGVjaWZpZXIiIDogewogICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMjU5OjMyLTI1OTo0MSIsCiAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJ7aHR0cDovL2hsNy5vcmcvZmhpcn1TbG90U3RhdHVzIiwKICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk5hbWVkVHlwZVNwZWNpZmllciIKICAgICAgICAgICAgICAgfQogICAgICAgICAgICB9IF0KICAgICAgICAgfSwgewogICAgICAgICAgICAibG9jYXRvciIgOiAiMjYwOjEtMjYwOjU4IiwKICAgICAgICAgICAgIm5hbWUiIDogIlRvU3RyaW5nIiwKICAgICAgICAgICAgImNvbnRleHQiIDogIlBhdGllbnQiLAogICAgICAgICAgICAiYWNjZXNzTGV2ZWwiIDogIlB1YmxpYyIsCiAgICAgICAgICAgICJ0eXBlIiA6ICJGdW5jdGlvbkRlZiIsCiAgICAgICAgICAgICJleHByZXNzaW9uIiA6IHsKICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjI2MDo0OC0yNjA6NTgiLAogICAgICAgICAgICAgICAicGF0aCIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAidHlwZSIgOiAiUHJvcGVydHkiLAogICAgICAgICAgICAgICAic291cmNlIiA6IHsKICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjI2MDo0OC0yNjA6NTIiLAogICAgICAgICAgICAgICAgICAibmFtZSIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAgICAidHlwZSIgOiAiT3BlcmFuZFJlZiIKICAgICAgICAgICAgICAgfQogICAgICAgICAgICB9LAogICAgICAgICAgICAib3BlcmFuZCIgOiBbIHsKICAgICAgICAgICAgICAgIm5hbWUiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgIm9wZXJhbmRUeXBlU3BlY2lmaWVyIiA6IHsKICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjI2MDozMi0yNjA6NDQiLAogICAgICAgICAgICAgICAgICAibmFtZSIgOiAie2h0dHA6Ly9obDcub3JnL2ZoaXJ9U29ydERpcmVjdGlvbiIsCiAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJOYW1lZFR5cGVTcGVjaWZpZXIiCiAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgfSBdCiAgICAgICAgIH0sIHsKICAgICAgICAgICAgImxvY2F0b3IiIDogIjI2MToxLTI2MTo3MiIsCiAgICAgICAgICAgICJuYW1lIiA6ICJUb1N0cmluZyIsCiAgICAgICAgICAgICJjb250ZXh0IiA6ICJQYXRpZW50IiwKICAgICAgICAgICAgImFjY2Vzc0xldmVsIiA6ICJQdWJsaWMiLAogICAgICAgICAgICAidHlwZSIgOiAiRnVuY3Rpb25EZWYiLAogICAgICAgICAgICAiZXhwcmVzc2lvbiIgOiB7CiAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIyNjE6NjItMjYxOjcyIiwKICAgICAgICAgICAgICAgInBhdGgiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgInR5cGUiIDogIlByb3BlcnR5IiwKICAgICAgICAgICAgICAgInNvdXJjZSIgOiB7CiAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIyNjE6NjItMjYxOjY2IiwKICAgICAgICAgICAgICAgICAgIm5hbWUiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk9wZXJhbmRSZWYiCiAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgfSwKICAgICAgICAgICAgIm9wZXJhbmQiIDogWyB7CiAgICAgICAgICAgICAgICJuYW1lIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICJvcGVyYW5kVHlwZVNwZWNpZmllciIgOiB7CiAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIyNjE6MzItMjYxOjU4IiwKICAgICAgICAgICAgICAgICAgIm5hbWUiIDogIntodHRwOi8vaGw3Lm9yZy9maGlyfVNwZWNpbWVuQ29udGFpbmVkUHJlZmVyZW5jZSIsCiAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJOYW1lZFR5cGVTcGVjaWZpZXIiCiAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgfSBdCiAgICAgICAgIH0sIHsKICAgICAgICAgICAgImxvY2F0b3IiIDogIjI2MjoxLTI2Mjo1OSIsCiAgICAgICAgICAgICJuYW1lIiA6ICJUb1N0cmluZyIsCiAgICAgICAgICAgICJjb250ZXh0IiA6ICJQYXRpZW50IiwKICAgICAgICAgICAgImFjY2Vzc0xldmVsIiA6ICJQdWJsaWMiLAogICAgICAgICAgICAidHlwZSIgOiAiRnVuY3Rpb25EZWYiLAogICAgICAgICAgICAiZXhwcmVzc2lvbiIgOiB7CiAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIyNjI6NDktMjYyOjU5IiwKICAgICAgICAgICAgICAgInBhdGgiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgInR5cGUiIDogIlByb3BlcnR5IiwKICAgICAgICAgICAgICAgInNvdXJjZSIgOiB7CiAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIyNjI6NDktMjYyOjUzIiwKICAgICAgICAgICAgICAgICAgIm5hbWUiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk9wZXJhbmRSZWYiCiAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgfSwKICAgICAgICAgICAgIm9wZXJhbmQiIDogWyB7CiAgICAgICAgICAgICAgICJuYW1lIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICJvcGVyYW5kVHlwZVNwZWNpZmllciIgOiB7CiAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIyNjI6MzItMjYyOjQ1IiwKICAgICAgICAgICAgICAgICAgIm5hbWUiIDogIntodHRwOi8vaGw3Lm9yZy9maGlyfVNwZWNpbWVuU3RhdHVzIiwKICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk5hbWVkVHlwZVNwZWNpZmllciIKICAgICAgICAgICAgICAgfQogICAgICAgICAgICB9IF0KICAgICAgICAgfSwgewogICAgICAgICAgICAibG9jYXRvciIgOiAiMjYzOjEtMjYzOjUxIiwKICAgICAgICAgICAgIm5hbWUiIDogIlRvU3RyaW5nIiwKICAgICAgICAgICAgImNvbnRleHQiIDogIlBhdGllbnQiLAogICAgICAgICAgICAiYWNjZXNzTGV2ZWwiIDogIlB1YmxpYyIsCiAgICAgICAgICAgICJ0eXBlIiA6ICJGdW5jdGlvbkRlZiIsCiAgICAgICAgICAgICJleHByZXNzaW9uIiA6IHsKICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjI2Mzo0MS0yNjM6NTEiLAogICAgICAgICAgICAgICAicGF0aCIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAidHlwZSIgOiAiUHJvcGVydHkiLAogICAgICAgICAgICAgICAic291cmNlIiA6IHsKICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjI2Mzo0MS0yNjM6NDUiLAogICAgICAgICAgICAgICAgICAibmFtZSIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAgICAidHlwZSIgOiAiT3BlcmFuZFJlZiIKICAgICAgICAgICAgICAgfQogICAgICAgICAgICB9LAogICAgICAgICAgICAib3BlcmFuZCIgOiBbIHsKICAgICAgICAgICAgICAgIm5hbWUiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgIm9wZXJhbmRUeXBlU3BlY2lmaWVyIiA6IHsKICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjI2MzozMi0yNjM6MzciLAogICAgICAgICAgICAgICAgICAibmFtZSIgOiAie2h0dHA6Ly9obDcub3JnL2ZoaXJ9U3RhdHVzIiwKICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk5hbWVkVHlwZVNwZWNpZmllciIKICAgICAgICAgICAgICAgfQogICAgICAgICAgICB9IF0KICAgICAgICAgfSwgewogICAgICAgICAgICAibG9jYXRvciIgOiAiMjY0OjEtMjY0OjU1IiwKICAgICAgICAgICAgIm5hbWUiIDogIlRvU3RyaW5nIiwKICAgICAgICAgICAgImNvbnRleHQiIDogIlBhdGllbnQiLAogICAgICAgICAgICAiYWNjZXNzTGV2ZWwiIDogIlB1YmxpYyIsCiAgICAgICAgICAgICJ0eXBlIiA6ICJGdW5jdGlvbkRlZiIsCiAgICAgICAgICAgICJleHByZXNzaW9uIiA6IHsKICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjI2NDo0NS0yNjQ6NTUiLAogICAgICAgICAgICAgICAicGF0aCIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAidHlwZSIgOiAiUHJvcGVydHkiLAogICAgICAgICAgICAgICAic291cmNlIiA6IHsKICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjI2NDo0NS0yNjQ6NDkiLAogICAgICAgICAgICAgICAgICAibmFtZSIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAgICAidHlwZSIgOiAiT3BlcmFuZFJlZiIKICAgICAgICAgICAgICAgfQogICAgICAgICAgICB9LAogICAgICAgICAgICAib3BlcmFuZCIgOiBbIHsKICAgICAgICAgICAgICAgIm5hbWUiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgIm9wZXJhbmRUeXBlU3BlY2lmaWVyIiA6IHsKICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjI2NDozMi0yNjQ6NDEiLAogICAgICAgICAgICAgICAgICAibmFtZSIgOiAie2h0dHA6Ly9obDcub3JnL2ZoaXJ9U3RyYW5kVHlwZSIsCiAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJOYW1lZFR5cGVTcGVjaWZpZXIiCiAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgfSBdCiAgICAgICAgIH0sIHsKICAgICAgICAgICAgImxvY2F0b3IiIDogIjI2NToxLTI2NTo2OCIsCiAgICAgICAgICAgICJuYW1lIiA6ICJUb1N0cmluZyIsCiAgICAgICAgICAgICJjb250ZXh0IiA6ICJQYXRpZW50IiwKICAgICAgICAgICAgImFjY2Vzc0xldmVsIiA6ICJQdWJsaWMiLAogICAgICAgICAgICAidHlwZSIgOiAiRnVuY3Rpb25EZWYiLAogICAgICAgICAgICAiZXhwcmVzc2lvbiIgOiB7CiAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIyNjU6NTgtMjY1OjY4IiwKICAgICAgICAgICAgICAgInBhdGgiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgInR5cGUiIDogIlByb3BlcnR5IiwKICAgICAgICAgICAgICAgInNvdXJjZSIgOiB7CiAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIyNjU6NTgtMjY1OjYyIiwKICAgICAgICAgICAgICAgICAgIm5hbWUiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk9wZXJhbmRSZWYiCiAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgfSwKICAgICAgICAgICAgIm9wZXJhbmQiIDogWyB7CiAgICAgICAgICAgICAgICJuYW1lIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICJvcGVyYW5kVHlwZVNwZWNpZmllciIgOiB7CiAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIyNjU6MzItMjY1OjU0IiwKICAgICAgICAgICAgICAgICAgIm5hbWUiIDogIntodHRwOi8vaGw3Lm9yZy9maGlyfVN0cnVjdHVyZURlZmluaXRpb25LaW5kIiwKICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk5hbWVkVHlwZVNwZWNpZmllciIKICAgICAgICAgICAgICAgfQogICAgICAgICAgICB9IF0KICAgICAgICAgfSwgewogICAgICAgICAgICAibG9jYXRvciIgOiAiMjY2OjEtMjY2OjY4IiwKICAgICAgICAgICAgIm5hbWUiIDogIlRvU3RyaW5nIiwKICAgICAgICAgICAgImNvbnRleHQiIDogIlBhdGllbnQiLAogICAgICAgICAgICAiYWNjZXNzTGV2ZWwiIDogIlB1YmxpYyIsCiAgICAgICAgICAgICJ0eXBlIiA6ICJGdW5jdGlvbkRlZiIsCiAgICAgICAgICAgICJleHByZXNzaW9uIiA6IHsKICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjI2Njo1OC0yNjY6NjgiLAogICAgICAgICAgICAgICAicGF0aCIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAidHlwZSIgOiAiUHJvcGVydHkiLAogICAgICAgICAgICAgICAic291cmNlIiA6IHsKICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjI2Njo1OC0yNjY6NjIiLAogICAgICAgICAgICAgICAgICAibmFtZSIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAgICAidHlwZSIgOiAiT3BlcmFuZFJlZiIKICAgICAgICAgICAgICAgfQogICAgICAgICAgICB9LAogICAgICAgICAgICAib3BlcmFuZCIgOiBbIHsKICAgICAgICAgICAgICAgIm5hbWUiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgIm9wZXJhbmRUeXBlU3BlY2lmaWVyIiA6IHsKICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjI2NjozMi0yNjY6NTQiLAogICAgICAgICAgICAgICAgICAibmFtZSIgOiAie2h0dHA6Ly9obDcub3JnL2ZoaXJ9U3RydWN0dXJlTWFwQ29udGV4dFR5cGUiLAogICAgICAgICAgICAgICAgICAidHlwZSIgOiAiTmFtZWRUeXBlU3BlY2lmaWVyIgogICAgICAgICAgICAgICB9CiAgICAgICAgICAgIH0gXQogICAgICAgICB9LCB7CiAgICAgICAgICAgICJsb2NhdG9yIiA6ICIyNjc6MS0yNjc6NzAiLAogICAgICAgICAgICAibmFtZSIgOiAiVG9TdHJpbmciLAogICAgICAgICAgICAiY29udGV4dCIgOiAiUGF0aWVudCIsCiAgICAgICAgICAgICJhY2Nlc3NMZXZlbCIgOiAiUHVibGljIiwKICAgICAgICAgICAgInR5cGUiIDogIkZ1bmN0aW9uRGVmIiwKICAgICAgICAgICAgImV4cHJlc3Npb24iIDogewogICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMjY3OjYwLTI2Nzo3MCIsCiAgICAgICAgICAgICAgICJwYXRoIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICJ0eXBlIiA6ICJQcm9wZXJ0eSIsCiAgICAgICAgICAgICAgICJzb3VyY2UiIDogewogICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMjY3OjYwLTI2Nzo2NCIsCiAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJPcGVyYW5kUmVmIgogICAgICAgICAgICAgICB9CiAgICAgICAgICAgIH0sCiAgICAgICAgICAgICJvcGVyYW5kIiA6IFsgewogICAgICAgICAgICAgICAibmFtZSIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAib3BlcmFuZFR5cGVTcGVjaWZpZXIiIDogewogICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMjY3OjMyLTI2Nzo1NiIsCiAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJ7aHR0cDovL2hsNy5vcmcvZmhpcn1TdHJ1Y3R1cmVNYXBHcm91cFR5cGVNb2RlIiwKICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk5hbWVkVHlwZVNwZWNpZmllciIKICAgICAgICAgICAgICAgfQogICAgICAgICAgICB9IF0KICAgICAgICAgfSwgewogICAgICAgICAgICAibG9jYXRvciIgOiAiMjY4OjEtMjY4OjY2IiwKICAgICAgICAgICAgIm5hbWUiIDogIlRvU3RyaW5nIiwKICAgICAgICAgICAgImNvbnRleHQiIDogIlBhdGllbnQiLAogICAgICAgICAgICAiYWNjZXNzTGV2ZWwiIDogIlB1YmxpYyIsCiAgICAgICAgICAgICJ0eXBlIiA6ICJGdW5jdGlvbkRlZiIsCiAgICAgICAgICAgICJleHByZXNzaW9uIiA6IHsKICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjI2ODo1Ni0yNjg6NjYiLAogICAgICAgICAgICAgICAicGF0aCIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAidHlwZSIgOiAiUHJvcGVydHkiLAogICAgICAgICAgICAgICAic291cmNlIiA6IHsKICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjI2ODo1Ni0yNjg6NjAiLAogICAgICAgICAgICAgICAgICAibmFtZSIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAgICAidHlwZSIgOiAiT3BlcmFuZFJlZiIKICAgICAgICAgICAgICAgfQogICAgICAgICAgICB9LAogICAgICAgICAgICAib3BlcmFuZCIgOiBbIHsKICAgICAgICAgICAgICAgIm5hbWUiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgIm9wZXJhbmRUeXBlU3BlY2lmaWVyIiA6IHsKICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjI2ODozMi0yNjg6NTIiLAogICAgICAgICAgICAgICAgICAibmFtZSIgOiAie2h0dHA6Ly9obDcub3JnL2ZoaXJ9U3RydWN0dXJlTWFwSW5wdXRNb2RlIiwKICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk5hbWVkVHlwZVNwZWNpZmllciIKICAgICAgICAgICAgICAgfQogICAgICAgICAgICB9IF0KICAgICAgICAgfSwgewogICAgICAgICAgICAibG9jYXRvciIgOiAiMjY5OjEtMjY5OjY2IiwKICAgICAgICAgICAgIm5hbWUiIDogIlRvU3RyaW5nIiwKICAgICAgICAgICAgImNvbnRleHQiIDogIlBhdGllbnQiLAogICAgICAgICAgICAiYWNjZXNzTGV2ZWwiIDogIlB1YmxpYyIsCiAgICAgICAgICAgICJ0eXBlIiA6ICJGdW5jdGlvbkRlZiIsCiAgICAgICAgICAgICJleHByZXNzaW9uIiA6IHsKICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjI2OTo1Ni0yNjk6NjYiLAogICAgICAgICAgICAgICAicGF0aCIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAidHlwZSIgOiAiUHJvcGVydHkiLAogICAgICAgICAgICAgICAic291cmNlIiA6IHsKICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjI2OTo1Ni0yNjk6NjAiLAogICAgICAgICAgICAgICAgICAibmFtZSIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAgICAidHlwZSIgOiAiT3BlcmFuZFJlZiIKICAgICAgICAgICAgICAgfQogICAgICAgICAgICB9LAogICAgICAgICAgICAib3BlcmFuZCIgOiBbIHsKICAgICAgICAgICAgICAgIm5hbWUiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgIm9wZXJhbmRUeXBlU3BlY2lmaWVyIiA6IHsKICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjI2OTozMi0yNjk6NTIiLAogICAgICAgICAgICAgICAgICAibmFtZSIgOiAie2h0dHA6Ly9obDcub3JnL2ZoaXJ9U3RydWN0dXJlTWFwTW9kZWxNb2RlIiwKICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk5hbWVkVHlwZVNwZWNpZmllciIKICAgICAgICAgICAgICAgfQogICAgICAgICAgICB9IF0KICAgICAgICAgfSwgewogICAgICAgICAgICAibG9jYXRvciIgOiAiMjcwOjEtMjcwOjcxIiwKICAgICAgICAgICAgIm5hbWUiIDogIlRvU3RyaW5nIiwKICAgICAgICAgICAgImNvbnRleHQiIDogIlBhdGllbnQiLAogICAgICAgICAgICAiYWNjZXNzTGV2ZWwiIDogIlB1YmxpYyIsCiAgICAgICAgICAgICJ0eXBlIiA6ICJGdW5jdGlvbkRlZiIsCiAgICAgICAgICAgICJleHByZXNzaW9uIiA6IHsKICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjI3MDo2MS0yNzA6NzEiLAogICAgICAgICAgICAgICAicGF0aCIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAidHlwZSIgOiAiUHJvcGVydHkiLAogICAgICAgICAgICAgICAic291cmNlIiA6IHsKICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjI3MDo2MS0yNzA6NjUiLAogICAgICAgICAgICAgICAgICAibmFtZSIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAgICAidHlwZSIgOiAiT3BlcmFuZFJlZiIKICAgICAgICAgICAgICAgfQogICAgICAgICAgICB9LAogICAgICAgICAgICAib3BlcmFuZCIgOiBbIHsKICAgICAgICAgICAgICAgIm5hbWUiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgIm9wZXJhbmRUeXBlU3BlY2lmaWVyIiA6IHsKICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjI3MDozMi0yNzA6NTciLAogICAgICAgICAgICAgICAgICAibmFtZSIgOiAie2h0dHA6Ly9obDcub3JnL2ZoaXJ9U3RydWN0dXJlTWFwU291cmNlTGlzdE1vZGUiLAogICAgICAgICAgICAgICAgICAidHlwZSIgOiAiTmFtZWRUeXBlU3BlY2lmaWVyIgogICAgICAgICAgICAgICB9CiAgICAgICAgICAgIH0gXQogICAgICAgICB9LCB7CiAgICAgICAgICAgICJsb2NhdG9yIiA6ICIyNzE6MS0yNzE6NzEiLAogICAgICAgICAgICAibmFtZSIgOiAiVG9TdHJpbmciLAogICAgICAgICAgICAiY29udGV4dCIgOiAiUGF0aWVudCIsCiAgICAgICAgICAgICJhY2Nlc3NMZXZlbCIgOiAiUHVibGljIiwKICAgICAgICAgICAgInR5cGUiIDogIkZ1bmN0aW9uRGVmIiwKICAgICAgICAgICAgImV4cHJlc3Npb24iIDogewogICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMjcxOjYxLTI3MTo3MSIsCiAgICAgICAgICAgICAgICJwYXRoIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICJ0eXBlIiA6ICJQcm9wZXJ0eSIsCiAgICAgICAgICAgICAgICJzb3VyY2UiIDogewogICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMjcxOjYxLTI3MTo2NSIsCiAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJPcGVyYW5kUmVmIgogICAgICAgICAgICAgICB9CiAgICAgICAgICAgIH0sCiAgICAgICAgICAgICJvcGVyYW5kIiA6IFsgewogICAgICAgICAgICAgICAibmFtZSIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAib3BlcmFuZFR5cGVTcGVjaWZpZXIiIDogewogICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMjcxOjMyLTI3MTo1NyIsCiAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJ7aHR0cDovL2hsNy5vcmcvZmhpcn1TdHJ1Y3R1cmVNYXBUYXJnZXRMaXN0TW9kZSIsCiAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJOYW1lZFR5cGVTcGVjaWZpZXIiCiAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgfSBdCiAgICAgICAgIH0sIHsKICAgICAgICAgICAgImxvY2F0b3IiIDogIjI3MjoxLTI3Mjo2NiIsCiAgICAgICAgICAgICJuYW1lIiA6ICJUb1N0cmluZyIsCiAgICAgICAgICAgICJjb250ZXh0IiA6ICJQYXRpZW50IiwKICAgICAgICAgICAgImFjY2Vzc0xldmVsIiA6ICJQdWJsaWMiLAogICAgICAgICAgICAidHlwZSIgOiAiRnVuY3Rpb25EZWYiLAogICAgICAgICAgICAiZXhwcmVzc2lvbiIgOiB7CiAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIyNzI6NTYtMjcyOjY2IiwKICAgICAgICAgICAgICAgInBhdGgiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgInR5cGUiIDogIlByb3BlcnR5IiwKICAgICAgICAgICAgICAgInNvdXJjZSIgOiB7CiAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIyNzI6NTYtMjcyOjYwIiwKICAgICAgICAgICAgICAgICAgIm5hbWUiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk9wZXJhbmRSZWYiCiAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgfSwKICAgICAgICAgICAgIm9wZXJhbmQiIDogWyB7CiAgICAgICAgICAgICAgICJuYW1lIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICJvcGVyYW5kVHlwZVNwZWNpZmllciIgOiB7CiAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIyNzI6MzItMjcyOjUyIiwKICAgICAgICAgICAgICAgICAgIm5hbWUiIDogIntodHRwOi8vaGw3Lm9yZy9maGlyfVN0cnVjdHVyZU1hcFRyYW5zZm9ybSIsCiAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJOYW1lZFR5cGVTcGVjaWZpZXIiCiAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgfSBdCiAgICAgICAgIH0sIHsKICAgICAgICAgICAgImxvY2F0b3IiIDogIjI3MzoxLTI3Mzo2OCIsCiAgICAgICAgICAgICJuYW1lIiA6ICJUb1N0cmluZyIsCiAgICAgICAgICAgICJjb250ZXh0IiA6ICJQYXRpZW50IiwKICAgICAgICAgICAgImFjY2Vzc0xldmVsIiA6ICJQdWJsaWMiLAogICAgICAgICAgICAidHlwZSIgOiAiRnVuY3Rpb25EZWYiLAogICAgICAgICAgICAiZXhwcmVzc2lvbiIgOiB7CiAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIyNzM6NTgtMjczOjY4IiwKICAgICAgICAgICAgICAgInBhdGgiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgInR5cGUiIDogIlByb3BlcnR5IiwKICAgICAgICAgICAgICAgInNvdXJjZSIgOiB7CiAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIyNzM6NTgtMjczOjYyIiwKICAgICAgICAgICAgICAgICAgIm5hbWUiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk9wZXJhbmRSZWYiCiAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgfSwKICAgICAgICAgICAgIm9wZXJhbmQiIDogWyB7CiAgICAgICAgICAgICAgICJuYW1lIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICJvcGVyYW5kVHlwZVNwZWNpZmllciIgOiB7CiAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIyNzM6MzItMjczOjU0IiwKICAgICAgICAgICAgICAgICAgIm5hbWUiIDogIntodHRwOi8vaGw3Lm9yZy9maGlyfVN1YnNjcmlwdGlvbkNoYW5uZWxUeXBlIiwKICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk5hbWVkVHlwZVNwZWNpZmllciIKICAgICAgICAgICAgICAgfQogICAgICAgICAgICB9IF0KICAgICAgICAgfSwgewogICAgICAgICAgICAibG9jYXRvciIgOiAiMjc0OjEtMjc0OjYzIiwKICAgICAgICAgICAgIm5hbWUiIDogIlRvU3RyaW5nIiwKICAgICAgICAgICAgImNvbnRleHQiIDogIlBhdGllbnQiLAogICAgICAgICAgICAiYWNjZXNzTGV2ZWwiIDogIlB1YmxpYyIsCiAgICAgICAgICAgICJ0eXBlIiA6ICJGdW5jdGlvbkRlZiIsCiAgICAgICAgICAgICJleHByZXNzaW9uIiA6IHsKICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjI3NDo1My0yNzQ6NjMiLAogICAgICAgICAgICAgICAicGF0aCIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAidHlwZSIgOiAiUHJvcGVydHkiLAogICAgICAgICAgICAgICAic291cmNlIiA6IHsKICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjI3NDo1My0yNzQ6NTciLAogICAgICAgICAgICAgICAgICAibmFtZSIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAgICAidHlwZSIgOiAiT3BlcmFuZFJlZiIKICAgICAgICAgICAgICAgfQogICAgICAgICAgICB9LAogICAgICAgICAgICAib3BlcmFuZCIgOiBbIHsKICAgICAgICAgICAgICAgIm5hbWUiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgIm9wZXJhbmRUeXBlU3BlY2lmaWVyIiA6IHsKICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjI3NDozMi0yNzQ6NDkiLAogICAgICAgICAgICAgICAgICAibmFtZSIgOiAie2h0dHA6Ly9obDcub3JnL2ZoaXJ9U3Vic2NyaXB0aW9uU3RhdHVzIiwKICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk5hbWVkVHlwZVNwZWNpZmllciIKICAgICAgICAgICAgICAgfQogICAgICAgICAgICB9IF0KICAgICAgICAgfSwgewogICAgICAgICAgICAibG9jYXRvciIgOiAiMjc1OjEtMjc1OjY1IiwKICAgICAgICAgICAgIm5hbWUiIDogIlRvU3RyaW5nIiwKICAgICAgICAgICAgImNvbnRleHQiIDogIlBhdGllbnQiLAogICAgICAgICAgICAiYWNjZXNzTGV2ZWwiIDogIlB1YmxpYyIsCiAgICAgICAgICAgICJ0eXBlIiA6ICJGdW5jdGlvbkRlZiIsCiAgICAgICAgICAgICJleHByZXNzaW9uIiA6IHsKICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjI3NTo1NS0yNzU6NjUiLAogICAgICAgICAgICAgICAicGF0aCIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAidHlwZSIgOiAiUHJvcGVydHkiLAogICAgICAgICAgICAgICAic291cmNlIiA6IHsKICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjI3NTo1NS0yNzU6NTkiLAogICAgICAgICAgICAgICAgICAibmFtZSIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAgICAidHlwZSIgOiAiT3BlcmFuZFJlZiIKICAgICAgICAgICAgICAgfQogICAgICAgICAgICB9LAogICAgICAgICAgICAib3BlcmFuZCIgOiBbIHsKICAgICAgICAgICAgICAgIm5hbWUiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgIm9wZXJhbmRUeXBlU3BlY2lmaWVyIiA6IHsKICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjI3NTozMi0yNzU6NTEiLAogICAgICAgICAgICAgICAgICAibmFtZSIgOiAie2h0dHA6Ly9obDcub3JnL2ZoaXJ9U3VwcGx5RGVsaXZlcnlTdGF0dXMiLAogICAgICAgICAgICAgICAgICAidHlwZSIgOiAiTmFtZWRUeXBlU3BlY2lmaWVyIgogICAgICAgICAgICAgICB9CiAgICAgICAgICAgIH0gXQogICAgICAgICB9LCB7CiAgICAgICAgICAgICJsb2NhdG9yIiA6ICIyNzY6MS0yNzY6NjQiLAogICAgICAgICAgICAibmFtZSIgOiAiVG9TdHJpbmciLAogICAgICAgICAgICAiY29udGV4dCIgOiAiUGF0aWVudCIsCiAgICAgICAgICAgICJhY2Nlc3NMZXZlbCIgOiAiUHVibGljIiwKICAgICAgICAgICAgInR5cGUiIDogIkZ1bmN0aW9uRGVmIiwKICAgICAgICAgICAgImV4cHJlc3Npb24iIDogewogICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMjc2OjU0LTI3Njo2NCIsCiAgICAgICAgICAgICAgICJwYXRoIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICJ0eXBlIiA6ICJQcm9wZXJ0eSIsCiAgICAgICAgICAgICAgICJzb3VyY2UiIDogewogICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMjc2OjU0LTI3Njo1OCIsCiAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJPcGVyYW5kUmVmIgogICAgICAgICAgICAgICB9CiAgICAgICAgICAgIH0sCiAgICAgICAgICAgICJvcGVyYW5kIiA6IFsgewogICAgICAgICAgICAgICAibmFtZSIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAib3BlcmFuZFR5cGVTcGVjaWZpZXIiIDogewogICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMjc2OjMyLTI3Njo1MCIsCiAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJ7aHR0cDovL2hsNy5vcmcvZmhpcn1TdXBwbHlSZXF1ZXN0U3RhdHVzIiwKICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk5hbWVkVHlwZVNwZWNpZmllciIKICAgICAgICAgICAgICAgfQogICAgICAgICAgICB9IF0KICAgICAgICAgfSwgewogICAgICAgICAgICAibG9jYXRvciIgOiAiMjc3OjEtMjc3OjY5IiwKICAgICAgICAgICAgIm5hbWUiIDogIlRvU3RyaW5nIiwKICAgICAgICAgICAgImNvbnRleHQiIDogIlBhdGllbnQiLAogICAgICAgICAgICAiYWNjZXNzTGV2ZWwiIDogIlB1YmxpYyIsCiAgICAgICAgICAgICJ0eXBlIiA6ICJGdW5jdGlvbkRlZiIsCiAgICAgICAgICAgICJleHByZXNzaW9uIiA6IHsKICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjI3Nzo1OS0yNzc6NjkiLAogICAgICAgICAgICAgICAicGF0aCIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAidHlwZSIgOiAiUHJvcGVydHkiLAogICAgICAgICAgICAgICAic291cmNlIiA6IHsKICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjI3Nzo1OS0yNzc6NjMiLAogICAgICAgICAgICAgICAgICAibmFtZSIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAgICAidHlwZSIgOiAiT3BlcmFuZFJlZiIKICAgICAgICAgICAgICAgfQogICAgICAgICAgICB9LAogICAgICAgICAgICAib3BlcmFuZCIgOiBbIHsKICAgICAgICAgICAgICAgIm5hbWUiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgIm9wZXJhbmRUeXBlU3BlY2lmaWVyIiA6IHsKICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjI3NzozMi0yNzc6NTUiLAogICAgICAgICAgICAgICAgICAibmFtZSIgOiAie2h0dHA6Ly9obDcub3JnL2ZoaXJ9U3lzdGVtUmVzdGZ1bEludGVyYWN0aW9uIiwKICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk5hbWVkVHlwZVNwZWNpZmllciIKICAgICAgICAgICAgICAgfQogICAgICAgICAgICB9IF0KICAgICAgICAgfSwgewogICAgICAgICAgICAibG9jYXRvciIgOiAiMjc4OjEtMjc4OjU1IiwKICAgICAgICAgICAgIm5hbWUiIDogIlRvU3RyaW5nIiwKICAgICAgICAgICAgImNvbnRleHQiIDogIlBhdGllbnQiLAogICAgICAgICAgICAiYWNjZXNzTGV2ZWwiIDogIlB1YmxpYyIsCiAgICAgICAgICAgICJ0eXBlIiA6ICJGdW5jdGlvbkRlZiIsCiAgICAgICAgICAgICJleHByZXNzaW9uIiA6IHsKICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjI3ODo0NS0yNzg6NTUiLAogICAgICAgICAgICAgICAicGF0aCIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAidHlwZSIgOiAiUHJvcGVydHkiLAogICAgICAgICAgICAgICAic291cmNlIiA6IHsKICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjI3ODo0NS0yNzg6NDkiLAogICAgICAgICAgICAgICAgICAibmFtZSIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAgICAidHlwZSIgOiAiT3BlcmFuZFJlZiIKICAgICAgICAgICAgICAgfQogICAgICAgICAgICB9LAogICAgICAgICAgICAib3BlcmFuZCIgOiBbIHsKICAgICAgICAgICAgICAgIm5hbWUiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgIm9wZXJhbmRUeXBlU3BlY2lmaWVyIiA6IHsKICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjI3ODozMi0yNzg6NDEiLAogICAgICAgICAgICAgICAgICAibmFtZSIgOiAie2h0dHA6Ly9obDcub3JnL2ZoaXJ9VGFza0ludGVudCIsCiAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJOYW1lZFR5cGVTcGVjaWZpZXIiCiAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgfSBdCiAgICAgICAgIH0sIHsKICAgICAgICAgICAgImxvY2F0b3IiIDogIjI3OToxLTI3OTo1NyIsCiAgICAgICAgICAgICJuYW1lIiA6ICJUb1N0cmluZyIsCiAgICAgICAgICAgICJjb250ZXh0IiA6ICJQYXRpZW50IiwKICAgICAgICAgICAgImFjY2Vzc0xldmVsIiA6ICJQdWJsaWMiLAogICAgICAgICAgICAidHlwZSIgOiAiRnVuY3Rpb25EZWYiLAogICAgICAgICAgICAiZXhwcmVzc2lvbiIgOiB7CiAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIyNzk6NDctMjc5OjU3IiwKICAgICAgICAgICAgICAgInBhdGgiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgInR5cGUiIDogIlByb3BlcnR5IiwKICAgICAgICAgICAgICAgInNvdXJjZSIgOiB7CiAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIyNzk6NDctMjc5OjUxIiwKICAgICAgICAgICAgICAgICAgIm5hbWUiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk9wZXJhbmRSZWYiCiAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgfSwKICAgICAgICAgICAgIm9wZXJhbmQiIDogWyB7CiAgICAgICAgICAgICAgICJuYW1lIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICJvcGVyYW5kVHlwZVNwZWNpZmllciIgOiB7CiAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIyNzk6MzItMjc5OjQzIiwKICAgICAgICAgICAgICAgICAgIm5hbWUiIDogIntodHRwOi8vaGw3Lm9yZy9maGlyfVRhc2tQcmlvcml0eSIsCiAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJOYW1lZFR5cGVTcGVjaWZpZXIiCiAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgfSBdCiAgICAgICAgIH0sIHsKICAgICAgICAgICAgImxvY2F0b3IiIDogIjI4MDoxLTI4MDo1NSIsCiAgICAgICAgICAgICJuYW1lIiA6ICJUb1N0cmluZyIsCiAgICAgICAgICAgICJjb250ZXh0IiA6ICJQYXRpZW50IiwKICAgICAgICAgICAgImFjY2Vzc0xldmVsIiA6ICJQdWJsaWMiLAogICAgICAgICAgICAidHlwZSIgOiAiRnVuY3Rpb25EZWYiLAogICAgICAgICAgICAiZXhwcmVzc2lvbiIgOiB7CiAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIyODA6NDUtMjgwOjU1IiwKICAgICAgICAgICAgICAgInBhdGgiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgInR5cGUiIDogIlByb3BlcnR5IiwKICAgICAgICAgICAgICAgInNvdXJjZSIgOiB7CiAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIyODA6NDUtMjgwOjQ5IiwKICAgICAgICAgICAgICAgICAgIm5hbWUiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk9wZXJhbmRSZWYiCiAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgfSwKICAgICAgICAgICAgIm9wZXJhbmQiIDogWyB7CiAgICAgICAgICAgICAgICJuYW1lIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICJvcGVyYW5kVHlwZVNwZWNpZmllciIgOiB7CiAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIyODA6MzItMjgwOjQxIiwKICAgICAgICAgICAgICAgICAgIm5hbWUiIDogIntodHRwOi8vaGw3Lm9yZy9maGlyfVRhc2tTdGF0dXMiLAogICAgICAgICAgICAgICAgICAidHlwZSIgOiAiTmFtZWRUeXBlU3BlY2lmaWVyIgogICAgICAgICAgICAgICB9CiAgICAgICAgICAgIH0gXQogICAgICAgICB9LCB7CiAgICAgICAgICAgICJsb2NhdG9yIiA6ICIyODE6MS0yODE6NjciLAogICAgICAgICAgICAibmFtZSIgOiAiVG9TdHJpbmciLAogICAgICAgICAgICAiY29udGV4dCIgOiAiUGF0aWVudCIsCiAgICAgICAgICAgICJhY2Nlc3NMZXZlbCIgOiAiUHVibGljIiwKICAgICAgICAgICAgInR5cGUiIDogIkZ1bmN0aW9uRGVmIiwKICAgICAgICAgICAgImV4cHJlc3Npb24iIDogewogICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMjgxOjU3LTI4MTo2NyIsCiAgICAgICAgICAgICAgICJwYXRoIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICJ0eXBlIiA6ICJQcm9wZXJ0eSIsCiAgICAgICAgICAgICAgICJzb3VyY2UiIDogewogICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMjgxOjU3LTI4MTo2MSIsCiAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJPcGVyYW5kUmVmIgogICAgICAgICAgICAgICB9CiAgICAgICAgICAgIH0sCiAgICAgICAgICAgICJvcGVyYW5kIiA6IFsgewogICAgICAgICAgICAgICAibmFtZSIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAib3BlcmFuZFR5cGVTcGVjaWZpZXIiIDogewogICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMjgxOjMyLTI4MTo1MyIsCiAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJ7aHR0cDovL2hsNy5vcmcvZmhpcn1UZXN0UmVwb3J0QWN0aW9uUmVzdWx0IiwKICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk5hbWVkVHlwZVNwZWNpZmllciIKICAgICAgICAgICAgICAgfQogICAgICAgICAgICB9IF0KICAgICAgICAgfSwgewogICAgICAgICAgICAibG9jYXRvciIgOiAiMjgyOjEtMjgyOjcwIiwKICAgICAgICAgICAgIm5hbWUiIDogIlRvU3RyaW5nIiwKICAgICAgICAgICAgImNvbnRleHQiIDogIlBhdGllbnQiLAogICAgICAgICAgICAiYWNjZXNzTGV2ZWwiIDogIlB1YmxpYyIsCiAgICAgICAgICAgICJ0eXBlIiA6ICJGdW5jdGlvbkRlZiIsCiAgICAgICAgICAgICJleHByZXNzaW9uIiA6IHsKICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjI4Mjo2MC0yODI6NzAiLAogICAgICAgICAgICAgICAicGF0aCIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAidHlwZSIgOiAiUHJvcGVydHkiLAogICAgICAgICAgICAgICAic291cmNlIiA6IHsKICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjI4Mjo2MC0yODI6NjQiLAogICAgICAgICAgICAgICAgICAibmFtZSIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAgICAidHlwZSIgOiAiT3BlcmFuZFJlZiIKICAgICAgICAgICAgICAgfQogICAgICAgICAgICB9LAogICAgICAgICAgICAib3BlcmFuZCIgOiBbIHsKICAgICAgICAgICAgICAgIm5hbWUiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgIm9wZXJhbmRUeXBlU3BlY2lmaWVyIiA6IHsKICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjI4MjozMi0yODI6NTYiLAogICAgICAgICAgICAgICAgICAibmFtZSIgOiAie2h0dHA6Ly9obDcub3JnL2ZoaXJ9VGVzdFJlcG9ydFBhcnRpY2lwYW50VHlwZSIsCiAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJOYW1lZFR5cGVTcGVjaWZpZXIiCiAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgfSBdCiAgICAgICAgIH0sIHsKICAgICAgICAgICAgImxvY2F0b3IiIDogIjI4MzoxLTI4Mzo2MSIsCiAgICAgICAgICAgICJuYW1lIiA6ICJUb1N0cmluZyIsCiAgICAgICAgICAgICJjb250ZXh0IiA6ICJQYXRpZW50IiwKICAgICAgICAgICAgImFjY2Vzc0xldmVsIiA6ICJQdWJsaWMiLAogICAgICAgICAgICAidHlwZSIgOiAiRnVuY3Rpb25EZWYiLAogICAgICAgICAgICAiZXhwcmVzc2lvbiIgOiB7CiAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIyODM6NTEtMjgzOjYxIiwKICAgICAgICAgICAgICAgInBhdGgiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgInR5cGUiIDogIlByb3BlcnR5IiwKICAgICAgICAgICAgICAgInNvdXJjZSIgOiB7CiAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIyODM6NTEtMjgzOjU1IiwKICAgICAgICAgICAgICAgICAgIm5hbWUiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk9wZXJhbmRSZWYiCiAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgfSwKICAgICAgICAgICAgIm9wZXJhbmQiIDogWyB7CiAgICAgICAgICAgICAgICJuYW1lIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICJvcGVyYW5kVHlwZVNwZWNpZmllciIgOiB7CiAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIyODM6MzItMjgzOjQ3IiwKICAgICAgICAgICAgICAgICAgIm5hbWUiIDogIntodHRwOi8vaGw3Lm9yZy9maGlyfVRlc3RSZXBvcnRSZXN1bHQiLAogICAgICAgICAgICAgICAgICAidHlwZSIgOiAiTmFtZWRUeXBlU3BlY2lmaWVyIgogICAgICAgICAgICAgICB9CiAgICAgICAgICAgIH0gXQogICAgICAgICB9LCB7CiAgICAgICAgICAgICJsb2NhdG9yIiA6ICIyODQ6MS0yODQ6NjEiLAogICAgICAgICAgICAibmFtZSIgOiAiVG9TdHJpbmciLAogICAgICAgICAgICAiY29udGV4dCIgOiAiUGF0aWVudCIsCiAgICAgICAgICAgICJhY2Nlc3NMZXZlbCIgOiAiUHVibGljIiwKICAgICAgICAgICAgInR5cGUiIDogIkZ1bmN0aW9uRGVmIiwKICAgICAgICAgICAgImV4cHJlc3Npb24iIDogewogICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMjg0OjUxLTI4NDo2MSIsCiAgICAgICAgICAgICAgICJwYXRoIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICJ0eXBlIiA6ICJQcm9wZXJ0eSIsCiAgICAgICAgICAgICAgICJzb3VyY2UiIDogewogICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMjg0OjUxLTI4NDo1NSIsCiAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJPcGVyYW5kUmVmIgogICAgICAgICAgICAgICB9CiAgICAgICAgICAgIH0sCiAgICAgICAgICAgICJvcGVyYW5kIiA6IFsgewogICAgICAgICAgICAgICAibmFtZSIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAib3BlcmFuZFR5cGVTcGVjaWZpZXIiIDogewogICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMjg0OjMyLTI4NDo0NyIsCiAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJ7aHR0cDovL2hsNy5vcmcvZmhpcn1UZXN0UmVwb3J0U3RhdHVzIiwKICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk5hbWVkVHlwZVNwZWNpZmllciIKICAgICAgICAgICAgICAgfQogICAgICAgICAgICB9IF0KICAgICAgICAgfSwgewogICAgICAgICAgICAibG9jYXRvciIgOiAiMjg1OjEtMjg1OjcyIiwKICAgICAgICAgICAgIm5hbWUiIDogIlRvU3RyaW5nIiwKICAgICAgICAgICAgImNvbnRleHQiIDogIlBhdGllbnQiLAogICAgICAgICAgICAiYWNjZXNzTGV2ZWwiIDogIlB1YmxpYyIsCiAgICAgICAgICAgICJ0eXBlIiA6ICJGdW5jdGlvbkRlZiIsCiAgICAgICAgICAgICJleHByZXNzaW9uIiA6IHsKICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjI4NTo2Mi0yODU6NzIiLAogICAgICAgICAgICAgICAicGF0aCIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAidHlwZSIgOiAiUHJvcGVydHkiLAogICAgICAgICAgICAgICAic291cmNlIiA6IHsKICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjI4NTo2Mi0yODU6NjYiLAogICAgICAgICAgICAgICAgICAibmFtZSIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAgICAidHlwZSIgOiAiT3BlcmFuZFJlZiIKICAgICAgICAgICAgICAgfQogICAgICAgICAgICB9LAogICAgICAgICAgICAib3BlcmFuZCIgOiBbIHsKICAgICAgICAgICAgICAgIm5hbWUiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgIm9wZXJhbmRUeXBlU3BlY2lmaWVyIiA6IHsKICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjI4NTozMi0yODU6NTgiLAogICAgICAgICAgICAgICAgICAibmFtZSIgOiAie2h0dHA6Ly9obDcub3JnL2ZoaXJ9VGVzdFNjcmlwdFJlcXVlc3RNZXRob2RDb2RlIiwKICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk5hbWVkVHlwZVNwZWNpZmllciIKICAgICAgICAgICAgICAgfQogICAgICAgICAgICB9IF0KICAgICAgICAgfSwgewogICAgICAgICAgICAibG9jYXRvciIgOiAiMjg2OjEtMjg2OjU2IiwKICAgICAgICAgICAgIm5hbWUiIDogIlRvU3RyaW5nIiwKICAgICAgICAgICAgImNvbnRleHQiIDogIlBhdGllbnQiLAogICAgICAgICAgICAiYWNjZXNzTGV2ZWwiIDogIlB1YmxpYyIsCiAgICAgICAgICAgICJ0eXBlIiA6ICJGdW5jdGlvbkRlZiIsCiAgICAgICAgICAgICJleHByZXNzaW9uIiA6IHsKICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjI4Njo0Ni0yODY6NTYiLAogICAgICAgICAgICAgICAicGF0aCIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAidHlwZSIgOiAiUHJvcGVydHkiLAogICAgICAgICAgICAgICAic291cmNlIiA6IHsKICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjI4Njo0Ni0yODY6NTAiLAogICAgICAgICAgICAgICAgICAibmFtZSIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAgICAidHlwZSIgOiAiT3BlcmFuZFJlZiIKICAgICAgICAgICAgICAgfQogICAgICAgICAgICB9LAogICAgICAgICAgICAib3BlcmFuZCIgOiBbIHsKICAgICAgICAgICAgICAgIm5hbWUiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgIm9wZXJhbmRUeXBlU3BlY2lmaWVyIiA6IHsKICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjI4NjozMi0yODY6NDIiLAogICAgICAgICAgICAgICAgICAibmFtZSIgOiAie2h0dHA6Ly9obDcub3JnL2ZoaXJ9VHJpZ2dlclR5cGUiLAogICAgICAgICAgICAgICAgICAidHlwZSIgOiAiTmFtZWRUeXBlU3BlY2lmaWVyIgogICAgICAgICAgICAgICB9CiAgICAgICAgICAgIH0gXQogICAgICAgICB9LCB7CiAgICAgICAgICAgICJsb2NhdG9yIiA6ICIyODc6MS0yODc6NjMiLAogICAgICAgICAgICAibmFtZSIgOiAiVG9TdHJpbmciLAogICAgICAgICAgICAiY29udGV4dCIgOiAiUGF0aWVudCIsCiAgICAgICAgICAgICJhY2Nlc3NMZXZlbCIgOiAiUHVibGljIiwKICAgICAgICAgICAgInR5cGUiIDogIkZ1bmN0aW9uRGVmIiwKICAgICAgICAgICAgImV4cHJlc3Npb24iIDogewogICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMjg3OjUzLTI4Nzo2MyIsCiAgICAgICAgICAgICAgICJwYXRoIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICJ0eXBlIiA6ICJQcm9wZXJ0eSIsCiAgICAgICAgICAgICAgICJzb3VyY2UiIDogewogICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMjg3OjUzLTI4Nzo1NyIsCiAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJPcGVyYW5kUmVmIgogICAgICAgICAgICAgICB9CiAgICAgICAgICAgIH0sCiAgICAgICAgICAgICJvcGVyYW5kIiA6IFsgewogICAgICAgICAgICAgICAibmFtZSIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAib3BlcmFuZFR5cGVTcGVjaWZpZXIiIDogewogICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMjg3OjMyLTI4Nzo0OSIsCiAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJ7aHR0cDovL2hsNy5vcmcvZmhpcn1UeXBlRGVyaXZhdGlvblJ1bGUiLAogICAgICAgICAgICAgICAgICAidHlwZSIgOiAiTmFtZWRUeXBlU3BlY2lmaWVyIgogICAgICAgICAgICAgICB9CiAgICAgICAgICAgIH0gXQogICAgICAgICB9LCB7CiAgICAgICAgICAgICJsb2NhdG9yIiA6ICIyODg6MS0yODg6NjciLAogICAgICAgICAgICAibmFtZSIgOiAiVG9TdHJpbmciLAogICAgICAgICAgICAiY29udGV4dCIgOiAiUGF0aWVudCIsCiAgICAgICAgICAgICJhY2Nlc3NMZXZlbCIgOiAiUHVibGljIiwKICAgICAgICAgICAgInR5cGUiIDogIkZ1bmN0aW9uRGVmIiwKICAgICAgICAgICAgImV4cHJlc3Npb24iIDogewogICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMjg4OjU3LTI4ODo2NyIsCiAgICAgICAgICAgICAgICJwYXRoIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICJ0eXBlIiA6ICJQcm9wZXJ0eSIsCiAgICAgICAgICAgICAgICJzb3VyY2UiIDogewogICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMjg4OjU3LTI4ODo2MSIsCiAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJPcGVyYW5kUmVmIgogICAgICAgICAgICAgICB9CiAgICAgICAgICAgIH0sCiAgICAgICAgICAgICJvcGVyYW5kIiA6IFsgewogICAgICAgICAgICAgICAibmFtZSIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAib3BlcmFuZFR5cGVTcGVjaWZpZXIiIDogewogICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMjg4OjMyLTI4ODo1MyIsCiAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJ7aHR0cDovL2hsNy5vcmcvZmhpcn1UeXBlUmVzdGZ1bEludGVyYWN0aW9uIiwKICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk5hbWVkVHlwZVNwZWNpZmllciIKICAgICAgICAgICAgICAgfQogICAgICAgICAgICB9IF0KICAgICAgICAgfSwgewogICAgICAgICAgICAibG9jYXRvciIgOiAiMjg5OjEtMjg5OjU3IiwKICAgICAgICAgICAgIm5hbWUiIDogIlRvU3RyaW5nIiwKICAgICAgICAgICAgImNvbnRleHQiIDogIlBhdGllbnQiLAogICAgICAgICAgICAiYWNjZXNzTGV2ZWwiIDogIlB1YmxpYyIsCiAgICAgICAgICAgICJ0eXBlIiA6ICJGdW5jdGlvbkRlZiIsCiAgICAgICAgICAgICJleHByZXNzaW9uIiA6IHsKICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjI4OTo0Ny0yODk6NTciLAogICAgICAgICAgICAgICAicGF0aCIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAidHlwZSIgOiAiUHJvcGVydHkiLAogICAgICAgICAgICAgICAic291cmNlIiA6IHsKICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjI4OTo0Ny0yODk6NTEiLAogICAgICAgICAgICAgICAgICAibmFtZSIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAgICAidHlwZSIgOiAiT3BlcmFuZFJlZiIKICAgICAgICAgICAgICAgfQogICAgICAgICAgICB9LAogICAgICAgICAgICAib3BlcmFuZCIgOiBbIHsKICAgICAgICAgICAgICAgIm5hbWUiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgIm9wZXJhbmRUeXBlU3BlY2lmaWVyIiA6IHsKICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjI4OTozMi0yODk6NDMiLAogICAgICAgICAgICAgICAgICAibmFtZSIgOiAie2h0dHA6Ly9obDcub3JnL2ZoaXJ9VURJRW50cnlUeXBlIiwKICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk5hbWVkVHlwZVNwZWNpZmllciIKICAgICAgICAgICAgICAgfQogICAgICAgICAgICB9IF0KICAgICAgICAgfSwgewogICAgICAgICAgICAibG9jYXRvciIgOiAiMjkwOjEtMjkwOjU2IiwKICAgICAgICAgICAgIm5hbWUiIDogIlRvU3RyaW5nIiwKICAgICAgICAgICAgImNvbnRleHQiIDogIlBhdGllbnQiLAogICAgICAgICAgICAiYWNjZXNzTGV2ZWwiIDogIlB1YmxpYyIsCiAgICAgICAgICAgICJ0eXBlIiA6ICJGdW5jdGlvbkRlZiIsCiAgICAgICAgICAgICJleHByZXNzaW9uIiA6IHsKICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjI5MDo0Ni0yOTA6NTYiLAogICAgICAgICAgICAgICAicGF0aCIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAidHlwZSIgOiAiUHJvcGVydHkiLAogICAgICAgICAgICAgICAic291cmNlIiA6IHsKICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjI5MDo0Ni0yOTA6NTAiLAogICAgICAgICAgICAgICAgICAibmFtZSIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAgICAidHlwZSIgOiAiT3BlcmFuZFJlZiIKICAgICAgICAgICAgICAgfQogICAgICAgICAgICB9LAogICAgICAgICAgICAib3BlcmFuZCIgOiBbIHsKICAgICAgICAgICAgICAgIm5hbWUiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgIm9wZXJhbmRUeXBlU3BlY2lmaWVyIiA6IHsKICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjI5MDozMi0yOTA6NDIiLAogICAgICAgICAgICAgICAgICAibmFtZSIgOiAie2h0dHA6Ly9obDcub3JnL2ZoaXJ9VW5pdHNPZlRpbWUiLAogICAgICAgICAgICAgICAgICAidHlwZSIgOiAiTmFtZWRUeXBlU3BlY2lmaWVyIgogICAgICAgICAgICAgICB9CiAgICAgICAgICAgIH0gXQogICAgICAgICB9LCB7CiAgICAgICAgICAgICJsb2NhdG9yIiA6ICIyOTE6MS0yOTE6NDgiLAogICAgICAgICAgICAibmFtZSIgOiAiVG9TdHJpbmciLAogICAgICAgICAgICAiY29udGV4dCIgOiAiUGF0aWVudCIsCiAgICAgICAgICAgICJhY2Nlc3NMZXZlbCIgOiAiUHVibGljIiwKICAgICAgICAgICAgInR5cGUiIDogIkZ1bmN0aW9uRGVmIiwKICAgICAgICAgICAgImV4cHJlc3Npb24iIDogewogICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMjkxOjM4LTI5MTo0OCIsCiAgICAgICAgICAgICAgICJwYXRoIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICJ0eXBlIiA6ICJQcm9wZXJ0eSIsCiAgICAgICAgICAgICAgICJzb3VyY2UiIDogewogICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMjkxOjM4LTI5MTo0MiIsCiAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJPcGVyYW5kUmVmIgogICAgICAgICAgICAgICB9CiAgICAgICAgICAgIH0sCiAgICAgICAgICAgICJvcGVyYW5kIiA6IFsgewogICAgICAgICAgICAgICAibmFtZSIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAib3BlcmFuZFR5cGVTcGVjaWZpZXIiIDogewogICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMjkxOjMyLTI5MTozNCIsCiAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJ7aHR0cDovL2hsNy5vcmcvZmhpcn1Vc2UiLAogICAgICAgICAgICAgICAgICAidHlwZSIgOiAiTmFtZWRUeXBlU3BlY2lmaWVyIgogICAgICAgICAgICAgICB9CiAgICAgICAgICAgIH0gXQogICAgICAgICB9LCB7CiAgICAgICAgICAgICJsb2NhdG9yIiA6ICIyOTI6MS0yOTI6NTciLAogICAgICAgICAgICAibmFtZSIgOiAiVG9TdHJpbmciLAogICAgICAgICAgICAiY29udGV4dCIgOiAiUGF0aWVudCIsCiAgICAgICAgICAgICJhY2Nlc3NMZXZlbCIgOiAiUHVibGljIiwKICAgICAgICAgICAgInR5cGUiIDogIkZ1bmN0aW9uRGVmIiwKICAgICAgICAgICAgImV4cHJlc3Npb24iIDogewogICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMjkyOjQ3LTI5Mjo1NyIsCiAgICAgICAgICAgICAgICJwYXRoIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICJ0eXBlIiA6ICJQcm9wZXJ0eSIsCiAgICAgICAgICAgICAgICJzb3VyY2UiIDogewogICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMjkyOjQ3LTI5Mjo1MSIsCiAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJPcGVyYW5kUmVmIgogICAgICAgICAgICAgICB9CiAgICAgICAgICAgIH0sCiAgICAgICAgICAgICJvcGVyYW5kIiA6IFsgewogICAgICAgICAgICAgICAibmFtZSIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAib3BlcmFuZFR5cGVTcGVjaWZpZXIiIDogewogICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMjkyOjMyLTI5Mjo0MyIsCiAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJ7aHR0cDovL2hsNy5vcmcvZmhpcn1WYXJpYWJsZVR5cGUiLAogICAgICAgICAgICAgICAgICAidHlwZSIgOiAiTmFtZWRUeXBlU3BlY2lmaWVyIgogICAgICAgICAgICAgICB9CiAgICAgICAgICAgIH0gXQogICAgICAgICB9LCB7CiAgICAgICAgICAgICJsb2NhdG9yIiA6ICIyOTM6MS0yOTM6NTUiLAogICAgICAgICAgICAibmFtZSIgOiAiVG9TdHJpbmciLAogICAgICAgICAgICAiY29udGV4dCIgOiAiUGF0aWVudCIsCiAgICAgICAgICAgICJhY2Nlc3NMZXZlbCIgOiAiUHVibGljIiwKICAgICAgICAgICAgInR5cGUiIDogIkZ1bmN0aW9uRGVmIiwKICAgICAgICAgICAgImV4cHJlc3Npb24iIDogewogICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMjkzOjQ1LTI5Mzo1NSIsCiAgICAgICAgICAgICAgICJwYXRoIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICJ0eXBlIiA6ICJQcm9wZXJ0eSIsCiAgICAgICAgICAgICAgICJzb3VyY2UiIDogewogICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMjkzOjQ1LTI5Mzo0OSIsCiAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJPcGVyYW5kUmVmIgogICAgICAgICAgICAgICB9CiAgICAgICAgICAgIH0sCiAgICAgICAgICAgICJvcGVyYW5kIiA6IFsgewogICAgICAgICAgICAgICAibmFtZSIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAib3BlcmFuZFR5cGVTcGVjaWZpZXIiIDogewogICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMjkzOjMyLTI5Mzo0MSIsCiAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJ7aHR0cDovL2hsNy5vcmcvZmhpcn1WaXNpb25CYXNlIiwKICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk5hbWVkVHlwZVNwZWNpZmllciIKICAgICAgICAgICAgICAgfQogICAgICAgICAgICB9IF0KICAgICAgICAgfSwgewogICAgICAgICAgICAibG9jYXRvciIgOiAiMjk0OjEtMjk0OjU1IiwKICAgICAgICAgICAgIm5hbWUiIDogIlRvU3RyaW5nIiwKICAgICAgICAgICAgImNvbnRleHQiIDogIlBhdGllbnQiLAogICAgICAgICAgICAiYWNjZXNzTGV2ZWwiIDogIlB1YmxpYyIsCiAgICAgICAgICAgICJ0eXBlIiA6ICJGdW5jdGlvbkRlZiIsCiAgICAgICAgICAgICJleHByZXNzaW9uIiA6IHsKICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjI5NDo0NS0yOTQ6NTUiLAogICAgICAgICAgICAgICAicGF0aCIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAidHlwZSIgOiAiUHJvcGVydHkiLAogICAgICAgICAgICAgICAic291cmNlIiA6IHsKICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjI5NDo0NS0yOTQ6NDkiLAogICAgICAgICAgICAgICAgICAibmFtZSIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAgICAidHlwZSIgOiAiT3BlcmFuZFJlZiIKICAgICAgICAgICAgICAgfQogICAgICAgICAgICB9LAogICAgICAgICAgICAib3BlcmFuZCIgOiBbIHsKICAgICAgICAgICAgICAgIm5hbWUiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgIm9wZXJhbmRUeXBlU3BlY2lmaWVyIiA6IHsKICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjI5NDozMi0yOTQ6NDEiLAogICAgICAgICAgICAgICAgICAibmFtZSIgOiAie2h0dHA6Ly9obDcub3JnL2ZoaXJ9VmlzaW9uRXllcyIsCiAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJOYW1lZFR5cGVTcGVjaWZpZXIiCiAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgfSBdCiAgICAgICAgIH0sIHsKICAgICAgICAgICAgImxvY2F0b3IiIDogIjI5NToxLTI5NTo1NyIsCiAgICAgICAgICAgICJuYW1lIiA6ICJUb1N0cmluZyIsCiAgICAgICAgICAgICJjb250ZXh0IiA6ICJQYXRpZW50IiwKICAgICAgICAgICAgImFjY2Vzc0xldmVsIiA6ICJQdWJsaWMiLAogICAgICAgICAgICAidHlwZSIgOiAiRnVuY3Rpb25EZWYiLAogICAgICAgICAgICAiZXhwcmVzc2lvbiIgOiB7CiAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIyOTU6NDctMjk1OjU3IiwKICAgICAgICAgICAgICAgInBhdGgiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgInR5cGUiIDogIlByb3BlcnR5IiwKICAgICAgICAgICAgICAgInNvdXJjZSIgOiB7CiAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIyOTU6NDctMjk1OjUxIiwKICAgICAgICAgICAgICAgICAgIm5hbWUiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk9wZXJhbmRSZWYiCiAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgfSwKICAgICAgICAgICAgIm9wZXJhbmQiIDogWyB7CiAgICAgICAgICAgICAgICJuYW1lIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICJvcGVyYW5kVHlwZVNwZWNpZmllciIgOiB7CiAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIyOTU6MzItMjk1OjQzIiwKICAgICAgICAgICAgICAgICAgIm5hbWUiIDogIntodHRwOi8vaGw3Lm9yZy9maGlyfVZpc2lvblN0YXR1cyIsCiAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJOYW1lZFR5cGVTcGVjaWZpZXIiCiAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgfSBdCiAgICAgICAgIH0sIHsKICAgICAgICAgICAgImxvY2F0b3IiIDogIjI5NjoxLTI5Njo1OSIsCiAgICAgICAgICAgICJuYW1lIiA6ICJUb1N0cmluZyIsCiAgICAgICAgICAgICJjb250ZXh0IiA6ICJQYXRpZW50IiwKICAgICAgICAgICAgImFjY2Vzc0xldmVsIiA6ICJQdWJsaWMiLAogICAgICAgICAgICAidHlwZSIgOiAiRnVuY3Rpb25EZWYiLAogICAgICAgICAgICAiZXhwcmVzc2lvbiIgOiB7CiAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIyOTY6NDktMjk2OjU5IiwKICAgICAgICAgICAgICAgInBhdGgiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgInR5cGUiIDogIlByb3BlcnR5IiwKICAgICAgICAgICAgICAgInNvdXJjZSIgOiB7CiAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIyOTY6NDktMjk2OjUzIiwKICAgICAgICAgICAgICAgICAgIm5hbWUiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk9wZXJhbmRSZWYiCiAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgfSwKICAgICAgICAgICAgIm9wZXJhbmQiIDogWyB7CiAgICAgICAgICAgICAgICJuYW1lIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICJvcGVyYW5kVHlwZVNwZWNpZmllciIgOiB7CiAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIyOTY6MzItMjk2OjQ1IiwKICAgICAgICAgICAgICAgICAgIm5hbWUiIDogIntodHRwOi8vaGw3Lm9yZy9maGlyfVhQYXRoVXNhZ2VUeXBlIiwKICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk5hbWVkVHlwZVNwZWNpZmllciIKICAgICAgICAgICAgICAgfQogICAgICAgICAgICB9IF0KICAgICAgICAgfSwgewogICAgICAgICAgICAibG9jYXRvciIgOiAiMjk3OjEtMjk3OjU3IiwKICAgICAgICAgICAgIm5hbWUiIDogIlRvU3RyaW5nIiwKICAgICAgICAgICAgImNvbnRleHQiIDogIlBhdGllbnQiLAogICAgICAgICAgICAiYWNjZXNzTGV2ZWwiIDogIlB1YmxpYyIsCiAgICAgICAgICAgICJ0eXBlIiA6ICJGdW5jdGlvbkRlZiIsCiAgICAgICAgICAgICJleHByZXNzaW9uIiA6IHsKICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjI5Nzo0Ny0yOTc6NTciLAogICAgICAgICAgICAgICAicGF0aCIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAidHlwZSIgOiAiUHJvcGVydHkiLAogICAgICAgICAgICAgICAic291cmNlIiA6IHsKICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjI5Nzo0Ny0yOTc6NTEiLAogICAgICAgICAgICAgICAgICAibmFtZSIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAgICAidHlwZSIgOiAiT3BlcmFuZFJlZiIKICAgICAgICAgICAgICAgfQogICAgICAgICAgICB9LAogICAgICAgICAgICAib3BlcmFuZCIgOiBbIHsKICAgICAgICAgICAgICAgIm5hbWUiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgIm9wZXJhbmRUeXBlU3BlY2lmaWVyIiA6IHsKICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjI5NzozMi0yOTc6NDMiLAogICAgICAgICAgICAgICAgICAibmFtZSIgOiAie2h0dHA6Ly9obDcub3JnL2ZoaXJ9YmFzZTY0QmluYXJ5IiwKICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk5hbWVkVHlwZVNwZWNpZmllciIKICAgICAgICAgICAgICAgfQogICAgICAgICAgICB9IF0KICAgICAgICAgfSwgewogICAgICAgICAgICAibG9jYXRvciIgOiAiMjk4OjEtMjk4OjQ3IiwKICAgICAgICAgICAgIm5hbWUiIDogIlRvU3RyaW5nIiwKICAgICAgICAgICAgImNvbnRleHQiIDogIlBhdGllbnQiLAogICAgICAgICAgICAiYWNjZXNzTGV2ZWwiIDogIlB1YmxpYyIsCiAgICAgICAgICAgICJ0eXBlIiA6ICJGdW5jdGlvbkRlZiIsCiAgICAgICAgICAgICJleHByZXNzaW9uIiA6IHsKICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjI5ODozNy0yOTg6NDciLAogICAgICAgICAgICAgICAicGF0aCIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAidHlwZSIgOiAiUHJvcGVydHkiLAogICAgICAgICAgICAgICAic291cmNlIiA6IHsKICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjI5ODozNy0yOTg6NDEiLAogICAgICAgICAgICAgICAgICAibmFtZSIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAgICAidHlwZSIgOiAiT3BlcmFuZFJlZiIKICAgICAgICAgICAgICAgfQogICAgICAgICAgICB9LAogICAgICAgICAgICAib3BlcmFuZCIgOiBbIHsKICAgICAgICAgICAgICAgIm5hbWUiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgIm9wZXJhbmRUeXBlU3BlY2lmaWVyIiA6IHsKICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjI5ODozMi0yOTg6MzMiLAogICAgICAgICAgICAgICAgICAibmFtZSIgOiAie2h0dHA6Ly9obDcub3JnL2ZoaXJ9aWQiLAogICAgICAgICAgICAgICAgICAidHlwZSIgOiAiTmFtZWRUeXBlU3BlY2lmaWVyIgogICAgICAgICAgICAgICB9CiAgICAgICAgICAgIH0gXQogICAgICAgICB9LCB7CiAgICAgICAgICAgICJsb2NhdG9yIiA6ICIyOTk6MS0yOTk6NTMiLAogICAgICAgICAgICAibmFtZSIgOiAiVG9Cb29sZWFuIiwKICAgICAgICAgICAgImNvbnRleHQiIDogIlBhdGllbnQiLAogICAgICAgICAgICAiYWNjZXNzTGV2ZWwiIDogIlB1YmxpYyIsCiAgICAgICAgICAgICJ0eXBlIiA6ICJGdW5jdGlvbkRlZiIsCiAgICAgICAgICAgICJleHByZXNzaW9uIiA6IHsKICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjI5OTo0My0yOTk6NTMiLAogICAgICAgICAgICAgICAicGF0aCIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAidHlwZSIgOiAiUHJvcGVydHkiLAogICAgICAgICAgICAgICAic291cmNlIiA6IHsKICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjI5OTo0My0yOTk6NDciLAogICAgICAgICAgICAgICAgICAibmFtZSIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAgICAidHlwZSIgOiAiT3BlcmFuZFJlZiIKICAgICAgICAgICAgICAgfQogICAgICAgICAgICB9LAogICAgICAgICAgICAib3BlcmFuZCIgOiBbIHsKICAgICAgICAgICAgICAgIm5hbWUiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgIm9wZXJhbmRUeXBlU3BlY2lmaWVyIiA6IHsKICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjI5OTozMy0yOTk6MzkiLAogICAgICAgICAgICAgICAgICAibmFtZSIgOiAie2h0dHA6Ly9obDcub3JnL2ZoaXJ9Ym9vbGVhbiIsCiAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJOYW1lZFR5cGVTcGVjaWZpZXIiCiAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgfSBdCiAgICAgICAgIH0sIHsKICAgICAgICAgICAgImxvY2F0b3IiIDogIjMwMDoxLTMwMDo0NyIsCiAgICAgICAgICAgICJuYW1lIiA6ICJUb0RhdGUiLAogICAgICAgICAgICAiY29udGV4dCIgOiAiUGF0aWVudCIsCiAgICAgICAgICAgICJhY2Nlc3NMZXZlbCIgOiAiUHVibGljIiwKICAgICAgICAgICAgInR5cGUiIDogIkZ1bmN0aW9uRGVmIiwKICAgICAgICAgICAgImV4cHJlc3Npb24iIDogewogICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMzAwOjM3LTMwMDo0NyIsCiAgICAgICAgICAgICAgICJwYXRoIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICJ0eXBlIiA6ICJQcm9wZXJ0eSIsCiAgICAgICAgICAgICAgICJzb3VyY2UiIDogewogICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMzAwOjM3LTMwMDo0MSIsCiAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJPcGVyYW5kUmVmIgogICAgICAgICAgICAgICB9CiAgICAgICAgICAgIH0sCiAgICAgICAgICAgICJvcGVyYW5kIiA6IFsgewogICAgICAgICAgICAgICAibmFtZSIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAib3BlcmFuZFR5cGVTcGVjaWZpZXIiIDogewogICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMzAwOjMwLTMwMDozMyIsCiAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJ7aHR0cDovL2hsNy5vcmcvZmhpcn1kYXRlIiwKICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk5hbWVkVHlwZVNwZWNpZmllciIKICAgICAgICAgICAgICAgfQogICAgICAgICAgICB9IF0KICAgICAgICAgfSwgewogICAgICAgICAgICAibG9jYXRvciIgOiAiMzAxOjEtMzAxOjU1IiwKICAgICAgICAgICAgIm5hbWUiIDogIlRvRGF0ZVRpbWUiLAogICAgICAgICAgICAiY29udGV4dCIgOiAiUGF0aWVudCIsCiAgICAgICAgICAgICJhY2Nlc3NMZXZlbCIgOiAiUHVibGljIiwKICAgICAgICAgICAgInR5cGUiIDogIkZ1bmN0aW9uRGVmIiwKICAgICAgICAgICAgImV4cHJlc3Npb24iIDogewogICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMzAxOjQ1LTMwMTo1NSIsCiAgICAgICAgICAgICAgICJwYXRoIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICJ0eXBlIiA6ICJQcm9wZXJ0eSIsCiAgICAgICAgICAgICAgICJzb3VyY2UiIDogewogICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMzAxOjQ1LTMwMTo0OSIsCiAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJPcGVyYW5kUmVmIgogICAgICAgICAgICAgICB9CiAgICAgICAgICAgIH0sCiAgICAgICAgICAgICJvcGVyYW5kIiA6IFsgewogICAgICAgICAgICAgICAibmFtZSIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAib3BlcmFuZFR5cGVTcGVjaWZpZXIiIDogewogICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMzAxOjM0LTMwMTo0MSIsCiAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJ7aHR0cDovL2hsNy5vcmcvZmhpcn1kYXRlVGltZSIsCiAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJOYW1lZFR5cGVTcGVjaWZpZXIiCiAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgfSBdCiAgICAgICAgIH0sIHsKICAgICAgICAgICAgImxvY2F0b3IiIDogIjMwMjoxLTMwMjo1MyIsCiAgICAgICAgICAgICJuYW1lIiA6ICJUb0RlY2ltYWwiLAogICAgICAgICAgICAiY29udGV4dCIgOiAiUGF0aWVudCIsCiAgICAgICAgICAgICJhY2Nlc3NMZXZlbCIgOiAiUHVibGljIiwKICAgICAgICAgICAgInR5cGUiIDogIkZ1bmN0aW9uRGVmIiwKICAgICAgICAgICAgImV4cHJlc3Npb24iIDogewogICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMzAyOjQzLTMwMjo1MyIsCiAgICAgICAgICAgICAgICJwYXRoIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICJ0eXBlIiA6ICJQcm9wZXJ0eSIsCiAgICAgICAgICAgICAgICJzb3VyY2UiIDogewogICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMzAyOjQzLTMwMjo0NyIsCiAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJPcGVyYW5kUmVmIgogICAgICAgICAgICAgICB9CiAgICAgICAgICAgIH0sCiAgICAgICAgICAgICJvcGVyYW5kIiA6IFsgewogICAgICAgICAgICAgICAibmFtZSIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAib3BlcmFuZFR5cGVTcGVjaWZpZXIiIDogewogICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMzAyOjMzLTMwMjozOSIsCiAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJ7aHR0cDovL2hsNy5vcmcvZmhpcn1kZWNpbWFsIiwKICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk5hbWVkVHlwZVNwZWNpZmllciIKICAgICAgICAgICAgICAgfQogICAgICAgICAgICB9IF0KICAgICAgICAgfSwgewogICAgICAgICAgICAibG9jYXRvciIgOiAiMzAzOjEtMzAzOjU0IiwKICAgICAgICAgICAgIm5hbWUiIDogIlRvRGF0ZVRpbWUiLAogICAgICAgICAgICAiY29udGV4dCIgOiAiUGF0aWVudCIsCiAgICAgICAgICAgICJhY2Nlc3NMZXZlbCIgOiAiUHVibGljIiwKICAgICAgICAgICAgInR5cGUiIDogIkZ1bmN0aW9uRGVmIiwKICAgICAgICAgICAgImV4cHJlc3Npb24iIDogewogICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMzAzOjQ0LTMwMzo1NCIsCiAgICAgICAgICAgICAgICJwYXRoIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICJ0eXBlIiA6ICJQcm9wZXJ0eSIsCiAgICAgICAgICAgICAgICJzb3VyY2UiIDogewogICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMzAzOjQ0LTMwMzo0OCIsCiAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJPcGVyYW5kUmVmIgogICAgICAgICAgICAgICB9CiAgICAgICAgICAgIH0sCiAgICAgICAgICAgICJvcGVyYW5kIiA6IFsgewogICAgICAgICAgICAgICAibmFtZSIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAib3BlcmFuZFR5cGVTcGVjaWZpZXIiIDogewogICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMzAzOjM0LTMwMzo0MCIsCiAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJ7aHR0cDovL2hsNy5vcmcvZmhpcn1pbnN0YW50IiwKICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk5hbWVkVHlwZVNwZWNpZmllciIKICAgICAgICAgICAgICAgfQogICAgICAgICAgICB9IF0KICAgICAgICAgfSwgewogICAgICAgICAgICAibG9jYXRvciIgOiAiMzA0OjEtMzA0OjUzIiwKICAgICAgICAgICAgIm5hbWUiIDogIlRvSW50ZWdlciIsCiAgICAgICAgICAgICJjb250ZXh0IiA6ICJQYXRpZW50IiwKICAgICAgICAgICAgImFjY2Vzc0xldmVsIiA6ICJQdWJsaWMiLAogICAgICAgICAgICAidHlwZSIgOiAiRnVuY3Rpb25EZWYiLAogICAgICAgICAgICAiZXhwcmVzc2lvbiIgOiB7CiAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIzMDQ6NDMtMzA0OjUzIiwKICAgICAgICAgICAgICAgInBhdGgiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgInR5cGUiIDogIlByb3BlcnR5IiwKICAgICAgICAgICAgICAgInNvdXJjZSIgOiB7CiAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIzMDQ6NDMtMzA0OjQ3IiwKICAgICAgICAgICAgICAgICAgIm5hbWUiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk9wZXJhbmRSZWYiCiAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgfSwKICAgICAgICAgICAgIm9wZXJhbmQiIDogWyB7CiAgICAgICAgICAgICAgICJuYW1lIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICJvcGVyYW5kVHlwZVNwZWNpZmllciIgOiB7CiAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIzMDQ6MzMtMzA0OjM5IiwKICAgICAgICAgICAgICAgICAgIm5hbWUiIDogIntodHRwOi8vaGw3Lm9yZy9maGlyfWludGVnZXIiLAogICAgICAgICAgICAgICAgICAidHlwZSIgOiAiTmFtZWRUeXBlU3BlY2lmaWVyIgogICAgICAgICAgICAgICB9CiAgICAgICAgICAgIH0gXQogICAgICAgICB9LCB7CiAgICAgICAgICAgICJsb2NhdG9yIiA6ICIzMDU6MS0zMDU6NTEiLAogICAgICAgICAgICAibmFtZSIgOiAiVG9TdHJpbmciLAogICAgICAgICAgICAiY29udGV4dCIgOiAiUGF0aWVudCIsCiAgICAgICAgICAgICJhY2Nlc3NMZXZlbCIgOiAiUHVibGljIiwKICAgICAgICAgICAgInR5cGUiIDogIkZ1bmN0aW9uRGVmIiwKICAgICAgICAgICAgImV4cHJlc3Npb24iIDogewogICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMzA1OjQxLTMwNTo1MSIsCiAgICAgICAgICAgICAgICJwYXRoIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICJ0eXBlIiA6ICJQcm9wZXJ0eSIsCiAgICAgICAgICAgICAgICJzb3VyY2UiIDogewogICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMzA1OjQxLTMwNTo0NSIsCiAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJPcGVyYW5kUmVmIgogICAgICAgICAgICAgICB9CiAgICAgICAgICAgIH0sCiAgICAgICAgICAgICJvcGVyYW5kIiA6IFsgewogICAgICAgICAgICAgICAibmFtZSIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAib3BlcmFuZFR5cGVTcGVjaWZpZXIiIDogewogICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMzA1OjMyLTMwNTozNyIsCiAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJ7aHR0cDovL2hsNy5vcmcvZmhpcn1zdHJpbmciLAogICAgICAgICAgICAgICAgICAidHlwZSIgOiAiTmFtZWRUeXBlU3BlY2lmaWVyIgogICAgICAgICAgICAgICB9CiAgICAgICAgICAgIH0gXQogICAgICAgICB9LCB7CiAgICAgICAgICAgICJsb2NhdG9yIiA6ICIzMDY6MS0zMDY6NDciLAogICAgICAgICAgICAibmFtZSIgOiAiVG9UaW1lIiwKICAgICAgICAgICAgImNvbnRleHQiIDogIlBhdGllbnQiLAogICAgICAgICAgICAiYWNjZXNzTGV2ZWwiIDogIlB1YmxpYyIsCiAgICAgICAgICAgICJ0eXBlIiA6ICJGdW5jdGlvbkRlZiIsCiAgICAgICAgICAgICJleHByZXNzaW9uIiA6IHsKICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjMwNjozNy0zMDY6NDciLAogICAgICAgICAgICAgICAicGF0aCIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAidHlwZSIgOiAiUHJvcGVydHkiLAogICAgICAgICAgICAgICAic291cmNlIiA6IHsKICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjMwNjozNy0zMDY6NDEiLAogICAgICAgICAgICAgICAgICAibmFtZSIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAgICAidHlwZSIgOiAiT3BlcmFuZFJlZiIKICAgICAgICAgICAgICAgfQogICAgICAgICAgICB9LAogICAgICAgICAgICAib3BlcmFuZCIgOiBbIHsKICAgICAgICAgICAgICAgIm5hbWUiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgIm9wZXJhbmRUeXBlU3BlY2lmaWVyIiA6IHsKICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjMwNjozMC0zMDY6MzMiLAogICAgICAgICAgICAgICAgICAibmFtZSIgOiAie2h0dHA6Ly9obDcub3JnL2ZoaXJ9dGltZSIsCiAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJOYW1lZFR5cGVTcGVjaWZpZXIiCiAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgfSBdCiAgICAgICAgIH0sIHsKICAgICAgICAgICAgImxvY2F0b3IiIDogIjMwNzoxLTMwNzo0OCIsCiAgICAgICAgICAgICJuYW1lIiA6ICJUb1N0cmluZyIsCiAgICAgICAgICAgICJjb250ZXh0IiA6ICJQYXRpZW50IiwKICAgICAgICAgICAgImFjY2Vzc0xldmVsIiA6ICJQdWJsaWMiLAogICAgICAgICAgICAidHlwZSIgOiAiRnVuY3Rpb25EZWYiLAogICAgICAgICAgICAiZXhwcmVzc2lvbiIgOiB7CiAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIzMDc6MzgtMzA3OjQ4IiwKICAgICAgICAgICAgICAgInBhdGgiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgInR5cGUiIDogIlByb3BlcnR5IiwKICAgICAgICAgICAgICAgInNvdXJjZSIgOiB7CiAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIzMDc6MzgtMzA3OjQyIiwKICAgICAgICAgICAgICAgICAgIm5hbWUiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk9wZXJhbmRSZWYiCiAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgfSwKICAgICAgICAgICAgIm9wZXJhbmQiIDogWyB7CiAgICAgICAgICAgICAgICJuYW1lIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICJvcGVyYW5kVHlwZVNwZWNpZmllciIgOiB7CiAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIzMDc6MzItMzA3OjM0IiwKICAgICAgICAgICAgICAgICAgIm5hbWUiIDogIntodHRwOi8vaGw3Lm9yZy9maGlyfXVyaSIsCiAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJOYW1lZFR5cGVTcGVjaWZpZXIiCiAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgfSBdCiAgICAgICAgIH0sIHsKICAgICAgICAgICAgImxvY2F0b3IiIDogIjMwODoxLTMwODo1MCIsCiAgICAgICAgICAgICJuYW1lIiA6ICJUb1N0cmluZyIsCiAgICAgICAgICAgICJjb250ZXh0IiA6ICJQYXRpZW50IiwKICAgICAgICAgICAgImFjY2Vzc0xldmVsIiA6ICJQdWJsaWMiLAogICAgICAgICAgICAidHlwZSIgOiAiRnVuY3Rpb25EZWYiLAogICAgICAgICAgICAiZXhwcmVzc2lvbiIgOiB7CiAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIzMDg6NDAtMzA4OjUwIiwKICAgICAgICAgICAgICAgInBhdGgiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgInR5cGUiIDogIlByb3BlcnR5IiwKICAgICAgICAgICAgICAgInNvdXJjZSIgOiB7CiAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIzMDg6NDAtMzA4OjQ0IiwKICAgICAgICAgICAgICAgICAgIm5hbWUiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk9wZXJhbmRSZWYiCiAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgfSwKICAgICAgICAgICAgIm9wZXJhbmQiIDogWyB7CiAgICAgICAgICAgICAgICJuYW1lIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICJvcGVyYW5kVHlwZVNwZWNpZmllciIgOiB7CiAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIzMDg6MzItMzA4OjM2IiwKICAgICAgICAgICAgICAgICAgIm5hbWUiIDogIntodHRwOi8vaGw3Lm9yZy9maGlyfXhodG1sIiwKICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk5hbWVkVHlwZVNwZWNpZmllciIKICAgICAgICAgICAgICAgfQogICAgICAgICAgICB9IF0KICAgICAgICAgfSBdCiAgICAgIH0KICAgfQp9" - } - ] - }, - "request": { - "method": "PUT", - "url": "Library/DQMFHIRHelpers" - } - }, - { - "resource": { - "resourceType": "ValueSet", - "id": "2.16.840.1.113762.1.4.1", - "url": "http://cts.nlm.nih.gov/fhir/ValueSet/2.16.840.1.113762.1.4.1", - "identifier": [ - { - "system": "urn:ietf:rfc:3986", - "value": "2.16.840.1.113762.1.4.1" - } - ], - "version": "20150331", - "name": "ONCAdministrativeSex", - "title": "ONC Administrative Sex", - "status": "active", - "experimental": false, - "publisher": "NLM", - "description": "Codes representing possible values for ONC Administrative Sex.", - "expansion": { - "identifier": "20210506", - "timestamp": "2021-08-19T13:27:33-06:00", - "contains": [ - { - "system": "http://terminology.hl7.org/CodeSystem/v3-AdministrativeGender", - "version": "HL7V3.0_2020-11", - "code": "F", - "display": "Female" - }, - { - "system": "http://terminology.hl7.org/CodeSystem/v3-AdministrativeGender", - "version": "HL7V3.0_2020-11", - "code": "M", - "display": "Male" - } - ] - } - }, - "request": { - "method": "PUT", - "url": "ValueSet/2.16.840.1.113762.1.4.1" - } - }, - { - "resource": { - "resourceType": "Library", - "id": "FHIRCommon", - "extension": [ - { - "url": "http://hl7.org/fhir/us/cqfmeasures/StructureDefinition/cqfm-softwaresystem", - "valueReference": { - "reference": "Device/cqf-tooling" - } - } - ], - "url": "http://content.alphora.com/fhir/dqm/Library/FHIRCommon", - "version": "4.0.1", - "name": "FHIRCommon", - "title": "Library - FHIR Common", - "type": { - "coding": [ - { - "system": "http://terminology.hl7.org/CodeSystem/library-type", - "code": "logic-library" - } - ] - }, - "description": "A Shared library encapsulating valuable common terminologies and functions used in FHIR-based CQL artifacts.", - "jurisdiction": [ - { - "coding": [ - { - "system": "urn:iso:std:iso:3166", - "version": "4.0.1", - "code": "US", - "display": "United States of America" - } - ], - "text": "United States of America" - } - ], - "relatedArtifact": [ - { - "type": "depends-on", - "display": "FHIR model information", - "resource": "http://fhir.org/guides/cqf/common/Library/FHIR-ModelInfo|4.0.1" - }, - { - "type": "depends-on", - "display": "Library FHIRHelpers", - "resource": "http://content.alphora.com/fhir/dqm/Library/DQMFHIRHelpers" - }, - { - "type": "depends-on", - "display": "Code system LOINC", - "resource": "http://loinc.org" - }, - { - "type": "depends-on", - "display": "Code system SNOMEDCT", - "resource": "http://snomed.info/sct" - }, - { - "type": "depends-on", - "display": "Code system RoleCode", - "resource": "http://terminology.hl7.org/CodeSystem/v3-RoleCode" - }, - { - "type": "depends-on", - "display": "Code system Diagnosis Role", - "resource": "http://terminology.hl7.org/CodeSystem/diagnosis-role" - }, - { - "type": "depends-on", - "display": "Code system RequestIntent", - "resource": "http://terminology.hl7.org/CodeSystem/request-intent" - }, - { - "type": "depends-on", - "display": "Code system MedicationRequestCategory", - "resource": "http://terminology.hl7.org/CodeSystem/medicationrequest-category" - }, - { - "type": "depends-on", - "display": "Code system ConditionClinicalStatusCodes", - "resource": "http://terminology.hl7.org/CodeSystem/condition-clinical" - }, - { - "type": "depends-on", - "display": "Code system ConditionVerificationStatusCodes", - "resource": "http://terminology.hl7.org/CodeSystem/condition-ver-status" - }, - { - "type": "depends-on", - "display": "Code system AllergyIntoleranceClinicalStatusCodes", - "resource": "http://terminology.hl7.org/CodeSystem/allergyintolerance-clinical" - }, - { - "type": "depends-on", - "display": "Code system AllergyIntoleranceVerificationStatusCodes", - "resource": "http://terminology.hl7.org/CodeSystem/allergyintolerance-verification" - } - ], - "parameter": [ - { - "name": "Patient", - "use": "out", - "min": 0, - "max": "1", - "type": "Patient" - } - ], - "dataRequirement": [ - { - "type": "Patient", - "profile": [ - "http://hl7.org/fhir/StructureDefinition/Patient" - ] - } - ], - "content": [ - { - "contentType": "text/cql", - "data": "LyoKQGF1dGhvcjogQnJ5biBSaG9kZXMKQGRlc2NyaXB0aW9uOiBDb21tb24gdGVybWlub2xvZ2llcyBhbmQgZnVuY3Rpb25zIHVzZWQgaW4gRkhJUi1iYXNlZCBDUUwgYXJ0aWZhY3RzCiovCgpsaWJyYXJ5IEZISVJDb21tb24gdmVyc2lvbiAnNC4wLjEnCgp1c2luZyBGSElSIHZlcnNpb24gJzQuMC4xJwoKaW5jbHVkZSBEUU1GSElSSGVscGVycyBjYWxsZWQgRkhJUkhlbHBlcnMKCmNvZGVzeXN0ZW0gIkxPSU5DIjogJ2h0dHA6Ly9sb2luYy5vcmcnCmNvZGVzeXN0ZW0gIlNOT01FRENUIjogJ2h0dHA6Ly9zbm9tZWQuaW5mby9zY3QnCmNvZGVzeXN0ZW0gIlJvbGVDb2RlIjogJ2h0dHA6Ly90ZXJtaW5vbG9neS5obDcub3JnL0NvZGVTeXN0ZW0vdjMtUm9sZUNvZGUnCmNvZGVzeXN0ZW0gIkRpYWdub3NpcyBSb2xlIjogJ2h0dHA6Ly90ZXJtaW5vbG9neS5obDcub3JnL0NvZGVTeXN0ZW0vZGlhZ25vc2lzLXJvbGUnCmNvZGVzeXN0ZW0gIlJlcXVlc3RJbnRlbnQiOiAnaHR0cDovL3Rlcm1pbm9sb2d5LmhsNy5vcmcvQ29kZVN5c3RlbS9yZXF1ZXN0LWludGVudCcKY29kZXN5c3RlbSAiTWVkaWNhdGlvblJlcXVlc3RDYXRlZ29yeSI6ICdodHRwOi8vdGVybWlub2xvZ3kuaGw3Lm9yZy9Db2RlU3lzdGVtL21lZGljYXRpb25yZXF1ZXN0LWNhdGVnb3J5Jwpjb2Rlc3lzdGVtICJDb25kaXRpb25DbGluaWNhbFN0YXR1c0NvZGVzIjogJ2h0dHA6Ly90ZXJtaW5vbG9neS5obDcub3JnL0NvZGVTeXN0ZW0vY29uZGl0aW9uLWNsaW5pY2FsJwpjb2Rlc3lzdGVtICJDb25kaXRpb25WZXJpZmljYXRpb25TdGF0dXNDb2RlcyI6ICdodHRwOi8vdGVybWlub2xvZ3kuaGw3Lm9yZy9Db2RlU3lzdGVtL2NvbmRpdGlvbi12ZXItc3RhdHVzJwpjb2Rlc3lzdGVtICJBbGxlcmd5SW50b2xlcmFuY2VDbGluaWNhbFN0YXR1c0NvZGVzIjogJ2h0dHA6Ly90ZXJtaW5vbG9neS5obDcub3JnL0NvZGVTeXN0ZW0vYWxsZXJneWludG9sZXJhbmNlLWNsaW5pY2FsJwpjb2Rlc3lzdGVtICJBbGxlcmd5SW50b2xlcmFuY2VWZXJpZmljYXRpb25TdGF0dXNDb2RlcyI6ICdodHRwOi8vdGVybWlub2xvZ3kuaGw3Lm9yZy9Db2RlU3lzdGVtL2FsbGVyZ3lpbnRvbGVyYW5jZS12ZXJpZmljYXRpb24nCgpjb2RlICJCaXJ0aGRhdGUiOiAnMjExMTItOCcgZnJvbSAiTE9JTkMiIGRpc3BsYXkgJ0JpcnRoIGRhdGUnCmNvZGUgIkRlYWQiOiAnNDE5MDk5MDA5JyBmcm9tICJTTk9NRURDVCIgZGlzcGxheSAnRGVhZCcKY29kZSAiRVIiOiAnRVInIGZyb20gIlJvbGVDb2RlIiBkaXNwbGF5ICdFbWVyZ2VuY3kgcm9vbScKY29kZSAiSUNVIjogJ0lDVScgZnJvbSAiUm9sZUNvZGUiIGRpc3BsYXkgJ0ludGVuc2l2ZSBjYXJlIHVuaXQnCmNvZGUgIkJpbGxpbmciOiAnYmlsbGluZycgZnJvbSAiRGlhZ25vc2lzIFJvbGUiIGRpc3BsYXkgJ0JpbGxpbmcnCgovLyBDb25kaXRpb24gQ2xpbmljYWwgU3RhdHVzIENvZGVzIC0gQ29uc2lkZXIgdmFsdWUgc2V0cyBmb3IgdGhlc2UKY29kZSAiYWN0aXZlIjogJ2FjdGl2ZScgZnJvbSAiQ29uZGl0aW9uQ2xpbmljYWxTdGF0dXNDb2RlcyIKY29kZSAicmVjdXJyZW5jZSI6ICdyZWN1cnJlbmNlJyBmcm9tICJDb25kaXRpb25DbGluaWNhbFN0YXR1c0NvZGVzIgpjb2RlICJyZWxhcHNlIjogJ3JlbGFwc2UnIGZyb20gIkNvbmRpdGlvbkNsaW5pY2FsU3RhdHVzQ29kZXMiCmNvZGUgImluYWN0aXZlIjogJ2luYWN0aXZlJyBmcm9tICJDb25kaXRpb25DbGluaWNhbFN0YXR1c0NvZGVzIgpjb2RlICJyZW1pc3Npb24iOiAncmVtaXNzaW9uJyBmcm9tICJDb25kaXRpb25DbGluaWNhbFN0YXR1c0NvZGVzIgpjb2RlICJyZXNvbHZlZCI6ICdyZXNvbHZlZCcgZnJvbSAiQ29uZGl0aW9uQ2xpbmljYWxTdGF0dXNDb2RlcyIKCi8vIENvbmRpdGlvbiBWZXJpZmljYXRpb24gU3RhdHVzIENvZGVzIC0gQ29uc2lkZXIgdmFsdWUgc2V0cyBmb3IgdGhlc2UKY29kZSAidW5jb25maXJtZWQiOiAndW5jb25maXJtZWQnIGZyb20gQ29uZGl0aW9uVmVyaWZpY2F0aW9uU3RhdHVzQ29kZXMKY29kZSAicHJvdmlzaW9uYWwiOiAncHJvdmlzaW9uYWwnIGZyb20gQ29uZGl0aW9uVmVyaWZpY2F0aW9uU3RhdHVzQ29kZXMKY29kZSAiZGlmZmVyZW50aWFsIjogJ2RpZmZlcmVudGlhbCcgZnJvbSBDb25kaXRpb25WZXJpZmljYXRpb25TdGF0dXNDb2Rlcwpjb2RlICJjb25maXJtZWQiOiAnY29uZmlybWVkJyBmcm9tIENvbmRpdGlvblZlcmlmaWNhdGlvblN0YXR1c0NvZGVzCmNvZGUgInJlZnV0ZWQiOiAncmVmdXRlZCcgZnJvbSBDb25kaXRpb25WZXJpZmljYXRpb25TdGF0dXNDb2Rlcwpjb2RlICJlbnRlcmVkLWluLWVycm9yIjogJ2VudGVyZWQtaW4tZXJyb3InIGZyb20gQ29uZGl0aW9uVmVyaWZpY2F0aW9uU3RhdHVzQ29kZXMKCi8vIEFsbGVyZ3kvSW50b2xlcmFuY2UgQ2xpbmljYWwgU3RhdHVzIENvZGVzIC0gQ29uc2lkZXIgdmFsdWUgc2V0cyBmb3IgdGhlc2UKY29kZSAiYWxsZXJneS1hY3RpdmUiOiAnYWN0aXZlJyBmcm9tICJBbGxlcmd5SW50b2xlcmFuY2VDbGluaWNhbFN0YXR1c0NvZGVzIgpjb2RlICJhbGxlcmd5LWluYWN0aXZlIjogJ2luYWN0aXZlJyBmcm9tICJBbGxlcmd5SW50b2xlcmFuY2VDbGluaWNhbFN0YXR1c0NvZGVzIgpjb2RlICJhbGxlcmd5LXJlc29sdmVkIjogJ3Jlc29sdmVkJyBmcm9tICJBbGxlcmd5SW50b2xlcmFuY2VDbGluaWNhbFN0YXR1c0NvZGVzIgoKLy8gQWxsZXJneS9JbnRvbGVyYW5jZSBWZXJpZmljYXRpb24gU3RhdHVzIENvZGVzIC0gQ29uc2lkZXIgdmFsdWUgc2V0cyBmb3IgdGhlc2UKY29kZSAiYWxsZXJneS11bmNvbmZpcm1lZCI6ICd1bmNvbmZpcm1lZCcgZnJvbSBBbGxlcmd5SW50b2xlcmFuY2VWZXJpZmljYXRpb25TdGF0dXNDb2Rlcwpjb2RlICJhbGxlcmd5LWNvbmZpcm1lZCI6ICdjb25maXJtZWQnIGZyb20gQWxsZXJneUludG9sZXJhbmNlVmVyaWZpY2F0aW9uU3RhdHVzQ29kZXMKY29kZSAiYWxsZXJneS1yZWZ1dGVkIjogJ3JlZnV0ZWQnIGZyb20gQWxsZXJneUludG9sZXJhbmNlVmVyaWZpY2F0aW9uU3RhdHVzQ29kZXMKCi8vIE1lZGljYXRpb25SZXF1ZXN0IENhdGVnb3J5IENvZGVzCmNvZGUgIkNvbW11bml0eSI6ICdjb21tdW5pdHknIGZyb20gIk1lZGljYXRpb25SZXF1ZXN0Q2F0ZWdvcnkiIGRpc3BsYXkgJ0NvbW11bml0eScKY29kZSAiRGlzY2hhcmdlIjogJ2Rpc2NoYXJnZScgZnJvbSAiTWVkaWNhdGlvblJlcXVlc3RDYXRlZ29yeSIgZGlzcGxheSAnRGlzY2hhcmdlJwoKLy8gRGlhZ25vc2lzIFJvbGUgQ29kZXMKY29kZSAiQUQiOiAnQUQnIGZyb20gIkRpYWdub3NpcyBSb2xlIiBkaXNwbGF5ICdBZG1pc3Npb24gZGlhZ25vc2lzJwpjb2RlICJERCI6ICdERCcgZnJvbSAiRGlhZ25vc2lzIFJvbGUiIGRpc3BsYXkgJ0Rpc2NoYXJnZSBkaWFnbm9zaXMnCmNvZGUgIkNDIjogJ0NDJyBmcm9tICJEaWFnbm9zaXMgUm9sZSIgZGlzcGxheSAnQ2hpZWYgY29tcGxhaW50Jwpjb2RlICJDTSI6ICdDTScgZnJvbSAiRGlhZ25vc2lzIFJvbGUiIGRpc3BsYXkgJ0NvbW9yYmlkaXR5IGRpYWdub3NpcycKY29kZSAicHJlLW9wIjogJ3ByZS1vcCcgZnJvbSAiRGlhZ25vc2lzIFJvbGUiIGRpc3BsYXkgJ3ByZS1vcCBkaWFnbm9zaXMnCmNvZGUgInBvc3Qtb3AiOiAncG9zdC1vcCcgZnJvbSAiRGlhZ25vc2lzIFJvbGUiIGRpc3BsYXkgJ3Bvc3Qtb3AgZGlhZ25vc2lzJwpjb2RlICJiaWxsaW5nIjogJ2JpbGxpbmcnIGZyb20gIkRpYWdub3NpcyBSb2xlIiBkaXNwbGF5ICdiaWxsaW5nIGRpYWdub3NpcycKCmNvbnRleHQgUGF0aWVudAoKCi8qCkBkZXNjcmlwdGlvbjogTm9ybWFsaXplcyBhIHZhbHVlIHRoYXQgaXMgYSBjaG9pY2Ugb2YgdGltaW5nLXZhbHVlZCB0eXBlcyB0byBhbiBlcXVpdmFsZW50IGludGVydmFsCkBjb21tZW50OiBOb3JtYWxpemVzIGEgY2hvaWNlIHR5cGUgb2YgRkhJUi5kYXRlVGltZSwgRkhJUi5QZXJpb2QsIEZISVIuVGltaW5nLCBGSElSLmluc3RhbmNlLCBGSElSLnN0cmluZywgRkhJUi5BZ2UsIG9yIEZISVIuUmFuZ2UgdHlwZXMKdG8gYW4gZXF1aXZhbGVudCBpbnRlcnZhbC4gVGhpcyBzZWxlY3Rpb24gb2YgY2hvaWNlIHR5cGVzIGlzIGEgc3VwZXJzZXQgb2YgdGhlIG1ham9yaXR5IG9mIGNob2ljZSB0eXBlcyB0aGF0IGFyZSB1c2VkIGFzIHBvc3NpYmxlCnJlcHJlc2VudGF0aW9ucyBmb3IgdGltaW5nLXZhbHVlZCBlbGVtZW50cyBpbiBGSElSLCBhbGxvd2luZyB0aGlzIGZ1bmN0aW9uIHRvIGJlIHVzZWQgYWNyb3NzIGFueSByZXNvdXJjZS4gTk9URTogRHVlIHRvIHRoZQpjb21wbGV4aXR5IG9mIGRldGVybWluaW5nIGEgc2luZ2xlIGludGVydmFsIGZyb20gYSBUaW1pbmcgb3IgU3RyaW5nIHR5cGUsIHRoaXMgZnVuY3Rpb24gd2lsbCB0aHJvdyBhIHJ1bi10aW1lIGV4Y2VwdGlvbiBpZiBpdCBpcyB1c2VkCndpdGggYSBUaW1pbmcgb3IgU3RyaW5nLiBOT1RFOiBUaW1pbmcgaGFzIGJlZW4gbW92ZWQgdG8gVG9UaW1pbmdJbnRlcnZhbC4KKi8KZGVmaW5lIGZ1bmN0aW9uIFRvSW50ZXJ2YWwoY2hvaWNlIENob2ljZTxGSElSLmRhdGVUaW1lLCBGSElSLlBlcmlvZCwgRkhJUi5UaW1pbmcsIEZISVIuaW5zdGFudCwgRkhJUi5zdHJpbmcsIEZISVIuQWdlLCBGSElSLlJhbmdlPik6CiAgY2FzZQoJICB3aGVuIGNob2ljZSBpcyBGSElSLmRhdGVUaW1lIHRoZW4KICAgIAlJbnRlcnZhbFtGSElSSGVscGVycy5Ub0RhdGVUaW1lKGNob2ljZSBhcyBGSElSLmRhdGVUaW1lKSwgRkhJUkhlbHBlcnMuVG9EYXRlVGltZShjaG9pY2UgYXMgRkhJUi5kYXRlVGltZSldCgkJd2hlbiBjaG9pY2UgaXMgRkhJUi5QZXJpb2QgdGhlbgogIAkJRkhJUkhlbHBlcnMuVG9JbnRlcnZhbChjaG9pY2UgYXMgRkhJUi5QZXJpb2QpCgkJd2hlbiBjaG9pY2UgaXMgRkhJUi5pbnN0YW50IHRoZW4KCQkJSW50ZXJ2YWxbRkhJUkhlbHBlcnMuVG9EYXRlVGltZShjaG9pY2UgYXMgRkhJUi5pbnN0YW50KSwgRkhJUkhlbHBlcnMuVG9EYXRlVGltZShjaG9pY2UgYXMgRkhJUi5pbnN0YW50KV0KCQl3aGVuIGNob2ljZSBpcyBGSElSLkFnZSB0aGVuCgkJICBJbnRlcnZhbFtGSElSSGVscGVycy5Ub0RhdGUoUGF0aWVudC5iaXJ0aERhdGUpICsgRkhJUkhlbHBlcnMuVG9RdWFudGl0eShjaG9pY2UgYXMgRkhJUi5BZ2UpLAoJCQkgIEZISVJIZWxwZXJzLlRvRGF0ZShQYXRpZW50LmJpcnRoRGF0ZSkgKyBGSElSSGVscGVycy5Ub1F1YW50aXR5KGNob2ljZSBhcyBGSElSLkFnZSkgKyAxIHllYXIpCgkJd2hlbiBjaG9pY2UgaXMgRkhJUi5SYW5nZSB0aGVuCgkJICBJbnRlcnZhbFtGSElSSGVscGVycy5Ub0RhdGUoUGF0aWVudC5iaXJ0aERhdGUpICsgRkhJUkhlbHBlcnMuVG9RdWFudGl0eSgoY2hvaWNlIGFzIEZISVIuUmFuZ2UpLmxvdyksCgkJCSAgRkhJUkhlbHBlcnMuVG9EYXRlKFBhdGllbnQuYmlydGhEYXRlKSArIEZISVJIZWxwZXJzLlRvUXVhbnRpdHkoKGNob2ljZSBhcyBGSElSLlJhbmdlKS5oaWdoKSArIDEgeWVhcikKICAgIHdoZW4gY2hvaWNlIGlzIEZISVIuVGltaW5nIHRoZW4KCQkgIE1lc3NhZ2UobnVsbCBhcyBJbnRlcnZhbDxEYXRlVGltZT4sIHRydWUsICcxJywgJ0Vycm9yJywgJ0Nhbm5vdCBjb21wdXRlIGEgc2luZ2xlIGludGVydmFsIGZyb20gYSBUaW1pbmcgdHlwZScpCiAgICB3aGVuIGNob2ljZSBpcyBGSElSLnN0cmluZyB0aGVuCiAgICAgIE1lc3NhZ2UobnVsbCBhcyBJbnRlcnZhbDxEYXRlVGltZT4sIHRydWUsICcxJywgJ0Vycm9yJywgJ0Nhbm5vdCBjb21wdXRlIGFuIGludGVydmFsIGZyb20gYSBTdHJpbmcgdmFsdWUnKQoJCWVsc2UKICAgICAgbnVsbCBhcyBJbnRlcnZhbDxEYXRlVGltZT4KCWVuZAoKCi8qCkBkZXNjcmlwdGlvbjogUmV0dXJucyBhIGxpc3Qgb2YgaW50ZXJ2YWxzIHJlcHJlc2VudGluZyB0aGUgbm9ybWFsaXplZCBFdmVudCBvciBCb3VuZHMgb2YgYSBUaW1pbmcgcmVzb3VyY2UuCkBjb21tZW50OiBOT1RFOiBUaW1pbmcuQm91bmRzIGlzIHJlc3RyaWN0ZWQgdG8gYSBQZXJpb2QuCiovCgpkZWZpbmUgZnVuY3Rpb24gVG9JbnRlcnZhbEZyb21MaXN0KHZhbHVlIExpc3Q8RkhJUi5kYXRlVGltZT4pOgogIHZhbHVlIHYKICAgIHJldHVybiBUb0ludGVydmFsKHYpCgpkZWZpbmUgZnVuY3Rpb24gTnVsbFRvRW1wdHlJbnRlcnZhbCh2YWx1ZSBMaXN0PEludGVydmFsPERhdGVUaW1lPj4pOgogIGlmIHZhbHVlIGlzIG5vdCBudWxsIHRoZW4gdmFsdWUgZWxzZSB7IH0KCmRlZmluZSBmdW5jdGlvbiBUb1RpbWluZ0ludGVydmFsKHRpbWluZyBGSElSLlRpbWluZyk6CiAgY29udmVydCAoCiAgICBOdWxsVG9FbXB0eUludGVydmFsKFRvSW50ZXJ2YWxGcm9tTGlzdCh0aW1pbmcuZXZlbnQpKQogICAgdW5pb24gTnVsbFRvRW1wdHlJbnRlcnZhbCh7VG9JbnRlcnZhbCh0aW1pbmcucmVwZWF0LmJvdW5kcyBhcyBGSElSLlBlcmlvZCl9KQogICkgdG8gTGlzdDxJbnRlcnZhbDxEYXRlVGltZT4+IGV4Y2VwdCB7IG51bGwgfQoKCi8qCkBkZXNjcmlwdGlvbjogcmV0dXJucyB0aGUgZXhpc3RpbmcgcG9zc2libGUgdGltZXN0YW1wIGZvciBvYnNlcnZhdGlvbiByZXNvdXJjZXMsIHByZWZlcnJpbmcgZWZmZWN0aXZlIG92ZXIgaXNzdWVkLgpAY29tbWVudDogTk9URTogbmVpdGhlciBlZmZlY3RpdmUgbm9yIGlzc3VlZCBhcmUgcmVxdWlyZWQgZmllbGRzIGluIFFJLUNvcmUKTk9URTogRnVuY3Rpb24gb3ZlcmxvYWRzIGluY29uc2lzdGVudGx5IGVyciBpbiBDUUwgMS41LCB0aGlzIGlzIGNvbW1lbnRlZCB1bnRpbCBhIGZpeCBpcyBmb3VuZApkZWZpbmUgZnVuY3Rpb24gVG9JbnRlcnZhbChvYnNlcnZhdGlvbiBGSElSLk9ic2VydmF0aW9uKToKICBvYnNlcnZhdGlvbiBvCiAgICByZXR1cm4gQ29hbGVzY2UoVG9JbnRlcnZhbChvLmVmZmVjdGl2ZSksIFRvSW50ZXJ2YWwoby5pc3N1ZWQpKQoqLwoKLyoKQGRlc2NyaXB0aW9uOiBSZXR1cm5zIGFuIGludGVydmFsIHJlcHJlc2VudGluZyB0aGUgbm9ybWFsaXplZCBBYmF0ZW1lbnQgb2YgYSBnaXZlbiBDb25kaXRpb24gcmVzb3VyY2UuCkBjb21tZW50OiBOT1RFOiBEdWUgdG8gdGhlIGNvbXBsZXhpdHkgb2YgZGV0ZXJtaW5pbmcgYW4gaW50ZXJ2YWwgZnJvbSBhIFN0cmluZywgdGhpcyBmdW5jdGlvbiB3aWxsIHRocm93CmEgcnVuLXRpbWUgZXhjZXB0aW9uIGlmIHVzZWQgd2l0aCBhIENvbmRpdGlvbiBpbnN0YW5jZSB0aGF0IGhhcyBhIFN0cmluZyBhcyB0aGUgYWJhdGVtZW50IHZhbHVlLgoqLwpkZWZpbmUgZnVuY3Rpb24gVG9BYmF0ZW1lbnRJbnRlcnZhbChjb25kaXRpb24gQ29uZGl0aW9uKToKICBpZiBjb25kaXRpb24ub25zZXQgaXMgbm90IG51bGwgYW5kIGNvbmRpdGlvbi5hYmF0ZW1lbnQgaXMgbnVsbCBhbmQgKGNvbmRpdGlvbi5jbGluaWNhbFN0YXR1cyB+ICJhY3RpdmUiCiAgICBvciBjb25kaXRpb24uY2xpbmljYWxTdGF0dXMgfiAicmVjdXJyZW5jZSIKICAgIG9yIGNvbmRpdGlvbi5jbGluaWNhbFN0YXR1cyB+ICJyZWxhcHNlIikgdGhlbgogICAgSW50ZXJ2YWxbTm93KCksIE5vdygpXQogIGVsc2UgaWYgY29uZGl0aW9uLmFiYXRlbWVudCBpcyBGSElSLmRhdGVUaW1lIHRoZW4KCSAgSW50ZXJ2YWxbRkhJUkhlbHBlcnMuVG9EYXRlVGltZShjb25kaXRpb24uYWJhdGVtZW50IGFzIEZISVIuZGF0ZVRpbWUpLCBGSElSSGVscGVycy5Ub0RhdGVUaW1lKGNvbmRpdGlvbi5hYmF0ZW1lbnQgYXMgRkhJUi5kYXRlVGltZSldCgllbHNlIGlmIGNvbmRpdGlvbi5hYmF0ZW1lbnQgaXMgRkhJUi5QZXJpb2QgdGhlbgoJICBGSElSSGVscGVycy5Ub0ludGVydmFsKGNvbmRpdGlvbi5hYmF0ZW1lbnQgYXMgRkhJUi5QZXJpb2QpCgllbHNlIGlmIGNvbmRpdGlvbi5hYmF0ZW1lbnQgaXMgRkhJUi5zdHJpbmcgdGhlbgogICAgTWVzc2FnZShudWxsIGFzIEludGVydmFsPERhdGVUaW1lPiwgdHJ1ZSwgJzEnLCAnRXJyb3InLCAnQ2Fubm90IGNvbXB1dGUgYW4gaW50ZXJ2YWwgZnJvbSBhIFN0cmluZyB2YWx1ZScpCgllbHNlIGlmIGNvbmRpdGlvbi5hYmF0ZW1lbnQgaXMgRkhJUi5BZ2UgdGhlbgoJCUludGVydmFsW0ZISVJIZWxwZXJzLlRvRGF0ZShQYXRpZW50LmJpcnRoRGF0ZSkgKyBGSElSSGVscGVycy5Ub1F1YW50aXR5KGNvbmRpdGlvbi5hYmF0ZW1lbnQgYXMgRkhJUi5BZ2UpLAoJCQlGSElSSGVscGVycy5Ub0RhdGUoUGF0aWVudC5iaXJ0aERhdGUpICsgRkhJUkhlbHBlcnMuVG9RdWFudGl0eShjb25kaXRpb24uYWJhdGVtZW50IGFzIEZISVIuQWdlKSArIDEgeWVhcikKCWVsc2UgaWYgY29uZGl0aW9uLmFiYXRlbWVudCBpcyBGSElSLlJhbmdlIHRoZW4KCSAgSW50ZXJ2YWxbRkhJUkhlbHBlcnMuVG9EYXRlKFBhdGllbnQuYmlydGhEYXRlKSArIEZISVJIZWxwZXJzLlRvUXVhbnRpdHkoKGNvbmRpdGlvbi5hYmF0ZW1lbnQgYXMgRkhJUi5SYW5nZSkubG93KSwKCQkgIEZISVJIZWxwZXJzLlRvRGF0ZShQYXRpZW50LmJpcnRoRGF0ZSkgKyBGSElSSGVscGVycy5Ub1F1YW50aXR5KChjb25kaXRpb24uYWJhdGVtZW50IGFzIEZISVIuUmFuZ2UpLmhpZ2gpICsgMSB5ZWFyKQoJZWxzZSBpZiBjb25kaXRpb24uYWJhdGVtZW50IGlzIEZISVIuYm9vbGVhbiB0aGVuCgkgIEludGVydmFsW2VuZCBvZiBUb0ludGVydmFsKGNvbmRpdGlvbi5vbnNldCksIGNvbmRpdGlvbi5yZWNvcmRlZERhdGUpCgllbHNlIG51bGwKCi8qCkBkZXNjcmlwdGlvbjogUmV0dXJucyBhbiBpbnRlcnZhbCByZXByZXNlbnRpbmcgdGhlIG5vcm1hbGl6ZWQgcHJldmFsZW5jZSBwZXJpb2Qgb2YgYSBnaXZlbiBDb25kaXRpb24gcmVzb3VyY2UuCkBjb21tZW50OiBVc2VzIHRoZSBUb0ludGVydmFsIGFuZCBUb0FiYXRlbWVudEludGVydmFsIGZ1bmN0aW9ucyB0byBkZXRlcm1pbmUgdGhlIHdpZGVzdCBwb3RlbnRpYWwgaW50ZXJ2YWwgZnJvbQpvbnNldCB0byBhYmF0ZW1lbnQgYXMgc3BlY2lmaWVkIGluIHRoZSBnaXZlbiBDb25kaXRpb24uCiovCmRlZmluZSBmdW5jdGlvbiBUb1ByZXZhbGVuY2VJbnRlcnZhbChjb25kaXRpb24gQ29uZGl0aW9uKToKICBJbnRlcnZhbFtzdGFydCBvZiBUb0ludGVydmFsKGNvbmRpdGlvbi5vbnNldCksIGVuZCBvZiBUb0FiYXRlbWVudEludGVydmFsKGNvbmRpdGlvbikpCgovKgpAZGVzY3JpcHRpb246IFJldHVybnMgYW4gaW50ZXJ2YWwgcmVwcmVzZW50aW5nIHRoZSBub3JtYWxpemVkIHByZXZhbGVuY2UgcGVyaW9kIG9mIGEgZ2l2ZW4gQWxsZXJneSBJbnRvbGVyYW5jZSByZXNvdXJjZS4KQGNvbW1lbnQ6IFVzZXMgdGhlIFRvSW50ZXJ2YWwgZnVuY3Rpb24gYW5kIGxhc3RPY2N1cmVuY2UgdG8gZGV0ZXJtaW5lIHRoZSB3aWRlc3QgcG90ZW50aWFsIGludGVydmFsLgoqLwpkZWZpbmUgZnVuY3Rpb24gVG9QcmV2YWxlbmNlSW50ZXJ2YWwoYWxsZXJneUludG9sZXJhbmNlIEFsbGVyZ3lJbnRvbGVyYW5jZSk6CiAgSW50ZXJ2YWxbc3RhcnQgb2YgVG9JbnRlcnZhbChhbGxlcmd5SW50b2xlcmFuY2Uub25zZXQpLCBDb2FsZXNjZShhbGxlcmd5SW50b2xlcmFuY2UubGFzdE9jY3VycmVuY2UsIE5vdygpKV0KCgovKgpAZGVzY3JpcHRpb246IFJldHVybnMgYW55IGV4dGVuc2lvbnMgZGVmaW5lZCBvbiB0aGUgZ2l2ZW4gcmVzb3VyY2Ugd2l0aCB0aGUgc3BlY2lmaWVkIHVybC4KQGNvbW1lbnQ6IE5PVEU6IEV4dGVuc2lvbnMgYXJlIG5vdCB0aGUgcHJlZmVycmVkIGFwcHJvYWNoLCBidXQgYXJlIHVzZWQgYXMgYSB3YXkgdG8gYWNjZXNzCmNvbnRlbnQgdGhhdCBpcyBkZWZpbmVkIGJ5IGV4dGVuc2lvbnMgYnV0IG5vdCB5ZXQgc3VyZmFjZWQgaW4gdGhlCkNRTCBtb2RlbCBpbmZvLgoqLwpkZWZpbmUgZnVuY3Rpb24gRXh0ZW5zaW9ucyhkb21haW5SZXNvdXJjZSBEb21haW5SZXNvdXJjZSwgdXJsIFN0cmluZyk6CiAgZG9tYWluUmVzb3VyY2UuZXh0ZW5zaW9uIEUKCSAgd2hlcmUgRS51cmwgPSB1cmwKCQlyZXR1cm4gRQoKLyoKQGRlc2NyaXB0aW9uOiBSZXR1cm5zIHRoZSBzaW5nbGUgZXh0ZW5zaW9uIChpZiBwcmVzZW50KSBvbiB0aGUgZ2l2ZW4gcmVzb3VyY2Ugd2l0aCB0aGUgc3BlY2lmaWVkIHVybC4KQGNvbW1lbnQ6IFRoaXMgZnVuY3Rpb24gdXNlcyBzaW5nbGV0b24gZnJvbSB0byBlbnN1cmUgdGhhdCBhIHJ1bi10aW1lIGV4Y2VwdGlvbiBpcyB0aHJvd24gaWYgdGhlcmUKaXMgbW9yZSB0aGFuIG9uZSBleHRlbnNpb24gb24gdGhlIGdpdmVuIHJlc291cmNlIHdpdGggdGhlIHNwZWNpZmllZCB1cmwuCiovCmRlZmluZSBmdW5jdGlvbiBFeHRlbnNpb24oZG9tYWluUmVzb3VyY2UgRG9tYWluUmVzb3VyY2UsIHVybCBTdHJpbmcpOgogIHNpbmdsZXRvbiBmcm9tICJFeHRlbnNpb25zIihkb21haW5SZXNvdXJjZSwgdXJsKQoKLyoKQGRlc2NyaXB0aW9uOiBSZXR1cm5zIGFueSBleHRlbnNpb25zIGRlZmluZWQgb24gdGhlIGdpdmVuIGVsZW1lbnQgd2l0aCB0aGUgc3BlY2lmaWVkIHVybC4KQGNvbW1lbnQ6IE5PVEU6IEV4dGVuc2lvbnMgYXJlIG5vdCB0aGUgcHJlZmVycmVkIGFwcHJvYWNoLCBidXQgYXJlIHVzZWQgYXMgYSB3YXkgdG8gYWNjZXNzCmNvbnRlbnQgdGhhdCBpcyBkZWZpbmVkIGJ5IGV4dGVuc2lvbnMgYnV0IG5vdCB5ZXQgc3VyZmFjZWQgaW4gdGhlIENRTCBtb2RlbCBpbmZvLgoqLwpkZWZpbmUgZnVuY3Rpb24gRXh0ZW5zaW9ucyhlbGVtZW50IEVsZW1lbnQsIHVybCBTdHJpbmcpOgogIGVsZW1lbnQuZXh0ZW5zaW9uIEUKCSAgd2hlcmUgRS51cmwgPSB1cmwKCQlyZXR1cm4gRQoKLyoKQGRlc2NyaXB0aW9uOiBSZXR1cm5zIHRoZSBzaW5nbGUgZXh0ZW5zaW9uIChpZiBwcmVzZW50KSBvbiB0aGUgZ2l2ZW4gZWxlbWVudCB3aXRoIHRoZSBzcGVjaWZpZWQgdXJsLgpAY29tbWVudDogVGhpcyBmdW5jdGlvbiB1c2VzIHNpbmdsZXRvbiBmcm9tIHRvIGVuc3VyZSB0aGF0IGEgcnVuLXRpbWUgZXhjZXB0aW9uIGlzIHRocm93biBpZiB0aGVyZQppcyBtb3JlIHRoYW4gb25lIGV4dGVuc2lvbiBvbiB0aGUgZ2l2ZW4gcmVzb3VyY2Ugd2l0aCB0aGUgc3BlY2lmaWVkIHVybC4KKi8KZGVmaW5lIGZ1bmN0aW9uIEV4dGVuc2lvbihlbGVtZW50IEVsZW1lbnQsIHVybCBTdHJpbmcpOgogIHNpbmdsZXRvbiBmcm9tIEV4dGVuc2lvbnMoZWxlbWVudCwgdXJsKQoKLyoKQGRlc2NyaXB0aW9uOiBSZXR1cm5zIGFueSBtb2RpZmllciBleHRlbnNpb25zIGRlZmluZWQgb24gdGhlIGdpdmVuIHJlc291cmNlIHdpdGggdGhlIHNwZWNpZmllZCB1cmwuCkBjb21tZW50OiBOT1RFOiBFeHRlbnNpb25zIGFyZSBub3QgdGhlIHByZWZlcnJlZCBhcHByb2FjaCwgYnV0IGFyZSB1c2VkIGFzIGEgd2F5IHRvIGFjY2Vzcwpjb250ZW50IHRoYXQgaXMgZGVmaW5lZCBieSBleHRlbnNpb25zIGJ1dCBub3QgeWV0IHN1cmZhY2VkIGluIHRoZQpDUUwgbW9kZWwgaW5mby4KKi8KZGVmaW5lIGZ1bmN0aW9uIE1vZGlmaWVyRXh0ZW5zaW9ucyhkb21haW5SZXNvdXJjZSBEb21haW5SZXNvdXJjZSwgdXJsIFN0cmluZyk6CiAgZG9tYWluUmVzb3VyY2UubW9kaWZpZXJFeHRlbnNpb24gRQoJICB3aGVyZSBFLnVybCA9IHVybAoJCXJldHVybiBFCgovKgpAZGVzY3JpcHRpb246IFJldHVybnMgdGhlIHNpbmdsZSBtb2RpZmllciBleHRlbnNpb24gKGlmIHByZXNlbnQpIG9uIHRoZSBnaXZlbiByZXNvdXJjZSB3aXRoIHRoZSBzcGVjaWZpZWQgdXJsLgpAY29tbWVudDogVGhpcyBmdW5jdGlvbiB1c2VzIHNpbmdsZXRvbiBmcm9tIHRvIGVuc3VyZSB0aGF0IGEgcnVuLXRpbWUgZXhjZXB0aW9uIGlzIHRocm93biBpZiB0aGVyZQppcyBtb3JlIHRoYW4gb25lIGV4dGVuc2lvbiBvbiB0aGUgZ2l2ZW4gcmVzb3VyY2Ugd2l0aCB0aGUgc3BlY2lmaWVkIHVybC4KKi8KZGVmaW5lIGZ1bmN0aW9uIE1vZGlmaWVyRXh0ZW5zaW9uKGRvbWFpblJlc291cmNlIERvbWFpblJlc291cmNlLCB1cmwgU3RyaW5nKToKICBzaW5nbGV0b24gZnJvbSBNb2RpZmllckV4dGVuc2lvbnMoZG9tYWluUmVzb3VyY2UsIHVybCkKCi8qCkBkZXNjcmlwdGlvbjogUmV0dXJucyBhbnkgbW9kaWZpZXIgZXh0ZW5zaW9ucyBkZWZpbmVkIG9uIHRoZSBnaXZlbiBlbGVtZW50IHdpdGggdGhlIHNwZWNpZmllZCB1cmwuCkBjb21tZW50OiBOT1RFOiBFeHRlbnNpb25zIGFyZSBub3QgdGhlIHByZWZlcnJlZCBhcHByb2FjaCwgYnV0IGFyZSB1c2VkIGFzIGEgd2F5IHRvIGFjY2Vzcwpjb250ZW50IHRoYXQgaXMgZGVmaW5lZCBieSBleHRlbnNpb25zIGJ1dCBub3QgeWV0IHN1cmZhY2VkIGluIHRoZSBDUUwgbW9kZWwgaW5mby4KKi8KZGVmaW5lIGZ1bmN0aW9uIE1vZGlmaWVyRXh0ZW5zaW9ucyhlbGVtZW50IEJhY2tib25lRWxlbWVudCwgdXJsIFN0cmluZyk6CiAgZWxlbWVudC5tb2RpZmllckV4dGVuc2lvbiBFCgkgIHdoZXJlIEUudXJsID0gdXJsCgkJcmV0dXJuIEUKCi8qCkBkZXNjcmlwdGlvbjogUmV0dXJucyB0aGUgc2luZ2xlIG1vZGlmaWVyIGV4dGVuc2lvbiAoaWYgcHJlc2VudCkgb24gdGhlIGdpdmVuIGVsZW1lbnQgd2l0aCB0aGUgc3BlY2lmaWVkIHVybC4KQGNvbW1lbnQ6IFRoaXMgZnVuY3Rpb24gdXNlcyBzaW5nbGV0b24gZnJvbSB0byBlbnN1cmUgdGhhdCBhIHJ1bi10aW1lIGV4Y2VwdGlvbiBpcyB0aHJvd24gaWYgdGhlcmUKaXMgbW9yZSB0aGFuIG9uZSBleHRlbnNpb24gb24gdGhlIGdpdmVuIHJlc291cmNlIHdpdGggdGhlIHNwZWNpZmllZCB1cmwuCiovCmRlZmluZSBmdW5jdGlvbiBNb2RpZmllckV4dGVuc2lvbihlbGVtZW50IEJhY2tib25lRWxlbWVudCwgdXJsIFN0cmluZyk6CiAgc2luZ2xldG9uIGZyb20gTW9kaWZpZXJFeHRlbnNpb25zKGVsZW1lbnQsIHVybCkKCi8qCkBkZXNjcmlwdGlvbjogUmV0dXJucyBhbnkgYmFzZS1GSElSIGV4dGVuc2lvbnMgZGVmaW5lZCBvbiB0aGUgZ2l2ZW4gcmVzb3VyY2Ugd2l0aCB0aGUgc3BlY2lmaWVkIGlkLgpAY29tbWVudDogTk9URTogRXh0ZW5zaW9ucyBhcmUgbm90IHRoZSBwcmVmZXJyZWQgYXBwcm9hY2gsIGJ1dCBhcmUgdXNlZCBhcyBhIHdheSB0byBhY2Nlc3MKY29udGVudCB0aGF0IGlzIGRlZmluZWQgYnkgZXh0ZW5zaW9ucyBidXQgbm90IHlldCBzdXJmYWNlZCBpbiB0aGUgQ1FMIG1vZGVsIGluZm8uCiovCmRlZmluZSBmdW5jdGlvbiBCYXNlRXh0ZW5zaW9ucyhkb21haW5SZXNvdXJjZSBEb21haW5SZXNvdXJjZSwgaWQgU3RyaW5nKToKICBkb21haW5SZXNvdXJjZS5leHRlbnNpb24gRQoJICB3aGVyZSBFLnVybCA9ICgnaHR0cDovL2hsNy5vcmcvZmhpci9TdHJ1Y3R1cmVEZWZpbml0aW9uLycgKyBpZCkKCQlyZXR1cm4gRQoKLyoKQGRlc2NyaXB0aW9uOiBSZXR1cm5zIHRoZSBzaW5nbGUgYmFzZS1GSElSIGV4dGVuc2lvbiAoaWYgcHJlc2VudCkgb24gdGhlIGdpdmVuIHJlc291cmNlIHdpdGggdGhlIHNwZWNpZmllZCBpZC4KQGNvbW1lbnQ6IFRoaXMgZnVuY3Rpb24gdXNlcyBzaW5nbGV0b24gZnJvbSB0byBlbnN1cmUgdGhhdCBhIHJ1bi10aW1lIGV4Y2VwdGlvbiBpcyB0aHJvd24gaWYgdGhlcmUKaXMgbW9yZSB0aGFuIG9uZSBleHRlbnNpb24gb24gdGhlIGdpdmVuIHJlc291cmNlIHdpdGggdGhlIHNwZWNpZmllZCB1cmwuCiovCmRlZmluZSBmdW5jdGlvbiBCYXNlRXh0ZW5zaW9uKGRvbWFpblJlc291cmNlIERvbWFpblJlc291cmNlLCBpZCBTdHJpbmcpOgogIHNpbmdsZXRvbiBmcm9tIEJhc2VFeHRlbnNpb25zKGRvbWFpblJlc291cmNlLCBpZCkKCi8qCkBkZXNjcmlwdGlvbjogUmV0dXJucyBhbnkgYmFzZS1GSElSIGV4dGVuc2lvbnMgZGVmaW5lZCBvbiB0aGUgZ2l2ZW4gZWxlbWVudCB3aXRoIHRoZSBzcGVjaWZpZWQgaWQuCkBjb21tZW50OiBOT1RFOiBFeHRlbnNpb25zIGFyZSBub3QgdGhlIHByZWZlcnJlZCBhcHByb2FjaCwgYnV0IGFyZSB1c2VkIGFzIGEgd2F5IHRvIGFjY2Vzcwpjb250ZW50IHRoYXQgaXMgZGVmaW5lZCBieSBleHRlbnNpb25zIGJ1dCBub3QgeWV0IHN1cmZhY2VkIGluIHRoZSBDUUwgbW9kZWwgaW5mby4KKi8KZGVmaW5lIGZ1bmN0aW9uIEJhc2VFeHRlbnNpb25zKGVsZW1lbnQgRWxlbWVudCwgaWQgU3RyaW5nKToKICBlbGVtZW50LmV4dGVuc2lvbiBFCgkgIHdoZXJlIEUudXJsID0gKCdodHRwOi8vaGw3Lm9yZy9maGlyL1N0cnVjdHVyZURlZmluaXRpb24vJyArIGlkKQoJCXJldHVybiBFCgovKgpAZGVzY3JpcHRpb246IFJldHVybnMgdGhlIHNpbmdsZSBiYXNlLUZISVIgZXh0ZW5zaW9uIChpZiBwcmVzZW50KSBvbiB0aGUgZ2l2ZW4gZWxlbWVudCB3aXRoIHRoZSBzcGVjaWZpZWQgaWQuCkBjb21tZW50OiBUaGlzIGZ1bmN0aW9uIHVzZXMgc2luZ2xldG9uIGZyb20gdG8gZW5zdXJlIHRoYXQgYSBydW4tdGltZSBleGNlcHRpb24gaXMgdGhyb3duIGlmIHRoZXJlCmlzIG1vcmUgdGhhbiBvbmUgZXh0ZW5zaW9uIG9uIHRoZSBnaXZlbiByZXNvdXJjZSB3aXRoIHRoZSBzcGVjaWZpZWQgdXJsLgoqLwpkZWZpbmUgZnVuY3Rpb24gQmFzZUV4dGVuc2lvbihlbGVtZW50IEVsZW1lbnQsIGlkIFN0cmluZyk6CiAgc2luZ2xldG9uIGZyb20gQmFzZUV4dGVuc2lvbnMoZWxlbWVudCwgaWQpCgovKgpAZGVzY3JpcHRpb246IFJldHVybnMgYW55IGJhc2UtRkhJUiBtb2RpZmllciBleHRlbnNpb25zIGRlZmluZWQgb24gdGhlIGdpdmVuIHJlc291cmNlIHdpdGggdGhlIHNwZWNpZmllZCBpZC4KQGNvbW1lbnQ6IE5PVEU6IEV4dGVuc2lvbnMgYXJlIG5vdCB0aGUgcHJlZmVycmVkIGFwcHJvYWNoLCBidXQgYXJlIHVzZWQgYXMgYSB3YXkgdG8gYWNjZXNzCmNvbnRlbnQgdGhhdCBpcyBkZWZpbmVkIGJ5IGV4dGVuc2lvbnMgYnV0IG5vdCB5ZXQgc3VyZmFjZWQgaW4gdGhlIENRTCBtb2RlbCBpbmZvLgoqLwpkZWZpbmUgZnVuY3Rpb24gQmFzZU1vZGlmaWVyRXh0ZW5zaW9ucyhkb21haW5SZXNvdXJjZSBEb21haW5SZXNvdXJjZSwgaWQgU3RyaW5nKToKICBkb21haW5SZXNvdXJjZS5tb2RpZmllckV4dGVuc2lvbiBFCgkgIHdoZXJlIEUudXJsID0gKCdodHRwOi8vaGw3Lm9yZy9maGlyL1N0cnVjdHVyZURlZmluaXRpb24vJyArIGlkKQoJCXJldHVybiBFCgovKgpAZGVzY3JpcHRpb246IFJldHVybnMgdGhlIHNpbmdsZSBiYXNlLUZISVIgbW9kaWZpZXIgZXh0ZW5zaW9uIChpZiBwcmVzZW50KSBvbiB0aGUgZ2l2ZW4gcmVzb3VyY2Ugd2l0aCB0aGUgc3BlY2lmaWVkIGlkLgpAY29tbWVudDogVGhpcyBmdW5jdGlvbiB1c2VzIHNpbmdsZXRvbiBmcm9tIHRvIGVuc3VyZSB0aGF0IGEgcnVuLXRpbWUgZXhjZXB0aW9uIGlzIHRocm93biBpZiB0aGVyZQppcyBtb3JlIHRoYW4gb25lIGV4dGVuc2lvbiBvbiB0aGUgZ2l2ZW4gcmVzb3VyY2Ugd2l0aCB0aGUgc3BlY2lmaWVkIHVybC4KKi8KZGVmaW5lIGZ1bmN0aW9uIEJhc2VNb2RpZmllckV4dGVuc2lvbihkb21haW5SZXNvdXJjZSBEb21haW5SZXNvdXJjZSwgaWQgU3RyaW5nKToKICBzaW5nbGV0b24gZnJvbSBCYXNlTW9kaWZpZXJFeHRlbnNpb25zKGRvbWFpblJlc291cmNlLCBpZCkKCi8qCkBkZXNjcmlwdGlvbjogUmV0dXJucyBhbnkgYmFzZS1GSElSIG1vZGlmaWVyIGV4dGVuc2lvbnMgZGVmaW5lZCBvbiB0aGUgZ2l2ZW4gZWxlbWVudCB3aXRoIHRoZSBzcGVjaWZpZWQgaWQuCkBjb21tZW50OiBOT1RFOiBFeHRlbnNpb25zIGFyZSBub3QgdGhlIHByZWZlcnJlZCBhcHByb2FjaCwgYnV0IGFyZSB1c2VkIGFzIGEgd2F5IHRvIGFjY2Vzcwpjb250ZW50IHRoYXQgaXMgZGVmaW5lZCBieSBleHRlbnNpb25zIGJ1dCBub3QgeWV0IHN1cmZhY2VkIGluIHRoZSBDUUwgbW9kZWwgaW5mby4KKi8KZGVmaW5lIGZ1bmN0aW9uIEJhc2VNb2RpZmllckV4dGVuc2lvbnMoZWxlbWVudCBCYWNrYm9uZUVsZW1lbnQsIGlkIFN0cmluZyk6CiAgZWxlbWVudC5tb2RpZmllckV4dGVuc2lvbiBFCgkgIHdoZXJlIEUudXJsID0gKCdodHRwOi8vaGw3Lm9yZy9maGlyL1N0cnVjdHVyZURlZmluaXRpb24vJyArIGlkKQoJCXJldHVybiBFCgovKgpAZGVzY3JpcHRpb246IFJldHVybnMgdGhlIHNpbmdsZSBiYXNlLUZISVIgZXh0ZW5zaW9uIChpZiBwcmVzZW50KSBvbiB0aGUgZ2l2ZW4gZWxlbWVudCB3aXRoIHRoZSBzcGVjaWZpZWQgaWQuCkBjb21tZW50OiBUaGlzIGZ1bmN0aW9uIHVzZXMgc2luZ2xldG9uIGZyb20gdG8gZW5zdXJlIHRoYXQgYSBydW4tdGltZSBleGNlcHRpb24gaXMgdGhyb3duIGlmIHRoZXJlCmlzIG1vcmUgdGhhbiBvbmUgZXh0ZW5zaW9uIG9uIHRoZSBnaXZlbiByZXNvdXJjZSB3aXRoIHRoZSBzcGVjaWZpZWQgdXJsLgoqLwpkZWZpbmUgZnVuY3Rpb24gQmFzZU1vZGlmaWVyRXh0ZW5zaW9uKGVsZW1lbnQgQmFja2JvbmVFbGVtZW50LCBpZCBTdHJpbmcpOgogIHNpbmdsZXRvbiBmcm9tIEJhc2VNb2RpZmllckV4dGVuc2lvbnMoZWxlbWVudCwgaWQpCg==" - }, - { - "contentType": "application/elm+xml", - "data": "PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0iVVRGLTgiPz4KPGxpYnJhcnkgeG1sbnM9InVybjpobDctb3JnOmVsbTpyMSIgeG1sbnM6dD0idXJuOmhsNy1vcmc6ZWxtLXR5cGVzOnIxIiB4bWxuczp4c2k9Imh0dHA6Ly93d3cudzMub3JnLzIwMDEvWE1MU2NoZW1hLWluc3RhbmNlIiB4bWxuczp4c2Q9Imh0dHA6Ly93d3cudzMub3JnLzIwMDEvWE1MU2NoZW1hIiB4bWxuczpmaGlyPSJodHRwOi8vaGw3Lm9yZy9maGlyIiB4bWxuczpxZG00Mz0idXJuOmhlYWx0aGl0LWdvdjpxZG06djRfMyIgeG1sbnM6cWRtNTM9InVybjpoZWFsdGhpdC1nb3Y6cWRtOnY1XzMiIHhtbG5zOmE9InVybjpobDctb3JnOmNxbC1hbm5vdGF0aW9uczpyMSI+CiAgIDxhbm5vdGF0aW9uIHRyYW5zbGF0b3JWZXJzaW9uPSIyLjcuMCIgdHJhbnNsYXRvck9wdGlvbnM9IkVuYWJsZUxvY2F0b3JzLERpc2FibGVMaXN0RGVtb3Rpb24sRGlzYWJsZUxpc3RQcm9tb3Rpb24iIHhzaTp0eXBlPSJhOkNxbFRvRWxtSW5mbyIvPgogICA8YW5ub3RhdGlvbiB4c2k6dHlwZT0iYTpBbm5vdGF0aW9uIj4KICAgICAgPGE6dCBuYW1lPSJhdXRob3IiIHZhbHVlPSJCcnluIFJob2RlcyIvPgogICAgICA8YTp0IG5hbWU9ImRlc2NyaXB0aW9uIiB2YWx1ZT0iQ29tbW9uIHRlcm1pbm9sb2dpZXMgYW5kIGZ1bmN0aW9ucyB1c2VkIGluIEZISVItYmFzZWQgQ1FMIGFydGlmYWN0cyIvPgogICA8L2Fubm90YXRpb24+CiAgIDxpZGVudGlmaWVyIGlkPSJGSElSQ29tbW9uIiBzeXN0ZW09Imh0dHA6Ly9jb250ZW50LmFscGhvcmEuY29tL2ZoaXIvZHFtIiB2ZXJzaW9uPSI0LjAuMSIvPgogICA8c2NoZW1hSWRlbnRpZmllciBpZD0idXJuOmhsNy1vcmc6ZWxtIiB2ZXJzaW9uPSJyMSIvPgogICA8dXNpbmdzPgogICAgICA8ZGVmIGxvY2FsSWRlbnRpZmllcj0iU3lzdGVtIiB1cmk9InVybjpobDctb3JnOmVsbS10eXBlczpyMSIvPgogICAgICA8ZGVmIGxvY2F0b3I9Ijg6MS04OjI2IiBsb2NhbElkZW50aWZpZXI9IkZISVIiIHVyaT0iaHR0cDovL2hsNy5vcmcvZmhpciIgdmVyc2lvbj0iNC4wLjEiLz4KICAgPC91c2luZ3M+CiAgIDxpbmNsdWRlcz4KICAgICAgPGRlZiBsb2NhdG9yPSIxMDoxLTEwOjQxIiBsb2NhbElkZW50aWZpZXI9IkZISVJIZWxwZXJzIiBwYXRoPSJodHRwOi8vY29udGVudC5hbHBob3JhLmNvbS9maGlyL2RxbS9EUU1GSElSSGVscGVycyIvPgogICA8L2luY2x1ZGVzPgogICA8Y29kZVN5c3RlbXM+CiAgICAgIDxkZWYgbG9jYXRvcj0iMTI6MS0xMjozOCIgbmFtZT0iTE9JTkMiIGlkPSJodHRwOi8vbG9pbmMub3JnIiBhY2Nlc3NMZXZlbD0iUHVibGljIi8+CiAgICAgIDxkZWYgbG9jYXRvcj0iMTM6MS0xMzo0NyIgbmFtZT0iU05PTUVEQ1QiIGlkPSJodHRwOi8vc25vbWVkLmluZm8vc2N0IiBhY2Nlc3NMZXZlbD0iUHVibGljIi8+CiAgICAgIDxkZWYgbG9jYXRvcj0iMTQ6MS0xNDo3NCIgbmFtZT0iUm9sZUNvZGUiIGlkPSJodHRwOi8vdGVybWlub2xvZ3kuaGw3Lm9yZy9Db2RlU3lzdGVtL3YzLVJvbGVDb2RlIiBhY2Nlc3NMZXZlbD0iUHVibGljIi8+CiAgICAgIDxkZWYgbG9jYXRvcj0iMTU6MS0xNTo4MyIgbmFtZT0iRGlhZ25vc2lzIFJvbGUiIGlkPSJodHRwOi8vdGVybWlub2xvZ3kuaGw3Lm9yZy9Db2RlU3lzdGVtL2RpYWdub3Npcy1yb2xlIiBhY2Nlc3NMZXZlbD0iUHVibGljIi8+CiAgICAgIDxkZWYgbG9jYXRvcj0iMTY6MS0xNjo4MiIgbmFtZT0iUmVxdWVzdEludGVudCIgaWQ9Imh0dHA6Ly90ZXJtaW5vbG9neS5obDcub3JnL0NvZGVTeXN0ZW0vcmVxdWVzdC1pbnRlbnQiIGFjY2Vzc0xldmVsPSJQdWJsaWMiLz4KICAgICAgPGRlZiBsb2NhdG9yPSIxNzoxLTE3OjEwNiIgbmFtZT0iTWVkaWNhdGlvblJlcXVlc3RDYXRlZ29yeSIgaWQ9Imh0dHA6Ly90ZXJtaW5vbG9neS5obDcub3JnL0NvZGVTeXN0ZW0vbWVkaWNhdGlvbnJlcXVlc3QtY2F0ZWdvcnkiIGFjY2Vzc0xldmVsPSJQdWJsaWMiLz4KICAgICAgPGRlZiBsb2NhdG9yPSIxODoxLTE4OjEwMSIgbmFtZT0iQ29uZGl0aW9uQ2xpbmljYWxTdGF0dXNDb2RlcyIgaWQ9Imh0dHA6Ly90ZXJtaW5vbG9neS5obDcub3JnL0NvZGVTeXN0ZW0vY29uZGl0aW9uLWNsaW5pY2FsIiBhY2Nlc3NMZXZlbD0iUHVibGljIi8+CiAgICAgIDxkZWYgbG9jYXRvcj0iMTk6MS0xOToxMDciIG5hbWU9IkNvbmRpdGlvblZlcmlmaWNhdGlvblN0YXR1c0NvZGVzIiBpZD0iaHR0cDovL3Rlcm1pbm9sb2d5LmhsNy5vcmcvQ29kZVN5c3RlbS9jb25kaXRpb24tdmVyLXN0YXR1cyIgYWNjZXNzTGV2ZWw9IlB1YmxpYyIvPgogICAgICA8ZGVmIGxvY2F0b3I9IjIwOjEtMjA6MTE5IiBuYW1lPSJBbGxlcmd5SW50b2xlcmFuY2VDbGluaWNhbFN0YXR1c0NvZGVzIiBpZD0iaHR0cDovL3Rlcm1pbm9sb2d5LmhsNy5vcmcvQ29kZVN5c3RlbS9hbGxlcmd5aW50b2xlcmFuY2UtY2xpbmljYWwiIGFjY2Vzc0xldmVsPSJQdWJsaWMiLz4KICAgICAgPGRlZiBsb2NhdG9yPSIyMToxLTIxOjEyNyIgbmFtZT0iQWxsZXJneUludG9sZXJhbmNlVmVyaWZpY2F0aW9uU3RhdHVzQ29kZXMiIGlkPSJodHRwOi8vdGVybWlub2xvZ3kuaGw3Lm9yZy9Db2RlU3lzdGVtL2FsbGVyZ3lpbnRvbGVyYW5jZS12ZXJpZmljYXRpb24iIGFjY2Vzc0xldmVsPSJQdWJsaWMiLz4KICAgPC9jb2RlU3lzdGVtcz4KICAgPGNvZGVzPgogICAgICA8ZGVmIGxvY2F0b3I9IjIzOjEtMjM6NjEiIG5hbWU9IkJpcnRoZGF0ZSIgaWQ9IjIxMTEyLTgiIGRpc3BsYXk9IkJpcnRoIGRhdGUiIGFjY2Vzc0xldmVsPSJQdWJsaWMiPgogICAgICAgICA8Y29kZVN5c3RlbSBsb2NhdG9yPSIyMzozNC0yMzo0MCIgbmFtZT0iTE9JTkMiLz4KICAgICAgPC9kZWY+CiAgICAgIDxkZWYgbG9jYXRvcj0iMjQ6MS0yNDo1NSIgbmFtZT0iRGVhZCIgaWQ9IjQxOTA5OTAwOSIgZGlzcGxheT0iRGVhZCIgYWNjZXNzTGV2ZWw9IlB1YmxpYyI+CiAgICAgICAgIDxjb2RlU3lzdGVtIGxvY2F0b3I9IjI0OjMxLTI0OjQwIiBuYW1lPSJTTk9NRURDVCIvPgogICAgICA8L2RlZj4KICAgICAgPGRlZiBsb2NhdG9yPSIyNToxLTI1OjU2IiBuYW1lPSJFUiIgaWQ9IkVSIiBkaXNwbGF5PSJFbWVyZ2VuY3kgcm9vbSIgYWNjZXNzTGV2ZWw9IlB1YmxpYyI+CiAgICAgICAgIDxjb2RlU3lzdGVtIGxvY2F0b3I9IjI1OjIyLTI1OjMxIiBuYW1lPSJSb2xlQ29kZSIvPgogICAgICA8L2RlZj4KICAgICAgPGRlZiBsb2NhdG9yPSIyNjoxLTI2OjYzIiBuYW1lPSJJQ1UiIGlkPSJJQ1UiIGRpc3BsYXk9IkludGVuc2l2ZSBjYXJlIHVuaXQiIGFjY2Vzc0xldmVsPSJQdWJsaWMiPgogICAgICAgICA8Y29kZVN5c3RlbSBsb2NhdG9yPSIyNjoyNC0yNjozMyIgbmFtZT0iUm9sZUNvZGUiLz4KICAgICAgPC9kZWY+CiAgICAgIDxkZWYgbG9jYXRvcj0iMjc6MS0yNzo2NSIgbmFtZT0iQmlsbGluZyIgaWQ9ImJpbGxpbmciIGRpc3BsYXk9IkJpbGxpbmciIGFjY2Vzc0xldmVsPSJQdWJsaWMiPgogICAgICAgICA8Y29kZVN5c3RlbSBsb2NhdG9yPSIyNzozMi0yNzo0NyIgbmFtZT0iRGlhZ25vc2lzIFJvbGUiLz4KICAgICAgPC9kZWY+CiAgICAgIDxkZWYgbG9jYXRvcj0iMzA6MS0zMDo1OSIgbmFtZT0iYWN0aXZlIiBpZD0iYWN0aXZlIiBhY2Nlc3NMZXZlbD0iUHVibGljIj4KICAgICAgICAgPGNvZGVTeXN0ZW0gbG9jYXRvcj0iMzA6MzAtMzA6NTkiIG5hbWU9IkNvbmRpdGlvbkNsaW5pY2FsU3RhdHVzQ29kZXMiLz4KICAgICAgPC9kZWY+CiAgICAgIDxkZWYgbG9jYXRvcj0iMzE6MS0zMTo2NyIgbmFtZT0icmVjdXJyZW5jZSIgaWQ9InJlY3VycmVuY2UiIGFjY2Vzc0xldmVsPSJQdWJsaWMiPgogICAgICAgICA8Y29kZVN5c3RlbSBsb2NhdG9yPSIzMTozOC0zMTo2NyIgbmFtZT0iQ29uZGl0aW9uQ2xpbmljYWxTdGF0dXNDb2RlcyIvPgogICAgICA8L2RlZj4KICAgICAgPGRlZiBsb2NhdG9yPSIzMjoxLTMyOjYxIiBuYW1lPSJyZWxhcHNlIiBpZD0icmVsYXBzZSIgYWNjZXNzTGV2ZWw9IlB1YmxpYyI+CiAgICAgICAgIDxjb2RlU3lzdGVtIGxvY2F0b3I9IjMyOjMyLTMyOjYxIiBuYW1lPSJDb25kaXRpb25DbGluaWNhbFN0YXR1c0NvZGVzIi8+CiAgICAgIDwvZGVmPgogICAgICA8ZGVmIGxvY2F0b3I9IjMzOjEtMzM6NjMiIG5hbWU9ImluYWN0aXZlIiBpZD0iaW5hY3RpdmUiIGFjY2Vzc0xldmVsPSJQdWJsaWMiPgogICAgICAgICA8Y29kZVN5c3RlbSBsb2NhdG9yPSIzMzozNC0zMzo2MyIgbmFtZT0iQ29uZGl0aW9uQ2xpbmljYWxTdGF0dXNDb2RlcyIvPgogICAgICA8L2RlZj4KICAgICAgPGRlZiBsb2NhdG9yPSIzNDoxLTM0OjY1IiBuYW1lPSJyZW1pc3Npb24iIGlkPSJyZW1pc3Npb24iIGFjY2Vzc0xldmVsPSJQdWJsaWMiPgogICAgICAgICA8Y29kZVN5c3RlbSBsb2NhdG9yPSIzNDozNi0zNDo2NSIgbmFtZT0iQ29uZGl0aW9uQ2xpbmljYWxTdGF0dXNDb2RlcyIvPgogICAgICA8L2RlZj4KICAgICAgPGRlZiBsb2NhdG9yPSIzNToxLTM1OjYzIiBuYW1lPSJyZXNvbHZlZCIgaWQ9InJlc29sdmVkIiBhY2Nlc3NMZXZlbD0iUHVibGljIj4KICAgICAgICAgPGNvZGVTeXN0ZW0gbG9jYXRvcj0iMzU6MzQtMzU6NjMiIG5hbWU9IkNvbmRpdGlvbkNsaW5pY2FsU3RhdHVzQ29kZXMiLz4KICAgICAgPC9kZWY+CiAgICAgIDxkZWYgbG9jYXRvcj0iMzg6MS0zODo3MSIgbmFtZT0idW5jb25maXJtZWQiIGlkPSJ1bmNvbmZpcm1lZCIgYWNjZXNzTGV2ZWw9IlB1YmxpYyI+CiAgICAgICAgIDxjb2RlU3lzdGVtIGxvY2F0b3I9IjM4OjQwLTM4OjcxIiBuYW1lPSJDb25kaXRpb25WZXJpZmljYXRpb25TdGF0dXNDb2RlcyIvPgogICAgICA8L2RlZj4KICAgICAgPGRlZiBsb2NhdG9yPSIzOToxLTM5OjcxIiBuYW1lPSJwcm92aXNpb25hbCIgaWQ9InByb3Zpc2lvbmFsIiBhY2Nlc3NMZXZlbD0iUHVibGljIj4KICAgICAgICAgPGNvZGVTeXN0ZW0gbG9jYXRvcj0iMzk6NDAtMzk6NzEiIG5hbWU9IkNvbmRpdGlvblZlcmlmaWNhdGlvblN0YXR1c0NvZGVzIi8+CiAgICAgIDwvZGVmPgogICAgICA8ZGVmIGxvY2F0b3I9IjQwOjEtNDA6NzMiIG5hbWU9ImRpZmZlcmVudGlhbCIgaWQ9ImRpZmZlcmVudGlhbCIgYWNjZXNzTGV2ZWw9IlB1YmxpYyI+CiAgICAgICAgIDxjb2RlU3lzdGVtIGxvY2F0b3I9IjQwOjQyLTQwOjczIiBuYW1lPSJDb25kaXRpb25WZXJpZmljYXRpb25TdGF0dXNDb2RlcyIvPgogICAgICA8L2RlZj4KICAgICAgPGRlZiBsb2NhdG9yPSI0MToxLTQxOjY3IiBuYW1lPSJjb25maXJtZWQiIGlkPSJjb25maXJtZWQiIGFjY2Vzc0xldmVsPSJQdWJsaWMiPgogICAgICAgICA8Y29kZVN5c3RlbSBsb2NhdG9yPSI0MTozNi00MTo2NyIgbmFtZT0iQ29uZGl0aW9uVmVyaWZpY2F0aW9uU3RhdHVzQ29kZXMiLz4KICAgICAgPC9kZWY+CiAgICAgIDxkZWYgbG9jYXRvcj0iNDI6MS00Mjo2MyIgbmFtZT0icmVmdXRlZCIgaWQ9InJlZnV0ZWQiIGFjY2Vzc0xldmVsPSJQdWJsaWMiPgogICAgICAgICA8Y29kZVN5c3RlbSBsb2NhdG9yPSI0MjozMi00Mjo2MyIgbmFtZT0iQ29uZGl0aW9uVmVyaWZpY2F0aW9uU3RhdHVzQ29kZXMiLz4KICAgICAgPC9kZWY+CiAgICAgIDxkZWYgbG9jYXRvcj0iNDM6MS00Mzo4MSIgbmFtZT0iZW50ZXJlZC1pbi1lcnJvciIgaWQ9ImVudGVyZWQtaW4tZXJyb3IiIGFjY2Vzc0xldmVsPSJQdWJsaWMiPgogICAgICAgICA8Y29kZVN5c3RlbSBsb2NhdG9yPSI0Mzo1MC00Mzo4MSIgbmFtZT0iQ29uZGl0aW9uVmVyaWZpY2F0aW9uU3RhdHVzQ29kZXMiLz4KICAgICAgPC9kZWY+CiAgICAgIDxkZWYgbG9jYXRvcj0iNDY6MS00Njo3NiIgbmFtZT0iYWxsZXJneS1hY3RpdmUiIGlkPSJhY3RpdmUiIGFjY2Vzc0xldmVsPSJQdWJsaWMiPgogICAgICAgICA8Y29kZVN5c3RlbSBsb2NhdG9yPSI0NjozOC00Njo3NiIgbmFtZT0iQWxsZXJneUludG9sZXJhbmNlQ2xpbmljYWxTdGF0dXNDb2RlcyIvPgogICAgICA8L2RlZj4KICAgICAgPGRlZiBsb2NhdG9yPSI0NzoxLTQ3OjgwIiBuYW1lPSJhbGxlcmd5LWluYWN0aXZlIiBpZD0iaW5hY3RpdmUiIGFjY2Vzc0xldmVsPSJQdWJsaWMiPgogICAgICAgICA8Y29kZVN5c3RlbSBsb2NhdG9yPSI0Nzo0Mi00Nzo4MCIgbmFtZT0iQWxsZXJneUludG9sZXJhbmNlQ2xpbmljYWxTdGF0dXNDb2RlcyIvPgogICAgICA8L2RlZj4KICAgICAgPGRlZiBsb2NhdG9yPSI0ODoxLTQ4OjgwIiBuYW1lPSJhbGxlcmd5LXJlc29sdmVkIiBpZD0icmVzb2x2ZWQiIGFjY2Vzc0xldmVsPSJQdWJsaWMiPgogICAgICAgICA8Y29kZVN5c3RlbSBsb2NhdG9yPSI0ODo0Mi00ODo4MCIgbmFtZT0iQWxsZXJneUludG9sZXJhbmNlQ2xpbmljYWxTdGF0dXNDb2RlcyIvPgogICAgICA8L2RlZj4KICAgICAgPGRlZiBsb2NhdG9yPSI1MToxLTUxOjg4IiBuYW1lPSJhbGxlcmd5LXVuY29uZmlybWVkIiBpZD0idW5jb25maXJtZWQiIGFjY2Vzc0xldmVsPSJQdWJsaWMiPgogICAgICAgICA8Y29kZVN5c3RlbSBsb2NhdG9yPSI1MTo0OC01MTo4OCIgbmFtZT0iQWxsZXJneUludG9sZXJhbmNlVmVyaWZpY2F0aW9uU3RhdHVzQ29kZXMiLz4KICAgICAgPC9kZWY+CiAgICAgIDxkZWYgbG9jYXRvcj0iNTI6MS01Mjo4NCIgbmFtZT0iYWxsZXJneS1jb25maXJtZWQiIGlkPSJjb25maXJtZWQiIGFjY2Vzc0xldmVsPSJQdWJsaWMiPgogICAgICAgICA8Y29kZVN5c3RlbSBsb2NhdG9yPSI1Mjo0NC01Mjo4NCIgbmFtZT0iQWxsZXJneUludG9sZXJhbmNlVmVyaWZpY2F0aW9uU3RhdHVzQ29kZXMiLz4KICAgICAgPC9kZWY+CiAgICAgIDxkZWYgbG9jYXRvcj0iNTM6MS01Mzo4MCIgbmFtZT0iYWxsZXJneS1yZWZ1dGVkIiBpZD0icmVmdXRlZCIgYWNjZXNzTGV2ZWw9IlB1YmxpYyI+CiAgICAgICAgIDxjb2RlU3lzdGVtIGxvY2F0b3I9IjUzOjQwLTUzOjgwIiBuYW1lPSJBbGxlcmd5SW50b2xlcmFuY2VWZXJpZmljYXRpb25TdGF0dXNDb2RlcyIvPgogICAgICA8L2RlZj4KICAgICAgPGRlZiBsb2NhdG9yPSI1NjoxLTU2OjgyIiBuYW1lPSJDb21tdW5pdHkiIGlkPSJjb21tdW5pdHkiIGRpc3BsYXk9IkNvbW11bml0eSIgYWNjZXNzTGV2ZWw9IlB1YmxpYyI+CiAgICAgICAgIDxjb2RlU3lzdGVtIGxvY2F0b3I9IjU2OjM2LTU2OjYyIiBuYW1lPSJNZWRpY2F0aW9uUmVxdWVzdENhdGVnb3J5Ii8+CiAgICAgIDwvZGVmPgogICAgICA8ZGVmIGxvY2F0b3I9IjU3OjEtNTc6ODIiIG5hbWU9IkRpc2NoYXJnZSIgaWQ9ImRpc2NoYXJnZSIgZGlzcGxheT0iRGlzY2hhcmdlIiBhY2Nlc3NMZXZlbD0iUHVibGljIj4KICAgICAgICAgPGNvZGVTeXN0ZW0gbG9jYXRvcj0iNTc6MzYtNTc6NjIiIG5hbWU9Ik1lZGljYXRpb25SZXF1ZXN0Q2F0ZWdvcnkiLz4KICAgICAgPC9kZWY+CiAgICAgIDxkZWYgbG9jYXRvcj0iNjA6MS02MDo2NyIgbmFtZT0iQUQiIGlkPSJBRCIgZGlzcGxheT0iQWRtaXNzaW9uIGRpYWdub3NpcyIgYWNjZXNzTGV2ZWw9IlB1YmxpYyI+CiAgICAgICAgIDxjb2RlU3lzdGVtIGxvY2F0b3I9IjYwOjIyLTYwOjM3IiBuYW1lPSJEaWFnbm9zaXMgUm9sZSIvPgogICAgICA8L2RlZj4KICAgICAgPGRlZiBsb2NhdG9yPSI2MToxLTYxOjY3IiBuYW1lPSJERCIgaWQ9IkREIiBkaXNwbGF5PSJEaXNjaGFyZ2UgZGlhZ25vc2lzIiBhY2Nlc3NMZXZlbD0iUHVibGljIj4KICAgICAgICAgPGNvZGVTeXN0ZW0gbG9jYXRvcj0iNjE6MjItNjE6MzciIG5hbWU9IkRpYWdub3NpcyBSb2xlIi8+CiAgICAgIDwvZGVmPgogICAgICA8ZGVmIGxvY2F0b3I9IjYyOjEtNjI6NjMiIG5hbWU9IkNDIiBpZD0iQ0MiIGRpc3BsYXk9IkNoaWVmIGNvbXBsYWludCIgYWNjZXNzTGV2ZWw9IlB1YmxpYyI+CiAgICAgICAgIDxjb2RlU3lzdGVtIGxvY2F0b3I9IjYyOjIyLTYyOjM3IiBuYW1lPSJEaWFnbm9zaXMgUm9sZSIvPgogICAgICA8L2RlZj4KICAgICAgPGRlZiBsb2NhdG9yPSI2MzoxLTYzOjY5IiBuYW1lPSJDTSIgaWQ9IkNNIiBkaXNwbGF5PSJDb21vcmJpZGl0eSBkaWFnbm9zaXMiIGFjY2Vzc0xldmVsPSJQdWJsaWMiPgogICAgICAgICA8Y29kZVN5c3RlbSBsb2NhdG9yPSI2MzoyMi02MzozNyIgbmFtZT0iRGlhZ25vc2lzIFJvbGUiLz4KICAgICAgPC9kZWY+CiAgICAgIDxkZWYgbG9jYXRvcj0iNjQ6MS02NDo3MiIgbmFtZT0icHJlLW9wIiBpZD0icHJlLW9wIiBkaXNwbGF5PSJwcmUtb3AgZGlhZ25vc2lzIiBhY2Nlc3NMZXZlbD0iUHVibGljIj4KICAgICAgICAgPGNvZGVTeXN0ZW0gbG9jYXRvcj0iNjQ6MzAtNjQ6NDUiIG5hbWU9IkRpYWdub3NpcyBSb2xlIi8+CiAgICAgIDwvZGVmPgogICAgICA8ZGVmIGxvY2F0b3I9IjY1OjEtNjU6NzUiIG5hbWU9InBvc3Qtb3AiIGlkPSJwb3N0LW9wIiBkaXNwbGF5PSJwb3N0LW9wIGRpYWdub3NpcyIgYWNjZXNzTGV2ZWw9IlB1YmxpYyI+CiAgICAgICAgIDxjb2RlU3lzdGVtIGxvY2F0b3I9IjY1OjMyLTY1OjQ3IiBuYW1lPSJEaWFnbm9zaXMgUm9sZSIvPgogICAgICA8L2RlZj4KICAgICAgPGRlZiBsb2NhdG9yPSI2NjoxLTY2Ojc1IiBuYW1lPSJiaWxsaW5nIiBpZD0iYmlsbGluZyIgZGlzcGxheT0iYmlsbGluZyBkaWFnbm9zaXMiIGFjY2Vzc0xldmVsPSJQdWJsaWMiPgogICAgICAgICA8Y29kZVN5c3RlbSBsb2NhdG9yPSI2NjozMi02Njo0NyIgbmFtZT0iRGlhZ25vc2lzIFJvbGUiLz4KICAgICAgPC9kZWY+CiAgIDwvY29kZXM+CiAgIDxjb250ZXh0cz4KICAgICAgPGRlZiBsb2NhdG9yPSI2ODoxLTY4OjE1IiBuYW1lPSJQYXRpZW50Ii8+CiAgIDwvY29udGV4dHM+CiAgIDxzdGF0ZW1lbnRzPgogICAgICA8ZGVmIGxvY2F0b3I9IjY4OjEtNjg6MTUiIG5hbWU9IlBhdGllbnQiIGNvbnRleHQ9IlBhdGllbnQiPgogICAgICAgICA8ZXhwcmVzc2lvbiB4c2k6dHlwZT0iU2luZ2xldG9uRnJvbSI+CiAgICAgICAgICAgIDxvcGVyYW5kIGxvY2F0b3I9IjY4OjEtNjg6MTUiIGRhdGFUeXBlPSJmaGlyOlBhdGllbnQiIHRlbXBsYXRlSWQ9Imh0dHA6Ly9obDcub3JnL2ZoaXIvU3RydWN0dXJlRGVmaW5pdGlvbi9QYXRpZW50IiB4c2k6dHlwZT0iUmV0cmlldmUiLz4KICAgICAgICAgPC9leHByZXNzaW9uPgogICAgICA8L2RlZj4KICAgICAgPGRlZiBsb2NhdG9yPSI3OToxLTk5OjQiIG5hbWU9IlRvSW50ZXJ2YWwiIGNvbnRleHQ9IlBhdGllbnQiIGFjY2Vzc0xldmVsPSJQdWJsaWMiIHhzaTp0eXBlPSJGdW5jdGlvbkRlZiI+CiAgICAgICAgIDxhbm5vdGF0aW9uIHhzaTp0eXBlPSJhOkFubm90YXRpb24iPgogICAgICAgICAgICA8YTp0IG5hbWU9ImRlc2NyaXB0aW9uIiB2YWx1ZT0iTm9ybWFsaXplcyBhIHZhbHVlIHRoYXQgaXMgYSBjaG9pY2Ugb2YgdGltaW5nLXZhbHVlZCB0eXBlcyB0byBhbiBlcXVpdmFsZW50IGludGVydmFsIi8+CiAgICAgICAgICAgIDxhOnQgbmFtZT0iY29tbWVudCIgdmFsdWU9Ik5vcm1hbGl6ZXMgYSBjaG9pY2UgdHlwZSBvZiBGSElSLmRhdGVUaW1lLCBGSElSLlBlcmlvZCwgRkhJUi5UaW1pbmcsIEZISVIuaW5zdGFuY2UsIEZISVIuc3RyaW5nLCBGSElSLkFnZSwgb3IgRkhJUi5SYW5nZSB0eXBlcyYjeGE7dG8gYW4gZXF1aXZhbGVudCBpbnRlcnZhbC4gVGhpcyBzZWxlY3Rpb24gb2YgY2hvaWNlIHR5cGVzIGlzIGEgc3VwZXJzZXQgb2YgdGhlIG1ham9yaXR5IG9mIGNob2ljZSB0eXBlcyB0aGF0IGFyZSB1c2VkIGFzIHBvc3NpYmxlJiN4YTtyZXByZXNlbnRhdGlvbnMgZm9yIHRpbWluZy12YWx1ZWQgZWxlbWVudHMgaW4gRkhJUiwgYWxsb3dpbmcgdGhpcyBmdW5jdGlvbiB0byBiZSB1c2VkIGFjcm9zcyBhbnkgcmVzb3VyY2UuIE5PVEU6IER1ZSB0byB0aGUmI3hhO2NvbXBsZXhpdHkgb2YgZGV0ZXJtaW5pbmcgYSBzaW5nbGUgaW50ZXJ2YWwgZnJvbSBhIFRpbWluZyBvciBTdHJpbmcgdHlwZSwgdGhpcyBmdW5jdGlvbiB3aWxsIHRocm93IGEgcnVuLXRpbWUgZXhjZXB0aW9uIGlmIGl0IGlzIHVzZWQmI3hhO3dpdGggYSBUaW1pbmcgb3IgU3RyaW5nLiBOT1RFOiBUaW1pbmcgaGFzIGJlZW4gbW92ZWQgdG8gVG9UaW1pbmdJbnRlcnZhbC4iLz4KICAgICAgICAgPC9hbm5vdGF0aW9uPgogICAgICAgICA8ZXhwcmVzc2lvbiBsb2NhdG9yPSI4MDozLTk5OjQiIHhzaTp0eXBlPSJDYXNlIj4KICAgICAgICAgICAgPGNhc2VJdGVtIGxvY2F0b3I9IjgxOjQtODI6MTExIj4KICAgICAgICAgICAgICAgPHdoZW4gbG9jYXRvcj0iODE6OS04MTozMSIgeHNpOnR5cGU9IklzIj4KICAgICAgICAgICAgICAgICAgPG9wZXJhbmQgbG9jYXRvcj0iODE6OS04MToxNCIgbmFtZT0iY2hvaWNlIiB4c2k6dHlwZT0iT3BlcmFuZFJlZiIvPgogICAgICAgICAgICAgICAgICA8aXNUeXBlU3BlY2lmaWVyIGxvY2F0b3I9IjgxOjE5LTgxOjMxIiBuYW1lPSJmaGlyOmRhdGVUaW1lIiB4c2k6dHlwZT0iTmFtZWRUeXBlU3BlY2lmaWVyIi8+CiAgICAgICAgICAgICAgIDwvd2hlbj4KICAgICAgICAgICAgICAgPHRoZW4gbG9jYXRvcj0iODI6Ni04MjoxMTEiIGxvd0Nsb3NlZD0idHJ1ZSIgaGlnaENsb3NlZD0idHJ1ZSIgeHNpOnR5cGU9IkludGVydmFsIj4KICAgICAgICAgICAgICAgICAgPGxvdyBsb2NhdG9yPSI4MjoxNS04Mjo2MSIgbmFtZT0iVG9EYXRlVGltZSIgbGlicmFyeU5hbWU9IkZISVJIZWxwZXJzIiB4c2k6dHlwZT0iRnVuY3Rpb25SZWYiPgogICAgICAgICAgICAgICAgICAgICA8b3BlcmFuZCBsb2NhdG9yPSI4MjozOC04Mjo2MCIgc3RyaWN0PSJmYWxzZSIgeHNpOnR5cGU9IkFzIj4KICAgICAgICAgICAgICAgICAgICAgICAgPG9wZXJhbmQgbG9jYXRvcj0iODI6MzgtODI6NDMiIG5hbWU9ImNob2ljZSIgeHNpOnR5cGU9Ik9wZXJhbmRSZWYiLz4KICAgICAgICAgICAgICAgICAgICAgICAgPGFzVHlwZVNwZWNpZmllciBsb2NhdG9yPSI4Mjo0OC04Mjo2MCIgbmFtZT0iZmhpcjpkYXRlVGltZSIgeHNpOnR5cGU9Ik5hbWVkVHlwZVNwZWNpZmllciIvPgogICAgICAgICAgICAgICAgICAgICA8L29wZXJhbmQ+CiAgICAgICAgICAgICAgICAgIDwvbG93PgogICAgICAgICAgICAgICAgICA8aGlnaCBsb2NhdG9yPSI4Mjo2NC04MjoxMTAiIG5hbWU9IlRvRGF0ZVRpbWUiIGxpYnJhcnlOYW1lPSJGSElSSGVscGVycyIgeHNpOnR5cGU9IkZ1bmN0aW9uUmVmIj4KICAgICAgICAgICAgICAgICAgICAgPG9wZXJhbmQgbG9jYXRvcj0iODI6ODctODI6MTA5IiBzdHJpY3Q9ImZhbHNlIiB4c2k6dHlwZT0iQXMiPgogICAgICAgICAgICAgICAgICAgICAgICA8b3BlcmFuZCBsb2NhdG9yPSI4Mjo4Ny04Mjo5MiIgbmFtZT0iY2hvaWNlIiB4c2k6dHlwZT0iT3BlcmFuZFJlZiIvPgogICAgICAgICAgICAgICAgICAgICAgICA8YXNUeXBlU3BlY2lmaWVyIGxvY2F0b3I9IjgyOjk3LTgyOjEwOSIgbmFtZT0iZmhpcjpkYXRlVGltZSIgeHNpOnR5cGU9Ik5hbWVkVHlwZVNwZWNpZmllciIvPgogICAgICAgICAgICAgICAgICAgICA8L29wZXJhbmQ+CiAgICAgICAgICAgICAgICAgIDwvaGlnaD4KICAgICAgICAgICAgICAgPC90aGVuPgogICAgICAgICAgICA8L2Nhc2VJdGVtPgogICAgICAgICAgICA8Y2FzZUl0ZW0gbG9jYXRvcj0iODM6My04NDo0OSI+CiAgICAgICAgICAgICAgIDx3aGVuIGxvY2F0b3I9IjgzOjgtODM6MjgiIHhzaTp0eXBlPSJJcyI+CiAgICAgICAgICAgICAgICAgIDxvcGVyYW5kIGxvY2F0b3I9IjgzOjgtODM6MTMiIG5hbWU9ImNob2ljZSIgeHNpOnR5cGU9Ik9wZXJhbmRSZWYiLz4KICAgICAgICAgICAgICAgICAgPGlzVHlwZVNwZWNpZmllciBsb2NhdG9yPSI4MzoxOC04MzoyOCIgbmFtZT0iZmhpcjpQZXJpb2QiIHhzaTp0eXBlPSJOYW1lZFR5cGVTcGVjaWZpZXIiLz4KICAgICAgICAgICAgICAgPC93aGVuPgogICAgICAgICAgICAgICA8dGhlbiBsb2NhdG9yPSI4NDo1LTg0OjQ5IiBuYW1lPSJUb0ludGVydmFsIiBsaWJyYXJ5TmFtZT0iRkhJUkhlbHBlcnMiIHhzaTp0eXBlPSJGdW5jdGlvblJlZiI+CiAgICAgICAgICAgICAgICAgIDxvcGVyYW5kIGxvY2F0b3I9Ijg0OjI4LTg0OjQ4IiBzdHJpY3Q9ImZhbHNlIiB4c2k6dHlwZT0iQXMiPgogICAgICAgICAgICAgICAgICAgICA8b3BlcmFuZCBsb2NhdG9yPSI4NDoyOC04NDozMyIgbmFtZT0iY2hvaWNlIiB4c2k6dHlwZT0iT3BlcmFuZFJlZiIvPgogICAgICAgICAgICAgICAgICAgICA8YXNUeXBlU3BlY2lmaWVyIGxvY2F0b3I9Ijg0OjM4LTg0OjQ4IiBuYW1lPSJmaGlyOlBlcmlvZCIgeHNpOnR5cGU9Ik5hbWVkVHlwZVNwZWNpZmllciIvPgogICAgICAgICAgICAgICAgICA8L29wZXJhbmQ+CiAgICAgICAgICAgICAgIDwvdGhlbj4KICAgICAgICAgICAgPC9jYXNlSXRlbT4KICAgICAgICAgICAgPGNhc2VJdGVtIGxvY2F0b3I9Ijg1OjMtODY6MTA3Ij4KICAgICAgICAgICAgICAgPHdoZW4gbG9jYXRvcj0iODU6OC04NToyOSIgeHNpOnR5cGU9IklzIj4KICAgICAgICAgICAgICAgICAgPG9wZXJhbmQgbG9jYXRvcj0iODU6OC04NToxMyIgbmFtZT0iY2hvaWNlIiB4c2k6dHlwZT0iT3BlcmFuZFJlZiIvPgogICAgICAgICAgICAgICAgICA8aXNUeXBlU3BlY2lmaWVyIGxvY2F0b3I9Ijg1OjE4LTg1OjI5IiBuYW1lPSJmaGlyOmluc3RhbnQiIHhzaTp0eXBlPSJOYW1lZFR5cGVTcGVjaWZpZXIiLz4KICAgICAgICAgICAgICAgPC93aGVuPgogICAgICAgICAgICAgICA8dGhlbiBsb2NhdG9yPSI4Njo0LTg2OjEwNyIgbG93Q2xvc2VkPSJ0cnVlIiBoaWdoQ2xvc2VkPSJ0cnVlIiB4c2k6dHlwZT0iSW50ZXJ2YWwiPgogICAgICAgICAgICAgICAgICA8bG93IGxvY2F0b3I9Ijg2OjEzLTg2OjU4IiBuYW1lPSJUb0RhdGVUaW1lIiBsaWJyYXJ5TmFtZT0iRkhJUkhlbHBlcnMiIHhzaTp0eXBlPSJGdW5jdGlvblJlZiI+CiAgICAgICAgICAgICAgICAgICAgIDxvcGVyYW5kIGxvY2F0b3I9Ijg2OjM2LTg2OjU3IiBzdHJpY3Q9ImZhbHNlIiB4c2k6dHlwZT0iQXMiPgogICAgICAgICAgICAgICAgICAgICAgICA8b3BlcmFuZCBsb2NhdG9yPSI4NjozNi04Njo0MSIgbmFtZT0iY2hvaWNlIiB4c2k6dHlwZT0iT3BlcmFuZFJlZiIvPgogICAgICAgICAgICAgICAgICAgICAgICA8YXNUeXBlU3BlY2lmaWVyIGxvY2F0b3I9Ijg2OjQ2LTg2OjU3IiBuYW1lPSJmaGlyOmluc3RhbnQiIHhzaTp0eXBlPSJOYW1lZFR5cGVTcGVjaWZpZXIiLz4KICAgICAgICAgICAgICAgICAgICAgPC9vcGVyYW5kPgogICAgICAgICAgICAgICAgICA8L2xvdz4KICAgICAgICAgICAgICAgICAgPGhpZ2ggbG9jYXRvcj0iODY6NjEtODY6MTA2IiBuYW1lPSJUb0RhdGVUaW1lIiBsaWJyYXJ5TmFtZT0iRkhJUkhlbHBlcnMiIHhzaTp0eXBlPSJGdW5jdGlvblJlZiI+CiAgICAgICAgICAgICAgICAgICAgIDxvcGVyYW5kIGxvY2F0b3I9Ijg2Ojg0LTg2OjEwNSIgc3RyaWN0PSJmYWxzZSIgeHNpOnR5cGU9IkFzIj4KICAgICAgICAgICAgICAgICAgICAgICAgPG9wZXJhbmQgbG9jYXRvcj0iODY6ODQtODY6ODkiIG5hbWU9ImNob2ljZSIgeHNpOnR5cGU9Ik9wZXJhbmRSZWYiLz4KICAgICAgICAgICAgICAgICAgICAgICAgPGFzVHlwZVNwZWNpZmllciBsb2NhdG9yPSI4Njo5NC04NjoxMDUiIG5hbWU9ImZoaXI6aW5zdGFudCIgeHNpOnR5cGU9Ik5hbWVkVHlwZVNwZWNpZmllciIvPgogICAgICAgICAgICAgICAgICAgICA8L29wZXJhbmQ+CiAgICAgICAgICAgICAgICAgIDwvaGlnaD4KICAgICAgICAgICAgICAgPC90aGVuPgogICAgICAgICAgICA8L2Nhc2VJdGVtPgogICAgICAgICAgICA8Y2FzZUl0ZW0gbG9jYXRvcj0iODc6My04OTo5NyI+CiAgICAgICAgICAgICAgIDx3aGVuIGxvY2F0b3I9Ijg3OjgtODc6MjUiIHhzaTp0eXBlPSJJcyI+CiAgICAgICAgICAgICAgICAgIDxvcGVyYW5kIGxvY2F0b3I9Ijg3OjgtODc6MTMiIG5hbWU9ImNob2ljZSIgeHNpOnR5cGU9Ik9wZXJhbmRSZWYiLz4KICAgICAgICAgICAgICAgICAgPGlzVHlwZVNwZWNpZmllciBsb2NhdG9yPSI4NzoxOC04NzoyNSIgbmFtZT0iZmhpcjpBZ2UiIHhzaTp0eXBlPSJOYW1lZFR5cGVTcGVjaWZpZXIiLz4KICAgICAgICAgICAgICAgPC93aGVuPgogICAgICAgICAgICAgICA8dGhlbiB4c2k6dHlwZT0iSW50ZXJ2YWwiPgogICAgICAgICAgICAgICAgICA8bG93IHhzaTp0eXBlPSJUb0RhdGVUaW1lIj4KICAgICAgICAgICAgICAgICAgICAgPG9wZXJhbmQgcGF0aD0ibG93IiB4c2k6dHlwZT0iUHJvcGVydHkiPgogICAgICAgICAgICAgICAgICAgICAgICA8c291cmNlIGxvY2F0b3I9Ijg4OjUtODk6OTciIGxvd0Nsb3NlZD0idHJ1ZSIgaGlnaENsb3NlZD0iZmFsc2UiIHhzaTp0eXBlPSJJbnRlcnZhbCI+CiAgICAgICAgICAgICAgICAgICAgICAgICAgIDxsb3cgbG9jYXRvcj0iODg6MTQtODg6OTUiIHhzaTp0eXBlPSJBZGQiPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8b3BlcmFuZCBsb2NhdG9yPSI4ODoxNC04ODo1MCIgbmFtZT0iVG9EYXRlIiBsaWJyYXJ5TmFtZT0iRkhJUkhlbHBlcnMiIHhzaTp0eXBlPSJGdW5jdGlvblJlZiI+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDxvcGVyYW5kIGxvY2F0b3I9Ijg4OjMzLTg4OjQ5IiBwYXRoPSJiaXJ0aERhdGUiIHhzaTp0eXBlPSJQcm9wZXJ0eSI+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDxzb3VyY2UgbG9jYXRvcj0iODg6MzMtODg6MzkiIG5hbWU9IlBhdGllbnQiIHhzaTp0eXBlPSJFeHByZXNzaW9uUmVmIi8+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDwvb3BlcmFuZD4KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPC9vcGVyYW5kPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8b3BlcmFuZCBsb2NhdG9yPSI4ODo1NC04ODo5NSIgbmFtZT0iVG9RdWFudGl0eSIgbGlicmFyeU5hbWU9IkZISVJIZWxwZXJzIiB4c2k6dHlwZT0iRnVuY3Rpb25SZWYiPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8b3BlcmFuZCBsb2NhdG9yPSI4ODo3Ny04ODo5NCIgc3RyaWN0PSJmYWxzZSIgeHNpOnR5cGU9IkFzIj4KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPG9wZXJhbmQgbG9jYXRvcj0iODg6NzctODg6ODIiIG5hbWU9ImNob2ljZSIgeHNpOnR5cGU9Ik9wZXJhbmRSZWYiLz4KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPGFzVHlwZVNwZWNpZmllciBsb2NhdG9yPSI4ODo4Ny04ODo5NCIgbmFtZT0iZmhpcjpBZ2UiIHhzaTp0eXBlPSJOYW1lZFR5cGVTcGVjaWZpZXIiLz4KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPC9vcGVyYW5kPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8L29wZXJhbmQ+CiAgICAgICAgICAgICAgICAgICAgICAgICAgIDwvbG93PgogICAgICAgICAgICAgICAgICAgICAgICAgICA8aGlnaCBsb2NhdG9yPSI4OTo2LTg5Ojk2IiB4c2k6dHlwZT0iQWRkIj4KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPG9wZXJhbmQgbG9jYXRvcj0iODk6Ni04OTo4NyIgeHNpOnR5cGU9IkFkZCI+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDxvcGVyYW5kIGxvY2F0b3I9Ijg5OjYtODk6NDIiIG5hbWU9IlRvRGF0ZSIgbGlicmFyeU5hbWU9IkZISVJIZWxwZXJzIiB4c2k6dHlwZT0iRnVuY3Rpb25SZWYiPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8b3BlcmFuZCBsb2NhdG9yPSI4OToyNS04OTo0MSIgcGF0aD0iYmlydGhEYXRlIiB4c2k6dHlwZT0iUHJvcGVydHkiPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8c291cmNlIGxvY2F0b3I9Ijg5OjI1LTg5OjMxIiBuYW1lPSJQYXRpZW50IiB4c2k6dHlwZT0iRXhwcmVzc2lvblJlZiIvPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8L29wZXJhbmQ+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDwvb3BlcmFuZD4KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPG9wZXJhbmQgbG9jYXRvcj0iODk6NDYtODk6ODciIG5hbWU9IlRvUXVhbnRpdHkiIGxpYnJhcnlOYW1lPSJGSElSSGVscGVycyIgeHNpOnR5cGU9IkZ1bmN0aW9uUmVmIj4KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPG9wZXJhbmQgbG9jYXRvcj0iODk6NjktODk6ODYiIHN0cmljdD0iZmFsc2UiIHhzaTp0eXBlPSJBcyI+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDxvcGVyYW5kIGxvY2F0b3I9Ijg5OjY5LTg5Ojc0IiBuYW1lPSJjaG9pY2UiIHhzaTp0eXBlPSJPcGVyYW5kUmVmIi8+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDxhc1R5cGVTcGVjaWZpZXIgbG9jYXRvcj0iODk6NzktODk6ODYiIG5hbWU9ImZoaXI6QWdlIiB4c2k6dHlwZT0iTmFtZWRUeXBlU3BlY2lmaWVyIi8+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDwvb3BlcmFuZD4KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPC9vcGVyYW5kPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8L29wZXJhbmQ+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDxvcGVyYW5kIGxvY2F0b3I9Ijg5OjkxLTg5Ojk2IiB2YWx1ZT0iMSIgdW5pdD0ieWVhciIgeHNpOnR5cGU9IlF1YW50aXR5Ii8+CiAgICAgICAgICAgICAgICAgICAgICAgICAgIDwvaGlnaD4KICAgICAgICAgICAgICAgICAgICAgICAgPC9zb3VyY2U+CiAgICAgICAgICAgICAgICAgICAgIDwvb3BlcmFuZD4KICAgICAgICAgICAgICAgICAgPC9sb3c+CiAgICAgICAgICAgICAgICAgIDxsb3dDbG9zZWRFeHByZXNzaW9uIHBhdGg9Imxvd0Nsb3NlZCIgeHNpOnR5cGU9IlByb3BlcnR5Ij4KICAgICAgICAgICAgICAgICAgICAgPHNvdXJjZSBsb2NhdG9yPSI4ODo1LTg5Ojk3IiBsb3dDbG9zZWQ9InRydWUiIGhpZ2hDbG9zZWQ9ImZhbHNlIiB4c2k6dHlwZT0iSW50ZXJ2YWwiPgogICAgICAgICAgICAgICAgICAgICAgICA8bG93IGxvY2F0b3I9Ijg4OjE0LTg4Ojk1IiB4c2k6dHlwZT0iQWRkIj4KICAgICAgICAgICAgICAgICAgICAgICAgICAgPG9wZXJhbmQgbG9jYXRvcj0iODg6MTQtODg6NTAiIG5hbWU9IlRvRGF0ZSIgbGlicmFyeU5hbWU9IkZISVJIZWxwZXJzIiB4c2k6dHlwZT0iRnVuY3Rpb25SZWYiPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8b3BlcmFuZCBsb2NhdG9yPSI4ODozMy04ODo0OSIgcGF0aD0iYmlydGhEYXRlIiB4c2k6dHlwZT0iUHJvcGVydHkiPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8c291cmNlIGxvY2F0b3I9Ijg4OjMzLTg4OjM5IiBuYW1lPSJQYXRpZW50IiB4c2k6dHlwZT0iRXhwcmVzc2lvblJlZiIvPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8L29wZXJhbmQ+CiAgICAgICAgICAgICAgICAgICAgICAgICAgIDwvb3BlcmFuZD4KICAgICAgICAgICAgICAgICAgICAgICAgICAgPG9wZXJhbmQgbG9jYXRvcj0iODg6NTQtODg6OTUiIG5hbWU9IlRvUXVhbnRpdHkiIGxpYnJhcnlOYW1lPSJGSElSSGVscGVycyIgeHNpOnR5cGU9IkZ1bmN0aW9uUmVmIj4KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPG9wZXJhbmQgbG9jYXRvcj0iODg6NzctODg6OTQiIHN0cmljdD0iZmFsc2UiIHhzaTp0eXBlPSJBcyI+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDxvcGVyYW5kIGxvY2F0b3I9Ijg4Ojc3LTg4OjgyIiBuYW1lPSJjaG9pY2UiIHhzaTp0eXBlPSJPcGVyYW5kUmVmIi8+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDxhc1R5cGVTcGVjaWZpZXIgbG9jYXRvcj0iODg6ODctODg6OTQiIG5hbWU9ImZoaXI6QWdlIiB4c2k6dHlwZT0iTmFtZWRUeXBlU3BlY2lmaWVyIi8+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDwvb3BlcmFuZD4KICAgICAgICAgICAgICAgICAgICAgICAgICAgPC9vcGVyYW5kPgogICAgICAgICAgICAgICAgICAgICAgICA8L2xvdz4KICAgICAgICAgICAgICAgICAgICAgICAgPGhpZ2ggbG9jYXRvcj0iODk6Ni04OTo5NiIgeHNpOnR5cGU9IkFkZCI+CiAgICAgICAgICAgICAgICAgICAgICAgICAgIDxvcGVyYW5kIGxvY2F0b3I9Ijg5OjYtODk6ODciIHhzaTp0eXBlPSJBZGQiPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8b3BlcmFuZCBsb2NhdG9yPSI4OTo2LTg5OjQyIiBuYW1lPSJUb0RhdGUiIGxpYnJhcnlOYW1lPSJGSElSSGVscGVycyIgeHNpOnR5cGU9IkZ1bmN0aW9uUmVmIj4KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPG9wZXJhbmQgbG9jYXRvcj0iODk6MjUtODk6NDEiIHBhdGg9ImJpcnRoRGF0ZSIgeHNpOnR5cGU9IlByb3BlcnR5Ij4KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPHNvdXJjZSBsb2NhdG9yPSI4OToyNS04OTozMSIgbmFtZT0iUGF0aWVudCIgeHNpOnR5cGU9IkV4cHJlc3Npb25SZWYiLz4KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPC9vcGVyYW5kPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8L29wZXJhbmQ+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDxvcGVyYW5kIGxvY2F0b3I9Ijg5OjQ2LTg5Ojg3IiBuYW1lPSJUb1F1YW50aXR5IiBsaWJyYXJ5TmFtZT0iRkhJUkhlbHBlcnMiIHhzaTp0eXBlPSJGdW5jdGlvblJlZiI+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDxvcGVyYW5kIGxvY2F0b3I9Ijg5OjY5LTg5Ojg2IiBzdHJpY3Q9ImZhbHNlIiB4c2k6dHlwZT0iQXMiPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8b3BlcmFuZCBsb2NhdG9yPSI4OTo2OS04OTo3NCIgbmFtZT0iY2hvaWNlIiB4c2k6dHlwZT0iT3BlcmFuZFJlZiIvPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8YXNUeXBlU3BlY2lmaWVyIGxvY2F0b3I9Ijg5Ojc5LTg5Ojg2IiBuYW1lPSJmaGlyOkFnZSIgeHNpOnR5cGU9Ik5hbWVkVHlwZVNwZWNpZmllciIvPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8L29wZXJhbmQ+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDwvb3BlcmFuZD4KICAgICAgICAgICAgICAgICAgICAgICAgICAgPC9vcGVyYW5kPgogICAgICAgICAgICAgICAgICAgICAgICAgICA8b3BlcmFuZCBsb2NhdG9yPSI4OTo5MS04OTo5NiIgdmFsdWU9IjEiIHVuaXQ9InllYXIiIHhzaTp0eXBlPSJRdWFudGl0eSIvPgogICAgICAgICAgICAgICAgICAgICAgICA8L2hpZ2g+CiAgICAgICAgICAgICAgICAgICAgIDwvc291cmNlPgogICAgICAgICAgICAgICAgICA8L2xvd0Nsb3NlZEV4cHJlc3Npb24+CiAgICAgICAgICAgICAgICAgIDxoaWdoIHhzaTp0eXBlPSJUb0RhdGVUaW1lIj4KICAgICAgICAgICAgICAgICAgICAgPG9wZXJhbmQgcGF0aD0iaGlnaCIgeHNpOnR5cGU9IlByb3BlcnR5Ij4KICAgICAgICAgICAgICAgICAgICAgICAgPHNvdXJjZSBsb2NhdG9yPSI4ODo1LTg5Ojk3IiBsb3dDbG9zZWQ9InRydWUiIGhpZ2hDbG9zZWQ9ImZhbHNlIiB4c2k6dHlwZT0iSW50ZXJ2YWwiPgogICAgICAgICAgICAgICAgICAgICAgICAgICA8bG93IGxvY2F0b3I9Ijg4OjE0LTg4Ojk1IiB4c2k6dHlwZT0iQWRkIj4KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPG9wZXJhbmQgbG9jYXRvcj0iODg6MTQtODg6NTAiIG5hbWU9IlRvRGF0ZSIgbGlicmFyeU5hbWU9IkZISVJIZWxwZXJzIiB4c2k6dHlwZT0iRnVuY3Rpb25SZWYiPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8b3BlcmFuZCBsb2NhdG9yPSI4ODozMy04ODo0OSIgcGF0aD0iYmlydGhEYXRlIiB4c2k6dHlwZT0iUHJvcGVydHkiPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8c291cmNlIGxvY2F0b3I9Ijg4OjMzLTg4OjM5IiBuYW1lPSJQYXRpZW50IiB4c2k6dHlwZT0iRXhwcmVzc2lvblJlZiIvPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8L29wZXJhbmQ+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDwvb3BlcmFuZD4KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPG9wZXJhbmQgbG9jYXRvcj0iODg6NTQtODg6OTUiIG5hbWU9IlRvUXVhbnRpdHkiIGxpYnJhcnlOYW1lPSJGSElSSGVscGVycyIgeHNpOnR5cGU9IkZ1bmN0aW9uUmVmIj4KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPG9wZXJhbmQgbG9jYXRvcj0iODg6NzctODg6OTQiIHN0cmljdD0iZmFsc2UiIHhzaTp0eXBlPSJBcyI+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDxvcGVyYW5kIGxvY2F0b3I9Ijg4Ojc3LTg4OjgyIiBuYW1lPSJjaG9pY2UiIHhzaTp0eXBlPSJPcGVyYW5kUmVmIi8+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDxhc1R5cGVTcGVjaWZpZXIgbG9jYXRvcj0iODg6ODctODg6OTQiIG5hbWU9ImZoaXI6QWdlIiB4c2k6dHlwZT0iTmFtZWRUeXBlU3BlY2lmaWVyIi8+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDwvb3BlcmFuZD4KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPC9vcGVyYW5kPgogICAgICAgICAgICAgICAgICAgICAgICAgICA8L2xvdz4KICAgICAgICAgICAgICAgICAgICAgICAgICAgPGhpZ2ggbG9jYXRvcj0iODk6Ni04OTo5NiIgeHNpOnR5cGU9IkFkZCI+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDxvcGVyYW5kIGxvY2F0b3I9Ijg5OjYtODk6ODciIHhzaTp0eXBlPSJBZGQiPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8b3BlcmFuZCBsb2NhdG9yPSI4OTo2LTg5OjQyIiBuYW1lPSJUb0RhdGUiIGxpYnJhcnlOYW1lPSJGSElSSGVscGVycyIgeHNpOnR5cGU9IkZ1bmN0aW9uUmVmIj4KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPG9wZXJhbmQgbG9jYXRvcj0iODk6MjUtODk6NDEiIHBhdGg9ImJpcnRoRGF0ZSIgeHNpOnR5cGU9IlByb3BlcnR5Ij4KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPHNvdXJjZSBsb2NhdG9yPSI4OToyNS04OTozMSIgbmFtZT0iUGF0aWVudCIgeHNpOnR5cGU9IkV4cHJlc3Npb25SZWYiLz4KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPC9vcGVyYW5kPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8L29wZXJhbmQ+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDxvcGVyYW5kIGxvY2F0b3I9Ijg5OjQ2LTg5Ojg3IiBuYW1lPSJUb1F1YW50aXR5IiBsaWJyYXJ5TmFtZT0iRkhJUkhlbHBlcnMiIHhzaTp0eXBlPSJGdW5jdGlvblJlZiI+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDxvcGVyYW5kIGxvY2F0b3I9Ijg5OjY5LTg5Ojg2IiBzdHJpY3Q9ImZhbHNlIiB4c2k6dHlwZT0iQXMiPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8b3BlcmFuZCBsb2NhdG9yPSI4OTo2OS04OTo3NCIgbmFtZT0iY2hvaWNlIiB4c2k6dHlwZT0iT3BlcmFuZFJlZiIvPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8YXNUeXBlU3BlY2lmaWVyIGxvY2F0b3I9Ijg5Ojc5LTg5Ojg2IiBuYW1lPSJmaGlyOkFnZSIgeHNpOnR5cGU9Ik5hbWVkVHlwZVNwZWNpZmllciIvPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8L29wZXJhbmQ+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDwvb3BlcmFuZD4KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPC9vcGVyYW5kPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8b3BlcmFuZCBsb2NhdG9yPSI4OTo5MS04OTo5NiIgdmFsdWU9IjEiIHVuaXQ9InllYXIiIHhzaTp0eXBlPSJRdWFudGl0eSIvPgogICAgICAgICAgICAgICAgICAgICAgICAgICA8L2hpZ2g+CiAgICAgICAgICAgICAgICAgICAgICAgIDwvc291cmNlPgogICAgICAgICAgICAgICAgICAgICA8L29wZXJhbmQ+CiAgICAgICAgICAgICAgICAgIDwvaGlnaD4KICAgICAgICAgICAgICAgICAgPGhpZ2hDbG9zZWRFeHByZXNzaW9uIHBhdGg9ImhpZ2hDbG9zZWQiIHhzaTp0eXBlPSJQcm9wZXJ0eSI+CiAgICAgICAgICAgICAgICAgICAgIDxzb3VyY2UgbG9jYXRvcj0iODg6NS04OTo5NyIgbG93Q2xvc2VkPSJ0cnVlIiBoaWdoQ2xvc2VkPSJmYWxzZSIgeHNpOnR5cGU9IkludGVydmFsIj4KICAgICAgICAgICAgICAgICAgICAgICAgPGxvdyBsb2NhdG9yPSI4ODoxNC04ODo5NSIgeHNpOnR5cGU9IkFkZCI+CiAgICAgICAgICAgICAgICAgICAgICAgICAgIDxvcGVyYW5kIGxvY2F0b3I9Ijg4OjE0LTg4OjUwIiBuYW1lPSJUb0RhdGUiIGxpYnJhcnlOYW1lPSJGSElSSGVscGVycyIgeHNpOnR5cGU9IkZ1bmN0aW9uUmVmIj4KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPG9wZXJhbmQgbG9jYXRvcj0iODg6MzMtODg6NDkiIHBhdGg9ImJpcnRoRGF0ZSIgeHNpOnR5cGU9IlByb3BlcnR5Ij4KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPHNvdXJjZSBsb2NhdG9yPSI4ODozMy04ODozOSIgbmFtZT0iUGF0aWVudCIgeHNpOnR5cGU9IkV4cHJlc3Npb25SZWYiLz4KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPC9vcGVyYW5kPgogICAgICAgICAgICAgICAgICAgICAgICAgICA8L29wZXJhbmQ+CiAgICAgICAgICAgICAgICAgICAgICAgICAgIDxvcGVyYW5kIGxvY2F0b3I9Ijg4OjU0LTg4Ojk1IiBuYW1lPSJUb1F1YW50aXR5IiBsaWJyYXJ5TmFtZT0iRkhJUkhlbHBlcnMiIHhzaTp0eXBlPSJGdW5jdGlvblJlZiI+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDxvcGVyYW5kIGxvY2F0b3I9Ijg4Ojc3LTg4Ojk0IiBzdHJpY3Q9ImZhbHNlIiB4c2k6dHlwZT0iQXMiPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8b3BlcmFuZCBsb2NhdG9yPSI4ODo3Ny04ODo4MiIgbmFtZT0iY2hvaWNlIiB4c2k6dHlwZT0iT3BlcmFuZFJlZiIvPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8YXNUeXBlU3BlY2lmaWVyIGxvY2F0b3I9Ijg4Ojg3LTg4Ojk0IiBuYW1lPSJmaGlyOkFnZSIgeHNpOnR5cGU9Ik5hbWVkVHlwZVNwZWNpZmllciIvPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8L29wZXJhbmQ+CiAgICAgICAgICAgICAgICAgICAgICAgICAgIDwvb3BlcmFuZD4KICAgICAgICAgICAgICAgICAgICAgICAgPC9sb3c+CiAgICAgICAgICAgICAgICAgICAgICAgIDxoaWdoIGxvY2F0b3I9Ijg5OjYtODk6OTYiIHhzaTp0eXBlPSJBZGQiPgogICAgICAgICAgICAgICAgICAgICAgICAgICA8b3BlcmFuZCBsb2NhdG9yPSI4OTo2LTg5Ojg3IiB4c2k6dHlwZT0iQWRkIj4KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPG9wZXJhbmQgbG9jYXRvcj0iODk6Ni04OTo0MiIgbmFtZT0iVG9EYXRlIiBsaWJyYXJ5TmFtZT0iRkhJUkhlbHBlcnMiIHhzaTp0eXBlPSJGdW5jdGlvblJlZiI+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDxvcGVyYW5kIGxvY2F0b3I9Ijg5OjI1LTg5OjQxIiBwYXRoPSJiaXJ0aERhdGUiIHhzaTp0eXBlPSJQcm9wZXJ0eSI+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDxzb3VyY2UgbG9jYXRvcj0iODk6MjUtODk6MzEiIG5hbWU9IlBhdGllbnQiIHhzaTp0eXBlPSJFeHByZXNzaW9uUmVmIi8+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDwvb3BlcmFuZD4KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPC9vcGVyYW5kPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8b3BlcmFuZCBsb2NhdG9yPSI4OTo0Ni04OTo4NyIgbmFtZT0iVG9RdWFudGl0eSIgbGlicmFyeU5hbWU9IkZISVJIZWxwZXJzIiB4c2k6dHlwZT0iRnVuY3Rpb25SZWYiPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8b3BlcmFuZCBsb2NhdG9yPSI4OTo2OS04OTo4NiIgc3RyaWN0PSJmYWxzZSIgeHNpOnR5cGU9IkFzIj4KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPG9wZXJhbmQgbG9jYXRvcj0iODk6NjktODk6NzQiIG5hbWU9ImNob2ljZSIgeHNpOnR5cGU9Ik9wZXJhbmRSZWYiLz4KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPGFzVHlwZVNwZWNpZmllciBsb2NhdG9yPSI4OTo3OS04OTo4NiIgbmFtZT0iZmhpcjpBZ2UiIHhzaTp0eXBlPSJOYW1lZFR5cGVTcGVjaWZpZXIiLz4KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPC9vcGVyYW5kPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8L29wZXJhbmQ+CiAgICAgICAgICAgICAgICAgICAgICAgICAgIDwvb3BlcmFuZD4KICAgICAgICAgICAgICAgICAgICAgICAgICAgPG9wZXJhbmQgbG9jYXRvcj0iODk6OTEtODk6OTYiIHZhbHVlPSIxIiB1bml0PSJ5ZWFyIiB4c2k6dHlwZT0iUXVhbnRpdHkiLz4KICAgICAgICAgICAgICAgICAgICAgICAgPC9oaWdoPgogICAgICAgICAgICAgICAgICAgICA8L3NvdXJjZT4KICAgICAgICAgICAgICAgICAgPC9oaWdoQ2xvc2VkRXhwcmVzc2lvbj4KICAgICAgICAgICAgICAgPC90aGVuPgogICAgICAgICAgICA8L2Nhc2VJdGVtPgogICAgICAgICAgICA8Y2FzZUl0ZW0gbG9jYXRvcj0iOTA6My05MjoxMDYiPgogICAgICAgICAgICAgICA8d2hlbiBsb2NhdG9yPSI5MDo4LTkwOjI3IiB4c2k6dHlwZT0iSXMiPgogICAgICAgICAgICAgICAgICA8b3BlcmFuZCBsb2NhdG9yPSI5MDo4LTkwOjEzIiBuYW1lPSJjaG9pY2UiIHhzaTp0eXBlPSJPcGVyYW5kUmVmIi8+CiAgICAgICAgICAgICAgICAgIDxpc1R5cGVTcGVjaWZpZXIgbG9jYXRvcj0iOTA6MTgtOTA6MjciIG5hbWU9ImZoaXI6UmFuZ2UiIHhzaTp0eXBlPSJOYW1lZFR5cGVTcGVjaWZpZXIiLz4KICAgICAgICAgICAgICAgPC93aGVuPgogICAgICAgICAgICAgICA8dGhlbiB4c2k6dHlwZT0iSW50ZXJ2YWwiPgogICAgICAgICAgICAgICAgICA8bG93IHhzaTp0eXBlPSJUb0RhdGVUaW1lIj4KICAgICAgICAgICAgICAgICAgICAgPG9wZXJhbmQgcGF0aD0ibG93IiB4c2k6dHlwZT0iUHJvcGVydHkiPgogICAgICAgICAgICAgICAgICAgICAgICA8c291cmNlIGxvY2F0b3I9IjkxOjUtOTI6MTA2IiBsb3dDbG9zZWQ9InRydWUiIGhpZ2hDbG9zZWQ9ImZhbHNlIiB4c2k6dHlwZT0iSW50ZXJ2YWwiPgogICAgICAgICAgICAgICAgICAgICAgICAgICA8bG93IGxvY2F0b3I9IjkxOjE0LTkxOjEwMyIgeHNpOnR5cGU9IkFkZCI+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDxvcGVyYW5kIGxvY2F0b3I9IjkxOjE0LTkxOjUwIiBuYW1lPSJUb0RhdGUiIGxpYnJhcnlOYW1lPSJGSElSSGVscGVycyIgeHNpOnR5cGU9IkZ1bmN0aW9uUmVmIj4KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPG9wZXJhbmQgbG9jYXRvcj0iOTE6MzMtOTE6NDkiIHBhdGg9ImJpcnRoRGF0ZSIgeHNpOnR5cGU9IlByb3BlcnR5Ij4KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPHNvdXJjZSBsb2NhdG9yPSI5MTozMy05MTozOSIgbmFtZT0iUGF0aWVudCIgeHNpOnR5cGU9IkV4cHJlc3Npb25SZWYiLz4KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPC9vcGVyYW5kPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8L29wZXJhbmQ+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDxvcGVyYW5kIGxvY2F0b3I9IjkxOjU0LTkxOjEwMyIgbmFtZT0iVG9RdWFudGl0eSIgbGlicmFyeU5hbWU9IkZISVJIZWxwZXJzIiB4c2k6dHlwZT0iRnVuY3Rpb25SZWYiPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8b3BlcmFuZCBsb2NhdG9yPSI5MTo3Ny05MToxMDIiIHBhdGg9ImxvdyIgeHNpOnR5cGU9IlByb3BlcnR5Ij4KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPHNvdXJjZSBsb2NhdG9yPSI5MTo3Ny05MTo5OCIgc3RyaWN0PSJmYWxzZSIgeHNpOnR5cGU9IkFzIj4KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPG9wZXJhbmQgbG9jYXRvcj0iOTE6NzgtOTE6ODMiIG5hbWU9ImNob2ljZSIgeHNpOnR5cGU9Ik9wZXJhbmRSZWYiLz4KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPGFzVHlwZVNwZWNpZmllciBsb2NhdG9yPSI5MTo4OC05MTo5NyIgbmFtZT0iZmhpcjpSYW5nZSIgeHNpOnR5cGU9Ik5hbWVkVHlwZVNwZWNpZmllciIvPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8L3NvdXJjZT4KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPC9vcGVyYW5kPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8L29wZXJhbmQ+CiAgICAgICAgICAgICAgICAgICAgICAgICAgIDwvbG93PgogICAgICAgICAgICAgICAgICAgICAgICAgICA8aGlnaCBsb2NhdG9yPSI5Mjo2LTkyOjEwNSIgeHNpOnR5cGU9IkFkZCI+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDxvcGVyYW5kIGxvY2F0b3I9IjkyOjYtOTI6OTYiIHhzaTp0eXBlPSJBZGQiPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8b3BlcmFuZCBsb2NhdG9yPSI5Mjo2LTkyOjQyIiBuYW1lPSJUb0RhdGUiIGxpYnJhcnlOYW1lPSJGSElSSGVscGVycyIgeHNpOnR5cGU9IkZ1bmN0aW9uUmVmIj4KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPG9wZXJhbmQgbG9jYXRvcj0iOTI6MjUtOTI6NDEiIHBhdGg9ImJpcnRoRGF0ZSIgeHNpOnR5cGU9IlByb3BlcnR5Ij4KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPHNvdXJjZSBsb2NhdG9yPSI5MjoyNS05MjozMSIgbmFtZT0iUGF0aWVudCIgeHNpOnR5cGU9IkV4cHJlc3Npb25SZWYiLz4KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPC9vcGVyYW5kPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8L29wZXJhbmQ+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDxvcGVyYW5kIGxvY2F0b3I9IjkyOjQ2LTkyOjk2IiBuYW1lPSJUb1F1YW50aXR5IiBsaWJyYXJ5TmFtZT0iRkhJUkhlbHBlcnMiIHhzaTp0eXBlPSJGdW5jdGlvblJlZiI+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDxvcGVyYW5kIGxvY2F0b3I9IjkyOjY5LTkyOjk1IiBwYXRoPSJoaWdoIiB4c2k6dHlwZT0iUHJvcGVydHkiPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8c291cmNlIGxvY2F0b3I9IjkyOjY5LTkyOjkwIiBzdHJpY3Q9ImZhbHNlIiB4c2k6dHlwZT0iQXMiPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8b3BlcmFuZCBsb2NhdG9yPSI5Mjo3MC05Mjo3NSIgbmFtZT0iY2hvaWNlIiB4c2k6dHlwZT0iT3BlcmFuZFJlZiIvPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8YXNUeXBlU3BlY2lmaWVyIGxvY2F0b3I9IjkyOjgwLTkyOjg5IiBuYW1lPSJmaGlyOlJhbmdlIiB4c2k6dHlwZT0iTmFtZWRUeXBlU3BlY2lmaWVyIi8+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDwvc291cmNlPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8L29wZXJhbmQ+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDwvb3BlcmFuZD4KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPC9vcGVyYW5kPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8b3BlcmFuZCBsb2NhdG9yPSI5MjoxMDAtOTI6MTA1IiB2YWx1ZT0iMSIgdW5pdD0ieWVhciIgeHNpOnR5cGU9IlF1YW50aXR5Ii8+CiAgICAgICAgICAgICAgICAgICAgICAgICAgIDwvaGlnaD4KICAgICAgICAgICAgICAgICAgICAgICAgPC9zb3VyY2U+CiAgICAgICAgICAgICAgICAgICAgIDwvb3BlcmFuZD4KICAgICAgICAgICAgICAgICAgPC9sb3c+CiAgICAgICAgICAgICAgICAgIDxsb3dDbG9zZWRFeHByZXNzaW9uIHBhdGg9Imxvd0Nsb3NlZCIgeHNpOnR5cGU9IlByb3BlcnR5Ij4KICAgICAgICAgICAgICAgICAgICAgPHNvdXJjZSBsb2NhdG9yPSI5MTo1LTkyOjEwNiIgbG93Q2xvc2VkPSJ0cnVlIiBoaWdoQ2xvc2VkPSJmYWxzZSIgeHNpOnR5cGU9IkludGVydmFsIj4KICAgICAgICAgICAgICAgICAgICAgICAgPGxvdyBsb2NhdG9yPSI5MToxNC05MToxMDMiIHhzaTp0eXBlPSJBZGQiPgogICAgICAgICAgICAgICAgICAgICAgICAgICA8b3BlcmFuZCBsb2NhdG9yPSI5MToxNC05MTo1MCIgbmFtZT0iVG9EYXRlIiBsaWJyYXJ5TmFtZT0iRkhJUkhlbHBlcnMiIHhzaTp0eXBlPSJGdW5jdGlvblJlZiI+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDxvcGVyYW5kIGxvY2F0b3I9IjkxOjMzLTkxOjQ5IiBwYXRoPSJiaXJ0aERhdGUiIHhzaTp0eXBlPSJQcm9wZXJ0eSI+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDxzb3VyY2UgbG9jYXRvcj0iOTE6MzMtOTE6MzkiIG5hbWU9IlBhdGllbnQiIHhzaTp0eXBlPSJFeHByZXNzaW9uUmVmIi8+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDwvb3BlcmFuZD4KICAgICAgICAgICAgICAgICAgICAgICAgICAgPC9vcGVyYW5kPgogICAgICAgICAgICAgICAgICAgICAgICAgICA8b3BlcmFuZCBsb2NhdG9yPSI5MTo1NC05MToxMDMiIG5hbWU9IlRvUXVhbnRpdHkiIGxpYnJhcnlOYW1lPSJGSElSSGVscGVycyIgeHNpOnR5cGU9IkZ1bmN0aW9uUmVmIj4KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPG9wZXJhbmQgbG9jYXRvcj0iOTE6NzctOTE6MTAyIiBwYXRoPSJsb3ciIHhzaTp0eXBlPSJQcm9wZXJ0eSI+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDxzb3VyY2UgbG9jYXRvcj0iOTE6NzctOTE6OTgiIHN0cmljdD0iZmFsc2UiIHhzaTp0eXBlPSJBcyI+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDxvcGVyYW5kIGxvY2F0b3I9IjkxOjc4LTkxOjgzIiBuYW1lPSJjaG9pY2UiIHhzaTp0eXBlPSJPcGVyYW5kUmVmIi8+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDxhc1R5cGVTcGVjaWZpZXIgbG9jYXRvcj0iOTE6ODgtOTE6OTciIG5hbWU9ImZoaXI6UmFuZ2UiIHhzaTp0eXBlPSJOYW1lZFR5cGVTcGVjaWZpZXIiLz4KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPC9zb3VyY2U+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDwvb3BlcmFuZD4KICAgICAgICAgICAgICAgICAgICAgICAgICAgPC9vcGVyYW5kPgogICAgICAgICAgICAgICAgICAgICAgICA8L2xvdz4KICAgICAgICAgICAgICAgICAgICAgICAgPGhpZ2ggbG9jYXRvcj0iOTI6Ni05MjoxMDUiIHhzaTp0eXBlPSJBZGQiPgogICAgICAgICAgICAgICAgICAgICAgICAgICA8b3BlcmFuZCBsb2NhdG9yPSI5Mjo2LTkyOjk2IiB4c2k6dHlwZT0iQWRkIj4KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPG9wZXJhbmQgbG9jYXRvcj0iOTI6Ni05Mjo0MiIgbmFtZT0iVG9EYXRlIiBsaWJyYXJ5TmFtZT0iRkhJUkhlbHBlcnMiIHhzaTp0eXBlPSJGdW5jdGlvblJlZiI+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDxvcGVyYW5kIGxvY2F0b3I9IjkyOjI1LTkyOjQxIiBwYXRoPSJiaXJ0aERhdGUiIHhzaTp0eXBlPSJQcm9wZXJ0eSI+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDxzb3VyY2UgbG9jYXRvcj0iOTI6MjUtOTI6MzEiIG5hbWU9IlBhdGllbnQiIHhzaTp0eXBlPSJFeHByZXNzaW9uUmVmIi8+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDwvb3BlcmFuZD4KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPC9vcGVyYW5kPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8b3BlcmFuZCBsb2NhdG9yPSI5Mjo0Ni05Mjo5NiIgbmFtZT0iVG9RdWFudGl0eSIgbGlicmFyeU5hbWU9IkZISVJIZWxwZXJzIiB4c2k6dHlwZT0iRnVuY3Rpb25SZWYiPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8b3BlcmFuZCBsb2NhdG9yPSI5Mjo2OS05Mjo5NSIgcGF0aD0iaGlnaCIgeHNpOnR5cGU9IlByb3BlcnR5Ij4KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPHNvdXJjZSBsb2NhdG9yPSI5Mjo2OS05Mjo5MCIgc3RyaWN0PSJmYWxzZSIgeHNpOnR5cGU9IkFzIj4KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPG9wZXJhbmQgbG9jYXRvcj0iOTI6NzAtOTI6NzUiIG5hbWU9ImNob2ljZSIgeHNpOnR5cGU9Ik9wZXJhbmRSZWYiLz4KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPGFzVHlwZVNwZWNpZmllciBsb2NhdG9yPSI5Mjo4MC05Mjo4OSIgbmFtZT0iZmhpcjpSYW5nZSIgeHNpOnR5cGU9Ik5hbWVkVHlwZVNwZWNpZmllciIvPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8L3NvdXJjZT4KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPC9vcGVyYW5kPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8L29wZXJhbmQ+CiAgICAgICAgICAgICAgICAgICAgICAgICAgIDwvb3BlcmFuZD4KICAgICAgICAgICAgICAgICAgICAgICAgICAgPG9wZXJhbmQgbG9jYXRvcj0iOTI6MTAwLTkyOjEwNSIgdmFsdWU9IjEiIHVuaXQ9InllYXIiIHhzaTp0eXBlPSJRdWFudGl0eSIvPgogICAgICAgICAgICAgICAgICAgICAgICA8L2hpZ2g+CiAgICAgICAgICAgICAgICAgICAgIDwvc291cmNlPgogICAgICAgICAgICAgICAgICA8L2xvd0Nsb3NlZEV4cHJlc3Npb24+CiAgICAgICAgICAgICAgICAgIDxoaWdoIHhzaTp0eXBlPSJUb0RhdGVUaW1lIj4KICAgICAgICAgICAgICAgICAgICAgPG9wZXJhbmQgcGF0aD0iaGlnaCIgeHNpOnR5cGU9IlByb3BlcnR5Ij4KICAgICAgICAgICAgICAgICAgICAgICAgPHNvdXJjZSBsb2NhdG9yPSI5MTo1LTkyOjEwNiIgbG93Q2xvc2VkPSJ0cnVlIiBoaWdoQ2xvc2VkPSJmYWxzZSIgeHNpOnR5cGU9IkludGVydmFsIj4KICAgICAgICAgICAgICAgICAgICAgICAgICAgPGxvdyBsb2NhdG9yPSI5MToxNC05MToxMDMiIHhzaTp0eXBlPSJBZGQiPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8b3BlcmFuZCBsb2NhdG9yPSI5MToxNC05MTo1MCIgbmFtZT0iVG9EYXRlIiBsaWJyYXJ5TmFtZT0iRkhJUkhlbHBlcnMiIHhzaTp0eXBlPSJGdW5jdGlvblJlZiI+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDxvcGVyYW5kIGxvY2F0b3I9IjkxOjMzLTkxOjQ5IiBwYXRoPSJiaXJ0aERhdGUiIHhzaTp0eXBlPSJQcm9wZXJ0eSI+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDxzb3VyY2UgbG9jYXRvcj0iOTE6MzMtOTE6MzkiIG5hbWU9IlBhdGllbnQiIHhzaTp0eXBlPSJFeHByZXNzaW9uUmVmIi8+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDwvb3BlcmFuZD4KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPC9vcGVyYW5kPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8b3BlcmFuZCBsb2NhdG9yPSI5MTo1NC05MToxMDMiIG5hbWU9IlRvUXVhbnRpdHkiIGxpYnJhcnlOYW1lPSJGSElSSGVscGVycyIgeHNpOnR5cGU9IkZ1bmN0aW9uUmVmIj4KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPG9wZXJhbmQgbG9jYXRvcj0iOTE6NzctOTE6MTAyIiBwYXRoPSJsb3ciIHhzaTp0eXBlPSJQcm9wZXJ0eSI+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDxzb3VyY2UgbG9jYXRvcj0iOTE6NzctOTE6OTgiIHN0cmljdD0iZmFsc2UiIHhzaTp0eXBlPSJBcyI+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDxvcGVyYW5kIGxvY2F0b3I9IjkxOjc4LTkxOjgzIiBuYW1lPSJjaG9pY2UiIHhzaTp0eXBlPSJPcGVyYW5kUmVmIi8+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDxhc1R5cGVTcGVjaWZpZXIgbG9jYXRvcj0iOTE6ODgtOTE6OTciIG5hbWU9ImZoaXI6UmFuZ2UiIHhzaTp0eXBlPSJOYW1lZFR5cGVTcGVjaWZpZXIiLz4KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPC9zb3VyY2U+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDwvb3BlcmFuZD4KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPC9vcGVyYW5kPgogICAgICAgICAgICAgICAgICAgICAgICAgICA8L2xvdz4KICAgICAgICAgICAgICAgICAgICAgICAgICAgPGhpZ2ggbG9jYXRvcj0iOTI6Ni05MjoxMDUiIHhzaTp0eXBlPSJBZGQiPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8b3BlcmFuZCBsb2NhdG9yPSI5Mjo2LTkyOjk2IiB4c2k6dHlwZT0iQWRkIj4KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPG9wZXJhbmQgbG9jYXRvcj0iOTI6Ni05Mjo0MiIgbmFtZT0iVG9EYXRlIiBsaWJyYXJ5TmFtZT0iRkhJUkhlbHBlcnMiIHhzaTp0eXBlPSJGdW5jdGlvblJlZiI+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDxvcGVyYW5kIGxvY2F0b3I9IjkyOjI1LTkyOjQxIiBwYXRoPSJiaXJ0aERhdGUiIHhzaTp0eXBlPSJQcm9wZXJ0eSI+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDxzb3VyY2UgbG9jYXRvcj0iOTI6MjUtOTI6MzEiIG5hbWU9IlBhdGllbnQiIHhzaTp0eXBlPSJFeHByZXNzaW9uUmVmIi8+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDwvb3BlcmFuZD4KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPC9vcGVyYW5kPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8b3BlcmFuZCBsb2NhdG9yPSI5Mjo0Ni05Mjo5NiIgbmFtZT0iVG9RdWFudGl0eSIgbGlicmFyeU5hbWU9IkZISVJIZWxwZXJzIiB4c2k6dHlwZT0iRnVuY3Rpb25SZWYiPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8b3BlcmFuZCBsb2NhdG9yPSI5Mjo2OS05Mjo5NSIgcGF0aD0iaGlnaCIgeHNpOnR5cGU9IlByb3BlcnR5Ij4KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPHNvdXJjZSBsb2NhdG9yPSI5Mjo2OS05Mjo5MCIgc3RyaWN0PSJmYWxzZSIgeHNpOnR5cGU9IkFzIj4KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPG9wZXJhbmQgbG9jYXRvcj0iOTI6NzAtOTI6NzUiIG5hbWU9ImNob2ljZSIgeHNpOnR5cGU9Ik9wZXJhbmRSZWYiLz4KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPGFzVHlwZVNwZWNpZmllciBsb2NhdG9yPSI5Mjo4MC05Mjo4OSIgbmFtZT0iZmhpcjpSYW5nZSIgeHNpOnR5cGU9Ik5hbWVkVHlwZVNwZWNpZmllciIvPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8L3NvdXJjZT4KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPC9vcGVyYW5kPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8L29wZXJhbmQ+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDwvb3BlcmFuZD4KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPG9wZXJhbmQgbG9jYXRvcj0iOTI6MTAwLTkyOjEwNSIgdmFsdWU9IjEiIHVuaXQ9InllYXIiIHhzaTp0eXBlPSJRdWFudGl0eSIvPgogICAgICAgICAgICAgICAgICAgICAgICAgICA8L2hpZ2g+CiAgICAgICAgICAgICAgICAgICAgICAgIDwvc291cmNlPgogICAgICAgICAgICAgICAgICAgICA8L29wZXJhbmQ+CiAgICAgICAgICAgICAgICAgIDwvaGlnaD4KICAgICAgICAgICAgICAgICAgPGhpZ2hDbG9zZWRFeHByZXNzaW9uIHBhdGg9ImhpZ2hDbG9zZWQiIHhzaTp0eXBlPSJQcm9wZXJ0eSI+CiAgICAgICAgICAgICAgICAgICAgIDxzb3VyY2UgbG9jYXRvcj0iOTE6NS05MjoxMDYiIGxvd0Nsb3NlZD0idHJ1ZSIgaGlnaENsb3NlZD0iZmFsc2UiIHhzaTp0eXBlPSJJbnRlcnZhbCI+CiAgICAgICAgICAgICAgICAgICAgICAgIDxsb3cgbG9jYXRvcj0iOTE6MTQtOTE6MTAzIiB4c2k6dHlwZT0iQWRkIj4KICAgICAgICAgICAgICAgICAgICAgICAgICAgPG9wZXJhbmQgbG9jYXRvcj0iOTE6MTQtOTE6NTAiIG5hbWU9IlRvRGF0ZSIgbGlicmFyeU5hbWU9IkZISVJIZWxwZXJzIiB4c2k6dHlwZT0iRnVuY3Rpb25SZWYiPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8b3BlcmFuZCBsb2NhdG9yPSI5MTozMy05MTo0OSIgcGF0aD0iYmlydGhEYXRlIiB4c2k6dHlwZT0iUHJvcGVydHkiPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8c291cmNlIGxvY2F0b3I9IjkxOjMzLTkxOjM5IiBuYW1lPSJQYXRpZW50IiB4c2k6dHlwZT0iRXhwcmVzc2lvblJlZiIvPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8L29wZXJhbmQ+CiAgICAgICAgICAgICAgICAgICAgICAgICAgIDwvb3BlcmFuZD4KICAgICAgICAgICAgICAgICAgICAgICAgICAgPG9wZXJhbmQgbG9jYXRvcj0iOTE6NTQtOTE6MTAzIiBuYW1lPSJUb1F1YW50aXR5IiBsaWJyYXJ5TmFtZT0iRkhJUkhlbHBlcnMiIHhzaTp0eXBlPSJGdW5jdGlvblJlZiI+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDxvcGVyYW5kIGxvY2F0b3I9IjkxOjc3LTkxOjEwMiIgcGF0aD0ibG93IiB4c2k6dHlwZT0iUHJvcGVydHkiPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8c291cmNlIGxvY2F0b3I9IjkxOjc3LTkxOjk4IiBzdHJpY3Q9ImZhbHNlIiB4c2k6dHlwZT0iQXMiPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8b3BlcmFuZCBsb2NhdG9yPSI5MTo3OC05MTo4MyIgbmFtZT0iY2hvaWNlIiB4c2k6dHlwZT0iT3BlcmFuZFJlZiIvPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8YXNUeXBlU3BlY2lmaWVyIGxvY2F0b3I9IjkxOjg4LTkxOjk3IiBuYW1lPSJmaGlyOlJhbmdlIiB4c2k6dHlwZT0iTmFtZWRUeXBlU3BlY2lmaWVyIi8+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDwvc291cmNlPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8L29wZXJhbmQ+CiAgICAgICAgICAgICAgICAgICAgICAgICAgIDwvb3BlcmFuZD4KICAgICAgICAgICAgICAgICAgICAgICAgPC9sb3c+CiAgICAgICAgICAgICAgICAgICAgICAgIDxoaWdoIGxvY2F0b3I9IjkyOjYtOTI6MTA1IiB4c2k6dHlwZT0iQWRkIj4KICAgICAgICAgICAgICAgICAgICAgICAgICAgPG9wZXJhbmQgbG9jYXRvcj0iOTI6Ni05Mjo5NiIgeHNpOnR5cGU9IkFkZCI+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDxvcGVyYW5kIGxvY2F0b3I9IjkyOjYtOTI6NDIiIG5hbWU9IlRvRGF0ZSIgbGlicmFyeU5hbWU9IkZISVJIZWxwZXJzIiB4c2k6dHlwZT0iRnVuY3Rpb25SZWYiPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8b3BlcmFuZCBsb2NhdG9yPSI5MjoyNS05Mjo0MSIgcGF0aD0iYmlydGhEYXRlIiB4c2k6dHlwZT0iUHJvcGVydHkiPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8c291cmNlIGxvY2F0b3I9IjkyOjI1LTkyOjMxIiBuYW1lPSJQYXRpZW50IiB4c2k6dHlwZT0iRXhwcmVzc2lvblJlZiIvPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8L29wZXJhbmQ+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDwvb3BlcmFuZD4KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPG9wZXJhbmQgbG9jYXRvcj0iOTI6NDYtOTI6OTYiIG5hbWU9IlRvUXVhbnRpdHkiIGxpYnJhcnlOYW1lPSJGSElSSGVscGVycyIgeHNpOnR5cGU9IkZ1bmN0aW9uUmVmIj4KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPG9wZXJhbmQgbG9jYXRvcj0iOTI6NjktOTI6OTUiIHBhdGg9ImhpZ2giIHhzaTp0eXBlPSJQcm9wZXJ0eSI+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDxzb3VyY2UgbG9jYXRvcj0iOTI6NjktOTI6OTAiIHN0cmljdD0iZmFsc2UiIHhzaTp0eXBlPSJBcyI+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDxvcGVyYW5kIGxvY2F0b3I9IjkyOjcwLTkyOjc1IiBuYW1lPSJjaG9pY2UiIHhzaTp0eXBlPSJPcGVyYW5kUmVmIi8+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDxhc1R5cGVTcGVjaWZpZXIgbG9jYXRvcj0iOTI6ODAtOTI6ODkiIG5hbWU9ImZoaXI6UmFuZ2UiIHhzaTp0eXBlPSJOYW1lZFR5cGVTcGVjaWZpZXIiLz4KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPC9zb3VyY2U+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDwvb3BlcmFuZD4KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPC9vcGVyYW5kPgogICAgICAgICAgICAgICAgICAgICAgICAgICA8L29wZXJhbmQ+CiAgICAgICAgICAgICAgICAgICAgICAgICAgIDxvcGVyYW5kIGxvY2F0b3I9IjkyOjEwMC05MjoxMDUiIHZhbHVlPSIxIiB1bml0PSJ5ZWFyIiB4c2k6dHlwZT0iUXVhbnRpdHkiLz4KICAgICAgICAgICAgICAgICAgICAgICAgPC9oaWdoPgogICAgICAgICAgICAgICAgICAgICA8L3NvdXJjZT4KICAgICAgICAgICAgICAgICAgPC9oaWdoQ2xvc2VkRXhwcmVzc2lvbj4KICAgICAgICAgICAgICAgPC90aGVuPgogICAgICAgICAgICA8L2Nhc2VJdGVtPgogICAgICAgICAgICA8Y2FzZUl0ZW0gbG9jYXRvcj0iOTM6NS05NDoxMTQiPgogICAgICAgICAgICAgICA8d2hlbiBsb2NhdG9yPSI5MzoxMC05MzozMCIgeHNpOnR5cGU9IklzIj4KICAgICAgICAgICAgICAgICAgPG9wZXJhbmQgbG9jYXRvcj0iOTM6MTAtOTM6MTUiIG5hbWU9ImNob2ljZSIgeHNpOnR5cGU9Ik9wZXJhbmRSZWYiLz4KICAgICAgICAgICAgICAgICAgPGlzVHlwZVNwZWNpZmllciBsb2NhdG9yPSI5MzoyMC05MzozMCIgbmFtZT0iZmhpcjpUaW1pbmciIHhzaTp0eXBlPSJOYW1lZFR5cGVTcGVjaWZpZXIiLz4KICAgICAgICAgICAgICAgPC93aGVuPgogICAgICAgICAgICAgICA8dGhlbiBsb2NhdG9yPSI5NDo1LTk0OjExNCIgeHNpOnR5cGU9Ik1lc3NhZ2UiPgogICAgICAgICAgICAgICAgICA8c291cmNlIGxvY2F0b3I9Ijk0OjEzLTk0OjM4IiBzdHJpY3Q9ImZhbHNlIiB4c2k6dHlwZT0iQXMiPgogICAgICAgICAgICAgICAgICAgICA8b3BlcmFuZCBsb2NhdG9yPSI5NDoxMy05NDoxNiIgeHNpOnR5cGU9Ik51bGwiLz4KICAgICAgICAgICAgICAgICAgICAgPGFzVHlwZVNwZWNpZmllciBsb2NhdG9yPSI5NDoyMS05NDozOCIgeHNpOnR5cGU9IkludGVydmFsVHlwZVNwZWNpZmllciI+CiAgICAgICAgICAgICAgICAgICAgICAgIDxwb2ludFR5cGUgbG9jYXRvcj0iOTQ6MzAtOTQ6MzciIG5hbWU9InQ6RGF0ZVRpbWUiIHhzaTp0eXBlPSJOYW1lZFR5cGVTcGVjaWZpZXIiLz4KICAgICAgICAgICAgICAgICAgICAgPC9hc1R5cGVTcGVjaWZpZXI+CiAgICAgICAgICAgICAgICAgIDwvc291cmNlPgogICAgICAgICAgICAgICAgICA8Y29uZGl0aW9uIGxvY2F0b3I9Ijk0OjQxLTk0OjQ0IiB2YWx1ZVR5cGU9InQ6Qm9vbGVhbiIgdmFsdWU9InRydWUiIHhzaTp0eXBlPSJMaXRlcmFsIi8+CiAgICAgICAgICAgICAgICAgIDxjb2RlIGxvY2F0b3I9Ijk0OjQ3LTk0OjQ5IiB2YWx1ZVR5cGU9InQ6U3RyaW5nIiB2YWx1ZT0iMSIgeHNpOnR5cGU9IkxpdGVyYWwiLz4KICAgICAgICAgICAgICAgICAgPHNldmVyaXR5IGxvY2F0b3I9Ijk0OjUyLTk0OjU4IiB2YWx1ZVR5cGU9InQ6U3RyaW5nIiB2YWx1ZT0iRXJyb3IiIHhzaTp0eXBlPSJMaXRlcmFsIi8+CiAgICAgICAgICAgICAgICAgIDxtZXNzYWdlIGxvY2F0b3I9Ijk0OjYxLTk0OjExMyIgdmFsdWVUeXBlPSJ0OlN0cmluZyIgdmFsdWU9IkNhbm5vdCBjb21wdXRlIGEgc2luZ2xlIGludGVydmFsIGZyb20gYSBUaW1pbmcgdHlwZSIgeHNpOnR5cGU9IkxpdGVyYWwiLz4KICAgICAgICAgICAgICAgPC90aGVuPgogICAgICAgICAgICA8L2Nhc2VJdGVtPgogICAgICAgICAgICA8Y2FzZUl0ZW0gbG9jYXRvcj0iOTU6NS05NjoxMTEiPgogICAgICAgICAgICAgICA8d2hlbiBsb2NhdG9yPSI5NToxMC05NTozMCIgeHNpOnR5cGU9IklzIj4KICAgICAgICAgICAgICAgICAgPG9wZXJhbmQgbG9jYXRvcj0iOTU6MTAtOTU6MTUiIG5hbWU9ImNob2ljZSIgeHNpOnR5cGU9Ik9wZXJhbmRSZWYiLz4KICAgICAgICAgICAgICAgICAgPGlzVHlwZVNwZWNpZmllciBsb2NhdG9yPSI5NToyMC05NTozMCIgbmFtZT0iZmhpcjpzdHJpbmciIHhzaTp0eXBlPSJOYW1lZFR5cGVTcGVjaWZpZXIiLz4KICAgICAgICAgICAgICAgPC93aGVuPgogICAgICAgICAgICAgICA8dGhlbiBsb2NhdG9yPSI5Njo3LTk2OjExMSIgeHNpOnR5cGU9Ik1lc3NhZ2UiPgogICAgICAgICAgICAgICAgICA8c291cmNlIGxvY2F0b3I9Ijk2OjE1LTk2OjQwIiBzdHJpY3Q9ImZhbHNlIiB4c2k6dHlwZT0iQXMiPgogICAgICAgICAgICAgICAgICAgICA8b3BlcmFuZCBsb2NhdG9yPSI5NjoxNS05NjoxOCIgeHNpOnR5cGU9Ik51bGwiLz4KICAgICAgICAgICAgICAgICAgICAgPGFzVHlwZVNwZWNpZmllciBsb2NhdG9yPSI5NjoyMy05Njo0MCIgeHNpOnR5cGU9IkludGVydmFsVHlwZVNwZWNpZmllciI+CiAgICAgICAgICAgICAgICAgICAgICAgIDxwb2ludFR5cGUgbG9jYXRvcj0iOTY6MzItOTY6MzkiIG5hbWU9InQ6RGF0ZVRpbWUiIHhzaTp0eXBlPSJOYW1lZFR5cGVTcGVjaWZpZXIiLz4KICAgICAgICAgICAgICAgICAgICAgPC9hc1R5cGVTcGVjaWZpZXI+CiAgICAgICAgICAgICAgICAgIDwvc291cmNlPgogICAgICAgICAgICAgICAgICA8Y29uZGl0aW9uIGxvY2F0b3I9Ijk2OjQzLTk2OjQ2IiB2YWx1ZVR5cGU9InQ6Qm9vbGVhbiIgdmFsdWU9InRydWUiIHhzaTp0eXBlPSJMaXRlcmFsIi8+CiAgICAgICAgICAgICAgICAgIDxjb2RlIGxvY2F0b3I9Ijk2OjQ5LTk2OjUxIiB2YWx1ZVR5cGU9InQ6U3RyaW5nIiB2YWx1ZT0iMSIgeHNpOnR5cGU9IkxpdGVyYWwiLz4KICAgICAgICAgICAgICAgICAgPHNldmVyaXR5IGxvY2F0b3I9Ijk2OjU0LTk2OjYwIiB2YWx1ZVR5cGU9InQ6U3RyaW5nIiB2YWx1ZT0iRXJyb3IiIHhzaTp0eXBlPSJMaXRlcmFsIi8+CiAgICAgICAgICAgICAgICAgIDxtZXNzYWdlIGxvY2F0b3I9Ijk2OjYzLTk2OjExMCIgdmFsdWVUeXBlPSJ0OlN0cmluZyIgdmFsdWU9IkNhbm5vdCBjb21wdXRlIGFuIGludGVydmFsIGZyb20gYSBTdHJpbmcgdmFsdWUiIHhzaTp0eXBlPSJMaXRlcmFsIi8+CiAgICAgICAgICAgICAgIDwvdGhlbj4KICAgICAgICAgICAgPC9jYXNlSXRlbT4KICAgICAgICAgICAgPGVsc2UgbG9jYXRvcj0iOTg6Ny05ODozMiIgc3RyaWN0PSJmYWxzZSIgeHNpOnR5cGU9IkFzIj4KICAgICAgICAgICAgICAgPG9wZXJhbmQgbG9jYXRvcj0iOTg6Ny05ODoxMCIgeHNpOnR5cGU9Ik51bGwiLz4KICAgICAgICAgICAgICAgPGFzVHlwZVNwZWNpZmllciBsb2NhdG9yPSI5ODoxNS05ODozMiIgeHNpOnR5cGU9IkludGVydmFsVHlwZVNwZWNpZmllciI+CiAgICAgICAgICAgICAgICAgIDxwb2ludFR5cGUgbG9jYXRvcj0iOTg6MjQtOTg6MzEiIG5hbWU9InQ6RGF0ZVRpbWUiIHhzaTp0eXBlPSJOYW1lZFR5cGVTcGVjaWZpZXIiLz4KICAgICAgICAgICAgICAgPC9hc1R5cGVTcGVjaWZpZXI+CiAgICAgICAgICAgIDwvZWxzZT4KICAgICAgICAgPC9leHByZXNzaW9uPgogICAgICAgICA8b3BlcmFuZCBuYW1lPSJjaG9pY2UiPgogICAgICAgICAgICA8b3BlcmFuZFR5cGVTcGVjaWZpZXIgbG9jYXRvcj0iNzk6MzUtNzk6MTMwIiB4c2k6dHlwZT0iQ2hvaWNlVHlwZVNwZWNpZmllciI+CiAgICAgICAgICAgICAgIDxjaG9pY2UgbG9jYXRvcj0iNzk6NDItNzk6NTQiIG5hbWU9ImZoaXI6ZGF0ZVRpbWUiIHhzaTp0eXBlPSJOYW1lZFR5cGVTcGVjaWZpZXIiLz4KICAgICAgICAgICAgICAgPGNob2ljZSBsb2NhdG9yPSI3OTo1Ny03OTo2NyIgbmFtZT0iZmhpcjpQZXJpb2QiIHhzaTp0eXBlPSJOYW1lZFR5cGVTcGVjaWZpZXIiLz4KICAgICAgICAgICAgICAgPGNob2ljZSBsb2NhdG9yPSI3OTo3MC03OTo4MCIgbmFtZT0iZmhpcjpUaW1pbmciIHhzaTp0eXBlPSJOYW1lZFR5cGVTcGVjaWZpZXIiLz4KICAgICAgICAgICAgICAgPGNob2ljZSBsb2NhdG9yPSI3OTo4My03OTo5NCIgbmFtZT0iZmhpcjppbnN0YW50IiB4c2k6dHlwZT0iTmFtZWRUeXBlU3BlY2lmaWVyIi8+CiAgICAgICAgICAgICAgIDxjaG9pY2UgbG9jYXRvcj0iNzk6OTctNzk6MTA3IiBuYW1lPSJmaGlyOnN0cmluZyIgeHNpOnR5cGU9Ik5hbWVkVHlwZVNwZWNpZmllciIvPgogICAgICAgICAgICAgICA8Y2hvaWNlIGxvY2F0b3I9Ijc5OjExMC03OToxMTciIG5hbWU9ImZoaXI6QWdlIiB4c2k6dHlwZT0iTmFtZWRUeXBlU3BlY2lmaWVyIi8+CiAgICAgICAgICAgICAgIDxjaG9pY2UgbG9jYXRvcj0iNzk6MTIwLTc5OjEyOSIgbmFtZT0iZmhpcjpSYW5nZSIgeHNpOnR5cGU9Ik5hbWVkVHlwZVNwZWNpZmllciIvPgogICAgICAgICAgICA8L29wZXJhbmRUeXBlU3BlY2lmaWVyPgogICAgICAgICA8L29wZXJhbmQ+CiAgICAgIDwvZGVmPgogICAgICA8ZGVmIGxvY2F0b3I9IjEwNzoxLTEwOToyNCIgbmFtZT0iVG9JbnRlcnZhbEZyb21MaXN0IiBjb250ZXh0PSJQYXRpZW50IiBhY2Nlc3NMZXZlbD0iUHVibGljIiB4c2k6dHlwZT0iRnVuY3Rpb25EZWYiPgogICAgICAgICA8YW5ub3RhdGlvbiB4c2k6dHlwZT0iYTpBbm5vdGF0aW9uIj4KICAgICAgICAgICAgPGE6dCBuYW1lPSJkZXNjcmlwdGlvbiIgdmFsdWU9IlJldHVybnMgYSBsaXN0IG9mIGludGVydmFscyByZXByZXNlbnRpbmcgdGhlIG5vcm1hbGl6ZWQgRXZlbnQgb3IgQm91bmRzIG9mIGEgVGltaW5nIHJlc291cmNlLiIvPgogICAgICAgICAgICA8YTp0IG5hbWU9ImNvbW1lbnQiIHZhbHVlPSJOT1RFOiBUaW1pbmcuQm91bmRzIGlzIHJlc3RyaWN0ZWQgdG8gYSBQZXJpb2QuIi8+CiAgICAgICAgIDwvYW5ub3RhdGlvbj4KICAgICAgICAgPGV4cHJlc3Npb24gbG9jYXRvcj0iMTA4OjMtMTA5OjI0IiB4c2k6dHlwZT0iUXVlcnkiPgogICAgICAgICAgICA8c291cmNlIGxvY2F0b3I9IjEwODozLTEwODo5IiBhbGlhcz0idiI+CiAgICAgICAgICAgICAgIDxleHByZXNzaW9uIGxvY2F0b3I9IjEwODozLTEwODo3IiBuYW1lPSJ2YWx1ZSIgeHNpOnR5cGU9Ik9wZXJhbmRSZWYiLz4KICAgICAgICAgICAgPC9zb3VyY2U+CiAgICAgICAgICAgIDxyZXR1cm4gbG9jYXRvcj0iMTA5OjUtMTA5OjI0Ij4KICAgICAgICAgICAgICAgPGV4cHJlc3Npb24gbG9jYXRvcj0iMTA5OjEyLTEwOToyNCIgbmFtZT0iVG9JbnRlcnZhbCIgeHNpOnR5cGU9IkZ1bmN0aW9uUmVmIj4KICAgICAgICAgICAgICAgICAgPG9wZXJhbmQgeHNpOnR5cGU9IkFzIj4KICAgICAgICAgICAgICAgICAgICAgPG9wZXJhbmQgbG9jYXRvcj0iMTA5OjIzIiBuYW1lPSJ2IiB4c2k6dHlwZT0iQWxpYXNSZWYiLz4KICAgICAgICAgICAgICAgICAgICAgPGFzVHlwZVNwZWNpZmllciB4c2k6dHlwZT0iQ2hvaWNlVHlwZVNwZWNpZmllciI+CiAgICAgICAgICAgICAgICAgICAgICAgIDxjaG9pY2UgbmFtZT0iZmhpcjpkYXRlVGltZSIgeHNpOnR5cGU9Ik5hbWVkVHlwZVNwZWNpZmllciIvPgogICAgICAgICAgICAgICAgICAgICAgICA8Y2hvaWNlIG5hbWU9ImZoaXI6UGVyaW9kIiB4c2k6dHlwZT0iTmFtZWRUeXBlU3BlY2lmaWVyIi8+CiAgICAgICAgICAgICAgICAgICAgICAgIDxjaG9pY2UgbmFtZT0iZmhpcjpUaW1pbmciIHhzaTp0eXBlPSJOYW1lZFR5cGVTcGVjaWZpZXIiLz4KICAgICAgICAgICAgICAgICAgICAgICAgPGNob2ljZSBuYW1lPSJmaGlyOmluc3RhbnQiIHhzaTp0eXBlPSJOYW1lZFR5cGVTcGVjaWZpZXIiLz4KICAgICAgICAgICAgICAgICAgICAgICAgPGNob2ljZSBuYW1lPSJmaGlyOnN0cmluZyIgeHNpOnR5cGU9Ik5hbWVkVHlwZVNwZWNpZmllciIvPgogICAgICAgICAgICAgICAgICAgICAgICA8Y2hvaWNlIG5hbWU9ImZoaXI6QWdlIiB4c2k6dHlwZT0iTmFtZWRUeXBlU3BlY2lmaWVyIi8+CiAgICAgICAgICAgICAgICAgICAgICAgIDxjaG9pY2UgbmFtZT0iZmhpcjpSYW5nZSIgeHNpOnR5cGU9Ik5hbWVkVHlwZVNwZWNpZmllciIvPgogICAgICAgICAgICAgICAgICAgICA8L2FzVHlwZVNwZWNpZmllcj4KICAgICAgICAgICAgICAgICAgPC9vcGVyYW5kPgogICAgICAgICAgICAgICA8L2V4cHJlc3Npb24+CiAgICAgICAgICAgIDwvcmV0dXJuPgogICAgICAgICA8L2V4cHJlc3Npb24+CiAgICAgICAgIDxvcGVyYW5kIG5hbWU9InZhbHVlIj4KICAgICAgICAgICAgPG9wZXJhbmRUeXBlU3BlY2lmaWVyIGxvY2F0b3I9IjEwNzo0Mi0xMDc6NjAiIHhzaTp0eXBlPSJMaXN0VHlwZVNwZWNpZmllciI+CiAgICAgICAgICAgICAgIDxlbGVtZW50VHlwZSBsb2NhdG9yPSIxMDc6NDctMTA3OjU5IiBuYW1lPSJmaGlyOmRhdGVUaW1lIiB4c2k6dHlwZT0iTmFtZWRUeXBlU3BlY2lmaWVyIi8+CiAgICAgICAgICAgIDwvb3BlcmFuZFR5cGVTcGVjaWZpZXI+CiAgICAgICAgIDwvb3BlcmFuZD4KICAgICAgPC9kZWY+CiAgICAgIDxkZWYgbG9jYXRvcj0iMTExOjEtMTEyOjQyIiBuYW1lPSJOdWxsVG9FbXB0eUludGVydmFsIiBjb250ZXh0PSJQYXRpZW50IiBhY2Nlc3NMZXZlbD0iUHVibGljIiB4c2k6dHlwZT0iRnVuY3Rpb25EZWYiPgogICAgICAgICA8ZXhwcmVzc2lvbiBsb2NhdG9yPSIxMTI6My0xMTI6NDIiIHhzaTp0eXBlPSJJZiI+CiAgICAgICAgICAgIDxjb25kaXRpb24gbG9jYXRvcj0iMTEyOjYtMTEyOjIyIiB4c2k6dHlwZT0iTm90Ij4KICAgICAgICAgICAgICAgPG9wZXJhbmQgbG9jYXRvcj0iMTEyOjYtMTEyOjIyIiB4c2k6dHlwZT0iSXNOdWxsIj4KICAgICAgICAgICAgICAgICAgPG9wZXJhbmQgbG9jYXRvcj0iMTEyOjYtMTEyOjEwIiBuYW1lPSJ2YWx1ZSIgeHNpOnR5cGU9Ik9wZXJhbmRSZWYiLz4KICAgICAgICAgICAgICAgPC9vcGVyYW5kPgogICAgICAgICAgICA8L2NvbmRpdGlvbj4KICAgICAgICAgICAgPHRoZW4gbG9jYXRvcj0iMTEyOjI5LTExMjozMyIgbmFtZT0idmFsdWUiIHhzaTp0eXBlPSJPcGVyYW5kUmVmIi8+CiAgICAgICAgICAgIDxlbHNlIGxvY2F0b3I9IjExMjo0MC0xMTI6NDIiIHhzaTp0eXBlPSJMaXN0Ii8+CiAgICAgICAgIDwvZXhwcmVzc2lvbj4KICAgICAgICAgPG9wZXJhbmQgbmFtZT0idmFsdWUiPgogICAgICAgICAgICA8b3BlcmFuZFR5cGVTcGVjaWZpZXIgbG9jYXRvcj0iMTExOjQzLTExMTo2NiIgeHNpOnR5cGU9Ikxpc3RUeXBlU3BlY2lmaWVyIj4KICAgICAgICAgICAgICAgPGVsZW1lbnRUeXBlIGxvY2F0b3I9IjExMTo0OC0xMTE6NjUiIHhzaTp0eXBlPSJJbnRlcnZhbFR5cGVTcGVjaWZpZXIiPgogICAgICAgICAgICAgICAgICA8cG9pbnRUeXBlIGxvY2F0b3I9IjExMTo1Ny0xMTE6NjQiIG5hbWU9InQ6RGF0ZVRpbWUiIHhzaTp0eXBlPSJOYW1lZFR5cGVTcGVjaWZpZXIiLz4KICAgICAgICAgICAgICAgPC9lbGVtZW50VHlwZT4KICAgICAgICAgICAgPC9vcGVyYW5kVHlwZVNwZWNpZmllcj4KICAgICAgICAgPC9vcGVyYW5kPgogICAgICA8L2RlZj4KICAgICAgPGRlZiBsb2NhdG9yPSIxMTQ6MS0xMTg6NDciIG5hbWU9IlRvVGltaW5nSW50ZXJ2YWwiIGNvbnRleHQ9IlBhdGllbnQiIGFjY2Vzc0xldmVsPSJQdWJsaWMiIHhzaTp0eXBlPSJGdW5jdGlvbkRlZiI+CiAgICAgICAgIDxleHByZXNzaW9uIGxvY2F0b3I9IjExNTozLTExODo0NyIgeHNpOnR5cGU9IkV4Y2VwdCI+CiAgICAgICAgICAgIDxvcGVyYW5kIGxvY2F0b3I9IjExNTozLTExODozMSIgeHNpOnR5cGU9IlF1ZXJ5Ij4KICAgICAgICAgICAgICAgPHNvdXJjZSBhbGlhcz0iWCI+CiAgICAgICAgICAgICAgICAgIDxleHByZXNzaW9uIGxvY2F0b3I9IjExNToxMS0xMTg6MyIgeHNpOnR5cGU9IlVuaW9uIj4KICAgICAgICAgICAgICAgICAgICAgPG9wZXJhbmQgbG9jYXRvcj0iMTE2OjUtMTE2OjU3IiBuYW1lPSJOdWxsVG9FbXB0eUludGVydmFsIiB4c2k6dHlwZT0iRnVuY3Rpb25SZWYiPgogICAgICAgICAgICAgICAgICAgICAgICA8b3BlcmFuZCBsb2NhdG9yPSIxMTY6MjUtMTE2OjU2IiBuYW1lPSJUb0ludGVydmFsRnJvbUxpc3QiIHhzaTp0eXBlPSJGdW5jdGlvblJlZiI+CiAgICAgICAgICAgICAgICAgICAgICAgICAgIDxvcGVyYW5kIGxvY2F0b3I9IjExNjo0NC0xMTY6NTUiIHBhdGg9ImV2ZW50IiB4c2k6dHlwZT0iUHJvcGVydHkiPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8c291cmNlIGxvY2F0b3I9IjExNjo0NC0xMTY6NDkiIG5hbWU9InRpbWluZyIgeHNpOnR5cGU9Ik9wZXJhbmRSZWYiLz4KICAgICAgICAgICAgICAgICAgICAgICAgICAgPC9vcGVyYW5kPgogICAgICAgICAgICAgICAgICAgICAgICA8L29wZXJhbmQ+CiAgICAgICAgICAgICAgICAgICAgIDwvb3BlcmFuZD4KICAgICAgICAgICAgICAgICAgICAgPG9wZXJhbmQgbG9jYXRvcj0iMTE3OjExLTExNzo4MCIgbmFtZT0iTnVsbFRvRW1wdHlJbnRlcnZhbCIgeHNpOnR5cGU9IkZ1bmN0aW9uUmVmIj4KICAgICAgICAgICAgICAgICAgICAgICAgPG9wZXJhbmQgbG9jYXRvcj0iMTE3OjMxLTExNzo3OSIgeHNpOnR5cGU9Ikxpc3QiPgogICAgICAgICAgICAgICAgICAgICAgICAgICA8ZWxlbWVudCBsb2NhdG9yPSIxMTc6MzItMTE3Ojc4IiBuYW1lPSJUb0ludGVydmFsIiB4c2k6dHlwZT0iRnVuY3Rpb25SZWYiPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8b3BlcmFuZCB4c2k6dHlwZT0iQXMiPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8b3BlcmFuZCBsb2NhdG9yPSIxMTc6NDMtMTE3Ojc3IiBzdHJpY3Q9ImZhbHNlIiB4c2k6dHlwZT0iQXMiPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8b3BlcmFuZCBsb2NhdG9yPSIxMTc6NDMtMTE3OjYyIiBwYXRoPSJib3VuZHMiIHhzaTp0eXBlPSJQcm9wZXJ0eSI+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDxzb3VyY2UgbG9jYXRvcj0iMTE3OjQzLTExNzo1NSIgcGF0aD0icmVwZWF0IiB4c2k6dHlwZT0iUHJvcGVydHkiPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8c291cmNlIGxvY2F0b3I9IjExNzo0My0xMTc6NDgiIG5hbWU9InRpbWluZyIgeHNpOnR5cGU9Ik9wZXJhbmRSZWYiLz4KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPC9zb3VyY2U+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDwvb3BlcmFuZD4KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPGFzVHlwZVNwZWNpZmllciBsb2NhdG9yPSIxMTc6NjctMTE3Ojc3IiBuYW1lPSJmaGlyOlBlcmlvZCIgeHNpOnR5cGU9Ik5hbWVkVHlwZVNwZWNpZmllciIvPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8L29wZXJhbmQ+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDxhc1R5cGVTcGVjaWZpZXIgeHNpOnR5cGU9IkNob2ljZVR5cGVTcGVjaWZpZXIiPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8Y2hvaWNlIG5hbWU9ImZoaXI6ZGF0ZVRpbWUiIHhzaTp0eXBlPSJOYW1lZFR5cGVTcGVjaWZpZXIiLz4KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPGNob2ljZSBuYW1lPSJmaGlyOlBlcmlvZCIgeHNpOnR5cGU9Ik5hbWVkVHlwZVNwZWNpZmllciIvPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8Y2hvaWNlIG5hbWU9ImZoaXI6VGltaW5nIiB4c2k6dHlwZT0iTmFtZWRUeXBlU3BlY2lmaWVyIi8+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDxjaG9pY2UgbmFtZT0iZmhpcjppbnN0YW50IiB4c2k6dHlwZT0iTmFtZWRUeXBlU3BlY2lmaWVyIi8+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDxjaG9pY2UgbmFtZT0iZmhpcjpzdHJpbmciIHhzaTp0eXBlPSJOYW1lZFR5cGVTcGVjaWZpZXIiLz4KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPGNob2ljZSBuYW1lPSJmaGlyOkFnZSIgeHNpOnR5cGU9Ik5hbWVkVHlwZVNwZWNpZmllciIvPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8Y2hvaWNlIG5hbWU9ImZoaXI6UmFuZ2UiIHhzaTp0eXBlPSJOYW1lZFR5cGVTcGVjaWZpZXIiLz4KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPC9hc1R5cGVTcGVjaWZpZXI+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDwvb3BlcmFuZD4KICAgICAgICAgICAgICAgICAgICAgICAgICAgPC9lbGVtZW50PgogICAgICAgICAgICAgICAgICAgICAgICA8L29wZXJhbmQ+CiAgICAgICAgICAgICAgICAgICAgIDwvb3BlcmFuZD4KICAgICAgICAgICAgICAgICAgPC9leHByZXNzaW9uPgogICAgICAgICAgICAgICA8L3NvdXJjZT4KICAgICAgICAgICAgICAgPHJldHVybiBkaXN0aW5jdD0iZmFsc2UiPgogICAgICAgICAgICAgICAgICA8ZXhwcmVzc2lvbiB4c2k6dHlwZT0iQXMiPgogICAgICAgICAgICAgICAgICAgICA8b3BlcmFuZCBuYW1lPSJYIiB4c2k6dHlwZT0iQWxpYXNSZWYiLz4KICAgICAgICAgICAgICAgICAgICAgPGFzVHlwZVNwZWNpZmllciB4c2k6dHlwZT0iSW50ZXJ2YWxUeXBlU3BlY2lmaWVyIj4KICAgICAgICAgICAgICAgICAgICAgICAgPHBvaW50VHlwZSBuYW1lPSJ0OkRhdGVUaW1lIiB4c2k6dHlwZT0iTmFtZWRUeXBlU3BlY2lmaWVyIi8+CiAgICAgICAgICAgICAgICAgICAgIDwvYXNUeXBlU3BlY2lmaWVyPgogICAgICAgICAgICAgICAgICA8L2V4cHJlc3Npb24+CiAgICAgICAgICAgICAgIDwvcmV0dXJuPgogICAgICAgICAgICA8L29wZXJhbmQ+CiAgICAgICAgICAgIDxvcGVyYW5kIHhzaTp0eXBlPSJRdWVyeSI+CiAgICAgICAgICAgICAgIDxzb3VyY2UgYWxpYXM9IlgiPgogICAgICAgICAgICAgICAgICA8ZXhwcmVzc2lvbiBsb2NhdG9yPSIxMTg6NDAtMTE4OjQ3IiB4c2k6dHlwZT0iTGlzdCI+CiAgICAgICAgICAgICAgICAgICAgIDxlbGVtZW50IGxvY2F0b3I9IjExODo0Mi0xMTg6NDUiIHhzaTp0eXBlPSJOdWxsIi8+CiAgICAgICAgICAgICAgICAgIDwvZXhwcmVzc2lvbj4KICAgICAgICAgICAgICAgPC9zb3VyY2U+CiAgICAgICAgICAgICAgIDxyZXR1cm4gZGlzdGluY3Q9ImZhbHNlIj4KICAgICAgICAgICAgICAgICAgPGV4cHJlc3Npb24geHNpOnR5cGU9IkFzIj4KICAgICAgICAgICAgICAgICAgICAgPG9wZXJhbmQgbmFtZT0iWCIgeHNpOnR5cGU9IkFsaWFzUmVmIi8+CiAgICAgICAgICAgICAgICAgICAgIDxhc1R5cGVTcGVjaWZpZXIgeHNpOnR5cGU9IkludGVydmFsVHlwZVNwZWNpZmllciI+CiAgICAgICAgICAgICAgICAgICAgICAgIDxwb2ludFR5cGUgbmFtZT0idDpEYXRlVGltZSIgeHNpOnR5cGU9Ik5hbWVkVHlwZVNwZWNpZmllciIvPgogICAgICAgICAgICAgICAgICAgICA8L2FzVHlwZVNwZWNpZmllcj4KICAgICAgICAgICAgICAgICAgPC9leHByZXNzaW9uPgogICAgICAgICAgICAgICA8L3JldHVybj4KICAgICAgICAgICAgPC9vcGVyYW5kPgogICAgICAgICA8L2V4cHJlc3Npb24+CiAgICAgICAgIDxvcGVyYW5kIG5hbWU9InRpbWluZyI+CiAgICAgICAgICAgIDxvcGVyYW5kVHlwZVNwZWNpZmllciBsb2NhdG9yPSIxMTQ6NDEtMTE0OjUxIiBuYW1lPSJmaGlyOlRpbWluZyIgeHNpOnR5cGU9Ik5hbWVkVHlwZVNwZWNpZmllciIvPgogICAgICAgICA8L29wZXJhbmQ+CiAgICAgIDwvZGVmPgogICAgICA8ZGVmIGxvY2F0b3I9IjEzNToxLTE1NDoxMCIgbmFtZT0iVG9BYmF0ZW1lbnRJbnRlcnZhbCIgY29udGV4dD0iUGF0aWVudCIgYWNjZXNzTGV2ZWw9IlB1YmxpYyIgeHNpOnR5cGU9IkZ1bmN0aW9uRGVmIj4KICAgICAgICAgPGFubm90YXRpb24geHNpOnR5cGU9ImE6QW5ub3RhdGlvbiI+CiAgICAgICAgICAgIDxhOnQgbmFtZT0iZGVzY3JpcHRpb24iIHZhbHVlPSJyZXR1cm5zIHRoZSBleGlzdGluZyBwb3NzaWJsZSB0aW1lc3RhbXAgZm9yIG9ic2VydmF0aW9uIHJlc291cmNlcywgcHJlZmVycmluZyBlZmZlY3RpdmUgb3ZlciBpc3N1ZWQuIi8+CiAgICAgICAgICAgIDxhOnQgbmFtZT0iY29tbWVudCIgdmFsdWU9Ik5PVEU6IG5laXRoZXIgZWZmZWN0aXZlIG5vciBpc3N1ZWQgYXJlIHJlcXVpcmVkIGZpZWxkcyBpbiBRSS1Db3JlJiN4YTtOT1RFOiBGdW5jdGlvbiBvdmVybG9hZHMgaW5jb25zaXN0ZW50bHkgZXJyIGluIENRTCAxLjUsIHRoaXMgaXMgY29tbWVudGVkIHVudGlsIGEgZml4IGlzIGZvdW5kJiN4YTtkZWZpbmUgZnVuY3Rpb24gVG9JbnRlcnZhbChvYnNlcnZhdGlvbiBGSElSLk9ic2VydmF0aW9uKTomI3hhOyAgb2JzZXJ2YXRpb24gbyYjeGE7ICAgIHJldHVybiBDb2FsZXNjZShUb0ludGVydmFsKG8uZWZmZWN0aXZlKSwgVG9JbnRlcnZhbChvLmlzc3VlZCkpIi8+CiAgICAgICAgICAgIDxhOnQgbmFtZT0iZGVzY3JpcHRpb24iIHZhbHVlPSJSZXR1cm5zIGFuIGludGVydmFsIHJlcHJlc2VudGluZyB0aGUgbm9ybWFsaXplZCBBYmF0ZW1lbnQgb2YgYSBnaXZlbiBDb25kaXRpb24gcmVzb3VyY2UuIi8+CiAgICAgICAgICAgIDxhOnQgbmFtZT0iY29tbWVudCIgdmFsdWU9Ik5PVEU6IER1ZSB0byB0aGUgY29tcGxleGl0eSBvZiBkZXRlcm1pbmluZyBhbiBpbnRlcnZhbCBmcm9tIGEgU3RyaW5nLCB0aGlzIGZ1bmN0aW9uIHdpbGwgdGhyb3cmI3hhO2EgcnVuLXRpbWUgZXhjZXB0aW9uIGlmIHVzZWQgd2l0aCBhIENvbmRpdGlvbiBpbnN0YW5jZSB0aGF0IGhhcyBhIFN0cmluZyBhcyB0aGUgYWJhdGVtZW50IHZhbHVlLiIvPgogICAgICAgICA8L2Fubm90YXRpb24+CiAgICAgICAgIDxleHByZXNzaW9uIGxvY2F0b3I9IjEzNjozLTE1NDoxMCIgeHNpOnR5cGU9IklmIj4KICAgICAgICAgICAgPGNvbmRpdGlvbiBsb2NhdG9yPSIxMzY6Ni0xMzg6NDQiIHhzaTp0eXBlPSJBbmQiPgogICAgICAgICAgICAgICA8b3BlcmFuZCBsb2NhdG9yPSIxMzY6Ni0xMzY6NjQiIHhzaTp0eXBlPSJBbmQiPgogICAgICAgICAgICAgICAgICA8b3BlcmFuZCBsb2NhdG9yPSIxMzY6Ni0xMzY6MzIiIHhzaTp0eXBlPSJOb3QiPgogICAgICAgICAgICAgICAgICAgICA8b3BlcmFuZCBsb2NhdG9yPSIxMzY6Ni0xMzY6MzIiIHhzaTp0eXBlPSJJc051bGwiPgogICAgICAgICAgICAgICAgICAgICAgICA8b3BlcmFuZCBsb2NhdG9yPSIxMzY6Ni0xMzY6MjAiIHBhdGg9Im9uc2V0IiB4c2k6dHlwZT0iUHJvcGVydHkiPgogICAgICAgICAgICAgICAgICAgICAgICAgICA8c291cmNlIGxvY2F0b3I9IjEzNjo2LTEzNjoxNCIgbmFtZT0iY29uZGl0aW9uIiB4c2k6dHlwZT0iT3BlcmFuZFJlZiIvPgogICAgICAgICAgICAgICAgICAgICAgICA8L29wZXJhbmQ+CiAgICAgICAgICAgICAgICAgICAgIDwvb3BlcmFuZD4KICAgICAgICAgICAgICAgICAgPC9vcGVyYW5kPgogICAgICAgICAgICAgICAgICA8b3BlcmFuZCBsb2NhdG9yPSIxMzY6MzgtMTM2OjY0IiB4c2k6dHlwZT0iSXNOdWxsIj4KICAgICAgICAgICAgICAgICAgICAgPG9wZXJhbmQgbG9jYXRvcj0iMTM2OjM4LTEzNjo1NiIgcGF0aD0iYWJhdGVtZW50IiB4c2k6dHlwZT0iUHJvcGVydHkiPgogICAgICAgICAgICAgICAgICAgICAgICA8c291cmNlIGxvY2F0b3I9IjEzNjozOC0xMzY6NDYiIG5hbWU9ImNvbmRpdGlvbiIgeHNpOnR5cGU9Ik9wZXJhbmRSZWYiLz4KICAgICAgICAgICAgICAgICAgICAgPC9vcGVyYW5kPgogICAgICAgICAgICAgICAgICA8L29wZXJhbmQ+CiAgICAgICAgICAgICAgIDwvb3BlcmFuZD4KICAgICAgICAgICAgICAgPG9wZXJhbmQgbG9jYXRvcj0iMTM2OjcwLTEzODo0NCIgeHNpOnR5cGU9Ik9yIj4KICAgICAgICAgICAgICAgICAgPG9wZXJhbmQgbG9jYXRvcj0iMTM2OjcxLTEzNzo0NiIgeHNpOnR5cGU9Ik9yIj4KICAgICAgICAgICAgICAgICAgICAgPG9wZXJhbmQgbG9jYXRvcj0iMTM2OjcxLTEzNjoxMDUiIHhzaTp0eXBlPSJFcXVpdmFsZW50Ij4KICAgICAgICAgICAgICAgICAgICAgICAgPG9wZXJhbmQgbmFtZT0iVG9Db25jZXB0IiBsaWJyYXJ5TmFtZT0iRkhJUkhlbHBlcnMiIHhzaTp0eXBlPSJGdW5jdGlvblJlZiI+CiAgICAgICAgICAgICAgICAgICAgICAgICAgIDxvcGVyYW5kIGxvY2F0b3I9IjEzNjo3MS0xMzY6OTQiIHBhdGg9ImNsaW5pY2FsU3RhdHVzIiB4c2k6dHlwZT0iUHJvcGVydHkiPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8c291cmNlIGxvY2F0b3I9IjEzNjo3MS0xMzY6NzkiIG5hbWU9ImNvbmRpdGlvbiIgeHNpOnR5cGU9Ik9wZXJhbmRSZWYiLz4KICAgICAgICAgICAgICAgICAgICAgICAgICAgPC9vcGVyYW5kPgogICAgICAgICAgICAgICAgICAgICAgICA8L29wZXJhbmQ+CiAgICAgICAgICAgICAgICAgICAgICAgIDxvcGVyYW5kIHhzaTp0eXBlPSJUb0NvbmNlcHQiPgogICAgICAgICAgICAgICAgICAgICAgICAgICA8b3BlcmFuZCBsb2NhdG9yPSIxMzY6OTgtMTM2OjEwNSIgbmFtZT0iYWN0aXZlIiB4c2k6dHlwZT0iQ29kZVJlZiIvPgogICAgICAgICAgICAgICAgICAgICAgICA8L29wZXJhbmQ+CiAgICAgICAgICAgICAgICAgICAgIDwvb3BlcmFuZD4KICAgICAgICAgICAgICAgICAgICAgPG9wZXJhbmQgbG9jYXRvcj0iMTM3OjgtMTM3OjQ2IiB4c2k6dHlwZT0iRXF1aXZhbGVudCI+CiAgICAgICAgICAgICAgICAgICAgICAgIDxvcGVyYW5kIG5hbWU9IlRvQ29uY2VwdCIgbGlicmFyeU5hbWU9IkZISVJIZWxwZXJzIiB4c2k6dHlwZT0iRnVuY3Rpb25SZWYiPgogICAgICAgICAgICAgICAgICAgICAgICAgICA8b3BlcmFuZCBsb2NhdG9yPSIxMzc6OC0xMzc6MzEiIHBhdGg9ImNsaW5pY2FsU3RhdHVzIiB4c2k6dHlwZT0iUHJvcGVydHkiPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8c291cmNlIGxvY2F0b3I9IjEzNzo4LTEzNzoxNiIgbmFtZT0iY29uZGl0aW9uIiB4c2k6dHlwZT0iT3BlcmFuZFJlZiIvPgogICAgICAgICAgICAgICAgICAgICAgICAgICA8L29wZXJhbmQ+CiAgICAgICAgICAgICAgICAgICAgICAgIDwvb3BlcmFuZD4KICAgICAgICAgICAgICAgICAgICAgICAgPG9wZXJhbmQgeHNpOnR5cGU9IlRvQ29uY2VwdCI+CiAgICAgICAgICAgICAgICAgICAgICAgICAgIDxvcGVyYW5kIGxvY2F0b3I9IjEzNzozNS0xMzc6NDYiIG5hbWU9InJlY3VycmVuY2UiIHhzaTp0eXBlPSJDb2RlUmVmIi8+CiAgICAgICAgICAgICAgICAgICAgICAgIDwvb3BlcmFuZD4KICAgICAgICAgICAgICAgICAgICAgPC9vcGVyYW5kPgogICAgICAgICAgICAgICAgICA8L29wZXJhbmQ+CiAgICAgICAgICAgICAgICAgIDxvcGVyYW5kIGxvY2F0b3I9IjEzODo4LTEzODo0MyIgeHNpOnR5cGU9IkVxdWl2YWxlbnQiPgogICAgICAgICAgICAgICAgICAgICA8b3BlcmFuZCBuYW1lPSJUb0NvbmNlcHQiIGxpYnJhcnlOYW1lPSJGSElSSGVscGVycyIgeHNpOnR5cGU9IkZ1bmN0aW9uUmVmIj4KICAgICAgICAgICAgICAgICAgICAgICAgPG9wZXJhbmQgbG9jYXRvcj0iMTM4OjgtMTM4OjMxIiBwYXRoPSJjbGluaWNhbFN0YXR1cyIgeHNpOnR5cGU9IlByb3BlcnR5Ij4KICAgICAgICAgICAgICAgICAgICAgICAgICAgPHNvdXJjZSBsb2NhdG9yPSIxMzg6OC0xMzg6MTYiIG5hbWU9ImNvbmRpdGlvbiIgeHNpOnR5cGU9Ik9wZXJhbmRSZWYiLz4KICAgICAgICAgICAgICAgICAgICAgICAgPC9vcGVyYW5kPgogICAgICAgICAgICAgICAgICAgICA8L29wZXJhbmQ+CiAgICAgICAgICAgICAgICAgICAgIDxvcGVyYW5kIHhzaTp0eXBlPSJUb0NvbmNlcHQiPgogICAgICAgICAgICAgICAgICAgICAgICA8b3BlcmFuZCBsb2NhdG9yPSIxMzg6MzUtMTM4OjQzIiBuYW1lPSJyZWxhcHNlIiB4c2k6dHlwZT0iQ29kZVJlZiIvPgogICAgICAgICAgICAgICAgICAgICA8L29wZXJhbmQ+CiAgICAgICAgICAgICAgICAgIDwvb3BlcmFuZD4KICAgICAgICAgICAgICAgPC9vcGVyYW5kPgogICAgICAgICAgICA8L2NvbmRpdGlvbj4KICAgICAgICAgICAgPHRoZW4gbG9jYXRvcj0iMTM5OjUtMTM5OjI2IiBsb3dDbG9zZWQ9InRydWUiIGhpZ2hDbG9zZWQ9InRydWUiIHhzaTp0eXBlPSJJbnRlcnZhbCI+CiAgICAgICAgICAgICAgIDxsb3cgbG9jYXRvcj0iMTM5OjE0LTEzOToxOCIgeHNpOnR5cGU9Ik5vdyIvPgogICAgICAgICAgICAgICA8aGlnaCBsb2NhdG9yPSIxMzk6MjEtMTM5OjI1IiB4c2k6dHlwZT0iTm93Ii8+CiAgICAgICAgICAgIDwvdGhlbj4KICAgICAgICAgICAgPGVsc2UgbG9jYXRvcj0iMTQwOjgtMTU0OjEwIiB4c2k6dHlwZT0iSWYiPgogICAgICAgICAgICAgICA8Y29uZGl0aW9uIGxvY2F0b3I9IjE0MDoxMS0xNDA6NDYiIHhzaTp0eXBlPSJJcyI+CiAgICAgICAgICAgICAgICAgIDxvcGVyYW5kIGxvY2F0b3I9IjE0MDoxMS0xNDA6MjkiIHBhdGg9ImFiYXRlbWVudCIgeHNpOnR5cGU9IlByb3BlcnR5Ij4KICAgICAgICAgICAgICAgICAgICAgPHNvdXJjZSBsb2NhdG9yPSIxNDA6MTEtMTQwOjE5IiBuYW1lPSJjb25kaXRpb24iIHhzaTp0eXBlPSJPcGVyYW5kUmVmIi8+CiAgICAgICAgICAgICAgICAgIDwvb3BlcmFuZD4KICAgICAgICAgICAgICAgICAgPGlzVHlwZVNwZWNpZmllciBsb2NhdG9yPSIxNDA6MzQtMTQwOjQ2IiBuYW1lPSJmaGlyOmRhdGVUaW1lIiB4c2k6dHlwZT0iTmFtZWRUeXBlU3BlY2lmaWVyIi8+CiAgICAgICAgICAgICAgIDwvY29uZGl0aW9uPgogICAgICAgICAgICAgICA8dGhlbiBsb2NhdG9yPSIxNDE6NC0xNDE6MTM1IiBsb3dDbG9zZWQ9InRydWUiIGhpZ2hDbG9zZWQ9InRydWUiIHhzaTp0eXBlPSJJbnRlcnZhbCI+CiAgICAgICAgICAgICAgICAgIDxsb3cgbG9jYXRvcj0iMTQxOjEzLTE0MTo3MiIgbmFtZT0iVG9EYXRlVGltZSIgbGlicmFyeU5hbWU9IkZISVJIZWxwZXJzIiB4c2k6dHlwZT0iRnVuY3Rpb25SZWYiPgogICAgICAgICAgICAgICAgICAgICA8b3BlcmFuZCBsb2NhdG9yPSIxNDE6MzYtMTQxOjcxIiBzdHJpY3Q9ImZhbHNlIiB4c2k6dHlwZT0iQXMiPgogICAgICAgICAgICAgICAgICAgICAgICA8b3BlcmFuZCBsb2NhdG9yPSIxNDE6MzYtMTQxOjU0IiBwYXRoPSJhYmF0ZW1lbnQiIHhzaTp0eXBlPSJQcm9wZXJ0eSI+CiAgICAgICAgICAgICAgICAgICAgICAgICAgIDxzb3VyY2UgbG9jYXRvcj0iMTQxOjM2LTE0MTo0NCIgbmFtZT0iY29uZGl0aW9uIiB4c2k6dHlwZT0iT3BlcmFuZFJlZiIvPgogICAgICAgICAgICAgICAgICAgICAgICA8L29wZXJhbmQ+CiAgICAgICAgICAgICAgICAgICAgICAgIDxhc1R5cGVTcGVjaWZpZXIgbG9jYXRvcj0iMTQxOjU5LTE0MTo3MSIgbmFtZT0iZmhpcjpkYXRlVGltZSIgeHNpOnR5cGU9Ik5hbWVkVHlwZVNwZWNpZmllciIvPgogICAgICAgICAgICAgICAgICAgICA8L29wZXJhbmQ+CiAgICAgICAgICAgICAgICAgIDwvbG93PgogICAgICAgICAgICAgICAgICA8aGlnaCBsb2NhdG9yPSIxNDE6NzUtMTQxOjEzNCIgbmFtZT0iVG9EYXRlVGltZSIgbGlicmFyeU5hbWU9IkZISVJIZWxwZXJzIiB4c2k6dHlwZT0iRnVuY3Rpb25SZWYiPgogICAgICAgICAgICAgICAgICAgICA8b3BlcmFuZCBsb2NhdG9yPSIxNDE6OTgtMTQxOjEzMyIgc3RyaWN0PSJmYWxzZSIgeHNpOnR5cGU9IkFzIj4KICAgICAgICAgICAgICAgICAgICAgICAgPG9wZXJhbmQgbG9jYXRvcj0iMTQxOjk4LTE0MToxMTYiIHBhdGg9ImFiYXRlbWVudCIgeHNpOnR5cGU9IlByb3BlcnR5Ij4KICAgICAgICAgICAgICAgICAgICAgICAgICAgPHNvdXJjZSBsb2NhdG9yPSIxNDE6OTgtMTQxOjEwNiIgbmFtZT0iY29uZGl0aW9uIiB4c2k6dHlwZT0iT3BlcmFuZFJlZiIvPgogICAgICAgICAgICAgICAgICAgICAgICA8L29wZXJhbmQ+CiAgICAgICAgICAgICAgICAgICAgICAgIDxhc1R5cGVTcGVjaWZpZXIgbG9jYXRvcj0iMTQxOjEyMS0xNDE6MTMzIiBuYW1lPSJmaGlyOmRhdGVUaW1lIiB4c2k6dHlwZT0iTmFtZWRUeXBlU3BlY2lmaWVyIi8+CiAgICAgICAgICAgICAgICAgICAgIDwvb3BlcmFuZD4KICAgICAgICAgICAgICAgICAgPC9oaWdoPgogICAgICAgICAgICAgICA8L3RoZW4+CiAgICAgICAgICAgICAgIDxlbHNlIGxvY2F0b3I9IjE0Mjo3LTE1NDoxMCIgeHNpOnR5cGU9IklmIj4KICAgICAgICAgICAgICAgICAgPGNvbmRpdGlvbiBsb2NhdG9yPSIxNDI6MTAtMTQyOjQzIiB4c2k6dHlwZT0iSXMiPgogICAgICAgICAgICAgICAgICAgICA8b3BlcmFuZCBsb2NhdG9yPSIxNDI6MTAtMTQyOjI4IiBwYXRoPSJhYmF0ZW1lbnQiIHhzaTp0eXBlPSJQcm9wZXJ0eSI+CiAgICAgICAgICAgICAgICAgICAgICAgIDxzb3VyY2UgbG9jYXRvcj0iMTQyOjEwLTE0MjoxOCIgbmFtZT0iY29uZGl0aW9uIiB4c2k6dHlwZT0iT3BlcmFuZFJlZiIvPgogICAgICAgICAgICAgICAgICAgICA8L29wZXJhbmQ+CiAgICAgICAgICAgICAgICAgICAgIDxpc1R5cGVTcGVjaWZpZXIgbG9jYXRvcj0iMTQyOjMzLTE0Mjo0MyIgbmFtZT0iZmhpcjpQZXJpb2QiIHhzaTp0eXBlPSJOYW1lZFR5cGVTcGVjaWZpZXIiLz4KICAgICAgICAgICAgICAgICAgPC9jb25kaXRpb24+CiAgICAgICAgICAgICAgICAgIDx0aGVuIGxvY2F0b3I9IjE0Mzo0LTE0Mzo2MSIgbmFtZT0iVG9JbnRlcnZhbCIgbGlicmFyeU5hbWU9IkZISVJIZWxwZXJzIiB4c2k6dHlwZT0iRnVuY3Rpb25SZWYiPgogICAgICAgICAgICAgICAgICAgICA8b3BlcmFuZCBsb2NhdG9yPSIxNDM6MjctMTQzOjYwIiBzdHJpY3Q9ImZhbHNlIiB4c2k6dHlwZT0iQXMiPgogICAgICAgICAgICAgICAgICAgICAgICA8b3BlcmFuZCBsb2NhdG9yPSIxNDM6MjctMTQzOjQ1IiBwYXRoPSJhYmF0ZW1lbnQiIHhzaTp0eXBlPSJQcm9wZXJ0eSI+CiAgICAgICAgICAgICAgICAgICAgICAgICAgIDxzb3VyY2UgbG9jYXRvcj0iMTQzOjI3LTE0MzozNSIgbmFtZT0iY29uZGl0aW9uIiB4c2k6dHlwZT0iT3BlcmFuZFJlZiIvPgogICAgICAgICAgICAgICAgICAgICAgICA8L29wZXJhbmQ+CiAgICAgICAgICAgICAgICAgICAgICAgIDxhc1R5cGVTcGVjaWZpZXIgbG9jYXRvcj0iMTQzOjUwLTE0Mzo2MCIgbmFtZT0iZmhpcjpQZXJpb2QiIHhzaTp0eXBlPSJOYW1lZFR5cGVTcGVjaWZpZXIiLz4KICAgICAgICAgICAgICAgICAgICAgPC9vcGVyYW5kPgogICAgICAgICAgICAgICAgICA8L3RoZW4+CiAgICAgICAgICAgICAgICAgIDxlbHNlIGxvY2F0b3I9IjE0NDo3LTE1NDoxMCIgeHNpOnR5cGU9IklmIj4KICAgICAgICAgICAgICAgICAgICAgPGNvbmRpdGlvbiBsb2NhdG9yPSIxNDQ6MTAtMTQ0OjQzIiB4c2k6dHlwZT0iSXMiPgogICAgICAgICAgICAgICAgICAgICAgICA8b3BlcmFuZCBsb2NhdG9yPSIxNDQ6MTAtMTQ0OjI4IiBwYXRoPSJhYmF0ZW1lbnQiIHhzaTp0eXBlPSJQcm9wZXJ0eSI+CiAgICAgICAgICAgICAgICAgICAgICAgICAgIDxzb3VyY2UgbG9jYXRvcj0iMTQ0OjEwLTE0NDoxOCIgbmFtZT0iY29uZGl0aW9uIiB4c2k6dHlwZT0iT3BlcmFuZFJlZiIvPgogICAgICAgICAgICAgICAgICAgICAgICA8L29wZXJhbmQ+CiAgICAgICAgICAgICAgICAgICAgICAgIDxpc1R5cGVTcGVjaWZpZXIgbG9jYXRvcj0iMTQ0OjMzLTE0NDo0MyIgbmFtZT0iZmhpcjpzdHJpbmciIHhzaTp0eXBlPSJOYW1lZFR5cGVTcGVjaWZpZXIiLz4KICAgICAgICAgICAgICAgICAgICAgPC9jb25kaXRpb24+CiAgICAgICAgICAgICAgICAgICAgIDx0aGVuIGxvY2F0b3I9IjE0NTo1LTE0NToxMDkiIHhzaTp0eXBlPSJNZXNzYWdlIj4KICAgICAgICAgICAgICAgICAgICAgICAgPHNvdXJjZSBsb2NhdG9yPSIxNDU6MTMtMTQ1OjM4IiBzdHJpY3Q9ImZhbHNlIiB4c2k6dHlwZT0iQXMiPgogICAgICAgICAgICAgICAgICAgICAgICAgICA8b3BlcmFuZCBsb2NhdG9yPSIxNDU6MTMtMTQ1OjE2IiB4c2k6dHlwZT0iTnVsbCIvPgogICAgICAgICAgICAgICAgICAgICAgICAgICA8YXNUeXBlU3BlY2lmaWVyIGxvY2F0b3I9IjE0NToyMS0xNDU6MzgiIHhzaTp0eXBlPSJJbnRlcnZhbFR5cGVTcGVjaWZpZXIiPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8cG9pbnRUeXBlIGxvY2F0b3I9IjE0NTozMC0xNDU6MzciIG5hbWU9InQ6RGF0ZVRpbWUiIHhzaTp0eXBlPSJOYW1lZFR5cGVTcGVjaWZpZXIiLz4KICAgICAgICAgICAgICAgICAgICAgICAgICAgPC9hc1R5cGVTcGVjaWZpZXI+CiAgICAgICAgICAgICAgICAgICAgICAgIDwvc291cmNlPgogICAgICAgICAgICAgICAgICAgICAgICA8Y29uZGl0aW9uIGxvY2F0b3I9IjE0NTo0MS0xNDU6NDQiIHZhbHVlVHlwZT0idDpCb29sZWFuIiB2YWx1ZT0idHJ1ZSIgeHNpOnR5cGU9IkxpdGVyYWwiLz4KICAgICAgICAgICAgICAgICAgICAgICAgPGNvZGUgbG9jYXRvcj0iMTQ1OjQ3LTE0NTo0OSIgdmFsdWVUeXBlPSJ0OlN0cmluZyIgdmFsdWU9IjEiIHhzaTp0eXBlPSJMaXRlcmFsIi8+CiAgICAgICAgICAgICAgICAgICAgICAgIDxzZXZlcml0eSBsb2NhdG9yPSIxNDU6NTItMTQ1OjU4IiB2YWx1ZVR5cGU9InQ6U3RyaW5nIiB2YWx1ZT0iRXJyb3IiIHhzaTp0eXBlPSJMaXRlcmFsIi8+CiAgICAgICAgICAgICAgICAgICAgICAgIDxtZXNzYWdlIGxvY2F0b3I9IjE0NTo2MS0xNDU6MTA4IiB2YWx1ZVR5cGU9InQ6U3RyaW5nIiB2YWx1ZT0iQ2Fubm90IGNvbXB1dGUgYW4gaW50ZXJ2YWwgZnJvbSBhIFN0cmluZyB2YWx1ZSIgeHNpOnR5cGU9IkxpdGVyYWwiLz4KICAgICAgICAgICAgICAgICAgICAgPC90aGVuPgogICAgICAgICAgICAgICAgICAgICA8ZWxzZSBsb2NhdG9yPSIxNDY6Ny0xNTQ6MTAiIHhzaTp0eXBlPSJJZiI+CiAgICAgICAgICAgICAgICAgICAgICAgIDxjb25kaXRpb24gbG9jYXRvcj0iMTQ2OjEwLTE0Njo0MCIgeHNpOnR5cGU9IklzIj4KICAgICAgICAgICAgICAgICAgICAgICAgICAgPG9wZXJhbmQgbG9jYXRvcj0iMTQ2OjEwLTE0NjoyOCIgcGF0aD0iYWJhdGVtZW50IiB4c2k6dHlwZT0iUHJvcGVydHkiPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8c291cmNlIGxvY2F0b3I9IjE0NjoxMC0xNDY6MTgiIG5hbWU9ImNvbmRpdGlvbiIgeHNpOnR5cGU9Ik9wZXJhbmRSZWYiLz4KICAgICAgICAgICAgICAgICAgICAgICAgICAgPC9vcGVyYW5kPgogICAgICAgICAgICAgICAgICAgICAgICAgICA8aXNUeXBlU3BlY2lmaWVyIGxvY2F0b3I9IjE0NjozMy0xNDY6NDAiIG5hbWU9ImZoaXI6QWdlIiB4c2k6dHlwZT0iTmFtZWRUeXBlU3BlY2lmaWVyIi8+CiAgICAgICAgICAgICAgICAgICAgICAgIDwvY29uZGl0aW9uPgogICAgICAgICAgICAgICAgICAgICAgICA8dGhlbiB4c2k6dHlwZT0iSW50ZXJ2YWwiPgogICAgICAgICAgICAgICAgICAgICAgICAgICA8bG93IHhzaTp0eXBlPSJUb0RhdGVUaW1lIj4KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPG9wZXJhbmQgcGF0aD0ibG93IiB4c2k6dHlwZT0iUHJvcGVydHkiPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8c291cmNlIGxvY2F0b3I9IjE0NzozLTE0ODoxMDgiIGxvd0Nsb3NlZD0idHJ1ZSIgaGlnaENsb3NlZD0iZmFsc2UiIHhzaTp0eXBlPSJJbnRlcnZhbCI+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDxsb3cgbG9jYXRvcj0iMTQ3OjEyLTE0NzoxMDYiIHhzaTp0eXBlPSJBZGQiPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8b3BlcmFuZCBsb2NhdG9yPSIxNDc6MTItMTQ3OjQ4IiBuYW1lPSJUb0RhdGUiIGxpYnJhcnlOYW1lPSJGSElSSGVscGVycyIgeHNpOnR5cGU9IkZ1bmN0aW9uUmVmIj4KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPG9wZXJhbmQgbG9jYXRvcj0iMTQ3OjMxLTE0Nzo0NyIgcGF0aD0iYmlydGhEYXRlIiB4c2k6dHlwZT0iUHJvcGVydHkiPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8c291cmNlIGxvY2F0b3I9IjE0NzozMS0xNDc6MzciIG5hbWU9IlBhdGllbnQiIHhzaTp0eXBlPSJFeHByZXNzaW9uUmVmIi8+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDwvb3BlcmFuZD4KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPC9vcGVyYW5kPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8b3BlcmFuZCBsb2NhdG9yPSIxNDc6NTItMTQ3OjEwNiIgbmFtZT0iVG9RdWFudGl0eSIgbGlicmFyeU5hbWU9IkZISVJIZWxwZXJzIiB4c2k6dHlwZT0iRnVuY3Rpb25SZWYiPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8b3BlcmFuZCBsb2NhdG9yPSIxNDc6NzUtMTQ3OjEwNSIgc3RyaWN0PSJmYWxzZSIgeHNpOnR5cGU9IkFzIj4KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPG9wZXJhbmQgbG9jYXRvcj0iMTQ3Ojc1LTE0Nzo5MyIgcGF0aD0iYWJhdGVtZW50IiB4c2k6dHlwZT0iUHJvcGVydHkiPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8c291cmNlIGxvY2F0b3I9IjE0Nzo3NS0xNDc6ODMiIG5hbWU9ImNvbmRpdGlvbiIgeHNpOnR5cGU9Ik9wZXJhbmRSZWYiLz4KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPC9vcGVyYW5kPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8YXNUeXBlU3BlY2lmaWVyIGxvY2F0b3I9IjE0Nzo5OC0xNDc6MTA1IiBuYW1lPSJmaGlyOkFnZSIgeHNpOnR5cGU9Ik5hbWVkVHlwZVNwZWNpZmllciIvPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8L29wZXJhbmQ+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDwvb3BlcmFuZD4KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPC9sb3c+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDxoaWdoIGxvY2F0b3I9IjE0ODo0LTE0ODoxMDciIHhzaTp0eXBlPSJBZGQiPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8b3BlcmFuZCBsb2NhdG9yPSIxNDg6NC0xNDg6OTgiIHhzaTp0eXBlPSJBZGQiPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8b3BlcmFuZCBsb2NhdG9yPSIxNDg6NC0xNDg6NDAiIG5hbWU9IlRvRGF0ZSIgbGlicmFyeU5hbWU9IkZISVJIZWxwZXJzIiB4c2k6dHlwZT0iRnVuY3Rpb25SZWYiPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8b3BlcmFuZCBsb2NhdG9yPSIxNDg6MjMtMTQ4OjM5IiBwYXRoPSJiaXJ0aERhdGUiIHhzaTp0eXBlPSJQcm9wZXJ0eSI+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDxzb3VyY2UgbG9jYXRvcj0iMTQ4OjIzLTE0ODoyOSIgbmFtZT0iUGF0aWVudCIgeHNpOnR5cGU9IkV4cHJlc3Npb25SZWYiLz4KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPC9vcGVyYW5kPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8L29wZXJhbmQ+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDxvcGVyYW5kIGxvY2F0b3I9IjE0ODo0NC0xNDg6OTgiIG5hbWU9IlRvUXVhbnRpdHkiIGxpYnJhcnlOYW1lPSJGSElSSGVscGVycyIgeHNpOnR5cGU9IkZ1bmN0aW9uUmVmIj4KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPG9wZXJhbmQgbG9jYXRvcj0iMTQ4OjY3LTE0ODo5NyIgc3RyaWN0PSJmYWxzZSIgeHNpOnR5cGU9IkFzIj4KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPG9wZXJhbmQgbG9jYXRvcj0iMTQ4OjY3LTE0ODo4NSIgcGF0aD0iYWJhdGVtZW50IiB4c2k6dHlwZT0iUHJvcGVydHkiPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8c291cmNlIGxvY2F0b3I9IjE0ODo2Ny0xNDg6NzUiIG5hbWU9ImNvbmRpdGlvbiIgeHNpOnR5cGU9Ik9wZXJhbmRSZWYiLz4KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPC9vcGVyYW5kPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8YXNUeXBlU3BlY2lmaWVyIGxvY2F0b3I9IjE0ODo5MC0xNDg6OTciIG5hbWU9ImZoaXI6QWdlIiB4c2k6dHlwZT0iTmFtZWRUeXBlU3BlY2lmaWVyIi8+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDwvb3BlcmFuZD4KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPC9vcGVyYW5kPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8L29wZXJhbmQ+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDxvcGVyYW5kIGxvY2F0b3I9IjE0ODoxMDItMTQ4OjEwNyIgdmFsdWU9IjEiIHVuaXQ9InllYXIiIHhzaTp0eXBlPSJRdWFudGl0eSIvPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8L2hpZ2g+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDwvc291cmNlPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8L29wZXJhbmQ+CiAgICAgICAgICAgICAgICAgICAgICAgICAgIDwvbG93PgogICAgICAgICAgICAgICAgICAgICAgICAgICA8bG93Q2xvc2VkRXhwcmVzc2lvbiBwYXRoPSJsb3dDbG9zZWQiIHhzaTp0eXBlPSJQcm9wZXJ0eSI+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDxzb3VyY2UgbG9jYXRvcj0iMTQ3OjMtMTQ4OjEwOCIgbG93Q2xvc2VkPSJ0cnVlIiBoaWdoQ2xvc2VkPSJmYWxzZSIgeHNpOnR5cGU9IkludGVydmFsIj4KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPGxvdyBsb2NhdG9yPSIxNDc6MTItMTQ3OjEwNiIgeHNpOnR5cGU9IkFkZCI+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDxvcGVyYW5kIGxvY2F0b3I9IjE0NzoxMi0xNDc6NDgiIG5hbWU9IlRvRGF0ZSIgbGlicmFyeU5hbWU9IkZISVJIZWxwZXJzIiB4c2k6dHlwZT0iRnVuY3Rpb25SZWYiPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8b3BlcmFuZCBsb2NhdG9yPSIxNDc6MzEtMTQ3OjQ3IiBwYXRoPSJiaXJ0aERhdGUiIHhzaTp0eXBlPSJQcm9wZXJ0eSI+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDxzb3VyY2UgbG9jYXRvcj0iMTQ3OjMxLTE0NzozNyIgbmFtZT0iUGF0aWVudCIgeHNpOnR5cGU9IkV4cHJlc3Npb25SZWYiLz4KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPC9vcGVyYW5kPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8L29wZXJhbmQ+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDxvcGVyYW5kIGxvY2F0b3I9IjE0Nzo1Mi0xNDc6MTA2IiBuYW1lPSJUb1F1YW50aXR5IiBsaWJyYXJ5TmFtZT0iRkhJUkhlbHBlcnMiIHhzaTp0eXBlPSJGdW5jdGlvblJlZiI+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDxvcGVyYW5kIGxvY2F0b3I9IjE0Nzo3NS0xNDc6MTA1IiBzdHJpY3Q9ImZhbHNlIiB4c2k6dHlwZT0iQXMiPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8b3BlcmFuZCBsb2NhdG9yPSIxNDc6NzUtMTQ3OjkzIiBwYXRoPSJhYmF0ZW1lbnQiIHhzaTp0eXBlPSJQcm9wZXJ0eSI+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDxzb3VyY2UgbG9jYXRvcj0iMTQ3Ojc1LTE0Nzo4MyIgbmFtZT0iY29uZGl0aW9uIiB4c2k6dHlwZT0iT3BlcmFuZFJlZiIvPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8L29wZXJhbmQ+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDxhc1R5cGVTcGVjaWZpZXIgbG9jYXRvcj0iMTQ3Ojk4LTE0NzoxMDUiIG5hbWU9ImZoaXI6QWdlIiB4c2k6dHlwZT0iTmFtZWRUeXBlU3BlY2lmaWVyIi8+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDwvb3BlcmFuZD4KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPC9vcGVyYW5kPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8L2xvdz4KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPGhpZ2ggbG9jYXRvcj0iMTQ4OjQtMTQ4OjEwNyIgeHNpOnR5cGU9IkFkZCI+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDxvcGVyYW5kIGxvY2F0b3I9IjE0ODo0LTE0ODo5OCIgeHNpOnR5cGU9IkFkZCI+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDxvcGVyYW5kIGxvY2F0b3I9IjE0ODo0LTE0ODo0MCIgbmFtZT0iVG9EYXRlIiBsaWJyYXJ5TmFtZT0iRkhJUkhlbHBlcnMiIHhzaTp0eXBlPSJGdW5jdGlvblJlZiI+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDxvcGVyYW5kIGxvY2F0b3I9IjE0ODoyMy0xNDg6MzkiIHBhdGg9ImJpcnRoRGF0ZSIgeHNpOnR5cGU9IlByb3BlcnR5Ij4KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPHNvdXJjZSBsb2NhdG9yPSIxNDg6MjMtMTQ4OjI5IiBuYW1lPSJQYXRpZW50IiB4c2k6dHlwZT0iRXhwcmVzc2lvblJlZiIvPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8L29wZXJhbmQ+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDwvb3BlcmFuZD4KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPG9wZXJhbmQgbG9jYXRvcj0iMTQ4OjQ0LTE0ODo5OCIgbmFtZT0iVG9RdWFudGl0eSIgbGlicmFyeU5hbWU9IkZISVJIZWxwZXJzIiB4c2k6dHlwZT0iRnVuY3Rpb25SZWYiPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8b3BlcmFuZCBsb2NhdG9yPSIxNDg6NjctMTQ4Ojk3IiBzdHJpY3Q9ImZhbHNlIiB4c2k6dHlwZT0iQXMiPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8b3BlcmFuZCBsb2NhdG9yPSIxNDg6NjctMTQ4Ojg1IiBwYXRoPSJhYmF0ZW1lbnQiIHhzaTp0eXBlPSJQcm9wZXJ0eSI+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDxzb3VyY2UgbG9jYXRvcj0iMTQ4OjY3LTE0ODo3NSIgbmFtZT0iY29uZGl0aW9uIiB4c2k6dHlwZT0iT3BlcmFuZFJlZiIvPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8L29wZXJhbmQ+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDxhc1R5cGVTcGVjaWZpZXIgbG9jYXRvcj0iMTQ4OjkwLTE0ODo5NyIgbmFtZT0iZmhpcjpBZ2UiIHhzaTp0eXBlPSJOYW1lZFR5cGVTcGVjaWZpZXIiLz4KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPC9vcGVyYW5kPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8L29wZXJhbmQ+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDwvb3BlcmFuZD4KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPG9wZXJhbmQgbG9jYXRvcj0iMTQ4OjEwMi0xNDg6MTA3IiB2YWx1ZT0iMSIgdW5pdD0ieWVhciIgeHNpOnR5cGU9IlF1YW50aXR5Ii8+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDwvaGlnaD4KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPC9zb3VyY2U+CiAgICAgICAgICAgICAgICAgICAgICAgICAgIDwvbG93Q2xvc2VkRXhwcmVzc2lvbj4KICAgICAgICAgICAgICAgICAgICAgICAgICAgPGhpZ2ggeHNpOnR5cGU9IlRvRGF0ZVRpbWUiPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8b3BlcmFuZCBwYXRoPSJoaWdoIiB4c2k6dHlwZT0iUHJvcGVydHkiPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8c291cmNlIGxvY2F0b3I9IjE0NzozLTE0ODoxMDgiIGxvd0Nsb3NlZD0idHJ1ZSIgaGlnaENsb3NlZD0iZmFsc2UiIHhzaTp0eXBlPSJJbnRlcnZhbCI+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDxsb3cgbG9jYXRvcj0iMTQ3OjEyLTE0NzoxMDYiIHhzaTp0eXBlPSJBZGQiPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8b3BlcmFuZCBsb2NhdG9yPSIxNDc6MTItMTQ3OjQ4IiBuYW1lPSJUb0RhdGUiIGxpYnJhcnlOYW1lPSJGSElSSGVscGVycyIgeHNpOnR5cGU9IkZ1bmN0aW9uUmVmIj4KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPG9wZXJhbmQgbG9jYXRvcj0iMTQ3OjMxLTE0Nzo0NyIgcGF0aD0iYmlydGhEYXRlIiB4c2k6dHlwZT0iUHJvcGVydHkiPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8c291cmNlIGxvY2F0b3I9IjE0NzozMS0xNDc6MzciIG5hbWU9IlBhdGllbnQiIHhzaTp0eXBlPSJFeHByZXNzaW9uUmVmIi8+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDwvb3BlcmFuZD4KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPC9vcGVyYW5kPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8b3BlcmFuZCBsb2NhdG9yPSIxNDc6NTItMTQ3OjEwNiIgbmFtZT0iVG9RdWFudGl0eSIgbGlicmFyeU5hbWU9IkZISVJIZWxwZXJzIiB4c2k6dHlwZT0iRnVuY3Rpb25SZWYiPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8b3BlcmFuZCBsb2NhdG9yPSIxNDc6NzUtMTQ3OjEwNSIgc3RyaWN0PSJmYWxzZSIgeHNpOnR5cGU9IkFzIj4KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPG9wZXJhbmQgbG9jYXRvcj0iMTQ3Ojc1LTE0Nzo5MyIgcGF0aD0iYWJhdGVtZW50IiB4c2k6dHlwZT0iUHJvcGVydHkiPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8c291cmNlIGxvY2F0b3I9IjE0Nzo3NS0xNDc6ODMiIG5hbWU9ImNvbmRpdGlvbiIgeHNpOnR5cGU9Ik9wZXJhbmRSZWYiLz4KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPC9vcGVyYW5kPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8YXNUeXBlU3BlY2lmaWVyIGxvY2F0b3I9IjE0Nzo5OC0xNDc6MTA1IiBuYW1lPSJmaGlyOkFnZSIgeHNpOnR5cGU9Ik5hbWVkVHlwZVNwZWNpZmllciIvPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8L29wZXJhbmQ+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDwvb3BlcmFuZD4KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPC9sb3c+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDxoaWdoIGxvY2F0b3I9IjE0ODo0LTE0ODoxMDciIHhzaTp0eXBlPSJBZGQiPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8b3BlcmFuZCBsb2NhdG9yPSIxNDg6NC0xNDg6OTgiIHhzaTp0eXBlPSJBZGQiPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8b3BlcmFuZCBsb2NhdG9yPSIxNDg6NC0xNDg6NDAiIG5hbWU9IlRvRGF0ZSIgbGlicmFyeU5hbWU9IkZISVJIZWxwZXJzIiB4c2k6dHlwZT0iRnVuY3Rpb25SZWYiPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8b3BlcmFuZCBsb2NhdG9yPSIxNDg6MjMtMTQ4OjM5IiBwYXRoPSJiaXJ0aERhdGUiIHhzaTp0eXBlPSJQcm9wZXJ0eSI+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDxzb3VyY2UgbG9jYXRvcj0iMTQ4OjIzLTE0ODoyOSIgbmFtZT0iUGF0aWVudCIgeHNpOnR5cGU9IkV4cHJlc3Npb25SZWYiLz4KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPC9vcGVyYW5kPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8L29wZXJhbmQ+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDxvcGVyYW5kIGxvY2F0b3I9IjE0ODo0NC0xNDg6OTgiIG5hbWU9IlRvUXVhbnRpdHkiIGxpYnJhcnlOYW1lPSJGSElSSGVscGVycyIgeHNpOnR5cGU9IkZ1bmN0aW9uUmVmIj4KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPG9wZXJhbmQgbG9jYXRvcj0iMTQ4OjY3LTE0ODo5NyIgc3RyaWN0PSJmYWxzZSIgeHNpOnR5cGU9IkFzIj4KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPG9wZXJhbmQgbG9jYXRvcj0iMTQ4OjY3LTE0ODo4NSIgcGF0aD0iYWJhdGVtZW50IiB4c2k6dHlwZT0iUHJvcGVydHkiPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8c291cmNlIGxvY2F0b3I9IjE0ODo2Ny0xNDg6NzUiIG5hbWU9ImNvbmRpdGlvbiIgeHNpOnR5cGU9Ik9wZXJhbmRSZWYiLz4KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPC9vcGVyYW5kPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8YXNUeXBlU3BlY2lmaWVyIGxvY2F0b3I9IjE0ODo5MC0xNDg6OTciIG5hbWU9ImZoaXI6QWdlIiB4c2k6dHlwZT0iTmFtZWRUeXBlU3BlY2lmaWVyIi8+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDwvb3BlcmFuZD4KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPC9vcGVyYW5kPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8L29wZXJhbmQ+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDxvcGVyYW5kIGxvY2F0b3I9IjE0ODoxMDItMTQ4OjEwNyIgdmFsdWU9IjEiIHVuaXQ9InllYXIiIHhzaTp0eXBlPSJRdWFudGl0eSIvPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8L2hpZ2g+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDwvc291cmNlPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8L29wZXJhbmQ+CiAgICAgICAgICAgICAgICAgICAgICAgICAgIDwvaGlnaD4KICAgICAgICAgICAgICAgICAgICAgICAgICAgPGhpZ2hDbG9zZWRFeHByZXNzaW9uIHBhdGg9ImhpZ2hDbG9zZWQiIHhzaTp0eXBlPSJQcm9wZXJ0eSI+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDxzb3VyY2UgbG9jYXRvcj0iMTQ3OjMtMTQ4OjEwOCIgbG93Q2xvc2VkPSJ0cnVlIiBoaWdoQ2xvc2VkPSJmYWxzZSIgeHNpOnR5cGU9IkludGVydmFsIj4KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPGxvdyBsb2NhdG9yPSIxNDc6MTItMTQ3OjEwNiIgeHNpOnR5cGU9IkFkZCI+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDxvcGVyYW5kIGxvY2F0b3I9IjE0NzoxMi0xNDc6NDgiIG5hbWU9IlRvRGF0ZSIgbGlicmFyeU5hbWU9IkZISVJIZWxwZXJzIiB4c2k6dHlwZT0iRnVuY3Rpb25SZWYiPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8b3BlcmFuZCBsb2NhdG9yPSIxNDc6MzEtMTQ3OjQ3IiBwYXRoPSJiaXJ0aERhdGUiIHhzaTp0eXBlPSJQcm9wZXJ0eSI+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDxzb3VyY2UgbG9jYXRvcj0iMTQ3OjMxLTE0NzozNyIgbmFtZT0iUGF0aWVudCIgeHNpOnR5cGU9IkV4cHJlc3Npb25SZWYiLz4KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPC9vcGVyYW5kPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8L29wZXJhbmQ+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDxvcGVyYW5kIGxvY2F0b3I9IjE0Nzo1Mi0xNDc6MTA2IiBuYW1lPSJUb1F1YW50aXR5IiBsaWJyYXJ5TmFtZT0iRkhJUkhlbHBlcnMiIHhzaTp0eXBlPSJGdW5jdGlvblJlZiI+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDxvcGVyYW5kIGxvY2F0b3I9IjE0Nzo3NS0xNDc6MTA1IiBzdHJpY3Q9ImZhbHNlIiB4c2k6dHlwZT0iQXMiPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8b3BlcmFuZCBsb2NhdG9yPSIxNDc6NzUtMTQ3OjkzIiBwYXRoPSJhYmF0ZW1lbnQiIHhzaTp0eXBlPSJQcm9wZXJ0eSI+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDxzb3VyY2UgbG9jYXRvcj0iMTQ3Ojc1LTE0Nzo4MyIgbmFtZT0iY29uZGl0aW9uIiB4c2k6dHlwZT0iT3BlcmFuZFJlZiIvPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8L29wZXJhbmQ+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDxhc1R5cGVTcGVjaWZpZXIgbG9jYXRvcj0iMTQ3Ojk4LTE0NzoxMDUiIG5hbWU9ImZoaXI6QWdlIiB4c2k6dHlwZT0iTmFtZWRUeXBlU3BlY2lmaWVyIi8+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDwvb3BlcmFuZD4KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPC9vcGVyYW5kPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8L2xvdz4KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPGhpZ2ggbG9jYXRvcj0iMTQ4OjQtMTQ4OjEwNyIgeHNpOnR5cGU9IkFkZCI+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDxvcGVyYW5kIGxvY2F0b3I9IjE0ODo0LTE0ODo5OCIgeHNpOnR5cGU9IkFkZCI+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDxvcGVyYW5kIGxvY2F0b3I9IjE0ODo0LTE0ODo0MCIgbmFtZT0iVG9EYXRlIiBsaWJyYXJ5TmFtZT0iRkhJUkhlbHBlcnMiIHhzaTp0eXBlPSJGdW5jdGlvblJlZiI+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDxvcGVyYW5kIGxvY2F0b3I9IjE0ODoyMy0xNDg6MzkiIHBhdGg9ImJpcnRoRGF0ZSIgeHNpOnR5cGU9IlByb3BlcnR5Ij4KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPHNvdXJjZSBsb2NhdG9yPSIxNDg6MjMtMTQ4OjI5IiBuYW1lPSJQYXRpZW50IiB4c2k6dHlwZT0iRXhwcmVzc2lvblJlZiIvPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8L29wZXJhbmQ+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDwvb3BlcmFuZD4KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPG9wZXJhbmQgbG9jYXRvcj0iMTQ4OjQ0LTE0ODo5OCIgbmFtZT0iVG9RdWFudGl0eSIgbGlicmFyeU5hbWU9IkZISVJIZWxwZXJzIiB4c2k6dHlwZT0iRnVuY3Rpb25SZWYiPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8b3BlcmFuZCBsb2NhdG9yPSIxNDg6NjctMTQ4Ojk3IiBzdHJpY3Q9ImZhbHNlIiB4c2k6dHlwZT0iQXMiPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8b3BlcmFuZCBsb2NhdG9yPSIxNDg6NjctMTQ4Ojg1IiBwYXRoPSJhYmF0ZW1lbnQiIHhzaTp0eXBlPSJQcm9wZXJ0eSI+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDxzb3VyY2UgbG9jYXRvcj0iMTQ4OjY3LTE0ODo3NSIgbmFtZT0iY29uZGl0aW9uIiB4c2k6dHlwZT0iT3BlcmFuZFJlZiIvPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8L29wZXJhbmQ+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDxhc1R5cGVTcGVjaWZpZXIgbG9jYXRvcj0iMTQ4OjkwLTE0ODo5NyIgbmFtZT0iZmhpcjpBZ2UiIHhzaTp0eXBlPSJOYW1lZFR5cGVTcGVjaWZpZXIiLz4KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPC9vcGVyYW5kPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8L29wZXJhbmQ+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDwvb3BlcmFuZD4KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPG9wZXJhbmQgbG9jYXRvcj0iMTQ4OjEwMi0xNDg6MTA3IiB2YWx1ZT0iMSIgdW5pdD0ieWVhciIgeHNpOnR5cGU9IlF1YW50aXR5Ii8+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDwvaGlnaD4KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPC9zb3VyY2U+CiAgICAgICAgICAgICAgICAgICAgICAgICAgIDwvaGlnaENsb3NlZEV4cHJlc3Npb24+CiAgICAgICAgICAgICAgICAgICAgICAgIDwvdGhlbj4KICAgICAgICAgICAgICAgICAgICAgICAgPGVsc2UgbG9jYXRvcj0iMTQ5OjctMTU0OjEwIiB4c2k6dHlwZT0iSWYiPgogICAgICAgICAgICAgICAgICAgICAgICAgICA8Y29uZGl0aW9uIGxvY2F0b3I9IjE0OToxMC0xNDk6NDIiIHhzaTp0eXBlPSJJcyI+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDxvcGVyYW5kIGxvY2F0b3I9IjE0OToxMC0xNDk6MjgiIHBhdGg9ImFiYXRlbWVudCIgeHNpOnR5cGU9IlByb3BlcnR5Ij4KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPHNvdXJjZSBsb2NhdG9yPSIxNDk6MTAtMTQ5OjE4IiBuYW1lPSJjb25kaXRpb24iIHhzaTp0eXBlPSJPcGVyYW5kUmVmIi8+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDwvb3BlcmFuZD4KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPGlzVHlwZVNwZWNpZmllciBsb2NhdG9yPSIxNDk6MzMtMTQ5OjQyIiBuYW1lPSJmaGlyOlJhbmdlIiB4c2k6dHlwZT0iTmFtZWRUeXBlU3BlY2lmaWVyIi8+CiAgICAgICAgICAgICAgICAgICAgICAgICAgIDwvY29uZGl0aW9uPgogICAgICAgICAgICAgICAgICAgICAgICAgICA8dGhlbiB4c2k6dHlwZT0iSW50ZXJ2YWwiPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8bG93IHhzaTp0eXBlPSJUb0RhdGVUaW1lIj4KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPG9wZXJhbmQgcGF0aD0ibG93IiB4c2k6dHlwZT0iUHJvcGVydHkiPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8c291cmNlIGxvY2F0b3I9IjE1MDo0LTE1MToxMTgiIGxvd0Nsb3NlZD0idHJ1ZSIgaGlnaENsb3NlZD0iZmFsc2UiIHhzaTp0eXBlPSJJbnRlcnZhbCI+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDxsb3cgbG9jYXRvcj0iMTUwOjEzLTE1MDoxMTUiIHhzaTp0eXBlPSJBZGQiPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8b3BlcmFuZCBsb2NhdG9yPSIxNTA6MTMtMTUwOjQ5IiBuYW1lPSJUb0RhdGUiIGxpYnJhcnlOYW1lPSJGSElSSGVscGVycyIgeHNpOnR5cGU9IkZ1bmN0aW9uUmVmIj4KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPG9wZXJhbmQgbG9jYXRvcj0iMTUwOjMyLTE1MDo0OCIgcGF0aD0iYmlydGhEYXRlIiB4c2k6dHlwZT0iUHJvcGVydHkiPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8c291cmNlIGxvY2F0b3I9IjE1MDozMi0xNTA6MzgiIG5hbWU9IlBhdGllbnQiIHhzaTp0eXBlPSJFeHByZXNzaW9uUmVmIi8+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDwvb3BlcmFuZD4KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPC9vcGVyYW5kPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8b3BlcmFuZCBsb2NhdG9yPSIxNTA6NTMtMTUwOjExNSIgbmFtZT0iVG9RdWFudGl0eSIgbGlicmFyeU5hbWU9IkZISVJIZWxwZXJzIiB4c2k6dHlwZT0iRnVuY3Rpb25SZWYiPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8b3BlcmFuZCBsb2NhdG9yPSIxNTA6NzYtMTUwOjExNCIgcGF0aD0ibG93IiB4c2k6dHlwZT0iUHJvcGVydHkiPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8c291cmNlIGxvY2F0b3I9IjE1MDo3Ni0xNTA6MTEwIiBzdHJpY3Q9ImZhbHNlIiB4c2k6dHlwZT0iQXMiPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8b3BlcmFuZCBsb2NhdG9yPSIxNTA6NzctMTUwOjk1IiBwYXRoPSJhYmF0ZW1lbnQiIHhzaTp0eXBlPSJQcm9wZXJ0eSI+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDxzb3VyY2UgbG9jYXRvcj0iMTUwOjc3LTE1MDo4NSIgbmFtZT0iY29uZGl0aW9uIiB4c2k6dHlwZT0iT3BlcmFuZFJlZiIvPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8L29wZXJhbmQ+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDxhc1R5cGVTcGVjaWZpZXIgbG9jYXRvcj0iMTUwOjEwMC0xNTA6MTA5IiBuYW1lPSJmaGlyOlJhbmdlIiB4c2k6dHlwZT0iTmFtZWRUeXBlU3BlY2lmaWVyIi8+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDwvc291cmNlPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8L29wZXJhbmQ+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDwvb3BlcmFuZD4KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPC9sb3c+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDxoaWdoIGxvY2F0b3I9IjE1MTo1LTE1MToxMTciIHhzaTp0eXBlPSJBZGQiPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8b3BlcmFuZCBsb2NhdG9yPSIxNTE6NS0xNTE6MTA4IiB4c2k6dHlwZT0iQWRkIj4KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPG9wZXJhbmQgbG9jYXRvcj0iMTUxOjUtMTUxOjQxIiBuYW1lPSJUb0RhdGUiIGxpYnJhcnlOYW1lPSJGSElSSGVscGVycyIgeHNpOnR5cGU9IkZ1bmN0aW9uUmVmIj4KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPG9wZXJhbmQgbG9jYXRvcj0iMTUxOjI0LTE1MTo0MCIgcGF0aD0iYmlydGhEYXRlIiB4c2k6dHlwZT0iUHJvcGVydHkiPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8c291cmNlIGxvY2F0b3I9IjE1MToyNC0xNTE6MzAiIG5hbWU9IlBhdGllbnQiIHhzaTp0eXBlPSJFeHByZXNzaW9uUmVmIi8+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDwvb3BlcmFuZD4KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPC9vcGVyYW5kPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8b3BlcmFuZCBsb2NhdG9yPSIxNTE6NDUtMTUxOjEwOCIgbmFtZT0iVG9RdWFudGl0eSIgbGlicmFyeU5hbWU9IkZISVJIZWxwZXJzIiB4c2k6dHlwZT0iRnVuY3Rpb25SZWYiPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8b3BlcmFuZCBsb2NhdG9yPSIxNTE6NjgtMTUxOjEwNyIgcGF0aD0iaGlnaCIgeHNpOnR5cGU9IlByb3BlcnR5Ij4KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPHNvdXJjZSBsb2NhdG9yPSIxNTE6NjgtMTUxOjEwMiIgc3RyaWN0PSJmYWxzZSIgeHNpOnR5cGU9IkFzIj4KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPG9wZXJhbmQgbG9jYXRvcj0iMTUxOjY5LTE1MTo4NyIgcGF0aD0iYWJhdGVtZW50IiB4c2k6dHlwZT0iUHJvcGVydHkiPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8c291cmNlIGxvY2F0b3I9IjE1MTo2OS0xNTE6NzciIG5hbWU9ImNvbmRpdGlvbiIgeHNpOnR5cGU9Ik9wZXJhbmRSZWYiLz4KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPC9vcGVyYW5kPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8YXNUeXBlU3BlY2lmaWVyIGxvY2F0b3I9IjE1MTo5Mi0xNTE6MTAxIiBuYW1lPSJmaGlyOlJhbmdlIiB4c2k6dHlwZT0iTmFtZWRUeXBlU3BlY2lmaWVyIi8+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDwvc291cmNlPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8L29wZXJhbmQ+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDwvb3BlcmFuZD4KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPC9vcGVyYW5kPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8b3BlcmFuZCBsb2NhdG9yPSIxNTE6MTEyLTE1MToxMTciIHZhbHVlPSIxIiB1bml0PSJ5ZWFyIiB4c2k6dHlwZT0iUXVhbnRpdHkiLz4KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPC9oaWdoPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8L3NvdXJjZT4KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPC9vcGVyYW5kPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8L2xvdz4KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPGxvd0Nsb3NlZEV4cHJlc3Npb24gcGF0aD0ibG93Q2xvc2VkIiB4c2k6dHlwZT0iUHJvcGVydHkiPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8c291cmNlIGxvY2F0b3I9IjE1MDo0LTE1MToxMTgiIGxvd0Nsb3NlZD0idHJ1ZSIgaGlnaENsb3NlZD0iZmFsc2UiIHhzaTp0eXBlPSJJbnRlcnZhbCI+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDxsb3cgbG9jYXRvcj0iMTUwOjEzLTE1MDoxMTUiIHhzaTp0eXBlPSJBZGQiPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8b3BlcmFuZCBsb2NhdG9yPSIxNTA6MTMtMTUwOjQ5IiBuYW1lPSJUb0RhdGUiIGxpYnJhcnlOYW1lPSJGSElSSGVscGVycyIgeHNpOnR5cGU9IkZ1bmN0aW9uUmVmIj4KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPG9wZXJhbmQgbG9jYXRvcj0iMTUwOjMyLTE1MDo0OCIgcGF0aD0iYmlydGhEYXRlIiB4c2k6dHlwZT0iUHJvcGVydHkiPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8c291cmNlIGxvY2F0b3I9IjE1MDozMi0xNTA6MzgiIG5hbWU9IlBhdGllbnQiIHhzaTp0eXBlPSJFeHByZXNzaW9uUmVmIi8+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDwvb3BlcmFuZD4KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPC9vcGVyYW5kPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8b3BlcmFuZCBsb2NhdG9yPSIxNTA6NTMtMTUwOjExNSIgbmFtZT0iVG9RdWFudGl0eSIgbGlicmFyeU5hbWU9IkZISVJIZWxwZXJzIiB4c2k6dHlwZT0iRnVuY3Rpb25SZWYiPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8b3BlcmFuZCBsb2NhdG9yPSIxNTA6NzYtMTUwOjExNCIgcGF0aD0ibG93IiB4c2k6dHlwZT0iUHJvcGVydHkiPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8c291cmNlIGxvY2F0b3I9IjE1MDo3Ni0xNTA6MTEwIiBzdHJpY3Q9ImZhbHNlIiB4c2k6dHlwZT0iQXMiPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8b3BlcmFuZCBsb2NhdG9yPSIxNTA6NzctMTUwOjk1IiBwYXRoPSJhYmF0ZW1lbnQiIHhzaTp0eXBlPSJQcm9wZXJ0eSI+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDxzb3VyY2UgbG9jYXRvcj0iMTUwOjc3LTE1MDo4NSIgbmFtZT0iY29uZGl0aW9uIiB4c2k6dHlwZT0iT3BlcmFuZFJlZiIvPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8L29wZXJhbmQ+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDxhc1R5cGVTcGVjaWZpZXIgbG9jYXRvcj0iMTUwOjEwMC0xNTA6MTA5IiBuYW1lPSJmaGlyOlJhbmdlIiB4c2k6dHlwZT0iTmFtZWRUeXBlU3BlY2lmaWVyIi8+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDwvc291cmNlPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8L29wZXJhbmQ+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDwvb3BlcmFuZD4KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPC9sb3c+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDxoaWdoIGxvY2F0b3I9IjE1MTo1LTE1MToxMTciIHhzaTp0eXBlPSJBZGQiPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8b3BlcmFuZCBsb2NhdG9yPSIxNTE6NS0xNTE6MTA4IiB4c2k6dHlwZT0iQWRkIj4KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPG9wZXJhbmQgbG9jYXRvcj0iMTUxOjUtMTUxOjQxIiBuYW1lPSJUb0RhdGUiIGxpYnJhcnlOYW1lPSJGSElSSGVscGVycyIgeHNpOnR5cGU9IkZ1bmN0aW9uUmVmIj4KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPG9wZXJhbmQgbG9jYXRvcj0iMTUxOjI0LTE1MTo0MCIgcGF0aD0iYmlydGhEYXRlIiB4c2k6dHlwZT0iUHJvcGVydHkiPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8c291cmNlIGxvY2F0b3I9IjE1MToyNC0xNTE6MzAiIG5hbWU9IlBhdGllbnQiIHhzaTp0eXBlPSJFeHByZXNzaW9uUmVmIi8+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDwvb3BlcmFuZD4KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPC9vcGVyYW5kPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8b3BlcmFuZCBsb2NhdG9yPSIxNTE6NDUtMTUxOjEwOCIgbmFtZT0iVG9RdWFudGl0eSIgbGlicmFyeU5hbWU9IkZISVJIZWxwZXJzIiB4c2k6dHlwZT0iRnVuY3Rpb25SZWYiPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8b3BlcmFuZCBsb2NhdG9yPSIxNTE6NjgtMTUxOjEwNyIgcGF0aD0iaGlnaCIgeHNpOnR5cGU9IlByb3BlcnR5Ij4KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPHNvdXJjZSBsb2NhdG9yPSIxNTE6NjgtMTUxOjEwMiIgc3RyaWN0PSJmYWxzZSIgeHNpOnR5cGU9IkFzIj4KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPG9wZXJhbmQgbG9jYXRvcj0iMTUxOjY5LTE1MTo4NyIgcGF0aD0iYWJhdGVtZW50IiB4c2k6dHlwZT0iUHJvcGVydHkiPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8c291cmNlIGxvY2F0b3I9IjE1MTo2OS0xNTE6NzciIG5hbWU9ImNvbmRpdGlvbiIgeHNpOnR5cGU9Ik9wZXJhbmRSZWYiLz4KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPC9vcGVyYW5kPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8YXNUeXBlU3BlY2lmaWVyIGxvY2F0b3I9IjE1MTo5Mi0xNTE6MTAxIiBuYW1lPSJmaGlyOlJhbmdlIiB4c2k6dHlwZT0iTmFtZWRUeXBlU3BlY2lmaWVyIi8+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDwvc291cmNlPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8L29wZXJhbmQ+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDwvb3BlcmFuZD4KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPC9vcGVyYW5kPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8b3BlcmFuZCBsb2NhdG9yPSIxNTE6MTEyLTE1MToxMTciIHZhbHVlPSIxIiB1bml0PSJ5ZWFyIiB4c2k6dHlwZT0iUXVhbnRpdHkiLz4KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPC9oaWdoPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8L3NvdXJjZT4KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPC9sb3dDbG9zZWRFeHByZXNzaW9uPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8aGlnaCB4c2k6dHlwZT0iVG9EYXRlVGltZSI+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDxvcGVyYW5kIHBhdGg9ImhpZ2giIHhzaTp0eXBlPSJQcm9wZXJ0eSI+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDxzb3VyY2UgbG9jYXRvcj0iMTUwOjQtMTUxOjExOCIgbG93Q2xvc2VkPSJ0cnVlIiBoaWdoQ2xvc2VkPSJmYWxzZSIgeHNpOnR5cGU9IkludGVydmFsIj4KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPGxvdyBsb2NhdG9yPSIxNTA6MTMtMTUwOjExNSIgeHNpOnR5cGU9IkFkZCI+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDxvcGVyYW5kIGxvY2F0b3I9IjE1MDoxMy0xNTA6NDkiIG5hbWU9IlRvRGF0ZSIgbGlicmFyeU5hbWU9IkZISVJIZWxwZXJzIiB4c2k6dHlwZT0iRnVuY3Rpb25SZWYiPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8b3BlcmFuZCBsb2NhdG9yPSIxNTA6MzItMTUwOjQ4IiBwYXRoPSJiaXJ0aERhdGUiIHhzaTp0eXBlPSJQcm9wZXJ0eSI+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDxzb3VyY2UgbG9jYXRvcj0iMTUwOjMyLTE1MDozOCIgbmFtZT0iUGF0aWVudCIgeHNpOnR5cGU9IkV4cHJlc3Npb25SZWYiLz4KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPC9vcGVyYW5kPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8L29wZXJhbmQ+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDxvcGVyYW5kIGxvY2F0b3I9IjE1MDo1My0xNTA6MTE1IiBuYW1lPSJUb1F1YW50aXR5IiBsaWJyYXJ5TmFtZT0iRkhJUkhlbHBlcnMiIHhzaTp0eXBlPSJGdW5jdGlvblJlZiI+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDxvcGVyYW5kIGxvY2F0b3I9IjE1MDo3Ni0xNTA6MTE0IiBwYXRoPSJsb3ciIHhzaTp0eXBlPSJQcm9wZXJ0eSI+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDxzb3VyY2UgbG9jYXRvcj0iMTUwOjc2LTE1MDoxMTAiIHN0cmljdD0iZmFsc2UiIHhzaTp0eXBlPSJBcyI+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDxvcGVyYW5kIGxvY2F0b3I9IjE1MDo3Ny0xNTA6OTUiIHBhdGg9ImFiYXRlbWVudCIgeHNpOnR5cGU9IlByb3BlcnR5Ij4KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPHNvdXJjZSBsb2NhdG9yPSIxNTA6NzctMTUwOjg1IiBuYW1lPSJjb25kaXRpb24iIHhzaTp0eXBlPSJPcGVyYW5kUmVmIi8+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDwvb3BlcmFuZD4KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPGFzVHlwZVNwZWNpZmllciBsb2NhdG9yPSIxNTA6MTAwLTE1MDoxMDkiIG5hbWU9ImZoaXI6UmFuZ2UiIHhzaTp0eXBlPSJOYW1lZFR5cGVTcGVjaWZpZXIiLz4KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPC9zb3VyY2U+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDwvb3BlcmFuZD4KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPC9vcGVyYW5kPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8L2xvdz4KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPGhpZ2ggbG9jYXRvcj0iMTUxOjUtMTUxOjExNyIgeHNpOnR5cGU9IkFkZCI+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDxvcGVyYW5kIGxvY2F0b3I9IjE1MTo1LTE1MToxMDgiIHhzaTp0eXBlPSJBZGQiPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8b3BlcmFuZCBsb2NhdG9yPSIxNTE6NS0xNTE6NDEiIG5hbWU9IlRvRGF0ZSIgbGlicmFyeU5hbWU9IkZISVJIZWxwZXJzIiB4c2k6dHlwZT0iRnVuY3Rpb25SZWYiPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8b3BlcmFuZCBsb2NhdG9yPSIxNTE6MjQtMTUxOjQwIiBwYXRoPSJiaXJ0aERhdGUiIHhzaTp0eXBlPSJQcm9wZXJ0eSI+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDxzb3VyY2UgbG9jYXRvcj0iMTUxOjI0LTE1MTozMCIgbmFtZT0iUGF0aWVudCIgeHNpOnR5cGU9IkV4cHJlc3Npb25SZWYiLz4KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPC9vcGVyYW5kPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8L29wZXJhbmQ+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDxvcGVyYW5kIGxvY2F0b3I9IjE1MTo0NS0xNTE6MTA4IiBuYW1lPSJUb1F1YW50aXR5IiBsaWJyYXJ5TmFtZT0iRkhJUkhlbHBlcnMiIHhzaTp0eXBlPSJGdW5jdGlvblJlZiI+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDxvcGVyYW5kIGxvY2F0b3I9IjE1MTo2OC0xNTE6MTA3IiBwYXRoPSJoaWdoIiB4c2k6dHlwZT0iUHJvcGVydHkiPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8c291cmNlIGxvY2F0b3I9IjE1MTo2OC0xNTE6MTAyIiBzdHJpY3Q9ImZhbHNlIiB4c2k6dHlwZT0iQXMiPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8b3BlcmFuZCBsb2NhdG9yPSIxNTE6NjktMTUxOjg3IiBwYXRoPSJhYmF0ZW1lbnQiIHhzaTp0eXBlPSJQcm9wZXJ0eSI+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDxzb3VyY2UgbG9jYXRvcj0iMTUxOjY5LTE1MTo3NyIgbmFtZT0iY29uZGl0aW9uIiB4c2k6dHlwZT0iT3BlcmFuZFJlZiIvPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8L29wZXJhbmQ+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDxhc1R5cGVTcGVjaWZpZXIgbG9jYXRvcj0iMTUxOjkyLTE1MToxMDEiIG5hbWU9ImZoaXI6UmFuZ2UiIHhzaTp0eXBlPSJOYW1lZFR5cGVTcGVjaWZpZXIiLz4KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPC9zb3VyY2U+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDwvb3BlcmFuZD4KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPC9vcGVyYW5kPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8L29wZXJhbmQ+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDxvcGVyYW5kIGxvY2F0b3I9IjE1MToxMTItMTUxOjExNyIgdmFsdWU9IjEiIHVuaXQ9InllYXIiIHhzaTp0eXBlPSJRdWFudGl0eSIvPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8L2hpZ2g+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDwvc291cmNlPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8L29wZXJhbmQ+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDwvaGlnaD4KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPGhpZ2hDbG9zZWRFeHByZXNzaW9uIHBhdGg9ImhpZ2hDbG9zZWQiIHhzaTp0eXBlPSJQcm9wZXJ0eSI+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDxzb3VyY2UgbG9jYXRvcj0iMTUwOjQtMTUxOjExOCIgbG93Q2xvc2VkPSJ0cnVlIiBoaWdoQ2xvc2VkPSJmYWxzZSIgeHNpOnR5cGU9IkludGVydmFsIj4KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPGxvdyBsb2NhdG9yPSIxNTA6MTMtMTUwOjExNSIgeHNpOnR5cGU9IkFkZCI+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDxvcGVyYW5kIGxvY2F0b3I9IjE1MDoxMy0xNTA6NDkiIG5hbWU9IlRvRGF0ZSIgbGlicmFyeU5hbWU9IkZISVJIZWxwZXJzIiB4c2k6dHlwZT0iRnVuY3Rpb25SZWYiPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8b3BlcmFuZCBsb2NhdG9yPSIxNTA6MzItMTUwOjQ4IiBwYXRoPSJiaXJ0aERhdGUiIHhzaTp0eXBlPSJQcm9wZXJ0eSI+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDxzb3VyY2UgbG9jYXRvcj0iMTUwOjMyLTE1MDozOCIgbmFtZT0iUGF0aWVudCIgeHNpOnR5cGU9IkV4cHJlc3Npb25SZWYiLz4KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPC9vcGVyYW5kPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8L29wZXJhbmQ+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDxvcGVyYW5kIGxvY2F0b3I9IjE1MDo1My0xNTA6MTE1IiBuYW1lPSJUb1F1YW50aXR5IiBsaWJyYXJ5TmFtZT0iRkhJUkhlbHBlcnMiIHhzaTp0eXBlPSJGdW5jdGlvblJlZiI+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDxvcGVyYW5kIGxvY2F0b3I9IjE1MDo3Ni0xNTA6MTE0IiBwYXRoPSJsb3ciIHhzaTp0eXBlPSJQcm9wZXJ0eSI+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDxzb3VyY2UgbG9jYXRvcj0iMTUwOjc2LTE1MDoxMTAiIHN0cmljdD0iZmFsc2UiIHhzaTp0eXBlPSJBcyI+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDxvcGVyYW5kIGxvY2F0b3I9IjE1MDo3Ny0xNTA6OTUiIHBhdGg9ImFiYXRlbWVudCIgeHNpOnR5cGU9IlByb3BlcnR5Ij4KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPHNvdXJjZSBsb2NhdG9yPSIxNTA6NzctMTUwOjg1IiBuYW1lPSJjb25kaXRpb24iIHhzaTp0eXBlPSJPcGVyYW5kUmVmIi8+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDwvb3BlcmFuZD4KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPGFzVHlwZVNwZWNpZmllciBsb2NhdG9yPSIxNTA6MTAwLTE1MDoxMDkiIG5hbWU9ImZoaXI6UmFuZ2UiIHhzaTp0eXBlPSJOYW1lZFR5cGVTcGVjaWZpZXIiLz4KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPC9zb3VyY2U+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDwvb3BlcmFuZD4KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPC9vcGVyYW5kPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8L2xvdz4KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPGhpZ2ggbG9jYXRvcj0iMTUxOjUtMTUxOjExNyIgeHNpOnR5cGU9IkFkZCI+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDxvcGVyYW5kIGxvY2F0b3I9IjE1MTo1LTE1MToxMDgiIHhzaTp0eXBlPSJBZGQiPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8b3BlcmFuZCBsb2NhdG9yPSIxNTE6NS0xNTE6NDEiIG5hbWU9IlRvRGF0ZSIgbGlicmFyeU5hbWU9IkZISVJIZWxwZXJzIiB4c2k6dHlwZT0iRnVuY3Rpb25SZWYiPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8b3BlcmFuZCBsb2NhdG9yPSIxNTE6MjQtMTUxOjQwIiBwYXRoPSJiaXJ0aERhdGUiIHhzaTp0eXBlPSJQcm9wZXJ0eSI+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDxzb3VyY2UgbG9jYXRvcj0iMTUxOjI0LTE1MTozMCIgbmFtZT0iUGF0aWVudCIgeHNpOnR5cGU9IkV4cHJlc3Npb25SZWYiLz4KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPC9vcGVyYW5kPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8L29wZXJhbmQ+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDxvcGVyYW5kIGxvY2F0b3I9IjE1MTo0NS0xNTE6MTA4IiBuYW1lPSJUb1F1YW50aXR5IiBsaWJyYXJ5TmFtZT0iRkhJUkhlbHBlcnMiIHhzaTp0eXBlPSJGdW5jdGlvblJlZiI+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDxvcGVyYW5kIGxvY2F0b3I9IjE1MTo2OC0xNTE6MTA3IiBwYXRoPSJoaWdoIiB4c2k6dHlwZT0iUHJvcGVydHkiPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8c291cmNlIGxvY2F0b3I9IjE1MTo2OC0xNTE6MTAyIiBzdHJpY3Q9ImZhbHNlIiB4c2k6dHlwZT0iQXMiPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8b3BlcmFuZCBsb2NhdG9yPSIxNTE6NjktMTUxOjg3IiBwYXRoPSJhYmF0ZW1lbnQiIHhzaTp0eXBlPSJQcm9wZXJ0eSI+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDxzb3VyY2UgbG9jYXRvcj0iMTUxOjY5LTE1MTo3NyIgbmFtZT0iY29uZGl0aW9uIiB4c2k6dHlwZT0iT3BlcmFuZFJlZiIvPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8L29wZXJhbmQ+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDxhc1R5cGVTcGVjaWZpZXIgbG9jYXRvcj0iMTUxOjkyLTE1MToxMDEiIG5hbWU9ImZoaXI6UmFuZ2UiIHhzaTp0eXBlPSJOYW1lZFR5cGVTcGVjaWZpZXIiLz4KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPC9zb3VyY2U+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDwvb3BlcmFuZD4KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPC9vcGVyYW5kPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8L29wZXJhbmQ+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDxvcGVyYW5kIGxvY2F0b3I9IjE1MToxMTItMTUxOjExNyIgdmFsdWU9IjEiIHVuaXQ9InllYXIiIHhzaTp0eXBlPSJRdWFudGl0eSIvPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8L2hpZ2g+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDwvc291cmNlPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8L2hpZ2hDbG9zZWRFeHByZXNzaW9uPgogICAgICAgICAgICAgICAgICAgICAgICAgICA8L3RoZW4+CiAgICAgICAgICAgICAgICAgICAgICAgICAgIDxlbHNlIGxvY2F0b3I9IjE1Mjo3LTE1NDoxMCIgeHNpOnR5cGU9IklmIj4KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPGNvbmRpdGlvbiBsb2NhdG9yPSIxNTI6MTAtMTUyOjQ0IiB4c2k6dHlwZT0iSXMiPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8b3BlcmFuZCBsb2NhdG9yPSIxNTI6MTAtMTUyOjI4IiBwYXRoPSJhYmF0ZW1lbnQiIHhzaTp0eXBlPSJQcm9wZXJ0eSI+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDxzb3VyY2UgbG9jYXRvcj0iMTUyOjEwLTE1MjoxOCIgbmFtZT0iY29uZGl0aW9uIiB4c2k6dHlwZT0iT3BlcmFuZFJlZiIvPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8L29wZXJhbmQ+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDxpc1R5cGVTcGVjaWZpZXIgbG9jYXRvcj0iMTUyOjMzLTE1Mjo0NCIgbmFtZT0iZmhpcjpib29sZWFuIiB4c2k6dHlwZT0iTmFtZWRUeXBlU3BlY2lmaWVyIi8+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDwvY29uZGl0aW9uPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8dGhlbiBsb2NhdG9yPSIxNTM6NC0xNTM6NzEiIGxvd0Nsb3NlZD0idHJ1ZSIgaGlnaENsb3NlZD0iZmFsc2UiIHhzaTp0eXBlPSJJbnRlcnZhbCI+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDxsb3cgbG9jYXRvcj0iMTUzOjEzLTE1Mzo0NiIgeHNpOnR5cGU9IkVuZCI+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDxvcGVyYW5kIGxvY2F0b3I9IjE1MzoyMC0xNTM6NDYiIG5hbWU9IlRvSW50ZXJ2YWwiIHhzaTp0eXBlPSJGdW5jdGlvblJlZiI+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDxvcGVyYW5kIGxvY2F0b3I9IjE1MzozMS0xNTM6NDUiIHBhdGg9Im9uc2V0IiB4c2k6dHlwZT0iUHJvcGVydHkiPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8c291cmNlIGxvY2F0b3I9IjE1MzozMS0xNTM6MzkiIG5hbWU9ImNvbmRpdGlvbiIgeHNpOnR5cGU9Ik9wZXJhbmRSZWYiLz4KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPC9vcGVyYW5kPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8L29wZXJhbmQ+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDwvbG93PgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8aGlnaCBuYW1lPSJUb0RhdGVUaW1lIiBsaWJyYXJ5TmFtZT0iRkhJUkhlbHBlcnMiIHhzaTp0eXBlPSJGdW5jdGlvblJlZiI+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDxvcGVyYW5kIGxvY2F0b3I9IjE1Mzo0OS0xNTM6NzAiIHBhdGg9InJlY29yZGVkRGF0ZSIgeHNpOnR5cGU9IlByb3BlcnR5Ij4KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPHNvdXJjZSBsb2NhdG9yPSIxNTM6NDktMTUzOjU3IiBuYW1lPSJjb25kaXRpb24iIHhzaTp0eXBlPSJPcGVyYW5kUmVmIi8+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDwvb3BlcmFuZD4KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPC9oaWdoPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8L3RoZW4+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDxlbHNlIHhzaTp0eXBlPSJBcyI+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDxvcGVyYW5kIGxvY2F0b3I9IjE1NDo3LTE1NDoxMCIgeHNpOnR5cGU9Ik51bGwiLz4KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPGFzVHlwZVNwZWNpZmllciB4c2k6dHlwZT0iSW50ZXJ2YWxUeXBlU3BlY2lmaWVyIj4KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPHBvaW50VHlwZSBuYW1lPSJ0OkRhdGVUaW1lIiB4c2k6dHlwZT0iTmFtZWRUeXBlU3BlY2lmaWVyIi8+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDwvYXNUeXBlU3BlY2lmaWVyPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8L2Vsc2U+CiAgICAgICAgICAgICAgICAgICAgICAgICAgIDwvZWxzZT4KICAgICAgICAgICAgICAgICAgICAgICAgPC9lbHNlPgogICAgICAgICAgICAgICAgICAgICA8L2Vsc2U+CiAgICAgICAgICAgICAgICAgIDwvZWxzZT4KICAgICAgICAgICAgICAgPC9lbHNlPgogICAgICAgICAgICA8L2Vsc2U+CiAgICAgICAgIDwvZXhwcmVzc2lvbj4KICAgICAgICAgPG9wZXJhbmQgbmFtZT0iY29uZGl0aW9uIj4KICAgICAgICAgICAgPG9wZXJhbmRUeXBlU3BlY2lmaWVyIGxvY2F0b3I9IjEzNTo0Ny0xMzU6NTUiIG5hbWU9ImZoaXI6Q29uZGl0aW9uIiB4c2k6dHlwZT0iTmFtZWRUeXBlU3BlY2lmaWVyIi8+CiAgICAgICAgIDwvb3BlcmFuZD4KICAgICAgPC9kZWY+CiAgICAgIDxkZWYgbG9jYXRvcj0iMTYxOjEtMTYyOjg3IiBuYW1lPSJUb1ByZXZhbGVuY2VJbnRlcnZhbCIgY29udGV4dD0iUGF0aWVudCIgYWNjZXNzTGV2ZWw9IlB1YmxpYyIgeHNpOnR5cGU9IkZ1bmN0aW9uRGVmIj4KICAgICAgICAgPGFubm90YXRpb24geHNpOnR5cGU9ImE6QW5ub3RhdGlvbiI+CiAgICAgICAgICAgIDxhOnQgbmFtZT0iZGVzY3JpcHRpb24iIHZhbHVlPSJSZXR1cm5zIGFuIGludGVydmFsIHJlcHJlc2VudGluZyB0aGUgbm9ybWFsaXplZCBwcmV2YWxlbmNlIHBlcmlvZCBvZiBhIGdpdmVuIENvbmRpdGlvbiByZXNvdXJjZS4iLz4KICAgICAgICAgICAgPGE6dCBuYW1lPSJjb21tZW50IiB2YWx1ZT0iVXNlcyB0aGUgVG9JbnRlcnZhbCBhbmQgVG9BYmF0ZW1lbnRJbnRlcnZhbCBmdW5jdGlvbnMgdG8gZGV0ZXJtaW5lIHRoZSB3aWRlc3QgcG90ZW50aWFsIGludGVydmFsIGZyb20mI3hhO29uc2V0IHRvIGFiYXRlbWVudCBhcyBzcGVjaWZpZWQgaW4gdGhlIGdpdmVuIENvbmRpdGlvbi4iLz4KICAgICAgICAgPC9hbm5vdGF0aW9uPgogICAgICAgICA8ZXhwcmVzc2lvbiBsb2NhdG9yPSIxNjI6My0xNjI6ODciIGxvd0Nsb3NlZD0idHJ1ZSIgaGlnaENsb3NlZD0iZmFsc2UiIHhzaTp0eXBlPSJJbnRlcnZhbCI+CiAgICAgICAgICAgIDxsb3cgbG9jYXRvcj0iMTYyOjEyLTE2Mjo0NyIgeHNpOnR5cGU9IlN0YXJ0Ij4KICAgICAgICAgICAgICAgPG9wZXJhbmQgbG9jYXRvcj0iMTYyOjIxLTE2Mjo0NyIgbmFtZT0iVG9JbnRlcnZhbCIgeHNpOnR5cGU9IkZ1bmN0aW9uUmVmIj4KICAgICAgICAgICAgICAgICAgPG9wZXJhbmQgbG9jYXRvcj0iMTYyOjMyLTE2Mjo0NiIgcGF0aD0ib25zZXQiIHhzaTp0eXBlPSJQcm9wZXJ0eSI+CiAgICAgICAgICAgICAgICAgICAgIDxzb3VyY2UgbG9jYXRvcj0iMTYyOjMyLTE2Mjo0MCIgbmFtZT0iY29uZGl0aW9uIiB4c2k6dHlwZT0iT3BlcmFuZFJlZiIvPgogICAgICAgICAgICAgICAgICA8L29wZXJhbmQ+CiAgICAgICAgICAgICAgIDwvb3BlcmFuZD4KICAgICAgICAgICAgPC9sb3c+CiAgICAgICAgICAgIDxoaWdoIGxvY2F0b3I9IjE2Mjo1MC0xNjI6ODYiIHhzaTp0eXBlPSJFbmQiPgogICAgICAgICAgICAgICA8b3BlcmFuZCBsb2NhdG9yPSIxNjI6NTctMTYyOjg2IiBuYW1lPSJUb0FiYXRlbWVudEludGVydmFsIiB4c2k6dHlwZT0iRnVuY3Rpb25SZWYiPgogICAgICAgICAgICAgICAgICA8b3BlcmFuZCBsb2NhdG9yPSIxNjI6NzctMTYyOjg1IiBuYW1lPSJjb25kaXRpb24iIHhzaTp0eXBlPSJPcGVyYW5kUmVmIi8+CiAgICAgICAgICAgICAgIDwvb3BlcmFuZD4KICAgICAgICAgICAgPC9oaWdoPgogICAgICAgICA8L2V4cHJlc3Npb24+CiAgICAgICAgIDxvcGVyYW5kIG5hbWU9ImNvbmRpdGlvbiI+CiAgICAgICAgICAgIDxvcGVyYW5kVHlwZVNwZWNpZmllciBsb2NhdG9yPSIxNjE6NDgtMTYxOjU2IiBuYW1lPSJmaGlyOkNvbmRpdGlvbiIgeHNpOnR5cGU9Ik5hbWVkVHlwZVNwZWNpZmllciIvPgogICAgICAgICA8L29wZXJhbmQ+CiAgICAgIDwvZGVmPgogICAgICA8ZGVmIGxvY2F0b3I9IjE2ODoxLTE2OToxMDkiIG5hbWU9IlRvUHJldmFsZW5jZUludGVydmFsIiBjb250ZXh0PSJQYXRpZW50IiBhY2Nlc3NMZXZlbD0iUHVibGljIiB4c2k6dHlwZT0iRnVuY3Rpb25EZWYiPgogICAgICAgICA8YW5ub3RhdGlvbiB4c2k6dHlwZT0iYTpBbm5vdGF0aW9uIj4KICAgICAgICAgICAgPGE6dCBuYW1lPSJkZXNjcmlwdGlvbiIgdmFsdWU9IlJldHVybnMgYW4gaW50ZXJ2YWwgcmVwcmVzZW50aW5nIHRoZSBub3JtYWxpemVkIHByZXZhbGVuY2UgcGVyaW9kIG9mIGEgZ2l2ZW4gQWxsZXJneSBJbnRvbGVyYW5jZSByZXNvdXJjZS4iLz4KICAgICAgICAgICAgPGE6dCBuYW1lPSJjb21tZW50IiB2YWx1ZT0iVXNlcyB0aGUgVG9JbnRlcnZhbCBmdW5jdGlvbiBhbmQgbGFzdE9jY3VyZW5jZSB0byBkZXRlcm1pbmUgdGhlIHdpZGVzdCBwb3RlbnRpYWwgaW50ZXJ2YWwuIi8+CiAgICAgICAgIDwvYW5ub3RhdGlvbj4KICAgICAgICAgPGV4cHJlc3Npb24gbG9jYXRvcj0iMTY5OjMtMTY5OjEwOSIgbG93Q2xvc2VkPSJ0cnVlIiBoaWdoQ2xvc2VkPSJ0cnVlIiB4c2k6dHlwZT0iSW50ZXJ2YWwiPgogICAgICAgICAgICA8bG93IGxvY2F0b3I9IjE2OToxMi0xNjk6NTYiIHhzaTp0eXBlPSJTdGFydCI+CiAgICAgICAgICAgICAgIDxvcGVyYW5kIGxvY2F0b3I9IjE2OToyMS0xNjk6NTYiIG5hbWU9IlRvSW50ZXJ2YWwiIHhzaTp0eXBlPSJGdW5jdGlvblJlZiI+CiAgICAgICAgICAgICAgICAgIDxvcGVyYW5kIGxvY2F0b3I9IjE2OTozMi0xNjk6NTUiIHBhdGg9Im9uc2V0IiB4c2k6dHlwZT0iUHJvcGVydHkiPgogICAgICAgICAgICAgICAgICAgICA8c291cmNlIGxvY2F0b3I9IjE2OTozMi0xNjk6NDkiIG5hbWU9ImFsbGVyZ3lJbnRvbGVyYW5jZSIgeHNpOnR5cGU9Ik9wZXJhbmRSZWYiLz4KICAgICAgICAgICAgICAgICAgPC9vcGVyYW5kPgogICAgICAgICAgICAgICA8L29wZXJhbmQ+CiAgICAgICAgICAgIDwvbG93PgogICAgICAgICAgICA8aGlnaCBsb2NhdG9yPSIxNjk6NTktMTY5OjEwOCIgeHNpOnR5cGU9IkNvYWxlc2NlIj4KICAgICAgICAgICAgICAgPG9wZXJhbmQgbmFtZT0iVG9EYXRlVGltZSIgbGlicmFyeU5hbWU9IkZISVJIZWxwZXJzIiB4c2k6dHlwZT0iRnVuY3Rpb25SZWYiPgogICAgICAgICAgICAgICAgICA8b3BlcmFuZCBsb2NhdG9yPSIxNjk6NjgtMTY5OjEwMCIgcGF0aD0ibGFzdE9jY3VycmVuY2UiIHhzaTp0eXBlPSJQcm9wZXJ0eSI+CiAgICAgICAgICAgICAgICAgICAgIDxzb3VyY2UgbG9jYXRvcj0iMTY5OjY4LTE2OTo4NSIgbmFtZT0iYWxsZXJneUludG9sZXJhbmNlIiB4c2k6dHlwZT0iT3BlcmFuZFJlZiIvPgogICAgICAgICAgICAgICAgICA8L29wZXJhbmQ+CiAgICAgICAgICAgICAgIDwvb3BlcmFuZD4KICAgICAgICAgICAgICAgPG9wZXJhbmQgbG9jYXRvcj0iMTY5OjEwMy0xNjk6MTA3IiB4c2k6dHlwZT0iTm93Ii8+CiAgICAgICAgICAgIDwvaGlnaD4KICAgICAgICAgPC9leHByZXNzaW9uPgogICAgICAgICA8b3BlcmFuZCBuYW1lPSJhbGxlcmd5SW50b2xlcmFuY2UiPgogICAgICAgICAgICA8b3BlcmFuZFR5cGVTcGVjaWZpZXIgbG9jYXRvcj0iMTY4OjU3LTE2ODo3NCIgbmFtZT0iZmhpcjpBbGxlcmd5SW50b2xlcmFuY2UiIHhzaTp0eXBlPSJOYW1lZFR5cGVTcGVjaWZpZXIiLz4KICAgICAgICAgPC9vcGVyYW5kPgogICAgICA8L2RlZj4KICAgICAgPGRlZiBsb2NhdG9yPSIxNzg6MS0xODE6MTAiIG5hbWU9IkV4dGVuc2lvbnMiIGNvbnRleHQ9IlBhdGllbnQiIGFjY2Vzc0xldmVsPSJQdWJsaWMiIHhzaTp0eXBlPSJGdW5jdGlvbkRlZiI+CiAgICAgICAgIDxhbm5vdGF0aW9uIHhzaTp0eXBlPSJhOkFubm90YXRpb24iPgogICAgICAgICAgICA8YTp0IG5hbWU9ImRlc2NyaXB0aW9uIiB2YWx1ZT0iUmV0dXJucyBhbnkgZXh0ZW5zaW9ucyBkZWZpbmVkIG9uIHRoZSBnaXZlbiByZXNvdXJjZSB3aXRoIHRoZSBzcGVjaWZpZWQgdXJsLiIvPgogICAgICAgICAgICA8YTp0IG5hbWU9ImNvbW1lbnQiIHZhbHVlPSJOT1RFOiBFeHRlbnNpb25zIGFyZSBub3QgdGhlIHByZWZlcnJlZCBhcHByb2FjaCwgYnV0IGFyZSB1c2VkIGFzIGEgd2F5IHRvIGFjY2VzcyYjeGE7Y29udGVudCB0aGF0IGlzIGRlZmluZWQgYnkgZXh0ZW5zaW9ucyBidXQgbm90IHlldCBzdXJmYWNlZCBpbiB0aGUmI3hhO0NRTCBtb2RlbCBpbmZvLiIvPgogICAgICAgICA8L2Fubm90YXRpb24+CiAgICAgICAgIDxleHByZXNzaW9uIGxvY2F0b3I9IjE3OTozLTE4MToxMCIgeHNpOnR5cGU9IlF1ZXJ5Ij4KICAgICAgICAgICAgPHNvdXJjZSBsb2NhdG9yPSIxNzk6My0xNzk6MjgiIGFsaWFzPSJFIj4KICAgICAgICAgICAgICAgPGV4cHJlc3Npb24gbG9jYXRvcj0iMTc5OjMtMTc5OjI2IiBwYXRoPSJleHRlbnNpb24iIHhzaTp0eXBlPSJQcm9wZXJ0eSI+CiAgICAgICAgICAgICAgICAgIDxzb3VyY2UgbmFtZT0iZG9tYWluUmVzb3VyY2UiIHhzaTp0eXBlPSJPcGVyYW5kUmVmIi8+CiAgICAgICAgICAgICAgIDwvZXhwcmVzc2lvbj4KICAgICAgICAgICAgPC9zb3VyY2U+CiAgICAgICAgICAgIDx3aGVyZSBsb2NhdG9yPSIxODA6NC0xODA6MjAiIHhzaTp0eXBlPSJFcXVhbCI+CiAgICAgICAgICAgICAgIDxvcGVyYW5kIG5hbWU9IlRvU3RyaW5nIiBsaWJyYXJ5TmFtZT0iRkhJUkhlbHBlcnMiIHhzaTp0eXBlPSJGdW5jdGlvblJlZiI+CiAgICAgICAgICAgICAgICAgIDxvcGVyYW5kIGxvY2F0b3I9IjE4MDoxMC0xODA6MTQiIHBhdGg9InVybCIgc2NvcGU9IkUiIHhzaTp0eXBlPSJQcm9wZXJ0eSIvPgogICAgICAgICAgICAgICA8L29wZXJhbmQ+CiAgICAgICAgICAgICAgIDxvcGVyYW5kIGxvY2F0b3I9IjE4MDoxOC0xODA6MjAiIG5hbWU9InVybCIgeHNpOnR5cGU9Ik9wZXJhbmRSZWYiLz4KICAgICAgICAgICAgPC93aGVyZT4KICAgICAgICAgICAgPHJldHVybiBsb2NhdG9yPSIxODE6My0xODE6MTAiPgogICAgICAgICAgICAgICA8ZXhwcmVzc2lvbiBsb2NhdG9yPSIxODE6MTAiIG5hbWU9IkUiIHhzaTp0eXBlPSJBbGlhc1JlZiIvPgogICAgICAgICAgICA8L3JldHVybj4KICAgICAgICAgPC9leHByZXNzaW9uPgogICAgICAgICA8b3BlcmFuZCBuYW1lPSJkb21haW5SZXNvdXJjZSI+CiAgICAgICAgICAgIDxvcGVyYW5kVHlwZVNwZWNpZmllciBsb2NhdG9yPSIxNzg6NDMtMTc4OjU2IiBuYW1lPSJmaGlyOkRvbWFpblJlc291cmNlIiB4c2k6dHlwZT0iTmFtZWRUeXBlU3BlY2lmaWVyIi8+CiAgICAgICAgIDwvb3BlcmFuZD4KICAgICAgICAgPG9wZXJhbmQgbmFtZT0idXJsIj4KICAgICAgICAgICAgPG9wZXJhbmRUeXBlU3BlY2lmaWVyIGxvY2F0b3I9IjE3ODo2My0xNzg6NjgiIG5hbWU9InQ6U3RyaW5nIiB4c2k6dHlwZT0iTmFtZWRUeXBlU3BlY2lmaWVyIi8+CiAgICAgICAgIDwvb3BlcmFuZD4KICAgICAgPC9kZWY+CiAgICAgIDxkZWYgbG9jYXRvcj0iMTg4OjEtMTg5OjUwIiBuYW1lPSJFeHRlbnNpb24iIGNvbnRleHQ9IlBhdGllbnQiIGFjY2Vzc0xldmVsPSJQdWJsaWMiIHhzaTp0eXBlPSJGdW5jdGlvbkRlZiI+CiAgICAgICAgIDxhbm5vdGF0aW9uIHhzaTp0eXBlPSJhOkFubm90YXRpb24iPgogICAgICAgICAgICA8YTp0IG5hbWU9ImRlc2NyaXB0aW9uIiB2YWx1ZT0iUmV0dXJucyB0aGUgc2luZ2xlIGV4dGVuc2lvbiAoaWYgcHJlc2VudCkgb24gdGhlIGdpdmVuIHJlc291cmNlIHdpdGggdGhlIHNwZWNpZmllZCB1cmwuIi8+CiAgICAgICAgICAgIDxhOnQgbmFtZT0iY29tbWVudCIgdmFsdWU9IlRoaXMgZnVuY3Rpb24gdXNlcyBzaW5nbGV0b24gZnJvbSB0byBlbnN1cmUgdGhhdCBhIHJ1bi10aW1lIGV4Y2VwdGlvbiBpcyB0aHJvd24gaWYgdGhlcmUmI3hhO2lzIG1vcmUgdGhhbiBvbmUgZXh0ZW5zaW9uIG9uIHRoZSBnaXZlbiByZXNvdXJjZSB3aXRoIHRoZSBzcGVjaWZpZWQgdXJsLiIvPgogICAgICAgICA8L2Fubm90YXRpb24+CiAgICAgICAgIDxleHByZXNzaW9uIGxvY2F0b3I9IjE4OTozLTE4OTo1MCIgeHNpOnR5cGU9IlNpbmdsZXRvbkZyb20iPgogICAgICAgICAgICA8b3BlcmFuZCBsb2NhdG9yPSIxODk6MTgtMTg5OjUwIiBuYW1lPSJFeHRlbnNpb25zIiB4c2k6dHlwZT0iRnVuY3Rpb25SZWYiPgogICAgICAgICAgICAgICA8b3BlcmFuZCBsb2NhdG9yPSIxODk6MzEtMTg5OjQ0IiBuYW1lPSJkb21haW5SZXNvdXJjZSIgeHNpOnR5cGU9Ik9wZXJhbmRSZWYiLz4KICAgICAgICAgICAgICAgPG9wZXJhbmQgbG9jYXRvcj0iMTg5OjQ3LTE4OTo0OSIgbmFtZT0idXJsIiB4c2k6dHlwZT0iT3BlcmFuZFJlZiIvPgogICAgICAgICAgICA8L29wZXJhbmQ+CiAgICAgICAgIDwvZXhwcmVzc2lvbj4KICAgICAgICAgPG9wZXJhbmQgbmFtZT0iZG9tYWluUmVzb3VyY2UiPgogICAgICAgICAgICA8b3BlcmFuZFR5cGVTcGVjaWZpZXIgbG9jYXRvcj0iMTg4OjQyLTE4ODo1NSIgbmFtZT0iZmhpcjpEb21haW5SZXNvdXJjZSIgeHNpOnR5cGU9Ik5hbWVkVHlwZVNwZWNpZmllciIvPgogICAgICAgICA8L29wZXJhbmQ+CiAgICAgICAgIDxvcGVyYW5kIG5hbWU9InVybCI+CiAgICAgICAgICAgIDxvcGVyYW5kVHlwZVNwZWNpZmllciBsb2NhdG9yPSIxODg6NjItMTg4OjY3IiBuYW1lPSJ0OlN0cmluZyIgeHNpOnR5cGU9Ik5hbWVkVHlwZVNwZWNpZmllciIvPgogICAgICAgICA8L29wZXJhbmQ+CiAgICAgIDwvZGVmPgogICAgICA8ZGVmIGxvY2F0b3I9IjE5NjoxLTE5OToxMCIgbmFtZT0iRXh0ZW5zaW9ucyIgY29udGV4dD0iUGF0aWVudCIgYWNjZXNzTGV2ZWw9IlB1YmxpYyIgeHNpOnR5cGU9IkZ1bmN0aW9uRGVmIj4KICAgICAgICAgPGFubm90YXRpb24geHNpOnR5cGU9ImE6QW5ub3RhdGlvbiI+CiAgICAgICAgICAgIDxhOnQgbmFtZT0iZGVzY3JpcHRpb24iIHZhbHVlPSJSZXR1cm5zIGFueSBleHRlbnNpb25zIGRlZmluZWQgb24gdGhlIGdpdmVuIGVsZW1lbnQgd2l0aCB0aGUgc3BlY2lmaWVkIHVybC4iLz4KICAgICAgICAgICAgPGE6dCBuYW1lPSJjb21tZW50IiB2YWx1ZT0iTk9URTogRXh0ZW5zaW9ucyBhcmUgbm90IHRoZSBwcmVmZXJyZWQgYXBwcm9hY2gsIGJ1dCBhcmUgdXNlZCBhcyBhIHdheSB0byBhY2Nlc3MmI3hhO2NvbnRlbnQgdGhhdCBpcyBkZWZpbmVkIGJ5IGV4dGVuc2lvbnMgYnV0IG5vdCB5ZXQgc3VyZmFjZWQgaW4gdGhlIENRTCBtb2RlbCBpbmZvLiIvPgogICAgICAgICA8L2Fubm90YXRpb24+CiAgICAgICAgIDxleHByZXNzaW9uIGxvY2F0b3I9IjE5NzozLTE5OToxMCIgeHNpOnR5cGU9IlF1ZXJ5Ij4KICAgICAgICAgICAgPHNvdXJjZSBsb2NhdG9yPSIxOTc6My0xOTc6MjEiIGFsaWFzPSJFIj4KICAgICAgICAgICAgICAgPGV4cHJlc3Npb24gbG9jYXRvcj0iMTk3OjMtMTk3OjE5IiBwYXRoPSJleHRlbnNpb24iIHhzaTp0eXBlPSJQcm9wZXJ0eSI+CiAgICAgICAgICAgICAgICAgIDxzb3VyY2UgbmFtZT0iZWxlbWVudCIgeHNpOnR5cGU9Ik9wZXJhbmRSZWYiLz4KICAgICAgICAgICAgICAgPC9leHByZXNzaW9uPgogICAgICAgICAgICA8L3NvdXJjZT4KICAgICAgICAgICAgPHdoZXJlIGxvY2F0b3I9IjE5ODo0LTE5ODoyMCIgeHNpOnR5cGU9IkVxdWFsIj4KICAgICAgICAgICAgICAgPG9wZXJhbmQgbmFtZT0iVG9TdHJpbmciIGxpYnJhcnlOYW1lPSJGSElSSGVscGVycyIgeHNpOnR5cGU9IkZ1bmN0aW9uUmVmIj4KICAgICAgICAgICAgICAgICAgPG9wZXJhbmQgbG9jYXRvcj0iMTk4OjEwLTE5ODoxNCIgcGF0aD0idXJsIiBzY29wZT0iRSIgeHNpOnR5cGU9IlByb3BlcnR5Ii8+CiAgICAgICAgICAgICAgIDwvb3BlcmFuZD4KICAgICAgICAgICAgICAgPG9wZXJhbmQgbG9jYXRvcj0iMTk4OjE4LTE5ODoyMCIgbmFtZT0idXJsIiB4c2k6dHlwZT0iT3BlcmFuZFJlZiIvPgogICAgICAgICAgICA8L3doZXJlPgogICAgICAgICAgICA8cmV0dXJuIGxvY2F0b3I9IjE5OTozLTE5OToxMCI+CiAgICAgICAgICAgICAgIDxleHByZXNzaW9uIGxvY2F0b3I9IjE5OToxMCIgbmFtZT0iRSIgeHNpOnR5cGU9IkFsaWFzUmVmIi8+CiAgICAgICAgICAgIDwvcmV0dXJuPgogICAgICAgICA8L2V4cHJlc3Npb24+CiAgICAgICAgIDxvcGVyYW5kIG5hbWU9ImVsZW1lbnQiPgogICAgICAgICAgICA8b3BlcmFuZFR5cGVTcGVjaWZpZXIgbG9jYXRvcj0iMTk2OjM2LTE5Njo0MiIgbmFtZT0iZmhpcjpFbGVtZW50IiB4c2k6dHlwZT0iTmFtZWRUeXBlU3BlY2lmaWVyIi8+CiAgICAgICAgIDwvb3BlcmFuZD4KICAgICAgICAgPG9wZXJhbmQgbmFtZT0idXJsIj4KICAgICAgICAgICAgPG9wZXJhbmRUeXBlU3BlY2lmaWVyIGxvY2F0b3I9IjE5Njo0OS0xOTY6NTQiIG5hbWU9InQ6U3RyaW5nIiB4c2k6dHlwZT0iTmFtZWRUeXBlU3BlY2lmaWVyIi8+CiAgICAgICAgIDwvb3BlcmFuZD4KICAgICAgPC9kZWY+CiAgICAgIDxkZWYgbG9jYXRvcj0iMjA2OjEtMjA3OjQxIiBuYW1lPSJFeHRlbnNpb24iIGNvbnRleHQ9IlBhdGllbnQiIGFjY2Vzc0xldmVsPSJQdWJsaWMiIHhzaTp0eXBlPSJGdW5jdGlvbkRlZiI+CiAgICAgICAgIDxhbm5vdGF0aW9uIHhzaTp0eXBlPSJhOkFubm90YXRpb24iPgogICAgICAgICAgICA8YTp0IG5hbWU9ImRlc2NyaXB0aW9uIiB2YWx1ZT0iUmV0dXJucyB0aGUgc2luZ2xlIGV4dGVuc2lvbiAoaWYgcHJlc2VudCkgb24gdGhlIGdpdmVuIGVsZW1lbnQgd2l0aCB0aGUgc3BlY2lmaWVkIHVybC4iLz4KICAgICAgICAgICAgPGE6dCBuYW1lPSJjb21tZW50IiB2YWx1ZT0iVGhpcyBmdW5jdGlvbiB1c2VzIHNpbmdsZXRvbiBmcm9tIHRvIGVuc3VyZSB0aGF0IGEgcnVuLXRpbWUgZXhjZXB0aW9uIGlzIHRocm93biBpZiB0aGVyZSYjeGE7aXMgbW9yZSB0aGFuIG9uZSBleHRlbnNpb24gb24gdGhlIGdpdmVuIHJlc291cmNlIHdpdGggdGhlIHNwZWNpZmllZCB1cmwuIi8+CiAgICAgICAgIDwvYW5ub3RhdGlvbj4KICAgICAgICAgPGV4cHJlc3Npb24gbG9jYXRvcj0iMjA3OjMtMjA3OjQxIiB4c2k6dHlwZT0iU2luZ2xldG9uRnJvbSI+CiAgICAgICAgICAgIDxvcGVyYW5kIGxvY2F0b3I9IjIwNzoxOC0yMDc6NDEiIG5hbWU9IkV4dGVuc2lvbnMiIHhzaTp0eXBlPSJGdW5jdGlvblJlZiI+CiAgICAgICAgICAgICAgIDxvcGVyYW5kIGxvY2F0b3I9IjIwNzoyOS0yMDc6MzUiIG5hbWU9ImVsZW1lbnQiIHhzaTp0eXBlPSJPcGVyYW5kUmVmIi8+CiAgICAgICAgICAgICAgIDxvcGVyYW5kIGxvY2F0b3I9IjIwNzozOC0yMDc6NDAiIG5hbWU9InVybCIgeHNpOnR5cGU9Ik9wZXJhbmRSZWYiLz4KICAgICAgICAgICAgPC9vcGVyYW5kPgogICAgICAgICA8L2V4cHJlc3Npb24+CiAgICAgICAgIDxvcGVyYW5kIG5hbWU9ImVsZW1lbnQiPgogICAgICAgICAgICA8b3BlcmFuZFR5cGVTcGVjaWZpZXIgbG9jYXRvcj0iMjA2OjM1LTIwNjo0MSIgbmFtZT0iZmhpcjpFbGVtZW50IiB4c2k6dHlwZT0iTmFtZWRUeXBlU3BlY2lmaWVyIi8+CiAgICAgICAgIDwvb3BlcmFuZD4KICAgICAgICAgPG9wZXJhbmQgbmFtZT0idXJsIj4KICAgICAgICAgICAgPG9wZXJhbmRUeXBlU3BlY2lmaWVyIGxvY2F0b3I9IjIwNjo0OC0yMDY6NTMiIG5hbWU9InQ6U3RyaW5nIiB4c2k6dHlwZT0iTmFtZWRUeXBlU3BlY2lmaWVyIi8+CiAgICAgICAgIDwvb3BlcmFuZD4KICAgICAgPC9kZWY+CiAgICAgIDxkZWYgbG9jYXRvcj0iMjE1OjEtMjE4OjEwIiBuYW1lPSJNb2RpZmllckV4dGVuc2lvbnMiIGNvbnRleHQ9IlBhdGllbnQiIGFjY2Vzc0xldmVsPSJQdWJsaWMiIHhzaTp0eXBlPSJGdW5jdGlvbkRlZiI+CiAgICAgICAgIDxhbm5vdGF0aW9uIHhzaTp0eXBlPSJhOkFubm90YXRpb24iPgogICAgICAgICAgICA8YTp0IG5hbWU9ImRlc2NyaXB0aW9uIiB2YWx1ZT0iUmV0dXJucyBhbnkgbW9kaWZpZXIgZXh0ZW5zaW9ucyBkZWZpbmVkIG9uIHRoZSBnaXZlbiByZXNvdXJjZSB3aXRoIHRoZSBzcGVjaWZpZWQgdXJsLiIvPgogICAgICAgICAgICA8YTp0IG5hbWU9ImNvbW1lbnQiIHZhbHVlPSJOT1RFOiBFeHRlbnNpb25zIGFyZSBub3QgdGhlIHByZWZlcnJlZCBhcHByb2FjaCwgYnV0IGFyZSB1c2VkIGFzIGEgd2F5IHRvIGFjY2VzcyYjeGE7Y29udGVudCB0aGF0IGlzIGRlZmluZWQgYnkgZXh0ZW5zaW9ucyBidXQgbm90IHlldCBzdXJmYWNlZCBpbiB0aGUmI3hhO0NRTCBtb2RlbCBpbmZvLiIvPgogICAgICAgICA8L2Fubm90YXRpb24+CiAgICAgICAgIDxleHByZXNzaW9uIGxvY2F0b3I9IjIxNjozLTIxODoxMCIgeHNpOnR5cGU9IlF1ZXJ5Ij4KICAgICAgICAgICAgPHNvdXJjZSBsb2NhdG9yPSIyMTY6My0yMTY6MzYiIGFsaWFzPSJFIj4KICAgICAgICAgICAgICAgPGV4cHJlc3Npb24gbG9jYXRvcj0iMjE2OjMtMjE2OjM0IiBwYXRoPSJtb2RpZmllckV4dGVuc2lvbiIgeHNpOnR5cGU9IlByb3BlcnR5Ij4KICAgICAgICAgICAgICAgICAgPHNvdXJjZSBuYW1lPSJkb21haW5SZXNvdXJjZSIgeHNpOnR5cGU9Ik9wZXJhbmRSZWYiLz4KICAgICAgICAgICAgICAgPC9leHByZXNzaW9uPgogICAgICAgICAgICA8L3NvdXJjZT4KICAgICAgICAgICAgPHdoZXJlIGxvY2F0b3I9IjIxNzo0LTIxNzoyMCIgeHNpOnR5cGU9IkVxdWFsIj4KICAgICAgICAgICAgICAgPG9wZXJhbmQgbmFtZT0iVG9TdHJpbmciIGxpYnJhcnlOYW1lPSJGSElSSGVscGVycyIgeHNpOnR5cGU9IkZ1bmN0aW9uUmVmIj4KICAgICAgICAgICAgICAgICAgPG9wZXJhbmQgbG9jYXRvcj0iMjE3OjEwLTIxNzoxNCIgcGF0aD0idXJsIiBzY29wZT0iRSIgeHNpOnR5cGU9IlByb3BlcnR5Ii8+CiAgICAgICAgICAgICAgIDwvb3BlcmFuZD4KICAgICAgICAgICAgICAgPG9wZXJhbmQgbG9jYXRvcj0iMjE3OjE4LTIxNzoyMCIgbmFtZT0idXJsIiB4c2k6dHlwZT0iT3BlcmFuZFJlZiIvPgogICAgICAgICAgICA8L3doZXJlPgogICAgICAgICAgICA8cmV0dXJuIGxvY2F0b3I9IjIxODozLTIxODoxMCI+CiAgICAgICAgICAgICAgIDxleHByZXNzaW9uIGxvY2F0b3I9IjIxODoxMCIgbmFtZT0iRSIgeHNpOnR5cGU9IkFsaWFzUmVmIi8+CiAgICAgICAgICAgIDwvcmV0dXJuPgogICAgICAgICA8L2V4cHJlc3Npb24+CiAgICAgICAgIDxvcGVyYW5kIG5hbWU9ImRvbWFpblJlc291cmNlIj4KICAgICAgICAgICAgPG9wZXJhbmRUeXBlU3BlY2lmaWVyIGxvY2F0b3I9IjIxNTo1MS0yMTU6NjQiIG5hbWU9ImZoaXI6RG9tYWluUmVzb3VyY2UiIHhzaTp0eXBlPSJOYW1lZFR5cGVTcGVjaWZpZXIiLz4KICAgICAgICAgPC9vcGVyYW5kPgogICAgICAgICA8b3BlcmFuZCBuYW1lPSJ1cmwiPgogICAgICAgICAgICA8b3BlcmFuZFR5cGVTcGVjaWZpZXIgbG9jYXRvcj0iMjE1OjcxLTIxNTo3NiIgbmFtZT0idDpTdHJpbmciIHhzaTp0eXBlPSJOYW1lZFR5cGVTcGVjaWZpZXIiLz4KICAgICAgICAgPC9vcGVyYW5kPgogICAgICA8L2RlZj4KICAgICAgPGRlZiBsb2NhdG9yPSIyMjU6MS0yMjY6NTYiIG5hbWU9Ik1vZGlmaWVyRXh0ZW5zaW9uIiBjb250ZXh0PSJQYXRpZW50IiBhY2Nlc3NMZXZlbD0iUHVibGljIiB4c2k6dHlwZT0iRnVuY3Rpb25EZWYiPgogICAgICAgICA8YW5ub3RhdGlvbiB4c2k6dHlwZT0iYTpBbm5vdGF0aW9uIj4KICAgICAgICAgICAgPGE6dCBuYW1lPSJkZXNjcmlwdGlvbiIgdmFsdWU9IlJldHVybnMgdGhlIHNpbmdsZSBtb2RpZmllciBleHRlbnNpb24gKGlmIHByZXNlbnQpIG9uIHRoZSBnaXZlbiByZXNvdXJjZSB3aXRoIHRoZSBzcGVjaWZpZWQgdXJsLiIvPgogICAgICAgICAgICA8YTp0IG5hbWU9ImNvbW1lbnQiIHZhbHVlPSJUaGlzIGZ1bmN0aW9uIHVzZXMgc2luZ2xldG9uIGZyb20gdG8gZW5zdXJlIHRoYXQgYSBydW4tdGltZSBleGNlcHRpb24gaXMgdGhyb3duIGlmIHRoZXJlJiN4YTtpcyBtb3JlIHRoYW4gb25lIGV4dGVuc2lvbiBvbiB0aGUgZ2l2ZW4gcmVzb3VyY2Ugd2l0aCB0aGUgc3BlY2lmaWVkIHVybC4iLz4KICAgICAgICAgPC9hbm5vdGF0aW9uPgogICAgICAgICA8ZXhwcmVzc2lvbiBsb2NhdG9yPSIyMjY6My0yMjY6NTYiIHhzaTp0eXBlPSJTaW5nbGV0b25Gcm9tIj4KICAgICAgICAgICAgPG9wZXJhbmQgbG9jYXRvcj0iMjI2OjE4LTIyNjo1NiIgbmFtZT0iTW9kaWZpZXJFeHRlbnNpb25zIiB4c2k6dHlwZT0iRnVuY3Rpb25SZWYiPgogICAgICAgICAgICAgICA8b3BlcmFuZCBsb2NhdG9yPSIyMjY6MzctMjI2OjUwIiBuYW1lPSJkb21haW5SZXNvdXJjZSIgeHNpOnR5cGU9Ik9wZXJhbmRSZWYiLz4KICAgICAgICAgICAgICAgPG9wZXJhbmQgbG9jYXRvcj0iMjI2OjUzLTIyNjo1NSIgbmFtZT0idXJsIiB4c2k6dHlwZT0iT3BlcmFuZFJlZiIvPgogICAgICAgICAgICA8L29wZXJhbmQ+CiAgICAgICAgIDwvZXhwcmVzc2lvbj4KICAgICAgICAgPG9wZXJhbmQgbmFtZT0iZG9tYWluUmVzb3VyY2UiPgogICAgICAgICAgICA8b3BlcmFuZFR5cGVTcGVjaWZpZXIgbG9jYXRvcj0iMjI1OjUwLTIyNTo2MyIgbmFtZT0iZmhpcjpEb21haW5SZXNvdXJjZSIgeHNpOnR5cGU9Ik5hbWVkVHlwZVNwZWNpZmllciIvPgogICAgICAgICA8L29wZXJhbmQ+CiAgICAgICAgIDxvcGVyYW5kIG5hbWU9InVybCI+CiAgICAgICAgICAgIDxvcGVyYW5kVHlwZVNwZWNpZmllciBsb2NhdG9yPSIyMjU6NzAtMjI1Ojc1IiBuYW1lPSJ0OlN0cmluZyIgeHNpOnR5cGU9Ik5hbWVkVHlwZVNwZWNpZmllciIvPgogICAgICAgICA8L29wZXJhbmQ+CiAgICAgIDwvZGVmPgogICAgICA8ZGVmIGxvY2F0b3I9IjIzMzoxLTIzNjoxMCIgbmFtZT0iTW9kaWZpZXJFeHRlbnNpb25zIiBjb250ZXh0PSJQYXRpZW50IiBhY2Nlc3NMZXZlbD0iUHVibGljIiB4c2k6dHlwZT0iRnVuY3Rpb25EZWYiPgogICAgICAgICA8YW5ub3RhdGlvbiB4c2k6dHlwZT0iYTpBbm5vdGF0aW9uIj4KICAgICAgICAgICAgPGE6dCBuYW1lPSJkZXNjcmlwdGlvbiIgdmFsdWU9IlJldHVybnMgYW55IG1vZGlmaWVyIGV4dGVuc2lvbnMgZGVmaW5lZCBvbiB0aGUgZ2l2ZW4gZWxlbWVudCB3aXRoIHRoZSBzcGVjaWZpZWQgdXJsLiIvPgogICAgICAgICAgICA8YTp0IG5hbWU9ImNvbW1lbnQiIHZhbHVlPSJOT1RFOiBFeHRlbnNpb25zIGFyZSBub3QgdGhlIHByZWZlcnJlZCBhcHByb2FjaCwgYnV0IGFyZSB1c2VkIGFzIGEgd2F5IHRvIGFjY2VzcyYjeGE7Y29udGVudCB0aGF0IGlzIGRlZmluZWQgYnkgZXh0ZW5zaW9ucyBidXQgbm90IHlldCBzdXJmYWNlZCBpbiB0aGUgQ1FMIG1vZGVsIGluZm8uIi8+CiAgICAgICAgIDwvYW5ub3RhdGlvbj4KICAgICAgICAgPGV4cHJlc3Npb24gbG9jYXRvcj0iMjM0OjMtMjM2OjEwIiB4c2k6dHlwZT0iUXVlcnkiPgogICAgICAgICAgICA8c291cmNlIGxvY2F0b3I9IjIzNDozLTIzNDoyOSIgYWxpYXM9IkUiPgogICAgICAgICAgICAgICA8ZXhwcmVzc2lvbiBsb2NhdG9yPSIyMzQ6My0yMzQ6MjciIHBhdGg9Im1vZGlmaWVyRXh0ZW5zaW9uIiB4c2k6dHlwZT0iUHJvcGVydHkiPgogICAgICAgICAgICAgICAgICA8c291cmNlIG5hbWU9ImVsZW1lbnQiIHhzaTp0eXBlPSJPcGVyYW5kUmVmIi8+CiAgICAgICAgICAgICAgIDwvZXhwcmVzc2lvbj4KICAgICAgICAgICAgPC9zb3VyY2U+CiAgICAgICAgICAgIDx3aGVyZSBsb2NhdG9yPSIyMzU6NC0yMzU6MjAiIHhzaTp0eXBlPSJFcXVhbCI+CiAgICAgICAgICAgICAgIDxvcGVyYW5kIG5hbWU9IlRvU3RyaW5nIiBsaWJyYXJ5TmFtZT0iRkhJUkhlbHBlcnMiIHhzaTp0eXBlPSJGdW5jdGlvblJlZiI+CiAgICAgICAgICAgICAgICAgIDxvcGVyYW5kIGxvY2F0b3I9IjIzNToxMC0yMzU6MTQiIHBhdGg9InVybCIgc2NvcGU9IkUiIHhzaTp0eXBlPSJQcm9wZXJ0eSIvPgogICAgICAgICAgICAgICA8L29wZXJhbmQ+CiAgICAgICAgICAgICAgIDxvcGVyYW5kIGxvY2F0b3I9IjIzNToxOC0yMzU6MjAiIG5hbWU9InVybCIgeHNpOnR5cGU9Ik9wZXJhbmRSZWYiLz4KICAgICAgICAgICAgPC93aGVyZT4KICAgICAgICAgICAgPHJldHVybiBsb2NhdG9yPSIyMzY6My0yMzY6MTAiPgogICAgICAgICAgICAgICA8ZXhwcmVzc2lvbiBsb2NhdG9yPSIyMzY6MTAiIG5hbWU9IkUiIHhzaTp0eXBlPSJBbGlhc1JlZiIvPgogICAgICAgICAgICA8L3JldHVybj4KICAgICAgICAgPC9leHByZXNzaW9uPgogICAgICAgICA8b3BlcmFuZCBuYW1lPSJlbGVtZW50Ij4KICAgICAgICAgICAgPG9wZXJhbmRUeXBlU3BlY2lmaWVyIGxvY2F0b3I9IjIzMzo0NC0yMzM6NTgiIG5hbWU9ImZoaXI6QmFja2JvbmVFbGVtZW50IiB4c2k6dHlwZT0iTmFtZWRUeXBlU3BlY2lmaWVyIi8+CiAgICAgICAgIDwvb3BlcmFuZD4KICAgICAgICAgPG9wZXJhbmQgbmFtZT0idXJsIj4KICAgICAgICAgICAgPG9wZXJhbmRUeXBlU3BlY2lmaWVyIGxvY2F0b3I9IjIzMzo2NS0yMzM6NzAiIG5hbWU9InQ6U3RyaW5nIiB4c2k6dHlwZT0iTmFtZWRUeXBlU3BlY2lmaWVyIi8+CiAgICAgICAgIDwvb3BlcmFuZD4KICAgICAgPC9kZWY+CiAgICAgIDxkZWYgbG9jYXRvcj0iMjQzOjEtMjQ0OjQ5IiBuYW1lPSJNb2RpZmllckV4dGVuc2lvbiIgY29udGV4dD0iUGF0aWVudCIgYWNjZXNzTGV2ZWw9IlB1YmxpYyIgeHNpOnR5cGU9IkZ1bmN0aW9uRGVmIj4KICAgICAgICAgPGFubm90YXRpb24geHNpOnR5cGU9ImE6QW5ub3RhdGlvbiI+CiAgICAgICAgICAgIDxhOnQgbmFtZT0iZGVzY3JpcHRpb24iIHZhbHVlPSJSZXR1cm5zIHRoZSBzaW5nbGUgbW9kaWZpZXIgZXh0ZW5zaW9uIChpZiBwcmVzZW50KSBvbiB0aGUgZ2l2ZW4gZWxlbWVudCB3aXRoIHRoZSBzcGVjaWZpZWQgdXJsLiIvPgogICAgICAgICAgICA8YTp0IG5hbWU9ImNvbW1lbnQiIHZhbHVlPSJUaGlzIGZ1bmN0aW9uIHVzZXMgc2luZ2xldG9uIGZyb20gdG8gZW5zdXJlIHRoYXQgYSBydW4tdGltZSBleGNlcHRpb24gaXMgdGhyb3duIGlmIHRoZXJlJiN4YTtpcyBtb3JlIHRoYW4gb25lIGV4dGVuc2lvbiBvbiB0aGUgZ2l2ZW4gcmVzb3VyY2Ugd2l0aCB0aGUgc3BlY2lmaWVkIHVybC4iLz4KICAgICAgICAgPC9hbm5vdGF0aW9uPgogICAgICAgICA8ZXhwcmVzc2lvbiBsb2NhdG9yPSIyNDQ6My0yNDQ6NDkiIHhzaTp0eXBlPSJTaW5nbGV0b25Gcm9tIj4KICAgICAgICAgICAgPG9wZXJhbmQgbG9jYXRvcj0iMjQ0OjE4LTI0NDo0OSIgbmFtZT0iTW9kaWZpZXJFeHRlbnNpb25zIiB4c2k6dHlwZT0iRnVuY3Rpb25SZWYiPgogICAgICAgICAgICAgICA8b3BlcmFuZCBsb2NhdG9yPSIyNDQ6MzctMjQ0OjQzIiBuYW1lPSJlbGVtZW50IiB4c2k6dHlwZT0iT3BlcmFuZFJlZiIvPgogICAgICAgICAgICAgICA8b3BlcmFuZCBsb2NhdG9yPSIyNDQ6NDYtMjQ0OjQ4IiBuYW1lPSJ1cmwiIHhzaTp0eXBlPSJPcGVyYW5kUmVmIi8+CiAgICAgICAgICAgIDwvb3BlcmFuZD4KICAgICAgICAgPC9leHByZXNzaW9uPgogICAgICAgICA8b3BlcmFuZCBuYW1lPSJlbGVtZW50Ij4KICAgICAgICAgICAgPG9wZXJhbmRUeXBlU3BlY2lmaWVyIGxvY2F0b3I9IjI0Mzo0My0yNDM6NTciIG5hbWU9ImZoaXI6QmFja2JvbmVFbGVtZW50IiB4c2k6dHlwZT0iTmFtZWRUeXBlU3BlY2lmaWVyIi8+CiAgICAgICAgIDwvb3BlcmFuZD4KICAgICAgICAgPG9wZXJhbmQgbmFtZT0idXJsIj4KICAgICAgICAgICAgPG9wZXJhbmRUeXBlU3BlY2lmaWVyIGxvY2F0b3I9IjI0Mzo2NC0yNDM6NjkiIG5hbWU9InQ6U3RyaW5nIiB4c2k6dHlwZT0iTmFtZWRUeXBlU3BlY2lmaWVyIi8+CiAgICAgICAgIDwvb3BlcmFuZD4KICAgICAgPC9kZWY+CiAgICAgIDxkZWYgbG9jYXRvcj0iMjUxOjEtMjU0OjEwIiBuYW1lPSJCYXNlRXh0ZW5zaW9ucyIgY29udGV4dD0iUGF0aWVudCIgYWNjZXNzTGV2ZWw9IlB1YmxpYyIgeHNpOnR5cGU9IkZ1bmN0aW9uRGVmIj4KICAgICAgICAgPGFubm90YXRpb24geHNpOnR5cGU9ImE6QW5ub3RhdGlvbiI+CiAgICAgICAgICAgIDxhOnQgbmFtZT0iZGVzY3JpcHRpb24iIHZhbHVlPSJSZXR1cm5zIGFueSBiYXNlLUZISVIgZXh0ZW5zaW9ucyBkZWZpbmVkIG9uIHRoZSBnaXZlbiByZXNvdXJjZSB3aXRoIHRoZSBzcGVjaWZpZWQgaWQuIi8+CiAgICAgICAgICAgIDxhOnQgbmFtZT0iY29tbWVudCIgdmFsdWU9Ik5PVEU6IEV4dGVuc2lvbnMgYXJlIG5vdCB0aGUgcHJlZmVycmVkIGFwcHJvYWNoLCBidXQgYXJlIHVzZWQgYXMgYSB3YXkgdG8gYWNjZXNzJiN4YTtjb250ZW50IHRoYXQgaXMgZGVmaW5lZCBieSBleHRlbnNpb25zIGJ1dCBub3QgeWV0IHN1cmZhY2VkIGluIHRoZSBDUUwgbW9kZWwgaW5mby4iLz4KICAgICAgICAgPC9hbm5vdGF0aW9uPgogICAgICAgICA8ZXhwcmVzc2lvbiBsb2NhdG9yPSIyNTI6My0yNTQ6MTAiIHhzaTp0eXBlPSJRdWVyeSI+CiAgICAgICAgICAgIDxzb3VyY2UgbG9jYXRvcj0iMjUyOjMtMjUyOjI4IiBhbGlhcz0iRSI+CiAgICAgICAgICAgICAgIDxleHByZXNzaW9uIGxvY2F0b3I9IjI1MjozLTI1MjoyNiIgcGF0aD0iZXh0ZW5zaW9uIiB4c2k6dHlwZT0iUHJvcGVydHkiPgogICAgICAgICAgICAgICAgICA8c291cmNlIG5hbWU9ImRvbWFpblJlc291cmNlIiB4c2k6dHlwZT0iT3BlcmFuZFJlZiIvPgogICAgICAgICAgICAgICA8L2V4cHJlc3Npb24+CiAgICAgICAgICAgIDwvc291cmNlPgogICAgICAgICAgICA8d2hlcmUgbG9jYXRvcj0iMjUzOjQtMjUzOjY2IiB4c2k6dHlwZT0iRXF1YWwiPgogICAgICAgICAgICAgICA8b3BlcmFuZCBuYW1lPSJUb1N0cmluZyIgbGlicmFyeU5hbWU9IkZISVJIZWxwZXJzIiB4c2k6dHlwZT0iRnVuY3Rpb25SZWYiPgogICAgICAgICAgICAgICAgICA8b3BlcmFuZCBsb2NhdG9yPSIyNTM6MTAtMjUzOjE0IiBwYXRoPSJ1cmwiIHNjb3BlPSJFIiB4c2k6dHlwZT0iUHJvcGVydHkiLz4KICAgICAgICAgICAgICAgPC9vcGVyYW5kPgogICAgICAgICAgICAgICA8b3BlcmFuZCBsb2NhdG9yPSIyNTM6MTgtMjUzOjY2IiB4c2k6dHlwZT0iQ29uY2F0ZW5hdGUiPgogICAgICAgICAgICAgICAgICA8b3BlcmFuZCBsb2NhdG9yPSIyNTM6MTktMjUzOjYwIiB2YWx1ZVR5cGU9InQ6U3RyaW5nIiB2YWx1ZT0iaHR0cDovL2hsNy5vcmcvZmhpci9TdHJ1Y3R1cmVEZWZpbml0aW9uLyIgeHNpOnR5cGU9IkxpdGVyYWwiLz4KICAgICAgICAgICAgICAgICAgPG9wZXJhbmQgbG9jYXRvcj0iMjUzOjY0LTI1Mzo2NSIgbmFtZT0iaWQiIHhzaTp0eXBlPSJPcGVyYW5kUmVmIi8+CiAgICAgICAgICAgICAgIDwvb3BlcmFuZD4KICAgICAgICAgICAgPC93aGVyZT4KICAgICAgICAgICAgPHJldHVybiBsb2NhdG9yPSIyNTQ6My0yNTQ6MTAiPgogICAgICAgICAgICAgICA8ZXhwcmVzc2lvbiBsb2NhdG9yPSIyNTQ6MTAiIG5hbWU9IkUiIHhzaTp0eXBlPSJBbGlhc1JlZiIvPgogICAgICAgICAgICA8L3JldHVybj4KICAgICAgICAgPC9leHByZXNzaW9uPgogICAgICAgICA8b3BlcmFuZCBuYW1lPSJkb21haW5SZXNvdXJjZSI+CiAgICAgICAgICAgIDxvcGVyYW5kVHlwZVNwZWNpZmllciBsb2NhdG9yPSIyNTE6NDctMjUxOjYwIiBuYW1lPSJmaGlyOkRvbWFpblJlc291cmNlIiB4c2k6dHlwZT0iTmFtZWRUeXBlU3BlY2lmaWVyIi8+CiAgICAgICAgIDwvb3BlcmFuZD4KICAgICAgICAgPG9wZXJhbmQgbmFtZT0iaWQiPgogICAgICAgICAgICA8b3BlcmFuZFR5cGVTcGVjaWZpZXIgbG9jYXRvcj0iMjUxOjY2LTI1MTo3MSIgbmFtZT0idDpTdHJpbmciIHhzaTp0eXBlPSJOYW1lZFR5cGVTcGVjaWZpZXIiLz4KICAgICAgICAgPC9vcGVyYW5kPgogICAgICA8L2RlZj4KICAgICAgPGRlZiBsb2NhdG9yPSIyNjE6MS0yNjI6NTEiIG5hbWU9IkJhc2VFeHRlbnNpb24iIGNvbnRleHQ9IlBhdGllbnQiIGFjY2Vzc0xldmVsPSJQdWJsaWMiIHhzaTp0eXBlPSJGdW5jdGlvbkRlZiI+CiAgICAgICAgIDxhbm5vdGF0aW9uIHhzaTp0eXBlPSJhOkFubm90YXRpb24iPgogICAgICAgICAgICA8YTp0IG5hbWU9ImRlc2NyaXB0aW9uIiB2YWx1ZT0iUmV0dXJucyB0aGUgc2luZ2xlIGJhc2UtRkhJUiBleHRlbnNpb24gKGlmIHByZXNlbnQpIG9uIHRoZSBnaXZlbiByZXNvdXJjZSB3aXRoIHRoZSBzcGVjaWZpZWQgaWQuIi8+CiAgICAgICAgICAgIDxhOnQgbmFtZT0iY29tbWVudCIgdmFsdWU9IlRoaXMgZnVuY3Rpb24gdXNlcyBzaW5nbGV0b24gZnJvbSB0byBlbnN1cmUgdGhhdCBhIHJ1bi10aW1lIGV4Y2VwdGlvbiBpcyB0aHJvd24gaWYgdGhlcmUmI3hhO2lzIG1vcmUgdGhhbiBvbmUgZXh0ZW5zaW9uIG9uIHRoZSBnaXZlbiByZXNvdXJjZSB3aXRoIHRoZSBzcGVjaWZpZWQgdXJsLiIvPgogICAgICAgICA8L2Fubm90YXRpb24+CiAgICAgICAgIDxleHByZXNzaW9uIGxvY2F0b3I9IjI2MjozLTI2Mjo1MSIgeHNpOnR5cGU9IlNpbmdsZXRvbkZyb20iPgogICAgICAgICAgICA8b3BlcmFuZCBsb2NhdG9yPSIyNjI6MTgtMjYyOjUxIiBuYW1lPSJCYXNlRXh0ZW5zaW9ucyIgeHNpOnR5cGU9IkZ1bmN0aW9uUmVmIj4KICAgICAgICAgICAgICAgPG9wZXJhbmQgbG9jYXRvcj0iMjYyOjMzLTI2Mjo0NiIgbmFtZT0iZG9tYWluUmVzb3VyY2UiIHhzaTp0eXBlPSJPcGVyYW5kUmVmIi8+CiAgICAgICAgICAgICAgIDxvcGVyYW5kIGxvY2F0b3I9IjI2Mjo0OS0yNjI6NTAiIG5hbWU9ImlkIiB4c2k6dHlwZT0iT3BlcmFuZFJlZiIvPgogICAgICAgICAgICA8L29wZXJhbmQ+CiAgICAgICAgIDwvZXhwcmVzc2lvbj4KICAgICAgICAgPG9wZXJhbmQgbmFtZT0iZG9tYWluUmVzb3VyY2UiPgogICAgICAgICAgICA8b3BlcmFuZFR5cGVTcGVjaWZpZXIgbG9jYXRvcj0iMjYxOjQ2LTI2MTo1OSIgbmFtZT0iZmhpcjpEb21haW5SZXNvdXJjZSIgeHNpOnR5cGU9Ik5hbWVkVHlwZVNwZWNpZmllciIvPgogICAgICAgICA8L29wZXJhbmQ+CiAgICAgICAgIDxvcGVyYW5kIG5hbWU9ImlkIj4KICAgICAgICAgICAgPG9wZXJhbmRUeXBlU3BlY2lmaWVyIGxvY2F0b3I9IjI2MTo2NS0yNjE6NzAiIG5hbWU9InQ6U3RyaW5nIiB4c2k6dHlwZT0iTmFtZWRUeXBlU3BlY2lmaWVyIi8+CiAgICAgICAgIDwvb3BlcmFuZD4KICAgICAgPC9kZWY+CiAgICAgIDxkZWYgbG9jYXRvcj0iMjY5OjEtMjcyOjEwIiBuYW1lPSJCYXNlRXh0ZW5zaW9ucyIgY29udGV4dD0iUGF0aWVudCIgYWNjZXNzTGV2ZWw9IlB1YmxpYyIgeHNpOnR5cGU9IkZ1bmN0aW9uRGVmIj4KICAgICAgICAgPGFubm90YXRpb24geHNpOnR5cGU9ImE6QW5ub3RhdGlvbiI+CiAgICAgICAgICAgIDxhOnQgbmFtZT0iZGVzY3JpcHRpb24iIHZhbHVlPSJSZXR1cm5zIGFueSBiYXNlLUZISVIgZXh0ZW5zaW9ucyBkZWZpbmVkIG9uIHRoZSBnaXZlbiBlbGVtZW50IHdpdGggdGhlIHNwZWNpZmllZCBpZC4iLz4KICAgICAgICAgICAgPGE6dCBuYW1lPSJjb21tZW50IiB2YWx1ZT0iTk9URTogRXh0ZW5zaW9ucyBhcmUgbm90IHRoZSBwcmVmZXJyZWQgYXBwcm9hY2gsIGJ1dCBhcmUgdXNlZCBhcyBhIHdheSB0byBhY2Nlc3MmI3hhO2NvbnRlbnQgdGhhdCBpcyBkZWZpbmVkIGJ5IGV4dGVuc2lvbnMgYnV0IG5vdCB5ZXQgc3VyZmFjZWQgaW4gdGhlIENRTCBtb2RlbCBpbmZvLiIvPgogICAgICAgICA8L2Fubm90YXRpb24+CiAgICAgICAgIDxleHByZXNzaW9uIGxvY2F0b3I9IjI3MDozLTI3MjoxMCIgeHNpOnR5cGU9IlF1ZXJ5Ij4KICAgICAgICAgICAgPHNvdXJjZSBsb2NhdG9yPSIyNzA6My0yNzA6MjEiIGFsaWFzPSJFIj4KICAgICAgICAgICAgICAgPGV4cHJlc3Npb24gbG9jYXRvcj0iMjcwOjMtMjcwOjE5IiBwYXRoPSJleHRlbnNpb24iIHhzaTp0eXBlPSJQcm9wZXJ0eSI+CiAgICAgICAgICAgICAgICAgIDxzb3VyY2UgbmFtZT0iZWxlbWVudCIgeHNpOnR5cGU9Ik9wZXJhbmRSZWYiLz4KICAgICAgICAgICAgICAgPC9leHByZXNzaW9uPgogICAgICAgICAgICA8L3NvdXJjZT4KICAgICAgICAgICAgPHdoZXJlIGxvY2F0b3I9IjI3MTo0LTI3MTo2NiIgeHNpOnR5cGU9IkVxdWFsIj4KICAgICAgICAgICAgICAgPG9wZXJhbmQgbmFtZT0iVG9TdHJpbmciIGxpYnJhcnlOYW1lPSJGSElSSGVscGVycyIgeHNpOnR5cGU9IkZ1bmN0aW9uUmVmIj4KICAgICAgICAgICAgICAgICAgPG9wZXJhbmQgbG9jYXRvcj0iMjcxOjEwLTI3MToxNCIgcGF0aD0idXJsIiBzY29wZT0iRSIgeHNpOnR5cGU9IlByb3BlcnR5Ii8+CiAgICAgICAgICAgICAgIDwvb3BlcmFuZD4KICAgICAgICAgICAgICAgPG9wZXJhbmQgbG9jYXRvcj0iMjcxOjE4LTI3MTo2NiIgeHNpOnR5cGU9IkNvbmNhdGVuYXRlIj4KICAgICAgICAgICAgICAgICAgPG9wZXJhbmQgbG9jYXRvcj0iMjcxOjE5LTI3MTo2MCIgdmFsdWVUeXBlPSJ0OlN0cmluZyIgdmFsdWU9Imh0dHA6Ly9obDcub3JnL2ZoaXIvU3RydWN0dXJlRGVmaW5pdGlvbi8iIHhzaTp0eXBlPSJMaXRlcmFsIi8+CiAgICAgICAgICAgICAgICAgIDxvcGVyYW5kIGxvY2F0b3I9IjI3MTo2NC0yNzE6NjUiIG5hbWU9ImlkIiB4c2k6dHlwZT0iT3BlcmFuZFJlZiIvPgogICAgICAgICAgICAgICA8L29wZXJhbmQ+CiAgICAgICAgICAgIDwvd2hlcmU+CiAgICAgICAgICAgIDxyZXR1cm4gbG9jYXRvcj0iMjcyOjMtMjcyOjEwIj4KICAgICAgICAgICAgICAgPGV4cHJlc3Npb24gbG9jYXRvcj0iMjcyOjEwIiBuYW1lPSJFIiB4c2k6dHlwZT0iQWxpYXNSZWYiLz4KICAgICAgICAgICAgPC9yZXR1cm4+CiAgICAgICAgIDwvZXhwcmVzc2lvbj4KICAgICAgICAgPG9wZXJhbmQgbmFtZT0iZWxlbWVudCI+CiAgICAgICAgICAgIDxvcGVyYW5kVHlwZVNwZWNpZmllciBsb2NhdG9yPSIyNjk6NDAtMjY5OjQ2IiBuYW1lPSJmaGlyOkVsZW1lbnQiIHhzaTp0eXBlPSJOYW1lZFR5cGVTcGVjaWZpZXIiLz4KICAgICAgICAgPC9vcGVyYW5kPgogICAgICAgICA8b3BlcmFuZCBuYW1lPSJpZCI+CiAgICAgICAgICAgIDxvcGVyYW5kVHlwZVNwZWNpZmllciBsb2NhdG9yPSIyNjk6NTItMjY5OjU3IiBuYW1lPSJ0OlN0cmluZyIgeHNpOnR5cGU9Ik5hbWVkVHlwZVNwZWNpZmllciIvPgogICAgICAgICA8L29wZXJhbmQ+CiAgICAgIDwvZGVmPgogICAgICA8ZGVmIGxvY2F0b3I9IjI3OToxLTI4MDo0NCIgbmFtZT0iQmFzZUV4dGVuc2lvbiIgY29udGV4dD0iUGF0aWVudCIgYWNjZXNzTGV2ZWw9IlB1YmxpYyIgeHNpOnR5cGU9IkZ1bmN0aW9uRGVmIj4KICAgICAgICAgPGFubm90YXRpb24geHNpOnR5cGU9ImE6QW5ub3RhdGlvbiI+CiAgICAgICAgICAgIDxhOnQgbmFtZT0iZGVzY3JpcHRpb24iIHZhbHVlPSJSZXR1cm5zIHRoZSBzaW5nbGUgYmFzZS1GSElSIGV4dGVuc2lvbiAoaWYgcHJlc2VudCkgb24gdGhlIGdpdmVuIGVsZW1lbnQgd2l0aCB0aGUgc3BlY2lmaWVkIGlkLiIvPgogICAgICAgICAgICA8YTp0IG5hbWU9ImNvbW1lbnQiIHZhbHVlPSJUaGlzIGZ1bmN0aW9uIHVzZXMgc2luZ2xldG9uIGZyb20gdG8gZW5zdXJlIHRoYXQgYSBydW4tdGltZSBleGNlcHRpb24gaXMgdGhyb3duIGlmIHRoZXJlJiN4YTtpcyBtb3JlIHRoYW4gb25lIGV4dGVuc2lvbiBvbiB0aGUgZ2l2ZW4gcmVzb3VyY2Ugd2l0aCB0aGUgc3BlY2lmaWVkIHVybC4iLz4KICAgICAgICAgPC9hbm5vdGF0aW9uPgogICAgICAgICA8ZXhwcmVzc2lvbiBsb2NhdG9yPSIyODA6My0yODA6NDQiIHhzaTp0eXBlPSJTaW5nbGV0b25Gcm9tIj4KICAgICAgICAgICAgPG9wZXJhbmQgbG9jYXRvcj0iMjgwOjE4LTI4MDo0NCIgbmFtZT0iQmFzZUV4dGVuc2lvbnMiIHhzaTp0eXBlPSJGdW5jdGlvblJlZiI+CiAgICAgICAgICAgICAgIDxvcGVyYW5kIGxvY2F0b3I9IjI4MDozMy0yODA6MzkiIG5hbWU9ImVsZW1lbnQiIHhzaTp0eXBlPSJPcGVyYW5kUmVmIi8+CiAgICAgICAgICAgICAgIDxvcGVyYW5kIGxvY2F0b3I9IjI4MDo0Mi0yODA6NDMiIG5hbWU9ImlkIiB4c2k6dHlwZT0iT3BlcmFuZFJlZiIvPgogICAgICAgICAgICA8L29wZXJhbmQ+CiAgICAgICAgIDwvZXhwcmVzc2lvbj4KICAgICAgICAgPG9wZXJhbmQgbmFtZT0iZWxlbWVudCI+CiAgICAgICAgICAgIDxvcGVyYW5kVHlwZVNwZWNpZmllciBsb2NhdG9yPSIyNzk6MzktMjc5OjQ1IiBuYW1lPSJmaGlyOkVsZW1lbnQiIHhzaTp0eXBlPSJOYW1lZFR5cGVTcGVjaWZpZXIiLz4KICAgICAgICAgPC9vcGVyYW5kPgogICAgICAgICA8b3BlcmFuZCBuYW1lPSJpZCI+CiAgICAgICAgICAgIDxvcGVyYW5kVHlwZVNwZWNpZmllciBsb2NhdG9yPSIyNzk6NTEtMjc5OjU2IiBuYW1lPSJ0OlN0cmluZyIgeHNpOnR5cGU9Ik5hbWVkVHlwZVNwZWNpZmllciIvPgogICAgICAgICA8L29wZXJhbmQ+CiAgICAgIDwvZGVmPgogICAgICA8ZGVmIGxvY2F0b3I9IjI4NzoxLTI5MDoxMCIgbmFtZT0iQmFzZU1vZGlmaWVyRXh0ZW5zaW9ucyIgY29udGV4dD0iUGF0aWVudCIgYWNjZXNzTGV2ZWw9IlB1YmxpYyIgeHNpOnR5cGU9IkZ1bmN0aW9uRGVmIj4KICAgICAgICAgPGFubm90YXRpb24geHNpOnR5cGU9ImE6QW5ub3RhdGlvbiI+CiAgICAgICAgICAgIDxhOnQgbmFtZT0iZGVzY3JpcHRpb24iIHZhbHVlPSJSZXR1cm5zIGFueSBiYXNlLUZISVIgbW9kaWZpZXIgZXh0ZW5zaW9ucyBkZWZpbmVkIG9uIHRoZSBnaXZlbiByZXNvdXJjZSB3aXRoIHRoZSBzcGVjaWZpZWQgaWQuIi8+CiAgICAgICAgICAgIDxhOnQgbmFtZT0iY29tbWVudCIgdmFsdWU9Ik5PVEU6IEV4dGVuc2lvbnMgYXJlIG5vdCB0aGUgcHJlZmVycmVkIGFwcHJvYWNoLCBidXQgYXJlIHVzZWQgYXMgYSB3YXkgdG8gYWNjZXNzJiN4YTtjb250ZW50IHRoYXQgaXMgZGVmaW5lZCBieSBleHRlbnNpb25zIGJ1dCBub3QgeWV0IHN1cmZhY2VkIGluIHRoZSBDUUwgbW9kZWwgaW5mby4iLz4KICAgICAgICAgPC9hbm5vdGF0aW9uPgogICAgICAgICA8ZXhwcmVzc2lvbiBsb2NhdG9yPSIyODg6My0yOTA6MTAiIHhzaTp0eXBlPSJRdWVyeSI+CiAgICAgICAgICAgIDxzb3VyY2UgbG9jYXRvcj0iMjg4OjMtMjg4OjM2IiBhbGlhcz0iRSI+CiAgICAgICAgICAgICAgIDxleHByZXNzaW9uIGxvY2F0b3I9IjI4ODozLTI4ODozNCIgcGF0aD0ibW9kaWZpZXJFeHRlbnNpb24iIHhzaTp0eXBlPSJQcm9wZXJ0eSI+CiAgICAgICAgICAgICAgICAgIDxzb3VyY2UgbmFtZT0iZG9tYWluUmVzb3VyY2UiIHhzaTp0eXBlPSJPcGVyYW5kUmVmIi8+CiAgICAgICAgICAgICAgIDwvZXhwcmVzc2lvbj4KICAgICAgICAgICAgPC9zb3VyY2U+CiAgICAgICAgICAgIDx3aGVyZSBsb2NhdG9yPSIyODk6NC0yODk6NjYiIHhzaTp0eXBlPSJFcXVhbCI+CiAgICAgICAgICAgICAgIDxvcGVyYW5kIG5hbWU9IlRvU3RyaW5nIiBsaWJyYXJ5TmFtZT0iRkhJUkhlbHBlcnMiIHhzaTp0eXBlPSJGdW5jdGlvblJlZiI+CiAgICAgICAgICAgICAgICAgIDxvcGVyYW5kIGxvY2F0b3I9IjI4OToxMC0yODk6MTQiIHBhdGg9InVybCIgc2NvcGU9IkUiIHhzaTp0eXBlPSJQcm9wZXJ0eSIvPgogICAgICAgICAgICAgICA8L29wZXJhbmQ+CiAgICAgICAgICAgICAgIDxvcGVyYW5kIGxvY2F0b3I9IjI4OToxOC0yODk6NjYiIHhzaTp0eXBlPSJDb25jYXRlbmF0ZSI+CiAgICAgICAgICAgICAgICAgIDxvcGVyYW5kIGxvY2F0b3I9IjI4OToxOS0yODk6NjAiIHZhbHVlVHlwZT0idDpTdHJpbmciIHZhbHVlPSJodHRwOi8vaGw3Lm9yZy9maGlyL1N0cnVjdHVyZURlZmluaXRpb24vIiB4c2k6dHlwZT0iTGl0ZXJhbCIvPgogICAgICAgICAgICAgICAgICA8b3BlcmFuZCBsb2NhdG9yPSIyODk6NjQtMjg5OjY1IiBuYW1lPSJpZCIgeHNpOnR5cGU9Ik9wZXJhbmRSZWYiLz4KICAgICAgICAgICAgICAgPC9vcGVyYW5kPgogICAgICAgICAgICA8L3doZXJlPgogICAgICAgICAgICA8cmV0dXJuIGxvY2F0b3I9IjI5MDozLTI5MDoxMCI+CiAgICAgICAgICAgICAgIDxleHByZXNzaW9uIGxvY2F0b3I9IjI5MDoxMCIgbmFtZT0iRSIgeHNpOnR5cGU9IkFsaWFzUmVmIi8+CiAgICAgICAgICAgIDwvcmV0dXJuPgogICAgICAgICA8L2V4cHJlc3Npb24+CiAgICAgICAgIDxvcGVyYW5kIG5hbWU9ImRvbWFpblJlc291cmNlIj4KICAgICAgICAgICAgPG9wZXJhbmRUeXBlU3BlY2lmaWVyIGxvY2F0b3I9IjI4Nzo1NS0yODc6NjgiIG5hbWU9ImZoaXI6RG9tYWluUmVzb3VyY2UiIHhzaTp0eXBlPSJOYW1lZFR5cGVTcGVjaWZpZXIiLz4KICAgICAgICAgPC9vcGVyYW5kPgogICAgICAgICA8b3BlcmFuZCBuYW1lPSJpZCI+CiAgICAgICAgICAgIDxvcGVyYW5kVHlwZVNwZWNpZmllciBsb2NhdG9yPSIyODc6NzQtMjg3Ojc5IiBuYW1lPSJ0OlN0cmluZyIgeHNpOnR5cGU9Ik5hbWVkVHlwZVNwZWNpZmllciIvPgogICAgICAgICA8L29wZXJhbmQ+CiAgICAgIDwvZGVmPgogICAgICA8ZGVmIGxvY2F0b3I9IjI5NzoxLTI5ODo1OSIgbmFtZT0iQmFzZU1vZGlmaWVyRXh0ZW5zaW9uIiBjb250ZXh0PSJQYXRpZW50IiBhY2Nlc3NMZXZlbD0iUHVibGljIiB4c2k6dHlwZT0iRnVuY3Rpb25EZWYiPgogICAgICAgICA8YW5ub3RhdGlvbiB4c2k6dHlwZT0iYTpBbm5vdGF0aW9uIj4KICAgICAgICAgICAgPGE6dCBuYW1lPSJkZXNjcmlwdGlvbiIgdmFsdWU9IlJldHVybnMgdGhlIHNpbmdsZSBiYXNlLUZISVIgbW9kaWZpZXIgZXh0ZW5zaW9uIChpZiBwcmVzZW50KSBvbiB0aGUgZ2l2ZW4gcmVzb3VyY2Ugd2l0aCB0aGUgc3BlY2lmaWVkIGlkLiIvPgogICAgICAgICAgICA8YTp0IG5hbWU9ImNvbW1lbnQiIHZhbHVlPSJUaGlzIGZ1bmN0aW9uIHVzZXMgc2luZ2xldG9uIGZyb20gdG8gZW5zdXJlIHRoYXQgYSBydW4tdGltZSBleGNlcHRpb24gaXMgdGhyb3duIGlmIHRoZXJlJiN4YTtpcyBtb3JlIHRoYW4gb25lIGV4dGVuc2lvbiBvbiB0aGUgZ2l2ZW4gcmVzb3VyY2Ugd2l0aCB0aGUgc3BlY2lmaWVkIHVybC4iLz4KICAgICAgICAgPC9hbm5vdGF0aW9uPgogICAgICAgICA8ZXhwcmVzc2lvbiBsb2NhdG9yPSIyOTg6My0yOTg6NTkiIHhzaTp0eXBlPSJTaW5nbGV0b25Gcm9tIj4KICAgICAgICAgICAgPG9wZXJhbmQgbG9jYXRvcj0iMjk4OjE4LTI5ODo1OSIgbmFtZT0iQmFzZU1vZGlmaWVyRXh0ZW5zaW9ucyIgeHNpOnR5cGU9IkZ1bmN0aW9uUmVmIj4KICAgICAgICAgICAgICAgPG9wZXJhbmQgbG9jYXRvcj0iMjk4OjQxLTI5ODo1NCIgbmFtZT0iZG9tYWluUmVzb3VyY2UiIHhzaTp0eXBlPSJPcGVyYW5kUmVmIi8+CiAgICAgICAgICAgICAgIDxvcGVyYW5kIGxvY2F0b3I9IjI5ODo1Ny0yOTg6NTgiIG5hbWU9ImlkIiB4c2k6dHlwZT0iT3BlcmFuZFJlZiIvPgogICAgICAgICAgICA8L29wZXJhbmQ+CiAgICAgICAgIDwvZXhwcmVzc2lvbj4KICAgICAgICAgPG9wZXJhbmQgbmFtZT0iZG9tYWluUmVzb3VyY2UiPgogICAgICAgICAgICA8b3BlcmFuZFR5cGVTcGVjaWZpZXIgbG9jYXRvcj0iMjk3OjU0LTI5Nzo2NyIgbmFtZT0iZmhpcjpEb21haW5SZXNvdXJjZSIgeHNpOnR5cGU9Ik5hbWVkVHlwZVNwZWNpZmllciIvPgogICAgICAgICA8L29wZXJhbmQ+CiAgICAgICAgIDxvcGVyYW5kIG5hbWU9ImlkIj4KICAgICAgICAgICAgPG9wZXJhbmRUeXBlU3BlY2lmaWVyIGxvY2F0b3I9IjI5Nzo3My0yOTc6NzgiIG5hbWU9InQ6U3RyaW5nIiB4c2k6dHlwZT0iTmFtZWRUeXBlU3BlY2lmaWVyIi8+CiAgICAgICAgIDwvb3BlcmFuZD4KICAgICAgPC9kZWY+CiAgICAgIDxkZWYgbG9jYXRvcj0iMzA1OjEtMzA4OjEwIiBuYW1lPSJCYXNlTW9kaWZpZXJFeHRlbnNpb25zIiBjb250ZXh0PSJQYXRpZW50IiBhY2Nlc3NMZXZlbD0iUHVibGljIiB4c2k6dHlwZT0iRnVuY3Rpb25EZWYiPgogICAgICAgICA8YW5ub3RhdGlvbiB4c2k6dHlwZT0iYTpBbm5vdGF0aW9uIj4KICAgICAgICAgICAgPGE6dCBuYW1lPSJkZXNjcmlwdGlvbiIgdmFsdWU9IlJldHVybnMgYW55IGJhc2UtRkhJUiBtb2RpZmllciBleHRlbnNpb25zIGRlZmluZWQgb24gdGhlIGdpdmVuIGVsZW1lbnQgd2l0aCB0aGUgc3BlY2lmaWVkIGlkLiIvPgogICAgICAgICAgICA8YTp0IG5hbWU9ImNvbW1lbnQiIHZhbHVlPSJOT1RFOiBFeHRlbnNpb25zIGFyZSBub3QgdGhlIHByZWZlcnJlZCBhcHByb2FjaCwgYnV0IGFyZSB1c2VkIGFzIGEgd2F5IHRvIGFjY2VzcyYjeGE7Y29udGVudCB0aGF0IGlzIGRlZmluZWQgYnkgZXh0ZW5zaW9ucyBidXQgbm90IHlldCBzdXJmYWNlZCBpbiB0aGUgQ1FMIG1vZGVsIGluZm8uIi8+CiAgICAgICAgIDwvYW5ub3RhdGlvbj4KICAgICAgICAgPGV4cHJlc3Npb24gbG9jYXRvcj0iMzA2OjMtMzA4OjEwIiB4c2k6dHlwZT0iUXVlcnkiPgogICAgICAgICAgICA8c291cmNlIGxvY2F0b3I9IjMwNjozLTMwNjoyOSIgYWxpYXM9IkUiPgogICAgICAgICAgICAgICA8ZXhwcmVzc2lvbiBsb2NhdG9yPSIzMDY6My0zMDY6MjciIHBhdGg9Im1vZGlmaWVyRXh0ZW5zaW9uIiB4c2k6dHlwZT0iUHJvcGVydHkiPgogICAgICAgICAgICAgICAgICA8c291cmNlIG5hbWU9ImVsZW1lbnQiIHhzaTp0eXBlPSJPcGVyYW5kUmVmIi8+CiAgICAgICAgICAgICAgIDwvZXhwcmVzc2lvbj4KICAgICAgICAgICAgPC9zb3VyY2U+CiAgICAgICAgICAgIDx3aGVyZSBsb2NhdG9yPSIzMDc6NC0zMDc6NjYiIHhzaTp0eXBlPSJFcXVhbCI+CiAgICAgICAgICAgICAgIDxvcGVyYW5kIG5hbWU9IlRvU3RyaW5nIiBsaWJyYXJ5TmFtZT0iRkhJUkhlbHBlcnMiIHhzaTp0eXBlPSJGdW5jdGlvblJlZiI+CiAgICAgICAgICAgICAgICAgIDxvcGVyYW5kIGxvY2F0b3I9IjMwNzoxMC0zMDc6MTQiIHBhdGg9InVybCIgc2NvcGU9IkUiIHhzaTp0eXBlPSJQcm9wZXJ0eSIvPgogICAgICAgICAgICAgICA8L29wZXJhbmQ+CiAgICAgICAgICAgICAgIDxvcGVyYW5kIGxvY2F0b3I9IjMwNzoxOC0zMDc6NjYiIHhzaTp0eXBlPSJDb25jYXRlbmF0ZSI+CiAgICAgICAgICAgICAgICAgIDxvcGVyYW5kIGxvY2F0b3I9IjMwNzoxOS0zMDc6NjAiIHZhbHVlVHlwZT0idDpTdHJpbmciIHZhbHVlPSJodHRwOi8vaGw3Lm9yZy9maGlyL1N0cnVjdHVyZURlZmluaXRpb24vIiB4c2k6dHlwZT0iTGl0ZXJhbCIvPgogICAgICAgICAgICAgICAgICA8b3BlcmFuZCBsb2NhdG9yPSIzMDc6NjQtMzA3OjY1IiBuYW1lPSJpZCIgeHNpOnR5cGU9Ik9wZXJhbmRSZWYiLz4KICAgICAgICAgICAgICAgPC9vcGVyYW5kPgogICAgICAgICAgICA8L3doZXJlPgogICAgICAgICAgICA8cmV0dXJuIGxvY2F0b3I9IjMwODozLTMwODoxMCI+CiAgICAgICAgICAgICAgIDxleHByZXNzaW9uIGxvY2F0b3I9IjMwODoxMCIgbmFtZT0iRSIgeHNpOnR5cGU9IkFsaWFzUmVmIi8+CiAgICAgICAgICAgIDwvcmV0dXJuPgogICAgICAgICA8L2V4cHJlc3Npb24+CiAgICAgICAgIDxvcGVyYW5kIG5hbWU9ImVsZW1lbnQiPgogICAgICAgICAgICA8b3BlcmFuZFR5cGVTcGVjaWZpZXIgbG9jYXRvcj0iMzA1OjQ4LTMwNTo2MiIgbmFtZT0iZmhpcjpCYWNrYm9uZUVsZW1lbnQiIHhzaTp0eXBlPSJOYW1lZFR5cGVTcGVjaWZpZXIiLz4KICAgICAgICAgPC9vcGVyYW5kPgogICAgICAgICA8b3BlcmFuZCBuYW1lPSJpZCI+CiAgICAgICAgICAgIDxvcGVyYW5kVHlwZVNwZWNpZmllciBsb2NhdG9yPSIzMDU6NjgtMzA1OjczIiBuYW1lPSJ0OlN0cmluZyIgeHNpOnR5cGU9Ik5hbWVkVHlwZVNwZWNpZmllciIvPgogICAgICAgICA8L29wZXJhbmQ+CiAgICAgIDwvZGVmPgogICAgICA8ZGVmIGxvY2F0b3I9IjMxNToxLTMxNjo1MiIgbmFtZT0iQmFzZU1vZGlmaWVyRXh0ZW5zaW9uIiBjb250ZXh0PSJQYXRpZW50IiBhY2Nlc3NMZXZlbD0iUHVibGljIiB4c2k6dHlwZT0iRnVuY3Rpb25EZWYiPgogICAgICAgICA8YW5ub3RhdGlvbiB4c2k6dHlwZT0iYTpBbm5vdGF0aW9uIj4KICAgICAgICAgICAgPGE6dCBuYW1lPSJkZXNjcmlwdGlvbiIgdmFsdWU9IlJldHVybnMgdGhlIHNpbmdsZSBiYXNlLUZISVIgZXh0ZW5zaW9uIChpZiBwcmVzZW50KSBvbiB0aGUgZ2l2ZW4gZWxlbWVudCB3aXRoIHRoZSBzcGVjaWZpZWQgaWQuIi8+CiAgICAgICAgICAgIDxhOnQgbmFtZT0iY29tbWVudCIgdmFsdWU9IlRoaXMgZnVuY3Rpb24gdXNlcyBzaW5nbGV0b24gZnJvbSB0byBlbnN1cmUgdGhhdCBhIHJ1bi10aW1lIGV4Y2VwdGlvbiBpcyB0aHJvd24gaWYgdGhlcmUmI3hhO2lzIG1vcmUgdGhhbiBvbmUgZXh0ZW5zaW9uIG9uIHRoZSBnaXZlbiByZXNvdXJjZSB3aXRoIHRoZSBzcGVjaWZpZWQgdXJsLiIvPgogICAgICAgICA8L2Fubm90YXRpb24+CiAgICAgICAgIDxleHByZXNzaW9uIGxvY2F0b3I9IjMxNjozLTMxNjo1MiIgeHNpOnR5cGU9IlNpbmdsZXRvbkZyb20iPgogICAgICAgICAgICA8b3BlcmFuZCBsb2NhdG9yPSIzMTY6MTgtMzE2OjUyIiBuYW1lPSJCYXNlTW9kaWZpZXJFeHRlbnNpb25zIiB4c2k6dHlwZT0iRnVuY3Rpb25SZWYiPgogICAgICAgICAgICAgICA8b3BlcmFuZCBsb2NhdG9yPSIzMTY6NDEtMzE2OjQ3IiBuYW1lPSJlbGVtZW50IiB4c2k6dHlwZT0iT3BlcmFuZFJlZiIvPgogICAgICAgICAgICAgICA8b3BlcmFuZCBsb2NhdG9yPSIzMTY6NTAtMzE2OjUxIiBuYW1lPSJpZCIgeHNpOnR5cGU9Ik9wZXJhbmRSZWYiLz4KICAgICAgICAgICAgPC9vcGVyYW5kPgogICAgICAgICA8L2V4cHJlc3Npb24+CiAgICAgICAgIDxvcGVyYW5kIG5hbWU9ImVsZW1lbnQiPgogICAgICAgICAgICA8b3BlcmFuZFR5cGVTcGVjaWZpZXIgbG9jYXRvcj0iMzE1OjQ3LTMxNTo2MSIgbmFtZT0iZmhpcjpCYWNrYm9uZUVsZW1lbnQiIHhzaTp0eXBlPSJOYW1lZFR5cGVTcGVjaWZpZXIiLz4KICAgICAgICAgPC9vcGVyYW5kPgogICAgICAgICA8b3BlcmFuZCBuYW1lPSJpZCI+CiAgICAgICAgICAgIDxvcGVyYW5kVHlwZVNwZWNpZmllciBsb2NhdG9yPSIzMTU6NjctMzE1OjcyIiBuYW1lPSJ0OlN0cmluZyIgeHNpOnR5cGU9Ik5hbWVkVHlwZVNwZWNpZmllciIvPgogICAgICAgICA8L29wZXJhbmQ+CiAgICAgIDwvZGVmPgogICA8L3N0YXRlbWVudHM+CjwvbGlicmFyeT4K" - }, - { - "contentType": "application/elm+json", - "data": "ewogICAibGlicmFyeSIgOiB7CiAgICAgICJhbm5vdGF0aW9uIiA6IFsgewogICAgICAgICAidHJhbnNsYXRvclZlcnNpb24iIDogIjIuNy4wIiwKICAgICAgICAgInRyYW5zbGF0b3JPcHRpb25zIiA6ICJFbmFibGVMb2NhdG9ycyxEaXNhYmxlTGlzdERlbW90aW9uLERpc2FibGVMaXN0UHJvbW90aW9uIiwKICAgICAgICAgInR5cGUiIDogIkNxbFRvRWxtSW5mbyIKICAgICAgfSwgewogICAgICAgICAidHlwZSIgOiAiQW5ub3RhdGlvbiIsCiAgICAgICAgICJ0IiA6IFsgewogICAgICAgICAgICAibmFtZSIgOiAiYXV0aG9yIiwKICAgICAgICAgICAgInZhbHVlIiA6ICJCcnluIFJob2RlcyIKICAgICAgICAgfSwgewogICAgICAgICAgICAibmFtZSIgOiAiZGVzY3JpcHRpb24iLAogICAgICAgICAgICAidmFsdWUiIDogIkNvbW1vbiB0ZXJtaW5vbG9naWVzIGFuZCBmdW5jdGlvbnMgdXNlZCBpbiBGSElSLWJhc2VkIENRTCBhcnRpZmFjdHMiCiAgICAgICAgIH0gXQogICAgICB9IF0sCiAgICAgICJpZGVudGlmaWVyIiA6IHsKICAgICAgICAgImlkIiA6ICJGSElSQ29tbW9uIiwKICAgICAgICAgInN5c3RlbSIgOiAiaHR0cDovL2NvbnRlbnQuYWxwaG9yYS5jb20vZmhpci9kcW0iLAogICAgICAgICAidmVyc2lvbiIgOiAiNC4wLjEiCiAgICAgIH0sCiAgICAgICJzY2hlbWFJZGVudGlmaWVyIiA6IHsKICAgICAgICAgImlkIiA6ICJ1cm46aGw3LW9yZzplbG0iLAogICAgICAgICAidmVyc2lvbiIgOiAicjEiCiAgICAgIH0sCiAgICAgICJ1c2luZ3MiIDogewogICAgICAgICAiZGVmIiA6IFsgewogICAgICAgICAgICAibG9jYWxJZGVudGlmaWVyIiA6ICJTeXN0ZW0iLAogICAgICAgICAgICAidXJpIiA6ICJ1cm46aGw3LW9yZzplbG0tdHlwZXM6cjEiCiAgICAgICAgIH0sIHsKICAgICAgICAgICAgImxvY2F0b3IiIDogIjg6MS04OjI2IiwKICAgICAgICAgICAgImxvY2FsSWRlbnRpZmllciIgOiAiRkhJUiIsCiAgICAgICAgICAgICJ1cmkiIDogImh0dHA6Ly9obDcub3JnL2ZoaXIiLAogICAgICAgICAgICAidmVyc2lvbiIgOiAiNC4wLjEiCiAgICAgICAgIH0gXQogICAgICB9LAogICAgICAiaW5jbHVkZXMiIDogewogICAgICAgICAiZGVmIiA6IFsgewogICAgICAgICAgICAibG9jYXRvciIgOiAiMTA6MS0xMDo0MSIsCiAgICAgICAgICAgICJsb2NhbElkZW50aWZpZXIiIDogIkZISVJIZWxwZXJzIiwKICAgICAgICAgICAgInBhdGgiIDogImh0dHA6Ly9jb250ZW50LmFscGhvcmEuY29tL2ZoaXIvZHFtL0RRTUZISVJIZWxwZXJzIgogICAgICAgICB9IF0KICAgICAgfSwKICAgICAgImNvZGVTeXN0ZW1zIiA6IHsKICAgICAgICAgImRlZiIgOiBbIHsKICAgICAgICAgICAgImxvY2F0b3IiIDogIjEyOjEtMTI6MzgiLAogICAgICAgICAgICAibmFtZSIgOiAiTE9JTkMiLAogICAgICAgICAgICAiaWQiIDogImh0dHA6Ly9sb2luYy5vcmciLAogICAgICAgICAgICAiYWNjZXNzTGV2ZWwiIDogIlB1YmxpYyIKICAgICAgICAgfSwgewogICAgICAgICAgICAibG9jYXRvciIgOiAiMTM6MS0xMzo0NyIsCiAgICAgICAgICAgICJuYW1lIiA6ICJTTk9NRURDVCIsCiAgICAgICAgICAgICJpZCIgOiAiaHR0cDovL3Nub21lZC5pbmZvL3NjdCIsCiAgICAgICAgICAgICJhY2Nlc3NMZXZlbCIgOiAiUHVibGljIgogICAgICAgICB9LCB7CiAgICAgICAgICAgICJsb2NhdG9yIiA6ICIxNDoxLTE0Ojc0IiwKICAgICAgICAgICAgIm5hbWUiIDogIlJvbGVDb2RlIiwKICAgICAgICAgICAgImlkIiA6ICJodHRwOi8vdGVybWlub2xvZ3kuaGw3Lm9yZy9Db2RlU3lzdGVtL3YzLVJvbGVDb2RlIiwKICAgICAgICAgICAgImFjY2Vzc0xldmVsIiA6ICJQdWJsaWMiCiAgICAgICAgIH0sIHsKICAgICAgICAgICAgImxvY2F0b3IiIDogIjE1OjEtMTU6ODMiLAogICAgICAgICAgICAibmFtZSIgOiAiRGlhZ25vc2lzIFJvbGUiLAogICAgICAgICAgICAiaWQiIDogImh0dHA6Ly90ZXJtaW5vbG9neS5obDcub3JnL0NvZGVTeXN0ZW0vZGlhZ25vc2lzLXJvbGUiLAogICAgICAgICAgICAiYWNjZXNzTGV2ZWwiIDogIlB1YmxpYyIKICAgICAgICAgfSwgewogICAgICAgICAgICAibG9jYXRvciIgOiAiMTY6MS0xNjo4MiIsCiAgICAgICAgICAgICJuYW1lIiA6ICJSZXF1ZXN0SW50ZW50IiwKICAgICAgICAgICAgImlkIiA6ICJodHRwOi8vdGVybWlub2xvZ3kuaGw3Lm9yZy9Db2RlU3lzdGVtL3JlcXVlc3QtaW50ZW50IiwKICAgICAgICAgICAgImFjY2Vzc0xldmVsIiA6ICJQdWJsaWMiCiAgICAgICAgIH0sIHsKICAgICAgICAgICAgImxvY2F0b3IiIDogIjE3OjEtMTc6MTA2IiwKICAgICAgICAgICAgIm5hbWUiIDogIk1lZGljYXRpb25SZXF1ZXN0Q2F0ZWdvcnkiLAogICAgICAgICAgICAiaWQiIDogImh0dHA6Ly90ZXJtaW5vbG9neS5obDcub3JnL0NvZGVTeXN0ZW0vbWVkaWNhdGlvbnJlcXVlc3QtY2F0ZWdvcnkiLAogICAgICAgICAgICAiYWNjZXNzTGV2ZWwiIDogIlB1YmxpYyIKICAgICAgICAgfSwgewogICAgICAgICAgICAibG9jYXRvciIgOiAiMTg6MS0xODoxMDEiLAogICAgICAgICAgICAibmFtZSIgOiAiQ29uZGl0aW9uQ2xpbmljYWxTdGF0dXNDb2RlcyIsCiAgICAgICAgICAgICJpZCIgOiAiaHR0cDovL3Rlcm1pbm9sb2d5LmhsNy5vcmcvQ29kZVN5c3RlbS9jb25kaXRpb24tY2xpbmljYWwiLAogICAgICAgICAgICAiYWNjZXNzTGV2ZWwiIDogIlB1YmxpYyIKICAgICAgICAgfSwgewogICAgICAgICAgICAibG9jYXRvciIgOiAiMTk6MS0xOToxMDciLAogICAgICAgICAgICAibmFtZSIgOiAiQ29uZGl0aW9uVmVyaWZpY2F0aW9uU3RhdHVzQ29kZXMiLAogICAgICAgICAgICAiaWQiIDogImh0dHA6Ly90ZXJtaW5vbG9neS5obDcub3JnL0NvZGVTeXN0ZW0vY29uZGl0aW9uLXZlci1zdGF0dXMiLAogICAgICAgICAgICAiYWNjZXNzTGV2ZWwiIDogIlB1YmxpYyIKICAgICAgICAgfSwgewogICAgICAgICAgICAibG9jYXRvciIgOiAiMjA6MS0yMDoxMTkiLAogICAgICAgICAgICAibmFtZSIgOiAiQWxsZXJneUludG9sZXJhbmNlQ2xpbmljYWxTdGF0dXNDb2RlcyIsCiAgICAgICAgICAgICJpZCIgOiAiaHR0cDovL3Rlcm1pbm9sb2d5LmhsNy5vcmcvQ29kZVN5c3RlbS9hbGxlcmd5aW50b2xlcmFuY2UtY2xpbmljYWwiLAogICAgICAgICAgICAiYWNjZXNzTGV2ZWwiIDogIlB1YmxpYyIKICAgICAgICAgfSwgewogICAgICAgICAgICAibG9jYXRvciIgOiAiMjE6MS0yMToxMjciLAogICAgICAgICAgICAibmFtZSIgOiAiQWxsZXJneUludG9sZXJhbmNlVmVyaWZpY2F0aW9uU3RhdHVzQ29kZXMiLAogICAgICAgICAgICAiaWQiIDogImh0dHA6Ly90ZXJtaW5vbG9neS5obDcub3JnL0NvZGVTeXN0ZW0vYWxsZXJneWludG9sZXJhbmNlLXZlcmlmaWNhdGlvbiIsCiAgICAgICAgICAgICJhY2Nlc3NMZXZlbCIgOiAiUHVibGljIgogICAgICAgICB9IF0KICAgICAgfSwKICAgICAgImNvZGVzIiA6IHsKICAgICAgICAgImRlZiIgOiBbIHsKICAgICAgICAgICAgImxvY2F0b3IiIDogIjIzOjEtMjM6NjEiLAogICAgICAgICAgICAibmFtZSIgOiAiQmlydGhkYXRlIiwKICAgICAgICAgICAgImlkIiA6ICIyMTExMi04IiwKICAgICAgICAgICAgImRpc3BsYXkiIDogIkJpcnRoIGRhdGUiLAogICAgICAgICAgICAiYWNjZXNzTGV2ZWwiIDogIlB1YmxpYyIsCiAgICAgICAgICAgICJjb2RlU3lzdGVtIiA6IHsKICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjIzOjM0LTIzOjQwIiwKICAgICAgICAgICAgICAgIm5hbWUiIDogIkxPSU5DIgogICAgICAgICAgICB9CiAgICAgICAgIH0sIHsKICAgICAgICAgICAgImxvY2F0b3IiIDogIjI0OjEtMjQ6NTUiLAogICAgICAgICAgICAibmFtZSIgOiAiRGVhZCIsCiAgICAgICAgICAgICJpZCIgOiAiNDE5MDk5MDA5IiwKICAgICAgICAgICAgImRpc3BsYXkiIDogIkRlYWQiLAogICAgICAgICAgICAiYWNjZXNzTGV2ZWwiIDogIlB1YmxpYyIsCiAgICAgICAgICAgICJjb2RlU3lzdGVtIiA6IHsKICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjI0OjMxLTI0OjQwIiwKICAgICAgICAgICAgICAgIm5hbWUiIDogIlNOT01FRENUIgogICAgICAgICAgICB9CiAgICAgICAgIH0sIHsKICAgICAgICAgICAgImxvY2F0b3IiIDogIjI1OjEtMjU6NTYiLAogICAgICAgICAgICAibmFtZSIgOiAiRVIiLAogICAgICAgICAgICAiaWQiIDogIkVSIiwKICAgICAgICAgICAgImRpc3BsYXkiIDogIkVtZXJnZW5jeSByb29tIiwKICAgICAgICAgICAgImFjY2Vzc0xldmVsIiA6ICJQdWJsaWMiLAogICAgICAgICAgICAiY29kZVN5c3RlbSIgOiB7CiAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIyNToyMi0yNTozMSIsCiAgICAgICAgICAgICAgICJuYW1lIiA6ICJSb2xlQ29kZSIKICAgICAgICAgICAgfQogICAgICAgICB9LCB7CiAgICAgICAgICAgICJsb2NhdG9yIiA6ICIyNjoxLTI2OjYzIiwKICAgICAgICAgICAgIm5hbWUiIDogIklDVSIsCiAgICAgICAgICAgICJpZCIgOiAiSUNVIiwKICAgICAgICAgICAgImRpc3BsYXkiIDogIkludGVuc2l2ZSBjYXJlIHVuaXQiLAogICAgICAgICAgICAiYWNjZXNzTGV2ZWwiIDogIlB1YmxpYyIsCiAgICAgICAgICAgICJjb2RlU3lzdGVtIiA6IHsKICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjI2OjI0LTI2OjMzIiwKICAgICAgICAgICAgICAgIm5hbWUiIDogIlJvbGVDb2RlIgogICAgICAgICAgICB9CiAgICAgICAgIH0sIHsKICAgICAgICAgICAgImxvY2F0b3IiIDogIjI3OjEtMjc6NjUiLAogICAgICAgICAgICAibmFtZSIgOiAiQmlsbGluZyIsCiAgICAgICAgICAgICJpZCIgOiAiYmlsbGluZyIsCiAgICAgICAgICAgICJkaXNwbGF5IiA6ICJCaWxsaW5nIiwKICAgICAgICAgICAgImFjY2Vzc0xldmVsIiA6ICJQdWJsaWMiLAogICAgICAgICAgICAiY29kZVN5c3RlbSIgOiB7CiAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIyNzozMi0yNzo0NyIsCiAgICAgICAgICAgICAgICJuYW1lIiA6ICJEaWFnbm9zaXMgUm9sZSIKICAgICAgICAgICAgfQogICAgICAgICB9LCB7CiAgICAgICAgICAgICJsb2NhdG9yIiA6ICIzMDoxLTMwOjU5IiwKICAgICAgICAgICAgIm5hbWUiIDogImFjdGl2ZSIsCiAgICAgICAgICAgICJpZCIgOiAiYWN0aXZlIiwKICAgICAgICAgICAgImFjY2Vzc0xldmVsIiA6ICJQdWJsaWMiLAogICAgICAgICAgICAiY29kZVN5c3RlbSIgOiB7CiAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIzMDozMC0zMDo1OSIsCiAgICAgICAgICAgICAgICJuYW1lIiA6ICJDb25kaXRpb25DbGluaWNhbFN0YXR1c0NvZGVzIgogICAgICAgICAgICB9CiAgICAgICAgIH0sIHsKICAgICAgICAgICAgImxvY2F0b3IiIDogIjMxOjEtMzE6NjciLAogICAgICAgICAgICAibmFtZSIgOiAicmVjdXJyZW5jZSIsCiAgICAgICAgICAgICJpZCIgOiAicmVjdXJyZW5jZSIsCiAgICAgICAgICAgICJhY2Nlc3NMZXZlbCIgOiAiUHVibGljIiwKICAgICAgICAgICAgImNvZGVTeXN0ZW0iIDogewogICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMzE6MzgtMzE6NjciLAogICAgICAgICAgICAgICAibmFtZSIgOiAiQ29uZGl0aW9uQ2xpbmljYWxTdGF0dXNDb2RlcyIKICAgICAgICAgICAgfQogICAgICAgICB9LCB7CiAgICAgICAgICAgICJsb2NhdG9yIiA6ICIzMjoxLTMyOjYxIiwKICAgICAgICAgICAgIm5hbWUiIDogInJlbGFwc2UiLAogICAgICAgICAgICAiaWQiIDogInJlbGFwc2UiLAogICAgICAgICAgICAiYWNjZXNzTGV2ZWwiIDogIlB1YmxpYyIsCiAgICAgICAgICAgICJjb2RlU3lzdGVtIiA6IHsKICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjMyOjMyLTMyOjYxIiwKICAgICAgICAgICAgICAgIm5hbWUiIDogIkNvbmRpdGlvbkNsaW5pY2FsU3RhdHVzQ29kZXMiCiAgICAgICAgICAgIH0KICAgICAgICAgfSwgewogICAgICAgICAgICAibG9jYXRvciIgOiAiMzM6MS0zMzo2MyIsCiAgICAgICAgICAgICJuYW1lIiA6ICJpbmFjdGl2ZSIsCiAgICAgICAgICAgICJpZCIgOiAiaW5hY3RpdmUiLAogICAgICAgICAgICAiYWNjZXNzTGV2ZWwiIDogIlB1YmxpYyIsCiAgICAgICAgICAgICJjb2RlU3lzdGVtIiA6IHsKICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjMzOjM0LTMzOjYzIiwKICAgICAgICAgICAgICAgIm5hbWUiIDogIkNvbmRpdGlvbkNsaW5pY2FsU3RhdHVzQ29kZXMiCiAgICAgICAgICAgIH0KICAgICAgICAgfSwgewogICAgICAgICAgICAibG9jYXRvciIgOiAiMzQ6MS0zNDo2NSIsCiAgICAgICAgICAgICJuYW1lIiA6ICJyZW1pc3Npb24iLAogICAgICAgICAgICAiaWQiIDogInJlbWlzc2lvbiIsCiAgICAgICAgICAgICJhY2Nlc3NMZXZlbCIgOiAiUHVibGljIiwKICAgICAgICAgICAgImNvZGVTeXN0ZW0iIDogewogICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMzQ6MzYtMzQ6NjUiLAogICAgICAgICAgICAgICAibmFtZSIgOiAiQ29uZGl0aW9uQ2xpbmljYWxTdGF0dXNDb2RlcyIKICAgICAgICAgICAgfQogICAgICAgICB9LCB7CiAgICAgICAgICAgICJsb2NhdG9yIiA6ICIzNToxLTM1OjYzIiwKICAgICAgICAgICAgIm5hbWUiIDogInJlc29sdmVkIiwKICAgICAgICAgICAgImlkIiA6ICJyZXNvbHZlZCIsCiAgICAgICAgICAgICJhY2Nlc3NMZXZlbCIgOiAiUHVibGljIiwKICAgICAgICAgICAgImNvZGVTeXN0ZW0iIDogewogICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMzU6MzQtMzU6NjMiLAogICAgICAgICAgICAgICAibmFtZSIgOiAiQ29uZGl0aW9uQ2xpbmljYWxTdGF0dXNDb2RlcyIKICAgICAgICAgICAgfQogICAgICAgICB9LCB7CiAgICAgICAgICAgICJsb2NhdG9yIiA6ICIzODoxLTM4OjcxIiwKICAgICAgICAgICAgIm5hbWUiIDogInVuY29uZmlybWVkIiwKICAgICAgICAgICAgImlkIiA6ICJ1bmNvbmZpcm1lZCIsCiAgICAgICAgICAgICJhY2Nlc3NMZXZlbCIgOiAiUHVibGljIiwKICAgICAgICAgICAgImNvZGVTeXN0ZW0iIDogewogICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMzg6NDAtMzg6NzEiLAogICAgICAgICAgICAgICAibmFtZSIgOiAiQ29uZGl0aW9uVmVyaWZpY2F0aW9uU3RhdHVzQ29kZXMiCiAgICAgICAgICAgIH0KICAgICAgICAgfSwgewogICAgICAgICAgICAibG9jYXRvciIgOiAiMzk6MS0zOTo3MSIsCiAgICAgICAgICAgICJuYW1lIiA6ICJwcm92aXNpb25hbCIsCiAgICAgICAgICAgICJpZCIgOiAicHJvdmlzaW9uYWwiLAogICAgICAgICAgICAiYWNjZXNzTGV2ZWwiIDogIlB1YmxpYyIsCiAgICAgICAgICAgICJjb2RlU3lzdGVtIiA6IHsKICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjM5OjQwLTM5OjcxIiwKICAgICAgICAgICAgICAgIm5hbWUiIDogIkNvbmRpdGlvblZlcmlmaWNhdGlvblN0YXR1c0NvZGVzIgogICAgICAgICAgICB9CiAgICAgICAgIH0sIHsKICAgICAgICAgICAgImxvY2F0b3IiIDogIjQwOjEtNDA6NzMiLAogICAgICAgICAgICAibmFtZSIgOiAiZGlmZmVyZW50aWFsIiwKICAgICAgICAgICAgImlkIiA6ICJkaWZmZXJlbnRpYWwiLAogICAgICAgICAgICAiYWNjZXNzTGV2ZWwiIDogIlB1YmxpYyIsCiAgICAgICAgICAgICJjb2RlU3lzdGVtIiA6IHsKICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjQwOjQyLTQwOjczIiwKICAgICAgICAgICAgICAgIm5hbWUiIDogIkNvbmRpdGlvblZlcmlmaWNhdGlvblN0YXR1c0NvZGVzIgogICAgICAgICAgICB9CiAgICAgICAgIH0sIHsKICAgICAgICAgICAgImxvY2F0b3IiIDogIjQxOjEtNDE6NjciLAogICAgICAgICAgICAibmFtZSIgOiAiY29uZmlybWVkIiwKICAgICAgICAgICAgImlkIiA6ICJjb25maXJtZWQiLAogICAgICAgICAgICAiYWNjZXNzTGV2ZWwiIDogIlB1YmxpYyIsCiAgICAgICAgICAgICJjb2RlU3lzdGVtIiA6IHsKICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjQxOjM2LTQxOjY3IiwKICAgICAgICAgICAgICAgIm5hbWUiIDogIkNvbmRpdGlvblZlcmlmaWNhdGlvblN0YXR1c0NvZGVzIgogICAgICAgICAgICB9CiAgICAgICAgIH0sIHsKICAgICAgICAgICAgImxvY2F0b3IiIDogIjQyOjEtNDI6NjMiLAogICAgICAgICAgICAibmFtZSIgOiAicmVmdXRlZCIsCiAgICAgICAgICAgICJpZCIgOiAicmVmdXRlZCIsCiAgICAgICAgICAgICJhY2Nlc3NMZXZlbCIgOiAiUHVibGljIiwKICAgICAgICAgICAgImNvZGVTeXN0ZW0iIDogewogICAgICAgICAgICAgICAibG9jYXRvciIgOiAiNDI6MzItNDI6NjMiLAogICAgICAgICAgICAgICAibmFtZSIgOiAiQ29uZGl0aW9uVmVyaWZpY2F0aW9uU3RhdHVzQ29kZXMiCiAgICAgICAgICAgIH0KICAgICAgICAgfSwgewogICAgICAgICAgICAibG9jYXRvciIgOiAiNDM6MS00Mzo4MSIsCiAgICAgICAgICAgICJuYW1lIiA6ICJlbnRlcmVkLWluLWVycm9yIiwKICAgICAgICAgICAgImlkIiA6ICJlbnRlcmVkLWluLWVycm9yIiwKICAgICAgICAgICAgImFjY2Vzc0xldmVsIiA6ICJQdWJsaWMiLAogICAgICAgICAgICAiY29kZVN5c3RlbSIgOiB7CiAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICI0Mzo1MC00Mzo4MSIsCiAgICAgICAgICAgICAgICJuYW1lIiA6ICJDb25kaXRpb25WZXJpZmljYXRpb25TdGF0dXNDb2RlcyIKICAgICAgICAgICAgfQogICAgICAgICB9LCB7CiAgICAgICAgICAgICJsb2NhdG9yIiA6ICI0NjoxLTQ2Ojc2IiwKICAgICAgICAgICAgIm5hbWUiIDogImFsbGVyZ3ktYWN0aXZlIiwKICAgICAgICAgICAgImlkIiA6ICJhY3RpdmUiLAogICAgICAgICAgICAiYWNjZXNzTGV2ZWwiIDogIlB1YmxpYyIsCiAgICAgICAgICAgICJjb2RlU3lzdGVtIiA6IHsKICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjQ2OjM4LTQ2Ojc2IiwKICAgICAgICAgICAgICAgIm5hbWUiIDogIkFsbGVyZ3lJbnRvbGVyYW5jZUNsaW5pY2FsU3RhdHVzQ29kZXMiCiAgICAgICAgICAgIH0KICAgICAgICAgfSwgewogICAgICAgICAgICAibG9jYXRvciIgOiAiNDc6MS00Nzo4MCIsCiAgICAgICAgICAgICJuYW1lIiA6ICJhbGxlcmd5LWluYWN0aXZlIiwKICAgICAgICAgICAgImlkIiA6ICJpbmFjdGl2ZSIsCiAgICAgICAgICAgICJhY2Nlc3NMZXZlbCIgOiAiUHVibGljIiwKICAgICAgICAgICAgImNvZGVTeXN0ZW0iIDogewogICAgICAgICAgICAgICAibG9jYXRvciIgOiAiNDc6NDItNDc6ODAiLAogICAgICAgICAgICAgICAibmFtZSIgOiAiQWxsZXJneUludG9sZXJhbmNlQ2xpbmljYWxTdGF0dXNDb2RlcyIKICAgICAgICAgICAgfQogICAgICAgICB9LCB7CiAgICAgICAgICAgICJsb2NhdG9yIiA6ICI0ODoxLTQ4OjgwIiwKICAgICAgICAgICAgIm5hbWUiIDogImFsbGVyZ3ktcmVzb2x2ZWQiLAogICAgICAgICAgICAiaWQiIDogInJlc29sdmVkIiwKICAgICAgICAgICAgImFjY2Vzc0xldmVsIiA6ICJQdWJsaWMiLAogICAgICAgICAgICAiY29kZVN5c3RlbSIgOiB7CiAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICI0ODo0Mi00ODo4MCIsCiAgICAgICAgICAgICAgICJuYW1lIiA6ICJBbGxlcmd5SW50b2xlcmFuY2VDbGluaWNhbFN0YXR1c0NvZGVzIgogICAgICAgICAgICB9CiAgICAgICAgIH0sIHsKICAgICAgICAgICAgImxvY2F0b3IiIDogIjUxOjEtNTE6ODgiLAogICAgICAgICAgICAibmFtZSIgOiAiYWxsZXJneS11bmNvbmZpcm1lZCIsCiAgICAgICAgICAgICJpZCIgOiAidW5jb25maXJtZWQiLAogICAgICAgICAgICAiYWNjZXNzTGV2ZWwiIDogIlB1YmxpYyIsCiAgICAgICAgICAgICJjb2RlU3lzdGVtIiA6IHsKICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjUxOjQ4LTUxOjg4IiwKICAgICAgICAgICAgICAgIm5hbWUiIDogIkFsbGVyZ3lJbnRvbGVyYW5jZVZlcmlmaWNhdGlvblN0YXR1c0NvZGVzIgogICAgICAgICAgICB9CiAgICAgICAgIH0sIHsKICAgICAgICAgICAgImxvY2F0b3IiIDogIjUyOjEtNTI6ODQiLAogICAgICAgICAgICAibmFtZSIgOiAiYWxsZXJneS1jb25maXJtZWQiLAogICAgICAgICAgICAiaWQiIDogImNvbmZpcm1lZCIsCiAgICAgICAgICAgICJhY2Nlc3NMZXZlbCIgOiAiUHVibGljIiwKICAgICAgICAgICAgImNvZGVTeXN0ZW0iIDogewogICAgICAgICAgICAgICAibG9jYXRvciIgOiAiNTI6NDQtNTI6ODQiLAogICAgICAgICAgICAgICAibmFtZSIgOiAiQWxsZXJneUludG9sZXJhbmNlVmVyaWZpY2F0aW9uU3RhdHVzQ29kZXMiCiAgICAgICAgICAgIH0KICAgICAgICAgfSwgewogICAgICAgICAgICAibG9jYXRvciIgOiAiNTM6MS01Mzo4MCIsCiAgICAgICAgICAgICJuYW1lIiA6ICJhbGxlcmd5LXJlZnV0ZWQiLAogICAgICAgICAgICAiaWQiIDogInJlZnV0ZWQiLAogICAgICAgICAgICAiYWNjZXNzTGV2ZWwiIDogIlB1YmxpYyIsCiAgICAgICAgICAgICJjb2RlU3lzdGVtIiA6IHsKICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjUzOjQwLTUzOjgwIiwKICAgICAgICAgICAgICAgIm5hbWUiIDogIkFsbGVyZ3lJbnRvbGVyYW5jZVZlcmlmaWNhdGlvblN0YXR1c0NvZGVzIgogICAgICAgICAgICB9CiAgICAgICAgIH0sIHsKICAgICAgICAgICAgImxvY2F0b3IiIDogIjU2OjEtNTY6ODIiLAogICAgICAgICAgICAibmFtZSIgOiAiQ29tbXVuaXR5IiwKICAgICAgICAgICAgImlkIiA6ICJjb21tdW5pdHkiLAogICAgICAgICAgICAiZGlzcGxheSIgOiAiQ29tbXVuaXR5IiwKICAgICAgICAgICAgImFjY2Vzc0xldmVsIiA6ICJQdWJsaWMiLAogICAgICAgICAgICAiY29kZVN5c3RlbSIgOiB7CiAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICI1NjozNi01Njo2MiIsCiAgICAgICAgICAgICAgICJuYW1lIiA6ICJNZWRpY2F0aW9uUmVxdWVzdENhdGVnb3J5IgogICAgICAgICAgICB9CiAgICAgICAgIH0sIHsKICAgICAgICAgICAgImxvY2F0b3IiIDogIjU3OjEtNTc6ODIiLAogICAgICAgICAgICAibmFtZSIgOiAiRGlzY2hhcmdlIiwKICAgICAgICAgICAgImlkIiA6ICJkaXNjaGFyZ2UiLAogICAgICAgICAgICAiZGlzcGxheSIgOiAiRGlzY2hhcmdlIiwKICAgICAgICAgICAgImFjY2Vzc0xldmVsIiA6ICJQdWJsaWMiLAogICAgICAgICAgICAiY29kZVN5c3RlbSIgOiB7CiAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICI1NzozNi01Nzo2MiIsCiAgICAgICAgICAgICAgICJuYW1lIiA6ICJNZWRpY2F0aW9uUmVxdWVzdENhdGVnb3J5IgogICAgICAgICAgICB9CiAgICAgICAgIH0sIHsKICAgICAgICAgICAgImxvY2F0b3IiIDogIjYwOjEtNjA6NjciLAogICAgICAgICAgICAibmFtZSIgOiAiQUQiLAogICAgICAgICAgICAiaWQiIDogIkFEIiwKICAgICAgICAgICAgImRpc3BsYXkiIDogIkFkbWlzc2lvbiBkaWFnbm9zaXMiLAogICAgICAgICAgICAiYWNjZXNzTGV2ZWwiIDogIlB1YmxpYyIsCiAgICAgICAgICAgICJjb2RlU3lzdGVtIiA6IHsKICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjYwOjIyLTYwOjM3IiwKICAgICAgICAgICAgICAgIm5hbWUiIDogIkRpYWdub3NpcyBSb2xlIgogICAgICAgICAgICB9CiAgICAgICAgIH0sIHsKICAgICAgICAgICAgImxvY2F0b3IiIDogIjYxOjEtNjE6NjciLAogICAgICAgICAgICAibmFtZSIgOiAiREQiLAogICAgICAgICAgICAiaWQiIDogIkREIiwKICAgICAgICAgICAgImRpc3BsYXkiIDogIkRpc2NoYXJnZSBkaWFnbm9zaXMiLAogICAgICAgICAgICAiYWNjZXNzTGV2ZWwiIDogIlB1YmxpYyIsCiAgICAgICAgICAgICJjb2RlU3lzdGVtIiA6IHsKICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjYxOjIyLTYxOjM3IiwKICAgICAgICAgICAgICAgIm5hbWUiIDogIkRpYWdub3NpcyBSb2xlIgogICAgICAgICAgICB9CiAgICAgICAgIH0sIHsKICAgICAgICAgICAgImxvY2F0b3IiIDogIjYyOjEtNjI6NjMiLAogICAgICAgICAgICAibmFtZSIgOiAiQ0MiLAogICAgICAgICAgICAiaWQiIDogIkNDIiwKICAgICAgICAgICAgImRpc3BsYXkiIDogIkNoaWVmIGNvbXBsYWludCIsCiAgICAgICAgICAgICJhY2Nlc3NMZXZlbCIgOiAiUHVibGljIiwKICAgICAgICAgICAgImNvZGVTeXN0ZW0iIDogewogICAgICAgICAgICAgICAibG9jYXRvciIgOiAiNjI6MjItNjI6MzciLAogICAgICAgICAgICAgICAibmFtZSIgOiAiRGlhZ25vc2lzIFJvbGUiCiAgICAgICAgICAgIH0KICAgICAgICAgfSwgewogICAgICAgICAgICAibG9jYXRvciIgOiAiNjM6MS02Mzo2OSIsCiAgICAgICAgICAgICJuYW1lIiA6ICJDTSIsCiAgICAgICAgICAgICJpZCIgOiAiQ00iLAogICAgICAgICAgICAiZGlzcGxheSIgOiAiQ29tb3JiaWRpdHkgZGlhZ25vc2lzIiwKICAgICAgICAgICAgImFjY2Vzc0xldmVsIiA6ICJQdWJsaWMiLAogICAgICAgICAgICAiY29kZVN5c3RlbSIgOiB7CiAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICI2MzoyMi02MzozNyIsCiAgICAgICAgICAgICAgICJuYW1lIiA6ICJEaWFnbm9zaXMgUm9sZSIKICAgICAgICAgICAgfQogICAgICAgICB9LCB7CiAgICAgICAgICAgICJsb2NhdG9yIiA6ICI2NDoxLTY0OjcyIiwKICAgICAgICAgICAgIm5hbWUiIDogInByZS1vcCIsCiAgICAgICAgICAgICJpZCIgOiAicHJlLW9wIiwKICAgICAgICAgICAgImRpc3BsYXkiIDogInByZS1vcCBkaWFnbm9zaXMiLAogICAgICAgICAgICAiYWNjZXNzTGV2ZWwiIDogIlB1YmxpYyIsCiAgICAgICAgICAgICJjb2RlU3lzdGVtIiA6IHsKICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjY0OjMwLTY0OjQ1IiwKICAgICAgICAgICAgICAgIm5hbWUiIDogIkRpYWdub3NpcyBSb2xlIgogICAgICAgICAgICB9CiAgICAgICAgIH0sIHsKICAgICAgICAgICAgImxvY2F0b3IiIDogIjY1OjEtNjU6NzUiLAogICAgICAgICAgICAibmFtZSIgOiAicG9zdC1vcCIsCiAgICAgICAgICAgICJpZCIgOiAicG9zdC1vcCIsCiAgICAgICAgICAgICJkaXNwbGF5IiA6ICJwb3N0LW9wIGRpYWdub3NpcyIsCiAgICAgICAgICAgICJhY2Nlc3NMZXZlbCIgOiAiUHVibGljIiwKICAgICAgICAgICAgImNvZGVTeXN0ZW0iIDogewogICAgICAgICAgICAgICAibG9jYXRvciIgOiAiNjU6MzItNjU6NDciLAogICAgICAgICAgICAgICAibmFtZSIgOiAiRGlhZ25vc2lzIFJvbGUiCiAgICAgICAgICAgIH0KICAgICAgICAgfSwgewogICAgICAgICAgICAibG9jYXRvciIgOiAiNjY6MS02Njo3NSIsCiAgICAgICAgICAgICJuYW1lIiA6ICJiaWxsaW5nIiwKICAgICAgICAgICAgImlkIiA6ICJiaWxsaW5nIiwKICAgICAgICAgICAgImRpc3BsYXkiIDogImJpbGxpbmcgZGlhZ25vc2lzIiwKICAgICAgICAgICAgImFjY2Vzc0xldmVsIiA6ICJQdWJsaWMiLAogICAgICAgICAgICAiY29kZVN5c3RlbSIgOiB7CiAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICI2NjozMi02Njo0NyIsCiAgICAgICAgICAgICAgICJuYW1lIiA6ICJEaWFnbm9zaXMgUm9sZSIKICAgICAgICAgICAgfQogICAgICAgICB9IF0KICAgICAgfSwKICAgICAgImNvbnRleHRzIiA6IHsKICAgICAgICAgImRlZiIgOiBbIHsKICAgICAgICAgICAgImxvY2F0b3IiIDogIjY4OjEtNjg6MTUiLAogICAgICAgICAgICAibmFtZSIgOiAiUGF0aWVudCIKICAgICAgICAgfSBdCiAgICAgIH0sCiAgICAgICJzdGF0ZW1lbnRzIiA6IHsKICAgICAgICAgImRlZiIgOiBbIHsKICAgICAgICAgICAgImxvY2F0b3IiIDogIjY4OjEtNjg6MTUiLAogICAgICAgICAgICAibmFtZSIgOiAiUGF0aWVudCIsCiAgICAgICAgICAgICJjb250ZXh0IiA6ICJQYXRpZW50IiwKICAgICAgICAgICAgImV4cHJlc3Npb24iIDogewogICAgICAgICAgICAgICAidHlwZSIgOiAiU2luZ2xldG9uRnJvbSIsCiAgICAgICAgICAgICAgICJvcGVyYW5kIiA6IHsKICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjY4OjEtNjg6MTUiLAogICAgICAgICAgICAgICAgICAiZGF0YVR5cGUiIDogIntodHRwOi8vaGw3Lm9yZy9maGlyfVBhdGllbnQiLAogICAgICAgICAgICAgICAgICAidGVtcGxhdGVJZCIgOiAiaHR0cDovL2hsNy5vcmcvZmhpci9TdHJ1Y3R1cmVEZWZpbml0aW9uL1BhdGllbnQiLAogICAgICAgICAgICAgICAgICAidHlwZSIgOiAiUmV0cmlldmUiCiAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgfQogICAgICAgICB9LCB7CiAgICAgICAgICAgICJsb2NhdG9yIiA6ICI3OToxLTk5OjQiLAogICAgICAgICAgICAibmFtZSIgOiAiVG9JbnRlcnZhbCIsCiAgICAgICAgICAgICJjb250ZXh0IiA6ICJQYXRpZW50IiwKICAgICAgICAgICAgImFjY2Vzc0xldmVsIiA6ICJQdWJsaWMiLAogICAgICAgICAgICAidHlwZSIgOiAiRnVuY3Rpb25EZWYiLAogICAgICAgICAgICAiYW5ub3RhdGlvbiIgOiBbIHsKICAgICAgICAgICAgICAgInR5cGUiIDogIkFubm90YXRpb24iLAogICAgICAgICAgICAgICAidCIgOiBbIHsKICAgICAgICAgICAgICAgICAgIm5hbWUiIDogImRlc2NyaXB0aW9uIiwKICAgICAgICAgICAgICAgICAgInZhbHVlIiA6ICJOb3JtYWxpemVzIGEgdmFsdWUgdGhhdCBpcyBhIGNob2ljZSBvZiB0aW1pbmctdmFsdWVkIHR5cGVzIHRvIGFuIGVxdWl2YWxlbnQgaW50ZXJ2YWwiCiAgICAgICAgICAgICAgIH0sIHsKICAgICAgICAgICAgICAgICAgIm5hbWUiIDogImNvbW1lbnQiLAogICAgICAgICAgICAgICAgICAidmFsdWUiIDogIk5vcm1hbGl6ZXMgYSBjaG9pY2UgdHlwZSBvZiBGSElSLmRhdGVUaW1lLCBGSElSLlBlcmlvZCwgRkhJUi5UaW1pbmcsIEZISVIuaW5zdGFuY2UsIEZISVIuc3RyaW5nLCBGSElSLkFnZSwgb3IgRkhJUi5SYW5nZSB0eXBlc1xudG8gYW4gZXF1aXZhbGVudCBpbnRlcnZhbC4gVGhpcyBzZWxlY3Rpb24gb2YgY2hvaWNlIHR5cGVzIGlzIGEgc3VwZXJzZXQgb2YgdGhlIG1ham9yaXR5IG9mIGNob2ljZSB0eXBlcyB0aGF0IGFyZSB1c2VkIGFzIHBvc3NpYmxlXG5yZXByZXNlbnRhdGlvbnMgZm9yIHRpbWluZy12YWx1ZWQgZWxlbWVudHMgaW4gRkhJUiwgYWxsb3dpbmcgdGhpcyBmdW5jdGlvbiB0byBiZSB1c2VkIGFjcm9zcyBhbnkgcmVzb3VyY2UuIE5PVEU6IER1ZSB0byB0aGVcbmNvbXBsZXhpdHkgb2YgZGV0ZXJtaW5pbmcgYSBzaW5nbGUgaW50ZXJ2YWwgZnJvbSBhIFRpbWluZyBvciBTdHJpbmcgdHlwZSwgdGhpcyBmdW5jdGlvbiB3aWxsIHRocm93IGEgcnVuLXRpbWUgZXhjZXB0aW9uIGlmIGl0IGlzIHVzZWRcbndpdGggYSBUaW1pbmcgb3IgU3RyaW5nLiBOT1RFOiBUaW1pbmcgaGFzIGJlZW4gbW92ZWQgdG8gVG9UaW1pbmdJbnRlcnZhbC4iCiAgICAgICAgICAgICAgIH0gXQogICAgICAgICAgICB9IF0sCiAgICAgICAgICAgICJleHByZXNzaW9uIiA6IHsKICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjgwOjMtOTk6NCIsCiAgICAgICAgICAgICAgICJ0eXBlIiA6ICJDYXNlIiwKICAgICAgICAgICAgICAgImNhc2VJdGVtIiA6IFsgewogICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiODE6NC04MjoxMTEiLAogICAgICAgICAgICAgICAgICAid2hlbiIgOiB7CiAgICAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICI4MTo5LTgxOjMxIiwKICAgICAgICAgICAgICAgICAgICAgInR5cGUiIDogIklzIiwKICAgICAgICAgICAgICAgICAgICAgIm9wZXJhbmQiIDogewogICAgICAgICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiODE6OS04MToxNCIsCiAgICAgICAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJjaG9pY2UiLAogICAgICAgICAgICAgICAgICAgICAgICAidHlwZSIgOiAiT3BlcmFuZFJlZiIKICAgICAgICAgICAgICAgICAgICAgfSwKICAgICAgICAgICAgICAgICAgICAgImlzVHlwZVNwZWNpZmllciIgOiB7CiAgICAgICAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICI4MToxOS04MTozMSIsCiAgICAgICAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJ7aHR0cDovL2hsNy5vcmcvZmhpcn1kYXRlVGltZSIsCiAgICAgICAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJOYW1lZFR5cGVTcGVjaWZpZXIiCiAgICAgICAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgICAgICAgfSwKICAgICAgICAgICAgICAgICAgInRoZW4iIDogewogICAgICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiODI6Ni04MjoxMTEiLAogICAgICAgICAgICAgICAgICAgICAibG93Q2xvc2VkIiA6IHRydWUsCiAgICAgICAgICAgICAgICAgICAgICJoaWdoQ2xvc2VkIiA6IHRydWUsCiAgICAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJJbnRlcnZhbCIsCiAgICAgICAgICAgICAgICAgICAgICJsb3ciIDogewogICAgICAgICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiODI6MTUtODI6NjEiLAogICAgICAgICAgICAgICAgICAgICAgICAibmFtZSIgOiAiVG9EYXRlVGltZSIsCiAgICAgICAgICAgICAgICAgICAgICAgICJsaWJyYXJ5TmFtZSIgOiAiRkhJUkhlbHBlcnMiLAogICAgICAgICAgICAgICAgICAgICAgICAidHlwZSIgOiAiRnVuY3Rpb25SZWYiLAogICAgICAgICAgICAgICAgICAgICAgICAib3BlcmFuZCIgOiBbIHsKICAgICAgICAgICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjgyOjM4LTgyOjYwIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgInN0cmljdCIgOiBmYWxzZSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgInR5cGUiIDogIkFzIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgIm9wZXJhbmQiIDogewogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiODI6MzgtODI6NDMiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAibmFtZSIgOiAiY2hvaWNlIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk9wZXJhbmRSZWYiCiAgICAgICAgICAgICAgICAgICAgICAgICAgIH0sCiAgICAgICAgICAgICAgICAgICAgICAgICAgICJhc1R5cGVTcGVjaWZpZXIiIDogewogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiODI6NDgtODI6NjAiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAibmFtZSIgOiAie2h0dHA6Ly9obDcub3JnL2ZoaXJ9ZGF0ZVRpbWUiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAidHlwZSIgOiAiTmFtZWRUeXBlU3BlY2lmaWVyIgogICAgICAgICAgICAgICAgICAgICAgICAgICB9CiAgICAgICAgICAgICAgICAgICAgICAgIH0gXQogICAgICAgICAgICAgICAgICAgICB9LAogICAgICAgICAgICAgICAgICAgICAiaGlnaCIgOiB7CiAgICAgICAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICI4Mjo2NC04MjoxMTAiLAogICAgICAgICAgICAgICAgICAgICAgICAibmFtZSIgOiAiVG9EYXRlVGltZSIsCiAgICAgICAgICAgICAgICAgICAgICAgICJsaWJyYXJ5TmFtZSIgOiAiRkhJUkhlbHBlcnMiLAogICAgICAgICAgICAgICAgICAgICAgICAidHlwZSIgOiAiRnVuY3Rpb25SZWYiLAogICAgICAgICAgICAgICAgICAgICAgICAib3BlcmFuZCIgOiBbIHsKICAgICAgICAgICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjgyOjg3LTgyOjEwOSIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICJzdHJpY3QiIDogZmFsc2UsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJBcyIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICJvcGVyYW5kIiA6IHsKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjgyOjg3LTgyOjkyIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIm5hbWUiIDogImNob2ljZSIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJPcGVyYW5kUmVmIgogICAgICAgICAgICAgICAgICAgICAgICAgICB9LAogICAgICAgICAgICAgICAgICAgICAgICAgICAiYXNUeXBlU3BlY2lmaWVyIiA6IHsKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjgyOjk3LTgyOjEwOSIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJ7aHR0cDovL2hsNy5vcmcvZmhpcn1kYXRlVGltZSIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJOYW1lZFR5cGVTcGVjaWZpZXIiCiAgICAgICAgICAgICAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgICAgICAgICAgICAgfSBdCiAgICAgICAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgICAgICAgfQogICAgICAgICAgICAgICB9LCB7CiAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICI4MzozLTg0OjQ5IiwKICAgICAgICAgICAgICAgICAgIndoZW4iIDogewogICAgICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiODM6OC04MzoyOCIsCiAgICAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJJcyIsCiAgICAgICAgICAgICAgICAgICAgICJvcGVyYW5kIiA6IHsKICAgICAgICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjgzOjgtODM6MTMiLAogICAgICAgICAgICAgICAgICAgICAgICAibmFtZSIgOiAiY2hvaWNlIiwKICAgICAgICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk9wZXJhbmRSZWYiCiAgICAgICAgICAgICAgICAgICAgIH0sCiAgICAgICAgICAgICAgICAgICAgICJpc1R5cGVTcGVjaWZpZXIiIDogewogICAgICAgICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiODM6MTgtODM6MjgiLAogICAgICAgICAgICAgICAgICAgICAgICAibmFtZSIgOiAie2h0dHA6Ly9obDcub3JnL2ZoaXJ9UGVyaW9kIiwKICAgICAgICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk5hbWVkVHlwZVNwZWNpZmllciIKICAgICAgICAgICAgICAgICAgICAgfQogICAgICAgICAgICAgICAgICB9LAogICAgICAgICAgICAgICAgICAidGhlbiIgOiB7CiAgICAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICI4NDo1LTg0OjQ5IiwKICAgICAgICAgICAgICAgICAgICAgIm5hbWUiIDogIlRvSW50ZXJ2YWwiLAogICAgICAgICAgICAgICAgICAgICAibGlicmFyeU5hbWUiIDogIkZISVJIZWxwZXJzIiwKICAgICAgICAgICAgICAgICAgICAgInR5cGUiIDogIkZ1bmN0aW9uUmVmIiwKICAgICAgICAgICAgICAgICAgICAgIm9wZXJhbmQiIDogWyB7CiAgICAgICAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICI4NDoyOC04NDo0OCIsCiAgICAgICAgICAgICAgICAgICAgICAgICJzdHJpY3QiIDogZmFsc2UsCiAgICAgICAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJBcyIsCiAgICAgICAgICAgICAgICAgICAgICAgICJvcGVyYW5kIiA6IHsKICAgICAgICAgICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjg0OjI4LTg0OjMzIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgIm5hbWUiIDogImNob2ljZSIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJPcGVyYW5kUmVmIgogICAgICAgICAgICAgICAgICAgICAgICB9LAogICAgICAgICAgICAgICAgICAgICAgICAiYXNUeXBlU3BlY2lmaWVyIiA6IHsKICAgICAgICAgICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjg0OjM4LTg0OjQ4IiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgIm5hbWUiIDogIntodHRwOi8vaGw3Lm9yZy9maGlyfVBlcmlvZCIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJOYW1lZFR5cGVTcGVjaWZpZXIiCiAgICAgICAgICAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgICAgICAgICAgfSBdCiAgICAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgICAgfSwgewogICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiODU6My04NjoxMDciLAogICAgICAgICAgICAgICAgICAid2hlbiIgOiB7CiAgICAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICI4NTo4LTg1OjI5IiwKICAgICAgICAgICAgICAgICAgICAgInR5cGUiIDogIklzIiwKICAgICAgICAgICAgICAgICAgICAgIm9wZXJhbmQiIDogewogICAgICAgICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiODU6OC04NToxMyIsCiAgICAgICAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJjaG9pY2UiLAogICAgICAgICAgICAgICAgICAgICAgICAidHlwZSIgOiAiT3BlcmFuZFJlZiIKICAgICAgICAgICAgICAgICAgICAgfSwKICAgICAgICAgICAgICAgICAgICAgImlzVHlwZVNwZWNpZmllciIgOiB7CiAgICAgICAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICI4NToxOC04NToyOSIsCiAgICAgICAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJ7aHR0cDovL2hsNy5vcmcvZmhpcn1pbnN0YW50IiwKICAgICAgICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk5hbWVkVHlwZVNwZWNpZmllciIKICAgICAgICAgICAgICAgICAgICAgfQogICAgICAgICAgICAgICAgICB9LAogICAgICAgICAgICAgICAgICAidGhlbiIgOiB7CiAgICAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICI4Njo0LTg2OjEwNyIsCiAgICAgICAgICAgICAgICAgICAgICJsb3dDbG9zZWQiIDogdHJ1ZSwKICAgICAgICAgICAgICAgICAgICAgImhpZ2hDbG9zZWQiIDogdHJ1ZSwKICAgICAgICAgICAgICAgICAgICAgInR5cGUiIDogIkludGVydmFsIiwKICAgICAgICAgICAgICAgICAgICAgImxvdyIgOiB7CiAgICAgICAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICI4NjoxMy04Njo1OCIsCiAgICAgICAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJUb0RhdGVUaW1lIiwKICAgICAgICAgICAgICAgICAgICAgICAgImxpYnJhcnlOYW1lIiA6ICJGSElSSGVscGVycyIsCiAgICAgICAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJGdW5jdGlvblJlZiIsCiAgICAgICAgICAgICAgICAgICAgICAgICJvcGVyYW5kIiA6IFsgewogICAgICAgICAgICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiODY6MzYtODY6NTciLAogICAgICAgICAgICAgICAgICAgICAgICAgICAic3RyaWN0IiA6IGZhbHNlLAogICAgICAgICAgICAgICAgICAgICAgICAgICAidHlwZSIgOiAiQXMiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAib3BlcmFuZCIgOiB7CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICI4NjozNi04Njo0MSIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJjaG9pY2UiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAidHlwZSIgOiAiT3BlcmFuZFJlZiIKICAgICAgICAgICAgICAgICAgICAgICAgICAgfSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgImFzVHlwZVNwZWNpZmllciIgOiB7CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICI4Njo0Ni04Njo1NyIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJ7aHR0cDovL2hsNy5vcmcvZmhpcn1pbnN0YW50IiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk5hbWVkVHlwZVNwZWNpZmllciIKICAgICAgICAgICAgICAgICAgICAgICAgICAgfQogICAgICAgICAgICAgICAgICAgICAgICB9IF0KICAgICAgICAgICAgICAgICAgICAgfSwKICAgICAgICAgICAgICAgICAgICAgImhpZ2giIDogewogICAgICAgICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiODY6NjEtODY6MTA2IiwKICAgICAgICAgICAgICAgICAgICAgICAgIm5hbWUiIDogIlRvRGF0ZVRpbWUiLAogICAgICAgICAgICAgICAgICAgICAgICAibGlicmFyeU5hbWUiIDogIkZISVJIZWxwZXJzIiwKICAgICAgICAgICAgICAgICAgICAgICAgInR5cGUiIDogIkZ1bmN0aW9uUmVmIiwKICAgICAgICAgICAgICAgICAgICAgICAgIm9wZXJhbmQiIDogWyB7CiAgICAgICAgICAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICI4Njo4NC04NjoxMDUiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAic3RyaWN0IiA6IGZhbHNlLAogICAgICAgICAgICAgICAgICAgICAgICAgICAidHlwZSIgOiAiQXMiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAib3BlcmFuZCIgOiB7CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICI4Njo4NC04Njo4OSIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJjaG9pY2UiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAidHlwZSIgOiAiT3BlcmFuZFJlZiIKICAgICAgICAgICAgICAgICAgICAgICAgICAgfSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgImFzVHlwZVNwZWNpZmllciIgOiB7CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICI4Njo5NC04NjoxMDUiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAibmFtZSIgOiAie2h0dHA6Ly9obDcub3JnL2ZoaXJ9aW5zdGFudCIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJOYW1lZFR5cGVTcGVjaWZpZXIiCiAgICAgICAgICAgICAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgICAgICAgICAgICAgfSBdCiAgICAgICAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgICAgICAgfQogICAgICAgICAgICAgICB9LCB7CiAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICI4NzozLTg5Ojk3IiwKICAgICAgICAgICAgICAgICAgIndoZW4iIDogewogICAgICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiODc6OC04NzoyNSIsCiAgICAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJJcyIsCiAgICAgICAgICAgICAgICAgICAgICJvcGVyYW5kIiA6IHsKICAgICAgICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjg3OjgtODc6MTMiLAogICAgICAgICAgICAgICAgICAgICAgICAibmFtZSIgOiAiY2hvaWNlIiwKICAgICAgICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk9wZXJhbmRSZWYiCiAgICAgICAgICAgICAgICAgICAgIH0sCiAgICAgICAgICAgICAgICAgICAgICJpc1R5cGVTcGVjaWZpZXIiIDogewogICAgICAgICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiODc6MTgtODc6MjUiLAogICAgICAgICAgICAgICAgICAgICAgICAibmFtZSIgOiAie2h0dHA6Ly9obDcub3JnL2ZoaXJ9QWdlIiwKICAgICAgICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk5hbWVkVHlwZVNwZWNpZmllciIKICAgICAgICAgICAgICAgICAgICAgfQogICAgICAgICAgICAgICAgICB9LAogICAgICAgICAgICAgICAgICAidGhlbiIgOiB7CiAgICAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJJbnRlcnZhbCIsCiAgICAgICAgICAgICAgICAgICAgICJsb3ciIDogewogICAgICAgICAgICAgICAgICAgICAgICAidHlwZSIgOiAiVG9EYXRlVGltZSIsCiAgICAgICAgICAgICAgICAgICAgICAgICJvcGVyYW5kIiA6IHsKICAgICAgICAgICAgICAgICAgICAgICAgICAgInBhdGgiIDogImxvdyIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJQcm9wZXJ0eSIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICJzb3VyY2UiIDogewogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiODg6NS04OTo5NyIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJsb3dDbG9zZWQiIDogdHJ1ZSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgImhpZ2hDbG9zZWQiIDogZmFsc2UsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJJbnRlcnZhbCIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJsb3ciIDogewogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiODg6MTQtODg6OTUiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAidHlwZSIgOiAiQWRkIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIm9wZXJhbmQiIDogWyB7CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICI4ODoxNC04ODo1MCIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJUb0RhdGUiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAibGlicmFyeU5hbWUiIDogIkZISVJIZWxwZXJzIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInR5cGUiIDogIkZ1bmN0aW9uUmVmIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIm9wZXJhbmQiIDogWyB7CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICI4ODozMy04ODo0OSIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJwYXRoIiA6ICJiaXJ0aERhdGUiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAidHlwZSIgOiAiUHJvcGVydHkiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAic291cmNlIiA6IHsKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjg4OjMzLTg4OjM5IiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIm5hbWUiIDogIlBhdGllbnQiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAidHlwZSIgOiAiRXhwcmVzc2lvblJlZiIKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB9IF0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfSwgewogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiODg6NTQtODg6OTUiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAibmFtZSIgOiAiVG9RdWFudGl0eSIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJsaWJyYXJ5TmFtZSIgOiAiRkhJUkhlbHBlcnMiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAidHlwZSIgOiAiRnVuY3Rpb25SZWYiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAib3BlcmFuZCIgOiBbIHsKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjg4Ojc3LTg4Ojk0IiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInN0cmljdCIgOiBmYWxzZSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInR5cGUiIDogIkFzIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIm9wZXJhbmQiIDogewogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiODg6NzctODg6ODIiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAibmFtZSIgOiAiY2hvaWNlIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk9wZXJhbmRSZWYiCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIH0sCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJhc1R5cGVTcGVjaWZpZXIiIDogewogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiODg6ODctODg6OTQiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAibmFtZSIgOiAie2h0dHA6Ly9obDcub3JnL2ZoaXJ9QWdlIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk5hbWVkVHlwZVNwZWNpZmllciIKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB9IF0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfSBdCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIH0sCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJoaWdoIiA6IHsKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjg5OjYtODk6OTYiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAidHlwZSIgOiAiQWRkIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIm9wZXJhbmQiIDogWyB7CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICI4OTo2LTg5Ojg3IiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInR5cGUiIDogIkFkZCIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJvcGVyYW5kIiA6IFsgewogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiODk6Ni04OTo0MiIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJUb0RhdGUiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAibGlicmFyeU5hbWUiIDogIkZISVJIZWxwZXJzIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInR5cGUiIDogIkZ1bmN0aW9uUmVmIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIm9wZXJhbmQiIDogWyB7CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICI4OToyNS04OTo0MSIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJwYXRoIiA6ICJiaXJ0aERhdGUiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAidHlwZSIgOiAiUHJvcGVydHkiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAic291cmNlIiA6IHsKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjg5OjI1LTg5OjMxIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIm5hbWUiIDogIlBhdGllbnQiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAidHlwZSIgOiAiRXhwcmVzc2lvblJlZiIKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB9IF0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfSwgewogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiODk6NDYtODk6ODciLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAibmFtZSIgOiAiVG9RdWFudGl0eSIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJsaWJyYXJ5TmFtZSIgOiAiRkhJUkhlbHBlcnMiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAidHlwZSIgOiAiRnVuY3Rpb25SZWYiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAib3BlcmFuZCIgOiBbIHsKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjg5OjY5LTg5Ojg2IiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInN0cmljdCIgOiBmYWxzZSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInR5cGUiIDogIkFzIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIm9wZXJhbmQiIDogewogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiODk6NjktODk6NzQiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAibmFtZSIgOiAiY2hvaWNlIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk9wZXJhbmRSZWYiCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIH0sCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJhc1R5cGVTcGVjaWZpZXIiIDogewogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiODk6NzktODk6ODYiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAibmFtZSIgOiAie2h0dHA6Ly9obDcub3JnL2ZoaXJ9QWdlIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk5hbWVkVHlwZVNwZWNpZmllciIKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB9IF0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfSBdCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIH0sIHsKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjg5OjkxLTg5Ojk2IiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInZhbHVlIiA6IDEsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJ1bml0IiA6ICJ5ZWFyIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInR5cGUiIDogIlF1YW50aXR5IgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB9IF0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfQogICAgICAgICAgICAgICAgICAgICAgICAgICB9CiAgICAgICAgICAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgICAgICAgICAgfSwKICAgICAgICAgICAgICAgICAgICAgImxvd0Nsb3NlZEV4cHJlc3Npb24iIDogewogICAgICAgICAgICAgICAgICAgICAgICAicGF0aCIgOiAibG93Q2xvc2VkIiwKICAgICAgICAgICAgICAgICAgICAgICAgInR5cGUiIDogIlByb3BlcnR5IiwKICAgICAgICAgICAgICAgICAgICAgICAgInNvdXJjZSIgOiB7CiAgICAgICAgICAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICI4ODo1LTg5Ojk3IiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgImxvd0Nsb3NlZCIgOiB0cnVlLAogICAgICAgICAgICAgICAgICAgICAgICAgICAiaGlnaENsb3NlZCIgOiBmYWxzZSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgInR5cGUiIDogIkludGVydmFsIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgImxvdyIgOiB7CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICI4ODoxNC04ODo5NSIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJBZGQiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAib3BlcmFuZCIgOiBbIHsKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjg4OjE0LTg4OjUwIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIm5hbWUiIDogIlRvRGF0ZSIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJsaWJyYXJ5TmFtZSIgOiAiRkhJUkhlbHBlcnMiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAidHlwZSIgOiAiRnVuY3Rpb25SZWYiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAib3BlcmFuZCIgOiBbIHsKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjg4OjMzLTg4OjQ5IiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInBhdGgiIDogImJpcnRoRGF0ZSIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJQcm9wZXJ0eSIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJzb3VyY2UiIDogewogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiODg6MzMtODg6MzkiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAibmFtZSIgOiAiUGF0aWVudCIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJFeHByZXNzaW9uUmVmIgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB9CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIH0gXQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICB9LCB7CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICI4ODo1NC04ODo5NSIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJUb1F1YW50aXR5IiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgImxpYnJhcnlOYW1lIiA6ICJGSElSSGVscGVycyIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJGdW5jdGlvblJlZiIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJvcGVyYW5kIiA6IFsgewogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiODg6NzctODg6OTQiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAic3RyaWN0IiA6IGZhbHNlLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAidHlwZSIgOiAiQXMiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAib3BlcmFuZCIgOiB7CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICI4ODo3Ny04ODo4MiIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJjaG9pY2UiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAidHlwZSIgOiAiT3BlcmFuZFJlZiIKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgImFzVHlwZVNwZWNpZmllciIgOiB7CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICI4ODo4Ny04ODo5NCIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJ7aHR0cDovL2hsNy5vcmcvZmhpcn1BZ2UiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAidHlwZSIgOiAiTmFtZWRUeXBlU3BlY2lmaWVyIgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB9CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIH0gXQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICB9IF0KICAgICAgICAgICAgICAgICAgICAgICAgICAgfSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgImhpZ2giIDogewogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiODk6Ni04OTo5NiIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJBZGQiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAib3BlcmFuZCIgOiBbIHsKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjg5OjYtODk6ODciLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAidHlwZSIgOiAiQWRkIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIm9wZXJhbmQiIDogWyB7CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICI4OTo2LTg5OjQyIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIm5hbWUiIDogIlRvRGF0ZSIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJsaWJyYXJ5TmFtZSIgOiAiRkhJUkhlbHBlcnMiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAidHlwZSIgOiAiRnVuY3Rpb25SZWYiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAib3BlcmFuZCIgOiBbIHsKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjg5OjI1LTg5OjQxIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInBhdGgiIDogImJpcnRoRGF0ZSIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJQcm9wZXJ0eSIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJzb3VyY2UiIDogewogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiODk6MjUtODk6MzEiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAibmFtZSIgOiAiUGF0aWVudCIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJFeHByZXNzaW9uUmVmIgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB9CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIH0gXQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB9LCB7CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICI4OTo0Ni04OTo4NyIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJUb1F1YW50aXR5IiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgImxpYnJhcnlOYW1lIiA6ICJGSElSSGVscGVycyIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJGdW5jdGlvblJlZiIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJvcGVyYW5kIiA6IFsgewogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiODk6NjktODk6ODYiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAic3RyaWN0IiA6IGZhbHNlLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAidHlwZSIgOiAiQXMiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAib3BlcmFuZCIgOiB7CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICI4OTo2OS04OTo3NCIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJjaG9pY2UiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAidHlwZSIgOiAiT3BlcmFuZFJlZiIKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgImFzVHlwZVNwZWNpZmllciIgOiB7CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICI4OTo3OS04OTo4NiIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJ7aHR0cDovL2hsNy5vcmcvZmhpcn1BZ2UiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAidHlwZSIgOiAiTmFtZWRUeXBlU3BlY2lmaWVyIgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB9CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIH0gXQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB9IF0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfSwgewogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiODk6OTEtODk6OTYiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAidmFsdWUiIDogMSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInVuaXQiIDogInllYXIiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAidHlwZSIgOiAiUXVhbnRpdHkiCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIH0gXQogICAgICAgICAgICAgICAgICAgICAgICAgICB9CiAgICAgICAgICAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgICAgICAgICAgfSwKICAgICAgICAgICAgICAgICAgICAgImhpZ2giIDogewogICAgICAgICAgICAgICAgICAgICAgICAidHlwZSIgOiAiVG9EYXRlVGltZSIsCiAgICAgICAgICAgICAgICAgICAgICAgICJvcGVyYW5kIiA6IHsKICAgICAgICAgICAgICAgICAgICAgICAgICAgInBhdGgiIDogImhpZ2giLAogICAgICAgICAgICAgICAgICAgICAgICAgICAidHlwZSIgOiAiUHJvcGVydHkiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAic291cmNlIiA6IHsKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjg4OjUtODk6OTciLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAibG93Q2xvc2VkIiA6IHRydWUsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJoaWdoQ2xvc2VkIiA6IGZhbHNlLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAidHlwZSIgOiAiSW50ZXJ2YWwiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAibG93IiA6IHsKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjg4OjE0LTg4Ojk1IiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInR5cGUiIDogIkFkZCIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJvcGVyYW5kIiA6IFsgewogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiODg6MTQtODg6NTAiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAibmFtZSIgOiAiVG9EYXRlIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgImxpYnJhcnlOYW1lIiA6ICJGSElSSGVscGVycyIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJGdW5jdGlvblJlZiIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJvcGVyYW5kIiA6IFsgewogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiODg6MzMtODg6NDkiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAicGF0aCIgOiAiYmlydGhEYXRlIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInR5cGUiIDogIlByb3BlcnR5IiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInNvdXJjZSIgOiB7CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICI4ODozMy04ODozOSIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJQYXRpZW50IiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInR5cGUiIDogIkV4cHJlc3Npb25SZWYiCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfSBdCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIH0sIHsKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjg4OjU0LTg4Ojk1IiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIm5hbWUiIDogIlRvUXVhbnRpdHkiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAibGlicmFyeU5hbWUiIDogIkZISVJIZWxwZXJzIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInR5cGUiIDogIkZ1bmN0aW9uUmVmIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIm9wZXJhbmQiIDogWyB7CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICI4ODo3Ny04ODo5NCIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJzdHJpY3QiIDogZmFsc2UsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJBcyIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJvcGVyYW5kIiA6IHsKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjg4Ojc3LTg4OjgyIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIm5hbWUiIDogImNob2ljZSIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJPcGVyYW5kUmVmIgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB9LAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAiYXNUeXBlU3BlY2lmaWVyIiA6IHsKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjg4Ojg3LTg4Ojk0IiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIm5hbWUiIDogIntodHRwOi8vaGw3Lm9yZy9maGlyfUFnZSIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJOYW1lZFR5cGVTcGVjaWZpZXIiCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfSBdCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIH0gXQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICB9LAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAiaGlnaCIgOiB7CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICI4OTo2LTg5Ojk2IiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInR5cGUiIDogIkFkZCIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJvcGVyYW5kIiA6IFsgewogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiODk6Ni04OTo4NyIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJBZGQiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAib3BlcmFuZCIgOiBbIHsKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjg5OjYtODk6NDIiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAibmFtZSIgOiAiVG9EYXRlIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgImxpYnJhcnlOYW1lIiA6ICJGSElSSGVscGVycyIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJGdW5jdGlvblJlZiIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJvcGVyYW5kIiA6IFsgewogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiODk6MjUtODk6NDEiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAicGF0aCIgOiAiYmlydGhEYXRlIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInR5cGUiIDogIlByb3BlcnR5IiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInNvdXJjZSIgOiB7CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICI4OToyNS04OTozMSIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJQYXRpZW50IiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInR5cGUiIDogIkV4cHJlc3Npb25SZWYiCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfSBdCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIH0sIHsKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjg5OjQ2LTg5Ojg3IiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIm5hbWUiIDogIlRvUXVhbnRpdHkiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAibGlicmFyeU5hbWUiIDogIkZISVJIZWxwZXJzIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInR5cGUiIDogIkZ1bmN0aW9uUmVmIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIm9wZXJhbmQiIDogWyB7CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICI4OTo2OS04OTo4NiIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJzdHJpY3QiIDogZmFsc2UsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJBcyIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJvcGVyYW5kIiA6IHsKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjg5OjY5LTg5Ojc0IiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIm5hbWUiIDogImNob2ljZSIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJPcGVyYW5kUmVmIgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB9LAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAiYXNUeXBlU3BlY2lmaWVyIiA6IHsKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjg5Ojc5LTg5Ojg2IiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIm5hbWUiIDogIntodHRwOi8vaGw3Lm9yZy9maGlyfUFnZSIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJOYW1lZFR5cGVTcGVjaWZpZXIiCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfSBdCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIH0gXQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB9LCB7CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICI4OTo5MS04OTo5NiIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJ2YWx1ZSIgOiAxLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAidW5pdCIgOiAieWVhciIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJRdWFudGl0eSIKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfSBdCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgICAgICAgICAgICAgICAgfQogICAgICAgICAgICAgICAgICAgICAgICB9CiAgICAgICAgICAgICAgICAgICAgIH0sCiAgICAgICAgICAgICAgICAgICAgICJoaWdoQ2xvc2VkRXhwcmVzc2lvbiIgOiB7CiAgICAgICAgICAgICAgICAgICAgICAgICJwYXRoIiA6ICJoaWdoQ2xvc2VkIiwKICAgICAgICAgICAgICAgICAgICAgICAgInR5cGUiIDogIlByb3BlcnR5IiwKICAgICAgICAgICAgICAgICAgICAgICAgInNvdXJjZSIgOiB7CiAgICAgICAgICAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICI4ODo1LTg5Ojk3IiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgImxvd0Nsb3NlZCIgOiB0cnVlLAogICAgICAgICAgICAgICAgICAgICAgICAgICAiaGlnaENsb3NlZCIgOiBmYWxzZSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgInR5cGUiIDogIkludGVydmFsIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgImxvdyIgOiB7CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICI4ODoxNC04ODo5NSIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJBZGQiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAib3BlcmFuZCIgOiBbIHsKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjg4OjE0LTg4OjUwIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIm5hbWUiIDogIlRvRGF0ZSIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJsaWJyYXJ5TmFtZSIgOiAiRkhJUkhlbHBlcnMiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAidHlwZSIgOiAiRnVuY3Rpb25SZWYiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAib3BlcmFuZCIgOiBbIHsKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjg4OjMzLTg4OjQ5IiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInBhdGgiIDogImJpcnRoRGF0ZSIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJQcm9wZXJ0eSIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJzb3VyY2UiIDogewogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiODg6MzMtODg6MzkiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAibmFtZSIgOiAiUGF0aWVudCIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJFeHByZXNzaW9uUmVmIgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB9CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIH0gXQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICB9LCB7CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICI4ODo1NC04ODo5NSIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJUb1F1YW50aXR5IiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgImxpYnJhcnlOYW1lIiA6ICJGSElSSGVscGVycyIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJGdW5jdGlvblJlZiIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJvcGVyYW5kIiA6IFsgewogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiODg6NzctODg6OTQiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAic3RyaWN0IiA6IGZhbHNlLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAidHlwZSIgOiAiQXMiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAib3BlcmFuZCIgOiB7CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICI4ODo3Ny04ODo4MiIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJjaG9pY2UiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAidHlwZSIgOiAiT3BlcmFuZFJlZiIKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgImFzVHlwZVNwZWNpZmllciIgOiB7CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICI4ODo4Ny04ODo5NCIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJ7aHR0cDovL2hsNy5vcmcvZmhpcn1BZ2UiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAidHlwZSIgOiAiTmFtZWRUeXBlU3BlY2lmaWVyIgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB9CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIH0gXQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICB9IF0KICAgICAgICAgICAgICAgICAgICAgICAgICAgfSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgImhpZ2giIDogewogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiODk6Ni04OTo5NiIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJBZGQiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAib3BlcmFuZCIgOiBbIHsKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjg5OjYtODk6ODciLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAidHlwZSIgOiAiQWRkIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIm9wZXJhbmQiIDogWyB7CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICI4OTo2LTg5OjQyIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIm5hbWUiIDogIlRvRGF0ZSIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJsaWJyYXJ5TmFtZSIgOiAiRkhJUkhlbHBlcnMiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAidHlwZSIgOiAiRnVuY3Rpb25SZWYiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAib3BlcmFuZCIgOiBbIHsKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjg5OjI1LTg5OjQxIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInBhdGgiIDogImJpcnRoRGF0ZSIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJQcm9wZXJ0eSIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJzb3VyY2UiIDogewogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiODk6MjUtODk6MzEiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAibmFtZSIgOiAiUGF0aWVudCIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJFeHByZXNzaW9uUmVmIgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB9CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIH0gXQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB9LCB7CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICI4OTo0Ni04OTo4NyIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJUb1F1YW50aXR5IiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgImxpYnJhcnlOYW1lIiA6ICJGSElSSGVscGVycyIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJGdW5jdGlvblJlZiIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJvcGVyYW5kIiA6IFsgewogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiODk6NjktODk6ODYiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAic3RyaWN0IiA6IGZhbHNlLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAidHlwZSIgOiAiQXMiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAib3BlcmFuZCIgOiB7CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICI4OTo2OS04OTo3NCIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJjaG9pY2UiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAidHlwZSIgOiAiT3BlcmFuZFJlZiIKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgImFzVHlwZVNwZWNpZmllciIgOiB7CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICI4OTo3OS04OTo4NiIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJ7aHR0cDovL2hsNy5vcmcvZmhpcn1BZ2UiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAidHlwZSIgOiAiTmFtZWRUeXBlU3BlY2lmaWVyIgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB9CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIH0gXQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB9IF0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfSwgewogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiODk6OTEtODk6OTYiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAidmFsdWUiIDogMSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInVuaXQiIDogInllYXIiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAidHlwZSIgOiAiUXVhbnRpdHkiCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIH0gXQogICAgICAgICAgICAgICAgICAgICAgICAgICB9CiAgICAgICAgICAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgICAgICAgICAgfQogICAgICAgICAgICAgICAgICB9CiAgICAgICAgICAgICAgIH0sIHsKICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjkwOjMtOTI6MTA2IiwKICAgICAgICAgICAgICAgICAgIndoZW4iIDogewogICAgICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiOTA6OC05MDoyNyIsCiAgICAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJJcyIsCiAgICAgICAgICAgICAgICAgICAgICJvcGVyYW5kIiA6IHsKICAgICAgICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjkwOjgtOTA6MTMiLAogICAgICAgICAgICAgICAgICAgICAgICAibmFtZSIgOiAiY2hvaWNlIiwKICAgICAgICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk9wZXJhbmRSZWYiCiAgICAgICAgICAgICAgICAgICAgIH0sCiAgICAgICAgICAgICAgICAgICAgICJpc1R5cGVTcGVjaWZpZXIiIDogewogICAgICAgICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiOTA6MTgtOTA6MjciLAogICAgICAgICAgICAgICAgICAgICAgICAibmFtZSIgOiAie2h0dHA6Ly9obDcub3JnL2ZoaXJ9UmFuZ2UiLAogICAgICAgICAgICAgICAgICAgICAgICAidHlwZSIgOiAiTmFtZWRUeXBlU3BlY2lmaWVyIgogICAgICAgICAgICAgICAgICAgICB9CiAgICAgICAgICAgICAgICAgIH0sCiAgICAgICAgICAgICAgICAgICJ0aGVuIiA6IHsKICAgICAgICAgICAgICAgICAgICAgInR5cGUiIDogIkludGVydmFsIiwKICAgICAgICAgICAgICAgICAgICAgImxvdyIgOiB7CiAgICAgICAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJUb0RhdGVUaW1lIiwKICAgICAgICAgICAgICAgICAgICAgICAgIm9wZXJhbmQiIDogewogICAgICAgICAgICAgICAgICAgICAgICAgICAicGF0aCIgOiAibG93IiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgInR5cGUiIDogIlByb3BlcnR5IiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgInNvdXJjZSIgOiB7CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICI5MTo1LTkyOjEwNiIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJsb3dDbG9zZWQiIDogdHJ1ZSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgImhpZ2hDbG9zZWQiIDogZmFsc2UsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJJbnRlcnZhbCIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJsb3ciIDogewogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiOTE6MTQtOTE6MTAzIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInR5cGUiIDogIkFkZCIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJvcGVyYW5kIiA6IFsgewogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiOTE6MTQtOTE6NTAiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAibmFtZSIgOiAiVG9EYXRlIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgImxpYnJhcnlOYW1lIiA6ICJGSElSSGVscGVycyIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJGdW5jdGlvblJlZiIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJvcGVyYW5kIiA6IFsgewogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiOTE6MzMtOTE6NDkiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAicGF0aCIgOiAiYmlydGhEYXRlIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInR5cGUiIDogIlByb3BlcnR5IiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInNvdXJjZSIgOiB7CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICI5MTozMy05MTozOSIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJQYXRpZW50IiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInR5cGUiIDogIkV4cHJlc3Npb25SZWYiCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfSBdCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIH0sIHsKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjkxOjU0LTkxOjEwMyIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJUb1F1YW50aXR5IiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgImxpYnJhcnlOYW1lIiA6ICJGSElSSGVscGVycyIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJGdW5jdGlvblJlZiIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJvcGVyYW5kIiA6IFsgewogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiOTE6NzctOTE6MTAyIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInBhdGgiIDogImxvdyIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJQcm9wZXJ0eSIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJzb3VyY2UiIDogewogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiOTE6NzctOTE6OTgiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAic3RyaWN0IiA6IGZhbHNlLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAidHlwZSIgOiAiQXMiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAib3BlcmFuZCIgOiB7CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICI5MTo3OC05MTo4MyIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJjaG9pY2UiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAidHlwZSIgOiAiT3BlcmFuZFJlZiIKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgImFzVHlwZVNwZWNpZmllciIgOiB7CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICI5MTo4OC05MTo5NyIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJ7aHR0cDovL2hsNy5vcmcvZmhpcn1SYW5nZSIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJOYW1lZFR5cGVTcGVjaWZpZXIiCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB9IF0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfSBdCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIH0sCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJoaWdoIiA6IHsKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjkyOjYtOTI6MTA1IiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInR5cGUiIDogIkFkZCIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJvcGVyYW5kIiA6IFsgewogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiOTI6Ni05Mjo5NiIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJBZGQiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAib3BlcmFuZCIgOiBbIHsKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjkyOjYtOTI6NDIiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAibmFtZSIgOiAiVG9EYXRlIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgImxpYnJhcnlOYW1lIiA6ICJGSElSSGVscGVycyIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJGdW5jdGlvblJlZiIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJvcGVyYW5kIiA6IFsgewogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiOTI6MjUtOTI6NDEiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAicGF0aCIgOiAiYmlydGhEYXRlIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInR5cGUiIDogIlByb3BlcnR5IiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInNvdXJjZSIgOiB7CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICI5MjoyNS05MjozMSIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJQYXRpZW50IiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInR5cGUiIDogIkV4cHJlc3Npb25SZWYiCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfSBdCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIH0sIHsKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjkyOjQ2LTkyOjk2IiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIm5hbWUiIDogIlRvUXVhbnRpdHkiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAibGlicmFyeU5hbWUiIDogIkZISVJIZWxwZXJzIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInR5cGUiIDogIkZ1bmN0aW9uUmVmIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIm9wZXJhbmQiIDogWyB7CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICI5Mjo2OS05Mjo5NSIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJwYXRoIiA6ICJoaWdoIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInR5cGUiIDogIlByb3BlcnR5IiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInNvdXJjZSIgOiB7CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICI5Mjo2OS05Mjo5MCIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJzdHJpY3QiIDogZmFsc2UsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJBcyIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJvcGVyYW5kIiA6IHsKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjkyOjcwLTkyOjc1IiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIm5hbWUiIDogImNob2ljZSIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJPcGVyYW5kUmVmIgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB9LAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAiYXNUeXBlU3BlY2lmaWVyIiA6IHsKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjkyOjgwLTkyOjg5IiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIm5hbWUiIDogIntodHRwOi8vaGw3Lm9yZy9maGlyfVJhbmdlIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk5hbWVkVHlwZVNwZWNpZmllciIKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB9CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIH0gXQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB9IF0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfSwgewogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiOTI6MTAwLTkyOjEwNSIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJ2YWx1ZSIgOiAxLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAidW5pdCIgOiAieWVhciIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJRdWFudGl0eSIKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfSBdCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgICAgICAgICAgICAgICAgfQogICAgICAgICAgICAgICAgICAgICAgICB9CiAgICAgICAgICAgICAgICAgICAgIH0sCiAgICAgICAgICAgICAgICAgICAgICJsb3dDbG9zZWRFeHByZXNzaW9uIiA6IHsKICAgICAgICAgICAgICAgICAgICAgICAgInBhdGgiIDogImxvd0Nsb3NlZCIsCiAgICAgICAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJQcm9wZXJ0eSIsCiAgICAgICAgICAgICAgICAgICAgICAgICJzb3VyY2UiIDogewogICAgICAgICAgICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiOTE6NS05MjoxMDYiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAibG93Q2xvc2VkIiA6IHRydWUsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICJoaWdoQ2xvc2VkIiA6IGZhbHNlLAogICAgICAgICAgICAgICAgICAgICAgICAgICAidHlwZSIgOiAiSW50ZXJ2YWwiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAibG93IiA6IHsKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjkxOjE0LTkxOjEwMyIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJBZGQiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAib3BlcmFuZCIgOiBbIHsKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjkxOjE0LTkxOjUwIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIm5hbWUiIDogIlRvRGF0ZSIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJsaWJyYXJ5TmFtZSIgOiAiRkhJUkhlbHBlcnMiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAidHlwZSIgOiAiRnVuY3Rpb25SZWYiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAib3BlcmFuZCIgOiBbIHsKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjkxOjMzLTkxOjQ5IiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInBhdGgiIDogImJpcnRoRGF0ZSIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJQcm9wZXJ0eSIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJzb3VyY2UiIDogewogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiOTE6MzMtOTE6MzkiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAibmFtZSIgOiAiUGF0aWVudCIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJFeHByZXNzaW9uUmVmIgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB9CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIH0gXQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICB9LCB7CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICI5MTo1NC05MToxMDMiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAibmFtZSIgOiAiVG9RdWFudGl0eSIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJsaWJyYXJ5TmFtZSIgOiAiRkhJUkhlbHBlcnMiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAidHlwZSIgOiAiRnVuY3Rpb25SZWYiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAib3BlcmFuZCIgOiBbIHsKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjkxOjc3LTkxOjEwMiIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJwYXRoIiA6ICJsb3ciLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAidHlwZSIgOiAiUHJvcGVydHkiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAic291cmNlIiA6IHsKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjkxOjc3LTkxOjk4IiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInN0cmljdCIgOiBmYWxzZSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInR5cGUiIDogIkFzIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIm9wZXJhbmQiIDogewogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiOTE6NzgtOTE6ODMiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAibmFtZSIgOiAiY2hvaWNlIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk9wZXJhbmRSZWYiCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIH0sCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJhc1R5cGVTcGVjaWZpZXIiIDogewogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiOTE6ODgtOTE6OTciLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAibmFtZSIgOiAie2h0dHA6Ly9obDcub3JnL2ZoaXJ9UmFuZ2UiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAidHlwZSIgOiAiTmFtZWRUeXBlU3BlY2lmaWVyIgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB9CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfSBdCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIH0gXQogICAgICAgICAgICAgICAgICAgICAgICAgICB9LAogICAgICAgICAgICAgICAgICAgICAgICAgICAiaGlnaCIgOiB7CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICI5Mjo2LTkyOjEwNSIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJBZGQiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAib3BlcmFuZCIgOiBbIHsKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjkyOjYtOTI6OTYiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAidHlwZSIgOiAiQWRkIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIm9wZXJhbmQiIDogWyB7CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICI5Mjo2LTkyOjQyIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIm5hbWUiIDogIlRvRGF0ZSIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJsaWJyYXJ5TmFtZSIgOiAiRkhJUkhlbHBlcnMiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAidHlwZSIgOiAiRnVuY3Rpb25SZWYiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAib3BlcmFuZCIgOiBbIHsKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjkyOjI1LTkyOjQxIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInBhdGgiIDogImJpcnRoRGF0ZSIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJQcm9wZXJ0eSIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJzb3VyY2UiIDogewogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiOTI6MjUtOTI6MzEiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAibmFtZSIgOiAiUGF0aWVudCIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJFeHByZXNzaW9uUmVmIgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB9CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIH0gXQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB9LCB7CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICI5Mjo0Ni05Mjo5NiIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJUb1F1YW50aXR5IiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgImxpYnJhcnlOYW1lIiA6ICJGSElSSGVscGVycyIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJGdW5jdGlvblJlZiIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJvcGVyYW5kIiA6IFsgewogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiOTI6NjktOTI6OTUiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAicGF0aCIgOiAiaGlnaCIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJQcm9wZXJ0eSIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJzb3VyY2UiIDogewogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiOTI6NjktOTI6OTAiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAic3RyaWN0IiA6IGZhbHNlLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAidHlwZSIgOiAiQXMiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAib3BlcmFuZCIgOiB7CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICI5Mjo3MC05Mjo3NSIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJjaG9pY2UiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAidHlwZSIgOiAiT3BlcmFuZFJlZiIKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgImFzVHlwZVNwZWNpZmllciIgOiB7CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICI5Mjo4MC05Mjo4OSIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJ7aHR0cDovL2hsNy5vcmcvZmhpcn1SYW5nZSIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJOYW1lZFR5cGVTcGVjaWZpZXIiCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB9IF0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfSBdCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIH0sIHsKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjkyOjEwMC05MjoxMDUiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAidmFsdWUiIDogMSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInVuaXQiIDogInllYXIiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAidHlwZSIgOiAiUXVhbnRpdHkiCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIH0gXQogICAgICAgICAgICAgICAgICAgICAgICAgICB9CiAgICAgICAgICAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgICAgICAgICAgfSwKICAgICAgICAgICAgICAgICAgICAgImhpZ2giIDogewogICAgICAgICAgICAgICAgICAgICAgICAidHlwZSIgOiAiVG9EYXRlVGltZSIsCiAgICAgICAgICAgICAgICAgICAgICAgICJvcGVyYW5kIiA6IHsKICAgICAgICAgICAgICAgICAgICAgICAgICAgInBhdGgiIDogImhpZ2giLAogICAgICAgICAgICAgICAgICAgICAgICAgICAidHlwZSIgOiAiUHJvcGVydHkiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAic291cmNlIiA6IHsKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjkxOjUtOTI6MTA2IiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgImxvd0Nsb3NlZCIgOiB0cnVlLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAiaGlnaENsb3NlZCIgOiBmYWxzZSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInR5cGUiIDogIkludGVydmFsIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgImxvdyIgOiB7CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICI5MToxNC05MToxMDMiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAidHlwZSIgOiAiQWRkIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIm9wZXJhbmQiIDogWyB7CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICI5MToxNC05MTo1MCIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJUb0RhdGUiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAibGlicmFyeU5hbWUiIDogIkZISVJIZWxwZXJzIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInR5cGUiIDogIkZ1bmN0aW9uUmVmIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIm9wZXJhbmQiIDogWyB7CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICI5MTozMy05MTo0OSIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJwYXRoIiA6ICJiaXJ0aERhdGUiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAidHlwZSIgOiAiUHJvcGVydHkiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAic291cmNlIiA6IHsKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjkxOjMzLTkxOjM5IiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIm5hbWUiIDogIlBhdGllbnQiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAidHlwZSIgOiAiRXhwcmVzc2lvblJlZiIKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB9IF0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfSwgewogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiOTE6NTQtOTE6MTAzIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIm5hbWUiIDogIlRvUXVhbnRpdHkiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAibGlicmFyeU5hbWUiIDogIkZISVJIZWxwZXJzIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInR5cGUiIDogIkZ1bmN0aW9uUmVmIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIm9wZXJhbmQiIDogWyB7CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICI5MTo3Ny05MToxMDIiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAicGF0aCIgOiAibG93IiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInR5cGUiIDogIlByb3BlcnR5IiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInNvdXJjZSIgOiB7CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICI5MTo3Ny05MTo5OCIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJzdHJpY3QiIDogZmFsc2UsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJBcyIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJvcGVyYW5kIiA6IHsKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjkxOjc4LTkxOjgzIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIm5hbWUiIDogImNob2ljZSIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJPcGVyYW5kUmVmIgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB9LAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAiYXNUeXBlU3BlY2lmaWVyIiA6IHsKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjkxOjg4LTkxOjk3IiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIm5hbWUiIDogIntodHRwOi8vaGw3Lm9yZy9maGlyfVJhbmdlIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk5hbWVkVHlwZVNwZWNpZmllciIKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB9CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIH0gXQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB9IF0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgImhpZ2giIDogewogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiOTI6Ni05MjoxMDUiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAidHlwZSIgOiAiQWRkIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIm9wZXJhbmQiIDogWyB7CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICI5Mjo2LTkyOjk2IiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInR5cGUiIDogIkFkZCIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJvcGVyYW5kIiA6IFsgewogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiOTI6Ni05Mjo0MiIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJUb0RhdGUiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAibGlicmFyeU5hbWUiIDogIkZISVJIZWxwZXJzIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInR5cGUiIDogIkZ1bmN0aW9uUmVmIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIm9wZXJhbmQiIDogWyB7CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICI5MjoyNS05Mjo0MSIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJwYXRoIiA6ICJiaXJ0aERhdGUiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAidHlwZSIgOiAiUHJvcGVydHkiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAic291cmNlIiA6IHsKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjkyOjI1LTkyOjMxIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIm5hbWUiIDogIlBhdGllbnQiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAidHlwZSIgOiAiRXhwcmVzc2lvblJlZiIKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB9IF0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfSwgewogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiOTI6NDYtOTI6OTYiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAibmFtZSIgOiAiVG9RdWFudGl0eSIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJsaWJyYXJ5TmFtZSIgOiAiRkhJUkhlbHBlcnMiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAidHlwZSIgOiAiRnVuY3Rpb25SZWYiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAib3BlcmFuZCIgOiBbIHsKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjkyOjY5LTkyOjk1IiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInBhdGgiIDogImhpZ2giLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAidHlwZSIgOiAiUHJvcGVydHkiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAic291cmNlIiA6IHsKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjkyOjY5LTkyOjkwIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInN0cmljdCIgOiBmYWxzZSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInR5cGUiIDogIkFzIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIm9wZXJhbmQiIDogewogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiOTI6NzAtOTI6NzUiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAibmFtZSIgOiAiY2hvaWNlIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk9wZXJhbmRSZWYiCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIH0sCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJhc1R5cGVTcGVjaWZpZXIiIDogewogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiOTI6ODAtOTI6ODkiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAibmFtZSIgOiAie2h0dHA6Ly9obDcub3JnL2ZoaXJ9UmFuZ2UiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAidHlwZSIgOiAiTmFtZWRUeXBlU3BlY2lmaWVyIgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB9CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfSBdCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIH0gXQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB9LCB7CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICI5MjoxMDAtOTI6MTA1IiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInZhbHVlIiA6IDEsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJ1bml0IiA6ICJ5ZWFyIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInR5cGUiIDogIlF1YW50aXR5IgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB9IF0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfQogICAgICAgICAgICAgICAgICAgICAgICAgICB9CiAgICAgICAgICAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgICAgICAgICAgfSwKICAgICAgICAgICAgICAgICAgICAgImhpZ2hDbG9zZWRFeHByZXNzaW9uIiA6IHsKICAgICAgICAgICAgICAgICAgICAgICAgInBhdGgiIDogImhpZ2hDbG9zZWQiLAogICAgICAgICAgICAgICAgICAgICAgICAidHlwZSIgOiAiUHJvcGVydHkiLAogICAgICAgICAgICAgICAgICAgICAgICAic291cmNlIiA6IHsKICAgICAgICAgICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjkxOjUtOTI6MTA2IiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgImxvd0Nsb3NlZCIgOiB0cnVlLAogICAgICAgICAgICAgICAgICAgICAgICAgICAiaGlnaENsb3NlZCIgOiBmYWxzZSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgInR5cGUiIDogIkludGVydmFsIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgImxvdyIgOiB7CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICI5MToxNC05MToxMDMiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAidHlwZSIgOiAiQWRkIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIm9wZXJhbmQiIDogWyB7CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICI5MToxNC05MTo1MCIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJUb0RhdGUiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAibGlicmFyeU5hbWUiIDogIkZISVJIZWxwZXJzIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInR5cGUiIDogIkZ1bmN0aW9uUmVmIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIm9wZXJhbmQiIDogWyB7CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICI5MTozMy05MTo0OSIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJwYXRoIiA6ICJiaXJ0aERhdGUiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAidHlwZSIgOiAiUHJvcGVydHkiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAic291cmNlIiA6IHsKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjkxOjMzLTkxOjM5IiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIm5hbWUiIDogIlBhdGllbnQiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAidHlwZSIgOiAiRXhwcmVzc2lvblJlZiIKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB9IF0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfSwgewogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiOTE6NTQtOTE6MTAzIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIm5hbWUiIDogIlRvUXVhbnRpdHkiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAibGlicmFyeU5hbWUiIDogIkZISVJIZWxwZXJzIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInR5cGUiIDogIkZ1bmN0aW9uUmVmIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIm9wZXJhbmQiIDogWyB7CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICI5MTo3Ny05MToxMDIiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAicGF0aCIgOiAibG93IiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInR5cGUiIDogIlByb3BlcnR5IiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInNvdXJjZSIgOiB7CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICI5MTo3Ny05MTo5OCIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJzdHJpY3QiIDogZmFsc2UsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJBcyIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJvcGVyYW5kIiA6IHsKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjkxOjc4LTkxOjgzIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIm5hbWUiIDogImNob2ljZSIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJPcGVyYW5kUmVmIgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB9LAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAiYXNUeXBlU3BlY2lmaWVyIiA6IHsKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjkxOjg4LTkxOjk3IiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIm5hbWUiIDogIntodHRwOi8vaGw3Lm9yZy9maGlyfVJhbmdlIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk5hbWVkVHlwZVNwZWNpZmllciIKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB9CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIH0gXQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICB9IF0KICAgICAgICAgICAgICAgICAgICAgICAgICAgfSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgImhpZ2giIDogewogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiOTI6Ni05MjoxMDUiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAidHlwZSIgOiAiQWRkIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIm9wZXJhbmQiIDogWyB7CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICI5Mjo2LTkyOjk2IiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInR5cGUiIDogIkFkZCIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJvcGVyYW5kIiA6IFsgewogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiOTI6Ni05Mjo0MiIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJUb0RhdGUiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAibGlicmFyeU5hbWUiIDogIkZISVJIZWxwZXJzIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInR5cGUiIDogIkZ1bmN0aW9uUmVmIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIm9wZXJhbmQiIDogWyB7CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICI5MjoyNS05Mjo0MSIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJwYXRoIiA6ICJiaXJ0aERhdGUiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAidHlwZSIgOiAiUHJvcGVydHkiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAic291cmNlIiA6IHsKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjkyOjI1LTkyOjMxIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIm5hbWUiIDogIlBhdGllbnQiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAidHlwZSIgOiAiRXhwcmVzc2lvblJlZiIKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB9IF0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfSwgewogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiOTI6NDYtOTI6OTYiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAibmFtZSIgOiAiVG9RdWFudGl0eSIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJsaWJyYXJ5TmFtZSIgOiAiRkhJUkhlbHBlcnMiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAidHlwZSIgOiAiRnVuY3Rpb25SZWYiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAib3BlcmFuZCIgOiBbIHsKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjkyOjY5LTkyOjk1IiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInBhdGgiIDogImhpZ2giLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAidHlwZSIgOiAiUHJvcGVydHkiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAic291cmNlIiA6IHsKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjkyOjY5LTkyOjkwIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInN0cmljdCIgOiBmYWxzZSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInR5cGUiIDogIkFzIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIm9wZXJhbmQiIDogewogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiOTI6NzAtOTI6NzUiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAibmFtZSIgOiAiY2hvaWNlIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk9wZXJhbmRSZWYiCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIH0sCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJhc1R5cGVTcGVjaWZpZXIiIDogewogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiOTI6ODAtOTI6ODkiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAibmFtZSIgOiAie2h0dHA6Ly9obDcub3JnL2ZoaXJ9UmFuZ2UiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAidHlwZSIgOiAiTmFtZWRUeXBlU3BlY2lmaWVyIgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB9CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfSBdCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIH0gXQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICB9LCB7CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICI5MjoxMDAtOTI6MTA1IiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInZhbHVlIiA6IDEsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJ1bml0IiA6ICJ5ZWFyIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInR5cGUiIDogIlF1YW50aXR5IgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICB9IF0KICAgICAgICAgICAgICAgICAgICAgICAgICAgfQogICAgICAgICAgICAgICAgICAgICAgICB9CiAgICAgICAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgICAgICAgfQogICAgICAgICAgICAgICB9LCB7CiAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICI5Mzo1LTk0OjExNCIsCiAgICAgICAgICAgICAgICAgICJ3aGVuIiA6IHsKICAgICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjkzOjEwLTkzOjMwIiwKICAgICAgICAgICAgICAgICAgICAgInR5cGUiIDogIklzIiwKICAgICAgICAgICAgICAgICAgICAgIm9wZXJhbmQiIDogewogICAgICAgICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiOTM6MTAtOTM6MTUiLAogICAgICAgICAgICAgICAgICAgICAgICAibmFtZSIgOiAiY2hvaWNlIiwKICAgICAgICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk9wZXJhbmRSZWYiCiAgICAgICAgICAgICAgICAgICAgIH0sCiAgICAgICAgICAgICAgICAgICAgICJpc1R5cGVTcGVjaWZpZXIiIDogewogICAgICAgICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiOTM6MjAtOTM6MzAiLAogICAgICAgICAgICAgICAgICAgICAgICAibmFtZSIgOiAie2h0dHA6Ly9obDcub3JnL2ZoaXJ9VGltaW5nIiwKICAgICAgICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk5hbWVkVHlwZVNwZWNpZmllciIKICAgICAgICAgICAgICAgICAgICAgfQogICAgICAgICAgICAgICAgICB9LAogICAgICAgICAgICAgICAgICAidGhlbiIgOiB7CiAgICAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICI5NDo1LTk0OjExNCIsCiAgICAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJNZXNzYWdlIiwKICAgICAgICAgICAgICAgICAgICAgInNvdXJjZSIgOiB7CiAgICAgICAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICI5NDoxMy05NDozOCIsCiAgICAgICAgICAgICAgICAgICAgICAgICJzdHJpY3QiIDogZmFsc2UsCiAgICAgICAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJBcyIsCiAgICAgICAgICAgICAgICAgICAgICAgICJvcGVyYW5kIiA6IHsKICAgICAgICAgICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjk0OjEzLTk0OjE2IiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk51bGwiCiAgICAgICAgICAgICAgICAgICAgICAgIH0sCiAgICAgICAgICAgICAgICAgICAgICAgICJhc1R5cGVTcGVjaWZpZXIiIDogewogICAgICAgICAgICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiOTQ6MjEtOTQ6MzgiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAidHlwZSIgOiAiSW50ZXJ2YWxUeXBlU3BlY2lmaWVyIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgInBvaW50VHlwZSIgOiB7CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICI5NDozMC05NDozNyIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJ7dXJuOmhsNy1vcmc6ZWxtLXR5cGVzOnIxfURhdGVUaW1lIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk5hbWVkVHlwZVNwZWNpZmllciIKICAgICAgICAgICAgICAgICAgICAgICAgICAgfQogICAgICAgICAgICAgICAgICAgICAgICB9CiAgICAgICAgICAgICAgICAgICAgIH0sCiAgICAgICAgICAgICAgICAgICAgICJjb25kaXRpb24iIDogewogICAgICAgICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiOTQ6NDEtOTQ6NDQiLAogICAgICAgICAgICAgICAgICAgICAgICAidmFsdWVUeXBlIiA6ICJ7dXJuOmhsNy1vcmc6ZWxtLXR5cGVzOnIxfUJvb2xlYW4iLAogICAgICAgICAgICAgICAgICAgICAgICAidmFsdWUiIDogInRydWUiLAogICAgICAgICAgICAgICAgICAgICAgICAidHlwZSIgOiAiTGl0ZXJhbCIKICAgICAgICAgICAgICAgICAgICAgfSwKICAgICAgICAgICAgICAgICAgICAgImNvZGUiIDogewogICAgICAgICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiOTQ6NDctOTQ6NDkiLAogICAgICAgICAgICAgICAgICAgICAgICAidmFsdWVUeXBlIiA6ICJ7dXJuOmhsNy1vcmc6ZWxtLXR5cGVzOnIxfVN0cmluZyIsCiAgICAgICAgICAgICAgICAgICAgICAgICJ2YWx1ZSIgOiAiMSIsCiAgICAgICAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJMaXRlcmFsIgogICAgICAgICAgICAgICAgICAgICB9LAogICAgICAgICAgICAgICAgICAgICAic2V2ZXJpdHkiIDogewogICAgICAgICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiOTQ6NTItOTQ6NTgiLAogICAgICAgICAgICAgICAgICAgICAgICAidmFsdWVUeXBlIiA6ICJ7dXJuOmhsNy1vcmc6ZWxtLXR5cGVzOnIxfVN0cmluZyIsCiAgICAgICAgICAgICAgICAgICAgICAgICJ2YWx1ZSIgOiAiRXJyb3IiLAogICAgICAgICAgICAgICAgICAgICAgICAidHlwZSIgOiAiTGl0ZXJhbCIKICAgICAgICAgICAgICAgICAgICAgfSwKICAgICAgICAgICAgICAgICAgICAgIm1lc3NhZ2UiIDogewogICAgICAgICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiOTQ6NjEtOTQ6MTEzIiwKICAgICAgICAgICAgICAgICAgICAgICAgInZhbHVlVHlwZSIgOiAie3VybjpobDctb3JnOmVsbS10eXBlczpyMX1TdHJpbmciLAogICAgICAgICAgICAgICAgICAgICAgICAidmFsdWUiIDogIkNhbm5vdCBjb21wdXRlIGEgc2luZ2xlIGludGVydmFsIGZyb20gYSBUaW1pbmcgdHlwZSIsCiAgICAgICAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJMaXRlcmFsIgogICAgICAgICAgICAgICAgICAgICB9CiAgICAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgICAgfSwgewogICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiOTU6NS05NjoxMTEiLAogICAgICAgICAgICAgICAgICAid2hlbiIgOiB7CiAgICAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICI5NToxMC05NTozMCIsCiAgICAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJJcyIsCiAgICAgICAgICAgICAgICAgICAgICJvcGVyYW5kIiA6IHsKICAgICAgICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjk1OjEwLTk1OjE1IiwKICAgICAgICAgICAgICAgICAgICAgICAgIm5hbWUiIDogImNob2ljZSIsCiAgICAgICAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJPcGVyYW5kUmVmIgogICAgICAgICAgICAgICAgICAgICB9LAogICAgICAgICAgICAgICAgICAgICAiaXNUeXBlU3BlY2lmaWVyIiA6IHsKICAgICAgICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjk1OjIwLTk1OjMwIiwKICAgICAgICAgICAgICAgICAgICAgICAgIm5hbWUiIDogIntodHRwOi8vaGw3Lm9yZy9maGlyfXN0cmluZyIsCiAgICAgICAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJOYW1lZFR5cGVTcGVjaWZpZXIiCiAgICAgICAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgICAgICAgfSwKICAgICAgICAgICAgICAgICAgInRoZW4iIDogewogICAgICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiOTY6Ny05NjoxMTEiLAogICAgICAgICAgICAgICAgICAgICAidHlwZSIgOiAiTWVzc2FnZSIsCiAgICAgICAgICAgICAgICAgICAgICJzb3VyY2UiIDogewogICAgICAgICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiOTY6MTUtOTY6NDAiLAogICAgICAgICAgICAgICAgICAgICAgICAic3RyaWN0IiA6IGZhbHNlLAogICAgICAgICAgICAgICAgICAgICAgICAidHlwZSIgOiAiQXMiLAogICAgICAgICAgICAgICAgICAgICAgICAib3BlcmFuZCIgOiB7CiAgICAgICAgICAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICI5NjoxNS05NjoxOCIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJOdWxsIgogICAgICAgICAgICAgICAgICAgICAgICB9LAogICAgICAgICAgICAgICAgICAgICAgICAiYXNUeXBlU3BlY2lmaWVyIiA6IHsKICAgICAgICAgICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjk2OjIzLTk2OjQwIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgInR5cGUiIDogIkludGVydmFsVHlwZVNwZWNpZmllciIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICJwb2ludFR5cGUiIDogewogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiOTY6MzItOTY6MzkiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAibmFtZSIgOiAie3VybjpobDctb3JnOmVsbS10eXBlczpyMX1EYXRlVGltZSIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJOYW1lZFR5cGVTcGVjaWZpZXIiCiAgICAgICAgICAgICAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgICAgICAgICAgICAgfQogICAgICAgICAgICAgICAgICAgICB9LAogICAgICAgICAgICAgICAgICAgICAiY29uZGl0aW9uIiA6IHsKICAgICAgICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjk2OjQzLTk2OjQ2IiwKICAgICAgICAgICAgICAgICAgICAgICAgInZhbHVlVHlwZSIgOiAie3VybjpobDctb3JnOmVsbS10eXBlczpyMX1Cb29sZWFuIiwKICAgICAgICAgICAgICAgICAgICAgICAgInZhbHVlIiA6ICJ0cnVlIiwKICAgICAgICAgICAgICAgICAgICAgICAgInR5cGUiIDogIkxpdGVyYWwiCiAgICAgICAgICAgICAgICAgICAgIH0sCiAgICAgICAgICAgICAgICAgICAgICJjb2RlIiA6IHsKICAgICAgICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjk2OjQ5LTk2OjUxIiwKICAgICAgICAgICAgICAgICAgICAgICAgInZhbHVlVHlwZSIgOiAie3VybjpobDctb3JnOmVsbS10eXBlczpyMX1TdHJpbmciLAogICAgICAgICAgICAgICAgICAgICAgICAidmFsdWUiIDogIjEiLAogICAgICAgICAgICAgICAgICAgICAgICAidHlwZSIgOiAiTGl0ZXJhbCIKICAgICAgICAgICAgICAgICAgICAgfSwKICAgICAgICAgICAgICAgICAgICAgInNldmVyaXR5IiA6IHsKICAgICAgICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjk2OjU0LTk2OjYwIiwKICAgICAgICAgICAgICAgICAgICAgICAgInZhbHVlVHlwZSIgOiAie3VybjpobDctb3JnOmVsbS10eXBlczpyMX1TdHJpbmciLAogICAgICAgICAgICAgICAgICAgICAgICAidmFsdWUiIDogIkVycm9yIiwKICAgICAgICAgICAgICAgICAgICAgICAgInR5cGUiIDogIkxpdGVyYWwiCiAgICAgICAgICAgICAgICAgICAgIH0sCiAgICAgICAgICAgICAgICAgICAgICJtZXNzYWdlIiA6IHsKICAgICAgICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjk2OjYzLTk2OjExMCIsCiAgICAgICAgICAgICAgICAgICAgICAgICJ2YWx1ZVR5cGUiIDogInt1cm46aGw3LW9yZzplbG0tdHlwZXM6cjF9U3RyaW5nIiwKICAgICAgICAgICAgICAgICAgICAgICAgInZhbHVlIiA6ICJDYW5ub3QgY29tcHV0ZSBhbiBpbnRlcnZhbCBmcm9tIGEgU3RyaW5nIHZhbHVlIiwKICAgICAgICAgICAgICAgICAgICAgICAgInR5cGUiIDogIkxpdGVyYWwiCiAgICAgICAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgICAgICAgfQogICAgICAgICAgICAgICB9IF0sCiAgICAgICAgICAgICAgICJlbHNlIiA6IHsKICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjk4OjctOTg6MzIiLAogICAgICAgICAgICAgICAgICAic3RyaWN0IiA6IGZhbHNlLAogICAgICAgICAgICAgICAgICAidHlwZSIgOiAiQXMiLAogICAgICAgICAgICAgICAgICAib3BlcmFuZCIgOiB7CiAgICAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICI5ODo3LTk4OjEwIiwKICAgICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk51bGwiCiAgICAgICAgICAgICAgICAgIH0sCiAgICAgICAgICAgICAgICAgICJhc1R5cGVTcGVjaWZpZXIiIDogewogICAgICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiOTg6MTUtOTg6MzIiLAogICAgICAgICAgICAgICAgICAgICAidHlwZSIgOiAiSW50ZXJ2YWxUeXBlU3BlY2lmaWVyIiwKICAgICAgICAgICAgICAgICAgICAgInBvaW50VHlwZSIgOiB7CiAgICAgICAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICI5ODoyNC05ODozMSIsCiAgICAgICAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJ7dXJuOmhsNy1vcmc6ZWxtLXR5cGVzOnIxfURhdGVUaW1lIiwKICAgICAgICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk5hbWVkVHlwZVNwZWNpZmllciIKICAgICAgICAgICAgICAgICAgICAgfQogICAgICAgICAgICAgICAgICB9CiAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgfSwKICAgICAgICAgICAgIm9wZXJhbmQiIDogWyB7CiAgICAgICAgICAgICAgICJuYW1lIiA6ICJjaG9pY2UiLAogICAgICAgICAgICAgICAib3BlcmFuZFR5cGVTcGVjaWZpZXIiIDogewogICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiNzk6MzUtNzk6MTMwIiwKICAgICAgICAgICAgICAgICAgInR5cGUiIDogIkNob2ljZVR5cGVTcGVjaWZpZXIiLAogICAgICAgICAgICAgICAgICAiY2hvaWNlIiA6IFsgewogICAgICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiNzk6NDItNzk6NTQiLAogICAgICAgICAgICAgICAgICAgICAibmFtZSIgOiAie2h0dHA6Ly9obDcub3JnL2ZoaXJ9ZGF0ZVRpbWUiLAogICAgICAgICAgICAgICAgICAgICAidHlwZSIgOiAiTmFtZWRUeXBlU3BlY2lmaWVyIgogICAgICAgICAgICAgICAgICB9LCB7CiAgICAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICI3OTo1Ny03OTo2NyIsCiAgICAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJ7aHR0cDovL2hsNy5vcmcvZmhpcn1QZXJpb2QiLAogICAgICAgICAgICAgICAgICAgICAidHlwZSIgOiAiTmFtZWRUeXBlU3BlY2lmaWVyIgogICAgICAgICAgICAgICAgICB9LCB7CiAgICAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICI3OTo3MC03OTo4MCIsCiAgICAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJ7aHR0cDovL2hsNy5vcmcvZmhpcn1UaW1pbmciLAogICAgICAgICAgICAgICAgICAgICAidHlwZSIgOiAiTmFtZWRUeXBlU3BlY2lmaWVyIgogICAgICAgICAgICAgICAgICB9LCB7CiAgICAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICI3OTo4My03OTo5NCIsCiAgICAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJ7aHR0cDovL2hsNy5vcmcvZmhpcn1pbnN0YW50IiwKICAgICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk5hbWVkVHlwZVNwZWNpZmllciIKICAgICAgICAgICAgICAgICAgfSwgewogICAgICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiNzk6OTctNzk6MTA3IiwKICAgICAgICAgICAgICAgICAgICAgIm5hbWUiIDogIntodHRwOi8vaGw3Lm9yZy9maGlyfXN0cmluZyIsCiAgICAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJOYW1lZFR5cGVTcGVjaWZpZXIiCiAgICAgICAgICAgICAgICAgIH0sIHsKICAgICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjc5OjExMC03OToxMTciLAogICAgICAgICAgICAgICAgICAgICAibmFtZSIgOiAie2h0dHA6Ly9obDcub3JnL2ZoaXJ9QWdlIiwKICAgICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk5hbWVkVHlwZVNwZWNpZmllciIKICAgICAgICAgICAgICAgICAgfSwgewogICAgICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiNzk6MTIwLTc5OjEyOSIsCiAgICAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJ7aHR0cDovL2hsNy5vcmcvZmhpcn1SYW5nZSIsCiAgICAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJOYW1lZFR5cGVTcGVjaWZpZXIiCiAgICAgICAgICAgICAgICAgIH0gXQogICAgICAgICAgICAgICB9CiAgICAgICAgICAgIH0gXQogICAgICAgICB9LCB7CiAgICAgICAgICAgICJsb2NhdG9yIiA6ICIxMDc6MS0xMDk6MjQiLAogICAgICAgICAgICAibmFtZSIgOiAiVG9JbnRlcnZhbEZyb21MaXN0IiwKICAgICAgICAgICAgImNvbnRleHQiIDogIlBhdGllbnQiLAogICAgICAgICAgICAiYWNjZXNzTGV2ZWwiIDogIlB1YmxpYyIsCiAgICAgICAgICAgICJ0eXBlIiA6ICJGdW5jdGlvbkRlZiIsCiAgICAgICAgICAgICJhbm5vdGF0aW9uIiA6IFsgewogICAgICAgICAgICAgICAidHlwZSIgOiAiQW5ub3RhdGlvbiIsCiAgICAgICAgICAgICAgICJ0IiA6IFsgewogICAgICAgICAgICAgICAgICAibmFtZSIgOiAiZGVzY3JpcHRpb24iLAogICAgICAgICAgICAgICAgICAidmFsdWUiIDogIlJldHVybnMgYSBsaXN0IG9mIGludGVydmFscyByZXByZXNlbnRpbmcgdGhlIG5vcm1hbGl6ZWQgRXZlbnQgb3IgQm91bmRzIG9mIGEgVGltaW5nIHJlc291cmNlLiIKICAgICAgICAgICAgICAgfSwgewogICAgICAgICAgICAgICAgICAibmFtZSIgOiAiY29tbWVudCIsCiAgICAgICAgICAgICAgICAgICJ2YWx1ZSIgOiAiTk9URTogVGltaW5nLkJvdW5kcyBpcyByZXN0cmljdGVkIHRvIGEgUGVyaW9kLiIKICAgICAgICAgICAgICAgfSBdCiAgICAgICAgICAgIH0gXSwKICAgICAgICAgICAgImV4cHJlc3Npb24iIDogewogICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMTA4OjMtMTA5OjI0IiwKICAgICAgICAgICAgICAgInR5cGUiIDogIlF1ZXJ5IiwKICAgICAgICAgICAgICAgInNvdXJjZSIgOiBbIHsKICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjEwODozLTEwODo5IiwKICAgICAgICAgICAgICAgICAgImFsaWFzIiA6ICJ2IiwKICAgICAgICAgICAgICAgICAgImV4cHJlc3Npb24iIDogewogICAgICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMTA4OjMtMTA4OjciLAogICAgICAgICAgICAgICAgICAgICAibmFtZSIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAgICAgICAidHlwZSIgOiAiT3BlcmFuZFJlZiIKICAgICAgICAgICAgICAgICAgfQogICAgICAgICAgICAgICB9IF0sCiAgICAgICAgICAgICAgICJyZWxhdGlvbnNoaXAiIDogWyBdLAogICAgICAgICAgICAgICAicmV0dXJuIiA6IHsKICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjEwOTo1LTEwOToyNCIsCiAgICAgICAgICAgICAgICAgICJleHByZXNzaW9uIiA6IHsKICAgICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjEwOToxMi0xMDk6MjQiLAogICAgICAgICAgICAgICAgICAgICAibmFtZSIgOiAiVG9JbnRlcnZhbCIsCiAgICAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJGdW5jdGlvblJlZiIsCiAgICAgICAgICAgICAgICAgICAgICJvcGVyYW5kIiA6IFsgewogICAgICAgICAgICAgICAgICAgICAgICAidHlwZSIgOiAiQXMiLAogICAgICAgICAgICAgICAgICAgICAgICAib3BlcmFuZCIgOiB7CiAgICAgICAgICAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIxMDk6MjMiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAibmFtZSIgOiAidiIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJBbGlhc1JlZiIKICAgICAgICAgICAgICAgICAgICAgICAgfSwKICAgICAgICAgICAgICAgICAgICAgICAgImFzVHlwZVNwZWNpZmllciIgOiB7CiAgICAgICAgICAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJDaG9pY2VUeXBlU3BlY2lmaWVyIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgImNob2ljZSIgOiBbIHsKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIm5hbWUiIDogIntodHRwOi8vaGw3Lm9yZy9maGlyfWRhdGVUaW1lIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk5hbWVkVHlwZVNwZWNpZmllciIKICAgICAgICAgICAgICAgICAgICAgICAgICAgfSwgewogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAibmFtZSIgOiAie2h0dHA6Ly9obDcub3JnL2ZoaXJ9UGVyaW9kIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk5hbWVkVHlwZVNwZWNpZmllciIKICAgICAgICAgICAgICAgICAgICAgICAgICAgfSwgewogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAibmFtZSIgOiAie2h0dHA6Ly9obDcub3JnL2ZoaXJ9VGltaW5nIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk5hbWVkVHlwZVNwZWNpZmllciIKICAgICAgICAgICAgICAgICAgICAgICAgICAgfSwgewogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAibmFtZSIgOiAie2h0dHA6Ly9obDcub3JnL2ZoaXJ9aW5zdGFudCIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJOYW1lZFR5cGVTcGVjaWZpZXIiCiAgICAgICAgICAgICAgICAgICAgICAgICAgIH0sIHsKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIm5hbWUiIDogIntodHRwOi8vaGw3Lm9yZy9maGlyfXN0cmluZyIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJOYW1lZFR5cGVTcGVjaWZpZXIiCiAgICAgICAgICAgICAgICAgICAgICAgICAgIH0sIHsKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIm5hbWUiIDogIntodHRwOi8vaGw3Lm9yZy9maGlyfUFnZSIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJOYW1lZFR5cGVTcGVjaWZpZXIiCiAgICAgICAgICAgICAgICAgICAgICAgICAgIH0sIHsKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIm5hbWUiIDogIntodHRwOi8vaGw3Lm9yZy9maGlyfVJhbmdlIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk5hbWVkVHlwZVNwZWNpZmllciIKICAgICAgICAgICAgICAgICAgICAgICAgICAgfSBdCiAgICAgICAgICAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgICAgICAgICAgfSBdCiAgICAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgICAgfQogICAgICAgICAgICB9LAogICAgICAgICAgICAib3BlcmFuZCIgOiBbIHsKICAgICAgICAgICAgICAgIm5hbWUiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgIm9wZXJhbmRUeXBlU3BlY2lmaWVyIiA6IHsKICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjEwNzo0Mi0xMDc6NjAiLAogICAgICAgICAgICAgICAgICAidHlwZSIgOiAiTGlzdFR5cGVTcGVjaWZpZXIiLAogICAgICAgICAgICAgICAgICAiZWxlbWVudFR5cGUiIDogewogICAgICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMTA3OjQ3LTEwNzo1OSIsCiAgICAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJ7aHR0cDovL2hsNy5vcmcvZmhpcn1kYXRlVGltZSIsCiAgICAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJOYW1lZFR5cGVTcGVjaWZpZXIiCiAgICAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgICAgfQogICAgICAgICAgICB9IF0KICAgICAgICAgfSwgewogICAgICAgICAgICAibG9jYXRvciIgOiAiMTExOjEtMTEyOjQyIiwKICAgICAgICAgICAgIm5hbWUiIDogIk51bGxUb0VtcHR5SW50ZXJ2YWwiLAogICAgICAgICAgICAiY29udGV4dCIgOiAiUGF0aWVudCIsCiAgICAgICAgICAgICJhY2Nlc3NMZXZlbCIgOiAiUHVibGljIiwKICAgICAgICAgICAgInR5cGUiIDogIkZ1bmN0aW9uRGVmIiwKICAgICAgICAgICAgImV4cHJlc3Npb24iIDogewogICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMTEyOjMtMTEyOjQyIiwKICAgICAgICAgICAgICAgInR5cGUiIDogIklmIiwKICAgICAgICAgICAgICAgImNvbmRpdGlvbiIgOiB7CiAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIxMTI6Ni0xMTI6MjIiLAogICAgICAgICAgICAgICAgICAidHlwZSIgOiAiTm90IiwKICAgICAgICAgICAgICAgICAgIm9wZXJhbmQiIDogewogICAgICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMTEyOjYtMTEyOjIyIiwKICAgICAgICAgICAgICAgICAgICAgInR5cGUiIDogIklzTnVsbCIsCiAgICAgICAgICAgICAgICAgICAgICJvcGVyYW5kIiA6IHsKICAgICAgICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjExMjo2LTExMjoxMCIsCiAgICAgICAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJPcGVyYW5kUmVmIgogICAgICAgICAgICAgICAgICAgICB9CiAgICAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgICAgfSwKICAgICAgICAgICAgICAgInRoZW4iIDogewogICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMTEyOjI5LTExMjozMyIsCiAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJPcGVyYW5kUmVmIgogICAgICAgICAgICAgICB9LAogICAgICAgICAgICAgICAiZWxzZSIgOiB7CiAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIxMTI6NDAtMTEyOjQyIiwKICAgICAgICAgICAgICAgICAgInR5cGUiIDogIkxpc3QiCiAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgfSwKICAgICAgICAgICAgIm9wZXJhbmQiIDogWyB7CiAgICAgICAgICAgICAgICJuYW1lIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICJvcGVyYW5kVHlwZVNwZWNpZmllciIgOiB7CiAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIxMTE6NDMtMTExOjY2IiwKICAgICAgICAgICAgICAgICAgInR5cGUiIDogIkxpc3RUeXBlU3BlY2lmaWVyIiwKICAgICAgICAgICAgICAgICAgImVsZW1lbnRUeXBlIiA6IHsKICAgICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjExMTo0OC0xMTE6NjUiLAogICAgICAgICAgICAgICAgICAgICAidHlwZSIgOiAiSW50ZXJ2YWxUeXBlU3BlY2lmaWVyIiwKICAgICAgICAgICAgICAgICAgICAgInBvaW50VHlwZSIgOiB7CiAgICAgICAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIxMTE6NTctMTExOjY0IiwKICAgICAgICAgICAgICAgICAgICAgICAgIm5hbWUiIDogInt1cm46aGw3LW9yZzplbG0tdHlwZXM6cjF9RGF0ZVRpbWUiLAogICAgICAgICAgICAgICAgICAgICAgICAidHlwZSIgOiAiTmFtZWRUeXBlU3BlY2lmaWVyIgogICAgICAgICAgICAgICAgICAgICB9CiAgICAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgICAgfQogICAgICAgICAgICB9IF0KICAgICAgICAgfSwgewogICAgICAgICAgICAibG9jYXRvciIgOiAiMTE0OjEtMTE4OjQ3IiwKICAgICAgICAgICAgIm5hbWUiIDogIlRvVGltaW5nSW50ZXJ2YWwiLAogICAgICAgICAgICAiY29udGV4dCIgOiAiUGF0aWVudCIsCiAgICAgICAgICAgICJhY2Nlc3NMZXZlbCIgOiAiUHVibGljIiwKICAgICAgICAgICAgInR5cGUiIDogIkZ1bmN0aW9uRGVmIiwKICAgICAgICAgICAgImV4cHJlc3Npb24iIDogewogICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMTE1OjMtMTE4OjQ3IiwKICAgICAgICAgICAgICAgInR5cGUiIDogIkV4Y2VwdCIsCiAgICAgICAgICAgICAgICJvcGVyYW5kIiA6IFsgewogICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMTE1OjMtMTE4OjMxIiwKICAgICAgICAgICAgICAgICAgInR5cGUiIDogIlF1ZXJ5IiwKICAgICAgICAgICAgICAgICAgInNvdXJjZSIgOiBbIHsKICAgICAgICAgICAgICAgICAgICAgImFsaWFzIiA6ICJYIiwKICAgICAgICAgICAgICAgICAgICAgImV4cHJlc3Npb24iIDogewogICAgICAgICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMTE1OjExLTExODozIiwKICAgICAgICAgICAgICAgICAgICAgICAgInR5cGUiIDogIlVuaW9uIiwKICAgICAgICAgICAgICAgICAgICAgICAgIm9wZXJhbmQiIDogWyB7CiAgICAgICAgICAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIxMTY6NS0xMTY6NTciLAogICAgICAgICAgICAgICAgICAgICAgICAgICAibmFtZSIgOiAiTnVsbFRvRW1wdHlJbnRlcnZhbCIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJGdW5jdGlvblJlZiIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICJvcGVyYW5kIiA6IFsgewogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMTE2OjI1LTExNjo1NiIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJUb0ludGVydmFsRnJvbUxpc3QiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAidHlwZSIgOiAiRnVuY3Rpb25SZWYiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAib3BlcmFuZCIgOiBbIHsKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjExNjo0NC0xMTY6NTUiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAicGF0aCIgOiAiZXZlbnQiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAidHlwZSIgOiAiUHJvcGVydHkiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAic291cmNlIiA6IHsKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjExNjo0NC0xMTY6NDkiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAibmFtZSIgOiAidGltaW5nIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk9wZXJhbmRSZWYiCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfSBdCiAgICAgICAgICAgICAgICAgICAgICAgICAgIH0gXQogICAgICAgICAgICAgICAgICAgICAgICB9LCB7CiAgICAgICAgICAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIxMTc6MTEtMTE3OjgwIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgIm5hbWUiIDogIk51bGxUb0VtcHR5SW50ZXJ2YWwiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAidHlwZSIgOiAiRnVuY3Rpb25SZWYiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAib3BlcmFuZCIgOiBbIHsKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjExNzozMS0xMTc6NzkiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAidHlwZSIgOiAiTGlzdCIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJlbGVtZW50IiA6IFsgewogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMTE3OjMyLTExNzo3OCIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJUb0ludGVydmFsIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInR5cGUiIDogIkZ1bmN0aW9uUmVmIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIm9wZXJhbmQiIDogWyB7CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJBcyIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJvcGVyYW5kIiA6IHsKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjExNzo0My0xMTc6NzciLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAic3RyaWN0IiA6IGZhbHNlLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAidHlwZSIgOiAiQXMiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAib3BlcmFuZCIgOiB7CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIxMTc6NDMtMTE3OjYyIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInBhdGgiIDogImJvdW5kcyIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJQcm9wZXJ0eSIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJzb3VyY2UiIDogewogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMTE3OjQzLTExNzo1NSIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJwYXRoIiA6ICJyZXBlYXQiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAidHlwZSIgOiAiUHJvcGVydHkiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAic291cmNlIiA6IHsKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjExNzo0My0xMTc6NDgiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAibmFtZSIgOiAidGltaW5nIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk9wZXJhbmRSZWYiCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB9LAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAiYXNUeXBlU3BlY2lmaWVyIiA6IHsKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjExNzo2Ny0xMTc6NzciLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAibmFtZSIgOiAie2h0dHA6Ly9obDcub3JnL2ZoaXJ9UGVyaW9kIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk5hbWVkVHlwZVNwZWNpZmllciIKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB9LAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAiYXNUeXBlU3BlY2lmaWVyIiA6IHsKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInR5cGUiIDogIkNob2ljZVR5cGVTcGVjaWZpZXIiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAiY2hvaWNlIiA6IFsgewogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAibmFtZSIgOiAie2h0dHA6Ly9obDcub3JnL2ZoaXJ9ZGF0ZVRpbWUiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAidHlwZSIgOiAiTmFtZWRUeXBlU3BlY2lmaWVyIgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB9LCB7CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJ7aHR0cDovL2hsNy5vcmcvZmhpcn1QZXJpb2QiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAidHlwZSIgOiAiTmFtZWRUeXBlU3BlY2lmaWVyIgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB9LCB7CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJ7aHR0cDovL2hsNy5vcmcvZmhpcn1UaW1pbmciLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAidHlwZSIgOiAiTmFtZWRUeXBlU3BlY2lmaWVyIgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB9LCB7CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJ7aHR0cDovL2hsNy5vcmcvZmhpcn1pbnN0YW50IiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk5hbWVkVHlwZVNwZWNpZmllciIKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfSwgewogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAibmFtZSIgOiAie2h0dHA6Ly9obDcub3JnL2ZoaXJ9c3RyaW5nIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk5hbWVkVHlwZVNwZWNpZmllciIKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfSwgewogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAibmFtZSIgOiAie2h0dHA6Ly9obDcub3JnL2ZoaXJ9QWdlIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk5hbWVkVHlwZVNwZWNpZmllciIKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfSwgewogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAibmFtZSIgOiAie2h0dHA6Ly9obDcub3JnL2ZoaXJ9UmFuZ2UiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAidHlwZSIgOiAiTmFtZWRUeXBlU3BlY2lmaWVyIgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB9IF0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB9IF0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfSBdCiAgICAgICAgICAgICAgICAgICAgICAgICAgIH0gXQogICAgICAgICAgICAgICAgICAgICAgICB9IF0KICAgICAgICAgICAgICAgICAgICAgfQogICAgICAgICAgICAgICAgICB9IF0sCiAgICAgICAgICAgICAgICAgICJyZXR1cm4iIDogewogICAgICAgICAgICAgICAgICAgICAiZGlzdGluY3QiIDogZmFsc2UsCiAgICAgICAgICAgICAgICAgICAgICJleHByZXNzaW9uIiA6IHsKICAgICAgICAgICAgICAgICAgICAgICAgInR5cGUiIDogIkFzIiwKICAgICAgICAgICAgICAgICAgICAgICAgIm9wZXJhbmQiIDogewogICAgICAgICAgICAgICAgICAgICAgICAgICAibmFtZSIgOiAiWCIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJBbGlhc1JlZiIKICAgICAgICAgICAgICAgICAgICAgICAgfSwKICAgICAgICAgICAgICAgICAgICAgICAgImFzVHlwZVNwZWNpZmllciIgOiB7CiAgICAgICAgICAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJJbnRlcnZhbFR5cGVTcGVjaWZpZXIiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAicG9pbnRUeXBlIiA6IHsKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIm5hbWUiIDogInt1cm46aGw3LW9yZzplbG0tdHlwZXM6cjF9RGF0ZVRpbWUiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAidHlwZSIgOiAiTmFtZWRUeXBlU3BlY2lmaWVyIgogICAgICAgICAgICAgICAgICAgICAgICAgICB9CiAgICAgICAgICAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgICAgICAgICAgfQogICAgICAgICAgICAgICAgICB9CiAgICAgICAgICAgICAgIH0sIHsKICAgICAgICAgICAgICAgICAgInR5cGUiIDogIlF1ZXJ5IiwKICAgICAgICAgICAgICAgICAgInNvdXJjZSIgOiBbIHsKICAgICAgICAgICAgICAgICAgICAgImFsaWFzIiA6ICJYIiwKICAgICAgICAgICAgICAgICAgICAgImV4cHJlc3Npb24iIDogewogICAgICAgICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMTE4OjQwLTExODo0NyIsCiAgICAgICAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJMaXN0IiwKICAgICAgICAgICAgICAgICAgICAgICAgImVsZW1lbnQiIDogWyB7CiAgICAgICAgICAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIxMTg6NDItMTE4OjQ1IiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk51bGwiCiAgICAgICAgICAgICAgICAgICAgICAgIH0gXQogICAgICAgICAgICAgICAgICAgICB9CiAgICAgICAgICAgICAgICAgIH0gXSwKICAgICAgICAgICAgICAgICAgInJldHVybiIgOiB7CiAgICAgICAgICAgICAgICAgICAgICJkaXN0aW5jdCIgOiBmYWxzZSwKICAgICAgICAgICAgICAgICAgICAgImV4cHJlc3Npb24iIDogewogICAgICAgICAgICAgICAgICAgICAgICAidHlwZSIgOiAiQXMiLAogICAgICAgICAgICAgICAgICAgICAgICAib3BlcmFuZCIgOiB7CiAgICAgICAgICAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJYIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgInR5cGUiIDogIkFsaWFzUmVmIgogICAgICAgICAgICAgICAgICAgICAgICB9LAogICAgICAgICAgICAgICAgICAgICAgICAiYXNUeXBlU3BlY2lmaWVyIiA6IHsKICAgICAgICAgICAgICAgICAgICAgICAgICAgInR5cGUiIDogIkludGVydmFsVHlwZVNwZWNpZmllciIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICJwb2ludFR5cGUiIDogewogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAibmFtZSIgOiAie3VybjpobDctb3JnOmVsbS10eXBlczpyMX1EYXRlVGltZSIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJOYW1lZFR5cGVTcGVjaWZpZXIiCiAgICAgICAgICAgICAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgICAgICAgICAgICAgfQogICAgICAgICAgICAgICAgICAgICB9CiAgICAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgICAgfSBdCiAgICAgICAgICAgIH0sCiAgICAgICAgICAgICJvcGVyYW5kIiA6IFsgewogICAgICAgICAgICAgICAibmFtZSIgOiAidGltaW5nIiwKICAgICAgICAgICAgICAgIm9wZXJhbmRUeXBlU3BlY2lmaWVyIiA6IHsKICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjExNDo0MS0xMTQ6NTEiLAogICAgICAgICAgICAgICAgICAibmFtZSIgOiAie2h0dHA6Ly9obDcub3JnL2ZoaXJ9VGltaW5nIiwKICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk5hbWVkVHlwZVNwZWNpZmllciIKICAgICAgICAgICAgICAgfQogICAgICAgICAgICB9IF0KICAgICAgICAgfSwgewogICAgICAgICAgICAibG9jYXRvciIgOiAiMTM1OjEtMTU0OjEwIiwKICAgICAgICAgICAgIm5hbWUiIDogIlRvQWJhdGVtZW50SW50ZXJ2YWwiLAogICAgICAgICAgICAiY29udGV4dCIgOiAiUGF0aWVudCIsCiAgICAgICAgICAgICJhY2Nlc3NMZXZlbCIgOiAiUHVibGljIiwKICAgICAgICAgICAgInR5cGUiIDogIkZ1bmN0aW9uRGVmIiwKICAgICAgICAgICAgImFubm90YXRpb24iIDogWyB7CiAgICAgICAgICAgICAgICJ0eXBlIiA6ICJBbm5vdGF0aW9uIiwKICAgICAgICAgICAgICAgInQiIDogWyB7CiAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJkZXNjcmlwdGlvbiIsCiAgICAgICAgICAgICAgICAgICJ2YWx1ZSIgOiAicmV0dXJucyB0aGUgZXhpc3RpbmcgcG9zc2libGUgdGltZXN0YW1wIGZvciBvYnNlcnZhdGlvbiByZXNvdXJjZXMsIHByZWZlcnJpbmcgZWZmZWN0aXZlIG92ZXIgaXNzdWVkLiIKICAgICAgICAgICAgICAgfSwgewogICAgICAgICAgICAgICAgICAibmFtZSIgOiAiY29tbWVudCIsCiAgICAgICAgICAgICAgICAgICJ2YWx1ZSIgOiAiTk9URTogbmVpdGhlciBlZmZlY3RpdmUgbm9yIGlzc3VlZCBhcmUgcmVxdWlyZWQgZmllbGRzIGluIFFJLUNvcmVcbk5PVEU6IEZ1bmN0aW9uIG92ZXJsb2FkcyBpbmNvbnNpc3RlbnRseSBlcnIgaW4gQ1FMIDEuNSwgdGhpcyBpcyBjb21tZW50ZWQgdW50aWwgYSBmaXggaXMgZm91bmRcbmRlZmluZSBmdW5jdGlvbiBUb0ludGVydmFsKG9ic2VydmF0aW9uIEZISVIuT2JzZXJ2YXRpb24pOlxuICBvYnNlcnZhdGlvbiBvXG4gICAgcmV0dXJuIENvYWxlc2NlKFRvSW50ZXJ2YWwoby5lZmZlY3RpdmUpLCBUb0ludGVydmFsKG8uaXNzdWVkKSkiCiAgICAgICAgICAgICAgIH0sIHsKICAgICAgICAgICAgICAgICAgIm5hbWUiIDogImRlc2NyaXB0aW9uIiwKICAgICAgICAgICAgICAgICAgInZhbHVlIiA6ICJSZXR1cm5zIGFuIGludGVydmFsIHJlcHJlc2VudGluZyB0aGUgbm9ybWFsaXplZCBBYmF0ZW1lbnQgb2YgYSBnaXZlbiBDb25kaXRpb24gcmVzb3VyY2UuIgogICAgICAgICAgICAgICB9LCB7CiAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJjb21tZW50IiwKICAgICAgICAgICAgICAgICAgInZhbHVlIiA6ICJOT1RFOiBEdWUgdG8gdGhlIGNvbXBsZXhpdHkgb2YgZGV0ZXJtaW5pbmcgYW4gaW50ZXJ2YWwgZnJvbSBhIFN0cmluZywgdGhpcyBmdW5jdGlvbiB3aWxsIHRocm93XG5hIHJ1bi10aW1lIGV4Y2VwdGlvbiBpZiB1c2VkIHdpdGggYSBDb25kaXRpb24gaW5zdGFuY2UgdGhhdCBoYXMgYSBTdHJpbmcgYXMgdGhlIGFiYXRlbWVudCB2YWx1ZS4iCiAgICAgICAgICAgICAgIH0gXQogICAgICAgICAgICB9IF0sCiAgICAgICAgICAgICJleHByZXNzaW9uIiA6IHsKICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjEzNjozLTE1NDoxMCIsCiAgICAgICAgICAgICAgICJ0eXBlIiA6ICJJZiIsCiAgICAgICAgICAgICAgICJjb25kaXRpb24iIDogewogICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMTM2OjYtMTM4OjQ0IiwKICAgICAgICAgICAgICAgICAgInR5cGUiIDogIkFuZCIsCiAgICAgICAgICAgICAgICAgICJvcGVyYW5kIiA6IFsgewogICAgICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMTM2OjYtMTM2OjY0IiwKICAgICAgICAgICAgICAgICAgICAgInR5cGUiIDogIkFuZCIsCiAgICAgICAgICAgICAgICAgICAgICJvcGVyYW5kIiA6IFsgewogICAgICAgICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMTM2OjYtMTM2OjMyIiwKICAgICAgICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk5vdCIsCiAgICAgICAgICAgICAgICAgICAgICAgICJvcGVyYW5kIiA6IHsKICAgICAgICAgICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjEzNjo2LTEzNjozMiIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJJc051bGwiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAib3BlcmFuZCIgOiB7CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIxMzY6Ni0xMzY6MjAiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAicGF0aCIgOiAib25zZXQiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAidHlwZSIgOiAiUHJvcGVydHkiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAic291cmNlIiA6IHsKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjEzNjo2LTEzNjoxNCIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJjb25kaXRpb24iLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAidHlwZSIgOiAiT3BlcmFuZFJlZiIKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfQogICAgICAgICAgICAgICAgICAgICAgICAgICB9CiAgICAgICAgICAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgICAgICAgICAgfSwgewogICAgICAgICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMTM2OjM4LTEzNjo2NCIsCiAgICAgICAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJJc051bGwiLAogICAgICAgICAgICAgICAgICAgICAgICAib3BlcmFuZCIgOiB7CiAgICAgICAgICAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIxMzY6MzgtMTM2OjU2IiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgInBhdGgiIDogImFiYXRlbWVudCIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJQcm9wZXJ0eSIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICJzb3VyY2UiIDogewogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMTM2OjM4LTEzNjo0NiIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJjb25kaXRpb24iLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAidHlwZSIgOiAiT3BlcmFuZFJlZiIKICAgICAgICAgICAgICAgICAgICAgICAgICAgfQogICAgICAgICAgICAgICAgICAgICAgICB9CiAgICAgICAgICAgICAgICAgICAgIH0gXQogICAgICAgICAgICAgICAgICB9LCB7CiAgICAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIxMzY6NzAtMTM4OjQ0IiwKICAgICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk9yIiwKICAgICAgICAgICAgICAgICAgICAgIm9wZXJhbmQiIDogWyB7CiAgICAgICAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIxMzY6NzEtMTM3OjQ2IiwKICAgICAgICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk9yIiwKICAgICAgICAgICAgICAgICAgICAgICAgIm9wZXJhbmQiIDogWyB7CiAgICAgICAgICAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIxMzY6NzEtMTM2OjEwNSIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJFcXVpdmFsZW50IiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgIm9wZXJhbmQiIDogWyB7CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJUb0NvbmNlcHQiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAibGlicmFyeU5hbWUiIDogIkZISVJIZWxwZXJzIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInR5cGUiIDogIkZ1bmN0aW9uUmVmIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIm9wZXJhbmQiIDogWyB7CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIxMzY6NzEtMTM2Ojk0IiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInBhdGgiIDogImNsaW5pY2FsU3RhdHVzIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInR5cGUiIDogIlByb3BlcnR5IiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInNvdXJjZSIgOiB7CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIxMzY6NzEtMTM2Ojc5IiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIm5hbWUiIDogImNvbmRpdGlvbiIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJPcGVyYW5kUmVmIgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB9CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIH0gXQogICAgICAgICAgICAgICAgICAgICAgICAgICB9LCB7CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJUb0NvbmNlcHQiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAib3BlcmFuZCIgOiB7CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIxMzY6OTgtMTM2OjEwNSIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJhY3RpdmUiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAidHlwZSIgOiAiQ29kZVJlZiIKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfQogICAgICAgICAgICAgICAgICAgICAgICAgICB9IF0KICAgICAgICAgICAgICAgICAgICAgICAgfSwgewogICAgICAgICAgICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMTM3OjgtMTM3OjQ2IiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgInR5cGUiIDogIkVxdWl2YWxlbnQiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAib3BlcmFuZCIgOiBbIHsKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIm5hbWUiIDogIlRvQ29uY2VwdCIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJsaWJyYXJ5TmFtZSIgOiAiRkhJUkhlbHBlcnMiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAidHlwZSIgOiAiRnVuY3Rpb25SZWYiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAib3BlcmFuZCIgOiBbIHsKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjEzNzo4LTEzNzozMSIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJwYXRoIiA6ICJjbGluaWNhbFN0YXR1cyIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJQcm9wZXJ0eSIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJzb3VyY2UiIDogewogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMTM3OjgtMTM3OjE2IiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIm5hbWUiIDogImNvbmRpdGlvbiIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJPcGVyYW5kUmVmIgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB9CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIH0gXQogICAgICAgICAgICAgICAgICAgICAgICAgICB9LCB7CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJUb0NvbmNlcHQiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAib3BlcmFuZCIgOiB7CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIxMzc6MzUtMTM3OjQ2IiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIm5hbWUiIDogInJlY3VycmVuY2UiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAidHlwZSIgOiAiQ29kZVJlZiIKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfQogICAgICAgICAgICAgICAgICAgICAgICAgICB9IF0KICAgICAgICAgICAgICAgICAgICAgICAgfSBdCiAgICAgICAgICAgICAgICAgICAgIH0sIHsKICAgICAgICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjEzODo4LTEzODo0MyIsCiAgICAgICAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJFcXVpdmFsZW50IiwKICAgICAgICAgICAgICAgICAgICAgICAgIm9wZXJhbmQiIDogWyB7CiAgICAgICAgICAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJUb0NvbmNlcHQiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAibGlicmFyeU5hbWUiIDogIkZISVJIZWxwZXJzIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgInR5cGUiIDogIkZ1bmN0aW9uUmVmIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgIm9wZXJhbmQiIDogWyB7CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIxMzg6OC0xMzg6MzEiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAicGF0aCIgOiAiY2xpbmljYWxTdGF0dXMiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAidHlwZSIgOiAiUHJvcGVydHkiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAic291cmNlIiA6IHsKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjEzODo4LTEzODoxNiIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJjb25kaXRpb24iLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAidHlwZSIgOiAiT3BlcmFuZFJlZiIKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfQogICAgICAgICAgICAgICAgICAgICAgICAgICB9IF0KICAgICAgICAgICAgICAgICAgICAgICAgfSwgewogICAgICAgICAgICAgICAgICAgICAgICAgICAidHlwZSIgOiAiVG9Db25jZXB0IiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgIm9wZXJhbmQiIDogewogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMTM4OjM1LTEzODo0MyIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJyZWxhcHNlIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInR5cGUiIDogIkNvZGVSZWYiCiAgICAgICAgICAgICAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgICAgICAgICAgICAgfSBdCiAgICAgICAgICAgICAgICAgICAgIH0gXQogICAgICAgICAgICAgICAgICB9IF0KICAgICAgICAgICAgICAgfSwKICAgICAgICAgICAgICAgInRoZW4iIDogewogICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMTM5OjUtMTM5OjI2IiwKICAgICAgICAgICAgICAgICAgImxvd0Nsb3NlZCIgOiB0cnVlLAogICAgICAgICAgICAgICAgICAiaGlnaENsb3NlZCIgOiB0cnVlLAogICAgICAgICAgICAgICAgICAidHlwZSIgOiAiSW50ZXJ2YWwiLAogICAgICAgICAgICAgICAgICAibG93IiA6IHsKICAgICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjEzOToxNC0xMzk6MTgiLAogICAgICAgICAgICAgICAgICAgICAidHlwZSIgOiAiTm93IgogICAgICAgICAgICAgICAgICB9LAogICAgICAgICAgICAgICAgICAiaGlnaCIgOiB7CiAgICAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIxMzk6MjEtMTM5OjI1IiwKICAgICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk5vdyIKICAgICAgICAgICAgICAgICAgfQogICAgICAgICAgICAgICB9LAogICAgICAgICAgICAgICAiZWxzZSIgOiB7CiAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIxNDA6OC0xNTQ6MTAiLAogICAgICAgICAgICAgICAgICAidHlwZSIgOiAiSWYiLAogICAgICAgICAgICAgICAgICAiY29uZGl0aW9uIiA6IHsKICAgICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjE0MDoxMS0xNDA6NDYiLAogICAgICAgICAgICAgICAgICAgICAidHlwZSIgOiAiSXMiLAogICAgICAgICAgICAgICAgICAgICAib3BlcmFuZCIgOiB7CiAgICAgICAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIxNDA6MTEtMTQwOjI5IiwKICAgICAgICAgICAgICAgICAgICAgICAgInBhdGgiIDogImFiYXRlbWVudCIsCiAgICAgICAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJQcm9wZXJ0eSIsCiAgICAgICAgICAgICAgICAgICAgICAgICJzb3VyY2UiIDogewogICAgICAgICAgICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMTQwOjExLTE0MDoxOSIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJjb25kaXRpb24iLAogICAgICAgICAgICAgICAgICAgICAgICAgICAidHlwZSIgOiAiT3BlcmFuZFJlZiIKICAgICAgICAgICAgICAgICAgICAgICAgfQogICAgICAgICAgICAgICAgICAgICB9LAogICAgICAgICAgICAgICAgICAgICAiaXNUeXBlU3BlY2lmaWVyIiA6IHsKICAgICAgICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjE0MDozNC0xNDA6NDYiLAogICAgICAgICAgICAgICAgICAgICAgICAibmFtZSIgOiAie2h0dHA6Ly9obDcub3JnL2ZoaXJ9ZGF0ZVRpbWUiLAogICAgICAgICAgICAgICAgICAgICAgICAidHlwZSIgOiAiTmFtZWRUeXBlU3BlY2lmaWVyIgogICAgICAgICAgICAgICAgICAgICB9CiAgICAgICAgICAgICAgICAgIH0sCiAgICAgICAgICAgICAgICAgICJ0aGVuIiA6IHsKICAgICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjE0MTo0LTE0MToxMzUiLAogICAgICAgICAgICAgICAgICAgICAibG93Q2xvc2VkIiA6IHRydWUsCiAgICAgICAgICAgICAgICAgICAgICJoaWdoQ2xvc2VkIiA6IHRydWUsCiAgICAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJJbnRlcnZhbCIsCiAgICAgICAgICAgICAgICAgICAgICJsb3ciIDogewogICAgICAgICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMTQxOjEzLTE0MTo3MiIsCiAgICAgICAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJUb0RhdGVUaW1lIiwKICAgICAgICAgICAgICAgICAgICAgICAgImxpYnJhcnlOYW1lIiA6ICJGSElSSGVscGVycyIsCiAgICAgICAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJGdW5jdGlvblJlZiIsCiAgICAgICAgICAgICAgICAgICAgICAgICJvcGVyYW5kIiA6IFsgewogICAgICAgICAgICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMTQxOjM2LTE0MTo3MSIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICJzdHJpY3QiIDogZmFsc2UsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJBcyIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICJvcGVyYW5kIiA6IHsKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjE0MTozNi0xNDE6NTQiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAicGF0aCIgOiAiYWJhdGVtZW50IiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInR5cGUiIDogIlByb3BlcnR5IiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInNvdXJjZSIgOiB7CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIxNDE6MzYtMTQxOjQ0IiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIm5hbWUiIDogImNvbmRpdGlvbiIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJPcGVyYW5kUmVmIgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICB9CiAgICAgICAgICAgICAgICAgICAgICAgICAgIH0sCiAgICAgICAgICAgICAgICAgICAgICAgICAgICJhc1R5cGVTcGVjaWZpZXIiIDogewogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMTQxOjU5LTE0MTo3MSIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJ7aHR0cDovL2hsNy5vcmcvZmhpcn1kYXRlVGltZSIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJOYW1lZFR5cGVTcGVjaWZpZXIiCiAgICAgICAgICAgICAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgICAgICAgICAgICAgfSBdCiAgICAgICAgICAgICAgICAgICAgIH0sCiAgICAgICAgICAgICAgICAgICAgICJoaWdoIiA6IHsKICAgICAgICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjE0MTo3NS0xNDE6MTM0IiwKICAgICAgICAgICAgICAgICAgICAgICAgIm5hbWUiIDogIlRvRGF0ZVRpbWUiLAogICAgICAgICAgICAgICAgICAgICAgICAibGlicmFyeU5hbWUiIDogIkZISVJIZWxwZXJzIiwKICAgICAgICAgICAgICAgICAgICAgICAgInR5cGUiIDogIkZ1bmN0aW9uUmVmIiwKICAgICAgICAgICAgICAgICAgICAgICAgIm9wZXJhbmQiIDogWyB7CiAgICAgICAgICAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIxNDE6OTgtMTQxOjEzMyIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICJzdHJpY3QiIDogZmFsc2UsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJBcyIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICJvcGVyYW5kIiA6IHsKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjE0MTo5OC0xNDE6MTE2IiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInBhdGgiIDogImFiYXRlbWVudCIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJQcm9wZXJ0eSIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJzb3VyY2UiIDogewogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMTQxOjk4LTE0MToxMDYiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAibmFtZSIgOiAiY29uZGl0aW9uIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk9wZXJhbmRSZWYiCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgICAgICAgICAgICAgICAgfSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgImFzVHlwZVNwZWNpZmllciIgOiB7CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIxNDE6MTIxLTE0MToxMzMiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAibmFtZSIgOiAie2h0dHA6Ly9obDcub3JnL2ZoaXJ9ZGF0ZVRpbWUiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAidHlwZSIgOiAiTmFtZWRUeXBlU3BlY2lmaWVyIgogICAgICAgICAgICAgICAgICAgICAgICAgICB9CiAgICAgICAgICAgICAgICAgICAgICAgIH0gXQogICAgICAgICAgICAgICAgICAgICB9CiAgICAgICAgICAgICAgICAgIH0sCiAgICAgICAgICAgICAgICAgICJlbHNlIiA6IHsKICAgICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjE0Mjo3LTE1NDoxMCIsCiAgICAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJJZiIsCiAgICAgICAgICAgICAgICAgICAgICJjb25kaXRpb24iIDogewogICAgICAgICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMTQyOjEwLTE0Mjo0MyIsCiAgICAgICAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJJcyIsCiAgICAgICAgICAgICAgICAgICAgICAgICJvcGVyYW5kIiA6IHsKICAgICAgICAgICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjE0MjoxMC0xNDI6MjgiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAicGF0aCIgOiAiYWJhdGVtZW50IiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgInR5cGUiIDogIlByb3BlcnR5IiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgInNvdXJjZSIgOiB7CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIxNDI6MTAtMTQyOjE4IiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIm5hbWUiIDogImNvbmRpdGlvbiIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJPcGVyYW5kUmVmIgogICAgICAgICAgICAgICAgICAgICAgICAgICB9CiAgICAgICAgICAgICAgICAgICAgICAgIH0sCiAgICAgICAgICAgICAgICAgICAgICAgICJpc1R5cGVTcGVjaWZpZXIiIDogewogICAgICAgICAgICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMTQyOjMzLTE0Mjo0MyIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJ7aHR0cDovL2hsNy5vcmcvZmhpcn1QZXJpb2QiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAidHlwZSIgOiAiTmFtZWRUeXBlU3BlY2lmaWVyIgogICAgICAgICAgICAgICAgICAgICAgICB9CiAgICAgICAgICAgICAgICAgICAgIH0sCiAgICAgICAgICAgICAgICAgICAgICJ0aGVuIiA6IHsKICAgICAgICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjE0Mzo0LTE0Mzo2MSIsCiAgICAgICAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJUb0ludGVydmFsIiwKICAgICAgICAgICAgICAgICAgICAgICAgImxpYnJhcnlOYW1lIiA6ICJGSElSSGVscGVycyIsCiAgICAgICAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJGdW5jdGlvblJlZiIsCiAgICAgICAgICAgICAgICAgICAgICAgICJvcGVyYW5kIiA6IFsgewogICAgICAgICAgICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMTQzOjI3LTE0Mzo2MCIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICJzdHJpY3QiIDogZmFsc2UsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJBcyIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICJvcGVyYW5kIiA6IHsKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjE0MzoyNy0xNDM6NDUiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAicGF0aCIgOiAiYWJhdGVtZW50IiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInR5cGUiIDogIlByb3BlcnR5IiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInNvdXJjZSIgOiB7CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIxNDM6MjctMTQzOjM1IiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIm5hbWUiIDogImNvbmRpdGlvbiIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJPcGVyYW5kUmVmIgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICB9CiAgICAgICAgICAgICAgICAgICAgICAgICAgIH0sCiAgICAgICAgICAgICAgICAgICAgICAgICAgICJhc1R5cGVTcGVjaWZpZXIiIDogewogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMTQzOjUwLTE0Mzo2MCIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJ7aHR0cDovL2hsNy5vcmcvZmhpcn1QZXJpb2QiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAidHlwZSIgOiAiTmFtZWRUeXBlU3BlY2lmaWVyIgogICAgICAgICAgICAgICAgICAgICAgICAgICB9CiAgICAgICAgICAgICAgICAgICAgICAgIH0gXQogICAgICAgICAgICAgICAgICAgICB9LAogICAgICAgICAgICAgICAgICAgICAiZWxzZSIgOiB7CiAgICAgICAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIxNDQ6Ny0xNTQ6MTAiLAogICAgICAgICAgICAgICAgICAgICAgICAidHlwZSIgOiAiSWYiLAogICAgICAgICAgICAgICAgICAgICAgICAiY29uZGl0aW9uIiA6IHsKICAgICAgICAgICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjE0NDoxMC0xNDQ6NDMiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAidHlwZSIgOiAiSXMiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAib3BlcmFuZCIgOiB7CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIxNDQ6MTAtMTQ0OjI4IiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInBhdGgiIDogImFiYXRlbWVudCIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJQcm9wZXJ0eSIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJzb3VyY2UiIDogewogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMTQ0OjEwLTE0NDoxOCIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJjb25kaXRpb24iLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAidHlwZSIgOiAiT3BlcmFuZFJlZiIKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfQogICAgICAgICAgICAgICAgICAgICAgICAgICB9LAogICAgICAgICAgICAgICAgICAgICAgICAgICAiaXNUeXBlU3BlY2lmaWVyIiA6IHsKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjE0NDozMy0xNDQ6NDMiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAibmFtZSIgOiAie2h0dHA6Ly9obDcub3JnL2ZoaXJ9c3RyaW5nIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk5hbWVkVHlwZVNwZWNpZmllciIKICAgICAgICAgICAgICAgICAgICAgICAgICAgfQogICAgICAgICAgICAgICAgICAgICAgICB9LAogICAgICAgICAgICAgICAgICAgICAgICAidGhlbiIgOiB7CiAgICAgICAgICAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIxNDU6NS0xNDU6MTA5IiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk1lc3NhZ2UiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAic291cmNlIiA6IHsKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjE0NToxMy0xNDU6MzgiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAic3RyaWN0IiA6IGZhbHNlLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAidHlwZSIgOiAiQXMiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAib3BlcmFuZCIgOiB7CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIxNDU6MTMtMTQ1OjE2IiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk51bGwiCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIH0sCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJhc1R5cGVTcGVjaWZpZXIiIDogewogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMTQ1OjIxLTE0NTozOCIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJJbnRlcnZhbFR5cGVTcGVjaWZpZXIiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAicG9pbnRUeXBlIiA6IHsKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjE0NTozMC0xNDU6MzciLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAibmFtZSIgOiAie3VybjpobDctb3JnOmVsbS10eXBlczpyMX1EYXRlVGltZSIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJOYW1lZFR5cGVTcGVjaWZpZXIiCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfQogICAgICAgICAgICAgICAgICAgICAgICAgICB9LAogICAgICAgICAgICAgICAgICAgICAgICAgICAiY29uZGl0aW9uIiA6IHsKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjE0NTo0MS0xNDU6NDQiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAidmFsdWVUeXBlIiA6ICJ7dXJuOmhsNy1vcmc6ZWxtLXR5cGVzOnIxfUJvb2xlYW4iLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAidmFsdWUiIDogInRydWUiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAidHlwZSIgOiAiTGl0ZXJhbCIKICAgICAgICAgICAgICAgICAgICAgICAgICAgfSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgImNvZGUiIDogewogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMTQ1OjQ3LTE0NTo0OSIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJ2YWx1ZVR5cGUiIDogInt1cm46aGw3LW9yZzplbG0tdHlwZXM6cjF9U3RyaW5nIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInZhbHVlIiA6ICIxIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInR5cGUiIDogIkxpdGVyYWwiCiAgICAgICAgICAgICAgICAgICAgICAgICAgIH0sCiAgICAgICAgICAgICAgICAgICAgICAgICAgICJzZXZlcml0eSIgOiB7CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIxNDU6NTItMTQ1OjU4IiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInZhbHVlVHlwZSIgOiAie3VybjpobDctb3JnOmVsbS10eXBlczpyMX1TdHJpbmciLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAidmFsdWUiIDogIkVycm9yIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInR5cGUiIDogIkxpdGVyYWwiCiAgICAgICAgICAgICAgICAgICAgICAgICAgIH0sCiAgICAgICAgICAgICAgICAgICAgICAgICAgICJtZXNzYWdlIiA6IHsKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjE0NTo2MS0xNDU6MTA4IiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInZhbHVlVHlwZSIgOiAie3VybjpobDctb3JnOmVsbS10eXBlczpyMX1TdHJpbmciLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAidmFsdWUiIDogIkNhbm5vdCBjb21wdXRlIGFuIGludGVydmFsIGZyb20gYSBTdHJpbmcgdmFsdWUiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAidHlwZSIgOiAiTGl0ZXJhbCIKICAgICAgICAgICAgICAgICAgICAgICAgICAgfQogICAgICAgICAgICAgICAgICAgICAgICB9LAogICAgICAgICAgICAgICAgICAgICAgICAiZWxzZSIgOiB7CiAgICAgICAgICAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIxNDY6Ny0xNTQ6MTAiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAidHlwZSIgOiAiSWYiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAiY29uZGl0aW9uIiA6IHsKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjE0NjoxMC0xNDY6NDAiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAidHlwZSIgOiAiSXMiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAib3BlcmFuZCIgOiB7CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIxNDY6MTAtMTQ2OjI4IiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInBhdGgiIDogImFiYXRlbWVudCIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJQcm9wZXJ0eSIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJzb3VyY2UiIDogewogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMTQ2OjEwLTE0NjoxOCIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJjb25kaXRpb24iLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAidHlwZSIgOiAiT3BlcmFuZFJlZiIKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICB9LAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAiaXNUeXBlU3BlY2lmaWVyIiA6IHsKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjE0NjozMy0xNDY6NDAiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAibmFtZSIgOiAie2h0dHA6Ly9obDcub3JnL2ZoaXJ9QWdlIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk5hbWVkVHlwZVNwZWNpZmllciIKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfQogICAgICAgICAgICAgICAgICAgICAgICAgICB9LAogICAgICAgICAgICAgICAgICAgICAgICAgICAidGhlbiIgOiB7CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJJbnRlcnZhbCIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJsb3ciIDogewogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAidHlwZSIgOiAiVG9EYXRlVGltZSIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJvcGVyYW5kIiA6IHsKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInBhdGgiIDogImxvdyIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJQcm9wZXJ0eSIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJzb3VyY2UiIDogewogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMTQ3OjMtMTQ4OjEwOCIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJsb3dDbG9zZWQiIDogdHJ1ZSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgImhpZ2hDbG9zZWQiIDogZmFsc2UsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJJbnRlcnZhbCIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJsb3ciIDogewogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMTQ3OjEyLTE0NzoxMDYiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAidHlwZSIgOiAiQWRkIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIm9wZXJhbmQiIDogWyB7CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIxNDc6MTItMTQ3OjQ4IiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIm5hbWUiIDogIlRvRGF0ZSIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJsaWJyYXJ5TmFtZSIgOiAiRkhJUkhlbHBlcnMiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAidHlwZSIgOiAiRnVuY3Rpb25SZWYiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAib3BlcmFuZCIgOiBbIHsKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjE0NzozMS0xNDc6NDciLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAicGF0aCIgOiAiYmlydGhEYXRlIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInR5cGUiIDogIlByb3BlcnR5IiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInNvdXJjZSIgOiB7CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIxNDc6MzEtMTQ3OjM3IiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIm5hbWUiIDogIlBhdGllbnQiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAidHlwZSIgOiAiRXhwcmVzc2lvblJlZiIKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB9IF0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfSwgewogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMTQ3OjUyLTE0NzoxMDYiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAibmFtZSIgOiAiVG9RdWFudGl0eSIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJsaWJyYXJ5TmFtZSIgOiAiRkhJUkhlbHBlcnMiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAidHlwZSIgOiAiRnVuY3Rpb25SZWYiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAib3BlcmFuZCIgOiBbIHsKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjE0Nzo3NS0xNDc6MTA1IiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInN0cmljdCIgOiBmYWxzZSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInR5cGUiIDogIkFzIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIm9wZXJhbmQiIDogewogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMTQ3Ojc1LTE0Nzo5MyIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJwYXRoIiA6ICJhYmF0ZW1lbnQiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAidHlwZSIgOiAiUHJvcGVydHkiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAic291cmNlIiA6IHsKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjE0Nzo3NS0xNDc6ODMiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAibmFtZSIgOiAiY29uZGl0aW9uIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk9wZXJhbmRSZWYiCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgImFzVHlwZVNwZWNpZmllciIgOiB7CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIxNDc6OTgtMTQ3OjEwNSIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJ7aHR0cDovL2hsNy5vcmcvZmhpcn1BZ2UiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAidHlwZSIgOiAiTmFtZWRUeXBlU3BlY2lmaWVyIgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB9CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIH0gXQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB9IF0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgImhpZ2giIDogewogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMTQ4OjQtMTQ4OjEwNyIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJBZGQiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAib3BlcmFuZCIgOiBbIHsKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjE0ODo0LTE0ODo5OCIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJBZGQiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAib3BlcmFuZCIgOiBbIHsKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjE0ODo0LTE0ODo0MCIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJUb0RhdGUiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAibGlicmFyeU5hbWUiIDogIkZISVJIZWxwZXJzIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInR5cGUiIDogIkZ1bmN0aW9uUmVmIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIm9wZXJhbmQiIDogWyB7CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIxNDg6MjMtMTQ4OjM5IiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInBhdGgiIDogImJpcnRoRGF0ZSIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJQcm9wZXJ0eSIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJzb3VyY2UiIDogewogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMTQ4OjIzLTE0ODoyOSIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJQYXRpZW50IiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInR5cGUiIDogIkV4cHJlc3Npb25SZWYiCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfSBdCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIH0sIHsKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjE0ODo0NC0xNDg6OTgiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAibmFtZSIgOiAiVG9RdWFudGl0eSIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJsaWJyYXJ5TmFtZSIgOiAiRkhJUkhlbHBlcnMiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAidHlwZSIgOiAiRnVuY3Rpb25SZWYiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAib3BlcmFuZCIgOiBbIHsKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjE0ODo2Ny0xNDg6OTciLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAic3RyaWN0IiA6IGZhbHNlLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAidHlwZSIgOiAiQXMiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAib3BlcmFuZCIgOiB7CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIxNDg6NjctMTQ4Ojg1IiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInBhdGgiIDogImFiYXRlbWVudCIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJQcm9wZXJ0eSIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJzb3VyY2UiIDogewogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMTQ4OjY3LTE0ODo3NSIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJjb25kaXRpb24iLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAidHlwZSIgOiAiT3BlcmFuZFJlZiIKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB9LAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAiYXNUeXBlU3BlY2lmaWVyIiA6IHsKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjE0ODo5MC0xNDg6OTciLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAibmFtZSIgOiAie2h0dHA6Ly9obDcub3JnL2ZoaXJ9QWdlIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk5hbWVkVHlwZVNwZWNpZmllciIKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB9IF0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfSBdCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIH0sIHsKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjE0ODoxMDItMTQ4OjEwNyIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJ2YWx1ZSIgOiAxLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAidW5pdCIgOiAieWVhciIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJRdWFudGl0eSIKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfSBdCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB9CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIH0sCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJsb3dDbG9zZWRFeHByZXNzaW9uIiA6IHsKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInBhdGgiIDogImxvd0Nsb3NlZCIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJQcm9wZXJ0eSIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJzb3VyY2UiIDogewogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMTQ3OjMtMTQ4OjEwOCIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJsb3dDbG9zZWQiIDogdHJ1ZSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgImhpZ2hDbG9zZWQiIDogZmFsc2UsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJJbnRlcnZhbCIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJsb3ciIDogewogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMTQ3OjEyLTE0NzoxMDYiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAidHlwZSIgOiAiQWRkIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIm9wZXJhbmQiIDogWyB7CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIxNDc6MTItMTQ3OjQ4IiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIm5hbWUiIDogIlRvRGF0ZSIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJsaWJyYXJ5TmFtZSIgOiAiRkhJUkhlbHBlcnMiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAidHlwZSIgOiAiRnVuY3Rpb25SZWYiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAib3BlcmFuZCIgOiBbIHsKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjE0NzozMS0xNDc6NDciLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAicGF0aCIgOiAiYmlydGhEYXRlIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInR5cGUiIDogIlByb3BlcnR5IiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInNvdXJjZSIgOiB7CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIxNDc6MzEtMTQ3OjM3IiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIm5hbWUiIDogIlBhdGllbnQiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAidHlwZSIgOiAiRXhwcmVzc2lvblJlZiIKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB9IF0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfSwgewogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMTQ3OjUyLTE0NzoxMDYiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAibmFtZSIgOiAiVG9RdWFudGl0eSIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJsaWJyYXJ5TmFtZSIgOiAiRkhJUkhlbHBlcnMiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAidHlwZSIgOiAiRnVuY3Rpb25SZWYiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAib3BlcmFuZCIgOiBbIHsKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjE0Nzo3NS0xNDc6MTA1IiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInN0cmljdCIgOiBmYWxzZSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInR5cGUiIDogIkFzIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIm9wZXJhbmQiIDogewogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMTQ3Ojc1LTE0Nzo5MyIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJwYXRoIiA6ICJhYmF0ZW1lbnQiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAidHlwZSIgOiAiUHJvcGVydHkiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAic291cmNlIiA6IHsKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjE0Nzo3NS0xNDc6ODMiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAibmFtZSIgOiAiY29uZGl0aW9uIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk9wZXJhbmRSZWYiCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgImFzVHlwZVNwZWNpZmllciIgOiB7CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIxNDc6OTgtMTQ3OjEwNSIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJ7aHR0cDovL2hsNy5vcmcvZmhpcn1BZ2UiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAidHlwZSIgOiAiTmFtZWRUeXBlU3BlY2lmaWVyIgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB9CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIH0gXQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB9IF0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgImhpZ2giIDogewogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMTQ4OjQtMTQ4OjEwNyIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJBZGQiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAib3BlcmFuZCIgOiBbIHsKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjE0ODo0LTE0ODo5OCIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJBZGQiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAib3BlcmFuZCIgOiBbIHsKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjE0ODo0LTE0ODo0MCIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJUb0RhdGUiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAibGlicmFyeU5hbWUiIDogIkZISVJIZWxwZXJzIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInR5cGUiIDogIkZ1bmN0aW9uUmVmIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIm9wZXJhbmQiIDogWyB7CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIxNDg6MjMtMTQ4OjM5IiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInBhdGgiIDogImJpcnRoRGF0ZSIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJQcm9wZXJ0eSIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJzb3VyY2UiIDogewogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMTQ4OjIzLTE0ODoyOSIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJQYXRpZW50IiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInR5cGUiIDogIkV4cHJlc3Npb25SZWYiCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfSBdCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIH0sIHsKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjE0ODo0NC0xNDg6OTgiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAibmFtZSIgOiAiVG9RdWFudGl0eSIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJsaWJyYXJ5TmFtZSIgOiAiRkhJUkhlbHBlcnMiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAidHlwZSIgOiAiRnVuY3Rpb25SZWYiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAib3BlcmFuZCIgOiBbIHsKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjE0ODo2Ny0xNDg6OTciLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAic3RyaWN0IiA6IGZhbHNlLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAidHlwZSIgOiAiQXMiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAib3BlcmFuZCIgOiB7CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIxNDg6NjctMTQ4Ojg1IiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInBhdGgiIDogImFiYXRlbWVudCIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJQcm9wZXJ0eSIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJzb3VyY2UiIDogewogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMTQ4OjY3LTE0ODo3NSIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJjb25kaXRpb24iLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAidHlwZSIgOiAiT3BlcmFuZFJlZiIKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB9LAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAiYXNUeXBlU3BlY2lmaWVyIiA6IHsKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjE0ODo5MC0xNDg6OTciLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAibmFtZSIgOiAie2h0dHA6Ly9obDcub3JnL2ZoaXJ9QWdlIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk5hbWVkVHlwZVNwZWNpZmllciIKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB9IF0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfSBdCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIH0sIHsKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjE0ODoxMDItMTQ4OjEwNyIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJ2YWx1ZSIgOiAxLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAidW5pdCIgOiAieWVhciIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJRdWFudGl0eSIKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfSBdCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICB9LAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAiaGlnaCIgOiB7CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJUb0RhdGVUaW1lIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIm9wZXJhbmQiIDogewogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAicGF0aCIgOiAiaGlnaCIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJQcm9wZXJ0eSIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJzb3VyY2UiIDogewogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMTQ3OjMtMTQ4OjEwOCIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJsb3dDbG9zZWQiIDogdHJ1ZSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgImhpZ2hDbG9zZWQiIDogZmFsc2UsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJJbnRlcnZhbCIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJsb3ciIDogewogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMTQ3OjEyLTE0NzoxMDYiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAidHlwZSIgOiAiQWRkIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIm9wZXJhbmQiIDogWyB7CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIxNDc6MTItMTQ3OjQ4IiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIm5hbWUiIDogIlRvRGF0ZSIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJsaWJyYXJ5TmFtZSIgOiAiRkhJUkhlbHBlcnMiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAidHlwZSIgOiAiRnVuY3Rpb25SZWYiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAib3BlcmFuZCIgOiBbIHsKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjE0NzozMS0xNDc6NDciLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAicGF0aCIgOiAiYmlydGhEYXRlIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInR5cGUiIDogIlByb3BlcnR5IiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInNvdXJjZSIgOiB7CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIxNDc6MzEtMTQ3OjM3IiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIm5hbWUiIDogIlBhdGllbnQiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAidHlwZSIgOiAiRXhwcmVzc2lvblJlZiIKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB9IF0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfSwgewogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMTQ3OjUyLTE0NzoxMDYiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAibmFtZSIgOiAiVG9RdWFudGl0eSIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJsaWJyYXJ5TmFtZSIgOiAiRkhJUkhlbHBlcnMiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAidHlwZSIgOiAiRnVuY3Rpb25SZWYiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAib3BlcmFuZCIgOiBbIHsKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjE0Nzo3NS0xNDc6MTA1IiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInN0cmljdCIgOiBmYWxzZSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInR5cGUiIDogIkFzIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIm9wZXJhbmQiIDogewogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMTQ3Ojc1LTE0Nzo5MyIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJwYXRoIiA6ICJhYmF0ZW1lbnQiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAidHlwZSIgOiAiUHJvcGVydHkiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAic291cmNlIiA6IHsKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjE0Nzo3NS0xNDc6ODMiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAibmFtZSIgOiAiY29uZGl0aW9uIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk9wZXJhbmRSZWYiCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgImFzVHlwZVNwZWNpZmllciIgOiB7CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIxNDc6OTgtMTQ3OjEwNSIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJ7aHR0cDovL2hsNy5vcmcvZmhpcn1BZ2UiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAidHlwZSIgOiAiTmFtZWRUeXBlU3BlY2lmaWVyIgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB9CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIH0gXQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB9IF0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgImhpZ2giIDogewogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMTQ4OjQtMTQ4OjEwNyIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJBZGQiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAib3BlcmFuZCIgOiBbIHsKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjE0ODo0LTE0ODo5OCIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJBZGQiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAib3BlcmFuZCIgOiBbIHsKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjE0ODo0LTE0ODo0MCIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJUb0RhdGUiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAibGlicmFyeU5hbWUiIDogIkZISVJIZWxwZXJzIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInR5cGUiIDogIkZ1bmN0aW9uUmVmIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIm9wZXJhbmQiIDogWyB7CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIxNDg6MjMtMTQ4OjM5IiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInBhdGgiIDogImJpcnRoRGF0ZSIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJQcm9wZXJ0eSIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJzb3VyY2UiIDogewogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMTQ4OjIzLTE0ODoyOSIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJQYXRpZW50IiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInR5cGUiIDogIkV4cHJlc3Npb25SZWYiCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfSBdCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIH0sIHsKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjE0ODo0NC0xNDg6OTgiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAibmFtZSIgOiAiVG9RdWFudGl0eSIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJsaWJyYXJ5TmFtZSIgOiAiRkhJUkhlbHBlcnMiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAidHlwZSIgOiAiRnVuY3Rpb25SZWYiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAib3BlcmFuZCIgOiBbIHsKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjE0ODo2Ny0xNDg6OTciLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAic3RyaWN0IiA6IGZhbHNlLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAidHlwZSIgOiAiQXMiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAib3BlcmFuZCIgOiB7CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIxNDg6NjctMTQ4Ojg1IiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInBhdGgiIDogImFiYXRlbWVudCIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJQcm9wZXJ0eSIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJzb3VyY2UiIDogewogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMTQ4OjY3LTE0ODo3NSIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJjb25kaXRpb24iLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAidHlwZSIgOiAiT3BlcmFuZFJlZiIKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB9LAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAiYXNUeXBlU3BlY2lmaWVyIiA6IHsKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjE0ODo5MC0xNDg6OTciLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAibmFtZSIgOiAie2h0dHA6Ly9obDcub3JnL2ZoaXJ9QWdlIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk5hbWVkVHlwZVNwZWNpZmllciIKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB9IF0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfSBdCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIH0sIHsKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjE0ODoxMDItMTQ4OjEwNyIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJ2YWx1ZSIgOiAxLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAidW5pdCIgOiAieWVhciIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJRdWFudGl0eSIKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfSBdCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB9CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIH0sCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJoaWdoQ2xvc2VkRXhwcmVzc2lvbiIgOiB7CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJwYXRoIiA6ICJoaWdoQ2xvc2VkIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInR5cGUiIDogIlByb3BlcnR5IiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInNvdXJjZSIgOiB7CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIxNDc6My0xNDg6MTA4IiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgImxvd0Nsb3NlZCIgOiB0cnVlLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAiaGlnaENsb3NlZCIgOiBmYWxzZSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInR5cGUiIDogIkludGVydmFsIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgImxvdyIgOiB7CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIxNDc6MTItMTQ3OjEwNiIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJBZGQiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAib3BlcmFuZCIgOiBbIHsKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjE0NzoxMi0xNDc6NDgiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAibmFtZSIgOiAiVG9EYXRlIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgImxpYnJhcnlOYW1lIiA6ICJGSElSSGVscGVycyIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJGdW5jdGlvblJlZiIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJvcGVyYW5kIiA6IFsgewogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMTQ3OjMxLTE0Nzo0NyIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJwYXRoIiA6ICJiaXJ0aERhdGUiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAidHlwZSIgOiAiUHJvcGVydHkiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAic291cmNlIiA6IHsKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjE0NzozMS0xNDc6MzciLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAibmFtZSIgOiAiUGF0aWVudCIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJFeHByZXNzaW9uUmVmIgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB9CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIH0gXQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB9LCB7CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIxNDc6NTItMTQ3OjEwNiIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJUb1F1YW50aXR5IiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgImxpYnJhcnlOYW1lIiA6ICJGSElSSGVscGVycyIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJGdW5jdGlvblJlZiIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJvcGVyYW5kIiA6IFsgewogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMTQ3Ojc1LTE0NzoxMDUiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAic3RyaWN0IiA6IGZhbHNlLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAidHlwZSIgOiAiQXMiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAib3BlcmFuZCIgOiB7CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIxNDc6NzUtMTQ3OjkzIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInBhdGgiIDogImFiYXRlbWVudCIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJQcm9wZXJ0eSIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJzb3VyY2UiIDogewogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMTQ3Ojc1LTE0Nzo4MyIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJjb25kaXRpb24iLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAidHlwZSIgOiAiT3BlcmFuZFJlZiIKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB9LAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAiYXNUeXBlU3BlY2lmaWVyIiA6IHsKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjE0Nzo5OC0xNDc6MTA1IiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIm5hbWUiIDogIntodHRwOi8vaGw3Lm9yZy9maGlyfUFnZSIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJOYW1lZFR5cGVTcGVjaWZpZXIiCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfSBdCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIH0gXQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB9LAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAiaGlnaCIgOiB7CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIxNDg6NC0xNDg6MTA3IiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInR5cGUiIDogIkFkZCIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJvcGVyYW5kIiA6IFsgewogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMTQ4OjQtMTQ4Ojk4IiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInR5cGUiIDogIkFkZCIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJvcGVyYW5kIiA6IFsgewogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMTQ4OjQtMTQ4OjQwIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIm5hbWUiIDogIlRvRGF0ZSIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJsaWJyYXJ5TmFtZSIgOiAiRkhJUkhlbHBlcnMiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAidHlwZSIgOiAiRnVuY3Rpb25SZWYiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAib3BlcmFuZCIgOiBbIHsKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjE0ODoyMy0xNDg6MzkiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAicGF0aCIgOiAiYmlydGhEYXRlIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInR5cGUiIDogIlByb3BlcnR5IiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInNvdXJjZSIgOiB7CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIxNDg6MjMtMTQ4OjI5IiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIm5hbWUiIDogIlBhdGllbnQiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAidHlwZSIgOiAiRXhwcmVzc2lvblJlZiIKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB9IF0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfSwgewogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMTQ4OjQ0LTE0ODo5OCIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJUb1F1YW50aXR5IiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgImxpYnJhcnlOYW1lIiA6ICJGSElSSGVscGVycyIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJGdW5jdGlvblJlZiIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJvcGVyYW5kIiA6IFsgewogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMTQ4OjY3LTE0ODo5NyIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJzdHJpY3QiIDogZmFsc2UsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJBcyIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJvcGVyYW5kIiA6IHsKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjE0ODo2Ny0xNDg6ODUiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAicGF0aCIgOiAiYWJhdGVtZW50IiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInR5cGUiIDogIlByb3BlcnR5IiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInNvdXJjZSIgOiB7CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIxNDg6NjctMTQ4Ojc1IiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIm5hbWUiIDogImNvbmRpdGlvbiIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJPcGVyYW5kUmVmIgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB9CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIH0sCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJhc1R5cGVTcGVjaWZpZXIiIDogewogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMTQ4OjkwLTE0ODo5NyIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJ7aHR0cDovL2hsNy5vcmcvZmhpcn1BZ2UiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAidHlwZSIgOiAiTmFtZWRUeXBlU3BlY2lmaWVyIgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB9CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIH0gXQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB9IF0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfSwgewogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMTQ4OjEwMi0xNDg6MTA3IiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInZhbHVlIiA6IDEsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJ1bml0IiA6ICJ5ZWFyIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInR5cGUiIDogIlF1YW50aXR5IgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB9IF0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB9CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgICAgICAgICAgICAgICAgfSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgImVsc2UiIDogewogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMTQ5OjctMTU0OjEwIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInR5cGUiIDogIklmIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgImNvbmRpdGlvbiIgOiB7CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIxNDk6MTAtMTQ5OjQyIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInR5cGUiIDogIklzIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIm9wZXJhbmQiIDogewogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMTQ5OjEwLTE0OToyOCIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJwYXRoIiA6ICJhYmF0ZW1lbnQiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAidHlwZSIgOiAiUHJvcGVydHkiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAic291cmNlIiA6IHsKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjE0OToxMC0xNDk6MTgiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAibmFtZSIgOiAiY29uZGl0aW9uIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk9wZXJhbmRSZWYiCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgImlzVHlwZVNwZWNpZmllciIgOiB7CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIxNDk6MzMtMTQ5OjQyIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIm5hbWUiIDogIntodHRwOi8vaGw3Lm9yZy9maGlyfVJhbmdlIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk5hbWVkVHlwZVNwZWNpZmllciIKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICB9LAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAidGhlbiIgOiB7CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJJbnRlcnZhbCIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJsb3ciIDogewogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAidHlwZSIgOiAiVG9EYXRlVGltZSIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJvcGVyYW5kIiA6IHsKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInBhdGgiIDogImxvdyIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJQcm9wZXJ0eSIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJzb3VyY2UiIDogewogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMTUwOjQtMTUxOjExOCIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJsb3dDbG9zZWQiIDogdHJ1ZSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgImhpZ2hDbG9zZWQiIDogZmFsc2UsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJJbnRlcnZhbCIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJsb3ciIDogewogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMTUwOjEzLTE1MDoxMTUiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAidHlwZSIgOiAiQWRkIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIm9wZXJhbmQiIDogWyB7CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIxNTA6MTMtMTUwOjQ5IiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIm5hbWUiIDogIlRvRGF0ZSIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJsaWJyYXJ5TmFtZSIgOiAiRkhJUkhlbHBlcnMiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAidHlwZSIgOiAiRnVuY3Rpb25SZWYiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAib3BlcmFuZCIgOiBbIHsKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjE1MDozMi0xNTA6NDgiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAicGF0aCIgOiAiYmlydGhEYXRlIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInR5cGUiIDogIlByb3BlcnR5IiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInNvdXJjZSIgOiB7CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIxNTA6MzItMTUwOjM4IiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIm5hbWUiIDogIlBhdGllbnQiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAidHlwZSIgOiAiRXhwcmVzc2lvblJlZiIKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB9IF0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfSwgewogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMTUwOjUzLTE1MDoxMTUiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAibmFtZSIgOiAiVG9RdWFudGl0eSIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJsaWJyYXJ5TmFtZSIgOiAiRkhJUkhlbHBlcnMiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAidHlwZSIgOiAiRnVuY3Rpb25SZWYiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAib3BlcmFuZCIgOiBbIHsKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjE1MDo3Ni0xNTA6MTE0IiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInBhdGgiIDogImxvdyIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJQcm9wZXJ0eSIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJzb3VyY2UiIDogewogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMTUwOjc2LTE1MDoxMTAiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAic3RyaWN0IiA6IGZhbHNlLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAidHlwZSIgOiAiQXMiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAib3BlcmFuZCIgOiB7CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIxNTA6NzctMTUwOjk1IiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInBhdGgiIDogImFiYXRlbWVudCIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJQcm9wZXJ0eSIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJzb3VyY2UiIDogewogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMTUwOjc3LTE1MDo4NSIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJjb25kaXRpb24iLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAidHlwZSIgOiAiT3BlcmFuZFJlZiIKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB9LAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAiYXNUeXBlU3BlY2lmaWVyIiA6IHsKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjE1MDoxMDAtMTUwOjEwOSIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJ7aHR0cDovL2hsNy5vcmcvZmhpcn1SYW5nZSIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJOYW1lZFR5cGVTcGVjaWZpZXIiCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB9IF0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfSBdCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIH0sCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJoaWdoIiA6IHsKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjE1MTo1LTE1MToxMTciLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAidHlwZSIgOiAiQWRkIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIm9wZXJhbmQiIDogWyB7CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIxNTE6NS0xNTE6MTA4IiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInR5cGUiIDogIkFkZCIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJvcGVyYW5kIiA6IFsgewogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMTUxOjUtMTUxOjQxIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIm5hbWUiIDogIlRvRGF0ZSIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJsaWJyYXJ5TmFtZSIgOiAiRkhJUkhlbHBlcnMiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAidHlwZSIgOiAiRnVuY3Rpb25SZWYiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAib3BlcmFuZCIgOiBbIHsKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjE1MToyNC0xNTE6NDAiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAicGF0aCIgOiAiYmlydGhEYXRlIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInR5cGUiIDogIlByb3BlcnR5IiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInNvdXJjZSIgOiB7CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIxNTE6MjQtMTUxOjMwIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIm5hbWUiIDogIlBhdGllbnQiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAidHlwZSIgOiAiRXhwcmVzc2lvblJlZiIKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB9IF0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfSwgewogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMTUxOjQ1LTE1MToxMDgiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAibmFtZSIgOiAiVG9RdWFudGl0eSIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJsaWJyYXJ5TmFtZSIgOiAiRkhJUkhlbHBlcnMiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAidHlwZSIgOiAiRnVuY3Rpb25SZWYiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAib3BlcmFuZCIgOiBbIHsKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjE1MTo2OC0xNTE6MTA3IiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInBhdGgiIDogImhpZ2giLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAidHlwZSIgOiAiUHJvcGVydHkiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAic291cmNlIiA6IHsKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjE1MTo2OC0xNTE6MTAyIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInN0cmljdCIgOiBmYWxzZSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInR5cGUiIDogIkFzIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIm9wZXJhbmQiIDogewogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMTUxOjY5LTE1MTo4NyIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJwYXRoIiA6ICJhYmF0ZW1lbnQiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAidHlwZSIgOiAiUHJvcGVydHkiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAic291cmNlIiA6IHsKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjE1MTo2OS0xNTE6NzciLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAibmFtZSIgOiAiY29uZGl0aW9uIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk9wZXJhbmRSZWYiCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgImFzVHlwZVNwZWNpZmllciIgOiB7CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIxNTE6OTItMTUxOjEwMSIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJ7aHR0cDovL2hsNy5vcmcvZmhpcn1SYW5nZSIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJOYW1lZFR5cGVTcGVjaWZpZXIiCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB9IF0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfSBdCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIH0sIHsKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjE1MToxMTItMTUxOjExNyIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJ2YWx1ZSIgOiAxLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAidW5pdCIgOiAieWVhciIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJRdWFudGl0eSIKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfSBdCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB9CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIH0sCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJsb3dDbG9zZWRFeHByZXNzaW9uIiA6IHsKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInBhdGgiIDogImxvd0Nsb3NlZCIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJQcm9wZXJ0eSIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJzb3VyY2UiIDogewogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMTUwOjQtMTUxOjExOCIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJsb3dDbG9zZWQiIDogdHJ1ZSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgImhpZ2hDbG9zZWQiIDogZmFsc2UsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJJbnRlcnZhbCIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJsb3ciIDogewogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMTUwOjEzLTE1MDoxMTUiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAidHlwZSIgOiAiQWRkIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIm9wZXJhbmQiIDogWyB7CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIxNTA6MTMtMTUwOjQ5IiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIm5hbWUiIDogIlRvRGF0ZSIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJsaWJyYXJ5TmFtZSIgOiAiRkhJUkhlbHBlcnMiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAidHlwZSIgOiAiRnVuY3Rpb25SZWYiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAib3BlcmFuZCIgOiBbIHsKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjE1MDozMi0xNTA6NDgiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAicGF0aCIgOiAiYmlydGhEYXRlIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInR5cGUiIDogIlByb3BlcnR5IiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInNvdXJjZSIgOiB7CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIxNTA6MzItMTUwOjM4IiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIm5hbWUiIDogIlBhdGllbnQiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAidHlwZSIgOiAiRXhwcmVzc2lvblJlZiIKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB9IF0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfSwgewogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMTUwOjUzLTE1MDoxMTUiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAibmFtZSIgOiAiVG9RdWFudGl0eSIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJsaWJyYXJ5TmFtZSIgOiAiRkhJUkhlbHBlcnMiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAidHlwZSIgOiAiRnVuY3Rpb25SZWYiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAib3BlcmFuZCIgOiBbIHsKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjE1MDo3Ni0xNTA6MTE0IiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInBhdGgiIDogImxvdyIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJQcm9wZXJ0eSIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJzb3VyY2UiIDogewogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMTUwOjc2LTE1MDoxMTAiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAic3RyaWN0IiA6IGZhbHNlLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAidHlwZSIgOiAiQXMiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAib3BlcmFuZCIgOiB7CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIxNTA6NzctMTUwOjk1IiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInBhdGgiIDogImFiYXRlbWVudCIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJQcm9wZXJ0eSIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJzb3VyY2UiIDogewogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMTUwOjc3LTE1MDo4NSIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJjb25kaXRpb24iLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAidHlwZSIgOiAiT3BlcmFuZFJlZiIKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB9LAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAiYXNUeXBlU3BlY2lmaWVyIiA6IHsKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjE1MDoxMDAtMTUwOjEwOSIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJ7aHR0cDovL2hsNy5vcmcvZmhpcn1SYW5nZSIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJOYW1lZFR5cGVTcGVjaWZpZXIiCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB9IF0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfSBdCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIH0sCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJoaWdoIiA6IHsKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjE1MTo1LTE1MToxMTciLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAidHlwZSIgOiAiQWRkIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIm9wZXJhbmQiIDogWyB7CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIxNTE6NS0xNTE6MTA4IiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInR5cGUiIDogIkFkZCIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJvcGVyYW5kIiA6IFsgewogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMTUxOjUtMTUxOjQxIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIm5hbWUiIDogIlRvRGF0ZSIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJsaWJyYXJ5TmFtZSIgOiAiRkhJUkhlbHBlcnMiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAidHlwZSIgOiAiRnVuY3Rpb25SZWYiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAib3BlcmFuZCIgOiBbIHsKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjE1MToyNC0xNTE6NDAiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAicGF0aCIgOiAiYmlydGhEYXRlIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInR5cGUiIDogIlByb3BlcnR5IiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInNvdXJjZSIgOiB7CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIxNTE6MjQtMTUxOjMwIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIm5hbWUiIDogIlBhdGllbnQiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAidHlwZSIgOiAiRXhwcmVzc2lvblJlZiIKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB9IF0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfSwgewogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMTUxOjQ1LTE1MToxMDgiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAibmFtZSIgOiAiVG9RdWFudGl0eSIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJsaWJyYXJ5TmFtZSIgOiAiRkhJUkhlbHBlcnMiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAidHlwZSIgOiAiRnVuY3Rpb25SZWYiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAib3BlcmFuZCIgOiBbIHsKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjE1MTo2OC0xNTE6MTA3IiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInBhdGgiIDogImhpZ2giLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAidHlwZSIgOiAiUHJvcGVydHkiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAic291cmNlIiA6IHsKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjE1MTo2OC0xNTE6MTAyIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInN0cmljdCIgOiBmYWxzZSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInR5cGUiIDogIkFzIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIm9wZXJhbmQiIDogewogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMTUxOjY5LTE1MTo4NyIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJwYXRoIiA6ICJhYmF0ZW1lbnQiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAidHlwZSIgOiAiUHJvcGVydHkiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAic291cmNlIiA6IHsKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjE1MTo2OS0xNTE6NzciLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAibmFtZSIgOiAiY29uZGl0aW9uIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk9wZXJhbmRSZWYiCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgImFzVHlwZVNwZWNpZmllciIgOiB7CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIxNTE6OTItMTUxOjEwMSIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJ7aHR0cDovL2hsNy5vcmcvZmhpcn1SYW5nZSIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJOYW1lZFR5cGVTcGVjaWZpZXIiCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB9IF0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfSBdCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIH0sIHsKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjE1MToxMTItMTUxOjExNyIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJ2YWx1ZSIgOiAxLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAidW5pdCIgOiAieWVhciIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJRdWFudGl0eSIKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfSBdCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB9LAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAiaGlnaCIgOiB7CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJUb0RhdGVUaW1lIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIm9wZXJhbmQiIDogewogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAicGF0aCIgOiAiaGlnaCIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJQcm9wZXJ0eSIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJzb3VyY2UiIDogewogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMTUwOjQtMTUxOjExOCIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJsb3dDbG9zZWQiIDogdHJ1ZSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgImhpZ2hDbG9zZWQiIDogZmFsc2UsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJJbnRlcnZhbCIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJsb3ciIDogewogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMTUwOjEzLTE1MDoxMTUiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAidHlwZSIgOiAiQWRkIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIm9wZXJhbmQiIDogWyB7CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIxNTA6MTMtMTUwOjQ5IiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIm5hbWUiIDogIlRvRGF0ZSIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJsaWJyYXJ5TmFtZSIgOiAiRkhJUkhlbHBlcnMiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAidHlwZSIgOiAiRnVuY3Rpb25SZWYiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAib3BlcmFuZCIgOiBbIHsKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjE1MDozMi0xNTA6NDgiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAicGF0aCIgOiAiYmlydGhEYXRlIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInR5cGUiIDogIlByb3BlcnR5IiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInNvdXJjZSIgOiB7CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIxNTA6MzItMTUwOjM4IiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIm5hbWUiIDogIlBhdGllbnQiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAidHlwZSIgOiAiRXhwcmVzc2lvblJlZiIKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB9IF0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfSwgewogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMTUwOjUzLTE1MDoxMTUiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAibmFtZSIgOiAiVG9RdWFudGl0eSIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJsaWJyYXJ5TmFtZSIgOiAiRkhJUkhlbHBlcnMiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAidHlwZSIgOiAiRnVuY3Rpb25SZWYiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAib3BlcmFuZCIgOiBbIHsKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjE1MDo3Ni0xNTA6MTE0IiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInBhdGgiIDogImxvdyIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJQcm9wZXJ0eSIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJzb3VyY2UiIDogewogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMTUwOjc2LTE1MDoxMTAiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAic3RyaWN0IiA6IGZhbHNlLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAidHlwZSIgOiAiQXMiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAib3BlcmFuZCIgOiB7CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIxNTA6NzctMTUwOjk1IiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInBhdGgiIDogImFiYXRlbWVudCIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJQcm9wZXJ0eSIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJzb3VyY2UiIDogewogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMTUwOjc3LTE1MDo4NSIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJjb25kaXRpb24iLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAidHlwZSIgOiAiT3BlcmFuZFJlZiIKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB9LAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAiYXNUeXBlU3BlY2lmaWVyIiA6IHsKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjE1MDoxMDAtMTUwOjEwOSIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJ7aHR0cDovL2hsNy5vcmcvZmhpcn1SYW5nZSIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJOYW1lZFR5cGVTcGVjaWZpZXIiCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB9IF0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfSBdCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIH0sCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJoaWdoIiA6IHsKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjE1MTo1LTE1MToxMTciLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAidHlwZSIgOiAiQWRkIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIm9wZXJhbmQiIDogWyB7CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIxNTE6NS0xNTE6MTA4IiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInR5cGUiIDogIkFkZCIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJvcGVyYW5kIiA6IFsgewogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMTUxOjUtMTUxOjQxIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIm5hbWUiIDogIlRvRGF0ZSIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJsaWJyYXJ5TmFtZSIgOiAiRkhJUkhlbHBlcnMiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAidHlwZSIgOiAiRnVuY3Rpb25SZWYiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAib3BlcmFuZCIgOiBbIHsKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjE1MToyNC0xNTE6NDAiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAicGF0aCIgOiAiYmlydGhEYXRlIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInR5cGUiIDogIlByb3BlcnR5IiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInNvdXJjZSIgOiB7CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIxNTE6MjQtMTUxOjMwIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIm5hbWUiIDogIlBhdGllbnQiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAidHlwZSIgOiAiRXhwcmVzc2lvblJlZiIKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB9IF0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfSwgewogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMTUxOjQ1LTE1MToxMDgiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAibmFtZSIgOiAiVG9RdWFudGl0eSIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJsaWJyYXJ5TmFtZSIgOiAiRkhJUkhlbHBlcnMiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAidHlwZSIgOiAiRnVuY3Rpb25SZWYiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAib3BlcmFuZCIgOiBbIHsKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjE1MTo2OC0xNTE6MTA3IiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInBhdGgiIDogImhpZ2giLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAidHlwZSIgOiAiUHJvcGVydHkiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAic291cmNlIiA6IHsKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjE1MTo2OC0xNTE6MTAyIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInN0cmljdCIgOiBmYWxzZSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInR5cGUiIDogIkFzIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIm9wZXJhbmQiIDogewogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMTUxOjY5LTE1MTo4NyIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJwYXRoIiA6ICJhYmF0ZW1lbnQiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAidHlwZSIgOiAiUHJvcGVydHkiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAic291cmNlIiA6IHsKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjE1MTo2OS0xNTE6NzciLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAibmFtZSIgOiAiY29uZGl0aW9uIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk9wZXJhbmRSZWYiCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgImFzVHlwZVNwZWNpZmllciIgOiB7CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIxNTE6OTItMTUxOjEwMSIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJ7aHR0cDovL2hsNy5vcmcvZmhpcn1SYW5nZSIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJOYW1lZFR5cGVTcGVjaWZpZXIiCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB9IF0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfSBdCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIH0sIHsKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjE1MToxMTItMTUxOjExNyIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJ2YWx1ZSIgOiAxLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAidW5pdCIgOiAieWVhciIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJRdWFudGl0eSIKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfSBdCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB9CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIH0sCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJoaWdoQ2xvc2VkRXhwcmVzc2lvbiIgOiB7CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJwYXRoIiA6ICJoaWdoQ2xvc2VkIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInR5cGUiIDogIlByb3BlcnR5IiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInNvdXJjZSIgOiB7CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIxNTA6NC0xNTE6MTE4IiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgImxvd0Nsb3NlZCIgOiB0cnVlLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAiaGlnaENsb3NlZCIgOiBmYWxzZSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInR5cGUiIDogIkludGVydmFsIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgImxvdyIgOiB7CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIxNTA6MTMtMTUwOjExNSIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJBZGQiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAib3BlcmFuZCIgOiBbIHsKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjE1MDoxMy0xNTA6NDkiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAibmFtZSIgOiAiVG9EYXRlIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgImxpYnJhcnlOYW1lIiA6ICJGSElSSGVscGVycyIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJGdW5jdGlvblJlZiIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJvcGVyYW5kIiA6IFsgewogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMTUwOjMyLTE1MDo0OCIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJwYXRoIiA6ICJiaXJ0aERhdGUiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAidHlwZSIgOiAiUHJvcGVydHkiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAic291cmNlIiA6IHsKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjE1MDozMi0xNTA6MzgiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAibmFtZSIgOiAiUGF0aWVudCIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJFeHByZXNzaW9uUmVmIgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB9CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIH0gXQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB9LCB7CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIxNTA6NTMtMTUwOjExNSIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJUb1F1YW50aXR5IiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgImxpYnJhcnlOYW1lIiA6ICJGSElSSGVscGVycyIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJGdW5jdGlvblJlZiIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJvcGVyYW5kIiA6IFsgewogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMTUwOjc2LTE1MDoxMTQiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAicGF0aCIgOiAibG93IiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInR5cGUiIDogIlByb3BlcnR5IiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInNvdXJjZSIgOiB7CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIxNTA6NzYtMTUwOjExMCIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJzdHJpY3QiIDogZmFsc2UsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJBcyIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJvcGVyYW5kIiA6IHsKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjE1MDo3Ny0xNTA6OTUiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAicGF0aCIgOiAiYWJhdGVtZW50IiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInR5cGUiIDogIlByb3BlcnR5IiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInNvdXJjZSIgOiB7CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIxNTA6NzctMTUwOjg1IiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIm5hbWUiIDogImNvbmRpdGlvbiIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJPcGVyYW5kUmVmIgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB9CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIH0sCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJhc1R5cGVTcGVjaWZpZXIiIDogewogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMTUwOjEwMC0xNTA6MTA5IiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIm5hbWUiIDogIntodHRwOi8vaGw3Lm9yZy9maGlyfVJhbmdlIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk5hbWVkVHlwZVNwZWNpZmllciIKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB9CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIH0gXQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB9IF0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgImhpZ2giIDogewogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMTUxOjUtMTUxOjExNyIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJBZGQiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAib3BlcmFuZCIgOiBbIHsKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjE1MTo1LTE1MToxMDgiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAidHlwZSIgOiAiQWRkIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIm9wZXJhbmQiIDogWyB7CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIxNTE6NS0xNTE6NDEiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAibmFtZSIgOiAiVG9EYXRlIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgImxpYnJhcnlOYW1lIiA6ICJGSElSSGVscGVycyIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJGdW5jdGlvblJlZiIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJvcGVyYW5kIiA6IFsgewogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMTUxOjI0LTE1MTo0MCIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJwYXRoIiA6ICJiaXJ0aERhdGUiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAidHlwZSIgOiAiUHJvcGVydHkiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAic291cmNlIiA6IHsKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjE1MToyNC0xNTE6MzAiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAibmFtZSIgOiAiUGF0aWVudCIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJFeHByZXNzaW9uUmVmIgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB9CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIH0gXQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB9LCB7CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIxNTE6NDUtMTUxOjEwOCIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJUb1F1YW50aXR5IiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgImxpYnJhcnlOYW1lIiA6ICJGSElSSGVscGVycyIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJGdW5jdGlvblJlZiIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJvcGVyYW5kIiA6IFsgewogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMTUxOjY4LTE1MToxMDciLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAicGF0aCIgOiAiaGlnaCIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJQcm9wZXJ0eSIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJzb3VyY2UiIDogewogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMTUxOjY4LTE1MToxMDIiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAic3RyaWN0IiA6IGZhbHNlLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAidHlwZSIgOiAiQXMiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAib3BlcmFuZCIgOiB7CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIxNTE6NjktMTUxOjg3IiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInBhdGgiIDogImFiYXRlbWVudCIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJQcm9wZXJ0eSIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJzb3VyY2UiIDogewogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMTUxOjY5LTE1MTo3NyIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJjb25kaXRpb24iLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAidHlwZSIgOiAiT3BlcmFuZFJlZiIKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB9LAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAiYXNUeXBlU3BlY2lmaWVyIiA6IHsKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjE1MTo5Mi0xNTE6MTAxIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIm5hbWUiIDogIntodHRwOi8vaGw3Lm9yZy9maGlyfVJhbmdlIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk5hbWVkVHlwZVNwZWNpZmllciIKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB9CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIH0gXQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB9IF0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfSwgewogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMTUxOjExMi0xNTE6MTE3IiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInZhbHVlIiA6IDEsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJ1bml0IiA6ICJ5ZWFyIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInR5cGUiIDogIlF1YW50aXR5IgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB9IF0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB9CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgImVsc2UiIDogewogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMTUyOjctMTU0OjEwIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInR5cGUiIDogIklmIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgImNvbmRpdGlvbiIgOiB7CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIxNTI6MTAtMTUyOjQ0IiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInR5cGUiIDogIklzIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIm9wZXJhbmQiIDogewogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMTUyOjEwLTE1MjoyOCIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJwYXRoIiA6ICJhYmF0ZW1lbnQiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAidHlwZSIgOiAiUHJvcGVydHkiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAic291cmNlIiA6IHsKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjE1MjoxMC0xNTI6MTgiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAibmFtZSIgOiAiY29uZGl0aW9uIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk9wZXJhbmRSZWYiCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgImlzVHlwZVNwZWNpZmllciIgOiB7CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIxNTI6MzMtMTUyOjQ0IiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIm5hbWUiIDogIntodHRwOi8vaGw3Lm9yZy9maGlyfWJvb2xlYW4iLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAidHlwZSIgOiAiTmFtZWRUeXBlU3BlY2lmaWVyIgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB9CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIH0sCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJ0aGVuIiA6IHsKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjE1Mzo0LTE1Mzo3MSIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJsb3dDbG9zZWQiIDogdHJ1ZSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgImhpZ2hDbG9zZWQiIDogZmFsc2UsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJJbnRlcnZhbCIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJsb3ciIDogewogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMTUzOjEzLTE1Mzo0NiIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJFbmQiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAib3BlcmFuZCIgOiB7CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIxNTM6MjAtMTUzOjQ2IiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIm5hbWUiIDogIlRvSW50ZXJ2YWwiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAidHlwZSIgOiAiRnVuY3Rpb25SZWYiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAib3BlcmFuZCIgOiBbIHsKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjE1MzozMS0xNTM6NDUiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAicGF0aCIgOiAib25zZXQiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAidHlwZSIgOiAiUHJvcGVydHkiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAic291cmNlIiA6IHsKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjE1MzozMS0xNTM6MzkiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAibmFtZSIgOiAiY29uZGl0aW9uIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk9wZXJhbmRSZWYiCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfSBdCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgImhpZ2giIDogewogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAibmFtZSIgOiAiVG9EYXRlVGltZSIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJsaWJyYXJ5TmFtZSIgOiAiRkhJUkhlbHBlcnMiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAidHlwZSIgOiAiRnVuY3Rpb25SZWYiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAib3BlcmFuZCIgOiBbIHsKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjE1Mzo0OS0xNTM6NzAiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAicGF0aCIgOiAicmVjb3JkZWREYXRlIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInR5cGUiIDogIlByb3BlcnR5IiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInNvdXJjZSIgOiB7CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIxNTM6NDktMTUzOjU3IiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIm5hbWUiIDogImNvbmRpdGlvbiIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJPcGVyYW5kUmVmIgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB9CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIH0gXQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB9CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIH0sCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJlbHNlIiA6IHsKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInR5cGUiIDogIkFzIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIm9wZXJhbmQiIDogewogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMTU0OjctMTU0OjEwIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk51bGwiCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIH0sCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJhc1R5cGVTcGVjaWZpZXIiIDogewogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAidHlwZSIgOiAiSW50ZXJ2YWxUeXBlU3BlY2lmaWVyIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInBvaW50VHlwZSIgOiB7CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJ7dXJuOmhsNy1vcmc6ZWxtLXR5cGVzOnIxfURhdGVUaW1lIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk5hbWVkVHlwZVNwZWNpZmllciIKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB9CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfQogICAgICAgICAgICAgICAgICAgICAgICAgICB9CiAgICAgICAgICAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgICAgICAgICAgfQogICAgICAgICAgICAgICAgICB9CiAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgfSwKICAgICAgICAgICAgIm9wZXJhbmQiIDogWyB7CiAgICAgICAgICAgICAgICJuYW1lIiA6ICJjb25kaXRpb24iLAogICAgICAgICAgICAgICAib3BlcmFuZFR5cGVTcGVjaWZpZXIiIDogewogICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMTM1OjQ3LTEzNTo1NSIsCiAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJ7aHR0cDovL2hsNy5vcmcvZmhpcn1Db25kaXRpb24iLAogICAgICAgICAgICAgICAgICAidHlwZSIgOiAiTmFtZWRUeXBlU3BlY2lmaWVyIgogICAgICAgICAgICAgICB9CiAgICAgICAgICAgIH0gXQogICAgICAgICB9LCB7CiAgICAgICAgICAgICJsb2NhdG9yIiA6ICIxNjE6MS0xNjI6ODciLAogICAgICAgICAgICAibmFtZSIgOiAiVG9QcmV2YWxlbmNlSW50ZXJ2YWwiLAogICAgICAgICAgICAiY29udGV4dCIgOiAiUGF0aWVudCIsCiAgICAgICAgICAgICJhY2Nlc3NMZXZlbCIgOiAiUHVibGljIiwKICAgICAgICAgICAgInR5cGUiIDogIkZ1bmN0aW9uRGVmIiwKICAgICAgICAgICAgImFubm90YXRpb24iIDogWyB7CiAgICAgICAgICAgICAgICJ0eXBlIiA6ICJBbm5vdGF0aW9uIiwKICAgICAgICAgICAgICAgInQiIDogWyB7CiAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJkZXNjcmlwdGlvbiIsCiAgICAgICAgICAgICAgICAgICJ2YWx1ZSIgOiAiUmV0dXJucyBhbiBpbnRlcnZhbCByZXByZXNlbnRpbmcgdGhlIG5vcm1hbGl6ZWQgcHJldmFsZW5jZSBwZXJpb2Qgb2YgYSBnaXZlbiBDb25kaXRpb24gcmVzb3VyY2UuIgogICAgICAgICAgICAgICB9LCB7CiAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJjb21tZW50IiwKICAgICAgICAgICAgICAgICAgInZhbHVlIiA6ICJVc2VzIHRoZSBUb0ludGVydmFsIGFuZCBUb0FiYXRlbWVudEludGVydmFsIGZ1bmN0aW9ucyB0byBkZXRlcm1pbmUgdGhlIHdpZGVzdCBwb3RlbnRpYWwgaW50ZXJ2YWwgZnJvbVxub25zZXQgdG8gYWJhdGVtZW50IGFzIHNwZWNpZmllZCBpbiB0aGUgZ2l2ZW4gQ29uZGl0aW9uLiIKICAgICAgICAgICAgICAgfSBdCiAgICAgICAgICAgIH0gXSwKICAgICAgICAgICAgImV4cHJlc3Npb24iIDogewogICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMTYyOjMtMTYyOjg3IiwKICAgICAgICAgICAgICAgImxvd0Nsb3NlZCIgOiB0cnVlLAogICAgICAgICAgICAgICAiaGlnaENsb3NlZCIgOiBmYWxzZSwKICAgICAgICAgICAgICAgInR5cGUiIDogIkludGVydmFsIiwKICAgICAgICAgICAgICAgImxvdyIgOiB7CiAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIxNjI6MTItMTYyOjQ3IiwKICAgICAgICAgICAgICAgICAgInR5cGUiIDogIlN0YXJ0IiwKICAgICAgICAgICAgICAgICAgIm9wZXJhbmQiIDogewogICAgICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMTYyOjIxLTE2Mjo0NyIsCiAgICAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJUb0ludGVydmFsIiwKICAgICAgICAgICAgICAgICAgICAgInR5cGUiIDogIkZ1bmN0aW9uUmVmIiwKICAgICAgICAgICAgICAgICAgICAgIm9wZXJhbmQiIDogWyB7CiAgICAgICAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIxNjI6MzItMTYyOjQ2IiwKICAgICAgICAgICAgICAgICAgICAgICAgInBhdGgiIDogIm9uc2V0IiwKICAgICAgICAgICAgICAgICAgICAgICAgInR5cGUiIDogIlByb3BlcnR5IiwKICAgICAgICAgICAgICAgICAgICAgICAgInNvdXJjZSIgOiB7CiAgICAgICAgICAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIxNjI6MzItMTYyOjQwIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgIm5hbWUiIDogImNvbmRpdGlvbiIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJPcGVyYW5kUmVmIgogICAgICAgICAgICAgICAgICAgICAgICB9CiAgICAgICAgICAgICAgICAgICAgIH0gXQogICAgICAgICAgICAgICAgICB9CiAgICAgICAgICAgICAgIH0sCiAgICAgICAgICAgICAgICJoaWdoIiA6IHsKICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjE2Mjo1MC0xNjI6ODYiLAogICAgICAgICAgICAgICAgICAidHlwZSIgOiAiRW5kIiwKICAgICAgICAgICAgICAgICAgIm9wZXJhbmQiIDogewogICAgICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMTYyOjU3LTE2Mjo4NiIsCiAgICAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJUb0FiYXRlbWVudEludGVydmFsIiwKICAgICAgICAgICAgICAgICAgICAgInR5cGUiIDogIkZ1bmN0aW9uUmVmIiwKICAgICAgICAgICAgICAgICAgICAgIm9wZXJhbmQiIDogWyB7CiAgICAgICAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIxNjI6NzctMTYyOjg1IiwKICAgICAgICAgICAgICAgICAgICAgICAgIm5hbWUiIDogImNvbmRpdGlvbiIsCiAgICAgICAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJPcGVyYW5kUmVmIgogICAgICAgICAgICAgICAgICAgICB9IF0KICAgICAgICAgICAgICAgICAgfQogICAgICAgICAgICAgICB9CiAgICAgICAgICAgIH0sCiAgICAgICAgICAgICJvcGVyYW5kIiA6IFsgewogICAgICAgICAgICAgICAibmFtZSIgOiAiY29uZGl0aW9uIiwKICAgICAgICAgICAgICAgIm9wZXJhbmRUeXBlU3BlY2lmaWVyIiA6IHsKICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjE2MTo0OC0xNjE6NTYiLAogICAgICAgICAgICAgICAgICAibmFtZSIgOiAie2h0dHA6Ly9obDcub3JnL2ZoaXJ9Q29uZGl0aW9uIiwKICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk5hbWVkVHlwZVNwZWNpZmllciIKICAgICAgICAgICAgICAgfQogICAgICAgICAgICB9IF0KICAgICAgICAgfSwgewogICAgICAgICAgICAibG9jYXRvciIgOiAiMTY4OjEtMTY5OjEwOSIsCiAgICAgICAgICAgICJuYW1lIiA6ICJUb1ByZXZhbGVuY2VJbnRlcnZhbCIsCiAgICAgICAgICAgICJjb250ZXh0IiA6ICJQYXRpZW50IiwKICAgICAgICAgICAgImFjY2Vzc0xldmVsIiA6ICJQdWJsaWMiLAogICAgICAgICAgICAidHlwZSIgOiAiRnVuY3Rpb25EZWYiLAogICAgICAgICAgICAiYW5ub3RhdGlvbiIgOiBbIHsKICAgICAgICAgICAgICAgInR5cGUiIDogIkFubm90YXRpb24iLAogICAgICAgICAgICAgICAidCIgOiBbIHsKICAgICAgICAgICAgICAgICAgIm5hbWUiIDogImRlc2NyaXB0aW9uIiwKICAgICAgICAgICAgICAgICAgInZhbHVlIiA6ICJSZXR1cm5zIGFuIGludGVydmFsIHJlcHJlc2VudGluZyB0aGUgbm9ybWFsaXplZCBwcmV2YWxlbmNlIHBlcmlvZCBvZiBhIGdpdmVuIEFsbGVyZ3kgSW50b2xlcmFuY2UgcmVzb3VyY2UuIgogICAgICAgICAgICAgICB9LCB7CiAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJjb21tZW50IiwKICAgICAgICAgICAgICAgICAgInZhbHVlIiA6ICJVc2VzIHRoZSBUb0ludGVydmFsIGZ1bmN0aW9uIGFuZCBsYXN0T2NjdXJlbmNlIHRvIGRldGVybWluZSB0aGUgd2lkZXN0IHBvdGVudGlhbCBpbnRlcnZhbC4iCiAgICAgICAgICAgICAgIH0gXQogICAgICAgICAgICB9IF0sCiAgICAgICAgICAgICJleHByZXNzaW9uIiA6IHsKICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjE2OTozLTE2OToxMDkiLAogICAgICAgICAgICAgICAibG93Q2xvc2VkIiA6IHRydWUsCiAgICAgICAgICAgICAgICJoaWdoQ2xvc2VkIiA6IHRydWUsCiAgICAgICAgICAgICAgICJ0eXBlIiA6ICJJbnRlcnZhbCIsCiAgICAgICAgICAgICAgICJsb3ciIDogewogICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMTY5OjEyLTE2OTo1NiIsCiAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJTdGFydCIsCiAgICAgICAgICAgICAgICAgICJvcGVyYW5kIiA6IHsKICAgICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjE2OToyMS0xNjk6NTYiLAogICAgICAgICAgICAgICAgICAgICAibmFtZSIgOiAiVG9JbnRlcnZhbCIsCiAgICAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJGdW5jdGlvblJlZiIsCiAgICAgICAgICAgICAgICAgICAgICJvcGVyYW5kIiA6IFsgewogICAgICAgICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMTY5OjMyLTE2OTo1NSIsCiAgICAgICAgICAgICAgICAgICAgICAgICJwYXRoIiA6ICJvbnNldCIsCiAgICAgICAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJQcm9wZXJ0eSIsCiAgICAgICAgICAgICAgICAgICAgICAgICJzb3VyY2UiIDogewogICAgICAgICAgICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMTY5OjMyLTE2OTo0OSIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJhbGxlcmd5SW50b2xlcmFuY2UiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAidHlwZSIgOiAiT3BlcmFuZFJlZiIKICAgICAgICAgICAgICAgICAgICAgICAgfQogICAgICAgICAgICAgICAgICAgICB9IF0KICAgICAgICAgICAgICAgICAgfQogICAgICAgICAgICAgICB9LAogICAgICAgICAgICAgICAiaGlnaCIgOiB7CiAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIxNjk6NTktMTY5OjEwOCIsCiAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJDb2FsZXNjZSIsCiAgICAgICAgICAgICAgICAgICJvcGVyYW5kIiA6IFsgewogICAgICAgICAgICAgICAgICAgICAibmFtZSIgOiAiVG9EYXRlVGltZSIsCiAgICAgICAgICAgICAgICAgICAgICJsaWJyYXJ5TmFtZSIgOiAiRkhJUkhlbHBlcnMiLAogICAgICAgICAgICAgICAgICAgICAidHlwZSIgOiAiRnVuY3Rpb25SZWYiLAogICAgICAgICAgICAgICAgICAgICAib3BlcmFuZCIgOiBbIHsKICAgICAgICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjE2OTo2OC0xNjk6MTAwIiwKICAgICAgICAgICAgICAgICAgICAgICAgInBhdGgiIDogImxhc3RPY2N1cnJlbmNlIiwKICAgICAgICAgICAgICAgICAgICAgICAgInR5cGUiIDogIlByb3BlcnR5IiwKICAgICAgICAgICAgICAgICAgICAgICAgInNvdXJjZSIgOiB7CiAgICAgICAgICAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIxNjk6NjgtMTY5Ojg1IiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgIm5hbWUiIDogImFsbGVyZ3lJbnRvbGVyYW5jZSIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJPcGVyYW5kUmVmIgogICAgICAgICAgICAgICAgICAgICAgICB9CiAgICAgICAgICAgICAgICAgICAgIH0gXQogICAgICAgICAgICAgICAgICB9LCB7CiAgICAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIxNjk6MTAzLTE2OToxMDciLAogICAgICAgICAgICAgICAgICAgICAidHlwZSIgOiAiTm93IgogICAgICAgICAgICAgICAgICB9IF0KICAgICAgICAgICAgICAgfQogICAgICAgICAgICB9LAogICAgICAgICAgICAib3BlcmFuZCIgOiBbIHsKICAgICAgICAgICAgICAgIm5hbWUiIDogImFsbGVyZ3lJbnRvbGVyYW5jZSIsCiAgICAgICAgICAgICAgICJvcGVyYW5kVHlwZVNwZWNpZmllciIgOiB7CiAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIxNjg6NTctMTY4Ojc0IiwKICAgICAgICAgICAgICAgICAgIm5hbWUiIDogIntodHRwOi8vaGw3Lm9yZy9maGlyfUFsbGVyZ3lJbnRvbGVyYW5jZSIsCiAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJOYW1lZFR5cGVTcGVjaWZpZXIiCiAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgfSBdCiAgICAgICAgIH0sIHsKICAgICAgICAgICAgImxvY2F0b3IiIDogIjE3ODoxLTE4MToxMCIsCiAgICAgICAgICAgICJuYW1lIiA6ICJFeHRlbnNpb25zIiwKICAgICAgICAgICAgImNvbnRleHQiIDogIlBhdGllbnQiLAogICAgICAgICAgICAiYWNjZXNzTGV2ZWwiIDogIlB1YmxpYyIsCiAgICAgICAgICAgICJ0eXBlIiA6ICJGdW5jdGlvbkRlZiIsCiAgICAgICAgICAgICJhbm5vdGF0aW9uIiA6IFsgewogICAgICAgICAgICAgICAidHlwZSIgOiAiQW5ub3RhdGlvbiIsCiAgICAgICAgICAgICAgICJ0IiA6IFsgewogICAgICAgICAgICAgICAgICAibmFtZSIgOiAiZGVzY3JpcHRpb24iLAogICAgICAgICAgICAgICAgICAidmFsdWUiIDogIlJldHVybnMgYW55IGV4dGVuc2lvbnMgZGVmaW5lZCBvbiB0aGUgZ2l2ZW4gcmVzb3VyY2Ugd2l0aCB0aGUgc3BlY2lmaWVkIHVybC4iCiAgICAgICAgICAgICAgIH0sIHsKICAgICAgICAgICAgICAgICAgIm5hbWUiIDogImNvbW1lbnQiLAogICAgICAgICAgICAgICAgICAidmFsdWUiIDogIk5PVEU6IEV4dGVuc2lvbnMgYXJlIG5vdCB0aGUgcHJlZmVycmVkIGFwcHJvYWNoLCBidXQgYXJlIHVzZWQgYXMgYSB3YXkgdG8gYWNjZXNzXG5jb250ZW50IHRoYXQgaXMgZGVmaW5lZCBieSBleHRlbnNpb25zIGJ1dCBub3QgeWV0IHN1cmZhY2VkIGluIHRoZVxuQ1FMIG1vZGVsIGluZm8uIgogICAgICAgICAgICAgICB9IF0KICAgICAgICAgICAgfSBdLAogICAgICAgICAgICAiZXhwcmVzc2lvbiIgOiB7CiAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIxNzk6My0xODE6MTAiLAogICAgICAgICAgICAgICAidHlwZSIgOiAiUXVlcnkiLAogICAgICAgICAgICAgICAic291cmNlIiA6IFsgewogICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMTc5OjMtMTc5OjI4IiwKICAgICAgICAgICAgICAgICAgImFsaWFzIiA6ICJFIiwKICAgICAgICAgICAgICAgICAgImV4cHJlc3Npb24iIDogewogICAgICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMTc5OjMtMTc5OjI2IiwKICAgICAgICAgICAgICAgICAgICAgInBhdGgiIDogImV4dGVuc2lvbiIsCiAgICAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJQcm9wZXJ0eSIsCiAgICAgICAgICAgICAgICAgICAgICJzb3VyY2UiIDogewogICAgICAgICAgICAgICAgICAgICAgICAibmFtZSIgOiAiZG9tYWluUmVzb3VyY2UiLAogICAgICAgICAgICAgICAgICAgICAgICAidHlwZSIgOiAiT3BlcmFuZFJlZiIKICAgICAgICAgICAgICAgICAgICAgfQogICAgICAgICAgICAgICAgICB9CiAgICAgICAgICAgICAgIH0gXSwKICAgICAgICAgICAgICAgInJlbGF0aW9uc2hpcCIgOiBbIF0sCiAgICAgICAgICAgICAgICJ3aGVyZSIgOiB7CiAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIxODA6NC0xODA6MjAiLAogICAgICAgICAgICAgICAgICAidHlwZSIgOiAiRXF1YWwiLAogICAgICAgICAgICAgICAgICAib3BlcmFuZCIgOiBbIHsKICAgICAgICAgICAgICAgICAgICAgIm5hbWUiIDogIlRvU3RyaW5nIiwKICAgICAgICAgICAgICAgICAgICAgImxpYnJhcnlOYW1lIiA6ICJGSElSSGVscGVycyIsCiAgICAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJGdW5jdGlvblJlZiIsCiAgICAgICAgICAgICAgICAgICAgICJvcGVyYW5kIiA6IFsgewogICAgICAgICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMTgwOjEwLTE4MDoxNCIsCiAgICAgICAgICAgICAgICAgICAgICAgICJwYXRoIiA6ICJ1cmwiLAogICAgICAgICAgICAgICAgICAgICAgICAic2NvcGUiIDogIkUiLAogICAgICAgICAgICAgICAgICAgICAgICAidHlwZSIgOiAiUHJvcGVydHkiCiAgICAgICAgICAgICAgICAgICAgIH0gXQogICAgICAgICAgICAgICAgICB9LCB7CiAgICAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIxODA6MTgtMTgwOjIwIiwKICAgICAgICAgICAgICAgICAgICAgIm5hbWUiIDogInVybCIsCiAgICAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJPcGVyYW5kUmVmIgogICAgICAgICAgICAgICAgICB9IF0KICAgICAgICAgICAgICAgfSwKICAgICAgICAgICAgICAgInJldHVybiIgOiB7CiAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIxODE6My0xODE6MTAiLAogICAgICAgICAgICAgICAgICAiZXhwcmVzc2lvbiIgOiB7CiAgICAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIxODE6MTAiLAogICAgICAgICAgICAgICAgICAgICAibmFtZSIgOiAiRSIsCiAgICAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJBbGlhc1JlZiIKICAgICAgICAgICAgICAgICAgfQogICAgICAgICAgICAgICB9CiAgICAgICAgICAgIH0sCiAgICAgICAgICAgICJvcGVyYW5kIiA6IFsgewogICAgICAgICAgICAgICAibmFtZSIgOiAiZG9tYWluUmVzb3VyY2UiLAogICAgICAgICAgICAgICAib3BlcmFuZFR5cGVTcGVjaWZpZXIiIDogewogICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMTc4OjQzLTE3ODo1NiIsCiAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJ7aHR0cDovL2hsNy5vcmcvZmhpcn1Eb21haW5SZXNvdXJjZSIsCiAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJOYW1lZFR5cGVTcGVjaWZpZXIiCiAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgfSwgewogICAgICAgICAgICAgICAibmFtZSIgOiAidXJsIiwKICAgICAgICAgICAgICAgIm9wZXJhbmRUeXBlU3BlY2lmaWVyIiA6IHsKICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjE3ODo2My0xNzg6NjgiLAogICAgICAgICAgICAgICAgICAibmFtZSIgOiAie3VybjpobDctb3JnOmVsbS10eXBlczpyMX1TdHJpbmciLAogICAgICAgICAgICAgICAgICAidHlwZSIgOiAiTmFtZWRUeXBlU3BlY2lmaWVyIgogICAgICAgICAgICAgICB9CiAgICAgICAgICAgIH0gXQogICAgICAgICB9LCB7CiAgICAgICAgICAgICJsb2NhdG9yIiA6ICIxODg6MS0xODk6NTAiLAogICAgICAgICAgICAibmFtZSIgOiAiRXh0ZW5zaW9uIiwKICAgICAgICAgICAgImNvbnRleHQiIDogIlBhdGllbnQiLAogICAgICAgICAgICAiYWNjZXNzTGV2ZWwiIDogIlB1YmxpYyIsCiAgICAgICAgICAgICJ0eXBlIiA6ICJGdW5jdGlvbkRlZiIsCiAgICAgICAgICAgICJhbm5vdGF0aW9uIiA6IFsgewogICAgICAgICAgICAgICAidHlwZSIgOiAiQW5ub3RhdGlvbiIsCiAgICAgICAgICAgICAgICJ0IiA6IFsgewogICAgICAgICAgICAgICAgICAibmFtZSIgOiAiZGVzY3JpcHRpb24iLAogICAgICAgICAgICAgICAgICAidmFsdWUiIDogIlJldHVybnMgdGhlIHNpbmdsZSBleHRlbnNpb24gKGlmIHByZXNlbnQpIG9uIHRoZSBnaXZlbiByZXNvdXJjZSB3aXRoIHRoZSBzcGVjaWZpZWQgdXJsLiIKICAgICAgICAgICAgICAgfSwgewogICAgICAgICAgICAgICAgICAibmFtZSIgOiAiY29tbWVudCIsCiAgICAgICAgICAgICAgICAgICJ2YWx1ZSIgOiAiVGhpcyBmdW5jdGlvbiB1c2VzIHNpbmdsZXRvbiBmcm9tIHRvIGVuc3VyZSB0aGF0IGEgcnVuLXRpbWUgZXhjZXB0aW9uIGlzIHRocm93biBpZiB0aGVyZVxuaXMgbW9yZSB0aGFuIG9uZSBleHRlbnNpb24gb24gdGhlIGdpdmVuIHJlc291cmNlIHdpdGggdGhlIHNwZWNpZmllZCB1cmwuIgogICAgICAgICAgICAgICB9IF0KICAgICAgICAgICAgfSBdLAogICAgICAgICAgICAiZXhwcmVzc2lvbiIgOiB7CiAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIxODk6My0xODk6NTAiLAogICAgICAgICAgICAgICAidHlwZSIgOiAiU2luZ2xldG9uRnJvbSIsCiAgICAgICAgICAgICAgICJvcGVyYW5kIiA6IHsKICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjE4OToxOC0xODk6NTAiLAogICAgICAgICAgICAgICAgICAibmFtZSIgOiAiRXh0ZW5zaW9ucyIsCiAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJGdW5jdGlvblJlZiIsCiAgICAgICAgICAgICAgICAgICJvcGVyYW5kIiA6IFsgewogICAgICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMTg5OjMxLTE4OTo0NCIsCiAgICAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJkb21haW5SZXNvdXJjZSIsCiAgICAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJPcGVyYW5kUmVmIgogICAgICAgICAgICAgICAgICB9LCB7CiAgICAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIxODk6NDctMTg5OjQ5IiwKICAgICAgICAgICAgICAgICAgICAgIm5hbWUiIDogInVybCIsCiAgICAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJPcGVyYW5kUmVmIgogICAgICAgICAgICAgICAgICB9IF0KICAgICAgICAgICAgICAgfQogICAgICAgICAgICB9LAogICAgICAgICAgICAib3BlcmFuZCIgOiBbIHsKICAgICAgICAgICAgICAgIm5hbWUiIDogImRvbWFpblJlc291cmNlIiwKICAgICAgICAgICAgICAgIm9wZXJhbmRUeXBlU3BlY2lmaWVyIiA6IHsKICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjE4ODo0Mi0xODg6NTUiLAogICAgICAgICAgICAgICAgICAibmFtZSIgOiAie2h0dHA6Ly9obDcub3JnL2ZoaXJ9RG9tYWluUmVzb3VyY2UiLAogICAgICAgICAgICAgICAgICAidHlwZSIgOiAiTmFtZWRUeXBlU3BlY2lmaWVyIgogICAgICAgICAgICAgICB9CiAgICAgICAgICAgIH0sIHsKICAgICAgICAgICAgICAgIm5hbWUiIDogInVybCIsCiAgICAgICAgICAgICAgICJvcGVyYW5kVHlwZVNwZWNpZmllciIgOiB7CiAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIxODg6NjItMTg4OjY3IiwKICAgICAgICAgICAgICAgICAgIm5hbWUiIDogInt1cm46aGw3LW9yZzplbG0tdHlwZXM6cjF9U3RyaW5nIiwKICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk5hbWVkVHlwZVNwZWNpZmllciIKICAgICAgICAgICAgICAgfQogICAgICAgICAgICB9IF0KICAgICAgICAgfSwgewogICAgICAgICAgICAibG9jYXRvciIgOiAiMTk2OjEtMTk5OjEwIiwKICAgICAgICAgICAgIm5hbWUiIDogIkV4dGVuc2lvbnMiLAogICAgICAgICAgICAiY29udGV4dCIgOiAiUGF0aWVudCIsCiAgICAgICAgICAgICJhY2Nlc3NMZXZlbCIgOiAiUHVibGljIiwKICAgICAgICAgICAgInR5cGUiIDogIkZ1bmN0aW9uRGVmIiwKICAgICAgICAgICAgImFubm90YXRpb24iIDogWyB7CiAgICAgICAgICAgICAgICJ0eXBlIiA6ICJBbm5vdGF0aW9uIiwKICAgICAgICAgICAgICAgInQiIDogWyB7CiAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJkZXNjcmlwdGlvbiIsCiAgICAgICAgICAgICAgICAgICJ2YWx1ZSIgOiAiUmV0dXJucyBhbnkgZXh0ZW5zaW9ucyBkZWZpbmVkIG9uIHRoZSBnaXZlbiBlbGVtZW50IHdpdGggdGhlIHNwZWNpZmllZCB1cmwuIgogICAgICAgICAgICAgICB9LCB7CiAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJjb21tZW50IiwKICAgICAgICAgICAgICAgICAgInZhbHVlIiA6ICJOT1RFOiBFeHRlbnNpb25zIGFyZSBub3QgdGhlIHByZWZlcnJlZCBhcHByb2FjaCwgYnV0IGFyZSB1c2VkIGFzIGEgd2F5IHRvIGFjY2Vzc1xuY29udGVudCB0aGF0IGlzIGRlZmluZWQgYnkgZXh0ZW5zaW9ucyBidXQgbm90IHlldCBzdXJmYWNlZCBpbiB0aGUgQ1FMIG1vZGVsIGluZm8uIgogICAgICAgICAgICAgICB9IF0KICAgICAgICAgICAgfSBdLAogICAgICAgICAgICAiZXhwcmVzc2lvbiIgOiB7CiAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIxOTc6My0xOTk6MTAiLAogICAgICAgICAgICAgICAidHlwZSIgOiAiUXVlcnkiLAogICAgICAgICAgICAgICAic291cmNlIiA6IFsgewogICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMTk3OjMtMTk3OjIxIiwKICAgICAgICAgICAgICAgICAgImFsaWFzIiA6ICJFIiwKICAgICAgICAgICAgICAgICAgImV4cHJlc3Npb24iIDogewogICAgICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMTk3OjMtMTk3OjE5IiwKICAgICAgICAgICAgICAgICAgICAgInBhdGgiIDogImV4dGVuc2lvbiIsCiAgICAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJQcm9wZXJ0eSIsCiAgICAgICAgICAgICAgICAgICAgICJzb3VyY2UiIDogewogICAgICAgICAgICAgICAgICAgICAgICAibmFtZSIgOiAiZWxlbWVudCIsCiAgICAgICAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJPcGVyYW5kUmVmIgogICAgICAgICAgICAgICAgICAgICB9CiAgICAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgICAgfSBdLAogICAgICAgICAgICAgICAicmVsYXRpb25zaGlwIiA6IFsgXSwKICAgICAgICAgICAgICAgIndoZXJlIiA6IHsKICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjE5ODo0LTE5ODoyMCIsCiAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJFcXVhbCIsCiAgICAgICAgICAgICAgICAgICJvcGVyYW5kIiA6IFsgewogICAgICAgICAgICAgICAgICAgICAibmFtZSIgOiAiVG9TdHJpbmciLAogICAgICAgICAgICAgICAgICAgICAibGlicmFyeU5hbWUiIDogIkZISVJIZWxwZXJzIiwKICAgICAgICAgICAgICAgICAgICAgInR5cGUiIDogIkZ1bmN0aW9uUmVmIiwKICAgICAgICAgICAgICAgICAgICAgIm9wZXJhbmQiIDogWyB7CiAgICAgICAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIxOTg6MTAtMTk4OjE0IiwKICAgICAgICAgICAgICAgICAgICAgICAgInBhdGgiIDogInVybCIsCiAgICAgICAgICAgICAgICAgICAgICAgICJzY29wZSIgOiAiRSIsCiAgICAgICAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJQcm9wZXJ0eSIKICAgICAgICAgICAgICAgICAgICAgfSBdCiAgICAgICAgICAgICAgICAgIH0sIHsKICAgICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjE5ODoxOC0xOTg6MjAiLAogICAgICAgICAgICAgICAgICAgICAibmFtZSIgOiAidXJsIiwKICAgICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk9wZXJhbmRSZWYiCiAgICAgICAgICAgICAgICAgIH0gXQogICAgICAgICAgICAgICB9LAogICAgICAgICAgICAgICAicmV0dXJuIiA6IHsKICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjE5OTozLTE5OToxMCIsCiAgICAgICAgICAgICAgICAgICJleHByZXNzaW9uIiA6IHsKICAgICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjE5OToxMCIsCiAgICAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJFIiwKICAgICAgICAgICAgICAgICAgICAgInR5cGUiIDogIkFsaWFzUmVmIgogICAgICAgICAgICAgICAgICB9CiAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgfSwKICAgICAgICAgICAgIm9wZXJhbmQiIDogWyB7CiAgICAgICAgICAgICAgICJuYW1lIiA6ICJlbGVtZW50IiwKICAgICAgICAgICAgICAgIm9wZXJhbmRUeXBlU3BlY2lmaWVyIiA6IHsKICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjE5NjozNi0xOTY6NDIiLAogICAgICAgICAgICAgICAgICAibmFtZSIgOiAie2h0dHA6Ly9obDcub3JnL2ZoaXJ9RWxlbWVudCIsCiAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJOYW1lZFR5cGVTcGVjaWZpZXIiCiAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgfSwgewogICAgICAgICAgICAgICAibmFtZSIgOiAidXJsIiwKICAgICAgICAgICAgICAgIm9wZXJhbmRUeXBlU3BlY2lmaWVyIiA6IHsKICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjE5Njo0OS0xOTY6NTQiLAogICAgICAgICAgICAgICAgICAibmFtZSIgOiAie3VybjpobDctb3JnOmVsbS10eXBlczpyMX1TdHJpbmciLAogICAgICAgICAgICAgICAgICAidHlwZSIgOiAiTmFtZWRUeXBlU3BlY2lmaWVyIgogICAgICAgICAgICAgICB9CiAgICAgICAgICAgIH0gXQogICAgICAgICB9LCB7CiAgICAgICAgICAgICJsb2NhdG9yIiA6ICIyMDY6MS0yMDc6NDEiLAogICAgICAgICAgICAibmFtZSIgOiAiRXh0ZW5zaW9uIiwKICAgICAgICAgICAgImNvbnRleHQiIDogIlBhdGllbnQiLAogICAgICAgICAgICAiYWNjZXNzTGV2ZWwiIDogIlB1YmxpYyIsCiAgICAgICAgICAgICJ0eXBlIiA6ICJGdW5jdGlvbkRlZiIsCiAgICAgICAgICAgICJhbm5vdGF0aW9uIiA6IFsgewogICAgICAgICAgICAgICAidHlwZSIgOiAiQW5ub3RhdGlvbiIsCiAgICAgICAgICAgICAgICJ0IiA6IFsgewogICAgICAgICAgICAgICAgICAibmFtZSIgOiAiZGVzY3JpcHRpb24iLAogICAgICAgICAgICAgICAgICAidmFsdWUiIDogIlJldHVybnMgdGhlIHNpbmdsZSBleHRlbnNpb24gKGlmIHByZXNlbnQpIG9uIHRoZSBnaXZlbiBlbGVtZW50IHdpdGggdGhlIHNwZWNpZmllZCB1cmwuIgogICAgICAgICAgICAgICB9LCB7CiAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJjb21tZW50IiwKICAgICAgICAgICAgICAgICAgInZhbHVlIiA6ICJUaGlzIGZ1bmN0aW9uIHVzZXMgc2luZ2xldG9uIGZyb20gdG8gZW5zdXJlIHRoYXQgYSBydW4tdGltZSBleGNlcHRpb24gaXMgdGhyb3duIGlmIHRoZXJlXG5pcyBtb3JlIHRoYW4gb25lIGV4dGVuc2lvbiBvbiB0aGUgZ2l2ZW4gcmVzb3VyY2Ugd2l0aCB0aGUgc3BlY2lmaWVkIHVybC4iCiAgICAgICAgICAgICAgIH0gXQogICAgICAgICAgICB9IF0sCiAgICAgICAgICAgICJleHByZXNzaW9uIiA6IHsKICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjIwNzozLTIwNzo0MSIsCiAgICAgICAgICAgICAgICJ0eXBlIiA6ICJTaW5nbGV0b25Gcm9tIiwKICAgICAgICAgICAgICAgIm9wZXJhbmQiIDogewogICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMjA3OjE4LTIwNzo0MSIsCiAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJFeHRlbnNpb25zIiwKICAgICAgICAgICAgICAgICAgInR5cGUiIDogIkZ1bmN0aW9uUmVmIiwKICAgICAgICAgICAgICAgICAgIm9wZXJhbmQiIDogWyB7CiAgICAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIyMDc6MjktMjA3OjM1IiwKICAgICAgICAgICAgICAgICAgICAgIm5hbWUiIDogImVsZW1lbnQiLAogICAgICAgICAgICAgICAgICAgICAidHlwZSIgOiAiT3BlcmFuZFJlZiIKICAgICAgICAgICAgICAgICAgfSwgewogICAgICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMjA3OjM4LTIwNzo0MCIsCiAgICAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJ1cmwiLAogICAgICAgICAgICAgICAgICAgICAidHlwZSIgOiAiT3BlcmFuZFJlZiIKICAgICAgICAgICAgICAgICAgfSBdCiAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgfSwKICAgICAgICAgICAgIm9wZXJhbmQiIDogWyB7CiAgICAgICAgICAgICAgICJuYW1lIiA6ICJlbGVtZW50IiwKICAgICAgICAgICAgICAgIm9wZXJhbmRUeXBlU3BlY2lmaWVyIiA6IHsKICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjIwNjozNS0yMDY6NDEiLAogICAgICAgICAgICAgICAgICAibmFtZSIgOiAie2h0dHA6Ly9obDcub3JnL2ZoaXJ9RWxlbWVudCIsCiAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJOYW1lZFR5cGVTcGVjaWZpZXIiCiAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgfSwgewogICAgICAgICAgICAgICAibmFtZSIgOiAidXJsIiwKICAgICAgICAgICAgICAgIm9wZXJhbmRUeXBlU3BlY2lmaWVyIiA6IHsKICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjIwNjo0OC0yMDY6NTMiLAogICAgICAgICAgICAgICAgICAibmFtZSIgOiAie3VybjpobDctb3JnOmVsbS10eXBlczpyMX1TdHJpbmciLAogICAgICAgICAgICAgICAgICAidHlwZSIgOiAiTmFtZWRUeXBlU3BlY2lmaWVyIgogICAgICAgICAgICAgICB9CiAgICAgICAgICAgIH0gXQogICAgICAgICB9LCB7CiAgICAgICAgICAgICJsb2NhdG9yIiA6ICIyMTU6MS0yMTg6MTAiLAogICAgICAgICAgICAibmFtZSIgOiAiTW9kaWZpZXJFeHRlbnNpb25zIiwKICAgICAgICAgICAgImNvbnRleHQiIDogIlBhdGllbnQiLAogICAgICAgICAgICAiYWNjZXNzTGV2ZWwiIDogIlB1YmxpYyIsCiAgICAgICAgICAgICJ0eXBlIiA6ICJGdW5jdGlvbkRlZiIsCiAgICAgICAgICAgICJhbm5vdGF0aW9uIiA6IFsgewogICAgICAgICAgICAgICAidHlwZSIgOiAiQW5ub3RhdGlvbiIsCiAgICAgICAgICAgICAgICJ0IiA6IFsgewogICAgICAgICAgICAgICAgICAibmFtZSIgOiAiZGVzY3JpcHRpb24iLAogICAgICAgICAgICAgICAgICAidmFsdWUiIDogIlJldHVybnMgYW55IG1vZGlmaWVyIGV4dGVuc2lvbnMgZGVmaW5lZCBvbiB0aGUgZ2l2ZW4gcmVzb3VyY2Ugd2l0aCB0aGUgc3BlY2lmaWVkIHVybC4iCiAgICAgICAgICAgICAgIH0sIHsKICAgICAgICAgICAgICAgICAgIm5hbWUiIDogImNvbW1lbnQiLAogICAgICAgICAgICAgICAgICAidmFsdWUiIDogIk5PVEU6IEV4dGVuc2lvbnMgYXJlIG5vdCB0aGUgcHJlZmVycmVkIGFwcHJvYWNoLCBidXQgYXJlIHVzZWQgYXMgYSB3YXkgdG8gYWNjZXNzXG5jb250ZW50IHRoYXQgaXMgZGVmaW5lZCBieSBleHRlbnNpb25zIGJ1dCBub3QgeWV0IHN1cmZhY2VkIGluIHRoZVxuQ1FMIG1vZGVsIGluZm8uIgogICAgICAgICAgICAgICB9IF0KICAgICAgICAgICAgfSBdLAogICAgICAgICAgICAiZXhwcmVzc2lvbiIgOiB7CiAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIyMTY6My0yMTg6MTAiLAogICAgICAgICAgICAgICAidHlwZSIgOiAiUXVlcnkiLAogICAgICAgICAgICAgICAic291cmNlIiA6IFsgewogICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMjE2OjMtMjE2OjM2IiwKICAgICAgICAgICAgICAgICAgImFsaWFzIiA6ICJFIiwKICAgICAgICAgICAgICAgICAgImV4cHJlc3Npb24iIDogewogICAgICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMjE2OjMtMjE2OjM0IiwKICAgICAgICAgICAgICAgICAgICAgInBhdGgiIDogIm1vZGlmaWVyRXh0ZW5zaW9uIiwKICAgICAgICAgICAgICAgICAgICAgInR5cGUiIDogIlByb3BlcnR5IiwKICAgICAgICAgICAgICAgICAgICAgInNvdXJjZSIgOiB7CiAgICAgICAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJkb21haW5SZXNvdXJjZSIsCiAgICAgICAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJPcGVyYW5kUmVmIgogICAgICAgICAgICAgICAgICAgICB9CiAgICAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgICAgfSBdLAogICAgICAgICAgICAgICAicmVsYXRpb25zaGlwIiA6IFsgXSwKICAgICAgICAgICAgICAgIndoZXJlIiA6IHsKICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjIxNzo0LTIxNzoyMCIsCiAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJFcXVhbCIsCiAgICAgICAgICAgICAgICAgICJvcGVyYW5kIiA6IFsgewogICAgICAgICAgICAgICAgICAgICAibmFtZSIgOiAiVG9TdHJpbmciLAogICAgICAgICAgICAgICAgICAgICAibGlicmFyeU5hbWUiIDogIkZISVJIZWxwZXJzIiwKICAgICAgICAgICAgICAgICAgICAgInR5cGUiIDogIkZ1bmN0aW9uUmVmIiwKICAgICAgICAgICAgICAgICAgICAgIm9wZXJhbmQiIDogWyB7CiAgICAgICAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIyMTc6MTAtMjE3OjE0IiwKICAgICAgICAgICAgICAgICAgICAgICAgInBhdGgiIDogInVybCIsCiAgICAgICAgICAgICAgICAgICAgICAgICJzY29wZSIgOiAiRSIsCiAgICAgICAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJQcm9wZXJ0eSIKICAgICAgICAgICAgICAgICAgICAgfSBdCiAgICAgICAgICAgICAgICAgIH0sIHsKICAgICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjIxNzoxOC0yMTc6MjAiLAogICAgICAgICAgICAgICAgICAgICAibmFtZSIgOiAidXJsIiwKICAgICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk9wZXJhbmRSZWYiCiAgICAgICAgICAgICAgICAgIH0gXQogICAgICAgICAgICAgICB9LAogICAgICAgICAgICAgICAicmV0dXJuIiA6IHsKICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjIxODozLTIxODoxMCIsCiAgICAgICAgICAgICAgICAgICJleHByZXNzaW9uIiA6IHsKICAgICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjIxODoxMCIsCiAgICAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJFIiwKICAgICAgICAgICAgICAgICAgICAgInR5cGUiIDogIkFsaWFzUmVmIgogICAgICAgICAgICAgICAgICB9CiAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgfSwKICAgICAgICAgICAgIm9wZXJhbmQiIDogWyB7CiAgICAgICAgICAgICAgICJuYW1lIiA6ICJkb21haW5SZXNvdXJjZSIsCiAgICAgICAgICAgICAgICJvcGVyYW5kVHlwZVNwZWNpZmllciIgOiB7CiAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIyMTU6NTEtMjE1OjY0IiwKICAgICAgICAgICAgICAgICAgIm5hbWUiIDogIntodHRwOi8vaGw3Lm9yZy9maGlyfURvbWFpblJlc291cmNlIiwKICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk5hbWVkVHlwZVNwZWNpZmllciIKICAgICAgICAgICAgICAgfQogICAgICAgICAgICB9LCB7CiAgICAgICAgICAgICAgICJuYW1lIiA6ICJ1cmwiLAogICAgICAgICAgICAgICAib3BlcmFuZFR5cGVTcGVjaWZpZXIiIDogewogICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMjE1OjcxLTIxNTo3NiIsCiAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJ7dXJuOmhsNy1vcmc6ZWxtLXR5cGVzOnIxfVN0cmluZyIsCiAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJOYW1lZFR5cGVTcGVjaWZpZXIiCiAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgfSBdCiAgICAgICAgIH0sIHsKICAgICAgICAgICAgImxvY2F0b3IiIDogIjIyNToxLTIyNjo1NiIsCiAgICAgICAgICAgICJuYW1lIiA6ICJNb2RpZmllckV4dGVuc2lvbiIsCiAgICAgICAgICAgICJjb250ZXh0IiA6ICJQYXRpZW50IiwKICAgICAgICAgICAgImFjY2Vzc0xldmVsIiA6ICJQdWJsaWMiLAogICAgICAgICAgICAidHlwZSIgOiAiRnVuY3Rpb25EZWYiLAogICAgICAgICAgICAiYW5ub3RhdGlvbiIgOiBbIHsKICAgICAgICAgICAgICAgInR5cGUiIDogIkFubm90YXRpb24iLAogICAgICAgICAgICAgICAidCIgOiBbIHsKICAgICAgICAgICAgICAgICAgIm5hbWUiIDogImRlc2NyaXB0aW9uIiwKICAgICAgICAgICAgICAgICAgInZhbHVlIiA6ICJSZXR1cm5zIHRoZSBzaW5nbGUgbW9kaWZpZXIgZXh0ZW5zaW9uIChpZiBwcmVzZW50KSBvbiB0aGUgZ2l2ZW4gcmVzb3VyY2Ugd2l0aCB0aGUgc3BlY2lmaWVkIHVybC4iCiAgICAgICAgICAgICAgIH0sIHsKICAgICAgICAgICAgICAgICAgIm5hbWUiIDogImNvbW1lbnQiLAogICAgICAgICAgICAgICAgICAidmFsdWUiIDogIlRoaXMgZnVuY3Rpb24gdXNlcyBzaW5nbGV0b24gZnJvbSB0byBlbnN1cmUgdGhhdCBhIHJ1bi10aW1lIGV4Y2VwdGlvbiBpcyB0aHJvd24gaWYgdGhlcmVcbmlzIG1vcmUgdGhhbiBvbmUgZXh0ZW5zaW9uIG9uIHRoZSBnaXZlbiByZXNvdXJjZSB3aXRoIHRoZSBzcGVjaWZpZWQgdXJsLiIKICAgICAgICAgICAgICAgfSBdCiAgICAgICAgICAgIH0gXSwKICAgICAgICAgICAgImV4cHJlc3Npb24iIDogewogICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMjI2OjMtMjI2OjU2IiwKICAgICAgICAgICAgICAgInR5cGUiIDogIlNpbmdsZXRvbkZyb20iLAogICAgICAgICAgICAgICAib3BlcmFuZCIgOiB7CiAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIyMjY6MTgtMjI2OjU2IiwKICAgICAgICAgICAgICAgICAgIm5hbWUiIDogIk1vZGlmaWVyRXh0ZW5zaW9ucyIsCiAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJGdW5jdGlvblJlZiIsCiAgICAgICAgICAgICAgICAgICJvcGVyYW5kIiA6IFsgewogICAgICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMjI2OjM3LTIyNjo1MCIsCiAgICAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJkb21haW5SZXNvdXJjZSIsCiAgICAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJPcGVyYW5kUmVmIgogICAgICAgICAgICAgICAgICB9LCB7CiAgICAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIyMjY6NTMtMjI2OjU1IiwKICAgICAgICAgICAgICAgICAgICAgIm5hbWUiIDogInVybCIsCiAgICAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJPcGVyYW5kUmVmIgogICAgICAgICAgICAgICAgICB9IF0KICAgICAgICAgICAgICAgfQogICAgICAgICAgICB9LAogICAgICAgICAgICAib3BlcmFuZCIgOiBbIHsKICAgICAgICAgICAgICAgIm5hbWUiIDogImRvbWFpblJlc291cmNlIiwKICAgICAgICAgICAgICAgIm9wZXJhbmRUeXBlU3BlY2lmaWVyIiA6IHsKICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjIyNTo1MC0yMjU6NjMiLAogICAgICAgICAgICAgICAgICAibmFtZSIgOiAie2h0dHA6Ly9obDcub3JnL2ZoaXJ9RG9tYWluUmVzb3VyY2UiLAogICAgICAgICAgICAgICAgICAidHlwZSIgOiAiTmFtZWRUeXBlU3BlY2lmaWVyIgogICAgICAgICAgICAgICB9CiAgICAgICAgICAgIH0sIHsKICAgICAgICAgICAgICAgIm5hbWUiIDogInVybCIsCiAgICAgICAgICAgICAgICJvcGVyYW5kVHlwZVNwZWNpZmllciIgOiB7CiAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIyMjU6NzAtMjI1Ojc1IiwKICAgICAgICAgICAgICAgICAgIm5hbWUiIDogInt1cm46aGw3LW9yZzplbG0tdHlwZXM6cjF9U3RyaW5nIiwKICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk5hbWVkVHlwZVNwZWNpZmllciIKICAgICAgICAgICAgICAgfQogICAgICAgICAgICB9IF0KICAgICAgICAgfSwgewogICAgICAgICAgICAibG9jYXRvciIgOiAiMjMzOjEtMjM2OjEwIiwKICAgICAgICAgICAgIm5hbWUiIDogIk1vZGlmaWVyRXh0ZW5zaW9ucyIsCiAgICAgICAgICAgICJjb250ZXh0IiA6ICJQYXRpZW50IiwKICAgICAgICAgICAgImFjY2Vzc0xldmVsIiA6ICJQdWJsaWMiLAogICAgICAgICAgICAidHlwZSIgOiAiRnVuY3Rpb25EZWYiLAogICAgICAgICAgICAiYW5ub3RhdGlvbiIgOiBbIHsKICAgICAgICAgICAgICAgInR5cGUiIDogIkFubm90YXRpb24iLAogICAgICAgICAgICAgICAidCIgOiBbIHsKICAgICAgICAgICAgICAgICAgIm5hbWUiIDogImRlc2NyaXB0aW9uIiwKICAgICAgICAgICAgICAgICAgInZhbHVlIiA6ICJSZXR1cm5zIGFueSBtb2RpZmllciBleHRlbnNpb25zIGRlZmluZWQgb24gdGhlIGdpdmVuIGVsZW1lbnQgd2l0aCB0aGUgc3BlY2lmaWVkIHVybC4iCiAgICAgICAgICAgICAgIH0sIHsKICAgICAgICAgICAgICAgICAgIm5hbWUiIDogImNvbW1lbnQiLAogICAgICAgICAgICAgICAgICAidmFsdWUiIDogIk5PVEU6IEV4dGVuc2lvbnMgYXJlIG5vdCB0aGUgcHJlZmVycmVkIGFwcHJvYWNoLCBidXQgYXJlIHVzZWQgYXMgYSB3YXkgdG8gYWNjZXNzXG5jb250ZW50IHRoYXQgaXMgZGVmaW5lZCBieSBleHRlbnNpb25zIGJ1dCBub3QgeWV0IHN1cmZhY2VkIGluIHRoZSBDUUwgbW9kZWwgaW5mby4iCiAgICAgICAgICAgICAgIH0gXQogICAgICAgICAgICB9IF0sCiAgICAgICAgICAgICJleHByZXNzaW9uIiA6IHsKICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjIzNDozLTIzNjoxMCIsCiAgICAgICAgICAgICAgICJ0eXBlIiA6ICJRdWVyeSIsCiAgICAgICAgICAgICAgICJzb3VyY2UiIDogWyB7CiAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIyMzQ6My0yMzQ6MjkiLAogICAgICAgICAgICAgICAgICAiYWxpYXMiIDogIkUiLAogICAgICAgICAgICAgICAgICAiZXhwcmVzc2lvbiIgOiB7CiAgICAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIyMzQ6My0yMzQ6MjciLAogICAgICAgICAgICAgICAgICAgICAicGF0aCIgOiAibW9kaWZpZXJFeHRlbnNpb24iLAogICAgICAgICAgICAgICAgICAgICAidHlwZSIgOiAiUHJvcGVydHkiLAogICAgICAgICAgICAgICAgICAgICAic291cmNlIiA6IHsKICAgICAgICAgICAgICAgICAgICAgICAgIm5hbWUiIDogImVsZW1lbnQiLAogICAgICAgICAgICAgICAgICAgICAgICAidHlwZSIgOiAiT3BlcmFuZFJlZiIKICAgICAgICAgICAgICAgICAgICAgfQogICAgICAgICAgICAgICAgICB9CiAgICAgICAgICAgICAgIH0gXSwKICAgICAgICAgICAgICAgInJlbGF0aW9uc2hpcCIgOiBbIF0sCiAgICAgICAgICAgICAgICJ3aGVyZSIgOiB7CiAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIyMzU6NC0yMzU6MjAiLAogICAgICAgICAgICAgICAgICAidHlwZSIgOiAiRXF1YWwiLAogICAgICAgICAgICAgICAgICAib3BlcmFuZCIgOiBbIHsKICAgICAgICAgICAgICAgICAgICAgIm5hbWUiIDogIlRvU3RyaW5nIiwKICAgICAgICAgICAgICAgICAgICAgImxpYnJhcnlOYW1lIiA6ICJGSElSSGVscGVycyIsCiAgICAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJGdW5jdGlvblJlZiIsCiAgICAgICAgICAgICAgICAgICAgICJvcGVyYW5kIiA6IFsgewogICAgICAgICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMjM1OjEwLTIzNToxNCIsCiAgICAgICAgICAgICAgICAgICAgICAgICJwYXRoIiA6ICJ1cmwiLAogICAgICAgICAgICAgICAgICAgICAgICAic2NvcGUiIDogIkUiLAogICAgICAgICAgICAgICAgICAgICAgICAidHlwZSIgOiAiUHJvcGVydHkiCiAgICAgICAgICAgICAgICAgICAgIH0gXQogICAgICAgICAgICAgICAgICB9LCB7CiAgICAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIyMzU6MTgtMjM1OjIwIiwKICAgICAgICAgICAgICAgICAgICAgIm5hbWUiIDogInVybCIsCiAgICAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJPcGVyYW5kUmVmIgogICAgICAgICAgICAgICAgICB9IF0KICAgICAgICAgICAgICAgfSwKICAgICAgICAgICAgICAgInJldHVybiIgOiB7CiAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIyMzY6My0yMzY6MTAiLAogICAgICAgICAgICAgICAgICAiZXhwcmVzc2lvbiIgOiB7CiAgICAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIyMzY6MTAiLAogICAgICAgICAgICAgICAgICAgICAibmFtZSIgOiAiRSIsCiAgICAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJBbGlhc1JlZiIKICAgICAgICAgICAgICAgICAgfQogICAgICAgICAgICAgICB9CiAgICAgICAgICAgIH0sCiAgICAgICAgICAgICJvcGVyYW5kIiA6IFsgewogICAgICAgICAgICAgICAibmFtZSIgOiAiZWxlbWVudCIsCiAgICAgICAgICAgICAgICJvcGVyYW5kVHlwZVNwZWNpZmllciIgOiB7CiAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIyMzM6NDQtMjMzOjU4IiwKICAgICAgICAgICAgICAgICAgIm5hbWUiIDogIntodHRwOi8vaGw3Lm9yZy9maGlyfUJhY2tib25lRWxlbWVudCIsCiAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJOYW1lZFR5cGVTcGVjaWZpZXIiCiAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgfSwgewogICAgICAgICAgICAgICAibmFtZSIgOiAidXJsIiwKICAgICAgICAgICAgICAgIm9wZXJhbmRUeXBlU3BlY2lmaWVyIiA6IHsKICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjIzMzo2NS0yMzM6NzAiLAogICAgICAgICAgICAgICAgICAibmFtZSIgOiAie3VybjpobDctb3JnOmVsbS10eXBlczpyMX1TdHJpbmciLAogICAgICAgICAgICAgICAgICAidHlwZSIgOiAiTmFtZWRUeXBlU3BlY2lmaWVyIgogICAgICAgICAgICAgICB9CiAgICAgICAgICAgIH0gXQogICAgICAgICB9LCB7CiAgICAgICAgICAgICJsb2NhdG9yIiA6ICIyNDM6MS0yNDQ6NDkiLAogICAgICAgICAgICAibmFtZSIgOiAiTW9kaWZpZXJFeHRlbnNpb24iLAogICAgICAgICAgICAiY29udGV4dCIgOiAiUGF0aWVudCIsCiAgICAgICAgICAgICJhY2Nlc3NMZXZlbCIgOiAiUHVibGljIiwKICAgICAgICAgICAgInR5cGUiIDogIkZ1bmN0aW9uRGVmIiwKICAgICAgICAgICAgImFubm90YXRpb24iIDogWyB7CiAgICAgICAgICAgICAgICJ0eXBlIiA6ICJBbm5vdGF0aW9uIiwKICAgICAgICAgICAgICAgInQiIDogWyB7CiAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJkZXNjcmlwdGlvbiIsCiAgICAgICAgICAgICAgICAgICJ2YWx1ZSIgOiAiUmV0dXJucyB0aGUgc2luZ2xlIG1vZGlmaWVyIGV4dGVuc2lvbiAoaWYgcHJlc2VudCkgb24gdGhlIGdpdmVuIGVsZW1lbnQgd2l0aCB0aGUgc3BlY2lmaWVkIHVybC4iCiAgICAgICAgICAgICAgIH0sIHsKICAgICAgICAgICAgICAgICAgIm5hbWUiIDogImNvbW1lbnQiLAogICAgICAgICAgICAgICAgICAidmFsdWUiIDogIlRoaXMgZnVuY3Rpb24gdXNlcyBzaW5nbGV0b24gZnJvbSB0byBlbnN1cmUgdGhhdCBhIHJ1bi10aW1lIGV4Y2VwdGlvbiBpcyB0aHJvd24gaWYgdGhlcmVcbmlzIG1vcmUgdGhhbiBvbmUgZXh0ZW5zaW9uIG9uIHRoZSBnaXZlbiByZXNvdXJjZSB3aXRoIHRoZSBzcGVjaWZpZWQgdXJsLiIKICAgICAgICAgICAgICAgfSBdCiAgICAgICAgICAgIH0gXSwKICAgICAgICAgICAgImV4cHJlc3Npb24iIDogewogICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMjQ0OjMtMjQ0OjQ5IiwKICAgICAgICAgICAgICAgInR5cGUiIDogIlNpbmdsZXRvbkZyb20iLAogICAgICAgICAgICAgICAib3BlcmFuZCIgOiB7CiAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIyNDQ6MTgtMjQ0OjQ5IiwKICAgICAgICAgICAgICAgICAgIm5hbWUiIDogIk1vZGlmaWVyRXh0ZW5zaW9ucyIsCiAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJGdW5jdGlvblJlZiIsCiAgICAgICAgICAgICAgICAgICJvcGVyYW5kIiA6IFsgewogICAgICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMjQ0OjM3LTI0NDo0MyIsCiAgICAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJlbGVtZW50IiwKICAgICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk9wZXJhbmRSZWYiCiAgICAgICAgICAgICAgICAgIH0sIHsKICAgICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjI0NDo0Ni0yNDQ6NDgiLAogICAgICAgICAgICAgICAgICAgICAibmFtZSIgOiAidXJsIiwKICAgICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk9wZXJhbmRSZWYiCiAgICAgICAgICAgICAgICAgIH0gXQogICAgICAgICAgICAgICB9CiAgICAgICAgICAgIH0sCiAgICAgICAgICAgICJvcGVyYW5kIiA6IFsgewogICAgICAgICAgICAgICAibmFtZSIgOiAiZWxlbWVudCIsCiAgICAgICAgICAgICAgICJvcGVyYW5kVHlwZVNwZWNpZmllciIgOiB7CiAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIyNDM6NDMtMjQzOjU3IiwKICAgICAgICAgICAgICAgICAgIm5hbWUiIDogIntodHRwOi8vaGw3Lm9yZy9maGlyfUJhY2tib25lRWxlbWVudCIsCiAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJOYW1lZFR5cGVTcGVjaWZpZXIiCiAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgfSwgewogICAgICAgICAgICAgICAibmFtZSIgOiAidXJsIiwKICAgICAgICAgICAgICAgIm9wZXJhbmRUeXBlU3BlY2lmaWVyIiA6IHsKICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjI0Mzo2NC0yNDM6NjkiLAogICAgICAgICAgICAgICAgICAibmFtZSIgOiAie3VybjpobDctb3JnOmVsbS10eXBlczpyMX1TdHJpbmciLAogICAgICAgICAgICAgICAgICAidHlwZSIgOiAiTmFtZWRUeXBlU3BlY2lmaWVyIgogICAgICAgICAgICAgICB9CiAgICAgICAgICAgIH0gXQogICAgICAgICB9LCB7CiAgICAgICAgICAgICJsb2NhdG9yIiA6ICIyNTE6MS0yNTQ6MTAiLAogICAgICAgICAgICAibmFtZSIgOiAiQmFzZUV4dGVuc2lvbnMiLAogICAgICAgICAgICAiY29udGV4dCIgOiAiUGF0aWVudCIsCiAgICAgICAgICAgICJhY2Nlc3NMZXZlbCIgOiAiUHVibGljIiwKICAgICAgICAgICAgInR5cGUiIDogIkZ1bmN0aW9uRGVmIiwKICAgICAgICAgICAgImFubm90YXRpb24iIDogWyB7CiAgICAgICAgICAgICAgICJ0eXBlIiA6ICJBbm5vdGF0aW9uIiwKICAgICAgICAgICAgICAgInQiIDogWyB7CiAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJkZXNjcmlwdGlvbiIsCiAgICAgICAgICAgICAgICAgICJ2YWx1ZSIgOiAiUmV0dXJucyBhbnkgYmFzZS1GSElSIGV4dGVuc2lvbnMgZGVmaW5lZCBvbiB0aGUgZ2l2ZW4gcmVzb3VyY2Ugd2l0aCB0aGUgc3BlY2lmaWVkIGlkLiIKICAgICAgICAgICAgICAgfSwgewogICAgICAgICAgICAgICAgICAibmFtZSIgOiAiY29tbWVudCIsCiAgICAgICAgICAgICAgICAgICJ2YWx1ZSIgOiAiTk9URTogRXh0ZW5zaW9ucyBhcmUgbm90IHRoZSBwcmVmZXJyZWQgYXBwcm9hY2gsIGJ1dCBhcmUgdXNlZCBhcyBhIHdheSB0byBhY2Nlc3NcbmNvbnRlbnQgdGhhdCBpcyBkZWZpbmVkIGJ5IGV4dGVuc2lvbnMgYnV0IG5vdCB5ZXQgc3VyZmFjZWQgaW4gdGhlIENRTCBtb2RlbCBpbmZvLiIKICAgICAgICAgICAgICAgfSBdCiAgICAgICAgICAgIH0gXSwKICAgICAgICAgICAgImV4cHJlc3Npb24iIDogewogICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMjUyOjMtMjU0OjEwIiwKICAgICAgICAgICAgICAgInR5cGUiIDogIlF1ZXJ5IiwKICAgICAgICAgICAgICAgInNvdXJjZSIgOiBbIHsKICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjI1MjozLTI1MjoyOCIsCiAgICAgICAgICAgICAgICAgICJhbGlhcyIgOiAiRSIsCiAgICAgICAgICAgICAgICAgICJleHByZXNzaW9uIiA6IHsKICAgICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjI1MjozLTI1MjoyNiIsCiAgICAgICAgICAgICAgICAgICAgICJwYXRoIiA6ICJleHRlbnNpb24iLAogICAgICAgICAgICAgICAgICAgICAidHlwZSIgOiAiUHJvcGVydHkiLAogICAgICAgICAgICAgICAgICAgICAic291cmNlIiA6IHsKICAgICAgICAgICAgICAgICAgICAgICAgIm5hbWUiIDogImRvbWFpblJlc291cmNlIiwKICAgICAgICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk9wZXJhbmRSZWYiCiAgICAgICAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgICAgICAgfQogICAgICAgICAgICAgICB9IF0sCiAgICAgICAgICAgICAgICJyZWxhdGlvbnNoaXAiIDogWyBdLAogICAgICAgICAgICAgICAid2hlcmUiIDogewogICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMjUzOjQtMjUzOjY2IiwKICAgICAgICAgICAgICAgICAgInR5cGUiIDogIkVxdWFsIiwKICAgICAgICAgICAgICAgICAgIm9wZXJhbmQiIDogWyB7CiAgICAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJUb1N0cmluZyIsCiAgICAgICAgICAgICAgICAgICAgICJsaWJyYXJ5TmFtZSIgOiAiRkhJUkhlbHBlcnMiLAogICAgICAgICAgICAgICAgICAgICAidHlwZSIgOiAiRnVuY3Rpb25SZWYiLAogICAgICAgICAgICAgICAgICAgICAib3BlcmFuZCIgOiBbIHsKICAgICAgICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjI1MzoxMC0yNTM6MTQiLAogICAgICAgICAgICAgICAgICAgICAgICAicGF0aCIgOiAidXJsIiwKICAgICAgICAgICAgICAgICAgICAgICAgInNjb3BlIiA6ICJFIiwKICAgICAgICAgICAgICAgICAgICAgICAgInR5cGUiIDogIlByb3BlcnR5IgogICAgICAgICAgICAgICAgICAgICB9IF0KICAgICAgICAgICAgICAgICAgfSwgewogICAgICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMjUzOjE4LTI1Mzo2NiIsCiAgICAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJDb25jYXRlbmF0ZSIsCiAgICAgICAgICAgICAgICAgICAgICJvcGVyYW5kIiA6IFsgewogICAgICAgICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMjUzOjE5LTI1Mzo2MCIsCiAgICAgICAgICAgICAgICAgICAgICAgICJ2YWx1ZVR5cGUiIDogInt1cm46aGw3LW9yZzplbG0tdHlwZXM6cjF9U3RyaW5nIiwKICAgICAgICAgICAgICAgICAgICAgICAgInZhbHVlIiA6ICJodHRwOi8vaGw3Lm9yZy9maGlyL1N0cnVjdHVyZURlZmluaXRpb24vIiwKICAgICAgICAgICAgICAgICAgICAgICAgInR5cGUiIDogIkxpdGVyYWwiCiAgICAgICAgICAgICAgICAgICAgIH0sIHsKICAgICAgICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjI1Mzo2NC0yNTM6NjUiLAogICAgICAgICAgICAgICAgICAgICAgICAibmFtZSIgOiAiaWQiLAogICAgICAgICAgICAgICAgICAgICAgICAidHlwZSIgOiAiT3BlcmFuZFJlZiIKICAgICAgICAgICAgICAgICAgICAgfSBdCiAgICAgICAgICAgICAgICAgIH0gXQogICAgICAgICAgICAgICB9LAogICAgICAgICAgICAgICAicmV0dXJuIiA6IHsKICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjI1NDozLTI1NDoxMCIsCiAgICAgICAgICAgICAgICAgICJleHByZXNzaW9uIiA6IHsKICAgICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjI1NDoxMCIsCiAgICAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJFIiwKICAgICAgICAgICAgICAgICAgICAgInR5cGUiIDogIkFsaWFzUmVmIgogICAgICAgICAgICAgICAgICB9CiAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgfSwKICAgICAgICAgICAgIm9wZXJhbmQiIDogWyB7CiAgICAgICAgICAgICAgICJuYW1lIiA6ICJkb21haW5SZXNvdXJjZSIsCiAgICAgICAgICAgICAgICJvcGVyYW5kVHlwZVNwZWNpZmllciIgOiB7CiAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIyNTE6NDctMjUxOjYwIiwKICAgICAgICAgICAgICAgICAgIm5hbWUiIDogIntodHRwOi8vaGw3Lm9yZy9maGlyfURvbWFpblJlc291cmNlIiwKICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk5hbWVkVHlwZVNwZWNpZmllciIKICAgICAgICAgICAgICAgfQogICAgICAgICAgICB9LCB7CiAgICAgICAgICAgICAgICJuYW1lIiA6ICJpZCIsCiAgICAgICAgICAgICAgICJvcGVyYW5kVHlwZVNwZWNpZmllciIgOiB7CiAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIyNTE6NjYtMjUxOjcxIiwKICAgICAgICAgICAgICAgICAgIm5hbWUiIDogInt1cm46aGw3LW9yZzplbG0tdHlwZXM6cjF9U3RyaW5nIiwKICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk5hbWVkVHlwZVNwZWNpZmllciIKICAgICAgICAgICAgICAgfQogICAgICAgICAgICB9IF0KICAgICAgICAgfSwgewogICAgICAgICAgICAibG9jYXRvciIgOiAiMjYxOjEtMjYyOjUxIiwKICAgICAgICAgICAgIm5hbWUiIDogIkJhc2VFeHRlbnNpb24iLAogICAgICAgICAgICAiY29udGV4dCIgOiAiUGF0aWVudCIsCiAgICAgICAgICAgICJhY2Nlc3NMZXZlbCIgOiAiUHVibGljIiwKICAgICAgICAgICAgInR5cGUiIDogIkZ1bmN0aW9uRGVmIiwKICAgICAgICAgICAgImFubm90YXRpb24iIDogWyB7CiAgICAgICAgICAgICAgICJ0eXBlIiA6ICJBbm5vdGF0aW9uIiwKICAgICAgICAgICAgICAgInQiIDogWyB7CiAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJkZXNjcmlwdGlvbiIsCiAgICAgICAgICAgICAgICAgICJ2YWx1ZSIgOiAiUmV0dXJucyB0aGUgc2luZ2xlIGJhc2UtRkhJUiBleHRlbnNpb24gKGlmIHByZXNlbnQpIG9uIHRoZSBnaXZlbiByZXNvdXJjZSB3aXRoIHRoZSBzcGVjaWZpZWQgaWQuIgogICAgICAgICAgICAgICB9LCB7CiAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJjb21tZW50IiwKICAgICAgICAgICAgICAgICAgInZhbHVlIiA6ICJUaGlzIGZ1bmN0aW9uIHVzZXMgc2luZ2xldG9uIGZyb20gdG8gZW5zdXJlIHRoYXQgYSBydW4tdGltZSBleGNlcHRpb24gaXMgdGhyb3duIGlmIHRoZXJlXG5pcyBtb3JlIHRoYW4gb25lIGV4dGVuc2lvbiBvbiB0aGUgZ2l2ZW4gcmVzb3VyY2Ugd2l0aCB0aGUgc3BlY2lmaWVkIHVybC4iCiAgICAgICAgICAgICAgIH0gXQogICAgICAgICAgICB9IF0sCiAgICAgICAgICAgICJleHByZXNzaW9uIiA6IHsKICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjI2MjozLTI2Mjo1MSIsCiAgICAgICAgICAgICAgICJ0eXBlIiA6ICJTaW5nbGV0b25Gcm9tIiwKICAgICAgICAgICAgICAgIm9wZXJhbmQiIDogewogICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMjYyOjE4LTI2Mjo1MSIsCiAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJCYXNlRXh0ZW5zaW9ucyIsCiAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJGdW5jdGlvblJlZiIsCiAgICAgICAgICAgICAgICAgICJvcGVyYW5kIiA6IFsgewogICAgICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMjYyOjMzLTI2Mjo0NiIsCiAgICAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJkb21haW5SZXNvdXJjZSIsCiAgICAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJPcGVyYW5kUmVmIgogICAgICAgICAgICAgICAgICB9LCB7CiAgICAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIyNjI6NDktMjYyOjUwIiwKICAgICAgICAgICAgICAgICAgICAgIm5hbWUiIDogImlkIiwKICAgICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk9wZXJhbmRSZWYiCiAgICAgICAgICAgICAgICAgIH0gXQogICAgICAgICAgICAgICB9CiAgICAgICAgICAgIH0sCiAgICAgICAgICAgICJvcGVyYW5kIiA6IFsgewogICAgICAgICAgICAgICAibmFtZSIgOiAiZG9tYWluUmVzb3VyY2UiLAogICAgICAgICAgICAgICAib3BlcmFuZFR5cGVTcGVjaWZpZXIiIDogewogICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMjYxOjQ2LTI2MTo1OSIsCiAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJ7aHR0cDovL2hsNy5vcmcvZmhpcn1Eb21haW5SZXNvdXJjZSIsCiAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJOYW1lZFR5cGVTcGVjaWZpZXIiCiAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgfSwgewogICAgICAgICAgICAgICAibmFtZSIgOiAiaWQiLAogICAgICAgICAgICAgICAib3BlcmFuZFR5cGVTcGVjaWZpZXIiIDogewogICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMjYxOjY1LTI2MTo3MCIsCiAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJ7dXJuOmhsNy1vcmc6ZWxtLXR5cGVzOnIxfVN0cmluZyIsCiAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJOYW1lZFR5cGVTcGVjaWZpZXIiCiAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgfSBdCiAgICAgICAgIH0sIHsKICAgICAgICAgICAgImxvY2F0b3IiIDogIjI2OToxLTI3MjoxMCIsCiAgICAgICAgICAgICJuYW1lIiA6ICJCYXNlRXh0ZW5zaW9ucyIsCiAgICAgICAgICAgICJjb250ZXh0IiA6ICJQYXRpZW50IiwKICAgICAgICAgICAgImFjY2Vzc0xldmVsIiA6ICJQdWJsaWMiLAogICAgICAgICAgICAidHlwZSIgOiAiRnVuY3Rpb25EZWYiLAogICAgICAgICAgICAiYW5ub3RhdGlvbiIgOiBbIHsKICAgICAgICAgICAgICAgInR5cGUiIDogIkFubm90YXRpb24iLAogICAgICAgICAgICAgICAidCIgOiBbIHsKICAgICAgICAgICAgICAgICAgIm5hbWUiIDogImRlc2NyaXB0aW9uIiwKICAgICAgICAgICAgICAgICAgInZhbHVlIiA6ICJSZXR1cm5zIGFueSBiYXNlLUZISVIgZXh0ZW5zaW9ucyBkZWZpbmVkIG9uIHRoZSBnaXZlbiBlbGVtZW50IHdpdGggdGhlIHNwZWNpZmllZCBpZC4iCiAgICAgICAgICAgICAgIH0sIHsKICAgICAgICAgICAgICAgICAgIm5hbWUiIDogImNvbW1lbnQiLAogICAgICAgICAgICAgICAgICAidmFsdWUiIDogIk5PVEU6IEV4dGVuc2lvbnMgYXJlIG5vdCB0aGUgcHJlZmVycmVkIGFwcHJvYWNoLCBidXQgYXJlIHVzZWQgYXMgYSB3YXkgdG8gYWNjZXNzXG5jb250ZW50IHRoYXQgaXMgZGVmaW5lZCBieSBleHRlbnNpb25zIGJ1dCBub3QgeWV0IHN1cmZhY2VkIGluIHRoZSBDUUwgbW9kZWwgaW5mby4iCiAgICAgICAgICAgICAgIH0gXQogICAgICAgICAgICB9IF0sCiAgICAgICAgICAgICJleHByZXNzaW9uIiA6IHsKICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjI3MDozLTI3MjoxMCIsCiAgICAgICAgICAgICAgICJ0eXBlIiA6ICJRdWVyeSIsCiAgICAgICAgICAgICAgICJzb3VyY2UiIDogWyB7CiAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIyNzA6My0yNzA6MjEiLAogICAgICAgICAgICAgICAgICAiYWxpYXMiIDogIkUiLAogICAgICAgICAgICAgICAgICAiZXhwcmVzc2lvbiIgOiB7CiAgICAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIyNzA6My0yNzA6MTkiLAogICAgICAgICAgICAgICAgICAgICAicGF0aCIgOiAiZXh0ZW5zaW9uIiwKICAgICAgICAgICAgICAgICAgICAgInR5cGUiIDogIlByb3BlcnR5IiwKICAgICAgICAgICAgICAgICAgICAgInNvdXJjZSIgOiB7CiAgICAgICAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJlbGVtZW50IiwKICAgICAgICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk9wZXJhbmRSZWYiCiAgICAgICAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgICAgICAgfQogICAgICAgICAgICAgICB9IF0sCiAgICAgICAgICAgICAgICJyZWxhdGlvbnNoaXAiIDogWyBdLAogICAgICAgICAgICAgICAid2hlcmUiIDogewogICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMjcxOjQtMjcxOjY2IiwKICAgICAgICAgICAgICAgICAgInR5cGUiIDogIkVxdWFsIiwKICAgICAgICAgICAgICAgICAgIm9wZXJhbmQiIDogWyB7CiAgICAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJUb1N0cmluZyIsCiAgICAgICAgICAgICAgICAgICAgICJsaWJyYXJ5TmFtZSIgOiAiRkhJUkhlbHBlcnMiLAogICAgICAgICAgICAgICAgICAgICAidHlwZSIgOiAiRnVuY3Rpb25SZWYiLAogICAgICAgICAgICAgICAgICAgICAib3BlcmFuZCIgOiBbIHsKICAgICAgICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjI3MToxMC0yNzE6MTQiLAogICAgICAgICAgICAgICAgICAgICAgICAicGF0aCIgOiAidXJsIiwKICAgICAgICAgICAgICAgICAgICAgICAgInNjb3BlIiA6ICJFIiwKICAgICAgICAgICAgICAgICAgICAgICAgInR5cGUiIDogIlByb3BlcnR5IgogICAgICAgICAgICAgICAgICAgICB9IF0KICAgICAgICAgICAgICAgICAgfSwgewogICAgICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMjcxOjE4LTI3MTo2NiIsCiAgICAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJDb25jYXRlbmF0ZSIsCiAgICAgICAgICAgICAgICAgICAgICJvcGVyYW5kIiA6IFsgewogICAgICAgICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMjcxOjE5LTI3MTo2MCIsCiAgICAgICAgICAgICAgICAgICAgICAgICJ2YWx1ZVR5cGUiIDogInt1cm46aGw3LW9yZzplbG0tdHlwZXM6cjF9U3RyaW5nIiwKICAgICAgICAgICAgICAgICAgICAgICAgInZhbHVlIiA6ICJodHRwOi8vaGw3Lm9yZy9maGlyL1N0cnVjdHVyZURlZmluaXRpb24vIiwKICAgICAgICAgICAgICAgICAgICAgICAgInR5cGUiIDogIkxpdGVyYWwiCiAgICAgICAgICAgICAgICAgICAgIH0sIHsKICAgICAgICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjI3MTo2NC0yNzE6NjUiLAogICAgICAgICAgICAgICAgICAgICAgICAibmFtZSIgOiAiaWQiLAogICAgICAgICAgICAgICAgICAgICAgICAidHlwZSIgOiAiT3BlcmFuZFJlZiIKICAgICAgICAgICAgICAgICAgICAgfSBdCiAgICAgICAgICAgICAgICAgIH0gXQogICAgICAgICAgICAgICB9LAogICAgICAgICAgICAgICAicmV0dXJuIiA6IHsKICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjI3MjozLTI3MjoxMCIsCiAgICAgICAgICAgICAgICAgICJleHByZXNzaW9uIiA6IHsKICAgICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjI3MjoxMCIsCiAgICAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJFIiwKICAgICAgICAgICAgICAgICAgICAgInR5cGUiIDogIkFsaWFzUmVmIgogICAgICAgICAgICAgICAgICB9CiAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgfSwKICAgICAgICAgICAgIm9wZXJhbmQiIDogWyB7CiAgICAgICAgICAgICAgICJuYW1lIiA6ICJlbGVtZW50IiwKICAgICAgICAgICAgICAgIm9wZXJhbmRUeXBlU3BlY2lmaWVyIiA6IHsKICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjI2OTo0MC0yNjk6NDYiLAogICAgICAgICAgICAgICAgICAibmFtZSIgOiAie2h0dHA6Ly9obDcub3JnL2ZoaXJ9RWxlbWVudCIsCiAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJOYW1lZFR5cGVTcGVjaWZpZXIiCiAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgfSwgewogICAgICAgICAgICAgICAibmFtZSIgOiAiaWQiLAogICAgICAgICAgICAgICAib3BlcmFuZFR5cGVTcGVjaWZpZXIiIDogewogICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMjY5OjUyLTI2OTo1NyIsCiAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJ7dXJuOmhsNy1vcmc6ZWxtLXR5cGVzOnIxfVN0cmluZyIsCiAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJOYW1lZFR5cGVTcGVjaWZpZXIiCiAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgfSBdCiAgICAgICAgIH0sIHsKICAgICAgICAgICAgImxvY2F0b3IiIDogIjI3OToxLTI4MDo0NCIsCiAgICAgICAgICAgICJuYW1lIiA6ICJCYXNlRXh0ZW5zaW9uIiwKICAgICAgICAgICAgImNvbnRleHQiIDogIlBhdGllbnQiLAogICAgICAgICAgICAiYWNjZXNzTGV2ZWwiIDogIlB1YmxpYyIsCiAgICAgICAgICAgICJ0eXBlIiA6ICJGdW5jdGlvbkRlZiIsCiAgICAgICAgICAgICJhbm5vdGF0aW9uIiA6IFsgewogICAgICAgICAgICAgICAidHlwZSIgOiAiQW5ub3RhdGlvbiIsCiAgICAgICAgICAgICAgICJ0IiA6IFsgewogICAgICAgICAgICAgICAgICAibmFtZSIgOiAiZGVzY3JpcHRpb24iLAogICAgICAgICAgICAgICAgICAidmFsdWUiIDogIlJldHVybnMgdGhlIHNpbmdsZSBiYXNlLUZISVIgZXh0ZW5zaW9uIChpZiBwcmVzZW50KSBvbiB0aGUgZ2l2ZW4gZWxlbWVudCB3aXRoIHRoZSBzcGVjaWZpZWQgaWQuIgogICAgICAgICAgICAgICB9LCB7CiAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJjb21tZW50IiwKICAgICAgICAgICAgICAgICAgInZhbHVlIiA6ICJUaGlzIGZ1bmN0aW9uIHVzZXMgc2luZ2xldG9uIGZyb20gdG8gZW5zdXJlIHRoYXQgYSBydW4tdGltZSBleGNlcHRpb24gaXMgdGhyb3duIGlmIHRoZXJlXG5pcyBtb3JlIHRoYW4gb25lIGV4dGVuc2lvbiBvbiB0aGUgZ2l2ZW4gcmVzb3VyY2Ugd2l0aCB0aGUgc3BlY2lmaWVkIHVybC4iCiAgICAgICAgICAgICAgIH0gXQogICAgICAgICAgICB9IF0sCiAgICAgICAgICAgICJleHByZXNzaW9uIiA6IHsKICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjI4MDozLTI4MDo0NCIsCiAgICAgICAgICAgICAgICJ0eXBlIiA6ICJTaW5nbGV0b25Gcm9tIiwKICAgICAgICAgICAgICAgIm9wZXJhbmQiIDogewogICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMjgwOjE4LTI4MDo0NCIsCiAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJCYXNlRXh0ZW5zaW9ucyIsCiAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJGdW5jdGlvblJlZiIsCiAgICAgICAgICAgICAgICAgICJvcGVyYW5kIiA6IFsgewogICAgICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMjgwOjMzLTI4MDozOSIsCiAgICAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJlbGVtZW50IiwKICAgICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk9wZXJhbmRSZWYiCiAgICAgICAgICAgICAgICAgIH0sIHsKICAgICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjI4MDo0Mi0yODA6NDMiLAogICAgICAgICAgICAgICAgICAgICAibmFtZSIgOiAiaWQiLAogICAgICAgICAgICAgICAgICAgICAidHlwZSIgOiAiT3BlcmFuZFJlZiIKICAgICAgICAgICAgICAgICAgfSBdCiAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgfSwKICAgICAgICAgICAgIm9wZXJhbmQiIDogWyB7CiAgICAgICAgICAgICAgICJuYW1lIiA6ICJlbGVtZW50IiwKICAgICAgICAgICAgICAgIm9wZXJhbmRUeXBlU3BlY2lmaWVyIiA6IHsKICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjI3OTozOS0yNzk6NDUiLAogICAgICAgICAgICAgICAgICAibmFtZSIgOiAie2h0dHA6Ly9obDcub3JnL2ZoaXJ9RWxlbWVudCIsCiAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJOYW1lZFR5cGVTcGVjaWZpZXIiCiAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgfSwgewogICAgICAgICAgICAgICAibmFtZSIgOiAiaWQiLAogICAgICAgICAgICAgICAib3BlcmFuZFR5cGVTcGVjaWZpZXIiIDogewogICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMjc5OjUxLTI3OTo1NiIsCiAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJ7dXJuOmhsNy1vcmc6ZWxtLXR5cGVzOnIxfVN0cmluZyIsCiAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJOYW1lZFR5cGVTcGVjaWZpZXIiCiAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgfSBdCiAgICAgICAgIH0sIHsKICAgICAgICAgICAgImxvY2F0b3IiIDogIjI4NzoxLTI5MDoxMCIsCiAgICAgICAgICAgICJuYW1lIiA6ICJCYXNlTW9kaWZpZXJFeHRlbnNpb25zIiwKICAgICAgICAgICAgImNvbnRleHQiIDogIlBhdGllbnQiLAogICAgICAgICAgICAiYWNjZXNzTGV2ZWwiIDogIlB1YmxpYyIsCiAgICAgICAgICAgICJ0eXBlIiA6ICJGdW5jdGlvbkRlZiIsCiAgICAgICAgICAgICJhbm5vdGF0aW9uIiA6IFsgewogICAgICAgICAgICAgICAidHlwZSIgOiAiQW5ub3RhdGlvbiIsCiAgICAgICAgICAgICAgICJ0IiA6IFsgewogICAgICAgICAgICAgICAgICAibmFtZSIgOiAiZGVzY3JpcHRpb24iLAogICAgICAgICAgICAgICAgICAidmFsdWUiIDogIlJldHVybnMgYW55IGJhc2UtRkhJUiBtb2RpZmllciBleHRlbnNpb25zIGRlZmluZWQgb24gdGhlIGdpdmVuIHJlc291cmNlIHdpdGggdGhlIHNwZWNpZmllZCBpZC4iCiAgICAgICAgICAgICAgIH0sIHsKICAgICAgICAgICAgICAgICAgIm5hbWUiIDogImNvbW1lbnQiLAogICAgICAgICAgICAgICAgICAidmFsdWUiIDogIk5PVEU6IEV4dGVuc2lvbnMgYXJlIG5vdCB0aGUgcHJlZmVycmVkIGFwcHJvYWNoLCBidXQgYXJlIHVzZWQgYXMgYSB3YXkgdG8gYWNjZXNzXG5jb250ZW50IHRoYXQgaXMgZGVmaW5lZCBieSBleHRlbnNpb25zIGJ1dCBub3QgeWV0IHN1cmZhY2VkIGluIHRoZSBDUUwgbW9kZWwgaW5mby4iCiAgICAgICAgICAgICAgIH0gXQogICAgICAgICAgICB9IF0sCiAgICAgICAgICAgICJleHByZXNzaW9uIiA6IHsKICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjI4ODozLTI5MDoxMCIsCiAgICAgICAgICAgICAgICJ0eXBlIiA6ICJRdWVyeSIsCiAgICAgICAgICAgICAgICJzb3VyY2UiIDogWyB7CiAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIyODg6My0yODg6MzYiLAogICAgICAgICAgICAgICAgICAiYWxpYXMiIDogIkUiLAogICAgICAgICAgICAgICAgICAiZXhwcmVzc2lvbiIgOiB7CiAgICAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIyODg6My0yODg6MzQiLAogICAgICAgICAgICAgICAgICAgICAicGF0aCIgOiAibW9kaWZpZXJFeHRlbnNpb24iLAogICAgICAgICAgICAgICAgICAgICAidHlwZSIgOiAiUHJvcGVydHkiLAogICAgICAgICAgICAgICAgICAgICAic291cmNlIiA6IHsKICAgICAgICAgICAgICAgICAgICAgICAgIm5hbWUiIDogImRvbWFpblJlc291cmNlIiwKICAgICAgICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk9wZXJhbmRSZWYiCiAgICAgICAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgICAgICAgfQogICAgICAgICAgICAgICB9IF0sCiAgICAgICAgICAgICAgICJyZWxhdGlvbnNoaXAiIDogWyBdLAogICAgICAgICAgICAgICAid2hlcmUiIDogewogICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMjg5OjQtMjg5OjY2IiwKICAgICAgICAgICAgICAgICAgInR5cGUiIDogIkVxdWFsIiwKICAgICAgICAgICAgICAgICAgIm9wZXJhbmQiIDogWyB7CiAgICAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJUb1N0cmluZyIsCiAgICAgICAgICAgICAgICAgICAgICJsaWJyYXJ5TmFtZSIgOiAiRkhJUkhlbHBlcnMiLAogICAgICAgICAgICAgICAgICAgICAidHlwZSIgOiAiRnVuY3Rpb25SZWYiLAogICAgICAgICAgICAgICAgICAgICAib3BlcmFuZCIgOiBbIHsKICAgICAgICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjI4OToxMC0yODk6MTQiLAogICAgICAgICAgICAgICAgICAgICAgICAicGF0aCIgOiAidXJsIiwKICAgICAgICAgICAgICAgICAgICAgICAgInNjb3BlIiA6ICJFIiwKICAgICAgICAgICAgICAgICAgICAgICAgInR5cGUiIDogIlByb3BlcnR5IgogICAgICAgICAgICAgICAgICAgICB9IF0KICAgICAgICAgICAgICAgICAgfSwgewogICAgICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMjg5OjE4LTI4OTo2NiIsCiAgICAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJDb25jYXRlbmF0ZSIsCiAgICAgICAgICAgICAgICAgICAgICJvcGVyYW5kIiA6IFsgewogICAgICAgICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMjg5OjE5LTI4OTo2MCIsCiAgICAgICAgICAgICAgICAgICAgICAgICJ2YWx1ZVR5cGUiIDogInt1cm46aGw3LW9yZzplbG0tdHlwZXM6cjF9U3RyaW5nIiwKICAgICAgICAgICAgICAgICAgICAgICAgInZhbHVlIiA6ICJodHRwOi8vaGw3Lm9yZy9maGlyL1N0cnVjdHVyZURlZmluaXRpb24vIiwKICAgICAgICAgICAgICAgICAgICAgICAgInR5cGUiIDogIkxpdGVyYWwiCiAgICAgICAgICAgICAgICAgICAgIH0sIHsKICAgICAgICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjI4OTo2NC0yODk6NjUiLAogICAgICAgICAgICAgICAgICAgICAgICAibmFtZSIgOiAiaWQiLAogICAgICAgICAgICAgICAgICAgICAgICAidHlwZSIgOiAiT3BlcmFuZFJlZiIKICAgICAgICAgICAgICAgICAgICAgfSBdCiAgICAgICAgICAgICAgICAgIH0gXQogICAgICAgICAgICAgICB9LAogICAgICAgICAgICAgICAicmV0dXJuIiA6IHsKICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjI5MDozLTI5MDoxMCIsCiAgICAgICAgICAgICAgICAgICJleHByZXNzaW9uIiA6IHsKICAgICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjI5MDoxMCIsCiAgICAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJFIiwKICAgICAgICAgICAgICAgICAgICAgInR5cGUiIDogIkFsaWFzUmVmIgogICAgICAgICAgICAgICAgICB9CiAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgfSwKICAgICAgICAgICAgIm9wZXJhbmQiIDogWyB7CiAgICAgICAgICAgICAgICJuYW1lIiA6ICJkb21haW5SZXNvdXJjZSIsCiAgICAgICAgICAgICAgICJvcGVyYW5kVHlwZVNwZWNpZmllciIgOiB7CiAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIyODc6NTUtMjg3OjY4IiwKICAgICAgICAgICAgICAgICAgIm5hbWUiIDogIntodHRwOi8vaGw3Lm9yZy9maGlyfURvbWFpblJlc291cmNlIiwKICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk5hbWVkVHlwZVNwZWNpZmllciIKICAgICAgICAgICAgICAgfQogICAgICAgICAgICB9LCB7CiAgICAgICAgICAgICAgICJuYW1lIiA6ICJpZCIsCiAgICAgICAgICAgICAgICJvcGVyYW5kVHlwZVNwZWNpZmllciIgOiB7CiAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIyODc6NzQtMjg3Ojc5IiwKICAgICAgICAgICAgICAgICAgIm5hbWUiIDogInt1cm46aGw3LW9yZzplbG0tdHlwZXM6cjF9U3RyaW5nIiwKICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk5hbWVkVHlwZVNwZWNpZmllciIKICAgICAgICAgICAgICAgfQogICAgICAgICAgICB9IF0KICAgICAgICAgfSwgewogICAgICAgICAgICAibG9jYXRvciIgOiAiMjk3OjEtMjk4OjU5IiwKICAgICAgICAgICAgIm5hbWUiIDogIkJhc2VNb2RpZmllckV4dGVuc2lvbiIsCiAgICAgICAgICAgICJjb250ZXh0IiA6ICJQYXRpZW50IiwKICAgICAgICAgICAgImFjY2Vzc0xldmVsIiA6ICJQdWJsaWMiLAogICAgICAgICAgICAidHlwZSIgOiAiRnVuY3Rpb25EZWYiLAogICAgICAgICAgICAiYW5ub3RhdGlvbiIgOiBbIHsKICAgICAgICAgICAgICAgInR5cGUiIDogIkFubm90YXRpb24iLAogICAgICAgICAgICAgICAidCIgOiBbIHsKICAgICAgICAgICAgICAgICAgIm5hbWUiIDogImRlc2NyaXB0aW9uIiwKICAgICAgICAgICAgICAgICAgInZhbHVlIiA6ICJSZXR1cm5zIHRoZSBzaW5nbGUgYmFzZS1GSElSIG1vZGlmaWVyIGV4dGVuc2lvbiAoaWYgcHJlc2VudCkgb24gdGhlIGdpdmVuIHJlc291cmNlIHdpdGggdGhlIHNwZWNpZmllZCBpZC4iCiAgICAgICAgICAgICAgIH0sIHsKICAgICAgICAgICAgICAgICAgIm5hbWUiIDogImNvbW1lbnQiLAogICAgICAgICAgICAgICAgICAidmFsdWUiIDogIlRoaXMgZnVuY3Rpb24gdXNlcyBzaW5nbGV0b24gZnJvbSB0byBlbnN1cmUgdGhhdCBhIHJ1bi10aW1lIGV4Y2VwdGlvbiBpcyB0aHJvd24gaWYgdGhlcmVcbmlzIG1vcmUgdGhhbiBvbmUgZXh0ZW5zaW9uIG9uIHRoZSBnaXZlbiByZXNvdXJjZSB3aXRoIHRoZSBzcGVjaWZpZWQgdXJsLiIKICAgICAgICAgICAgICAgfSBdCiAgICAgICAgICAgIH0gXSwKICAgICAgICAgICAgImV4cHJlc3Npb24iIDogewogICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMjk4OjMtMjk4OjU5IiwKICAgICAgICAgICAgICAgInR5cGUiIDogIlNpbmdsZXRvbkZyb20iLAogICAgICAgICAgICAgICAib3BlcmFuZCIgOiB7CiAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIyOTg6MTgtMjk4OjU5IiwKICAgICAgICAgICAgICAgICAgIm5hbWUiIDogIkJhc2VNb2RpZmllckV4dGVuc2lvbnMiLAogICAgICAgICAgICAgICAgICAidHlwZSIgOiAiRnVuY3Rpb25SZWYiLAogICAgICAgICAgICAgICAgICAib3BlcmFuZCIgOiBbIHsKICAgICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjI5ODo0MS0yOTg6NTQiLAogICAgICAgICAgICAgICAgICAgICAibmFtZSIgOiAiZG9tYWluUmVzb3VyY2UiLAogICAgICAgICAgICAgICAgICAgICAidHlwZSIgOiAiT3BlcmFuZFJlZiIKICAgICAgICAgICAgICAgICAgfSwgewogICAgICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMjk4OjU3LTI5ODo1OCIsCiAgICAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJpZCIsCiAgICAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJPcGVyYW5kUmVmIgogICAgICAgICAgICAgICAgICB9IF0KICAgICAgICAgICAgICAgfQogICAgICAgICAgICB9LAogICAgICAgICAgICAib3BlcmFuZCIgOiBbIHsKICAgICAgICAgICAgICAgIm5hbWUiIDogImRvbWFpblJlc291cmNlIiwKICAgICAgICAgICAgICAgIm9wZXJhbmRUeXBlU3BlY2lmaWVyIiA6IHsKICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjI5Nzo1NC0yOTc6NjciLAogICAgICAgICAgICAgICAgICAibmFtZSIgOiAie2h0dHA6Ly9obDcub3JnL2ZoaXJ9RG9tYWluUmVzb3VyY2UiLAogICAgICAgICAgICAgICAgICAidHlwZSIgOiAiTmFtZWRUeXBlU3BlY2lmaWVyIgogICAgICAgICAgICAgICB9CiAgICAgICAgICAgIH0sIHsKICAgICAgICAgICAgICAgIm5hbWUiIDogImlkIiwKICAgICAgICAgICAgICAgIm9wZXJhbmRUeXBlU3BlY2lmaWVyIiA6IHsKICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjI5Nzo3My0yOTc6NzgiLAogICAgICAgICAgICAgICAgICAibmFtZSIgOiAie3VybjpobDctb3JnOmVsbS10eXBlczpyMX1TdHJpbmciLAogICAgICAgICAgICAgICAgICAidHlwZSIgOiAiTmFtZWRUeXBlU3BlY2lmaWVyIgogICAgICAgICAgICAgICB9CiAgICAgICAgICAgIH0gXQogICAgICAgICB9LCB7CiAgICAgICAgICAgICJsb2NhdG9yIiA6ICIzMDU6MS0zMDg6MTAiLAogICAgICAgICAgICAibmFtZSIgOiAiQmFzZU1vZGlmaWVyRXh0ZW5zaW9ucyIsCiAgICAgICAgICAgICJjb250ZXh0IiA6ICJQYXRpZW50IiwKICAgICAgICAgICAgImFjY2Vzc0xldmVsIiA6ICJQdWJsaWMiLAogICAgICAgICAgICAidHlwZSIgOiAiRnVuY3Rpb25EZWYiLAogICAgICAgICAgICAiYW5ub3RhdGlvbiIgOiBbIHsKICAgICAgICAgICAgICAgInR5cGUiIDogIkFubm90YXRpb24iLAogICAgICAgICAgICAgICAidCIgOiBbIHsKICAgICAgICAgICAgICAgICAgIm5hbWUiIDogImRlc2NyaXB0aW9uIiwKICAgICAgICAgICAgICAgICAgInZhbHVlIiA6ICJSZXR1cm5zIGFueSBiYXNlLUZISVIgbW9kaWZpZXIgZXh0ZW5zaW9ucyBkZWZpbmVkIG9uIHRoZSBnaXZlbiBlbGVtZW50IHdpdGggdGhlIHNwZWNpZmllZCBpZC4iCiAgICAgICAgICAgICAgIH0sIHsKICAgICAgICAgICAgICAgICAgIm5hbWUiIDogImNvbW1lbnQiLAogICAgICAgICAgICAgICAgICAidmFsdWUiIDogIk5PVEU6IEV4dGVuc2lvbnMgYXJlIG5vdCB0aGUgcHJlZmVycmVkIGFwcHJvYWNoLCBidXQgYXJlIHVzZWQgYXMgYSB3YXkgdG8gYWNjZXNzXG5jb250ZW50IHRoYXQgaXMgZGVmaW5lZCBieSBleHRlbnNpb25zIGJ1dCBub3QgeWV0IHN1cmZhY2VkIGluIHRoZSBDUUwgbW9kZWwgaW5mby4iCiAgICAgICAgICAgICAgIH0gXQogICAgICAgICAgICB9IF0sCiAgICAgICAgICAgICJleHByZXNzaW9uIiA6IHsKICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjMwNjozLTMwODoxMCIsCiAgICAgICAgICAgICAgICJ0eXBlIiA6ICJRdWVyeSIsCiAgICAgICAgICAgICAgICJzb3VyY2UiIDogWyB7CiAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIzMDY6My0zMDY6MjkiLAogICAgICAgICAgICAgICAgICAiYWxpYXMiIDogIkUiLAogICAgICAgICAgICAgICAgICAiZXhwcmVzc2lvbiIgOiB7CiAgICAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIzMDY6My0zMDY6MjciLAogICAgICAgICAgICAgICAgICAgICAicGF0aCIgOiAibW9kaWZpZXJFeHRlbnNpb24iLAogICAgICAgICAgICAgICAgICAgICAidHlwZSIgOiAiUHJvcGVydHkiLAogICAgICAgICAgICAgICAgICAgICAic291cmNlIiA6IHsKICAgICAgICAgICAgICAgICAgICAgICAgIm5hbWUiIDogImVsZW1lbnQiLAogICAgICAgICAgICAgICAgICAgICAgICAidHlwZSIgOiAiT3BlcmFuZFJlZiIKICAgICAgICAgICAgICAgICAgICAgfQogICAgICAgICAgICAgICAgICB9CiAgICAgICAgICAgICAgIH0gXSwKICAgICAgICAgICAgICAgInJlbGF0aW9uc2hpcCIgOiBbIF0sCiAgICAgICAgICAgICAgICJ3aGVyZSIgOiB7CiAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIzMDc6NC0zMDc6NjYiLAogICAgICAgICAgICAgICAgICAidHlwZSIgOiAiRXF1YWwiLAogICAgICAgICAgICAgICAgICAib3BlcmFuZCIgOiBbIHsKICAgICAgICAgICAgICAgICAgICAgIm5hbWUiIDogIlRvU3RyaW5nIiwKICAgICAgICAgICAgICAgICAgICAgImxpYnJhcnlOYW1lIiA6ICJGSElSSGVscGVycyIsCiAgICAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJGdW5jdGlvblJlZiIsCiAgICAgICAgICAgICAgICAgICAgICJvcGVyYW5kIiA6IFsgewogICAgICAgICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMzA3OjEwLTMwNzoxNCIsCiAgICAgICAgICAgICAgICAgICAgICAgICJwYXRoIiA6ICJ1cmwiLAogICAgICAgICAgICAgICAgICAgICAgICAic2NvcGUiIDogIkUiLAogICAgICAgICAgICAgICAgICAgICAgICAidHlwZSIgOiAiUHJvcGVydHkiCiAgICAgICAgICAgICAgICAgICAgIH0gXQogICAgICAgICAgICAgICAgICB9LCB7CiAgICAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIzMDc6MTgtMzA3OjY2IiwKICAgICAgICAgICAgICAgICAgICAgInR5cGUiIDogIkNvbmNhdGVuYXRlIiwKICAgICAgICAgICAgICAgICAgICAgIm9wZXJhbmQiIDogWyB7CiAgICAgICAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIzMDc6MTktMzA3OjYwIiwKICAgICAgICAgICAgICAgICAgICAgICAgInZhbHVlVHlwZSIgOiAie3VybjpobDctb3JnOmVsbS10eXBlczpyMX1TdHJpbmciLAogICAgICAgICAgICAgICAgICAgICAgICAidmFsdWUiIDogImh0dHA6Ly9obDcub3JnL2ZoaXIvU3RydWN0dXJlRGVmaW5pdGlvbi8iLAogICAgICAgICAgICAgICAgICAgICAgICAidHlwZSIgOiAiTGl0ZXJhbCIKICAgICAgICAgICAgICAgICAgICAgfSwgewogICAgICAgICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMzA3OjY0LTMwNzo2NSIsCiAgICAgICAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJpZCIsCiAgICAgICAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJPcGVyYW5kUmVmIgogICAgICAgICAgICAgICAgICAgICB9IF0KICAgICAgICAgICAgICAgICAgfSBdCiAgICAgICAgICAgICAgIH0sCiAgICAgICAgICAgICAgICJyZXR1cm4iIDogewogICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMzA4OjMtMzA4OjEwIiwKICAgICAgICAgICAgICAgICAgImV4cHJlc3Npb24iIDogewogICAgICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMzA4OjEwIiwKICAgICAgICAgICAgICAgICAgICAgIm5hbWUiIDogIkUiLAogICAgICAgICAgICAgICAgICAgICAidHlwZSIgOiAiQWxpYXNSZWYiCiAgICAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgICAgfQogICAgICAgICAgICB9LAogICAgICAgICAgICAib3BlcmFuZCIgOiBbIHsKICAgICAgICAgICAgICAgIm5hbWUiIDogImVsZW1lbnQiLAogICAgICAgICAgICAgICAib3BlcmFuZFR5cGVTcGVjaWZpZXIiIDogewogICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMzA1OjQ4LTMwNTo2MiIsCiAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJ7aHR0cDovL2hsNy5vcmcvZmhpcn1CYWNrYm9uZUVsZW1lbnQiLAogICAgICAgICAgICAgICAgICAidHlwZSIgOiAiTmFtZWRUeXBlU3BlY2lmaWVyIgogICAgICAgICAgICAgICB9CiAgICAgICAgICAgIH0sIHsKICAgICAgICAgICAgICAgIm5hbWUiIDogImlkIiwKICAgICAgICAgICAgICAgIm9wZXJhbmRUeXBlU3BlY2lmaWVyIiA6IHsKICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjMwNTo2OC0zMDU6NzMiLAogICAgICAgICAgICAgICAgICAibmFtZSIgOiAie3VybjpobDctb3JnOmVsbS10eXBlczpyMX1TdHJpbmciLAogICAgICAgICAgICAgICAgICAidHlwZSIgOiAiTmFtZWRUeXBlU3BlY2lmaWVyIgogICAgICAgICAgICAgICB9CiAgICAgICAgICAgIH0gXQogICAgICAgICB9LCB7CiAgICAgICAgICAgICJsb2NhdG9yIiA6ICIzMTU6MS0zMTY6NTIiLAogICAgICAgICAgICAibmFtZSIgOiAiQmFzZU1vZGlmaWVyRXh0ZW5zaW9uIiwKICAgICAgICAgICAgImNvbnRleHQiIDogIlBhdGllbnQiLAogICAgICAgICAgICAiYWNjZXNzTGV2ZWwiIDogIlB1YmxpYyIsCiAgICAgICAgICAgICJ0eXBlIiA6ICJGdW5jdGlvbkRlZiIsCiAgICAgICAgICAgICJhbm5vdGF0aW9uIiA6IFsgewogICAgICAgICAgICAgICAidHlwZSIgOiAiQW5ub3RhdGlvbiIsCiAgICAgICAgICAgICAgICJ0IiA6IFsgewogICAgICAgICAgICAgICAgICAibmFtZSIgOiAiZGVzY3JpcHRpb24iLAogICAgICAgICAgICAgICAgICAidmFsdWUiIDogIlJldHVybnMgdGhlIHNpbmdsZSBiYXNlLUZISVIgZXh0ZW5zaW9uIChpZiBwcmVzZW50KSBvbiB0aGUgZ2l2ZW4gZWxlbWVudCB3aXRoIHRoZSBzcGVjaWZpZWQgaWQuIgogICAgICAgICAgICAgICB9LCB7CiAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJjb21tZW50IiwKICAgICAgICAgICAgICAgICAgInZhbHVlIiA6ICJUaGlzIGZ1bmN0aW9uIHVzZXMgc2luZ2xldG9uIGZyb20gdG8gZW5zdXJlIHRoYXQgYSBydW4tdGltZSBleGNlcHRpb24gaXMgdGhyb3duIGlmIHRoZXJlXG5pcyBtb3JlIHRoYW4gb25lIGV4dGVuc2lvbiBvbiB0aGUgZ2l2ZW4gcmVzb3VyY2Ugd2l0aCB0aGUgc3BlY2lmaWVkIHVybC4iCiAgICAgICAgICAgICAgIH0gXQogICAgICAgICAgICB9IF0sCiAgICAgICAgICAgICJleHByZXNzaW9uIiA6IHsKICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjMxNjozLTMxNjo1MiIsCiAgICAgICAgICAgICAgICJ0eXBlIiA6ICJTaW5nbGV0b25Gcm9tIiwKICAgICAgICAgICAgICAgIm9wZXJhbmQiIDogewogICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMzE2OjE4LTMxNjo1MiIsCiAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJCYXNlTW9kaWZpZXJFeHRlbnNpb25zIiwKICAgICAgICAgICAgICAgICAgInR5cGUiIDogIkZ1bmN0aW9uUmVmIiwKICAgICAgICAgICAgICAgICAgIm9wZXJhbmQiIDogWyB7CiAgICAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIzMTY6NDEtMzE2OjQ3IiwKICAgICAgICAgICAgICAgICAgICAgIm5hbWUiIDogImVsZW1lbnQiLAogICAgICAgICAgICAgICAgICAgICAidHlwZSIgOiAiT3BlcmFuZFJlZiIKICAgICAgICAgICAgICAgICAgfSwgewogICAgICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMzE2OjUwLTMxNjo1MSIsCiAgICAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJpZCIsCiAgICAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJPcGVyYW5kUmVmIgogICAgICAgICAgICAgICAgICB9IF0KICAgICAgICAgICAgICAgfQogICAgICAgICAgICB9LAogICAgICAgICAgICAib3BlcmFuZCIgOiBbIHsKICAgICAgICAgICAgICAgIm5hbWUiIDogImVsZW1lbnQiLAogICAgICAgICAgICAgICAib3BlcmFuZFR5cGVTcGVjaWZpZXIiIDogewogICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMzE1OjQ3LTMxNTo2MSIsCiAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJ7aHR0cDovL2hsNy5vcmcvZmhpcn1CYWNrYm9uZUVsZW1lbnQiLAogICAgICAgICAgICAgICAgICAidHlwZSIgOiAiTmFtZWRUeXBlU3BlY2lmaWVyIgogICAgICAgICAgICAgICB9CiAgICAgICAgICAgIH0sIHsKICAgICAgICAgICAgICAgIm5hbWUiIDogImlkIiwKICAgICAgICAgICAgICAgIm9wZXJhbmRUeXBlU3BlY2lmaWVyIiA6IHsKICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjMxNTo2Ny0zMTU6NzIiLAogICAgICAgICAgICAgICAgICAibmFtZSIgOiAie3VybjpobDctb3JnOmVsbS10eXBlczpyMX1TdHJpbmciLAogICAgICAgICAgICAgICAgICAidHlwZSIgOiAiTmFtZWRUeXBlU3BlY2lmaWVyIgogICAgICAgICAgICAgICB9CiAgICAgICAgICAgIH0gXQogICAgICAgICB9IF0KICAgICAgfQogICB9Cn0=" - } - ] - }, - "request": { - "method": "PUT", - "url": "Library/FHIRCommon" - } - }, - { - "resource": { - "resourceType": "Patient", - "id": "CMSTest-patient-1", - "meta": { - "profile": [ - "http://hl7.org/fhir/us/core/StructureDefinition/us-core-patient" - ] - }, - "extension": [ - { - "url": "http://hl7.org/fhir/us/core/StructureDefinition/us-core-race", - "extension": [ - { - "url": "ombCategory", - "valueCoding": { - "system": "urn:oid:2.16.840.1.113883.6.238", - "code": "2028-9", - "display": "Asian" - } - } - ] - }, - { - "url": "http://hl7.org/fhir/us/core/StructureDefinition/us-core-ethnicity", - "extension": [ - { - "url": "ombCategory", - "valueCoding": { - "system": "urn:oid:2.16.840.1.113883.6.238", - "code": "2135-2", - "display": "Hispanic or Latino" - } - } - ] - } - ], - "identifier": [ - { - "use": "usual", - "type": { - "coding": [ - { - "system": "http://terminology.hl7.org/CodeSystem/v2-0203", - "code": "MR", - "display": "Medical Record Number" - } - ] - }, - "system": "http://hospital.smarthealthit.org", - "value": "999999992" - } - ], - "name": [ - { - "family": "Dere", - "given": [ - "Ben" - ] - } - ], - "gender": "male", - "birthDate": "1965-01-01" - }, - "request": { - "method": "PUT", - "url": "Patient/CMSTest-patient-1" - } - } - ] + "resourceType": "Bundle", + "id": "CMSTest-bundle", + "type": "transaction", + "entry": [ + { + "resource": { + "resourceType": "ValueSet", + "id": "2.16.840.1.114222.4.11.837", + "url": "http://cts.nlm.nih.gov/fhir/ValueSet/2.16.840.1.114222.4.11.837", + "identifier": [ + { + "system": "urn:ietf:rfc:3986", + "value": "2.16.840.1.114222.4.11.837" + } + ], + "version": "20121025", + "name": "Ethnicity", + "title": "Ethnicity", + "status": "active", + "experimental": false, + "publisher": "NLM", + "description": "Codes representing possible values for Ethnicity.", + "expansion": { + "identifier": "20210506", + "timestamp": "2021-08-19T13:27:33-06:00", + "contains": [ + { + "system": "http://terminology.hl7.org/CodeSystem/PHRaceAndEthnicityCDC", + "version": "1.2", + "code": "2135-2", + "display": "Hispanic or Latino" + }, + { + "system": "http://terminology.hl7.org/CodeSystem/PHRaceAndEthnicityCDC", + "version": "1.2", + "code": "2186-5", + "display": "Not Hispanic or Latino" + } + ] + } + }, + "request": { + "method": "PUT", + "url": "ValueSet/2.16.840.1.114222.4.11.837" + } + }, + { + "resource": { + "resourceType": "ValueSet", + "id": "2.16.840.1.114222.4.11.3591", + "url": "http://cts.nlm.nih.gov/fhir/ValueSet/2.16.840.1.114222.4.11.3591", + "identifier": [ + { + "system": "urn:ietf:rfc:3986", + "value": "2.16.840.1.114222.4.11.3591" + } + ], + "version": "20180718", + "name": "Payer", + "title": "Payer", + "status": "active", + "experimental": false, + "publisher": "NLM", + "description": "Codes representing possible values for Payer.", + "expansion": { + "identifier": "20210506", + "timestamp": "2021-08-19T13:27:33-06:00", + "contains": [ + { + "system": "https://nahdo.org/sopt", + "version": "9.2", + "code": "1", + "display": "MEDICARE" + }, + { + "system": "https://nahdo.org/sopt", + "version": "9.2", + "code": "11", + "display": "Medicare Managed Care (Includes Medicare Advantage Plans)" + }, + { + "system": "https://nahdo.org/sopt", + "version": "9.2", + "code": "111", + "display": "Medicare HMO" + }, + { + "system": "https://nahdo.org/sopt", + "version": "9.2", + "code": "112", + "display": "Medicare PPO" + }, + { + "system": "https://nahdo.org/sopt", + "version": "9.2", + "code": "113", + "display": "Medicare POS" + }, + { + "system": "https://nahdo.org/sopt", + "version": "9.2", + "code": "119", + "display": "Medicare Managed Care Other" + }, + { + "system": "https://nahdo.org/sopt", + "version": "9.2", + "code": "12", + "display": "Medicare (Non-managed Care)" + }, + { + "system": "https://nahdo.org/sopt", + "version": "9.2", + "code": "121", + "display": "Medicare FFS" + }, + { + "system": "https://nahdo.org/sopt", + "version": "9.2", + "code": "122", + "display": "Medicare Drug Benefit" + }, + { + "system": "https://nahdo.org/sopt", + "version": "9.2", + "code": "123", + "display": "Medicare Medical Savings Account (MSA)" + }, + { + "system": "https://nahdo.org/sopt", + "version": "9.2", + "code": "129", + "display": "Medicare Non-managed Care Other" + }, + { + "system": "https://nahdo.org/sopt", + "version": "9.2", + "code": "13", + "display": "Medicare Hospice" + }, + { + "system": "https://nahdo.org/sopt", + "version": "9.2", + "code": "14", + "display": "Dual Eligibility Medicare/Medicaid Organization" + }, + { + "system": "https://nahdo.org/sopt", + "version": "9.2", + "code": "19", + "display": "Medicare Other" + }, + { + "system": "https://nahdo.org/sopt", + "version": "9.2", + "code": "191", + "display": "Medicare Pharmacy Benefit Manager" + }, + { + "system": "https://nahdo.org/sopt", + "version": "9.2", + "code": "2", + "display": "MEDICAID" + }, + { + "system": "https://nahdo.org/sopt", + "version": "9.2", + "code": "21", + "display": "Medicaid (Managed Care)" + }, + { + "system": "https://nahdo.org/sopt", + "version": "9.2", + "code": "211", + "display": "Medicaid HMO" + }, + { + "system": "https://nahdo.org/sopt", + "version": "9.2", + "code": "212", + "display": "Medicaid PPO" + }, + { + "system": "https://nahdo.org/sopt", + "version": "9.2", + "code": "213", + "display": "Medicaid PCCM (Primary Care Case Management)" + }, + { + "system": "https://nahdo.org/sopt", + "version": "9.2", + "code": "219", + "display": "Medicaid Managed Care Other" + }, + { + "system": "https://nahdo.org/sopt", + "version": "9.2", + "code": "22", + "display": "Medicaid (Non-managed Care Plan)" + }, + { + "system": "https://nahdo.org/sopt", + "version": "9.2", + "code": "23", + "display": "Medicaid/SCHIP" + }, + { + "system": "https://nahdo.org/sopt", + "version": "9.2", + "code": "25", + "display": "Medicaid - Out of State" + }, + { + "system": "https://nahdo.org/sopt", + "version": "9.2", + "code": "26", + "display": "Medicaid - Long Term Care" + }, + { + "system": "https://nahdo.org/sopt", + "version": "9.2", + "code": "29", + "display": "Medicaid Other" + }, + { + "system": "https://nahdo.org/sopt", + "version": "9.2", + "code": "291", + "display": "Medicaid Pharmacy Benefit Manager" + }, + { + "system": "https://nahdo.org/sopt", + "version": "9.2", + "code": "299", + "display": "Medicaid - Dental" + }, + { + "system": "https://nahdo.org/sopt", + "version": "9.2", + "code": "3", + "display": "OTHER GOVERNMENT (Federal/State/Local) (excluding Department of Corrections)" + }, + { + "system": "https://nahdo.org/sopt", + "version": "9.2", + "code": "31", + "display": "Department of Defense" + }, + { + "system": "https://nahdo.org/sopt", + "version": "9.2", + "code": "311", + "display": "TRICARE (CHAMPUS)" + }, + { + "system": "https://nahdo.org/sopt", + "version": "9.2", + "code": "3111", + "display": "TRICARE Prime--HMO" + }, + { + "system": "https://nahdo.org/sopt", + "version": "9.2", + "code": "3112", + "display": "TRICARE Extra--PPO" + }, + { + "system": "https://nahdo.org/sopt", + "version": "9.2", + "code": "3113", + "display": "TRICARE Standard - Fee For Service" + }, + { + "system": "https://nahdo.org/sopt", + "version": "9.2", + "code": "3114", + "display": "TRICARE For Life--Medicare Supplement" + }, + { + "system": "https://nahdo.org/sopt", + "version": "9.2", + "code": "3115", + "display": "TRICARE Reserve Select" + }, + { + "system": "https://nahdo.org/sopt", + "version": "9.2", + "code": "3116", + "display": "Uniformed Services Family Health Plan (USFHP) -- HMO" + }, + { + "system": "https://nahdo.org/sopt", + "version": "9.2", + "code": "3119", + "display": "Department of Defense - (other)" + }, + { + "system": "https://nahdo.org/sopt", + "version": "9.2", + "code": "312", + "display": "Military Treatment Facility" + }, + { + "system": "https://nahdo.org/sopt", + "version": "9.2", + "code": "3121", + "display": "Enrolled Prime--HMO" + }, + { + "system": "https://nahdo.org/sopt", + "version": "9.2", + "code": "3122", + "display": "Non-enrolled Space Available" + }, + { + "system": "https://nahdo.org/sopt", + "version": "9.2", + "code": "3123", + "display": "TRICARE For Life (TFL)" + }, + { + "system": "https://nahdo.org/sopt", + "version": "9.2", + "code": "313", + "display": "Dental --Stand Alone" + }, + { + "system": "https://nahdo.org/sopt", + "version": "9.2", + "code": "32", + "display": "Department of Veterans Affairs" + }, + { + "system": "https://nahdo.org/sopt", + "version": "9.2", + "code": "321", + "display": "Veteran care-Care provided to Veterans" + }, + { + "system": "https://nahdo.org/sopt", + "version": "9.2", + "code": "3211", + "display": "Direct Care-Care provided in VA facilities" + }, + { + "system": "https://nahdo.org/sopt", + "version": "9.2", + "code": "3212", + "display": "Indirect Care-Care provided outside VA facilities" + }, + { + "system": "https://nahdo.org/sopt", + "version": "9.2", + "code": "32121", + "display": "Fee Basis" + }, + { + "system": "https://nahdo.org/sopt", + "version": "9.2", + "code": "32122", + "display": "Foreign Fee/Foreign Medical Program (FMP)" + }, + { + "system": "https://nahdo.org/sopt", + "version": "9.2", + "code": "32123", + "display": "Contract Nursing Home/Community Nursing Home" + }, + { + "system": "https://nahdo.org/sopt", + "version": "9.2", + "code": "32124", + "display": "State Veterans Home" + }, + { + "system": "https://nahdo.org/sopt", + "version": "9.2", + "code": "32125", + "display": "Sharing Agreements" + }, + { + "system": "https://nahdo.org/sopt", + "version": "9.2", + "code": "32126", + "display": "Other Federal Agency" + }, + { + "system": "https://nahdo.org/sopt", + "version": "9.2", + "code": "32127", + "display": "Dental Care" + }, + { + "system": "https://nahdo.org/sopt", + "version": "9.2", + "code": "32128", + "display": "Vision Care" + }, + { + "system": "https://nahdo.org/sopt", + "version": "9.2", + "code": "322", + "display": "Non-veteran care" + }, + { + "system": "https://nahdo.org/sopt", + "version": "9.2", + "code": "3221", + "display": "Civilian Health and Medical Program for the VA (CHAMPVA)" + }, + { + "system": "https://nahdo.org/sopt", + "version": "9.2", + "code": "3222", + "display": "Spina Bifida Health Care Program (SB)" + }, + { + "system": "https://nahdo.org/sopt", + "version": "9.2", + "code": "3223", + "display": "Children of Women Vietnam Veterans (CWVV)" + }, + { + "system": "https://nahdo.org/sopt", + "version": "9.2", + "code": "3229", + "display": "Other non-veteran care" + }, + { + "system": "https://nahdo.org/sopt", + "version": "9.2", + "code": "33", + "display": "Indian Health Service or Tribe" + }, + { + "system": "https://nahdo.org/sopt", + "version": "9.2", + "code": "331", + "display": "Indian Health Service - Regular" + }, + { + "system": "https://nahdo.org/sopt", + "version": "9.2", + "code": "332", + "display": "Indian Health Service - Contract" + }, + { + "system": "https://nahdo.org/sopt", + "version": "9.2", + "code": "333", + "display": "Indian Health Service - Managed Care" + }, + { + "system": "https://nahdo.org/sopt", + "version": "9.2", + "code": "334", + "display": "Indian Tribe - Sponsored Coverage" + }, + { + "system": "https://nahdo.org/sopt", + "version": "9.2", + "code": "34", + "display": "HRSA Program" + }, + { + "system": "https://nahdo.org/sopt", + "version": "9.2", + "code": "341", + "display": "Title V (MCH Block Grant)" + }, + { + "system": "https://nahdo.org/sopt", + "version": "9.2", + "code": "342", + "display": "Migrant Health Program" + }, + { + "system": "https://nahdo.org/sopt", + "version": "9.2", + "code": "343", + "display": "Ryan White Act" + }, + { + "system": "https://nahdo.org/sopt", + "version": "9.2", + "code": "349", + "display": "Other" + }, + { + "system": "https://nahdo.org/sopt", + "version": "9.2", + "code": "35", + "display": "Black Lung" + }, + { + "system": "https://nahdo.org/sopt", + "version": "9.2", + "code": "36", + "display": "State Government" + }, + { + "system": "https://nahdo.org/sopt", + "version": "9.2", + "code": "361", + "display": "State SCHIP program (codes for individual states)" + }, + { + "system": "https://nahdo.org/sopt", + "version": "9.2", + "code": "362", + "display": "Specific state programs (list/ local code)" + }, + { + "system": "https://nahdo.org/sopt", + "version": "9.2", + "code": "369", + "display": "State, not otherwise specified (other state)" + }, + { + "system": "https://nahdo.org/sopt", + "version": "9.2", + "code": "37", + "display": "Local Government" + }, + { + "system": "https://nahdo.org/sopt", + "version": "9.2", + "code": "371", + "display": "Local - Managed care" + }, + { + "system": "https://nahdo.org/sopt", + "version": "9.2", + "code": "3711", + "display": "HMO" + }, + { + "system": "https://nahdo.org/sopt", + "version": "9.2", + "code": "3712", + "display": "PPO" + }, + { + "system": "https://nahdo.org/sopt", + "version": "9.2", + "code": "3713", + "display": "POS" + }, + { + "system": "https://nahdo.org/sopt", + "version": "9.2", + "code": "372", + "display": "FFS/Indemnity" + }, + { + "system": "https://nahdo.org/sopt", + "version": "9.2", + "code": "379", + "display": "Local, not otherwise specified (other local, county)" + }, + { + "system": "https://nahdo.org/sopt", + "version": "9.2", + "code": "38", + "display": "Other Government (Federal, State, Local not specified)" + }, + { + "system": "https://nahdo.org/sopt", + "version": "9.2", + "code": "381", + "display": "Federal, State, Local not specified managed care" + }, + { + "system": "https://nahdo.org/sopt", + "version": "9.2", + "code": "3811", + "display": "Federal, State, Local not specified - HMO" + }, + { + "system": "https://nahdo.org/sopt", + "version": "9.2", + "code": "3812", + "display": "Federal, State, Local not specified - PPO" + }, + { + "system": "https://nahdo.org/sopt", + "version": "9.2", + "code": "3813", + "display": "Federal, State, Local not specified - POS" + }, + { + "system": "https://nahdo.org/sopt", + "version": "9.2", + "code": "3819", + "display": "Federal, State, Local not specified - not specified managed care" + }, + { + "system": "https://nahdo.org/sopt", + "version": "9.2", + "code": "382", + "display": "Federal, State, Local not specified - FFS" + }, + { + "system": "https://nahdo.org/sopt", + "version": "9.2", + "code": "389", + "display": "Federal, State, Local not specified - Other" + }, + { + "system": "https://nahdo.org/sopt", + "version": "9.2", + "code": "39", + "display": "Other Federal" + }, + { + "system": "https://nahdo.org/sopt", + "version": "9.2", + "code": "391", + "display": "Federal Employee Health Plan - Use when known" + }, + { + "system": "https://nahdo.org/sopt", + "version": "9.2", + "code": "4", + "display": "DEPARTMENTS OF CORRECTIONS" + }, + { + "system": "https://nahdo.org/sopt", + "version": "9.2", + "code": "41", + "display": "Corrections Federal" + }, + { + "system": "https://nahdo.org/sopt", + "version": "9.2", + "code": "42", + "display": "Corrections State" + }, + { + "system": "https://nahdo.org/sopt", + "version": "9.2", + "code": "43", + "display": "Corrections Local" + }, + { + "system": "https://nahdo.org/sopt", + "version": "9.2", + "code": "44", + "display": "Corrections Unknown Level" + }, + { + "system": "https://nahdo.org/sopt", + "version": "9.2", + "code": "5", + "display": "PRIVATE HEALTH INSURANCE" + }, + { + "system": "https://nahdo.org/sopt", + "version": "9.2", + "code": "51", + "display": "Managed Care (Private)" + }, + { + "system": "https://nahdo.org/sopt", + "version": "9.2", + "code": "511", + "display": "Commercial Managed Care - HMO" + }, + { + "system": "https://nahdo.org/sopt", + "version": "9.2", + "code": "512", + "display": "Commercial Managed Care - PPO" + }, + { + "system": "https://nahdo.org/sopt", + "version": "9.2", + "code": "513", + "display": "Commercial Managed Care - POS" + }, + { + "system": "https://nahdo.org/sopt", + "version": "9.2", + "code": "514", + "display": "Exclusive Provider Organization" + }, + { + "system": "https://nahdo.org/sopt", + "version": "9.2", + "code": "515", + "display": "Gatekeeper PPO (GPPO)" + }, + { + "system": "https://nahdo.org/sopt", + "version": "9.2", + "code": "516", + "display": "Commercial Managed Care - Pharmacy Benefit Manager" + }, + { + "system": "https://nahdo.org/sopt", + "version": "9.2", + "code": "517", + "display": "Commercial Managed Care - Dental" + }, + { + "system": "https://nahdo.org/sopt", + "version": "9.2", + "code": "519", + "display": "Managed Care, Other (non HMO)" + }, + { + "system": "https://nahdo.org/sopt", + "version": "9.2", + "code": "52", + "display": "Private Health Insurance - Indemnity" + }, + { + "system": "https://nahdo.org/sopt", + "version": "9.2", + "code": "521", + "display": "Commercial Indemnity" + }, + { + "system": "https://nahdo.org/sopt", + "version": "9.2", + "code": "522", + "display": "Self-insured (ERISA) Administrative Services Only (ASO) plan" + }, + { + "system": "https://nahdo.org/sopt", + "version": "9.2", + "code": "523", + "display": "Medicare supplemental policy (as second payer)" + }, + { + "system": "https://nahdo.org/sopt", + "version": "9.2", + "code": "524", + "display": "Indemnity Insurance - Dental" + }, + { + "system": "https://nahdo.org/sopt", + "version": "9.2", + "code": "529", + "display": "Private health insurance--other commercial Indemnity" + }, + { + "system": "https://nahdo.org/sopt", + "version": "9.2", + "code": "53", + "display": "Managed Care (private) or private health insurance (indemnity), not otherwise specified" + }, + { + "system": "https://nahdo.org/sopt", + "version": "9.2", + "code": "54", + "display": "Organized Delivery System" + }, + { + "system": "https://nahdo.org/sopt", + "version": "9.2", + "code": "55", + "display": "Small Employer Purchasing Group" + }, + { + "system": "https://nahdo.org/sopt", + "version": "9.2", + "code": "56", + "display": "Specialized Stand-Alone Plan" + }, + { + "system": "https://nahdo.org/sopt", + "version": "9.2", + "code": "561", + "display": "Dental" + }, + { + "system": "https://nahdo.org/sopt", + "version": "9.2", + "code": "562", + "display": "Vision" + }, + { + "system": "https://nahdo.org/sopt", + "version": "9.2", + "code": "59", + "display": "Other Private Insurance" + }, + { + "system": "https://nahdo.org/sopt", + "version": "9.2", + "code": "6", + "display": "BLUE CROSS/BLUE SHIELD" + }, + { + "system": "https://nahdo.org/sopt", + "version": "9.2", + "code": "61", + "display": "BC Managed Care" + }, + { + "system": "https://nahdo.org/sopt", + "version": "9.2", + "code": "611", + "display": "BC Managed Care - HMO" + }, + { + "system": "https://nahdo.org/sopt", + "version": "9.2", + "code": "612", + "display": "BC Managed Care - PPO" + }, + { + "system": "https://nahdo.org/sopt", + "version": "9.2", + "code": "613", + "display": "BC Managed Care - POS" + }, + { + "system": "https://nahdo.org/sopt", + "version": "9.2", + "code": "614", + "display": "BC Managed Care - Dental" + }, + { + "system": "https://nahdo.org/sopt", + "version": "9.2", + "code": "619", + "display": "BC Managed Care - Other" + }, + { + "system": "https://nahdo.org/sopt", + "version": "9.2", + "code": "62", + "display": "BC Insurance Indemnity" + }, + { + "system": "https://nahdo.org/sopt", + "version": "9.2", + "code": "621", + "display": "BC Indemnity" + }, + { + "system": "https://nahdo.org/sopt", + "version": "9.2", + "code": "622", + "display": "BC Self-insured (ERISA) Administrative Services Only (ASO)Plan" + }, + { + "system": "https://nahdo.org/sopt", + "version": "9.2", + "code": "623", + "display": "BC Medicare Supplemental Plan" + }, + { + "system": "https://nahdo.org/sopt", + "version": "9.2", + "code": "629", + "display": "BC Indemnity - Dental" + }, + { + "system": "https://nahdo.org/sopt", + "version": "9.2", + "code": "7", + "display": "MANAGED CARE, UNSPECIFIED (to be used only if one can't distinguish public from private)" + }, + { + "system": "https://nahdo.org/sopt", + "version": "9.2", + "code": "71", + "display": "HMO" + }, + { + "system": "https://nahdo.org/sopt", + "version": "9.2", + "code": "72", + "display": "PPO" + }, + { + "system": "https://nahdo.org/sopt", + "version": "9.2", + "code": "73", + "display": "POS" + }, + { + "system": "https://nahdo.org/sopt", + "version": "9.2", + "code": "79", + "display": "Other Managed Care" + }, + { + "system": "https://nahdo.org/sopt", + "version": "9.2", + "code": "8", + "display": "NO PAYMENT from an Organization/Agency/Program/Private Payer Listed" + }, + { + "system": "https://nahdo.org/sopt", + "version": "9.2", + "code": "81", + "display": "Self-pay (Includes applicants for insurance and Medicaid applicants)" + }, + { + "system": "https://nahdo.org/sopt", + "version": "9.2", + "code": "82", + "display": "No Charge" + }, + { + "system": "https://nahdo.org/sopt", + "version": "9.2", + "code": "821", + "display": "Charity" + }, + { + "system": "https://nahdo.org/sopt", + "version": "9.2", + "code": "822", + "display": "Professional Courtesy" + }, + { + "system": "https://nahdo.org/sopt", + "version": "9.2", + "code": "823", + "display": "Research/Clinical Trial" + }, + { + "system": "https://nahdo.org/sopt", + "version": "9.2", + "code": "83", + "display": "Refusal to Pay/Bad Debt" + }, + { + "system": "https://nahdo.org/sopt", + "version": "9.2", + "code": "84", + "display": "Hill Burton Free Care" + }, + { + "system": "https://nahdo.org/sopt", + "version": "9.2", + "code": "85", + "display": "Research/Donor" + }, + { + "system": "https://nahdo.org/sopt", + "version": "9.2", + "code": "89", + "display": "No Payment, Other" + }, + { + "system": "https://nahdo.org/sopt", + "version": "9.2", + "code": "9", + "display": "MISCELLANEOUS/OTHER" + }, + { + "system": "https://nahdo.org/sopt", + "version": "9.2", + "code": "91", + "display": "Foreign National" + }, + { + "system": "https://nahdo.org/sopt", + "version": "9.2", + "code": "92", + "display": "Other (Non-government)" + }, + { + "system": "https://nahdo.org/sopt", + "version": "9.2", + "code": "93", + "display": "Disability Insurance" + }, + { + "system": "https://nahdo.org/sopt", + "version": "9.2", + "code": "94", + "display": "Long-term Care Insurance" + }, + { + "system": "https://nahdo.org/sopt", + "version": "9.2", + "code": "95", + "display": "Worker's Compensation" + }, + { + "system": "https://nahdo.org/sopt", + "version": "9.2", + "code": "951", + "display": "Worker's Comp HMO" + }, + { + "system": "https://nahdo.org/sopt", + "version": "9.2", + "code": "953", + "display": "Worker's Comp Fee-for-Service" + }, + { + "system": "https://nahdo.org/sopt", + "version": "9.2", + "code": "954", + "display": "Worker's Comp Other Managed Care" + }, + { + "system": "https://nahdo.org/sopt", + "version": "9.2", + "code": "959", + "display": "Worker's Comp, Other unspecified" + }, + { + "system": "https://nahdo.org/sopt", + "version": "9.2", + "code": "96", + "display": "Auto Insurance (includes no fault)" + }, + { + "system": "https://nahdo.org/sopt", + "version": "9.2", + "code": "97", + "display": "Legal Liability / Liability Insurance" + }, + { + "system": "https://nahdo.org/sopt", + "version": "9.2", + "code": "98", + "display": "Other specified but not otherwise classifiable (includes Hospice - Unspecified plan)" + }, + { + "system": "https://nahdo.org/sopt", + "version": "9.2", + "code": "99", + "display": "No Typology Code available for payment source" + }, + { + "system": "https://nahdo.org/sopt", + "version": "9.2", + "code": "9999", + "display": "Unavailable / No Payer Specified / Blank" + } + ] + } + }, + "request": { + "method": "PUT", + "url": "ValueSet/2.16.840.1.114222.4.11.3591" + } + }, + { + "resource": { + "resourceType": "Condition", + "id": "CMSTest-patient-1-condition-1", + "meta": { + "profile": [ + "http://hl7.org/fhir/us/qicore/StructureDefinition/qicore-condition" + ] + }, + "clinicalStatus": { + "coding": [ + { + "system": "http://terminology.hl7.org/CodeSystem/condition-clinical", + "version": "0.5.0", + "code": "active", + "display": "Active" + } + ] + }, + "verificationStatus": { + "coding": [ + { + "system": "http://terminology.hl7.org/CodeSystem/condition-ver-status", + "version": "0.5.0", + "code": "confirmed", + "display": "Confirmed" + } + ] + }, + "category": [ + { + "coding": [ + { + "system": "http://terminology.hl7.org/ValueSet/condition-category", + "version": "0.5.0", + "code": "encounter-diagnosis", + "display": "Encounter Diagnosis" + } + ] + } + ], + "code": { + "coding": [ + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2021", + "code": "O30.833", + "display": "Other specified multiple gestation, number of chorions and amnions are both equal to the number of fetuses, third trimester" + } + ] + }, + "subject": { + "reference": "Patient/CMSTest-patient-1" + }, + "onsetDateTime": "2023-01-05T10:00:00-07:00" + }, + "request": { + "method": "PUT", + "url": "Condition/CMSTest-patient-1-condition-1" + } + }, + { + "resource": { + "resourceType": "ValueSet", + "id": "2.16.840.1.114222.4.11.836", + "url": "http://cts.nlm.nih.gov/fhir/ValueSet/2.16.840.1.114222.4.11.836", + "identifier": [ + { + "system": "urn:ietf:rfc:3986", + "value": "2.16.840.1.114222.4.11.836" + } + ], + "version": "20121025", + "name": "Race", + "title": "Race", + "status": "active", + "experimental": false, + "publisher": "NLM", + "description": "Codes representing possible values for Race.", + "expansion": { + "identifier": "20210506", + "timestamp": "2021-08-19T13:27:33-06:00", + "contains": [ + { + "system": "http://terminology.hl7.org/CodeSystem/PHRaceAndEthnicityCDC", + "version": "1.2", + "code": "1002-5", + "display": "American Indian or Alaska Native" + }, + { + "system": "http://terminology.hl7.org/CodeSystem/PHRaceAndEthnicityCDC", + "version": "1.2", + "code": "2028-9", + "display": "Asian" + }, + { + "system": "http://terminology.hl7.org/CodeSystem/PHRaceAndEthnicityCDC", + "version": "1.2", + "code": "2054-5", + "display": "Black or African American" + }, + { + "system": "http://terminology.hl7.org/CodeSystem/PHRaceAndEthnicityCDC", + "version": "1.2", + "code": "2076-8", + "display": "Native Hawaiian or Other Pacific Islander" + }, + { + "system": "http://terminology.hl7.org/CodeSystem/PHRaceAndEthnicityCDC", + "version": "1.2", + "code": "2106-3", + "display": "White" + }, + { + "system": "http://terminology.hl7.org/CodeSystem/PHRaceAndEthnicityCDC", + "version": "1.2", + "code": "2131-1", + "display": "Other Race" + } + ] + } + }, + "request": { + "method": "PUT", + "url": "ValueSet/2.16.840.1.114222.4.11.836" + } + }, + { + "resource": { + "resourceType": "Measure", + "id": "CMSTest", + "meta": { + "profile": [ + "http://hl7.org/fhir/us/cqfmeasures/StructureDefinition/computable-measure-cqfm" + ] + }, + "contained": [ + { + "resourceType": "Library", + "id": "effective-data-requirements", + "status": "active", + "type": { + "coding": [ + { + "system": "http://terminology.hl7.org/CodeSystem/library-type", + "code": "module-definition" + } + ] + }, + "relatedArtifact": [ + { + "type": "depends-on", + "display": "Library SDE", + "resource": "http://content.alphora.com/fhir/dqm/Library/SDE" + }, + { + "type": "depends-on", + "display": "Library FHIRHelpers", + "resource": "http://content.alphora.com/fhir/dqm/Library/DQMFHIRHelpers" + } + ], + "parameter": [ + { + "name": "Measurement Period", + "use": "in", + "min": 0, + "max": "1", + "type": "Period" + }, + { + "name": "SDE Sex", + "use": "out", + "min": 0, + "max": "1", + "type": "Coding" + }, + { + "name": "Numerator", + "use": "out", + "min": 0, + "max": "1", + "type": "boolean" + }, + { + "name": "Denominator", + "use": "out", + "min": 0, + "max": "1", + "type": "boolean" + }, + { + "name": "Initial Population", + "use": "out", + "min": 0, + "max": "1", + "type": "boolean" + } + ], + "dataRequirement": [ + { + "type": "Patient", + "profile": [ + "http://hl7.org/fhir/StructureDefinition/Patient" + ] + }, + { + "type": "Patient", + "profile": [ + "http://hl7.org/fhir/StructureDefinition/Patient" + ] + } + ] + } + ], + "extension": [ + { + "url": "http://hl7.org/fhir/us/cqfmeasures/StructureDefinition/cqfm-softwaresystem", + "valueReference": { + "reference": "Device/cqf-tooling" + } + }, + { + "id": "effective-data-requirements", + "url": "http://hl7.org/fhir/us/cqfmeasures/StructureDefinition/cqfm-effectiveDataRequirements", + "valueReference": { + "reference": "#effective-data-requirements" + } + } + ], + "url": "http://content.alphora.com/fhir/dqm/Measure/CMSTest", + "name": "CMSTest", + "title": "Measure - CMSTest: test", + "status": "active", + "experimental": true, + "date": "2023-06-21T16:16:00-07:00", + "publisher": "Alphora", + "contact": [ + { + "telecom": [ + { + "system": "url", + "value": "https://alphora.com" + } + ] + } + ], + "description": "Percentage of visits for patients aged 18 years and older for which the eligible professional or eligible clinician attests to documenting a list of current medications using all immediate resources available on the date of the encounter", + "useContext": [ + { + "code": { + "system": "http://terminology.hl7.org/CodeSystem/usage-context-type", + "version": "4.0.1", + "code": "program", + "display": "Program" + }, + "valueCodeableConcept": { + "text": "eligible-provider" + } + } + ], + "jurisdiction": [ + { + "coding": [ + { + "system": "urn:iso:std:iso:3166", + "version": "4.0.1", + "code": "US", + "display": "United States of America" + } + ] + } + ], + "purpose": "Percentage of visits for patients aged 18 years and older for which the eligible professional or eligible clinician attests to documenting a list of current medications using all immediate resources available on the date of the encounter", + "effectivePeriod": { + "start": "2020-01-01T00:00:00-07:00", + "end": "2020-12-31T23:59:59-07:00" + }, + "topic": [ + { + "coding": [ + { + "system": "http://loinc.org", + "code": "57024-2", + "display": "Health Quality Measure Document" + } + ] + } + ], + "library": [ + "http://content.alphora.com/fhir/dqm/Library/CMSTest" + ], + "scoring": { + "coding": [ + { + "system": "http://terminology.hl7.org/CodeSystem/measure-scoring", + "version": "4.0.1", + "code": "proportion", + "display": "Proportion" + } + ] + }, + "type": [ + { + "coding": [ + { + "system": "http://terminology.hl7.org/CodeSystem/measure-type", + "version": "4.2.0", + "code": "process", + "display": "Process" + } + ] + } + ], + "rationale": "test.", + "clinicalRecommendationStatement": "test", + "improvementNotation": { + "coding": [ + { + "system": "http://terminology.hl7.org/CodeSystem/measure-improvement-notation", + "version": "0.1.0", + "code": "increase", + "display": "Increased score indicates improvement" + } + ] + }, + "group": [ + { + "id": "group-1", + "population": [ + { + "code": { + "coding": [ + { + "system": "http://terminology.hl7.org/CodeSystem/measure-population", + "code": "initial-population", + "display": "Initial Population" + } + ] + }, + "criteria": { + "language": "text/cql.identifier", + "expression": "Initial Population" + } + }, + { + "code": { + "coding": [ + { + "system": "http://terminology.hl7.org/CodeSystem/measure-population", + "code": "denominator", + "display": "Denominator" + } + ] + }, + "criteria": { + "language": "text/cql.identifier", + "expression": "Denominator" + } + }, + { + "code": { + "coding": [ + { + "system": "http://terminology.hl7.org/CodeSystem/measure-population", + "code": "numerator", + "display": "Numerator" + } + ] + }, + "criteria": { + "language": "text/cql.identifier", + "expression": "Numerator" + } + } + ] + } + ], + "supplementalData": [ + { + "id": "sde-sex", + "code": { + "text": "sde-sex" + }, + "usage": [ + { + "coding": [ + { + "system": "http://terminology.hl7.org/CodeSystem/measure-data-usage", + "code": "supplemental-data" + } + ] + } + ], + "criteria": { + "language": "text/cql.identifier", + "expression": "SDE Sex" + } + } + ] + }, + "request": { + "method": "PUT", + "url": "Measure/CMSTest" + } + }, + { + "resource": { + "resourceType": "Library", + "id": "CMSTest", + "extension": [ + { + "url": "http://hl7.org/fhir/us/cqfmeasures/StructureDefinition/cqfm-softwaresystem", + "valueReference": { + "reference": "Device/cqf-tooling" + } + } + ], + "url": "http://content.alphora.com/fhir/dqm/Library/CMSTest", + "name": "CMSTest", + "relatedArtifact": [ + { + "type": "depends-on", + "display": "FHIR model information", + "resource": "http://fhir.org/guides/cqf/common/Library/FHIR-ModelInfo|4.0.1" + }, + { + "type": "depends-on", + "display": "Library FHIRHelpers", + "resource": "http://content.alphora.com/fhir/dqm/Library/FHIRHelpers|4.0.1" + }, + { + "type": "depends-on", + "display": "Library FC", + "resource": "http://content.alphora.com/fhir/dqm/Library/FHIRCommon|4.0.1" + }, + { + "type": "depends-on", + "display": "Library SDE", + "resource": "http://content.alphora.com/fhir/dqm/Library/SDE" + }, + { + "type": "depends-on", + "display": "Library FHIRHelpers", + "resource": "http://content.alphora.com/fhir/dqm/Library/DQMFHIRHelpers" + }, + { + "type": "depends-on", + "display": "Code system ConditionClinicalStatusCodes", + "resource": "http://terminology.hl7.org/CodeSystem/condition-clinical" + }, + { + "type": "depends-on", + "display": "Code system ConditionVerificationStatusCodes", + "resource": "http://terminology.hl7.org/CodeSystem/condition-ver-status" + }, + { + "type": "depends-on", + "display": "Value set Pregnancy", + "resource": "http://cts.nlm.nih.gov/fhir/ValueSet/2.16.840.1.113883.3.526.3.378" + } + ], + "parameter": [ + { + "name": "Measurement Period", + "use": "in", + "min": 0, + "max": "1", + "type": "Period" + }, + { + "name": "Patient", + "use": "out", + "min": 0, + "max": "1", + "type": "Patient" + }, + { + "name": "SDE Sex", + "use": "out", + "min": 0, + "max": "1", + "type": "Coding" + }, + { + "name": "Initial Population", + "use": "out", + "min": 0, + "max": "1", + "type": "boolean" + }, + { + "name": "Denominator", + "use": "out", + "min": 0, + "max": "1", + "type": "boolean" + }, + { + "name": "Pregnant Patient", + "use": "out", + "min": 0, + "max": "*", + "type": "Condition" + }, + { + "name": "Numerator", + "use": "out", + "min": 0, + "max": "1", + "type": "boolean" + } + ], + "dataRequirement": [ + { + "type": "Patient", + "profile": [ + "http://hl7.org/fhir/StructureDefinition/Patient" + ] + }, + { + "type": "Patient", + "profile": [ + "http://hl7.org/fhir/StructureDefinition/Patient" + ] + }, + { + "type": "Condition", + "profile": [ + "http://hl7.org/fhir/StructureDefinition/Condition" + ], + "mustSupport": [ + "code" + ], + "codeFilter": [ + { + "path": "code", + "valueSet": "http://cts.nlm.nih.gov/fhir/ValueSet/2.16.840.1.113883.3.526.3.378" + } + ] + }, + { + "type": "Condition", + "profile": [ + "http://hl7.org/fhir/StructureDefinition/Condition" + ], + "mustSupport": [ + "code" + ], + "codeFilter": [ + { + "path": "code", + "valueSet": "http://cts.nlm.nih.gov/fhir/ValueSet/2.16.840.1.113883.3.526.3.378" + } + ] + }, + { + "type": "Condition", + "profile": [ + "http://hl7.org/fhir/StructureDefinition/Condition" + ], + "mustSupport": [ + "code" + ], + "codeFilter": [ + { + "path": "code", + "valueSet": "http://cts.nlm.nih.gov/fhir/ValueSet/2.16.840.1.113883.3.526.3.378" + } + ] + } + ], + "content": [ + { + "contentType": "text/cql", + "data": "bGlicmFyeSBDTVNUZXN0Cgp1c2luZyBGSElSIHZlcnNpb24gJzQuMC4xJwppbmNsdWRlIEZISVJIZWxwZXJzIHZlcnNpb24gJzQuMC4xJyBjYWxsZWQgRkhJUkhlbHBlcnMKaW5jbHVkZSBGSElSQ29tbW9uIHZlcnNpb24gJzQuMC4xJyBjYWxsZWQgRkMKCmluY2x1ZGUgU0RFIGNhbGxlZCBTREUKdmFsdWVzZXQgIlByZWduYW5jeSI6ICdodHRwOi8vY3RzLm5sbS5uaWguZ292L2ZoaXIvVmFsdWVTZXQvMi4xNi44NDAuMS4xMTM4ODMuMy41MjYuMy4zNzgnCgpwYXJhbWV0ZXIgIk1lYXN1cmVtZW50IFBlcmlvZCIgZGVmYXVsdCBJbnRlcnZhbFtAMjAyMy0wMS0wMVQwMDowMDowMC4wMDAsIEAyMDIzLTEyLTMxVDAwOjAwOjAwLjAwMCkKCmNvbnRleHQgUGF0aWVudAoKZGVmaW5lICJTREUgU2V4IjoKICBTREUuIlNERSBTZXgiCgpkZWZpbmUgIkluaXRpYWwgUG9wdWxhdGlvbiI6CiAgQWdlSW5ZZWFyc0F0KHN0YXJ0IG9mICJNZWFzdXJlbWVudCBQZXJpb2QiKSA+IDE4CiAgICAgICAgICAgIApkZWZpbmUgZnVuY3Rpb24gUXVhbGlmaWVkQ29uZGl0aW9ucyh2YWx1ZSBMaXN0PEZISVIuQ29uZGl0aW9uPik6CiAgdmFsdWUgQ29uZGl0aW9uCiAgICB3aGVyZSAoCiAgICAgIEZISVJIZWxwZXJzLlRvQ29uY2VwdChDb25kaXRpb24uY2xpbmljYWxTdGF0dXMpIH4gRkMuImFjdGl2ZSIKICAgICkKICAgIGFuZCAoCiAgICAgIEZISVJIZWxwZXJzLlRvQ29uY2VwdChDb25kaXRpb24udmVyaWZpY2F0aW9uU3RhdHVzKSB+IEZDLiJjb25maXJtZWQiCiAgICApCgpkZWZpbmUgIkRlbm9taW5hdG9yIjoKICAgQWdlSW5ZZWFyc0F0KHN0YXJ0IG9mICJNZWFzdXJlbWVudCBQZXJpb2QiKSA8IDk5CgoKZGVmaW5lICJOdW1lcmF0b3IiOgogIGV4aXN0cyAiUHJlZ25hbnQgUGF0aWVudCIKCmRlZmluZSAiUHJlZ25hbnQgUGF0aWVudCI6ICAgCiAgKFF1YWxpZmllZENvbmRpdGlvbnMoW0NvbmRpdGlvbjogIlByZWduYW5jeSJdKSkgUHJlZ25hbmN5CiB3aGVyZSBGQy5Ub1ByZXZhbGVuY2VJbnRlcnZhbChQcmVnbmFuY3kpIG92ZXJsYXBzICJNZWFzdXJlbWVudCBQZXJpb2QiICAgICAgICAgCiAgCgoKCg==" + } + ] + }, + "request": { + "method": "PUT", + "url": "Library/CMSTest" + } + }, + { + "resource": { + "resourceType": "Library", + "id": "FHIRHelpers", + "extension": [ + { + "url": "http://hl7.org/fhir/us/cqfmeasures/StructureDefinition/cqfm-softwaresystem", + "valueReference": { + "reference": "Device/cqf-tooling" + } + } + ], + "url": "http://content.alphora.com/fhir/dqm/Library/FHIRHelpers", + "version": "4.0.1", + "name": "FHIRHelpers", + "title": "Library - FHIR Helpers", + "type": { + "coding": [ + { + "system": "http://terminology.hl7.org/CodeSystem/library-type", + "code": "logic-library" + } + ] + }, + "description": "Logic library used in mapping CQL logic with the R4 FHIR Data Model.", + "jurisdiction": [ + { + "coding": [ + { + "system": "urn:iso:std:iso:3166", + "version": "4.0.1", + "code": "US", + "display": "United States of America" + } + ], + "text": "United States of America" + } + ], + "relatedArtifact": [ + { + "type": "depends-on", + "display": "FHIR model information", + "resource": "http://fhir.org/guides/cqf/common/Library/FHIR-ModelInfo|4.0.1" + } + ], + "content": [ + { + "contentType": "text/cql", + "data": "LyoKQGF1dGhvcjogQnJ5biBSaG9kZXMKQGRlc2NyaXB0aW9uOiBUaGlzIGxpYnJhcnkgZGVmaW5lcyBmdW5jdGlvbnMgdG8gY29udmVydCBiZXR3ZWVuIEZISVIKIGRhdGEgdHlwZXMgYW5kIENRTCBzeXN0ZW0tZGVmaW5lZCB0eXBlcywgYXMgd2VsbCBhcyBmdW5jdGlvbnMgdG8gc3VwcG9ydAogRkhJUlBhdGggaW1wbGVtZW50YXRpb24uIEZvciBtb3JlIGluZm9ybWF0aW9uLCBzZWUgdGhlIEZISVJIZWxwZXJzIHdpa2kgcGFnZToKIGh0dHBzOi8vZ2l0aHViLmNvbS9jcWZyYW1ld29yay9jbGluaWNhbF9xdWFsaXR5X2xhbmd1YWdlL3dpa2kvRkhJUkhlbHBlcnMKQGFsbG93Rmx1ZW50OiB0cnVlCiovCmxpYnJhcnkgRkhJUkhlbHBlcnMgdmVyc2lvbiAnNC4wLjEnCgp1c2luZyBGSElSIHZlcnNpb24gJzQuMC4xJwoKZGVmaW5lIGZ1bmN0aW9uIFRvSW50ZXJ2YWwocGVyaW9kIEZISVIuUGVyaW9kKToKICAgIGlmIHBlcmlvZCBpcyBudWxsIHRoZW4KICAgICAgICBudWxsCiAgICBlbHNlCiAgICAgICAgaWYgcGVyaW9kLiJzdGFydCIgaXMgbnVsbCB0aGVuCiAgICAgICAgICAgIEludGVydmFsKHBlcmlvZC4ic3RhcnQiLnZhbHVlLCBwZXJpb2QuImVuZCIudmFsdWVdCiAgICAgICAgZWxzZQogICAgICAgICAgICBJbnRlcnZhbFtwZXJpb2QuInN0YXJ0Ii52YWx1ZSwgcGVyaW9kLiJlbmQiLnZhbHVlXQoKZGVmaW5lIGZ1bmN0aW9uIFRvQ2FsZW5kYXJVbml0KHVuaXQgU3lzdGVtLlN0cmluZyk6CiAgICBjYXNlIHVuaXQKICAgICAgICB3aGVuICdtcycgdGhlbiAnbWlsbGlzZWNvbmQnCiAgICAgICAgd2hlbiAncycgdGhlbiAnc2Vjb25kJwogICAgICAgIHdoZW4gJ21pbicgdGhlbiAnbWludXRlJwogICAgICAgIHdoZW4gJ2gnIHRoZW4gJ2hvdXInCiAgICAgICAgd2hlbiAnZCcgdGhlbiAnZGF5JwogICAgICAgIHdoZW4gJ3drJyB0aGVuICd3ZWVrJwogICAgICAgIHdoZW4gJ21vJyB0aGVuICdtb250aCcKICAgICAgICB3aGVuICdhJyB0aGVuICd5ZWFyJwogICAgICAgIGVsc2UgdW5pdAogICAgZW5kCgpkZWZpbmUgZnVuY3Rpb24gVG9RdWFudGl0eShxdWFudGl0eSBGSElSLlF1YW50aXR5KToKICAgIGNhc2UKICAgICAgICB3aGVuIHF1YW50aXR5IGlzIG51bGwgdGhlbiBudWxsCiAgICAgICAgd2hlbiBxdWFudGl0eS52YWx1ZSBpcyBudWxsIHRoZW4gbnVsbAogICAgICAgIHdoZW4gcXVhbnRpdHkuY29tcGFyYXRvciBpcyBub3QgbnVsbCB0aGVuCiAgICAgICAgICAgIE1lc3NhZ2UobnVsbCwgdHJ1ZSwgJ0ZISVJIZWxwZXJzLlRvUXVhbnRpdHkuQ29tcGFyYXRvclF1YW50aXR5Tm90U3VwcG9ydGVkJywgJ0Vycm9yJywgJ0ZISVIgUXVhbnRpdHkgdmFsdWUgaGFzIGEgY29tcGFyYXRvciBhbmQgY2Fubm90IGJlIGNvbnZlcnRlZCB0byBhIFN5c3RlbS5RdWFudGl0eSB2YWx1ZS4nKQogICAgICAgIHdoZW4gcXVhbnRpdHkuc3lzdGVtIGlzIG51bGwgb3IgcXVhbnRpdHkuc3lzdGVtLnZhbHVlID0gJ2h0dHA6Ly91bml0c29mbWVhc3VyZS5vcmcnCiAgICAgICAgICAgICAgb3IgcXVhbnRpdHkuc3lzdGVtLnZhbHVlID0gJ2h0dHA6Ly9obDcub3JnL2ZoaXJwYXRoL0NvZGVTeXN0ZW0vY2FsZW5kYXItdW5pdHMnIHRoZW4KICAgICAgICAgICAgU3lzdGVtLlF1YW50aXR5IHsgdmFsdWU6IHF1YW50aXR5LnZhbHVlLnZhbHVlLCB1bml0OiBUb0NhbGVuZGFyVW5pdChDb2FsZXNjZShxdWFudGl0eS5jb2RlLnZhbHVlLCBxdWFudGl0eS51bml0LnZhbHVlLCAnMScpKSB9CiAgICAgICAgZWxzZQogICAgICAgICAgICBNZXNzYWdlKG51bGwsIHRydWUsICdGSElSSGVscGVycy5Ub1F1YW50aXR5LkludmFsaWRGSElSUXVhbnRpdHknLCAnRXJyb3InLCAnSW52YWxpZCBGSElSIFF1YW50aXR5IGNvZGU6ICcgJiBxdWFudGl0eS51bml0LnZhbHVlICYgJyAoJyAmIHF1YW50aXR5LnN5c3RlbS52YWx1ZSAmICd8JyAmIHF1YW50aXR5LmNvZGUudmFsdWUgJiAnKScpCiAgICBlbmQKCmRlZmluZSBmdW5jdGlvbiBUb1F1YW50aXR5SWdub3JpbmdDb21wYXJhdG9yKHF1YW50aXR5IEZISVIuUXVhbnRpdHkpOgogICAgY2FzZQogICAgICAgIHdoZW4gcXVhbnRpdHkgaXMgbnVsbCB0aGVuIG51bGwKICAgICAgICB3aGVuIHF1YW50aXR5LnZhbHVlIGlzIG51bGwgdGhlbiBudWxsCiAgICAgICAgd2hlbiBxdWFudGl0eS5zeXN0ZW0gaXMgbnVsbCBvciBxdWFudGl0eS5zeXN0ZW0udmFsdWUgPSAnaHR0cDovL3VuaXRzb2ZtZWFzdXJlLm9yZycKICAgICAgICAgICAgICBvciBxdWFudGl0eS5zeXN0ZW0udmFsdWUgPSAnaHR0cDovL2hsNy5vcmcvZmhpcnBhdGgvQ29kZVN5c3RlbS9jYWxlbmRhci11bml0cycgdGhlbgogICAgICAgICAgICBTeXN0ZW0uUXVhbnRpdHkgeyB2YWx1ZTogcXVhbnRpdHkudmFsdWUudmFsdWUsIHVuaXQ6IFRvQ2FsZW5kYXJVbml0KENvYWxlc2NlKHF1YW50aXR5LmNvZGUudmFsdWUsIHF1YW50aXR5LnVuaXQudmFsdWUsICcxJykpIH0KICAgICAgICBlbHNlCiAgICAgICAgICAgIE1lc3NhZ2UobnVsbCwgdHJ1ZSwgJ0ZISVJIZWxwZXJzLlRvUXVhbnRpdHkuSW52YWxpZEZISVJRdWFudGl0eScsICdFcnJvcicsICdJbnZhbGlkIEZISVIgUXVhbnRpdHkgY29kZTogJyAmIHF1YW50aXR5LnVuaXQudmFsdWUgJiAnICgnICYgcXVhbnRpdHkuc3lzdGVtLnZhbHVlICYgJ3wnICYgcXVhbnRpdHkuY29kZS52YWx1ZSAmICcpJykKICAgIGVuZAoKZGVmaW5lIGZ1bmN0aW9uIFRvSW50ZXJ2YWwocXVhbnRpdHkgRkhJUi5RdWFudGl0eSk6CiAgICBpZiBxdWFudGl0eSBpcyBudWxsIHRoZW4gbnVsbCBlbHNlCiAgICAgICAgY2FzZSBxdWFudGl0eS5jb21wYXJhdG9yLnZhbHVlCiAgICAgICAgICAgIHdoZW4gJzwnIHRoZW4KICAgICAgICAgICAgICAgIEludGVydmFsWwogICAgICAgICAgICAgICAgICAgIG51bGwsCiAgICAgICAgICAgICAgICAgICAgVG9RdWFudGl0eUlnbm9yaW5nQ29tcGFyYXRvcihxdWFudGl0eSkKICAgICAgICAgICAgICAgICkKICAgICAgICAgICAgd2hlbiAnPD0nIHRoZW4KICAgICAgICAgICAgICAgIEludGVydmFsWwogICAgICAgICAgICAgICAgICAgIG51bGwsCiAgICAgICAgICAgICAgICAgICAgVG9RdWFudGl0eUlnbm9yaW5nQ29tcGFyYXRvcihxdWFudGl0eSkKICAgICAgICAgICAgICAgIF0KICAgICAgICAgICAgd2hlbiAnPj0nIHRoZW4KICAgICAgICAgICAgICAgIEludGVydmFsWwogICAgICAgICAgICAgICAgICAgIFRvUXVhbnRpdHlJZ25vcmluZ0NvbXBhcmF0b3IocXVhbnRpdHkpLAogICAgICAgICAgICAgICAgICAgIG51bGwKICAgICAgICAgICAgICAgIF0KICAgICAgICAgICAgd2hlbiAnPicgdGhlbgogICAgICAgICAgICAgICAgSW50ZXJ2YWwoCiAgICAgICAgICAgICAgICAgICAgVG9RdWFudGl0eUlnbm9yaW5nQ29tcGFyYXRvcihxdWFudGl0eSksCiAgICAgICAgICAgICAgICAgICAgbnVsbAogICAgICAgICAgICAgICAgXQogICAgICAgICAgICBlbHNlCiAgICAgICAgICAgICAgICBJbnRlcnZhbFtUb1F1YW50aXR5KHF1YW50aXR5KSwgVG9RdWFudGl0eShxdWFudGl0eSldCiAgICAgICAgZW5kCgpkZWZpbmUgZnVuY3Rpb24gVG9SYXRpbyhyYXRpbyBGSElSLlJhdGlvKToKICAgIGlmIHJhdGlvIGlzIG51bGwgdGhlbgogICAgICAgIG51bGwKICAgIGVsc2UKICAgICAgICBTeXN0ZW0uUmF0aW8geyBudW1lcmF0b3I6IFRvUXVhbnRpdHkocmF0aW8ubnVtZXJhdG9yKSwgZGVub21pbmF0b3I6IFRvUXVhbnRpdHkocmF0aW8uZGVub21pbmF0b3IpIH0KCmRlZmluZSBmdW5jdGlvbiBUb0ludGVydmFsKHJhbmdlIEZISVIuUmFuZ2UpOgogICAgaWYgcmFuZ2UgaXMgbnVsbCB0aGVuCiAgICAgICAgbnVsbAogICAgZWxzZQogICAgICAgIEludGVydmFsW1RvUXVhbnRpdHkocmFuZ2UubG93KSwgVG9RdWFudGl0eShyYW5nZS5oaWdoKV0KCmRlZmluZSBmdW5jdGlvbiBUb0NvZGUoY29kaW5nIEZISVIuQ29kaW5nKToKICAgIGlmIGNvZGluZyBpcyBudWxsIHRoZW4KICAgICAgICBudWxsCiAgICBlbHNlCiAgICAgICAgU3lzdGVtLkNvZGUgewogICAgICAgICAgY29kZTogY29kaW5nLmNvZGUudmFsdWUsCiAgICAgICAgICBzeXN0ZW06IGNvZGluZy5zeXN0ZW0udmFsdWUsCiAgICAgICAgICB2ZXJzaW9uOiBjb2RpbmcudmVyc2lvbi52YWx1ZSwKICAgICAgICAgIGRpc3BsYXk6IGNvZGluZy5kaXNwbGF5LnZhbHVlCiAgICAgICAgfQoKZGVmaW5lIGZ1bmN0aW9uIFRvQ29uY2VwdChjb25jZXB0IEZISVIuQ29kZWFibGVDb25jZXB0KToKICAgIGlmIGNvbmNlcHQgaXMgbnVsbCB0aGVuCiAgICAgICAgbnVsbAogICAgZWxzZQogICAgICAgIFN5c3RlbS5Db25jZXB0IHsKICAgICAgICAgICAgY29kZXM6IGNvbmNlcHQuY29kaW5nIEMgcmV0dXJuIFRvQ29kZShDKSwKICAgICAgICAgICAgZGlzcGxheTogY29uY2VwdC50ZXh0LnZhbHVlCiAgICAgICAgfQoKZGVmaW5lIGZ1bmN0aW9uIHJlZmVyZW5jZShyZWZlcmVuY2UgU3RyaW5nKToKICAgIGlmIHJlZmVyZW5jZSBpcyBudWxsIHRoZW4KICAgICAgICBudWxsCiAgICBlbHNlCiAgICAgICAgUmVmZXJlbmNlIHsgcmVmZXJlbmNlOiBzdHJpbmcgeyB2YWx1ZTogcmVmZXJlbmNlIH0gfQoKZGVmaW5lIGZ1bmN0aW9uIHJlc29sdmUocmVmZXJlbmNlIFN0cmluZykgcmV0dXJucyBSZXNvdXJjZTogZXh0ZXJuYWwKZGVmaW5lIGZ1bmN0aW9uIHJlc29sdmUocmVmZXJlbmNlIFJlZmVyZW5jZSkgcmV0dXJucyBSZXNvdXJjZTogZXh0ZXJuYWwKZGVmaW5lIGZ1bmN0aW9uIHJlZmVyZW5jZShyZXNvdXJjZSBSZXNvdXJjZSkgcmV0dXJucyBSZWZlcmVuY2U6IGV4dGVybmFsCmRlZmluZSBmdW5jdGlvbiBleHRlbnNpb24oZWxlbWVudCBFbGVtZW50LCB1cmwgU3RyaW5nKSByZXR1cm5zIExpc3Q8RWxlbWVudD46IGV4dGVybmFsCmRlZmluZSBmdW5jdGlvbiBleHRlbnNpb24ocmVzb3VyY2UgUmVzb3VyY2UsIHVybCBTdHJpbmcpIHJldHVybnMgTGlzdDxFbGVtZW50PjogZXh0ZXJuYWwKZGVmaW5lIGZ1bmN0aW9uIGhhc1ZhbHVlKGVsZW1lbnQgRWxlbWVudCkgcmV0dXJucyBCb29sZWFuOiBleHRlcm5hbApkZWZpbmUgZnVuY3Rpb24gZ2V0VmFsdWUoZWxlbWVudCBFbGVtZW50KSByZXR1cm5zIEFueTogZXh0ZXJuYWwKZGVmaW5lIGZ1bmN0aW9uIG9mVHlwZShpZGVudGlmaWVyIFN0cmluZykgcmV0dXJucyBMaXN0PEFueT46IGV4dGVybmFsCmRlZmluZSBmdW5jdGlvbiBpcyhpZGVudGlmaWVyIFN0cmluZykgcmV0dXJucyBCb29sZWFuOiBleHRlcm5hbApkZWZpbmUgZnVuY3Rpb24gYXMoaWRlbnRpZmllciBTdHJpbmcpIHJldHVybnMgQW55OiBleHRlcm5hbApkZWZpbmUgZnVuY3Rpb24gZWxlbWVudERlZmluaXRpb24oZWxlbWVudCBFbGVtZW50KSByZXR1cm5zIEVsZW1lbnREZWZpbml0aW9uOiBleHRlcm5hbApkZWZpbmUgZnVuY3Rpb24gc2xpY2UoZWxlbWVudCBFbGVtZW50LCB1cmwgU3RyaW5nLCBuYW1lIFN0cmluZykgcmV0dXJucyBMaXN0PEVsZW1lbnQ+OiBleHRlcm5hbApkZWZpbmUgZnVuY3Rpb24gY2hlY2tNb2RpZmllcnMocmVzb3VyY2UgUmVzb3VyY2UpIHJldHVybnMgUmVzb3VyY2U6IGV4dGVybmFsCmRlZmluZSBmdW5jdGlvbiBjaGVja01vZGlmaWVycyhyZXNvdXJjZSBSZXNvdXJjZSwgbW9kaWZpZXIgU3RyaW5nKSByZXR1cm5zIFJlc291cmNlOiBleHRlcm5hbApkZWZpbmUgZnVuY3Rpb24gY2hlY2tNb2RpZmllcnMoZWxlbWVudCBFbGVtZW50KSByZXR1cm5zIEVsZW1lbnQ6IGV4dGVybmFsCmRlZmluZSBmdW5jdGlvbiBjaGVja01vZGlmaWVycyhlbGVtZW50IEVsZW1lbnQsIG1vZGlmaWVyIFN0cmluZykgcmV0dXJucyBFbGVtZW50OiBleHRlcm5hbApkZWZpbmUgZnVuY3Rpb24gY29uZm9ybXNUbyhyZXNvdXJjZSBSZXNvdXJjZSwgc3RydWN0dXJlIFN0cmluZykgcmV0dXJucyBCb29sZWFuOiBleHRlcm5hbApkZWZpbmUgZnVuY3Rpb24gbWVtYmVyT2YoY29kZSBjb2RlLCB2YWx1ZVNldCBTdHJpbmcpIHJldHVybnMgQm9vbGVhbjogZXh0ZXJuYWwKZGVmaW5lIGZ1bmN0aW9uIG1lbWJlck9mKGNvZGluZyBDb2RpbmcsIHZhbHVlU2V0IFN0cmluZykgcmV0dXJucyBCb29sZWFuOiBleHRlcm5hbApkZWZpbmUgZnVuY3Rpb24gbWVtYmVyT2YoY29uY2VwdCBDb2RlYWJsZUNvbmNlcHQsIHZhbHVlU2V0IFN0cmluZykgcmV0dXJucyBCb29sZWFuOiBleHRlcm5hbApkZWZpbmUgZnVuY3Rpb24gc3Vic3VtZXMoY29kaW5nIENvZGluZywgc3Vic3VtZWRDb2RpbmcgQ29kaW5nKSByZXR1cm5zIEJvb2xlYW46IGV4dGVybmFsCmRlZmluZSBmdW5jdGlvbiBzdWJzdW1lcyhjb25jZXB0IENvZGVhYmxlQ29uY2VwdCwgc3Vic3VtZWRDb25jZXB0IENvZGVhYmxlQ29uY2VwdCkgcmV0dXJucyBCb29sZWFuOiBleHRlcm5hbApkZWZpbmUgZnVuY3Rpb24gc3Vic3VtZWRCeShjb2RpbmcgQ29kaW5nLCBzdWJzdW1pbmdDb2RpbmcgQ29kaW5nKSByZXR1cm5zIEJvb2xlYW46IGV4dGVybmFsCmRlZmluZSBmdW5jdGlvbiBzdWJzdW1lZEJ5KGNvbmNlcHQgQ29kZWFibGVDb25jZXB0LCBzdWJzdW1pbmdDb25jZXB0IENvZGVhYmxlQ29uY2VwdCkgcmV0dXJucyBCb29sZWFuOiBleHRlcm5hbApkZWZpbmUgZnVuY3Rpb24gaHRtbENoZWNrcyhlbGVtZW50IEVsZW1lbnQpIHJldHVybnMgQm9vbGVhbjogZXh0ZXJuYWwKCmRlZmluZSBmdW5jdGlvbiBUb1N0cmluZyh2YWx1ZSBBY2NvdW50U3RhdHVzKTogdmFsdWUudmFsdWUKZGVmaW5lIGZ1bmN0aW9uIFRvU3RyaW5nKHZhbHVlIEFjdGlvbkNhcmRpbmFsaXR5QmVoYXZpb3IpOiB2YWx1ZS52YWx1ZQpkZWZpbmUgZnVuY3Rpb24gVG9TdHJpbmcodmFsdWUgQWN0aW9uQ29uZGl0aW9uS2luZCk6IHZhbHVlLnZhbHVlCmRlZmluZSBmdW5jdGlvbiBUb1N0cmluZyh2YWx1ZSBBY3Rpb25Hcm91cGluZ0JlaGF2aW9yKTogdmFsdWUudmFsdWUKZGVmaW5lIGZ1bmN0aW9uIFRvU3RyaW5nKHZhbHVlIEFjdGlvblBhcnRpY2lwYW50VHlwZSk6IHZhbHVlLnZhbHVlCmRlZmluZSBmdW5jdGlvbiBUb1N0cmluZyh2YWx1ZSBBY3Rpb25QcmVjaGVja0JlaGF2aW9yKTogdmFsdWUudmFsdWUKZGVmaW5lIGZ1bmN0aW9uIFRvU3RyaW5nKHZhbHVlIEFjdGlvblJlbGF0aW9uc2hpcFR5cGUpOiB2YWx1ZS52YWx1ZQpkZWZpbmUgZnVuY3Rpb24gVG9TdHJpbmcodmFsdWUgQWN0aW9uUmVxdWlyZWRCZWhhdmlvcik6IHZhbHVlLnZhbHVlCmRlZmluZSBmdW5jdGlvbiBUb1N0cmluZyh2YWx1ZSBBY3Rpb25TZWxlY3Rpb25CZWhhdmlvcik6IHZhbHVlLnZhbHVlCmRlZmluZSBmdW5jdGlvbiBUb1N0cmluZyh2YWx1ZSBBY3Rpdml0eURlZmluaXRpb25LaW5kKTogdmFsdWUudmFsdWUKZGVmaW5lIGZ1bmN0aW9uIFRvU3RyaW5nKHZhbHVlIEFjdGl2aXR5UGFydGljaXBhbnRUeXBlKTogdmFsdWUudmFsdWUKZGVmaW5lIGZ1bmN0aW9uIFRvU3RyaW5nKHZhbHVlIEFkZHJlc3NUeXBlKTogdmFsdWUudmFsdWUKZGVmaW5lIGZ1bmN0aW9uIFRvU3RyaW5nKHZhbHVlIEFkZHJlc3NVc2UpOiB2YWx1ZS52YWx1ZQpkZWZpbmUgZnVuY3Rpb24gVG9TdHJpbmcodmFsdWUgQWRtaW5pc3RyYXRpdmVHZW5kZXIpOiB2YWx1ZS52YWx1ZQpkZWZpbmUgZnVuY3Rpb24gVG9TdHJpbmcodmFsdWUgQWR2ZXJzZUV2ZW50QWN0dWFsaXR5KTogdmFsdWUudmFsdWUKZGVmaW5lIGZ1bmN0aW9uIFRvU3RyaW5nKHZhbHVlIEFnZ3JlZ2F0aW9uTW9kZSk6IHZhbHVlLnZhbHVlCmRlZmluZSBmdW5jdGlvbiBUb1N0cmluZyh2YWx1ZSBBbGxlcmd5SW50b2xlcmFuY2VDYXRlZ29yeSk6IHZhbHVlLnZhbHVlCmRlZmluZSBmdW5jdGlvbiBUb1N0cmluZyh2YWx1ZSBBbGxlcmd5SW50b2xlcmFuY2VDcml0aWNhbGl0eSk6IHZhbHVlLnZhbHVlCmRlZmluZSBmdW5jdGlvbiBUb1N0cmluZyh2YWx1ZSBBbGxlcmd5SW50b2xlcmFuY2VTZXZlcml0eSk6IHZhbHVlLnZhbHVlCmRlZmluZSBmdW5jdGlvbiBUb1N0cmluZyh2YWx1ZSBBbGxlcmd5SW50b2xlcmFuY2VUeXBlKTogdmFsdWUudmFsdWUKZGVmaW5lIGZ1bmN0aW9uIFRvU3RyaW5nKHZhbHVlIEFwcG9pbnRtZW50U3RhdHVzKTogdmFsdWUudmFsdWUKZGVmaW5lIGZ1bmN0aW9uIFRvU3RyaW5nKHZhbHVlIEFzc2VydGlvbkRpcmVjdGlvblR5cGUpOiB2YWx1ZS52YWx1ZQpkZWZpbmUgZnVuY3Rpb24gVG9TdHJpbmcodmFsdWUgQXNzZXJ0aW9uT3BlcmF0b3JUeXBlKTogdmFsdWUudmFsdWUKZGVmaW5lIGZ1bmN0aW9uIFRvU3RyaW5nKHZhbHVlIEFzc2VydGlvblJlc3BvbnNlVHlwZXMpOiB2YWx1ZS52YWx1ZQpkZWZpbmUgZnVuY3Rpb24gVG9TdHJpbmcodmFsdWUgQXVkaXRFdmVudEFjdGlvbik6IHZhbHVlLnZhbHVlCmRlZmluZSBmdW5jdGlvbiBUb1N0cmluZyh2YWx1ZSBBdWRpdEV2ZW50QWdlbnROZXR3b3JrVHlwZSk6IHZhbHVlLnZhbHVlCmRlZmluZSBmdW5jdGlvbiBUb1N0cmluZyh2YWx1ZSBBdWRpdEV2ZW50T3V0Y29tZSk6IHZhbHVlLnZhbHVlCmRlZmluZSBmdW5jdGlvbiBUb1N0cmluZyh2YWx1ZSBCaW5kaW5nU3RyZW5ndGgpOiB2YWx1ZS52YWx1ZQpkZWZpbmUgZnVuY3Rpb24gVG9TdHJpbmcodmFsdWUgQmlvbG9naWNhbGx5RGVyaXZlZFByb2R1Y3RDYXRlZ29yeSk6IHZhbHVlLnZhbHVlCmRlZmluZSBmdW5jdGlvbiBUb1N0cmluZyh2YWx1ZSBCaW9sb2dpY2FsbHlEZXJpdmVkUHJvZHVjdFN0YXR1cyk6IHZhbHVlLnZhbHVlCmRlZmluZSBmdW5jdGlvbiBUb1N0cmluZyh2YWx1ZSBCaW9sb2dpY2FsbHlEZXJpdmVkUHJvZHVjdFN0b3JhZ2VTY2FsZSk6IHZhbHVlLnZhbHVlCmRlZmluZSBmdW5jdGlvbiBUb1N0cmluZyh2YWx1ZSBCdW5kbGVUeXBlKTogdmFsdWUudmFsdWUKZGVmaW5lIGZ1bmN0aW9uIFRvU3RyaW5nKHZhbHVlIENhcGFiaWxpdHlTdGF0ZW1lbnRLaW5kKTogdmFsdWUudmFsdWUKZGVmaW5lIGZ1bmN0aW9uIFRvU3RyaW5nKHZhbHVlIENhcmVQbGFuQWN0aXZpdHlLaW5kKTogdmFsdWUudmFsdWUKZGVmaW5lIGZ1bmN0aW9uIFRvU3RyaW5nKHZhbHVlIENhcmVQbGFuQWN0aXZpdHlTdGF0dXMpOiB2YWx1ZS52YWx1ZQpkZWZpbmUgZnVuY3Rpb24gVG9TdHJpbmcodmFsdWUgQ2FyZVBsYW5JbnRlbnQpOiB2YWx1ZS52YWx1ZQpkZWZpbmUgZnVuY3Rpb24gVG9TdHJpbmcodmFsdWUgQ2FyZVBsYW5TdGF0dXMpOiB2YWx1ZS52YWx1ZQpkZWZpbmUgZnVuY3Rpb24gVG9TdHJpbmcodmFsdWUgQ2FyZVRlYW1TdGF0dXMpOiB2YWx1ZS52YWx1ZQpkZWZpbmUgZnVuY3Rpb24gVG9TdHJpbmcodmFsdWUgQ2F0YWxvZ0VudHJ5UmVsYXRpb25UeXBlKTogdmFsdWUudmFsdWUKZGVmaW5lIGZ1bmN0aW9uIFRvU3RyaW5nKHZhbHVlIENoYXJnZUl0ZW1EZWZpbml0aW9uUHJpY2VDb21wb25lbnRUeXBlKTogdmFsdWUudmFsdWUKZGVmaW5lIGZ1bmN0aW9uIFRvU3RyaW5nKHZhbHVlIENoYXJnZUl0ZW1TdGF0dXMpOiB2YWx1ZS52YWx1ZQpkZWZpbmUgZnVuY3Rpb24gVG9TdHJpbmcodmFsdWUgQ2xhaW1SZXNwb25zZVN0YXR1cyk6IHZhbHVlLnZhbHVlCmRlZmluZSBmdW5jdGlvbiBUb1N0cmluZyh2YWx1ZSBDbGFpbVN0YXR1cyk6IHZhbHVlLnZhbHVlCmRlZmluZSBmdW5jdGlvbiBUb1N0cmluZyh2YWx1ZSBDbGluaWNhbEltcHJlc3Npb25TdGF0dXMpOiB2YWx1ZS52YWx1ZQpkZWZpbmUgZnVuY3Rpb24gVG9TdHJpbmcodmFsdWUgQ29kZVNlYXJjaFN1cHBvcnQpOiB2YWx1ZS52YWx1ZQpkZWZpbmUgZnVuY3Rpb24gVG9TdHJpbmcodmFsdWUgQ29kZVN5c3RlbUNvbnRlbnRNb2RlKTogdmFsdWUudmFsdWUKZGVmaW5lIGZ1bmN0aW9uIFRvU3RyaW5nKHZhbHVlIENvZGVTeXN0ZW1IaWVyYXJjaHlNZWFuaW5nKTogdmFsdWUudmFsdWUKZGVmaW5lIGZ1bmN0aW9uIFRvU3RyaW5nKHZhbHVlIENvbW11bmljYXRpb25Qcmlvcml0eSk6IHZhbHVlLnZhbHVlCmRlZmluZSBmdW5jdGlvbiBUb1N0cmluZyh2YWx1ZSBDb21tdW5pY2F0aW9uUmVxdWVzdFN0YXR1cyk6IHZhbHVlLnZhbHVlCmRlZmluZSBmdW5jdGlvbiBUb1N0cmluZyh2YWx1ZSBDb21tdW5pY2F0aW9uU3RhdHVzKTogdmFsdWUudmFsdWUKZGVmaW5lIGZ1bmN0aW9uIFRvU3RyaW5nKHZhbHVlIENvbXBhcnRtZW50Q29kZSk6IHZhbHVlLnZhbHVlCmRlZmluZSBmdW5jdGlvbiBUb1N0cmluZyh2YWx1ZSBDb21wYXJ0bWVudFR5cGUpOiB2YWx1ZS52YWx1ZQpkZWZpbmUgZnVuY3Rpb24gVG9TdHJpbmcodmFsdWUgQ29tcG9zaXRpb25BdHRlc3RhdGlvbk1vZGUpOiB2YWx1ZS52YWx1ZQpkZWZpbmUgZnVuY3Rpb24gVG9TdHJpbmcodmFsdWUgQ29tcG9zaXRpb25TdGF0dXMpOiB2YWx1ZS52YWx1ZQpkZWZpbmUgZnVuY3Rpb24gVG9TdHJpbmcodmFsdWUgQ29uY2VwdE1hcEVxdWl2YWxlbmNlKTogdmFsdWUudmFsdWUKZGVmaW5lIGZ1bmN0aW9uIFRvU3RyaW5nKHZhbHVlIENvbmNlcHRNYXBHcm91cFVubWFwcGVkTW9kZSk6IHZhbHVlLnZhbHVlCmRlZmluZSBmdW5jdGlvbiBUb1N0cmluZyh2YWx1ZSBDb25kaXRpb25hbERlbGV0ZVN0YXR1cyk6IHZhbHVlLnZhbHVlCmRlZmluZSBmdW5jdGlvbiBUb1N0cmluZyh2YWx1ZSBDb25kaXRpb25hbFJlYWRTdGF0dXMpOiB2YWx1ZS52YWx1ZQpkZWZpbmUgZnVuY3Rpb24gVG9TdHJpbmcodmFsdWUgQ29uc2VudERhdGFNZWFuaW5nKTogdmFsdWUudmFsdWUKZGVmaW5lIGZ1bmN0aW9uIFRvU3RyaW5nKHZhbHVlIENvbnNlbnRQcm92aXNpb25UeXBlKTogdmFsdWUudmFsdWUKZGVmaW5lIGZ1bmN0aW9uIFRvU3RyaW5nKHZhbHVlIENvbnNlbnRTdGF0ZSk6IHZhbHVlLnZhbHVlCmRlZmluZSBmdW5jdGlvbiBUb1N0cmluZyh2YWx1ZSBDb25zdHJhaW50U2V2ZXJpdHkpOiB2YWx1ZS52YWx1ZQpkZWZpbmUgZnVuY3Rpb24gVG9TdHJpbmcodmFsdWUgQ29udGFjdFBvaW50U3lzdGVtKTogdmFsdWUudmFsdWUKZGVmaW5lIGZ1bmN0aW9uIFRvU3RyaW5nKHZhbHVlIENvbnRhY3RQb2ludFVzZSk6IHZhbHVlLnZhbHVlCmRlZmluZSBmdW5jdGlvbiBUb1N0cmluZyh2YWx1ZSBDb250cmFjdFB1YmxpY2F0aW9uU3RhdHVzKTogdmFsdWUudmFsdWUKZGVmaW5lIGZ1bmN0aW9uIFRvU3RyaW5nKHZhbHVlIENvbnRyYWN0U3RhdHVzKTogdmFsdWUudmFsdWUKZGVmaW5lIGZ1bmN0aW9uIFRvU3RyaW5nKHZhbHVlIENvbnRyaWJ1dG9yVHlwZSk6IHZhbHVlLnZhbHVlCmRlZmluZSBmdW5jdGlvbiBUb1N0cmluZyh2YWx1ZSBDb3ZlcmFnZVN0YXR1cyk6IHZhbHVlLnZhbHVlCmRlZmluZSBmdW5jdGlvbiBUb1N0cmluZyh2YWx1ZSBDdXJyZW5jeUNvZGUpOiB2YWx1ZS52YWx1ZQpkZWZpbmUgZnVuY3Rpb24gVG9TdHJpbmcodmFsdWUgRGF5T2ZXZWVrKTogdmFsdWUudmFsdWUKZGVmaW5lIGZ1bmN0aW9uIFRvU3RyaW5nKHZhbHVlIERheXNPZldlZWspOiB2YWx1ZS52YWx1ZQpkZWZpbmUgZnVuY3Rpb24gVG9TdHJpbmcodmFsdWUgRGV0ZWN0ZWRJc3N1ZVNldmVyaXR5KTogdmFsdWUudmFsdWUKZGVmaW5lIGZ1bmN0aW9uIFRvU3RyaW5nKHZhbHVlIERldGVjdGVkSXNzdWVTdGF0dXMpOiB2YWx1ZS52YWx1ZQpkZWZpbmUgZnVuY3Rpb24gVG9TdHJpbmcodmFsdWUgRGV2aWNlTWV0cmljQ2FsaWJyYXRpb25TdGF0ZSk6IHZhbHVlLnZhbHVlCmRlZmluZSBmdW5jdGlvbiBUb1N0cmluZyh2YWx1ZSBEZXZpY2VNZXRyaWNDYWxpYnJhdGlvblR5cGUpOiB2YWx1ZS52YWx1ZQpkZWZpbmUgZnVuY3Rpb24gVG9TdHJpbmcodmFsdWUgRGV2aWNlTWV0cmljQ2F0ZWdvcnkpOiB2YWx1ZS52YWx1ZQpkZWZpbmUgZnVuY3Rpb24gVG9TdHJpbmcodmFsdWUgRGV2aWNlTWV0cmljQ29sb3IpOiB2YWx1ZS52YWx1ZQpkZWZpbmUgZnVuY3Rpb24gVG9TdHJpbmcodmFsdWUgRGV2aWNlTWV0cmljT3BlcmF0aW9uYWxTdGF0dXMpOiB2YWx1ZS52YWx1ZQpkZWZpbmUgZnVuY3Rpb24gVG9TdHJpbmcodmFsdWUgRGV2aWNlTmFtZVR5cGUpOiB2YWx1ZS52YWx1ZQpkZWZpbmUgZnVuY3Rpb24gVG9TdHJpbmcodmFsdWUgRGV2aWNlUmVxdWVzdFN0YXR1cyk6IHZhbHVlLnZhbHVlCmRlZmluZSBmdW5jdGlvbiBUb1N0cmluZyh2YWx1ZSBEZXZpY2VVc2VTdGF0ZW1lbnRTdGF0dXMpOiB2YWx1ZS52YWx1ZQpkZWZpbmUgZnVuY3Rpb24gVG9TdHJpbmcodmFsdWUgRGlhZ25vc3RpY1JlcG9ydFN0YXR1cyk6IHZhbHVlLnZhbHVlCmRlZmluZSBmdW5jdGlvbiBUb1N0cmluZyh2YWx1ZSBEaXNjcmltaW5hdG9yVHlwZSk6IHZhbHVlLnZhbHVlCmRlZmluZSBmdW5jdGlvbiBUb1N0cmluZyh2YWx1ZSBEb2N1bWVudENvbmZpZGVudGlhbGl0eSk6IHZhbHVlLnZhbHVlCmRlZmluZSBmdW5jdGlvbiBUb1N0cmluZyh2YWx1ZSBEb2N1bWVudE1vZGUpOiB2YWx1ZS52YWx1ZQpkZWZpbmUgZnVuY3Rpb24gVG9TdHJpbmcodmFsdWUgRG9jdW1lbnRSZWZlcmVuY2VTdGF0dXMpOiB2YWx1ZS52YWx1ZQpkZWZpbmUgZnVuY3Rpb24gVG9TdHJpbmcodmFsdWUgRG9jdW1lbnRSZWxhdGlvbnNoaXBUeXBlKTogdmFsdWUudmFsdWUKZGVmaW5lIGZ1bmN0aW9uIFRvU3RyaW5nKHZhbHVlIEVsaWdpYmlsaXR5UmVxdWVzdFB1cnBvc2UpOiB2YWx1ZS52YWx1ZQpkZWZpbmUgZnVuY3Rpb24gVG9TdHJpbmcodmFsdWUgRWxpZ2liaWxpdHlSZXF1ZXN0U3RhdHVzKTogdmFsdWUudmFsdWUKZGVmaW5lIGZ1bmN0aW9uIFRvU3RyaW5nKHZhbHVlIEVsaWdpYmlsaXR5UmVzcG9uc2VQdXJwb3NlKTogdmFsdWUudmFsdWUKZGVmaW5lIGZ1bmN0aW9uIFRvU3RyaW5nKHZhbHVlIEVsaWdpYmlsaXR5UmVzcG9uc2VTdGF0dXMpOiB2YWx1ZS52YWx1ZQpkZWZpbmUgZnVuY3Rpb24gVG9TdHJpbmcodmFsdWUgRW5hYmxlV2hlbkJlaGF2aW9yKTogdmFsdWUudmFsdWUKZGVmaW5lIGZ1bmN0aW9uIFRvU3RyaW5nKHZhbHVlIEVuY291bnRlckxvY2F0aW9uU3RhdHVzKTogdmFsdWUudmFsdWUKZGVmaW5lIGZ1bmN0aW9uIFRvU3RyaW5nKHZhbHVlIEVuY291bnRlclN0YXR1cyk6IHZhbHVlLnZhbHVlCmRlZmluZSBmdW5jdGlvbiBUb1N0cmluZyh2YWx1ZSBFbmRwb2ludFN0YXR1cyk6IHZhbHVlLnZhbHVlCmRlZmluZSBmdW5jdGlvbiBUb1N0cmluZyh2YWx1ZSBFbnJvbGxtZW50UmVxdWVzdFN0YXR1cyk6IHZhbHVlLnZhbHVlCmRlZmluZSBmdW5jdGlvbiBUb1N0cmluZyh2YWx1ZSBFbnJvbGxtZW50UmVzcG9uc2VTdGF0dXMpOiB2YWx1ZS52YWx1ZQpkZWZpbmUgZnVuY3Rpb24gVG9TdHJpbmcodmFsdWUgRXBpc29kZU9mQ2FyZVN0YXR1cyk6IHZhbHVlLnZhbHVlCmRlZmluZSBmdW5jdGlvbiBUb1N0cmluZyh2YWx1ZSBFdmVudENhcGFiaWxpdHlNb2RlKTogdmFsdWUudmFsdWUKZGVmaW5lIGZ1bmN0aW9uIFRvU3RyaW5nKHZhbHVlIEV2ZW50VGltaW5nKTogdmFsdWUudmFsdWUKZGVmaW5lIGZ1bmN0aW9uIFRvU3RyaW5nKHZhbHVlIEV2aWRlbmNlVmFyaWFibGVUeXBlKTogdmFsdWUudmFsdWUKZGVmaW5lIGZ1bmN0aW9uIFRvU3RyaW5nKHZhbHVlIEV4YW1wbGVTY2VuYXJpb0FjdG9yVHlwZSk6IHZhbHVlLnZhbHVlCmRlZmluZSBmdW5jdGlvbiBUb1N0cmluZyh2YWx1ZSBFeHBsYW5hdGlvbk9mQmVuZWZpdFN0YXR1cyk6IHZhbHVlLnZhbHVlCmRlZmluZSBmdW5jdGlvbiBUb1N0cmluZyh2YWx1ZSBFeHBvc3VyZVN0YXRlKTogdmFsdWUudmFsdWUKZGVmaW5lIGZ1bmN0aW9uIFRvU3RyaW5nKHZhbHVlIEV4dGVuc2lvbkNvbnRleHRUeXBlKTogdmFsdWUudmFsdWUKZGVmaW5lIGZ1bmN0aW9uIFRvU3RyaW5nKHZhbHVlIEZISVJBbGxUeXBlcyk6IHZhbHVlLnZhbHVlCmRlZmluZSBmdW5jdGlvbiBUb1N0cmluZyh2YWx1ZSBGSElSRGVmaW5lZFR5cGUpOiB2YWx1ZS52YWx1ZQpkZWZpbmUgZnVuY3Rpb24gVG9TdHJpbmcodmFsdWUgRkhJUkRldmljZVN0YXR1cyk6IHZhbHVlLnZhbHVlCmRlZmluZSBmdW5jdGlvbiBUb1N0cmluZyh2YWx1ZSBGSElSUmVzb3VyY2VUeXBlKTogdmFsdWUudmFsdWUKZGVmaW5lIGZ1bmN0aW9uIFRvU3RyaW5nKHZhbHVlIEZISVJTdWJzdGFuY2VTdGF0dXMpOiB2YWx1ZS52YWx1ZQpkZWZpbmUgZnVuY3Rpb24gVG9TdHJpbmcodmFsdWUgRkhJUlZlcnNpb24pOiB2YWx1ZS52YWx1ZQpkZWZpbmUgZnVuY3Rpb24gVG9TdHJpbmcodmFsdWUgRmFtaWx5SGlzdG9yeVN0YXR1cyk6IHZhbHVlLnZhbHVlCmRlZmluZSBmdW5jdGlvbiBUb1N0cmluZyh2YWx1ZSBGaWx0ZXJPcGVyYXRvcik6IHZhbHVlLnZhbHVlCmRlZmluZSBmdW5jdGlvbiBUb1N0cmluZyh2YWx1ZSBGbGFnU3RhdHVzKTogdmFsdWUudmFsdWUKZGVmaW5lIGZ1bmN0aW9uIFRvU3RyaW5nKHZhbHVlIEdvYWxMaWZlY3ljbGVTdGF0dXMpOiB2YWx1ZS52YWx1ZQpkZWZpbmUgZnVuY3Rpb24gVG9TdHJpbmcodmFsdWUgR3JhcGhDb21wYXJ0bWVudFJ1bGUpOiB2YWx1ZS52YWx1ZQpkZWZpbmUgZnVuY3Rpb24gVG9TdHJpbmcodmFsdWUgR3JhcGhDb21wYXJ0bWVudFVzZSk6IHZhbHVlLnZhbHVlCmRlZmluZSBmdW5jdGlvbiBUb1N0cmluZyh2YWx1ZSBHcm91cE1lYXN1cmUpOiB2YWx1ZS52YWx1ZQpkZWZpbmUgZnVuY3Rpb24gVG9TdHJpbmcodmFsdWUgR3JvdXBUeXBlKTogdmFsdWUudmFsdWUKZGVmaW5lIGZ1bmN0aW9uIFRvU3RyaW5nKHZhbHVlIEd1aWRhbmNlUmVzcG9uc2VTdGF0dXMpOiB2YWx1ZS52YWx1ZQpkZWZpbmUgZnVuY3Rpb24gVG9TdHJpbmcodmFsdWUgR3VpZGVQYWdlR2VuZXJhdGlvbik6IHZhbHVlLnZhbHVlCmRlZmluZSBmdW5jdGlvbiBUb1N0cmluZyh2YWx1ZSBHdWlkZVBhcmFtZXRlckNvZGUpOiB2YWx1ZS52YWx1ZQpkZWZpbmUgZnVuY3Rpb24gVG9TdHJpbmcodmFsdWUgSFRUUFZlcmIpOiB2YWx1ZS52YWx1ZQpkZWZpbmUgZnVuY3Rpb24gVG9TdHJpbmcodmFsdWUgSWRlbnRpZmllclVzZSk6IHZhbHVlLnZhbHVlCmRlZmluZSBmdW5jdGlvbiBUb1N0cmluZyh2YWx1ZSBJZGVudGl0eUFzc3VyYW5jZUxldmVsKTogdmFsdWUudmFsdWUKZGVmaW5lIGZ1bmN0aW9uIFRvU3RyaW5nKHZhbHVlIEltYWdpbmdTdHVkeVN0YXR1cyk6IHZhbHVlLnZhbHVlCmRlZmluZSBmdW5jdGlvbiBUb1N0cmluZyh2YWx1ZSBJbW11bml6YXRpb25FdmFsdWF0aW9uU3RhdHVzKTogdmFsdWUudmFsdWUKZGVmaW5lIGZ1bmN0aW9uIFRvU3RyaW5nKHZhbHVlIEltbXVuaXphdGlvblN0YXR1cyk6IHZhbHVlLnZhbHVlCmRlZmluZSBmdW5jdGlvbiBUb1N0cmluZyh2YWx1ZSBJbnZvaWNlUHJpY2VDb21wb25lbnRUeXBlKTogdmFsdWUudmFsdWUKZGVmaW5lIGZ1bmN0aW9uIFRvU3RyaW5nKHZhbHVlIEludm9pY2VTdGF0dXMpOiB2YWx1ZS52YWx1ZQpkZWZpbmUgZnVuY3Rpb24gVG9TdHJpbmcodmFsdWUgSXNzdWVTZXZlcml0eSk6IHZhbHVlLnZhbHVlCmRlZmluZSBmdW5jdGlvbiBUb1N0cmluZyh2YWx1ZSBJc3N1ZVR5cGUpOiB2YWx1ZS52YWx1ZQpkZWZpbmUgZnVuY3Rpb24gVG9TdHJpbmcodmFsdWUgTGlua1R5cGUpOiB2YWx1ZS52YWx1ZQpkZWZpbmUgZnVuY3Rpb24gVG9TdHJpbmcodmFsdWUgTGlua2FnZVR5cGUpOiB2YWx1ZS52YWx1ZQpkZWZpbmUgZnVuY3Rpb24gVG9TdHJpbmcodmFsdWUgTGlzdE1vZGUpOiB2YWx1ZS52YWx1ZQpkZWZpbmUgZnVuY3Rpb24gVG9TdHJpbmcodmFsdWUgTGlzdFN0YXR1cyk6IHZhbHVlLnZhbHVlCmRlZmluZSBmdW5jdGlvbiBUb1N0cmluZyh2YWx1ZSBMb2NhdGlvbk1vZGUpOiB2YWx1ZS52YWx1ZQpkZWZpbmUgZnVuY3Rpb24gVG9TdHJpbmcodmFsdWUgTG9jYXRpb25TdGF0dXMpOiB2YWx1ZS52YWx1ZQpkZWZpbmUgZnVuY3Rpb24gVG9TdHJpbmcodmFsdWUgTWVhc3VyZVJlcG9ydFN0YXR1cyk6IHZhbHVlLnZhbHVlCmRlZmluZSBmdW5jdGlvbiBUb1N0cmluZyh2YWx1ZSBNZWFzdXJlUmVwb3J0VHlwZSk6IHZhbHVlLnZhbHVlCmRlZmluZSBmdW5jdGlvbiBUb1N0cmluZyh2YWx1ZSBNZWRpYVN0YXR1cyk6IHZhbHVlLnZhbHVlCmRlZmluZSBmdW5jdGlvbiBUb1N0cmluZyh2YWx1ZSBNZWRpY2F0aW9uQWRtaW5pc3RyYXRpb25TdGF0dXMpOiB2YWx1ZS52YWx1ZQpkZWZpbmUgZnVuY3Rpb24gVG9TdHJpbmcodmFsdWUgTWVkaWNhdGlvbkRpc3BlbnNlU3RhdHVzKTogdmFsdWUudmFsdWUKZGVmaW5lIGZ1bmN0aW9uIFRvU3RyaW5nKHZhbHVlIE1lZGljYXRpb25Lbm93bGVkZ2VTdGF0dXMpOiB2YWx1ZS52YWx1ZQpkZWZpbmUgZnVuY3Rpb24gVG9TdHJpbmcodmFsdWUgTWVkaWNhdGlvblJlcXVlc3RJbnRlbnQpOiB2YWx1ZS52YWx1ZQpkZWZpbmUgZnVuY3Rpb24gVG9TdHJpbmcodmFsdWUgTWVkaWNhdGlvblJlcXVlc3RQcmlvcml0eSk6IHZhbHVlLnZhbHVlCmRlZmluZSBmdW5jdGlvbiBUb1N0cmluZyh2YWx1ZSBNZWRpY2F0aW9uUmVxdWVzdFN0YXR1cyk6IHZhbHVlLnZhbHVlCmRlZmluZSBmdW5jdGlvbiBUb1N0cmluZyh2YWx1ZSBNZWRpY2F0aW9uU3RhdGVtZW50U3RhdHVzKTogdmFsdWUudmFsdWUKZGVmaW5lIGZ1bmN0aW9uIFRvU3RyaW5nKHZhbHVlIE1lZGljYXRpb25TdGF0dXMpOiB2YWx1ZS52YWx1ZQpkZWZpbmUgZnVuY3Rpb24gVG9TdHJpbmcodmFsdWUgTWVzc2FnZVNpZ25pZmljYW5jZUNhdGVnb3J5KTogdmFsdWUudmFsdWUKZGVmaW5lIGZ1bmN0aW9uIFRvU3RyaW5nKHZhbHVlIE1lc3NhZ2VoZWFkZXJfUmVzcG9uc2VfUmVxdWVzdCk6IHZhbHVlLnZhbHVlCmRlZmluZSBmdW5jdGlvbiBUb1N0cmluZyh2YWx1ZSBNaW1lVHlwZSk6IHZhbHVlLnZhbHVlCmRlZmluZSBmdW5jdGlvbiBUb1N0cmluZyh2YWx1ZSBOYW1lVXNlKTogdmFsdWUudmFsdWUKZGVmaW5lIGZ1bmN0aW9uIFRvU3RyaW5nKHZhbHVlIE5hbWluZ1N5c3RlbUlkZW50aWZpZXJUeXBlKTogdmFsdWUudmFsdWUKZGVmaW5lIGZ1bmN0aW9uIFRvU3RyaW5nKHZhbHVlIE5hbWluZ1N5c3RlbVR5cGUpOiB2YWx1ZS52YWx1ZQpkZWZpbmUgZnVuY3Rpb24gVG9TdHJpbmcodmFsdWUgTmFycmF0aXZlU3RhdHVzKTogdmFsdWUudmFsdWUKZGVmaW5lIGZ1bmN0aW9uIFRvU3RyaW5nKHZhbHVlIE5vdGVUeXBlKTogdmFsdWUudmFsdWUKZGVmaW5lIGZ1bmN0aW9uIFRvU3RyaW5nKHZhbHVlIE51dHJpdGlpb25PcmRlckludGVudCk6IHZhbHVlLnZhbHVlCmRlZmluZSBmdW5jdGlvbiBUb1N0cmluZyh2YWx1ZSBOdXRyaXRpb25PcmRlclN0YXR1cyk6IHZhbHVlLnZhbHVlCmRlZmluZSBmdW5jdGlvbiBUb1N0cmluZyh2YWx1ZSBPYnNlcnZhdGlvbkRhdGFUeXBlKTogdmFsdWUudmFsdWUKZGVmaW5lIGZ1bmN0aW9uIFRvU3RyaW5nKHZhbHVlIE9ic2VydmF0aW9uUmFuZ2VDYXRlZ29yeSk6IHZhbHVlLnZhbHVlCmRlZmluZSBmdW5jdGlvbiBUb1N0cmluZyh2YWx1ZSBPYnNlcnZhdGlvblN0YXR1cyk6IHZhbHVlLnZhbHVlCmRlZmluZSBmdW5jdGlvbiBUb1N0cmluZyh2YWx1ZSBPcGVyYXRpb25LaW5kKTogdmFsdWUudmFsdWUKZGVmaW5lIGZ1bmN0aW9uIFRvU3RyaW5nKHZhbHVlIE9wZXJhdGlvblBhcmFtZXRlclVzZSk6IHZhbHVlLnZhbHVlCmRlZmluZSBmdW5jdGlvbiBUb1N0cmluZyh2YWx1ZSBPcmllbnRhdGlvblR5cGUpOiB2YWx1ZS52YWx1ZQpkZWZpbmUgZnVuY3Rpb24gVG9TdHJpbmcodmFsdWUgUGFyYW1ldGVyVXNlKTogdmFsdWUudmFsdWUKZGVmaW5lIGZ1bmN0aW9uIFRvU3RyaW5nKHZhbHVlIFBhcnRpY2lwYW50UmVxdWlyZWQpOiB2YWx1ZS52YWx1ZQpkZWZpbmUgZnVuY3Rpb24gVG9TdHJpbmcodmFsdWUgUGFydGljaXBhbnRTdGF0dXMpOiB2YWx1ZS52YWx1ZQpkZWZpbmUgZnVuY3Rpb24gVG9TdHJpbmcodmFsdWUgUGFydGljaXBhdGlvblN0YXR1cyk6IHZhbHVlLnZhbHVlCmRlZmluZSBmdW5jdGlvbiBUb1N0cmluZyh2YWx1ZSBQYXltZW50Tm90aWNlU3RhdHVzKTogdmFsdWUudmFsdWUKZGVmaW5lIGZ1bmN0aW9uIFRvU3RyaW5nKHZhbHVlIFBheW1lbnRSZWNvbmNpbGlhdGlvblN0YXR1cyk6IHZhbHVlLnZhbHVlCmRlZmluZSBmdW5jdGlvbiBUb1N0cmluZyh2YWx1ZSBQcm9jZWR1cmVTdGF0dXMpOiB2YWx1ZS52YWx1ZQpkZWZpbmUgZnVuY3Rpb24gVG9TdHJpbmcodmFsdWUgUHJvcGVydHlSZXByZXNlbnRhdGlvbik6IHZhbHVlLnZhbHVlCmRlZmluZSBmdW5jdGlvbiBUb1N0cmluZyh2YWx1ZSBQcm9wZXJ0eVR5cGUpOiB2YWx1ZS52YWx1ZQpkZWZpbmUgZnVuY3Rpb24gVG9TdHJpbmcodmFsdWUgUHJvdmVuYW5jZUVudGl0eVJvbGUpOiB2YWx1ZS52YWx1ZQpkZWZpbmUgZnVuY3Rpb24gVG9TdHJpbmcodmFsdWUgUHVibGljYXRpb25TdGF0dXMpOiB2YWx1ZS52YWx1ZQpkZWZpbmUgZnVuY3Rpb24gVG9TdHJpbmcodmFsdWUgUXVhbGl0eVR5cGUpOiB2YWx1ZS52YWx1ZQpkZWZpbmUgZnVuY3Rpb24gVG9TdHJpbmcodmFsdWUgUXVhbnRpdHlDb21wYXJhdG9yKTogdmFsdWUudmFsdWUKZGVmaW5lIGZ1bmN0aW9uIFRvU3RyaW5nKHZhbHVlIFF1ZXN0aW9ubmFpcmVJdGVtT3BlcmF0b3IpOiB2YWx1ZS52YWx1ZQpkZWZpbmUgZnVuY3Rpb24gVG9TdHJpbmcodmFsdWUgUXVlc3Rpb25uYWlyZUl0ZW1UeXBlKTogdmFsdWUudmFsdWUKZGVmaW5lIGZ1bmN0aW9uIFRvU3RyaW5nKHZhbHVlIFF1ZXN0aW9ubmFpcmVSZXNwb25zZVN0YXR1cyk6IHZhbHVlLnZhbHVlCmRlZmluZSBmdW5jdGlvbiBUb1N0cmluZyh2YWx1ZSBSZWZlcmVuY2VIYW5kbGluZ1BvbGljeSk6IHZhbHVlLnZhbHVlCmRlZmluZSBmdW5jdGlvbiBUb1N0cmluZyh2YWx1ZSBSZWZlcmVuY2VWZXJzaW9uUnVsZXMpOiB2YWx1ZS52YWx1ZQpkZWZpbmUgZnVuY3Rpb24gVG9TdHJpbmcodmFsdWUgUmVmZXJyZWREb2N1bWVudFN0YXR1cyk6IHZhbHVlLnZhbHVlCmRlZmluZSBmdW5jdGlvbiBUb1N0cmluZyh2YWx1ZSBSZWxhdGVkQXJ0aWZhY3RUeXBlKTogdmFsdWUudmFsdWUKZGVmaW5lIGZ1bmN0aW9uIFRvU3RyaW5nKHZhbHVlIFJlbWl0dGFuY2VPdXRjb21lKTogdmFsdWUudmFsdWUKZGVmaW5lIGZ1bmN0aW9uIFRvU3RyaW5nKHZhbHVlIFJlcG9zaXRvcnlUeXBlKTogdmFsdWUudmFsdWUKZGVmaW5lIGZ1bmN0aW9uIFRvU3RyaW5nKHZhbHVlIFJlcXVlc3RJbnRlbnQpOiB2YWx1ZS52YWx1ZQpkZWZpbmUgZnVuY3Rpb24gVG9TdHJpbmcodmFsdWUgUmVxdWVzdFByaW9yaXR5KTogdmFsdWUudmFsdWUKZGVmaW5lIGZ1bmN0aW9uIFRvU3RyaW5nKHZhbHVlIFJlcXVlc3RTdGF0dXMpOiB2YWx1ZS52YWx1ZQpkZWZpbmUgZnVuY3Rpb24gVG9TdHJpbmcodmFsdWUgUmVzZWFyY2hFbGVtZW50VHlwZSk6IHZhbHVlLnZhbHVlCmRlZmluZSBmdW5jdGlvbiBUb1N0cmluZyh2YWx1ZSBSZXNlYXJjaFN0dWR5U3RhdHVzKTogdmFsdWUudmFsdWUKZGVmaW5lIGZ1bmN0aW9uIFRvU3RyaW5nKHZhbHVlIFJlc2VhcmNoU3ViamVjdFN0YXR1cyk6IHZhbHVlLnZhbHVlCmRlZmluZSBmdW5jdGlvbiBUb1N0cmluZyh2YWx1ZSBSZXNvdXJjZVR5cGUpOiB2YWx1ZS52YWx1ZQpkZWZpbmUgZnVuY3Rpb24gVG9TdHJpbmcodmFsdWUgUmVzb3VyY2VWZXJzaW9uUG9saWN5KTogdmFsdWUudmFsdWUKZGVmaW5lIGZ1bmN0aW9uIFRvU3RyaW5nKHZhbHVlIFJlc3BvbnNlVHlwZSk6IHZhbHVlLnZhbHVlCmRlZmluZSBmdW5jdGlvbiBUb1N0cmluZyh2YWx1ZSBSZXN0ZnVsQ2FwYWJpbGl0eU1vZGUpOiB2YWx1ZS52YWx1ZQpkZWZpbmUgZnVuY3Rpb24gVG9TdHJpbmcodmFsdWUgUmlza0Fzc2Vzc21lbnRTdGF0dXMpOiB2YWx1ZS52YWx1ZQpkZWZpbmUgZnVuY3Rpb24gVG9TdHJpbmcodmFsdWUgU1BEWExpY2Vuc2UpOiB2YWx1ZS52YWx1ZQpkZWZpbmUgZnVuY3Rpb24gVG9TdHJpbmcodmFsdWUgU2VhcmNoQ29tcGFyYXRvcik6IHZhbHVlLnZhbHVlCmRlZmluZSBmdW5jdGlvbiBUb1N0cmluZyh2YWx1ZSBTZWFyY2hFbnRyeU1vZGUpOiB2YWx1ZS52YWx1ZQpkZWZpbmUgZnVuY3Rpb24gVG9TdHJpbmcodmFsdWUgU2VhcmNoTW9kaWZpZXJDb2RlKTogdmFsdWUudmFsdWUKZGVmaW5lIGZ1bmN0aW9uIFRvU3RyaW5nKHZhbHVlIFNlYXJjaFBhcmFtVHlwZSk6IHZhbHVlLnZhbHVlCmRlZmluZSBmdW5jdGlvbiBUb1N0cmluZyh2YWx1ZSBTZWN0aW9uTW9kZSk6IHZhbHVlLnZhbHVlCmRlZmluZSBmdW5jdGlvbiBUb1N0cmluZyh2YWx1ZSBTZXF1ZW5jZVR5cGUpOiB2YWx1ZS52YWx1ZQpkZWZpbmUgZnVuY3Rpb24gVG9TdHJpbmcodmFsdWUgU2VydmljZVJlcXVlc3RJbnRlbnQpOiB2YWx1ZS52YWx1ZQpkZWZpbmUgZnVuY3Rpb24gVG9TdHJpbmcodmFsdWUgU2VydmljZVJlcXVlc3RQcmlvcml0eSk6IHZhbHVlLnZhbHVlCmRlZmluZSBmdW5jdGlvbiBUb1N0cmluZyh2YWx1ZSBTZXJ2aWNlUmVxdWVzdFN0YXR1cyk6IHZhbHVlLnZhbHVlCmRlZmluZSBmdW5jdGlvbiBUb1N0cmluZyh2YWx1ZSBTbGljaW5nUnVsZXMpOiB2YWx1ZS52YWx1ZQpkZWZpbmUgZnVuY3Rpb24gVG9TdHJpbmcodmFsdWUgU2xvdFN0YXR1cyk6IHZhbHVlLnZhbHVlCmRlZmluZSBmdW5jdGlvbiBUb1N0cmluZyh2YWx1ZSBTb3J0RGlyZWN0aW9uKTogdmFsdWUudmFsdWUKZGVmaW5lIGZ1bmN0aW9uIFRvU3RyaW5nKHZhbHVlIFNwZWNpbWVuQ29udGFpbmVkUHJlZmVyZW5jZSk6IHZhbHVlLnZhbHVlCmRlZmluZSBmdW5jdGlvbiBUb1N0cmluZyh2YWx1ZSBTcGVjaW1lblN0YXR1cyk6IHZhbHVlLnZhbHVlCmRlZmluZSBmdW5jdGlvbiBUb1N0cmluZyh2YWx1ZSBTdGF0dXMpOiB2YWx1ZS52YWx1ZQpkZWZpbmUgZnVuY3Rpb24gVG9TdHJpbmcodmFsdWUgU3RyYW5kVHlwZSk6IHZhbHVlLnZhbHVlCmRlZmluZSBmdW5jdGlvbiBUb1N0cmluZyh2YWx1ZSBTdHJ1Y3R1cmVEZWZpbml0aW9uS2luZCk6IHZhbHVlLnZhbHVlCmRlZmluZSBmdW5jdGlvbiBUb1N0cmluZyh2YWx1ZSBTdHJ1Y3R1cmVNYXBDb250ZXh0VHlwZSk6IHZhbHVlLnZhbHVlCmRlZmluZSBmdW5jdGlvbiBUb1N0cmluZyh2YWx1ZSBTdHJ1Y3R1cmVNYXBHcm91cFR5cGVNb2RlKTogdmFsdWUudmFsdWUKZGVmaW5lIGZ1bmN0aW9uIFRvU3RyaW5nKHZhbHVlIFN0cnVjdHVyZU1hcElucHV0TW9kZSk6IHZhbHVlLnZhbHVlCmRlZmluZSBmdW5jdGlvbiBUb1N0cmluZyh2YWx1ZSBTdHJ1Y3R1cmVNYXBNb2RlbE1vZGUpOiB2YWx1ZS52YWx1ZQpkZWZpbmUgZnVuY3Rpb24gVG9TdHJpbmcodmFsdWUgU3RydWN0dXJlTWFwU291cmNlTGlzdE1vZGUpOiB2YWx1ZS52YWx1ZQpkZWZpbmUgZnVuY3Rpb24gVG9TdHJpbmcodmFsdWUgU3RydWN0dXJlTWFwVGFyZ2V0TGlzdE1vZGUpOiB2YWx1ZS52YWx1ZQpkZWZpbmUgZnVuY3Rpb24gVG9TdHJpbmcodmFsdWUgU3RydWN0dXJlTWFwVHJhbnNmb3JtKTogdmFsdWUudmFsdWUKZGVmaW5lIGZ1bmN0aW9uIFRvU3RyaW5nKHZhbHVlIFN1YnNjcmlwdGlvbkNoYW5uZWxUeXBlKTogdmFsdWUudmFsdWUKZGVmaW5lIGZ1bmN0aW9uIFRvU3RyaW5nKHZhbHVlIFN1YnNjcmlwdGlvblN0YXR1cyk6IHZhbHVlLnZhbHVlCmRlZmluZSBmdW5jdGlvbiBUb1N0cmluZyh2YWx1ZSBTdXBwbHlEZWxpdmVyeVN0YXR1cyk6IHZhbHVlLnZhbHVlCmRlZmluZSBmdW5jdGlvbiBUb1N0cmluZyh2YWx1ZSBTdXBwbHlSZXF1ZXN0U3RhdHVzKTogdmFsdWUudmFsdWUKZGVmaW5lIGZ1bmN0aW9uIFRvU3RyaW5nKHZhbHVlIFN5c3RlbVJlc3RmdWxJbnRlcmFjdGlvbik6IHZhbHVlLnZhbHVlCmRlZmluZSBmdW5jdGlvbiBUb1N0cmluZyh2YWx1ZSBUYXNrSW50ZW50KTogdmFsdWUudmFsdWUKZGVmaW5lIGZ1bmN0aW9uIFRvU3RyaW5nKHZhbHVlIFRhc2tQcmlvcml0eSk6IHZhbHVlLnZhbHVlCmRlZmluZSBmdW5jdGlvbiBUb1N0cmluZyh2YWx1ZSBUYXNrU3RhdHVzKTogdmFsdWUudmFsdWUKZGVmaW5lIGZ1bmN0aW9uIFRvU3RyaW5nKHZhbHVlIFRlc3RSZXBvcnRBY3Rpb25SZXN1bHQpOiB2YWx1ZS52YWx1ZQpkZWZpbmUgZnVuY3Rpb24gVG9TdHJpbmcodmFsdWUgVGVzdFJlcG9ydFBhcnRpY2lwYW50VHlwZSk6IHZhbHVlLnZhbHVlCmRlZmluZSBmdW5jdGlvbiBUb1N0cmluZyh2YWx1ZSBUZXN0UmVwb3J0UmVzdWx0KTogdmFsdWUudmFsdWUKZGVmaW5lIGZ1bmN0aW9uIFRvU3RyaW5nKHZhbHVlIFRlc3RSZXBvcnRTdGF0dXMpOiB2YWx1ZS52YWx1ZQpkZWZpbmUgZnVuY3Rpb24gVG9TdHJpbmcodmFsdWUgVGVzdFNjcmlwdFJlcXVlc3RNZXRob2RDb2RlKTogdmFsdWUudmFsdWUKZGVmaW5lIGZ1bmN0aW9uIFRvU3RyaW5nKHZhbHVlIFRyaWdnZXJUeXBlKTogdmFsdWUudmFsdWUKZGVmaW5lIGZ1bmN0aW9uIFRvU3RyaW5nKHZhbHVlIFR5cGVEZXJpdmF0aW9uUnVsZSk6IHZhbHVlLnZhbHVlCmRlZmluZSBmdW5jdGlvbiBUb1N0cmluZyh2YWx1ZSBUeXBlUmVzdGZ1bEludGVyYWN0aW9uKTogdmFsdWUudmFsdWUKZGVmaW5lIGZ1bmN0aW9uIFRvU3RyaW5nKHZhbHVlIFVESUVudHJ5VHlwZSk6IHZhbHVlLnZhbHVlCmRlZmluZSBmdW5jdGlvbiBUb1N0cmluZyh2YWx1ZSBVbml0c09mVGltZSk6IHZhbHVlLnZhbHVlCmRlZmluZSBmdW5jdGlvbiBUb1N0cmluZyh2YWx1ZSBVc2UpOiB2YWx1ZS52YWx1ZQpkZWZpbmUgZnVuY3Rpb24gVG9TdHJpbmcodmFsdWUgVmFyaWFibGVUeXBlKTogdmFsdWUudmFsdWUKZGVmaW5lIGZ1bmN0aW9uIFRvU3RyaW5nKHZhbHVlIFZpc2lvbkJhc2UpOiB2YWx1ZS52YWx1ZQpkZWZpbmUgZnVuY3Rpb24gVG9TdHJpbmcodmFsdWUgVmlzaW9uRXllcyk6IHZhbHVlLnZhbHVlCmRlZmluZSBmdW5jdGlvbiBUb1N0cmluZyh2YWx1ZSBWaXNpb25TdGF0dXMpOiB2YWx1ZS52YWx1ZQpkZWZpbmUgZnVuY3Rpb24gVG9TdHJpbmcodmFsdWUgWFBhdGhVc2FnZVR5cGUpOiB2YWx1ZS52YWx1ZQpkZWZpbmUgZnVuY3Rpb24gVG9TdHJpbmcodmFsdWUgYmFzZTY0QmluYXJ5KTogdmFsdWUudmFsdWUKZGVmaW5lIGZ1bmN0aW9uIFRvQm9vbGVhbih2YWx1ZSBib29sZWFuKTogdmFsdWUudmFsdWUKZGVmaW5lIGZ1bmN0aW9uIFRvRGF0ZSh2YWx1ZSBkYXRlKTogdmFsdWUudmFsdWUKZGVmaW5lIGZ1bmN0aW9uIFRvRGF0ZVRpbWUodmFsdWUgZGF0ZVRpbWUpOiB2YWx1ZS52YWx1ZQpkZWZpbmUgZnVuY3Rpb24gVG9EZWNpbWFsKHZhbHVlIGRlY2ltYWwpOiB2YWx1ZS52YWx1ZQpkZWZpbmUgZnVuY3Rpb24gVG9EYXRlVGltZSh2YWx1ZSBpbnN0YW50KTogdmFsdWUudmFsdWUKZGVmaW5lIGZ1bmN0aW9uIFRvSW50ZWdlcih2YWx1ZSBpbnRlZ2VyKTogdmFsdWUudmFsdWUKZGVmaW5lIGZ1bmN0aW9uIFRvU3RyaW5nKHZhbHVlIHN0cmluZyk6IHZhbHVlLnZhbHVlCmRlZmluZSBmdW5jdGlvbiBUb1RpbWUodmFsdWUgdGltZSk6IHZhbHVlLnZhbHVlCmRlZmluZSBmdW5jdGlvbiBUb1N0cmluZyh2YWx1ZSB1cmkpOiB2YWx1ZS52YWx1ZQpkZWZpbmUgZnVuY3Rpb24gVG9TdHJpbmcodmFsdWUgeGh0bWwpOiB2YWx1ZS52YWx1ZQo=" + }, + { + "contentType": "application/elm+xml", + "data": "PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0iVVRGLTgiPz4KPGxpYnJhcnkgeG1sbnM9InVybjpobDctb3JnOmVsbTpyMSIgeG1sbnM6dD0idXJuOmhsNy1vcmc6ZWxtLXR5cGVzOnIxIiB4bWxuczp4c2k9Imh0dHA6Ly93d3cudzMub3JnLzIwMDEvWE1MU2NoZW1hLWluc3RhbmNlIiB4bWxuczp4c2Q9Imh0dHA6Ly93d3cudzMub3JnLzIwMDEvWE1MU2NoZW1hIiB4bWxuczpmaGlyPSJodHRwOi8vaGw3Lm9yZy9maGlyIiB4bWxuczpxZG00Mz0idXJuOmhlYWx0aGl0LWdvdjpxZG06djRfMyIgeG1sbnM6cWRtNTM9InVybjpoZWFsdGhpdC1nb3Y6cWRtOnY1XzMiIHhtbG5zOmE9InVybjpobDctb3JnOmNxbC1hbm5vdGF0aW9uczpyMSI+CiAgIDxhbm5vdGF0aW9uIHRyYW5zbGF0b3JWZXJzaW9uPSIyLjcuMCIgdHJhbnNsYXRvck9wdGlvbnM9IkVuYWJsZUxvY2F0b3JzLERpc2FibGVMaXN0RGVtb3Rpb24sRGlzYWJsZUxpc3RQcm9tb3Rpb24iIHhzaTp0eXBlPSJhOkNxbFRvRWxtSW5mbyIvPgogICA8YW5ub3RhdGlvbiB4c2k6dHlwZT0iYTpBbm5vdGF0aW9uIj4KICAgICAgPGE6dCBuYW1lPSJhdXRob3IiIHZhbHVlPSJCcnluIFJob2RlcyIvPgogICAgICA8YTp0IG5hbWU9ImRlc2NyaXB0aW9uIiB2YWx1ZT0iVGhpcyBsaWJyYXJ5IGRlZmluZXMgZnVuY3Rpb25zIHRvIGNvbnZlcnQgYmV0d2VlbiBGSElSJiN4YTsgZGF0YSB0eXBlcyBhbmQgQ1FMIHN5c3RlbS1kZWZpbmVkIHR5cGVzLCBhcyB3ZWxsIGFzIGZ1bmN0aW9ucyB0byBzdXBwb3J0JiN4YTsgRkhJUlBhdGggaW1wbGVtZW50YXRpb24uIEZvciBtb3JlIGluZm9ybWF0aW9uLCBzZWUgdGhlIEZISVJIZWxwZXJzIHdpa2kgcGFnZTomI3hhOyBodHRwczovL2dpdGh1Yi5jb20vY3FmcmFtZXdvcmsvY2xpbmljYWxfcXVhbGl0eV9sYW5ndWFnZS93aWtpL0ZISVJIZWxwZXJzIi8+CiAgICAgIDxhOnQgbmFtZT0iYWxsb3dGbHVlbnQiIHZhbHVlPSJ0cnVlIi8+CiAgIDwvYW5ub3RhdGlvbj4KICAgPGlkZW50aWZpZXIgaWQ9IkZISVJIZWxwZXJzIiBzeXN0ZW09Imh0dHA6Ly9jb250ZW50LmFscGhvcmEuY29tL2ZoaXIvZHFtIiB2ZXJzaW9uPSI0LjAuMSIvPgogICA8c2NoZW1hSWRlbnRpZmllciBpZD0idXJuOmhsNy1vcmc6ZWxtIiB2ZXJzaW9uPSJyMSIvPgogICA8dXNpbmdzPgogICAgICA8ZGVmIGxvY2FsSWRlbnRpZmllcj0iU3lzdGVtIiB1cmk9InVybjpobDctb3JnOmVsbS10eXBlczpyMSIvPgogICAgICA8ZGVmIGxvY2F0b3I9IjExOjEtMTE6MjYiIGxvY2FsSWRlbnRpZmllcj0iRkhJUiIgdXJpPSJodHRwOi8vaGw3Lm9yZy9maGlyIiB2ZXJzaW9uPSI0LjAuMSIvPgogICA8L3VzaW5ncz4KICAgPHN0YXRlbWVudHM+CiAgICAgIDxkZWYgbG9jYXRvcj0iMTM6MS0yMDo2MiIgbmFtZT0iVG9JbnRlcnZhbCIgY29udGV4dD0iUGF0aWVudCIgYWNjZXNzTGV2ZWw9IlB1YmxpYyIgeHNpOnR5cGU9IkZ1bmN0aW9uRGVmIj4KICAgICAgICAgPGV4cHJlc3Npb24gbG9jYXRvcj0iMTQ6NS0yMDo2MiIgeHNpOnR5cGU9IklmIj4KICAgICAgICAgICAgPGNvbmRpdGlvbiBsb2NhdG9yPSIxNDo4LTE0OjIxIiB4c2k6dHlwZT0iSXNOdWxsIj4KICAgICAgICAgICAgICAgPG9wZXJhbmQgbG9jYXRvcj0iMTQ6OC0xNDoxMyIgbmFtZT0icGVyaW9kIiB4c2k6dHlwZT0iT3BlcmFuZFJlZiIvPgogICAgICAgICAgICA8L2NvbmRpdGlvbj4KICAgICAgICAgICAgPHRoZW4geHNpOnR5cGU9IkFzIj4KICAgICAgICAgICAgICAgPG9wZXJhbmQgbG9jYXRvcj0iMTU6OS0xNToxMiIgeHNpOnR5cGU9Ik51bGwiLz4KICAgICAgICAgICAgICAgPGFzVHlwZVNwZWNpZmllciB4c2k6dHlwZT0iSW50ZXJ2YWxUeXBlU3BlY2lmaWVyIj4KICAgICAgICAgICAgICAgICAgPHBvaW50VHlwZSBuYW1lPSJ0OkRhdGVUaW1lIiB4c2k6dHlwZT0iTmFtZWRUeXBlU3BlY2lmaWVyIi8+CiAgICAgICAgICAgICAgIDwvYXNUeXBlU3BlY2lmaWVyPgogICAgICAgICAgICA8L3RoZW4+CiAgICAgICAgICAgIDxlbHNlIGxvY2F0b3I9IjE3OjktMjA6NjIiIHhzaTp0eXBlPSJJZiI+CiAgICAgICAgICAgICAgIDxjb25kaXRpb24gbG9jYXRvcj0iMTc6MTItMTc6MzMiIHhzaTp0eXBlPSJJc051bGwiPgogICAgICAgICAgICAgICAgICA8b3BlcmFuZCBsb2NhdG9yPSIxNzoxMi0xNzoyNSIgcGF0aD0ic3RhcnQiIHhzaTp0eXBlPSJQcm9wZXJ0eSI+CiAgICAgICAgICAgICAgICAgICAgIDxzb3VyY2UgbG9jYXRvcj0iMTc6MTItMTc6MTciIG5hbWU9InBlcmlvZCIgeHNpOnR5cGU9Ik9wZXJhbmRSZWYiLz4KICAgICAgICAgICAgICAgICAgPC9vcGVyYW5kPgogICAgICAgICAgICAgICA8L2NvbmRpdGlvbj4KICAgICAgICAgICAgICAgPHRoZW4gbG9jYXRvcj0iMTg6MTMtMTg6NjIiIGxvd0Nsb3NlZD0iZmFsc2UiIGhpZ2hDbG9zZWQ9InRydWUiIHhzaTp0eXBlPSJJbnRlcnZhbCI+CiAgICAgICAgICAgICAgICAgIDxsb3cgbG9jYXRvcj0iMTg6MjItMTg6NDEiIHBhdGg9InZhbHVlIiB4c2k6dHlwZT0iUHJvcGVydHkiPgogICAgICAgICAgICAgICAgICAgICA8c291cmNlIGxvY2F0b3I9IjE4OjIyLTE4OjM1IiBwYXRoPSJzdGFydCIgeHNpOnR5cGU9IlByb3BlcnR5Ij4KICAgICAgICAgICAgICAgICAgICAgICAgPHNvdXJjZSBsb2NhdG9yPSIxODoyMi0xODoyNyIgbmFtZT0icGVyaW9kIiB4c2k6dHlwZT0iT3BlcmFuZFJlZiIvPgogICAgICAgICAgICAgICAgICAgICA8L3NvdXJjZT4KICAgICAgICAgICAgICAgICAgPC9sb3c+CiAgICAgICAgICAgICAgICAgIDxoaWdoIGxvY2F0b3I9IjE4OjQ0LTE4OjYxIiBwYXRoPSJ2YWx1ZSIgeHNpOnR5cGU9IlByb3BlcnR5Ij4KICAgICAgICAgICAgICAgICAgICAgPHNvdXJjZSBsb2NhdG9yPSIxODo0NC0xODo1NSIgcGF0aD0iZW5kIiB4c2k6dHlwZT0iUHJvcGVydHkiPgogICAgICAgICAgICAgICAgICAgICAgICA8c291cmNlIGxvY2F0b3I9IjE4OjQ0LTE4OjQ5IiBuYW1lPSJwZXJpb2QiIHhzaTp0eXBlPSJPcGVyYW5kUmVmIi8+CiAgICAgICAgICAgICAgICAgICAgIDwvc291cmNlPgogICAgICAgICAgICAgICAgICA8L2hpZ2g+CiAgICAgICAgICAgICAgIDwvdGhlbj4KICAgICAgICAgICAgICAgPGVsc2UgbG9jYXRvcj0iMjA6MTMtMjA6NjIiIGxvd0Nsb3NlZD0idHJ1ZSIgaGlnaENsb3NlZD0idHJ1ZSIgeHNpOnR5cGU9IkludGVydmFsIj4KICAgICAgICAgICAgICAgICAgPGxvdyBsb2NhdG9yPSIyMDoyMi0yMDo0MSIgcGF0aD0idmFsdWUiIHhzaTp0eXBlPSJQcm9wZXJ0eSI+CiAgICAgICAgICAgICAgICAgICAgIDxzb3VyY2UgbG9jYXRvcj0iMjA6MjItMjA6MzUiIHBhdGg9InN0YXJ0IiB4c2k6dHlwZT0iUHJvcGVydHkiPgogICAgICAgICAgICAgICAgICAgICAgICA8c291cmNlIGxvY2F0b3I9IjIwOjIyLTIwOjI3IiBuYW1lPSJwZXJpb2QiIHhzaTp0eXBlPSJPcGVyYW5kUmVmIi8+CiAgICAgICAgICAgICAgICAgICAgIDwvc291cmNlPgogICAgICAgICAgICAgICAgICA8L2xvdz4KICAgICAgICAgICAgICAgICAgPGhpZ2ggbG9jYXRvcj0iMjA6NDQtMjA6NjEiIHBhdGg9InZhbHVlIiB4c2k6dHlwZT0iUHJvcGVydHkiPgogICAgICAgICAgICAgICAgICAgICA8c291cmNlIGxvY2F0b3I9IjIwOjQ0LTIwOjU1IiBwYXRoPSJlbmQiIHhzaTp0eXBlPSJQcm9wZXJ0eSI+CiAgICAgICAgICAgICAgICAgICAgICAgIDxzb3VyY2UgbG9jYXRvcj0iMjA6NDQtMjA6NDkiIG5hbWU9InBlcmlvZCIgeHNpOnR5cGU9Ik9wZXJhbmRSZWYiLz4KICAgICAgICAgICAgICAgICAgICAgPC9zb3VyY2U+CiAgICAgICAgICAgICAgICAgIDwvaGlnaD4KICAgICAgICAgICAgICAgPC9lbHNlPgogICAgICAgICAgICA8L2Vsc2U+CiAgICAgICAgIDwvZXhwcmVzc2lvbj4KICAgICAgICAgPG9wZXJhbmQgbmFtZT0icGVyaW9kIj4KICAgICAgICAgICAgPG9wZXJhbmRUeXBlU3BlY2lmaWVyIGxvY2F0b3I9IjEzOjM1LTEzOjQ1IiBuYW1lPSJmaGlyOlBlcmlvZCIgeHNpOnR5cGU9Ik5hbWVkVHlwZVNwZWNpZmllciIvPgogICAgICAgICA8L29wZXJhbmQ+CiAgICAgIDwvZGVmPgogICAgICA8ZGVmIGxvY2F0b3I9IjIyOjEtMzM6NyIgbmFtZT0iVG9DYWxlbmRhclVuaXQiIGNvbnRleHQ9IlBhdGllbnQiIGFjY2Vzc0xldmVsPSJQdWJsaWMiIHhzaTp0eXBlPSJGdW5jdGlvbkRlZiI+CiAgICAgICAgIDxleHByZXNzaW9uIGxvY2F0b3I9IjIzOjUtMzM6NyIgeHNpOnR5cGU9IkNhc2UiPgogICAgICAgICAgICA8Y29tcGFyYW5kIGxvY2F0b3I9IjIzOjEwLTIzOjEzIiBuYW1lPSJ1bml0IiB4c2k6dHlwZT0iT3BlcmFuZFJlZiIvPgogICAgICAgICAgICA8Y2FzZUl0ZW0gbG9jYXRvcj0iMjQ6OS0yNDozNiI+CiAgICAgICAgICAgICAgIDx3aGVuIGxvY2F0b3I9IjI0OjE0LTI0OjE3IiB2YWx1ZVR5cGU9InQ6U3RyaW5nIiB2YWx1ZT0ibXMiIHhzaTp0eXBlPSJMaXRlcmFsIi8+CiAgICAgICAgICAgICAgIDx0aGVuIGxvY2F0b3I9IjI0OjI0LTI0OjM2IiB2YWx1ZVR5cGU9InQ6U3RyaW5nIiB2YWx1ZT0ibWlsbGlzZWNvbmQiIHhzaTp0eXBlPSJMaXRlcmFsIi8+CiAgICAgICAgICAgIDwvY2FzZUl0ZW0+CiAgICAgICAgICAgIDxjYXNlSXRlbSBsb2NhdG9yPSIyNTo5LTI1OjMwIj4KICAgICAgICAgICAgICAgPHdoZW4gbG9jYXRvcj0iMjU6MTQtMjU6MTYiIHZhbHVlVHlwZT0idDpTdHJpbmciIHZhbHVlPSJzIiB4c2k6dHlwZT0iTGl0ZXJhbCIvPgogICAgICAgICAgICAgICA8dGhlbiBsb2NhdG9yPSIyNToyMy0yNTozMCIgdmFsdWVUeXBlPSJ0OlN0cmluZyIgdmFsdWU9InNlY29uZCIgeHNpOnR5cGU9IkxpdGVyYWwiLz4KICAgICAgICAgICAgPC9jYXNlSXRlbT4KICAgICAgICAgICAgPGNhc2VJdGVtIGxvY2F0b3I9IjI2OjktMjY6MzIiPgogICAgICAgICAgICAgICA8d2hlbiBsb2NhdG9yPSIyNjoxNC0yNjoxOCIgdmFsdWVUeXBlPSJ0OlN0cmluZyIgdmFsdWU9Im1pbiIgeHNpOnR5cGU9IkxpdGVyYWwiLz4KICAgICAgICAgICAgICAgPHRoZW4gbG9jYXRvcj0iMjY6MjUtMjY6MzIiIHZhbHVlVHlwZT0idDpTdHJpbmciIHZhbHVlPSJtaW51dGUiIHhzaTp0eXBlPSJMaXRlcmFsIi8+CiAgICAgICAgICAgIDwvY2FzZUl0ZW0+CiAgICAgICAgICAgIDxjYXNlSXRlbSBsb2NhdG9yPSIyNzo5LTI3OjI4Ij4KICAgICAgICAgICAgICAgPHdoZW4gbG9jYXRvcj0iMjc6MTQtMjc6MTYiIHZhbHVlVHlwZT0idDpTdHJpbmciIHZhbHVlPSJoIiB4c2k6dHlwZT0iTGl0ZXJhbCIvPgogICAgICAgICAgICAgICA8dGhlbiBsb2NhdG9yPSIyNzoyMy0yNzoyOCIgdmFsdWVUeXBlPSJ0OlN0cmluZyIgdmFsdWU9ImhvdXIiIHhzaTp0eXBlPSJMaXRlcmFsIi8+CiAgICAgICAgICAgIDwvY2FzZUl0ZW0+CiAgICAgICAgICAgIDxjYXNlSXRlbSBsb2NhdG9yPSIyODo5LTI4OjI3Ij4KICAgICAgICAgICAgICAgPHdoZW4gbG9jYXRvcj0iMjg6MTQtMjg6MTYiIHZhbHVlVHlwZT0idDpTdHJpbmciIHZhbHVlPSJkIiB4c2k6dHlwZT0iTGl0ZXJhbCIvPgogICAgICAgICAgICAgICA8dGhlbiBsb2NhdG9yPSIyODoyMy0yODoyNyIgdmFsdWVUeXBlPSJ0OlN0cmluZyIgdmFsdWU9ImRheSIgeHNpOnR5cGU9IkxpdGVyYWwiLz4KICAgICAgICAgICAgPC9jYXNlSXRlbT4KICAgICAgICAgICAgPGNhc2VJdGVtIGxvY2F0b3I9IjI5OjktMjk6MjkiPgogICAgICAgICAgICAgICA8d2hlbiBsb2NhdG9yPSIyOToxNC0yOToxNyIgdmFsdWVUeXBlPSJ0OlN0cmluZyIgdmFsdWU9IndrIiB4c2k6dHlwZT0iTGl0ZXJhbCIvPgogICAgICAgICAgICAgICA8dGhlbiBsb2NhdG9yPSIyOToyNC0yOToyOSIgdmFsdWVUeXBlPSJ0OlN0cmluZyIgdmFsdWU9IndlZWsiIHhzaTp0eXBlPSJMaXRlcmFsIi8+CiAgICAgICAgICAgIDwvY2FzZUl0ZW0+CiAgICAgICAgICAgIDxjYXNlSXRlbSBsb2NhdG9yPSIzMDo5LTMwOjMwIj4KICAgICAgICAgICAgICAgPHdoZW4gbG9jYXRvcj0iMzA6MTQtMzA6MTciIHZhbHVlVHlwZT0idDpTdHJpbmciIHZhbHVlPSJtbyIgeHNpOnR5cGU9IkxpdGVyYWwiLz4KICAgICAgICAgICAgICAgPHRoZW4gbG9jYXRvcj0iMzA6MjQtMzA6MzAiIHZhbHVlVHlwZT0idDpTdHJpbmciIHZhbHVlPSJtb250aCIgeHNpOnR5cGU9IkxpdGVyYWwiLz4KICAgICAgICAgICAgPC9jYXNlSXRlbT4KICAgICAgICAgICAgPGNhc2VJdGVtIGxvY2F0b3I9IjMxOjktMzE6MjgiPgogICAgICAgICAgICAgICA8d2hlbiBsb2NhdG9yPSIzMToxNC0zMToxNiIgdmFsdWVUeXBlPSJ0OlN0cmluZyIgdmFsdWU9ImEiIHhzaTp0eXBlPSJMaXRlcmFsIi8+CiAgICAgICAgICAgICAgIDx0aGVuIGxvY2F0b3I9IjMxOjIzLTMxOjI4IiB2YWx1ZVR5cGU9InQ6U3RyaW5nIiB2YWx1ZT0ieWVhciIgeHNpOnR5cGU9IkxpdGVyYWwiLz4KICAgICAgICAgICAgPC9jYXNlSXRlbT4KICAgICAgICAgICAgPGVsc2UgbG9jYXRvcj0iMzI6MTQtMzI6MTciIG5hbWU9InVuaXQiIHhzaTp0eXBlPSJPcGVyYW5kUmVmIi8+CiAgICAgICAgIDwvZXhwcmVzc2lvbj4KICAgICAgICAgPG9wZXJhbmQgbmFtZT0idW5pdCI+CiAgICAgICAgICAgIDxvcGVyYW5kVHlwZVNwZWNpZmllciBsb2NhdG9yPSIyMjozNy0yMjo0OSIgbmFtZT0idDpTdHJpbmciIHhzaTp0eXBlPSJOYW1lZFR5cGVTcGVjaWZpZXIiLz4KICAgICAgICAgPC9vcGVyYW5kPgogICAgICA8L2RlZj4KICAgICAgPGRlZiBsb2NhdG9yPSIzNToxLTQ2OjciIG5hbWU9IlRvUXVhbnRpdHkiIGNvbnRleHQ9IlBhdGllbnQiIGFjY2Vzc0xldmVsPSJQdWJsaWMiIHhzaTp0eXBlPSJGdW5jdGlvbkRlZiI+CiAgICAgICAgIDxleHByZXNzaW9uIGxvY2F0b3I9IjM2OjUtNDY6NyIgeHNpOnR5cGU9IkNhc2UiPgogICAgICAgICAgICA8Y2FzZUl0ZW0gbG9jYXRvcj0iMzc6OS0zNzozOSI+CiAgICAgICAgICAgICAgIDx3aGVuIGxvY2F0b3I9IjM3OjE0LTM3OjI5IiB4c2k6dHlwZT0iSXNOdWxsIj4KICAgICAgICAgICAgICAgICAgPG9wZXJhbmQgbG9jYXRvcj0iMzc6MTQtMzc6MjEiIG5hbWU9InF1YW50aXR5IiB4c2k6dHlwZT0iT3BlcmFuZFJlZiIvPgogICAgICAgICAgICAgICA8L3doZW4+CiAgICAgICAgICAgICAgIDx0aGVuIGFzVHlwZT0idDpRdWFudGl0eSIgeHNpOnR5cGU9IkFzIj4KICAgICAgICAgICAgICAgICAgPG9wZXJhbmQgbG9jYXRvcj0iMzc6MzYtMzc6MzkiIHhzaTp0eXBlPSJOdWxsIi8+CiAgICAgICAgICAgICAgIDwvdGhlbj4KICAgICAgICAgICAgPC9jYXNlSXRlbT4KICAgICAgICAgICAgPGNhc2VJdGVtIGxvY2F0b3I9IjM4OjktMzg6NDUiPgogICAgICAgICAgICAgICA8d2hlbiBsb2NhdG9yPSIzODoxNC0zODozNSIgeHNpOnR5cGU9IklzTnVsbCI+CiAgICAgICAgICAgICAgICAgIDxvcGVyYW5kIGxvY2F0b3I9IjM4OjE0LTM4OjI3IiBwYXRoPSJ2YWx1ZSIgeHNpOnR5cGU9IlByb3BlcnR5Ij4KICAgICAgICAgICAgICAgICAgICAgPHNvdXJjZSBsb2NhdG9yPSIzODoxNC0zODoyMSIgbmFtZT0icXVhbnRpdHkiIHhzaTp0eXBlPSJPcGVyYW5kUmVmIi8+CiAgICAgICAgICAgICAgICAgIDwvb3BlcmFuZD4KICAgICAgICAgICAgICAgPC93aGVuPgogICAgICAgICAgICAgICA8dGhlbiBhc1R5cGU9InQ6UXVhbnRpdHkiIHhzaTp0eXBlPSJBcyI+CiAgICAgICAgICAgICAgICAgIDxvcGVyYW5kIGxvY2F0b3I9IjM4OjQyLTM4OjQ1IiB4c2k6dHlwZT0iTnVsbCIvPgogICAgICAgICAgICAgICA8L3RoZW4+CiAgICAgICAgICAgIDwvY2FzZUl0ZW0+CiAgICAgICAgICAgIDxjYXNlSXRlbSBsb2NhdG9yPSIzOTo5LTQwOjE4OSI+CiAgICAgICAgICAgICAgIDx3aGVuIGxvY2F0b3I9IjM5OjE0LTM5OjQ0IiB4c2k6dHlwZT0iTm90Ij4KICAgICAgICAgICAgICAgICAgPG9wZXJhbmQgbG9jYXRvcj0iMzk6MTQtMzk6NDQiIHhzaTp0eXBlPSJJc051bGwiPgogICAgICAgICAgICAgICAgICAgICA8b3BlcmFuZCBsb2NhdG9yPSIzOToxNC0zOTozMiIgcGF0aD0iY29tcGFyYXRvciIgeHNpOnR5cGU9IlByb3BlcnR5Ij4KICAgICAgICAgICAgICAgICAgICAgICAgPHNvdXJjZSBsb2NhdG9yPSIzOToxNC0zOToyMSIgbmFtZT0icXVhbnRpdHkiIHhzaTp0eXBlPSJPcGVyYW5kUmVmIi8+CiAgICAgICAgICAgICAgICAgICAgIDwvb3BlcmFuZD4KICAgICAgICAgICAgICAgICAgPC9vcGVyYW5kPgogICAgICAgICAgICAgICA8L3doZW4+CiAgICAgICAgICAgICAgIDx0aGVuIGFzVHlwZT0idDpRdWFudGl0eSIgeHNpOnR5cGU9IkFzIj4KICAgICAgICAgICAgICAgICAgPG9wZXJhbmQgbG9jYXRvcj0iNDA6MTMtNDA6MTg5IiB4c2k6dHlwZT0iTWVzc2FnZSI+CiAgICAgICAgICAgICAgICAgICAgIDxzb3VyY2UgbG9jYXRvcj0iNDA6MjEtNDA6MjQiIHhzaTp0eXBlPSJOdWxsIi8+CiAgICAgICAgICAgICAgICAgICAgIDxjb25kaXRpb24gbG9jYXRvcj0iNDA6MjctNDA6MzAiIHZhbHVlVHlwZT0idDpCb29sZWFuIiB2YWx1ZT0idHJ1ZSIgeHNpOnR5cGU9IkxpdGVyYWwiLz4KICAgICAgICAgICAgICAgICAgICAgPGNvZGUgbG9jYXRvcj0iNDA6MzMtNDA6ODciIHZhbHVlVHlwZT0idDpTdHJpbmciIHZhbHVlPSJGSElSSGVscGVycy5Ub1F1YW50aXR5LkNvbXBhcmF0b3JRdWFudGl0eU5vdFN1cHBvcnRlZCIgeHNpOnR5cGU9IkxpdGVyYWwiLz4KICAgICAgICAgICAgICAgICAgICAgPHNldmVyaXR5IGxvY2F0b3I9IjQwOjkwLTQwOjk2IiB2YWx1ZVR5cGU9InQ6U3RyaW5nIiB2YWx1ZT0iRXJyb3IiIHhzaTp0eXBlPSJMaXRlcmFsIi8+CiAgICAgICAgICAgICAgICAgICAgIDxtZXNzYWdlIGxvY2F0b3I9IjQwOjk5LTQwOjE4OCIgdmFsdWVUeXBlPSJ0OlN0cmluZyIgdmFsdWU9IkZISVIgUXVhbnRpdHkgdmFsdWUgaGFzIGEgY29tcGFyYXRvciBhbmQgY2Fubm90IGJlIGNvbnZlcnRlZCB0byBhIFN5c3RlbS5RdWFudGl0eSB2YWx1ZS4iIHhzaTp0eXBlPSJMaXRlcmFsIi8+CiAgICAgICAgICAgICAgICAgIDwvb3BlcmFuZD4KICAgICAgICAgICAgICAgPC90aGVuPgogICAgICAgICAgICA8L2Nhc2VJdGVtPgogICAgICAgICAgICA8Y2FzZUl0ZW0gbG9jYXRvcj0iNDE6OS00MzoxMzgiPgogICAgICAgICAgICAgICA8d2hlbiBsb2NhdG9yPSI0MToxNC00Mjo5MiIgeHNpOnR5cGU9Ik9yIj4KICAgICAgICAgICAgICAgICAgPG9wZXJhbmQgbG9jYXRvcj0iNDE6MTQtNDE6OTEiIHhzaTp0eXBlPSJPciI+CiAgICAgICAgICAgICAgICAgICAgIDxvcGVyYW5kIGxvY2F0b3I9IjQxOjE0LTQxOjM2IiB4c2k6dHlwZT0iSXNOdWxsIj4KICAgICAgICAgICAgICAgICAgICAgICAgPG9wZXJhbmQgbG9jYXRvcj0iNDE6MTQtNDE6MjgiIHBhdGg9InN5c3RlbSIgeHNpOnR5cGU9IlByb3BlcnR5Ij4KICAgICAgICAgICAgICAgICAgICAgICAgICAgPHNvdXJjZSBsb2NhdG9yPSI0MToxNC00MToyMSIgbmFtZT0icXVhbnRpdHkiIHhzaTp0eXBlPSJPcGVyYW5kUmVmIi8+CiAgICAgICAgICAgICAgICAgICAgICAgIDwvb3BlcmFuZD4KICAgICAgICAgICAgICAgICAgICAgPC9vcGVyYW5kPgogICAgICAgICAgICAgICAgICAgICA8b3BlcmFuZCBsb2NhdG9yPSI0MTo0MS00MTo5MSIgeHNpOnR5cGU9IkVxdWFsIj4KICAgICAgICAgICAgICAgICAgICAgICAgPG9wZXJhbmQgbG9jYXRvcj0iNDE6NDEtNDE6NjEiIHBhdGg9InZhbHVlIiB4c2k6dHlwZT0iUHJvcGVydHkiPgogICAgICAgICAgICAgICAgICAgICAgICAgICA8c291cmNlIGxvY2F0b3I9IjQxOjQxLTQxOjU1IiBwYXRoPSJzeXN0ZW0iIHhzaTp0eXBlPSJQcm9wZXJ0eSI+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDxzb3VyY2UgbG9jYXRvcj0iNDE6NDEtNDE6NDgiIG5hbWU9InF1YW50aXR5IiB4c2k6dHlwZT0iT3BlcmFuZFJlZiIvPgogICAgICAgICAgICAgICAgICAgICAgICAgICA8L3NvdXJjZT4KICAgICAgICAgICAgICAgICAgICAgICAgPC9vcGVyYW5kPgogICAgICAgICAgICAgICAgICAgICAgICA8b3BlcmFuZCBsb2NhdG9yPSI0MTo2NS00MTo5MSIgdmFsdWVUeXBlPSJ0OlN0cmluZyIgdmFsdWU9Imh0dHA6Ly91bml0c29mbWVhc3VyZS5vcmciIHhzaTp0eXBlPSJMaXRlcmFsIi8+CiAgICAgICAgICAgICAgICAgICAgIDwvb3BlcmFuZD4KICAgICAgICAgICAgICAgICAgPC9vcGVyYW5kPgogICAgICAgICAgICAgICAgICA8b3BlcmFuZCBsb2NhdG9yPSI0MjoxOC00Mjo5MiIgeHNpOnR5cGU9IkVxdWFsIj4KICAgICAgICAgICAgICAgICAgICAgPG9wZXJhbmQgbG9jYXRvcj0iNDI6MTgtNDI6MzgiIHBhdGg9InZhbHVlIiB4c2k6dHlwZT0iUHJvcGVydHkiPgogICAgICAgICAgICAgICAgICAgICAgICA8c291cmNlIGxvY2F0b3I9IjQyOjE4LTQyOjMyIiBwYXRoPSJzeXN0ZW0iIHhzaTp0eXBlPSJQcm9wZXJ0eSI+CiAgICAgICAgICAgICAgICAgICAgICAgICAgIDxzb3VyY2UgbG9jYXRvcj0iNDI6MTgtNDI6MjUiIG5hbWU9InF1YW50aXR5IiB4c2k6dHlwZT0iT3BlcmFuZFJlZiIvPgogICAgICAgICAgICAgICAgICAgICAgICA8L3NvdXJjZT4KICAgICAgICAgICAgICAgICAgICAgPC9vcGVyYW5kPgogICAgICAgICAgICAgICAgICAgICA8b3BlcmFuZCBsb2NhdG9yPSI0Mjo0Mi00Mjo5MiIgdmFsdWVUeXBlPSJ0OlN0cmluZyIgdmFsdWU9Imh0dHA6Ly9obDcub3JnL2ZoaXJwYXRoL0NvZGVTeXN0ZW0vY2FsZW5kYXItdW5pdHMiIHhzaTp0eXBlPSJMaXRlcmFsIi8+CiAgICAgICAgICAgICAgICAgIDwvb3BlcmFuZD4KICAgICAgICAgICAgICAgPC93aGVuPgogICAgICAgICAgICAgICA8dGhlbiBsb2NhdG9yPSI0MzoxMy00MzoxMzgiIGNsYXNzVHlwZT0idDpRdWFudGl0eSIgeHNpOnR5cGU9Ikluc3RhbmNlIj4KICAgICAgICAgICAgICAgICAgPGVsZW1lbnQgbmFtZT0idmFsdWUiPgogICAgICAgICAgICAgICAgICAgICA8dmFsdWUgbG9jYXRvcj0iNDM6MzgtNDM6NTciIHBhdGg9InZhbHVlIiB4c2k6dHlwZT0iUHJvcGVydHkiPgogICAgICAgICAgICAgICAgICAgICAgICA8c291cmNlIGxvY2F0b3I9IjQzOjM4LTQzOjUxIiBwYXRoPSJ2YWx1ZSIgeHNpOnR5cGU9IlByb3BlcnR5Ij4KICAgICAgICAgICAgICAgICAgICAgICAgICAgPHNvdXJjZSBsb2NhdG9yPSI0MzozOC00Mzo0NSIgbmFtZT0icXVhbnRpdHkiIHhzaTp0eXBlPSJPcGVyYW5kUmVmIi8+CiAgICAgICAgICAgICAgICAgICAgICAgIDwvc291cmNlPgogICAgICAgICAgICAgICAgICAgICA8L3ZhbHVlPgogICAgICAgICAgICAgICAgICA8L2VsZW1lbnQ+CiAgICAgICAgICAgICAgICAgIDxlbGVtZW50IG5hbWU9InVuaXQiPgogICAgICAgICAgICAgICAgICAgICA8dmFsdWUgbG9jYXRvcj0iNDM6NjYtNDM6MTM2IiBuYW1lPSJUb0NhbGVuZGFyVW5pdCIgeHNpOnR5cGU9IkZ1bmN0aW9uUmVmIj4KICAgICAgICAgICAgICAgICAgICAgICAgPG9wZXJhbmQgbG9jYXRvcj0iNDM6ODEtNDM6MTM1IiB4c2k6dHlwZT0iQ29hbGVzY2UiPgogICAgICAgICAgICAgICAgICAgICAgICAgICA8b3BlcmFuZCBsb2NhdG9yPSI0Mzo5MC00MzoxMDgiIHBhdGg9InZhbHVlIiB4c2k6dHlwZT0iUHJvcGVydHkiPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8c291cmNlIGxvY2F0b3I9IjQzOjkwLTQzOjEwMiIgcGF0aD0iY29kZSIgeHNpOnR5cGU9IlByb3BlcnR5Ij4KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPHNvdXJjZSBsb2NhdG9yPSI0Mzo5MC00Mzo5NyIgbmFtZT0icXVhbnRpdHkiIHhzaTp0eXBlPSJPcGVyYW5kUmVmIi8+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDwvc291cmNlPgogICAgICAgICAgICAgICAgICAgICAgICAgICA8L29wZXJhbmQ+CiAgICAgICAgICAgICAgICAgICAgICAgICAgIDxvcGVyYW5kIGxvY2F0b3I9IjQzOjExMS00MzoxMjkiIHBhdGg9InZhbHVlIiB4c2k6dHlwZT0iUHJvcGVydHkiPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8c291cmNlIGxvY2F0b3I9IjQzOjExMS00MzoxMjMiIHBhdGg9InVuaXQiIHhzaTp0eXBlPSJQcm9wZXJ0eSI+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDxzb3VyY2UgbG9jYXRvcj0iNDM6MTExLTQzOjExOCIgbmFtZT0icXVhbnRpdHkiIHhzaTp0eXBlPSJPcGVyYW5kUmVmIi8+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDwvc291cmNlPgogICAgICAgICAgICAgICAgICAgICAgICAgICA8L29wZXJhbmQ+CiAgICAgICAgICAgICAgICAgICAgICAgICAgIDxvcGVyYW5kIGxvY2F0b3I9IjQzOjEzMi00MzoxMzQiIHZhbHVlVHlwZT0idDpTdHJpbmciIHZhbHVlPSIxIiB4c2k6dHlwZT0iTGl0ZXJhbCIvPgogICAgICAgICAgICAgICAgICAgICAgICA8L29wZXJhbmQ+CiAgICAgICAgICAgICAgICAgICAgIDwvdmFsdWU+CiAgICAgICAgICAgICAgICAgIDwvZWxlbWVudD4KICAgICAgICAgICAgICAgPC90aGVuPgogICAgICAgICAgICA8L2Nhc2VJdGVtPgogICAgICAgICAgICA8ZWxzZSBhc1R5cGU9InQ6UXVhbnRpdHkiIHhzaTp0eXBlPSJBcyI+CiAgICAgICAgICAgICAgIDxvcGVyYW5kIGxvY2F0b3I9IjQ1OjEzLTQ1OjIwNSIgeHNpOnR5cGU9Ik1lc3NhZ2UiPgogICAgICAgICAgICAgICAgICA8c291cmNlIGxvY2F0b3I9IjQ1OjIxLTQ1OjI0IiB4c2k6dHlwZT0iTnVsbCIvPgogICAgICAgICAgICAgICAgICA8Y29uZGl0aW9uIGxvY2F0b3I9IjQ1OjI3LTQ1OjMwIiB2YWx1ZVR5cGU9InQ6Qm9vbGVhbiIgdmFsdWU9InRydWUiIHhzaTp0eXBlPSJMaXRlcmFsIi8+CiAgICAgICAgICAgICAgICAgIDxjb2RlIGxvY2F0b3I9IjQ1OjMzLTQ1Ojc2IiB2YWx1ZVR5cGU9InQ6U3RyaW5nIiB2YWx1ZT0iRkhJUkhlbHBlcnMuVG9RdWFudGl0eS5JbnZhbGlkRkhJUlF1YW50aXR5IiB4c2k6dHlwZT0iTGl0ZXJhbCIvPgogICAgICAgICAgICAgICAgICA8c2V2ZXJpdHkgbG9jYXRvcj0iNDU6NzktNDU6ODUiIHZhbHVlVHlwZT0idDpTdHJpbmciIHZhbHVlPSJFcnJvciIgeHNpOnR5cGU9IkxpdGVyYWwiLz4KICAgICAgICAgICAgICAgICAgPG1lc3NhZ2UgbG9jYXRvcj0iNDU6ODgtNDU6MjA0IiB4c2k6dHlwZT0iQ29uY2F0ZW5hdGUiPgogICAgICAgICAgICAgICAgICAgICA8b3BlcmFuZCB4c2k6dHlwZT0iQ29hbGVzY2UiPgogICAgICAgICAgICAgICAgICAgICAgICA8b3BlcmFuZCBsb2NhdG9yPSI0NTo4OC00NToxOTgiIHhzaTp0eXBlPSJDb25jYXRlbmF0ZSI+CiAgICAgICAgICAgICAgICAgICAgICAgICAgIDxvcGVyYW5kIHhzaTp0eXBlPSJDb2FsZXNjZSI+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDxvcGVyYW5kIGxvY2F0b3I9IjQ1Ojg4LTQ1OjE3NiIgeHNpOnR5cGU9IkNvbmNhdGVuYXRlIj4KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPG9wZXJhbmQgeHNpOnR5cGU9IkNvYWxlc2NlIj4KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPG9wZXJhbmQgbG9jYXRvcj0iNDU6ODgtNDU6MTcwIiB4c2k6dHlwZT0iQ29uY2F0ZW5hdGUiPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8b3BlcmFuZCB4c2k6dHlwZT0iQ29hbGVzY2UiPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8b3BlcmFuZCBsb2NhdG9yPSI0NTo4OC00NToxNDYiIHhzaTp0eXBlPSJDb25jYXRlbmF0ZSI+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDxvcGVyYW5kIHhzaTp0eXBlPSJDb2FsZXNjZSI+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDxvcGVyYW5kIGxvY2F0b3I9IjQ1Ojg4LTQ1OjEzOSIgeHNpOnR5cGU9IkNvbmNhdGVuYXRlIj4KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPG9wZXJhbmQgeHNpOnR5cGU9IkNvYWxlc2NlIj4KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPG9wZXJhbmQgbG9jYXRvcj0iNDU6ODgtNDU6MTE3IiB2YWx1ZVR5cGU9InQ6U3RyaW5nIiB2YWx1ZT0iSW52YWxpZCBGSElSIFF1YW50aXR5IGNvZGU6ICIgeHNpOnR5cGU9IkxpdGVyYWwiLz4KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPG9wZXJhbmQgdmFsdWVUeXBlPSJ0OlN0cmluZyIgdmFsdWU9IiIgeHNpOnR5cGU9IkxpdGVyYWwiLz4KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPC9vcGVyYW5kPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8b3BlcmFuZCB4c2k6dHlwZT0iQ29hbGVzY2UiPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8b3BlcmFuZCBsb2NhdG9yPSI0NToxMjEtNDU6MTM5IiBwYXRoPSJ2YWx1ZSIgeHNpOnR5cGU9IlByb3BlcnR5Ij4KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPHNvdXJjZSBsb2NhdG9yPSI0NToxMjEtNDU6MTMzIiBwYXRoPSJ1bml0IiB4c2k6dHlwZT0iUHJvcGVydHkiPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8c291cmNlIGxvY2F0b3I9IjQ1OjEyMS00NToxMjgiIG5hbWU9InF1YW50aXR5IiB4c2k6dHlwZT0iT3BlcmFuZFJlZiIvPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8L3NvdXJjZT4KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPC9vcGVyYW5kPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8b3BlcmFuZCB2YWx1ZVR5cGU9InQ6U3RyaW5nIiB2YWx1ZT0iIiB4c2k6dHlwZT0iTGl0ZXJhbCIvPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8L29wZXJhbmQ+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDwvb3BlcmFuZD4KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPG9wZXJhbmQgdmFsdWVUeXBlPSJ0OlN0cmluZyIgdmFsdWU9IiIgeHNpOnR5cGU9IkxpdGVyYWwiLz4KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPC9vcGVyYW5kPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8b3BlcmFuZCB4c2k6dHlwZT0iQ29hbGVzY2UiPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8b3BlcmFuZCBsb2NhdG9yPSI0NToxNDMtNDU6MTQ2IiB2YWx1ZVR5cGU9InQ6U3RyaW5nIiB2YWx1ZT0iICgiIHhzaTp0eXBlPSJMaXRlcmFsIi8+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDxvcGVyYW5kIHZhbHVlVHlwZT0idDpTdHJpbmciIHZhbHVlPSIiIHhzaTp0eXBlPSJMaXRlcmFsIi8+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDwvb3BlcmFuZD4KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPC9vcGVyYW5kPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8b3BlcmFuZCB2YWx1ZVR5cGU9InQ6U3RyaW5nIiB2YWx1ZT0iIiB4c2k6dHlwZT0iTGl0ZXJhbCIvPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8L29wZXJhbmQ+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDxvcGVyYW5kIHhzaTp0eXBlPSJDb2FsZXNjZSI+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDxvcGVyYW5kIGxvY2F0b3I9IjQ1OjE1MC00NToxNzAiIHBhdGg9InZhbHVlIiB4c2k6dHlwZT0iUHJvcGVydHkiPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8c291cmNlIGxvY2F0b3I9IjQ1OjE1MC00NToxNjQiIHBhdGg9InN5c3RlbSIgeHNpOnR5cGU9IlByb3BlcnR5Ij4KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPHNvdXJjZSBsb2NhdG9yPSI0NToxNTAtNDU6MTU3IiBuYW1lPSJxdWFudGl0eSIgeHNpOnR5cGU9Ik9wZXJhbmRSZWYiLz4KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPC9zb3VyY2U+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDwvb3BlcmFuZD4KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPG9wZXJhbmQgdmFsdWVUeXBlPSJ0OlN0cmluZyIgdmFsdWU9IiIgeHNpOnR5cGU9IkxpdGVyYWwiLz4KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPC9vcGVyYW5kPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8L29wZXJhbmQ+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDxvcGVyYW5kIHZhbHVlVHlwZT0idDpTdHJpbmciIHZhbHVlPSIiIHhzaTp0eXBlPSJMaXRlcmFsIi8+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDwvb3BlcmFuZD4KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPG9wZXJhbmQgeHNpOnR5cGU9IkNvYWxlc2NlIj4KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPG9wZXJhbmQgbG9jYXRvcj0iNDU6MTc0LTQ1OjE3NiIgdmFsdWVUeXBlPSJ0OlN0cmluZyIgdmFsdWU9InwiIHhzaTp0eXBlPSJMaXRlcmFsIi8+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDxvcGVyYW5kIHZhbHVlVHlwZT0idDpTdHJpbmciIHZhbHVlPSIiIHhzaTp0eXBlPSJMaXRlcmFsIi8+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDwvb3BlcmFuZD4KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPC9vcGVyYW5kPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8b3BlcmFuZCB2YWx1ZVR5cGU9InQ6U3RyaW5nIiB2YWx1ZT0iIiB4c2k6dHlwZT0iTGl0ZXJhbCIvPgogICAgICAgICAgICAgICAgICAgICAgICAgICA8L29wZXJhbmQ+CiAgICAgICAgICAgICAgICAgICAgICAgICAgIDxvcGVyYW5kIHhzaTp0eXBlPSJDb2FsZXNjZSI+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDxvcGVyYW5kIGxvY2F0b3I9IjQ1OjE4MC00NToxOTgiIHBhdGg9InZhbHVlIiB4c2k6dHlwZT0iUHJvcGVydHkiPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8c291cmNlIGxvY2F0b3I9IjQ1OjE4MC00NToxOTIiIHBhdGg9ImNvZGUiIHhzaTp0eXBlPSJQcm9wZXJ0eSI+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDxzb3VyY2UgbG9jYXRvcj0iNDU6MTgwLTQ1OjE4NyIgbmFtZT0icXVhbnRpdHkiIHhzaTp0eXBlPSJPcGVyYW5kUmVmIi8+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDwvc291cmNlPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8L29wZXJhbmQ+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDxvcGVyYW5kIHZhbHVlVHlwZT0idDpTdHJpbmciIHZhbHVlPSIiIHhzaTp0eXBlPSJMaXRlcmFsIi8+CiAgICAgICAgICAgICAgICAgICAgICAgICAgIDwvb3BlcmFuZD4KICAgICAgICAgICAgICAgICAgICAgICAgPC9vcGVyYW5kPgogICAgICAgICAgICAgICAgICAgICAgICA8b3BlcmFuZCB2YWx1ZVR5cGU9InQ6U3RyaW5nIiB2YWx1ZT0iIiB4c2k6dHlwZT0iTGl0ZXJhbCIvPgogICAgICAgICAgICAgICAgICAgICA8L29wZXJhbmQ+CiAgICAgICAgICAgICAgICAgICAgIDxvcGVyYW5kIHhzaTp0eXBlPSJDb2FsZXNjZSI+CiAgICAgICAgICAgICAgICAgICAgICAgIDxvcGVyYW5kIGxvY2F0b3I9IjQ1OjIwMi00NToyMDQiIHZhbHVlVHlwZT0idDpTdHJpbmciIHZhbHVlPSIpIiB4c2k6dHlwZT0iTGl0ZXJhbCIvPgogICAgICAgICAgICAgICAgICAgICAgICA8b3BlcmFuZCB2YWx1ZVR5cGU9InQ6U3RyaW5nIiB2YWx1ZT0iIiB4c2k6dHlwZT0iTGl0ZXJhbCIvPgogICAgICAgICAgICAgICAgICAgICA8L29wZXJhbmQ+CiAgICAgICAgICAgICAgICAgIDwvbWVzc2FnZT4KICAgICAgICAgICAgICAgPC9vcGVyYW5kPgogICAgICAgICAgICA8L2Vsc2U+CiAgICAgICAgIDwvZXhwcmVzc2lvbj4KICAgICAgICAgPG9wZXJhbmQgbmFtZT0icXVhbnRpdHkiPgogICAgICAgICAgICA8b3BlcmFuZFR5cGVTcGVjaWZpZXIgbG9jYXRvcj0iMzU6MzctMzU6NDkiIG5hbWU9ImZoaXI6UXVhbnRpdHkiIHhzaTp0eXBlPSJOYW1lZFR5cGVTcGVjaWZpZXIiLz4KICAgICAgICAgPC9vcGVyYW5kPgogICAgICA8L2RlZj4KICAgICAgPGRlZiBsb2NhdG9yPSI0ODoxLTU3OjciIG5hbWU9IlRvUXVhbnRpdHlJZ25vcmluZ0NvbXBhcmF0b3IiIGNvbnRleHQ9IlBhdGllbnQiIGFjY2Vzc0xldmVsPSJQdWJsaWMiIHhzaTp0eXBlPSJGdW5jdGlvbkRlZiI+CiAgICAgICAgIDxleHByZXNzaW9uIGxvY2F0b3I9IjQ5OjUtNTc6NyIgeHNpOnR5cGU9IkNhc2UiPgogICAgICAgICAgICA8Y2FzZUl0ZW0gbG9jYXRvcj0iNTA6OS01MDozOSI+CiAgICAgICAgICAgICAgIDx3aGVuIGxvY2F0b3I9IjUwOjE0LTUwOjI5IiB4c2k6dHlwZT0iSXNOdWxsIj4KICAgICAgICAgICAgICAgICAgPG9wZXJhbmQgbG9jYXRvcj0iNTA6MTQtNTA6MjEiIG5hbWU9InF1YW50aXR5IiB4c2k6dHlwZT0iT3BlcmFuZFJlZiIvPgogICAgICAgICAgICAgICA8L3doZW4+CiAgICAgICAgICAgICAgIDx0aGVuIGFzVHlwZT0idDpRdWFudGl0eSIgeHNpOnR5cGU9IkFzIj4KICAgICAgICAgICAgICAgICAgPG9wZXJhbmQgbG9jYXRvcj0iNTA6MzYtNTA6MzkiIHhzaTp0eXBlPSJOdWxsIi8+CiAgICAgICAgICAgICAgIDwvdGhlbj4KICAgICAgICAgICAgPC9jYXNlSXRlbT4KICAgICAgICAgICAgPGNhc2VJdGVtIGxvY2F0b3I9IjUxOjktNTE6NDUiPgogICAgICAgICAgICAgICA8d2hlbiBsb2NhdG9yPSI1MToxNC01MTozNSIgeHNpOnR5cGU9IklzTnVsbCI+CiAgICAgICAgICAgICAgICAgIDxvcGVyYW5kIGxvY2F0b3I9IjUxOjE0LTUxOjI3IiBwYXRoPSJ2YWx1ZSIgeHNpOnR5cGU9IlByb3BlcnR5Ij4KICAgICAgICAgICAgICAgICAgICAgPHNvdXJjZSBsb2NhdG9yPSI1MToxNC01MToyMSIgbmFtZT0icXVhbnRpdHkiIHhzaTp0eXBlPSJPcGVyYW5kUmVmIi8+CiAgICAgICAgICAgICAgICAgIDwvb3BlcmFuZD4KICAgICAgICAgICAgICAgPC93aGVuPgogICAgICAgICAgICAgICA8dGhlbiBhc1R5cGU9InQ6UXVhbnRpdHkiIHhzaTp0eXBlPSJBcyI+CiAgICAgICAgICAgICAgICAgIDxvcGVyYW5kIGxvY2F0b3I9IjUxOjQyLTUxOjQ1IiB4c2k6dHlwZT0iTnVsbCIvPgogICAgICAgICAgICAgICA8L3RoZW4+CiAgICAgICAgICAgIDwvY2FzZUl0ZW0+CiAgICAgICAgICAgIDxjYXNlSXRlbSBsb2NhdG9yPSI1Mjo5LTU0OjEzOCI+CiAgICAgICAgICAgICAgIDx3aGVuIGxvY2F0b3I9IjUyOjE0LTUzOjkyIiB4c2k6dHlwZT0iT3IiPgogICAgICAgICAgICAgICAgICA8b3BlcmFuZCBsb2NhdG9yPSI1MjoxNC01Mjo5MSIgeHNpOnR5cGU9Ik9yIj4KICAgICAgICAgICAgICAgICAgICAgPG9wZXJhbmQgbG9jYXRvcj0iNTI6MTQtNTI6MzYiIHhzaTp0eXBlPSJJc051bGwiPgogICAgICAgICAgICAgICAgICAgICAgICA8b3BlcmFuZCBsb2NhdG9yPSI1MjoxNC01MjoyOCIgcGF0aD0ic3lzdGVtIiB4c2k6dHlwZT0iUHJvcGVydHkiPgogICAgICAgICAgICAgICAgICAgICAgICAgICA8c291cmNlIGxvY2F0b3I9IjUyOjE0LTUyOjIxIiBuYW1lPSJxdWFudGl0eSIgeHNpOnR5cGU9Ik9wZXJhbmRSZWYiLz4KICAgICAgICAgICAgICAgICAgICAgICAgPC9vcGVyYW5kPgogICAgICAgICAgICAgICAgICAgICA8L29wZXJhbmQ+CiAgICAgICAgICAgICAgICAgICAgIDxvcGVyYW5kIGxvY2F0b3I9IjUyOjQxLTUyOjkxIiB4c2k6dHlwZT0iRXF1YWwiPgogICAgICAgICAgICAgICAgICAgICAgICA8b3BlcmFuZCBsb2NhdG9yPSI1Mjo0MS01Mjo2MSIgcGF0aD0idmFsdWUiIHhzaTp0eXBlPSJQcm9wZXJ0eSI+CiAgICAgICAgICAgICAgICAgICAgICAgICAgIDxzb3VyY2UgbG9jYXRvcj0iNTI6NDEtNTI6NTUiIHBhdGg9InN5c3RlbSIgeHNpOnR5cGU9IlByb3BlcnR5Ij4KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPHNvdXJjZSBsb2NhdG9yPSI1Mjo0MS01Mjo0OCIgbmFtZT0icXVhbnRpdHkiIHhzaTp0eXBlPSJPcGVyYW5kUmVmIi8+CiAgICAgICAgICAgICAgICAgICAgICAgICAgIDwvc291cmNlPgogICAgICAgICAgICAgICAgICAgICAgICA8L29wZXJhbmQ+CiAgICAgICAgICAgICAgICAgICAgICAgIDxvcGVyYW5kIGxvY2F0b3I9IjUyOjY1LTUyOjkxIiB2YWx1ZVR5cGU9InQ6U3RyaW5nIiB2YWx1ZT0iaHR0cDovL3VuaXRzb2ZtZWFzdXJlLm9yZyIgeHNpOnR5cGU9IkxpdGVyYWwiLz4KICAgICAgICAgICAgICAgICAgICAgPC9vcGVyYW5kPgogICAgICAgICAgICAgICAgICA8L29wZXJhbmQ+CiAgICAgICAgICAgICAgICAgIDxvcGVyYW5kIGxvY2F0b3I9IjUzOjE4LTUzOjkyIiB4c2k6dHlwZT0iRXF1YWwiPgogICAgICAgICAgICAgICAgICAgICA8b3BlcmFuZCBsb2NhdG9yPSI1MzoxOC01MzozOCIgcGF0aD0idmFsdWUiIHhzaTp0eXBlPSJQcm9wZXJ0eSI+CiAgICAgICAgICAgICAgICAgICAgICAgIDxzb3VyY2UgbG9jYXRvcj0iNTM6MTgtNTM6MzIiIHBhdGg9InN5c3RlbSIgeHNpOnR5cGU9IlByb3BlcnR5Ij4KICAgICAgICAgICAgICAgICAgICAgICAgICAgPHNvdXJjZSBsb2NhdG9yPSI1MzoxOC01MzoyNSIgbmFtZT0icXVhbnRpdHkiIHhzaTp0eXBlPSJPcGVyYW5kUmVmIi8+CiAgICAgICAgICAgICAgICAgICAgICAgIDwvc291cmNlPgogICAgICAgICAgICAgICAgICAgICA8L29wZXJhbmQ+CiAgICAgICAgICAgICAgICAgICAgIDxvcGVyYW5kIGxvY2F0b3I9IjUzOjQyLTUzOjkyIiB2YWx1ZVR5cGU9InQ6U3RyaW5nIiB2YWx1ZT0iaHR0cDovL2hsNy5vcmcvZmhpcnBhdGgvQ29kZVN5c3RlbS9jYWxlbmRhci11bml0cyIgeHNpOnR5cGU9IkxpdGVyYWwiLz4KICAgICAgICAgICAgICAgICAgPC9vcGVyYW5kPgogICAgICAgICAgICAgICA8L3doZW4+CiAgICAgICAgICAgICAgIDx0aGVuIGxvY2F0b3I9IjU0OjEzLTU0OjEzOCIgY2xhc3NUeXBlPSJ0OlF1YW50aXR5IiB4c2k6dHlwZT0iSW5zdGFuY2UiPgogICAgICAgICAgICAgICAgICA8ZWxlbWVudCBuYW1lPSJ2YWx1ZSI+CiAgICAgICAgICAgICAgICAgICAgIDx2YWx1ZSBsb2NhdG9yPSI1NDozOC01NDo1NyIgcGF0aD0idmFsdWUiIHhzaTp0eXBlPSJQcm9wZXJ0eSI+CiAgICAgICAgICAgICAgICAgICAgICAgIDxzb3VyY2UgbG9jYXRvcj0iNTQ6MzgtNTQ6NTEiIHBhdGg9InZhbHVlIiB4c2k6dHlwZT0iUHJvcGVydHkiPgogICAgICAgICAgICAgICAgICAgICAgICAgICA8c291cmNlIGxvY2F0b3I9IjU0OjM4LTU0OjQ1IiBuYW1lPSJxdWFudGl0eSIgeHNpOnR5cGU9Ik9wZXJhbmRSZWYiLz4KICAgICAgICAgICAgICAgICAgICAgICAgPC9zb3VyY2U+CiAgICAgICAgICAgICAgICAgICAgIDwvdmFsdWU+CiAgICAgICAgICAgICAgICAgIDwvZWxlbWVudD4KICAgICAgICAgICAgICAgICAgPGVsZW1lbnQgbmFtZT0idW5pdCI+CiAgICAgICAgICAgICAgICAgICAgIDx2YWx1ZSBsb2NhdG9yPSI1NDo2Ni01NDoxMzYiIG5hbWU9IlRvQ2FsZW5kYXJVbml0IiB4c2k6dHlwZT0iRnVuY3Rpb25SZWYiPgogICAgICAgICAgICAgICAgICAgICAgICA8b3BlcmFuZCBsb2NhdG9yPSI1NDo4MS01NDoxMzUiIHhzaTp0eXBlPSJDb2FsZXNjZSI+CiAgICAgICAgICAgICAgICAgICAgICAgICAgIDxvcGVyYW5kIGxvY2F0b3I9IjU0OjkwLTU0OjEwOCIgcGF0aD0idmFsdWUiIHhzaTp0eXBlPSJQcm9wZXJ0eSI+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDxzb3VyY2UgbG9jYXRvcj0iNTQ6OTAtNTQ6MTAyIiBwYXRoPSJjb2RlIiB4c2k6dHlwZT0iUHJvcGVydHkiPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8c291cmNlIGxvY2F0b3I9IjU0OjkwLTU0Ojk3IiBuYW1lPSJxdWFudGl0eSIgeHNpOnR5cGU9Ik9wZXJhbmRSZWYiLz4KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPC9zb3VyY2U+CiAgICAgICAgICAgICAgICAgICAgICAgICAgIDwvb3BlcmFuZD4KICAgICAgICAgICAgICAgICAgICAgICAgICAgPG9wZXJhbmQgbG9jYXRvcj0iNTQ6MTExLTU0OjEyOSIgcGF0aD0idmFsdWUiIHhzaTp0eXBlPSJQcm9wZXJ0eSI+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDxzb3VyY2UgbG9jYXRvcj0iNTQ6MTExLTU0OjEyMyIgcGF0aD0idW5pdCIgeHNpOnR5cGU9IlByb3BlcnR5Ij4KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPHNvdXJjZSBsb2NhdG9yPSI1NDoxMTEtNTQ6MTE4IiBuYW1lPSJxdWFudGl0eSIgeHNpOnR5cGU9Ik9wZXJhbmRSZWYiLz4KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPC9zb3VyY2U+CiAgICAgICAgICAgICAgICAgICAgICAgICAgIDwvb3BlcmFuZD4KICAgICAgICAgICAgICAgICAgICAgICAgICAgPG9wZXJhbmQgbG9jYXRvcj0iNTQ6MTMyLTU0OjEzNCIgdmFsdWVUeXBlPSJ0OlN0cmluZyIgdmFsdWU9IjEiIHhzaTp0eXBlPSJMaXRlcmFsIi8+CiAgICAgICAgICAgICAgICAgICAgICAgIDwvb3BlcmFuZD4KICAgICAgICAgICAgICAgICAgICAgPC92YWx1ZT4KICAgICAgICAgICAgICAgICAgPC9lbGVtZW50PgogICAgICAgICAgICAgICA8L3RoZW4+CiAgICAgICAgICAgIDwvY2FzZUl0ZW0+CiAgICAgICAgICAgIDxlbHNlIGFzVHlwZT0idDpRdWFudGl0eSIgeHNpOnR5cGU9IkFzIj4KICAgICAgICAgICAgICAgPG9wZXJhbmQgbG9jYXRvcj0iNTY6MTMtNTY6MjA1IiB4c2k6dHlwZT0iTWVzc2FnZSI+CiAgICAgICAgICAgICAgICAgIDxzb3VyY2UgbG9jYXRvcj0iNTY6MjEtNTY6MjQiIHhzaTp0eXBlPSJOdWxsIi8+CiAgICAgICAgICAgICAgICAgIDxjb25kaXRpb24gbG9jYXRvcj0iNTY6MjctNTY6MzAiIHZhbHVlVHlwZT0idDpCb29sZWFuIiB2YWx1ZT0idHJ1ZSIgeHNpOnR5cGU9IkxpdGVyYWwiLz4KICAgICAgICAgICAgICAgICAgPGNvZGUgbG9jYXRvcj0iNTY6MzMtNTY6NzYiIHZhbHVlVHlwZT0idDpTdHJpbmciIHZhbHVlPSJGSElSSGVscGVycy5Ub1F1YW50aXR5LkludmFsaWRGSElSUXVhbnRpdHkiIHhzaTp0eXBlPSJMaXRlcmFsIi8+CiAgICAgICAgICAgICAgICAgIDxzZXZlcml0eSBsb2NhdG9yPSI1Njo3OS01Njo4NSIgdmFsdWVUeXBlPSJ0OlN0cmluZyIgdmFsdWU9IkVycm9yIiB4c2k6dHlwZT0iTGl0ZXJhbCIvPgogICAgICAgICAgICAgICAgICA8bWVzc2FnZSBsb2NhdG9yPSI1Njo4OC01NjoyMDQiIHhzaTp0eXBlPSJDb25jYXRlbmF0ZSI+CiAgICAgICAgICAgICAgICAgICAgIDxvcGVyYW5kIHhzaTp0eXBlPSJDb2FsZXNjZSI+CiAgICAgICAgICAgICAgICAgICAgICAgIDxvcGVyYW5kIGxvY2F0b3I9IjU2Ojg4LTU2OjE5OCIgeHNpOnR5cGU9IkNvbmNhdGVuYXRlIj4KICAgICAgICAgICAgICAgICAgICAgICAgICAgPG9wZXJhbmQgeHNpOnR5cGU9IkNvYWxlc2NlIj4KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPG9wZXJhbmQgbG9jYXRvcj0iNTY6ODgtNTY6MTc2IiB4c2k6dHlwZT0iQ29uY2F0ZW5hdGUiPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8b3BlcmFuZCB4c2k6dHlwZT0iQ29hbGVzY2UiPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8b3BlcmFuZCBsb2NhdG9yPSI1Njo4OC01NjoxNzAiIHhzaTp0eXBlPSJDb25jYXRlbmF0ZSI+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDxvcGVyYW5kIHhzaTp0eXBlPSJDb2FsZXNjZSI+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDxvcGVyYW5kIGxvY2F0b3I9IjU2Ojg4LTU2OjE0NiIgeHNpOnR5cGU9IkNvbmNhdGVuYXRlIj4KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPG9wZXJhbmQgeHNpOnR5cGU9IkNvYWxlc2NlIj4KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPG9wZXJhbmQgbG9jYXRvcj0iNTY6ODgtNTY6MTM5IiB4c2k6dHlwZT0iQ29uY2F0ZW5hdGUiPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8b3BlcmFuZCB4c2k6dHlwZT0iQ29hbGVzY2UiPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8b3BlcmFuZCBsb2NhdG9yPSI1Njo4OC01NjoxMTciIHZhbHVlVHlwZT0idDpTdHJpbmciIHZhbHVlPSJJbnZhbGlkIEZISVIgUXVhbnRpdHkgY29kZTogIiB4c2k6dHlwZT0iTGl0ZXJhbCIvPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8b3BlcmFuZCB2YWx1ZVR5cGU9InQ6U3RyaW5nIiB2YWx1ZT0iIiB4c2k6dHlwZT0iTGl0ZXJhbCIvPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8L29wZXJhbmQ+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDxvcGVyYW5kIHhzaTp0eXBlPSJDb2FsZXNjZSI+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDxvcGVyYW5kIGxvY2F0b3I9IjU2OjEyMS01NjoxMzkiIHBhdGg9InZhbHVlIiB4c2k6dHlwZT0iUHJvcGVydHkiPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8c291cmNlIGxvY2F0b3I9IjU2OjEyMS01NjoxMzMiIHBhdGg9InVuaXQiIHhzaTp0eXBlPSJQcm9wZXJ0eSI+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDxzb3VyY2UgbG9jYXRvcj0iNTY6MTIxLTU2OjEyOCIgbmFtZT0icXVhbnRpdHkiIHhzaTp0eXBlPSJPcGVyYW5kUmVmIi8+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDwvc291cmNlPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8L29wZXJhbmQ+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDxvcGVyYW5kIHZhbHVlVHlwZT0idDpTdHJpbmciIHZhbHVlPSIiIHhzaTp0eXBlPSJMaXRlcmFsIi8+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDwvb3BlcmFuZD4KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPC9vcGVyYW5kPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8b3BlcmFuZCB2YWx1ZVR5cGU9InQ6U3RyaW5nIiB2YWx1ZT0iIiB4c2k6dHlwZT0iTGl0ZXJhbCIvPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8L29wZXJhbmQ+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDxvcGVyYW5kIHhzaTp0eXBlPSJDb2FsZXNjZSI+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDxvcGVyYW5kIGxvY2F0b3I9IjU2OjE0My01NjoxNDYiIHZhbHVlVHlwZT0idDpTdHJpbmciIHZhbHVlPSIgKCIgeHNpOnR5cGU9IkxpdGVyYWwiLz4KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPG9wZXJhbmQgdmFsdWVUeXBlPSJ0OlN0cmluZyIgdmFsdWU9IiIgeHNpOnR5cGU9IkxpdGVyYWwiLz4KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPC9vcGVyYW5kPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8L29wZXJhbmQ+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDxvcGVyYW5kIHZhbHVlVHlwZT0idDpTdHJpbmciIHZhbHVlPSIiIHhzaTp0eXBlPSJMaXRlcmFsIi8+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDwvb3BlcmFuZD4KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPG9wZXJhbmQgeHNpOnR5cGU9IkNvYWxlc2NlIj4KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPG9wZXJhbmQgbG9jYXRvcj0iNTY6MTUwLTU2OjE3MCIgcGF0aD0idmFsdWUiIHhzaTp0eXBlPSJQcm9wZXJ0eSI+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDxzb3VyY2UgbG9jYXRvcj0iNTY6MTUwLTU2OjE2NCIgcGF0aD0ic3lzdGVtIiB4c2k6dHlwZT0iUHJvcGVydHkiPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8c291cmNlIGxvY2F0b3I9IjU2OjE1MC01NjoxNTciIG5hbWU9InF1YW50aXR5IiB4c2k6dHlwZT0iT3BlcmFuZFJlZiIvPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8L3NvdXJjZT4KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPC9vcGVyYW5kPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8b3BlcmFuZCB2YWx1ZVR5cGU9InQ6U3RyaW5nIiB2YWx1ZT0iIiB4c2k6dHlwZT0iTGl0ZXJhbCIvPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8L29wZXJhbmQ+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDwvb3BlcmFuZD4KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPG9wZXJhbmQgdmFsdWVUeXBlPSJ0OlN0cmluZyIgdmFsdWU9IiIgeHNpOnR5cGU9IkxpdGVyYWwiLz4KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPC9vcGVyYW5kPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8b3BlcmFuZCB4c2k6dHlwZT0iQ29hbGVzY2UiPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8b3BlcmFuZCBsb2NhdG9yPSI1NjoxNzQtNTY6MTc2IiB2YWx1ZVR5cGU9InQ6U3RyaW5nIiB2YWx1ZT0ifCIgeHNpOnR5cGU9IkxpdGVyYWwiLz4KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPG9wZXJhbmQgdmFsdWVUeXBlPSJ0OlN0cmluZyIgdmFsdWU9IiIgeHNpOnR5cGU9IkxpdGVyYWwiLz4KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPC9vcGVyYW5kPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8L29wZXJhbmQ+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDxvcGVyYW5kIHZhbHVlVHlwZT0idDpTdHJpbmciIHZhbHVlPSIiIHhzaTp0eXBlPSJMaXRlcmFsIi8+CiAgICAgICAgICAgICAgICAgICAgICAgICAgIDwvb3BlcmFuZD4KICAgICAgICAgICAgICAgICAgICAgICAgICAgPG9wZXJhbmQgeHNpOnR5cGU9IkNvYWxlc2NlIj4KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPG9wZXJhbmQgbG9jYXRvcj0iNTY6MTgwLTU2OjE5OCIgcGF0aD0idmFsdWUiIHhzaTp0eXBlPSJQcm9wZXJ0eSI+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDxzb3VyY2UgbG9jYXRvcj0iNTY6MTgwLTU2OjE5MiIgcGF0aD0iY29kZSIgeHNpOnR5cGU9IlByb3BlcnR5Ij4KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPHNvdXJjZSBsb2NhdG9yPSI1NjoxODAtNTY6MTg3IiBuYW1lPSJxdWFudGl0eSIgeHNpOnR5cGU9Ik9wZXJhbmRSZWYiLz4KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPC9zb3VyY2U+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDwvb3BlcmFuZD4KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPG9wZXJhbmQgdmFsdWVUeXBlPSJ0OlN0cmluZyIgdmFsdWU9IiIgeHNpOnR5cGU9IkxpdGVyYWwiLz4KICAgICAgICAgICAgICAgICAgICAgICAgICAgPC9vcGVyYW5kPgogICAgICAgICAgICAgICAgICAgICAgICA8L29wZXJhbmQ+CiAgICAgICAgICAgICAgICAgICAgICAgIDxvcGVyYW5kIHZhbHVlVHlwZT0idDpTdHJpbmciIHZhbHVlPSIiIHhzaTp0eXBlPSJMaXRlcmFsIi8+CiAgICAgICAgICAgICAgICAgICAgIDwvb3BlcmFuZD4KICAgICAgICAgICAgICAgICAgICAgPG9wZXJhbmQgeHNpOnR5cGU9IkNvYWxlc2NlIj4KICAgICAgICAgICAgICAgICAgICAgICAgPG9wZXJhbmQgbG9jYXRvcj0iNTY6MjAyLTU2OjIwNCIgdmFsdWVUeXBlPSJ0OlN0cmluZyIgdmFsdWU9IikiIHhzaTp0eXBlPSJMaXRlcmFsIi8+CiAgICAgICAgICAgICAgICAgICAgICAgIDxvcGVyYW5kIHZhbHVlVHlwZT0idDpTdHJpbmciIHZhbHVlPSIiIHhzaTp0eXBlPSJMaXRlcmFsIi8+CiAgICAgICAgICAgICAgICAgICAgIDwvb3BlcmFuZD4KICAgICAgICAgICAgICAgICAgPC9tZXNzYWdlPgogICAgICAgICAgICAgICA8L29wZXJhbmQ+CiAgICAgICAgICAgIDwvZWxzZT4KICAgICAgICAgPC9leHByZXNzaW9uPgogICAgICAgICA8b3BlcmFuZCBuYW1lPSJxdWFudGl0eSI+CiAgICAgICAgICAgIDxvcGVyYW5kVHlwZVNwZWNpZmllciBsb2NhdG9yPSI0ODo1NS00ODo2NyIgbmFtZT0iZmhpcjpRdWFudGl0eSIgeHNpOnR5cGU9Ik5hbWVkVHlwZVNwZWNpZmllciIvPgogICAgICAgICA8L29wZXJhbmQ+CiAgICAgIDwvZGVmPgogICAgICA8ZGVmIGxvY2F0b3I9IjU5OjEtODQ6MTEiIG5hbWU9IlRvSW50ZXJ2YWwiIGNvbnRleHQ9IlBhdGllbnQiIGFjY2Vzc0xldmVsPSJQdWJsaWMiIHhzaTp0eXBlPSJGdW5jdGlvbkRlZiI+CiAgICAgICAgIDxleHByZXNzaW9uIGxvY2F0b3I9IjYwOjUtODQ6MTEiIHhzaTp0eXBlPSJJZiI+CiAgICAgICAgICAgIDxjb25kaXRpb24gbG9jYXRvcj0iNjA6OC02MDoyMyIgeHNpOnR5cGU9IklzTnVsbCI+CiAgICAgICAgICAgICAgIDxvcGVyYW5kIGxvY2F0b3I9IjYwOjgtNjA6MTUiIG5hbWU9InF1YW50aXR5IiB4c2k6dHlwZT0iT3BlcmFuZFJlZiIvPgogICAgICAgICAgICA8L2NvbmRpdGlvbj4KICAgICAgICAgICAgPHRoZW4geHNpOnR5cGU9IkFzIj4KICAgICAgICAgICAgICAgPG9wZXJhbmQgbG9jYXRvcj0iNjA6MzAtNjA6MzMiIHhzaTp0eXBlPSJOdWxsIi8+CiAgICAgICAgICAgICAgIDxhc1R5cGVTcGVjaWZpZXIgeHNpOnR5cGU9IkludGVydmFsVHlwZVNwZWNpZmllciI+CiAgICAgICAgICAgICAgICAgIDxwb2ludFR5cGUgbmFtZT0idDpRdWFudGl0eSIgeHNpOnR5cGU9Ik5hbWVkVHlwZVNwZWNpZmllciIvPgogICAgICAgICAgICAgICA8L2FzVHlwZVNwZWNpZmllcj4KICAgICAgICAgICAgPC90aGVuPgogICAgICAgICAgICA8ZWxzZSBsb2NhdG9yPSI2MTo5LTg0OjExIiB4c2k6dHlwZT0iQ2FzZSI+CiAgICAgICAgICAgICAgIDxjb21wYXJhbmQgbG9jYXRvcj0iNjE6MTQtNjE6MzgiIHBhdGg9InZhbHVlIiB4c2k6dHlwZT0iUHJvcGVydHkiPgogICAgICAgICAgICAgICAgICA8c291cmNlIGxvY2F0b3I9IjYxOjE0LTYxOjMyIiBwYXRoPSJjb21wYXJhdG9yIiB4c2k6dHlwZT0iUHJvcGVydHkiPgogICAgICAgICAgICAgICAgICAgICA8c291cmNlIGxvY2F0b3I9IjYxOjE0LTYxOjIxIiBuYW1lPSJxdWFudGl0eSIgeHNpOnR5cGU9Ik9wZXJhbmRSZWYiLz4KICAgICAgICAgICAgICAgICAgPC9zb3VyY2U+CiAgICAgICAgICAgICAgIDwvY29tcGFyYW5kPgogICAgICAgICAgICAgICA8Y2FzZUl0ZW0gbG9jYXRvcj0iNjI6MTMtNjY6MTciPgogICAgICAgICAgICAgICAgICA8d2hlbiBsb2NhdG9yPSI2MjoxOC02MjoyMCIgdmFsdWVUeXBlPSJ0OlN0cmluZyIgdmFsdWU9IiZsdDsiIHhzaTp0eXBlPSJMaXRlcmFsIi8+CiAgICAgICAgICAgICAgICAgIDx0aGVuIGxvY2F0b3I9IjYzOjE3LTY2OjE3IiBsb3dDbG9zZWQ9InRydWUiIGhpZ2hDbG9zZWQ9ImZhbHNlIiB4c2k6dHlwZT0iSW50ZXJ2YWwiPgogICAgICAgICAgICAgICAgICAgICA8bG93IGFzVHlwZT0idDpRdWFudGl0eSIgeHNpOnR5cGU9IkFzIj4KICAgICAgICAgICAgICAgICAgICAgICAgPG9wZXJhbmQgbG9jYXRvcj0iNjQ6MjEtNjQ6MjQiIHhzaTp0eXBlPSJOdWxsIi8+CiAgICAgICAgICAgICAgICAgICAgIDwvbG93PgogICAgICAgICAgICAgICAgICAgICA8aGlnaCBsb2NhdG9yPSI2NToyMS02NTo1OCIgbmFtZT0iVG9RdWFudGl0eUlnbm9yaW5nQ29tcGFyYXRvciIgeHNpOnR5cGU9IkZ1bmN0aW9uUmVmIj4KICAgICAgICAgICAgICAgICAgICAgICAgPG9wZXJhbmQgbG9jYXRvcj0iNjU6NTAtNjU6NTciIG5hbWU9InF1YW50aXR5IiB4c2k6dHlwZT0iT3BlcmFuZFJlZiIvPgogICAgICAgICAgICAgICAgICAgICA8L2hpZ2g+CiAgICAgICAgICAgICAgICAgIDwvdGhlbj4KICAgICAgICAgICAgICAgPC9jYXNlSXRlbT4KICAgICAgICAgICAgICAgPGNhc2VJdGVtIGxvY2F0b3I9IjY3OjEzLTcxOjE3Ij4KICAgICAgICAgICAgICAgICAgPHdoZW4gbG9jYXRvcj0iNjc6MTgtNjc6MjEiIHZhbHVlVHlwZT0idDpTdHJpbmciIHZhbHVlPSImbHQ7PSIgeHNpOnR5cGU9IkxpdGVyYWwiLz4KICAgICAgICAgICAgICAgICAgPHRoZW4gbG9jYXRvcj0iNjg6MTctNzE6MTciIGxvd0Nsb3NlZD0idHJ1ZSIgaGlnaENsb3NlZD0idHJ1ZSIgeHNpOnR5cGU9IkludGVydmFsIj4KICAgICAgICAgICAgICAgICAgICAgPGxvdyBhc1R5cGU9InQ6UXVhbnRpdHkiIHhzaTp0eXBlPSJBcyI+CiAgICAgICAgICAgICAgICAgICAgICAgIDxvcGVyYW5kIGxvY2F0b3I9IjY5OjIxLTY5OjI0IiB4c2k6dHlwZT0iTnVsbCIvPgogICAgICAgICAgICAgICAgICAgICA8L2xvdz4KICAgICAgICAgICAgICAgICAgICAgPGhpZ2ggbG9jYXRvcj0iNzA6MjEtNzA6NTgiIG5hbWU9IlRvUXVhbnRpdHlJZ25vcmluZ0NvbXBhcmF0b3IiIHhzaTp0eXBlPSJGdW5jdGlvblJlZiI+CiAgICAgICAgICAgICAgICAgICAgICAgIDxvcGVyYW5kIGxvY2F0b3I9IjcwOjUwLTcwOjU3IiBuYW1lPSJxdWFudGl0eSIgeHNpOnR5cGU9Ik9wZXJhbmRSZWYiLz4KICAgICAgICAgICAgICAgICAgICAgPC9oaWdoPgogICAgICAgICAgICAgICAgICA8L3RoZW4+CiAgICAgICAgICAgICAgIDwvY2FzZUl0ZW0+CiAgICAgICAgICAgICAgIDxjYXNlSXRlbSBsb2NhdG9yPSI3MjoxMy03NjoxNyI+CiAgICAgICAgICAgICAgICAgIDx3aGVuIGxvY2F0b3I9IjcyOjE4LTcyOjIxIiB2YWx1ZVR5cGU9InQ6U3RyaW5nIiB2YWx1ZT0iPj0iIHhzaTp0eXBlPSJMaXRlcmFsIi8+CiAgICAgICAgICAgICAgICAgIDx0aGVuIGxvY2F0b3I9IjczOjE3LTc2OjE3IiBsb3dDbG9zZWQ9InRydWUiIGhpZ2hDbG9zZWQ9InRydWUiIHhzaTp0eXBlPSJJbnRlcnZhbCI+CiAgICAgICAgICAgICAgICAgICAgIDxsb3cgbG9jYXRvcj0iNzQ6MjEtNzQ6NTgiIG5hbWU9IlRvUXVhbnRpdHlJZ25vcmluZ0NvbXBhcmF0b3IiIHhzaTp0eXBlPSJGdW5jdGlvblJlZiI+CiAgICAgICAgICAgICAgICAgICAgICAgIDxvcGVyYW5kIGxvY2F0b3I9Ijc0OjUwLTc0OjU3IiBuYW1lPSJxdWFudGl0eSIgeHNpOnR5cGU9Ik9wZXJhbmRSZWYiLz4KICAgICAgICAgICAgICAgICAgICAgPC9sb3c+CiAgICAgICAgICAgICAgICAgICAgIDxoaWdoIGFzVHlwZT0idDpRdWFudGl0eSIgeHNpOnR5cGU9IkFzIj4KICAgICAgICAgICAgICAgICAgICAgICAgPG9wZXJhbmQgbG9jYXRvcj0iNzU6MjEtNzU6MjQiIHhzaTp0eXBlPSJOdWxsIi8+CiAgICAgICAgICAgICAgICAgICAgIDwvaGlnaD4KICAgICAgICAgICAgICAgICAgPC90aGVuPgogICAgICAgICAgICAgICA8L2Nhc2VJdGVtPgogICAgICAgICAgICAgICA8Y2FzZUl0ZW0gbG9jYXRvcj0iNzc6MTMtODE6MTciPgogICAgICAgICAgICAgICAgICA8d2hlbiBsb2NhdG9yPSI3NzoxOC03NzoyMCIgdmFsdWVUeXBlPSJ0OlN0cmluZyIgdmFsdWU9Ij4iIHhzaTp0eXBlPSJMaXRlcmFsIi8+CiAgICAgICAgICAgICAgICAgIDx0aGVuIGxvY2F0b3I9Ijc4OjE3LTgxOjE3IiBsb3dDbG9zZWQ9ImZhbHNlIiBoaWdoQ2xvc2VkPSJ0cnVlIiB4c2k6dHlwZT0iSW50ZXJ2YWwiPgogICAgICAgICAgICAgICAgICAgICA8bG93IGxvY2F0b3I9Ijc5OjIxLTc5OjU4IiBuYW1lPSJUb1F1YW50aXR5SWdub3JpbmdDb21wYXJhdG9yIiB4c2k6dHlwZT0iRnVuY3Rpb25SZWYiPgogICAgICAgICAgICAgICAgICAgICAgICA8b3BlcmFuZCBsb2NhdG9yPSI3OTo1MC03OTo1NyIgbmFtZT0icXVhbnRpdHkiIHhzaTp0eXBlPSJPcGVyYW5kUmVmIi8+CiAgICAgICAgICAgICAgICAgICAgIDwvbG93PgogICAgICAgICAgICAgICAgICAgICA8aGlnaCBhc1R5cGU9InQ6UXVhbnRpdHkiIHhzaTp0eXBlPSJBcyI+CiAgICAgICAgICAgICAgICAgICAgICAgIDxvcGVyYW5kIGxvY2F0b3I9IjgwOjIxLTgwOjI0IiB4c2k6dHlwZT0iTnVsbCIvPgogICAgICAgICAgICAgICAgICAgICA8L2hpZ2g+CiAgICAgICAgICAgICAgICAgIDwvdGhlbj4KICAgICAgICAgICAgICAgPC9jYXNlSXRlbT4KICAgICAgICAgICAgICAgPGVsc2UgbG9jYXRvcj0iODM6MTctODM6NjgiIGxvd0Nsb3NlZD0idHJ1ZSIgaGlnaENsb3NlZD0idHJ1ZSIgeHNpOnR5cGU9IkludGVydmFsIj4KICAgICAgICAgICAgICAgICAgPGxvdyBsb2NhdG9yPSI4MzoyNi04Mzo0NSIgbmFtZT0iVG9RdWFudGl0eSIgeHNpOnR5cGU9IkZ1bmN0aW9uUmVmIj4KICAgICAgICAgICAgICAgICAgICAgPG9wZXJhbmQgbG9jYXRvcj0iODM6MzctODM6NDQiIG5hbWU9InF1YW50aXR5IiB4c2k6dHlwZT0iT3BlcmFuZFJlZiIvPgogICAgICAgICAgICAgICAgICA8L2xvdz4KICAgICAgICAgICAgICAgICAgPGhpZ2ggbG9jYXRvcj0iODM6NDgtODM6NjciIG5hbWU9IlRvUXVhbnRpdHkiIHhzaTp0eXBlPSJGdW5jdGlvblJlZiI+CiAgICAgICAgICAgICAgICAgICAgIDxvcGVyYW5kIGxvY2F0b3I9IjgzOjU5LTgzOjY2IiBuYW1lPSJxdWFudGl0eSIgeHNpOnR5cGU9Ik9wZXJhbmRSZWYiLz4KICAgICAgICAgICAgICAgICAgPC9oaWdoPgogICAgICAgICAgICAgICA8L2Vsc2U+CiAgICAgICAgICAgIDwvZWxzZT4KICAgICAgICAgPC9leHByZXNzaW9uPgogICAgICAgICA8b3BlcmFuZCBuYW1lPSJxdWFudGl0eSI+CiAgICAgICAgICAgIDxvcGVyYW5kVHlwZVNwZWNpZmllciBsb2NhdG9yPSI1OTozNy01OTo0OSIgbmFtZT0iZmhpcjpRdWFudGl0eSIgeHNpOnR5cGU9Ik5hbWVkVHlwZVNwZWNpZmllciIvPgogICAgICAgICA8L29wZXJhbmQ+CiAgICAgIDwvZGVmPgogICAgICA8ZGVmIGxvY2F0b3I9Ijg2OjEtOTA6MTA3IiBuYW1lPSJUb1JhdGlvIiBjb250ZXh0PSJQYXRpZW50IiBhY2Nlc3NMZXZlbD0iUHVibGljIiB4c2k6dHlwZT0iRnVuY3Rpb25EZWYiPgogICAgICAgICA8ZXhwcmVzc2lvbiBsb2NhdG9yPSI4Nzo1LTkwOjEwNyIgeHNpOnR5cGU9IklmIj4KICAgICAgICAgICAgPGNvbmRpdGlvbiBsb2NhdG9yPSI4Nzo4LTg3OjIwIiB4c2k6dHlwZT0iSXNOdWxsIj4KICAgICAgICAgICAgICAgPG9wZXJhbmQgbG9jYXRvcj0iODc6OC04NzoxMiIgbmFtZT0icmF0aW8iIHhzaTp0eXBlPSJPcGVyYW5kUmVmIi8+CiAgICAgICAgICAgIDwvY29uZGl0aW9uPgogICAgICAgICAgICA8dGhlbiBhc1R5cGU9InQ6UmF0aW8iIHhzaTp0eXBlPSJBcyI+CiAgICAgICAgICAgICAgIDxvcGVyYW5kIGxvY2F0b3I9Ijg4OjktODg6MTIiIHhzaTp0eXBlPSJOdWxsIi8+CiAgICAgICAgICAgIDwvdGhlbj4KICAgICAgICAgICAgPGVsc2UgbG9jYXRvcj0iOTA6OS05MDoxMDciIGNsYXNzVHlwZT0idDpSYXRpbyIgeHNpOnR5cGU9Ikluc3RhbmNlIj4KICAgICAgICAgICAgICAgPGVsZW1lbnQgbmFtZT0ibnVtZXJhdG9yIj4KICAgICAgICAgICAgICAgICAgPHZhbHVlIGxvY2F0b3I9IjkwOjM1LTkwOjYxIiBuYW1lPSJUb1F1YW50aXR5IiB4c2k6dHlwZT0iRnVuY3Rpb25SZWYiPgogICAgICAgICAgICAgICAgICAgICA8b3BlcmFuZCBsb2NhdG9yPSI5MDo0Ni05MDo2MCIgcGF0aD0ibnVtZXJhdG9yIiB4c2k6dHlwZT0iUHJvcGVydHkiPgogICAgICAgICAgICAgICAgICAgICAgICA8c291cmNlIGxvY2F0b3I9IjkwOjQ2LTkwOjUwIiBuYW1lPSJyYXRpbyIgeHNpOnR5cGU9Ik9wZXJhbmRSZWYiLz4KICAgICAgICAgICAgICAgICAgICAgPC9vcGVyYW5kPgogICAgICAgICAgICAgICAgICA8L3ZhbHVlPgogICAgICAgICAgICAgICA8L2VsZW1lbnQ+CiAgICAgICAgICAgICAgIDxlbGVtZW50IG5hbWU9ImRlbm9taW5hdG9yIj4KICAgICAgICAgICAgICAgICAgPHZhbHVlIGxvY2F0b3I9IjkwOjc3LTkwOjEwNSIgbmFtZT0iVG9RdWFudGl0eSIgeHNpOnR5cGU9IkZ1bmN0aW9uUmVmIj4KICAgICAgICAgICAgICAgICAgICAgPG9wZXJhbmQgbG9jYXRvcj0iOTA6ODgtOTA6MTA0IiBwYXRoPSJkZW5vbWluYXRvciIgeHNpOnR5cGU9IlByb3BlcnR5Ij4KICAgICAgICAgICAgICAgICAgICAgICAgPHNvdXJjZSBsb2NhdG9yPSI5MDo4OC05MDo5MiIgbmFtZT0icmF0aW8iIHhzaTp0eXBlPSJPcGVyYW5kUmVmIi8+CiAgICAgICAgICAgICAgICAgICAgIDwvb3BlcmFuZD4KICAgICAgICAgICAgICAgICAgPC92YWx1ZT4KICAgICAgICAgICAgICAgPC9lbGVtZW50PgogICAgICAgICAgICA8L2Vsc2U+CiAgICAgICAgIDwvZXhwcmVzc2lvbj4KICAgICAgICAgPG9wZXJhbmQgbmFtZT0icmF0aW8iPgogICAgICAgICAgICA8b3BlcmFuZFR5cGVTcGVjaWZpZXIgbG9jYXRvcj0iODY6MzEtODY6NDAiIG5hbWU9ImZoaXI6UmF0aW8iIHhzaTp0eXBlPSJOYW1lZFR5cGVTcGVjaWZpZXIiLz4KICAgICAgICAgPC9vcGVyYW5kPgogICAgICA8L2RlZj4KICAgICAgPGRlZiBsb2NhdG9yPSI5MjoxLTk2OjYzIiBuYW1lPSJUb0ludGVydmFsIiBjb250ZXh0PSJQYXRpZW50IiBhY2Nlc3NMZXZlbD0iUHVibGljIiB4c2k6dHlwZT0iRnVuY3Rpb25EZWYiPgogICAgICAgICA8ZXhwcmVzc2lvbiBsb2NhdG9yPSI5Mzo1LTk2OjYzIiB4c2k6dHlwZT0iSWYiPgogICAgICAgICAgICA8Y29uZGl0aW9uIGxvY2F0b3I9IjkzOjgtOTM6MjAiIHhzaTp0eXBlPSJJc051bGwiPgogICAgICAgICAgICAgICA8b3BlcmFuZCBsb2NhdG9yPSI5Mzo4LTkzOjEyIiBuYW1lPSJyYW5nZSIgeHNpOnR5cGU9Ik9wZXJhbmRSZWYiLz4KICAgICAgICAgICAgPC9jb25kaXRpb24+CiAgICAgICAgICAgIDx0aGVuIHhzaTp0eXBlPSJBcyI+CiAgICAgICAgICAgICAgIDxvcGVyYW5kIGxvY2F0b3I9Ijk0OjktOTQ6MTIiIHhzaTp0eXBlPSJOdWxsIi8+CiAgICAgICAgICAgICAgIDxhc1R5cGVTcGVjaWZpZXIgeHNpOnR5cGU9IkludGVydmFsVHlwZVNwZWNpZmllciI+CiAgICAgICAgICAgICAgICAgIDxwb2ludFR5cGUgbmFtZT0idDpRdWFudGl0eSIgeHNpOnR5cGU9Ik5hbWVkVHlwZVNwZWNpZmllciIvPgogICAgICAgICAgICAgICA8L2FzVHlwZVNwZWNpZmllcj4KICAgICAgICAgICAgPC90aGVuPgogICAgICAgICAgICA8ZWxzZSBsb2NhdG9yPSI5Njo5LTk2OjYzIiBsb3dDbG9zZWQ9InRydWUiIGhpZ2hDbG9zZWQ9InRydWUiIHhzaTp0eXBlPSJJbnRlcnZhbCI+CiAgICAgICAgICAgICAgIDxsb3cgbG9jYXRvcj0iOTY6MTgtOTY6MzgiIG5hbWU9IlRvUXVhbnRpdHkiIHhzaTp0eXBlPSJGdW5jdGlvblJlZiI+CiAgICAgICAgICAgICAgICAgIDxvcGVyYW5kIGxvY2F0b3I9Ijk2OjI5LTk2OjM3IiBwYXRoPSJsb3ciIHhzaTp0eXBlPSJQcm9wZXJ0eSI+CiAgICAgICAgICAgICAgICAgICAgIDxzb3VyY2UgbG9jYXRvcj0iOTY6MjktOTY6MzMiIG5hbWU9InJhbmdlIiB4c2k6dHlwZT0iT3BlcmFuZFJlZiIvPgogICAgICAgICAgICAgICAgICA8L29wZXJhbmQ+CiAgICAgICAgICAgICAgIDwvbG93PgogICAgICAgICAgICAgICA8aGlnaCBsb2NhdG9yPSI5Njo0MS05Njo2MiIgbmFtZT0iVG9RdWFudGl0eSIgeHNpOnR5cGU9IkZ1bmN0aW9uUmVmIj4KICAgICAgICAgICAgICAgICAgPG9wZXJhbmQgbG9jYXRvcj0iOTY6NTItOTY6NjEiIHBhdGg9ImhpZ2giIHhzaTp0eXBlPSJQcm9wZXJ0eSI+CiAgICAgICAgICAgICAgICAgICAgIDxzb3VyY2UgbG9jYXRvcj0iOTY6NTItOTY6NTYiIG5hbWU9InJhbmdlIiB4c2k6dHlwZT0iT3BlcmFuZFJlZiIvPgogICAgICAgICAgICAgICAgICA8L29wZXJhbmQ+CiAgICAgICAgICAgICAgIDwvaGlnaD4KICAgICAgICAgICAgPC9lbHNlPgogICAgICAgICA8L2V4cHJlc3Npb24+CiAgICAgICAgIDxvcGVyYW5kIG5hbWU9InJhbmdlIj4KICAgICAgICAgICAgPG9wZXJhbmRUeXBlU3BlY2lmaWVyIGxvY2F0b3I9IjkyOjM0LTkyOjQzIiBuYW1lPSJmaGlyOlJhbmdlIiB4c2k6dHlwZT0iTmFtZWRUeXBlU3BlY2lmaWVyIi8+CiAgICAgICAgIDwvb3BlcmFuZD4KICAgICAgPC9kZWY+CiAgICAgIDxkZWYgbG9jYXRvcj0iOTg6MS0xMDc6OSIgbmFtZT0iVG9Db2RlIiBjb250ZXh0PSJQYXRpZW50IiBhY2Nlc3NMZXZlbD0iUHVibGljIiB4c2k6dHlwZT0iRnVuY3Rpb25EZWYiPgogICAgICAgICA8ZXhwcmVzc2lvbiBsb2NhdG9yPSI5OTo1LTEwNzo5IiB4c2k6dHlwZT0iSWYiPgogICAgICAgICAgICA8Y29uZGl0aW9uIGxvY2F0b3I9Ijk5OjgtOTk6MjEiIHhzaTp0eXBlPSJJc051bGwiPgogICAgICAgICAgICAgICA8b3BlcmFuZCBsb2NhdG9yPSI5OTo4LTk5OjEzIiBuYW1lPSJjb2RpbmciIHhzaTp0eXBlPSJPcGVyYW5kUmVmIi8+CiAgICAgICAgICAgIDwvY29uZGl0aW9uPgogICAgICAgICAgICA8dGhlbiBhc1R5cGU9InQ6Q29kZSIgeHNpOnR5cGU9IkFzIj4KICAgICAgICAgICAgICAgPG9wZXJhbmQgbG9jYXRvcj0iMTAwOjktMTAwOjEyIiB4c2k6dHlwZT0iTnVsbCIvPgogICAgICAgICAgICA8L3RoZW4+CiAgICAgICAgICAgIDxlbHNlIGxvY2F0b3I9IjEwMjo5LTEwNzo5IiBjbGFzc1R5cGU9InQ6Q29kZSIgeHNpOnR5cGU9Ikluc3RhbmNlIj4KICAgICAgICAgICAgICAgPGVsZW1lbnQgbmFtZT0iY29kZSI+CiAgICAgICAgICAgICAgICAgIDx2YWx1ZSBsb2NhdG9yPSIxMDM6MTctMTAzOjMzIiBwYXRoPSJ2YWx1ZSIgeHNpOnR5cGU9IlByb3BlcnR5Ij4KICAgICAgICAgICAgICAgICAgICAgPHNvdXJjZSBsb2NhdG9yPSIxMDM6MTctMTAzOjI3IiBwYXRoPSJjb2RlIiB4c2k6dHlwZT0iUHJvcGVydHkiPgogICAgICAgICAgICAgICAgICAgICAgICA8c291cmNlIGxvY2F0b3I9IjEwMzoxNy0xMDM6MjIiIG5hbWU9ImNvZGluZyIgeHNpOnR5cGU9Ik9wZXJhbmRSZWYiLz4KICAgICAgICAgICAgICAgICAgICAgPC9zb3VyY2U+CiAgICAgICAgICAgICAgICAgIDwvdmFsdWU+CiAgICAgICAgICAgICAgIDwvZWxlbWVudD4KICAgICAgICAgICAgICAgPGVsZW1lbnQgbmFtZT0ic3lzdGVtIj4KICAgICAgICAgICAgICAgICAgPHZhbHVlIGxvY2F0b3I9IjEwNDoxOS0xMDQ6MzciIHBhdGg9InZhbHVlIiB4c2k6dHlwZT0iUHJvcGVydHkiPgogICAgICAgICAgICAgICAgICAgICA8c291cmNlIGxvY2F0b3I9IjEwNDoxOS0xMDQ6MzEiIHBhdGg9InN5c3RlbSIgeHNpOnR5cGU9IlByb3BlcnR5Ij4KICAgICAgICAgICAgICAgICAgICAgICAgPHNvdXJjZSBsb2NhdG9yPSIxMDQ6MTktMTA0OjI0IiBuYW1lPSJjb2RpbmciIHhzaTp0eXBlPSJPcGVyYW5kUmVmIi8+CiAgICAgICAgICAgICAgICAgICAgIDwvc291cmNlPgogICAgICAgICAgICAgICAgICA8L3ZhbHVlPgogICAgICAgICAgICAgICA8L2VsZW1lbnQ+CiAgICAgICAgICAgICAgIDxlbGVtZW50IG5hbWU9InZlcnNpb24iPgogICAgICAgICAgICAgICAgICA8dmFsdWUgbG9jYXRvcj0iMTA1OjIwLTEwNTozOSIgcGF0aD0idmFsdWUiIHhzaTp0eXBlPSJQcm9wZXJ0eSI+CiAgICAgICAgICAgICAgICAgICAgIDxzb3VyY2UgbG9jYXRvcj0iMTA1OjIwLTEwNTozMyIgcGF0aD0idmVyc2lvbiIgeHNpOnR5cGU9IlByb3BlcnR5Ij4KICAgICAgICAgICAgICAgICAgICAgICAgPHNvdXJjZSBsb2NhdG9yPSIxMDU6MjAtMTA1OjI1IiBuYW1lPSJjb2RpbmciIHhzaTp0eXBlPSJPcGVyYW5kUmVmIi8+CiAgICAgICAgICAgICAgICAgICAgIDwvc291cmNlPgogICAgICAgICAgICAgICAgICA8L3ZhbHVlPgogICAgICAgICAgICAgICA8L2VsZW1lbnQ+CiAgICAgICAgICAgICAgIDxlbGVtZW50IG5hbWU9ImRpc3BsYXkiPgogICAgICAgICAgICAgICAgICA8dmFsdWUgbG9jYXRvcj0iMTA2OjIwLTEwNjozOSIgcGF0aD0idmFsdWUiIHhzaTp0eXBlPSJQcm9wZXJ0eSI+CiAgICAgICAgICAgICAgICAgICAgIDxzb3VyY2UgbG9jYXRvcj0iMTA2OjIwLTEwNjozMyIgcGF0aD0iZGlzcGxheSIgeHNpOnR5cGU9IlByb3BlcnR5Ij4KICAgICAgICAgICAgICAgICAgICAgICAgPHNvdXJjZSBsb2NhdG9yPSIxMDY6MjAtMTA2OjI1IiBuYW1lPSJjb2RpbmciIHhzaTp0eXBlPSJPcGVyYW5kUmVmIi8+CiAgICAgICAgICAgICAgICAgICAgIDwvc291cmNlPgogICAgICAgICAgICAgICAgICA8L3ZhbHVlPgogICAgICAgICAgICAgICA8L2VsZW1lbnQ+CiAgICAgICAgICAgIDwvZWxzZT4KICAgICAgICAgPC9leHByZXNzaW9uPgogICAgICAgICA8b3BlcmFuZCBuYW1lPSJjb2RpbmciPgogICAgICAgICAgICA8b3BlcmFuZFR5cGVTcGVjaWZpZXIgbG9jYXRvcj0iOTg6MzEtOTg6NDEiIG5hbWU9ImZoaXI6Q29kaW5nIiB4c2k6dHlwZT0iTmFtZWRUeXBlU3BlY2lmaWVyIi8+CiAgICAgICAgIDwvb3BlcmFuZD4KICAgICAgPC9kZWY+CiAgICAgIDxkZWYgbG9jYXRvcj0iMTA5OjEtMTE2OjkiIG5hbWU9IlRvQ29uY2VwdCIgY29udGV4dD0iUGF0aWVudCIgYWNjZXNzTGV2ZWw9IlB1YmxpYyIgeHNpOnR5cGU9IkZ1bmN0aW9uRGVmIj4KICAgICAgICAgPGV4cHJlc3Npb24gbG9jYXRvcj0iMTEwOjUtMTE2OjkiIHhzaTp0eXBlPSJJZiI+CiAgICAgICAgICAgIDxjb25kaXRpb24gbG9jYXRvcj0iMTEwOjgtMTEwOjIyIiB4c2k6dHlwZT0iSXNOdWxsIj4KICAgICAgICAgICAgICAgPG9wZXJhbmQgbG9jYXRvcj0iMTEwOjgtMTEwOjE0IiBuYW1lPSJjb25jZXB0IiB4c2k6dHlwZT0iT3BlcmFuZFJlZiIvPgogICAgICAgICAgICA8L2NvbmRpdGlvbj4KICAgICAgICAgICAgPHRoZW4gYXNUeXBlPSJ0OkNvbmNlcHQiIHhzaTp0eXBlPSJBcyI+CiAgICAgICAgICAgICAgIDxvcGVyYW5kIGxvY2F0b3I9IjExMTo5LTExMToxMiIgeHNpOnR5cGU9Ik51bGwiLz4KICAgICAgICAgICAgPC90aGVuPgogICAgICAgICAgICA8ZWxzZSBsb2NhdG9yPSIxMTM6OS0xMTY6OSIgY2xhc3NUeXBlPSJ0OkNvbmNlcHQiIHhzaTp0eXBlPSJJbnN0YW5jZSI+CiAgICAgICAgICAgICAgIDxlbGVtZW50IG5hbWU9ImNvZGVzIj4KICAgICAgICAgICAgICAgICAgPHZhbHVlIGxvY2F0b3I9IjExNDoyMC0xMTQ6NTIiIHhzaTp0eXBlPSJRdWVyeSI+CiAgICAgICAgICAgICAgICAgICAgIDxzb3VyY2UgbG9jYXRvcj0iMTE0OjIwLTExNDozNSIgYWxpYXM9IkMiPgogICAgICAgICAgICAgICAgICAgICAgICA8ZXhwcmVzc2lvbiBsb2NhdG9yPSIxMTQ6MjAtMTE0OjMzIiBwYXRoPSJjb2RpbmciIHhzaTp0eXBlPSJQcm9wZXJ0eSI+CiAgICAgICAgICAgICAgICAgICAgICAgICAgIDxzb3VyY2UgbmFtZT0iY29uY2VwdCIgeHNpOnR5cGU9Ik9wZXJhbmRSZWYiLz4KICAgICAgICAgICAgICAgICAgICAgICAgPC9leHByZXNzaW9uPgogICAgICAgICAgICAgICAgICAgICA8L3NvdXJjZT4KICAgICAgICAgICAgICAgICAgICAgPHJldHVybiBsb2NhdG9yPSIxMTQ6MzctMTE0OjUyIj4KICAgICAgICAgICAgICAgICAgICAgICAgPGV4cHJlc3Npb24gbG9jYXRvcj0iMTE0OjQ0LTExNDo1MiIgbmFtZT0iVG9Db2RlIiB4c2k6dHlwZT0iRnVuY3Rpb25SZWYiPgogICAgICAgICAgICAgICAgICAgICAgICAgICA8b3BlcmFuZCBsb2NhdG9yPSIxMTQ6NTEiIG5hbWU9IkMiIHhzaTp0eXBlPSJBbGlhc1JlZiIvPgogICAgICAgICAgICAgICAgICAgICAgICA8L2V4cHJlc3Npb24+CiAgICAgICAgICAgICAgICAgICAgIDwvcmV0dXJuPgogICAgICAgICAgICAgICAgICA8L3ZhbHVlPgogICAgICAgICAgICAgICA8L2VsZW1lbnQ+CiAgICAgICAgICAgICAgIDxlbGVtZW50IG5hbWU9ImRpc3BsYXkiPgogICAgICAgICAgICAgICAgICA8dmFsdWUgbG9jYXRvcj0iMTE1OjIyLTExNTozOSIgcGF0aD0idmFsdWUiIHhzaTp0eXBlPSJQcm9wZXJ0eSI+CiAgICAgICAgICAgICAgICAgICAgIDxzb3VyY2UgbG9jYXRvcj0iMTE1OjIyLTExNTozMyIgcGF0aD0idGV4dCIgeHNpOnR5cGU9IlByb3BlcnR5Ij4KICAgICAgICAgICAgICAgICAgICAgICAgPHNvdXJjZSBsb2NhdG9yPSIxMTU6MjItMTE1OjI4IiBuYW1lPSJjb25jZXB0IiB4c2k6dHlwZT0iT3BlcmFuZFJlZiIvPgogICAgICAgICAgICAgICAgICAgICA8L3NvdXJjZT4KICAgICAgICAgICAgICAgICAgPC92YWx1ZT4KICAgICAgICAgICAgICAgPC9lbGVtZW50PgogICAgICAgICAgICA8L2Vsc2U+CiAgICAgICAgIDwvZXhwcmVzc2lvbj4KICAgICAgICAgPG9wZXJhbmQgbmFtZT0iY29uY2VwdCI+CiAgICAgICAgICAgIDxvcGVyYW5kVHlwZVNwZWNpZmllciBsb2NhdG9yPSIxMDk6MzUtMTA5OjU0IiBuYW1lPSJmaGlyOkNvZGVhYmxlQ29uY2VwdCIgeHNpOnR5cGU9Ik5hbWVkVHlwZVNwZWNpZmllciIvPgogICAgICAgICA8L29wZXJhbmQ+CiAgICAgIDwvZGVmPgogICAgICA8ZGVmIGxvY2F0b3I9IjExODoxLTEyMjo2MCIgbmFtZT0icmVmZXJlbmNlIiBjb250ZXh0PSJQYXRpZW50IiBhY2Nlc3NMZXZlbD0iUHVibGljIiB4c2k6dHlwZT0iRnVuY3Rpb25EZWYiPgogICAgICAgICA8ZXhwcmVzc2lvbiBsb2NhdG9yPSIxMTk6NS0xMjI6NjAiIHhzaTp0eXBlPSJJZiI+CiAgICAgICAgICAgIDxjb25kaXRpb24gbG9jYXRvcj0iMTE5OjgtMTE5OjI0IiB4c2k6dHlwZT0iSXNOdWxsIj4KICAgICAgICAgICAgICAgPG9wZXJhbmQgbG9jYXRvcj0iMTE5OjgtMTE5OjE2IiBuYW1lPSJyZWZlcmVuY2UiIHhzaTp0eXBlPSJPcGVyYW5kUmVmIi8+CiAgICAgICAgICAgIDwvY29uZGl0aW9uPgogICAgICAgICAgICA8dGhlbiBhc1R5cGU9ImZoaXI6UmVmZXJlbmNlIiB4c2k6dHlwZT0iQXMiPgogICAgICAgICAgICAgICA8b3BlcmFuZCBsb2NhdG9yPSIxMjA6OS0xMjA6MTIiIHhzaTp0eXBlPSJOdWxsIi8+CiAgICAgICAgICAgIDwvdGhlbj4KICAgICAgICAgICAgPGVsc2UgbG9jYXRvcj0iMTIyOjktMTIyOjYwIiBjbGFzc1R5cGU9ImZoaXI6UmVmZXJlbmNlIiB4c2k6dHlwZT0iSW5zdGFuY2UiPgogICAgICAgICAgICAgICA8ZWxlbWVudCBuYW1lPSJyZWZlcmVuY2UiPgogICAgICAgICAgICAgICAgICA8dmFsdWUgbG9jYXRvcj0iMTIyOjMyLTEyMjo1OCIgY2xhc3NUeXBlPSJmaGlyOnN0cmluZyIgeHNpOnR5cGU9Ikluc3RhbmNlIj4KICAgICAgICAgICAgICAgICAgICAgPGVsZW1lbnQgbmFtZT0idmFsdWUiPgogICAgICAgICAgICAgICAgICAgICAgICA8dmFsdWUgbG9jYXRvcj0iMTIyOjQ4LTEyMjo1NiIgbmFtZT0icmVmZXJlbmNlIiB4c2k6dHlwZT0iT3BlcmFuZFJlZiIvPgogICAgICAgICAgICAgICAgICAgICA8L2VsZW1lbnQ+CiAgICAgICAgICAgICAgICAgIDwvdmFsdWU+CiAgICAgICAgICAgICAgIDwvZWxlbWVudD4KICAgICAgICAgICAgPC9lbHNlPgogICAgICAgICA8L2V4cHJlc3Npb24+CiAgICAgICAgIDxvcGVyYW5kIG5hbWU9InJlZmVyZW5jZSI+CiAgICAgICAgICAgIDxvcGVyYW5kVHlwZVNwZWNpZmllciBsb2NhdG9yPSIxMTg6MzctMTE4OjQyIiBuYW1lPSJ0OlN0cmluZyIgeHNpOnR5cGU9Ik5hbWVkVHlwZVNwZWNpZmllciIvPgogICAgICAgICA8L29wZXJhbmQ+CiAgICAgIDwvZGVmPgogICAgICA8ZGVmIGxvY2F0b3I9IjEyNDoxLTEyNDo2OCIgbmFtZT0icmVzb2x2ZSIgY29udGV4dD0iUGF0aWVudCIgYWNjZXNzTGV2ZWw9IlB1YmxpYyIgZXh0ZXJuYWw9InRydWUiIHhzaTp0eXBlPSJGdW5jdGlvbkRlZiI+CiAgICAgICAgIDxvcGVyYW5kIG5hbWU9InJlZmVyZW5jZSI+CiAgICAgICAgICAgIDxvcGVyYW5kVHlwZVNwZWNpZmllciBsb2NhdG9yPSIxMjQ6MzUtMTI0OjQwIiBuYW1lPSJ0OlN0cmluZyIgeHNpOnR5cGU9Ik5hbWVkVHlwZVNwZWNpZmllciIvPgogICAgICAgICA8L29wZXJhbmQ+CiAgICAgIDwvZGVmPgogICAgICA8ZGVmIGxvY2F0b3I9IjEyNToxLTEyNTo3MSIgbmFtZT0icmVzb2x2ZSIgY29udGV4dD0iUGF0aWVudCIgYWNjZXNzTGV2ZWw9IlB1YmxpYyIgZXh0ZXJuYWw9InRydWUiIHhzaTp0eXBlPSJGdW5jdGlvbkRlZiI+CiAgICAgICAgIDxvcGVyYW5kIG5hbWU9InJlZmVyZW5jZSI+CiAgICAgICAgICAgIDxvcGVyYW5kVHlwZVNwZWNpZmllciBsb2NhdG9yPSIxMjU6MzUtMTI1OjQzIiBuYW1lPSJmaGlyOlJlZmVyZW5jZSIgeHNpOnR5cGU9Ik5hbWVkVHlwZVNwZWNpZmllciIvPgogICAgICAgICA8L29wZXJhbmQ+CiAgICAgIDwvZGVmPgogICAgICA8ZGVmIGxvY2F0b3I9IjEyNjoxLTEyNjo3MiIgbmFtZT0icmVmZXJlbmNlIiBjb250ZXh0PSJQYXRpZW50IiBhY2Nlc3NMZXZlbD0iUHVibGljIiBleHRlcm5hbD0idHJ1ZSIgeHNpOnR5cGU9IkZ1bmN0aW9uRGVmIj4KICAgICAgICAgPG9wZXJhbmQgbmFtZT0icmVzb3VyY2UiPgogICAgICAgICAgICA8b3BlcmFuZFR5cGVTcGVjaWZpZXIgbG9jYXRvcj0iMTI2OjM2LTEyNjo0MyIgbmFtZT0iZmhpcjpSZXNvdXJjZSIgeHNpOnR5cGU9Ik5hbWVkVHlwZVNwZWNpZmllciIvPgogICAgICAgICA8L29wZXJhbmQ+CiAgICAgIDwvZGVmPgogICAgICA8ZGVmIGxvY2F0b3I9IjEyNzoxLTEyNzo4NiIgbmFtZT0iZXh0ZW5zaW9uIiBjb250ZXh0PSJQYXRpZW50IiBhY2Nlc3NMZXZlbD0iUHVibGljIiBleHRlcm5hbD0idHJ1ZSIgeHNpOnR5cGU9IkZ1bmN0aW9uRGVmIj4KICAgICAgICAgPG9wZXJhbmQgbmFtZT0iZWxlbWVudCI+CiAgICAgICAgICAgIDxvcGVyYW5kVHlwZVNwZWNpZmllciBsb2NhdG9yPSIxMjc6MzUtMTI3OjQxIiBuYW1lPSJmaGlyOkVsZW1lbnQiIHhzaTp0eXBlPSJOYW1lZFR5cGVTcGVjaWZpZXIiLz4KICAgICAgICAgPC9vcGVyYW5kPgogICAgICAgICA8b3BlcmFuZCBuYW1lPSJ1cmwiPgogICAgICAgICAgICA8b3BlcmFuZFR5cGVTcGVjaWZpZXIgbG9jYXRvcj0iMTI3OjQ4LTEyNzo1MyIgbmFtZT0idDpTdHJpbmciIHhzaTp0eXBlPSJOYW1lZFR5cGVTcGVjaWZpZXIiLz4KICAgICAgICAgPC9vcGVyYW5kPgogICAgICA8L2RlZj4KICAgICAgPGRlZiBsb2NhdG9yPSIxMjg6MS0xMjg6ODgiIG5hbWU9ImV4dGVuc2lvbiIgY29udGV4dD0iUGF0aWVudCIgYWNjZXNzTGV2ZWw9IlB1YmxpYyIgZXh0ZXJuYWw9InRydWUiIHhzaTp0eXBlPSJGdW5jdGlvbkRlZiI+CiAgICAgICAgIDxvcGVyYW5kIG5hbWU9InJlc291cmNlIj4KICAgICAgICAgICAgPG9wZXJhbmRUeXBlU3BlY2lmaWVyIGxvY2F0b3I9IjEyODozNi0xMjg6NDMiIG5hbWU9ImZoaXI6UmVzb3VyY2UiIHhzaTp0eXBlPSJOYW1lZFR5cGVTcGVjaWZpZXIiLz4KICAgICAgICAgPC9vcGVyYW5kPgogICAgICAgICA8b3BlcmFuZCBuYW1lPSJ1cmwiPgogICAgICAgICAgICA8b3BlcmFuZFR5cGVTcGVjaWZpZXIgbG9jYXRvcj0iMTI4OjUwLTEyODo1NSIgbmFtZT0idDpTdHJpbmciIHhzaTp0eXBlPSJOYW1lZFR5cGVTcGVjaWZpZXIiLz4KICAgICAgICAgPC9vcGVyYW5kPgogICAgICA8L2RlZj4KICAgICAgPGRlZiBsb2NhdG9yPSIxMjk6MS0xMjk6NjciIG5hbWU9Imhhc1ZhbHVlIiBjb250ZXh0PSJQYXRpZW50IiBhY2Nlc3NMZXZlbD0iUHVibGljIiBleHRlcm5hbD0idHJ1ZSIgeHNpOnR5cGU9IkZ1bmN0aW9uRGVmIj4KICAgICAgICAgPG9wZXJhbmQgbmFtZT0iZWxlbWVudCI+CiAgICAgICAgICAgIDxvcGVyYW5kVHlwZVNwZWNpZmllciBsb2NhdG9yPSIxMjk6MzQtMTI5OjQwIiBuYW1lPSJmaGlyOkVsZW1lbnQiIHhzaTp0eXBlPSJOYW1lZFR5cGVTcGVjaWZpZXIiLz4KICAgICAgICAgPC9vcGVyYW5kPgogICAgICA8L2RlZj4KICAgICAgPGRlZiBsb2NhdG9yPSIxMzA6MS0xMzA6NjMiIG5hbWU9ImdldFZhbHVlIiBjb250ZXh0PSJQYXRpZW50IiBhY2Nlc3NMZXZlbD0iUHVibGljIiBleHRlcm5hbD0idHJ1ZSIgeHNpOnR5cGU9IkZ1bmN0aW9uRGVmIj4KICAgICAgICAgPG9wZXJhbmQgbmFtZT0iZWxlbWVudCI+CiAgICAgICAgICAgIDxvcGVyYW5kVHlwZVNwZWNpZmllciBsb2NhdG9yPSIxMzA6MzQtMTMwOjQwIiBuYW1lPSJmaGlyOkVsZW1lbnQiIHhzaTp0eXBlPSJOYW1lZFR5cGVTcGVjaWZpZXIiLz4KICAgICAgICAgPC9vcGVyYW5kPgogICAgICA8L2RlZj4KICAgICAgPGRlZiBsb2NhdG9yPSIxMzE6MS0xMzE6NjkiIG5hbWU9Im9mVHlwZSIgY29udGV4dD0iUGF0aWVudCIgYWNjZXNzTGV2ZWw9IlB1YmxpYyIgZXh0ZXJuYWw9InRydWUiIHhzaTp0eXBlPSJGdW5jdGlvbkRlZiI+CiAgICAgICAgIDxvcGVyYW5kIG5hbWU9ImlkZW50aWZpZXIiPgogICAgICAgICAgICA8b3BlcmFuZFR5cGVTcGVjaWZpZXIgbG9jYXRvcj0iMTMxOjM1LTEzMTo0MCIgbmFtZT0idDpTdHJpbmciIHhzaTp0eXBlPSJOYW1lZFR5cGVTcGVjaWZpZXIiLz4KICAgICAgICAgPC9vcGVyYW5kPgogICAgICA8L2RlZj4KICAgICAgPGRlZiBsb2NhdG9yPSIxMzI6MS0xMzI6NjMiIG5hbWU9ImlzIiBjb250ZXh0PSJQYXRpZW50IiBhY2Nlc3NMZXZlbD0iUHVibGljIiBleHRlcm5hbD0idHJ1ZSIgeHNpOnR5cGU9IkZ1bmN0aW9uRGVmIj4KICAgICAgICAgPG9wZXJhbmQgbmFtZT0iaWRlbnRpZmllciI+CiAgICAgICAgICAgIDxvcGVyYW5kVHlwZVNwZWNpZmllciBsb2NhdG9yPSIxMzI6MzEtMTMyOjM2IiBuYW1lPSJ0OlN0cmluZyIgeHNpOnR5cGU9Ik5hbWVkVHlwZVNwZWNpZmllciIvPgogICAgICAgICA8L29wZXJhbmQ+CiAgICAgIDwvZGVmPgogICAgICA8ZGVmIGxvY2F0b3I9IjEzMzoxLTEzMzo1OSIgbmFtZT0iYXMiIGNvbnRleHQ9IlBhdGllbnQiIGFjY2Vzc0xldmVsPSJQdWJsaWMiIGV4dGVybmFsPSJ0cnVlIiB4c2k6dHlwZT0iRnVuY3Rpb25EZWYiPgogICAgICAgICA8b3BlcmFuZCBuYW1lPSJpZGVudGlmaWVyIj4KICAgICAgICAgICAgPG9wZXJhbmRUeXBlU3BlY2lmaWVyIGxvY2F0b3I9IjEzMzozMS0xMzM6MzYiIG5hbWU9InQ6U3RyaW5nIiB4c2k6dHlwZT0iTmFtZWRUeXBlU3BlY2lmaWVyIi8+CiAgICAgICAgIDwvb3BlcmFuZD4KICAgICAgPC9kZWY+CiAgICAgIDxkZWYgbG9jYXRvcj0iMTM0OjEtMTM0Ojg2IiBuYW1lPSJlbGVtZW50RGVmaW5pdGlvbiIgY29udGV4dD0iUGF0aWVudCIgYWNjZXNzTGV2ZWw9IlB1YmxpYyIgZXh0ZXJuYWw9InRydWUiIHhzaTp0eXBlPSJGdW5jdGlvbkRlZiI+CiAgICAgICAgIDxvcGVyYW5kIG5hbWU9ImVsZW1lbnQiPgogICAgICAgICAgICA8b3BlcmFuZFR5cGVTcGVjaWZpZXIgbG9jYXRvcj0iMTM0OjQzLTEzNDo0OSIgbmFtZT0iZmhpcjpFbGVtZW50IiB4c2k6dHlwZT0iTmFtZWRUeXBlU3BlY2lmaWVyIi8+CiAgICAgICAgIDwvb3BlcmFuZD4KICAgICAgPC9kZWY+CiAgICAgIDxkZWYgbG9jYXRvcj0iMTM1OjEtMTM1Ojk1IiBuYW1lPSJzbGljZSIgY29udGV4dD0iUGF0aWVudCIgYWNjZXNzTGV2ZWw9IlB1YmxpYyIgZXh0ZXJuYWw9InRydWUiIHhzaTp0eXBlPSJGdW5jdGlvbkRlZiI+CiAgICAgICAgIDxvcGVyYW5kIG5hbWU9ImVsZW1lbnQiPgogICAgICAgICAgICA8b3BlcmFuZFR5cGVTcGVjaWZpZXIgbG9jYXRvcj0iMTM1OjMxLTEzNTozNyIgbmFtZT0iZmhpcjpFbGVtZW50IiB4c2k6dHlwZT0iTmFtZWRUeXBlU3BlY2lmaWVyIi8+CiAgICAgICAgIDwvb3BlcmFuZD4KICAgICAgICAgPG9wZXJhbmQgbmFtZT0idXJsIj4KICAgICAgICAgICAgPG9wZXJhbmRUeXBlU3BlY2lmaWVyIGxvY2F0b3I9IjEzNTo0NC0xMzU6NDkiIG5hbWU9InQ6U3RyaW5nIiB4c2k6dHlwZT0iTmFtZWRUeXBlU3BlY2lmaWVyIi8+CiAgICAgICAgIDwvb3BlcmFuZD4KICAgICAgICAgPG9wZXJhbmQgbmFtZT0ibmFtZSI+CiAgICAgICAgICAgIDxvcGVyYW5kVHlwZVNwZWNpZmllciBsb2NhdG9yPSIxMzU6NTctMTM1OjYyIiBuYW1lPSJ0OlN0cmluZyIgeHNpOnR5cGU9Ik5hbWVkVHlwZVNwZWNpZmllciIvPgogICAgICAgICA8L29wZXJhbmQ+CiAgICAgIDwvZGVmPgogICAgICA8ZGVmIGxvY2F0b3I9IjEzNjoxLTEzNjo3NiIgbmFtZT0iY2hlY2tNb2RpZmllcnMiIGNvbnRleHQ9IlBhdGllbnQiIGFjY2Vzc0xldmVsPSJQdWJsaWMiIGV4dGVybmFsPSJ0cnVlIiB4c2k6dHlwZT0iRnVuY3Rpb25EZWYiPgogICAgICAgICA8b3BlcmFuZCBuYW1lPSJyZXNvdXJjZSI+CiAgICAgICAgICAgIDxvcGVyYW5kVHlwZVNwZWNpZmllciBsb2NhdG9yPSIxMzY6NDEtMTM2OjQ4IiBuYW1lPSJmaGlyOlJlc291cmNlIiB4c2k6dHlwZT0iTmFtZWRUeXBlU3BlY2lmaWVyIi8+CiAgICAgICAgIDwvb3BlcmFuZD4KICAgICAgPC9kZWY+CiAgICAgIDxkZWYgbG9jYXRvcj0iMTM3OjEtMTM3OjkzIiBuYW1lPSJjaGVja01vZGlmaWVycyIgY29udGV4dD0iUGF0aWVudCIgYWNjZXNzTGV2ZWw9IlB1YmxpYyIgZXh0ZXJuYWw9InRydWUiIHhzaTp0eXBlPSJGdW5jdGlvbkRlZiI+CiAgICAgICAgIDxvcGVyYW5kIG5hbWU9InJlc291cmNlIj4KICAgICAgICAgICAgPG9wZXJhbmRUeXBlU3BlY2lmaWVyIGxvY2F0b3I9IjEzNzo0MS0xMzc6NDgiIG5hbWU9ImZoaXI6UmVzb3VyY2UiIHhzaTp0eXBlPSJOYW1lZFR5cGVTcGVjaWZpZXIiLz4KICAgICAgICAgPC9vcGVyYW5kPgogICAgICAgICA8b3BlcmFuZCBuYW1lPSJtb2RpZmllciI+CiAgICAgICAgICAgIDxvcGVyYW5kVHlwZVNwZWNpZmllciBsb2NhdG9yPSIxMzc6NjAtMTM3OjY1IiBuYW1lPSJ0OlN0cmluZyIgeHNpOnR5cGU9Ik5hbWVkVHlwZVNwZWNpZmllciIvPgogICAgICAgICA8L29wZXJhbmQ+CiAgICAgIDwvZGVmPgogICAgICA8ZGVmIGxvY2F0b3I9IjEzODoxLTEzODo3MyIgbmFtZT0iY2hlY2tNb2RpZmllcnMiIGNvbnRleHQ9IlBhdGllbnQiIGFjY2Vzc0xldmVsPSJQdWJsaWMiIGV4dGVybmFsPSJ0cnVlIiB4c2k6dHlwZT0iRnVuY3Rpb25EZWYiPgogICAgICAgICA8b3BlcmFuZCBuYW1lPSJlbGVtZW50Ij4KICAgICAgICAgICAgPG9wZXJhbmRUeXBlU3BlY2lmaWVyIGxvY2F0b3I9IjEzODo0MC0xMzg6NDYiIG5hbWU9ImZoaXI6RWxlbWVudCIgeHNpOnR5cGU9Ik5hbWVkVHlwZVNwZWNpZmllciIvPgogICAgICAgICA8L29wZXJhbmQ+CiAgICAgIDwvZGVmPgogICAgICA8ZGVmIGxvY2F0b3I9IjEzOToxLTEzOTo5MCIgbmFtZT0iY2hlY2tNb2RpZmllcnMiIGNvbnRleHQ9IlBhdGllbnQiIGFjY2Vzc0xldmVsPSJQdWJsaWMiIGV4dGVybmFsPSJ0cnVlIiB4c2k6dHlwZT0iRnVuY3Rpb25EZWYiPgogICAgICAgICA8b3BlcmFuZCBuYW1lPSJlbGVtZW50Ij4KICAgICAgICAgICAgPG9wZXJhbmRUeXBlU3BlY2lmaWVyIGxvY2F0b3I9IjEzOTo0MC0xMzk6NDYiIG5hbWU9ImZoaXI6RWxlbWVudCIgeHNpOnR5cGU9Ik5hbWVkVHlwZVNwZWNpZmllciIvPgogICAgICAgICA8L29wZXJhbmQ+CiAgICAgICAgIDxvcGVyYW5kIG5hbWU9Im1vZGlmaWVyIj4KICAgICAgICAgICAgPG9wZXJhbmRUeXBlU3BlY2lmaWVyIGxvY2F0b3I9IjEzOTo1OC0xMzk6NjMiIG5hbWU9InQ6U3RyaW5nIiB4c2k6dHlwZT0iTmFtZWRUeXBlU3BlY2lmaWVyIi8+CiAgICAgICAgIDwvb3BlcmFuZD4KICAgICAgPC9kZWY+CiAgICAgIDxkZWYgbG9jYXRvcj0iMTQwOjEtMTQwOjg5IiBuYW1lPSJjb25mb3Jtc1RvIiBjb250ZXh0PSJQYXRpZW50IiBhY2Nlc3NMZXZlbD0iUHVibGljIiBleHRlcm5hbD0idHJ1ZSIgeHNpOnR5cGU9IkZ1bmN0aW9uRGVmIj4KICAgICAgICAgPG9wZXJhbmQgbmFtZT0icmVzb3VyY2UiPgogICAgICAgICAgICA8b3BlcmFuZFR5cGVTcGVjaWZpZXIgbG9jYXRvcj0iMTQwOjM3LTE0MDo0NCIgbmFtZT0iZmhpcjpSZXNvdXJjZSIgeHNpOnR5cGU9Ik5hbWVkVHlwZVNwZWNpZmllciIvPgogICAgICAgICA8L29wZXJhbmQ+CiAgICAgICAgIDxvcGVyYW5kIG5hbWU9InN0cnVjdHVyZSI+CiAgICAgICAgICAgIDxvcGVyYW5kVHlwZVNwZWNpZmllciBsb2NhdG9yPSIxNDA6NTctMTQwOjYyIiBuYW1lPSJ0OlN0cmluZyIgeHNpOnR5cGU9Ik5hbWVkVHlwZVNwZWNpZmllciIvPgogICAgICAgICA8L29wZXJhbmQ+CiAgICAgIDwvZGVmPgogICAgICA8ZGVmIGxvY2F0b3I9IjE0MToxLTE0MTo3OCIgbmFtZT0ibWVtYmVyT2YiIGNvbnRleHQ9IlBhdGllbnQiIGFjY2Vzc0xldmVsPSJQdWJsaWMiIGV4dGVybmFsPSJ0cnVlIiB4c2k6dHlwZT0iRnVuY3Rpb25EZWYiPgogICAgICAgICA8b3BlcmFuZCBuYW1lPSJjb2RlIj4KICAgICAgICAgICAgPG9wZXJhbmRUeXBlU3BlY2lmaWVyIGxvY2F0b3I9IjE0MTozMS0xNDE6MzQiIG5hbWU9ImZoaXI6Y29kZSIgeHNpOnR5cGU9Ik5hbWVkVHlwZVNwZWNpZmllciIvPgogICAgICAgICA8L29wZXJhbmQ+CiAgICAgICAgIDxvcGVyYW5kIG5hbWU9InZhbHVlU2V0Ij4KICAgICAgICAgICAgPG9wZXJhbmRUeXBlU3BlY2lmaWVyIGxvY2F0b3I9IjE0MTo0Ni0xNDE6NTEiIG5hbWU9InQ6U3RyaW5nIiB4c2k6dHlwZT0iTmFtZWRUeXBlU3BlY2lmaWVyIi8+CiAgICAgICAgIDwvb3BlcmFuZD4KICAgICAgPC9kZWY+CiAgICAgIDxkZWYgbG9jYXRvcj0iMTQyOjEtMTQyOjgyIiBuYW1lPSJtZW1iZXJPZiIgY29udGV4dD0iUGF0aWVudCIgYWNjZXNzTGV2ZWw9IlB1YmxpYyIgZXh0ZXJuYWw9InRydWUiIHhzaTp0eXBlPSJGdW5jdGlvbkRlZiI+CiAgICAgICAgIDxvcGVyYW5kIG5hbWU9ImNvZGluZyI+CiAgICAgICAgICAgIDxvcGVyYW5kVHlwZVNwZWNpZmllciBsb2NhdG9yPSIxNDI6MzMtMTQyOjM4IiBuYW1lPSJmaGlyOkNvZGluZyIgeHNpOnR5cGU9Ik5hbWVkVHlwZVNwZWNpZmllciIvPgogICAgICAgICA8L29wZXJhbmQ+CiAgICAgICAgIDxvcGVyYW5kIG5hbWU9InZhbHVlU2V0Ij4KICAgICAgICAgICAgPG9wZXJhbmRUeXBlU3BlY2lmaWVyIGxvY2F0b3I9IjE0Mjo1MC0xNDI6NTUiIG5hbWU9InQ6U3RyaW5nIiB4c2k6dHlwZT0iTmFtZWRUeXBlU3BlY2lmaWVyIi8+CiAgICAgICAgIDwvb3BlcmFuZD4KICAgICAgPC9kZWY+CiAgICAgIDxkZWYgbG9jYXRvcj0iMTQzOjEtMTQzOjkyIiBuYW1lPSJtZW1iZXJPZiIgY29udGV4dD0iUGF0aWVudCIgYWNjZXNzTGV2ZWw9IlB1YmxpYyIgZXh0ZXJuYWw9InRydWUiIHhzaTp0eXBlPSJGdW5jdGlvbkRlZiI+CiAgICAgICAgIDxvcGVyYW5kIG5hbWU9ImNvbmNlcHQiPgogICAgICAgICAgICA8b3BlcmFuZFR5cGVTcGVjaWZpZXIgbG9jYXRvcj0iMTQzOjM0LTE0Mzo0OCIgbmFtZT0iZmhpcjpDb2RlYWJsZUNvbmNlcHQiIHhzaTp0eXBlPSJOYW1lZFR5cGVTcGVjaWZpZXIiLz4KICAgICAgICAgPC9vcGVyYW5kPgogICAgICAgICA8b3BlcmFuZCBuYW1lPSJ2YWx1ZVNldCI+CiAgICAgICAgICAgIDxvcGVyYW5kVHlwZVNwZWNpZmllciBsb2NhdG9yPSIxNDM6NjAtMTQzOjY1IiBuYW1lPSJ0OlN0cmluZyIgeHNpOnR5cGU9Ik5hbWVkVHlwZVNwZWNpZmllciIvPgogICAgICAgICA8L29wZXJhbmQ+CiAgICAgIDwvZGVmPgogICAgICA8ZGVmIGxvY2F0b3I9IjE0NDoxLTE0NDo4OCIgbmFtZT0ic3Vic3VtZXMiIGNvbnRleHQ9IlBhdGllbnQiIGFjY2Vzc0xldmVsPSJQdWJsaWMiIGV4dGVybmFsPSJ0cnVlIiB4c2k6dHlwZT0iRnVuY3Rpb25EZWYiPgogICAgICAgICA8b3BlcmFuZCBuYW1lPSJjb2RpbmciPgogICAgICAgICAgICA8b3BlcmFuZFR5cGVTcGVjaWZpZXIgbG9jYXRvcj0iMTQ0OjMzLTE0NDozOCIgbmFtZT0iZmhpcjpDb2RpbmciIHhzaTp0eXBlPSJOYW1lZFR5cGVTcGVjaWZpZXIiLz4KICAgICAgICAgPC9vcGVyYW5kPgogICAgICAgICA8b3BlcmFuZCBuYW1lPSJzdWJzdW1lZENvZGluZyI+CiAgICAgICAgICAgIDxvcGVyYW5kVHlwZVNwZWNpZmllciBsb2NhdG9yPSIxNDQ6NTYtMTQ0OjYxIiBuYW1lPSJmaGlyOkNvZGluZyIgeHNpOnR5cGU9Ik5hbWVkVHlwZVNwZWNpZmllciIvPgogICAgICAgICA8L29wZXJhbmQ+CiAgICAgIDwvZGVmPgogICAgICA8ZGVmIGxvY2F0b3I9IjE0NToxLTE0NToxMDgiIG5hbWU9InN1YnN1bWVzIiBjb250ZXh0PSJQYXRpZW50IiBhY2Nlc3NMZXZlbD0iUHVibGljIiBleHRlcm5hbD0idHJ1ZSIgeHNpOnR5cGU9IkZ1bmN0aW9uRGVmIj4KICAgICAgICAgPG9wZXJhbmQgbmFtZT0iY29uY2VwdCI+CiAgICAgICAgICAgIDxvcGVyYW5kVHlwZVNwZWNpZmllciBsb2NhdG9yPSIxNDU6MzQtMTQ1OjQ4IiBuYW1lPSJmaGlyOkNvZGVhYmxlQ29uY2VwdCIgeHNpOnR5cGU9Ik5hbWVkVHlwZVNwZWNpZmllciIvPgogICAgICAgICA8L29wZXJhbmQ+CiAgICAgICAgIDxvcGVyYW5kIG5hbWU9InN1YnN1bWVkQ29uY2VwdCI+CiAgICAgICAgICAgIDxvcGVyYW5kVHlwZVNwZWNpZmllciBsb2NhdG9yPSIxNDU6NjctMTQ1OjgxIiBuYW1lPSJmaGlyOkNvZGVhYmxlQ29uY2VwdCIgeHNpOnR5cGU9Ik5hbWVkVHlwZVNwZWNpZmllciIvPgogICAgICAgICA8L29wZXJhbmQ+CiAgICAgIDwvZGVmPgogICAgICA8ZGVmIGxvY2F0b3I9IjE0NjoxLTE0Njo5MSIgbmFtZT0ic3Vic3VtZWRCeSIgY29udGV4dD0iUGF0aWVudCIgYWNjZXNzTGV2ZWw9IlB1YmxpYyIgZXh0ZXJuYWw9InRydWUiIHhzaTp0eXBlPSJGdW5jdGlvbkRlZiI+CiAgICAgICAgIDxvcGVyYW5kIG5hbWU9ImNvZGluZyI+CiAgICAgICAgICAgIDxvcGVyYW5kVHlwZVNwZWNpZmllciBsb2NhdG9yPSIxNDY6MzUtMTQ2OjQwIiBuYW1lPSJmaGlyOkNvZGluZyIgeHNpOnR5cGU9Ik5hbWVkVHlwZVNwZWNpZmllciIvPgogICAgICAgICA8L29wZXJhbmQ+CiAgICAgICAgIDxvcGVyYW5kIG5hbWU9InN1YnN1bWluZ0NvZGluZyI+CiAgICAgICAgICAgIDxvcGVyYW5kVHlwZVNwZWNpZmllciBsb2NhdG9yPSIxNDY6NTktMTQ2OjY0IiBuYW1lPSJmaGlyOkNvZGluZyIgeHNpOnR5cGU9Ik5hbWVkVHlwZVNwZWNpZmllciIvPgogICAgICAgICA8L29wZXJhbmQ+CiAgICAgIDwvZGVmPgogICAgICA8ZGVmIGxvY2F0b3I9IjE0NzoxLTE0NzoxMTEiIG5hbWU9InN1YnN1bWVkQnkiIGNvbnRleHQ9IlBhdGllbnQiIGFjY2Vzc0xldmVsPSJQdWJsaWMiIGV4dGVybmFsPSJ0cnVlIiB4c2k6dHlwZT0iRnVuY3Rpb25EZWYiPgogICAgICAgICA8b3BlcmFuZCBuYW1lPSJjb25jZXB0Ij4KICAgICAgICAgICAgPG9wZXJhbmRUeXBlU3BlY2lmaWVyIGxvY2F0b3I9IjE0NzozNi0xNDc6NTAiIG5hbWU9ImZoaXI6Q29kZWFibGVDb25jZXB0IiB4c2k6dHlwZT0iTmFtZWRUeXBlU3BlY2lmaWVyIi8+CiAgICAgICAgIDwvb3BlcmFuZD4KICAgICAgICAgPG9wZXJhbmQgbmFtZT0ic3Vic3VtaW5nQ29uY2VwdCI+CiAgICAgICAgICAgIDxvcGVyYW5kVHlwZVNwZWNpZmllciBsb2NhdG9yPSIxNDc6NzAtMTQ3Ojg0IiBuYW1lPSJmaGlyOkNvZGVhYmxlQ29uY2VwdCIgeHNpOnR5cGU9Ik5hbWVkVHlwZVNwZWNpZmllciIvPgogICAgICAgICA8L29wZXJhbmQ+CiAgICAgIDwvZGVmPgogICAgICA8ZGVmIGxvY2F0b3I9IjE0ODoxLTE0ODo2OSIgbmFtZT0iaHRtbENoZWNrcyIgY29udGV4dD0iUGF0aWVudCIgYWNjZXNzTGV2ZWw9IlB1YmxpYyIgZXh0ZXJuYWw9InRydWUiIHhzaTp0eXBlPSJGdW5jdGlvbkRlZiI+CiAgICAgICAgIDxvcGVyYW5kIG5hbWU9ImVsZW1lbnQiPgogICAgICAgICAgICA8b3BlcmFuZFR5cGVTcGVjaWZpZXIgbG9jYXRvcj0iMTQ4OjM2LTE0ODo0MiIgbmFtZT0iZmhpcjpFbGVtZW50IiB4c2k6dHlwZT0iTmFtZWRUeXBlU3BlY2lmaWVyIi8+CiAgICAgICAgIDwvb3BlcmFuZD4KICAgICAgPC9kZWY+CiAgICAgIDxkZWYgbG9jYXRvcj0iMTUwOjEtMTUwOjU4IiBuYW1lPSJUb1N0cmluZyIgY29udGV4dD0iUGF0aWVudCIgYWNjZXNzTGV2ZWw9IlB1YmxpYyIgeHNpOnR5cGU9IkZ1bmN0aW9uRGVmIj4KICAgICAgICAgPGV4cHJlc3Npb24gbG9jYXRvcj0iMTUwOjQ4LTE1MDo1OCIgcGF0aD0idmFsdWUiIHhzaTp0eXBlPSJQcm9wZXJ0eSI+CiAgICAgICAgICAgIDxzb3VyY2UgbG9jYXRvcj0iMTUwOjQ4LTE1MDo1MiIgbmFtZT0idmFsdWUiIHhzaTp0eXBlPSJPcGVyYW5kUmVmIi8+CiAgICAgICAgIDwvZXhwcmVzc2lvbj4KICAgICAgICAgPG9wZXJhbmQgbmFtZT0idmFsdWUiPgogICAgICAgICAgICA8b3BlcmFuZFR5cGVTcGVjaWZpZXIgbG9jYXRvcj0iMTUwOjMyLTE1MDo0NCIgbmFtZT0iZmhpcjpBY2NvdW50U3RhdHVzIiB4c2k6dHlwZT0iTmFtZWRUeXBlU3BlY2lmaWVyIi8+CiAgICAgICAgIDwvb3BlcmFuZD4KICAgICAgPC9kZWY+CiAgICAgIDxkZWYgbG9jYXRvcj0iMTUxOjEtMTUxOjcwIiBuYW1lPSJUb1N0cmluZyIgY29udGV4dD0iUGF0aWVudCIgYWNjZXNzTGV2ZWw9IlB1YmxpYyIgeHNpOnR5cGU9IkZ1bmN0aW9uRGVmIj4KICAgICAgICAgPGV4cHJlc3Npb24gbG9jYXRvcj0iMTUxOjYwLTE1MTo3MCIgcGF0aD0idmFsdWUiIHhzaTp0eXBlPSJQcm9wZXJ0eSI+CiAgICAgICAgICAgIDxzb3VyY2UgbG9jYXRvcj0iMTUxOjYwLTE1MTo2NCIgbmFtZT0idmFsdWUiIHhzaTp0eXBlPSJPcGVyYW5kUmVmIi8+CiAgICAgICAgIDwvZXhwcmVzc2lvbj4KICAgICAgICAgPG9wZXJhbmQgbmFtZT0idmFsdWUiPgogICAgICAgICAgICA8b3BlcmFuZFR5cGVTcGVjaWZpZXIgbG9jYXRvcj0iMTUxOjMyLTE1MTo1NiIgbmFtZT0iZmhpcjpBY3Rpb25DYXJkaW5hbGl0eUJlaGF2aW9yIiB4c2k6dHlwZT0iTmFtZWRUeXBlU3BlY2lmaWVyIi8+CiAgICAgICAgIDwvb3BlcmFuZD4KICAgICAgPC9kZWY+CiAgICAgIDxkZWYgbG9jYXRvcj0iMTUyOjEtMTUyOjY0IiBuYW1lPSJUb1N0cmluZyIgY29udGV4dD0iUGF0aWVudCIgYWNjZXNzTGV2ZWw9IlB1YmxpYyIgeHNpOnR5cGU9IkZ1bmN0aW9uRGVmIj4KICAgICAgICAgPGV4cHJlc3Npb24gbG9jYXRvcj0iMTUyOjU0LTE1Mjo2NCIgcGF0aD0idmFsdWUiIHhzaTp0eXBlPSJQcm9wZXJ0eSI+CiAgICAgICAgICAgIDxzb3VyY2UgbG9jYXRvcj0iMTUyOjU0LTE1Mjo1OCIgbmFtZT0idmFsdWUiIHhzaTp0eXBlPSJPcGVyYW5kUmVmIi8+CiAgICAgICAgIDwvZXhwcmVzc2lvbj4KICAgICAgICAgPG9wZXJhbmQgbmFtZT0idmFsdWUiPgogICAgICAgICAgICA8b3BlcmFuZFR5cGVTcGVjaWZpZXIgbG9jYXRvcj0iMTUyOjMyLTE1Mjo1MCIgbmFtZT0iZmhpcjpBY3Rpb25Db25kaXRpb25LaW5kIiB4c2k6dHlwZT0iTmFtZWRUeXBlU3BlY2lmaWVyIi8+CiAgICAgICAgIDwvb3BlcmFuZD4KICAgICAgPC9kZWY+CiAgICAgIDxkZWYgbG9jYXRvcj0iMTUzOjEtMTUzOjY3IiBuYW1lPSJUb1N0cmluZyIgY29udGV4dD0iUGF0aWVudCIgYWNjZXNzTGV2ZWw9IlB1YmxpYyIgeHNpOnR5cGU9IkZ1bmN0aW9uRGVmIj4KICAgICAgICAgPGV4cHJlc3Npb24gbG9jYXRvcj0iMTUzOjU3LTE1Mzo2NyIgcGF0aD0idmFsdWUiIHhzaTp0eXBlPSJQcm9wZXJ0eSI+CiAgICAgICAgICAgIDxzb3VyY2UgbG9jYXRvcj0iMTUzOjU3LTE1Mzo2MSIgbmFtZT0idmFsdWUiIHhzaTp0eXBlPSJPcGVyYW5kUmVmIi8+CiAgICAgICAgIDwvZXhwcmVzc2lvbj4KICAgICAgICAgPG9wZXJhbmQgbmFtZT0idmFsdWUiPgogICAgICAgICAgICA8b3BlcmFuZFR5cGVTcGVjaWZpZXIgbG9jYXRvcj0iMTUzOjMyLTE1Mzo1MyIgbmFtZT0iZmhpcjpBY3Rpb25Hcm91cGluZ0JlaGF2aW9yIiB4c2k6dHlwZT0iTmFtZWRUeXBlU3BlY2lmaWVyIi8+CiAgICAgICAgIDwvb3BlcmFuZD4KICAgICAgPC9kZWY+CiAgICAgIDxkZWYgbG9jYXRvcj0iMTU0OjEtMTU0OjY2IiBuYW1lPSJUb1N0cmluZyIgY29udGV4dD0iUGF0aWVudCIgYWNjZXNzTGV2ZWw9IlB1YmxpYyIgeHNpOnR5cGU9IkZ1bmN0aW9uRGVmIj4KICAgICAgICAgPGV4cHJlc3Npb24gbG9jYXRvcj0iMTU0OjU2LTE1NDo2NiIgcGF0aD0idmFsdWUiIHhzaTp0eXBlPSJQcm9wZXJ0eSI+CiAgICAgICAgICAgIDxzb3VyY2UgbG9jYXRvcj0iMTU0OjU2LTE1NDo2MCIgbmFtZT0idmFsdWUiIHhzaTp0eXBlPSJPcGVyYW5kUmVmIi8+CiAgICAgICAgIDwvZXhwcmVzc2lvbj4KICAgICAgICAgPG9wZXJhbmQgbmFtZT0idmFsdWUiPgogICAgICAgICAgICA8b3BlcmFuZFR5cGVTcGVjaWZpZXIgbG9jYXRvcj0iMTU0OjMyLTE1NDo1MiIgbmFtZT0iZmhpcjpBY3Rpb25QYXJ0aWNpcGFudFR5cGUiIHhzaTp0eXBlPSJOYW1lZFR5cGVTcGVjaWZpZXIiLz4KICAgICAgICAgPC9vcGVyYW5kPgogICAgICA8L2RlZj4KICAgICAgPGRlZiBsb2NhdG9yPSIxNTU6MS0xNTU6NjciIG5hbWU9IlRvU3RyaW5nIiBjb250ZXh0PSJQYXRpZW50IiBhY2Nlc3NMZXZlbD0iUHVibGljIiB4c2k6dHlwZT0iRnVuY3Rpb25EZWYiPgogICAgICAgICA8ZXhwcmVzc2lvbiBsb2NhdG9yPSIxNTU6NTctMTU1OjY3IiBwYXRoPSJ2YWx1ZSIgeHNpOnR5cGU9IlByb3BlcnR5Ij4KICAgICAgICAgICAgPHNvdXJjZSBsb2NhdG9yPSIxNTU6NTctMTU1OjYxIiBuYW1lPSJ2YWx1ZSIgeHNpOnR5cGU9Ik9wZXJhbmRSZWYiLz4KICAgICAgICAgPC9leHByZXNzaW9uPgogICAgICAgICA8b3BlcmFuZCBuYW1lPSJ2YWx1ZSI+CiAgICAgICAgICAgIDxvcGVyYW5kVHlwZVNwZWNpZmllciBsb2NhdG9yPSIxNTU6MzItMTU1OjUzIiBuYW1lPSJmaGlyOkFjdGlvblByZWNoZWNrQmVoYXZpb3IiIHhzaTp0eXBlPSJOYW1lZFR5cGVTcGVjaWZpZXIiLz4KICAgICAgICAgPC9vcGVyYW5kPgogICAgICA8L2RlZj4KICAgICAgPGRlZiBsb2NhdG9yPSIxNTY6MS0xNTY6NjciIG5hbWU9IlRvU3RyaW5nIiBjb250ZXh0PSJQYXRpZW50IiBhY2Nlc3NMZXZlbD0iUHVibGljIiB4c2k6dHlwZT0iRnVuY3Rpb25EZWYiPgogICAgICAgICA8ZXhwcmVzc2lvbiBsb2NhdG9yPSIxNTY6NTctMTU2OjY3IiBwYXRoPSJ2YWx1ZSIgeHNpOnR5cGU9IlByb3BlcnR5Ij4KICAgICAgICAgICAgPHNvdXJjZSBsb2NhdG9yPSIxNTY6NTctMTU2OjYxIiBuYW1lPSJ2YWx1ZSIgeHNpOnR5cGU9Ik9wZXJhbmRSZWYiLz4KICAgICAgICAgPC9leHByZXNzaW9uPgogICAgICAgICA8b3BlcmFuZCBuYW1lPSJ2YWx1ZSI+CiAgICAgICAgICAgIDxvcGVyYW5kVHlwZVNwZWNpZmllciBsb2NhdG9yPSIxNTY6MzItMTU2OjUzIiBuYW1lPSJmaGlyOkFjdGlvblJlbGF0aW9uc2hpcFR5cGUiIHhzaTp0eXBlPSJOYW1lZFR5cGVTcGVjaWZpZXIiLz4KICAgICAgICAgPC9vcGVyYW5kPgogICAgICA8L2RlZj4KICAgICAgPGRlZiBsb2NhdG9yPSIxNTc6MS0xNTc6NjciIG5hbWU9IlRvU3RyaW5nIiBjb250ZXh0PSJQYXRpZW50IiBhY2Nlc3NMZXZlbD0iUHVibGljIiB4c2k6dHlwZT0iRnVuY3Rpb25EZWYiPgogICAgICAgICA8ZXhwcmVzc2lvbiBsb2NhdG9yPSIxNTc6NTctMTU3OjY3IiBwYXRoPSJ2YWx1ZSIgeHNpOnR5cGU9IlByb3BlcnR5Ij4KICAgICAgICAgICAgPHNvdXJjZSBsb2NhdG9yPSIxNTc6NTctMTU3OjYxIiBuYW1lPSJ2YWx1ZSIgeHNpOnR5cGU9Ik9wZXJhbmRSZWYiLz4KICAgICAgICAgPC9leHByZXNzaW9uPgogICAgICAgICA8b3BlcmFuZCBuYW1lPSJ2YWx1ZSI+CiAgICAgICAgICAgIDxvcGVyYW5kVHlwZVNwZWNpZmllciBsb2NhdG9yPSIxNTc6MzItMTU3OjUzIiBuYW1lPSJmaGlyOkFjdGlvblJlcXVpcmVkQmVoYXZpb3IiIHhzaTp0eXBlPSJOYW1lZFR5cGVTcGVjaWZpZXIiLz4KICAgICAgICAgPC9vcGVyYW5kPgogICAgICA8L2RlZj4KICAgICAgPGRlZiBsb2NhdG9yPSIxNTg6MS0xNTg6NjgiIG5hbWU9IlRvU3RyaW5nIiBjb250ZXh0PSJQYXRpZW50IiBhY2Nlc3NMZXZlbD0iUHVibGljIiB4c2k6dHlwZT0iRnVuY3Rpb25EZWYiPgogICAgICAgICA8ZXhwcmVzc2lvbiBsb2NhdG9yPSIxNTg6NTgtMTU4OjY4IiBwYXRoPSJ2YWx1ZSIgeHNpOnR5cGU9IlByb3BlcnR5Ij4KICAgICAgICAgICAgPHNvdXJjZSBsb2NhdG9yPSIxNTg6NTgtMTU4OjYyIiBuYW1lPSJ2YWx1ZSIgeHNpOnR5cGU9Ik9wZXJhbmRSZWYiLz4KICAgICAgICAgPC9leHByZXNzaW9uPgogICAgICAgICA8b3BlcmFuZCBuYW1lPSJ2YWx1ZSI+CiAgICAgICAgICAgIDxvcGVyYW5kVHlwZVNwZWNpZmllciBsb2NhdG9yPSIxNTg6MzItMTU4OjU0IiBuYW1lPSJmaGlyOkFjdGlvblNlbGVjdGlvbkJlaGF2aW9yIiB4c2k6dHlwZT0iTmFtZWRUeXBlU3BlY2lmaWVyIi8+CiAgICAgICAgIDwvb3BlcmFuZD4KICAgICAgPC9kZWY+CiAgICAgIDxkZWYgbG9jYXRvcj0iMTU5OjEtMTU5OjY3IiBuYW1lPSJUb1N0cmluZyIgY29udGV4dD0iUGF0aWVudCIgYWNjZXNzTGV2ZWw9IlB1YmxpYyIgeHNpOnR5cGU9IkZ1bmN0aW9uRGVmIj4KICAgICAgICAgPGV4cHJlc3Npb24gbG9jYXRvcj0iMTU5OjU3LTE1OTo2NyIgcGF0aD0idmFsdWUiIHhzaTp0eXBlPSJQcm9wZXJ0eSI+CiAgICAgICAgICAgIDxzb3VyY2UgbG9jYXRvcj0iMTU5OjU3LTE1OTo2MSIgbmFtZT0idmFsdWUiIHhzaTp0eXBlPSJPcGVyYW5kUmVmIi8+CiAgICAgICAgIDwvZXhwcmVzc2lvbj4KICAgICAgICAgPG9wZXJhbmQgbmFtZT0idmFsdWUiPgogICAgICAgICAgICA8b3BlcmFuZFR5cGVTcGVjaWZpZXIgbG9jYXRvcj0iMTU5OjMyLTE1OTo1MyIgbmFtZT0iZmhpcjpBY3Rpdml0eURlZmluaXRpb25LaW5kIiB4c2k6dHlwZT0iTmFtZWRUeXBlU3BlY2lmaWVyIi8+CiAgICAgICAgIDwvb3BlcmFuZD4KICAgICAgPC9kZWY+CiAgICAgIDxkZWYgbG9jYXRvcj0iMTYwOjEtMTYwOjY4IiBuYW1lPSJUb1N0cmluZyIgY29udGV4dD0iUGF0aWVudCIgYWNjZXNzTGV2ZWw9IlB1YmxpYyIgeHNpOnR5cGU9IkZ1bmN0aW9uRGVmIj4KICAgICAgICAgPGV4cHJlc3Npb24gbG9jYXRvcj0iMTYwOjU4LTE2MDo2OCIgcGF0aD0idmFsdWUiIHhzaTp0eXBlPSJQcm9wZXJ0eSI+CiAgICAgICAgICAgIDxzb3VyY2UgbG9jYXRvcj0iMTYwOjU4LTE2MDo2MiIgbmFtZT0idmFsdWUiIHhzaTp0eXBlPSJPcGVyYW5kUmVmIi8+CiAgICAgICAgIDwvZXhwcmVzc2lvbj4KICAgICAgICAgPG9wZXJhbmQgbmFtZT0idmFsdWUiPgogICAgICAgICAgICA8b3BlcmFuZFR5cGVTcGVjaWZpZXIgbG9jYXRvcj0iMTYwOjMyLTE2MDo1NCIgbmFtZT0iZmhpcjpBY3Rpdml0eVBhcnRpY2lwYW50VHlwZSIgeHNpOnR5cGU9Ik5hbWVkVHlwZVNwZWNpZmllciIvPgogICAgICAgICA8L29wZXJhbmQ+CiAgICAgIDwvZGVmPgogICAgICA8ZGVmIGxvY2F0b3I9IjE2MToxLTE2MTo1NiIgbmFtZT0iVG9TdHJpbmciIGNvbnRleHQ9IlBhdGllbnQiIGFjY2Vzc0xldmVsPSJQdWJsaWMiIHhzaTp0eXBlPSJGdW5jdGlvbkRlZiI+CiAgICAgICAgIDxleHByZXNzaW9uIGxvY2F0b3I9IjE2MTo0Ni0xNjE6NTYiIHBhdGg9InZhbHVlIiB4c2k6dHlwZT0iUHJvcGVydHkiPgogICAgICAgICAgICA8c291cmNlIGxvY2F0b3I9IjE2MTo0Ni0xNjE6NTAiIG5hbWU9InZhbHVlIiB4c2k6dHlwZT0iT3BlcmFuZFJlZiIvPgogICAgICAgICA8L2V4cHJlc3Npb24+CiAgICAgICAgIDxvcGVyYW5kIG5hbWU9InZhbHVlIj4KICAgICAgICAgICAgPG9wZXJhbmRUeXBlU3BlY2lmaWVyIGxvY2F0b3I9IjE2MTozMi0xNjE6NDIiIG5hbWU9ImZoaXI6QWRkcmVzc1R5cGUiIHhzaTp0eXBlPSJOYW1lZFR5cGVTcGVjaWZpZXIiLz4KICAgICAgICAgPC9vcGVyYW5kPgogICAgICA8L2RlZj4KICAgICAgPGRlZiBsb2NhdG9yPSIxNjI6MS0xNjI6NTUiIG5hbWU9IlRvU3RyaW5nIiBjb250ZXh0PSJQYXRpZW50IiBhY2Nlc3NMZXZlbD0iUHVibGljIiB4c2k6dHlwZT0iRnVuY3Rpb25EZWYiPgogICAgICAgICA8ZXhwcmVzc2lvbiBsb2NhdG9yPSIxNjI6NDUtMTYyOjU1IiBwYXRoPSJ2YWx1ZSIgeHNpOnR5cGU9IlByb3BlcnR5Ij4KICAgICAgICAgICAgPHNvdXJjZSBsb2NhdG9yPSIxNjI6NDUtMTYyOjQ5IiBuYW1lPSJ2YWx1ZSIgeHNpOnR5cGU9Ik9wZXJhbmRSZWYiLz4KICAgICAgICAgPC9leHByZXNzaW9uPgogICAgICAgICA8b3BlcmFuZCBuYW1lPSJ2YWx1ZSI+CiAgICAgICAgICAgIDxvcGVyYW5kVHlwZVNwZWNpZmllciBsb2NhdG9yPSIxNjI6MzItMTYyOjQxIiBuYW1lPSJmaGlyOkFkZHJlc3NVc2UiIHhzaTp0eXBlPSJOYW1lZFR5cGVTcGVjaWZpZXIiLz4KICAgICAgICAgPC9vcGVyYW5kPgogICAgICA8L2RlZj4KICAgICAgPGRlZiBsb2NhdG9yPSIxNjM6MS0xNjM6NjUiIG5hbWU9IlRvU3RyaW5nIiBjb250ZXh0PSJQYXRpZW50IiBhY2Nlc3NMZXZlbD0iUHVibGljIiB4c2k6dHlwZT0iRnVuY3Rpb25EZWYiPgogICAgICAgICA8ZXhwcmVzc2lvbiBsb2NhdG9yPSIxNjM6NTUtMTYzOjY1IiBwYXRoPSJ2YWx1ZSIgeHNpOnR5cGU9IlByb3BlcnR5Ij4KICAgICAgICAgICAgPHNvdXJjZSBsb2NhdG9yPSIxNjM6NTUtMTYzOjU5IiBuYW1lPSJ2YWx1ZSIgeHNpOnR5cGU9Ik9wZXJhbmRSZWYiLz4KICAgICAgICAgPC9leHByZXNzaW9uPgogICAgICAgICA8b3BlcmFuZCBuYW1lPSJ2YWx1ZSI+CiAgICAgICAgICAgIDxvcGVyYW5kVHlwZVNwZWNpZmllciBsb2NhdG9yPSIxNjM6MzItMTYzOjUxIiBuYW1lPSJmaGlyOkFkbWluaXN0cmF0aXZlR2VuZGVyIiB4c2k6dHlwZT0iTmFtZWRUeXBlU3BlY2lmaWVyIi8+CiAgICAgICAgIDwvb3BlcmFuZD4KICAgICAgPC9kZWY+CiAgICAgIDxkZWYgbG9jYXRvcj0iMTY0OjEtMTY0OjY2IiBuYW1lPSJUb1N0cmluZyIgY29udGV4dD0iUGF0aWVudCIgYWNjZXNzTGV2ZWw9IlB1YmxpYyIgeHNpOnR5cGU9IkZ1bmN0aW9uRGVmIj4KICAgICAgICAgPGV4cHJlc3Npb24gbG9jYXRvcj0iMTY0OjU2LTE2NDo2NiIgcGF0aD0idmFsdWUiIHhzaTp0eXBlPSJQcm9wZXJ0eSI+CiAgICAgICAgICAgIDxzb3VyY2UgbG9jYXRvcj0iMTY0OjU2LTE2NDo2MCIgbmFtZT0idmFsdWUiIHhzaTp0eXBlPSJPcGVyYW5kUmVmIi8+CiAgICAgICAgIDwvZXhwcmVzc2lvbj4KICAgICAgICAgPG9wZXJhbmQgbmFtZT0idmFsdWUiPgogICAgICAgICAgICA8b3BlcmFuZFR5cGVTcGVjaWZpZXIgbG9jYXRvcj0iMTY0OjMyLTE2NDo1MiIgbmFtZT0iZmhpcjpBZHZlcnNlRXZlbnRBY3R1YWxpdHkiIHhzaTp0eXBlPSJOYW1lZFR5cGVTcGVjaWZpZXIiLz4KICAgICAgICAgPC9vcGVyYW5kPgogICAgICA8L2RlZj4KICAgICAgPGRlZiBsb2NhdG9yPSIxNjU6MS0xNjU6NjAiIG5hbWU9IlRvU3RyaW5nIiBjb250ZXh0PSJQYXRpZW50IiBhY2Nlc3NMZXZlbD0iUHVibGljIiB4c2k6dHlwZT0iRnVuY3Rpb25EZWYiPgogICAgICAgICA8ZXhwcmVzc2lvbiBsb2NhdG9yPSIxNjU6NTAtMTY1OjYwIiBwYXRoPSJ2YWx1ZSIgeHNpOnR5cGU9IlByb3BlcnR5Ij4KICAgICAgICAgICAgPHNvdXJjZSBsb2NhdG9yPSIxNjU6NTAtMTY1OjU0IiBuYW1lPSJ2YWx1ZSIgeHNpOnR5cGU9Ik9wZXJhbmRSZWYiLz4KICAgICAgICAgPC9leHByZXNzaW9uPgogICAgICAgICA8b3BlcmFuZCBuYW1lPSJ2YWx1ZSI+CiAgICAgICAgICAgIDxvcGVyYW5kVHlwZVNwZWNpZmllciBsb2NhdG9yPSIxNjU6MzItMTY1OjQ2IiBuYW1lPSJmaGlyOkFnZ3JlZ2F0aW9uTW9kZSIgeHNpOnR5cGU9Ik5hbWVkVHlwZVNwZWNpZmllciIvPgogICAgICAgICA8L29wZXJhbmQ+CiAgICAgIDwvZGVmPgogICAgICA8ZGVmIGxvY2F0b3I9IjE2NjoxLTE2Njo3MSIgbmFtZT0iVG9TdHJpbmciIGNvbnRleHQ9IlBhdGllbnQiIGFjY2Vzc0xldmVsPSJQdWJsaWMiIHhzaTp0eXBlPSJGdW5jdGlvbkRlZiI+CiAgICAgICAgIDxleHByZXNzaW9uIGxvY2F0b3I9IjE2Njo2MS0xNjY6NzEiIHBhdGg9InZhbHVlIiB4c2k6dHlwZT0iUHJvcGVydHkiPgogICAgICAgICAgICA8c291cmNlIGxvY2F0b3I9IjE2Njo2MS0xNjY6NjUiIG5hbWU9InZhbHVlIiB4c2k6dHlwZT0iT3BlcmFuZFJlZiIvPgogICAgICAgICA8L2V4cHJlc3Npb24+CiAgICAgICAgIDxvcGVyYW5kIG5hbWU9InZhbHVlIj4KICAgICAgICAgICAgPG9wZXJhbmRUeXBlU3BlY2lmaWVyIGxvY2F0b3I9IjE2NjozMi0xNjY6NTciIG5hbWU9ImZoaXI6QWxsZXJneUludG9sZXJhbmNlQ2F0ZWdvcnkiIHhzaTp0eXBlPSJOYW1lZFR5cGVTcGVjaWZpZXIiLz4KICAgICAgICAgPC9vcGVyYW5kPgogICAgICA8L2RlZj4KICAgICAgPGRlZiBsb2NhdG9yPSIxNjc6MS0xNjc6NzQiIG5hbWU9IlRvU3RyaW5nIiBjb250ZXh0PSJQYXRpZW50IiBhY2Nlc3NMZXZlbD0iUHVibGljIiB4c2k6dHlwZT0iRnVuY3Rpb25EZWYiPgogICAgICAgICA8ZXhwcmVzc2lvbiBsb2NhdG9yPSIxNjc6NjQtMTY3Ojc0IiBwYXRoPSJ2YWx1ZSIgeHNpOnR5cGU9IlByb3BlcnR5Ij4KICAgICAgICAgICAgPHNvdXJjZSBsb2NhdG9yPSIxNjc6NjQtMTY3OjY4IiBuYW1lPSJ2YWx1ZSIgeHNpOnR5cGU9Ik9wZXJhbmRSZWYiLz4KICAgICAgICAgPC9leHByZXNzaW9uPgogICAgICAgICA8b3BlcmFuZCBuYW1lPSJ2YWx1ZSI+CiAgICAgICAgICAgIDxvcGVyYW5kVHlwZVNwZWNpZmllciBsb2NhdG9yPSIxNjc6MzItMTY3OjYwIiBuYW1lPSJmaGlyOkFsbGVyZ3lJbnRvbGVyYW5jZUNyaXRpY2FsaXR5IiB4c2k6dHlwZT0iTmFtZWRUeXBlU3BlY2lmaWVyIi8+CiAgICAgICAgIDwvb3BlcmFuZD4KICAgICAgPC9kZWY+CiAgICAgIDxkZWYgbG9jYXRvcj0iMTY4OjEtMTY4OjcxIiBuYW1lPSJUb1N0cmluZyIgY29udGV4dD0iUGF0aWVudCIgYWNjZXNzTGV2ZWw9IlB1YmxpYyIgeHNpOnR5cGU9IkZ1bmN0aW9uRGVmIj4KICAgICAgICAgPGV4cHJlc3Npb24gbG9jYXRvcj0iMTY4OjYxLTE2ODo3MSIgcGF0aD0idmFsdWUiIHhzaTp0eXBlPSJQcm9wZXJ0eSI+CiAgICAgICAgICAgIDxzb3VyY2UgbG9jYXRvcj0iMTY4OjYxLTE2ODo2NSIgbmFtZT0idmFsdWUiIHhzaTp0eXBlPSJPcGVyYW5kUmVmIi8+CiAgICAgICAgIDwvZXhwcmVzc2lvbj4KICAgICAgICAgPG9wZXJhbmQgbmFtZT0idmFsdWUiPgogICAgICAgICAgICA8b3BlcmFuZFR5cGVTcGVjaWZpZXIgbG9jYXRvcj0iMTY4OjMyLTE2ODo1NyIgbmFtZT0iZmhpcjpBbGxlcmd5SW50b2xlcmFuY2VTZXZlcml0eSIgeHNpOnR5cGU9Ik5hbWVkVHlwZVNwZWNpZmllciIvPgogICAgICAgICA8L29wZXJhbmQ+CiAgICAgIDwvZGVmPgogICAgICA8ZGVmIGxvY2F0b3I9IjE2OToxLTE2OTo2NyIgbmFtZT0iVG9TdHJpbmciIGNvbnRleHQ9IlBhdGllbnQiIGFjY2Vzc0xldmVsPSJQdWJsaWMiIHhzaTp0eXBlPSJGdW5jdGlvbkRlZiI+CiAgICAgICAgIDxleHByZXNzaW9uIGxvY2F0b3I9IjE2OTo1Ny0xNjk6NjciIHBhdGg9InZhbHVlIiB4c2k6dHlwZT0iUHJvcGVydHkiPgogICAgICAgICAgICA8c291cmNlIGxvY2F0b3I9IjE2OTo1Ny0xNjk6NjEiIG5hbWU9InZhbHVlIiB4c2k6dHlwZT0iT3BlcmFuZFJlZiIvPgogICAgICAgICA8L2V4cHJlc3Npb24+CiAgICAgICAgIDxvcGVyYW5kIG5hbWU9InZhbHVlIj4KICAgICAgICAgICAgPG9wZXJhbmRUeXBlU3BlY2lmaWVyIGxvY2F0b3I9IjE2OTozMi0xNjk6NTMiIG5hbWU9ImZoaXI6QWxsZXJneUludG9sZXJhbmNlVHlwZSIgeHNpOnR5cGU9Ik5hbWVkVHlwZVNwZWNpZmllciIvPgogICAgICAgICA8L29wZXJhbmQ+CiAgICAgIDwvZGVmPgogICAgICA8ZGVmIGxvY2F0b3I9IjE3MDoxLTE3MDo2MiIgbmFtZT0iVG9TdHJpbmciIGNvbnRleHQ9IlBhdGllbnQiIGFjY2Vzc0xldmVsPSJQdWJsaWMiIHhzaTp0eXBlPSJGdW5jdGlvbkRlZiI+CiAgICAgICAgIDxleHByZXNzaW9uIGxvY2F0b3I9IjE3MDo1Mi0xNzA6NjIiIHBhdGg9InZhbHVlIiB4c2k6dHlwZT0iUHJvcGVydHkiPgogICAgICAgICAgICA8c291cmNlIGxvY2F0b3I9IjE3MDo1Mi0xNzA6NTYiIG5hbWU9InZhbHVlIiB4c2k6dHlwZT0iT3BlcmFuZFJlZiIvPgogICAgICAgICA8L2V4cHJlc3Npb24+CiAgICAgICAgIDxvcGVyYW5kIG5hbWU9InZhbHVlIj4KICAgICAgICAgICAgPG9wZXJhbmRUeXBlU3BlY2lmaWVyIGxvY2F0b3I9IjE3MDozMi0xNzA6NDgiIG5hbWU9ImZoaXI6QXBwb2ludG1lbnRTdGF0dXMiIHhzaTp0eXBlPSJOYW1lZFR5cGVTcGVjaWZpZXIiLz4KICAgICAgICAgPC9vcGVyYW5kPgogICAgICA8L2RlZj4KICAgICAgPGRlZiBsb2NhdG9yPSIxNzE6MS0xNzE6NjciIG5hbWU9IlRvU3RyaW5nIiBjb250ZXh0PSJQYXRpZW50IiBhY2Nlc3NMZXZlbD0iUHVibGljIiB4c2k6dHlwZT0iRnVuY3Rpb25EZWYiPgogICAgICAgICA8ZXhwcmVzc2lvbiBsb2NhdG9yPSIxNzE6NTctMTcxOjY3IiBwYXRoPSJ2YWx1ZSIgeHNpOnR5cGU9IlByb3BlcnR5Ij4KICAgICAgICAgICAgPHNvdXJjZSBsb2NhdG9yPSIxNzE6NTctMTcxOjYxIiBuYW1lPSJ2YWx1ZSIgeHNpOnR5cGU9Ik9wZXJhbmRSZWYiLz4KICAgICAgICAgPC9leHByZXNzaW9uPgogICAgICAgICA8b3BlcmFuZCBuYW1lPSJ2YWx1ZSI+CiAgICAgICAgICAgIDxvcGVyYW5kVHlwZVNwZWNpZmllciBsb2NhdG9yPSIxNzE6MzItMTcxOjUzIiBuYW1lPSJmaGlyOkFzc2VydGlvbkRpcmVjdGlvblR5cGUiIHhzaTp0eXBlPSJOYW1lZFR5cGVTcGVjaWZpZXIiLz4KICAgICAgICAgPC9vcGVyYW5kPgogICAgICA8L2RlZj4KICAgICAgPGRlZiBsb2NhdG9yPSIxNzI6MS0xNzI6NjYiIG5hbWU9IlRvU3RyaW5nIiBjb250ZXh0PSJQYXRpZW50IiBhY2Nlc3NMZXZlbD0iUHVibGljIiB4c2k6dHlwZT0iRnVuY3Rpb25EZWYiPgogICAgICAgICA8ZXhwcmVzc2lvbiBsb2NhdG9yPSIxNzI6NTYtMTcyOjY2IiBwYXRoPSJ2YWx1ZSIgeHNpOnR5cGU9IlByb3BlcnR5Ij4KICAgICAgICAgICAgPHNvdXJjZSBsb2NhdG9yPSIxNzI6NTYtMTcyOjYwIiBuYW1lPSJ2YWx1ZSIgeHNpOnR5cGU9Ik9wZXJhbmRSZWYiLz4KICAgICAgICAgPC9leHByZXNzaW9uPgogICAgICAgICA8b3BlcmFuZCBuYW1lPSJ2YWx1ZSI+CiAgICAgICAgICAgIDxvcGVyYW5kVHlwZVNwZWNpZmllciBsb2NhdG9yPSIxNzI6MzItMTcyOjUyIiBuYW1lPSJmaGlyOkFzc2VydGlvbk9wZXJhdG9yVHlwZSIgeHNpOnR5cGU9Ik5hbWVkVHlwZVNwZWNpZmllciIvPgogICAgICAgICA8L29wZXJhbmQ+CiAgICAgIDwvZGVmPgogICAgICA8ZGVmIGxvY2F0b3I9IjE3MzoxLTE3Mzo2NyIgbmFtZT0iVG9TdHJpbmciIGNvbnRleHQ9IlBhdGllbnQiIGFjY2Vzc0xldmVsPSJQdWJsaWMiIHhzaTp0eXBlPSJGdW5jdGlvbkRlZiI+CiAgICAgICAgIDxleHByZXNzaW9uIGxvY2F0b3I9IjE3Mzo1Ny0xNzM6NjciIHBhdGg9InZhbHVlIiB4c2k6dHlwZT0iUHJvcGVydHkiPgogICAgICAgICAgICA8c291cmNlIGxvY2F0b3I9IjE3Mzo1Ny0xNzM6NjEiIG5hbWU9InZhbHVlIiB4c2k6dHlwZT0iT3BlcmFuZFJlZiIvPgogICAgICAgICA8L2V4cHJlc3Npb24+CiAgICAgICAgIDxvcGVyYW5kIG5hbWU9InZhbHVlIj4KICAgICAgICAgICAgPG9wZXJhbmRUeXBlU3BlY2lmaWVyIGxvY2F0b3I9IjE3MzozMi0xNzM6NTMiIG5hbWU9ImZoaXI6QXNzZXJ0aW9uUmVzcG9uc2VUeXBlcyIgeHNpOnR5cGU9Ik5hbWVkVHlwZVNwZWNpZmllciIvPgogICAgICAgICA8L29wZXJhbmQ+CiAgICAgIDwvZGVmPgogICAgICA8ZGVmIGxvY2F0b3I9IjE3NDoxLTE3NDo2MSIgbmFtZT0iVG9TdHJpbmciIGNvbnRleHQ9IlBhdGllbnQiIGFjY2Vzc0xldmVsPSJQdWJsaWMiIHhzaTp0eXBlPSJGdW5jdGlvbkRlZiI+CiAgICAgICAgIDxleHByZXNzaW9uIGxvY2F0b3I9IjE3NDo1MS0xNzQ6NjEiIHBhdGg9InZhbHVlIiB4c2k6dHlwZT0iUHJvcGVydHkiPgogICAgICAgICAgICA8c291cmNlIGxvY2F0b3I9IjE3NDo1MS0xNzQ6NTUiIG5hbWU9InZhbHVlIiB4c2k6dHlwZT0iT3BlcmFuZFJlZiIvPgogICAgICAgICA8L2V4cHJlc3Npb24+CiAgICAgICAgIDxvcGVyYW5kIG5hbWU9InZhbHVlIj4KICAgICAgICAgICAgPG9wZXJhbmRUeXBlU3BlY2lmaWVyIGxvY2F0b3I9IjE3NDozMi0xNzQ6NDciIG5hbWU9ImZoaXI6QXVkaXRFdmVudEFjdGlvbiIgeHNpOnR5cGU9Ik5hbWVkVHlwZVNwZWNpZmllciIvPgogICAgICAgICA8L29wZXJhbmQ+CiAgICAgIDwvZGVmPgogICAgICA8ZGVmIGxvY2F0b3I9IjE3NToxLTE3NTo3MSIgbmFtZT0iVG9TdHJpbmciIGNvbnRleHQ9IlBhdGllbnQiIGFjY2Vzc0xldmVsPSJQdWJsaWMiIHhzaTp0eXBlPSJGdW5jdGlvbkRlZiI+CiAgICAgICAgIDxleHByZXNzaW9uIGxvY2F0b3I9IjE3NTo2MS0xNzU6NzEiIHBhdGg9InZhbHVlIiB4c2k6dHlwZT0iUHJvcGVydHkiPgogICAgICAgICAgICA8c291cmNlIGxvY2F0b3I9IjE3NTo2MS0xNzU6NjUiIG5hbWU9InZhbHVlIiB4c2k6dHlwZT0iT3BlcmFuZFJlZiIvPgogICAgICAgICA8L2V4cHJlc3Npb24+CiAgICAgICAgIDxvcGVyYW5kIG5hbWU9InZhbHVlIj4KICAgICAgICAgICAgPG9wZXJhbmRUeXBlU3BlY2lmaWVyIGxvY2F0b3I9IjE3NTozMi0xNzU6NTciIG5hbWU9ImZoaXI6QXVkaXRFdmVudEFnZW50TmV0d29ya1R5cGUiIHhzaTp0eXBlPSJOYW1lZFR5cGVTcGVjaWZpZXIiLz4KICAgICAgICAgPC9vcGVyYW5kPgogICAgICA8L2RlZj4KICAgICAgPGRlZiBsb2NhdG9yPSIxNzY6MS0xNzY6NjIiIG5hbWU9IlRvU3RyaW5nIiBjb250ZXh0PSJQYXRpZW50IiBhY2Nlc3NMZXZlbD0iUHVibGljIiB4c2k6dHlwZT0iRnVuY3Rpb25EZWYiPgogICAgICAgICA8ZXhwcmVzc2lvbiBsb2NhdG9yPSIxNzY6NTItMTc2OjYyIiBwYXRoPSJ2YWx1ZSIgeHNpOnR5cGU9IlByb3BlcnR5Ij4KICAgICAgICAgICAgPHNvdXJjZSBsb2NhdG9yPSIxNzY6NTItMTc2OjU2IiBuYW1lPSJ2YWx1ZSIgeHNpOnR5cGU9Ik9wZXJhbmRSZWYiLz4KICAgICAgICAgPC9leHByZXNzaW9uPgogICAgICAgICA8b3BlcmFuZCBuYW1lPSJ2YWx1ZSI+CiAgICAgICAgICAgIDxvcGVyYW5kVHlwZVNwZWNpZmllciBsb2NhdG9yPSIxNzY6MzItMTc2OjQ4IiBuYW1lPSJmaGlyOkF1ZGl0RXZlbnRPdXRjb21lIiB4c2k6dHlwZT0iTmFtZWRUeXBlU3BlY2lmaWVyIi8+CiAgICAgICAgIDwvb3BlcmFuZD4KICAgICAgPC9kZWY+CiAgICAgIDxkZWYgbG9jYXRvcj0iMTc3OjEtMTc3OjYwIiBuYW1lPSJUb1N0cmluZyIgY29udGV4dD0iUGF0aWVudCIgYWNjZXNzTGV2ZWw9IlB1YmxpYyIgeHNpOnR5cGU9IkZ1bmN0aW9uRGVmIj4KICAgICAgICAgPGV4cHJlc3Npb24gbG9jYXRvcj0iMTc3OjUwLTE3Nzo2MCIgcGF0aD0idmFsdWUiIHhzaTp0eXBlPSJQcm9wZXJ0eSI+CiAgICAgICAgICAgIDxzb3VyY2UgbG9jYXRvcj0iMTc3OjUwLTE3Nzo1NCIgbmFtZT0idmFsdWUiIHhzaTp0eXBlPSJPcGVyYW5kUmVmIi8+CiAgICAgICAgIDwvZXhwcmVzc2lvbj4KICAgICAgICAgPG9wZXJhbmQgbmFtZT0idmFsdWUiPgogICAgICAgICAgICA8b3BlcmFuZFR5cGVTcGVjaWZpZXIgbG9jYXRvcj0iMTc3OjMyLTE3Nzo0NiIgbmFtZT0iZmhpcjpCaW5kaW5nU3RyZW5ndGgiIHhzaTp0eXBlPSJOYW1lZFR5cGVTcGVjaWZpZXIiLz4KICAgICAgICAgPC9vcGVyYW5kPgogICAgICA8L2RlZj4KICAgICAgPGRlZiBsb2NhdG9yPSIxNzg6MS0xNzg6NzkiIG5hbWU9IlRvU3RyaW5nIiBjb250ZXh0PSJQYXRpZW50IiBhY2Nlc3NMZXZlbD0iUHVibGljIiB4c2k6dHlwZT0iRnVuY3Rpb25EZWYiPgogICAgICAgICA8ZXhwcmVzc2lvbiBsb2NhdG9yPSIxNzg6NjktMTc4Ojc5IiBwYXRoPSJ2YWx1ZSIgeHNpOnR5cGU9IlByb3BlcnR5Ij4KICAgICAgICAgICAgPHNvdXJjZSBsb2NhdG9yPSIxNzg6NjktMTc4OjczIiBuYW1lPSJ2YWx1ZSIgeHNpOnR5cGU9Ik9wZXJhbmRSZWYiLz4KICAgICAgICAgPC9leHByZXNzaW9uPgogICAgICAgICA8b3BlcmFuZCBuYW1lPSJ2YWx1ZSI+CiAgICAgICAgICAgIDxvcGVyYW5kVHlwZVNwZWNpZmllciBsb2NhdG9yPSIxNzg6MzItMTc4OjY1IiBuYW1lPSJmaGlyOkJpb2xvZ2ljYWxseURlcml2ZWRQcm9kdWN0Q2F0ZWdvcnkiIHhzaTp0eXBlPSJOYW1lZFR5cGVTcGVjaWZpZXIiLz4KICAgICAgICAgPC9vcGVyYW5kPgogICAgICA8L2RlZj4KICAgICAgPGRlZiBsb2NhdG9yPSIxNzk6MS0xNzk6NzciIG5hbWU9IlRvU3RyaW5nIiBjb250ZXh0PSJQYXRpZW50IiBhY2Nlc3NMZXZlbD0iUHVibGljIiB4c2k6dHlwZT0iRnVuY3Rpb25EZWYiPgogICAgICAgICA8ZXhwcmVzc2lvbiBsb2NhdG9yPSIxNzk6NjctMTc5Ojc3IiBwYXRoPSJ2YWx1ZSIgeHNpOnR5cGU9IlByb3BlcnR5Ij4KICAgICAgICAgICAgPHNvdXJjZSBsb2NhdG9yPSIxNzk6NjctMTc5OjcxIiBuYW1lPSJ2YWx1ZSIgeHNpOnR5cGU9Ik9wZXJhbmRSZWYiLz4KICAgICAgICAgPC9leHByZXNzaW9uPgogICAgICAgICA8b3BlcmFuZCBuYW1lPSJ2YWx1ZSI+CiAgICAgICAgICAgIDxvcGVyYW5kVHlwZVNwZWNpZmllciBsb2NhdG9yPSIxNzk6MzItMTc5OjYzIiBuYW1lPSJmaGlyOkJpb2xvZ2ljYWxseURlcml2ZWRQcm9kdWN0U3RhdHVzIiB4c2k6dHlwZT0iTmFtZWRUeXBlU3BlY2lmaWVyIi8+CiAgICAgICAgIDwvb3BlcmFuZD4KICAgICAgPC9kZWY+CiAgICAgIDxkZWYgbG9jYXRvcj0iMTgwOjEtMTgwOjgzIiBuYW1lPSJUb1N0cmluZyIgY29udGV4dD0iUGF0aWVudCIgYWNjZXNzTGV2ZWw9IlB1YmxpYyIgeHNpOnR5cGU9IkZ1bmN0aW9uRGVmIj4KICAgICAgICAgPGV4cHJlc3Npb24gbG9jYXRvcj0iMTgwOjczLTE4MDo4MyIgcGF0aD0idmFsdWUiIHhzaTp0eXBlPSJQcm9wZXJ0eSI+CiAgICAgICAgICAgIDxzb3VyY2UgbG9jYXRvcj0iMTgwOjczLTE4MDo3NyIgbmFtZT0idmFsdWUiIHhzaTp0eXBlPSJPcGVyYW5kUmVmIi8+CiAgICAgICAgIDwvZXhwcmVzc2lvbj4KICAgICAgICAgPG9wZXJhbmQgbmFtZT0idmFsdWUiPgogICAgICAgICAgICA8b3BlcmFuZFR5cGVTcGVjaWZpZXIgbG9jYXRvcj0iMTgwOjMyLTE4MDo2OSIgbmFtZT0iZmhpcjpCaW9sb2dpY2FsbHlEZXJpdmVkUHJvZHVjdFN0b3JhZ2VTY2FsZSIgeHNpOnR5cGU9Ik5hbWVkVHlwZVNwZWNpZmllciIvPgogICAgICAgICA8L29wZXJhbmQ+CiAgICAgIDwvZGVmPgogICAgICA8ZGVmIGxvY2F0b3I9IjE4MToxLTE4MTo1NSIgbmFtZT0iVG9TdHJpbmciIGNvbnRleHQ9IlBhdGllbnQiIGFjY2Vzc0xldmVsPSJQdWJsaWMiIHhzaTp0eXBlPSJGdW5jdGlvbkRlZiI+CiAgICAgICAgIDxleHByZXNzaW9uIGxvY2F0b3I9IjE4MTo0NS0xODE6NTUiIHBhdGg9InZhbHVlIiB4c2k6dHlwZT0iUHJvcGVydHkiPgogICAgICAgICAgICA8c291cmNlIGxvY2F0b3I9IjE4MTo0NS0xODE6NDkiIG5hbWU9InZhbHVlIiB4c2k6dHlwZT0iT3BlcmFuZFJlZiIvPgogICAgICAgICA8L2V4cHJlc3Npb24+CiAgICAgICAgIDxvcGVyYW5kIG5hbWU9InZhbHVlIj4KICAgICAgICAgICAgPG9wZXJhbmRUeXBlU3BlY2lmaWVyIGxvY2F0b3I9IjE4MTozMi0xODE6NDEiIG5hbWU9ImZoaXI6QnVuZGxlVHlwZSIgeHNpOnR5cGU9Ik5hbWVkVHlwZVNwZWNpZmllciIvPgogICAgICAgICA8L29wZXJhbmQ+CiAgICAgIDwvZGVmPgogICAgICA8ZGVmIGxvY2F0b3I9IjE4MjoxLTE4Mjo2OCIgbmFtZT0iVG9TdHJpbmciIGNvbnRleHQ9IlBhdGllbnQiIGFjY2Vzc0xldmVsPSJQdWJsaWMiIHhzaTp0eXBlPSJGdW5jdGlvbkRlZiI+CiAgICAgICAgIDxleHByZXNzaW9uIGxvY2F0b3I9IjE4Mjo1OC0xODI6NjgiIHBhdGg9InZhbHVlIiB4c2k6dHlwZT0iUHJvcGVydHkiPgogICAgICAgICAgICA8c291cmNlIGxvY2F0b3I9IjE4Mjo1OC0xODI6NjIiIG5hbWU9InZhbHVlIiB4c2k6dHlwZT0iT3BlcmFuZFJlZiIvPgogICAgICAgICA8L2V4cHJlc3Npb24+CiAgICAgICAgIDxvcGVyYW5kIG5hbWU9InZhbHVlIj4KICAgICAgICAgICAgPG9wZXJhbmRUeXBlU3BlY2lmaWVyIGxvY2F0b3I9IjE4MjozMi0xODI6NTQiIG5hbWU9ImZoaXI6Q2FwYWJpbGl0eVN0YXRlbWVudEtpbmQiIHhzaTp0eXBlPSJOYW1lZFR5cGVTcGVjaWZpZXIiLz4KICAgICAgICAgPC9vcGVyYW5kPgogICAgICA8L2RlZj4KICAgICAgPGRlZiBsb2NhdG9yPSIxODM6MS0xODM6NjUiIG5hbWU9IlRvU3RyaW5nIiBjb250ZXh0PSJQYXRpZW50IiBhY2Nlc3NMZXZlbD0iUHVibGljIiB4c2k6dHlwZT0iRnVuY3Rpb25EZWYiPgogICAgICAgICA8ZXhwcmVzc2lvbiBsb2NhdG9yPSIxODM6NTUtMTgzOjY1IiBwYXRoPSJ2YWx1ZSIgeHNpOnR5cGU9IlByb3BlcnR5Ij4KICAgICAgICAgICAgPHNvdXJjZSBsb2NhdG9yPSIxODM6NTUtMTgzOjU5IiBuYW1lPSJ2YWx1ZSIgeHNpOnR5cGU9Ik9wZXJhbmRSZWYiLz4KICAgICAgICAgPC9leHByZXNzaW9uPgogICAgICAgICA8b3BlcmFuZCBuYW1lPSJ2YWx1ZSI+CiAgICAgICAgICAgIDxvcGVyYW5kVHlwZVNwZWNpZmllciBsb2NhdG9yPSIxODM6MzItMTgzOjUxIiBuYW1lPSJmaGlyOkNhcmVQbGFuQWN0aXZpdHlLaW5kIiB4c2k6dHlwZT0iTmFtZWRUeXBlU3BlY2lmaWVyIi8+CiAgICAgICAgIDwvb3BlcmFuZD4KICAgICAgPC9kZWY+CiAgICAgIDxkZWYgbG9jYXRvcj0iMTg0OjEtMTg0OjY3IiBuYW1lPSJUb1N0cmluZyIgY29udGV4dD0iUGF0aWVudCIgYWNjZXNzTGV2ZWw9IlB1YmxpYyIgeHNpOnR5cGU9IkZ1bmN0aW9uRGVmIj4KICAgICAgICAgPGV4cHJlc3Npb24gbG9jYXRvcj0iMTg0OjU3LTE4NDo2NyIgcGF0aD0idmFsdWUiIHhzaTp0eXBlPSJQcm9wZXJ0eSI+CiAgICAgICAgICAgIDxzb3VyY2UgbG9jYXRvcj0iMTg0OjU3LTE4NDo2MSIgbmFtZT0idmFsdWUiIHhzaTp0eXBlPSJPcGVyYW5kUmVmIi8+CiAgICAgICAgIDwvZXhwcmVzc2lvbj4KICAgICAgICAgPG9wZXJhbmQgbmFtZT0idmFsdWUiPgogICAgICAgICAgICA8b3BlcmFuZFR5cGVTcGVjaWZpZXIgbG9jYXRvcj0iMTg0OjMyLTE4NDo1MyIgbmFtZT0iZmhpcjpDYXJlUGxhbkFjdGl2aXR5U3RhdHVzIiB4c2k6dHlwZT0iTmFtZWRUeXBlU3BlY2lmaWVyIi8+CiAgICAgICAgIDwvb3BlcmFuZD4KICAgICAgPC9kZWY+CiAgICAgIDxkZWYgbG9jYXRvcj0iMTg1OjEtMTg1OjU5IiBuYW1lPSJUb1N0cmluZyIgY29udGV4dD0iUGF0aWVudCIgYWNjZXNzTGV2ZWw9IlB1YmxpYyIgeHNpOnR5cGU9IkZ1bmN0aW9uRGVmIj4KICAgICAgICAgPGV4cHJlc3Npb24gbG9jYXRvcj0iMTg1OjQ5LTE4NTo1OSIgcGF0aD0idmFsdWUiIHhzaTp0eXBlPSJQcm9wZXJ0eSI+CiAgICAgICAgICAgIDxzb3VyY2UgbG9jYXRvcj0iMTg1OjQ5LTE4NTo1MyIgbmFtZT0idmFsdWUiIHhzaTp0eXBlPSJPcGVyYW5kUmVmIi8+CiAgICAgICAgIDwvZXhwcmVzc2lvbj4KICAgICAgICAgPG9wZXJhbmQgbmFtZT0idmFsdWUiPgogICAgICAgICAgICA8b3BlcmFuZFR5cGVTcGVjaWZpZXIgbG9jYXRvcj0iMTg1OjMyLTE4NTo0NSIgbmFtZT0iZmhpcjpDYXJlUGxhbkludGVudCIgeHNpOnR5cGU9Ik5hbWVkVHlwZVNwZWNpZmllciIvPgogICAgICAgICA8L29wZXJhbmQ+CiAgICAgIDwvZGVmPgogICAgICA8ZGVmIGxvY2F0b3I9IjE4NjoxLTE4Njo1OSIgbmFtZT0iVG9TdHJpbmciIGNvbnRleHQ9IlBhdGllbnQiIGFjY2Vzc0xldmVsPSJQdWJsaWMiIHhzaTp0eXBlPSJGdW5jdGlvbkRlZiI+CiAgICAgICAgIDxleHByZXNzaW9uIGxvY2F0b3I9IjE4Njo0OS0xODY6NTkiIHBhdGg9InZhbHVlIiB4c2k6dHlwZT0iUHJvcGVydHkiPgogICAgICAgICAgICA8c291cmNlIGxvY2F0b3I9IjE4Njo0OS0xODY6NTMiIG5hbWU9InZhbHVlIiB4c2k6dHlwZT0iT3BlcmFuZFJlZiIvPgogICAgICAgICA8L2V4cHJlc3Npb24+CiAgICAgICAgIDxvcGVyYW5kIG5hbWU9InZhbHVlIj4KICAgICAgICAgICAgPG9wZXJhbmRUeXBlU3BlY2lmaWVyIGxvY2F0b3I9IjE4NjozMi0xODY6NDUiIG5hbWU9ImZoaXI6Q2FyZVBsYW5TdGF0dXMiIHhzaTp0eXBlPSJOYW1lZFR5cGVTcGVjaWZpZXIiLz4KICAgICAgICAgPC9vcGVyYW5kPgogICAgICA8L2RlZj4KICAgICAgPGRlZiBsb2NhdG9yPSIxODc6MS0xODc6NTkiIG5hbWU9IlRvU3RyaW5nIiBjb250ZXh0PSJQYXRpZW50IiBhY2Nlc3NMZXZlbD0iUHVibGljIiB4c2k6dHlwZT0iRnVuY3Rpb25EZWYiPgogICAgICAgICA8ZXhwcmVzc2lvbiBsb2NhdG9yPSIxODc6NDktMTg3OjU5IiBwYXRoPSJ2YWx1ZSIgeHNpOnR5cGU9IlByb3BlcnR5Ij4KICAgICAgICAgICAgPHNvdXJjZSBsb2NhdG9yPSIxODc6NDktMTg3OjUzIiBuYW1lPSJ2YWx1ZSIgeHNpOnR5cGU9Ik9wZXJhbmRSZWYiLz4KICAgICAgICAgPC9leHByZXNzaW9uPgogICAgICAgICA8b3BlcmFuZCBuYW1lPSJ2YWx1ZSI+CiAgICAgICAgICAgIDxvcGVyYW5kVHlwZVNwZWNpZmllciBsb2NhdG9yPSIxODc6MzItMTg3OjQ1IiBuYW1lPSJmaGlyOkNhcmVUZWFtU3RhdHVzIiB4c2k6dHlwZT0iTmFtZWRUeXBlU3BlY2lmaWVyIi8+CiAgICAgICAgIDwvb3BlcmFuZD4KICAgICAgPC9kZWY+CiAgICAgIDxkZWYgbG9jYXRvcj0iMTg4OjEtMTg4OjY5IiBuYW1lPSJUb1N0cmluZyIgY29udGV4dD0iUGF0aWVudCIgYWNjZXNzTGV2ZWw9IlB1YmxpYyIgeHNpOnR5cGU9IkZ1bmN0aW9uRGVmIj4KICAgICAgICAgPGV4cHJlc3Npb24gbG9jYXRvcj0iMTg4OjU5LTE4ODo2OSIgcGF0aD0idmFsdWUiIHhzaTp0eXBlPSJQcm9wZXJ0eSI+CiAgICAgICAgICAgIDxzb3VyY2UgbG9jYXRvcj0iMTg4OjU5LTE4ODo2MyIgbmFtZT0idmFsdWUiIHhzaTp0eXBlPSJPcGVyYW5kUmVmIi8+CiAgICAgICAgIDwvZXhwcmVzc2lvbj4KICAgICAgICAgPG9wZXJhbmQgbmFtZT0idmFsdWUiPgogICAgICAgICAgICA8b3BlcmFuZFR5cGVTcGVjaWZpZXIgbG9jYXRvcj0iMTg4OjMyLTE4ODo1NSIgbmFtZT0iZmhpcjpDYXRhbG9nRW50cnlSZWxhdGlvblR5cGUiIHhzaTp0eXBlPSJOYW1lZFR5cGVTcGVjaWZpZXIiLz4KICAgICAgICAgPC9vcGVyYW5kPgogICAgICA8L2RlZj4KICAgICAgPGRlZiBsb2NhdG9yPSIxODk6MS0xODk6ODMiIG5hbWU9IlRvU3RyaW5nIiBjb250ZXh0PSJQYXRpZW50IiBhY2Nlc3NMZXZlbD0iUHVibGljIiB4c2k6dHlwZT0iRnVuY3Rpb25EZWYiPgogICAgICAgICA8ZXhwcmVzc2lvbiBsb2NhdG9yPSIxODk6NzMtMTg5OjgzIiBwYXRoPSJ2YWx1ZSIgeHNpOnR5cGU9IlByb3BlcnR5Ij4KICAgICAgICAgICAgPHNvdXJjZSBsb2NhdG9yPSIxODk6NzMtMTg5Ojc3IiBuYW1lPSJ2YWx1ZSIgeHNpOnR5cGU9Ik9wZXJhbmRSZWYiLz4KICAgICAgICAgPC9leHByZXNzaW9uPgogICAgICAgICA8b3BlcmFuZCBuYW1lPSJ2YWx1ZSI+CiAgICAgICAgICAgIDxvcGVyYW5kVHlwZVNwZWNpZmllciBsb2NhdG9yPSIxODk6MzItMTg5OjY5IiBuYW1lPSJmaGlyOkNoYXJnZUl0ZW1EZWZpbml0aW9uUHJpY2VDb21wb25lbnRUeXBlIiB4c2k6dHlwZT0iTmFtZWRUeXBlU3BlY2lmaWVyIi8+CiAgICAgICAgIDwvb3BlcmFuZD4KICAgICAgPC9kZWY+CiAgICAgIDxkZWYgbG9jYXRvcj0iMTkwOjEtMTkwOjYxIiBuYW1lPSJUb1N0cmluZyIgY29udGV4dD0iUGF0aWVudCIgYWNjZXNzTGV2ZWw9IlB1YmxpYyIgeHNpOnR5cGU9IkZ1bmN0aW9uRGVmIj4KICAgICAgICAgPGV4cHJlc3Npb24gbG9jYXRvcj0iMTkwOjUxLTE5MDo2MSIgcGF0aD0idmFsdWUiIHhzaTp0eXBlPSJQcm9wZXJ0eSI+CiAgICAgICAgICAgIDxzb3VyY2UgbG9jYXRvcj0iMTkwOjUxLTE5MDo1NSIgbmFtZT0idmFsdWUiIHhzaTp0eXBlPSJPcGVyYW5kUmVmIi8+CiAgICAgICAgIDwvZXhwcmVzc2lvbj4KICAgICAgICAgPG9wZXJhbmQgbmFtZT0idmFsdWUiPgogICAgICAgICAgICA8b3BlcmFuZFR5cGVTcGVjaWZpZXIgbG9jYXRvcj0iMTkwOjMyLTE5MDo0NyIgbmFtZT0iZmhpcjpDaGFyZ2VJdGVtU3RhdHVzIiB4c2k6dHlwZT0iTmFtZWRUeXBlU3BlY2lmaWVyIi8+CiAgICAgICAgIDwvb3BlcmFuZD4KICAgICAgPC9kZWY+CiAgICAgIDxkZWYgbG9jYXRvcj0iMTkxOjEtMTkxOjY0IiBuYW1lPSJUb1N0cmluZyIgY29udGV4dD0iUGF0aWVudCIgYWNjZXNzTGV2ZWw9IlB1YmxpYyIgeHNpOnR5cGU9IkZ1bmN0aW9uRGVmIj4KICAgICAgICAgPGV4cHJlc3Npb24gbG9jYXRvcj0iMTkxOjU0LTE5MTo2NCIgcGF0aD0idmFsdWUiIHhzaTp0eXBlPSJQcm9wZXJ0eSI+CiAgICAgICAgICAgIDxzb3VyY2UgbG9jYXRvcj0iMTkxOjU0LTE5MTo1OCIgbmFtZT0idmFsdWUiIHhzaTp0eXBlPSJPcGVyYW5kUmVmIi8+CiAgICAgICAgIDwvZXhwcmVzc2lvbj4KICAgICAgICAgPG9wZXJhbmQgbmFtZT0idmFsdWUiPgogICAgICAgICAgICA8b3BlcmFuZFR5cGVTcGVjaWZpZXIgbG9jYXRvcj0iMTkxOjMyLTE5MTo1MCIgbmFtZT0iZmhpcjpDbGFpbVJlc3BvbnNlU3RhdHVzIiB4c2k6dHlwZT0iTmFtZWRUeXBlU3BlY2lmaWVyIi8+CiAgICAgICAgIDwvb3BlcmFuZD4KICAgICAgPC9kZWY+CiAgICAgIDxkZWYgbG9jYXRvcj0iMTkyOjEtMTkyOjU2IiBuYW1lPSJUb1N0cmluZyIgY29udGV4dD0iUGF0aWVudCIgYWNjZXNzTGV2ZWw9IlB1YmxpYyIgeHNpOnR5cGU9IkZ1bmN0aW9uRGVmIj4KICAgICAgICAgPGV4cHJlc3Npb24gbG9jYXRvcj0iMTkyOjQ2LTE5Mjo1NiIgcGF0aD0idmFsdWUiIHhzaTp0eXBlPSJQcm9wZXJ0eSI+CiAgICAgICAgICAgIDxzb3VyY2UgbG9jYXRvcj0iMTkyOjQ2LTE5Mjo1MCIgbmFtZT0idmFsdWUiIHhzaTp0eXBlPSJPcGVyYW5kUmVmIi8+CiAgICAgICAgIDwvZXhwcmVzc2lvbj4KICAgICAgICAgPG9wZXJhbmQgbmFtZT0idmFsdWUiPgogICAgICAgICAgICA8b3BlcmFuZFR5cGVTcGVjaWZpZXIgbG9jYXRvcj0iMTkyOjMyLTE5Mjo0MiIgbmFtZT0iZmhpcjpDbGFpbVN0YXR1cyIgeHNpOnR5cGU9Ik5hbWVkVHlwZVNwZWNpZmllciIvPgogICAgICAgICA8L29wZXJhbmQ+CiAgICAgIDwvZGVmPgogICAgICA8ZGVmIGxvY2F0b3I9IjE5MzoxLTE5Mzo2OSIgbmFtZT0iVG9TdHJpbmciIGNvbnRleHQ9IlBhdGllbnQiIGFjY2Vzc0xldmVsPSJQdWJsaWMiIHhzaTp0eXBlPSJGdW5jdGlvbkRlZiI+CiAgICAgICAgIDxleHByZXNzaW9uIGxvY2F0b3I9IjE5Mzo1OS0xOTM6NjkiIHBhdGg9InZhbHVlIiB4c2k6dHlwZT0iUHJvcGVydHkiPgogICAgICAgICAgICA8c291cmNlIGxvY2F0b3I9IjE5Mzo1OS0xOTM6NjMiIG5hbWU9InZhbHVlIiB4c2k6dHlwZT0iT3BlcmFuZFJlZiIvPgogICAgICAgICA8L2V4cHJlc3Npb24+CiAgICAgICAgIDxvcGVyYW5kIG5hbWU9InZhbHVlIj4KICAgICAgICAgICAgPG9wZXJhbmRUeXBlU3BlY2lmaWVyIGxvY2F0b3I9IjE5MzozMi0xOTM6NTUiIG5hbWU9ImZoaXI6Q2xpbmljYWxJbXByZXNzaW9uU3RhdHVzIiB4c2k6dHlwZT0iTmFtZWRUeXBlU3BlY2lmaWVyIi8+CiAgICAgICAgIDwvb3BlcmFuZD4KICAgICAgPC9kZWY+CiAgICAgIDxkZWYgbG9jYXRvcj0iMTk0OjEtMTk0OjYyIiBuYW1lPSJUb1N0cmluZyIgY29udGV4dD0iUGF0aWVudCIgYWNjZXNzTGV2ZWw9IlB1YmxpYyIgeHNpOnR5cGU9IkZ1bmN0aW9uRGVmIj4KICAgICAgICAgPGV4cHJlc3Npb24gbG9jYXRvcj0iMTk0OjUyLTE5NDo2MiIgcGF0aD0idmFsdWUiIHhzaTp0eXBlPSJQcm9wZXJ0eSI+CiAgICAgICAgICAgIDxzb3VyY2UgbG9jYXRvcj0iMTk0OjUyLTE5NDo1NiIgbmFtZT0idmFsdWUiIHhzaTp0eXBlPSJPcGVyYW5kUmVmIi8+CiAgICAgICAgIDwvZXhwcmVzc2lvbj4KICAgICAgICAgPG9wZXJhbmQgbmFtZT0idmFsdWUiPgogICAgICAgICAgICA8b3BlcmFuZFR5cGVTcGVjaWZpZXIgbG9jYXRvcj0iMTk0OjMyLTE5NDo0OCIgbmFtZT0iZmhpcjpDb2RlU2VhcmNoU3VwcG9ydCIgeHNpOnR5cGU9Ik5hbWVkVHlwZVNwZWNpZmllciIvPgogICAgICAgICA8L29wZXJhbmQ+CiAgICAgIDwvZGVmPgogICAgICA8ZGVmIGxvY2F0b3I9IjE5NToxLTE5NTo2NiIgbmFtZT0iVG9TdHJpbmciIGNvbnRleHQ9IlBhdGllbnQiIGFjY2Vzc0xldmVsPSJQdWJsaWMiIHhzaTp0eXBlPSJGdW5jdGlvbkRlZiI+CiAgICAgICAgIDxleHByZXNzaW9uIGxvY2F0b3I9IjE5NTo1Ni0xOTU6NjYiIHBhdGg9InZhbHVlIiB4c2k6dHlwZT0iUHJvcGVydHkiPgogICAgICAgICAgICA8c291cmNlIGxvY2F0b3I9IjE5NTo1Ni0xOTU6NjAiIG5hbWU9InZhbHVlIiB4c2k6dHlwZT0iT3BlcmFuZFJlZiIvPgogICAgICAgICA8L2V4cHJlc3Npb24+CiAgICAgICAgIDxvcGVyYW5kIG5hbWU9InZhbHVlIj4KICAgICAgICAgICAgPG9wZXJhbmRUeXBlU3BlY2lmaWVyIGxvY2F0b3I9IjE5NTozMi0xOTU6NTIiIG5hbWU9ImZoaXI6Q29kZVN5c3RlbUNvbnRlbnRNb2RlIiB4c2k6dHlwZT0iTmFtZWRUeXBlU3BlY2lmaWVyIi8+CiAgICAgICAgIDwvb3BlcmFuZD4KICAgICAgPC9kZWY+CiAgICAgIDxkZWYgbG9jYXRvcj0iMTk2OjEtMTk2OjcxIiBuYW1lPSJUb1N0cmluZyIgY29udGV4dD0iUGF0aWVudCIgYWNjZXNzTGV2ZWw9IlB1YmxpYyIgeHNpOnR5cGU9IkZ1bmN0aW9uRGVmIj4KICAgICAgICAgPGV4cHJlc3Npb24gbG9jYXRvcj0iMTk2OjYxLTE5Njo3MSIgcGF0aD0idmFsdWUiIHhzaTp0eXBlPSJQcm9wZXJ0eSI+CiAgICAgICAgICAgIDxzb3VyY2UgbG9jYXRvcj0iMTk2OjYxLTE5Njo2NSIgbmFtZT0idmFsdWUiIHhzaTp0eXBlPSJPcGVyYW5kUmVmIi8+CiAgICAgICAgIDwvZXhwcmVzc2lvbj4KICAgICAgICAgPG9wZXJhbmQgbmFtZT0idmFsdWUiPgogICAgICAgICAgICA8b3BlcmFuZFR5cGVTcGVjaWZpZXIgbG9jYXRvcj0iMTk2OjMyLTE5Njo1NyIgbmFtZT0iZmhpcjpDb2RlU3lzdGVtSGllcmFyY2h5TWVhbmluZyIgeHNpOnR5cGU9Ik5hbWVkVHlwZVNwZWNpZmllciIvPgogICAgICAgICA8L29wZXJhbmQ+CiAgICAgIDwvZGVmPgogICAgICA8ZGVmIGxvY2F0b3I9IjE5NzoxLTE5Nzo2NiIgbmFtZT0iVG9TdHJpbmciIGNvbnRleHQ9IlBhdGllbnQiIGFjY2Vzc0xldmVsPSJQdWJsaWMiIHhzaTp0eXBlPSJGdW5jdGlvbkRlZiI+CiAgICAgICAgIDxleHByZXNzaW9uIGxvY2F0b3I9IjE5Nzo1Ni0xOTc6NjYiIHBhdGg9InZhbHVlIiB4c2k6dHlwZT0iUHJvcGVydHkiPgogICAgICAgICAgICA8c291cmNlIGxvY2F0b3I9IjE5Nzo1Ni0xOTc6NjAiIG5hbWU9InZhbHVlIiB4c2k6dHlwZT0iT3BlcmFuZFJlZiIvPgogICAgICAgICA8L2V4cHJlc3Npb24+CiAgICAgICAgIDxvcGVyYW5kIG5hbWU9InZhbHVlIj4KICAgICAgICAgICAgPG9wZXJhbmRUeXBlU3BlY2lmaWVyIGxvY2F0b3I9IjE5NzozMi0xOTc6NTIiIG5hbWU9ImZoaXI6Q29tbXVuaWNhdGlvblByaW9yaXR5IiB4c2k6dHlwZT0iTmFtZWRUeXBlU3BlY2lmaWVyIi8+CiAgICAgICAgIDwvb3BlcmFuZD4KICAgICAgPC9kZWY+CiAgICAgIDxkZWYgbG9jYXRvcj0iMTk4OjEtMTk4OjcxIiBuYW1lPSJUb1N0cmluZyIgY29udGV4dD0iUGF0aWVudCIgYWNjZXNzTGV2ZWw9IlB1YmxpYyIgeHNpOnR5cGU9IkZ1bmN0aW9uRGVmIj4KICAgICAgICAgPGV4cHJlc3Npb24gbG9jYXRvcj0iMTk4OjYxLTE5ODo3MSIgcGF0aD0idmFsdWUiIHhzaTp0eXBlPSJQcm9wZXJ0eSI+CiAgICAgICAgICAgIDxzb3VyY2UgbG9jYXRvcj0iMTk4OjYxLTE5ODo2NSIgbmFtZT0idmFsdWUiIHhzaTp0eXBlPSJPcGVyYW5kUmVmIi8+CiAgICAgICAgIDwvZXhwcmVzc2lvbj4KICAgICAgICAgPG9wZXJhbmQgbmFtZT0idmFsdWUiPgogICAgICAgICAgICA8b3BlcmFuZFR5cGVTcGVjaWZpZXIgbG9jYXRvcj0iMTk4OjMyLTE5ODo1NyIgbmFtZT0iZmhpcjpDb21tdW5pY2F0aW9uUmVxdWVzdFN0YXR1cyIgeHNpOnR5cGU9Ik5hbWVkVHlwZVNwZWNpZmllciIvPgogICAgICAgICA8L29wZXJhbmQ+CiAgICAgIDwvZGVmPgogICAgICA8ZGVmIGxvY2F0b3I9IjE5OToxLTE5OTo2NCIgbmFtZT0iVG9TdHJpbmciIGNvbnRleHQ9IlBhdGllbnQiIGFjY2Vzc0xldmVsPSJQdWJsaWMiIHhzaTp0eXBlPSJGdW5jdGlvbkRlZiI+CiAgICAgICAgIDxleHByZXNzaW9uIGxvY2F0b3I9IjE5OTo1NC0xOTk6NjQiIHBhdGg9InZhbHVlIiB4c2k6dHlwZT0iUHJvcGVydHkiPgogICAgICAgICAgICA8c291cmNlIGxvY2F0b3I9IjE5OTo1NC0xOTk6NTgiIG5hbWU9InZhbHVlIiB4c2k6dHlwZT0iT3BlcmFuZFJlZiIvPgogICAgICAgICA8L2V4cHJlc3Npb24+CiAgICAgICAgIDxvcGVyYW5kIG5hbWU9InZhbHVlIj4KICAgICAgICAgICAgPG9wZXJhbmRUeXBlU3BlY2lmaWVyIGxvY2F0b3I9IjE5OTozMi0xOTk6NTAiIG5hbWU9ImZoaXI6Q29tbXVuaWNhdGlvblN0YXR1cyIgeHNpOnR5cGU9Ik5hbWVkVHlwZVNwZWNpZmllciIvPgogICAgICAgICA8L29wZXJhbmQ+CiAgICAgIDwvZGVmPgogICAgICA8ZGVmIGxvY2F0b3I9IjIwMDoxLTIwMDo2MCIgbmFtZT0iVG9TdHJpbmciIGNvbnRleHQ9IlBhdGllbnQiIGFjY2Vzc0xldmVsPSJQdWJsaWMiIHhzaTp0eXBlPSJGdW5jdGlvbkRlZiI+CiAgICAgICAgIDxleHByZXNzaW9uIGxvY2F0b3I9IjIwMDo1MC0yMDA6NjAiIHBhdGg9InZhbHVlIiB4c2k6dHlwZT0iUHJvcGVydHkiPgogICAgICAgICAgICA8c291cmNlIGxvY2F0b3I9IjIwMDo1MC0yMDA6NTQiIG5hbWU9InZhbHVlIiB4c2k6dHlwZT0iT3BlcmFuZFJlZiIvPgogICAgICAgICA8L2V4cHJlc3Npb24+CiAgICAgICAgIDxvcGVyYW5kIG5hbWU9InZhbHVlIj4KICAgICAgICAgICAgPG9wZXJhbmRUeXBlU3BlY2lmaWVyIGxvY2F0b3I9IjIwMDozMi0yMDA6NDYiIG5hbWU9ImZoaXI6Q29tcGFydG1lbnRDb2RlIiB4c2k6dHlwZT0iTmFtZWRUeXBlU3BlY2lmaWVyIi8+CiAgICAgICAgIDwvb3BlcmFuZD4KICAgICAgPC9kZWY+CiAgICAgIDxkZWYgbG9jYXRvcj0iMjAxOjEtMjAxOjYwIiBuYW1lPSJUb1N0cmluZyIgY29udGV4dD0iUGF0aWVudCIgYWNjZXNzTGV2ZWw9IlB1YmxpYyIgeHNpOnR5cGU9IkZ1bmN0aW9uRGVmIj4KICAgICAgICAgPGV4cHJlc3Npb24gbG9jYXRvcj0iMjAxOjUwLTIwMTo2MCIgcGF0aD0idmFsdWUiIHhzaTp0eXBlPSJQcm9wZXJ0eSI+CiAgICAgICAgICAgIDxzb3VyY2UgbG9jYXRvcj0iMjAxOjUwLTIwMTo1NCIgbmFtZT0idmFsdWUiIHhzaTp0eXBlPSJPcGVyYW5kUmVmIi8+CiAgICAgICAgIDwvZXhwcmVzc2lvbj4KICAgICAgICAgPG9wZXJhbmQgbmFtZT0idmFsdWUiPgogICAgICAgICAgICA8b3BlcmFuZFR5cGVTcGVjaWZpZXIgbG9jYXRvcj0iMjAxOjMyLTIwMTo0NiIgbmFtZT0iZmhpcjpDb21wYXJ0bWVudFR5cGUiIHhzaTp0eXBlPSJOYW1lZFR5cGVTcGVjaWZpZXIiLz4KICAgICAgICAgPC9vcGVyYW5kPgogICAgICA8L2RlZj4KICAgICAgPGRlZiBsb2NhdG9yPSIyMDI6MS0yMDI6NzEiIG5hbWU9IlRvU3RyaW5nIiBjb250ZXh0PSJQYXRpZW50IiBhY2Nlc3NMZXZlbD0iUHVibGljIiB4c2k6dHlwZT0iRnVuY3Rpb25EZWYiPgogICAgICAgICA8ZXhwcmVzc2lvbiBsb2NhdG9yPSIyMDI6NjEtMjAyOjcxIiBwYXRoPSJ2YWx1ZSIgeHNpOnR5cGU9IlByb3BlcnR5Ij4KICAgICAgICAgICAgPHNvdXJjZSBsb2NhdG9yPSIyMDI6NjEtMjAyOjY1IiBuYW1lPSJ2YWx1ZSIgeHNpOnR5cGU9Ik9wZXJhbmRSZWYiLz4KICAgICAgICAgPC9leHByZXNzaW9uPgogICAgICAgICA8b3BlcmFuZCBuYW1lPSJ2YWx1ZSI+CiAgICAgICAgICAgIDxvcGVyYW5kVHlwZVNwZWNpZmllciBsb2NhdG9yPSIyMDI6MzItMjAyOjU3IiBuYW1lPSJmaGlyOkNvbXBvc2l0aW9uQXR0ZXN0YXRpb25Nb2RlIiB4c2k6dHlwZT0iTmFtZWRUeXBlU3BlY2lmaWVyIi8+CiAgICAgICAgIDwvb3BlcmFuZD4KICAgICAgPC9kZWY+CiAgICAgIDxkZWYgbG9jYXRvcj0iMjAzOjEtMjAzOjYyIiBuYW1lPSJUb1N0cmluZyIgY29udGV4dD0iUGF0aWVudCIgYWNjZXNzTGV2ZWw9IlB1YmxpYyIgeHNpOnR5cGU9IkZ1bmN0aW9uRGVmIj4KICAgICAgICAgPGV4cHJlc3Npb24gbG9jYXRvcj0iMjAzOjUyLTIwMzo2MiIgcGF0aD0idmFsdWUiIHhzaTp0eXBlPSJQcm9wZXJ0eSI+CiAgICAgICAgICAgIDxzb3VyY2UgbG9jYXRvcj0iMjAzOjUyLTIwMzo1NiIgbmFtZT0idmFsdWUiIHhzaTp0eXBlPSJPcGVyYW5kUmVmIi8+CiAgICAgICAgIDwvZXhwcmVzc2lvbj4KICAgICAgICAgPG9wZXJhbmQgbmFtZT0idmFsdWUiPgogICAgICAgICAgICA8b3BlcmFuZFR5cGVTcGVjaWZpZXIgbG9jYXRvcj0iMjAzOjMyLTIwMzo0OCIgbmFtZT0iZmhpcjpDb21wb3NpdGlvblN0YXR1cyIgeHNpOnR5cGU9Ik5hbWVkVHlwZVNwZWNpZmllciIvPgogICAgICAgICA8L29wZXJhbmQ+CiAgICAgIDwvZGVmPgogICAgICA8ZGVmIGxvY2F0b3I9IjIwNDoxLTIwNDo2NiIgbmFtZT0iVG9TdHJpbmciIGNvbnRleHQ9IlBhdGllbnQiIGFjY2Vzc0xldmVsPSJQdWJsaWMiIHhzaTp0eXBlPSJGdW5jdGlvbkRlZiI+CiAgICAgICAgIDxleHByZXNzaW9uIGxvY2F0b3I9IjIwNDo1Ni0yMDQ6NjYiIHBhdGg9InZhbHVlIiB4c2k6dHlwZT0iUHJvcGVydHkiPgogICAgICAgICAgICA8c291cmNlIGxvY2F0b3I9IjIwNDo1Ni0yMDQ6NjAiIG5hbWU9InZhbHVlIiB4c2k6dHlwZT0iT3BlcmFuZFJlZiIvPgogICAgICAgICA8L2V4cHJlc3Npb24+CiAgICAgICAgIDxvcGVyYW5kIG5hbWU9InZhbHVlIj4KICAgICAgICAgICAgPG9wZXJhbmRUeXBlU3BlY2lmaWVyIGxvY2F0b3I9IjIwNDozMi0yMDQ6NTIiIG5hbWU9ImZoaXI6Q29uY2VwdE1hcEVxdWl2YWxlbmNlIiB4c2k6dHlwZT0iTmFtZWRUeXBlU3BlY2lmaWVyIi8+CiAgICAgICAgIDwvb3BlcmFuZD4KICAgICAgPC9kZWY+CiAgICAgIDxkZWYgbG9jYXRvcj0iMjA1OjEtMjA1OjcyIiBuYW1lPSJUb1N0cmluZyIgY29udGV4dD0iUGF0aWVudCIgYWNjZXNzTGV2ZWw9IlB1YmxpYyIgeHNpOnR5cGU9IkZ1bmN0aW9uRGVmIj4KICAgICAgICAgPGV4cHJlc3Npb24gbG9jYXRvcj0iMjA1OjYyLTIwNTo3MiIgcGF0aD0idmFsdWUiIHhzaTp0eXBlPSJQcm9wZXJ0eSI+CiAgICAgICAgICAgIDxzb3VyY2UgbG9jYXRvcj0iMjA1OjYyLTIwNTo2NiIgbmFtZT0idmFsdWUiIHhzaTp0eXBlPSJPcGVyYW5kUmVmIi8+CiAgICAgICAgIDwvZXhwcmVzc2lvbj4KICAgICAgICAgPG9wZXJhbmQgbmFtZT0idmFsdWUiPgogICAgICAgICAgICA8b3BlcmFuZFR5cGVTcGVjaWZpZXIgbG9jYXRvcj0iMjA1OjMyLTIwNTo1OCIgbmFtZT0iZmhpcjpDb25jZXB0TWFwR3JvdXBVbm1hcHBlZE1vZGUiIHhzaTp0eXBlPSJOYW1lZFR5cGVTcGVjaWZpZXIiLz4KICAgICAgICAgPC9vcGVyYW5kPgogICAgICA8L2RlZj4KICAgICAgPGRlZiBsb2NhdG9yPSIyMDY6MS0yMDY6NjgiIG5hbWU9IlRvU3RyaW5nIiBjb250ZXh0PSJQYXRpZW50IiBhY2Nlc3NMZXZlbD0iUHVibGljIiB4c2k6dHlwZT0iRnVuY3Rpb25EZWYiPgogICAgICAgICA8ZXhwcmVzc2lvbiBsb2NhdG9yPSIyMDY6NTgtMjA2OjY4IiBwYXRoPSJ2YWx1ZSIgeHNpOnR5cGU9IlByb3BlcnR5Ij4KICAgICAgICAgICAgPHNvdXJjZSBsb2NhdG9yPSIyMDY6NTgtMjA2OjYyIiBuYW1lPSJ2YWx1ZSIgeHNpOnR5cGU9Ik9wZXJhbmRSZWYiLz4KICAgICAgICAgPC9leHByZXNzaW9uPgogICAgICAgICA8b3BlcmFuZCBuYW1lPSJ2YWx1ZSI+CiAgICAgICAgICAgIDxvcGVyYW5kVHlwZVNwZWNpZmllciBsb2NhdG9yPSIyMDY6MzItMjA2OjU0IiBuYW1lPSJmaGlyOkNvbmRpdGlvbmFsRGVsZXRlU3RhdHVzIiB4c2k6dHlwZT0iTmFtZWRUeXBlU3BlY2lmaWVyIi8+CiAgICAgICAgIDwvb3BlcmFuZD4KICAgICAgPC9kZWY+CiAgICAgIDxkZWYgbG9jYXRvcj0iMjA3OjEtMjA3OjY2IiBuYW1lPSJUb1N0cmluZyIgY29udGV4dD0iUGF0aWVudCIgYWNjZXNzTGV2ZWw9IlB1YmxpYyIgeHNpOnR5cGU9IkZ1bmN0aW9uRGVmIj4KICAgICAgICAgPGV4cHJlc3Npb24gbG9jYXRvcj0iMjA3OjU2LTIwNzo2NiIgcGF0aD0idmFsdWUiIHhzaTp0eXBlPSJQcm9wZXJ0eSI+CiAgICAgICAgICAgIDxzb3VyY2UgbG9jYXRvcj0iMjA3OjU2LTIwNzo2MCIgbmFtZT0idmFsdWUiIHhzaTp0eXBlPSJPcGVyYW5kUmVmIi8+CiAgICAgICAgIDwvZXhwcmVzc2lvbj4KICAgICAgICAgPG9wZXJhbmQgbmFtZT0idmFsdWUiPgogICAgICAgICAgICA8b3BlcmFuZFR5cGVTcGVjaWZpZXIgbG9jYXRvcj0iMjA3OjMyLTIwNzo1MiIgbmFtZT0iZmhpcjpDb25kaXRpb25hbFJlYWRTdGF0dXMiIHhzaTp0eXBlPSJOYW1lZFR5cGVTcGVjaWZpZXIiLz4KICAgICAgICAgPC9vcGVyYW5kPgogICAgICA8L2RlZj4KICAgICAgPGRlZiBsb2NhdG9yPSIyMDg6MS0yMDg6NjMiIG5hbWU9IlRvU3RyaW5nIiBjb250ZXh0PSJQYXRpZW50IiBhY2Nlc3NMZXZlbD0iUHVibGljIiB4c2k6dHlwZT0iRnVuY3Rpb25EZWYiPgogICAgICAgICA8ZXhwcmVzc2lvbiBsb2NhdG9yPSIyMDg6NTMtMjA4OjYzIiBwYXRoPSJ2YWx1ZSIgeHNpOnR5cGU9IlByb3BlcnR5Ij4KICAgICAgICAgICAgPHNvdXJjZSBsb2NhdG9yPSIyMDg6NTMtMjA4OjU3IiBuYW1lPSJ2YWx1ZSIgeHNpOnR5cGU9Ik9wZXJhbmRSZWYiLz4KICAgICAgICAgPC9leHByZXNzaW9uPgogICAgICAgICA8b3BlcmFuZCBuYW1lPSJ2YWx1ZSI+CiAgICAgICAgICAgIDxvcGVyYW5kVHlwZVNwZWNpZmllciBsb2NhdG9yPSIyMDg6MzItMjA4OjQ5IiBuYW1lPSJmaGlyOkNvbnNlbnREYXRhTWVhbmluZyIgeHNpOnR5cGU9Ik5hbWVkVHlwZVNwZWNpZmllciIvPgogICAgICAgICA8L29wZXJhbmQ+CiAgICAgIDwvZGVmPgogICAgICA8ZGVmIGxvY2F0b3I9IjIwOToxLTIwOTo2NSIgbmFtZT0iVG9TdHJpbmciIGNvbnRleHQ9IlBhdGllbnQiIGFjY2Vzc0xldmVsPSJQdWJsaWMiIHhzaTp0eXBlPSJGdW5jdGlvbkRlZiI+CiAgICAgICAgIDxleHByZXNzaW9uIGxvY2F0b3I9IjIwOTo1NS0yMDk6NjUiIHBhdGg9InZhbHVlIiB4c2k6dHlwZT0iUHJvcGVydHkiPgogICAgICAgICAgICA8c291cmNlIGxvY2F0b3I9IjIwOTo1NS0yMDk6NTkiIG5hbWU9InZhbHVlIiB4c2k6dHlwZT0iT3BlcmFuZFJlZiIvPgogICAgICAgICA8L2V4cHJlc3Npb24+CiAgICAgICAgIDxvcGVyYW5kIG5hbWU9InZhbHVlIj4KICAgICAgICAgICAgPG9wZXJhbmRUeXBlU3BlY2lmaWVyIGxvY2F0b3I9IjIwOTozMi0yMDk6NTEiIG5hbWU9ImZoaXI6Q29uc2VudFByb3Zpc2lvblR5cGUiIHhzaTp0eXBlPSJOYW1lZFR5cGVTcGVjaWZpZXIiLz4KICAgICAgICAgPC9vcGVyYW5kPgogICAgICA8L2RlZj4KICAgICAgPGRlZiBsb2NhdG9yPSIyMTA6MS0yMTA6NTciIG5hbWU9IlRvU3RyaW5nIiBjb250ZXh0PSJQYXRpZW50IiBhY2Nlc3NMZXZlbD0iUHVibGljIiB4c2k6dHlwZT0iRnVuY3Rpb25EZWYiPgogICAgICAgICA8ZXhwcmVzc2lvbiBsb2NhdG9yPSIyMTA6NDctMjEwOjU3IiBwYXRoPSJ2YWx1ZSIgeHNpOnR5cGU9IlByb3BlcnR5Ij4KICAgICAgICAgICAgPHNvdXJjZSBsb2NhdG9yPSIyMTA6NDctMjEwOjUxIiBuYW1lPSJ2YWx1ZSIgeHNpOnR5cGU9Ik9wZXJhbmRSZWYiLz4KICAgICAgICAgPC9leHByZXNzaW9uPgogICAgICAgICA8b3BlcmFuZCBuYW1lPSJ2YWx1ZSI+CiAgICAgICAgICAgIDxvcGVyYW5kVHlwZVNwZWNpZmllciBsb2NhdG9yPSIyMTA6MzItMjEwOjQzIiBuYW1lPSJmaGlyOkNvbnNlbnRTdGF0ZSIgeHNpOnR5cGU9Ik5hbWVkVHlwZVNwZWNpZmllciIvPgogICAgICAgICA8L29wZXJhbmQ+CiAgICAgIDwvZGVmPgogICAgICA8ZGVmIGxvY2F0b3I9IjIxMToxLTIxMTo2MyIgbmFtZT0iVG9TdHJpbmciIGNvbnRleHQ9IlBhdGllbnQiIGFjY2Vzc0xldmVsPSJQdWJsaWMiIHhzaTp0eXBlPSJGdW5jdGlvbkRlZiI+CiAgICAgICAgIDxleHByZXNzaW9uIGxvY2F0b3I9IjIxMTo1My0yMTE6NjMiIHBhdGg9InZhbHVlIiB4c2k6dHlwZT0iUHJvcGVydHkiPgogICAgICAgICAgICA8c291cmNlIGxvY2F0b3I9IjIxMTo1My0yMTE6NTciIG5hbWU9InZhbHVlIiB4c2k6dHlwZT0iT3BlcmFuZFJlZiIvPgogICAgICAgICA8L2V4cHJlc3Npb24+CiAgICAgICAgIDxvcGVyYW5kIG5hbWU9InZhbHVlIj4KICAgICAgICAgICAgPG9wZXJhbmRUeXBlU3BlY2lmaWVyIGxvY2F0b3I9IjIxMTozMi0yMTE6NDkiIG5hbWU9ImZoaXI6Q29uc3RyYWludFNldmVyaXR5IiB4c2k6dHlwZT0iTmFtZWRUeXBlU3BlY2lmaWVyIi8+CiAgICAgICAgIDwvb3BlcmFuZD4KICAgICAgPC9kZWY+CiAgICAgIDxkZWYgbG9jYXRvcj0iMjEyOjEtMjEyOjYzIiBuYW1lPSJUb1N0cmluZyIgY29udGV4dD0iUGF0aWVudCIgYWNjZXNzTGV2ZWw9IlB1YmxpYyIgeHNpOnR5cGU9IkZ1bmN0aW9uRGVmIj4KICAgICAgICAgPGV4cHJlc3Npb24gbG9jYXRvcj0iMjEyOjUzLTIxMjo2MyIgcGF0aD0idmFsdWUiIHhzaTp0eXBlPSJQcm9wZXJ0eSI+CiAgICAgICAgICAgIDxzb3VyY2UgbG9jYXRvcj0iMjEyOjUzLTIxMjo1NyIgbmFtZT0idmFsdWUiIHhzaTp0eXBlPSJPcGVyYW5kUmVmIi8+CiAgICAgICAgIDwvZXhwcmVzc2lvbj4KICAgICAgICAgPG9wZXJhbmQgbmFtZT0idmFsdWUiPgogICAgICAgICAgICA8b3BlcmFuZFR5cGVTcGVjaWZpZXIgbG9jYXRvcj0iMjEyOjMyLTIxMjo0OSIgbmFtZT0iZmhpcjpDb250YWN0UG9pbnRTeXN0ZW0iIHhzaTp0eXBlPSJOYW1lZFR5cGVTcGVjaWZpZXIiLz4KICAgICAgICAgPC9vcGVyYW5kPgogICAgICA8L2RlZj4KICAgICAgPGRlZiBsb2NhdG9yPSIyMTM6MS0yMTM6NjAiIG5hbWU9IlRvU3RyaW5nIiBjb250ZXh0PSJQYXRpZW50IiBhY2Nlc3NMZXZlbD0iUHVibGljIiB4c2k6dHlwZT0iRnVuY3Rpb25EZWYiPgogICAgICAgICA8ZXhwcmVzc2lvbiBsb2NhdG9yPSIyMTM6NTAtMjEzOjYwIiBwYXRoPSJ2YWx1ZSIgeHNpOnR5cGU9IlByb3BlcnR5Ij4KICAgICAgICAgICAgPHNvdXJjZSBsb2NhdG9yPSIyMTM6NTAtMjEzOjU0IiBuYW1lPSJ2YWx1ZSIgeHNpOnR5cGU9Ik9wZXJhbmRSZWYiLz4KICAgICAgICAgPC9leHByZXNzaW9uPgogICAgICAgICA8b3BlcmFuZCBuYW1lPSJ2YWx1ZSI+CiAgICAgICAgICAgIDxvcGVyYW5kVHlwZVNwZWNpZmllciBsb2NhdG9yPSIyMTM6MzItMjEzOjQ2IiBuYW1lPSJmaGlyOkNvbnRhY3RQb2ludFVzZSIgeHNpOnR5cGU9Ik5hbWVkVHlwZVNwZWNpZmllciIvPgogICAgICAgICA8L29wZXJhbmQ+CiAgICAgIDwvZGVmPgogICAgICA8ZGVmIGxvY2F0b3I9IjIxNDoxLTIxNDo3MCIgbmFtZT0iVG9TdHJpbmciIGNvbnRleHQ9IlBhdGllbnQiIGFjY2Vzc0xldmVsPSJQdWJsaWMiIHhzaTp0eXBlPSJGdW5jdGlvbkRlZiI+CiAgICAgICAgIDxleHByZXNzaW9uIGxvY2F0b3I9IjIxNDo2MC0yMTQ6NzAiIHBhdGg9InZhbHVlIiB4c2k6dHlwZT0iUHJvcGVydHkiPgogICAgICAgICAgICA8c291cmNlIGxvY2F0b3I9IjIxNDo2MC0yMTQ6NjQiIG5hbWU9InZhbHVlIiB4c2k6dHlwZT0iT3BlcmFuZFJlZiIvPgogICAgICAgICA8L2V4cHJlc3Npb24+CiAgICAgICAgIDxvcGVyYW5kIG5hbWU9InZhbHVlIj4KICAgICAgICAgICAgPG9wZXJhbmRUeXBlU3BlY2lmaWVyIGxvY2F0b3I9IjIxNDozMi0yMTQ6NTYiIG5hbWU9ImZoaXI6Q29udHJhY3RQdWJsaWNhdGlvblN0YXR1cyIgeHNpOnR5cGU9Ik5hbWVkVHlwZVNwZWNpZmllciIvPgogICAgICAgICA8L29wZXJhbmQ+CiAgICAgIDwvZGVmPgogICAgICA8ZGVmIGxvY2F0b3I9IjIxNToxLTIxNTo1OSIgbmFtZT0iVG9TdHJpbmciIGNvbnRleHQ9IlBhdGllbnQiIGFjY2Vzc0xldmVsPSJQdWJsaWMiIHhzaTp0eXBlPSJGdW5jdGlvbkRlZiI+CiAgICAgICAgIDxleHByZXNzaW9uIGxvY2F0b3I9IjIxNTo0OS0yMTU6NTkiIHBhdGg9InZhbHVlIiB4c2k6dHlwZT0iUHJvcGVydHkiPgogICAgICAgICAgICA8c291cmNlIGxvY2F0b3I9IjIxNTo0OS0yMTU6NTMiIG5hbWU9InZhbHVlIiB4c2k6dHlwZT0iT3BlcmFuZFJlZiIvPgogICAgICAgICA8L2V4cHJlc3Npb24+CiAgICAgICAgIDxvcGVyYW5kIG5hbWU9InZhbHVlIj4KICAgICAgICAgICAgPG9wZXJhbmRUeXBlU3BlY2lmaWVyIGxvY2F0b3I9IjIxNTozMi0yMTU6NDUiIG5hbWU9ImZoaXI6Q29udHJhY3RTdGF0dXMiIHhzaTp0eXBlPSJOYW1lZFR5cGVTcGVjaWZpZXIiLz4KICAgICAgICAgPC9vcGVyYW5kPgogICAgICA8L2RlZj4KICAgICAgPGRlZiBsb2NhdG9yPSIyMTY6MS0yMTY6NjAiIG5hbWU9IlRvU3RyaW5nIiBjb250ZXh0PSJQYXRpZW50IiBhY2Nlc3NMZXZlbD0iUHVibGljIiB4c2k6dHlwZT0iRnVuY3Rpb25EZWYiPgogICAgICAgICA8ZXhwcmVzc2lvbiBsb2NhdG9yPSIyMTY6NTAtMjE2OjYwIiBwYXRoPSJ2YWx1ZSIgeHNpOnR5cGU9IlByb3BlcnR5Ij4KICAgICAgICAgICAgPHNvdXJjZSBsb2NhdG9yPSIyMTY6NTAtMjE2OjU0IiBuYW1lPSJ2YWx1ZSIgeHNpOnR5cGU9Ik9wZXJhbmRSZWYiLz4KICAgICAgICAgPC9leHByZXNzaW9uPgogICAgICAgICA8b3BlcmFuZCBuYW1lPSJ2YWx1ZSI+CiAgICAgICAgICAgIDxvcGVyYW5kVHlwZVNwZWNpZmllciBsb2NhdG9yPSIyMTY6MzItMjE2OjQ2IiBuYW1lPSJmaGlyOkNvbnRyaWJ1dG9yVHlwZSIgeHNpOnR5cGU9Ik5hbWVkVHlwZVNwZWNpZmllciIvPgogICAgICAgICA8L29wZXJhbmQ+CiAgICAgIDwvZGVmPgogICAgICA8ZGVmIGxvY2F0b3I9IjIxNzoxLTIxNzo1OSIgbmFtZT0iVG9TdHJpbmciIGNvbnRleHQ9IlBhdGllbnQiIGFjY2Vzc0xldmVsPSJQdWJsaWMiIHhzaTp0eXBlPSJGdW5jdGlvbkRlZiI+CiAgICAgICAgIDxleHByZXNzaW9uIGxvY2F0b3I9IjIxNzo0OS0yMTc6NTkiIHBhdGg9InZhbHVlIiB4c2k6dHlwZT0iUHJvcGVydHkiPgogICAgICAgICAgICA8c291cmNlIGxvY2F0b3I9IjIxNzo0OS0yMTc6NTMiIG5hbWU9InZhbHVlIiB4c2k6dHlwZT0iT3BlcmFuZFJlZiIvPgogICAgICAgICA8L2V4cHJlc3Npb24+CiAgICAgICAgIDxvcGVyYW5kIG5hbWU9InZhbHVlIj4KICAgICAgICAgICAgPG9wZXJhbmRUeXBlU3BlY2lmaWVyIGxvY2F0b3I9IjIxNzozMi0yMTc6NDUiIG5hbWU9ImZoaXI6Q292ZXJhZ2VTdGF0dXMiIHhzaTp0eXBlPSJOYW1lZFR5cGVTcGVjaWZpZXIiLz4KICAgICAgICAgPC9vcGVyYW5kPgogICAgICA8L2RlZj4KICAgICAgPGRlZiBsb2NhdG9yPSIyMTg6MS0yMTg6NTciIG5hbWU9IlRvU3RyaW5nIiBjb250ZXh0PSJQYXRpZW50IiBhY2Nlc3NMZXZlbD0iUHVibGljIiB4c2k6dHlwZT0iRnVuY3Rpb25EZWYiPgogICAgICAgICA8ZXhwcmVzc2lvbiBsb2NhdG9yPSIyMTg6NDctMjE4OjU3IiBwYXRoPSJ2YWx1ZSIgeHNpOnR5cGU9IlByb3BlcnR5Ij4KICAgICAgICAgICAgPHNvdXJjZSBsb2NhdG9yPSIyMTg6NDctMjE4OjUxIiBuYW1lPSJ2YWx1ZSIgeHNpOnR5cGU9Ik9wZXJhbmRSZWYiLz4KICAgICAgICAgPC9leHByZXNzaW9uPgogICAgICAgICA8b3BlcmFuZCBuYW1lPSJ2YWx1ZSI+CiAgICAgICAgICAgIDxvcGVyYW5kVHlwZVNwZWNpZmllciBsb2NhdG9yPSIyMTg6MzItMjE4OjQzIiBuYW1lPSJmaGlyOkN1cnJlbmN5Q29kZSIgeHNpOnR5cGU9Ik5hbWVkVHlwZVNwZWNpZmllciIvPgogICAgICAgICA8L29wZXJhbmQ+CiAgICAgIDwvZGVmPgogICAgICA8ZGVmIGxvY2F0b3I9IjIxOToxLTIxOTo1NCIgbmFtZT0iVG9TdHJpbmciIGNvbnRleHQ9IlBhdGllbnQiIGFjY2Vzc0xldmVsPSJQdWJsaWMiIHhzaTp0eXBlPSJGdW5jdGlvbkRlZiI+CiAgICAgICAgIDxleHByZXNzaW9uIGxvY2F0b3I9IjIxOTo0NC0yMTk6NTQiIHBhdGg9InZhbHVlIiB4c2k6dHlwZT0iUHJvcGVydHkiPgogICAgICAgICAgICA8c291cmNlIGxvY2F0b3I9IjIxOTo0NC0yMTk6NDgiIG5hbWU9InZhbHVlIiB4c2k6dHlwZT0iT3BlcmFuZFJlZiIvPgogICAgICAgICA8L2V4cHJlc3Npb24+CiAgICAgICAgIDxvcGVyYW5kIG5hbWU9InZhbHVlIj4KICAgICAgICAgICAgPG9wZXJhbmRUeXBlU3BlY2lmaWVyIGxvY2F0b3I9IjIxOTozMi0yMTk6NDAiIG5hbWU9ImZoaXI6RGF5T2ZXZWVrIiB4c2k6dHlwZT0iTmFtZWRUeXBlU3BlY2lmaWVyIi8+CiAgICAgICAgIDwvb3BlcmFuZD4KICAgICAgPC9kZWY+CiAgICAgIDxkZWYgbG9jYXRvcj0iMjIwOjEtMjIwOjU1IiBuYW1lPSJUb1N0cmluZyIgY29udGV4dD0iUGF0aWVudCIgYWNjZXNzTGV2ZWw9IlB1YmxpYyIgeHNpOnR5cGU9IkZ1bmN0aW9uRGVmIj4KICAgICAgICAgPGV4cHJlc3Npb24gbG9jYXRvcj0iMjIwOjQ1LTIyMDo1NSIgcGF0aD0idmFsdWUiIHhzaTp0eXBlPSJQcm9wZXJ0eSI+CiAgICAgICAgICAgIDxzb3VyY2UgbG9jYXRvcj0iMjIwOjQ1LTIyMDo0OSIgbmFtZT0idmFsdWUiIHhzaTp0eXBlPSJPcGVyYW5kUmVmIi8+CiAgICAgICAgIDwvZXhwcmVzc2lvbj4KICAgICAgICAgPG9wZXJhbmQgbmFtZT0idmFsdWUiPgogICAgICAgICAgICA8b3BlcmFuZFR5cGVTcGVjaWZpZXIgbG9jYXRvcj0iMjIwOjMyLTIyMDo0MSIgbmFtZT0iZmhpcjpEYXlzT2ZXZWVrIiB4c2k6dHlwZT0iTmFtZWRUeXBlU3BlY2lmaWVyIi8+CiAgICAgICAgIDwvb3BlcmFuZD4KICAgICAgPC9kZWY+CiAgICAgIDxkZWYgbG9jYXRvcj0iMjIxOjEtMjIxOjY2IiBuYW1lPSJUb1N0cmluZyIgY29udGV4dD0iUGF0aWVudCIgYWNjZXNzTGV2ZWw9IlB1YmxpYyIgeHNpOnR5cGU9IkZ1bmN0aW9uRGVmIj4KICAgICAgICAgPGV4cHJlc3Npb24gbG9jYXRvcj0iMjIxOjU2LTIyMTo2NiIgcGF0aD0idmFsdWUiIHhzaTp0eXBlPSJQcm9wZXJ0eSI+CiAgICAgICAgICAgIDxzb3VyY2UgbG9jYXRvcj0iMjIxOjU2LTIyMTo2MCIgbmFtZT0idmFsdWUiIHhzaTp0eXBlPSJPcGVyYW5kUmVmIi8+CiAgICAgICAgIDwvZXhwcmVzc2lvbj4KICAgICAgICAgPG9wZXJhbmQgbmFtZT0idmFsdWUiPgogICAgICAgICAgICA8b3BlcmFuZFR5cGVTcGVjaWZpZXIgbG9jYXRvcj0iMjIxOjMyLTIyMTo1MiIgbmFtZT0iZmhpcjpEZXRlY3RlZElzc3VlU2V2ZXJpdHkiIHhzaTp0eXBlPSJOYW1lZFR5cGVTcGVjaWZpZXIiLz4KICAgICAgICAgPC9vcGVyYW5kPgogICAgICA8L2RlZj4KICAgICAgPGRlZiBsb2NhdG9yPSIyMjI6MS0yMjI6NjQiIG5hbWU9IlRvU3RyaW5nIiBjb250ZXh0PSJQYXRpZW50IiBhY2Nlc3NMZXZlbD0iUHVibGljIiB4c2k6dHlwZT0iRnVuY3Rpb25EZWYiPgogICAgICAgICA8ZXhwcmVzc2lvbiBsb2NhdG9yPSIyMjI6NTQtMjIyOjY0IiBwYXRoPSJ2YWx1ZSIgeHNpOnR5cGU9IlByb3BlcnR5Ij4KICAgICAgICAgICAgPHNvdXJjZSBsb2NhdG9yPSIyMjI6NTQtMjIyOjU4IiBuYW1lPSJ2YWx1ZSIgeHNpOnR5cGU9Ik9wZXJhbmRSZWYiLz4KICAgICAgICAgPC9leHByZXNzaW9uPgogICAgICAgICA8b3BlcmFuZCBuYW1lPSJ2YWx1ZSI+CiAgICAgICAgICAgIDxvcGVyYW5kVHlwZVNwZWNpZmllciBsb2NhdG9yPSIyMjI6MzItMjIyOjUwIiBuYW1lPSJmaGlyOkRldGVjdGVkSXNzdWVTdGF0dXMiIHhzaTp0eXBlPSJOYW1lZFR5cGVTcGVjaWZpZXIiLz4KICAgICAgICAgPC9vcGVyYW5kPgogICAgICA8L2RlZj4KICAgICAgPGRlZiBsb2NhdG9yPSIyMjM6MS0yMjM6NzMiIG5hbWU9IlRvU3RyaW5nIiBjb250ZXh0PSJQYXRpZW50IiBhY2Nlc3NMZXZlbD0iUHVibGljIiB4c2k6dHlwZT0iRnVuY3Rpb25EZWYiPgogICAgICAgICA8ZXhwcmVzc2lvbiBsb2NhdG9yPSIyMjM6NjMtMjIzOjczIiBwYXRoPSJ2YWx1ZSIgeHNpOnR5cGU9IlByb3BlcnR5Ij4KICAgICAgICAgICAgPHNvdXJjZSBsb2NhdG9yPSIyMjM6NjMtMjIzOjY3IiBuYW1lPSJ2YWx1ZSIgeHNpOnR5cGU9Ik9wZXJhbmRSZWYiLz4KICAgICAgICAgPC9leHByZXNzaW9uPgogICAgICAgICA8b3BlcmFuZCBuYW1lPSJ2YWx1ZSI+CiAgICAgICAgICAgIDxvcGVyYW5kVHlwZVNwZWNpZmllciBsb2NhdG9yPSIyMjM6MzItMjIzOjU5IiBuYW1lPSJmaGlyOkRldmljZU1ldHJpY0NhbGlicmF0aW9uU3RhdGUiIHhzaTp0eXBlPSJOYW1lZFR5cGVTcGVjaWZpZXIiLz4KICAgICAgICAgPC9vcGVyYW5kPgogICAgICA8L2RlZj4KICAgICAgPGRlZiBsb2NhdG9yPSIyMjQ6MS0yMjQ6NzIiIG5hbWU9IlRvU3RyaW5nIiBjb250ZXh0PSJQYXRpZW50IiBhY2Nlc3NMZXZlbD0iUHVibGljIiB4c2k6dHlwZT0iRnVuY3Rpb25EZWYiPgogICAgICAgICA8ZXhwcmVzc2lvbiBsb2NhdG9yPSIyMjQ6NjItMjI0OjcyIiBwYXRoPSJ2YWx1ZSIgeHNpOnR5cGU9IlByb3BlcnR5Ij4KICAgICAgICAgICAgPHNvdXJjZSBsb2NhdG9yPSIyMjQ6NjItMjI0OjY2IiBuYW1lPSJ2YWx1ZSIgeHNpOnR5cGU9Ik9wZXJhbmRSZWYiLz4KICAgICAgICAgPC9leHByZXNzaW9uPgogICAgICAgICA8b3BlcmFuZCBuYW1lPSJ2YWx1ZSI+CiAgICAgICAgICAgIDxvcGVyYW5kVHlwZVNwZWNpZmllciBsb2NhdG9yPSIyMjQ6MzItMjI0OjU4IiBuYW1lPSJmaGlyOkRldmljZU1ldHJpY0NhbGlicmF0aW9uVHlwZSIgeHNpOnR5cGU9Ik5hbWVkVHlwZVNwZWNpZmllciIvPgogICAgICAgICA8L29wZXJhbmQ+CiAgICAgIDwvZGVmPgogICAgICA8ZGVmIGxvY2F0b3I9IjIyNToxLTIyNTo2NSIgbmFtZT0iVG9TdHJpbmciIGNvbnRleHQ9IlBhdGllbnQiIGFjY2Vzc0xldmVsPSJQdWJsaWMiIHhzaTp0eXBlPSJGdW5jdGlvbkRlZiI+CiAgICAgICAgIDxleHByZXNzaW9uIGxvY2F0b3I9IjIyNTo1NS0yMjU6NjUiIHBhdGg9InZhbHVlIiB4c2k6dHlwZT0iUHJvcGVydHkiPgogICAgICAgICAgICA8c291cmNlIGxvY2F0b3I9IjIyNTo1NS0yMjU6NTkiIG5hbWU9InZhbHVlIiB4c2k6dHlwZT0iT3BlcmFuZFJlZiIvPgogICAgICAgICA8L2V4cHJlc3Npb24+CiAgICAgICAgIDxvcGVyYW5kIG5hbWU9InZhbHVlIj4KICAgICAgICAgICAgPG9wZXJhbmRUeXBlU3BlY2lmaWVyIGxvY2F0b3I9IjIyNTozMi0yMjU6NTEiIG5hbWU9ImZoaXI6RGV2aWNlTWV0cmljQ2F0ZWdvcnkiIHhzaTp0eXBlPSJOYW1lZFR5cGVTcGVjaWZpZXIiLz4KICAgICAgICAgPC9vcGVyYW5kPgogICAgICA8L2RlZj4KICAgICAgPGRlZiBsb2NhdG9yPSIyMjY6MS0yMjY6NjIiIG5hbWU9IlRvU3RyaW5nIiBjb250ZXh0PSJQYXRpZW50IiBhY2Nlc3NMZXZlbD0iUHVibGljIiB4c2k6dHlwZT0iRnVuY3Rpb25EZWYiPgogICAgICAgICA8ZXhwcmVzc2lvbiBsb2NhdG9yPSIyMjY6NTItMjI2OjYyIiBwYXRoPSJ2YWx1ZSIgeHNpOnR5cGU9IlByb3BlcnR5Ij4KICAgICAgICAgICAgPHNvdXJjZSBsb2NhdG9yPSIyMjY6NTItMjI2OjU2IiBuYW1lPSJ2YWx1ZSIgeHNpOnR5cGU9Ik9wZXJhbmRSZWYiLz4KICAgICAgICAgPC9leHByZXNzaW9uPgogICAgICAgICA8b3BlcmFuZCBuYW1lPSJ2YWx1ZSI+CiAgICAgICAgICAgIDxvcGVyYW5kVHlwZVNwZWNpZmllciBsb2NhdG9yPSIyMjY6MzItMjI2OjQ4IiBuYW1lPSJmaGlyOkRldmljZU1ldHJpY0NvbG9yIiB4c2k6dHlwZT0iTmFtZWRUeXBlU3BlY2lmaWVyIi8+CiAgICAgICAgIDwvb3BlcmFuZD4KICAgICAgPC9kZWY+CiAgICAgIDxkZWYgbG9jYXRvcj0iMjI3OjEtMjI3Ojc0IiBuYW1lPSJUb1N0cmluZyIgY29udGV4dD0iUGF0aWVudCIgYWNjZXNzTGV2ZWw9IlB1YmxpYyIgeHNpOnR5cGU9IkZ1bmN0aW9uRGVmIj4KICAgICAgICAgPGV4cHJlc3Npb24gbG9jYXRvcj0iMjI3OjY0LTIyNzo3NCIgcGF0aD0idmFsdWUiIHhzaTp0eXBlPSJQcm9wZXJ0eSI+CiAgICAgICAgICAgIDxzb3VyY2UgbG9jYXRvcj0iMjI3OjY0LTIyNzo2OCIgbmFtZT0idmFsdWUiIHhzaTp0eXBlPSJPcGVyYW5kUmVmIi8+CiAgICAgICAgIDwvZXhwcmVzc2lvbj4KICAgICAgICAgPG9wZXJhbmQgbmFtZT0idmFsdWUiPgogICAgICAgICAgICA8b3BlcmFuZFR5cGVTcGVjaWZpZXIgbG9jYXRvcj0iMjI3OjMyLTIyNzo2MCIgbmFtZT0iZmhpcjpEZXZpY2VNZXRyaWNPcGVyYXRpb25hbFN0YXR1cyIgeHNpOnR5cGU9Ik5hbWVkVHlwZVNwZWNpZmllciIvPgogICAgICAgICA8L29wZXJhbmQ+CiAgICAgIDwvZGVmPgogICAgICA8ZGVmIGxvY2F0b3I9IjIyODoxLTIyODo1OSIgbmFtZT0iVG9TdHJpbmciIGNvbnRleHQ9IlBhdGllbnQiIGFjY2Vzc0xldmVsPSJQdWJsaWMiIHhzaTp0eXBlPSJGdW5jdGlvbkRlZiI+CiAgICAgICAgIDxleHByZXNzaW9uIGxvY2F0b3I9IjIyODo0OS0yMjg6NTkiIHBhdGg9InZhbHVlIiB4c2k6dHlwZT0iUHJvcGVydHkiPgogICAgICAgICAgICA8c291cmNlIGxvY2F0b3I9IjIyODo0OS0yMjg6NTMiIG5hbWU9InZhbHVlIiB4c2k6dHlwZT0iT3BlcmFuZFJlZiIvPgogICAgICAgICA8L2V4cHJlc3Npb24+CiAgICAgICAgIDxvcGVyYW5kIG5hbWU9InZhbHVlIj4KICAgICAgICAgICAgPG9wZXJhbmRUeXBlU3BlY2lmaWVyIGxvY2F0b3I9IjIyODozMi0yMjg6NDUiIG5hbWU9ImZoaXI6RGV2aWNlTmFtZVR5cGUiIHhzaTp0eXBlPSJOYW1lZFR5cGVTcGVjaWZpZXIiLz4KICAgICAgICAgPC9vcGVyYW5kPgogICAgICA8L2RlZj4KICAgICAgPGRlZiBsb2NhdG9yPSIyMjk6MS0yMjk6NjQiIG5hbWU9IlRvU3RyaW5nIiBjb250ZXh0PSJQYXRpZW50IiBhY2Nlc3NMZXZlbD0iUHVibGljIiB4c2k6dHlwZT0iRnVuY3Rpb25EZWYiPgogICAgICAgICA8ZXhwcmVzc2lvbiBsb2NhdG9yPSIyMjk6NTQtMjI5OjY0IiBwYXRoPSJ2YWx1ZSIgeHNpOnR5cGU9IlByb3BlcnR5Ij4KICAgICAgICAgICAgPHNvdXJjZSBsb2NhdG9yPSIyMjk6NTQtMjI5OjU4IiBuYW1lPSJ2YWx1ZSIgeHNpOnR5cGU9Ik9wZXJhbmRSZWYiLz4KICAgICAgICAgPC9leHByZXNzaW9uPgogICAgICAgICA8b3BlcmFuZCBuYW1lPSJ2YWx1ZSI+CiAgICAgICAgICAgIDxvcGVyYW5kVHlwZVNwZWNpZmllciBsb2NhdG9yPSIyMjk6MzItMjI5OjUwIiBuYW1lPSJmaGlyOkRldmljZVJlcXVlc3RTdGF0dXMiIHhzaTp0eXBlPSJOYW1lZFR5cGVTcGVjaWZpZXIiLz4KICAgICAgICAgPC9vcGVyYW5kPgogICAgICA8L2RlZj4KICAgICAgPGRlZiBsb2NhdG9yPSIyMzA6MS0yMzA6NjkiIG5hbWU9IlRvU3RyaW5nIiBjb250ZXh0PSJQYXRpZW50IiBhY2Nlc3NMZXZlbD0iUHVibGljIiB4c2k6dHlwZT0iRnVuY3Rpb25EZWYiPgogICAgICAgICA8ZXhwcmVzc2lvbiBsb2NhdG9yPSIyMzA6NTktMjMwOjY5IiBwYXRoPSJ2YWx1ZSIgeHNpOnR5cGU9IlByb3BlcnR5Ij4KICAgICAgICAgICAgPHNvdXJjZSBsb2NhdG9yPSIyMzA6NTktMjMwOjYzIiBuYW1lPSJ2YWx1ZSIgeHNpOnR5cGU9Ik9wZXJhbmRSZWYiLz4KICAgICAgICAgPC9leHByZXNzaW9uPgogICAgICAgICA8b3BlcmFuZCBuYW1lPSJ2YWx1ZSI+CiAgICAgICAgICAgIDxvcGVyYW5kVHlwZVNwZWNpZmllciBsb2NhdG9yPSIyMzA6MzItMjMwOjU1IiBuYW1lPSJmaGlyOkRldmljZVVzZVN0YXRlbWVudFN0YXR1cyIgeHNpOnR5cGU9Ik5hbWVkVHlwZVNwZWNpZmllciIvPgogICAgICAgICA8L29wZXJhbmQ+CiAgICAgIDwvZGVmPgogICAgICA8ZGVmIGxvY2F0b3I9IjIzMToxLTIzMTo2NyIgbmFtZT0iVG9TdHJpbmciIGNvbnRleHQ9IlBhdGllbnQiIGFjY2Vzc0xldmVsPSJQdWJsaWMiIHhzaTp0eXBlPSJGdW5jdGlvbkRlZiI+CiAgICAgICAgIDxleHByZXNzaW9uIGxvY2F0b3I9IjIzMTo1Ny0yMzE6NjciIHBhdGg9InZhbHVlIiB4c2k6dHlwZT0iUHJvcGVydHkiPgogICAgICAgICAgICA8c291cmNlIGxvY2F0b3I9IjIzMTo1Ny0yMzE6NjEiIG5hbWU9InZhbHVlIiB4c2k6dHlwZT0iT3BlcmFuZFJlZiIvPgogICAgICAgICA8L2V4cHJlc3Npb24+CiAgICAgICAgIDxvcGVyYW5kIG5hbWU9InZhbHVlIj4KICAgICAgICAgICAgPG9wZXJhbmRUeXBlU3BlY2lmaWVyIGxvY2F0b3I9IjIzMTozMi0yMzE6NTMiIG5hbWU9ImZoaXI6RGlhZ25vc3RpY1JlcG9ydFN0YXR1cyIgeHNpOnR5cGU9Ik5hbWVkVHlwZVNwZWNpZmllciIvPgogICAgICAgICA8L29wZXJhbmQ+CiAgICAgIDwvZGVmPgogICAgICA8ZGVmIGxvY2F0b3I9IjIzMjoxLTIzMjo2MiIgbmFtZT0iVG9TdHJpbmciIGNvbnRleHQ9IlBhdGllbnQiIGFjY2Vzc0xldmVsPSJQdWJsaWMiIHhzaTp0eXBlPSJGdW5jdGlvbkRlZiI+CiAgICAgICAgIDxleHByZXNzaW9uIGxvY2F0b3I9IjIzMjo1Mi0yMzI6NjIiIHBhdGg9InZhbHVlIiB4c2k6dHlwZT0iUHJvcGVydHkiPgogICAgICAgICAgICA8c291cmNlIGxvY2F0b3I9IjIzMjo1Mi0yMzI6NTYiIG5hbWU9InZhbHVlIiB4c2k6dHlwZT0iT3BlcmFuZFJlZiIvPgogICAgICAgICA8L2V4cHJlc3Npb24+CiAgICAgICAgIDxvcGVyYW5kIG5hbWU9InZhbHVlIj4KICAgICAgICAgICAgPG9wZXJhbmRUeXBlU3BlY2lmaWVyIGxvY2F0b3I9IjIzMjozMi0yMzI6NDgiIG5hbWU9ImZoaXI6RGlzY3JpbWluYXRvclR5cGUiIHhzaTp0eXBlPSJOYW1lZFR5cGVTcGVjaWZpZXIiLz4KICAgICAgICAgPC9vcGVyYW5kPgogICAgICA8L2RlZj4KICAgICAgPGRlZiBsb2NhdG9yPSIyMzM6MS0yMzM6NjgiIG5hbWU9IlRvU3RyaW5nIiBjb250ZXh0PSJQYXRpZW50IiBhY2Nlc3NMZXZlbD0iUHVibGljIiB4c2k6dHlwZT0iRnVuY3Rpb25EZWYiPgogICAgICAgICA8ZXhwcmVzc2lvbiBsb2NhdG9yPSIyMzM6NTgtMjMzOjY4IiBwYXRoPSJ2YWx1ZSIgeHNpOnR5cGU9IlByb3BlcnR5Ij4KICAgICAgICAgICAgPHNvdXJjZSBsb2NhdG9yPSIyMzM6NTgtMjMzOjYyIiBuYW1lPSJ2YWx1ZSIgeHNpOnR5cGU9Ik9wZXJhbmRSZWYiLz4KICAgICAgICAgPC9leHByZXNzaW9uPgogICAgICAgICA8b3BlcmFuZCBuYW1lPSJ2YWx1ZSI+CiAgICAgICAgICAgIDxvcGVyYW5kVHlwZVNwZWNpZmllciBsb2NhdG9yPSIyMzM6MzItMjMzOjU0IiBuYW1lPSJmaGlyOkRvY3VtZW50Q29uZmlkZW50aWFsaXR5IiB4c2k6dHlwZT0iTmFtZWRUeXBlU3BlY2lmaWVyIi8+CiAgICAgICAgIDwvb3BlcmFuZD4KICAgICAgPC9kZWY+CiAgICAgIDxkZWYgbG9jYXRvcj0iMjM0OjEtMjM0OjU3IiBuYW1lPSJUb1N0cmluZyIgY29udGV4dD0iUGF0aWVudCIgYWNjZXNzTGV2ZWw9IlB1YmxpYyIgeHNpOnR5cGU9IkZ1bmN0aW9uRGVmIj4KICAgICAgICAgPGV4cHJlc3Npb24gbG9jYXRvcj0iMjM0OjQ3LTIzNDo1NyIgcGF0aD0idmFsdWUiIHhzaTp0eXBlPSJQcm9wZXJ0eSI+CiAgICAgICAgICAgIDxzb3VyY2UgbG9jYXRvcj0iMjM0OjQ3LTIzNDo1MSIgbmFtZT0idmFsdWUiIHhzaTp0eXBlPSJPcGVyYW5kUmVmIi8+CiAgICAgICAgIDwvZXhwcmVzc2lvbj4KICAgICAgICAgPG9wZXJhbmQgbmFtZT0idmFsdWUiPgogICAgICAgICAgICA8b3BlcmFuZFR5cGVTcGVjaWZpZXIgbG9jYXRvcj0iMjM0OjMyLTIzNDo0MyIgbmFtZT0iZmhpcjpEb2N1bWVudE1vZGUiIHhzaTp0eXBlPSJOYW1lZFR5cGVTcGVjaWZpZXIiLz4KICAgICAgICAgPC9vcGVyYW5kPgogICAgICA8L2RlZj4KICAgICAgPGRlZiBsb2NhdG9yPSIyMzU6MS0yMzU6NjgiIG5hbWU9IlRvU3RyaW5nIiBjb250ZXh0PSJQYXRpZW50IiBhY2Nlc3NMZXZlbD0iUHVibGljIiB4c2k6dHlwZT0iRnVuY3Rpb25EZWYiPgogICAgICAgICA8ZXhwcmVzc2lvbiBsb2NhdG9yPSIyMzU6NTgtMjM1OjY4IiBwYXRoPSJ2YWx1ZSIgeHNpOnR5cGU9IlByb3BlcnR5Ij4KICAgICAgICAgICAgPHNvdXJjZSBsb2NhdG9yPSIyMzU6NTgtMjM1OjYyIiBuYW1lPSJ2YWx1ZSIgeHNpOnR5cGU9Ik9wZXJhbmRSZWYiLz4KICAgICAgICAgPC9leHByZXNzaW9uPgogICAgICAgICA8b3BlcmFuZCBuYW1lPSJ2YWx1ZSI+CiAgICAgICAgICAgIDxvcGVyYW5kVHlwZVNwZWNpZmllciBsb2NhdG9yPSIyMzU6MzItMjM1OjU0IiBuYW1lPSJmaGlyOkRvY3VtZW50UmVmZXJlbmNlU3RhdHVzIiB4c2k6dHlwZT0iTmFtZWRUeXBlU3BlY2lmaWVyIi8+CiAgICAgICAgIDwvb3BlcmFuZD4KICAgICAgPC9kZWY+CiAgICAgIDxkZWYgbG9jYXRvcj0iMjM2OjEtMjM2OjY5IiBuYW1lPSJUb1N0cmluZyIgY29udGV4dD0iUGF0aWVudCIgYWNjZXNzTGV2ZWw9IlB1YmxpYyIgeHNpOnR5cGU9IkZ1bmN0aW9uRGVmIj4KICAgICAgICAgPGV4cHJlc3Npb24gbG9jYXRvcj0iMjM2OjU5LTIzNjo2OSIgcGF0aD0idmFsdWUiIHhzaTp0eXBlPSJQcm9wZXJ0eSI+CiAgICAgICAgICAgIDxzb3VyY2UgbG9jYXRvcj0iMjM2OjU5LTIzNjo2MyIgbmFtZT0idmFsdWUiIHhzaTp0eXBlPSJPcGVyYW5kUmVmIi8+CiAgICAgICAgIDwvZXhwcmVzc2lvbj4KICAgICAgICAgPG9wZXJhbmQgbmFtZT0idmFsdWUiPgogICAgICAgICAgICA8b3BlcmFuZFR5cGVTcGVjaWZpZXIgbG9jYXRvcj0iMjM2OjMyLTIzNjo1NSIgbmFtZT0iZmhpcjpEb2N1bWVudFJlbGF0aW9uc2hpcFR5cGUiIHhzaTp0eXBlPSJOYW1lZFR5cGVTcGVjaWZpZXIiLz4KICAgICAgICAgPC9vcGVyYW5kPgogICAgICA8L2RlZj4KICAgICAgPGRlZiBsb2NhdG9yPSIyMzc6MS0yMzc6NzAiIG5hbWU9IlRvU3RyaW5nIiBjb250ZXh0PSJQYXRpZW50IiBhY2Nlc3NMZXZlbD0iUHVibGljIiB4c2k6dHlwZT0iRnVuY3Rpb25EZWYiPgogICAgICAgICA8ZXhwcmVzc2lvbiBsb2NhdG9yPSIyMzc6NjAtMjM3OjcwIiBwYXRoPSJ2YWx1ZSIgeHNpOnR5cGU9IlByb3BlcnR5Ij4KICAgICAgICAgICAgPHNvdXJjZSBsb2NhdG9yPSIyMzc6NjAtMjM3OjY0IiBuYW1lPSJ2YWx1ZSIgeHNpOnR5cGU9Ik9wZXJhbmRSZWYiLz4KICAgICAgICAgPC9leHByZXNzaW9uPgogICAgICAgICA8b3BlcmFuZCBuYW1lPSJ2YWx1ZSI+CiAgICAgICAgICAgIDxvcGVyYW5kVHlwZVNwZWNpZmllciBsb2NhdG9yPSIyMzc6MzItMjM3OjU2IiBuYW1lPSJmaGlyOkVsaWdpYmlsaXR5UmVxdWVzdFB1cnBvc2UiIHhzaTp0eXBlPSJOYW1lZFR5cGVTcGVjaWZpZXIiLz4KICAgICAgICAgPC9vcGVyYW5kPgogICAgICA8L2RlZj4KICAgICAgPGRlZiBsb2NhdG9yPSIyMzg6MS0yMzg6NjkiIG5hbWU9IlRvU3RyaW5nIiBjb250ZXh0PSJQYXRpZW50IiBhY2Nlc3NMZXZlbD0iUHVibGljIiB4c2k6dHlwZT0iRnVuY3Rpb25EZWYiPgogICAgICAgICA8ZXhwcmVzc2lvbiBsb2NhdG9yPSIyMzg6NTktMjM4OjY5IiBwYXRoPSJ2YWx1ZSIgeHNpOnR5cGU9IlByb3BlcnR5Ij4KICAgICAgICAgICAgPHNvdXJjZSBsb2NhdG9yPSIyMzg6NTktMjM4OjYzIiBuYW1lPSJ2YWx1ZSIgeHNpOnR5cGU9Ik9wZXJhbmRSZWYiLz4KICAgICAgICAgPC9leHByZXNzaW9uPgogICAgICAgICA8b3BlcmFuZCBuYW1lPSJ2YWx1ZSI+CiAgICAgICAgICAgIDxvcGVyYW5kVHlwZVNwZWNpZmllciBsb2NhdG9yPSIyMzg6MzItMjM4OjU1IiBuYW1lPSJmaGlyOkVsaWdpYmlsaXR5UmVxdWVzdFN0YXR1cyIgeHNpOnR5cGU9Ik5hbWVkVHlwZVNwZWNpZmllciIvPgogICAgICAgICA8L29wZXJhbmQ+CiAgICAgIDwvZGVmPgogICAgICA8ZGVmIGxvY2F0b3I9IjIzOToxLTIzOTo3MSIgbmFtZT0iVG9TdHJpbmciIGNvbnRleHQ9IlBhdGllbnQiIGFjY2Vzc0xldmVsPSJQdWJsaWMiIHhzaTp0eXBlPSJGdW5jdGlvbkRlZiI+CiAgICAgICAgIDxleHByZXNzaW9uIGxvY2F0b3I9IjIzOTo2MS0yMzk6NzEiIHBhdGg9InZhbHVlIiB4c2k6dHlwZT0iUHJvcGVydHkiPgogICAgICAgICAgICA8c291cmNlIGxvY2F0b3I9IjIzOTo2MS0yMzk6NjUiIG5hbWU9InZhbHVlIiB4c2k6dHlwZT0iT3BlcmFuZFJlZiIvPgogICAgICAgICA8L2V4cHJlc3Npb24+CiAgICAgICAgIDxvcGVyYW5kIG5hbWU9InZhbHVlIj4KICAgICAgICAgICAgPG9wZXJhbmRUeXBlU3BlY2lmaWVyIGxvY2F0b3I9IjIzOTozMi0yMzk6NTciIG5hbWU9ImZoaXI6RWxpZ2liaWxpdHlSZXNwb25zZVB1cnBvc2UiIHhzaTp0eXBlPSJOYW1lZFR5cGVTcGVjaWZpZXIiLz4KICAgICAgICAgPC9vcGVyYW5kPgogICAgICA8L2RlZj4KICAgICAgPGRlZiBsb2NhdG9yPSIyNDA6MS0yNDA6NzAiIG5hbWU9IlRvU3RyaW5nIiBjb250ZXh0PSJQYXRpZW50IiBhY2Nlc3NMZXZlbD0iUHVibGljIiB4c2k6dHlwZT0iRnVuY3Rpb25EZWYiPgogICAgICAgICA8ZXhwcmVzc2lvbiBsb2NhdG9yPSIyNDA6NjAtMjQwOjcwIiBwYXRoPSJ2YWx1ZSIgeHNpOnR5cGU9IlByb3BlcnR5Ij4KICAgICAgICAgICAgPHNvdXJjZSBsb2NhdG9yPSIyNDA6NjAtMjQwOjY0IiBuYW1lPSJ2YWx1ZSIgeHNpOnR5cGU9Ik9wZXJhbmRSZWYiLz4KICAgICAgICAgPC9leHByZXNzaW9uPgogICAgICAgICA8b3BlcmFuZCBuYW1lPSJ2YWx1ZSI+CiAgICAgICAgICAgIDxvcGVyYW5kVHlwZVNwZWNpZmllciBsb2NhdG9yPSIyNDA6MzItMjQwOjU2IiBuYW1lPSJmaGlyOkVsaWdpYmlsaXR5UmVzcG9uc2VTdGF0dXMiIHhzaTp0eXBlPSJOYW1lZFR5cGVTcGVjaWZpZXIiLz4KICAgICAgICAgPC9vcGVyYW5kPgogICAgICA8L2RlZj4KICAgICAgPGRlZiBsb2NhdG9yPSIyNDE6MS0yNDE6NjMiIG5hbWU9IlRvU3RyaW5nIiBjb250ZXh0PSJQYXRpZW50IiBhY2Nlc3NMZXZlbD0iUHVibGljIiB4c2k6dHlwZT0iRnVuY3Rpb25EZWYiPgogICAgICAgICA8ZXhwcmVzc2lvbiBsb2NhdG9yPSIyNDE6NTMtMjQxOjYzIiBwYXRoPSJ2YWx1ZSIgeHNpOnR5cGU9IlByb3BlcnR5Ij4KICAgICAgICAgICAgPHNvdXJjZSBsb2NhdG9yPSIyNDE6NTMtMjQxOjU3IiBuYW1lPSJ2YWx1ZSIgeHNpOnR5cGU9Ik9wZXJhbmRSZWYiLz4KICAgICAgICAgPC9leHByZXNzaW9uPgogICAgICAgICA8b3BlcmFuZCBuYW1lPSJ2YWx1ZSI+CiAgICAgICAgICAgIDxvcGVyYW5kVHlwZVNwZWNpZmllciBsb2NhdG9yPSIyNDE6MzItMjQxOjQ5IiBuYW1lPSJmaGlyOkVuYWJsZVdoZW5CZWhhdmlvciIgeHNpOnR5cGU9Ik5hbWVkVHlwZVNwZWNpZmllciIvPgogICAgICAgICA8L29wZXJhbmQ+CiAgICAgIDwvZGVmPgogICAgICA8ZGVmIGxvY2F0b3I9IjI0MjoxLTI0Mjo2OCIgbmFtZT0iVG9TdHJpbmciIGNvbnRleHQ9IlBhdGllbnQiIGFjY2Vzc0xldmVsPSJQdWJsaWMiIHhzaTp0eXBlPSJGdW5jdGlvbkRlZiI+CiAgICAgICAgIDxleHByZXNzaW9uIGxvY2F0b3I9IjI0Mjo1OC0yNDI6NjgiIHBhdGg9InZhbHVlIiB4c2k6dHlwZT0iUHJvcGVydHkiPgogICAgICAgICAgICA8c291cmNlIGxvY2F0b3I9IjI0Mjo1OC0yNDI6NjIiIG5hbWU9InZhbHVlIiB4c2k6dHlwZT0iT3BlcmFuZFJlZiIvPgogICAgICAgICA8L2V4cHJlc3Npb24+CiAgICAgICAgIDxvcGVyYW5kIG5hbWU9InZhbHVlIj4KICAgICAgICAgICAgPG9wZXJhbmRUeXBlU3BlY2lmaWVyIGxvY2F0b3I9IjI0MjozMi0yNDI6NTQiIG5hbWU9ImZoaXI6RW5jb3VudGVyTG9jYXRpb25TdGF0dXMiIHhzaTp0eXBlPSJOYW1lZFR5cGVTcGVjaWZpZXIiLz4KICAgICAgICAgPC9vcGVyYW5kPgogICAgICA8L2RlZj4KICAgICAgPGRlZiBsb2NhdG9yPSIyNDM6MS0yNDM6NjAiIG5hbWU9IlRvU3RyaW5nIiBjb250ZXh0PSJQYXRpZW50IiBhY2Nlc3NMZXZlbD0iUHVibGljIiB4c2k6dHlwZT0iRnVuY3Rpb25EZWYiPgogICAgICAgICA8ZXhwcmVzc2lvbiBsb2NhdG9yPSIyNDM6NTAtMjQzOjYwIiBwYXRoPSJ2YWx1ZSIgeHNpOnR5cGU9IlByb3BlcnR5Ij4KICAgICAgICAgICAgPHNvdXJjZSBsb2NhdG9yPSIyNDM6NTAtMjQzOjU0IiBuYW1lPSJ2YWx1ZSIgeHNpOnR5cGU9Ik9wZXJhbmRSZWYiLz4KICAgICAgICAgPC9leHByZXNzaW9uPgogICAgICAgICA8b3BlcmFuZCBuYW1lPSJ2YWx1ZSI+CiAgICAgICAgICAgIDxvcGVyYW5kVHlwZVNwZWNpZmllciBsb2NhdG9yPSIyNDM6MzItMjQzOjQ2IiBuYW1lPSJmaGlyOkVuY291bnRlclN0YXR1cyIgeHNpOnR5cGU9Ik5hbWVkVHlwZVNwZWNpZmllciIvPgogICAgICAgICA8L29wZXJhbmQ+CiAgICAgIDwvZGVmPgogICAgICA8ZGVmIGxvY2F0b3I9IjI0NDoxLTI0NDo1OSIgbmFtZT0iVG9TdHJpbmciIGNvbnRleHQ9IlBhdGllbnQiIGFjY2Vzc0xldmVsPSJQdWJsaWMiIHhzaTp0eXBlPSJGdW5jdGlvbkRlZiI+CiAgICAgICAgIDxleHByZXNzaW9uIGxvY2F0b3I9IjI0NDo0OS0yNDQ6NTkiIHBhdGg9InZhbHVlIiB4c2k6dHlwZT0iUHJvcGVydHkiPgogICAgICAgICAgICA8c291cmNlIGxvY2F0b3I9IjI0NDo0OS0yNDQ6NTMiIG5hbWU9InZhbHVlIiB4c2k6dHlwZT0iT3BlcmFuZFJlZiIvPgogICAgICAgICA8L2V4cHJlc3Npb24+CiAgICAgICAgIDxvcGVyYW5kIG5hbWU9InZhbHVlIj4KICAgICAgICAgICAgPG9wZXJhbmRUeXBlU3BlY2lmaWVyIGxvY2F0b3I9IjI0NDozMi0yNDQ6NDUiIG5hbWU9ImZoaXI6RW5kcG9pbnRTdGF0dXMiIHhzaTp0eXBlPSJOYW1lZFR5cGVTcGVjaWZpZXIiLz4KICAgICAgICAgPC9vcGVyYW5kPgogICAgICA8L2RlZj4KICAgICAgPGRlZiBsb2NhdG9yPSIyNDU6MS0yNDU6NjgiIG5hbWU9IlRvU3RyaW5nIiBjb250ZXh0PSJQYXRpZW50IiBhY2Nlc3NMZXZlbD0iUHVibGljIiB4c2k6dHlwZT0iRnVuY3Rpb25EZWYiPgogICAgICAgICA8ZXhwcmVzc2lvbiBsb2NhdG9yPSIyNDU6NTgtMjQ1OjY4IiBwYXRoPSJ2YWx1ZSIgeHNpOnR5cGU9IlByb3BlcnR5Ij4KICAgICAgICAgICAgPHNvdXJjZSBsb2NhdG9yPSIyNDU6NTgtMjQ1OjYyIiBuYW1lPSJ2YWx1ZSIgeHNpOnR5cGU9Ik9wZXJhbmRSZWYiLz4KICAgICAgICAgPC9leHByZXNzaW9uPgogICAgICAgICA8b3BlcmFuZCBuYW1lPSJ2YWx1ZSI+CiAgICAgICAgICAgIDxvcGVyYW5kVHlwZVNwZWNpZmllciBsb2NhdG9yPSIyNDU6MzItMjQ1OjU0IiBuYW1lPSJmaGlyOkVucm9sbG1lbnRSZXF1ZXN0U3RhdHVzIiB4c2k6dHlwZT0iTmFtZWRUeXBlU3BlY2lmaWVyIi8+CiAgICAgICAgIDwvb3BlcmFuZD4KICAgICAgPC9kZWY+CiAgICAgIDxkZWYgbG9jYXRvcj0iMjQ2OjEtMjQ2OjY5IiBuYW1lPSJUb1N0cmluZyIgY29udGV4dD0iUGF0aWVudCIgYWNjZXNzTGV2ZWw9IlB1YmxpYyIgeHNpOnR5cGU9IkZ1bmN0aW9uRGVmIj4KICAgICAgICAgPGV4cHJlc3Npb24gbG9jYXRvcj0iMjQ2OjU5LTI0Njo2OSIgcGF0aD0idmFsdWUiIHhzaTp0eXBlPSJQcm9wZXJ0eSI+CiAgICAgICAgICAgIDxzb3VyY2UgbG9jYXRvcj0iMjQ2OjU5LTI0Njo2MyIgbmFtZT0idmFsdWUiIHhzaTp0eXBlPSJPcGVyYW5kUmVmIi8+CiAgICAgICAgIDwvZXhwcmVzc2lvbj4KICAgICAgICAgPG9wZXJhbmQgbmFtZT0idmFsdWUiPgogICAgICAgICAgICA8b3BlcmFuZFR5cGVTcGVjaWZpZXIgbG9jYXRvcj0iMjQ2OjMyLTI0Njo1NSIgbmFtZT0iZmhpcjpFbnJvbGxtZW50UmVzcG9uc2VTdGF0dXMiIHhzaTp0eXBlPSJOYW1lZFR5cGVTcGVjaWZpZXIiLz4KICAgICAgICAgPC9vcGVyYW5kPgogICAgICA8L2RlZj4KICAgICAgPGRlZiBsb2NhdG9yPSIyNDc6MS0yNDc6NjQiIG5hbWU9IlRvU3RyaW5nIiBjb250ZXh0PSJQYXRpZW50IiBhY2Nlc3NMZXZlbD0iUHVibGljIiB4c2k6dHlwZT0iRnVuY3Rpb25EZWYiPgogICAgICAgICA8ZXhwcmVzc2lvbiBsb2NhdG9yPSIyNDc6NTQtMjQ3OjY0IiBwYXRoPSJ2YWx1ZSIgeHNpOnR5cGU9IlByb3BlcnR5Ij4KICAgICAgICAgICAgPHNvdXJjZSBsb2NhdG9yPSIyNDc6NTQtMjQ3OjU4IiBuYW1lPSJ2YWx1ZSIgeHNpOnR5cGU9Ik9wZXJhbmRSZWYiLz4KICAgICAgICAgPC9leHByZXNzaW9uPgogICAgICAgICA8b3BlcmFuZCBuYW1lPSJ2YWx1ZSI+CiAgICAgICAgICAgIDxvcGVyYW5kVHlwZVNwZWNpZmllciBsb2NhdG9yPSIyNDc6MzItMjQ3OjUwIiBuYW1lPSJmaGlyOkVwaXNvZGVPZkNhcmVTdGF0dXMiIHhzaTp0eXBlPSJOYW1lZFR5cGVTcGVjaWZpZXIiLz4KICAgICAgICAgPC9vcGVyYW5kPgogICAgICA8L2RlZj4KICAgICAgPGRlZiBsb2NhdG9yPSIyNDg6MS0yNDg6NjQiIG5hbWU9IlRvU3RyaW5nIiBjb250ZXh0PSJQYXRpZW50IiBhY2Nlc3NMZXZlbD0iUHVibGljIiB4c2k6dHlwZT0iRnVuY3Rpb25EZWYiPgogICAgICAgICA8ZXhwcmVzc2lvbiBsb2NhdG9yPSIyNDg6NTQtMjQ4OjY0IiBwYXRoPSJ2YWx1ZSIgeHNpOnR5cGU9IlByb3BlcnR5Ij4KICAgICAgICAgICAgPHNvdXJjZSBsb2NhdG9yPSIyNDg6NTQtMjQ4OjU4IiBuYW1lPSJ2YWx1ZSIgeHNpOnR5cGU9Ik9wZXJhbmRSZWYiLz4KICAgICAgICAgPC9leHByZXNzaW9uPgogICAgICAgICA8b3BlcmFuZCBuYW1lPSJ2YWx1ZSI+CiAgICAgICAgICAgIDxvcGVyYW5kVHlwZVNwZWNpZmllciBsb2NhdG9yPSIyNDg6MzItMjQ4OjUwIiBuYW1lPSJmaGlyOkV2ZW50Q2FwYWJpbGl0eU1vZGUiIHhzaTp0eXBlPSJOYW1lZFR5cGVTcGVjaWZpZXIiLz4KICAgICAgICAgPC9vcGVyYW5kPgogICAgICA8L2RlZj4KICAgICAgPGRlZiBsb2NhdG9yPSIyNDk6MS0yNDk6NTYiIG5hbWU9IlRvU3RyaW5nIiBjb250ZXh0PSJQYXRpZW50IiBhY2Nlc3NMZXZlbD0iUHVibGljIiB4c2k6dHlwZT0iRnVuY3Rpb25EZWYiPgogICAgICAgICA8ZXhwcmVzc2lvbiBsb2NhdG9yPSIyNDk6NDYtMjQ5OjU2IiBwYXRoPSJ2YWx1ZSIgeHNpOnR5cGU9IlByb3BlcnR5Ij4KICAgICAgICAgICAgPHNvdXJjZSBsb2NhdG9yPSIyNDk6NDYtMjQ5OjUwIiBuYW1lPSJ2YWx1ZSIgeHNpOnR5cGU9Ik9wZXJhbmRSZWYiLz4KICAgICAgICAgPC9leHByZXNzaW9uPgogICAgICAgICA8b3BlcmFuZCBuYW1lPSJ2YWx1ZSI+CiAgICAgICAgICAgIDxvcGVyYW5kVHlwZVNwZWNpZmllciBsb2NhdG9yPSIyNDk6MzItMjQ5OjQyIiBuYW1lPSJmaGlyOkV2ZW50VGltaW5nIiB4c2k6dHlwZT0iTmFtZWRUeXBlU3BlY2lmaWVyIi8+CiAgICAgICAgIDwvb3BlcmFuZD4KICAgICAgPC9kZWY+CiAgICAgIDxkZWYgbG9jYXRvcj0iMjUwOjEtMjUwOjY1IiBuYW1lPSJUb1N0cmluZyIgY29udGV4dD0iUGF0aWVudCIgYWNjZXNzTGV2ZWw9IlB1YmxpYyIgeHNpOnR5cGU9IkZ1bmN0aW9uRGVmIj4KICAgICAgICAgPGV4cHJlc3Npb24gbG9jYXRvcj0iMjUwOjU1LTI1MDo2NSIgcGF0aD0idmFsdWUiIHhzaTp0eXBlPSJQcm9wZXJ0eSI+CiAgICAgICAgICAgIDxzb3VyY2UgbG9jYXRvcj0iMjUwOjU1LTI1MDo1OSIgbmFtZT0idmFsdWUiIHhzaTp0eXBlPSJPcGVyYW5kUmVmIi8+CiAgICAgICAgIDwvZXhwcmVzc2lvbj4KICAgICAgICAgPG9wZXJhbmQgbmFtZT0idmFsdWUiPgogICAgICAgICAgICA8b3BlcmFuZFR5cGVTcGVjaWZpZXIgbG9jYXRvcj0iMjUwOjMyLTI1MDo1MSIgbmFtZT0iZmhpcjpFdmlkZW5jZVZhcmlhYmxlVHlwZSIgeHNpOnR5cGU9Ik5hbWVkVHlwZVNwZWNpZmllciIvPgogICAgICAgICA8L29wZXJhbmQ+CiAgICAgIDwvZGVmPgogICAgICA8ZGVmIGxvY2F0b3I9IjI1MToxLTI1MTo2OSIgbmFtZT0iVG9TdHJpbmciIGNvbnRleHQ9IlBhdGllbnQiIGFjY2Vzc0xldmVsPSJQdWJsaWMiIHhzaTp0eXBlPSJGdW5jdGlvbkRlZiI+CiAgICAgICAgIDxleHByZXNzaW9uIGxvY2F0b3I9IjI1MTo1OS0yNTE6NjkiIHBhdGg9InZhbHVlIiB4c2k6dHlwZT0iUHJvcGVydHkiPgogICAgICAgICAgICA8c291cmNlIGxvY2F0b3I9IjI1MTo1OS0yNTE6NjMiIG5hbWU9InZhbHVlIiB4c2k6dHlwZT0iT3BlcmFuZFJlZiIvPgogICAgICAgICA8L2V4cHJlc3Npb24+CiAgICAgICAgIDxvcGVyYW5kIG5hbWU9InZhbHVlIj4KICAgICAgICAgICAgPG9wZXJhbmRUeXBlU3BlY2lmaWVyIGxvY2F0b3I9IjI1MTozMi0yNTE6NTUiIG5hbWU9ImZoaXI6RXhhbXBsZVNjZW5hcmlvQWN0b3JUeXBlIiB4c2k6dHlwZT0iTmFtZWRUeXBlU3BlY2lmaWVyIi8+CiAgICAgICAgIDwvb3BlcmFuZD4KICAgICAgPC9kZWY+CiAgICAgIDxkZWYgbG9jYXRvcj0iMjUyOjEtMjUyOjcxIiBuYW1lPSJUb1N0cmluZyIgY29udGV4dD0iUGF0aWVudCIgYWNjZXNzTGV2ZWw9IlB1YmxpYyIgeHNpOnR5cGU9IkZ1bmN0aW9uRGVmIj4KICAgICAgICAgPGV4cHJlc3Npb24gbG9jYXRvcj0iMjUyOjYxLTI1Mjo3MSIgcGF0aD0idmFsdWUiIHhzaTp0eXBlPSJQcm9wZXJ0eSI+CiAgICAgICAgICAgIDxzb3VyY2UgbG9jYXRvcj0iMjUyOjYxLTI1Mjo2NSIgbmFtZT0idmFsdWUiIHhzaTp0eXBlPSJPcGVyYW5kUmVmIi8+CiAgICAgICAgIDwvZXhwcmVzc2lvbj4KICAgICAgICAgPG9wZXJhbmQgbmFtZT0idmFsdWUiPgogICAgICAgICAgICA8b3BlcmFuZFR5cGVTcGVjaWZpZXIgbG9jYXRvcj0iMjUyOjMyLTI1Mjo1NyIgbmFtZT0iZmhpcjpFeHBsYW5hdGlvbk9mQmVuZWZpdFN0YXR1cyIgeHNpOnR5cGU9Ik5hbWVkVHlwZVNwZWNpZmllciIvPgogICAgICAgICA8L29wZXJhbmQ+CiAgICAgIDwvZGVmPgogICAgICA8ZGVmIGxvY2F0b3I9IjI1MzoxLTI1Mzo1OCIgbmFtZT0iVG9TdHJpbmciIGNvbnRleHQ9IlBhdGllbnQiIGFjY2Vzc0xldmVsPSJQdWJsaWMiIHhzaTp0eXBlPSJGdW5jdGlvbkRlZiI+CiAgICAgICAgIDxleHByZXNzaW9uIGxvY2F0b3I9IjI1Mzo0OC0yNTM6NTgiIHBhdGg9InZhbHVlIiB4c2k6dHlwZT0iUHJvcGVydHkiPgogICAgICAgICAgICA8c291cmNlIGxvY2F0b3I9IjI1Mzo0OC0yNTM6NTIiIG5hbWU9InZhbHVlIiB4c2k6dHlwZT0iT3BlcmFuZFJlZiIvPgogICAgICAgICA8L2V4cHJlc3Npb24+CiAgICAgICAgIDxvcGVyYW5kIG5hbWU9InZhbHVlIj4KICAgICAgICAgICAgPG9wZXJhbmRUeXBlU3BlY2lmaWVyIGxvY2F0b3I9IjI1MzozMi0yNTM6NDQiIG5hbWU9ImZoaXI6RXhwb3N1cmVTdGF0ZSIgeHNpOnR5cGU9Ik5hbWVkVHlwZVNwZWNpZmllciIvPgogICAgICAgICA8L29wZXJhbmQ+CiAgICAgIDwvZGVmPgogICAgICA8ZGVmIGxvY2F0b3I9IjI1NDoxLTI1NDo2NSIgbmFtZT0iVG9TdHJpbmciIGNvbnRleHQ9IlBhdGllbnQiIGFjY2Vzc0xldmVsPSJQdWJsaWMiIHhzaTp0eXBlPSJGdW5jdGlvbkRlZiI+CiAgICAgICAgIDxleHByZXNzaW9uIGxvY2F0b3I9IjI1NDo1NS0yNTQ6NjUiIHBhdGg9InZhbHVlIiB4c2k6dHlwZT0iUHJvcGVydHkiPgogICAgICAgICAgICA8c291cmNlIGxvY2F0b3I9IjI1NDo1NS0yNTQ6NTkiIG5hbWU9InZhbHVlIiB4c2k6dHlwZT0iT3BlcmFuZFJlZiIvPgogICAgICAgICA8L2V4cHJlc3Npb24+CiAgICAgICAgIDxvcGVyYW5kIG5hbWU9InZhbHVlIj4KICAgICAgICAgICAgPG9wZXJhbmRUeXBlU3BlY2lmaWVyIGxvY2F0b3I9IjI1NDozMi0yNTQ6NTEiIG5hbWU9ImZoaXI6RXh0ZW5zaW9uQ29udGV4dFR5cGUiIHhzaTp0eXBlPSJOYW1lZFR5cGVTcGVjaWZpZXIiLz4KICAgICAgICAgPC9vcGVyYW5kPgogICAgICA8L2RlZj4KICAgICAgPGRlZiBsb2NhdG9yPSIyNTU6MS0yNTU6NTciIG5hbWU9IlRvU3RyaW5nIiBjb250ZXh0PSJQYXRpZW50IiBhY2Nlc3NMZXZlbD0iUHVibGljIiB4c2k6dHlwZT0iRnVuY3Rpb25EZWYiPgogICAgICAgICA8ZXhwcmVzc2lvbiBsb2NhdG9yPSIyNTU6NDctMjU1OjU3IiBwYXRoPSJ2YWx1ZSIgeHNpOnR5cGU9IlByb3BlcnR5Ij4KICAgICAgICAgICAgPHNvdXJjZSBsb2NhdG9yPSIyNTU6NDctMjU1OjUxIiBuYW1lPSJ2YWx1ZSIgeHNpOnR5cGU9Ik9wZXJhbmRSZWYiLz4KICAgICAgICAgPC9leHByZXNzaW9uPgogICAgICAgICA8b3BlcmFuZCBuYW1lPSJ2YWx1ZSI+CiAgICAgICAgICAgIDxvcGVyYW5kVHlwZVNwZWNpZmllciBsb2NhdG9yPSIyNTU6MzItMjU1OjQzIiBuYW1lPSJmaGlyOkZISVJBbGxUeXBlcyIgeHNpOnR5cGU9Ik5hbWVkVHlwZVNwZWNpZmllciIvPgogICAgICAgICA8L29wZXJhbmQ+CiAgICAgIDwvZGVmPgogICAgICA8ZGVmIGxvY2F0b3I9IjI1NjoxLTI1Njo2MCIgbmFtZT0iVG9TdHJpbmciIGNvbnRleHQ9IlBhdGllbnQiIGFjY2Vzc0xldmVsPSJQdWJsaWMiIHhzaTp0eXBlPSJGdW5jdGlvbkRlZiI+CiAgICAgICAgIDxleHByZXNzaW9uIGxvY2F0b3I9IjI1Njo1MC0yNTY6NjAiIHBhdGg9InZhbHVlIiB4c2k6dHlwZT0iUHJvcGVydHkiPgogICAgICAgICAgICA8c291cmNlIGxvY2F0b3I9IjI1Njo1MC0yNTY6NTQiIG5hbWU9InZhbHVlIiB4c2k6dHlwZT0iT3BlcmFuZFJlZiIvPgogICAgICAgICA8L2V4cHJlc3Npb24+CiAgICAgICAgIDxvcGVyYW5kIG5hbWU9InZhbHVlIj4KICAgICAgICAgICAgPG9wZXJhbmRUeXBlU3BlY2lmaWVyIGxvY2F0b3I9IjI1NjozMi0yNTY6NDYiIG5hbWU9ImZoaXI6RkhJUkRlZmluZWRUeXBlIiB4c2k6dHlwZT0iTmFtZWRUeXBlU3BlY2lmaWVyIi8+CiAgICAgICAgIDwvb3BlcmFuZD4KICAgICAgPC9kZWY+CiAgICAgIDxkZWYgbG9jYXRvcj0iMjU3OjEtMjU3OjYxIiBuYW1lPSJUb1N0cmluZyIgY29udGV4dD0iUGF0aWVudCIgYWNjZXNzTGV2ZWw9IlB1YmxpYyIgeHNpOnR5cGU9IkZ1bmN0aW9uRGVmIj4KICAgICAgICAgPGV4cHJlc3Npb24gbG9jYXRvcj0iMjU3OjUxLTI1Nzo2MSIgcGF0aD0idmFsdWUiIHhzaTp0eXBlPSJQcm9wZXJ0eSI+CiAgICAgICAgICAgIDxzb3VyY2UgbG9jYXRvcj0iMjU3OjUxLTI1Nzo1NSIgbmFtZT0idmFsdWUiIHhzaTp0eXBlPSJPcGVyYW5kUmVmIi8+CiAgICAgICAgIDwvZXhwcmVzc2lvbj4KICAgICAgICAgPG9wZXJhbmQgbmFtZT0idmFsdWUiPgogICAgICAgICAgICA8b3BlcmFuZFR5cGVTcGVjaWZpZXIgbG9jYXRvcj0iMjU3OjMyLTI1Nzo0NyIgbmFtZT0iZmhpcjpGSElSRGV2aWNlU3RhdHVzIiB4c2k6dHlwZT0iTmFtZWRUeXBlU3BlY2lmaWVyIi8+CiAgICAgICAgIDwvb3BlcmFuZD4KICAgICAgPC9kZWY+CiAgICAgIDxkZWYgbG9jYXRvcj0iMjU4OjEtMjU4OjYxIiBuYW1lPSJUb1N0cmluZyIgY29udGV4dD0iUGF0aWVudCIgYWNjZXNzTGV2ZWw9IlB1YmxpYyIgeHNpOnR5cGU9IkZ1bmN0aW9uRGVmIj4KICAgICAgICAgPGV4cHJlc3Npb24gbG9jYXRvcj0iMjU4OjUxLTI1ODo2MSIgcGF0aD0idmFsdWUiIHhzaTp0eXBlPSJQcm9wZXJ0eSI+CiAgICAgICAgICAgIDxzb3VyY2UgbG9jYXRvcj0iMjU4OjUxLTI1ODo1NSIgbmFtZT0idmFsdWUiIHhzaTp0eXBlPSJPcGVyYW5kUmVmIi8+CiAgICAgICAgIDwvZXhwcmVzc2lvbj4KICAgICAgICAgPG9wZXJhbmQgbmFtZT0idmFsdWUiPgogICAgICAgICAgICA8b3BlcmFuZFR5cGVTcGVjaWZpZXIgbG9jYXRvcj0iMjU4OjMyLTI1ODo0NyIgbmFtZT0iZmhpcjpGSElSUmVzb3VyY2VUeXBlIiB4c2k6dHlwZT0iTmFtZWRUeXBlU3BlY2lmaWVyIi8+CiAgICAgICAgIDwvb3BlcmFuZD4KICAgICAgPC9kZWY+CiAgICAgIDxkZWYgbG9jYXRvcj0iMjU5OjEtMjU5OjY0IiBuYW1lPSJUb1N0cmluZyIgY29udGV4dD0iUGF0aWVudCIgYWNjZXNzTGV2ZWw9IlB1YmxpYyIgeHNpOnR5cGU9IkZ1bmN0aW9uRGVmIj4KICAgICAgICAgPGV4cHJlc3Npb24gbG9jYXRvcj0iMjU5OjU0LTI1OTo2NCIgcGF0aD0idmFsdWUiIHhzaTp0eXBlPSJQcm9wZXJ0eSI+CiAgICAgICAgICAgIDxzb3VyY2UgbG9jYXRvcj0iMjU5OjU0LTI1OTo1OCIgbmFtZT0idmFsdWUiIHhzaTp0eXBlPSJPcGVyYW5kUmVmIi8+CiAgICAgICAgIDwvZXhwcmVzc2lvbj4KICAgICAgICAgPG9wZXJhbmQgbmFtZT0idmFsdWUiPgogICAgICAgICAgICA8b3BlcmFuZFR5cGVTcGVjaWZpZXIgbG9jYXRvcj0iMjU5OjMyLTI1OTo1MCIgbmFtZT0iZmhpcjpGSElSU3Vic3RhbmNlU3RhdHVzIiB4c2k6dHlwZT0iTmFtZWRUeXBlU3BlY2lmaWVyIi8+CiAgICAgICAgIDwvb3BlcmFuZD4KICAgICAgPC9kZWY+CiAgICAgIDxkZWYgbG9jYXRvcj0iMjYwOjEtMjYwOjU2IiBuYW1lPSJUb1N0cmluZyIgY29udGV4dD0iUGF0aWVudCIgYWNjZXNzTGV2ZWw9IlB1YmxpYyIgeHNpOnR5cGU9IkZ1bmN0aW9uRGVmIj4KICAgICAgICAgPGV4cHJlc3Npb24gbG9jYXRvcj0iMjYwOjQ2LTI2MDo1NiIgcGF0aD0idmFsdWUiIHhzaTp0eXBlPSJQcm9wZXJ0eSI+CiAgICAgICAgICAgIDxzb3VyY2UgbG9jYXRvcj0iMjYwOjQ2LTI2MDo1MCIgbmFtZT0idmFsdWUiIHhzaTp0eXBlPSJPcGVyYW5kUmVmIi8+CiAgICAgICAgIDwvZXhwcmVzc2lvbj4KICAgICAgICAgPG9wZXJhbmQgbmFtZT0idmFsdWUiPgogICAgICAgICAgICA8b3BlcmFuZFR5cGVTcGVjaWZpZXIgbG9jYXRvcj0iMjYwOjMyLTI2MDo0MiIgbmFtZT0iZmhpcjpGSElSVmVyc2lvbiIgeHNpOnR5cGU9Ik5hbWVkVHlwZVNwZWNpZmllciIvPgogICAgICAgICA8L29wZXJhbmQ+CiAgICAgIDwvZGVmPgogICAgICA8ZGVmIGxvY2F0b3I9IjI2MToxLTI2MTo2NCIgbmFtZT0iVG9TdHJpbmciIGNvbnRleHQ9IlBhdGllbnQiIGFjY2Vzc0xldmVsPSJQdWJsaWMiIHhzaTp0eXBlPSJGdW5jdGlvbkRlZiI+CiAgICAgICAgIDxleHByZXNzaW9uIGxvY2F0b3I9IjI2MTo1NC0yNjE6NjQiIHBhdGg9InZhbHVlIiB4c2k6dHlwZT0iUHJvcGVydHkiPgogICAgICAgICAgICA8c291cmNlIGxvY2F0b3I9IjI2MTo1NC0yNjE6NTgiIG5hbWU9InZhbHVlIiB4c2k6dHlwZT0iT3BlcmFuZFJlZiIvPgogICAgICAgICA8L2V4cHJlc3Npb24+CiAgICAgICAgIDxvcGVyYW5kIG5hbWU9InZhbHVlIj4KICAgICAgICAgICAgPG9wZXJhbmRUeXBlU3BlY2lmaWVyIGxvY2F0b3I9IjI2MTozMi0yNjE6NTAiIG5hbWU9ImZoaXI6RmFtaWx5SGlzdG9yeVN0YXR1cyIgeHNpOnR5cGU9Ik5hbWVkVHlwZVNwZWNpZmllciIvPgogICAgICAgICA8L29wZXJhbmQ+CiAgICAgIDwvZGVmPgogICAgICA8ZGVmIGxvY2F0b3I9IjI2MjoxLTI2Mjo1OSIgbmFtZT0iVG9TdHJpbmciIGNvbnRleHQ9IlBhdGllbnQiIGFjY2Vzc0xldmVsPSJQdWJsaWMiIHhzaTp0eXBlPSJGdW5jdGlvbkRlZiI+CiAgICAgICAgIDxleHByZXNzaW9uIGxvY2F0b3I9IjI2Mjo0OS0yNjI6NTkiIHBhdGg9InZhbHVlIiB4c2k6dHlwZT0iUHJvcGVydHkiPgogICAgICAgICAgICA8c291cmNlIGxvY2F0b3I9IjI2Mjo0OS0yNjI6NTMiIG5hbWU9InZhbHVlIiB4c2k6dHlwZT0iT3BlcmFuZFJlZiIvPgogICAgICAgICA8L2V4cHJlc3Npb24+CiAgICAgICAgIDxvcGVyYW5kIG5hbWU9InZhbHVlIj4KICAgICAgICAgICAgPG9wZXJhbmRUeXBlU3BlY2lmaWVyIGxvY2F0b3I9IjI2MjozMi0yNjI6NDUiIG5hbWU9ImZoaXI6RmlsdGVyT3BlcmF0b3IiIHhzaTp0eXBlPSJOYW1lZFR5cGVTcGVjaWZpZXIiLz4KICAgICAgICAgPC9vcGVyYW5kPgogICAgICA8L2RlZj4KICAgICAgPGRlZiBsb2NhdG9yPSIyNjM6MS0yNjM6NTUiIG5hbWU9IlRvU3RyaW5nIiBjb250ZXh0PSJQYXRpZW50IiBhY2Nlc3NMZXZlbD0iUHVibGljIiB4c2k6dHlwZT0iRnVuY3Rpb25EZWYiPgogICAgICAgICA8ZXhwcmVzc2lvbiBsb2NhdG9yPSIyNjM6NDUtMjYzOjU1IiBwYXRoPSJ2YWx1ZSIgeHNpOnR5cGU9IlByb3BlcnR5Ij4KICAgICAgICAgICAgPHNvdXJjZSBsb2NhdG9yPSIyNjM6NDUtMjYzOjQ5IiBuYW1lPSJ2YWx1ZSIgeHNpOnR5cGU9Ik9wZXJhbmRSZWYiLz4KICAgICAgICAgPC9leHByZXNzaW9uPgogICAgICAgICA8b3BlcmFuZCBuYW1lPSJ2YWx1ZSI+CiAgICAgICAgICAgIDxvcGVyYW5kVHlwZVNwZWNpZmllciBsb2NhdG9yPSIyNjM6MzItMjYzOjQxIiBuYW1lPSJmaGlyOkZsYWdTdGF0dXMiIHhzaTp0eXBlPSJOYW1lZFR5cGVTcGVjaWZpZXIiLz4KICAgICAgICAgPC9vcGVyYW5kPgogICAgICA8L2RlZj4KICAgICAgPGRlZiBsb2NhdG9yPSIyNjQ6MS0yNjQ6NjQiIG5hbWU9IlRvU3RyaW5nIiBjb250ZXh0PSJQYXRpZW50IiBhY2Nlc3NMZXZlbD0iUHVibGljIiB4c2k6dHlwZT0iRnVuY3Rpb25EZWYiPgogICAgICAgICA8ZXhwcmVzc2lvbiBsb2NhdG9yPSIyNjQ6NTQtMjY0OjY0IiBwYXRoPSJ2YWx1ZSIgeHNpOnR5cGU9IlByb3BlcnR5Ij4KICAgICAgICAgICAgPHNvdXJjZSBsb2NhdG9yPSIyNjQ6NTQtMjY0OjU4IiBuYW1lPSJ2YWx1ZSIgeHNpOnR5cGU9Ik9wZXJhbmRSZWYiLz4KICAgICAgICAgPC9leHByZXNzaW9uPgogICAgICAgICA8b3BlcmFuZCBuYW1lPSJ2YWx1ZSI+CiAgICAgICAgICAgIDxvcGVyYW5kVHlwZVNwZWNpZmllciBsb2NhdG9yPSIyNjQ6MzItMjY0OjUwIiBuYW1lPSJmaGlyOkdvYWxMaWZlY3ljbGVTdGF0dXMiIHhzaTp0eXBlPSJOYW1lZFR5cGVTcGVjaWZpZXIiLz4KICAgICAgICAgPC9vcGVyYW5kPgogICAgICA8L2RlZj4KICAgICAgPGRlZiBsb2NhdG9yPSIyNjU6MS0yNjU6NjUiIG5hbWU9IlRvU3RyaW5nIiBjb250ZXh0PSJQYXRpZW50IiBhY2Nlc3NMZXZlbD0iUHVibGljIiB4c2k6dHlwZT0iRnVuY3Rpb25EZWYiPgogICAgICAgICA8ZXhwcmVzc2lvbiBsb2NhdG9yPSIyNjU6NTUtMjY1OjY1IiBwYXRoPSJ2YWx1ZSIgeHNpOnR5cGU9IlByb3BlcnR5Ij4KICAgICAgICAgICAgPHNvdXJjZSBsb2NhdG9yPSIyNjU6NTUtMjY1OjU5IiBuYW1lPSJ2YWx1ZSIgeHNpOnR5cGU9Ik9wZXJhbmRSZWYiLz4KICAgICAgICAgPC9leHByZXNzaW9uPgogICAgICAgICA8b3BlcmFuZCBuYW1lPSJ2YWx1ZSI+CiAgICAgICAgICAgIDxvcGVyYW5kVHlwZVNwZWNpZmllciBsb2NhdG9yPSIyNjU6MzItMjY1OjUxIiBuYW1lPSJmaGlyOkdyYXBoQ29tcGFydG1lbnRSdWxlIiB4c2k6dHlwZT0iTmFtZWRUeXBlU3BlY2lmaWVyIi8+CiAgICAgICAgIDwvb3BlcmFuZD4KICAgICAgPC9kZWY+CiAgICAgIDxkZWYgbG9jYXRvcj0iMjY2OjEtMjY2OjY0IiBuYW1lPSJUb1N0cmluZyIgY29udGV4dD0iUGF0aWVudCIgYWNjZXNzTGV2ZWw9IlB1YmxpYyIgeHNpOnR5cGU9IkZ1bmN0aW9uRGVmIj4KICAgICAgICAgPGV4cHJlc3Npb24gbG9jYXRvcj0iMjY2OjU0LTI2Njo2NCIgcGF0aD0idmFsdWUiIHhzaTp0eXBlPSJQcm9wZXJ0eSI+CiAgICAgICAgICAgIDxzb3VyY2UgbG9jYXRvcj0iMjY2OjU0LTI2Njo1OCIgbmFtZT0idmFsdWUiIHhzaTp0eXBlPSJPcGVyYW5kUmVmIi8+CiAgICAgICAgIDwvZXhwcmVzc2lvbj4KICAgICAgICAgPG9wZXJhbmQgbmFtZT0idmFsdWUiPgogICAgICAgICAgICA8b3BlcmFuZFR5cGVTcGVjaWZpZXIgbG9jYXRvcj0iMjY2OjMyLTI2Njo1MCIgbmFtZT0iZmhpcjpHcmFwaENvbXBhcnRtZW50VXNlIiB4c2k6dHlwZT0iTmFtZWRUeXBlU3BlY2lmaWVyIi8+CiAgICAgICAgIDwvb3BlcmFuZD4KICAgICAgPC9kZWY+CiAgICAgIDxkZWYgbG9jYXRvcj0iMjY3OjEtMjY3OjU3IiBuYW1lPSJUb1N0cmluZyIgY29udGV4dD0iUGF0aWVudCIgYWNjZXNzTGV2ZWw9IlB1YmxpYyIgeHNpOnR5cGU9IkZ1bmN0aW9uRGVmIj4KICAgICAgICAgPGV4cHJlc3Npb24gbG9jYXRvcj0iMjY3OjQ3LTI2Nzo1NyIgcGF0aD0idmFsdWUiIHhzaTp0eXBlPSJQcm9wZXJ0eSI+CiAgICAgICAgICAgIDxzb3VyY2UgbG9jYXRvcj0iMjY3OjQ3LTI2Nzo1MSIgbmFtZT0idmFsdWUiIHhzaTp0eXBlPSJPcGVyYW5kUmVmIi8+CiAgICAgICAgIDwvZXhwcmVzc2lvbj4KICAgICAgICAgPG9wZXJhbmQgbmFtZT0idmFsdWUiPgogICAgICAgICAgICA8b3BlcmFuZFR5cGVTcGVjaWZpZXIgbG9jYXRvcj0iMjY3OjMyLTI2Nzo0MyIgbmFtZT0iZmhpcjpHcm91cE1lYXN1cmUiIHhzaTp0eXBlPSJOYW1lZFR5cGVTcGVjaWZpZXIiLz4KICAgICAgICAgPC9vcGVyYW5kPgogICAgICA8L2RlZj4KICAgICAgPGRlZiBsb2NhdG9yPSIyNjg6MS0yNjg6NTQiIG5hbWU9IlRvU3RyaW5nIiBjb250ZXh0PSJQYXRpZW50IiBhY2Nlc3NMZXZlbD0iUHVibGljIiB4c2k6dHlwZT0iRnVuY3Rpb25EZWYiPgogICAgICAgICA8ZXhwcmVzc2lvbiBsb2NhdG9yPSIyNjg6NDQtMjY4OjU0IiBwYXRoPSJ2YWx1ZSIgeHNpOnR5cGU9IlByb3BlcnR5Ij4KICAgICAgICAgICAgPHNvdXJjZSBsb2NhdG9yPSIyNjg6NDQtMjY4OjQ4IiBuYW1lPSJ2YWx1ZSIgeHNpOnR5cGU9Ik9wZXJhbmRSZWYiLz4KICAgICAgICAgPC9leHByZXNzaW9uPgogICAgICAgICA8b3BlcmFuZCBuYW1lPSJ2YWx1ZSI+CiAgICAgICAgICAgIDxvcGVyYW5kVHlwZVNwZWNpZmllciBsb2NhdG9yPSIyNjg6MzItMjY4OjQwIiBuYW1lPSJmaGlyOkdyb3VwVHlwZSIgeHNpOnR5cGU9Ik5hbWVkVHlwZVNwZWNpZmllciIvPgogICAgICAgICA8L29wZXJhbmQ+CiAgICAgIDwvZGVmPgogICAgICA8ZGVmIGxvY2F0b3I9IjI2OToxLTI2OTo2NyIgbmFtZT0iVG9TdHJpbmciIGNvbnRleHQ9IlBhdGllbnQiIGFjY2Vzc0xldmVsPSJQdWJsaWMiIHhzaTp0eXBlPSJGdW5jdGlvbkRlZiI+CiAgICAgICAgIDxleHByZXNzaW9uIGxvY2F0b3I9IjI2OTo1Ny0yNjk6NjciIHBhdGg9InZhbHVlIiB4c2k6dHlwZT0iUHJvcGVydHkiPgogICAgICAgICAgICA8c291cmNlIGxvY2F0b3I9IjI2OTo1Ny0yNjk6NjEiIG5hbWU9InZhbHVlIiB4c2k6dHlwZT0iT3BlcmFuZFJlZiIvPgogICAgICAgICA8L2V4cHJlc3Npb24+CiAgICAgICAgIDxvcGVyYW5kIG5hbWU9InZhbHVlIj4KICAgICAgICAgICAgPG9wZXJhbmRUeXBlU3BlY2lmaWVyIGxvY2F0b3I9IjI2OTozMi0yNjk6NTMiIG5hbWU9ImZoaXI6R3VpZGFuY2VSZXNwb25zZVN0YXR1cyIgeHNpOnR5cGU9Ik5hbWVkVHlwZVNwZWNpZmllciIvPgogICAgICAgICA8L29wZXJhbmQ+CiAgICAgIDwvZGVmPgogICAgICA8ZGVmIGxvY2F0b3I9IjI3MDoxLTI3MDo2NCIgbmFtZT0iVG9TdHJpbmciIGNvbnRleHQ9IlBhdGllbnQiIGFjY2Vzc0xldmVsPSJQdWJsaWMiIHhzaTp0eXBlPSJGdW5jdGlvbkRlZiI+CiAgICAgICAgIDxleHByZXNzaW9uIGxvY2F0b3I9IjI3MDo1NC0yNzA6NjQiIHBhdGg9InZhbHVlIiB4c2k6dHlwZT0iUHJvcGVydHkiPgogICAgICAgICAgICA8c291cmNlIGxvY2F0b3I9IjI3MDo1NC0yNzA6NTgiIG5hbWU9InZhbHVlIiB4c2k6dHlwZT0iT3BlcmFuZFJlZiIvPgogICAgICAgICA8L2V4cHJlc3Npb24+CiAgICAgICAgIDxvcGVyYW5kIG5hbWU9InZhbHVlIj4KICAgICAgICAgICAgPG9wZXJhbmRUeXBlU3BlY2lmaWVyIGxvY2F0b3I9IjI3MDozMi0yNzA6NTAiIG5hbWU9ImZoaXI6R3VpZGVQYWdlR2VuZXJhdGlvbiIgeHNpOnR5cGU9Ik5hbWVkVHlwZVNwZWNpZmllciIvPgogICAgICAgICA8L29wZXJhbmQ+CiAgICAgIDwvZGVmPgogICAgICA8ZGVmIGxvY2F0b3I9IjI3MToxLTI3MTo2MyIgbmFtZT0iVG9TdHJpbmciIGNvbnRleHQ9IlBhdGllbnQiIGFjY2Vzc0xldmVsPSJQdWJsaWMiIHhzaTp0eXBlPSJGdW5jdGlvbkRlZiI+CiAgICAgICAgIDxleHByZXNzaW9uIGxvY2F0b3I9IjI3MTo1My0yNzE6NjMiIHBhdGg9InZhbHVlIiB4c2k6dHlwZT0iUHJvcGVydHkiPgogICAgICAgICAgICA8c291cmNlIGxvY2F0b3I9IjI3MTo1My0yNzE6NTciIG5hbWU9InZhbHVlIiB4c2k6dHlwZT0iT3BlcmFuZFJlZiIvPgogICAgICAgICA8L2V4cHJlc3Npb24+CiAgICAgICAgIDxvcGVyYW5kIG5hbWU9InZhbHVlIj4KICAgICAgICAgICAgPG9wZXJhbmRUeXBlU3BlY2lmaWVyIGxvY2F0b3I9IjI3MTozMi0yNzE6NDkiIG5hbWU9ImZoaXI6R3VpZGVQYXJhbWV0ZXJDb2RlIiB4c2k6dHlwZT0iTmFtZWRUeXBlU3BlY2lmaWVyIi8+CiAgICAgICAgIDwvb3BlcmFuZD4KICAgICAgPC9kZWY+CiAgICAgIDxkZWYgbG9jYXRvcj0iMjcyOjEtMjcyOjUzIiBuYW1lPSJUb1N0cmluZyIgY29udGV4dD0iUGF0aWVudCIgYWNjZXNzTGV2ZWw9IlB1YmxpYyIgeHNpOnR5cGU9IkZ1bmN0aW9uRGVmIj4KICAgICAgICAgPGV4cHJlc3Npb24gbG9jYXRvcj0iMjcyOjQzLTI3Mjo1MyIgcGF0aD0idmFsdWUiIHhzaTp0eXBlPSJQcm9wZXJ0eSI+CiAgICAgICAgICAgIDxzb3VyY2UgbG9jYXRvcj0iMjcyOjQzLTI3Mjo0NyIgbmFtZT0idmFsdWUiIHhzaTp0eXBlPSJPcGVyYW5kUmVmIi8+CiAgICAgICAgIDwvZXhwcmVzc2lvbj4KICAgICAgICAgPG9wZXJhbmQgbmFtZT0idmFsdWUiPgogICAgICAgICAgICA8b3BlcmFuZFR5cGVTcGVjaWZpZXIgbG9jYXRvcj0iMjcyOjMyLTI3MjozOSIgbmFtZT0iZmhpcjpIVFRQVmVyYiIgeHNpOnR5cGU9Ik5hbWVkVHlwZVNwZWNpZmllciIvPgogICAgICAgICA8L29wZXJhbmQ+CiAgICAgIDwvZGVmPgogICAgICA8ZGVmIGxvY2F0b3I9IjI3MzoxLTI3Mzo1OCIgbmFtZT0iVG9TdHJpbmciIGNvbnRleHQ9IlBhdGllbnQiIGFjY2Vzc0xldmVsPSJQdWJsaWMiIHhzaTp0eXBlPSJGdW5jdGlvbkRlZiI+CiAgICAgICAgIDxleHByZXNzaW9uIGxvY2F0b3I9IjI3Mzo0OC0yNzM6NTgiIHBhdGg9InZhbHVlIiB4c2k6dHlwZT0iUHJvcGVydHkiPgogICAgICAgICAgICA8c291cmNlIGxvY2F0b3I9IjI3Mzo0OC0yNzM6NTIiIG5hbWU9InZhbHVlIiB4c2k6dHlwZT0iT3BlcmFuZFJlZiIvPgogICAgICAgICA8L2V4cHJlc3Npb24+CiAgICAgICAgIDxvcGVyYW5kIG5hbWU9InZhbHVlIj4KICAgICAgICAgICAgPG9wZXJhbmRUeXBlU3BlY2lmaWVyIGxvY2F0b3I9IjI3MzozMi0yNzM6NDQiIG5hbWU9ImZoaXI6SWRlbnRpZmllclVzZSIgeHNpOnR5cGU9Ik5hbWVkVHlwZVNwZWNpZmllciIvPgogICAgICAgICA8L29wZXJhbmQ+CiAgICAgIDwvZGVmPgogICAgICA8ZGVmIGxvY2F0b3I9IjI3NDoxLTI3NDo2NyIgbmFtZT0iVG9TdHJpbmciIGNvbnRleHQ9IlBhdGllbnQiIGFjY2Vzc0xldmVsPSJQdWJsaWMiIHhzaTp0eXBlPSJGdW5jdGlvbkRlZiI+CiAgICAgICAgIDxleHByZXNzaW9uIGxvY2F0b3I9IjI3NDo1Ny0yNzQ6NjciIHBhdGg9InZhbHVlIiB4c2k6dHlwZT0iUHJvcGVydHkiPgogICAgICAgICAgICA8c291cmNlIGxvY2F0b3I9IjI3NDo1Ny0yNzQ6NjEiIG5hbWU9InZhbHVlIiB4c2k6dHlwZT0iT3BlcmFuZFJlZiIvPgogICAgICAgICA8L2V4cHJlc3Npb24+CiAgICAgICAgIDxvcGVyYW5kIG5hbWU9InZhbHVlIj4KICAgICAgICAgICAgPG9wZXJhbmRUeXBlU3BlY2lmaWVyIGxvY2F0b3I9IjI3NDozMi0yNzQ6NTMiIG5hbWU9ImZoaXI6SWRlbnRpdHlBc3N1cmFuY2VMZXZlbCIgeHNpOnR5cGU9Ik5hbWVkVHlwZVNwZWNpZmllciIvPgogICAgICAgICA8L29wZXJhbmQ+CiAgICAgIDwvZGVmPgogICAgICA8ZGVmIGxvY2F0b3I9IjI3NToxLTI3NTo2MyIgbmFtZT0iVG9TdHJpbmciIGNvbnRleHQ9IlBhdGllbnQiIGFjY2Vzc0xldmVsPSJQdWJsaWMiIHhzaTp0eXBlPSJGdW5jdGlvbkRlZiI+CiAgICAgICAgIDxleHByZXNzaW9uIGxvY2F0b3I9IjI3NTo1My0yNzU6NjMiIHBhdGg9InZhbHVlIiB4c2k6dHlwZT0iUHJvcGVydHkiPgogICAgICAgICAgICA8c291cmNlIGxvY2F0b3I9IjI3NTo1My0yNzU6NTciIG5hbWU9InZhbHVlIiB4c2k6dHlwZT0iT3BlcmFuZFJlZiIvPgogICAgICAgICA8L2V4cHJlc3Npb24+CiAgICAgICAgIDxvcGVyYW5kIG5hbWU9InZhbHVlIj4KICAgICAgICAgICAgPG9wZXJhbmRUeXBlU3BlY2lmaWVyIGxvY2F0b3I9IjI3NTozMi0yNzU6NDkiIG5hbWU9ImZoaXI6SW1hZ2luZ1N0dWR5U3RhdHVzIiB4c2k6dHlwZT0iTmFtZWRUeXBlU3BlY2lmaWVyIi8+CiAgICAgICAgIDwvb3BlcmFuZD4KICAgICAgPC9kZWY+CiAgICAgIDxkZWYgbG9jYXRvcj0iMjc2OjEtMjc2OjczIiBuYW1lPSJUb1N0cmluZyIgY29udGV4dD0iUGF0aWVudCIgYWNjZXNzTGV2ZWw9IlB1YmxpYyIgeHNpOnR5cGU9IkZ1bmN0aW9uRGVmIj4KICAgICAgICAgPGV4cHJlc3Npb24gbG9jYXRvcj0iMjc2OjYzLTI3Njo3MyIgcGF0aD0idmFsdWUiIHhzaTp0eXBlPSJQcm9wZXJ0eSI+CiAgICAgICAgICAgIDxzb3VyY2UgbG9jYXRvcj0iMjc2OjYzLTI3Njo2NyIgbmFtZT0idmFsdWUiIHhzaTp0eXBlPSJPcGVyYW5kUmVmIi8+CiAgICAgICAgIDwvZXhwcmVzc2lvbj4KICAgICAgICAgPG9wZXJhbmQgbmFtZT0idmFsdWUiPgogICAgICAgICAgICA8b3BlcmFuZFR5cGVTcGVjaWZpZXIgbG9jYXRvcj0iMjc2OjMyLTI3Njo1OSIgbmFtZT0iZmhpcjpJbW11bml6YXRpb25FdmFsdWF0aW9uU3RhdHVzIiB4c2k6dHlwZT0iTmFtZWRUeXBlU3BlY2lmaWVyIi8+CiAgICAgICAgIDwvb3BlcmFuZD4KICAgICAgPC9kZWY+CiAgICAgIDxkZWYgbG9jYXRvcj0iMjc3OjEtMjc3OjYzIiBuYW1lPSJUb1N0cmluZyIgY29udGV4dD0iUGF0aWVudCIgYWNjZXNzTGV2ZWw9IlB1YmxpYyIgeHNpOnR5cGU9IkZ1bmN0aW9uRGVmIj4KICAgICAgICAgPGV4cHJlc3Npb24gbG9jYXRvcj0iMjc3OjUzLTI3Nzo2MyIgcGF0aD0idmFsdWUiIHhzaTp0eXBlPSJQcm9wZXJ0eSI+CiAgICAgICAgICAgIDxzb3VyY2UgbG9jYXRvcj0iMjc3OjUzLTI3Nzo1NyIgbmFtZT0idmFsdWUiIHhzaTp0eXBlPSJPcGVyYW5kUmVmIi8+CiAgICAgICAgIDwvZXhwcmVzc2lvbj4KICAgICAgICAgPG9wZXJhbmQgbmFtZT0idmFsdWUiPgogICAgICAgICAgICA8b3BlcmFuZFR5cGVTcGVjaWZpZXIgbG9jYXRvcj0iMjc3OjMyLTI3Nzo0OSIgbmFtZT0iZmhpcjpJbW11bml6YXRpb25TdGF0dXMiIHhzaTp0eXBlPSJOYW1lZFR5cGVTcGVjaWZpZXIiLz4KICAgICAgICAgPC9vcGVyYW5kPgogICAgICA8L2RlZj4KICAgICAgPGRlZiBsb2NhdG9yPSIyNzg6MS0yNzg6NzAiIG5hbWU9IlRvU3RyaW5nIiBjb250ZXh0PSJQYXRpZW50IiBhY2Nlc3NMZXZlbD0iUHVibGljIiB4c2k6dHlwZT0iRnVuY3Rpb25EZWYiPgogICAgICAgICA8ZXhwcmVzc2lvbiBsb2NhdG9yPSIyNzg6NjAtMjc4OjcwIiBwYXRoPSJ2YWx1ZSIgeHNpOnR5cGU9IlByb3BlcnR5Ij4KICAgICAgICAgICAgPHNvdXJjZSBsb2NhdG9yPSIyNzg6NjAtMjc4OjY0IiBuYW1lPSJ2YWx1ZSIgeHNpOnR5cGU9Ik9wZXJhbmRSZWYiLz4KICAgICAgICAgPC9leHByZXNzaW9uPgogICAgICAgICA8b3BlcmFuZCBuYW1lPSJ2YWx1ZSI+CiAgICAgICAgICAgIDxvcGVyYW5kVHlwZVNwZWNpZmllciBsb2NhdG9yPSIyNzg6MzItMjc4OjU2IiBuYW1lPSJmaGlyOkludm9pY2VQcmljZUNvbXBvbmVudFR5cGUiIHhzaTp0eXBlPSJOYW1lZFR5cGVTcGVjaWZpZXIiLz4KICAgICAgICAgPC9vcGVyYW5kPgogICAgICA8L2RlZj4KICAgICAgPGRlZiBsb2NhdG9yPSIyNzk6MS0yNzk6NTgiIG5hbWU9IlRvU3RyaW5nIiBjb250ZXh0PSJQYXRpZW50IiBhY2Nlc3NMZXZlbD0iUHVibGljIiB4c2k6dHlwZT0iRnVuY3Rpb25EZWYiPgogICAgICAgICA8ZXhwcmVzc2lvbiBsb2NhdG9yPSIyNzk6NDgtMjc5OjU4IiBwYXRoPSJ2YWx1ZSIgeHNpOnR5cGU9IlByb3BlcnR5Ij4KICAgICAgICAgICAgPHNvdXJjZSBsb2NhdG9yPSIyNzk6NDgtMjc5OjUyIiBuYW1lPSJ2YWx1ZSIgeHNpOnR5cGU9Ik9wZXJhbmRSZWYiLz4KICAgICAgICAgPC9leHByZXNzaW9uPgogICAgICAgICA8b3BlcmFuZCBuYW1lPSJ2YWx1ZSI+CiAgICAgICAgICAgIDxvcGVyYW5kVHlwZVNwZWNpZmllciBsb2NhdG9yPSIyNzk6MzItMjc5OjQ0IiBuYW1lPSJmaGlyOkludm9pY2VTdGF0dXMiIHhzaTp0eXBlPSJOYW1lZFR5cGVTcGVjaWZpZXIiLz4KICAgICAgICAgPC9vcGVyYW5kPgogICAgICA8L2RlZj4KICAgICAgPGRlZiBsb2NhdG9yPSIyODA6MS0yODA6NTgiIG5hbWU9IlRvU3RyaW5nIiBjb250ZXh0PSJQYXRpZW50IiBhY2Nlc3NMZXZlbD0iUHVibGljIiB4c2k6dHlwZT0iRnVuY3Rpb25EZWYiPgogICAgICAgICA8ZXhwcmVzc2lvbiBsb2NhdG9yPSIyODA6NDgtMjgwOjU4IiBwYXRoPSJ2YWx1ZSIgeHNpOnR5cGU9IlByb3BlcnR5Ij4KICAgICAgICAgICAgPHNvdXJjZSBsb2NhdG9yPSIyODA6NDgtMjgwOjUyIiBuYW1lPSJ2YWx1ZSIgeHNpOnR5cGU9Ik9wZXJhbmRSZWYiLz4KICAgICAgICAgPC9leHByZXNzaW9uPgogICAgICAgICA8b3BlcmFuZCBuYW1lPSJ2YWx1ZSI+CiAgICAgICAgICAgIDxvcGVyYW5kVHlwZVNwZWNpZmllciBsb2NhdG9yPSIyODA6MzItMjgwOjQ0IiBuYW1lPSJmaGlyOklzc3VlU2V2ZXJpdHkiIHhzaTp0eXBlPSJOYW1lZFR5cGVTcGVjaWZpZXIiLz4KICAgICAgICAgPC9vcGVyYW5kPgogICAgICA8L2RlZj4KICAgICAgPGRlZiBsb2NhdG9yPSIyODE6MS0yODE6NTQiIG5hbWU9IlRvU3RyaW5nIiBjb250ZXh0PSJQYXRpZW50IiBhY2Nlc3NMZXZlbD0iUHVibGljIiB4c2k6dHlwZT0iRnVuY3Rpb25EZWYiPgogICAgICAgICA8ZXhwcmVzc2lvbiBsb2NhdG9yPSIyODE6NDQtMjgxOjU0IiBwYXRoPSJ2YWx1ZSIgeHNpOnR5cGU9IlByb3BlcnR5Ij4KICAgICAgICAgICAgPHNvdXJjZSBsb2NhdG9yPSIyODE6NDQtMjgxOjQ4IiBuYW1lPSJ2YWx1ZSIgeHNpOnR5cGU9Ik9wZXJhbmRSZWYiLz4KICAgICAgICAgPC9leHByZXNzaW9uPgogICAgICAgICA8b3BlcmFuZCBuYW1lPSJ2YWx1ZSI+CiAgICAgICAgICAgIDxvcGVyYW5kVHlwZVNwZWNpZmllciBsb2NhdG9yPSIyODE6MzItMjgxOjQwIiBuYW1lPSJmaGlyOklzc3VlVHlwZSIgeHNpOnR5cGU9Ik5hbWVkVHlwZVNwZWNpZmllciIvPgogICAgICAgICA8L29wZXJhbmQ+CiAgICAgIDwvZGVmPgogICAgICA8ZGVmIGxvY2F0b3I9IjI4MjoxLTI4Mjo1MyIgbmFtZT0iVG9TdHJpbmciIGNvbnRleHQ9IlBhdGllbnQiIGFjY2Vzc0xldmVsPSJQdWJsaWMiIHhzaTp0eXBlPSJGdW5jdGlvbkRlZiI+CiAgICAgICAgIDxleHByZXNzaW9uIGxvY2F0b3I9IjI4Mjo0My0yODI6NTMiIHBhdGg9InZhbHVlIiB4c2k6dHlwZT0iUHJvcGVydHkiPgogICAgICAgICAgICA8c291cmNlIGxvY2F0b3I9IjI4Mjo0My0yODI6NDciIG5hbWU9InZhbHVlIiB4c2k6dHlwZT0iT3BlcmFuZFJlZiIvPgogICAgICAgICA8L2V4cHJlc3Npb24+CiAgICAgICAgIDxvcGVyYW5kIG5hbWU9InZhbHVlIj4KICAgICAgICAgICAgPG9wZXJhbmRUeXBlU3BlY2lmaWVyIGxvY2F0b3I9IjI4MjozMi0yODI6MzkiIG5hbWU9ImZoaXI6TGlua1R5cGUiIHhzaTp0eXBlPSJOYW1lZFR5cGVTcGVjaWZpZXIiLz4KICAgICAgICAgPC9vcGVyYW5kPgogICAgICA8L2RlZj4KICAgICAgPGRlZiBsb2NhdG9yPSIyODM6MS0yODM6NTYiIG5hbWU9IlRvU3RyaW5nIiBjb250ZXh0PSJQYXRpZW50IiBhY2Nlc3NMZXZlbD0iUHVibGljIiB4c2k6dHlwZT0iRnVuY3Rpb25EZWYiPgogICAgICAgICA8ZXhwcmVzc2lvbiBsb2NhdG9yPSIyODM6NDYtMjgzOjU2IiBwYXRoPSJ2YWx1ZSIgeHNpOnR5cGU9IlByb3BlcnR5Ij4KICAgICAgICAgICAgPHNvdXJjZSBsb2NhdG9yPSIyODM6NDYtMjgzOjUwIiBuYW1lPSJ2YWx1ZSIgeHNpOnR5cGU9Ik9wZXJhbmRSZWYiLz4KICAgICAgICAgPC9leHByZXNzaW9uPgogICAgICAgICA8b3BlcmFuZCBuYW1lPSJ2YWx1ZSI+CiAgICAgICAgICAgIDxvcGVyYW5kVHlwZVNwZWNpZmllciBsb2NhdG9yPSIyODM6MzItMjgzOjQyIiBuYW1lPSJmaGlyOkxpbmthZ2VUeXBlIiB4c2k6dHlwZT0iTmFtZWRUeXBlU3BlY2lmaWVyIi8+CiAgICAgICAgIDwvb3BlcmFuZD4KICAgICAgPC9kZWY+CiAgICAgIDxkZWYgbG9jYXRvcj0iMjg0OjEtMjg0OjUzIiBuYW1lPSJUb1N0cmluZyIgY29udGV4dD0iUGF0aWVudCIgYWNjZXNzTGV2ZWw9IlB1YmxpYyIgeHNpOnR5cGU9IkZ1bmN0aW9uRGVmIj4KICAgICAgICAgPGV4cHJlc3Npb24gbG9jYXRvcj0iMjg0OjQzLTI4NDo1MyIgcGF0aD0idmFsdWUiIHhzaTp0eXBlPSJQcm9wZXJ0eSI+CiAgICAgICAgICAgIDxzb3VyY2UgbG9jYXRvcj0iMjg0OjQzLTI4NDo0NyIgbmFtZT0idmFsdWUiIHhzaTp0eXBlPSJPcGVyYW5kUmVmIi8+CiAgICAgICAgIDwvZXhwcmVzc2lvbj4KICAgICAgICAgPG9wZXJhbmQgbmFtZT0idmFsdWUiPgogICAgICAgICAgICA8b3BlcmFuZFR5cGVTcGVjaWZpZXIgbG9jYXRvcj0iMjg0OjMyLTI4NDozOSIgbmFtZT0iZmhpcjpMaXN0TW9kZSIgeHNpOnR5cGU9Ik5hbWVkVHlwZVNwZWNpZmllciIvPgogICAgICAgICA8L29wZXJhbmQ+CiAgICAgIDwvZGVmPgogICAgICA8ZGVmIGxvY2F0b3I9IjI4NToxLTI4NTo1NSIgbmFtZT0iVG9TdHJpbmciIGNvbnRleHQ9IlBhdGllbnQiIGFjY2Vzc0xldmVsPSJQdWJsaWMiIHhzaTp0eXBlPSJGdW5jdGlvbkRlZiI+CiAgICAgICAgIDxleHByZXNzaW9uIGxvY2F0b3I9IjI4NTo0NS0yODU6NTUiIHBhdGg9InZhbHVlIiB4c2k6dHlwZT0iUHJvcGVydHkiPgogICAgICAgICAgICA8c291cmNlIGxvY2F0b3I9IjI4NTo0NS0yODU6NDkiIG5hbWU9InZhbHVlIiB4c2k6dHlwZT0iT3BlcmFuZFJlZiIvPgogICAgICAgICA8L2V4cHJlc3Npb24+CiAgICAgICAgIDxvcGVyYW5kIG5hbWU9InZhbHVlIj4KICAgICAgICAgICAgPG9wZXJhbmRUeXBlU3BlY2lmaWVyIGxvY2F0b3I9IjI4NTozMi0yODU6NDEiIG5hbWU9ImZoaXI6TGlzdFN0YXR1cyIgeHNpOnR5cGU9Ik5hbWVkVHlwZVNwZWNpZmllciIvPgogICAgICAgICA8L29wZXJhbmQ+CiAgICAgIDwvZGVmPgogICAgICA8ZGVmIGxvY2F0b3I9IjI4NjoxLTI4Njo1NyIgbmFtZT0iVG9TdHJpbmciIGNvbnRleHQ9IlBhdGllbnQiIGFjY2Vzc0xldmVsPSJQdWJsaWMiIHhzaTp0eXBlPSJGdW5jdGlvbkRlZiI+CiAgICAgICAgIDxleHByZXNzaW9uIGxvY2F0b3I9IjI4Njo0Ny0yODY6NTciIHBhdGg9InZhbHVlIiB4c2k6dHlwZT0iUHJvcGVydHkiPgogICAgICAgICAgICA8c291cmNlIGxvY2F0b3I9IjI4Njo0Ny0yODY6NTEiIG5hbWU9InZhbHVlIiB4c2k6dHlwZT0iT3BlcmFuZFJlZiIvPgogICAgICAgICA8L2V4cHJlc3Npb24+CiAgICAgICAgIDxvcGVyYW5kIG5hbWU9InZhbHVlIj4KICAgICAgICAgICAgPG9wZXJhbmRUeXBlU3BlY2lmaWVyIGxvY2F0b3I9IjI4NjozMi0yODY6NDMiIG5hbWU9ImZoaXI6TG9jYXRpb25Nb2RlIiB4c2k6dHlwZT0iTmFtZWRUeXBlU3BlY2lmaWVyIi8+CiAgICAgICAgIDwvb3BlcmFuZD4KICAgICAgPC9kZWY+CiAgICAgIDxkZWYgbG9jYXRvcj0iMjg3OjEtMjg3OjU5IiBuYW1lPSJUb1N0cmluZyIgY29udGV4dD0iUGF0aWVudCIgYWNjZXNzTGV2ZWw9IlB1YmxpYyIgeHNpOnR5cGU9IkZ1bmN0aW9uRGVmIj4KICAgICAgICAgPGV4cHJlc3Npb24gbG9jYXRvcj0iMjg3OjQ5LTI4Nzo1OSIgcGF0aD0idmFsdWUiIHhzaTp0eXBlPSJQcm9wZXJ0eSI+CiAgICAgICAgICAgIDxzb3VyY2UgbG9jYXRvcj0iMjg3OjQ5LTI4Nzo1MyIgbmFtZT0idmFsdWUiIHhzaTp0eXBlPSJPcGVyYW5kUmVmIi8+CiAgICAgICAgIDwvZXhwcmVzc2lvbj4KICAgICAgICAgPG9wZXJhbmQgbmFtZT0idmFsdWUiPgogICAgICAgICAgICA8b3BlcmFuZFR5cGVTcGVjaWZpZXIgbG9jYXRvcj0iMjg3OjMyLTI4Nzo0NSIgbmFtZT0iZmhpcjpMb2NhdGlvblN0YXR1cyIgeHNpOnR5cGU9Ik5hbWVkVHlwZVNwZWNpZmllciIvPgogICAgICAgICA8L29wZXJhbmQ+CiAgICAgIDwvZGVmPgogICAgICA8ZGVmIGxvY2F0b3I9IjI4ODoxLTI4ODo2NCIgbmFtZT0iVG9TdHJpbmciIGNvbnRleHQ9IlBhdGllbnQiIGFjY2Vzc0xldmVsPSJQdWJsaWMiIHhzaTp0eXBlPSJGdW5jdGlvbkRlZiI+CiAgICAgICAgIDxleHByZXNzaW9uIGxvY2F0b3I9IjI4ODo1NC0yODg6NjQiIHBhdGg9InZhbHVlIiB4c2k6dHlwZT0iUHJvcGVydHkiPgogICAgICAgICAgICA8c291cmNlIGxvY2F0b3I9IjI4ODo1NC0yODg6NTgiIG5hbWU9InZhbHVlIiB4c2k6dHlwZT0iT3BlcmFuZFJlZiIvPgogICAgICAgICA8L2V4cHJlc3Npb24+CiAgICAgICAgIDxvcGVyYW5kIG5hbWU9InZhbHVlIj4KICAgICAgICAgICAgPG9wZXJhbmRUeXBlU3BlY2lmaWVyIGxvY2F0b3I9IjI4ODozMi0yODg6NTAiIG5hbWU9ImZoaXI6TWVhc3VyZVJlcG9ydFN0YXR1cyIgeHNpOnR5cGU9Ik5hbWVkVHlwZVNwZWNpZmllciIvPgogICAgICAgICA8L29wZXJhbmQ+CiAgICAgIDwvZGVmPgogICAgICA8ZGVmIGxvY2F0b3I9IjI4OToxLTI4OTo2MiIgbmFtZT0iVG9TdHJpbmciIGNvbnRleHQ9IlBhdGllbnQiIGFjY2Vzc0xldmVsPSJQdWJsaWMiIHhzaTp0eXBlPSJGdW5jdGlvbkRlZiI+CiAgICAgICAgIDxleHByZXNzaW9uIGxvY2F0b3I9IjI4OTo1Mi0yODk6NjIiIHBhdGg9InZhbHVlIiB4c2k6dHlwZT0iUHJvcGVydHkiPgogICAgICAgICAgICA8c291cmNlIGxvY2F0b3I9IjI4OTo1Mi0yODk6NTYiIG5hbWU9InZhbHVlIiB4c2k6dHlwZT0iT3BlcmFuZFJlZiIvPgogICAgICAgICA8L2V4cHJlc3Npb24+CiAgICAgICAgIDxvcGVyYW5kIG5hbWU9InZhbHVlIj4KICAgICAgICAgICAgPG9wZXJhbmRUeXBlU3BlY2lmaWVyIGxvY2F0b3I9IjI4OTozMi0yODk6NDgiIG5hbWU9ImZoaXI6TWVhc3VyZVJlcG9ydFR5cGUiIHhzaTp0eXBlPSJOYW1lZFR5cGVTcGVjaWZpZXIiLz4KICAgICAgICAgPC9vcGVyYW5kPgogICAgICA8L2RlZj4KICAgICAgPGRlZiBsb2NhdG9yPSIyOTA6MS0yOTA6NTYiIG5hbWU9IlRvU3RyaW5nIiBjb250ZXh0PSJQYXRpZW50IiBhY2Nlc3NMZXZlbD0iUHVibGljIiB4c2k6dHlwZT0iRnVuY3Rpb25EZWYiPgogICAgICAgICA8ZXhwcmVzc2lvbiBsb2NhdG9yPSIyOTA6NDYtMjkwOjU2IiBwYXRoPSJ2YWx1ZSIgeHNpOnR5cGU9IlByb3BlcnR5Ij4KICAgICAgICAgICAgPHNvdXJjZSBsb2NhdG9yPSIyOTA6NDYtMjkwOjUwIiBuYW1lPSJ2YWx1ZSIgeHNpOnR5cGU9Ik9wZXJhbmRSZWYiLz4KICAgICAgICAgPC9leHByZXNzaW9uPgogICAgICAgICA8b3BlcmFuZCBuYW1lPSJ2YWx1ZSI+CiAgICAgICAgICAgIDxvcGVyYW5kVHlwZVNwZWNpZmllciBsb2NhdG9yPSIyOTA6MzItMjkwOjQyIiBuYW1lPSJmaGlyOk1lZGlhU3RhdHVzIiB4c2k6dHlwZT0iTmFtZWRUeXBlU3BlY2lmaWVyIi8+CiAgICAgICAgIDwvb3BlcmFuZD4KICAgICAgPC9kZWY+CiAgICAgIDxkZWYgbG9jYXRvcj0iMjkxOjEtMjkxOjc1IiBuYW1lPSJUb1N0cmluZyIgY29udGV4dD0iUGF0aWVudCIgYWNjZXNzTGV2ZWw9IlB1YmxpYyIgeHNpOnR5cGU9IkZ1bmN0aW9uRGVmIj4KICAgICAgICAgPGV4cHJlc3Npb24gbG9jYXRvcj0iMjkxOjY1LTI5MTo3NSIgcGF0aD0idmFsdWUiIHhzaTp0eXBlPSJQcm9wZXJ0eSI+CiAgICAgICAgICAgIDxzb3VyY2UgbG9jYXRvcj0iMjkxOjY1LTI5MTo2OSIgbmFtZT0idmFsdWUiIHhzaTp0eXBlPSJPcGVyYW5kUmVmIi8+CiAgICAgICAgIDwvZXhwcmVzc2lvbj4KICAgICAgICAgPG9wZXJhbmQgbmFtZT0idmFsdWUiPgogICAgICAgICAgICA8b3BlcmFuZFR5cGVTcGVjaWZpZXIgbG9jYXRvcj0iMjkxOjMyLTI5MTo2MSIgbmFtZT0iZmhpcjpNZWRpY2F0aW9uQWRtaW5pc3RyYXRpb25TdGF0dXMiIHhzaTp0eXBlPSJOYW1lZFR5cGVTcGVjaWZpZXIiLz4KICAgICAgICAgPC9vcGVyYW5kPgogICAgICA8L2RlZj4KICAgICAgPGRlZiBsb2NhdG9yPSIyOTI6MS0yOTI6NjkiIG5hbWU9IlRvU3RyaW5nIiBjb250ZXh0PSJQYXRpZW50IiBhY2Nlc3NMZXZlbD0iUHVibGljIiB4c2k6dHlwZT0iRnVuY3Rpb25EZWYiPgogICAgICAgICA8ZXhwcmVzc2lvbiBsb2NhdG9yPSIyOTI6NTktMjkyOjY5IiBwYXRoPSJ2YWx1ZSIgeHNpOnR5cGU9IlByb3BlcnR5Ij4KICAgICAgICAgICAgPHNvdXJjZSBsb2NhdG9yPSIyOTI6NTktMjkyOjYzIiBuYW1lPSJ2YWx1ZSIgeHNpOnR5cGU9Ik9wZXJhbmRSZWYiLz4KICAgICAgICAgPC9leHByZXNzaW9uPgogICAgICAgICA8b3BlcmFuZCBuYW1lPSJ2YWx1ZSI+CiAgICAgICAgICAgIDxvcGVyYW5kVHlwZVNwZWNpZmllciBsb2NhdG9yPSIyOTI6MzItMjkyOjU1IiBuYW1lPSJmaGlyOk1lZGljYXRpb25EaXNwZW5zZVN0YXR1cyIgeHNpOnR5cGU9Ik5hbWVkVHlwZVNwZWNpZmllciIvPgogICAgICAgICA8L29wZXJhbmQ+CiAgICAgIDwvZGVmPgogICAgICA8ZGVmIGxvY2F0b3I9IjI5MzoxLTI5Mzo3MCIgbmFtZT0iVG9TdHJpbmciIGNvbnRleHQ9IlBhdGllbnQiIGFjY2Vzc0xldmVsPSJQdWJsaWMiIHhzaTp0eXBlPSJGdW5jdGlvbkRlZiI+CiAgICAgICAgIDxleHByZXNzaW9uIGxvY2F0b3I9IjI5Mzo2MC0yOTM6NzAiIHBhdGg9InZhbHVlIiB4c2k6dHlwZT0iUHJvcGVydHkiPgogICAgICAgICAgICA8c291cmNlIGxvY2F0b3I9IjI5Mzo2MC0yOTM6NjQiIG5hbWU9InZhbHVlIiB4c2k6dHlwZT0iT3BlcmFuZFJlZiIvPgogICAgICAgICA8L2V4cHJlc3Npb24+CiAgICAgICAgIDxvcGVyYW5kIG5hbWU9InZhbHVlIj4KICAgICAgICAgICAgPG9wZXJhbmRUeXBlU3BlY2lmaWVyIGxvY2F0b3I9IjI5MzozMi0yOTM6NTYiIG5hbWU9ImZoaXI6TWVkaWNhdGlvbktub3dsZWRnZVN0YXR1cyIgeHNpOnR5cGU9Ik5hbWVkVHlwZVNwZWNpZmllciIvPgogICAgICAgICA8L29wZXJhbmQ+CiAgICAgIDwvZGVmPgogICAgICA8ZGVmIGxvY2F0b3I9IjI5NDoxLTI5NDo2OCIgbmFtZT0iVG9TdHJpbmciIGNvbnRleHQ9IlBhdGllbnQiIGFjY2Vzc0xldmVsPSJQdWJsaWMiIHhzaTp0eXBlPSJGdW5jdGlvbkRlZiI+CiAgICAgICAgIDxleHByZXNzaW9uIGxvY2F0b3I9IjI5NDo1OC0yOTQ6NjgiIHBhdGg9InZhbHVlIiB4c2k6dHlwZT0iUHJvcGVydHkiPgogICAgICAgICAgICA8c291cmNlIGxvY2F0b3I9IjI5NDo1OC0yOTQ6NjIiIG5hbWU9InZhbHVlIiB4c2k6dHlwZT0iT3BlcmFuZFJlZiIvPgogICAgICAgICA8L2V4cHJlc3Npb24+CiAgICAgICAgIDxvcGVyYW5kIG5hbWU9InZhbHVlIj4KICAgICAgICAgICAgPG9wZXJhbmRUeXBlU3BlY2lmaWVyIGxvY2F0b3I9IjI5NDozMi0yOTQ6NTQiIG5hbWU9ImZoaXI6TWVkaWNhdGlvblJlcXVlc3RJbnRlbnQiIHhzaTp0eXBlPSJOYW1lZFR5cGVTcGVjaWZpZXIiLz4KICAgICAgICAgPC9vcGVyYW5kPgogICAgICA8L2RlZj4KICAgICAgPGRlZiBsb2NhdG9yPSIyOTU6MS0yOTU6NzAiIG5hbWU9IlRvU3RyaW5nIiBjb250ZXh0PSJQYXRpZW50IiBhY2Nlc3NMZXZlbD0iUHVibGljIiB4c2k6dHlwZT0iRnVuY3Rpb25EZWYiPgogICAgICAgICA8ZXhwcmVzc2lvbiBsb2NhdG9yPSIyOTU6NjAtMjk1OjcwIiBwYXRoPSJ2YWx1ZSIgeHNpOnR5cGU9IlByb3BlcnR5Ij4KICAgICAgICAgICAgPHNvdXJjZSBsb2NhdG9yPSIyOTU6NjAtMjk1OjY0IiBuYW1lPSJ2YWx1ZSIgeHNpOnR5cGU9Ik9wZXJhbmRSZWYiLz4KICAgICAgICAgPC9leHByZXNzaW9uPgogICAgICAgICA8b3BlcmFuZCBuYW1lPSJ2YWx1ZSI+CiAgICAgICAgICAgIDxvcGVyYW5kVHlwZVNwZWNpZmllciBsb2NhdG9yPSIyOTU6MzItMjk1OjU2IiBuYW1lPSJmaGlyOk1lZGljYXRpb25SZXF1ZXN0UHJpb3JpdHkiIHhzaTp0eXBlPSJOYW1lZFR5cGVTcGVjaWZpZXIiLz4KICAgICAgICAgPC9vcGVyYW5kPgogICAgICA8L2RlZj4KICAgICAgPGRlZiBsb2NhdG9yPSIyOTY6MS0yOTY6NjgiIG5hbWU9IlRvU3RyaW5nIiBjb250ZXh0PSJQYXRpZW50IiBhY2Nlc3NMZXZlbD0iUHVibGljIiB4c2k6dHlwZT0iRnVuY3Rpb25EZWYiPgogICAgICAgICA8ZXhwcmVzc2lvbiBsb2NhdG9yPSIyOTY6NTgtMjk2OjY4IiBwYXRoPSJ2YWx1ZSIgeHNpOnR5cGU9IlByb3BlcnR5Ij4KICAgICAgICAgICAgPHNvdXJjZSBsb2NhdG9yPSIyOTY6NTgtMjk2OjYyIiBuYW1lPSJ2YWx1ZSIgeHNpOnR5cGU9Ik9wZXJhbmRSZWYiLz4KICAgICAgICAgPC9leHByZXNzaW9uPgogICAgICAgICA8b3BlcmFuZCBuYW1lPSJ2YWx1ZSI+CiAgICAgICAgICAgIDxvcGVyYW5kVHlwZVNwZWNpZmllciBsb2NhdG9yPSIyOTY6MzItMjk2OjU0IiBuYW1lPSJmaGlyOk1lZGljYXRpb25SZXF1ZXN0U3RhdHVzIiB4c2k6dHlwZT0iTmFtZWRUeXBlU3BlY2lmaWVyIi8+CiAgICAgICAgIDwvb3BlcmFuZD4KICAgICAgPC9kZWY+CiAgICAgIDxkZWYgbG9jYXRvcj0iMjk3OjEtMjk3OjcwIiBuYW1lPSJUb1N0cmluZyIgY29udGV4dD0iUGF0aWVudCIgYWNjZXNzTGV2ZWw9IlB1YmxpYyIgeHNpOnR5cGU9IkZ1bmN0aW9uRGVmIj4KICAgICAgICAgPGV4cHJlc3Npb24gbG9jYXRvcj0iMjk3OjYwLTI5Nzo3MCIgcGF0aD0idmFsdWUiIHhzaTp0eXBlPSJQcm9wZXJ0eSI+CiAgICAgICAgICAgIDxzb3VyY2UgbG9jYXRvcj0iMjk3OjYwLTI5Nzo2NCIgbmFtZT0idmFsdWUiIHhzaTp0eXBlPSJPcGVyYW5kUmVmIi8+CiAgICAgICAgIDwvZXhwcmVzc2lvbj4KICAgICAgICAgPG9wZXJhbmQgbmFtZT0idmFsdWUiPgogICAgICAgICAgICA8b3BlcmFuZFR5cGVTcGVjaWZpZXIgbG9jYXRvcj0iMjk3OjMyLTI5Nzo1NiIgbmFtZT0iZmhpcjpNZWRpY2F0aW9uU3RhdGVtZW50U3RhdHVzIiB4c2k6dHlwZT0iTmFtZWRUeXBlU3BlY2lmaWVyIi8+CiAgICAgICAgIDwvb3BlcmFuZD4KICAgICAgPC9kZWY+CiAgICAgIDxkZWYgbG9jYXRvcj0iMjk4OjEtMjk4OjYxIiBuYW1lPSJUb1N0cmluZyIgY29udGV4dD0iUGF0aWVudCIgYWNjZXNzTGV2ZWw9IlB1YmxpYyIgeHNpOnR5cGU9IkZ1bmN0aW9uRGVmIj4KICAgICAgICAgPGV4cHJlc3Npb24gbG9jYXRvcj0iMjk4OjUxLTI5ODo2MSIgcGF0aD0idmFsdWUiIHhzaTp0eXBlPSJQcm9wZXJ0eSI+CiAgICAgICAgICAgIDxzb3VyY2UgbG9jYXRvcj0iMjk4OjUxLTI5ODo1NSIgbmFtZT0idmFsdWUiIHhzaTp0eXBlPSJPcGVyYW5kUmVmIi8+CiAgICAgICAgIDwvZXhwcmVzc2lvbj4KICAgICAgICAgPG9wZXJhbmQgbmFtZT0idmFsdWUiPgogICAgICAgICAgICA8b3BlcmFuZFR5cGVTcGVjaWZpZXIgbG9jYXRvcj0iMjk4OjMyLTI5ODo0NyIgbmFtZT0iZmhpcjpNZWRpY2F0aW9uU3RhdHVzIiB4c2k6dHlwZT0iTmFtZWRUeXBlU3BlY2lmaWVyIi8+CiAgICAgICAgIDwvb3BlcmFuZD4KICAgICAgPC9kZWY+CiAgICAgIDxkZWYgbG9jYXRvcj0iMjk5OjEtMjk5OjcyIiBuYW1lPSJUb1N0cmluZyIgY29udGV4dD0iUGF0aWVudCIgYWNjZXNzTGV2ZWw9IlB1YmxpYyIgeHNpOnR5cGU9IkZ1bmN0aW9uRGVmIj4KICAgICAgICAgPGV4cHJlc3Npb24gbG9jYXRvcj0iMjk5OjYyLTI5OTo3MiIgcGF0aD0idmFsdWUiIHhzaTp0eXBlPSJQcm9wZXJ0eSI+CiAgICAgICAgICAgIDxzb3VyY2UgbG9jYXRvcj0iMjk5OjYyLTI5OTo2NiIgbmFtZT0idmFsdWUiIHhzaTp0eXBlPSJPcGVyYW5kUmVmIi8+CiAgICAgICAgIDwvZXhwcmVzc2lvbj4KICAgICAgICAgPG9wZXJhbmQgbmFtZT0idmFsdWUiPgogICAgICAgICAgICA8b3BlcmFuZFR5cGVTcGVjaWZpZXIgbG9jYXRvcj0iMjk5OjMyLTI5OTo1OCIgbmFtZT0iZmhpcjpNZXNzYWdlU2lnbmlmaWNhbmNlQ2F0ZWdvcnkiIHhzaTp0eXBlPSJOYW1lZFR5cGVTcGVjaWZpZXIiLz4KICAgICAgICAgPC9vcGVyYW5kPgogICAgICA8L2RlZj4KICAgICAgPGRlZiBsb2NhdG9yPSIzMDA6MS0zMDA6NzUiIG5hbWU9IlRvU3RyaW5nIiBjb250ZXh0PSJQYXRpZW50IiBhY2Nlc3NMZXZlbD0iUHVibGljIiB4c2k6dHlwZT0iRnVuY3Rpb25EZWYiPgogICAgICAgICA8ZXhwcmVzc2lvbiBsb2NhdG9yPSIzMDA6NjUtMzAwOjc1IiBwYXRoPSJ2YWx1ZSIgeHNpOnR5cGU9IlByb3BlcnR5Ij4KICAgICAgICAgICAgPHNvdXJjZSBsb2NhdG9yPSIzMDA6NjUtMzAwOjY5IiBuYW1lPSJ2YWx1ZSIgeHNpOnR5cGU9Ik9wZXJhbmRSZWYiLz4KICAgICAgICAgPC9leHByZXNzaW9uPgogICAgICAgICA8b3BlcmFuZCBuYW1lPSJ2YWx1ZSI+CiAgICAgICAgICAgIDxvcGVyYW5kVHlwZVNwZWNpZmllciBsb2NhdG9yPSIzMDA6MzItMzAwOjYxIiBuYW1lPSJmaGlyOk1lc3NhZ2VoZWFkZXJfUmVzcG9uc2VfUmVxdWVzdCIgeHNpOnR5cGU9Ik5hbWVkVHlwZVNwZWNpZmllciIvPgogICAgICAgICA8L29wZXJhbmQ+CiAgICAgIDwvZGVmPgogICAgICA8ZGVmIGxvY2F0b3I9IjMwMToxLTMwMTo1MyIgbmFtZT0iVG9TdHJpbmciIGNvbnRleHQ9IlBhdGllbnQiIGFjY2Vzc0xldmVsPSJQdWJsaWMiIHhzaTp0eXBlPSJGdW5jdGlvbkRlZiI+CiAgICAgICAgIDxleHByZXNzaW9uIGxvY2F0b3I9IjMwMTo0My0zMDE6NTMiIHBhdGg9InZhbHVlIiB4c2k6dHlwZT0iUHJvcGVydHkiPgogICAgICAgICAgICA8c291cmNlIGxvY2F0b3I9IjMwMTo0My0zMDE6NDciIG5hbWU9InZhbHVlIiB4c2k6dHlwZT0iT3BlcmFuZFJlZiIvPgogICAgICAgICA8L2V4cHJlc3Npb24+CiAgICAgICAgIDxvcGVyYW5kIG5hbWU9InZhbHVlIj4KICAgICAgICAgICAgPG9wZXJhbmRUeXBlU3BlY2lmaWVyIGxvY2F0b3I9IjMwMTozMi0zMDE6MzkiIG5hbWU9ImZoaXI6TWltZVR5cGUiIHhzaTp0eXBlPSJOYW1lZFR5cGVTcGVjaWZpZXIiLz4KICAgICAgICAgPC9vcGVyYW5kPgogICAgICA8L2RlZj4KICAgICAgPGRlZiBsb2NhdG9yPSIzMDI6MS0zMDI6NTIiIG5hbWU9IlRvU3RyaW5nIiBjb250ZXh0PSJQYXRpZW50IiBhY2Nlc3NMZXZlbD0iUHVibGljIiB4c2k6dHlwZT0iRnVuY3Rpb25EZWYiPgogICAgICAgICA8ZXhwcmVzc2lvbiBsb2NhdG9yPSIzMDI6NDItMzAyOjUyIiBwYXRoPSJ2YWx1ZSIgeHNpOnR5cGU9IlByb3BlcnR5Ij4KICAgICAgICAgICAgPHNvdXJjZSBsb2NhdG9yPSIzMDI6NDItMzAyOjQ2IiBuYW1lPSJ2YWx1ZSIgeHNpOnR5cGU9Ik9wZXJhbmRSZWYiLz4KICAgICAgICAgPC9leHByZXNzaW9uPgogICAgICAgICA8b3BlcmFuZCBuYW1lPSJ2YWx1ZSI+CiAgICAgICAgICAgIDxvcGVyYW5kVHlwZVNwZWNpZmllciBsb2NhdG9yPSIzMDI6MzItMzAyOjM4IiBuYW1lPSJmaGlyOk5hbWVVc2UiIHhzaTp0eXBlPSJOYW1lZFR5cGVTcGVjaWZpZXIiLz4KICAgICAgICAgPC9vcGVyYW5kPgogICAgICA8L2RlZj4KICAgICAgPGRlZiBsb2NhdG9yPSIzMDM6MS0zMDM6NzEiIG5hbWU9IlRvU3RyaW5nIiBjb250ZXh0PSJQYXRpZW50IiBhY2Nlc3NMZXZlbD0iUHVibGljIiB4c2k6dHlwZT0iRnVuY3Rpb25EZWYiPgogICAgICAgICA8ZXhwcmVzc2lvbiBsb2NhdG9yPSIzMDM6NjEtMzAzOjcxIiBwYXRoPSJ2YWx1ZSIgeHNpOnR5cGU9IlByb3BlcnR5Ij4KICAgICAgICAgICAgPHNvdXJjZSBsb2NhdG9yPSIzMDM6NjEtMzAzOjY1IiBuYW1lPSJ2YWx1ZSIgeHNpOnR5cGU9Ik9wZXJhbmRSZWYiLz4KICAgICAgICAgPC9leHByZXNzaW9uPgogICAgICAgICA8b3BlcmFuZCBuYW1lPSJ2YWx1ZSI+CiAgICAgICAgICAgIDxvcGVyYW5kVHlwZVNwZWNpZmllciBsb2NhdG9yPSIzMDM6MzItMzAzOjU3IiBuYW1lPSJmaGlyOk5hbWluZ1N5c3RlbUlkZW50aWZpZXJUeXBlIiB4c2k6dHlwZT0iTmFtZWRUeXBlU3BlY2lmaWVyIi8+CiAgICAgICAgIDwvb3BlcmFuZD4KICAgICAgPC9kZWY+CiAgICAgIDxkZWYgbG9jYXRvcj0iMzA0OjEtMzA0OjYxIiBuYW1lPSJUb1N0cmluZyIgY29udGV4dD0iUGF0aWVudCIgYWNjZXNzTGV2ZWw9IlB1YmxpYyIgeHNpOnR5cGU9IkZ1bmN0aW9uRGVmIj4KICAgICAgICAgPGV4cHJlc3Npb24gbG9jYXRvcj0iMzA0OjUxLTMwNDo2MSIgcGF0aD0idmFsdWUiIHhzaTp0eXBlPSJQcm9wZXJ0eSI+CiAgICAgICAgICAgIDxzb3VyY2UgbG9jYXRvcj0iMzA0OjUxLTMwNDo1NSIgbmFtZT0idmFsdWUiIHhzaTp0eXBlPSJPcGVyYW5kUmVmIi8+CiAgICAgICAgIDwvZXhwcmVzc2lvbj4KICAgICAgICAgPG9wZXJhbmQgbmFtZT0idmFsdWUiPgogICAgICAgICAgICA8b3BlcmFuZFR5cGVTcGVjaWZpZXIgbG9jYXRvcj0iMzA0OjMyLTMwNDo0NyIgbmFtZT0iZmhpcjpOYW1pbmdTeXN0ZW1UeXBlIiB4c2k6dHlwZT0iTmFtZWRUeXBlU3BlY2lmaWVyIi8+CiAgICAgICAgIDwvb3BlcmFuZD4KICAgICAgPC9kZWY+CiAgICAgIDxkZWYgbG9jYXRvcj0iMzA1OjEtMzA1OjYwIiBuYW1lPSJUb1N0cmluZyIgY29udGV4dD0iUGF0aWVudCIgYWNjZXNzTGV2ZWw9IlB1YmxpYyIgeHNpOnR5cGU9IkZ1bmN0aW9uRGVmIj4KICAgICAgICAgPGV4cHJlc3Npb24gbG9jYXRvcj0iMzA1OjUwLTMwNTo2MCIgcGF0aD0idmFsdWUiIHhzaTp0eXBlPSJQcm9wZXJ0eSI+CiAgICAgICAgICAgIDxzb3VyY2UgbG9jYXRvcj0iMzA1OjUwLTMwNTo1NCIgbmFtZT0idmFsdWUiIHhzaTp0eXBlPSJPcGVyYW5kUmVmIi8+CiAgICAgICAgIDwvZXhwcmVzc2lvbj4KICAgICAgICAgPG9wZXJhbmQgbmFtZT0idmFsdWUiPgogICAgICAgICAgICA8b3BlcmFuZFR5cGVTcGVjaWZpZXIgbG9jYXRvcj0iMzA1OjMyLTMwNTo0NiIgbmFtZT0iZmhpcjpOYXJyYXRpdmVTdGF0dXMiIHhzaTp0eXBlPSJOYW1lZFR5cGVTcGVjaWZpZXIiLz4KICAgICAgICAgPC9vcGVyYW5kPgogICAgICA8L2RlZj4KICAgICAgPGRlZiBsb2NhdG9yPSIzMDY6MS0zMDY6NTMiIG5hbWU9IlRvU3RyaW5nIiBjb250ZXh0PSJQYXRpZW50IiBhY2Nlc3NMZXZlbD0iUHVibGljIiB4c2k6dHlwZT0iRnVuY3Rpb25EZWYiPgogICAgICAgICA8ZXhwcmVzc2lvbiBsb2NhdG9yPSIzMDY6NDMtMzA2OjUzIiBwYXRoPSJ2YWx1ZSIgeHNpOnR5cGU9IlByb3BlcnR5Ij4KICAgICAgICAgICAgPHNvdXJjZSBsb2NhdG9yPSIzMDY6NDMtMzA2OjQ3IiBuYW1lPSJ2YWx1ZSIgeHNpOnR5cGU9Ik9wZXJhbmRSZWYiLz4KICAgICAgICAgPC9leHByZXNzaW9uPgogICAgICAgICA8b3BlcmFuZCBuYW1lPSJ2YWx1ZSI+CiAgICAgICAgICAgIDxvcGVyYW5kVHlwZVNwZWNpZmllciBsb2NhdG9yPSIzMDY6MzItMzA2OjM5IiBuYW1lPSJmaGlyOk5vdGVUeXBlIiB4c2k6dHlwZT0iTmFtZWRUeXBlU3BlY2lmaWVyIi8+CiAgICAgICAgIDwvb3BlcmFuZD4KICAgICAgPC9kZWY+CiAgICAgIDxkZWYgbG9jYXRvcj0iMzA3OjEtMzA3OjY2IiBuYW1lPSJUb1N0cmluZyIgY29udGV4dD0iUGF0aWVudCIgYWNjZXNzTGV2ZWw9IlB1YmxpYyIgeHNpOnR5cGU9IkZ1bmN0aW9uRGVmIj4KICAgICAgICAgPGV4cHJlc3Npb24gbG9jYXRvcj0iMzA3OjU2LTMwNzo2NiIgcGF0aD0idmFsdWUiIHhzaTp0eXBlPSJQcm9wZXJ0eSI+CiAgICAgICAgICAgIDxzb3VyY2UgbG9jYXRvcj0iMzA3OjU2LTMwNzo2MCIgbmFtZT0idmFsdWUiIHhzaTp0eXBlPSJPcGVyYW5kUmVmIi8+CiAgICAgICAgIDwvZXhwcmVzc2lvbj4KICAgICAgICAgPG9wZXJhbmQgbmFtZT0idmFsdWUiPgogICAgICAgICAgICA8b3BlcmFuZFR5cGVTcGVjaWZpZXIgbG9jYXRvcj0iMzA3OjMyLTMwNzo1MiIgbmFtZT0iZmhpcjpOdXRyaXRpaW9uT3JkZXJJbnRlbnQiIHhzaTp0eXBlPSJOYW1lZFR5cGVTcGVjaWZpZXIiLz4KICAgICAgICAgPC9vcGVyYW5kPgogICAgICA8L2RlZj4KICAgICAgPGRlZiBsb2NhdG9yPSIzMDg6MS0zMDg6NjUiIG5hbWU9IlRvU3RyaW5nIiBjb250ZXh0PSJQYXRpZW50IiBhY2Nlc3NMZXZlbD0iUHVibGljIiB4c2k6dHlwZT0iRnVuY3Rpb25EZWYiPgogICAgICAgICA8ZXhwcmVzc2lvbiBsb2NhdG9yPSIzMDg6NTUtMzA4OjY1IiBwYXRoPSJ2YWx1ZSIgeHNpOnR5cGU9IlByb3BlcnR5Ij4KICAgICAgICAgICAgPHNvdXJjZSBsb2NhdG9yPSIzMDg6NTUtMzA4OjU5IiBuYW1lPSJ2YWx1ZSIgeHNpOnR5cGU9Ik9wZXJhbmRSZWYiLz4KICAgICAgICAgPC9leHByZXNzaW9uPgogICAgICAgICA8b3BlcmFuZCBuYW1lPSJ2YWx1ZSI+CiAgICAgICAgICAgIDxvcGVyYW5kVHlwZVNwZWNpZmllciBsb2NhdG9yPSIzMDg6MzItMzA4OjUxIiBuYW1lPSJmaGlyOk51dHJpdGlvbk9yZGVyU3RhdHVzIiB4c2k6dHlwZT0iTmFtZWRUeXBlU3BlY2lmaWVyIi8+CiAgICAgICAgIDwvb3BlcmFuZD4KICAgICAgPC9kZWY+CiAgICAgIDxkZWYgbG9jYXRvcj0iMzA5OjEtMzA5OjY0IiBuYW1lPSJUb1N0cmluZyIgY29udGV4dD0iUGF0aWVudCIgYWNjZXNzTGV2ZWw9IlB1YmxpYyIgeHNpOnR5cGU9IkZ1bmN0aW9uRGVmIj4KICAgICAgICAgPGV4cHJlc3Npb24gbG9jYXRvcj0iMzA5OjU0LTMwOTo2NCIgcGF0aD0idmFsdWUiIHhzaTp0eXBlPSJQcm9wZXJ0eSI+CiAgICAgICAgICAgIDxzb3VyY2UgbG9jYXRvcj0iMzA5OjU0LTMwOTo1OCIgbmFtZT0idmFsdWUiIHhzaTp0eXBlPSJPcGVyYW5kUmVmIi8+CiAgICAgICAgIDwvZXhwcmVzc2lvbj4KICAgICAgICAgPG9wZXJhbmQgbmFtZT0idmFsdWUiPgogICAgICAgICAgICA8b3BlcmFuZFR5cGVTcGVjaWZpZXIgbG9jYXRvcj0iMzA5OjMyLTMwOTo1MCIgbmFtZT0iZmhpcjpPYnNlcnZhdGlvbkRhdGFUeXBlIiB4c2k6dHlwZT0iTmFtZWRUeXBlU3BlY2lmaWVyIi8+CiAgICAgICAgIDwvb3BlcmFuZD4KICAgICAgPC9kZWY+CiAgICAgIDxkZWYgbG9jYXRvcj0iMzEwOjEtMzEwOjY5IiBuYW1lPSJUb1N0cmluZyIgY29udGV4dD0iUGF0aWVudCIgYWNjZXNzTGV2ZWw9IlB1YmxpYyIgeHNpOnR5cGU9IkZ1bmN0aW9uRGVmIj4KICAgICAgICAgPGV4cHJlc3Npb24gbG9jYXRvcj0iMzEwOjU5LTMxMDo2OSIgcGF0aD0idmFsdWUiIHhzaTp0eXBlPSJQcm9wZXJ0eSI+CiAgICAgICAgICAgIDxzb3VyY2UgbG9jYXRvcj0iMzEwOjU5LTMxMDo2MyIgbmFtZT0idmFsdWUiIHhzaTp0eXBlPSJPcGVyYW5kUmVmIi8+CiAgICAgICAgIDwvZXhwcmVzc2lvbj4KICAgICAgICAgPG9wZXJhbmQgbmFtZT0idmFsdWUiPgogICAgICAgICAgICA8b3BlcmFuZFR5cGVTcGVjaWZpZXIgbG9jYXRvcj0iMzEwOjMyLTMxMDo1NSIgbmFtZT0iZmhpcjpPYnNlcnZhdGlvblJhbmdlQ2F0ZWdvcnkiIHhzaTp0eXBlPSJOYW1lZFR5cGVTcGVjaWZpZXIiLz4KICAgICAgICAgPC9vcGVyYW5kPgogICAgICA8L2RlZj4KICAgICAgPGRlZiBsb2NhdG9yPSIzMTE6MS0zMTE6NjIiIG5hbWU9IlRvU3RyaW5nIiBjb250ZXh0PSJQYXRpZW50IiBhY2Nlc3NMZXZlbD0iUHVibGljIiB4c2k6dHlwZT0iRnVuY3Rpb25EZWYiPgogICAgICAgICA8ZXhwcmVzc2lvbiBsb2NhdG9yPSIzMTE6NTItMzExOjYyIiBwYXRoPSJ2YWx1ZSIgeHNpOnR5cGU9IlByb3BlcnR5Ij4KICAgICAgICAgICAgPHNvdXJjZSBsb2NhdG9yPSIzMTE6NTItMzExOjU2IiBuYW1lPSJ2YWx1ZSIgeHNpOnR5cGU9Ik9wZXJhbmRSZWYiLz4KICAgICAgICAgPC9leHByZXNzaW9uPgogICAgICAgICA8b3BlcmFuZCBuYW1lPSJ2YWx1ZSI+CiAgICAgICAgICAgIDxvcGVyYW5kVHlwZVNwZWNpZmllciBsb2NhdG9yPSIzMTE6MzItMzExOjQ4IiBuYW1lPSJmaGlyOk9ic2VydmF0aW9uU3RhdHVzIiB4c2k6dHlwZT0iTmFtZWRUeXBlU3BlY2lmaWVyIi8+CiAgICAgICAgIDwvb3BlcmFuZD4KICAgICAgPC9kZWY+CiAgICAgIDxkZWYgbG9jYXRvcj0iMzEyOjEtMzEyOjU4IiBuYW1lPSJUb1N0cmluZyIgY29udGV4dD0iUGF0aWVudCIgYWNjZXNzTGV2ZWw9IlB1YmxpYyIgeHNpOnR5cGU9IkZ1bmN0aW9uRGVmIj4KICAgICAgICAgPGV4cHJlc3Npb24gbG9jYXRvcj0iMzEyOjQ4LTMxMjo1OCIgcGF0aD0idmFsdWUiIHhzaTp0eXBlPSJQcm9wZXJ0eSI+CiAgICAgICAgICAgIDxzb3VyY2UgbG9jYXRvcj0iMzEyOjQ4LTMxMjo1MiIgbmFtZT0idmFsdWUiIHhzaTp0eXBlPSJPcGVyYW5kUmVmIi8+CiAgICAgICAgIDwvZXhwcmVzc2lvbj4KICAgICAgICAgPG9wZXJhbmQgbmFtZT0idmFsdWUiPgogICAgICAgICAgICA8b3BlcmFuZFR5cGVTcGVjaWZpZXIgbG9jYXRvcj0iMzEyOjMyLTMxMjo0NCIgbmFtZT0iZmhpcjpPcGVyYXRpb25LaW5kIiB4c2k6dHlwZT0iTmFtZWRUeXBlU3BlY2lmaWVyIi8+CiAgICAgICAgIDwvb3BlcmFuZD4KICAgICAgPC9kZWY+CiAgICAgIDxkZWYgbG9jYXRvcj0iMzEzOjEtMzEzOjY2IiBuYW1lPSJUb1N0cmluZyIgY29udGV4dD0iUGF0aWVudCIgYWNjZXNzTGV2ZWw9IlB1YmxpYyIgeHNpOnR5cGU9IkZ1bmN0aW9uRGVmIj4KICAgICAgICAgPGV4cHJlc3Npb24gbG9jYXRvcj0iMzEzOjU2LTMxMzo2NiIgcGF0aD0idmFsdWUiIHhzaTp0eXBlPSJQcm9wZXJ0eSI+CiAgICAgICAgICAgIDxzb3VyY2UgbG9jYXRvcj0iMzEzOjU2LTMxMzo2MCIgbmFtZT0idmFsdWUiIHhzaTp0eXBlPSJPcGVyYW5kUmVmIi8+CiAgICAgICAgIDwvZXhwcmVzc2lvbj4KICAgICAgICAgPG9wZXJhbmQgbmFtZT0idmFsdWUiPgogICAgICAgICAgICA8b3BlcmFuZFR5cGVTcGVjaWZpZXIgbG9jYXRvcj0iMzEzOjMyLTMxMzo1MiIgbmFtZT0iZmhpcjpPcGVyYXRpb25QYXJhbWV0ZXJVc2UiIHhzaTp0eXBlPSJOYW1lZFR5cGVTcGVjaWZpZXIiLz4KICAgICAgICAgPC9vcGVyYW5kPgogICAgICA8L2RlZj4KICAgICAgPGRlZiBsb2NhdG9yPSIzMTQ6MS0zMTQ6NjAiIG5hbWU9IlRvU3RyaW5nIiBjb250ZXh0PSJQYXRpZW50IiBhY2Nlc3NMZXZlbD0iUHVibGljIiB4c2k6dHlwZT0iRnVuY3Rpb25EZWYiPgogICAgICAgICA8ZXhwcmVzc2lvbiBsb2NhdG9yPSIzMTQ6NTAtMzE0OjYwIiBwYXRoPSJ2YWx1ZSIgeHNpOnR5cGU9IlByb3BlcnR5Ij4KICAgICAgICAgICAgPHNvdXJjZSBsb2NhdG9yPSIzMTQ6NTAtMzE0OjU0IiBuYW1lPSJ2YWx1ZSIgeHNpOnR5cGU9Ik9wZXJhbmRSZWYiLz4KICAgICAgICAgPC9leHByZXNzaW9uPgogICAgICAgICA8b3BlcmFuZCBuYW1lPSJ2YWx1ZSI+CiAgICAgICAgICAgIDxvcGVyYW5kVHlwZVNwZWNpZmllciBsb2NhdG9yPSIzMTQ6MzItMzE0OjQ2IiBuYW1lPSJmaGlyOk9yaWVudGF0aW9uVHlwZSIgeHNpOnR5cGU9Ik5hbWVkVHlwZVNwZWNpZmllciIvPgogICAgICAgICA8L29wZXJhbmQ+CiAgICAgIDwvZGVmPgogICAgICA8ZGVmIGxvY2F0b3I9IjMxNToxLTMxNTo1NyIgbmFtZT0iVG9TdHJpbmciIGNvbnRleHQ9IlBhdGllbnQiIGFjY2Vzc0xldmVsPSJQdWJsaWMiIHhzaTp0eXBlPSJGdW5jdGlvbkRlZiI+CiAgICAgICAgIDxleHByZXNzaW9uIGxvY2F0b3I9IjMxNTo0Ny0zMTU6NTciIHBhdGg9InZhbHVlIiB4c2k6dHlwZT0iUHJvcGVydHkiPgogICAgICAgICAgICA8c291cmNlIGxvY2F0b3I9IjMxNTo0Ny0zMTU6NTEiIG5hbWU9InZhbHVlIiB4c2k6dHlwZT0iT3BlcmFuZFJlZiIvPgogICAgICAgICA8L2V4cHJlc3Npb24+CiAgICAgICAgIDxvcGVyYW5kIG5hbWU9InZhbHVlIj4KICAgICAgICAgICAgPG9wZXJhbmRUeXBlU3BlY2lmaWVyIGxvY2F0b3I9IjMxNTozMi0zMTU6NDMiIG5hbWU9ImZoaXI6UGFyYW1ldGVyVXNlIiB4c2k6dHlwZT0iTmFtZWRUeXBlU3BlY2lmaWVyIi8+CiAgICAgICAgIDwvb3BlcmFuZD4KICAgICAgPC9kZWY+CiAgICAgIDxkZWYgbG9jYXRvcj0iMzE2OjEtMzE2OjY0IiBuYW1lPSJUb1N0cmluZyIgY29udGV4dD0iUGF0aWVudCIgYWNjZXNzTGV2ZWw9IlB1YmxpYyIgeHNpOnR5cGU9IkZ1bmN0aW9uRGVmIj4KICAgICAgICAgPGV4cHJlc3Npb24gbG9jYXRvcj0iMzE2OjU0LTMxNjo2NCIgcGF0aD0idmFsdWUiIHhzaTp0eXBlPSJQcm9wZXJ0eSI+CiAgICAgICAgICAgIDxzb3VyY2UgbG9jYXRvcj0iMzE2OjU0LTMxNjo1OCIgbmFtZT0idmFsdWUiIHhzaTp0eXBlPSJPcGVyYW5kUmVmIi8+CiAgICAgICAgIDwvZXhwcmVzc2lvbj4KICAgICAgICAgPG9wZXJhbmQgbmFtZT0idmFsdWUiPgogICAgICAgICAgICA8b3BlcmFuZFR5cGVTcGVjaWZpZXIgbG9jYXRvcj0iMzE2OjMyLTMxNjo1MCIgbmFtZT0iZmhpcjpQYXJ0aWNpcGFudFJlcXVpcmVkIiB4c2k6dHlwZT0iTmFtZWRUeXBlU3BlY2lmaWVyIi8+CiAgICAgICAgIDwvb3BlcmFuZD4KICAgICAgPC9kZWY+CiAgICAgIDxkZWYgbG9jYXRvcj0iMzE3OjEtMzE3OjYyIiBuYW1lPSJUb1N0cmluZyIgY29udGV4dD0iUGF0aWVudCIgYWNjZXNzTGV2ZWw9IlB1YmxpYyIgeHNpOnR5cGU9IkZ1bmN0aW9uRGVmIj4KICAgICAgICAgPGV4cHJlc3Npb24gbG9jYXRvcj0iMzE3OjUyLTMxNzo2MiIgcGF0aD0idmFsdWUiIHhzaTp0eXBlPSJQcm9wZXJ0eSI+CiAgICAgICAgICAgIDxzb3VyY2UgbG9jYXRvcj0iMzE3OjUyLTMxNzo1NiIgbmFtZT0idmFsdWUiIHhzaTp0eXBlPSJPcGVyYW5kUmVmIi8+CiAgICAgICAgIDwvZXhwcmVzc2lvbj4KICAgICAgICAgPG9wZXJhbmQgbmFtZT0idmFsdWUiPgogICAgICAgICAgICA8b3BlcmFuZFR5cGVTcGVjaWZpZXIgbG9jYXRvcj0iMzE3OjMyLTMxNzo0OCIgbmFtZT0iZmhpcjpQYXJ0aWNpcGFudFN0YXR1cyIgeHNpOnR5cGU9Ik5hbWVkVHlwZVNwZWNpZmllciIvPgogICAgICAgICA8L29wZXJhbmQ+CiAgICAgIDwvZGVmPgogICAgICA8ZGVmIGxvY2F0b3I9IjMxODoxLTMxODo2NCIgbmFtZT0iVG9TdHJpbmciIGNvbnRleHQ9IlBhdGllbnQiIGFjY2Vzc0xldmVsPSJQdWJsaWMiIHhzaTp0eXBlPSJGdW5jdGlvbkRlZiI+CiAgICAgICAgIDxleHByZXNzaW9uIGxvY2F0b3I9IjMxODo1NC0zMTg6NjQiIHBhdGg9InZhbHVlIiB4c2k6dHlwZT0iUHJvcGVydHkiPgogICAgICAgICAgICA8c291cmNlIGxvY2F0b3I9IjMxODo1NC0zMTg6NTgiIG5hbWU9InZhbHVlIiB4c2k6dHlwZT0iT3BlcmFuZFJlZiIvPgogICAgICAgICA8L2V4cHJlc3Npb24+CiAgICAgICAgIDxvcGVyYW5kIG5hbWU9InZhbHVlIj4KICAgICAgICAgICAgPG9wZXJhbmRUeXBlU3BlY2lmaWVyIGxvY2F0b3I9IjMxODozMi0zMTg6NTAiIG5hbWU9ImZoaXI6UGFydGljaXBhdGlvblN0YXR1cyIgeHNpOnR5cGU9Ik5hbWVkVHlwZVNwZWNpZmllciIvPgogICAgICAgICA8L29wZXJhbmQ+CiAgICAgIDwvZGVmPgogICAgICA8ZGVmIGxvY2F0b3I9IjMxOToxLTMxOTo2NCIgbmFtZT0iVG9TdHJpbmciIGNvbnRleHQ9IlBhdGllbnQiIGFjY2Vzc0xldmVsPSJQdWJsaWMiIHhzaTp0eXBlPSJGdW5jdGlvbkRlZiI+CiAgICAgICAgIDxleHByZXNzaW9uIGxvY2F0b3I9IjMxOTo1NC0zMTk6NjQiIHBhdGg9InZhbHVlIiB4c2k6dHlwZT0iUHJvcGVydHkiPgogICAgICAgICAgICA8c291cmNlIGxvY2F0b3I9IjMxOTo1NC0zMTk6NTgiIG5hbWU9InZhbHVlIiB4c2k6dHlwZT0iT3BlcmFuZFJlZiIvPgogICAgICAgICA8L2V4cHJlc3Npb24+CiAgICAgICAgIDxvcGVyYW5kIG5hbWU9InZhbHVlIj4KICAgICAgICAgICAgPG9wZXJhbmRUeXBlU3BlY2lmaWVyIGxvY2F0b3I9IjMxOTozMi0zMTk6NTAiIG5hbWU9ImZoaXI6UGF5bWVudE5vdGljZVN0YXR1cyIgeHNpOnR5cGU9Ik5hbWVkVHlwZVNwZWNpZmllciIvPgogICAgICAgICA8L29wZXJhbmQ+CiAgICAgIDwvZGVmPgogICAgICA8ZGVmIGxvY2F0b3I9IjMyMDoxLTMyMDo3MiIgbmFtZT0iVG9TdHJpbmciIGNvbnRleHQ9IlBhdGllbnQiIGFjY2Vzc0xldmVsPSJQdWJsaWMiIHhzaTp0eXBlPSJGdW5jdGlvbkRlZiI+CiAgICAgICAgIDxleHByZXNzaW9uIGxvY2F0b3I9IjMyMDo2Mi0zMjA6NzIiIHBhdGg9InZhbHVlIiB4c2k6dHlwZT0iUHJvcGVydHkiPgogICAgICAgICAgICA8c291cmNlIGxvY2F0b3I9IjMyMDo2Mi0zMjA6NjYiIG5hbWU9InZhbHVlIiB4c2k6dHlwZT0iT3BlcmFuZFJlZiIvPgogICAgICAgICA8L2V4cHJlc3Npb24+CiAgICAgICAgIDxvcGVyYW5kIG5hbWU9InZhbHVlIj4KICAgICAgICAgICAgPG9wZXJhbmRUeXBlU3BlY2lmaWVyIGxvY2F0b3I9IjMyMDozMi0zMjA6NTgiIG5hbWU9ImZoaXI6UGF5bWVudFJlY29uY2lsaWF0aW9uU3RhdHVzIiB4c2k6dHlwZT0iTmFtZWRUeXBlU3BlY2lmaWVyIi8+CiAgICAgICAgIDwvb3BlcmFuZD4KICAgICAgPC9kZWY+CiAgICAgIDxkZWYgbG9jYXRvcj0iMzIxOjEtMzIxOjYwIiBuYW1lPSJUb1N0cmluZyIgY29udGV4dD0iUGF0aWVudCIgYWNjZXNzTGV2ZWw9IlB1YmxpYyIgeHNpOnR5cGU9IkZ1bmN0aW9uRGVmIj4KICAgICAgICAgPGV4cHJlc3Npb24gbG9jYXRvcj0iMzIxOjUwLTMyMTo2MCIgcGF0aD0idmFsdWUiIHhzaTp0eXBlPSJQcm9wZXJ0eSI+CiAgICAgICAgICAgIDxzb3VyY2UgbG9jYXRvcj0iMzIxOjUwLTMyMTo1NCIgbmFtZT0idmFsdWUiIHhzaTp0eXBlPSJPcGVyYW5kUmVmIi8+CiAgICAgICAgIDwvZXhwcmVzc2lvbj4KICAgICAgICAgPG9wZXJhbmQgbmFtZT0idmFsdWUiPgogICAgICAgICAgICA8b3BlcmFuZFR5cGVTcGVjaWZpZXIgbG9jYXRvcj0iMzIxOjMyLTMyMTo0NiIgbmFtZT0iZmhpcjpQcm9jZWR1cmVTdGF0dXMiIHhzaTp0eXBlPSJOYW1lZFR5cGVTcGVjaWZpZXIiLz4KICAgICAgICAgPC9vcGVyYW5kPgogICAgICA8L2RlZj4KICAgICAgPGRlZiBsb2NhdG9yPSIzMjI6MS0zMjI6NjciIG5hbWU9IlRvU3RyaW5nIiBjb250ZXh0PSJQYXRpZW50IiBhY2Nlc3NMZXZlbD0iUHVibGljIiB4c2k6dHlwZT0iRnVuY3Rpb25EZWYiPgogICAgICAgICA8ZXhwcmVzc2lvbiBsb2NhdG9yPSIzMjI6NTctMzIyOjY3IiBwYXRoPSJ2YWx1ZSIgeHNpOnR5cGU9IlByb3BlcnR5Ij4KICAgICAgICAgICAgPHNvdXJjZSBsb2NhdG9yPSIzMjI6NTctMzIyOjYxIiBuYW1lPSJ2YWx1ZSIgeHNpOnR5cGU9Ik9wZXJhbmRSZWYiLz4KICAgICAgICAgPC9leHByZXNzaW9uPgogICAgICAgICA8b3BlcmFuZCBuYW1lPSJ2YWx1ZSI+CiAgICAgICAgICAgIDxvcGVyYW5kVHlwZVNwZWNpZmllciBsb2NhdG9yPSIzMjI6MzItMzIyOjUzIiBuYW1lPSJmaGlyOlByb3BlcnR5UmVwcmVzZW50YXRpb24iIHhzaTp0eXBlPSJOYW1lZFR5cGVTcGVjaWZpZXIiLz4KICAgICAgICAgPC9vcGVyYW5kPgogICAgICA8L2RlZj4KICAgICAgPGRlZiBsb2NhdG9yPSIzMjM6MS0zMjM6NTciIG5hbWU9IlRvU3RyaW5nIiBjb250ZXh0PSJQYXRpZW50IiBhY2Nlc3NMZXZlbD0iUHVibGljIiB4c2k6dHlwZT0iRnVuY3Rpb25EZWYiPgogICAgICAgICA8ZXhwcmVzc2lvbiBsb2NhdG9yPSIzMjM6NDctMzIzOjU3IiBwYXRoPSJ2YWx1ZSIgeHNpOnR5cGU9IlByb3BlcnR5Ij4KICAgICAgICAgICAgPHNvdXJjZSBsb2NhdG9yPSIzMjM6NDctMzIzOjUxIiBuYW1lPSJ2YWx1ZSIgeHNpOnR5cGU9Ik9wZXJhbmRSZWYiLz4KICAgICAgICAgPC9leHByZXNzaW9uPgogICAgICAgICA8b3BlcmFuZCBuYW1lPSJ2YWx1ZSI+CiAgICAgICAgICAgIDxvcGVyYW5kVHlwZVNwZWNpZmllciBsb2NhdG9yPSIzMjM6MzItMzIzOjQzIiBuYW1lPSJmaGlyOlByb3BlcnR5VHlwZSIgeHNpOnR5cGU9Ik5hbWVkVHlwZVNwZWNpZmllciIvPgogICAgICAgICA8L29wZXJhbmQ+CiAgICAgIDwvZGVmPgogICAgICA8ZGVmIGxvY2F0b3I9IjMyNDoxLTMyNDo2NSIgbmFtZT0iVG9TdHJpbmciIGNvbnRleHQ9IlBhdGllbnQiIGFjY2Vzc0xldmVsPSJQdWJsaWMiIHhzaTp0eXBlPSJGdW5jdGlvbkRlZiI+CiAgICAgICAgIDxleHByZXNzaW9uIGxvY2F0b3I9IjMyNDo1NS0zMjQ6NjUiIHBhdGg9InZhbHVlIiB4c2k6dHlwZT0iUHJvcGVydHkiPgogICAgICAgICAgICA8c291cmNlIGxvY2F0b3I9IjMyNDo1NS0zMjQ6NTkiIG5hbWU9InZhbHVlIiB4c2k6dHlwZT0iT3BlcmFuZFJlZiIvPgogICAgICAgICA8L2V4cHJlc3Npb24+CiAgICAgICAgIDxvcGVyYW5kIG5hbWU9InZhbHVlIj4KICAgICAgICAgICAgPG9wZXJhbmRUeXBlU3BlY2lmaWVyIGxvY2F0b3I9IjMyNDozMi0zMjQ6NTEiIG5hbWU9ImZoaXI6UHJvdmVuYW5jZUVudGl0eVJvbGUiIHhzaTp0eXBlPSJOYW1lZFR5cGVTcGVjaWZpZXIiLz4KICAgICAgICAgPC9vcGVyYW5kPgogICAgICA8L2RlZj4KICAgICAgPGRlZiBsb2NhdG9yPSIzMjU6MS0zMjU6NjIiIG5hbWU9IlRvU3RyaW5nIiBjb250ZXh0PSJQYXRpZW50IiBhY2Nlc3NMZXZlbD0iUHVibGljIiB4c2k6dHlwZT0iRnVuY3Rpb25EZWYiPgogICAgICAgICA8ZXhwcmVzc2lvbiBsb2NhdG9yPSIzMjU6NTItMzI1OjYyIiBwYXRoPSJ2YWx1ZSIgeHNpOnR5cGU9IlByb3BlcnR5Ij4KICAgICAgICAgICAgPHNvdXJjZSBsb2NhdG9yPSIzMjU6NTItMzI1OjU2IiBuYW1lPSJ2YWx1ZSIgeHNpOnR5cGU9Ik9wZXJhbmRSZWYiLz4KICAgICAgICAgPC9leHByZXNzaW9uPgogICAgICAgICA8b3BlcmFuZCBuYW1lPSJ2YWx1ZSI+CiAgICAgICAgICAgIDxvcGVyYW5kVHlwZVNwZWNpZmllciBsb2NhdG9yPSIzMjU6MzItMzI1OjQ4IiBuYW1lPSJmaGlyOlB1YmxpY2F0aW9uU3RhdHVzIiB4c2k6dHlwZT0iTmFtZWRUeXBlU3BlY2lmaWVyIi8+CiAgICAgICAgIDwvb3BlcmFuZD4KICAgICAgPC9kZWY+CiAgICAgIDxkZWYgbG9jYXRvcj0iMzI2OjEtMzI2OjU2IiBuYW1lPSJUb1N0cmluZyIgY29udGV4dD0iUGF0aWVudCIgYWNjZXNzTGV2ZWw9IlB1YmxpYyIgeHNpOnR5cGU9IkZ1bmN0aW9uRGVmIj4KICAgICAgICAgPGV4cHJlc3Npb24gbG9jYXRvcj0iMzI2OjQ2LTMyNjo1NiIgcGF0aD0idmFsdWUiIHhzaTp0eXBlPSJQcm9wZXJ0eSI+CiAgICAgICAgICAgIDxzb3VyY2UgbG9jYXRvcj0iMzI2OjQ2LTMyNjo1MCIgbmFtZT0idmFsdWUiIHhzaTp0eXBlPSJPcGVyYW5kUmVmIi8+CiAgICAgICAgIDwvZXhwcmVzc2lvbj4KICAgICAgICAgPG9wZXJhbmQgbmFtZT0idmFsdWUiPgogICAgICAgICAgICA8b3BlcmFuZFR5cGVTcGVjaWZpZXIgbG9jYXRvcj0iMzI2OjMyLTMyNjo0MiIgbmFtZT0iZmhpcjpRdWFsaXR5VHlwZSIgeHNpOnR5cGU9Ik5hbWVkVHlwZVNwZWNpZmllciIvPgogICAgICAgICA8L29wZXJhbmQ+CiAgICAgIDwvZGVmPgogICAgICA8ZGVmIGxvY2F0b3I9IjMyNzoxLTMyNzo2MyIgbmFtZT0iVG9TdHJpbmciIGNvbnRleHQ9IlBhdGllbnQiIGFjY2Vzc0xldmVsPSJQdWJsaWMiIHhzaTp0eXBlPSJGdW5jdGlvbkRlZiI+CiAgICAgICAgIDxleHByZXNzaW9uIGxvY2F0b3I9IjMyNzo1My0zMjc6NjMiIHBhdGg9InZhbHVlIiB4c2k6dHlwZT0iUHJvcGVydHkiPgogICAgICAgICAgICA8c291cmNlIGxvY2F0b3I9IjMyNzo1My0zMjc6NTciIG5hbWU9InZhbHVlIiB4c2k6dHlwZT0iT3BlcmFuZFJlZiIvPgogICAgICAgICA8L2V4cHJlc3Npb24+CiAgICAgICAgIDxvcGVyYW5kIG5hbWU9InZhbHVlIj4KICAgICAgICAgICAgPG9wZXJhbmRUeXBlU3BlY2lmaWVyIGxvY2F0b3I9IjMyNzozMi0zMjc6NDkiIG5hbWU9ImZoaXI6UXVhbnRpdHlDb21wYXJhdG9yIiB4c2k6dHlwZT0iTmFtZWRUeXBlU3BlY2lmaWVyIi8+CiAgICAgICAgIDwvb3BlcmFuZD4KICAgICAgPC9kZWY+CiAgICAgIDxkZWYgbG9jYXRvcj0iMzI4OjEtMzI4OjcwIiBuYW1lPSJUb1N0cmluZyIgY29udGV4dD0iUGF0aWVudCIgYWNjZXNzTGV2ZWw9IlB1YmxpYyIgeHNpOnR5cGU9IkZ1bmN0aW9uRGVmIj4KICAgICAgICAgPGV4cHJlc3Npb24gbG9jYXRvcj0iMzI4OjYwLTMyODo3MCIgcGF0aD0idmFsdWUiIHhzaTp0eXBlPSJQcm9wZXJ0eSI+CiAgICAgICAgICAgIDxzb3VyY2UgbG9jYXRvcj0iMzI4OjYwLTMyODo2NCIgbmFtZT0idmFsdWUiIHhzaTp0eXBlPSJPcGVyYW5kUmVmIi8+CiAgICAgICAgIDwvZXhwcmVzc2lvbj4KICAgICAgICAgPG9wZXJhbmQgbmFtZT0idmFsdWUiPgogICAgICAgICAgICA8b3BlcmFuZFR5cGVTcGVjaWZpZXIgbG9jYXRvcj0iMzI4OjMyLTMyODo1NiIgbmFtZT0iZmhpcjpRdWVzdGlvbm5haXJlSXRlbU9wZXJhdG9yIiB4c2k6dHlwZT0iTmFtZWRUeXBlU3BlY2lmaWVyIi8+CiAgICAgICAgIDwvb3BlcmFuZD4KICAgICAgPC9kZWY+CiAgICAgIDxkZWYgbG9jYXRvcj0iMzI5OjEtMzI5OjY2IiBuYW1lPSJUb1N0cmluZyIgY29udGV4dD0iUGF0aWVudCIgYWNjZXNzTGV2ZWw9IlB1YmxpYyIgeHNpOnR5cGU9IkZ1bmN0aW9uRGVmIj4KICAgICAgICAgPGV4cHJlc3Npb24gbG9jYXRvcj0iMzI5OjU2LTMyOTo2NiIgcGF0aD0idmFsdWUiIHhzaTp0eXBlPSJQcm9wZXJ0eSI+CiAgICAgICAgICAgIDxzb3VyY2UgbG9jYXRvcj0iMzI5OjU2LTMyOTo2MCIgbmFtZT0idmFsdWUiIHhzaTp0eXBlPSJPcGVyYW5kUmVmIi8+CiAgICAgICAgIDwvZXhwcmVzc2lvbj4KICAgICAgICAgPG9wZXJhbmQgbmFtZT0idmFsdWUiPgogICAgICAgICAgICA8b3BlcmFuZFR5cGVTcGVjaWZpZXIgbG9jYXRvcj0iMzI5OjMyLTMyOTo1MiIgbmFtZT0iZmhpcjpRdWVzdGlvbm5haXJlSXRlbVR5cGUiIHhzaTp0eXBlPSJOYW1lZFR5cGVTcGVjaWZpZXIiLz4KICAgICAgICAgPC9vcGVyYW5kPgogICAgICA8L2RlZj4KICAgICAgPGRlZiBsb2NhdG9yPSIzMzA6MS0zMzA6NzIiIG5hbWU9IlRvU3RyaW5nIiBjb250ZXh0PSJQYXRpZW50IiBhY2Nlc3NMZXZlbD0iUHVibGljIiB4c2k6dHlwZT0iRnVuY3Rpb25EZWYiPgogICAgICAgICA8ZXhwcmVzc2lvbiBsb2NhdG9yPSIzMzA6NjItMzMwOjcyIiBwYXRoPSJ2YWx1ZSIgeHNpOnR5cGU9IlByb3BlcnR5Ij4KICAgICAgICAgICAgPHNvdXJjZSBsb2NhdG9yPSIzMzA6NjItMzMwOjY2IiBuYW1lPSJ2YWx1ZSIgeHNpOnR5cGU9Ik9wZXJhbmRSZWYiLz4KICAgICAgICAgPC9leHByZXNzaW9uPgogICAgICAgICA8b3BlcmFuZCBuYW1lPSJ2YWx1ZSI+CiAgICAgICAgICAgIDxvcGVyYW5kVHlwZVNwZWNpZmllciBsb2NhdG9yPSIzMzA6MzItMzMwOjU4IiBuYW1lPSJmaGlyOlF1ZXN0aW9ubmFpcmVSZXNwb25zZVN0YXR1cyIgeHNpOnR5cGU9Ik5hbWVkVHlwZVNwZWNpZmllciIvPgogICAgICAgICA8L29wZXJhbmQ+CiAgICAgIDwvZGVmPgogICAgICA8ZGVmIGxvY2F0b3I9IjMzMToxLTMzMTo2OCIgbmFtZT0iVG9TdHJpbmciIGNvbnRleHQ9IlBhdGllbnQiIGFjY2Vzc0xldmVsPSJQdWJsaWMiIHhzaTp0eXBlPSJGdW5jdGlvbkRlZiI+CiAgICAgICAgIDxleHByZXNzaW9uIGxvY2F0b3I9IjMzMTo1OC0zMzE6NjgiIHBhdGg9InZhbHVlIiB4c2k6dHlwZT0iUHJvcGVydHkiPgogICAgICAgICAgICA8c291cmNlIGxvY2F0b3I9IjMzMTo1OC0zMzE6NjIiIG5hbWU9InZhbHVlIiB4c2k6dHlwZT0iT3BlcmFuZFJlZiIvPgogICAgICAgICA8L2V4cHJlc3Npb24+CiAgICAgICAgIDxvcGVyYW5kIG5hbWU9InZhbHVlIj4KICAgICAgICAgICAgPG9wZXJhbmRUeXBlU3BlY2lmaWVyIGxvY2F0b3I9IjMzMTozMi0zMzE6NTQiIG5hbWU9ImZoaXI6UmVmZXJlbmNlSGFuZGxpbmdQb2xpY3kiIHhzaTp0eXBlPSJOYW1lZFR5cGVTcGVjaWZpZXIiLz4KICAgICAgICAgPC9vcGVyYW5kPgogICAgICA8L2RlZj4KICAgICAgPGRlZiBsb2NhdG9yPSIzMzI6MS0zMzI6NjYiIG5hbWU9IlRvU3RyaW5nIiBjb250ZXh0PSJQYXRpZW50IiBhY2Nlc3NMZXZlbD0iUHVibGljIiB4c2k6dHlwZT0iRnVuY3Rpb25EZWYiPgogICAgICAgICA8ZXhwcmVzc2lvbiBsb2NhdG9yPSIzMzI6NTYtMzMyOjY2IiBwYXRoPSJ2YWx1ZSIgeHNpOnR5cGU9IlByb3BlcnR5Ij4KICAgICAgICAgICAgPHNvdXJjZSBsb2NhdG9yPSIzMzI6NTYtMzMyOjYwIiBuYW1lPSJ2YWx1ZSIgeHNpOnR5cGU9Ik9wZXJhbmRSZWYiLz4KICAgICAgICAgPC9leHByZXNzaW9uPgogICAgICAgICA8b3BlcmFuZCBuYW1lPSJ2YWx1ZSI+CiAgICAgICAgICAgIDxvcGVyYW5kVHlwZVNwZWNpZmllciBsb2NhdG9yPSIzMzI6MzItMzMyOjUyIiBuYW1lPSJmaGlyOlJlZmVyZW5jZVZlcnNpb25SdWxlcyIgeHNpOnR5cGU9Ik5hbWVkVHlwZVNwZWNpZmllciIvPgogICAgICAgICA8L29wZXJhbmQ+CiAgICAgIDwvZGVmPgogICAgICA8ZGVmIGxvY2F0b3I9IjMzMzoxLTMzMzo2NyIgbmFtZT0iVG9TdHJpbmciIGNvbnRleHQ9IlBhdGllbnQiIGFjY2Vzc0xldmVsPSJQdWJsaWMiIHhzaTp0eXBlPSJGdW5jdGlvbkRlZiI+CiAgICAgICAgIDxleHByZXNzaW9uIGxvY2F0b3I9IjMzMzo1Ny0zMzM6NjciIHBhdGg9InZhbHVlIiB4c2k6dHlwZT0iUHJvcGVydHkiPgogICAgICAgICAgICA8c291cmNlIGxvY2F0b3I9IjMzMzo1Ny0zMzM6NjEiIG5hbWU9InZhbHVlIiB4c2k6dHlwZT0iT3BlcmFuZFJlZiIvPgogICAgICAgICA8L2V4cHJlc3Npb24+CiAgICAgICAgIDxvcGVyYW5kIG5hbWU9InZhbHVlIj4KICAgICAgICAgICAgPG9wZXJhbmRUeXBlU3BlY2lmaWVyIGxvY2F0b3I9IjMzMzozMi0zMzM6NTMiIG5hbWU9ImZoaXI6UmVmZXJyZWREb2N1bWVudFN0YXR1cyIgeHNpOnR5cGU9Ik5hbWVkVHlwZVNwZWNpZmllciIvPgogICAgICAgICA8L29wZXJhbmQ+CiAgICAgIDwvZGVmPgogICAgICA8ZGVmIGxvY2F0b3I9IjMzNDoxLTMzNDo2NCIgbmFtZT0iVG9TdHJpbmciIGNvbnRleHQ9IlBhdGllbnQiIGFjY2Vzc0xldmVsPSJQdWJsaWMiIHhzaTp0eXBlPSJGdW5jdGlvbkRlZiI+CiAgICAgICAgIDxleHByZXNzaW9uIGxvY2F0b3I9IjMzNDo1NC0zMzQ6NjQiIHBhdGg9InZhbHVlIiB4c2k6dHlwZT0iUHJvcGVydHkiPgogICAgICAgICAgICA8c291cmNlIGxvY2F0b3I9IjMzNDo1NC0zMzQ6NTgiIG5hbWU9InZhbHVlIiB4c2k6dHlwZT0iT3BlcmFuZFJlZiIvPgogICAgICAgICA8L2V4cHJlc3Npb24+CiAgICAgICAgIDxvcGVyYW5kIG5hbWU9InZhbHVlIj4KICAgICAgICAgICAgPG9wZXJhbmRUeXBlU3BlY2lmaWVyIGxvY2F0b3I9IjMzNDozMi0zMzQ6NTAiIG5hbWU9ImZoaXI6UmVsYXRlZEFydGlmYWN0VHlwZSIgeHNpOnR5cGU9Ik5hbWVkVHlwZVNwZWNpZmllciIvPgogICAgICAgICA8L29wZXJhbmQ+CiAgICAgIDwvZGVmPgogICAgICA8ZGVmIGxvY2F0b3I9IjMzNToxLTMzNTo2MiIgbmFtZT0iVG9TdHJpbmciIGNvbnRleHQ9IlBhdGllbnQiIGFjY2Vzc0xldmVsPSJQdWJsaWMiIHhzaTp0eXBlPSJGdW5jdGlvbkRlZiI+CiAgICAgICAgIDxleHByZXNzaW9uIGxvY2F0b3I9IjMzNTo1Mi0zMzU6NjIiIHBhdGg9InZhbHVlIiB4c2k6dHlwZT0iUHJvcGVydHkiPgogICAgICAgICAgICA8c291cmNlIGxvY2F0b3I9IjMzNTo1Mi0zMzU6NTYiIG5hbWU9InZhbHVlIiB4c2k6dHlwZT0iT3BlcmFuZFJlZiIvPgogICAgICAgICA8L2V4cHJlc3Npb24+CiAgICAgICAgIDxvcGVyYW5kIG5hbWU9InZhbHVlIj4KICAgICAgICAgICAgPG9wZXJhbmRUeXBlU3BlY2lmaWVyIGxvY2F0b3I9IjMzNTozMi0zMzU6NDgiIG5hbWU9ImZoaXI6UmVtaXR0YW5jZU91dGNvbWUiIHhzaTp0eXBlPSJOYW1lZFR5cGVTcGVjaWZpZXIiLz4KICAgICAgICAgPC9vcGVyYW5kPgogICAgICA8L2RlZj4KICAgICAgPGRlZiBsb2NhdG9yPSIzMzY6MS0zMzY6NTkiIG5hbWU9IlRvU3RyaW5nIiBjb250ZXh0PSJQYXRpZW50IiBhY2Nlc3NMZXZlbD0iUHVibGljIiB4c2k6dHlwZT0iRnVuY3Rpb25EZWYiPgogICAgICAgICA8ZXhwcmVzc2lvbiBsb2NhdG9yPSIzMzY6NDktMzM2OjU5IiBwYXRoPSJ2YWx1ZSIgeHNpOnR5cGU9IlByb3BlcnR5Ij4KICAgICAgICAgICAgPHNvdXJjZSBsb2NhdG9yPSIzMzY6NDktMzM2OjUzIiBuYW1lPSJ2YWx1ZSIgeHNpOnR5cGU9Ik9wZXJhbmRSZWYiLz4KICAgICAgICAgPC9leHByZXNzaW9uPgogICAgICAgICA8b3BlcmFuZCBuYW1lPSJ2YWx1ZSI+CiAgICAgICAgICAgIDxvcGVyYW5kVHlwZVNwZWNpZmllciBsb2NhdG9yPSIzMzY6MzItMzM2OjQ1IiBuYW1lPSJmaGlyOlJlcG9zaXRvcnlUeXBlIiB4c2k6dHlwZT0iTmFtZWRUeXBlU3BlY2lmaWVyIi8+CiAgICAgICAgIDwvb3BlcmFuZD4KICAgICAgPC9kZWY+CiAgICAgIDxkZWYgbG9jYXRvcj0iMzM3OjEtMzM3OjU4IiBuYW1lPSJUb1N0cmluZyIgY29udGV4dD0iUGF0aWVudCIgYWNjZXNzTGV2ZWw9IlB1YmxpYyIgeHNpOnR5cGU9IkZ1bmN0aW9uRGVmIj4KICAgICAgICAgPGV4cHJlc3Npb24gbG9jYXRvcj0iMzM3OjQ4LTMzNzo1OCIgcGF0aD0idmFsdWUiIHhzaTp0eXBlPSJQcm9wZXJ0eSI+CiAgICAgICAgICAgIDxzb3VyY2UgbG9jYXRvcj0iMzM3OjQ4LTMzNzo1MiIgbmFtZT0idmFsdWUiIHhzaTp0eXBlPSJPcGVyYW5kUmVmIi8+CiAgICAgICAgIDwvZXhwcmVzc2lvbj4KICAgICAgICAgPG9wZXJhbmQgbmFtZT0idmFsdWUiPgogICAgICAgICAgICA8b3BlcmFuZFR5cGVTcGVjaWZpZXIgbG9jYXRvcj0iMzM3OjMyLTMzNzo0NCIgbmFtZT0iZmhpcjpSZXF1ZXN0SW50ZW50IiB4c2k6dHlwZT0iTmFtZWRUeXBlU3BlY2lmaWVyIi8+CiAgICAgICAgIDwvb3BlcmFuZD4KICAgICAgPC9kZWY+CiAgICAgIDxkZWYgbG9jYXRvcj0iMzM4OjEtMzM4OjYwIiBuYW1lPSJUb1N0cmluZyIgY29udGV4dD0iUGF0aWVudCIgYWNjZXNzTGV2ZWw9IlB1YmxpYyIgeHNpOnR5cGU9IkZ1bmN0aW9uRGVmIj4KICAgICAgICAgPGV4cHJlc3Npb24gbG9jYXRvcj0iMzM4OjUwLTMzODo2MCIgcGF0aD0idmFsdWUiIHhzaTp0eXBlPSJQcm9wZXJ0eSI+CiAgICAgICAgICAgIDxzb3VyY2UgbG9jYXRvcj0iMzM4OjUwLTMzODo1NCIgbmFtZT0idmFsdWUiIHhzaTp0eXBlPSJPcGVyYW5kUmVmIi8+CiAgICAgICAgIDwvZXhwcmVzc2lvbj4KICAgICAgICAgPG9wZXJhbmQgbmFtZT0idmFsdWUiPgogICAgICAgICAgICA8b3BlcmFuZFR5cGVTcGVjaWZpZXIgbG9jYXRvcj0iMzM4OjMyLTMzODo0NiIgbmFtZT0iZmhpcjpSZXF1ZXN0UHJpb3JpdHkiIHhzaTp0eXBlPSJOYW1lZFR5cGVTcGVjaWZpZXIiLz4KICAgICAgICAgPC9vcGVyYW5kPgogICAgICA8L2RlZj4KICAgICAgPGRlZiBsb2NhdG9yPSIzMzk6MS0zMzk6NTgiIG5hbWU9IlRvU3RyaW5nIiBjb250ZXh0PSJQYXRpZW50IiBhY2Nlc3NMZXZlbD0iUHVibGljIiB4c2k6dHlwZT0iRnVuY3Rpb25EZWYiPgogICAgICAgICA8ZXhwcmVzc2lvbiBsb2NhdG9yPSIzMzk6NDgtMzM5OjU4IiBwYXRoPSJ2YWx1ZSIgeHNpOnR5cGU9IlByb3BlcnR5Ij4KICAgICAgICAgICAgPHNvdXJjZSBsb2NhdG9yPSIzMzk6NDgtMzM5OjUyIiBuYW1lPSJ2YWx1ZSIgeHNpOnR5cGU9Ik9wZXJhbmRSZWYiLz4KICAgICAgICAgPC9leHByZXNzaW9uPgogICAgICAgICA8b3BlcmFuZCBuYW1lPSJ2YWx1ZSI+CiAgICAgICAgICAgIDxvcGVyYW5kVHlwZVNwZWNpZmllciBsb2NhdG9yPSIzMzk6MzItMzM5OjQ0IiBuYW1lPSJmaGlyOlJlcXVlc3RTdGF0dXMiIHhzaTp0eXBlPSJOYW1lZFR5cGVTcGVjaWZpZXIiLz4KICAgICAgICAgPC9vcGVyYW5kPgogICAgICA8L2RlZj4KICAgICAgPGRlZiBsb2NhdG9yPSIzNDA6MS0zNDA6NjQiIG5hbWU9IlRvU3RyaW5nIiBjb250ZXh0PSJQYXRpZW50IiBhY2Nlc3NMZXZlbD0iUHVibGljIiB4c2k6dHlwZT0iRnVuY3Rpb25EZWYiPgogICAgICAgICA8ZXhwcmVzc2lvbiBsb2NhdG9yPSIzNDA6NTQtMzQwOjY0IiBwYXRoPSJ2YWx1ZSIgeHNpOnR5cGU9IlByb3BlcnR5Ij4KICAgICAgICAgICAgPHNvdXJjZSBsb2NhdG9yPSIzNDA6NTQtMzQwOjU4IiBuYW1lPSJ2YWx1ZSIgeHNpOnR5cGU9Ik9wZXJhbmRSZWYiLz4KICAgICAgICAgPC9leHByZXNzaW9uPgogICAgICAgICA8b3BlcmFuZCBuYW1lPSJ2YWx1ZSI+CiAgICAgICAgICAgIDxvcGVyYW5kVHlwZVNwZWNpZmllciBsb2NhdG9yPSIzNDA6MzItMzQwOjUwIiBuYW1lPSJmaGlyOlJlc2VhcmNoRWxlbWVudFR5cGUiIHhzaTp0eXBlPSJOYW1lZFR5cGVTcGVjaWZpZXIiLz4KICAgICAgICAgPC9vcGVyYW5kPgogICAgICA8L2RlZj4KICAgICAgPGRlZiBsb2NhdG9yPSIzNDE6MS0zNDE6NjQiIG5hbWU9IlRvU3RyaW5nIiBjb250ZXh0PSJQYXRpZW50IiBhY2Nlc3NMZXZlbD0iUHVibGljIiB4c2k6dHlwZT0iRnVuY3Rpb25EZWYiPgogICAgICAgICA8ZXhwcmVzc2lvbiBsb2NhdG9yPSIzNDE6NTQtMzQxOjY0IiBwYXRoPSJ2YWx1ZSIgeHNpOnR5cGU9IlByb3BlcnR5Ij4KICAgICAgICAgICAgPHNvdXJjZSBsb2NhdG9yPSIzNDE6NTQtMzQxOjU4IiBuYW1lPSJ2YWx1ZSIgeHNpOnR5cGU9Ik9wZXJhbmRSZWYiLz4KICAgICAgICAgPC9leHByZXNzaW9uPgogICAgICAgICA8b3BlcmFuZCBuYW1lPSJ2YWx1ZSI+CiAgICAgICAgICAgIDxvcGVyYW5kVHlwZVNwZWNpZmllciBsb2NhdG9yPSIzNDE6MzItMzQxOjUwIiBuYW1lPSJmaGlyOlJlc2VhcmNoU3R1ZHlTdGF0dXMiIHhzaTp0eXBlPSJOYW1lZFR5cGVTcGVjaWZpZXIiLz4KICAgICAgICAgPC9vcGVyYW5kPgogICAgICA8L2RlZj4KICAgICAgPGRlZiBsb2NhdG9yPSIzNDI6MS0zNDI6NjYiIG5hbWU9IlRvU3RyaW5nIiBjb250ZXh0PSJQYXRpZW50IiBhY2Nlc3NMZXZlbD0iUHVibGljIiB4c2k6dHlwZT0iRnVuY3Rpb25EZWYiPgogICAgICAgICA8ZXhwcmVzc2lvbiBsb2NhdG9yPSIzNDI6NTYtMzQyOjY2IiBwYXRoPSJ2YWx1ZSIgeHNpOnR5cGU9IlByb3BlcnR5Ij4KICAgICAgICAgICAgPHNvdXJjZSBsb2NhdG9yPSIzNDI6NTYtMzQyOjYwIiBuYW1lPSJ2YWx1ZSIgeHNpOnR5cGU9Ik9wZXJhbmRSZWYiLz4KICAgICAgICAgPC9leHByZXNzaW9uPgogICAgICAgICA8b3BlcmFuZCBuYW1lPSJ2YWx1ZSI+CiAgICAgICAgICAgIDxvcGVyYW5kVHlwZVNwZWNpZmllciBsb2NhdG9yPSIzNDI6MzItMzQyOjUyIiBuYW1lPSJmaGlyOlJlc2VhcmNoU3ViamVjdFN0YXR1cyIgeHNpOnR5cGU9Ik5hbWVkVHlwZVNwZWNpZmllciIvPgogICAgICAgICA8L29wZXJhbmQ+CiAgICAgIDwvZGVmPgogICAgICA8ZGVmIGxvY2F0b3I9IjM0MzoxLTM0Mzo1NyIgbmFtZT0iVG9TdHJpbmciIGNvbnRleHQ9IlBhdGllbnQiIGFjY2Vzc0xldmVsPSJQdWJsaWMiIHhzaTp0eXBlPSJGdW5jdGlvbkRlZiI+CiAgICAgICAgIDxleHByZXNzaW9uIGxvY2F0b3I9IjM0Mzo0Ny0zNDM6NTciIHBhdGg9InZhbHVlIiB4c2k6dHlwZT0iUHJvcGVydHkiPgogICAgICAgICAgICA8c291cmNlIGxvY2F0b3I9IjM0Mzo0Ny0zNDM6NTEiIG5hbWU9InZhbHVlIiB4c2k6dHlwZT0iT3BlcmFuZFJlZiIvPgogICAgICAgICA8L2V4cHJlc3Npb24+CiAgICAgICAgIDxvcGVyYW5kIG5hbWU9InZhbHVlIj4KICAgICAgICAgICAgPG9wZXJhbmRUeXBlU3BlY2lmaWVyIGxvY2F0b3I9IjM0MzozMi0zNDM6NDMiIG5hbWU9ImZoaXI6UmVzb3VyY2VUeXBlIiB4c2k6dHlwZT0iTmFtZWRUeXBlU3BlY2lmaWVyIi8+CiAgICAgICAgIDwvb3BlcmFuZD4KICAgICAgPC9kZWY+CiAgICAgIDxkZWYgbG9jYXRvcj0iMzQ0OjEtMzQ0OjY2IiBuYW1lPSJUb1N0cmluZyIgY29udGV4dD0iUGF0aWVudCIgYWNjZXNzTGV2ZWw9IlB1YmxpYyIgeHNpOnR5cGU9IkZ1bmN0aW9uRGVmIj4KICAgICAgICAgPGV4cHJlc3Npb24gbG9jYXRvcj0iMzQ0OjU2LTM0NDo2NiIgcGF0aD0idmFsdWUiIHhzaTp0eXBlPSJQcm9wZXJ0eSI+CiAgICAgICAgICAgIDxzb3VyY2UgbG9jYXRvcj0iMzQ0OjU2LTM0NDo2MCIgbmFtZT0idmFsdWUiIHhzaTp0eXBlPSJPcGVyYW5kUmVmIi8+CiAgICAgICAgIDwvZXhwcmVzc2lvbj4KICAgICAgICAgPG9wZXJhbmQgbmFtZT0idmFsdWUiPgogICAgICAgICAgICA8b3BlcmFuZFR5cGVTcGVjaWZpZXIgbG9jYXRvcj0iMzQ0OjMyLTM0NDo1MiIgbmFtZT0iZmhpcjpSZXNvdXJjZVZlcnNpb25Qb2xpY3kiIHhzaTp0eXBlPSJOYW1lZFR5cGVTcGVjaWZpZXIiLz4KICAgICAgICAgPC9vcGVyYW5kPgogICAgICA8L2RlZj4KICAgICAgPGRlZiBsb2NhdG9yPSIzNDU6MS0zNDU6NTciIG5hbWU9IlRvU3RyaW5nIiBjb250ZXh0PSJQYXRpZW50IiBhY2Nlc3NMZXZlbD0iUHVibGljIiB4c2k6dHlwZT0iRnVuY3Rpb25EZWYiPgogICAgICAgICA8ZXhwcmVzc2lvbiBsb2NhdG9yPSIzNDU6NDctMzQ1OjU3IiBwYXRoPSJ2YWx1ZSIgeHNpOnR5cGU9IlByb3BlcnR5Ij4KICAgICAgICAgICAgPHNvdXJjZSBsb2NhdG9yPSIzNDU6NDctMzQ1OjUxIiBuYW1lPSJ2YWx1ZSIgeHNpOnR5cGU9Ik9wZXJhbmRSZWYiLz4KICAgICAgICAgPC9leHByZXNzaW9uPgogICAgICAgICA8b3BlcmFuZCBuYW1lPSJ2YWx1ZSI+CiAgICAgICAgICAgIDxvcGVyYW5kVHlwZVNwZWNpZmllciBsb2NhdG9yPSIzNDU6MzItMzQ1OjQzIiBuYW1lPSJmaGlyOlJlc3BvbnNlVHlwZSIgeHNpOnR5cGU9Ik5hbWVkVHlwZVNwZWNpZmllciIvPgogICAgICAgICA8L29wZXJhbmQ+CiAgICAgIDwvZGVmPgogICAgICA8ZGVmIGxvY2F0b3I9IjM0NjoxLTM0Njo2NiIgbmFtZT0iVG9TdHJpbmciIGNvbnRleHQ9IlBhdGllbnQiIGFjY2Vzc0xldmVsPSJQdWJsaWMiIHhzaTp0eXBlPSJGdW5jdGlvbkRlZiI+CiAgICAgICAgIDxleHByZXNzaW9uIGxvY2F0b3I9IjM0Njo1Ni0zNDY6NjYiIHBhdGg9InZhbHVlIiB4c2k6dHlwZT0iUHJvcGVydHkiPgogICAgICAgICAgICA8c291cmNlIGxvY2F0b3I9IjM0Njo1Ni0zNDY6NjAiIG5hbWU9InZhbHVlIiB4c2k6dHlwZT0iT3BlcmFuZFJlZiIvPgogICAgICAgICA8L2V4cHJlc3Npb24+CiAgICAgICAgIDxvcGVyYW5kIG5hbWU9InZhbHVlIj4KICAgICAgICAgICAgPG9wZXJhbmRUeXBlU3BlY2lmaWVyIGxvY2F0b3I9IjM0NjozMi0zNDY6NTIiIG5hbWU9ImZoaXI6UmVzdGZ1bENhcGFiaWxpdHlNb2RlIiB4c2k6dHlwZT0iTmFtZWRUeXBlU3BlY2lmaWVyIi8+CiAgICAgICAgIDwvb3BlcmFuZD4KICAgICAgPC9kZWY+CiAgICAgIDxkZWYgbG9jYXRvcj0iMzQ3OjEtMzQ3OjY1IiBuYW1lPSJUb1N0cmluZyIgY29udGV4dD0iUGF0aWVudCIgYWNjZXNzTGV2ZWw9IlB1YmxpYyIgeHNpOnR5cGU9IkZ1bmN0aW9uRGVmIj4KICAgICAgICAgPGV4cHJlc3Npb24gbG9jYXRvcj0iMzQ3OjU1LTM0Nzo2NSIgcGF0aD0idmFsdWUiIHhzaTp0eXBlPSJQcm9wZXJ0eSI+CiAgICAgICAgICAgIDxzb3VyY2UgbG9jYXRvcj0iMzQ3OjU1LTM0Nzo1OSIgbmFtZT0idmFsdWUiIHhzaTp0eXBlPSJPcGVyYW5kUmVmIi8+CiAgICAgICAgIDwvZXhwcmVzc2lvbj4KICAgICAgICAgPG9wZXJhbmQgbmFtZT0idmFsdWUiPgogICAgICAgICAgICA8b3BlcmFuZFR5cGVTcGVjaWZpZXIgbG9jYXRvcj0iMzQ3OjMyLTM0Nzo1MSIgbmFtZT0iZmhpcjpSaXNrQXNzZXNzbWVudFN0YXR1cyIgeHNpOnR5cGU9Ik5hbWVkVHlwZVNwZWNpZmllciIvPgogICAgICAgICA8L29wZXJhbmQ+CiAgICAgIDwvZGVmPgogICAgICA8ZGVmIGxvY2F0b3I9IjM0ODoxLTM0ODo1NiIgbmFtZT0iVG9TdHJpbmciIGNvbnRleHQ9IlBhdGllbnQiIGFjY2Vzc0xldmVsPSJQdWJsaWMiIHhzaTp0eXBlPSJGdW5jdGlvbkRlZiI+CiAgICAgICAgIDxleHByZXNzaW9uIGxvY2F0b3I9IjM0ODo0Ni0zNDg6NTYiIHBhdGg9InZhbHVlIiB4c2k6dHlwZT0iUHJvcGVydHkiPgogICAgICAgICAgICA8c291cmNlIGxvY2F0b3I9IjM0ODo0Ni0zNDg6NTAiIG5hbWU9InZhbHVlIiB4c2k6dHlwZT0iT3BlcmFuZFJlZiIvPgogICAgICAgICA8L2V4cHJlc3Npb24+CiAgICAgICAgIDxvcGVyYW5kIG5hbWU9InZhbHVlIj4KICAgICAgICAgICAgPG9wZXJhbmRUeXBlU3BlY2lmaWVyIGxvY2F0b3I9IjM0ODozMi0zNDg6NDIiIG5hbWU9ImZoaXI6U1BEWExpY2Vuc2UiIHhzaTp0eXBlPSJOYW1lZFR5cGVTcGVjaWZpZXIiLz4KICAgICAgICAgPC9vcGVyYW5kPgogICAgICA8L2RlZj4KICAgICAgPGRlZiBsb2NhdG9yPSIzNDk6MS0zNDk6NjEiIG5hbWU9IlRvU3RyaW5nIiBjb250ZXh0PSJQYXRpZW50IiBhY2Nlc3NMZXZlbD0iUHVibGljIiB4c2k6dHlwZT0iRnVuY3Rpb25EZWYiPgogICAgICAgICA8ZXhwcmVzc2lvbiBsb2NhdG9yPSIzNDk6NTEtMzQ5OjYxIiBwYXRoPSJ2YWx1ZSIgeHNpOnR5cGU9IlByb3BlcnR5Ij4KICAgICAgICAgICAgPHNvdXJjZSBsb2NhdG9yPSIzNDk6NTEtMzQ5OjU1IiBuYW1lPSJ2YWx1ZSIgeHNpOnR5cGU9Ik9wZXJhbmRSZWYiLz4KICAgICAgICAgPC9leHByZXNzaW9uPgogICAgICAgICA8b3BlcmFuZCBuYW1lPSJ2YWx1ZSI+CiAgICAgICAgICAgIDxvcGVyYW5kVHlwZVNwZWNpZmllciBsb2NhdG9yPSIzNDk6MzItMzQ5OjQ3IiBuYW1lPSJmaGlyOlNlYXJjaENvbXBhcmF0b3IiIHhzaTp0eXBlPSJOYW1lZFR5cGVTcGVjaWZpZXIiLz4KICAgICAgICAgPC9vcGVyYW5kPgogICAgICA8L2RlZj4KICAgICAgPGRlZiBsb2NhdG9yPSIzNTA6MS0zNTA6NjAiIG5hbWU9IlRvU3RyaW5nIiBjb250ZXh0PSJQYXRpZW50IiBhY2Nlc3NMZXZlbD0iUHVibGljIiB4c2k6dHlwZT0iRnVuY3Rpb25EZWYiPgogICAgICAgICA8ZXhwcmVzc2lvbiBsb2NhdG9yPSIzNTA6NTAtMzUwOjYwIiBwYXRoPSJ2YWx1ZSIgeHNpOnR5cGU9IlByb3BlcnR5Ij4KICAgICAgICAgICAgPHNvdXJjZSBsb2NhdG9yPSIzNTA6NTAtMzUwOjU0IiBuYW1lPSJ2YWx1ZSIgeHNpOnR5cGU9Ik9wZXJhbmRSZWYiLz4KICAgICAgICAgPC9leHByZXNzaW9uPgogICAgICAgICA8b3BlcmFuZCBuYW1lPSJ2YWx1ZSI+CiAgICAgICAgICAgIDxvcGVyYW5kVHlwZVNwZWNpZmllciBsb2NhdG9yPSIzNTA6MzItMzUwOjQ2IiBuYW1lPSJmaGlyOlNlYXJjaEVudHJ5TW9kZSIgeHNpOnR5cGU9Ik5hbWVkVHlwZVNwZWNpZmllciIvPgogICAgICAgICA8L29wZXJhbmQ+CiAgICAgIDwvZGVmPgogICAgICA8ZGVmIGxvY2F0b3I9IjM1MToxLTM1MTo2MyIgbmFtZT0iVG9TdHJpbmciIGNvbnRleHQ9IlBhdGllbnQiIGFjY2Vzc0xldmVsPSJQdWJsaWMiIHhzaTp0eXBlPSJGdW5jdGlvbkRlZiI+CiAgICAgICAgIDxleHByZXNzaW9uIGxvY2F0b3I9IjM1MTo1My0zNTE6NjMiIHBhdGg9InZhbHVlIiB4c2k6dHlwZT0iUHJvcGVydHkiPgogICAgICAgICAgICA8c291cmNlIGxvY2F0b3I9IjM1MTo1My0zNTE6NTciIG5hbWU9InZhbHVlIiB4c2k6dHlwZT0iT3BlcmFuZFJlZiIvPgogICAgICAgICA8L2V4cHJlc3Npb24+CiAgICAgICAgIDxvcGVyYW5kIG5hbWU9InZhbHVlIj4KICAgICAgICAgICAgPG9wZXJhbmRUeXBlU3BlY2lmaWVyIGxvY2F0b3I9IjM1MTozMi0zNTE6NDkiIG5hbWU9ImZoaXI6U2VhcmNoTW9kaWZpZXJDb2RlIiB4c2k6dHlwZT0iTmFtZWRUeXBlU3BlY2lmaWVyIi8+CiAgICAgICAgIDwvb3BlcmFuZD4KICAgICAgPC9kZWY+CiAgICAgIDxkZWYgbG9jYXRvcj0iMzUyOjEtMzUyOjYwIiBuYW1lPSJUb1N0cmluZyIgY29udGV4dD0iUGF0aWVudCIgYWNjZXNzTGV2ZWw9IlB1YmxpYyIgeHNpOnR5cGU9IkZ1bmN0aW9uRGVmIj4KICAgICAgICAgPGV4cHJlc3Npb24gbG9jYXRvcj0iMzUyOjUwLTM1Mjo2MCIgcGF0aD0idmFsdWUiIHhzaTp0eXBlPSJQcm9wZXJ0eSI+CiAgICAgICAgICAgIDxzb3VyY2UgbG9jYXRvcj0iMzUyOjUwLTM1Mjo1NCIgbmFtZT0idmFsdWUiIHhzaTp0eXBlPSJPcGVyYW5kUmVmIi8+CiAgICAgICAgIDwvZXhwcmVzc2lvbj4KICAgICAgICAgPG9wZXJhbmQgbmFtZT0idmFsdWUiPgogICAgICAgICAgICA8b3BlcmFuZFR5cGVTcGVjaWZpZXIgbG9jYXRvcj0iMzUyOjMyLTM1Mjo0NiIgbmFtZT0iZmhpcjpTZWFyY2hQYXJhbVR5cGUiIHhzaTp0eXBlPSJOYW1lZFR5cGVTcGVjaWZpZXIiLz4KICAgICAgICAgPC9vcGVyYW5kPgogICAgICA8L2RlZj4KICAgICAgPGRlZiBsb2NhdG9yPSIzNTM6MS0zNTM6NTYiIG5hbWU9IlRvU3RyaW5nIiBjb250ZXh0PSJQYXRpZW50IiBhY2Nlc3NMZXZlbD0iUHVibGljIiB4c2k6dHlwZT0iRnVuY3Rpb25EZWYiPgogICAgICAgICA8ZXhwcmVzc2lvbiBsb2NhdG9yPSIzNTM6NDYtMzUzOjU2IiBwYXRoPSJ2YWx1ZSIgeHNpOnR5cGU9IlByb3BlcnR5Ij4KICAgICAgICAgICAgPHNvdXJjZSBsb2NhdG9yPSIzNTM6NDYtMzUzOjUwIiBuYW1lPSJ2YWx1ZSIgeHNpOnR5cGU9Ik9wZXJhbmRSZWYiLz4KICAgICAgICAgPC9leHByZXNzaW9uPgogICAgICAgICA8b3BlcmFuZCBuYW1lPSJ2YWx1ZSI+CiAgICAgICAgICAgIDxvcGVyYW5kVHlwZVNwZWNpZmllciBsb2NhdG9yPSIzNTM6MzItMzUzOjQyIiBuYW1lPSJmaGlyOlNlY3Rpb25Nb2RlIiB4c2k6dHlwZT0iTmFtZWRUeXBlU3BlY2lmaWVyIi8+CiAgICAgICAgIDwvb3BlcmFuZD4KICAgICAgPC9kZWY+CiAgICAgIDxkZWYgbG9jYXRvcj0iMzU0OjEtMzU0OjU3IiBuYW1lPSJUb1N0cmluZyIgY29udGV4dD0iUGF0aWVudCIgYWNjZXNzTGV2ZWw9IlB1YmxpYyIgeHNpOnR5cGU9IkZ1bmN0aW9uRGVmIj4KICAgICAgICAgPGV4cHJlc3Npb24gbG9jYXRvcj0iMzU0OjQ3LTM1NDo1NyIgcGF0aD0idmFsdWUiIHhzaTp0eXBlPSJQcm9wZXJ0eSI+CiAgICAgICAgICAgIDxzb3VyY2UgbG9jYXRvcj0iMzU0OjQ3LTM1NDo1MSIgbmFtZT0idmFsdWUiIHhzaTp0eXBlPSJPcGVyYW5kUmVmIi8+CiAgICAgICAgIDwvZXhwcmVzc2lvbj4KICAgICAgICAgPG9wZXJhbmQgbmFtZT0idmFsdWUiPgogICAgICAgICAgICA8b3BlcmFuZFR5cGVTcGVjaWZpZXIgbG9jYXRvcj0iMzU0OjMyLTM1NDo0MyIgbmFtZT0iZmhpcjpTZXF1ZW5jZVR5cGUiIHhzaTp0eXBlPSJOYW1lZFR5cGVTcGVjaWZpZXIiLz4KICAgICAgICAgPC9vcGVyYW5kPgogICAgICA8L2RlZj4KICAgICAgPGRlZiBsb2NhdG9yPSIzNTU6MS0zNTU6NjUiIG5hbWU9IlRvU3RyaW5nIiBjb250ZXh0PSJQYXRpZW50IiBhY2Nlc3NMZXZlbD0iUHVibGljIiB4c2k6dHlwZT0iRnVuY3Rpb25EZWYiPgogICAgICAgICA8ZXhwcmVzc2lvbiBsb2NhdG9yPSIzNTU6NTUtMzU1OjY1IiBwYXRoPSJ2YWx1ZSIgeHNpOnR5cGU9IlByb3BlcnR5Ij4KICAgICAgICAgICAgPHNvdXJjZSBsb2NhdG9yPSIzNTU6NTUtMzU1OjU5IiBuYW1lPSJ2YWx1ZSIgeHNpOnR5cGU9Ik9wZXJhbmRSZWYiLz4KICAgICAgICAgPC9leHByZXNzaW9uPgogICAgICAgICA8b3BlcmFuZCBuYW1lPSJ2YWx1ZSI+CiAgICAgICAgICAgIDxvcGVyYW5kVHlwZVNwZWNpZmllciBsb2NhdG9yPSIzNTU6MzItMzU1OjUxIiBuYW1lPSJmaGlyOlNlcnZpY2VSZXF1ZXN0SW50ZW50IiB4c2k6dHlwZT0iTmFtZWRUeXBlU3BlY2lmaWVyIi8+CiAgICAgICAgIDwvb3BlcmFuZD4KICAgICAgPC9kZWY+CiAgICAgIDxkZWYgbG9jYXRvcj0iMzU2OjEtMzU2OjY3IiBuYW1lPSJUb1N0cmluZyIgY29udGV4dD0iUGF0aWVudCIgYWNjZXNzTGV2ZWw9IlB1YmxpYyIgeHNpOnR5cGU9IkZ1bmN0aW9uRGVmIj4KICAgICAgICAgPGV4cHJlc3Npb24gbG9jYXRvcj0iMzU2OjU3LTM1Njo2NyIgcGF0aD0idmFsdWUiIHhzaTp0eXBlPSJQcm9wZXJ0eSI+CiAgICAgICAgICAgIDxzb3VyY2UgbG9jYXRvcj0iMzU2OjU3LTM1Njo2MSIgbmFtZT0idmFsdWUiIHhzaTp0eXBlPSJPcGVyYW5kUmVmIi8+CiAgICAgICAgIDwvZXhwcmVzc2lvbj4KICAgICAgICAgPG9wZXJhbmQgbmFtZT0idmFsdWUiPgogICAgICAgICAgICA8b3BlcmFuZFR5cGVTcGVjaWZpZXIgbG9jYXRvcj0iMzU2OjMyLTM1Njo1MyIgbmFtZT0iZmhpcjpTZXJ2aWNlUmVxdWVzdFByaW9yaXR5IiB4c2k6dHlwZT0iTmFtZWRUeXBlU3BlY2lmaWVyIi8+CiAgICAgICAgIDwvb3BlcmFuZD4KICAgICAgPC9kZWY+CiAgICAgIDxkZWYgbG9jYXRvcj0iMzU3OjEtMzU3OjY1IiBuYW1lPSJUb1N0cmluZyIgY29udGV4dD0iUGF0aWVudCIgYWNjZXNzTGV2ZWw9IlB1YmxpYyIgeHNpOnR5cGU9IkZ1bmN0aW9uRGVmIj4KICAgICAgICAgPGV4cHJlc3Npb24gbG9jYXRvcj0iMzU3OjU1LTM1Nzo2NSIgcGF0aD0idmFsdWUiIHhzaTp0eXBlPSJQcm9wZXJ0eSI+CiAgICAgICAgICAgIDxzb3VyY2UgbG9jYXRvcj0iMzU3OjU1LTM1Nzo1OSIgbmFtZT0idmFsdWUiIHhzaTp0eXBlPSJPcGVyYW5kUmVmIi8+CiAgICAgICAgIDwvZXhwcmVzc2lvbj4KICAgICAgICAgPG9wZXJhbmQgbmFtZT0idmFsdWUiPgogICAgICAgICAgICA8b3BlcmFuZFR5cGVTcGVjaWZpZXIgbG9jYXRvcj0iMzU3OjMyLTM1Nzo1MSIgbmFtZT0iZmhpcjpTZXJ2aWNlUmVxdWVzdFN0YXR1cyIgeHNpOnR5cGU9Ik5hbWVkVHlwZVNwZWNpZmllciIvPgogICAgICAgICA8L29wZXJhbmQ+CiAgICAgIDwvZGVmPgogICAgICA8ZGVmIGxvY2F0b3I9IjM1ODoxLTM1ODo1NyIgbmFtZT0iVG9TdHJpbmciIGNvbnRleHQ9IlBhdGllbnQiIGFjY2Vzc0xldmVsPSJQdWJsaWMiIHhzaTp0eXBlPSJGdW5jdGlvbkRlZiI+CiAgICAgICAgIDxleHByZXNzaW9uIGxvY2F0b3I9IjM1ODo0Ny0zNTg6NTciIHBhdGg9InZhbHVlIiB4c2k6dHlwZT0iUHJvcGVydHkiPgogICAgICAgICAgICA8c291cmNlIGxvY2F0b3I9IjM1ODo0Ny0zNTg6NTEiIG5hbWU9InZhbHVlIiB4c2k6dHlwZT0iT3BlcmFuZFJlZiIvPgogICAgICAgICA8L2V4cHJlc3Npb24+CiAgICAgICAgIDxvcGVyYW5kIG5hbWU9InZhbHVlIj4KICAgICAgICAgICAgPG9wZXJhbmRUeXBlU3BlY2lmaWVyIGxvY2F0b3I9IjM1ODozMi0zNTg6NDMiIG5hbWU9ImZoaXI6U2xpY2luZ1J1bGVzIiB4c2k6dHlwZT0iTmFtZWRUeXBlU3BlY2lmaWVyIi8+CiAgICAgICAgIDwvb3BlcmFuZD4KICAgICAgPC9kZWY+CiAgICAgIDxkZWYgbG9jYXRvcj0iMzU5OjEtMzU5OjU1IiBuYW1lPSJUb1N0cmluZyIgY29udGV4dD0iUGF0aWVudCIgYWNjZXNzTGV2ZWw9IlB1YmxpYyIgeHNpOnR5cGU9IkZ1bmN0aW9uRGVmIj4KICAgICAgICAgPGV4cHJlc3Npb24gbG9jYXRvcj0iMzU5OjQ1LTM1OTo1NSIgcGF0aD0idmFsdWUiIHhzaTp0eXBlPSJQcm9wZXJ0eSI+CiAgICAgICAgICAgIDxzb3VyY2UgbG9jYXRvcj0iMzU5OjQ1LTM1OTo0OSIgbmFtZT0idmFsdWUiIHhzaTp0eXBlPSJPcGVyYW5kUmVmIi8+CiAgICAgICAgIDwvZXhwcmVzc2lvbj4KICAgICAgICAgPG9wZXJhbmQgbmFtZT0idmFsdWUiPgogICAgICAgICAgICA8b3BlcmFuZFR5cGVTcGVjaWZpZXIgbG9jYXRvcj0iMzU5OjMyLTM1OTo0MSIgbmFtZT0iZmhpcjpTbG90U3RhdHVzIiB4c2k6dHlwZT0iTmFtZWRUeXBlU3BlY2lmaWVyIi8+CiAgICAgICAgIDwvb3BlcmFuZD4KICAgICAgPC9kZWY+CiAgICAgIDxkZWYgbG9jYXRvcj0iMzYwOjEtMzYwOjU4IiBuYW1lPSJUb1N0cmluZyIgY29udGV4dD0iUGF0aWVudCIgYWNjZXNzTGV2ZWw9IlB1YmxpYyIgeHNpOnR5cGU9IkZ1bmN0aW9uRGVmIj4KICAgICAgICAgPGV4cHJlc3Npb24gbG9jYXRvcj0iMzYwOjQ4LTM2MDo1OCIgcGF0aD0idmFsdWUiIHhzaTp0eXBlPSJQcm9wZXJ0eSI+CiAgICAgICAgICAgIDxzb3VyY2UgbG9jYXRvcj0iMzYwOjQ4LTM2MDo1MiIgbmFtZT0idmFsdWUiIHhzaTp0eXBlPSJPcGVyYW5kUmVmIi8+CiAgICAgICAgIDwvZXhwcmVzc2lvbj4KICAgICAgICAgPG9wZXJhbmQgbmFtZT0idmFsdWUiPgogICAgICAgICAgICA8b3BlcmFuZFR5cGVTcGVjaWZpZXIgbG9jYXRvcj0iMzYwOjMyLTM2MDo0NCIgbmFtZT0iZmhpcjpTb3J0RGlyZWN0aW9uIiB4c2k6dHlwZT0iTmFtZWRUeXBlU3BlY2lmaWVyIi8+CiAgICAgICAgIDwvb3BlcmFuZD4KICAgICAgPC9kZWY+CiAgICAgIDxkZWYgbG9jYXRvcj0iMzYxOjEtMzYxOjcyIiBuYW1lPSJUb1N0cmluZyIgY29udGV4dD0iUGF0aWVudCIgYWNjZXNzTGV2ZWw9IlB1YmxpYyIgeHNpOnR5cGU9IkZ1bmN0aW9uRGVmIj4KICAgICAgICAgPGV4cHJlc3Npb24gbG9jYXRvcj0iMzYxOjYyLTM2MTo3MiIgcGF0aD0idmFsdWUiIHhzaTp0eXBlPSJQcm9wZXJ0eSI+CiAgICAgICAgICAgIDxzb3VyY2UgbG9jYXRvcj0iMzYxOjYyLTM2MTo2NiIgbmFtZT0idmFsdWUiIHhzaTp0eXBlPSJPcGVyYW5kUmVmIi8+CiAgICAgICAgIDwvZXhwcmVzc2lvbj4KICAgICAgICAgPG9wZXJhbmQgbmFtZT0idmFsdWUiPgogICAgICAgICAgICA8b3BlcmFuZFR5cGVTcGVjaWZpZXIgbG9jYXRvcj0iMzYxOjMyLTM2MTo1OCIgbmFtZT0iZmhpcjpTcGVjaW1lbkNvbnRhaW5lZFByZWZlcmVuY2UiIHhzaTp0eXBlPSJOYW1lZFR5cGVTcGVjaWZpZXIiLz4KICAgICAgICAgPC9vcGVyYW5kPgogICAgICA8L2RlZj4KICAgICAgPGRlZiBsb2NhdG9yPSIzNjI6MS0zNjI6NTkiIG5hbWU9IlRvU3RyaW5nIiBjb250ZXh0PSJQYXRpZW50IiBhY2Nlc3NMZXZlbD0iUHVibGljIiB4c2k6dHlwZT0iRnVuY3Rpb25EZWYiPgogICAgICAgICA8ZXhwcmVzc2lvbiBsb2NhdG9yPSIzNjI6NDktMzYyOjU5IiBwYXRoPSJ2YWx1ZSIgeHNpOnR5cGU9IlByb3BlcnR5Ij4KICAgICAgICAgICAgPHNvdXJjZSBsb2NhdG9yPSIzNjI6NDktMzYyOjUzIiBuYW1lPSJ2YWx1ZSIgeHNpOnR5cGU9Ik9wZXJhbmRSZWYiLz4KICAgICAgICAgPC9leHByZXNzaW9uPgogICAgICAgICA8b3BlcmFuZCBuYW1lPSJ2YWx1ZSI+CiAgICAgICAgICAgIDxvcGVyYW5kVHlwZVNwZWNpZmllciBsb2NhdG9yPSIzNjI6MzItMzYyOjQ1IiBuYW1lPSJmaGlyOlNwZWNpbWVuU3RhdHVzIiB4c2k6dHlwZT0iTmFtZWRUeXBlU3BlY2lmaWVyIi8+CiAgICAgICAgIDwvb3BlcmFuZD4KICAgICAgPC9kZWY+CiAgICAgIDxkZWYgbG9jYXRvcj0iMzYzOjEtMzYzOjUxIiBuYW1lPSJUb1N0cmluZyIgY29udGV4dD0iUGF0aWVudCIgYWNjZXNzTGV2ZWw9IlB1YmxpYyIgeHNpOnR5cGU9IkZ1bmN0aW9uRGVmIj4KICAgICAgICAgPGV4cHJlc3Npb24gbG9jYXRvcj0iMzYzOjQxLTM2Mzo1MSIgcGF0aD0idmFsdWUiIHhzaTp0eXBlPSJQcm9wZXJ0eSI+CiAgICAgICAgICAgIDxzb3VyY2UgbG9jYXRvcj0iMzYzOjQxLTM2Mzo0NSIgbmFtZT0idmFsdWUiIHhzaTp0eXBlPSJPcGVyYW5kUmVmIi8+CiAgICAgICAgIDwvZXhwcmVzc2lvbj4KICAgICAgICAgPG9wZXJhbmQgbmFtZT0idmFsdWUiPgogICAgICAgICAgICA8b3BlcmFuZFR5cGVTcGVjaWZpZXIgbG9jYXRvcj0iMzYzOjMyLTM2MzozNyIgbmFtZT0iZmhpcjpTdGF0dXMiIHhzaTp0eXBlPSJOYW1lZFR5cGVTcGVjaWZpZXIiLz4KICAgICAgICAgPC9vcGVyYW5kPgogICAgICA8L2RlZj4KICAgICAgPGRlZiBsb2NhdG9yPSIzNjQ6MS0zNjQ6NTUiIG5hbWU9IlRvU3RyaW5nIiBjb250ZXh0PSJQYXRpZW50IiBhY2Nlc3NMZXZlbD0iUHVibGljIiB4c2k6dHlwZT0iRnVuY3Rpb25EZWYiPgogICAgICAgICA8ZXhwcmVzc2lvbiBsb2NhdG9yPSIzNjQ6NDUtMzY0OjU1IiBwYXRoPSJ2YWx1ZSIgeHNpOnR5cGU9IlByb3BlcnR5Ij4KICAgICAgICAgICAgPHNvdXJjZSBsb2NhdG9yPSIzNjQ6NDUtMzY0OjQ5IiBuYW1lPSJ2YWx1ZSIgeHNpOnR5cGU9Ik9wZXJhbmRSZWYiLz4KICAgICAgICAgPC9leHByZXNzaW9uPgogICAgICAgICA8b3BlcmFuZCBuYW1lPSJ2YWx1ZSI+CiAgICAgICAgICAgIDxvcGVyYW5kVHlwZVNwZWNpZmllciBsb2NhdG9yPSIzNjQ6MzItMzY0OjQxIiBuYW1lPSJmaGlyOlN0cmFuZFR5cGUiIHhzaTp0eXBlPSJOYW1lZFR5cGVTcGVjaWZpZXIiLz4KICAgICAgICAgPC9vcGVyYW5kPgogICAgICA8L2RlZj4KICAgICAgPGRlZiBsb2NhdG9yPSIzNjU6MS0zNjU6NjgiIG5hbWU9IlRvU3RyaW5nIiBjb250ZXh0PSJQYXRpZW50IiBhY2Nlc3NMZXZlbD0iUHVibGljIiB4c2k6dHlwZT0iRnVuY3Rpb25EZWYiPgogICAgICAgICA8ZXhwcmVzc2lvbiBsb2NhdG9yPSIzNjU6NTgtMzY1OjY4IiBwYXRoPSJ2YWx1ZSIgeHNpOnR5cGU9IlByb3BlcnR5Ij4KICAgICAgICAgICAgPHNvdXJjZSBsb2NhdG9yPSIzNjU6NTgtMzY1OjYyIiBuYW1lPSJ2YWx1ZSIgeHNpOnR5cGU9Ik9wZXJhbmRSZWYiLz4KICAgICAgICAgPC9leHByZXNzaW9uPgogICAgICAgICA8b3BlcmFuZCBuYW1lPSJ2YWx1ZSI+CiAgICAgICAgICAgIDxvcGVyYW5kVHlwZVNwZWNpZmllciBsb2NhdG9yPSIzNjU6MzItMzY1OjU0IiBuYW1lPSJmaGlyOlN0cnVjdHVyZURlZmluaXRpb25LaW5kIiB4c2k6dHlwZT0iTmFtZWRUeXBlU3BlY2lmaWVyIi8+CiAgICAgICAgIDwvb3BlcmFuZD4KICAgICAgPC9kZWY+CiAgICAgIDxkZWYgbG9jYXRvcj0iMzY2OjEtMzY2OjY4IiBuYW1lPSJUb1N0cmluZyIgY29udGV4dD0iUGF0aWVudCIgYWNjZXNzTGV2ZWw9IlB1YmxpYyIgeHNpOnR5cGU9IkZ1bmN0aW9uRGVmIj4KICAgICAgICAgPGV4cHJlc3Npb24gbG9jYXRvcj0iMzY2OjU4LTM2Njo2OCIgcGF0aD0idmFsdWUiIHhzaTp0eXBlPSJQcm9wZXJ0eSI+CiAgICAgICAgICAgIDxzb3VyY2UgbG9jYXRvcj0iMzY2OjU4LTM2Njo2MiIgbmFtZT0idmFsdWUiIHhzaTp0eXBlPSJPcGVyYW5kUmVmIi8+CiAgICAgICAgIDwvZXhwcmVzc2lvbj4KICAgICAgICAgPG9wZXJhbmQgbmFtZT0idmFsdWUiPgogICAgICAgICAgICA8b3BlcmFuZFR5cGVTcGVjaWZpZXIgbG9jYXRvcj0iMzY2OjMyLTM2Njo1NCIgbmFtZT0iZmhpcjpTdHJ1Y3R1cmVNYXBDb250ZXh0VHlwZSIgeHNpOnR5cGU9Ik5hbWVkVHlwZVNwZWNpZmllciIvPgogICAgICAgICA8L29wZXJhbmQ+CiAgICAgIDwvZGVmPgogICAgICA8ZGVmIGxvY2F0b3I9IjM2NzoxLTM2Nzo3MCIgbmFtZT0iVG9TdHJpbmciIGNvbnRleHQ9IlBhdGllbnQiIGFjY2Vzc0xldmVsPSJQdWJsaWMiIHhzaTp0eXBlPSJGdW5jdGlvbkRlZiI+CiAgICAgICAgIDxleHByZXNzaW9uIGxvY2F0b3I9IjM2Nzo2MC0zNjc6NzAiIHBhdGg9InZhbHVlIiB4c2k6dHlwZT0iUHJvcGVydHkiPgogICAgICAgICAgICA8c291cmNlIGxvY2F0b3I9IjM2Nzo2MC0zNjc6NjQiIG5hbWU9InZhbHVlIiB4c2k6dHlwZT0iT3BlcmFuZFJlZiIvPgogICAgICAgICA8L2V4cHJlc3Npb24+CiAgICAgICAgIDxvcGVyYW5kIG5hbWU9InZhbHVlIj4KICAgICAgICAgICAgPG9wZXJhbmRUeXBlU3BlY2lmaWVyIGxvY2F0b3I9IjM2NzozMi0zNjc6NTYiIG5hbWU9ImZoaXI6U3RydWN0dXJlTWFwR3JvdXBUeXBlTW9kZSIgeHNpOnR5cGU9Ik5hbWVkVHlwZVNwZWNpZmllciIvPgogICAgICAgICA8L29wZXJhbmQ+CiAgICAgIDwvZGVmPgogICAgICA8ZGVmIGxvY2F0b3I9IjM2ODoxLTM2ODo2NiIgbmFtZT0iVG9TdHJpbmciIGNvbnRleHQ9IlBhdGllbnQiIGFjY2Vzc0xldmVsPSJQdWJsaWMiIHhzaTp0eXBlPSJGdW5jdGlvbkRlZiI+CiAgICAgICAgIDxleHByZXNzaW9uIGxvY2F0b3I9IjM2ODo1Ni0zNjg6NjYiIHBhdGg9InZhbHVlIiB4c2k6dHlwZT0iUHJvcGVydHkiPgogICAgICAgICAgICA8c291cmNlIGxvY2F0b3I9IjM2ODo1Ni0zNjg6NjAiIG5hbWU9InZhbHVlIiB4c2k6dHlwZT0iT3BlcmFuZFJlZiIvPgogICAgICAgICA8L2V4cHJlc3Npb24+CiAgICAgICAgIDxvcGVyYW5kIG5hbWU9InZhbHVlIj4KICAgICAgICAgICAgPG9wZXJhbmRUeXBlU3BlY2lmaWVyIGxvY2F0b3I9IjM2ODozMi0zNjg6NTIiIG5hbWU9ImZoaXI6U3RydWN0dXJlTWFwSW5wdXRNb2RlIiB4c2k6dHlwZT0iTmFtZWRUeXBlU3BlY2lmaWVyIi8+CiAgICAgICAgIDwvb3BlcmFuZD4KICAgICAgPC9kZWY+CiAgICAgIDxkZWYgbG9jYXRvcj0iMzY5OjEtMzY5OjY2IiBuYW1lPSJUb1N0cmluZyIgY29udGV4dD0iUGF0aWVudCIgYWNjZXNzTGV2ZWw9IlB1YmxpYyIgeHNpOnR5cGU9IkZ1bmN0aW9uRGVmIj4KICAgICAgICAgPGV4cHJlc3Npb24gbG9jYXRvcj0iMzY5OjU2LTM2OTo2NiIgcGF0aD0idmFsdWUiIHhzaTp0eXBlPSJQcm9wZXJ0eSI+CiAgICAgICAgICAgIDxzb3VyY2UgbG9jYXRvcj0iMzY5OjU2LTM2OTo2MCIgbmFtZT0idmFsdWUiIHhzaTp0eXBlPSJPcGVyYW5kUmVmIi8+CiAgICAgICAgIDwvZXhwcmVzc2lvbj4KICAgICAgICAgPG9wZXJhbmQgbmFtZT0idmFsdWUiPgogICAgICAgICAgICA8b3BlcmFuZFR5cGVTcGVjaWZpZXIgbG9jYXRvcj0iMzY5OjMyLTM2OTo1MiIgbmFtZT0iZmhpcjpTdHJ1Y3R1cmVNYXBNb2RlbE1vZGUiIHhzaTp0eXBlPSJOYW1lZFR5cGVTcGVjaWZpZXIiLz4KICAgICAgICAgPC9vcGVyYW5kPgogICAgICA8L2RlZj4KICAgICAgPGRlZiBsb2NhdG9yPSIzNzA6MS0zNzA6NzEiIG5hbWU9IlRvU3RyaW5nIiBjb250ZXh0PSJQYXRpZW50IiBhY2Nlc3NMZXZlbD0iUHVibGljIiB4c2k6dHlwZT0iRnVuY3Rpb25EZWYiPgogICAgICAgICA8ZXhwcmVzc2lvbiBsb2NhdG9yPSIzNzA6NjEtMzcwOjcxIiBwYXRoPSJ2YWx1ZSIgeHNpOnR5cGU9IlByb3BlcnR5Ij4KICAgICAgICAgICAgPHNvdXJjZSBsb2NhdG9yPSIzNzA6NjEtMzcwOjY1IiBuYW1lPSJ2YWx1ZSIgeHNpOnR5cGU9Ik9wZXJhbmRSZWYiLz4KICAgICAgICAgPC9leHByZXNzaW9uPgogICAgICAgICA8b3BlcmFuZCBuYW1lPSJ2YWx1ZSI+CiAgICAgICAgICAgIDxvcGVyYW5kVHlwZVNwZWNpZmllciBsb2NhdG9yPSIzNzA6MzItMzcwOjU3IiBuYW1lPSJmaGlyOlN0cnVjdHVyZU1hcFNvdXJjZUxpc3RNb2RlIiB4c2k6dHlwZT0iTmFtZWRUeXBlU3BlY2lmaWVyIi8+CiAgICAgICAgIDwvb3BlcmFuZD4KICAgICAgPC9kZWY+CiAgICAgIDxkZWYgbG9jYXRvcj0iMzcxOjEtMzcxOjcxIiBuYW1lPSJUb1N0cmluZyIgY29udGV4dD0iUGF0aWVudCIgYWNjZXNzTGV2ZWw9IlB1YmxpYyIgeHNpOnR5cGU9IkZ1bmN0aW9uRGVmIj4KICAgICAgICAgPGV4cHJlc3Npb24gbG9jYXRvcj0iMzcxOjYxLTM3MTo3MSIgcGF0aD0idmFsdWUiIHhzaTp0eXBlPSJQcm9wZXJ0eSI+CiAgICAgICAgICAgIDxzb3VyY2UgbG9jYXRvcj0iMzcxOjYxLTM3MTo2NSIgbmFtZT0idmFsdWUiIHhzaTp0eXBlPSJPcGVyYW5kUmVmIi8+CiAgICAgICAgIDwvZXhwcmVzc2lvbj4KICAgICAgICAgPG9wZXJhbmQgbmFtZT0idmFsdWUiPgogICAgICAgICAgICA8b3BlcmFuZFR5cGVTcGVjaWZpZXIgbG9jYXRvcj0iMzcxOjMyLTM3MTo1NyIgbmFtZT0iZmhpcjpTdHJ1Y3R1cmVNYXBUYXJnZXRMaXN0TW9kZSIgeHNpOnR5cGU9Ik5hbWVkVHlwZVNwZWNpZmllciIvPgogICAgICAgICA8L29wZXJhbmQ+CiAgICAgIDwvZGVmPgogICAgICA8ZGVmIGxvY2F0b3I9IjM3MjoxLTM3Mjo2NiIgbmFtZT0iVG9TdHJpbmciIGNvbnRleHQ9IlBhdGllbnQiIGFjY2Vzc0xldmVsPSJQdWJsaWMiIHhzaTp0eXBlPSJGdW5jdGlvbkRlZiI+CiAgICAgICAgIDxleHByZXNzaW9uIGxvY2F0b3I9IjM3Mjo1Ni0zNzI6NjYiIHBhdGg9InZhbHVlIiB4c2k6dHlwZT0iUHJvcGVydHkiPgogICAgICAgICAgICA8c291cmNlIGxvY2F0b3I9IjM3Mjo1Ni0zNzI6NjAiIG5hbWU9InZhbHVlIiB4c2k6dHlwZT0iT3BlcmFuZFJlZiIvPgogICAgICAgICA8L2V4cHJlc3Npb24+CiAgICAgICAgIDxvcGVyYW5kIG5hbWU9InZhbHVlIj4KICAgICAgICAgICAgPG9wZXJhbmRUeXBlU3BlY2lmaWVyIGxvY2F0b3I9IjM3MjozMi0zNzI6NTIiIG5hbWU9ImZoaXI6U3RydWN0dXJlTWFwVHJhbnNmb3JtIiB4c2k6dHlwZT0iTmFtZWRUeXBlU3BlY2lmaWVyIi8+CiAgICAgICAgIDwvb3BlcmFuZD4KICAgICAgPC9kZWY+CiAgICAgIDxkZWYgbG9jYXRvcj0iMzczOjEtMzczOjY4IiBuYW1lPSJUb1N0cmluZyIgY29udGV4dD0iUGF0aWVudCIgYWNjZXNzTGV2ZWw9IlB1YmxpYyIgeHNpOnR5cGU9IkZ1bmN0aW9uRGVmIj4KICAgICAgICAgPGV4cHJlc3Npb24gbG9jYXRvcj0iMzczOjU4LTM3Mzo2OCIgcGF0aD0idmFsdWUiIHhzaTp0eXBlPSJQcm9wZXJ0eSI+CiAgICAgICAgICAgIDxzb3VyY2UgbG9jYXRvcj0iMzczOjU4LTM3Mzo2MiIgbmFtZT0idmFsdWUiIHhzaTp0eXBlPSJPcGVyYW5kUmVmIi8+CiAgICAgICAgIDwvZXhwcmVzc2lvbj4KICAgICAgICAgPG9wZXJhbmQgbmFtZT0idmFsdWUiPgogICAgICAgICAgICA8b3BlcmFuZFR5cGVTcGVjaWZpZXIgbG9jYXRvcj0iMzczOjMyLTM3Mzo1NCIgbmFtZT0iZmhpcjpTdWJzY3JpcHRpb25DaGFubmVsVHlwZSIgeHNpOnR5cGU9Ik5hbWVkVHlwZVNwZWNpZmllciIvPgogICAgICAgICA8L29wZXJhbmQ+CiAgICAgIDwvZGVmPgogICAgICA8ZGVmIGxvY2F0b3I9IjM3NDoxLTM3NDo2MyIgbmFtZT0iVG9TdHJpbmciIGNvbnRleHQ9IlBhdGllbnQiIGFjY2Vzc0xldmVsPSJQdWJsaWMiIHhzaTp0eXBlPSJGdW5jdGlvbkRlZiI+CiAgICAgICAgIDxleHByZXNzaW9uIGxvY2F0b3I9IjM3NDo1My0zNzQ6NjMiIHBhdGg9InZhbHVlIiB4c2k6dHlwZT0iUHJvcGVydHkiPgogICAgICAgICAgICA8c291cmNlIGxvY2F0b3I9IjM3NDo1My0zNzQ6NTciIG5hbWU9InZhbHVlIiB4c2k6dHlwZT0iT3BlcmFuZFJlZiIvPgogICAgICAgICA8L2V4cHJlc3Npb24+CiAgICAgICAgIDxvcGVyYW5kIG5hbWU9InZhbHVlIj4KICAgICAgICAgICAgPG9wZXJhbmRUeXBlU3BlY2lmaWVyIGxvY2F0b3I9IjM3NDozMi0zNzQ6NDkiIG5hbWU9ImZoaXI6U3Vic2NyaXB0aW9uU3RhdHVzIiB4c2k6dHlwZT0iTmFtZWRUeXBlU3BlY2lmaWVyIi8+CiAgICAgICAgIDwvb3BlcmFuZD4KICAgICAgPC9kZWY+CiAgICAgIDxkZWYgbG9jYXRvcj0iMzc1OjEtMzc1OjY1IiBuYW1lPSJUb1N0cmluZyIgY29udGV4dD0iUGF0aWVudCIgYWNjZXNzTGV2ZWw9IlB1YmxpYyIgeHNpOnR5cGU9IkZ1bmN0aW9uRGVmIj4KICAgICAgICAgPGV4cHJlc3Npb24gbG9jYXRvcj0iMzc1OjU1LTM3NTo2NSIgcGF0aD0idmFsdWUiIHhzaTp0eXBlPSJQcm9wZXJ0eSI+CiAgICAgICAgICAgIDxzb3VyY2UgbG9jYXRvcj0iMzc1OjU1LTM3NTo1OSIgbmFtZT0idmFsdWUiIHhzaTp0eXBlPSJPcGVyYW5kUmVmIi8+CiAgICAgICAgIDwvZXhwcmVzc2lvbj4KICAgICAgICAgPG9wZXJhbmQgbmFtZT0idmFsdWUiPgogICAgICAgICAgICA8b3BlcmFuZFR5cGVTcGVjaWZpZXIgbG9jYXRvcj0iMzc1OjMyLTM3NTo1MSIgbmFtZT0iZmhpcjpTdXBwbHlEZWxpdmVyeVN0YXR1cyIgeHNpOnR5cGU9Ik5hbWVkVHlwZVNwZWNpZmllciIvPgogICAgICAgICA8L29wZXJhbmQ+CiAgICAgIDwvZGVmPgogICAgICA8ZGVmIGxvY2F0b3I9IjM3NjoxLTM3Njo2NCIgbmFtZT0iVG9TdHJpbmciIGNvbnRleHQ9IlBhdGllbnQiIGFjY2Vzc0xldmVsPSJQdWJsaWMiIHhzaTp0eXBlPSJGdW5jdGlvbkRlZiI+CiAgICAgICAgIDxleHByZXNzaW9uIGxvY2F0b3I9IjM3Njo1NC0zNzY6NjQiIHBhdGg9InZhbHVlIiB4c2k6dHlwZT0iUHJvcGVydHkiPgogICAgICAgICAgICA8c291cmNlIGxvY2F0b3I9IjM3Njo1NC0zNzY6NTgiIG5hbWU9InZhbHVlIiB4c2k6dHlwZT0iT3BlcmFuZFJlZiIvPgogICAgICAgICA8L2V4cHJlc3Npb24+CiAgICAgICAgIDxvcGVyYW5kIG5hbWU9InZhbHVlIj4KICAgICAgICAgICAgPG9wZXJhbmRUeXBlU3BlY2lmaWVyIGxvY2F0b3I9IjM3NjozMi0zNzY6NTAiIG5hbWU9ImZoaXI6U3VwcGx5UmVxdWVzdFN0YXR1cyIgeHNpOnR5cGU9Ik5hbWVkVHlwZVNwZWNpZmllciIvPgogICAgICAgICA8L29wZXJhbmQ+CiAgICAgIDwvZGVmPgogICAgICA8ZGVmIGxvY2F0b3I9IjM3NzoxLTM3Nzo2OSIgbmFtZT0iVG9TdHJpbmciIGNvbnRleHQ9IlBhdGllbnQiIGFjY2Vzc0xldmVsPSJQdWJsaWMiIHhzaTp0eXBlPSJGdW5jdGlvbkRlZiI+CiAgICAgICAgIDxleHByZXNzaW9uIGxvY2F0b3I9IjM3Nzo1OS0zNzc6NjkiIHBhdGg9InZhbHVlIiB4c2k6dHlwZT0iUHJvcGVydHkiPgogICAgICAgICAgICA8c291cmNlIGxvY2F0b3I9IjM3Nzo1OS0zNzc6NjMiIG5hbWU9InZhbHVlIiB4c2k6dHlwZT0iT3BlcmFuZFJlZiIvPgogICAgICAgICA8L2V4cHJlc3Npb24+CiAgICAgICAgIDxvcGVyYW5kIG5hbWU9InZhbHVlIj4KICAgICAgICAgICAgPG9wZXJhbmRUeXBlU3BlY2lmaWVyIGxvY2F0b3I9IjM3NzozMi0zNzc6NTUiIG5hbWU9ImZoaXI6U3lzdGVtUmVzdGZ1bEludGVyYWN0aW9uIiB4c2k6dHlwZT0iTmFtZWRUeXBlU3BlY2lmaWVyIi8+CiAgICAgICAgIDwvb3BlcmFuZD4KICAgICAgPC9kZWY+CiAgICAgIDxkZWYgbG9jYXRvcj0iMzc4OjEtMzc4OjU1IiBuYW1lPSJUb1N0cmluZyIgY29udGV4dD0iUGF0aWVudCIgYWNjZXNzTGV2ZWw9IlB1YmxpYyIgeHNpOnR5cGU9IkZ1bmN0aW9uRGVmIj4KICAgICAgICAgPGV4cHJlc3Npb24gbG9jYXRvcj0iMzc4OjQ1LTM3ODo1NSIgcGF0aD0idmFsdWUiIHhzaTp0eXBlPSJQcm9wZXJ0eSI+CiAgICAgICAgICAgIDxzb3VyY2UgbG9jYXRvcj0iMzc4OjQ1LTM3ODo0OSIgbmFtZT0idmFsdWUiIHhzaTp0eXBlPSJPcGVyYW5kUmVmIi8+CiAgICAgICAgIDwvZXhwcmVzc2lvbj4KICAgICAgICAgPG9wZXJhbmQgbmFtZT0idmFsdWUiPgogICAgICAgICAgICA8b3BlcmFuZFR5cGVTcGVjaWZpZXIgbG9jYXRvcj0iMzc4OjMyLTM3ODo0MSIgbmFtZT0iZmhpcjpUYXNrSW50ZW50IiB4c2k6dHlwZT0iTmFtZWRUeXBlU3BlY2lmaWVyIi8+CiAgICAgICAgIDwvb3BlcmFuZD4KICAgICAgPC9kZWY+CiAgICAgIDxkZWYgbG9jYXRvcj0iMzc5OjEtMzc5OjU3IiBuYW1lPSJUb1N0cmluZyIgY29udGV4dD0iUGF0aWVudCIgYWNjZXNzTGV2ZWw9IlB1YmxpYyIgeHNpOnR5cGU9IkZ1bmN0aW9uRGVmIj4KICAgICAgICAgPGV4cHJlc3Npb24gbG9jYXRvcj0iMzc5OjQ3LTM3OTo1NyIgcGF0aD0idmFsdWUiIHhzaTp0eXBlPSJQcm9wZXJ0eSI+CiAgICAgICAgICAgIDxzb3VyY2UgbG9jYXRvcj0iMzc5OjQ3LTM3OTo1MSIgbmFtZT0idmFsdWUiIHhzaTp0eXBlPSJPcGVyYW5kUmVmIi8+CiAgICAgICAgIDwvZXhwcmVzc2lvbj4KICAgICAgICAgPG9wZXJhbmQgbmFtZT0idmFsdWUiPgogICAgICAgICAgICA8b3BlcmFuZFR5cGVTcGVjaWZpZXIgbG9jYXRvcj0iMzc5OjMyLTM3OTo0MyIgbmFtZT0iZmhpcjpUYXNrUHJpb3JpdHkiIHhzaTp0eXBlPSJOYW1lZFR5cGVTcGVjaWZpZXIiLz4KICAgICAgICAgPC9vcGVyYW5kPgogICAgICA8L2RlZj4KICAgICAgPGRlZiBsb2NhdG9yPSIzODA6MS0zODA6NTUiIG5hbWU9IlRvU3RyaW5nIiBjb250ZXh0PSJQYXRpZW50IiBhY2Nlc3NMZXZlbD0iUHVibGljIiB4c2k6dHlwZT0iRnVuY3Rpb25EZWYiPgogICAgICAgICA8ZXhwcmVzc2lvbiBsb2NhdG9yPSIzODA6NDUtMzgwOjU1IiBwYXRoPSJ2YWx1ZSIgeHNpOnR5cGU9IlByb3BlcnR5Ij4KICAgICAgICAgICAgPHNvdXJjZSBsb2NhdG9yPSIzODA6NDUtMzgwOjQ5IiBuYW1lPSJ2YWx1ZSIgeHNpOnR5cGU9Ik9wZXJhbmRSZWYiLz4KICAgICAgICAgPC9leHByZXNzaW9uPgogICAgICAgICA8b3BlcmFuZCBuYW1lPSJ2YWx1ZSI+CiAgICAgICAgICAgIDxvcGVyYW5kVHlwZVNwZWNpZmllciBsb2NhdG9yPSIzODA6MzItMzgwOjQxIiBuYW1lPSJmaGlyOlRhc2tTdGF0dXMiIHhzaTp0eXBlPSJOYW1lZFR5cGVTcGVjaWZpZXIiLz4KICAgICAgICAgPC9vcGVyYW5kPgogICAgICA8L2RlZj4KICAgICAgPGRlZiBsb2NhdG9yPSIzODE6MS0zODE6NjciIG5hbWU9IlRvU3RyaW5nIiBjb250ZXh0PSJQYXRpZW50IiBhY2Nlc3NMZXZlbD0iUHVibGljIiB4c2k6dHlwZT0iRnVuY3Rpb25EZWYiPgogICAgICAgICA8ZXhwcmVzc2lvbiBsb2NhdG9yPSIzODE6NTctMzgxOjY3IiBwYXRoPSJ2YWx1ZSIgeHNpOnR5cGU9IlByb3BlcnR5Ij4KICAgICAgICAgICAgPHNvdXJjZSBsb2NhdG9yPSIzODE6NTctMzgxOjYxIiBuYW1lPSJ2YWx1ZSIgeHNpOnR5cGU9Ik9wZXJhbmRSZWYiLz4KICAgICAgICAgPC9leHByZXNzaW9uPgogICAgICAgICA8b3BlcmFuZCBuYW1lPSJ2YWx1ZSI+CiAgICAgICAgICAgIDxvcGVyYW5kVHlwZVNwZWNpZmllciBsb2NhdG9yPSIzODE6MzItMzgxOjUzIiBuYW1lPSJmaGlyOlRlc3RSZXBvcnRBY3Rpb25SZXN1bHQiIHhzaTp0eXBlPSJOYW1lZFR5cGVTcGVjaWZpZXIiLz4KICAgICAgICAgPC9vcGVyYW5kPgogICAgICA8L2RlZj4KICAgICAgPGRlZiBsb2NhdG9yPSIzODI6MS0zODI6NzAiIG5hbWU9IlRvU3RyaW5nIiBjb250ZXh0PSJQYXRpZW50IiBhY2Nlc3NMZXZlbD0iUHVibGljIiB4c2k6dHlwZT0iRnVuY3Rpb25EZWYiPgogICAgICAgICA8ZXhwcmVzc2lvbiBsb2NhdG9yPSIzODI6NjAtMzgyOjcwIiBwYXRoPSJ2YWx1ZSIgeHNpOnR5cGU9IlByb3BlcnR5Ij4KICAgICAgICAgICAgPHNvdXJjZSBsb2NhdG9yPSIzODI6NjAtMzgyOjY0IiBuYW1lPSJ2YWx1ZSIgeHNpOnR5cGU9Ik9wZXJhbmRSZWYiLz4KICAgICAgICAgPC9leHByZXNzaW9uPgogICAgICAgICA8b3BlcmFuZCBuYW1lPSJ2YWx1ZSI+CiAgICAgICAgICAgIDxvcGVyYW5kVHlwZVNwZWNpZmllciBsb2NhdG9yPSIzODI6MzItMzgyOjU2IiBuYW1lPSJmaGlyOlRlc3RSZXBvcnRQYXJ0aWNpcGFudFR5cGUiIHhzaTp0eXBlPSJOYW1lZFR5cGVTcGVjaWZpZXIiLz4KICAgICAgICAgPC9vcGVyYW5kPgogICAgICA8L2RlZj4KICAgICAgPGRlZiBsb2NhdG9yPSIzODM6MS0zODM6NjEiIG5hbWU9IlRvU3RyaW5nIiBjb250ZXh0PSJQYXRpZW50IiBhY2Nlc3NMZXZlbD0iUHVibGljIiB4c2k6dHlwZT0iRnVuY3Rpb25EZWYiPgogICAgICAgICA8ZXhwcmVzc2lvbiBsb2NhdG9yPSIzODM6NTEtMzgzOjYxIiBwYXRoPSJ2YWx1ZSIgeHNpOnR5cGU9IlByb3BlcnR5Ij4KICAgICAgICAgICAgPHNvdXJjZSBsb2NhdG9yPSIzODM6NTEtMzgzOjU1IiBuYW1lPSJ2YWx1ZSIgeHNpOnR5cGU9Ik9wZXJhbmRSZWYiLz4KICAgICAgICAgPC9leHByZXNzaW9uPgogICAgICAgICA8b3BlcmFuZCBuYW1lPSJ2YWx1ZSI+CiAgICAgICAgICAgIDxvcGVyYW5kVHlwZVNwZWNpZmllciBsb2NhdG9yPSIzODM6MzItMzgzOjQ3IiBuYW1lPSJmaGlyOlRlc3RSZXBvcnRSZXN1bHQiIHhzaTp0eXBlPSJOYW1lZFR5cGVTcGVjaWZpZXIiLz4KICAgICAgICAgPC9vcGVyYW5kPgogICAgICA8L2RlZj4KICAgICAgPGRlZiBsb2NhdG9yPSIzODQ6MS0zODQ6NjEiIG5hbWU9IlRvU3RyaW5nIiBjb250ZXh0PSJQYXRpZW50IiBhY2Nlc3NMZXZlbD0iUHVibGljIiB4c2k6dHlwZT0iRnVuY3Rpb25EZWYiPgogICAgICAgICA8ZXhwcmVzc2lvbiBsb2NhdG9yPSIzODQ6NTEtMzg0OjYxIiBwYXRoPSJ2YWx1ZSIgeHNpOnR5cGU9IlByb3BlcnR5Ij4KICAgICAgICAgICAgPHNvdXJjZSBsb2NhdG9yPSIzODQ6NTEtMzg0OjU1IiBuYW1lPSJ2YWx1ZSIgeHNpOnR5cGU9Ik9wZXJhbmRSZWYiLz4KICAgICAgICAgPC9leHByZXNzaW9uPgogICAgICAgICA8b3BlcmFuZCBuYW1lPSJ2YWx1ZSI+CiAgICAgICAgICAgIDxvcGVyYW5kVHlwZVNwZWNpZmllciBsb2NhdG9yPSIzODQ6MzItMzg0OjQ3IiBuYW1lPSJmaGlyOlRlc3RSZXBvcnRTdGF0dXMiIHhzaTp0eXBlPSJOYW1lZFR5cGVTcGVjaWZpZXIiLz4KICAgICAgICAgPC9vcGVyYW5kPgogICAgICA8L2RlZj4KICAgICAgPGRlZiBsb2NhdG9yPSIzODU6MS0zODU6NzIiIG5hbWU9IlRvU3RyaW5nIiBjb250ZXh0PSJQYXRpZW50IiBhY2Nlc3NMZXZlbD0iUHVibGljIiB4c2k6dHlwZT0iRnVuY3Rpb25EZWYiPgogICAgICAgICA8ZXhwcmVzc2lvbiBsb2NhdG9yPSIzODU6NjItMzg1OjcyIiBwYXRoPSJ2YWx1ZSIgeHNpOnR5cGU9IlByb3BlcnR5Ij4KICAgICAgICAgICAgPHNvdXJjZSBsb2NhdG9yPSIzODU6NjItMzg1OjY2IiBuYW1lPSJ2YWx1ZSIgeHNpOnR5cGU9Ik9wZXJhbmRSZWYiLz4KICAgICAgICAgPC9leHByZXNzaW9uPgogICAgICAgICA8b3BlcmFuZCBuYW1lPSJ2YWx1ZSI+CiAgICAgICAgICAgIDxvcGVyYW5kVHlwZVNwZWNpZmllciBsb2NhdG9yPSIzODU6MzItMzg1OjU4IiBuYW1lPSJmaGlyOlRlc3RTY3JpcHRSZXF1ZXN0TWV0aG9kQ29kZSIgeHNpOnR5cGU9Ik5hbWVkVHlwZVNwZWNpZmllciIvPgogICAgICAgICA8L29wZXJhbmQ+CiAgICAgIDwvZGVmPgogICAgICA8ZGVmIGxvY2F0b3I9IjM4NjoxLTM4Njo1NiIgbmFtZT0iVG9TdHJpbmciIGNvbnRleHQ9IlBhdGllbnQiIGFjY2Vzc0xldmVsPSJQdWJsaWMiIHhzaTp0eXBlPSJGdW5jdGlvbkRlZiI+CiAgICAgICAgIDxleHByZXNzaW9uIGxvY2F0b3I9IjM4Njo0Ni0zODY6NTYiIHBhdGg9InZhbHVlIiB4c2k6dHlwZT0iUHJvcGVydHkiPgogICAgICAgICAgICA8c291cmNlIGxvY2F0b3I9IjM4Njo0Ni0zODY6NTAiIG5hbWU9InZhbHVlIiB4c2k6dHlwZT0iT3BlcmFuZFJlZiIvPgogICAgICAgICA8L2V4cHJlc3Npb24+CiAgICAgICAgIDxvcGVyYW5kIG5hbWU9InZhbHVlIj4KICAgICAgICAgICAgPG9wZXJhbmRUeXBlU3BlY2lmaWVyIGxvY2F0b3I9IjM4NjozMi0zODY6NDIiIG5hbWU9ImZoaXI6VHJpZ2dlclR5cGUiIHhzaTp0eXBlPSJOYW1lZFR5cGVTcGVjaWZpZXIiLz4KICAgICAgICAgPC9vcGVyYW5kPgogICAgICA8L2RlZj4KICAgICAgPGRlZiBsb2NhdG9yPSIzODc6MS0zODc6NjMiIG5hbWU9IlRvU3RyaW5nIiBjb250ZXh0PSJQYXRpZW50IiBhY2Nlc3NMZXZlbD0iUHVibGljIiB4c2k6dHlwZT0iRnVuY3Rpb25EZWYiPgogICAgICAgICA8ZXhwcmVzc2lvbiBsb2NhdG9yPSIzODc6NTMtMzg3OjYzIiBwYXRoPSJ2YWx1ZSIgeHNpOnR5cGU9IlByb3BlcnR5Ij4KICAgICAgICAgICAgPHNvdXJjZSBsb2NhdG9yPSIzODc6NTMtMzg3OjU3IiBuYW1lPSJ2YWx1ZSIgeHNpOnR5cGU9Ik9wZXJhbmRSZWYiLz4KICAgICAgICAgPC9leHByZXNzaW9uPgogICAgICAgICA8b3BlcmFuZCBuYW1lPSJ2YWx1ZSI+CiAgICAgICAgICAgIDxvcGVyYW5kVHlwZVNwZWNpZmllciBsb2NhdG9yPSIzODc6MzItMzg3OjQ5IiBuYW1lPSJmaGlyOlR5cGVEZXJpdmF0aW9uUnVsZSIgeHNpOnR5cGU9Ik5hbWVkVHlwZVNwZWNpZmllciIvPgogICAgICAgICA8L29wZXJhbmQ+CiAgICAgIDwvZGVmPgogICAgICA8ZGVmIGxvY2F0b3I9IjM4ODoxLTM4ODo2NyIgbmFtZT0iVG9TdHJpbmciIGNvbnRleHQ9IlBhdGllbnQiIGFjY2Vzc0xldmVsPSJQdWJsaWMiIHhzaTp0eXBlPSJGdW5jdGlvbkRlZiI+CiAgICAgICAgIDxleHByZXNzaW9uIGxvY2F0b3I9IjM4ODo1Ny0zODg6NjciIHBhdGg9InZhbHVlIiB4c2k6dHlwZT0iUHJvcGVydHkiPgogICAgICAgICAgICA8c291cmNlIGxvY2F0b3I9IjM4ODo1Ny0zODg6NjEiIG5hbWU9InZhbHVlIiB4c2k6dHlwZT0iT3BlcmFuZFJlZiIvPgogICAgICAgICA8L2V4cHJlc3Npb24+CiAgICAgICAgIDxvcGVyYW5kIG5hbWU9InZhbHVlIj4KICAgICAgICAgICAgPG9wZXJhbmRUeXBlU3BlY2lmaWVyIGxvY2F0b3I9IjM4ODozMi0zODg6NTMiIG5hbWU9ImZoaXI6VHlwZVJlc3RmdWxJbnRlcmFjdGlvbiIgeHNpOnR5cGU9Ik5hbWVkVHlwZVNwZWNpZmllciIvPgogICAgICAgICA8L29wZXJhbmQ+CiAgICAgIDwvZGVmPgogICAgICA8ZGVmIGxvY2F0b3I9IjM4OToxLTM4OTo1NyIgbmFtZT0iVG9TdHJpbmciIGNvbnRleHQ9IlBhdGllbnQiIGFjY2Vzc0xldmVsPSJQdWJsaWMiIHhzaTp0eXBlPSJGdW5jdGlvbkRlZiI+CiAgICAgICAgIDxleHByZXNzaW9uIGxvY2F0b3I9IjM4OTo0Ny0zODk6NTciIHBhdGg9InZhbHVlIiB4c2k6dHlwZT0iUHJvcGVydHkiPgogICAgICAgICAgICA8c291cmNlIGxvY2F0b3I9IjM4OTo0Ny0zODk6NTEiIG5hbWU9InZhbHVlIiB4c2k6dHlwZT0iT3BlcmFuZFJlZiIvPgogICAgICAgICA8L2V4cHJlc3Npb24+CiAgICAgICAgIDxvcGVyYW5kIG5hbWU9InZhbHVlIj4KICAgICAgICAgICAgPG9wZXJhbmRUeXBlU3BlY2lmaWVyIGxvY2F0b3I9IjM4OTozMi0zODk6NDMiIG5hbWU9ImZoaXI6VURJRW50cnlUeXBlIiB4c2k6dHlwZT0iTmFtZWRUeXBlU3BlY2lmaWVyIi8+CiAgICAgICAgIDwvb3BlcmFuZD4KICAgICAgPC9kZWY+CiAgICAgIDxkZWYgbG9jYXRvcj0iMzkwOjEtMzkwOjU2IiBuYW1lPSJUb1N0cmluZyIgY29udGV4dD0iUGF0aWVudCIgYWNjZXNzTGV2ZWw9IlB1YmxpYyIgeHNpOnR5cGU9IkZ1bmN0aW9uRGVmIj4KICAgICAgICAgPGV4cHJlc3Npb24gbG9jYXRvcj0iMzkwOjQ2LTM5MDo1NiIgcGF0aD0idmFsdWUiIHhzaTp0eXBlPSJQcm9wZXJ0eSI+CiAgICAgICAgICAgIDxzb3VyY2UgbG9jYXRvcj0iMzkwOjQ2LTM5MDo1MCIgbmFtZT0idmFsdWUiIHhzaTp0eXBlPSJPcGVyYW5kUmVmIi8+CiAgICAgICAgIDwvZXhwcmVzc2lvbj4KICAgICAgICAgPG9wZXJhbmQgbmFtZT0idmFsdWUiPgogICAgICAgICAgICA8b3BlcmFuZFR5cGVTcGVjaWZpZXIgbG9jYXRvcj0iMzkwOjMyLTM5MDo0MiIgbmFtZT0iZmhpcjpVbml0c09mVGltZSIgeHNpOnR5cGU9Ik5hbWVkVHlwZVNwZWNpZmllciIvPgogICAgICAgICA8L29wZXJhbmQ+CiAgICAgIDwvZGVmPgogICAgICA8ZGVmIGxvY2F0b3I9IjM5MToxLTM5MTo0OCIgbmFtZT0iVG9TdHJpbmciIGNvbnRleHQ9IlBhdGllbnQiIGFjY2Vzc0xldmVsPSJQdWJsaWMiIHhzaTp0eXBlPSJGdW5jdGlvbkRlZiI+CiAgICAgICAgIDxleHByZXNzaW9uIGxvY2F0b3I9IjM5MTozOC0zOTE6NDgiIHBhdGg9InZhbHVlIiB4c2k6dHlwZT0iUHJvcGVydHkiPgogICAgICAgICAgICA8c291cmNlIGxvY2F0b3I9IjM5MTozOC0zOTE6NDIiIG5hbWU9InZhbHVlIiB4c2k6dHlwZT0iT3BlcmFuZFJlZiIvPgogICAgICAgICA8L2V4cHJlc3Npb24+CiAgICAgICAgIDxvcGVyYW5kIG5hbWU9InZhbHVlIj4KICAgICAgICAgICAgPG9wZXJhbmRUeXBlU3BlY2lmaWVyIGxvY2F0b3I9IjM5MTozMi0zOTE6MzQiIG5hbWU9ImZoaXI6VXNlIiB4c2k6dHlwZT0iTmFtZWRUeXBlU3BlY2lmaWVyIi8+CiAgICAgICAgIDwvb3BlcmFuZD4KICAgICAgPC9kZWY+CiAgICAgIDxkZWYgbG9jYXRvcj0iMzkyOjEtMzkyOjU3IiBuYW1lPSJUb1N0cmluZyIgY29udGV4dD0iUGF0aWVudCIgYWNjZXNzTGV2ZWw9IlB1YmxpYyIgeHNpOnR5cGU9IkZ1bmN0aW9uRGVmIj4KICAgICAgICAgPGV4cHJlc3Npb24gbG9jYXRvcj0iMzkyOjQ3LTM5Mjo1NyIgcGF0aD0idmFsdWUiIHhzaTp0eXBlPSJQcm9wZXJ0eSI+CiAgICAgICAgICAgIDxzb3VyY2UgbG9jYXRvcj0iMzkyOjQ3LTM5Mjo1MSIgbmFtZT0idmFsdWUiIHhzaTp0eXBlPSJPcGVyYW5kUmVmIi8+CiAgICAgICAgIDwvZXhwcmVzc2lvbj4KICAgICAgICAgPG9wZXJhbmQgbmFtZT0idmFsdWUiPgogICAgICAgICAgICA8b3BlcmFuZFR5cGVTcGVjaWZpZXIgbG9jYXRvcj0iMzkyOjMyLTM5Mjo0MyIgbmFtZT0iZmhpcjpWYXJpYWJsZVR5cGUiIHhzaTp0eXBlPSJOYW1lZFR5cGVTcGVjaWZpZXIiLz4KICAgICAgICAgPC9vcGVyYW5kPgogICAgICA8L2RlZj4KICAgICAgPGRlZiBsb2NhdG9yPSIzOTM6MS0zOTM6NTUiIG5hbWU9IlRvU3RyaW5nIiBjb250ZXh0PSJQYXRpZW50IiBhY2Nlc3NMZXZlbD0iUHVibGljIiB4c2k6dHlwZT0iRnVuY3Rpb25EZWYiPgogICAgICAgICA8ZXhwcmVzc2lvbiBsb2NhdG9yPSIzOTM6NDUtMzkzOjU1IiBwYXRoPSJ2YWx1ZSIgeHNpOnR5cGU9IlByb3BlcnR5Ij4KICAgICAgICAgICAgPHNvdXJjZSBsb2NhdG9yPSIzOTM6NDUtMzkzOjQ5IiBuYW1lPSJ2YWx1ZSIgeHNpOnR5cGU9Ik9wZXJhbmRSZWYiLz4KICAgICAgICAgPC9leHByZXNzaW9uPgogICAgICAgICA8b3BlcmFuZCBuYW1lPSJ2YWx1ZSI+CiAgICAgICAgICAgIDxvcGVyYW5kVHlwZVNwZWNpZmllciBsb2NhdG9yPSIzOTM6MzItMzkzOjQxIiBuYW1lPSJmaGlyOlZpc2lvbkJhc2UiIHhzaTp0eXBlPSJOYW1lZFR5cGVTcGVjaWZpZXIiLz4KICAgICAgICAgPC9vcGVyYW5kPgogICAgICA8L2RlZj4KICAgICAgPGRlZiBsb2NhdG9yPSIzOTQ6MS0zOTQ6NTUiIG5hbWU9IlRvU3RyaW5nIiBjb250ZXh0PSJQYXRpZW50IiBhY2Nlc3NMZXZlbD0iUHVibGljIiB4c2k6dHlwZT0iRnVuY3Rpb25EZWYiPgogICAgICAgICA8ZXhwcmVzc2lvbiBsb2NhdG9yPSIzOTQ6NDUtMzk0OjU1IiBwYXRoPSJ2YWx1ZSIgeHNpOnR5cGU9IlByb3BlcnR5Ij4KICAgICAgICAgICAgPHNvdXJjZSBsb2NhdG9yPSIzOTQ6NDUtMzk0OjQ5IiBuYW1lPSJ2YWx1ZSIgeHNpOnR5cGU9Ik9wZXJhbmRSZWYiLz4KICAgICAgICAgPC9leHByZXNzaW9uPgogICAgICAgICA8b3BlcmFuZCBuYW1lPSJ2YWx1ZSI+CiAgICAgICAgICAgIDxvcGVyYW5kVHlwZVNwZWNpZmllciBsb2NhdG9yPSIzOTQ6MzItMzk0OjQxIiBuYW1lPSJmaGlyOlZpc2lvbkV5ZXMiIHhzaTp0eXBlPSJOYW1lZFR5cGVTcGVjaWZpZXIiLz4KICAgICAgICAgPC9vcGVyYW5kPgogICAgICA8L2RlZj4KICAgICAgPGRlZiBsb2NhdG9yPSIzOTU6MS0zOTU6NTciIG5hbWU9IlRvU3RyaW5nIiBjb250ZXh0PSJQYXRpZW50IiBhY2Nlc3NMZXZlbD0iUHVibGljIiB4c2k6dHlwZT0iRnVuY3Rpb25EZWYiPgogICAgICAgICA8ZXhwcmVzc2lvbiBsb2NhdG9yPSIzOTU6NDctMzk1OjU3IiBwYXRoPSJ2YWx1ZSIgeHNpOnR5cGU9IlByb3BlcnR5Ij4KICAgICAgICAgICAgPHNvdXJjZSBsb2NhdG9yPSIzOTU6NDctMzk1OjUxIiBuYW1lPSJ2YWx1ZSIgeHNpOnR5cGU9Ik9wZXJhbmRSZWYiLz4KICAgICAgICAgPC9leHByZXNzaW9uPgogICAgICAgICA8b3BlcmFuZCBuYW1lPSJ2YWx1ZSI+CiAgICAgICAgICAgIDxvcGVyYW5kVHlwZVNwZWNpZmllciBsb2NhdG9yPSIzOTU6MzItMzk1OjQzIiBuYW1lPSJmaGlyOlZpc2lvblN0YXR1cyIgeHNpOnR5cGU9Ik5hbWVkVHlwZVNwZWNpZmllciIvPgogICAgICAgICA8L29wZXJhbmQ+CiAgICAgIDwvZGVmPgogICAgICA8ZGVmIGxvY2F0b3I9IjM5NjoxLTM5Njo1OSIgbmFtZT0iVG9TdHJpbmciIGNvbnRleHQ9IlBhdGllbnQiIGFjY2Vzc0xldmVsPSJQdWJsaWMiIHhzaTp0eXBlPSJGdW5jdGlvbkRlZiI+CiAgICAgICAgIDxleHByZXNzaW9uIGxvY2F0b3I9IjM5Njo0OS0zOTY6NTkiIHBhdGg9InZhbHVlIiB4c2k6dHlwZT0iUHJvcGVydHkiPgogICAgICAgICAgICA8c291cmNlIGxvY2F0b3I9IjM5Njo0OS0zOTY6NTMiIG5hbWU9InZhbHVlIiB4c2k6dHlwZT0iT3BlcmFuZFJlZiIvPgogICAgICAgICA8L2V4cHJlc3Npb24+CiAgICAgICAgIDxvcGVyYW5kIG5hbWU9InZhbHVlIj4KICAgICAgICAgICAgPG9wZXJhbmRUeXBlU3BlY2lmaWVyIGxvY2F0b3I9IjM5NjozMi0zOTY6NDUiIG5hbWU9ImZoaXI6WFBhdGhVc2FnZVR5cGUiIHhzaTp0eXBlPSJOYW1lZFR5cGVTcGVjaWZpZXIiLz4KICAgICAgICAgPC9vcGVyYW5kPgogICAgICA8L2RlZj4KICAgICAgPGRlZiBsb2NhdG9yPSIzOTc6MS0zOTc6NTciIG5hbWU9IlRvU3RyaW5nIiBjb250ZXh0PSJQYXRpZW50IiBhY2Nlc3NMZXZlbD0iUHVibGljIiB4c2k6dHlwZT0iRnVuY3Rpb25EZWYiPgogICAgICAgICA8ZXhwcmVzc2lvbiBsb2NhdG9yPSIzOTc6NDctMzk3OjU3IiBwYXRoPSJ2YWx1ZSIgeHNpOnR5cGU9IlByb3BlcnR5Ij4KICAgICAgICAgICAgPHNvdXJjZSBsb2NhdG9yPSIzOTc6NDctMzk3OjUxIiBuYW1lPSJ2YWx1ZSIgeHNpOnR5cGU9Ik9wZXJhbmRSZWYiLz4KICAgICAgICAgPC9leHByZXNzaW9uPgogICAgICAgICA8b3BlcmFuZCBuYW1lPSJ2YWx1ZSI+CiAgICAgICAgICAgIDxvcGVyYW5kVHlwZVNwZWNpZmllciBsb2NhdG9yPSIzOTc6MzItMzk3OjQzIiBuYW1lPSJmaGlyOmJhc2U2NEJpbmFyeSIgeHNpOnR5cGU9Ik5hbWVkVHlwZVNwZWNpZmllciIvPgogICAgICAgICA8L29wZXJhbmQ+CiAgICAgIDwvZGVmPgogICAgICA8ZGVmIGxvY2F0b3I9IjM5ODoxLTM5ODo1MyIgbmFtZT0iVG9Cb29sZWFuIiBjb250ZXh0PSJQYXRpZW50IiBhY2Nlc3NMZXZlbD0iUHVibGljIiB4c2k6dHlwZT0iRnVuY3Rpb25EZWYiPgogICAgICAgICA8ZXhwcmVzc2lvbiBsb2NhdG9yPSIzOTg6NDMtMzk4OjUzIiBwYXRoPSJ2YWx1ZSIgeHNpOnR5cGU9IlByb3BlcnR5Ij4KICAgICAgICAgICAgPHNvdXJjZSBsb2NhdG9yPSIzOTg6NDMtMzk4OjQ3IiBuYW1lPSJ2YWx1ZSIgeHNpOnR5cGU9Ik9wZXJhbmRSZWYiLz4KICAgICAgICAgPC9leHByZXNzaW9uPgogICAgICAgICA8b3BlcmFuZCBuYW1lPSJ2YWx1ZSI+CiAgICAgICAgICAgIDxvcGVyYW5kVHlwZVNwZWNpZmllciBsb2NhdG9yPSIzOTg6MzMtMzk4OjM5IiBuYW1lPSJmaGlyOmJvb2xlYW4iIHhzaTp0eXBlPSJOYW1lZFR5cGVTcGVjaWZpZXIiLz4KICAgICAgICAgPC9vcGVyYW5kPgogICAgICA8L2RlZj4KICAgICAgPGRlZiBsb2NhdG9yPSIzOTk6MS0zOTk6NDciIG5hbWU9IlRvRGF0ZSIgY29udGV4dD0iUGF0aWVudCIgYWNjZXNzTGV2ZWw9IlB1YmxpYyIgeHNpOnR5cGU9IkZ1bmN0aW9uRGVmIj4KICAgICAgICAgPGV4cHJlc3Npb24gbG9jYXRvcj0iMzk5OjM3LTM5OTo0NyIgcGF0aD0idmFsdWUiIHhzaTp0eXBlPSJQcm9wZXJ0eSI+CiAgICAgICAgICAgIDxzb3VyY2UgbG9jYXRvcj0iMzk5OjM3LTM5OTo0MSIgbmFtZT0idmFsdWUiIHhzaTp0eXBlPSJPcGVyYW5kUmVmIi8+CiAgICAgICAgIDwvZXhwcmVzc2lvbj4KICAgICAgICAgPG9wZXJhbmQgbmFtZT0idmFsdWUiPgogICAgICAgICAgICA8b3BlcmFuZFR5cGVTcGVjaWZpZXIgbG9jYXRvcj0iMzk5OjMwLTM5OTozMyIgbmFtZT0iZmhpcjpkYXRlIiB4c2k6dHlwZT0iTmFtZWRUeXBlU3BlY2lmaWVyIi8+CiAgICAgICAgIDwvb3BlcmFuZD4KICAgICAgPC9kZWY+CiAgICAgIDxkZWYgbG9jYXRvcj0iNDAwOjEtNDAwOjU1IiBuYW1lPSJUb0RhdGVUaW1lIiBjb250ZXh0PSJQYXRpZW50IiBhY2Nlc3NMZXZlbD0iUHVibGljIiB4c2k6dHlwZT0iRnVuY3Rpb25EZWYiPgogICAgICAgICA8ZXhwcmVzc2lvbiBsb2NhdG9yPSI0MDA6NDUtNDAwOjU1IiBwYXRoPSJ2YWx1ZSIgeHNpOnR5cGU9IlByb3BlcnR5Ij4KICAgICAgICAgICAgPHNvdXJjZSBsb2NhdG9yPSI0MDA6NDUtNDAwOjQ5IiBuYW1lPSJ2YWx1ZSIgeHNpOnR5cGU9Ik9wZXJhbmRSZWYiLz4KICAgICAgICAgPC9leHByZXNzaW9uPgogICAgICAgICA8b3BlcmFuZCBuYW1lPSJ2YWx1ZSI+CiAgICAgICAgICAgIDxvcGVyYW5kVHlwZVNwZWNpZmllciBsb2NhdG9yPSI0MDA6MzQtNDAwOjQxIiBuYW1lPSJmaGlyOmRhdGVUaW1lIiB4c2k6dHlwZT0iTmFtZWRUeXBlU3BlY2lmaWVyIi8+CiAgICAgICAgIDwvb3BlcmFuZD4KICAgICAgPC9kZWY+CiAgICAgIDxkZWYgbG9jYXRvcj0iNDAxOjEtNDAxOjUzIiBuYW1lPSJUb0RlY2ltYWwiIGNvbnRleHQ9IlBhdGllbnQiIGFjY2Vzc0xldmVsPSJQdWJsaWMiIHhzaTp0eXBlPSJGdW5jdGlvbkRlZiI+CiAgICAgICAgIDxleHByZXNzaW9uIGxvY2F0b3I9IjQwMTo0My00MDE6NTMiIHBhdGg9InZhbHVlIiB4c2k6dHlwZT0iUHJvcGVydHkiPgogICAgICAgICAgICA8c291cmNlIGxvY2F0b3I9IjQwMTo0My00MDE6NDciIG5hbWU9InZhbHVlIiB4c2k6dHlwZT0iT3BlcmFuZFJlZiIvPgogICAgICAgICA8L2V4cHJlc3Npb24+CiAgICAgICAgIDxvcGVyYW5kIG5hbWU9InZhbHVlIj4KICAgICAgICAgICAgPG9wZXJhbmRUeXBlU3BlY2lmaWVyIGxvY2F0b3I9IjQwMTozMy00MDE6MzkiIG5hbWU9ImZoaXI6ZGVjaW1hbCIgeHNpOnR5cGU9Ik5hbWVkVHlwZVNwZWNpZmllciIvPgogICAgICAgICA8L29wZXJhbmQ+CiAgICAgIDwvZGVmPgogICAgICA8ZGVmIGxvY2F0b3I9IjQwMjoxLTQwMjo1NCIgbmFtZT0iVG9EYXRlVGltZSIgY29udGV4dD0iUGF0aWVudCIgYWNjZXNzTGV2ZWw9IlB1YmxpYyIgeHNpOnR5cGU9IkZ1bmN0aW9uRGVmIj4KICAgICAgICAgPGV4cHJlc3Npb24gbG9jYXRvcj0iNDAyOjQ0LTQwMjo1NCIgcGF0aD0idmFsdWUiIHhzaTp0eXBlPSJQcm9wZXJ0eSI+CiAgICAgICAgICAgIDxzb3VyY2UgbG9jYXRvcj0iNDAyOjQ0LTQwMjo0OCIgbmFtZT0idmFsdWUiIHhzaTp0eXBlPSJPcGVyYW5kUmVmIi8+CiAgICAgICAgIDwvZXhwcmVzc2lvbj4KICAgICAgICAgPG9wZXJhbmQgbmFtZT0idmFsdWUiPgogICAgICAgICAgICA8b3BlcmFuZFR5cGVTcGVjaWZpZXIgbG9jYXRvcj0iNDAyOjM0LTQwMjo0MCIgbmFtZT0iZmhpcjppbnN0YW50IiB4c2k6dHlwZT0iTmFtZWRUeXBlU3BlY2lmaWVyIi8+CiAgICAgICAgIDwvb3BlcmFuZD4KICAgICAgPC9kZWY+CiAgICAgIDxkZWYgbG9jYXRvcj0iNDAzOjEtNDAzOjUzIiBuYW1lPSJUb0ludGVnZXIiIGNvbnRleHQ9IlBhdGllbnQiIGFjY2Vzc0xldmVsPSJQdWJsaWMiIHhzaTp0eXBlPSJGdW5jdGlvbkRlZiI+CiAgICAgICAgIDxleHByZXNzaW9uIGxvY2F0b3I9IjQwMzo0My00MDM6NTMiIHBhdGg9InZhbHVlIiB4c2k6dHlwZT0iUHJvcGVydHkiPgogICAgICAgICAgICA8c291cmNlIGxvY2F0b3I9IjQwMzo0My00MDM6NDciIG5hbWU9InZhbHVlIiB4c2k6dHlwZT0iT3BlcmFuZFJlZiIvPgogICAgICAgICA8L2V4cHJlc3Npb24+CiAgICAgICAgIDxvcGVyYW5kIG5hbWU9InZhbHVlIj4KICAgICAgICAgICAgPG9wZXJhbmRUeXBlU3BlY2lmaWVyIGxvY2F0b3I9IjQwMzozMy00MDM6MzkiIG5hbWU9ImZoaXI6aW50ZWdlciIgeHNpOnR5cGU9Ik5hbWVkVHlwZVNwZWNpZmllciIvPgogICAgICAgICA8L29wZXJhbmQ+CiAgICAgIDwvZGVmPgogICAgICA8ZGVmIGxvY2F0b3I9IjQwNDoxLTQwNDo1MSIgbmFtZT0iVG9TdHJpbmciIGNvbnRleHQ9IlBhdGllbnQiIGFjY2Vzc0xldmVsPSJQdWJsaWMiIHhzaTp0eXBlPSJGdW5jdGlvbkRlZiI+CiAgICAgICAgIDxleHByZXNzaW9uIGxvY2F0b3I9IjQwNDo0MS00MDQ6NTEiIHBhdGg9InZhbHVlIiB4c2k6dHlwZT0iUHJvcGVydHkiPgogICAgICAgICAgICA8c291cmNlIGxvY2F0b3I9IjQwNDo0MS00MDQ6NDUiIG5hbWU9InZhbHVlIiB4c2k6dHlwZT0iT3BlcmFuZFJlZiIvPgogICAgICAgICA8L2V4cHJlc3Npb24+CiAgICAgICAgIDxvcGVyYW5kIG5hbWU9InZhbHVlIj4KICAgICAgICAgICAgPG9wZXJhbmRUeXBlU3BlY2lmaWVyIGxvY2F0b3I9IjQwNDozMi00MDQ6MzciIG5hbWU9ImZoaXI6c3RyaW5nIiB4c2k6dHlwZT0iTmFtZWRUeXBlU3BlY2lmaWVyIi8+CiAgICAgICAgIDwvb3BlcmFuZD4KICAgICAgPC9kZWY+CiAgICAgIDxkZWYgbG9jYXRvcj0iNDA1OjEtNDA1OjQ3IiBuYW1lPSJUb1RpbWUiIGNvbnRleHQ9IlBhdGllbnQiIGFjY2Vzc0xldmVsPSJQdWJsaWMiIHhzaTp0eXBlPSJGdW5jdGlvbkRlZiI+CiAgICAgICAgIDxleHByZXNzaW9uIGxvY2F0b3I9IjQwNTozNy00MDU6NDciIHBhdGg9InZhbHVlIiB4c2k6dHlwZT0iUHJvcGVydHkiPgogICAgICAgICAgICA8c291cmNlIGxvY2F0b3I9IjQwNTozNy00MDU6NDEiIG5hbWU9InZhbHVlIiB4c2k6dHlwZT0iT3BlcmFuZFJlZiIvPgogICAgICAgICA8L2V4cHJlc3Npb24+CiAgICAgICAgIDxvcGVyYW5kIG5hbWU9InZhbHVlIj4KICAgICAgICAgICAgPG9wZXJhbmRUeXBlU3BlY2lmaWVyIGxvY2F0b3I9IjQwNTozMC00MDU6MzMiIG5hbWU9ImZoaXI6dGltZSIgeHNpOnR5cGU9Ik5hbWVkVHlwZVNwZWNpZmllciIvPgogICAgICAgICA8L29wZXJhbmQ+CiAgICAgIDwvZGVmPgogICAgICA8ZGVmIGxvY2F0b3I9IjQwNjoxLTQwNjo0OCIgbmFtZT0iVG9TdHJpbmciIGNvbnRleHQ9IlBhdGllbnQiIGFjY2Vzc0xldmVsPSJQdWJsaWMiIHhzaTp0eXBlPSJGdW5jdGlvbkRlZiI+CiAgICAgICAgIDxleHByZXNzaW9uIGxvY2F0b3I9IjQwNjozOC00MDY6NDgiIHBhdGg9InZhbHVlIiB4c2k6dHlwZT0iUHJvcGVydHkiPgogICAgICAgICAgICA8c291cmNlIGxvY2F0b3I9IjQwNjozOC00MDY6NDIiIG5hbWU9InZhbHVlIiB4c2k6dHlwZT0iT3BlcmFuZFJlZiIvPgogICAgICAgICA8L2V4cHJlc3Npb24+CiAgICAgICAgIDxvcGVyYW5kIG5hbWU9InZhbHVlIj4KICAgICAgICAgICAgPG9wZXJhbmRUeXBlU3BlY2lmaWVyIGxvY2F0b3I9IjQwNjozMi00MDY6MzQiIG5hbWU9ImZoaXI6dXJpIiB4c2k6dHlwZT0iTmFtZWRUeXBlU3BlY2lmaWVyIi8+CiAgICAgICAgIDwvb3BlcmFuZD4KICAgICAgPC9kZWY+CiAgICAgIDxkZWYgbG9jYXRvcj0iNDA3OjEtNDA3OjUwIiBuYW1lPSJUb1N0cmluZyIgY29udGV4dD0iUGF0aWVudCIgYWNjZXNzTGV2ZWw9IlB1YmxpYyIgeHNpOnR5cGU9IkZ1bmN0aW9uRGVmIj4KICAgICAgICAgPGV4cHJlc3Npb24gbG9jYXRvcj0iNDA3OjQwLTQwNzo1MCIgcGF0aD0idmFsdWUiIHhzaTp0eXBlPSJQcm9wZXJ0eSI+CiAgICAgICAgICAgIDxzb3VyY2UgbG9jYXRvcj0iNDA3OjQwLTQwNzo0NCIgbmFtZT0idmFsdWUiIHhzaTp0eXBlPSJPcGVyYW5kUmVmIi8+CiAgICAgICAgIDwvZXhwcmVzc2lvbj4KICAgICAgICAgPG9wZXJhbmQgbmFtZT0idmFsdWUiPgogICAgICAgICAgICA8b3BlcmFuZFR5cGVTcGVjaWZpZXIgbG9jYXRvcj0iNDA3OjMyLTQwNzozNiIgbmFtZT0iZmhpcjp4aHRtbCIgeHNpOnR5cGU9Ik5hbWVkVHlwZVNwZWNpZmllciIvPgogICAgICAgICA8L29wZXJhbmQ+CiAgICAgIDwvZGVmPgogICA8L3N0YXRlbWVudHM+CjwvbGlicmFyeT4K" + }, + { + "contentType": "application/elm+json", + "data": "ewogICAibGlicmFyeSIgOiB7CiAgICAgICJhbm5vdGF0aW9uIiA6IFsgewogICAgICAgICAidHJhbnNsYXRvclZlcnNpb24iIDogIjIuNy4wIiwKICAgICAgICAgInRyYW5zbGF0b3JPcHRpb25zIiA6ICJFbmFibGVMb2NhdG9ycyxEaXNhYmxlTGlzdERlbW90aW9uLERpc2FibGVMaXN0UHJvbW90aW9uIiwKICAgICAgICAgInR5cGUiIDogIkNxbFRvRWxtSW5mbyIKICAgICAgfSwgewogICAgICAgICAidHlwZSIgOiAiQW5ub3RhdGlvbiIsCiAgICAgICAgICJ0IiA6IFsgewogICAgICAgICAgICAibmFtZSIgOiAiYXV0aG9yIiwKICAgICAgICAgICAgInZhbHVlIiA6ICJCcnluIFJob2RlcyIKICAgICAgICAgfSwgewogICAgICAgICAgICAibmFtZSIgOiAiZGVzY3JpcHRpb24iLAogICAgICAgICAgICAidmFsdWUiIDogIlRoaXMgbGlicmFyeSBkZWZpbmVzIGZ1bmN0aW9ucyB0byBjb252ZXJ0IGJldHdlZW4gRkhJUlxuIGRhdGEgdHlwZXMgYW5kIENRTCBzeXN0ZW0tZGVmaW5lZCB0eXBlcywgYXMgd2VsbCBhcyBmdW5jdGlvbnMgdG8gc3VwcG9ydFxuIEZISVJQYXRoIGltcGxlbWVudGF0aW9uLiBGb3IgbW9yZSBpbmZvcm1hdGlvbiwgc2VlIHRoZSBGSElSSGVscGVycyB3aWtpIHBhZ2U6XG4gaHR0cHM6Ly9naXRodWIuY29tL2NxZnJhbWV3b3JrL2NsaW5pY2FsX3F1YWxpdHlfbGFuZ3VhZ2Uvd2lraS9GSElSSGVscGVycyIKICAgICAgICAgfSwgewogICAgICAgICAgICAibmFtZSIgOiAiYWxsb3dGbHVlbnQiLAogICAgICAgICAgICAidmFsdWUiIDogInRydWUiCiAgICAgICAgIH0gXQogICAgICB9IF0sCiAgICAgICJpZGVudGlmaWVyIiA6IHsKICAgICAgICAgImlkIiA6ICJGSElSSGVscGVycyIsCiAgICAgICAgICJzeXN0ZW0iIDogImh0dHA6Ly9jb250ZW50LmFscGhvcmEuY29tL2ZoaXIvZHFtIiwKICAgICAgICAgInZlcnNpb24iIDogIjQuMC4xIgogICAgICB9LAogICAgICAic2NoZW1hSWRlbnRpZmllciIgOiB7CiAgICAgICAgICJpZCIgOiAidXJuOmhsNy1vcmc6ZWxtIiwKICAgICAgICAgInZlcnNpb24iIDogInIxIgogICAgICB9LAogICAgICAidXNpbmdzIiA6IHsKICAgICAgICAgImRlZiIgOiBbIHsKICAgICAgICAgICAgImxvY2FsSWRlbnRpZmllciIgOiAiU3lzdGVtIiwKICAgICAgICAgICAgInVyaSIgOiAidXJuOmhsNy1vcmc6ZWxtLXR5cGVzOnIxIgogICAgICAgICB9LCB7CiAgICAgICAgICAgICJsb2NhdG9yIiA6ICIxMToxLTExOjI2IiwKICAgICAgICAgICAgImxvY2FsSWRlbnRpZmllciIgOiAiRkhJUiIsCiAgICAgICAgICAgICJ1cmkiIDogImh0dHA6Ly9obDcub3JnL2ZoaXIiLAogICAgICAgICAgICAidmVyc2lvbiIgOiAiNC4wLjEiCiAgICAgICAgIH0gXQogICAgICB9LAogICAgICAic3RhdGVtZW50cyIgOiB7CiAgICAgICAgICJkZWYiIDogWyB7CiAgICAgICAgICAgICJsb2NhdG9yIiA6ICIxMzoxLTIwOjYyIiwKICAgICAgICAgICAgIm5hbWUiIDogIlRvSW50ZXJ2YWwiLAogICAgICAgICAgICAiY29udGV4dCIgOiAiUGF0aWVudCIsCiAgICAgICAgICAgICJhY2Nlc3NMZXZlbCIgOiAiUHVibGljIiwKICAgICAgICAgICAgInR5cGUiIDogIkZ1bmN0aW9uRGVmIiwKICAgICAgICAgICAgImV4cHJlc3Npb24iIDogewogICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMTQ6NS0yMDo2MiIsCiAgICAgICAgICAgICAgICJ0eXBlIiA6ICJJZiIsCiAgICAgICAgICAgICAgICJjb25kaXRpb24iIDogewogICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMTQ6OC0xNDoyMSIsCiAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJJc051bGwiLAogICAgICAgICAgICAgICAgICAib3BlcmFuZCIgOiB7CiAgICAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIxNDo4LTE0OjEzIiwKICAgICAgICAgICAgICAgICAgICAgIm5hbWUiIDogInBlcmlvZCIsCiAgICAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJPcGVyYW5kUmVmIgogICAgICAgICAgICAgICAgICB9CiAgICAgICAgICAgICAgIH0sCiAgICAgICAgICAgICAgICJ0aGVuIiA6IHsKICAgICAgICAgICAgICAgICAgInR5cGUiIDogIkFzIiwKICAgICAgICAgICAgICAgICAgIm9wZXJhbmQiIDogewogICAgICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMTU6OS0xNToxMiIsCiAgICAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJOdWxsIgogICAgICAgICAgICAgICAgICB9LAogICAgICAgICAgICAgICAgICAiYXNUeXBlU3BlY2lmaWVyIiA6IHsKICAgICAgICAgICAgICAgICAgICAgInR5cGUiIDogIkludGVydmFsVHlwZVNwZWNpZmllciIsCiAgICAgICAgICAgICAgICAgICAgICJwb2ludFR5cGUiIDogewogICAgICAgICAgICAgICAgICAgICAgICAibmFtZSIgOiAie3VybjpobDctb3JnOmVsbS10eXBlczpyMX1EYXRlVGltZSIsCiAgICAgICAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJOYW1lZFR5cGVTcGVjaWZpZXIiCiAgICAgICAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgICAgICAgfQogICAgICAgICAgICAgICB9LAogICAgICAgICAgICAgICAiZWxzZSIgOiB7CiAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIxNzo5LTIwOjYyIiwKICAgICAgICAgICAgICAgICAgInR5cGUiIDogIklmIiwKICAgICAgICAgICAgICAgICAgImNvbmRpdGlvbiIgOiB7CiAgICAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIxNzoxMi0xNzozMyIsCiAgICAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJJc051bGwiLAogICAgICAgICAgICAgICAgICAgICAib3BlcmFuZCIgOiB7CiAgICAgICAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIxNzoxMi0xNzoyNSIsCiAgICAgICAgICAgICAgICAgICAgICAgICJwYXRoIiA6ICJzdGFydCIsCiAgICAgICAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJQcm9wZXJ0eSIsCiAgICAgICAgICAgICAgICAgICAgICAgICJzb3VyY2UiIDogewogICAgICAgICAgICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMTc6MTItMTc6MTciLAogICAgICAgICAgICAgICAgICAgICAgICAgICAibmFtZSIgOiAicGVyaW9kIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk9wZXJhbmRSZWYiCiAgICAgICAgICAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgICAgICAgICAgfQogICAgICAgICAgICAgICAgICB9LAogICAgICAgICAgICAgICAgICAidGhlbiIgOiB7CiAgICAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIxODoxMy0xODo2MiIsCiAgICAgICAgICAgICAgICAgICAgICJsb3dDbG9zZWQiIDogZmFsc2UsCiAgICAgICAgICAgICAgICAgICAgICJoaWdoQ2xvc2VkIiA6IHRydWUsCiAgICAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJJbnRlcnZhbCIsCiAgICAgICAgICAgICAgICAgICAgICJsb3ciIDogewogICAgICAgICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMTg6MjItMTg6NDEiLAogICAgICAgICAgICAgICAgICAgICAgICAicGF0aCIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAgICAgICAgICAidHlwZSIgOiAiUHJvcGVydHkiLAogICAgICAgICAgICAgICAgICAgICAgICAic291cmNlIiA6IHsKICAgICAgICAgICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjE4OjIyLTE4OjM1IiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgInBhdGgiIDogInN0YXJ0IiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgInR5cGUiIDogIlByb3BlcnR5IiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgInNvdXJjZSIgOiB7CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIxODoyMi0xODoyNyIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJwZXJpb2QiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAidHlwZSIgOiAiT3BlcmFuZFJlZiIKICAgICAgICAgICAgICAgICAgICAgICAgICAgfQogICAgICAgICAgICAgICAgICAgICAgICB9CiAgICAgICAgICAgICAgICAgICAgIH0sCiAgICAgICAgICAgICAgICAgICAgICJoaWdoIiA6IHsKICAgICAgICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjE4OjQ0LTE4OjYxIiwKICAgICAgICAgICAgICAgICAgICAgICAgInBhdGgiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgICAgICAgICAgInR5cGUiIDogIlByb3BlcnR5IiwKICAgICAgICAgICAgICAgICAgICAgICAgInNvdXJjZSIgOiB7CiAgICAgICAgICAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIxODo0NC0xODo1NSIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICJwYXRoIiA6ICJlbmQiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAidHlwZSIgOiAiUHJvcGVydHkiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAic291cmNlIiA6IHsKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjE4OjQ0LTE4OjQ5IiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIm5hbWUiIDogInBlcmlvZCIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJPcGVyYW5kUmVmIgogICAgICAgICAgICAgICAgICAgICAgICAgICB9CiAgICAgICAgICAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgICAgICAgICAgfQogICAgICAgICAgICAgICAgICB9LAogICAgICAgICAgICAgICAgICAiZWxzZSIgOiB7CiAgICAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIyMDoxMy0yMDo2MiIsCiAgICAgICAgICAgICAgICAgICAgICJsb3dDbG9zZWQiIDogdHJ1ZSwKICAgICAgICAgICAgICAgICAgICAgImhpZ2hDbG9zZWQiIDogdHJ1ZSwKICAgICAgICAgICAgICAgICAgICAgInR5cGUiIDogIkludGVydmFsIiwKICAgICAgICAgICAgICAgICAgICAgImxvdyIgOiB7CiAgICAgICAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIyMDoyMi0yMDo0MSIsCiAgICAgICAgICAgICAgICAgICAgICAgICJwYXRoIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJQcm9wZXJ0eSIsCiAgICAgICAgICAgICAgICAgICAgICAgICJzb3VyY2UiIDogewogICAgICAgICAgICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMjA6MjItMjA6MzUiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAicGF0aCIgOiAic3RhcnQiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAidHlwZSIgOiAiUHJvcGVydHkiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAic291cmNlIiA6IHsKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjIwOjIyLTIwOjI3IiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIm5hbWUiIDogInBlcmlvZCIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJPcGVyYW5kUmVmIgogICAgICAgICAgICAgICAgICAgICAgICAgICB9CiAgICAgICAgICAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgICAgICAgICAgfSwKICAgICAgICAgICAgICAgICAgICAgImhpZ2giIDogewogICAgICAgICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMjA6NDQtMjA6NjEiLAogICAgICAgICAgICAgICAgICAgICAgICAicGF0aCIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAgICAgICAgICAidHlwZSIgOiAiUHJvcGVydHkiLAogICAgICAgICAgICAgICAgICAgICAgICAic291cmNlIiA6IHsKICAgICAgICAgICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjIwOjQ0LTIwOjU1IiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgInBhdGgiIDogImVuZCIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJQcm9wZXJ0eSIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICJzb3VyY2UiIDogewogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMjA6NDQtMjA6NDkiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAibmFtZSIgOiAicGVyaW9kIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk9wZXJhbmRSZWYiCiAgICAgICAgICAgICAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgICAgICAgICAgICAgfQogICAgICAgICAgICAgICAgICAgICB9CiAgICAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgICAgfQogICAgICAgICAgICB9LAogICAgICAgICAgICAib3BlcmFuZCIgOiBbIHsKICAgICAgICAgICAgICAgIm5hbWUiIDogInBlcmlvZCIsCiAgICAgICAgICAgICAgICJvcGVyYW5kVHlwZVNwZWNpZmllciIgOiB7CiAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIxMzozNS0xMzo0NSIsCiAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJ7aHR0cDovL2hsNy5vcmcvZmhpcn1QZXJpb2QiLAogICAgICAgICAgICAgICAgICAidHlwZSIgOiAiTmFtZWRUeXBlU3BlY2lmaWVyIgogICAgICAgICAgICAgICB9CiAgICAgICAgICAgIH0gXQogICAgICAgICB9LCB7CiAgICAgICAgICAgICJsb2NhdG9yIiA6ICIyMjoxLTMzOjciLAogICAgICAgICAgICAibmFtZSIgOiAiVG9DYWxlbmRhclVuaXQiLAogICAgICAgICAgICAiY29udGV4dCIgOiAiUGF0aWVudCIsCiAgICAgICAgICAgICJhY2Nlc3NMZXZlbCIgOiAiUHVibGljIiwKICAgICAgICAgICAgInR5cGUiIDogIkZ1bmN0aW9uRGVmIiwKICAgICAgICAgICAgImV4cHJlc3Npb24iIDogewogICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMjM6NS0zMzo3IiwKICAgICAgICAgICAgICAgInR5cGUiIDogIkNhc2UiLAogICAgICAgICAgICAgICAiY29tcGFyYW5kIiA6IHsKICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjIzOjEwLTIzOjEzIiwKICAgICAgICAgICAgICAgICAgIm5hbWUiIDogInVuaXQiLAogICAgICAgICAgICAgICAgICAidHlwZSIgOiAiT3BlcmFuZFJlZiIKICAgICAgICAgICAgICAgfSwKICAgICAgICAgICAgICAgImNhc2VJdGVtIiA6IFsgewogICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMjQ6OS0yNDozNiIsCiAgICAgICAgICAgICAgICAgICJ3aGVuIiA6IHsKICAgICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjI0OjE0LTI0OjE3IiwKICAgICAgICAgICAgICAgICAgICAgInZhbHVlVHlwZSIgOiAie3VybjpobDctb3JnOmVsbS10eXBlczpyMX1TdHJpbmciLAogICAgICAgICAgICAgICAgICAgICAidmFsdWUiIDogIm1zIiwKICAgICAgICAgICAgICAgICAgICAgInR5cGUiIDogIkxpdGVyYWwiCiAgICAgICAgICAgICAgICAgIH0sCiAgICAgICAgICAgICAgICAgICJ0aGVuIiA6IHsKICAgICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjI0OjI0LTI0OjM2IiwKICAgICAgICAgICAgICAgICAgICAgInZhbHVlVHlwZSIgOiAie3VybjpobDctb3JnOmVsbS10eXBlczpyMX1TdHJpbmciLAogICAgICAgICAgICAgICAgICAgICAidmFsdWUiIDogIm1pbGxpc2Vjb25kIiwKICAgICAgICAgICAgICAgICAgICAgInR5cGUiIDogIkxpdGVyYWwiCiAgICAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgICAgfSwgewogICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMjU6OS0yNTozMCIsCiAgICAgICAgICAgICAgICAgICJ3aGVuIiA6IHsKICAgICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjI1OjE0LTI1OjE2IiwKICAgICAgICAgICAgICAgICAgICAgInZhbHVlVHlwZSIgOiAie3VybjpobDctb3JnOmVsbS10eXBlczpyMX1TdHJpbmciLAogICAgICAgICAgICAgICAgICAgICAidmFsdWUiIDogInMiLAogICAgICAgICAgICAgICAgICAgICAidHlwZSIgOiAiTGl0ZXJhbCIKICAgICAgICAgICAgICAgICAgfSwKICAgICAgICAgICAgICAgICAgInRoZW4iIDogewogICAgICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMjU6MjMtMjU6MzAiLAogICAgICAgICAgICAgICAgICAgICAidmFsdWVUeXBlIiA6ICJ7dXJuOmhsNy1vcmc6ZWxtLXR5cGVzOnIxfVN0cmluZyIsCiAgICAgICAgICAgICAgICAgICAgICJ2YWx1ZSIgOiAic2Vjb25kIiwKICAgICAgICAgICAgICAgICAgICAgInR5cGUiIDogIkxpdGVyYWwiCiAgICAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgICAgfSwgewogICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMjY6OS0yNjozMiIsCiAgICAgICAgICAgICAgICAgICJ3aGVuIiA6IHsKICAgICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjI2OjE0LTI2OjE4IiwKICAgICAgICAgICAgICAgICAgICAgInZhbHVlVHlwZSIgOiAie3VybjpobDctb3JnOmVsbS10eXBlczpyMX1TdHJpbmciLAogICAgICAgICAgICAgICAgICAgICAidmFsdWUiIDogIm1pbiIsCiAgICAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJMaXRlcmFsIgogICAgICAgICAgICAgICAgICB9LAogICAgICAgICAgICAgICAgICAidGhlbiIgOiB7CiAgICAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIyNjoyNS0yNjozMiIsCiAgICAgICAgICAgICAgICAgICAgICJ2YWx1ZVR5cGUiIDogInt1cm46aGw3LW9yZzplbG0tdHlwZXM6cjF9U3RyaW5nIiwKICAgICAgICAgICAgICAgICAgICAgInZhbHVlIiA6ICJtaW51dGUiLAogICAgICAgICAgICAgICAgICAgICAidHlwZSIgOiAiTGl0ZXJhbCIKICAgICAgICAgICAgICAgICAgfQogICAgICAgICAgICAgICB9LCB7CiAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIyNzo5LTI3OjI4IiwKICAgICAgICAgICAgICAgICAgIndoZW4iIDogewogICAgICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMjc6MTQtMjc6MTYiLAogICAgICAgICAgICAgICAgICAgICAidmFsdWVUeXBlIiA6ICJ7dXJuOmhsNy1vcmc6ZWxtLXR5cGVzOnIxfVN0cmluZyIsCiAgICAgICAgICAgICAgICAgICAgICJ2YWx1ZSIgOiAiaCIsCiAgICAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJMaXRlcmFsIgogICAgICAgICAgICAgICAgICB9LAogICAgICAgICAgICAgICAgICAidGhlbiIgOiB7CiAgICAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIyNzoyMy0yNzoyOCIsCiAgICAgICAgICAgICAgICAgICAgICJ2YWx1ZVR5cGUiIDogInt1cm46aGw3LW9yZzplbG0tdHlwZXM6cjF9U3RyaW5nIiwKICAgICAgICAgICAgICAgICAgICAgInZhbHVlIiA6ICJob3VyIiwKICAgICAgICAgICAgICAgICAgICAgInR5cGUiIDogIkxpdGVyYWwiCiAgICAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgICAgfSwgewogICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMjg6OS0yODoyNyIsCiAgICAgICAgICAgICAgICAgICJ3aGVuIiA6IHsKICAgICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjI4OjE0LTI4OjE2IiwKICAgICAgICAgICAgICAgICAgICAgInZhbHVlVHlwZSIgOiAie3VybjpobDctb3JnOmVsbS10eXBlczpyMX1TdHJpbmciLAogICAgICAgICAgICAgICAgICAgICAidmFsdWUiIDogImQiLAogICAgICAgICAgICAgICAgICAgICAidHlwZSIgOiAiTGl0ZXJhbCIKICAgICAgICAgICAgICAgICAgfSwKICAgICAgICAgICAgICAgICAgInRoZW4iIDogewogICAgICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMjg6MjMtMjg6MjciLAogICAgICAgICAgICAgICAgICAgICAidmFsdWVUeXBlIiA6ICJ7dXJuOmhsNy1vcmc6ZWxtLXR5cGVzOnIxfVN0cmluZyIsCiAgICAgICAgICAgICAgICAgICAgICJ2YWx1ZSIgOiAiZGF5IiwKICAgICAgICAgICAgICAgICAgICAgInR5cGUiIDogIkxpdGVyYWwiCiAgICAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgICAgfSwgewogICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMjk6OS0yOToyOSIsCiAgICAgICAgICAgICAgICAgICJ3aGVuIiA6IHsKICAgICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjI5OjE0LTI5OjE3IiwKICAgICAgICAgICAgICAgICAgICAgInZhbHVlVHlwZSIgOiAie3VybjpobDctb3JnOmVsbS10eXBlczpyMX1TdHJpbmciLAogICAgICAgICAgICAgICAgICAgICAidmFsdWUiIDogIndrIiwKICAgICAgICAgICAgICAgICAgICAgInR5cGUiIDogIkxpdGVyYWwiCiAgICAgICAgICAgICAgICAgIH0sCiAgICAgICAgICAgICAgICAgICJ0aGVuIiA6IHsKICAgICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjI5OjI0LTI5OjI5IiwKICAgICAgICAgICAgICAgICAgICAgInZhbHVlVHlwZSIgOiAie3VybjpobDctb3JnOmVsbS10eXBlczpyMX1TdHJpbmciLAogICAgICAgICAgICAgICAgICAgICAidmFsdWUiIDogIndlZWsiLAogICAgICAgICAgICAgICAgICAgICAidHlwZSIgOiAiTGl0ZXJhbCIKICAgICAgICAgICAgICAgICAgfQogICAgICAgICAgICAgICB9LCB7CiAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIzMDo5LTMwOjMwIiwKICAgICAgICAgICAgICAgICAgIndoZW4iIDogewogICAgICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMzA6MTQtMzA6MTciLAogICAgICAgICAgICAgICAgICAgICAidmFsdWVUeXBlIiA6ICJ7dXJuOmhsNy1vcmc6ZWxtLXR5cGVzOnIxfVN0cmluZyIsCiAgICAgICAgICAgICAgICAgICAgICJ2YWx1ZSIgOiAibW8iLAogICAgICAgICAgICAgICAgICAgICAidHlwZSIgOiAiTGl0ZXJhbCIKICAgICAgICAgICAgICAgICAgfSwKICAgICAgICAgICAgICAgICAgInRoZW4iIDogewogICAgICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMzA6MjQtMzA6MzAiLAogICAgICAgICAgICAgICAgICAgICAidmFsdWVUeXBlIiA6ICJ7dXJuOmhsNy1vcmc6ZWxtLXR5cGVzOnIxfVN0cmluZyIsCiAgICAgICAgICAgICAgICAgICAgICJ2YWx1ZSIgOiAibW9udGgiLAogICAgICAgICAgICAgICAgICAgICAidHlwZSIgOiAiTGl0ZXJhbCIKICAgICAgICAgICAgICAgICAgfQogICAgICAgICAgICAgICB9LCB7CiAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIzMTo5LTMxOjI4IiwKICAgICAgICAgICAgICAgICAgIndoZW4iIDogewogICAgICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMzE6MTQtMzE6MTYiLAogICAgICAgICAgICAgICAgICAgICAidmFsdWVUeXBlIiA6ICJ7dXJuOmhsNy1vcmc6ZWxtLXR5cGVzOnIxfVN0cmluZyIsCiAgICAgICAgICAgICAgICAgICAgICJ2YWx1ZSIgOiAiYSIsCiAgICAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJMaXRlcmFsIgogICAgICAgICAgICAgICAgICB9LAogICAgICAgICAgICAgICAgICAidGhlbiIgOiB7CiAgICAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIzMToyMy0zMToyOCIsCiAgICAgICAgICAgICAgICAgICAgICJ2YWx1ZVR5cGUiIDogInt1cm46aGw3LW9yZzplbG0tdHlwZXM6cjF9U3RyaW5nIiwKICAgICAgICAgICAgICAgICAgICAgInZhbHVlIiA6ICJ5ZWFyIiwKICAgICAgICAgICAgICAgICAgICAgInR5cGUiIDogIkxpdGVyYWwiCiAgICAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgICAgfSBdLAogICAgICAgICAgICAgICAiZWxzZSIgOiB7CiAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIzMjoxNC0zMjoxNyIsCiAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJ1bml0IiwKICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk9wZXJhbmRSZWYiCiAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgfSwKICAgICAgICAgICAgIm9wZXJhbmQiIDogWyB7CiAgICAgICAgICAgICAgICJuYW1lIiA6ICJ1bml0IiwKICAgICAgICAgICAgICAgIm9wZXJhbmRUeXBlU3BlY2lmaWVyIiA6IHsKICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjIyOjM3LTIyOjQ5IiwKICAgICAgICAgICAgICAgICAgIm5hbWUiIDogInt1cm46aGw3LW9yZzplbG0tdHlwZXM6cjF9U3RyaW5nIiwKICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk5hbWVkVHlwZVNwZWNpZmllciIKICAgICAgICAgICAgICAgfQogICAgICAgICAgICB9IF0KICAgICAgICAgfSwgewogICAgICAgICAgICAibG9jYXRvciIgOiAiMzU6MS00Njo3IiwKICAgICAgICAgICAgIm5hbWUiIDogIlRvUXVhbnRpdHkiLAogICAgICAgICAgICAiY29udGV4dCIgOiAiUGF0aWVudCIsCiAgICAgICAgICAgICJhY2Nlc3NMZXZlbCIgOiAiUHVibGljIiwKICAgICAgICAgICAgInR5cGUiIDogIkZ1bmN0aW9uRGVmIiwKICAgICAgICAgICAgImV4cHJlc3Npb24iIDogewogICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMzY6NS00Njo3IiwKICAgICAgICAgICAgICAgInR5cGUiIDogIkNhc2UiLAogICAgICAgICAgICAgICAiY2FzZUl0ZW0iIDogWyB7CiAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIzNzo5LTM3OjM5IiwKICAgICAgICAgICAgICAgICAgIndoZW4iIDogewogICAgICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMzc6MTQtMzc6MjkiLAogICAgICAgICAgICAgICAgICAgICAidHlwZSIgOiAiSXNOdWxsIiwKICAgICAgICAgICAgICAgICAgICAgIm9wZXJhbmQiIDogewogICAgICAgICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMzc6MTQtMzc6MjEiLAogICAgICAgICAgICAgICAgICAgICAgICAibmFtZSIgOiAicXVhbnRpdHkiLAogICAgICAgICAgICAgICAgICAgICAgICAidHlwZSIgOiAiT3BlcmFuZFJlZiIKICAgICAgICAgICAgICAgICAgICAgfQogICAgICAgICAgICAgICAgICB9LAogICAgICAgICAgICAgICAgICAidGhlbiIgOiB7CiAgICAgICAgICAgICAgICAgICAgICJhc1R5cGUiIDogInt1cm46aGw3LW9yZzplbG0tdHlwZXM6cjF9UXVhbnRpdHkiLAogICAgICAgICAgICAgICAgICAgICAidHlwZSIgOiAiQXMiLAogICAgICAgICAgICAgICAgICAgICAib3BlcmFuZCIgOiB7CiAgICAgICAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIzNzozNi0zNzozOSIsCiAgICAgICAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJOdWxsIgogICAgICAgICAgICAgICAgICAgICB9CiAgICAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgICAgfSwgewogICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMzg6OS0zODo0NSIsCiAgICAgICAgICAgICAgICAgICJ3aGVuIiA6IHsKICAgICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjM4OjE0LTM4OjM1IiwKICAgICAgICAgICAgICAgICAgICAgInR5cGUiIDogIklzTnVsbCIsCiAgICAgICAgICAgICAgICAgICAgICJvcGVyYW5kIiA6IHsKICAgICAgICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjM4OjE0LTM4OjI3IiwKICAgICAgICAgICAgICAgICAgICAgICAgInBhdGgiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgICAgICAgICAgInR5cGUiIDogIlByb3BlcnR5IiwKICAgICAgICAgICAgICAgICAgICAgICAgInNvdXJjZSIgOiB7CiAgICAgICAgICAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIzODoxNC0zODoyMSIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJxdWFudGl0eSIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJPcGVyYW5kUmVmIgogICAgICAgICAgICAgICAgICAgICAgICB9CiAgICAgICAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgICAgICAgfSwKICAgICAgICAgICAgICAgICAgInRoZW4iIDogewogICAgICAgICAgICAgICAgICAgICAiYXNUeXBlIiA6ICJ7dXJuOmhsNy1vcmc6ZWxtLXR5cGVzOnIxfVF1YW50aXR5IiwKICAgICAgICAgICAgICAgICAgICAgInR5cGUiIDogIkFzIiwKICAgICAgICAgICAgICAgICAgICAgIm9wZXJhbmQiIDogewogICAgICAgICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMzg6NDItMzg6NDUiLAogICAgICAgICAgICAgICAgICAgICAgICAidHlwZSIgOiAiTnVsbCIKICAgICAgICAgICAgICAgICAgICAgfQogICAgICAgICAgICAgICAgICB9CiAgICAgICAgICAgICAgIH0sIHsKICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjM5OjktNDA6MTg5IiwKICAgICAgICAgICAgICAgICAgIndoZW4iIDogewogICAgICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMzk6MTQtMzk6NDQiLAogICAgICAgICAgICAgICAgICAgICAidHlwZSIgOiAiTm90IiwKICAgICAgICAgICAgICAgICAgICAgIm9wZXJhbmQiIDogewogICAgICAgICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMzk6MTQtMzk6NDQiLAogICAgICAgICAgICAgICAgICAgICAgICAidHlwZSIgOiAiSXNOdWxsIiwKICAgICAgICAgICAgICAgICAgICAgICAgIm9wZXJhbmQiIDogewogICAgICAgICAgICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMzk6MTQtMzk6MzIiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAicGF0aCIgOiAiY29tcGFyYXRvciIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJQcm9wZXJ0eSIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICJzb3VyY2UiIDogewogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMzk6MTQtMzk6MjEiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAibmFtZSIgOiAicXVhbnRpdHkiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAidHlwZSIgOiAiT3BlcmFuZFJlZiIKICAgICAgICAgICAgICAgICAgICAgICAgICAgfQogICAgICAgICAgICAgICAgICAgICAgICB9CiAgICAgICAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgICAgICAgfSwKICAgICAgICAgICAgICAgICAgInRoZW4iIDogewogICAgICAgICAgICAgICAgICAgICAiYXNUeXBlIiA6ICJ7dXJuOmhsNy1vcmc6ZWxtLXR5cGVzOnIxfVF1YW50aXR5IiwKICAgICAgICAgICAgICAgICAgICAgInR5cGUiIDogIkFzIiwKICAgICAgICAgICAgICAgICAgICAgIm9wZXJhbmQiIDogewogICAgICAgICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiNDA6MTMtNDA6MTg5IiwKICAgICAgICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk1lc3NhZ2UiLAogICAgICAgICAgICAgICAgICAgICAgICAic291cmNlIiA6IHsKICAgICAgICAgICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjQwOjIxLTQwOjI0IiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk51bGwiCiAgICAgICAgICAgICAgICAgICAgICAgIH0sCiAgICAgICAgICAgICAgICAgICAgICAgICJjb25kaXRpb24iIDogewogICAgICAgICAgICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiNDA6MjctNDA6MzAiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAidmFsdWVUeXBlIiA6ICJ7dXJuOmhsNy1vcmc6ZWxtLXR5cGVzOnIxfUJvb2xlYW4iLAogICAgICAgICAgICAgICAgICAgICAgICAgICAidmFsdWUiIDogInRydWUiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAidHlwZSIgOiAiTGl0ZXJhbCIKICAgICAgICAgICAgICAgICAgICAgICAgfSwKICAgICAgICAgICAgICAgICAgICAgICAgImNvZGUiIDogewogICAgICAgICAgICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiNDA6MzMtNDA6ODciLAogICAgICAgICAgICAgICAgICAgICAgICAgICAidmFsdWVUeXBlIiA6ICJ7dXJuOmhsNy1vcmc6ZWxtLXR5cGVzOnIxfVN0cmluZyIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICJ2YWx1ZSIgOiAiRkhJUkhlbHBlcnMuVG9RdWFudGl0eS5Db21wYXJhdG9yUXVhbnRpdHlOb3RTdXBwb3J0ZWQiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAidHlwZSIgOiAiTGl0ZXJhbCIKICAgICAgICAgICAgICAgICAgICAgICAgfSwKICAgICAgICAgICAgICAgICAgICAgICAgInNldmVyaXR5IiA6IHsKICAgICAgICAgICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjQwOjkwLTQwOjk2IiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgInZhbHVlVHlwZSIgOiAie3VybjpobDctb3JnOmVsbS10eXBlczpyMX1TdHJpbmciLAogICAgICAgICAgICAgICAgICAgICAgICAgICAidmFsdWUiIDogIkVycm9yIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgInR5cGUiIDogIkxpdGVyYWwiCiAgICAgICAgICAgICAgICAgICAgICAgIH0sCiAgICAgICAgICAgICAgICAgICAgICAgICJtZXNzYWdlIiA6IHsKICAgICAgICAgICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjQwOjk5LTQwOjE4OCIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICJ2YWx1ZVR5cGUiIDogInt1cm46aGw3LW9yZzplbG0tdHlwZXM6cjF9U3RyaW5nIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgInZhbHVlIiA6ICJGSElSIFF1YW50aXR5IHZhbHVlIGhhcyBhIGNvbXBhcmF0b3IgYW5kIGNhbm5vdCBiZSBjb252ZXJ0ZWQgdG8gYSBTeXN0ZW0uUXVhbnRpdHkgdmFsdWUuIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgInR5cGUiIDogIkxpdGVyYWwiCiAgICAgICAgICAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgICAgICAgICAgfQogICAgICAgICAgICAgICAgICB9CiAgICAgICAgICAgICAgIH0sIHsKICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjQxOjktNDM6MTM4IiwKICAgICAgICAgICAgICAgICAgIndoZW4iIDogewogICAgICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiNDE6MTQtNDI6OTIiLAogICAgICAgICAgICAgICAgICAgICAidHlwZSIgOiAiT3IiLAogICAgICAgICAgICAgICAgICAgICAib3BlcmFuZCIgOiBbIHsKICAgICAgICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjQxOjE0LTQxOjkxIiwKICAgICAgICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk9yIiwKICAgICAgICAgICAgICAgICAgICAgICAgIm9wZXJhbmQiIDogWyB7CiAgICAgICAgICAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICI0MToxNC00MTozNiIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJJc051bGwiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAib3BlcmFuZCIgOiB7CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICI0MToxNC00MToyOCIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJwYXRoIiA6ICJzeXN0ZW0iLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAidHlwZSIgOiAiUHJvcGVydHkiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAic291cmNlIiA6IHsKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjQxOjE0LTQxOjIxIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIm5hbWUiIDogInF1YW50aXR5IiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk9wZXJhbmRSZWYiCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgICAgICAgICAgICAgICAgfQogICAgICAgICAgICAgICAgICAgICAgICB9LCB7CiAgICAgICAgICAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICI0MTo0MS00MTo5MSIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJFcXVhbCIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICJvcGVyYW5kIiA6IFsgewogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiNDE6NDEtNDE6NjEiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAicGF0aCIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAidHlwZSIgOiAiUHJvcGVydHkiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAic291cmNlIiA6IHsKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjQxOjQxLTQxOjU1IiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInBhdGgiIDogInN5c3RlbSIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJQcm9wZXJ0eSIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJzb3VyY2UiIDogewogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiNDE6NDEtNDE6NDgiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAibmFtZSIgOiAicXVhbnRpdHkiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAidHlwZSIgOiAiT3BlcmFuZFJlZiIKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICB9CiAgICAgICAgICAgICAgICAgICAgICAgICAgIH0sIHsKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjQxOjY1LTQxOjkxIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInZhbHVlVHlwZSIgOiAie3VybjpobDctb3JnOmVsbS10eXBlczpyMX1TdHJpbmciLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAidmFsdWUiIDogImh0dHA6Ly91bml0c29mbWVhc3VyZS5vcmciLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAidHlwZSIgOiAiTGl0ZXJhbCIKICAgICAgICAgICAgICAgICAgICAgICAgICAgfSBdCiAgICAgICAgICAgICAgICAgICAgICAgIH0gXQogICAgICAgICAgICAgICAgICAgICB9LCB7CiAgICAgICAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICI0MjoxOC00Mjo5MiIsCiAgICAgICAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJFcXVhbCIsCiAgICAgICAgICAgICAgICAgICAgICAgICJvcGVyYW5kIiA6IFsgewogICAgICAgICAgICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiNDI6MTgtNDI6MzgiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAicGF0aCIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAidHlwZSIgOiAiUHJvcGVydHkiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAic291cmNlIiA6IHsKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjQyOjE4LTQyOjMyIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInBhdGgiIDogInN5c3RlbSIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJQcm9wZXJ0eSIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJzb3VyY2UiIDogewogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiNDI6MTgtNDI6MjUiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAibmFtZSIgOiAicXVhbnRpdHkiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAidHlwZSIgOiAiT3BlcmFuZFJlZiIKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfQogICAgICAgICAgICAgICAgICAgICAgICAgICB9CiAgICAgICAgICAgICAgICAgICAgICAgIH0sIHsKICAgICAgICAgICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjQyOjQyLTQyOjkyIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgInZhbHVlVHlwZSIgOiAie3VybjpobDctb3JnOmVsbS10eXBlczpyMX1TdHJpbmciLAogICAgICAgICAgICAgICAgICAgICAgICAgICAidmFsdWUiIDogImh0dHA6Ly9obDcub3JnL2ZoaXJwYXRoL0NvZGVTeXN0ZW0vY2FsZW5kYXItdW5pdHMiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAidHlwZSIgOiAiTGl0ZXJhbCIKICAgICAgICAgICAgICAgICAgICAgICAgfSBdCiAgICAgICAgICAgICAgICAgICAgIH0gXQogICAgICAgICAgICAgICAgICB9LAogICAgICAgICAgICAgICAgICAidGhlbiIgOiB7CiAgICAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICI0MzoxMy00MzoxMzgiLAogICAgICAgICAgICAgICAgICAgICAiY2xhc3NUeXBlIiA6ICJ7dXJuOmhsNy1vcmc6ZWxtLXR5cGVzOnIxfVF1YW50aXR5IiwKICAgICAgICAgICAgICAgICAgICAgInR5cGUiIDogIkluc3RhbmNlIiwKICAgICAgICAgICAgICAgICAgICAgImVsZW1lbnQiIDogWyB7CiAgICAgICAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICAgICAgICAgICJ2YWx1ZSIgOiB7CiAgICAgICAgICAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICI0MzozOC00Mzo1NyIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICJwYXRoIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJQcm9wZXJ0eSIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICJzb3VyY2UiIDogewogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiNDM6MzgtNDM6NTEiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAicGF0aCIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAidHlwZSIgOiAiUHJvcGVydHkiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAic291cmNlIiA6IHsKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjQzOjM4LTQzOjQ1IiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIm5hbWUiIDogInF1YW50aXR5IiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk9wZXJhbmRSZWYiCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgICAgICAgICAgICAgICAgfQogICAgICAgICAgICAgICAgICAgICAgICB9CiAgICAgICAgICAgICAgICAgICAgIH0sIHsKICAgICAgICAgICAgICAgICAgICAgICAgIm5hbWUiIDogInVuaXQiLAogICAgICAgICAgICAgICAgICAgICAgICAidmFsdWUiIDogewogICAgICAgICAgICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiNDM6NjYtNDM6MTM2IiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgIm5hbWUiIDogIlRvQ2FsZW5kYXJVbml0IiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgInR5cGUiIDogIkZ1bmN0aW9uUmVmIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgIm9wZXJhbmQiIDogWyB7CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICI0Mzo4MS00MzoxMzUiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAidHlwZSIgOiAiQ29hbGVzY2UiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAib3BlcmFuZCIgOiBbIHsKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjQzOjkwLTQzOjEwOCIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJwYXRoIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJQcm9wZXJ0eSIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJzb3VyY2UiIDogewogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiNDM6OTAtNDM6MTAyIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInBhdGgiIDogImNvZGUiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAidHlwZSIgOiAiUHJvcGVydHkiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAic291cmNlIiA6IHsKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjQzOjkwLTQzOjk3IiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIm5hbWUiIDogInF1YW50aXR5IiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk9wZXJhbmRSZWYiCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICB9LCB7CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICI0MzoxMTEtNDM6MTI5IiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInBhdGgiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInR5cGUiIDogIlByb3BlcnR5IiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInNvdXJjZSIgOiB7CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICI0MzoxMTEtNDM6MTIzIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInBhdGgiIDogInVuaXQiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAidHlwZSIgOiAiUHJvcGVydHkiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAic291cmNlIiA6IHsKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjQzOjExMS00MzoxMTgiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAibmFtZSIgOiAicXVhbnRpdHkiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAidHlwZSIgOiAiT3BlcmFuZFJlZiIKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB9CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIH0sIHsKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjQzOjEzMi00MzoxMzQiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAidmFsdWVUeXBlIiA6ICJ7dXJuOmhsNy1vcmc6ZWxtLXR5cGVzOnIxfVN0cmluZyIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJ2YWx1ZSIgOiAiMSIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJMaXRlcmFsIgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICB9IF0KICAgICAgICAgICAgICAgICAgICAgICAgICAgfSBdCiAgICAgICAgICAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgICAgICAgICAgfSBdCiAgICAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgICAgfSBdLAogICAgICAgICAgICAgICAiZWxzZSIgOiB7CiAgICAgICAgICAgICAgICAgICJhc1R5cGUiIDogInt1cm46aGw3LW9yZzplbG0tdHlwZXM6cjF9UXVhbnRpdHkiLAogICAgICAgICAgICAgICAgICAidHlwZSIgOiAiQXMiLAogICAgICAgICAgICAgICAgICAib3BlcmFuZCIgOiB7CiAgICAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICI0NToxMy00NToyMDUiLAogICAgICAgICAgICAgICAgICAgICAidHlwZSIgOiAiTWVzc2FnZSIsCiAgICAgICAgICAgICAgICAgICAgICJzb3VyY2UiIDogewogICAgICAgICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiNDU6MjEtNDU6MjQiLAogICAgICAgICAgICAgICAgICAgICAgICAidHlwZSIgOiAiTnVsbCIKICAgICAgICAgICAgICAgICAgICAgfSwKICAgICAgICAgICAgICAgICAgICAgImNvbmRpdGlvbiIgOiB7CiAgICAgICAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICI0NToyNy00NTozMCIsCiAgICAgICAgICAgICAgICAgICAgICAgICJ2YWx1ZVR5cGUiIDogInt1cm46aGw3LW9yZzplbG0tdHlwZXM6cjF9Qm9vbGVhbiIsCiAgICAgICAgICAgICAgICAgICAgICAgICJ2YWx1ZSIgOiAidHJ1ZSIsCiAgICAgICAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJMaXRlcmFsIgogICAgICAgICAgICAgICAgICAgICB9LAogICAgICAgICAgICAgICAgICAgICAiY29kZSIgOiB7CiAgICAgICAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICI0NTozMy00NTo3NiIsCiAgICAgICAgICAgICAgICAgICAgICAgICJ2YWx1ZVR5cGUiIDogInt1cm46aGw3LW9yZzplbG0tdHlwZXM6cjF9U3RyaW5nIiwKICAgICAgICAgICAgICAgICAgICAgICAgInZhbHVlIiA6ICJGSElSSGVscGVycy5Ub1F1YW50aXR5LkludmFsaWRGSElSUXVhbnRpdHkiLAogICAgICAgICAgICAgICAgICAgICAgICAidHlwZSIgOiAiTGl0ZXJhbCIKICAgICAgICAgICAgICAgICAgICAgfSwKICAgICAgICAgICAgICAgICAgICAgInNldmVyaXR5IiA6IHsKICAgICAgICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjQ1Ojc5LTQ1Ojg1IiwKICAgICAgICAgICAgICAgICAgICAgICAgInZhbHVlVHlwZSIgOiAie3VybjpobDctb3JnOmVsbS10eXBlczpyMX1TdHJpbmciLAogICAgICAgICAgICAgICAgICAgICAgICAidmFsdWUiIDogIkVycm9yIiwKICAgICAgICAgICAgICAgICAgICAgICAgInR5cGUiIDogIkxpdGVyYWwiCiAgICAgICAgICAgICAgICAgICAgIH0sCiAgICAgICAgICAgICAgICAgICAgICJtZXNzYWdlIiA6IHsKICAgICAgICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjQ1Ojg4LTQ1OjIwNCIsCiAgICAgICAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJDb25jYXRlbmF0ZSIsCiAgICAgICAgICAgICAgICAgICAgICAgICJvcGVyYW5kIiA6IFsgewogICAgICAgICAgICAgICAgICAgICAgICAgICAidHlwZSIgOiAiQ29hbGVzY2UiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAib3BlcmFuZCIgOiBbIHsKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjQ1Ojg4LTQ1OjE5OCIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJDb25jYXRlbmF0ZSIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJvcGVyYW5kIiA6IFsgewogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAidHlwZSIgOiAiQ29hbGVzY2UiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAib3BlcmFuZCIgOiBbIHsKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjQ1Ojg4LTQ1OjE3NiIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJDb25jYXRlbmF0ZSIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJvcGVyYW5kIiA6IFsgewogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAidHlwZSIgOiAiQ29hbGVzY2UiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAib3BlcmFuZCIgOiBbIHsKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjQ1Ojg4LTQ1OjE3MCIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJDb25jYXRlbmF0ZSIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJvcGVyYW5kIiA6IFsgewogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAidHlwZSIgOiAiQ29hbGVzY2UiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAib3BlcmFuZCIgOiBbIHsKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjQ1Ojg4LTQ1OjE0NiIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJDb25jYXRlbmF0ZSIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJvcGVyYW5kIiA6IFsgewogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAidHlwZSIgOiAiQ29hbGVzY2UiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAib3BlcmFuZCIgOiBbIHsKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjQ1Ojg4LTQ1OjEzOSIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJDb25jYXRlbmF0ZSIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJvcGVyYW5kIiA6IFsgewogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAidHlwZSIgOiAiQ29hbGVzY2UiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAib3BlcmFuZCIgOiBbIHsKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjQ1Ojg4LTQ1OjExNyIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJ2YWx1ZVR5cGUiIDogInt1cm46aGw3LW9yZzplbG0tdHlwZXM6cjF9U3RyaW5nIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInZhbHVlIiA6ICJJbnZhbGlkIEZISVIgUXVhbnRpdHkgY29kZTogIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInR5cGUiIDogIkxpdGVyYWwiCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIH0sIHsKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInZhbHVlVHlwZSIgOiAie3VybjpobDctb3JnOmVsbS10eXBlczpyMX1TdHJpbmciLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAidmFsdWUiIDogIiIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJMaXRlcmFsIgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB9IF0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfSwgewogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAidHlwZSIgOiAiQ29hbGVzY2UiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAib3BlcmFuZCIgOiBbIHsKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjQ1OjEyMS00NToxMzkiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAicGF0aCIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAidHlwZSIgOiAiUHJvcGVydHkiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAic291cmNlIiA6IHsKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjQ1OjEyMS00NToxMzMiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAicGF0aCIgOiAidW5pdCIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJQcm9wZXJ0eSIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJzb3VyY2UiIDogewogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiNDU6MTIxLTQ1OjEyOCIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJxdWFudGl0eSIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJPcGVyYW5kUmVmIgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB9CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfSwgewogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAidmFsdWVUeXBlIiA6ICJ7dXJuOmhsNy1vcmc6ZWxtLXR5cGVzOnIxfVN0cmluZyIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJ2YWx1ZSIgOiAiIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInR5cGUiIDogIkxpdGVyYWwiCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIH0gXQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB9IF0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfSwgewogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAidmFsdWVUeXBlIiA6ICJ7dXJuOmhsNy1vcmc6ZWxtLXR5cGVzOnIxfVN0cmluZyIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJ2YWx1ZSIgOiAiIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInR5cGUiIDogIkxpdGVyYWwiCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIH0gXQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB9LCB7CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJDb2FsZXNjZSIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJvcGVyYW5kIiA6IFsgewogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiNDU6MTQzLTQ1OjE0NiIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJ2YWx1ZVR5cGUiIDogInt1cm46aGw3LW9yZzplbG0tdHlwZXM6cjF9U3RyaW5nIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInZhbHVlIiA6ICIgKCIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJMaXRlcmFsIgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB9LCB7CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJ2YWx1ZVR5cGUiIDogInt1cm46aGw3LW9yZzplbG0tdHlwZXM6cjF9U3RyaW5nIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInZhbHVlIiA6ICIiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAidHlwZSIgOiAiTGl0ZXJhbCIKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfSBdCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIH0gXQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB9LCB7CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJ2YWx1ZVR5cGUiIDogInt1cm46aGw3LW9yZzplbG0tdHlwZXM6cjF9U3RyaW5nIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInZhbHVlIiA6ICIiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAidHlwZSIgOiAiTGl0ZXJhbCIKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfSBdCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIH0sIHsKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInR5cGUiIDogIkNvYWxlc2NlIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIm9wZXJhbmQiIDogWyB7CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICI0NToxNTAtNDU6MTcwIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInBhdGgiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInR5cGUiIDogIlByb3BlcnR5IiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInNvdXJjZSIgOiB7CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICI0NToxNTAtNDU6MTY0IiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInBhdGgiIDogInN5c3RlbSIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJQcm9wZXJ0eSIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJzb3VyY2UiIDogewogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiNDU6MTUwLTQ1OjE1NyIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJxdWFudGl0eSIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJPcGVyYW5kUmVmIgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB9CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfSwgewogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAidmFsdWVUeXBlIiA6ICJ7dXJuOmhsNy1vcmc6ZWxtLXR5cGVzOnIxfVN0cmluZyIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJ2YWx1ZSIgOiAiIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInR5cGUiIDogIkxpdGVyYWwiCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIH0gXQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB9IF0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfSwgewogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAidmFsdWVUeXBlIiA6ICJ7dXJuOmhsNy1vcmc6ZWxtLXR5cGVzOnIxfVN0cmluZyIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJ2YWx1ZSIgOiAiIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInR5cGUiIDogIkxpdGVyYWwiCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIH0gXQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB9LCB7CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJDb2FsZXNjZSIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJvcGVyYW5kIiA6IFsgewogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiNDU6MTc0LTQ1OjE3NiIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJ2YWx1ZVR5cGUiIDogInt1cm46aGw3LW9yZzplbG0tdHlwZXM6cjF9U3RyaW5nIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInZhbHVlIiA6ICJ8IiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInR5cGUiIDogIkxpdGVyYWwiCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIH0sIHsKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInZhbHVlVHlwZSIgOiAie3VybjpobDctb3JnOmVsbS10eXBlczpyMX1TdHJpbmciLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAidmFsdWUiIDogIiIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJMaXRlcmFsIgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB9IF0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfSBdCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIH0sIHsKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInZhbHVlVHlwZSIgOiAie3VybjpobDctb3JnOmVsbS10eXBlczpyMX1TdHJpbmciLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAidmFsdWUiIDogIiIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJMaXRlcmFsIgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB9IF0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfSwgewogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAidHlwZSIgOiAiQ29hbGVzY2UiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAib3BlcmFuZCIgOiBbIHsKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjQ1OjE4MC00NToxOTgiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAicGF0aCIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAidHlwZSIgOiAiUHJvcGVydHkiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAic291cmNlIiA6IHsKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjQ1OjE4MC00NToxOTIiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAicGF0aCIgOiAiY29kZSIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJQcm9wZXJ0eSIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJzb3VyY2UiIDogewogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiNDU6MTgwLTQ1OjE4NyIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJxdWFudGl0eSIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJPcGVyYW5kUmVmIgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB9CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfSwgewogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAidmFsdWVUeXBlIiA6ICJ7dXJuOmhsNy1vcmc6ZWxtLXR5cGVzOnIxfVN0cmluZyIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJ2YWx1ZSIgOiAiIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInR5cGUiIDogIkxpdGVyYWwiCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIH0gXQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICB9IF0KICAgICAgICAgICAgICAgICAgICAgICAgICAgfSwgewogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAidmFsdWVUeXBlIiA6ICJ7dXJuOmhsNy1vcmc6ZWxtLXR5cGVzOnIxfVN0cmluZyIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJ2YWx1ZSIgOiAiIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInR5cGUiIDogIkxpdGVyYWwiCiAgICAgICAgICAgICAgICAgICAgICAgICAgIH0gXQogICAgICAgICAgICAgICAgICAgICAgICB9LCB7CiAgICAgICAgICAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJDb2FsZXNjZSIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICJvcGVyYW5kIiA6IFsgewogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiNDU6MjAyLTQ1OjIwNCIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJ2YWx1ZVR5cGUiIDogInt1cm46aGw3LW9yZzplbG0tdHlwZXM6cjF9U3RyaW5nIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInZhbHVlIiA6ICIpIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInR5cGUiIDogIkxpdGVyYWwiCiAgICAgICAgICAgICAgICAgICAgICAgICAgIH0sIHsKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInZhbHVlVHlwZSIgOiAie3VybjpobDctb3JnOmVsbS10eXBlczpyMX1TdHJpbmciLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAidmFsdWUiIDogIiIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJMaXRlcmFsIgogICAgICAgICAgICAgICAgICAgICAgICAgICB9IF0KICAgICAgICAgICAgICAgICAgICAgICAgfSBdCiAgICAgICAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgICAgICAgfQogICAgICAgICAgICAgICB9CiAgICAgICAgICAgIH0sCiAgICAgICAgICAgICJvcGVyYW5kIiA6IFsgewogICAgICAgICAgICAgICAibmFtZSIgOiAicXVhbnRpdHkiLAogICAgICAgICAgICAgICAib3BlcmFuZFR5cGVTcGVjaWZpZXIiIDogewogICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMzU6MzctMzU6NDkiLAogICAgICAgICAgICAgICAgICAibmFtZSIgOiAie2h0dHA6Ly9obDcub3JnL2ZoaXJ9UXVhbnRpdHkiLAogICAgICAgICAgICAgICAgICAidHlwZSIgOiAiTmFtZWRUeXBlU3BlY2lmaWVyIgogICAgICAgICAgICAgICB9CiAgICAgICAgICAgIH0gXQogICAgICAgICB9LCB7CiAgICAgICAgICAgICJsb2NhdG9yIiA6ICI0ODoxLTU3OjciLAogICAgICAgICAgICAibmFtZSIgOiAiVG9RdWFudGl0eUlnbm9yaW5nQ29tcGFyYXRvciIsCiAgICAgICAgICAgICJjb250ZXh0IiA6ICJQYXRpZW50IiwKICAgICAgICAgICAgImFjY2Vzc0xldmVsIiA6ICJQdWJsaWMiLAogICAgICAgICAgICAidHlwZSIgOiAiRnVuY3Rpb25EZWYiLAogICAgICAgICAgICAiZXhwcmVzc2lvbiIgOiB7CiAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICI0OTo1LTU3OjciLAogICAgICAgICAgICAgICAidHlwZSIgOiAiQ2FzZSIsCiAgICAgICAgICAgICAgICJjYXNlSXRlbSIgOiBbIHsKICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjUwOjktNTA6MzkiLAogICAgICAgICAgICAgICAgICAid2hlbiIgOiB7CiAgICAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICI1MDoxNC01MDoyOSIsCiAgICAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJJc051bGwiLAogICAgICAgICAgICAgICAgICAgICAib3BlcmFuZCIgOiB7CiAgICAgICAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICI1MDoxNC01MDoyMSIsCiAgICAgICAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJxdWFudGl0eSIsCiAgICAgICAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJPcGVyYW5kUmVmIgogICAgICAgICAgICAgICAgICAgICB9CiAgICAgICAgICAgICAgICAgIH0sCiAgICAgICAgICAgICAgICAgICJ0aGVuIiA6IHsKICAgICAgICAgICAgICAgICAgICAgImFzVHlwZSIgOiAie3VybjpobDctb3JnOmVsbS10eXBlczpyMX1RdWFudGl0eSIsCiAgICAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJBcyIsCiAgICAgICAgICAgICAgICAgICAgICJvcGVyYW5kIiA6IHsKICAgICAgICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjUwOjM2LTUwOjM5IiwKICAgICAgICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk51bGwiCiAgICAgICAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgICAgICAgfQogICAgICAgICAgICAgICB9LCB7CiAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICI1MTo5LTUxOjQ1IiwKICAgICAgICAgICAgICAgICAgIndoZW4iIDogewogICAgICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiNTE6MTQtNTE6MzUiLAogICAgICAgICAgICAgICAgICAgICAidHlwZSIgOiAiSXNOdWxsIiwKICAgICAgICAgICAgICAgICAgICAgIm9wZXJhbmQiIDogewogICAgICAgICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiNTE6MTQtNTE6MjciLAogICAgICAgICAgICAgICAgICAgICAgICAicGF0aCIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAgICAgICAgICAidHlwZSIgOiAiUHJvcGVydHkiLAogICAgICAgICAgICAgICAgICAgICAgICAic291cmNlIiA6IHsKICAgICAgICAgICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjUxOjE0LTUxOjIxIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgIm5hbWUiIDogInF1YW50aXR5IiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk9wZXJhbmRSZWYiCiAgICAgICAgICAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgICAgICAgICAgfQogICAgICAgICAgICAgICAgICB9LAogICAgICAgICAgICAgICAgICAidGhlbiIgOiB7CiAgICAgICAgICAgICAgICAgICAgICJhc1R5cGUiIDogInt1cm46aGw3LW9yZzplbG0tdHlwZXM6cjF9UXVhbnRpdHkiLAogICAgICAgICAgICAgICAgICAgICAidHlwZSIgOiAiQXMiLAogICAgICAgICAgICAgICAgICAgICAib3BlcmFuZCIgOiB7CiAgICAgICAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICI1MTo0Mi01MTo0NSIsCiAgICAgICAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJOdWxsIgogICAgICAgICAgICAgICAgICAgICB9CiAgICAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgICAgfSwgewogICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiNTI6OS01NDoxMzgiLAogICAgICAgICAgICAgICAgICAid2hlbiIgOiB7CiAgICAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICI1MjoxNC01Mzo5MiIsCiAgICAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJPciIsCiAgICAgICAgICAgICAgICAgICAgICJvcGVyYW5kIiA6IFsgewogICAgICAgICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiNTI6MTQtNTI6OTEiLAogICAgICAgICAgICAgICAgICAgICAgICAidHlwZSIgOiAiT3IiLAogICAgICAgICAgICAgICAgICAgICAgICAib3BlcmFuZCIgOiBbIHsKICAgICAgICAgICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjUyOjE0LTUyOjM2IiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgInR5cGUiIDogIklzTnVsbCIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICJvcGVyYW5kIiA6IHsKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjUyOjE0LTUyOjI4IiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInBhdGgiIDogInN5c3RlbSIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJQcm9wZXJ0eSIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJzb3VyY2UiIDogewogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiNTI6MTQtNTI6MjEiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAibmFtZSIgOiAicXVhbnRpdHkiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAidHlwZSIgOiAiT3BlcmFuZFJlZiIKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfQogICAgICAgICAgICAgICAgICAgICAgICAgICB9CiAgICAgICAgICAgICAgICAgICAgICAgIH0sIHsKICAgICAgICAgICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjUyOjQxLTUyOjkxIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgInR5cGUiIDogIkVxdWFsIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgIm9wZXJhbmQiIDogWyB7CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICI1Mjo0MS01Mjo2MSIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJwYXRoIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJQcm9wZXJ0eSIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJzb3VyY2UiIDogewogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiNTI6NDEtNTI6NTUiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAicGF0aCIgOiAic3lzdGVtIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInR5cGUiIDogIlByb3BlcnR5IiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInNvdXJjZSIgOiB7CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICI1Mjo0MS01Mjo0OCIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJxdWFudGl0eSIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJPcGVyYW5kUmVmIgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB9CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgICAgICAgICAgICAgICAgfSwgewogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiNTI6NjUtNTI6OTEiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAidmFsdWVUeXBlIiA6ICJ7dXJuOmhsNy1vcmc6ZWxtLXR5cGVzOnIxfVN0cmluZyIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJ2YWx1ZSIgOiAiaHR0cDovL3VuaXRzb2ZtZWFzdXJlLm9yZyIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJMaXRlcmFsIgogICAgICAgICAgICAgICAgICAgICAgICAgICB9IF0KICAgICAgICAgICAgICAgICAgICAgICAgfSBdCiAgICAgICAgICAgICAgICAgICAgIH0sIHsKICAgICAgICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjUzOjE4LTUzOjkyIiwKICAgICAgICAgICAgICAgICAgICAgICAgInR5cGUiIDogIkVxdWFsIiwKICAgICAgICAgICAgICAgICAgICAgICAgIm9wZXJhbmQiIDogWyB7CiAgICAgICAgICAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICI1MzoxOC01MzozOCIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICJwYXRoIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJQcm9wZXJ0eSIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICJzb3VyY2UiIDogewogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiNTM6MTgtNTM6MzIiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAicGF0aCIgOiAic3lzdGVtIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInR5cGUiIDogIlByb3BlcnR5IiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInNvdXJjZSIgOiB7CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICI1MzoxOC01MzoyNSIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJxdWFudGl0eSIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJPcGVyYW5kUmVmIgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICB9CiAgICAgICAgICAgICAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgICAgICAgICAgICAgfSwgewogICAgICAgICAgICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiNTM6NDItNTM6OTIiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAidmFsdWVUeXBlIiA6ICJ7dXJuOmhsNy1vcmc6ZWxtLXR5cGVzOnIxfVN0cmluZyIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICJ2YWx1ZSIgOiAiaHR0cDovL2hsNy5vcmcvZmhpcnBhdGgvQ29kZVN5c3RlbS9jYWxlbmRhci11bml0cyIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJMaXRlcmFsIgogICAgICAgICAgICAgICAgICAgICAgICB9IF0KICAgICAgICAgICAgICAgICAgICAgfSBdCiAgICAgICAgICAgICAgICAgIH0sCiAgICAgICAgICAgICAgICAgICJ0aGVuIiA6IHsKICAgICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjU0OjEzLTU0OjEzOCIsCiAgICAgICAgICAgICAgICAgICAgICJjbGFzc1R5cGUiIDogInt1cm46aGw3LW9yZzplbG0tdHlwZXM6cjF9UXVhbnRpdHkiLAogICAgICAgICAgICAgICAgICAgICAidHlwZSIgOiAiSW5zdGFuY2UiLAogICAgICAgICAgICAgICAgICAgICAiZWxlbWVudCIgOiBbIHsKICAgICAgICAgICAgICAgICAgICAgICAgIm5hbWUiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgICAgICAgICAgInZhbHVlIiA6IHsKICAgICAgICAgICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjU0OjM4LTU0OjU3IiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgInBhdGgiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgInR5cGUiIDogIlByb3BlcnR5IiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgInNvdXJjZSIgOiB7CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICI1NDozOC01NDo1MSIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJwYXRoIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJQcm9wZXJ0eSIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJzb3VyY2UiIDogewogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiNTQ6MzgtNTQ6NDUiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAibmFtZSIgOiAicXVhbnRpdHkiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAidHlwZSIgOiAiT3BlcmFuZFJlZiIKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfQogICAgICAgICAgICAgICAgICAgICAgICAgICB9CiAgICAgICAgICAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgICAgICAgICAgfSwgewogICAgICAgICAgICAgICAgICAgICAgICAibmFtZSIgOiAidW5pdCIsCiAgICAgICAgICAgICAgICAgICAgICAgICJ2YWx1ZSIgOiB7CiAgICAgICAgICAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICI1NDo2Ni01NDoxMzYiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAibmFtZSIgOiAiVG9DYWxlbmRhclVuaXQiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAidHlwZSIgOiAiRnVuY3Rpb25SZWYiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAib3BlcmFuZCIgOiBbIHsKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjU0OjgxLTU0OjEzNSIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJDb2FsZXNjZSIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJvcGVyYW5kIiA6IFsgewogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiNTQ6OTAtNTQ6MTA4IiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInBhdGgiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInR5cGUiIDogIlByb3BlcnR5IiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInNvdXJjZSIgOiB7CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICI1NDo5MC01NDoxMDIiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAicGF0aCIgOiAiY29kZSIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJQcm9wZXJ0eSIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJzb3VyY2UiIDogewogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiNTQ6OTAtNTQ6OTciLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAibmFtZSIgOiAicXVhbnRpdHkiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAidHlwZSIgOiAiT3BlcmFuZFJlZiIKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB9CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIH0sIHsKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjU0OjExMS01NDoxMjkiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAicGF0aCIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAidHlwZSIgOiAiUHJvcGVydHkiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAic291cmNlIiA6IHsKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjU0OjExMS01NDoxMjMiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAicGF0aCIgOiAidW5pdCIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJQcm9wZXJ0eSIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJzb3VyY2UiIDogewogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiNTQ6MTExLTU0OjExOCIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJxdWFudGl0eSIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJPcGVyYW5kUmVmIgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB9CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfSwgewogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiNTQ6MTMyLTU0OjEzNCIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJ2YWx1ZVR5cGUiIDogInt1cm46aGw3LW9yZzplbG0tdHlwZXM6cjF9U3RyaW5nIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInZhbHVlIiA6ICIxIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInR5cGUiIDogIkxpdGVyYWwiCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIH0gXQogICAgICAgICAgICAgICAgICAgICAgICAgICB9IF0KICAgICAgICAgICAgICAgICAgICAgICAgfQogICAgICAgICAgICAgICAgICAgICB9IF0KICAgICAgICAgICAgICAgICAgfQogICAgICAgICAgICAgICB9IF0sCiAgICAgICAgICAgICAgICJlbHNlIiA6IHsKICAgICAgICAgICAgICAgICAgImFzVHlwZSIgOiAie3VybjpobDctb3JnOmVsbS10eXBlczpyMX1RdWFudGl0eSIsCiAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJBcyIsCiAgICAgICAgICAgICAgICAgICJvcGVyYW5kIiA6IHsKICAgICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjU2OjEzLTU2OjIwNSIsCiAgICAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJNZXNzYWdlIiwKICAgICAgICAgICAgICAgICAgICAgInNvdXJjZSIgOiB7CiAgICAgICAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICI1NjoyMS01NjoyNCIsCiAgICAgICAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJOdWxsIgogICAgICAgICAgICAgICAgICAgICB9LAogICAgICAgICAgICAgICAgICAgICAiY29uZGl0aW9uIiA6IHsKICAgICAgICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjU2OjI3LTU2OjMwIiwKICAgICAgICAgICAgICAgICAgICAgICAgInZhbHVlVHlwZSIgOiAie3VybjpobDctb3JnOmVsbS10eXBlczpyMX1Cb29sZWFuIiwKICAgICAgICAgICAgICAgICAgICAgICAgInZhbHVlIiA6ICJ0cnVlIiwKICAgICAgICAgICAgICAgICAgICAgICAgInR5cGUiIDogIkxpdGVyYWwiCiAgICAgICAgICAgICAgICAgICAgIH0sCiAgICAgICAgICAgICAgICAgICAgICJjb2RlIiA6IHsKICAgICAgICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjU2OjMzLTU2Ojc2IiwKICAgICAgICAgICAgICAgICAgICAgICAgInZhbHVlVHlwZSIgOiAie3VybjpobDctb3JnOmVsbS10eXBlczpyMX1TdHJpbmciLAogICAgICAgICAgICAgICAgICAgICAgICAidmFsdWUiIDogIkZISVJIZWxwZXJzLlRvUXVhbnRpdHkuSW52YWxpZEZISVJRdWFudGl0eSIsCiAgICAgICAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJMaXRlcmFsIgogICAgICAgICAgICAgICAgICAgICB9LAogICAgICAgICAgICAgICAgICAgICAic2V2ZXJpdHkiIDogewogICAgICAgICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiNTY6NzktNTY6ODUiLAogICAgICAgICAgICAgICAgICAgICAgICAidmFsdWVUeXBlIiA6ICJ7dXJuOmhsNy1vcmc6ZWxtLXR5cGVzOnIxfVN0cmluZyIsCiAgICAgICAgICAgICAgICAgICAgICAgICJ2YWx1ZSIgOiAiRXJyb3IiLAogICAgICAgICAgICAgICAgICAgICAgICAidHlwZSIgOiAiTGl0ZXJhbCIKICAgICAgICAgICAgICAgICAgICAgfSwKICAgICAgICAgICAgICAgICAgICAgIm1lc3NhZ2UiIDogewogICAgICAgICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiNTY6ODgtNTY6MjA0IiwKICAgICAgICAgICAgICAgICAgICAgICAgInR5cGUiIDogIkNvbmNhdGVuYXRlIiwKICAgICAgICAgICAgICAgICAgICAgICAgIm9wZXJhbmQiIDogWyB7CiAgICAgICAgICAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJDb2FsZXNjZSIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICJvcGVyYW5kIiA6IFsgewogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiNTY6ODgtNTY6MTk4IiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInR5cGUiIDogIkNvbmNhdGVuYXRlIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIm9wZXJhbmQiIDogWyB7CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJDb2FsZXNjZSIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJvcGVyYW5kIiA6IFsgewogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiNTY6ODgtNTY6MTc2IiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInR5cGUiIDogIkNvbmNhdGVuYXRlIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIm9wZXJhbmQiIDogWyB7CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJDb2FsZXNjZSIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJvcGVyYW5kIiA6IFsgewogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiNTY6ODgtNTY6MTcwIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInR5cGUiIDogIkNvbmNhdGVuYXRlIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIm9wZXJhbmQiIDogWyB7CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJDb2FsZXNjZSIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJvcGVyYW5kIiA6IFsgewogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiNTY6ODgtNTY6MTQ2IiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInR5cGUiIDogIkNvbmNhdGVuYXRlIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIm9wZXJhbmQiIDogWyB7CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJDb2FsZXNjZSIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJvcGVyYW5kIiA6IFsgewogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiNTY6ODgtNTY6MTM5IiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInR5cGUiIDogIkNvbmNhdGVuYXRlIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIm9wZXJhbmQiIDogWyB7CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJDb2FsZXNjZSIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJvcGVyYW5kIiA6IFsgewogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiNTY6ODgtNTY6MTE3IiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInZhbHVlVHlwZSIgOiAie3VybjpobDctb3JnOmVsbS10eXBlczpyMX1TdHJpbmciLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAidmFsdWUiIDogIkludmFsaWQgRkhJUiBRdWFudGl0eSBjb2RlOiAiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAidHlwZSIgOiAiTGl0ZXJhbCIKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfSwgewogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAidmFsdWVUeXBlIiA6ICJ7dXJuOmhsNy1vcmc6ZWxtLXR5cGVzOnIxfVN0cmluZyIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJ2YWx1ZSIgOiAiIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInR5cGUiIDogIkxpdGVyYWwiCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIH0gXQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB9LCB7CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJDb2FsZXNjZSIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJvcGVyYW5kIiA6IFsgewogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiNTY6MTIxLTU2OjEzOSIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJwYXRoIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJQcm9wZXJ0eSIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJzb3VyY2UiIDogewogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiNTY6MTIxLTU2OjEzMyIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJwYXRoIiA6ICJ1bml0IiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInR5cGUiIDogIlByb3BlcnR5IiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInNvdXJjZSIgOiB7CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICI1NjoxMjEtNTY6MTI4IiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIm5hbWUiIDogInF1YW50aXR5IiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk9wZXJhbmRSZWYiCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB9LCB7CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJ2YWx1ZVR5cGUiIDogInt1cm46aGw3LW9yZzplbG0tdHlwZXM6cjF9U3RyaW5nIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInZhbHVlIiA6ICIiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAidHlwZSIgOiAiTGl0ZXJhbCIKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfSBdCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIH0gXQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB9LCB7CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJ2YWx1ZVR5cGUiIDogInt1cm46aGw3LW9yZzplbG0tdHlwZXM6cjF9U3RyaW5nIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInZhbHVlIiA6ICIiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAidHlwZSIgOiAiTGl0ZXJhbCIKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfSBdCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIH0sIHsKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInR5cGUiIDogIkNvYWxlc2NlIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIm9wZXJhbmQiIDogWyB7CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICI1NjoxNDMtNTY6MTQ2IiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInZhbHVlVHlwZSIgOiAie3VybjpobDctb3JnOmVsbS10eXBlczpyMX1TdHJpbmciLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAidmFsdWUiIDogIiAoIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInR5cGUiIDogIkxpdGVyYWwiCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIH0sIHsKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInZhbHVlVHlwZSIgOiAie3VybjpobDctb3JnOmVsbS10eXBlczpyMX1TdHJpbmciLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAidmFsdWUiIDogIiIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJMaXRlcmFsIgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB9IF0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfSBdCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIH0sIHsKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInZhbHVlVHlwZSIgOiAie3VybjpobDctb3JnOmVsbS10eXBlczpyMX1TdHJpbmciLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAidmFsdWUiIDogIiIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJMaXRlcmFsIgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB9IF0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfSwgewogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAidHlwZSIgOiAiQ29hbGVzY2UiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAib3BlcmFuZCIgOiBbIHsKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjU2OjE1MC01NjoxNzAiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAicGF0aCIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAidHlwZSIgOiAiUHJvcGVydHkiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAic291cmNlIiA6IHsKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjU2OjE1MC01NjoxNjQiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAicGF0aCIgOiAic3lzdGVtIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInR5cGUiIDogIlByb3BlcnR5IiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInNvdXJjZSIgOiB7CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICI1NjoxNTAtNTY6MTU3IiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIm5hbWUiIDogInF1YW50aXR5IiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk9wZXJhbmRSZWYiCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB9LCB7CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJ2YWx1ZVR5cGUiIDogInt1cm46aGw3LW9yZzplbG0tdHlwZXM6cjF9U3RyaW5nIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInZhbHVlIiA6ICIiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAidHlwZSIgOiAiTGl0ZXJhbCIKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfSBdCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIH0gXQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB9LCB7CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJ2YWx1ZVR5cGUiIDogInt1cm46aGw3LW9yZzplbG0tdHlwZXM6cjF9U3RyaW5nIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInZhbHVlIiA6ICIiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAidHlwZSIgOiAiTGl0ZXJhbCIKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfSBdCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIH0sIHsKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInR5cGUiIDogIkNvYWxlc2NlIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIm9wZXJhbmQiIDogWyB7CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICI1NjoxNzQtNTY6MTc2IiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInZhbHVlVHlwZSIgOiAie3VybjpobDctb3JnOmVsbS10eXBlczpyMX1TdHJpbmciLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAidmFsdWUiIDogInwiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAidHlwZSIgOiAiTGl0ZXJhbCIKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfSwgewogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAidmFsdWVUeXBlIiA6ICJ7dXJuOmhsNy1vcmc6ZWxtLXR5cGVzOnIxfVN0cmluZyIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJ2YWx1ZSIgOiAiIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInR5cGUiIDogIkxpdGVyYWwiCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIH0gXQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB9IF0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfSwgewogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAidmFsdWVUeXBlIiA6ICJ7dXJuOmhsNy1vcmc6ZWxtLXR5cGVzOnIxfVN0cmluZyIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJ2YWx1ZSIgOiAiIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInR5cGUiIDogIkxpdGVyYWwiCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIH0gXQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICB9LCB7CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJDb2FsZXNjZSIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJvcGVyYW5kIiA6IFsgewogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiNTY6MTgwLTU2OjE5OCIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJwYXRoIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJQcm9wZXJ0eSIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJzb3VyY2UiIDogewogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiNTY6MTgwLTU2OjE5MiIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJwYXRoIiA6ICJjb2RlIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInR5cGUiIDogIlByb3BlcnR5IiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInNvdXJjZSIgOiB7CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICI1NjoxODAtNTY6MTg3IiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIm5hbWUiIDogInF1YW50aXR5IiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk9wZXJhbmRSZWYiCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB9LCB7CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJ2YWx1ZVR5cGUiIDogInt1cm46aGw3LW9yZzplbG0tdHlwZXM6cjF9U3RyaW5nIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInZhbHVlIiA6ICIiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAidHlwZSIgOiAiTGl0ZXJhbCIKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfSBdCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIH0gXQogICAgICAgICAgICAgICAgICAgICAgICAgICB9LCB7CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJ2YWx1ZVR5cGUiIDogInt1cm46aGw3LW9yZzplbG0tdHlwZXM6cjF9U3RyaW5nIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInZhbHVlIiA6ICIiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAidHlwZSIgOiAiTGl0ZXJhbCIKICAgICAgICAgICAgICAgICAgICAgICAgICAgfSBdCiAgICAgICAgICAgICAgICAgICAgICAgIH0sIHsKICAgICAgICAgICAgICAgICAgICAgICAgICAgInR5cGUiIDogIkNvYWxlc2NlIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgIm9wZXJhbmQiIDogWyB7CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICI1NjoyMDItNTY6MjA0IiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInZhbHVlVHlwZSIgOiAie3VybjpobDctb3JnOmVsbS10eXBlczpyMX1TdHJpbmciLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAidmFsdWUiIDogIikiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAidHlwZSIgOiAiTGl0ZXJhbCIKICAgICAgICAgICAgICAgICAgICAgICAgICAgfSwgewogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAidmFsdWVUeXBlIiA6ICJ7dXJuOmhsNy1vcmc6ZWxtLXR5cGVzOnIxfVN0cmluZyIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJ2YWx1ZSIgOiAiIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInR5cGUiIDogIkxpdGVyYWwiCiAgICAgICAgICAgICAgICAgICAgICAgICAgIH0gXQogICAgICAgICAgICAgICAgICAgICAgICB9IF0KICAgICAgICAgICAgICAgICAgICAgfQogICAgICAgICAgICAgICAgICB9CiAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgfSwKICAgICAgICAgICAgIm9wZXJhbmQiIDogWyB7CiAgICAgICAgICAgICAgICJuYW1lIiA6ICJxdWFudGl0eSIsCiAgICAgICAgICAgICAgICJvcGVyYW5kVHlwZVNwZWNpZmllciIgOiB7CiAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICI0ODo1NS00ODo2NyIsCiAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJ7aHR0cDovL2hsNy5vcmcvZmhpcn1RdWFudGl0eSIsCiAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJOYW1lZFR5cGVTcGVjaWZpZXIiCiAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgfSBdCiAgICAgICAgIH0sIHsKICAgICAgICAgICAgImxvY2F0b3IiIDogIjU5OjEtODQ6MTEiLAogICAgICAgICAgICAibmFtZSIgOiAiVG9JbnRlcnZhbCIsCiAgICAgICAgICAgICJjb250ZXh0IiA6ICJQYXRpZW50IiwKICAgICAgICAgICAgImFjY2Vzc0xldmVsIiA6ICJQdWJsaWMiLAogICAgICAgICAgICAidHlwZSIgOiAiRnVuY3Rpb25EZWYiLAogICAgICAgICAgICAiZXhwcmVzc2lvbiIgOiB7CiAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICI2MDo1LTg0OjExIiwKICAgICAgICAgICAgICAgInR5cGUiIDogIklmIiwKICAgICAgICAgICAgICAgImNvbmRpdGlvbiIgOiB7CiAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICI2MDo4LTYwOjIzIiwKICAgICAgICAgICAgICAgICAgInR5cGUiIDogIklzTnVsbCIsCiAgICAgICAgICAgICAgICAgICJvcGVyYW5kIiA6IHsKICAgICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjYwOjgtNjA6MTUiLAogICAgICAgICAgICAgICAgICAgICAibmFtZSIgOiAicXVhbnRpdHkiLAogICAgICAgICAgICAgICAgICAgICAidHlwZSIgOiAiT3BlcmFuZFJlZiIKICAgICAgICAgICAgICAgICAgfQogICAgICAgICAgICAgICB9LAogICAgICAgICAgICAgICAidGhlbiIgOiB7CiAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJBcyIsCiAgICAgICAgICAgICAgICAgICJvcGVyYW5kIiA6IHsKICAgICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjYwOjMwLTYwOjMzIiwKICAgICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk51bGwiCiAgICAgICAgICAgICAgICAgIH0sCiAgICAgICAgICAgICAgICAgICJhc1R5cGVTcGVjaWZpZXIiIDogewogICAgICAgICAgICAgICAgICAgICAidHlwZSIgOiAiSW50ZXJ2YWxUeXBlU3BlY2lmaWVyIiwKICAgICAgICAgICAgICAgICAgICAgInBvaW50VHlwZSIgOiB7CiAgICAgICAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJ7dXJuOmhsNy1vcmc6ZWxtLXR5cGVzOnIxfVF1YW50aXR5IiwKICAgICAgICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk5hbWVkVHlwZVNwZWNpZmllciIKICAgICAgICAgICAgICAgICAgICAgfQogICAgICAgICAgICAgICAgICB9CiAgICAgICAgICAgICAgIH0sCiAgICAgICAgICAgICAgICJlbHNlIiA6IHsKICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjYxOjktODQ6MTEiLAogICAgICAgICAgICAgICAgICAidHlwZSIgOiAiQ2FzZSIsCiAgICAgICAgICAgICAgICAgICJjb21wYXJhbmQiIDogewogICAgICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiNjE6MTQtNjE6MzgiLAogICAgICAgICAgICAgICAgICAgICAicGF0aCIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAgICAgICAidHlwZSIgOiAiUHJvcGVydHkiLAogICAgICAgICAgICAgICAgICAgICAic291cmNlIiA6IHsKICAgICAgICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjYxOjE0LTYxOjMyIiwKICAgICAgICAgICAgICAgICAgICAgICAgInBhdGgiIDogImNvbXBhcmF0b3IiLAogICAgICAgICAgICAgICAgICAgICAgICAidHlwZSIgOiAiUHJvcGVydHkiLAogICAgICAgICAgICAgICAgICAgICAgICAic291cmNlIiA6IHsKICAgICAgICAgICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjYxOjE0LTYxOjIxIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgIm5hbWUiIDogInF1YW50aXR5IiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk9wZXJhbmRSZWYiCiAgICAgICAgICAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgICAgICAgICAgfQogICAgICAgICAgICAgICAgICB9LAogICAgICAgICAgICAgICAgICAiY2FzZUl0ZW0iIDogWyB7CiAgICAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICI2MjoxMy02NjoxNyIsCiAgICAgICAgICAgICAgICAgICAgICJ3aGVuIiA6IHsKICAgICAgICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjYyOjE4LTYyOjIwIiwKICAgICAgICAgICAgICAgICAgICAgICAgInZhbHVlVHlwZSIgOiAie3VybjpobDctb3JnOmVsbS10eXBlczpyMX1TdHJpbmciLAogICAgICAgICAgICAgICAgICAgICAgICAidmFsdWUiIDogIjwiLAogICAgICAgICAgICAgICAgICAgICAgICAidHlwZSIgOiAiTGl0ZXJhbCIKICAgICAgICAgICAgICAgICAgICAgfSwKICAgICAgICAgICAgICAgICAgICAgInRoZW4iIDogewogICAgICAgICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiNjM6MTctNjY6MTciLAogICAgICAgICAgICAgICAgICAgICAgICAibG93Q2xvc2VkIiA6IHRydWUsCiAgICAgICAgICAgICAgICAgICAgICAgICJoaWdoQ2xvc2VkIiA6IGZhbHNlLAogICAgICAgICAgICAgICAgICAgICAgICAidHlwZSIgOiAiSW50ZXJ2YWwiLAogICAgICAgICAgICAgICAgICAgICAgICAibG93IiA6IHsKICAgICAgICAgICAgICAgICAgICAgICAgICAgImFzVHlwZSIgOiAie3VybjpobDctb3JnOmVsbS10eXBlczpyMX1RdWFudGl0eSIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJBcyIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICJvcGVyYW5kIiA6IHsKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjY0OjIxLTY0OjI0IiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk51bGwiCiAgICAgICAgICAgICAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgICAgICAgICAgICAgfSwKICAgICAgICAgICAgICAgICAgICAgICAgImhpZ2giIDogewogICAgICAgICAgICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiNjU6MjEtNjU6NTgiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAibmFtZSIgOiAiVG9RdWFudGl0eUlnbm9yaW5nQ29tcGFyYXRvciIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJGdW5jdGlvblJlZiIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICJvcGVyYW5kIiA6IFsgewogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiNjU6NTAtNjU6NTciLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAibmFtZSIgOiAicXVhbnRpdHkiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAidHlwZSIgOiAiT3BlcmFuZFJlZiIKICAgICAgICAgICAgICAgICAgICAgICAgICAgfSBdCiAgICAgICAgICAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgICAgICAgICAgfQogICAgICAgICAgICAgICAgICB9LCB7CiAgICAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICI2NzoxMy03MToxNyIsCiAgICAgICAgICAgICAgICAgICAgICJ3aGVuIiA6IHsKICAgICAgICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjY3OjE4LTY3OjIxIiwKICAgICAgICAgICAgICAgICAgICAgICAgInZhbHVlVHlwZSIgOiAie3VybjpobDctb3JnOmVsbS10eXBlczpyMX1TdHJpbmciLAogICAgICAgICAgICAgICAgICAgICAgICAidmFsdWUiIDogIjw9IiwKICAgICAgICAgICAgICAgICAgICAgICAgInR5cGUiIDogIkxpdGVyYWwiCiAgICAgICAgICAgICAgICAgICAgIH0sCiAgICAgICAgICAgICAgICAgICAgICJ0aGVuIiA6IHsKICAgICAgICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjY4OjE3LTcxOjE3IiwKICAgICAgICAgICAgICAgICAgICAgICAgImxvd0Nsb3NlZCIgOiB0cnVlLAogICAgICAgICAgICAgICAgICAgICAgICAiaGlnaENsb3NlZCIgOiB0cnVlLAogICAgICAgICAgICAgICAgICAgICAgICAidHlwZSIgOiAiSW50ZXJ2YWwiLAogICAgICAgICAgICAgICAgICAgICAgICAibG93IiA6IHsKICAgICAgICAgICAgICAgICAgICAgICAgICAgImFzVHlwZSIgOiAie3VybjpobDctb3JnOmVsbS10eXBlczpyMX1RdWFudGl0eSIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJBcyIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICJvcGVyYW5kIiA6IHsKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjY5OjIxLTY5OjI0IiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk51bGwiCiAgICAgICAgICAgICAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgICAgICAgICAgICAgfSwKICAgICAgICAgICAgICAgICAgICAgICAgImhpZ2giIDogewogICAgICAgICAgICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiNzA6MjEtNzA6NTgiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAibmFtZSIgOiAiVG9RdWFudGl0eUlnbm9yaW5nQ29tcGFyYXRvciIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJGdW5jdGlvblJlZiIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICJvcGVyYW5kIiA6IFsgewogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiNzA6NTAtNzA6NTciLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAibmFtZSIgOiAicXVhbnRpdHkiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAidHlwZSIgOiAiT3BlcmFuZFJlZiIKICAgICAgICAgICAgICAgICAgICAgICAgICAgfSBdCiAgICAgICAgICAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgICAgICAgICAgfQogICAgICAgICAgICAgICAgICB9LCB7CiAgICAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICI3MjoxMy03NjoxNyIsCiAgICAgICAgICAgICAgICAgICAgICJ3aGVuIiA6IHsKICAgICAgICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjcyOjE4LTcyOjIxIiwKICAgICAgICAgICAgICAgICAgICAgICAgInZhbHVlVHlwZSIgOiAie3VybjpobDctb3JnOmVsbS10eXBlczpyMX1TdHJpbmciLAogICAgICAgICAgICAgICAgICAgICAgICAidmFsdWUiIDogIj49IiwKICAgICAgICAgICAgICAgICAgICAgICAgInR5cGUiIDogIkxpdGVyYWwiCiAgICAgICAgICAgICAgICAgICAgIH0sCiAgICAgICAgICAgICAgICAgICAgICJ0aGVuIiA6IHsKICAgICAgICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjczOjE3LTc2OjE3IiwKICAgICAgICAgICAgICAgICAgICAgICAgImxvd0Nsb3NlZCIgOiB0cnVlLAogICAgICAgICAgICAgICAgICAgICAgICAiaGlnaENsb3NlZCIgOiB0cnVlLAogICAgICAgICAgICAgICAgICAgICAgICAidHlwZSIgOiAiSW50ZXJ2YWwiLAogICAgICAgICAgICAgICAgICAgICAgICAibG93IiA6IHsKICAgICAgICAgICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjc0OjIxLTc0OjU4IiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgIm5hbWUiIDogIlRvUXVhbnRpdHlJZ25vcmluZ0NvbXBhcmF0b3IiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAidHlwZSIgOiAiRnVuY3Rpb25SZWYiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAib3BlcmFuZCIgOiBbIHsKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjc0OjUwLTc0OjU3IiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIm5hbWUiIDogInF1YW50aXR5IiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk9wZXJhbmRSZWYiCiAgICAgICAgICAgICAgICAgICAgICAgICAgIH0gXQogICAgICAgICAgICAgICAgICAgICAgICB9LAogICAgICAgICAgICAgICAgICAgICAgICAiaGlnaCIgOiB7CiAgICAgICAgICAgICAgICAgICAgICAgICAgICJhc1R5cGUiIDogInt1cm46aGw3LW9yZzplbG0tdHlwZXM6cjF9UXVhbnRpdHkiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAidHlwZSIgOiAiQXMiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAib3BlcmFuZCIgOiB7CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICI3NToyMS03NToyNCIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJOdWxsIgogICAgICAgICAgICAgICAgICAgICAgICAgICB9CiAgICAgICAgICAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgICAgICAgICAgfQogICAgICAgICAgICAgICAgICB9LCB7CiAgICAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICI3NzoxMy04MToxNyIsCiAgICAgICAgICAgICAgICAgICAgICJ3aGVuIiA6IHsKICAgICAgICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjc3OjE4LTc3OjIwIiwKICAgICAgICAgICAgICAgICAgICAgICAgInZhbHVlVHlwZSIgOiAie3VybjpobDctb3JnOmVsbS10eXBlczpyMX1TdHJpbmciLAogICAgICAgICAgICAgICAgICAgICAgICAidmFsdWUiIDogIj4iLAogICAgICAgICAgICAgICAgICAgICAgICAidHlwZSIgOiAiTGl0ZXJhbCIKICAgICAgICAgICAgICAgICAgICAgfSwKICAgICAgICAgICAgICAgICAgICAgInRoZW4iIDogewogICAgICAgICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiNzg6MTctODE6MTciLAogICAgICAgICAgICAgICAgICAgICAgICAibG93Q2xvc2VkIiA6IGZhbHNlLAogICAgICAgICAgICAgICAgICAgICAgICAiaGlnaENsb3NlZCIgOiB0cnVlLAogICAgICAgICAgICAgICAgICAgICAgICAidHlwZSIgOiAiSW50ZXJ2YWwiLAogICAgICAgICAgICAgICAgICAgICAgICAibG93IiA6IHsKICAgICAgICAgICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjc5OjIxLTc5OjU4IiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgIm5hbWUiIDogIlRvUXVhbnRpdHlJZ25vcmluZ0NvbXBhcmF0b3IiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAidHlwZSIgOiAiRnVuY3Rpb25SZWYiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAib3BlcmFuZCIgOiBbIHsKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjc5OjUwLTc5OjU3IiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIm5hbWUiIDogInF1YW50aXR5IiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk9wZXJhbmRSZWYiCiAgICAgICAgICAgICAgICAgICAgICAgICAgIH0gXQogICAgICAgICAgICAgICAgICAgICAgICB9LAogICAgICAgICAgICAgICAgICAgICAgICAiaGlnaCIgOiB7CiAgICAgICAgICAgICAgICAgICAgICAgICAgICJhc1R5cGUiIDogInt1cm46aGw3LW9yZzplbG0tdHlwZXM6cjF9UXVhbnRpdHkiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAidHlwZSIgOiAiQXMiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAib3BlcmFuZCIgOiB7CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICI4MDoyMS04MDoyNCIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJOdWxsIgogICAgICAgICAgICAgICAgICAgICAgICAgICB9CiAgICAgICAgICAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgICAgICAgICAgfQogICAgICAgICAgICAgICAgICB9IF0sCiAgICAgICAgICAgICAgICAgICJlbHNlIiA6IHsKICAgICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjgzOjE3LTgzOjY4IiwKICAgICAgICAgICAgICAgICAgICAgImxvd0Nsb3NlZCIgOiB0cnVlLAogICAgICAgICAgICAgICAgICAgICAiaGlnaENsb3NlZCIgOiB0cnVlLAogICAgICAgICAgICAgICAgICAgICAidHlwZSIgOiAiSW50ZXJ2YWwiLAogICAgICAgICAgICAgICAgICAgICAibG93IiA6IHsKICAgICAgICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjgzOjI2LTgzOjQ1IiwKICAgICAgICAgICAgICAgICAgICAgICAgIm5hbWUiIDogIlRvUXVhbnRpdHkiLAogICAgICAgICAgICAgICAgICAgICAgICAidHlwZSIgOiAiRnVuY3Rpb25SZWYiLAogICAgICAgICAgICAgICAgICAgICAgICAib3BlcmFuZCIgOiBbIHsKICAgICAgICAgICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjgzOjM3LTgzOjQ0IiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgIm5hbWUiIDogInF1YW50aXR5IiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk9wZXJhbmRSZWYiCiAgICAgICAgICAgICAgICAgICAgICAgIH0gXQogICAgICAgICAgICAgICAgICAgICB9LAogICAgICAgICAgICAgICAgICAgICAiaGlnaCIgOiB7CiAgICAgICAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICI4Mzo0OC04Mzo2NyIsCiAgICAgICAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJUb1F1YW50aXR5IiwKICAgICAgICAgICAgICAgICAgICAgICAgInR5cGUiIDogIkZ1bmN0aW9uUmVmIiwKICAgICAgICAgICAgICAgICAgICAgICAgIm9wZXJhbmQiIDogWyB7CiAgICAgICAgICAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICI4Mzo1OS04Mzo2NiIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJxdWFudGl0eSIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJPcGVyYW5kUmVmIgogICAgICAgICAgICAgICAgICAgICAgICB9IF0KICAgICAgICAgICAgICAgICAgICAgfQogICAgICAgICAgICAgICAgICB9CiAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgfSwKICAgICAgICAgICAgIm9wZXJhbmQiIDogWyB7CiAgICAgICAgICAgICAgICJuYW1lIiA6ICJxdWFudGl0eSIsCiAgICAgICAgICAgICAgICJvcGVyYW5kVHlwZVNwZWNpZmllciIgOiB7CiAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICI1OTozNy01OTo0OSIsCiAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJ7aHR0cDovL2hsNy5vcmcvZmhpcn1RdWFudGl0eSIsCiAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJOYW1lZFR5cGVTcGVjaWZpZXIiCiAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgfSBdCiAgICAgICAgIH0sIHsKICAgICAgICAgICAgImxvY2F0b3IiIDogIjg2OjEtOTA6MTA3IiwKICAgICAgICAgICAgIm5hbWUiIDogIlRvUmF0aW8iLAogICAgICAgICAgICAiY29udGV4dCIgOiAiUGF0aWVudCIsCiAgICAgICAgICAgICJhY2Nlc3NMZXZlbCIgOiAiUHVibGljIiwKICAgICAgICAgICAgInR5cGUiIDogIkZ1bmN0aW9uRGVmIiwKICAgICAgICAgICAgImV4cHJlc3Npb24iIDogewogICAgICAgICAgICAgICAibG9jYXRvciIgOiAiODc6NS05MDoxMDciLAogICAgICAgICAgICAgICAidHlwZSIgOiAiSWYiLAogICAgICAgICAgICAgICAiY29uZGl0aW9uIiA6IHsKICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjg3OjgtODc6MjAiLAogICAgICAgICAgICAgICAgICAidHlwZSIgOiAiSXNOdWxsIiwKICAgICAgICAgICAgICAgICAgIm9wZXJhbmQiIDogewogICAgICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiODc6OC04NzoxMiIsCiAgICAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJyYXRpbyIsCiAgICAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJPcGVyYW5kUmVmIgogICAgICAgICAgICAgICAgICB9CiAgICAgICAgICAgICAgIH0sCiAgICAgICAgICAgICAgICJ0aGVuIiA6IHsKICAgICAgICAgICAgICAgICAgImFzVHlwZSIgOiAie3VybjpobDctb3JnOmVsbS10eXBlczpyMX1SYXRpbyIsCiAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJBcyIsCiAgICAgICAgICAgICAgICAgICJvcGVyYW5kIiA6IHsKICAgICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjg4OjktODg6MTIiLAogICAgICAgICAgICAgICAgICAgICAidHlwZSIgOiAiTnVsbCIKICAgICAgICAgICAgICAgICAgfQogICAgICAgICAgICAgICB9LAogICAgICAgICAgICAgICAiZWxzZSIgOiB7CiAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICI5MDo5LTkwOjEwNyIsCiAgICAgICAgICAgICAgICAgICJjbGFzc1R5cGUiIDogInt1cm46aGw3LW9yZzplbG0tdHlwZXM6cjF9UmF0aW8iLAogICAgICAgICAgICAgICAgICAidHlwZSIgOiAiSW5zdGFuY2UiLAogICAgICAgICAgICAgICAgICAiZWxlbWVudCIgOiBbIHsKICAgICAgICAgICAgICAgICAgICAgIm5hbWUiIDogIm51bWVyYXRvciIsCiAgICAgICAgICAgICAgICAgICAgICJ2YWx1ZSIgOiB7CiAgICAgICAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICI5MDozNS05MDo2MSIsCiAgICAgICAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJUb1F1YW50aXR5IiwKICAgICAgICAgICAgICAgICAgICAgICAgInR5cGUiIDogIkZ1bmN0aW9uUmVmIiwKICAgICAgICAgICAgICAgICAgICAgICAgIm9wZXJhbmQiIDogWyB7CiAgICAgICAgICAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICI5MDo0Ni05MDo2MCIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICJwYXRoIiA6ICJudW1lcmF0b3IiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAidHlwZSIgOiAiUHJvcGVydHkiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAic291cmNlIiA6IHsKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjkwOjQ2LTkwOjUwIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIm5hbWUiIDogInJhdGlvIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk9wZXJhbmRSZWYiCiAgICAgICAgICAgICAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgICAgICAgICAgICAgfSBdCiAgICAgICAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgICAgICAgfSwgewogICAgICAgICAgICAgICAgICAgICAibmFtZSIgOiAiZGVub21pbmF0b3IiLAogICAgICAgICAgICAgICAgICAgICAidmFsdWUiIDogewogICAgICAgICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiOTA6NzctOTA6MTA1IiwKICAgICAgICAgICAgICAgICAgICAgICAgIm5hbWUiIDogIlRvUXVhbnRpdHkiLAogICAgICAgICAgICAgICAgICAgICAgICAidHlwZSIgOiAiRnVuY3Rpb25SZWYiLAogICAgICAgICAgICAgICAgICAgICAgICAib3BlcmFuZCIgOiBbIHsKICAgICAgICAgICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjkwOjg4LTkwOjEwNCIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICJwYXRoIiA6ICJkZW5vbWluYXRvciIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJQcm9wZXJ0eSIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICJzb3VyY2UiIDogewogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiOTA6ODgtOTA6OTIiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAibmFtZSIgOiAicmF0aW8iLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAidHlwZSIgOiAiT3BlcmFuZFJlZiIKICAgICAgICAgICAgICAgICAgICAgICAgICAgfQogICAgICAgICAgICAgICAgICAgICAgICB9IF0KICAgICAgICAgICAgICAgICAgICAgfQogICAgICAgICAgICAgICAgICB9IF0KICAgICAgICAgICAgICAgfQogICAgICAgICAgICB9LAogICAgICAgICAgICAib3BlcmFuZCIgOiBbIHsKICAgICAgICAgICAgICAgIm5hbWUiIDogInJhdGlvIiwKICAgICAgICAgICAgICAgIm9wZXJhbmRUeXBlU3BlY2lmaWVyIiA6IHsKICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjg2OjMxLTg2OjQwIiwKICAgICAgICAgICAgICAgICAgIm5hbWUiIDogIntodHRwOi8vaGw3Lm9yZy9maGlyfVJhdGlvIiwKICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk5hbWVkVHlwZVNwZWNpZmllciIKICAgICAgICAgICAgICAgfQogICAgICAgICAgICB9IF0KICAgICAgICAgfSwgewogICAgICAgICAgICAibG9jYXRvciIgOiAiOTI6MS05Njo2MyIsCiAgICAgICAgICAgICJuYW1lIiA6ICJUb0ludGVydmFsIiwKICAgICAgICAgICAgImNvbnRleHQiIDogIlBhdGllbnQiLAogICAgICAgICAgICAiYWNjZXNzTGV2ZWwiIDogIlB1YmxpYyIsCiAgICAgICAgICAgICJ0eXBlIiA6ICJGdW5jdGlvbkRlZiIsCiAgICAgICAgICAgICJleHByZXNzaW9uIiA6IHsKICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjkzOjUtOTY6NjMiLAogICAgICAgICAgICAgICAidHlwZSIgOiAiSWYiLAogICAgICAgICAgICAgICAiY29uZGl0aW9uIiA6IHsKICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjkzOjgtOTM6MjAiLAogICAgICAgICAgICAgICAgICAidHlwZSIgOiAiSXNOdWxsIiwKICAgICAgICAgICAgICAgICAgIm9wZXJhbmQiIDogewogICAgICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiOTM6OC05MzoxMiIsCiAgICAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJyYW5nZSIsCiAgICAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJPcGVyYW5kUmVmIgogICAgICAgICAgICAgICAgICB9CiAgICAgICAgICAgICAgIH0sCiAgICAgICAgICAgICAgICJ0aGVuIiA6IHsKICAgICAgICAgICAgICAgICAgInR5cGUiIDogIkFzIiwKICAgICAgICAgICAgICAgICAgIm9wZXJhbmQiIDogewogICAgICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiOTQ6OS05NDoxMiIsCiAgICAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJOdWxsIgogICAgICAgICAgICAgICAgICB9LAogICAgICAgICAgICAgICAgICAiYXNUeXBlU3BlY2lmaWVyIiA6IHsKICAgICAgICAgICAgICAgICAgICAgInR5cGUiIDogIkludGVydmFsVHlwZVNwZWNpZmllciIsCiAgICAgICAgICAgICAgICAgICAgICJwb2ludFR5cGUiIDogewogICAgICAgICAgICAgICAgICAgICAgICAibmFtZSIgOiAie3VybjpobDctb3JnOmVsbS10eXBlczpyMX1RdWFudGl0eSIsCiAgICAgICAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJOYW1lZFR5cGVTcGVjaWZpZXIiCiAgICAgICAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgICAgICAgfQogICAgICAgICAgICAgICB9LAogICAgICAgICAgICAgICAiZWxzZSIgOiB7CiAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICI5Njo5LTk2OjYzIiwKICAgICAgICAgICAgICAgICAgImxvd0Nsb3NlZCIgOiB0cnVlLAogICAgICAgICAgICAgICAgICAiaGlnaENsb3NlZCIgOiB0cnVlLAogICAgICAgICAgICAgICAgICAidHlwZSIgOiAiSW50ZXJ2YWwiLAogICAgICAgICAgICAgICAgICAibG93IiA6IHsKICAgICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjk2OjE4LTk2OjM4IiwKICAgICAgICAgICAgICAgICAgICAgIm5hbWUiIDogIlRvUXVhbnRpdHkiLAogICAgICAgICAgICAgICAgICAgICAidHlwZSIgOiAiRnVuY3Rpb25SZWYiLAogICAgICAgICAgICAgICAgICAgICAib3BlcmFuZCIgOiBbIHsKICAgICAgICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjk2OjI5LTk2OjM3IiwKICAgICAgICAgICAgICAgICAgICAgICAgInBhdGgiIDogImxvdyIsCiAgICAgICAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJQcm9wZXJ0eSIsCiAgICAgICAgICAgICAgICAgICAgICAgICJzb3VyY2UiIDogewogICAgICAgICAgICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiOTY6MjktOTY6MzMiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAibmFtZSIgOiAicmFuZ2UiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAidHlwZSIgOiAiT3BlcmFuZFJlZiIKICAgICAgICAgICAgICAgICAgICAgICAgfQogICAgICAgICAgICAgICAgICAgICB9IF0KICAgICAgICAgICAgICAgICAgfSwKICAgICAgICAgICAgICAgICAgImhpZ2giIDogewogICAgICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiOTY6NDEtOTY6NjIiLAogICAgICAgICAgICAgICAgICAgICAibmFtZSIgOiAiVG9RdWFudGl0eSIsCiAgICAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJGdW5jdGlvblJlZiIsCiAgICAgICAgICAgICAgICAgICAgICJvcGVyYW5kIiA6IFsgewogICAgICAgICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiOTY6NTItOTY6NjEiLAogICAgICAgICAgICAgICAgICAgICAgICAicGF0aCIgOiAiaGlnaCIsCiAgICAgICAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJQcm9wZXJ0eSIsCiAgICAgICAgICAgICAgICAgICAgICAgICJzb3VyY2UiIDogewogICAgICAgICAgICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiOTY6NTItOTY6NTYiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAibmFtZSIgOiAicmFuZ2UiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAidHlwZSIgOiAiT3BlcmFuZFJlZiIKICAgICAgICAgICAgICAgICAgICAgICAgfQogICAgICAgICAgICAgICAgICAgICB9IF0KICAgICAgICAgICAgICAgICAgfQogICAgICAgICAgICAgICB9CiAgICAgICAgICAgIH0sCiAgICAgICAgICAgICJvcGVyYW5kIiA6IFsgewogICAgICAgICAgICAgICAibmFtZSIgOiAicmFuZ2UiLAogICAgICAgICAgICAgICAib3BlcmFuZFR5cGVTcGVjaWZpZXIiIDogewogICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiOTI6MzQtOTI6NDMiLAogICAgICAgICAgICAgICAgICAibmFtZSIgOiAie2h0dHA6Ly9obDcub3JnL2ZoaXJ9UmFuZ2UiLAogICAgICAgICAgICAgICAgICAidHlwZSIgOiAiTmFtZWRUeXBlU3BlY2lmaWVyIgogICAgICAgICAgICAgICB9CiAgICAgICAgICAgIH0gXQogICAgICAgICB9LCB7CiAgICAgICAgICAgICJsb2NhdG9yIiA6ICI5ODoxLTEwNzo5IiwKICAgICAgICAgICAgIm5hbWUiIDogIlRvQ29kZSIsCiAgICAgICAgICAgICJjb250ZXh0IiA6ICJQYXRpZW50IiwKICAgICAgICAgICAgImFjY2Vzc0xldmVsIiA6ICJQdWJsaWMiLAogICAgICAgICAgICAidHlwZSIgOiAiRnVuY3Rpb25EZWYiLAogICAgICAgICAgICAiZXhwcmVzc2lvbiIgOiB7CiAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICI5OTo1LTEwNzo5IiwKICAgICAgICAgICAgICAgInR5cGUiIDogIklmIiwKICAgICAgICAgICAgICAgImNvbmRpdGlvbiIgOiB7CiAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICI5OTo4LTk5OjIxIiwKICAgICAgICAgICAgICAgICAgInR5cGUiIDogIklzTnVsbCIsCiAgICAgICAgICAgICAgICAgICJvcGVyYW5kIiA6IHsKICAgICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjk5OjgtOTk6MTMiLAogICAgICAgICAgICAgICAgICAgICAibmFtZSIgOiAiY29kaW5nIiwKICAgICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk9wZXJhbmRSZWYiCiAgICAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgICAgfSwKICAgICAgICAgICAgICAgInRoZW4iIDogewogICAgICAgICAgICAgICAgICAiYXNUeXBlIiA6ICJ7dXJuOmhsNy1vcmc6ZWxtLXR5cGVzOnIxfUNvZGUiLAogICAgICAgICAgICAgICAgICAidHlwZSIgOiAiQXMiLAogICAgICAgICAgICAgICAgICAib3BlcmFuZCIgOiB7CiAgICAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIxMDA6OS0xMDA6MTIiLAogICAgICAgICAgICAgICAgICAgICAidHlwZSIgOiAiTnVsbCIKICAgICAgICAgICAgICAgICAgfQogICAgICAgICAgICAgICB9LAogICAgICAgICAgICAgICAiZWxzZSIgOiB7CiAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIxMDI6OS0xMDc6OSIsCiAgICAgICAgICAgICAgICAgICJjbGFzc1R5cGUiIDogInt1cm46aGw3LW9yZzplbG0tdHlwZXM6cjF9Q29kZSIsCiAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJJbnN0YW5jZSIsCiAgICAgICAgICAgICAgICAgICJlbGVtZW50IiA6IFsgewogICAgICAgICAgICAgICAgICAgICAibmFtZSIgOiAiY29kZSIsCiAgICAgICAgICAgICAgICAgICAgICJ2YWx1ZSIgOiB7CiAgICAgICAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIxMDM6MTctMTAzOjMzIiwKICAgICAgICAgICAgICAgICAgICAgICAgInBhdGgiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgICAgICAgICAgInR5cGUiIDogIlByb3BlcnR5IiwKICAgICAgICAgICAgICAgICAgICAgICAgInNvdXJjZSIgOiB7CiAgICAgICAgICAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIxMDM6MTctMTAzOjI3IiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgInBhdGgiIDogImNvZGUiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAidHlwZSIgOiAiUHJvcGVydHkiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAic291cmNlIiA6IHsKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjEwMzoxNy0xMDM6MjIiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAibmFtZSIgOiAiY29kaW5nIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk9wZXJhbmRSZWYiCiAgICAgICAgICAgICAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgICAgICAgICAgICAgfQogICAgICAgICAgICAgICAgICAgICB9CiAgICAgICAgICAgICAgICAgIH0sIHsKICAgICAgICAgICAgICAgICAgICAgIm5hbWUiIDogInN5c3RlbSIsCiAgICAgICAgICAgICAgICAgICAgICJ2YWx1ZSIgOiB7CiAgICAgICAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIxMDQ6MTktMTA0OjM3IiwKICAgICAgICAgICAgICAgICAgICAgICAgInBhdGgiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgICAgICAgICAgInR5cGUiIDogIlByb3BlcnR5IiwKICAgICAgICAgICAgICAgICAgICAgICAgInNvdXJjZSIgOiB7CiAgICAgICAgICAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIxMDQ6MTktMTA0OjMxIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgInBhdGgiIDogInN5c3RlbSIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJQcm9wZXJ0eSIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICJzb3VyY2UiIDogewogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMTA0OjE5LTEwNDoyNCIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJjb2RpbmciLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAidHlwZSIgOiAiT3BlcmFuZFJlZiIKICAgICAgICAgICAgICAgICAgICAgICAgICAgfQogICAgICAgICAgICAgICAgICAgICAgICB9CiAgICAgICAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgICAgICAgfSwgewogICAgICAgICAgICAgICAgICAgICAibmFtZSIgOiAidmVyc2lvbiIsCiAgICAgICAgICAgICAgICAgICAgICJ2YWx1ZSIgOiB7CiAgICAgICAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIxMDU6MjAtMTA1OjM5IiwKICAgICAgICAgICAgICAgICAgICAgICAgInBhdGgiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgICAgICAgICAgInR5cGUiIDogIlByb3BlcnR5IiwKICAgICAgICAgICAgICAgICAgICAgICAgInNvdXJjZSIgOiB7CiAgICAgICAgICAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIxMDU6MjAtMTA1OjMzIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgInBhdGgiIDogInZlcnNpb24iLAogICAgICAgICAgICAgICAgICAgICAgICAgICAidHlwZSIgOiAiUHJvcGVydHkiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAic291cmNlIiA6IHsKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjEwNToyMC0xMDU6MjUiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAibmFtZSIgOiAiY29kaW5nIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk9wZXJhbmRSZWYiCiAgICAgICAgICAgICAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgICAgICAgICAgICAgfQogICAgICAgICAgICAgICAgICAgICB9CiAgICAgICAgICAgICAgICAgIH0sIHsKICAgICAgICAgICAgICAgICAgICAgIm5hbWUiIDogImRpc3BsYXkiLAogICAgICAgICAgICAgICAgICAgICAidmFsdWUiIDogewogICAgICAgICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMTA2OjIwLTEwNjozOSIsCiAgICAgICAgICAgICAgICAgICAgICAgICJwYXRoIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJQcm9wZXJ0eSIsCiAgICAgICAgICAgICAgICAgICAgICAgICJzb3VyY2UiIDogewogICAgICAgICAgICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMTA2OjIwLTEwNjozMyIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICJwYXRoIiA6ICJkaXNwbGF5IiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgInR5cGUiIDogIlByb3BlcnR5IiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgInNvdXJjZSIgOiB7CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIxMDY6MjAtMTA2OjI1IiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIm5hbWUiIDogImNvZGluZyIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJPcGVyYW5kUmVmIgogICAgICAgICAgICAgICAgICAgICAgICAgICB9CiAgICAgICAgICAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgICAgICAgICAgfQogICAgICAgICAgICAgICAgICB9IF0KICAgICAgICAgICAgICAgfQogICAgICAgICAgICB9LAogICAgICAgICAgICAib3BlcmFuZCIgOiBbIHsKICAgICAgICAgICAgICAgIm5hbWUiIDogImNvZGluZyIsCiAgICAgICAgICAgICAgICJvcGVyYW5kVHlwZVNwZWNpZmllciIgOiB7CiAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICI5ODozMS05ODo0MSIsCiAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJ7aHR0cDovL2hsNy5vcmcvZmhpcn1Db2RpbmciLAogICAgICAgICAgICAgICAgICAidHlwZSIgOiAiTmFtZWRUeXBlU3BlY2lmaWVyIgogICAgICAgICAgICAgICB9CiAgICAgICAgICAgIH0gXQogICAgICAgICB9LCB7CiAgICAgICAgICAgICJsb2NhdG9yIiA6ICIxMDk6MS0xMTY6OSIsCiAgICAgICAgICAgICJuYW1lIiA6ICJUb0NvbmNlcHQiLAogICAgICAgICAgICAiY29udGV4dCIgOiAiUGF0aWVudCIsCiAgICAgICAgICAgICJhY2Nlc3NMZXZlbCIgOiAiUHVibGljIiwKICAgICAgICAgICAgInR5cGUiIDogIkZ1bmN0aW9uRGVmIiwKICAgICAgICAgICAgImV4cHJlc3Npb24iIDogewogICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMTEwOjUtMTE2OjkiLAogICAgICAgICAgICAgICAidHlwZSIgOiAiSWYiLAogICAgICAgICAgICAgICAiY29uZGl0aW9uIiA6IHsKICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjExMDo4LTExMDoyMiIsCiAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJJc051bGwiLAogICAgICAgICAgICAgICAgICAib3BlcmFuZCIgOiB7CiAgICAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIxMTA6OC0xMTA6MTQiLAogICAgICAgICAgICAgICAgICAgICAibmFtZSIgOiAiY29uY2VwdCIsCiAgICAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJPcGVyYW5kUmVmIgogICAgICAgICAgICAgICAgICB9CiAgICAgICAgICAgICAgIH0sCiAgICAgICAgICAgICAgICJ0aGVuIiA6IHsKICAgICAgICAgICAgICAgICAgImFzVHlwZSIgOiAie3VybjpobDctb3JnOmVsbS10eXBlczpyMX1Db25jZXB0IiwKICAgICAgICAgICAgICAgICAgInR5cGUiIDogIkFzIiwKICAgICAgICAgICAgICAgICAgIm9wZXJhbmQiIDogewogICAgICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMTExOjktMTExOjEyIiwKICAgICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk51bGwiCiAgICAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgICAgfSwKICAgICAgICAgICAgICAgImVsc2UiIDogewogICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMTEzOjktMTE2OjkiLAogICAgICAgICAgICAgICAgICAiY2xhc3NUeXBlIiA6ICJ7dXJuOmhsNy1vcmc6ZWxtLXR5cGVzOnIxfUNvbmNlcHQiLAogICAgICAgICAgICAgICAgICAidHlwZSIgOiAiSW5zdGFuY2UiLAogICAgICAgICAgICAgICAgICAiZWxlbWVudCIgOiBbIHsKICAgICAgICAgICAgICAgICAgICAgIm5hbWUiIDogImNvZGVzIiwKICAgICAgICAgICAgICAgICAgICAgInZhbHVlIiA6IHsKICAgICAgICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjExNDoyMC0xMTQ6NTIiLAogICAgICAgICAgICAgICAgICAgICAgICAidHlwZSIgOiAiUXVlcnkiLAogICAgICAgICAgICAgICAgICAgICAgICAic291cmNlIiA6IFsgewogICAgICAgICAgICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMTE0OjIwLTExNDozNSIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICJhbGlhcyIgOiAiQyIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICJleHByZXNzaW9uIiA6IHsKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjExNDoyMC0xMTQ6MzMiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAicGF0aCIgOiAiY29kaW5nIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInR5cGUiIDogIlByb3BlcnR5IiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInNvdXJjZSIgOiB7CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJjb25jZXB0IiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk9wZXJhbmRSZWYiCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgICAgICAgICAgICAgICAgfQogICAgICAgICAgICAgICAgICAgICAgICB9IF0sCiAgICAgICAgICAgICAgICAgICAgICAgICJyZWxhdGlvbnNoaXAiIDogWyBdLAogICAgICAgICAgICAgICAgICAgICAgICAicmV0dXJuIiA6IHsKICAgICAgICAgICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjExNDozNy0xMTQ6NTIiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAiZXhwcmVzc2lvbiIgOiB7CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIxMTQ6NDQtMTE0OjUyIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIm5hbWUiIDogIlRvQ29kZSIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJGdW5jdGlvblJlZiIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJvcGVyYW5kIiA6IFsgewogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMTE0OjUxIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIm5hbWUiIDogIkMiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAidHlwZSIgOiAiQWxpYXNSZWYiCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIH0gXQogICAgICAgICAgICAgICAgICAgICAgICAgICB9CiAgICAgICAgICAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgICAgICAgICAgfQogICAgICAgICAgICAgICAgICB9LCB7CiAgICAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJkaXNwbGF5IiwKICAgICAgICAgICAgICAgICAgICAgInZhbHVlIiA6IHsKICAgICAgICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjExNToyMi0xMTU6MzkiLAogICAgICAgICAgICAgICAgICAgICAgICAicGF0aCIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAgICAgICAgICAidHlwZSIgOiAiUHJvcGVydHkiLAogICAgICAgICAgICAgICAgICAgICAgICAic291cmNlIiA6IHsKICAgICAgICAgICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjExNToyMi0xMTU6MzMiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAicGF0aCIgOiAidGV4dCIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJQcm9wZXJ0eSIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICJzb3VyY2UiIDogewogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMTE1OjIyLTExNToyOCIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJjb25jZXB0IiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk9wZXJhbmRSZWYiCiAgICAgICAgICAgICAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgICAgICAgICAgICAgfQogICAgICAgICAgICAgICAgICAgICB9CiAgICAgICAgICAgICAgICAgIH0gXQogICAgICAgICAgICAgICB9CiAgICAgICAgICAgIH0sCiAgICAgICAgICAgICJvcGVyYW5kIiA6IFsgewogICAgICAgICAgICAgICAibmFtZSIgOiAiY29uY2VwdCIsCiAgICAgICAgICAgICAgICJvcGVyYW5kVHlwZVNwZWNpZmllciIgOiB7CiAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIxMDk6MzUtMTA5OjU0IiwKICAgICAgICAgICAgICAgICAgIm5hbWUiIDogIntodHRwOi8vaGw3Lm9yZy9maGlyfUNvZGVhYmxlQ29uY2VwdCIsCiAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJOYW1lZFR5cGVTcGVjaWZpZXIiCiAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgfSBdCiAgICAgICAgIH0sIHsKICAgICAgICAgICAgImxvY2F0b3IiIDogIjExODoxLTEyMjo2MCIsCiAgICAgICAgICAgICJuYW1lIiA6ICJyZWZlcmVuY2UiLAogICAgICAgICAgICAiY29udGV4dCIgOiAiUGF0aWVudCIsCiAgICAgICAgICAgICJhY2Nlc3NMZXZlbCIgOiAiUHVibGljIiwKICAgICAgICAgICAgInR5cGUiIDogIkZ1bmN0aW9uRGVmIiwKICAgICAgICAgICAgImV4cHJlc3Npb24iIDogewogICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMTE5OjUtMTIyOjYwIiwKICAgICAgICAgICAgICAgInR5cGUiIDogIklmIiwKICAgICAgICAgICAgICAgImNvbmRpdGlvbiIgOiB7CiAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIxMTk6OC0xMTk6MjQiLAogICAgICAgICAgICAgICAgICAidHlwZSIgOiAiSXNOdWxsIiwKICAgICAgICAgICAgICAgICAgIm9wZXJhbmQiIDogewogICAgICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMTE5OjgtMTE5OjE2IiwKICAgICAgICAgICAgICAgICAgICAgIm5hbWUiIDogInJlZmVyZW5jZSIsCiAgICAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJPcGVyYW5kUmVmIgogICAgICAgICAgICAgICAgICB9CiAgICAgICAgICAgICAgIH0sCiAgICAgICAgICAgICAgICJ0aGVuIiA6IHsKICAgICAgICAgICAgICAgICAgImFzVHlwZSIgOiAie2h0dHA6Ly9obDcub3JnL2ZoaXJ9UmVmZXJlbmNlIiwKICAgICAgICAgICAgICAgICAgInR5cGUiIDogIkFzIiwKICAgICAgICAgICAgICAgICAgIm9wZXJhbmQiIDogewogICAgICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMTIwOjktMTIwOjEyIiwKICAgICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk51bGwiCiAgICAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgICAgfSwKICAgICAgICAgICAgICAgImVsc2UiIDogewogICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMTIyOjktMTIyOjYwIiwKICAgICAgICAgICAgICAgICAgImNsYXNzVHlwZSIgOiAie2h0dHA6Ly9obDcub3JnL2ZoaXJ9UmVmZXJlbmNlIiwKICAgICAgICAgICAgICAgICAgInR5cGUiIDogIkluc3RhbmNlIiwKICAgICAgICAgICAgICAgICAgImVsZW1lbnQiIDogWyB7CiAgICAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJyZWZlcmVuY2UiLAogICAgICAgICAgICAgICAgICAgICAidmFsdWUiIDogewogICAgICAgICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMTIyOjMyLTEyMjo1OCIsCiAgICAgICAgICAgICAgICAgICAgICAgICJjbGFzc1R5cGUiIDogIntodHRwOi8vaGw3Lm9yZy9maGlyfXN0cmluZyIsCiAgICAgICAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJJbnN0YW5jZSIsCiAgICAgICAgICAgICAgICAgICAgICAgICJlbGVtZW50IiA6IFsgewogICAgICAgICAgICAgICAgICAgICAgICAgICAibmFtZSIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAidmFsdWUiIDogewogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMTIyOjQ4LTEyMjo1NiIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJyZWZlcmVuY2UiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAidHlwZSIgOiAiT3BlcmFuZFJlZiIKICAgICAgICAgICAgICAgICAgICAgICAgICAgfQogICAgICAgICAgICAgICAgICAgICAgICB9IF0KICAgICAgICAgICAgICAgICAgICAgfQogICAgICAgICAgICAgICAgICB9IF0KICAgICAgICAgICAgICAgfQogICAgICAgICAgICB9LAogICAgICAgICAgICAib3BlcmFuZCIgOiBbIHsKICAgICAgICAgICAgICAgIm5hbWUiIDogInJlZmVyZW5jZSIsCiAgICAgICAgICAgICAgICJvcGVyYW5kVHlwZVNwZWNpZmllciIgOiB7CiAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIxMTg6MzctMTE4OjQyIiwKICAgICAgICAgICAgICAgICAgIm5hbWUiIDogInt1cm46aGw3LW9yZzplbG0tdHlwZXM6cjF9U3RyaW5nIiwKICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk5hbWVkVHlwZVNwZWNpZmllciIKICAgICAgICAgICAgICAgfQogICAgICAgICAgICB9IF0KICAgICAgICAgfSwgewogICAgICAgICAgICAibG9jYXRvciIgOiAiMTI0OjEtMTI0OjY4IiwKICAgICAgICAgICAgIm5hbWUiIDogInJlc29sdmUiLAogICAgICAgICAgICAiY29udGV4dCIgOiAiUGF0aWVudCIsCiAgICAgICAgICAgICJhY2Nlc3NMZXZlbCIgOiAiUHVibGljIiwKICAgICAgICAgICAgImV4dGVybmFsIiA6IHRydWUsCiAgICAgICAgICAgICJ0eXBlIiA6ICJGdW5jdGlvbkRlZiIsCiAgICAgICAgICAgICJvcGVyYW5kIiA6IFsgewogICAgICAgICAgICAgICAibmFtZSIgOiAicmVmZXJlbmNlIiwKICAgICAgICAgICAgICAgIm9wZXJhbmRUeXBlU3BlY2lmaWVyIiA6IHsKICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjEyNDozNS0xMjQ6NDAiLAogICAgICAgICAgICAgICAgICAibmFtZSIgOiAie3VybjpobDctb3JnOmVsbS10eXBlczpyMX1TdHJpbmciLAogICAgICAgICAgICAgICAgICAidHlwZSIgOiAiTmFtZWRUeXBlU3BlY2lmaWVyIgogICAgICAgICAgICAgICB9CiAgICAgICAgICAgIH0gXQogICAgICAgICB9LCB7CiAgICAgICAgICAgICJsb2NhdG9yIiA6ICIxMjU6MS0xMjU6NzEiLAogICAgICAgICAgICAibmFtZSIgOiAicmVzb2x2ZSIsCiAgICAgICAgICAgICJjb250ZXh0IiA6ICJQYXRpZW50IiwKICAgICAgICAgICAgImFjY2Vzc0xldmVsIiA6ICJQdWJsaWMiLAogICAgICAgICAgICAiZXh0ZXJuYWwiIDogdHJ1ZSwKICAgICAgICAgICAgInR5cGUiIDogIkZ1bmN0aW9uRGVmIiwKICAgICAgICAgICAgIm9wZXJhbmQiIDogWyB7CiAgICAgICAgICAgICAgICJuYW1lIiA6ICJyZWZlcmVuY2UiLAogICAgICAgICAgICAgICAib3BlcmFuZFR5cGVTcGVjaWZpZXIiIDogewogICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMTI1OjM1LTEyNTo0MyIsCiAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJ7aHR0cDovL2hsNy5vcmcvZmhpcn1SZWZlcmVuY2UiLAogICAgICAgICAgICAgICAgICAidHlwZSIgOiAiTmFtZWRUeXBlU3BlY2lmaWVyIgogICAgICAgICAgICAgICB9CiAgICAgICAgICAgIH0gXQogICAgICAgICB9LCB7CiAgICAgICAgICAgICJsb2NhdG9yIiA6ICIxMjY6MS0xMjY6NzIiLAogICAgICAgICAgICAibmFtZSIgOiAicmVmZXJlbmNlIiwKICAgICAgICAgICAgImNvbnRleHQiIDogIlBhdGllbnQiLAogICAgICAgICAgICAiYWNjZXNzTGV2ZWwiIDogIlB1YmxpYyIsCiAgICAgICAgICAgICJleHRlcm5hbCIgOiB0cnVlLAogICAgICAgICAgICAidHlwZSIgOiAiRnVuY3Rpb25EZWYiLAogICAgICAgICAgICAib3BlcmFuZCIgOiBbIHsKICAgICAgICAgICAgICAgIm5hbWUiIDogInJlc291cmNlIiwKICAgICAgICAgICAgICAgIm9wZXJhbmRUeXBlU3BlY2lmaWVyIiA6IHsKICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjEyNjozNi0xMjY6NDMiLAogICAgICAgICAgICAgICAgICAibmFtZSIgOiAie2h0dHA6Ly9obDcub3JnL2ZoaXJ9UmVzb3VyY2UiLAogICAgICAgICAgICAgICAgICAidHlwZSIgOiAiTmFtZWRUeXBlU3BlY2lmaWVyIgogICAgICAgICAgICAgICB9CiAgICAgICAgICAgIH0gXQogICAgICAgICB9LCB7CiAgICAgICAgICAgICJsb2NhdG9yIiA6ICIxMjc6MS0xMjc6ODYiLAogICAgICAgICAgICAibmFtZSIgOiAiZXh0ZW5zaW9uIiwKICAgICAgICAgICAgImNvbnRleHQiIDogIlBhdGllbnQiLAogICAgICAgICAgICAiYWNjZXNzTGV2ZWwiIDogIlB1YmxpYyIsCiAgICAgICAgICAgICJleHRlcm5hbCIgOiB0cnVlLAogICAgICAgICAgICAidHlwZSIgOiAiRnVuY3Rpb25EZWYiLAogICAgICAgICAgICAib3BlcmFuZCIgOiBbIHsKICAgICAgICAgICAgICAgIm5hbWUiIDogImVsZW1lbnQiLAogICAgICAgICAgICAgICAib3BlcmFuZFR5cGVTcGVjaWZpZXIiIDogewogICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMTI3OjM1LTEyNzo0MSIsCiAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJ7aHR0cDovL2hsNy5vcmcvZmhpcn1FbGVtZW50IiwKICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk5hbWVkVHlwZVNwZWNpZmllciIKICAgICAgICAgICAgICAgfQogICAgICAgICAgICB9LCB7CiAgICAgICAgICAgICAgICJuYW1lIiA6ICJ1cmwiLAogICAgICAgICAgICAgICAib3BlcmFuZFR5cGVTcGVjaWZpZXIiIDogewogICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMTI3OjQ4LTEyNzo1MyIsCiAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJ7dXJuOmhsNy1vcmc6ZWxtLXR5cGVzOnIxfVN0cmluZyIsCiAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJOYW1lZFR5cGVTcGVjaWZpZXIiCiAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgfSBdCiAgICAgICAgIH0sIHsKICAgICAgICAgICAgImxvY2F0b3IiIDogIjEyODoxLTEyODo4OCIsCiAgICAgICAgICAgICJuYW1lIiA6ICJleHRlbnNpb24iLAogICAgICAgICAgICAiY29udGV4dCIgOiAiUGF0aWVudCIsCiAgICAgICAgICAgICJhY2Nlc3NMZXZlbCIgOiAiUHVibGljIiwKICAgICAgICAgICAgImV4dGVybmFsIiA6IHRydWUsCiAgICAgICAgICAgICJ0eXBlIiA6ICJGdW5jdGlvbkRlZiIsCiAgICAgICAgICAgICJvcGVyYW5kIiA6IFsgewogICAgICAgICAgICAgICAibmFtZSIgOiAicmVzb3VyY2UiLAogICAgICAgICAgICAgICAib3BlcmFuZFR5cGVTcGVjaWZpZXIiIDogewogICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMTI4OjM2LTEyODo0MyIsCiAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJ7aHR0cDovL2hsNy5vcmcvZmhpcn1SZXNvdXJjZSIsCiAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJOYW1lZFR5cGVTcGVjaWZpZXIiCiAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgfSwgewogICAgICAgICAgICAgICAibmFtZSIgOiAidXJsIiwKICAgICAgICAgICAgICAgIm9wZXJhbmRUeXBlU3BlY2lmaWVyIiA6IHsKICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjEyODo1MC0xMjg6NTUiLAogICAgICAgICAgICAgICAgICAibmFtZSIgOiAie3VybjpobDctb3JnOmVsbS10eXBlczpyMX1TdHJpbmciLAogICAgICAgICAgICAgICAgICAidHlwZSIgOiAiTmFtZWRUeXBlU3BlY2lmaWVyIgogICAgICAgICAgICAgICB9CiAgICAgICAgICAgIH0gXQogICAgICAgICB9LCB7CiAgICAgICAgICAgICJsb2NhdG9yIiA6ICIxMjk6MS0xMjk6NjciLAogICAgICAgICAgICAibmFtZSIgOiAiaGFzVmFsdWUiLAogICAgICAgICAgICAiY29udGV4dCIgOiAiUGF0aWVudCIsCiAgICAgICAgICAgICJhY2Nlc3NMZXZlbCIgOiAiUHVibGljIiwKICAgICAgICAgICAgImV4dGVybmFsIiA6IHRydWUsCiAgICAgICAgICAgICJ0eXBlIiA6ICJGdW5jdGlvbkRlZiIsCiAgICAgICAgICAgICJvcGVyYW5kIiA6IFsgewogICAgICAgICAgICAgICAibmFtZSIgOiAiZWxlbWVudCIsCiAgICAgICAgICAgICAgICJvcGVyYW5kVHlwZVNwZWNpZmllciIgOiB7CiAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIxMjk6MzQtMTI5OjQwIiwKICAgICAgICAgICAgICAgICAgIm5hbWUiIDogIntodHRwOi8vaGw3Lm9yZy9maGlyfUVsZW1lbnQiLAogICAgICAgICAgICAgICAgICAidHlwZSIgOiAiTmFtZWRUeXBlU3BlY2lmaWVyIgogICAgICAgICAgICAgICB9CiAgICAgICAgICAgIH0gXQogICAgICAgICB9LCB7CiAgICAgICAgICAgICJsb2NhdG9yIiA6ICIxMzA6MS0xMzA6NjMiLAogICAgICAgICAgICAibmFtZSIgOiAiZ2V0VmFsdWUiLAogICAgICAgICAgICAiY29udGV4dCIgOiAiUGF0aWVudCIsCiAgICAgICAgICAgICJhY2Nlc3NMZXZlbCIgOiAiUHVibGljIiwKICAgICAgICAgICAgImV4dGVybmFsIiA6IHRydWUsCiAgICAgICAgICAgICJ0eXBlIiA6ICJGdW5jdGlvbkRlZiIsCiAgICAgICAgICAgICJvcGVyYW5kIiA6IFsgewogICAgICAgICAgICAgICAibmFtZSIgOiAiZWxlbWVudCIsCiAgICAgICAgICAgICAgICJvcGVyYW5kVHlwZVNwZWNpZmllciIgOiB7CiAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIxMzA6MzQtMTMwOjQwIiwKICAgICAgICAgICAgICAgICAgIm5hbWUiIDogIntodHRwOi8vaGw3Lm9yZy9maGlyfUVsZW1lbnQiLAogICAgICAgICAgICAgICAgICAidHlwZSIgOiAiTmFtZWRUeXBlU3BlY2lmaWVyIgogICAgICAgICAgICAgICB9CiAgICAgICAgICAgIH0gXQogICAgICAgICB9LCB7CiAgICAgICAgICAgICJsb2NhdG9yIiA6ICIxMzE6MS0xMzE6NjkiLAogICAgICAgICAgICAibmFtZSIgOiAib2ZUeXBlIiwKICAgICAgICAgICAgImNvbnRleHQiIDogIlBhdGllbnQiLAogICAgICAgICAgICAiYWNjZXNzTGV2ZWwiIDogIlB1YmxpYyIsCiAgICAgICAgICAgICJleHRlcm5hbCIgOiB0cnVlLAogICAgICAgICAgICAidHlwZSIgOiAiRnVuY3Rpb25EZWYiLAogICAgICAgICAgICAib3BlcmFuZCIgOiBbIHsKICAgICAgICAgICAgICAgIm5hbWUiIDogImlkZW50aWZpZXIiLAogICAgICAgICAgICAgICAib3BlcmFuZFR5cGVTcGVjaWZpZXIiIDogewogICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMTMxOjM1LTEzMTo0MCIsCiAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJ7dXJuOmhsNy1vcmc6ZWxtLXR5cGVzOnIxfVN0cmluZyIsCiAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJOYW1lZFR5cGVTcGVjaWZpZXIiCiAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgfSBdCiAgICAgICAgIH0sIHsKICAgICAgICAgICAgImxvY2F0b3IiIDogIjEzMjoxLTEzMjo2MyIsCiAgICAgICAgICAgICJuYW1lIiA6ICJpcyIsCiAgICAgICAgICAgICJjb250ZXh0IiA6ICJQYXRpZW50IiwKICAgICAgICAgICAgImFjY2Vzc0xldmVsIiA6ICJQdWJsaWMiLAogICAgICAgICAgICAiZXh0ZXJuYWwiIDogdHJ1ZSwKICAgICAgICAgICAgInR5cGUiIDogIkZ1bmN0aW9uRGVmIiwKICAgICAgICAgICAgIm9wZXJhbmQiIDogWyB7CiAgICAgICAgICAgICAgICJuYW1lIiA6ICJpZGVudGlmaWVyIiwKICAgICAgICAgICAgICAgIm9wZXJhbmRUeXBlU3BlY2lmaWVyIiA6IHsKICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjEzMjozMS0xMzI6MzYiLAogICAgICAgICAgICAgICAgICAibmFtZSIgOiAie3VybjpobDctb3JnOmVsbS10eXBlczpyMX1TdHJpbmciLAogICAgICAgICAgICAgICAgICAidHlwZSIgOiAiTmFtZWRUeXBlU3BlY2lmaWVyIgogICAgICAgICAgICAgICB9CiAgICAgICAgICAgIH0gXQogICAgICAgICB9LCB7CiAgICAgICAgICAgICJsb2NhdG9yIiA6ICIxMzM6MS0xMzM6NTkiLAogICAgICAgICAgICAibmFtZSIgOiAiYXMiLAogICAgICAgICAgICAiY29udGV4dCIgOiAiUGF0aWVudCIsCiAgICAgICAgICAgICJhY2Nlc3NMZXZlbCIgOiAiUHVibGljIiwKICAgICAgICAgICAgImV4dGVybmFsIiA6IHRydWUsCiAgICAgICAgICAgICJ0eXBlIiA6ICJGdW5jdGlvbkRlZiIsCiAgICAgICAgICAgICJvcGVyYW5kIiA6IFsgewogICAgICAgICAgICAgICAibmFtZSIgOiAiaWRlbnRpZmllciIsCiAgICAgICAgICAgICAgICJvcGVyYW5kVHlwZVNwZWNpZmllciIgOiB7CiAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIxMzM6MzEtMTMzOjM2IiwKICAgICAgICAgICAgICAgICAgIm5hbWUiIDogInt1cm46aGw3LW9yZzplbG0tdHlwZXM6cjF9U3RyaW5nIiwKICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk5hbWVkVHlwZVNwZWNpZmllciIKICAgICAgICAgICAgICAgfQogICAgICAgICAgICB9IF0KICAgICAgICAgfSwgewogICAgICAgICAgICAibG9jYXRvciIgOiAiMTM0OjEtMTM0Ojg2IiwKICAgICAgICAgICAgIm5hbWUiIDogImVsZW1lbnREZWZpbml0aW9uIiwKICAgICAgICAgICAgImNvbnRleHQiIDogIlBhdGllbnQiLAogICAgICAgICAgICAiYWNjZXNzTGV2ZWwiIDogIlB1YmxpYyIsCiAgICAgICAgICAgICJleHRlcm5hbCIgOiB0cnVlLAogICAgICAgICAgICAidHlwZSIgOiAiRnVuY3Rpb25EZWYiLAogICAgICAgICAgICAib3BlcmFuZCIgOiBbIHsKICAgICAgICAgICAgICAgIm5hbWUiIDogImVsZW1lbnQiLAogICAgICAgICAgICAgICAib3BlcmFuZFR5cGVTcGVjaWZpZXIiIDogewogICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMTM0OjQzLTEzNDo0OSIsCiAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJ7aHR0cDovL2hsNy5vcmcvZmhpcn1FbGVtZW50IiwKICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk5hbWVkVHlwZVNwZWNpZmllciIKICAgICAgICAgICAgICAgfQogICAgICAgICAgICB9IF0KICAgICAgICAgfSwgewogICAgICAgICAgICAibG9jYXRvciIgOiAiMTM1OjEtMTM1Ojk1IiwKICAgICAgICAgICAgIm5hbWUiIDogInNsaWNlIiwKICAgICAgICAgICAgImNvbnRleHQiIDogIlBhdGllbnQiLAogICAgICAgICAgICAiYWNjZXNzTGV2ZWwiIDogIlB1YmxpYyIsCiAgICAgICAgICAgICJleHRlcm5hbCIgOiB0cnVlLAogICAgICAgICAgICAidHlwZSIgOiAiRnVuY3Rpb25EZWYiLAogICAgICAgICAgICAib3BlcmFuZCIgOiBbIHsKICAgICAgICAgICAgICAgIm5hbWUiIDogImVsZW1lbnQiLAogICAgICAgICAgICAgICAib3BlcmFuZFR5cGVTcGVjaWZpZXIiIDogewogICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMTM1OjMxLTEzNTozNyIsCiAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJ7aHR0cDovL2hsNy5vcmcvZmhpcn1FbGVtZW50IiwKICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk5hbWVkVHlwZVNwZWNpZmllciIKICAgICAgICAgICAgICAgfQogICAgICAgICAgICB9LCB7CiAgICAgICAgICAgICAgICJuYW1lIiA6ICJ1cmwiLAogICAgICAgICAgICAgICAib3BlcmFuZFR5cGVTcGVjaWZpZXIiIDogewogICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMTM1OjQ0LTEzNTo0OSIsCiAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJ7dXJuOmhsNy1vcmc6ZWxtLXR5cGVzOnIxfVN0cmluZyIsCiAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJOYW1lZFR5cGVTcGVjaWZpZXIiCiAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgfSwgewogICAgICAgICAgICAgICAibmFtZSIgOiAibmFtZSIsCiAgICAgICAgICAgICAgICJvcGVyYW5kVHlwZVNwZWNpZmllciIgOiB7CiAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIxMzU6NTctMTM1OjYyIiwKICAgICAgICAgICAgICAgICAgIm5hbWUiIDogInt1cm46aGw3LW9yZzplbG0tdHlwZXM6cjF9U3RyaW5nIiwKICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk5hbWVkVHlwZVNwZWNpZmllciIKICAgICAgICAgICAgICAgfQogICAgICAgICAgICB9IF0KICAgICAgICAgfSwgewogICAgICAgICAgICAibG9jYXRvciIgOiAiMTM2OjEtMTM2Ojc2IiwKICAgICAgICAgICAgIm5hbWUiIDogImNoZWNrTW9kaWZpZXJzIiwKICAgICAgICAgICAgImNvbnRleHQiIDogIlBhdGllbnQiLAogICAgICAgICAgICAiYWNjZXNzTGV2ZWwiIDogIlB1YmxpYyIsCiAgICAgICAgICAgICJleHRlcm5hbCIgOiB0cnVlLAogICAgICAgICAgICAidHlwZSIgOiAiRnVuY3Rpb25EZWYiLAogICAgICAgICAgICAib3BlcmFuZCIgOiBbIHsKICAgICAgICAgICAgICAgIm5hbWUiIDogInJlc291cmNlIiwKICAgICAgICAgICAgICAgIm9wZXJhbmRUeXBlU3BlY2lmaWVyIiA6IHsKICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjEzNjo0MS0xMzY6NDgiLAogICAgICAgICAgICAgICAgICAibmFtZSIgOiAie2h0dHA6Ly9obDcub3JnL2ZoaXJ9UmVzb3VyY2UiLAogICAgICAgICAgICAgICAgICAidHlwZSIgOiAiTmFtZWRUeXBlU3BlY2lmaWVyIgogICAgICAgICAgICAgICB9CiAgICAgICAgICAgIH0gXQogICAgICAgICB9LCB7CiAgICAgICAgICAgICJsb2NhdG9yIiA6ICIxMzc6MS0xMzc6OTMiLAogICAgICAgICAgICAibmFtZSIgOiAiY2hlY2tNb2RpZmllcnMiLAogICAgICAgICAgICAiY29udGV4dCIgOiAiUGF0aWVudCIsCiAgICAgICAgICAgICJhY2Nlc3NMZXZlbCIgOiAiUHVibGljIiwKICAgICAgICAgICAgImV4dGVybmFsIiA6IHRydWUsCiAgICAgICAgICAgICJ0eXBlIiA6ICJGdW5jdGlvbkRlZiIsCiAgICAgICAgICAgICJvcGVyYW5kIiA6IFsgewogICAgICAgICAgICAgICAibmFtZSIgOiAicmVzb3VyY2UiLAogICAgICAgICAgICAgICAib3BlcmFuZFR5cGVTcGVjaWZpZXIiIDogewogICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMTM3OjQxLTEzNzo0OCIsCiAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJ7aHR0cDovL2hsNy5vcmcvZmhpcn1SZXNvdXJjZSIsCiAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJOYW1lZFR5cGVTcGVjaWZpZXIiCiAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgfSwgewogICAgICAgICAgICAgICAibmFtZSIgOiAibW9kaWZpZXIiLAogICAgICAgICAgICAgICAib3BlcmFuZFR5cGVTcGVjaWZpZXIiIDogewogICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMTM3OjYwLTEzNzo2NSIsCiAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJ7dXJuOmhsNy1vcmc6ZWxtLXR5cGVzOnIxfVN0cmluZyIsCiAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJOYW1lZFR5cGVTcGVjaWZpZXIiCiAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgfSBdCiAgICAgICAgIH0sIHsKICAgICAgICAgICAgImxvY2F0b3IiIDogIjEzODoxLTEzODo3MyIsCiAgICAgICAgICAgICJuYW1lIiA6ICJjaGVja01vZGlmaWVycyIsCiAgICAgICAgICAgICJjb250ZXh0IiA6ICJQYXRpZW50IiwKICAgICAgICAgICAgImFjY2Vzc0xldmVsIiA6ICJQdWJsaWMiLAogICAgICAgICAgICAiZXh0ZXJuYWwiIDogdHJ1ZSwKICAgICAgICAgICAgInR5cGUiIDogIkZ1bmN0aW9uRGVmIiwKICAgICAgICAgICAgIm9wZXJhbmQiIDogWyB7CiAgICAgICAgICAgICAgICJuYW1lIiA6ICJlbGVtZW50IiwKICAgICAgICAgICAgICAgIm9wZXJhbmRUeXBlU3BlY2lmaWVyIiA6IHsKICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjEzODo0MC0xMzg6NDYiLAogICAgICAgICAgICAgICAgICAibmFtZSIgOiAie2h0dHA6Ly9obDcub3JnL2ZoaXJ9RWxlbWVudCIsCiAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJOYW1lZFR5cGVTcGVjaWZpZXIiCiAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgfSBdCiAgICAgICAgIH0sIHsKICAgICAgICAgICAgImxvY2F0b3IiIDogIjEzOToxLTEzOTo5MCIsCiAgICAgICAgICAgICJuYW1lIiA6ICJjaGVja01vZGlmaWVycyIsCiAgICAgICAgICAgICJjb250ZXh0IiA6ICJQYXRpZW50IiwKICAgICAgICAgICAgImFjY2Vzc0xldmVsIiA6ICJQdWJsaWMiLAogICAgICAgICAgICAiZXh0ZXJuYWwiIDogdHJ1ZSwKICAgICAgICAgICAgInR5cGUiIDogIkZ1bmN0aW9uRGVmIiwKICAgICAgICAgICAgIm9wZXJhbmQiIDogWyB7CiAgICAgICAgICAgICAgICJuYW1lIiA6ICJlbGVtZW50IiwKICAgICAgICAgICAgICAgIm9wZXJhbmRUeXBlU3BlY2lmaWVyIiA6IHsKICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjEzOTo0MC0xMzk6NDYiLAogICAgICAgICAgICAgICAgICAibmFtZSIgOiAie2h0dHA6Ly9obDcub3JnL2ZoaXJ9RWxlbWVudCIsCiAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJOYW1lZFR5cGVTcGVjaWZpZXIiCiAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgfSwgewogICAgICAgICAgICAgICAibmFtZSIgOiAibW9kaWZpZXIiLAogICAgICAgICAgICAgICAib3BlcmFuZFR5cGVTcGVjaWZpZXIiIDogewogICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMTM5OjU4LTEzOTo2MyIsCiAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJ7dXJuOmhsNy1vcmc6ZWxtLXR5cGVzOnIxfVN0cmluZyIsCiAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJOYW1lZFR5cGVTcGVjaWZpZXIiCiAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgfSBdCiAgICAgICAgIH0sIHsKICAgICAgICAgICAgImxvY2F0b3IiIDogIjE0MDoxLTE0MDo4OSIsCiAgICAgICAgICAgICJuYW1lIiA6ICJjb25mb3Jtc1RvIiwKICAgICAgICAgICAgImNvbnRleHQiIDogIlBhdGllbnQiLAogICAgICAgICAgICAiYWNjZXNzTGV2ZWwiIDogIlB1YmxpYyIsCiAgICAgICAgICAgICJleHRlcm5hbCIgOiB0cnVlLAogICAgICAgICAgICAidHlwZSIgOiAiRnVuY3Rpb25EZWYiLAogICAgICAgICAgICAib3BlcmFuZCIgOiBbIHsKICAgICAgICAgICAgICAgIm5hbWUiIDogInJlc291cmNlIiwKICAgICAgICAgICAgICAgIm9wZXJhbmRUeXBlU3BlY2lmaWVyIiA6IHsKICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjE0MDozNy0xNDA6NDQiLAogICAgICAgICAgICAgICAgICAibmFtZSIgOiAie2h0dHA6Ly9obDcub3JnL2ZoaXJ9UmVzb3VyY2UiLAogICAgICAgICAgICAgICAgICAidHlwZSIgOiAiTmFtZWRUeXBlU3BlY2lmaWVyIgogICAgICAgICAgICAgICB9CiAgICAgICAgICAgIH0sIHsKICAgICAgICAgICAgICAgIm5hbWUiIDogInN0cnVjdHVyZSIsCiAgICAgICAgICAgICAgICJvcGVyYW5kVHlwZVNwZWNpZmllciIgOiB7CiAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIxNDA6NTctMTQwOjYyIiwKICAgICAgICAgICAgICAgICAgIm5hbWUiIDogInt1cm46aGw3LW9yZzplbG0tdHlwZXM6cjF9U3RyaW5nIiwKICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk5hbWVkVHlwZVNwZWNpZmllciIKICAgICAgICAgICAgICAgfQogICAgICAgICAgICB9IF0KICAgICAgICAgfSwgewogICAgICAgICAgICAibG9jYXRvciIgOiAiMTQxOjEtMTQxOjc4IiwKICAgICAgICAgICAgIm5hbWUiIDogIm1lbWJlck9mIiwKICAgICAgICAgICAgImNvbnRleHQiIDogIlBhdGllbnQiLAogICAgICAgICAgICAiYWNjZXNzTGV2ZWwiIDogIlB1YmxpYyIsCiAgICAgICAgICAgICJleHRlcm5hbCIgOiB0cnVlLAogICAgICAgICAgICAidHlwZSIgOiAiRnVuY3Rpb25EZWYiLAogICAgICAgICAgICAib3BlcmFuZCIgOiBbIHsKICAgICAgICAgICAgICAgIm5hbWUiIDogImNvZGUiLAogICAgICAgICAgICAgICAib3BlcmFuZFR5cGVTcGVjaWZpZXIiIDogewogICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMTQxOjMxLTE0MTozNCIsCiAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJ7aHR0cDovL2hsNy5vcmcvZmhpcn1jb2RlIiwKICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk5hbWVkVHlwZVNwZWNpZmllciIKICAgICAgICAgICAgICAgfQogICAgICAgICAgICB9LCB7CiAgICAgICAgICAgICAgICJuYW1lIiA6ICJ2YWx1ZVNldCIsCiAgICAgICAgICAgICAgICJvcGVyYW5kVHlwZVNwZWNpZmllciIgOiB7CiAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIxNDE6NDYtMTQxOjUxIiwKICAgICAgICAgICAgICAgICAgIm5hbWUiIDogInt1cm46aGw3LW9yZzplbG0tdHlwZXM6cjF9U3RyaW5nIiwKICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk5hbWVkVHlwZVNwZWNpZmllciIKICAgICAgICAgICAgICAgfQogICAgICAgICAgICB9IF0KICAgICAgICAgfSwgewogICAgICAgICAgICAibG9jYXRvciIgOiAiMTQyOjEtMTQyOjgyIiwKICAgICAgICAgICAgIm5hbWUiIDogIm1lbWJlck9mIiwKICAgICAgICAgICAgImNvbnRleHQiIDogIlBhdGllbnQiLAogICAgICAgICAgICAiYWNjZXNzTGV2ZWwiIDogIlB1YmxpYyIsCiAgICAgICAgICAgICJleHRlcm5hbCIgOiB0cnVlLAogICAgICAgICAgICAidHlwZSIgOiAiRnVuY3Rpb25EZWYiLAogICAgICAgICAgICAib3BlcmFuZCIgOiBbIHsKICAgICAgICAgICAgICAgIm5hbWUiIDogImNvZGluZyIsCiAgICAgICAgICAgICAgICJvcGVyYW5kVHlwZVNwZWNpZmllciIgOiB7CiAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIxNDI6MzMtMTQyOjM4IiwKICAgICAgICAgICAgICAgICAgIm5hbWUiIDogIntodHRwOi8vaGw3Lm9yZy9maGlyfUNvZGluZyIsCiAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJOYW1lZFR5cGVTcGVjaWZpZXIiCiAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgfSwgewogICAgICAgICAgICAgICAibmFtZSIgOiAidmFsdWVTZXQiLAogICAgICAgICAgICAgICAib3BlcmFuZFR5cGVTcGVjaWZpZXIiIDogewogICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMTQyOjUwLTE0Mjo1NSIsCiAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJ7dXJuOmhsNy1vcmc6ZWxtLXR5cGVzOnIxfVN0cmluZyIsCiAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJOYW1lZFR5cGVTcGVjaWZpZXIiCiAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgfSBdCiAgICAgICAgIH0sIHsKICAgICAgICAgICAgImxvY2F0b3IiIDogIjE0MzoxLTE0Mzo5MiIsCiAgICAgICAgICAgICJuYW1lIiA6ICJtZW1iZXJPZiIsCiAgICAgICAgICAgICJjb250ZXh0IiA6ICJQYXRpZW50IiwKICAgICAgICAgICAgImFjY2Vzc0xldmVsIiA6ICJQdWJsaWMiLAogICAgICAgICAgICAiZXh0ZXJuYWwiIDogdHJ1ZSwKICAgICAgICAgICAgInR5cGUiIDogIkZ1bmN0aW9uRGVmIiwKICAgICAgICAgICAgIm9wZXJhbmQiIDogWyB7CiAgICAgICAgICAgICAgICJuYW1lIiA6ICJjb25jZXB0IiwKICAgICAgICAgICAgICAgIm9wZXJhbmRUeXBlU3BlY2lmaWVyIiA6IHsKICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjE0MzozNC0xNDM6NDgiLAogICAgICAgICAgICAgICAgICAibmFtZSIgOiAie2h0dHA6Ly9obDcub3JnL2ZoaXJ9Q29kZWFibGVDb25jZXB0IiwKICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk5hbWVkVHlwZVNwZWNpZmllciIKICAgICAgICAgICAgICAgfQogICAgICAgICAgICB9LCB7CiAgICAgICAgICAgICAgICJuYW1lIiA6ICJ2YWx1ZVNldCIsCiAgICAgICAgICAgICAgICJvcGVyYW5kVHlwZVNwZWNpZmllciIgOiB7CiAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIxNDM6NjAtMTQzOjY1IiwKICAgICAgICAgICAgICAgICAgIm5hbWUiIDogInt1cm46aGw3LW9yZzplbG0tdHlwZXM6cjF9U3RyaW5nIiwKICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk5hbWVkVHlwZVNwZWNpZmllciIKICAgICAgICAgICAgICAgfQogICAgICAgICAgICB9IF0KICAgICAgICAgfSwgewogICAgICAgICAgICAibG9jYXRvciIgOiAiMTQ0OjEtMTQ0Ojg4IiwKICAgICAgICAgICAgIm5hbWUiIDogInN1YnN1bWVzIiwKICAgICAgICAgICAgImNvbnRleHQiIDogIlBhdGllbnQiLAogICAgICAgICAgICAiYWNjZXNzTGV2ZWwiIDogIlB1YmxpYyIsCiAgICAgICAgICAgICJleHRlcm5hbCIgOiB0cnVlLAogICAgICAgICAgICAidHlwZSIgOiAiRnVuY3Rpb25EZWYiLAogICAgICAgICAgICAib3BlcmFuZCIgOiBbIHsKICAgICAgICAgICAgICAgIm5hbWUiIDogImNvZGluZyIsCiAgICAgICAgICAgICAgICJvcGVyYW5kVHlwZVNwZWNpZmllciIgOiB7CiAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIxNDQ6MzMtMTQ0OjM4IiwKICAgICAgICAgICAgICAgICAgIm5hbWUiIDogIntodHRwOi8vaGw3Lm9yZy9maGlyfUNvZGluZyIsCiAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJOYW1lZFR5cGVTcGVjaWZpZXIiCiAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgfSwgewogICAgICAgICAgICAgICAibmFtZSIgOiAic3Vic3VtZWRDb2RpbmciLAogICAgICAgICAgICAgICAib3BlcmFuZFR5cGVTcGVjaWZpZXIiIDogewogICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMTQ0OjU2LTE0NDo2MSIsCiAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJ7aHR0cDovL2hsNy5vcmcvZmhpcn1Db2RpbmciLAogICAgICAgICAgICAgICAgICAidHlwZSIgOiAiTmFtZWRUeXBlU3BlY2lmaWVyIgogICAgICAgICAgICAgICB9CiAgICAgICAgICAgIH0gXQogICAgICAgICB9LCB7CiAgICAgICAgICAgICJsb2NhdG9yIiA6ICIxNDU6MS0xNDU6MTA4IiwKICAgICAgICAgICAgIm5hbWUiIDogInN1YnN1bWVzIiwKICAgICAgICAgICAgImNvbnRleHQiIDogIlBhdGllbnQiLAogICAgICAgICAgICAiYWNjZXNzTGV2ZWwiIDogIlB1YmxpYyIsCiAgICAgICAgICAgICJleHRlcm5hbCIgOiB0cnVlLAogICAgICAgICAgICAidHlwZSIgOiAiRnVuY3Rpb25EZWYiLAogICAgICAgICAgICAib3BlcmFuZCIgOiBbIHsKICAgICAgICAgICAgICAgIm5hbWUiIDogImNvbmNlcHQiLAogICAgICAgICAgICAgICAib3BlcmFuZFR5cGVTcGVjaWZpZXIiIDogewogICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMTQ1OjM0LTE0NTo0OCIsCiAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJ7aHR0cDovL2hsNy5vcmcvZmhpcn1Db2RlYWJsZUNvbmNlcHQiLAogICAgICAgICAgICAgICAgICAidHlwZSIgOiAiTmFtZWRUeXBlU3BlY2lmaWVyIgogICAgICAgICAgICAgICB9CiAgICAgICAgICAgIH0sIHsKICAgICAgICAgICAgICAgIm5hbWUiIDogInN1YnN1bWVkQ29uY2VwdCIsCiAgICAgICAgICAgICAgICJvcGVyYW5kVHlwZVNwZWNpZmllciIgOiB7CiAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIxNDU6NjctMTQ1OjgxIiwKICAgICAgICAgICAgICAgICAgIm5hbWUiIDogIntodHRwOi8vaGw3Lm9yZy9maGlyfUNvZGVhYmxlQ29uY2VwdCIsCiAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJOYW1lZFR5cGVTcGVjaWZpZXIiCiAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgfSBdCiAgICAgICAgIH0sIHsKICAgICAgICAgICAgImxvY2F0b3IiIDogIjE0NjoxLTE0Njo5MSIsCiAgICAgICAgICAgICJuYW1lIiA6ICJzdWJzdW1lZEJ5IiwKICAgICAgICAgICAgImNvbnRleHQiIDogIlBhdGllbnQiLAogICAgICAgICAgICAiYWNjZXNzTGV2ZWwiIDogIlB1YmxpYyIsCiAgICAgICAgICAgICJleHRlcm5hbCIgOiB0cnVlLAogICAgICAgICAgICAidHlwZSIgOiAiRnVuY3Rpb25EZWYiLAogICAgICAgICAgICAib3BlcmFuZCIgOiBbIHsKICAgICAgICAgICAgICAgIm5hbWUiIDogImNvZGluZyIsCiAgICAgICAgICAgICAgICJvcGVyYW5kVHlwZVNwZWNpZmllciIgOiB7CiAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIxNDY6MzUtMTQ2OjQwIiwKICAgICAgICAgICAgICAgICAgIm5hbWUiIDogIntodHRwOi8vaGw3Lm9yZy9maGlyfUNvZGluZyIsCiAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJOYW1lZFR5cGVTcGVjaWZpZXIiCiAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgfSwgewogICAgICAgICAgICAgICAibmFtZSIgOiAic3Vic3VtaW5nQ29kaW5nIiwKICAgICAgICAgICAgICAgIm9wZXJhbmRUeXBlU3BlY2lmaWVyIiA6IHsKICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjE0Njo1OS0xNDY6NjQiLAogICAgICAgICAgICAgICAgICAibmFtZSIgOiAie2h0dHA6Ly9obDcub3JnL2ZoaXJ9Q29kaW5nIiwKICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk5hbWVkVHlwZVNwZWNpZmllciIKICAgICAgICAgICAgICAgfQogICAgICAgICAgICB9IF0KICAgICAgICAgfSwgewogICAgICAgICAgICAibG9jYXRvciIgOiAiMTQ3OjEtMTQ3OjExMSIsCiAgICAgICAgICAgICJuYW1lIiA6ICJzdWJzdW1lZEJ5IiwKICAgICAgICAgICAgImNvbnRleHQiIDogIlBhdGllbnQiLAogICAgICAgICAgICAiYWNjZXNzTGV2ZWwiIDogIlB1YmxpYyIsCiAgICAgICAgICAgICJleHRlcm5hbCIgOiB0cnVlLAogICAgICAgICAgICAidHlwZSIgOiAiRnVuY3Rpb25EZWYiLAogICAgICAgICAgICAib3BlcmFuZCIgOiBbIHsKICAgICAgICAgICAgICAgIm5hbWUiIDogImNvbmNlcHQiLAogICAgICAgICAgICAgICAib3BlcmFuZFR5cGVTcGVjaWZpZXIiIDogewogICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMTQ3OjM2LTE0Nzo1MCIsCiAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJ7aHR0cDovL2hsNy5vcmcvZmhpcn1Db2RlYWJsZUNvbmNlcHQiLAogICAgICAgICAgICAgICAgICAidHlwZSIgOiAiTmFtZWRUeXBlU3BlY2lmaWVyIgogICAgICAgICAgICAgICB9CiAgICAgICAgICAgIH0sIHsKICAgICAgICAgICAgICAgIm5hbWUiIDogInN1YnN1bWluZ0NvbmNlcHQiLAogICAgICAgICAgICAgICAib3BlcmFuZFR5cGVTcGVjaWZpZXIiIDogewogICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMTQ3OjcwLTE0Nzo4NCIsCiAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJ7aHR0cDovL2hsNy5vcmcvZmhpcn1Db2RlYWJsZUNvbmNlcHQiLAogICAgICAgICAgICAgICAgICAidHlwZSIgOiAiTmFtZWRUeXBlU3BlY2lmaWVyIgogICAgICAgICAgICAgICB9CiAgICAgICAgICAgIH0gXQogICAgICAgICB9LCB7CiAgICAgICAgICAgICJsb2NhdG9yIiA6ICIxNDg6MS0xNDg6NjkiLAogICAgICAgICAgICAibmFtZSIgOiAiaHRtbENoZWNrcyIsCiAgICAgICAgICAgICJjb250ZXh0IiA6ICJQYXRpZW50IiwKICAgICAgICAgICAgImFjY2Vzc0xldmVsIiA6ICJQdWJsaWMiLAogICAgICAgICAgICAiZXh0ZXJuYWwiIDogdHJ1ZSwKICAgICAgICAgICAgInR5cGUiIDogIkZ1bmN0aW9uRGVmIiwKICAgICAgICAgICAgIm9wZXJhbmQiIDogWyB7CiAgICAgICAgICAgICAgICJuYW1lIiA6ICJlbGVtZW50IiwKICAgICAgICAgICAgICAgIm9wZXJhbmRUeXBlU3BlY2lmaWVyIiA6IHsKICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjE0ODozNi0xNDg6NDIiLAogICAgICAgICAgICAgICAgICAibmFtZSIgOiAie2h0dHA6Ly9obDcub3JnL2ZoaXJ9RWxlbWVudCIsCiAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJOYW1lZFR5cGVTcGVjaWZpZXIiCiAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgfSBdCiAgICAgICAgIH0sIHsKICAgICAgICAgICAgImxvY2F0b3IiIDogIjE1MDoxLTE1MDo1OCIsCiAgICAgICAgICAgICJuYW1lIiA6ICJUb1N0cmluZyIsCiAgICAgICAgICAgICJjb250ZXh0IiA6ICJQYXRpZW50IiwKICAgICAgICAgICAgImFjY2Vzc0xldmVsIiA6ICJQdWJsaWMiLAogICAgICAgICAgICAidHlwZSIgOiAiRnVuY3Rpb25EZWYiLAogICAgICAgICAgICAiZXhwcmVzc2lvbiIgOiB7CiAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIxNTA6NDgtMTUwOjU4IiwKICAgICAgICAgICAgICAgInBhdGgiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgInR5cGUiIDogIlByb3BlcnR5IiwKICAgICAgICAgICAgICAgInNvdXJjZSIgOiB7CiAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIxNTA6NDgtMTUwOjUyIiwKICAgICAgICAgICAgICAgICAgIm5hbWUiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk9wZXJhbmRSZWYiCiAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgfSwKICAgICAgICAgICAgIm9wZXJhbmQiIDogWyB7CiAgICAgICAgICAgICAgICJuYW1lIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICJvcGVyYW5kVHlwZVNwZWNpZmllciIgOiB7CiAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIxNTA6MzItMTUwOjQ0IiwKICAgICAgICAgICAgICAgICAgIm5hbWUiIDogIntodHRwOi8vaGw3Lm9yZy9maGlyfUFjY291bnRTdGF0dXMiLAogICAgICAgICAgICAgICAgICAidHlwZSIgOiAiTmFtZWRUeXBlU3BlY2lmaWVyIgogICAgICAgICAgICAgICB9CiAgICAgICAgICAgIH0gXQogICAgICAgICB9LCB7CiAgICAgICAgICAgICJsb2NhdG9yIiA6ICIxNTE6MS0xNTE6NzAiLAogICAgICAgICAgICAibmFtZSIgOiAiVG9TdHJpbmciLAogICAgICAgICAgICAiY29udGV4dCIgOiAiUGF0aWVudCIsCiAgICAgICAgICAgICJhY2Nlc3NMZXZlbCIgOiAiUHVibGljIiwKICAgICAgICAgICAgInR5cGUiIDogIkZ1bmN0aW9uRGVmIiwKICAgICAgICAgICAgImV4cHJlc3Npb24iIDogewogICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMTUxOjYwLTE1MTo3MCIsCiAgICAgICAgICAgICAgICJwYXRoIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICJ0eXBlIiA6ICJQcm9wZXJ0eSIsCiAgICAgICAgICAgICAgICJzb3VyY2UiIDogewogICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMTUxOjYwLTE1MTo2NCIsCiAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJPcGVyYW5kUmVmIgogICAgICAgICAgICAgICB9CiAgICAgICAgICAgIH0sCiAgICAgICAgICAgICJvcGVyYW5kIiA6IFsgewogICAgICAgICAgICAgICAibmFtZSIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAib3BlcmFuZFR5cGVTcGVjaWZpZXIiIDogewogICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMTUxOjMyLTE1MTo1NiIsCiAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJ7aHR0cDovL2hsNy5vcmcvZmhpcn1BY3Rpb25DYXJkaW5hbGl0eUJlaGF2aW9yIiwKICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk5hbWVkVHlwZVNwZWNpZmllciIKICAgICAgICAgICAgICAgfQogICAgICAgICAgICB9IF0KICAgICAgICAgfSwgewogICAgICAgICAgICAibG9jYXRvciIgOiAiMTUyOjEtMTUyOjY0IiwKICAgICAgICAgICAgIm5hbWUiIDogIlRvU3RyaW5nIiwKICAgICAgICAgICAgImNvbnRleHQiIDogIlBhdGllbnQiLAogICAgICAgICAgICAiYWNjZXNzTGV2ZWwiIDogIlB1YmxpYyIsCiAgICAgICAgICAgICJ0eXBlIiA6ICJGdW5jdGlvbkRlZiIsCiAgICAgICAgICAgICJleHByZXNzaW9uIiA6IHsKICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjE1Mjo1NC0xNTI6NjQiLAogICAgICAgICAgICAgICAicGF0aCIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAidHlwZSIgOiAiUHJvcGVydHkiLAogICAgICAgICAgICAgICAic291cmNlIiA6IHsKICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjE1Mjo1NC0xNTI6NTgiLAogICAgICAgICAgICAgICAgICAibmFtZSIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAgICAidHlwZSIgOiAiT3BlcmFuZFJlZiIKICAgICAgICAgICAgICAgfQogICAgICAgICAgICB9LAogICAgICAgICAgICAib3BlcmFuZCIgOiBbIHsKICAgICAgICAgICAgICAgIm5hbWUiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgIm9wZXJhbmRUeXBlU3BlY2lmaWVyIiA6IHsKICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjE1MjozMi0xNTI6NTAiLAogICAgICAgICAgICAgICAgICAibmFtZSIgOiAie2h0dHA6Ly9obDcub3JnL2ZoaXJ9QWN0aW9uQ29uZGl0aW9uS2luZCIsCiAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJOYW1lZFR5cGVTcGVjaWZpZXIiCiAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgfSBdCiAgICAgICAgIH0sIHsKICAgICAgICAgICAgImxvY2F0b3IiIDogIjE1MzoxLTE1Mzo2NyIsCiAgICAgICAgICAgICJuYW1lIiA6ICJUb1N0cmluZyIsCiAgICAgICAgICAgICJjb250ZXh0IiA6ICJQYXRpZW50IiwKICAgICAgICAgICAgImFjY2Vzc0xldmVsIiA6ICJQdWJsaWMiLAogICAgICAgICAgICAidHlwZSIgOiAiRnVuY3Rpb25EZWYiLAogICAgICAgICAgICAiZXhwcmVzc2lvbiIgOiB7CiAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIxNTM6NTctMTUzOjY3IiwKICAgICAgICAgICAgICAgInBhdGgiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgInR5cGUiIDogIlByb3BlcnR5IiwKICAgICAgICAgICAgICAgInNvdXJjZSIgOiB7CiAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIxNTM6NTctMTUzOjYxIiwKICAgICAgICAgICAgICAgICAgIm5hbWUiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk9wZXJhbmRSZWYiCiAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgfSwKICAgICAgICAgICAgIm9wZXJhbmQiIDogWyB7CiAgICAgICAgICAgICAgICJuYW1lIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICJvcGVyYW5kVHlwZVNwZWNpZmllciIgOiB7CiAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIxNTM6MzItMTUzOjUzIiwKICAgICAgICAgICAgICAgICAgIm5hbWUiIDogIntodHRwOi8vaGw3Lm9yZy9maGlyfUFjdGlvbkdyb3VwaW5nQmVoYXZpb3IiLAogICAgICAgICAgICAgICAgICAidHlwZSIgOiAiTmFtZWRUeXBlU3BlY2lmaWVyIgogICAgICAgICAgICAgICB9CiAgICAgICAgICAgIH0gXQogICAgICAgICB9LCB7CiAgICAgICAgICAgICJsb2NhdG9yIiA6ICIxNTQ6MS0xNTQ6NjYiLAogICAgICAgICAgICAibmFtZSIgOiAiVG9TdHJpbmciLAogICAgICAgICAgICAiY29udGV4dCIgOiAiUGF0aWVudCIsCiAgICAgICAgICAgICJhY2Nlc3NMZXZlbCIgOiAiUHVibGljIiwKICAgICAgICAgICAgInR5cGUiIDogIkZ1bmN0aW9uRGVmIiwKICAgICAgICAgICAgImV4cHJlc3Npb24iIDogewogICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMTU0OjU2LTE1NDo2NiIsCiAgICAgICAgICAgICAgICJwYXRoIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICJ0eXBlIiA6ICJQcm9wZXJ0eSIsCiAgICAgICAgICAgICAgICJzb3VyY2UiIDogewogICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMTU0OjU2LTE1NDo2MCIsCiAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJPcGVyYW5kUmVmIgogICAgICAgICAgICAgICB9CiAgICAgICAgICAgIH0sCiAgICAgICAgICAgICJvcGVyYW5kIiA6IFsgewogICAgICAgICAgICAgICAibmFtZSIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAib3BlcmFuZFR5cGVTcGVjaWZpZXIiIDogewogICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMTU0OjMyLTE1NDo1MiIsCiAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJ7aHR0cDovL2hsNy5vcmcvZmhpcn1BY3Rpb25QYXJ0aWNpcGFudFR5cGUiLAogICAgICAgICAgICAgICAgICAidHlwZSIgOiAiTmFtZWRUeXBlU3BlY2lmaWVyIgogICAgICAgICAgICAgICB9CiAgICAgICAgICAgIH0gXQogICAgICAgICB9LCB7CiAgICAgICAgICAgICJsb2NhdG9yIiA6ICIxNTU6MS0xNTU6NjciLAogICAgICAgICAgICAibmFtZSIgOiAiVG9TdHJpbmciLAogICAgICAgICAgICAiY29udGV4dCIgOiAiUGF0aWVudCIsCiAgICAgICAgICAgICJhY2Nlc3NMZXZlbCIgOiAiUHVibGljIiwKICAgICAgICAgICAgInR5cGUiIDogIkZ1bmN0aW9uRGVmIiwKICAgICAgICAgICAgImV4cHJlc3Npb24iIDogewogICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMTU1OjU3LTE1NTo2NyIsCiAgICAgICAgICAgICAgICJwYXRoIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICJ0eXBlIiA6ICJQcm9wZXJ0eSIsCiAgICAgICAgICAgICAgICJzb3VyY2UiIDogewogICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMTU1OjU3LTE1NTo2MSIsCiAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJPcGVyYW5kUmVmIgogICAgICAgICAgICAgICB9CiAgICAgICAgICAgIH0sCiAgICAgICAgICAgICJvcGVyYW5kIiA6IFsgewogICAgICAgICAgICAgICAibmFtZSIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAib3BlcmFuZFR5cGVTcGVjaWZpZXIiIDogewogICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMTU1OjMyLTE1NTo1MyIsCiAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJ7aHR0cDovL2hsNy5vcmcvZmhpcn1BY3Rpb25QcmVjaGVja0JlaGF2aW9yIiwKICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk5hbWVkVHlwZVNwZWNpZmllciIKICAgICAgICAgICAgICAgfQogICAgICAgICAgICB9IF0KICAgICAgICAgfSwgewogICAgICAgICAgICAibG9jYXRvciIgOiAiMTU2OjEtMTU2OjY3IiwKICAgICAgICAgICAgIm5hbWUiIDogIlRvU3RyaW5nIiwKICAgICAgICAgICAgImNvbnRleHQiIDogIlBhdGllbnQiLAogICAgICAgICAgICAiYWNjZXNzTGV2ZWwiIDogIlB1YmxpYyIsCiAgICAgICAgICAgICJ0eXBlIiA6ICJGdW5jdGlvbkRlZiIsCiAgICAgICAgICAgICJleHByZXNzaW9uIiA6IHsKICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjE1Njo1Ny0xNTY6NjciLAogICAgICAgICAgICAgICAicGF0aCIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAidHlwZSIgOiAiUHJvcGVydHkiLAogICAgICAgICAgICAgICAic291cmNlIiA6IHsKICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjE1Njo1Ny0xNTY6NjEiLAogICAgICAgICAgICAgICAgICAibmFtZSIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAgICAidHlwZSIgOiAiT3BlcmFuZFJlZiIKICAgICAgICAgICAgICAgfQogICAgICAgICAgICB9LAogICAgICAgICAgICAib3BlcmFuZCIgOiBbIHsKICAgICAgICAgICAgICAgIm5hbWUiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgIm9wZXJhbmRUeXBlU3BlY2lmaWVyIiA6IHsKICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjE1NjozMi0xNTY6NTMiLAogICAgICAgICAgICAgICAgICAibmFtZSIgOiAie2h0dHA6Ly9obDcub3JnL2ZoaXJ9QWN0aW9uUmVsYXRpb25zaGlwVHlwZSIsCiAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJOYW1lZFR5cGVTcGVjaWZpZXIiCiAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgfSBdCiAgICAgICAgIH0sIHsKICAgICAgICAgICAgImxvY2F0b3IiIDogIjE1NzoxLTE1Nzo2NyIsCiAgICAgICAgICAgICJuYW1lIiA6ICJUb1N0cmluZyIsCiAgICAgICAgICAgICJjb250ZXh0IiA6ICJQYXRpZW50IiwKICAgICAgICAgICAgImFjY2Vzc0xldmVsIiA6ICJQdWJsaWMiLAogICAgICAgICAgICAidHlwZSIgOiAiRnVuY3Rpb25EZWYiLAogICAgICAgICAgICAiZXhwcmVzc2lvbiIgOiB7CiAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIxNTc6NTctMTU3OjY3IiwKICAgICAgICAgICAgICAgInBhdGgiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgInR5cGUiIDogIlByb3BlcnR5IiwKICAgICAgICAgICAgICAgInNvdXJjZSIgOiB7CiAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIxNTc6NTctMTU3OjYxIiwKICAgICAgICAgICAgICAgICAgIm5hbWUiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk9wZXJhbmRSZWYiCiAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgfSwKICAgICAgICAgICAgIm9wZXJhbmQiIDogWyB7CiAgICAgICAgICAgICAgICJuYW1lIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICJvcGVyYW5kVHlwZVNwZWNpZmllciIgOiB7CiAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIxNTc6MzItMTU3OjUzIiwKICAgICAgICAgICAgICAgICAgIm5hbWUiIDogIntodHRwOi8vaGw3Lm9yZy9maGlyfUFjdGlvblJlcXVpcmVkQmVoYXZpb3IiLAogICAgICAgICAgICAgICAgICAidHlwZSIgOiAiTmFtZWRUeXBlU3BlY2lmaWVyIgogICAgICAgICAgICAgICB9CiAgICAgICAgICAgIH0gXQogICAgICAgICB9LCB7CiAgICAgICAgICAgICJsb2NhdG9yIiA6ICIxNTg6MS0xNTg6NjgiLAogICAgICAgICAgICAibmFtZSIgOiAiVG9TdHJpbmciLAogICAgICAgICAgICAiY29udGV4dCIgOiAiUGF0aWVudCIsCiAgICAgICAgICAgICJhY2Nlc3NMZXZlbCIgOiAiUHVibGljIiwKICAgICAgICAgICAgInR5cGUiIDogIkZ1bmN0aW9uRGVmIiwKICAgICAgICAgICAgImV4cHJlc3Npb24iIDogewogICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMTU4OjU4LTE1ODo2OCIsCiAgICAgICAgICAgICAgICJwYXRoIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICJ0eXBlIiA6ICJQcm9wZXJ0eSIsCiAgICAgICAgICAgICAgICJzb3VyY2UiIDogewogICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMTU4OjU4LTE1ODo2MiIsCiAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJPcGVyYW5kUmVmIgogICAgICAgICAgICAgICB9CiAgICAgICAgICAgIH0sCiAgICAgICAgICAgICJvcGVyYW5kIiA6IFsgewogICAgICAgICAgICAgICAibmFtZSIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAib3BlcmFuZFR5cGVTcGVjaWZpZXIiIDogewogICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMTU4OjMyLTE1ODo1NCIsCiAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJ7aHR0cDovL2hsNy5vcmcvZmhpcn1BY3Rpb25TZWxlY3Rpb25CZWhhdmlvciIsCiAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJOYW1lZFR5cGVTcGVjaWZpZXIiCiAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgfSBdCiAgICAgICAgIH0sIHsKICAgICAgICAgICAgImxvY2F0b3IiIDogIjE1OToxLTE1OTo2NyIsCiAgICAgICAgICAgICJuYW1lIiA6ICJUb1N0cmluZyIsCiAgICAgICAgICAgICJjb250ZXh0IiA6ICJQYXRpZW50IiwKICAgICAgICAgICAgImFjY2Vzc0xldmVsIiA6ICJQdWJsaWMiLAogICAgICAgICAgICAidHlwZSIgOiAiRnVuY3Rpb25EZWYiLAogICAgICAgICAgICAiZXhwcmVzc2lvbiIgOiB7CiAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIxNTk6NTctMTU5OjY3IiwKICAgICAgICAgICAgICAgInBhdGgiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgInR5cGUiIDogIlByb3BlcnR5IiwKICAgICAgICAgICAgICAgInNvdXJjZSIgOiB7CiAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIxNTk6NTctMTU5OjYxIiwKICAgICAgICAgICAgICAgICAgIm5hbWUiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk9wZXJhbmRSZWYiCiAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgfSwKICAgICAgICAgICAgIm9wZXJhbmQiIDogWyB7CiAgICAgICAgICAgICAgICJuYW1lIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICJvcGVyYW5kVHlwZVNwZWNpZmllciIgOiB7CiAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIxNTk6MzItMTU5OjUzIiwKICAgICAgICAgICAgICAgICAgIm5hbWUiIDogIntodHRwOi8vaGw3Lm9yZy9maGlyfUFjdGl2aXR5RGVmaW5pdGlvbktpbmQiLAogICAgICAgICAgICAgICAgICAidHlwZSIgOiAiTmFtZWRUeXBlU3BlY2lmaWVyIgogICAgICAgICAgICAgICB9CiAgICAgICAgICAgIH0gXQogICAgICAgICB9LCB7CiAgICAgICAgICAgICJsb2NhdG9yIiA6ICIxNjA6MS0xNjA6NjgiLAogICAgICAgICAgICAibmFtZSIgOiAiVG9TdHJpbmciLAogICAgICAgICAgICAiY29udGV4dCIgOiAiUGF0aWVudCIsCiAgICAgICAgICAgICJhY2Nlc3NMZXZlbCIgOiAiUHVibGljIiwKICAgICAgICAgICAgInR5cGUiIDogIkZ1bmN0aW9uRGVmIiwKICAgICAgICAgICAgImV4cHJlc3Npb24iIDogewogICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMTYwOjU4LTE2MDo2OCIsCiAgICAgICAgICAgICAgICJwYXRoIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICJ0eXBlIiA6ICJQcm9wZXJ0eSIsCiAgICAgICAgICAgICAgICJzb3VyY2UiIDogewogICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMTYwOjU4LTE2MDo2MiIsCiAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJPcGVyYW5kUmVmIgogICAgICAgICAgICAgICB9CiAgICAgICAgICAgIH0sCiAgICAgICAgICAgICJvcGVyYW5kIiA6IFsgewogICAgICAgICAgICAgICAibmFtZSIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAib3BlcmFuZFR5cGVTcGVjaWZpZXIiIDogewogICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMTYwOjMyLTE2MDo1NCIsCiAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJ7aHR0cDovL2hsNy5vcmcvZmhpcn1BY3Rpdml0eVBhcnRpY2lwYW50VHlwZSIsCiAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJOYW1lZFR5cGVTcGVjaWZpZXIiCiAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgfSBdCiAgICAgICAgIH0sIHsKICAgICAgICAgICAgImxvY2F0b3IiIDogIjE2MToxLTE2MTo1NiIsCiAgICAgICAgICAgICJuYW1lIiA6ICJUb1N0cmluZyIsCiAgICAgICAgICAgICJjb250ZXh0IiA6ICJQYXRpZW50IiwKICAgICAgICAgICAgImFjY2Vzc0xldmVsIiA6ICJQdWJsaWMiLAogICAgICAgICAgICAidHlwZSIgOiAiRnVuY3Rpb25EZWYiLAogICAgICAgICAgICAiZXhwcmVzc2lvbiIgOiB7CiAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIxNjE6NDYtMTYxOjU2IiwKICAgICAgICAgICAgICAgInBhdGgiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgInR5cGUiIDogIlByb3BlcnR5IiwKICAgICAgICAgICAgICAgInNvdXJjZSIgOiB7CiAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIxNjE6NDYtMTYxOjUwIiwKICAgICAgICAgICAgICAgICAgIm5hbWUiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk9wZXJhbmRSZWYiCiAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgfSwKICAgICAgICAgICAgIm9wZXJhbmQiIDogWyB7CiAgICAgICAgICAgICAgICJuYW1lIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICJvcGVyYW5kVHlwZVNwZWNpZmllciIgOiB7CiAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIxNjE6MzItMTYxOjQyIiwKICAgICAgICAgICAgICAgICAgIm5hbWUiIDogIntodHRwOi8vaGw3Lm9yZy9maGlyfUFkZHJlc3NUeXBlIiwKICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk5hbWVkVHlwZVNwZWNpZmllciIKICAgICAgICAgICAgICAgfQogICAgICAgICAgICB9IF0KICAgICAgICAgfSwgewogICAgICAgICAgICAibG9jYXRvciIgOiAiMTYyOjEtMTYyOjU1IiwKICAgICAgICAgICAgIm5hbWUiIDogIlRvU3RyaW5nIiwKICAgICAgICAgICAgImNvbnRleHQiIDogIlBhdGllbnQiLAogICAgICAgICAgICAiYWNjZXNzTGV2ZWwiIDogIlB1YmxpYyIsCiAgICAgICAgICAgICJ0eXBlIiA6ICJGdW5jdGlvbkRlZiIsCiAgICAgICAgICAgICJleHByZXNzaW9uIiA6IHsKICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjE2Mjo0NS0xNjI6NTUiLAogICAgICAgICAgICAgICAicGF0aCIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAidHlwZSIgOiAiUHJvcGVydHkiLAogICAgICAgICAgICAgICAic291cmNlIiA6IHsKICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjE2Mjo0NS0xNjI6NDkiLAogICAgICAgICAgICAgICAgICAibmFtZSIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAgICAidHlwZSIgOiAiT3BlcmFuZFJlZiIKICAgICAgICAgICAgICAgfQogICAgICAgICAgICB9LAogICAgICAgICAgICAib3BlcmFuZCIgOiBbIHsKICAgICAgICAgICAgICAgIm5hbWUiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgIm9wZXJhbmRUeXBlU3BlY2lmaWVyIiA6IHsKICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjE2MjozMi0xNjI6NDEiLAogICAgICAgICAgICAgICAgICAibmFtZSIgOiAie2h0dHA6Ly9obDcub3JnL2ZoaXJ9QWRkcmVzc1VzZSIsCiAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJOYW1lZFR5cGVTcGVjaWZpZXIiCiAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgfSBdCiAgICAgICAgIH0sIHsKICAgICAgICAgICAgImxvY2F0b3IiIDogIjE2MzoxLTE2Mzo2NSIsCiAgICAgICAgICAgICJuYW1lIiA6ICJUb1N0cmluZyIsCiAgICAgICAgICAgICJjb250ZXh0IiA6ICJQYXRpZW50IiwKICAgICAgICAgICAgImFjY2Vzc0xldmVsIiA6ICJQdWJsaWMiLAogICAgICAgICAgICAidHlwZSIgOiAiRnVuY3Rpb25EZWYiLAogICAgICAgICAgICAiZXhwcmVzc2lvbiIgOiB7CiAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIxNjM6NTUtMTYzOjY1IiwKICAgICAgICAgICAgICAgInBhdGgiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgInR5cGUiIDogIlByb3BlcnR5IiwKICAgICAgICAgICAgICAgInNvdXJjZSIgOiB7CiAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIxNjM6NTUtMTYzOjU5IiwKICAgICAgICAgICAgICAgICAgIm5hbWUiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk9wZXJhbmRSZWYiCiAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgfSwKICAgICAgICAgICAgIm9wZXJhbmQiIDogWyB7CiAgICAgICAgICAgICAgICJuYW1lIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICJvcGVyYW5kVHlwZVNwZWNpZmllciIgOiB7CiAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIxNjM6MzItMTYzOjUxIiwKICAgICAgICAgICAgICAgICAgIm5hbWUiIDogIntodHRwOi8vaGw3Lm9yZy9maGlyfUFkbWluaXN0cmF0aXZlR2VuZGVyIiwKICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk5hbWVkVHlwZVNwZWNpZmllciIKICAgICAgICAgICAgICAgfQogICAgICAgICAgICB9IF0KICAgICAgICAgfSwgewogICAgICAgICAgICAibG9jYXRvciIgOiAiMTY0OjEtMTY0OjY2IiwKICAgICAgICAgICAgIm5hbWUiIDogIlRvU3RyaW5nIiwKICAgICAgICAgICAgImNvbnRleHQiIDogIlBhdGllbnQiLAogICAgICAgICAgICAiYWNjZXNzTGV2ZWwiIDogIlB1YmxpYyIsCiAgICAgICAgICAgICJ0eXBlIiA6ICJGdW5jdGlvbkRlZiIsCiAgICAgICAgICAgICJleHByZXNzaW9uIiA6IHsKICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjE2NDo1Ni0xNjQ6NjYiLAogICAgICAgICAgICAgICAicGF0aCIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAidHlwZSIgOiAiUHJvcGVydHkiLAogICAgICAgICAgICAgICAic291cmNlIiA6IHsKICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjE2NDo1Ni0xNjQ6NjAiLAogICAgICAgICAgICAgICAgICAibmFtZSIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAgICAidHlwZSIgOiAiT3BlcmFuZFJlZiIKICAgICAgICAgICAgICAgfQogICAgICAgICAgICB9LAogICAgICAgICAgICAib3BlcmFuZCIgOiBbIHsKICAgICAgICAgICAgICAgIm5hbWUiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgIm9wZXJhbmRUeXBlU3BlY2lmaWVyIiA6IHsKICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjE2NDozMi0xNjQ6NTIiLAogICAgICAgICAgICAgICAgICAibmFtZSIgOiAie2h0dHA6Ly9obDcub3JnL2ZoaXJ9QWR2ZXJzZUV2ZW50QWN0dWFsaXR5IiwKICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk5hbWVkVHlwZVNwZWNpZmllciIKICAgICAgICAgICAgICAgfQogICAgICAgICAgICB9IF0KICAgICAgICAgfSwgewogICAgICAgICAgICAibG9jYXRvciIgOiAiMTY1OjEtMTY1OjYwIiwKICAgICAgICAgICAgIm5hbWUiIDogIlRvU3RyaW5nIiwKICAgICAgICAgICAgImNvbnRleHQiIDogIlBhdGllbnQiLAogICAgICAgICAgICAiYWNjZXNzTGV2ZWwiIDogIlB1YmxpYyIsCiAgICAgICAgICAgICJ0eXBlIiA6ICJGdW5jdGlvbkRlZiIsCiAgICAgICAgICAgICJleHByZXNzaW9uIiA6IHsKICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjE2NTo1MC0xNjU6NjAiLAogICAgICAgICAgICAgICAicGF0aCIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAidHlwZSIgOiAiUHJvcGVydHkiLAogICAgICAgICAgICAgICAic291cmNlIiA6IHsKICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjE2NTo1MC0xNjU6NTQiLAogICAgICAgICAgICAgICAgICAibmFtZSIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAgICAidHlwZSIgOiAiT3BlcmFuZFJlZiIKICAgICAgICAgICAgICAgfQogICAgICAgICAgICB9LAogICAgICAgICAgICAib3BlcmFuZCIgOiBbIHsKICAgICAgICAgICAgICAgIm5hbWUiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgIm9wZXJhbmRUeXBlU3BlY2lmaWVyIiA6IHsKICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjE2NTozMi0xNjU6NDYiLAogICAgICAgICAgICAgICAgICAibmFtZSIgOiAie2h0dHA6Ly9obDcub3JnL2ZoaXJ9QWdncmVnYXRpb25Nb2RlIiwKICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk5hbWVkVHlwZVNwZWNpZmllciIKICAgICAgICAgICAgICAgfQogICAgICAgICAgICB9IF0KICAgICAgICAgfSwgewogICAgICAgICAgICAibG9jYXRvciIgOiAiMTY2OjEtMTY2OjcxIiwKICAgICAgICAgICAgIm5hbWUiIDogIlRvU3RyaW5nIiwKICAgICAgICAgICAgImNvbnRleHQiIDogIlBhdGllbnQiLAogICAgICAgICAgICAiYWNjZXNzTGV2ZWwiIDogIlB1YmxpYyIsCiAgICAgICAgICAgICJ0eXBlIiA6ICJGdW5jdGlvbkRlZiIsCiAgICAgICAgICAgICJleHByZXNzaW9uIiA6IHsKICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjE2Njo2MS0xNjY6NzEiLAogICAgICAgICAgICAgICAicGF0aCIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAidHlwZSIgOiAiUHJvcGVydHkiLAogICAgICAgICAgICAgICAic291cmNlIiA6IHsKICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjE2Njo2MS0xNjY6NjUiLAogICAgICAgICAgICAgICAgICAibmFtZSIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAgICAidHlwZSIgOiAiT3BlcmFuZFJlZiIKICAgICAgICAgICAgICAgfQogICAgICAgICAgICB9LAogICAgICAgICAgICAib3BlcmFuZCIgOiBbIHsKICAgICAgICAgICAgICAgIm5hbWUiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgIm9wZXJhbmRUeXBlU3BlY2lmaWVyIiA6IHsKICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjE2NjozMi0xNjY6NTciLAogICAgICAgICAgICAgICAgICAibmFtZSIgOiAie2h0dHA6Ly9obDcub3JnL2ZoaXJ9QWxsZXJneUludG9sZXJhbmNlQ2F0ZWdvcnkiLAogICAgICAgICAgICAgICAgICAidHlwZSIgOiAiTmFtZWRUeXBlU3BlY2lmaWVyIgogICAgICAgICAgICAgICB9CiAgICAgICAgICAgIH0gXQogICAgICAgICB9LCB7CiAgICAgICAgICAgICJsb2NhdG9yIiA6ICIxNjc6MS0xNjc6NzQiLAogICAgICAgICAgICAibmFtZSIgOiAiVG9TdHJpbmciLAogICAgICAgICAgICAiY29udGV4dCIgOiAiUGF0aWVudCIsCiAgICAgICAgICAgICJhY2Nlc3NMZXZlbCIgOiAiUHVibGljIiwKICAgICAgICAgICAgInR5cGUiIDogIkZ1bmN0aW9uRGVmIiwKICAgICAgICAgICAgImV4cHJlc3Npb24iIDogewogICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMTY3OjY0LTE2Nzo3NCIsCiAgICAgICAgICAgICAgICJwYXRoIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICJ0eXBlIiA6ICJQcm9wZXJ0eSIsCiAgICAgICAgICAgICAgICJzb3VyY2UiIDogewogICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMTY3OjY0LTE2Nzo2OCIsCiAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJPcGVyYW5kUmVmIgogICAgICAgICAgICAgICB9CiAgICAgICAgICAgIH0sCiAgICAgICAgICAgICJvcGVyYW5kIiA6IFsgewogICAgICAgICAgICAgICAibmFtZSIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAib3BlcmFuZFR5cGVTcGVjaWZpZXIiIDogewogICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMTY3OjMyLTE2Nzo2MCIsCiAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJ7aHR0cDovL2hsNy5vcmcvZmhpcn1BbGxlcmd5SW50b2xlcmFuY2VDcml0aWNhbGl0eSIsCiAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJOYW1lZFR5cGVTcGVjaWZpZXIiCiAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgfSBdCiAgICAgICAgIH0sIHsKICAgICAgICAgICAgImxvY2F0b3IiIDogIjE2ODoxLTE2ODo3MSIsCiAgICAgICAgICAgICJuYW1lIiA6ICJUb1N0cmluZyIsCiAgICAgICAgICAgICJjb250ZXh0IiA6ICJQYXRpZW50IiwKICAgICAgICAgICAgImFjY2Vzc0xldmVsIiA6ICJQdWJsaWMiLAogICAgICAgICAgICAidHlwZSIgOiAiRnVuY3Rpb25EZWYiLAogICAgICAgICAgICAiZXhwcmVzc2lvbiIgOiB7CiAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIxNjg6NjEtMTY4OjcxIiwKICAgICAgICAgICAgICAgInBhdGgiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgInR5cGUiIDogIlByb3BlcnR5IiwKICAgICAgICAgICAgICAgInNvdXJjZSIgOiB7CiAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIxNjg6NjEtMTY4OjY1IiwKICAgICAgICAgICAgICAgICAgIm5hbWUiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk9wZXJhbmRSZWYiCiAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgfSwKICAgICAgICAgICAgIm9wZXJhbmQiIDogWyB7CiAgICAgICAgICAgICAgICJuYW1lIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICJvcGVyYW5kVHlwZVNwZWNpZmllciIgOiB7CiAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIxNjg6MzItMTY4OjU3IiwKICAgICAgICAgICAgICAgICAgIm5hbWUiIDogIntodHRwOi8vaGw3Lm9yZy9maGlyfUFsbGVyZ3lJbnRvbGVyYW5jZVNldmVyaXR5IiwKICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk5hbWVkVHlwZVNwZWNpZmllciIKICAgICAgICAgICAgICAgfQogICAgICAgICAgICB9IF0KICAgICAgICAgfSwgewogICAgICAgICAgICAibG9jYXRvciIgOiAiMTY5OjEtMTY5OjY3IiwKICAgICAgICAgICAgIm5hbWUiIDogIlRvU3RyaW5nIiwKICAgICAgICAgICAgImNvbnRleHQiIDogIlBhdGllbnQiLAogICAgICAgICAgICAiYWNjZXNzTGV2ZWwiIDogIlB1YmxpYyIsCiAgICAgICAgICAgICJ0eXBlIiA6ICJGdW5jdGlvbkRlZiIsCiAgICAgICAgICAgICJleHByZXNzaW9uIiA6IHsKICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjE2OTo1Ny0xNjk6NjciLAogICAgICAgICAgICAgICAicGF0aCIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAidHlwZSIgOiAiUHJvcGVydHkiLAogICAgICAgICAgICAgICAic291cmNlIiA6IHsKICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjE2OTo1Ny0xNjk6NjEiLAogICAgICAgICAgICAgICAgICAibmFtZSIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAgICAidHlwZSIgOiAiT3BlcmFuZFJlZiIKICAgICAgICAgICAgICAgfQogICAgICAgICAgICB9LAogICAgICAgICAgICAib3BlcmFuZCIgOiBbIHsKICAgICAgICAgICAgICAgIm5hbWUiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgIm9wZXJhbmRUeXBlU3BlY2lmaWVyIiA6IHsKICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjE2OTozMi0xNjk6NTMiLAogICAgICAgICAgICAgICAgICAibmFtZSIgOiAie2h0dHA6Ly9obDcub3JnL2ZoaXJ9QWxsZXJneUludG9sZXJhbmNlVHlwZSIsCiAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJOYW1lZFR5cGVTcGVjaWZpZXIiCiAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgfSBdCiAgICAgICAgIH0sIHsKICAgICAgICAgICAgImxvY2F0b3IiIDogIjE3MDoxLTE3MDo2MiIsCiAgICAgICAgICAgICJuYW1lIiA6ICJUb1N0cmluZyIsCiAgICAgICAgICAgICJjb250ZXh0IiA6ICJQYXRpZW50IiwKICAgICAgICAgICAgImFjY2Vzc0xldmVsIiA6ICJQdWJsaWMiLAogICAgICAgICAgICAidHlwZSIgOiAiRnVuY3Rpb25EZWYiLAogICAgICAgICAgICAiZXhwcmVzc2lvbiIgOiB7CiAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIxNzA6NTItMTcwOjYyIiwKICAgICAgICAgICAgICAgInBhdGgiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgInR5cGUiIDogIlByb3BlcnR5IiwKICAgICAgICAgICAgICAgInNvdXJjZSIgOiB7CiAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIxNzA6NTItMTcwOjU2IiwKICAgICAgICAgICAgICAgICAgIm5hbWUiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk9wZXJhbmRSZWYiCiAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgfSwKICAgICAgICAgICAgIm9wZXJhbmQiIDogWyB7CiAgICAgICAgICAgICAgICJuYW1lIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICJvcGVyYW5kVHlwZVNwZWNpZmllciIgOiB7CiAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIxNzA6MzItMTcwOjQ4IiwKICAgICAgICAgICAgICAgICAgIm5hbWUiIDogIntodHRwOi8vaGw3Lm9yZy9maGlyfUFwcG9pbnRtZW50U3RhdHVzIiwKICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk5hbWVkVHlwZVNwZWNpZmllciIKICAgICAgICAgICAgICAgfQogICAgICAgICAgICB9IF0KICAgICAgICAgfSwgewogICAgICAgICAgICAibG9jYXRvciIgOiAiMTcxOjEtMTcxOjY3IiwKICAgICAgICAgICAgIm5hbWUiIDogIlRvU3RyaW5nIiwKICAgICAgICAgICAgImNvbnRleHQiIDogIlBhdGllbnQiLAogICAgICAgICAgICAiYWNjZXNzTGV2ZWwiIDogIlB1YmxpYyIsCiAgICAgICAgICAgICJ0eXBlIiA6ICJGdW5jdGlvbkRlZiIsCiAgICAgICAgICAgICJleHByZXNzaW9uIiA6IHsKICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjE3MTo1Ny0xNzE6NjciLAogICAgICAgICAgICAgICAicGF0aCIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAidHlwZSIgOiAiUHJvcGVydHkiLAogICAgICAgICAgICAgICAic291cmNlIiA6IHsKICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjE3MTo1Ny0xNzE6NjEiLAogICAgICAgICAgICAgICAgICAibmFtZSIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAgICAidHlwZSIgOiAiT3BlcmFuZFJlZiIKICAgICAgICAgICAgICAgfQogICAgICAgICAgICB9LAogICAgICAgICAgICAib3BlcmFuZCIgOiBbIHsKICAgICAgICAgICAgICAgIm5hbWUiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgIm9wZXJhbmRUeXBlU3BlY2lmaWVyIiA6IHsKICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjE3MTozMi0xNzE6NTMiLAogICAgICAgICAgICAgICAgICAibmFtZSIgOiAie2h0dHA6Ly9obDcub3JnL2ZoaXJ9QXNzZXJ0aW9uRGlyZWN0aW9uVHlwZSIsCiAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJOYW1lZFR5cGVTcGVjaWZpZXIiCiAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgfSBdCiAgICAgICAgIH0sIHsKICAgICAgICAgICAgImxvY2F0b3IiIDogIjE3MjoxLTE3Mjo2NiIsCiAgICAgICAgICAgICJuYW1lIiA6ICJUb1N0cmluZyIsCiAgICAgICAgICAgICJjb250ZXh0IiA6ICJQYXRpZW50IiwKICAgICAgICAgICAgImFjY2Vzc0xldmVsIiA6ICJQdWJsaWMiLAogICAgICAgICAgICAidHlwZSIgOiAiRnVuY3Rpb25EZWYiLAogICAgICAgICAgICAiZXhwcmVzc2lvbiIgOiB7CiAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIxNzI6NTYtMTcyOjY2IiwKICAgICAgICAgICAgICAgInBhdGgiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgInR5cGUiIDogIlByb3BlcnR5IiwKICAgICAgICAgICAgICAgInNvdXJjZSIgOiB7CiAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIxNzI6NTYtMTcyOjYwIiwKICAgICAgICAgICAgICAgICAgIm5hbWUiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk9wZXJhbmRSZWYiCiAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgfSwKICAgICAgICAgICAgIm9wZXJhbmQiIDogWyB7CiAgICAgICAgICAgICAgICJuYW1lIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICJvcGVyYW5kVHlwZVNwZWNpZmllciIgOiB7CiAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIxNzI6MzItMTcyOjUyIiwKICAgICAgICAgICAgICAgICAgIm5hbWUiIDogIntodHRwOi8vaGw3Lm9yZy9maGlyfUFzc2VydGlvbk9wZXJhdG9yVHlwZSIsCiAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJOYW1lZFR5cGVTcGVjaWZpZXIiCiAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgfSBdCiAgICAgICAgIH0sIHsKICAgICAgICAgICAgImxvY2F0b3IiIDogIjE3MzoxLTE3Mzo2NyIsCiAgICAgICAgICAgICJuYW1lIiA6ICJUb1N0cmluZyIsCiAgICAgICAgICAgICJjb250ZXh0IiA6ICJQYXRpZW50IiwKICAgICAgICAgICAgImFjY2Vzc0xldmVsIiA6ICJQdWJsaWMiLAogICAgICAgICAgICAidHlwZSIgOiAiRnVuY3Rpb25EZWYiLAogICAgICAgICAgICAiZXhwcmVzc2lvbiIgOiB7CiAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIxNzM6NTctMTczOjY3IiwKICAgICAgICAgICAgICAgInBhdGgiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgInR5cGUiIDogIlByb3BlcnR5IiwKICAgICAgICAgICAgICAgInNvdXJjZSIgOiB7CiAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIxNzM6NTctMTczOjYxIiwKICAgICAgICAgICAgICAgICAgIm5hbWUiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk9wZXJhbmRSZWYiCiAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgfSwKICAgICAgICAgICAgIm9wZXJhbmQiIDogWyB7CiAgICAgICAgICAgICAgICJuYW1lIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICJvcGVyYW5kVHlwZVNwZWNpZmllciIgOiB7CiAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIxNzM6MzItMTczOjUzIiwKICAgICAgICAgICAgICAgICAgIm5hbWUiIDogIntodHRwOi8vaGw3Lm9yZy9maGlyfUFzc2VydGlvblJlc3BvbnNlVHlwZXMiLAogICAgICAgICAgICAgICAgICAidHlwZSIgOiAiTmFtZWRUeXBlU3BlY2lmaWVyIgogICAgICAgICAgICAgICB9CiAgICAgICAgICAgIH0gXQogICAgICAgICB9LCB7CiAgICAgICAgICAgICJsb2NhdG9yIiA6ICIxNzQ6MS0xNzQ6NjEiLAogICAgICAgICAgICAibmFtZSIgOiAiVG9TdHJpbmciLAogICAgICAgICAgICAiY29udGV4dCIgOiAiUGF0aWVudCIsCiAgICAgICAgICAgICJhY2Nlc3NMZXZlbCIgOiAiUHVibGljIiwKICAgICAgICAgICAgInR5cGUiIDogIkZ1bmN0aW9uRGVmIiwKICAgICAgICAgICAgImV4cHJlc3Npb24iIDogewogICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMTc0OjUxLTE3NDo2MSIsCiAgICAgICAgICAgICAgICJwYXRoIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICJ0eXBlIiA6ICJQcm9wZXJ0eSIsCiAgICAgICAgICAgICAgICJzb3VyY2UiIDogewogICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMTc0OjUxLTE3NDo1NSIsCiAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJPcGVyYW5kUmVmIgogICAgICAgICAgICAgICB9CiAgICAgICAgICAgIH0sCiAgICAgICAgICAgICJvcGVyYW5kIiA6IFsgewogICAgICAgICAgICAgICAibmFtZSIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAib3BlcmFuZFR5cGVTcGVjaWZpZXIiIDogewogICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMTc0OjMyLTE3NDo0NyIsCiAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJ7aHR0cDovL2hsNy5vcmcvZmhpcn1BdWRpdEV2ZW50QWN0aW9uIiwKICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk5hbWVkVHlwZVNwZWNpZmllciIKICAgICAgICAgICAgICAgfQogICAgICAgICAgICB9IF0KICAgICAgICAgfSwgewogICAgICAgICAgICAibG9jYXRvciIgOiAiMTc1OjEtMTc1OjcxIiwKICAgICAgICAgICAgIm5hbWUiIDogIlRvU3RyaW5nIiwKICAgICAgICAgICAgImNvbnRleHQiIDogIlBhdGllbnQiLAogICAgICAgICAgICAiYWNjZXNzTGV2ZWwiIDogIlB1YmxpYyIsCiAgICAgICAgICAgICJ0eXBlIiA6ICJGdW5jdGlvbkRlZiIsCiAgICAgICAgICAgICJleHByZXNzaW9uIiA6IHsKICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjE3NTo2MS0xNzU6NzEiLAogICAgICAgICAgICAgICAicGF0aCIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAidHlwZSIgOiAiUHJvcGVydHkiLAogICAgICAgICAgICAgICAic291cmNlIiA6IHsKICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjE3NTo2MS0xNzU6NjUiLAogICAgICAgICAgICAgICAgICAibmFtZSIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAgICAidHlwZSIgOiAiT3BlcmFuZFJlZiIKICAgICAgICAgICAgICAgfQogICAgICAgICAgICB9LAogICAgICAgICAgICAib3BlcmFuZCIgOiBbIHsKICAgICAgICAgICAgICAgIm5hbWUiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgIm9wZXJhbmRUeXBlU3BlY2lmaWVyIiA6IHsKICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjE3NTozMi0xNzU6NTciLAogICAgICAgICAgICAgICAgICAibmFtZSIgOiAie2h0dHA6Ly9obDcub3JnL2ZoaXJ9QXVkaXRFdmVudEFnZW50TmV0d29ya1R5cGUiLAogICAgICAgICAgICAgICAgICAidHlwZSIgOiAiTmFtZWRUeXBlU3BlY2lmaWVyIgogICAgICAgICAgICAgICB9CiAgICAgICAgICAgIH0gXQogICAgICAgICB9LCB7CiAgICAgICAgICAgICJsb2NhdG9yIiA6ICIxNzY6MS0xNzY6NjIiLAogICAgICAgICAgICAibmFtZSIgOiAiVG9TdHJpbmciLAogICAgICAgICAgICAiY29udGV4dCIgOiAiUGF0aWVudCIsCiAgICAgICAgICAgICJhY2Nlc3NMZXZlbCIgOiAiUHVibGljIiwKICAgICAgICAgICAgInR5cGUiIDogIkZ1bmN0aW9uRGVmIiwKICAgICAgICAgICAgImV4cHJlc3Npb24iIDogewogICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMTc2OjUyLTE3Njo2MiIsCiAgICAgICAgICAgICAgICJwYXRoIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICJ0eXBlIiA6ICJQcm9wZXJ0eSIsCiAgICAgICAgICAgICAgICJzb3VyY2UiIDogewogICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMTc2OjUyLTE3Njo1NiIsCiAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJPcGVyYW5kUmVmIgogICAgICAgICAgICAgICB9CiAgICAgICAgICAgIH0sCiAgICAgICAgICAgICJvcGVyYW5kIiA6IFsgewogICAgICAgICAgICAgICAibmFtZSIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAib3BlcmFuZFR5cGVTcGVjaWZpZXIiIDogewogICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMTc2OjMyLTE3Njo0OCIsCiAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJ7aHR0cDovL2hsNy5vcmcvZmhpcn1BdWRpdEV2ZW50T3V0Y29tZSIsCiAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJOYW1lZFR5cGVTcGVjaWZpZXIiCiAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgfSBdCiAgICAgICAgIH0sIHsKICAgICAgICAgICAgImxvY2F0b3IiIDogIjE3NzoxLTE3Nzo2MCIsCiAgICAgICAgICAgICJuYW1lIiA6ICJUb1N0cmluZyIsCiAgICAgICAgICAgICJjb250ZXh0IiA6ICJQYXRpZW50IiwKICAgICAgICAgICAgImFjY2Vzc0xldmVsIiA6ICJQdWJsaWMiLAogICAgICAgICAgICAidHlwZSIgOiAiRnVuY3Rpb25EZWYiLAogICAgICAgICAgICAiZXhwcmVzc2lvbiIgOiB7CiAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIxNzc6NTAtMTc3OjYwIiwKICAgICAgICAgICAgICAgInBhdGgiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgInR5cGUiIDogIlByb3BlcnR5IiwKICAgICAgICAgICAgICAgInNvdXJjZSIgOiB7CiAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIxNzc6NTAtMTc3OjU0IiwKICAgICAgICAgICAgICAgICAgIm5hbWUiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk9wZXJhbmRSZWYiCiAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgfSwKICAgICAgICAgICAgIm9wZXJhbmQiIDogWyB7CiAgICAgICAgICAgICAgICJuYW1lIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICJvcGVyYW5kVHlwZVNwZWNpZmllciIgOiB7CiAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIxNzc6MzItMTc3OjQ2IiwKICAgICAgICAgICAgICAgICAgIm5hbWUiIDogIntodHRwOi8vaGw3Lm9yZy9maGlyfUJpbmRpbmdTdHJlbmd0aCIsCiAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJOYW1lZFR5cGVTcGVjaWZpZXIiCiAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgfSBdCiAgICAgICAgIH0sIHsKICAgICAgICAgICAgImxvY2F0b3IiIDogIjE3ODoxLTE3ODo3OSIsCiAgICAgICAgICAgICJuYW1lIiA6ICJUb1N0cmluZyIsCiAgICAgICAgICAgICJjb250ZXh0IiA6ICJQYXRpZW50IiwKICAgICAgICAgICAgImFjY2Vzc0xldmVsIiA6ICJQdWJsaWMiLAogICAgICAgICAgICAidHlwZSIgOiAiRnVuY3Rpb25EZWYiLAogICAgICAgICAgICAiZXhwcmVzc2lvbiIgOiB7CiAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIxNzg6NjktMTc4Ojc5IiwKICAgICAgICAgICAgICAgInBhdGgiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgInR5cGUiIDogIlByb3BlcnR5IiwKICAgICAgICAgICAgICAgInNvdXJjZSIgOiB7CiAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIxNzg6NjktMTc4OjczIiwKICAgICAgICAgICAgICAgICAgIm5hbWUiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk9wZXJhbmRSZWYiCiAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgfSwKICAgICAgICAgICAgIm9wZXJhbmQiIDogWyB7CiAgICAgICAgICAgICAgICJuYW1lIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICJvcGVyYW5kVHlwZVNwZWNpZmllciIgOiB7CiAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIxNzg6MzItMTc4OjY1IiwKICAgICAgICAgICAgICAgICAgIm5hbWUiIDogIntodHRwOi8vaGw3Lm9yZy9maGlyfUJpb2xvZ2ljYWxseURlcml2ZWRQcm9kdWN0Q2F0ZWdvcnkiLAogICAgICAgICAgICAgICAgICAidHlwZSIgOiAiTmFtZWRUeXBlU3BlY2lmaWVyIgogICAgICAgICAgICAgICB9CiAgICAgICAgICAgIH0gXQogICAgICAgICB9LCB7CiAgICAgICAgICAgICJsb2NhdG9yIiA6ICIxNzk6MS0xNzk6NzciLAogICAgICAgICAgICAibmFtZSIgOiAiVG9TdHJpbmciLAogICAgICAgICAgICAiY29udGV4dCIgOiAiUGF0aWVudCIsCiAgICAgICAgICAgICJhY2Nlc3NMZXZlbCIgOiAiUHVibGljIiwKICAgICAgICAgICAgInR5cGUiIDogIkZ1bmN0aW9uRGVmIiwKICAgICAgICAgICAgImV4cHJlc3Npb24iIDogewogICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMTc5OjY3LTE3OTo3NyIsCiAgICAgICAgICAgICAgICJwYXRoIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICJ0eXBlIiA6ICJQcm9wZXJ0eSIsCiAgICAgICAgICAgICAgICJzb3VyY2UiIDogewogICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMTc5OjY3LTE3OTo3MSIsCiAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJPcGVyYW5kUmVmIgogICAgICAgICAgICAgICB9CiAgICAgICAgICAgIH0sCiAgICAgICAgICAgICJvcGVyYW5kIiA6IFsgewogICAgICAgICAgICAgICAibmFtZSIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAib3BlcmFuZFR5cGVTcGVjaWZpZXIiIDogewogICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMTc5OjMyLTE3OTo2MyIsCiAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJ7aHR0cDovL2hsNy5vcmcvZmhpcn1CaW9sb2dpY2FsbHlEZXJpdmVkUHJvZHVjdFN0YXR1cyIsCiAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJOYW1lZFR5cGVTcGVjaWZpZXIiCiAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgfSBdCiAgICAgICAgIH0sIHsKICAgICAgICAgICAgImxvY2F0b3IiIDogIjE4MDoxLTE4MDo4MyIsCiAgICAgICAgICAgICJuYW1lIiA6ICJUb1N0cmluZyIsCiAgICAgICAgICAgICJjb250ZXh0IiA6ICJQYXRpZW50IiwKICAgICAgICAgICAgImFjY2Vzc0xldmVsIiA6ICJQdWJsaWMiLAogICAgICAgICAgICAidHlwZSIgOiAiRnVuY3Rpb25EZWYiLAogICAgICAgICAgICAiZXhwcmVzc2lvbiIgOiB7CiAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIxODA6NzMtMTgwOjgzIiwKICAgICAgICAgICAgICAgInBhdGgiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgInR5cGUiIDogIlByb3BlcnR5IiwKICAgICAgICAgICAgICAgInNvdXJjZSIgOiB7CiAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIxODA6NzMtMTgwOjc3IiwKICAgICAgICAgICAgICAgICAgIm5hbWUiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk9wZXJhbmRSZWYiCiAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgfSwKICAgICAgICAgICAgIm9wZXJhbmQiIDogWyB7CiAgICAgICAgICAgICAgICJuYW1lIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICJvcGVyYW5kVHlwZVNwZWNpZmllciIgOiB7CiAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIxODA6MzItMTgwOjY5IiwKICAgICAgICAgICAgICAgICAgIm5hbWUiIDogIntodHRwOi8vaGw3Lm9yZy9maGlyfUJpb2xvZ2ljYWxseURlcml2ZWRQcm9kdWN0U3RvcmFnZVNjYWxlIiwKICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk5hbWVkVHlwZVNwZWNpZmllciIKICAgICAgICAgICAgICAgfQogICAgICAgICAgICB9IF0KICAgICAgICAgfSwgewogICAgICAgICAgICAibG9jYXRvciIgOiAiMTgxOjEtMTgxOjU1IiwKICAgICAgICAgICAgIm5hbWUiIDogIlRvU3RyaW5nIiwKICAgICAgICAgICAgImNvbnRleHQiIDogIlBhdGllbnQiLAogICAgICAgICAgICAiYWNjZXNzTGV2ZWwiIDogIlB1YmxpYyIsCiAgICAgICAgICAgICJ0eXBlIiA6ICJGdW5jdGlvbkRlZiIsCiAgICAgICAgICAgICJleHByZXNzaW9uIiA6IHsKICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjE4MTo0NS0xODE6NTUiLAogICAgICAgICAgICAgICAicGF0aCIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAidHlwZSIgOiAiUHJvcGVydHkiLAogICAgICAgICAgICAgICAic291cmNlIiA6IHsKICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjE4MTo0NS0xODE6NDkiLAogICAgICAgICAgICAgICAgICAibmFtZSIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAgICAidHlwZSIgOiAiT3BlcmFuZFJlZiIKICAgICAgICAgICAgICAgfQogICAgICAgICAgICB9LAogICAgICAgICAgICAib3BlcmFuZCIgOiBbIHsKICAgICAgICAgICAgICAgIm5hbWUiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgIm9wZXJhbmRUeXBlU3BlY2lmaWVyIiA6IHsKICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjE4MTozMi0xODE6NDEiLAogICAgICAgICAgICAgICAgICAibmFtZSIgOiAie2h0dHA6Ly9obDcub3JnL2ZoaXJ9QnVuZGxlVHlwZSIsCiAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJOYW1lZFR5cGVTcGVjaWZpZXIiCiAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgfSBdCiAgICAgICAgIH0sIHsKICAgICAgICAgICAgImxvY2F0b3IiIDogIjE4MjoxLTE4Mjo2OCIsCiAgICAgICAgICAgICJuYW1lIiA6ICJUb1N0cmluZyIsCiAgICAgICAgICAgICJjb250ZXh0IiA6ICJQYXRpZW50IiwKICAgICAgICAgICAgImFjY2Vzc0xldmVsIiA6ICJQdWJsaWMiLAogICAgICAgICAgICAidHlwZSIgOiAiRnVuY3Rpb25EZWYiLAogICAgICAgICAgICAiZXhwcmVzc2lvbiIgOiB7CiAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIxODI6NTgtMTgyOjY4IiwKICAgICAgICAgICAgICAgInBhdGgiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgInR5cGUiIDogIlByb3BlcnR5IiwKICAgICAgICAgICAgICAgInNvdXJjZSIgOiB7CiAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIxODI6NTgtMTgyOjYyIiwKICAgICAgICAgICAgICAgICAgIm5hbWUiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk9wZXJhbmRSZWYiCiAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgfSwKICAgICAgICAgICAgIm9wZXJhbmQiIDogWyB7CiAgICAgICAgICAgICAgICJuYW1lIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICJvcGVyYW5kVHlwZVNwZWNpZmllciIgOiB7CiAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIxODI6MzItMTgyOjU0IiwKICAgICAgICAgICAgICAgICAgIm5hbWUiIDogIntodHRwOi8vaGw3Lm9yZy9maGlyfUNhcGFiaWxpdHlTdGF0ZW1lbnRLaW5kIiwKICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk5hbWVkVHlwZVNwZWNpZmllciIKICAgICAgICAgICAgICAgfQogICAgICAgICAgICB9IF0KICAgICAgICAgfSwgewogICAgICAgICAgICAibG9jYXRvciIgOiAiMTgzOjEtMTgzOjY1IiwKICAgICAgICAgICAgIm5hbWUiIDogIlRvU3RyaW5nIiwKICAgICAgICAgICAgImNvbnRleHQiIDogIlBhdGllbnQiLAogICAgICAgICAgICAiYWNjZXNzTGV2ZWwiIDogIlB1YmxpYyIsCiAgICAgICAgICAgICJ0eXBlIiA6ICJGdW5jdGlvbkRlZiIsCiAgICAgICAgICAgICJleHByZXNzaW9uIiA6IHsKICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjE4Mzo1NS0xODM6NjUiLAogICAgICAgICAgICAgICAicGF0aCIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAidHlwZSIgOiAiUHJvcGVydHkiLAogICAgICAgICAgICAgICAic291cmNlIiA6IHsKICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjE4Mzo1NS0xODM6NTkiLAogICAgICAgICAgICAgICAgICAibmFtZSIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAgICAidHlwZSIgOiAiT3BlcmFuZFJlZiIKICAgICAgICAgICAgICAgfQogICAgICAgICAgICB9LAogICAgICAgICAgICAib3BlcmFuZCIgOiBbIHsKICAgICAgICAgICAgICAgIm5hbWUiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgIm9wZXJhbmRUeXBlU3BlY2lmaWVyIiA6IHsKICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjE4MzozMi0xODM6NTEiLAogICAgICAgICAgICAgICAgICAibmFtZSIgOiAie2h0dHA6Ly9obDcub3JnL2ZoaXJ9Q2FyZVBsYW5BY3Rpdml0eUtpbmQiLAogICAgICAgICAgICAgICAgICAidHlwZSIgOiAiTmFtZWRUeXBlU3BlY2lmaWVyIgogICAgICAgICAgICAgICB9CiAgICAgICAgICAgIH0gXQogICAgICAgICB9LCB7CiAgICAgICAgICAgICJsb2NhdG9yIiA6ICIxODQ6MS0xODQ6NjciLAogICAgICAgICAgICAibmFtZSIgOiAiVG9TdHJpbmciLAogICAgICAgICAgICAiY29udGV4dCIgOiAiUGF0aWVudCIsCiAgICAgICAgICAgICJhY2Nlc3NMZXZlbCIgOiAiUHVibGljIiwKICAgICAgICAgICAgInR5cGUiIDogIkZ1bmN0aW9uRGVmIiwKICAgICAgICAgICAgImV4cHJlc3Npb24iIDogewogICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMTg0OjU3LTE4NDo2NyIsCiAgICAgICAgICAgICAgICJwYXRoIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICJ0eXBlIiA6ICJQcm9wZXJ0eSIsCiAgICAgICAgICAgICAgICJzb3VyY2UiIDogewogICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMTg0OjU3LTE4NDo2MSIsCiAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJPcGVyYW5kUmVmIgogICAgICAgICAgICAgICB9CiAgICAgICAgICAgIH0sCiAgICAgICAgICAgICJvcGVyYW5kIiA6IFsgewogICAgICAgICAgICAgICAibmFtZSIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAib3BlcmFuZFR5cGVTcGVjaWZpZXIiIDogewogICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMTg0OjMyLTE4NDo1MyIsCiAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJ7aHR0cDovL2hsNy5vcmcvZmhpcn1DYXJlUGxhbkFjdGl2aXR5U3RhdHVzIiwKICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk5hbWVkVHlwZVNwZWNpZmllciIKICAgICAgICAgICAgICAgfQogICAgICAgICAgICB9IF0KICAgICAgICAgfSwgewogICAgICAgICAgICAibG9jYXRvciIgOiAiMTg1OjEtMTg1OjU5IiwKICAgICAgICAgICAgIm5hbWUiIDogIlRvU3RyaW5nIiwKICAgICAgICAgICAgImNvbnRleHQiIDogIlBhdGllbnQiLAogICAgICAgICAgICAiYWNjZXNzTGV2ZWwiIDogIlB1YmxpYyIsCiAgICAgICAgICAgICJ0eXBlIiA6ICJGdW5jdGlvbkRlZiIsCiAgICAgICAgICAgICJleHByZXNzaW9uIiA6IHsKICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjE4NTo0OS0xODU6NTkiLAogICAgICAgICAgICAgICAicGF0aCIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAidHlwZSIgOiAiUHJvcGVydHkiLAogICAgICAgICAgICAgICAic291cmNlIiA6IHsKICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjE4NTo0OS0xODU6NTMiLAogICAgICAgICAgICAgICAgICAibmFtZSIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAgICAidHlwZSIgOiAiT3BlcmFuZFJlZiIKICAgICAgICAgICAgICAgfQogICAgICAgICAgICB9LAogICAgICAgICAgICAib3BlcmFuZCIgOiBbIHsKICAgICAgICAgICAgICAgIm5hbWUiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgIm9wZXJhbmRUeXBlU3BlY2lmaWVyIiA6IHsKICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjE4NTozMi0xODU6NDUiLAogICAgICAgICAgICAgICAgICAibmFtZSIgOiAie2h0dHA6Ly9obDcub3JnL2ZoaXJ9Q2FyZVBsYW5JbnRlbnQiLAogICAgICAgICAgICAgICAgICAidHlwZSIgOiAiTmFtZWRUeXBlU3BlY2lmaWVyIgogICAgICAgICAgICAgICB9CiAgICAgICAgICAgIH0gXQogICAgICAgICB9LCB7CiAgICAgICAgICAgICJsb2NhdG9yIiA6ICIxODY6MS0xODY6NTkiLAogICAgICAgICAgICAibmFtZSIgOiAiVG9TdHJpbmciLAogICAgICAgICAgICAiY29udGV4dCIgOiAiUGF0aWVudCIsCiAgICAgICAgICAgICJhY2Nlc3NMZXZlbCIgOiAiUHVibGljIiwKICAgICAgICAgICAgInR5cGUiIDogIkZ1bmN0aW9uRGVmIiwKICAgICAgICAgICAgImV4cHJlc3Npb24iIDogewogICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMTg2OjQ5LTE4Njo1OSIsCiAgICAgICAgICAgICAgICJwYXRoIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICJ0eXBlIiA6ICJQcm9wZXJ0eSIsCiAgICAgICAgICAgICAgICJzb3VyY2UiIDogewogICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMTg2OjQ5LTE4Njo1MyIsCiAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJPcGVyYW5kUmVmIgogICAgICAgICAgICAgICB9CiAgICAgICAgICAgIH0sCiAgICAgICAgICAgICJvcGVyYW5kIiA6IFsgewogICAgICAgICAgICAgICAibmFtZSIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAib3BlcmFuZFR5cGVTcGVjaWZpZXIiIDogewogICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMTg2OjMyLTE4Njo0NSIsCiAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJ7aHR0cDovL2hsNy5vcmcvZmhpcn1DYXJlUGxhblN0YXR1cyIsCiAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJOYW1lZFR5cGVTcGVjaWZpZXIiCiAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgfSBdCiAgICAgICAgIH0sIHsKICAgICAgICAgICAgImxvY2F0b3IiIDogIjE4NzoxLTE4Nzo1OSIsCiAgICAgICAgICAgICJuYW1lIiA6ICJUb1N0cmluZyIsCiAgICAgICAgICAgICJjb250ZXh0IiA6ICJQYXRpZW50IiwKICAgICAgICAgICAgImFjY2Vzc0xldmVsIiA6ICJQdWJsaWMiLAogICAgICAgICAgICAidHlwZSIgOiAiRnVuY3Rpb25EZWYiLAogICAgICAgICAgICAiZXhwcmVzc2lvbiIgOiB7CiAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIxODc6NDktMTg3OjU5IiwKICAgICAgICAgICAgICAgInBhdGgiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgInR5cGUiIDogIlByb3BlcnR5IiwKICAgICAgICAgICAgICAgInNvdXJjZSIgOiB7CiAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIxODc6NDktMTg3OjUzIiwKICAgICAgICAgICAgICAgICAgIm5hbWUiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk9wZXJhbmRSZWYiCiAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgfSwKICAgICAgICAgICAgIm9wZXJhbmQiIDogWyB7CiAgICAgICAgICAgICAgICJuYW1lIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICJvcGVyYW5kVHlwZVNwZWNpZmllciIgOiB7CiAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIxODc6MzItMTg3OjQ1IiwKICAgICAgICAgICAgICAgICAgIm5hbWUiIDogIntodHRwOi8vaGw3Lm9yZy9maGlyfUNhcmVUZWFtU3RhdHVzIiwKICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk5hbWVkVHlwZVNwZWNpZmllciIKICAgICAgICAgICAgICAgfQogICAgICAgICAgICB9IF0KICAgICAgICAgfSwgewogICAgICAgICAgICAibG9jYXRvciIgOiAiMTg4OjEtMTg4OjY5IiwKICAgICAgICAgICAgIm5hbWUiIDogIlRvU3RyaW5nIiwKICAgICAgICAgICAgImNvbnRleHQiIDogIlBhdGllbnQiLAogICAgICAgICAgICAiYWNjZXNzTGV2ZWwiIDogIlB1YmxpYyIsCiAgICAgICAgICAgICJ0eXBlIiA6ICJGdW5jdGlvbkRlZiIsCiAgICAgICAgICAgICJleHByZXNzaW9uIiA6IHsKICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjE4ODo1OS0xODg6NjkiLAogICAgICAgICAgICAgICAicGF0aCIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAidHlwZSIgOiAiUHJvcGVydHkiLAogICAgICAgICAgICAgICAic291cmNlIiA6IHsKICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjE4ODo1OS0xODg6NjMiLAogICAgICAgICAgICAgICAgICAibmFtZSIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAgICAidHlwZSIgOiAiT3BlcmFuZFJlZiIKICAgICAgICAgICAgICAgfQogICAgICAgICAgICB9LAogICAgICAgICAgICAib3BlcmFuZCIgOiBbIHsKICAgICAgICAgICAgICAgIm5hbWUiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgIm9wZXJhbmRUeXBlU3BlY2lmaWVyIiA6IHsKICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjE4ODozMi0xODg6NTUiLAogICAgICAgICAgICAgICAgICAibmFtZSIgOiAie2h0dHA6Ly9obDcub3JnL2ZoaXJ9Q2F0YWxvZ0VudHJ5UmVsYXRpb25UeXBlIiwKICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk5hbWVkVHlwZVNwZWNpZmllciIKICAgICAgICAgICAgICAgfQogICAgICAgICAgICB9IF0KICAgICAgICAgfSwgewogICAgICAgICAgICAibG9jYXRvciIgOiAiMTg5OjEtMTg5OjgzIiwKICAgICAgICAgICAgIm5hbWUiIDogIlRvU3RyaW5nIiwKICAgICAgICAgICAgImNvbnRleHQiIDogIlBhdGllbnQiLAogICAgICAgICAgICAiYWNjZXNzTGV2ZWwiIDogIlB1YmxpYyIsCiAgICAgICAgICAgICJ0eXBlIiA6ICJGdW5jdGlvbkRlZiIsCiAgICAgICAgICAgICJleHByZXNzaW9uIiA6IHsKICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjE4OTo3My0xODk6ODMiLAogICAgICAgICAgICAgICAicGF0aCIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAidHlwZSIgOiAiUHJvcGVydHkiLAogICAgICAgICAgICAgICAic291cmNlIiA6IHsKICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjE4OTo3My0xODk6NzciLAogICAgICAgICAgICAgICAgICAibmFtZSIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAgICAidHlwZSIgOiAiT3BlcmFuZFJlZiIKICAgICAgICAgICAgICAgfQogICAgICAgICAgICB9LAogICAgICAgICAgICAib3BlcmFuZCIgOiBbIHsKICAgICAgICAgICAgICAgIm5hbWUiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgIm9wZXJhbmRUeXBlU3BlY2lmaWVyIiA6IHsKICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjE4OTozMi0xODk6NjkiLAogICAgICAgICAgICAgICAgICAibmFtZSIgOiAie2h0dHA6Ly9obDcub3JnL2ZoaXJ9Q2hhcmdlSXRlbURlZmluaXRpb25QcmljZUNvbXBvbmVudFR5cGUiLAogICAgICAgICAgICAgICAgICAidHlwZSIgOiAiTmFtZWRUeXBlU3BlY2lmaWVyIgogICAgICAgICAgICAgICB9CiAgICAgICAgICAgIH0gXQogICAgICAgICB9LCB7CiAgICAgICAgICAgICJsb2NhdG9yIiA6ICIxOTA6MS0xOTA6NjEiLAogICAgICAgICAgICAibmFtZSIgOiAiVG9TdHJpbmciLAogICAgICAgICAgICAiY29udGV4dCIgOiAiUGF0aWVudCIsCiAgICAgICAgICAgICJhY2Nlc3NMZXZlbCIgOiAiUHVibGljIiwKICAgICAgICAgICAgInR5cGUiIDogIkZ1bmN0aW9uRGVmIiwKICAgICAgICAgICAgImV4cHJlc3Npb24iIDogewogICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMTkwOjUxLTE5MDo2MSIsCiAgICAgICAgICAgICAgICJwYXRoIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICJ0eXBlIiA6ICJQcm9wZXJ0eSIsCiAgICAgICAgICAgICAgICJzb3VyY2UiIDogewogICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMTkwOjUxLTE5MDo1NSIsCiAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJPcGVyYW5kUmVmIgogICAgICAgICAgICAgICB9CiAgICAgICAgICAgIH0sCiAgICAgICAgICAgICJvcGVyYW5kIiA6IFsgewogICAgICAgICAgICAgICAibmFtZSIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAib3BlcmFuZFR5cGVTcGVjaWZpZXIiIDogewogICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMTkwOjMyLTE5MDo0NyIsCiAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJ7aHR0cDovL2hsNy5vcmcvZmhpcn1DaGFyZ2VJdGVtU3RhdHVzIiwKICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk5hbWVkVHlwZVNwZWNpZmllciIKICAgICAgICAgICAgICAgfQogICAgICAgICAgICB9IF0KICAgICAgICAgfSwgewogICAgICAgICAgICAibG9jYXRvciIgOiAiMTkxOjEtMTkxOjY0IiwKICAgICAgICAgICAgIm5hbWUiIDogIlRvU3RyaW5nIiwKICAgICAgICAgICAgImNvbnRleHQiIDogIlBhdGllbnQiLAogICAgICAgICAgICAiYWNjZXNzTGV2ZWwiIDogIlB1YmxpYyIsCiAgICAgICAgICAgICJ0eXBlIiA6ICJGdW5jdGlvbkRlZiIsCiAgICAgICAgICAgICJleHByZXNzaW9uIiA6IHsKICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjE5MTo1NC0xOTE6NjQiLAogICAgICAgICAgICAgICAicGF0aCIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAidHlwZSIgOiAiUHJvcGVydHkiLAogICAgICAgICAgICAgICAic291cmNlIiA6IHsKICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjE5MTo1NC0xOTE6NTgiLAogICAgICAgICAgICAgICAgICAibmFtZSIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAgICAidHlwZSIgOiAiT3BlcmFuZFJlZiIKICAgICAgICAgICAgICAgfQogICAgICAgICAgICB9LAogICAgICAgICAgICAib3BlcmFuZCIgOiBbIHsKICAgICAgICAgICAgICAgIm5hbWUiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgIm9wZXJhbmRUeXBlU3BlY2lmaWVyIiA6IHsKICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjE5MTozMi0xOTE6NTAiLAogICAgICAgICAgICAgICAgICAibmFtZSIgOiAie2h0dHA6Ly9obDcub3JnL2ZoaXJ9Q2xhaW1SZXNwb25zZVN0YXR1cyIsCiAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJOYW1lZFR5cGVTcGVjaWZpZXIiCiAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgfSBdCiAgICAgICAgIH0sIHsKICAgICAgICAgICAgImxvY2F0b3IiIDogIjE5MjoxLTE5Mjo1NiIsCiAgICAgICAgICAgICJuYW1lIiA6ICJUb1N0cmluZyIsCiAgICAgICAgICAgICJjb250ZXh0IiA6ICJQYXRpZW50IiwKICAgICAgICAgICAgImFjY2Vzc0xldmVsIiA6ICJQdWJsaWMiLAogICAgICAgICAgICAidHlwZSIgOiAiRnVuY3Rpb25EZWYiLAogICAgICAgICAgICAiZXhwcmVzc2lvbiIgOiB7CiAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIxOTI6NDYtMTkyOjU2IiwKICAgICAgICAgICAgICAgInBhdGgiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgInR5cGUiIDogIlByb3BlcnR5IiwKICAgICAgICAgICAgICAgInNvdXJjZSIgOiB7CiAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIxOTI6NDYtMTkyOjUwIiwKICAgICAgICAgICAgICAgICAgIm5hbWUiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk9wZXJhbmRSZWYiCiAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgfSwKICAgICAgICAgICAgIm9wZXJhbmQiIDogWyB7CiAgICAgICAgICAgICAgICJuYW1lIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICJvcGVyYW5kVHlwZVNwZWNpZmllciIgOiB7CiAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIxOTI6MzItMTkyOjQyIiwKICAgICAgICAgICAgICAgICAgIm5hbWUiIDogIntodHRwOi8vaGw3Lm9yZy9maGlyfUNsYWltU3RhdHVzIiwKICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk5hbWVkVHlwZVNwZWNpZmllciIKICAgICAgICAgICAgICAgfQogICAgICAgICAgICB9IF0KICAgICAgICAgfSwgewogICAgICAgICAgICAibG9jYXRvciIgOiAiMTkzOjEtMTkzOjY5IiwKICAgICAgICAgICAgIm5hbWUiIDogIlRvU3RyaW5nIiwKICAgICAgICAgICAgImNvbnRleHQiIDogIlBhdGllbnQiLAogICAgICAgICAgICAiYWNjZXNzTGV2ZWwiIDogIlB1YmxpYyIsCiAgICAgICAgICAgICJ0eXBlIiA6ICJGdW5jdGlvbkRlZiIsCiAgICAgICAgICAgICJleHByZXNzaW9uIiA6IHsKICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjE5Mzo1OS0xOTM6NjkiLAogICAgICAgICAgICAgICAicGF0aCIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAidHlwZSIgOiAiUHJvcGVydHkiLAogICAgICAgICAgICAgICAic291cmNlIiA6IHsKICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjE5Mzo1OS0xOTM6NjMiLAogICAgICAgICAgICAgICAgICAibmFtZSIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAgICAidHlwZSIgOiAiT3BlcmFuZFJlZiIKICAgICAgICAgICAgICAgfQogICAgICAgICAgICB9LAogICAgICAgICAgICAib3BlcmFuZCIgOiBbIHsKICAgICAgICAgICAgICAgIm5hbWUiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgIm9wZXJhbmRUeXBlU3BlY2lmaWVyIiA6IHsKICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjE5MzozMi0xOTM6NTUiLAogICAgICAgICAgICAgICAgICAibmFtZSIgOiAie2h0dHA6Ly9obDcub3JnL2ZoaXJ9Q2xpbmljYWxJbXByZXNzaW9uU3RhdHVzIiwKICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk5hbWVkVHlwZVNwZWNpZmllciIKICAgICAgICAgICAgICAgfQogICAgICAgICAgICB9IF0KICAgICAgICAgfSwgewogICAgICAgICAgICAibG9jYXRvciIgOiAiMTk0OjEtMTk0OjYyIiwKICAgICAgICAgICAgIm5hbWUiIDogIlRvU3RyaW5nIiwKICAgICAgICAgICAgImNvbnRleHQiIDogIlBhdGllbnQiLAogICAgICAgICAgICAiYWNjZXNzTGV2ZWwiIDogIlB1YmxpYyIsCiAgICAgICAgICAgICJ0eXBlIiA6ICJGdW5jdGlvbkRlZiIsCiAgICAgICAgICAgICJleHByZXNzaW9uIiA6IHsKICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjE5NDo1Mi0xOTQ6NjIiLAogICAgICAgICAgICAgICAicGF0aCIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAidHlwZSIgOiAiUHJvcGVydHkiLAogICAgICAgICAgICAgICAic291cmNlIiA6IHsKICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjE5NDo1Mi0xOTQ6NTYiLAogICAgICAgICAgICAgICAgICAibmFtZSIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAgICAidHlwZSIgOiAiT3BlcmFuZFJlZiIKICAgICAgICAgICAgICAgfQogICAgICAgICAgICB9LAogICAgICAgICAgICAib3BlcmFuZCIgOiBbIHsKICAgICAgICAgICAgICAgIm5hbWUiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgIm9wZXJhbmRUeXBlU3BlY2lmaWVyIiA6IHsKICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjE5NDozMi0xOTQ6NDgiLAogICAgICAgICAgICAgICAgICAibmFtZSIgOiAie2h0dHA6Ly9obDcub3JnL2ZoaXJ9Q29kZVNlYXJjaFN1cHBvcnQiLAogICAgICAgICAgICAgICAgICAidHlwZSIgOiAiTmFtZWRUeXBlU3BlY2lmaWVyIgogICAgICAgICAgICAgICB9CiAgICAgICAgICAgIH0gXQogICAgICAgICB9LCB7CiAgICAgICAgICAgICJsb2NhdG9yIiA6ICIxOTU6MS0xOTU6NjYiLAogICAgICAgICAgICAibmFtZSIgOiAiVG9TdHJpbmciLAogICAgICAgICAgICAiY29udGV4dCIgOiAiUGF0aWVudCIsCiAgICAgICAgICAgICJhY2Nlc3NMZXZlbCIgOiAiUHVibGljIiwKICAgICAgICAgICAgInR5cGUiIDogIkZ1bmN0aW9uRGVmIiwKICAgICAgICAgICAgImV4cHJlc3Npb24iIDogewogICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMTk1OjU2LTE5NTo2NiIsCiAgICAgICAgICAgICAgICJwYXRoIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICJ0eXBlIiA6ICJQcm9wZXJ0eSIsCiAgICAgICAgICAgICAgICJzb3VyY2UiIDogewogICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMTk1OjU2LTE5NTo2MCIsCiAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJPcGVyYW5kUmVmIgogICAgICAgICAgICAgICB9CiAgICAgICAgICAgIH0sCiAgICAgICAgICAgICJvcGVyYW5kIiA6IFsgewogICAgICAgICAgICAgICAibmFtZSIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAib3BlcmFuZFR5cGVTcGVjaWZpZXIiIDogewogICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMTk1OjMyLTE5NTo1MiIsCiAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJ7aHR0cDovL2hsNy5vcmcvZmhpcn1Db2RlU3lzdGVtQ29udGVudE1vZGUiLAogICAgICAgICAgICAgICAgICAidHlwZSIgOiAiTmFtZWRUeXBlU3BlY2lmaWVyIgogICAgICAgICAgICAgICB9CiAgICAgICAgICAgIH0gXQogICAgICAgICB9LCB7CiAgICAgICAgICAgICJsb2NhdG9yIiA6ICIxOTY6MS0xOTY6NzEiLAogICAgICAgICAgICAibmFtZSIgOiAiVG9TdHJpbmciLAogICAgICAgICAgICAiY29udGV4dCIgOiAiUGF0aWVudCIsCiAgICAgICAgICAgICJhY2Nlc3NMZXZlbCIgOiAiUHVibGljIiwKICAgICAgICAgICAgInR5cGUiIDogIkZ1bmN0aW9uRGVmIiwKICAgICAgICAgICAgImV4cHJlc3Npb24iIDogewogICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMTk2OjYxLTE5Njo3MSIsCiAgICAgICAgICAgICAgICJwYXRoIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICJ0eXBlIiA6ICJQcm9wZXJ0eSIsCiAgICAgICAgICAgICAgICJzb3VyY2UiIDogewogICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMTk2OjYxLTE5Njo2NSIsCiAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJPcGVyYW5kUmVmIgogICAgICAgICAgICAgICB9CiAgICAgICAgICAgIH0sCiAgICAgICAgICAgICJvcGVyYW5kIiA6IFsgewogICAgICAgICAgICAgICAibmFtZSIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAib3BlcmFuZFR5cGVTcGVjaWZpZXIiIDogewogICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMTk2OjMyLTE5Njo1NyIsCiAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJ7aHR0cDovL2hsNy5vcmcvZmhpcn1Db2RlU3lzdGVtSGllcmFyY2h5TWVhbmluZyIsCiAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJOYW1lZFR5cGVTcGVjaWZpZXIiCiAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgfSBdCiAgICAgICAgIH0sIHsKICAgICAgICAgICAgImxvY2F0b3IiIDogIjE5NzoxLTE5Nzo2NiIsCiAgICAgICAgICAgICJuYW1lIiA6ICJUb1N0cmluZyIsCiAgICAgICAgICAgICJjb250ZXh0IiA6ICJQYXRpZW50IiwKICAgICAgICAgICAgImFjY2Vzc0xldmVsIiA6ICJQdWJsaWMiLAogICAgICAgICAgICAidHlwZSIgOiAiRnVuY3Rpb25EZWYiLAogICAgICAgICAgICAiZXhwcmVzc2lvbiIgOiB7CiAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIxOTc6NTYtMTk3OjY2IiwKICAgICAgICAgICAgICAgInBhdGgiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgInR5cGUiIDogIlByb3BlcnR5IiwKICAgICAgICAgICAgICAgInNvdXJjZSIgOiB7CiAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIxOTc6NTYtMTk3OjYwIiwKICAgICAgICAgICAgICAgICAgIm5hbWUiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk9wZXJhbmRSZWYiCiAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgfSwKICAgICAgICAgICAgIm9wZXJhbmQiIDogWyB7CiAgICAgICAgICAgICAgICJuYW1lIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICJvcGVyYW5kVHlwZVNwZWNpZmllciIgOiB7CiAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIxOTc6MzItMTk3OjUyIiwKICAgICAgICAgICAgICAgICAgIm5hbWUiIDogIntodHRwOi8vaGw3Lm9yZy9maGlyfUNvbW11bmljYXRpb25Qcmlvcml0eSIsCiAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJOYW1lZFR5cGVTcGVjaWZpZXIiCiAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgfSBdCiAgICAgICAgIH0sIHsKICAgICAgICAgICAgImxvY2F0b3IiIDogIjE5ODoxLTE5ODo3MSIsCiAgICAgICAgICAgICJuYW1lIiA6ICJUb1N0cmluZyIsCiAgICAgICAgICAgICJjb250ZXh0IiA6ICJQYXRpZW50IiwKICAgICAgICAgICAgImFjY2Vzc0xldmVsIiA6ICJQdWJsaWMiLAogICAgICAgICAgICAidHlwZSIgOiAiRnVuY3Rpb25EZWYiLAogICAgICAgICAgICAiZXhwcmVzc2lvbiIgOiB7CiAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIxOTg6NjEtMTk4OjcxIiwKICAgICAgICAgICAgICAgInBhdGgiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgInR5cGUiIDogIlByb3BlcnR5IiwKICAgICAgICAgICAgICAgInNvdXJjZSIgOiB7CiAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIxOTg6NjEtMTk4OjY1IiwKICAgICAgICAgICAgICAgICAgIm5hbWUiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk9wZXJhbmRSZWYiCiAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgfSwKICAgICAgICAgICAgIm9wZXJhbmQiIDogWyB7CiAgICAgICAgICAgICAgICJuYW1lIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICJvcGVyYW5kVHlwZVNwZWNpZmllciIgOiB7CiAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIxOTg6MzItMTk4OjU3IiwKICAgICAgICAgICAgICAgICAgIm5hbWUiIDogIntodHRwOi8vaGw3Lm9yZy9maGlyfUNvbW11bmljYXRpb25SZXF1ZXN0U3RhdHVzIiwKICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk5hbWVkVHlwZVNwZWNpZmllciIKICAgICAgICAgICAgICAgfQogICAgICAgICAgICB9IF0KICAgICAgICAgfSwgewogICAgICAgICAgICAibG9jYXRvciIgOiAiMTk5OjEtMTk5OjY0IiwKICAgICAgICAgICAgIm5hbWUiIDogIlRvU3RyaW5nIiwKICAgICAgICAgICAgImNvbnRleHQiIDogIlBhdGllbnQiLAogICAgICAgICAgICAiYWNjZXNzTGV2ZWwiIDogIlB1YmxpYyIsCiAgICAgICAgICAgICJ0eXBlIiA6ICJGdW5jdGlvbkRlZiIsCiAgICAgICAgICAgICJleHByZXNzaW9uIiA6IHsKICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjE5OTo1NC0xOTk6NjQiLAogICAgICAgICAgICAgICAicGF0aCIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAidHlwZSIgOiAiUHJvcGVydHkiLAogICAgICAgICAgICAgICAic291cmNlIiA6IHsKICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjE5OTo1NC0xOTk6NTgiLAogICAgICAgICAgICAgICAgICAibmFtZSIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAgICAidHlwZSIgOiAiT3BlcmFuZFJlZiIKICAgICAgICAgICAgICAgfQogICAgICAgICAgICB9LAogICAgICAgICAgICAib3BlcmFuZCIgOiBbIHsKICAgICAgICAgICAgICAgIm5hbWUiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgIm9wZXJhbmRUeXBlU3BlY2lmaWVyIiA6IHsKICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjE5OTozMi0xOTk6NTAiLAogICAgICAgICAgICAgICAgICAibmFtZSIgOiAie2h0dHA6Ly9obDcub3JnL2ZoaXJ9Q29tbXVuaWNhdGlvblN0YXR1cyIsCiAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJOYW1lZFR5cGVTcGVjaWZpZXIiCiAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgfSBdCiAgICAgICAgIH0sIHsKICAgICAgICAgICAgImxvY2F0b3IiIDogIjIwMDoxLTIwMDo2MCIsCiAgICAgICAgICAgICJuYW1lIiA6ICJUb1N0cmluZyIsCiAgICAgICAgICAgICJjb250ZXh0IiA6ICJQYXRpZW50IiwKICAgICAgICAgICAgImFjY2Vzc0xldmVsIiA6ICJQdWJsaWMiLAogICAgICAgICAgICAidHlwZSIgOiAiRnVuY3Rpb25EZWYiLAogICAgICAgICAgICAiZXhwcmVzc2lvbiIgOiB7CiAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIyMDA6NTAtMjAwOjYwIiwKICAgICAgICAgICAgICAgInBhdGgiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgInR5cGUiIDogIlByb3BlcnR5IiwKICAgICAgICAgICAgICAgInNvdXJjZSIgOiB7CiAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIyMDA6NTAtMjAwOjU0IiwKICAgICAgICAgICAgICAgICAgIm5hbWUiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk9wZXJhbmRSZWYiCiAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgfSwKICAgICAgICAgICAgIm9wZXJhbmQiIDogWyB7CiAgICAgICAgICAgICAgICJuYW1lIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICJvcGVyYW5kVHlwZVNwZWNpZmllciIgOiB7CiAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIyMDA6MzItMjAwOjQ2IiwKICAgICAgICAgICAgICAgICAgIm5hbWUiIDogIntodHRwOi8vaGw3Lm9yZy9maGlyfUNvbXBhcnRtZW50Q29kZSIsCiAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJOYW1lZFR5cGVTcGVjaWZpZXIiCiAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgfSBdCiAgICAgICAgIH0sIHsKICAgICAgICAgICAgImxvY2F0b3IiIDogIjIwMToxLTIwMTo2MCIsCiAgICAgICAgICAgICJuYW1lIiA6ICJUb1N0cmluZyIsCiAgICAgICAgICAgICJjb250ZXh0IiA6ICJQYXRpZW50IiwKICAgICAgICAgICAgImFjY2Vzc0xldmVsIiA6ICJQdWJsaWMiLAogICAgICAgICAgICAidHlwZSIgOiAiRnVuY3Rpb25EZWYiLAogICAgICAgICAgICAiZXhwcmVzc2lvbiIgOiB7CiAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIyMDE6NTAtMjAxOjYwIiwKICAgICAgICAgICAgICAgInBhdGgiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgInR5cGUiIDogIlByb3BlcnR5IiwKICAgICAgICAgICAgICAgInNvdXJjZSIgOiB7CiAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIyMDE6NTAtMjAxOjU0IiwKICAgICAgICAgICAgICAgICAgIm5hbWUiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk9wZXJhbmRSZWYiCiAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgfSwKICAgICAgICAgICAgIm9wZXJhbmQiIDogWyB7CiAgICAgICAgICAgICAgICJuYW1lIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICJvcGVyYW5kVHlwZVNwZWNpZmllciIgOiB7CiAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIyMDE6MzItMjAxOjQ2IiwKICAgICAgICAgICAgICAgICAgIm5hbWUiIDogIntodHRwOi8vaGw3Lm9yZy9maGlyfUNvbXBhcnRtZW50VHlwZSIsCiAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJOYW1lZFR5cGVTcGVjaWZpZXIiCiAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgfSBdCiAgICAgICAgIH0sIHsKICAgICAgICAgICAgImxvY2F0b3IiIDogIjIwMjoxLTIwMjo3MSIsCiAgICAgICAgICAgICJuYW1lIiA6ICJUb1N0cmluZyIsCiAgICAgICAgICAgICJjb250ZXh0IiA6ICJQYXRpZW50IiwKICAgICAgICAgICAgImFjY2Vzc0xldmVsIiA6ICJQdWJsaWMiLAogICAgICAgICAgICAidHlwZSIgOiAiRnVuY3Rpb25EZWYiLAogICAgICAgICAgICAiZXhwcmVzc2lvbiIgOiB7CiAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIyMDI6NjEtMjAyOjcxIiwKICAgICAgICAgICAgICAgInBhdGgiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgInR5cGUiIDogIlByb3BlcnR5IiwKICAgICAgICAgICAgICAgInNvdXJjZSIgOiB7CiAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIyMDI6NjEtMjAyOjY1IiwKICAgICAgICAgICAgICAgICAgIm5hbWUiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk9wZXJhbmRSZWYiCiAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgfSwKICAgICAgICAgICAgIm9wZXJhbmQiIDogWyB7CiAgICAgICAgICAgICAgICJuYW1lIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICJvcGVyYW5kVHlwZVNwZWNpZmllciIgOiB7CiAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIyMDI6MzItMjAyOjU3IiwKICAgICAgICAgICAgICAgICAgIm5hbWUiIDogIntodHRwOi8vaGw3Lm9yZy9maGlyfUNvbXBvc2l0aW9uQXR0ZXN0YXRpb25Nb2RlIiwKICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk5hbWVkVHlwZVNwZWNpZmllciIKICAgICAgICAgICAgICAgfQogICAgICAgICAgICB9IF0KICAgICAgICAgfSwgewogICAgICAgICAgICAibG9jYXRvciIgOiAiMjAzOjEtMjAzOjYyIiwKICAgICAgICAgICAgIm5hbWUiIDogIlRvU3RyaW5nIiwKICAgICAgICAgICAgImNvbnRleHQiIDogIlBhdGllbnQiLAogICAgICAgICAgICAiYWNjZXNzTGV2ZWwiIDogIlB1YmxpYyIsCiAgICAgICAgICAgICJ0eXBlIiA6ICJGdW5jdGlvbkRlZiIsCiAgICAgICAgICAgICJleHByZXNzaW9uIiA6IHsKICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjIwMzo1Mi0yMDM6NjIiLAogICAgICAgICAgICAgICAicGF0aCIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAidHlwZSIgOiAiUHJvcGVydHkiLAogICAgICAgICAgICAgICAic291cmNlIiA6IHsKICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjIwMzo1Mi0yMDM6NTYiLAogICAgICAgICAgICAgICAgICAibmFtZSIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAgICAidHlwZSIgOiAiT3BlcmFuZFJlZiIKICAgICAgICAgICAgICAgfQogICAgICAgICAgICB9LAogICAgICAgICAgICAib3BlcmFuZCIgOiBbIHsKICAgICAgICAgICAgICAgIm5hbWUiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgIm9wZXJhbmRUeXBlU3BlY2lmaWVyIiA6IHsKICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjIwMzozMi0yMDM6NDgiLAogICAgICAgICAgICAgICAgICAibmFtZSIgOiAie2h0dHA6Ly9obDcub3JnL2ZoaXJ9Q29tcG9zaXRpb25TdGF0dXMiLAogICAgICAgICAgICAgICAgICAidHlwZSIgOiAiTmFtZWRUeXBlU3BlY2lmaWVyIgogICAgICAgICAgICAgICB9CiAgICAgICAgICAgIH0gXQogICAgICAgICB9LCB7CiAgICAgICAgICAgICJsb2NhdG9yIiA6ICIyMDQ6MS0yMDQ6NjYiLAogICAgICAgICAgICAibmFtZSIgOiAiVG9TdHJpbmciLAogICAgICAgICAgICAiY29udGV4dCIgOiAiUGF0aWVudCIsCiAgICAgICAgICAgICJhY2Nlc3NMZXZlbCIgOiAiUHVibGljIiwKICAgICAgICAgICAgInR5cGUiIDogIkZ1bmN0aW9uRGVmIiwKICAgICAgICAgICAgImV4cHJlc3Npb24iIDogewogICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMjA0OjU2LTIwNDo2NiIsCiAgICAgICAgICAgICAgICJwYXRoIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICJ0eXBlIiA6ICJQcm9wZXJ0eSIsCiAgICAgICAgICAgICAgICJzb3VyY2UiIDogewogICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMjA0OjU2LTIwNDo2MCIsCiAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJPcGVyYW5kUmVmIgogICAgICAgICAgICAgICB9CiAgICAgICAgICAgIH0sCiAgICAgICAgICAgICJvcGVyYW5kIiA6IFsgewogICAgICAgICAgICAgICAibmFtZSIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAib3BlcmFuZFR5cGVTcGVjaWZpZXIiIDogewogICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMjA0OjMyLTIwNDo1MiIsCiAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJ7aHR0cDovL2hsNy5vcmcvZmhpcn1Db25jZXB0TWFwRXF1aXZhbGVuY2UiLAogICAgICAgICAgICAgICAgICAidHlwZSIgOiAiTmFtZWRUeXBlU3BlY2lmaWVyIgogICAgICAgICAgICAgICB9CiAgICAgICAgICAgIH0gXQogICAgICAgICB9LCB7CiAgICAgICAgICAgICJsb2NhdG9yIiA6ICIyMDU6MS0yMDU6NzIiLAogICAgICAgICAgICAibmFtZSIgOiAiVG9TdHJpbmciLAogICAgICAgICAgICAiY29udGV4dCIgOiAiUGF0aWVudCIsCiAgICAgICAgICAgICJhY2Nlc3NMZXZlbCIgOiAiUHVibGljIiwKICAgICAgICAgICAgInR5cGUiIDogIkZ1bmN0aW9uRGVmIiwKICAgICAgICAgICAgImV4cHJlc3Npb24iIDogewogICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMjA1OjYyLTIwNTo3MiIsCiAgICAgICAgICAgICAgICJwYXRoIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICJ0eXBlIiA6ICJQcm9wZXJ0eSIsCiAgICAgICAgICAgICAgICJzb3VyY2UiIDogewogICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMjA1OjYyLTIwNTo2NiIsCiAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJPcGVyYW5kUmVmIgogICAgICAgICAgICAgICB9CiAgICAgICAgICAgIH0sCiAgICAgICAgICAgICJvcGVyYW5kIiA6IFsgewogICAgICAgICAgICAgICAibmFtZSIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAib3BlcmFuZFR5cGVTcGVjaWZpZXIiIDogewogICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMjA1OjMyLTIwNTo1OCIsCiAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJ7aHR0cDovL2hsNy5vcmcvZmhpcn1Db25jZXB0TWFwR3JvdXBVbm1hcHBlZE1vZGUiLAogICAgICAgICAgICAgICAgICAidHlwZSIgOiAiTmFtZWRUeXBlU3BlY2lmaWVyIgogICAgICAgICAgICAgICB9CiAgICAgICAgICAgIH0gXQogICAgICAgICB9LCB7CiAgICAgICAgICAgICJsb2NhdG9yIiA6ICIyMDY6MS0yMDY6NjgiLAogICAgICAgICAgICAibmFtZSIgOiAiVG9TdHJpbmciLAogICAgICAgICAgICAiY29udGV4dCIgOiAiUGF0aWVudCIsCiAgICAgICAgICAgICJhY2Nlc3NMZXZlbCIgOiAiUHVibGljIiwKICAgICAgICAgICAgInR5cGUiIDogIkZ1bmN0aW9uRGVmIiwKICAgICAgICAgICAgImV4cHJlc3Npb24iIDogewogICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMjA2OjU4LTIwNjo2OCIsCiAgICAgICAgICAgICAgICJwYXRoIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICJ0eXBlIiA6ICJQcm9wZXJ0eSIsCiAgICAgICAgICAgICAgICJzb3VyY2UiIDogewogICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMjA2OjU4LTIwNjo2MiIsCiAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJPcGVyYW5kUmVmIgogICAgICAgICAgICAgICB9CiAgICAgICAgICAgIH0sCiAgICAgICAgICAgICJvcGVyYW5kIiA6IFsgewogICAgICAgICAgICAgICAibmFtZSIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAib3BlcmFuZFR5cGVTcGVjaWZpZXIiIDogewogICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMjA2OjMyLTIwNjo1NCIsCiAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJ7aHR0cDovL2hsNy5vcmcvZmhpcn1Db25kaXRpb25hbERlbGV0ZVN0YXR1cyIsCiAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJOYW1lZFR5cGVTcGVjaWZpZXIiCiAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgfSBdCiAgICAgICAgIH0sIHsKICAgICAgICAgICAgImxvY2F0b3IiIDogIjIwNzoxLTIwNzo2NiIsCiAgICAgICAgICAgICJuYW1lIiA6ICJUb1N0cmluZyIsCiAgICAgICAgICAgICJjb250ZXh0IiA6ICJQYXRpZW50IiwKICAgICAgICAgICAgImFjY2Vzc0xldmVsIiA6ICJQdWJsaWMiLAogICAgICAgICAgICAidHlwZSIgOiAiRnVuY3Rpb25EZWYiLAogICAgICAgICAgICAiZXhwcmVzc2lvbiIgOiB7CiAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIyMDc6NTYtMjA3OjY2IiwKICAgICAgICAgICAgICAgInBhdGgiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgInR5cGUiIDogIlByb3BlcnR5IiwKICAgICAgICAgICAgICAgInNvdXJjZSIgOiB7CiAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIyMDc6NTYtMjA3OjYwIiwKICAgICAgICAgICAgICAgICAgIm5hbWUiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk9wZXJhbmRSZWYiCiAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgfSwKICAgICAgICAgICAgIm9wZXJhbmQiIDogWyB7CiAgICAgICAgICAgICAgICJuYW1lIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICJvcGVyYW5kVHlwZVNwZWNpZmllciIgOiB7CiAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIyMDc6MzItMjA3OjUyIiwKICAgICAgICAgICAgICAgICAgIm5hbWUiIDogIntodHRwOi8vaGw3Lm9yZy9maGlyfUNvbmRpdGlvbmFsUmVhZFN0YXR1cyIsCiAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJOYW1lZFR5cGVTcGVjaWZpZXIiCiAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgfSBdCiAgICAgICAgIH0sIHsKICAgICAgICAgICAgImxvY2F0b3IiIDogIjIwODoxLTIwODo2MyIsCiAgICAgICAgICAgICJuYW1lIiA6ICJUb1N0cmluZyIsCiAgICAgICAgICAgICJjb250ZXh0IiA6ICJQYXRpZW50IiwKICAgICAgICAgICAgImFjY2Vzc0xldmVsIiA6ICJQdWJsaWMiLAogICAgICAgICAgICAidHlwZSIgOiAiRnVuY3Rpb25EZWYiLAogICAgICAgICAgICAiZXhwcmVzc2lvbiIgOiB7CiAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIyMDg6NTMtMjA4OjYzIiwKICAgICAgICAgICAgICAgInBhdGgiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgInR5cGUiIDogIlByb3BlcnR5IiwKICAgICAgICAgICAgICAgInNvdXJjZSIgOiB7CiAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIyMDg6NTMtMjA4OjU3IiwKICAgICAgICAgICAgICAgICAgIm5hbWUiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk9wZXJhbmRSZWYiCiAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgfSwKICAgICAgICAgICAgIm9wZXJhbmQiIDogWyB7CiAgICAgICAgICAgICAgICJuYW1lIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICJvcGVyYW5kVHlwZVNwZWNpZmllciIgOiB7CiAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIyMDg6MzItMjA4OjQ5IiwKICAgICAgICAgICAgICAgICAgIm5hbWUiIDogIntodHRwOi8vaGw3Lm9yZy9maGlyfUNvbnNlbnREYXRhTWVhbmluZyIsCiAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJOYW1lZFR5cGVTcGVjaWZpZXIiCiAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgfSBdCiAgICAgICAgIH0sIHsKICAgICAgICAgICAgImxvY2F0b3IiIDogIjIwOToxLTIwOTo2NSIsCiAgICAgICAgICAgICJuYW1lIiA6ICJUb1N0cmluZyIsCiAgICAgICAgICAgICJjb250ZXh0IiA6ICJQYXRpZW50IiwKICAgICAgICAgICAgImFjY2Vzc0xldmVsIiA6ICJQdWJsaWMiLAogICAgICAgICAgICAidHlwZSIgOiAiRnVuY3Rpb25EZWYiLAogICAgICAgICAgICAiZXhwcmVzc2lvbiIgOiB7CiAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIyMDk6NTUtMjA5OjY1IiwKICAgICAgICAgICAgICAgInBhdGgiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgInR5cGUiIDogIlByb3BlcnR5IiwKICAgICAgICAgICAgICAgInNvdXJjZSIgOiB7CiAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIyMDk6NTUtMjA5OjU5IiwKICAgICAgICAgICAgICAgICAgIm5hbWUiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk9wZXJhbmRSZWYiCiAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgfSwKICAgICAgICAgICAgIm9wZXJhbmQiIDogWyB7CiAgICAgICAgICAgICAgICJuYW1lIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICJvcGVyYW5kVHlwZVNwZWNpZmllciIgOiB7CiAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIyMDk6MzItMjA5OjUxIiwKICAgICAgICAgICAgICAgICAgIm5hbWUiIDogIntodHRwOi8vaGw3Lm9yZy9maGlyfUNvbnNlbnRQcm92aXNpb25UeXBlIiwKICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk5hbWVkVHlwZVNwZWNpZmllciIKICAgICAgICAgICAgICAgfQogICAgICAgICAgICB9IF0KICAgICAgICAgfSwgewogICAgICAgICAgICAibG9jYXRvciIgOiAiMjEwOjEtMjEwOjU3IiwKICAgICAgICAgICAgIm5hbWUiIDogIlRvU3RyaW5nIiwKICAgICAgICAgICAgImNvbnRleHQiIDogIlBhdGllbnQiLAogICAgICAgICAgICAiYWNjZXNzTGV2ZWwiIDogIlB1YmxpYyIsCiAgICAgICAgICAgICJ0eXBlIiA6ICJGdW5jdGlvbkRlZiIsCiAgICAgICAgICAgICJleHByZXNzaW9uIiA6IHsKICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjIxMDo0Ny0yMTA6NTciLAogICAgICAgICAgICAgICAicGF0aCIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAidHlwZSIgOiAiUHJvcGVydHkiLAogICAgICAgICAgICAgICAic291cmNlIiA6IHsKICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjIxMDo0Ny0yMTA6NTEiLAogICAgICAgICAgICAgICAgICAibmFtZSIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAgICAidHlwZSIgOiAiT3BlcmFuZFJlZiIKICAgICAgICAgICAgICAgfQogICAgICAgICAgICB9LAogICAgICAgICAgICAib3BlcmFuZCIgOiBbIHsKICAgICAgICAgICAgICAgIm5hbWUiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgIm9wZXJhbmRUeXBlU3BlY2lmaWVyIiA6IHsKICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjIxMDozMi0yMTA6NDMiLAogICAgICAgICAgICAgICAgICAibmFtZSIgOiAie2h0dHA6Ly9obDcub3JnL2ZoaXJ9Q29uc2VudFN0YXRlIiwKICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk5hbWVkVHlwZVNwZWNpZmllciIKICAgICAgICAgICAgICAgfQogICAgICAgICAgICB9IF0KICAgICAgICAgfSwgewogICAgICAgICAgICAibG9jYXRvciIgOiAiMjExOjEtMjExOjYzIiwKICAgICAgICAgICAgIm5hbWUiIDogIlRvU3RyaW5nIiwKICAgICAgICAgICAgImNvbnRleHQiIDogIlBhdGllbnQiLAogICAgICAgICAgICAiYWNjZXNzTGV2ZWwiIDogIlB1YmxpYyIsCiAgICAgICAgICAgICJ0eXBlIiA6ICJGdW5jdGlvbkRlZiIsCiAgICAgICAgICAgICJleHByZXNzaW9uIiA6IHsKICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjIxMTo1My0yMTE6NjMiLAogICAgICAgICAgICAgICAicGF0aCIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAidHlwZSIgOiAiUHJvcGVydHkiLAogICAgICAgICAgICAgICAic291cmNlIiA6IHsKICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjIxMTo1My0yMTE6NTciLAogICAgICAgICAgICAgICAgICAibmFtZSIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAgICAidHlwZSIgOiAiT3BlcmFuZFJlZiIKICAgICAgICAgICAgICAgfQogICAgICAgICAgICB9LAogICAgICAgICAgICAib3BlcmFuZCIgOiBbIHsKICAgICAgICAgICAgICAgIm5hbWUiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgIm9wZXJhbmRUeXBlU3BlY2lmaWVyIiA6IHsKICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjIxMTozMi0yMTE6NDkiLAogICAgICAgICAgICAgICAgICAibmFtZSIgOiAie2h0dHA6Ly9obDcub3JnL2ZoaXJ9Q29uc3RyYWludFNldmVyaXR5IiwKICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk5hbWVkVHlwZVNwZWNpZmllciIKICAgICAgICAgICAgICAgfQogICAgICAgICAgICB9IF0KICAgICAgICAgfSwgewogICAgICAgICAgICAibG9jYXRvciIgOiAiMjEyOjEtMjEyOjYzIiwKICAgICAgICAgICAgIm5hbWUiIDogIlRvU3RyaW5nIiwKICAgICAgICAgICAgImNvbnRleHQiIDogIlBhdGllbnQiLAogICAgICAgICAgICAiYWNjZXNzTGV2ZWwiIDogIlB1YmxpYyIsCiAgICAgICAgICAgICJ0eXBlIiA6ICJGdW5jdGlvbkRlZiIsCiAgICAgICAgICAgICJleHByZXNzaW9uIiA6IHsKICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjIxMjo1My0yMTI6NjMiLAogICAgICAgICAgICAgICAicGF0aCIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAidHlwZSIgOiAiUHJvcGVydHkiLAogICAgICAgICAgICAgICAic291cmNlIiA6IHsKICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjIxMjo1My0yMTI6NTciLAogICAgICAgICAgICAgICAgICAibmFtZSIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAgICAidHlwZSIgOiAiT3BlcmFuZFJlZiIKICAgICAgICAgICAgICAgfQogICAgICAgICAgICB9LAogICAgICAgICAgICAib3BlcmFuZCIgOiBbIHsKICAgICAgICAgICAgICAgIm5hbWUiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgIm9wZXJhbmRUeXBlU3BlY2lmaWVyIiA6IHsKICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjIxMjozMi0yMTI6NDkiLAogICAgICAgICAgICAgICAgICAibmFtZSIgOiAie2h0dHA6Ly9obDcub3JnL2ZoaXJ9Q29udGFjdFBvaW50U3lzdGVtIiwKICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk5hbWVkVHlwZVNwZWNpZmllciIKICAgICAgICAgICAgICAgfQogICAgICAgICAgICB9IF0KICAgICAgICAgfSwgewogICAgICAgICAgICAibG9jYXRvciIgOiAiMjEzOjEtMjEzOjYwIiwKICAgICAgICAgICAgIm5hbWUiIDogIlRvU3RyaW5nIiwKICAgICAgICAgICAgImNvbnRleHQiIDogIlBhdGllbnQiLAogICAgICAgICAgICAiYWNjZXNzTGV2ZWwiIDogIlB1YmxpYyIsCiAgICAgICAgICAgICJ0eXBlIiA6ICJGdW5jdGlvbkRlZiIsCiAgICAgICAgICAgICJleHByZXNzaW9uIiA6IHsKICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjIxMzo1MC0yMTM6NjAiLAogICAgICAgICAgICAgICAicGF0aCIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAidHlwZSIgOiAiUHJvcGVydHkiLAogICAgICAgICAgICAgICAic291cmNlIiA6IHsKICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjIxMzo1MC0yMTM6NTQiLAogICAgICAgICAgICAgICAgICAibmFtZSIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAgICAidHlwZSIgOiAiT3BlcmFuZFJlZiIKICAgICAgICAgICAgICAgfQogICAgICAgICAgICB9LAogICAgICAgICAgICAib3BlcmFuZCIgOiBbIHsKICAgICAgICAgICAgICAgIm5hbWUiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgIm9wZXJhbmRUeXBlU3BlY2lmaWVyIiA6IHsKICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjIxMzozMi0yMTM6NDYiLAogICAgICAgICAgICAgICAgICAibmFtZSIgOiAie2h0dHA6Ly9obDcub3JnL2ZoaXJ9Q29udGFjdFBvaW50VXNlIiwKICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk5hbWVkVHlwZVNwZWNpZmllciIKICAgICAgICAgICAgICAgfQogICAgICAgICAgICB9IF0KICAgICAgICAgfSwgewogICAgICAgICAgICAibG9jYXRvciIgOiAiMjE0OjEtMjE0OjcwIiwKICAgICAgICAgICAgIm5hbWUiIDogIlRvU3RyaW5nIiwKICAgICAgICAgICAgImNvbnRleHQiIDogIlBhdGllbnQiLAogICAgICAgICAgICAiYWNjZXNzTGV2ZWwiIDogIlB1YmxpYyIsCiAgICAgICAgICAgICJ0eXBlIiA6ICJGdW5jdGlvbkRlZiIsCiAgICAgICAgICAgICJleHByZXNzaW9uIiA6IHsKICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjIxNDo2MC0yMTQ6NzAiLAogICAgICAgICAgICAgICAicGF0aCIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAidHlwZSIgOiAiUHJvcGVydHkiLAogICAgICAgICAgICAgICAic291cmNlIiA6IHsKICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjIxNDo2MC0yMTQ6NjQiLAogICAgICAgICAgICAgICAgICAibmFtZSIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAgICAidHlwZSIgOiAiT3BlcmFuZFJlZiIKICAgICAgICAgICAgICAgfQogICAgICAgICAgICB9LAogICAgICAgICAgICAib3BlcmFuZCIgOiBbIHsKICAgICAgICAgICAgICAgIm5hbWUiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgIm9wZXJhbmRUeXBlU3BlY2lmaWVyIiA6IHsKICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjIxNDozMi0yMTQ6NTYiLAogICAgICAgICAgICAgICAgICAibmFtZSIgOiAie2h0dHA6Ly9obDcub3JnL2ZoaXJ9Q29udHJhY3RQdWJsaWNhdGlvblN0YXR1cyIsCiAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJOYW1lZFR5cGVTcGVjaWZpZXIiCiAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgfSBdCiAgICAgICAgIH0sIHsKICAgICAgICAgICAgImxvY2F0b3IiIDogIjIxNToxLTIxNTo1OSIsCiAgICAgICAgICAgICJuYW1lIiA6ICJUb1N0cmluZyIsCiAgICAgICAgICAgICJjb250ZXh0IiA6ICJQYXRpZW50IiwKICAgICAgICAgICAgImFjY2Vzc0xldmVsIiA6ICJQdWJsaWMiLAogICAgICAgICAgICAidHlwZSIgOiAiRnVuY3Rpb25EZWYiLAogICAgICAgICAgICAiZXhwcmVzc2lvbiIgOiB7CiAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIyMTU6NDktMjE1OjU5IiwKICAgICAgICAgICAgICAgInBhdGgiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgInR5cGUiIDogIlByb3BlcnR5IiwKICAgICAgICAgICAgICAgInNvdXJjZSIgOiB7CiAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIyMTU6NDktMjE1OjUzIiwKICAgICAgICAgICAgICAgICAgIm5hbWUiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk9wZXJhbmRSZWYiCiAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgfSwKICAgICAgICAgICAgIm9wZXJhbmQiIDogWyB7CiAgICAgICAgICAgICAgICJuYW1lIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICJvcGVyYW5kVHlwZVNwZWNpZmllciIgOiB7CiAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIyMTU6MzItMjE1OjQ1IiwKICAgICAgICAgICAgICAgICAgIm5hbWUiIDogIntodHRwOi8vaGw3Lm9yZy9maGlyfUNvbnRyYWN0U3RhdHVzIiwKICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk5hbWVkVHlwZVNwZWNpZmllciIKICAgICAgICAgICAgICAgfQogICAgICAgICAgICB9IF0KICAgICAgICAgfSwgewogICAgICAgICAgICAibG9jYXRvciIgOiAiMjE2OjEtMjE2OjYwIiwKICAgICAgICAgICAgIm5hbWUiIDogIlRvU3RyaW5nIiwKICAgICAgICAgICAgImNvbnRleHQiIDogIlBhdGllbnQiLAogICAgICAgICAgICAiYWNjZXNzTGV2ZWwiIDogIlB1YmxpYyIsCiAgICAgICAgICAgICJ0eXBlIiA6ICJGdW5jdGlvbkRlZiIsCiAgICAgICAgICAgICJleHByZXNzaW9uIiA6IHsKICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjIxNjo1MC0yMTY6NjAiLAogICAgICAgICAgICAgICAicGF0aCIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAidHlwZSIgOiAiUHJvcGVydHkiLAogICAgICAgICAgICAgICAic291cmNlIiA6IHsKICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjIxNjo1MC0yMTY6NTQiLAogICAgICAgICAgICAgICAgICAibmFtZSIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAgICAidHlwZSIgOiAiT3BlcmFuZFJlZiIKICAgICAgICAgICAgICAgfQogICAgICAgICAgICB9LAogICAgICAgICAgICAib3BlcmFuZCIgOiBbIHsKICAgICAgICAgICAgICAgIm5hbWUiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgIm9wZXJhbmRUeXBlU3BlY2lmaWVyIiA6IHsKICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjIxNjozMi0yMTY6NDYiLAogICAgICAgICAgICAgICAgICAibmFtZSIgOiAie2h0dHA6Ly9obDcub3JnL2ZoaXJ9Q29udHJpYnV0b3JUeXBlIiwKICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk5hbWVkVHlwZVNwZWNpZmllciIKICAgICAgICAgICAgICAgfQogICAgICAgICAgICB9IF0KICAgICAgICAgfSwgewogICAgICAgICAgICAibG9jYXRvciIgOiAiMjE3OjEtMjE3OjU5IiwKICAgICAgICAgICAgIm5hbWUiIDogIlRvU3RyaW5nIiwKICAgICAgICAgICAgImNvbnRleHQiIDogIlBhdGllbnQiLAogICAgICAgICAgICAiYWNjZXNzTGV2ZWwiIDogIlB1YmxpYyIsCiAgICAgICAgICAgICJ0eXBlIiA6ICJGdW5jdGlvbkRlZiIsCiAgICAgICAgICAgICJleHByZXNzaW9uIiA6IHsKICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjIxNzo0OS0yMTc6NTkiLAogICAgICAgICAgICAgICAicGF0aCIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAidHlwZSIgOiAiUHJvcGVydHkiLAogICAgICAgICAgICAgICAic291cmNlIiA6IHsKICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjIxNzo0OS0yMTc6NTMiLAogICAgICAgICAgICAgICAgICAibmFtZSIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAgICAidHlwZSIgOiAiT3BlcmFuZFJlZiIKICAgICAgICAgICAgICAgfQogICAgICAgICAgICB9LAogICAgICAgICAgICAib3BlcmFuZCIgOiBbIHsKICAgICAgICAgICAgICAgIm5hbWUiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgIm9wZXJhbmRUeXBlU3BlY2lmaWVyIiA6IHsKICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjIxNzozMi0yMTc6NDUiLAogICAgICAgICAgICAgICAgICAibmFtZSIgOiAie2h0dHA6Ly9obDcub3JnL2ZoaXJ9Q292ZXJhZ2VTdGF0dXMiLAogICAgICAgICAgICAgICAgICAidHlwZSIgOiAiTmFtZWRUeXBlU3BlY2lmaWVyIgogICAgICAgICAgICAgICB9CiAgICAgICAgICAgIH0gXQogICAgICAgICB9LCB7CiAgICAgICAgICAgICJsb2NhdG9yIiA6ICIyMTg6MS0yMTg6NTciLAogICAgICAgICAgICAibmFtZSIgOiAiVG9TdHJpbmciLAogICAgICAgICAgICAiY29udGV4dCIgOiAiUGF0aWVudCIsCiAgICAgICAgICAgICJhY2Nlc3NMZXZlbCIgOiAiUHVibGljIiwKICAgICAgICAgICAgInR5cGUiIDogIkZ1bmN0aW9uRGVmIiwKICAgICAgICAgICAgImV4cHJlc3Npb24iIDogewogICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMjE4OjQ3LTIxODo1NyIsCiAgICAgICAgICAgICAgICJwYXRoIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICJ0eXBlIiA6ICJQcm9wZXJ0eSIsCiAgICAgICAgICAgICAgICJzb3VyY2UiIDogewogICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMjE4OjQ3LTIxODo1MSIsCiAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJPcGVyYW5kUmVmIgogICAgICAgICAgICAgICB9CiAgICAgICAgICAgIH0sCiAgICAgICAgICAgICJvcGVyYW5kIiA6IFsgewogICAgICAgICAgICAgICAibmFtZSIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAib3BlcmFuZFR5cGVTcGVjaWZpZXIiIDogewogICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMjE4OjMyLTIxODo0MyIsCiAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJ7aHR0cDovL2hsNy5vcmcvZmhpcn1DdXJyZW5jeUNvZGUiLAogICAgICAgICAgICAgICAgICAidHlwZSIgOiAiTmFtZWRUeXBlU3BlY2lmaWVyIgogICAgICAgICAgICAgICB9CiAgICAgICAgICAgIH0gXQogICAgICAgICB9LCB7CiAgICAgICAgICAgICJsb2NhdG9yIiA6ICIyMTk6MS0yMTk6NTQiLAogICAgICAgICAgICAibmFtZSIgOiAiVG9TdHJpbmciLAogICAgICAgICAgICAiY29udGV4dCIgOiAiUGF0aWVudCIsCiAgICAgICAgICAgICJhY2Nlc3NMZXZlbCIgOiAiUHVibGljIiwKICAgICAgICAgICAgInR5cGUiIDogIkZ1bmN0aW9uRGVmIiwKICAgICAgICAgICAgImV4cHJlc3Npb24iIDogewogICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMjE5OjQ0LTIxOTo1NCIsCiAgICAgICAgICAgICAgICJwYXRoIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICJ0eXBlIiA6ICJQcm9wZXJ0eSIsCiAgICAgICAgICAgICAgICJzb3VyY2UiIDogewogICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMjE5OjQ0LTIxOTo0OCIsCiAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJPcGVyYW5kUmVmIgogICAgICAgICAgICAgICB9CiAgICAgICAgICAgIH0sCiAgICAgICAgICAgICJvcGVyYW5kIiA6IFsgewogICAgICAgICAgICAgICAibmFtZSIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAib3BlcmFuZFR5cGVTcGVjaWZpZXIiIDogewogICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMjE5OjMyLTIxOTo0MCIsCiAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJ7aHR0cDovL2hsNy5vcmcvZmhpcn1EYXlPZldlZWsiLAogICAgICAgICAgICAgICAgICAidHlwZSIgOiAiTmFtZWRUeXBlU3BlY2lmaWVyIgogICAgICAgICAgICAgICB9CiAgICAgICAgICAgIH0gXQogICAgICAgICB9LCB7CiAgICAgICAgICAgICJsb2NhdG9yIiA6ICIyMjA6MS0yMjA6NTUiLAogICAgICAgICAgICAibmFtZSIgOiAiVG9TdHJpbmciLAogICAgICAgICAgICAiY29udGV4dCIgOiAiUGF0aWVudCIsCiAgICAgICAgICAgICJhY2Nlc3NMZXZlbCIgOiAiUHVibGljIiwKICAgICAgICAgICAgInR5cGUiIDogIkZ1bmN0aW9uRGVmIiwKICAgICAgICAgICAgImV4cHJlc3Npb24iIDogewogICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMjIwOjQ1LTIyMDo1NSIsCiAgICAgICAgICAgICAgICJwYXRoIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICJ0eXBlIiA6ICJQcm9wZXJ0eSIsCiAgICAgICAgICAgICAgICJzb3VyY2UiIDogewogICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMjIwOjQ1LTIyMDo0OSIsCiAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJPcGVyYW5kUmVmIgogICAgICAgICAgICAgICB9CiAgICAgICAgICAgIH0sCiAgICAgICAgICAgICJvcGVyYW5kIiA6IFsgewogICAgICAgICAgICAgICAibmFtZSIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAib3BlcmFuZFR5cGVTcGVjaWZpZXIiIDogewogICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMjIwOjMyLTIyMDo0MSIsCiAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJ7aHR0cDovL2hsNy5vcmcvZmhpcn1EYXlzT2ZXZWVrIiwKICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk5hbWVkVHlwZVNwZWNpZmllciIKICAgICAgICAgICAgICAgfQogICAgICAgICAgICB9IF0KICAgICAgICAgfSwgewogICAgICAgICAgICAibG9jYXRvciIgOiAiMjIxOjEtMjIxOjY2IiwKICAgICAgICAgICAgIm5hbWUiIDogIlRvU3RyaW5nIiwKICAgICAgICAgICAgImNvbnRleHQiIDogIlBhdGllbnQiLAogICAgICAgICAgICAiYWNjZXNzTGV2ZWwiIDogIlB1YmxpYyIsCiAgICAgICAgICAgICJ0eXBlIiA6ICJGdW5jdGlvbkRlZiIsCiAgICAgICAgICAgICJleHByZXNzaW9uIiA6IHsKICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjIyMTo1Ni0yMjE6NjYiLAogICAgICAgICAgICAgICAicGF0aCIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAidHlwZSIgOiAiUHJvcGVydHkiLAogICAgICAgICAgICAgICAic291cmNlIiA6IHsKICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjIyMTo1Ni0yMjE6NjAiLAogICAgICAgICAgICAgICAgICAibmFtZSIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAgICAidHlwZSIgOiAiT3BlcmFuZFJlZiIKICAgICAgICAgICAgICAgfQogICAgICAgICAgICB9LAogICAgICAgICAgICAib3BlcmFuZCIgOiBbIHsKICAgICAgICAgICAgICAgIm5hbWUiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgIm9wZXJhbmRUeXBlU3BlY2lmaWVyIiA6IHsKICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjIyMTozMi0yMjE6NTIiLAogICAgICAgICAgICAgICAgICAibmFtZSIgOiAie2h0dHA6Ly9obDcub3JnL2ZoaXJ9RGV0ZWN0ZWRJc3N1ZVNldmVyaXR5IiwKICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk5hbWVkVHlwZVNwZWNpZmllciIKICAgICAgICAgICAgICAgfQogICAgICAgICAgICB9IF0KICAgICAgICAgfSwgewogICAgICAgICAgICAibG9jYXRvciIgOiAiMjIyOjEtMjIyOjY0IiwKICAgICAgICAgICAgIm5hbWUiIDogIlRvU3RyaW5nIiwKICAgICAgICAgICAgImNvbnRleHQiIDogIlBhdGllbnQiLAogICAgICAgICAgICAiYWNjZXNzTGV2ZWwiIDogIlB1YmxpYyIsCiAgICAgICAgICAgICJ0eXBlIiA6ICJGdW5jdGlvbkRlZiIsCiAgICAgICAgICAgICJleHByZXNzaW9uIiA6IHsKICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjIyMjo1NC0yMjI6NjQiLAogICAgICAgICAgICAgICAicGF0aCIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAidHlwZSIgOiAiUHJvcGVydHkiLAogICAgICAgICAgICAgICAic291cmNlIiA6IHsKICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjIyMjo1NC0yMjI6NTgiLAogICAgICAgICAgICAgICAgICAibmFtZSIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAgICAidHlwZSIgOiAiT3BlcmFuZFJlZiIKICAgICAgICAgICAgICAgfQogICAgICAgICAgICB9LAogICAgICAgICAgICAib3BlcmFuZCIgOiBbIHsKICAgICAgICAgICAgICAgIm5hbWUiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgIm9wZXJhbmRUeXBlU3BlY2lmaWVyIiA6IHsKICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjIyMjozMi0yMjI6NTAiLAogICAgICAgICAgICAgICAgICAibmFtZSIgOiAie2h0dHA6Ly9obDcub3JnL2ZoaXJ9RGV0ZWN0ZWRJc3N1ZVN0YXR1cyIsCiAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJOYW1lZFR5cGVTcGVjaWZpZXIiCiAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgfSBdCiAgICAgICAgIH0sIHsKICAgICAgICAgICAgImxvY2F0b3IiIDogIjIyMzoxLTIyMzo3MyIsCiAgICAgICAgICAgICJuYW1lIiA6ICJUb1N0cmluZyIsCiAgICAgICAgICAgICJjb250ZXh0IiA6ICJQYXRpZW50IiwKICAgICAgICAgICAgImFjY2Vzc0xldmVsIiA6ICJQdWJsaWMiLAogICAgICAgICAgICAidHlwZSIgOiAiRnVuY3Rpb25EZWYiLAogICAgICAgICAgICAiZXhwcmVzc2lvbiIgOiB7CiAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIyMjM6NjMtMjIzOjczIiwKICAgICAgICAgICAgICAgInBhdGgiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgInR5cGUiIDogIlByb3BlcnR5IiwKICAgICAgICAgICAgICAgInNvdXJjZSIgOiB7CiAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIyMjM6NjMtMjIzOjY3IiwKICAgICAgICAgICAgICAgICAgIm5hbWUiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk9wZXJhbmRSZWYiCiAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgfSwKICAgICAgICAgICAgIm9wZXJhbmQiIDogWyB7CiAgICAgICAgICAgICAgICJuYW1lIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICJvcGVyYW5kVHlwZVNwZWNpZmllciIgOiB7CiAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIyMjM6MzItMjIzOjU5IiwKICAgICAgICAgICAgICAgICAgIm5hbWUiIDogIntodHRwOi8vaGw3Lm9yZy9maGlyfURldmljZU1ldHJpY0NhbGlicmF0aW9uU3RhdGUiLAogICAgICAgICAgICAgICAgICAidHlwZSIgOiAiTmFtZWRUeXBlU3BlY2lmaWVyIgogICAgICAgICAgICAgICB9CiAgICAgICAgICAgIH0gXQogICAgICAgICB9LCB7CiAgICAgICAgICAgICJsb2NhdG9yIiA6ICIyMjQ6MS0yMjQ6NzIiLAogICAgICAgICAgICAibmFtZSIgOiAiVG9TdHJpbmciLAogICAgICAgICAgICAiY29udGV4dCIgOiAiUGF0aWVudCIsCiAgICAgICAgICAgICJhY2Nlc3NMZXZlbCIgOiAiUHVibGljIiwKICAgICAgICAgICAgInR5cGUiIDogIkZ1bmN0aW9uRGVmIiwKICAgICAgICAgICAgImV4cHJlc3Npb24iIDogewogICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMjI0OjYyLTIyNDo3MiIsCiAgICAgICAgICAgICAgICJwYXRoIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICJ0eXBlIiA6ICJQcm9wZXJ0eSIsCiAgICAgICAgICAgICAgICJzb3VyY2UiIDogewogICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMjI0OjYyLTIyNDo2NiIsCiAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJPcGVyYW5kUmVmIgogICAgICAgICAgICAgICB9CiAgICAgICAgICAgIH0sCiAgICAgICAgICAgICJvcGVyYW5kIiA6IFsgewogICAgICAgICAgICAgICAibmFtZSIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAib3BlcmFuZFR5cGVTcGVjaWZpZXIiIDogewogICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMjI0OjMyLTIyNDo1OCIsCiAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJ7aHR0cDovL2hsNy5vcmcvZmhpcn1EZXZpY2VNZXRyaWNDYWxpYnJhdGlvblR5cGUiLAogICAgICAgICAgICAgICAgICAidHlwZSIgOiAiTmFtZWRUeXBlU3BlY2lmaWVyIgogICAgICAgICAgICAgICB9CiAgICAgICAgICAgIH0gXQogICAgICAgICB9LCB7CiAgICAgICAgICAgICJsb2NhdG9yIiA6ICIyMjU6MS0yMjU6NjUiLAogICAgICAgICAgICAibmFtZSIgOiAiVG9TdHJpbmciLAogICAgICAgICAgICAiY29udGV4dCIgOiAiUGF0aWVudCIsCiAgICAgICAgICAgICJhY2Nlc3NMZXZlbCIgOiAiUHVibGljIiwKICAgICAgICAgICAgInR5cGUiIDogIkZ1bmN0aW9uRGVmIiwKICAgICAgICAgICAgImV4cHJlc3Npb24iIDogewogICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMjI1OjU1LTIyNTo2NSIsCiAgICAgICAgICAgICAgICJwYXRoIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICJ0eXBlIiA6ICJQcm9wZXJ0eSIsCiAgICAgICAgICAgICAgICJzb3VyY2UiIDogewogICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMjI1OjU1LTIyNTo1OSIsCiAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJPcGVyYW5kUmVmIgogICAgICAgICAgICAgICB9CiAgICAgICAgICAgIH0sCiAgICAgICAgICAgICJvcGVyYW5kIiA6IFsgewogICAgICAgICAgICAgICAibmFtZSIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAib3BlcmFuZFR5cGVTcGVjaWZpZXIiIDogewogICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMjI1OjMyLTIyNTo1MSIsCiAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJ7aHR0cDovL2hsNy5vcmcvZmhpcn1EZXZpY2VNZXRyaWNDYXRlZ29yeSIsCiAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJOYW1lZFR5cGVTcGVjaWZpZXIiCiAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgfSBdCiAgICAgICAgIH0sIHsKICAgICAgICAgICAgImxvY2F0b3IiIDogIjIyNjoxLTIyNjo2MiIsCiAgICAgICAgICAgICJuYW1lIiA6ICJUb1N0cmluZyIsCiAgICAgICAgICAgICJjb250ZXh0IiA6ICJQYXRpZW50IiwKICAgICAgICAgICAgImFjY2Vzc0xldmVsIiA6ICJQdWJsaWMiLAogICAgICAgICAgICAidHlwZSIgOiAiRnVuY3Rpb25EZWYiLAogICAgICAgICAgICAiZXhwcmVzc2lvbiIgOiB7CiAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIyMjY6NTItMjI2OjYyIiwKICAgICAgICAgICAgICAgInBhdGgiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgInR5cGUiIDogIlByb3BlcnR5IiwKICAgICAgICAgICAgICAgInNvdXJjZSIgOiB7CiAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIyMjY6NTItMjI2OjU2IiwKICAgICAgICAgICAgICAgICAgIm5hbWUiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk9wZXJhbmRSZWYiCiAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgfSwKICAgICAgICAgICAgIm9wZXJhbmQiIDogWyB7CiAgICAgICAgICAgICAgICJuYW1lIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICJvcGVyYW5kVHlwZVNwZWNpZmllciIgOiB7CiAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIyMjY6MzItMjI2OjQ4IiwKICAgICAgICAgICAgICAgICAgIm5hbWUiIDogIntodHRwOi8vaGw3Lm9yZy9maGlyfURldmljZU1ldHJpY0NvbG9yIiwKICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk5hbWVkVHlwZVNwZWNpZmllciIKICAgICAgICAgICAgICAgfQogICAgICAgICAgICB9IF0KICAgICAgICAgfSwgewogICAgICAgICAgICAibG9jYXRvciIgOiAiMjI3OjEtMjI3Ojc0IiwKICAgICAgICAgICAgIm5hbWUiIDogIlRvU3RyaW5nIiwKICAgICAgICAgICAgImNvbnRleHQiIDogIlBhdGllbnQiLAogICAgICAgICAgICAiYWNjZXNzTGV2ZWwiIDogIlB1YmxpYyIsCiAgICAgICAgICAgICJ0eXBlIiA6ICJGdW5jdGlvbkRlZiIsCiAgICAgICAgICAgICJleHByZXNzaW9uIiA6IHsKICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjIyNzo2NC0yMjc6NzQiLAogICAgICAgICAgICAgICAicGF0aCIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAidHlwZSIgOiAiUHJvcGVydHkiLAogICAgICAgICAgICAgICAic291cmNlIiA6IHsKICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjIyNzo2NC0yMjc6NjgiLAogICAgICAgICAgICAgICAgICAibmFtZSIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAgICAidHlwZSIgOiAiT3BlcmFuZFJlZiIKICAgICAgICAgICAgICAgfQogICAgICAgICAgICB9LAogICAgICAgICAgICAib3BlcmFuZCIgOiBbIHsKICAgICAgICAgICAgICAgIm5hbWUiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgIm9wZXJhbmRUeXBlU3BlY2lmaWVyIiA6IHsKICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjIyNzozMi0yMjc6NjAiLAogICAgICAgICAgICAgICAgICAibmFtZSIgOiAie2h0dHA6Ly9obDcub3JnL2ZoaXJ9RGV2aWNlTWV0cmljT3BlcmF0aW9uYWxTdGF0dXMiLAogICAgICAgICAgICAgICAgICAidHlwZSIgOiAiTmFtZWRUeXBlU3BlY2lmaWVyIgogICAgICAgICAgICAgICB9CiAgICAgICAgICAgIH0gXQogICAgICAgICB9LCB7CiAgICAgICAgICAgICJsb2NhdG9yIiA6ICIyMjg6MS0yMjg6NTkiLAogICAgICAgICAgICAibmFtZSIgOiAiVG9TdHJpbmciLAogICAgICAgICAgICAiY29udGV4dCIgOiAiUGF0aWVudCIsCiAgICAgICAgICAgICJhY2Nlc3NMZXZlbCIgOiAiUHVibGljIiwKICAgICAgICAgICAgInR5cGUiIDogIkZ1bmN0aW9uRGVmIiwKICAgICAgICAgICAgImV4cHJlc3Npb24iIDogewogICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMjI4OjQ5LTIyODo1OSIsCiAgICAgICAgICAgICAgICJwYXRoIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICJ0eXBlIiA6ICJQcm9wZXJ0eSIsCiAgICAgICAgICAgICAgICJzb3VyY2UiIDogewogICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMjI4OjQ5LTIyODo1MyIsCiAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJPcGVyYW5kUmVmIgogICAgICAgICAgICAgICB9CiAgICAgICAgICAgIH0sCiAgICAgICAgICAgICJvcGVyYW5kIiA6IFsgewogICAgICAgICAgICAgICAibmFtZSIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAib3BlcmFuZFR5cGVTcGVjaWZpZXIiIDogewogICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMjI4OjMyLTIyODo0NSIsCiAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJ7aHR0cDovL2hsNy5vcmcvZmhpcn1EZXZpY2VOYW1lVHlwZSIsCiAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJOYW1lZFR5cGVTcGVjaWZpZXIiCiAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgfSBdCiAgICAgICAgIH0sIHsKICAgICAgICAgICAgImxvY2F0b3IiIDogIjIyOToxLTIyOTo2NCIsCiAgICAgICAgICAgICJuYW1lIiA6ICJUb1N0cmluZyIsCiAgICAgICAgICAgICJjb250ZXh0IiA6ICJQYXRpZW50IiwKICAgICAgICAgICAgImFjY2Vzc0xldmVsIiA6ICJQdWJsaWMiLAogICAgICAgICAgICAidHlwZSIgOiAiRnVuY3Rpb25EZWYiLAogICAgICAgICAgICAiZXhwcmVzc2lvbiIgOiB7CiAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIyMjk6NTQtMjI5OjY0IiwKICAgICAgICAgICAgICAgInBhdGgiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgInR5cGUiIDogIlByb3BlcnR5IiwKICAgICAgICAgICAgICAgInNvdXJjZSIgOiB7CiAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIyMjk6NTQtMjI5OjU4IiwKICAgICAgICAgICAgICAgICAgIm5hbWUiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk9wZXJhbmRSZWYiCiAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgfSwKICAgICAgICAgICAgIm9wZXJhbmQiIDogWyB7CiAgICAgICAgICAgICAgICJuYW1lIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICJvcGVyYW5kVHlwZVNwZWNpZmllciIgOiB7CiAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIyMjk6MzItMjI5OjUwIiwKICAgICAgICAgICAgICAgICAgIm5hbWUiIDogIntodHRwOi8vaGw3Lm9yZy9maGlyfURldmljZVJlcXVlc3RTdGF0dXMiLAogICAgICAgICAgICAgICAgICAidHlwZSIgOiAiTmFtZWRUeXBlU3BlY2lmaWVyIgogICAgICAgICAgICAgICB9CiAgICAgICAgICAgIH0gXQogICAgICAgICB9LCB7CiAgICAgICAgICAgICJsb2NhdG9yIiA6ICIyMzA6MS0yMzA6NjkiLAogICAgICAgICAgICAibmFtZSIgOiAiVG9TdHJpbmciLAogICAgICAgICAgICAiY29udGV4dCIgOiAiUGF0aWVudCIsCiAgICAgICAgICAgICJhY2Nlc3NMZXZlbCIgOiAiUHVibGljIiwKICAgICAgICAgICAgInR5cGUiIDogIkZ1bmN0aW9uRGVmIiwKICAgICAgICAgICAgImV4cHJlc3Npb24iIDogewogICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMjMwOjU5LTIzMDo2OSIsCiAgICAgICAgICAgICAgICJwYXRoIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICJ0eXBlIiA6ICJQcm9wZXJ0eSIsCiAgICAgICAgICAgICAgICJzb3VyY2UiIDogewogICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMjMwOjU5LTIzMDo2MyIsCiAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJPcGVyYW5kUmVmIgogICAgICAgICAgICAgICB9CiAgICAgICAgICAgIH0sCiAgICAgICAgICAgICJvcGVyYW5kIiA6IFsgewogICAgICAgICAgICAgICAibmFtZSIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAib3BlcmFuZFR5cGVTcGVjaWZpZXIiIDogewogICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMjMwOjMyLTIzMDo1NSIsCiAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJ7aHR0cDovL2hsNy5vcmcvZmhpcn1EZXZpY2VVc2VTdGF0ZW1lbnRTdGF0dXMiLAogICAgICAgICAgICAgICAgICAidHlwZSIgOiAiTmFtZWRUeXBlU3BlY2lmaWVyIgogICAgICAgICAgICAgICB9CiAgICAgICAgICAgIH0gXQogICAgICAgICB9LCB7CiAgICAgICAgICAgICJsb2NhdG9yIiA6ICIyMzE6MS0yMzE6NjciLAogICAgICAgICAgICAibmFtZSIgOiAiVG9TdHJpbmciLAogICAgICAgICAgICAiY29udGV4dCIgOiAiUGF0aWVudCIsCiAgICAgICAgICAgICJhY2Nlc3NMZXZlbCIgOiAiUHVibGljIiwKICAgICAgICAgICAgInR5cGUiIDogIkZ1bmN0aW9uRGVmIiwKICAgICAgICAgICAgImV4cHJlc3Npb24iIDogewogICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMjMxOjU3LTIzMTo2NyIsCiAgICAgICAgICAgICAgICJwYXRoIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICJ0eXBlIiA6ICJQcm9wZXJ0eSIsCiAgICAgICAgICAgICAgICJzb3VyY2UiIDogewogICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMjMxOjU3LTIzMTo2MSIsCiAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJPcGVyYW5kUmVmIgogICAgICAgICAgICAgICB9CiAgICAgICAgICAgIH0sCiAgICAgICAgICAgICJvcGVyYW5kIiA6IFsgewogICAgICAgICAgICAgICAibmFtZSIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAib3BlcmFuZFR5cGVTcGVjaWZpZXIiIDogewogICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMjMxOjMyLTIzMTo1MyIsCiAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJ7aHR0cDovL2hsNy5vcmcvZmhpcn1EaWFnbm9zdGljUmVwb3J0U3RhdHVzIiwKICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk5hbWVkVHlwZVNwZWNpZmllciIKICAgICAgICAgICAgICAgfQogICAgICAgICAgICB9IF0KICAgICAgICAgfSwgewogICAgICAgICAgICAibG9jYXRvciIgOiAiMjMyOjEtMjMyOjYyIiwKICAgICAgICAgICAgIm5hbWUiIDogIlRvU3RyaW5nIiwKICAgICAgICAgICAgImNvbnRleHQiIDogIlBhdGllbnQiLAogICAgICAgICAgICAiYWNjZXNzTGV2ZWwiIDogIlB1YmxpYyIsCiAgICAgICAgICAgICJ0eXBlIiA6ICJGdW5jdGlvbkRlZiIsCiAgICAgICAgICAgICJleHByZXNzaW9uIiA6IHsKICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjIzMjo1Mi0yMzI6NjIiLAogICAgICAgICAgICAgICAicGF0aCIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAidHlwZSIgOiAiUHJvcGVydHkiLAogICAgICAgICAgICAgICAic291cmNlIiA6IHsKICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjIzMjo1Mi0yMzI6NTYiLAogICAgICAgICAgICAgICAgICAibmFtZSIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAgICAidHlwZSIgOiAiT3BlcmFuZFJlZiIKICAgICAgICAgICAgICAgfQogICAgICAgICAgICB9LAogICAgICAgICAgICAib3BlcmFuZCIgOiBbIHsKICAgICAgICAgICAgICAgIm5hbWUiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgIm9wZXJhbmRUeXBlU3BlY2lmaWVyIiA6IHsKICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjIzMjozMi0yMzI6NDgiLAogICAgICAgICAgICAgICAgICAibmFtZSIgOiAie2h0dHA6Ly9obDcub3JnL2ZoaXJ9RGlzY3JpbWluYXRvclR5cGUiLAogICAgICAgICAgICAgICAgICAidHlwZSIgOiAiTmFtZWRUeXBlU3BlY2lmaWVyIgogICAgICAgICAgICAgICB9CiAgICAgICAgICAgIH0gXQogICAgICAgICB9LCB7CiAgICAgICAgICAgICJsb2NhdG9yIiA6ICIyMzM6MS0yMzM6NjgiLAogICAgICAgICAgICAibmFtZSIgOiAiVG9TdHJpbmciLAogICAgICAgICAgICAiY29udGV4dCIgOiAiUGF0aWVudCIsCiAgICAgICAgICAgICJhY2Nlc3NMZXZlbCIgOiAiUHVibGljIiwKICAgICAgICAgICAgInR5cGUiIDogIkZ1bmN0aW9uRGVmIiwKICAgICAgICAgICAgImV4cHJlc3Npb24iIDogewogICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMjMzOjU4LTIzMzo2OCIsCiAgICAgICAgICAgICAgICJwYXRoIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICJ0eXBlIiA6ICJQcm9wZXJ0eSIsCiAgICAgICAgICAgICAgICJzb3VyY2UiIDogewogICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMjMzOjU4LTIzMzo2MiIsCiAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJPcGVyYW5kUmVmIgogICAgICAgICAgICAgICB9CiAgICAgICAgICAgIH0sCiAgICAgICAgICAgICJvcGVyYW5kIiA6IFsgewogICAgICAgICAgICAgICAibmFtZSIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAib3BlcmFuZFR5cGVTcGVjaWZpZXIiIDogewogICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMjMzOjMyLTIzMzo1NCIsCiAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJ7aHR0cDovL2hsNy5vcmcvZmhpcn1Eb2N1bWVudENvbmZpZGVudGlhbGl0eSIsCiAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJOYW1lZFR5cGVTcGVjaWZpZXIiCiAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgfSBdCiAgICAgICAgIH0sIHsKICAgICAgICAgICAgImxvY2F0b3IiIDogIjIzNDoxLTIzNDo1NyIsCiAgICAgICAgICAgICJuYW1lIiA6ICJUb1N0cmluZyIsCiAgICAgICAgICAgICJjb250ZXh0IiA6ICJQYXRpZW50IiwKICAgICAgICAgICAgImFjY2Vzc0xldmVsIiA6ICJQdWJsaWMiLAogICAgICAgICAgICAidHlwZSIgOiAiRnVuY3Rpb25EZWYiLAogICAgICAgICAgICAiZXhwcmVzc2lvbiIgOiB7CiAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIyMzQ6NDctMjM0OjU3IiwKICAgICAgICAgICAgICAgInBhdGgiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgInR5cGUiIDogIlByb3BlcnR5IiwKICAgICAgICAgICAgICAgInNvdXJjZSIgOiB7CiAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIyMzQ6NDctMjM0OjUxIiwKICAgICAgICAgICAgICAgICAgIm5hbWUiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk9wZXJhbmRSZWYiCiAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgfSwKICAgICAgICAgICAgIm9wZXJhbmQiIDogWyB7CiAgICAgICAgICAgICAgICJuYW1lIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICJvcGVyYW5kVHlwZVNwZWNpZmllciIgOiB7CiAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIyMzQ6MzItMjM0OjQzIiwKICAgICAgICAgICAgICAgICAgIm5hbWUiIDogIntodHRwOi8vaGw3Lm9yZy9maGlyfURvY3VtZW50TW9kZSIsCiAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJOYW1lZFR5cGVTcGVjaWZpZXIiCiAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgfSBdCiAgICAgICAgIH0sIHsKICAgICAgICAgICAgImxvY2F0b3IiIDogIjIzNToxLTIzNTo2OCIsCiAgICAgICAgICAgICJuYW1lIiA6ICJUb1N0cmluZyIsCiAgICAgICAgICAgICJjb250ZXh0IiA6ICJQYXRpZW50IiwKICAgICAgICAgICAgImFjY2Vzc0xldmVsIiA6ICJQdWJsaWMiLAogICAgICAgICAgICAidHlwZSIgOiAiRnVuY3Rpb25EZWYiLAogICAgICAgICAgICAiZXhwcmVzc2lvbiIgOiB7CiAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIyMzU6NTgtMjM1OjY4IiwKICAgICAgICAgICAgICAgInBhdGgiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgInR5cGUiIDogIlByb3BlcnR5IiwKICAgICAgICAgICAgICAgInNvdXJjZSIgOiB7CiAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIyMzU6NTgtMjM1OjYyIiwKICAgICAgICAgICAgICAgICAgIm5hbWUiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk9wZXJhbmRSZWYiCiAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgfSwKICAgICAgICAgICAgIm9wZXJhbmQiIDogWyB7CiAgICAgICAgICAgICAgICJuYW1lIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICJvcGVyYW5kVHlwZVNwZWNpZmllciIgOiB7CiAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIyMzU6MzItMjM1OjU0IiwKICAgICAgICAgICAgICAgICAgIm5hbWUiIDogIntodHRwOi8vaGw3Lm9yZy9maGlyfURvY3VtZW50UmVmZXJlbmNlU3RhdHVzIiwKICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk5hbWVkVHlwZVNwZWNpZmllciIKICAgICAgICAgICAgICAgfQogICAgICAgICAgICB9IF0KICAgICAgICAgfSwgewogICAgICAgICAgICAibG9jYXRvciIgOiAiMjM2OjEtMjM2OjY5IiwKICAgICAgICAgICAgIm5hbWUiIDogIlRvU3RyaW5nIiwKICAgICAgICAgICAgImNvbnRleHQiIDogIlBhdGllbnQiLAogICAgICAgICAgICAiYWNjZXNzTGV2ZWwiIDogIlB1YmxpYyIsCiAgICAgICAgICAgICJ0eXBlIiA6ICJGdW5jdGlvbkRlZiIsCiAgICAgICAgICAgICJleHByZXNzaW9uIiA6IHsKICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjIzNjo1OS0yMzY6NjkiLAogICAgICAgICAgICAgICAicGF0aCIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAidHlwZSIgOiAiUHJvcGVydHkiLAogICAgICAgICAgICAgICAic291cmNlIiA6IHsKICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjIzNjo1OS0yMzY6NjMiLAogICAgICAgICAgICAgICAgICAibmFtZSIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAgICAidHlwZSIgOiAiT3BlcmFuZFJlZiIKICAgICAgICAgICAgICAgfQogICAgICAgICAgICB9LAogICAgICAgICAgICAib3BlcmFuZCIgOiBbIHsKICAgICAgICAgICAgICAgIm5hbWUiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgIm9wZXJhbmRUeXBlU3BlY2lmaWVyIiA6IHsKICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjIzNjozMi0yMzY6NTUiLAogICAgICAgICAgICAgICAgICAibmFtZSIgOiAie2h0dHA6Ly9obDcub3JnL2ZoaXJ9RG9jdW1lbnRSZWxhdGlvbnNoaXBUeXBlIiwKICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk5hbWVkVHlwZVNwZWNpZmllciIKICAgICAgICAgICAgICAgfQogICAgICAgICAgICB9IF0KICAgICAgICAgfSwgewogICAgICAgICAgICAibG9jYXRvciIgOiAiMjM3OjEtMjM3OjcwIiwKICAgICAgICAgICAgIm5hbWUiIDogIlRvU3RyaW5nIiwKICAgICAgICAgICAgImNvbnRleHQiIDogIlBhdGllbnQiLAogICAgICAgICAgICAiYWNjZXNzTGV2ZWwiIDogIlB1YmxpYyIsCiAgICAgICAgICAgICJ0eXBlIiA6ICJGdW5jdGlvbkRlZiIsCiAgICAgICAgICAgICJleHByZXNzaW9uIiA6IHsKICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjIzNzo2MC0yMzc6NzAiLAogICAgICAgICAgICAgICAicGF0aCIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAidHlwZSIgOiAiUHJvcGVydHkiLAogICAgICAgICAgICAgICAic291cmNlIiA6IHsKICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjIzNzo2MC0yMzc6NjQiLAogICAgICAgICAgICAgICAgICAibmFtZSIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAgICAidHlwZSIgOiAiT3BlcmFuZFJlZiIKICAgICAgICAgICAgICAgfQogICAgICAgICAgICB9LAogICAgICAgICAgICAib3BlcmFuZCIgOiBbIHsKICAgICAgICAgICAgICAgIm5hbWUiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgIm9wZXJhbmRUeXBlU3BlY2lmaWVyIiA6IHsKICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjIzNzozMi0yMzc6NTYiLAogICAgICAgICAgICAgICAgICAibmFtZSIgOiAie2h0dHA6Ly9obDcub3JnL2ZoaXJ9RWxpZ2liaWxpdHlSZXF1ZXN0UHVycG9zZSIsCiAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJOYW1lZFR5cGVTcGVjaWZpZXIiCiAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgfSBdCiAgICAgICAgIH0sIHsKICAgICAgICAgICAgImxvY2F0b3IiIDogIjIzODoxLTIzODo2OSIsCiAgICAgICAgICAgICJuYW1lIiA6ICJUb1N0cmluZyIsCiAgICAgICAgICAgICJjb250ZXh0IiA6ICJQYXRpZW50IiwKICAgICAgICAgICAgImFjY2Vzc0xldmVsIiA6ICJQdWJsaWMiLAogICAgICAgICAgICAidHlwZSIgOiAiRnVuY3Rpb25EZWYiLAogICAgICAgICAgICAiZXhwcmVzc2lvbiIgOiB7CiAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIyMzg6NTktMjM4OjY5IiwKICAgICAgICAgICAgICAgInBhdGgiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgInR5cGUiIDogIlByb3BlcnR5IiwKICAgICAgICAgICAgICAgInNvdXJjZSIgOiB7CiAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIyMzg6NTktMjM4OjYzIiwKICAgICAgICAgICAgICAgICAgIm5hbWUiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk9wZXJhbmRSZWYiCiAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgfSwKICAgICAgICAgICAgIm9wZXJhbmQiIDogWyB7CiAgICAgICAgICAgICAgICJuYW1lIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICJvcGVyYW5kVHlwZVNwZWNpZmllciIgOiB7CiAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIyMzg6MzItMjM4OjU1IiwKICAgICAgICAgICAgICAgICAgIm5hbWUiIDogIntodHRwOi8vaGw3Lm9yZy9maGlyfUVsaWdpYmlsaXR5UmVxdWVzdFN0YXR1cyIsCiAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJOYW1lZFR5cGVTcGVjaWZpZXIiCiAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgfSBdCiAgICAgICAgIH0sIHsKICAgICAgICAgICAgImxvY2F0b3IiIDogIjIzOToxLTIzOTo3MSIsCiAgICAgICAgICAgICJuYW1lIiA6ICJUb1N0cmluZyIsCiAgICAgICAgICAgICJjb250ZXh0IiA6ICJQYXRpZW50IiwKICAgICAgICAgICAgImFjY2Vzc0xldmVsIiA6ICJQdWJsaWMiLAogICAgICAgICAgICAidHlwZSIgOiAiRnVuY3Rpb25EZWYiLAogICAgICAgICAgICAiZXhwcmVzc2lvbiIgOiB7CiAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIyMzk6NjEtMjM5OjcxIiwKICAgICAgICAgICAgICAgInBhdGgiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgInR5cGUiIDogIlByb3BlcnR5IiwKICAgICAgICAgICAgICAgInNvdXJjZSIgOiB7CiAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIyMzk6NjEtMjM5OjY1IiwKICAgICAgICAgICAgICAgICAgIm5hbWUiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk9wZXJhbmRSZWYiCiAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgfSwKICAgICAgICAgICAgIm9wZXJhbmQiIDogWyB7CiAgICAgICAgICAgICAgICJuYW1lIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICJvcGVyYW5kVHlwZVNwZWNpZmllciIgOiB7CiAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIyMzk6MzItMjM5OjU3IiwKICAgICAgICAgICAgICAgICAgIm5hbWUiIDogIntodHRwOi8vaGw3Lm9yZy9maGlyfUVsaWdpYmlsaXR5UmVzcG9uc2VQdXJwb3NlIiwKICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk5hbWVkVHlwZVNwZWNpZmllciIKICAgICAgICAgICAgICAgfQogICAgICAgICAgICB9IF0KICAgICAgICAgfSwgewogICAgICAgICAgICAibG9jYXRvciIgOiAiMjQwOjEtMjQwOjcwIiwKICAgICAgICAgICAgIm5hbWUiIDogIlRvU3RyaW5nIiwKICAgICAgICAgICAgImNvbnRleHQiIDogIlBhdGllbnQiLAogICAgICAgICAgICAiYWNjZXNzTGV2ZWwiIDogIlB1YmxpYyIsCiAgICAgICAgICAgICJ0eXBlIiA6ICJGdW5jdGlvbkRlZiIsCiAgICAgICAgICAgICJleHByZXNzaW9uIiA6IHsKICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjI0MDo2MC0yNDA6NzAiLAogICAgICAgICAgICAgICAicGF0aCIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAidHlwZSIgOiAiUHJvcGVydHkiLAogICAgICAgICAgICAgICAic291cmNlIiA6IHsKICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjI0MDo2MC0yNDA6NjQiLAogICAgICAgICAgICAgICAgICAibmFtZSIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAgICAidHlwZSIgOiAiT3BlcmFuZFJlZiIKICAgICAgICAgICAgICAgfQogICAgICAgICAgICB9LAogICAgICAgICAgICAib3BlcmFuZCIgOiBbIHsKICAgICAgICAgICAgICAgIm5hbWUiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgIm9wZXJhbmRUeXBlU3BlY2lmaWVyIiA6IHsKICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjI0MDozMi0yNDA6NTYiLAogICAgICAgICAgICAgICAgICAibmFtZSIgOiAie2h0dHA6Ly9obDcub3JnL2ZoaXJ9RWxpZ2liaWxpdHlSZXNwb25zZVN0YXR1cyIsCiAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJOYW1lZFR5cGVTcGVjaWZpZXIiCiAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgfSBdCiAgICAgICAgIH0sIHsKICAgICAgICAgICAgImxvY2F0b3IiIDogIjI0MToxLTI0MTo2MyIsCiAgICAgICAgICAgICJuYW1lIiA6ICJUb1N0cmluZyIsCiAgICAgICAgICAgICJjb250ZXh0IiA6ICJQYXRpZW50IiwKICAgICAgICAgICAgImFjY2Vzc0xldmVsIiA6ICJQdWJsaWMiLAogICAgICAgICAgICAidHlwZSIgOiAiRnVuY3Rpb25EZWYiLAogICAgICAgICAgICAiZXhwcmVzc2lvbiIgOiB7CiAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIyNDE6NTMtMjQxOjYzIiwKICAgICAgICAgICAgICAgInBhdGgiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgInR5cGUiIDogIlByb3BlcnR5IiwKICAgICAgICAgICAgICAgInNvdXJjZSIgOiB7CiAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIyNDE6NTMtMjQxOjU3IiwKICAgICAgICAgICAgICAgICAgIm5hbWUiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk9wZXJhbmRSZWYiCiAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgfSwKICAgICAgICAgICAgIm9wZXJhbmQiIDogWyB7CiAgICAgICAgICAgICAgICJuYW1lIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICJvcGVyYW5kVHlwZVNwZWNpZmllciIgOiB7CiAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIyNDE6MzItMjQxOjQ5IiwKICAgICAgICAgICAgICAgICAgIm5hbWUiIDogIntodHRwOi8vaGw3Lm9yZy9maGlyfUVuYWJsZVdoZW5CZWhhdmlvciIsCiAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJOYW1lZFR5cGVTcGVjaWZpZXIiCiAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgfSBdCiAgICAgICAgIH0sIHsKICAgICAgICAgICAgImxvY2F0b3IiIDogIjI0MjoxLTI0Mjo2OCIsCiAgICAgICAgICAgICJuYW1lIiA6ICJUb1N0cmluZyIsCiAgICAgICAgICAgICJjb250ZXh0IiA6ICJQYXRpZW50IiwKICAgICAgICAgICAgImFjY2Vzc0xldmVsIiA6ICJQdWJsaWMiLAogICAgICAgICAgICAidHlwZSIgOiAiRnVuY3Rpb25EZWYiLAogICAgICAgICAgICAiZXhwcmVzc2lvbiIgOiB7CiAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIyNDI6NTgtMjQyOjY4IiwKICAgICAgICAgICAgICAgInBhdGgiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgInR5cGUiIDogIlByb3BlcnR5IiwKICAgICAgICAgICAgICAgInNvdXJjZSIgOiB7CiAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIyNDI6NTgtMjQyOjYyIiwKICAgICAgICAgICAgICAgICAgIm5hbWUiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk9wZXJhbmRSZWYiCiAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgfSwKICAgICAgICAgICAgIm9wZXJhbmQiIDogWyB7CiAgICAgICAgICAgICAgICJuYW1lIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICJvcGVyYW5kVHlwZVNwZWNpZmllciIgOiB7CiAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIyNDI6MzItMjQyOjU0IiwKICAgICAgICAgICAgICAgICAgIm5hbWUiIDogIntodHRwOi8vaGw3Lm9yZy9maGlyfUVuY291bnRlckxvY2F0aW9uU3RhdHVzIiwKICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk5hbWVkVHlwZVNwZWNpZmllciIKICAgICAgICAgICAgICAgfQogICAgICAgICAgICB9IF0KICAgICAgICAgfSwgewogICAgICAgICAgICAibG9jYXRvciIgOiAiMjQzOjEtMjQzOjYwIiwKICAgICAgICAgICAgIm5hbWUiIDogIlRvU3RyaW5nIiwKICAgICAgICAgICAgImNvbnRleHQiIDogIlBhdGllbnQiLAogICAgICAgICAgICAiYWNjZXNzTGV2ZWwiIDogIlB1YmxpYyIsCiAgICAgICAgICAgICJ0eXBlIiA6ICJGdW5jdGlvbkRlZiIsCiAgICAgICAgICAgICJleHByZXNzaW9uIiA6IHsKICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjI0Mzo1MC0yNDM6NjAiLAogICAgICAgICAgICAgICAicGF0aCIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAidHlwZSIgOiAiUHJvcGVydHkiLAogICAgICAgICAgICAgICAic291cmNlIiA6IHsKICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjI0Mzo1MC0yNDM6NTQiLAogICAgICAgICAgICAgICAgICAibmFtZSIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAgICAidHlwZSIgOiAiT3BlcmFuZFJlZiIKICAgICAgICAgICAgICAgfQogICAgICAgICAgICB9LAogICAgICAgICAgICAib3BlcmFuZCIgOiBbIHsKICAgICAgICAgICAgICAgIm5hbWUiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgIm9wZXJhbmRUeXBlU3BlY2lmaWVyIiA6IHsKICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjI0MzozMi0yNDM6NDYiLAogICAgICAgICAgICAgICAgICAibmFtZSIgOiAie2h0dHA6Ly9obDcub3JnL2ZoaXJ9RW5jb3VudGVyU3RhdHVzIiwKICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk5hbWVkVHlwZVNwZWNpZmllciIKICAgICAgICAgICAgICAgfQogICAgICAgICAgICB9IF0KICAgICAgICAgfSwgewogICAgICAgICAgICAibG9jYXRvciIgOiAiMjQ0OjEtMjQ0OjU5IiwKICAgICAgICAgICAgIm5hbWUiIDogIlRvU3RyaW5nIiwKICAgICAgICAgICAgImNvbnRleHQiIDogIlBhdGllbnQiLAogICAgICAgICAgICAiYWNjZXNzTGV2ZWwiIDogIlB1YmxpYyIsCiAgICAgICAgICAgICJ0eXBlIiA6ICJGdW5jdGlvbkRlZiIsCiAgICAgICAgICAgICJleHByZXNzaW9uIiA6IHsKICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjI0NDo0OS0yNDQ6NTkiLAogICAgICAgICAgICAgICAicGF0aCIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAidHlwZSIgOiAiUHJvcGVydHkiLAogICAgICAgICAgICAgICAic291cmNlIiA6IHsKICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjI0NDo0OS0yNDQ6NTMiLAogICAgICAgICAgICAgICAgICAibmFtZSIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAgICAidHlwZSIgOiAiT3BlcmFuZFJlZiIKICAgICAgICAgICAgICAgfQogICAgICAgICAgICB9LAogICAgICAgICAgICAib3BlcmFuZCIgOiBbIHsKICAgICAgICAgICAgICAgIm5hbWUiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgIm9wZXJhbmRUeXBlU3BlY2lmaWVyIiA6IHsKICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjI0NDozMi0yNDQ6NDUiLAogICAgICAgICAgICAgICAgICAibmFtZSIgOiAie2h0dHA6Ly9obDcub3JnL2ZoaXJ9RW5kcG9pbnRTdGF0dXMiLAogICAgICAgICAgICAgICAgICAidHlwZSIgOiAiTmFtZWRUeXBlU3BlY2lmaWVyIgogICAgICAgICAgICAgICB9CiAgICAgICAgICAgIH0gXQogICAgICAgICB9LCB7CiAgICAgICAgICAgICJsb2NhdG9yIiA6ICIyNDU6MS0yNDU6NjgiLAogICAgICAgICAgICAibmFtZSIgOiAiVG9TdHJpbmciLAogICAgICAgICAgICAiY29udGV4dCIgOiAiUGF0aWVudCIsCiAgICAgICAgICAgICJhY2Nlc3NMZXZlbCIgOiAiUHVibGljIiwKICAgICAgICAgICAgInR5cGUiIDogIkZ1bmN0aW9uRGVmIiwKICAgICAgICAgICAgImV4cHJlc3Npb24iIDogewogICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMjQ1OjU4LTI0NTo2OCIsCiAgICAgICAgICAgICAgICJwYXRoIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICJ0eXBlIiA6ICJQcm9wZXJ0eSIsCiAgICAgICAgICAgICAgICJzb3VyY2UiIDogewogICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMjQ1OjU4LTI0NTo2MiIsCiAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJPcGVyYW5kUmVmIgogICAgICAgICAgICAgICB9CiAgICAgICAgICAgIH0sCiAgICAgICAgICAgICJvcGVyYW5kIiA6IFsgewogICAgICAgICAgICAgICAibmFtZSIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAib3BlcmFuZFR5cGVTcGVjaWZpZXIiIDogewogICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMjQ1OjMyLTI0NTo1NCIsCiAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJ7aHR0cDovL2hsNy5vcmcvZmhpcn1FbnJvbGxtZW50UmVxdWVzdFN0YXR1cyIsCiAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJOYW1lZFR5cGVTcGVjaWZpZXIiCiAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgfSBdCiAgICAgICAgIH0sIHsKICAgICAgICAgICAgImxvY2F0b3IiIDogIjI0NjoxLTI0Njo2OSIsCiAgICAgICAgICAgICJuYW1lIiA6ICJUb1N0cmluZyIsCiAgICAgICAgICAgICJjb250ZXh0IiA6ICJQYXRpZW50IiwKICAgICAgICAgICAgImFjY2Vzc0xldmVsIiA6ICJQdWJsaWMiLAogICAgICAgICAgICAidHlwZSIgOiAiRnVuY3Rpb25EZWYiLAogICAgICAgICAgICAiZXhwcmVzc2lvbiIgOiB7CiAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIyNDY6NTktMjQ2OjY5IiwKICAgICAgICAgICAgICAgInBhdGgiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgInR5cGUiIDogIlByb3BlcnR5IiwKICAgICAgICAgICAgICAgInNvdXJjZSIgOiB7CiAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIyNDY6NTktMjQ2OjYzIiwKICAgICAgICAgICAgICAgICAgIm5hbWUiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk9wZXJhbmRSZWYiCiAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgfSwKICAgICAgICAgICAgIm9wZXJhbmQiIDogWyB7CiAgICAgICAgICAgICAgICJuYW1lIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICJvcGVyYW5kVHlwZVNwZWNpZmllciIgOiB7CiAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIyNDY6MzItMjQ2OjU1IiwKICAgICAgICAgICAgICAgICAgIm5hbWUiIDogIntodHRwOi8vaGw3Lm9yZy9maGlyfUVucm9sbG1lbnRSZXNwb25zZVN0YXR1cyIsCiAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJOYW1lZFR5cGVTcGVjaWZpZXIiCiAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgfSBdCiAgICAgICAgIH0sIHsKICAgICAgICAgICAgImxvY2F0b3IiIDogIjI0NzoxLTI0Nzo2NCIsCiAgICAgICAgICAgICJuYW1lIiA6ICJUb1N0cmluZyIsCiAgICAgICAgICAgICJjb250ZXh0IiA6ICJQYXRpZW50IiwKICAgICAgICAgICAgImFjY2Vzc0xldmVsIiA6ICJQdWJsaWMiLAogICAgICAgICAgICAidHlwZSIgOiAiRnVuY3Rpb25EZWYiLAogICAgICAgICAgICAiZXhwcmVzc2lvbiIgOiB7CiAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIyNDc6NTQtMjQ3OjY0IiwKICAgICAgICAgICAgICAgInBhdGgiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgInR5cGUiIDogIlByb3BlcnR5IiwKICAgICAgICAgICAgICAgInNvdXJjZSIgOiB7CiAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIyNDc6NTQtMjQ3OjU4IiwKICAgICAgICAgICAgICAgICAgIm5hbWUiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk9wZXJhbmRSZWYiCiAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgfSwKICAgICAgICAgICAgIm9wZXJhbmQiIDogWyB7CiAgICAgICAgICAgICAgICJuYW1lIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICJvcGVyYW5kVHlwZVNwZWNpZmllciIgOiB7CiAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIyNDc6MzItMjQ3OjUwIiwKICAgICAgICAgICAgICAgICAgIm5hbWUiIDogIntodHRwOi8vaGw3Lm9yZy9maGlyfUVwaXNvZGVPZkNhcmVTdGF0dXMiLAogICAgICAgICAgICAgICAgICAidHlwZSIgOiAiTmFtZWRUeXBlU3BlY2lmaWVyIgogICAgICAgICAgICAgICB9CiAgICAgICAgICAgIH0gXQogICAgICAgICB9LCB7CiAgICAgICAgICAgICJsb2NhdG9yIiA6ICIyNDg6MS0yNDg6NjQiLAogICAgICAgICAgICAibmFtZSIgOiAiVG9TdHJpbmciLAogICAgICAgICAgICAiY29udGV4dCIgOiAiUGF0aWVudCIsCiAgICAgICAgICAgICJhY2Nlc3NMZXZlbCIgOiAiUHVibGljIiwKICAgICAgICAgICAgInR5cGUiIDogIkZ1bmN0aW9uRGVmIiwKICAgICAgICAgICAgImV4cHJlc3Npb24iIDogewogICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMjQ4OjU0LTI0ODo2NCIsCiAgICAgICAgICAgICAgICJwYXRoIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICJ0eXBlIiA6ICJQcm9wZXJ0eSIsCiAgICAgICAgICAgICAgICJzb3VyY2UiIDogewogICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMjQ4OjU0LTI0ODo1OCIsCiAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJPcGVyYW5kUmVmIgogICAgICAgICAgICAgICB9CiAgICAgICAgICAgIH0sCiAgICAgICAgICAgICJvcGVyYW5kIiA6IFsgewogICAgICAgICAgICAgICAibmFtZSIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAib3BlcmFuZFR5cGVTcGVjaWZpZXIiIDogewogICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMjQ4OjMyLTI0ODo1MCIsCiAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJ7aHR0cDovL2hsNy5vcmcvZmhpcn1FdmVudENhcGFiaWxpdHlNb2RlIiwKICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk5hbWVkVHlwZVNwZWNpZmllciIKICAgICAgICAgICAgICAgfQogICAgICAgICAgICB9IF0KICAgICAgICAgfSwgewogICAgICAgICAgICAibG9jYXRvciIgOiAiMjQ5OjEtMjQ5OjU2IiwKICAgICAgICAgICAgIm5hbWUiIDogIlRvU3RyaW5nIiwKICAgICAgICAgICAgImNvbnRleHQiIDogIlBhdGllbnQiLAogICAgICAgICAgICAiYWNjZXNzTGV2ZWwiIDogIlB1YmxpYyIsCiAgICAgICAgICAgICJ0eXBlIiA6ICJGdW5jdGlvbkRlZiIsCiAgICAgICAgICAgICJleHByZXNzaW9uIiA6IHsKICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjI0OTo0Ni0yNDk6NTYiLAogICAgICAgICAgICAgICAicGF0aCIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAidHlwZSIgOiAiUHJvcGVydHkiLAogICAgICAgICAgICAgICAic291cmNlIiA6IHsKICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjI0OTo0Ni0yNDk6NTAiLAogICAgICAgICAgICAgICAgICAibmFtZSIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAgICAidHlwZSIgOiAiT3BlcmFuZFJlZiIKICAgICAgICAgICAgICAgfQogICAgICAgICAgICB9LAogICAgICAgICAgICAib3BlcmFuZCIgOiBbIHsKICAgICAgICAgICAgICAgIm5hbWUiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgIm9wZXJhbmRUeXBlU3BlY2lmaWVyIiA6IHsKICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjI0OTozMi0yNDk6NDIiLAogICAgICAgICAgICAgICAgICAibmFtZSIgOiAie2h0dHA6Ly9obDcub3JnL2ZoaXJ9RXZlbnRUaW1pbmciLAogICAgICAgICAgICAgICAgICAidHlwZSIgOiAiTmFtZWRUeXBlU3BlY2lmaWVyIgogICAgICAgICAgICAgICB9CiAgICAgICAgICAgIH0gXQogICAgICAgICB9LCB7CiAgICAgICAgICAgICJsb2NhdG9yIiA6ICIyNTA6MS0yNTA6NjUiLAogICAgICAgICAgICAibmFtZSIgOiAiVG9TdHJpbmciLAogICAgICAgICAgICAiY29udGV4dCIgOiAiUGF0aWVudCIsCiAgICAgICAgICAgICJhY2Nlc3NMZXZlbCIgOiAiUHVibGljIiwKICAgICAgICAgICAgInR5cGUiIDogIkZ1bmN0aW9uRGVmIiwKICAgICAgICAgICAgImV4cHJlc3Npb24iIDogewogICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMjUwOjU1LTI1MDo2NSIsCiAgICAgICAgICAgICAgICJwYXRoIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICJ0eXBlIiA6ICJQcm9wZXJ0eSIsCiAgICAgICAgICAgICAgICJzb3VyY2UiIDogewogICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMjUwOjU1LTI1MDo1OSIsCiAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJPcGVyYW5kUmVmIgogICAgICAgICAgICAgICB9CiAgICAgICAgICAgIH0sCiAgICAgICAgICAgICJvcGVyYW5kIiA6IFsgewogICAgICAgICAgICAgICAibmFtZSIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAib3BlcmFuZFR5cGVTcGVjaWZpZXIiIDogewogICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMjUwOjMyLTI1MDo1MSIsCiAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJ7aHR0cDovL2hsNy5vcmcvZmhpcn1FdmlkZW5jZVZhcmlhYmxlVHlwZSIsCiAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJOYW1lZFR5cGVTcGVjaWZpZXIiCiAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgfSBdCiAgICAgICAgIH0sIHsKICAgICAgICAgICAgImxvY2F0b3IiIDogIjI1MToxLTI1MTo2OSIsCiAgICAgICAgICAgICJuYW1lIiA6ICJUb1N0cmluZyIsCiAgICAgICAgICAgICJjb250ZXh0IiA6ICJQYXRpZW50IiwKICAgICAgICAgICAgImFjY2Vzc0xldmVsIiA6ICJQdWJsaWMiLAogICAgICAgICAgICAidHlwZSIgOiAiRnVuY3Rpb25EZWYiLAogICAgICAgICAgICAiZXhwcmVzc2lvbiIgOiB7CiAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIyNTE6NTktMjUxOjY5IiwKICAgICAgICAgICAgICAgInBhdGgiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgInR5cGUiIDogIlByb3BlcnR5IiwKICAgICAgICAgICAgICAgInNvdXJjZSIgOiB7CiAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIyNTE6NTktMjUxOjYzIiwKICAgICAgICAgICAgICAgICAgIm5hbWUiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk9wZXJhbmRSZWYiCiAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgfSwKICAgICAgICAgICAgIm9wZXJhbmQiIDogWyB7CiAgICAgICAgICAgICAgICJuYW1lIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICJvcGVyYW5kVHlwZVNwZWNpZmllciIgOiB7CiAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIyNTE6MzItMjUxOjU1IiwKICAgICAgICAgICAgICAgICAgIm5hbWUiIDogIntodHRwOi8vaGw3Lm9yZy9maGlyfUV4YW1wbGVTY2VuYXJpb0FjdG9yVHlwZSIsCiAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJOYW1lZFR5cGVTcGVjaWZpZXIiCiAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgfSBdCiAgICAgICAgIH0sIHsKICAgICAgICAgICAgImxvY2F0b3IiIDogIjI1MjoxLTI1Mjo3MSIsCiAgICAgICAgICAgICJuYW1lIiA6ICJUb1N0cmluZyIsCiAgICAgICAgICAgICJjb250ZXh0IiA6ICJQYXRpZW50IiwKICAgICAgICAgICAgImFjY2Vzc0xldmVsIiA6ICJQdWJsaWMiLAogICAgICAgICAgICAidHlwZSIgOiAiRnVuY3Rpb25EZWYiLAogICAgICAgICAgICAiZXhwcmVzc2lvbiIgOiB7CiAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIyNTI6NjEtMjUyOjcxIiwKICAgICAgICAgICAgICAgInBhdGgiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgInR5cGUiIDogIlByb3BlcnR5IiwKICAgICAgICAgICAgICAgInNvdXJjZSIgOiB7CiAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIyNTI6NjEtMjUyOjY1IiwKICAgICAgICAgICAgICAgICAgIm5hbWUiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk9wZXJhbmRSZWYiCiAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgfSwKICAgICAgICAgICAgIm9wZXJhbmQiIDogWyB7CiAgICAgICAgICAgICAgICJuYW1lIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICJvcGVyYW5kVHlwZVNwZWNpZmllciIgOiB7CiAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIyNTI6MzItMjUyOjU3IiwKICAgICAgICAgICAgICAgICAgIm5hbWUiIDogIntodHRwOi8vaGw3Lm9yZy9maGlyfUV4cGxhbmF0aW9uT2ZCZW5lZml0U3RhdHVzIiwKICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk5hbWVkVHlwZVNwZWNpZmllciIKICAgICAgICAgICAgICAgfQogICAgICAgICAgICB9IF0KICAgICAgICAgfSwgewogICAgICAgICAgICAibG9jYXRvciIgOiAiMjUzOjEtMjUzOjU4IiwKICAgICAgICAgICAgIm5hbWUiIDogIlRvU3RyaW5nIiwKICAgICAgICAgICAgImNvbnRleHQiIDogIlBhdGllbnQiLAogICAgICAgICAgICAiYWNjZXNzTGV2ZWwiIDogIlB1YmxpYyIsCiAgICAgICAgICAgICJ0eXBlIiA6ICJGdW5jdGlvbkRlZiIsCiAgICAgICAgICAgICJleHByZXNzaW9uIiA6IHsKICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjI1Mzo0OC0yNTM6NTgiLAogICAgICAgICAgICAgICAicGF0aCIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAidHlwZSIgOiAiUHJvcGVydHkiLAogICAgICAgICAgICAgICAic291cmNlIiA6IHsKICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjI1Mzo0OC0yNTM6NTIiLAogICAgICAgICAgICAgICAgICAibmFtZSIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAgICAidHlwZSIgOiAiT3BlcmFuZFJlZiIKICAgICAgICAgICAgICAgfQogICAgICAgICAgICB9LAogICAgICAgICAgICAib3BlcmFuZCIgOiBbIHsKICAgICAgICAgICAgICAgIm5hbWUiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgIm9wZXJhbmRUeXBlU3BlY2lmaWVyIiA6IHsKICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjI1MzozMi0yNTM6NDQiLAogICAgICAgICAgICAgICAgICAibmFtZSIgOiAie2h0dHA6Ly9obDcub3JnL2ZoaXJ9RXhwb3N1cmVTdGF0ZSIsCiAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJOYW1lZFR5cGVTcGVjaWZpZXIiCiAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgfSBdCiAgICAgICAgIH0sIHsKICAgICAgICAgICAgImxvY2F0b3IiIDogIjI1NDoxLTI1NDo2NSIsCiAgICAgICAgICAgICJuYW1lIiA6ICJUb1N0cmluZyIsCiAgICAgICAgICAgICJjb250ZXh0IiA6ICJQYXRpZW50IiwKICAgICAgICAgICAgImFjY2Vzc0xldmVsIiA6ICJQdWJsaWMiLAogICAgICAgICAgICAidHlwZSIgOiAiRnVuY3Rpb25EZWYiLAogICAgICAgICAgICAiZXhwcmVzc2lvbiIgOiB7CiAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIyNTQ6NTUtMjU0OjY1IiwKICAgICAgICAgICAgICAgInBhdGgiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgInR5cGUiIDogIlByb3BlcnR5IiwKICAgICAgICAgICAgICAgInNvdXJjZSIgOiB7CiAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIyNTQ6NTUtMjU0OjU5IiwKICAgICAgICAgICAgICAgICAgIm5hbWUiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk9wZXJhbmRSZWYiCiAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgfSwKICAgICAgICAgICAgIm9wZXJhbmQiIDogWyB7CiAgICAgICAgICAgICAgICJuYW1lIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICJvcGVyYW5kVHlwZVNwZWNpZmllciIgOiB7CiAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIyNTQ6MzItMjU0OjUxIiwKICAgICAgICAgICAgICAgICAgIm5hbWUiIDogIntodHRwOi8vaGw3Lm9yZy9maGlyfUV4dGVuc2lvbkNvbnRleHRUeXBlIiwKICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk5hbWVkVHlwZVNwZWNpZmllciIKICAgICAgICAgICAgICAgfQogICAgICAgICAgICB9IF0KICAgICAgICAgfSwgewogICAgICAgICAgICAibG9jYXRvciIgOiAiMjU1OjEtMjU1OjU3IiwKICAgICAgICAgICAgIm5hbWUiIDogIlRvU3RyaW5nIiwKICAgICAgICAgICAgImNvbnRleHQiIDogIlBhdGllbnQiLAogICAgICAgICAgICAiYWNjZXNzTGV2ZWwiIDogIlB1YmxpYyIsCiAgICAgICAgICAgICJ0eXBlIiA6ICJGdW5jdGlvbkRlZiIsCiAgICAgICAgICAgICJleHByZXNzaW9uIiA6IHsKICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjI1NTo0Ny0yNTU6NTciLAogICAgICAgICAgICAgICAicGF0aCIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAidHlwZSIgOiAiUHJvcGVydHkiLAogICAgICAgICAgICAgICAic291cmNlIiA6IHsKICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjI1NTo0Ny0yNTU6NTEiLAogICAgICAgICAgICAgICAgICAibmFtZSIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAgICAidHlwZSIgOiAiT3BlcmFuZFJlZiIKICAgICAgICAgICAgICAgfQogICAgICAgICAgICB9LAogICAgICAgICAgICAib3BlcmFuZCIgOiBbIHsKICAgICAgICAgICAgICAgIm5hbWUiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgIm9wZXJhbmRUeXBlU3BlY2lmaWVyIiA6IHsKICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjI1NTozMi0yNTU6NDMiLAogICAgICAgICAgICAgICAgICAibmFtZSIgOiAie2h0dHA6Ly9obDcub3JnL2ZoaXJ9RkhJUkFsbFR5cGVzIiwKICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk5hbWVkVHlwZVNwZWNpZmllciIKICAgICAgICAgICAgICAgfQogICAgICAgICAgICB9IF0KICAgICAgICAgfSwgewogICAgICAgICAgICAibG9jYXRvciIgOiAiMjU2OjEtMjU2OjYwIiwKICAgICAgICAgICAgIm5hbWUiIDogIlRvU3RyaW5nIiwKICAgICAgICAgICAgImNvbnRleHQiIDogIlBhdGllbnQiLAogICAgICAgICAgICAiYWNjZXNzTGV2ZWwiIDogIlB1YmxpYyIsCiAgICAgICAgICAgICJ0eXBlIiA6ICJGdW5jdGlvbkRlZiIsCiAgICAgICAgICAgICJleHByZXNzaW9uIiA6IHsKICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjI1Njo1MC0yNTY6NjAiLAogICAgICAgICAgICAgICAicGF0aCIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAidHlwZSIgOiAiUHJvcGVydHkiLAogICAgICAgICAgICAgICAic291cmNlIiA6IHsKICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjI1Njo1MC0yNTY6NTQiLAogICAgICAgICAgICAgICAgICAibmFtZSIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAgICAidHlwZSIgOiAiT3BlcmFuZFJlZiIKICAgICAgICAgICAgICAgfQogICAgICAgICAgICB9LAogICAgICAgICAgICAib3BlcmFuZCIgOiBbIHsKICAgICAgICAgICAgICAgIm5hbWUiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgIm9wZXJhbmRUeXBlU3BlY2lmaWVyIiA6IHsKICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjI1NjozMi0yNTY6NDYiLAogICAgICAgICAgICAgICAgICAibmFtZSIgOiAie2h0dHA6Ly9obDcub3JnL2ZoaXJ9RkhJUkRlZmluZWRUeXBlIiwKICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk5hbWVkVHlwZVNwZWNpZmllciIKICAgICAgICAgICAgICAgfQogICAgICAgICAgICB9IF0KICAgICAgICAgfSwgewogICAgICAgICAgICAibG9jYXRvciIgOiAiMjU3OjEtMjU3OjYxIiwKICAgICAgICAgICAgIm5hbWUiIDogIlRvU3RyaW5nIiwKICAgICAgICAgICAgImNvbnRleHQiIDogIlBhdGllbnQiLAogICAgICAgICAgICAiYWNjZXNzTGV2ZWwiIDogIlB1YmxpYyIsCiAgICAgICAgICAgICJ0eXBlIiA6ICJGdW5jdGlvbkRlZiIsCiAgICAgICAgICAgICJleHByZXNzaW9uIiA6IHsKICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjI1Nzo1MS0yNTc6NjEiLAogICAgICAgICAgICAgICAicGF0aCIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAidHlwZSIgOiAiUHJvcGVydHkiLAogICAgICAgICAgICAgICAic291cmNlIiA6IHsKICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjI1Nzo1MS0yNTc6NTUiLAogICAgICAgICAgICAgICAgICAibmFtZSIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAgICAidHlwZSIgOiAiT3BlcmFuZFJlZiIKICAgICAgICAgICAgICAgfQogICAgICAgICAgICB9LAogICAgICAgICAgICAib3BlcmFuZCIgOiBbIHsKICAgICAgICAgICAgICAgIm5hbWUiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgIm9wZXJhbmRUeXBlU3BlY2lmaWVyIiA6IHsKICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjI1NzozMi0yNTc6NDciLAogICAgICAgICAgICAgICAgICAibmFtZSIgOiAie2h0dHA6Ly9obDcub3JnL2ZoaXJ9RkhJUkRldmljZVN0YXR1cyIsCiAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJOYW1lZFR5cGVTcGVjaWZpZXIiCiAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgfSBdCiAgICAgICAgIH0sIHsKICAgICAgICAgICAgImxvY2F0b3IiIDogIjI1ODoxLTI1ODo2MSIsCiAgICAgICAgICAgICJuYW1lIiA6ICJUb1N0cmluZyIsCiAgICAgICAgICAgICJjb250ZXh0IiA6ICJQYXRpZW50IiwKICAgICAgICAgICAgImFjY2Vzc0xldmVsIiA6ICJQdWJsaWMiLAogICAgICAgICAgICAidHlwZSIgOiAiRnVuY3Rpb25EZWYiLAogICAgICAgICAgICAiZXhwcmVzc2lvbiIgOiB7CiAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIyNTg6NTEtMjU4OjYxIiwKICAgICAgICAgICAgICAgInBhdGgiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgInR5cGUiIDogIlByb3BlcnR5IiwKICAgICAgICAgICAgICAgInNvdXJjZSIgOiB7CiAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIyNTg6NTEtMjU4OjU1IiwKICAgICAgICAgICAgICAgICAgIm5hbWUiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk9wZXJhbmRSZWYiCiAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgfSwKICAgICAgICAgICAgIm9wZXJhbmQiIDogWyB7CiAgICAgICAgICAgICAgICJuYW1lIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICJvcGVyYW5kVHlwZVNwZWNpZmllciIgOiB7CiAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIyNTg6MzItMjU4OjQ3IiwKICAgICAgICAgICAgICAgICAgIm5hbWUiIDogIntodHRwOi8vaGw3Lm9yZy9maGlyfUZISVJSZXNvdXJjZVR5cGUiLAogICAgICAgICAgICAgICAgICAidHlwZSIgOiAiTmFtZWRUeXBlU3BlY2lmaWVyIgogICAgICAgICAgICAgICB9CiAgICAgICAgICAgIH0gXQogICAgICAgICB9LCB7CiAgICAgICAgICAgICJsb2NhdG9yIiA6ICIyNTk6MS0yNTk6NjQiLAogICAgICAgICAgICAibmFtZSIgOiAiVG9TdHJpbmciLAogICAgICAgICAgICAiY29udGV4dCIgOiAiUGF0aWVudCIsCiAgICAgICAgICAgICJhY2Nlc3NMZXZlbCIgOiAiUHVibGljIiwKICAgICAgICAgICAgInR5cGUiIDogIkZ1bmN0aW9uRGVmIiwKICAgICAgICAgICAgImV4cHJlc3Npb24iIDogewogICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMjU5OjU0LTI1OTo2NCIsCiAgICAgICAgICAgICAgICJwYXRoIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICJ0eXBlIiA6ICJQcm9wZXJ0eSIsCiAgICAgICAgICAgICAgICJzb3VyY2UiIDogewogICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMjU5OjU0LTI1OTo1OCIsCiAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJPcGVyYW5kUmVmIgogICAgICAgICAgICAgICB9CiAgICAgICAgICAgIH0sCiAgICAgICAgICAgICJvcGVyYW5kIiA6IFsgewogICAgICAgICAgICAgICAibmFtZSIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAib3BlcmFuZFR5cGVTcGVjaWZpZXIiIDogewogICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMjU5OjMyLTI1OTo1MCIsCiAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJ7aHR0cDovL2hsNy5vcmcvZmhpcn1GSElSU3Vic3RhbmNlU3RhdHVzIiwKICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk5hbWVkVHlwZVNwZWNpZmllciIKICAgICAgICAgICAgICAgfQogICAgICAgICAgICB9IF0KICAgICAgICAgfSwgewogICAgICAgICAgICAibG9jYXRvciIgOiAiMjYwOjEtMjYwOjU2IiwKICAgICAgICAgICAgIm5hbWUiIDogIlRvU3RyaW5nIiwKICAgICAgICAgICAgImNvbnRleHQiIDogIlBhdGllbnQiLAogICAgICAgICAgICAiYWNjZXNzTGV2ZWwiIDogIlB1YmxpYyIsCiAgICAgICAgICAgICJ0eXBlIiA6ICJGdW5jdGlvbkRlZiIsCiAgICAgICAgICAgICJleHByZXNzaW9uIiA6IHsKICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjI2MDo0Ni0yNjA6NTYiLAogICAgICAgICAgICAgICAicGF0aCIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAidHlwZSIgOiAiUHJvcGVydHkiLAogICAgICAgICAgICAgICAic291cmNlIiA6IHsKICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjI2MDo0Ni0yNjA6NTAiLAogICAgICAgICAgICAgICAgICAibmFtZSIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAgICAidHlwZSIgOiAiT3BlcmFuZFJlZiIKICAgICAgICAgICAgICAgfQogICAgICAgICAgICB9LAogICAgICAgICAgICAib3BlcmFuZCIgOiBbIHsKICAgICAgICAgICAgICAgIm5hbWUiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgIm9wZXJhbmRUeXBlU3BlY2lmaWVyIiA6IHsKICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjI2MDozMi0yNjA6NDIiLAogICAgICAgICAgICAgICAgICAibmFtZSIgOiAie2h0dHA6Ly9obDcub3JnL2ZoaXJ9RkhJUlZlcnNpb24iLAogICAgICAgICAgICAgICAgICAidHlwZSIgOiAiTmFtZWRUeXBlU3BlY2lmaWVyIgogICAgICAgICAgICAgICB9CiAgICAgICAgICAgIH0gXQogICAgICAgICB9LCB7CiAgICAgICAgICAgICJsb2NhdG9yIiA6ICIyNjE6MS0yNjE6NjQiLAogICAgICAgICAgICAibmFtZSIgOiAiVG9TdHJpbmciLAogICAgICAgICAgICAiY29udGV4dCIgOiAiUGF0aWVudCIsCiAgICAgICAgICAgICJhY2Nlc3NMZXZlbCIgOiAiUHVibGljIiwKICAgICAgICAgICAgInR5cGUiIDogIkZ1bmN0aW9uRGVmIiwKICAgICAgICAgICAgImV4cHJlc3Npb24iIDogewogICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMjYxOjU0LTI2MTo2NCIsCiAgICAgICAgICAgICAgICJwYXRoIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICJ0eXBlIiA6ICJQcm9wZXJ0eSIsCiAgICAgICAgICAgICAgICJzb3VyY2UiIDogewogICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMjYxOjU0LTI2MTo1OCIsCiAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJPcGVyYW5kUmVmIgogICAgICAgICAgICAgICB9CiAgICAgICAgICAgIH0sCiAgICAgICAgICAgICJvcGVyYW5kIiA6IFsgewogICAgICAgICAgICAgICAibmFtZSIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAib3BlcmFuZFR5cGVTcGVjaWZpZXIiIDogewogICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMjYxOjMyLTI2MTo1MCIsCiAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJ7aHR0cDovL2hsNy5vcmcvZmhpcn1GYW1pbHlIaXN0b3J5U3RhdHVzIiwKICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk5hbWVkVHlwZVNwZWNpZmllciIKICAgICAgICAgICAgICAgfQogICAgICAgICAgICB9IF0KICAgICAgICAgfSwgewogICAgICAgICAgICAibG9jYXRvciIgOiAiMjYyOjEtMjYyOjU5IiwKICAgICAgICAgICAgIm5hbWUiIDogIlRvU3RyaW5nIiwKICAgICAgICAgICAgImNvbnRleHQiIDogIlBhdGllbnQiLAogICAgICAgICAgICAiYWNjZXNzTGV2ZWwiIDogIlB1YmxpYyIsCiAgICAgICAgICAgICJ0eXBlIiA6ICJGdW5jdGlvbkRlZiIsCiAgICAgICAgICAgICJleHByZXNzaW9uIiA6IHsKICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjI2Mjo0OS0yNjI6NTkiLAogICAgICAgICAgICAgICAicGF0aCIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAidHlwZSIgOiAiUHJvcGVydHkiLAogICAgICAgICAgICAgICAic291cmNlIiA6IHsKICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjI2Mjo0OS0yNjI6NTMiLAogICAgICAgICAgICAgICAgICAibmFtZSIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAgICAidHlwZSIgOiAiT3BlcmFuZFJlZiIKICAgICAgICAgICAgICAgfQogICAgICAgICAgICB9LAogICAgICAgICAgICAib3BlcmFuZCIgOiBbIHsKICAgICAgICAgICAgICAgIm5hbWUiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgIm9wZXJhbmRUeXBlU3BlY2lmaWVyIiA6IHsKICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjI2MjozMi0yNjI6NDUiLAogICAgICAgICAgICAgICAgICAibmFtZSIgOiAie2h0dHA6Ly9obDcub3JnL2ZoaXJ9RmlsdGVyT3BlcmF0b3IiLAogICAgICAgICAgICAgICAgICAidHlwZSIgOiAiTmFtZWRUeXBlU3BlY2lmaWVyIgogICAgICAgICAgICAgICB9CiAgICAgICAgICAgIH0gXQogICAgICAgICB9LCB7CiAgICAgICAgICAgICJsb2NhdG9yIiA6ICIyNjM6MS0yNjM6NTUiLAogICAgICAgICAgICAibmFtZSIgOiAiVG9TdHJpbmciLAogICAgICAgICAgICAiY29udGV4dCIgOiAiUGF0aWVudCIsCiAgICAgICAgICAgICJhY2Nlc3NMZXZlbCIgOiAiUHVibGljIiwKICAgICAgICAgICAgInR5cGUiIDogIkZ1bmN0aW9uRGVmIiwKICAgICAgICAgICAgImV4cHJlc3Npb24iIDogewogICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMjYzOjQ1LTI2Mzo1NSIsCiAgICAgICAgICAgICAgICJwYXRoIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICJ0eXBlIiA6ICJQcm9wZXJ0eSIsCiAgICAgICAgICAgICAgICJzb3VyY2UiIDogewogICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMjYzOjQ1LTI2Mzo0OSIsCiAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJPcGVyYW5kUmVmIgogICAgICAgICAgICAgICB9CiAgICAgICAgICAgIH0sCiAgICAgICAgICAgICJvcGVyYW5kIiA6IFsgewogICAgICAgICAgICAgICAibmFtZSIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAib3BlcmFuZFR5cGVTcGVjaWZpZXIiIDogewogICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMjYzOjMyLTI2Mzo0MSIsCiAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJ7aHR0cDovL2hsNy5vcmcvZmhpcn1GbGFnU3RhdHVzIiwKICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk5hbWVkVHlwZVNwZWNpZmllciIKICAgICAgICAgICAgICAgfQogICAgICAgICAgICB9IF0KICAgICAgICAgfSwgewogICAgICAgICAgICAibG9jYXRvciIgOiAiMjY0OjEtMjY0OjY0IiwKICAgICAgICAgICAgIm5hbWUiIDogIlRvU3RyaW5nIiwKICAgICAgICAgICAgImNvbnRleHQiIDogIlBhdGllbnQiLAogICAgICAgICAgICAiYWNjZXNzTGV2ZWwiIDogIlB1YmxpYyIsCiAgICAgICAgICAgICJ0eXBlIiA6ICJGdW5jdGlvbkRlZiIsCiAgICAgICAgICAgICJleHByZXNzaW9uIiA6IHsKICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjI2NDo1NC0yNjQ6NjQiLAogICAgICAgICAgICAgICAicGF0aCIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAidHlwZSIgOiAiUHJvcGVydHkiLAogICAgICAgICAgICAgICAic291cmNlIiA6IHsKICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjI2NDo1NC0yNjQ6NTgiLAogICAgICAgICAgICAgICAgICAibmFtZSIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAgICAidHlwZSIgOiAiT3BlcmFuZFJlZiIKICAgICAgICAgICAgICAgfQogICAgICAgICAgICB9LAogICAgICAgICAgICAib3BlcmFuZCIgOiBbIHsKICAgICAgICAgICAgICAgIm5hbWUiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgIm9wZXJhbmRUeXBlU3BlY2lmaWVyIiA6IHsKICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjI2NDozMi0yNjQ6NTAiLAogICAgICAgICAgICAgICAgICAibmFtZSIgOiAie2h0dHA6Ly9obDcub3JnL2ZoaXJ9R29hbExpZmVjeWNsZVN0YXR1cyIsCiAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJOYW1lZFR5cGVTcGVjaWZpZXIiCiAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgfSBdCiAgICAgICAgIH0sIHsKICAgICAgICAgICAgImxvY2F0b3IiIDogIjI2NToxLTI2NTo2NSIsCiAgICAgICAgICAgICJuYW1lIiA6ICJUb1N0cmluZyIsCiAgICAgICAgICAgICJjb250ZXh0IiA6ICJQYXRpZW50IiwKICAgICAgICAgICAgImFjY2Vzc0xldmVsIiA6ICJQdWJsaWMiLAogICAgICAgICAgICAidHlwZSIgOiAiRnVuY3Rpb25EZWYiLAogICAgICAgICAgICAiZXhwcmVzc2lvbiIgOiB7CiAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIyNjU6NTUtMjY1OjY1IiwKICAgICAgICAgICAgICAgInBhdGgiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgInR5cGUiIDogIlByb3BlcnR5IiwKICAgICAgICAgICAgICAgInNvdXJjZSIgOiB7CiAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIyNjU6NTUtMjY1OjU5IiwKICAgICAgICAgICAgICAgICAgIm5hbWUiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk9wZXJhbmRSZWYiCiAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgfSwKICAgICAgICAgICAgIm9wZXJhbmQiIDogWyB7CiAgICAgICAgICAgICAgICJuYW1lIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICJvcGVyYW5kVHlwZVNwZWNpZmllciIgOiB7CiAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIyNjU6MzItMjY1OjUxIiwKICAgICAgICAgICAgICAgICAgIm5hbWUiIDogIntodHRwOi8vaGw3Lm9yZy9maGlyfUdyYXBoQ29tcGFydG1lbnRSdWxlIiwKICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk5hbWVkVHlwZVNwZWNpZmllciIKICAgICAgICAgICAgICAgfQogICAgICAgICAgICB9IF0KICAgICAgICAgfSwgewogICAgICAgICAgICAibG9jYXRvciIgOiAiMjY2OjEtMjY2OjY0IiwKICAgICAgICAgICAgIm5hbWUiIDogIlRvU3RyaW5nIiwKICAgICAgICAgICAgImNvbnRleHQiIDogIlBhdGllbnQiLAogICAgICAgICAgICAiYWNjZXNzTGV2ZWwiIDogIlB1YmxpYyIsCiAgICAgICAgICAgICJ0eXBlIiA6ICJGdW5jdGlvbkRlZiIsCiAgICAgICAgICAgICJleHByZXNzaW9uIiA6IHsKICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjI2Njo1NC0yNjY6NjQiLAogICAgICAgICAgICAgICAicGF0aCIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAidHlwZSIgOiAiUHJvcGVydHkiLAogICAgICAgICAgICAgICAic291cmNlIiA6IHsKICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjI2Njo1NC0yNjY6NTgiLAogICAgICAgICAgICAgICAgICAibmFtZSIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAgICAidHlwZSIgOiAiT3BlcmFuZFJlZiIKICAgICAgICAgICAgICAgfQogICAgICAgICAgICB9LAogICAgICAgICAgICAib3BlcmFuZCIgOiBbIHsKICAgICAgICAgICAgICAgIm5hbWUiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgIm9wZXJhbmRUeXBlU3BlY2lmaWVyIiA6IHsKICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjI2NjozMi0yNjY6NTAiLAogICAgICAgICAgICAgICAgICAibmFtZSIgOiAie2h0dHA6Ly9obDcub3JnL2ZoaXJ9R3JhcGhDb21wYXJ0bWVudFVzZSIsCiAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJOYW1lZFR5cGVTcGVjaWZpZXIiCiAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgfSBdCiAgICAgICAgIH0sIHsKICAgICAgICAgICAgImxvY2F0b3IiIDogIjI2NzoxLTI2Nzo1NyIsCiAgICAgICAgICAgICJuYW1lIiA6ICJUb1N0cmluZyIsCiAgICAgICAgICAgICJjb250ZXh0IiA6ICJQYXRpZW50IiwKICAgICAgICAgICAgImFjY2Vzc0xldmVsIiA6ICJQdWJsaWMiLAogICAgICAgICAgICAidHlwZSIgOiAiRnVuY3Rpb25EZWYiLAogICAgICAgICAgICAiZXhwcmVzc2lvbiIgOiB7CiAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIyNjc6NDctMjY3OjU3IiwKICAgICAgICAgICAgICAgInBhdGgiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgInR5cGUiIDogIlByb3BlcnR5IiwKICAgICAgICAgICAgICAgInNvdXJjZSIgOiB7CiAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIyNjc6NDctMjY3OjUxIiwKICAgICAgICAgICAgICAgICAgIm5hbWUiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk9wZXJhbmRSZWYiCiAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgfSwKICAgICAgICAgICAgIm9wZXJhbmQiIDogWyB7CiAgICAgICAgICAgICAgICJuYW1lIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICJvcGVyYW5kVHlwZVNwZWNpZmllciIgOiB7CiAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIyNjc6MzItMjY3OjQzIiwKICAgICAgICAgICAgICAgICAgIm5hbWUiIDogIntodHRwOi8vaGw3Lm9yZy9maGlyfUdyb3VwTWVhc3VyZSIsCiAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJOYW1lZFR5cGVTcGVjaWZpZXIiCiAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgfSBdCiAgICAgICAgIH0sIHsKICAgICAgICAgICAgImxvY2F0b3IiIDogIjI2ODoxLTI2ODo1NCIsCiAgICAgICAgICAgICJuYW1lIiA6ICJUb1N0cmluZyIsCiAgICAgICAgICAgICJjb250ZXh0IiA6ICJQYXRpZW50IiwKICAgICAgICAgICAgImFjY2Vzc0xldmVsIiA6ICJQdWJsaWMiLAogICAgICAgICAgICAidHlwZSIgOiAiRnVuY3Rpb25EZWYiLAogICAgICAgICAgICAiZXhwcmVzc2lvbiIgOiB7CiAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIyNjg6NDQtMjY4OjU0IiwKICAgICAgICAgICAgICAgInBhdGgiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgInR5cGUiIDogIlByb3BlcnR5IiwKICAgICAgICAgICAgICAgInNvdXJjZSIgOiB7CiAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIyNjg6NDQtMjY4OjQ4IiwKICAgICAgICAgICAgICAgICAgIm5hbWUiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk9wZXJhbmRSZWYiCiAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgfSwKICAgICAgICAgICAgIm9wZXJhbmQiIDogWyB7CiAgICAgICAgICAgICAgICJuYW1lIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICJvcGVyYW5kVHlwZVNwZWNpZmllciIgOiB7CiAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIyNjg6MzItMjY4OjQwIiwKICAgICAgICAgICAgICAgICAgIm5hbWUiIDogIntodHRwOi8vaGw3Lm9yZy9maGlyfUdyb3VwVHlwZSIsCiAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJOYW1lZFR5cGVTcGVjaWZpZXIiCiAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgfSBdCiAgICAgICAgIH0sIHsKICAgICAgICAgICAgImxvY2F0b3IiIDogIjI2OToxLTI2OTo2NyIsCiAgICAgICAgICAgICJuYW1lIiA6ICJUb1N0cmluZyIsCiAgICAgICAgICAgICJjb250ZXh0IiA6ICJQYXRpZW50IiwKICAgICAgICAgICAgImFjY2Vzc0xldmVsIiA6ICJQdWJsaWMiLAogICAgICAgICAgICAidHlwZSIgOiAiRnVuY3Rpb25EZWYiLAogICAgICAgICAgICAiZXhwcmVzc2lvbiIgOiB7CiAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIyNjk6NTctMjY5OjY3IiwKICAgICAgICAgICAgICAgInBhdGgiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgInR5cGUiIDogIlByb3BlcnR5IiwKICAgICAgICAgICAgICAgInNvdXJjZSIgOiB7CiAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIyNjk6NTctMjY5OjYxIiwKICAgICAgICAgICAgICAgICAgIm5hbWUiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk9wZXJhbmRSZWYiCiAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgfSwKICAgICAgICAgICAgIm9wZXJhbmQiIDogWyB7CiAgICAgICAgICAgICAgICJuYW1lIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICJvcGVyYW5kVHlwZVNwZWNpZmllciIgOiB7CiAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIyNjk6MzItMjY5OjUzIiwKICAgICAgICAgICAgICAgICAgIm5hbWUiIDogIntodHRwOi8vaGw3Lm9yZy9maGlyfUd1aWRhbmNlUmVzcG9uc2VTdGF0dXMiLAogICAgICAgICAgICAgICAgICAidHlwZSIgOiAiTmFtZWRUeXBlU3BlY2lmaWVyIgogICAgICAgICAgICAgICB9CiAgICAgICAgICAgIH0gXQogICAgICAgICB9LCB7CiAgICAgICAgICAgICJsb2NhdG9yIiA6ICIyNzA6MS0yNzA6NjQiLAogICAgICAgICAgICAibmFtZSIgOiAiVG9TdHJpbmciLAogICAgICAgICAgICAiY29udGV4dCIgOiAiUGF0aWVudCIsCiAgICAgICAgICAgICJhY2Nlc3NMZXZlbCIgOiAiUHVibGljIiwKICAgICAgICAgICAgInR5cGUiIDogIkZ1bmN0aW9uRGVmIiwKICAgICAgICAgICAgImV4cHJlc3Npb24iIDogewogICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMjcwOjU0LTI3MDo2NCIsCiAgICAgICAgICAgICAgICJwYXRoIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICJ0eXBlIiA6ICJQcm9wZXJ0eSIsCiAgICAgICAgICAgICAgICJzb3VyY2UiIDogewogICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMjcwOjU0LTI3MDo1OCIsCiAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJPcGVyYW5kUmVmIgogICAgICAgICAgICAgICB9CiAgICAgICAgICAgIH0sCiAgICAgICAgICAgICJvcGVyYW5kIiA6IFsgewogICAgICAgICAgICAgICAibmFtZSIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAib3BlcmFuZFR5cGVTcGVjaWZpZXIiIDogewogICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMjcwOjMyLTI3MDo1MCIsCiAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJ7aHR0cDovL2hsNy5vcmcvZmhpcn1HdWlkZVBhZ2VHZW5lcmF0aW9uIiwKICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk5hbWVkVHlwZVNwZWNpZmllciIKICAgICAgICAgICAgICAgfQogICAgICAgICAgICB9IF0KICAgICAgICAgfSwgewogICAgICAgICAgICAibG9jYXRvciIgOiAiMjcxOjEtMjcxOjYzIiwKICAgICAgICAgICAgIm5hbWUiIDogIlRvU3RyaW5nIiwKICAgICAgICAgICAgImNvbnRleHQiIDogIlBhdGllbnQiLAogICAgICAgICAgICAiYWNjZXNzTGV2ZWwiIDogIlB1YmxpYyIsCiAgICAgICAgICAgICJ0eXBlIiA6ICJGdW5jdGlvbkRlZiIsCiAgICAgICAgICAgICJleHByZXNzaW9uIiA6IHsKICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjI3MTo1My0yNzE6NjMiLAogICAgICAgICAgICAgICAicGF0aCIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAidHlwZSIgOiAiUHJvcGVydHkiLAogICAgICAgICAgICAgICAic291cmNlIiA6IHsKICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjI3MTo1My0yNzE6NTciLAogICAgICAgICAgICAgICAgICAibmFtZSIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAgICAidHlwZSIgOiAiT3BlcmFuZFJlZiIKICAgICAgICAgICAgICAgfQogICAgICAgICAgICB9LAogICAgICAgICAgICAib3BlcmFuZCIgOiBbIHsKICAgICAgICAgICAgICAgIm5hbWUiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgIm9wZXJhbmRUeXBlU3BlY2lmaWVyIiA6IHsKICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjI3MTozMi0yNzE6NDkiLAogICAgICAgICAgICAgICAgICAibmFtZSIgOiAie2h0dHA6Ly9obDcub3JnL2ZoaXJ9R3VpZGVQYXJhbWV0ZXJDb2RlIiwKICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk5hbWVkVHlwZVNwZWNpZmllciIKICAgICAgICAgICAgICAgfQogICAgICAgICAgICB9IF0KICAgICAgICAgfSwgewogICAgICAgICAgICAibG9jYXRvciIgOiAiMjcyOjEtMjcyOjUzIiwKICAgICAgICAgICAgIm5hbWUiIDogIlRvU3RyaW5nIiwKICAgICAgICAgICAgImNvbnRleHQiIDogIlBhdGllbnQiLAogICAgICAgICAgICAiYWNjZXNzTGV2ZWwiIDogIlB1YmxpYyIsCiAgICAgICAgICAgICJ0eXBlIiA6ICJGdW5jdGlvbkRlZiIsCiAgICAgICAgICAgICJleHByZXNzaW9uIiA6IHsKICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjI3Mjo0My0yNzI6NTMiLAogICAgICAgICAgICAgICAicGF0aCIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAidHlwZSIgOiAiUHJvcGVydHkiLAogICAgICAgICAgICAgICAic291cmNlIiA6IHsKICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjI3Mjo0My0yNzI6NDciLAogICAgICAgICAgICAgICAgICAibmFtZSIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAgICAidHlwZSIgOiAiT3BlcmFuZFJlZiIKICAgICAgICAgICAgICAgfQogICAgICAgICAgICB9LAogICAgICAgICAgICAib3BlcmFuZCIgOiBbIHsKICAgICAgICAgICAgICAgIm5hbWUiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgIm9wZXJhbmRUeXBlU3BlY2lmaWVyIiA6IHsKICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjI3MjozMi0yNzI6MzkiLAogICAgICAgICAgICAgICAgICAibmFtZSIgOiAie2h0dHA6Ly9obDcub3JnL2ZoaXJ9SFRUUFZlcmIiLAogICAgICAgICAgICAgICAgICAidHlwZSIgOiAiTmFtZWRUeXBlU3BlY2lmaWVyIgogICAgICAgICAgICAgICB9CiAgICAgICAgICAgIH0gXQogICAgICAgICB9LCB7CiAgICAgICAgICAgICJsb2NhdG9yIiA6ICIyNzM6MS0yNzM6NTgiLAogICAgICAgICAgICAibmFtZSIgOiAiVG9TdHJpbmciLAogICAgICAgICAgICAiY29udGV4dCIgOiAiUGF0aWVudCIsCiAgICAgICAgICAgICJhY2Nlc3NMZXZlbCIgOiAiUHVibGljIiwKICAgICAgICAgICAgInR5cGUiIDogIkZ1bmN0aW9uRGVmIiwKICAgICAgICAgICAgImV4cHJlc3Npb24iIDogewogICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMjczOjQ4LTI3Mzo1OCIsCiAgICAgICAgICAgICAgICJwYXRoIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICJ0eXBlIiA6ICJQcm9wZXJ0eSIsCiAgICAgICAgICAgICAgICJzb3VyY2UiIDogewogICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMjczOjQ4LTI3Mzo1MiIsCiAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJPcGVyYW5kUmVmIgogICAgICAgICAgICAgICB9CiAgICAgICAgICAgIH0sCiAgICAgICAgICAgICJvcGVyYW5kIiA6IFsgewogICAgICAgICAgICAgICAibmFtZSIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAib3BlcmFuZFR5cGVTcGVjaWZpZXIiIDogewogICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMjczOjMyLTI3Mzo0NCIsCiAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJ7aHR0cDovL2hsNy5vcmcvZmhpcn1JZGVudGlmaWVyVXNlIiwKICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk5hbWVkVHlwZVNwZWNpZmllciIKICAgICAgICAgICAgICAgfQogICAgICAgICAgICB9IF0KICAgICAgICAgfSwgewogICAgICAgICAgICAibG9jYXRvciIgOiAiMjc0OjEtMjc0OjY3IiwKICAgICAgICAgICAgIm5hbWUiIDogIlRvU3RyaW5nIiwKICAgICAgICAgICAgImNvbnRleHQiIDogIlBhdGllbnQiLAogICAgICAgICAgICAiYWNjZXNzTGV2ZWwiIDogIlB1YmxpYyIsCiAgICAgICAgICAgICJ0eXBlIiA6ICJGdW5jdGlvbkRlZiIsCiAgICAgICAgICAgICJleHByZXNzaW9uIiA6IHsKICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjI3NDo1Ny0yNzQ6NjciLAogICAgICAgICAgICAgICAicGF0aCIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAidHlwZSIgOiAiUHJvcGVydHkiLAogICAgICAgICAgICAgICAic291cmNlIiA6IHsKICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjI3NDo1Ny0yNzQ6NjEiLAogICAgICAgICAgICAgICAgICAibmFtZSIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAgICAidHlwZSIgOiAiT3BlcmFuZFJlZiIKICAgICAgICAgICAgICAgfQogICAgICAgICAgICB9LAogICAgICAgICAgICAib3BlcmFuZCIgOiBbIHsKICAgICAgICAgICAgICAgIm5hbWUiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgIm9wZXJhbmRUeXBlU3BlY2lmaWVyIiA6IHsKICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjI3NDozMi0yNzQ6NTMiLAogICAgICAgICAgICAgICAgICAibmFtZSIgOiAie2h0dHA6Ly9obDcub3JnL2ZoaXJ9SWRlbnRpdHlBc3N1cmFuY2VMZXZlbCIsCiAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJOYW1lZFR5cGVTcGVjaWZpZXIiCiAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgfSBdCiAgICAgICAgIH0sIHsKICAgICAgICAgICAgImxvY2F0b3IiIDogIjI3NToxLTI3NTo2MyIsCiAgICAgICAgICAgICJuYW1lIiA6ICJUb1N0cmluZyIsCiAgICAgICAgICAgICJjb250ZXh0IiA6ICJQYXRpZW50IiwKICAgICAgICAgICAgImFjY2Vzc0xldmVsIiA6ICJQdWJsaWMiLAogICAgICAgICAgICAidHlwZSIgOiAiRnVuY3Rpb25EZWYiLAogICAgICAgICAgICAiZXhwcmVzc2lvbiIgOiB7CiAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIyNzU6NTMtMjc1OjYzIiwKICAgICAgICAgICAgICAgInBhdGgiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgInR5cGUiIDogIlByb3BlcnR5IiwKICAgICAgICAgICAgICAgInNvdXJjZSIgOiB7CiAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIyNzU6NTMtMjc1OjU3IiwKICAgICAgICAgICAgICAgICAgIm5hbWUiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk9wZXJhbmRSZWYiCiAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgfSwKICAgICAgICAgICAgIm9wZXJhbmQiIDogWyB7CiAgICAgICAgICAgICAgICJuYW1lIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICJvcGVyYW5kVHlwZVNwZWNpZmllciIgOiB7CiAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIyNzU6MzItMjc1OjQ5IiwKICAgICAgICAgICAgICAgICAgIm5hbWUiIDogIntodHRwOi8vaGw3Lm9yZy9maGlyfUltYWdpbmdTdHVkeVN0YXR1cyIsCiAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJOYW1lZFR5cGVTcGVjaWZpZXIiCiAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgfSBdCiAgICAgICAgIH0sIHsKICAgICAgICAgICAgImxvY2F0b3IiIDogIjI3NjoxLTI3Njo3MyIsCiAgICAgICAgICAgICJuYW1lIiA6ICJUb1N0cmluZyIsCiAgICAgICAgICAgICJjb250ZXh0IiA6ICJQYXRpZW50IiwKICAgICAgICAgICAgImFjY2Vzc0xldmVsIiA6ICJQdWJsaWMiLAogICAgICAgICAgICAidHlwZSIgOiAiRnVuY3Rpb25EZWYiLAogICAgICAgICAgICAiZXhwcmVzc2lvbiIgOiB7CiAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIyNzY6NjMtMjc2OjczIiwKICAgICAgICAgICAgICAgInBhdGgiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgInR5cGUiIDogIlByb3BlcnR5IiwKICAgICAgICAgICAgICAgInNvdXJjZSIgOiB7CiAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIyNzY6NjMtMjc2OjY3IiwKICAgICAgICAgICAgICAgICAgIm5hbWUiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk9wZXJhbmRSZWYiCiAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgfSwKICAgICAgICAgICAgIm9wZXJhbmQiIDogWyB7CiAgICAgICAgICAgICAgICJuYW1lIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICJvcGVyYW5kVHlwZVNwZWNpZmllciIgOiB7CiAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIyNzY6MzItMjc2OjU5IiwKICAgICAgICAgICAgICAgICAgIm5hbWUiIDogIntodHRwOi8vaGw3Lm9yZy9maGlyfUltbXVuaXphdGlvbkV2YWx1YXRpb25TdGF0dXMiLAogICAgICAgICAgICAgICAgICAidHlwZSIgOiAiTmFtZWRUeXBlU3BlY2lmaWVyIgogICAgICAgICAgICAgICB9CiAgICAgICAgICAgIH0gXQogICAgICAgICB9LCB7CiAgICAgICAgICAgICJsb2NhdG9yIiA6ICIyNzc6MS0yNzc6NjMiLAogICAgICAgICAgICAibmFtZSIgOiAiVG9TdHJpbmciLAogICAgICAgICAgICAiY29udGV4dCIgOiAiUGF0aWVudCIsCiAgICAgICAgICAgICJhY2Nlc3NMZXZlbCIgOiAiUHVibGljIiwKICAgICAgICAgICAgInR5cGUiIDogIkZ1bmN0aW9uRGVmIiwKICAgICAgICAgICAgImV4cHJlc3Npb24iIDogewogICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMjc3OjUzLTI3Nzo2MyIsCiAgICAgICAgICAgICAgICJwYXRoIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICJ0eXBlIiA6ICJQcm9wZXJ0eSIsCiAgICAgICAgICAgICAgICJzb3VyY2UiIDogewogICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMjc3OjUzLTI3Nzo1NyIsCiAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJPcGVyYW5kUmVmIgogICAgICAgICAgICAgICB9CiAgICAgICAgICAgIH0sCiAgICAgICAgICAgICJvcGVyYW5kIiA6IFsgewogICAgICAgICAgICAgICAibmFtZSIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAib3BlcmFuZFR5cGVTcGVjaWZpZXIiIDogewogICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMjc3OjMyLTI3Nzo0OSIsCiAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJ7aHR0cDovL2hsNy5vcmcvZmhpcn1JbW11bml6YXRpb25TdGF0dXMiLAogICAgICAgICAgICAgICAgICAidHlwZSIgOiAiTmFtZWRUeXBlU3BlY2lmaWVyIgogICAgICAgICAgICAgICB9CiAgICAgICAgICAgIH0gXQogICAgICAgICB9LCB7CiAgICAgICAgICAgICJsb2NhdG9yIiA6ICIyNzg6MS0yNzg6NzAiLAogICAgICAgICAgICAibmFtZSIgOiAiVG9TdHJpbmciLAogICAgICAgICAgICAiY29udGV4dCIgOiAiUGF0aWVudCIsCiAgICAgICAgICAgICJhY2Nlc3NMZXZlbCIgOiAiUHVibGljIiwKICAgICAgICAgICAgInR5cGUiIDogIkZ1bmN0aW9uRGVmIiwKICAgICAgICAgICAgImV4cHJlc3Npb24iIDogewogICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMjc4OjYwLTI3ODo3MCIsCiAgICAgICAgICAgICAgICJwYXRoIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICJ0eXBlIiA6ICJQcm9wZXJ0eSIsCiAgICAgICAgICAgICAgICJzb3VyY2UiIDogewogICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMjc4OjYwLTI3ODo2NCIsCiAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJPcGVyYW5kUmVmIgogICAgICAgICAgICAgICB9CiAgICAgICAgICAgIH0sCiAgICAgICAgICAgICJvcGVyYW5kIiA6IFsgewogICAgICAgICAgICAgICAibmFtZSIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAib3BlcmFuZFR5cGVTcGVjaWZpZXIiIDogewogICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMjc4OjMyLTI3ODo1NiIsCiAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJ7aHR0cDovL2hsNy5vcmcvZmhpcn1JbnZvaWNlUHJpY2VDb21wb25lbnRUeXBlIiwKICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk5hbWVkVHlwZVNwZWNpZmllciIKICAgICAgICAgICAgICAgfQogICAgICAgICAgICB9IF0KICAgICAgICAgfSwgewogICAgICAgICAgICAibG9jYXRvciIgOiAiMjc5OjEtMjc5OjU4IiwKICAgICAgICAgICAgIm5hbWUiIDogIlRvU3RyaW5nIiwKICAgICAgICAgICAgImNvbnRleHQiIDogIlBhdGllbnQiLAogICAgICAgICAgICAiYWNjZXNzTGV2ZWwiIDogIlB1YmxpYyIsCiAgICAgICAgICAgICJ0eXBlIiA6ICJGdW5jdGlvbkRlZiIsCiAgICAgICAgICAgICJleHByZXNzaW9uIiA6IHsKICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjI3OTo0OC0yNzk6NTgiLAogICAgICAgICAgICAgICAicGF0aCIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAidHlwZSIgOiAiUHJvcGVydHkiLAogICAgICAgICAgICAgICAic291cmNlIiA6IHsKICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjI3OTo0OC0yNzk6NTIiLAogICAgICAgICAgICAgICAgICAibmFtZSIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAgICAidHlwZSIgOiAiT3BlcmFuZFJlZiIKICAgICAgICAgICAgICAgfQogICAgICAgICAgICB9LAogICAgICAgICAgICAib3BlcmFuZCIgOiBbIHsKICAgICAgICAgICAgICAgIm5hbWUiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgIm9wZXJhbmRUeXBlU3BlY2lmaWVyIiA6IHsKICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjI3OTozMi0yNzk6NDQiLAogICAgICAgICAgICAgICAgICAibmFtZSIgOiAie2h0dHA6Ly9obDcub3JnL2ZoaXJ9SW52b2ljZVN0YXR1cyIsCiAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJOYW1lZFR5cGVTcGVjaWZpZXIiCiAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgfSBdCiAgICAgICAgIH0sIHsKICAgICAgICAgICAgImxvY2F0b3IiIDogIjI4MDoxLTI4MDo1OCIsCiAgICAgICAgICAgICJuYW1lIiA6ICJUb1N0cmluZyIsCiAgICAgICAgICAgICJjb250ZXh0IiA6ICJQYXRpZW50IiwKICAgICAgICAgICAgImFjY2Vzc0xldmVsIiA6ICJQdWJsaWMiLAogICAgICAgICAgICAidHlwZSIgOiAiRnVuY3Rpb25EZWYiLAogICAgICAgICAgICAiZXhwcmVzc2lvbiIgOiB7CiAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIyODA6NDgtMjgwOjU4IiwKICAgICAgICAgICAgICAgInBhdGgiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgInR5cGUiIDogIlByb3BlcnR5IiwKICAgICAgICAgICAgICAgInNvdXJjZSIgOiB7CiAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIyODA6NDgtMjgwOjUyIiwKICAgICAgICAgICAgICAgICAgIm5hbWUiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk9wZXJhbmRSZWYiCiAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgfSwKICAgICAgICAgICAgIm9wZXJhbmQiIDogWyB7CiAgICAgICAgICAgICAgICJuYW1lIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICJvcGVyYW5kVHlwZVNwZWNpZmllciIgOiB7CiAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIyODA6MzItMjgwOjQ0IiwKICAgICAgICAgICAgICAgICAgIm5hbWUiIDogIntodHRwOi8vaGw3Lm9yZy9maGlyfUlzc3VlU2V2ZXJpdHkiLAogICAgICAgICAgICAgICAgICAidHlwZSIgOiAiTmFtZWRUeXBlU3BlY2lmaWVyIgogICAgICAgICAgICAgICB9CiAgICAgICAgICAgIH0gXQogICAgICAgICB9LCB7CiAgICAgICAgICAgICJsb2NhdG9yIiA6ICIyODE6MS0yODE6NTQiLAogICAgICAgICAgICAibmFtZSIgOiAiVG9TdHJpbmciLAogICAgICAgICAgICAiY29udGV4dCIgOiAiUGF0aWVudCIsCiAgICAgICAgICAgICJhY2Nlc3NMZXZlbCIgOiAiUHVibGljIiwKICAgICAgICAgICAgInR5cGUiIDogIkZ1bmN0aW9uRGVmIiwKICAgICAgICAgICAgImV4cHJlc3Npb24iIDogewogICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMjgxOjQ0LTI4MTo1NCIsCiAgICAgICAgICAgICAgICJwYXRoIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICJ0eXBlIiA6ICJQcm9wZXJ0eSIsCiAgICAgICAgICAgICAgICJzb3VyY2UiIDogewogICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMjgxOjQ0LTI4MTo0OCIsCiAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJPcGVyYW5kUmVmIgogICAgICAgICAgICAgICB9CiAgICAgICAgICAgIH0sCiAgICAgICAgICAgICJvcGVyYW5kIiA6IFsgewogICAgICAgICAgICAgICAibmFtZSIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAib3BlcmFuZFR5cGVTcGVjaWZpZXIiIDogewogICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMjgxOjMyLTI4MTo0MCIsCiAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJ7aHR0cDovL2hsNy5vcmcvZmhpcn1Jc3N1ZVR5cGUiLAogICAgICAgICAgICAgICAgICAidHlwZSIgOiAiTmFtZWRUeXBlU3BlY2lmaWVyIgogICAgICAgICAgICAgICB9CiAgICAgICAgICAgIH0gXQogICAgICAgICB9LCB7CiAgICAgICAgICAgICJsb2NhdG9yIiA6ICIyODI6MS0yODI6NTMiLAogICAgICAgICAgICAibmFtZSIgOiAiVG9TdHJpbmciLAogICAgICAgICAgICAiY29udGV4dCIgOiAiUGF0aWVudCIsCiAgICAgICAgICAgICJhY2Nlc3NMZXZlbCIgOiAiUHVibGljIiwKICAgICAgICAgICAgInR5cGUiIDogIkZ1bmN0aW9uRGVmIiwKICAgICAgICAgICAgImV4cHJlc3Npb24iIDogewogICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMjgyOjQzLTI4Mjo1MyIsCiAgICAgICAgICAgICAgICJwYXRoIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICJ0eXBlIiA6ICJQcm9wZXJ0eSIsCiAgICAgICAgICAgICAgICJzb3VyY2UiIDogewogICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMjgyOjQzLTI4Mjo0NyIsCiAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJPcGVyYW5kUmVmIgogICAgICAgICAgICAgICB9CiAgICAgICAgICAgIH0sCiAgICAgICAgICAgICJvcGVyYW5kIiA6IFsgewogICAgICAgICAgICAgICAibmFtZSIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAib3BlcmFuZFR5cGVTcGVjaWZpZXIiIDogewogICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMjgyOjMyLTI4MjozOSIsCiAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJ7aHR0cDovL2hsNy5vcmcvZmhpcn1MaW5rVHlwZSIsCiAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJOYW1lZFR5cGVTcGVjaWZpZXIiCiAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgfSBdCiAgICAgICAgIH0sIHsKICAgICAgICAgICAgImxvY2F0b3IiIDogIjI4MzoxLTI4Mzo1NiIsCiAgICAgICAgICAgICJuYW1lIiA6ICJUb1N0cmluZyIsCiAgICAgICAgICAgICJjb250ZXh0IiA6ICJQYXRpZW50IiwKICAgICAgICAgICAgImFjY2Vzc0xldmVsIiA6ICJQdWJsaWMiLAogICAgICAgICAgICAidHlwZSIgOiAiRnVuY3Rpb25EZWYiLAogICAgICAgICAgICAiZXhwcmVzc2lvbiIgOiB7CiAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIyODM6NDYtMjgzOjU2IiwKICAgICAgICAgICAgICAgInBhdGgiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgInR5cGUiIDogIlByb3BlcnR5IiwKICAgICAgICAgICAgICAgInNvdXJjZSIgOiB7CiAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIyODM6NDYtMjgzOjUwIiwKICAgICAgICAgICAgICAgICAgIm5hbWUiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk9wZXJhbmRSZWYiCiAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgfSwKICAgICAgICAgICAgIm9wZXJhbmQiIDogWyB7CiAgICAgICAgICAgICAgICJuYW1lIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICJvcGVyYW5kVHlwZVNwZWNpZmllciIgOiB7CiAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIyODM6MzItMjgzOjQyIiwKICAgICAgICAgICAgICAgICAgIm5hbWUiIDogIntodHRwOi8vaGw3Lm9yZy9maGlyfUxpbmthZ2VUeXBlIiwKICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk5hbWVkVHlwZVNwZWNpZmllciIKICAgICAgICAgICAgICAgfQogICAgICAgICAgICB9IF0KICAgICAgICAgfSwgewogICAgICAgICAgICAibG9jYXRvciIgOiAiMjg0OjEtMjg0OjUzIiwKICAgICAgICAgICAgIm5hbWUiIDogIlRvU3RyaW5nIiwKICAgICAgICAgICAgImNvbnRleHQiIDogIlBhdGllbnQiLAogICAgICAgICAgICAiYWNjZXNzTGV2ZWwiIDogIlB1YmxpYyIsCiAgICAgICAgICAgICJ0eXBlIiA6ICJGdW5jdGlvbkRlZiIsCiAgICAgICAgICAgICJleHByZXNzaW9uIiA6IHsKICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjI4NDo0My0yODQ6NTMiLAogICAgICAgICAgICAgICAicGF0aCIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAidHlwZSIgOiAiUHJvcGVydHkiLAogICAgICAgICAgICAgICAic291cmNlIiA6IHsKICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjI4NDo0My0yODQ6NDciLAogICAgICAgICAgICAgICAgICAibmFtZSIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAgICAidHlwZSIgOiAiT3BlcmFuZFJlZiIKICAgICAgICAgICAgICAgfQogICAgICAgICAgICB9LAogICAgICAgICAgICAib3BlcmFuZCIgOiBbIHsKICAgICAgICAgICAgICAgIm5hbWUiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgIm9wZXJhbmRUeXBlU3BlY2lmaWVyIiA6IHsKICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjI4NDozMi0yODQ6MzkiLAogICAgICAgICAgICAgICAgICAibmFtZSIgOiAie2h0dHA6Ly9obDcub3JnL2ZoaXJ9TGlzdE1vZGUiLAogICAgICAgICAgICAgICAgICAidHlwZSIgOiAiTmFtZWRUeXBlU3BlY2lmaWVyIgogICAgICAgICAgICAgICB9CiAgICAgICAgICAgIH0gXQogICAgICAgICB9LCB7CiAgICAgICAgICAgICJsb2NhdG9yIiA6ICIyODU6MS0yODU6NTUiLAogICAgICAgICAgICAibmFtZSIgOiAiVG9TdHJpbmciLAogICAgICAgICAgICAiY29udGV4dCIgOiAiUGF0aWVudCIsCiAgICAgICAgICAgICJhY2Nlc3NMZXZlbCIgOiAiUHVibGljIiwKICAgICAgICAgICAgInR5cGUiIDogIkZ1bmN0aW9uRGVmIiwKICAgICAgICAgICAgImV4cHJlc3Npb24iIDogewogICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMjg1OjQ1LTI4NTo1NSIsCiAgICAgICAgICAgICAgICJwYXRoIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICJ0eXBlIiA6ICJQcm9wZXJ0eSIsCiAgICAgICAgICAgICAgICJzb3VyY2UiIDogewogICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMjg1OjQ1LTI4NTo0OSIsCiAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJPcGVyYW5kUmVmIgogICAgICAgICAgICAgICB9CiAgICAgICAgICAgIH0sCiAgICAgICAgICAgICJvcGVyYW5kIiA6IFsgewogICAgICAgICAgICAgICAibmFtZSIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAib3BlcmFuZFR5cGVTcGVjaWZpZXIiIDogewogICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMjg1OjMyLTI4NTo0MSIsCiAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJ7aHR0cDovL2hsNy5vcmcvZmhpcn1MaXN0U3RhdHVzIiwKICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk5hbWVkVHlwZVNwZWNpZmllciIKICAgICAgICAgICAgICAgfQogICAgICAgICAgICB9IF0KICAgICAgICAgfSwgewogICAgICAgICAgICAibG9jYXRvciIgOiAiMjg2OjEtMjg2OjU3IiwKICAgICAgICAgICAgIm5hbWUiIDogIlRvU3RyaW5nIiwKICAgICAgICAgICAgImNvbnRleHQiIDogIlBhdGllbnQiLAogICAgICAgICAgICAiYWNjZXNzTGV2ZWwiIDogIlB1YmxpYyIsCiAgICAgICAgICAgICJ0eXBlIiA6ICJGdW5jdGlvbkRlZiIsCiAgICAgICAgICAgICJleHByZXNzaW9uIiA6IHsKICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjI4Njo0Ny0yODY6NTciLAogICAgICAgICAgICAgICAicGF0aCIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAidHlwZSIgOiAiUHJvcGVydHkiLAogICAgICAgICAgICAgICAic291cmNlIiA6IHsKICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjI4Njo0Ny0yODY6NTEiLAogICAgICAgICAgICAgICAgICAibmFtZSIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAgICAidHlwZSIgOiAiT3BlcmFuZFJlZiIKICAgICAgICAgICAgICAgfQogICAgICAgICAgICB9LAogICAgICAgICAgICAib3BlcmFuZCIgOiBbIHsKICAgICAgICAgICAgICAgIm5hbWUiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgIm9wZXJhbmRUeXBlU3BlY2lmaWVyIiA6IHsKICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjI4NjozMi0yODY6NDMiLAogICAgICAgICAgICAgICAgICAibmFtZSIgOiAie2h0dHA6Ly9obDcub3JnL2ZoaXJ9TG9jYXRpb25Nb2RlIiwKICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk5hbWVkVHlwZVNwZWNpZmllciIKICAgICAgICAgICAgICAgfQogICAgICAgICAgICB9IF0KICAgICAgICAgfSwgewogICAgICAgICAgICAibG9jYXRvciIgOiAiMjg3OjEtMjg3OjU5IiwKICAgICAgICAgICAgIm5hbWUiIDogIlRvU3RyaW5nIiwKICAgICAgICAgICAgImNvbnRleHQiIDogIlBhdGllbnQiLAogICAgICAgICAgICAiYWNjZXNzTGV2ZWwiIDogIlB1YmxpYyIsCiAgICAgICAgICAgICJ0eXBlIiA6ICJGdW5jdGlvbkRlZiIsCiAgICAgICAgICAgICJleHByZXNzaW9uIiA6IHsKICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjI4Nzo0OS0yODc6NTkiLAogICAgICAgICAgICAgICAicGF0aCIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAidHlwZSIgOiAiUHJvcGVydHkiLAogICAgICAgICAgICAgICAic291cmNlIiA6IHsKICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjI4Nzo0OS0yODc6NTMiLAogICAgICAgICAgICAgICAgICAibmFtZSIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAgICAidHlwZSIgOiAiT3BlcmFuZFJlZiIKICAgICAgICAgICAgICAgfQogICAgICAgICAgICB9LAogICAgICAgICAgICAib3BlcmFuZCIgOiBbIHsKICAgICAgICAgICAgICAgIm5hbWUiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgIm9wZXJhbmRUeXBlU3BlY2lmaWVyIiA6IHsKICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjI4NzozMi0yODc6NDUiLAogICAgICAgICAgICAgICAgICAibmFtZSIgOiAie2h0dHA6Ly9obDcub3JnL2ZoaXJ9TG9jYXRpb25TdGF0dXMiLAogICAgICAgICAgICAgICAgICAidHlwZSIgOiAiTmFtZWRUeXBlU3BlY2lmaWVyIgogICAgICAgICAgICAgICB9CiAgICAgICAgICAgIH0gXQogICAgICAgICB9LCB7CiAgICAgICAgICAgICJsb2NhdG9yIiA6ICIyODg6MS0yODg6NjQiLAogICAgICAgICAgICAibmFtZSIgOiAiVG9TdHJpbmciLAogICAgICAgICAgICAiY29udGV4dCIgOiAiUGF0aWVudCIsCiAgICAgICAgICAgICJhY2Nlc3NMZXZlbCIgOiAiUHVibGljIiwKICAgICAgICAgICAgInR5cGUiIDogIkZ1bmN0aW9uRGVmIiwKICAgICAgICAgICAgImV4cHJlc3Npb24iIDogewogICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMjg4OjU0LTI4ODo2NCIsCiAgICAgICAgICAgICAgICJwYXRoIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICJ0eXBlIiA6ICJQcm9wZXJ0eSIsCiAgICAgICAgICAgICAgICJzb3VyY2UiIDogewogICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMjg4OjU0LTI4ODo1OCIsCiAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJPcGVyYW5kUmVmIgogICAgICAgICAgICAgICB9CiAgICAgICAgICAgIH0sCiAgICAgICAgICAgICJvcGVyYW5kIiA6IFsgewogICAgICAgICAgICAgICAibmFtZSIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAib3BlcmFuZFR5cGVTcGVjaWZpZXIiIDogewogICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMjg4OjMyLTI4ODo1MCIsCiAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJ7aHR0cDovL2hsNy5vcmcvZmhpcn1NZWFzdXJlUmVwb3J0U3RhdHVzIiwKICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk5hbWVkVHlwZVNwZWNpZmllciIKICAgICAgICAgICAgICAgfQogICAgICAgICAgICB9IF0KICAgICAgICAgfSwgewogICAgICAgICAgICAibG9jYXRvciIgOiAiMjg5OjEtMjg5OjYyIiwKICAgICAgICAgICAgIm5hbWUiIDogIlRvU3RyaW5nIiwKICAgICAgICAgICAgImNvbnRleHQiIDogIlBhdGllbnQiLAogICAgICAgICAgICAiYWNjZXNzTGV2ZWwiIDogIlB1YmxpYyIsCiAgICAgICAgICAgICJ0eXBlIiA6ICJGdW5jdGlvbkRlZiIsCiAgICAgICAgICAgICJleHByZXNzaW9uIiA6IHsKICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjI4OTo1Mi0yODk6NjIiLAogICAgICAgICAgICAgICAicGF0aCIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAidHlwZSIgOiAiUHJvcGVydHkiLAogICAgICAgICAgICAgICAic291cmNlIiA6IHsKICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjI4OTo1Mi0yODk6NTYiLAogICAgICAgICAgICAgICAgICAibmFtZSIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAgICAidHlwZSIgOiAiT3BlcmFuZFJlZiIKICAgICAgICAgICAgICAgfQogICAgICAgICAgICB9LAogICAgICAgICAgICAib3BlcmFuZCIgOiBbIHsKICAgICAgICAgICAgICAgIm5hbWUiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgIm9wZXJhbmRUeXBlU3BlY2lmaWVyIiA6IHsKICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjI4OTozMi0yODk6NDgiLAogICAgICAgICAgICAgICAgICAibmFtZSIgOiAie2h0dHA6Ly9obDcub3JnL2ZoaXJ9TWVhc3VyZVJlcG9ydFR5cGUiLAogICAgICAgICAgICAgICAgICAidHlwZSIgOiAiTmFtZWRUeXBlU3BlY2lmaWVyIgogICAgICAgICAgICAgICB9CiAgICAgICAgICAgIH0gXQogICAgICAgICB9LCB7CiAgICAgICAgICAgICJsb2NhdG9yIiA6ICIyOTA6MS0yOTA6NTYiLAogICAgICAgICAgICAibmFtZSIgOiAiVG9TdHJpbmciLAogICAgICAgICAgICAiY29udGV4dCIgOiAiUGF0aWVudCIsCiAgICAgICAgICAgICJhY2Nlc3NMZXZlbCIgOiAiUHVibGljIiwKICAgICAgICAgICAgInR5cGUiIDogIkZ1bmN0aW9uRGVmIiwKICAgICAgICAgICAgImV4cHJlc3Npb24iIDogewogICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMjkwOjQ2LTI5MDo1NiIsCiAgICAgICAgICAgICAgICJwYXRoIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICJ0eXBlIiA6ICJQcm9wZXJ0eSIsCiAgICAgICAgICAgICAgICJzb3VyY2UiIDogewogICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMjkwOjQ2LTI5MDo1MCIsCiAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJPcGVyYW5kUmVmIgogICAgICAgICAgICAgICB9CiAgICAgICAgICAgIH0sCiAgICAgICAgICAgICJvcGVyYW5kIiA6IFsgewogICAgICAgICAgICAgICAibmFtZSIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAib3BlcmFuZFR5cGVTcGVjaWZpZXIiIDogewogICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMjkwOjMyLTI5MDo0MiIsCiAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJ7aHR0cDovL2hsNy5vcmcvZmhpcn1NZWRpYVN0YXR1cyIsCiAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJOYW1lZFR5cGVTcGVjaWZpZXIiCiAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgfSBdCiAgICAgICAgIH0sIHsKICAgICAgICAgICAgImxvY2F0b3IiIDogIjI5MToxLTI5MTo3NSIsCiAgICAgICAgICAgICJuYW1lIiA6ICJUb1N0cmluZyIsCiAgICAgICAgICAgICJjb250ZXh0IiA6ICJQYXRpZW50IiwKICAgICAgICAgICAgImFjY2Vzc0xldmVsIiA6ICJQdWJsaWMiLAogICAgICAgICAgICAidHlwZSIgOiAiRnVuY3Rpb25EZWYiLAogICAgICAgICAgICAiZXhwcmVzc2lvbiIgOiB7CiAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIyOTE6NjUtMjkxOjc1IiwKICAgICAgICAgICAgICAgInBhdGgiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgInR5cGUiIDogIlByb3BlcnR5IiwKICAgICAgICAgICAgICAgInNvdXJjZSIgOiB7CiAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIyOTE6NjUtMjkxOjY5IiwKICAgICAgICAgICAgICAgICAgIm5hbWUiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk9wZXJhbmRSZWYiCiAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgfSwKICAgICAgICAgICAgIm9wZXJhbmQiIDogWyB7CiAgICAgICAgICAgICAgICJuYW1lIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICJvcGVyYW5kVHlwZVNwZWNpZmllciIgOiB7CiAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIyOTE6MzItMjkxOjYxIiwKICAgICAgICAgICAgICAgICAgIm5hbWUiIDogIntodHRwOi8vaGw3Lm9yZy9maGlyfU1lZGljYXRpb25BZG1pbmlzdHJhdGlvblN0YXR1cyIsCiAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJOYW1lZFR5cGVTcGVjaWZpZXIiCiAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgfSBdCiAgICAgICAgIH0sIHsKICAgICAgICAgICAgImxvY2F0b3IiIDogIjI5MjoxLTI5Mjo2OSIsCiAgICAgICAgICAgICJuYW1lIiA6ICJUb1N0cmluZyIsCiAgICAgICAgICAgICJjb250ZXh0IiA6ICJQYXRpZW50IiwKICAgICAgICAgICAgImFjY2Vzc0xldmVsIiA6ICJQdWJsaWMiLAogICAgICAgICAgICAidHlwZSIgOiAiRnVuY3Rpb25EZWYiLAogICAgICAgICAgICAiZXhwcmVzc2lvbiIgOiB7CiAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIyOTI6NTktMjkyOjY5IiwKICAgICAgICAgICAgICAgInBhdGgiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgInR5cGUiIDogIlByb3BlcnR5IiwKICAgICAgICAgICAgICAgInNvdXJjZSIgOiB7CiAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIyOTI6NTktMjkyOjYzIiwKICAgICAgICAgICAgICAgICAgIm5hbWUiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk9wZXJhbmRSZWYiCiAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgfSwKICAgICAgICAgICAgIm9wZXJhbmQiIDogWyB7CiAgICAgICAgICAgICAgICJuYW1lIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICJvcGVyYW5kVHlwZVNwZWNpZmllciIgOiB7CiAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIyOTI6MzItMjkyOjU1IiwKICAgICAgICAgICAgICAgICAgIm5hbWUiIDogIntodHRwOi8vaGw3Lm9yZy9maGlyfU1lZGljYXRpb25EaXNwZW5zZVN0YXR1cyIsCiAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJOYW1lZFR5cGVTcGVjaWZpZXIiCiAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgfSBdCiAgICAgICAgIH0sIHsKICAgICAgICAgICAgImxvY2F0b3IiIDogIjI5MzoxLTI5Mzo3MCIsCiAgICAgICAgICAgICJuYW1lIiA6ICJUb1N0cmluZyIsCiAgICAgICAgICAgICJjb250ZXh0IiA6ICJQYXRpZW50IiwKICAgICAgICAgICAgImFjY2Vzc0xldmVsIiA6ICJQdWJsaWMiLAogICAgICAgICAgICAidHlwZSIgOiAiRnVuY3Rpb25EZWYiLAogICAgICAgICAgICAiZXhwcmVzc2lvbiIgOiB7CiAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIyOTM6NjAtMjkzOjcwIiwKICAgICAgICAgICAgICAgInBhdGgiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgInR5cGUiIDogIlByb3BlcnR5IiwKICAgICAgICAgICAgICAgInNvdXJjZSIgOiB7CiAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIyOTM6NjAtMjkzOjY0IiwKICAgICAgICAgICAgICAgICAgIm5hbWUiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk9wZXJhbmRSZWYiCiAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgfSwKICAgICAgICAgICAgIm9wZXJhbmQiIDogWyB7CiAgICAgICAgICAgICAgICJuYW1lIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICJvcGVyYW5kVHlwZVNwZWNpZmllciIgOiB7CiAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIyOTM6MzItMjkzOjU2IiwKICAgICAgICAgICAgICAgICAgIm5hbWUiIDogIntodHRwOi8vaGw3Lm9yZy9maGlyfU1lZGljYXRpb25Lbm93bGVkZ2VTdGF0dXMiLAogICAgICAgICAgICAgICAgICAidHlwZSIgOiAiTmFtZWRUeXBlU3BlY2lmaWVyIgogICAgICAgICAgICAgICB9CiAgICAgICAgICAgIH0gXQogICAgICAgICB9LCB7CiAgICAgICAgICAgICJsb2NhdG9yIiA6ICIyOTQ6MS0yOTQ6NjgiLAogICAgICAgICAgICAibmFtZSIgOiAiVG9TdHJpbmciLAogICAgICAgICAgICAiY29udGV4dCIgOiAiUGF0aWVudCIsCiAgICAgICAgICAgICJhY2Nlc3NMZXZlbCIgOiAiUHVibGljIiwKICAgICAgICAgICAgInR5cGUiIDogIkZ1bmN0aW9uRGVmIiwKICAgICAgICAgICAgImV4cHJlc3Npb24iIDogewogICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMjk0OjU4LTI5NDo2OCIsCiAgICAgICAgICAgICAgICJwYXRoIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICJ0eXBlIiA6ICJQcm9wZXJ0eSIsCiAgICAgICAgICAgICAgICJzb3VyY2UiIDogewogICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMjk0OjU4LTI5NDo2MiIsCiAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJPcGVyYW5kUmVmIgogICAgICAgICAgICAgICB9CiAgICAgICAgICAgIH0sCiAgICAgICAgICAgICJvcGVyYW5kIiA6IFsgewogICAgICAgICAgICAgICAibmFtZSIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAib3BlcmFuZFR5cGVTcGVjaWZpZXIiIDogewogICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMjk0OjMyLTI5NDo1NCIsCiAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJ7aHR0cDovL2hsNy5vcmcvZmhpcn1NZWRpY2F0aW9uUmVxdWVzdEludGVudCIsCiAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJOYW1lZFR5cGVTcGVjaWZpZXIiCiAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgfSBdCiAgICAgICAgIH0sIHsKICAgICAgICAgICAgImxvY2F0b3IiIDogIjI5NToxLTI5NTo3MCIsCiAgICAgICAgICAgICJuYW1lIiA6ICJUb1N0cmluZyIsCiAgICAgICAgICAgICJjb250ZXh0IiA6ICJQYXRpZW50IiwKICAgICAgICAgICAgImFjY2Vzc0xldmVsIiA6ICJQdWJsaWMiLAogICAgICAgICAgICAidHlwZSIgOiAiRnVuY3Rpb25EZWYiLAogICAgICAgICAgICAiZXhwcmVzc2lvbiIgOiB7CiAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIyOTU6NjAtMjk1OjcwIiwKICAgICAgICAgICAgICAgInBhdGgiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgInR5cGUiIDogIlByb3BlcnR5IiwKICAgICAgICAgICAgICAgInNvdXJjZSIgOiB7CiAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIyOTU6NjAtMjk1OjY0IiwKICAgICAgICAgICAgICAgICAgIm5hbWUiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk9wZXJhbmRSZWYiCiAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgfSwKICAgICAgICAgICAgIm9wZXJhbmQiIDogWyB7CiAgICAgICAgICAgICAgICJuYW1lIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICJvcGVyYW5kVHlwZVNwZWNpZmllciIgOiB7CiAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIyOTU6MzItMjk1OjU2IiwKICAgICAgICAgICAgICAgICAgIm5hbWUiIDogIntodHRwOi8vaGw3Lm9yZy9maGlyfU1lZGljYXRpb25SZXF1ZXN0UHJpb3JpdHkiLAogICAgICAgICAgICAgICAgICAidHlwZSIgOiAiTmFtZWRUeXBlU3BlY2lmaWVyIgogICAgICAgICAgICAgICB9CiAgICAgICAgICAgIH0gXQogICAgICAgICB9LCB7CiAgICAgICAgICAgICJsb2NhdG9yIiA6ICIyOTY6MS0yOTY6NjgiLAogICAgICAgICAgICAibmFtZSIgOiAiVG9TdHJpbmciLAogICAgICAgICAgICAiY29udGV4dCIgOiAiUGF0aWVudCIsCiAgICAgICAgICAgICJhY2Nlc3NMZXZlbCIgOiAiUHVibGljIiwKICAgICAgICAgICAgInR5cGUiIDogIkZ1bmN0aW9uRGVmIiwKICAgICAgICAgICAgImV4cHJlc3Npb24iIDogewogICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMjk2OjU4LTI5Njo2OCIsCiAgICAgICAgICAgICAgICJwYXRoIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICJ0eXBlIiA6ICJQcm9wZXJ0eSIsCiAgICAgICAgICAgICAgICJzb3VyY2UiIDogewogICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMjk2OjU4LTI5Njo2MiIsCiAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJPcGVyYW5kUmVmIgogICAgICAgICAgICAgICB9CiAgICAgICAgICAgIH0sCiAgICAgICAgICAgICJvcGVyYW5kIiA6IFsgewogICAgICAgICAgICAgICAibmFtZSIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAib3BlcmFuZFR5cGVTcGVjaWZpZXIiIDogewogICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMjk2OjMyLTI5Njo1NCIsCiAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJ7aHR0cDovL2hsNy5vcmcvZmhpcn1NZWRpY2F0aW9uUmVxdWVzdFN0YXR1cyIsCiAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJOYW1lZFR5cGVTcGVjaWZpZXIiCiAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgfSBdCiAgICAgICAgIH0sIHsKICAgICAgICAgICAgImxvY2F0b3IiIDogIjI5NzoxLTI5Nzo3MCIsCiAgICAgICAgICAgICJuYW1lIiA6ICJUb1N0cmluZyIsCiAgICAgICAgICAgICJjb250ZXh0IiA6ICJQYXRpZW50IiwKICAgICAgICAgICAgImFjY2Vzc0xldmVsIiA6ICJQdWJsaWMiLAogICAgICAgICAgICAidHlwZSIgOiAiRnVuY3Rpb25EZWYiLAogICAgICAgICAgICAiZXhwcmVzc2lvbiIgOiB7CiAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIyOTc6NjAtMjk3OjcwIiwKICAgICAgICAgICAgICAgInBhdGgiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgInR5cGUiIDogIlByb3BlcnR5IiwKICAgICAgICAgICAgICAgInNvdXJjZSIgOiB7CiAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIyOTc6NjAtMjk3OjY0IiwKICAgICAgICAgICAgICAgICAgIm5hbWUiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk9wZXJhbmRSZWYiCiAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgfSwKICAgICAgICAgICAgIm9wZXJhbmQiIDogWyB7CiAgICAgICAgICAgICAgICJuYW1lIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICJvcGVyYW5kVHlwZVNwZWNpZmllciIgOiB7CiAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIyOTc6MzItMjk3OjU2IiwKICAgICAgICAgICAgICAgICAgIm5hbWUiIDogIntodHRwOi8vaGw3Lm9yZy9maGlyfU1lZGljYXRpb25TdGF0ZW1lbnRTdGF0dXMiLAogICAgICAgICAgICAgICAgICAidHlwZSIgOiAiTmFtZWRUeXBlU3BlY2lmaWVyIgogICAgICAgICAgICAgICB9CiAgICAgICAgICAgIH0gXQogICAgICAgICB9LCB7CiAgICAgICAgICAgICJsb2NhdG9yIiA6ICIyOTg6MS0yOTg6NjEiLAogICAgICAgICAgICAibmFtZSIgOiAiVG9TdHJpbmciLAogICAgICAgICAgICAiY29udGV4dCIgOiAiUGF0aWVudCIsCiAgICAgICAgICAgICJhY2Nlc3NMZXZlbCIgOiAiUHVibGljIiwKICAgICAgICAgICAgInR5cGUiIDogIkZ1bmN0aW9uRGVmIiwKICAgICAgICAgICAgImV4cHJlc3Npb24iIDogewogICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMjk4OjUxLTI5ODo2MSIsCiAgICAgICAgICAgICAgICJwYXRoIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICJ0eXBlIiA6ICJQcm9wZXJ0eSIsCiAgICAgICAgICAgICAgICJzb3VyY2UiIDogewogICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMjk4OjUxLTI5ODo1NSIsCiAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJPcGVyYW5kUmVmIgogICAgICAgICAgICAgICB9CiAgICAgICAgICAgIH0sCiAgICAgICAgICAgICJvcGVyYW5kIiA6IFsgewogICAgICAgICAgICAgICAibmFtZSIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAib3BlcmFuZFR5cGVTcGVjaWZpZXIiIDogewogICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMjk4OjMyLTI5ODo0NyIsCiAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJ7aHR0cDovL2hsNy5vcmcvZmhpcn1NZWRpY2F0aW9uU3RhdHVzIiwKICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk5hbWVkVHlwZVNwZWNpZmllciIKICAgICAgICAgICAgICAgfQogICAgICAgICAgICB9IF0KICAgICAgICAgfSwgewogICAgICAgICAgICAibG9jYXRvciIgOiAiMjk5OjEtMjk5OjcyIiwKICAgICAgICAgICAgIm5hbWUiIDogIlRvU3RyaW5nIiwKICAgICAgICAgICAgImNvbnRleHQiIDogIlBhdGllbnQiLAogICAgICAgICAgICAiYWNjZXNzTGV2ZWwiIDogIlB1YmxpYyIsCiAgICAgICAgICAgICJ0eXBlIiA6ICJGdW5jdGlvbkRlZiIsCiAgICAgICAgICAgICJleHByZXNzaW9uIiA6IHsKICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjI5OTo2Mi0yOTk6NzIiLAogICAgICAgICAgICAgICAicGF0aCIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAidHlwZSIgOiAiUHJvcGVydHkiLAogICAgICAgICAgICAgICAic291cmNlIiA6IHsKICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjI5OTo2Mi0yOTk6NjYiLAogICAgICAgICAgICAgICAgICAibmFtZSIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAgICAidHlwZSIgOiAiT3BlcmFuZFJlZiIKICAgICAgICAgICAgICAgfQogICAgICAgICAgICB9LAogICAgICAgICAgICAib3BlcmFuZCIgOiBbIHsKICAgICAgICAgICAgICAgIm5hbWUiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgIm9wZXJhbmRUeXBlU3BlY2lmaWVyIiA6IHsKICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjI5OTozMi0yOTk6NTgiLAogICAgICAgICAgICAgICAgICAibmFtZSIgOiAie2h0dHA6Ly9obDcub3JnL2ZoaXJ9TWVzc2FnZVNpZ25pZmljYW5jZUNhdGVnb3J5IiwKICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk5hbWVkVHlwZVNwZWNpZmllciIKICAgICAgICAgICAgICAgfQogICAgICAgICAgICB9IF0KICAgICAgICAgfSwgewogICAgICAgICAgICAibG9jYXRvciIgOiAiMzAwOjEtMzAwOjc1IiwKICAgICAgICAgICAgIm5hbWUiIDogIlRvU3RyaW5nIiwKICAgICAgICAgICAgImNvbnRleHQiIDogIlBhdGllbnQiLAogICAgICAgICAgICAiYWNjZXNzTGV2ZWwiIDogIlB1YmxpYyIsCiAgICAgICAgICAgICJ0eXBlIiA6ICJGdW5jdGlvbkRlZiIsCiAgICAgICAgICAgICJleHByZXNzaW9uIiA6IHsKICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjMwMDo2NS0zMDA6NzUiLAogICAgICAgICAgICAgICAicGF0aCIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAidHlwZSIgOiAiUHJvcGVydHkiLAogICAgICAgICAgICAgICAic291cmNlIiA6IHsKICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjMwMDo2NS0zMDA6NjkiLAogICAgICAgICAgICAgICAgICAibmFtZSIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAgICAidHlwZSIgOiAiT3BlcmFuZFJlZiIKICAgICAgICAgICAgICAgfQogICAgICAgICAgICB9LAogICAgICAgICAgICAib3BlcmFuZCIgOiBbIHsKICAgICAgICAgICAgICAgIm5hbWUiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgIm9wZXJhbmRUeXBlU3BlY2lmaWVyIiA6IHsKICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjMwMDozMi0zMDA6NjEiLAogICAgICAgICAgICAgICAgICAibmFtZSIgOiAie2h0dHA6Ly9obDcub3JnL2ZoaXJ9TWVzc2FnZWhlYWRlcl9SZXNwb25zZV9SZXF1ZXN0IiwKICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk5hbWVkVHlwZVNwZWNpZmllciIKICAgICAgICAgICAgICAgfQogICAgICAgICAgICB9IF0KICAgICAgICAgfSwgewogICAgICAgICAgICAibG9jYXRvciIgOiAiMzAxOjEtMzAxOjUzIiwKICAgICAgICAgICAgIm5hbWUiIDogIlRvU3RyaW5nIiwKICAgICAgICAgICAgImNvbnRleHQiIDogIlBhdGllbnQiLAogICAgICAgICAgICAiYWNjZXNzTGV2ZWwiIDogIlB1YmxpYyIsCiAgICAgICAgICAgICJ0eXBlIiA6ICJGdW5jdGlvbkRlZiIsCiAgICAgICAgICAgICJleHByZXNzaW9uIiA6IHsKICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjMwMTo0My0zMDE6NTMiLAogICAgICAgICAgICAgICAicGF0aCIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAidHlwZSIgOiAiUHJvcGVydHkiLAogICAgICAgICAgICAgICAic291cmNlIiA6IHsKICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjMwMTo0My0zMDE6NDciLAogICAgICAgICAgICAgICAgICAibmFtZSIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAgICAidHlwZSIgOiAiT3BlcmFuZFJlZiIKICAgICAgICAgICAgICAgfQogICAgICAgICAgICB9LAogICAgICAgICAgICAib3BlcmFuZCIgOiBbIHsKICAgICAgICAgICAgICAgIm5hbWUiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgIm9wZXJhbmRUeXBlU3BlY2lmaWVyIiA6IHsKICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjMwMTozMi0zMDE6MzkiLAogICAgICAgICAgICAgICAgICAibmFtZSIgOiAie2h0dHA6Ly9obDcub3JnL2ZoaXJ9TWltZVR5cGUiLAogICAgICAgICAgICAgICAgICAidHlwZSIgOiAiTmFtZWRUeXBlU3BlY2lmaWVyIgogICAgICAgICAgICAgICB9CiAgICAgICAgICAgIH0gXQogICAgICAgICB9LCB7CiAgICAgICAgICAgICJsb2NhdG9yIiA6ICIzMDI6MS0zMDI6NTIiLAogICAgICAgICAgICAibmFtZSIgOiAiVG9TdHJpbmciLAogICAgICAgICAgICAiY29udGV4dCIgOiAiUGF0aWVudCIsCiAgICAgICAgICAgICJhY2Nlc3NMZXZlbCIgOiAiUHVibGljIiwKICAgICAgICAgICAgInR5cGUiIDogIkZ1bmN0aW9uRGVmIiwKICAgICAgICAgICAgImV4cHJlc3Npb24iIDogewogICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMzAyOjQyLTMwMjo1MiIsCiAgICAgICAgICAgICAgICJwYXRoIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICJ0eXBlIiA6ICJQcm9wZXJ0eSIsCiAgICAgICAgICAgICAgICJzb3VyY2UiIDogewogICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMzAyOjQyLTMwMjo0NiIsCiAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJPcGVyYW5kUmVmIgogICAgICAgICAgICAgICB9CiAgICAgICAgICAgIH0sCiAgICAgICAgICAgICJvcGVyYW5kIiA6IFsgewogICAgICAgICAgICAgICAibmFtZSIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAib3BlcmFuZFR5cGVTcGVjaWZpZXIiIDogewogICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMzAyOjMyLTMwMjozOCIsCiAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJ7aHR0cDovL2hsNy5vcmcvZmhpcn1OYW1lVXNlIiwKICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk5hbWVkVHlwZVNwZWNpZmllciIKICAgICAgICAgICAgICAgfQogICAgICAgICAgICB9IF0KICAgICAgICAgfSwgewogICAgICAgICAgICAibG9jYXRvciIgOiAiMzAzOjEtMzAzOjcxIiwKICAgICAgICAgICAgIm5hbWUiIDogIlRvU3RyaW5nIiwKICAgICAgICAgICAgImNvbnRleHQiIDogIlBhdGllbnQiLAogICAgICAgICAgICAiYWNjZXNzTGV2ZWwiIDogIlB1YmxpYyIsCiAgICAgICAgICAgICJ0eXBlIiA6ICJGdW5jdGlvbkRlZiIsCiAgICAgICAgICAgICJleHByZXNzaW9uIiA6IHsKICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjMwMzo2MS0zMDM6NzEiLAogICAgICAgICAgICAgICAicGF0aCIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAidHlwZSIgOiAiUHJvcGVydHkiLAogICAgICAgICAgICAgICAic291cmNlIiA6IHsKICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjMwMzo2MS0zMDM6NjUiLAogICAgICAgICAgICAgICAgICAibmFtZSIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAgICAidHlwZSIgOiAiT3BlcmFuZFJlZiIKICAgICAgICAgICAgICAgfQogICAgICAgICAgICB9LAogICAgICAgICAgICAib3BlcmFuZCIgOiBbIHsKICAgICAgICAgICAgICAgIm5hbWUiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgIm9wZXJhbmRUeXBlU3BlY2lmaWVyIiA6IHsKICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjMwMzozMi0zMDM6NTciLAogICAgICAgICAgICAgICAgICAibmFtZSIgOiAie2h0dHA6Ly9obDcub3JnL2ZoaXJ9TmFtaW5nU3lzdGVtSWRlbnRpZmllclR5cGUiLAogICAgICAgICAgICAgICAgICAidHlwZSIgOiAiTmFtZWRUeXBlU3BlY2lmaWVyIgogICAgICAgICAgICAgICB9CiAgICAgICAgICAgIH0gXQogICAgICAgICB9LCB7CiAgICAgICAgICAgICJsb2NhdG9yIiA6ICIzMDQ6MS0zMDQ6NjEiLAogICAgICAgICAgICAibmFtZSIgOiAiVG9TdHJpbmciLAogICAgICAgICAgICAiY29udGV4dCIgOiAiUGF0aWVudCIsCiAgICAgICAgICAgICJhY2Nlc3NMZXZlbCIgOiAiUHVibGljIiwKICAgICAgICAgICAgInR5cGUiIDogIkZ1bmN0aW9uRGVmIiwKICAgICAgICAgICAgImV4cHJlc3Npb24iIDogewogICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMzA0OjUxLTMwNDo2MSIsCiAgICAgICAgICAgICAgICJwYXRoIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICJ0eXBlIiA6ICJQcm9wZXJ0eSIsCiAgICAgICAgICAgICAgICJzb3VyY2UiIDogewogICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMzA0OjUxLTMwNDo1NSIsCiAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJPcGVyYW5kUmVmIgogICAgICAgICAgICAgICB9CiAgICAgICAgICAgIH0sCiAgICAgICAgICAgICJvcGVyYW5kIiA6IFsgewogICAgICAgICAgICAgICAibmFtZSIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAib3BlcmFuZFR5cGVTcGVjaWZpZXIiIDogewogICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMzA0OjMyLTMwNDo0NyIsCiAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJ7aHR0cDovL2hsNy5vcmcvZmhpcn1OYW1pbmdTeXN0ZW1UeXBlIiwKICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk5hbWVkVHlwZVNwZWNpZmllciIKICAgICAgICAgICAgICAgfQogICAgICAgICAgICB9IF0KICAgICAgICAgfSwgewogICAgICAgICAgICAibG9jYXRvciIgOiAiMzA1OjEtMzA1OjYwIiwKICAgICAgICAgICAgIm5hbWUiIDogIlRvU3RyaW5nIiwKICAgICAgICAgICAgImNvbnRleHQiIDogIlBhdGllbnQiLAogICAgICAgICAgICAiYWNjZXNzTGV2ZWwiIDogIlB1YmxpYyIsCiAgICAgICAgICAgICJ0eXBlIiA6ICJGdW5jdGlvbkRlZiIsCiAgICAgICAgICAgICJleHByZXNzaW9uIiA6IHsKICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjMwNTo1MC0zMDU6NjAiLAogICAgICAgICAgICAgICAicGF0aCIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAidHlwZSIgOiAiUHJvcGVydHkiLAogICAgICAgICAgICAgICAic291cmNlIiA6IHsKICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjMwNTo1MC0zMDU6NTQiLAogICAgICAgICAgICAgICAgICAibmFtZSIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAgICAidHlwZSIgOiAiT3BlcmFuZFJlZiIKICAgICAgICAgICAgICAgfQogICAgICAgICAgICB9LAogICAgICAgICAgICAib3BlcmFuZCIgOiBbIHsKICAgICAgICAgICAgICAgIm5hbWUiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgIm9wZXJhbmRUeXBlU3BlY2lmaWVyIiA6IHsKICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjMwNTozMi0zMDU6NDYiLAogICAgICAgICAgICAgICAgICAibmFtZSIgOiAie2h0dHA6Ly9obDcub3JnL2ZoaXJ9TmFycmF0aXZlU3RhdHVzIiwKICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk5hbWVkVHlwZVNwZWNpZmllciIKICAgICAgICAgICAgICAgfQogICAgICAgICAgICB9IF0KICAgICAgICAgfSwgewogICAgICAgICAgICAibG9jYXRvciIgOiAiMzA2OjEtMzA2OjUzIiwKICAgICAgICAgICAgIm5hbWUiIDogIlRvU3RyaW5nIiwKICAgICAgICAgICAgImNvbnRleHQiIDogIlBhdGllbnQiLAogICAgICAgICAgICAiYWNjZXNzTGV2ZWwiIDogIlB1YmxpYyIsCiAgICAgICAgICAgICJ0eXBlIiA6ICJGdW5jdGlvbkRlZiIsCiAgICAgICAgICAgICJleHByZXNzaW9uIiA6IHsKICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjMwNjo0My0zMDY6NTMiLAogICAgICAgICAgICAgICAicGF0aCIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAidHlwZSIgOiAiUHJvcGVydHkiLAogICAgICAgICAgICAgICAic291cmNlIiA6IHsKICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjMwNjo0My0zMDY6NDciLAogICAgICAgICAgICAgICAgICAibmFtZSIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAgICAidHlwZSIgOiAiT3BlcmFuZFJlZiIKICAgICAgICAgICAgICAgfQogICAgICAgICAgICB9LAogICAgICAgICAgICAib3BlcmFuZCIgOiBbIHsKICAgICAgICAgICAgICAgIm5hbWUiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgIm9wZXJhbmRUeXBlU3BlY2lmaWVyIiA6IHsKICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjMwNjozMi0zMDY6MzkiLAogICAgICAgICAgICAgICAgICAibmFtZSIgOiAie2h0dHA6Ly9obDcub3JnL2ZoaXJ9Tm90ZVR5cGUiLAogICAgICAgICAgICAgICAgICAidHlwZSIgOiAiTmFtZWRUeXBlU3BlY2lmaWVyIgogICAgICAgICAgICAgICB9CiAgICAgICAgICAgIH0gXQogICAgICAgICB9LCB7CiAgICAgICAgICAgICJsb2NhdG9yIiA6ICIzMDc6MS0zMDc6NjYiLAogICAgICAgICAgICAibmFtZSIgOiAiVG9TdHJpbmciLAogICAgICAgICAgICAiY29udGV4dCIgOiAiUGF0aWVudCIsCiAgICAgICAgICAgICJhY2Nlc3NMZXZlbCIgOiAiUHVibGljIiwKICAgICAgICAgICAgInR5cGUiIDogIkZ1bmN0aW9uRGVmIiwKICAgICAgICAgICAgImV4cHJlc3Npb24iIDogewogICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMzA3OjU2LTMwNzo2NiIsCiAgICAgICAgICAgICAgICJwYXRoIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICJ0eXBlIiA6ICJQcm9wZXJ0eSIsCiAgICAgICAgICAgICAgICJzb3VyY2UiIDogewogICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMzA3OjU2LTMwNzo2MCIsCiAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJPcGVyYW5kUmVmIgogICAgICAgICAgICAgICB9CiAgICAgICAgICAgIH0sCiAgICAgICAgICAgICJvcGVyYW5kIiA6IFsgewogICAgICAgICAgICAgICAibmFtZSIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAib3BlcmFuZFR5cGVTcGVjaWZpZXIiIDogewogICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMzA3OjMyLTMwNzo1MiIsCiAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJ7aHR0cDovL2hsNy5vcmcvZmhpcn1OdXRyaXRpaW9uT3JkZXJJbnRlbnQiLAogICAgICAgICAgICAgICAgICAidHlwZSIgOiAiTmFtZWRUeXBlU3BlY2lmaWVyIgogICAgICAgICAgICAgICB9CiAgICAgICAgICAgIH0gXQogICAgICAgICB9LCB7CiAgICAgICAgICAgICJsb2NhdG9yIiA6ICIzMDg6MS0zMDg6NjUiLAogICAgICAgICAgICAibmFtZSIgOiAiVG9TdHJpbmciLAogICAgICAgICAgICAiY29udGV4dCIgOiAiUGF0aWVudCIsCiAgICAgICAgICAgICJhY2Nlc3NMZXZlbCIgOiAiUHVibGljIiwKICAgICAgICAgICAgInR5cGUiIDogIkZ1bmN0aW9uRGVmIiwKICAgICAgICAgICAgImV4cHJlc3Npb24iIDogewogICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMzA4OjU1LTMwODo2NSIsCiAgICAgICAgICAgICAgICJwYXRoIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICJ0eXBlIiA6ICJQcm9wZXJ0eSIsCiAgICAgICAgICAgICAgICJzb3VyY2UiIDogewogICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMzA4OjU1LTMwODo1OSIsCiAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJPcGVyYW5kUmVmIgogICAgICAgICAgICAgICB9CiAgICAgICAgICAgIH0sCiAgICAgICAgICAgICJvcGVyYW5kIiA6IFsgewogICAgICAgICAgICAgICAibmFtZSIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAib3BlcmFuZFR5cGVTcGVjaWZpZXIiIDogewogICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMzA4OjMyLTMwODo1MSIsCiAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJ7aHR0cDovL2hsNy5vcmcvZmhpcn1OdXRyaXRpb25PcmRlclN0YXR1cyIsCiAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJOYW1lZFR5cGVTcGVjaWZpZXIiCiAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgfSBdCiAgICAgICAgIH0sIHsKICAgICAgICAgICAgImxvY2F0b3IiIDogIjMwOToxLTMwOTo2NCIsCiAgICAgICAgICAgICJuYW1lIiA6ICJUb1N0cmluZyIsCiAgICAgICAgICAgICJjb250ZXh0IiA6ICJQYXRpZW50IiwKICAgICAgICAgICAgImFjY2Vzc0xldmVsIiA6ICJQdWJsaWMiLAogICAgICAgICAgICAidHlwZSIgOiAiRnVuY3Rpb25EZWYiLAogICAgICAgICAgICAiZXhwcmVzc2lvbiIgOiB7CiAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIzMDk6NTQtMzA5OjY0IiwKICAgICAgICAgICAgICAgInBhdGgiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgInR5cGUiIDogIlByb3BlcnR5IiwKICAgICAgICAgICAgICAgInNvdXJjZSIgOiB7CiAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIzMDk6NTQtMzA5OjU4IiwKICAgICAgICAgICAgICAgICAgIm5hbWUiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk9wZXJhbmRSZWYiCiAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgfSwKICAgICAgICAgICAgIm9wZXJhbmQiIDogWyB7CiAgICAgICAgICAgICAgICJuYW1lIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICJvcGVyYW5kVHlwZVNwZWNpZmllciIgOiB7CiAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIzMDk6MzItMzA5OjUwIiwKICAgICAgICAgICAgICAgICAgIm5hbWUiIDogIntodHRwOi8vaGw3Lm9yZy9maGlyfU9ic2VydmF0aW9uRGF0YVR5cGUiLAogICAgICAgICAgICAgICAgICAidHlwZSIgOiAiTmFtZWRUeXBlU3BlY2lmaWVyIgogICAgICAgICAgICAgICB9CiAgICAgICAgICAgIH0gXQogICAgICAgICB9LCB7CiAgICAgICAgICAgICJsb2NhdG9yIiA6ICIzMTA6MS0zMTA6NjkiLAogICAgICAgICAgICAibmFtZSIgOiAiVG9TdHJpbmciLAogICAgICAgICAgICAiY29udGV4dCIgOiAiUGF0aWVudCIsCiAgICAgICAgICAgICJhY2Nlc3NMZXZlbCIgOiAiUHVibGljIiwKICAgICAgICAgICAgInR5cGUiIDogIkZ1bmN0aW9uRGVmIiwKICAgICAgICAgICAgImV4cHJlc3Npb24iIDogewogICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMzEwOjU5LTMxMDo2OSIsCiAgICAgICAgICAgICAgICJwYXRoIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICJ0eXBlIiA6ICJQcm9wZXJ0eSIsCiAgICAgICAgICAgICAgICJzb3VyY2UiIDogewogICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMzEwOjU5LTMxMDo2MyIsCiAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJPcGVyYW5kUmVmIgogICAgICAgICAgICAgICB9CiAgICAgICAgICAgIH0sCiAgICAgICAgICAgICJvcGVyYW5kIiA6IFsgewogICAgICAgICAgICAgICAibmFtZSIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAib3BlcmFuZFR5cGVTcGVjaWZpZXIiIDogewogICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMzEwOjMyLTMxMDo1NSIsCiAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJ7aHR0cDovL2hsNy5vcmcvZmhpcn1PYnNlcnZhdGlvblJhbmdlQ2F0ZWdvcnkiLAogICAgICAgICAgICAgICAgICAidHlwZSIgOiAiTmFtZWRUeXBlU3BlY2lmaWVyIgogICAgICAgICAgICAgICB9CiAgICAgICAgICAgIH0gXQogICAgICAgICB9LCB7CiAgICAgICAgICAgICJsb2NhdG9yIiA6ICIzMTE6MS0zMTE6NjIiLAogICAgICAgICAgICAibmFtZSIgOiAiVG9TdHJpbmciLAogICAgICAgICAgICAiY29udGV4dCIgOiAiUGF0aWVudCIsCiAgICAgICAgICAgICJhY2Nlc3NMZXZlbCIgOiAiUHVibGljIiwKICAgICAgICAgICAgInR5cGUiIDogIkZ1bmN0aW9uRGVmIiwKICAgICAgICAgICAgImV4cHJlc3Npb24iIDogewogICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMzExOjUyLTMxMTo2MiIsCiAgICAgICAgICAgICAgICJwYXRoIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICJ0eXBlIiA6ICJQcm9wZXJ0eSIsCiAgICAgICAgICAgICAgICJzb3VyY2UiIDogewogICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMzExOjUyLTMxMTo1NiIsCiAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJPcGVyYW5kUmVmIgogICAgICAgICAgICAgICB9CiAgICAgICAgICAgIH0sCiAgICAgICAgICAgICJvcGVyYW5kIiA6IFsgewogICAgICAgICAgICAgICAibmFtZSIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAib3BlcmFuZFR5cGVTcGVjaWZpZXIiIDogewogICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMzExOjMyLTMxMTo0OCIsCiAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJ7aHR0cDovL2hsNy5vcmcvZmhpcn1PYnNlcnZhdGlvblN0YXR1cyIsCiAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJOYW1lZFR5cGVTcGVjaWZpZXIiCiAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgfSBdCiAgICAgICAgIH0sIHsKICAgICAgICAgICAgImxvY2F0b3IiIDogIjMxMjoxLTMxMjo1OCIsCiAgICAgICAgICAgICJuYW1lIiA6ICJUb1N0cmluZyIsCiAgICAgICAgICAgICJjb250ZXh0IiA6ICJQYXRpZW50IiwKICAgICAgICAgICAgImFjY2Vzc0xldmVsIiA6ICJQdWJsaWMiLAogICAgICAgICAgICAidHlwZSIgOiAiRnVuY3Rpb25EZWYiLAogICAgICAgICAgICAiZXhwcmVzc2lvbiIgOiB7CiAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIzMTI6NDgtMzEyOjU4IiwKICAgICAgICAgICAgICAgInBhdGgiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgInR5cGUiIDogIlByb3BlcnR5IiwKICAgICAgICAgICAgICAgInNvdXJjZSIgOiB7CiAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIzMTI6NDgtMzEyOjUyIiwKICAgICAgICAgICAgICAgICAgIm5hbWUiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk9wZXJhbmRSZWYiCiAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgfSwKICAgICAgICAgICAgIm9wZXJhbmQiIDogWyB7CiAgICAgICAgICAgICAgICJuYW1lIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICJvcGVyYW5kVHlwZVNwZWNpZmllciIgOiB7CiAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIzMTI6MzItMzEyOjQ0IiwKICAgICAgICAgICAgICAgICAgIm5hbWUiIDogIntodHRwOi8vaGw3Lm9yZy9maGlyfU9wZXJhdGlvbktpbmQiLAogICAgICAgICAgICAgICAgICAidHlwZSIgOiAiTmFtZWRUeXBlU3BlY2lmaWVyIgogICAgICAgICAgICAgICB9CiAgICAgICAgICAgIH0gXQogICAgICAgICB9LCB7CiAgICAgICAgICAgICJsb2NhdG9yIiA6ICIzMTM6MS0zMTM6NjYiLAogICAgICAgICAgICAibmFtZSIgOiAiVG9TdHJpbmciLAogICAgICAgICAgICAiY29udGV4dCIgOiAiUGF0aWVudCIsCiAgICAgICAgICAgICJhY2Nlc3NMZXZlbCIgOiAiUHVibGljIiwKICAgICAgICAgICAgInR5cGUiIDogIkZ1bmN0aW9uRGVmIiwKICAgICAgICAgICAgImV4cHJlc3Npb24iIDogewogICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMzEzOjU2LTMxMzo2NiIsCiAgICAgICAgICAgICAgICJwYXRoIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICJ0eXBlIiA6ICJQcm9wZXJ0eSIsCiAgICAgICAgICAgICAgICJzb3VyY2UiIDogewogICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMzEzOjU2LTMxMzo2MCIsCiAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJPcGVyYW5kUmVmIgogICAgICAgICAgICAgICB9CiAgICAgICAgICAgIH0sCiAgICAgICAgICAgICJvcGVyYW5kIiA6IFsgewogICAgICAgICAgICAgICAibmFtZSIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAib3BlcmFuZFR5cGVTcGVjaWZpZXIiIDogewogICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMzEzOjMyLTMxMzo1MiIsCiAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJ7aHR0cDovL2hsNy5vcmcvZmhpcn1PcGVyYXRpb25QYXJhbWV0ZXJVc2UiLAogICAgICAgICAgICAgICAgICAidHlwZSIgOiAiTmFtZWRUeXBlU3BlY2lmaWVyIgogICAgICAgICAgICAgICB9CiAgICAgICAgICAgIH0gXQogICAgICAgICB9LCB7CiAgICAgICAgICAgICJsb2NhdG9yIiA6ICIzMTQ6MS0zMTQ6NjAiLAogICAgICAgICAgICAibmFtZSIgOiAiVG9TdHJpbmciLAogICAgICAgICAgICAiY29udGV4dCIgOiAiUGF0aWVudCIsCiAgICAgICAgICAgICJhY2Nlc3NMZXZlbCIgOiAiUHVibGljIiwKICAgICAgICAgICAgInR5cGUiIDogIkZ1bmN0aW9uRGVmIiwKICAgICAgICAgICAgImV4cHJlc3Npb24iIDogewogICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMzE0OjUwLTMxNDo2MCIsCiAgICAgICAgICAgICAgICJwYXRoIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICJ0eXBlIiA6ICJQcm9wZXJ0eSIsCiAgICAgICAgICAgICAgICJzb3VyY2UiIDogewogICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMzE0OjUwLTMxNDo1NCIsCiAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJPcGVyYW5kUmVmIgogICAgICAgICAgICAgICB9CiAgICAgICAgICAgIH0sCiAgICAgICAgICAgICJvcGVyYW5kIiA6IFsgewogICAgICAgICAgICAgICAibmFtZSIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAib3BlcmFuZFR5cGVTcGVjaWZpZXIiIDogewogICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMzE0OjMyLTMxNDo0NiIsCiAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJ7aHR0cDovL2hsNy5vcmcvZmhpcn1PcmllbnRhdGlvblR5cGUiLAogICAgICAgICAgICAgICAgICAidHlwZSIgOiAiTmFtZWRUeXBlU3BlY2lmaWVyIgogICAgICAgICAgICAgICB9CiAgICAgICAgICAgIH0gXQogICAgICAgICB9LCB7CiAgICAgICAgICAgICJsb2NhdG9yIiA6ICIzMTU6MS0zMTU6NTciLAogICAgICAgICAgICAibmFtZSIgOiAiVG9TdHJpbmciLAogICAgICAgICAgICAiY29udGV4dCIgOiAiUGF0aWVudCIsCiAgICAgICAgICAgICJhY2Nlc3NMZXZlbCIgOiAiUHVibGljIiwKICAgICAgICAgICAgInR5cGUiIDogIkZ1bmN0aW9uRGVmIiwKICAgICAgICAgICAgImV4cHJlc3Npb24iIDogewogICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMzE1OjQ3LTMxNTo1NyIsCiAgICAgICAgICAgICAgICJwYXRoIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICJ0eXBlIiA6ICJQcm9wZXJ0eSIsCiAgICAgICAgICAgICAgICJzb3VyY2UiIDogewogICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMzE1OjQ3LTMxNTo1MSIsCiAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJPcGVyYW5kUmVmIgogICAgICAgICAgICAgICB9CiAgICAgICAgICAgIH0sCiAgICAgICAgICAgICJvcGVyYW5kIiA6IFsgewogICAgICAgICAgICAgICAibmFtZSIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAib3BlcmFuZFR5cGVTcGVjaWZpZXIiIDogewogICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMzE1OjMyLTMxNTo0MyIsCiAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJ7aHR0cDovL2hsNy5vcmcvZmhpcn1QYXJhbWV0ZXJVc2UiLAogICAgICAgICAgICAgICAgICAidHlwZSIgOiAiTmFtZWRUeXBlU3BlY2lmaWVyIgogICAgICAgICAgICAgICB9CiAgICAgICAgICAgIH0gXQogICAgICAgICB9LCB7CiAgICAgICAgICAgICJsb2NhdG9yIiA6ICIzMTY6MS0zMTY6NjQiLAogICAgICAgICAgICAibmFtZSIgOiAiVG9TdHJpbmciLAogICAgICAgICAgICAiY29udGV4dCIgOiAiUGF0aWVudCIsCiAgICAgICAgICAgICJhY2Nlc3NMZXZlbCIgOiAiUHVibGljIiwKICAgICAgICAgICAgInR5cGUiIDogIkZ1bmN0aW9uRGVmIiwKICAgICAgICAgICAgImV4cHJlc3Npb24iIDogewogICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMzE2OjU0LTMxNjo2NCIsCiAgICAgICAgICAgICAgICJwYXRoIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICJ0eXBlIiA6ICJQcm9wZXJ0eSIsCiAgICAgICAgICAgICAgICJzb3VyY2UiIDogewogICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMzE2OjU0LTMxNjo1OCIsCiAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJPcGVyYW5kUmVmIgogICAgICAgICAgICAgICB9CiAgICAgICAgICAgIH0sCiAgICAgICAgICAgICJvcGVyYW5kIiA6IFsgewogICAgICAgICAgICAgICAibmFtZSIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAib3BlcmFuZFR5cGVTcGVjaWZpZXIiIDogewogICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMzE2OjMyLTMxNjo1MCIsCiAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJ7aHR0cDovL2hsNy5vcmcvZmhpcn1QYXJ0aWNpcGFudFJlcXVpcmVkIiwKICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk5hbWVkVHlwZVNwZWNpZmllciIKICAgICAgICAgICAgICAgfQogICAgICAgICAgICB9IF0KICAgICAgICAgfSwgewogICAgICAgICAgICAibG9jYXRvciIgOiAiMzE3OjEtMzE3OjYyIiwKICAgICAgICAgICAgIm5hbWUiIDogIlRvU3RyaW5nIiwKICAgICAgICAgICAgImNvbnRleHQiIDogIlBhdGllbnQiLAogICAgICAgICAgICAiYWNjZXNzTGV2ZWwiIDogIlB1YmxpYyIsCiAgICAgICAgICAgICJ0eXBlIiA6ICJGdW5jdGlvbkRlZiIsCiAgICAgICAgICAgICJleHByZXNzaW9uIiA6IHsKICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjMxNzo1Mi0zMTc6NjIiLAogICAgICAgICAgICAgICAicGF0aCIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAidHlwZSIgOiAiUHJvcGVydHkiLAogICAgICAgICAgICAgICAic291cmNlIiA6IHsKICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjMxNzo1Mi0zMTc6NTYiLAogICAgICAgICAgICAgICAgICAibmFtZSIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAgICAidHlwZSIgOiAiT3BlcmFuZFJlZiIKICAgICAgICAgICAgICAgfQogICAgICAgICAgICB9LAogICAgICAgICAgICAib3BlcmFuZCIgOiBbIHsKICAgICAgICAgICAgICAgIm5hbWUiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgIm9wZXJhbmRUeXBlU3BlY2lmaWVyIiA6IHsKICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjMxNzozMi0zMTc6NDgiLAogICAgICAgICAgICAgICAgICAibmFtZSIgOiAie2h0dHA6Ly9obDcub3JnL2ZoaXJ9UGFydGljaXBhbnRTdGF0dXMiLAogICAgICAgICAgICAgICAgICAidHlwZSIgOiAiTmFtZWRUeXBlU3BlY2lmaWVyIgogICAgICAgICAgICAgICB9CiAgICAgICAgICAgIH0gXQogICAgICAgICB9LCB7CiAgICAgICAgICAgICJsb2NhdG9yIiA6ICIzMTg6MS0zMTg6NjQiLAogICAgICAgICAgICAibmFtZSIgOiAiVG9TdHJpbmciLAogICAgICAgICAgICAiY29udGV4dCIgOiAiUGF0aWVudCIsCiAgICAgICAgICAgICJhY2Nlc3NMZXZlbCIgOiAiUHVibGljIiwKICAgICAgICAgICAgInR5cGUiIDogIkZ1bmN0aW9uRGVmIiwKICAgICAgICAgICAgImV4cHJlc3Npb24iIDogewogICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMzE4OjU0LTMxODo2NCIsCiAgICAgICAgICAgICAgICJwYXRoIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICJ0eXBlIiA6ICJQcm9wZXJ0eSIsCiAgICAgICAgICAgICAgICJzb3VyY2UiIDogewogICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMzE4OjU0LTMxODo1OCIsCiAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJPcGVyYW5kUmVmIgogICAgICAgICAgICAgICB9CiAgICAgICAgICAgIH0sCiAgICAgICAgICAgICJvcGVyYW5kIiA6IFsgewogICAgICAgICAgICAgICAibmFtZSIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAib3BlcmFuZFR5cGVTcGVjaWZpZXIiIDogewogICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMzE4OjMyLTMxODo1MCIsCiAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJ7aHR0cDovL2hsNy5vcmcvZmhpcn1QYXJ0aWNpcGF0aW9uU3RhdHVzIiwKICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk5hbWVkVHlwZVNwZWNpZmllciIKICAgICAgICAgICAgICAgfQogICAgICAgICAgICB9IF0KICAgICAgICAgfSwgewogICAgICAgICAgICAibG9jYXRvciIgOiAiMzE5OjEtMzE5OjY0IiwKICAgICAgICAgICAgIm5hbWUiIDogIlRvU3RyaW5nIiwKICAgICAgICAgICAgImNvbnRleHQiIDogIlBhdGllbnQiLAogICAgICAgICAgICAiYWNjZXNzTGV2ZWwiIDogIlB1YmxpYyIsCiAgICAgICAgICAgICJ0eXBlIiA6ICJGdW5jdGlvbkRlZiIsCiAgICAgICAgICAgICJleHByZXNzaW9uIiA6IHsKICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjMxOTo1NC0zMTk6NjQiLAogICAgICAgICAgICAgICAicGF0aCIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAidHlwZSIgOiAiUHJvcGVydHkiLAogICAgICAgICAgICAgICAic291cmNlIiA6IHsKICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjMxOTo1NC0zMTk6NTgiLAogICAgICAgICAgICAgICAgICAibmFtZSIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAgICAidHlwZSIgOiAiT3BlcmFuZFJlZiIKICAgICAgICAgICAgICAgfQogICAgICAgICAgICB9LAogICAgICAgICAgICAib3BlcmFuZCIgOiBbIHsKICAgICAgICAgICAgICAgIm5hbWUiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgIm9wZXJhbmRUeXBlU3BlY2lmaWVyIiA6IHsKICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjMxOTozMi0zMTk6NTAiLAogICAgICAgICAgICAgICAgICAibmFtZSIgOiAie2h0dHA6Ly9obDcub3JnL2ZoaXJ9UGF5bWVudE5vdGljZVN0YXR1cyIsCiAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJOYW1lZFR5cGVTcGVjaWZpZXIiCiAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgfSBdCiAgICAgICAgIH0sIHsKICAgICAgICAgICAgImxvY2F0b3IiIDogIjMyMDoxLTMyMDo3MiIsCiAgICAgICAgICAgICJuYW1lIiA6ICJUb1N0cmluZyIsCiAgICAgICAgICAgICJjb250ZXh0IiA6ICJQYXRpZW50IiwKICAgICAgICAgICAgImFjY2Vzc0xldmVsIiA6ICJQdWJsaWMiLAogICAgICAgICAgICAidHlwZSIgOiAiRnVuY3Rpb25EZWYiLAogICAgICAgICAgICAiZXhwcmVzc2lvbiIgOiB7CiAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIzMjA6NjItMzIwOjcyIiwKICAgICAgICAgICAgICAgInBhdGgiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgInR5cGUiIDogIlByb3BlcnR5IiwKICAgICAgICAgICAgICAgInNvdXJjZSIgOiB7CiAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIzMjA6NjItMzIwOjY2IiwKICAgICAgICAgICAgICAgICAgIm5hbWUiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk9wZXJhbmRSZWYiCiAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgfSwKICAgICAgICAgICAgIm9wZXJhbmQiIDogWyB7CiAgICAgICAgICAgICAgICJuYW1lIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICJvcGVyYW5kVHlwZVNwZWNpZmllciIgOiB7CiAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIzMjA6MzItMzIwOjU4IiwKICAgICAgICAgICAgICAgICAgIm5hbWUiIDogIntodHRwOi8vaGw3Lm9yZy9maGlyfVBheW1lbnRSZWNvbmNpbGlhdGlvblN0YXR1cyIsCiAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJOYW1lZFR5cGVTcGVjaWZpZXIiCiAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgfSBdCiAgICAgICAgIH0sIHsKICAgICAgICAgICAgImxvY2F0b3IiIDogIjMyMToxLTMyMTo2MCIsCiAgICAgICAgICAgICJuYW1lIiA6ICJUb1N0cmluZyIsCiAgICAgICAgICAgICJjb250ZXh0IiA6ICJQYXRpZW50IiwKICAgICAgICAgICAgImFjY2Vzc0xldmVsIiA6ICJQdWJsaWMiLAogICAgICAgICAgICAidHlwZSIgOiAiRnVuY3Rpb25EZWYiLAogICAgICAgICAgICAiZXhwcmVzc2lvbiIgOiB7CiAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIzMjE6NTAtMzIxOjYwIiwKICAgICAgICAgICAgICAgInBhdGgiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgInR5cGUiIDogIlByb3BlcnR5IiwKICAgICAgICAgICAgICAgInNvdXJjZSIgOiB7CiAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIzMjE6NTAtMzIxOjU0IiwKICAgICAgICAgICAgICAgICAgIm5hbWUiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk9wZXJhbmRSZWYiCiAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgfSwKICAgICAgICAgICAgIm9wZXJhbmQiIDogWyB7CiAgICAgICAgICAgICAgICJuYW1lIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICJvcGVyYW5kVHlwZVNwZWNpZmllciIgOiB7CiAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIzMjE6MzItMzIxOjQ2IiwKICAgICAgICAgICAgICAgICAgIm5hbWUiIDogIntodHRwOi8vaGw3Lm9yZy9maGlyfVByb2NlZHVyZVN0YXR1cyIsCiAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJOYW1lZFR5cGVTcGVjaWZpZXIiCiAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgfSBdCiAgICAgICAgIH0sIHsKICAgICAgICAgICAgImxvY2F0b3IiIDogIjMyMjoxLTMyMjo2NyIsCiAgICAgICAgICAgICJuYW1lIiA6ICJUb1N0cmluZyIsCiAgICAgICAgICAgICJjb250ZXh0IiA6ICJQYXRpZW50IiwKICAgICAgICAgICAgImFjY2Vzc0xldmVsIiA6ICJQdWJsaWMiLAogICAgICAgICAgICAidHlwZSIgOiAiRnVuY3Rpb25EZWYiLAogICAgICAgICAgICAiZXhwcmVzc2lvbiIgOiB7CiAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIzMjI6NTctMzIyOjY3IiwKICAgICAgICAgICAgICAgInBhdGgiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgInR5cGUiIDogIlByb3BlcnR5IiwKICAgICAgICAgICAgICAgInNvdXJjZSIgOiB7CiAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIzMjI6NTctMzIyOjYxIiwKICAgICAgICAgICAgICAgICAgIm5hbWUiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk9wZXJhbmRSZWYiCiAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgfSwKICAgICAgICAgICAgIm9wZXJhbmQiIDogWyB7CiAgICAgICAgICAgICAgICJuYW1lIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICJvcGVyYW5kVHlwZVNwZWNpZmllciIgOiB7CiAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIzMjI6MzItMzIyOjUzIiwKICAgICAgICAgICAgICAgICAgIm5hbWUiIDogIntodHRwOi8vaGw3Lm9yZy9maGlyfVByb3BlcnR5UmVwcmVzZW50YXRpb24iLAogICAgICAgICAgICAgICAgICAidHlwZSIgOiAiTmFtZWRUeXBlU3BlY2lmaWVyIgogICAgICAgICAgICAgICB9CiAgICAgICAgICAgIH0gXQogICAgICAgICB9LCB7CiAgICAgICAgICAgICJsb2NhdG9yIiA6ICIzMjM6MS0zMjM6NTciLAogICAgICAgICAgICAibmFtZSIgOiAiVG9TdHJpbmciLAogICAgICAgICAgICAiY29udGV4dCIgOiAiUGF0aWVudCIsCiAgICAgICAgICAgICJhY2Nlc3NMZXZlbCIgOiAiUHVibGljIiwKICAgICAgICAgICAgInR5cGUiIDogIkZ1bmN0aW9uRGVmIiwKICAgICAgICAgICAgImV4cHJlc3Npb24iIDogewogICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMzIzOjQ3LTMyMzo1NyIsCiAgICAgICAgICAgICAgICJwYXRoIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICJ0eXBlIiA6ICJQcm9wZXJ0eSIsCiAgICAgICAgICAgICAgICJzb3VyY2UiIDogewogICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMzIzOjQ3LTMyMzo1MSIsCiAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJPcGVyYW5kUmVmIgogICAgICAgICAgICAgICB9CiAgICAgICAgICAgIH0sCiAgICAgICAgICAgICJvcGVyYW5kIiA6IFsgewogICAgICAgICAgICAgICAibmFtZSIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAib3BlcmFuZFR5cGVTcGVjaWZpZXIiIDogewogICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMzIzOjMyLTMyMzo0MyIsCiAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJ7aHR0cDovL2hsNy5vcmcvZmhpcn1Qcm9wZXJ0eVR5cGUiLAogICAgICAgICAgICAgICAgICAidHlwZSIgOiAiTmFtZWRUeXBlU3BlY2lmaWVyIgogICAgICAgICAgICAgICB9CiAgICAgICAgICAgIH0gXQogICAgICAgICB9LCB7CiAgICAgICAgICAgICJsb2NhdG9yIiA6ICIzMjQ6MS0zMjQ6NjUiLAogICAgICAgICAgICAibmFtZSIgOiAiVG9TdHJpbmciLAogICAgICAgICAgICAiY29udGV4dCIgOiAiUGF0aWVudCIsCiAgICAgICAgICAgICJhY2Nlc3NMZXZlbCIgOiAiUHVibGljIiwKICAgICAgICAgICAgInR5cGUiIDogIkZ1bmN0aW9uRGVmIiwKICAgICAgICAgICAgImV4cHJlc3Npb24iIDogewogICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMzI0OjU1LTMyNDo2NSIsCiAgICAgICAgICAgICAgICJwYXRoIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICJ0eXBlIiA6ICJQcm9wZXJ0eSIsCiAgICAgICAgICAgICAgICJzb3VyY2UiIDogewogICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMzI0OjU1LTMyNDo1OSIsCiAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJPcGVyYW5kUmVmIgogICAgICAgICAgICAgICB9CiAgICAgICAgICAgIH0sCiAgICAgICAgICAgICJvcGVyYW5kIiA6IFsgewogICAgICAgICAgICAgICAibmFtZSIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAib3BlcmFuZFR5cGVTcGVjaWZpZXIiIDogewogICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMzI0OjMyLTMyNDo1MSIsCiAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJ7aHR0cDovL2hsNy5vcmcvZmhpcn1Qcm92ZW5hbmNlRW50aXR5Um9sZSIsCiAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJOYW1lZFR5cGVTcGVjaWZpZXIiCiAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgfSBdCiAgICAgICAgIH0sIHsKICAgICAgICAgICAgImxvY2F0b3IiIDogIjMyNToxLTMyNTo2MiIsCiAgICAgICAgICAgICJuYW1lIiA6ICJUb1N0cmluZyIsCiAgICAgICAgICAgICJjb250ZXh0IiA6ICJQYXRpZW50IiwKICAgICAgICAgICAgImFjY2Vzc0xldmVsIiA6ICJQdWJsaWMiLAogICAgICAgICAgICAidHlwZSIgOiAiRnVuY3Rpb25EZWYiLAogICAgICAgICAgICAiZXhwcmVzc2lvbiIgOiB7CiAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIzMjU6NTItMzI1OjYyIiwKICAgICAgICAgICAgICAgInBhdGgiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgInR5cGUiIDogIlByb3BlcnR5IiwKICAgICAgICAgICAgICAgInNvdXJjZSIgOiB7CiAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIzMjU6NTItMzI1OjU2IiwKICAgICAgICAgICAgICAgICAgIm5hbWUiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk9wZXJhbmRSZWYiCiAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgfSwKICAgICAgICAgICAgIm9wZXJhbmQiIDogWyB7CiAgICAgICAgICAgICAgICJuYW1lIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICJvcGVyYW5kVHlwZVNwZWNpZmllciIgOiB7CiAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIzMjU6MzItMzI1OjQ4IiwKICAgICAgICAgICAgICAgICAgIm5hbWUiIDogIntodHRwOi8vaGw3Lm9yZy9maGlyfVB1YmxpY2F0aW9uU3RhdHVzIiwKICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk5hbWVkVHlwZVNwZWNpZmllciIKICAgICAgICAgICAgICAgfQogICAgICAgICAgICB9IF0KICAgICAgICAgfSwgewogICAgICAgICAgICAibG9jYXRvciIgOiAiMzI2OjEtMzI2OjU2IiwKICAgICAgICAgICAgIm5hbWUiIDogIlRvU3RyaW5nIiwKICAgICAgICAgICAgImNvbnRleHQiIDogIlBhdGllbnQiLAogICAgICAgICAgICAiYWNjZXNzTGV2ZWwiIDogIlB1YmxpYyIsCiAgICAgICAgICAgICJ0eXBlIiA6ICJGdW5jdGlvbkRlZiIsCiAgICAgICAgICAgICJleHByZXNzaW9uIiA6IHsKICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjMyNjo0Ni0zMjY6NTYiLAogICAgICAgICAgICAgICAicGF0aCIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAidHlwZSIgOiAiUHJvcGVydHkiLAogICAgICAgICAgICAgICAic291cmNlIiA6IHsKICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjMyNjo0Ni0zMjY6NTAiLAogICAgICAgICAgICAgICAgICAibmFtZSIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAgICAidHlwZSIgOiAiT3BlcmFuZFJlZiIKICAgICAgICAgICAgICAgfQogICAgICAgICAgICB9LAogICAgICAgICAgICAib3BlcmFuZCIgOiBbIHsKICAgICAgICAgICAgICAgIm5hbWUiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgIm9wZXJhbmRUeXBlU3BlY2lmaWVyIiA6IHsKICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjMyNjozMi0zMjY6NDIiLAogICAgICAgICAgICAgICAgICAibmFtZSIgOiAie2h0dHA6Ly9obDcub3JnL2ZoaXJ9UXVhbGl0eVR5cGUiLAogICAgICAgICAgICAgICAgICAidHlwZSIgOiAiTmFtZWRUeXBlU3BlY2lmaWVyIgogICAgICAgICAgICAgICB9CiAgICAgICAgICAgIH0gXQogICAgICAgICB9LCB7CiAgICAgICAgICAgICJsb2NhdG9yIiA6ICIzMjc6MS0zMjc6NjMiLAogICAgICAgICAgICAibmFtZSIgOiAiVG9TdHJpbmciLAogICAgICAgICAgICAiY29udGV4dCIgOiAiUGF0aWVudCIsCiAgICAgICAgICAgICJhY2Nlc3NMZXZlbCIgOiAiUHVibGljIiwKICAgICAgICAgICAgInR5cGUiIDogIkZ1bmN0aW9uRGVmIiwKICAgICAgICAgICAgImV4cHJlc3Npb24iIDogewogICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMzI3OjUzLTMyNzo2MyIsCiAgICAgICAgICAgICAgICJwYXRoIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICJ0eXBlIiA6ICJQcm9wZXJ0eSIsCiAgICAgICAgICAgICAgICJzb3VyY2UiIDogewogICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMzI3OjUzLTMyNzo1NyIsCiAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJPcGVyYW5kUmVmIgogICAgICAgICAgICAgICB9CiAgICAgICAgICAgIH0sCiAgICAgICAgICAgICJvcGVyYW5kIiA6IFsgewogICAgICAgICAgICAgICAibmFtZSIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAib3BlcmFuZFR5cGVTcGVjaWZpZXIiIDogewogICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMzI3OjMyLTMyNzo0OSIsCiAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJ7aHR0cDovL2hsNy5vcmcvZmhpcn1RdWFudGl0eUNvbXBhcmF0b3IiLAogICAgICAgICAgICAgICAgICAidHlwZSIgOiAiTmFtZWRUeXBlU3BlY2lmaWVyIgogICAgICAgICAgICAgICB9CiAgICAgICAgICAgIH0gXQogICAgICAgICB9LCB7CiAgICAgICAgICAgICJsb2NhdG9yIiA6ICIzMjg6MS0zMjg6NzAiLAogICAgICAgICAgICAibmFtZSIgOiAiVG9TdHJpbmciLAogICAgICAgICAgICAiY29udGV4dCIgOiAiUGF0aWVudCIsCiAgICAgICAgICAgICJhY2Nlc3NMZXZlbCIgOiAiUHVibGljIiwKICAgICAgICAgICAgInR5cGUiIDogIkZ1bmN0aW9uRGVmIiwKICAgICAgICAgICAgImV4cHJlc3Npb24iIDogewogICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMzI4OjYwLTMyODo3MCIsCiAgICAgICAgICAgICAgICJwYXRoIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICJ0eXBlIiA6ICJQcm9wZXJ0eSIsCiAgICAgICAgICAgICAgICJzb3VyY2UiIDogewogICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMzI4OjYwLTMyODo2NCIsCiAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJPcGVyYW5kUmVmIgogICAgICAgICAgICAgICB9CiAgICAgICAgICAgIH0sCiAgICAgICAgICAgICJvcGVyYW5kIiA6IFsgewogICAgICAgICAgICAgICAibmFtZSIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAib3BlcmFuZFR5cGVTcGVjaWZpZXIiIDogewogICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMzI4OjMyLTMyODo1NiIsCiAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJ7aHR0cDovL2hsNy5vcmcvZmhpcn1RdWVzdGlvbm5haXJlSXRlbU9wZXJhdG9yIiwKICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk5hbWVkVHlwZVNwZWNpZmllciIKICAgICAgICAgICAgICAgfQogICAgICAgICAgICB9IF0KICAgICAgICAgfSwgewogICAgICAgICAgICAibG9jYXRvciIgOiAiMzI5OjEtMzI5OjY2IiwKICAgICAgICAgICAgIm5hbWUiIDogIlRvU3RyaW5nIiwKICAgICAgICAgICAgImNvbnRleHQiIDogIlBhdGllbnQiLAogICAgICAgICAgICAiYWNjZXNzTGV2ZWwiIDogIlB1YmxpYyIsCiAgICAgICAgICAgICJ0eXBlIiA6ICJGdW5jdGlvbkRlZiIsCiAgICAgICAgICAgICJleHByZXNzaW9uIiA6IHsKICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjMyOTo1Ni0zMjk6NjYiLAogICAgICAgICAgICAgICAicGF0aCIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAidHlwZSIgOiAiUHJvcGVydHkiLAogICAgICAgICAgICAgICAic291cmNlIiA6IHsKICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjMyOTo1Ni0zMjk6NjAiLAogICAgICAgICAgICAgICAgICAibmFtZSIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAgICAidHlwZSIgOiAiT3BlcmFuZFJlZiIKICAgICAgICAgICAgICAgfQogICAgICAgICAgICB9LAogICAgICAgICAgICAib3BlcmFuZCIgOiBbIHsKICAgICAgICAgICAgICAgIm5hbWUiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgIm9wZXJhbmRUeXBlU3BlY2lmaWVyIiA6IHsKICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjMyOTozMi0zMjk6NTIiLAogICAgICAgICAgICAgICAgICAibmFtZSIgOiAie2h0dHA6Ly9obDcub3JnL2ZoaXJ9UXVlc3Rpb25uYWlyZUl0ZW1UeXBlIiwKICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk5hbWVkVHlwZVNwZWNpZmllciIKICAgICAgICAgICAgICAgfQogICAgICAgICAgICB9IF0KICAgICAgICAgfSwgewogICAgICAgICAgICAibG9jYXRvciIgOiAiMzMwOjEtMzMwOjcyIiwKICAgICAgICAgICAgIm5hbWUiIDogIlRvU3RyaW5nIiwKICAgICAgICAgICAgImNvbnRleHQiIDogIlBhdGllbnQiLAogICAgICAgICAgICAiYWNjZXNzTGV2ZWwiIDogIlB1YmxpYyIsCiAgICAgICAgICAgICJ0eXBlIiA6ICJGdW5jdGlvbkRlZiIsCiAgICAgICAgICAgICJleHByZXNzaW9uIiA6IHsKICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjMzMDo2Mi0zMzA6NzIiLAogICAgICAgICAgICAgICAicGF0aCIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAidHlwZSIgOiAiUHJvcGVydHkiLAogICAgICAgICAgICAgICAic291cmNlIiA6IHsKICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjMzMDo2Mi0zMzA6NjYiLAogICAgICAgICAgICAgICAgICAibmFtZSIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAgICAidHlwZSIgOiAiT3BlcmFuZFJlZiIKICAgICAgICAgICAgICAgfQogICAgICAgICAgICB9LAogICAgICAgICAgICAib3BlcmFuZCIgOiBbIHsKICAgICAgICAgICAgICAgIm5hbWUiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgIm9wZXJhbmRUeXBlU3BlY2lmaWVyIiA6IHsKICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjMzMDozMi0zMzA6NTgiLAogICAgICAgICAgICAgICAgICAibmFtZSIgOiAie2h0dHA6Ly9obDcub3JnL2ZoaXJ9UXVlc3Rpb25uYWlyZVJlc3BvbnNlU3RhdHVzIiwKICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk5hbWVkVHlwZVNwZWNpZmllciIKICAgICAgICAgICAgICAgfQogICAgICAgICAgICB9IF0KICAgICAgICAgfSwgewogICAgICAgICAgICAibG9jYXRvciIgOiAiMzMxOjEtMzMxOjY4IiwKICAgICAgICAgICAgIm5hbWUiIDogIlRvU3RyaW5nIiwKICAgICAgICAgICAgImNvbnRleHQiIDogIlBhdGllbnQiLAogICAgICAgICAgICAiYWNjZXNzTGV2ZWwiIDogIlB1YmxpYyIsCiAgICAgICAgICAgICJ0eXBlIiA6ICJGdW5jdGlvbkRlZiIsCiAgICAgICAgICAgICJleHByZXNzaW9uIiA6IHsKICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjMzMTo1OC0zMzE6NjgiLAogICAgICAgICAgICAgICAicGF0aCIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAidHlwZSIgOiAiUHJvcGVydHkiLAogICAgICAgICAgICAgICAic291cmNlIiA6IHsKICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjMzMTo1OC0zMzE6NjIiLAogICAgICAgICAgICAgICAgICAibmFtZSIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAgICAidHlwZSIgOiAiT3BlcmFuZFJlZiIKICAgICAgICAgICAgICAgfQogICAgICAgICAgICB9LAogICAgICAgICAgICAib3BlcmFuZCIgOiBbIHsKICAgICAgICAgICAgICAgIm5hbWUiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgIm9wZXJhbmRUeXBlU3BlY2lmaWVyIiA6IHsKICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjMzMTozMi0zMzE6NTQiLAogICAgICAgICAgICAgICAgICAibmFtZSIgOiAie2h0dHA6Ly9obDcub3JnL2ZoaXJ9UmVmZXJlbmNlSGFuZGxpbmdQb2xpY3kiLAogICAgICAgICAgICAgICAgICAidHlwZSIgOiAiTmFtZWRUeXBlU3BlY2lmaWVyIgogICAgICAgICAgICAgICB9CiAgICAgICAgICAgIH0gXQogICAgICAgICB9LCB7CiAgICAgICAgICAgICJsb2NhdG9yIiA6ICIzMzI6MS0zMzI6NjYiLAogICAgICAgICAgICAibmFtZSIgOiAiVG9TdHJpbmciLAogICAgICAgICAgICAiY29udGV4dCIgOiAiUGF0aWVudCIsCiAgICAgICAgICAgICJhY2Nlc3NMZXZlbCIgOiAiUHVibGljIiwKICAgICAgICAgICAgInR5cGUiIDogIkZ1bmN0aW9uRGVmIiwKICAgICAgICAgICAgImV4cHJlc3Npb24iIDogewogICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMzMyOjU2LTMzMjo2NiIsCiAgICAgICAgICAgICAgICJwYXRoIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICJ0eXBlIiA6ICJQcm9wZXJ0eSIsCiAgICAgICAgICAgICAgICJzb3VyY2UiIDogewogICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMzMyOjU2LTMzMjo2MCIsCiAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJPcGVyYW5kUmVmIgogICAgICAgICAgICAgICB9CiAgICAgICAgICAgIH0sCiAgICAgICAgICAgICJvcGVyYW5kIiA6IFsgewogICAgICAgICAgICAgICAibmFtZSIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAib3BlcmFuZFR5cGVTcGVjaWZpZXIiIDogewogICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMzMyOjMyLTMzMjo1MiIsCiAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJ7aHR0cDovL2hsNy5vcmcvZmhpcn1SZWZlcmVuY2VWZXJzaW9uUnVsZXMiLAogICAgICAgICAgICAgICAgICAidHlwZSIgOiAiTmFtZWRUeXBlU3BlY2lmaWVyIgogICAgICAgICAgICAgICB9CiAgICAgICAgICAgIH0gXQogICAgICAgICB9LCB7CiAgICAgICAgICAgICJsb2NhdG9yIiA6ICIzMzM6MS0zMzM6NjciLAogICAgICAgICAgICAibmFtZSIgOiAiVG9TdHJpbmciLAogICAgICAgICAgICAiY29udGV4dCIgOiAiUGF0aWVudCIsCiAgICAgICAgICAgICJhY2Nlc3NMZXZlbCIgOiAiUHVibGljIiwKICAgICAgICAgICAgInR5cGUiIDogIkZ1bmN0aW9uRGVmIiwKICAgICAgICAgICAgImV4cHJlc3Npb24iIDogewogICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMzMzOjU3LTMzMzo2NyIsCiAgICAgICAgICAgICAgICJwYXRoIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICJ0eXBlIiA6ICJQcm9wZXJ0eSIsCiAgICAgICAgICAgICAgICJzb3VyY2UiIDogewogICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMzMzOjU3LTMzMzo2MSIsCiAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJPcGVyYW5kUmVmIgogICAgICAgICAgICAgICB9CiAgICAgICAgICAgIH0sCiAgICAgICAgICAgICJvcGVyYW5kIiA6IFsgewogICAgICAgICAgICAgICAibmFtZSIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAib3BlcmFuZFR5cGVTcGVjaWZpZXIiIDogewogICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMzMzOjMyLTMzMzo1MyIsCiAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJ7aHR0cDovL2hsNy5vcmcvZmhpcn1SZWZlcnJlZERvY3VtZW50U3RhdHVzIiwKICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk5hbWVkVHlwZVNwZWNpZmllciIKICAgICAgICAgICAgICAgfQogICAgICAgICAgICB9IF0KICAgICAgICAgfSwgewogICAgICAgICAgICAibG9jYXRvciIgOiAiMzM0OjEtMzM0OjY0IiwKICAgICAgICAgICAgIm5hbWUiIDogIlRvU3RyaW5nIiwKICAgICAgICAgICAgImNvbnRleHQiIDogIlBhdGllbnQiLAogICAgICAgICAgICAiYWNjZXNzTGV2ZWwiIDogIlB1YmxpYyIsCiAgICAgICAgICAgICJ0eXBlIiA6ICJGdW5jdGlvbkRlZiIsCiAgICAgICAgICAgICJleHByZXNzaW9uIiA6IHsKICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjMzNDo1NC0zMzQ6NjQiLAogICAgICAgICAgICAgICAicGF0aCIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAidHlwZSIgOiAiUHJvcGVydHkiLAogICAgICAgICAgICAgICAic291cmNlIiA6IHsKICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjMzNDo1NC0zMzQ6NTgiLAogICAgICAgICAgICAgICAgICAibmFtZSIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAgICAidHlwZSIgOiAiT3BlcmFuZFJlZiIKICAgICAgICAgICAgICAgfQogICAgICAgICAgICB9LAogICAgICAgICAgICAib3BlcmFuZCIgOiBbIHsKICAgICAgICAgICAgICAgIm5hbWUiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgIm9wZXJhbmRUeXBlU3BlY2lmaWVyIiA6IHsKICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjMzNDozMi0zMzQ6NTAiLAogICAgICAgICAgICAgICAgICAibmFtZSIgOiAie2h0dHA6Ly9obDcub3JnL2ZoaXJ9UmVsYXRlZEFydGlmYWN0VHlwZSIsCiAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJOYW1lZFR5cGVTcGVjaWZpZXIiCiAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgfSBdCiAgICAgICAgIH0sIHsKICAgICAgICAgICAgImxvY2F0b3IiIDogIjMzNToxLTMzNTo2MiIsCiAgICAgICAgICAgICJuYW1lIiA6ICJUb1N0cmluZyIsCiAgICAgICAgICAgICJjb250ZXh0IiA6ICJQYXRpZW50IiwKICAgICAgICAgICAgImFjY2Vzc0xldmVsIiA6ICJQdWJsaWMiLAogICAgICAgICAgICAidHlwZSIgOiAiRnVuY3Rpb25EZWYiLAogICAgICAgICAgICAiZXhwcmVzc2lvbiIgOiB7CiAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIzMzU6NTItMzM1OjYyIiwKICAgICAgICAgICAgICAgInBhdGgiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgInR5cGUiIDogIlByb3BlcnR5IiwKICAgICAgICAgICAgICAgInNvdXJjZSIgOiB7CiAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIzMzU6NTItMzM1OjU2IiwKICAgICAgICAgICAgICAgICAgIm5hbWUiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk9wZXJhbmRSZWYiCiAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgfSwKICAgICAgICAgICAgIm9wZXJhbmQiIDogWyB7CiAgICAgICAgICAgICAgICJuYW1lIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICJvcGVyYW5kVHlwZVNwZWNpZmllciIgOiB7CiAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIzMzU6MzItMzM1OjQ4IiwKICAgICAgICAgICAgICAgICAgIm5hbWUiIDogIntodHRwOi8vaGw3Lm9yZy9maGlyfVJlbWl0dGFuY2VPdXRjb21lIiwKICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk5hbWVkVHlwZVNwZWNpZmllciIKICAgICAgICAgICAgICAgfQogICAgICAgICAgICB9IF0KICAgICAgICAgfSwgewogICAgICAgICAgICAibG9jYXRvciIgOiAiMzM2OjEtMzM2OjU5IiwKICAgICAgICAgICAgIm5hbWUiIDogIlRvU3RyaW5nIiwKICAgICAgICAgICAgImNvbnRleHQiIDogIlBhdGllbnQiLAogICAgICAgICAgICAiYWNjZXNzTGV2ZWwiIDogIlB1YmxpYyIsCiAgICAgICAgICAgICJ0eXBlIiA6ICJGdW5jdGlvbkRlZiIsCiAgICAgICAgICAgICJleHByZXNzaW9uIiA6IHsKICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjMzNjo0OS0zMzY6NTkiLAogICAgICAgICAgICAgICAicGF0aCIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAidHlwZSIgOiAiUHJvcGVydHkiLAogICAgICAgICAgICAgICAic291cmNlIiA6IHsKICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjMzNjo0OS0zMzY6NTMiLAogICAgICAgICAgICAgICAgICAibmFtZSIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAgICAidHlwZSIgOiAiT3BlcmFuZFJlZiIKICAgICAgICAgICAgICAgfQogICAgICAgICAgICB9LAogICAgICAgICAgICAib3BlcmFuZCIgOiBbIHsKICAgICAgICAgICAgICAgIm5hbWUiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgIm9wZXJhbmRUeXBlU3BlY2lmaWVyIiA6IHsKICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjMzNjozMi0zMzY6NDUiLAogICAgICAgICAgICAgICAgICAibmFtZSIgOiAie2h0dHA6Ly9obDcub3JnL2ZoaXJ9UmVwb3NpdG9yeVR5cGUiLAogICAgICAgICAgICAgICAgICAidHlwZSIgOiAiTmFtZWRUeXBlU3BlY2lmaWVyIgogICAgICAgICAgICAgICB9CiAgICAgICAgICAgIH0gXQogICAgICAgICB9LCB7CiAgICAgICAgICAgICJsb2NhdG9yIiA6ICIzMzc6MS0zMzc6NTgiLAogICAgICAgICAgICAibmFtZSIgOiAiVG9TdHJpbmciLAogICAgICAgICAgICAiY29udGV4dCIgOiAiUGF0aWVudCIsCiAgICAgICAgICAgICJhY2Nlc3NMZXZlbCIgOiAiUHVibGljIiwKICAgICAgICAgICAgInR5cGUiIDogIkZ1bmN0aW9uRGVmIiwKICAgICAgICAgICAgImV4cHJlc3Npb24iIDogewogICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMzM3OjQ4LTMzNzo1OCIsCiAgICAgICAgICAgICAgICJwYXRoIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICJ0eXBlIiA6ICJQcm9wZXJ0eSIsCiAgICAgICAgICAgICAgICJzb3VyY2UiIDogewogICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMzM3OjQ4LTMzNzo1MiIsCiAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJPcGVyYW5kUmVmIgogICAgICAgICAgICAgICB9CiAgICAgICAgICAgIH0sCiAgICAgICAgICAgICJvcGVyYW5kIiA6IFsgewogICAgICAgICAgICAgICAibmFtZSIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAib3BlcmFuZFR5cGVTcGVjaWZpZXIiIDogewogICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMzM3OjMyLTMzNzo0NCIsCiAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJ7aHR0cDovL2hsNy5vcmcvZmhpcn1SZXF1ZXN0SW50ZW50IiwKICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk5hbWVkVHlwZVNwZWNpZmllciIKICAgICAgICAgICAgICAgfQogICAgICAgICAgICB9IF0KICAgICAgICAgfSwgewogICAgICAgICAgICAibG9jYXRvciIgOiAiMzM4OjEtMzM4OjYwIiwKICAgICAgICAgICAgIm5hbWUiIDogIlRvU3RyaW5nIiwKICAgICAgICAgICAgImNvbnRleHQiIDogIlBhdGllbnQiLAogICAgICAgICAgICAiYWNjZXNzTGV2ZWwiIDogIlB1YmxpYyIsCiAgICAgICAgICAgICJ0eXBlIiA6ICJGdW5jdGlvbkRlZiIsCiAgICAgICAgICAgICJleHByZXNzaW9uIiA6IHsKICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjMzODo1MC0zMzg6NjAiLAogICAgICAgICAgICAgICAicGF0aCIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAidHlwZSIgOiAiUHJvcGVydHkiLAogICAgICAgICAgICAgICAic291cmNlIiA6IHsKICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjMzODo1MC0zMzg6NTQiLAogICAgICAgICAgICAgICAgICAibmFtZSIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAgICAidHlwZSIgOiAiT3BlcmFuZFJlZiIKICAgICAgICAgICAgICAgfQogICAgICAgICAgICB9LAogICAgICAgICAgICAib3BlcmFuZCIgOiBbIHsKICAgICAgICAgICAgICAgIm5hbWUiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgIm9wZXJhbmRUeXBlU3BlY2lmaWVyIiA6IHsKICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjMzODozMi0zMzg6NDYiLAogICAgICAgICAgICAgICAgICAibmFtZSIgOiAie2h0dHA6Ly9obDcub3JnL2ZoaXJ9UmVxdWVzdFByaW9yaXR5IiwKICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk5hbWVkVHlwZVNwZWNpZmllciIKICAgICAgICAgICAgICAgfQogICAgICAgICAgICB9IF0KICAgICAgICAgfSwgewogICAgICAgICAgICAibG9jYXRvciIgOiAiMzM5OjEtMzM5OjU4IiwKICAgICAgICAgICAgIm5hbWUiIDogIlRvU3RyaW5nIiwKICAgICAgICAgICAgImNvbnRleHQiIDogIlBhdGllbnQiLAogICAgICAgICAgICAiYWNjZXNzTGV2ZWwiIDogIlB1YmxpYyIsCiAgICAgICAgICAgICJ0eXBlIiA6ICJGdW5jdGlvbkRlZiIsCiAgICAgICAgICAgICJleHByZXNzaW9uIiA6IHsKICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjMzOTo0OC0zMzk6NTgiLAogICAgICAgICAgICAgICAicGF0aCIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAidHlwZSIgOiAiUHJvcGVydHkiLAogICAgICAgICAgICAgICAic291cmNlIiA6IHsKICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjMzOTo0OC0zMzk6NTIiLAogICAgICAgICAgICAgICAgICAibmFtZSIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAgICAidHlwZSIgOiAiT3BlcmFuZFJlZiIKICAgICAgICAgICAgICAgfQogICAgICAgICAgICB9LAogICAgICAgICAgICAib3BlcmFuZCIgOiBbIHsKICAgICAgICAgICAgICAgIm5hbWUiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgIm9wZXJhbmRUeXBlU3BlY2lmaWVyIiA6IHsKICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjMzOTozMi0zMzk6NDQiLAogICAgICAgICAgICAgICAgICAibmFtZSIgOiAie2h0dHA6Ly9obDcub3JnL2ZoaXJ9UmVxdWVzdFN0YXR1cyIsCiAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJOYW1lZFR5cGVTcGVjaWZpZXIiCiAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgfSBdCiAgICAgICAgIH0sIHsKICAgICAgICAgICAgImxvY2F0b3IiIDogIjM0MDoxLTM0MDo2NCIsCiAgICAgICAgICAgICJuYW1lIiA6ICJUb1N0cmluZyIsCiAgICAgICAgICAgICJjb250ZXh0IiA6ICJQYXRpZW50IiwKICAgICAgICAgICAgImFjY2Vzc0xldmVsIiA6ICJQdWJsaWMiLAogICAgICAgICAgICAidHlwZSIgOiAiRnVuY3Rpb25EZWYiLAogICAgICAgICAgICAiZXhwcmVzc2lvbiIgOiB7CiAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIzNDA6NTQtMzQwOjY0IiwKICAgICAgICAgICAgICAgInBhdGgiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgInR5cGUiIDogIlByb3BlcnR5IiwKICAgICAgICAgICAgICAgInNvdXJjZSIgOiB7CiAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIzNDA6NTQtMzQwOjU4IiwKICAgICAgICAgICAgICAgICAgIm5hbWUiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk9wZXJhbmRSZWYiCiAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgfSwKICAgICAgICAgICAgIm9wZXJhbmQiIDogWyB7CiAgICAgICAgICAgICAgICJuYW1lIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICJvcGVyYW5kVHlwZVNwZWNpZmllciIgOiB7CiAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIzNDA6MzItMzQwOjUwIiwKICAgICAgICAgICAgICAgICAgIm5hbWUiIDogIntodHRwOi8vaGw3Lm9yZy9maGlyfVJlc2VhcmNoRWxlbWVudFR5cGUiLAogICAgICAgICAgICAgICAgICAidHlwZSIgOiAiTmFtZWRUeXBlU3BlY2lmaWVyIgogICAgICAgICAgICAgICB9CiAgICAgICAgICAgIH0gXQogICAgICAgICB9LCB7CiAgICAgICAgICAgICJsb2NhdG9yIiA6ICIzNDE6MS0zNDE6NjQiLAogICAgICAgICAgICAibmFtZSIgOiAiVG9TdHJpbmciLAogICAgICAgICAgICAiY29udGV4dCIgOiAiUGF0aWVudCIsCiAgICAgICAgICAgICJhY2Nlc3NMZXZlbCIgOiAiUHVibGljIiwKICAgICAgICAgICAgInR5cGUiIDogIkZ1bmN0aW9uRGVmIiwKICAgICAgICAgICAgImV4cHJlc3Npb24iIDogewogICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMzQxOjU0LTM0MTo2NCIsCiAgICAgICAgICAgICAgICJwYXRoIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICJ0eXBlIiA6ICJQcm9wZXJ0eSIsCiAgICAgICAgICAgICAgICJzb3VyY2UiIDogewogICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMzQxOjU0LTM0MTo1OCIsCiAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJPcGVyYW5kUmVmIgogICAgICAgICAgICAgICB9CiAgICAgICAgICAgIH0sCiAgICAgICAgICAgICJvcGVyYW5kIiA6IFsgewogICAgICAgICAgICAgICAibmFtZSIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAib3BlcmFuZFR5cGVTcGVjaWZpZXIiIDogewogICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMzQxOjMyLTM0MTo1MCIsCiAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJ7aHR0cDovL2hsNy5vcmcvZmhpcn1SZXNlYXJjaFN0dWR5U3RhdHVzIiwKICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk5hbWVkVHlwZVNwZWNpZmllciIKICAgICAgICAgICAgICAgfQogICAgICAgICAgICB9IF0KICAgICAgICAgfSwgewogICAgICAgICAgICAibG9jYXRvciIgOiAiMzQyOjEtMzQyOjY2IiwKICAgICAgICAgICAgIm5hbWUiIDogIlRvU3RyaW5nIiwKICAgICAgICAgICAgImNvbnRleHQiIDogIlBhdGllbnQiLAogICAgICAgICAgICAiYWNjZXNzTGV2ZWwiIDogIlB1YmxpYyIsCiAgICAgICAgICAgICJ0eXBlIiA6ICJGdW5jdGlvbkRlZiIsCiAgICAgICAgICAgICJleHByZXNzaW9uIiA6IHsKICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjM0Mjo1Ni0zNDI6NjYiLAogICAgICAgICAgICAgICAicGF0aCIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAidHlwZSIgOiAiUHJvcGVydHkiLAogICAgICAgICAgICAgICAic291cmNlIiA6IHsKICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjM0Mjo1Ni0zNDI6NjAiLAogICAgICAgICAgICAgICAgICAibmFtZSIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAgICAidHlwZSIgOiAiT3BlcmFuZFJlZiIKICAgICAgICAgICAgICAgfQogICAgICAgICAgICB9LAogICAgICAgICAgICAib3BlcmFuZCIgOiBbIHsKICAgICAgICAgICAgICAgIm5hbWUiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgIm9wZXJhbmRUeXBlU3BlY2lmaWVyIiA6IHsKICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjM0MjozMi0zNDI6NTIiLAogICAgICAgICAgICAgICAgICAibmFtZSIgOiAie2h0dHA6Ly9obDcub3JnL2ZoaXJ9UmVzZWFyY2hTdWJqZWN0U3RhdHVzIiwKICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk5hbWVkVHlwZVNwZWNpZmllciIKICAgICAgICAgICAgICAgfQogICAgICAgICAgICB9IF0KICAgICAgICAgfSwgewogICAgICAgICAgICAibG9jYXRvciIgOiAiMzQzOjEtMzQzOjU3IiwKICAgICAgICAgICAgIm5hbWUiIDogIlRvU3RyaW5nIiwKICAgICAgICAgICAgImNvbnRleHQiIDogIlBhdGllbnQiLAogICAgICAgICAgICAiYWNjZXNzTGV2ZWwiIDogIlB1YmxpYyIsCiAgICAgICAgICAgICJ0eXBlIiA6ICJGdW5jdGlvbkRlZiIsCiAgICAgICAgICAgICJleHByZXNzaW9uIiA6IHsKICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjM0Mzo0Ny0zNDM6NTciLAogICAgICAgICAgICAgICAicGF0aCIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAidHlwZSIgOiAiUHJvcGVydHkiLAogICAgICAgICAgICAgICAic291cmNlIiA6IHsKICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjM0Mzo0Ny0zNDM6NTEiLAogICAgICAgICAgICAgICAgICAibmFtZSIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAgICAidHlwZSIgOiAiT3BlcmFuZFJlZiIKICAgICAgICAgICAgICAgfQogICAgICAgICAgICB9LAogICAgICAgICAgICAib3BlcmFuZCIgOiBbIHsKICAgICAgICAgICAgICAgIm5hbWUiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgIm9wZXJhbmRUeXBlU3BlY2lmaWVyIiA6IHsKICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjM0MzozMi0zNDM6NDMiLAogICAgICAgICAgICAgICAgICAibmFtZSIgOiAie2h0dHA6Ly9obDcub3JnL2ZoaXJ9UmVzb3VyY2VUeXBlIiwKICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk5hbWVkVHlwZVNwZWNpZmllciIKICAgICAgICAgICAgICAgfQogICAgICAgICAgICB9IF0KICAgICAgICAgfSwgewogICAgICAgICAgICAibG9jYXRvciIgOiAiMzQ0OjEtMzQ0OjY2IiwKICAgICAgICAgICAgIm5hbWUiIDogIlRvU3RyaW5nIiwKICAgICAgICAgICAgImNvbnRleHQiIDogIlBhdGllbnQiLAogICAgICAgICAgICAiYWNjZXNzTGV2ZWwiIDogIlB1YmxpYyIsCiAgICAgICAgICAgICJ0eXBlIiA6ICJGdW5jdGlvbkRlZiIsCiAgICAgICAgICAgICJleHByZXNzaW9uIiA6IHsKICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjM0NDo1Ni0zNDQ6NjYiLAogICAgICAgICAgICAgICAicGF0aCIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAidHlwZSIgOiAiUHJvcGVydHkiLAogICAgICAgICAgICAgICAic291cmNlIiA6IHsKICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjM0NDo1Ni0zNDQ6NjAiLAogICAgICAgICAgICAgICAgICAibmFtZSIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAgICAidHlwZSIgOiAiT3BlcmFuZFJlZiIKICAgICAgICAgICAgICAgfQogICAgICAgICAgICB9LAogICAgICAgICAgICAib3BlcmFuZCIgOiBbIHsKICAgICAgICAgICAgICAgIm5hbWUiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgIm9wZXJhbmRUeXBlU3BlY2lmaWVyIiA6IHsKICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjM0NDozMi0zNDQ6NTIiLAogICAgICAgICAgICAgICAgICAibmFtZSIgOiAie2h0dHA6Ly9obDcub3JnL2ZoaXJ9UmVzb3VyY2VWZXJzaW9uUG9saWN5IiwKICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk5hbWVkVHlwZVNwZWNpZmllciIKICAgICAgICAgICAgICAgfQogICAgICAgICAgICB9IF0KICAgICAgICAgfSwgewogICAgICAgICAgICAibG9jYXRvciIgOiAiMzQ1OjEtMzQ1OjU3IiwKICAgICAgICAgICAgIm5hbWUiIDogIlRvU3RyaW5nIiwKICAgICAgICAgICAgImNvbnRleHQiIDogIlBhdGllbnQiLAogICAgICAgICAgICAiYWNjZXNzTGV2ZWwiIDogIlB1YmxpYyIsCiAgICAgICAgICAgICJ0eXBlIiA6ICJGdW5jdGlvbkRlZiIsCiAgICAgICAgICAgICJleHByZXNzaW9uIiA6IHsKICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjM0NTo0Ny0zNDU6NTciLAogICAgICAgICAgICAgICAicGF0aCIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAidHlwZSIgOiAiUHJvcGVydHkiLAogICAgICAgICAgICAgICAic291cmNlIiA6IHsKICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjM0NTo0Ny0zNDU6NTEiLAogICAgICAgICAgICAgICAgICAibmFtZSIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAgICAidHlwZSIgOiAiT3BlcmFuZFJlZiIKICAgICAgICAgICAgICAgfQogICAgICAgICAgICB9LAogICAgICAgICAgICAib3BlcmFuZCIgOiBbIHsKICAgICAgICAgICAgICAgIm5hbWUiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgIm9wZXJhbmRUeXBlU3BlY2lmaWVyIiA6IHsKICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjM0NTozMi0zNDU6NDMiLAogICAgICAgICAgICAgICAgICAibmFtZSIgOiAie2h0dHA6Ly9obDcub3JnL2ZoaXJ9UmVzcG9uc2VUeXBlIiwKICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk5hbWVkVHlwZVNwZWNpZmllciIKICAgICAgICAgICAgICAgfQogICAgICAgICAgICB9IF0KICAgICAgICAgfSwgewogICAgICAgICAgICAibG9jYXRvciIgOiAiMzQ2OjEtMzQ2OjY2IiwKICAgICAgICAgICAgIm5hbWUiIDogIlRvU3RyaW5nIiwKICAgICAgICAgICAgImNvbnRleHQiIDogIlBhdGllbnQiLAogICAgICAgICAgICAiYWNjZXNzTGV2ZWwiIDogIlB1YmxpYyIsCiAgICAgICAgICAgICJ0eXBlIiA6ICJGdW5jdGlvbkRlZiIsCiAgICAgICAgICAgICJleHByZXNzaW9uIiA6IHsKICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjM0Njo1Ni0zNDY6NjYiLAogICAgICAgICAgICAgICAicGF0aCIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAidHlwZSIgOiAiUHJvcGVydHkiLAogICAgICAgICAgICAgICAic291cmNlIiA6IHsKICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjM0Njo1Ni0zNDY6NjAiLAogICAgICAgICAgICAgICAgICAibmFtZSIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAgICAidHlwZSIgOiAiT3BlcmFuZFJlZiIKICAgICAgICAgICAgICAgfQogICAgICAgICAgICB9LAogICAgICAgICAgICAib3BlcmFuZCIgOiBbIHsKICAgICAgICAgICAgICAgIm5hbWUiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgIm9wZXJhbmRUeXBlU3BlY2lmaWVyIiA6IHsKICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjM0NjozMi0zNDY6NTIiLAogICAgICAgICAgICAgICAgICAibmFtZSIgOiAie2h0dHA6Ly9obDcub3JnL2ZoaXJ9UmVzdGZ1bENhcGFiaWxpdHlNb2RlIiwKICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk5hbWVkVHlwZVNwZWNpZmllciIKICAgICAgICAgICAgICAgfQogICAgICAgICAgICB9IF0KICAgICAgICAgfSwgewogICAgICAgICAgICAibG9jYXRvciIgOiAiMzQ3OjEtMzQ3OjY1IiwKICAgICAgICAgICAgIm5hbWUiIDogIlRvU3RyaW5nIiwKICAgICAgICAgICAgImNvbnRleHQiIDogIlBhdGllbnQiLAogICAgICAgICAgICAiYWNjZXNzTGV2ZWwiIDogIlB1YmxpYyIsCiAgICAgICAgICAgICJ0eXBlIiA6ICJGdW5jdGlvbkRlZiIsCiAgICAgICAgICAgICJleHByZXNzaW9uIiA6IHsKICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjM0Nzo1NS0zNDc6NjUiLAogICAgICAgICAgICAgICAicGF0aCIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAidHlwZSIgOiAiUHJvcGVydHkiLAogICAgICAgICAgICAgICAic291cmNlIiA6IHsKICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjM0Nzo1NS0zNDc6NTkiLAogICAgICAgICAgICAgICAgICAibmFtZSIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAgICAidHlwZSIgOiAiT3BlcmFuZFJlZiIKICAgICAgICAgICAgICAgfQogICAgICAgICAgICB9LAogICAgICAgICAgICAib3BlcmFuZCIgOiBbIHsKICAgICAgICAgICAgICAgIm5hbWUiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgIm9wZXJhbmRUeXBlU3BlY2lmaWVyIiA6IHsKICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjM0NzozMi0zNDc6NTEiLAogICAgICAgICAgICAgICAgICAibmFtZSIgOiAie2h0dHA6Ly9obDcub3JnL2ZoaXJ9Umlza0Fzc2Vzc21lbnRTdGF0dXMiLAogICAgICAgICAgICAgICAgICAidHlwZSIgOiAiTmFtZWRUeXBlU3BlY2lmaWVyIgogICAgICAgICAgICAgICB9CiAgICAgICAgICAgIH0gXQogICAgICAgICB9LCB7CiAgICAgICAgICAgICJsb2NhdG9yIiA6ICIzNDg6MS0zNDg6NTYiLAogICAgICAgICAgICAibmFtZSIgOiAiVG9TdHJpbmciLAogICAgICAgICAgICAiY29udGV4dCIgOiAiUGF0aWVudCIsCiAgICAgICAgICAgICJhY2Nlc3NMZXZlbCIgOiAiUHVibGljIiwKICAgICAgICAgICAgInR5cGUiIDogIkZ1bmN0aW9uRGVmIiwKICAgICAgICAgICAgImV4cHJlc3Npb24iIDogewogICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMzQ4OjQ2LTM0ODo1NiIsCiAgICAgICAgICAgICAgICJwYXRoIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICJ0eXBlIiA6ICJQcm9wZXJ0eSIsCiAgICAgICAgICAgICAgICJzb3VyY2UiIDogewogICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMzQ4OjQ2LTM0ODo1MCIsCiAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJPcGVyYW5kUmVmIgogICAgICAgICAgICAgICB9CiAgICAgICAgICAgIH0sCiAgICAgICAgICAgICJvcGVyYW5kIiA6IFsgewogICAgICAgICAgICAgICAibmFtZSIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAib3BlcmFuZFR5cGVTcGVjaWZpZXIiIDogewogICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMzQ4OjMyLTM0ODo0MiIsCiAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJ7aHR0cDovL2hsNy5vcmcvZmhpcn1TUERYTGljZW5zZSIsCiAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJOYW1lZFR5cGVTcGVjaWZpZXIiCiAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgfSBdCiAgICAgICAgIH0sIHsKICAgICAgICAgICAgImxvY2F0b3IiIDogIjM0OToxLTM0OTo2MSIsCiAgICAgICAgICAgICJuYW1lIiA6ICJUb1N0cmluZyIsCiAgICAgICAgICAgICJjb250ZXh0IiA6ICJQYXRpZW50IiwKICAgICAgICAgICAgImFjY2Vzc0xldmVsIiA6ICJQdWJsaWMiLAogICAgICAgICAgICAidHlwZSIgOiAiRnVuY3Rpb25EZWYiLAogICAgICAgICAgICAiZXhwcmVzc2lvbiIgOiB7CiAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIzNDk6NTEtMzQ5OjYxIiwKICAgICAgICAgICAgICAgInBhdGgiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgInR5cGUiIDogIlByb3BlcnR5IiwKICAgICAgICAgICAgICAgInNvdXJjZSIgOiB7CiAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIzNDk6NTEtMzQ5OjU1IiwKICAgICAgICAgICAgICAgICAgIm5hbWUiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk9wZXJhbmRSZWYiCiAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgfSwKICAgICAgICAgICAgIm9wZXJhbmQiIDogWyB7CiAgICAgICAgICAgICAgICJuYW1lIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICJvcGVyYW5kVHlwZVNwZWNpZmllciIgOiB7CiAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIzNDk6MzItMzQ5OjQ3IiwKICAgICAgICAgICAgICAgICAgIm5hbWUiIDogIntodHRwOi8vaGw3Lm9yZy9maGlyfVNlYXJjaENvbXBhcmF0b3IiLAogICAgICAgICAgICAgICAgICAidHlwZSIgOiAiTmFtZWRUeXBlU3BlY2lmaWVyIgogICAgICAgICAgICAgICB9CiAgICAgICAgICAgIH0gXQogICAgICAgICB9LCB7CiAgICAgICAgICAgICJsb2NhdG9yIiA6ICIzNTA6MS0zNTA6NjAiLAogICAgICAgICAgICAibmFtZSIgOiAiVG9TdHJpbmciLAogICAgICAgICAgICAiY29udGV4dCIgOiAiUGF0aWVudCIsCiAgICAgICAgICAgICJhY2Nlc3NMZXZlbCIgOiAiUHVibGljIiwKICAgICAgICAgICAgInR5cGUiIDogIkZ1bmN0aW9uRGVmIiwKICAgICAgICAgICAgImV4cHJlc3Npb24iIDogewogICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMzUwOjUwLTM1MDo2MCIsCiAgICAgICAgICAgICAgICJwYXRoIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICJ0eXBlIiA6ICJQcm9wZXJ0eSIsCiAgICAgICAgICAgICAgICJzb3VyY2UiIDogewogICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMzUwOjUwLTM1MDo1NCIsCiAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJPcGVyYW5kUmVmIgogICAgICAgICAgICAgICB9CiAgICAgICAgICAgIH0sCiAgICAgICAgICAgICJvcGVyYW5kIiA6IFsgewogICAgICAgICAgICAgICAibmFtZSIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAib3BlcmFuZFR5cGVTcGVjaWZpZXIiIDogewogICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMzUwOjMyLTM1MDo0NiIsCiAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJ7aHR0cDovL2hsNy5vcmcvZmhpcn1TZWFyY2hFbnRyeU1vZGUiLAogICAgICAgICAgICAgICAgICAidHlwZSIgOiAiTmFtZWRUeXBlU3BlY2lmaWVyIgogICAgICAgICAgICAgICB9CiAgICAgICAgICAgIH0gXQogICAgICAgICB9LCB7CiAgICAgICAgICAgICJsb2NhdG9yIiA6ICIzNTE6MS0zNTE6NjMiLAogICAgICAgICAgICAibmFtZSIgOiAiVG9TdHJpbmciLAogICAgICAgICAgICAiY29udGV4dCIgOiAiUGF0aWVudCIsCiAgICAgICAgICAgICJhY2Nlc3NMZXZlbCIgOiAiUHVibGljIiwKICAgICAgICAgICAgInR5cGUiIDogIkZ1bmN0aW9uRGVmIiwKICAgICAgICAgICAgImV4cHJlc3Npb24iIDogewogICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMzUxOjUzLTM1MTo2MyIsCiAgICAgICAgICAgICAgICJwYXRoIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICJ0eXBlIiA6ICJQcm9wZXJ0eSIsCiAgICAgICAgICAgICAgICJzb3VyY2UiIDogewogICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMzUxOjUzLTM1MTo1NyIsCiAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJPcGVyYW5kUmVmIgogICAgICAgICAgICAgICB9CiAgICAgICAgICAgIH0sCiAgICAgICAgICAgICJvcGVyYW5kIiA6IFsgewogICAgICAgICAgICAgICAibmFtZSIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAib3BlcmFuZFR5cGVTcGVjaWZpZXIiIDogewogICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMzUxOjMyLTM1MTo0OSIsCiAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJ7aHR0cDovL2hsNy5vcmcvZmhpcn1TZWFyY2hNb2RpZmllckNvZGUiLAogICAgICAgICAgICAgICAgICAidHlwZSIgOiAiTmFtZWRUeXBlU3BlY2lmaWVyIgogICAgICAgICAgICAgICB9CiAgICAgICAgICAgIH0gXQogICAgICAgICB9LCB7CiAgICAgICAgICAgICJsb2NhdG9yIiA6ICIzNTI6MS0zNTI6NjAiLAogICAgICAgICAgICAibmFtZSIgOiAiVG9TdHJpbmciLAogICAgICAgICAgICAiY29udGV4dCIgOiAiUGF0aWVudCIsCiAgICAgICAgICAgICJhY2Nlc3NMZXZlbCIgOiAiUHVibGljIiwKICAgICAgICAgICAgInR5cGUiIDogIkZ1bmN0aW9uRGVmIiwKICAgICAgICAgICAgImV4cHJlc3Npb24iIDogewogICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMzUyOjUwLTM1Mjo2MCIsCiAgICAgICAgICAgICAgICJwYXRoIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICJ0eXBlIiA6ICJQcm9wZXJ0eSIsCiAgICAgICAgICAgICAgICJzb3VyY2UiIDogewogICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMzUyOjUwLTM1Mjo1NCIsCiAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJPcGVyYW5kUmVmIgogICAgICAgICAgICAgICB9CiAgICAgICAgICAgIH0sCiAgICAgICAgICAgICJvcGVyYW5kIiA6IFsgewogICAgICAgICAgICAgICAibmFtZSIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAib3BlcmFuZFR5cGVTcGVjaWZpZXIiIDogewogICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMzUyOjMyLTM1Mjo0NiIsCiAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJ7aHR0cDovL2hsNy5vcmcvZmhpcn1TZWFyY2hQYXJhbVR5cGUiLAogICAgICAgICAgICAgICAgICAidHlwZSIgOiAiTmFtZWRUeXBlU3BlY2lmaWVyIgogICAgICAgICAgICAgICB9CiAgICAgICAgICAgIH0gXQogICAgICAgICB9LCB7CiAgICAgICAgICAgICJsb2NhdG9yIiA6ICIzNTM6MS0zNTM6NTYiLAogICAgICAgICAgICAibmFtZSIgOiAiVG9TdHJpbmciLAogICAgICAgICAgICAiY29udGV4dCIgOiAiUGF0aWVudCIsCiAgICAgICAgICAgICJhY2Nlc3NMZXZlbCIgOiAiUHVibGljIiwKICAgICAgICAgICAgInR5cGUiIDogIkZ1bmN0aW9uRGVmIiwKICAgICAgICAgICAgImV4cHJlc3Npb24iIDogewogICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMzUzOjQ2LTM1Mzo1NiIsCiAgICAgICAgICAgICAgICJwYXRoIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICJ0eXBlIiA6ICJQcm9wZXJ0eSIsCiAgICAgICAgICAgICAgICJzb3VyY2UiIDogewogICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMzUzOjQ2LTM1Mzo1MCIsCiAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJPcGVyYW5kUmVmIgogICAgICAgICAgICAgICB9CiAgICAgICAgICAgIH0sCiAgICAgICAgICAgICJvcGVyYW5kIiA6IFsgewogICAgICAgICAgICAgICAibmFtZSIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAib3BlcmFuZFR5cGVTcGVjaWZpZXIiIDogewogICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMzUzOjMyLTM1Mzo0MiIsCiAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJ7aHR0cDovL2hsNy5vcmcvZmhpcn1TZWN0aW9uTW9kZSIsCiAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJOYW1lZFR5cGVTcGVjaWZpZXIiCiAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgfSBdCiAgICAgICAgIH0sIHsKICAgICAgICAgICAgImxvY2F0b3IiIDogIjM1NDoxLTM1NDo1NyIsCiAgICAgICAgICAgICJuYW1lIiA6ICJUb1N0cmluZyIsCiAgICAgICAgICAgICJjb250ZXh0IiA6ICJQYXRpZW50IiwKICAgICAgICAgICAgImFjY2Vzc0xldmVsIiA6ICJQdWJsaWMiLAogICAgICAgICAgICAidHlwZSIgOiAiRnVuY3Rpb25EZWYiLAogICAgICAgICAgICAiZXhwcmVzc2lvbiIgOiB7CiAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIzNTQ6NDctMzU0OjU3IiwKICAgICAgICAgICAgICAgInBhdGgiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgInR5cGUiIDogIlByb3BlcnR5IiwKICAgICAgICAgICAgICAgInNvdXJjZSIgOiB7CiAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIzNTQ6NDctMzU0OjUxIiwKICAgICAgICAgICAgICAgICAgIm5hbWUiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk9wZXJhbmRSZWYiCiAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgfSwKICAgICAgICAgICAgIm9wZXJhbmQiIDogWyB7CiAgICAgICAgICAgICAgICJuYW1lIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICJvcGVyYW5kVHlwZVNwZWNpZmllciIgOiB7CiAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIzNTQ6MzItMzU0OjQzIiwKICAgICAgICAgICAgICAgICAgIm5hbWUiIDogIntodHRwOi8vaGw3Lm9yZy9maGlyfVNlcXVlbmNlVHlwZSIsCiAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJOYW1lZFR5cGVTcGVjaWZpZXIiCiAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgfSBdCiAgICAgICAgIH0sIHsKICAgICAgICAgICAgImxvY2F0b3IiIDogIjM1NToxLTM1NTo2NSIsCiAgICAgICAgICAgICJuYW1lIiA6ICJUb1N0cmluZyIsCiAgICAgICAgICAgICJjb250ZXh0IiA6ICJQYXRpZW50IiwKICAgICAgICAgICAgImFjY2Vzc0xldmVsIiA6ICJQdWJsaWMiLAogICAgICAgICAgICAidHlwZSIgOiAiRnVuY3Rpb25EZWYiLAogICAgICAgICAgICAiZXhwcmVzc2lvbiIgOiB7CiAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIzNTU6NTUtMzU1OjY1IiwKICAgICAgICAgICAgICAgInBhdGgiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgInR5cGUiIDogIlByb3BlcnR5IiwKICAgICAgICAgICAgICAgInNvdXJjZSIgOiB7CiAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIzNTU6NTUtMzU1OjU5IiwKICAgICAgICAgICAgICAgICAgIm5hbWUiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk9wZXJhbmRSZWYiCiAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgfSwKICAgICAgICAgICAgIm9wZXJhbmQiIDogWyB7CiAgICAgICAgICAgICAgICJuYW1lIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICJvcGVyYW5kVHlwZVNwZWNpZmllciIgOiB7CiAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIzNTU6MzItMzU1OjUxIiwKICAgICAgICAgICAgICAgICAgIm5hbWUiIDogIntodHRwOi8vaGw3Lm9yZy9maGlyfVNlcnZpY2VSZXF1ZXN0SW50ZW50IiwKICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk5hbWVkVHlwZVNwZWNpZmllciIKICAgICAgICAgICAgICAgfQogICAgICAgICAgICB9IF0KICAgICAgICAgfSwgewogICAgICAgICAgICAibG9jYXRvciIgOiAiMzU2OjEtMzU2OjY3IiwKICAgICAgICAgICAgIm5hbWUiIDogIlRvU3RyaW5nIiwKICAgICAgICAgICAgImNvbnRleHQiIDogIlBhdGllbnQiLAogICAgICAgICAgICAiYWNjZXNzTGV2ZWwiIDogIlB1YmxpYyIsCiAgICAgICAgICAgICJ0eXBlIiA6ICJGdW5jdGlvbkRlZiIsCiAgICAgICAgICAgICJleHByZXNzaW9uIiA6IHsKICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjM1Njo1Ny0zNTY6NjciLAogICAgICAgICAgICAgICAicGF0aCIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAidHlwZSIgOiAiUHJvcGVydHkiLAogICAgICAgICAgICAgICAic291cmNlIiA6IHsKICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjM1Njo1Ny0zNTY6NjEiLAogICAgICAgICAgICAgICAgICAibmFtZSIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAgICAidHlwZSIgOiAiT3BlcmFuZFJlZiIKICAgICAgICAgICAgICAgfQogICAgICAgICAgICB9LAogICAgICAgICAgICAib3BlcmFuZCIgOiBbIHsKICAgICAgICAgICAgICAgIm5hbWUiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgIm9wZXJhbmRUeXBlU3BlY2lmaWVyIiA6IHsKICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjM1NjozMi0zNTY6NTMiLAogICAgICAgICAgICAgICAgICAibmFtZSIgOiAie2h0dHA6Ly9obDcub3JnL2ZoaXJ9U2VydmljZVJlcXVlc3RQcmlvcml0eSIsCiAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJOYW1lZFR5cGVTcGVjaWZpZXIiCiAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgfSBdCiAgICAgICAgIH0sIHsKICAgICAgICAgICAgImxvY2F0b3IiIDogIjM1NzoxLTM1Nzo2NSIsCiAgICAgICAgICAgICJuYW1lIiA6ICJUb1N0cmluZyIsCiAgICAgICAgICAgICJjb250ZXh0IiA6ICJQYXRpZW50IiwKICAgICAgICAgICAgImFjY2Vzc0xldmVsIiA6ICJQdWJsaWMiLAogICAgICAgICAgICAidHlwZSIgOiAiRnVuY3Rpb25EZWYiLAogICAgICAgICAgICAiZXhwcmVzc2lvbiIgOiB7CiAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIzNTc6NTUtMzU3OjY1IiwKICAgICAgICAgICAgICAgInBhdGgiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgInR5cGUiIDogIlByb3BlcnR5IiwKICAgICAgICAgICAgICAgInNvdXJjZSIgOiB7CiAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIzNTc6NTUtMzU3OjU5IiwKICAgICAgICAgICAgICAgICAgIm5hbWUiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk9wZXJhbmRSZWYiCiAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgfSwKICAgICAgICAgICAgIm9wZXJhbmQiIDogWyB7CiAgICAgICAgICAgICAgICJuYW1lIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICJvcGVyYW5kVHlwZVNwZWNpZmllciIgOiB7CiAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIzNTc6MzItMzU3OjUxIiwKICAgICAgICAgICAgICAgICAgIm5hbWUiIDogIntodHRwOi8vaGw3Lm9yZy9maGlyfVNlcnZpY2VSZXF1ZXN0U3RhdHVzIiwKICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk5hbWVkVHlwZVNwZWNpZmllciIKICAgICAgICAgICAgICAgfQogICAgICAgICAgICB9IF0KICAgICAgICAgfSwgewogICAgICAgICAgICAibG9jYXRvciIgOiAiMzU4OjEtMzU4OjU3IiwKICAgICAgICAgICAgIm5hbWUiIDogIlRvU3RyaW5nIiwKICAgICAgICAgICAgImNvbnRleHQiIDogIlBhdGllbnQiLAogICAgICAgICAgICAiYWNjZXNzTGV2ZWwiIDogIlB1YmxpYyIsCiAgICAgICAgICAgICJ0eXBlIiA6ICJGdW5jdGlvbkRlZiIsCiAgICAgICAgICAgICJleHByZXNzaW9uIiA6IHsKICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjM1ODo0Ny0zNTg6NTciLAogICAgICAgICAgICAgICAicGF0aCIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAidHlwZSIgOiAiUHJvcGVydHkiLAogICAgICAgICAgICAgICAic291cmNlIiA6IHsKICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjM1ODo0Ny0zNTg6NTEiLAogICAgICAgICAgICAgICAgICAibmFtZSIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAgICAidHlwZSIgOiAiT3BlcmFuZFJlZiIKICAgICAgICAgICAgICAgfQogICAgICAgICAgICB9LAogICAgICAgICAgICAib3BlcmFuZCIgOiBbIHsKICAgICAgICAgICAgICAgIm5hbWUiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgIm9wZXJhbmRUeXBlU3BlY2lmaWVyIiA6IHsKICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjM1ODozMi0zNTg6NDMiLAogICAgICAgICAgICAgICAgICAibmFtZSIgOiAie2h0dHA6Ly9obDcub3JnL2ZoaXJ9U2xpY2luZ1J1bGVzIiwKICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk5hbWVkVHlwZVNwZWNpZmllciIKICAgICAgICAgICAgICAgfQogICAgICAgICAgICB9IF0KICAgICAgICAgfSwgewogICAgICAgICAgICAibG9jYXRvciIgOiAiMzU5OjEtMzU5OjU1IiwKICAgICAgICAgICAgIm5hbWUiIDogIlRvU3RyaW5nIiwKICAgICAgICAgICAgImNvbnRleHQiIDogIlBhdGllbnQiLAogICAgICAgICAgICAiYWNjZXNzTGV2ZWwiIDogIlB1YmxpYyIsCiAgICAgICAgICAgICJ0eXBlIiA6ICJGdW5jdGlvbkRlZiIsCiAgICAgICAgICAgICJleHByZXNzaW9uIiA6IHsKICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjM1OTo0NS0zNTk6NTUiLAogICAgICAgICAgICAgICAicGF0aCIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAidHlwZSIgOiAiUHJvcGVydHkiLAogICAgICAgICAgICAgICAic291cmNlIiA6IHsKICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjM1OTo0NS0zNTk6NDkiLAogICAgICAgICAgICAgICAgICAibmFtZSIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAgICAidHlwZSIgOiAiT3BlcmFuZFJlZiIKICAgICAgICAgICAgICAgfQogICAgICAgICAgICB9LAogICAgICAgICAgICAib3BlcmFuZCIgOiBbIHsKICAgICAgICAgICAgICAgIm5hbWUiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgIm9wZXJhbmRUeXBlU3BlY2lmaWVyIiA6IHsKICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjM1OTozMi0zNTk6NDEiLAogICAgICAgICAgICAgICAgICAibmFtZSIgOiAie2h0dHA6Ly9obDcub3JnL2ZoaXJ9U2xvdFN0YXR1cyIsCiAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJOYW1lZFR5cGVTcGVjaWZpZXIiCiAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgfSBdCiAgICAgICAgIH0sIHsKICAgICAgICAgICAgImxvY2F0b3IiIDogIjM2MDoxLTM2MDo1OCIsCiAgICAgICAgICAgICJuYW1lIiA6ICJUb1N0cmluZyIsCiAgICAgICAgICAgICJjb250ZXh0IiA6ICJQYXRpZW50IiwKICAgICAgICAgICAgImFjY2Vzc0xldmVsIiA6ICJQdWJsaWMiLAogICAgICAgICAgICAidHlwZSIgOiAiRnVuY3Rpb25EZWYiLAogICAgICAgICAgICAiZXhwcmVzc2lvbiIgOiB7CiAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIzNjA6NDgtMzYwOjU4IiwKICAgICAgICAgICAgICAgInBhdGgiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgInR5cGUiIDogIlByb3BlcnR5IiwKICAgICAgICAgICAgICAgInNvdXJjZSIgOiB7CiAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIzNjA6NDgtMzYwOjUyIiwKICAgICAgICAgICAgICAgICAgIm5hbWUiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk9wZXJhbmRSZWYiCiAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgfSwKICAgICAgICAgICAgIm9wZXJhbmQiIDogWyB7CiAgICAgICAgICAgICAgICJuYW1lIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICJvcGVyYW5kVHlwZVNwZWNpZmllciIgOiB7CiAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIzNjA6MzItMzYwOjQ0IiwKICAgICAgICAgICAgICAgICAgIm5hbWUiIDogIntodHRwOi8vaGw3Lm9yZy9maGlyfVNvcnREaXJlY3Rpb24iLAogICAgICAgICAgICAgICAgICAidHlwZSIgOiAiTmFtZWRUeXBlU3BlY2lmaWVyIgogICAgICAgICAgICAgICB9CiAgICAgICAgICAgIH0gXQogICAgICAgICB9LCB7CiAgICAgICAgICAgICJsb2NhdG9yIiA6ICIzNjE6MS0zNjE6NzIiLAogICAgICAgICAgICAibmFtZSIgOiAiVG9TdHJpbmciLAogICAgICAgICAgICAiY29udGV4dCIgOiAiUGF0aWVudCIsCiAgICAgICAgICAgICJhY2Nlc3NMZXZlbCIgOiAiUHVibGljIiwKICAgICAgICAgICAgInR5cGUiIDogIkZ1bmN0aW9uRGVmIiwKICAgICAgICAgICAgImV4cHJlc3Npb24iIDogewogICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMzYxOjYyLTM2MTo3MiIsCiAgICAgICAgICAgICAgICJwYXRoIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICJ0eXBlIiA6ICJQcm9wZXJ0eSIsCiAgICAgICAgICAgICAgICJzb3VyY2UiIDogewogICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMzYxOjYyLTM2MTo2NiIsCiAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJPcGVyYW5kUmVmIgogICAgICAgICAgICAgICB9CiAgICAgICAgICAgIH0sCiAgICAgICAgICAgICJvcGVyYW5kIiA6IFsgewogICAgICAgICAgICAgICAibmFtZSIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAib3BlcmFuZFR5cGVTcGVjaWZpZXIiIDogewogICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMzYxOjMyLTM2MTo1OCIsCiAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJ7aHR0cDovL2hsNy5vcmcvZmhpcn1TcGVjaW1lbkNvbnRhaW5lZFByZWZlcmVuY2UiLAogICAgICAgICAgICAgICAgICAidHlwZSIgOiAiTmFtZWRUeXBlU3BlY2lmaWVyIgogICAgICAgICAgICAgICB9CiAgICAgICAgICAgIH0gXQogICAgICAgICB9LCB7CiAgICAgICAgICAgICJsb2NhdG9yIiA6ICIzNjI6MS0zNjI6NTkiLAogICAgICAgICAgICAibmFtZSIgOiAiVG9TdHJpbmciLAogICAgICAgICAgICAiY29udGV4dCIgOiAiUGF0aWVudCIsCiAgICAgICAgICAgICJhY2Nlc3NMZXZlbCIgOiAiUHVibGljIiwKICAgICAgICAgICAgInR5cGUiIDogIkZ1bmN0aW9uRGVmIiwKICAgICAgICAgICAgImV4cHJlc3Npb24iIDogewogICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMzYyOjQ5LTM2Mjo1OSIsCiAgICAgICAgICAgICAgICJwYXRoIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICJ0eXBlIiA6ICJQcm9wZXJ0eSIsCiAgICAgICAgICAgICAgICJzb3VyY2UiIDogewogICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMzYyOjQ5LTM2Mjo1MyIsCiAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJPcGVyYW5kUmVmIgogICAgICAgICAgICAgICB9CiAgICAgICAgICAgIH0sCiAgICAgICAgICAgICJvcGVyYW5kIiA6IFsgewogICAgICAgICAgICAgICAibmFtZSIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAib3BlcmFuZFR5cGVTcGVjaWZpZXIiIDogewogICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMzYyOjMyLTM2Mjo0NSIsCiAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJ7aHR0cDovL2hsNy5vcmcvZmhpcn1TcGVjaW1lblN0YXR1cyIsCiAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJOYW1lZFR5cGVTcGVjaWZpZXIiCiAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgfSBdCiAgICAgICAgIH0sIHsKICAgICAgICAgICAgImxvY2F0b3IiIDogIjM2MzoxLTM2Mzo1MSIsCiAgICAgICAgICAgICJuYW1lIiA6ICJUb1N0cmluZyIsCiAgICAgICAgICAgICJjb250ZXh0IiA6ICJQYXRpZW50IiwKICAgICAgICAgICAgImFjY2Vzc0xldmVsIiA6ICJQdWJsaWMiLAogICAgICAgICAgICAidHlwZSIgOiAiRnVuY3Rpb25EZWYiLAogICAgICAgICAgICAiZXhwcmVzc2lvbiIgOiB7CiAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIzNjM6NDEtMzYzOjUxIiwKICAgICAgICAgICAgICAgInBhdGgiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgInR5cGUiIDogIlByb3BlcnR5IiwKICAgICAgICAgICAgICAgInNvdXJjZSIgOiB7CiAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIzNjM6NDEtMzYzOjQ1IiwKICAgICAgICAgICAgICAgICAgIm5hbWUiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk9wZXJhbmRSZWYiCiAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgfSwKICAgICAgICAgICAgIm9wZXJhbmQiIDogWyB7CiAgICAgICAgICAgICAgICJuYW1lIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICJvcGVyYW5kVHlwZVNwZWNpZmllciIgOiB7CiAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIzNjM6MzItMzYzOjM3IiwKICAgICAgICAgICAgICAgICAgIm5hbWUiIDogIntodHRwOi8vaGw3Lm9yZy9maGlyfVN0YXR1cyIsCiAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJOYW1lZFR5cGVTcGVjaWZpZXIiCiAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgfSBdCiAgICAgICAgIH0sIHsKICAgICAgICAgICAgImxvY2F0b3IiIDogIjM2NDoxLTM2NDo1NSIsCiAgICAgICAgICAgICJuYW1lIiA6ICJUb1N0cmluZyIsCiAgICAgICAgICAgICJjb250ZXh0IiA6ICJQYXRpZW50IiwKICAgICAgICAgICAgImFjY2Vzc0xldmVsIiA6ICJQdWJsaWMiLAogICAgICAgICAgICAidHlwZSIgOiAiRnVuY3Rpb25EZWYiLAogICAgICAgICAgICAiZXhwcmVzc2lvbiIgOiB7CiAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIzNjQ6NDUtMzY0OjU1IiwKICAgICAgICAgICAgICAgInBhdGgiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgInR5cGUiIDogIlByb3BlcnR5IiwKICAgICAgICAgICAgICAgInNvdXJjZSIgOiB7CiAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIzNjQ6NDUtMzY0OjQ5IiwKICAgICAgICAgICAgICAgICAgIm5hbWUiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk9wZXJhbmRSZWYiCiAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgfSwKICAgICAgICAgICAgIm9wZXJhbmQiIDogWyB7CiAgICAgICAgICAgICAgICJuYW1lIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICJvcGVyYW5kVHlwZVNwZWNpZmllciIgOiB7CiAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIzNjQ6MzItMzY0OjQxIiwKICAgICAgICAgICAgICAgICAgIm5hbWUiIDogIntodHRwOi8vaGw3Lm9yZy9maGlyfVN0cmFuZFR5cGUiLAogICAgICAgICAgICAgICAgICAidHlwZSIgOiAiTmFtZWRUeXBlU3BlY2lmaWVyIgogICAgICAgICAgICAgICB9CiAgICAgICAgICAgIH0gXQogICAgICAgICB9LCB7CiAgICAgICAgICAgICJsb2NhdG9yIiA6ICIzNjU6MS0zNjU6NjgiLAogICAgICAgICAgICAibmFtZSIgOiAiVG9TdHJpbmciLAogICAgICAgICAgICAiY29udGV4dCIgOiAiUGF0aWVudCIsCiAgICAgICAgICAgICJhY2Nlc3NMZXZlbCIgOiAiUHVibGljIiwKICAgICAgICAgICAgInR5cGUiIDogIkZ1bmN0aW9uRGVmIiwKICAgICAgICAgICAgImV4cHJlc3Npb24iIDogewogICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMzY1OjU4LTM2NTo2OCIsCiAgICAgICAgICAgICAgICJwYXRoIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICJ0eXBlIiA6ICJQcm9wZXJ0eSIsCiAgICAgICAgICAgICAgICJzb3VyY2UiIDogewogICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMzY1OjU4LTM2NTo2MiIsCiAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJPcGVyYW5kUmVmIgogICAgICAgICAgICAgICB9CiAgICAgICAgICAgIH0sCiAgICAgICAgICAgICJvcGVyYW5kIiA6IFsgewogICAgICAgICAgICAgICAibmFtZSIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAib3BlcmFuZFR5cGVTcGVjaWZpZXIiIDogewogICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMzY1OjMyLTM2NTo1NCIsCiAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJ7aHR0cDovL2hsNy5vcmcvZmhpcn1TdHJ1Y3R1cmVEZWZpbml0aW9uS2luZCIsCiAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJOYW1lZFR5cGVTcGVjaWZpZXIiCiAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgfSBdCiAgICAgICAgIH0sIHsKICAgICAgICAgICAgImxvY2F0b3IiIDogIjM2NjoxLTM2Njo2OCIsCiAgICAgICAgICAgICJuYW1lIiA6ICJUb1N0cmluZyIsCiAgICAgICAgICAgICJjb250ZXh0IiA6ICJQYXRpZW50IiwKICAgICAgICAgICAgImFjY2Vzc0xldmVsIiA6ICJQdWJsaWMiLAogICAgICAgICAgICAidHlwZSIgOiAiRnVuY3Rpb25EZWYiLAogICAgICAgICAgICAiZXhwcmVzc2lvbiIgOiB7CiAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIzNjY6NTgtMzY2OjY4IiwKICAgICAgICAgICAgICAgInBhdGgiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgInR5cGUiIDogIlByb3BlcnR5IiwKICAgICAgICAgICAgICAgInNvdXJjZSIgOiB7CiAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIzNjY6NTgtMzY2OjYyIiwKICAgICAgICAgICAgICAgICAgIm5hbWUiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk9wZXJhbmRSZWYiCiAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgfSwKICAgICAgICAgICAgIm9wZXJhbmQiIDogWyB7CiAgICAgICAgICAgICAgICJuYW1lIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICJvcGVyYW5kVHlwZVNwZWNpZmllciIgOiB7CiAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIzNjY6MzItMzY2OjU0IiwKICAgICAgICAgICAgICAgICAgIm5hbWUiIDogIntodHRwOi8vaGw3Lm9yZy9maGlyfVN0cnVjdHVyZU1hcENvbnRleHRUeXBlIiwKICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk5hbWVkVHlwZVNwZWNpZmllciIKICAgICAgICAgICAgICAgfQogICAgICAgICAgICB9IF0KICAgICAgICAgfSwgewogICAgICAgICAgICAibG9jYXRvciIgOiAiMzY3OjEtMzY3OjcwIiwKICAgICAgICAgICAgIm5hbWUiIDogIlRvU3RyaW5nIiwKICAgICAgICAgICAgImNvbnRleHQiIDogIlBhdGllbnQiLAogICAgICAgICAgICAiYWNjZXNzTGV2ZWwiIDogIlB1YmxpYyIsCiAgICAgICAgICAgICJ0eXBlIiA6ICJGdW5jdGlvbkRlZiIsCiAgICAgICAgICAgICJleHByZXNzaW9uIiA6IHsKICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjM2Nzo2MC0zNjc6NzAiLAogICAgICAgICAgICAgICAicGF0aCIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAidHlwZSIgOiAiUHJvcGVydHkiLAogICAgICAgICAgICAgICAic291cmNlIiA6IHsKICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjM2Nzo2MC0zNjc6NjQiLAogICAgICAgICAgICAgICAgICAibmFtZSIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAgICAidHlwZSIgOiAiT3BlcmFuZFJlZiIKICAgICAgICAgICAgICAgfQogICAgICAgICAgICB9LAogICAgICAgICAgICAib3BlcmFuZCIgOiBbIHsKICAgICAgICAgICAgICAgIm5hbWUiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgIm9wZXJhbmRUeXBlU3BlY2lmaWVyIiA6IHsKICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjM2NzozMi0zNjc6NTYiLAogICAgICAgICAgICAgICAgICAibmFtZSIgOiAie2h0dHA6Ly9obDcub3JnL2ZoaXJ9U3RydWN0dXJlTWFwR3JvdXBUeXBlTW9kZSIsCiAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJOYW1lZFR5cGVTcGVjaWZpZXIiCiAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgfSBdCiAgICAgICAgIH0sIHsKICAgICAgICAgICAgImxvY2F0b3IiIDogIjM2ODoxLTM2ODo2NiIsCiAgICAgICAgICAgICJuYW1lIiA6ICJUb1N0cmluZyIsCiAgICAgICAgICAgICJjb250ZXh0IiA6ICJQYXRpZW50IiwKICAgICAgICAgICAgImFjY2Vzc0xldmVsIiA6ICJQdWJsaWMiLAogICAgICAgICAgICAidHlwZSIgOiAiRnVuY3Rpb25EZWYiLAogICAgICAgICAgICAiZXhwcmVzc2lvbiIgOiB7CiAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIzNjg6NTYtMzY4OjY2IiwKICAgICAgICAgICAgICAgInBhdGgiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgInR5cGUiIDogIlByb3BlcnR5IiwKICAgICAgICAgICAgICAgInNvdXJjZSIgOiB7CiAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIzNjg6NTYtMzY4OjYwIiwKICAgICAgICAgICAgICAgICAgIm5hbWUiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk9wZXJhbmRSZWYiCiAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgfSwKICAgICAgICAgICAgIm9wZXJhbmQiIDogWyB7CiAgICAgICAgICAgICAgICJuYW1lIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICJvcGVyYW5kVHlwZVNwZWNpZmllciIgOiB7CiAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIzNjg6MzItMzY4OjUyIiwKICAgICAgICAgICAgICAgICAgIm5hbWUiIDogIntodHRwOi8vaGw3Lm9yZy9maGlyfVN0cnVjdHVyZU1hcElucHV0TW9kZSIsCiAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJOYW1lZFR5cGVTcGVjaWZpZXIiCiAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgfSBdCiAgICAgICAgIH0sIHsKICAgICAgICAgICAgImxvY2F0b3IiIDogIjM2OToxLTM2OTo2NiIsCiAgICAgICAgICAgICJuYW1lIiA6ICJUb1N0cmluZyIsCiAgICAgICAgICAgICJjb250ZXh0IiA6ICJQYXRpZW50IiwKICAgICAgICAgICAgImFjY2Vzc0xldmVsIiA6ICJQdWJsaWMiLAogICAgICAgICAgICAidHlwZSIgOiAiRnVuY3Rpb25EZWYiLAogICAgICAgICAgICAiZXhwcmVzc2lvbiIgOiB7CiAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIzNjk6NTYtMzY5OjY2IiwKICAgICAgICAgICAgICAgInBhdGgiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgInR5cGUiIDogIlByb3BlcnR5IiwKICAgICAgICAgICAgICAgInNvdXJjZSIgOiB7CiAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIzNjk6NTYtMzY5OjYwIiwKICAgICAgICAgICAgICAgICAgIm5hbWUiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk9wZXJhbmRSZWYiCiAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgfSwKICAgICAgICAgICAgIm9wZXJhbmQiIDogWyB7CiAgICAgICAgICAgICAgICJuYW1lIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICJvcGVyYW5kVHlwZVNwZWNpZmllciIgOiB7CiAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIzNjk6MzItMzY5OjUyIiwKICAgICAgICAgICAgICAgICAgIm5hbWUiIDogIntodHRwOi8vaGw3Lm9yZy9maGlyfVN0cnVjdHVyZU1hcE1vZGVsTW9kZSIsCiAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJOYW1lZFR5cGVTcGVjaWZpZXIiCiAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgfSBdCiAgICAgICAgIH0sIHsKICAgICAgICAgICAgImxvY2F0b3IiIDogIjM3MDoxLTM3MDo3MSIsCiAgICAgICAgICAgICJuYW1lIiA6ICJUb1N0cmluZyIsCiAgICAgICAgICAgICJjb250ZXh0IiA6ICJQYXRpZW50IiwKICAgICAgICAgICAgImFjY2Vzc0xldmVsIiA6ICJQdWJsaWMiLAogICAgICAgICAgICAidHlwZSIgOiAiRnVuY3Rpb25EZWYiLAogICAgICAgICAgICAiZXhwcmVzc2lvbiIgOiB7CiAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIzNzA6NjEtMzcwOjcxIiwKICAgICAgICAgICAgICAgInBhdGgiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgInR5cGUiIDogIlByb3BlcnR5IiwKICAgICAgICAgICAgICAgInNvdXJjZSIgOiB7CiAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIzNzA6NjEtMzcwOjY1IiwKICAgICAgICAgICAgICAgICAgIm5hbWUiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk9wZXJhbmRSZWYiCiAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgfSwKICAgICAgICAgICAgIm9wZXJhbmQiIDogWyB7CiAgICAgICAgICAgICAgICJuYW1lIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICJvcGVyYW5kVHlwZVNwZWNpZmllciIgOiB7CiAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIzNzA6MzItMzcwOjU3IiwKICAgICAgICAgICAgICAgICAgIm5hbWUiIDogIntodHRwOi8vaGw3Lm9yZy9maGlyfVN0cnVjdHVyZU1hcFNvdXJjZUxpc3RNb2RlIiwKICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk5hbWVkVHlwZVNwZWNpZmllciIKICAgICAgICAgICAgICAgfQogICAgICAgICAgICB9IF0KICAgICAgICAgfSwgewogICAgICAgICAgICAibG9jYXRvciIgOiAiMzcxOjEtMzcxOjcxIiwKICAgICAgICAgICAgIm5hbWUiIDogIlRvU3RyaW5nIiwKICAgICAgICAgICAgImNvbnRleHQiIDogIlBhdGllbnQiLAogICAgICAgICAgICAiYWNjZXNzTGV2ZWwiIDogIlB1YmxpYyIsCiAgICAgICAgICAgICJ0eXBlIiA6ICJGdW5jdGlvbkRlZiIsCiAgICAgICAgICAgICJleHByZXNzaW9uIiA6IHsKICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjM3MTo2MS0zNzE6NzEiLAogICAgICAgICAgICAgICAicGF0aCIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAidHlwZSIgOiAiUHJvcGVydHkiLAogICAgICAgICAgICAgICAic291cmNlIiA6IHsKICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjM3MTo2MS0zNzE6NjUiLAogICAgICAgICAgICAgICAgICAibmFtZSIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAgICAidHlwZSIgOiAiT3BlcmFuZFJlZiIKICAgICAgICAgICAgICAgfQogICAgICAgICAgICB9LAogICAgICAgICAgICAib3BlcmFuZCIgOiBbIHsKICAgICAgICAgICAgICAgIm5hbWUiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgIm9wZXJhbmRUeXBlU3BlY2lmaWVyIiA6IHsKICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjM3MTozMi0zNzE6NTciLAogICAgICAgICAgICAgICAgICAibmFtZSIgOiAie2h0dHA6Ly9obDcub3JnL2ZoaXJ9U3RydWN0dXJlTWFwVGFyZ2V0TGlzdE1vZGUiLAogICAgICAgICAgICAgICAgICAidHlwZSIgOiAiTmFtZWRUeXBlU3BlY2lmaWVyIgogICAgICAgICAgICAgICB9CiAgICAgICAgICAgIH0gXQogICAgICAgICB9LCB7CiAgICAgICAgICAgICJsb2NhdG9yIiA6ICIzNzI6MS0zNzI6NjYiLAogICAgICAgICAgICAibmFtZSIgOiAiVG9TdHJpbmciLAogICAgICAgICAgICAiY29udGV4dCIgOiAiUGF0aWVudCIsCiAgICAgICAgICAgICJhY2Nlc3NMZXZlbCIgOiAiUHVibGljIiwKICAgICAgICAgICAgInR5cGUiIDogIkZ1bmN0aW9uRGVmIiwKICAgICAgICAgICAgImV4cHJlc3Npb24iIDogewogICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMzcyOjU2LTM3Mjo2NiIsCiAgICAgICAgICAgICAgICJwYXRoIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICJ0eXBlIiA6ICJQcm9wZXJ0eSIsCiAgICAgICAgICAgICAgICJzb3VyY2UiIDogewogICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMzcyOjU2LTM3Mjo2MCIsCiAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJPcGVyYW5kUmVmIgogICAgICAgICAgICAgICB9CiAgICAgICAgICAgIH0sCiAgICAgICAgICAgICJvcGVyYW5kIiA6IFsgewogICAgICAgICAgICAgICAibmFtZSIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAib3BlcmFuZFR5cGVTcGVjaWZpZXIiIDogewogICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMzcyOjMyLTM3Mjo1MiIsCiAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJ7aHR0cDovL2hsNy5vcmcvZmhpcn1TdHJ1Y3R1cmVNYXBUcmFuc2Zvcm0iLAogICAgICAgICAgICAgICAgICAidHlwZSIgOiAiTmFtZWRUeXBlU3BlY2lmaWVyIgogICAgICAgICAgICAgICB9CiAgICAgICAgICAgIH0gXQogICAgICAgICB9LCB7CiAgICAgICAgICAgICJsb2NhdG9yIiA6ICIzNzM6MS0zNzM6NjgiLAogICAgICAgICAgICAibmFtZSIgOiAiVG9TdHJpbmciLAogICAgICAgICAgICAiY29udGV4dCIgOiAiUGF0aWVudCIsCiAgICAgICAgICAgICJhY2Nlc3NMZXZlbCIgOiAiUHVibGljIiwKICAgICAgICAgICAgInR5cGUiIDogIkZ1bmN0aW9uRGVmIiwKICAgICAgICAgICAgImV4cHJlc3Npb24iIDogewogICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMzczOjU4LTM3Mzo2OCIsCiAgICAgICAgICAgICAgICJwYXRoIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICJ0eXBlIiA6ICJQcm9wZXJ0eSIsCiAgICAgICAgICAgICAgICJzb3VyY2UiIDogewogICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMzczOjU4LTM3Mzo2MiIsCiAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJPcGVyYW5kUmVmIgogICAgICAgICAgICAgICB9CiAgICAgICAgICAgIH0sCiAgICAgICAgICAgICJvcGVyYW5kIiA6IFsgewogICAgICAgICAgICAgICAibmFtZSIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAib3BlcmFuZFR5cGVTcGVjaWZpZXIiIDogewogICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMzczOjMyLTM3Mzo1NCIsCiAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJ7aHR0cDovL2hsNy5vcmcvZmhpcn1TdWJzY3JpcHRpb25DaGFubmVsVHlwZSIsCiAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJOYW1lZFR5cGVTcGVjaWZpZXIiCiAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgfSBdCiAgICAgICAgIH0sIHsKICAgICAgICAgICAgImxvY2F0b3IiIDogIjM3NDoxLTM3NDo2MyIsCiAgICAgICAgICAgICJuYW1lIiA6ICJUb1N0cmluZyIsCiAgICAgICAgICAgICJjb250ZXh0IiA6ICJQYXRpZW50IiwKICAgICAgICAgICAgImFjY2Vzc0xldmVsIiA6ICJQdWJsaWMiLAogICAgICAgICAgICAidHlwZSIgOiAiRnVuY3Rpb25EZWYiLAogICAgICAgICAgICAiZXhwcmVzc2lvbiIgOiB7CiAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIzNzQ6NTMtMzc0OjYzIiwKICAgICAgICAgICAgICAgInBhdGgiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgInR5cGUiIDogIlByb3BlcnR5IiwKICAgICAgICAgICAgICAgInNvdXJjZSIgOiB7CiAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIzNzQ6NTMtMzc0OjU3IiwKICAgICAgICAgICAgICAgICAgIm5hbWUiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk9wZXJhbmRSZWYiCiAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgfSwKICAgICAgICAgICAgIm9wZXJhbmQiIDogWyB7CiAgICAgICAgICAgICAgICJuYW1lIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICJvcGVyYW5kVHlwZVNwZWNpZmllciIgOiB7CiAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIzNzQ6MzItMzc0OjQ5IiwKICAgICAgICAgICAgICAgICAgIm5hbWUiIDogIntodHRwOi8vaGw3Lm9yZy9maGlyfVN1YnNjcmlwdGlvblN0YXR1cyIsCiAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJOYW1lZFR5cGVTcGVjaWZpZXIiCiAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgfSBdCiAgICAgICAgIH0sIHsKICAgICAgICAgICAgImxvY2F0b3IiIDogIjM3NToxLTM3NTo2NSIsCiAgICAgICAgICAgICJuYW1lIiA6ICJUb1N0cmluZyIsCiAgICAgICAgICAgICJjb250ZXh0IiA6ICJQYXRpZW50IiwKICAgICAgICAgICAgImFjY2Vzc0xldmVsIiA6ICJQdWJsaWMiLAogICAgICAgICAgICAidHlwZSIgOiAiRnVuY3Rpb25EZWYiLAogICAgICAgICAgICAiZXhwcmVzc2lvbiIgOiB7CiAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIzNzU6NTUtMzc1OjY1IiwKICAgICAgICAgICAgICAgInBhdGgiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgInR5cGUiIDogIlByb3BlcnR5IiwKICAgICAgICAgICAgICAgInNvdXJjZSIgOiB7CiAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIzNzU6NTUtMzc1OjU5IiwKICAgICAgICAgICAgICAgICAgIm5hbWUiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk9wZXJhbmRSZWYiCiAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgfSwKICAgICAgICAgICAgIm9wZXJhbmQiIDogWyB7CiAgICAgICAgICAgICAgICJuYW1lIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICJvcGVyYW5kVHlwZVNwZWNpZmllciIgOiB7CiAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIzNzU6MzItMzc1OjUxIiwKICAgICAgICAgICAgICAgICAgIm5hbWUiIDogIntodHRwOi8vaGw3Lm9yZy9maGlyfVN1cHBseURlbGl2ZXJ5U3RhdHVzIiwKICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk5hbWVkVHlwZVNwZWNpZmllciIKICAgICAgICAgICAgICAgfQogICAgICAgICAgICB9IF0KICAgICAgICAgfSwgewogICAgICAgICAgICAibG9jYXRvciIgOiAiMzc2OjEtMzc2OjY0IiwKICAgICAgICAgICAgIm5hbWUiIDogIlRvU3RyaW5nIiwKICAgICAgICAgICAgImNvbnRleHQiIDogIlBhdGllbnQiLAogICAgICAgICAgICAiYWNjZXNzTGV2ZWwiIDogIlB1YmxpYyIsCiAgICAgICAgICAgICJ0eXBlIiA6ICJGdW5jdGlvbkRlZiIsCiAgICAgICAgICAgICJleHByZXNzaW9uIiA6IHsKICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjM3Njo1NC0zNzY6NjQiLAogICAgICAgICAgICAgICAicGF0aCIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAidHlwZSIgOiAiUHJvcGVydHkiLAogICAgICAgICAgICAgICAic291cmNlIiA6IHsKICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjM3Njo1NC0zNzY6NTgiLAogICAgICAgICAgICAgICAgICAibmFtZSIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAgICAidHlwZSIgOiAiT3BlcmFuZFJlZiIKICAgICAgICAgICAgICAgfQogICAgICAgICAgICB9LAogICAgICAgICAgICAib3BlcmFuZCIgOiBbIHsKICAgICAgICAgICAgICAgIm5hbWUiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgIm9wZXJhbmRUeXBlU3BlY2lmaWVyIiA6IHsKICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjM3NjozMi0zNzY6NTAiLAogICAgICAgICAgICAgICAgICAibmFtZSIgOiAie2h0dHA6Ly9obDcub3JnL2ZoaXJ9U3VwcGx5UmVxdWVzdFN0YXR1cyIsCiAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJOYW1lZFR5cGVTcGVjaWZpZXIiCiAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgfSBdCiAgICAgICAgIH0sIHsKICAgICAgICAgICAgImxvY2F0b3IiIDogIjM3NzoxLTM3Nzo2OSIsCiAgICAgICAgICAgICJuYW1lIiA6ICJUb1N0cmluZyIsCiAgICAgICAgICAgICJjb250ZXh0IiA6ICJQYXRpZW50IiwKICAgICAgICAgICAgImFjY2Vzc0xldmVsIiA6ICJQdWJsaWMiLAogICAgICAgICAgICAidHlwZSIgOiAiRnVuY3Rpb25EZWYiLAogICAgICAgICAgICAiZXhwcmVzc2lvbiIgOiB7CiAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIzNzc6NTktMzc3OjY5IiwKICAgICAgICAgICAgICAgInBhdGgiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgInR5cGUiIDogIlByb3BlcnR5IiwKICAgICAgICAgICAgICAgInNvdXJjZSIgOiB7CiAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIzNzc6NTktMzc3OjYzIiwKICAgICAgICAgICAgICAgICAgIm5hbWUiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk9wZXJhbmRSZWYiCiAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgfSwKICAgICAgICAgICAgIm9wZXJhbmQiIDogWyB7CiAgICAgICAgICAgICAgICJuYW1lIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICJvcGVyYW5kVHlwZVNwZWNpZmllciIgOiB7CiAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIzNzc6MzItMzc3OjU1IiwKICAgICAgICAgICAgICAgICAgIm5hbWUiIDogIntodHRwOi8vaGw3Lm9yZy9maGlyfVN5c3RlbVJlc3RmdWxJbnRlcmFjdGlvbiIsCiAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJOYW1lZFR5cGVTcGVjaWZpZXIiCiAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgfSBdCiAgICAgICAgIH0sIHsKICAgICAgICAgICAgImxvY2F0b3IiIDogIjM3ODoxLTM3ODo1NSIsCiAgICAgICAgICAgICJuYW1lIiA6ICJUb1N0cmluZyIsCiAgICAgICAgICAgICJjb250ZXh0IiA6ICJQYXRpZW50IiwKICAgICAgICAgICAgImFjY2Vzc0xldmVsIiA6ICJQdWJsaWMiLAogICAgICAgICAgICAidHlwZSIgOiAiRnVuY3Rpb25EZWYiLAogICAgICAgICAgICAiZXhwcmVzc2lvbiIgOiB7CiAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIzNzg6NDUtMzc4OjU1IiwKICAgICAgICAgICAgICAgInBhdGgiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgInR5cGUiIDogIlByb3BlcnR5IiwKICAgICAgICAgICAgICAgInNvdXJjZSIgOiB7CiAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIzNzg6NDUtMzc4OjQ5IiwKICAgICAgICAgICAgICAgICAgIm5hbWUiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk9wZXJhbmRSZWYiCiAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgfSwKICAgICAgICAgICAgIm9wZXJhbmQiIDogWyB7CiAgICAgICAgICAgICAgICJuYW1lIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICJvcGVyYW5kVHlwZVNwZWNpZmllciIgOiB7CiAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIzNzg6MzItMzc4OjQxIiwKICAgICAgICAgICAgICAgICAgIm5hbWUiIDogIntodHRwOi8vaGw3Lm9yZy9maGlyfVRhc2tJbnRlbnQiLAogICAgICAgICAgICAgICAgICAidHlwZSIgOiAiTmFtZWRUeXBlU3BlY2lmaWVyIgogICAgICAgICAgICAgICB9CiAgICAgICAgICAgIH0gXQogICAgICAgICB9LCB7CiAgICAgICAgICAgICJsb2NhdG9yIiA6ICIzNzk6MS0zNzk6NTciLAogICAgICAgICAgICAibmFtZSIgOiAiVG9TdHJpbmciLAogICAgICAgICAgICAiY29udGV4dCIgOiAiUGF0aWVudCIsCiAgICAgICAgICAgICJhY2Nlc3NMZXZlbCIgOiAiUHVibGljIiwKICAgICAgICAgICAgInR5cGUiIDogIkZ1bmN0aW9uRGVmIiwKICAgICAgICAgICAgImV4cHJlc3Npb24iIDogewogICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMzc5OjQ3LTM3OTo1NyIsCiAgICAgICAgICAgICAgICJwYXRoIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICJ0eXBlIiA6ICJQcm9wZXJ0eSIsCiAgICAgICAgICAgICAgICJzb3VyY2UiIDogewogICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMzc5OjQ3LTM3OTo1MSIsCiAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJPcGVyYW5kUmVmIgogICAgICAgICAgICAgICB9CiAgICAgICAgICAgIH0sCiAgICAgICAgICAgICJvcGVyYW5kIiA6IFsgewogICAgICAgICAgICAgICAibmFtZSIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAib3BlcmFuZFR5cGVTcGVjaWZpZXIiIDogewogICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMzc5OjMyLTM3OTo0MyIsCiAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJ7aHR0cDovL2hsNy5vcmcvZmhpcn1UYXNrUHJpb3JpdHkiLAogICAgICAgICAgICAgICAgICAidHlwZSIgOiAiTmFtZWRUeXBlU3BlY2lmaWVyIgogICAgICAgICAgICAgICB9CiAgICAgICAgICAgIH0gXQogICAgICAgICB9LCB7CiAgICAgICAgICAgICJsb2NhdG9yIiA6ICIzODA6MS0zODA6NTUiLAogICAgICAgICAgICAibmFtZSIgOiAiVG9TdHJpbmciLAogICAgICAgICAgICAiY29udGV4dCIgOiAiUGF0aWVudCIsCiAgICAgICAgICAgICJhY2Nlc3NMZXZlbCIgOiAiUHVibGljIiwKICAgICAgICAgICAgInR5cGUiIDogIkZ1bmN0aW9uRGVmIiwKICAgICAgICAgICAgImV4cHJlc3Npb24iIDogewogICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMzgwOjQ1LTM4MDo1NSIsCiAgICAgICAgICAgICAgICJwYXRoIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICJ0eXBlIiA6ICJQcm9wZXJ0eSIsCiAgICAgICAgICAgICAgICJzb3VyY2UiIDogewogICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMzgwOjQ1LTM4MDo0OSIsCiAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJPcGVyYW5kUmVmIgogICAgICAgICAgICAgICB9CiAgICAgICAgICAgIH0sCiAgICAgICAgICAgICJvcGVyYW5kIiA6IFsgewogICAgICAgICAgICAgICAibmFtZSIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAib3BlcmFuZFR5cGVTcGVjaWZpZXIiIDogewogICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMzgwOjMyLTM4MDo0MSIsCiAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJ7aHR0cDovL2hsNy5vcmcvZmhpcn1UYXNrU3RhdHVzIiwKICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk5hbWVkVHlwZVNwZWNpZmllciIKICAgICAgICAgICAgICAgfQogICAgICAgICAgICB9IF0KICAgICAgICAgfSwgewogICAgICAgICAgICAibG9jYXRvciIgOiAiMzgxOjEtMzgxOjY3IiwKICAgICAgICAgICAgIm5hbWUiIDogIlRvU3RyaW5nIiwKICAgICAgICAgICAgImNvbnRleHQiIDogIlBhdGllbnQiLAogICAgICAgICAgICAiYWNjZXNzTGV2ZWwiIDogIlB1YmxpYyIsCiAgICAgICAgICAgICJ0eXBlIiA6ICJGdW5jdGlvbkRlZiIsCiAgICAgICAgICAgICJleHByZXNzaW9uIiA6IHsKICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjM4MTo1Ny0zODE6NjciLAogICAgICAgICAgICAgICAicGF0aCIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAidHlwZSIgOiAiUHJvcGVydHkiLAogICAgICAgICAgICAgICAic291cmNlIiA6IHsKICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjM4MTo1Ny0zODE6NjEiLAogICAgICAgICAgICAgICAgICAibmFtZSIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAgICAidHlwZSIgOiAiT3BlcmFuZFJlZiIKICAgICAgICAgICAgICAgfQogICAgICAgICAgICB9LAogICAgICAgICAgICAib3BlcmFuZCIgOiBbIHsKICAgICAgICAgICAgICAgIm5hbWUiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgIm9wZXJhbmRUeXBlU3BlY2lmaWVyIiA6IHsKICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjM4MTozMi0zODE6NTMiLAogICAgICAgICAgICAgICAgICAibmFtZSIgOiAie2h0dHA6Ly9obDcub3JnL2ZoaXJ9VGVzdFJlcG9ydEFjdGlvblJlc3VsdCIsCiAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJOYW1lZFR5cGVTcGVjaWZpZXIiCiAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgfSBdCiAgICAgICAgIH0sIHsKICAgICAgICAgICAgImxvY2F0b3IiIDogIjM4MjoxLTM4Mjo3MCIsCiAgICAgICAgICAgICJuYW1lIiA6ICJUb1N0cmluZyIsCiAgICAgICAgICAgICJjb250ZXh0IiA6ICJQYXRpZW50IiwKICAgICAgICAgICAgImFjY2Vzc0xldmVsIiA6ICJQdWJsaWMiLAogICAgICAgICAgICAidHlwZSIgOiAiRnVuY3Rpb25EZWYiLAogICAgICAgICAgICAiZXhwcmVzc2lvbiIgOiB7CiAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIzODI6NjAtMzgyOjcwIiwKICAgICAgICAgICAgICAgInBhdGgiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgInR5cGUiIDogIlByb3BlcnR5IiwKICAgICAgICAgICAgICAgInNvdXJjZSIgOiB7CiAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIzODI6NjAtMzgyOjY0IiwKICAgICAgICAgICAgICAgICAgIm5hbWUiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk9wZXJhbmRSZWYiCiAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgfSwKICAgICAgICAgICAgIm9wZXJhbmQiIDogWyB7CiAgICAgICAgICAgICAgICJuYW1lIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICJvcGVyYW5kVHlwZVNwZWNpZmllciIgOiB7CiAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIzODI6MzItMzgyOjU2IiwKICAgICAgICAgICAgICAgICAgIm5hbWUiIDogIntodHRwOi8vaGw3Lm9yZy9maGlyfVRlc3RSZXBvcnRQYXJ0aWNpcGFudFR5cGUiLAogICAgICAgICAgICAgICAgICAidHlwZSIgOiAiTmFtZWRUeXBlU3BlY2lmaWVyIgogICAgICAgICAgICAgICB9CiAgICAgICAgICAgIH0gXQogICAgICAgICB9LCB7CiAgICAgICAgICAgICJsb2NhdG9yIiA6ICIzODM6MS0zODM6NjEiLAogICAgICAgICAgICAibmFtZSIgOiAiVG9TdHJpbmciLAogICAgICAgICAgICAiY29udGV4dCIgOiAiUGF0aWVudCIsCiAgICAgICAgICAgICJhY2Nlc3NMZXZlbCIgOiAiUHVibGljIiwKICAgICAgICAgICAgInR5cGUiIDogIkZ1bmN0aW9uRGVmIiwKICAgICAgICAgICAgImV4cHJlc3Npb24iIDogewogICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMzgzOjUxLTM4Mzo2MSIsCiAgICAgICAgICAgICAgICJwYXRoIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICJ0eXBlIiA6ICJQcm9wZXJ0eSIsCiAgICAgICAgICAgICAgICJzb3VyY2UiIDogewogICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMzgzOjUxLTM4Mzo1NSIsCiAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJPcGVyYW5kUmVmIgogICAgICAgICAgICAgICB9CiAgICAgICAgICAgIH0sCiAgICAgICAgICAgICJvcGVyYW5kIiA6IFsgewogICAgICAgICAgICAgICAibmFtZSIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAib3BlcmFuZFR5cGVTcGVjaWZpZXIiIDogewogICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMzgzOjMyLTM4Mzo0NyIsCiAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJ7aHR0cDovL2hsNy5vcmcvZmhpcn1UZXN0UmVwb3J0UmVzdWx0IiwKICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk5hbWVkVHlwZVNwZWNpZmllciIKICAgICAgICAgICAgICAgfQogICAgICAgICAgICB9IF0KICAgICAgICAgfSwgewogICAgICAgICAgICAibG9jYXRvciIgOiAiMzg0OjEtMzg0OjYxIiwKICAgICAgICAgICAgIm5hbWUiIDogIlRvU3RyaW5nIiwKICAgICAgICAgICAgImNvbnRleHQiIDogIlBhdGllbnQiLAogICAgICAgICAgICAiYWNjZXNzTGV2ZWwiIDogIlB1YmxpYyIsCiAgICAgICAgICAgICJ0eXBlIiA6ICJGdW5jdGlvbkRlZiIsCiAgICAgICAgICAgICJleHByZXNzaW9uIiA6IHsKICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjM4NDo1MS0zODQ6NjEiLAogICAgICAgICAgICAgICAicGF0aCIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAidHlwZSIgOiAiUHJvcGVydHkiLAogICAgICAgICAgICAgICAic291cmNlIiA6IHsKICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjM4NDo1MS0zODQ6NTUiLAogICAgICAgICAgICAgICAgICAibmFtZSIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAgICAidHlwZSIgOiAiT3BlcmFuZFJlZiIKICAgICAgICAgICAgICAgfQogICAgICAgICAgICB9LAogICAgICAgICAgICAib3BlcmFuZCIgOiBbIHsKICAgICAgICAgICAgICAgIm5hbWUiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgIm9wZXJhbmRUeXBlU3BlY2lmaWVyIiA6IHsKICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjM4NDozMi0zODQ6NDciLAogICAgICAgICAgICAgICAgICAibmFtZSIgOiAie2h0dHA6Ly9obDcub3JnL2ZoaXJ9VGVzdFJlcG9ydFN0YXR1cyIsCiAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJOYW1lZFR5cGVTcGVjaWZpZXIiCiAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgfSBdCiAgICAgICAgIH0sIHsKICAgICAgICAgICAgImxvY2F0b3IiIDogIjM4NToxLTM4NTo3MiIsCiAgICAgICAgICAgICJuYW1lIiA6ICJUb1N0cmluZyIsCiAgICAgICAgICAgICJjb250ZXh0IiA6ICJQYXRpZW50IiwKICAgICAgICAgICAgImFjY2Vzc0xldmVsIiA6ICJQdWJsaWMiLAogICAgICAgICAgICAidHlwZSIgOiAiRnVuY3Rpb25EZWYiLAogICAgICAgICAgICAiZXhwcmVzc2lvbiIgOiB7CiAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIzODU6NjItMzg1OjcyIiwKICAgICAgICAgICAgICAgInBhdGgiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgInR5cGUiIDogIlByb3BlcnR5IiwKICAgICAgICAgICAgICAgInNvdXJjZSIgOiB7CiAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIzODU6NjItMzg1OjY2IiwKICAgICAgICAgICAgICAgICAgIm5hbWUiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk9wZXJhbmRSZWYiCiAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgfSwKICAgICAgICAgICAgIm9wZXJhbmQiIDogWyB7CiAgICAgICAgICAgICAgICJuYW1lIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICJvcGVyYW5kVHlwZVNwZWNpZmllciIgOiB7CiAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIzODU6MzItMzg1OjU4IiwKICAgICAgICAgICAgICAgICAgIm5hbWUiIDogIntodHRwOi8vaGw3Lm9yZy9maGlyfVRlc3RTY3JpcHRSZXF1ZXN0TWV0aG9kQ29kZSIsCiAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJOYW1lZFR5cGVTcGVjaWZpZXIiCiAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgfSBdCiAgICAgICAgIH0sIHsKICAgICAgICAgICAgImxvY2F0b3IiIDogIjM4NjoxLTM4Njo1NiIsCiAgICAgICAgICAgICJuYW1lIiA6ICJUb1N0cmluZyIsCiAgICAgICAgICAgICJjb250ZXh0IiA6ICJQYXRpZW50IiwKICAgICAgICAgICAgImFjY2Vzc0xldmVsIiA6ICJQdWJsaWMiLAogICAgICAgICAgICAidHlwZSIgOiAiRnVuY3Rpb25EZWYiLAogICAgICAgICAgICAiZXhwcmVzc2lvbiIgOiB7CiAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIzODY6NDYtMzg2OjU2IiwKICAgICAgICAgICAgICAgInBhdGgiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgInR5cGUiIDogIlByb3BlcnR5IiwKICAgICAgICAgICAgICAgInNvdXJjZSIgOiB7CiAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIzODY6NDYtMzg2OjUwIiwKICAgICAgICAgICAgICAgICAgIm5hbWUiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk9wZXJhbmRSZWYiCiAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgfSwKICAgICAgICAgICAgIm9wZXJhbmQiIDogWyB7CiAgICAgICAgICAgICAgICJuYW1lIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICJvcGVyYW5kVHlwZVNwZWNpZmllciIgOiB7CiAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIzODY6MzItMzg2OjQyIiwKICAgICAgICAgICAgICAgICAgIm5hbWUiIDogIntodHRwOi8vaGw3Lm9yZy9maGlyfVRyaWdnZXJUeXBlIiwKICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk5hbWVkVHlwZVNwZWNpZmllciIKICAgICAgICAgICAgICAgfQogICAgICAgICAgICB9IF0KICAgICAgICAgfSwgewogICAgICAgICAgICAibG9jYXRvciIgOiAiMzg3OjEtMzg3OjYzIiwKICAgICAgICAgICAgIm5hbWUiIDogIlRvU3RyaW5nIiwKICAgICAgICAgICAgImNvbnRleHQiIDogIlBhdGllbnQiLAogICAgICAgICAgICAiYWNjZXNzTGV2ZWwiIDogIlB1YmxpYyIsCiAgICAgICAgICAgICJ0eXBlIiA6ICJGdW5jdGlvbkRlZiIsCiAgICAgICAgICAgICJleHByZXNzaW9uIiA6IHsKICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjM4Nzo1My0zODc6NjMiLAogICAgICAgICAgICAgICAicGF0aCIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAidHlwZSIgOiAiUHJvcGVydHkiLAogICAgICAgICAgICAgICAic291cmNlIiA6IHsKICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjM4Nzo1My0zODc6NTciLAogICAgICAgICAgICAgICAgICAibmFtZSIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAgICAidHlwZSIgOiAiT3BlcmFuZFJlZiIKICAgICAgICAgICAgICAgfQogICAgICAgICAgICB9LAogICAgICAgICAgICAib3BlcmFuZCIgOiBbIHsKICAgICAgICAgICAgICAgIm5hbWUiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgIm9wZXJhbmRUeXBlU3BlY2lmaWVyIiA6IHsKICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjM4NzozMi0zODc6NDkiLAogICAgICAgICAgICAgICAgICAibmFtZSIgOiAie2h0dHA6Ly9obDcub3JnL2ZoaXJ9VHlwZURlcml2YXRpb25SdWxlIiwKICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk5hbWVkVHlwZVNwZWNpZmllciIKICAgICAgICAgICAgICAgfQogICAgICAgICAgICB9IF0KICAgICAgICAgfSwgewogICAgICAgICAgICAibG9jYXRvciIgOiAiMzg4OjEtMzg4OjY3IiwKICAgICAgICAgICAgIm5hbWUiIDogIlRvU3RyaW5nIiwKICAgICAgICAgICAgImNvbnRleHQiIDogIlBhdGllbnQiLAogICAgICAgICAgICAiYWNjZXNzTGV2ZWwiIDogIlB1YmxpYyIsCiAgICAgICAgICAgICJ0eXBlIiA6ICJGdW5jdGlvbkRlZiIsCiAgICAgICAgICAgICJleHByZXNzaW9uIiA6IHsKICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjM4ODo1Ny0zODg6NjciLAogICAgICAgICAgICAgICAicGF0aCIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAidHlwZSIgOiAiUHJvcGVydHkiLAogICAgICAgICAgICAgICAic291cmNlIiA6IHsKICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjM4ODo1Ny0zODg6NjEiLAogICAgICAgICAgICAgICAgICAibmFtZSIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAgICAidHlwZSIgOiAiT3BlcmFuZFJlZiIKICAgICAgICAgICAgICAgfQogICAgICAgICAgICB9LAogICAgICAgICAgICAib3BlcmFuZCIgOiBbIHsKICAgICAgICAgICAgICAgIm5hbWUiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgIm9wZXJhbmRUeXBlU3BlY2lmaWVyIiA6IHsKICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjM4ODozMi0zODg6NTMiLAogICAgICAgICAgICAgICAgICAibmFtZSIgOiAie2h0dHA6Ly9obDcub3JnL2ZoaXJ9VHlwZVJlc3RmdWxJbnRlcmFjdGlvbiIsCiAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJOYW1lZFR5cGVTcGVjaWZpZXIiCiAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgfSBdCiAgICAgICAgIH0sIHsKICAgICAgICAgICAgImxvY2F0b3IiIDogIjM4OToxLTM4OTo1NyIsCiAgICAgICAgICAgICJuYW1lIiA6ICJUb1N0cmluZyIsCiAgICAgICAgICAgICJjb250ZXh0IiA6ICJQYXRpZW50IiwKICAgICAgICAgICAgImFjY2Vzc0xldmVsIiA6ICJQdWJsaWMiLAogICAgICAgICAgICAidHlwZSIgOiAiRnVuY3Rpb25EZWYiLAogICAgICAgICAgICAiZXhwcmVzc2lvbiIgOiB7CiAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIzODk6NDctMzg5OjU3IiwKICAgICAgICAgICAgICAgInBhdGgiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgInR5cGUiIDogIlByb3BlcnR5IiwKICAgICAgICAgICAgICAgInNvdXJjZSIgOiB7CiAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIzODk6NDctMzg5OjUxIiwKICAgICAgICAgICAgICAgICAgIm5hbWUiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk9wZXJhbmRSZWYiCiAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgfSwKICAgICAgICAgICAgIm9wZXJhbmQiIDogWyB7CiAgICAgICAgICAgICAgICJuYW1lIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICJvcGVyYW5kVHlwZVNwZWNpZmllciIgOiB7CiAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIzODk6MzItMzg5OjQzIiwKICAgICAgICAgICAgICAgICAgIm5hbWUiIDogIntodHRwOi8vaGw3Lm9yZy9maGlyfVVESUVudHJ5VHlwZSIsCiAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJOYW1lZFR5cGVTcGVjaWZpZXIiCiAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgfSBdCiAgICAgICAgIH0sIHsKICAgICAgICAgICAgImxvY2F0b3IiIDogIjM5MDoxLTM5MDo1NiIsCiAgICAgICAgICAgICJuYW1lIiA6ICJUb1N0cmluZyIsCiAgICAgICAgICAgICJjb250ZXh0IiA6ICJQYXRpZW50IiwKICAgICAgICAgICAgImFjY2Vzc0xldmVsIiA6ICJQdWJsaWMiLAogICAgICAgICAgICAidHlwZSIgOiAiRnVuY3Rpb25EZWYiLAogICAgICAgICAgICAiZXhwcmVzc2lvbiIgOiB7CiAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIzOTA6NDYtMzkwOjU2IiwKICAgICAgICAgICAgICAgInBhdGgiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgInR5cGUiIDogIlByb3BlcnR5IiwKICAgICAgICAgICAgICAgInNvdXJjZSIgOiB7CiAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIzOTA6NDYtMzkwOjUwIiwKICAgICAgICAgICAgICAgICAgIm5hbWUiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk9wZXJhbmRSZWYiCiAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgfSwKICAgICAgICAgICAgIm9wZXJhbmQiIDogWyB7CiAgICAgICAgICAgICAgICJuYW1lIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICJvcGVyYW5kVHlwZVNwZWNpZmllciIgOiB7CiAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIzOTA6MzItMzkwOjQyIiwKICAgICAgICAgICAgICAgICAgIm5hbWUiIDogIntodHRwOi8vaGw3Lm9yZy9maGlyfVVuaXRzT2ZUaW1lIiwKICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk5hbWVkVHlwZVNwZWNpZmllciIKICAgICAgICAgICAgICAgfQogICAgICAgICAgICB9IF0KICAgICAgICAgfSwgewogICAgICAgICAgICAibG9jYXRvciIgOiAiMzkxOjEtMzkxOjQ4IiwKICAgICAgICAgICAgIm5hbWUiIDogIlRvU3RyaW5nIiwKICAgICAgICAgICAgImNvbnRleHQiIDogIlBhdGllbnQiLAogICAgICAgICAgICAiYWNjZXNzTGV2ZWwiIDogIlB1YmxpYyIsCiAgICAgICAgICAgICJ0eXBlIiA6ICJGdW5jdGlvbkRlZiIsCiAgICAgICAgICAgICJleHByZXNzaW9uIiA6IHsKICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjM5MTozOC0zOTE6NDgiLAogICAgICAgICAgICAgICAicGF0aCIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAidHlwZSIgOiAiUHJvcGVydHkiLAogICAgICAgICAgICAgICAic291cmNlIiA6IHsKICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjM5MTozOC0zOTE6NDIiLAogICAgICAgICAgICAgICAgICAibmFtZSIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAgICAidHlwZSIgOiAiT3BlcmFuZFJlZiIKICAgICAgICAgICAgICAgfQogICAgICAgICAgICB9LAogICAgICAgICAgICAib3BlcmFuZCIgOiBbIHsKICAgICAgICAgICAgICAgIm5hbWUiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgIm9wZXJhbmRUeXBlU3BlY2lmaWVyIiA6IHsKICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjM5MTozMi0zOTE6MzQiLAogICAgICAgICAgICAgICAgICAibmFtZSIgOiAie2h0dHA6Ly9obDcub3JnL2ZoaXJ9VXNlIiwKICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk5hbWVkVHlwZVNwZWNpZmllciIKICAgICAgICAgICAgICAgfQogICAgICAgICAgICB9IF0KICAgICAgICAgfSwgewogICAgICAgICAgICAibG9jYXRvciIgOiAiMzkyOjEtMzkyOjU3IiwKICAgICAgICAgICAgIm5hbWUiIDogIlRvU3RyaW5nIiwKICAgICAgICAgICAgImNvbnRleHQiIDogIlBhdGllbnQiLAogICAgICAgICAgICAiYWNjZXNzTGV2ZWwiIDogIlB1YmxpYyIsCiAgICAgICAgICAgICJ0eXBlIiA6ICJGdW5jdGlvbkRlZiIsCiAgICAgICAgICAgICJleHByZXNzaW9uIiA6IHsKICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjM5Mjo0Ny0zOTI6NTciLAogICAgICAgICAgICAgICAicGF0aCIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAidHlwZSIgOiAiUHJvcGVydHkiLAogICAgICAgICAgICAgICAic291cmNlIiA6IHsKICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjM5Mjo0Ny0zOTI6NTEiLAogICAgICAgICAgICAgICAgICAibmFtZSIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAgICAidHlwZSIgOiAiT3BlcmFuZFJlZiIKICAgICAgICAgICAgICAgfQogICAgICAgICAgICB9LAogICAgICAgICAgICAib3BlcmFuZCIgOiBbIHsKICAgICAgICAgICAgICAgIm5hbWUiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgIm9wZXJhbmRUeXBlU3BlY2lmaWVyIiA6IHsKICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjM5MjozMi0zOTI6NDMiLAogICAgICAgICAgICAgICAgICAibmFtZSIgOiAie2h0dHA6Ly9obDcub3JnL2ZoaXJ9VmFyaWFibGVUeXBlIiwKICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk5hbWVkVHlwZVNwZWNpZmllciIKICAgICAgICAgICAgICAgfQogICAgICAgICAgICB9IF0KICAgICAgICAgfSwgewogICAgICAgICAgICAibG9jYXRvciIgOiAiMzkzOjEtMzkzOjU1IiwKICAgICAgICAgICAgIm5hbWUiIDogIlRvU3RyaW5nIiwKICAgICAgICAgICAgImNvbnRleHQiIDogIlBhdGllbnQiLAogICAgICAgICAgICAiYWNjZXNzTGV2ZWwiIDogIlB1YmxpYyIsCiAgICAgICAgICAgICJ0eXBlIiA6ICJGdW5jdGlvbkRlZiIsCiAgICAgICAgICAgICJleHByZXNzaW9uIiA6IHsKICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjM5Mzo0NS0zOTM6NTUiLAogICAgICAgICAgICAgICAicGF0aCIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAidHlwZSIgOiAiUHJvcGVydHkiLAogICAgICAgICAgICAgICAic291cmNlIiA6IHsKICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjM5Mzo0NS0zOTM6NDkiLAogICAgICAgICAgICAgICAgICAibmFtZSIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAgICAidHlwZSIgOiAiT3BlcmFuZFJlZiIKICAgICAgICAgICAgICAgfQogICAgICAgICAgICB9LAogICAgICAgICAgICAib3BlcmFuZCIgOiBbIHsKICAgICAgICAgICAgICAgIm5hbWUiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgIm9wZXJhbmRUeXBlU3BlY2lmaWVyIiA6IHsKICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjM5MzozMi0zOTM6NDEiLAogICAgICAgICAgICAgICAgICAibmFtZSIgOiAie2h0dHA6Ly9obDcub3JnL2ZoaXJ9VmlzaW9uQmFzZSIsCiAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJOYW1lZFR5cGVTcGVjaWZpZXIiCiAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgfSBdCiAgICAgICAgIH0sIHsKICAgICAgICAgICAgImxvY2F0b3IiIDogIjM5NDoxLTM5NDo1NSIsCiAgICAgICAgICAgICJuYW1lIiA6ICJUb1N0cmluZyIsCiAgICAgICAgICAgICJjb250ZXh0IiA6ICJQYXRpZW50IiwKICAgICAgICAgICAgImFjY2Vzc0xldmVsIiA6ICJQdWJsaWMiLAogICAgICAgICAgICAidHlwZSIgOiAiRnVuY3Rpb25EZWYiLAogICAgICAgICAgICAiZXhwcmVzc2lvbiIgOiB7CiAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIzOTQ6NDUtMzk0OjU1IiwKICAgICAgICAgICAgICAgInBhdGgiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgInR5cGUiIDogIlByb3BlcnR5IiwKICAgICAgICAgICAgICAgInNvdXJjZSIgOiB7CiAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIzOTQ6NDUtMzk0OjQ5IiwKICAgICAgICAgICAgICAgICAgIm5hbWUiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk9wZXJhbmRSZWYiCiAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgfSwKICAgICAgICAgICAgIm9wZXJhbmQiIDogWyB7CiAgICAgICAgICAgICAgICJuYW1lIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICJvcGVyYW5kVHlwZVNwZWNpZmllciIgOiB7CiAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIzOTQ6MzItMzk0OjQxIiwKICAgICAgICAgICAgICAgICAgIm5hbWUiIDogIntodHRwOi8vaGw3Lm9yZy9maGlyfVZpc2lvbkV5ZXMiLAogICAgICAgICAgICAgICAgICAidHlwZSIgOiAiTmFtZWRUeXBlU3BlY2lmaWVyIgogICAgICAgICAgICAgICB9CiAgICAgICAgICAgIH0gXQogICAgICAgICB9LCB7CiAgICAgICAgICAgICJsb2NhdG9yIiA6ICIzOTU6MS0zOTU6NTciLAogICAgICAgICAgICAibmFtZSIgOiAiVG9TdHJpbmciLAogICAgICAgICAgICAiY29udGV4dCIgOiAiUGF0aWVudCIsCiAgICAgICAgICAgICJhY2Nlc3NMZXZlbCIgOiAiUHVibGljIiwKICAgICAgICAgICAgInR5cGUiIDogIkZ1bmN0aW9uRGVmIiwKICAgICAgICAgICAgImV4cHJlc3Npb24iIDogewogICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMzk1OjQ3LTM5NTo1NyIsCiAgICAgICAgICAgICAgICJwYXRoIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICJ0eXBlIiA6ICJQcm9wZXJ0eSIsCiAgICAgICAgICAgICAgICJzb3VyY2UiIDogewogICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMzk1OjQ3LTM5NTo1MSIsCiAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJPcGVyYW5kUmVmIgogICAgICAgICAgICAgICB9CiAgICAgICAgICAgIH0sCiAgICAgICAgICAgICJvcGVyYW5kIiA6IFsgewogICAgICAgICAgICAgICAibmFtZSIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAib3BlcmFuZFR5cGVTcGVjaWZpZXIiIDogewogICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMzk1OjMyLTM5NTo0MyIsCiAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJ7aHR0cDovL2hsNy5vcmcvZmhpcn1WaXNpb25TdGF0dXMiLAogICAgICAgICAgICAgICAgICAidHlwZSIgOiAiTmFtZWRUeXBlU3BlY2lmaWVyIgogICAgICAgICAgICAgICB9CiAgICAgICAgICAgIH0gXQogICAgICAgICB9LCB7CiAgICAgICAgICAgICJsb2NhdG9yIiA6ICIzOTY6MS0zOTY6NTkiLAogICAgICAgICAgICAibmFtZSIgOiAiVG9TdHJpbmciLAogICAgICAgICAgICAiY29udGV4dCIgOiAiUGF0aWVudCIsCiAgICAgICAgICAgICJhY2Nlc3NMZXZlbCIgOiAiUHVibGljIiwKICAgICAgICAgICAgInR5cGUiIDogIkZ1bmN0aW9uRGVmIiwKICAgICAgICAgICAgImV4cHJlc3Npb24iIDogewogICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMzk2OjQ5LTM5Njo1OSIsCiAgICAgICAgICAgICAgICJwYXRoIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICJ0eXBlIiA6ICJQcm9wZXJ0eSIsCiAgICAgICAgICAgICAgICJzb3VyY2UiIDogewogICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMzk2OjQ5LTM5Njo1MyIsCiAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJPcGVyYW5kUmVmIgogICAgICAgICAgICAgICB9CiAgICAgICAgICAgIH0sCiAgICAgICAgICAgICJvcGVyYW5kIiA6IFsgewogICAgICAgICAgICAgICAibmFtZSIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAib3BlcmFuZFR5cGVTcGVjaWZpZXIiIDogewogICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMzk2OjMyLTM5Njo0NSIsCiAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJ7aHR0cDovL2hsNy5vcmcvZmhpcn1YUGF0aFVzYWdlVHlwZSIsCiAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJOYW1lZFR5cGVTcGVjaWZpZXIiCiAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgfSBdCiAgICAgICAgIH0sIHsKICAgICAgICAgICAgImxvY2F0b3IiIDogIjM5NzoxLTM5Nzo1NyIsCiAgICAgICAgICAgICJuYW1lIiA6ICJUb1N0cmluZyIsCiAgICAgICAgICAgICJjb250ZXh0IiA6ICJQYXRpZW50IiwKICAgICAgICAgICAgImFjY2Vzc0xldmVsIiA6ICJQdWJsaWMiLAogICAgICAgICAgICAidHlwZSIgOiAiRnVuY3Rpb25EZWYiLAogICAgICAgICAgICAiZXhwcmVzc2lvbiIgOiB7CiAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIzOTc6NDctMzk3OjU3IiwKICAgICAgICAgICAgICAgInBhdGgiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgInR5cGUiIDogIlByb3BlcnR5IiwKICAgICAgICAgICAgICAgInNvdXJjZSIgOiB7CiAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIzOTc6NDctMzk3OjUxIiwKICAgICAgICAgICAgICAgICAgIm5hbWUiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk9wZXJhbmRSZWYiCiAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgfSwKICAgICAgICAgICAgIm9wZXJhbmQiIDogWyB7CiAgICAgICAgICAgICAgICJuYW1lIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICJvcGVyYW5kVHlwZVNwZWNpZmllciIgOiB7CiAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIzOTc6MzItMzk3OjQzIiwKICAgICAgICAgICAgICAgICAgIm5hbWUiIDogIntodHRwOi8vaGw3Lm9yZy9maGlyfWJhc2U2NEJpbmFyeSIsCiAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJOYW1lZFR5cGVTcGVjaWZpZXIiCiAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgfSBdCiAgICAgICAgIH0sIHsKICAgICAgICAgICAgImxvY2F0b3IiIDogIjM5ODoxLTM5ODo1MyIsCiAgICAgICAgICAgICJuYW1lIiA6ICJUb0Jvb2xlYW4iLAogICAgICAgICAgICAiY29udGV4dCIgOiAiUGF0aWVudCIsCiAgICAgICAgICAgICJhY2Nlc3NMZXZlbCIgOiAiUHVibGljIiwKICAgICAgICAgICAgInR5cGUiIDogIkZ1bmN0aW9uRGVmIiwKICAgICAgICAgICAgImV4cHJlc3Npb24iIDogewogICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMzk4OjQzLTM5ODo1MyIsCiAgICAgICAgICAgICAgICJwYXRoIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICJ0eXBlIiA6ICJQcm9wZXJ0eSIsCiAgICAgICAgICAgICAgICJzb3VyY2UiIDogewogICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMzk4OjQzLTM5ODo0NyIsCiAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJPcGVyYW5kUmVmIgogICAgICAgICAgICAgICB9CiAgICAgICAgICAgIH0sCiAgICAgICAgICAgICJvcGVyYW5kIiA6IFsgewogICAgICAgICAgICAgICAibmFtZSIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAib3BlcmFuZFR5cGVTcGVjaWZpZXIiIDogewogICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMzk4OjMzLTM5ODozOSIsCiAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJ7aHR0cDovL2hsNy5vcmcvZmhpcn1ib29sZWFuIiwKICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk5hbWVkVHlwZVNwZWNpZmllciIKICAgICAgICAgICAgICAgfQogICAgICAgICAgICB9IF0KICAgICAgICAgfSwgewogICAgICAgICAgICAibG9jYXRvciIgOiAiMzk5OjEtMzk5OjQ3IiwKICAgICAgICAgICAgIm5hbWUiIDogIlRvRGF0ZSIsCiAgICAgICAgICAgICJjb250ZXh0IiA6ICJQYXRpZW50IiwKICAgICAgICAgICAgImFjY2Vzc0xldmVsIiA6ICJQdWJsaWMiLAogICAgICAgICAgICAidHlwZSIgOiAiRnVuY3Rpb25EZWYiLAogICAgICAgICAgICAiZXhwcmVzc2lvbiIgOiB7CiAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIzOTk6MzctMzk5OjQ3IiwKICAgICAgICAgICAgICAgInBhdGgiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgInR5cGUiIDogIlByb3BlcnR5IiwKICAgICAgICAgICAgICAgInNvdXJjZSIgOiB7CiAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIzOTk6MzctMzk5OjQxIiwKICAgICAgICAgICAgICAgICAgIm5hbWUiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk9wZXJhbmRSZWYiCiAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgfSwKICAgICAgICAgICAgIm9wZXJhbmQiIDogWyB7CiAgICAgICAgICAgICAgICJuYW1lIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICJvcGVyYW5kVHlwZVNwZWNpZmllciIgOiB7CiAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIzOTk6MzAtMzk5OjMzIiwKICAgICAgICAgICAgICAgICAgIm5hbWUiIDogIntodHRwOi8vaGw3Lm9yZy9maGlyfWRhdGUiLAogICAgICAgICAgICAgICAgICAidHlwZSIgOiAiTmFtZWRUeXBlU3BlY2lmaWVyIgogICAgICAgICAgICAgICB9CiAgICAgICAgICAgIH0gXQogICAgICAgICB9LCB7CiAgICAgICAgICAgICJsb2NhdG9yIiA6ICI0MDA6MS00MDA6NTUiLAogICAgICAgICAgICAibmFtZSIgOiAiVG9EYXRlVGltZSIsCiAgICAgICAgICAgICJjb250ZXh0IiA6ICJQYXRpZW50IiwKICAgICAgICAgICAgImFjY2Vzc0xldmVsIiA6ICJQdWJsaWMiLAogICAgICAgICAgICAidHlwZSIgOiAiRnVuY3Rpb25EZWYiLAogICAgICAgICAgICAiZXhwcmVzc2lvbiIgOiB7CiAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICI0MDA6NDUtNDAwOjU1IiwKICAgICAgICAgICAgICAgInBhdGgiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgInR5cGUiIDogIlByb3BlcnR5IiwKICAgICAgICAgICAgICAgInNvdXJjZSIgOiB7CiAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICI0MDA6NDUtNDAwOjQ5IiwKICAgICAgICAgICAgICAgICAgIm5hbWUiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk9wZXJhbmRSZWYiCiAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgfSwKICAgICAgICAgICAgIm9wZXJhbmQiIDogWyB7CiAgICAgICAgICAgICAgICJuYW1lIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICJvcGVyYW5kVHlwZVNwZWNpZmllciIgOiB7CiAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICI0MDA6MzQtNDAwOjQxIiwKICAgICAgICAgICAgICAgICAgIm5hbWUiIDogIntodHRwOi8vaGw3Lm9yZy9maGlyfWRhdGVUaW1lIiwKICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk5hbWVkVHlwZVNwZWNpZmllciIKICAgICAgICAgICAgICAgfQogICAgICAgICAgICB9IF0KICAgICAgICAgfSwgewogICAgICAgICAgICAibG9jYXRvciIgOiAiNDAxOjEtNDAxOjUzIiwKICAgICAgICAgICAgIm5hbWUiIDogIlRvRGVjaW1hbCIsCiAgICAgICAgICAgICJjb250ZXh0IiA6ICJQYXRpZW50IiwKICAgICAgICAgICAgImFjY2Vzc0xldmVsIiA6ICJQdWJsaWMiLAogICAgICAgICAgICAidHlwZSIgOiAiRnVuY3Rpb25EZWYiLAogICAgICAgICAgICAiZXhwcmVzc2lvbiIgOiB7CiAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICI0MDE6NDMtNDAxOjUzIiwKICAgICAgICAgICAgICAgInBhdGgiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgInR5cGUiIDogIlByb3BlcnR5IiwKICAgICAgICAgICAgICAgInNvdXJjZSIgOiB7CiAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICI0MDE6NDMtNDAxOjQ3IiwKICAgICAgICAgICAgICAgICAgIm5hbWUiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk9wZXJhbmRSZWYiCiAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgfSwKICAgICAgICAgICAgIm9wZXJhbmQiIDogWyB7CiAgICAgICAgICAgICAgICJuYW1lIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICJvcGVyYW5kVHlwZVNwZWNpZmllciIgOiB7CiAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICI0MDE6MzMtNDAxOjM5IiwKICAgICAgICAgICAgICAgICAgIm5hbWUiIDogIntodHRwOi8vaGw3Lm9yZy9maGlyfWRlY2ltYWwiLAogICAgICAgICAgICAgICAgICAidHlwZSIgOiAiTmFtZWRUeXBlU3BlY2lmaWVyIgogICAgICAgICAgICAgICB9CiAgICAgICAgICAgIH0gXQogICAgICAgICB9LCB7CiAgICAgICAgICAgICJsb2NhdG9yIiA6ICI0MDI6MS00MDI6NTQiLAogICAgICAgICAgICAibmFtZSIgOiAiVG9EYXRlVGltZSIsCiAgICAgICAgICAgICJjb250ZXh0IiA6ICJQYXRpZW50IiwKICAgICAgICAgICAgImFjY2Vzc0xldmVsIiA6ICJQdWJsaWMiLAogICAgICAgICAgICAidHlwZSIgOiAiRnVuY3Rpb25EZWYiLAogICAgICAgICAgICAiZXhwcmVzc2lvbiIgOiB7CiAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICI0MDI6NDQtNDAyOjU0IiwKICAgICAgICAgICAgICAgInBhdGgiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgInR5cGUiIDogIlByb3BlcnR5IiwKICAgICAgICAgICAgICAgInNvdXJjZSIgOiB7CiAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICI0MDI6NDQtNDAyOjQ4IiwKICAgICAgICAgICAgICAgICAgIm5hbWUiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk9wZXJhbmRSZWYiCiAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgfSwKICAgICAgICAgICAgIm9wZXJhbmQiIDogWyB7CiAgICAgICAgICAgICAgICJuYW1lIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICJvcGVyYW5kVHlwZVNwZWNpZmllciIgOiB7CiAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICI0MDI6MzQtNDAyOjQwIiwKICAgICAgICAgICAgICAgICAgIm5hbWUiIDogIntodHRwOi8vaGw3Lm9yZy9maGlyfWluc3RhbnQiLAogICAgICAgICAgICAgICAgICAidHlwZSIgOiAiTmFtZWRUeXBlU3BlY2lmaWVyIgogICAgICAgICAgICAgICB9CiAgICAgICAgICAgIH0gXQogICAgICAgICB9LCB7CiAgICAgICAgICAgICJsb2NhdG9yIiA6ICI0MDM6MS00MDM6NTMiLAogICAgICAgICAgICAibmFtZSIgOiAiVG9JbnRlZ2VyIiwKICAgICAgICAgICAgImNvbnRleHQiIDogIlBhdGllbnQiLAogICAgICAgICAgICAiYWNjZXNzTGV2ZWwiIDogIlB1YmxpYyIsCiAgICAgICAgICAgICJ0eXBlIiA6ICJGdW5jdGlvbkRlZiIsCiAgICAgICAgICAgICJleHByZXNzaW9uIiA6IHsKICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjQwMzo0My00MDM6NTMiLAogICAgICAgICAgICAgICAicGF0aCIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAidHlwZSIgOiAiUHJvcGVydHkiLAogICAgICAgICAgICAgICAic291cmNlIiA6IHsKICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjQwMzo0My00MDM6NDciLAogICAgICAgICAgICAgICAgICAibmFtZSIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAgICAidHlwZSIgOiAiT3BlcmFuZFJlZiIKICAgICAgICAgICAgICAgfQogICAgICAgICAgICB9LAogICAgICAgICAgICAib3BlcmFuZCIgOiBbIHsKICAgICAgICAgICAgICAgIm5hbWUiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgIm9wZXJhbmRUeXBlU3BlY2lmaWVyIiA6IHsKICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjQwMzozMy00MDM6MzkiLAogICAgICAgICAgICAgICAgICAibmFtZSIgOiAie2h0dHA6Ly9obDcub3JnL2ZoaXJ9aW50ZWdlciIsCiAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJOYW1lZFR5cGVTcGVjaWZpZXIiCiAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgfSBdCiAgICAgICAgIH0sIHsKICAgICAgICAgICAgImxvY2F0b3IiIDogIjQwNDoxLTQwNDo1MSIsCiAgICAgICAgICAgICJuYW1lIiA6ICJUb1N0cmluZyIsCiAgICAgICAgICAgICJjb250ZXh0IiA6ICJQYXRpZW50IiwKICAgICAgICAgICAgImFjY2Vzc0xldmVsIiA6ICJQdWJsaWMiLAogICAgICAgICAgICAidHlwZSIgOiAiRnVuY3Rpb25EZWYiLAogICAgICAgICAgICAiZXhwcmVzc2lvbiIgOiB7CiAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICI0MDQ6NDEtNDA0OjUxIiwKICAgICAgICAgICAgICAgInBhdGgiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgInR5cGUiIDogIlByb3BlcnR5IiwKICAgICAgICAgICAgICAgInNvdXJjZSIgOiB7CiAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICI0MDQ6NDEtNDA0OjQ1IiwKICAgICAgICAgICAgICAgICAgIm5hbWUiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk9wZXJhbmRSZWYiCiAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgfSwKICAgICAgICAgICAgIm9wZXJhbmQiIDogWyB7CiAgICAgICAgICAgICAgICJuYW1lIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICJvcGVyYW5kVHlwZVNwZWNpZmllciIgOiB7CiAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICI0MDQ6MzItNDA0OjM3IiwKICAgICAgICAgICAgICAgICAgIm5hbWUiIDogIntodHRwOi8vaGw3Lm9yZy9maGlyfXN0cmluZyIsCiAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJOYW1lZFR5cGVTcGVjaWZpZXIiCiAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgfSBdCiAgICAgICAgIH0sIHsKICAgICAgICAgICAgImxvY2F0b3IiIDogIjQwNToxLTQwNTo0NyIsCiAgICAgICAgICAgICJuYW1lIiA6ICJUb1RpbWUiLAogICAgICAgICAgICAiY29udGV4dCIgOiAiUGF0aWVudCIsCiAgICAgICAgICAgICJhY2Nlc3NMZXZlbCIgOiAiUHVibGljIiwKICAgICAgICAgICAgInR5cGUiIDogIkZ1bmN0aW9uRGVmIiwKICAgICAgICAgICAgImV4cHJlc3Npb24iIDogewogICAgICAgICAgICAgICAibG9jYXRvciIgOiAiNDA1OjM3LTQwNTo0NyIsCiAgICAgICAgICAgICAgICJwYXRoIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICJ0eXBlIiA6ICJQcm9wZXJ0eSIsCiAgICAgICAgICAgICAgICJzb3VyY2UiIDogewogICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiNDA1OjM3LTQwNTo0MSIsCiAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJPcGVyYW5kUmVmIgogICAgICAgICAgICAgICB9CiAgICAgICAgICAgIH0sCiAgICAgICAgICAgICJvcGVyYW5kIiA6IFsgewogICAgICAgICAgICAgICAibmFtZSIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAib3BlcmFuZFR5cGVTcGVjaWZpZXIiIDogewogICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiNDA1OjMwLTQwNTozMyIsCiAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJ7aHR0cDovL2hsNy5vcmcvZmhpcn10aW1lIiwKICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk5hbWVkVHlwZVNwZWNpZmllciIKICAgICAgICAgICAgICAgfQogICAgICAgICAgICB9IF0KICAgICAgICAgfSwgewogICAgICAgICAgICAibG9jYXRvciIgOiAiNDA2OjEtNDA2OjQ4IiwKICAgICAgICAgICAgIm5hbWUiIDogIlRvU3RyaW5nIiwKICAgICAgICAgICAgImNvbnRleHQiIDogIlBhdGllbnQiLAogICAgICAgICAgICAiYWNjZXNzTGV2ZWwiIDogIlB1YmxpYyIsCiAgICAgICAgICAgICJ0eXBlIiA6ICJGdW5jdGlvbkRlZiIsCiAgICAgICAgICAgICJleHByZXNzaW9uIiA6IHsKICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjQwNjozOC00MDY6NDgiLAogICAgICAgICAgICAgICAicGF0aCIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAidHlwZSIgOiAiUHJvcGVydHkiLAogICAgICAgICAgICAgICAic291cmNlIiA6IHsKICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjQwNjozOC00MDY6NDIiLAogICAgICAgICAgICAgICAgICAibmFtZSIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAgICAidHlwZSIgOiAiT3BlcmFuZFJlZiIKICAgICAgICAgICAgICAgfQogICAgICAgICAgICB9LAogICAgICAgICAgICAib3BlcmFuZCIgOiBbIHsKICAgICAgICAgICAgICAgIm5hbWUiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgIm9wZXJhbmRUeXBlU3BlY2lmaWVyIiA6IHsKICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjQwNjozMi00MDY6MzQiLAogICAgICAgICAgICAgICAgICAibmFtZSIgOiAie2h0dHA6Ly9obDcub3JnL2ZoaXJ9dXJpIiwKICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk5hbWVkVHlwZVNwZWNpZmllciIKICAgICAgICAgICAgICAgfQogICAgICAgICAgICB9IF0KICAgICAgICAgfSwgewogICAgICAgICAgICAibG9jYXRvciIgOiAiNDA3OjEtNDA3OjUwIiwKICAgICAgICAgICAgIm5hbWUiIDogIlRvU3RyaW5nIiwKICAgICAgICAgICAgImNvbnRleHQiIDogIlBhdGllbnQiLAogICAgICAgICAgICAiYWNjZXNzTGV2ZWwiIDogIlB1YmxpYyIsCiAgICAgICAgICAgICJ0eXBlIiA6ICJGdW5jdGlvbkRlZiIsCiAgICAgICAgICAgICJleHByZXNzaW9uIiA6IHsKICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjQwNzo0MC00MDc6NTAiLAogICAgICAgICAgICAgICAicGF0aCIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAidHlwZSIgOiAiUHJvcGVydHkiLAogICAgICAgICAgICAgICAic291cmNlIiA6IHsKICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjQwNzo0MC00MDc6NDQiLAogICAgICAgICAgICAgICAgICAibmFtZSIgOiAidmFsdWUiLAogICAgICAgICAgICAgICAgICAidHlwZSIgOiAiT3BlcmFuZFJlZiIKICAgICAgICAgICAgICAgfQogICAgICAgICAgICB9LAogICAgICAgICAgICAib3BlcmFuZCIgOiBbIHsKICAgICAgICAgICAgICAgIm5hbWUiIDogInZhbHVlIiwKICAgICAgICAgICAgICAgIm9wZXJhbmRUeXBlU3BlY2lmaWVyIiA6IHsKICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjQwNzozMi00MDc6MzYiLAogICAgICAgICAgICAgICAgICAibmFtZSIgOiAie2h0dHA6Ly9obDcub3JnL2ZoaXJ9eGh0bWwiLAogICAgICAgICAgICAgICAgICAidHlwZSIgOiAiTmFtZWRUeXBlU3BlY2lmaWVyIgogICAgICAgICAgICAgICB9CiAgICAgICAgICAgIH0gXQogICAgICAgICB9IF0KICAgICAgfQogICB9Cn0=" + } + ] + }, + "request": { + "method": "PUT", + "url": "Library/FHIRHelpers" + } + }, + { + "resource": { + "resourceType": "Patient", + "id": "CMSTest-patient-2", + "meta": { + "profile": [ + "http://hl7.org/fhir/us/core/StructureDefinition/us-core-patient" + ] + }, + "extension": [ + { + "url": "http://hl7.org/fhir/us/core/StructureDefinition/us-core-race", + "extension": [ + { + "url": "ombCategory", + "valueCoding": { + "system": "urn:oid:2.16.840.1.113883.6.238", + "code": "2028-9", + "display": "Asian" + } + } + ] + }, + { + "url": "http://hl7.org/fhir/us/core/StructureDefinition/us-core-ethnicity", + "extension": [ + { + "url": "ombCategory", + "valueCoding": { + "system": "urn:oid:2.16.840.1.113883.6.238", + "code": "2135-2", + "display": "Hispanic or Latino" + } + } + ] + } + ], + "identifier": [ + { + "use": "usual", + "type": { + "coding": [ + { + "system": "http://terminology.hl7.org/CodeSystem/v2-0203", + "code": "MR", + "display": "Medical Record Number" + } + ] + }, + "system": "http://hospital.smarthealthit.org", + "value": "999999999" + } + ], + "name": [ + { + "family": "Dere", + "given": [ + "Ben" + ] + } + ], + "gender": "female", + "birthDate": "2000-03-01" + }, + "request": { + "method": "PUT", + "url": "Patient/CMSTest-patient-2" + } + }, + { + "resource": { + "resourceType": "Library", + "id": "SDE", + "extension": [ + { + "url": "http://hl7.org/fhir/us/cqfmeasures/StructureDefinition/cqfm-softwaresystem", + "valueReference": { + "reference": "Device/cqf-tooling" + } + } + ], + "url": "http://content.alphora.com/fhir/dqm/Library/SDE", + "name": "SDE", + "title": "Library - Supplemental Data Elements", + "type": { + "coding": [ + { + "system": "http://terminology.hl7.org/CodeSystem/library-type", + "code": "logic-library" + } + ] + }, + "description": "A Shared library encapsulating valuable common functions related to the use of Supplemental Data Elements used in Alphora dQM and QICore-based CQL artifacts.", + "jurisdiction": [ + { + "coding": [ + { + "system": "urn:iso:std:iso:3166", + "version": "4.0.1", + "code": "US", + "display": "United States of America" + } + ], + "text": "United States of America" + } + ], + "relatedArtifact": [ + { + "type": "depends-on", + "display": "FHIR model information", + "resource": "http://fhir.org/guides/cqf/common/Library/FHIR-ModelInfo|4.0.1" + }, + { + "type": "depends-on", + "display": "Library FHIRHelpers", + "resource": "http://content.alphora.com/fhir/dqm/Library/DQMFHIRHelpers" + }, + { + "type": "depends-on", + "display": "Value set Ethnicity", + "resource": "http://cts.nlm.nih.gov/fhir/ValueSet/2.16.840.1.114222.4.11.837" + }, + { + "type": "depends-on", + "display": "Value set ONC Administrative Sex", + "resource": "http://cts.nlm.nih.gov/fhir/ValueSet/2.16.840.1.113762.1.4.1" + }, + { + "type": "depends-on", + "display": "Value set Payer", + "resource": "http://cts.nlm.nih.gov/fhir/ValueSet/2.16.840.1.114222.4.11.3591" + }, + { + "type": "depends-on", + "display": "Value set Race", + "resource": "http://cts.nlm.nih.gov/fhir/ValueSet/2.16.840.1.114222.4.11.836" + } + ], + "parameter": [ + { + "name": "Measurement Period", + "use": "in", + "min": 0, + "max": "1", + "type": "Period" + }, + { + "name": "Patient", + "use": "out", + "min": 0, + "max": "1", + "type": "Patient" + }, + { + "name": "SDE Ethnicity", + "use": "out", + "min": 0, + "max": "*", + "type": "Coding" + }, + { + "name": "SDE Payer", + "use": "out", + "min": 0, + "max": "*", + "type": "Coding" + }, + { + "name": "SDE Race", + "use": "out", + "min": 0, + "max": "*", + "type": "Coding" + }, + { + "name": "SDE Sex", + "use": "out", + "min": 0, + "max": "1", + "type": "Coding" + } + ], + "dataRequirement": [ + { + "type": "Patient", + "profile": [ + "http://hl7.org/fhir/StructureDefinition/Patient" + ] + }, + { + "type": "Patient", + "profile": [ + "http://hl7.org/fhir/StructureDefinition/Patient" + ], + "mustSupport": [ + "url", + "extension", + "value" + ] + }, + { + "type": "Patient", + "profile": [ + "http://hl7.org/fhir/StructureDefinition/Patient" + ], + "mustSupport": [ + "url", + "extension", + "value" + ] + }, + { + "type": "Coverage", + "profile": [ + "http://hl7.org/fhir/StructureDefinition/Coverage" + ], + "mustSupport": [ + "type", + "period", + "type.coding" + ], + "codeFilter": [ + { + "path": "type", + "valueSet": "http://cts.nlm.nih.gov/fhir/ValueSet/2.16.840.1.114222.4.11.3591" + } + ] + }, + { + "type": "Patient", + "profile": [ + "http://hl7.org/fhir/StructureDefinition/Patient" + ], + "mustSupport": [ + "url", + "extension", + "value" + ] + }, + { + "type": "Patient", + "profile": [ + "http://hl7.org/fhir/StructureDefinition/Patient" + ], + "mustSupport": [ + "url", + "extension", + "value" + ] + } + ], + "content": [ + { + "contentType": "text/cql", + "data": "bGlicmFyeSBTREUKCi8qQHVwZGF0ZTogQEBCVFIgMjAyMC0wMy0zMSAtPgpJbmNyZW1lbnRlZCB2ZXJzaW9uIHRvIDIuMC4wClVwZGF0ZWQgRkhJUiB2ZXJzaW9uIHRvIDQuMC4xCkBAQCovCgp1c2luZyBGSElSIHZlcnNpb24gJzQuMC4xJwoKaW5jbHVkZSBEUU1GSElSSGVscGVycyBjYWxsZWQgRkhJUkhlbHBlcnMKCnZhbHVlc2V0ICJFdGhuaWNpdHkiOiAnaHR0cDovL2N0cy5ubG0ubmloLmdvdi9maGlyL1ZhbHVlU2V0LzIuMTYuODQwLjEuMTE0MjIyLjQuMTEuODM3Jwp2YWx1ZXNldCAiT05DIEFkbWluaXN0cmF0aXZlIFNleCI6ICdodHRwOi8vY3RzLm5sbS5uaWguZ292L2ZoaXIvVmFsdWVTZXQvMi4xNi44NDAuMS4xMTM3NjIuMS40LjEnCnZhbHVlc2V0ICJQYXllciI6ICdodHRwOi8vY3RzLm5sbS5uaWguZ292L2ZoaXIvVmFsdWVTZXQvMi4xNi44NDAuMS4xMTQyMjIuNC4xMS4zNTkxJwp2YWx1ZXNldCAiUmFjZSI6ICdodHRwOi8vY3RzLm5sbS5uaWguZ292L2ZoaXIvVmFsdWVTZXQvMi4xNi44NDAuMS4xMTQyMjIuNC4xMS44MzYnCgpwYXJhbWV0ZXIgIk1lYXN1cmVtZW50IFBlcmlvZCIgZGVmYXVsdCBJbnRlcnZhbFtAMjAyMC0wMS0wMVQwMDowMDowMC4wMDAsIEAyMDIxLTAxLTAxVDAwOjAwOjAwLjAwMCkKY29udGV4dCBQYXRpZW50CgpkZWZpbmUgIlNERSBFdGhuaWNpdHkiOgogIChmbGF0dGVuICgKICAgICAgUGF0aWVudC5leHRlbnNpb24gRXh0ZW5zaW9uCiAgICAgICAgd2hlcmUgRXh0ZW5zaW9uLnVybCA9ICdodHRwOi8vaGw3Lm9yZy9maGlyL3VzL2NvcmUvU3RydWN0dXJlRGVmaW5pdGlvbi91cy1jb3JlLWV0aG5pY2l0eScKICAgICAgICAgIHJldHVybiBFeHRlbnNpb24uZXh0ZW5zaW9uCiAgICApKSBFCiAgICAgIHdoZXJlIEUudXJsID0gJ29tYkNhdGVnb3J5JwogICAgICAgIG9yIEUudXJsID0gJ2RldGFpbGVkJwogICAgICByZXR1cm4gRkhJUkhlbHBlcnMuVG9Db2RlKEUudmFsdWUpCgpkZWZpbmUgIlNERSBQYXllciI6CiAgZmxhdHRlbihbQ292ZXJhZ2U6IHR5cGUgaW4gIlBheWVyIl0gUGF5ZXIKICAgIHdoZXJlIFBheWVyLnBlcmlvZCBvdmVybGFwcyAiTWVhc3VyZW1lbnQgUGVyaW9kIgogICAgICByZXR1cm4gKAogICAgICAgIFBheWVyLnR5cGUuY29kaW5nIGMKICAgICAgICAgIHJldHVybiBGSElSSGVscGVycy5Ub0NvZGUoYykKICAgICAgKQogICkKCmRlZmluZSAiU0RFIFJhY2UiOgogIChmbGF0dGVuICgKICAgICAgUGF0aWVudC5leHRlbnNpb24gRXh0ZW5zaW9uCiAgICAgICAgd2hlcmUgRXh0ZW5zaW9uLnVybCA9ICdodHRwOi8vaGw3Lm9yZy9maGlyL3VzL2NvcmUvU3RydWN0dXJlRGVmaW5pdGlvbi91cy1jb3JlLXJhY2UnCiAgICAgICAgICByZXR1cm4gRXh0ZW5zaW9uLmV4dGVuc2lvbgogICAgKSkgRQogICAgICB3aGVyZSBFLnVybCA9ICdvbWJDYXRlZ29yeScKICAgICAgICBvciBFLnVybCA9ICdkZXRhaWxlZCcKICAgICAgcmV0dXJuIEZISVJIZWxwZXJzLlRvQ29kZShFLnZhbHVlKQoKZGVmaW5lICJTREUgU2V4IjoKICBjYXNlCiAgICAgIHdoZW4gUGF0aWVudC5nZW5kZXIgPSAnbWFsZScgdGhlbiBDb2RlIHsgY29kZTogJ00nLCBzeXN0ZW06ICdodHRwOi8vaGw3Lm9yZy9maGlyL3YzL0FkbWluaXN0cmF0aXZlR2VuZGVyJywgZGlzcGxheTogJ01hbGUnIH0KICAgICAgd2hlbiBQYXRpZW50LmdlbmRlciA9ICdmZW1hbGUnIHRoZW4gQ29kZSB7IGNvZGU6ICdGJywgc3lzdGVtOiAnaHR0cDovL2hsNy5vcmcvZmhpci92My9BZG1pbmlzdHJhdGl2ZUdlbmRlcicsIGRpc3BsYXk6ICdGZW1hbGUnIH0KICAgICAgZWxzZSBudWxsCiAgICBlbmQK" + }, + { + "contentType": "application/elm+xml", + "data": "PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0iVVRGLTgiPz4KPGxpYnJhcnkgeG1sbnM9InVybjpobDctb3JnOmVsbTpyMSIgeG1sbnM6dD0idXJuOmhsNy1vcmc6ZWxtLXR5cGVzOnIxIiB4bWxuczp4c2k9Imh0dHA6Ly93d3cudzMub3JnLzIwMDEvWE1MU2NoZW1hLWluc3RhbmNlIiB4bWxuczp4c2Q9Imh0dHA6Ly93d3cudzMub3JnLzIwMDEvWE1MU2NoZW1hIiB4bWxuczpmaGlyPSJodHRwOi8vaGw3Lm9yZy9maGlyIiB4bWxuczpxZG00Mz0idXJuOmhlYWx0aGl0LWdvdjpxZG06djRfMyIgeG1sbnM6cWRtNTM9InVybjpoZWFsdGhpdC1nb3Y6cWRtOnY1XzMiIHhtbG5zOmE9InVybjpobDctb3JnOmNxbC1hbm5vdGF0aW9uczpyMSI+CiAgIDxhbm5vdGF0aW9uIHRyYW5zbGF0b3JWZXJzaW9uPSIyLjcuMCIgdHJhbnNsYXRvck9wdGlvbnM9IkVuYWJsZUxvY2F0b3JzLERpc2FibGVMaXN0RGVtb3Rpb24sRGlzYWJsZUxpc3RQcm9tb3Rpb24iIHhzaTp0eXBlPSJhOkNxbFRvRWxtSW5mbyIvPgogICA8aWRlbnRpZmllciBpZD0iU0RFIiBzeXN0ZW09Imh0dHA6Ly9jb250ZW50LmFscGhvcmEuY29tL2ZoaXIvZHFtIi8+CiAgIDxzY2hlbWFJZGVudGlmaWVyIGlkPSJ1cm46aGw3LW9yZzplbG0iIHZlcnNpb249InIxIi8+CiAgIDx1c2luZ3M+CiAgICAgIDxkZWYgbG9jYWxJZGVudGlmaWVyPSJTeXN0ZW0iIHVyaT0idXJuOmhsNy1vcmc6ZWxtLXR5cGVzOnIxIi8+CiAgICAgIDxkZWYgbG9jYXRvcj0iODoxLTg6MjYiIGxvY2FsSWRlbnRpZmllcj0iRkhJUiIgdXJpPSJodHRwOi8vaGw3Lm9yZy9maGlyIiB2ZXJzaW9uPSI0LjAuMSI+CiAgICAgICAgIDxhbm5vdGF0aW9uIHhzaTp0eXBlPSJhOkFubm90YXRpb24iPgogICAgICAgICAgICA8YTp0IG5hbWU9InVwZGF0ZSIvPgogICAgICAgICA8L2Fubm90YXRpb24+CiAgICAgIDwvZGVmPgogICA8L3VzaW5ncz4KICAgPGluY2x1ZGVzPgogICAgICA8ZGVmIGxvY2F0b3I9IjEwOjEtMTA6NDEiIGxvY2FsSWRlbnRpZmllcj0iRkhJUkhlbHBlcnMiIHBhdGg9Imh0dHA6Ly9jb250ZW50LmFscGhvcmEuY29tL2ZoaXIvZHFtL0RRTUZISVJIZWxwZXJzIi8+CiAgIDwvaW5jbHVkZXM+CiAgIDxwYXJhbWV0ZXJzPgogICAgICA8ZGVmIGxvY2F0b3I9IjE3OjEtMTc6OTkiIG5hbWU9Ik1lYXN1cmVtZW50IFBlcmlvZCIgYWNjZXNzTGV2ZWw9IlB1YmxpYyI+CiAgICAgICAgIDxkZWZhdWx0IGxvY2F0b3I9IjE3OjQwLTE3Ojk5IiBsb3dDbG9zZWQ9InRydWUiIGhpZ2hDbG9zZWQ9ImZhbHNlIiB4c2k6dHlwZT0iSW50ZXJ2YWwiPgogICAgICAgICAgICA8bG93IGxvY2F0b3I9IjE3OjQ5LTE3OjcyIiB4c2k6dHlwZT0iRGF0ZVRpbWUiPgogICAgICAgICAgICAgICA8eWVhciB2YWx1ZVR5cGU9InQ6SW50ZWdlciIgdmFsdWU9IjIwMjAiIHhzaTp0eXBlPSJMaXRlcmFsIi8+CiAgICAgICAgICAgICAgIDxtb250aCB2YWx1ZVR5cGU9InQ6SW50ZWdlciIgdmFsdWU9IjEiIHhzaTp0eXBlPSJMaXRlcmFsIi8+CiAgICAgICAgICAgICAgIDxkYXkgdmFsdWVUeXBlPSJ0OkludGVnZXIiIHZhbHVlPSIxIiB4c2k6dHlwZT0iTGl0ZXJhbCIvPgogICAgICAgICAgICAgICA8aG91ciB2YWx1ZVR5cGU9InQ6SW50ZWdlciIgdmFsdWU9IjAiIHhzaTp0eXBlPSJMaXRlcmFsIi8+CiAgICAgICAgICAgICAgIDxtaW51dGUgdmFsdWVUeXBlPSJ0OkludGVnZXIiIHZhbHVlPSIwIiB4c2k6dHlwZT0iTGl0ZXJhbCIvPgogICAgICAgICAgICAgICA8c2Vjb25kIHZhbHVlVHlwZT0idDpJbnRlZ2VyIiB2YWx1ZT0iMCIgeHNpOnR5cGU9IkxpdGVyYWwiLz4KICAgICAgICAgICAgICAgPG1pbGxpc2Vjb25kIHZhbHVlVHlwZT0idDpJbnRlZ2VyIiB2YWx1ZT0iMCIgeHNpOnR5cGU9IkxpdGVyYWwiLz4KICAgICAgICAgICAgPC9sb3c+CiAgICAgICAgICAgIDxoaWdoIGxvY2F0b3I9IjE3Ojc1LTE3Ojk4IiB4c2k6dHlwZT0iRGF0ZVRpbWUiPgogICAgICAgICAgICAgICA8eWVhciB2YWx1ZVR5cGU9InQ6SW50ZWdlciIgdmFsdWU9IjIwMjEiIHhzaTp0eXBlPSJMaXRlcmFsIi8+CiAgICAgICAgICAgICAgIDxtb250aCB2YWx1ZVR5cGU9InQ6SW50ZWdlciIgdmFsdWU9IjEiIHhzaTp0eXBlPSJMaXRlcmFsIi8+CiAgICAgICAgICAgICAgIDxkYXkgdmFsdWVUeXBlPSJ0OkludGVnZXIiIHZhbHVlPSIxIiB4c2k6dHlwZT0iTGl0ZXJhbCIvPgogICAgICAgICAgICAgICA8aG91ciB2YWx1ZVR5cGU9InQ6SW50ZWdlciIgdmFsdWU9IjAiIHhzaTp0eXBlPSJMaXRlcmFsIi8+CiAgICAgICAgICAgICAgIDxtaW51dGUgdmFsdWVUeXBlPSJ0OkludGVnZXIiIHZhbHVlPSIwIiB4c2k6dHlwZT0iTGl0ZXJhbCIvPgogICAgICAgICAgICAgICA8c2Vjb25kIHZhbHVlVHlwZT0idDpJbnRlZ2VyIiB2YWx1ZT0iMCIgeHNpOnR5cGU9IkxpdGVyYWwiLz4KICAgICAgICAgICAgICAgPG1pbGxpc2Vjb25kIHZhbHVlVHlwZT0idDpJbnRlZ2VyIiB2YWx1ZT0iMCIgeHNpOnR5cGU9IkxpdGVyYWwiLz4KICAgICAgICAgICAgPC9oaWdoPgogICAgICAgICA8L2RlZmF1bHQ+CiAgICAgIDwvZGVmPgogICA8L3BhcmFtZXRlcnM+CiAgIDx2YWx1ZVNldHM+CiAgICAgIDxkZWYgbG9jYXRvcj0iMTI6MS0xMjo4NyIgbmFtZT0iRXRobmljaXR5IiBpZD0iaHR0cDovL2N0cy5ubG0ubmloLmdvdi9maGlyL1ZhbHVlU2V0LzIuMTYuODQwLjEuMTE0MjIyLjQuMTEuODM3IiBhY2Nlc3NMZXZlbD0iUHVibGljIi8+CiAgICAgIDxkZWYgbG9jYXRvcj0iMTM6MS0xMzo5NyIgbmFtZT0iT05DIEFkbWluaXN0cmF0aXZlIFNleCIgaWQ9Imh0dHA6Ly9jdHMubmxtLm5paC5nb3YvZmhpci9WYWx1ZVNldC8yLjE2Ljg0MC4xLjExMzc2Mi4xLjQuMSIgYWNjZXNzTGV2ZWw9IlB1YmxpYyIvPgogICAgICA8ZGVmIGxvY2F0b3I9IjE0OjEtMTQ6ODQiIG5hbWU9IlBheWVyIiBpZD0iaHR0cDovL2N0cy5ubG0ubmloLmdvdi9maGlyL1ZhbHVlU2V0LzIuMTYuODQwLjEuMTE0MjIyLjQuMTEuMzU5MSIgYWNjZXNzTGV2ZWw9IlB1YmxpYyIvPgogICAgICA8ZGVmIGxvY2F0b3I9IjE1OjEtMTU6ODIiIG5hbWU9IlJhY2UiIGlkPSJodHRwOi8vY3RzLm5sbS5uaWguZ292L2ZoaXIvVmFsdWVTZXQvMi4xNi44NDAuMS4xMTQyMjIuNC4xMS44MzYiIGFjY2Vzc0xldmVsPSJQdWJsaWMiLz4KICAgPC92YWx1ZVNldHM+CiAgIDxjb250ZXh0cz4KICAgICAgPGRlZiBsb2NhdG9yPSIxODoxLTE4OjE1IiBuYW1lPSJQYXRpZW50Ii8+CiAgIDwvY29udGV4dHM+CiAgIDxzdGF0ZW1lbnRzPgogICAgICA8ZGVmIGxvY2F0b3I9IjE4OjEtMTg6MTUiIG5hbWU9IlBhdGllbnQiIGNvbnRleHQ9IlBhdGllbnQiPgogICAgICAgICA8ZXhwcmVzc2lvbiB4c2k6dHlwZT0iU2luZ2xldG9uRnJvbSI+CiAgICAgICAgICAgIDxvcGVyYW5kIGxvY2F0b3I9IjE4OjEtMTg6MTUiIGRhdGFUeXBlPSJmaGlyOlBhdGllbnQiIHRlbXBsYXRlSWQ9Imh0dHA6Ly9obDcub3JnL2ZoaXIvU3RydWN0dXJlRGVmaW5pdGlvbi9QYXRpZW50IiB4c2k6dHlwZT0iUmV0cmlldmUiLz4KICAgICAgICAgPC9leHByZXNzaW9uPgogICAgICA8L2RlZj4KICAgICAgPGRlZiBsb2NhdG9yPSIyMDoxLTI4OjQwIiBuYW1lPSJTREUgRXRobmljaXR5IiBjb250ZXh0PSJQYXRpZW50IiBhY2Nlc3NMZXZlbD0iUHVibGljIj4KICAgICAgICAgPGV4cHJlc3Npb24gbG9jYXRvcj0iMjE6My0yODo0MCIgeHNpOnR5cGU9IlF1ZXJ5Ij4KICAgICAgICAgICAgPHNvdXJjZSBsb2NhdG9yPSIyMTozLTI1OjgiIGFsaWFzPSJFIj4KICAgICAgICAgICAgICAgPGV4cHJlc3Npb24gbG9jYXRvcj0iMjE6My0yNTo2IiB4c2k6dHlwZT0iRmxhdHRlbiI+CiAgICAgICAgICAgICAgICAgIDxvcGVyYW5kIGxvY2F0b3I9IjIxOjEyLTI1OjUiIHhzaTp0eXBlPSJRdWVyeSI+CiAgICAgICAgICAgICAgICAgICAgIDxzb3VyY2UgbG9jYXRvcj0iMjI6Ny0yMjozMyIgYWxpYXM9IkV4dGVuc2lvbiI+CiAgICAgICAgICAgICAgICAgICAgICAgIDxleHByZXNzaW9uIGxvY2F0b3I9IjIyOjctMjI6MjMiIHBhdGg9ImV4dGVuc2lvbiIgeHNpOnR5cGU9IlByb3BlcnR5Ij4KICAgICAgICAgICAgICAgICAgICAgICAgICAgPHNvdXJjZSBuYW1lPSJQYXRpZW50IiB4c2k6dHlwZT0iRXhwcmVzc2lvblJlZiIvPgogICAgICAgICAgICAgICAgICAgICAgICA8L2V4cHJlc3Npb24+CiAgICAgICAgICAgICAgICAgICAgIDwvc291cmNlPgogICAgICAgICAgICAgICAgICAgICA8d2hlcmUgbG9jYXRvcj0iMjM6OS0yMzo5NyIgeHNpOnR5cGU9IkVxdWFsIj4KICAgICAgICAgICAgICAgICAgICAgICAgPG9wZXJhbmQgbmFtZT0iVG9TdHJpbmciIGxpYnJhcnlOYW1lPSJGSElSSGVscGVycyIgeHNpOnR5cGU9IkZ1bmN0aW9uUmVmIj4KICAgICAgICAgICAgICAgICAgICAgICAgICAgPG9wZXJhbmQgbG9jYXRvcj0iMjM6MTUtMjM6MjciIHBhdGg9InVybCIgc2NvcGU9IkV4dGVuc2lvbiIgeHNpOnR5cGU9IlByb3BlcnR5Ii8+CiAgICAgICAgICAgICAgICAgICAgICAgIDwvb3BlcmFuZD4KICAgICAgICAgICAgICAgICAgICAgICAgPG9wZXJhbmQgbG9jYXRvcj0iMjM6MzEtMjM6OTciIHZhbHVlVHlwZT0idDpTdHJpbmciIHZhbHVlPSJodHRwOi8vaGw3Lm9yZy9maGlyL3VzL2NvcmUvU3RydWN0dXJlRGVmaW5pdGlvbi91cy1jb3JlLWV0aG5pY2l0eSIgeHNpOnR5cGU9IkxpdGVyYWwiLz4KICAgICAgICAgICAgICAgICAgICAgPC93aGVyZT4KICAgICAgICAgICAgICAgICAgICAgPHJldHVybiBsb2NhdG9yPSIyNDoxMS0yNDozNiI+CiAgICAgICAgICAgICAgICAgICAgICAgIDxleHByZXNzaW9uIGxvY2F0b3I9IjI0OjE4LTI0OjM2IiBwYXRoPSJleHRlbnNpb24iIHNjb3BlPSJFeHRlbnNpb24iIHhzaTp0eXBlPSJQcm9wZXJ0eSIvPgogICAgICAgICAgICAgICAgICAgICA8L3JldHVybj4KICAgICAgICAgICAgICAgICAgPC9vcGVyYW5kPgogICAgICAgICAgICAgICA8L2V4cHJlc3Npb24+CiAgICAgICAgICAgIDwvc291cmNlPgogICAgICAgICAgICA8d2hlcmUgbG9jYXRvcj0iMjY6Ny0yNzoyOSIgeHNpOnR5cGU9Ik9yIj4KICAgICAgICAgICAgICAgPG9wZXJhbmQgbG9jYXRvcj0iMjY6MTMtMjY6MzMiIHhzaTp0eXBlPSJFcXVhbCI+CiAgICAgICAgICAgICAgICAgIDxvcGVyYW5kIG5hbWU9IlRvU3RyaW5nIiBsaWJyYXJ5TmFtZT0iRkhJUkhlbHBlcnMiIHhzaTp0eXBlPSJGdW5jdGlvblJlZiI+CiAgICAgICAgICAgICAgICAgICAgIDxvcGVyYW5kIGxvY2F0b3I9IjI2OjEzLTI2OjE3IiBwYXRoPSJ1cmwiIHNjb3BlPSJFIiB4c2k6dHlwZT0iUHJvcGVydHkiLz4KICAgICAgICAgICAgICAgICAgPC9vcGVyYW5kPgogICAgICAgICAgICAgICAgICA8b3BlcmFuZCBsb2NhdG9yPSIyNjoyMS0yNjozMyIgdmFsdWVUeXBlPSJ0OlN0cmluZyIgdmFsdWU9Im9tYkNhdGVnb3J5IiB4c2k6dHlwZT0iTGl0ZXJhbCIvPgogICAgICAgICAgICAgICA8L29wZXJhbmQ+CiAgICAgICAgICAgICAgIDxvcGVyYW5kIGxvY2F0b3I9IjI3OjEyLTI3OjI5IiB4c2k6dHlwZT0iRXF1YWwiPgogICAgICAgICAgICAgICAgICA8b3BlcmFuZCBuYW1lPSJUb1N0cmluZyIgbGlicmFyeU5hbWU9IkZISVJIZWxwZXJzIiB4c2k6dHlwZT0iRnVuY3Rpb25SZWYiPgogICAgICAgICAgICAgICAgICAgICA8b3BlcmFuZCBsb2NhdG9yPSIyNzoxMi0yNzoxNiIgcGF0aD0idXJsIiBzY29wZT0iRSIgeHNpOnR5cGU9IlByb3BlcnR5Ii8+CiAgICAgICAgICAgICAgICAgIDwvb3BlcmFuZD4KICAgICAgICAgICAgICAgICAgPG9wZXJhbmQgbG9jYXRvcj0iMjc6MjAtMjc6MjkiIHZhbHVlVHlwZT0idDpTdHJpbmciIHZhbHVlPSJkZXRhaWxlZCIgeHNpOnR5cGU9IkxpdGVyYWwiLz4KICAgICAgICAgICAgICAgPC9vcGVyYW5kPgogICAgICAgICAgICA8L3doZXJlPgogICAgICAgICAgICA8cmV0dXJuIGxvY2F0b3I9IjI4OjctMjg6NDAiPgogICAgICAgICAgICAgICA8ZXhwcmVzc2lvbiBsb2NhdG9yPSIyODoxNC0yODo0MCIgbmFtZT0iVG9Db2RlIiBsaWJyYXJ5TmFtZT0iRkhJUkhlbHBlcnMiIHhzaTp0eXBlPSJGdW5jdGlvblJlZiI+CiAgICAgICAgICAgICAgICAgIDxvcGVyYW5kIGFzVHlwZT0iZmhpcjpDb2RpbmciIHhzaTp0eXBlPSJBcyI+CiAgICAgICAgICAgICAgICAgICAgIDxvcGVyYW5kIGxvY2F0b3I9IjI4OjMzLTI4OjM5IiBwYXRoPSJ2YWx1ZSIgc2NvcGU9IkUiIHhzaTp0eXBlPSJQcm9wZXJ0eSIvPgogICAgICAgICAgICAgICAgICA8L29wZXJhbmQ+CiAgICAgICAgICAgICAgIDwvZXhwcmVzc2lvbj4KICAgICAgICAgICAgPC9yZXR1cm4+CiAgICAgICAgIDwvZXhwcmVzc2lvbj4KICAgICAgPC9kZWY+CiAgICAgIDxkZWYgbG9jYXRvcj0iMzA6MS0zNzozIiBuYW1lPSJTREUgUGF5ZXIiIGNvbnRleHQ9IlBhdGllbnQiIGFjY2Vzc0xldmVsPSJQdWJsaWMiPgogICAgICAgICA8ZXhwcmVzc2lvbiBsb2NhdG9yPSIzMTozLTM3OjMiIHhzaTp0eXBlPSJGbGF0dGVuIj4KICAgICAgICAgICAgPG9wZXJhbmQgbG9jYXRvcj0iMzE6MTAtMzc6MyIgeHNpOnR5cGU9IlF1ZXJ5Ij4KICAgICAgICAgICAgICAgPHNvdXJjZSBsb2NhdG9yPSIzMToxMS0zMTo0MyIgYWxpYXM9IlBheWVyIj4KICAgICAgICAgICAgICAgICAgPGV4cHJlc3Npb24gbG9jYXRvcj0iMzE6MTEtMzE6MzciIGRhdGFUeXBlPSJmaGlyOkNvdmVyYWdlIiB0ZW1wbGF0ZUlkPSJodHRwOi8vaGw3Lm9yZy9maGlyL1N0cnVjdHVyZURlZmluaXRpb24vQ292ZXJhZ2UiIGNvZGVQcm9wZXJ0eT0idHlwZSIgY29kZUNvbXBhcmF0b3I9ImluIiB4c2k6dHlwZT0iUmV0cmlldmUiPgogICAgICAgICAgICAgICAgICAgICA8Y29kZXMgbG9jYXRvcj0iMzE6MzAtMzE6MzYiIG5hbWU9IlBheWVyIiBwcmVzZXJ2ZT0idHJ1ZSIgeHNpOnR5cGU9IlZhbHVlU2V0UmVmIi8+CiAgICAgICAgICAgICAgICAgIDwvZXhwcmVzc2lvbj4KICAgICAgICAgICAgICAgPC9zb3VyY2U+CiAgICAgICAgICAgICAgIDx3aGVyZSBsb2NhdG9yPSIzMjo1LTMyOjUyIiB4c2k6dHlwZT0iT3ZlcmxhcHMiPgogICAgICAgICAgICAgICAgICA8b3BlcmFuZCBuYW1lPSJUb0ludGVydmFsIiBsaWJyYXJ5TmFtZT0iRkhJUkhlbHBlcnMiIHhzaTp0eXBlPSJGdW5jdGlvblJlZiI+CiAgICAgICAgICAgICAgICAgICAgIDxvcGVyYW5kIGxvY2F0b3I9IjMyOjExLTMyOjIyIiBwYXRoPSJwZXJpb2QiIHNjb3BlPSJQYXllciIgeHNpOnR5cGU9IlByb3BlcnR5Ii8+CiAgICAgICAgICAgICAgICAgIDwvb3BlcmFuZD4KICAgICAgICAgICAgICAgICAgPG9wZXJhbmQgbG9jYXRvcj0iMzI6MzMtMzI6NTIiIG5hbWU9Ik1lYXN1cmVtZW50IFBlcmlvZCIgeHNpOnR5cGU9IlBhcmFtZXRlclJlZiIvPgogICAgICAgICAgICAgICA8L3doZXJlPgogICAgICAgICAgICAgICA8cmV0dXJuIGxvY2F0b3I9IjMzOjctMzY6NyI+CiAgICAgICAgICAgICAgICAgIDxleHByZXNzaW9uIGxvY2F0b3I9IjMzOjE0LTM2OjciIHhzaTp0eXBlPSJRdWVyeSI+CiAgICAgICAgICAgICAgICAgICAgIDxzb3VyY2UgbG9jYXRvcj0iMzQ6OS0zNDoyNyIgYWxpYXM9ImMiPgogICAgICAgICAgICAgICAgICAgICAgICA8ZXhwcmVzc2lvbiBsb2NhdG9yPSIzNDo5LTM0OjI1IiBwYXRoPSJjb2RpbmciIHhzaTp0eXBlPSJQcm9wZXJ0eSI+CiAgICAgICAgICAgICAgICAgICAgICAgICAgIDxzb3VyY2UgcGF0aD0idHlwZSIgc2NvcGU9IlBheWVyIiB4c2k6dHlwZT0iUHJvcGVydHkiLz4KICAgICAgICAgICAgICAgICAgICAgICAgPC9leHByZXNzaW9uPgogICAgICAgICAgICAgICAgICAgICA8L3NvdXJjZT4KICAgICAgICAgICAgICAgICAgICAgPHJldHVybiBsb2NhdG9yPSIzNToxMS0zNTozOCI+CiAgICAgICAgICAgICAgICAgICAgICAgIDxleHByZXNzaW9uIGxvY2F0b3I9IjM1OjE4LTM1OjM4IiBuYW1lPSJUb0NvZGUiIGxpYnJhcnlOYW1lPSJGSElSSGVscGVycyIgeHNpOnR5cGU9IkZ1bmN0aW9uUmVmIj4KICAgICAgICAgICAgICAgICAgICAgICAgICAgPG9wZXJhbmQgbG9jYXRvcj0iMzU6MzciIG5hbWU9ImMiIHhzaTp0eXBlPSJBbGlhc1JlZiIvPgogICAgICAgICAgICAgICAgICAgICAgICA8L2V4cHJlc3Npb24+CiAgICAgICAgICAgICAgICAgICAgIDwvcmV0dXJuPgogICAgICAgICAgICAgICAgICA8L2V4cHJlc3Npb24+CiAgICAgICAgICAgICAgIDwvcmV0dXJuPgogICAgICAgICAgICA8L29wZXJhbmQ+CiAgICAgICAgIDwvZXhwcmVzc2lvbj4KICAgICAgPC9kZWY+CiAgICAgIDxkZWYgbG9jYXRvcj0iMzk6MS00Nzo0MCIgbmFtZT0iU0RFIFJhY2UiIGNvbnRleHQ9IlBhdGllbnQiIGFjY2Vzc0xldmVsPSJQdWJsaWMiPgogICAgICAgICA8ZXhwcmVzc2lvbiBsb2NhdG9yPSI0MDozLTQ3OjQwIiB4c2k6dHlwZT0iUXVlcnkiPgogICAgICAgICAgICA8c291cmNlIGxvY2F0b3I9IjQwOjMtNDQ6OCIgYWxpYXM9IkUiPgogICAgICAgICAgICAgICA8ZXhwcmVzc2lvbiBsb2NhdG9yPSI0MDozLTQ0OjYiIHhzaTp0eXBlPSJGbGF0dGVuIj4KICAgICAgICAgICAgICAgICAgPG9wZXJhbmQgbG9jYXRvcj0iNDA6MTItNDQ6NSIgeHNpOnR5cGU9IlF1ZXJ5Ij4KICAgICAgICAgICAgICAgICAgICAgPHNvdXJjZSBsb2NhdG9yPSI0MTo3LTQxOjMzIiBhbGlhcz0iRXh0ZW5zaW9uIj4KICAgICAgICAgICAgICAgICAgICAgICAgPGV4cHJlc3Npb24gbG9jYXRvcj0iNDE6Ny00MToyMyIgcGF0aD0iZXh0ZW5zaW9uIiB4c2k6dHlwZT0iUHJvcGVydHkiPgogICAgICAgICAgICAgICAgICAgICAgICAgICA8c291cmNlIG5hbWU9IlBhdGllbnQiIHhzaTp0eXBlPSJFeHByZXNzaW9uUmVmIi8+CiAgICAgICAgICAgICAgICAgICAgICAgIDwvZXhwcmVzc2lvbj4KICAgICAgICAgICAgICAgICAgICAgPC9zb3VyY2U+CiAgICAgICAgICAgICAgICAgICAgIDx3aGVyZSBsb2NhdG9yPSI0Mjo5LTQyOjkyIiB4c2k6dHlwZT0iRXF1YWwiPgogICAgICAgICAgICAgICAgICAgICAgICA8b3BlcmFuZCBuYW1lPSJUb1N0cmluZyIgbGlicmFyeU5hbWU9IkZISVJIZWxwZXJzIiB4c2k6dHlwZT0iRnVuY3Rpb25SZWYiPgogICAgICAgICAgICAgICAgICAgICAgICAgICA8b3BlcmFuZCBsb2NhdG9yPSI0MjoxNS00MjoyNyIgcGF0aD0idXJsIiBzY29wZT0iRXh0ZW5zaW9uIiB4c2k6dHlwZT0iUHJvcGVydHkiLz4KICAgICAgICAgICAgICAgICAgICAgICAgPC9vcGVyYW5kPgogICAgICAgICAgICAgICAgICAgICAgICA8b3BlcmFuZCBsb2NhdG9yPSI0MjozMS00Mjo5MiIgdmFsdWVUeXBlPSJ0OlN0cmluZyIgdmFsdWU9Imh0dHA6Ly9obDcub3JnL2ZoaXIvdXMvY29yZS9TdHJ1Y3R1cmVEZWZpbml0aW9uL3VzLWNvcmUtcmFjZSIgeHNpOnR5cGU9IkxpdGVyYWwiLz4KICAgICAgICAgICAgICAgICAgICAgPC93aGVyZT4KICAgICAgICAgICAgICAgICAgICAgPHJldHVybiBsb2NhdG9yPSI0MzoxMS00MzozNiI+CiAgICAgICAgICAgICAgICAgICAgICAgIDxleHByZXNzaW9uIGxvY2F0b3I9IjQzOjE4LTQzOjM2IiBwYXRoPSJleHRlbnNpb24iIHNjb3BlPSJFeHRlbnNpb24iIHhzaTp0eXBlPSJQcm9wZXJ0eSIvPgogICAgICAgICAgICAgICAgICAgICA8L3JldHVybj4KICAgICAgICAgICAgICAgICAgPC9vcGVyYW5kPgogICAgICAgICAgICAgICA8L2V4cHJlc3Npb24+CiAgICAgICAgICAgIDwvc291cmNlPgogICAgICAgICAgICA8d2hlcmUgbG9jYXRvcj0iNDU6Ny00NjoyOSIgeHNpOnR5cGU9Ik9yIj4KICAgICAgICAgICAgICAgPG9wZXJhbmQgbG9jYXRvcj0iNDU6MTMtNDU6MzMiIHhzaTp0eXBlPSJFcXVhbCI+CiAgICAgICAgICAgICAgICAgIDxvcGVyYW5kIG5hbWU9IlRvU3RyaW5nIiBsaWJyYXJ5TmFtZT0iRkhJUkhlbHBlcnMiIHhzaTp0eXBlPSJGdW5jdGlvblJlZiI+CiAgICAgICAgICAgICAgICAgICAgIDxvcGVyYW5kIGxvY2F0b3I9IjQ1OjEzLTQ1OjE3IiBwYXRoPSJ1cmwiIHNjb3BlPSJFIiB4c2k6dHlwZT0iUHJvcGVydHkiLz4KICAgICAgICAgICAgICAgICAgPC9vcGVyYW5kPgogICAgICAgICAgICAgICAgICA8b3BlcmFuZCBsb2NhdG9yPSI0NToyMS00NTozMyIgdmFsdWVUeXBlPSJ0OlN0cmluZyIgdmFsdWU9Im9tYkNhdGVnb3J5IiB4c2k6dHlwZT0iTGl0ZXJhbCIvPgogICAgICAgICAgICAgICA8L29wZXJhbmQ+CiAgICAgICAgICAgICAgIDxvcGVyYW5kIGxvY2F0b3I9IjQ2OjEyLTQ2OjI5IiB4c2k6dHlwZT0iRXF1YWwiPgogICAgICAgICAgICAgICAgICA8b3BlcmFuZCBuYW1lPSJUb1N0cmluZyIgbGlicmFyeU5hbWU9IkZISVJIZWxwZXJzIiB4c2k6dHlwZT0iRnVuY3Rpb25SZWYiPgogICAgICAgICAgICAgICAgICAgICA8b3BlcmFuZCBsb2NhdG9yPSI0NjoxMi00NjoxNiIgcGF0aD0idXJsIiBzY29wZT0iRSIgeHNpOnR5cGU9IlByb3BlcnR5Ii8+CiAgICAgICAgICAgICAgICAgIDwvb3BlcmFuZD4KICAgICAgICAgICAgICAgICAgPG9wZXJhbmQgbG9jYXRvcj0iNDY6MjAtNDY6MjkiIHZhbHVlVHlwZT0idDpTdHJpbmciIHZhbHVlPSJkZXRhaWxlZCIgeHNpOnR5cGU9IkxpdGVyYWwiLz4KICAgICAgICAgICAgICAgPC9vcGVyYW5kPgogICAgICAgICAgICA8L3doZXJlPgogICAgICAgICAgICA8cmV0dXJuIGxvY2F0b3I9IjQ3OjctNDc6NDAiPgogICAgICAgICAgICAgICA8ZXhwcmVzc2lvbiBsb2NhdG9yPSI0NzoxNC00Nzo0MCIgbmFtZT0iVG9Db2RlIiBsaWJyYXJ5TmFtZT0iRkhJUkhlbHBlcnMiIHhzaTp0eXBlPSJGdW5jdGlvblJlZiI+CiAgICAgICAgICAgICAgICAgIDxvcGVyYW5kIGFzVHlwZT0iZmhpcjpDb2RpbmciIHhzaTp0eXBlPSJBcyI+CiAgICAgICAgICAgICAgICAgICAgIDxvcGVyYW5kIGxvY2F0b3I9IjQ3OjMzLTQ3OjM5IiBwYXRoPSJ2YWx1ZSIgc2NvcGU9IkUiIHhzaTp0eXBlPSJQcm9wZXJ0eSIvPgogICAgICAgICAgICAgICAgICA8L29wZXJhbmQ+CiAgICAgICAgICAgICAgIDwvZXhwcmVzc2lvbj4KICAgICAgICAgICAgPC9yZXR1cm4+CiAgICAgICAgIDwvZXhwcmVzc2lvbj4KICAgICAgPC9kZWY+CiAgICAgIDxkZWYgbG9jYXRvcj0iNDk6MS01NDo3IiBuYW1lPSJTREUgU2V4IiBjb250ZXh0PSJQYXRpZW50IiBhY2Nlc3NMZXZlbD0iUHVibGljIj4KICAgICAgICAgPGV4cHJlc3Npb24gbG9jYXRvcj0iNTA6My01NDo3IiB4c2k6dHlwZT0iQ2FzZSI+CiAgICAgICAgICAgIDxjYXNlSXRlbSBsb2NhdG9yPSI1MTo3LTUxOjEzMCI+CiAgICAgICAgICAgICAgIDx3aGVuIGxvY2F0b3I9IjUxOjEyLTUxOjM0IiB4c2k6dHlwZT0iRXF1YWwiPgogICAgICAgICAgICAgICAgICA8b3BlcmFuZCBuYW1lPSJUb1N0cmluZyIgbGlicmFyeU5hbWU9IkZISVJIZWxwZXJzIiB4c2k6dHlwZT0iRnVuY3Rpb25SZWYiPgogICAgICAgICAgICAgICAgICAgICA8b3BlcmFuZCBsb2NhdG9yPSI1MToxMi01MToyNSIgcGF0aD0iZ2VuZGVyIiB4c2k6dHlwZT0iUHJvcGVydHkiPgogICAgICAgICAgICAgICAgICAgICAgICA8c291cmNlIGxvY2F0b3I9IjUxOjEyLTUxOjE4IiBuYW1lPSJQYXRpZW50IiB4c2k6dHlwZT0iRXhwcmVzc2lvblJlZiIvPgogICAgICAgICAgICAgICAgICAgICA8L29wZXJhbmQ+CiAgICAgICAgICAgICAgICAgIDwvb3BlcmFuZD4KICAgICAgICAgICAgICAgICAgPG9wZXJhbmQgbG9jYXRvcj0iNTE6MjktNTE6MzQiIHZhbHVlVHlwZT0idDpTdHJpbmciIHZhbHVlPSJtYWxlIiB4c2k6dHlwZT0iTGl0ZXJhbCIvPgogICAgICAgICAgICAgICA8L3doZW4+CiAgICAgICAgICAgICAgIDx0aGVuIGxvY2F0b3I9IjUxOjQxLTUxOjEzMCIgY2xhc3NUeXBlPSJ0OkNvZGUiIHhzaTp0eXBlPSJJbnN0YW5jZSI+CiAgICAgICAgICAgICAgICAgIDxlbGVtZW50IG5hbWU9ImNvZGUiPgogICAgICAgICAgICAgICAgICAgICA8dmFsdWUgbG9jYXRvcj0iNTE6NTQtNTE6NTYiIHZhbHVlVHlwZT0idDpTdHJpbmciIHZhbHVlPSJNIiB4c2k6dHlwZT0iTGl0ZXJhbCIvPgogICAgICAgICAgICAgICAgICA8L2VsZW1lbnQ+CiAgICAgICAgICAgICAgICAgIDxlbGVtZW50IG5hbWU9InN5c3RlbSI+CiAgICAgICAgICAgICAgICAgICAgIDx2YWx1ZSBsb2NhdG9yPSI1MTo2Ny01MToxMTEiIHZhbHVlVHlwZT0idDpTdHJpbmciIHZhbHVlPSJodHRwOi8vaGw3Lm9yZy9maGlyL3YzL0FkbWluaXN0cmF0aXZlR2VuZGVyIiB4c2k6dHlwZT0iTGl0ZXJhbCIvPgogICAgICAgICAgICAgICAgICA8L2VsZW1lbnQ+CiAgICAgICAgICAgICAgICAgIDxlbGVtZW50IG5hbWU9ImRpc3BsYXkiPgogICAgICAgICAgICAgICAgICAgICA8dmFsdWUgbG9jYXRvcj0iNTE6MTIzLTUxOjEyOCIgdmFsdWVUeXBlPSJ0OlN0cmluZyIgdmFsdWU9Ik1hbGUiIHhzaTp0eXBlPSJMaXRlcmFsIi8+CiAgICAgICAgICAgICAgICAgIDwvZWxlbWVudD4KICAgICAgICAgICAgICAgPC90aGVuPgogICAgICAgICAgICA8L2Nhc2VJdGVtPgogICAgICAgICAgICA8Y2FzZUl0ZW0gbG9jYXRvcj0iNTI6Ny01MjoxMzQiPgogICAgICAgICAgICAgICA8d2hlbiBsb2NhdG9yPSI1MjoxMi01MjozNiIgeHNpOnR5cGU9IkVxdWFsIj4KICAgICAgICAgICAgICAgICAgPG9wZXJhbmQgbmFtZT0iVG9TdHJpbmciIGxpYnJhcnlOYW1lPSJGSElSSGVscGVycyIgeHNpOnR5cGU9IkZ1bmN0aW9uUmVmIj4KICAgICAgICAgICAgICAgICAgICAgPG9wZXJhbmQgbG9jYXRvcj0iNTI6MTItNTI6MjUiIHBhdGg9ImdlbmRlciIgeHNpOnR5cGU9IlByb3BlcnR5Ij4KICAgICAgICAgICAgICAgICAgICAgICAgPHNvdXJjZSBsb2NhdG9yPSI1MjoxMi01MjoxOCIgbmFtZT0iUGF0aWVudCIgeHNpOnR5cGU9IkV4cHJlc3Npb25SZWYiLz4KICAgICAgICAgICAgICAgICAgICAgPC9vcGVyYW5kPgogICAgICAgICAgICAgICAgICA8L29wZXJhbmQ+CiAgICAgICAgICAgICAgICAgIDxvcGVyYW5kIGxvY2F0b3I9IjUyOjI5LTUyOjM2IiB2YWx1ZVR5cGU9InQ6U3RyaW5nIiB2YWx1ZT0iZmVtYWxlIiB4c2k6dHlwZT0iTGl0ZXJhbCIvPgogICAgICAgICAgICAgICA8L3doZW4+CiAgICAgICAgICAgICAgIDx0aGVuIGxvY2F0b3I9IjUyOjQzLTUyOjEzNCIgY2xhc3NUeXBlPSJ0OkNvZGUiIHhzaTp0eXBlPSJJbnN0YW5jZSI+CiAgICAgICAgICAgICAgICAgIDxlbGVtZW50IG5hbWU9ImNvZGUiPgogICAgICAgICAgICAgICAgICAgICA8dmFsdWUgbG9jYXRvcj0iNTI6NTYtNTI6NTgiIHZhbHVlVHlwZT0idDpTdHJpbmciIHZhbHVlPSJGIiB4c2k6dHlwZT0iTGl0ZXJhbCIvPgogICAgICAgICAgICAgICAgICA8L2VsZW1lbnQ+CiAgICAgICAgICAgICAgICAgIDxlbGVtZW50IG5hbWU9InN5c3RlbSI+CiAgICAgICAgICAgICAgICAgICAgIDx2YWx1ZSBsb2NhdG9yPSI1Mjo2OS01MjoxMTMiIHZhbHVlVHlwZT0idDpTdHJpbmciIHZhbHVlPSJodHRwOi8vaGw3Lm9yZy9maGlyL3YzL0FkbWluaXN0cmF0aXZlR2VuZGVyIiB4c2k6dHlwZT0iTGl0ZXJhbCIvPgogICAgICAgICAgICAgICAgICA8L2VsZW1lbnQ+CiAgICAgICAgICAgICAgICAgIDxlbGVtZW50IG5hbWU9ImRpc3BsYXkiPgogICAgICAgICAgICAgICAgICAgICA8dmFsdWUgbG9jYXRvcj0iNTI6MTI1LTUyOjEzMiIgdmFsdWVUeXBlPSJ0OlN0cmluZyIgdmFsdWU9IkZlbWFsZSIgeHNpOnR5cGU9IkxpdGVyYWwiLz4KICAgICAgICAgICAgICAgICAgPC9lbGVtZW50PgogICAgICAgICAgICAgICA8L3RoZW4+CiAgICAgICAgICAgIDwvY2FzZUl0ZW0+CiAgICAgICAgICAgIDxlbHNlIGFzVHlwZT0idDpDb2RlIiB4c2k6dHlwZT0iQXMiPgogICAgICAgICAgICAgICA8b3BlcmFuZCBsb2NhdG9yPSI1MzoxMi01MzoxNSIgeHNpOnR5cGU9Ik51bGwiLz4KICAgICAgICAgICAgPC9lbHNlPgogICAgICAgICA8L2V4cHJlc3Npb24+CiAgICAgIDwvZGVmPgogICA8L3N0YXRlbWVudHM+CjwvbGlicmFyeT4K" + }, + { + "contentType": "application/elm+json", + "data": "ewogICAibGlicmFyeSIgOiB7CiAgICAgICJhbm5vdGF0aW9uIiA6IFsgewogICAgICAgICAidHJhbnNsYXRvclZlcnNpb24iIDogIjIuNy4wIiwKICAgICAgICAgInRyYW5zbGF0b3JPcHRpb25zIiA6ICJFbmFibGVMb2NhdG9ycyxEaXNhYmxlTGlzdERlbW90aW9uLERpc2FibGVMaXN0UHJvbW90aW9uIiwKICAgICAgICAgInR5cGUiIDogIkNxbFRvRWxtSW5mbyIKICAgICAgfSBdLAogICAgICAiaWRlbnRpZmllciIgOiB7CiAgICAgICAgICJpZCIgOiAiU0RFIiwKICAgICAgICAgInN5c3RlbSIgOiAiaHR0cDovL2NvbnRlbnQuYWxwaG9yYS5jb20vZmhpci9kcW0iCiAgICAgIH0sCiAgICAgICJzY2hlbWFJZGVudGlmaWVyIiA6IHsKICAgICAgICAgImlkIiA6ICJ1cm46aGw3LW9yZzplbG0iLAogICAgICAgICAidmVyc2lvbiIgOiAicjEiCiAgICAgIH0sCiAgICAgICJ1c2luZ3MiIDogewogICAgICAgICAiZGVmIiA6IFsgewogICAgICAgICAgICAibG9jYWxJZGVudGlmaWVyIiA6ICJTeXN0ZW0iLAogICAgICAgICAgICAidXJpIiA6ICJ1cm46aGw3LW9yZzplbG0tdHlwZXM6cjEiCiAgICAgICAgIH0sIHsKICAgICAgICAgICAgImxvY2F0b3IiIDogIjg6MS04OjI2IiwKICAgICAgICAgICAgImxvY2FsSWRlbnRpZmllciIgOiAiRkhJUiIsCiAgICAgICAgICAgICJ1cmkiIDogImh0dHA6Ly9obDcub3JnL2ZoaXIiLAogICAgICAgICAgICAidmVyc2lvbiIgOiAiNC4wLjEiLAogICAgICAgICAgICAiYW5ub3RhdGlvbiIgOiBbIHsKICAgICAgICAgICAgICAgInR5cGUiIDogIkFubm90YXRpb24iLAogICAgICAgICAgICAgICAidCIgOiBbIHsKICAgICAgICAgICAgICAgICAgIm5hbWUiIDogInVwZGF0ZSIKICAgICAgICAgICAgICAgfSBdCiAgICAgICAgICAgIH0gXQogICAgICAgICB9IF0KICAgICAgfSwKICAgICAgImluY2x1ZGVzIiA6IHsKICAgICAgICAgImRlZiIgOiBbIHsKICAgICAgICAgICAgImxvY2F0b3IiIDogIjEwOjEtMTA6NDEiLAogICAgICAgICAgICAibG9jYWxJZGVudGlmaWVyIiA6ICJGSElSSGVscGVycyIsCiAgICAgICAgICAgICJwYXRoIiA6ICJodHRwOi8vY29udGVudC5hbHBob3JhLmNvbS9maGlyL2RxbS9EUU1GSElSSGVscGVycyIKICAgICAgICAgfSBdCiAgICAgIH0sCiAgICAgICJwYXJhbWV0ZXJzIiA6IHsKICAgICAgICAgImRlZiIgOiBbIHsKICAgICAgICAgICAgImxvY2F0b3IiIDogIjE3OjEtMTc6OTkiLAogICAgICAgICAgICAibmFtZSIgOiAiTWVhc3VyZW1lbnQgUGVyaW9kIiwKICAgICAgICAgICAgImFjY2Vzc0xldmVsIiA6ICJQdWJsaWMiLAogICAgICAgICAgICAiZGVmYXVsdCIgOiB7CiAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIxNzo0MC0xNzo5OSIsCiAgICAgICAgICAgICAgICJsb3dDbG9zZWQiIDogdHJ1ZSwKICAgICAgICAgICAgICAgImhpZ2hDbG9zZWQiIDogZmFsc2UsCiAgICAgICAgICAgICAgICJ0eXBlIiA6ICJJbnRlcnZhbCIsCiAgICAgICAgICAgICAgICJsb3ciIDogewogICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMTc6NDktMTc6NzIiLAogICAgICAgICAgICAgICAgICAidHlwZSIgOiAiRGF0ZVRpbWUiLAogICAgICAgICAgICAgICAgICAieWVhciIgOiB7CiAgICAgICAgICAgICAgICAgICAgICJ2YWx1ZVR5cGUiIDogInt1cm46aGw3LW9yZzplbG0tdHlwZXM6cjF9SW50ZWdlciIsCiAgICAgICAgICAgICAgICAgICAgICJ2YWx1ZSIgOiAiMjAyMCIsCiAgICAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJMaXRlcmFsIgogICAgICAgICAgICAgICAgICB9LAogICAgICAgICAgICAgICAgICAibW9udGgiIDogewogICAgICAgICAgICAgICAgICAgICAidmFsdWVUeXBlIiA6ICJ7dXJuOmhsNy1vcmc6ZWxtLXR5cGVzOnIxfUludGVnZXIiLAogICAgICAgICAgICAgICAgICAgICAidmFsdWUiIDogIjEiLAogICAgICAgICAgICAgICAgICAgICAidHlwZSIgOiAiTGl0ZXJhbCIKICAgICAgICAgICAgICAgICAgfSwKICAgICAgICAgICAgICAgICAgImRheSIgOiB7CiAgICAgICAgICAgICAgICAgICAgICJ2YWx1ZVR5cGUiIDogInt1cm46aGw3LW9yZzplbG0tdHlwZXM6cjF9SW50ZWdlciIsCiAgICAgICAgICAgICAgICAgICAgICJ2YWx1ZSIgOiAiMSIsCiAgICAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJMaXRlcmFsIgogICAgICAgICAgICAgICAgICB9LAogICAgICAgICAgICAgICAgICAiaG91ciIgOiB7CiAgICAgICAgICAgICAgICAgICAgICJ2YWx1ZVR5cGUiIDogInt1cm46aGw3LW9yZzplbG0tdHlwZXM6cjF9SW50ZWdlciIsCiAgICAgICAgICAgICAgICAgICAgICJ2YWx1ZSIgOiAiMCIsCiAgICAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJMaXRlcmFsIgogICAgICAgICAgICAgICAgICB9LAogICAgICAgICAgICAgICAgICAibWludXRlIiA6IHsKICAgICAgICAgICAgICAgICAgICAgInZhbHVlVHlwZSIgOiAie3VybjpobDctb3JnOmVsbS10eXBlczpyMX1JbnRlZ2VyIiwKICAgICAgICAgICAgICAgICAgICAgInZhbHVlIiA6ICIwIiwKICAgICAgICAgICAgICAgICAgICAgInR5cGUiIDogIkxpdGVyYWwiCiAgICAgICAgICAgICAgICAgIH0sCiAgICAgICAgICAgICAgICAgICJzZWNvbmQiIDogewogICAgICAgICAgICAgICAgICAgICAidmFsdWVUeXBlIiA6ICJ7dXJuOmhsNy1vcmc6ZWxtLXR5cGVzOnIxfUludGVnZXIiLAogICAgICAgICAgICAgICAgICAgICAidmFsdWUiIDogIjAiLAogICAgICAgICAgICAgICAgICAgICAidHlwZSIgOiAiTGl0ZXJhbCIKICAgICAgICAgICAgICAgICAgfSwKICAgICAgICAgICAgICAgICAgIm1pbGxpc2Vjb25kIiA6IHsKICAgICAgICAgICAgICAgICAgICAgInZhbHVlVHlwZSIgOiAie3VybjpobDctb3JnOmVsbS10eXBlczpyMX1JbnRlZ2VyIiwKICAgICAgICAgICAgICAgICAgICAgInZhbHVlIiA6ICIwIiwKICAgICAgICAgICAgICAgICAgICAgInR5cGUiIDogIkxpdGVyYWwiCiAgICAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgICAgfSwKICAgICAgICAgICAgICAgImhpZ2giIDogewogICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMTc6NzUtMTc6OTgiLAogICAgICAgICAgICAgICAgICAidHlwZSIgOiAiRGF0ZVRpbWUiLAogICAgICAgICAgICAgICAgICAieWVhciIgOiB7CiAgICAgICAgICAgICAgICAgICAgICJ2YWx1ZVR5cGUiIDogInt1cm46aGw3LW9yZzplbG0tdHlwZXM6cjF9SW50ZWdlciIsCiAgICAgICAgICAgICAgICAgICAgICJ2YWx1ZSIgOiAiMjAyMSIsCiAgICAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJMaXRlcmFsIgogICAgICAgICAgICAgICAgICB9LAogICAgICAgICAgICAgICAgICAibW9udGgiIDogewogICAgICAgICAgICAgICAgICAgICAidmFsdWVUeXBlIiA6ICJ7dXJuOmhsNy1vcmc6ZWxtLXR5cGVzOnIxfUludGVnZXIiLAogICAgICAgICAgICAgICAgICAgICAidmFsdWUiIDogIjEiLAogICAgICAgICAgICAgICAgICAgICAidHlwZSIgOiAiTGl0ZXJhbCIKICAgICAgICAgICAgICAgICAgfSwKICAgICAgICAgICAgICAgICAgImRheSIgOiB7CiAgICAgICAgICAgICAgICAgICAgICJ2YWx1ZVR5cGUiIDogInt1cm46aGw3LW9yZzplbG0tdHlwZXM6cjF9SW50ZWdlciIsCiAgICAgICAgICAgICAgICAgICAgICJ2YWx1ZSIgOiAiMSIsCiAgICAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJMaXRlcmFsIgogICAgICAgICAgICAgICAgICB9LAogICAgICAgICAgICAgICAgICAiaG91ciIgOiB7CiAgICAgICAgICAgICAgICAgICAgICJ2YWx1ZVR5cGUiIDogInt1cm46aGw3LW9yZzplbG0tdHlwZXM6cjF9SW50ZWdlciIsCiAgICAgICAgICAgICAgICAgICAgICJ2YWx1ZSIgOiAiMCIsCiAgICAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJMaXRlcmFsIgogICAgICAgICAgICAgICAgICB9LAogICAgICAgICAgICAgICAgICAibWludXRlIiA6IHsKICAgICAgICAgICAgICAgICAgICAgInZhbHVlVHlwZSIgOiAie3VybjpobDctb3JnOmVsbS10eXBlczpyMX1JbnRlZ2VyIiwKICAgICAgICAgICAgICAgICAgICAgInZhbHVlIiA6ICIwIiwKICAgICAgICAgICAgICAgICAgICAgInR5cGUiIDogIkxpdGVyYWwiCiAgICAgICAgICAgICAgICAgIH0sCiAgICAgICAgICAgICAgICAgICJzZWNvbmQiIDogewogICAgICAgICAgICAgICAgICAgICAidmFsdWVUeXBlIiA6ICJ7dXJuOmhsNy1vcmc6ZWxtLXR5cGVzOnIxfUludGVnZXIiLAogICAgICAgICAgICAgICAgICAgICAidmFsdWUiIDogIjAiLAogICAgICAgICAgICAgICAgICAgICAidHlwZSIgOiAiTGl0ZXJhbCIKICAgICAgICAgICAgICAgICAgfSwKICAgICAgICAgICAgICAgICAgIm1pbGxpc2Vjb25kIiA6IHsKICAgICAgICAgICAgICAgICAgICAgInZhbHVlVHlwZSIgOiAie3VybjpobDctb3JnOmVsbS10eXBlczpyMX1JbnRlZ2VyIiwKICAgICAgICAgICAgICAgICAgICAgInZhbHVlIiA6ICIwIiwKICAgICAgICAgICAgICAgICAgICAgInR5cGUiIDogIkxpdGVyYWwiCiAgICAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgICAgfQogICAgICAgICAgICB9CiAgICAgICAgIH0gXQogICAgICB9LAogICAgICAidmFsdWVTZXRzIiA6IHsKICAgICAgICAgImRlZiIgOiBbIHsKICAgICAgICAgICAgImxvY2F0b3IiIDogIjEyOjEtMTI6ODciLAogICAgICAgICAgICAibmFtZSIgOiAiRXRobmljaXR5IiwKICAgICAgICAgICAgImlkIiA6ICJodHRwOi8vY3RzLm5sbS5uaWguZ292L2ZoaXIvVmFsdWVTZXQvMi4xNi44NDAuMS4xMTQyMjIuNC4xMS44MzciLAogICAgICAgICAgICAiYWNjZXNzTGV2ZWwiIDogIlB1YmxpYyIKICAgICAgICAgfSwgewogICAgICAgICAgICAibG9jYXRvciIgOiAiMTM6MS0xMzo5NyIsCiAgICAgICAgICAgICJuYW1lIiA6ICJPTkMgQWRtaW5pc3RyYXRpdmUgU2V4IiwKICAgICAgICAgICAgImlkIiA6ICJodHRwOi8vY3RzLm5sbS5uaWguZ292L2ZoaXIvVmFsdWVTZXQvMi4xNi44NDAuMS4xMTM3NjIuMS40LjEiLAogICAgICAgICAgICAiYWNjZXNzTGV2ZWwiIDogIlB1YmxpYyIKICAgICAgICAgfSwgewogICAgICAgICAgICAibG9jYXRvciIgOiAiMTQ6MS0xNDo4NCIsCiAgICAgICAgICAgICJuYW1lIiA6ICJQYXllciIsCiAgICAgICAgICAgICJpZCIgOiAiaHR0cDovL2N0cy5ubG0ubmloLmdvdi9maGlyL1ZhbHVlU2V0LzIuMTYuODQwLjEuMTE0MjIyLjQuMTEuMzU5MSIsCiAgICAgICAgICAgICJhY2Nlc3NMZXZlbCIgOiAiUHVibGljIgogICAgICAgICB9LCB7CiAgICAgICAgICAgICJsb2NhdG9yIiA6ICIxNToxLTE1OjgyIiwKICAgICAgICAgICAgIm5hbWUiIDogIlJhY2UiLAogICAgICAgICAgICAiaWQiIDogImh0dHA6Ly9jdHMubmxtLm5paC5nb3YvZmhpci9WYWx1ZVNldC8yLjE2Ljg0MC4xLjExNDIyMi40LjExLjgzNiIsCiAgICAgICAgICAgICJhY2Nlc3NMZXZlbCIgOiAiUHVibGljIgogICAgICAgICB9IF0KICAgICAgfSwKICAgICAgImNvbnRleHRzIiA6IHsKICAgICAgICAgImRlZiIgOiBbIHsKICAgICAgICAgICAgImxvY2F0b3IiIDogIjE4OjEtMTg6MTUiLAogICAgICAgICAgICAibmFtZSIgOiAiUGF0aWVudCIKICAgICAgICAgfSBdCiAgICAgIH0sCiAgICAgICJzdGF0ZW1lbnRzIiA6IHsKICAgICAgICAgImRlZiIgOiBbIHsKICAgICAgICAgICAgImxvY2F0b3IiIDogIjE4OjEtMTg6MTUiLAogICAgICAgICAgICAibmFtZSIgOiAiUGF0aWVudCIsCiAgICAgICAgICAgICJjb250ZXh0IiA6ICJQYXRpZW50IiwKICAgICAgICAgICAgImV4cHJlc3Npb24iIDogewogICAgICAgICAgICAgICAidHlwZSIgOiAiU2luZ2xldG9uRnJvbSIsCiAgICAgICAgICAgICAgICJvcGVyYW5kIiA6IHsKICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjE4OjEtMTg6MTUiLAogICAgICAgICAgICAgICAgICAiZGF0YVR5cGUiIDogIntodHRwOi8vaGw3Lm9yZy9maGlyfVBhdGllbnQiLAogICAgICAgICAgICAgICAgICAidGVtcGxhdGVJZCIgOiAiaHR0cDovL2hsNy5vcmcvZmhpci9TdHJ1Y3R1cmVEZWZpbml0aW9uL1BhdGllbnQiLAogICAgICAgICAgICAgICAgICAidHlwZSIgOiAiUmV0cmlldmUiCiAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgfQogICAgICAgICB9LCB7CiAgICAgICAgICAgICJsb2NhdG9yIiA6ICIyMDoxLTI4OjQwIiwKICAgICAgICAgICAgIm5hbWUiIDogIlNERSBFdGhuaWNpdHkiLAogICAgICAgICAgICAiY29udGV4dCIgOiAiUGF0aWVudCIsCiAgICAgICAgICAgICJhY2Nlc3NMZXZlbCIgOiAiUHVibGljIiwKICAgICAgICAgICAgImV4cHJlc3Npb24iIDogewogICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMjE6My0yODo0MCIsCiAgICAgICAgICAgICAgICJ0eXBlIiA6ICJRdWVyeSIsCiAgICAgICAgICAgICAgICJzb3VyY2UiIDogWyB7CiAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIyMTozLTI1OjgiLAogICAgICAgICAgICAgICAgICAiYWxpYXMiIDogIkUiLAogICAgICAgICAgICAgICAgICAiZXhwcmVzc2lvbiIgOiB7CiAgICAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIyMTozLTI1OjYiLAogICAgICAgICAgICAgICAgICAgICAidHlwZSIgOiAiRmxhdHRlbiIsCiAgICAgICAgICAgICAgICAgICAgICJvcGVyYW5kIiA6IHsKICAgICAgICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjIxOjEyLTI1OjUiLAogICAgICAgICAgICAgICAgICAgICAgICAidHlwZSIgOiAiUXVlcnkiLAogICAgICAgICAgICAgICAgICAgICAgICAic291cmNlIiA6IFsgewogICAgICAgICAgICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMjI6Ny0yMjozMyIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICJhbGlhcyIgOiAiRXh0ZW5zaW9uIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgImV4cHJlc3Npb24iIDogewogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMjI6Ny0yMjoyMyIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJwYXRoIiA6ICJleHRlbnNpb24iLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAidHlwZSIgOiAiUHJvcGVydHkiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAic291cmNlIiA6IHsKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIm5hbWUiIDogIlBhdGllbnQiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAidHlwZSIgOiAiRXhwcmVzc2lvblJlZiIKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfQogICAgICAgICAgICAgICAgICAgICAgICAgICB9CiAgICAgICAgICAgICAgICAgICAgICAgIH0gXSwKICAgICAgICAgICAgICAgICAgICAgICAgInJlbGF0aW9uc2hpcCIgOiBbIF0sCiAgICAgICAgICAgICAgICAgICAgICAgICJ3aGVyZSIgOiB7CiAgICAgICAgICAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIyMzo5LTIzOjk3IiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgInR5cGUiIDogIkVxdWFsIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgIm9wZXJhbmQiIDogWyB7CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJUb1N0cmluZyIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJsaWJyYXJ5TmFtZSIgOiAiRkhJUkhlbHBlcnMiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAidHlwZSIgOiAiRnVuY3Rpb25SZWYiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAib3BlcmFuZCIgOiBbIHsKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjIzOjE1LTIzOjI3IiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInBhdGgiIDogInVybCIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJzY29wZSIgOiAiRXh0ZW5zaW9uIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInR5cGUiIDogIlByb3BlcnR5IgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICB9IF0KICAgICAgICAgICAgICAgICAgICAgICAgICAgfSwgewogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMjM6MzEtMjM6OTciLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAidmFsdWVUeXBlIiA6ICJ7dXJuOmhsNy1vcmc6ZWxtLXR5cGVzOnIxfVN0cmluZyIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJ2YWx1ZSIgOiAiaHR0cDovL2hsNy5vcmcvZmhpci91cy9jb3JlL1N0cnVjdHVyZURlZmluaXRpb24vdXMtY29yZS1ldGhuaWNpdHkiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAidHlwZSIgOiAiTGl0ZXJhbCIKICAgICAgICAgICAgICAgICAgICAgICAgICAgfSBdCiAgICAgICAgICAgICAgICAgICAgICAgIH0sCiAgICAgICAgICAgICAgICAgICAgICAgICJyZXR1cm4iIDogewogICAgICAgICAgICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMjQ6MTEtMjQ6MzYiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAiZXhwcmVzc2lvbiIgOiB7CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIyNDoxOC0yNDozNiIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJwYXRoIiA6ICJleHRlbnNpb24iLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAic2NvcGUiIDogIkV4dGVuc2lvbiIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJQcm9wZXJ0eSIKICAgICAgICAgICAgICAgICAgICAgICAgICAgfQogICAgICAgICAgICAgICAgICAgICAgICB9CiAgICAgICAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgICAgICAgfQogICAgICAgICAgICAgICB9IF0sCiAgICAgICAgICAgICAgICJyZWxhdGlvbnNoaXAiIDogWyBdLAogICAgICAgICAgICAgICAid2hlcmUiIDogewogICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMjY6Ny0yNzoyOSIsCiAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJPciIsCiAgICAgICAgICAgICAgICAgICJvcGVyYW5kIiA6IFsgewogICAgICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMjY6MTMtMjY6MzMiLAogICAgICAgICAgICAgICAgICAgICAidHlwZSIgOiAiRXF1YWwiLAogICAgICAgICAgICAgICAgICAgICAib3BlcmFuZCIgOiBbIHsKICAgICAgICAgICAgICAgICAgICAgICAgIm5hbWUiIDogIlRvU3RyaW5nIiwKICAgICAgICAgICAgICAgICAgICAgICAgImxpYnJhcnlOYW1lIiA6ICJGSElSSGVscGVycyIsCiAgICAgICAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJGdW5jdGlvblJlZiIsCiAgICAgICAgICAgICAgICAgICAgICAgICJvcGVyYW5kIiA6IFsgewogICAgICAgICAgICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMjY6MTMtMjY6MTciLAogICAgICAgICAgICAgICAgICAgICAgICAgICAicGF0aCIgOiAidXJsIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgInNjb3BlIiA6ICJFIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgInR5cGUiIDogIlByb3BlcnR5IgogICAgICAgICAgICAgICAgICAgICAgICB9IF0KICAgICAgICAgICAgICAgICAgICAgfSwgewogICAgICAgICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMjY6MjEtMjY6MzMiLAogICAgICAgICAgICAgICAgICAgICAgICAidmFsdWVUeXBlIiA6ICJ7dXJuOmhsNy1vcmc6ZWxtLXR5cGVzOnIxfVN0cmluZyIsCiAgICAgICAgICAgICAgICAgICAgICAgICJ2YWx1ZSIgOiAib21iQ2F0ZWdvcnkiLAogICAgICAgICAgICAgICAgICAgICAgICAidHlwZSIgOiAiTGl0ZXJhbCIKICAgICAgICAgICAgICAgICAgICAgfSBdCiAgICAgICAgICAgICAgICAgIH0sIHsKICAgICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjI3OjEyLTI3OjI5IiwKICAgICAgICAgICAgICAgICAgICAgInR5cGUiIDogIkVxdWFsIiwKICAgICAgICAgICAgICAgICAgICAgIm9wZXJhbmQiIDogWyB7CiAgICAgICAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJUb1N0cmluZyIsCiAgICAgICAgICAgICAgICAgICAgICAgICJsaWJyYXJ5TmFtZSIgOiAiRkhJUkhlbHBlcnMiLAogICAgICAgICAgICAgICAgICAgICAgICAidHlwZSIgOiAiRnVuY3Rpb25SZWYiLAogICAgICAgICAgICAgICAgICAgICAgICAib3BlcmFuZCIgOiBbIHsKICAgICAgICAgICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjI3OjEyLTI3OjE2IiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgInBhdGgiIDogInVybCIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICJzY29wZSIgOiAiRSIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJQcm9wZXJ0eSIKICAgICAgICAgICAgICAgICAgICAgICAgfSBdCiAgICAgICAgICAgICAgICAgICAgIH0sIHsKICAgICAgICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjI3OjIwLTI3OjI5IiwKICAgICAgICAgICAgICAgICAgICAgICAgInZhbHVlVHlwZSIgOiAie3VybjpobDctb3JnOmVsbS10eXBlczpyMX1TdHJpbmciLAogICAgICAgICAgICAgICAgICAgICAgICAidmFsdWUiIDogImRldGFpbGVkIiwKICAgICAgICAgICAgICAgICAgICAgICAgInR5cGUiIDogIkxpdGVyYWwiCiAgICAgICAgICAgICAgICAgICAgIH0gXQogICAgICAgICAgICAgICAgICB9IF0KICAgICAgICAgICAgICAgfSwKICAgICAgICAgICAgICAgInJldHVybiIgOiB7CiAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIyODo3LTI4OjQwIiwKICAgICAgICAgICAgICAgICAgImV4cHJlc3Npb24iIDogewogICAgICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMjg6MTQtMjg6NDAiLAogICAgICAgICAgICAgICAgICAgICAibmFtZSIgOiAiVG9Db2RlIiwKICAgICAgICAgICAgICAgICAgICAgImxpYnJhcnlOYW1lIiA6ICJGSElSSGVscGVycyIsCiAgICAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJGdW5jdGlvblJlZiIsCiAgICAgICAgICAgICAgICAgICAgICJvcGVyYW5kIiA6IFsgewogICAgICAgICAgICAgICAgICAgICAgICAiYXNUeXBlIiA6ICJ7aHR0cDovL2hsNy5vcmcvZmhpcn1Db2RpbmciLAogICAgICAgICAgICAgICAgICAgICAgICAidHlwZSIgOiAiQXMiLAogICAgICAgICAgICAgICAgICAgICAgICAib3BlcmFuZCIgOiB7CiAgICAgICAgICAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIyODozMy0yODozOSIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICJwYXRoIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICJzY29wZSIgOiAiRSIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJQcm9wZXJ0eSIKICAgICAgICAgICAgICAgICAgICAgICAgfQogICAgICAgICAgICAgICAgICAgICB9IF0KICAgICAgICAgICAgICAgICAgfQogICAgICAgICAgICAgICB9CiAgICAgICAgICAgIH0KICAgICAgICAgfSwgewogICAgICAgICAgICAibG9jYXRvciIgOiAiMzA6MS0zNzozIiwKICAgICAgICAgICAgIm5hbWUiIDogIlNERSBQYXllciIsCiAgICAgICAgICAgICJjb250ZXh0IiA6ICJQYXRpZW50IiwKICAgICAgICAgICAgImFjY2Vzc0xldmVsIiA6ICJQdWJsaWMiLAogICAgICAgICAgICAiZXhwcmVzc2lvbiIgOiB7CiAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIzMTozLTM3OjMiLAogICAgICAgICAgICAgICAidHlwZSIgOiAiRmxhdHRlbiIsCiAgICAgICAgICAgICAgICJvcGVyYW5kIiA6IHsKICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjMxOjEwLTM3OjMiLAogICAgICAgICAgICAgICAgICAidHlwZSIgOiAiUXVlcnkiLAogICAgICAgICAgICAgICAgICAic291cmNlIiA6IFsgewogICAgICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMzE6MTEtMzE6NDMiLAogICAgICAgICAgICAgICAgICAgICAiYWxpYXMiIDogIlBheWVyIiwKICAgICAgICAgICAgICAgICAgICAgImV4cHJlc3Npb24iIDogewogICAgICAgICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMzE6MTEtMzE6MzciLAogICAgICAgICAgICAgICAgICAgICAgICAiZGF0YVR5cGUiIDogIntodHRwOi8vaGw3Lm9yZy9maGlyfUNvdmVyYWdlIiwKICAgICAgICAgICAgICAgICAgICAgICAgInRlbXBsYXRlSWQiIDogImh0dHA6Ly9obDcub3JnL2ZoaXIvU3RydWN0dXJlRGVmaW5pdGlvbi9Db3ZlcmFnZSIsCiAgICAgICAgICAgICAgICAgICAgICAgICJjb2RlUHJvcGVydHkiIDogInR5cGUiLAogICAgICAgICAgICAgICAgICAgICAgICAiY29kZUNvbXBhcmF0b3IiIDogImluIiwKICAgICAgICAgICAgICAgICAgICAgICAgInR5cGUiIDogIlJldHJpZXZlIiwKICAgICAgICAgICAgICAgICAgICAgICAgImNvZGVzIiA6IHsKICAgICAgICAgICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjMxOjMwLTMxOjM2IiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgIm5hbWUiIDogIlBheWVyIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgInByZXNlcnZlIiA6IHRydWUsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJWYWx1ZVNldFJlZiIKICAgICAgICAgICAgICAgICAgICAgICAgfQogICAgICAgICAgICAgICAgICAgICB9CiAgICAgICAgICAgICAgICAgIH0gXSwKICAgICAgICAgICAgICAgICAgInJlbGF0aW9uc2hpcCIgOiBbIF0sCiAgICAgICAgICAgICAgICAgICJ3aGVyZSIgOiB7CiAgICAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIzMjo1LTMyOjUyIiwKICAgICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk92ZXJsYXBzIiwKICAgICAgICAgICAgICAgICAgICAgIm9wZXJhbmQiIDogWyB7CiAgICAgICAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJUb0ludGVydmFsIiwKICAgICAgICAgICAgICAgICAgICAgICAgImxpYnJhcnlOYW1lIiA6ICJGSElSSGVscGVycyIsCiAgICAgICAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJGdW5jdGlvblJlZiIsCiAgICAgICAgICAgICAgICAgICAgICAgICJvcGVyYW5kIiA6IFsgewogICAgICAgICAgICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMzI6MTEtMzI6MjIiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAicGF0aCIgOiAicGVyaW9kIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgInNjb3BlIiA6ICJQYXllciIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJQcm9wZXJ0eSIKICAgICAgICAgICAgICAgICAgICAgICAgfSBdCiAgICAgICAgICAgICAgICAgICAgIH0sIHsKICAgICAgICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjMyOjMzLTMyOjUyIiwKICAgICAgICAgICAgICAgICAgICAgICAgIm5hbWUiIDogIk1lYXN1cmVtZW50IFBlcmlvZCIsCiAgICAgICAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJQYXJhbWV0ZXJSZWYiCiAgICAgICAgICAgICAgICAgICAgIH0gXQogICAgICAgICAgICAgICAgICB9LAogICAgICAgICAgICAgICAgICAicmV0dXJuIiA6IHsKICAgICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjMzOjctMzY6NyIsCiAgICAgICAgICAgICAgICAgICAgICJleHByZXNzaW9uIiA6IHsKICAgICAgICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjMzOjE0LTM2OjciLAogICAgICAgICAgICAgICAgICAgICAgICAidHlwZSIgOiAiUXVlcnkiLAogICAgICAgICAgICAgICAgICAgICAgICAic291cmNlIiA6IFsgewogICAgICAgICAgICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMzQ6OS0zNDoyNyIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICJhbGlhcyIgOiAiYyIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICJleHByZXNzaW9uIiA6IHsKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjM0OjktMzQ6MjUiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAicGF0aCIgOiAiY29kaW5nIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInR5cGUiIDogIlByb3BlcnR5IiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInNvdXJjZSIgOiB7CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJwYXRoIiA6ICJ0eXBlIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInNjb3BlIiA6ICJQYXllciIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJQcm9wZXJ0eSIKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfQogICAgICAgICAgICAgICAgICAgICAgICAgICB9CiAgICAgICAgICAgICAgICAgICAgICAgIH0gXSwKICAgICAgICAgICAgICAgICAgICAgICAgInJlbGF0aW9uc2hpcCIgOiBbIF0sCiAgICAgICAgICAgICAgICAgICAgICAgICJyZXR1cm4iIDogewogICAgICAgICAgICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMzU6MTEtMzU6MzgiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAiZXhwcmVzc2lvbiIgOiB7CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIzNToxOC0zNTozOCIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJUb0NvZGUiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAibGlicmFyeU5hbWUiIDogIkZISVJIZWxwZXJzIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInR5cGUiIDogIkZ1bmN0aW9uUmVmIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIm9wZXJhbmQiIDogWyB7CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIzNTozNyIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJjIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInR5cGUiIDogIkFsaWFzUmVmIgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICB9IF0KICAgICAgICAgICAgICAgICAgICAgICAgICAgfQogICAgICAgICAgICAgICAgICAgICAgICB9CiAgICAgICAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgICAgICAgfQogICAgICAgICAgICAgICB9CiAgICAgICAgICAgIH0KICAgICAgICAgfSwgewogICAgICAgICAgICAibG9jYXRvciIgOiAiMzk6MS00Nzo0MCIsCiAgICAgICAgICAgICJuYW1lIiA6ICJTREUgUmFjZSIsCiAgICAgICAgICAgICJjb250ZXh0IiA6ICJQYXRpZW50IiwKICAgICAgICAgICAgImFjY2Vzc0xldmVsIiA6ICJQdWJsaWMiLAogICAgICAgICAgICAiZXhwcmVzc2lvbiIgOiB7CiAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICI0MDozLTQ3OjQwIiwKICAgICAgICAgICAgICAgInR5cGUiIDogIlF1ZXJ5IiwKICAgICAgICAgICAgICAgInNvdXJjZSIgOiBbIHsKICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjQwOjMtNDQ6OCIsCiAgICAgICAgICAgICAgICAgICJhbGlhcyIgOiAiRSIsCiAgICAgICAgICAgICAgICAgICJleHByZXNzaW9uIiA6IHsKICAgICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjQwOjMtNDQ6NiIsCiAgICAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJGbGF0dGVuIiwKICAgICAgICAgICAgICAgICAgICAgIm9wZXJhbmQiIDogewogICAgICAgICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiNDA6MTItNDQ6NSIsCiAgICAgICAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJRdWVyeSIsCiAgICAgICAgICAgICAgICAgICAgICAgICJzb3VyY2UiIDogWyB7CiAgICAgICAgICAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICI0MTo3LTQxOjMzIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgImFsaWFzIiA6ICJFeHRlbnNpb24iLAogICAgICAgICAgICAgICAgICAgICAgICAgICAiZXhwcmVzc2lvbiIgOiB7CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICI0MTo3LTQxOjIzIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInBhdGgiIDogImV4dGVuc2lvbiIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJQcm9wZXJ0eSIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJzb3VyY2UiIDogewogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAibmFtZSIgOiAiUGF0aWVudCIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJFeHByZXNzaW9uUmVmIgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICB9CiAgICAgICAgICAgICAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgICAgICAgICAgICAgfSBdLAogICAgICAgICAgICAgICAgICAgICAgICAicmVsYXRpb25zaGlwIiA6IFsgXSwKICAgICAgICAgICAgICAgICAgICAgICAgIndoZXJlIiA6IHsKICAgICAgICAgICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjQyOjktNDI6OTIiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAidHlwZSIgOiAiRXF1YWwiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAib3BlcmFuZCIgOiBbIHsKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIm5hbWUiIDogIlRvU3RyaW5nIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgImxpYnJhcnlOYW1lIiA6ICJGSElSSGVscGVycyIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJGdW5jdGlvblJlZiIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJvcGVyYW5kIiA6IFsgewogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiNDI6MTUtNDI6MjciLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAicGF0aCIgOiAidXJsIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInNjb3BlIiA6ICJFeHRlbnNpb24iLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAidHlwZSIgOiAiUHJvcGVydHkiCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIH0gXQogICAgICAgICAgICAgICAgICAgICAgICAgICB9LCB7CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICI0MjozMS00Mjo5MiIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJ2YWx1ZVR5cGUiIDogInt1cm46aGw3LW9yZzplbG0tdHlwZXM6cjF9U3RyaW5nIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInZhbHVlIiA6ICJodHRwOi8vaGw3Lm9yZy9maGlyL3VzL2NvcmUvU3RydWN0dXJlRGVmaW5pdGlvbi91cy1jb3JlLXJhY2UiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAidHlwZSIgOiAiTGl0ZXJhbCIKICAgICAgICAgICAgICAgICAgICAgICAgICAgfSBdCiAgICAgICAgICAgICAgICAgICAgICAgIH0sCiAgICAgICAgICAgICAgICAgICAgICAgICJyZXR1cm4iIDogewogICAgICAgICAgICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiNDM6MTEtNDM6MzYiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAiZXhwcmVzc2lvbiIgOiB7CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICI0MzoxOC00MzozNiIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJwYXRoIiA6ICJleHRlbnNpb24iLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAic2NvcGUiIDogIkV4dGVuc2lvbiIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJQcm9wZXJ0eSIKICAgICAgICAgICAgICAgICAgICAgICAgICAgfQogICAgICAgICAgICAgICAgICAgICAgICB9CiAgICAgICAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgICAgICAgfQogICAgICAgICAgICAgICB9IF0sCiAgICAgICAgICAgICAgICJyZWxhdGlvbnNoaXAiIDogWyBdLAogICAgICAgICAgICAgICAid2hlcmUiIDogewogICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiNDU6Ny00NjoyOSIsCiAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJPciIsCiAgICAgICAgICAgICAgICAgICJvcGVyYW5kIiA6IFsgewogICAgICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiNDU6MTMtNDU6MzMiLAogICAgICAgICAgICAgICAgICAgICAidHlwZSIgOiAiRXF1YWwiLAogICAgICAgICAgICAgICAgICAgICAib3BlcmFuZCIgOiBbIHsKICAgICAgICAgICAgICAgICAgICAgICAgIm5hbWUiIDogIlRvU3RyaW5nIiwKICAgICAgICAgICAgICAgICAgICAgICAgImxpYnJhcnlOYW1lIiA6ICJGSElSSGVscGVycyIsCiAgICAgICAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJGdW5jdGlvblJlZiIsCiAgICAgICAgICAgICAgICAgICAgICAgICJvcGVyYW5kIiA6IFsgewogICAgICAgICAgICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiNDU6MTMtNDU6MTciLAogICAgICAgICAgICAgICAgICAgICAgICAgICAicGF0aCIgOiAidXJsIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgInNjb3BlIiA6ICJFIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgInR5cGUiIDogIlByb3BlcnR5IgogICAgICAgICAgICAgICAgICAgICAgICB9IF0KICAgICAgICAgICAgICAgICAgICAgfSwgewogICAgICAgICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiNDU6MjEtNDU6MzMiLAogICAgICAgICAgICAgICAgICAgICAgICAidmFsdWVUeXBlIiA6ICJ7dXJuOmhsNy1vcmc6ZWxtLXR5cGVzOnIxfVN0cmluZyIsCiAgICAgICAgICAgICAgICAgICAgICAgICJ2YWx1ZSIgOiAib21iQ2F0ZWdvcnkiLAogICAgICAgICAgICAgICAgICAgICAgICAidHlwZSIgOiAiTGl0ZXJhbCIKICAgICAgICAgICAgICAgICAgICAgfSBdCiAgICAgICAgICAgICAgICAgIH0sIHsKICAgICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjQ2OjEyLTQ2OjI5IiwKICAgICAgICAgICAgICAgICAgICAgInR5cGUiIDogIkVxdWFsIiwKICAgICAgICAgICAgICAgICAgICAgIm9wZXJhbmQiIDogWyB7CiAgICAgICAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJUb1N0cmluZyIsCiAgICAgICAgICAgICAgICAgICAgICAgICJsaWJyYXJ5TmFtZSIgOiAiRkhJUkhlbHBlcnMiLAogICAgICAgICAgICAgICAgICAgICAgICAidHlwZSIgOiAiRnVuY3Rpb25SZWYiLAogICAgICAgICAgICAgICAgICAgICAgICAib3BlcmFuZCIgOiBbIHsKICAgICAgICAgICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjQ2OjEyLTQ2OjE2IiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgInBhdGgiIDogInVybCIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICJzY29wZSIgOiAiRSIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJQcm9wZXJ0eSIKICAgICAgICAgICAgICAgICAgICAgICAgfSBdCiAgICAgICAgICAgICAgICAgICAgIH0sIHsKICAgICAgICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjQ2OjIwLTQ2OjI5IiwKICAgICAgICAgICAgICAgICAgICAgICAgInZhbHVlVHlwZSIgOiAie3VybjpobDctb3JnOmVsbS10eXBlczpyMX1TdHJpbmciLAogICAgICAgICAgICAgICAgICAgICAgICAidmFsdWUiIDogImRldGFpbGVkIiwKICAgICAgICAgICAgICAgICAgICAgICAgInR5cGUiIDogIkxpdGVyYWwiCiAgICAgICAgICAgICAgICAgICAgIH0gXQogICAgICAgICAgICAgICAgICB9IF0KICAgICAgICAgICAgICAgfSwKICAgICAgICAgICAgICAgInJldHVybiIgOiB7CiAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICI0Nzo3LTQ3OjQwIiwKICAgICAgICAgICAgICAgICAgImV4cHJlc3Npb24iIDogewogICAgICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiNDc6MTQtNDc6NDAiLAogICAgICAgICAgICAgICAgICAgICAibmFtZSIgOiAiVG9Db2RlIiwKICAgICAgICAgICAgICAgICAgICAgImxpYnJhcnlOYW1lIiA6ICJGSElSSGVscGVycyIsCiAgICAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJGdW5jdGlvblJlZiIsCiAgICAgICAgICAgICAgICAgICAgICJvcGVyYW5kIiA6IFsgewogICAgICAgICAgICAgICAgICAgICAgICAiYXNUeXBlIiA6ICJ7aHR0cDovL2hsNy5vcmcvZmhpcn1Db2RpbmciLAogICAgICAgICAgICAgICAgICAgICAgICAidHlwZSIgOiAiQXMiLAogICAgICAgICAgICAgICAgICAgICAgICAib3BlcmFuZCIgOiB7CiAgICAgICAgICAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICI0NzozMy00NzozOSIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICJwYXRoIiA6ICJ2YWx1ZSIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICJzY29wZSIgOiAiRSIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJQcm9wZXJ0eSIKICAgICAgICAgICAgICAgICAgICAgICAgfQogICAgICAgICAgICAgICAgICAgICB9IF0KICAgICAgICAgICAgICAgICAgfQogICAgICAgICAgICAgICB9CiAgICAgICAgICAgIH0KICAgICAgICAgfSwgewogICAgICAgICAgICAibG9jYXRvciIgOiAiNDk6MS01NDo3IiwKICAgICAgICAgICAgIm5hbWUiIDogIlNERSBTZXgiLAogICAgICAgICAgICAiY29udGV4dCIgOiAiUGF0aWVudCIsCiAgICAgICAgICAgICJhY2Nlc3NMZXZlbCIgOiAiUHVibGljIiwKICAgICAgICAgICAgImV4cHJlc3Npb24iIDogewogICAgICAgICAgICAgICAibG9jYXRvciIgOiAiNTA6My01NDo3IiwKICAgICAgICAgICAgICAgInR5cGUiIDogIkNhc2UiLAogICAgICAgICAgICAgICAiY2FzZUl0ZW0iIDogWyB7CiAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICI1MTo3LTUxOjEzMCIsCiAgICAgICAgICAgICAgICAgICJ3aGVuIiA6IHsKICAgICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjUxOjEyLTUxOjM0IiwKICAgICAgICAgICAgICAgICAgICAgInR5cGUiIDogIkVxdWFsIiwKICAgICAgICAgICAgICAgICAgICAgIm9wZXJhbmQiIDogWyB7CiAgICAgICAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJUb1N0cmluZyIsCiAgICAgICAgICAgICAgICAgICAgICAgICJsaWJyYXJ5TmFtZSIgOiAiRkhJUkhlbHBlcnMiLAogICAgICAgICAgICAgICAgICAgICAgICAidHlwZSIgOiAiRnVuY3Rpb25SZWYiLAogICAgICAgICAgICAgICAgICAgICAgICAib3BlcmFuZCIgOiBbIHsKICAgICAgICAgICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjUxOjEyLTUxOjI1IiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgInBhdGgiIDogImdlbmRlciIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJQcm9wZXJ0eSIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICJzb3VyY2UiIDogewogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiNTE6MTItNTE6MTgiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAibmFtZSIgOiAiUGF0aWVudCIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJFeHByZXNzaW9uUmVmIgogICAgICAgICAgICAgICAgICAgICAgICAgICB9CiAgICAgICAgICAgICAgICAgICAgICAgIH0gXQogICAgICAgICAgICAgICAgICAgICB9LCB7CiAgICAgICAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICI1MToyOS01MTozNCIsCiAgICAgICAgICAgICAgICAgICAgICAgICJ2YWx1ZVR5cGUiIDogInt1cm46aGw3LW9yZzplbG0tdHlwZXM6cjF9U3RyaW5nIiwKICAgICAgICAgICAgICAgICAgICAgICAgInZhbHVlIiA6ICJtYWxlIiwKICAgICAgICAgICAgICAgICAgICAgICAgInR5cGUiIDogIkxpdGVyYWwiCiAgICAgICAgICAgICAgICAgICAgIH0gXQogICAgICAgICAgICAgICAgICB9LAogICAgICAgICAgICAgICAgICAidGhlbiIgOiB7CiAgICAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICI1MTo0MS01MToxMzAiLAogICAgICAgICAgICAgICAgICAgICAiY2xhc3NUeXBlIiA6ICJ7dXJuOmhsNy1vcmc6ZWxtLXR5cGVzOnIxfUNvZGUiLAogICAgICAgICAgICAgICAgICAgICAidHlwZSIgOiAiSW5zdGFuY2UiLAogICAgICAgICAgICAgICAgICAgICAiZWxlbWVudCIgOiBbIHsKICAgICAgICAgICAgICAgICAgICAgICAgIm5hbWUiIDogImNvZGUiLAogICAgICAgICAgICAgICAgICAgICAgICAidmFsdWUiIDogewogICAgICAgICAgICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiNTE6NTQtNTE6NTYiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAidmFsdWVUeXBlIiA6ICJ7dXJuOmhsNy1vcmc6ZWxtLXR5cGVzOnIxfVN0cmluZyIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICJ2YWx1ZSIgOiAiTSIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJMaXRlcmFsIgogICAgICAgICAgICAgICAgICAgICAgICB9CiAgICAgICAgICAgICAgICAgICAgIH0sIHsKICAgICAgICAgICAgICAgICAgICAgICAgIm5hbWUiIDogInN5c3RlbSIsCiAgICAgICAgICAgICAgICAgICAgICAgICJ2YWx1ZSIgOiB7CiAgICAgICAgICAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICI1MTo2Ny01MToxMTEiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAidmFsdWVUeXBlIiA6ICJ7dXJuOmhsNy1vcmc6ZWxtLXR5cGVzOnIxfVN0cmluZyIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICJ2YWx1ZSIgOiAiaHR0cDovL2hsNy5vcmcvZmhpci92My9BZG1pbmlzdHJhdGl2ZUdlbmRlciIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJMaXRlcmFsIgogICAgICAgICAgICAgICAgICAgICAgICB9CiAgICAgICAgICAgICAgICAgICAgIH0sIHsKICAgICAgICAgICAgICAgICAgICAgICAgIm5hbWUiIDogImRpc3BsYXkiLAogICAgICAgICAgICAgICAgICAgICAgICAidmFsdWUiIDogewogICAgICAgICAgICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiNTE6MTIzLTUxOjEyOCIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICJ2YWx1ZVR5cGUiIDogInt1cm46aGw3LW9yZzplbG0tdHlwZXM6cjF9U3RyaW5nIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgInZhbHVlIiA6ICJNYWxlIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgInR5cGUiIDogIkxpdGVyYWwiCiAgICAgICAgICAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgICAgICAgICAgfSBdCiAgICAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgICAgfSwgewogICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiNTI6Ny01MjoxMzQiLAogICAgICAgICAgICAgICAgICAid2hlbiIgOiB7CiAgICAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICI1MjoxMi01MjozNiIsCiAgICAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJFcXVhbCIsCiAgICAgICAgICAgICAgICAgICAgICJvcGVyYW5kIiA6IFsgewogICAgICAgICAgICAgICAgICAgICAgICAibmFtZSIgOiAiVG9TdHJpbmciLAogICAgICAgICAgICAgICAgICAgICAgICAibGlicmFyeU5hbWUiIDogIkZISVJIZWxwZXJzIiwKICAgICAgICAgICAgICAgICAgICAgICAgInR5cGUiIDogIkZ1bmN0aW9uUmVmIiwKICAgICAgICAgICAgICAgICAgICAgICAgIm9wZXJhbmQiIDogWyB7CiAgICAgICAgICAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICI1MjoxMi01MjoyNSIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICJwYXRoIiA6ICJnZW5kZXIiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAidHlwZSIgOiAiUHJvcGVydHkiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAic291cmNlIiA6IHsKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjUyOjEyLTUyOjE4IiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIm5hbWUiIDogIlBhdGllbnQiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAidHlwZSIgOiAiRXhwcmVzc2lvblJlZiIKICAgICAgICAgICAgICAgICAgICAgICAgICAgfQogICAgICAgICAgICAgICAgICAgICAgICB9IF0KICAgICAgICAgICAgICAgICAgICAgfSwgewogICAgICAgICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiNTI6MjktNTI6MzYiLAogICAgICAgICAgICAgICAgICAgICAgICAidmFsdWVUeXBlIiA6ICJ7dXJuOmhsNy1vcmc6ZWxtLXR5cGVzOnIxfVN0cmluZyIsCiAgICAgICAgICAgICAgICAgICAgICAgICJ2YWx1ZSIgOiAiZmVtYWxlIiwKICAgICAgICAgICAgICAgICAgICAgICAgInR5cGUiIDogIkxpdGVyYWwiCiAgICAgICAgICAgICAgICAgICAgIH0gXQogICAgICAgICAgICAgICAgICB9LAogICAgICAgICAgICAgICAgICAidGhlbiIgOiB7CiAgICAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICI1Mjo0My01MjoxMzQiLAogICAgICAgICAgICAgICAgICAgICAiY2xhc3NUeXBlIiA6ICJ7dXJuOmhsNy1vcmc6ZWxtLXR5cGVzOnIxfUNvZGUiLAogICAgICAgICAgICAgICAgICAgICAidHlwZSIgOiAiSW5zdGFuY2UiLAogICAgICAgICAgICAgICAgICAgICAiZWxlbWVudCIgOiBbIHsKICAgICAgICAgICAgICAgICAgICAgICAgIm5hbWUiIDogImNvZGUiLAogICAgICAgICAgICAgICAgICAgICAgICAidmFsdWUiIDogewogICAgICAgICAgICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiNTI6NTYtNTI6NTgiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAidmFsdWVUeXBlIiA6ICJ7dXJuOmhsNy1vcmc6ZWxtLXR5cGVzOnIxfVN0cmluZyIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICJ2YWx1ZSIgOiAiRiIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJMaXRlcmFsIgogICAgICAgICAgICAgICAgICAgICAgICB9CiAgICAgICAgICAgICAgICAgICAgIH0sIHsKICAgICAgICAgICAgICAgICAgICAgICAgIm5hbWUiIDogInN5c3RlbSIsCiAgICAgICAgICAgICAgICAgICAgICAgICJ2YWx1ZSIgOiB7CiAgICAgICAgICAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICI1Mjo2OS01MjoxMTMiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAidmFsdWVUeXBlIiA6ICJ7dXJuOmhsNy1vcmc6ZWxtLXR5cGVzOnIxfVN0cmluZyIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICJ2YWx1ZSIgOiAiaHR0cDovL2hsNy5vcmcvZmhpci92My9BZG1pbmlzdHJhdGl2ZUdlbmRlciIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJMaXRlcmFsIgogICAgICAgICAgICAgICAgICAgICAgICB9CiAgICAgICAgICAgICAgICAgICAgIH0sIHsKICAgICAgICAgICAgICAgICAgICAgICAgIm5hbWUiIDogImRpc3BsYXkiLAogICAgICAgICAgICAgICAgICAgICAgICAidmFsdWUiIDogewogICAgICAgICAgICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiNTI6MTI1LTUyOjEzMiIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICJ2YWx1ZVR5cGUiIDogInt1cm46aGw3LW9yZzplbG0tdHlwZXM6cjF9U3RyaW5nIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgInZhbHVlIiA6ICJGZW1hbGUiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAidHlwZSIgOiAiTGl0ZXJhbCIKICAgICAgICAgICAgICAgICAgICAgICAgfQogICAgICAgICAgICAgICAgICAgICB9IF0KICAgICAgICAgICAgICAgICAgfQogICAgICAgICAgICAgICB9IF0sCiAgICAgICAgICAgICAgICJlbHNlIiA6IHsKICAgICAgICAgICAgICAgICAgImFzVHlwZSIgOiAie3VybjpobDctb3JnOmVsbS10eXBlczpyMX1Db2RlIiwKICAgICAgICAgICAgICAgICAgInR5cGUiIDogIkFzIiwKICAgICAgICAgICAgICAgICAgIm9wZXJhbmQiIDogewogICAgICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiNTM6MTItNTM6MTUiLAogICAgICAgICAgICAgICAgICAgICAidHlwZSIgOiAiTnVsbCIKICAgICAgICAgICAgICAgICAgfQogICAgICAgICAgICAgICB9CiAgICAgICAgICAgIH0KICAgICAgICAgfSBdCiAgICAgIH0KICAgfQp9" + } + ] + }, + "request": { + "method": "PUT", + "url": "Library/SDE" + } + }, + { + "resource": { + "resourceType": "ValueSet", + "id": "2.16.840.1.113883.3.526.3.378", + "meta": { + "profile": [ + "http://hl7.org/fhir/uv/cpg/StructureDefinition/cpg-shareablevalueset", + "http://hl7.org/fhir/uv/cpg/StructureDefinition/cpg-publishablevalueset", + "http://hl7.org/fhir/uv/cpg/StructureDefinition/cpg-executablevalueset" + ] + }, + "extension": [ + { + "url": "http://hl7.org/fhir/uv/cpg/StructureDefinition/cpg-knowledgeCapability", + "valueCode": "shareable" + }, + { + "url": "http://hl7.org/fhir/uv/cpg/StructureDefinition/cpg-knowledgeRepresentationLevel", + "valueCode": "narrative" + }, + { + "url": "http://hl7.org/fhir/uv/cpg/StructureDefinition/cpg-knowledgeCapability", + "valueCode": "publishable" + }, + { + "url": "http://fhir.org/guides/cdc/opioid-cds/StructureDefinition/cdc-valueset-inclusion", + "valueString": "Includes concepts that represent a diagnosis of pregnancy." + }, + { + "url": "http://fhir.org/guides/cdc/opioid-cds/StructureDefinition/cdc-valueset-exclusion", + "valueString": "No exclusions." + }, + { + "url": "http://hl7.org/fhir/uv/cpg/StructureDefinition/cpg-knowledgeCapability", + "valueCode": "executable" + }, + { + "url": "http://hl7.org/fhir/uv/cpg/StructureDefinition/cpg-knowledgeRepresentationLevel", + "valueCode": "executable" + }, + { + "url": "http://hl7.org/fhir/uv/cpg/StructureDefinition/cpg-usageWarning", + "valueString": "This value set contains a point-in-time expansion enumerating the codes that meet the value set intent. As new versions of the code systems used by the value set are released, the contents of this expansion will need to be updated to incorporate newly defined codes that meet the value set intent. Before, and periodically during production use, the value set expansion contents SHOULD be updated. The value set expansion specifies the timestamp when the expansion was produced, SHOULD contain the parameters used for the expansion, and SHALL contain the codes that are obtained by evaluating the value set definition. If this is ONLY an executable value set, a distributable definition of the value set must be obtained to compute the updated expansion." + } + ], + "url": "http://cts.nlm.nih.gov/fhir/ValueSet/2.16.840.1.113883.3.526.3.378", + "version": "20210218", + "title": "Pregnancy", + "status": "active", + "experimental": false, + "publisher": "American Heart Association, Inc.", + "description": "The purpose of this value set is to represent concepts for diagnoses of pregnancy.", + "purpose": "This value set may use a model element related to Diagnosis.", + "expansion": { + "timestamp": "2023-01-02T15:36:29-07:00", + "contains": [ + { + "system": "http://snomed.info/sct", + "version": "2022-09", + "code": "10231000132102", + "display": "In-vitro fertilization pregnancy (finding)" + }, + { + "system": "http://snomed.info/sct", + "version": "2022-09", + "code": "102872000", + "display": "Pregnancy on oral contraceptive (finding)" + }, + { + "system": "http://snomed.info/sct", + "version": "2022-09", + "code": "102875003", + "display": "Surrogate pregnancy (finding)" + }, + { + "system": "http://snomed.info/sct", + "version": "2022-09", + "code": "11082009", + "display": "Abnormal pregnancy (finding)" + }, + { + "system": "http://snomed.info/sct", + "version": "2022-09", + "code": "127364007", + "display": "Primigravida (finding)" + }, + { + "system": "http://snomed.info/sct", + "version": "2022-09", + "code": "134781000119106", + "display": "High risk pregnancy due to recurrent miscarriage (finding)" + }, + { + "system": "http://snomed.info/sct", + "version": "2022-09", + "code": "14080002", + "display": "Illegitimate pregnancy, life event (finding)" + }, + { + "system": "http://snomed.info/sct", + "version": "2022-09", + "code": "14418008", + "display": "Precocious pregnancy (finding)" + }, + { + "system": "http://snomed.info/sct", + "version": "2022-09", + "code": "16356006", + "display": "Multiple pregnancy (disorder)" + }, + { + "system": "http://snomed.info/sct", + "version": "2022-09", + "code": "169561007", + "display": "Pregnant - blood test confirms (finding)" + }, + { + "system": "http://snomed.info/sct", + "version": "2022-09", + "code": "169562000", + "display": "Pregnant - vaginal examination confirms (finding)" + }, + { + "system": "http://snomed.info/sct", + "version": "2022-09", + "code": "169563005", + "display": "Pregnant - on history (finding)" + }, + { + "system": "http://snomed.info/sct", + "version": "2022-09", + "code": "169564004", + "display": "Pregnant - on abdominal palpation (finding)" + }, + { + "system": "http://snomed.info/sct", + "version": "2022-09", + "code": "169565003", + "display": "Pregnant - planned (finding)" + }, + { + "system": "http://snomed.info/sct", + "version": "2022-09", + "code": "169566002", + "display": "Pregnancy unplanned but wanted (finding)" + }, + { + "system": "http://snomed.info/sct", + "version": "2022-09", + "code": "169567006", + "display": "Pregnancy unplanned and unwanted (finding)" + }, + { + "system": "http://snomed.info/sct", + "version": "2022-09", + "code": "169568001", + "display": "Unplanned pregnancy unknown if child is wanted (finding)" + }, + { + "system": "http://snomed.info/sct", + "version": "2022-09", + "code": "199064003", + "display": "Post-term pregnancy - not delivered (disorder)" + }, + { + "system": "http://snomed.info/sct", + "version": "2022-09", + "code": "199306007", + "display": "Continuing pregnancy after abortion of one fetus or more (disorder)" + }, + { + "system": "http://snomed.info/sct", + "version": "2022-09", + "code": "22281000119101", + "display": "Post-term pregnancy of 40 to 42 weeks (finding)" + }, + { + "system": "http://snomed.info/sct", + "version": "2022-09", + "code": "237233002", + "display": "Concealed pregnancy (finding)" + }, + { + "system": "http://snomed.info/sct", + "version": "2022-09", + "code": "237238006", + "display": "Pregnancy with uncertain dates (finding)" + }, + { + "system": "http://snomed.info/sct", + "version": "2022-09", + "code": "237239003", + "display": "Low risk pregnancy (finding)" + }, + { + "system": "http://snomed.info/sct", + "version": "2022-09", + "code": "237240001", + "display": "Teenage pregnancy (finding)" + }, + { + "system": "http://snomed.info/sct", + "version": "2022-09", + "code": "237241002", + "display": "Viable pregnancy (finding)" + }, + { + "system": "http://snomed.info/sct", + "version": "2022-09", + "code": "237244005", + "display": "Single pregnancy (finding)" + }, + { + "system": "http://snomed.info/sct", + "version": "2022-09", + "code": "248985009", + "display": "Presentation of pregnancy (finding)" + }, + { + "system": "http://snomed.info/sct", + "version": "2022-09", + "code": "276367008", + "display": "Wanted pregnancy (finding)" + }, + { + "system": "http://snomed.info/sct", + "version": "2022-09", + "code": "281307002", + "display": "Uncertain viability of pregnancy (finding)" + }, + { + "system": "http://snomed.info/sct", + "version": "2022-09", + "code": "314204000", + "display": "Early stage of pregnancy (finding)" + }, + { + "system": "http://snomed.info/sct", + "version": "2022-09", + "code": "35381000119101", + "display": "Quadruplet pregnancy with loss of one or more fetuses (disorder)" + }, + { + "system": "http://snomed.info/sct", + "version": "2022-09", + "code": "36801000119105", + "display": "Continuing triplet pregnancy after spontaneous abortion of one or more fetuses (disorder)" + }, + { + "system": "http://snomed.info/sct", + "version": "2022-09", + "code": "38720006", + "display": "Septuplet pregnancy (disorder)" + }, + { + "system": "http://snomed.info/sct", + "version": "2022-09", + "code": "41587001", + "display": "Third trimester pregnancy (finding)" + }, + { + "system": "http://snomed.info/sct", + "version": "2022-09", + "code": "41991004", + "display": "Angiectasis pregnancy (disorder)" + }, + { + "system": "http://snomed.info/sct", + "version": "2022-09", + "code": "429187001", + "display": "Continuing pregnancy after intrauterine death of twin fetus (disorder)" + }, + { + "system": "http://snomed.info/sct", + "version": "2022-09", + "code": "43990006", + "display": "Sextuplet pregnancy (disorder)" + }, + { + "system": "http://snomed.info/sct", + "version": "2022-09", + "code": "442478007", + "display": "Multiple pregnancy involving intrauterine pregnancy and tubal pregnancy (disorder)" + }, + { + "system": "http://snomed.info/sct", + "version": "2022-09", + "code": "444661007", + "display": "High risk pregnancy due to history of preterm labor (finding)" + }, + { + "system": "http://snomed.info/sct", + "version": "2022-09", + "code": "45307008", + "display": "Extrachorial pregnancy (finding)" + }, + { + "system": "http://snomed.info/sct", + "version": "2022-09", + "code": "457811000124103", + "display": "Normal pregnancy in primigravida (finding)" + }, + { + "system": "http://snomed.info/sct", + "version": "2022-09", + "code": "457821000124106", + "display": "Normal pregnancy in multigravida (finding)" + }, + { + "system": "http://snomed.info/sct", + "version": "2022-09", + "code": "459167000", + "display": "Monochorionic twin pregnancy (disorder)" + }, + { + "system": "http://snomed.info/sct", + "version": "2022-09", + "code": "459169002", + "display": "Monochorionic diamniotic twin pregnancy with similar amniotic fluid volumes (disorder)" + }, + { + "system": "http://snomed.info/sct", + "version": "2022-09", + "code": "459170001", + "display": "Monochorionic diamniotic twin pregnancy with dissimilar amniotic fluid volumes (disorder)" + }, + { + "system": "http://snomed.info/sct", + "version": "2022-09", + "code": "47200007", + "display": "High risk pregnancy (finding)" + }, + { + "system": "http://snomed.info/sct", + "version": "2022-09", + "code": "472321009", + "display": "Continuing pregnancy after intrauterine death of one twin with intrauterine retention of dead twin (disorder)" + }, + { + "system": "http://snomed.info/sct", + "version": "2022-09", + "code": "57630001", + "display": "First trimester pregnancy (finding)" + }, + { + "system": "http://snomed.info/sct", + "version": "2022-09", + "code": "58532003", + "display": "Unwanted pregnancy (finding)" + }, + { + "system": "http://snomed.info/sct", + "version": "2022-09", + "code": "59466002", + "display": "Second trimester pregnancy (finding)" + }, + { + "system": "http://snomed.info/sct", + "version": "2022-09", + "code": "60810003", + "display": "Quadruplet pregnancy (disorder)" + }, + { + "system": "http://snomed.info/sct", + "version": "2022-09", + "code": "64254006", + "display": "Triplet pregnancy (disorder)" + }, + { + "system": "http://snomed.info/sct", + "version": "2022-09", + "code": "65147003", + "display": "Twin pregnancy (disorder)" + }, + { + "system": "http://snomed.info/sct", + "version": "2022-09", + "code": "65727000", + "display": "Intrauterine pregnancy (finding)" + }, + { + "system": "http://snomed.info/sct", + "version": "2022-09", + "code": "713575004", + "display": "Dizygotic twin pregnancy (disorder)" + }, + { + "system": "http://snomed.info/sct", + "version": "2022-09", + "code": "713576003", + "display": "Monozygotic twin pregnancy (disorder)" + }, + { + "system": "http://snomed.info/sct", + "version": "2022-09", + "code": "72892002", + "display": "Normal pregnancy (finding)" + }, + { + "system": "http://snomed.info/sct", + "version": "2022-09", + "code": "77386006", + "display": "Pregnancy (finding)" + }, + { + "system": "http://snomed.info/sct", + "version": "2022-09", + "code": "80997009", + "display": "Quintuplet pregnancy (disorder)" + }, + { + "system": "http://snomed.info/sct", + "version": "2022-09", + "code": "83074005", + "display": "Unplanned pregnancy (finding)" + }, + { + "system": "http://snomed.info/sct", + "version": "2022-09", + "code": "87527008", + "display": "Term pregnancy (finding)" + }, + { + "system": "http://snomed.info/sct", + "version": "2022-09", + "code": "90968009", + "display": "Prolonged pregnancy (disorder)" + }, + { + "system": "http://snomed.info/sct", + "version": "2022-09", + "code": "9279009", + "display": "Extra-amniotic pregnancy (finding)" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O09.00", + "display": "Supervision of pregnancy with history of infertility, unspecified trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O09.01", + "display": "Supervision of pregnancy with history of infertility, first trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O09.02", + "display": "Supervision of pregnancy with history of infertility, second trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O09.03", + "display": "Supervision of pregnancy with history of infertility, third trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O09.10", + "display": "Supervision of pregnancy with history of ectopic pregnancy, unspecified trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O09.11", + "display": "Supervision of pregnancy with history of ectopic pregnancy, first trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O09.12", + "display": "Supervision of pregnancy with history of ectopic pregnancy, second trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O09.13", + "display": "Supervision of pregnancy with history of ectopic pregnancy, third trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O09.211", + "display": "Supervision of pregnancy with history of pre-term labor, first trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O09.212", + "display": "Supervision of pregnancy with history of pre-term labor, second trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O09.213", + "display": "Supervision of pregnancy with history of pre-term labor, third trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O09.219", + "display": "Supervision of pregnancy with history of pre-term labor, unspecified trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O09.291", + "display": "Supervision of pregnancy with other poor reproductive or obstetric history, first trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O09.292", + "display": "Supervision of pregnancy with other poor reproductive or obstetric history, second trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O09.293", + "display": "Supervision of pregnancy with other poor reproductive or obstetric history, third trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O09.299", + "display": "Supervision of pregnancy with other poor reproductive or obstetric history, unspecified trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O09.30", + "display": "Supervision of pregnancy with insufficient antenatal care, unspecified trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O09.31", + "display": "Supervision of pregnancy with insufficient antenatal care, first trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O09.32", + "display": "Supervision of pregnancy with insufficient antenatal care, second trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O09.33", + "display": "Supervision of pregnancy with insufficient antenatal care, third trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O09.40", + "display": "Supervision of pregnancy with grand multiparity, unspecified trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O09.41", + "display": "Supervision of pregnancy with grand multiparity, first trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O09.42", + "display": "Supervision of pregnancy with grand multiparity, second trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O09.43", + "display": "Supervision of pregnancy with grand multiparity, third trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O09.511", + "display": "Supervision of elderly primigravida, first trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O09.512", + "display": "Supervision of elderly primigravida, second trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O09.513", + "display": "Supervision of elderly primigravida, third trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O09.519", + "display": "Supervision of elderly primigravida, unspecified trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O09.521", + "display": "Supervision of elderly multigravida, first trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O09.522", + "display": "Supervision of elderly multigravida, second trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O09.523", + "display": "Supervision of elderly multigravida, third trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O09.529", + "display": "Supervision of elderly multigravida, unspecified trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O09.611", + "display": "Supervision of young primigravida, first trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O09.612", + "display": "Supervision of young primigravida, second trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O09.613", + "display": "Supervision of young primigravida, third trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O09.619", + "display": "Supervision of young primigravida, unspecified trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O09.621", + "display": "Supervision of young multigravida, first trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O09.622", + "display": "Supervision of young multigravida, second trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O09.623", + "display": "Supervision of young multigravida, third trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O09.629", + "display": "Supervision of young multigravida, unspecified trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O09.70", + "display": "Supervision of high risk pregnancy due to social problems, unspecified trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O09.71", + "display": "Supervision of high risk pregnancy due to social problems, first trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O09.72", + "display": "Supervision of high risk pregnancy due to social problems, second trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O09.73", + "display": "Supervision of high risk pregnancy due to social problems, third trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O09.811", + "display": "Supervision of pregnancy resulting from assisted reproductive technology, first trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O09.812", + "display": "Supervision of pregnancy resulting from assisted reproductive technology, second trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O09.813", + "display": "Supervision of pregnancy resulting from assisted reproductive technology, third trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O09.819", + "display": "Supervision of pregnancy resulting from assisted reproductive technology, unspecified trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O09.821", + "display": "Supervision of pregnancy with history of in utero procedure during previous pregnancy, first trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O09.822", + "display": "Supervision of pregnancy with history of in utero procedure during previous pregnancy, second trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O09.823", + "display": "Supervision of pregnancy with history of in utero procedure during previous pregnancy, third trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O09.829", + "display": "Supervision of pregnancy with history of in utero procedure during previous pregnancy, unspecified trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O09.891", + "display": "Supervision of other high risk pregnancies, first trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O09.892", + "display": "Supervision of other high risk pregnancies, second trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O09.893", + "display": "Supervision of other high risk pregnancies, third trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O09.899", + "display": "Supervision of other high risk pregnancies, unspecified trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O09.90", + "display": "Supervision of high risk pregnancy, unspecified, unspecified trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O09.91", + "display": "Supervision of high risk pregnancy, unspecified, first trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O09.92", + "display": "Supervision of high risk pregnancy, unspecified, second trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O09.93", + "display": "Supervision of high risk pregnancy, unspecified, third trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O09.A0", + "display": "Supervision of pregnancy with history of molar pregnancy, unspecified trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O09.A1", + "display": "Supervision of pregnancy with history of molar pregnancy, first trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O09.A2", + "display": "Supervision of pregnancy with history of molar pregnancy, second trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O09.A3", + "display": "Supervision of pregnancy with history of molar pregnancy, third trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O10.011", + "display": "Pre-existing essential hypertension complicating pregnancy, first trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O10.012", + "display": "Pre-existing essential hypertension complicating pregnancy, second trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O10.013", + "display": "Pre-existing essential hypertension complicating pregnancy, third trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O10.019", + "display": "Pre-existing essential hypertension complicating pregnancy, unspecified trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O10.111", + "display": "Pre-existing hypertensive heart disease complicating pregnancy, first trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O10.112", + "display": "Pre-existing hypertensive heart disease complicating pregnancy, second trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O10.113", + "display": "Pre-existing hypertensive heart disease complicating pregnancy, third trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O10.119", + "display": "Pre-existing hypertensive heart disease complicating pregnancy, unspecified trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O10.211", + "display": "Pre-existing hypertensive chronic kidney disease complicating pregnancy, first trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O10.212", + "display": "Pre-existing hypertensive chronic kidney disease complicating pregnancy, second trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O10.213", + "display": "Pre-existing hypertensive chronic kidney disease complicating pregnancy, third trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O10.219", + "display": "Pre-existing hypertensive chronic kidney disease complicating pregnancy, unspecified trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O10.311", + "display": "Pre-existing hypertensive heart and chronic kidney disease complicating pregnancy, first trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O10.312", + "display": "Pre-existing hypertensive heart and chronic kidney disease complicating pregnancy, second trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O10.313", + "display": "Pre-existing hypertensive heart and chronic kidney disease complicating pregnancy, third trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O10.319", + "display": "Pre-existing hypertensive heart and chronic kidney disease complicating pregnancy, unspecified trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O10.411", + "display": "Pre-existing secondary hypertension complicating pregnancy, first trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O10.412", + "display": "Pre-existing secondary hypertension complicating pregnancy, second trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O10.413", + "display": "Pre-existing secondary hypertension complicating pregnancy, third trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O10.419", + "display": "Pre-existing secondary hypertension complicating pregnancy, unspecified trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O10.911", + "display": "Unspecified pre-existing hypertension complicating pregnancy, first trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O10.912", + "display": "Unspecified pre-existing hypertension complicating pregnancy, second trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O10.913", + "display": "Unspecified pre-existing hypertension complicating pregnancy, third trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O10.919", + "display": "Unspecified pre-existing hypertension complicating pregnancy, unspecified trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O11.1", + "display": "Pre-existing hypertension with pre-eclampsia, first trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O11.2", + "display": "Pre-existing hypertension with pre-eclampsia, second trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O11.3", + "display": "Pre-existing hypertension with pre-eclampsia, third trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O11.9", + "display": "Pre-existing hypertension with pre-eclampsia, unspecified trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O12.00", + "display": "Gestational edema, unspecified trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O12.01", + "display": "Gestational edema, first trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O12.02", + "display": "Gestational edema, second trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O12.03", + "display": "Gestational edema, third trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O12.10", + "display": "Gestational proteinuria, unspecified trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O12.11", + "display": "Gestational proteinuria, first trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O12.12", + "display": "Gestational proteinuria, second trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O12.13", + "display": "Gestational proteinuria, third trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O12.20", + "display": "Gestational edema with proteinuria, unspecified trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O12.21", + "display": "Gestational edema with proteinuria, first trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O12.22", + "display": "Gestational edema with proteinuria, second trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O12.23", + "display": "Gestational edema with proteinuria, third trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O13.1", + "display": "Gestational [pregnancy-induced] hypertension without significant proteinuria, first trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O13.2", + "display": "Gestational [pregnancy-induced] hypertension without significant proteinuria, second trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O13.3", + "display": "Gestational [pregnancy-induced] hypertension without significant proteinuria, third trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O13.9", + "display": "Gestational [pregnancy-induced] hypertension without significant proteinuria, unspecified trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O14.00", + "display": "Mild to moderate pre-eclampsia, unspecified trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O14.02", + "display": "Mild to moderate pre-eclampsia, second trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O14.03", + "display": "Mild to moderate pre-eclampsia, third trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O14.10", + "display": "Severe pre-eclampsia, unspecified trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O14.12", + "display": "Severe pre-eclampsia, second trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O14.13", + "display": "Severe pre-eclampsia, third trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O14.20", + "display": "HELLP syndrome (HELLP), unspecified trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O14.22", + "display": "HELLP syndrome (HELLP), second trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O14.23", + "display": "HELLP syndrome (HELLP), third trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O14.90", + "display": "Unspecified pre-eclampsia, unspecified trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O14.92", + "display": "Unspecified pre-eclampsia, second trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O14.93", + "display": "Unspecified pre-eclampsia, third trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O15.00", + "display": "Eclampsia complicating pregnancy, unspecified trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O15.02", + "display": "Eclampsia complicating pregnancy, second trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O15.03", + "display": "Eclampsia complicating pregnancy, third trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O16.1", + "display": "Unspecified maternal hypertension, first trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O16.2", + "display": "Unspecified maternal hypertension, second trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O16.3", + "display": "Unspecified maternal hypertension, third trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O16.9", + "display": "Unspecified maternal hypertension, unspecified trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O20.0", + "display": "Threatened abortion" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O20.8", + "display": "Other hemorrhage in early pregnancy" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O20.9", + "display": "Hemorrhage in early pregnancy, unspecified" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O21.0", + "display": "Mild hyperemesis gravidarum" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O21.1", + "display": "Hyperemesis gravidarum with metabolic disturbance" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O21.2", + "display": "Late vomiting of pregnancy" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O21.8", + "display": "Other vomiting complicating pregnancy" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O21.9", + "display": "Vomiting of pregnancy, unspecified" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O22.00", + "display": "Varicose veins of lower extremity in pregnancy, unspecified trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O22.01", + "display": "Varicose veins of lower extremity in pregnancy, first trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O22.02", + "display": "Varicose veins of lower extremity in pregnancy, second trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O22.03", + "display": "Varicose veins of lower extremity in pregnancy, third trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O22.10", + "display": "Genital varices in pregnancy, unspecified trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O22.11", + "display": "Genital varices in pregnancy, first trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O22.12", + "display": "Genital varices in pregnancy, second trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O22.13", + "display": "Genital varices in pregnancy, third trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O22.20", + "display": "Superficial thrombophlebitis in pregnancy, unspecified trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O22.21", + "display": "Superficial thrombophlebitis in pregnancy, first trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O22.22", + "display": "Superficial thrombophlebitis in pregnancy, second trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O22.23", + "display": "Superficial thrombophlebitis in pregnancy, third trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O22.30", + "display": "Deep phlebothrombosis in pregnancy, unspecified trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O22.31", + "display": "Deep phlebothrombosis in pregnancy, first trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O22.32", + "display": "Deep phlebothrombosis in pregnancy, second trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O22.33", + "display": "Deep phlebothrombosis in pregnancy, third trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O22.40", + "display": "Hemorrhoids in pregnancy, unspecified trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O22.41", + "display": "Hemorrhoids in pregnancy, first trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O22.42", + "display": "Hemorrhoids in pregnancy, second trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O22.43", + "display": "Hemorrhoids in pregnancy, third trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O22.50", + "display": "Cerebral venous thrombosis in pregnancy, unspecified trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O22.51", + "display": "Cerebral venous thrombosis in pregnancy, first trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O22.52", + "display": "Cerebral venous thrombosis in pregnancy, second trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O22.53", + "display": "Cerebral venous thrombosis in pregnancy, third trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O22.8X1", + "display": "Other venous complications in pregnancy, first trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O22.8X2", + "display": "Other venous complications in pregnancy, second trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O22.8X3", + "display": "Other venous complications in pregnancy, third trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O22.8X9", + "display": "Other venous complications in pregnancy, unspecified trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O22.90", + "display": "Venous complication in pregnancy, unspecified, unspecified trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O22.91", + "display": "Venous complication in pregnancy, unspecified, first trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O22.92", + "display": "Venous complication in pregnancy, unspecified, second trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O22.93", + "display": "Venous complication in pregnancy, unspecified, third trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O23.00", + "display": "Infections of kidney in pregnancy, unspecified trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O23.01", + "display": "Infections of kidney in pregnancy, first trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O23.02", + "display": "Infections of kidney in pregnancy, second trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O23.03", + "display": "Infections of kidney in pregnancy, third trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O23.10", + "display": "Infections of bladder in pregnancy, unspecified trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O23.11", + "display": "Infections of bladder in pregnancy, first trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O23.12", + "display": "Infections of bladder in pregnancy, second trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O23.13", + "display": "Infections of bladder in pregnancy, third trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O23.20", + "display": "Infections of urethra in pregnancy, unspecified trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O23.21", + "display": "Infections of urethra in pregnancy, first trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O23.22", + "display": "Infections of urethra in pregnancy, second trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O23.23", + "display": "Infections of urethra in pregnancy, third trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O23.30", + "display": "Infections of other parts of urinary tract in pregnancy, unspecified trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O23.31", + "display": "Infections of other parts of urinary tract in pregnancy, first trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O23.32", + "display": "Infections of other parts of urinary tract in pregnancy, second trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O23.33", + "display": "Infections of other parts of urinary tract in pregnancy, third trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O23.40", + "display": "Unspecified infection of urinary tract in pregnancy, unspecified trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O23.41", + "display": "Unspecified infection of urinary tract in pregnancy, first trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O23.42", + "display": "Unspecified infection of urinary tract in pregnancy, second trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O23.43", + "display": "Unspecified infection of urinary tract in pregnancy, third trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O23.511", + "display": "Infections of cervix in pregnancy, first trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O23.512", + "display": "Infections of cervix in pregnancy, second trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O23.513", + "display": "Infections of cervix in pregnancy, third trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O23.519", + "display": "Infections of cervix in pregnancy, unspecified trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O23.521", + "display": "Salpingo-oophoritis in pregnancy, first trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O23.522", + "display": "Salpingo-oophoritis in pregnancy, second trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O23.523", + "display": "Salpingo-oophoritis in pregnancy, third trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O23.529", + "display": "Salpingo-oophoritis in pregnancy, unspecified trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O23.591", + "display": "Infection of other part of genital tract in pregnancy, first trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O23.592", + "display": "Infection of other part of genital tract in pregnancy, second trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O23.593", + "display": "Infection of other part of genital tract in pregnancy, third trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O23.599", + "display": "Infection of other part of genital tract in pregnancy, unspecified trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O23.90", + "display": "Unspecified genitourinary tract infection in pregnancy, unspecified trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O23.91", + "display": "Unspecified genitourinary tract infection in pregnancy, first trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O23.92", + "display": "Unspecified genitourinary tract infection in pregnancy, second trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O23.93", + "display": "Unspecified genitourinary tract infection in pregnancy, third trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O24.011", + "display": "Pre-existing type 1 diabetes mellitus, in pregnancy, first trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O24.012", + "display": "Pre-existing type 1 diabetes mellitus, in pregnancy, second trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O24.013", + "display": "Pre-existing type 1 diabetes mellitus, in pregnancy, third trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O24.019", + "display": "Pre-existing type 1 diabetes mellitus, in pregnancy, unspecified trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O24.111", + "display": "Pre-existing type 2 diabetes mellitus, in pregnancy, first trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O24.112", + "display": "Pre-existing type 2 diabetes mellitus, in pregnancy, second trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O24.113", + "display": "Pre-existing type 2 diabetes mellitus, in pregnancy, third trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O24.119", + "display": "Pre-existing type 2 diabetes mellitus, in pregnancy, unspecified trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O24.311", + "display": "Unspecified pre-existing diabetes mellitus in pregnancy, first trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O24.312", + "display": "Unspecified pre-existing diabetes mellitus in pregnancy, second trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O24.313", + "display": "Unspecified pre-existing diabetes mellitus in pregnancy, third trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O24.319", + "display": "Unspecified pre-existing diabetes mellitus in pregnancy, unspecified trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O24.410", + "display": "Gestational diabetes mellitus in pregnancy, diet controlled" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O24.414", + "display": "Gestational diabetes mellitus in pregnancy, insulin controlled" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O24.415", + "display": "Gestational diabetes mellitus in pregnancy, controlled by oral hypoglycemic drugs" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O24.419", + "display": "Gestational diabetes mellitus in pregnancy, unspecified control" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O24.811", + "display": "Other pre-existing diabetes mellitus in pregnancy, first trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O24.812", + "display": "Other pre-existing diabetes mellitus in pregnancy, second trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O24.813", + "display": "Other pre-existing diabetes mellitus in pregnancy, third trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O24.819", + "display": "Other pre-existing diabetes mellitus in pregnancy, unspecified trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O24.911", + "display": "Unspecified diabetes mellitus in pregnancy, first trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O24.912", + "display": "Unspecified diabetes mellitus in pregnancy, second trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O24.913", + "display": "Unspecified diabetes mellitus in pregnancy, third trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O24.919", + "display": "Unspecified diabetes mellitus in pregnancy, unspecified trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O25.10", + "display": "Malnutrition in pregnancy, unspecified trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O25.11", + "display": "Malnutrition in pregnancy, first trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O25.12", + "display": "Malnutrition in pregnancy, second trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O25.13", + "display": "Malnutrition in pregnancy, third trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O26.00", + "display": "Excessive weight gain in pregnancy, unspecified trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O26.01", + "display": "Excessive weight gain in pregnancy, first trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O26.02", + "display": "Excessive weight gain in pregnancy, second trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O26.03", + "display": "Excessive weight gain in pregnancy, third trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O26.10", + "display": "Low weight gain in pregnancy, unspecified trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O26.11", + "display": "Low weight gain in pregnancy, first trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O26.12", + "display": "Low weight gain in pregnancy, second trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O26.13", + "display": "Low weight gain in pregnancy, third trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O26.20", + "display": "Pregnancy care for patient with recurrent pregnancy loss, unspecified trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O26.21", + "display": "Pregnancy care for patient with recurrent pregnancy loss, first trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O26.22", + "display": "Pregnancy care for patient with recurrent pregnancy loss, second trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O26.23", + "display": "Pregnancy care for patient with recurrent pregnancy loss, third trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O26.30", + "display": "Retained intrauterine contraceptive device in pregnancy, unspecified trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O26.31", + "display": "Retained intrauterine contraceptive device in pregnancy, first trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O26.32", + "display": "Retained intrauterine contraceptive device in pregnancy, second trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O26.33", + "display": "Retained intrauterine contraceptive device in pregnancy, third trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O26.40", + "display": "Herpes gestationis, unspecified trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O26.41", + "display": "Herpes gestationis, first trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O26.42", + "display": "Herpes gestationis, second trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O26.43", + "display": "Herpes gestationis, third trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O26.50", + "display": "Maternal hypotension syndrome, unspecified trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O26.51", + "display": "Maternal hypotension syndrome, first trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O26.52", + "display": "Maternal hypotension syndrome, second trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O26.53", + "display": "Maternal hypotension syndrome, third trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O26.611", + "display": "Liver and biliary tract disorders in pregnancy, first trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O26.612", + "display": "Liver and biliary tract disorders in pregnancy, second trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O26.613", + "display": "Liver and biliary tract disorders in pregnancy, third trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O26.619", + "display": "Liver and biliary tract disorders in pregnancy, unspecified trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O26.711", + "display": "Subluxation of symphysis (pubis) in pregnancy, first trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O26.712", + "display": "Subluxation of symphysis (pubis) in pregnancy, second trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O26.713", + "display": "Subluxation of symphysis (pubis) in pregnancy, third trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O26.719", + "display": "Subluxation of symphysis (pubis) in pregnancy, unspecified trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O26.811", + "display": "Pregnancy related exhaustion and fatigue, first trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O26.812", + "display": "Pregnancy related exhaustion and fatigue, second trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O26.813", + "display": "Pregnancy related exhaustion and fatigue, third trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O26.819", + "display": "Pregnancy related exhaustion and fatigue, unspecified trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O26.821", + "display": "Pregnancy related peripheral neuritis, first trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O26.822", + "display": "Pregnancy related peripheral neuritis, second trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O26.823", + "display": "Pregnancy related peripheral neuritis, third trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O26.829", + "display": "Pregnancy related peripheral neuritis, unspecified trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O26.831", + "display": "Pregnancy related renal disease, first trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O26.832", + "display": "Pregnancy related renal disease, second trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O26.833", + "display": "Pregnancy related renal disease, third trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O26.839", + "display": "Pregnancy related renal disease, unspecified trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O26.841", + "display": "Uterine size-date discrepancy, first trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O26.842", + "display": "Uterine size-date discrepancy, second trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O26.843", + "display": "Uterine size-date discrepancy, third trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O26.849", + "display": "Uterine size-date discrepancy, unspecified trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O26.851", + "display": "Spotting complicating pregnancy, first trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O26.852", + "display": "Spotting complicating pregnancy, second trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O26.853", + "display": "Spotting complicating pregnancy, third trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O26.859", + "display": "Spotting complicating pregnancy, unspecified trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O26.86", + "display": "Pruritic urticarial papules and plaques of pregnancy (PUPPP)" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O26.872", + "display": "Cervical shortening, second trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O26.873", + "display": "Cervical shortening, third trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O26.879", + "display": "Cervical shortening, unspecified trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O26.891", + "display": "Other specified pregnancy related conditions, first trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O26.892", + "display": "Other specified pregnancy related conditions, second trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O26.893", + "display": "Other specified pregnancy related conditions, third trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O26.899", + "display": "Other specified pregnancy related conditions, unspecified trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O26.90", + "display": "Pregnancy related conditions, unspecified, unspecified trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O26.91", + "display": "Pregnancy related conditions, unspecified, first trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O26.92", + "display": "Pregnancy related conditions, unspecified, second trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O26.93", + "display": "Pregnancy related conditions, unspecified, third trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O28.0", + "display": "Abnormal hematological finding on antenatal screening of mother" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O28.1", + "display": "Abnormal biochemical finding on antenatal screening of mother" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O28.2", + "display": "Abnormal cytological finding on antenatal screening of mother" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O28.3", + "display": "Abnormal ultrasonic finding on antenatal screening of mother" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O28.4", + "display": "Abnormal radiological finding on antenatal screening of mother" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O28.5", + "display": "Abnormal chromosomal and genetic finding on antenatal screening of mother" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O28.8", + "display": "Other abnormal findings on antenatal screening of mother" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O28.9", + "display": "Unspecified abnormal findings on antenatal screening of mother" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O29.011", + "display": "Aspiration pneumonitis due to anesthesia during pregnancy, first trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O29.012", + "display": "Aspiration pneumonitis due to anesthesia during pregnancy, second trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O29.013", + "display": "Aspiration pneumonitis due to anesthesia during pregnancy, third trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O29.019", + "display": "Aspiration pneumonitis due to anesthesia during pregnancy, unspecified trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O29.021", + "display": "Pressure collapse of lung due to anesthesia during pregnancy, first trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O29.022", + "display": "Pressure collapse of lung due to anesthesia during pregnancy, second trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O29.023", + "display": "Pressure collapse of lung due to anesthesia during pregnancy, third trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O29.029", + "display": "Pressure collapse of lung due to anesthesia during pregnancy, unspecified trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O29.091", + "display": "Other pulmonary complications of anesthesia during pregnancy, first trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O29.092", + "display": "Other pulmonary complications of anesthesia during pregnancy, second trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O29.093", + "display": "Other pulmonary complications of anesthesia during pregnancy, third trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O29.099", + "display": "Other pulmonary complications of anesthesia during pregnancy, unspecified trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O29.111", + "display": "Cardiac arrest due to anesthesia during pregnancy, first trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O29.112", + "display": "Cardiac arrest due to anesthesia during pregnancy, second trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O29.113", + "display": "Cardiac arrest due to anesthesia during pregnancy, third trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O29.119", + "display": "Cardiac arrest due to anesthesia during pregnancy, unspecified trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O29.121", + "display": "Cardiac failure due to anesthesia during pregnancy, first trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O29.122", + "display": "Cardiac failure due to anesthesia during pregnancy, second trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O29.123", + "display": "Cardiac failure due to anesthesia during pregnancy, third trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O29.129", + "display": "Cardiac failure due to anesthesia during pregnancy, unspecified trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O29.191", + "display": "Other cardiac complications of anesthesia during pregnancy, first trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O29.192", + "display": "Other cardiac complications of anesthesia during pregnancy, second trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O29.193", + "display": "Other cardiac complications of anesthesia during pregnancy, third trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O29.199", + "display": "Other cardiac complications of anesthesia during pregnancy, unspecified trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O29.211", + "display": "Cerebral anoxia due to anesthesia during pregnancy, first trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O29.212", + "display": "Cerebral anoxia due to anesthesia during pregnancy, second trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O29.213", + "display": "Cerebral anoxia due to anesthesia during pregnancy, third trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O29.219", + "display": "Cerebral anoxia due to anesthesia during pregnancy, unspecified trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O29.291", + "display": "Other central nervous system complications of anesthesia during pregnancy, first trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O29.292", + "display": "Other central nervous system complications of anesthesia during pregnancy, second trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O29.293", + "display": "Other central nervous system complications of anesthesia during pregnancy, third trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O29.299", + "display": "Other central nervous system complications of anesthesia during pregnancy, unspecified trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O29.3X1", + "display": "Toxic reaction to local anesthesia during pregnancy, first trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O29.3X2", + "display": "Toxic reaction to local anesthesia during pregnancy, second trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O29.3X3", + "display": "Toxic reaction to local anesthesia during pregnancy, third trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O29.3X9", + "display": "Toxic reaction to local anesthesia during pregnancy, unspecified trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O29.40", + "display": "Spinal and epidural anesthesia induced headache during pregnancy, unspecified trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O29.41", + "display": "Spinal and epidural anesthesia induced headache during pregnancy, first trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O29.42", + "display": "Spinal and epidural anesthesia induced headache during pregnancy, second trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O29.43", + "display": "Spinal and epidural anesthesia induced headache during pregnancy, third trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O29.5X1", + "display": "Other complications of spinal and epidural anesthesia during pregnancy, first trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O29.5X2", + "display": "Other complications of spinal and epidural anesthesia during pregnancy, second trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O29.5X3", + "display": "Other complications of spinal and epidural anesthesia during pregnancy, third trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O29.5X9", + "display": "Other complications of spinal and epidural anesthesia during pregnancy, unspecified trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O29.60", + "display": "Failed or difficult intubation for anesthesia during pregnancy, unspecified trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O29.61", + "display": "Failed or difficult intubation for anesthesia during pregnancy, first trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O29.62", + "display": "Failed or difficult intubation for anesthesia during pregnancy, second trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O29.63", + "display": "Failed or difficult intubation for anesthesia during pregnancy, third trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O29.8X1", + "display": "Other complications of anesthesia during pregnancy, first trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O29.8X2", + "display": "Other complications of anesthesia during pregnancy, second trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O29.8X3", + "display": "Other complications of anesthesia during pregnancy, third trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O29.8X9", + "display": "Other complications of anesthesia during pregnancy, unspecified trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O29.90", + "display": "Unspecified complication of anesthesia during pregnancy, unspecified trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O29.91", + "display": "Unspecified complication of anesthesia during pregnancy, first trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O29.92", + "display": "Unspecified complication of anesthesia during pregnancy, second trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O29.93", + "display": "Unspecified complication of anesthesia during pregnancy, third trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O30.001", + "display": "Twin pregnancy, unspecified number of placenta and unspecified number of amniotic sacs, first trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O30.002", + "display": "Twin pregnancy, unspecified number of placenta and unspecified number of amniotic sacs, second trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O30.003", + "display": "Twin pregnancy, unspecified number of placenta and unspecified number of amniotic sacs, third trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O30.009", + "display": "Twin pregnancy, unspecified number of placenta and unspecified number of amniotic sacs, unspecified trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O30.011", + "display": "Twin pregnancy, monochorionic/monoamniotic, first trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O30.012", + "display": "Twin pregnancy, monochorionic/monoamniotic, second trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O30.013", + "display": "Twin pregnancy, monochorionic/monoamniotic, third trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O30.019", + "display": "Twin pregnancy, monochorionic/monoamniotic, unspecified trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O30.021", + "display": "Conjoined twin pregnancy, first trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O30.022", + "display": "Conjoined twin pregnancy, second trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O30.023", + "display": "Conjoined twin pregnancy, third trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O30.029", + "display": "Conjoined twin pregnancy, unspecified trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O30.031", + "display": "Twin pregnancy, monochorionic/diamniotic, first trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O30.032", + "display": "Twin pregnancy, monochorionic/diamniotic, second trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O30.033", + "display": "Twin pregnancy, monochorionic/diamniotic, third trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O30.039", + "display": "Twin pregnancy, monochorionic/diamniotic, unspecified trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O30.041", + "display": "Twin pregnancy, dichorionic/diamniotic, first trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O30.042", + "display": "Twin pregnancy, dichorionic/diamniotic, second trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O30.043", + "display": "Twin pregnancy, dichorionic/diamniotic, third trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O30.049", + "display": "Twin pregnancy, dichorionic/diamniotic, unspecified trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O30.091", + "display": "Twin pregnancy, unable to determine number of placenta and number of amniotic sacs, first trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O30.092", + "display": "Twin pregnancy, unable to determine number of placenta and number of amniotic sacs, second trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O30.093", + "display": "Twin pregnancy, unable to determine number of placenta and number of amniotic sacs, third trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O30.099", + "display": "Twin pregnancy, unable to determine number of placenta and number of amniotic sacs, unspecified trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O30.101", + "display": "Triplet pregnancy, unspecified number of placenta and unspecified number of amniotic sacs, first trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O30.102", + "display": "Triplet pregnancy, unspecified number of placenta and unspecified number of amniotic sacs, second trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O30.103", + "display": "Triplet pregnancy, unspecified number of placenta and unspecified number of amniotic sacs, third trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O30.109", + "display": "Triplet pregnancy, unspecified number of placenta and unspecified number of amniotic sacs, unspecified trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O30.111", + "display": "Triplet pregnancy with two or more monochorionic fetuses, first trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O30.112", + "display": "Triplet pregnancy with two or more monochorionic fetuses, second trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O30.113", + "display": "Triplet pregnancy with two or more monochorionic fetuses, third trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O30.119", + "display": "Triplet pregnancy with two or more monochorionic fetuses, unspecified trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O30.121", + "display": "Triplet pregnancy with two or more monoamniotic fetuses, first trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O30.122", + "display": "Triplet pregnancy with two or more monoamniotic fetuses, second trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O30.123", + "display": "Triplet pregnancy with two or more monoamniotic fetuses, third trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O30.129", + "display": "Triplet pregnancy with two or more monoamniotic fetuses, unspecified trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O30.131", + "display": "Triplet pregnancy, trichorionic/triamniotic, first trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O30.132", + "display": "Triplet pregnancy, trichorionic/triamniotic, second trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O30.133", + "display": "Triplet pregnancy, trichorionic/triamniotic, third trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O30.139", + "display": "Triplet pregnancy, trichorionic/triamniotic, unspecified trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O30.191", + "display": "Triplet pregnancy, unable to determine number of placenta and number of amniotic sacs, first trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O30.192", + "display": "Triplet pregnancy, unable to determine number of placenta and number of amniotic sacs, second trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O30.193", + "display": "Triplet pregnancy, unable to determine number of placenta and number of amniotic sacs, third trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O30.199", + "display": "Triplet pregnancy, unable to determine number of placenta and number of amniotic sacs, unspecified trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O30.201", + "display": "Quadruplet pregnancy, unspecified number of placenta and unspecified number of amniotic sacs, first trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O30.202", + "display": "Quadruplet pregnancy, unspecified number of placenta and unspecified number of amniotic sacs, second trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O30.203", + "display": "Quadruplet pregnancy, unspecified number of placenta and unspecified number of amniotic sacs, third trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O30.209", + "display": "Quadruplet pregnancy, unspecified number of placenta and unspecified number of amniotic sacs, unspecified trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O30.211", + "display": "Quadruplet pregnancy with two or more monochorionic fetuses, first trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O30.212", + "display": "Quadruplet pregnancy with two or more monochorionic fetuses, second trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O30.213", + "display": "Quadruplet pregnancy with two or more monochorionic fetuses, third trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O30.219", + "display": "Quadruplet pregnancy with two or more monochorionic fetuses, unspecified trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O30.221", + "display": "Quadruplet pregnancy with two or more monoamniotic fetuses, first trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O30.222", + "display": "Quadruplet pregnancy with two or more monoamniotic fetuses, second trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O30.223", + "display": "Quadruplet pregnancy with two or more monoamniotic fetuses, third trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O30.229", + "display": "Quadruplet pregnancy with two or more monoamniotic fetuses, unspecified trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O30.231", + "display": "Quadruplet pregnancy, quadrachorionic/quadra-amniotic, first trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O30.232", + "display": "Quadruplet pregnancy, quadrachorionic/quadra-amniotic, second trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O30.233", + "display": "Quadruplet pregnancy, quadrachorionic/quadra-amniotic, third trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O30.239", + "display": "Quadruplet pregnancy, quadrachorionic/quadra-amniotic, unspecified trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O30.291", + "display": "Quadruplet pregnancy, unable to determine number of placenta and number of amniotic sacs, first trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O30.292", + "display": "Quadruplet pregnancy, unable to determine number of placenta and number of amniotic sacs, second trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O30.293", + "display": "Quadruplet pregnancy, unable to determine number of placenta and number of amniotic sacs, third trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O30.299", + "display": "Quadruplet pregnancy, unable to determine number of placenta and number of amniotic sacs, unspecified trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O30.801", + "display": "Other specified multiple gestation, unspecified number of placenta and unspecified number of amniotic sacs, first trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O30.802", + "display": "Other specified multiple gestation, unspecified number of placenta and unspecified number of amniotic sacs, second trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O30.803", + "display": "Other specified multiple gestation, unspecified number of placenta and unspecified number of amniotic sacs, third trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O30.809", + "display": "Other specified multiple gestation, unspecified number of placenta and unspecified number of amniotic sacs, unspecified trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O30.811", + "display": "Other specified multiple gestation with two or more monochorionic fetuses, first trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O30.812", + "display": "Other specified multiple gestation with two or more monochorionic fetuses, second trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O30.813", + "display": "Other specified multiple gestation with two or more monochorionic fetuses, third trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O30.819", + "display": "Other specified multiple gestation with two or more monochorionic fetuses, unspecified trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O30.821", + "display": "Other specified multiple gestation with two or more monoamniotic fetuses, first trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O30.822", + "display": "Other specified multiple gestation with two or more monoamniotic fetuses, second trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O30.823", + "display": "Other specified multiple gestation with two or more monoamniotic fetuses, third trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O30.829", + "display": "Other specified multiple gestation with two or more monoamniotic fetuses, unspecified trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O30.831", + "display": "Other specified multiple gestation, number of chorions and amnions are both equal to the number of fetuses, first trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O30.832", + "display": "Other specified multiple gestation, number of chorions and amnions are both equal to the number of fetuses, second trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O30.833", + "display": "Other specified multiple gestation, number of chorions and amnions are both equal to the number of fetuses, third trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O30.839", + "display": "Other specified multiple gestation, number of chorions and amnions are both equal to the number of fetuses, unspecified trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O30.891", + "display": "Other specified multiple gestation, unable to determine number of placenta and number of amniotic sacs, first trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O30.892", + "display": "Other specified multiple gestation, unable to determine number of placenta and number of amniotic sacs, second trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O30.893", + "display": "Other specified multiple gestation, unable to determine number of placenta and number of amniotic sacs, third trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O30.899", + "display": "Other specified multiple gestation, unable to determine number of placenta and number of amniotic sacs, unspecified trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O30.90", + "display": "Multiple gestation, unspecified, unspecified trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O30.91", + "display": "Multiple gestation, unspecified, first trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O30.92", + "display": "Multiple gestation, unspecified, second trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O30.93", + "display": "Multiple gestation, unspecified, third trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O31.00X0", + "display": "Papyraceous fetus, unspecified trimester, not applicable or unspecified" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O31.00X1", + "display": "Papyraceous fetus, unspecified trimester, fetus 1" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O31.00X2", + "display": "Papyraceous fetus, unspecified trimester, fetus 2" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O31.00X3", + "display": "Papyraceous fetus, unspecified trimester, fetus 3" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O31.00X4", + "display": "Papyraceous fetus, unspecified trimester, fetus 4" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O31.00X5", + "display": "Papyraceous fetus, unspecified trimester, fetus 5" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O31.00X9", + "display": "Papyraceous fetus, unspecified trimester, other fetus" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O31.01X0", + "display": "Papyraceous fetus, first trimester, not applicable or unspecified" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O31.01X1", + "display": "Papyraceous fetus, first trimester, fetus 1" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O31.01X2", + "display": "Papyraceous fetus, first trimester, fetus 2" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O31.01X3", + "display": "Papyraceous fetus, first trimester, fetus 3" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O31.01X4", + "display": "Papyraceous fetus, first trimester, fetus 4" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O31.01X5", + "display": "Papyraceous fetus, first trimester, fetus 5" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O31.01X9", + "display": "Papyraceous fetus, first trimester, other fetus" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O31.02X0", + "display": "Papyraceous fetus, second trimester, not applicable or unspecified" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O31.02X1", + "display": "Papyraceous fetus, second trimester, fetus 1" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O31.02X2", + "display": "Papyraceous fetus, second trimester, fetus 2" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O31.02X3", + "display": "Papyraceous fetus, second trimester, fetus 3" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O31.02X4", + "display": "Papyraceous fetus, second trimester, fetus 4" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O31.02X5", + "display": "Papyraceous fetus, second trimester, fetus 5" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O31.02X9", + "display": "Papyraceous fetus, second trimester, other fetus" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O31.03X0", + "display": "Papyraceous fetus, third trimester, not applicable or unspecified" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O31.03X1", + "display": "Papyraceous fetus, third trimester, fetus 1" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O31.03X2", + "display": "Papyraceous fetus, third trimester, fetus 2" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O31.03X3", + "display": "Papyraceous fetus, third trimester, fetus 3" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O31.03X4", + "display": "Papyraceous fetus, third trimester, fetus 4" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O31.03X5", + "display": "Papyraceous fetus, third trimester, fetus 5" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O31.03X9", + "display": "Papyraceous fetus, third trimester, other fetus" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O31.10X0", + "display": "Continuing pregnancy after spontaneous abortion of one fetus or more, unspecified trimester, not applicable or unspecified" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O31.10X1", + "display": "Continuing pregnancy after spontaneous abortion of one fetus or more, unspecified trimester, fetus 1" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O31.10X2", + "display": "Continuing pregnancy after spontaneous abortion of one fetus or more, unspecified trimester, fetus 2" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O31.10X3", + "display": "Continuing pregnancy after spontaneous abortion of one fetus or more, unspecified trimester, fetus 3" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O31.10X4", + "display": "Continuing pregnancy after spontaneous abortion of one fetus or more, unspecified trimester, fetus 4" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O31.10X5", + "display": "Continuing pregnancy after spontaneous abortion of one fetus or more, unspecified trimester, fetus 5" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O31.10X9", + "display": "Continuing pregnancy after spontaneous abortion of one fetus or more, unspecified trimester, other fetus" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O31.11X0", + "display": "Continuing pregnancy after spontaneous abortion of one fetus or more, first trimester, not applicable or unspecified" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O31.11X1", + "display": "Continuing pregnancy after spontaneous abortion of one fetus or more, first trimester, fetus 1" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O31.11X2", + "display": "Continuing pregnancy after spontaneous abortion of one fetus or more, first trimester, fetus 2" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O31.11X3", + "display": "Continuing pregnancy after spontaneous abortion of one fetus or more, first trimester, fetus 3" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O31.11X4", + "display": "Continuing pregnancy after spontaneous abortion of one fetus or more, first trimester, fetus 4" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O31.11X5", + "display": "Continuing pregnancy after spontaneous abortion of one fetus or more, first trimester, fetus 5" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O31.11X9", + "display": "Continuing pregnancy after spontaneous abortion of one fetus or more, first trimester, other fetus" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O31.12X0", + "display": "Continuing pregnancy after spontaneous abortion of one fetus or more, second trimester, not applicable or unspecified" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O31.12X1", + "display": "Continuing pregnancy after spontaneous abortion of one fetus or more, second trimester, fetus 1" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O31.12X2", + "display": "Continuing pregnancy after spontaneous abortion of one fetus or more, second trimester, fetus 2" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O31.12X3", + "display": "Continuing pregnancy after spontaneous abortion of one fetus or more, second trimester, fetus 3" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O31.12X4", + "display": "Continuing pregnancy after spontaneous abortion of one fetus or more, second trimester, fetus 4" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O31.12X5", + "display": "Continuing pregnancy after spontaneous abortion of one fetus or more, second trimester, fetus 5" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O31.12X9", + "display": "Continuing pregnancy after spontaneous abortion of one fetus or more, second trimester, other fetus" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O31.13X0", + "display": "Continuing pregnancy after spontaneous abortion of one fetus or more, third trimester, not applicable or unspecified" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O31.13X1", + "display": "Continuing pregnancy after spontaneous abortion of one fetus or more, third trimester, fetus 1" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O31.13X2", + "display": "Continuing pregnancy after spontaneous abortion of one fetus or more, third trimester, fetus 2" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O31.13X3", + "display": "Continuing pregnancy after spontaneous abortion of one fetus or more, third trimester, fetus 3" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O31.13X4", + "display": "Continuing pregnancy after spontaneous abortion of one fetus or more, third trimester, fetus 4" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O31.13X5", + "display": "Continuing pregnancy after spontaneous abortion of one fetus or more, third trimester, fetus 5" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O31.13X9", + "display": "Continuing pregnancy after spontaneous abortion of one fetus or more, third trimester, other fetus" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O31.20X0", + "display": "Continuing pregnancy after intrauterine death of one fetus or more, unspecified trimester, not applicable or unspecified" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O31.20X1", + "display": "Continuing pregnancy after intrauterine death of one fetus or more, unspecified trimester, fetus 1" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O31.20X2", + "display": "Continuing pregnancy after intrauterine death of one fetus or more, unspecified trimester, fetus 2" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O31.20X3", + "display": "Continuing pregnancy after intrauterine death of one fetus or more, unspecified trimester, fetus 3" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O31.20X4", + "display": "Continuing pregnancy after intrauterine death of one fetus or more, unspecified trimester, fetus 4" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O31.20X5", + "display": "Continuing pregnancy after intrauterine death of one fetus or more, unspecified trimester, fetus 5" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O31.20X9", + "display": "Continuing pregnancy after intrauterine death of one fetus or more, unspecified trimester, other fetus" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O31.21X0", + "display": "Continuing pregnancy after intrauterine death of one fetus or more, first trimester, not applicable or unspecified" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O31.21X1", + "display": "Continuing pregnancy after intrauterine death of one fetus or more, first trimester, fetus 1" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O31.21X2", + "display": "Continuing pregnancy after intrauterine death of one fetus or more, first trimester, fetus 2" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O31.21X3", + "display": "Continuing pregnancy after intrauterine death of one fetus or more, first trimester, fetus 3" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O31.21X4", + "display": "Continuing pregnancy after intrauterine death of one fetus or more, first trimester, fetus 4" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O31.21X5", + "display": "Continuing pregnancy after intrauterine death of one fetus or more, first trimester, fetus 5" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O31.21X9", + "display": "Continuing pregnancy after intrauterine death of one fetus or more, first trimester, other fetus" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O31.22X0", + "display": "Continuing pregnancy after intrauterine death of one fetus or more, second trimester, not applicable or unspecified" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O31.22X1", + "display": "Continuing pregnancy after intrauterine death of one fetus or more, second trimester, fetus 1" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O31.22X2", + "display": "Continuing pregnancy after intrauterine death of one fetus or more, second trimester, fetus 2" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O31.22X3", + "display": "Continuing pregnancy after intrauterine death of one fetus or more, second trimester, fetus 3" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O31.22X4", + "display": "Continuing pregnancy after intrauterine death of one fetus or more, second trimester, fetus 4" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O31.22X5", + "display": "Continuing pregnancy after intrauterine death of one fetus or more, second trimester, fetus 5" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O31.22X9", + "display": "Continuing pregnancy after intrauterine death of one fetus or more, second trimester, other fetus" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O31.23X0", + "display": "Continuing pregnancy after intrauterine death of one fetus or more, third trimester, not applicable or unspecified" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O31.23X1", + "display": "Continuing pregnancy after intrauterine death of one fetus or more, third trimester, fetus 1" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O31.23X2", + "display": "Continuing pregnancy after intrauterine death of one fetus or more, third trimester, fetus 2" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O31.23X3", + "display": "Continuing pregnancy after intrauterine death of one fetus or more, third trimester, fetus 3" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O31.23X4", + "display": "Continuing pregnancy after intrauterine death of one fetus or more, third trimester, fetus 4" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O31.23X5", + "display": "Continuing pregnancy after intrauterine death of one fetus or more, third trimester, fetus 5" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O31.23X9", + "display": "Continuing pregnancy after intrauterine death of one fetus or more, third trimester, other fetus" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O31.30X0", + "display": "Continuing pregnancy after elective fetal reduction of one fetus or more, unspecified trimester, not applicable or unspecified" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O31.30X1", + "display": "Continuing pregnancy after elective fetal reduction of one fetus or more, unspecified trimester, fetus 1" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O31.30X2", + "display": "Continuing pregnancy after elective fetal reduction of one fetus or more, unspecified trimester, fetus 2" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O31.30X3", + "display": "Continuing pregnancy after elective fetal reduction of one fetus or more, unspecified trimester, fetus 3" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O31.30X4", + "display": "Continuing pregnancy after elective fetal reduction of one fetus or more, unspecified trimester, fetus 4" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O31.30X5", + "display": "Continuing pregnancy after elective fetal reduction of one fetus or more, unspecified trimester, fetus 5" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O31.30X9", + "display": "Continuing pregnancy after elective fetal reduction of one fetus or more, unspecified trimester, other fetus" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O31.31X0", + "display": "Continuing pregnancy after elective fetal reduction of one fetus or more, first trimester, not applicable or unspecified" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O31.31X1", + "display": "Continuing pregnancy after elective fetal reduction of one fetus or more, first trimester, fetus 1" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O31.31X2", + "display": "Continuing pregnancy after elective fetal reduction of one fetus or more, first trimester, fetus 2" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O31.31X3", + "display": "Continuing pregnancy after elective fetal reduction of one fetus or more, first trimester, fetus 3" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O31.31X4", + "display": "Continuing pregnancy after elective fetal reduction of one fetus or more, first trimester, fetus 4" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O31.31X5", + "display": "Continuing pregnancy after elective fetal reduction of one fetus or more, first trimester, fetus 5" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O31.31X9", + "display": "Continuing pregnancy after elective fetal reduction of one fetus or more, first trimester, other fetus" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O31.32X0", + "display": "Continuing pregnancy after elective fetal reduction of one fetus or more, second trimester, not applicable or unspecified" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O31.32X1", + "display": "Continuing pregnancy after elective fetal reduction of one fetus or more, second trimester, fetus 1" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O31.32X2", + "display": "Continuing pregnancy after elective fetal reduction of one fetus or more, second trimester, fetus 2" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O31.32X3", + "display": "Continuing pregnancy after elective fetal reduction of one fetus or more, second trimester, fetus 3" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O31.32X4", + "display": "Continuing pregnancy after elective fetal reduction of one fetus or more, second trimester, fetus 4" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O31.32X5", + "display": "Continuing pregnancy after elective fetal reduction of one fetus or more, second trimester, fetus 5" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O31.32X9", + "display": "Continuing pregnancy after elective fetal reduction of one fetus or more, second trimester, other fetus" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O31.33X0", + "display": "Continuing pregnancy after elective fetal reduction of one fetus or more, third trimester, not applicable or unspecified" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O31.33X1", + "display": "Continuing pregnancy after elective fetal reduction of one fetus or more, third trimester, fetus 1" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O31.33X2", + "display": "Continuing pregnancy after elective fetal reduction of one fetus or more, third trimester, fetus 2" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O31.33X3", + "display": "Continuing pregnancy after elective fetal reduction of one fetus or more, third trimester, fetus 3" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O31.33X4", + "display": "Continuing pregnancy after elective fetal reduction of one fetus or more, third trimester, fetus 4" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O31.33X5", + "display": "Continuing pregnancy after elective fetal reduction of one fetus or more, third trimester, fetus 5" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O31.33X9", + "display": "Continuing pregnancy after elective fetal reduction of one fetus or more, third trimester, other fetus" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O31.8X10", + "display": "Other complications specific to multiple gestation, first trimester, not applicable or unspecified" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O31.8X11", + "display": "Other complications specific to multiple gestation, first trimester, fetus 1" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O31.8X12", + "display": "Other complications specific to multiple gestation, first trimester, fetus 2" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O31.8X13", + "display": "Other complications specific to multiple gestation, first trimester, fetus 3" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O31.8X14", + "display": "Other complications specific to multiple gestation, first trimester, fetus 4" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O31.8X15", + "display": "Other complications specific to multiple gestation, first trimester, fetus 5" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O31.8X19", + "display": "Other complications specific to multiple gestation, first trimester, other fetus" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O31.8X20", + "display": "Other complications specific to multiple gestation, second trimester, not applicable or unspecified" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O31.8X21", + "display": "Other complications specific to multiple gestation, second trimester, fetus 1" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O31.8X22", + "display": "Other complications specific to multiple gestation, second trimester, fetus 2" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O31.8X23", + "display": "Other complications specific to multiple gestation, second trimester, fetus 3" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O31.8X24", + "display": "Other complications specific to multiple gestation, second trimester, fetus 4" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O31.8X25", + "display": "Other complications specific to multiple gestation, second trimester, fetus 5" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O31.8X29", + "display": "Other complications specific to multiple gestation, second trimester, other fetus" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O31.8X30", + "display": "Other complications specific to multiple gestation, third trimester, not applicable or unspecified" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O31.8X31", + "display": "Other complications specific to multiple gestation, third trimester, fetus 1" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O31.8X32", + "display": "Other complications specific to multiple gestation, third trimester, fetus 2" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O31.8X33", + "display": "Other complications specific to multiple gestation, third trimester, fetus 3" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O31.8X34", + "display": "Other complications specific to multiple gestation, third trimester, fetus 4" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O31.8X35", + "display": "Other complications specific to multiple gestation, third trimester, fetus 5" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O31.8X39", + "display": "Other complications specific to multiple gestation, third trimester, other fetus" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O31.8X90", + "display": "Other complications specific to multiple gestation, unspecified trimester, not applicable or unspecified" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O31.8X91", + "display": "Other complications specific to multiple gestation, unspecified trimester, fetus 1" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O31.8X92", + "display": "Other complications specific to multiple gestation, unspecified trimester, fetus 2" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O31.8X93", + "display": "Other complications specific to multiple gestation, unspecified trimester, fetus 3" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O31.8X94", + "display": "Other complications specific to multiple gestation, unspecified trimester, fetus 4" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O31.8X95", + "display": "Other complications specific to multiple gestation, unspecified trimester, fetus 5" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O31.8X99", + "display": "Other complications specific to multiple gestation, unspecified trimester, other fetus" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O32.0XX0", + "display": "Maternal care for unstable lie, not applicable or unspecified" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O32.0XX1", + "display": "Maternal care for unstable lie, fetus 1" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O32.0XX2", + "display": "Maternal care for unstable lie, fetus 2" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O32.0XX3", + "display": "Maternal care for unstable lie, fetus 3" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O32.0XX4", + "display": "Maternal care for unstable lie, fetus 4" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O32.0XX5", + "display": "Maternal care for unstable lie, fetus 5" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O32.0XX9", + "display": "Maternal care for unstable lie, other fetus" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O32.1XX0", + "display": "Maternal care for breech presentation, not applicable or unspecified" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O32.1XX1", + "display": "Maternal care for breech presentation, fetus 1" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O32.1XX2", + "display": "Maternal care for breech presentation, fetus 2" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O32.1XX3", + "display": "Maternal care for breech presentation, fetus 3" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O32.1XX4", + "display": "Maternal care for breech presentation, fetus 4" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O32.1XX5", + "display": "Maternal care for breech presentation, fetus 5" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O32.1XX9", + "display": "Maternal care for breech presentation, other fetus" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O32.2XX0", + "display": "Maternal care for transverse and oblique lie, not applicable or unspecified" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O32.2XX1", + "display": "Maternal care for transverse and oblique lie, fetus 1" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O32.2XX2", + "display": "Maternal care for transverse and oblique lie, fetus 2" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O32.2XX3", + "display": "Maternal care for transverse and oblique lie, fetus 3" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O32.2XX4", + "display": "Maternal care for transverse and oblique lie, fetus 4" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O32.2XX5", + "display": "Maternal care for transverse and oblique lie, fetus 5" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O32.2XX9", + "display": "Maternal care for transverse and oblique lie, other fetus" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O32.3XX0", + "display": "Maternal care for face, brow and chin presentation, not applicable or unspecified" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O32.3XX1", + "display": "Maternal care for face, brow and chin presentation, fetus 1" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O32.3XX2", + "display": "Maternal care for face, brow and chin presentation, fetus 2" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O32.3XX3", + "display": "Maternal care for face, brow and chin presentation, fetus 3" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O32.3XX4", + "display": "Maternal care for face, brow and chin presentation, fetus 4" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O32.3XX5", + "display": "Maternal care for face, brow and chin presentation, fetus 5" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O32.3XX9", + "display": "Maternal care for face, brow and chin presentation, other fetus" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O32.4XX0", + "display": "Maternal care for high head at term, not applicable or unspecified" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O32.4XX1", + "display": "Maternal care for high head at term, fetus 1" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O32.4XX2", + "display": "Maternal care for high head at term, fetus 2" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O32.4XX3", + "display": "Maternal care for high head at term, fetus 3" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O32.4XX4", + "display": "Maternal care for high head at term, fetus 4" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O32.4XX5", + "display": "Maternal care for high head at term, fetus 5" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O32.4XX9", + "display": "Maternal care for high head at term, other fetus" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O32.6XX0", + "display": "Maternal care for compound presentation, not applicable or unspecified" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O32.6XX1", + "display": "Maternal care for compound presentation, fetus 1" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O32.6XX2", + "display": "Maternal care for compound presentation, fetus 2" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O32.6XX3", + "display": "Maternal care for compound presentation, fetus 3" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O32.6XX4", + "display": "Maternal care for compound presentation, fetus 4" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O32.6XX5", + "display": "Maternal care for compound presentation, fetus 5" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O32.6XX9", + "display": "Maternal care for compound presentation, other fetus" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O32.8XX0", + "display": "Maternal care for other malpresentation of fetus, not applicable or unspecified" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O32.8XX1", + "display": "Maternal care for other malpresentation of fetus, fetus 1" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O32.8XX2", + "display": "Maternal care for other malpresentation of fetus, fetus 2" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O32.8XX3", + "display": "Maternal care for other malpresentation of fetus, fetus 3" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O32.8XX4", + "display": "Maternal care for other malpresentation of fetus, fetus 4" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O32.8XX5", + "display": "Maternal care for other malpresentation of fetus, fetus 5" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O32.8XX9", + "display": "Maternal care for other malpresentation of fetus, other fetus" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O32.9XX0", + "display": "Maternal care for malpresentation of fetus, unspecified, not applicable or unspecified" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O32.9XX1", + "display": "Maternal care for malpresentation of fetus, unspecified, fetus 1" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O32.9XX2", + "display": "Maternal care for malpresentation of fetus, unspecified, fetus 2" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O32.9XX3", + "display": "Maternal care for malpresentation of fetus, unspecified, fetus 3" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O32.9XX4", + "display": "Maternal care for malpresentation of fetus, unspecified, fetus 4" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O32.9XX5", + "display": "Maternal care for malpresentation of fetus, unspecified, fetus 5" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O32.9XX9", + "display": "Maternal care for malpresentation of fetus, unspecified, other fetus" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O33.0", + "display": "Maternal care for disproportion due to deformity of maternal pelvic bones" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O33.1", + "display": "Maternal care for disproportion due to generally contracted pelvis" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O33.2", + "display": "Maternal care for disproportion due to inlet contraction of pelvis" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O33.3XX0", + "display": "Maternal care for disproportion due to outlet contraction of pelvis, not applicable or unspecified" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O33.3XX1", + "display": "Maternal care for disproportion due to outlet contraction of pelvis, fetus 1" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O33.3XX2", + "display": "Maternal care for disproportion due to outlet contraction of pelvis, fetus 2" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O33.3XX3", + "display": "Maternal care for disproportion due to outlet contraction of pelvis, fetus 3" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O33.3XX4", + "display": "Maternal care for disproportion due to outlet contraction of pelvis, fetus 4" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O33.3XX5", + "display": "Maternal care for disproportion due to outlet contraction of pelvis, fetus 5" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O33.3XX9", + "display": "Maternal care for disproportion due to outlet contraction of pelvis, other fetus" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O33.4XX0", + "display": "Maternal care for disproportion of mixed maternal and fetal origin, not applicable or unspecified" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O33.4XX1", + "display": "Maternal care for disproportion of mixed maternal and fetal origin, fetus 1" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O33.4XX2", + "display": "Maternal care for disproportion of mixed maternal and fetal origin, fetus 2" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O33.4XX3", + "display": "Maternal care for disproportion of mixed maternal and fetal origin, fetus 3" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O33.4XX4", + "display": "Maternal care for disproportion of mixed maternal and fetal origin, fetus 4" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O33.4XX5", + "display": "Maternal care for disproportion of mixed maternal and fetal origin, fetus 5" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O33.4XX9", + "display": "Maternal care for disproportion of mixed maternal and fetal origin, other fetus" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O33.5XX0", + "display": "Maternal care for disproportion due to unusually large fetus, not applicable or unspecified" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O33.5XX1", + "display": "Maternal care for disproportion due to unusually large fetus, fetus 1" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O33.5XX2", + "display": "Maternal care for disproportion due to unusually large fetus, fetus 2" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O33.5XX3", + "display": "Maternal care for disproportion due to unusually large fetus, fetus 3" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O33.5XX4", + "display": "Maternal care for disproportion due to unusually large fetus, fetus 4" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O33.5XX5", + "display": "Maternal care for disproportion due to unusually large fetus, fetus 5" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O33.5XX9", + "display": "Maternal care for disproportion due to unusually large fetus, other fetus" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O33.6XX0", + "display": "Maternal care for disproportion due to hydrocephalic fetus, not applicable or unspecified" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O33.6XX1", + "display": "Maternal care for disproportion due to hydrocephalic fetus, fetus 1" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O33.6XX2", + "display": "Maternal care for disproportion due to hydrocephalic fetus, fetus 2" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O33.6XX3", + "display": "Maternal care for disproportion due to hydrocephalic fetus, fetus 3" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O33.6XX4", + "display": "Maternal care for disproportion due to hydrocephalic fetus, fetus 4" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O33.6XX5", + "display": "Maternal care for disproportion due to hydrocephalic fetus, fetus 5" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O33.6XX9", + "display": "Maternal care for disproportion due to hydrocephalic fetus, other fetus" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O33.7XX0", + "display": "Maternal care for disproportion due to other fetal deformities, not applicable or unspecified" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O33.7XX1", + "display": "Maternal care for disproportion due to other fetal deformities, fetus 1" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O33.7XX2", + "display": "Maternal care for disproportion due to other fetal deformities, fetus 2" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O33.7XX3", + "display": "Maternal care for disproportion due to other fetal deformities, fetus 3" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O33.7XX4", + "display": "Maternal care for disproportion due to other fetal deformities, fetus 4" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O33.7XX5", + "display": "Maternal care for disproportion due to other fetal deformities, fetus 5" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O33.7XX9", + "display": "Maternal care for disproportion due to other fetal deformities, other fetus" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O33.8", + "display": "Maternal care for disproportion of other origin" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O33.9", + "display": "Maternal care for disproportion, unspecified" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O34.00", + "display": "Maternal care for unspecified congenital malformation of uterus, unspecified trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O34.01", + "display": "Maternal care for unspecified congenital malformation of uterus, first trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O34.02", + "display": "Maternal care for unspecified congenital malformation of uterus, second trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O34.03", + "display": "Maternal care for unspecified congenital malformation of uterus, third trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O34.10", + "display": "Maternal care for benign tumor of corpus uteri, unspecified trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O34.11", + "display": "Maternal care for benign tumor of corpus uteri, first trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O34.12", + "display": "Maternal care for benign tumor of corpus uteri, second trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O34.13", + "display": "Maternal care for benign tumor of corpus uteri, third trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O34.211", + "display": "Maternal care for low transverse scar from previous cesarean delivery" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O34.212", + "display": "Maternal care for vertical scar from previous cesarean delivery" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O34.219", + "display": "Maternal care for unspecified type scar from previous cesarean delivery" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O34.29", + "display": "Maternal care due to uterine scar from other previous surgery" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O34.30", + "display": "Maternal care for cervical incompetence, unspecified trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O34.31", + "display": "Maternal care for cervical incompetence, first trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O34.32", + "display": "Maternal care for cervical incompetence, second trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O34.33", + "display": "Maternal care for cervical incompetence, third trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O34.40", + "display": "Maternal care for other abnormalities of cervix, unspecified trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O34.41", + "display": "Maternal care for other abnormalities of cervix, first trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O34.42", + "display": "Maternal care for other abnormalities of cervix, second trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O34.43", + "display": "Maternal care for other abnormalities of cervix, third trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O34.511", + "display": "Maternal care for incarceration of gravid uterus, first trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O34.512", + "display": "Maternal care for incarceration of gravid uterus, second trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O34.513", + "display": "Maternal care for incarceration of gravid uterus, third trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O34.519", + "display": "Maternal care for incarceration of gravid uterus, unspecified trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O34.521", + "display": "Maternal care for prolapse of gravid uterus, first trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O34.522", + "display": "Maternal care for prolapse of gravid uterus, second trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O34.523", + "display": "Maternal care for prolapse of gravid uterus, third trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O34.529", + "display": "Maternal care for prolapse of gravid uterus, unspecified trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O34.531", + "display": "Maternal care for retroversion of gravid uterus, first trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O34.532", + "display": "Maternal care for retroversion of gravid uterus, second trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O34.533", + "display": "Maternal care for retroversion of gravid uterus, third trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O34.539", + "display": "Maternal care for retroversion of gravid uterus, unspecified trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O34.591", + "display": "Maternal care for other abnormalities of gravid uterus, first trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O34.592", + "display": "Maternal care for other abnormalities of gravid uterus, second trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O34.593", + "display": "Maternal care for other abnormalities of gravid uterus, third trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O34.599", + "display": "Maternal care for other abnormalities of gravid uterus, unspecified trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O34.60", + "display": "Maternal care for abnormality of vagina, unspecified trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O34.61", + "display": "Maternal care for abnormality of vagina, first trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O34.62", + "display": "Maternal care for abnormality of vagina, second trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O34.63", + "display": "Maternal care for abnormality of vagina, third trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O34.70", + "display": "Maternal care for abnormality of vulva and perineum, unspecified trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O34.71", + "display": "Maternal care for abnormality of vulva and perineum, first trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O34.72", + "display": "Maternal care for abnormality of vulva and perineum, second trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O34.73", + "display": "Maternal care for abnormality of vulva and perineum, third trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O34.80", + "display": "Maternal care for other abnormalities of pelvic organs, unspecified trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O34.81", + "display": "Maternal care for other abnormalities of pelvic organs, first trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O34.82", + "display": "Maternal care for other abnormalities of pelvic organs, second trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O34.83", + "display": "Maternal care for other abnormalities of pelvic organs, third trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O34.90", + "display": "Maternal care for abnormality of pelvic organ, unspecified, unspecified trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O34.91", + "display": "Maternal care for abnormality of pelvic organ, unspecified, first trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O34.92", + "display": "Maternal care for abnormality of pelvic organ, unspecified, second trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O34.93", + "display": "Maternal care for abnormality of pelvic organ, unspecified, third trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O35.2XX0", + "display": "Maternal care for (suspected) hereditary disease in fetus, not applicable or unspecified" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O35.2XX1", + "display": "Maternal care for (suspected) hereditary disease in fetus, fetus 1" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O35.2XX2", + "display": "Maternal care for (suspected) hereditary disease in fetus, fetus 2" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O35.2XX3", + "display": "Maternal care for (suspected) hereditary disease in fetus, fetus 3" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O35.2XX4", + "display": "Maternal care for (suspected) hereditary disease in fetus, fetus 4" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O35.2XX5", + "display": "Maternal care for (suspected) hereditary disease in fetus, fetus 5" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O35.2XX9", + "display": "Maternal care for (suspected) hereditary disease in fetus, other fetus" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O35.3XX0", + "display": "Maternal care for (suspected) damage to fetus from viral disease in mother, not applicable or unspecified" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O35.3XX1", + "display": "Maternal care for (suspected) damage to fetus from viral disease in mother, fetus 1" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O35.3XX2", + "display": "Maternal care for (suspected) damage to fetus from viral disease in mother, fetus 2" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O35.3XX3", + "display": "Maternal care for (suspected) damage to fetus from viral disease in mother, fetus 3" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O35.3XX4", + "display": "Maternal care for (suspected) damage to fetus from viral disease in mother, fetus 4" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O35.3XX5", + "display": "Maternal care for (suspected) damage to fetus from viral disease in mother, fetus 5" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O35.3XX9", + "display": "Maternal care for (suspected) damage to fetus from viral disease in mother, other fetus" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O35.4XX0", + "display": "Maternal care for (suspected) damage to fetus from alcohol, not applicable or unspecified" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O35.4XX1", + "display": "Maternal care for (suspected) damage to fetus from alcohol, fetus 1" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O35.4XX2", + "display": "Maternal care for (suspected) damage to fetus from alcohol, fetus 2" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O35.4XX3", + "display": "Maternal care for (suspected) damage to fetus from alcohol, fetus 3" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O35.4XX4", + "display": "Maternal care for (suspected) damage to fetus from alcohol, fetus 4" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O35.4XX5", + "display": "Maternal care for (suspected) damage to fetus from alcohol, fetus 5" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O35.4XX9", + "display": "Maternal care for (suspected) damage to fetus from alcohol, other fetus" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O35.5XX0", + "display": "Maternal care for (suspected) damage to fetus by drugs, not applicable or unspecified" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O35.5XX1", + "display": "Maternal care for (suspected) damage to fetus by drugs, fetus 1" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O35.5XX2", + "display": "Maternal care for (suspected) damage to fetus by drugs, fetus 2" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O35.5XX3", + "display": "Maternal care for (suspected) damage to fetus by drugs, fetus 3" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O35.5XX4", + "display": "Maternal care for (suspected) damage to fetus by drugs, fetus 4" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O35.5XX5", + "display": "Maternal care for (suspected) damage to fetus by drugs, fetus 5" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O35.5XX9", + "display": "Maternal care for (suspected) damage to fetus by drugs, other fetus" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O35.6XX0", + "display": "Maternal care for (suspected) damage to fetus by radiation, not applicable or unspecified" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O35.6XX1", + "display": "Maternal care for (suspected) damage to fetus by radiation, fetus 1" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O35.6XX2", + "display": "Maternal care for (suspected) damage to fetus by radiation, fetus 2" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O35.6XX3", + "display": "Maternal care for (suspected) damage to fetus by radiation, fetus 3" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O35.6XX4", + "display": "Maternal care for (suspected) damage to fetus by radiation, fetus 4" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O35.6XX5", + "display": "Maternal care for (suspected) damage to fetus by radiation, fetus 5" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O35.6XX9", + "display": "Maternal care for (suspected) damage to fetus by radiation, other fetus" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O35.7XX0", + "display": "Maternal care for (suspected) damage to fetus by other medical procedures, not applicable or unspecified" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O35.7XX1", + "display": "Maternal care for (suspected) damage to fetus by other medical procedures, fetus 1" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O35.7XX2", + "display": "Maternal care for (suspected) damage to fetus by other medical procedures, fetus 2" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O35.7XX3", + "display": "Maternal care for (suspected) damage to fetus by other medical procedures, fetus 3" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O35.7XX4", + "display": "Maternal care for (suspected) damage to fetus by other medical procedures, fetus 4" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O35.7XX5", + "display": "Maternal care for (suspected) damage to fetus by other medical procedures, fetus 5" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O35.7XX9", + "display": "Maternal care for (suspected) damage to fetus by other medical procedures, other fetus" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O35.8XX0", + "display": "Maternal care for other (suspected) fetal abnormality and damage, not applicable or unspecified" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O35.8XX1", + "display": "Maternal care for other (suspected) fetal abnormality and damage, fetus 1" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O35.8XX2", + "display": "Maternal care for other (suspected) fetal abnormality and damage, fetus 2" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O35.8XX3", + "display": "Maternal care for other (suspected) fetal abnormality and damage, fetus 3" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O35.8XX4", + "display": "Maternal care for other (suspected) fetal abnormality and damage, fetus 4" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O35.8XX5", + "display": "Maternal care for other (suspected) fetal abnormality and damage, fetus 5" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O35.8XX9", + "display": "Maternal care for other (suspected) fetal abnormality and damage, other fetus" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O35.9XX0", + "display": "Maternal care for (suspected) fetal abnormality and damage, unspecified, not applicable or unspecified" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O35.9XX1", + "display": "Maternal care for (suspected) fetal abnormality and damage, unspecified, fetus 1" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O35.9XX2", + "display": "Maternal care for (suspected) fetal abnormality and damage, unspecified, fetus 2" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O35.9XX3", + "display": "Maternal care for (suspected) fetal abnormality and damage, unspecified, fetus 3" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O35.9XX4", + "display": "Maternal care for (suspected) fetal abnormality and damage, unspecified, fetus 4" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O35.9XX5", + "display": "Maternal care for (suspected) fetal abnormality and damage, unspecified, fetus 5" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O35.9XX9", + "display": "Maternal care for (suspected) fetal abnormality and damage, unspecified, other fetus" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O36.0110", + "display": "Maternal care for anti-D [Rh] antibodies, first trimester, not applicable or unspecified" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O36.0111", + "display": "Maternal care for anti-D [Rh] antibodies, first trimester, fetus 1" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O36.0112", + "display": "Maternal care for anti-D [Rh] antibodies, first trimester, fetus 2" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O36.0113", + "display": "Maternal care for anti-D [Rh] antibodies, first trimester, fetus 3" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O36.0114", + "display": "Maternal care for anti-D [Rh] antibodies, first trimester, fetus 4" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O36.0115", + "display": "Maternal care for anti-D [Rh] antibodies, first trimester, fetus 5" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O36.0119", + "display": "Maternal care for anti-D [Rh] antibodies, first trimester, other fetus" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O36.0120", + "display": "Maternal care for anti-D [Rh] antibodies, second trimester, not applicable or unspecified" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O36.0121", + "display": "Maternal care for anti-D [Rh] antibodies, second trimester, fetus 1" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O36.0122", + "display": "Maternal care for anti-D [Rh] antibodies, second trimester, fetus 2" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O36.0123", + "display": "Maternal care for anti-D [Rh] antibodies, second trimester, fetus 3" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O36.0124", + "display": "Maternal care for anti-D [Rh] antibodies, second trimester, fetus 4" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O36.0125", + "display": "Maternal care for anti-D [Rh] antibodies, second trimester, fetus 5" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O36.0129", + "display": "Maternal care for anti-D [Rh] antibodies, second trimester, other fetus" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O36.0130", + "display": "Maternal care for anti-D [Rh] antibodies, third trimester, not applicable or unspecified" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O36.0131", + "display": "Maternal care for anti-D [Rh] antibodies, third trimester, fetus 1" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O36.0132", + "display": "Maternal care for anti-D [Rh] antibodies, third trimester, fetus 2" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O36.0133", + "display": "Maternal care for anti-D [Rh] antibodies, third trimester, fetus 3" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O36.0134", + "display": "Maternal care for anti-D [Rh] antibodies, third trimester, fetus 4" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O36.0135", + "display": "Maternal care for anti-D [Rh] antibodies, third trimester, fetus 5" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O36.0139", + "display": "Maternal care for anti-D [Rh] antibodies, third trimester, other fetus" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O36.0190", + "display": "Maternal care for anti-D [Rh] antibodies, unspecified trimester, not applicable or unspecified" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O36.0191", + "display": "Maternal care for anti-D [Rh] antibodies, unspecified trimester, fetus 1" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O36.0192", + "display": "Maternal care for anti-D [Rh] antibodies, unspecified trimester, fetus 2" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O36.0193", + "display": "Maternal care for anti-D [Rh] antibodies, unspecified trimester, fetus 3" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O36.0194", + "display": "Maternal care for anti-D [Rh] antibodies, unspecified trimester, fetus 4" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O36.0195", + "display": "Maternal care for anti-D [Rh] antibodies, unspecified trimester, fetus 5" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O36.0199", + "display": "Maternal care for anti-D [Rh] antibodies, unspecified trimester, other fetus" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O36.0910", + "display": "Maternal care for other rhesus isoimmunization, first trimester, not applicable or unspecified" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O36.0911", + "display": "Maternal care for other rhesus isoimmunization, first trimester, fetus 1" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O36.0912", + "display": "Maternal care for other rhesus isoimmunization, first trimester, fetus 2" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O36.0913", + "display": "Maternal care for other rhesus isoimmunization, first trimester, fetus 3" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O36.0914", + "display": "Maternal care for other rhesus isoimmunization, first trimester, fetus 4" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O36.0915", + "display": "Maternal care for other rhesus isoimmunization, first trimester, fetus 5" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O36.0919", + "display": "Maternal care for other rhesus isoimmunization, first trimester, other fetus" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O36.0920", + "display": "Maternal care for other rhesus isoimmunization, second trimester, not applicable or unspecified" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O36.0921", + "display": "Maternal care for other rhesus isoimmunization, second trimester, fetus 1" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O36.0922", + "display": "Maternal care for other rhesus isoimmunization, second trimester, fetus 2" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O36.0923", + "display": "Maternal care for other rhesus isoimmunization, second trimester, fetus 3" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O36.0924", + "display": "Maternal care for other rhesus isoimmunization, second trimester, fetus 4" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O36.0925", + "display": "Maternal care for other rhesus isoimmunization, second trimester, fetus 5" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O36.0929", + "display": "Maternal care for other rhesus isoimmunization, second trimester, other fetus" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O36.0930", + "display": "Maternal care for other rhesus isoimmunization, third trimester, not applicable or unspecified" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O36.0931", + "display": "Maternal care for other rhesus isoimmunization, third trimester, fetus 1" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O36.0932", + "display": "Maternal care for other rhesus isoimmunization, third trimester, fetus 2" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O36.0933", + "display": "Maternal care for other rhesus isoimmunization, third trimester, fetus 3" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O36.0934", + "display": "Maternal care for other rhesus isoimmunization, third trimester, fetus 4" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O36.0935", + "display": "Maternal care for other rhesus isoimmunization, third trimester, fetus 5" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O36.0939", + "display": "Maternal care for other rhesus isoimmunization, third trimester, other fetus" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O36.0990", + "display": "Maternal care for other rhesus isoimmunization, unspecified trimester, not applicable or unspecified" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O36.0991", + "display": "Maternal care for other rhesus isoimmunization, unspecified trimester, fetus 1" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O36.0992", + "display": "Maternal care for other rhesus isoimmunization, unspecified trimester, fetus 2" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O36.0993", + "display": "Maternal care for other rhesus isoimmunization, unspecified trimester, fetus 3" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O36.0994", + "display": "Maternal care for other rhesus isoimmunization, unspecified trimester, fetus 4" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O36.0995", + "display": "Maternal care for other rhesus isoimmunization, unspecified trimester, fetus 5" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O36.0999", + "display": "Maternal care for other rhesus isoimmunization, unspecified trimester, other fetus" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O36.1110", + "display": "Maternal care for Anti-A sensitization, first trimester, not applicable or unspecified" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O36.1111", + "display": "Maternal care for Anti-A sensitization, first trimester, fetus 1" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O36.1112", + "display": "Maternal care for Anti-A sensitization, first trimester, fetus 2" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O36.1113", + "display": "Maternal care for Anti-A sensitization, first trimester, fetus 3" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O36.1114", + "display": "Maternal care for Anti-A sensitization, first trimester, fetus 4" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O36.1115", + "display": "Maternal care for Anti-A sensitization, first trimester, fetus 5" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O36.1119", + "display": "Maternal care for Anti-A sensitization, first trimester, other fetus" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O36.1120", + "display": "Maternal care for Anti-A sensitization, second trimester, not applicable or unspecified" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O36.1121", + "display": "Maternal care for Anti-A sensitization, second trimester, fetus 1" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O36.1122", + "display": "Maternal care for Anti-A sensitization, second trimester, fetus 2" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O36.1123", + "display": "Maternal care for Anti-A sensitization, second trimester, fetus 3" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O36.1124", + "display": "Maternal care for Anti-A sensitization, second trimester, fetus 4" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O36.1125", + "display": "Maternal care for Anti-A sensitization, second trimester, fetus 5" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O36.1129", + "display": "Maternal care for Anti-A sensitization, second trimester, other fetus" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O36.1130", + "display": "Maternal care for Anti-A sensitization, third trimester, not applicable or unspecified" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O36.1131", + "display": "Maternal care for Anti-A sensitization, third trimester, fetus 1" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O36.1132", + "display": "Maternal care for Anti-A sensitization, third trimester, fetus 2" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O36.1133", + "display": "Maternal care for Anti-A sensitization, third trimester, fetus 3" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O36.1134", + "display": "Maternal care for Anti-A sensitization, third trimester, fetus 4" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O36.1135", + "display": "Maternal care for Anti-A sensitization, third trimester, fetus 5" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O36.1139", + "display": "Maternal care for Anti-A sensitization, third trimester, other fetus" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O36.1190", + "display": "Maternal care for Anti-A sensitization, unspecified trimester, not applicable or unspecified" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O36.1191", + "display": "Maternal care for Anti-A sensitization, unspecified trimester, fetus 1" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O36.1192", + "display": "Maternal care for Anti-A sensitization, unspecified trimester, fetus 2" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O36.1193", + "display": "Maternal care for Anti-A sensitization, unspecified trimester, fetus 3" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O36.1194", + "display": "Maternal care for Anti-A sensitization, unspecified trimester, fetus 4" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O36.1195", + "display": "Maternal care for Anti-A sensitization, unspecified trimester, fetus 5" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O36.1199", + "display": "Maternal care for Anti-A sensitization, unspecified trimester, other fetus" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O36.1910", + "display": "Maternal care for other isoimmunization, first trimester, not applicable or unspecified" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O36.1911", + "display": "Maternal care for other isoimmunization, first trimester, fetus 1" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O36.1912", + "display": "Maternal care for other isoimmunization, first trimester, fetus 2" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O36.1913", + "display": "Maternal care for other isoimmunization, first trimester, fetus 3" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O36.1914", + "display": "Maternal care for other isoimmunization, first trimester, fetus 4" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O36.1915", + "display": "Maternal care for other isoimmunization, first trimester, fetus 5" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O36.1919", + "display": "Maternal care for other isoimmunization, first trimester, other fetus" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O36.1920", + "display": "Maternal care for other isoimmunization, second trimester, not applicable or unspecified" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O36.1921", + "display": "Maternal care for other isoimmunization, second trimester, fetus 1" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O36.1922", + "display": "Maternal care for other isoimmunization, second trimester, fetus 2" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O36.1923", + "display": "Maternal care for other isoimmunization, second trimester, fetus 3" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O36.1924", + "display": "Maternal care for other isoimmunization, second trimester, fetus 4" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O36.1925", + "display": "Maternal care for other isoimmunization, second trimester, fetus 5" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O36.1929", + "display": "Maternal care for other isoimmunization, second trimester, other fetus" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O36.1930", + "display": "Maternal care for other isoimmunization, third trimester, not applicable or unspecified" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O36.1931", + "display": "Maternal care for other isoimmunization, third trimester, fetus 1" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O36.1932", + "display": "Maternal care for other isoimmunization, third trimester, fetus 2" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O36.1933", + "display": "Maternal care for other isoimmunization, third trimester, fetus 3" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O36.1934", + "display": "Maternal care for other isoimmunization, third trimester, fetus 4" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O36.1935", + "display": "Maternal care for other isoimmunization, third trimester, fetus 5" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O36.1939", + "display": "Maternal care for other isoimmunization, third trimester, other fetus" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O36.1990", + "display": "Maternal care for other isoimmunization, unspecified trimester, not applicable or unspecified" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O36.1991", + "display": "Maternal care for other isoimmunization, unspecified trimester, fetus 1" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O36.1992", + "display": "Maternal care for other isoimmunization, unspecified trimester, fetus 2" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O36.1993", + "display": "Maternal care for other isoimmunization, unspecified trimester, fetus 3" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O36.1994", + "display": "Maternal care for other isoimmunization, unspecified trimester, fetus 4" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O36.1995", + "display": "Maternal care for other isoimmunization, unspecified trimester, fetus 5" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O36.1999", + "display": "Maternal care for other isoimmunization, unspecified trimester, other fetus" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O36.20X0", + "display": "Maternal care for hydrops fetalis, unspecified trimester, not applicable or unspecified" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O36.20X1", + "display": "Maternal care for hydrops fetalis, unspecified trimester, fetus 1" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O36.20X2", + "display": "Maternal care for hydrops fetalis, unspecified trimester, fetus 2" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O36.20X3", + "display": "Maternal care for hydrops fetalis, unspecified trimester, fetus 3" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O36.20X4", + "display": "Maternal care for hydrops fetalis, unspecified trimester, fetus 4" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O36.20X5", + "display": "Maternal care for hydrops fetalis, unspecified trimester, fetus 5" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O36.20X9", + "display": "Maternal care for hydrops fetalis, unspecified trimester, other fetus" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O36.21X0", + "display": "Maternal care for hydrops fetalis, first trimester, not applicable or unspecified" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O36.21X1", + "display": "Maternal care for hydrops fetalis, first trimester, fetus 1" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O36.21X2", + "display": "Maternal care for hydrops fetalis, first trimester, fetus 2" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O36.21X3", + "display": "Maternal care for hydrops fetalis, first trimester, fetus 3" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O36.21X4", + "display": "Maternal care for hydrops fetalis, first trimester, fetus 4" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O36.21X5", + "display": "Maternal care for hydrops fetalis, first trimester, fetus 5" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O36.21X9", + "display": "Maternal care for hydrops fetalis, first trimester, other fetus" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O36.22X0", + "display": "Maternal care for hydrops fetalis, second trimester, not applicable or unspecified" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O36.22X1", + "display": "Maternal care for hydrops fetalis, second trimester, fetus 1" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O36.22X2", + "display": "Maternal care for hydrops fetalis, second trimester, fetus 2" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O36.22X3", + "display": "Maternal care for hydrops fetalis, second trimester, fetus 3" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O36.22X4", + "display": "Maternal care for hydrops fetalis, second trimester, fetus 4" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O36.22X5", + "display": "Maternal care for hydrops fetalis, second trimester, fetus 5" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O36.22X9", + "display": "Maternal care for hydrops fetalis, second trimester, other fetus" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O36.23X0", + "display": "Maternal care for hydrops fetalis, third trimester, not applicable or unspecified" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O36.23X1", + "display": "Maternal care for hydrops fetalis, third trimester, fetus 1" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O36.23X2", + "display": "Maternal care for hydrops fetalis, third trimester, fetus 2" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O36.23X3", + "display": "Maternal care for hydrops fetalis, third trimester, fetus 3" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O36.23X4", + "display": "Maternal care for hydrops fetalis, third trimester, fetus 4" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O36.23X5", + "display": "Maternal care for hydrops fetalis, third trimester, fetus 5" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O36.23X9", + "display": "Maternal care for hydrops fetalis, third trimester, other fetus" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O36.4XX0", + "display": "Maternal care for intrauterine death, not applicable or unspecified" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O36.4XX1", + "display": "Maternal care for intrauterine death, fetus 1" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O36.4XX2", + "display": "Maternal care for intrauterine death, fetus 2" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O36.4XX3", + "display": "Maternal care for intrauterine death, fetus 3" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O36.4XX4", + "display": "Maternal care for intrauterine death, fetus 4" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O36.4XX5", + "display": "Maternal care for intrauterine death, fetus 5" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O36.4XX9", + "display": "Maternal care for intrauterine death, other fetus" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O36.5110", + "display": "Maternal care for known or suspected placental insufficiency, first trimester, not applicable or unspecified" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O36.5111", + "display": "Maternal care for known or suspected placental insufficiency, first trimester, fetus 1" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O36.5112", + "display": "Maternal care for known or suspected placental insufficiency, first trimester, fetus 2" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O36.5113", + "display": "Maternal care for known or suspected placental insufficiency, first trimester, fetus 3" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O36.5114", + "display": "Maternal care for known or suspected placental insufficiency, first trimester, fetus 4" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O36.5115", + "display": "Maternal care for known or suspected placental insufficiency, first trimester, fetus 5" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O36.5119", + "display": "Maternal care for known or suspected placental insufficiency, first trimester, other fetus" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O36.5120", + "display": "Maternal care for known or suspected placental insufficiency, second trimester, not applicable or unspecified" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O36.5121", + "display": "Maternal care for known or suspected placental insufficiency, second trimester, fetus 1" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O36.5122", + "display": "Maternal care for known or suspected placental insufficiency, second trimester, fetus 2" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O36.5123", + "display": "Maternal care for known or suspected placental insufficiency, second trimester, fetus 3" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O36.5124", + "display": "Maternal care for known or suspected placental insufficiency, second trimester, fetus 4" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O36.5125", + "display": "Maternal care for known or suspected placental insufficiency, second trimester, fetus 5" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O36.5129", + "display": "Maternal care for known or suspected placental insufficiency, second trimester, other fetus" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O36.5130", + "display": "Maternal care for known or suspected placental insufficiency, third trimester, not applicable or unspecified" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O36.5131", + "display": "Maternal care for known or suspected placental insufficiency, third trimester, fetus 1" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O36.5132", + "display": "Maternal care for known or suspected placental insufficiency, third trimester, fetus 2" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O36.5133", + "display": "Maternal care for known or suspected placental insufficiency, third trimester, fetus 3" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O36.5134", + "display": "Maternal care for known or suspected placental insufficiency, third trimester, fetus 4" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O36.5135", + "display": "Maternal care for known or suspected placental insufficiency, third trimester, fetus 5" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O36.5139", + "display": "Maternal care for known or suspected placental insufficiency, third trimester, other fetus" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O36.5190", + "display": "Maternal care for known or suspected placental insufficiency, unspecified trimester, not applicable or unspecified" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O36.5191", + "display": "Maternal care for known or suspected placental insufficiency, unspecified trimester, fetus 1" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O36.5192", + "display": "Maternal care for known or suspected placental insufficiency, unspecified trimester, fetus 2" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O36.5193", + "display": "Maternal care for known or suspected placental insufficiency, unspecified trimester, fetus 3" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O36.5194", + "display": "Maternal care for known or suspected placental insufficiency, unspecified trimester, fetus 4" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O36.5195", + "display": "Maternal care for known or suspected placental insufficiency, unspecified trimester, fetus 5" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O36.5199", + "display": "Maternal care for known or suspected placental insufficiency, unspecified trimester, other fetus" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O36.5910", + "display": "Maternal care for other known or suspected poor fetal growth, first trimester, not applicable or unspecified" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O36.5911", + "display": "Maternal care for other known or suspected poor fetal growth, first trimester, fetus 1" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O36.5912", + "display": "Maternal care for other known or suspected poor fetal growth, first trimester, fetus 2" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O36.5913", + "display": "Maternal care for other known or suspected poor fetal growth, first trimester, fetus 3" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O36.5914", + "display": "Maternal care for other known or suspected poor fetal growth, first trimester, fetus 4" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O36.5915", + "display": "Maternal care for other known or suspected poor fetal growth, first trimester, fetus 5" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O36.5919", + "display": "Maternal care for other known or suspected poor fetal growth, first trimester, other fetus" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O36.5920", + "display": "Maternal care for other known or suspected poor fetal growth, second trimester, not applicable or unspecified" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O36.5921", + "display": "Maternal care for other known or suspected poor fetal growth, second trimester, fetus 1" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O36.5922", + "display": "Maternal care for other known or suspected poor fetal growth, second trimester, fetus 2" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O36.5923", + "display": "Maternal care for other known or suspected poor fetal growth, second trimester, fetus 3" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O36.5924", + "display": "Maternal care for other known or suspected poor fetal growth, second trimester, fetus 4" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O36.5925", + "display": "Maternal care for other known or suspected poor fetal growth, second trimester, fetus 5" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O36.5929", + "display": "Maternal care for other known or suspected poor fetal growth, second trimester, other fetus" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O36.5930", + "display": "Maternal care for other known or suspected poor fetal growth, third trimester, not applicable or unspecified" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O36.5931", + "display": "Maternal care for other known or suspected poor fetal growth, third trimester, fetus 1" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O36.5932", + "display": "Maternal care for other known or suspected poor fetal growth, third trimester, fetus 2" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O36.5933", + "display": "Maternal care for other known or suspected poor fetal growth, third trimester, fetus 3" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O36.5934", + "display": "Maternal care for other known or suspected poor fetal growth, third trimester, fetus 4" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O36.5935", + "display": "Maternal care for other known or suspected poor fetal growth, third trimester, fetus 5" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O36.5939", + "display": "Maternal care for other known or suspected poor fetal growth, third trimester, other fetus" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O36.5990", + "display": "Maternal care for other known or suspected poor fetal growth, unspecified trimester, not applicable or unspecified" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O36.5991", + "display": "Maternal care for other known or suspected poor fetal growth, unspecified trimester, fetus 1" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O36.5992", + "display": "Maternal care for other known or suspected poor fetal growth, unspecified trimester, fetus 2" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O36.5993", + "display": "Maternal care for other known or suspected poor fetal growth, unspecified trimester, fetus 3" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O36.5994", + "display": "Maternal care for other known or suspected poor fetal growth, unspecified trimester, fetus 4" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O36.5995", + "display": "Maternal care for other known or suspected poor fetal growth, unspecified trimester, fetus 5" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O36.5999", + "display": "Maternal care for other known or suspected poor fetal growth, unspecified trimester, other fetus" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O36.60X0", + "display": "Maternal care for excessive fetal growth, unspecified trimester, not applicable or unspecified" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O36.60X1", + "display": "Maternal care for excessive fetal growth, unspecified trimester, fetus 1" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O36.60X2", + "display": "Maternal care for excessive fetal growth, unspecified trimester, fetus 2" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O36.60X3", + "display": "Maternal care for excessive fetal growth, unspecified trimester, fetus 3" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O36.60X4", + "display": "Maternal care for excessive fetal growth, unspecified trimester, fetus 4" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O36.60X5", + "display": "Maternal care for excessive fetal growth, unspecified trimester, fetus 5" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O36.60X9", + "display": "Maternal care for excessive fetal growth, unspecified trimester, other fetus" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O36.61X0", + "display": "Maternal care for excessive fetal growth, first trimester, not applicable or unspecified" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O36.61X1", + "display": "Maternal care for excessive fetal growth, first trimester, fetus 1" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O36.61X2", + "display": "Maternal care for excessive fetal growth, first trimester, fetus 2" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O36.61X3", + "display": "Maternal care for excessive fetal growth, first trimester, fetus 3" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O36.61X4", + "display": "Maternal care for excessive fetal growth, first trimester, fetus 4" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O36.61X5", + "display": "Maternal care for excessive fetal growth, first trimester, fetus 5" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O36.61X9", + "display": "Maternal care for excessive fetal growth, first trimester, other fetus" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O36.62X0", + "display": "Maternal care for excessive fetal growth, second trimester, not applicable or unspecified" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O36.62X1", + "display": "Maternal care for excessive fetal growth, second trimester, fetus 1" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O36.62X2", + "display": "Maternal care for excessive fetal growth, second trimester, fetus 2" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O36.62X3", + "display": "Maternal care for excessive fetal growth, second trimester, fetus 3" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O36.62X4", + "display": "Maternal care for excessive fetal growth, second trimester, fetus 4" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O36.62X5", + "display": "Maternal care for excessive fetal growth, second trimester, fetus 5" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O36.62X9", + "display": "Maternal care for excessive fetal growth, second trimester, other fetus" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O36.63X0", + "display": "Maternal care for excessive fetal growth, third trimester, not applicable or unspecified" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O36.63X1", + "display": "Maternal care for excessive fetal growth, third trimester, fetus 1" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O36.63X2", + "display": "Maternal care for excessive fetal growth, third trimester, fetus 2" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O36.63X3", + "display": "Maternal care for excessive fetal growth, third trimester, fetus 3" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O36.63X4", + "display": "Maternal care for excessive fetal growth, third trimester, fetus 4" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O36.63X5", + "display": "Maternal care for excessive fetal growth, third trimester, fetus 5" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O36.63X9", + "display": "Maternal care for excessive fetal growth, third trimester, other fetus" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O36.70X0", + "display": "Maternal care for viable fetus in abdominal pregnancy, unspecified trimester, not applicable or unspecified" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O36.70X1", + "display": "Maternal care for viable fetus in abdominal pregnancy, unspecified trimester, fetus 1" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O36.70X2", + "display": "Maternal care for viable fetus in abdominal pregnancy, unspecified trimester, fetus 2" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O36.70X3", + "display": "Maternal care for viable fetus in abdominal pregnancy, unspecified trimester, fetus 3" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O36.70X4", + "display": "Maternal care for viable fetus in abdominal pregnancy, unspecified trimester, fetus 4" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O36.70X5", + "display": "Maternal care for viable fetus in abdominal pregnancy, unspecified trimester, fetus 5" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O36.70X9", + "display": "Maternal care for viable fetus in abdominal pregnancy, unspecified trimester, other fetus" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O36.71X0", + "display": "Maternal care for viable fetus in abdominal pregnancy, first trimester, not applicable or unspecified" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O36.71X1", + "display": "Maternal care for viable fetus in abdominal pregnancy, first trimester, fetus 1" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O36.71X2", + "display": "Maternal care for viable fetus in abdominal pregnancy, first trimester, fetus 2" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O36.71X3", + "display": "Maternal care for viable fetus in abdominal pregnancy, first trimester, fetus 3" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O36.71X4", + "display": "Maternal care for viable fetus in abdominal pregnancy, first trimester, fetus 4" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O36.71X5", + "display": "Maternal care for viable fetus in abdominal pregnancy, first trimester, fetus 5" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O36.71X9", + "display": "Maternal care for viable fetus in abdominal pregnancy, first trimester, other fetus" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O36.72X0", + "display": "Maternal care for viable fetus in abdominal pregnancy, second trimester, not applicable or unspecified" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O36.72X1", + "display": "Maternal care for viable fetus in abdominal pregnancy, second trimester, fetus 1" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O36.72X2", + "display": "Maternal care for viable fetus in abdominal pregnancy, second trimester, fetus 2" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O36.72X3", + "display": "Maternal care for viable fetus in abdominal pregnancy, second trimester, fetus 3" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O36.72X4", + "display": "Maternal care for viable fetus in abdominal pregnancy, second trimester, fetus 4" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O36.72X5", + "display": "Maternal care for viable fetus in abdominal pregnancy, second trimester, fetus 5" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O36.72X9", + "display": "Maternal care for viable fetus in abdominal pregnancy, second trimester, other fetus" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O36.73X0", + "display": "Maternal care for viable fetus in abdominal pregnancy, third trimester, not applicable or unspecified" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O36.73X1", + "display": "Maternal care for viable fetus in abdominal pregnancy, third trimester, fetus 1" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O36.73X2", + "display": "Maternal care for viable fetus in abdominal pregnancy, third trimester, fetus 2" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O36.73X3", + "display": "Maternal care for viable fetus in abdominal pregnancy, third trimester, fetus 3" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O36.73X4", + "display": "Maternal care for viable fetus in abdominal pregnancy, third trimester, fetus 4" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O36.73X5", + "display": "Maternal care for viable fetus in abdominal pregnancy, third trimester, fetus 5" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O36.73X9", + "display": "Maternal care for viable fetus in abdominal pregnancy, third trimester, other fetus" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O36.8120", + "display": "Decreased fetal movements, second trimester, not applicable or unspecified" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O36.8121", + "display": "Decreased fetal movements, second trimester, fetus 1" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O36.8122", + "display": "Decreased fetal movements, second trimester, fetus 2" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O36.8123", + "display": "Decreased fetal movements, second trimester, fetus 3" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O36.8124", + "display": "Decreased fetal movements, second trimester, fetus 4" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O36.8125", + "display": "Decreased fetal movements, second trimester, fetus 5" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O36.8129", + "display": "Decreased fetal movements, second trimester, other fetus" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O36.8130", + "display": "Decreased fetal movements, third trimester, not applicable or unspecified" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O36.8131", + "display": "Decreased fetal movements, third trimester, fetus 1" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O36.8132", + "display": "Decreased fetal movements, third trimester, fetus 2" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O36.8133", + "display": "Decreased fetal movements, third trimester, fetus 3" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O36.8134", + "display": "Decreased fetal movements, third trimester, fetus 4" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O36.8135", + "display": "Decreased fetal movements, third trimester, fetus 5" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O36.8139", + "display": "Decreased fetal movements, third trimester, other fetus" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O36.8190", + "display": "Decreased fetal movements, unspecified trimester, not applicable or unspecified" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O36.8191", + "display": "Decreased fetal movements, unspecified trimester, fetus 1" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O36.8192", + "display": "Decreased fetal movements, unspecified trimester, fetus 2" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O36.8193", + "display": "Decreased fetal movements, unspecified trimester, fetus 3" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O36.8194", + "display": "Decreased fetal movements, unspecified trimester, fetus 4" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O36.8195", + "display": "Decreased fetal movements, unspecified trimester, fetus 5" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O36.8199", + "display": "Decreased fetal movements, unspecified trimester, other fetus" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O36.8210", + "display": "Fetal anemia and thrombocytopenia, first trimester, not applicable or unspecified" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O36.8211", + "display": "Fetal anemia and thrombocytopenia, first trimester, fetus 1" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O36.8212", + "display": "Fetal anemia and thrombocytopenia, first trimester, fetus 2" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O36.8213", + "display": "Fetal anemia and thrombocytopenia, first trimester, fetus 3" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O36.8214", + "display": "Fetal anemia and thrombocytopenia, first trimester, fetus 4" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O36.8215", + "display": "Fetal anemia and thrombocytopenia, first trimester, fetus 5" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O36.8219", + "display": "Fetal anemia and thrombocytopenia, first trimester, other fetus" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O36.8220", + "display": "Fetal anemia and thrombocytopenia, second trimester, not applicable or unspecified" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O36.8221", + "display": "Fetal anemia and thrombocytopenia, second trimester, fetus 1" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O36.8222", + "display": "Fetal anemia and thrombocytopenia, second trimester, fetus 2" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O36.8223", + "display": "Fetal anemia and thrombocytopenia, second trimester, fetus 3" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O36.8224", + "display": "Fetal anemia and thrombocytopenia, second trimester, fetus 4" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O36.8225", + "display": "Fetal anemia and thrombocytopenia, second trimester, fetus 5" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O36.8229", + "display": "Fetal anemia and thrombocytopenia, second trimester, other fetus" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O36.8230", + "display": "Fetal anemia and thrombocytopenia, third trimester, not applicable or unspecified" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O36.8231", + "display": "Fetal anemia and thrombocytopenia, third trimester, fetus 1" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O36.8232", + "display": "Fetal anemia and thrombocytopenia, third trimester, fetus 2" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O36.8233", + "display": "Fetal anemia and thrombocytopenia, third trimester, fetus 3" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O36.8234", + "display": "Fetal anemia and thrombocytopenia, third trimester, fetus 4" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O36.8235", + "display": "Fetal anemia and thrombocytopenia, third trimester, fetus 5" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O36.8239", + "display": "Fetal anemia and thrombocytopenia, third trimester, other fetus" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O36.8290", + "display": "Fetal anemia and thrombocytopenia, unspecified trimester, not applicable or unspecified" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O36.8291", + "display": "Fetal anemia and thrombocytopenia, unspecified trimester, fetus 1" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O36.8292", + "display": "Fetal anemia and thrombocytopenia, unspecified trimester, fetus 2" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O36.8293", + "display": "Fetal anemia and thrombocytopenia, unspecified trimester, fetus 3" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O36.8294", + "display": "Fetal anemia and thrombocytopenia, unspecified trimester, fetus 4" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O36.8295", + "display": "Fetal anemia and thrombocytopenia, unspecified trimester, fetus 5" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O36.8299", + "display": "Fetal anemia and thrombocytopenia, unspecified trimester, other fetus" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O36.8310", + "display": "Maternal care for abnormalities of the fetal heart rate or rhythm, first trimester, not applicable or unspecified" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O36.8311", + "display": "Maternal care for abnormalities of the fetal heart rate or rhythm, first trimester, fetus 1" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O36.8312", + "display": "Maternal care for abnormalities of the fetal heart rate or rhythm, first trimester, fetus 2" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O36.8313", + "display": "Maternal care for abnormalities of the fetal heart rate or rhythm, first trimester, fetus 3" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O36.8314", + "display": "Maternal care for abnormalities of the fetal heart rate or rhythm, first trimester, fetus 4" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O36.8315", + "display": "Maternal care for abnormalities of the fetal heart rate or rhythm, first trimester, fetus 5" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O36.8319", + "display": "Maternal care for abnormalities of the fetal heart rate or rhythm, first trimester, other fetus" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O36.8320", + "display": "Maternal care for abnormalities of the fetal heart rate or rhythm, second trimester, not applicable or unspecified" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O36.8321", + "display": "Maternal care for abnormalities of the fetal heart rate or rhythm, second trimester, fetus 1" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O36.8322", + "display": "Maternal care for abnormalities of the fetal heart rate or rhythm, second trimester, fetus 2" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O36.8323", + "display": "Maternal care for abnormalities of the fetal heart rate or rhythm, second trimester, fetus 3" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O36.8324", + "display": "Maternal care for abnormalities of the fetal heart rate or rhythm, second trimester, fetus 4" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O36.8325", + "display": "Maternal care for abnormalities of the fetal heart rate or rhythm, second trimester, fetus 5" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O36.8329", + "display": "Maternal care for abnormalities of the fetal heart rate or rhythm, second trimester, other fetus" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O36.8330", + "display": "Maternal care for abnormalities of the fetal heart rate or rhythm, third trimester, not applicable or unspecified" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O36.8331", + "display": "Maternal care for abnormalities of the fetal heart rate or rhythm, third trimester, fetus 1" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O36.8332", + "display": "Maternal care for abnormalities of the fetal heart rate or rhythm, third trimester, fetus 2" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O36.8333", + "display": "Maternal care for abnormalities of the fetal heart rate or rhythm, third trimester, fetus 3" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O36.8334", + "display": "Maternal care for abnormalities of the fetal heart rate or rhythm, third trimester, fetus 4" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O36.8335", + "display": "Maternal care for abnormalities of the fetal heart rate or rhythm, third trimester, fetus 5" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O36.8339", + "display": "Maternal care for abnormalities of the fetal heart rate or rhythm, third trimester, other fetus" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O36.8390", + "display": "Maternal care for abnormalities of the fetal heart rate or rhythm, unspecified trimester, not applicable or unspecified" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O36.8391", + "display": "Maternal care for abnormalities of the fetal heart rate or rhythm, unspecified trimester, fetus 1" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O36.8392", + "display": "Maternal care for abnormalities of the fetal heart rate or rhythm, unspecified trimester, fetus 2" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O36.8393", + "display": "Maternal care for abnormalities of the fetal heart rate or rhythm, unspecified trimester, fetus 3" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O36.8394", + "display": "Maternal care for abnormalities of the fetal heart rate or rhythm, unspecified trimester, fetus 4" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O36.8395", + "display": "Maternal care for abnormalities of the fetal heart rate or rhythm, unspecified trimester, fetus 5" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O36.8399", + "display": "Maternal care for abnormalities of the fetal heart rate or rhythm, unspecified trimester, other fetus" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O36.8910", + "display": "Maternal care for other specified fetal problems, first trimester, not applicable or unspecified" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O36.8911", + "display": "Maternal care for other specified fetal problems, first trimester, fetus 1" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O36.8912", + "display": "Maternal care for other specified fetal problems, first trimester, fetus 2" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O36.8913", + "display": "Maternal care for other specified fetal problems, first trimester, fetus 3" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O36.8914", + "display": "Maternal care for other specified fetal problems, first trimester, fetus 4" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O36.8915", + "display": "Maternal care for other specified fetal problems, first trimester, fetus 5" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O36.8919", + "display": "Maternal care for other specified fetal problems, first trimester, other fetus" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O36.8920", + "display": "Maternal care for other specified fetal problems, second trimester, not applicable or unspecified" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O36.8921", + "display": "Maternal care for other specified fetal problems, second trimester, fetus 1" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O36.8922", + "display": "Maternal care for other specified fetal problems, second trimester, fetus 2" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O36.8923", + "display": "Maternal care for other specified fetal problems, second trimester, fetus 3" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O36.8924", + "display": "Maternal care for other specified fetal problems, second trimester, fetus 4" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O36.8925", + "display": "Maternal care for other specified fetal problems, second trimester, fetus 5" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O36.8929", + "display": "Maternal care for other specified fetal problems, second trimester, other fetus" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O36.8930", + "display": "Maternal care for other specified fetal problems, third trimester, not applicable or unspecified" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O36.8931", + "display": "Maternal care for other specified fetal problems, third trimester, fetus 1" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O36.8932", + "display": "Maternal care for other specified fetal problems, third trimester, fetus 2" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O36.8933", + "display": "Maternal care for other specified fetal problems, third trimester, fetus 3" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O36.8934", + "display": "Maternal care for other specified fetal problems, third trimester, fetus 4" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O36.8935", + "display": "Maternal care for other specified fetal problems, third trimester, fetus 5" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O36.8939", + "display": "Maternal care for other specified fetal problems, third trimester, other fetus" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O36.8990", + "display": "Maternal care for other specified fetal problems, unspecified trimester, not applicable or unspecified" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O36.8991", + "display": "Maternal care for other specified fetal problems, unspecified trimester, fetus 1" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O36.8992", + "display": "Maternal care for other specified fetal problems, unspecified trimester, fetus 2" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O36.8993", + "display": "Maternal care for other specified fetal problems, unspecified trimester, fetus 3" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O36.8994", + "display": "Maternal care for other specified fetal problems, unspecified trimester, fetus 4" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O36.8995", + "display": "Maternal care for other specified fetal problems, unspecified trimester, fetus 5" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O36.8999", + "display": "Maternal care for other specified fetal problems, unspecified trimester, other fetus" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O36.90X0", + "display": "Maternal care for fetal problem, unspecified, unspecified trimester, not applicable or unspecified" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O36.90X1", + "display": "Maternal care for fetal problem, unspecified, unspecified trimester, fetus 1" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O36.90X2", + "display": "Maternal care for fetal problem, unspecified, unspecified trimester, fetus 2" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O36.90X3", + "display": "Maternal care for fetal problem, unspecified, unspecified trimester, fetus 3" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O36.90X4", + "display": "Maternal care for fetal problem, unspecified, unspecified trimester, fetus 4" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O36.90X5", + "display": "Maternal care for fetal problem, unspecified, unspecified trimester, fetus 5" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O36.90X9", + "display": "Maternal care for fetal problem, unspecified, unspecified trimester, other fetus" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O36.91X0", + "display": "Maternal care for fetal problem, unspecified, first trimester, not applicable or unspecified" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O36.91X1", + "display": "Maternal care for fetal problem, unspecified, first trimester, fetus 1" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O36.91X2", + "display": "Maternal care for fetal problem, unspecified, first trimester, fetus 2" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O36.91X3", + "display": "Maternal care for fetal problem, unspecified, first trimester, fetus 3" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O36.91X4", + "display": "Maternal care for fetal problem, unspecified, first trimester, fetus 4" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O36.91X5", + "display": "Maternal care for fetal problem, unspecified, first trimester, fetus 5" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O36.91X9", + "display": "Maternal care for fetal problem, unspecified, first trimester, other fetus" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O36.92X0", + "display": "Maternal care for fetal problem, unspecified, second trimester, not applicable or unspecified" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O36.92X1", + "display": "Maternal care for fetal problem, unspecified, second trimester, fetus 1" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O36.92X2", + "display": "Maternal care for fetal problem, unspecified, second trimester, fetus 2" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O36.92X3", + "display": "Maternal care for fetal problem, unspecified, second trimester, fetus 3" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O36.92X4", + "display": "Maternal care for fetal problem, unspecified, second trimester, fetus 4" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O36.92X5", + "display": "Maternal care for fetal problem, unspecified, second trimester, fetus 5" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O36.92X9", + "display": "Maternal care for fetal problem, unspecified, second trimester, other fetus" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O36.93X0", + "display": "Maternal care for fetal problem, unspecified, third trimester, not applicable or unspecified" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O36.93X1", + "display": "Maternal care for fetal problem, unspecified, third trimester, fetus 1" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O36.93X2", + "display": "Maternal care for fetal problem, unspecified, third trimester, fetus 2" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O36.93X3", + "display": "Maternal care for fetal problem, unspecified, third trimester, fetus 3" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O36.93X4", + "display": "Maternal care for fetal problem, unspecified, third trimester, fetus 4" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O36.93X5", + "display": "Maternal care for fetal problem, unspecified, third trimester, fetus 5" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O36.93X9", + "display": "Maternal care for fetal problem, unspecified, third trimester, other fetus" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O40.1XX0", + "display": "Polyhydramnios, first trimester, not applicable or unspecified" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O40.1XX1", + "display": "Polyhydramnios, first trimester, fetus 1" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O40.1XX2", + "display": "Polyhydramnios, first trimester, fetus 2" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O40.1XX3", + "display": "Polyhydramnios, first trimester, fetus 3" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O40.1XX4", + "display": "Polyhydramnios, first trimester, fetus 4" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O40.1XX5", + "display": "Polyhydramnios, first trimester, fetus 5" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O40.1XX9", + "display": "Polyhydramnios, first trimester, other fetus" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O40.2XX0", + "display": "Polyhydramnios, second trimester, not applicable or unspecified" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O40.2XX1", + "display": "Polyhydramnios, second trimester, fetus 1" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O40.2XX2", + "display": "Polyhydramnios, second trimester, fetus 2" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O40.2XX3", + "display": "Polyhydramnios, second trimester, fetus 3" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O40.2XX4", + "display": "Polyhydramnios, second trimester, fetus 4" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O40.2XX5", + "display": "Polyhydramnios, second trimester, fetus 5" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O40.2XX9", + "display": "Polyhydramnios, second trimester, other fetus" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O40.3XX0", + "display": "Polyhydramnios, third trimester, not applicable or unspecified" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O40.3XX1", + "display": "Polyhydramnios, third trimester, fetus 1" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O40.3XX2", + "display": "Polyhydramnios, third trimester, fetus 2" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O40.3XX3", + "display": "Polyhydramnios, third trimester, fetus 3" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O40.3XX4", + "display": "Polyhydramnios, third trimester, fetus 4" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O40.3XX5", + "display": "Polyhydramnios, third trimester, fetus 5" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O40.3XX9", + "display": "Polyhydramnios, third trimester, other fetus" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O40.9XX0", + "display": "Polyhydramnios, unspecified trimester, not applicable or unspecified" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O40.9XX1", + "display": "Polyhydramnios, unspecified trimester, fetus 1" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O40.9XX2", + "display": "Polyhydramnios, unspecified trimester, fetus 2" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O40.9XX3", + "display": "Polyhydramnios, unspecified trimester, fetus 3" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O40.9XX4", + "display": "Polyhydramnios, unspecified trimester, fetus 4" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O40.9XX5", + "display": "Polyhydramnios, unspecified trimester, fetus 5" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O40.9XX9", + "display": "Polyhydramnios, unspecified trimester, other fetus" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O41.00X0", + "display": "Oligohydramnios, unspecified trimester, not applicable or unspecified" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O41.00X1", + "display": "Oligohydramnios, unspecified trimester, fetus 1" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O41.00X2", + "display": "Oligohydramnios, unspecified trimester, fetus 2" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O41.00X3", + "display": "Oligohydramnios, unspecified trimester, fetus 3" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O41.00X4", + "display": "Oligohydramnios, unspecified trimester, fetus 4" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O41.00X5", + "display": "Oligohydramnios, unspecified trimester, fetus 5" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O41.00X9", + "display": "Oligohydramnios, unspecified trimester, other fetus" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O41.01X0", + "display": "Oligohydramnios, first trimester, not applicable or unspecified" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O41.01X1", + "display": "Oligohydramnios, first trimester, fetus 1" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O41.01X2", + "display": "Oligohydramnios, first trimester, fetus 2" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O41.01X3", + "display": "Oligohydramnios, first trimester, fetus 3" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O41.01X4", + "display": "Oligohydramnios, first trimester, fetus 4" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O41.01X5", + "display": "Oligohydramnios, first trimester, fetus 5" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O41.01X9", + "display": "Oligohydramnios, first trimester, other fetus" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O41.02X0", + "display": "Oligohydramnios, second trimester, not applicable or unspecified" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O41.02X1", + "display": "Oligohydramnios, second trimester, fetus 1" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O41.02X2", + "display": "Oligohydramnios, second trimester, fetus 2" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O41.02X3", + "display": "Oligohydramnios, second trimester, fetus 3" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O41.02X4", + "display": "Oligohydramnios, second trimester, fetus 4" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O41.02X5", + "display": "Oligohydramnios, second trimester, fetus 5" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O41.02X9", + "display": "Oligohydramnios, second trimester, other fetus" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O41.03X0", + "display": "Oligohydramnios, third trimester, not applicable or unspecified" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O41.03X1", + "display": "Oligohydramnios, third trimester, fetus 1" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O41.03X2", + "display": "Oligohydramnios, third trimester, fetus 2" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O41.03X3", + "display": "Oligohydramnios, third trimester, fetus 3" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O41.03X4", + "display": "Oligohydramnios, third trimester, fetus 4" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O41.03X5", + "display": "Oligohydramnios, third trimester, fetus 5" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O41.03X9", + "display": "Oligohydramnios, third trimester, other fetus" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O41.1010", + "display": "Infection of amniotic sac and membranes, unspecified, first trimester, not applicable or unspecified" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O41.1011", + "display": "Infection of amniotic sac and membranes, unspecified, first trimester, fetus 1" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O41.1012", + "display": "Infection of amniotic sac and membranes, unspecified, first trimester, fetus 2" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O41.1013", + "display": "Infection of amniotic sac and membranes, unspecified, first trimester, fetus 3" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O41.1014", + "display": "Infection of amniotic sac and membranes, unspecified, first trimester, fetus 4" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O41.1015", + "display": "Infection of amniotic sac and membranes, unspecified, first trimester, fetus 5" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O41.1019", + "display": "Infection of amniotic sac and membranes, unspecified, first trimester, other fetus" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O41.1020", + "display": "Infection of amniotic sac and membranes, unspecified, second trimester, not applicable or unspecified" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O41.1021", + "display": "Infection of amniotic sac and membranes, unspecified, second trimester, fetus 1" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O41.1022", + "display": "Infection of amniotic sac and membranes, unspecified, second trimester, fetus 2" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O41.1023", + "display": "Infection of amniotic sac and membranes, unspecified, second trimester, fetus 3" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O41.1024", + "display": "Infection of amniotic sac and membranes, unspecified, second trimester, fetus 4" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O41.1025", + "display": "Infection of amniotic sac and membranes, unspecified, second trimester, fetus 5" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O41.1029", + "display": "Infection of amniotic sac and membranes, unspecified, second trimester, other fetus" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O41.1030", + "display": "Infection of amniotic sac and membranes, unspecified, third trimester, not applicable or unspecified" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O41.1031", + "display": "Infection of amniotic sac and membranes, unspecified, third trimester, fetus 1" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O41.1032", + "display": "Infection of amniotic sac and membranes, unspecified, third trimester, fetus 2" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O41.1033", + "display": "Infection of amniotic sac and membranes, unspecified, third trimester, fetus 3" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O41.1034", + "display": "Infection of amniotic sac and membranes, unspecified, third trimester, fetus 4" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O41.1035", + "display": "Infection of amniotic sac and membranes, unspecified, third trimester, fetus 5" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O41.1039", + "display": "Infection of amniotic sac and membranes, unspecified, third trimester, other fetus" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O41.1090", + "display": "Infection of amniotic sac and membranes, unspecified, unspecified trimester, not applicable or unspecified" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O41.1091", + "display": "Infection of amniotic sac and membranes, unspecified, unspecified trimester, fetus 1" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O41.1092", + "display": "Infection of amniotic sac and membranes, unspecified, unspecified trimester, fetus 2" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O41.1093", + "display": "Infection of amniotic sac and membranes, unspecified, unspecified trimester, fetus 3" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O41.1094", + "display": "Infection of amniotic sac and membranes, unspecified, unspecified trimester, fetus 4" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O41.1095", + "display": "Infection of amniotic sac and membranes, unspecified, unspecified trimester, fetus 5" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O41.1099", + "display": "Infection of amniotic sac and membranes, unspecified, unspecified trimester, other fetus" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O41.1210", + "display": "Chorioamnionitis, first trimester, not applicable or unspecified" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O41.1211", + "display": "Chorioamnionitis, first trimester, fetus 1" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O41.1212", + "display": "Chorioamnionitis, first trimester, fetus 2" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O41.1213", + "display": "Chorioamnionitis, first trimester, fetus 3" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O41.1214", + "display": "Chorioamnionitis, first trimester, fetus 4" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O41.1215", + "display": "Chorioamnionitis, first trimester, fetus 5" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O41.1219", + "display": "Chorioamnionitis, first trimester, other fetus" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O41.1220", + "display": "Chorioamnionitis, second trimester, not applicable or unspecified" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O41.1221", + "display": "Chorioamnionitis, second trimester, fetus 1" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O41.1222", + "display": "Chorioamnionitis, second trimester, fetus 2" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O41.1223", + "display": "Chorioamnionitis, second trimester, fetus 3" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O41.1224", + "display": "Chorioamnionitis, second trimester, fetus 4" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O41.1225", + "display": "Chorioamnionitis, second trimester, fetus 5" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O41.1229", + "display": "Chorioamnionitis, second trimester, other fetus" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O41.1230", + "display": "Chorioamnionitis, third trimester, not applicable or unspecified" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O41.1231", + "display": "Chorioamnionitis, third trimester, fetus 1" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O41.1232", + "display": "Chorioamnionitis, third trimester, fetus 2" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O41.1233", + "display": "Chorioamnionitis, third trimester, fetus 3" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O41.1234", + "display": "Chorioamnionitis, third trimester, fetus 4" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O41.1235", + "display": "Chorioamnionitis, third trimester, fetus 5" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O41.1239", + "display": "Chorioamnionitis, third trimester, other fetus" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O41.1290", + "display": "Chorioamnionitis, unspecified trimester, not applicable or unspecified" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O41.1291", + "display": "Chorioamnionitis, unspecified trimester, fetus 1" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O41.1292", + "display": "Chorioamnionitis, unspecified trimester, fetus 2" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O41.1293", + "display": "Chorioamnionitis, unspecified trimester, fetus 3" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O41.1294", + "display": "Chorioamnionitis, unspecified trimester, fetus 4" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O41.1295", + "display": "Chorioamnionitis, unspecified trimester, fetus 5" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O41.1299", + "display": "Chorioamnionitis, unspecified trimester, other fetus" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O41.1410", + "display": "Placentitis, first trimester, not applicable or unspecified" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O41.1411", + "display": "Placentitis, first trimester, fetus 1" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O41.1412", + "display": "Placentitis, first trimester, fetus 2" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O41.1413", + "display": "Placentitis, first trimester, fetus 3" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O41.1414", + "display": "Placentitis, first trimester, fetus 4" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O41.1415", + "display": "Placentitis, first trimester, fetus 5" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O41.1419", + "display": "Placentitis, first trimester, other fetus" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O41.1420", + "display": "Placentitis, second trimester, not applicable or unspecified" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O41.1421", + "display": "Placentitis, second trimester, fetus 1" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O41.1422", + "display": "Placentitis, second trimester, fetus 2" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O41.1423", + "display": "Placentitis, second trimester, fetus 3" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O41.1424", + "display": "Placentitis, second trimester, fetus 4" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O41.1425", + "display": "Placentitis, second trimester, fetus 5" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O41.1429", + "display": "Placentitis, second trimester, other fetus" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O41.1430", + "display": "Placentitis, third trimester, not applicable or unspecified" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O41.1431", + "display": "Placentitis, third trimester, fetus 1" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O41.1432", + "display": "Placentitis, third trimester, fetus 2" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O41.1433", + "display": "Placentitis, third trimester, fetus 3" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O41.1434", + "display": "Placentitis, third trimester, fetus 4" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O41.1435", + "display": "Placentitis, third trimester, fetus 5" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O41.1439", + "display": "Placentitis, third trimester, other fetus" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O41.1490", + "display": "Placentitis, unspecified trimester, not applicable or unspecified" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O41.1491", + "display": "Placentitis, unspecified trimester, fetus 1" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O41.1492", + "display": "Placentitis, unspecified trimester, fetus 2" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O41.1493", + "display": "Placentitis, unspecified trimester, fetus 3" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O41.1494", + "display": "Placentitis, unspecified trimester, fetus 4" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O41.1495", + "display": "Placentitis, unspecified trimester, fetus 5" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O41.1499", + "display": "Placentitis, unspecified trimester, other fetus" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O41.8X10", + "display": "Other specified disorders of amniotic fluid and membranes, first trimester, not applicable or unspecified" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O41.8X11", + "display": "Other specified disorders of amniotic fluid and membranes, first trimester, fetus 1" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O41.8X12", + "display": "Other specified disorders of amniotic fluid and membranes, first trimester, fetus 2" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O41.8X13", + "display": "Other specified disorders of amniotic fluid and membranes, first trimester, fetus 3" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O41.8X14", + "display": "Other specified disorders of amniotic fluid and membranes, first trimester, fetus 4" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O41.8X15", + "display": "Other specified disorders of amniotic fluid and membranes, first trimester, fetus 5" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O41.8X19", + "display": "Other specified disorders of amniotic fluid and membranes, first trimester, other fetus" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O41.8X20", + "display": "Other specified disorders of amniotic fluid and membranes, second trimester, not applicable or unspecified" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O41.8X21", + "display": "Other specified disorders of amniotic fluid and membranes, second trimester, fetus 1" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O41.8X22", + "display": "Other specified disorders of amniotic fluid and membranes, second trimester, fetus 2" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O41.8X23", + "display": "Other specified disorders of amniotic fluid and membranes, second trimester, fetus 3" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O41.8X24", + "display": "Other specified disorders of amniotic fluid and membranes, second trimester, fetus 4" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O41.8X25", + "display": "Other specified disorders of amniotic fluid and membranes, second trimester, fetus 5" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O41.8X29", + "display": "Other specified disorders of amniotic fluid and membranes, second trimester, other fetus" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O41.8X30", + "display": "Other specified disorders of amniotic fluid and membranes, third trimester, not applicable or unspecified" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O41.8X31", + "display": "Other specified disorders of amniotic fluid and membranes, third trimester, fetus 1" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O41.8X32", + "display": "Other specified disorders of amniotic fluid and membranes, third trimester, fetus 2" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O41.8X33", + "display": "Other specified disorders of amniotic fluid and membranes, third trimester, fetus 3" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O41.8X34", + "display": "Other specified disorders of amniotic fluid and membranes, third trimester, fetus 4" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O41.8X35", + "display": "Other specified disorders of amniotic fluid and membranes, third trimester, fetus 5" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O41.8X39", + "display": "Other specified disorders of amniotic fluid and membranes, third trimester, other fetus" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O41.8X90", + "display": "Other specified disorders of amniotic fluid and membranes, unspecified trimester, not applicable or unspecified" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O41.8X91", + "display": "Other specified disorders of amniotic fluid and membranes, unspecified trimester, fetus 1" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O41.8X92", + "display": "Other specified disorders of amniotic fluid and membranes, unspecified trimester, fetus 2" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O41.8X93", + "display": "Other specified disorders of amniotic fluid and membranes, unspecified trimester, fetus 3" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O41.8X94", + "display": "Other specified disorders of amniotic fluid and membranes, unspecified trimester, fetus 4" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O41.8X95", + "display": "Other specified disorders of amniotic fluid and membranes, unspecified trimester, fetus 5" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O41.8X99", + "display": "Other specified disorders of amniotic fluid and membranes, unspecified trimester, other fetus" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O41.90X0", + "display": "Disorder of amniotic fluid and membranes, unspecified, unspecified trimester, not applicable or unspecified" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O41.90X1", + "display": "Disorder of amniotic fluid and membranes, unspecified, unspecified trimester, fetus 1" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O41.90X2", + "display": "Disorder of amniotic fluid and membranes, unspecified, unspecified trimester, fetus 2" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O41.90X3", + "display": "Disorder of amniotic fluid and membranes, unspecified, unspecified trimester, fetus 3" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O41.90X4", + "display": "Disorder of amniotic fluid and membranes, unspecified, unspecified trimester, fetus 4" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O41.90X5", + "display": "Disorder of amniotic fluid and membranes, unspecified, unspecified trimester, fetus 5" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O41.90X9", + "display": "Disorder of amniotic fluid and membranes, unspecified, unspecified trimester, other fetus" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O41.91X0", + "display": "Disorder of amniotic fluid and membranes, unspecified, first trimester, not applicable or unspecified" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O41.91X1", + "display": "Disorder of amniotic fluid and membranes, unspecified, first trimester, fetus 1" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O41.91X2", + "display": "Disorder of amniotic fluid and membranes, unspecified, first trimester, fetus 2" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O41.91X3", + "display": "Disorder of amniotic fluid and membranes, unspecified, first trimester, fetus 3" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O41.91X4", + "display": "Disorder of amniotic fluid and membranes, unspecified, first trimester, fetus 4" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O41.91X5", + "display": "Disorder of amniotic fluid and membranes, unspecified, first trimester, fetus 5" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O41.91X9", + "display": "Disorder of amniotic fluid and membranes, unspecified, first trimester, other fetus" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O41.92X0", + "display": "Disorder of amniotic fluid and membranes, unspecified, second trimester, not applicable or unspecified" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O41.92X1", + "display": "Disorder of amniotic fluid and membranes, unspecified, second trimester, fetus 1" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O41.92X2", + "display": "Disorder of amniotic fluid and membranes, unspecified, second trimester, fetus 2" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O41.92X3", + "display": "Disorder of amniotic fluid and membranes, unspecified, second trimester, fetus 3" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O41.92X4", + "display": "Disorder of amniotic fluid and membranes, unspecified, second trimester, fetus 4" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O41.92X5", + "display": "Disorder of amniotic fluid and membranes, unspecified, second trimester, fetus 5" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O41.92X9", + "display": "Disorder of amniotic fluid and membranes, unspecified, second trimester, other fetus" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O41.93X0", + "display": "Disorder of amniotic fluid and membranes, unspecified, third trimester, not applicable or unspecified" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O41.93X1", + "display": "Disorder of amniotic fluid and membranes, unspecified, third trimester, fetus 1" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O41.93X2", + "display": "Disorder of amniotic fluid and membranes, unspecified, third trimester, fetus 2" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O41.93X3", + "display": "Disorder of amniotic fluid and membranes, unspecified, third trimester, fetus 3" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O41.93X4", + "display": "Disorder of amniotic fluid and membranes, unspecified, third trimester, fetus 4" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O41.93X5", + "display": "Disorder of amniotic fluid and membranes, unspecified, third trimester, fetus 5" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O41.93X9", + "display": "Disorder of amniotic fluid and membranes, unspecified, third trimester, other fetus" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O42.00", + "display": "Premature rupture of membranes, onset of labor within 24 hours of rupture, unspecified weeks of gestation" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O42.011", + "display": "Preterm premature rupture of membranes, onset of labor within 24 hours of rupture, first trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O42.012", + "display": "Preterm premature rupture of membranes, onset of labor within 24 hours of rupture, second trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O42.013", + "display": "Preterm premature rupture of membranes, onset of labor within 24 hours of rupture, third trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O42.019", + "display": "Preterm premature rupture of membranes, onset of labor within 24 hours of rupture, unspecified trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O42.02", + "display": "Full-term premature rupture of membranes, onset of labor within 24 hours of rupture" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O42.10", + "display": "Premature rupture of membranes, onset of labor more than 24 hours following rupture, unspecified weeks of gestation" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O42.111", + "display": "Preterm premature rupture of membranes, onset of labor more than 24 hours following rupture, first trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O42.112", + "display": "Preterm premature rupture of membranes, onset of labor more than 24 hours following rupture, second trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O42.113", + "display": "Preterm premature rupture of membranes, onset of labor more than 24 hours following rupture, third trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O42.119", + "display": "Preterm premature rupture of membranes, onset of labor more than 24 hours following rupture, unspecified trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O42.12", + "display": "Full-term premature rupture of membranes, onset of labor more than 24 hours following rupture" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O42.90", + "display": "Premature rupture of membranes, unspecified as to length of time between rupture and onset of labor, unspecified weeks of gestation" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O42.911", + "display": "Preterm premature rupture of membranes, unspecified as to length of time between rupture and onset of labor, first trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O42.912", + "display": "Preterm premature rupture of membranes, unspecified as to length of time between rupture and onset of labor, second trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O42.913", + "display": "Preterm premature rupture of membranes, unspecified as to length of time between rupture and onset of labor, third trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O42.919", + "display": "Preterm premature rupture of membranes, unspecified as to length of time between rupture and onset of labor, unspecified trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O42.92", + "display": "Full-term premature rupture of membranes, unspecified as to length of time between rupture and onset of labor" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O43.011", + "display": "Fetomaternal placental transfusion syndrome, first trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O43.012", + "display": "Fetomaternal placental transfusion syndrome, second trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O43.013", + "display": "Fetomaternal placental transfusion syndrome, third trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O43.019", + "display": "Fetomaternal placental transfusion syndrome, unspecified trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O43.021", + "display": "Fetus-to-fetus placental transfusion syndrome, first trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O43.022", + "display": "Fetus-to-fetus placental transfusion syndrome, second trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O43.023", + "display": "Fetus-to-fetus placental transfusion syndrome, third trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O43.029", + "display": "Fetus-to-fetus placental transfusion syndrome, unspecified trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O43.101", + "display": "Malformation of placenta, unspecified, first trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O43.102", + "display": "Malformation of placenta, unspecified, second trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O43.103", + "display": "Malformation of placenta, unspecified, third trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O43.109", + "display": "Malformation of placenta, unspecified, unspecified trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O43.111", + "display": "Circumvallate placenta, first trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O43.112", + "display": "Circumvallate placenta, second trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O43.113", + "display": "Circumvallate placenta, third trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O43.119", + "display": "Circumvallate placenta, unspecified trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O43.121", + "display": "Velamentous insertion of umbilical cord, first trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O43.122", + "display": "Velamentous insertion of umbilical cord, second trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O43.123", + "display": "Velamentous insertion of umbilical cord, third trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O43.129", + "display": "Velamentous insertion of umbilical cord, unspecified trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O43.191", + "display": "Other malformation of placenta, first trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O43.192", + "display": "Other malformation of placenta, second trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O43.193", + "display": "Other malformation of placenta, third trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O43.199", + "display": "Other malformation of placenta, unspecified trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O43.211", + "display": "Placenta accreta, first trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O43.212", + "display": "Placenta accreta, second trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O43.213", + "display": "Placenta accreta, third trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O43.219", + "display": "Placenta accreta, unspecified trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O43.221", + "display": "Placenta increta, first trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O43.222", + "display": "Placenta increta, second trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O43.223", + "display": "Placenta increta, third trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O43.229", + "display": "Placenta increta, unspecified trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O43.231", + "display": "Placenta percreta, first trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O43.232", + "display": "Placenta percreta, second trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O43.233", + "display": "Placenta percreta, third trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O43.239", + "display": "Placenta percreta, unspecified trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O43.811", + "display": "Placental infarction, first trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O43.812", + "display": "Placental infarction, second trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O43.813", + "display": "Placental infarction, third trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O43.819", + "display": "Placental infarction, unspecified trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O43.891", + "display": "Other placental disorders, first trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O43.892", + "display": "Other placental disorders, second trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O43.893", + "display": "Other placental disorders, third trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O43.899", + "display": "Other placental disorders, unspecified trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O43.90", + "display": "Unspecified placental disorder, unspecified trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O43.91", + "display": "Unspecified placental disorder, first trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O43.92", + "display": "Unspecified placental disorder, second trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O43.93", + "display": "Unspecified placental disorder, third trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O44.00", + "display": "Complete placenta previa NOS or without hemorrhage, unspecified trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O44.01", + "display": "Complete placenta previa NOS or without hemorrhage, first trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O44.02", + "display": "Complete placenta previa NOS or without hemorrhage, second trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O44.03", + "display": "Complete placenta previa NOS or without hemorrhage, third trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O44.10", + "display": "Complete placenta previa with hemorrhage, unspecified trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O44.11", + "display": "Complete placenta previa with hemorrhage, first trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O44.12", + "display": "Complete placenta previa with hemorrhage, second trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O44.13", + "display": "Complete placenta previa with hemorrhage, third trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O44.20", + "display": "Partial placenta previa NOS or without hemorrhage, unspecified trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O44.21", + "display": "Partial placenta previa NOS or without hemorrhage, first trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O44.22", + "display": "Partial placenta previa NOS or without hemorrhage, second trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O44.23", + "display": "Partial placenta previa NOS or without hemorrhage, third trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O44.30", + "display": "Partial placenta previa with hemorrhage, unspecified trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O44.31", + "display": "Partial placenta previa with hemorrhage, first trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O44.32", + "display": "Partial placenta previa with hemorrhage, second trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O44.33", + "display": "Partial placenta previa with hemorrhage, third trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O44.40", + "display": "Low lying placenta NOS or without hemorrhage, unspecified trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O44.41", + "display": "Low lying placenta NOS or without hemorrhage, first trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O44.42", + "display": "Low lying placenta NOS or without hemorrhage, second trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O44.43", + "display": "Low lying placenta NOS or without hemorrhage, third trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O44.50", + "display": "Low lying placenta with hemorrhage, unspecified trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O44.51", + "display": "Low lying placenta with hemorrhage, first trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O44.52", + "display": "Low lying placenta with hemorrhage, second trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O44.53", + "display": "Low lying placenta with hemorrhage, third trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O45.001", + "display": "Premature separation of placenta with coagulation defect, unspecified, first trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O45.002", + "display": "Premature separation of placenta with coagulation defect, unspecified, second trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O45.003", + "display": "Premature separation of placenta with coagulation defect, unspecified, third trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O45.009", + "display": "Premature separation of placenta with coagulation defect, unspecified, unspecified trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O45.011", + "display": "Premature separation of placenta with afibrinogenemia, first trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O45.012", + "display": "Premature separation of placenta with afibrinogenemia, second trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O45.013", + "display": "Premature separation of placenta with afibrinogenemia, third trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O45.019", + "display": "Premature separation of placenta with afibrinogenemia, unspecified trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O45.021", + "display": "Premature separation of placenta with disseminated intravascular coagulation, first trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O45.022", + "display": "Premature separation of placenta with disseminated intravascular coagulation, second trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O45.023", + "display": "Premature separation of placenta with disseminated intravascular coagulation, third trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O45.029", + "display": "Premature separation of placenta with disseminated intravascular coagulation, unspecified trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O45.091", + "display": "Premature separation of placenta with other coagulation defect, first trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O45.092", + "display": "Premature separation of placenta with other coagulation defect, second trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O45.093", + "display": "Premature separation of placenta with other coagulation defect, third trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O45.099", + "display": "Premature separation of placenta with other coagulation defect, unspecified trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O45.8X1", + "display": "Other premature separation of placenta, first trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O45.8X2", + "display": "Other premature separation of placenta, second trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O45.8X3", + "display": "Other premature separation of placenta, third trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O45.8X9", + "display": "Other premature separation of placenta, unspecified trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O45.90", + "display": "Premature separation of placenta, unspecified, unspecified trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O45.91", + "display": "Premature separation of placenta, unspecified, first trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O45.92", + "display": "Premature separation of placenta, unspecified, second trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O45.93", + "display": "Premature separation of placenta, unspecified, third trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O46.001", + "display": "Antepartum hemorrhage with coagulation defect, unspecified, first trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O46.002", + "display": "Antepartum hemorrhage with coagulation defect, unspecified, second trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O46.003", + "display": "Antepartum hemorrhage with coagulation defect, unspecified, third trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O46.009", + "display": "Antepartum hemorrhage with coagulation defect, unspecified, unspecified trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O46.011", + "display": "Antepartum hemorrhage with afibrinogenemia, first trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O46.012", + "display": "Antepartum hemorrhage with afibrinogenemia, second trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O46.013", + "display": "Antepartum hemorrhage with afibrinogenemia, third trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O46.019", + "display": "Antepartum hemorrhage with afibrinogenemia, unspecified trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O46.021", + "display": "Antepartum hemorrhage with disseminated intravascular coagulation, first trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O46.022", + "display": "Antepartum hemorrhage with disseminated intravascular coagulation, second trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O46.023", + "display": "Antepartum hemorrhage with disseminated intravascular coagulation, third trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O46.029", + "display": "Antepartum hemorrhage with disseminated intravascular coagulation, unspecified trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O46.091", + "display": "Antepartum hemorrhage with other coagulation defect, first trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O46.092", + "display": "Antepartum hemorrhage with other coagulation defect, second trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O46.093", + "display": "Antepartum hemorrhage with other coagulation defect, third trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O46.099", + "display": "Antepartum hemorrhage with other coagulation defect, unspecified trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O46.8X1", + "display": "Other antepartum hemorrhage, first trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O46.8X2", + "display": "Other antepartum hemorrhage, second trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O46.8X3", + "display": "Other antepartum hemorrhage, third trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O46.8X9", + "display": "Other antepartum hemorrhage, unspecified trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O46.90", + "display": "Antepartum hemorrhage, unspecified, unspecified trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O46.91", + "display": "Antepartum hemorrhage, unspecified, first trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O46.92", + "display": "Antepartum hemorrhage, unspecified, second trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O46.93", + "display": "Antepartum hemorrhage, unspecified, third trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O47.00", + "display": "False labor before 37 completed weeks of gestation, unspecified trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O47.02", + "display": "False labor before 37 completed weeks of gestation, second trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O47.03", + "display": "False labor before 37 completed weeks of gestation, third trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O47.1", + "display": "False labor at or after 37 completed weeks of gestation" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O47.9", + "display": "False labor, unspecified" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O48.0", + "display": "Post-term pregnancy" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O48.1", + "display": "Prolonged pregnancy" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O60.00", + "display": "Preterm labor without delivery, unspecified trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O60.02", + "display": "Preterm labor without delivery, second trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O60.03", + "display": "Preterm labor without delivery, third trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O71.00", + "display": "Rupture of uterus before onset of labor, unspecified trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O71.02", + "display": "Rupture of uterus before onset of labor, second trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O71.03", + "display": "Rupture of uterus before onset of labor, third trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O88.011", + "display": "Air embolism in pregnancy, first trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O88.012", + "display": "Air embolism in pregnancy, second trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O88.013", + "display": "Air embolism in pregnancy, third trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O88.019", + "display": "Air embolism in pregnancy, unspecified trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O88.111", + "display": "Amniotic fluid embolism in pregnancy, first trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O88.112", + "display": "Amniotic fluid embolism in pregnancy, second trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O88.113", + "display": "Amniotic fluid embolism in pregnancy, third trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O88.119", + "display": "Amniotic fluid embolism in pregnancy, unspecified trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O88.211", + "display": "Thromboembolism in pregnancy, first trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O88.212", + "display": "Thromboembolism in pregnancy, second trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O88.213", + "display": "Thromboembolism in pregnancy, third trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O88.219", + "display": "Thromboembolism in pregnancy, unspecified trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O88.311", + "display": "Pyemic and septic embolism in pregnancy, first trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O88.312", + "display": "Pyemic and septic embolism in pregnancy, second trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O88.313", + "display": "Pyemic and septic embolism in pregnancy, third trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O88.319", + "display": "Pyemic and septic embolism in pregnancy, unspecified trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O88.811", + "display": "Other embolism in pregnancy, first trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O88.812", + "display": "Other embolism in pregnancy, second trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O88.813", + "display": "Other embolism in pregnancy, third trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O88.819", + "display": "Other embolism in pregnancy, unspecified trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O90.3", + "display": "Peripartum cardiomyopathy" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O91.011", + "display": "Infection of nipple associated with pregnancy, first trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O91.012", + "display": "Infection of nipple associated with pregnancy, second trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O91.013", + "display": "Infection of nipple associated with pregnancy, third trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O91.019", + "display": "Infection of nipple associated with pregnancy, unspecified trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O91.111", + "display": "Abscess of breast associated with pregnancy, first trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O91.112", + "display": "Abscess of breast associated with pregnancy, second trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O91.113", + "display": "Abscess of breast associated with pregnancy, third trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O91.119", + "display": "Abscess of breast associated with pregnancy, unspecified trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O91.211", + "display": "Nonpurulent mastitis associated with pregnancy, first trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O91.212", + "display": "Nonpurulent mastitis associated with pregnancy, second trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O91.213", + "display": "Nonpurulent mastitis associated with pregnancy, third trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O91.219", + "display": "Nonpurulent mastitis associated with pregnancy, unspecified trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O92.011", + "display": "Retracted nipple associated with pregnancy, first trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O92.012", + "display": "Retracted nipple associated with pregnancy, second trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O92.013", + "display": "Retracted nipple associated with pregnancy, third trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O92.019", + "display": "Retracted nipple associated with pregnancy, unspecified trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O92.111", + "display": "Cracked nipple associated with pregnancy, first trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O92.112", + "display": "Cracked nipple associated with pregnancy, second trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O92.113", + "display": "Cracked nipple associated with pregnancy, third trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O92.119", + "display": "Cracked nipple associated with pregnancy, unspecified trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O92.20", + "display": "Unspecified disorder of breast associated with pregnancy and the puerperium" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O92.29", + "display": "Other disorders of breast associated with pregnancy and the puerperium" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O98.011", + "display": "Tuberculosis complicating pregnancy, first trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O98.012", + "display": "Tuberculosis complicating pregnancy, second trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O98.013", + "display": "Tuberculosis complicating pregnancy, third trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O98.019", + "display": "Tuberculosis complicating pregnancy, unspecified trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O98.111", + "display": "Syphilis complicating pregnancy, first trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O98.112", + "display": "Syphilis complicating pregnancy, second trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O98.113", + "display": "Syphilis complicating pregnancy, third trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O98.119", + "display": "Syphilis complicating pregnancy, unspecified trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O98.211", + "display": "Gonorrhea complicating pregnancy, first trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O98.212", + "display": "Gonorrhea complicating pregnancy, second trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O98.213", + "display": "Gonorrhea complicating pregnancy, third trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O98.219", + "display": "Gonorrhea complicating pregnancy, unspecified trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O98.311", + "display": "Other infections with a predominantly sexual mode of transmission complicating pregnancy, first trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O98.312", + "display": "Other infections with a predominantly sexual mode of transmission complicating pregnancy, second trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O98.313", + "display": "Other infections with a predominantly sexual mode of transmission complicating pregnancy, third trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O98.319", + "display": "Other infections with a predominantly sexual mode of transmission complicating pregnancy, unspecified trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O98.411", + "display": "Viral hepatitis complicating pregnancy, first trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O98.412", + "display": "Viral hepatitis complicating pregnancy, second trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O98.413", + "display": "Viral hepatitis complicating pregnancy, third trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O98.419", + "display": "Viral hepatitis complicating pregnancy, unspecified trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O98.511", + "display": "Other viral diseases complicating pregnancy, first trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O98.512", + "display": "Other viral diseases complicating pregnancy, second trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O98.513", + "display": "Other viral diseases complicating pregnancy, third trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O98.519", + "display": "Other viral diseases complicating pregnancy, unspecified trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O98.611", + "display": "Protozoal diseases complicating pregnancy, first trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O98.612", + "display": "Protozoal diseases complicating pregnancy, second trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O98.613", + "display": "Protozoal diseases complicating pregnancy, third trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O98.619", + "display": "Protozoal diseases complicating pregnancy, unspecified trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O98.711", + "display": "Human immunodeficiency virus [HIV] disease complicating pregnancy, first trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O98.712", + "display": "Human immunodeficiency virus [HIV] disease complicating pregnancy, second trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O98.713", + "display": "Human immunodeficiency virus [HIV] disease complicating pregnancy, third trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O98.719", + "display": "Human immunodeficiency virus [HIV] disease complicating pregnancy, unspecified trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O98.811", + "display": "Other maternal infectious and parasitic diseases complicating pregnancy, first trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O98.812", + "display": "Other maternal infectious and parasitic diseases complicating pregnancy, second trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O98.813", + "display": "Other maternal infectious and parasitic diseases complicating pregnancy, third trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O98.819", + "display": "Other maternal infectious and parasitic diseases complicating pregnancy, unspecified trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O98.911", + "display": "Unspecified maternal infectious and parasitic disease complicating pregnancy, first trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O98.912", + "display": "Unspecified maternal infectious and parasitic disease complicating pregnancy, second trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O98.913", + "display": "Unspecified maternal infectious and parasitic disease complicating pregnancy, third trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O98.919", + "display": "Unspecified maternal infectious and parasitic disease complicating pregnancy, unspecified trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O99.011", + "display": "Anemia complicating pregnancy, first trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O99.012", + "display": "Anemia complicating pregnancy, second trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O99.013", + "display": "Anemia complicating pregnancy, third trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O99.019", + "display": "Anemia complicating pregnancy, unspecified trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O99.111", + "display": "Other diseases of the blood and blood-forming organs and certain disorders involving the immune mechanism complicating pregnancy, first trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O99.112", + "display": "Other diseases of the blood and blood-forming organs and certain disorders involving the immune mechanism complicating pregnancy, second trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O99.113", + "display": "Other diseases of the blood and blood-forming organs and certain disorders involving the immune mechanism complicating pregnancy, third trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O99.119", + "display": "Other diseases of the blood and blood-forming organs and certain disorders involving the immune mechanism complicating pregnancy, unspecified trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O99.210", + "display": "Obesity complicating pregnancy, unspecified trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O99.211", + "display": "Obesity complicating pregnancy, first trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O99.212", + "display": "Obesity complicating pregnancy, second trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O99.213", + "display": "Obesity complicating pregnancy, third trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O99.280", + "display": "Endocrine, nutritional and metabolic diseases complicating pregnancy, unspecified trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O99.281", + "display": "Endocrine, nutritional and metabolic diseases complicating pregnancy, first trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O99.282", + "display": "Endocrine, nutritional and metabolic diseases complicating pregnancy, second trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O99.283", + "display": "Endocrine, nutritional and metabolic diseases complicating pregnancy, third trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O99.310", + "display": "Alcohol use complicating pregnancy, unspecified trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O99.311", + "display": "Alcohol use complicating pregnancy, first trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O99.312", + "display": "Alcohol use complicating pregnancy, second trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O99.313", + "display": "Alcohol use complicating pregnancy, third trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O99.320", + "display": "Drug use complicating pregnancy, unspecified trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O99.321", + "display": "Drug use complicating pregnancy, first trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O99.322", + "display": "Drug use complicating pregnancy, second trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O99.323", + "display": "Drug use complicating pregnancy, third trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O99.330", + "display": "Smoking (tobacco) complicating pregnancy, unspecified trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O99.331", + "display": "Smoking (tobacco) complicating pregnancy, first trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O99.332", + "display": "Smoking (tobacco) complicating pregnancy, second trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O99.333", + "display": "Smoking (tobacco) complicating pregnancy, third trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O99.340", + "display": "Other mental disorders complicating pregnancy, unspecified trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O99.341", + "display": "Other mental disorders complicating pregnancy, first trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O99.342", + "display": "Other mental disorders complicating pregnancy, second trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O99.343", + "display": "Other mental disorders complicating pregnancy, third trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O99.350", + "display": "Diseases of the nervous system complicating pregnancy, unspecified trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O99.351", + "display": "Diseases of the nervous system complicating pregnancy, first trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O99.352", + "display": "Diseases of the nervous system complicating pregnancy, second trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O99.353", + "display": "Diseases of the nervous system complicating pregnancy, third trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O99.411", + "display": "Diseases of the circulatory system complicating pregnancy, first trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O99.412", + "display": "Diseases of the circulatory system complicating pregnancy, second trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O99.413", + "display": "Diseases of the circulatory system complicating pregnancy, third trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O99.419", + "display": "Diseases of the circulatory system complicating pregnancy, unspecified trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O99.511", + "display": "Diseases of the respiratory system complicating pregnancy, first trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O99.512", + "display": "Diseases of the respiratory system complicating pregnancy, second trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O99.513", + "display": "Diseases of the respiratory system complicating pregnancy, third trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O99.519", + "display": "Diseases of the respiratory system complicating pregnancy, unspecified trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O99.611", + "display": "Diseases of the digestive system complicating pregnancy, first trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O99.612", + "display": "Diseases of the digestive system complicating pregnancy, second trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O99.613", + "display": "Diseases of the digestive system complicating pregnancy, third trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O99.619", + "display": "Diseases of the digestive system complicating pregnancy, unspecified trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O99.711", + "display": "Diseases of the skin and subcutaneous tissue complicating pregnancy, first trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O99.712", + "display": "Diseases of the skin and subcutaneous tissue complicating pregnancy, second trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O99.713", + "display": "Diseases of the skin and subcutaneous tissue complicating pregnancy, third trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O99.719", + "display": "Diseases of the skin and subcutaneous tissue complicating pregnancy, unspecified trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O99.810", + "display": "Abnormal glucose complicating pregnancy" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O99.820", + "display": "Streptococcus B carrier state complicating pregnancy" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O99.830", + "display": "Other infection carrier state complicating pregnancy" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O99.840", + "display": "Bariatric surgery status complicating pregnancy, unspecified trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O99.841", + "display": "Bariatric surgery status complicating pregnancy, first trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O99.842", + "display": "Bariatric surgery status complicating pregnancy, second trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O99.843", + "display": "Bariatric surgery status complicating pregnancy, third trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O9A.111", + "display": "Malignant neoplasm complicating pregnancy, first trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O9A.112", + "display": "Malignant neoplasm complicating pregnancy, second trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O9A.113", + "display": "Malignant neoplasm complicating pregnancy, third trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O9A.119", + "display": "Malignant neoplasm complicating pregnancy, unspecified trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O9A.211", + "display": "Injury, poisoning and certain other consequences of external causes complicating pregnancy, first trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O9A.212", + "display": "Injury, poisoning and certain other consequences of external causes complicating pregnancy, second trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O9A.213", + "display": "Injury, poisoning and certain other consequences of external causes complicating pregnancy, third trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O9A.219", + "display": "Injury, poisoning and certain other consequences of external causes complicating pregnancy, unspecified trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O9A.311", + "display": "Physical abuse complicating pregnancy, first trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O9A.312", + "display": "Physical abuse complicating pregnancy, second trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O9A.313", + "display": "Physical abuse complicating pregnancy, third trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O9A.319", + "display": "Physical abuse complicating pregnancy, unspecified trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O9A.411", + "display": "Sexual abuse complicating pregnancy, first trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O9A.412", + "display": "Sexual abuse complicating pregnancy, second trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O9A.413", + "display": "Sexual abuse complicating pregnancy, third trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O9A.419", + "display": "Sexual abuse complicating pregnancy, unspecified trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O9A.511", + "display": "Psychological abuse complicating pregnancy, first trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O9A.512", + "display": "Psychological abuse complicating pregnancy, second trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O9A.513", + "display": "Psychological abuse complicating pregnancy, third trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O9A.519", + "display": "Psychological abuse complicating pregnancy, unspecified trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "Z33.1", + "display": "Pregnant state, incidental" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "Z33.3", + "display": "Pregnant state, gestational carrier" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "Z34.00", + "display": "Encounter for supervision of normal first pregnancy, unspecified trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "Z34.01", + "display": "Encounter for supervision of normal first pregnancy, first trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "Z34.02", + "display": "Encounter for supervision of normal first pregnancy, second trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "Z34.03", + "display": "Encounter for supervision of normal first pregnancy, third trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "Z34.80", + "display": "Encounter for supervision of other normal pregnancy, unspecified trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "Z34.81", + "display": "Encounter for supervision of other normal pregnancy, first trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "Z34.82", + "display": "Encounter for supervision of other normal pregnancy, second trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "Z34.83", + "display": "Encounter for supervision of other normal pregnancy, third trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "Z34.90", + "display": "Encounter for supervision of normal pregnancy, unspecified, unspecified trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "Z34.91", + "display": "Encounter for supervision of normal pregnancy, unspecified, first trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "Z34.92", + "display": "Encounter for supervision of normal pregnancy, unspecified, second trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "Z34.93", + "display": "Encounter for supervision of normal pregnancy, unspecified, third trimester" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "Z3A.01", + "display": "Less than 8 weeks gestation of pregnancy" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "Z3A.08", + "display": "8 weeks gestation of pregnancy" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "Z3A.09", + "display": "9 weeks gestation of pregnancy" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "Z3A.10", + "display": "10 weeks gestation of pregnancy" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "Z3A.11", + "display": "11 weeks gestation of pregnancy" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "Z3A.12", + "display": "12 weeks gestation of pregnancy" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "Z3A.13", + "display": "13 weeks gestation of pregnancy" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "Z3A.14", + "display": "14 weeks gestation of pregnancy" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "Z3A.15", + "display": "15 weeks gestation of pregnancy" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "Z3A.16", + "display": "16 weeks gestation of pregnancy" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "Z3A.17", + "display": "17 weeks gestation of pregnancy" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "Z3A.18", + "display": "18 weeks gestation of pregnancy" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "Z3A.19", + "display": "19 weeks gestation of pregnancy" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "Z3A.20", + "display": "20 weeks gestation of pregnancy" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "Z3A.21", + "display": "21 weeks gestation of pregnancy" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "Z3A.22", + "display": "22 weeks gestation of pregnancy" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "Z3A.23", + "display": "23 weeks gestation of pregnancy" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "Z3A.24", + "display": "24 weeks gestation of pregnancy" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "Z3A.25", + "display": "25 weeks gestation of pregnancy" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "Z3A.26", + "display": "26 weeks gestation of pregnancy" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "Z3A.27", + "display": "27 weeks gestation of pregnancy" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "Z3A.28", + "display": "28 weeks gestation of pregnancy" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "Z3A.29", + "display": "29 weeks gestation of pregnancy" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "Z3A.30", + "display": "30 weeks gestation of pregnancy" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "Z3A.31", + "display": "31 weeks gestation of pregnancy" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "Z3A.32", + "display": "32 weeks gestation of pregnancy" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "Z3A.33", + "display": "33 weeks gestation of pregnancy" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "Z3A.34", + "display": "34 weeks gestation of pregnancy" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "Z3A.35", + "display": "35 weeks gestation of pregnancy" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "Z3A.36", + "display": "36 weeks gestation of pregnancy" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "Z3A.37", + "display": "37 weeks gestation of pregnancy" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "Z3A.38", + "display": "38 weeks gestation of pregnancy" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "Z3A.39", + "display": "39 weeks gestation of pregnancy" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "Z3A.40", + "display": "40 weeks gestation of pregnancy" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "Z3A.41", + "display": "41 weeks gestation of pregnancy" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "Z3A.42", + "display": "42 weeks gestation of pregnancy" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "Z3A.49", + "display": "Greater than 42 weeks gestation of pregnancy" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O0000", + "display": "Abdominal pregnancy without intrauterine pregnancy" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O0001", + "display": "Abdominal pregnancy with intrauterine pregnancy" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O00101", + "display": "Right tubal pregnancy without intrauterine pregnancy" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O00102", + "display": "Left tubal pregnancy without intrauterine pregnancy" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O00109", + "display": "Unspecified tubal pregnancy without intrauterine pregnancy" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O00111", + "display": "Right tubal pregnancy with intrauterine pregnancy" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O00112", + "display": "Left tubal pregnancy with intrauterine pregnancy" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O00119", + "display": "Unspecified tubal pregnancy with intrauterine pregnancy" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O00201", + "display": "Right ovarian pregnancy without intrauterine pregnancy" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O00202", + "display": "Left ovarian pregnancy without intrauterine pregnancy" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O00209", + "display": "Unspecified ovarian pregnancy without intrauterine pregnancy" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O00211", + "display": "Right ovarian pregnancy with intrauterine pregnancy" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O00212", + "display": "Left ovarian pregnancy with intrauterine pregnancy" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O00219", + "display": "Unspecified ovarian pregnancy with intrauterine pregnancy" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O0080", + "display": "Other ectopic pregnancy without intrauterine pregnancy" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O0081", + "display": "Other ectopic pregnancy with intrauterine pregnancy" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O0090", + "display": "Unspecified ectopic pregnancy without intrauterine pregnancy" + }, + { + "system": "http://hl7.org/fhir/sid/icd-10-cm", + "version": "2023", + "code": "O0091", + "display": "Unspecified ectopic pregnancy with intrauterine pregnancy" + } + ] + } + }, + "request": { + "method": "PUT", + "url": "ValueSet/2.16.840.1.113883.3.526.3.378" + } + }, + { + "resource": { + "resourceType": "Library", + "id": "DQMFHIRHelpers", + "extension": [ + { + "url": "http://hl7.org/fhir/us/cqfmeasures/StructureDefinition/cqfm-softwaresystem", + "valueReference": { + "reference": "Device/cqf-tooling" + } + } + ], + "url": "http://content.alphora.com/fhir/dqm/Library/DQMFHIRHelpers", + "version": "4.0.1", + "name": "DQMFHIRHelpers", + "relatedArtifact": [ + { + "type": "depends-on", + "display": "FHIR model information", + "resource": "http://fhir.org/guides/cqf/common/Library/FHIR-ModelInfo|4.0.1" + } + ], + "content": [ + { + "contentType": "text/cql", + "data": "bGlicmFyeSBEUU1GSElSSGVscGVycyB2ZXJzaW9uICc0LjAuMScKCnVzaW5nIEZISVIgdmVyc2lvbiAnNC4wLjEnCgpkZWZpbmUgZnVuY3Rpb24gVG9JbnRlcnZhbChwZXJpb2QgRkhJUi5QZXJpb2QpOgogICAgaWYgcGVyaW9kIGlzIG51bGwgdGhlbgogICAgICAgIG51bGwKICAgIGVsc2UKICAgICAgICBJbnRlcnZhbFtwZXJpb2QuInN0YXJ0Ii52YWx1ZSwgcGVyaW9kLiJlbmQiLnZhbHVlXQoKZGVmaW5lIGZ1bmN0aW9uIFRvUXVhbnRpdHkocXVhbnRpdHkgRkhJUi5RdWFudGl0eSk6CiAgICBpZiBxdWFudGl0eSBpcyBudWxsIHRoZW4KICAgICAgICBudWxsCiAgICBlbHNlCiAgICAgICAgU3lzdGVtLlF1YW50aXR5IHsgdmFsdWU6IHF1YW50aXR5LnZhbHVlLnZhbHVlLCB1bml0OiBxdWFudGl0eS51bml0LnZhbHVlIH0KCmRlZmluZSBmdW5jdGlvbiBUb1JhdGlvKHJhdGlvIEZISVIuUmF0aW8pOgogICAgaWYgcmF0aW8gaXMgbnVsbCB0aGVuCiAgICAgICAgbnVsbAogICAgZWxzZQogICAgICAgIFN5c3RlbS5SYXRpbyB7IG51bWVyYXRvcjogVG9RdWFudGl0eShyYXRpby5udW1lcmF0b3IpLCBkZW5vbWluYXRvcjogVG9RdWFudGl0eShyYXRpby5kZW5vbWluYXRvcikgfQoKZGVmaW5lIGZ1bmN0aW9uIFRvSW50ZXJ2YWwocmFuZ2UgRkhJUi5SYW5nZSk6CiAgICBpZiByYW5nZSBpcyBudWxsIHRoZW4KICAgICAgICBudWxsCiAgICBlbHNlCiAgICAgICAgSW50ZXJ2YWxbVG9RdWFudGl0eShyYW5nZS5sb3cpLCBUb1F1YW50aXR5KHJhbmdlLmhpZ2gpXQoKZGVmaW5lIGZ1bmN0aW9uIFRvQ29kZShjb2RpbmcgRkhJUi5Db2RpbmcpOgogICAgaWYgY29kaW5nIGlzIG51bGwgdGhlbgogICAgICAgIG51bGwKICAgIGVsc2UKICAgICAgICBTeXN0ZW0uQ29kZSB7CiAgICAgICAgICBjb2RlOiBjb2RpbmcuY29kZS52YWx1ZSwKICAgICAgICAgIHN5c3RlbTogY29kaW5nLnN5c3RlbS52YWx1ZSwKICAgICAgICAgIHZlcnNpb246IGNvZGluZy52ZXJzaW9uLnZhbHVlLAogICAgICAgICAgZGlzcGxheTogY29kaW5nLmRpc3BsYXkudmFsdWUKICAgICAgICB9CgpkZWZpbmUgZnVuY3Rpb24gVG9Db25jZXB0KGNvbmNlcHQgRkhJUi5Db2RlYWJsZUNvbmNlcHQpOgogICAgaWYgY29uY2VwdCBpcyBudWxsIHRoZW4KICAgICAgICBudWxsCiAgICBlbHNlCiAgICAgICAgU3lzdGVtLkNvbmNlcHQgewogICAgICAgICAgICBjb2RlczogY29uY2VwdC5jb2RpbmcgQyByZXR1cm4gVG9Db2RlKEMpLAogICAgICAgICAgICBkaXNwbGF5OiBjb25jZXB0LnRleHQudmFsdWUKICAgICAgICB9CgoKZGVmaW5lIGZ1bmN0aW9uIFRvU3RyaW5nKHZhbHVlIEFjY291bnRTdGF0dXMpOiB2YWx1ZS52YWx1ZQpkZWZpbmUgZnVuY3Rpb24gVG9TdHJpbmcodmFsdWUgQWN0aW9uQ2FyZGluYWxpdHlCZWhhdmlvcik6IHZhbHVlLnZhbHVlCmRlZmluZSBmdW5jdGlvbiBUb1N0cmluZyh2YWx1ZSBBY3Rpb25Db25kaXRpb25LaW5kKTogdmFsdWUudmFsdWUKZGVmaW5lIGZ1bmN0aW9uIFRvU3RyaW5nKHZhbHVlIEFjdGlvbkdyb3VwaW5nQmVoYXZpb3IpOiB2YWx1ZS52YWx1ZQpkZWZpbmUgZnVuY3Rpb24gVG9TdHJpbmcodmFsdWUgQWN0aW9uUGFydGljaXBhbnRUeXBlKTogdmFsdWUudmFsdWUKZGVmaW5lIGZ1bmN0aW9uIFRvU3RyaW5nKHZhbHVlIEFjdGlvblByZWNoZWNrQmVoYXZpb3IpOiB2YWx1ZS52YWx1ZQpkZWZpbmUgZnVuY3Rpb24gVG9TdHJpbmcodmFsdWUgQWN0aW9uUmVsYXRpb25zaGlwVHlwZSk6IHZhbHVlLnZhbHVlCmRlZmluZSBmdW5jdGlvbiBUb1N0cmluZyh2YWx1ZSBBY3Rpb25SZXF1aXJlZEJlaGF2aW9yKTogdmFsdWUudmFsdWUKZGVmaW5lIGZ1bmN0aW9uIFRvU3RyaW5nKHZhbHVlIEFjdGlvblNlbGVjdGlvbkJlaGF2aW9yKTogdmFsdWUudmFsdWUKZGVmaW5lIGZ1bmN0aW9uIFRvU3RyaW5nKHZhbHVlIEFjdGl2aXR5RGVmaW5pdGlvbktpbmQpOiB2YWx1ZS52YWx1ZQpkZWZpbmUgZnVuY3Rpb24gVG9TdHJpbmcodmFsdWUgQWN0aXZpdHlQYXJ0aWNpcGFudFR5cGUpOiB2YWx1ZS52YWx1ZQpkZWZpbmUgZnVuY3Rpb24gVG9TdHJpbmcodmFsdWUgQWRkcmVzc1R5cGUpOiB2YWx1ZS52YWx1ZQpkZWZpbmUgZnVuY3Rpb24gVG9TdHJpbmcodmFsdWUgQWRkcmVzc1VzZSk6IHZhbHVlLnZhbHVlCmRlZmluZSBmdW5jdGlvbiBUb1N0cmluZyh2YWx1ZSBBZG1pbmlzdHJhdGl2ZUdlbmRlcik6IHZhbHVlLnZhbHVlCmRlZmluZSBmdW5jdGlvbiBUb1N0cmluZyh2YWx1ZSBBZHZlcnNlRXZlbnRBY3R1YWxpdHkpOiB2YWx1ZS52YWx1ZQpkZWZpbmUgZnVuY3Rpb24gVG9TdHJpbmcodmFsdWUgQWdncmVnYXRpb25Nb2RlKTogdmFsdWUudmFsdWUKZGVmaW5lIGZ1bmN0aW9uIFRvU3RyaW5nKHZhbHVlIEFsbGVyZ3lJbnRvbGVyYW5jZUNhdGVnb3J5KTogdmFsdWUudmFsdWUKZGVmaW5lIGZ1bmN0aW9uIFRvU3RyaW5nKHZhbHVlIEFsbGVyZ3lJbnRvbGVyYW5jZUNyaXRpY2FsaXR5KTogdmFsdWUudmFsdWUKZGVmaW5lIGZ1bmN0aW9uIFRvU3RyaW5nKHZhbHVlIEFsbGVyZ3lJbnRvbGVyYW5jZVNldmVyaXR5KTogdmFsdWUudmFsdWUKZGVmaW5lIGZ1bmN0aW9uIFRvU3RyaW5nKHZhbHVlIEFsbGVyZ3lJbnRvbGVyYW5jZVR5cGUpOiB2YWx1ZS52YWx1ZQpkZWZpbmUgZnVuY3Rpb24gVG9TdHJpbmcodmFsdWUgQXBwb2ludG1lbnRTdGF0dXMpOiB2YWx1ZS52YWx1ZQpkZWZpbmUgZnVuY3Rpb24gVG9TdHJpbmcodmFsdWUgQXNzZXJ0aW9uRGlyZWN0aW9uVHlwZSk6IHZhbHVlLnZhbHVlCmRlZmluZSBmdW5jdGlvbiBUb1N0cmluZyh2YWx1ZSBBc3NlcnRpb25PcGVyYXRvclR5cGUpOiB2YWx1ZS52YWx1ZQpkZWZpbmUgZnVuY3Rpb24gVG9TdHJpbmcodmFsdWUgQXNzZXJ0aW9uUmVzcG9uc2VUeXBlcyk6IHZhbHVlLnZhbHVlCmRlZmluZSBmdW5jdGlvbiBUb1N0cmluZyh2YWx1ZSBBdWRpdEV2ZW50QWN0aW9uKTogdmFsdWUudmFsdWUKZGVmaW5lIGZ1bmN0aW9uIFRvU3RyaW5nKHZhbHVlIEF1ZGl0RXZlbnRBZ2VudE5ldHdvcmtUeXBlKTogdmFsdWUudmFsdWUKZGVmaW5lIGZ1bmN0aW9uIFRvU3RyaW5nKHZhbHVlIEF1ZGl0RXZlbnRPdXRjb21lKTogdmFsdWUudmFsdWUKZGVmaW5lIGZ1bmN0aW9uIFRvU3RyaW5nKHZhbHVlIEJpbmRpbmdTdHJlbmd0aCk6IHZhbHVlLnZhbHVlCmRlZmluZSBmdW5jdGlvbiBUb1N0cmluZyh2YWx1ZSBCaW9sb2dpY2FsbHlEZXJpdmVkUHJvZHVjdENhdGVnb3J5KTogdmFsdWUudmFsdWUKZGVmaW5lIGZ1bmN0aW9uIFRvU3RyaW5nKHZhbHVlIEJpb2xvZ2ljYWxseURlcml2ZWRQcm9kdWN0U3RhdHVzKTogdmFsdWUudmFsdWUKZGVmaW5lIGZ1bmN0aW9uIFRvU3RyaW5nKHZhbHVlIEJpb2xvZ2ljYWxseURlcml2ZWRQcm9kdWN0U3RvcmFnZVNjYWxlKTogdmFsdWUudmFsdWUKZGVmaW5lIGZ1bmN0aW9uIFRvU3RyaW5nKHZhbHVlIEJ1bmRsZVR5cGUpOiB2YWx1ZS52YWx1ZQpkZWZpbmUgZnVuY3Rpb24gVG9TdHJpbmcodmFsdWUgQ2FwYWJpbGl0eVN0YXRlbWVudEtpbmQpOiB2YWx1ZS52YWx1ZQpkZWZpbmUgZnVuY3Rpb24gVG9TdHJpbmcodmFsdWUgQ2FyZVBsYW5BY3Rpdml0eUtpbmQpOiB2YWx1ZS52YWx1ZQpkZWZpbmUgZnVuY3Rpb24gVG9TdHJpbmcodmFsdWUgQ2FyZVBsYW5BY3Rpdml0eVN0YXR1cyk6IHZhbHVlLnZhbHVlCmRlZmluZSBmdW5jdGlvbiBUb1N0cmluZyh2YWx1ZSBDYXJlUGxhbkludGVudCk6IHZhbHVlLnZhbHVlCmRlZmluZSBmdW5jdGlvbiBUb1N0cmluZyh2YWx1ZSBDYXJlUGxhblN0YXR1cyk6IHZhbHVlLnZhbHVlCmRlZmluZSBmdW5jdGlvbiBUb1N0cmluZyh2YWx1ZSBDYXJlVGVhbVN0YXR1cyk6IHZhbHVlLnZhbHVlCmRlZmluZSBmdW5jdGlvbiBUb1N0cmluZyh2YWx1ZSBDYXRhbG9nRW50cnlSZWxhdGlvblR5cGUpOiB2YWx1ZS52YWx1ZQpkZWZpbmUgZnVuY3Rpb24gVG9TdHJpbmcodmFsdWUgQ2hhcmdlSXRlbURlZmluaXRpb25QcmljZUNvbXBvbmVudFR5cGUpOiB2YWx1ZS52YWx1ZQpkZWZpbmUgZnVuY3Rpb24gVG9TdHJpbmcodmFsdWUgQ2hhcmdlSXRlbVN0YXR1cyk6IHZhbHVlLnZhbHVlCmRlZmluZSBmdW5jdGlvbiBUb1N0cmluZyh2YWx1ZSBDbGFpbVJlc3BvbnNlU3RhdHVzKTogdmFsdWUudmFsdWUKZGVmaW5lIGZ1bmN0aW9uIFRvU3RyaW5nKHZhbHVlIENsYWltU3RhdHVzKTogdmFsdWUudmFsdWUKZGVmaW5lIGZ1bmN0aW9uIFRvU3RyaW5nKHZhbHVlIENsaW5pY2FsSW1wcmVzc2lvblN0YXR1cyk6IHZhbHVlLnZhbHVlCmRlZmluZSBmdW5jdGlvbiBUb1N0cmluZyh2YWx1ZSBDb2RlU2VhcmNoU3VwcG9ydCk6IHZhbHVlLnZhbHVlCmRlZmluZSBmdW5jdGlvbiBUb1N0cmluZyh2YWx1ZSBDb2RlU3lzdGVtQ29udGVudE1vZGUpOiB2YWx1ZS52YWx1ZQpkZWZpbmUgZnVuY3Rpb24gVG9TdHJpbmcodmFsdWUgQ29kZVN5c3RlbUhpZXJhcmNoeU1lYW5pbmcpOiB2YWx1ZS52YWx1ZQpkZWZpbmUgZnVuY3Rpb24gVG9TdHJpbmcodmFsdWUgQ29tbXVuaWNhdGlvblByaW9yaXR5KTogdmFsdWUudmFsdWUKZGVmaW5lIGZ1bmN0aW9uIFRvU3RyaW5nKHZhbHVlIENvbW11bmljYXRpb25SZXF1ZXN0U3RhdHVzKTogdmFsdWUudmFsdWUKZGVmaW5lIGZ1bmN0aW9uIFRvU3RyaW5nKHZhbHVlIENvbW11bmljYXRpb25TdGF0dXMpOiB2YWx1ZS52YWx1ZQpkZWZpbmUgZnVuY3Rpb24gVG9TdHJpbmcodmFsdWUgQ29tcGFydG1lbnRDb2RlKTogdmFsdWUudmFsdWUKZGVmaW5lIGZ1bmN0aW9uIFRvU3RyaW5nKHZhbHVlIENvbXBhcnRtZW50VHlwZSk6IHZhbHVlLnZhbHVlCmRlZmluZSBmdW5jdGlvbiBUb1N0cmluZyh2YWx1ZSBDb21wb3NpdGlvbkF0dGVzdGF0aW9uTW9kZSk6IHZhbHVlLnZhbHVlCmRlZmluZSBmdW5jdGlvbiBUb1N0cmluZyh2YWx1ZSBDb21wb3NpdGlvblN0YXR1cyk6IHZhbHVlLnZhbHVlCmRlZmluZSBmdW5jdGlvbiBUb1N0cmluZyh2YWx1ZSBDb25jZXB0TWFwRXF1aXZhbGVuY2UpOiB2YWx1ZS52YWx1ZQpkZWZpbmUgZnVuY3Rpb24gVG9TdHJpbmcodmFsdWUgQ29uY2VwdE1hcEdyb3VwVW5tYXBwZWRNb2RlKTogdmFsdWUudmFsdWUKZGVmaW5lIGZ1bmN0aW9uIFRvU3RyaW5nKHZhbHVlIENvbmRpdGlvbmFsRGVsZXRlU3RhdHVzKTogdmFsdWUudmFsdWUKZGVmaW5lIGZ1bmN0aW9uIFRvU3RyaW5nKHZhbHVlIENvbmRpdGlvbmFsUmVhZFN0YXR1cyk6IHZhbHVlLnZhbHVlCmRlZmluZSBmdW5jdGlvbiBUb1N0cmluZyh2YWx1ZSBDb25zZW50RGF0YU1lYW5pbmcpOiB2YWx1ZS52YWx1ZQpkZWZpbmUgZnVuY3Rpb24gVG9TdHJpbmcodmFsdWUgQ29uc2VudFByb3Zpc2lvblR5cGUpOiB2YWx1ZS52YWx1ZQpkZWZpbmUgZnVuY3Rpb24gVG9TdHJpbmcodmFsdWUgQ29uc2VudFN0YXRlKTogdmFsdWUudmFsdWUKZGVmaW5lIGZ1bmN0aW9uIFRvU3RyaW5nKHZhbHVlIENvbnN0cmFpbnRTZXZlcml0eSk6IHZhbHVlLnZhbHVlCmRlZmluZSBmdW5jdGlvbiBUb1N0cmluZyh2YWx1ZSBDb250YWN0UG9pbnRTeXN0ZW0pOiB2YWx1ZS52YWx1ZQpkZWZpbmUgZnVuY3Rpb24gVG9TdHJpbmcodmFsdWUgQ29udGFjdFBvaW50VXNlKTogdmFsdWUudmFsdWUKZGVmaW5lIGZ1bmN0aW9uIFRvU3RyaW5nKHZhbHVlIENvbnRyYWN0UHVibGljYXRpb25TdGF0dXMpOiB2YWx1ZS52YWx1ZQpkZWZpbmUgZnVuY3Rpb24gVG9TdHJpbmcodmFsdWUgQ29udHJhY3RTdGF0dXMpOiB2YWx1ZS52YWx1ZQpkZWZpbmUgZnVuY3Rpb24gVG9TdHJpbmcodmFsdWUgQ29udHJpYnV0b3JUeXBlKTogdmFsdWUudmFsdWUKZGVmaW5lIGZ1bmN0aW9uIFRvU3RyaW5nKHZhbHVlIENvdmVyYWdlU3RhdHVzKTogdmFsdWUudmFsdWUKZGVmaW5lIGZ1bmN0aW9uIFRvU3RyaW5nKHZhbHVlIEN1cnJlbmN5Q29kZSk6IHZhbHVlLnZhbHVlCmRlZmluZSBmdW5jdGlvbiBUb1N0cmluZyh2YWx1ZSBEYXlPZldlZWspOiB2YWx1ZS52YWx1ZQpkZWZpbmUgZnVuY3Rpb24gVG9TdHJpbmcodmFsdWUgRGF5c09mV2Vlayk6IHZhbHVlLnZhbHVlCmRlZmluZSBmdW5jdGlvbiBUb1N0cmluZyh2YWx1ZSBEZXRlY3RlZElzc3VlU2V2ZXJpdHkpOiB2YWx1ZS52YWx1ZQpkZWZpbmUgZnVuY3Rpb24gVG9TdHJpbmcodmFsdWUgRGV0ZWN0ZWRJc3N1ZVN0YXR1cyk6IHZhbHVlLnZhbHVlCmRlZmluZSBmdW5jdGlvbiBUb1N0cmluZyh2YWx1ZSBEZXZpY2VNZXRyaWNDYWxpYnJhdGlvblN0YXRlKTogdmFsdWUudmFsdWUKZGVmaW5lIGZ1bmN0aW9uIFRvU3RyaW5nKHZhbHVlIERldmljZU1ldHJpY0NhbGlicmF0aW9uVHlwZSk6IHZhbHVlLnZhbHVlCmRlZmluZSBmdW5jdGlvbiBUb1N0cmluZyh2YWx1ZSBEZXZpY2VNZXRyaWNDYXRlZ29yeSk6IHZhbHVlLnZhbHVlCmRlZmluZSBmdW5jdGlvbiBUb1N0cmluZyh2YWx1ZSBEZXZpY2VNZXRyaWNDb2xvcik6IHZhbHVlLnZhbHVlCmRlZmluZSBmdW5jdGlvbiBUb1N0cmluZyh2YWx1ZSBEZXZpY2VNZXRyaWNPcGVyYXRpb25hbFN0YXR1cyk6IHZhbHVlLnZhbHVlCmRlZmluZSBmdW5jdGlvbiBUb1N0cmluZyh2YWx1ZSBEZXZpY2VOYW1lVHlwZSk6IHZhbHVlLnZhbHVlCmRlZmluZSBmdW5jdGlvbiBUb1N0cmluZyh2YWx1ZSBEZXZpY2VSZXF1ZXN0U3RhdHVzKTogdmFsdWUudmFsdWUKZGVmaW5lIGZ1bmN0aW9uIFRvU3RyaW5nKHZhbHVlIERldmljZVVzZVN0YXRlbWVudFN0YXR1cyk6IHZhbHVlLnZhbHVlCmRlZmluZSBmdW5jdGlvbiBUb1N0cmluZyh2YWx1ZSBEaWFnbm9zdGljUmVwb3J0U3RhdHVzKTogdmFsdWUudmFsdWUKZGVmaW5lIGZ1bmN0aW9uIFRvU3RyaW5nKHZhbHVlIERpc2NyaW1pbmF0b3JUeXBlKTogdmFsdWUudmFsdWUKZGVmaW5lIGZ1bmN0aW9uIFRvU3RyaW5nKHZhbHVlIERvY3VtZW50Q29uZmlkZW50aWFsaXR5KTogdmFsdWUudmFsdWUKZGVmaW5lIGZ1bmN0aW9uIFRvU3RyaW5nKHZhbHVlIERvY3VtZW50TW9kZSk6IHZhbHVlLnZhbHVlCmRlZmluZSBmdW5jdGlvbiBUb1N0cmluZyh2YWx1ZSBEb2N1bWVudFJlZmVyZW5jZVN0YXR1cyk6IHZhbHVlLnZhbHVlCmRlZmluZSBmdW5jdGlvbiBUb1N0cmluZyh2YWx1ZSBEb2N1bWVudFJlbGF0aW9uc2hpcFR5cGUpOiB2YWx1ZS52YWx1ZQpkZWZpbmUgZnVuY3Rpb24gVG9TdHJpbmcodmFsdWUgRWxpZ2liaWxpdHlSZXF1ZXN0UHVycG9zZSk6IHZhbHVlLnZhbHVlCmRlZmluZSBmdW5jdGlvbiBUb1N0cmluZyh2YWx1ZSBFbGlnaWJpbGl0eVJlcXVlc3RTdGF0dXMpOiB2YWx1ZS52YWx1ZQpkZWZpbmUgZnVuY3Rpb24gVG9TdHJpbmcodmFsdWUgRWxpZ2liaWxpdHlSZXNwb25zZVB1cnBvc2UpOiB2YWx1ZS52YWx1ZQpkZWZpbmUgZnVuY3Rpb24gVG9TdHJpbmcodmFsdWUgRWxpZ2liaWxpdHlSZXNwb25zZVN0YXR1cyk6IHZhbHVlLnZhbHVlCmRlZmluZSBmdW5jdGlvbiBUb1N0cmluZyh2YWx1ZSBFbmFibGVXaGVuQmVoYXZpb3IpOiB2YWx1ZS52YWx1ZQpkZWZpbmUgZnVuY3Rpb24gVG9TdHJpbmcodmFsdWUgRW5jb3VudGVyTG9jYXRpb25TdGF0dXMpOiB2YWx1ZS52YWx1ZQpkZWZpbmUgZnVuY3Rpb24gVG9TdHJpbmcodmFsdWUgRW5jb3VudGVyU3RhdHVzKTogdmFsdWUudmFsdWUKZGVmaW5lIGZ1bmN0aW9uIFRvU3RyaW5nKHZhbHVlIEVuZHBvaW50U3RhdHVzKTogdmFsdWUudmFsdWUKZGVmaW5lIGZ1bmN0aW9uIFRvU3RyaW5nKHZhbHVlIEVucm9sbG1lbnRSZXF1ZXN0U3RhdHVzKTogdmFsdWUudmFsdWUKZGVmaW5lIGZ1bmN0aW9uIFRvU3RyaW5nKHZhbHVlIEVucm9sbG1lbnRSZXNwb25zZVN0YXR1cyk6IHZhbHVlLnZhbHVlCmRlZmluZSBmdW5jdGlvbiBUb1N0cmluZyh2YWx1ZSBFcGlzb2RlT2ZDYXJlU3RhdHVzKTogdmFsdWUudmFsdWUKZGVmaW5lIGZ1bmN0aW9uIFRvU3RyaW5nKHZhbHVlIEV2ZW50Q2FwYWJpbGl0eU1vZGUpOiB2YWx1ZS52YWx1ZQpkZWZpbmUgZnVuY3Rpb24gVG9TdHJpbmcodmFsdWUgRXZlbnRUaW1pbmcpOiB2YWx1ZS52YWx1ZQpkZWZpbmUgZnVuY3Rpb24gVG9TdHJpbmcodmFsdWUgRXZpZGVuY2VWYXJpYWJsZVR5cGUpOiB2YWx1ZS52YWx1ZQpkZWZpbmUgZnVuY3Rpb24gVG9TdHJpbmcodmFsdWUgRXhhbXBsZVNjZW5hcmlvQWN0b3JUeXBlKTogdmFsdWUudmFsdWUKZGVmaW5lIGZ1bmN0aW9uIFRvU3RyaW5nKHZhbHVlIEV4cGxhbmF0aW9uT2ZCZW5lZml0U3RhdHVzKTogdmFsdWUudmFsdWUKZGVmaW5lIGZ1bmN0aW9uIFRvU3RyaW5nKHZhbHVlIEV4cG9zdXJlU3RhdGUpOiB2YWx1ZS52YWx1ZQpkZWZpbmUgZnVuY3Rpb24gVG9TdHJpbmcodmFsdWUgRXh0ZW5zaW9uQ29udGV4dFR5cGUpOiB2YWx1ZS52YWx1ZQpkZWZpbmUgZnVuY3Rpb24gVG9TdHJpbmcodmFsdWUgRkhJUkFsbFR5cGVzKTogdmFsdWUudmFsdWUKZGVmaW5lIGZ1bmN0aW9uIFRvU3RyaW5nKHZhbHVlIEZISVJEZWZpbmVkVHlwZSk6IHZhbHVlLnZhbHVlCmRlZmluZSBmdW5jdGlvbiBUb1N0cmluZyh2YWx1ZSBGSElSRGV2aWNlU3RhdHVzKTogdmFsdWUudmFsdWUKZGVmaW5lIGZ1bmN0aW9uIFRvU3RyaW5nKHZhbHVlIEZISVJSZXNvdXJjZVR5cGUpOiB2YWx1ZS52YWx1ZQpkZWZpbmUgZnVuY3Rpb24gVG9TdHJpbmcodmFsdWUgRkhJUlN1YnN0YW5jZVN0YXR1cyk6IHZhbHVlLnZhbHVlCmRlZmluZSBmdW5jdGlvbiBUb1N0cmluZyh2YWx1ZSBGSElSVmVyc2lvbik6IHZhbHVlLnZhbHVlCmRlZmluZSBmdW5jdGlvbiBUb1N0cmluZyh2YWx1ZSBGYW1pbHlIaXN0b3J5U3RhdHVzKTogdmFsdWUudmFsdWUKZGVmaW5lIGZ1bmN0aW9uIFRvU3RyaW5nKHZhbHVlIEZpbHRlck9wZXJhdG9yKTogdmFsdWUudmFsdWUKZGVmaW5lIGZ1bmN0aW9uIFRvU3RyaW5nKHZhbHVlIEZsYWdTdGF0dXMpOiB2YWx1ZS52YWx1ZQpkZWZpbmUgZnVuY3Rpb24gVG9TdHJpbmcodmFsdWUgR29hbExpZmVjeWNsZVN0YXR1cyk6IHZhbHVlLnZhbHVlCmRlZmluZSBmdW5jdGlvbiBUb1N0cmluZyh2YWx1ZSBHcmFwaENvbXBhcnRtZW50UnVsZSk6IHZhbHVlLnZhbHVlCmRlZmluZSBmdW5jdGlvbiBUb1N0cmluZyh2YWx1ZSBHcmFwaENvbXBhcnRtZW50VXNlKTogdmFsdWUudmFsdWUKZGVmaW5lIGZ1bmN0aW9uIFRvU3RyaW5nKHZhbHVlIEdyb3VwTWVhc3VyZSk6IHZhbHVlLnZhbHVlCmRlZmluZSBmdW5jdGlvbiBUb1N0cmluZyh2YWx1ZSBHcm91cFR5cGUpOiB2YWx1ZS52YWx1ZQpkZWZpbmUgZnVuY3Rpb24gVG9TdHJpbmcodmFsdWUgR3VpZGFuY2VSZXNwb25zZVN0YXR1cyk6IHZhbHVlLnZhbHVlCmRlZmluZSBmdW5jdGlvbiBUb1N0cmluZyh2YWx1ZSBHdWlkZVBhZ2VHZW5lcmF0aW9uKTogdmFsdWUudmFsdWUKZGVmaW5lIGZ1bmN0aW9uIFRvU3RyaW5nKHZhbHVlIEd1aWRlUGFyYW1ldGVyQ29kZSk6IHZhbHVlLnZhbHVlCmRlZmluZSBmdW5jdGlvbiBUb1N0cmluZyh2YWx1ZSBIVFRQVmVyYik6IHZhbHVlLnZhbHVlCmRlZmluZSBmdW5jdGlvbiBUb1N0cmluZyh2YWx1ZSBJZGVudGlmaWVyVXNlKTogdmFsdWUudmFsdWUKZGVmaW5lIGZ1bmN0aW9uIFRvU3RyaW5nKHZhbHVlIElkZW50aXR5QXNzdXJhbmNlTGV2ZWwpOiB2YWx1ZS52YWx1ZQpkZWZpbmUgZnVuY3Rpb24gVG9TdHJpbmcodmFsdWUgSW1hZ2luZ1N0dWR5U3RhdHVzKTogdmFsdWUudmFsdWUKZGVmaW5lIGZ1bmN0aW9uIFRvU3RyaW5nKHZhbHVlIEltbXVuaXphdGlvbkV2YWx1YXRpb25TdGF0dXMpOiB2YWx1ZS52YWx1ZQpkZWZpbmUgZnVuY3Rpb24gVG9TdHJpbmcodmFsdWUgSW1tdW5pemF0aW9uU3RhdHVzKTogdmFsdWUudmFsdWUKZGVmaW5lIGZ1bmN0aW9uIFRvU3RyaW5nKHZhbHVlIEludm9pY2VQcmljZUNvbXBvbmVudFR5cGUpOiB2YWx1ZS52YWx1ZQpkZWZpbmUgZnVuY3Rpb24gVG9TdHJpbmcodmFsdWUgSW52b2ljZVN0YXR1cyk6IHZhbHVlLnZhbHVlCmRlZmluZSBmdW5jdGlvbiBUb1N0cmluZyh2YWx1ZSBJc3N1ZVNldmVyaXR5KTogdmFsdWUudmFsdWUKZGVmaW5lIGZ1bmN0aW9uIFRvU3RyaW5nKHZhbHVlIElzc3VlVHlwZSk6IHZhbHVlLnZhbHVlCmRlZmluZSBmdW5jdGlvbiBUb1N0cmluZyh2YWx1ZSBMaW5rVHlwZSk6IHZhbHVlLnZhbHVlCmRlZmluZSBmdW5jdGlvbiBUb1N0cmluZyh2YWx1ZSBMaW5rYWdlVHlwZSk6IHZhbHVlLnZhbHVlCmRlZmluZSBmdW5jdGlvbiBUb1N0cmluZyh2YWx1ZSBMaXN0TW9kZSk6IHZhbHVlLnZhbHVlCmRlZmluZSBmdW5jdGlvbiBUb1N0cmluZyh2YWx1ZSBMaXN0U3RhdHVzKTogdmFsdWUudmFsdWUKZGVmaW5lIGZ1bmN0aW9uIFRvU3RyaW5nKHZhbHVlIExvY2F0aW9uTW9kZSk6IHZhbHVlLnZhbHVlCmRlZmluZSBmdW5jdGlvbiBUb1N0cmluZyh2YWx1ZSBMb2NhdGlvblN0YXR1cyk6IHZhbHVlLnZhbHVlCmRlZmluZSBmdW5jdGlvbiBUb1N0cmluZyh2YWx1ZSBNZWFzdXJlUmVwb3J0U3RhdHVzKTogdmFsdWUudmFsdWUKZGVmaW5lIGZ1bmN0aW9uIFRvU3RyaW5nKHZhbHVlIE1lYXN1cmVSZXBvcnRUeXBlKTogdmFsdWUudmFsdWUKZGVmaW5lIGZ1bmN0aW9uIFRvU3RyaW5nKHZhbHVlIE1lZGlhU3RhdHVzKTogdmFsdWUudmFsdWUKZGVmaW5lIGZ1bmN0aW9uIFRvU3RyaW5nKHZhbHVlIE1lZGljYXRpb25BZG1pbmlzdHJhdGlvblN0YXR1cyk6IHZhbHVlLnZhbHVlCmRlZmluZSBmdW5jdGlvbiBUb1N0cmluZyh2YWx1ZSBNZWRpY2F0aW9uRGlzcGVuc2VTdGF0dXMpOiB2YWx1ZS52YWx1ZQpkZWZpbmUgZnVuY3Rpb24gVG9TdHJpbmcodmFsdWUgTWVkaWNhdGlvbktub3dsZWRnZVN0YXR1cyk6IHZhbHVlLnZhbHVlCmRlZmluZSBmdW5jdGlvbiBUb1N0cmluZyh2YWx1ZSBNZWRpY2F0aW9uUmVxdWVzdEludGVudCk6IHZhbHVlLnZhbHVlCmRlZmluZSBmdW5jdGlvbiBUb1N0cmluZyh2YWx1ZSBNZWRpY2F0aW9uUmVxdWVzdFByaW9yaXR5KTogdmFsdWUudmFsdWUKZGVmaW5lIGZ1bmN0aW9uIFRvU3RyaW5nKHZhbHVlIE1lZGljYXRpb25SZXF1ZXN0U3RhdHVzKTogdmFsdWUudmFsdWUKZGVmaW5lIGZ1bmN0aW9uIFRvU3RyaW5nKHZhbHVlIE1lZGljYXRpb25TdGF0ZW1lbnRTdGF0dXMpOiB2YWx1ZS52YWx1ZQpkZWZpbmUgZnVuY3Rpb24gVG9TdHJpbmcodmFsdWUgTWVkaWNhdGlvblN0YXR1cyk6IHZhbHVlLnZhbHVlCmRlZmluZSBmdW5jdGlvbiBUb1N0cmluZyh2YWx1ZSBNZXNzYWdlU2lnbmlmaWNhbmNlQ2F0ZWdvcnkpOiB2YWx1ZS52YWx1ZQpkZWZpbmUgZnVuY3Rpb24gVG9TdHJpbmcodmFsdWUgTWVzc2FnZWhlYWRlcl9SZXNwb25zZV9SZXF1ZXN0KTogdmFsdWUudmFsdWUKZGVmaW5lIGZ1bmN0aW9uIFRvU3RyaW5nKHZhbHVlIE1pbWVUeXBlKTogdmFsdWUudmFsdWUKZGVmaW5lIGZ1bmN0aW9uIFRvU3RyaW5nKHZhbHVlIE5hbWVVc2UpOiB2YWx1ZS52YWx1ZQpkZWZpbmUgZnVuY3Rpb24gVG9TdHJpbmcodmFsdWUgTmFtaW5nU3lzdGVtSWRlbnRpZmllclR5cGUpOiB2YWx1ZS52YWx1ZQpkZWZpbmUgZnVuY3Rpb24gVG9TdHJpbmcodmFsdWUgTmFtaW5nU3lzdGVtVHlwZSk6IHZhbHVlLnZhbHVlCmRlZmluZSBmdW5jdGlvbiBUb1N0cmluZyh2YWx1ZSBOYXJyYXRpdmVTdGF0dXMpOiB2YWx1ZS52YWx1ZQpkZWZpbmUgZnVuY3Rpb24gVG9TdHJpbmcodmFsdWUgTm90ZVR5cGUpOiB2YWx1ZS52YWx1ZQpkZWZpbmUgZnVuY3Rpb24gVG9TdHJpbmcodmFsdWUgTnV0cml0aWlvbk9yZGVySW50ZW50KTogdmFsdWUudmFsdWUKZGVmaW5lIGZ1bmN0aW9uIFRvU3RyaW5nKHZhbHVlIE51dHJpdGlvbk9yZGVyU3RhdHVzKTogdmFsdWUudmFsdWUKZGVmaW5lIGZ1bmN0aW9uIFRvU3RyaW5nKHZhbHVlIE9ic2VydmF0aW9uRGF0YVR5cGUpOiB2YWx1ZS52YWx1ZQpkZWZpbmUgZnVuY3Rpb24gVG9TdHJpbmcodmFsdWUgT2JzZXJ2YXRpb25SYW5nZUNhdGVnb3J5KTogdmFsdWUudmFsdWUKZGVmaW5lIGZ1bmN0aW9uIFRvU3RyaW5nKHZhbHVlIE9ic2VydmF0aW9uU3RhdHVzKTogdmFsdWUudmFsdWUKZGVmaW5lIGZ1bmN0aW9uIFRvU3RyaW5nKHZhbHVlIE9wZXJhdGlvbktpbmQpOiB2YWx1ZS52YWx1ZQpkZWZpbmUgZnVuY3Rpb24gVG9TdHJpbmcodmFsdWUgT3BlcmF0aW9uUGFyYW1ldGVyVXNlKTogdmFsdWUudmFsdWUKZGVmaW5lIGZ1bmN0aW9uIFRvU3RyaW5nKHZhbHVlIE9yaWVudGF0aW9uVHlwZSk6IHZhbHVlLnZhbHVlCmRlZmluZSBmdW5jdGlvbiBUb1N0cmluZyh2YWx1ZSBQYXJhbWV0ZXJVc2UpOiB2YWx1ZS52YWx1ZQpkZWZpbmUgZnVuY3Rpb24gVG9TdHJpbmcodmFsdWUgUGFydGljaXBhbnRSZXF1aXJlZCk6IHZhbHVlLnZhbHVlCmRlZmluZSBmdW5jdGlvbiBUb1N0cmluZyh2YWx1ZSBQYXJ0aWNpcGFudFN0YXR1cyk6IHZhbHVlLnZhbHVlCmRlZmluZSBmdW5jdGlvbiBUb1N0cmluZyh2YWx1ZSBQYXJ0aWNpcGF0aW9uU3RhdHVzKTogdmFsdWUudmFsdWUKZGVmaW5lIGZ1bmN0aW9uIFRvU3RyaW5nKHZhbHVlIFBheW1lbnROb3RpY2VTdGF0dXMpOiB2YWx1ZS52YWx1ZQpkZWZpbmUgZnVuY3Rpb24gVG9TdHJpbmcodmFsdWUgUGF5bWVudFJlY29uY2lsaWF0aW9uU3RhdHVzKTogdmFsdWUudmFsdWUKZGVmaW5lIGZ1bmN0aW9uIFRvU3RyaW5nKHZhbHVlIFByb2NlZHVyZVN0YXR1cyk6IHZhbHVlLnZhbHVlCmRlZmluZSBmdW5jdGlvbiBUb1N0cmluZyh2YWx1ZSBQcm9wZXJ0eVJlcHJlc2VudGF0aW9uKTogdmFsdWUudmFsdWUKZGVmaW5lIGZ1bmN0aW9uIFRvU3RyaW5nKHZhbHVlIFByb3BlcnR5VHlwZSk6IHZhbHVlLnZhbHVlCmRlZmluZSBmdW5jdGlvbiBUb1N0cmluZyh2YWx1ZSBQcm92ZW5hbmNlRW50aXR5Um9sZSk6IHZhbHVlLnZhbHVlCmRlZmluZSBmdW5jdGlvbiBUb1N0cmluZyh2YWx1ZSBQdWJsaWNhdGlvblN0YXR1cyk6IHZhbHVlLnZhbHVlCmRlZmluZSBmdW5jdGlvbiBUb1N0cmluZyh2YWx1ZSBRdWFsaXR5VHlwZSk6IHZhbHVlLnZhbHVlCmRlZmluZSBmdW5jdGlvbiBUb1N0cmluZyh2YWx1ZSBRdWFudGl0eUNvbXBhcmF0b3IpOiB2YWx1ZS52YWx1ZQpkZWZpbmUgZnVuY3Rpb24gVG9TdHJpbmcodmFsdWUgUXVlc3Rpb25uYWlyZUl0ZW1PcGVyYXRvcik6IHZhbHVlLnZhbHVlCmRlZmluZSBmdW5jdGlvbiBUb1N0cmluZyh2YWx1ZSBRdWVzdGlvbm5haXJlSXRlbVR5cGUpOiB2YWx1ZS52YWx1ZQpkZWZpbmUgZnVuY3Rpb24gVG9TdHJpbmcodmFsdWUgUXVlc3Rpb25uYWlyZVJlc3BvbnNlU3RhdHVzKTogdmFsdWUudmFsdWUKZGVmaW5lIGZ1bmN0aW9uIFRvU3RyaW5nKHZhbHVlIFJlZmVyZW5jZUhhbmRsaW5nUG9saWN5KTogdmFsdWUudmFsdWUKZGVmaW5lIGZ1bmN0aW9uIFRvU3RyaW5nKHZhbHVlIFJlZmVyZW5jZVZlcnNpb25SdWxlcyk6IHZhbHVlLnZhbHVlCmRlZmluZSBmdW5jdGlvbiBUb1N0cmluZyh2YWx1ZSBSZWZlcnJlZERvY3VtZW50U3RhdHVzKTogdmFsdWUudmFsdWUKZGVmaW5lIGZ1bmN0aW9uIFRvU3RyaW5nKHZhbHVlIFJlbGF0ZWRBcnRpZmFjdFR5cGUpOiB2YWx1ZS52YWx1ZQpkZWZpbmUgZnVuY3Rpb24gVG9TdHJpbmcodmFsdWUgUmVtaXR0YW5jZU91dGNvbWUpOiB2YWx1ZS52YWx1ZQpkZWZpbmUgZnVuY3Rpb24gVG9TdHJpbmcodmFsdWUgUmVwb3NpdG9yeVR5cGUpOiB2YWx1ZS52YWx1ZQpkZWZpbmUgZnVuY3Rpb24gVG9TdHJpbmcodmFsdWUgUmVxdWVzdEludGVudCk6IHZhbHVlLnZhbHVlCmRlZmluZSBmdW5jdGlvbiBUb1N0cmluZyh2YWx1ZSBSZXF1ZXN0UHJpb3JpdHkpOiB2YWx1ZS52YWx1ZQpkZWZpbmUgZnVuY3Rpb24gVG9TdHJpbmcodmFsdWUgUmVxdWVzdFN0YXR1cyk6IHZhbHVlLnZhbHVlCmRlZmluZSBmdW5jdGlvbiBUb1N0cmluZyh2YWx1ZSBSZXNlYXJjaEVsZW1lbnRUeXBlKTogdmFsdWUudmFsdWUKZGVmaW5lIGZ1bmN0aW9uIFRvU3RyaW5nKHZhbHVlIFJlc2VhcmNoU3R1ZHlTdGF0dXMpOiB2YWx1ZS52YWx1ZQpkZWZpbmUgZnVuY3Rpb24gVG9TdHJpbmcodmFsdWUgUmVzZWFyY2hTdWJqZWN0U3RhdHVzKTogdmFsdWUudmFsdWUKZGVmaW5lIGZ1bmN0aW9uIFRvU3RyaW5nKHZhbHVlIFJlc291cmNlVHlwZSk6IHZhbHVlLnZhbHVlCmRlZmluZSBmdW5jdGlvbiBUb1N0cmluZyh2YWx1ZSBSZXNvdXJjZVZlcnNpb25Qb2xpY3kpOiB2YWx1ZS52YWx1ZQpkZWZpbmUgZnVuY3Rpb24gVG9TdHJpbmcodmFsdWUgUmVzcG9uc2VUeXBlKTogdmFsdWUudmFsdWUKZGVmaW5lIGZ1bmN0aW9uIFRvU3RyaW5nKHZhbHVlIFJlc3RmdWxDYXBhYmlsaXR5TW9kZSk6IHZhbHVlLnZhbHVlCmRlZmluZSBmdW5jdGlvbiBUb1N0cmluZyh2YWx1ZSBSaXNrQXNzZXNzbWVudFN0YXR1cyk6IHZhbHVlLnZhbHVlCmRlZmluZSBmdW5jdGlvbiBUb1N0cmluZyh2YWx1ZSBTUERYTGljZW5zZSk6IHZhbHVlLnZhbHVlCmRlZmluZSBmdW5jdGlvbiBUb1N0cmluZyh2YWx1ZSBTZWFyY2hDb21wYXJhdG9yKTogdmFsdWUudmFsdWUKZGVmaW5lIGZ1bmN0aW9uIFRvU3RyaW5nKHZhbHVlIFNlYXJjaEVudHJ5TW9kZSk6IHZhbHVlLnZhbHVlCmRlZmluZSBmdW5jdGlvbiBUb1N0cmluZyh2YWx1ZSBTZWFyY2hNb2RpZmllckNvZGUpOiB2YWx1ZS52YWx1ZQpkZWZpbmUgZnVuY3Rpb24gVG9TdHJpbmcodmFsdWUgU2VhcmNoUGFyYW1UeXBlKTogdmFsdWUudmFsdWUKZGVmaW5lIGZ1bmN0aW9uIFRvU3RyaW5nKHZhbHVlIFNlY3Rpb25Nb2RlKTogdmFsdWUudmFsdWUKZGVmaW5lIGZ1bmN0aW9uIFRvU3RyaW5nKHZhbHVlIFNlcXVlbmNlVHlwZSk6IHZhbHVlLnZhbHVlCmRlZmluZSBmdW5jdGlvbiBUb1N0cmluZyh2YWx1ZSBTZXJ2aWNlUmVxdWVzdEludGVudCk6IHZhbHVlLnZhbHVlCmRlZmluZSBmdW5jdGlvbiBUb1N0cmluZyh2YWx1ZSBTZXJ2aWNlUmVxdWVzdFByaW9yaXR5KTogdmFsdWUudmFsdWUKZGVmaW5lIGZ1bmN0aW9uIFRvU3RyaW5nKHZhbHVlIFNlcnZpY2VSZXF1ZXN0U3RhdHVzKTogdmFsdWUudmFsdWUKZGVmaW5lIGZ1bmN0aW9uIFRvU3RyaW5nKHZhbHVlIFNsaWNpbmdSdWxlcyk6IHZhbHVlLnZhbHVlCmRlZmluZSBmdW5jdGlvbiBUb1N0cmluZyh2YWx1ZSBTbG90U3RhdHVzKTogdmFsdWUudmFsdWUKZGVmaW5lIGZ1bmN0aW9uIFRvU3RyaW5nKHZhbHVlIFNvcnREaXJlY3Rpb24pOiB2YWx1ZS52YWx1ZQpkZWZpbmUgZnVuY3Rpb24gVG9TdHJpbmcodmFsdWUgU3BlY2ltZW5Db250YWluZWRQcmVmZXJlbmNlKTogdmFsdWUudmFsdWUKZGVmaW5lIGZ1bmN0aW9uIFRvU3RyaW5nKHZhbHVlIFNwZWNpbWVuU3RhdHVzKTogdmFsdWUudmFsdWUKZGVmaW5lIGZ1bmN0aW9uIFRvU3RyaW5nKHZhbHVlIFN0YXR1cyk6IHZhbHVlLnZhbHVlCmRlZmluZSBmdW5jdGlvbiBUb1N0cmluZyh2YWx1ZSBTdHJhbmRUeXBlKTogdmFsdWUudmFsdWUKZGVmaW5lIGZ1bmN0aW9uIFRvU3RyaW5nKHZhbHVlIFN0cnVjdHVyZURlZmluaXRpb25LaW5kKTogdmFsdWUudmFsdWUKZGVmaW5lIGZ1bmN0aW9uIFRvU3RyaW5nKHZhbHVlIFN0cnVjdHVyZU1hcENvbnRleHRUeXBlKTogdmFsdWUudmFsdWUKZGVmaW5lIGZ1bmN0aW9uIFRvU3RyaW5nKHZhbHVlIFN0cnVjdHVyZU1hcEdyb3VwVHlwZU1vZGUpOiB2YWx1ZS52YWx1ZQpkZWZpbmUgZnVuY3Rpb24gVG9TdHJpbmcodmFsdWUgU3RydWN0dXJlTWFwSW5wdXRNb2RlKTogdmFsdWUudmFsdWUKZGVmaW5lIGZ1bmN0aW9uIFRvU3RyaW5nKHZhbHVlIFN0cnVjdHVyZU1hcE1vZGVsTW9kZSk6IHZhbHVlLnZhbHVlCmRlZmluZSBmdW5jdGlvbiBUb1N0cmluZyh2YWx1ZSBTdHJ1Y3R1cmVNYXBTb3VyY2VMaXN0TW9kZSk6IHZhbHVlLnZhbHVlCmRlZmluZSBmdW5jdGlvbiBUb1N0cmluZyh2YWx1ZSBTdHJ1Y3R1cmVNYXBUYXJnZXRMaXN0TW9kZSk6IHZhbHVlLnZhbHVlCmRlZmluZSBmdW5jdGlvbiBUb1N0cmluZyh2YWx1ZSBTdHJ1Y3R1cmVNYXBUcmFuc2Zvcm0pOiB2YWx1ZS52YWx1ZQpkZWZpbmUgZnVuY3Rpb24gVG9TdHJpbmcodmFsdWUgU3Vic2NyaXB0aW9uQ2hhbm5lbFR5cGUpOiB2YWx1ZS52YWx1ZQpkZWZpbmUgZnVuY3Rpb24gVG9TdHJpbmcodmFsdWUgU3Vic2NyaXB0aW9uU3RhdHVzKTogdmFsdWUudmFsdWUKZGVmaW5lIGZ1bmN0aW9uIFRvU3RyaW5nKHZhbHVlIFN1cHBseURlbGl2ZXJ5U3RhdHVzKTogdmFsdWUudmFsdWUKZGVmaW5lIGZ1bmN0aW9uIFRvU3RyaW5nKHZhbHVlIFN1cHBseVJlcXVlc3RTdGF0dXMpOiB2YWx1ZS52YWx1ZQpkZWZpbmUgZnVuY3Rpb24gVG9TdHJpbmcodmFsdWUgU3lzdGVtUmVzdGZ1bEludGVyYWN0aW9uKTogdmFsdWUudmFsdWUKZGVmaW5lIGZ1bmN0aW9uIFRvU3RyaW5nKHZhbHVlIFRhc2tJbnRlbnQpOiB2YWx1ZS52YWx1ZQpkZWZpbmUgZnVuY3Rpb24gVG9TdHJpbmcodmFsdWUgVGFza1ByaW9yaXR5KTogdmFsdWUudmFsdWUKZGVmaW5lIGZ1bmN0aW9uIFRvU3RyaW5nKHZhbHVlIFRhc2tTdGF0dXMpOiB2YWx1ZS52YWx1ZQpkZWZpbmUgZnVuY3Rpb24gVG9TdHJpbmcodmFsdWUgVGVzdFJlcG9ydEFjdGlvblJlc3VsdCk6IHZhbHVlLnZhbHVlCmRlZmluZSBmdW5jdGlvbiBUb1N0cmluZyh2YWx1ZSBUZXN0UmVwb3J0UGFydGljaXBhbnRUeXBlKTogdmFsdWUudmFsdWUKZGVmaW5lIGZ1bmN0aW9uIFRvU3RyaW5nKHZhbHVlIFRlc3RSZXBvcnRSZXN1bHQpOiB2YWx1ZS52YWx1ZQpkZWZpbmUgZnVuY3Rpb24gVG9TdHJpbmcodmFsdWUgVGVzdFJlcG9ydFN0YXR1cyk6IHZhbHVlLnZhbHVlCmRlZmluZSBmdW5jdGlvbiBUb1N0cmluZyh2YWx1ZSBUZXN0U2NyaXB0UmVxdWVzdE1ldGhvZENvZGUpOiB2YWx1ZS52YWx1ZQpkZWZpbmUgZnVuY3Rpb24gVG9TdHJpbmcodmFsdWUgVHJpZ2dlclR5cGUpOiB2YWx1ZS52YWx1ZQpkZWZpbmUgZnVuY3Rpb24gVG9TdHJpbmcodmFsdWUgVHlwZURlcml2YXRpb25SdWxlKTogdmFsdWUudmFsdWUKZGVmaW5lIGZ1bmN0aW9uIFRvU3RyaW5nKHZhbHVlIFR5cGVSZXN0ZnVsSW50ZXJhY3Rpb24pOiB2YWx1ZS52YWx1ZQpkZWZpbmUgZnVuY3Rpb24gVG9TdHJpbmcodmFsdWUgVURJRW50cnlUeXBlKTogdmFsdWUudmFsdWUKZGVmaW5lIGZ1bmN0aW9uIFRvU3RyaW5nKHZhbHVlIFVuaXRzT2ZUaW1lKTogdmFsdWUudmFsdWUKZGVmaW5lIGZ1bmN0aW9uIFRvU3RyaW5nKHZhbHVlIFVzZSk6IHZhbHVlLnZhbHVlCmRlZmluZSBmdW5jdGlvbiBUb1N0cmluZyh2YWx1ZSBWYXJpYWJsZVR5cGUpOiB2YWx1ZS52YWx1ZQpkZWZpbmUgZnVuY3Rpb24gVG9TdHJpbmcodmFsdWUgVmlzaW9uQmFzZSk6IHZhbHVlLnZhbHVlCmRlZmluZSBmdW5jdGlvbiBUb1N0cmluZyh2YWx1ZSBWaXNpb25FeWVzKTogdmFsdWUudmFsdWUKZGVmaW5lIGZ1bmN0aW9uIFRvU3RyaW5nKHZhbHVlIFZpc2lvblN0YXR1cyk6IHZhbHVlLnZhbHVlCmRlZmluZSBmdW5jdGlvbiBUb1N0cmluZyh2YWx1ZSBYUGF0aFVzYWdlVHlwZSk6IHZhbHVlLnZhbHVlCmRlZmluZSBmdW5jdGlvbiBUb1N0cmluZyh2YWx1ZSBiYXNlNjRCaW5hcnkpOiB2YWx1ZS52YWx1ZQpkZWZpbmUgZnVuY3Rpb24gVG9TdHJpbmcodmFsdWUgaWQpOiB2YWx1ZS52YWx1ZQpkZWZpbmUgZnVuY3Rpb24gVG9Cb29sZWFuKHZhbHVlIGJvb2xlYW4pOiB2YWx1ZS52YWx1ZQpkZWZpbmUgZnVuY3Rpb24gVG9EYXRlKHZhbHVlIGRhdGUpOiB2YWx1ZS52YWx1ZQpkZWZpbmUgZnVuY3Rpb24gVG9EYXRlVGltZSh2YWx1ZSBkYXRlVGltZSk6IHZhbHVlLnZhbHVlCmRlZmluZSBmdW5jdGlvbiBUb0RlY2ltYWwodmFsdWUgZGVjaW1hbCk6IHZhbHVlLnZhbHVlCmRlZmluZSBmdW5jdGlvbiBUb0RhdGVUaW1lKHZhbHVlIGluc3RhbnQpOiB2YWx1ZS52YWx1ZQpkZWZpbmUgZnVuY3Rpb24gVG9JbnRlZ2VyKHZhbHVlIGludGVnZXIpOiB2YWx1ZS52YWx1ZQpkZWZpbmUgZnVuY3Rpb24gVG9TdHJpbmcodmFsdWUgc3RyaW5nKTogdmFsdWUudmFsdWUKZGVmaW5lIGZ1bmN0aW9uIFRvVGltZSh2YWx1ZSB0aW1lKTogdmFsdWUudmFsdWUKZGVmaW5lIGZ1bmN0aW9uIFRvU3RyaW5nKHZhbHVlIHVyaSk6IHZhbHVlLnZhbHVlCmRlZmluZSBmdW5jdGlvbiBUb1N0cmluZyh2YWx1ZSB4aHRtbCk6IHZhbHVlLnZhbHVlCg==" + }, + { + "contentType": "application/elm+xml", + "data": "PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0iVVRGLTgiPz4KPGxpYnJhcnkgeG1sbnM9InVybjpobDctb3JnOmVsbTpyMSIgeG1sbnM6dD0idXJuOmhsNy1vcmc6ZWxtLXR5cGVzOnIxIiB4bWxuczp4c2k9Imh0dHA6Ly93d3cudzMub3JnLzIwMDEvWE1MU2NoZW1hLWluc3RhbmNlIiB4bWxuczp4c2Q9Imh0dHA6Ly93d3cudzMub3JnLzIwMDEvWE1MU2NoZW1hIiB4bWxuczpmaGlyPSJodHRwOi8vaGw3Lm9yZy9maGlyIiB4bWxuczpxZG00Mz0idXJuOmhlYWx0aGl0LWdvdjpxZG06djRfMyIgeG1sbnM6cWRtNTM9InVybjpoZWFsdGhpdC1nb3Y6cWRtOnY1XzMiIHhtbG5zOmE9InVybjpobDctb3JnOmNxbC1hbm5vdGF0aW9uczpyMSI+CiAgIDxhbm5vdGF0aW9uIHRyYW5zbGF0b3JWZXJzaW9uPSIyLjcuMCIgdHJhbnNsYXRvck9wdGlvbnM9IkVuYWJsZUxvY2F0b3JzLERpc2FibGVMaXN0RGVtb3Rpb24sRGlzYWJsZUxpc3RQcm9tb3Rpb24iIHhzaTp0eXBlPSJhOkNxbFRvRWxtSW5mbyIvPgogICA8aWRlbnRpZmllciBpZD0iRFFNRkhJUkhlbHBlcnMiIHN5c3RlbT0iaHR0cDovL2NvbnRlbnQuYWxwaG9yYS5jb20vZmhpci9kcW0iIHZlcnNpb249IjQuMC4xIi8+CiAgIDxzY2hlbWFJZGVudGlmaWVyIGlkPSJ1cm46aGw3LW9yZzplbG0iIHZlcnNpb249InIxIi8+CiAgIDx1c2luZ3M+CiAgICAgIDxkZWYgbG9jYWxJZGVudGlmaWVyPSJTeXN0ZW0iIHVyaT0idXJuOmhsNy1vcmc6ZWxtLXR5cGVzOnIxIi8+CiAgICAgIDxkZWYgbG9jYXRvcj0iMzoxLTM6MjYiIGxvY2FsSWRlbnRpZmllcj0iRkhJUiIgdXJpPSJodHRwOi8vaGw3Lm9yZy9maGlyIiB2ZXJzaW9uPSI0LjAuMSIvPgogICA8L3VzaW5ncz4KICAgPHN0YXRlbWVudHM+CiAgICAgIDxkZWYgbG9jYXRvcj0iNToxLTk6NTgiIG5hbWU9IlRvSW50ZXJ2YWwiIGNvbnRleHQ9IlBhdGllbnQiIGFjY2Vzc0xldmVsPSJQdWJsaWMiIHhzaTp0eXBlPSJGdW5jdGlvbkRlZiI+CiAgICAgICAgIDxleHByZXNzaW9uIGxvY2F0b3I9IjY6NS05OjU4IiB4c2k6dHlwZT0iSWYiPgogICAgICAgICAgICA8Y29uZGl0aW9uIGxvY2F0b3I9IjY6OC02OjIxIiB4c2k6dHlwZT0iSXNOdWxsIj4KICAgICAgICAgICAgICAgPG9wZXJhbmQgbG9jYXRvcj0iNjo4LTY6MTMiIG5hbWU9InBlcmlvZCIgeHNpOnR5cGU9Ik9wZXJhbmRSZWYiLz4KICAgICAgICAgICAgPC9jb25kaXRpb24+CiAgICAgICAgICAgIDx0aGVuIHhzaTp0eXBlPSJBcyI+CiAgICAgICAgICAgICAgIDxvcGVyYW5kIGxvY2F0b3I9Ijc6OS03OjEyIiB4c2k6dHlwZT0iTnVsbCIvPgogICAgICAgICAgICAgICA8YXNUeXBlU3BlY2lmaWVyIHhzaTp0eXBlPSJJbnRlcnZhbFR5cGVTcGVjaWZpZXIiPgogICAgICAgICAgICAgICAgICA8cG9pbnRUeXBlIG5hbWU9InQ6RGF0ZVRpbWUiIHhzaTp0eXBlPSJOYW1lZFR5cGVTcGVjaWZpZXIiLz4KICAgICAgICAgICAgICAgPC9hc1R5cGVTcGVjaWZpZXI+CiAgICAgICAgICAgIDwvdGhlbj4KICAgICAgICAgICAgPGVsc2UgbG9jYXRvcj0iOTo5LTk6NTgiIGxvd0Nsb3NlZD0idHJ1ZSIgaGlnaENsb3NlZD0idHJ1ZSIgeHNpOnR5cGU9IkludGVydmFsIj4KICAgICAgICAgICAgICAgPGxvdyBsb2NhdG9yPSI5OjE4LTk6MzciIHBhdGg9InZhbHVlIiB4c2k6dHlwZT0iUHJvcGVydHkiPgogICAgICAgICAgICAgICAgICA8c291cmNlIGxvY2F0b3I9Ijk6MTgtOTozMSIgcGF0aD0ic3RhcnQiIHhzaTp0eXBlPSJQcm9wZXJ0eSI+CiAgICAgICAgICAgICAgICAgICAgIDxzb3VyY2UgbG9jYXRvcj0iOToxOC05OjIzIiBuYW1lPSJwZXJpb2QiIHhzaTp0eXBlPSJPcGVyYW5kUmVmIi8+CiAgICAgICAgICAgICAgICAgIDwvc291cmNlPgogICAgICAgICAgICAgICA8L2xvdz4KICAgICAgICAgICAgICAgPGhpZ2ggbG9jYXRvcj0iOTo0MC05OjU3IiBwYXRoPSJ2YWx1ZSIgeHNpOnR5cGU9IlByb3BlcnR5Ij4KICAgICAgICAgICAgICAgICAgPHNvdXJjZSBsb2NhdG9yPSI5OjQwLTk6NTEiIHBhdGg9ImVuZCIgeHNpOnR5cGU9IlByb3BlcnR5Ij4KICAgICAgICAgICAgICAgICAgICAgPHNvdXJjZSBsb2NhdG9yPSI5OjQwLTk6NDUiIG5hbWU9InBlcmlvZCIgeHNpOnR5cGU9Ik9wZXJhbmRSZWYiLz4KICAgICAgICAgICAgICAgICAgPC9zb3VyY2U+CiAgICAgICAgICAgICAgIDwvaGlnaD4KICAgICAgICAgICAgPC9lbHNlPgogICAgICAgICA8L2V4cHJlc3Npb24+CiAgICAgICAgIDxvcGVyYW5kIG5hbWU9InBlcmlvZCI+CiAgICAgICAgICAgIDxvcGVyYW5kVHlwZVNwZWNpZmllciBsb2NhdG9yPSI1OjM1LTU6NDUiIG5hbWU9ImZoaXI6UGVyaW9kIiB4c2k6dHlwZT0iTmFtZWRUeXBlU3BlY2lmaWVyIi8+CiAgICAgICAgIDwvb3BlcmFuZD4KICAgICAgPC9kZWY+CiAgICAgIDxkZWYgbG9jYXRvcj0iMTE6MS0xNTo4MiIgbmFtZT0iVG9RdWFudGl0eSIgY29udGV4dD0iUGF0aWVudCIgYWNjZXNzTGV2ZWw9IlB1YmxpYyIgeHNpOnR5cGU9IkZ1bmN0aW9uRGVmIj4KICAgICAgICAgPGV4cHJlc3Npb24gbG9jYXRvcj0iMTI6NS0xNTo4MiIgeHNpOnR5cGU9IklmIj4KICAgICAgICAgICAgPGNvbmRpdGlvbiBsb2NhdG9yPSIxMjo4LTEyOjIzIiB4c2k6dHlwZT0iSXNOdWxsIj4KICAgICAgICAgICAgICAgPG9wZXJhbmQgbG9jYXRvcj0iMTI6OC0xMjoxNSIgbmFtZT0icXVhbnRpdHkiIHhzaTp0eXBlPSJPcGVyYW5kUmVmIi8+CiAgICAgICAgICAgIDwvY29uZGl0aW9uPgogICAgICAgICAgICA8dGhlbiBhc1R5cGU9InQ6UXVhbnRpdHkiIHhzaTp0eXBlPSJBcyI+CiAgICAgICAgICAgICAgIDxvcGVyYW5kIGxvY2F0b3I9IjEzOjktMTM6MTIiIHhzaTp0eXBlPSJOdWxsIi8+CiAgICAgICAgICAgIDwvdGhlbj4KICAgICAgICAgICAgPGVsc2UgbG9jYXRvcj0iMTU6OS0xNTo4MiIgY2xhc3NUeXBlPSJ0OlF1YW50aXR5IiB4c2k6dHlwZT0iSW5zdGFuY2UiPgogICAgICAgICAgICAgICA8ZWxlbWVudCBuYW1lPSJ2YWx1ZSI+CiAgICAgICAgICAgICAgICAgIDx2YWx1ZSBsb2NhdG9yPSIxNTozNC0xNTo1MyIgcGF0aD0idmFsdWUiIHhzaTp0eXBlPSJQcm9wZXJ0eSI+CiAgICAgICAgICAgICAgICAgICAgIDxzb3VyY2UgbG9jYXRvcj0iMTU6MzQtMTU6NDciIHBhdGg9InZhbHVlIiB4c2k6dHlwZT0iUHJvcGVydHkiPgogICAgICAgICAgICAgICAgICAgICAgICA8c291cmNlIGxvY2F0b3I9IjE1OjM0LTE1OjQxIiBuYW1lPSJxdWFudGl0eSIgeHNpOnR5cGU9Ik9wZXJhbmRSZWYiLz4KICAgICAgICAgICAgICAgICAgICAgPC9zb3VyY2U+CiAgICAgICAgICAgICAgICAgIDwvdmFsdWU+CiAgICAgICAgICAgICAgIDwvZWxlbWVudD4KICAgICAgICAgICAgICAgPGVsZW1lbnQgbmFtZT0idW5pdCI+CiAgICAgICAgICAgICAgICAgIDx2YWx1ZSBsb2NhdG9yPSIxNTo2Mi0xNTo4MCIgcGF0aD0idmFsdWUiIHhzaTp0eXBlPSJQcm9wZXJ0eSI+CiAgICAgICAgICAgICAgICAgICAgIDxzb3VyY2UgbG9jYXRvcj0iMTU6NjItMTU6NzQiIHBhdGg9InVuaXQiIHhzaTp0eXBlPSJQcm9wZXJ0eSI+CiAgICAgICAgICAgICAgICAgICAgICAgIDxzb3VyY2UgbG9jYXRvcj0iMTU6NjItMTU6NjkiIG5hbWU9InF1YW50aXR5IiB4c2k6dHlwZT0iT3BlcmFuZFJlZiIvPgogICAgICAgICAgICAgICAgICAgICA8L3NvdXJjZT4KICAgICAgICAgICAgICAgICAgPC92YWx1ZT4KICAgICAgICAgICAgICAgPC9lbGVtZW50PgogICAgICAgICAgICA8L2Vsc2U+CiAgICAgICAgIDwvZXhwcmVzc2lvbj4KICAgICAgICAgPG9wZXJhbmQgbmFtZT0icXVhbnRpdHkiPgogICAgICAgICAgICA8b3BlcmFuZFR5cGVTcGVjaWZpZXIgbG9jYXRvcj0iMTE6MzctMTE6NDkiIG5hbWU9ImZoaXI6UXVhbnRpdHkiIHhzaTp0eXBlPSJOYW1lZFR5cGVTcGVjaWZpZXIiLz4KICAgICAgICAgPC9vcGVyYW5kPgogICAgICA8L2RlZj4KICAgICAgPGRlZiBsb2NhdG9yPSIxNzoxLTIxOjEwNyIgbmFtZT0iVG9SYXRpbyIgY29udGV4dD0iUGF0aWVudCIgYWNjZXNzTGV2ZWw9IlB1YmxpYyIgeHNpOnR5cGU9IkZ1bmN0aW9uRGVmIj4KICAgICAgICAgPGV4cHJlc3Npb24gbG9jYXRvcj0iMTg6NS0yMToxMDciIHhzaTp0eXBlPSJJZiI+CiAgICAgICAgICAgIDxjb25kaXRpb24gbG9jYXRvcj0iMTg6OC0xODoyMCIgeHNpOnR5cGU9IklzTnVsbCI+CiAgICAgICAgICAgICAgIDxvcGVyYW5kIGxvY2F0b3I9IjE4OjgtMTg6MTIiIG5hbWU9InJhdGlvIiB4c2k6dHlwZT0iT3BlcmFuZFJlZiIvPgogICAgICAgICAgICA8L2NvbmRpdGlvbj4KICAgICAgICAgICAgPHRoZW4gYXNUeXBlPSJ0OlJhdGlvIiB4c2k6dHlwZT0iQXMiPgogICAgICAgICAgICAgICA8b3BlcmFuZCBsb2NhdG9yPSIxOTo5LTE5OjEyIiB4c2k6dHlwZT0iTnVsbCIvPgogICAgICAgICAgICA8L3RoZW4+CiAgICAgICAgICAgIDxlbHNlIGxvY2F0b3I9IjIxOjktMjE6MTA3IiBjbGFzc1R5cGU9InQ6UmF0aW8iIHhzaTp0eXBlPSJJbnN0YW5jZSI+CiAgICAgICAgICAgICAgIDxlbGVtZW50IG5hbWU9Im51bWVyYXRvciI+CiAgICAgICAgICAgICAgICAgIDx2YWx1ZSBsb2NhdG9yPSIyMTozNS0yMTo2MSIgbmFtZT0iVG9RdWFudGl0eSIgeHNpOnR5cGU9IkZ1bmN0aW9uUmVmIj4KICAgICAgICAgICAgICAgICAgICAgPG9wZXJhbmQgbG9jYXRvcj0iMjE6NDYtMjE6NjAiIHBhdGg9Im51bWVyYXRvciIgeHNpOnR5cGU9IlByb3BlcnR5Ij4KICAgICAgICAgICAgICAgICAgICAgICAgPHNvdXJjZSBsb2NhdG9yPSIyMTo0Ni0yMTo1MCIgbmFtZT0icmF0aW8iIHhzaTp0eXBlPSJPcGVyYW5kUmVmIi8+CiAgICAgICAgICAgICAgICAgICAgIDwvb3BlcmFuZD4KICAgICAgICAgICAgICAgICAgPC92YWx1ZT4KICAgICAgICAgICAgICAgPC9lbGVtZW50PgogICAgICAgICAgICAgICA8ZWxlbWVudCBuYW1lPSJkZW5vbWluYXRvciI+CiAgICAgICAgICAgICAgICAgIDx2YWx1ZSBsb2NhdG9yPSIyMTo3Ny0yMToxMDUiIG5hbWU9IlRvUXVhbnRpdHkiIHhzaTp0eXBlPSJGdW5jdGlvblJlZiI+CiAgICAgICAgICAgICAgICAgICAgIDxvcGVyYW5kIGxvY2F0b3I9IjIxOjg4LTIxOjEwNCIgcGF0aD0iZGVub21pbmF0b3IiIHhzaTp0eXBlPSJQcm9wZXJ0eSI+CiAgICAgICAgICAgICAgICAgICAgICAgIDxzb3VyY2UgbG9jYXRvcj0iMjE6ODgtMjE6OTIiIG5hbWU9InJhdGlvIiB4c2k6dHlwZT0iT3BlcmFuZFJlZiIvPgogICAgICAgICAgICAgICAgICAgICA8L29wZXJhbmQ+CiAgICAgICAgICAgICAgICAgIDwvdmFsdWU+CiAgICAgICAgICAgICAgIDwvZWxlbWVudD4KICAgICAgICAgICAgPC9lbHNlPgogICAgICAgICA8L2V4cHJlc3Npb24+CiAgICAgICAgIDxvcGVyYW5kIG5hbWU9InJhdGlvIj4KICAgICAgICAgICAgPG9wZXJhbmRUeXBlU3BlY2lmaWVyIGxvY2F0b3I9IjE3OjMxLTE3OjQwIiBuYW1lPSJmaGlyOlJhdGlvIiB4c2k6dHlwZT0iTmFtZWRUeXBlU3BlY2lmaWVyIi8+CiAgICAgICAgIDwvb3BlcmFuZD4KICAgICAgPC9kZWY+CiAgICAgIDxkZWYgbG9jYXRvcj0iMjM6MS0yNzo2MyIgbmFtZT0iVG9JbnRlcnZhbCIgY29udGV4dD0iUGF0aWVudCIgYWNjZXNzTGV2ZWw9IlB1YmxpYyIgeHNpOnR5cGU9IkZ1bmN0aW9uRGVmIj4KICAgICAgICAgPGV4cHJlc3Npb24gbG9jYXRvcj0iMjQ6NS0yNzo2MyIgeHNpOnR5cGU9IklmIj4KICAgICAgICAgICAgPGNvbmRpdGlvbiBsb2NhdG9yPSIyNDo4LTI0OjIwIiB4c2k6dHlwZT0iSXNOdWxsIj4KICAgICAgICAgICAgICAgPG9wZXJhbmQgbG9jYXRvcj0iMjQ6OC0yNDoxMiIgbmFtZT0icmFuZ2UiIHhzaTp0eXBlPSJPcGVyYW5kUmVmIi8+CiAgICAgICAgICAgIDwvY29uZGl0aW9uPgogICAgICAgICAgICA8dGhlbiB4c2k6dHlwZT0iQXMiPgogICAgICAgICAgICAgICA8b3BlcmFuZCBsb2NhdG9yPSIyNTo5LTI1OjEyIiB4c2k6dHlwZT0iTnVsbCIvPgogICAgICAgICAgICAgICA8YXNUeXBlU3BlY2lmaWVyIHhzaTp0eXBlPSJJbnRlcnZhbFR5cGVTcGVjaWZpZXIiPgogICAgICAgICAgICAgICAgICA8cG9pbnRUeXBlIG5hbWU9InQ6UXVhbnRpdHkiIHhzaTp0eXBlPSJOYW1lZFR5cGVTcGVjaWZpZXIiLz4KICAgICAgICAgICAgICAgPC9hc1R5cGVTcGVjaWZpZXI+CiAgICAgICAgICAgIDwvdGhlbj4KICAgICAgICAgICAgPGVsc2UgbG9jYXRvcj0iMjc6OS0yNzo2MyIgbG93Q2xvc2VkPSJ0cnVlIiBoaWdoQ2xvc2VkPSJ0cnVlIiB4c2k6dHlwZT0iSW50ZXJ2YWwiPgogICAgICAgICAgICAgICA8bG93IGxvY2F0b3I9IjI3OjE4LTI3OjM4IiBuYW1lPSJUb1F1YW50aXR5IiB4c2k6dHlwZT0iRnVuY3Rpb25SZWYiPgogICAgICAgICAgICAgICAgICA8b3BlcmFuZCBsb2NhdG9yPSIyNzoyOS0yNzozNyIgcGF0aD0ibG93IiB4c2k6dHlwZT0iUHJvcGVydHkiPgogICAgICAgICAgICAgICAgICAgICA8c291cmNlIGxvY2F0b3I9IjI3OjI5LTI3OjMzIiBuYW1lPSJyYW5nZSIgeHNpOnR5cGU9Ik9wZXJhbmRSZWYiLz4KICAgICAgICAgICAgICAgICAgPC9vcGVyYW5kPgogICAgICAgICAgICAgICA8L2xvdz4KICAgICAgICAgICAgICAgPGhpZ2ggbG9jYXRvcj0iMjc6NDEtMjc6NjIiIG5hbWU9IlRvUXVhbnRpdHkiIHhzaTp0eXBlPSJGdW5jdGlvblJlZiI+CiAgICAgICAgICAgICAgICAgIDxvcGVyYW5kIGxvY2F0b3I9IjI3OjUyLTI3OjYxIiBwYXRoPSJoaWdoIiB4c2k6dHlwZT0iUHJvcGVydHkiPgogICAgICAgICAgICAgICAgICAgICA8c291cmNlIGxvY2F0b3I9IjI3OjUyLTI3OjU2IiBuYW1lPSJyYW5nZSIgeHNpOnR5cGU9Ik9wZXJhbmRSZWYiLz4KICAgICAgICAgICAgICAgICAgPC9vcGVyYW5kPgogICAgICAgICAgICAgICA8L2hpZ2g+CiAgICAgICAgICAgIDwvZWxzZT4KICAgICAgICAgPC9leHByZXNzaW9uPgogICAgICAgICA8b3BlcmFuZCBuYW1lPSJyYW5nZSI+CiAgICAgICAgICAgIDxvcGVyYW5kVHlwZVNwZWNpZmllciBsb2NhdG9yPSIyMzozNC0yMzo0MyIgbmFtZT0iZmhpcjpSYW5nZSIgeHNpOnR5cGU9Ik5hbWVkVHlwZVNwZWNpZmllciIvPgogICAgICAgICA8L29wZXJhbmQ+CiAgICAgIDwvZGVmPgogICAgICA8ZGVmIGxvY2F0b3I9IjI5OjEtMzg6OSIgbmFtZT0iVG9Db2RlIiBjb250ZXh0PSJQYXRpZW50IiBhY2Nlc3NMZXZlbD0iUHVibGljIiB4c2k6dHlwZT0iRnVuY3Rpb25EZWYiPgogICAgICAgICA8ZXhwcmVzc2lvbiBsb2NhdG9yPSIzMDo1LTM4OjkiIHhzaTp0eXBlPSJJZiI+CiAgICAgICAgICAgIDxjb25kaXRpb24gbG9jYXRvcj0iMzA6OC0zMDoyMSIgeHNpOnR5cGU9IklzTnVsbCI+CiAgICAgICAgICAgICAgIDxvcGVyYW5kIGxvY2F0b3I9IjMwOjgtMzA6MTMiIG5hbWU9ImNvZGluZyIgeHNpOnR5cGU9Ik9wZXJhbmRSZWYiLz4KICAgICAgICAgICAgPC9jb25kaXRpb24+CiAgICAgICAgICAgIDx0aGVuIGFzVHlwZT0idDpDb2RlIiB4c2k6dHlwZT0iQXMiPgogICAgICAgICAgICAgICA8b3BlcmFuZCBsb2NhdG9yPSIzMTo5LTMxOjEyIiB4c2k6dHlwZT0iTnVsbCIvPgogICAgICAgICAgICA8L3RoZW4+CiAgICAgICAgICAgIDxlbHNlIGxvY2F0b3I9IjMzOjktMzg6OSIgY2xhc3NUeXBlPSJ0OkNvZGUiIHhzaTp0eXBlPSJJbnN0YW5jZSI+CiAgICAgICAgICAgICAgIDxlbGVtZW50IG5hbWU9ImNvZGUiPgogICAgICAgICAgICAgICAgICA8dmFsdWUgbG9jYXRvcj0iMzQ6MTctMzQ6MzMiIHBhdGg9InZhbHVlIiB4c2k6dHlwZT0iUHJvcGVydHkiPgogICAgICAgICAgICAgICAgICAgICA8c291cmNlIGxvY2F0b3I9IjM0OjE3LTM0OjI3IiBwYXRoPSJjb2RlIiB4c2k6dHlwZT0iUHJvcGVydHkiPgogICAgICAgICAgICAgICAgICAgICAgICA8c291cmNlIGxvY2F0b3I9IjM0OjE3LTM0OjIyIiBuYW1lPSJjb2RpbmciIHhzaTp0eXBlPSJPcGVyYW5kUmVmIi8+CiAgICAgICAgICAgICAgICAgICAgIDwvc291cmNlPgogICAgICAgICAgICAgICAgICA8L3ZhbHVlPgogICAgICAgICAgICAgICA8L2VsZW1lbnQ+CiAgICAgICAgICAgICAgIDxlbGVtZW50IG5hbWU9InN5c3RlbSI+CiAgICAgICAgICAgICAgICAgIDx2YWx1ZSBsb2NhdG9yPSIzNToxOS0zNTozNyIgcGF0aD0idmFsdWUiIHhzaTp0eXBlPSJQcm9wZXJ0eSI+CiAgICAgICAgICAgICAgICAgICAgIDxzb3VyY2UgbG9jYXRvcj0iMzU6MTktMzU6MzEiIHBhdGg9InN5c3RlbSIgeHNpOnR5cGU9IlByb3BlcnR5Ij4KICAgICAgICAgICAgICAgICAgICAgICAgPHNvdXJjZSBsb2NhdG9yPSIzNToxOS0zNToyNCIgbmFtZT0iY29kaW5nIiB4c2k6dHlwZT0iT3BlcmFuZFJlZiIvPgogICAgICAgICAgICAgICAgICAgICA8L3NvdXJjZT4KICAgICAgICAgICAgICAgICAgPC92YWx1ZT4KICAgICAgICAgICAgICAgPC9lbGVtZW50PgogICAgICAgICAgICAgICA8ZWxlbWVudCBuYW1lPSJ2ZXJzaW9uIj4KICAgICAgICAgICAgICAgICAgPHZhbHVlIGxvY2F0b3I9IjM2OjIwLTM2OjM5IiBwYXRoPSJ2YWx1ZSIgeHNpOnR5cGU9IlByb3BlcnR5Ij4KICAgICAgICAgICAgICAgICAgICAgPHNvdXJjZSBsb2NhdG9yPSIzNjoyMC0zNjozMyIgcGF0aD0idmVyc2lvbiIgeHNpOnR5cGU9IlByb3BlcnR5Ij4KICAgICAgICAgICAgICAgICAgICAgICAgPHNvdXJjZSBsb2NhdG9yPSIzNjoyMC0zNjoyNSIgbmFtZT0iY29kaW5nIiB4c2k6dHlwZT0iT3BlcmFuZFJlZiIvPgogICAgICAgICAgICAgICAgICAgICA8L3NvdXJjZT4KICAgICAgICAgICAgICAgICAgPC92YWx1ZT4KICAgICAgICAgICAgICAgPC9lbGVtZW50PgogICAgICAgICAgICAgICA8ZWxlbWVudCBuYW1lPSJkaXNwbGF5Ij4KICAgICAgICAgICAgICAgICAgPHZhbHVlIGxvY2F0b3I9IjM3OjIwLTM3OjM5IiBwYXRoPSJ2YWx1ZSIgeHNpOnR5cGU9IlByb3BlcnR5Ij4KICAgICAgICAgICAgICAgICAgICAgPHNvdXJjZSBsb2NhdG9yPSIzNzoyMC0zNzozMyIgcGF0aD0iZGlzcGxheSIgeHNpOnR5cGU9IlByb3BlcnR5Ij4KICAgICAgICAgICAgICAgICAgICAgICAgPHNvdXJjZSBsb2NhdG9yPSIzNzoyMC0zNzoyNSIgbmFtZT0iY29kaW5nIiB4c2k6dHlwZT0iT3BlcmFuZFJlZiIvPgogICAgICAgICAgICAgICAgICAgICA8L3NvdXJjZT4KICAgICAgICAgICAgICAgICAgPC92YWx1ZT4KICAgICAgICAgICAgICAgPC9lbGVtZW50PgogICAgICAgICAgICA8L2Vsc2U+CiAgICAgICAgIDwvZXhwcmVzc2lvbj4KICAgICAgICAgPG9wZXJhbmQgbmFtZT0iY29kaW5nIj4KICAgICAgICAgICAgPG9wZXJhbmRUeXBlU3BlY2lmaWVyIGxvY2F0b3I9IjI5OjMxLTI5OjQxIiBuYW1lPSJmaGlyOkNvZGluZyIgeHNpOnR5cGU9Ik5hbWVkVHlwZVNwZWNpZmllciIvPgogICAgICAgICA8L29wZXJhbmQ+CiAgICAgIDwvZGVmPgogICAgICA8ZGVmIGxvY2F0b3I9IjQwOjEtNDc6OSIgbmFtZT0iVG9Db25jZXB0IiBjb250ZXh0PSJQYXRpZW50IiBhY2Nlc3NMZXZlbD0iUHVibGljIiB4c2k6dHlwZT0iRnVuY3Rpb25EZWYiPgogICAgICAgICA8ZXhwcmVzc2lvbiBsb2NhdG9yPSI0MTo1LTQ3OjkiIHhzaTp0eXBlPSJJZiI+CiAgICAgICAgICAgIDxjb25kaXRpb24gbG9jYXRvcj0iNDE6OC00MToyMiIgeHNpOnR5cGU9IklzTnVsbCI+CiAgICAgICAgICAgICAgIDxvcGVyYW5kIGxvY2F0b3I9IjQxOjgtNDE6MTQiIG5hbWU9ImNvbmNlcHQiIHhzaTp0eXBlPSJPcGVyYW5kUmVmIi8+CiAgICAgICAgICAgIDwvY29uZGl0aW9uPgogICAgICAgICAgICA8dGhlbiBhc1R5cGU9InQ6Q29uY2VwdCIgeHNpOnR5cGU9IkFzIj4KICAgICAgICAgICAgICAgPG9wZXJhbmQgbG9jYXRvcj0iNDI6OS00MjoxMiIgeHNpOnR5cGU9Ik51bGwiLz4KICAgICAgICAgICAgPC90aGVuPgogICAgICAgICAgICA8ZWxzZSBsb2NhdG9yPSI0NDo5LTQ3OjkiIGNsYXNzVHlwZT0idDpDb25jZXB0IiB4c2k6dHlwZT0iSW5zdGFuY2UiPgogICAgICAgICAgICAgICA8ZWxlbWVudCBuYW1lPSJjb2RlcyI+CiAgICAgICAgICAgICAgICAgIDx2YWx1ZSBsb2NhdG9yPSI0NToyMC00NTo1MiIgeHNpOnR5cGU9IlF1ZXJ5Ij4KICAgICAgICAgICAgICAgICAgICAgPHNvdXJjZSBsb2NhdG9yPSI0NToyMC00NTozNSIgYWxpYXM9IkMiPgogICAgICAgICAgICAgICAgICAgICAgICA8ZXhwcmVzc2lvbiBsb2NhdG9yPSI0NToyMC00NTozMyIgcGF0aD0iY29kaW5nIiB4c2k6dHlwZT0iUHJvcGVydHkiPgogICAgICAgICAgICAgICAgICAgICAgICAgICA8c291cmNlIG5hbWU9ImNvbmNlcHQiIHhzaTp0eXBlPSJPcGVyYW5kUmVmIi8+CiAgICAgICAgICAgICAgICAgICAgICAgIDwvZXhwcmVzc2lvbj4KICAgICAgICAgICAgICAgICAgICAgPC9zb3VyY2U+CiAgICAgICAgICAgICAgICAgICAgIDxyZXR1cm4gbG9jYXRvcj0iNDU6MzctNDU6NTIiPgogICAgICAgICAgICAgICAgICAgICAgICA8ZXhwcmVzc2lvbiBsb2NhdG9yPSI0NTo0NC00NTo1MiIgbmFtZT0iVG9Db2RlIiB4c2k6dHlwZT0iRnVuY3Rpb25SZWYiPgogICAgICAgICAgICAgICAgICAgICAgICAgICA8b3BlcmFuZCBsb2NhdG9yPSI0NTo1MSIgbmFtZT0iQyIgeHNpOnR5cGU9IkFsaWFzUmVmIi8+CiAgICAgICAgICAgICAgICAgICAgICAgIDwvZXhwcmVzc2lvbj4KICAgICAgICAgICAgICAgICAgICAgPC9yZXR1cm4+CiAgICAgICAgICAgICAgICAgIDwvdmFsdWU+CiAgICAgICAgICAgICAgIDwvZWxlbWVudD4KICAgICAgICAgICAgICAgPGVsZW1lbnQgbmFtZT0iZGlzcGxheSI+CiAgICAgICAgICAgICAgICAgIDx2YWx1ZSBsb2NhdG9yPSI0NjoyMi00NjozOSIgcGF0aD0idmFsdWUiIHhzaTp0eXBlPSJQcm9wZXJ0eSI+CiAgICAgICAgICAgICAgICAgICAgIDxzb3VyY2UgbG9jYXRvcj0iNDY6MjItNDY6MzMiIHBhdGg9InRleHQiIHhzaTp0eXBlPSJQcm9wZXJ0eSI+CiAgICAgICAgICAgICAgICAgICAgICAgIDxzb3VyY2UgbG9jYXRvcj0iNDY6MjItNDY6MjgiIG5hbWU9ImNvbmNlcHQiIHhzaTp0eXBlPSJPcGVyYW5kUmVmIi8+CiAgICAgICAgICAgICAgICAgICAgIDwvc291cmNlPgogICAgICAgICAgICAgICAgICA8L3ZhbHVlPgogICAgICAgICAgICAgICA8L2VsZW1lbnQ+CiAgICAgICAgICAgIDwvZWxzZT4KICAgICAgICAgPC9leHByZXNzaW9uPgogICAgICAgICA8b3BlcmFuZCBuYW1lPSJjb25jZXB0Ij4KICAgICAgICAgICAgPG9wZXJhbmRUeXBlU3BlY2lmaWVyIGxvY2F0b3I9IjQwOjM1LTQwOjU0IiBuYW1lPSJmaGlyOkNvZGVhYmxlQ29uY2VwdCIgeHNpOnR5cGU9Ik5hbWVkVHlwZVNwZWNpZmllciIvPgogICAgICAgICA8L29wZXJhbmQ+CiAgICAgIDwvZGVmPgogICAgICA8ZGVmIGxvY2F0b3I9IjUwOjEtNTA6NTgiIG5hbWU9IlRvU3RyaW5nIiBjb250ZXh0PSJQYXRpZW50IiBhY2Nlc3NMZXZlbD0iUHVibGljIiB4c2k6dHlwZT0iRnVuY3Rpb25EZWYiPgogICAgICAgICA8ZXhwcmVzc2lvbiBsb2NhdG9yPSI1MDo0OC01MDo1OCIgcGF0aD0idmFsdWUiIHhzaTp0eXBlPSJQcm9wZXJ0eSI+CiAgICAgICAgICAgIDxzb3VyY2UgbG9jYXRvcj0iNTA6NDgtNTA6NTIiIG5hbWU9InZhbHVlIiB4c2k6dHlwZT0iT3BlcmFuZFJlZiIvPgogICAgICAgICA8L2V4cHJlc3Npb24+CiAgICAgICAgIDxvcGVyYW5kIG5hbWU9InZhbHVlIj4KICAgICAgICAgICAgPG9wZXJhbmRUeXBlU3BlY2lmaWVyIGxvY2F0b3I9IjUwOjMyLTUwOjQ0IiBuYW1lPSJmaGlyOkFjY291bnRTdGF0dXMiIHhzaTp0eXBlPSJOYW1lZFR5cGVTcGVjaWZpZXIiLz4KICAgICAgICAgPC9vcGVyYW5kPgogICAgICA8L2RlZj4KICAgICAgPGRlZiBsb2NhdG9yPSI1MToxLTUxOjcwIiBuYW1lPSJUb1N0cmluZyIgY29udGV4dD0iUGF0aWVudCIgYWNjZXNzTGV2ZWw9IlB1YmxpYyIgeHNpOnR5cGU9IkZ1bmN0aW9uRGVmIj4KICAgICAgICAgPGV4cHJlc3Npb24gbG9jYXRvcj0iNTE6NjAtNTE6NzAiIHBhdGg9InZhbHVlIiB4c2k6dHlwZT0iUHJvcGVydHkiPgogICAgICAgICAgICA8c291cmNlIGxvY2F0b3I9IjUxOjYwLTUxOjY0IiBuYW1lPSJ2YWx1ZSIgeHNpOnR5cGU9Ik9wZXJhbmRSZWYiLz4KICAgICAgICAgPC9leHByZXNzaW9uPgogICAgICAgICA8b3BlcmFuZCBuYW1lPSJ2YWx1ZSI+CiAgICAgICAgICAgIDxvcGVyYW5kVHlwZVNwZWNpZmllciBsb2NhdG9yPSI1MTozMi01MTo1NiIgbmFtZT0iZmhpcjpBY3Rpb25DYXJkaW5hbGl0eUJlaGF2aW9yIiB4c2k6dHlwZT0iTmFtZWRUeXBlU3BlY2lmaWVyIi8+CiAgICAgICAgIDwvb3BlcmFuZD4KICAgICAgPC9kZWY+CiAgICAgIDxkZWYgbG9jYXRvcj0iNTI6MS01Mjo2NCIgbmFtZT0iVG9TdHJpbmciIGNvbnRleHQ9IlBhdGllbnQiIGFjY2Vzc0xldmVsPSJQdWJsaWMiIHhzaTp0eXBlPSJGdW5jdGlvbkRlZiI+CiAgICAgICAgIDxleHByZXNzaW9uIGxvY2F0b3I9IjUyOjU0LTUyOjY0IiBwYXRoPSJ2YWx1ZSIgeHNpOnR5cGU9IlByb3BlcnR5Ij4KICAgICAgICAgICAgPHNvdXJjZSBsb2NhdG9yPSI1Mjo1NC01Mjo1OCIgbmFtZT0idmFsdWUiIHhzaTp0eXBlPSJPcGVyYW5kUmVmIi8+CiAgICAgICAgIDwvZXhwcmVzc2lvbj4KICAgICAgICAgPG9wZXJhbmQgbmFtZT0idmFsdWUiPgogICAgICAgICAgICA8b3BlcmFuZFR5cGVTcGVjaWZpZXIgbG9jYXRvcj0iNTI6MzItNTI6NTAiIG5hbWU9ImZoaXI6QWN0aW9uQ29uZGl0aW9uS2luZCIgeHNpOnR5cGU9Ik5hbWVkVHlwZVNwZWNpZmllciIvPgogICAgICAgICA8L29wZXJhbmQ+CiAgICAgIDwvZGVmPgogICAgICA8ZGVmIGxvY2F0b3I9IjUzOjEtNTM6NjciIG5hbWU9IlRvU3RyaW5nIiBjb250ZXh0PSJQYXRpZW50IiBhY2Nlc3NMZXZlbD0iUHVibGljIiB4c2k6dHlwZT0iRnVuY3Rpb25EZWYiPgogICAgICAgICA8ZXhwcmVzc2lvbiBsb2NhdG9yPSI1Mzo1Ny01Mzo2NyIgcGF0aD0idmFsdWUiIHhzaTp0eXBlPSJQcm9wZXJ0eSI+CiAgICAgICAgICAgIDxzb3VyY2UgbG9jYXRvcj0iNTM6NTctNTM6NjEiIG5hbWU9InZhbHVlIiB4c2k6dHlwZT0iT3BlcmFuZFJlZiIvPgogICAgICAgICA8L2V4cHJlc3Npb24+CiAgICAgICAgIDxvcGVyYW5kIG5hbWU9InZhbHVlIj4KICAgICAgICAgICAgPG9wZXJhbmRUeXBlU3BlY2lmaWVyIGxvY2F0b3I9IjUzOjMyLTUzOjUzIiBuYW1lPSJmaGlyOkFjdGlvbkdyb3VwaW5nQmVoYXZpb3IiIHhzaTp0eXBlPSJOYW1lZFR5cGVTcGVjaWZpZXIiLz4KICAgICAgICAgPC9vcGVyYW5kPgogICAgICA8L2RlZj4KICAgICAgPGRlZiBsb2NhdG9yPSI1NDoxLTU0OjY2IiBuYW1lPSJUb1N0cmluZyIgY29udGV4dD0iUGF0aWVudCIgYWNjZXNzTGV2ZWw9IlB1YmxpYyIgeHNpOnR5cGU9IkZ1bmN0aW9uRGVmIj4KICAgICAgICAgPGV4cHJlc3Npb24gbG9jYXRvcj0iNTQ6NTYtNTQ6NjYiIHBhdGg9InZhbHVlIiB4c2k6dHlwZT0iUHJvcGVydHkiPgogICAgICAgICAgICA8c291cmNlIGxvY2F0b3I9IjU0OjU2LTU0OjYwIiBuYW1lPSJ2YWx1ZSIgeHNpOnR5cGU9Ik9wZXJhbmRSZWYiLz4KICAgICAgICAgPC9leHByZXNzaW9uPgogICAgICAgICA8b3BlcmFuZCBuYW1lPSJ2YWx1ZSI+CiAgICAgICAgICAgIDxvcGVyYW5kVHlwZVNwZWNpZmllciBsb2NhdG9yPSI1NDozMi01NDo1MiIgbmFtZT0iZmhpcjpBY3Rpb25QYXJ0aWNpcGFudFR5cGUiIHhzaTp0eXBlPSJOYW1lZFR5cGVTcGVjaWZpZXIiLz4KICAgICAgICAgPC9vcGVyYW5kPgogICAgICA8L2RlZj4KICAgICAgPGRlZiBsb2NhdG9yPSI1NToxLTU1OjY3IiBuYW1lPSJUb1N0cmluZyIgY29udGV4dD0iUGF0aWVudCIgYWNjZXNzTGV2ZWw9IlB1YmxpYyIgeHNpOnR5cGU9IkZ1bmN0aW9uRGVmIj4KICAgICAgICAgPGV4cHJlc3Npb24gbG9jYXRvcj0iNTU6NTctNTU6NjciIHBhdGg9InZhbHVlIiB4c2k6dHlwZT0iUHJvcGVydHkiPgogICAgICAgICAgICA8c291cmNlIGxvY2F0b3I9IjU1OjU3LTU1OjYxIiBuYW1lPSJ2YWx1ZSIgeHNpOnR5cGU9Ik9wZXJhbmRSZWYiLz4KICAgICAgICAgPC9leHByZXNzaW9uPgogICAgICAgICA8b3BlcmFuZCBuYW1lPSJ2YWx1ZSI+CiAgICAgICAgICAgIDxvcGVyYW5kVHlwZVNwZWNpZmllciBsb2NhdG9yPSI1NTozMi01NTo1MyIgbmFtZT0iZmhpcjpBY3Rpb25QcmVjaGVja0JlaGF2aW9yIiB4c2k6dHlwZT0iTmFtZWRUeXBlU3BlY2lmaWVyIi8+CiAgICAgICAgIDwvb3BlcmFuZD4KICAgICAgPC9kZWY+CiAgICAgIDxkZWYgbG9jYXRvcj0iNTY6MS01Njo2NyIgbmFtZT0iVG9TdHJpbmciIGNvbnRleHQ9IlBhdGllbnQiIGFjY2Vzc0xldmVsPSJQdWJsaWMiIHhzaTp0eXBlPSJGdW5jdGlvbkRlZiI+CiAgICAgICAgIDxleHByZXNzaW9uIGxvY2F0b3I9IjU2OjU3LTU2OjY3IiBwYXRoPSJ2YWx1ZSIgeHNpOnR5cGU9IlByb3BlcnR5Ij4KICAgICAgICAgICAgPHNvdXJjZSBsb2NhdG9yPSI1Njo1Ny01Njo2MSIgbmFtZT0idmFsdWUiIHhzaTp0eXBlPSJPcGVyYW5kUmVmIi8+CiAgICAgICAgIDwvZXhwcmVzc2lvbj4KICAgICAgICAgPG9wZXJhbmQgbmFtZT0idmFsdWUiPgogICAgICAgICAgICA8b3BlcmFuZFR5cGVTcGVjaWZpZXIgbG9jYXRvcj0iNTY6MzItNTY6NTMiIG5hbWU9ImZoaXI6QWN0aW9uUmVsYXRpb25zaGlwVHlwZSIgeHNpOnR5cGU9Ik5hbWVkVHlwZVNwZWNpZmllciIvPgogICAgICAgICA8L29wZXJhbmQ+CiAgICAgIDwvZGVmPgogICAgICA8ZGVmIGxvY2F0b3I9IjU3OjEtNTc6NjciIG5hbWU9IlRvU3RyaW5nIiBjb250ZXh0PSJQYXRpZW50IiBhY2Nlc3NMZXZlbD0iUHVibGljIiB4c2k6dHlwZT0iRnVuY3Rpb25EZWYiPgogICAgICAgICA8ZXhwcmVzc2lvbiBsb2NhdG9yPSI1Nzo1Ny01Nzo2NyIgcGF0aD0idmFsdWUiIHhzaTp0eXBlPSJQcm9wZXJ0eSI+CiAgICAgICAgICAgIDxzb3VyY2UgbG9jYXRvcj0iNTc6NTctNTc6NjEiIG5hbWU9InZhbHVlIiB4c2k6dHlwZT0iT3BlcmFuZFJlZiIvPgogICAgICAgICA8L2V4cHJlc3Npb24+CiAgICAgICAgIDxvcGVyYW5kIG5hbWU9InZhbHVlIj4KICAgICAgICAgICAgPG9wZXJhbmRUeXBlU3BlY2lmaWVyIGxvY2F0b3I9IjU3OjMyLTU3OjUzIiBuYW1lPSJmaGlyOkFjdGlvblJlcXVpcmVkQmVoYXZpb3IiIHhzaTp0eXBlPSJOYW1lZFR5cGVTcGVjaWZpZXIiLz4KICAgICAgICAgPC9vcGVyYW5kPgogICAgICA8L2RlZj4KICAgICAgPGRlZiBsb2NhdG9yPSI1ODoxLTU4OjY4IiBuYW1lPSJUb1N0cmluZyIgY29udGV4dD0iUGF0aWVudCIgYWNjZXNzTGV2ZWw9IlB1YmxpYyIgeHNpOnR5cGU9IkZ1bmN0aW9uRGVmIj4KICAgICAgICAgPGV4cHJlc3Npb24gbG9jYXRvcj0iNTg6NTgtNTg6NjgiIHBhdGg9InZhbHVlIiB4c2k6dHlwZT0iUHJvcGVydHkiPgogICAgICAgICAgICA8c291cmNlIGxvY2F0b3I9IjU4OjU4LTU4OjYyIiBuYW1lPSJ2YWx1ZSIgeHNpOnR5cGU9Ik9wZXJhbmRSZWYiLz4KICAgICAgICAgPC9leHByZXNzaW9uPgogICAgICAgICA8b3BlcmFuZCBuYW1lPSJ2YWx1ZSI+CiAgICAgICAgICAgIDxvcGVyYW5kVHlwZVNwZWNpZmllciBsb2NhdG9yPSI1ODozMi01ODo1NCIgbmFtZT0iZmhpcjpBY3Rpb25TZWxlY3Rpb25CZWhhdmlvciIgeHNpOnR5cGU9Ik5hbWVkVHlwZVNwZWNpZmllciIvPgogICAgICAgICA8L29wZXJhbmQ+CiAgICAgIDwvZGVmPgogICAgICA8ZGVmIGxvY2F0b3I9IjU5OjEtNTk6NjciIG5hbWU9IlRvU3RyaW5nIiBjb250ZXh0PSJQYXRpZW50IiBhY2Nlc3NMZXZlbD0iUHVibGljIiB4c2k6dHlwZT0iRnVuY3Rpb25EZWYiPgogICAgICAgICA8ZXhwcmVzc2lvbiBsb2NhdG9yPSI1OTo1Ny01OTo2NyIgcGF0aD0idmFsdWUiIHhzaTp0eXBlPSJQcm9wZXJ0eSI+CiAgICAgICAgICAgIDxzb3VyY2UgbG9jYXRvcj0iNTk6NTctNTk6NjEiIG5hbWU9InZhbHVlIiB4c2k6dHlwZT0iT3BlcmFuZFJlZiIvPgogICAgICAgICA8L2V4cHJlc3Npb24+CiAgICAgICAgIDxvcGVyYW5kIG5hbWU9InZhbHVlIj4KICAgICAgICAgICAgPG9wZXJhbmRUeXBlU3BlY2lmaWVyIGxvY2F0b3I9IjU5OjMyLTU5OjUzIiBuYW1lPSJmaGlyOkFjdGl2aXR5RGVmaW5pdGlvbktpbmQiIHhzaTp0eXBlPSJOYW1lZFR5cGVTcGVjaWZpZXIiLz4KICAgICAgICAgPC9vcGVyYW5kPgogICAgICA8L2RlZj4KICAgICAgPGRlZiBsb2NhdG9yPSI2MDoxLTYwOjY4IiBuYW1lPSJUb1N0cmluZyIgY29udGV4dD0iUGF0aWVudCIgYWNjZXNzTGV2ZWw9IlB1YmxpYyIgeHNpOnR5cGU9IkZ1bmN0aW9uRGVmIj4KICAgICAgICAgPGV4cHJlc3Npb24gbG9jYXRvcj0iNjA6NTgtNjA6NjgiIHBhdGg9InZhbHVlIiB4c2k6dHlwZT0iUHJvcGVydHkiPgogICAgICAgICAgICA8c291cmNlIGxvY2F0b3I9IjYwOjU4LTYwOjYyIiBuYW1lPSJ2YWx1ZSIgeHNpOnR5cGU9Ik9wZXJhbmRSZWYiLz4KICAgICAgICAgPC9leHByZXNzaW9uPgogICAgICAgICA8b3BlcmFuZCBuYW1lPSJ2YWx1ZSI+CiAgICAgICAgICAgIDxvcGVyYW5kVHlwZVNwZWNpZmllciBsb2NhdG9yPSI2MDozMi02MDo1NCIgbmFtZT0iZmhpcjpBY3Rpdml0eVBhcnRpY2lwYW50VHlwZSIgeHNpOnR5cGU9Ik5hbWVkVHlwZVNwZWNpZmllciIvPgogICAgICAgICA8L29wZXJhbmQ+CiAgICAgIDwvZGVmPgogICAgICA8ZGVmIGxvY2F0b3I9IjYxOjEtNjE6NTYiIG5hbWU9IlRvU3RyaW5nIiBjb250ZXh0PSJQYXRpZW50IiBhY2Nlc3NMZXZlbD0iUHVibGljIiB4c2k6dHlwZT0iRnVuY3Rpb25EZWYiPgogICAgICAgICA8ZXhwcmVzc2lvbiBsb2NhdG9yPSI2MTo0Ni02MTo1NiIgcGF0aD0idmFsdWUiIHhzaTp0eXBlPSJQcm9wZXJ0eSI+CiAgICAgICAgICAgIDxzb3VyY2UgbG9jYXRvcj0iNjE6NDYtNjE6NTAiIG5hbWU9InZhbHVlIiB4c2k6dHlwZT0iT3BlcmFuZFJlZiIvPgogICAgICAgICA8L2V4cHJlc3Npb24+CiAgICAgICAgIDxvcGVyYW5kIG5hbWU9InZhbHVlIj4KICAgICAgICAgICAgPG9wZXJhbmRUeXBlU3BlY2lmaWVyIGxvY2F0b3I9IjYxOjMyLTYxOjQyIiBuYW1lPSJmaGlyOkFkZHJlc3NUeXBlIiB4c2k6dHlwZT0iTmFtZWRUeXBlU3BlY2lmaWVyIi8+CiAgICAgICAgIDwvb3BlcmFuZD4KICAgICAgPC9kZWY+CiAgICAgIDxkZWYgbG9jYXRvcj0iNjI6MS02Mjo1NSIgbmFtZT0iVG9TdHJpbmciIGNvbnRleHQ9IlBhdGllbnQiIGFjY2Vzc0xldmVsPSJQdWJsaWMiIHhzaTp0eXBlPSJGdW5jdGlvbkRlZiI+CiAgICAgICAgIDxleHByZXNzaW9uIGxvY2F0b3I9IjYyOjQ1LTYyOjU1IiBwYXRoPSJ2YWx1ZSIgeHNpOnR5cGU9IlByb3BlcnR5Ij4KICAgICAgICAgICAgPHNvdXJjZSBsb2NhdG9yPSI2Mjo0NS02Mjo0OSIgbmFtZT0idmFsdWUiIHhzaTp0eXBlPSJPcGVyYW5kUmVmIi8+CiAgICAgICAgIDwvZXhwcmVzc2lvbj4KICAgICAgICAgPG9wZXJhbmQgbmFtZT0idmFsdWUiPgogICAgICAgICAgICA8b3BlcmFuZFR5cGVTcGVjaWZpZXIgbG9jYXRvcj0iNjI6MzItNjI6NDEiIG5hbWU9ImZoaXI6QWRkcmVzc1VzZSIgeHNpOnR5cGU9Ik5hbWVkVHlwZVNwZWNpZmllciIvPgogICAgICAgICA8L29wZXJhbmQ+CiAgICAgIDwvZGVmPgogICAgICA8ZGVmIGxvY2F0b3I9IjYzOjEtNjM6NjUiIG5hbWU9IlRvU3RyaW5nIiBjb250ZXh0PSJQYXRpZW50IiBhY2Nlc3NMZXZlbD0iUHVibGljIiB4c2k6dHlwZT0iRnVuY3Rpb25EZWYiPgogICAgICAgICA8ZXhwcmVzc2lvbiBsb2NhdG9yPSI2Mzo1NS02Mzo2NSIgcGF0aD0idmFsdWUiIHhzaTp0eXBlPSJQcm9wZXJ0eSI+CiAgICAgICAgICAgIDxzb3VyY2UgbG9jYXRvcj0iNjM6NTUtNjM6NTkiIG5hbWU9InZhbHVlIiB4c2k6dHlwZT0iT3BlcmFuZFJlZiIvPgogICAgICAgICA8L2V4cHJlc3Npb24+CiAgICAgICAgIDxvcGVyYW5kIG5hbWU9InZhbHVlIj4KICAgICAgICAgICAgPG9wZXJhbmRUeXBlU3BlY2lmaWVyIGxvY2F0b3I9IjYzOjMyLTYzOjUxIiBuYW1lPSJmaGlyOkFkbWluaXN0cmF0aXZlR2VuZGVyIiB4c2k6dHlwZT0iTmFtZWRUeXBlU3BlY2lmaWVyIi8+CiAgICAgICAgIDwvb3BlcmFuZD4KICAgICAgPC9kZWY+CiAgICAgIDxkZWYgbG9jYXRvcj0iNjQ6MS02NDo2NiIgbmFtZT0iVG9TdHJpbmciIGNvbnRleHQ9IlBhdGllbnQiIGFjY2Vzc0xldmVsPSJQdWJsaWMiIHhzaTp0eXBlPSJGdW5jdGlvbkRlZiI+CiAgICAgICAgIDxleHByZXNzaW9uIGxvY2F0b3I9IjY0OjU2LTY0OjY2IiBwYXRoPSJ2YWx1ZSIgeHNpOnR5cGU9IlByb3BlcnR5Ij4KICAgICAgICAgICAgPHNvdXJjZSBsb2NhdG9yPSI2NDo1Ni02NDo2MCIgbmFtZT0idmFsdWUiIHhzaTp0eXBlPSJPcGVyYW5kUmVmIi8+CiAgICAgICAgIDwvZXhwcmVzc2lvbj4KICAgICAgICAgPG9wZXJhbmQgbmFtZT0idmFsdWUiPgogICAgICAgICAgICA8b3BlcmFuZFR5cGVTcGVjaWZpZXIgbG9jYXRvcj0iNjQ6MzItNjQ6NTIiIG5hbWU9ImZoaXI6QWR2ZXJzZUV2ZW50QWN0dWFsaXR5IiB4c2k6dHlwZT0iTmFtZWRUeXBlU3BlY2lmaWVyIi8+CiAgICAgICAgIDwvb3BlcmFuZD4KICAgICAgPC9kZWY+CiAgICAgIDxkZWYgbG9jYXRvcj0iNjU6MS02NTo2MCIgbmFtZT0iVG9TdHJpbmciIGNvbnRleHQ9IlBhdGllbnQiIGFjY2Vzc0xldmVsPSJQdWJsaWMiIHhzaTp0eXBlPSJGdW5jdGlvbkRlZiI+CiAgICAgICAgIDxleHByZXNzaW9uIGxvY2F0b3I9IjY1OjUwLTY1OjYwIiBwYXRoPSJ2YWx1ZSIgeHNpOnR5cGU9IlByb3BlcnR5Ij4KICAgICAgICAgICAgPHNvdXJjZSBsb2NhdG9yPSI2NTo1MC02NTo1NCIgbmFtZT0idmFsdWUiIHhzaTp0eXBlPSJPcGVyYW5kUmVmIi8+CiAgICAgICAgIDwvZXhwcmVzc2lvbj4KICAgICAgICAgPG9wZXJhbmQgbmFtZT0idmFsdWUiPgogICAgICAgICAgICA8b3BlcmFuZFR5cGVTcGVjaWZpZXIgbG9jYXRvcj0iNjU6MzItNjU6NDYiIG5hbWU9ImZoaXI6QWdncmVnYXRpb25Nb2RlIiB4c2k6dHlwZT0iTmFtZWRUeXBlU3BlY2lmaWVyIi8+CiAgICAgICAgIDwvb3BlcmFuZD4KICAgICAgPC9kZWY+CiAgICAgIDxkZWYgbG9jYXRvcj0iNjY6MS02Njo3MSIgbmFtZT0iVG9TdHJpbmciIGNvbnRleHQ9IlBhdGllbnQiIGFjY2Vzc0xldmVsPSJQdWJsaWMiIHhzaTp0eXBlPSJGdW5jdGlvbkRlZiI+CiAgICAgICAgIDxleHByZXNzaW9uIGxvY2F0b3I9IjY2OjYxLTY2OjcxIiBwYXRoPSJ2YWx1ZSIgeHNpOnR5cGU9IlByb3BlcnR5Ij4KICAgICAgICAgICAgPHNvdXJjZSBsb2NhdG9yPSI2Njo2MS02Njo2NSIgbmFtZT0idmFsdWUiIHhzaTp0eXBlPSJPcGVyYW5kUmVmIi8+CiAgICAgICAgIDwvZXhwcmVzc2lvbj4KICAgICAgICAgPG9wZXJhbmQgbmFtZT0idmFsdWUiPgogICAgICAgICAgICA8b3BlcmFuZFR5cGVTcGVjaWZpZXIgbG9jYXRvcj0iNjY6MzItNjY6NTciIG5hbWU9ImZoaXI6QWxsZXJneUludG9sZXJhbmNlQ2F0ZWdvcnkiIHhzaTp0eXBlPSJOYW1lZFR5cGVTcGVjaWZpZXIiLz4KICAgICAgICAgPC9vcGVyYW5kPgogICAgICA8L2RlZj4KICAgICAgPGRlZiBsb2NhdG9yPSI2NzoxLTY3Ojc0IiBuYW1lPSJUb1N0cmluZyIgY29udGV4dD0iUGF0aWVudCIgYWNjZXNzTGV2ZWw9IlB1YmxpYyIgeHNpOnR5cGU9IkZ1bmN0aW9uRGVmIj4KICAgICAgICAgPGV4cHJlc3Npb24gbG9jYXRvcj0iNjc6NjQtNjc6NzQiIHBhdGg9InZhbHVlIiB4c2k6dHlwZT0iUHJvcGVydHkiPgogICAgICAgICAgICA8c291cmNlIGxvY2F0b3I9IjY3OjY0LTY3OjY4IiBuYW1lPSJ2YWx1ZSIgeHNpOnR5cGU9Ik9wZXJhbmRSZWYiLz4KICAgICAgICAgPC9leHByZXNzaW9uPgogICAgICAgICA8b3BlcmFuZCBuYW1lPSJ2YWx1ZSI+CiAgICAgICAgICAgIDxvcGVyYW5kVHlwZVNwZWNpZmllciBsb2NhdG9yPSI2NzozMi02Nzo2MCIgbmFtZT0iZmhpcjpBbGxlcmd5SW50b2xlcmFuY2VDcml0aWNhbGl0eSIgeHNpOnR5cGU9Ik5hbWVkVHlwZVNwZWNpZmllciIvPgogICAgICAgICA8L29wZXJhbmQ+CiAgICAgIDwvZGVmPgogICAgICA8ZGVmIGxvY2F0b3I9IjY4OjEtNjg6NzEiIG5hbWU9IlRvU3RyaW5nIiBjb250ZXh0PSJQYXRpZW50IiBhY2Nlc3NMZXZlbD0iUHVibGljIiB4c2k6dHlwZT0iRnVuY3Rpb25EZWYiPgogICAgICAgICA8ZXhwcmVzc2lvbiBsb2NhdG9yPSI2ODo2MS02ODo3MSIgcGF0aD0idmFsdWUiIHhzaTp0eXBlPSJQcm9wZXJ0eSI+CiAgICAgICAgICAgIDxzb3VyY2UgbG9jYXRvcj0iNjg6NjEtNjg6NjUiIG5hbWU9InZhbHVlIiB4c2k6dHlwZT0iT3BlcmFuZFJlZiIvPgogICAgICAgICA8L2V4cHJlc3Npb24+CiAgICAgICAgIDxvcGVyYW5kIG5hbWU9InZhbHVlIj4KICAgICAgICAgICAgPG9wZXJhbmRUeXBlU3BlY2lmaWVyIGxvY2F0b3I9IjY4OjMyLTY4OjU3IiBuYW1lPSJmaGlyOkFsbGVyZ3lJbnRvbGVyYW5jZVNldmVyaXR5IiB4c2k6dHlwZT0iTmFtZWRUeXBlU3BlY2lmaWVyIi8+CiAgICAgICAgIDwvb3BlcmFuZD4KICAgICAgPC9kZWY+CiAgICAgIDxkZWYgbG9jYXRvcj0iNjk6MS02OTo2NyIgbmFtZT0iVG9TdHJpbmciIGNvbnRleHQ9IlBhdGllbnQiIGFjY2Vzc0xldmVsPSJQdWJsaWMiIHhzaTp0eXBlPSJGdW5jdGlvbkRlZiI+CiAgICAgICAgIDxleHByZXNzaW9uIGxvY2F0b3I9IjY5OjU3LTY5OjY3IiBwYXRoPSJ2YWx1ZSIgeHNpOnR5cGU9IlByb3BlcnR5Ij4KICAgICAgICAgICAgPHNvdXJjZSBsb2NhdG9yPSI2OTo1Ny02OTo2MSIgbmFtZT0idmFsdWUiIHhzaTp0eXBlPSJPcGVyYW5kUmVmIi8+CiAgICAgICAgIDwvZXhwcmVzc2lvbj4KICAgICAgICAgPG9wZXJhbmQgbmFtZT0idmFsdWUiPgogICAgICAgICAgICA8b3BlcmFuZFR5cGVTcGVjaWZpZXIgbG9jYXRvcj0iNjk6MzItNjk6NTMiIG5hbWU9ImZoaXI6QWxsZXJneUludG9sZXJhbmNlVHlwZSIgeHNpOnR5cGU9Ik5hbWVkVHlwZVNwZWNpZmllciIvPgogICAgICAgICA8L29wZXJhbmQ+CiAgICAgIDwvZGVmPgogICAgICA8ZGVmIGxvY2F0b3I9IjcwOjEtNzA6NjIiIG5hbWU9IlRvU3RyaW5nIiBjb250ZXh0PSJQYXRpZW50IiBhY2Nlc3NMZXZlbD0iUHVibGljIiB4c2k6dHlwZT0iRnVuY3Rpb25EZWYiPgogICAgICAgICA8ZXhwcmVzc2lvbiBsb2NhdG9yPSI3MDo1Mi03MDo2MiIgcGF0aD0idmFsdWUiIHhzaTp0eXBlPSJQcm9wZXJ0eSI+CiAgICAgICAgICAgIDxzb3VyY2UgbG9jYXRvcj0iNzA6NTItNzA6NTYiIG5hbWU9InZhbHVlIiB4c2k6dHlwZT0iT3BlcmFuZFJlZiIvPgogICAgICAgICA8L2V4cHJlc3Npb24+CiAgICAgICAgIDxvcGVyYW5kIG5hbWU9InZhbHVlIj4KICAgICAgICAgICAgPG9wZXJhbmRUeXBlU3BlY2lmaWVyIGxvY2F0b3I9IjcwOjMyLTcwOjQ4IiBuYW1lPSJmaGlyOkFwcG9pbnRtZW50U3RhdHVzIiB4c2k6dHlwZT0iTmFtZWRUeXBlU3BlY2lmaWVyIi8+CiAgICAgICAgIDwvb3BlcmFuZD4KICAgICAgPC9kZWY+CiAgICAgIDxkZWYgbG9jYXRvcj0iNzE6MS03MTo2NyIgbmFtZT0iVG9TdHJpbmciIGNvbnRleHQ9IlBhdGllbnQiIGFjY2Vzc0xldmVsPSJQdWJsaWMiIHhzaTp0eXBlPSJGdW5jdGlvbkRlZiI+CiAgICAgICAgIDxleHByZXNzaW9uIGxvY2F0b3I9IjcxOjU3LTcxOjY3IiBwYXRoPSJ2YWx1ZSIgeHNpOnR5cGU9IlByb3BlcnR5Ij4KICAgICAgICAgICAgPHNvdXJjZSBsb2NhdG9yPSI3MTo1Ny03MTo2MSIgbmFtZT0idmFsdWUiIHhzaTp0eXBlPSJPcGVyYW5kUmVmIi8+CiAgICAgICAgIDwvZXhwcmVzc2lvbj4KICAgICAgICAgPG9wZXJhbmQgbmFtZT0idmFsdWUiPgogICAgICAgICAgICA8b3BlcmFuZFR5cGVTcGVjaWZpZXIgbG9jYXRvcj0iNzE6MzItNzE6NTMiIG5hbWU9ImZoaXI6QXNzZXJ0aW9uRGlyZWN0aW9uVHlwZSIgeHNpOnR5cGU9Ik5hbWVkVHlwZVNwZWNpZmllciIvPgogICAgICAgICA8L29wZXJhbmQ+CiAgICAgIDwvZGVmPgogICAgICA8ZGVmIGxvY2F0b3I9IjcyOjEtNzI6NjYiIG5hbWU9IlRvU3RyaW5nIiBjb250ZXh0PSJQYXRpZW50IiBhY2Nlc3NMZXZlbD0iUHVibGljIiB4c2k6dHlwZT0iRnVuY3Rpb25EZWYiPgogICAgICAgICA8ZXhwcmVzc2lvbiBsb2NhdG9yPSI3Mjo1Ni03Mjo2NiIgcGF0aD0idmFsdWUiIHhzaTp0eXBlPSJQcm9wZXJ0eSI+CiAgICAgICAgICAgIDxzb3VyY2UgbG9jYXRvcj0iNzI6NTYtNzI6NjAiIG5hbWU9InZhbHVlIiB4c2k6dHlwZT0iT3BlcmFuZFJlZiIvPgogICAgICAgICA8L2V4cHJlc3Npb24+CiAgICAgICAgIDxvcGVyYW5kIG5hbWU9InZhbHVlIj4KICAgICAgICAgICAgPG9wZXJhbmRUeXBlU3BlY2lmaWVyIGxvY2F0b3I9IjcyOjMyLTcyOjUyIiBuYW1lPSJmaGlyOkFzc2VydGlvbk9wZXJhdG9yVHlwZSIgeHNpOnR5cGU9Ik5hbWVkVHlwZVNwZWNpZmllciIvPgogICAgICAgICA8L29wZXJhbmQ+CiAgICAgIDwvZGVmPgogICAgICA8ZGVmIGxvY2F0b3I9IjczOjEtNzM6NjciIG5hbWU9IlRvU3RyaW5nIiBjb250ZXh0PSJQYXRpZW50IiBhY2Nlc3NMZXZlbD0iUHVibGljIiB4c2k6dHlwZT0iRnVuY3Rpb25EZWYiPgogICAgICAgICA8ZXhwcmVzc2lvbiBsb2NhdG9yPSI3Mzo1Ny03Mzo2NyIgcGF0aD0idmFsdWUiIHhzaTp0eXBlPSJQcm9wZXJ0eSI+CiAgICAgICAgICAgIDxzb3VyY2UgbG9jYXRvcj0iNzM6NTctNzM6NjEiIG5hbWU9InZhbHVlIiB4c2k6dHlwZT0iT3BlcmFuZFJlZiIvPgogICAgICAgICA8L2V4cHJlc3Npb24+CiAgICAgICAgIDxvcGVyYW5kIG5hbWU9InZhbHVlIj4KICAgICAgICAgICAgPG9wZXJhbmRUeXBlU3BlY2lmaWVyIGxvY2F0b3I9IjczOjMyLTczOjUzIiBuYW1lPSJmaGlyOkFzc2VydGlvblJlc3BvbnNlVHlwZXMiIHhzaTp0eXBlPSJOYW1lZFR5cGVTcGVjaWZpZXIiLz4KICAgICAgICAgPC9vcGVyYW5kPgogICAgICA8L2RlZj4KICAgICAgPGRlZiBsb2NhdG9yPSI3NDoxLTc0OjYxIiBuYW1lPSJUb1N0cmluZyIgY29udGV4dD0iUGF0aWVudCIgYWNjZXNzTGV2ZWw9IlB1YmxpYyIgeHNpOnR5cGU9IkZ1bmN0aW9uRGVmIj4KICAgICAgICAgPGV4cHJlc3Npb24gbG9jYXRvcj0iNzQ6NTEtNzQ6NjEiIHBhdGg9InZhbHVlIiB4c2k6dHlwZT0iUHJvcGVydHkiPgogICAgICAgICAgICA8c291cmNlIGxvY2F0b3I9Ijc0OjUxLTc0OjU1IiBuYW1lPSJ2YWx1ZSIgeHNpOnR5cGU9Ik9wZXJhbmRSZWYiLz4KICAgICAgICAgPC9leHByZXNzaW9uPgogICAgICAgICA8b3BlcmFuZCBuYW1lPSJ2YWx1ZSI+CiAgICAgICAgICAgIDxvcGVyYW5kVHlwZVNwZWNpZmllciBsb2NhdG9yPSI3NDozMi03NDo0NyIgbmFtZT0iZmhpcjpBdWRpdEV2ZW50QWN0aW9uIiB4c2k6dHlwZT0iTmFtZWRUeXBlU3BlY2lmaWVyIi8+CiAgICAgICAgIDwvb3BlcmFuZD4KICAgICAgPC9kZWY+CiAgICAgIDxkZWYgbG9jYXRvcj0iNzU6MS03NTo3MSIgbmFtZT0iVG9TdHJpbmciIGNvbnRleHQ9IlBhdGllbnQiIGFjY2Vzc0xldmVsPSJQdWJsaWMiIHhzaTp0eXBlPSJGdW5jdGlvbkRlZiI+CiAgICAgICAgIDxleHByZXNzaW9uIGxvY2F0b3I9Ijc1OjYxLTc1OjcxIiBwYXRoPSJ2YWx1ZSIgeHNpOnR5cGU9IlByb3BlcnR5Ij4KICAgICAgICAgICAgPHNvdXJjZSBsb2NhdG9yPSI3NTo2MS03NTo2NSIgbmFtZT0idmFsdWUiIHhzaTp0eXBlPSJPcGVyYW5kUmVmIi8+CiAgICAgICAgIDwvZXhwcmVzc2lvbj4KICAgICAgICAgPG9wZXJhbmQgbmFtZT0idmFsdWUiPgogICAgICAgICAgICA8b3BlcmFuZFR5cGVTcGVjaWZpZXIgbG9jYXRvcj0iNzU6MzItNzU6NTciIG5hbWU9ImZoaXI6QXVkaXRFdmVudEFnZW50TmV0d29ya1R5cGUiIHhzaTp0eXBlPSJOYW1lZFR5cGVTcGVjaWZpZXIiLz4KICAgICAgICAgPC9vcGVyYW5kPgogICAgICA8L2RlZj4KICAgICAgPGRlZiBsb2NhdG9yPSI3NjoxLTc2OjYyIiBuYW1lPSJUb1N0cmluZyIgY29udGV4dD0iUGF0aWVudCIgYWNjZXNzTGV2ZWw9IlB1YmxpYyIgeHNpOnR5cGU9IkZ1bmN0aW9uRGVmIj4KICAgICAgICAgPGV4cHJlc3Npb24gbG9jYXRvcj0iNzY6NTItNzY6NjIiIHBhdGg9InZhbHVlIiB4c2k6dHlwZT0iUHJvcGVydHkiPgogICAgICAgICAgICA8c291cmNlIGxvY2F0b3I9Ijc2OjUyLTc2OjU2IiBuYW1lPSJ2YWx1ZSIgeHNpOnR5cGU9Ik9wZXJhbmRSZWYiLz4KICAgICAgICAgPC9leHByZXNzaW9uPgogICAgICAgICA8b3BlcmFuZCBuYW1lPSJ2YWx1ZSI+CiAgICAgICAgICAgIDxvcGVyYW5kVHlwZVNwZWNpZmllciBsb2NhdG9yPSI3NjozMi03Njo0OCIgbmFtZT0iZmhpcjpBdWRpdEV2ZW50T3V0Y29tZSIgeHNpOnR5cGU9Ik5hbWVkVHlwZVNwZWNpZmllciIvPgogICAgICAgICA8L29wZXJhbmQ+CiAgICAgIDwvZGVmPgogICAgICA8ZGVmIGxvY2F0b3I9Ijc3OjEtNzc6NjAiIG5hbWU9IlRvU3RyaW5nIiBjb250ZXh0PSJQYXRpZW50IiBhY2Nlc3NMZXZlbD0iUHVibGljIiB4c2k6dHlwZT0iRnVuY3Rpb25EZWYiPgogICAgICAgICA8ZXhwcmVzc2lvbiBsb2NhdG9yPSI3Nzo1MC03Nzo2MCIgcGF0aD0idmFsdWUiIHhzaTp0eXBlPSJQcm9wZXJ0eSI+CiAgICAgICAgICAgIDxzb3VyY2UgbG9jYXRvcj0iNzc6NTAtNzc6NTQiIG5hbWU9InZhbHVlIiB4c2k6dHlwZT0iT3BlcmFuZFJlZiIvPgogICAgICAgICA8L2V4cHJlc3Npb24+CiAgICAgICAgIDxvcGVyYW5kIG5hbWU9InZhbHVlIj4KICAgICAgICAgICAgPG9wZXJhbmRUeXBlU3BlY2lmaWVyIGxvY2F0b3I9Ijc3OjMyLTc3OjQ2IiBuYW1lPSJmaGlyOkJpbmRpbmdTdHJlbmd0aCIgeHNpOnR5cGU9Ik5hbWVkVHlwZVNwZWNpZmllciIvPgogICAgICAgICA8L29wZXJhbmQ+CiAgICAgIDwvZGVmPgogICAgICA8ZGVmIGxvY2F0b3I9Ijc4OjEtNzg6NzkiIG5hbWU9IlRvU3RyaW5nIiBjb250ZXh0PSJQYXRpZW50IiBhY2Nlc3NMZXZlbD0iUHVibGljIiB4c2k6dHlwZT0iRnVuY3Rpb25EZWYiPgogICAgICAgICA8ZXhwcmVzc2lvbiBsb2NhdG9yPSI3ODo2OS03ODo3OSIgcGF0aD0idmFsdWUiIHhzaTp0eXBlPSJQcm9wZXJ0eSI+CiAgICAgICAgICAgIDxzb3VyY2UgbG9jYXRvcj0iNzg6NjktNzg6NzMiIG5hbWU9InZhbHVlIiB4c2k6dHlwZT0iT3BlcmFuZFJlZiIvPgogICAgICAgICA8L2V4cHJlc3Npb24+CiAgICAgICAgIDxvcGVyYW5kIG5hbWU9InZhbHVlIj4KICAgICAgICAgICAgPG9wZXJhbmRUeXBlU3BlY2lmaWVyIGxvY2F0b3I9Ijc4OjMyLTc4OjY1IiBuYW1lPSJmaGlyOkJpb2xvZ2ljYWxseURlcml2ZWRQcm9kdWN0Q2F0ZWdvcnkiIHhzaTp0eXBlPSJOYW1lZFR5cGVTcGVjaWZpZXIiLz4KICAgICAgICAgPC9vcGVyYW5kPgogICAgICA8L2RlZj4KICAgICAgPGRlZiBsb2NhdG9yPSI3OToxLTc5Ojc3IiBuYW1lPSJUb1N0cmluZyIgY29udGV4dD0iUGF0aWVudCIgYWNjZXNzTGV2ZWw9IlB1YmxpYyIgeHNpOnR5cGU9IkZ1bmN0aW9uRGVmIj4KICAgICAgICAgPGV4cHJlc3Npb24gbG9jYXRvcj0iNzk6NjctNzk6NzciIHBhdGg9InZhbHVlIiB4c2k6dHlwZT0iUHJvcGVydHkiPgogICAgICAgICAgICA8c291cmNlIGxvY2F0b3I9Ijc5OjY3LTc5OjcxIiBuYW1lPSJ2YWx1ZSIgeHNpOnR5cGU9Ik9wZXJhbmRSZWYiLz4KICAgICAgICAgPC9leHByZXNzaW9uPgogICAgICAgICA8b3BlcmFuZCBuYW1lPSJ2YWx1ZSI+CiAgICAgICAgICAgIDxvcGVyYW5kVHlwZVNwZWNpZmllciBsb2NhdG9yPSI3OTozMi03OTo2MyIgbmFtZT0iZmhpcjpCaW9sb2dpY2FsbHlEZXJpdmVkUHJvZHVjdFN0YXR1cyIgeHNpOnR5cGU9Ik5hbWVkVHlwZVNwZWNpZmllciIvPgogICAgICAgICA8L29wZXJhbmQ+CiAgICAgIDwvZGVmPgogICAgICA8ZGVmIGxvY2F0b3I9IjgwOjEtODA6ODMiIG5hbWU9IlRvU3RyaW5nIiBjb250ZXh0PSJQYXRpZW50IiBhY2Nlc3NMZXZlbD0iUHVibGljIiB4c2k6dHlwZT0iRnVuY3Rpb25EZWYiPgogICAgICAgICA8ZXhwcmVzc2lvbiBsb2NhdG9yPSI4MDo3My04MDo4MyIgcGF0aD0idmFsdWUiIHhzaTp0eXBlPSJQcm9wZXJ0eSI+CiAgICAgICAgICAgIDxzb3VyY2UgbG9jYXRvcj0iODA6NzMtODA6NzciIG5hbWU9InZhbHVlIiB4c2k6dHlwZT0iT3BlcmFuZFJlZiIvPgogICAgICAgICA8L2V4cHJlc3Npb24+CiAgICAgICAgIDxvcGVyYW5kIG5hbWU9InZhbHVlIj4KICAgICAgICAgICAgPG9wZXJhbmRUeXBlU3BlY2lmaWVyIGxvY2F0b3I9IjgwOjMyLTgwOjY5IiBuYW1lPSJmaGlyOkJpb2xvZ2ljYWxseURlcml2ZWRQcm9kdWN0U3RvcmFnZVNjYWxlIiB4c2k6dHlwZT0iTmFtZWRUeXBlU3BlY2lmaWVyIi8+CiAgICAgICAgIDwvb3BlcmFuZD4KICAgICAgPC9kZWY+CiAgICAgIDxkZWYgbG9jYXRvcj0iODE6MS04MTo1NSIgbmFtZT0iVG9TdHJpbmciIGNvbnRleHQ9IlBhdGllbnQiIGFjY2Vzc0xldmVsPSJQdWJsaWMiIHhzaTp0eXBlPSJGdW5jdGlvbkRlZiI+CiAgICAgICAgIDxleHByZXNzaW9uIGxvY2F0b3I9IjgxOjQ1LTgxOjU1IiBwYXRoPSJ2YWx1ZSIgeHNpOnR5cGU9IlByb3BlcnR5Ij4KICAgICAgICAgICAgPHNvdXJjZSBsb2NhdG9yPSI4MTo0NS04MTo0OSIgbmFtZT0idmFsdWUiIHhzaTp0eXBlPSJPcGVyYW5kUmVmIi8+CiAgICAgICAgIDwvZXhwcmVzc2lvbj4KICAgICAgICAgPG9wZXJhbmQgbmFtZT0idmFsdWUiPgogICAgICAgICAgICA8b3BlcmFuZFR5cGVTcGVjaWZpZXIgbG9jYXRvcj0iODE6MzItODE6NDEiIG5hbWU9ImZoaXI6QnVuZGxlVHlwZSIgeHNpOnR5cGU9Ik5hbWVkVHlwZVNwZWNpZmllciIvPgogICAgICAgICA8L29wZXJhbmQ+CiAgICAgIDwvZGVmPgogICAgICA8ZGVmIGxvY2F0b3I9IjgyOjEtODI6NjgiIG5hbWU9IlRvU3RyaW5nIiBjb250ZXh0PSJQYXRpZW50IiBhY2Nlc3NMZXZlbD0iUHVibGljIiB4c2k6dHlwZT0iRnVuY3Rpb25EZWYiPgogICAgICAgICA8ZXhwcmVzc2lvbiBsb2NhdG9yPSI4Mjo1OC04Mjo2OCIgcGF0aD0idmFsdWUiIHhzaTp0eXBlPSJQcm9wZXJ0eSI+CiAgICAgICAgICAgIDxzb3VyY2UgbG9jYXRvcj0iODI6NTgtODI6NjIiIG5hbWU9InZhbHVlIiB4c2k6dHlwZT0iT3BlcmFuZFJlZiIvPgogICAgICAgICA8L2V4cHJlc3Npb24+CiAgICAgICAgIDxvcGVyYW5kIG5hbWU9InZhbHVlIj4KICAgICAgICAgICAgPG9wZXJhbmRUeXBlU3BlY2lmaWVyIGxvY2F0b3I9IjgyOjMyLTgyOjU0IiBuYW1lPSJmaGlyOkNhcGFiaWxpdHlTdGF0ZW1lbnRLaW5kIiB4c2k6dHlwZT0iTmFtZWRUeXBlU3BlY2lmaWVyIi8+CiAgICAgICAgIDwvb3BlcmFuZD4KICAgICAgPC9kZWY+CiAgICAgIDxkZWYgbG9jYXRvcj0iODM6MS04Mzo2NSIgbmFtZT0iVG9TdHJpbmciIGNvbnRleHQ9IlBhdGllbnQiIGFjY2Vzc0xldmVsPSJQdWJsaWMiIHhzaTp0eXBlPSJGdW5jdGlvbkRlZiI+CiAgICAgICAgIDxleHByZXNzaW9uIGxvY2F0b3I9IjgzOjU1LTgzOjY1IiBwYXRoPSJ2YWx1ZSIgeHNpOnR5cGU9IlByb3BlcnR5Ij4KICAgICAgICAgICAgPHNvdXJjZSBsb2NhdG9yPSI4Mzo1NS04Mzo1OSIgbmFtZT0idmFsdWUiIHhzaTp0eXBlPSJPcGVyYW5kUmVmIi8+CiAgICAgICAgIDwvZXhwcmVzc2lvbj4KICAgICAgICAgPG9wZXJhbmQgbmFtZT0idmFsdWUiPgogICAgICAgICAgICA8b3BlcmFuZFR5cGVTcGVjaWZpZXIgbG9jYXRvcj0iODM6MzItODM6NTEiIG5hbWU9ImZoaXI6Q2FyZVBsYW5BY3Rpdml0eUtpbmQiIHhzaTp0eXBlPSJOYW1lZFR5cGVTcGVjaWZpZXIiLz4KICAgICAgICAgPC9vcGVyYW5kPgogICAgICA8L2RlZj4KICAgICAgPGRlZiBsb2NhdG9yPSI4NDoxLTg0OjY3IiBuYW1lPSJUb1N0cmluZyIgY29udGV4dD0iUGF0aWVudCIgYWNjZXNzTGV2ZWw9IlB1YmxpYyIgeHNpOnR5cGU9IkZ1bmN0aW9uRGVmIj4KICAgICAgICAgPGV4cHJlc3Npb24gbG9jYXRvcj0iODQ6NTctODQ6NjciIHBhdGg9InZhbHVlIiB4c2k6dHlwZT0iUHJvcGVydHkiPgogICAgICAgICAgICA8c291cmNlIGxvY2F0b3I9Ijg0OjU3LTg0OjYxIiBuYW1lPSJ2YWx1ZSIgeHNpOnR5cGU9Ik9wZXJhbmRSZWYiLz4KICAgICAgICAgPC9leHByZXNzaW9uPgogICAgICAgICA8b3BlcmFuZCBuYW1lPSJ2YWx1ZSI+CiAgICAgICAgICAgIDxvcGVyYW5kVHlwZVNwZWNpZmllciBsb2NhdG9yPSI4NDozMi04NDo1MyIgbmFtZT0iZmhpcjpDYXJlUGxhbkFjdGl2aXR5U3RhdHVzIiB4c2k6dHlwZT0iTmFtZWRUeXBlU3BlY2lmaWVyIi8+CiAgICAgICAgIDwvb3BlcmFuZD4KICAgICAgPC9kZWY+CiAgICAgIDxkZWYgbG9jYXRvcj0iODU6MS04NTo1OSIgbmFtZT0iVG9TdHJpbmciIGNvbnRleHQ9IlBhdGllbnQiIGFjY2Vzc0xldmVsPSJQdWJsaWMiIHhzaTp0eXBlPSJGdW5jdGlvbkRlZiI+CiAgICAgICAgIDxleHByZXNzaW9uIGxvY2F0b3I9Ijg1OjQ5LTg1OjU5IiBwYXRoPSJ2YWx1ZSIgeHNpOnR5cGU9IlByb3BlcnR5Ij4KICAgICAgICAgICAgPHNvdXJjZSBsb2NhdG9yPSI4NTo0OS04NTo1MyIgbmFtZT0idmFsdWUiIHhzaTp0eXBlPSJPcGVyYW5kUmVmIi8+CiAgICAgICAgIDwvZXhwcmVzc2lvbj4KICAgICAgICAgPG9wZXJhbmQgbmFtZT0idmFsdWUiPgogICAgICAgICAgICA8b3BlcmFuZFR5cGVTcGVjaWZpZXIgbG9jYXRvcj0iODU6MzItODU6NDUiIG5hbWU9ImZoaXI6Q2FyZVBsYW5JbnRlbnQiIHhzaTp0eXBlPSJOYW1lZFR5cGVTcGVjaWZpZXIiLz4KICAgICAgICAgPC9vcGVyYW5kPgogICAgICA8L2RlZj4KICAgICAgPGRlZiBsb2NhdG9yPSI4NjoxLTg2OjU5IiBuYW1lPSJUb1N0cmluZyIgY29udGV4dD0iUGF0aWVudCIgYWNjZXNzTGV2ZWw9IlB1YmxpYyIgeHNpOnR5cGU9IkZ1bmN0aW9uRGVmIj4KICAgICAgICAgPGV4cHJlc3Npb24gbG9jYXRvcj0iODY6NDktODY6NTkiIHBhdGg9InZhbHVlIiB4c2k6dHlwZT0iUHJvcGVydHkiPgogICAgICAgICAgICA8c291cmNlIGxvY2F0b3I9Ijg2OjQ5LTg2OjUzIiBuYW1lPSJ2YWx1ZSIgeHNpOnR5cGU9Ik9wZXJhbmRSZWYiLz4KICAgICAgICAgPC9leHByZXNzaW9uPgogICAgICAgICA8b3BlcmFuZCBuYW1lPSJ2YWx1ZSI+CiAgICAgICAgICAgIDxvcGVyYW5kVHlwZVNwZWNpZmllciBsb2NhdG9yPSI4NjozMi04Njo0NSIgbmFtZT0iZmhpcjpDYXJlUGxhblN0YXR1cyIgeHNpOnR5cGU9Ik5hbWVkVHlwZVNwZWNpZmllciIvPgogICAgICAgICA8L29wZXJhbmQ+CiAgICAgIDwvZGVmPgogICAgICA8ZGVmIGxvY2F0b3I9Ijg3OjEtODc6NTkiIG5hbWU9IlRvU3RyaW5nIiBjb250ZXh0PSJQYXRpZW50IiBhY2Nlc3NMZXZlbD0iUHVibGljIiB4c2k6dHlwZT0iRnVuY3Rpb25EZWYiPgogICAgICAgICA8ZXhwcmVzc2lvbiBsb2NhdG9yPSI4Nzo0OS04Nzo1OSIgcGF0aD0idmFsdWUiIHhzaTp0eXBlPSJQcm9wZXJ0eSI+CiAgICAgICAgICAgIDxzb3VyY2UgbG9jYXRvcj0iODc6NDktODc6NTMiIG5hbWU9InZhbHVlIiB4c2k6dHlwZT0iT3BlcmFuZFJlZiIvPgogICAgICAgICA8L2V4cHJlc3Npb24+CiAgICAgICAgIDxvcGVyYW5kIG5hbWU9InZhbHVlIj4KICAgICAgICAgICAgPG9wZXJhbmRUeXBlU3BlY2lmaWVyIGxvY2F0b3I9Ijg3OjMyLTg3OjQ1IiBuYW1lPSJmaGlyOkNhcmVUZWFtU3RhdHVzIiB4c2k6dHlwZT0iTmFtZWRUeXBlU3BlY2lmaWVyIi8+CiAgICAgICAgIDwvb3BlcmFuZD4KICAgICAgPC9kZWY+CiAgICAgIDxkZWYgbG9jYXRvcj0iODg6MS04ODo2OSIgbmFtZT0iVG9TdHJpbmciIGNvbnRleHQ9IlBhdGllbnQiIGFjY2Vzc0xldmVsPSJQdWJsaWMiIHhzaTp0eXBlPSJGdW5jdGlvbkRlZiI+CiAgICAgICAgIDxleHByZXNzaW9uIGxvY2F0b3I9Ijg4OjU5LTg4OjY5IiBwYXRoPSJ2YWx1ZSIgeHNpOnR5cGU9IlByb3BlcnR5Ij4KICAgICAgICAgICAgPHNvdXJjZSBsb2NhdG9yPSI4ODo1OS04ODo2MyIgbmFtZT0idmFsdWUiIHhzaTp0eXBlPSJPcGVyYW5kUmVmIi8+CiAgICAgICAgIDwvZXhwcmVzc2lvbj4KICAgICAgICAgPG9wZXJhbmQgbmFtZT0idmFsdWUiPgogICAgICAgICAgICA8b3BlcmFuZFR5cGVTcGVjaWZpZXIgbG9jYXRvcj0iODg6MzItODg6NTUiIG5hbWU9ImZoaXI6Q2F0YWxvZ0VudHJ5UmVsYXRpb25UeXBlIiB4c2k6dHlwZT0iTmFtZWRUeXBlU3BlY2lmaWVyIi8+CiAgICAgICAgIDwvb3BlcmFuZD4KICAgICAgPC9kZWY+CiAgICAgIDxkZWYgbG9jYXRvcj0iODk6MS04OTo4MyIgbmFtZT0iVG9TdHJpbmciIGNvbnRleHQ9IlBhdGllbnQiIGFjY2Vzc0xldmVsPSJQdWJsaWMiIHhzaTp0eXBlPSJGdW5jdGlvbkRlZiI+CiAgICAgICAgIDxleHByZXNzaW9uIGxvY2F0b3I9Ijg5OjczLTg5OjgzIiBwYXRoPSJ2YWx1ZSIgeHNpOnR5cGU9IlByb3BlcnR5Ij4KICAgICAgICAgICAgPHNvdXJjZSBsb2NhdG9yPSI4OTo3My04OTo3NyIgbmFtZT0idmFsdWUiIHhzaTp0eXBlPSJPcGVyYW5kUmVmIi8+CiAgICAgICAgIDwvZXhwcmVzc2lvbj4KICAgICAgICAgPG9wZXJhbmQgbmFtZT0idmFsdWUiPgogICAgICAgICAgICA8b3BlcmFuZFR5cGVTcGVjaWZpZXIgbG9jYXRvcj0iODk6MzItODk6NjkiIG5hbWU9ImZoaXI6Q2hhcmdlSXRlbURlZmluaXRpb25QcmljZUNvbXBvbmVudFR5cGUiIHhzaTp0eXBlPSJOYW1lZFR5cGVTcGVjaWZpZXIiLz4KICAgICAgICAgPC9vcGVyYW5kPgogICAgICA8L2RlZj4KICAgICAgPGRlZiBsb2NhdG9yPSI5MDoxLTkwOjYxIiBuYW1lPSJUb1N0cmluZyIgY29udGV4dD0iUGF0aWVudCIgYWNjZXNzTGV2ZWw9IlB1YmxpYyIgeHNpOnR5cGU9IkZ1bmN0aW9uRGVmIj4KICAgICAgICAgPGV4cHJlc3Npb24gbG9jYXRvcj0iOTA6NTEtOTA6NjEiIHBhdGg9InZhbHVlIiB4c2k6dHlwZT0iUHJvcGVydHkiPgogICAgICAgICAgICA8c291cmNlIGxvY2F0b3I9IjkwOjUxLTkwOjU1IiBuYW1lPSJ2YWx1ZSIgeHNpOnR5cGU9Ik9wZXJhbmRSZWYiLz4KICAgICAgICAgPC9leHByZXNzaW9uPgogICAgICAgICA8b3BlcmFuZCBuYW1lPSJ2YWx1ZSI+CiAgICAgICAgICAgIDxvcGVyYW5kVHlwZVNwZWNpZmllciBsb2NhdG9yPSI5MDozMi05MDo0NyIgbmFtZT0iZmhpcjpDaGFyZ2VJdGVtU3RhdHVzIiB4c2k6dHlwZT0iTmFtZWRUeXBlU3BlY2lmaWVyIi8+CiAgICAgICAgIDwvb3BlcmFuZD4KICAgICAgPC9kZWY+CiAgICAgIDxkZWYgbG9jYXRvcj0iOTE6MS05MTo2NCIgbmFtZT0iVG9TdHJpbmciIGNvbnRleHQ9IlBhdGllbnQiIGFjY2Vzc0xldmVsPSJQdWJsaWMiIHhzaTp0eXBlPSJGdW5jdGlvbkRlZiI+CiAgICAgICAgIDxleHByZXNzaW9uIGxvY2F0b3I9IjkxOjU0LTkxOjY0IiBwYXRoPSJ2YWx1ZSIgeHNpOnR5cGU9IlByb3BlcnR5Ij4KICAgICAgICAgICAgPHNvdXJjZSBsb2NhdG9yPSI5MTo1NC05MTo1OCIgbmFtZT0idmFsdWUiIHhzaTp0eXBlPSJPcGVyYW5kUmVmIi8+CiAgICAgICAgIDwvZXhwcmVzc2lvbj4KICAgICAgICAgPG9wZXJhbmQgbmFtZT0idmFsdWUiPgogICAgICAgICAgICA8b3BlcmFuZFR5cGVTcGVjaWZpZXIgbG9jYXRvcj0iOTE6MzItOTE6NTAiIG5hbWU9ImZoaXI6Q2xhaW1SZXNwb25zZVN0YXR1cyIgeHNpOnR5cGU9Ik5hbWVkVHlwZVNwZWNpZmllciIvPgogICAgICAgICA8L29wZXJhbmQ+CiAgICAgIDwvZGVmPgogICAgICA8ZGVmIGxvY2F0b3I9IjkyOjEtOTI6NTYiIG5hbWU9IlRvU3RyaW5nIiBjb250ZXh0PSJQYXRpZW50IiBhY2Nlc3NMZXZlbD0iUHVibGljIiB4c2k6dHlwZT0iRnVuY3Rpb25EZWYiPgogICAgICAgICA8ZXhwcmVzc2lvbiBsb2NhdG9yPSI5Mjo0Ni05Mjo1NiIgcGF0aD0idmFsdWUiIHhzaTp0eXBlPSJQcm9wZXJ0eSI+CiAgICAgICAgICAgIDxzb3VyY2UgbG9jYXRvcj0iOTI6NDYtOTI6NTAiIG5hbWU9InZhbHVlIiB4c2k6dHlwZT0iT3BlcmFuZFJlZiIvPgogICAgICAgICA8L2V4cHJlc3Npb24+CiAgICAgICAgIDxvcGVyYW5kIG5hbWU9InZhbHVlIj4KICAgICAgICAgICAgPG9wZXJhbmRUeXBlU3BlY2lmaWVyIGxvY2F0b3I9IjkyOjMyLTkyOjQyIiBuYW1lPSJmaGlyOkNsYWltU3RhdHVzIiB4c2k6dHlwZT0iTmFtZWRUeXBlU3BlY2lmaWVyIi8+CiAgICAgICAgIDwvb3BlcmFuZD4KICAgICAgPC9kZWY+CiAgICAgIDxkZWYgbG9jYXRvcj0iOTM6MS05Mzo2OSIgbmFtZT0iVG9TdHJpbmciIGNvbnRleHQ9IlBhdGllbnQiIGFjY2Vzc0xldmVsPSJQdWJsaWMiIHhzaTp0eXBlPSJGdW5jdGlvbkRlZiI+CiAgICAgICAgIDxleHByZXNzaW9uIGxvY2F0b3I9IjkzOjU5LTkzOjY5IiBwYXRoPSJ2YWx1ZSIgeHNpOnR5cGU9IlByb3BlcnR5Ij4KICAgICAgICAgICAgPHNvdXJjZSBsb2NhdG9yPSI5Mzo1OS05Mzo2MyIgbmFtZT0idmFsdWUiIHhzaTp0eXBlPSJPcGVyYW5kUmVmIi8+CiAgICAgICAgIDwvZXhwcmVzc2lvbj4KICAgICAgICAgPG9wZXJhbmQgbmFtZT0idmFsdWUiPgogICAgICAgICAgICA8b3BlcmFuZFR5cGVTcGVjaWZpZXIgbG9jYXRvcj0iOTM6MzItOTM6NTUiIG5hbWU9ImZoaXI6Q2xpbmljYWxJbXByZXNzaW9uU3RhdHVzIiB4c2k6dHlwZT0iTmFtZWRUeXBlU3BlY2lmaWVyIi8+CiAgICAgICAgIDwvb3BlcmFuZD4KICAgICAgPC9kZWY+CiAgICAgIDxkZWYgbG9jYXRvcj0iOTQ6MS05NDo2MiIgbmFtZT0iVG9TdHJpbmciIGNvbnRleHQ9IlBhdGllbnQiIGFjY2Vzc0xldmVsPSJQdWJsaWMiIHhzaTp0eXBlPSJGdW5jdGlvbkRlZiI+CiAgICAgICAgIDxleHByZXNzaW9uIGxvY2F0b3I9Ijk0OjUyLTk0OjYyIiBwYXRoPSJ2YWx1ZSIgeHNpOnR5cGU9IlByb3BlcnR5Ij4KICAgICAgICAgICAgPHNvdXJjZSBsb2NhdG9yPSI5NDo1Mi05NDo1NiIgbmFtZT0idmFsdWUiIHhzaTp0eXBlPSJPcGVyYW5kUmVmIi8+CiAgICAgICAgIDwvZXhwcmVzc2lvbj4KICAgICAgICAgPG9wZXJhbmQgbmFtZT0idmFsdWUiPgogICAgICAgICAgICA8b3BlcmFuZFR5cGVTcGVjaWZpZXIgbG9jYXRvcj0iOTQ6MzItOTQ6NDgiIG5hbWU9ImZoaXI6Q29kZVNlYXJjaFN1cHBvcnQiIHhzaTp0eXBlPSJOYW1lZFR5cGVTcGVjaWZpZXIiLz4KICAgICAgICAgPC9vcGVyYW5kPgogICAgICA8L2RlZj4KICAgICAgPGRlZiBsb2NhdG9yPSI5NToxLTk1OjY2IiBuYW1lPSJUb1N0cmluZyIgY29udGV4dD0iUGF0aWVudCIgYWNjZXNzTGV2ZWw9IlB1YmxpYyIgeHNpOnR5cGU9IkZ1bmN0aW9uRGVmIj4KICAgICAgICAgPGV4cHJlc3Npb24gbG9jYXRvcj0iOTU6NTYtOTU6NjYiIHBhdGg9InZhbHVlIiB4c2k6dHlwZT0iUHJvcGVydHkiPgogICAgICAgICAgICA8c291cmNlIGxvY2F0b3I9Ijk1OjU2LTk1OjYwIiBuYW1lPSJ2YWx1ZSIgeHNpOnR5cGU9Ik9wZXJhbmRSZWYiLz4KICAgICAgICAgPC9leHByZXNzaW9uPgogICAgICAgICA8b3BlcmFuZCBuYW1lPSJ2YWx1ZSI+CiAgICAgICAgICAgIDxvcGVyYW5kVHlwZVNwZWNpZmllciBsb2NhdG9yPSI5NTozMi05NTo1MiIgbmFtZT0iZmhpcjpDb2RlU3lzdGVtQ29udGVudE1vZGUiIHhzaTp0eXBlPSJOYW1lZFR5cGVTcGVjaWZpZXIiLz4KICAgICAgICAgPC9vcGVyYW5kPgogICAgICA8L2RlZj4KICAgICAgPGRlZiBsb2NhdG9yPSI5NjoxLTk2OjcxIiBuYW1lPSJUb1N0cmluZyIgY29udGV4dD0iUGF0aWVudCIgYWNjZXNzTGV2ZWw9IlB1YmxpYyIgeHNpOnR5cGU9IkZ1bmN0aW9uRGVmIj4KICAgICAgICAgPGV4cHJlc3Npb24gbG9jYXRvcj0iOTY6NjEtOTY6NzEiIHBhdGg9InZhbHVlIiB4c2k6dHlwZT0iUHJvcGVydHkiPgogICAgICAgICAgICA8c291cmNlIGxvY2F0b3I9Ijk2OjYxLTk2OjY1IiBuYW1lPSJ2YWx1ZSIgeHNpOnR5cGU9Ik9wZXJhbmRSZWYiLz4KICAgICAgICAgPC9leHByZXNzaW9uPgogICAgICAgICA8b3BlcmFuZCBuYW1lPSJ2YWx1ZSI+CiAgICAgICAgICAgIDxvcGVyYW5kVHlwZVNwZWNpZmllciBsb2NhdG9yPSI5NjozMi05Njo1NyIgbmFtZT0iZmhpcjpDb2RlU3lzdGVtSGllcmFyY2h5TWVhbmluZyIgeHNpOnR5cGU9Ik5hbWVkVHlwZVNwZWNpZmllciIvPgogICAgICAgICA8L29wZXJhbmQ+CiAgICAgIDwvZGVmPgogICAgICA8ZGVmIGxvY2F0b3I9Ijk3OjEtOTc6NjYiIG5hbWU9IlRvU3RyaW5nIiBjb250ZXh0PSJQYXRpZW50IiBhY2Nlc3NMZXZlbD0iUHVibGljIiB4c2k6dHlwZT0iRnVuY3Rpb25EZWYiPgogICAgICAgICA8ZXhwcmVzc2lvbiBsb2NhdG9yPSI5Nzo1Ni05Nzo2NiIgcGF0aD0idmFsdWUiIHhzaTp0eXBlPSJQcm9wZXJ0eSI+CiAgICAgICAgICAgIDxzb3VyY2UgbG9jYXRvcj0iOTc6NTYtOTc6NjAiIG5hbWU9InZhbHVlIiB4c2k6dHlwZT0iT3BlcmFuZFJlZiIvPgogICAgICAgICA8L2V4cHJlc3Npb24+CiAgICAgICAgIDxvcGVyYW5kIG5hbWU9InZhbHVlIj4KICAgICAgICAgICAgPG9wZXJhbmRUeXBlU3BlY2lmaWVyIGxvY2F0b3I9Ijk3OjMyLTk3OjUyIiBuYW1lPSJmaGlyOkNvbW11bmljYXRpb25Qcmlvcml0eSIgeHNpOnR5cGU9Ik5hbWVkVHlwZVNwZWNpZmllciIvPgogICAgICAgICA8L29wZXJhbmQ+CiAgICAgIDwvZGVmPgogICAgICA8ZGVmIGxvY2F0b3I9Ijk4OjEtOTg6NzEiIG5hbWU9IlRvU3RyaW5nIiBjb250ZXh0PSJQYXRpZW50IiBhY2Nlc3NMZXZlbD0iUHVibGljIiB4c2k6dHlwZT0iRnVuY3Rpb25EZWYiPgogICAgICAgICA8ZXhwcmVzc2lvbiBsb2NhdG9yPSI5ODo2MS05ODo3MSIgcGF0aD0idmFsdWUiIHhzaTp0eXBlPSJQcm9wZXJ0eSI+CiAgICAgICAgICAgIDxzb3VyY2UgbG9jYXRvcj0iOTg6NjEtOTg6NjUiIG5hbWU9InZhbHVlIiB4c2k6dHlwZT0iT3BlcmFuZFJlZiIvPgogICAgICAgICA8L2V4cHJlc3Npb24+CiAgICAgICAgIDxvcGVyYW5kIG5hbWU9InZhbHVlIj4KICAgICAgICAgICAgPG9wZXJhbmRUeXBlU3BlY2lmaWVyIGxvY2F0b3I9Ijk4OjMyLTk4OjU3IiBuYW1lPSJmaGlyOkNvbW11bmljYXRpb25SZXF1ZXN0U3RhdHVzIiB4c2k6dHlwZT0iTmFtZWRUeXBlU3BlY2lmaWVyIi8+CiAgICAgICAgIDwvb3BlcmFuZD4KICAgICAgPC9kZWY+CiAgICAgIDxkZWYgbG9jYXRvcj0iOTk6MS05OTo2NCIgbmFtZT0iVG9TdHJpbmciIGNvbnRleHQ9IlBhdGllbnQiIGFjY2Vzc0xldmVsPSJQdWJsaWMiIHhzaTp0eXBlPSJGdW5jdGlvbkRlZiI+CiAgICAgICAgIDxleHByZXNzaW9uIGxvY2F0b3I9Ijk5OjU0LTk5OjY0IiBwYXRoPSJ2YWx1ZSIgeHNpOnR5cGU9IlByb3BlcnR5Ij4KICAgICAgICAgICAgPHNvdXJjZSBsb2NhdG9yPSI5OTo1NC05OTo1OCIgbmFtZT0idmFsdWUiIHhzaTp0eXBlPSJPcGVyYW5kUmVmIi8+CiAgICAgICAgIDwvZXhwcmVzc2lvbj4KICAgICAgICAgPG9wZXJhbmQgbmFtZT0idmFsdWUiPgogICAgICAgICAgICA8b3BlcmFuZFR5cGVTcGVjaWZpZXIgbG9jYXRvcj0iOTk6MzItOTk6NTAiIG5hbWU9ImZoaXI6Q29tbXVuaWNhdGlvblN0YXR1cyIgeHNpOnR5cGU9Ik5hbWVkVHlwZVNwZWNpZmllciIvPgogICAgICAgICA8L29wZXJhbmQ+CiAgICAgIDwvZGVmPgogICAgICA8ZGVmIGxvY2F0b3I9IjEwMDoxLTEwMDo2MCIgbmFtZT0iVG9TdHJpbmciIGNvbnRleHQ9IlBhdGllbnQiIGFjY2Vzc0xldmVsPSJQdWJsaWMiIHhzaTp0eXBlPSJGdW5jdGlvbkRlZiI+CiAgICAgICAgIDxleHByZXNzaW9uIGxvY2F0b3I9IjEwMDo1MC0xMDA6NjAiIHBhdGg9InZhbHVlIiB4c2k6dHlwZT0iUHJvcGVydHkiPgogICAgICAgICAgICA8c291cmNlIGxvY2F0b3I9IjEwMDo1MC0xMDA6NTQiIG5hbWU9InZhbHVlIiB4c2k6dHlwZT0iT3BlcmFuZFJlZiIvPgogICAgICAgICA8L2V4cHJlc3Npb24+CiAgICAgICAgIDxvcGVyYW5kIG5hbWU9InZhbHVlIj4KICAgICAgICAgICAgPG9wZXJhbmRUeXBlU3BlY2lmaWVyIGxvY2F0b3I9IjEwMDozMi0xMDA6NDYiIG5hbWU9ImZoaXI6Q29tcGFydG1lbnRDb2RlIiB4c2k6dHlwZT0iTmFtZWRUeXBlU3BlY2lmaWVyIi8+CiAgICAgICAgIDwvb3BlcmFuZD4KICAgICAgPC9kZWY+CiAgICAgIDxkZWYgbG9jYXRvcj0iMTAxOjEtMTAxOjYwIiBuYW1lPSJUb1N0cmluZyIgY29udGV4dD0iUGF0aWVudCIgYWNjZXNzTGV2ZWw9IlB1YmxpYyIgeHNpOnR5cGU9IkZ1bmN0aW9uRGVmIj4KICAgICAgICAgPGV4cHJlc3Npb24gbG9jYXRvcj0iMTAxOjUwLTEwMTo2MCIgcGF0aD0idmFsdWUiIHhzaTp0eXBlPSJQcm9wZXJ0eSI+CiAgICAgICAgICAgIDxzb3VyY2UgbG9jYXRvcj0iMTAxOjUwLTEwMTo1NCIgbmFtZT0idmFsdWUiIHhzaTp0eXBlPSJPcGVyYW5kUmVmIi8+CiAgICAgICAgIDwvZXhwcmVzc2lvbj4KICAgICAgICAgPG9wZXJhbmQgbmFtZT0idmFsdWUiPgogICAgICAgICAgICA8b3BlcmFuZFR5cGVTcGVjaWZpZXIgbG9jYXRvcj0iMTAxOjMyLTEwMTo0NiIgbmFtZT0iZmhpcjpDb21wYXJ0bWVudFR5cGUiIHhzaTp0eXBlPSJOYW1lZFR5cGVTcGVjaWZpZXIiLz4KICAgICAgICAgPC9vcGVyYW5kPgogICAgICA8L2RlZj4KICAgICAgPGRlZiBsb2NhdG9yPSIxMDI6MS0xMDI6NzEiIG5hbWU9IlRvU3RyaW5nIiBjb250ZXh0PSJQYXRpZW50IiBhY2Nlc3NMZXZlbD0iUHVibGljIiB4c2k6dHlwZT0iRnVuY3Rpb25EZWYiPgogICAgICAgICA8ZXhwcmVzc2lvbiBsb2NhdG9yPSIxMDI6NjEtMTAyOjcxIiBwYXRoPSJ2YWx1ZSIgeHNpOnR5cGU9IlByb3BlcnR5Ij4KICAgICAgICAgICAgPHNvdXJjZSBsb2NhdG9yPSIxMDI6NjEtMTAyOjY1IiBuYW1lPSJ2YWx1ZSIgeHNpOnR5cGU9Ik9wZXJhbmRSZWYiLz4KICAgICAgICAgPC9leHByZXNzaW9uPgogICAgICAgICA8b3BlcmFuZCBuYW1lPSJ2YWx1ZSI+CiAgICAgICAgICAgIDxvcGVyYW5kVHlwZVNwZWNpZmllciBsb2NhdG9yPSIxMDI6MzItMTAyOjU3IiBuYW1lPSJmaGlyOkNvbXBvc2l0aW9uQXR0ZXN0YXRpb25Nb2RlIiB4c2k6dHlwZT0iTmFtZWRUeXBlU3BlY2lmaWVyIi8+CiAgICAgICAgIDwvb3BlcmFuZD4KICAgICAgPC9kZWY+CiAgICAgIDxkZWYgbG9jYXRvcj0iMTAzOjEtMTAzOjYyIiBuYW1lPSJUb1N0cmluZyIgY29udGV4dD0iUGF0aWVudCIgYWNjZXNzTGV2ZWw9IlB1YmxpYyIgeHNpOnR5cGU9IkZ1bmN0aW9uRGVmIj4KICAgICAgICAgPGV4cHJlc3Npb24gbG9jYXRvcj0iMTAzOjUyLTEwMzo2MiIgcGF0aD0idmFsdWUiIHhzaTp0eXBlPSJQcm9wZXJ0eSI+CiAgICAgICAgICAgIDxzb3VyY2UgbG9jYXRvcj0iMTAzOjUyLTEwMzo1NiIgbmFtZT0idmFsdWUiIHhzaTp0eXBlPSJPcGVyYW5kUmVmIi8+CiAgICAgICAgIDwvZXhwcmVzc2lvbj4KICAgICAgICAgPG9wZXJhbmQgbmFtZT0idmFsdWUiPgogICAgICAgICAgICA8b3BlcmFuZFR5cGVTcGVjaWZpZXIgbG9jYXRvcj0iMTAzOjMyLTEwMzo0OCIgbmFtZT0iZmhpcjpDb21wb3NpdGlvblN0YXR1cyIgeHNpOnR5cGU9Ik5hbWVkVHlwZVNwZWNpZmllciIvPgogICAgICAgICA8L29wZXJhbmQ+CiAgICAgIDwvZGVmPgogICAgICA8ZGVmIGxvY2F0b3I9IjEwNDoxLTEwNDo2NiIgbmFtZT0iVG9TdHJpbmciIGNvbnRleHQ9IlBhdGllbnQiIGFjY2Vzc0xldmVsPSJQdWJsaWMiIHhzaTp0eXBlPSJGdW5jdGlvbkRlZiI+CiAgICAgICAgIDxleHByZXNzaW9uIGxvY2F0b3I9IjEwNDo1Ni0xMDQ6NjYiIHBhdGg9InZhbHVlIiB4c2k6dHlwZT0iUHJvcGVydHkiPgogICAgICAgICAgICA8c291cmNlIGxvY2F0b3I9IjEwNDo1Ni0xMDQ6NjAiIG5hbWU9InZhbHVlIiB4c2k6dHlwZT0iT3BlcmFuZFJlZiIvPgogICAgICAgICA8L2V4cHJlc3Npb24+CiAgICAgICAgIDxvcGVyYW5kIG5hbWU9InZhbHVlIj4KICAgICAgICAgICAgPG9wZXJhbmRUeXBlU3BlY2lmaWVyIGxvY2F0b3I9IjEwNDozMi0xMDQ6NTIiIG5hbWU9ImZoaXI6Q29uY2VwdE1hcEVxdWl2YWxlbmNlIiB4c2k6dHlwZT0iTmFtZWRUeXBlU3BlY2lmaWVyIi8+CiAgICAgICAgIDwvb3BlcmFuZD4KICAgICAgPC9kZWY+CiAgICAgIDxkZWYgbG9jYXRvcj0iMTA1OjEtMTA1OjcyIiBuYW1lPSJUb1N0cmluZyIgY29udGV4dD0iUGF0aWVudCIgYWNjZXNzTGV2ZWw9IlB1YmxpYyIgeHNpOnR5cGU9IkZ1bmN0aW9uRGVmIj4KICAgICAgICAgPGV4cHJlc3Npb24gbG9jYXRvcj0iMTA1OjYyLTEwNTo3MiIgcGF0aD0idmFsdWUiIHhzaTp0eXBlPSJQcm9wZXJ0eSI+CiAgICAgICAgICAgIDxzb3VyY2UgbG9jYXRvcj0iMTA1OjYyLTEwNTo2NiIgbmFtZT0idmFsdWUiIHhzaTp0eXBlPSJPcGVyYW5kUmVmIi8+CiAgICAgICAgIDwvZXhwcmVzc2lvbj4KICAgICAgICAgPG9wZXJhbmQgbmFtZT0idmFsdWUiPgogICAgICAgICAgICA8b3BlcmFuZFR5cGVTcGVjaWZpZXIgbG9jYXRvcj0iMTA1OjMyLTEwNTo1OCIgbmFtZT0iZmhpcjpDb25jZXB0TWFwR3JvdXBVbm1hcHBlZE1vZGUiIHhzaTp0eXBlPSJOYW1lZFR5cGVTcGVjaWZpZXIiLz4KICAgICAgICAgPC9vcGVyYW5kPgogICAgICA8L2RlZj4KICAgICAgPGRlZiBsb2NhdG9yPSIxMDY6MS0xMDY6NjgiIG5hbWU9IlRvU3RyaW5nIiBjb250ZXh0PSJQYXRpZW50IiBhY2Nlc3NMZXZlbD0iUHVibGljIiB4c2k6dHlwZT0iRnVuY3Rpb25EZWYiPgogICAgICAgICA8ZXhwcmVzc2lvbiBsb2NhdG9yPSIxMDY6NTgtMTA2OjY4IiBwYXRoPSJ2YWx1ZSIgeHNpOnR5cGU9IlByb3BlcnR5Ij4KICAgICAgICAgICAgPHNvdXJjZSBsb2NhdG9yPSIxMDY6NTgtMTA2OjYyIiBuYW1lPSJ2YWx1ZSIgeHNpOnR5cGU9Ik9wZXJhbmRSZWYiLz4KICAgICAgICAgPC9leHByZXNzaW9uPgogICAgICAgICA8b3BlcmFuZCBuYW1lPSJ2YWx1ZSI+CiAgICAgICAgICAgIDxvcGVyYW5kVHlwZVNwZWNpZmllciBsb2NhdG9yPSIxMDY6MzItMTA2OjU0IiBuYW1lPSJmaGlyOkNvbmRpdGlvbmFsRGVsZXRlU3RhdHVzIiB4c2k6dHlwZT0iTmFtZWRUeXBlU3BlY2lmaWVyIi8+CiAgICAgICAgIDwvb3BlcmFuZD4KICAgICAgPC9kZWY+CiAgICAgIDxkZWYgbG9jYXRvcj0iMTA3OjEtMTA3OjY2IiBuYW1lPSJUb1N0cmluZyIgY29udGV4dD0iUGF0aWVudCIgYWNjZXNzTGV2ZWw9IlB1YmxpYyIgeHNpOnR5cGU9IkZ1bmN0aW9uRGVmIj4KICAgICAgICAgPGV4cHJlc3Npb24gbG9jYXRvcj0iMTA3OjU2LTEwNzo2NiIgcGF0aD0idmFsdWUiIHhzaTp0eXBlPSJQcm9wZXJ0eSI+CiAgICAgICAgICAgIDxzb3VyY2UgbG9jYXRvcj0iMTA3OjU2LTEwNzo2MCIgbmFtZT0idmFsdWUiIHhzaTp0eXBlPSJPcGVyYW5kUmVmIi8+CiAgICAgICAgIDwvZXhwcmVzc2lvbj4KICAgICAgICAgPG9wZXJhbmQgbmFtZT0idmFsdWUiPgogICAgICAgICAgICA8b3BlcmFuZFR5cGVTcGVjaWZpZXIgbG9jYXRvcj0iMTA3OjMyLTEwNzo1MiIgbmFtZT0iZmhpcjpDb25kaXRpb25hbFJlYWRTdGF0dXMiIHhzaTp0eXBlPSJOYW1lZFR5cGVTcGVjaWZpZXIiLz4KICAgICAgICAgPC9vcGVyYW5kPgogICAgICA8L2RlZj4KICAgICAgPGRlZiBsb2NhdG9yPSIxMDg6MS0xMDg6NjMiIG5hbWU9IlRvU3RyaW5nIiBjb250ZXh0PSJQYXRpZW50IiBhY2Nlc3NMZXZlbD0iUHVibGljIiB4c2k6dHlwZT0iRnVuY3Rpb25EZWYiPgogICAgICAgICA8ZXhwcmVzc2lvbiBsb2NhdG9yPSIxMDg6NTMtMTA4OjYzIiBwYXRoPSJ2YWx1ZSIgeHNpOnR5cGU9IlByb3BlcnR5Ij4KICAgICAgICAgICAgPHNvdXJjZSBsb2NhdG9yPSIxMDg6NTMtMTA4OjU3IiBuYW1lPSJ2YWx1ZSIgeHNpOnR5cGU9Ik9wZXJhbmRSZWYiLz4KICAgICAgICAgPC9leHByZXNzaW9uPgogICAgICAgICA8b3BlcmFuZCBuYW1lPSJ2YWx1ZSI+CiAgICAgICAgICAgIDxvcGVyYW5kVHlwZVNwZWNpZmllciBsb2NhdG9yPSIxMDg6MzItMTA4OjQ5IiBuYW1lPSJmaGlyOkNvbnNlbnREYXRhTWVhbmluZyIgeHNpOnR5cGU9Ik5hbWVkVHlwZVNwZWNpZmllciIvPgogICAgICAgICA8L29wZXJhbmQ+CiAgICAgIDwvZGVmPgogICAgICA8ZGVmIGxvY2F0b3I9IjEwOToxLTEwOTo2NSIgbmFtZT0iVG9TdHJpbmciIGNvbnRleHQ9IlBhdGllbnQiIGFjY2Vzc0xldmVsPSJQdWJsaWMiIHhzaTp0eXBlPSJGdW5jdGlvbkRlZiI+CiAgICAgICAgIDxleHByZXNzaW9uIGxvY2F0b3I9IjEwOTo1NS0xMDk6NjUiIHBhdGg9InZhbHVlIiB4c2k6dHlwZT0iUHJvcGVydHkiPgogICAgICAgICAgICA8c291cmNlIGxvY2F0b3I9IjEwOTo1NS0xMDk6NTkiIG5hbWU9InZhbHVlIiB4c2k6dHlwZT0iT3BlcmFuZFJlZiIvPgogICAgICAgICA8L2V4cHJlc3Npb24+CiAgICAgICAgIDxvcGVyYW5kIG5hbWU9InZhbHVlIj4KICAgICAgICAgICAgPG9wZXJhbmRUeXBlU3BlY2lmaWVyIGxvY2F0b3I9IjEwOTozMi0xMDk6NTEiIG5hbWU9ImZoaXI6Q29uc2VudFByb3Zpc2lvblR5cGUiIHhzaTp0eXBlPSJOYW1lZFR5cGVTcGVjaWZpZXIiLz4KICAgICAgICAgPC9vcGVyYW5kPgogICAgICA8L2RlZj4KICAgICAgPGRlZiBsb2NhdG9yPSIxMTA6MS0xMTA6NTciIG5hbWU9IlRvU3RyaW5nIiBjb250ZXh0PSJQYXRpZW50IiBhY2Nlc3NMZXZlbD0iUHVibGljIiB4c2k6dHlwZT0iRnVuY3Rpb25EZWYiPgogICAgICAgICA8ZXhwcmVzc2lvbiBsb2NhdG9yPSIxMTA6NDctMTEwOjU3IiBwYXRoPSJ2YWx1ZSIgeHNpOnR5cGU9IlByb3BlcnR5Ij4KICAgICAgICAgICAgPHNvdXJjZSBsb2NhdG9yPSIxMTA6NDctMTEwOjUxIiBuYW1lPSJ2YWx1ZSIgeHNpOnR5cGU9Ik9wZXJhbmRSZWYiLz4KICAgICAgICAgPC9leHByZXNzaW9uPgogICAgICAgICA8b3BlcmFuZCBuYW1lPSJ2YWx1ZSI+CiAgICAgICAgICAgIDxvcGVyYW5kVHlwZVNwZWNpZmllciBsb2NhdG9yPSIxMTA6MzItMTEwOjQzIiBuYW1lPSJmaGlyOkNvbnNlbnRTdGF0ZSIgeHNpOnR5cGU9Ik5hbWVkVHlwZVNwZWNpZmllciIvPgogICAgICAgICA8L29wZXJhbmQ+CiAgICAgIDwvZGVmPgogICAgICA8ZGVmIGxvY2F0b3I9IjExMToxLTExMTo2MyIgbmFtZT0iVG9TdHJpbmciIGNvbnRleHQ9IlBhdGllbnQiIGFjY2Vzc0xldmVsPSJQdWJsaWMiIHhzaTp0eXBlPSJGdW5jdGlvbkRlZiI+CiAgICAgICAgIDxleHByZXNzaW9uIGxvY2F0b3I9IjExMTo1My0xMTE6NjMiIHBhdGg9InZhbHVlIiB4c2k6dHlwZT0iUHJvcGVydHkiPgogICAgICAgICAgICA8c291cmNlIGxvY2F0b3I9IjExMTo1My0xMTE6NTciIG5hbWU9InZhbHVlIiB4c2k6dHlwZT0iT3BlcmFuZFJlZiIvPgogICAgICAgICA8L2V4cHJlc3Npb24+CiAgICAgICAgIDxvcGVyYW5kIG5hbWU9InZhbHVlIj4KICAgICAgICAgICAgPG9wZXJhbmRUeXBlU3BlY2lmaWVyIGxvY2F0b3I9IjExMTozMi0xMTE6NDkiIG5hbWU9ImZoaXI6Q29uc3RyYWludFNldmVyaXR5IiB4c2k6dHlwZT0iTmFtZWRUeXBlU3BlY2lmaWVyIi8+CiAgICAgICAgIDwvb3BlcmFuZD4KICAgICAgPC9kZWY+CiAgICAgIDxkZWYgbG9jYXRvcj0iMTEyOjEtMTEyOjYzIiBuYW1lPSJUb1N0cmluZyIgY29udGV4dD0iUGF0aWVudCIgYWNjZXNzTGV2ZWw9IlB1YmxpYyIgeHNpOnR5cGU9IkZ1bmN0aW9uRGVmIj4KICAgICAgICAgPGV4cHJlc3Npb24gbG9jYXRvcj0iMTEyOjUzLTExMjo2MyIgcGF0aD0idmFsdWUiIHhzaTp0eXBlPSJQcm9wZXJ0eSI+CiAgICAgICAgICAgIDxzb3VyY2UgbG9jYXRvcj0iMTEyOjUzLTExMjo1NyIgbmFtZT0idmFsdWUiIHhzaTp0eXBlPSJPcGVyYW5kUmVmIi8+CiAgICAgICAgIDwvZXhwcmVzc2lvbj4KICAgICAgICAgPG9wZXJhbmQgbmFtZT0idmFsdWUiPgogICAgICAgICAgICA8b3BlcmFuZFR5cGVTcGVjaWZpZXIgbG9jYXRvcj0iMTEyOjMyLTExMjo0OSIgbmFtZT0iZmhpcjpDb250YWN0UG9pbnRTeXN0ZW0iIHhzaTp0eXBlPSJOYW1lZFR5cGVTcGVjaWZpZXIiLz4KICAgICAgICAgPC9vcGVyYW5kPgogICAgICA8L2RlZj4KICAgICAgPGRlZiBsb2NhdG9yPSIxMTM6MS0xMTM6NjAiIG5hbWU9IlRvU3RyaW5nIiBjb250ZXh0PSJQYXRpZW50IiBhY2Nlc3NMZXZlbD0iUHVibGljIiB4c2k6dHlwZT0iRnVuY3Rpb25EZWYiPgogICAgICAgICA8ZXhwcmVzc2lvbiBsb2NhdG9yPSIxMTM6NTAtMTEzOjYwIiBwYXRoPSJ2YWx1ZSIgeHNpOnR5cGU9IlByb3BlcnR5Ij4KICAgICAgICAgICAgPHNvdXJjZSBsb2NhdG9yPSIxMTM6NTAtMTEzOjU0IiBuYW1lPSJ2YWx1ZSIgeHNpOnR5cGU9Ik9wZXJhbmRSZWYiLz4KICAgICAgICAgPC9leHByZXNzaW9uPgogICAgICAgICA8b3BlcmFuZCBuYW1lPSJ2YWx1ZSI+CiAgICAgICAgICAgIDxvcGVyYW5kVHlwZVNwZWNpZmllciBsb2NhdG9yPSIxMTM6MzItMTEzOjQ2IiBuYW1lPSJmaGlyOkNvbnRhY3RQb2ludFVzZSIgeHNpOnR5cGU9Ik5hbWVkVHlwZVNwZWNpZmllciIvPgogICAgICAgICA8L29wZXJhbmQ+CiAgICAgIDwvZGVmPgogICAgICA8ZGVmIGxvY2F0b3I9IjExNDoxLTExNDo3MCIgbmFtZT0iVG9TdHJpbmciIGNvbnRleHQ9IlBhdGllbnQiIGFjY2Vzc0xldmVsPSJQdWJsaWMiIHhzaTp0eXBlPSJGdW5jdGlvbkRlZiI+CiAgICAgICAgIDxleHByZXNzaW9uIGxvY2F0b3I9IjExNDo2MC0xMTQ6NzAiIHBhdGg9InZhbHVlIiB4c2k6dHlwZT0iUHJvcGVydHkiPgogICAgICAgICAgICA8c291cmNlIGxvY2F0b3I9IjExNDo2MC0xMTQ6NjQiIG5hbWU9InZhbHVlIiB4c2k6dHlwZT0iT3BlcmFuZFJlZiIvPgogICAgICAgICA8L2V4cHJlc3Npb24+CiAgICAgICAgIDxvcGVyYW5kIG5hbWU9InZhbHVlIj4KICAgICAgICAgICAgPG9wZXJhbmRUeXBlU3BlY2lmaWVyIGxvY2F0b3I9IjExNDozMi0xMTQ6NTYiIG5hbWU9ImZoaXI6Q29udHJhY3RQdWJsaWNhdGlvblN0YXR1cyIgeHNpOnR5cGU9Ik5hbWVkVHlwZVNwZWNpZmllciIvPgogICAgICAgICA8L29wZXJhbmQ+CiAgICAgIDwvZGVmPgogICAgICA8ZGVmIGxvY2F0b3I9IjExNToxLTExNTo1OSIgbmFtZT0iVG9TdHJpbmciIGNvbnRleHQ9IlBhdGllbnQiIGFjY2Vzc0xldmVsPSJQdWJsaWMiIHhzaTp0eXBlPSJGdW5jdGlvbkRlZiI+CiAgICAgICAgIDxleHByZXNzaW9uIGxvY2F0b3I9IjExNTo0OS0xMTU6NTkiIHBhdGg9InZhbHVlIiB4c2k6dHlwZT0iUHJvcGVydHkiPgogICAgICAgICAgICA8c291cmNlIGxvY2F0b3I9IjExNTo0OS0xMTU6NTMiIG5hbWU9InZhbHVlIiB4c2k6dHlwZT0iT3BlcmFuZFJlZiIvPgogICAgICAgICA8L2V4cHJlc3Npb24+CiAgICAgICAgIDxvcGVyYW5kIG5hbWU9InZhbHVlIj4KICAgICAgICAgICAgPG9wZXJhbmRUeXBlU3BlY2lmaWVyIGxvY2F0b3I9IjExNTozMi0xMTU6NDUiIG5hbWU9ImZoaXI6Q29udHJhY3RTdGF0dXMiIHhzaTp0eXBlPSJOYW1lZFR5cGVTcGVjaWZpZXIiLz4KICAgICAgICAgPC9vcGVyYW5kPgogICAgICA8L2RlZj4KICAgICAgPGRlZiBsb2NhdG9yPSIxMTY6MS0xMTY6NjAiIG5hbWU9IlRvU3RyaW5nIiBjb250ZXh0PSJQYXRpZW50IiBhY2Nlc3NMZXZlbD0iUHVibGljIiB4c2k6dHlwZT0iRnVuY3Rpb25EZWYiPgogICAgICAgICA8ZXhwcmVzc2lvbiBsb2NhdG9yPSIxMTY6NTAtMTE2OjYwIiBwYXRoPSJ2YWx1ZSIgeHNpOnR5cGU9IlByb3BlcnR5Ij4KICAgICAgICAgICAgPHNvdXJjZSBsb2NhdG9yPSIxMTY6NTAtMTE2OjU0IiBuYW1lPSJ2YWx1ZSIgeHNpOnR5cGU9Ik9wZXJhbmRSZWYiLz4KICAgICAgICAgPC9leHByZXNzaW9uPgogICAgICAgICA8b3BlcmFuZCBuYW1lPSJ2YWx1ZSI+CiAgICAgICAgICAgIDxvcGVyYW5kVHlwZVNwZWNpZmllciBsb2NhdG9yPSIxMTY6MzItMTE2OjQ2IiBuYW1lPSJmaGlyOkNvbnRyaWJ1dG9yVHlwZSIgeHNpOnR5cGU9Ik5hbWVkVHlwZVNwZWNpZmllciIvPgogICAgICAgICA8L29wZXJhbmQ+CiAgICAgIDwvZGVmPgogICAgICA8ZGVmIGxvY2F0b3I9IjExNzoxLTExNzo1OSIgbmFtZT0iVG9TdHJpbmciIGNvbnRleHQ9IlBhdGllbnQiIGFjY2Vzc0xldmVsPSJQdWJsaWMiIHhzaTp0eXBlPSJGdW5jdGlvbkRlZiI+CiAgICAgICAgIDxleHByZXNzaW9uIGxvY2F0b3I9IjExNzo0OS0xMTc6NTkiIHBhdGg9InZhbHVlIiB4c2k6dHlwZT0iUHJvcGVydHkiPgogICAgICAgICAgICA8c291cmNlIGxvY2F0b3I9IjExNzo0OS0xMTc6NTMiIG5hbWU9InZhbHVlIiB4c2k6dHlwZT0iT3BlcmFuZFJlZiIvPgogICAgICAgICA8L2V4cHJlc3Npb24+CiAgICAgICAgIDxvcGVyYW5kIG5hbWU9InZhbHVlIj4KICAgICAgICAgICAgPG9wZXJhbmRUeXBlU3BlY2lmaWVyIGxvY2F0b3I9IjExNzozMi0xMTc6NDUiIG5hbWU9ImZoaXI6Q292ZXJhZ2VTdGF0dXMiIHhzaTp0eXBlPSJOYW1lZFR5cGVTcGVjaWZpZXIiLz4KICAgICAgICAgPC9vcGVyYW5kPgogICAgICA8L2RlZj4KICAgICAgPGRlZiBsb2NhdG9yPSIxMTg6MS0xMTg6NTciIG5hbWU9IlRvU3RyaW5nIiBjb250ZXh0PSJQYXRpZW50IiBhY2Nlc3NMZXZlbD0iUHVibGljIiB4c2k6dHlwZT0iRnVuY3Rpb25EZWYiPgogICAgICAgICA8ZXhwcmVzc2lvbiBsb2NhdG9yPSIxMTg6NDctMTE4OjU3IiBwYXRoPSJ2YWx1ZSIgeHNpOnR5cGU9IlByb3BlcnR5Ij4KICAgICAgICAgICAgPHNvdXJjZSBsb2NhdG9yPSIxMTg6NDctMTE4OjUxIiBuYW1lPSJ2YWx1ZSIgeHNpOnR5cGU9Ik9wZXJhbmRSZWYiLz4KICAgICAgICAgPC9leHByZXNzaW9uPgogICAgICAgICA8b3BlcmFuZCBuYW1lPSJ2YWx1ZSI+CiAgICAgICAgICAgIDxvcGVyYW5kVHlwZVNwZWNpZmllciBsb2NhdG9yPSIxMTg6MzItMTE4OjQzIiBuYW1lPSJmaGlyOkN1cnJlbmN5Q29kZSIgeHNpOnR5cGU9Ik5hbWVkVHlwZVNwZWNpZmllciIvPgogICAgICAgICA8L29wZXJhbmQ+CiAgICAgIDwvZGVmPgogICAgICA8ZGVmIGxvY2F0b3I9IjExOToxLTExOTo1NCIgbmFtZT0iVG9TdHJpbmciIGNvbnRleHQ9IlBhdGllbnQiIGFjY2Vzc0xldmVsPSJQdWJsaWMiIHhzaTp0eXBlPSJGdW5jdGlvbkRlZiI+CiAgICAgICAgIDxleHByZXNzaW9uIGxvY2F0b3I9IjExOTo0NC0xMTk6NTQiIHBhdGg9InZhbHVlIiB4c2k6dHlwZT0iUHJvcGVydHkiPgogICAgICAgICAgICA8c291cmNlIGxvY2F0b3I9IjExOTo0NC0xMTk6NDgiIG5hbWU9InZhbHVlIiB4c2k6dHlwZT0iT3BlcmFuZFJlZiIvPgogICAgICAgICA8L2V4cHJlc3Npb24+CiAgICAgICAgIDxvcGVyYW5kIG5hbWU9InZhbHVlIj4KICAgICAgICAgICAgPG9wZXJhbmRUeXBlU3BlY2lmaWVyIGxvY2F0b3I9IjExOTozMi0xMTk6NDAiIG5hbWU9ImZoaXI6RGF5T2ZXZWVrIiB4c2k6dHlwZT0iTmFtZWRUeXBlU3BlY2lmaWVyIi8+CiAgICAgICAgIDwvb3BlcmFuZD4KICAgICAgPC9kZWY+CiAgICAgIDxkZWYgbG9jYXRvcj0iMTIwOjEtMTIwOjU1IiBuYW1lPSJUb1N0cmluZyIgY29udGV4dD0iUGF0aWVudCIgYWNjZXNzTGV2ZWw9IlB1YmxpYyIgeHNpOnR5cGU9IkZ1bmN0aW9uRGVmIj4KICAgICAgICAgPGV4cHJlc3Npb24gbG9jYXRvcj0iMTIwOjQ1LTEyMDo1NSIgcGF0aD0idmFsdWUiIHhzaTp0eXBlPSJQcm9wZXJ0eSI+CiAgICAgICAgICAgIDxzb3VyY2UgbG9jYXRvcj0iMTIwOjQ1LTEyMDo0OSIgbmFtZT0idmFsdWUiIHhzaTp0eXBlPSJPcGVyYW5kUmVmIi8+CiAgICAgICAgIDwvZXhwcmVzc2lvbj4KICAgICAgICAgPG9wZXJhbmQgbmFtZT0idmFsdWUiPgogICAgICAgICAgICA8b3BlcmFuZFR5cGVTcGVjaWZpZXIgbG9jYXRvcj0iMTIwOjMyLTEyMDo0MSIgbmFtZT0iZmhpcjpEYXlzT2ZXZWVrIiB4c2k6dHlwZT0iTmFtZWRUeXBlU3BlY2lmaWVyIi8+CiAgICAgICAgIDwvb3BlcmFuZD4KICAgICAgPC9kZWY+CiAgICAgIDxkZWYgbG9jYXRvcj0iMTIxOjEtMTIxOjY2IiBuYW1lPSJUb1N0cmluZyIgY29udGV4dD0iUGF0aWVudCIgYWNjZXNzTGV2ZWw9IlB1YmxpYyIgeHNpOnR5cGU9IkZ1bmN0aW9uRGVmIj4KICAgICAgICAgPGV4cHJlc3Npb24gbG9jYXRvcj0iMTIxOjU2LTEyMTo2NiIgcGF0aD0idmFsdWUiIHhzaTp0eXBlPSJQcm9wZXJ0eSI+CiAgICAgICAgICAgIDxzb3VyY2UgbG9jYXRvcj0iMTIxOjU2LTEyMTo2MCIgbmFtZT0idmFsdWUiIHhzaTp0eXBlPSJPcGVyYW5kUmVmIi8+CiAgICAgICAgIDwvZXhwcmVzc2lvbj4KICAgICAgICAgPG9wZXJhbmQgbmFtZT0idmFsdWUiPgogICAgICAgICAgICA8b3BlcmFuZFR5cGVTcGVjaWZpZXIgbG9jYXRvcj0iMTIxOjMyLTEyMTo1MiIgbmFtZT0iZmhpcjpEZXRlY3RlZElzc3VlU2V2ZXJpdHkiIHhzaTp0eXBlPSJOYW1lZFR5cGVTcGVjaWZpZXIiLz4KICAgICAgICAgPC9vcGVyYW5kPgogICAgICA8L2RlZj4KICAgICAgPGRlZiBsb2NhdG9yPSIxMjI6MS0xMjI6NjQiIG5hbWU9IlRvU3RyaW5nIiBjb250ZXh0PSJQYXRpZW50IiBhY2Nlc3NMZXZlbD0iUHVibGljIiB4c2k6dHlwZT0iRnVuY3Rpb25EZWYiPgogICAgICAgICA8ZXhwcmVzc2lvbiBsb2NhdG9yPSIxMjI6NTQtMTIyOjY0IiBwYXRoPSJ2YWx1ZSIgeHNpOnR5cGU9IlByb3BlcnR5Ij4KICAgICAgICAgICAgPHNvdXJjZSBsb2NhdG9yPSIxMjI6NTQtMTIyOjU4IiBuYW1lPSJ2YWx1ZSIgeHNpOnR5cGU9Ik9wZXJhbmRSZWYiLz4KICAgICAgICAgPC9leHByZXNzaW9uPgogICAgICAgICA8b3BlcmFuZCBuYW1lPSJ2YWx1ZSI+CiAgICAgICAgICAgIDxvcGVyYW5kVHlwZVNwZWNpZmllciBsb2NhdG9yPSIxMjI6MzItMTIyOjUwIiBuYW1lPSJmaGlyOkRldGVjdGVkSXNzdWVTdGF0dXMiIHhzaTp0eXBlPSJOYW1lZFR5cGVTcGVjaWZpZXIiLz4KICAgICAgICAgPC9vcGVyYW5kPgogICAgICA8L2RlZj4KICAgICAgPGRlZiBsb2NhdG9yPSIxMjM6MS0xMjM6NzMiIG5hbWU9IlRvU3RyaW5nIiBjb250ZXh0PSJQYXRpZW50IiBhY2Nlc3NMZXZlbD0iUHVibGljIiB4c2k6dHlwZT0iRnVuY3Rpb25EZWYiPgogICAgICAgICA8ZXhwcmVzc2lvbiBsb2NhdG9yPSIxMjM6NjMtMTIzOjczIiBwYXRoPSJ2YWx1ZSIgeHNpOnR5cGU9IlByb3BlcnR5Ij4KICAgICAgICAgICAgPHNvdXJjZSBsb2NhdG9yPSIxMjM6NjMtMTIzOjY3IiBuYW1lPSJ2YWx1ZSIgeHNpOnR5cGU9Ik9wZXJhbmRSZWYiLz4KICAgICAgICAgPC9leHByZXNzaW9uPgogICAgICAgICA8b3BlcmFuZCBuYW1lPSJ2YWx1ZSI+CiAgICAgICAgICAgIDxvcGVyYW5kVHlwZVNwZWNpZmllciBsb2NhdG9yPSIxMjM6MzItMTIzOjU5IiBuYW1lPSJmaGlyOkRldmljZU1ldHJpY0NhbGlicmF0aW9uU3RhdGUiIHhzaTp0eXBlPSJOYW1lZFR5cGVTcGVjaWZpZXIiLz4KICAgICAgICAgPC9vcGVyYW5kPgogICAgICA8L2RlZj4KICAgICAgPGRlZiBsb2NhdG9yPSIxMjQ6MS0xMjQ6NzIiIG5hbWU9IlRvU3RyaW5nIiBjb250ZXh0PSJQYXRpZW50IiBhY2Nlc3NMZXZlbD0iUHVibGljIiB4c2k6dHlwZT0iRnVuY3Rpb25EZWYiPgogICAgICAgICA8ZXhwcmVzc2lvbiBsb2NhdG9yPSIxMjQ6NjItMTI0OjcyIiBwYXRoPSJ2YWx1ZSIgeHNpOnR5cGU9IlByb3BlcnR5Ij4KICAgICAgICAgICAgPHNvdXJjZSBsb2NhdG9yPSIxMjQ6NjItMTI0OjY2IiBuYW1lPSJ2YWx1ZSIgeHNpOnR5cGU9Ik9wZXJhbmRSZWYiLz4KICAgICAgICAgPC9leHByZXNzaW9uPgogICAgICAgICA8b3BlcmFuZCBuYW1lPSJ2YWx1ZSI+CiAgICAgICAgICAgIDxvcGVyYW5kVHlwZVNwZWNpZmllciBsb2NhdG9yPSIxMjQ6MzItMTI0OjU4IiBuYW1lPSJmaGlyOkRldmljZU1ldHJpY0NhbGlicmF0aW9uVHlwZSIgeHNpOnR5cGU9Ik5hbWVkVHlwZVNwZWNpZmllciIvPgogICAgICAgICA8L29wZXJhbmQ+CiAgICAgIDwvZGVmPgogICAgICA8ZGVmIGxvY2F0b3I9IjEyNToxLTEyNTo2NSIgbmFtZT0iVG9TdHJpbmciIGNvbnRleHQ9IlBhdGllbnQiIGFjY2Vzc0xldmVsPSJQdWJsaWMiIHhzaTp0eXBlPSJGdW5jdGlvbkRlZiI+CiAgICAgICAgIDxleHByZXNzaW9uIGxvY2F0b3I9IjEyNTo1NS0xMjU6NjUiIHBhdGg9InZhbHVlIiB4c2k6dHlwZT0iUHJvcGVydHkiPgogICAgICAgICAgICA8c291cmNlIGxvY2F0b3I9IjEyNTo1NS0xMjU6NTkiIG5hbWU9InZhbHVlIiB4c2k6dHlwZT0iT3BlcmFuZFJlZiIvPgogICAgICAgICA8L2V4cHJlc3Npb24+CiAgICAgICAgIDxvcGVyYW5kIG5hbWU9InZhbHVlIj4KICAgICAgICAgICAgPG9wZXJhbmRUeXBlU3BlY2lmaWVyIGxvY2F0b3I9IjEyNTozMi0xMjU6NTEiIG5hbWU9ImZoaXI6RGV2aWNlTWV0cmljQ2F0ZWdvcnkiIHhzaTp0eXBlPSJOYW1lZFR5cGVTcGVjaWZpZXIiLz4KICAgICAgICAgPC9vcGVyYW5kPgogICAgICA8L2RlZj4KICAgICAgPGRlZiBsb2NhdG9yPSIxMjY6MS0xMjY6NjIiIG5hbWU9IlRvU3RyaW5nIiBjb250ZXh0PSJQYXRpZW50IiBhY2Nlc3NMZXZlbD0iUHVibGljIiB4c2k6dHlwZT0iRnVuY3Rpb25EZWYiPgogICAgICAgICA8ZXhwcmVzc2lvbiBsb2NhdG9yPSIxMjY6NTItMTI2OjYyIiBwYXRoPSJ2YWx1ZSIgeHNpOnR5cGU9IlByb3BlcnR5Ij4KICAgICAgICAgICAgPHNvdXJjZSBsb2NhdG9yPSIxMjY6NTItMTI2OjU2IiBuYW1lPSJ2YWx1ZSIgeHNpOnR5cGU9Ik9wZXJhbmRSZWYiLz4KICAgICAgICAgPC9leHByZXNzaW9uPgogICAgICAgICA8b3BlcmFuZCBuYW1lPSJ2YWx1ZSI+CiAgICAgICAgICAgIDxvcGVyYW5kVHlwZVNwZWNpZmllciBsb2NhdG9yPSIxMjY6MzItMTI2OjQ4IiBuYW1lPSJmaGlyOkRldmljZU1ldHJpY0NvbG9yIiB4c2k6dHlwZT0iTmFtZWRUeXBlU3BlY2lmaWVyIi8+CiAgICAgICAgIDwvb3BlcmFuZD4KICAgICAgPC9kZWY+CiAgICAgIDxkZWYgbG9jYXRvcj0iMTI3OjEtMTI3Ojc0IiBuYW1lPSJUb1N0cmluZyIgY29udGV4dD0iUGF0aWVudCIgYWNjZXNzTGV2ZWw9IlB1YmxpYyIgeHNpOnR5cGU9IkZ1bmN0aW9uRGVmIj4KICAgICAgICAgPGV4cHJlc3Npb24gbG9jYXRvcj0iMTI3OjY0LTEyNzo3NCIgcGF0aD0idmFsdWUiIHhzaTp0eXBlPSJQcm9wZXJ0eSI+CiAgICAgICAgICAgIDxzb3VyY2UgbG9jYXRvcj0iMTI3OjY0LTEyNzo2OCIgbmFtZT0idmFsdWUiIHhzaTp0eXBlPSJPcGVyYW5kUmVmIi8+CiAgICAgICAgIDwvZXhwcmVzc2lvbj4KICAgICAgICAgPG9wZXJhbmQgbmFtZT0idmFsdWUiPgogICAgICAgICAgICA8b3BlcmFuZFR5cGVTcGVjaWZpZXIgbG9jYXRvcj0iMTI3OjMyLTEyNzo2MCIgbmFtZT0iZmhpcjpEZXZpY2VNZXRyaWNPcGVyYXRpb25hbFN0YXR1cyIgeHNpOnR5cGU9Ik5hbWVkVHlwZVNwZWNpZmllciIvPgogICAgICAgICA8L29wZXJhbmQ+CiAgICAgIDwvZGVmPgogICAgICA8ZGVmIGxvY2F0b3I9IjEyODoxLTEyODo1OSIgbmFtZT0iVG9TdHJpbmciIGNvbnRleHQ9IlBhdGllbnQiIGFjY2Vzc0xldmVsPSJQdWJsaWMiIHhzaTp0eXBlPSJGdW5jdGlvbkRlZiI+CiAgICAgICAgIDxleHByZXNzaW9uIGxvY2F0b3I9IjEyODo0OS0xMjg6NTkiIHBhdGg9InZhbHVlIiB4c2k6dHlwZT0iUHJvcGVydHkiPgogICAgICAgICAgICA8c291cmNlIGxvY2F0b3I9IjEyODo0OS0xMjg6NTMiIG5hbWU9InZhbHVlIiB4c2k6dHlwZT0iT3BlcmFuZFJlZiIvPgogICAgICAgICA8L2V4cHJlc3Npb24+CiAgICAgICAgIDxvcGVyYW5kIG5hbWU9InZhbHVlIj4KICAgICAgICAgICAgPG9wZXJhbmRUeXBlU3BlY2lmaWVyIGxvY2F0b3I9IjEyODozMi0xMjg6NDUiIG5hbWU9ImZoaXI6RGV2aWNlTmFtZVR5cGUiIHhzaTp0eXBlPSJOYW1lZFR5cGVTcGVjaWZpZXIiLz4KICAgICAgICAgPC9vcGVyYW5kPgogICAgICA8L2RlZj4KICAgICAgPGRlZiBsb2NhdG9yPSIxMjk6MS0xMjk6NjQiIG5hbWU9IlRvU3RyaW5nIiBjb250ZXh0PSJQYXRpZW50IiBhY2Nlc3NMZXZlbD0iUHVibGljIiB4c2k6dHlwZT0iRnVuY3Rpb25EZWYiPgogICAgICAgICA8ZXhwcmVzc2lvbiBsb2NhdG9yPSIxMjk6NTQtMTI5OjY0IiBwYXRoPSJ2YWx1ZSIgeHNpOnR5cGU9IlByb3BlcnR5Ij4KICAgICAgICAgICAgPHNvdXJjZSBsb2NhdG9yPSIxMjk6NTQtMTI5OjU4IiBuYW1lPSJ2YWx1ZSIgeHNpOnR5cGU9Ik9wZXJhbmRSZWYiLz4KICAgICAgICAgPC9leHByZXNzaW9uPgogICAgICAgICA8b3BlcmFuZCBuYW1lPSJ2YWx1ZSI+CiAgICAgICAgICAgIDxvcGVyYW5kVHlwZVNwZWNpZmllciBsb2NhdG9yPSIxMjk6MzItMTI5OjUwIiBuYW1lPSJmaGlyOkRldmljZVJlcXVlc3RTdGF0dXMiIHhzaTp0eXBlPSJOYW1lZFR5cGVTcGVjaWZpZXIiLz4KICAgICAgICAgPC9vcGVyYW5kPgogICAgICA8L2RlZj4KICAgICAgPGRlZiBsb2NhdG9yPSIxMzA6MS0xMzA6NjkiIG5hbWU9IlRvU3RyaW5nIiBjb250ZXh0PSJQYXRpZW50IiBhY2Nlc3NMZXZlbD0iUHVibGljIiB4c2k6dHlwZT0iRnVuY3Rpb25EZWYiPgogICAgICAgICA8ZXhwcmVzc2lvbiBsb2NhdG9yPSIxMzA6NTktMTMwOjY5IiBwYXRoPSJ2YWx1ZSIgeHNpOnR5cGU9IlByb3BlcnR5Ij4KICAgICAgICAgICAgPHNvdXJjZSBsb2NhdG9yPSIxMzA6NTktMTMwOjYzIiBuYW1lPSJ2YWx1ZSIgeHNpOnR5cGU9Ik9wZXJhbmRSZWYiLz4KICAgICAgICAgPC9leHByZXNzaW9uPgogICAgICAgICA8b3BlcmFuZCBuYW1lPSJ2YWx1ZSI+CiAgICAgICAgICAgIDxvcGVyYW5kVHlwZVNwZWNpZmllciBsb2NhdG9yPSIxMzA6MzItMTMwOjU1IiBuYW1lPSJmaGlyOkRldmljZVVzZVN0YXRlbWVudFN0YXR1cyIgeHNpOnR5cGU9Ik5hbWVkVHlwZVNwZWNpZmllciIvPgogICAgICAgICA8L29wZXJhbmQ+CiAgICAgIDwvZGVmPgogICAgICA8ZGVmIGxvY2F0b3I9IjEzMToxLTEzMTo2NyIgbmFtZT0iVG9TdHJpbmciIGNvbnRleHQ9IlBhdGllbnQiIGFjY2Vzc0xldmVsPSJQdWJsaWMiIHhzaTp0eXBlPSJGdW5jdGlvbkRlZiI+CiAgICAgICAgIDxleHByZXNzaW9uIGxvY2F0b3I9IjEzMTo1Ny0xMzE6NjciIHBhdGg9InZhbHVlIiB4c2k6dHlwZT0iUHJvcGVydHkiPgogICAgICAgICAgICA8c291cmNlIGxvY2F0b3I9IjEzMTo1Ny0xMzE6NjEiIG5hbWU9InZhbHVlIiB4c2k6dHlwZT0iT3BlcmFuZFJlZiIvPgogICAgICAgICA8L2V4cHJlc3Npb24+CiAgICAgICAgIDxvcGVyYW5kIG5hbWU9InZhbHVlIj4KICAgICAgICAgICAgPG9wZXJhbmRUeXBlU3BlY2lmaWVyIGxvY2F0b3I9IjEzMTozMi0xMzE6NTMiIG5hbWU9ImZoaXI6RGlhZ25vc3RpY1JlcG9ydFN0YXR1cyIgeHNpOnR5cGU9Ik5hbWVkVHlwZVNwZWNpZmllciIvPgogICAgICAgICA8L29wZXJhbmQ+CiAgICAgIDwvZGVmPgogICAgICA8ZGVmIGxvY2F0b3I9IjEzMjoxLTEzMjo2MiIgbmFtZT0iVG9TdHJpbmciIGNvbnRleHQ9IlBhdGllbnQiIGFjY2Vzc0xldmVsPSJQdWJsaWMiIHhzaTp0eXBlPSJGdW5jdGlvbkRlZiI+CiAgICAgICAgIDxleHByZXNzaW9uIGxvY2F0b3I9IjEzMjo1Mi0xMzI6NjIiIHBhdGg9InZhbHVlIiB4c2k6dHlwZT0iUHJvcGVydHkiPgogICAgICAgICAgICA8c291cmNlIGxvY2F0b3I9IjEzMjo1Mi0xMzI6NTYiIG5hbWU9InZhbHVlIiB4c2k6dHlwZT0iT3BlcmFuZFJlZiIvPgogICAgICAgICA8L2V4cHJlc3Npb24+CiAgICAgICAgIDxvcGVyYW5kIG5hbWU9InZhbHVlIj4KICAgICAgICAgICAgPG9wZXJhbmRUeXBlU3BlY2lmaWVyIGxvY2F0b3I9IjEzMjozMi0xMzI6NDgiIG5hbWU9ImZoaXI6RGlzY3JpbWluYXRvclR5cGUiIHhzaTp0eXBlPSJOYW1lZFR5cGVTcGVjaWZpZXIiLz4KICAgICAgICAgPC9vcGVyYW5kPgogICAgICA8L2RlZj4KICAgICAgPGRlZiBsb2NhdG9yPSIxMzM6MS0xMzM6NjgiIG5hbWU9IlRvU3RyaW5nIiBjb250ZXh0PSJQYXRpZW50IiBhY2Nlc3NMZXZlbD0iUHVibGljIiB4c2k6dHlwZT0iRnVuY3Rpb25EZWYiPgogICAgICAgICA8ZXhwcmVzc2lvbiBsb2NhdG9yPSIxMzM6NTgtMTMzOjY4IiBwYXRoPSJ2YWx1ZSIgeHNpOnR5cGU9IlByb3BlcnR5Ij4KICAgICAgICAgICAgPHNvdXJjZSBsb2NhdG9yPSIxMzM6NTgtMTMzOjYyIiBuYW1lPSJ2YWx1ZSIgeHNpOnR5cGU9Ik9wZXJhbmRSZWYiLz4KICAgICAgICAgPC9leHByZXNzaW9uPgogICAgICAgICA8b3BlcmFuZCBuYW1lPSJ2YWx1ZSI+CiAgICAgICAgICAgIDxvcGVyYW5kVHlwZVNwZWNpZmllciBsb2NhdG9yPSIxMzM6MzItMTMzOjU0IiBuYW1lPSJmaGlyOkRvY3VtZW50Q29uZmlkZW50aWFsaXR5IiB4c2k6dHlwZT0iTmFtZWRUeXBlU3BlY2lmaWVyIi8+CiAgICAgICAgIDwvb3BlcmFuZD4KICAgICAgPC9kZWY+CiAgICAgIDxkZWYgbG9jYXRvcj0iMTM0OjEtMTM0OjU3IiBuYW1lPSJUb1N0cmluZyIgY29udGV4dD0iUGF0aWVudCIgYWNjZXNzTGV2ZWw9IlB1YmxpYyIgeHNpOnR5cGU9IkZ1bmN0aW9uRGVmIj4KICAgICAgICAgPGV4cHJlc3Npb24gbG9jYXRvcj0iMTM0OjQ3LTEzNDo1NyIgcGF0aD0idmFsdWUiIHhzaTp0eXBlPSJQcm9wZXJ0eSI+CiAgICAgICAgICAgIDxzb3VyY2UgbG9jYXRvcj0iMTM0OjQ3LTEzNDo1MSIgbmFtZT0idmFsdWUiIHhzaTp0eXBlPSJPcGVyYW5kUmVmIi8+CiAgICAgICAgIDwvZXhwcmVzc2lvbj4KICAgICAgICAgPG9wZXJhbmQgbmFtZT0idmFsdWUiPgogICAgICAgICAgICA8b3BlcmFuZFR5cGVTcGVjaWZpZXIgbG9jYXRvcj0iMTM0OjMyLTEzNDo0MyIgbmFtZT0iZmhpcjpEb2N1bWVudE1vZGUiIHhzaTp0eXBlPSJOYW1lZFR5cGVTcGVjaWZpZXIiLz4KICAgICAgICAgPC9vcGVyYW5kPgogICAgICA8L2RlZj4KICAgICAgPGRlZiBsb2NhdG9yPSIxMzU6MS0xMzU6NjgiIG5hbWU9IlRvU3RyaW5nIiBjb250ZXh0PSJQYXRpZW50IiBhY2Nlc3NMZXZlbD0iUHVibGljIiB4c2k6dHlwZT0iRnVuY3Rpb25EZWYiPgogICAgICAgICA8ZXhwcmVzc2lvbiBsb2NhdG9yPSIxMzU6NTgtMTM1OjY4IiBwYXRoPSJ2YWx1ZSIgeHNpOnR5cGU9IlByb3BlcnR5Ij4KICAgICAgICAgICAgPHNvdXJjZSBsb2NhdG9yPSIxMzU6NTgtMTM1OjYyIiBuYW1lPSJ2YWx1ZSIgeHNpOnR5cGU9Ik9wZXJhbmRSZWYiLz4KICAgICAgICAgPC9leHByZXNzaW9uPgogICAgICAgICA8b3BlcmFuZCBuYW1lPSJ2YWx1ZSI+CiAgICAgICAgICAgIDxvcGVyYW5kVHlwZVNwZWNpZmllciBsb2NhdG9yPSIxMzU6MzItMTM1OjU0IiBuYW1lPSJmaGlyOkRvY3VtZW50UmVmZXJlbmNlU3RhdHVzIiB4c2k6dHlwZT0iTmFtZWRUeXBlU3BlY2lmaWVyIi8+CiAgICAgICAgIDwvb3BlcmFuZD4KICAgICAgPC9kZWY+CiAgICAgIDxkZWYgbG9jYXRvcj0iMTM2OjEtMTM2OjY5IiBuYW1lPSJUb1N0cmluZyIgY29udGV4dD0iUGF0aWVudCIgYWNjZXNzTGV2ZWw9IlB1YmxpYyIgeHNpOnR5cGU9IkZ1bmN0aW9uRGVmIj4KICAgICAgICAgPGV4cHJlc3Npb24gbG9jYXRvcj0iMTM2OjU5LTEzNjo2OSIgcGF0aD0idmFsdWUiIHhzaTp0eXBlPSJQcm9wZXJ0eSI+CiAgICAgICAgICAgIDxzb3VyY2UgbG9jYXRvcj0iMTM2OjU5LTEzNjo2MyIgbmFtZT0idmFsdWUiIHhzaTp0eXBlPSJPcGVyYW5kUmVmIi8+CiAgICAgICAgIDwvZXhwcmVzc2lvbj4KICAgICAgICAgPG9wZXJhbmQgbmFtZT0idmFsdWUiPgogICAgICAgICAgICA8b3BlcmFuZFR5cGVTcGVjaWZpZXIgbG9jYXRvcj0iMTM2OjMyLTEzNjo1NSIgbmFtZT0iZmhpcjpEb2N1bWVudFJlbGF0aW9uc2hpcFR5cGUiIHhzaTp0eXBlPSJOYW1lZFR5cGVTcGVjaWZpZXIiLz4KICAgICAgICAgPC9vcGVyYW5kPgogICAgICA8L2RlZj4KICAgICAgPGRlZiBsb2NhdG9yPSIxMzc6MS0xMzc6NzAiIG5hbWU9IlRvU3RyaW5nIiBjb250ZXh0PSJQYXRpZW50IiBhY2Nlc3NMZXZlbD0iUHVibGljIiB4c2k6dHlwZT0iRnVuY3Rpb25EZWYiPgogICAgICAgICA8ZXhwcmVzc2lvbiBsb2NhdG9yPSIxMzc6NjAtMTM3OjcwIiBwYXRoPSJ2YWx1ZSIgeHNpOnR5cGU9IlByb3BlcnR5Ij4KICAgICAgICAgICAgPHNvdXJjZSBsb2NhdG9yPSIxMzc6NjAtMTM3OjY0IiBuYW1lPSJ2YWx1ZSIgeHNpOnR5cGU9Ik9wZXJhbmRSZWYiLz4KICAgICAgICAgPC9leHByZXNzaW9uPgogICAgICAgICA8b3BlcmFuZCBuYW1lPSJ2YWx1ZSI+CiAgICAgICAgICAgIDxvcGVyYW5kVHlwZVNwZWNpZmllciBsb2NhdG9yPSIxMzc6MzItMTM3OjU2IiBuYW1lPSJmaGlyOkVsaWdpYmlsaXR5UmVxdWVzdFB1cnBvc2UiIHhzaTp0eXBlPSJOYW1lZFR5cGVTcGVjaWZpZXIiLz4KICAgICAgICAgPC9vcGVyYW5kPgogICAgICA8L2RlZj4KICAgICAgPGRlZiBsb2NhdG9yPSIxMzg6MS0xMzg6NjkiIG5hbWU9IlRvU3RyaW5nIiBjb250ZXh0PSJQYXRpZW50IiBhY2Nlc3NMZXZlbD0iUHVibGljIiB4c2k6dHlwZT0iRnVuY3Rpb25EZWYiPgogICAgICAgICA8ZXhwcmVzc2lvbiBsb2NhdG9yPSIxMzg6NTktMTM4OjY5IiBwYXRoPSJ2YWx1ZSIgeHNpOnR5cGU9IlByb3BlcnR5Ij4KICAgICAgICAgICAgPHNvdXJjZSBsb2NhdG9yPSIxMzg6NTktMTM4OjYzIiBuYW1lPSJ2YWx1ZSIgeHNpOnR5cGU9Ik9wZXJhbmRSZWYiLz4KICAgICAgICAgPC9leHByZXNzaW9uPgogICAgICAgICA8b3BlcmFuZCBuYW1lPSJ2YWx1ZSI+CiAgICAgICAgICAgIDxvcGVyYW5kVHlwZVNwZWNpZmllciBsb2NhdG9yPSIxMzg6MzItMTM4OjU1IiBuYW1lPSJmaGlyOkVsaWdpYmlsaXR5UmVxdWVzdFN0YXR1cyIgeHNpOnR5cGU9Ik5hbWVkVHlwZVNwZWNpZmllciIvPgogICAgICAgICA8L29wZXJhbmQ+CiAgICAgIDwvZGVmPgogICAgICA8ZGVmIGxvY2F0b3I9IjEzOToxLTEzOTo3MSIgbmFtZT0iVG9TdHJpbmciIGNvbnRleHQ9IlBhdGllbnQiIGFjY2Vzc0xldmVsPSJQdWJsaWMiIHhzaTp0eXBlPSJGdW5jdGlvbkRlZiI+CiAgICAgICAgIDxleHByZXNzaW9uIGxvY2F0b3I9IjEzOTo2MS0xMzk6NzEiIHBhdGg9InZhbHVlIiB4c2k6dHlwZT0iUHJvcGVydHkiPgogICAgICAgICAgICA8c291cmNlIGxvY2F0b3I9IjEzOTo2MS0xMzk6NjUiIG5hbWU9InZhbHVlIiB4c2k6dHlwZT0iT3BlcmFuZFJlZiIvPgogICAgICAgICA8L2V4cHJlc3Npb24+CiAgICAgICAgIDxvcGVyYW5kIG5hbWU9InZhbHVlIj4KICAgICAgICAgICAgPG9wZXJhbmRUeXBlU3BlY2lmaWVyIGxvY2F0b3I9IjEzOTozMi0xMzk6NTciIG5hbWU9ImZoaXI6RWxpZ2liaWxpdHlSZXNwb25zZVB1cnBvc2UiIHhzaTp0eXBlPSJOYW1lZFR5cGVTcGVjaWZpZXIiLz4KICAgICAgICAgPC9vcGVyYW5kPgogICAgICA8L2RlZj4KICAgICAgPGRlZiBsb2NhdG9yPSIxNDA6MS0xNDA6NzAiIG5hbWU9IlRvU3RyaW5nIiBjb250ZXh0PSJQYXRpZW50IiBhY2Nlc3NMZXZlbD0iUHVibGljIiB4c2k6dHlwZT0iRnVuY3Rpb25EZWYiPgogICAgICAgICA8ZXhwcmVzc2lvbiBsb2NhdG9yPSIxNDA6NjAtMTQwOjcwIiBwYXRoPSJ2YWx1ZSIgeHNpOnR5cGU9IlByb3BlcnR5Ij4KICAgICAgICAgICAgPHNvdXJjZSBsb2NhdG9yPSIxNDA6NjAtMTQwOjY0IiBuYW1lPSJ2YWx1ZSIgeHNpOnR5cGU9Ik9wZXJhbmRSZWYiLz4KICAgICAgICAgPC9leHByZXNzaW9uPgogICAgICAgICA8b3BlcmFuZCBuYW1lPSJ2YWx1ZSI+CiAgICAgICAgICAgIDxvcGVyYW5kVHlwZVNwZWNpZmllciBsb2NhdG9yPSIxNDA6MzItMTQwOjU2IiBuYW1lPSJmaGlyOkVsaWdpYmlsaXR5UmVzcG9uc2VTdGF0dXMiIHhzaTp0eXBlPSJOYW1lZFR5cGVTcGVjaWZpZXIiLz4KICAgICAgICAgPC9vcGVyYW5kPgogICAgICA8L2RlZj4KICAgICAgPGRlZiBsb2NhdG9yPSIxNDE6MS0xNDE6NjMiIG5hbWU9IlRvU3RyaW5nIiBjb250ZXh0PSJQYXRpZW50IiBhY2Nlc3NMZXZlbD0iUHVibGljIiB4c2k6dHlwZT0iRnVuY3Rpb25EZWYiPgogICAgICAgICA8ZXhwcmVzc2lvbiBsb2NhdG9yPSIxNDE6NTMtMTQxOjYzIiBwYXRoPSJ2YWx1ZSIgeHNpOnR5cGU9IlByb3BlcnR5Ij4KICAgICAgICAgICAgPHNvdXJjZSBsb2NhdG9yPSIxNDE6NTMtMTQxOjU3IiBuYW1lPSJ2YWx1ZSIgeHNpOnR5cGU9Ik9wZXJhbmRSZWYiLz4KICAgICAgICAgPC9leHByZXNzaW9uPgogICAgICAgICA8b3BlcmFuZCBuYW1lPSJ2YWx1ZSI+CiAgICAgICAgICAgIDxvcGVyYW5kVHlwZVNwZWNpZmllciBsb2NhdG9yPSIxNDE6MzItMTQxOjQ5IiBuYW1lPSJmaGlyOkVuYWJsZVdoZW5CZWhhdmlvciIgeHNpOnR5cGU9Ik5hbWVkVHlwZVNwZWNpZmllciIvPgogICAgICAgICA8L29wZXJhbmQ+CiAgICAgIDwvZGVmPgogICAgICA8ZGVmIGxvY2F0b3I9IjE0MjoxLTE0Mjo2OCIgbmFtZT0iVG9TdHJpbmciIGNvbnRleHQ9IlBhdGllbnQiIGFjY2Vzc0xldmVsPSJQdWJsaWMiIHhzaTp0eXBlPSJGdW5jdGlvbkRlZiI+CiAgICAgICAgIDxleHByZXNzaW9uIGxvY2F0b3I9IjE0Mjo1OC0xNDI6NjgiIHBhdGg9InZhbHVlIiB4c2k6dHlwZT0iUHJvcGVydHkiPgogICAgICAgICAgICA8c291cmNlIGxvY2F0b3I9IjE0Mjo1OC0xNDI6NjIiIG5hbWU9InZhbHVlIiB4c2k6dHlwZT0iT3BlcmFuZFJlZiIvPgogICAgICAgICA8L2V4cHJlc3Npb24+CiAgICAgICAgIDxvcGVyYW5kIG5hbWU9InZhbHVlIj4KICAgICAgICAgICAgPG9wZXJhbmRUeXBlU3BlY2lmaWVyIGxvY2F0b3I9IjE0MjozMi0xNDI6NTQiIG5hbWU9ImZoaXI6RW5jb3VudGVyTG9jYXRpb25TdGF0dXMiIHhzaTp0eXBlPSJOYW1lZFR5cGVTcGVjaWZpZXIiLz4KICAgICAgICAgPC9vcGVyYW5kPgogICAgICA8L2RlZj4KICAgICAgPGRlZiBsb2NhdG9yPSIxNDM6MS0xNDM6NjAiIG5hbWU9IlRvU3RyaW5nIiBjb250ZXh0PSJQYXRpZW50IiBhY2Nlc3NMZXZlbD0iUHVibGljIiB4c2k6dHlwZT0iRnVuY3Rpb25EZWYiPgogICAgICAgICA8ZXhwcmVzc2lvbiBsb2NhdG9yPSIxNDM6NTAtMTQzOjYwIiBwYXRoPSJ2YWx1ZSIgeHNpOnR5cGU9IlByb3BlcnR5Ij4KICAgICAgICAgICAgPHNvdXJjZSBsb2NhdG9yPSIxNDM6NTAtMTQzOjU0IiBuYW1lPSJ2YWx1ZSIgeHNpOnR5cGU9Ik9wZXJhbmRSZWYiLz4KICAgICAgICAgPC9leHByZXNzaW9uPgogICAgICAgICA8b3BlcmFuZCBuYW1lPSJ2YWx1ZSI+CiAgICAgICAgICAgIDxvcGVyYW5kVHlwZVNwZWNpZmllciBsb2NhdG9yPSIxNDM6MzItMTQzOjQ2IiBuYW1lPSJmaGlyOkVuY291bnRlclN0YXR1cyIgeHNpOnR5cGU9Ik5hbWVkVHlwZVNwZWNpZmllciIvPgogICAgICAgICA8L29wZXJhbmQ+CiAgICAgIDwvZGVmPgogICAgICA8ZGVmIGxvY2F0b3I9IjE0NDoxLTE0NDo1OSIgbmFtZT0iVG9TdHJpbmciIGNvbnRleHQ9IlBhdGllbnQiIGFjY2Vzc0xldmVsPSJQdWJsaWMiIHhzaTp0eXBlPSJGdW5jdGlvbkRlZiI+CiAgICAgICAgIDxleHByZXNzaW9uIGxvY2F0b3I9IjE0NDo0OS0xNDQ6NTkiIHBhdGg9InZhbHVlIiB4c2k6dHlwZT0iUHJvcGVydHkiPgogICAgICAgICAgICA8c291cmNlIGxvY2F0b3I9IjE0NDo0OS0xNDQ6NTMiIG5hbWU9InZhbHVlIiB4c2k6dHlwZT0iT3BlcmFuZFJlZiIvPgogICAgICAgICA8L2V4cHJlc3Npb24+CiAgICAgICAgIDxvcGVyYW5kIG5hbWU9InZhbHVlIj4KICAgICAgICAgICAgPG9wZXJhbmRUeXBlU3BlY2lmaWVyIGxvY2F0b3I9IjE0NDozMi0xNDQ6NDUiIG5hbWU9ImZoaXI6RW5kcG9pbnRTdGF0dXMiIHhzaTp0eXBlPSJOYW1lZFR5cGVTcGVjaWZpZXIiLz4KICAgICAgICAgPC9vcGVyYW5kPgogICAgICA8L2RlZj4KICAgICAgPGRlZiBsb2NhdG9yPSIxNDU6MS0xNDU6NjgiIG5hbWU9IlRvU3RyaW5nIiBjb250ZXh0PSJQYXRpZW50IiBhY2Nlc3NMZXZlbD0iUHVibGljIiB4c2k6dHlwZT0iRnVuY3Rpb25EZWYiPgogICAgICAgICA8ZXhwcmVzc2lvbiBsb2NhdG9yPSIxNDU6NTgtMTQ1OjY4IiBwYXRoPSJ2YWx1ZSIgeHNpOnR5cGU9IlByb3BlcnR5Ij4KICAgICAgICAgICAgPHNvdXJjZSBsb2NhdG9yPSIxNDU6NTgtMTQ1OjYyIiBuYW1lPSJ2YWx1ZSIgeHNpOnR5cGU9Ik9wZXJhbmRSZWYiLz4KICAgICAgICAgPC9leHByZXNzaW9uPgogICAgICAgICA8b3BlcmFuZCBuYW1lPSJ2YWx1ZSI+CiAgICAgICAgICAgIDxvcGVyYW5kVHlwZVNwZWNpZmllciBsb2NhdG9yPSIxNDU6MzItMTQ1OjU0IiBuYW1lPSJmaGlyOkVucm9sbG1lbnRSZXF1ZXN0U3RhdHVzIiB4c2k6dHlwZT0iTmFtZWRUeXBlU3BlY2lmaWVyIi8+CiAgICAgICAgIDwvb3BlcmFuZD4KICAgICAgPC9kZWY+CiAgICAgIDxkZWYgbG9jYXRvcj0iMTQ2OjEtMTQ2OjY5IiBuYW1lPSJUb1N0cmluZyIgY29udGV4dD0iUGF0aWVudCIgYWNjZXNzTGV2ZWw9IlB1YmxpYyIgeHNpOnR5cGU9IkZ1bmN0aW9uRGVmIj4KICAgICAgICAgPGV4cHJlc3Npb24gbG9jYXRvcj0iMTQ2OjU5LTE0Njo2OSIgcGF0aD0idmFsdWUiIHhzaTp0eXBlPSJQcm9wZXJ0eSI+CiAgICAgICAgICAgIDxzb3VyY2UgbG9jYXRvcj0iMTQ2OjU5LTE0Njo2MyIgbmFtZT0idmFsdWUiIHhzaTp0eXBlPSJPcGVyYW5kUmVmIi8+CiAgICAgICAgIDwvZXhwcmVzc2lvbj4KICAgICAgICAgPG9wZXJhbmQgbmFtZT0idmFsdWUiPgogICAgICAgICAgICA8b3BlcmFuZFR5cGVTcGVjaWZpZXIgbG9jYXRvcj0iMTQ2OjMyLTE0Njo1NSIgbmFtZT0iZmhpcjpFbnJvbGxtZW50UmVzcG9uc2VTdGF0dXMiIHhzaTp0eXBlPSJOYW1lZFR5cGVTcGVjaWZpZXIiLz4KICAgICAgICAgPC9vcGVyYW5kPgogICAgICA8L2RlZj4KICAgICAgPGRlZiBsb2NhdG9yPSIxNDc6MS0xNDc6NjQiIG5hbWU9IlRvU3RyaW5nIiBjb250ZXh0PSJQYXRpZW50IiBhY2Nlc3NMZXZlbD0iUHVibGljIiB4c2k6dHlwZT0iRnVuY3Rpb25EZWYiPgogICAgICAgICA8ZXhwcmVzc2lvbiBsb2NhdG9yPSIxNDc6NTQtMTQ3OjY0IiBwYXRoPSJ2YWx1ZSIgeHNpOnR5cGU9IlByb3BlcnR5Ij4KICAgICAgICAgICAgPHNvdXJjZSBsb2NhdG9yPSIxNDc6NTQtMTQ3OjU4IiBuYW1lPSJ2YWx1ZSIgeHNpOnR5cGU9Ik9wZXJhbmRSZWYiLz4KICAgICAgICAgPC9leHByZXNzaW9uPgogICAgICAgICA8b3BlcmFuZCBuYW1lPSJ2YWx1ZSI+CiAgICAgICAgICAgIDxvcGVyYW5kVHlwZVNwZWNpZmllciBsb2NhdG9yPSIxNDc6MzItMTQ3OjUwIiBuYW1lPSJmaGlyOkVwaXNvZGVPZkNhcmVTdGF0dXMiIHhzaTp0eXBlPSJOYW1lZFR5cGVTcGVjaWZpZXIiLz4KICAgICAgICAgPC9vcGVyYW5kPgogICAgICA8L2RlZj4KICAgICAgPGRlZiBsb2NhdG9yPSIxNDg6MS0xNDg6NjQiIG5hbWU9IlRvU3RyaW5nIiBjb250ZXh0PSJQYXRpZW50IiBhY2Nlc3NMZXZlbD0iUHVibGljIiB4c2k6dHlwZT0iRnVuY3Rpb25EZWYiPgogICAgICAgICA8ZXhwcmVzc2lvbiBsb2NhdG9yPSIxNDg6NTQtMTQ4OjY0IiBwYXRoPSJ2YWx1ZSIgeHNpOnR5cGU9IlByb3BlcnR5Ij4KICAgICAgICAgICAgPHNvdXJjZSBsb2NhdG9yPSIxNDg6NTQtMTQ4OjU4IiBuYW1lPSJ2YWx1ZSIgeHNpOnR5cGU9Ik9wZXJhbmRSZWYiLz4KICAgICAgICAgPC9leHByZXNzaW9uPgogICAgICAgICA8b3BlcmFuZCBuYW1lPSJ2YWx1ZSI+CiAgICAgICAgICAgIDxvcGVyYW5kVHlwZVNwZWNpZmllciBsb2NhdG9yPSIxNDg6MzItMTQ4OjUwIiBuYW1lPSJmaGlyOkV2ZW50Q2FwYWJpbGl0eU1vZGUiIHhzaTp0eXBlPSJOYW1lZFR5cGVTcGVjaWZpZXIiLz4KICAgICAgICAgPC9vcGVyYW5kPgogICAgICA8L2RlZj4KICAgICAgPGRlZiBsb2NhdG9yPSIxNDk6MS0xNDk6NTYiIG5hbWU9IlRvU3RyaW5nIiBjb250ZXh0PSJQYXRpZW50IiBhY2Nlc3NMZXZlbD0iUHVibGljIiB4c2k6dHlwZT0iRnVuY3Rpb25EZWYiPgogICAgICAgICA8ZXhwcmVzc2lvbiBsb2NhdG9yPSIxNDk6NDYtMTQ5OjU2IiBwYXRoPSJ2YWx1ZSIgeHNpOnR5cGU9IlByb3BlcnR5Ij4KICAgICAgICAgICAgPHNvdXJjZSBsb2NhdG9yPSIxNDk6NDYtMTQ5OjUwIiBuYW1lPSJ2YWx1ZSIgeHNpOnR5cGU9Ik9wZXJhbmRSZWYiLz4KICAgICAgICAgPC9leHByZXNzaW9uPgogICAgICAgICA8b3BlcmFuZCBuYW1lPSJ2YWx1ZSI+CiAgICAgICAgICAgIDxvcGVyYW5kVHlwZVNwZWNpZmllciBsb2NhdG9yPSIxNDk6MzItMTQ5OjQyIiBuYW1lPSJmaGlyOkV2ZW50VGltaW5nIiB4c2k6dHlwZT0iTmFtZWRUeXBlU3BlY2lmaWVyIi8+CiAgICAgICAgIDwvb3BlcmFuZD4KICAgICAgPC9kZWY+CiAgICAgIDxkZWYgbG9jYXRvcj0iMTUwOjEtMTUwOjY1IiBuYW1lPSJUb1N0cmluZyIgY29udGV4dD0iUGF0aWVudCIgYWNjZXNzTGV2ZWw9IlB1YmxpYyIgeHNpOnR5cGU9IkZ1bmN0aW9uRGVmIj4KICAgICAgICAgPGV4cHJlc3Npb24gbG9jYXRvcj0iMTUwOjU1LTE1MDo2NSIgcGF0aD0idmFsdWUiIHhzaTp0eXBlPSJQcm9wZXJ0eSI+CiAgICAgICAgICAgIDxzb3VyY2UgbG9jYXRvcj0iMTUwOjU1LTE1MDo1OSIgbmFtZT0idmFsdWUiIHhzaTp0eXBlPSJPcGVyYW5kUmVmIi8+CiAgICAgICAgIDwvZXhwcmVzc2lvbj4KICAgICAgICAgPG9wZXJhbmQgbmFtZT0idmFsdWUiPgogICAgICAgICAgICA8b3BlcmFuZFR5cGVTcGVjaWZpZXIgbG9jYXRvcj0iMTUwOjMyLTE1MDo1MSIgbmFtZT0iZmhpcjpFdmlkZW5jZVZhcmlhYmxlVHlwZSIgeHNpOnR5cGU9Ik5hbWVkVHlwZVNwZWNpZmllciIvPgogICAgICAgICA8L29wZXJhbmQ+CiAgICAgIDwvZGVmPgogICAgICA8ZGVmIGxvY2F0b3I9IjE1MToxLTE1MTo2OSIgbmFtZT0iVG9TdHJpbmciIGNvbnRleHQ9IlBhdGllbnQiIGFjY2Vzc0xldmVsPSJQdWJsaWMiIHhzaTp0eXBlPSJGdW5jdGlvbkRlZiI+CiAgICAgICAgIDxleHByZXNzaW9uIGxvY2F0b3I9IjE1MTo1OS0xNTE6NjkiIHBhdGg9InZhbHVlIiB4c2k6dHlwZT0iUHJvcGVydHkiPgogICAgICAgICAgICA8c291cmNlIGxvY2F0b3I9IjE1MTo1OS0xNTE6NjMiIG5hbWU9InZhbHVlIiB4c2k6dHlwZT0iT3BlcmFuZFJlZiIvPgogICAgICAgICA8L2V4cHJlc3Npb24+CiAgICAgICAgIDxvcGVyYW5kIG5hbWU9InZhbHVlIj4KICAgICAgICAgICAgPG9wZXJhbmRUeXBlU3BlY2lmaWVyIGxvY2F0b3I9IjE1MTozMi0xNTE6NTUiIG5hbWU9ImZoaXI6RXhhbXBsZVNjZW5hcmlvQWN0b3JUeXBlIiB4c2k6dHlwZT0iTmFtZWRUeXBlU3BlY2lmaWVyIi8+CiAgICAgICAgIDwvb3BlcmFuZD4KICAgICAgPC9kZWY+CiAgICAgIDxkZWYgbG9jYXRvcj0iMTUyOjEtMTUyOjcxIiBuYW1lPSJUb1N0cmluZyIgY29udGV4dD0iUGF0aWVudCIgYWNjZXNzTGV2ZWw9IlB1YmxpYyIgeHNpOnR5cGU9IkZ1bmN0aW9uRGVmIj4KICAgICAgICAgPGV4cHJlc3Npb24gbG9jYXRvcj0iMTUyOjYxLTE1Mjo3MSIgcGF0aD0idmFsdWUiIHhzaTp0eXBlPSJQcm9wZXJ0eSI+CiAgICAgICAgICAgIDxzb3VyY2UgbG9jYXRvcj0iMTUyOjYxLTE1Mjo2NSIgbmFtZT0idmFsdWUiIHhzaTp0eXBlPSJPcGVyYW5kUmVmIi8+CiAgICAgICAgIDwvZXhwcmVzc2lvbj4KICAgICAgICAgPG9wZXJhbmQgbmFtZT0idmFsdWUiPgogICAgICAgICAgICA8b3BlcmFuZFR5cGVTcGVjaWZpZXIgbG9jYXRvcj0iMTUyOjMyLTE1Mjo1NyIgbmFtZT0iZmhpcjpFeHBsYW5hdGlvbk9mQmVuZWZpdFN0YXR1cyIgeHNpOnR5cGU9Ik5hbWVkVHlwZVNwZWNpZmllciIvPgogICAgICAgICA8L29wZXJhbmQ+CiAgICAgIDwvZGVmPgogICAgICA8ZGVmIGxvY2F0b3I9IjE1MzoxLTE1Mzo1OCIgbmFtZT0iVG9TdHJpbmciIGNvbnRleHQ9IlBhdGllbnQiIGFjY2Vzc0xldmVsPSJQdWJsaWMiIHhzaTp0eXBlPSJGdW5jdGlvbkRlZiI+CiAgICAgICAgIDxleHByZXNzaW9uIGxvY2F0b3I9IjE1Mzo0OC0xNTM6NTgiIHBhdGg9InZhbHVlIiB4c2k6dHlwZT0iUHJvcGVydHkiPgogICAgICAgICAgICA8c291cmNlIGxvY2F0b3I9IjE1Mzo0OC0xNTM6NTIiIG5hbWU9InZhbHVlIiB4c2k6dHlwZT0iT3BlcmFuZFJlZiIvPgogICAgICAgICA8L2V4cHJlc3Npb24+CiAgICAgICAgIDxvcGVyYW5kIG5hbWU9InZhbHVlIj4KICAgICAgICAgICAgPG9wZXJhbmRUeXBlU3BlY2lmaWVyIGxvY2F0b3I9IjE1MzozMi0xNTM6NDQiIG5hbWU9ImZoaXI6RXhwb3N1cmVTdGF0ZSIgeHNpOnR5cGU9Ik5hbWVkVHlwZVNwZWNpZmllciIvPgogICAgICAgICA8L29wZXJhbmQ+CiAgICAgIDwvZGVmPgogICAgICA8ZGVmIGxvY2F0b3I9IjE1NDoxLTE1NDo2NSIgbmFtZT0iVG9TdHJpbmciIGNvbnRleHQ9IlBhdGllbnQiIGFjY2Vzc0xldmVsPSJQdWJsaWMiIHhzaTp0eXBlPSJGdW5jdGlvbkRlZiI+CiAgICAgICAgIDxleHByZXNzaW9uIGxvY2F0b3I9IjE1NDo1NS0xNTQ6NjUiIHBhdGg9InZhbHVlIiB4c2k6dHlwZT0iUHJvcGVydHkiPgogICAgICAgICAgICA8c291cmNlIGxvY2F0b3I9IjE1NDo1NS0xNTQ6NTkiIG5hbWU9InZhbHVlIiB4c2k6dHlwZT0iT3BlcmFuZFJlZiIvPgogICAgICAgICA8L2V4cHJlc3Npb24+CiAgICAgICAgIDxvcGVyYW5kIG5hbWU9InZhbHVlIj4KICAgICAgICAgICAgPG9wZXJhbmRUeXBlU3BlY2lmaWVyIGxvY2F0b3I9IjE1NDozMi0xNTQ6NTEiIG5hbWU9ImZoaXI6RXh0ZW5zaW9uQ29udGV4dFR5cGUiIHhzaTp0eXBlPSJOYW1lZFR5cGVTcGVjaWZpZXIiLz4KICAgICAgICAgPC9vcGVyYW5kPgogICAgICA8L2RlZj4KICAgICAgPGRlZiBsb2NhdG9yPSIxNTU6MS0xNTU6NTciIG5hbWU9IlRvU3RyaW5nIiBjb250ZXh0PSJQYXRpZW50IiBhY2Nlc3NMZXZlbD0iUHVibGljIiB4c2k6dHlwZT0iRnVuY3Rpb25EZWYiPgogICAgICAgICA8ZXhwcmVzc2lvbiBsb2NhdG9yPSIxNTU6NDctMTU1OjU3IiBwYXRoPSJ2YWx1ZSIgeHNpOnR5cGU9IlByb3BlcnR5Ij4KICAgICAgICAgICAgPHNvdXJjZSBsb2NhdG9yPSIxNTU6NDctMTU1OjUxIiBuYW1lPSJ2YWx1ZSIgeHNpOnR5cGU9Ik9wZXJhbmRSZWYiLz4KICAgICAgICAgPC9leHByZXNzaW9uPgogICAgICAgICA8b3BlcmFuZCBuYW1lPSJ2YWx1ZSI+CiAgICAgICAgICAgIDxvcGVyYW5kVHlwZVNwZWNpZmllciBsb2NhdG9yPSIxNTU6MzItMTU1OjQzIiBuYW1lPSJmaGlyOkZISVJBbGxUeXBlcyIgeHNpOnR5cGU9Ik5hbWVkVHlwZVNwZWNpZmllciIvPgogICAgICAgICA8L29wZXJhbmQ+CiAgICAgIDwvZGVmPgogICAgICA8ZGVmIGxvY2F0b3I9IjE1NjoxLTE1Njo2MCIgbmFtZT0iVG9TdHJpbmciIGNvbnRleHQ9IlBhdGllbnQiIGFjY2Vzc0xldmVsPSJQdWJsaWMiIHhzaTp0eXBlPSJGdW5jdGlvbkRlZiI+CiAgICAgICAgIDxleHByZXNzaW9uIGxvY2F0b3I9IjE1Njo1MC0xNTY6NjAiIHBhdGg9InZhbHVlIiB4c2k6dHlwZT0iUHJvcGVydHkiPgogICAgICAgICAgICA8c291cmNlIGxvY2F0b3I9IjE1Njo1MC0xNTY6NTQiIG5hbWU9InZhbHVlIiB4c2k6dHlwZT0iT3BlcmFuZFJlZiIvPgogICAgICAgICA8L2V4cHJlc3Npb24+CiAgICAgICAgIDxvcGVyYW5kIG5hbWU9InZhbHVlIj4KICAgICAgICAgICAgPG9wZXJhbmRUeXBlU3BlY2lmaWVyIGxvY2F0b3I9IjE1NjozMi0xNTY6NDYiIG5hbWU9ImZoaXI6RkhJUkRlZmluZWRUeXBlIiB4c2k6dHlwZT0iTmFtZWRUeXBlU3BlY2lmaWVyIi8+CiAgICAgICAgIDwvb3BlcmFuZD4KICAgICAgPC9kZWY+CiAgICAgIDxkZWYgbG9jYXRvcj0iMTU3OjEtMTU3OjYxIiBuYW1lPSJUb1N0cmluZyIgY29udGV4dD0iUGF0aWVudCIgYWNjZXNzTGV2ZWw9IlB1YmxpYyIgeHNpOnR5cGU9IkZ1bmN0aW9uRGVmIj4KICAgICAgICAgPGV4cHJlc3Npb24gbG9jYXRvcj0iMTU3OjUxLTE1Nzo2MSIgcGF0aD0idmFsdWUiIHhzaTp0eXBlPSJQcm9wZXJ0eSI+CiAgICAgICAgICAgIDxzb3VyY2UgbG9jYXRvcj0iMTU3OjUxLTE1Nzo1NSIgbmFtZT0idmFsdWUiIHhzaTp0eXBlPSJPcGVyYW5kUmVmIi8+CiAgICAgICAgIDwvZXhwcmVzc2lvbj4KICAgICAgICAgPG9wZXJhbmQgbmFtZT0idmFsdWUiPgogICAgICAgICAgICA8b3BlcmFuZFR5cGVTcGVjaWZpZXIgbG9jYXRvcj0iMTU3OjMyLTE1Nzo0NyIgbmFtZT0iZmhpcjpGSElSRGV2aWNlU3RhdHVzIiB4c2k6dHlwZT0iTmFtZWRUeXBlU3BlY2lmaWVyIi8+CiAgICAgICAgIDwvb3BlcmFuZD4KICAgICAgPC9kZWY+CiAgICAgIDxkZWYgbG9jYXRvcj0iMTU4OjEtMTU4OjYxIiBuYW1lPSJUb1N0cmluZyIgY29udGV4dD0iUGF0aWVudCIgYWNjZXNzTGV2ZWw9IlB1YmxpYyIgeHNpOnR5cGU9IkZ1bmN0aW9uRGVmIj4KICAgICAgICAgPGV4cHJlc3Npb24gbG9jYXRvcj0iMTU4OjUxLTE1ODo2MSIgcGF0aD0idmFsdWUiIHhzaTp0eXBlPSJQcm9wZXJ0eSI+CiAgICAgICAgICAgIDxzb3VyY2UgbG9jYXRvcj0iMTU4OjUxLTE1ODo1NSIgbmFtZT0idmFsdWUiIHhzaTp0eXBlPSJPcGVyYW5kUmVmIi8+CiAgICAgICAgIDwvZXhwcmVzc2lvbj4KICAgICAgICAgPG9wZXJhbmQgbmFtZT0idmFsdWUiPgogICAgICAgICAgICA8b3BlcmFuZFR5cGVTcGVjaWZpZXIgbG9jYXRvcj0iMTU4OjMyLTE1ODo0NyIgbmFtZT0iZmhpcjpGSElSUmVzb3VyY2VUeXBlIiB4c2k6dHlwZT0iTmFtZWRUeXBlU3BlY2lmaWVyIi8+CiAgICAgICAgIDwvb3BlcmFuZD4KICAgICAgPC9kZWY+CiAgICAgIDxkZWYgbG9jYXRvcj0iMTU5OjEtMTU5OjY0IiBuYW1lPSJUb1N0cmluZyIgY29udGV4dD0iUGF0aWVudCIgYWNjZXNzTGV2ZWw9IlB1YmxpYyIgeHNpOnR5cGU9IkZ1bmN0aW9uRGVmIj4KICAgICAgICAgPGV4cHJlc3Npb24gbG9jYXRvcj0iMTU5OjU0LTE1OTo2NCIgcGF0aD0idmFsdWUiIHhzaTp0eXBlPSJQcm9wZXJ0eSI+CiAgICAgICAgICAgIDxzb3VyY2UgbG9jYXRvcj0iMTU5OjU0LTE1OTo1OCIgbmFtZT0idmFsdWUiIHhzaTp0eXBlPSJPcGVyYW5kUmVmIi8+CiAgICAgICAgIDwvZXhwcmVzc2lvbj4KICAgICAgICAgPG9wZXJhbmQgbmFtZT0idmFsdWUiPgogICAgICAgICAgICA8b3BlcmFuZFR5cGVTcGVjaWZpZXIgbG9jYXRvcj0iMTU5OjMyLTE1OTo1MCIgbmFtZT0iZmhpcjpGSElSU3Vic3RhbmNlU3RhdHVzIiB4c2k6dHlwZT0iTmFtZWRUeXBlU3BlY2lmaWVyIi8+CiAgICAgICAgIDwvb3BlcmFuZD4KICAgICAgPC9kZWY+CiAgICAgIDxkZWYgbG9jYXRvcj0iMTYwOjEtMTYwOjU2IiBuYW1lPSJUb1N0cmluZyIgY29udGV4dD0iUGF0aWVudCIgYWNjZXNzTGV2ZWw9IlB1YmxpYyIgeHNpOnR5cGU9IkZ1bmN0aW9uRGVmIj4KICAgICAgICAgPGV4cHJlc3Npb24gbG9jYXRvcj0iMTYwOjQ2LTE2MDo1NiIgcGF0aD0idmFsdWUiIHhzaTp0eXBlPSJQcm9wZXJ0eSI+CiAgICAgICAgICAgIDxzb3VyY2UgbG9jYXRvcj0iMTYwOjQ2LTE2MDo1MCIgbmFtZT0idmFsdWUiIHhzaTp0eXBlPSJPcGVyYW5kUmVmIi8+CiAgICAgICAgIDwvZXhwcmVzc2lvbj4KICAgICAgICAgPG9wZXJhbmQgbmFtZT0idmFsdWUiPgogICAgICAgICAgICA8b3BlcmFuZFR5cGVTcGVjaWZpZXIgbG9jYXRvcj0iMTYwOjMyLTE2MDo0MiIgbmFtZT0iZmhpcjpGSElSVmVyc2lvbiIgeHNpOnR5cGU9Ik5hbWVkVHlwZVNwZWNpZmllciIvPgogICAgICAgICA8L29wZXJhbmQ+CiAgICAgIDwvZGVmPgogICAgICA8ZGVmIGxvY2F0b3I9IjE2MToxLTE2MTo2NCIgbmFtZT0iVG9TdHJpbmciIGNvbnRleHQ9IlBhdGllbnQiIGFjY2Vzc0xldmVsPSJQdWJsaWMiIHhzaTp0eXBlPSJGdW5jdGlvbkRlZiI+CiAgICAgICAgIDxleHByZXNzaW9uIGxvY2F0b3I9IjE2MTo1NC0xNjE6NjQiIHBhdGg9InZhbHVlIiB4c2k6dHlwZT0iUHJvcGVydHkiPgogICAgICAgICAgICA8c291cmNlIGxvY2F0b3I9IjE2MTo1NC0xNjE6NTgiIG5hbWU9InZhbHVlIiB4c2k6dHlwZT0iT3BlcmFuZFJlZiIvPgogICAgICAgICA8L2V4cHJlc3Npb24+CiAgICAgICAgIDxvcGVyYW5kIG5hbWU9InZhbHVlIj4KICAgICAgICAgICAgPG9wZXJhbmRUeXBlU3BlY2lmaWVyIGxvY2F0b3I9IjE2MTozMi0xNjE6NTAiIG5hbWU9ImZoaXI6RmFtaWx5SGlzdG9yeVN0YXR1cyIgeHNpOnR5cGU9Ik5hbWVkVHlwZVNwZWNpZmllciIvPgogICAgICAgICA8L29wZXJhbmQ+CiAgICAgIDwvZGVmPgogICAgICA8ZGVmIGxvY2F0b3I9IjE2MjoxLTE2Mjo1OSIgbmFtZT0iVG9TdHJpbmciIGNvbnRleHQ9IlBhdGllbnQiIGFjY2Vzc0xldmVsPSJQdWJsaWMiIHhzaTp0eXBlPSJGdW5jdGlvbkRlZiI+CiAgICAgICAgIDxleHByZXNzaW9uIGxvY2F0b3I9IjE2Mjo0OS0xNjI6NTkiIHBhdGg9InZhbHVlIiB4c2k6dHlwZT0iUHJvcGVydHkiPgogICAgICAgICAgICA8c291cmNlIGxvY2F0b3I9IjE2Mjo0OS0xNjI6NTMiIG5hbWU9InZhbHVlIiB4c2k6dHlwZT0iT3BlcmFuZFJlZiIvPgogICAgICAgICA8L2V4cHJlc3Npb24+CiAgICAgICAgIDxvcGVyYW5kIG5hbWU9InZhbHVlIj4KICAgICAgICAgICAgPG9wZXJhbmRUeXBlU3BlY2lmaWVyIGxvY2F0b3I9IjE2MjozMi0xNjI6NDUiIG5hbWU9ImZoaXI6RmlsdGVyT3BlcmF0b3IiIHhzaTp0eXBlPSJOYW1lZFR5cGVTcGVjaWZpZXIiLz4KICAgICAgICAgPC9vcGVyYW5kPgogICAgICA8L2RlZj4KICAgICAgPGRlZiBsb2NhdG9yPSIxNjM6MS0xNjM6NTUiIG5hbWU9IlRvU3RyaW5nIiBjb250ZXh0PSJQYXRpZW50IiBhY2Nlc3NMZXZlbD0iUHVibGljIiB4c2k6dHlwZT0iRnVuY3Rpb25EZWYiPgogICAgICAgICA8ZXhwcmVzc2lvbiBsb2NhdG9yPSIxNjM6NDUtMTYzOjU1IiBwYXRoPSJ2YWx1ZSIgeHNpOnR5cGU9IlByb3BlcnR5Ij4KICAgICAgICAgICAgPHNvdXJjZSBsb2NhdG9yPSIxNjM6NDUtMTYzOjQ5IiBuYW1lPSJ2YWx1ZSIgeHNpOnR5cGU9Ik9wZXJhbmRSZWYiLz4KICAgICAgICAgPC9leHByZXNzaW9uPgogICAgICAgICA8b3BlcmFuZCBuYW1lPSJ2YWx1ZSI+CiAgICAgICAgICAgIDxvcGVyYW5kVHlwZVNwZWNpZmllciBsb2NhdG9yPSIxNjM6MzItMTYzOjQxIiBuYW1lPSJmaGlyOkZsYWdTdGF0dXMiIHhzaTp0eXBlPSJOYW1lZFR5cGVTcGVjaWZpZXIiLz4KICAgICAgICAgPC9vcGVyYW5kPgogICAgICA8L2RlZj4KICAgICAgPGRlZiBsb2NhdG9yPSIxNjQ6MS0xNjQ6NjQiIG5hbWU9IlRvU3RyaW5nIiBjb250ZXh0PSJQYXRpZW50IiBhY2Nlc3NMZXZlbD0iUHVibGljIiB4c2k6dHlwZT0iRnVuY3Rpb25EZWYiPgogICAgICAgICA8ZXhwcmVzc2lvbiBsb2NhdG9yPSIxNjQ6NTQtMTY0OjY0IiBwYXRoPSJ2YWx1ZSIgeHNpOnR5cGU9IlByb3BlcnR5Ij4KICAgICAgICAgICAgPHNvdXJjZSBsb2NhdG9yPSIxNjQ6NTQtMTY0OjU4IiBuYW1lPSJ2YWx1ZSIgeHNpOnR5cGU9Ik9wZXJhbmRSZWYiLz4KICAgICAgICAgPC9leHByZXNzaW9uPgogICAgICAgICA8b3BlcmFuZCBuYW1lPSJ2YWx1ZSI+CiAgICAgICAgICAgIDxvcGVyYW5kVHlwZVNwZWNpZmllciBsb2NhdG9yPSIxNjQ6MzItMTY0OjUwIiBuYW1lPSJmaGlyOkdvYWxMaWZlY3ljbGVTdGF0dXMiIHhzaTp0eXBlPSJOYW1lZFR5cGVTcGVjaWZpZXIiLz4KICAgICAgICAgPC9vcGVyYW5kPgogICAgICA8L2RlZj4KICAgICAgPGRlZiBsb2NhdG9yPSIxNjU6MS0xNjU6NjUiIG5hbWU9IlRvU3RyaW5nIiBjb250ZXh0PSJQYXRpZW50IiBhY2Nlc3NMZXZlbD0iUHVibGljIiB4c2k6dHlwZT0iRnVuY3Rpb25EZWYiPgogICAgICAgICA8ZXhwcmVzc2lvbiBsb2NhdG9yPSIxNjU6NTUtMTY1OjY1IiBwYXRoPSJ2YWx1ZSIgeHNpOnR5cGU9IlByb3BlcnR5Ij4KICAgICAgICAgICAgPHNvdXJjZSBsb2NhdG9yPSIxNjU6NTUtMTY1OjU5IiBuYW1lPSJ2YWx1ZSIgeHNpOnR5cGU9Ik9wZXJhbmRSZWYiLz4KICAgICAgICAgPC9leHByZXNzaW9uPgogICAgICAgICA8b3BlcmFuZCBuYW1lPSJ2YWx1ZSI+CiAgICAgICAgICAgIDxvcGVyYW5kVHlwZVNwZWNpZmllciBsb2NhdG9yPSIxNjU6MzItMTY1OjUxIiBuYW1lPSJmaGlyOkdyYXBoQ29tcGFydG1lbnRSdWxlIiB4c2k6dHlwZT0iTmFtZWRUeXBlU3BlY2lmaWVyIi8+CiAgICAgICAgIDwvb3BlcmFuZD4KICAgICAgPC9kZWY+CiAgICAgIDxkZWYgbG9jYXRvcj0iMTY2OjEtMTY2OjY0IiBuYW1lPSJUb1N0cmluZyIgY29udGV4dD0iUGF0aWVudCIgYWNjZXNzTGV2ZWw9IlB1YmxpYyIgeHNpOnR5cGU9IkZ1bmN0aW9uRGVmIj4KICAgICAgICAgPGV4cHJlc3Npb24gbG9jYXRvcj0iMTY2OjU0LTE2Njo2NCIgcGF0aD0idmFsdWUiIHhzaTp0eXBlPSJQcm9wZXJ0eSI+CiAgICAgICAgICAgIDxzb3VyY2UgbG9jYXRvcj0iMTY2OjU0LTE2Njo1OCIgbmFtZT0idmFsdWUiIHhzaTp0eXBlPSJPcGVyYW5kUmVmIi8+CiAgICAgICAgIDwvZXhwcmVzc2lvbj4KICAgICAgICAgPG9wZXJhbmQgbmFtZT0idmFsdWUiPgogICAgICAgICAgICA8b3BlcmFuZFR5cGVTcGVjaWZpZXIgbG9jYXRvcj0iMTY2OjMyLTE2Njo1MCIgbmFtZT0iZmhpcjpHcmFwaENvbXBhcnRtZW50VXNlIiB4c2k6dHlwZT0iTmFtZWRUeXBlU3BlY2lmaWVyIi8+CiAgICAgICAgIDwvb3BlcmFuZD4KICAgICAgPC9kZWY+CiAgICAgIDxkZWYgbG9jYXRvcj0iMTY3OjEtMTY3OjU3IiBuYW1lPSJUb1N0cmluZyIgY29udGV4dD0iUGF0aWVudCIgYWNjZXNzTGV2ZWw9IlB1YmxpYyIgeHNpOnR5cGU9IkZ1bmN0aW9uRGVmIj4KICAgICAgICAgPGV4cHJlc3Npb24gbG9jYXRvcj0iMTY3OjQ3LTE2Nzo1NyIgcGF0aD0idmFsdWUiIHhzaTp0eXBlPSJQcm9wZXJ0eSI+CiAgICAgICAgICAgIDxzb3VyY2UgbG9jYXRvcj0iMTY3OjQ3LTE2Nzo1MSIgbmFtZT0idmFsdWUiIHhzaTp0eXBlPSJPcGVyYW5kUmVmIi8+CiAgICAgICAgIDwvZXhwcmVzc2lvbj4KICAgICAgICAgPG9wZXJhbmQgbmFtZT0idmFsdWUiPgogICAgICAgICAgICA8b3BlcmFuZFR5cGVTcGVjaWZpZXIgbG9jYXRvcj0iMTY3OjMyLTE2Nzo0MyIgbmFtZT0iZmhpcjpHcm91cE1lYXN1cmUiIHhzaTp0eXBlPSJOYW1lZFR5cGVTcGVjaWZpZXIiLz4KICAgICAgICAgPC9vcGVyYW5kPgogICAgICA8L2RlZj4KICAgICAgPGRlZiBsb2NhdG9yPSIxNjg6MS0xNjg6NTQiIG5hbWU9IlRvU3RyaW5nIiBjb250ZXh0PSJQYXRpZW50IiBhY2Nlc3NMZXZlbD0iUHVibGljIiB4c2k6dHlwZT0iRnVuY3Rpb25EZWYiPgogICAgICAgICA8ZXhwcmVzc2lvbiBsb2NhdG9yPSIxNjg6NDQtMTY4OjU0IiBwYXRoPSJ2YWx1ZSIgeHNpOnR5cGU9IlByb3BlcnR5Ij4KICAgICAgICAgICAgPHNvdXJjZSBsb2NhdG9yPSIxNjg6NDQtMTY4OjQ4IiBuYW1lPSJ2YWx1ZSIgeHNpOnR5cGU9Ik9wZXJhbmRSZWYiLz4KICAgICAgICAgPC9leHByZXNzaW9uPgogICAgICAgICA8b3BlcmFuZCBuYW1lPSJ2YWx1ZSI+CiAgICAgICAgICAgIDxvcGVyYW5kVHlwZVNwZWNpZmllciBsb2NhdG9yPSIxNjg6MzItMTY4OjQwIiBuYW1lPSJmaGlyOkdyb3VwVHlwZSIgeHNpOnR5cGU9Ik5hbWVkVHlwZVNwZWNpZmllciIvPgogICAgICAgICA8L29wZXJhbmQ+CiAgICAgIDwvZGVmPgogICAgICA8ZGVmIGxvY2F0b3I9IjE2OToxLTE2OTo2NyIgbmFtZT0iVG9TdHJpbmciIGNvbnRleHQ9IlBhdGllbnQiIGFjY2Vzc0xldmVsPSJQdWJsaWMiIHhzaTp0eXBlPSJGdW5jdGlvbkRlZiI+CiAgICAgICAgIDxleHByZXNzaW9uIGxvY2F0b3I9IjE2OTo1Ny0xNjk6NjciIHBhdGg9InZhbHVlIiB4c2k6dHlwZT0iUHJvcGVydHkiPgogICAgICAgICAgICA8c291cmNlIGxvY2F0b3I9IjE2OTo1Ny0xNjk6NjEiIG5hbWU9InZhbHVlIiB4c2k6dHlwZT0iT3BlcmFuZFJlZiIvPgogICAgICAgICA8L2V4cHJlc3Npb24+CiAgICAgICAgIDxvcGVyYW5kIG5hbWU9InZhbHVlIj4KICAgICAgICAgICAgPG9wZXJhbmRUeXBlU3BlY2lmaWVyIGxvY2F0b3I9IjE2OTozMi0xNjk6NTMiIG5hbWU9ImZoaXI6R3VpZGFuY2VSZXNwb25zZVN0YXR1cyIgeHNpOnR5cGU9Ik5hbWVkVHlwZVNwZWNpZmllciIvPgogICAgICAgICA8L29wZXJhbmQ+CiAgICAgIDwvZGVmPgogICAgICA8ZGVmIGxvY2F0b3I9IjE3MDoxLTE3MDo2NCIgbmFtZT0iVG9TdHJpbmciIGNvbnRleHQ9IlBhdGllbnQiIGFjY2Vzc0xldmVsPSJQdWJsaWMiIHhzaTp0eXBlPSJGdW5jdGlvbkRlZiI+CiAgICAgICAgIDxleHByZXNzaW9uIGxvY2F0b3I9IjE3MDo1NC0xNzA6NjQiIHBhdGg9InZhbHVlIiB4c2k6dHlwZT0iUHJvcGVydHkiPgogICAgICAgICAgICA8c291cmNlIGxvY2F0b3I9IjE3MDo1NC0xNzA6NTgiIG5hbWU9InZhbHVlIiB4c2k6dHlwZT0iT3BlcmFuZFJlZiIvPgogICAgICAgICA8L2V4cHJlc3Npb24+CiAgICAgICAgIDxvcGVyYW5kIG5hbWU9InZhbHVlIj4KICAgICAgICAgICAgPG9wZXJhbmRUeXBlU3BlY2lmaWVyIGxvY2F0b3I9IjE3MDozMi0xNzA6NTAiIG5hbWU9ImZoaXI6R3VpZGVQYWdlR2VuZXJhdGlvbiIgeHNpOnR5cGU9Ik5hbWVkVHlwZVNwZWNpZmllciIvPgogICAgICAgICA8L29wZXJhbmQ+CiAgICAgIDwvZGVmPgogICAgICA8ZGVmIGxvY2F0b3I9IjE3MToxLTE3MTo2MyIgbmFtZT0iVG9TdHJpbmciIGNvbnRleHQ9IlBhdGllbnQiIGFjY2Vzc0xldmVsPSJQdWJsaWMiIHhzaTp0eXBlPSJGdW5jdGlvbkRlZiI+CiAgICAgICAgIDxleHByZXNzaW9uIGxvY2F0b3I9IjE3MTo1My0xNzE6NjMiIHBhdGg9InZhbHVlIiB4c2k6dHlwZT0iUHJvcGVydHkiPgogICAgICAgICAgICA8c291cmNlIGxvY2F0b3I9IjE3MTo1My0xNzE6NTciIG5hbWU9InZhbHVlIiB4c2k6dHlwZT0iT3BlcmFuZFJlZiIvPgogICAgICAgICA8L2V4cHJlc3Npb24+CiAgICAgICAgIDxvcGVyYW5kIG5hbWU9InZhbHVlIj4KICAgICAgICAgICAgPG9wZXJhbmRUeXBlU3BlY2lmaWVyIGxvY2F0b3I9IjE3MTozMi0xNzE6NDkiIG5hbWU9ImZoaXI6R3VpZGVQYXJhbWV0ZXJDb2RlIiB4c2k6dHlwZT0iTmFtZWRUeXBlU3BlY2lmaWVyIi8+CiAgICAgICAgIDwvb3BlcmFuZD4KICAgICAgPC9kZWY+CiAgICAgIDxkZWYgbG9jYXRvcj0iMTcyOjEtMTcyOjUzIiBuYW1lPSJUb1N0cmluZyIgY29udGV4dD0iUGF0aWVudCIgYWNjZXNzTGV2ZWw9IlB1YmxpYyIgeHNpOnR5cGU9IkZ1bmN0aW9uRGVmIj4KICAgICAgICAgPGV4cHJlc3Npb24gbG9jYXRvcj0iMTcyOjQzLTE3Mjo1MyIgcGF0aD0idmFsdWUiIHhzaTp0eXBlPSJQcm9wZXJ0eSI+CiAgICAgICAgICAgIDxzb3VyY2UgbG9jYXRvcj0iMTcyOjQzLTE3Mjo0NyIgbmFtZT0idmFsdWUiIHhzaTp0eXBlPSJPcGVyYW5kUmVmIi8+CiAgICAgICAgIDwvZXhwcmVzc2lvbj4KICAgICAgICAgPG9wZXJhbmQgbmFtZT0idmFsdWUiPgogICAgICAgICAgICA8b3BlcmFuZFR5cGVTcGVjaWZpZXIgbG9jYXRvcj0iMTcyOjMyLTE3MjozOSIgbmFtZT0iZmhpcjpIVFRQVmVyYiIgeHNpOnR5cGU9Ik5hbWVkVHlwZVNwZWNpZmllciIvPgogICAgICAgICA8L29wZXJhbmQ+CiAgICAgIDwvZGVmPgogICAgICA8ZGVmIGxvY2F0b3I9IjE3MzoxLTE3Mzo1OCIgbmFtZT0iVG9TdHJpbmciIGNvbnRleHQ9IlBhdGllbnQiIGFjY2Vzc0xldmVsPSJQdWJsaWMiIHhzaTp0eXBlPSJGdW5jdGlvbkRlZiI+CiAgICAgICAgIDxleHByZXNzaW9uIGxvY2F0b3I9IjE3Mzo0OC0xNzM6NTgiIHBhdGg9InZhbHVlIiB4c2k6dHlwZT0iUHJvcGVydHkiPgogICAgICAgICAgICA8c291cmNlIGxvY2F0b3I9IjE3Mzo0OC0xNzM6NTIiIG5hbWU9InZhbHVlIiB4c2k6dHlwZT0iT3BlcmFuZFJlZiIvPgogICAgICAgICA8L2V4cHJlc3Npb24+CiAgICAgICAgIDxvcGVyYW5kIG5hbWU9InZhbHVlIj4KICAgICAgICAgICAgPG9wZXJhbmRUeXBlU3BlY2lmaWVyIGxvY2F0b3I9IjE3MzozMi0xNzM6NDQiIG5hbWU9ImZoaXI6SWRlbnRpZmllclVzZSIgeHNpOnR5cGU9Ik5hbWVkVHlwZVNwZWNpZmllciIvPgogICAgICAgICA8L29wZXJhbmQ+CiAgICAgIDwvZGVmPgogICAgICA8ZGVmIGxvY2F0b3I9IjE3NDoxLTE3NDo2NyIgbmFtZT0iVG9TdHJpbmciIGNvbnRleHQ9IlBhdGllbnQiIGFjY2Vzc0xldmVsPSJQdWJsaWMiIHhzaTp0eXBlPSJGdW5jdGlvbkRlZiI+CiAgICAgICAgIDxleHByZXNzaW9uIGxvY2F0b3I9IjE3NDo1Ny0xNzQ6NjciIHBhdGg9InZhbHVlIiB4c2k6dHlwZT0iUHJvcGVydHkiPgogICAgICAgICAgICA8c291cmNlIGxvY2F0b3I9IjE3NDo1Ny0xNzQ6NjEiIG5hbWU9InZhbHVlIiB4c2k6dHlwZT0iT3BlcmFuZFJlZiIvPgogICAgICAgICA8L2V4cHJlc3Npb24+CiAgICAgICAgIDxvcGVyYW5kIG5hbWU9InZhbHVlIj4KICAgICAgICAgICAgPG9wZXJhbmRUeXBlU3BlY2lmaWVyIGxvY2F0b3I9IjE3NDozMi0xNzQ6NTMiIG5hbWU9ImZoaXI6SWRlbnRpdHlBc3N1cmFuY2VMZXZlbCIgeHNpOnR5cGU9Ik5hbWVkVHlwZVNwZWNpZmllciIvPgogICAgICAgICA8L29wZXJhbmQ+CiAgICAgIDwvZGVmPgogICAgICA8ZGVmIGxvY2F0b3I9IjE3NToxLTE3NTo2MyIgbmFtZT0iVG9TdHJpbmciIGNvbnRleHQ9IlBhdGllbnQiIGFjY2Vzc0xldmVsPSJQdWJsaWMiIHhzaTp0eXBlPSJGdW5jdGlvbkRlZiI+CiAgICAgICAgIDxleHByZXNzaW9uIGxvY2F0b3I9IjE3NTo1My0xNzU6NjMiIHBhdGg9InZhbHVlIiB4c2k6dHlwZT0iUHJvcGVydHkiPgogICAgICAgICAgICA8c291cmNlIGxvY2F0b3I9IjE3NTo1My0xNzU6NTciIG5hbWU9InZhbHVlIiB4c2k6dHlwZT0iT3BlcmFuZFJlZiIvPgogICAgICAgICA8L2V4cHJlc3Npb24+CiAgICAgICAgIDxvcGVyYW5kIG5hbWU9InZhbHVlIj4KICAgICAgICAgICAgPG9wZXJhbmRUeXBlU3BlY2lmaWVyIGxvY2F0b3I9IjE3NTozMi0xNzU6NDkiIG5hbWU9ImZoaXI6SW1hZ2luZ1N0dWR5U3RhdHVzIiB4c2k6dHlwZT0iTmFtZWRUeXBlU3BlY2lmaWVyIi8+CiAgICAgICAgIDwvb3BlcmFuZD4KICAgICAgPC9kZWY+CiAgICAgIDxkZWYgbG9jYXRvcj0iMTc2OjEtMTc2OjczIiBuYW1lPSJUb1N0cmluZyIgY29udGV4dD0iUGF0aWVudCIgYWNjZXNzTGV2ZWw9IlB1YmxpYyIgeHNpOnR5cGU9IkZ1bmN0aW9uRGVmIj4KICAgICAgICAgPGV4cHJlc3Npb24gbG9jYXRvcj0iMTc2OjYzLTE3Njo3MyIgcGF0aD0idmFsdWUiIHhzaTp0eXBlPSJQcm9wZXJ0eSI+CiAgICAgICAgICAgIDxzb3VyY2UgbG9jYXRvcj0iMTc2OjYzLTE3Njo2NyIgbmFtZT0idmFsdWUiIHhzaTp0eXBlPSJPcGVyYW5kUmVmIi8+CiAgICAgICAgIDwvZXhwcmVzc2lvbj4KICAgICAgICAgPG9wZXJhbmQgbmFtZT0idmFsdWUiPgogICAgICAgICAgICA8b3BlcmFuZFR5cGVTcGVjaWZpZXIgbG9jYXRvcj0iMTc2OjMyLTE3Njo1OSIgbmFtZT0iZmhpcjpJbW11bml6YXRpb25FdmFsdWF0aW9uU3RhdHVzIiB4c2k6dHlwZT0iTmFtZWRUeXBlU3BlY2lmaWVyIi8+CiAgICAgICAgIDwvb3BlcmFuZD4KICAgICAgPC9kZWY+CiAgICAgIDxkZWYgbG9jYXRvcj0iMTc3OjEtMTc3OjYzIiBuYW1lPSJUb1N0cmluZyIgY29udGV4dD0iUGF0aWVudCIgYWNjZXNzTGV2ZWw9IlB1YmxpYyIgeHNpOnR5cGU9IkZ1bmN0aW9uRGVmIj4KICAgICAgICAgPGV4cHJlc3Npb24gbG9jYXRvcj0iMTc3OjUzLTE3Nzo2MyIgcGF0aD0idmFsdWUiIHhzaTp0eXBlPSJQcm9wZXJ0eSI+CiAgICAgICAgICAgIDxzb3VyY2UgbG9jYXRvcj0iMTc3OjUzLTE3Nzo1NyIgbmFtZT0idmFsdWUiIHhzaTp0eXBlPSJPcGVyYW5kUmVmIi8+CiAgICAgICAgIDwvZXhwcmVzc2lvbj4KICAgICAgICAgPG9wZXJhbmQgbmFtZT0idmFsdWUiPgogICAgICAgICAgICA8b3BlcmFuZFR5cGVTcGVjaWZpZXIgbG9jYXRvcj0iMTc3OjMyLTE3Nzo0OSIgbmFtZT0iZmhpcjpJbW11bml6YXRpb25TdGF0dXMiIHhzaTp0eXBlPSJOYW1lZFR5cGVTcGVjaWZpZXIiLz4KICAgICAgICAgPC9vcGVyYW5kPgogICAgICA8L2RlZj4KICAgICAgPGRlZiBsb2NhdG9yPSIxNzg6MS0xNzg6NzAiIG5hbWU9IlRvU3RyaW5nIiBjb250ZXh0PSJQYXRpZW50IiBhY2Nlc3NMZXZlbD0iUHVibGljIiB4c2k6dHlwZT0iRnVuY3Rpb25EZWYiPgogICAgICAgICA8ZXhwcmVzc2lvbiBsb2NhdG9yPSIxNzg6NjAtMTc4OjcwIiBwYXRoPSJ2YWx1ZSIgeHNpOnR5cGU9IlByb3BlcnR5Ij4KICAgICAgICAgICAgPHNvdXJjZSBsb2NhdG9yPSIxNzg6NjAtMTc4OjY0IiBuYW1lPSJ2YWx1ZSIgeHNpOnR5cGU9Ik9wZXJhbmRSZWYiLz4KICAgICAgICAgPC9leHByZXNzaW9uPgogICAgICAgICA8b3BlcmFuZCBuYW1lPSJ2YWx1ZSI+CiAgICAgICAgICAgIDxvcGVyYW5kVHlwZVNwZWNpZmllciBsb2NhdG9yPSIxNzg6MzItMTc4OjU2IiBuYW1lPSJmaGlyOkludm9pY2VQcmljZUNvbXBvbmVudFR5cGUiIHhzaTp0eXBlPSJOYW1lZFR5cGVTcGVjaWZpZXIiLz4KICAgICAgICAgPC9vcGVyYW5kPgogICAgICA8L2RlZj4KICAgICAgPGRlZiBsb2NhdG9yPSIxNzk6MS0xNzk6NTgiIG5hbWU9IlRvU3RyaW5nIiBjb250ZXh0PSJQYXRpZW50IiBhY2Nlc3NMZXZlbD0iUHVibGljIiB4c2k6dHlwZT0iRnVuY3Rpb25EZWYiPgogICAgICAgICA8ZXhwcmVzc2lvbiBsb2NhdG9yPSIxNzk6NDgtMTc5OjU4IiBwYXRoPSJ2YWx1ZSIgeHNpOnR5cGU9IlByb3BlcnR5Ij4KICAgICAgICAgICAgPHNvdXJjZSBsb2NhdG9yPSIxNzk6NDgtMTc5OjUyIiBuYW1lPSJ2YWx1ZSIgeHNpOnR5cGU9Ik9wZXJhbmRSZWYiLz4KICAgICAgICAgPC9leHByZXNzaW9uPgogICAgICAgICA8b3BlcmFuZCBuYW1lPSJ2YWx1ZSI+CiAgICAgICAgICAgIDxvcGVyYW5kVHlwZVNwZWNpZmllciBsb2NhdG9yPSIxNzk6MzItMTc5OjQ0IiBuYW1lPSJmaGlyOkludm9pY2VTdGF0dXMiIHhzaTp0eXBlPSJOYW1lZFR5cGVTcGVjaWZpZXIiLz4KICAgICAgICAgPC9vcGVyYW5kPgogICAgICA8L2RlZj4KICAgICAgPGRlZiBsb2NhdG9yPSIxODA6MS0xODA6NTgiIG5hbWU9IlRvU3RyaW5nIiBjb250ZXh0PSJQYXRpZW50IiBhY2Nlc3NMZXZlbD0iUHVibGljIiB4c2k6dHlwZT0iRnVuY3Rpb25EZWYiPgogICAgICAgICA8ZXhwcmVzc2lvbiBsb2NhdG9yPSIxODA6NDgtMTgwOjU4IiBwYXRoPSJ2YWx1ZSIgeHNpOnR5cGU9IlByb3BlcnR5Ij4KICAgICAgICAgICAgPHNvdXJjZSBsb2NhdG9yPSIxODA6NDgtMTgwOjUyIiBuYW1lPSJ2YWx1ZSIgeHNpOnR5cGU9Ik9wZXJhbmRSZWYiLz4KICAgICAgICAgPC9leHByZXNzaW9uPgogICAgICAgICA8b3BlcmFuZCBuYW1lPSJ2YWx1ZSI+CiAgICAgICAgICAgIDxvcGVyYW5kVHlwZVNwZWNpZmllciBsb2NhdG9yPSIxODA6MzItMTgwOjQ0IiBuYW1lPSJmaGlyOklzc3VlU2V2ZXJpdHkiIHhzaTp0eXBlPSJOYW1lZFR5cGVTcGVjaWZpZXIiLz4KICAgICAgICAgPC9vcGVyYW5kPgogICAgICA8L2RlZj4KICAgICAgPGRlZiBsb2NhdG9yPSIxODE6MS0xODE6NTQiIG5hbWU9IlRvU3RyaW5nIiBjb250ZXh0PSJQYXRpZW50IiBhY2Nlc3NMZXZlbD0iUHVibGljIiB4c2k6dHlwZT0iRnVuY3Rpb25EZWYiPgogICAgICAgICA8ZXhwcmVzc2lvbiBsb2NhdG9yPSIxODE6NDQtMTgxOjU0IiBwYXRoPSJ2YWx1ZSIgeHNpOnR5cGU9IlByb3BlcnR5Ij4KICAgICAgICAgICAgPHNvdXJjZSBsb2NhdG9yPSIxODE6NDQtMTgxOjQ4IiBuYW1lPSJ2YWx1ZSIgeHNpOnR5cGU9Ik9wZXJhbmRSZWYiLz4KICAgICAgICAgPC9leHByZXNzaW9uPgogICAgICAgICA8b3BlcmFuZCBuYW1lPSJ2YWx1ZSI+CiAgICAgICAgICAgIDxvcGVyYW5kVHlwZVNwZWNpZmllciBsb2NhdG9yPSIxODE6MzItMTgxOjQwIiBuYW1lPSJmaGlyOklzc3VlVHlwZSIgeHNpOnR5cGU9Ik5hbWVkVHlwZVNwZWNpZmllciIvPgogICAgICAgICA8L29wZXJhbmQ+CiAgICAgIDwvZGVmPgogICAgICA8ZGVmIGxvY2F0b3I9IjE4MjoxLTE4Mjo1MyIgbmFtZT0iVG9TdHJpbmciIGNvbnRleHQ9IlBhdGllbnQiIGFjY2Vzc0xldmVsPSJQdWJsaWMiIHhzaTp0eXBlPSJGdW5jdGlvbkRlZiI+CiAgICAgICAgIDxleHByZXNzaW9uIGxvY2F0b3I9IjE4Mjo0My0xODI6NTMiIHBhdGg9InZhbHVlIiB4c2k6dHlwZT0iUHJvcGVydHkiPgogICAgICAgICAgICA8c291cmNlIGxvY2F0b3I9IjE4Mjo0My0xODI6NDciIG5hbWU9InZhbHVlIiB4c2k6dHlwZT0iT3BlcmFuZFJlZiIvPgogICAgICAgICA8L2V4cHJlc3Npb24+CiAgICAgICAgIDxvcGVyYW5kIG5hbWU9InZhbHVlIj4KICAgICAgICAgICAgPG9wZXJhbmRUeXBlU3BlY2lmaWVyIGxvY2F0b3I9IjE4MjozMi0xODI6MzkiIG5hbWU9ImZoaXI6TGlua1R5cGUiIHhzaTp0eXBlPSJOYW1lZFR5cGVTcGVjaWZpZXIiLz4KICAgICAgICAgPC9vcGVyYW5kPgogICAgICA8L2RlZj4KICAgICAgPGRlZiBsb2NhdG9yPSIxODM6MS0xODM6NTYiIG5hbWU9IlRvU3RyaW5nIiBjb250ZXh0PSJQYXRpZW50IiBhY2Nlc3NMZXZlbD0iUHVibGljIiB4c2k6dHlwZT0iRnVuY3Rpb25EZWYiPgogICAgICAgICA8ZXhwcmVzc2lvbiBsb2NhdG9yPSIxODM6NDYtMTgzOjU2IiBwYXRoPSJ2YWx1ZSIgeHNpOnR5cGU9IlByb3BlcnR5Ij4KICAgICAgICAgICAgPHNvdXJjZSBsb2NhdG9yPSIxODM6NDYtMTgzOjUwIiBuYW1lPSJ2YWx1ZSIgeHNpOnR5cGU9Ik9wZXJhbmRSZWYiLz4KICAgICAgICAgPC9leHByZXNzaW9uPgogICAgICAgICA8b3BlcmFuZCBuYW1lPSJ2YWx1ZSI+CiAgICAgICAgICAgIDxvcGVyYW5kVHlwZVNwZWNpZmllciBsb2NhdG9yPSIxODM6MzItMTgzOjQyIiBuYW1lPSJmaGlyOkxpbmthZ2VUeXBlIiB4c2k6dHlwZT0iTmFtZWRUeXBlU3BlY2lmaWVyIi8+CiAgICAgICAgIDwvb3BlcmFuZD4KICAgICAgPC9kZWY+CiAgICAgIDxkZWYgbG9jYXRvcj0iMTg0OjEtMTg0OjUzIiBuYW1lPSJUb1N0cmluZyIgY29udGV4dD0iUGF0aWVudCIgYWNjZXNzTGV2ZWw9IlB1YmxpYyIgeHNpOnR5cGU9IkZ1bmN0aW9uRGVmIj4KICAgICAgICAgPGV4cHJlc3Npb24gbG9jYXRvcj0iMTg0OjQzLTE4NDo1MyIgcGF0aD0idmFsdWUiIHhzaTp0eXBlPSJQcm9wZXJ0eSI+CiAgICAgICAgICAgIDxzb3VyY2UgbG9jYXRvcj0iMTg0OjQzLTE4NDo0NyIgbmFtZT0idmFsdWUiIHhzaTp0eXBlPSJPcGVyYW5kUmVmIi8+CiAgICAgICAgIDwvZXhwcmVzc2lvbj4KICAgICAgICAgPG9wZXJhbmQgbmFtZT0idmFsdWUiPgogICAgICAgICAgICA8b3BlcmFuZFR5cGVTcGVjaWZpZXIgbG9jYXRvcj0iMTg0OjMyLTE4NDozOSIgbmFtZT0iZmhpcjpMaXN0TW9kZSIgeHNpOnR5cGU9Ik5hbWVkVHlwZVNwZWNpZmllciIvPgogICAgICAgICA8L29wZXJhbmQ+CiAgICAgIDwvZGVmPgogICAgICA8ZGVmIGxvY2F0b3I9IjE4NToxLTE4NTo1NSIgbmFtZT0iVG9TdHJpbmciIGNvbnRleHQ9IlBhdGllbnQiIGFjY2Vzc0xldmVsPSJQdWJsaWMiIHhzaTp0eXBlPSJGdW5jdGlvbkRlZiI+CiAgICAgICAgIDxleHByZXNzaW9uIGxvY2F0b3I9IjE4NTo0NS0xODU6NTUiIHBhdGg9InZhbHVlIiB4c2k6dHlwZT0iUHJvcGVydHkiPgogICAgICAgICAgICA8c291cmNlIGxvY2F0b3I9IjE4NTo0NS0xODU6NDkiIG5hbWU9InZhbHVlIiB4c2k6dHlwZT0iT3BlcmFuZFJlZiIvPgogICAgICAgICA8L2V4cHJlc3Npb24+CiAgICAgICAgIDxvcGVyYW5kIG5hbWU9InZhbHVlIj4KICAgICAgICAgICAgPG9wZXJhbmRUeXBlU3BlY2lmaWVyIGxvY2F0b3I9IjE4NTozMi0xODU6NDEiIG5hbWU9ImZoaXI6TGlzdFN0YXR1cyIgeHNpOnR5cGU9Ik5hbWVkVHlwZVNwZWNpZmllciIvPgogICAgICAgICA8L29wZXJhbmQ+CiAgICAgIDwvZGVmPgogICAgICA8ZGVmIGxvY2F0b3I9IjE4NjoxLTE4Njo1NyIgbmFtZT0iVG9TdHJpbmciIGNvbnRleHQ9IlBhdGllbnQiIGFjY2Vzc0xldmVsPSJQdWJsaWMiIHhzaTp0eXBlPSJGdW5jdGlvbkRlZiI+CiAgICAgICAgIDxleHByZXNzaW9uIGxvY2F0b3I9IjE4Njo0Ny0xODY6NTciIHBhdGg9InZhbHVlIiB4c2k6dHlwZT0iUHJvcGVydHkiPgogICAgICAgICAgICA8c291cmNlIGxvY2F0b3I9IjE4Njo0Ny0xODY6NTEiIG5hbWU9InZhbHVlIiB4c2k6dHlwZT0iT3BlcmFuZFJlZiIvPgogICAgICAgICA8L2V4cHJlc3Npb24+CiAgICAgICAgIDxvcGVyYW5kIG5hbWU9InZhbHVlIj4KICAgICAgICAgICAgPG9wZXJhbmRUeXBlU3BlY2lmaWVyIGxvY2F0b3I9IjE4NjozMi0xODY6NDMiIG5hbWU9ImZoaXI6TG9jYXRpb25Nb2RlIiB4c2k6dHlwZT0iTmFtZWRUeXBlU3BlY2lmaWVyIi8+CiAgICAgICAgIDwvb3BlcmFuZD4KICAgICAgPC9kZWY+CiAgICAgIDxkZWYgbG9jYXRvcj0iMTg3OjEtMTg3OjU5IiBuYW1lPSJUb1N0cmluZyIgY29udGV4dD0iUGF0aWVudCIgYWNjZXNzTGV2ZWw9IlB1YmxpYyIgeHNpOnR5cGU9IkZ1bmN0aW9uRGVmIj4KICAgICAgICAgPGV4cHJlc3Npb24gbG9jYXRvcj0iMTg3OjQ5LTE4Nzo1OSIgcGF0aD0idmFsdWUiIHhzaTp0eXBlPSJQcm9wZXJ0eSI+CiAgICAgICAgICAgIDxzb3VyY2UgbG9jYXRvcj0iMTg3OjQ5LTE4Nzo1MyIgbmFtZT0idmFsdWUiIHhzaTp0eXBlPSJPcGVyYW5kUmVmIi8+CiAgICAgICAgIDwvZXhwcmVzc2lvbj4KICAgICAgICAgPG9wZXJhbmQgbmFtZT0idmFsdWUiPgogICAgICAgICAgICA8b3BlcmFuZFR5cGVTcGVjaWZpZXIgbG9jYXRvcj0iMTg3OjMyLTE4Nzo0NSIgbmFtZT0iZmhpcjpMb2NhdGlvblN0YXR1cyIgeHNpOnR5cGU9Ik5hbWVkVHlwZVNwZWNpZmllciIvPgogICAgICAgICA8L29wZXJhbmQ+CiAgICAgIDwvZGVmPgogICAgICA8ZGVmIGxvY2F0b3I9IjE4ODoxLTE4ODo2NCIgbmFtZT0iVG9TdHJpbmciIGNvbnRleHQ9IlBhdGllbnQiIGFjY2Vzc0xldmVsPSJQdWJsaWMiIHhzaTp0eXBlPSJGdW5jdGlvbkRlZiI+CiAgICAgICAgIDxleHByZXNzaW9uIGxvY2F0b3I9IjE4ODo1NC0xODg6NjQiIHBhdGg9InZhbHVlIiB4c2k6dHlwZT0iUHJvcGVydHkiPgogICAgICAgICAgICA8c291cmNlIGxvY2F0b3I9IjE4ODo1NC0xODg6NTgiIG5hbWU9InZhbHVlIiB4c2k6dHlwZT0iT3BlcmFuZFJlZiIvPgogICAgICAgICA8L2V4cHJlc3Npb24+CiAgICAgICAgIDxvcGVyYW5kIG5hbWU9InZhbHVlIj4KICAgICAgICAgICAgPG9wZXJhbmRUeXBlU3BlY2lmaWVyIGxvY2F0b3I9IjE4ODozMi0xODg6NTAiIG5hbWU9ImZoaXI6TWVhc3VyZVJlcG9ydFN0YXR1cyIgeHNpOnR5cGU9Ik5hbWVkVHlwZVNwZWNpZmllciIvPgogICAgICAgICA8L29wZXJhbmQ+CiAgICAgIDwvZGVmPgogICAgICA8ZGVmIGxvY2F0b3I9IjE4OToxLTE4OTo2MiIgbmFtZT0iVG9TdHJpbmciIGNvbnRleHQ9IlBhdGllbnQiIGFjY2Vzc0xldmVsPSJQdWJsaWMiIHhzaTp0eXBlPSJGdW5jdGlvbkRlZiI+CiAgICAgICAgIDxleHByZXNzaW9uIGxvY2F0b3I9IjE4OTo1Mi0xODk6NjIiIHBhdGg9InZhbHVlIiB4c2k6dHlwZT0iUHJvcGVydHkiPgogICAgICAgICAgICA8c291cmNlIGxvY2F0b3I9IjE4OTo1Mi0xODk6NTYiIG5hbWU9InZhbHVlIiB4c2k6dHlwZT0iT3BlcmFuZFJlZiIvPgogICAgICAgICA8L2V4cHJlc3Npb24+CiAgICAgICAgIDxvcGVyYW5kIG5hbWU9InZhbHVlIj4KICAgICAgICAgICAgPG9wZXJhbmRUeXBlU3BlY2lmaWVyIGxvY2F0b3I9IjE4OTozMi0xODk6NDgiIG5hbWU9ImZoaXI6TWVhc3VyZVJlcG9ydFR5cGUiIHhzaTp0eXBlPSJOYW1lZFR5cGVTcGVjaWZpZXIiLz4KICAgICAgICAgPC9vcGVyYW5kPgogICAgICA8L2RlZj4KICAgICAgPGRlZiBsb2NhdG9yPSIxOTA6MS0xOTA6NTYiIG5hbWU9IlRvU3RyaW5nIiBjb250ZXh0PSJQYXRpZW50IiBhY2Nlc3NMZXZlbD0iUHVibGljIiB4c2k6dHlwZT0iRnVuY3Rpb25EZWYiPgogICAgICAgICA8ZXhwcmVzc2lvbiBsb2NhdG9yPSIxOTA6NDYtMTkwOjU2IiBwYXRoPSJ2YWx1ZSIgeHNpOnR5cGU9IlByb3BlcnR5Ij4KICAgICAgICAgICAgPHNvdXJjZSBsb2NhdG9yPSIxOTA6NDYtMTkwOjUwIiBuYW1lPSJ2YWx1ZSIgeHNpOnR5cGU9Ik9wZXJhbmRSZWYiLz4KICAgICAgICAgPC9leHByZXNzaW9uPgogICAgICAgICA8b3BlcmFuZCBuYW1lPSJ2YWx1ZSI+CiAgICAgICAgICAgIDxvcGVyYW5kVHlwZVNwZWNpZmllciBsb2NhdG9yPSIxOTA6MzItMTkwOjQyIiBuYW1lPSJmaGlyOk1lZGlhU3RhdHVzIiB4c2k6dHlwZT0iTmFtZWRUeXBlU3BlY2lmaWVyIi8+CiAgICAgICAgIDwvb3BlcmFuZD4KICAgICAgPC9kZWY+CiAgICAgIDxkZWYgbG9jYXRvcj0iMTkxOjEtMTkxOjc1IiBuYW1lPSJUb1N0cmluZyIgY29udGV4dD0iUGF0aWVudCIgYWNjZXNzTGV2ZWw9IlB1YmxpYyIgeHNpOnR5cGU9IkZ1bmN0aW9uRGVmIj4KICAgICAgICAgPGV4cHJlc3Npb24gbG9jYXRvcj0iMTkxOjY1LTE5MTo3NSIgcGF0aD0idmFsdWUiIHhzaTp0eXBlPSJQcm9wZXJ0eSI+CiAgICAgICAgICAgIDxzb3VyY2UgbG9jYXRvcj0iMTkxOjY1LTE5MTo2OSIgbmFtZT0idmFsdWUiIHhzaTp0eXBlPSJPcGVyYW5kUmVmIi8+CiAgICAgICAgIDwvZXhwcmVzc2lvbj4KICAgICAgICAgPG9wZXJhbmQgbmFtZT0idmFsdWUiPgogICAgICAgICAgICA8b3BlcmFuZFR5cGVTcGVjaWZpZXIgbG9jYXRvcj0iMTkxOjMyLTE5MTo2MSIgbmFtZT0iZmhpcjpNZWRpY2F0aW9uQWRtaW5pc3RyYXRpb25TdGF0dXMiIHhzaTp0eXBlPSJOYW1lZFR5cGVTcGVjaWZpZXIiLz4KICAgICAgICAgPC9vcGVyYW5kPgogICAgICA8L2RlZj4KICAgICAgPGRlZiBsb2NhdG9yPSIxOTI6MS0xOTI6NjkiIG5hbWU9IlRvU3RyaW5nIiBjb250ZXh0PSJQYXRpZW50IiBhY2Nlc3NMZXZlbD0iUHVibGljIiB4c2k6dHlwZT0iRnVuY3Rpb25EZWYiPgogICAgICAgICA8ZXhwcmVzc2lvbiBsb2NhdG9yPSIxOTI6NTktMTkyOjY5IiBwYXRoPSJ2YWx1ZSIgeHNpOnR5cGU9IlByb3BlcnR5Ij4KICAgICAgICAgICAgPHNvdXJjZSBsb2NhdG9yPSIxOTI6NTktMTkyOjYzIiBuYW1lPSJ2YWx1ZSIgeHNpOnR5cGU9Ik9wZXJhbmRSZWYiLz4KICAgICAgICAgPC9leHByZXNzaW9uPgogICAgICAgICA8b3BlcmFuZCBuYW1lPSJ2YWx1ZSI+CiAgICAgICAgICAgIDxvcGVyYW5kVHlwZVNwZWNpZmllciBsb2NhdG9yPSIxOTI6MzItMTkyOjU1IiBuYW1lPSJmaGlyOk1lZGljYXRpb25EaXNwZW5zZVN0YXR1cyIgeHNpOnR5cGU9Ik5hbWVkVHlwZVNwZWNpZmllciIvPgogICAgICAgICA8L29wZXJhbmQ+CiAgICAgIDwvZGVmPgogICAgICA8ZGVmIGxvY2F0b3I9IjE5MzoxLTE5Mzo3MCIgbmFtZT0iVG9TdHJpbmciIGNvbnRleHQ9IlBhdGllbnQiIGFjY2Vzc0xldmVsPSJQdWJsaWMiIHhzaTp0eXBlPSJGdW5jdGlvbkRlZiI+CiAgICAgICAgIDxleHByZXNzaW9uIGxvY2F0b3I9IjE5Mzo2MC0xOTM6NzAiIHBhdGg9InZhbHVlIiB4c2k6dHlwZT0iUHJvcGVydHkiPgogICAgICAgICAgICA8c291cmNlIGxvY2F0b3I9IjE5Mzo2MC0xOTM6NjQiIG5hbWU9InZhbHVlIiB4c2k6dHlwZT0iT3BlcmFuZFJlZiIvPgogICAgICAgICA8L2V4cHJlc3Npb24+CiAgICAgICAgIDxvcGVyYW5kIG5hbWU9InZhbHVlIj4KICAgICAgICAgICAgPG9wZXJhbmRUeXBlU3BlY2lmaWVyIGxvY2F0b3I9IjE5MzozMi0xOTM6NTYiIG5hbWU9ImZoaXI6TWVkaWNhdGlvbktub3dsZWRnZVN0YXR1cyIgeHNpOnR5cGU9Ik5hbWVkVHlwZVNwZWNpZmllciIvPgogICAgICAgICA8L29wZXJhbmQ+CiAgICAgIDwvZGVmPgogICAgICA8ZGVmIGxvY2F0b3I9IjE5NDoxLTE5NDo2OCIgbmFtZT0iVG9TdHJpbmciIGNvbnRleHQ9IlBhdGllbnQiIGFjY2Vzc0xldmVsPSJQdWJsaWMiIHhzaTp0eXBlPSJGdW5jdGlvbkRlZiI+CiAgICAgICAgIDxleHByZXNzaW9uIGxvY2F0b3I9IjE5NDo1OC0xOTQ6NjgiIHBhdGg9InZhbHVlIiB4c2k6dHlwZT0iUHJvcGVydHkiPgogICAgICAgICAgICA8c291cmNlIGxvY2F0b3I9IjE5NDo1OC0xOTQ6NjIiIG5hbWU9InZhbHVlIiB4c2k6dHlwZT0iT3BlcmFuZFJlZiIvPgogICAgICAgICA8L2V4cHJlc3Npb24+CiAgICAgICAgIDxvcGVyYW5kIG5hbWU9InZhbHVlIj4KICAgICAgICAgICAgPG9wZXJhbmRUeXBlU3BlY2lmaWVyIGxvY2F0b3I9IjE5NDozMi0xOTQ6NTQiIG5hbWU9ImZoaXI6TWVkaWNhdGlvblJlcXVlc3RJbnRlbnQiIHhzaTp0eXBlPSJOYW1lZFR5cGVTcGVjaWZpZXIiLz4KICAgICAgICAgPC9vcGVyYW5kPgogICAgICA8L2RlZj4KICAgICAgPGRlZiBsb2NhdG9yPSIxOTU6MS0xOTU6NzAiIG5hbWU9IlRvU3RyaW5nIiBjb250ZXh0PSJQYXRpZW50IiBhY2Nlc3NMZXZlbD0iUHVibGljIiB4c2k6dHlwZT0iRnVuY3Rpb25EZWYiPgogICAgICAgICA8ZXhwcmVzc2lvbiBsb2NhdG9yPSIxOTU6NjAtMTk1OjcwIiBwYXRoPSJ2YWx1ZSIgeHNpOnR5cGU9IlByb3BlcnR5Ij4KICAgICAgICAgICAgPHNvdXJjZSBsb2NhdG9yPSIxOTU6NjAtMTk1OjY0IiBuYW1lPSJ2YWx1ZSIgeHNpOnR5cGU9Ik9wZXJhbmRSZWYiLz4KICAgICAgICAgPC9leHByZXNzaW9uPgogICAgICAgICA8b3BlcmFuZCBuYW1lPSJ2YWx1ZSI+CiAgICAgICAgICAgIDxvcGVyYW5kVHlwZVNwZWNpZmllciBsb2NhdG9yPSIxOTU6MzItMTk1OjU2IiBuYW1lPSJmaGlyOk1lZGljYXRpb25SZXF1ZXN0UHJpb3JpdHkiIHhzaTp0eXBlPSJOYW1lZFR5cGVTcGVjaWZpZXIiLz4KICAgICAgICAgPC9vcGVyYW5kPgogICAgICA8L2RlZj4KICAgICAgPGRlZiBsb2NhdG9yPSIxOTY6MS0xOTY6NjgiIG5hbWU9IlRvU3RyaW5nIiBjb250ZXh0PSJQYXRpZW50IiBhY2Nlc3NMZXZlbD0iUHVibGljIiB4c2k6dHlwZT0iRnVuY3Rpb25EZWYiPgogICAgICAgICA8ZXhwcmVzc2lvbiBsb2NhdG9yPSIxOTY6NTgtMTk2OjY4IiBwYXRoPSJ2YWx1ZSIgeHNpOnR5cGU9IlByb3BlcnR5Ij4KICAgICAgICAgICAgPHNvdXJjZSBsb2NhdG9yPSIxOTY6NTgtMTk2OjYyIiBuYW1lPSJ2YWx1ZSIgeHNpOnR5cGU9Ik9wZXJhbmRSZWYiLz4KICAgICAgICAgPC9leHByZXNzaW9uPgogICAgICAgICA8b3BlcmFuZCBuYW1lPSJ2YWx1ZSI+CiAgICAgICAgICAgIDxvcGVyYW5kVHlwZVNwZWNpZmllciBsb2NhdG9yPSIxOTY6MzItMTk2OjU0IiBuYW1lPSJmaGlyOk1lZGljYXRpb25SZXF1ZXN0U3RhdHVzIiB4c2k6dHlwZT0iTmFtZWRUeXBlU3BlY2lmaWVyIi8+CiAgICAgICAgIDwvb3BlcmFuZD4KICAgICAgPC9kZWY+CiAgICAgIDxkZWYgbG9jYXRvcj0iMTk3OjEtMTk3OjcwIiBuYW1lPSJUb1N0cmluZyIgY29udGV4dD0iUGF0aWVudCIgYWNjZXNzTGV2ZWw9IlB1YmxpYyIgeHNpOnR5cGU9IkZ1bmN0aW9uRGVmIj4KICAgICAgICAgPGV4cHJlc3Npb24gbG9jYXRvcj0iMTk3OjYwLTE5Nzo3MCIgcGF0aD0idmFsdWUiIHhzaTp0eXBlPSJQcm9wZXJ0eSI+CiAgICAgICAgICAgIDxzb3VyY2UgbG9jYXRvcj0iMTk3OjYwLTE5Nzo2NCIgbmFtZT0idmFsdWUiIHhzaTp0eXBlPSJPcGVyYW5kUmVmIi8+CiAgICAgICAgIDwvZXhwcmVzc2lvbj4KICAgICAgICAgPG9wZXJhbmQgbmFtZT0idmFsdWUiPgogICAgICAgICAgICA8b3BlcmFuZFR5cGVTcGVjaWZpZXIgbG9jYXRvcj0iMTk3OjMyLTE5Nzo1NiIgbmFtZT0iZmhpcjpNZWRpY2F0aW9uU3RhdGVtZW50U3RhdHVzIiB4c2k6dHlwZT0iTmFtZWRUeXBlU3BlY2lmaWVyIi8+CiAgICAgICAgIDwvb3BlcmFuZD4KICAgICAgPC9kZWY+CiAgICAgIDxkZWYgbG9jYXRvcj0iMTk4OjEtMTk4OjYxIiBuYW1lPSJUb1N0cmluZyIgY29udGV4dD0iUGF0aWVudCIgYWNjZXNzTGV2ZWw9IlB1YmxpYyIgeHNpOnR5cGU9IkZ1bmN0aW9uRGVmIj4KICAgICAgICAgPGV4cHJlc3Npb24gbG9jYXRvcj0iMTk4OjUxLTE5ODo2MSIgcGF0aD0idmFsdWUiIHhzaTp0eXBlPSJQcm9wZXJ0eSI+CiAgICAgICAgICAgIDxzb3VyY2UgbG9jYXRvcj0iMTk4OjUxLTE5ODo1NSIgbmFtZT0idmFsdWUiIHhzaTp0eXBlPSJPcGVyYW5kUmVmIi8+CiAgICAgICAgIDwvZXhwcmVzc2lvbj4KICAgICAgICAgPG9wZXJhbmQgbmFtZT0idmFsdWUiPgogICAgICAgICAgICA8b3BlcmFuZFR5cGVTcGVjaWZpZXIgbG9jYXRvcj0iMTk4OjMyLTE5ODo0NyIgbmFtZT0iZmhpcjpNZWRpY2F0aW9uU3RhdHVzIiB4c2k6dHlwZT0iTmFtZWRUeXBlU3BlY2lmaWVyIi8+CiAgICAgICAgIDwvb3BlcmFuZD4KICAgICAgPC9kZWY+CiAgICAgIDxkZWYgbG9jYXRvcj0iMTk5OjEtMTk5OjcyIiBuYW1lPSJUb1N0cmluZyIgY29udGV4dD0iUGF0aWVudCIgYWNjZXNzTGV2ZWw9IlB1YmxpYyIgeHNpOnR5cGU9IkZ1bmN0aW9uRGVmIj4KICAgICAgICAgPGV4cHJlc3Npb24gbG9jYXRvcj0iMTk5OjYyLTE5OTo3MiIgcGF0aD0idmFsdWUiIHhzaTp0eXBlPSJQcm9wZXJ0eSI+CiAgICAgICAgICAgIDxzb3VyY2UgbG9jYXRvcj0iMTk5OjYyLTE5OTo2NiIgbmFtZT0idmFsdWUiIHhzaTp0eXBlPSJPcGVyYW5kUmVmIi8+CiAgICAgICAgIDwvZXhwcmVzc2lvbj4KICAgICAgICAgPG9wZXJhbmQgbmFtZT0idmFsdWUiPgogICAgICAgICAgICA8b3BlcmFuZFR5cGVTcGVjaWZpZXIgbG9jYXRvcj0iMTk5OjMyLTE5OTo1OCIgbmFtZT0iZmhpcjpNZXNzYWdlU2lnbmlmaWNhbmNlQ2F0ZWdvcnkiIHhzaTp0eXBlPSJOYW1lZFR5cGVTcGVjaWZpZXIiLz4KICAgICAgICAgPC9vcGVyYW5kPgogICAgICA8L2RlZj4KICAgICAgPGRlZiBsb2NhdG9yPSIyMDA6MS0yMDA6NzUiIG5hbWU9IlRvU3RyaW5nIiBjb250ZXh0PSJQYXRpZW50IiBhY2Nlc3NMZXZlbD0iUHVibGljIiB4c2k6dHlwZT0iRnVuY3Rpb25EZWYiPgogICAgICAgICA8ZXhwcmVzc2lvbiBsb2NhdG9yPSIyMDA6NjUtMjAwOjc1IiBwYXRoPSJ2YWx1ZSIgeHNpOnR5cGU9IlByb3BlcnR5Ij4KICAgICAgICAgICAgPHNvdXJjZSBsb2NhdG9yPSIyMDA6NjUtMjAwOjY5IiBuYW1lPSJ2YWx1ZSIgeHNpOnR5cGU9Ik9wZXJhbmRSZWYiLz4KICAgICAgICAgPC9leHByZXNzaW9uPgogICAgICAgICA8b3BlcmFuZCBuYW1lPSJ2YWx1ZSI+CiAgICAgICAgICAgIDxvcGVyYW5kVHlwZVNwZWNpZmllciBsb2NhdG9yPSIyMDA6MzItMjAwOjYxIiBuYW1lPSJmaGlyOk1lc3NhZ2VoZWFkZXJfUmVzcG9uc2VfUmVxdWVzdCIgeHNpOnR5cGU9Ik5hbWVkVHlwZVNwZWNpZmllciIvPgogICAgICAgICA8L29wZXJhbmQ+CiAgICAgIDwvZGVmPgogICAgICA8ZGVmIGxvY2F0b3I9IjIwMToxLTIwMTo1MyIgbmFtZT0iVG9TdHJpbmciIGNvbnRleHQ9IlBhdGllbnQiIGFjY2Vzc0xldmVsPSJQdWJsaWMiIHhzaTp0eXBlPSJGdW5jdGlvbkRlZiI+CiAgICAgICAgIDxleHByZXNzaW9uIGxvY2F0b3I9IjIwMTo0My0yMDE6NTMiIHBhdGg9InZhbHVlIiB4c2k6dHlwZT0iUHJvcGVydHkiPgogICAgICAgICAgICA8c291cmNlIGxvY2F0b3I9IjIwMTo0My0yMDE6NDciIG5hbWU9InZhbHVlIiB4c2k6dHlwZT0iT3BlcmFuZFJlZiIvPgogICAgICAgICA8L2V4cHJlc3Npb24+CiAgICAgICAgIDxvcGVyYW5kIG5hbWU9InZhbHVlIj4KICAgICAgICAgICAgPG9wZXJhbmRUeXBlU3BlY2lmaWVyIGxvY2F0b3I9IjIwMTozMi0yMDE6MzkiIG5hbWU9ImZoaXI6TWltZVR5cGUiIHhzaTp0eXBlPSJOYW1lZFR5cGVTcGVjaWZpZXIiLz4KICAgICAgICAgPC9vcGVyYW5kPgogICAgICA8L2RlZj4KICAgICAgPGRlZiBsb2NhdG9yPSIyMDI6MS0yMDI6NTIiIG5hbWU9IlRvU3RyaW5nIiBjb250ZXh0PSJQYXRpZW50IiBhY2Nlc3NMZXZlbD0iUHVibGljIiB4c2k6dHlwZT0iRnVuY3Rpb25EZWYiPgogICAgICAgICA8ZXhwcmVzc2lvbiBsb2NhdG9yPSIyMDI6NDItMjAyOjUyIiBwYXRoPSJ2YWx1ZSIgeHNpOnR5cGU9IlByb3BlcnR5Ij4KICAgICAgICAgICAgPHNvdXJjZSBsb2NhdG9yPSIyMDI6NDItMjAyOjQ2IiBuYW1lPSJ2YWx1ZSIgeHNpOnR5cGU9Ik9wZXJhbmRSZWYiLz4KICAgICAgICAgPC9leHByZXNzaW9uPgogICAgICAgICA8b3BlcmFuZCBuYW1lPSJ2YWx1ZSI+CiAgICAgICAgICAgIDxvcGVyYW5kVHlwZVNwZWNpZmllciBsb2NhdG9yPSIyMDI6MzItMjAyOjM4IiBuYW1lPSJmaGlyOk5hbWVVc2UiIHhzaTp0eXBlPSJOYW1lZFR5cGVTcGVjaWZpZXIiLz4KICAgICAgICAgPC9vcGVyYW5kPgogICAgICA8L2RlZj4KICAgICAgPGRlZiBsb2NhdG9yPSIyMDM6MS0yMDM6NzEiIG5hbWU9IlRvU3RyaW5nIiBjb250ZXh0PSJQYXRpZW50IiBhY2Nlc3NMZXZlbD0iUHVibGljIiB4c2k6dHlwZT0iRnVuY3Rpb25EZWYiPgogICAgICAgICA8ZXhwcmVzc2lvbiBsb2NhdG9yPSIyMDM6NjEtMjAzOjcxIiBwYXRoPSJ2YWx1ZSIgeHNpOnR5cGU9IlByb3BlcnR5Ij4KICAgICAgICAgICAgPHNvdXJjZSBsb2NhdG9yPSIyMDM6NjEtMjAzOjY1IiBuYW1lPSJ2YWx1ZSIgeHNpOnR5cGU9Ik9wZXJhbmRSZWYiLz4KICAgICAgICAgPC9leHByZXNzaW9uPgogICAgICAgICA8b3BlcmFuZCBuYW1lPSJ2YWx1ZSI+CiAgICAgICAgICAgIDxvcGVyYW5kVHlwZVNwZWNpZmllciBsb2NhdG9yPSIyMDM6MzItMjAzOjU3IiBuYW1lPSJmaGlyOk5hbWluZ1N5c3RlbUlkZW50aWZpZXJUeXBlIiB4c2k6dHlwZT0iTmFtZWRUeXBlU3BlY2lmaWVyIi8+CiAgICAgICAgIDwvb3BlcmFuZD4KICAgICAgPC9kZWY+CiAgICAgIDxkZWYgbG9jYXRvcj0iMjA0OjEtMjA0OjYxIiBuYW1lPSJUb1N0cmluZyIgY29udGV4dD0iUGF0aWVudCIgYWNjZXNzTGV2ZWw9IlB1YmxpYyIgeHNpOnR5cGU9IkZ1bmN0aW9uRGVmIj4KICAgICAgICAgPGV4cHJlc3Npb24gbG9jYXRvcj0iMjA0OjUxLTIwNDo2MSIgcGF0aD0idmFsdWUiIHhzaTp0eXBlPSJQcm9wZXJ0eSI+CiAgICAgICAgICAgIDxzb3VyY2UgbG9jYXRvcj0iMjA0OjUxLTIwNDo1NSIgbmFtZT0idmFsdWUiIHhzaTp0eXBlPSJPcGVyYW5kUmVmIi8+CiAgICAgICAgIDwvZXhwcmVzc2lvbj4KICAgICAgICAgPG9wZXJhbmQgbmFtZT0idmFsdWUiPgogICAgICAgICAgICA8b3BlcmFuZFR5cGVTcGVjaWZpZXIgbG9jYXRvcj0iMjA0OjMyLTIwNDo0NyIgbmFtZT0iZmhpcjpOYW1pbmdTeXN0ZW1UeXBlIiB4c2k6dHlwZT0iTmFtZWRUeXBlU3BlY2lmaWVyIi8+CiAgICAgICAgIDwvb3BlcmFuZD4KICAgICAgPC9kZWY+CiAgICAgIDxkZWYgbG9jYXRvcj0iMjA1OjEtMjA1OjYwIiBuYW1lPSJUb1N0cmluZyIgY29udGV4dD0iUGF0aWVudCIgYWNjZXNzTGV2ZWw9IlB1YmxpYyIgeHNpOnR5cGU9IkZ1bmN0aW9uRGVmIj4KICAgICAgICAgPGV4cHJlc3Npb24gbG9jYXRvcj0iMjA1OjUwLTIwNTo2MCIgcGF0aD0idmFsdWUiIHhzaTp0eXBlPSJQcm9wZXJ0eSI+CiAgICAgICAgICAgIDxzb3VyY2UgbG9jYXRvcj0iMjA1OjUwLTIwNTo1NCIgbmFtZT0idmFsdWUiIHhzaTp0eXBlPSJPcGVyYW5kUmVmIi8+CiAgICAgICAgIDwvZXhwcmVzc2lvbj4KICAgICAgICAgPG9wZXJhbmQgbmFtZT0idmFsdWUiPgogICAgICAgICAgICA8b3BlcmFuZFR5cGVTcGVjaWZpZXIgbG9jYXRvcj0iMjA1OjMyLTIwNTo0NiIgbmFtZT0iZmhpcjpOYXJyYXRpdmVTdGF0dXMiIHhzaTp0eXBlPSJOYW1lZFR5cGVTcGVjaWZpZXIiLz4KICAgICAgICAgPC9vcGVyYW5kPgogICAgICA8L2RlZj4KICAgICAgPGRlZiBsb2NhdG9yPSIyMDY6MS0yMDY6NTMiIG5hbWU9IlRvU3RyaW5nIiBjb250ZXh0PSJQYXRpZW50IiBhY2Nlc3NMZXZlbD0iUHVibGljIiB4c2k6dHlwZT0iRnVuY3Rpb25EZWYiPgogICAgICAgICA8ZXhwcmVzc2lvbiBsb2NhdG9yPSIyMDY6NDMtMjA2OjUzIiBwYXRoPSJ2YWx1ZSIgeHNpOnR5cGU9IlByb3BlcnR5Ij4KICAgICAgICAgICAgPHNvdXJjZSBsb2NhdG9yPSIyMDY6NDMtMjA2OjQ3IiBuYW1lPSJ2YWx1ZSIgeHNpOnR5cGU9Ik9wZXJhbmRSZWYiLz4KICAgICAgICAgPC9leHByZXNzaW9uPgogICAgICAgICA8b3BlcmFuZCBuYW1lPSJ2YWx1ZSI+CiAgICAgICAgICAgIDxvcGVyYW5kVHlwZVNwZWNpZmllciBsb2NhdG9yPSIyMDY6MzItMjA2OjM5IiBuYW1lPSJmaGlyOk5vdGVUeXBlIiB4c2k6dHlwZT0iTmFtZWRUeXBlU3BlY2lmaWVyIi8+CiAgICAgICAgIDwvb3BlcmFuZD4KICAgICAgPC9kZWY+CiAgICAgIDxkZWYgbG9jYXRvcj0iMjA3OjEtMjA3OjY2IiBuYW1lPSJUb1N0cmluZyIgY29udGV4dD0iUGF0aWVudCIgYWNjZXNzTGV2ZWw9IlB1YmxpYyIgeHNpOnR5cGU9IkZ1bmN0aW9uRGVmIj4KICAgICAgICAgPGV4cHJlc3Npb24gbG9jYXRvcj0iMjA3OjU2LTIwNzo2NiIgcGF0aD0idmFsdWUiIHhzaTp0eXBlPSJQcm9wZXJ0eSI+CiAgICAgICAgICAgIDxzb3VyY2UgbG9jYXRvcj0iMjA3OjU2LTIwNzo2MCIgbmFtZT0idmFsdWUiIHhzaTp0eXBlPSJPcGVyYW5kUmVmIi8+CiAgICAgICAgIDwvZXhwcmVzc2lvbj4KICAgICAgICAgPG9wZXJhbmQgbmFtZT0idmFsdWUiPgogICAgICAgICAgICA8b3BlcmFuZFR5cGVTcGVjaWZpZXIgbG9jYXRvcj0iMjA3OjMyLTIwNzo1MiIgbmFtZT0iZmhpcjpOdXRyaXRpaW9uT3JkZXJJbnRlbnQiIHhzaTp0eXBlPSJOYW1lZFR5cGVTcGVjaWZpZXIiLz4KICAgICAgICAgPC9vcGVyYW5kPgogICAgICA8L2RlZj4KICAgICAgPGRlZiBsb2NhdG9yPSIyMDg6MS0yMDg6NjUiIG5hbWU9IlRvU3RyaW5nIiBjb250ZXh0PSJQYXRpZW50IiBhY2Nlc3NMZXZlbD0iUHVibGljIiB4c2k6dHlwZT0iRnVuY3Rpb25EZWYiPgogICAgICAgICA8ZXhwcmVzc2lvbiBsb2NhdG9yPSIyMDg6NTUtMjA4OjY1IiBwYXRoPSJ2YWx1ZSIgeHNpOnR5cGU9IlByb3BlcnR5Ij4KICAgICAgICAgICAgPHNvdXJjZSBsb2NhdG9yPSIyMDg6NTUtMjA4OjU5IiBuYW1lPSJ2YWx1ZSIgeHNpOnR5cGU9Ik9wZXJhbmRSZWYiLz4KICAgICAgICAgPC9leHByZXNzaW9uPgogICAgICAgICA8b3BlcmFuZCBuYW1lPSJ2YWx1ZSI+CiAgICAgICAgICAgIDxvcGVyYW5kVHlwZVNwZWNpZmllciBsb2NhdG9yPSIyMDg6MzItMjA4OjUxIiBuYW1lPSJmaGlyOk51dHJpdGlvbk9yZGVyU3RhdHVzIiB4c2k6dHlwZT0iTmFtZWRUeXBlU3BlY2lmaWVyIi8+CiAgICAgICAgIDwvb3BlcmFuZD4KICAgICAgPC9kZWY+CiAgICAgIDxkZWYgbG9jYXRvcj0iMjA5OjEtMjA5OjY0IiBuYW1lPSJUb1N0cmluZyIgY29udGV4dD0iUGF0aWVudCIgYWNjZXNzTGV2ZWw9IlB1YmxpYyIgeHNpOnR5cGU9IkZ1bmN0aW9uRGVmIj4KICAgICAgICAgPGV4cHJlc3Npb24gbG9jYXRvcj0iMjA5OjU0LTIwOTo2NCIgcGF0aD0idmFsdWUiIHhzaTp0eXBlPSJQcm9wZXJ0eSI+CiAgICAgICAgICAgIDxzb3VyY2UgbG9jYXRvcj0iMjA5OjU0LTIwOTo1OCIgbmFtZT0idmFsdWUiIHhzaTp0eXBlPSJPcGVyYW5kUmVmIi8+CiAgICAgICAgIDwvZXhwcmVzc2lvbj4KICAgICAgICAgPG9wZXJhbmQgbmFtZT0idmFsdWUiPgogICAgICAgICAgICA8b3BlcmFuZFR5cGVTcGVjaWZpZXIgbG9jYXRvcj0iMjA5OjMyLTIwOTo1MCIgbmFtZT0iZmhpcjpPYnNlcnZhdGlvbkRhdGFUeXBlIiB4c2k6dHlwZT0iTmFtZWRUeXBlU3BlY2lmaWVyIi8+CiAgICAgICAgIDwvb3BlcmFuZD4KICAgICAgPC9kZWY+CiAgICAgIDxkZWYgbG9jYXRvcj0iMjEwOjEtMjEwOjY5IiBuYW1lPSJUb1N0cmluZyIgY29udGV4dD0iUGF0aWVudCIgYWNjZXNzTGV2ZWw9IlB1YmxpYyIgeHNpOnR5cGU9IkZ1bmN0aW9uRGVmIj4KICAgICAgICAgPGV4cHJlc3Npb24gbG9jYXRvcj0iMjEwOjU5LTIxMDo2OSIgcGF0aD0idmFsdWUiIHhzaTp0eXBlPSJQcm9wZXJ0eSI+CiAgICAgICAgICAgIDxzb3VyY2UgbG9jYXRvcj0iMjEwOjU5LTIxMDo2MyIgbmFtZT0idmFsdWUiIHhzaTp0eXBlPSJPcGVyYW5kUmVmIi8+CiAgICAgICAgIDwvZXhwcmVzc2lvbj4KICAgICAgICAgPG9wZXJhbmQgbmFtZT0idmFsdWUiPgogICAgICAgICAgICA8b3BlcmFuZFR5cGVTcGVjaWZpZXIgbG9jYXRvcj0iMjEwOjMyLTIxMDo1NSIgbmFtZT0iZmhpcjpPYnNlcnZhdGlvblJhbmdlQ2F0ZWdvcnkiIHhzaTp0eXBlPSJOYW1lZFR5cGVTcGVjaWZpZXIiLz4KICAgICAgICAgPC9vcGVyYW5kPgogICAgICA8L2RlZj4KICAgICAgPGRlZiBsb2NhdG9yPSIyMTE6MS0yMTE6NjIiIG5hbWU9IlRvU3RyaW5nIiBjb250ZXh0PSJQYXRpZW50IiBhY2Nlc3NMZXZlbD0iUHVibGljIiB4c2k6dHlwZT0iRnVuY3Rpb25EZWYiPgogICAgICAgICA8ZXhwcmVzc2lvbiBsb2NhdG9yPSIyMTE6NTItMjExOjYyIiBwYXRoPSJ2YWx1ZSIgeHNpOnR5cGU9IlByb3BlcnR5Ij4KICAgICAgICAgICAgPHNvdXJjZSBsb2NhdG9yPSIyMTE6NTItMjExOjU2IiBuYW1lPSJ2YWx1ZSIgeHNpOnR5cGU9Ik9wZXJhbmRSZWYiLz4KICAgICAgICAgPC9leHByZXNzaW9uPgogICAgICAgICA8b3BlcmFuZCBuYW1lPSJ2YWx1ZSI+CiAgICAgICAgICAgIDxvcGVyYW5kVHlwZVNwZWNpZmllciBsb2NhdG9yPSIyMTE6MzItMjExOjQ4IiBuYW1lPSJmaGlyOk9ic2VydmF0aW9uU3RhdHVzIiB4c2k6dHlwZT0iTmFtZWRUeXBlU3BlY2lmaWVyIi8+CiAgICAgICAgIDwvb3BlcmFuZD4KICAgICAgPC9kZWY+CiAgICAgIDxkZWYgbG9jYXRvcj0iMjEyOjEtMjEyOjU4IiBuYW1lPSJUb1N0cmluZyIgY29udGV4dD0iUGF0aWVudCIgYWNjZXNzTGV2ZWw9IlB1YmxpYyIgeHNpOnR5cGU9IkZ1bmN0aW9uRGVmIj4KICAgICAgICAgPGV4cHJlc3Npb24gbG9jYXRvcj0iMjEyOjQ4LTIxMjo1OCIgcGF0aD0idmFsdWUiIHhzaTp0eXBlPSJQcm9wZXJ0eSI+CiAgICAgICAgICAgIDxzb3VyY2UgbG9jYXRvcj0iMjEyOjQ4LTIxMjo1MiIgbmFtZT0idmFsdWUiIHhzaTp0eXBlPSJPcGVyYW5kUmVmIi8+CiAgICAgICAgIDwvZXhwcmVzc2lvbj4KICAgICAgICAgPG9wZXJhbmQgbmFtZT0idmFsdWUiPgogICAgICAgICAgICA8b3BlcmFuZFR5cGVTcGVjaWZpZXIgbG9jYXRvcj0iMjEyOjMyLTIxMjo0NCIgbmFtZT0iZmhpcjpPcGVyYXRpb25LaW5kIiB4c2k6dHlwZT0iTmFtZWRUeXBlU3BlY2lmaWVyIi8+CiAgICAgICAgIDwvb3BlcmFuZD4KICAgICAgPC9kZWY+CiAgICAgIDxkZWYgbG9jYXRvcj0iMjEzOjEtMjEzOjY2IiBuYW1lPSJUb1N0cmluZyIgY29udGV4dD0iUGF0aWVudCIgYWNjZXNzTGV2ZWw9IlB1YmxpYyIgeHNpOnR5cGU9IkZ1bmN0aW9uRGVmIj4KICAgICAgICAgPGV4cHJlc3Npb24gbG9jYXRvcj0iMjEzOjU2LTIxMzo2NiIgcGF0aD0idmFsdWUiIHhzaTp0eXBlPSJQcm9wZXJ0eSI+CiAgICAgICAgICAgIDxzb3VyY2UgbG9jYXRvcj0iMjEzOjU2LTIxMzo2MCIgbmFtZT0idmFsdWUiIHhzaTp0eXBlPSJPcGVyYW5kUmVmIi8+CiAgICAgICAgIDwvZXhwcmVzc2lvbj4KICAgICAgICAgPG9wZXJhbmQgbmFtZT0idmFsdWUiPgogICAgICAgICAgICA8b3BlcmFuZFR5cGVTcGVjaWZpZXIgbG9jYXRvcj0iMjEzOjMyLTIxMzo1MiIgbmFtZT0iZmhpcjpPcGVyYXRpb25QYXJhbWV0ZXJVc2UiIHhzaTp0eXBlPSJOYW1lZFR5cGVTcGVjaWZpZXIiLz4KICAgICAgICAgPC9vcGVyYW5kPgogICAgICA8L2RlZj4KICAgICAgPGRlZiBsb2NhdG9yPSIyMTQ6MS0yMTQ6NjAiIG5hbWU9IlRvU3RyaW5nIiBjb250ZXh0PSJQYXRpZW50IiBhY2Nlc3NMZXZlbD0iUHVibGljIiB4c2k6dHlwZT0iRnVuY3Rpb25EZWYiPgogICAgICAgICA8ZXhwcmVzc2lvbiBsb2NhdG9yPSIyMTQ6NTAtMjE0OjYwIiBwYXRoPSJ2YWx1ZSIgeHNpOnR5cGU9IlByb3BlcnR5Ij4KICAgICAgICAgICAgPHNvdXJjZSBsb2NhdG9yPSIyMTQ6NTAtMjE0OjU0IiBuYW1lPSJ2YWx1ZSIgeHNpOnR5cGU9Ik9wZXJhbmRSZWYiLz4KICAgICAgICAgPC9leHByZXNzaW9uPgogICAgICAgICA8b3BlcmFuZCBuYW1lPSJ2YWx1ZSI+CiAgICAgICAgICAgIDxvcGVyYW5kVHlwZVNwZWNpZmllciBsb2NhdG9yPSIyMTQ6MzItMjE0OjQ2IiBuYW1lPSJmaGlyOk9yaWVudGF0aW9uVHlwZSIgeHNpOnR5cGU9Ik5hbWVkVHlwZVNwZWNpZmllciIvPgogICAgICAgICA8L29wZXJhbmQ+CiAgICAgIDwvZGVmPgogICAgICA8ZGVmIGxvY2F0b3I9IjIxNToxLTIxNTo1NyIgbmFtZT0iVG9TdHJpbmciIGNvbnRleHQ9IlBhdGllbnQiIGFjY2Vzc0xldmVsPSJQdWJsaWMiIHhzaTp0eXBlPSJGdW5jdGlvbkRlZiI+CiAgICAgICAgIDxleHByZXNzaW9uIGxvY2F0b3I9IjIxNTo0Ny0yMTU6NTciIHBhdGg9InZhbHVlIiB4c2k6dHlwZT0iUHJvcGVydHkiPgogICAgICAgICAgICA8c291cmNlIGxvY2F0b3I9IjIxNTo0Ny0yMTU6NTEiIG5hbWU9InZhbHVlIiB4c2k6dHlwZT0iT3BlcmFuZFJlZiIvPgogICAgICAgICA8L2V4cHJlc3Npb24+CiAgICAgICAgIDxvcGVyYW5kIG5hbWU9InZhbHVlIj4KICAgICAgICAgICAgPG9wZXJhbmRUeXBlU3BlY2lmaWVyIGxvY2F0b3I9IjIxNTozMi0yMTU6NDMiIG5hbWU9ImZoaXI6UGFyYW1ldGVyVXNlIiB4c2k6dHlwZT0iTmFtZWRUeXBlU3BlY2lmaWVyIi8+CiAgICAgICAgIDwvb3BlcmFuZD4KICAgICAgPC9kZWY+CiAgICAgIDxkZWYgbG9jYXRvcj0iMjE2OjEtMjE2OjY0IiBuYW1lPSJUb1N0cmluZyIgY29udGV4dD0iUGF0aWVudCIgYWNjZXNzTGV2ZWw9IlB1YmxpYyIgeHNpOnR5cGU9IkZ1bmN0aW9uRGVmIj4KICAgICAgICAgPGV4cHJlc3Npb24gbG9jYXRvcj0iMjE2OjU0LTIxNjo2NCIgcGF0aD0idmFsdWUiIHhzaTp0eXBlPSJQcm9wZXJ0eSI+CiAgICAgICAgICAgIDxzb3VyY2UgbG9jYXRvcj0iMjE2OjU0LTIxNjo1OCIgbmFtZT0idmFsdWUiIHhzaTp0eXBlPSJPcGVyYW5kUmVmIi8+CiAgICAgICAgIDwvZXhwcmVzc2lvbj4KICAgICAgICAgPG9wZXJhbmQgbmFtZT0idmFsdWUiPgogICAgICAgICAgICA8b3BlcmFuZFR5cGVTcGVjaWZpZXIgbG9jYXRvcj0iMjE2OjMyLTIxNjo1MCIgbmFtZT0iZmhpcjpQYXJ0aWNpcGFudFJlcXVpcmVkIiB4c2k6dHlwZT0iTmFtZWRUeXBlU3BlY2lmaWVyIi8+CiAgICAgICAgIDwvb3BlcmFuZD4KICAgICAgPC9kZWY+CiAgICAgIDxkZWYgbG9jYXRvcj0iMjE3OjEtMjE3OjYyIiBuYW1lPSJUb1N0cmluZyIgY29udGV4dD0iUGF0aWVudCIgYWNjZXNzTGV2ZWw9IlB1YmxpYyIgeHNpOnR5cGU9IkZ1bmN0aW9uRGVmIj4KICAgICAgICAgPGV4cHJlc3Npb24gbG9jYXRvcj0iMjE3OjUyLTIxNzo2MiIgcGF0aD0idmFsdWUiIHhzaTp0eXBlPSJQcm9wZXJ0eSI+CiAgICAgICAgICAgIDxzb3VyY2UgbG9jYXRvcj0iMjE3OjUyLTIxNzo1NiIgbmFtZT0idmFsdWUiIHhzaTp0eXBlPSJPcGVyYW5kUmVmIi8+CiAgICAgICAgIDwvZXhwcmVzc2lvbj4KICAgICAgICAgPG9wZXJhbmQgbmFtZT0idmFsdWUiPgogICAgICAgICAgICA8b3BlcmFuZFR5cGVTcGVjaWZpZXIgbG9jYXRvcj0iMjE3OjMyLTIxNzo0OCIgbmFtZT0iZmhpcjpQYXJ0aWNpcGFudFN0YXR1cyIgeHNpOnR5cGU9Ik5hbWVkVHlwZVNwZWNpZmllciIvPgogICAgICAgICA8L29wZXJhbmQ+CiAgICAgIDwvZGVmPgogICAgICA8ZGVmIGxvY2F0b3I9IjIxODoxLTIxODo2NCIgbmFtZT0iVG9TdHJpbmciIGNvbnRleHQ9IlBhdGllbnQiIGFjY2Vzc0xldmVsPSJQdWJsaWMiIHhzaTp0eXBlPSJGdW5jdGlvbkRlZiI+CiAgICAgICAgIDxleHByZXNzaW9uIGxvY2F0b3I9IjIxODo1NC0yMTg6NjQiIHBhdGg9InZhbHVlIiB4c2k6dHlwZT0iUHJvcGVydHkiPgogICAgICAgICAgICA8c291cmNlIGxvY2F0b3I9IjIxODo1NC0yMTg6NTgiIG5hbWU9InZhbHVlIiB4c2k6dHlwZT0iT3BlcmFuZFJlZiIvPgogICAgICAgICA8L2V4cHJlc3Npb24+CiAgICAgICAgIDxvcGVyYW5kIG5hbWU9InZhbHVlIj4KICAgICAgICAgICAgPG9wZXJhbmRUeXBlU3BlY2lmaWVyIGxvY2F0b3I9IjIxODozMi0yMTg6NTAiIG5hbWU9ImZoaXI6UGFydGljaXBhdGlvblN0YXR1cyIgeHNpOnR5cGU9Ik5hbWVkVHlwZVNwZWNpZmllciIvPgogICAgICAgICA8L29wZXJhbmQ+CiAgICAgIDwvZGVmPgogICAgICA8ZGVmIGxvY2F0b3I9IjIxOToxLTIxOTo2NCIgbmFtZT0iVG9TdHJpbmciIGNvbnRleHQ9IlBhdGllbnQiIGFjY2Vzc0xldmVsPSJQdWJsaWMiIHhzaTp0eXBlPSJGdW5jdGlvbkRlZiI+CiAgICAgICAgIDxleHByZXNzaW9uIGxvY2F0b3I9IjIxOTo1NC0yMTk6NjQiIHBhdGg9InZhbHVlIiB4c2k6dHlwZT0iUHJvcGVydHkiPgogICAgICAgICAgICA8c291cmNlIGxvY2F0b3I9IjIxOTo1NC0yMTk6NTgiIG5hbWU9InZhbHVlIiB4c2k6dHlwZT0iT3BlcmFuZFJlZiIvPgogICAgICAgICA8L2V4cHJlc3Npb24+CiAgICAgICAgIDxvcGVyYW5kIG5hbWU9InZhbHVlIj4KICAgICAgICAgICAgPG9wZXJhbmRUeXBlU3BlY2lmaWVyIGxvY2F0b3I9IjIxOTozMi0yMTk6NTAiIG5hbWU9ImZoaXI6UGF5bWVudE5vdGljZVN0YXR1cyIgeHNpOnR5cGU9Ik5hbWVkVHlwZVNwZWNpZmllciIvPgogICAgICAgICA8L29wZXJhbmQ+CiAgICAgIDwvZGVmPgogICAgICA8ZGVmIGxvY2F0b3I9IjIyMDoxLTIyMDo3MiIgbmFtZT0iVG9TdHJpbmciIGNvbnRleHQ9IlBhdGllbnQiIGFjY2Vzc0xldmVsPSJQdWJsaWMiIHhzaTp0eXBlPSJGdW5jdGlvbkRlZiI+CiAgICAgICAgIDxleHByZXNzaW9uIGxvY2F0b3I9IjIyMDo2Mi0yMjA6NzIiIHBhdGg9InZhbHVlIiB4c2k6dHlwZT0iUHJvcGVydHkiPgogICAgICAgICAgICA8c291cmNlIGxvY2F0b3I9IjIyMDo2Mi0yMjA6NjYiIG5hbWU9InZhbHVlIiB4c2k6dHlwZT0iT3BlcmFuZFJlZiIvPgogICAgICAgICA8L2V4cHJlc3Npb24+CiAgICAgICAgIDxvcGVyYW5kIG5hbWU9InZhbHVlIj4KICAgICAgICAgICAgPG9wZXJhbmRUeXBlU3BlY2lmaWVyIGxvY2F0b3I9IjIyMDozMi0yMjA6NTgiIG5hbWU9ImZoaXI6UGF5bWVudFJlY29uY2lsaWF0aW9uU3RhdHVzIiB4c2k6dHlwZT0iTmFtZWRUeXBlU3BlY2lmaWVyIi8+CiAgICAgICAgIDwvb3BlcmFuZD4KICAgICAgPC9kZWY+CiAgICAgIDxkZWYgbG9jYXRvcj0iMjIxOjEtMjIxOjYwIiBuYW1lPSJUb1N0cmluZyIgY29udGV4dD0iUGF0aWVudCIgYWNjZXNzTGV2ZWw9IlB1YmxpYyIgeHNpOnR5cGU9IkZ1bmN0aW9uRGVmIj4KICAgICAgICAgPGV4cHJlc3Npb24gbG9jYXRvcj0iMjIxOjUwLTIyMTo2MCIgcGF0aD0idmFsdWUiIHhzaTp0eXBlPSJQcm9wZXJ0eSI+CiAgICAgICAgICAgIDxzb3VyY2UgbG9jYXRvcj0iMjIxOjUwLTIyMTo1NCIgbmFtZT0idmFsdWUiIHhzaTp0eXBlPSJPcGVyYW5kUmVmIi8+CiAgICAgICAgIDwvZXhwcmVzc2lvbj4KICAgICAgICAgPG9wZXJhbmQgbmFtZT0idmFsdWUiPgogICAgICAgICAgICA8b3BlcmFuZFR5cGVTcGVjaWZpZXIgbG9jYXRvcj0iMjIxOjMyLTIyMTo0NiIgbmFtZT0iZmhpcjpQcm9jZWR1cmVTdGF0dXMiIHhzaTp0eXBlPSJOYW1lZFR5cGVTcGVjaWZpZXIiLz4KICAgICAgICAgPC9vcGVyYW5kPgogICAgICA8L2RlZj4KICAgICAgPGRlZiBsb2NhdG9yPSIyMjI6MS0yMjI6NjciIG5hbWU9IlRvU3RyaW5nIiBjb250ZXh0PSJQYXRpZW50IiBhY2Nlc3NMZXZlbD0iUHVibGljIiB4c2k6dHlwZT0iRnVuY3Rpb25EZWYiPgogICAgICAgICA8ZXhwcmVzc2lvbiBsb2NhdG9yPSIyMjI6NTctMjIyOjY3IiBwYXRoPSJ2YWx1ZSIgeHNpOnR5cGU9IlByb3BlcnR5Ij4KICAgICAgICAgICAgPHNvdXJjZSBsb2NhdG9yPSIyMjI6NTctMjIyOjYxIiBuYW1lPSJ2YWx1ZSIgeHNpOnR5cGU9Ik9wZXJhbmRSZWYiLz4KICAgICAgICAgPC9leHByZXNzaW9uPgogICAgICAgICA8b3BlcmFuZCBuYW1lPSJ2YWx1ZSI+CiAgICAgICAgICAgIDxvcGVyYW5kVHlwZVNwZWNpZmllciBsb2NhdG9yPSIyMjI6MzItMjIyOjUzIiBuYW1lPSJmaGlyOlByb3BlcnR5UmVwcmVzZW50YXRpb24iIHhzaTp0eXBlPSJOYW1lZFR5cGVTcGVjaWZpZXIiLz4KICAgICAgICAgPC9vcGVyYW5kPgogICAgICA8L2RlZj4KICAgICAgPGRlZiBsb2NhdG9yPSIyMjM6MS0yMjM6NTciIG5hbWU9IlRvU3RyaW5nIiBjb250ZXh0PSJQYXRpZW50IiBhY2Nlc3NMZXZlbD0iUHVibGljIiB4c2k6dHlwZT0iRnVuY3Rpb25EZWYiPgogICAgICAgICA8ZXhwcmVzc2lvbiBsb2NhdG9yPSIyMjM6NDctMjIzOjU3IiBwYXRoPSJ2YWx1ZSIgeHNpOnR5cGU9IlByb3BlcnR5Ij4KICAgICAgICAgICAgPHNvdXJjZSBsb2NhdG9yPSIyMjM6NDctMjIzOjUxIiBuYW1lPSJ2YWx1ZSIgeHNpOnR5cGU9Ik9wZXJhbmRSZWYiLz4KICAgICAgICAgPC9leHByZXNzaW9uPgogICAgICAgICA8b3BlcmFuZCBuYW1lPSJ2YWx1ZSI+CiAgICAgICAgICAgIDxvcGVyYW5kVHlwZVNwZWNpZmllciBsb2NhdG9yPSIyMjM6MzItMjIzOjQzIiBuYW1lPSJmaGlyOlByb3BlcnR5VHlwZSIgeHNpOnR5cGU9Ik5hbWVkVHlwZVNwZWNpZmllciIvPgogICAgICAgICA8L29wZXJhbmQ+CiAgICAgIDwvZGVmPgogICAgICA8ZGVmIGxvY2F0b3I9IjIyNDoxLTIyNDo2NSIgbmFtZT0iVG9TdHJpbmciIGNvbnRleHQ9IlBhdGllbnQiIGFjY2Vzc0xldmVsPSJQdWJsaWMiIHhzaTp0eXBlPSJGdW5jdGlvbkRlZiI+CiAgICAgICAgIDxleHByZXNzaW9uIGxvY2F0b3I9IjIyNDo1NS0yMjQ6NjUiIHBhdGg9InZhbHVlIiB4c2k6dHlwZT0iUHJvcGVydHkiPgogICAgICAgICAgICA8c291cmNlIGxvY2F0b3I9IjIyNDo1NS0yMjQ6NTkiIG5hbWU9InZhbHVlIiB4c2k6dHlwZT0iT3BlcmFuZFJlZiIvPgogICAgICAgICA8L2V4cHJlc3Npb24+CiAgICAgICAgIDxvcGVyYW5kIG5hbWU9InZhbHVlIj4KICAgICAgICAgICAgPG9wZXJhbmRUeXBlU3BlY2lmaWVyIGxvY2F0b3I9IjIyNDozMi0yMjQ6NTEiIG5hbWU9ImZoaXI6UHJvdmVuYW5jZUVudGl0eVJvbGUiIHhzaTp0eXBlPSJOYW1lZFR5cGVTcGVjaWZpZXIiLz4KICAgICAgICAgPC9vcGVyYW5kPgogICAgICA8L2RlZj4KICAgICAgPGRlZiBsb2NhdG9yPSIyMjU6MS0yMjU6NjIiIG5hbWU9IlRvU3RyaW5nIiBjb250ZXh0PSJQYXRpZW50IiBhY2Nlc3NMZXZlbD0iUHVibGljIiB4c2k6dHlwZT0iRnVuY3Rpb25EZWYiPgogICAgICAgICA8ZXhwcmVzc2lvbiBsb2NhdG9yPSIyMjU6NTItMjI1OjYyIiBwYXRoPSJ2YWx1ZSIgeHNpOnR5cGU9IlByb3BlcnR5Ij4KICAgICAgICAgICAgPHNvdXJjZSBsb2NhdG9yPSIyMjU6NTItMjI1OjU2IiBuYW1lPSJ2YWx1ZSIgeHNpOnR5cGU9Ik9wZXJhbmRSZWYiLz4KICAgICAgICAgPC9leHByZXNzaW9uPgogICAgICAgICA8b3BlcmFuZCBuYW1lPSJ2YWx1ZSI+CiAgICAgICAgICAgIDxvcGVyYW5kVHlwZVNwZWNpZmllciBsb2NhdG9yPSIyMjU6MzItMjI1OjQ4IiBuYW1lPSJmaGlyOlB1YmxpY2F0aW9uU3RhdHVzIiB4c2k6dHlwZT0iTmFtZWRUeXBlU3BlY2lmaWVyIi8+CiAgICAgICAgIDwvb3BlcmFuZD4KICAgICAgPC9kZWY+CiAgICAgIDxkZWYgbG9jYXRvcj0iMjI2OjEtMjI2OjU2IiBuYW1lPSJUb1N0cmluZyIgY29udGV4dD0iUGF0aWVudCIgYWNjZXNzTGV2ZWw9IlB1YmxpYyIgeHNpOnR5cGU9IkZ1bmN0aW9uRGVmIj4KICAgICAgICAgPGV4cHJlc3Npb24gbG9jYXRvcj0iMjI2OjQ2LTIyNjo1NiIgcGF0aD0idmFsdWUiIHhzaTp0eXBlPSJQcm9wZXJ0eSI+CiAgICAgICAgICAgIDxzb3VyY2UgbG9jYXRvcj0iMjI2OjQ2LTIyNjo1MCIgbmFtZT0idmFsdWUiIHhzaTp0eXBlPSJPcGVyYW5kUmVmIi8+CiAgICAgICAgIDwvZXhwcmVzc2lvbj4KICAgICAgICAgPG9wZXJhbmQgbmFtZT0idmFsdWUiPgogICAgICAgICAgICA8b3BlcmFuZFR5cGVTcGVjaWZpZXIgbG9jYXRvcj0iMjI2OjMyLTIyNjo0MiIgbmFtZT0iZmhpcjpRdWFsaXR5VHlwZSIgeHNpOnR5cGU9Ik5hbWVkVHlwZVNwZWNpZmllciIvPgogICAgICAgICA8L29wZXJhbmQ+CiAgICAgIDwvZGVmPgogICAgICA8ZGVmIGxvY2F0b3I9IjIyNzoxLTIyNzo2MyIgbmFtZT0iVG9TdHJpbmciIGNvbnRleHQ9IlBhdGllbnQiIGFjY2Vzc0xldmVsPSJQdWJsaWMiIHhzaTp0eXBlPSJGdW5jdGlvbkRlZiI+CiAgICAgICAgIDxleHByZXNzaW9uIGxvY2F0b3I9IjIyNzo1My0yMjc6NjMiIHBhdGg9InZhbHVlIiB4c2k6dHlwZT0iUHJvcGVydHkiPgogICAgICAgICAgICA8c291cmNlIGxvY2F0b3I9IjIyNzo1My0yMjc6NTciIG5hbWU9InZhbHVlIiB4c2k6dHlwZT0iT3BlcmFuZFJlZiIvPgogICAgICAgICA8L2V4cHJlc3Npb24+CiAgICAgICAgIDxvcGVyYW5kIG5hbWU9InZhbHVlIj4KICAgICAgICAgICAgPG9wZXJhbmRUeXBlU3BlY2lmaWVyIGxvY2F0b3I9IjIyNzozMi0yMjc6NDkiIG5hbWU9ImZoaXI6UXVhbnRpdHlDb21wYXJhdG9yIiB4c2k6dHlwZT0iTmFtZWRUeXBlU3BlY2lmaWVyIi8+CiAgICAgICAgIDwvb3BlcmFuZD4KICAgICAgPC9kZWY+CiAgICAgIDxkZWYgbG9jYXRvcj0iMjI4OjEtMjI4OjcwIiBuYW1lPSJUb1N0cmluZyIgY29udGV4dD0iUGF0aWVudCIgYWNjZXNzTGV2ZWw9IlB1YmxpYyIgeHNpOnR5cGU9IkZ1bmN0aW9uRGVmIj4KICAgICAgICAgPGV4cHJlc3Npb24gbG9jYXRvcj0iMjI4OjYwLTIyODo3MCIgcGF0aD0idmFsdWUiIHhzaTp0eXBlPSJQcm9wZXJ0eSI+CiAgICAgICAgICAgIDxzb3VyY2UgbG9jYXRvcj0iMjI4OjYwLTIyODo2NCIgbmFtZT0idmFsdWUiIHhzaTp0eXBlPSJPcGVyYW5kUmVmIi8+CiAgICAgICAgIDwvZXhwcmVzc2lvbj4KICAgICAgICAgPG9wZXJhbmQgbmFtZT0idmFsdWUiPgogICAgICAgICAgICA8b3BlcmFuZFR5cGVTcGVjaWZpZXIgbG9jYXRvcj0iMjI4OjMyLTIyODo1NiIgbmFtZT0iZmhpcjpRdWVzdGlvbm5haXJlSXRlbU9wZXJhdG9yIiB4c2k6dHlwZT0iTmFtZWRUeXBlU3BlY2lmaWVyIi8+CiAgICAgICAgIDwvb3BlcmFuZD4KICAgICAgPC9kZWY+CiAgICAgIDxkZWYgbG9jYXRvcj0iMjI5OjEtMjI5OjY2IiBuYW1lPSJUb1N0cmluZyIgY29udGV4dD0iUGF0aWVudCIgYWNjZXNzTGV2ZWw9IlB1YmxpYyIgeHNpOnR5cGU9IkZ1bmN0aW9uRGVmIj4KICAgICAgICAgPGV4cHJlc3Npb24gbG9jYXRvcj0iMjI5OjU2LTIyOTo2NiIgcGF0aD0idmFsdWUiIHhzaTp0eXBlPSJQcm9wZXJ0eSI+CiAgICAgICAgICAgIDxzb3VyY2UgbG9jYXRvcj0iMjI5OjU2LTIyOTo2MCIgbmFtZT0idmFsdWUiIHhzaTp0eXBlPSJPcGVyYW5kUmVmIi8+CiAgICAgICAgIDwvZXhwcmVzc2lvbj4KICAgICAgICAgPG9wZXJhbmQgbmFtZT0idmFsdWUiPgogICAgICAgICAgICA8b3BlcmFuZFR5cGVTcGVjaWZpZXIgbG9jYXRvcj0iMjI5OjMyLTIyOTo1MiIgbmFtZT0iZmhpcjpRdWVzdGlvbm5haXJlSXRlbVR5cGUiIHhzaTp0eXBlPSJOYW1lZFR5cGVTcGVjaWZpZXIiLz4KICAgICAgICAgPC9vcGVyYW5kPgogICAgICA8L2RlZj4KICAgICAgPGRlZiBsb2NhdG9yPSIyMzA6MS0yMzA6NzIiIG5hbWU9IlRvU3RyaW5nIiBjb250ZXh0PSJQYXRpZW50IiBhY2Nlc3NMZXZlbD0iUHVibGljIiB4c2k6dHlwZT0iRnVuY3Rpb25EZWYiPgogICAgICAgICA8ZXhwcmVzc2lvbiBsb2NhdG9yPSIyMzA6NjItMjMwOjcyIiBwYXRoPSJ2YWx1ZSIgeHNpOnR5cGU9IlByb3BlcnR5Ij4KICAgICAgICAgICAgPHNvdXJjZSBsb2NhdG9yPSIyMzA6NjItMjMwOjY2IiBuYW1lPSJ2YWx1ZSIgeHNpOnR5cGU9Ik9wZXJhbmRSZWYiLz4KICAgICAgICAgPC9leHByZXNzaW9uPgogICAgICAgICA8b3BlcmFuZCBuYW1lPSJ2YWx1ZSI+CiAgICAgICAgICAgIDxvcGVyYW5kVHlwZVNwZWNpZmllciBsb2NhdG9yPSIyMzA6MzItMjMwOjU4IiBuYW1lPSJmaGlyOlF1ZXN0aW9ubmFpcmVSZXNwb25zZVN0YXR1cyIgeHNpOnR5cGU9Ik5hbWVkVHlwZVNwZWNpZmllciIvPgogICAgICAgICA8L29wZXJhbmQ+CiAgICAgIDwvZGVmPgogICAgICA8ZGVmIGxvY2F0b3I9IjIzMToxLTIzMTo2OCIgbmFtZT0iVG9TdHJpbmciIGNvbnRleHQ9IlBhdGllbnQiIGFjY2Vzc0xldmVsPSJQdWJsaWMiIHhzaTp0eXBlPSJGdW5jdGlvbkRlZiI+CiAgICAgICAgIDxleHByZXNzaW9uIGxvY2F0b3I9IjIzMTo1OC0yMzE6NjgiIHBhdGg9InZhbHVlIiB4c2k6dHlwZT0iUHJvcGVydHkiPgogICAgICAgICAgICA8c291cmNlIGxvY2F0b3I9IjIzMTo1OC0yMzE6NjIiIG5hbWU9InZhbHVlIiB4c2k6dHlwZT0iT3BlcmFuZFJlZiIvPgogICAgICAgICA8L2V4cHJlc3Npb24+CiAgICAgICAgIDxvcGVyYW5kIG5hbWU9InZhbHVlIj4KICAgICAgICAgICAgPG9wZXJhbmRUeXBlU3BlY2lmaWVyIGxvY2F0b3I9IjIzMTozMi0yMzE6NTQiIG5hbWU9ImZoaXI6UmVmZXJlbmNlSGFuZGxpbmdQb2xpY3kiIHhzaTp0eXBlPSJOYW1lZFR5cGVTcGVjaWZpZXIiLz4KICAgICAgICAgPC9vcGVyYW5kPgogICAgICA8L2RlZj4KICAgICAgPGRlZiBsb2NhdG9yPSIyMzI6MS0yMzI6NjYiIG5hbWU9IlRvU3RyaW5nIiBjb250ZXh0PSJQYXRpZW50IiBhY2Nlc3NMZXZlbD0iUHVibGljIiB4c2k6dHlwZT0iRnVuY3Rpb25EZWYiPgogICAgICAgICA8ZXhwcmVzc2lvbiBsb2NhdG9yPSIyMzI6NTYtMjMyOjY2IiBwYXRoPSJ2YWx1ZSIgeHNpOnR5cGU9IlByb3BlcnR5Ij4KICAgICAgICAgICAgPHNvdXJjZSBsb2NhdG9yPSIyMzI6NTYtMjMyOjYwIiBuYW1lPSJ2YWx1ZSIgeHNpOnR5cGU9Ik9wZXJhbmRSZWYiLz4KICAgICAgICAgPC9leHByZXNzaW9uPgogICAgICAgICA8b3BlcmFuZCBuYW1lPSJ2YWx1ZSI+CiAgICAgICAgICAgIDxvcGVyYW5kVHlwZVNwZWNpZmllciBsb2NhdG9yPSIyMzI6MzItMjMyOjUyIiBuYW1lPSJmaGlyOlJlZmVyZW5jZVZlcnNpb25SdWxlcyIgeHNpOnR5cGU9Ik5hbWVkVHlwZVNwZWNpZmllciIvPgogICAgICAgICA8L29wZXJhbmQ+CiAgICAgIDwvZGVmPgogICAgICA8ZGVmIGxvY2F0b3I9IjIzMzoxLTIzMzo2NyIgbmFtZT0iVG9TdHJpbmciIGNvbnRleHQ9IlBhdGllbnQiIGFjY2Vzc0xldmVsPSJQdWJsaWMiIHhzaTp0eXBlPSJGdW5jdGlvbkRlZiI+CiAgICAgICAgIDxleHByZXNzaW9uIGxvY2F0b3I9IjIzMzo1Ny0yMzM6NjciIHBhdGg9InZhbHVlIiB4c2k6dHlwZT0iUHJvcGVydHkiPgogICAgICAgICAgICA8c291cmNlIGxvY2F0b3I9IjIzMzo1Ny0yMzM6NjEiIG5hbWU9InZhbHVlIiB4c2k6dHlwZT0iT3BlcmFuZFJlZiIvPgogICAgICAgICA8L2V4cHJlc3Npb24+CiAgICAgICAgIDxvcGVyYW5kIG5hbWU9InZhbHVlIj4KICAgICAgICAgICAgPG9wZXJhbmRUeXBlU3BlY2lmaWVyIGxvY2F0b3I9IjIzMzozMi0yMzM6NTMiIG5hbWU9ImZoaXI6UmVmZXJyZWREb2N1bWVudFN0YXR1cyIgeHNpOnR5cGU9Ik5hbWVkVHlwZVNwZWNpZmllciIvPgogICAgICAgICA8L29wZXJhbmQ+CiAgICAgIDwvZGVmPgogICAgICA8ZGVmIGxvY2F0b3I9IjIzNDoxLTIzNDo2NCIgbmFtZT0iVG9TdHJpbmciIGNvbnRleHQ9IlBhdGllbnQiIGFjY2Vzc0xldmVsPSJQdWJsaWMiIHhzaTp0eXBlPSJGdW5jdGlvbkRlZiI+CiAgICAgICAgIDxleHByZXNzaW9uIGxvY2F0b3I9IjIzNDo1NC0yMzQ6NjQiIHBhdGg9InZhbHVlIiB4c2k6dHlwZT0iUHJvcGVydHkiPgogICAgICAgICAgICA8c291cmNlIGxvY2F0b3I9IjIzNDo1NC0yMzQ6NTgiIG5hbWU9InZhbHVlIiB4c2k6dHlwZT0iT3BlcmFuZFJlZiIvPgogICAgICAgICA8L2V4cHJlc3Npb24+CiAgICAgICAgIDxvcGVyYW5kIG5hbWU9InZhbHVlIj4KICAgICAgICAgICAgPG9wZXJhbmRUeXBlU3BlY2lmaWVyIGxvY2F0b3I9IjIzNDozMi0yMzQ6NTAiIG5hbWU9ImZoaXI6UmVsYXRlZEFydGlmYWN0VHlwZSIgeHNpOnR5cGU9Ik5hbWVkVHlwZVNwZWNpZmllciIvPgogICAgICAgICA8L29wZXJhbmQ+CiAgICAgIDwvZGVmPgogICAgICA8ZGVmIGxvY2F0b3I9IjIzNToxLTIzNTo2MiIgbmFtZT0iVG9TdHJpbmciIGNvbnRleHQ9IlBhdGllbnQiIGFjY2Vzc0xldmVsPSJQdWJsaWMiIHhzaTp0eXBlPSJGdW5jdGlvbkRlZiI+CiAgICAgICAgIDxleHByZXNzaW9uIGxvY2F0b3I9IjIzNTo1Mi0yMzU6NjIiIHBhdGg9InZhbHVlIiB4c2k6dHlwZT0iUHJvcGVydHkiPgogICAgICAgICAgICA8c291cmNlIGxvY2F0b3I9IjIzNTo1Mi0yMzU6NTYiIG5hbWU9InZhbHVlIiB4c2k6dHlwZT0iT3BlcmFuZFJlZiIvPgogICAgICAgICA8L2V4cHJlc3Npb24+CiAgICAgICAgIDxvcGVyYW5kIG5hbWU9InZhbHVlIj4KICAgICAgICAgICAgPG9wZXJhbmRUeXBlU3BlY2lmaWVyIGxvY2F0b3I9IjIzNTozMi0yMzU6NDgiIG5hbWU9ImZoaXI6UmVtaXR0YW5jZU91dGNvbWUiIHhzaTp0eXBlPSJOYW1lZFR5cGVTcGVjaWZpZXIiLz4KICAgICAgICAgPC9vcGVyYW5kPgogICAgICA8L2RlZj4KICAgICAgPGRlZiBsb2NhdG9yPSIyMzY6MS0yMzY6NTkiIG5hbWU9IlRvU3RyaW5nIiBjb250ZXh0PSJQYXRpZW50IiBhY2Nlc3NMZXZlbD0iUHVibGljIiB4c2k6dHlwZT0iRnVuY3Rpb25EZWYiPgogICAgICAgICA8ZXhwcmVzc2lvbiBsb2NhdG9yPSIyMzY6NDktMjM2OjU5IiBwYXRoPSJ2YWx1ZSIgeHNpOnR5cGU9IlByb3BlcnR5Ij4KICAgICAgICAgICAgPHNvdXJjZSBsb2NhdG9yPSIyMzY6NDktMjM2OjUzIiBuYW1lPSJ2YWx1ZSIgeHNpOnR5cGU9Ik9wZXJhbmRSZWYiLz4KICAgICAgICAgPC9leHByZXNzaW9uPgogICAgICAgICA8b3BlcmFuZCBuYW1lPSJ2YWx1ZSI+CiAgICAgICAgICAgIDxvcGVyYW5kVHlwZVNwZWNpZmllciBsb2NhdG9yPSIyMzY6MzItMjM2OjQ1IiBuYW1lPSJmaGlyOlJlcG9zaXRvcnlUeXBlIiB4c2k6dHlwZT0iTmFtZWRUeXBlU3BlY2lmaWVyIi8+CiAgICAgICAgIDwvb3BlcmFuZD4KICAgICAgPC9kZWY+CiAgICAgIDxkZWYgbG9jYXRvcj0iMjM3OjEtMjM3OjU4IiBuYW1lPSJUb1N0cmluZyIgY29udGV4dD0iUGF0aWVudCIgYWNjZXNzTGV2ZWw9IlB1YmxpYyIgeHNpOnR5cGU9IkZ1bmN0aW9uRGVmIj4KICAgICAgICAgPGV4cHJlc3Npb24gbG9jYXRvcj0iMjM3OjQ4LTIzNzo1OCIgcGF0aD0idmFsdWUiIHhzaTp0eXBlPSJQcm9wZXJ0eSI+CiAgICAgICAgICAgIDxzb3VyY2UgbG9jYXRvcj0iMjM3OjQ4LTIzNzo1MiIgbmFtZT0idmFsdWUiIHhzaTp0eXBlPSJPcGVyYW5kUmVmIi8+CiAgICAgICAgIDwvZXhwcmVzc2lvbj4KICAgICAgICAgPG9wZXJhbmQgbmFtZT0idmFsdWUiPgogICAgICAgICAgICA8b3BlcmFuZFR5cGVTcGVjaWZpZXIgbG9jYXRvcj0iMjM3OjMyLTIzNzo0NCIgbmFtZT0iZmhpcjpSZXF1ZXN0SW50ZW50IiB4c2k6dHlwZT0iTmFtZWRUeXBlU3BlY2lmaWVyIi8+CiAgICAgICAgIDwvb3BlcmFuZD4KICAgICAgPC9kZWY+CiAgICAgIDxkZWYgbG9jYXRvcj0iMjM4OjEtMjM4OjYwIiBuYW1lPSJUb1N0cmluZyIgY29udGV4dD0iUGF0aWVudCIgYWNjZXNzTGV2ZWw9IlB1YmxpYyIgeHNpOnR5cGU9IkZ1bmN0aW9uRGVmIj4KICAgICAgICAgPGV4cHJlc3Npb24gbG9jYXRvcj0iMjM4OjUwLTIzODo2MCIgcGF0aD0idmFsdWUiIHhzaTp0eXBlPSJQcm9wZXJ0eSI+CiAgICAgICAgICAgIDxzb3VyY2UgbG9jYXRvcj0iMjM4OjUwLTIzODo1NCIgbmFtZT0idmFsdWUiIHhzaTp0eXBlPSJPcGVyYW5kUmVmIi8+CiAgICAgICAgIDwvZXhwcmVzc2lvbj4KICAgICAgICAgPG9wZXJhbmQgbmFtZT0idmFsdWUiPgogICAgICAgICAgICA8b3BlcmFuZFR5cGVTcGVjaWZpZXIgbG9jYXRvcj0iMjM4OjMyLTIzODo0NiIgbmFtZT0iZmhpcjpSZXF1ZXN0UHJpb3JpdHkiIHhzaTp0eXBlPSJOYW1lZFR5cGVTcGVjaWZpZXIiLz4KICAgICAgICAgPC9vcGVyYW5kPgogICAgICA8L2RlZj4KICAgICAgPGRlZiBsb2NhdG9yPSIyMzk6MS0yMzk6NTgiIG5hbWU9IlRvU3RyaW5nIiBjb250ZXh0PSJQYXRpZW50IiBhY2Nlc3NMZXZlbD0iUHVibGljIiB4c2k6dHlwZT0iRnVuY3Rpb25EZWYiPgogICAgICAgICA8ZXhwcmVzc2lvbiBsb2NhdG9yPSIyMzk6NDgtMjM5OjU4IiBwYXRoPSJ2YWx1ZSIgeHNpOnR5cGU9IlByb3BlcnR5Ij4KICAgICAgICAgICAgPHNvdXJjZSBsb2NhdG9yPSIyMzk6NDgtMjM5OjUyIiBuYW1lPSJ2YWx1ZSIgeHNpOnR5cGU9Ik9wZXJhbmRSZWYiLz4KICAgICAgICAgPC9leHByZXNzaW9uPgogICAgICAgICA8b3BlcmFuZCBuYW1lPSJ2YWx1ZSI+CiAgICAgICAgICAgIDxvcGVyYW5kVHlwZVNwZWNpZmllciBsb2NhdG9yPSIyMzk6MzItMjM5OjQ0IiBuYW1lPSJmaGlyOlJlcXVlc3RTdGF0dXMiIHhzaTp0eXBlPSJOYW1lZFR5cGVTcGVjaWZpZXIiLz4KICAgICAgICAgPC9vcGVyYW5kPgogICAgICA8L2RlZj4KICAgICAgPGRlZiBsb2NhdG9yPSIyNDA6MS0yNDA6NjQiIG5hbWU9IlRvU3RyaW5nIiBjb250ZXh0PSJQYXRpZW50IiBhY2Nlc3NMZXZlbD0iUHVibGljIiB4c2k6dHlwZT0iRnVuY3Rpb25EZWYiPgogICAgICAgICA8ZXhwcmVzc2lvbiBsb2NhdG9yPSIyNDA6NTQtMjQwOjY0IiBwYXRoPSJ2YWx1ZSIgeHNpOnR5cGU9IlByb3BlcnR5Ij4KICAgICAgICAgICAgPHNvdXJjZSBsb2NhdG9yPSIyNDA6NTQtMjQwOjU4IiBuYW1lPSJ2YWx1ZSIgeHNpOnR5cGU9Ik9wZXJhbmRSZWYiLz4KICAgICAgICAgPC9leHByZXNzaW9uPgogICAgICAgICA8b3BlcmFuZCBuYW1lPSJ2YWx1ZSI+CiAgICAgICAgICAgIDxvcGVyYW5kVHlwZVNwZWNpZmllciBsb2NhdG9yPSIyNDA6MzItMjQwOjUwIiBuYW1lPSJmaGlyOlJlc2VhcmNoRWxlbWVudFR5cGUiIHhzaTp0eXBlPSJOYW1lZFR5cGVTcGVjaWZpZXIiLz4KICAgICAgICAgPC9vcGVyYW5kPgogICAgICA8L2RlZj4KICAgICAgPGRlZiBsb2NhdG9yPSIyNDE6MS0yNDE6NjQiIG5hbWU9IlRvU3RyaW5nIiBjb250ZXh0PSJQYXRpZW50IiBhY2Nlc3NMZXZlbD0iUHVibGljIiB4c2k6dHlwZT0iRnVuY3Rpb25EZWYiPgogICAgICAgICA8ZXhwcmVzc2lvbiBsb2NhdG9yPSIyNDE6NTQtMjQxOjY0IiBwYXRoPSJ2YWx1ZSIgeHNpOnR5cGU9IlByb3BlcnR5Ij4KICAgICAgICAgICAgPHNvdXJjZSBsb2NhdG9yPSIyNDE6NTQtMjQxOjU4IiBuYW1lPSJ2YWx1ZSIgeHNpOnR5cGU9Ik9wZXJhbmRSZWYiLz4KICAgICAgICAgPC9leHByZXNzaW9uPgogICAgICAgICA8b3BlcmFuZCBuYW1lPSJ2YWx1ZSI+CiAgICAgICAgICAgIDxvcGVyYW5kVHlwZVNwZWNpZmllciBsb2NhdG9yPSIyNDE6MzItMjQxOjUwIiBuYW1lPSJmaGlyOlJlc2VhcmNoU3R1ZHlTdGF0dXMiIHhzaTp0eXBlPSJOYW1lZFR5cGVTcGVjaWZpZXIiLz4KICAgICAgICAgPC9vcGVyYW5kPgogICAgICA8L2RlZj4KICAgICAgPGRlZiBsb2NhdG9yPSIyNDI6MS0yNDI6NjYiIG5hbWU9IlRvU3RyaW5nIiBjb250ZXh0PSJQYXRpZW50IiBhY2Nlc3NMZXZlbD0iUHVibGljIiB4c2k6dHlwZT0iRnVuY3Rpb25EZWYiPgogICAgICAgICA8ZXhwcmVzc2lvbiBsb2NhdG9yPSIyNDI6NTYtMjQyOjY2IiBwYXRoPSJ2YWx1ZSIgeHNpOnR5cGU9IlByb3BlcnR5Ij4KICAgICAgICAgICAgPHNvdXJjZSBsb2NhdG9yPSIyNDI6NTYtMjQyOjYwIiBuYW1lPSJ2YWx1ZSIgeHNpOnR5cGU9Ik9wZXJhbmRSZWYiLz4KICAgICAgICAgPC9leHByZXNzaW9uPgogICAgICAgICA8b3BlcmFuZCBuYW1lPSJ2YWx1ZSI+CiAgICAgICAgICAgIDxvcGVyYW5kVHlwZVNwZWNpZmllciBsb2NhdG9yPSIyNDI6MzItMjQyOjUyIiBuYW1lPSJmaGlyOlJlc2VhcmNoU3ViamVjdFN0YXR1cyIgeHNpOnR5cGU9Ik5hbWVkVHlwZVNwZWNpZmllciIvPgogICAgICAgICA8L29wZXJhbmQ+CiAgICAgIDwvZGVmPgogICAgICA8ZGVmIGxvY2F0b3I9IjI0MzoxLTI0Mzo1NyIgbmFtZT0iVG9TdHJpbmciIGNvbnRleHQ9IlBhdGllbnQiIGFjY2Vzc0xldmVsPSJQdWJsaWMiIHhzaTp0eXBlPSJGdW5jdGlvbkRlZiI+CiAgICAgICAgIDxleHByZXNzaW9uIGxvY2F0b3I9IjI0Mzo0Ny0yNDM6NTciIHBhdGg9InZhbHVlIiB4c2k6dHlwZT0iUHJvcGVydHkiPgogICAgICAgICAgICA8c291cmNlIGxvY2F0b3I9IjI0Mzo0Ny0yNDM6NTEiIG5hbWU9InZhbHVlIiB4c2k6dHlwZT0iT3BlcmFuZFJlZiIvPgogICAgICAgICA8L2V4cHJlc3Npb24+CiAgICAgICAgIDxvcGVyYW5kIG5hbWU9InZhbHVlIj4KICAgICAgICAgICAgPG9wZXJhbmRUeXBlU3BlY2lmaWVyIGxvY2F0b3I9IjI0MzozMi0yNDM6NDMiIG5hbWU9ImZoaXI6UmVzb3VyY2VUeXBlIiB4c2k6dHlwZT0iTmFtZWRUeXBlU3BlY2lmaWVyIi8+CiAgICAgICAgIDwvb3BlcmFuZD4KICAgICAgPC9kZWY+CiAgICAgIDxkZWYgbG9jYXRvcj0iMjQ0OjEtMjQ0OjY2IiBuYW1lPSJUb1N0cmluZyIgY29udGV4dD0iUGF0aWVudCIgYWNjZXNzTGV2ZWw9IlB1YmxpYyIgeHNpOnR5cGU9IkZ1bmN0aW9uRGVmIj4KICAgICAgICAgPGV4cHJlc3Npb24gbG9jYXRvcj0iMjQ0OjU2LTI0NDo2NiIgcGF0aD0idmFsdWUiIHhzaTp0eXBlPSJQcm9wZXJ0eSI+CiAgICAgICAgICAgIDxzb3VyY2UgbG9jYXRvcj0iMjQ0OjU2LTI0NDo2MCIgbmFtZT0idmFsdWUiIHhzaTp0eXBlPSJPcGVyYW5kUmVmIi8+CiAgICAgICAgIDwvZXhwcmVzc2lvbj4KICAgICAgICAgPG9wZXJhbmQgbmFtZT0idmFsdWUiPgogICAgICAgICAgICA8b3BlcmFuZFR5cGVTcGVjaWZpZXIgbG9jYXRvcj0iMjQ0OjMyLTI0NDo1MiIgbmFtZT0iZmhpcjpSZXNvdXJjZVZlcnNpb25Qb2xpY3kiIHhzaTp0eXBlPSJOYW1lZFR5cGVTcGVjaWZpZXIiLz4KICAgICAgICAgPC9vcGVyYW5kPgogICAgICA8L2RlZj4KICAgICAgPGRlZiBsb2NhdG9yPSIyNDU6MS0yNDU6NTciIG5hbWU9IlRvU3RyaW5nIiBjb250ZXh0PSJQYXRpZW50IiBhY2Nlc3NMZXZlbD0iUHVibGljIiB4c2k6dHlwZT0iRnVuY3Rpb25EZWYiPgogICAgICAgICA8ZXhwcmVzc2lvbiBsb2NhdG9yPSIyNDU6NDctMjQ1OjU3IiBwYXRoPSJ2YWx1ZSIgeHNpOnR5cGU9IlByb3BlcnR5Ij4KICAgICAgICAgICAgPHNvdXJjZSBsb2NhdG9yPSIyNDU6NDctMjQ1OjUxIiBuYW1lPSJ2YWx1ZSIgeHNpOnR5cGU9Ik9wZXJhbmRSZWYiLz4KICAgICAgICAgPC9leHByZXNzaW9uPgogICAgICAgICA8b3BlcmFuZCBuYW1lPSJ2YWx1ZSI+CiAgICAgICAgICAgIDxvcGVyYW5kVHlwZVNwZWNpZmllciBsb2NhdG9yPSIyNDU6MzItMjQ1OjQzIiBuYW1lPSJmaGlyOlJlc3BvbnNlVHlwZSIgeHNpOnR5cGU9Ik5hbWVkVHlwZVNwZWNpZmllciIvPgogICAgICAgICA8L29wZXJhbmQ+CiAgICAgIDwvZGVmPgogICAgICA8ZGVmIGxvY2F0b3I9IjI0NjoxLTI0Njo2NiIgbmFtZT0iVG9TdHJpbmciIGNvbnRleHQ9IlBhdGllbnQiIGFjY2Vzc0xldmVsPSJQdWJsaWMiIHhzaTp0eXBlPSJGdW5jdGlvbkRlZiI+CiAgICAgICAgIDxleHByZXNzaW9uIGxvY2F0b3I9IjI0Njo1Ni0yNDY6NjYiIHBhdGg9InZhbHVlIiB4c2k6dHlwZT0iUHJvcGVydHkiPgogICAgICAgICAgICA8c291cmNlIGxvY2F0b3I9IjI0Njo1Ni0yNDY6NjAiIG5hbWU9InZhbHVlIiB4c2k6dHlwZT0iT3BlcmFuZFJlZiIvPgogICAgICAgICA8L2V4cHJlc3Npb24+CiAgICAgICAgIDxvcGVyYW5kIG5hbWU9InZhbHVlIj4KICAgICAgICAgICAgPG9wZXJhbmRUeXBlU3BlY2lmaWVyIGxvY2F0b3I9IjI0NjozMi0yNDY6NTIiIG5hbWU9ImZoaXI6UmVzdGZ1bENhcGFiaWxpdHlNb2RlIiB4c2k6dHlwZT0iTmFtZWRUeXBlU3BlY2lmaWVyIi8+CiAgICAgICAgIDwvb3BlcmFuZD4KICAgICAgPC9kZWY+CiAgICAgIDxkZWYgbG9jYXRvcj0iMjQ3OjEtMjQ3OjY1IiBuYW1lPSJUb1N0cmluZyIgY29udGV4dD0iUGF0aWVudCIgYWNjZXNzTGV2ZWw9IlB1YmxpYyIgeHNpOnR5cGU9IkZ1bmN0aW9uRGVmIj4KICAgICAgICAgPGV4cHJlc3Npb24gbG9jYXRvcj0iMjQ3OjU1LTI0Nzo2NSIgcGF0aD0idmFsdWUiIHhzaTp0eXBlPSJQcm9wZXJ0eSI+CiAgICAgICAgICAgIDxzb3VyY2UgbG9jYXRvcj0iMjQ3OjU1LTI0Nzo1OSIgbmFtZT0idmFsdWUiIHhzaTp0eXBlPSJPcGVyYW5kUmVmIi8+CiAgICAgICAgIDwvZXhwcmVzc2lvbj4KICAgICAgICAgPG9wZXJhbmQgbmFtZT0idmFsdWUiPgogICAgICAgICAgICA8b3BlcmFuZFR5cGVTcGVjaWZpZXIgbG9jYXRvcj0iMjQ3OjMyLTI0Nzo1MSIgbmFtZT0iZmhpcjpSaXNrQXNzZXNzbWVudFN0YXR1cyIgeHNpOnR5cGU9Ik5hbWVkVHlwZVNwZWNpZmllciIvPgogICAgICAgICA8L29wZXJhbmQ+CiAgICAgIDwvZGVmPgogICAgICA8ZGVmIGxvY2F0b3I9IjI0ODoxLTI0ODo1NiIgbmFtZT0iVG9TdHJpbmciIGNvbnRleHQ9IlBhdGllbnQiIGFjY2Vzc0xldmVsPSJQdWJsaWMiIHhzaTp0eXBlPSJGdW5jdGlvbkRlZiI+CiAgICAgICAgIDxleHByZXNzaW9uIGxvY2F0b3I9IjI0ODo0Ni0yNDg6NTYiIHBhdGg9InZhbHVlIiB4c2k6dHlwZT0iUHJvcGVydHkiPgogICAgICAgICAgICA8c291cmNlIGxvY2F0b3I9IjI0ODo0Ni0yNDg6NTAiIG5hbWU9InZhbHVlIiB4c2k6dHlwZT0iT3BlcmFuZFJlZiIvPgogICAgICAgICA8L2V4cHJlc3Npb24+CiAgICAgICAgIDxvcGVyYW5kIG5hbWU9InZhbHVlIj4KICAgICAgICAgICAgPG9wZXJhbmRUeXBlU3BlY2lmaWVyIGxvY2F0b3I9IjI0ODozMi0yNDg6NDIiIG5hbWU9ImZoaXI6U1BEWExpY2Vuc2UiIHhzaTp0eXBlPSJOYW1lZFR5cGVTcGVjaWZpZXIiLz4KICAgICAgICAgPC9vcGVyYW5kPgogICAgICA8L2RlZj4KICAgICAgPGRlZiBsb2NhdG9yPSIyNDk6MS0yNDk6NjEiIG5hbWU9IlRvU3RyaW5nIiBjb250ZXh0PSJQYXRpZW50IiBhY2Nlc3NMZXZlbD0iUHVibGljIiB4c2k6dHlwZT0iRnVuY3Rpb25EZWYiPgogICAgICAgICA8ZXhwcmVzc2lvbiBsb2NhdG9yPSIyNDk6NTEtMjQ5OjYxIiBwYXRoPSJ2YWx1ZSIgeHNpOnR5cGU9IlByb3BlcnR5Ij4KICAgICAgICAgICAgPHNvdXJjZSBsb2NhdG9yPSIyNDk6NTEtMjQ5OjU1IiBuYW1lPSJ2YWx1ZSIgeHNpOnR5cGU9Ik9wZXJhbmRSZWYiLz4KICAgICAgICAgPC9leHByZXNzaW9uPgogICAgICAgICA8b3BlcmFuZCBuYW1lPSJ2YWx1ZSI+CiAgICAgICAgICAgIDxvcGVyYW5kVHlwZVNwZWNpZmllciBsb2NhdG9yPSIyNDk6MzItMjQ5OjQ3IiBuYW1lPSJmaGlyOlNlYXJjaENvbXBhcmF0b3IiIHhzaTp0eXBlPSJOYW1lZFR5cGVTcGVjaWZpZXIiLz4KICAgICAgICAgPC9vcGVyYW5kPgogICAgICA8L2RlZj4KICAgICAgPGRlZiBsb2NhdG9yPSIyNTA6MS0yNTA6NjAiIG5hbWU9IlRvU3RyaW5nIiBjb250ZXh0PSJQYXRpZW50IiBhY2Nlc3NMZXZlbD0iUHVibGljIiB4c2k6dHlwZT0iRnVuY3Rpb25EZWYiPgogICAgICAgICA8ZXhwcmVzc2lvbiBsb2NhdG9yPSIyNTA6NTAtMjUwOjYwIiBwYXRoPSJ2YWx1ZSIgeHNpOnR5cGU9IlByb3BlcnR5Ij4KICAgICAgICAgICAgPHNvdXJjZSBsb2NhdG9yPSIyNTA6NTAtMjUwOjU0IiBuYW1lPSJ2YWx1ZSIgeHNpOnR5cGU9Ik9wZXJhbmRSZWYiLz4KICAgICAgICAgPC9leHByZXNzaW9uPgogICAgICAgICA8b3BlcmFuZCBuYW1lPSJ2YWx1ZSI+CiAgICAgICAgICAgIDxvcGVyYW5kVHlwZVNwZWNpZmllciBsb2NhdG9yPSIyNTA6MzItMjUwOjQ2IiBuYW1lPSJmaGlyOlNlYXJjaEVudHJ5TW9kZSIgeHNpOnR5cGU9Ik5hbWVkVHlwZVNwZWNpZmllciIvPgogICAgICAgICA8L29wZXJhbmQ+CiAgICAgIDwvZGVmPgogICAgICA8ZGVmIGxvY2F0b3I9IjI1MToxLTI1MTo2MyIgbmFtZT0iVG9TdHJpbmciIGNvbnRleHQ9IlBhdGllbnQiIGFjY2Vzc0xldmVsPSJQdWJsaWMiIHhzaTp0eXBlPSJGdW5jdGlvbkRlZiI+CiAgICAgICAgIDxleHByZXNzaW9uIGxvY2F0b3I9IjI1MTo1My0yNTE6NjMiIHBhdGg9InZhbHVlIiB4c2k6dHlwZT0iUHJvcGVydHkiPgogICAgICAgICAgICA8c291cmNlIGxvY2F0b3I9IjI1MTo1My0yNTE6NTciIG5hbWU9InZhbHVlIiB4c2k6dHlwZT0iT3BlcmFuZFJlZiIvPgogICAgICAgICA8L2V4cHJlc3Npb24+CiAgICAgICAgIDxvcGVyYW5kIG5hbWU9InZhbHVlIj4KICAgICAgICAgICAgPG9wZXJhbmRUeXBlU3BlY2lmaWVyIGxvY2F0b3I9IjI1MTozMi0yNTE6NDkiIG5hbWU9ImZoaXI6U2VhcmNoTW9kaWZpZXJDb2RlIiB4c2k6dHlwZT0iTmFtZWRUeXBlU3BlY2lmaWVyIi8+CiAgICAgICAgIDwvb3BlcmFuZD4KICAgICAgPC9kZWY+CiAgICAgIDxkZWYgbG9jYXRvcj0iMjUyOjEtMjUyOjYwIiBuYW1lPSJUb1N0cmluZyIgY29udGV4dD0iUGF0aWVudCIgYWNjZXNzTGV2ZWw9IlB1YmxpYyIgeHNpOnR5cGU9IkZ1bmN0aW9uRGVmIj4KICAgICAgICAgPGV4cHJlc3Npb24gbG9jYXRvcj0iMjUyOjUwLTI1Mjo2MCIgcGF0aD0idmFsdWUiIHhzaTp0eXBlPSJQcm9wZXJ0eSI+CiAgICAgICAgICAgIDxzb3VyY2UgbG9jYXRvcj0iMjUyOjUwLTI1Mjo1NCIgbmFtZT0idmFsdWUiIHhzaTp0eXBlPSJPcGVyYW5kUmVmIi8+CiAgICAgICAgIDwvZXhwcmVzc2lvbj4KICAgICAgICAgPG9wZXJhbmQgbmFtZT0idmFsdWUiPgogICAgICAgICAgICA8b3BlcmFuZFR5cGVTcGVjaWZpZXIgbG9jYXRvcj0iMjUyOjMyLTI1Mjo0NiIgbmFtZT0iZmhpcjpTZWFyY2hQYXJhbVR5cGUiIHhzaTp0eXBlPSJOYW1lZFR5cGVTcGVjaWZpZXIiLz4KICAgICAgICAgPC9vcGVyYW5kPgogICAgICA8L2RlZj4KICAgICAgPGRlZiBsb2NhdG9yPSIyNTM6MS0yNTM6NTYiIG5hbWU9IlRvU3RyaW5nIiBjb250ZXh0PSJQYXRpZW50IiBhY2Nlc3NMZXZlbD0iUHVibGljIiB4c2k6dHlwZT0iRnVuY3Rpb25EZWYiPgogICAgICAgICA8ZXhwcmVzc2lvbiBsb2NhdG9yPSIyNTM6NDYtMjUzOjU2IiBwYXRoPSJ2YWx1ZSIgeHNpOnR5cGU9IlByb3BlcnR5Ij4KICAgICAgICAgICAgPHNvdXJjZSBsb2NhdG9yPSIyNTM6NDYtMjUzOjUwIiBuYW1lPSJ2YWx1ZSIgeHNpOnR5cGU9Ik9wZXJhbmRSZWYiLz4KICAgICAgICAgPC9leHByZXNzaW9uPgogICAgICAgICA8b3BlcmFuZCBuYW1lPSJ2YWx1ZSI+CiAgICAgICAgICAgIDxvcGVyYW5kVHlwZVNwZWNpZmllciBsb2NhdG9yPSIyNTM6MzItMjUzOjQyIiBuYW1lPSJmaGlyOlNlY3Rpb25Nb2RlIiB4c2k6dHlwZT0iTmFtZWRUeXBlU3BlY2lmaWVyIi8+CiAgICAgICAgIDwvb3BlcmFuZD4KICAgICAgPC9kZWY+CiAgICAgIDxkZWYgbG9jYXRvcj0iMjU0OjEtMjU0OjU3IiBuYW1lPSJUb1N0cmluZyIgY29udGV4dD0iUGF0aWVudCIgYWNjZXNzTGV2ZWw9IlB1YmxpYyIgeHNpOnR5cGU9IkZ1bmN0aW9uRGVmIj4KICAgICAgICAgPGV4cHJlc3Npb24gbG9jYXRvcj0iMjU0OjQ3LTI1NDo1NyIgcGF0aD0idmFsdWUiIHhzaTp0eXBlPSJQcm9wZXJ0eSI+CiAgICAgICAgICAgIDxzb3VyY2UgbG9jYXRvcj0iMjU0OjQ3LTI1NDo1MSIgbmFtZT0idmFsdWUiIHhzaTp0eXBlPSJPcGVyYW5kUmVmIi8+CiAgICAgICAgIDwvZXhwcmVzc2lvbj4KICAgICAgICAgPG9wZXJhbmQgbmFtZT0idmFsdWUiPgogICAgICAgICAgICA8b3BlcmFuZFR5cGVTcGVjaWZpZXIgbG9jYXRvcj0iMjU0OjMyLTI1NDo0MyIgbmFtZT0iZmhpcjpTZXF1ZW5jZVR5cGUiIHhzaTp0eXBlPSJOYW1lZFR5cGVTcGVjaWZpZXIiLz4KICAgICAgICAgPC9vcGVyYW5kPgogICAgICA8L2RlZj4KICAgICAgPGRlZiBsb2NhdG9yPSIyNTU6MS0yNTU6NjUiIG5hbWU9IlRvU3RyaW5nIiBjb250ZXh0PSJQYXRpZW50IiBhY2Nlc3NMZXZlbD0iUHVibGljIiB4c2k6dHlwZT0iRnVuY3Rpb25EZWYiPgogICAgICAgICA8ZXhwcmVzc2lvbiBsb2NhdG9yPSIyNTU6NTUtMjU1OjY1IiBwYXRoPSJ2YWx1ZSIgeHNpOnR5cGU9IlByb3BlcnR5Ij4KICAgICAgICAgICAgPHNvdXJjZSBsb2NhdG9yPSIyNTU6NTUtMjU1OjU5IiBuYW1lPSJ2YWx1ZSIgeHNpOnR5cGU9Ik9wZXJhbmRSZWYiLz4KICAgICAgICAgPC9leHByZXNzaW9uPgogICAgICAgICA8b3BlcmFuZCBuYW1lPSJ2YWx1ZSI+CiAgICAgICAgICAgIDxvcGVyYW5kVHlwZVNwZWNpZmllciBsb2NhdG9yPSIyNTU6MzItMjU1OjUxIiBuYW1lPSJmaGlyOlNlcnZpY2VSZXF1ZXN0SW50ZW50IiB4c2k6dHlwZT0iTmFtZWRUeXBlU3BlY2lmaWVyIi8+CiAgICAgICAgIDwvb3BlcmFuZD4KICAgICAgPC9kZWY+CiAgICAgIDxkZWYgbG9jYXRvcj0iMjU2OjEtMjU2OjY3IiBuYW1lPSJUb1N0cmluZyIgY29udGV4dD0iUGF0aWVudCIgYWNjZXNzTGV2ZWw9IlB1YmxpYyIgeHNpOnR5cGU9IkZ1bmN0aW9uRGVmIj4KICAgICAgICAgPGV4cHJlc3Npb24gbG9jYXRvcj0iMjU2OjU3LTI1Njo2NyIgcGF0aD0idmFsdWUiIHhzaTp0eXBlPSJQcm9wZXJ0eSI+CiAgICAgICAgICAgIDxzb3VyY2UgbG9jYXRvcj0iMjU2OjU3LTI1Njo2MSIgbmFtZT0idmFsdWUiIHhzaTp0eXBlPSJPcGVyYW5kUmVmIi8+CiAgICAgICAgIDwvZXhwcmVzc2lvbj4KICAgICAgICAgPG9wZXJhbmQgbmFtZT0idmFsdWUiPgogICAgICAgICAgICA8b3BlcmFuZFR5cGVTcGVjaWZpZXIgbG9jYXRvcj0iMjU2OjMyLTI1Njo1MyIgbmFtZT0iZmhpcjpTZXJ2aWNlUmVxdWVzdFByaW9yaXR5IiB4c2k6dHlwZT0iTmFtZWRUeXBlU3BlY2lmaWVyIi8+CiAgICAgICAgIDwvb3BlcmFuZD4KICAgICAgPC9kZWY+CiAgICAgIDxkZWYgbG9jYXRvcj0iMjU3OjEtMjU3OjY1IiBuYW1lPSJUb1N0cmluZyIgY29udGV4dD0iUGF0aWVudCIgYWNjZXNzTGV2ZWw9IlB1YmxpYyIgeHNpOnR5cGU9IkZ1bmN0aW9uRGVmIj4KICAgICAgICAgPGV4cHJlc3Npb24gbG9jYXRvcj0iMjU3OjU1LTI1Nzo2NSIgcGF0aD0idmFsdWUiIHhzaTp0eXBlPSJQcm9wZXJ0eSI+CiAgICAgICAgICAgIDxzb3VyY2UgbG9jYXRvcj0iMjU3OjU1LTI1Nzo1OSIgbmFtZT0idmFsdWUiIHhzaTp0eXBlPSJPcGVyYW5kUmVmIi8+CiAgICAgICAgIDwvZXhwcmVzc2lvbj4KICAgICAgICAgPG9wZXJhbmQgbmFtZT0idmFsdWUiPgogICAgICAgICAgICA8b3BlcmFuZFR5cGVTcGVjaWZpZXIgbG9jYXRvcj0iMjU3OjMyLTI1Nzo1MSIgbmFtZT0iZmhpcjpTZXJ2aWNlUmVxdWVzdFN0YXR1cyIgeHNpOnR5cGU9Ik5hbWVkVHlwZVNwZWNpZmllciIvPgogICAgICAgICA8L29wZXJhbmQ+CiAgICAgIDwvZGVmPgogICAgICA8ZGVmIGxvY2F0b3I9IjI1ODoxLTI1ODo1NyIgbmFtZT0iVG9TdHJpbmciIGNvbnRleHQ9IlBhdGllbnQiIGFjY2Vzc0xldmVsPSJQdWJsaWMiIHhzaTp0eXBlPSJGdW5jdGlvbkRlZiI+CiAgICAgICAgIDxleHByZXNzaW9uIGxvY2F0b3I9IjI1ODo0Ny0yNTg6NTciIHBhdGg9InZhbHVlIiB4c2k6dHlwZT0iUHJvcGVydHkiPgogICAgICAgICAgICA8c291cmNlIGxvY2F0b3I9IjI1ODo0Ny0yNTg6NTEiIG5hbWU9InZhbHVlIiB4c2k6dHlwZT0iT3BlcmFuZFJlZiIvPgogICAgICAgICA8L2V4cHJlc3Npb24+CiAgICAgICAgIDxvcGVyYW5kIG5hbWU9InZhbHVlIj4KICAgICAgICAgICAgPG9wZXJhbmRUeXBlU3BlY2lmaWVyIGxvY2F0b3I9IjI1ODozMi0yNTg6NDMiIG5hbWU9ImZoaXI6U2xpY2luZ1J1bGVzIiB4c2k6dHlwZT0iTmFtZWRUeXBlU3BlY2lmaWVyIi8+CiAgICAgICAgIDwvb3BlcmFuZD4KICAgICAgPC9kZWY+CiAgICAgIDxkZWYgbG9jYXRvcj0iMjU5OjEtMjU5OjU1IiBuYW1lPSJUb1N0cmluZyIgY29udGV4dD0iUGF0aWVudCIgYWNjZXNzTGV2ZWw9IlB1YmxpYyIgeHNpOnR5cGU9IkZ1bmN0aW9uRGVmIj4KICAgICAgICAgPGV4cHJlc3Npb24gbG9jYXRvcj0iMjU5OjQ1LTI1OTo1NSIgcGF0aD0idmFsdWUiIHhzaTp0eXBlPSJQcm9wZXJ0eSI+CiAgICAgICAgICAgIDxzb3VyY2UgbG9jYXRvcj0iMjU5OjQ1LTI1OTo0OSIgbmFtZT0idmFsdWUiIHhzaTp0eXBlPSJPcGVyYW5kUmVmIi8+CiAgICAgICAgIDwvZXhwcmVzc2lvbj4KICAgICAgICAgPG9wZXJhbmQgbmFtZT0idmFsdWUiPgogICAgICAgICAgICA8b3BlcmFuZFR5cGVTcGVjaWZpZXIgbG9jYXRvcj0iMjU5OjMyLTI1OTo0MSIgbmFtZT0iZmhpcjpTbG90U3RhdHVzIiB4c2k6dHlwZT0iTmFtZWRUeXBlU3BlY2lmaWVyIi8+CiAgICAgICAgIDwvb3BlcmFuZD4KICAgICAgPC9kZWY+CiAgICAgIDxkZWYgbG9jYXRvcj0iMjYwOjEtMjYwOjU4IiBuYW1lPSJUb1N0cmluZyIgY29udGV4dD0iUGF0aWVudCIgYWNjZXNzTGV2ZWw9IlB1YmxpYyIgeHNpOnR5cGU9IkZ1bmN0aW9uRGVmIj4KICAgICAgICAgPGV4cHJlc3Npb24gbG9jYXRvcj0iMjYwOjQ4LTI2MDo1OCIgcGF0aD0idmFsdWUiIHhzaTp0eXBlPSJQcm9wZXJ0eSI+CiAgICAgICAgICAgIDxzb3VyY2UgbG9jYXRvcj0iMjYwOjQ4LTI2MDo1MiIgbmFtZT0idmFsdWUiIHhzaTp0eXBlPSJPcGVyYW5kUmVmIi8+CiAgICAgICAgIDwvZXhwcmVzc2lvbj4KICAgICAgICAgPG9wZXJhbmQgbmFtZT0idmFsdWUiPgogICAgICAgICAgICA8b3BlcmFuZFR5cGVTcGVjaWZpZXIgbG9jYXRvcj0iMjYwOjMyLTI2MDo0NCIgbmFtZT0iZmhpcjpTb3J0RGlyZWN0aW9uIiB4c2k6dHlwZT0iTmFtZWRUeXBlU3BlY2lmaWVyIi8+CiAgICAgICAgIDwvb3BlcmFuZD4KICAgICAgPC9kZWY+CiAgICAgIDxkZWYgbG9jYXRvcj0iMjYxOjEtMjYxOjcyIiBuYW1lPSJUb1N0cmluZyIgY29udGV4dD0iUGF0aWVudCIgYWNjZXNzTGV2ZWw9IlB1YmxpYyIgeHNpOnR5cGU9IkZ1bmN0aW9uRGVmIj4KICAgICAgICAgPGV4cHJlc3Npb24gbG9jYXRvcj0iMjYxOjYyLTI2MTo3MiIgcGF0aD0idmFsdWUiIHhzaTp0eXBlPSJQcm9wZXJ0eSI+CiAgICAgICAgICAgIDxzb3VyY2UgbG9jYXRvcj0iMjYxOjYyLTI2MTo2NiIgbmFtZT0idmFsdWUiIHhzaTp0eXBlPSJPcGVyYW5kUmVmIi8+CiAgICAgICAgIDwvZXhwcmVzc2lvbj4KICAgICAgICAgPG9wZXJhbmQgbmFtZT0idmFsdWUiPgogICAgICAgICAgICA8b3BlcmFuZFR5cGVTcGVjaWZpZXIgbG9jYXRvcj0iMjYxOjMyLTI2MTo1OCIgbmFtZT0iZmhpcjpTcGVjaW1lbkNvbnRhaW5lZFByZWZlcmVuY2UiIHhzaTp0eXBlPSJOYW1lZFR5cGVTcGVjaWZpZXIiLz4KICAgICAgICAgPC9vcGVyYW5kPgogICAgICA8L2RlZj4KICAgICAgPGRlZiBsb2NhdG9yPSIyNjI6MS0yNjI6NTkiIG5hbWU9IlRvU3RyaW5nIiBjb250ZXh0PSJQYXRpZW50IiBhY2Nlc3NMZXZlbD0iUHVibGljIiB4c2k6dHlwZT0iRnVuY3Rpb25EZWYiPgogICAgICAgICA8ZXhwcmVzc2lvbiBsb2NhdG9yPSIyNjI6NDktMjYyOjU5IiBwYXRoPSJ2YWx1ZSIgeHNpOnR5cGU9IlByb3BlcnR5Ij4KICAgICAgICAgICAgPHNvdXJjZSBsb2NhdG9yPSIyNjI6NDktMjYyOjUzIiBuYW1lPSJ2YWx1ZSIgeHNpOnR5cGU9Ik9wZXJhbmRSZWYiLz4KICAgICAgICAgPC9leHByZXNzaW9uPgogICAgICAgICA8b3BlcmFuZCBuYW1lPSJ2YWx1ZSI+CiAgICAgICAgICAgIDxvcGVyYW5kVHlwZVNwZWNpZmllciBsb2NhdG9yPSIyNjI6MzItMjYyOjQ1IiBuYW1lPSJmaGlyOlNwZWNpbWVuU3RhdHVzIiB4c2k6dHlwZT0iTmFtZWRUeXBlU3BlY2lmaWVyIi8+CiAgICAgICAgIDwvb3BlcmFuZD4KICAgICAgPC9kZWY+CiAgICAgIDxkZWYgbG9jYXRvcj0iMjYzOjEtMjYzOjUxIiBuYW1lPSJUb1N0cmluZyIgY29udGV4dD0iUGF0aWVudCIgYWNjZXNzTGV2ZWw9IlB1YmxpYyIgeHNpOnR5cGU9IkZ1bmN0aW9uRGVmIj4KICAgICAgICAgPGV4cHJlc3Npb24gbG9jYXRvcj0iMjYzOjQxLTI2Mzo1MSIgcGF0aD0idmFsdWUiIHhzaTp0eXBlPSJQcm9wZXJ0eSI+CiAgICAgICAgICAgIDxzb3VyY2UgbG9jYXRvcj0iMjYzOjQxLTI2Mzo0NSIgbmFtZT0idmFsdWUiIHhzaTp0eXBlPSJPcGVyYW5kUmVmIi8+CiAgICAgICAgIDwvZXhwcmVzc2lvbj4KICAgICAgICAgPG9wZXJhbmQgbmFtZT0idmFsdWUiPgogICAgICAgICAgICA8b3BlcmFuZFR5cGVTcGVjaWZpZXIgbG9jYXRvcj0iMjYzOjMyLTI2MzozNyIgbmFtZT0iZmhpcjpTdGF0dXMiIHhzaTp0eXBlPSJOYW1lZFR5cGVTcGVjaWZpZXIiLz4KICAgICAgICAgPC9vcGVyYW5kPgogICAgICA8L2RlZj4KICAgICAgPGRlZiBsb2NhdG9yPSIyNjQ6MS0yNjQ6NTUiIG5hbWU9IlRvU3RyaW5nIiBjb250ZXh0PSJQYXRpZW50IiBhY2Nlc3NMZXZlbD0iUHVibGljIiB4c2k6dHlwZT0iRnVuY3Rpb25EZWYiPgogICAgICAgICA8ZXhwcmVzc2lvbiBsb2NhdG9yPSIyNjQ6NDUtMjY0OjU1IiBwYXRoPSJ2YWx1ZSIgeHNpOnR5cGU9IlByb3BlcnR5Ij4KICAgICAgICAgICAgPHNvdXJjZSBsb2NhdG9yPSIyNjQ6NDUtMjY0OjQ5IiBuYW1lPSJ2YWx1ZSIgeHNpOnR5cGU9Ik9wZXJhbmRSZWYiLz4KICAgICAgICAgPC9leHByZXNzaW9uPgogICAgICAgICA8b3BlcmFuZCBuYW1lPSJ2YWx1ZSI+CiAgICAgICAgICAgIDxvcGVyYW5kVHlwZVNwZWNpZmllciBsb2NhdG9yPSIyNjQ6MzItMjY0OjQxIiBuYW1lPSJmaGlyOlN0cmFuZFR5cGUiIHhzaTp0eXBlPSJOYW1lZFR5cGVTcGVjaWZpZXIiLz4KICAgICAgICAgPC9vcGVyYW5kPgogICAgICA8L2RlZj4KICAgICAgPGRlZiBsb2NhdG9yPSIyNjU6MS0yNjU6NjgiIG5hbWU9IlRvU3RyaW5nIiBjb250ZXh0PSJQYXRpZW50IiBhY2Nlc3NMZXZlbD0iUHVibGljIiB4c2k6dHlwZT0iRnVuY3Rpb25EZWYiPgogICAgICAgICA8ZXhwcmVzc2lvbiBsb2NhdG9yPSIyNjU6NTgtMjY1OjY4IiBwYXRoPSJ2YWx1ZSIgeHNpOnR5cGU9IlByb3BlcnR5Ij4KICAgICAgICAgICAgPHNvdXJjZSBsb2NhdG9yPSIyNjU6NTgtMjY1OjYyIiBuYW1lPSJ2YWx1ZSIgeHNpOnR5cGU9Ik9wZXJhbmRSZWYiLz4KICAgICAgICAgPC9leHByZXNzaW9uPgogICAgICAgICA8b3BlcmFuZCBuYW1lPSJ2YWx1ZSI+CiAgICAgICAgICAgIDxvcGVyYW5kVHlwZVNwZWNpZmllciBsb2NhdG9yPSIyNjU6MzItMjY1OjU0IiBuYW1lPSJmaGlyOlN0cnVjdHVyZURlZmluaXRpb25LaW5kIiB4c2k6dHlwZT0iTmFtZWRUeXBlU3BlY2lmaWVyIi8+CiAgICAgICAgIDwvb3BlcmFuZD4KICAgICAgPC9kZWY+CiAgICAgIDxkZWYgbG9jYXRvcj0iMjY2OjEtMjY2OjY4IiBuYW1lPSJUb1N0cmluZyIgY29udGV4dD0iUGF0aWVudCIgYWNjZXNzTGV2ZWw9IlB1YmxpYyIgeHNpOnR5cGU9IkZ1bmN0aW9uRGVmIj4KICAgICAgICAgPGV4cHJlc3Npb24gbG9jYXRvcj0iMjY2OjU4LTI2Njo2OCIgcGF0aD0idmFsdWUiIHhzaTp0eXBlPSJQcm9wZXJ0eSI+CiAgICAgICAgICAgIDxzb3VyY2UgbG9jYXRvcj0iMjY2OjU4LTI2Njo2MiIgbmFtZT0idmFsdWUiIHhzaTp0eXBlPSJPcGVyYW5kUmVmIi8+CiAgICAgICAgIDwvZXhwcmVzc2lvbj4KICAgICAgICAgPG9wZXJhbmQgbmFtZT0idmFsdWUiPgogICAgICAgICAgICA8b3BlcmFuZFR5cGVTcGVjaWZpZXIgbG9jYXRvcj0iMjY2OjMyLTI2Njo1NCIgbmFtZT0iZmhpcjpTdHJ1Y3R1cmVNYXBDb250ZXh0VHlwZSIgeHNpOnR5cGU9Ik5hbWVkVHlwZVNwZWNpZmllciIvPgogICAgICAgICA8L29wZXJhbmQ+CiAgICAgIDwvZGVmPgogICAgICA8ZGVmIGxvY2F0b3I9IjI2NzoxLTI2Nzo3MCIgbmFtZT0iVG9TdHJpbmciIGNvbnRleHQ9IlBhdGllbnQiIGFjY2Vzc0xldmVsPSJQdWJsaWMiIHhzaTp0eXBlPSJGdW5jdGlvbkRlZiI+CiAgICAgICAgIDxleHByZXNzaW9uIGxvY2F0b3I9IjI2Nzo2MC0yNjc6NzAiIHBhdGg9InZhbHVlIiB4c2k6dHlwZT0iUHJvcGVydHkiPgogICAgICAgICAgICA8c291cmNlIGxvY2F0b3I9IjI2Nzo2MC0yNjc6NjQiIG5hbWU9InZhbHVlIiB4c2k6dHlwZT0iT3BlcmFuZFJlZiIvPgogICAgICAgICA8L2V4cHJlc3Npb24+CiAgICAgICAgIDxvcGVyYW5kIG5hbWU9InZhbHVlIj4KICAgICAgICAgICAgPG9wZXJhbmRUeXBlU3BlY2lmaWVyIGxvY2F0b3I9IjI2NzozMi0yNjc6NTYiIG5hbWU9ImZoaXI6U3RydWN0dXJlTWFwR3JvdXBUeXBlTW9kZSIgeHNpOnR5cGU9Ik5hbWVkVHlwZVNwZWNpZmllciIvPgogICAgICAgICA8L29wZXJhbmQ+CiAgICAgIDwvZGVmPgogICAgICA8ZGVmIGxvY2F0b3I9IjI2ODoxLTI2ODo2NiIgbmFtZT0iVG9TdHJpbmciIGNvbnRleHQ9IlBhdGllbnQiIGFjY2Vzc0xldmVsPSJQdWJsaWMiIHhzaTp0eXBlPSJGdW5jdGlvbkRlZiI+CiAgICAgICAgIDxleHByZXNzaW9uIGxvY2F0b3I9IjI2ODo1Ni0yNjg6NjYiIHBhdGg9InZhbHVlIiB4c2k6dHlwZT0iUHJvcGVydHkiPgogICAgICAgICAgICA8c291cmNlIGxvY2F0b3I9IjI2ODo1Ni0yNjg6NjAiIG5hbWU9InZhbHVlIiB4c2k6dHlwZT0iT3BlcmFuZFJlZiIvPgogICAgICAgICA8L2V4cHJlc3Npb24+CiAgICAgICAgIDxvcGVyYW5kIG5hbWU9InZhbHVlIj4KICAgICAgICAgICAgPG9wZXJhbmRUeXBlU3BlY2lmaWVyIGxvY2F0b3I9IjI2ODozMi0yNjg6NTIiIG5hbWU9ImZoaXI6U3RydWN0dXJlTWFwSW5wdXRNb2RlIiB4c2k6dHlwZT0iTmFtZWRUeXBlU3BlY2lmaWVyIi8+CiAgICAgICAgIDwvb3BlcmFuZD4KICAgICAgPC9kZWY+CiAgICAgIDxkZWYgbG9jYXRvcj0iMjY5OjEtMjY5OjY2IiBuYW1lPSJUb1N0cmluZyIgY29udGV4dD0iUGF0aWVudCIgYWNjZXNzTGV2ZWw9IlB1YmxpYyIgeHNpOnR5cGU9IkZ1bmN0aW9uRGVmIj4KICAgICAgICAgPGV4cHJlc3Npb24gbG9jYXRvcj0iMjY5OjU2LTI2OTo2NiIgcGF0aD0idmFsdWUiIHhzaTp0eXBlPSJQcm9wZXJ0eSI+CiAgICAgICAgICAgIDxzb3VyY2UgbG9jYXRvcj0iMjY5OjU2LTI2OTo2MCIgbmFtZT0idmFsdWUiIHhzaTp0eXBlPSJPcGVyYW5kUmVmIi8+CiAgICAgICAgIDwvZXhwcmVzc2lvbj4KICAgICAgICAgPG9wZXJhbmQgbmFtZT0idmFsdWUiPgogICAgICAgICAgICA8b3BlcmFuZFR5cGVTcGVjaWZpZXIgbG9jYXRvcj0iMjY5OjMyLTI2OTo1MiIgbmFtZT0iZmhpcjpTdHJ1Y3R1cmVNYXBNb2RlbE1vZGUiIHhzaTp0eXBlPSJOYW1lZFR5cGVTcGVjaWZpZXIiLz4KICAgICAgICAgPC9vcGVyYW5kPgogICAgICA8L2RlZj4KICAgICAgPGRlZiBsb2NhdG9yPSIyNzA6MS0yNzA6NzEiIG5hbWU9IlRvU3RyaW5nIiBjb250ZXh0PSJQYXRpZW50IiBhY2Nlc3NMZXZlbD0iUHVibGljIiB4c2k6dHlwZT0iRnVuY3Rpb25EZWYiPgogICAgICAgICA8ZXhwcmVzc2lvbiBsb2NhdG9yPSIyNzA6NjEtMjcwOjcxIiBwYXRoPSJ2YWx1ZSIgeHNpOnR5cGU9IlByb3BlcnR5Ij4KICAgICAgICAgICAgPHNvdXJjZSBsb2NhdG9yPSIyNzA6NjEtMjcwOjY1IiBuYW1lPSJ2YWx1ZSIgeHNpOnR5cGU9Ik9wZXJhbmRSZWYiLz4KICAgICAgICAgPC9leHByZXNzaW9uPgogICAgICAgICA8b3BlcmFuZCBuYW1lPSJ2YWx1ZSI+CiAgICAgICAgICAgIDxvcGVyYW5kVHlwZVNwZWNpZmllciBsb2NhdG9yPSIyNzA6MzItMjcwOjU3IiBuYW1lPSJmaGlyOlN0cnVjdHVyZU1hcFNvdXJjZUxpc3RNb2RlIiB4c2k6dHlwZT0iTmFtZWRUeXBlU3BlY2lmaWVyIi8+CiAgICAgICAgIDwvb3BlcmFuZD4KICAgICAgPC9kZWY+CiAgICAgIDxkZWYgbG9jYXRvcj0iMjcxOjEtMjcxOjcxIiBuYW1lPSJUb1N0cmluZyIgY29udGV4dD0iUGF0aWVudCIgYWNjZXNzTGV2ZWw9IlB1YmxpYyIgeHNpOnR5cGU9IkZ1bmN0aW9uRGVmIj4KICAgICAgICAgPGV4cHJlc3Npb24gbG9jYXRvcj0iMjcxOjYxLTI3MTo3MSIgcGF0aD0idmFsdWUiIHhzaTp0eXBlPSJQcm9wZXJ0eSI+CiAgICAgICAgICAgIDxzb3VyY2UgbG9jYXRvcj0iMjcxOjYxLTI3MTo2NSIgbmFtZT0idmFsdWUiIHhzaTp0eXBlPSJPcGVyYW5kUmVmIi8+CiAgICAgICAgIDwvZXhwcmVzc2lvbj4KICAgICAgICAgPG9wZXJhbmQgbmFtZT0idmFsdWUiPgogICAgICAgICAgICA8b3BlcmFuZFR5cGVTcGVjaWZpZXIgbG9jYXRvcj0iMjcxOjMyLTI3MTo1NyIgbmFtZT0iZmhpcjpTdHJ1Y3R1cmVNYXBUYXJnZXRMaXN0TW9kZSIgeHNpOnR5cGU9Ik5hbWVkVHlwZVNwZWNpZmllciIvPgogICAgICAgICA8L29wZXJhbmQ+CiAgICAgIDwvZGVmPgogICAgICA8ZGVmIGxvY2F0b3I9IjI3MjoxLTI3Mjo2NiIgbmFtZT0iVG9TdHJpbmciIGNvbnRleHQ9IlBhdGllbnQiIGFjY2Vzc0xldmVsPSJQdWJsaWMiIHhzaTp0eXBlPSJGdW5jdGlvbkRlZiI+CiAgICAgICAgIDxleHByZXNzaW9uIGxvY2F0b3I9IjI3Mjo1Ni0yNzI6NjYiIHBhdGg9InZhbHVlIiB4c2k6dHlwZT0iUHJvcGVydHkiPgogICAgICAgICAgICA8c291cmNlIGxvY2F0b3I9IjI3Mjo1Ni0yNzI6NjAiIG5hbWU9InZhbHVlIiB4c2k6dHlwZT0iT3BlcmFuZFJlZiIvPgogICAgICAgICA8L2V4cHJlc3Npb24+CiAgICAgICAgIDxvcGVyYW5kIG5hbWU9InZhbHVlIj4KICAgICAgICAgICAgPG9wZXJhbmRUeXBlU3BlY2lmaWVyIGxvY2F0b3I9IjI3MjozMi0yNzI6NTIiIG5hbWU9ImZoaXI6U3RydWN0dXJlTWFwVHJhbnNmb3JtIiB4c2k6dHlwZT0iTmFtZWRUeXBlU3BlY2lmaWVyIi8+CiAgICAgICAgIDwvb3BlcmFuZD4KICAgICAgPC9kZWY+CiAgICAgIDxkZWYgbG9jYXRvcj0iMjczOjEtMjczOjY4IiBuYW1lPSJUb1N0cmluZyIgY29udGV4dD0iUGF0aWVudCIgYWNjZXNzTGV2ZWw9IlB1YmxpYyIgeHNpOnR5cGU9IkZ1bmN0aW9uRGVmIj4KICAgICAgICAgPGV4cHJlc3Npb24gbG9jYXRvcj0iMjczOjU4LTI3Mzo2OCIgcGF0aD0idmFsdWUiIHhzaTp0eXBlPSJQcm9wZXJ0eSI+CiAgICAgICAgICAgIDxzb3VyY2UgbG9jYXRvcj0iMjczOjU4LTI3Mzo2MiIgbmFtZT0idmFsdWUiIHhzaTp0eXBlPSJPcGVyYW5kUmVmIi8+CiAgICAgICAgIDwvZXhwcmVzc2lvbj4KICAgICAgICAgPG9wZXJhbmQgbmFtZT0idmFsdWUiPgogICAgICAgICAgICA8b3BlcmFuZFR5cGVTcGVjaWZpZXIgbG9jYXRvcj0iMjczOjMyLTI3Mzo1NCIgbmFtZT0iZmhpcjpTdWJzY3JpcHRpb25DaGFubmVsVHlwZSIgeHNpOnR5cGU9Ik5hbWVkVHlwZVNwZWNpZmllciIvPgogICAgICAgICA8L29wZXJhbmQ+CiAgICAgIDwvZGVmPgogICAgICA8ZGVmIGxvY2F0b3I9IjI3NDoxLTI3NDo2MyIgbmFtZT0iVG9TdHJpbmciIGNvbnRleHQ9IlBhdGllbnQiIGFjY2Vzc0xldmVsPSJQdWJsaWMiIHhzaTp0eXBlPSJGdW5jdGlvbkRlZiI+CiAgICAgICAgIDxleHByZXNzaW9uIGxvY2F0b3I9IjI3NDo1My0yNzQ6NjMiIHBhdGg9InZhbHVlIiB4c2k6dHlwZT0iUHJvcGVydHkiPgogICAgICAgICAgICA8c291cmNlIGxvY2F0b3I9IjI3NDo1My0yNzQ6NTciIG5hbWU9InZhbHVlIiB4c2k6dHlwZT0iT3BlcmFuZFJlZiIvPgogICAgICAgICA8L2V4cHJlc3Npb24+CiAgICAgICAgIDxvcGVyYW5kIG5hbWU9InZhbHVlIj4KICAgICAgICAgICAgPG9wZXJhbmRUeXBlU3BlY2lmaWVyIGxvY2F0b3I9IjI3NDozMi0yNzQ6NDkiIG5hbWU9ImZoaXI6U3Vic2NyaXB0aW9uU3RhdHVzIiB4c2k6dHlwZT0iTmFtZWRUeXBlU3BlY2lmaWVyIi8+CiAgICAgICAgIDwvb3BlcmFuZD4KICAgICAgPC9kZWY+CiAgICAgIDxkZWYgbG9jYXRvcj0iMjc1OjEtMjc1OjY1IiBuYW1lPSJUb1N0cmluZyIgY29udGV4dD0iUGF0aWVudCIgYWNjZXNzTGV2ZWw9IlB1YmxpYyIgeHNpOnR5cGU9IkZ1bmN0aW9uRGVmIj4KICAgICAgICAgPGV4cHJlc3Npb24gbG9jYXRvcj0iMjc1OjU1LTI3NTo2NSIgcGF0aD0idmFsdWUiIHhzaTp0eXBlPSJQcm9wZXJ0eSI+CiAgICAgICAgICAgIDxzb3VyY2UgbG9jYXRvcj0iMjc1OjU1LTI3NTo1OSIgbmFtZT0idmFsdWUiIHhzaTp0eXBlPSJPcGVyYW5kUmVmIi8+CiAgICAgICAgIDwvZXhwcmVzc2lvbj4KICAgICAgICAgPG9wZXJhbmQgbmFtZT0idmFsdWUiPgogICAgICAgICAgICA8b3BlcmFuZFR5cGVTcGVjaWZpZXIgbG9jYXRvcj0iMjc1OjMyLTI3NTo1MSIgbmFtZT0iZmhpcjpTdXBwbHlEZWxpdmVyeVN0YXR1cyIgeHNpOnR5cGU9Ik5hbWVkVHlwZVNwZWNpZmllciIvPgogICAgICAgICA8L29wZXJhbmQ+CiAgICAgIDwvZGVmPgogICAgICA8ZGVmIGxvY2F0b3I9IjI3NjoxLTI3Njo2NCIgbmFtZT0iVG9TdHJpbmciIGNvbnRleHQ9IlBhdGllbnQiIGFjY2Vzc0xldmVsPSJQdWJsaWMiIHhzaTp0eXBlPSJGdW5jdGlvbkRlZiI+CiAgICAgICAgIDxleHByZXNzaW9uIGxvY2F0b3I9IjI3Njo1NC0yNzY6NjQiIHBhdGg9InZhbHVlIiB4c2k6dHlwZT0iUHJvcGVydHkiPgogICAgICAgICAgICA8c291cmNlIGxvY2F0b3I9IjI3Njo1NC0yNzY6NTgiIG5hbWU9InZhbHVlIiB4c2k6dHlwZT0iT3BlcmFuZFJlZiIvPgogICAgICAgICA8L2V4cHJlc3Npb24+CiAgICAgICAgIDxvcGVyYW5kIG5hbWU9InZhbHVlIj4KICAgICAgICAgICAgPG9wZXJhbmRUeXBlU3BlY2lmaWVyIGxvY2F0b3I9IjI3NjozMi0yNzY6NTAiIG5hbWU9ImZoaXI6U3VwcGx5UmVxdWVzdFN0YXR1cyIgeHNpOnR5cGU9Ik5hbWVkVHlwZVNwZWNpZmllciIvPgogICAgICAgICA8L29wZXJhbmQ+CiAgICAgIDwvZGVmPgogICAgICA8ZGVmIGxvY2F0b3I9IjI3NzoxLTI3Nzo2OSIgbmFtZT0iVG9TdHJpbmciIGNvbnRleHQ9IlBhdGllbnQiIGFjY2Vzc0xldmVsPSJQdWJsaWMiIHhzaTp0eXBlPSJGdW5jdGlvbkRlZiI+CiAgICAgICAgIDxleHByZXNzaW9uIGxvY2F0b3I9IjI3Nzo1OS0yNzc6NjkiIHBhdGg9InZhbHVlIiB4c2k6dHlwZT0iUHJvcGVydHkiPgogICAgICAgICAgICA8c291cmNlIGxvY2F0b3I9IjI3Nzo1OS0yNzc6NjMiIG5hbWU9InZhbHVlIiB4c2k6dHlwZT0iT3BlcmFuZFJlZiIvPgogICAgICAgICA8L2V4cHJlc3Npb24+CiAgICAgICAgIDxvcGVyYW5kIG5hbWU9InZhbHVlIj4KICAgICAgICAgICAgPG9wZXJhbmRUeXBlU3BlY2lmaWVyIGxvY2F0b3I9IjI3NzozMi0yNzc6NTUiIG5hbWU9ImZoaXI6U3lzdGVtUmVzdGZ1bEludGVyYWN0aW9uIiB4c2k6dHlwZT0iTmFtZWRUeXBlU3BlY2lmaWVyIi8+CiAgICAgICAgIDwvb3BlcmFuZD4KICAgICAgPC9kZWY+CiAgICAgIDxkZWYgbG9jYXRvcj0iMjc4OjEtMjc4OjU1IiBuYW1lPSJUb1N0cmluZyIgY29udGV4dD0iUGF0aWVudCIgYWNjZXNzTGV2ZWw9IlB1YmxpYyIgeHNpOnR5cGU9IkZ1bmN0aW9uRGVmIj4KICAgICAgICAgPGV4cHJlc3Npb24gbG9jYXRvcj0iMjc4OjQ1LTI3ODo1NSIgcGF0aD0idmFsdWUiIHhzaTp0eXBlPSJQcm9wZXJ0eSI+CiAgICAgICAgICAgIDxzb3VyY2UgbG9jYXRvcj0iMjc4OjQ1LTI3ODo0OSIgbmFtZT0idmFsdWUiIHhzaTp0eXBlPSJPcGVyYW5kUmVmIi8+CiAgICAgICAgIDwvZXhwcmVzc2lvbj4KICAgICAgICAgPG9wZXJhbmQgbmFtZT0idmFsdWUiPgogICAgICAgICAgICA8b3BlcmFuZFR5cGVTcGVjaWZpZXIgbG9jYXRvcj0iMjc4OjMyLTI3ODo0MSIgbmFtZT0iZmhpcjpUYXNrSW50ZW50IiB4c2k6dHlwZT0iTmFtZWRUeXBlU3BlY2lmaWVyIi8+CiAgICAgICAgIDwvb3BlcmFuZD4KICAgICAgPC9kZWY+CiAgICAgIDxkZWYgbG9jYXRvcj0iMjc5OjEtMjc5OjU3IiBuYW1lPSJUb1N0cmluZyIgY29udGV4dD0iUGF0aWVudCIgYWNjZXNzTGV2ZWw9IlB1YmxpYyIgeHNpOnR5cGU9IkZ1bmN0aW9uRGVmIj4KICAgICAgICAgPGV4cHJlc3Npb24gbG9jYXRvcj0iMjc5OjQ3LTI3OTo1NyIgcGF0aD0idmFsdWUiIHhzaTp0eXBlPSJQcm9wZXJ0eSI+CiAgICAgICAgICAgIDxzb3VyY2UgbG9jYXRvcj0iMjc5OjQ3LTI3OTo1MSIgbmFtZT0idmFsdWUiIHhzaTp0eXBlPSJPcGVyYW5kUmVmIi8+CiAgICAgICAgIDwvZXhwcmVzc2lvbj4KICAgICAgICAgPG9wZXJhbmQgbmFtZT0idmFsdWUiPgogICAgICAgICAgICA8b3BlcmFuZFR5cGVTcGVjaWZpZXIgbG9jYXRvcj0iMjc5OjMyLTI3OTo0MyIgbmFtZT0iZmhpcjpUYXNrUHJpb3JpdHkiIHhzaTp0eXBlPSJOYW1lZFR5cGVTcGVjaWZpZXIiLz4KICAgICAgICAgPC9vcGVyYW5kPgogICAgICA8L2RlZj4KICAgICAgPGRlZiBsb2NhdG9yPSIyODA6MS0yODA6NTUiIG5hbWU9IlRvU3RyaW5nIiBjb250ZXh0PSJQYXRpZW50IiBhY2Nlc3NMZXZlbD0iUHVibGljIiB4c2k6dHlwZT0iRnVuY3Rpb25EZWYiPgogICAgICAgICA8ZXhwcmVzc2lvbiBsb2NhdG9yPSIyODA6NDUtMjgwOjU1IiBwYXRoPSJ2YWx1ZSIgeHNpOnR5cGU9IlByb3BlcnR5Ij4KICAgICAgICAgICAgPHNvdXJjZSBsb2NhdG9yPSIyODA6NDUtMjgwOjQ5IiBuYW1lPSJ2YWx1ZSIgeHNpOnR5cGU9Ik9wZXJhbmRSZWYiLz4KICAgICAgICAgPC9leHByZXNzaW9uPgogICAgICAgICA8b3BlcmFuZCBuYW1lPSJ2YWx1ZSI+CiAgICAgICAgICAgIDxvcGVyYW5kVHlwZVNwZWNpZmllciBsb2NhdG9yPSIyODA6MzItMjgwOjQxIiBuYW1lPSJmaGlyOlRhc2tTdGF0dXMiIHhzaTp0eXBlPSJOYW1lZFR5cGVTcGVjaWZpZXIiLz4KICAgICAgICAgPC9vcGVyYW5kPgogICAgICA8L2RlZj4KICAgICAgPGRlZiBsb2NhdG9yPSIyODE6MS0yODE6NjciIG5hbWU9IlRvU3RyaW5nIiBjb250ZXh0PSJQYXRpZW50IiBhY2Nlc3NMZXZlbD0iUHVibGljIiB4c2k6dHlwZT0iRnVuY3Rpb25EZWYiPgogICAgICAgICA8ZXhwcmVzc2lvbiBsb2NhdG9yPSIyODE6NTctMjgxOjY3IiBwYXRoPSJ2YWx1ZSIgeHNpOnR5cGU9IlByb3BlcnR5Ij4KICAgICAgICAgICAgPHNvdXJjZSBsb2NhdG9yPSIyODE6NTctMjgxOjYxIiBuYW1lPSJ2YWx1ZSIgeHNpOnR5cGU9Ik9wZXJhbmRSZWYiLz4KICAgICAgICAgPC9leHByZXNzaW9uPgogICAgICAgICA8b3BlcmFuZCBuYW1lPSJ2YWx1ZSI+CiAgICAgICAgICAgIDxvcGVyYW5kVHlwZVNwZWNpZmllciBsb2NhdG9yPSIyODE6MzItMjgxOjUzIiBuYW1lPSJmaGlyOlRlc3RSZXBvcnRBY3Rpb25SZXN1bHQiIHhzaTp0eXBlPSJOYW1lZFR5cGVTcGVjaWZpZXIiLz4KICAgICAgICAgPC9vcGVyYW5kPgogICAgICA8L2RlZj4KICAgICAgPGRlZiBsb2NhdG9yPSIyODI6MS0yODI6NzAiIG5hbWU9IlRvU3RyaW5nIiBjb250ZXh0PSJQYXRpZW50IiBhY2Nlc3NMZXZlbD0iUHVibGljIiB4c2k6dHlwZT0iRnVuY3Rpb25EZWYiPgogICAgICAgICA8ZXhwcmVzc2lvbiBsb2NhdG9yPSIyODI6NjAtMjgyOjcwIiBwYXRoPSJ2YWx1ZSIgeHNpOnR5cGU9IlByb3BlcnR5Ij4KICAgICAgICAgICAgPHNvdXJjZSBsb2NhdG9yPSIyODI6NjAtMjgyOjY0IiBuYW1lPSJ2YWx1ZSIgeHNpOnR5cGU9Ik9wZXJhbmRSZWYiLz4KICAgICAgICAgPC9leHByZXNzaW9uPgogICAgICAgICA8b3BlcmFuZCBuYW1lPSJ2YWx1ZSI+CiAgICAgICAgICAgIDxvcGVyYW5kVHlwZVNwZWNpZmllciBsb2NhdG9yPSIyODI6MzItMjgyOjU2IiBuYW1lPSJmaGlyOlRlc3RSZXBvcnRQYXJ0aWNpcGFudFR5cGUiIHhzaTp0eXBlPSJOYW1lZFR5cGVTcGVjaWZpZXIiLz4KICAgICAgICAgPC9vcGVyYW5kPgogICAgICA8L2RlZj4KICAgICAgPGRlZiBsb2NhdG9yPSIyODM6MS0yODM6NjEiIG5hbWU9IlRvU3RyaW5nIiBjb250ZXh0PSJQYXRpZW50IiBhY2Nlc3NMZXZlbD0iUHVibGljIiB4c2k6dHlwZT0iRnVuY3Rpb25EZWYiPgogICAgICAgICA8ZXhwcmVzc2lvbiBsb2NhdG9yPSIyODM6NTEtMjgzOjYxIiBwYXRoPSJ2YWx1ZSIgeHNpOnR5cGU9IlByb3BlcnR5Ij4KICAgICAgICAgICAgPHNvdXJjZSBsb2NhdG9yPSIyODM6NTEtMjgzOjU1IiBuYW1lPSJ2YWx1ZSIgeHNpOnR5cGU9Ik9wZXJhbmRSZWYiLz4KICAgICAgICAgPC9leHByZXNzaW9uPgogICAgICAgICA8b3BlcmFuZCBuYW1lPSJ2YWx1ZSI+CiAgICAgICAgICAgIDxvcGVyYW5kVHlwZVNwZWNpZmllciBsb2NhdG9yPSIyODM6MzItMjgzOjQ3IiBuYW1lPSJmaGlyOlRlc3RSZXBvcnRSZXN1bHQiIHhzaTp0eXBlPSJOYW1lZFR5cGVTcGVjaWZpZXIiLz4KICAgICAgICAgPC9vcGVyYW5kPgogICAgICA8L2RlZj4KICAgICAgPGRlZiBsb2NhdG9yPSIyODQ6MS0yODQ6NjEiIG5hbWU9IlRvU3RyaW5nIiBjb250ZXh0PSJQYXRpZW50IiBhY2Nlc3NMZXZlbD0iUHVibGljIiB4c2k6dHlwZT0iRnVuY3Rpb25EZWYiPgogICAgICAgICA8ZXhwcmVzc2lvbiBsb2NhdG9yPSIyODQ6NTEtMjg0OjYxIiBwYXRoPSJ2YWx1ZSIgeHNpOnR5cGU9IlByb3BlcnR5Ij4KICAgICAgICAgICAgPHNvdXJjZSBsb2NhdG9yPSIyODQ6NTEtMjg0OjU1IiBuYW1lPSJ2YWx1ZSIgeHNpOnR5cGU9Ik9wZXJhbmRSZWYiLz4KICAgICAgICAgPC9leHByZXNzaW9uPgogICAgICAgICA8b3BlcmFuZCBuYW1lPSJ2YWx1ZSI+CiAgICAgICAgICAgIDxvcGVyYW5kVHlwZVNwZWNpZmllciBsb2NhdG9yPSIyODQ6MzItMjg0OjQ3IiBuYW1lPSJmaGlyOlRlc3RSZXBvcnRTdGF0dXMiIHhzaTp0eXBlPSJOYW1lZFR5cGVTcGVjaWZpZXIiLz4KICAgICAgICAgPC9vcGVyYW5kPgogICAgICA8L2RlZj4KICAgICAgPGRlZiBsb2NhdG9yPSIyODU6MS0yODU6NzIiIG5hbWU9IlRvU3RyaW5nIiBjb250ZXh0PSJQYXRpZW50IiBhY2Nlc3NMZXZlbD0iUHVibGljIiB4c2k6dHlwZT0iRnVuY3Rpb25EZWYiPgogICAgICAgICA8ZXhwcmVzc2lvbiBsb2NhdG9yPSIyODU6NjItMjg1OjcyIiBwYXRoPSJ2YWx1ZSIgeHNpOnR5cGU9IlByb3BlcnR5Ij4KICAgICAgICAgICAgPHNvdXJjZSBsb2NhdG9yPSIyODU6NjItMjg1OjY2IiBuYW1lPSJ2YWx1ZSIgeHNpOnR5cGU9Ik9wZXJhbmRSZWYiLz4KICAgICAgICAgPC9leHByZXNzaW9uPgogICAgICAgICA8b3BlcmFuZCBuYW1lPSJ2YWx1ZSI+CiAgICAgICAgICAgIDxvcGVyYW5kVHlwZVNwZWNpZmllciBsb2NhdG9yPSIyODU6MzItMjg1OjU4IiBuYW1lPSJmaGlyOlRlc3RTY3JpcHRSZXF1ZXN0TWV0aG9kQ29kZSIgeHNpOnR5cGU9Ik5hbWVkVHlwZVNwZWNpZmllciIvPgogICAgICAgICA8L29wZXJhbmQ+CiAgICAgIDwvZGVmPgogICAgICA8ZGVmIGxvY2F0b3I9IjI4NjoxLTI4Njo1NiIgbmFtZT0iVG9TdHJpbmciIGNvbnRleHQ9IlBhdGllbnQiIGFjY2Vzc0xldmVsPSJQdWJsaWMiIHhzaTp0eXBlPSJGdW5jdGlvbkRlZiI+CiAgICAgICAgIDxleHByZXNzaW9uIGxvY2F0b3I9IjI4Njo0Ni0yODY6NTYiIHBhdGg9InZhbHVlIiB4c2k6dHlwZT0iUHJvcGVydHkiPgogICAgICAgICAgICA8c291cmNlIGxvY2F0b3I9IjI4Njo0Ni0yODY6NTAiIG5hbWU9InZhbHVlIiB4c2k6dHlwZT0iT3BlcmFuZFJlZiIvPgogICAgICAgICA8L2V4cHJlc3Npb24+CiAgICAgICAgIDxvcGVyYW5kIG5hbWU9InZhbHVlIj4KICAgICAgICAgICAgPG9wZXJhbmRUeXBlU3BlY2lmaWVyIGxvY2F0b3I9IjI4NjozMi0yODY6NDIiIG5hbWU9ImZoaXI6VHJpZ2dlclR5cGUiIHhzaTp0eXBlPSJOYW1lZFR5cGVTcGVjaWZpZXIiLz4KICAgICAgICAgPC9vcGVyYW5kPgogICAgICA8L2RlZj4KICAgICAgPGRlZiBsb2NhdG9yPSIyODc6MS0yODc6NjMiIG5hbWU9IlRvU3RyaW5nIiBjb250ZXh0PSJQYXRpZW50IiBhY2Nlc3NMZXZlbD0iUHVibGljIiB4c2k6dHlwZT0iRnVuY3Rpb25EZWYiPgogICAgICAgICA8ZXhwcmVzc2lvbiBsb2NhdG9yPSIyODc6NTMtMjg3OjYzIiBwYXRoPSJ2YWx1ZSIgeHNpOnR5cGU9IlByb3BlcnR5Ij4KICAgICAgICAgICAgPHNvdXJjZSBsb2NhdG9yPSIyODc6NTMtMjg3OjU3IiBuYW1lPSJ2YWx1ZSIgeHNpOnR5cGU9Ik9wZXJhbmRSZWYiLz4KICAgICAgICAgPC9leHByZXNzaW9uPgogICAgICAgICA8b3BlcmFuZCBuYW1lPSJ2YWx1ZSI+CiAgICAgICAgICAgIDxvcGVyYW5kVHlwZVNwZWNpZmllciBsb2NhdG9yPSIyODc6MzItMjg3OjQ5IiBuYW1lPSJmaGlyOlR5cGVEZXJpdmF0aW9uUnVsZSIgeHNpOnR5cGU9Ik5hbWVkVHlwZVNwZWNpZmllciIvPgogICAgICAgICA8L29wZXJhbmQ+CiAgICAgIDwvZGVmPgogICAgICA8ZGVmIGxvY2F0b3I9IjI4ODoxLTI4ODo2NyIgbmFtZT0iVG9TdHJpbmciIGNvbnRleHQ9IlBhdGllbnQiIGFjY2Vzc0xldmVsPSJQdWJsaWMiIHhzaTp0eXBlPSJGdW5jdGlvbkRlZiI+CiAgICAgICAgIDxleHByZXNzaW9uIGxvY2F0b3I9IjI4ODo1Ny0yODg6NjciIHBhdGg9InZhbHVlIiB4c2k6dHlwZT0iUHJvcGVydHkiPgogICAgICAgICAgICA8c291cmNlIGxvY2F0b3I9IjI4ODo1Ny0yODg6NjEiIG5hbWU9InZhbHVlIiB4c2k6dHlwZT0iT3BlcmFuZFJlZiIvPgogICAgICAgICA8L2V4cHJlc3Npb24+CiAgICAgICAgIDxvcGVyYW5kIG5hbWU9InZhbHVlIj4KICAgICAgICAgICAgPG9wZXJhbmRUeXBlU3BlY2lmaWVyIGxvY2F0b3I9IjI4ODozMi0yODg6NTMiIG5hbWU9ImZoaXI6VHlwZVJlc3RmdWxJbnRlcmFjdGlvbiIgeHNpOnR5cGU9Ik5hbWVkVHlwZVNwZWNpZmllciIvPgogICAgICAgICA8L29wZXJhbmQ+CiAgICAgIDwvZGVmPgogICAgICA8ZGVmIGxvY2F0b3I9IjI4OToxLTI4OTo1NyIgbmFtZT0iVG9TdHJpbmciIGNvbnRleHQ9IlBhdGllbnQiIGFjY2Vzc0xldmVsPSJQdWJsaWMiIHhzaTp0eXBlPSJGdW5jdGlvbkRlZiI+CiAgICAgICAgIDxleHByZXNzaW9uIGxvY2F0b3I9IjI4OTo0Ny0yODk6NTciIHBhdGg9InZhbHVlIiB4c2k6dHlwZT0iUHJvcGVydHkiPgogICAgICAgICAgICA8c291cmNlIGxvY2F0b3I9IjI4OTo0Ny0yODk6NTEiIG5hbWU9InZhbHVlIiB4c2k6dHlwZT0iT3BlcmFuZFJlZiIvPgogICAgICAgICA8L2V4cHJlc3Npb24+CiAgICAgICAgIDxvcGVyYW5kIG5hbWU9InZhbHVlIj4KICAgICAgICAgICAgPG9wZXJhbmRUeXBlU3BlY2lmaWVyIGxvY2F0b3I9IjI4OTozMi0yODk6NDMiIG5hbWU9ImZoaXI6VURJRW50cnlUeXBlIiB4c2k6dHlwZT0iTmFtZWRUeXBlU3BlY2lmaWVyIi8+CiAgICAgICAgIDwvb3BlcmFuZD4KICAgICAgPC9kZWY+CiAgICAgIDxkZWYgbG9jYXRvcj0iMjkwOjEtMjkwOjU2IiBuYW1lPSJUb1N0cmluZyIgY29udGV4dD0iUGF0aWVudCIgYWNjZXNzTGV2ZWw9IlB1YmxpYyIgeHNpOnR5cGU9IkZ1bmN0aW9uRGVmIj4KICAgICAgICAgPGV4cHJlc3Npb24gbG9jYXRvcj0iMjkwOjQ2LTI5MDo1NiIgcGF0aD0idmFsdWUiIHhzaTp0eXBlPSJQcm9wZXJ0eSI+CiAgICAgICAgICAgIDxzb3VyY2UgbG9jYXRvcj0iMjkwOjQ2LTI5MDo1MCIgbmFtZT0idmFsdWUiIHhzaTp0eXBlPSJPcGVyYW5kUmVmIi8+CiAgICAgICAgIDwvZXhwcmVzc2lvbj4KICAgICAgICAgPG9wZXJhbmQgbmFtZT0idmFsdWUiPgogICAgICAgICAgICA8b3BlcmFuZFR5cGVTcGVjaWZpZXIgbG9jYXRvcj0iMjkwOjMyLTI5MDo0MiIgbmFtZT0iZmhpcjpVbml0c09mVGltZSIgeHNpOnR5cGU9Ik5hbWVkVHlwZVNwZWNpZmllciIvPgogICAgICAgICA8L29wZXJhbmQ+CiAgICAgIDwvZGVmPgogICAgICA8ZGVmIGxvY2F0b3I9IjI5MToxLTI5MTo0OCIgbmFtZT0iVG9TdHJpbmciIGNvbnRleHQ9IlBhdGllbnQiIGFjY2Vzc0xldmVsPSJQdWJsaWMiIHhzaTp0eXBlPSJGdW5jdGlvbkRlZiI+CiAgICAgICAgIDxleHByZXNzaW9uIGxvY2F0b3I9IjI5MTozOC0yOTE6NDgiIHBhdGg9InZhbHVlIiB4c2k6dHlwZT0iUHJvcGVydHkiPgogICAgICAgICAgICA8c291cmNlIGxvY2F0b3I9IjI5MTozOC0yOTE6NDIiIG5hbWU9InZhbHVlIiB4c2k6dHlwZT0iT3BlcmFuZFJlZiIvPgogICAgICAgICA8L2V4cHJlc3Npb24+CiAgICAgICAgIDxvcGVyYW5kIG5hbWU9InZhbHVlIj4KICAgICAgICAgICAgPG9wZXJhbmRUeXBlU3BlY2lmaWVyIGxvY2F0b3I9IjI5MTozMi0yOTE6MzQiIG5hbWU9ImZoaXI6VXNlIiB4c2k6dHlwZT0iTmFtZWRUeXBlU3BlY2lmaWVyIi8+CiAgICAgICAgIDwvb3BlcmFuZD4KICAgICAgPC9kZWY+CiAgICAgIDxkZWYgbG9jYXRvcj0iMjkyOjEtMjkyOjU3IiBuYW1lPSJUb1N0cmluZyIgY29udGV4dD0iUGF0aWVudCIgYWNjZXNzTGV2ZWw9IlB1YmxpYyIgeHNpOnR5cGU9IkZ1bmN0aW9uRGVmIj4KICAgICAgICAgPGV4cHJlc3Npb24gbG9jYXRvcj0iMjkyOjQ3LTI5Mjo1NyIgcGF0aD0idmFsdWUiIHhzaTp0eXBlPSJQcm9wZXJ0eSI+CiAgICAgICAgICAgIDxzb3VyY2UgbG9jYXRvcj0iMjkyOjQ3LTI5Mjo1MSIgbmFtZT0idmFsdWUiIHhzaTp0eXBlPSJPcGVyYW5kUmVmIi8+CiAgICAgICAgIDwvZXhwcmVzc2lvbj4KICAgICAgICAgPG9wZXJhbmQgbmFtZT0idmFsdWUiPgogICAgICAgICAgICA8b3BlcmFuZFR5cGVTcGVjaWZpZXIgbG9jYXRvcj0iMjkyOjMyLTI5Mjo0MyIgbmFtZT0iZmhpcjpWYXJpYWJsZVR5cGUiIHhzaTp0eXBlPSJOYW1lZFR5cGVTcGVjaWZpZXIiLz4KICAgICAgICAgPC9vcGVyYW5kPgogICAgICA8L2RlZj4KICAgICAgPGRlZiBsb2NhdG9yPSIyOTM6MS0yOTM6NTUiIG5hbWU9IlRvU3RyaW5nIiBjb250ZXh0PSJQYXRpZW50IiBhY2Nlc3NMZXZlbD0iUHVibGljIiB4c2k6dHlwZT0iRnVuY3Rpb25EZWYiPgogICAgICAgICA8ZXhwcmVzc2lvbiBsb2NhdG9yPSIyOTM6NDUtMjkzOjU1IiBwYXRoPSJ2YWx1ZSIgeHNpOnR5cGU9IlByb3BlcnR5Ij4KICAgICAgICAgICAgPHNvdXJjZSBsb2NhdG9yPSIyOTM6NDUtMjkzOjQ5IiBuYW1lPSJ2YWx1ZSIgeHNpOnR5cGU9Ik9wZXJhbmRSZWYiLz4KICAgICAgICAgPC9leHByZXNzaW9uPgogICAgICAgICA8b3BlcmFuZCBuYW1lPSJ2YWx1ZSI+CiAgICAgICAgICAgIDxvcGVyYW5kVHlwZVNwZWNpZmllciBsb2NhdG9yPSIyOTM6MzItMjkzOjQxIiBuYW1lPSJmaGlyOlZpc2lvbkJhc2UiIHhzaTp0eXBlPSJOYW1lZFR5cGVTcGVjaWZpZXIiLz4KICAgICAgICAgPC9vcGVyYW5kPgogICAgICA8L2RlZj4KICAgICAgPGRlZiBsb2NhdG9yPSIyOTQ6MS0yOTQ6NTUiIG5hbWU9IlRvU3RyaW5nIiBjb250ZXh0PSJQYXRpZW50IiBhY2Nlc3NMZXZlbD0iUHVibGljIiB4c2k6dHlwZT0iRnVuY3Rpb25EZWYiPgogICAgICAgICA8ZXhwcmVzc2lvbiBsb2NhdG9yPSIyOTQ6NDUtMjk0OjU1IiBwYXRoPSJ2YWx1ZSIgeHNpOnR5cGU9IlByb3BlcnR5Ij4KICAgICAgICAgICAgPHNvdXJjZSBsb2NhdG9yPSIyOTQ6NDUtMjk0OjQ5IiBuYW1lPSJ2YWx1ZSIgeHNpOnR5cGU9Ik9wZXJhbmRSZWYiLz4KICAgICAgICAgPC9leHByZXNzaW9uPgogICAgICAgICA8b3BlcmFuZCBuYW1lPSJ2YWx1ZSI+CiAgICAgICAgICAgIDxvcGVyYW5kVHlwZVNwZWNpZmllciBsb2NhdG9yPSIyOTQ6MzItMjk0OjQxIiBuYW1lPSJmaGlyOlZpc2lvbkV5ZXMiIHhzaTp0eXBlPSJOYW1lZFR5cGVTcGVjaWZpZXIiLz4KICAgICAgICAgPC9vcGVyYW5kPgogICAgICA8L2RlZj4KICAgICAgPGRlZiBsb2NhdG9yPSIyOTU6MS0yOTU6NTciIG5hbWU9IlRvU3RyaW5nIiBjb250ZXh0PSJQYXRpZW50IiBhY2Nlc3NMZXZlbD0iUHVibGljIiB4c2k6dHlwZT0iRnVuY3Rpb25EZWYiPgogICAgICAgICA8ZXhwcmVzc2lvbiBsb2NhdG9yPSIyOTU6NDctMjk1OjU3IiBwYXRoPSJ2YWx1ZSIgeHNpOnR5cGU9IlByb3BlcnR5Ij4KICAgICAgICAgICAgPHNvdXJjZSBsb2NhdG9yPSIyOTU6NDctMjk1OjUxIiBuYW1lPSJ2YWx1ZSIgeHNpOnR5cGU9Ik9wZXJhbmRSZWYiLz4KICAgICAgICAgPC9leHByZXNzaW9uPgogICAgICAgICA8b3BlcmFuZCBuYW1lPSJ2YWx1ZSI+CiAgICAgICAgICAgIDxvcGVyYW5kVHlwZVNwZWNpZmllciBsb2NhdG9yPSIyOTU6MzItMjk1OjQzIiBuYW1lPSJmaGlyOlZpc2lvblN0YXR1cyIgeHNpOnR5cGU9Ik5hbWVkVHlwZVNwZWNpZmllciIvPgogICAgICAgICA8L29wZXJhbmQ+CiAgICAgIDwvZGVmPgogICAgICA8ZGVmIGxvY2F0b3I9IjI5NjoxLTI5Njo1OSIgbmFtZT0iVG9TdHJpbmciIGNvbnRleHQ9IlBhdGllbnQiIGFjY2Vzc0xldmVsPSJQdWJsaWMiIHhzaTp0eXBlPSJGdW5jdGlvbkRlZiI+CiAgICAgICAgIDxleHByZXNzaW9uIGxvY2F0b3I9IjI5Njo0OS0yOTY6NTkiIHBhdGg9InZhbHVlIiB4c2k6dHlwZT0iUHJvcGVydHkiPgogICAgICAgICAgICA8c291cmNlIGxvY2F0b3I9IjI5Njo0OS0yOTY6NTMiIG5hbWU9InZhbHVlIiB4c2k6dHlwZT0iT3BlcmFuZFJlZiIvPgogICAgICAgICA8L2V4cHJlc3Npb24+CiAgICAgICAgIDxvcGVyYW5kIG5hbWU9InZhbHVlIj4KICAgICAgICAgICAgPG9wZXJhbmRUeXBlU3BlY2lmaWVyIGxvY2F0b3I9IjI5NjozMi0yOTY6NDUiIG5hbWU9ImZoaXI6WFBhdGhVc2FnZVR5cGUiIHhzaTp0eXBlPSJOYW1lZFR5cGVTcGVjaWZpZXIiLz4KICAgICAgICAgPC9vcGVyYW5kPgogICAgICA8L2RlZj4KICAgICAgPGRlZiBsb2NhdG9yPSIyOTc6MS0yOTc6NTciIG5hbWU9IlRvU3RyaW5nIiBjb250ZXh0PSJQYXRpZW50IiBhY2Nlc3NMZXZlbD0iUHVibGljIiB4c2k6dHlwZT0iRnVuY3Rpb25EZWYiPgogICAgICAgICA8ZXhwcmVzc2lvbiBsb2NhdG9yPSIyOTc6NDctMjk3OjU3IiBwYXRoPSJ2YWx1ZSIgeHNpOnR5cGU9IlByb3BlcnR5Ij4KICAgICAgICAgICAgPHNvdXJjZSBsb2NhdG9yPSIyOTc6NDctMjk3OjUxIiBuYW1lPSJ2YWx1ZSIgeHNpOnR5cGU9Ik9wZXJhbmRSZWYiLz4KICAgICAgICAgPC9leHByZXNzaW9uPgogICAgICAgICA8b3BlcmFuZCBuYW1lPSJ2YWx1ZSI+CiAgICAgICAgICAgIDxvcGVyYW5kVHlwZVNwZWNpZmllciBsb2NhdG9yPSIyOTc6MzItMjk3OjQzIiBuYW1lPSJmaGlyOmJhc2U2NEJpbmFyeSIgeHNpOnR5cGU9Ik5hbWVkVHlwZVNwZWNpZmllciIvPgogICAgICAgICA8L29wZXJhbmQ+CiAgICAgIDwvZGVmPgogICAgICA8ZGVmIGxvY2F0b3I9IjI5ODoxLTI5ODo0NyIgbmFtZT0iVG9TdHJpbmciIGNvbnRleHQ9IlBhdGllbnQiIGFjY2Vzc0xldmVsPSJQdWJsaWMiIHhzaTp0eXBlPSJGdW5jdGlvbkRlZiI+CiAgICAgICAgIDxleHByZXNzaW9uIGxvY2F0b3I9IjI5ODozNy0yOTg6NDciIHBhdGg9InZhbHVlIiB4c2k6dHlwZT0iUHJvcGVydHkiPgogICAgICAgICAgICA8c291cmNlIGxvY2F0b3I9IjI5ODozNy0yOTg6NDEiIG5hbWU9InZhbHVlIiB4c2k6dHlwZT0iT3BlcmFuZFJlZiIvPgogICAgICAgICA8L2V4cHJlc3Npb24+CiAgICAgICAgIDxvcGVyYW5kIG5hbWU9InZhbHVlIj4KICAgICAgICAgICAgPG9wZXJhbmRUeXBlU3BlY2lmaWVyIGxvY2F0b3I9IjI5ODozMi0yOTg6MzMiIG5hbWU9ImZoaXI6aWQiIHhzaTp0eXBlPSJOYW1lZFR5cGVTcGVjaWZpZXIiLz4KICAgICAgICAgPC9vcGVyYW5kPgogICAgICA8L2RlZj4KICAgICAgPGRlZiBsb2NhdG9yPSIyOTk6MS0yOTk6NTMiIG5hbWU9IlRvQm9vbGVhbiIgY29udGV4dD0iUGF0aWVudCIgYWNjZXNzTGV2ZWw9IlB1YmxpYyIgeHNpOnR5cGU9IkZ1bmN0aW9uRGVmIj4KICAgICAgICAgPGV4cHJlc3Npb24gbG9jYXRvcj0iMjk5OjQzLTI5OTo1MyIgcGF0aD0idmFsdWUiIHhzaTp0eXBlPSJQcm9wZXJ0eSI+CiAgICAgICAgICAgIDxzb3VyY2UgbG9jYXRvcj0iMjk5OjQzLTI5OTo0NyIgbmFtZT0idmFsdWUiIHhzaTp0eXBlPSJPcGVyYW5kUmVmIi8+CiAgICAgICAgIDwvZXhwcmVzc2lvbj4KICAgICAgICAgPG9wZXJhbmQgbmFtZT0idmFsdWUiPgogICAgICAgICAgICA8b3BlcmFuZFR5cGVTcGVjaWZpZXIgbG9jYXRvcj0iMjk5OjMzLTI5OTozOSIgbmFtZT0iZmhpcjpib29sZWFuIiB4c2k6dHlwZT0iTmFtZWRUeXBlU3BlY2lmaWVyIi8+CiAgICAgICAgIDwvb3BlcmFuZD4KICAgICAgPC9kZWY+CiAgICAgIDxkZWYgbG9jYXRvcj0iMzAwOjEtMzAwOjQ3IiBuYW1lPSJUb0RhdGUiIGNvbnRleHQ9IlBhdGllbnQiIGFjY2Vzc0xldmVsPSJQdWJsaWMiIHhzaTp0eXBlPSJGdW5jdGlvbkRlZiI+CiAgICAgICAgIDxleHByZXNzaW9uIGxvY2F0b3I9IjMwMDozNy0zMDA6NDciIHBhdGg9InZhbHVlIiB4c2k6dHlwZT0iUHJvcGVydHkiPgogICAgICAgICAgICA8c291cmNlIGxvY2F0b3I9IjMwMDozNy0zMDA6NDEiIG5hbWU9InZhbHVlIiB4c2k6dHlwZT0iT3BlcmFuZFJlZiIvPgogICAgICAgICA8L2V4cHJlc3Npb24+CiAgICAgICAgIDxvcGVyYW5kIG5hbWU9InZhbHVlIj4KICAgICAgICAgICAgPG9wZXJhbmRUeXBlU3BlY2lmaWVyIGxvY2F0b3I9IjMwMDozMC0zMDA6MzMiIG5hbWU9ImZoaXI6ZGF0ZSIgeHNpOnR5cGU9Ik5hbWVkVHlwZVNwZWNpZmllciIvPgogICAgICAgICA8L29wZXJhbmQ+CiAgICAgIDwvZGVmPgogICAgICA8ZGVmIGxvY2F0b3I9IjMwMToxLTMwMTo1NSIgbmFtZT0iVG9EYXRlVGltZSIgY29udGV4dD0iUGF0aWVudCIgYWNjZXNzTGV2ZWw9IlB1YmxpYyIgeHNpOnR5cGU9IkZ1bmN0aW9uRGVmIj4KICAgICAgICAgPGV4cHJlc3Npb24gbG9jYXRvcj0iMzAxOjQ1LTMwMTo1NSIgcGF0aD0idmFsdWUiIHhzaTp0eXBlPSJQcm9wZXJ0eSI+CiAgICAgICAgICAgIDxzb3VyY2UgbG9jYXRvcj0iMzAxOjQ1LTMwMTo0OSIgbmFtZT0idmFsdWUiIHhzaTp0eXBlPSJPcGVyYW5kUmVmIi8+CiAgICAgICAgIDwvZXhwcmVzc2lvbj4KICAgICAgICAgPG9wZXJhbmQgbmFtZT0idmFsdWUiPgogICAgICAgICAgICA8b3BlcmFuZFR5cGVTcGVjaWZpZXIgbG9jYXRvcj0iMzAxOjM0LTMwMTo0MSIgbmFtZT0iZmhpcjpkYXRlVGltZSIgeHNpOnR5cGU9Ik5hbWVkVHlwZVNwZWNpZmllciIvPgogICAgICAgICA8L29wZXJhbmQ+CiAgICAgIDwvZGVmPgogICAgICA8ZGVmIGxvY2F0b3I9IjMwMjoxLTMwMjo1MyIgbmFtZT0iVG9EZWNpbWFsIiBjb250ZXh0PSJQYXRpZW50IiBhY2Nlc3NMZXZlbD0iUHVibGljIiB4c2k6dHlwZT0iRnVuY3Rpb25EZWYiPgogICAgICAgICA8ZXhwcmVzc2lvbiBsb2NhdG9yPSIzMDI6NDMtMzAyOjUzIiBwYXRoPSJ2YWx1ZSIgeHNpOnR5cGU9IlByb3BlcnR5Ij4KICAgICAgICAgICAgPHNvdXJjZSBsb2NhdG9yPSIzMDI6NDMtMzAyOjQ3IiBuYW1lPSJ2YWx1ZSIgeHNpOnR5cGU9Ik9wZXJhbmRSZWYiLz4KICAgICAgICAgPC9leHByZXNzaW9uPgogICAgICAgICA8b3BlcmFuZCBuYW1lPSJ2YWx1ZSI+CiAgICAgICAgICAgIDxvcGVyYW5kVHlwZVNwZWNpZmllciBsb2NhdG9yPSIzMDI6MzMtMzAyOjM5IiBuYW1lPSJmaGlyOmRlY2ltYWwiIHhzaTp0eXBlPSJOYW1lZFR5cGVTcGVjaWZpZXIiLz4KICAgICAgICAgPC9vcGVyYW5kPgogICAgICA8L2RlZj4KICAgICAgPGRlZiBsb2NhdG9yPSIzMDM6MS0zMDM6NTQiIG5hbWU9IlRvRGF0ZVRpbWUiIGNvbnRleHQ9IlBhdGllbnQiIGFjY2Vzc0xldmVsPSJQdWJsaWMiIHhzaTp0eXBlPSJGdW5jdGlvbkRlZiI+CiAgICAgICAgIDxleHByZXNzaW9uIGxvY2F0b3I9IjMwMzo0NC0zMDM6NTQiIHBhdGg9InZhbHVlIiB4c2k6dHlwZT0iUHJvcGVydHkiPgogICAgICAgICAgICA8c291cmNlIGxvY2F0b3I9IjMwMzo0NC0zMDM6NDgiIG5hbWU9InZhbHVlIiB4c2k6dHlwZT0iT3BlcmFuZFJlZiIvPgogICAgICAgICA8L2V4cHJlc3Npb24+CiAgICAgICAgIDxvcGVyYW5kIG5hbWU9InZhbHVlIj4KICAgICAgICAgICAgPG9wZXJhbmRUeXBlU3BlY2lmaWVyIGxvY2F0b3I9IjMwMzozNC0zMDM6NDAiIG5hbWU9ImZoaXI6aW5zdGFudCIgeHNpOnR5cGU9Ik5hbWVkVHlwZVNwZWNpZmllciIvPgogICAgICAgICA8L29wZXJhbmQ+CiAgICAgIDwvZGVmPgogICAgICA8ZGVmIGxvY2F0b3I9IjMwNDoxLTMwNDo1MyIgbmFtZT0iVG9JbnRlZ2VyIiBjb250ZXh0PSJQYXRpZW50IiBhY2Nlc3NMZXZlbD0iUHVibGljIiB4c2k6dHlwZT0iRnVuY3Rpb25EZWYiPgogICAgICAgICA8ZXhwcmVzc2lvbiBsb2NhdG9yPSIzMDQ6NDMtMzA0OjUzIiBwYXRoPSJ2YWx1ZSIgeHNpOnR5cGU9IlByb3BlcnR5Ij4KICAgICAgICAgICAgPHNvdXJjZSBsb2NhdG9yPSIzMDQ6NDMtMzA0OjQ3IiBuYW1lPSJ2YWx1ZSIgeHNpOnR5cGU9Ik9wZXJhbmRSZWYiLz4KICAgICAgICAgPC9leHByZXNzaW9uPgogICAgICAgICA8b3BlcmFuZCBuYW1lPSJ2YWx1ZSI+CiAgICAgICAgICAgIDxvcGVyYW5kVHlwZVNwZWNpZmllciBsb2NhdG9yPSIzMDQ6MzMtMzA0OjM5IiBuYW1lPSJmaGlyOmludGVnZXIiIHhzaTp0eXBlPSJOYW1lZFR5cGVTcGVjaWZpZXIiLz4KICAgICAgICAgPC9vcGVyYW5kPgogICAgICA8L2RlZj4KICAgICAgPGRlZiBsb2NhdG9yPSIzMDU6MS0zMDU6NTEiIG5hbWU9IlRvU3RyaW5nIiBjb250ZXh0PSJQYXRpZW50IiBhY2Nlc3NMZXZlbD0iUHVibGljIiB4c2k6dHlwZT0iRnVuY3Rpb25EZWYiPgogICAgICAgICA8ZXhwcmVzc2lvbiBsb2NhdG9yPSIzMDU6NDEtMzA1OjUxIiBwYXRoPSJ2YWx1ZSIgeHNpOnR5cGU9IlByb3BlcnR5Ij4KICAgICAgICAgICAgPHNvdXJjZSBsb2NhdG9yPSIzMDU6NDEtMzA1OjQ1IiBuYW1lPSJ2YWx1ZSIgeHNpOnR5cGU9Ik9wZXJhbmRSZWYiLz4KICAgICAgICAgPC9leHByZXNzaW9uPgogICAgICAgICA8b3BlcmFuZCBuYW1lPSJ2YWx1ZSI+CiAgICAgICAgICAgIDxvcGVyYW5kVHlwZVNwZWNpZmllciBsb2NhdG9yPSIzMDU6MzItMzA1OjM3IiBuYW1lPSJmaGlyOnN0cmluZyIgeHNpOnR5cGU9Ik5hbWVkVHlwZVNwZWNpZmllciIvPgogICAgICAgICA8L29wZXJhbmQ+CiAgICAgIDwvZGVmPgogICAgICA8ZGVmIGxvY2F0b3I9IjMwNjoxLTMwNjo0NyIgbmFtZT0iVG9UaW1lIiBjb250ZXh0PSJQYXRpZW50IiBhY2Nlc3NMZXZlbD0iUHVibGljIiB4c2k6dHlwZT0iRnVuY3Rpb25EZWYiPgogICAgICAgICA8ZXhwcmVzc2lvbiBsb2NhdG9yPSIzMDY6MzctMzA2OjQ3IiBwYXRoPSJ2YWx1ZSIgeHNpOnR5cGU9IlByb3BlcnR5Ij4KICAgICAgICAgICAgPHNvdXJjZSBsb2NhdG9yPSIzMDY6MzctMzA2OjQxIiBuYW1lPSJ2YWx1ZSIgeHNpOnR5cGU9Ik9wZXJhbmRSZWYiLz4KICAgICAgICAgPC9leHByZXNzaW9uPgogICAgICAgICA8b3BlcmFuZCBuYW1lPSJ2YWx1ZSI+CiAgICAgICAgICAgIDxvcGVyYW5kVHlwZVNwZWNpZmllciBsb2NhdG9yPSIzMDY6MzAtMzA2OjMzIiBuYW1lPSJmaGlyOnRpbWUiIHhzaTp0eXBlPSJOYW1lZFR5cGVTcGVjaWZpZXIiLz4KICAgICAgICAgPC9vcGVyYW5kPgogICAgICA8L2RlZj4KICAgICAgPGRlZiBsb2NhdG9yPSIzMDc6MS0zMDc6NDgiIG5hbWU9IlRvU3RyaW5nIiBjb250ZXh0PSJQYXRpZW50IiBhY2Nlc3NMZXZlbD0iUHVibGljIiB4c2k6dHlwZT0iRnVuY3Rpb25EZWYiPgogICAgICAgICA8ZXhwcmVzc2lvbiBsb2NhdG9yPSIzMDc6MzgtMzA3OjQ4IiBwYXRoPSJ2YWx1ZSIgeHNpOnR5cGU9IlByb3BlcnR5Ij4KICAgICAgICAgICAgPHNvdXJjZSBsb2NhdG9yPSIzMDc6MzgtMzA3OjQyIiBuYW1lPSJ2YWx1ZSIgeHNpOnR5cGU9Ik9wZXJhbmRSZWYiLz4KICAgICAgICAgPC9leHByZXNzaW9uPgogICAgICAgICA8b3BlcmFuZCBuYW1lPSJ2YWx1ZSI+CiAgICAgICAgICAgIDxvcGVyYW5kVHlwZVNwZWNpZmllciBsb2NhdG9yPSIzMDc6MzItMzA3OjM0IiBuYW1lPSJmaGlyOnVyaSIgeHNpOnR5cGU9Ik5hbWVkVHlwZVNwZWNpZmllciIvPgogICAgICAgICA8L29wZXJhbmQ+CiAgICAgIDwvZGVmPgogICAgICA8ZGVmIGxvY2F0b3I9IjMwODoxLTMwODo1MCIgbmFtZT0iVG9TdHJpbmciIGNvbnRleHQ9IlBhdGllbnQiIGFjY2Vzc0xldmVsPSJQdWJsaWMiIHhzaTp0eXBlPSJGdW5jdGlvbkRlZiI+CiAgICAgICAgIDxleHByZXNzaW9uIGxvY2F0b3I9IjMwODo0MC0zMDg6NTAiIHBhdGg9InZhbHVlIiB4c2k6dHlwZT0iUHJvcGVydHkiPgogICAgICAgICAgICA8c291cmNlIGxvY2F0b3I9IjMwODo0MC0zMDg6NDQiIG5hbWU9InZhbHVlIiB4c2k6dHlwZT0iT3BlcmFuZFJlZiIvPgogICAgICAgICA8L2V4cHJlc3Npb24+CiAgICAgICAgIDxvcGVyYW5kIG5hbWU9InZhbHVlIj4KICAgICAgICAgICAgPG9wZXJhbmRUeXBlU3BlY2lmaWVyIGxvY2F0b3I9IjMwODozMi0zMDg6MzYiIG5hbWU9ImZoaXI6eGh0bWwiIHhzaTp0eXBlPSJOYW1lZFR5cGVTcGVjaWZpZXIiLz4KICAgICAgICAgPC9vcGVyYW5kPgogICAgICA8L2RlZj4KICAgPC9zdGF0ZW1lbnRzPgo8L2xpYnJhcnk+Cg==" + } + ] + }, + "request": { + "method": "PUT", + "url": "Library/DQMFHIRHelpers" + } + }, + { + "resource": { + "resourceType": "ValueSet", + "id": "2.16.840.1.113762.1.4.1", + "url": "http://cts.nlm.nih.gov/fhir/ValueSet/2.16.840.1.113762.1.4.1", + "identifier": [ + { + "system": "urn:ietf:rfc:3986", + "value": "2.16.840.1.113762.1.4.1" + } + ], + "version": "20150331", + "name": "ONCAdministrativeSex", + "title": "ONC Administrative Sex", + "status": "active", + "experimental": false, + "publisher": "NLM", + "description": "Codes representing possible values for ONC Administrative Sex.", + "expansion": { + "identifier": "20210506", + "timestamp": "2021-08-19T13:27:33-06:00", + "contains": [ + { + "system": "http://terminology.hl7.org/CodeSystem/v3-AdministrativeGender", + "version": "HL7V3.0_2020-11", + "code": "F", + "display": "Female" + }, + { + "system": "http://terminology.hl7.org/CodeSystem/v3-AdministrativeGender", + "version": "HL7V3.0_2020-11", + "code": "M", + "display": "Male" + } + ] + } + }, + "request": { + "method": "PUT", + "url": "ValueSet/2.16.840.1.113762.1.4.1" + } + }, + { + "resource": { + "resourceType": "Library", + "id": "FHIRCommon", + "extension": [ + { + "url": "http://hl7.org/fhir/us/cqfmeasures/StructureDefinition/cqfm-softwaresystem", + "valueReference": { + "reference": "Device/cqf-tooling" + } + } + ], + "url": "http://content.alphora.com/fhir/dqm/Library/FHIRCommon", + "version": "4.0.1", + "name": "FHIRCommon", + "title": "Library - FHIR Common", + "type": { + "coding": [ + { + "system": "http://terminology.hl7.org/CodeSystem/library-type", + "code": "logic-library" + } + ] + }, + "description": "A Shared library encapsulating valuable common terminologies and functions used in FHIR-based CQL artifacts.", + "jurisdiction": [ + { + "coding": [ + { + "system": "urn:iso:std:iso:3166", + "version": "4.0.1", + "code": "US", + "display": "United States of America" + } + ], + "text": "United States of America" + } + ], + "relatedArtifact": [ + { + "type": "depends-on", + "display": "FHIR model information", + "resource": "http://fhir.org/guides/cqf/common/Library/FHIR-ModelInfo|4.0.1" + }, + { + "type": "depends-on", + "display": "Library FHIRHelpers", + "resource": "http://content.alphora.com/fhir/dqm/Library/DQMFHIRHelpers" + }, + { + "type": "depends-on", + "display": "Code system LOINC", + "resource": "http://loinc.org" + }, + { + "type": "depends-on", + "display": "Code system SNOMEDCT", + "resource": "http://snomed.info/sct" + }, + { + "type": "depends-on", + "display": "Code system RoleCode", + "resource": "http://terminology.hl7.org/CodeSystem/v3-RoleCode" + }, + { + "type": "depends-on", + "display": "Code system Diagnosis Role", + "resource": "http://terminology.hl7.org/CodeSystem/diagnosis-role" + }, + { + "type": "depends-on", + "display": "Code system RequestIntent", + "resource": "http://terminology.hl7.org/CodeSystem/request-intent" + }, + { + "type": "depends-on", + "display": "Code system MedicationRequestCategory", + "resource": "http://terminology.hl7.org/CodeSystem/medicationrequest-category" + }, + { + "type": "depends-on", + "display": "Code system ConditionClinicalStatusCodes", + "resource": "http://terminology.hl7.org/CodeSystem/condition-clinical" + }, + { + "type": "depends-on", + "display": "Code system ConditionVerificationStatusCodes", + "resource": "http://terminology.hl7.org/CodeSystem/condition-ver-status" + }, + { + "type": "depends-on", + "display": "Code system AllergyIntoleranceClinicalStatusCodes", + "resource": "http://terminology.hl7.org/CodeSystem/allergyintolerance-clinical" + }, + { + "type": "depends-on", + "display": "Code system AllergyIntoleranceVerificationStatusCodes", + "resource": "http://terminology.hl7.org/CodeSystem/allergyintolerance-verification" + } + ], + "parameter": [ + { + "name": "Patient", + "use": "out", + "min": 0, + "max": "1", + "type": "Patient" + } + ], + "dataRequirement": [ + { + "type": "Patient", + "profile": [ + "http://hl7.org/fhir/StructureDefinition/Patient" + ] + } + ], + "content": [ + { + "contentType": "text/cql", + "data": "LyoKQGF1dGhvcjogQnJ5biBSaG9kZXMKQGRlc2NyaXB0aW9uOiBDb21tb24gdGVybWlub2xvZ2llcyBhbmQgZnVuY3Rpb25zIHVzZWQgaW4gRkhJUi1iYXNlZCBDUUwgYXJ0aWZhY3RzCiovCgpsaWJyYXJ5IEZISVJDb21tb24gdmVyc2lvbiAnNC4wLjEnCgp1c2luZyBGSElSIHZlcnNpb24gJzQuMC4xJwoKaW5jbHVkZSBEUU1GSElSSGVscGVycyBjYWxsZWQgRkhJUkhlbHBlcnMKCmNvZGVzeXN0ZW0gIkxPSU5DIjogJ2h0dHA6Ly9sb2luYy5vcmcnCmNvZGVzeXN0ZW0gIlNOT01FRENUIjogJ2h0dHA6Ly9zbm9tZWQuaW5mby9zY3QnCmNvZGVzeXN0ZW0gIlJvbGVDb2RlIjogJ2h0dHA6Ly90ZXJtaW5vbG9neS5obDcub3JnL0NvZGVTeXN0ZW0vdjMtUm9sZUNvZGUnCmNvZGVzeXN0ZW0gIkRpYWdub3NpcyBSb2xlIjogJ2h0dHA6Ly90ZXJtaW5vbG9neS5obDcub3JnL0NvZGVTeXN0ZW0vZGlhZ25vc2lzLXJvbGUnCmNvZGVzeXN0ZW0gIlJlcXVlc3RJbnRlbnQiOiAnaHR0cDovL3Rlcm1pbm9sb2d5LmhsNy5vcmcvQ29kZVN5c3RlbS9yZXF1ZXN0LWludGVudCcKY29kZXN5c3RlbSAiTWVkaWNhdGlvblJlcXVlc3RDYXRlZ29yeSI6ICdodHRwOi8vdGVybWlub2xvZ3kuaGw3Lm9yZy9Db2RlU3lzdGVtL21lZGljYXRpb25yZXF1ZXN0LWNhdGVnb3J5Jwpjb2Rlc3lzdGVtICJDb25kaXRpb25DbGluaWNhbFN0YXR1c0NvZGVzIjogJ2h0dHA6Ly90ZXJtaW5vbG9neS5obDcub3JnL0NvZGVTeXN0ZW0vY29uZGl0aW9uLWNsaW5pY2FsJwpjb2Rlc3lzdGVtICJDb25kaXRpb25WZXJpZmljYXRpb25TdGF0dXNDb2RlcyI6ICdodHRwOi8vdGVybWlub2xvZ3kuaGw3Lm9yZy9Db2RlU3lzdGVtL2NvbmRpdGlvbi12ZXItc3RhdHVzJwpjb2Rlc3lzdGVtICJBbGxlcmd5SW50b2xlcmFuY2VDbGluaWNhbFN0YXR1c0NvZGVzIjogJ2h0dHA6Ly90ZXJtaW5vbG9neS5obDcub3JnL0NvZGVTeXN0ZW0vYWxsZXJneWludG9sZXJhbmNlLWNsaW5pY2FsJwpjb2Rlc3lzdGVtICJBbGxlcmd5SW50b2xlcmFuY2VWZXJpZmljYXRpb25TdGF0dXNDb2RlcyI6ICdodHRwOi8vdGVybWlub2xvZ3kuaGw3Lm9yZy9Db2RlU3lzdGVtL2FsbGVyZ3lpbnRvbGVyYW5jZS12ZXJpZmljYXRpb24nCgpjb2RlICJCaXJ0aGRhdGUiOiAnMjExMTItOCcgZnJvbSAiTE9JTkMiIGRpc3BsYXkgJ0JpcnRoIGRhdGUnCmNvZGUgIkRlYWQiOiAnNDE5MDk5MDA5JyBmcm9tICJTTk9NRURDVCIgZGlzcGxheSAnRGVhZCcKY29kZSAiRVIiOiAnRVInIGZyb20gIlJvbGVDb2RlIiBkaXNwbGF5ICdFbWVyZ2VuY3kgcm9vbScKY29kZSAiSUNVIjogJ0lDVScgZnJvbSAiUm9sZUNvZGUiIGRpc3BsYXkgJ0ludGVuc2l2ZSBjYXJlIHVuaXQnCmNvZGUgIkJpbGxpbmciOiAnYmlsbGluZycgZnJvbSAiRGlhZ25vc2lzIFJvbGUiIGRpc3BsYXkgJ0JpbGxpbmcnCgovLyBDb25kaXRpb24gQ2xpbmljYWwgU3RhdHVzIENvZGVzIC0gQ29uc2lkZXIgdmFsdWUgc2V0cyBmb3IgdGhlc2UKY29kZSAiYWN0aXZlIjogJ2FjdGl2ZScgZnJvbSAiQ29uZGl0aW9uQ2xpbmljYWxTdGF0dXNDb2RlcyIKY29kZSAicmVjdXJyZW5jZSI6ICdyZWN1cnJlbmNlJyBmcm9tICJDb25kaXRpb25DbGluaWNhbFN0YXR1c0NvZGVzIgpjb2RlICJyZWxhcHNlIjogJ3JlbGFwc2UnIGZyb20gIkNvbmRpdGlvbkNsaW5pY2FsU3RhdHVzQ29kZXMiCmNvZGUgImluYWN0aXZlIjogJ2luYWN0aXZlJyBmcm9tICJDb25kaXRpb25DbGluaWNhbFN0YXR1c0NvZGVzIgpjb2RlICJyZW1pc3Npb24iOiAncmVtaXNzaW9uJyBmcm9tICJDb25kaXRpb25DbGluaWNhbFN0YXR1c0NvZGVzIgpjb2RlICJyZXNvbHZlZCI6ICdyZXNvbHZlZCcgZnJvbSAiQ29uZGl0aW9uQ2xpbmljYWxTdGF0dXNDb2RlcyIKCi8vIENvbmRpdGlvbiBWZXJpZmljYXRpb24gU3RhdHVzIENvZGVzIC0gQ29uc2lkZXIgdmFsdWUgc2V0cyBmb3IgdGhlc2UKY29kZSAidW5jb25maXJtZWQiOiAndW5jb25maXJtZWQnIGZyb20gQ29uZGl0aW9uVmVyaWZpY2F0aW9uU3RhdHVzQ29kZXMKY29kZSAicHJvdmlzaW9uYWwiOiAncHJvdmlzaW9uYWwnIGZyb20gQ29uZGl0aW9uVmVyaWZpY2F0aW9uU3RhdHVzQ29kZXMKY29kZSAiZGlmZmVyZW50aWFsIjogJ2RpZmZlcmVudGlhbCcgZnJvbSBDb25kaXRpb25WZXJpZmljYXRpb25TdGF0dXNDb2Rlcwpjb2RlICJjb25maXJtZWQiOiAnY29uZmlybWVkJyBmcm9tIENvbmRpdGlvblZlcmlmaWNhdGlvblN0YXR1c0NvZGVzCmNvZGUgInJlZnV0ZWQiOiAncmVmdXRlZCcgZnJvbSBDb25kaXRpb25WZXJpZmljYXRpb25TdGF0dXNDb2Rlcwpjb2RlICJlbnRlcmVkLWluLWVycm9yIjogJ2VudGVyZWQtaW4tZXJyb3InIGZyb20gQ29uZGl0aW9uVmVyaWZpY2F0aW9uU3RhdHVzQ29kZXMKCi8vIEFsbGVyZ3kvSW50b2xlcmFuY2UgQ2xpbmljYWwgU3RhdHVzIENvZGVzIC0gQ29uc2lkZXIgdmFsdWUgc2V0cyBmb3IgdGhlc2UKY29kZSAiYWxsZXJneS1hY3RpdmUiOiAnYWN0aXZlJyBmcm9tICJBbGxlcmd5SW50b2xlcmFuY2VDbGluaWNhbFN0YXR1c0NvZGVzIgpjb2RlICJhbGxlcmd5LWluYWN0aXZlIjogJ2luYWN0aXZlJyBmcm9tICJBbGxlcmd5SW50b2xlcmFuY2VDbGluaWNhbFN0YXR1c0NvZGVzIgpjb2RlICJhbGxlcmd5LXJlc29sdmVkIjogJ3Jlc29sdmVkJyBmcm9tICJBbGxlcmd5SW50b2xlcmFuY2VDbGluaWNhbFN0YXR1c0NvZGVzIgoKLy8gQWxsZXJneS9JbnRvbGVyYW5jZSBWZXJpZmljYXRpb24gU3RhdHVzIENvZGVzIC0gQ29uc2lkZXIgdmFsdWUgc2V0cyBmb3IgdGhlc2UKY29kZSAiYWxsZXJneS11bmNvbmZpcm1lZCI6ICd1bmNvbmZpcm1lZCcgZnJvbSBBbGxlcmd5SW50b2xlcmFuY2VWZXJpZmljYXRpb25TdGF0dXNDb2Rlcwpjb2RlICJhbGxlcmd5LWNvbmZpcm1lZCI6ICdjb25maXJtZWQnIGZyb20gQWxsZXJneUludG9sZXJhbmNlVmVyaWZpY2F0aW9uU3RhdHVzQ29kZXMKY29kZSAiYWxsZXJneS1yZWZ1dGVkIjogJ3JlZnV0ZWQnIGZyb20gQWxsZXJneUludG9sZXJhbmNlVmVyaWZpY2F0aW9uU3RhdHVzQ29kZXMKCi8vIE1lZGljYXRpb25SZXF1ZXN0IENhdGVnb3J5IENvZGVzCmNvZGUgIkNvbW11bml0eSI6ICdjb21tdW5pdHknIGZyb20gIk1lZGljYXRpb25SZXF1ZXN0Q2F0ZWdvcnkiIGRpc3BsYXkgJ0NvbW11bml0eScKY29kZSAiRGlzY2hhcmdlIjogJ2Rpc2NoYXJnZScgZnJvbSAiTWVkaWNhdGlvblJlcXVlc3RDYXRlZ29yeSIgZGlzcGxheSAnRGlzY2hhcmdlJwoKLy8gRGlhZ25vc2lzIFJvbGUgQ29kZXMKY29kZSAiQUQiOiAnQUQnIGZyb20gIkRpYWdub3NpcyBSb2xlIiBkaXNwbGF5ICdBZG1pc3Npb24gZGlhZ25vc2lzJwpjb2RlICJERCI6ICdERCcgZnJvbSAiRGlhZ25vc2lzIFJvbGUiIGRpc3BsYXkgJ0Rpc2NoYXJnZSBkaWFnbm9zaXMnCmNvZGUgIkNDIjogJ0NDJyBmcm9tICJEaWFnbm9zaXMgUm9sZSIgZGlzcGxheSAnQ2hpZWYgY29tcGxhaW50Jwpjb2RlICJDTSI6ICdDTScgZnJvbSAiRGlhZ25vc2lzIFJvbGUiIGRpc3BsYXkgJ0NvbW9yYmlkaXR5IGRpYWdub3NpcycKY29kZSAicHJlLW9wIjogJ3ByZS1vcCcgZnJvbSAiRGlhZ25vc2lzIFJvbGUiIGRpc3BsYXkgJ3ByZS1vcCBkaWFnbm9zaXMnCmNvZGUgInBvc3Qtb3AiOiAncG9zdC1vcCcgZnJvbSAiRGlhZ25vc2lzIFJvbGUiIGRpc3BsYXkgJ3Bvc3Qtb3AgZGlhZ25vc2lzJwpjb2RlICJiaWxsaW5nIjogJ2JpbGxpbmcnIGZyb20gIkRpYWdub3NpcyBSb2xlIiBkaXNwbGF5ICdiaWxsaW5nIGRpYWdub3NpcycKCmNvbnRleHQgUGF0aWVudAoKCi8qCkBkZXNjcmlwdGlvbjogTm9ybWFsaXplcyBhIHZhbHVlIHRoYXQgaXMgYSBjaG9pY2Ugb2YgdGltaW5nLXZhbHVlZCB0eXBlcyB0byBhbiBlcXVpdmFsZW50IGludGVydmFsCkBjb21tZW50OiBOb3JtYWxpemVzIGEgY2hvaWNlIHR5cGUgb2YgRkhJUi5kYXRlVGltZSwgRkhJUi5QZXJpb2QsIEZISVIuVGltaW5nLCBGSElSLmluc3RhbmNlLCBGSElSLnN0cmluZywgRkhJUi5BZ2UsIG9yIEZISVIuUmFuZ2UgdHlwZXMKdG8gYW4gZXF1aXZhbGVudCBpbnRlcnZhbC4gVGhpcyBzZWxlY3Rpb24gb2YgY2hvaWNlIHR5cGVzIGlzIGEgc3VwZXJzZXQgb2YgdGhlIG1ham9yaXR5IG9mIGNob2ljZSB0eXBlcyB0aGF0IGFyZSB1c2VkIGFzIHBvc3NpYmxlCnJlcHJlc2VudGF0aW9ucyBmb3IgdGltaW5nLXZhbHVlZCBlbGVtZW50cyBpbiBGSElSLCBhbGxvd2luZyB0aGlzIGZ1bmN0aW9uIHRvIGJlIHVzZWQgYWNyb3NzIGFueSByZXNvdXJjZS4gTk9URTogRHVlIHRvIHRoZQpjb21wbGV4aXR5IG9mIGRldGVybWluaW5nIGEgc2luZ2xlIGludGVydmFsIGZyb20gYSBUaW1pbmcgb3IgU3RyaW5nIHR5cGUsIHRoaXMgZnVuY3Rpb24gd2lsbCB0aHJvdyBhIHJ1bi10aW1lIGV4Y2VwdGlvbiBpZiBpdCBpcyB1c2VkCndpdGggYSBUaW1pbmcgb3IgU3RyaW5nLiBOT1RFOiBUaW1pbmcgaGFzIGJlZW4gbW92ZWQgdG8gVG9UaW1pbmdJbnRlcnZhbC4KKi8KZGVmaW5lIGZ1bmN0aW9uIFRvSW50ZXJ2YWwoY2hvaWNlIENob2ljZTxGSElSLmRhdGVUaW1lLCBGSElSLlBlcmlvZCwgRkhJUi5UaW1pbmcsIEZISVIuaW5zdGFudCwgRkhJUi5zdHJpbmcsIEZISVIuQWdlLCBGSElSLlJhbmdlPik6CiAgY2FzZQoJICB3aGVuIGNob2ljZSBpcyBGSElSLmRhdGVUaW1lIHRoZW4KICAgIAlJbnRlcnZhbFtGSElSSGVscGVycy5Ub0RhdGVUaW1lKGNob2ljZSBhcyBGSElSLmRhdGVUaW1lKSwgRkhJUkhlbHBlcnMuVG9EYXRlVGltZShjaG9pY2UgYXMgRkhJUi5kYXRlVGltZSldCgkJd2hlbiBjaG9pY2UgaXMgRkhJUi5QZXJpb2QgdGhlbgogIAkJRkhJUkhlbHBlcnMuVG9JbnRlcnZhbChjaG9pY2UgYXMgRkhJUi5QZXJpb2QpCgkJd2hlbiBjaG9pY2UgaXMgRkhJUi5pbnN0YW50IHRoZW4KCQkJSW50ZXJ2YWxbRkhJUkhlbHBlcnMuVG9EYXRlVGltZShjaG9pY2UgYXMgRkhJUi5pbnN0YW50KSwgRkhJUkhlbHBlcnMuVG9EYXRlVGltZShjaG9pY2UgYXMgRkhJUi5pbnN0YW50KV0KCQl3aGVuIGNob2ljZSBpcyBGSElSLkFnZSB0aGVuCgkJICBJbnRlcnZhbFtGSElSSGVscGVycy5Ub0RhdGUoUGF0aWVudC5iaXJ0aERhdGUpICsgRkhJUkhlbHBlcnMuVG9RdWFudGl0eShjaG9pY2UgYXMgRkhJUi5BZ2UpLAoJCQkgIEZISVJIZWxwZXJzLlRvRGF0ZShQYXRpZW50LmJpcnRoRGF0ZSkgKyBGSElSSGVscGVycy5Ub1F1YW50aXR5KGNob2ljZSBhcyBGSElSLkFnZSkgKyAxIHllYXIpCgkJd2hlbiBjaG9pY2UgaXMgRkhJUi5SYW5nZSB0aGVuCgkJICBJbnRlcnZhbFtGSElSSGVscGVycy5Ub0RhdGUoUGF0aWVudC5iaXJ0aERhdGUpICsgRkhJUkhlbHBlcnMuVG9RdWFudGl0eSgoY2hvaWNlIGFzIEZISVIuUmFuZ2UpLmxvdyksCgkJCSAgRkhJUkhlbHBlcnMuVG9EYXRlKFBhdGllbnQuYmlydGhEYXRlKSArIEZISVJIZWxwZXJzLlRvUXVhbnRpdHkoKGNob2ljZSBhcyBGSElSLlJhbmdlKS5oaWdoKSArIDEgeWVhcikKICAgIHdoZW4gY2hvaWNlIGlzIEZISVIuVGltaW5nIHRoZW4KCQkgIE1lc3NhZ2UobnVsbCBhcyBJbnRlcnZhbDxEYXRlVGltZT4sIHRydWUsICcxJywgJ0Vycm9yJywgJ0Nhbm5vdCBjb21wdXRlIGEgc2luZ2xlIGludGVydmFsIGZyb20gYSBUaW1pbmcgdHlwZScpCiAgICB3aGVuIGNob2ljZSBpcyBGSElSLnN0cmluZyB0aGVuCiAgICAgIE1lc3NhZ2UobnVsbCBhcyBJbnRlcnZhbDxEYXRlVGltZT4sIHRydWUsICcxJywgJ0Vycm9yJywgJ0Nhbm5vdCBjb21wdXRlIGFuIGludGVydmFsIGZyb20gYSBTdHJpbmcgdmFsdWUnKQoJCWVsc2UKICAgICAgbnVsbCBhcyBJbnRlcnZhbDxEYXRlVGltZT4KCWVuZAoKCi8qCkBkZXNjcmlwdGlvbjogUmV0dXJucyBhIGxpc3Qgb2YgaW50ZXJ2YWxzIHJlcHJlc2VudGluZyB0aGUgbm9ybWFsaXplZCBFdmVudCBvciBCb3VuZHMgb2YgYSBUaW1pbmcgcmVzb3VyY2UuCkBjb21tZW50OiBOT1RFOiBUaW1pbmcuQm91bmRzIGlzIHJlc3RyaWN0ZWQgdG8gYSBQZXJpb2QuCiovCgpkZWZpbmUgZnVuY3Rpb24gVG9JbnRlcnZhbEZyb21MaXN0KHZhbHVlIExpc3Q8RkhJUi5kYXRlVGltZT4pOgogIHZhbHVlIHYKICAgIHJldHVybiBUb0ludGVydmFsKHYpCgpkZWZpbmUgZnVuY3Rpb24gTnVsbFRvRW1wdHlJbnRlcnZhbCh2YWx1ZSBMaXN0PEludGVydmFsPERhdGVUaW1lPj4pOgogIGlmIHZhbHVlIGlzIG5vdCBudWxsIHRoZW4gdmFsdWUgZWxzZSB7IH0KCmRlZmluZSBmdW5jdGlvbiBUb1RpbWluZ0ludGVydmFsKHRpbWluZyBGSElSLlRpbWluZyk6CiAgY29udmVydCAoCiAgICBOdWxsVG9FbXB0eUludGVydmFsKFRvSW50ZXJ2YWxGcm9tTGlzdCh0aW1pbmcuZXZlbnQpKQogICAgdW5pb24gTnVsbFRvRW1wdHlJbnRlcnZhbCh7VG9JbnRlcnZhbCh0aW1pbmcucmVwZWF0LmJvdW5kcyBhcyBGSElSLlBlcmlvZCl9KQogICkgdG8gTGlzdDxJbnRlcnZhbDxEYXRlVGltZT4+IGV4Y2VwdCB7IG51bGwgfQoKCi8qCkBkZXNjcmlwdGlvbjogcmV0dXJucyB0aGUgZXhpc3RpbmcgcG9zc2libGUgdGltZXN0YW1wIGZvciBvYnNlcnZhdGlvbiByZXNvdXJjZXMsIHByZWZlcnJpbmcgZWZmZWN0aXZlIG92ZXIgaXNzdWVkLgpAY29tbWVudDogTk9URTogbmVpdGhlciBlZmZlY3RpdmUgbm9yIGlzc3VlZCBhcmUgcmVxdWlyZWQgZmllbGRzIGluIFFJLUNvcmUKTk9URTogRnVuY3Rpb24gb3ZlcmxvYWRzIGluY29uc2lzdGVudGx5IGVyciBpbiBDUUwgMS41LCB0aGlzIGlzIGNvbW1lbnRlZCB1bnRpbCBhIGZpeCBpcyBmb3VuZApkZWZpbmUgZnVuY3Rpb24gVG9JbnRlcnZhbChvYnNlcnZhdGlvbiBGSElSLk9ic2VydmF0aW9uKToKICBvYnNlcnZhdGlvbiBvCiAgICByZXR1cm4gQ29hbGVzY2UoVG9JbnRlcnZhbChvLmVmZmVjdGl2ZSksIFRvSW50ZXJ2YWwoby5pc3N1ZWQpKQoqLwoKLyoKQGRlc2NyaXB0aW9uOiBSZXR1cm5zIGFuIGludGVydmFsIHJlcHJlc2VudGluZyB0aGUgbm9ybWFsaXplZCBBYmF0ZW1lbnQgb2YgYSBnaXZlbiBDb25kaXRpb24gcmVzb3VyY2UuCkBjb21tZW50OiBOT1RFOiBEdWUgdG8gdGhlIGNvbXBsZXhpdHkgb2YgZGV0ZXJtaW5pbmcgYW4gaW50ZXJ2YWwgZnJvbSBhIFN0cmluZywgdGhpcyBmdW5jdGlvbiB3aWxsIHRocm93CmEgcnVuLXRpbWUgZXhjZXB0aW9uIGlmIHVzZWQgd2l0aCBhIENvbmRpdGlvbiBpbnN0YW5jZSB0aGF0IGhhcyBhIFN0cmluZyBhcyB0aGUgYWJhdGVtZW50IHZhbHVlLgoqLwpkZWZpbmUgZnVuY3Rpb24gVG9BYmF0ZW1lbnRJbnRlcnZhbChjb25kaXRpb24gQ29uZGl0aW9uKToKICBpZiBjb25kaXRpb24ub25zZXQgaXMgbm90IG51bGwgYW5kIGNvbmRpdGlvbi5hYmF0ZW1lbnQgaXMgbnVsbCBhbmQgKGNvbmRpdGlvbi5jbGluaWNhbFN0YXR1cyB+ICJhY3RpdmUiCiAgICBvciBjb25kaXRpb24uY2xpbmljYWxTdGF0dXMgfiAicmVjdXJyZW5jZSIKICAgIG9yIGNvbmRpdGlvbi5jbGluaWNhbFN0YXR1cyB+ICJyZWxhcHNlIikgdGhlbgogICAgSW50ZXJ2YWxbTm93KCksIE5vdygpXQogIGVsc2UgaWYgY29uZGl0aW9uLmFiYXRlbWVudCBpcyBGSElSLmRhdGVUaW1lIHRoZW4KCSAgSW50ZXJ2YWxbRkhJUkhlbHBlcnMuVG9EYXRlVGltZShjb25kaXRpb24uYWJhdGVtZW50IGFzIEZISVIuZGF0ZVRpbWUpLCBGSElSSGVscGVycy5Ub0RhdGVUaW1lKGNvbmRpdGlvbi5hYmF0ZW1lbnQgYXMgRkhJUi5kYXRlVGltZSldCgllbHNlIGlmIGNvbmRpdGlvbi5hYmF0ZW1lbnQgaXMgRkhJUi5QZXJpb2QgdGhlbgoJICBGSElSSGVscGVycy5Ub0ludGVydmFsKGNvbmRpdGlvbi5hYmF0ZW1lbnQgYXMgRkhJUi5QZXJpb2QpCgllbHNlIGlmIGNvbmRpdGlvbi5hYmF0ZW1lbnQgaXMgRkhJUi5zdHJpbmcgdGhlbgogICAgTWVzc2FnZShudWxsIGFzIEludGVydmFsPERhdGVUaW1lPiwgdHJ1ZSwgJzEnLCAnRXJyb3InLCAnQ2Fubm90IGNvbXB1dGUgYW4gaW50ZXJ2YWwgZnJvbSBhIFN0cmluZyB2YWx1ZScpCgllbHNlIGlmIGNvbmRpdGlvbi5hYmF0ZW1lbnQgaXMgRkhJUi5BZ2UgdGhlbgoJCUludGVydmFsW0ZISVJIZWxwZXJzLlRvRGF0ZShQYXRpZW50LmJpcnRoRGF0ZSkgKyBGSElSSGVscGVycy5Ub1F1YW50aXR5KGNvbmRpdGlvbi5hYmF0ZW1lbnQgYXMgRkhJUi5BZ2UpLAoJCQlGSElSSGVscGVycy5Ub0RhdGUoUGF0aWVudC5iaXJ0aERhdGUpICsgRkhJUkhlbHBlcnMuVG9RdWFudGl0eShjb25kaXRpb24uYWJhdGVtZW50IGFzIEZISVIuQWdlKSArIDEgeWVhcikKCWVsc2UgaWYgY29uZGl0aW9uLmFiYXRlbWVudCBpcyBGSElSLlJhbmdlIHRoZW4KCSAgSW50ZXJ2YWxbRkhJUkhlbHBlcnMuVG9EYXRlKFBhdGllbnQuYmlydGhEYXRlKSArIEZISVJIZWxwZXJzLlRvUXVhbnRpdHkoKGNvbmRpdGlvbi5hYmF0ZW1lbnQgYXMgRkhJUi5SYW5nZSkubG93KSwKCQkgIEZISVJIZWxwZXJzLlRvRGF0ZShQYXRpZW50LmJpcnRoRGF0ZSkgKyBGSElSSGVscGVycy5Ub1F1YW50aXR5KChjb25kaXRpb24uYWJhdGVtZW50IGFzIEZISVIuUmFuZ2UpLmhpZ2gpICsgMSB5ZWFyKQoJZWxzZSBpZiBjb25kaXRpb24uYWJhdGVtZW50IGlzIEZISVIuYm9vbGVhbiB0aGVuCgkgIEludGVydmFsW2VuZCBvZiBUb0ludGVydmFsKGNvbmRpdGlvbi5vbnNldCksIGNvbmRpdGlvbi5yZWNvcmRlZERhdGUpCgllbHNlIG51bGwKCi8qCkBkZXNjcmlwdGlvbjogUmV0dXJucyBhbiBpbnRlcnZhbCByZXByZXNlbnRpbmcgdGhlIG5vcm1hbGl6ZWQgcHJldmFsZW5jZSBwZXJpb2Qgb2YgYSBnaXZlbiBDb25kaXRpb24gcmVzb3VyY2UuCkBjb21tZW50OiBVc2VzIHRoZSBUb0ludGVydmFsIGFuZCBUb0FiYXRlbWVudEludGVydmFsIGZ1bmN0aW9ucyB0byBkZXRlcm1pbmUgdGhlIHdpZGVzdCBwb3RlbnRpYWwgaW50ZXJ2YWwgZnJvbQpvbnNldCB0byBhYmF0ZW1lbnQgYXMgc3BlY2lmaWVkIGluIHRoZSBnaXZlbiBDb25kaXRpb24uCiovCmRlZmluZSBmdW5jdGlvbiBUb1ByZXZhbGVuY2VJbnRlcnZhbChjb25kaXRpb24gQ29uZGl0aW9uKToKICBJbnRlcnZhbFtzdGFydCBvZiBUb0ludGVydmFsKGNvbmRpdGlvbi5vbnNldCksIGVuZCBvZiBUb0FiYXRlbWVudEludGVydmFsKGNvbmRpdGlvbikpCgovKgpAZGVzY3JpcHRpb246IFJldHVybnMgYW4gaW50ZXJ2YWwgcmVwcmVzZW50aW5nIHRoZSBub3JtYWxpemVkIHByZXZhbGVuY2UgcGVyaW9kIG9mIGEgZ2l2ZW4gQWxsZXJneSBJbnRvbGVyYW5jZSByZXNvdXJjZS4KQGNvbW1lbnQ6IFVzZXMgdGhlIFRvSW50ZXJ2YWwgZnVuY3Rpb24gYW5kIGxhc3RPY2N1cmVuY2UgdG8gZGV0ZXJtaW5lIHRoZSB3aWRlc3QgcG90ZW50aWFsIGludGVydmFsLgoqLwpkZWZpbmUgZnVuY3Rpb24gVG9QcmV2YWxlbmNlSW50ZXJ2YWwoYWxsZXJneUludG9sZXJhbmNlIEFsbGVyZ3lJbnRvbGVyYW5jZSk6CiAgSW50ZXJ2YWxbc3RhcnQgb2YgVG9JbnRlcnZhbChhbGxlcmd5SW50b2xlcmFuY2Uub25zZXQpLCBDb2FsZXNjZShhbGxlcmd5SW50b2xlcmFuY2UubGFzdE9jY3VycmVuY2UsIE5vdygpKV0KCgovKgpAZGVzY3JpcHRpb246IFJldHVybnMgYW55IGV4dGVuc2lvbnMgZGVmaW5lZCBvbiB0aGUgZ2l2ZW4gcmVzb3VyY2Ugd2l0aCB0aGUgc3BlY2lmaWVkIHVybC4KQGNvbW1lbnQ6IE5PVEU6IEV4dGVuc2lvbnMgYXJlIG5vdCB0aGUgcHJlZmVycmVkIGFwcHJvYWNoLCBidXQgYXJlIHVzZWQgYXMgYSB3YXkgdG8gYWNjZXNzCmNvbnRlbnQgdGhhdCBpcyBkZWZpbmVkIGJ5IGV4dGVuc2lvbnMgYnV0IG5vdCB5ZXQgc3VyZmFjZWQgaW4gdGhlCkNRTCBtb2RlbCBpbmZvLgoqLwpkZWZpbmUgZnVuY3Rpb24gRXh0ZW5zaW9ucyhkb21haW5SZXNvdXJjZSBEb21haW5SZXNvdXJjZSwgdXJsIFN0cmluZyk6CiAgZG9tYWluUmVzb3VyY2UuZXh0ZW5zaW9uIEUKCSAgd2hlcmUgRS51cmwgPSB1cmwKCQlyZXR1cm4gRQoKLyoKQGRlc2NyaXB0aW9uOiBSZXR1cm5zIHRoZSBzaW5nbGUgZXh0ZW5zaW9uIChpZiBwcmVzZW50KSBvbiB0aGUgZ2l2ZW4gcmVzb3VyY2Ugd2l0aCB0aGUgc3BlY2lmaWVkIHVybC4KQGNvbW1lbnQ6IFRoaXMgZnVuY3Rpb24gdXNlcyBzaW5nbGV0b24gZnJvbSB0byBlbnN1cmUgdGhhdCBhIHJ1bi10aW1lIGV4Y2VwdGlvbiBpcyB0aHJvd24gaWYgdGhlcmUKaXMgbW9yZSB0aGFuIG9uZSBleHRlbnNpb24gb24gdGhlIGdpdmVuIHJlc291cmNlIHdpdGggdGhlIHNwZWNpZmllZCB1cmwuCiovCmRlZmluZSBmdW5jdGlvbiBFeHRlbnNpb24oZG9tYWluUmVzb3VyY2UgRG9tYWluUmVzb3VyY2UsIHVybCBTdHJpbmcpOgogIHNpbmdsZXRvbiBmcm9tICJFeHRlbnNpb25zIihkb21haW5SZXNvdXJjZSwgdXJsKQoKLyoKQGRlc2NyaXB0aW9uOiBSZXR1cm5zIGFueSBleHRlbnNpb25zIGRlZmluZWQgb24gdGhlIGdpdmVuIGVsZW1lbnQgd2l0aCB0aGUgc3BlY2lmaWVkIHVybC4KQGNvbW1lbnQ6IE5PVEU6IEV4dGVuc2lvbnMgYXJlIG5vdCB0aGUgcHJlZmVycmVkIGFwcHJvYWNoLCBidXQgYXJlIHVzZWQgYXMgYSB3YXkgdG8gYWNjZXNzCmNvbnRlbnQgdGhhdCBpcyBkZWZpbmVkIGJ5IGV4dGVuc2lvbnMgYnV0IG5vdCB5ZXQgc3VyZmFjZWQgaW4gdGhlIENRTCBtb2RlbCBpbmZvLgoqLwpkZWZpbmUgZnVuY3Rpb24gRXh0ZW5zaW9ucyhlbGVtZW50IEVsZW1lbnQsIHVybCBTdHJpbmcpOgogIGVsZW1lbnQuZXh0ZW5zaW9uIEUKCSAgd2hlcmUgRS51cmwgPSB1cmwKCQlyZXR1cm4gRQoKLyoKQGRlc2NyaXB0aW9uOiBSZXR1cm5zIHRoZSBzaW5nbGUgZXh0ZW5zaW9uIChpZiBwcmVzZW50KSBvbiB0aGUgZ2l2ZW4gZWxlbWVudCB3aXRoIHRoZSBzcGVjaWZpZWQgdXJsLgpAY29tbWVudDogVGhpcyBmdW5jdGlvbiB1c2VzIHNpbmdsZXRvbiBmcm9tIHRvIGVuc3VyZSB0aGF0IGEgcnVuLXRpbWUgZXhjZXB0aW9uIGlzIHRocm93biBpZiB0aGVyZQppcyBtb3JlIHRoYW4gb25lIGV4dGVuc2lvbiBvbiB0aGUgZ2l2ZW4gcmVzb3VyY2Ugd2l0aCB0aGUgc3BlY2lmaWVkIHVybC4KKi8KZGVmaW5lIGZ1bmN0aW9uIEV4dGVuc2lvbihlbGVtZW50IEVsZW1lbnQsIHVybCBTdHJpbmcpOgogIHNpbmdsZXRvbiBmcm9tIEV4dGVuc2lvbnMoZWxlbWVudCwgdXJsKQoKLyoKQGRlc2NyaXB0aW9uOiBSZXR1cm5zIGFueSBtb2RpZmllciBleHRlbnNpb25zIGRlZmluZWQgb24gdGhlIGdpdmVuIHJlc291cmNlIHdpdGggdGhlIHNwZWNpZmllZCB1cmwuCkBjb21tZW50OiBOT1RFOiBFeHRlbnNpb25zIGFyZSBub3QgdGhlIHByZWZlcnJlZCBhcHByb2FjaCwgYnV0IGFyZSB1c2VkIGFzIGEgd2F5IHRvIGFjY2Vzcwpjb250ZW50IHRoYXQgaXMgZGVmaW5lZCBieSBleHRlbnNpb25zIGJ1dCBub3QgeWV0IHN1cmZhY2VkIGluIHRoZQpDUUwgbW9kZWwgaW5mby4KKi8KZGVmaW5lIGZ1bmN0aW9uIE1vZGlmaWVyRXh0ZW5zaW9ucyhkb21haW5SZXNvdXJjZSBEb21haW5SZXNvdXJjZSwgdXJsIFN0cmluZyk6CiAgZG9tYWluUmVzb3VyY2UubW9kaWZpZXJFeHRlbnNpb24gRQoJICB3aGVyZSBFLnVybCA9IHVybAoJCXJldHVybiBFCgovKgpAZGVzY3JpcHRpb246IFJldHVybnMgdGhlIHNpbmdsZSBtb2RpZmllciBleHRlbnNpb24gKGlmIHByZXNlbnQpIG9uIHRoZSBnaXZlbiByZXNvdXJjZSB3aXRoIHRoZSBzcGVjaWZpZWQgdXJsLgpAY29tbWVudDogVGhpcyBmdW5jdGlvbiB1c2VzIHNpbmdsZXRvbiBmcm9tIHRvIGVuc3VyZSB0aGF0IGEgcnVuLXRpbWUgZXhjZXB0aW9uIGlzIHRocm93biBpZiB0aGVyZQppcyBtb3JlIHRoYW4gb25lIGV4dGVuc2lvbiBvbiB0aGUgZ2l2ZW4gcmVzb3VyY2Ugd2l0aCB0aGUgc3BlY2lmaWVkIHVybC4KKi8KZGVmaW5lIGZ1bmN0aW9uIE1vZGlmaWVyRXh0ZW5zaW9uKGRvbWFpblJlc291cmNlIERvbWFpblJlc291cmNlLCB1cmwgU3RyaW5nKToKICBzaW5nbGV0b24gZnJvbSBNb2RpZmllckV4dGVuc2lvbnMoZG9tYWluUmVzb3VyY2UsIHVybCkKCi8qCkBkZXNjcmlwdGlvbjogUmV0dXJucyBhbnkgbW9kaWZpZXIgZXh0ZW5zaW9ucyBkZWZpbmVkIG9uIHRoZSBnaXZlbiBlbGVtZW50IHdpdGggdGhlIHNwZWNpZmllZCB1cmwuCkBjb21tZW50OiBOT1RFOiBFeHRlbnNpb25zIGFyZSBub3QgdGhlIHByZWZlcnJlZCBhcHByb2FjaCwgYnV0IGFyZSB1c2VkIGFzIGEgd2F5IHRvIGFjY2Vzcwpjb250ZW50IHRoYXQgaXMgZGVmaW5lZCBieSBleHRlbnNpb25zIGJ1dCBub3QgeWV0IHN1cmZhY2VkIGluIHRoZSBDUUwgbW9kZWwgaW5mby4KKi8KZGVmaW5lIGZ1bmN0aW9uIE1vZGlmaWVyRXh0ZW5zaW9ucyhlbGVtZW50IEJhY2tib25lRWxlbWVudCwgdXJsIFN0cmluZyk6CiAgZWxlbWVudC5tb2RpZmllckV4dGVuc2lvbiBFCgkgIHdoZXJlIEUudXJsID0gdXJsCgkJcmV0dXJuIEUKCi8qCkBkZXNjcmlwdGlvbjogUmV0dXJucyB0aGUgc2luZ2xlIG1vZGlmaWVyIGV4dGVuc2lvbiAoaWYgcHJlc2VudCkgb24gdGhlIGdpdmVuIGVsZW1lbnQgd2l0aCB0aGUgc3BlY2lmaWVkIHVybC4KQGNvbW1lbnQ6IFRoaXMgZnVuY3Rpb24gdXNlcyBzaW5nbGV0b24gZnJvbSB0byBlbnN1cmUgdGhhdCBhIHJ1bi10aW1lIGV4Y2VwdGlvbiBpcyB0aHJvd24gaWYgdGhlcmUKaXMgbW9yZSB0aGFuIG9uZSBleHRlbnNpb24gb24gdGhlIGdpdmVuIHJlc291cmNlIHdpdGggdGhlIHNwZWNpZmllZCB1cmwuCiovCmRlZmluZSBmdW5jdGlvbiBNb2RpZmllckV4dGVuc2lvbihlbGVtZW50IEJhY2tib25lRWxlbWVudCwgdXJsIFN0cmluZyk6CiAgc2luZ2xldG9uIGZyb20gTW9kaWZpZXJFeHRlbnNpb25zKGVsZW1lbnQsIHVybCkKCi8qCkBkZXNjcmlwdGlvbjogUmV0dXJucyBhbnkgYmFzZS1GSElSIGV4dGVuc2lvbnMgZGVmaW5lZCBvbiB0aGUgZ2l2ZW4gcmVzb3VyY2Ugd2l0aCB0aGUgc3BlY2lmaWVkIGlkLgpAY29tbWVudDogTk9URTogRXh0ZW5zaW9ucyBhcmUgbm90IHRoZSBwcmVmZXJyZWQgYXBwcm9hY2gsIGJ1dCBhcmUgdXNlZCBhcyBhIHdheSB0byBhY2Nlc3MKY29udGVudCB0aGF0IGlzIGRlZmluZWQgYnkgZXh0ZW5zaW9ucyBidXQgbm90IHlldCBzdXJmYWNlZCBpbiB0aGUgQ1FMIG1vZGVsIGluZm8uCiovCmRlZmluZSBmdW5jdGlvbiBCYXNlRXh0ZW5zaW9ucyhkb21haW5SZXNvdXJjZSBEb21haW5SZXNvdXJjZSwgaWQgU3RyaW5nKToKICBkb21haW5SZXNvdXJjZS5leHRlbnNpb24gRQoJICB3aGVyZSBFLnVybCA9ICgnaHR0cDovL2hsNy5vcmcvZmhpci9TdHJ1Y3R1cmVEZWZpbml0aW9uLycgKyBpZCkKCQlyZXR1cm4gRQoKLyoKQGRlc2NyaXB0aW9uOiBSZXR1cm5zIHRoZSBzaW5nbGUgYmFzZS1GSElSIGV4dGVuc2lvbiAoaWYgcHJlc2VudCkgb24gdGhlIGdpdmVuIHJlc291cmNlIHdpdGggdGhlIHNwZWNpZmllZCBpZC4KQGNvbW1lbnQ6IFRoaXMgZnVuY3Rpb24gdXNlcyBzaW5nbGV0b24gZnJvbSB0byBlbnN1cmUgdGhhdCBhIHJ1bi10aW1lIGV4Y2VwdGlvbiBpcyB0aHJvd24gaWYgdGhlcmUKaXMgbW9yZSB0aGFuIG9uZSBleHRlbnNpb24gb24gdGhlIGdpdmVuIHJlc291cmNlIHdpdGggdGhlIHNwZWNpZmllZCB1cmwuCiovCmRlZmluZSBmdW5jdGlvbiBCYXNlRXh0ZW5zaW9uKGRvbWFpblJlc291cmNlIERvbWFpblJlc291cmNlLCBpZCBTdHJpbmcpOgogIHNpbmdsZXRvbiBmcm9tIEJhc2VFeHRlbnNpb25zKGRvbWFpblJlc291cmNlLCBpZCkKCi8qCkBkZXNjcmlwdGlvbjogUmV0dXJucyBhbnkgYmFzZS1GSElSIGV4dGVuc2lvbnMgZGVmaW5lZCBvbiB0aGUgZ2l2ZW4gZWxlbWVudCB3aXRoIHRoZSBzcGVjaWZpZWQgaWQuCkBjb21tZW50OiBOT1RFOiBFeHRlbnNpb25zIGFyZSBub3QgdGhlIHByZWZlcnJlZCBhcHByb2FjaCwgYnV0IGFyZSB1c2VkIGFzIGEgd2F5IHRvIGFjY2Vzcwpjb250ZW50IHRoYXQgaXMgZGVmaW5lZCBieSBleHRlbnNpb25zIGJ1dCBub3QgeWV0IHN1cmZhY2VkIGluIHRoZSBDUUwgbW9kZWwgaW5mby4KKi8KZGVmaW5lIGZ1bmN0aW9uIEJhc2VFeHRlbnNpb25zKGVsZW1lbnQgRWxlbWVudCwgaWQgU3RyaW5nKToKICBlbGVtZW50LmV4dGVuc2lvbiBFCgkgIHdoZXJlIEUudXJsID0gKCdodHRwOi8vaGw3Lm9yZy9maGlyL1N0cnVjdHVyZURlZmluaXRpb24vJyArIGlkKQoJCXJldHVybiBFCgovKgpAZGVzY3JpcHRpb246IFJldHVybnMgdGhlIHNpbmdsZSBiYXNlLUZISVIgZXh0ZW5zaW9uIChpZiBwcmVzZW50KSBvbiB0aGUgZ2l2ZW4gZWxlbWVudCB3aXRoIHRoZSBzcGVjaWZpZWQgaWQuCkBjb21tZW50OiBUaGlzIGZ1bmN0aW9uIHVzZXMgc2luZ2xldG9uIGZyb20gdG8gZW5zdXJlIHRoYXQgYSBydW4tdGltZSBleGNlcHRpb24gaXMgdGhyb3duIGlmIHRoZXJlCmlzIG1vcmUgdGhhbiBvbmUgZXh0ZW5zaW9uIG9uIHRoZSBnaXZlbiByZXNvdXJjZSB3aXRoIHRoZSBzcGVjaWZpZWQgdXJsLgoqLwpkZWZpbmUgZnVuY3Rpb24gQmFzZUV4dGVuc2lvbihlbGVtZW50IEVsZW1lbnQsIGlkIFN0cmluZyk6CiAgc2luZ2xldG9uIGZyb20gQmFzZUV4dGVuc2lvbnMoZWxlbWVudCwgaWQpCgovKgpAZGVzY3JpcHRpb246IFJldHVybnMgYW55IGJhc2UtRkhJUiBtb2RpZmllciBleHRlbnNpb25zIGRlZmluZWQgb24gdGhlIGdpdmVuIHJlc291cmNlIHdpdGggdGhlIHNwZWNpZmllZCBpZC4KQGNvbW1lbnQ6IE5PVEU6IEV4dGVuc2lvbnMgYXJlIG5vdCB0aGUgcHJlZmVycmVkIGFwcHJvYWNoLCBidXQgYXJlIHVzZWQgYXMgYSB3YXkgdG8gYWNjZXNzCmNvbnRlbnQgdGhhdCBpcyBkZWZpbmVkIGJ5IGV4dGVuc2lvbnMgYnV0IG5vdCB5ZXQgc3VyZmFjZWQgaW4gdGhlIENRTCBtb2RlbCBpbmZvLgoqLwpkZWZpbmUgZnVuY3Rpb24gQmFzZU1vZGlmaWVyRXh0ZW5zaW9ucyhkb21haW5SZXNvdXJjZSBEb21haW5SZXNvdXJjZSwgaWQgU3RyaW5nKToKICBkb21haW5SZXNvdXJjZS5tb2RpZmllckV4dGVuc2lvbiBFCgkgIHdoZXJlIEUudXJsID0gKCdodHRwOi8vaGw3Lm9yZy9maGlyL1N0cnVjdHVyZURlZmluaXRpb24vJyArIGlkKQoJCXJldHVybiBFCgovKgpAZGVzY3JpcHRpb246IFJldHVybnMgdGhlIHNpbmdsZSBiYXNlLUZISVIgbW9kaWZpZXIgZXh0ZW5zaW9uIChpZiBwcmVzZW50KSBvbiB0aGUgZ2l2ZW4gcmVzb3VyY2Ugd2l0aCB0aGUgc3BlY2lmaWVkIGlkLgpAY29tbWVudDogVGhpcyBmdW5jdGlvbiB1c2VzIHNpbmdsZXRvbiBmcm9tIHRvIGVuc3VyZSB0aGF0IGEgcnVuLXRpbWUgZXhjZXB0aW9uIGlzIHRocm93biBpZiB0aGVyZQppcyBtb3JlIHRoYW4gb25lIGV4dGVuc2lvbiBvbiB0aGUgZ2l2ZW4gcmVzb3VyY2Ugd2l0aCB0aGUgc3BlY2lmaWVkIHVybC4KKi8KZGVmaW5lIGZ1bmN0aW9uIEJhc2VNb2RpZmllckV4dGVuc2lvbihkb21haW5SZXNvdXJjZSBEb21haW5SZXNvdXJjZSwgaWQgU3RyaW5nKToKICBzaW5nbGV0b24gZnJvbSBCYXNlTW9kaWZpZXJFeHRlbnNpb25zKGRvbWFpblJlc291cmNlLCBpZCkKCi8qCkBkZXNjcmlwdGlvbjogUmV0dXJucyBhbnkgYmFzZS1GSElSIG1vZGlmaWVyIGV4dGVuc2lvbnMgZGVmaW5lZCBvbiB0aGUgZ2l2ZW4gZWxlbWVudCB3aXRoIHRoZSBzcGVjaWZpZWQgaWQuCkBjb21tZW50OiBOT1RFOiBFeHRlbnNpb25zIGFyZSBub3QgdGhlIHByZWZlcnJlZCBhcHByb2FjaCwgYnV0IGFyZSB1c2VkIGFzIGEgd2F5IHRvIGFjY2Vzcwpjb250ZW50IHRoYXQgaXMgZGVmaW5lZCBieSBleHRlbnNpb25zIGJ1dCBub3QgeWV0IHN1cmZhY2VkIGluIHRoZSBDUUwgbW9kZWwgaW5mby4KKi8KZGVmaW5lIGZ1bmN0aW9uIEJhc2VNb2RpZmllckV4dGVuc2lvbnMoZWxlbWVudCBCYWNrYm9uZUVsZW1lbnQsIGlkIFN0cmluZyk6CiAgZWxlbWVudC5tb2RpZmllckV4dGVuc2lvbiBFCgkgIHdoZXJlIEUudXJsID0gKCdodHRwOi8vaGw3Lm9yZy9maGlyL1N0cnVjdHVyZURlZmluaXRpb24vJyArIGlkKQoJCXJldHVybiBFCgovKgpAZGVzY3JpcHRpb246IFJldHVybnMgdGhlIHNpbmdsZSBiYXNlLUZISVIgZXh0ZW5zaW9uIChpZiBwcmVzZW50KSBvbiB0aGUgZ2l2ZW4gZWxlbWVudCB3aXRoIHRoZSBzcGVjaWZpZWQgaWQuCkBjb21tZW50OiBUaGlzIGZ1bmN0aW9uIHVzZXMgc2luZ2xldG9uIGZyb20gdG8gZW5zdXJlIHRoYXQgYSBydW4tdGltZSBleGNlcHRpb24gaXMgdGhyb3duIGlmIHRoZXJlCmlzIG1vcmUgdGhhbiBvbmUgZXh0ZW5zaW9uIG9uIHRoZSBnaXZlbiByZXNvdXJjZSB3aXRoIHRoZSBzcGVjaWZpZWQgdXJsLgoqLwpkZWZpbmUgZnVuY3Rpb24gQmFzZU1vZGlmaWVyRXh0ZW5zaW9uKGVsZW1lbnQgQmFja2JvbmVFbGVtZW50LCBpZCBTdHJpbmcpOgogIHNpbmdsZXRvbiBmcm9tIEJhc2VNb2RpZmllckV4dGVuc2lvbnMoZWxlbWVudCwgaWQpCg==" + } + ] + }, + "request": { + "method": "PUT", + "url": "Library/FHIRCommon" + } + }, + { + "resource": { + "resourceType": "Patient", + "id": "CMSTest-patient-1", + "meta": { + "profile": [ + "http://hl7.org/fhir/us/core/StructureDefinition/us-core-patient" + ] + }, + "extension": [ + { + "url": "http://hl7.org/fhir/us/core/StructureDefinition/us-core-race", + "extension": [ + { + "url": "ombCategory", + "valueCoding": { + "system": "urn:oid:2.16.840.1.113883.6.238", + "code": "2028-9", + "display": "Asian" + } + } + ] + }, + { + "url": "http://hl7.org/fhir/us/core/StructureDefinition/us-core-ethnicity", + "extension": [ + { + "url": "ombCategory", + "valueCoding": { + "system": "urn:oid:2.16.840.1.113883.6.238", + "code": "2135-2", + "display": "Hispanic or Latino" + } + } + ] + } + ], + "identifier": [ + { + "use": "usual", + "type": { + "coding": [ + { + "system": "http://terminology.hl7.org/CodeSystem/v2-0203", + "code": "MR", + "display": "Medical Record Number" + } + ] + }, + "system": "http://hospital.smarthealthit.org", + "value": "999999992" + } + ], + "name": [ + { + "family": "Dere", + "given": [ + "Ben" + ] + } + ], + "gender": "male", + "birthDate": "1965-01-01" + }, + "request": { + "method": "PUT", + "url": "Patient/CMSTest-patient-1" + } + } + ] } diff --git a/hapi-fhir-storage-cr/src/test/resources/multiversion/EXM130-0.0.001-bundle.json b/hapi-fhir-storage-cr/src/test/resources/multiversion/EXM130-0.0.001-bundle.json new file mode 100644 index 00000000000..a5f8dd31414 --- /dev/null +++ b/hapi-fhir-storage-cr/src/test/resources/multiversion/EXM130-0.0.001-bundle.json @@ -0,0 +1,916 @@ +{ + "resourceType": "Bundle", + "id": "GeneratedBundle2", + "type": "transaction", + "entry": [ + { + "resource": { + "resourceType": "Library", + "id": "ColorectalCancerScreeningsFHIR", + "meta": { + "profile": [ + "http://hl7.org/fhir/us/cqfmeasures/StructureDefinition/computable-library-cqfm" + ] + }, + "language": "en", + "extension": [ + { + "url": "http://hl7.org/fhir/us/cqfmeasures/StructureDefinition/cqfm-softwaresystem", + "valueReference": { + "reference": "Device/cqf-tooling" + } + } + ], + "url": "http://ecqi.healthit.gov/ecqms/Library/ColorectalCancerScreeningsFHIR", + "version": "0.0.001", + "name": "ColorectalCancerScreeningsFHIR", + "status": "active", + "experimental": false, + "type": { + "coding": [ + { + "system": "http://terminology.hl7.org/CodeSystem/library-type", + "code": "logic-library" + } + ] + }, + "date": "2023-10-03T15:32:03+00:00", + "publisher": "National Committee for Quality Assurance", + "description": "Colorectal Cancer ScreeningFHIR", + "relatedArtifact": [ + { + "type": "depends-on", + "display": "FHIR model information", + "resource": "http://fhir.org/guides/cqf/common/Library/FHIR-ModelInfo|4.0.1" + }, + { + "type": "depends-on", + "display": "Library FHIRHelpers", + "resource": "http://ecqi.healthit.gov/ecqms/Library/FHIRHelpers|4.0.001" + }, + { + "type": "depends-on", + "display": "Library SDE", + "resource": "http://ecqi.healthit.gov/ecqms/Library/SupplementalDataElementsFHIR4|2.0.000" + }, + { + "type": "depends-on", + "display": "Library Global", + "resource": "http://ecqi.healthit.gov/ecqms/Library/MATGlobalCommonFunctionsFHIR4|6.0.000" + }, + { + "type": "depends-on", + "display": "Library AdultOutpatientEncounters", + "resource": "http://ecqi.healthit.gov/ecqms/Library/AdultOutpatientEncountersFHIR4|2.0.000" + }, + { + "type": "depends-on", + "display": "Library Hospice", + "resource": "http://ecqi.healthit.gov/ecqms/Library/HospiceFHIR4|2.0.000" + }, + { + "type": "depends-on", + "display": "Library Frailty", + "resource": "http://ecqi.healthit.gov/ecqms/Library/AdvancedIllnessandFrailtyExclusionECQMFHIR4|5.12.000" + }, + { + "type": "depends-on", + "display": "Code system LOINC", + "resource": "http://loinc.org" + }, + { + "type": "depends-on", + "display": "Code system SNOMEDCT:2017-09", + "resource": "http://snomed.info/sct|http://snomed.info/sct/version/201709" + }, + { + "type": "depends-on", + "display": "Value set Acute Inpatient", + "resource": "http://cts.nlm.nih.gov/fhir/ValueSet/2.16.840.1.113883.3.464.1003.101.12.1083" + }, + { + "type": "depends-on", + "display": "Value set Advanced Illness", + "resource": "http://cts.nlm.nih.gov/fhir/ValueSet/2.16.840.1.113883.3.464.1003.110.12.1082" + }, + { + "type": "depends-on", + "display": "Value set Annual Wellness Visit", + "resource": "http://cts.nlm.nih.gov/fhir/ValueSet/2.16.840.1.113883.3.526.3.1240" + }, + { + "type": "depends-on", + "display": "Value set Care Services in Long-Term Residential Facility", + "resource": "http://cts.nlm.nih.gov/fhir/ValueSet/2.16.840.1.113883.3.464.1003.101.12.1014" + }, + { + "type": "depends-on", + "display": "Value set Colonoscopy", + "resource": "http://cts.nlm.nih.gov/fhir/ValueSet/2.16.840.1.113883.3.464.1003.108.12.1020" + }, + { + "type": "depends-on", + "display": "Value set CT Colonography", + "resource": "http://cts.nlm.nih.gov/fhir/ValueSet/2.16.840.1.113883.3.464.1003.108.12.1038" + }, + { + "type": "depends-on", + "display": "Value set Dementia Medications", + "resource": "http://cts.nlm.nih.gov/fhir/ValueSet/2.16.840.1.113883.3.464.1003.196.12.1510" + }, + { + "type": "depends-on", + "display": "Value set Discharged to Health Care Facility for Hospice Care", + "resource": "http://cts.nlm.nih.gov/fhir/ValueSet/2.16.840.1.113883.3.117.1.7.1.207" + }, + { + "type": "depends-on", + "display": "Value set Discharged to Home for Hospice Care", + "resource": "http://cts.nlm.nih.gov/fhir/ValueSet/2.16.840.1.113883.3.117.1.7.1.209" + }, + { + "type": "depends-on", + "display": "Value set ED", + "resource": "http://cts.nlm.nih.gov/fhir/ValueSet/2.16.840.1.113883.3.464.1003.101.12.1085" + }, + { + "type": "depends-on", + "display": "Value set Encounter Inpatient", + "resource": "http://cts.nlm.nih.gov/fhir/ValueSet/2.16.840.1.113883.3.666.5.307" + }, + { + "type": "depends-on", + "display": "Value set Fecal Occult Blood Test (FOBT)", + "resource": "http://cts.nlm.nih.gov/fhir/ValueSet/2.16.840.1.113883.3.464.1003.198.12.1011" + }, + { + "type": "depends-on", + "display": "Value set FIT DNA", + "resource": "http://cts.nlm.nih.gov/fhir/ValueSet/2.16.840.1.113883.3.464.1003.108.12.1039" + }, + { + "type": "depends-on", + "display": "Value set Flexible Sigmoidoscopy", + "resource": "http://cts.nlm.nih.gov/fhir/ValueSet/2.16.840.1.113883.3.464.1003.198.12.1010" + }, + { + "type": "depends-on", + "display": "Value set Frailty Device", + "resource": "http://cts.nlm.nih.gov/fhir/ValueSet/2.16.840.1.113883.3.464.1003.118.12.1300" + }, + { + "type": "depends-on", + "display": "Value set Frailty Diagnosis", + "resource": "http://cts.nlm.nih.gov/fhir/ValueSet/2.16.840.1.113883.3.464.1003.113.12.1074" + }, + { + "type": "depends-on", + "display": "Value set Frailty Encounter", + "resource": "http://cts.nlm.nih.gov/fhir/ValueSet/2.16.840.1.113883.3.464.1003.101.12.1088" + }, + { + "type": "depends-on", + "display": "Value set Frailty Symptom", + "resource": "http://cts.nlm.nih.gov/fhir/ValueSet/2.16.840.1.113883.3.464.1003.113.12.1075" + }, + { + "type": "depends-on", + "display": "Value set Home Healthcare Services", + "resource": "http://cts.nlm.nih.gov/fhir/ValueSet/2.16.840.1.113883.3.464.1003.101.12.1016" + }, + { + "type": "depends-on", + "display": "Value set Hospice care ambulatory", + "resource": "http://cts.nlm.nih.gov/fhir/ValueSet/2.16.840.1.113762.1.4.1108.15" + }, + { + "type": "depends-on", + "display": "Value set Malignant Neoplasm of Colon", + "resource": "http://cts.nlm.nih.gov/fhir/ValueSet/2.16.840.1.113883.3.464.1003.108.12.1001" + }, + { + "type": "depends-on", + "display": "Value set Nonacute Inpatient", + "resource": "http://cts.nlm.nih.gov/fhir/ValueSet/2.16.840.1.113883.3.464.1003.101.12.1084" + }, + { + "type": "depends-on", + "display": "Value set Nursing Facility Visit", + "resource": "http://cts.nlm.nih.gov/fhir/ValueSet/2.16.840.1.113883.3.464.1003.101.12.1012" + }, + { + "type": "depends-on", + "display": "Value set Observation", + "resource": "http://cts.nlm.nih.gov/fhir/ValueSet/2.16.840.1.113883.3.464.1003.101.12.1086" + }, + { + "type": "depends-on", + "display": "Value set Office Visit", + "resource": "http://cts.nlm.nih.gov/fhir/ValueSet/2.16.840.1.113883.3.464.1003.101.12.1001" + }, + { + "type": "depends-on", + "display": "Value set Outpatient", + "resource": "http://cts.nlm.nih.gov/fhir/ValueSet/2.16.840.1.113883.3.464.1003.101.12.1087" + }, + { + "type": "depends-on", + "display": "Value set Preventive Care Services - Established Office Visit, 18 and Up", + "resource": "http://cts.nlm.nih.gov/fhir/ValueSet/2.16.840.1.113883.3.464.1003.101.12.1025" + }, + { + "type": "depends-on", + "display": "Value set Preventive Care Services-Initial Office Visit, 18 and Up", + "resource": "http://cts.nlm.nih.gov/fhir/ValueSet/2.16.840.1.113883.3.464.1003.101.12.1023" + }, + { + "type": "depends-on", + "display": "Value set Total Colectomy", + "resource": "http://cts.nlm.nih.gov/fhir/ValueSet/2.16.840.1.113883.3.464.1003.198.12.1019" + }, + { + "type": "depends-on", + "display": "Value set Payer", + "resource": "http://cts.nlm.nih.gov/fhir/ValueSet/2.16.840.1.114222.4.11.3591" + } + ], + "parameter": [ + { + "name": "Measurement Period", + "use": "in", + "min": 0, + "max": "1", + "type": "Period" + }, + { + "name": "Patient", + "use": "out", + "min": 0, + "max": "1", + "type": "Patient" + }, + { + "name": "SDE Ethnicity", + "use": "out", + "min": 0, + "max": "*", + "type": "Coding" + }, + { + "name": "SDE Payer", + "use": "out", + "min": 0, + "max": "*", + "type": "Any" + }, + { + "name": "SDE Race", + "use": "out", + "min": 0, + "max": "*", + "type": "Coding" + }, + { + "name": "SDE Sex", + "use": "out", + "min": 0, + "max": "1", + "type": "Coding" + }, + { + "name": "Initial Population", + "use": "out", + "min": 0, + "max": "1", + "type": "boolean" + }, + { + "name": "Denominator", + "use": "out", + "min": 0, + "max": "1", + "type": "boolean" + }, + { + "name": "Flexible Sigmoidoscopy Performed", + "use": "out", + "min": 0, + "max": "*", + "type": "Procedure" + }, + { + "name": "CT Colonography Performed", + "use": "out", + "min": 0, + "max": "*", + "type": "Procedure" + }, + { + "name": "Total Colectomy Performed", + "use": "out", + "min": 0, + "max": "*", + "type": "Procedure" + }, + { + "name": "Fecal Occult Blood Test Performed", + "use": "out", + "min": 0, + "max": "*", + "type": "Observation" + }, + { + "name": "Fecal Immunochemical Test DNA", + "use": "out", + "min": 0, + "max": "*", + "type": "Observation" + }, + { + "name": "Colonoscopy Performed", + "use": "out", + "min": 0, + "max": "*", + "type": "Procedure" + }, + { + "name": "Numerator", + "use": "out", + "min": 0, + "max": "1", + "type": "boolean" + }, + { + "name": "Malignant Neoplasm", + "use": "out", + "min": 0, + "max": "*", + "type": "Condition" + }, + { + "name": "Denominator Exclusion", + "use": "out", + "min": 0, + "max": "1", + "type": "boolean" + } + ], + "dataRequirement": [ + { + "type": "Patient", + "profile": [ + "http://hl7.org/fhir/StructureDefinition/Patient" + ] + }, + { + "type": "Patient", + "profile": [ + "http://hl7.org/fhir/StructureDefinition/Patient" + ], + "mustSupport": [ + "extension", + "value", + "url" + ], + "codeFilter": [ + { + "path": "url", + "code": [ + { + "code": "http://hl7.org/fhir/us/core/StructureDefinition/us-core-ethnicity" + } + ] + } + ] + }, + { + "type": "Patient", + "profile": [ + "http://hl7.org/fhir/StructureDefinition/Patient" + ], + "mustSupport": [ + "extension", + "value", + "url" + ], + "codeFilter": [ + { + "path": "url", + "code": [ + { + "code": "http://hl7.org/fhir/us/core/StructureDefinition/us-core-race" + } + ] + } + ] + }, + { + "type": "Procedure", + "profile": [ + "http://hl7.org/fhir/StructureDefinition/Procedure" + ], + "mustSupport": [ + "code", + "performed", + "status" + ], + "codeFilter": [ + { + "path": "code", + "valueSet": "http://cts.nlm.nih.gov/fhir/ValueSet/2.16.840.1.113883.3.464.1003.198.12.1010" + }, + { + "path": "status", + "code": [ + { + "code": "completed" + } + ] + } + ] + }, + { + "type": "Procedure", + "profile": [ + "http://hl7.org/fhir/StructureDefinition/Procedure" + ], + "mustSupport": [ + "code", + "performed", + "status" + ], + "codeFilter": [ + { + "path": "code", + "valueSet": "http://cts.nlm.nih.gov/fhir/ValueSet/2.16.840.1.113883.3.464.1003.108.12.1038" + }, + { + "path": "status", + "code": [ + { + "code": "completed" + } + ] + } + ] + }, + { + "type": "Procedure", + "profile": [ + "http://hl7.org/fhir/StructureDefinition/Procedure" + ], + "mustSupport": [ + "code", + "performed", + "status" + ], + "codeFilter": [ + { + "path": "code", + "valueSet": "http://cts.nlm.nih.gov/fhir/ValueSet/2.16.840.1.113883.3.464.1003.198.12.1019" + }, + { + "path": "status", + "code": [ + { + "code": "completed" + } + ] + } + ] + }, + { + "type": "Procedure", + "profile": [ + "http://hl7.org/fhir/StructureDefinition/Procedure" + ], + "mustSupport": [ + "code", + "performed", + "status" + ], + "codeFilter": [ + { + "path": "code", + "valueSet": "http://cts.nlm.nih.gov/fhir/ValueSet/2.16.840.1.113883.3.464.1003.108.12.1020" + }, + { + "path": "status", + "code": [ + { + "code": "completed" + } + ] + } + ] + }, + { + "type": "Procedure", + "profile": [ + "http://hl7.org/fhir/StructureDefinition/Procedure" + ], + "mustSupport": [ + "code", + "performed", + "status" + ], + "codeFilter": [ + { + "path": "code", + "valueSet": "http://cts.nlm.nih.gov/fhir/ValueSet/2.16.840.1.113762.1.4.1108.15" + }, + { + "path": "status", + "code": [ + { + "code": "completed" + } + ] + }, + { + "path": "status", + "code": [ + { + "code": "completed" + } + ] + }, + { + "path": "status", + "code": [ + { + "code": "completed" + } + ] + }, + { + "path": "status", + "code": [ + { + "code": "completed" + } + ] + } + ] + }, + { + "type": "Observation", + "profile": [ + "http://hl7.org/fhir/StructureDefinition/Observation" + ], + "mustSupport": [ + "effective", + "code", + "value", + "status" + ], + "codeFilter": [ + { + "path": "code", + "valueSet": "http://cts.nlm.nih.gov/fhir/ValueSet/2.16.840.1.113883.3.464.1003.198.12.1011" + } + ] + }, + { + "type": "Observation", + "profile": [ + "http://hl7.org/fhir/StructureDefinition/Observation" + ], + "mustSupport": [ + "effective", + "code", + "value", + "status" + ], + "codeFilter": [ + { + "path": "code", + "valueSet": "http://cts.nlm.nih.gov/fhir/ValueSet/2.16.840.1.113883.3.464.1003.108.12.1039" + } + ] + }, + { + "type": "Observation", + "profile": [ + "http://hl7.org/fhir/StructureDefinition/Observation" + ], + "mustSupport": [ + "effective", + "code" + ], + "codeFilter": [ + { + "path": "code", + "valueSet": "http://cts.nlm.nih.gov/fhir/ValueSet/2.16.840.1.113883.3.464.1003.113.12.1075" + } + ] + }, + { + "type": "Condition", + "profile": [ + "http://hl7.org/fhir/StructureDefinition/Condition" + ], + "mustSupport": [ + "code" + ], + "codeFilter": [ + { + "path": "code", + "valueSet": "http://cts.nlm.nih.gov/fhir/ValueSet/2.16.840.1.113883.3.464.1003.108.12.1001" + } + ] + }, + { + "type": "Condition", + "profile": [ + "http://hl7.org/fhir/StructureDefinition/Condition" + ], + "mustSupport": [ + "code" + ], + "codeFilter": [ + { + "path": "code", + "valueSet": "http://cts.nlm.nih.gov/fhir/ValueSet/2.16.840.1.113883.3.464.1003.113.12.1074" + } + ] + }, + { + "type": "Encounter", + "profile": [ + "http://hl7.org/fhir/StructureDefinition/Encounter" + ], + "mustSupport": [ + "period", + "hospitalization", + "hospitalization.dischargeDisposition", + "type", + "status" + ], + "codeFilter": [ + { + "path": "type", + "valueSet": "http://cts.nlm.nih.gov/fhir/ValueSet/2.16.840.1.113883.3.666.5.307" + } + ], + "dateFilter": [ + { + "path": "period", + "_valueDateTime": { + "extension": [ + { + "url": "http://hl7.org/fhir/StructureDefinition/cqf-expression", + "valueExpression": { + "language": "text/cql-identifier", + "expression": "Measurement Period" + } + } + ] + } + }, + { + "path": "period", + "_valueDateTime": { + "extension": [ + { + "url": "http://hl7.org/fhir/StructureDefinition/cqf-expression", + "valueExpression": { + "language": "text/cql-identifier", + "expression": "Measurement Period" + } + } + ] + } + }, + { + "path": "period", + "_valueDateTime": { + "extension": [ + { + "url": "http://hl7.org/fhir/StructureDefinition/cqf-expression", + "valueExpression": { + "language": "text/cql-identifier", + "expression": "Measurement Period" + } + } + ] + } + }, + { + "path": "period", + "_valueDateTime": { + "extension": [ + { + "url": "http://hl7.org/fhir/StructureDefinition/cqf-expression", + "valueExpression": { + "language": "text/cql-identifier", + "expression": "Measurement Period" + } + } + ] + } + } + ] + }, + { + "type": "Encounter", + "profile": [ + "http://hl7.org/fhir/StructureDefinition/Encounter" + ], + "mustSupport": [ + "period", + "type" + ], + "codeFilter": [ + { + "path": "type", + "valueSet": "http://cts.nlm.nih.gov/fhir/ValueSet/2.16.840.1.113883.3.464.1003.101.12.1088" + } + ] + }, + { + "type": "ServiceRequest", + "profile": [ + "http://hl7.org/fhir/StructureDefinition/ServiceRequest" + ], + "mustSupport": [ + "code", + "authoredOn", + "intent" + ], + "codeFilter": [ + { + "path": "code", + "valueSet": "http://cts.nlm.nih.gov/fhir/ValueSet/2.16.840.1.113762.1.4.1108.15" + }, + { + "path": "intent", + "code": [ + { + "code": "order" + } + ] + }, + { + "path": "intent", + "code": [ + { + "code": "order" + } + ] + }, + { + "path": "intent", + "code": [ + { + "code": "order" + } + ] + }, + { + "path": "intent", + "code": [ + { + "code": "order" + } + ] + } + ], + "dateFilter": [ + { + "path": "authoredOn", + "_valueDateTime": { + "extension": [ + { + "url": "http://hl7.org/fhir/StructureDefinition/cqf-expression", + "valueExpression": { + "language": "text/cql-identifier", + "expression": "Measurement Period" + } + } + ] + } + }, + { + "path": "authoredOn", + "_valueDateTime": { + "extension": [ + { + "url": "http://hl7.org/fhir/StructureDefinition/cqf-expression", + "valueExpression": { + "language": "text/cql-identifier", + "expression": "Measurement Period" + } + } + ] + } + }, + { + "path": "authoredOn", + "_valueDateTime": { + "extension": [ + { + "url": "http://hl7.org/fhir/StructureDefinition/cqf-expression", + "valueExpression": { + "language": "text/cql-identifier", + "expression": "Measurement Period" + } + } + ] + } + }, + { + "path": "authoredOn", + "_valueDateTime": { + "extension": [ + { + "url": "http://hl7.org/fhir/StructureDefinition/cqf-expression", + "valueExpression": { + "language": "text/cql-identifier", + "expression": "Measurement Period" + } + } + ] + } + } + ] + }, + { + "type": "DeviceRequest", + "profile": [ + "http://hl7.org/fhir/StructureDefinition/DeviceRequest" + ], + "mustSupport": [ + "code", + "authoredOn" + ], + "codeFilter": [ + { + "path": "code", + "valueSet": "http://cts.nlm.nih.gov/fhir/ValueSet/2.16.840.1.113883.3.464.1003.118.12.1300" + } + ], + "dateFilter": [ + { + "path": "authoredOn", + "_valueDateTime": { + "extension": [ + { + "url": "http://hl7.org/fhir/StructureDefinition/cqf-expression", + "valueExpression": { + "language": "text/cql-identifier", + "expression": "Measurement Period" + } + } + ] + } + }, + { + "path": "authoredOn", + "_valueDateTime": { + "extension": [ + { + "url": "http://hl7.org/fhir/StructureDefinition/cqf-expression", + "valueExpression": { + "language": "text/cql-identifier", + "expression": "Measurement Period" + } + } + ] + } + }, + { + "path": "authoredOn", + "_valueDateTime": { + "extension": [ + { + "url": "http://hl7.org/fhir/StructureDefinition/cqf-expression", + "valueExpression": { + "language": "text/cql-identifier", + "expression": "Measurement Period" + } + } + ] + } + } + ] + } + ], + "content": [ + { + "contentType": "text/cql", + "data": "bGlicmFyeSBDb2xvcmVjdGFsQ2FuY2VyU2NyZWVuaW5nc0ZISVIgdmVyc2lvbiAnMC4wLjAwMScNCg0KdXNpbmcgRkhJUiB2ZXJzaW9uICc0LjAuMScNCg0KaW5jbHVkZSBGSElSSGVscGVycyB2ZXJzaW9uICc0LjAuMDAxJyBjYWxsZWQgRkhJUkhlbHBlcnMNCmluY2x1ZGUgU3VwcGxlbWVudGFsRGF0YUVsZW1lbnRzRkhJUjQgdmVyc2lvbiAnMi4wLjAwMCcgY2FsbGVkIFNERQ0KaW5jbHVkZSBNQVRHbG9iYWxDb21tb25GdW5jdGlvbnNGSElSNCB2ZXJzaW9uICc2LjAuMDAwJyBjYWxsZWQgR2xvYmFsDQppbmNsdWRlIEFkdWx0T3V0cGF0aWVudEVuY291bnRlcnNGSElSNCB2ZXJzaW9uICcyLjAuMDAwJyBjYWxsZWQgQWR1bHRPdXRwYXRpZW50RW5jb3VudGVycw0KaW5jbHVkZSBIb3NwaWNlRkhJUjQgdmVyc2lvbiAnMi4wLjAwMCcgY2FsbGVkIEhvc3BpY2UNCmluY2x1ZGUgQWR2YW5jZWRJbGxuZXNzYW5kRnJhaWx0eUV4Y2x1c2lvbkVDUU1GSElSNCB2ZXJzaW9uICc1LjEyLjAwMCcgY2FsbGVkIEZyYWlsdHkNCg0KY29kZXN5c3RlbSAiTE9JTkMiOiAnaHR0cDovL2xvaW5jLm9yZycNCg0KdmFsdWVzZXQgIkFjdXRlIElucGF0aWVudCI6ICdodHRwOi8vY3RzLm5sbS5uaWguZ292L2ZoaXIvVmFsdWVTZXQvMi4xNi44NDAuMS4xMTM4ODMuMy40NjQuMTAwMy4xMDEuMTIuMTA4MycNCnZhbHVlc2V0ICJBZHZhbmNlZCBJbGxuZXNzIjogJ2h0dHA6Ly9jdHMubmxtLm5paC5nb3YvZmhpci9WYWx1ZVNldC8yLjE2Ljg0MC4xLjExMzg4My4zLjQ2NC4xMDAzLjExMC4xMi4xMDgyJw0KdmFsdWVzZXQgIkFubnVhbCBXZWxsbmVzcyBWaXNpdCI6ICdodHRwOi8vY3RzLm5sbS5uaWguZ292L2ZoaXIvVmFsdWVTZXQvMi4xNi44NDAuMS4xMTM4ODMuMy41MjYuMy4xMjQwJw0KdmFsdWVzZXQgIkNhcmUgU2VydmljZXMgaW4gTG9uZy1UZXJtIFJlc2lkZW50aWFsIEZhY2lsaXR5IjogJ2h0dHA6Ly9jdHMubmxtLm5paC5nb3YvZmhpci9WYWx1ZVNldC8yLjE2Ljg0MC4xLjExMzg4My4zLjQ2NC4xMDAzLjEwMS4xMi4xMDE0Jw0KdmFsdWVzZXQgIkNvbG9ub3Njb3B5IjogJ2h0dHA6Ly9jdHMubmxtLm5paC5nb3YvZmhpci9WYWx1ZVNldC8yLjE2Ljg0MC4xLjExMzg4My4zLjQ2NC4xMDAzLjEwOC4xMi4xMDIwJw0KdmFsdWVzZXQgIkNUIENvbG9ub2dyYXBoeSI6ICdodHRwOi8vY3RzLm5sbS5uaWguZ292L2ZoaXIvVmFsdWVTZXQvMi4xNi44NDAuMS4xMTM4ODMuMy40NjQuMTAwMy4xMDguMTIuMTAzOCcNCnZhbHVlc2V0ICJEZW1lbnRpYSBNZWRpY2F0aW9ucyI6ICdodHRwOi8vY3RzLm5sbS5uaWguZ292L2ZoaXIvVmFsdWVTZXQvMi4xNi44NDAuMS4xMTM4ODMuMy40NjQuMTAwMy4xOTYuMTIuMTUxMCcNCnZhbHVlc2V0ICJEaXNjaGFyZ2VkIHRvIEhlYWx0aCBDYXJlIEZhY2lsaXR5IGZvciBIb3NwaWNlIENhcmUiOiAnaHR0cDovL2N0cy5ubG0ubmloLmdvdi9maGlyL1ZhbHVlU2V0LzIuMTYuODQwLjEuMTEzODgzLjMuMTE3LjEuNy4xLjIwNycNCnZhbHVlc2V0ICJEaXNjaGFyZ2VkIHRvIEhvbWUgZm9yIEhvc3BpY2UgQ2FyZSI6ICdodHRwOi8vY3RzLm5sbS5uaWguZ292L2ZoaXIvVmFsdWVTZXQvMi4xNi44NDAuMS4xMTM4ODMuMy4xMTcuMS43LjEuMjA5Jw0KdmFsdWVzZXQgIkVEIjogJ2h0dHA6Ly9jdHMubmxtLm5paC5nb3YvZmhpci9WYWx1ZVNldC8yLjE2Ljg0MC4xLjExMzg4My4zLjQ2NC4xMDAzLjEwMS4xMi4xMDg1Jw0KdmFsdWVzZXQgIkVuY291bnRlciBJbnBhdGllbnQiOiAnaHR0cDovL2N0cy5ubG0ubmloLmdvdi9maGlyL1ZhbHVlU2V0LzIuMTYuODQwLjEuMTEzODgzLjMuNjY2LjUuMzA3Jw0KdmFsdWVzZXQgIkZlY2FsIE9jY3VsdCBCbG9vZCBUZXN0IChGT0JUKSI6ICdodHRwOi8vY3RzLm5sbS5uaWguZ292L2ZoaXIvVmFsdWVTZXQvMi4xNi44NDAuMS4xMTM4ODMuMy40NjQuMTAwMy4xOTguMTIuMTAxMScNCnZhbHVlc2V0ICJGSVQgRE5BIjogJ2h0dHA6Ly9jdHMubmxtLm5paC5nb3YvZmhpci9WYWx1ZVNldC8yLjE2Ljg0MC4xLjExMzg4My4zLjQ2NC4xMDAzLjEwOC4xMi4xMDM5Jw0KdmFsdWVzZXQgIkZsZXhpYmxlIFNpZ21vaWRvc2NvcHkiOiAnaHR0cDovL2N0cy5ubG0ubmloLmdvdi9maGlyL1ZhbHVlU2V0LzIuMTYuODQwLjEuMTEzODgzLjMuNDY0LjEwMDMuMTk4LjEyLjEwMTAnDQp2YWx1ZXNldCAiRnJhaWx0eSBEZXZpY2UiOiAnaHR0cDovL2N0cy5ubG0ubmloLmdvdi9maGlyL1ZhbHVlU2V0LzIuMTYuODQwLjEuMTEzODgzLjMuNDY0LjEwMDMuMTE4LjEyLjEzMDAnDQp2YWx1ZXNldCAiRnJhaWx0eSBEaWFnbm9zaXMiOiAnaHR0cDovL2N0cy5ubG0ubmloLmdvdi9maGlyL1ZhbHVlU2V0LzIuMTYuODQwLjEuMTEzODgzLjMuNDY0LjEwMDMuMTEzLjEyLjEwNzQnDQp2YWx1ZXNldCAiRnJhaWx0eSBFbmNvdW50ZXIiOiAnaHR0cDovL2N0cy5ubG0ubmloLmdvdi9maGlyL1ZhbHVlU2V0LzIuMTYuODQwLjEuMTEzODgzLjMuNDY0LjEwMDMuMTAxLjEyLjEwODgnDQp2YWx1ZXNldCAiRnJhaWx0eSBTeW1wdG9tIjogJ2h0dHA6Ly9jdHMubmxtLm5paC5nb3YvZmhpci9WYWx1ZVNldC8yLjE2Ljg0MC4xLjExMzg4My4zLjQ2NC4xMDAzLjExMy4xMi4xMDc1Jw0KdmFsdWVzZXQgIkhvbWUgSGVhbHRoY2FyZSBTZXJ2aWNlcyI6ICdodHRwOi8vY3RzLm5sbS5uaWguZ292L2ZoaXIvVmFsdWVTZXQvMi4xNi44NDAuMS4xMTM4ODMuMy40NjQuMTAwMy4xMDEuMTIuMTAxNicNCnZhbHVlc2V0ICJIb3NwaWNlIGNhcmUgYW1idWxhdG9yeSI6ICdodHRwOi8vY3RzLm5sbS5uaWguZ292L2ZoaXIvVmFsdWVTZXQvMi4xNi44NDAuMS4xMTM3NjIuMS40LjExMDguMTUnDQp2YWx1ZXNldCAiTWFsaWduYW50IE5lb3BsYXNtIG9mIENvbG9uIjogJ2h0dHA6Ly9jdHMubmxtLm5paC5nb3YvZmhpci9WYWx1ZVNldC8yLjE2Ljg0MC4xLjExMzg4My4zLjQ2NC4xMDAzLjEwOC4xMi4xMDAxJw0KdmFsdWVzZXQgIk5vbmFjdXRlIElucGF0aWVudCI6ICdodHRwOi8vY3RzLm5sbS5uaWguZ292L2ZoaXIvVmFsdWVTZXQvMi4xNi44NDAuMS4xMTM4ODMuMy40NjQuMTAwMy4xMDEuMTIuMTA4NCcNCnZhbHVlc2V0ICJOdXJzaW5nIEZhY2lsaXR5IFZpc2l0IjogJ2h0dHA6Ly9jdHMubmxtLm5paC5nb3YvZmhpci9WYWx1ZVNldC8yLjE2Ljg0MC4xLjExMzg4My4zLjQ2NC4xMDAzLjEwMS4xMi4xMDEyJw0KdmFsdWVzZXQgIk9ic2VydmF0aW9uIjogJ2h0dHA6Ly9jdHMubmxtLm5paC5nb3YvZmhpci9WYWx1ZVNldC8yLjE2Ljg0MC4xLjExMzg4My4zLjQ2NC4xMDAzLjEwMS4xMi4xMDg2Jw0KdmFsdWVzZXQgIk9mZmljZSBWaXNpdCI6ICdodHRwOi8vY3RzLm5sbS5uaWguZ292L2ZoaXIvVmFsdWVTZXQvMi4xNi44NDAuMS4xMTM4ODMuMy40NjQuMTAwMy4xMDEuMTIuMTAwMScNCnZhbHVlc2V0ICJPdXRwYXRpZW50IjogJ2h0dHA6Ly9jdHMubmxtLm5paC5nb3YvZmhpci9WYWx1ZVNldC8yLjE2Ljg0MC4xLjExMzg4My4zLjQ2NC4xMDAzLjEwMS4xMi4xMDg3Jw0KdmFsdWVzZXQgIlByZXZlbnRpdmUgQ2FyZSBTZXJ2aWNlcyAtIEVzdGFibGlzaGVkIE9mZmljZSBWaXNpdCwgMTggYW5kIFVwIjogJ2h0dHA6Ly9jdHMubmxtLm5paC5nb3YvZmhpci9WYWx1ZVNldC8yLjE2Ljg0MC4xLjExMzg4My4zLjQ2NC4xMDAzLjEwMS4xMi4xMDI1Jw0KdmFsdWVzZXQgIlByZXZlbnRpdmUgQ2FyZSBTZXJ2aWNlcy1Jbml0aWFsIE9mZmljZSBWaXNpdCwgMTggYW5kIFVwIjogJ2h0dHA6Ly9jdHMubmxtLm5paC5nb3YvZmhpci9WYWx1ZVNldC8yLjE2Ljg0MC4xLjExMzg4My4zLjQ2NC4xMDAzLjEwMS4xMi4xMDIzJw0KdmFsdWVzZXQgIlRvdGFsIENvbGVjdG9teSI6ICdodHRwOi8vY3RzLm5sbS5uaWguZ292L2ZoaXIvVmFsdWVTZXQvMi4xNi44NDAuMS4xMTM4ODMuMy40NjQuMTAwMy4xOTguMTIuMTAxOScNCg0KY29kZSAiQmlydGggZGF0ZSI6ICcyMTExMi04JyBmcm9tICJMT0lOQyIgZGlzcGxheSAnQmlydGggZGF0ZScNCg0KcGFyYW1ldGVyICJNZWFzdXJlbWVudCBQZXJpb2QiIEludGVydmFsPERhdGVUaW1lPg0KICBkZWZhdWx0IEludGVydmFsW0AyMDE5LTAxLTAxVDAwOjAwOjAwLjAsIEAyMDIwLTAxLTAxVDAwOjAwOjAwLjApDQoNCmNvbnRleHQgUGF0aWVudA0KDQpkZWZpbmUgIlNERSBFdGhuaWNpdHkiOg0KICBTREUuIlNERSBFdGhuaWNpdHkiDQoNCmRlZmluZSAiU0RFIFBheWVyIjoNCiAgU0RFLiJTREUgUGF5ZXIiDQoNCmRlZmluZSAiU0RFIFJhY2UiOg0KICBTREUuIlNERSBSYWNlIg0KDQpkZWZpbmUgIlNERSBTZXgiOg0KICBTREUuIlNERSBTZXgiDQoNCmRlZmluZSAiSW5pdGlhbCBQb3B1bGF0aW9uIjoNCiAgQWdlSW5ZZWFyc0F0KHN0YXJ0IG9mICJNZWFzdXJlbWVudCBQZXJpb2QiKSBiZXR3ZWVuIDUwIGFuZCA3NQ0KCSBhbmQgZXhpc3RzIEFkdWx0T3V0cGF0aWVudEVuY291bnRlcnMuIlF1YWxpZnlpbmcgRW5jb3VudGVycyINCg0KZGVmaW5lICJEZW5vbWluYXRvciI6DQogICJJbml0aWFsIFBvcHVsYXRpb24iDQoNCmRlZmluZSAiRmxleGlibGUgU2lnbW9pZG9zY29weSBQZXJmb3JtZWQiOg0KICBbUHJvY2VkdXJlOiAiRmxleGlibGUgU2lnbW9pZG9zY29weSJdIEZsZXhpYmxlU2lnbW9pZG9zY29weQ0KICAgICAgICAgICAgICAgICAgICAJCXdoZXJlIEZsZXhpYmxlU2lnbW9pZG9zY29weS5zdGF0dXMgPSAnY29tcGxldGVkJw0KICAgICAgICAgICAgICAgICAgICAJCQlhbmQgR2xvYmFsLiJOb3JtYWxpemUgSW50ZXJ2YWwiKEZsZXhpYmxlU2lnbW9pZG9zY29weS5wZXJmb3JtZWQpIGVuZHMgNSB5ZWFycyBvciBsZXNzIG9uIG9yIGJlZm9yZSBlbmQgb2YgIk1lYXN1cmVtZW50IFBlcmlvZCINCg0KZGVmaW5lICJDVCBDb2xvbm9ncmFwaHkgUGVyZm9ybWVkIjoNCiAgW1Byb2NlZHVyZTogIkNUIENvbG9ub2dyYXBoeSJdIENvbG9ub2dyYXBoeQ0KICAgICAgICAgICAgICAgICAgICAJCXdoZXJlIENvbG9ub2dyYXBoeS5zdGF0dXMgPSAnY29tcGxldGVkJw0KICAgICAgICAgICAgICAgICAgICAJCQlhbmQgR2xvYmFsLiJOb3JtYWxpemUgSW50ZXJ2YWwiKENvbG9ub2dyYXBoeS5wZXJmb3JtZWQpIGVuZHMgNSB5ZWFycyBvciBsZXNzIG9uIG9yIGJlZm9yZSBlbmQgb2YgIk1lYXN1cmVtZW50IFBlcmlvZCINCg0KZGVmaW5lICJUb3RhbCBDb2xlY3RvbXkgUGVyZm9ybWVkIjoNCiAgW1Byb2NlZHVyZTogIlRvdGFsIENvbGVjdG9teSJdIENvbGVjdG9teQ0KICAgICAgICAgICAgICAgICAgICAJCXdoZXJlIENvbGVjdG9teS5zdGF0dXMgPSAnY29tcGxldGVkJw0KICAgICAgICAgICAgICAgICAgICAJCQlhbmQgR2xvYmFsLiJOb3JtYWxpemUgSW50ZXJ2YWwiKENvbGVjdG9teS5wZXJmb3JtZWQpIHN0YXJ0cyBvbiBvciBiZWZvcmUgZW5kIG9mICJNZWFzdXJlbWVudCBQZXJpb2QiDQoNCmRlZmluZSAiRmVjYWwgT2NjdWx0IEJsb29kIFRlc3QgUGVyZm9ybWVkIjoNCiAgW09ic2VydmF0aW9uOiAiRmVjYWwgT2NjdWx0IEJsb29kIFRlc3QgKEZPQlQpIl0gRmVjYWxPY2N1bHRSZXN1bHQNCiAgICAgICAgICAgICAgICAgICAgCQl3aGVyZSBGZWNhbE9jY3VsdFJlc3VsdC5zdGF0dXMgaW4geydmaW5hbCcsICdhbWVuZGVkJywgJ2NvcnJlY3RlZCcsICdwcmVsaW1pbmFyeSd9DQogICAgICAgICAgICAgICAgICAgIAkJCWFuZCBGZWNhbE9jY3VsdFJlc3VsdC52YWx1ZSBpcyBub3QgbnVsbA0KICAgICAgICAgICAgICAgICAgICAJCQlhbmQgR2xvYmFsLiJOb3JtYWxpemUgSW50ZXJ2YWwiKEZlY2FsT2NjdWx0UmVzdWx0LmVmZmVjdGl2ZSkgZHVyaW5nIGRheSBvZiAiTWVhc3VyZW1lbnQgUGVyaW9kIg0KDQpkZWZpbmUgIkZlY2FsIEltbXVub2NoZW1pY2FsIFRlc3QgRE5BIjoNCiAgW09ic2VydmF0aW9uOiAiRklUIEROQSJdIEZpdEROQQ0KICAgICAgICAgICAgICAgICAgICAJCXdoZXJlIEZpdEROQS5zdGF0dXMgaW4geydmaW5hbCcsICdhbWVuZGVkJywgJ2NvcnJlY3RlZCcsICdwcmVsaW1pbmFyeSd9DQogICAgICAgICAgICAgICAgICAgIAkJCWFuZCBGaXRETkEudmFsdWUgaXMgbm90IG51bGwNCiAgICAgICAgICAgICAgICAgICAgCQkJYW5kIEdsb2JhbC4iTm9ybWFsaXplIEludGVydmFsIihGaXRETkEuZWZmZWN0aXZlKSBvY2N1cnMgMyB5ZWFycyBvciBsZXNzIG9uIG9yIGJlZm9yZSBkYXkgb2YgZW5kIG9mICJNZWFzdXJlbWVudCBQZXJpb2QiDQoNCmRlZmluZSAiQ29sb25vc2NvcHkgUGVyZm9ybWVkIjoNCiAgW1Byb2NlZHVyZTogIkNvbG9ub3Njb3B5Il0gQ29sb25vc2NvcHkNCiAgICAgICAgICAgICAgICAgICAgCQl3aGVyZSBDb2xvbm9zY29weS5zdGF0dXMgPSAnY29tcGxldGVkJw0KICAgICAgICAgICAgICAgICAgICAJCSAgYW5kIEdsb2JhbC4iTm9ybWFsaXplIEludGVydmFsIihDb2xvbm9zY29weS5wZXJmb3JtZWQpIGVuZHMgMTAgeWVhcnMgb3IgbGVzcyBvbiBvciBiZWZvcmUgZW5kIG9mICJNZWFzdXJlbWVudCBQZXJpb2QiDQoNCmRlZmluZSAiTnVtZXJhdG9yIjoNCiAgZXhpc3RzICJDb2xvbm9zY29weSBQZXJmb3JtZWQiDQogICAgICAgICAgICAgICAgICAgIAkJb3IgZXhpc3RzICJGZWNhbCBPY2N1bHQgQmxvb2QgVGVzdCBQZXJmb3JtZWQiDQogICAgICAgICAgICAgICAgICAgIAkJb3IgZXhpc3RzICJGbGV4aWJsZSBTaWdtb2lkb3Njb3B5IFBlcmZvcm1lZCINCiAgICAgICAgICAgICAgICAgICAgCQlvciBleGlzdHMgIkZlY2FsIEltbXVub2NoZW1pY2FsIFRlc3QgRE5BIg0KICAgICAgICAgICAgICAgICAgICAJCW9yIGV4aXN0cyAiQ1QgQ29sb25vZ3JhcGh5IFBlcmZvcm1lZCINCg0KZGVmaW5lICJNYWxpZ25hbnQgTmVvcGxhc20iOg0KICBbQ29uZGl0aW9uOiAiTWFsaWduYW50IE5lb3BsYXNtIG9mIENvbG9uIl0gQ29sb3JlY3RhbENhbmNlcg0KICAgICAgICAgICAgICAgICAgICAJCXdoZXJlIEdsb2JhbC4iUHJldmFsZW5jZSBQZXJpb2QiKENvbG9yZWN0YWxDYW5jZXIpIHN0YXJ0cyBvbiBvciBiZWZvcmUgZW5kIG9mICJNZWFzdXJlbWVudCBQZXJpb2QiDQoNCmRlZmluZSAiRGVub21pbmF0b3IgRXhjbHVzaW9uIjoNCiAgSG9zcGljZS4iSGFzIEhvc3BpY2UiDQogICAgICAgICAgICAgICAgICAgIAkJb3IgZXhpc3RzICJNYWxpZ25hbnQgTmVvcGxhc20iDQogICAgICAgICAgICAgICAgICAgIAkJb3IgZXhpc3RzICJUb3RhbCBDb2xlY3RvbXkgUGVyZm9ybWVkIg0KICAgICAgICAgICAgICAgICAgICAJCW9yIEZyYWlsdHkuICJBZHZhbmNlZCBJbGxuZXNzIGFuZCBGcmFpbHR5IEV4Y2x1c2lvbiBOb3QgSW5jbHVkaW5nIE92ZXIgQWdlIDgwIg0KICAgICAgICAgICAgICAgICAgICAJCWFuZCBGcmFpbHR5LiJMb25nIFRlcm0gQ2FyZSBQZXJpb2RzIExvbmdlciBUaGFuIDkwIENvbnNlY3V0aXZlIERheXMiDQo=" + }, + { + "contentType": "application/elm+xml", + "data": "PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0iVVRGLTgiPz4NCjxsaWJyYXJ5IHhtbG5zPSJ1cm46aGw3LW9yZzplbG06cjEiIHhtbG5zOnQ9InVybjpobDctb3JnOmVsbS10eXBlczpyMSIgeG1sbnM6eHNpPSJodHRwOi8vd3d3LnczLm9yZy8yMDAxL1hNTFNjaGVtYS1pbnN0YW5jZSIgeG1sbnM6eHNkPSJodHRwOi8vd3d3LnczLm9yZy8yMDAxL1hNTFNjaGVtYSIgeG1sbnM6Zmhpcj0iaHR0cDovL2hsNy5vcmcvZmhpciIgeG1sbnM6cWRtNDM9InVybjpoZWFsdGhpdC1nb3Y6cWRtOnY0XzMiIHhtbG5zOnFkbTUzPSJ1cm46aGVhbHRoaXQtZ292OnFkbTp2NV8zIiB4bWxuczphPSJ1cm46aGw3LW9yZzpjcWwtYW5ub3RhdGlvbnM6cjEiPg0KICAgPGFubm90YXRpb24gdHJhbnNsYXRvclZlcnNpb249IjEuMyIgdHJhbnNsYXRvck9wdGlvbnM9IkVuYWJsZUFubm90YXRpb25zLEVuYWJsZUxvY2F0b3JzLERpc2FibGVMaXN0RGVtb3Rpb24sRGlzYWJsZUxpc3RQcm9tb3Rpb24iIHhzaTp0eXBlPSJhOkNxbFRvRWxtSW5mbyIvPg0KICAgPGFubm90YXRpb24geHNpOnR5cGU9ImE6QW5ub3RhdGlvbiI+DQogICAgICA8YTpzIHI9IjIyNSI+DQogICAgICAgICA8YTpzPmxpYnJhcnkgQ29sb3JlY3RhbENhbmNlclNjcmVlbmluZ3NGSElSIHZlcnNpb24gJzAuMC4wMDEnPC9hOnM+DQogICAgICA8L2E6cz4NCiAgIDwvYW5ub3RhdGlvbj4NCiAgIDxpZGVudGlmaWVyIGlkPSJDb2xvcmVjdGFsQ2FuY2VyU2NyZWVuaW5nc0ZISVIiIHN5c3RlbT0iaHR0cDovL2VjcWkuaGVhbHRoaXQuZ292L2VjcW1zIiB2ZXJzaW9uPSIwLjAuMDAxIi8+DQogICA8c2NoZW1hSWRlbnRpZmllciBpZD0idXJuOmhsNy1vcmc6ZWxtIiB2ZXJzaW9uPSJyMSIvPg0KICAgPHVzaW5ncz4NCiAgICAgIDxkZWYgbG9jYWxJZGVudGlmaWVyPSJTeXN0ZW0iIHVyaT0idXJuOmhsNy1vcmc6ZWxtLXR5cGVzOnIxIi8+DQogICAgICA8ZGVmIGxvY2FsSWQ9IjEiIGxvY2F0b3I9IjM6MS0zOjI2IiBsb2NhbElkZW50aWZpZXI9IkZISVIiIHVyaT0iaHR0cDovL2hsNy5vcmcvZmhpciIgdmVyc2lvbj0iNC4wLjEiPg0KICAgICAgICAgPGFubm90YXRpb24geHNpOnR5cGU9ImE6QW5ub3RhdGlvbiI+DQogICAgICAgICAgICA8YTpzIHI9IjEiPg0KICAgICAgICAgICAgICAgPGE6cz51c2luZyA8L2E6cz4NCiAgICAgICAgICAgICAgIDxhOnM+DQogICAgICAgICAgICAgICAgICA8YTpzPkZISVI8L2E6cz4NCiAgICAgICAgICAgICAgIDwvYTpzPg0KICAgICAgICAgICAgICAgPGE6cz4gdmVyc2lvbiAnNC4wLjEnPC9hOnM+DQogICAgICAgICAgICA8L2E6cz4NCiAgICAgICAgIDwvYW5ub3RhdGlvbj4NCiAgICAgIDwvZGVmPg0KICAgPC91c2luZ3M+DQogICA8aW5jbHVkZXM+DQogICAgICA8ZGVmIGxvY2FsSWQ9IjIiIGxvY2F0b3I9IjU6MS01OjU2IiBsb2NhbElkZW50aWZpZXI9IkZISVJIZWxwZXJzIiBwYXRoPSJodHRwOi8vZWNxaS5oZWFsdGhpdC5nb3YvZWNxbXMvRkhJUkhlbHBlcnMiIHZlcnNpb249IjQuMC4wMDEiPg0KICAgICAgICAgPGFubm90YXRpb24geHNpOnR5cGU9ImE6QW5ub3RhdGlvbiI+DQogICAgICAgICAgICA8YTpzIHI9IjIiPg0KICAgICAgICAgICAgICAgPGE6cz5pbmNsdWRlIDwvYTpzPg0KICAgICAgICAgICAgICAgPGE6cz4NCiAgICAgICAgICAgICAgICAgIDxhOnM+RkhJUkhlbHBlcnM8L2E6cz4NCiAgICAgICAgICAgICAgIDwvYTpzPg0KICAgICAgICAgICAgICAgPGE6cz4gdmVyc2lvbiAnNC4wLjAwMScgY2FsbGVkIEZISVJIZWxwZXJzPC9hOnM+DQogICAgICAgICAgICA8L2E6cz4NCiAgICAgICAgIDwvYW5ub3RhdGlvbj4NCiAgICAgIDwvZGVmPg0KICAgICAgPGRlZiBsb2NhbElkPSIzIiBsb2NhdG9yPSI2OjEtNjo2NiIgbG9jYWxJZGVudGlmaWVyPSJTREUiIHBhdGg9Imh0dHA6Ly9lY3FpLmhlYWx0aGl0Lmdvdi9lY3Ftcy9TdXBwbGVtZW50YWxEYXRhRWxlbWVudHNGSElSNCIgdmVyc2lvbj0iMi4wLjAwMCI+DQogICAgICAgICA8YW5ub3RhdGlvbiB4c2k6dHlwZT0iYTpBbm5vdGF0aW9uIj4NCiAgICAgICAgICAgIDxhOnMgcj0iMyI+DQogICAgICAgICAgICAgICA8YTpzPmluY2x1ZGUgPC9hOnM+DQogICAgICAgICAgICAgICA8YTpzPg0KICAgICAgICAgICAgICAgICAgPGE6cz5TdXBwbGVtZW50YWxEYXRhRWxlbWVudHNGSElSNDwvYTpzPg0KICAgICAgICAgICAgICAgPC9hOnM+DQogICAgICAgICAgICAgICA8YTpzPiB2ZXJzaW9uICcyLjAuMDAwJyBjYWxsZWQgU0RFPC9hOnM+DQogICAgICAgICAgICA8L2E6cz4NCiAgICAgICAgIDwvYW5ub3RhdGlvbj4NCiAgICAgIDwvZGVmPg0KICAgICAgPGRlZiBsb2NhbElkPSI0IiBsb2NhdG9yPSI3OjEtNzo2OSIgbG9jYWxJZGVudGlmaWVyPSJHbG9iYWwiIHBhdGg9Imh0dHA6Ly9lY3FpLmhlYWx0aGl0Lmdvdi9lY3Ftcy9NQVRHbG9iYWxDb21tb25GdW5jdGlvbnNGSElSNCIgdmVyc2lvbj0iNi4wLjAwMCI+DQogICAgICAgICA8YW5ub3RhdGlvbiB4c2k6dHlwZT0iYTpBbm5vdGF0aW9uIj4NCiAgICAgICAgICAgIDxhOnMgcj0iNCI+DQogICAgICAgICAgICAgICA8YTpzPmluY2x1ZGUgPC9hOnM+DQogICAgICAgICAgICAgICA8YTpzPg0KICAgICAgICAgICAgICAgICAgPGE6cz5NQVRHbG9iYWxDb21tb25GdW5jdGlvbnNGSElSNDwvYTpzPg0KICAgICAgICAgICAgICAgPC9hOnM+DQogICAgICAgICAgICAgICA8YTpzPiB2ZXJzaW9uICc2LjAuMDAwJyBjYWxsZWQgR2xvYmFsPC9hOnM+DQogICAgICAgICAgICA8L2E6cz4NCiAgICAgICAgIDwvYW5ub3RhdGlvbj4NCiAgICAgIDwvZGVmPg0KICAgICAgPGRlZiBsb2NhbElkPSI1IiBsb2NhdG9yPSI4OjEtODo4OSIgbG9jYWxJZGVudGlmaWVyPSJBZHVsdE91dHBhdGllbnRFbmNvdW50ZXJzIiBwYXRoPSJodHRwOi8vZWNxaS5oZWFsdGhpdC5nb3YvZWNxbXMvQWR1bHRPdXRwYXRpZW50RW5jb3VudGVyc0ZISVI0IiB2ZXJzaW9uPSIyLjAuMDAwIj4NCiAgICAgICAgIDxhbm5vdGF0aW9uIHhzaTp0eXBlPSJhOkFubm90YXRpb24iPg0KICAgICAgICAgICAgPGE6cyByPSI1Ij4NCiAgICAgICAgICAgICAgIDxhOnM+aW5jbHVkZSA8L2E6cz4NCiAgICAgICAgICAgICAgIDxhOnM+DQogICAgICAgICAgICAgICAgICA8YTpzPkFkdWx0T3V0cGF0aWVudEVuY291bnRlcnNGSElSNDwvYTpzPg0KICAgICAgICAgICAgICAgPC9hOnM+DQogICAgICAgICAgICAgICA8YTpzPiB2ZXJzaW9uICcyLjAuMDAwJyBjYWxsZWQgQWR1bHRPdXRwYXRpZW50RW5jb3VudGVyczwvYTpzPg0KICAgICAgICAgICAgPC9hOnM+DQogICAgICAgICA8L2Fubm90YXRpb24+DQogICAgICA8L2RlZj4NCiAgICAgIDxkZWYgbG9jYWxJZD0iNiIgbG9jYXRvcj0iOToxLTk6NTMiIGxvY2FsSWRlbnRpZmllcj0iSG9zcGljZSIgcGF0aD0iaHR0cDovL2VjcWkuaGVhbHRoaXQuZ292L2VjcW1zL0hvc3BpY2VGSElSNCIgdmVyc2lvbj0iMi4wLjAwMCI+DQogICAgICAgICA8YW5ub3RhdGlvbiB4c2k6dHlwZT0iYTpBbm5vdGF0aW9uIj4NCiAgICAgICAgICAgIDxhOnMgcj0iNiI+DQogICAgICAgICAgICAgICA8YTpzPmluY2x1ZGUgPC9hOnM+DQogICAgICAgICAgICAgICA8YTpzPg0KICAgICAgICAgICAgICAgICAgPGE6cz5Ib3NwaWNlRkhJUjQ8L2E6cz4NCiAgICAgICAgICAgICAgIDwvYTpzPg0KICAgICAgICAgICAgICAgPGE6cz4gdmVyc2lvbiAnMi4wLjAwMCcgY2FsbGVkIEhvc3BpY2U8L2E6cz4NCiAgICAgICAgICAgIDwvYTpzPg0KICAgICAgICAgPC9hbm5vdGF0aW9uPg0KICAgICAgPC9kZWY+DQogICAgICA8ZGVmIGxvY2FsSWQ9IjciIGxvY2F0b3I9IjEwOjEtMTA6ODUiIGxvY2FsSWRlbnRpZmllcj0iRnJhaWx0eSIgcGF0aD0iaHR0cDovL2VjcWkuaGVhbHRoaXQuZ292L2VjcW1zL0FkdmFuY2VkSWxsbmVzc2FuZEZyYWlsdHlFeGNsdXNpb25FQ1FNRkhJUjQiIHZlcnNpb249IjUuMTIuMDAwIj4NCiAgICAgICAgIDxhbm5vdGF0aW9uIHhzaTp0eXBlPSJhOkFubm90YXRpb24iPg0KICAgICAgICAgICAgPGE6cyByPSI3Ij4NCiAgICAgICAgICAgICAgIDxhOnM+aW5jbHVkZSA8L2E6cz4NCiAgICAgICAgICAgICAgIDxhOnM+DQogICAgICAgICAgICAgICAgICA8YTpzPkFkdmFuY2VkSWxsbmVzc2FuZEZyYWlsdHlFeGNsdXNpb25FQ1FNRkhJUjQ8L2E6cz4NCiAgICAgICAgICAgICAgIDwvYTpzPg0KICAgICAgICAgICAgICAgPGE6cz4gdmVyc2lvbiAnNS4xMi4wMDAnIGNhbGxlZCBGcmFpbHR5PC9hOnM+DQogICAgICAgICAgICA8L2E6cz4NCiAgICAgICAgIDwvYW5ub3RhdGlvbj4NCiAgICAgIDwvZGVmPg0KICAgPC9pbmNsdWRlcz4NCiAgIDxwYXJhbWV0ZXJzPg0KICAgICAgPGRlZiBsb2NhbElkPSI0NSIgbG9jYXRvcj0iNDY6MS00Nzo2NiIgbmFtZT0iTWVhc3VyZW1lbnQgUGVyaW9kIiBhY2Nlc3NMZXZlbD0iUHVibGljIj4NCiAgICAgICAgIDxhbm5vdGF0aW9uIHhzaTp0eXBlPSJhOkFubm90YXRpb24iPg0KICAgICAgICAgICAgPGE6cyByPSI0NSI+DQogICAgICAgICAgICAgICA8YTpzPnBhcmFtZXRlciAmcXVvdDtNZWFzdXJlbWVudCBQZXJpb2QmcXVvdDsgPC9hOnM+DQogICAgICAgICAgICAgICA8YTpzIHI9IjQ0Ij4NCiAgICAgICAgICAgICAgICAgIDxhOnM+SW50ZXJ2YWwmbHQ7PC9hOnM+DQogICAgICAgICAgICAgICAgICA8YTpzIHI9IjQzIj4NCiAgICAgICAgICAgICAgICAgICAgIDxhOnM+RGF0ZVRpbWU8L2E6cz4NCiAgICAgICAgICAgICAgICAgIDwvYTpzPg0KICAgICAgICAgICAgICAgICAgPGE6cz4+PC9hOnM+DQogICAgICAgICAgICAgICA8L2E6cz4NCiAgICAgICAgICAgICAgIDxhOnM+CiAgZGVmYXVsdCA8L2E6cz4NCiAgICAgICAgICAgICAgIDxhOnMgcj0iNDIiPg0KICAgICAgICAgICAgICAgICAgPGE6cyByPSI0MCI+SW50ZXJ2YWxbQDIwMTktMDEtMDFUMDA6MDA6MDAuMCwgQDIwMjAtMDEtMDFUMDA6MDA6MDAuMCk8L2E6cz4NCiAgICAgICAgICAgICAgIDwvYTpzPg0KICAgICAgICAgICAgPC9hOnM+DQogICAgICAgICA8L2Fubm90YXRpb24+DQogICAgICAgICA8ZGVmYXVsdCBsb2NhbElkPSI0MiIgbG9jYXRvcj0iNDc6MTEtNDc6NjYiIGxvd0Nsb3NlZD0idHJ1ZSIgaGlnaENsb3NlZD0iZmFsc2UiIHhzaTp0eXBlPSJJbnRlcnZhbCI+DQogICAgICAgICAgICA8bG93IGxvY2FsSWQ9IjQwIiBsb2NhdG9yPSI0NzoyMC00Nzo0MSIgeHNpOnR5cGU9IkRhdGVUaW1lIj4NCiAgICAgICAgICAgICAgIDx5ZWFyIHZhbHVlVHlwZT0idDpJbnRlZ2VyIiB2YWx1ZT0iMjAxOSIgeHNpOnR5cGU9IkxpdGVyYWwiLz4NCiAgICAgICAgICAgICAgIDxtb250aCB2YWx1ZVR5cGU9InQ6SW50ZWdlciIgdmFsdWU9IjEiIHhzaTp0eXBlPSJMaXRlcmFsIi8+DQogICAgICAgICAgICAgICA8ZGF5IHZhbHVlVHlwZT0idDpJbnRlZ2VyIiB2YWx1ZT0iMSIgeHNpOnR5cGU9IkxpdGVyYWwiLz4NCiAgICAgICAgICAgICAgIDxob3VyIHZhbHVlVHlwZT0idDpJbnRlZ2VyIiB2YWx1ZT0iMCIgeHNpOnR5cGU9IkxpdGVyYWwiLz4NCiAgICAgICAgICAgICAgIDxtaW51dGUgdmFsdWVUeXBlPSJ0OkludGVnZXIiIHZhbHVlPSIwIiB4c2k6dHlwZT0iTGl0ZXJhbCIvPg0KICAgICAgICAgICAgICAgPHNlY29uZCB2YWx1ZVR5cGU9InQ6SW50ZWdlciIgdmFsdWU9IjAiIHhzaTp0eXBlPSJMaXRlcmFsIi8+DQogICAgICAgICAgICAgICA8bWlsbGlzZWNvbmQgdmFsdWVUeXBlPSJ0OkludGVnZXIiIHZhbHVlPSIwIiB4c2k6dHlwZT0iTGl0ZXJhbCIvPg0KICAgICAgICAgICAgPC9sb3c+DQogICAgICAgICAgICA8aGlnaCBsb2NhbElkPSI0MSIgbG9jYXRvcj0iNDc6NDQtNDc6NjUiIHhzaTp0eXBlPSJEYXRlVGltZSI+DQogICAgICAgICAgICAgICA8eWVhciB2YWx1ZVR5cGU9InQ6SW50ZWdlciIgdmFsdWU9IjIwMjAiIHhzaTp0eXBlPSJMaXRlcmFsIi8+DQogICAgICAgICAgICAgICA8bW9udGggdmFsdWVUeXBlPSJ0OkludGVnZXIiIHZhbHVlPSIxIiB4c2k6dHlwZT0iTGl0ZXJhbCIvPg0KICAgICAgICAgICAgICAgPGRheSB2YWx1ZVR5cGU9InQ6SW50ZWdlciIgdmFsdWU9IjEiIHhzaTp0eXBlPSJMaXRlcmFsIi8+DQogICAgICAgICAgICAgICA8aG91ciB2YWx1ZVR5cGU9InQ6SW50ZWdlciIgdmFsdWU9IjAiIHhzaTp0eXBlPSJMaXRlcmFsIi8+DQogICAgICAgICAgICAgICA8bWludXRlIHZhbHVlVHlwZT0idDpJbnRlZ2VyIiB2YWx1ZT0iMCIgeHNpOnR5cGU9IkxpdGVyYWwiLz4NCiAgICAgICAgICAgICAgIDxzZWNvbmQgdmFsdWVUeXBlPSJ0OkludGVnZXIiIHZhbHVlPSIwIiB4c2k6dHlwZT0iTGl0ZXJhbCIvPg0KICAgICAgICAgICAgICAgPG1pbGxpc2Vjb25kIHZhbHVlVHlwZT0idDpJbnRlZ2VyIiB2YWx1ZT0iMCIgeHNpOnR5cGU9IkxpdGVyYWwiLz4NCiAgICAgICAgICAgIDwvaGlnaD4NCiAgICAgICAgIDwvZGVmYXVsdD4NCiAgICAgICAgIDxwYXJhbWV0ZXJUeXBlU3BlY2lmaWVyIGxvY2FsSWQ9IjQ0IiBsb2NhdG9yPSI0NjozMi00Njo0OSIgeHNpOnR5cGU9IkludGVydmFsVHlwZVNwZWNpZmllciI+DQogICAgICAgICAgICA8cG9pbnRUeXBlIGxvY2FsSWQ9IjQzIiBsb2NhdG9yPSI0Njo0MS00Njo0OCIgbmFtZT0idDpEYXRlVGltZSIgeHNpOnR5cGU9Ik5hbWVkVHlwZVNwZWNpZmllciIvPg0KICAgICAgICAgPC9wYXJhbWV0ZXJUeXBlU3BlY2lmaWVyPg0KICAgICAgPC9kZWY+DQogICA8L3BhcmFtZXRlcnM+DQogICA8Y29kZVN5c3RlbXM+DQogICAgICA8ZGVmIGxvY2FsSWQ9IjgiIGxvY2F0b3I9IjEyOjEtMTI6MzgiIG5hbWU9IkxPSU5DIiBpZD0iaHR0cDovL2xvaW5jLm9yZyIgYWNjZXNzTGV2ZWw9IlB1YmxpYyI+DQogICAgICAgICA8YW5ub3RhdGlvbiB4c2k6dHlwZT0iYTpBbm5vdGF0aW9uIj4NCiAgICAgICAgICAgIDxhOnMgcj0iOCI+DQogICAgICAgICAgICAgICA8YTpzPmNvZGVzeXN0ZW0gJnF1b3Q7TE9JTkMmcXVvdDs6ICdodHRwOi8vbG9pbmMub3JnJzwvYTpzPg0KICAgICAgICAgICAgPC9hOnM+DQogICAgICAgICA8L2Fubm90YXRpb24+DQogICAgICA8L2RlZj4NCiAgIDwvY29kZVN5c3RlbXM+DQogICA8dmFsdWVTZXRzPg0KICAgICAgPGRlZiBsb2NhbElkPSI5IiBsb2NhdG9yPSIxNDoxLTE0OjEwNyIgbmFtZT0iQWN1dGUgSW5wYXRpZW50IiBpZD0iaHR0cDovL2N0cy5ubG0ubmloLmdvdi9maGlyL1ZhbHVlU2V0LzIuMTYuODQwLjEuMTEzODgzLjMuNDY0LjEwMDMuMTAxLjEyLjEwODMiIGFjY2Vzc0xldmVsPSJQdWJsaWMiPg0KICAgICAgICAgPGFubm90YXRpb24geHNpOnR5cGU9ImE6QW5ub3RhdGlvbiI+DQogICAgICAgICAgICA8YTpzIHI9IjkiPg0KICAgICAgICAgICAgICAgPGE6cz52YWx1ZXNldCAmcXVvdDtBY3V0ZSBJbnBhdGllbnQmcXVvdDs6ICdodHRwOi8vY3RzLm5sbS5uaWguZ292L2ZoaXIvVmFsdWVTZXQvMi4xNi44NDAuMS4xMTM4ODMuMy40NjQuMTAwMy4xMDEuMTIuMTA4Myc8L2E6cz4NCiAgICAgICAgICAgIDwvYTpzPg0KICAgICAgICAgPC9hbm5vdGF0aW9uPg0KICAgICAgPC9kZWY+DQogICAgICA8ZGVmIGxvY2FsSWQ9IjEwIiBsb2NhdG9yPSIxNToxLTE1OjEwOCIgbmFtZT0iQWR2YW5jZWQgSWxsbmVzcyIgaWQ9Imh0dHA6Ly9jdHMubmxtLm5paC5nb3YvZmhpci9WYWx1ZVNldC8yLjE2Ljg0MC4xLjExMzg4My4zLjQ2NC4xMDAzLjExMC4xMi4xMDgyIiBhY2Nlc3NMZXZlbD0iUHVibGljIj4NCiAgICAgICAgIDxhbm5vdGF0aW9uIHhzaTp0eXBlPSJhOkFubm90YXRpb24iPg0KICAgICAgICAgICAgPGE6cyByPSIxMCI+DQogICAgICAgICAgICAgICA8YTpzPnZhbHVlc2V0ICZxdW90O0FkdmFuY2VkIElsbG5lc3MmcXVvdDs6ICdodHRwOi8vY3RzLm5sbS5uaWguZ292L2ZoaXIvVmFsdWVTZXQvMi4xNi44NDAuMS4xMTM4ODMuMy40NjQuMTAwMy4xMTAuMTIuMTA4Mic8L2E6cz4NCiAgICAgICAgICAgIDwvYTpzPg0KICAgICAgICAgPC9hbm5vdGF0aW9uPg0KICAgICAgPC9kZWY+DQogICAgICA8ZGVmIGxvY2FsSWQ9IjExIiBsb2NhdG9yPSIxNjoxLTE2OjEwMyIgbmFtZT0iQW5udWFsIFdlbGxuZXNzIFZpc2l0IiBpZD0iaHR0cDovL2N0cy5ubG0ubmloLmdvdi9maGlyL1ZhbHVlU2V0LzIuMTYuODQwLjEuMTEzODgzLjMuNTI2LjMuMTI0MCIgYWNjZXNzTGV2ZWw9IlB1YmxpYyI+DQogICAgICAgICA8YW5ub3RhdGlvbiB4c2k6dHlwZT0iYTpBbm5vdGF0aW9uIj4NCiAgICAgICAgICAgIDxhOnMgcj0iMTEiPg0KICAgICAgICAgICAgICAgPGE6cz52YWx1ZXNldCAmcXVvdDtBbm51YWwgV2VsbG5lc3MgVmlzaXQmcXVvdDs6ICdodHRwOi8vY3RzLm5sbS5uaWguZ292L2ZoaXIvVmFsdWVTZXQvMi4xNi44NDAuMS4xMTM4ODMuMy41MjYuMy4xMjQwJzwvYTpzPg0KICAgICAgICAgICAgPC9hOnM+DQogICAgICAgICA8L2Fubm90YXRpb24+DQogICAgICA8L2RlZj4NCiAgICAgIDxkZWYgbG9jYWxJZD0iMTIiIGxvY2F0b3I9IjE3OjEtMTc6MTM5IiBuYW1lPSJDYXJlIFNlcnZpY2VzIGluIExvbmctVGVybSBSZXNpZGVudGlhbCBGYWNpbGl0eSIgaWQ9Imh0dHA6Ly9jdHMubmxtLm5paC5nb3YvZmhpci9WYWx1ZVNldC8yLjE2Ljg0MC4xLjExMzg4My4zLjQ2NC4xMDAzLjEwMS4xMi4xMDE0IiBhY2Nlc3NMZXZlbD0iUHVibGljIj4NCiAgICAgICAgIDxhbm5vdGF0aW9uIHhzaTp0eXBlPSJhOkFubm90YXRpb24iPg0KICAgICAgICAgICAgPGE6cyByPSIxMiI+DQogICAgICAgICAgICAgICA8YTpzPnZhbHVlc2V0ICZxdW90O0NhcmUgU2VydmljZXMgaW4gTG9uZy1UZXJtIFJlc2lkZW50aWFsIEZhY2lsaXR5JnF1b3Q7OiAnaHR0cDovL2N0cy5ubG0ubmloLmdvdi9maGlyL1ZhbHVlU2V0LzIuMTYuODQwLjEuMTEzODgzLjMuNDY0LjEwMDMuMTAxLjEyLjEwMTQnPC9hOnM+DQogICAgICAgICAgICA8L2E6cz4NCiAgICAgICAgIDwvYW5ub3RhdGlvbj4NCiAgICAgIDwvZGVmPg0KICAgICAgPGRlZiBsb2NhbElkPSIxMyIgbG9jYXRvcj0iMTg6MS0xODoxMDMiIG5hbWU9IkNvbG9ub3Njb3B5IiBpZD0iaHR0cDovL2N0cy5ubG0ubmloLmdvdi9maGlyL1ZhbHVlU2V0LzIuMTYuODQwLjEuMTEzODgzLjMuNDY0LjEwMDMuMTA4LjEyLjEwMjAiIGFjY2Vzc0xldmVsPSJQdWJsaWMiPg0KICAgICAgICAgPGFubm90YXRpb24geHNpOnR5cGU9ImE6QW5ub3RhdGlvbiI+DQogICAgICAgICAgICA8YTpzIHI9IjEzIj4NCiAgICAgICAgICAgICAgIDxhOnM+dmFsdWVzZXQgJnF1b3Q7Q29sb25vc2NvcHkmcXVvdDs6ICdodHRwOi8vY3RzLm5sbS5uaWguZ292L2ZoaXIvVmFsdWVTZXQvMi4xNi44NDAuMS4xMTM4ODMuMy40NjQuMTAwMy4xMDguMTIuMTAyMCc8L2E6cz4NCiAgICAgICAgICAgIDwvYTpzPg0KICAgICAgICAgPC9hbm5vdGF0aW9uPg0KICAgICAgPC9kZWY+DQogICAgICA8ZGVmIGxvY2FsSWQ9IjE0IiBsb2NhdG9yPSIxOToxLTE5OjEwNyIgbmFtZT0iQ1QgQ29sb25vZ3JhcGh5IiBpZD0iaHR0cDovL2N0cy5ubG0ubmloLmdvdi9maGlyL1ZhbHVlU2V0LzIuMTYuODQwLjEuMTEzODgzLjMuNDY0LjEwMDMuMTA4LjEyLjEwMzgiIGFjY2Vzc0xldmVsPSJQdWJsaWMiPg0KICAgICAgICAgPGFubm90YXRpb24geHNpOnR5cGU9ImE6QW5ub3RhdGlvbiI+DQogICAgICAgICAgICA8YTpzIHI9IjE0Ij4NCiAgICAgICAgICAgICAgIDxhOnM+dmFsdWVzZXQgJnF1b3Q7Q1QgQ29sb25vZ3JhcGh5JnF1b3Q7OiAnaHR0cDovL2N0cy5ubG0ubmloLmdvdi9maGlyL1ZhbHVlU2V0LzIuMTYuODQwLjEuMTEzODgzLjMuNDY0LjEwMDMuMTA4LjEyLjEwMzgnPC9hOnM+DQogICAgICAgICAgICA8L2E6cz4NCiAgICAgICAgIDwvYW5ub3RhdGlvbj4NCiAgICAgIDwvZGVmPg0KICAgICAgPGRlZiBsb2NhbElkPSIxNSIgbG9jYXRvcj0iMjA6MS0yMDoxMTIiIG5hbWU9IkRlbWVudGlhIE1lZGljYXRpb25zIiBpZD0iaHR0cDovL2N0cy5ubG0ubmloLmdvdi9maGlyL1ZhbHVlU2V0LzIuMTYuODQwLjEuMTEzODgzLjMuNDY0LjEwMDMuMTk2LjEyLjE1MTAiIGFjY2Vzc0xldmVsPSJQdWJsaWMiPg0KICAgICAgICAgPGFubm90YXRpb24geHNpOnR5cGU9ImE6QW5ub3RhdGlvbiI+DQogICAgICAgICAgICA8YTpzIHI9IjE1Ij4NCiAgICAgICAgICAgICAgIDxhOnM+dmFsdWVzZXQgJnF1b3Q7RGVtZW50aWEgTWVkaWNhdGlvbnMmcXVvdDs6ICdodHRwOi8vY3RzLm5sbS5uaWguZ292L2ZoaXIvVmFsdWVTZXQvMi4xNi44NDAuMS4xMTM4ODMuMy40NjQuMTAwMy4xOTYuMTIuMTUxMCc8L2E6cz4NCiAgICAgICAgICAgIDwvYTpzPg0KICAgICAgICAgPC9hbm5vdGF0aW9uPg0KICAgICAgPC9kZWY+DQogICAgICA8ZGVmIGxvY2FsSWQ9IjE2IiBsb2NhdG9yPSIyMToxLTIxOjEzNiIgbmFtZT0iRGlzY2hhcmdlZCB0byBIZWFsdGggQ2FyZSBGYWNpbGl0eSBmb3IgSG9zcGljZSBDYXJlIiBpZD0iaHR0cDovL2N0cy5ubG0ubmloLmdvdi9maGlyL1ZhbHVlU2V0LzIuMTYuODQwLjEuMTEzODgzLjMuMTE3LjEuNy4xLjIwNyIgYWNjZXNzTGV2ZWw9IlB1YmxpYyI+DQogICAgICAgICA8YW5ub3RhdGlvbiB4c2k6dHlwZT0iYTpBbm5vdGF0aW9uIj4NCiAgICAgICAgICAgIDxhOnMgcj0iMTYiPg0KICAgICAgICAgICAgICAgPGE6cz52YWx1ZXNldCAmcXVvdDtEaXNjaGFyZ2VkIHRvIEhlYWx0aCBDYXJlIEZhY2lsaXR5IGZvciBIb3NwaWNlIENhcmUmcXVvdDs6ICdodHRwOi8vY3RzLm5sbS5uaWguZ292L2ZoaXIvVmFsdWVTZXQvMi4xNi44NDAuMS4xMTM4ODMuMy4xMTcuMS43LjEuMjA3JzwvYTpzPg0KICAgICAgICAgICAgPC9hOnM+DQogICAgICAgICA8L2Fubm90YXRpb24+DQogICAgICA8L2RlZj4NCiAgICAgIDxkZWYgbG9jYWxJZD0iMTciIGxvY2F0b3I9IjIyOjEtMjI6MTIwIiBuYW1lPSJEaXNjaGFyZ2VkIHRvIEhvbWUgZm9yIEhvc3BpY2UgQ2FyZSIgaWQ9Imh0dHA6Ly9jdHMubmxtLm5paC5nb3YvZmhpci9WYWx1ZVNldC8yLjE2Ljg0MC4xLjExMzg4My4zLjExNy4xLjcuMS4yMDkiIGFjY2Vzc0xldmVsPSJQdWJsaWMiPg0KICAgICAgICAgPGFubm90YXRpb24geHNpOnR5cGU9ImE6QW5ub3RhdGlvbiI+DQogICAgICAgICAgICA8YTpzIHI9IjE3Ij4NCiAgICAgICAgICAgICAgIDxhOnM+dmFsdWVzZXQgJnF1b3Q7RGlzY2hhcmdlZCB0byBIb21lIGZvciBIb3NwaWNlIENhcmUmcXVvdDs6ICdodHRwOi8vY3RzLm5sbS5uaWguZ292L2ZoaXIvVmFsdWVTZXQvMi4xNi44NDAuMS4xMTM4ODMuMy4xMTcuMS43LjEuMjA5JzwvYTpzPg0KICAgICAgICAgICAgPC9hOnM+DQogICAgICAgICA8L2Fubm90YXRpb24+DQogICAgICA8L2RlZj4NCiAgICAgIDxkZWYgbG9jYWxJZD0iMTgiIGxvY2F0b3I9IjIzOjEtMjM6OTQiIG5hbWU9IkVEIiBpZD0iaHR0cDovL2N0cy5ubG0ubmloLmdvdi9maGlyL1ZhbHVlU2V0LzIuMTYuODQwLjEuMTEzODgzLjMuNDY0LjEwMDMuMTAxLjEyLjEwODUiIGFjY2Vzc0xldmVsPSJQdWJsaWMiPg0KICAgICAgICAgPGFubm90YXRpb24geHNpOnR5cGU9ImE6QW5ub3RhdGlvbiI+DQogICAgICAgICAgICA8YTpzIHI9IjE4Ij4NCiAgICAgICAgICAgICAgIDxhOnM+dmFsdWVzZXQgJnF1b3Q7RUQmcXVvdDs6ICdodHRwOi8vY3RzLm5sbS5uaWguZ292L2ZoaXIvVmFsdWVTZXQvMi4xNi44NDAuMS4xMTM4ODMuMy40NjQuMTAwMy4xMDEuMTIuMTA4NSc8L2E6cz4NCiAgICAgICAgICAgIDwvYTpzPg0KICAgICAgICAgPC9hbm5vdGF0aW9uPg0KICAgICAgPC9kZWY+DQogICAgICA8ZGVmIGxvY2FsSWQ9IjE5IiBsb2NhdG9yPSIyNDoxLTI0OjEwMCIgbmFtZT0iRW5jb3VudGVyIElucGF0aWVudCIgaWQ9Imh0dHA6Ly9jdHMubmxtLm5paC5nb3YvZmhpci9WYWx1ZVNldC8yLjE2Ljg0MC4xLjExMzg4My4zLjY2Ni41LjMwNyIgYWNjZXNzTGV2ZWw9IlB1YmxpYyI+DQogICAgICAgICA8YW5ub3RhdGlvbiB4c2k6dHlwZT0iYTpBbm5vdGF0aW9uIj4NCiAgICAgICAgICAgIDxhOnMgcj0iMTkiPg0KICAgICAgICAgICAgICAgPGE6cz52YWx1ZXNldCAmcXVvdDtFbmNvdW50ZXIgSW5wYXRpZW50JnF1b3Q7OiAnaHR0cDovL2N0cy5ubG0ubmloLmdvdi9maGlyL1ZhbHVlU2V0LzIuMTYuODQwLjEuMTEzODgzLjMuNjY2LjUuMzA3JzwvYTpzPg0KICAgICAgICAgICAgPC9hOnM+DQogICAgICAgICA8L2Fubm90YXRpb24+DQogICAgICA8L2RlZj4NCiAgICAgIDxkZWYgbG9jYWxJZD0iMjAiIGxvY2F0b3I9IjI1OjEtMjU6MTIyIiBuYW1lPSJGZWNhbCBPY2N1bHQgQmxvb2QgVGVzdCAoRk9CVCkiIGlkPSJodHRwOi8vY3RzLm5sbS5uaWguZ292L2ZoaXIvVmFsdWVTZXQvMi4xNi44NDAuMS4xMTM4ODMuMy40NjQuMTAwMy4xOTguMTIuMTAxMSIgYWNjZXNzTGV2ZWw9IlB1YmxpYyI+DQogICAgICAgICA8YW5ub3RhdGlvbiB4c2k6dHlwZT0iYTpBbm5vdGF0aW9uIj4NCiAgICAgICAgICAgIDxhOnMgcj0iMjAiPg0KICAgICAgICAgICAgICAgPGE6cz52YWx1ZXNldCAmcXVvdDtGZWNhbCBPY2N1bHQgQmxvb2QgVGVzdCAoRk9CVCkmcXVvdDs6ICdodHRwOi8vY3RzLm5sbS5uaWguZ292L2ZoaXIvVmFsdWVTZXQvMi4xNi44NDAuMS4xMTM4ODMuMy40NjQuMTAwMy4xOTguMTIuMTAxMSc8L2E6cz4NCiAgICAgICAgICAgIDwvYTpzPg0KICAgICAgICAgPC9hbm5vdGF0aW9uPg0KICAgICAgPC9kZWY+DQogICAgICA8ZGVmIGxvY2FsSWQ9IjIxIiBsb2NhdG9yPSIyNjoxLTI2Ojk5IiBuYW1lPSJGSVQgRE5BIiBpZD0iaHR0cDovL2N0cy5ubG0ubmloLmdvdi9maGlyL1ZhbHVlU2V0LzIuMTYuODQwLjEuMTEzODgzLjMuNDY0LjEwMDMuMTA4LjEyLjEwMzkiIGFjY2Vzc0xldmVsPSJQdWJsaWMiPg0KICAgICAgICAgPGFubm90YXRpb24geHNpOnR5cGU9ImE6QW5ub3RhdGlvbiI+DQogICAgICAgICAgICA8YTpzIHI9IjIxIj4NCiAgICAgICAgICAgICAgIDxhOnM+dmFsdWVzZXQgJnF1b3Q7RklUIEROQSZxdW90OzogJ2h0dHA6Ly9jdHMubmxtLm5paC5nb3YvZmhpci9WYWx1ZVNldC8yLjE2Ljg0MC4xLjExMzg4My4zLjQ2NC4xMDAzLjEwOC4xMi4xMDM5JzwvYTpzPg0KICAgICAgICAgICAgPC9hOnM+DQogICAgICAgICA8L2Fubm90YXRpb24+DQogICAgICA8L2RlZj4NCiAgICAgIDxkZWYgbG9jYWxJZD0iMjIiIGxvY2F0b3I9IjI3OjEtMjc6MTE0IiBuYW1lPSJGbGV4aWJsZSBTaWdtb2lkb3Njb3B5IiBpZD0iaHR0cDovL2N0cy5ubG0ubmloLmdvdi9maGlyL1ZhbHVlU2V0LzIuMTYuODQwLjEuMTEzODgzLjMuNDY0LjEwMDMuMTk4LjEyLjEwMTAiIGFjY2Vzc0xldmVsPSJQdWJsaWMiPg0KICAgICAgICAgPGFubm90YXRpb24geHNpOnR5cGU9ImE6QW5ub3RhdGlvbiI+DQogICAgICAgICAgICA8YTpzIHI9IjIyIj4NCiAgICAgICAgICAgICAgIDxhOnM+dmFsdWVzZXQgJnF1b3Q7RmxleGlibGUgU2lnbW9pZG9zY29weSZxdW90OzogJ2h0dHA6Ly9jdHMubmxtLm5paC5nb3YvZmhpci9WYWx1ZVNldC8yLjE2Ljg0MC4xLjExMzg4My4zLjQ2NC4xMDAzLjE5OC4xMi4xMDEwJzwvYTpzPg0KICAgICAgICAgICAgPC9hOnM+DQogICAgICAgICA8L2Fubm90YXRpb24+DQogICAgICA8L2RlZj4NCiAgICAgIDxkZWYgbG9jYWxJZD0iMjMiIGxvY2F0b3I9IjI4OjEtMjg6MTA2IiBuYW1lPSJGcmFpbHR5IERldmljZSIgaWQ9Imh0dHA6Ly9jdHMubmxtLm5paC5nb3YvZmhpci9WYWx1ZVNldC8yLjE2Ljg0MC4xLjExMzg4My4zLjQ2NC4xMDAzLjExOC4xMi4xMzAwIiBhY2Nlc3NMZXZlbD0iUHVibGljIj4NCiAgICAgICAgIDxhbm5vdGF0aW9uIHhzaTp0eXBlPSJhOkFubm90YXRpb24iPg0KICAgICAgICAgICAgPGE6cyByPSIyMyI+DQogICAgICAgICAgICAgICA8YTpzPnZhbHVlc2V0ICZxdW90O0ZyYWlsdHkgRGV2aWNlJnF1b3Q7OiAnaHR0cDovL2N0cy5ubG0ubmloLmdvdi9maGlyL1ZhbHVlU2V0LzIuMTYuODQwLjEuMTEzODgzLjMuNDY0LjEwMDMuMTE4LjEyLjEzMDAnPC9hOnM+DQogICAgICAgICAgICA8L2E6cz4NCiAgICAgICAgIDwvYW5ub3RhdGlvbj4NCiAgICAgIDwvZGVmPg0KICAgICAgPGRlZiBsb2NhbElkPSIyNCIgbG9jYXRvcj0iMjk6MS0yOToxMDkiIG5hbWU9IkZyYWlsdHkgRGlhZ25vc2lzIiBpZD0iaHR0cDovL2N0cy5ubG0ubmloLmdvdi9maGlyL1ZhbHVlU2V0LzIuMTYuODQwLjEuMTEzODgzLjMuNDY0LjEwMDMuMTEzLjEyLjEwNzQiIGFjY2Vzc0xldmVsPSJQdWJsaWMiPg0KICAgICAgICAgPGFubm90YXRpb24geHNpOnR5cGU9ImE6QW5ub3RhdGlvbiI+DQogICAgICAgICAgICA8YTpzIHI9IjI0Ij4NCiAgICAgICAgICAgICAgIDxhOnM+dmFsdWVzZXQgJnF1b3Q7RnJhaWx0eSBEaWFnbm9zaXMmcXVvdDs6ICdodHRwOi8vY3RzLm5sbS5uaWguZ292L2ZoaXIvVmFsdWVTZXQvMi4xNi44NDAuMS4xMTM4ODMuMy40NjQuMTAwMy4xMTMuMTIuMTA3NCc8L2E6cz4NCiAgICAgICAgICAgIDwvYTpzPg0KICAgICAgICAgPC9hbm5vdGF0aW9uPg0KICAgICAgPC9kZWY+DQogICAgICA8ZGVmIGxvY2FsSWQ9IjI1IiBsb2NhdG9yPSIzMDoxLTMwOjEwOSIgbmFtZT0iRnJhaWx0eSBFbmNvdW50ZXIiIGlkPSJodHRwOi8vY3RzLm5sbS5uaWguZ292L2ZoaXIvVmFsdWVTZXQvMi4xNi44NDAuMS4xMTM4ODMuMy40NjQuMTAwMy4xMDEuMTIuMTA4OCIgYWNjZXNzTGV2ZWw9IlB1YmxpYyI+DQogICAgICAgICA8YW5ub3RhdGlvbiB4c2k6dHlwZT0iYTpBbm5vdGF0aW9uIj4NCiAgICAgICAgICAgIDxhOnMgcj0iMjUiPg0KICAgICAgICAgICAgICAgPGE6cz52YWx1ZXNldCAmcXVvdDtGcmFpbHR5IEVuY291bnRlciZxdW90OzogJ2h0dHA6Ly9jdHMubmxtLm5paC5nb3YvZmhpci9WYWx1ZVNldC8yLjE2Ljg0MC4xLjExMzg4My4zLjQ2NC4xMDAzLjEwMS4xMi4xMDg4JzwvYTpzPg0KICAgICAgICAgICAgPC9hOnM+DQogICAgICAgICA8L2Fubm90YXRpb24+DQogICAgICA8L2RlZj4NCiAgICAgIDxkZWYgbG9jYWxJZD0iMjYiIGxvY2F0b3I9IjMxOjEtMzE6MTA3IiBuYW1lPSJGcmFpbHR5IFN5bXB0b20iIGlkPSJodHRwOi8vY3RzLm5sbS5uaWguZ292L2ZoaXIvVmFsdWVTZXQvMi4xNi44NDAuMS4xMTM4ODMuMy40NjQuMTAwMy4xMTMuMTIuMTA3NSIgYWNjZXNzTGV2ZWw9IlB1YmxpYyI+DQogICAgICAgICA8YW5ub3RhdGlvbiB4c2k6dHlwZT0iYTpBbm5vdGF0aW9uIj4NCiAgICAgICAgICAgIDxhOnMgcj0iMjYiPg0KICAgICAgICAgICAgICAgPGE6cz52YWx1ZXNldCAmcXVvdDtGcmFpbHR5IFN5bXB0b20mcXVvdDs6ICdodHRwOi8vY3RzLm5sbS5uaWguZ292L2ZoaXIvVmFsdWVTZXQvMi4xNi44NDAuMS4xMTM4ODMuMy40NjQuMTAwMy4xMTMuMTIuMTA3NSc8L2E6cz4NCiAgICAgICAgICAgIDwvYTpzPg0KICAgICAgICAgPC9hbm5vdGF0aW9uPg0KICAgICAgPC9kZWY+DQogICAgICA8ZGVmIGxvY2FsSWQ9IjI3IiBsb2NhdG9yPSIzMjoxLTMyOjExNiIgbmFtZT0iSG9tZSBIZWFsdGhjYXJlIFNlcnZpY2VzIiBpZD0iaHR0cDovL2N0cy5ubG0ubmloLmdvdi9maGlyL1ZhbHVlU2V0LzIuMTYuODQwLjEuMTEzODgzLjMuNDY0LjEwMDMuMTAxLjEyLjEwMTYiIGFjY2Vzc0xldmVsPSJQdWJsaWMiPg0KICAgICAgICAgPGFubm90YXRpb24geHNpOnR5cGU9ImE6QW5ub3RhdGlvbiI+DQogICAgICAgICAgICA8YTpzIHI9IjI3Ij4NCiAgICAgICAgICAgICAgIDxhOnM+dmFsdWVzZXQgJnF1b3Q7SG9tZSBIZWFsdGhjYXJlIFNlcnZpY2VzJnF1b3Q7OiAnaHR0cDovL2N0cy5ubG0ubmloLmdvdi9maGlyL1ZhbHVlU2V0LzIuMTYuODQwLjEuMTEzODgzLjMuNDY0LjEwMDMuMTAxLjEyLjEwMTYnPC9hOnM+DQogICAgICAgICAgICA8L2E6cz4NCiAgICAgICAgIDwvYW5ub3RhdGlvbj4NCiAgICAgIDwvZGVmPg0KICAgICAgPGRlZiBsb2NhbElkPSIyOCIgbG9jYXRvcj0iMzM6MS0zMzoxMDQiIG5hbWU9Ikhvc3BpY2UgY2FyZSBhbWJ1bGF0b3J5IiBpZD0iaHR0cDovL2N0cy5ubG0ubmloLmdvdi9maGlyL1ZhbHVlU2V0LzIuMTYuODQwLjEuMTEzNzYyLjEuNC4xMTA4LjE1IiBhY2Nlc3NMZXZlbD0iUHVibGljIj4NCiAgICAgICAgIDxhbm5vdGF0aW9uIHhzaTp0eXBlPSJhOkFubm90YXRpb24iPg0KICAgICAgICAgICAgPGE6cyByPSIyOCI+DQogICAgICAgICAgICAgICA8YTpzPnZhbHVlc2V0ICZxdW90O0hvc3BpY2UgY2FyZSBhbWJ1bGF0b3J5JnF1b3Q7OiAnaHR0cDovL2N0cy5ubG0ubmloLmdvdi9maGlyL1ZhbHVlU2V0LzIuMTYuODQwLjEuMTEzNzYyLjEuNC4xMTA4LjE1JzwvYTpzPg0KICAgICAgICAgICAgPC9hOnM+DQogICAgICAgICA8L2Fubm90YXRpb24+DQogICAgICA8L2RlZj4NCiAgICAgIDxkZWYgbG9jYWxJZD0iMjkiIGxvY2F0b3I9IjM0OjEtMzQ6MTE5IiBuYW1lPSJNYWxpZ25hbnQgTmVvcGxhc20gb2YgQ29sb24iIGlkPSJodHRwOi8vY3RzLm5sbS5uaWguZ292L2ZoaXIvVmFsdWVTZXQvMi4xNi44NDAuMS4xMTM4ODMuMy40NjQuMTAwMy4xMDguMTIuMTAwMSIgYWNjZXNzTGV2ZWw9IlB1YmxpYyI+DQogICAgICAgICA8YW5ub3RhdGlvbiB4c2k6dHlwZT0iYTpBbm5vdGF0aW9uIj4NCiAgICAgICAgICAgIDxhOnMgcj0iMjkiPg0KICAgICAgICAgICAgICAgPGE6cz52YWx1ZXNldCAmcXVvdDtNYWxpZ25hbnQgTmVvcGxhc20gb2YgQ29sb24mcXVvdDs6ICdodHRwOi8vY3RzLm5sbS5uaWguZ292L2ZoaXIvVmFsdWVTZXQvMi4xNi44NDAuMS4xMTM4ODMuMy40NjQuMTAwMy4xMDguMTIuMTAwMSc8L2E6cz4NCiAgICAgICAgICAgIDwvYTpzPg0KICAgICAgICAgPC9hbm5vdGF0aW9uPg0KICAgICAgPC9kZWY+DQogICAgICA8ZGVmIGxvY2FsSWQ9IjMwIiBsb2NhdG9yPSIzNToxLTM1OjExMCIgbmFtZT0iTm9uYWN1dGUgSW5wYXRpZW50IiBpZD0iaHR0cDovL2N0cy5ubG0ubmloLmdvdi9maGlyL1ZhbHVlU2V0LzIuMTYuODQwLjEuMTEzODgzLjMuNDY0LjEwMDMuMTAxLjEyLjEwODQiIGFjY2Vzc0xldmVsPSJQdWJsaWMiPg0KICAgICAgICAgPGFubm90YXRpb24geHNpOnR5cGU9ImE6QW5ub3RhdGlvbiI+DQogICAgICAgICAgICA8YTpzIHI9IjMwIj4NCiAgICAgICAgICAgICAgIDxhOnM+dmFsdWVzZXQgJnF1b3Q7Tm9uYWN1dGUgSW5wYXRpZW50JnF1b3Q7OiAnaHR0cDovL2N0cy5ubG0ubmloLmdvdi9maGlyL1ZhbHVlU2V0LzIuMTYuODQwLjEuMTEzODgzLjMuNDY0LjEwMDMuMTAxLjEyLjEwODQnPC9hOnM+DQogICAgICAgICAgICA8L2E6cz4NCiAgICAgICAgIDwvYW5ub3RhdGlvbj4NCiAgICAgIDwvZGVmPg0KICAgICAgPGRlZiBsb2NhbElkPSIzMSIgbG9jYXRvcj0iMzY6MS0zNjoxMTQiIG5hbWU9Ik51cnNpbmcgRmFjaWxpdHkgVmlzaXQiIGlkPSJodHRwOi8vY3RzLm5sbS5uaWguZ292L2ZoaXIvVmFsdWVTZXQvMi4xNi44NDAuMS4xMTM4ODMuMy40NjQuMTAwMy4xMDEuMTIuMTAxMiIgYWNjZXNzTGV2ZWw9IlB1YmxpYyI+DQogICAgICAgICA8YW5ub3RhdGlvbiB4c2k6dHlwZT0iYTpBbm5vdGF0aW9uIj4NCiAgICAgICAgICAgIDxhOnMgcj0iMzEiPg0KICAgICAgICAgICAgICAgPGE6cz52YWx1ZXNldCAmcXVvdDtOdXJzaW5nIEZhY2lsaXR5IFZpc2l0JnF1b3Q7OiAnaHR0cDovL2N0cy5ubG0ubmloLmdvdi9maGlyL1ZhbHVlU2V0LzIuMTYuODQwLjEuMTEzODgzLjMuNDY0LjEwMDMuMTAxLjEyLjEwMTInPC9hOnM+DQogICAgICAgICAgICA8L2E6cz4NCiAgICAgICAgIDwvYW5ub3RhdGlvbj4NCiAgICAgIDwvZGVmPg0KICAgICAgPGRlZiBsb2NhbElkPSIzMiIgbG9jYXRvcj0iMzc6MS0zNzoxMDMiIG5hbWU9Ik9ic2VydmF0aW9uIiBpZD0iaHR0cDovL2N0cy5ubG0ubmloLmdvdi9maGlyL1ZhbHVlU2V0LzIuMTYuODQwLjEuMTEzODgzLjMuNDY0LjEwMDMuMTAxLjEyLjEwODYiIGFjY2Vzc0xldmVsPSJQdWJsaWMiPg0KICAgICAgICAgPGFubm90YXRpb24geHNpOnR5cGU9ImE6QW5ub3RhdGlvbiI+DQogICAgICAgICAgICA8YTpzIHI9IjMyIj4NCiAgICAgICAgICAgICAgIDxhOnM+dmFsdWVzZXQgJnF1b3Q7T2JzZXJ2YXRpb24mcXVvdDs6ICdodHRwOi8vY3RzLm5sbS5uaWguZ292L2ZoaXIvVmFsdWVTZXQvMi4xNi44NDAuMS4xMTM4ODMuMy40NjQuMTAwMy4xMDEuMTIuMTA4Nic8L2E6cz4NCiAgICAgICAgICAgIDwvYTpzPg0KICAgICAgICAgPC9hbm5vdGF0aW9uPg0KICAgICAgPC9kZWY+DQogICAgICA8ZGVmIGxvY2FsSWQ9IjMzIiBsb2NhdG9yPSIzODoxLTM4OjEwNCIgbmFtZT0iT2ZmaWNlIFZpc2l0IiBpZD0iaHR0cDovL2N0cy5ubG0ubmloLmdvdi9maGlyL1ZhbHVlU2V0LzIuMTYuODQwLjEuMTEzODgzLjMuNDY0LjEwMDMuMTAxLjEyLjEwMDEiIGFjY2Vzc0xldmVsPSJQdWJsaWMiPg0KICAgICAgICAgPGFubm90YXRpb24geHNpOnR5cGU9ImE6QW5ub3RhdGlvbiI+DQogICAgICAgICAgICA8YTpzIHI9IjMzIj4NCiAgICAgICAgICAgICAgIDxhOnM+dmFsdWVzZXQgJnF1b3Q7T2ZmaWNlIFZpc2l0JnF1b3Q7OiAnaHR0cDovL2N0cy5ubG0ubmloLmdvdi9maGlyL1ZhbHVlU2V0LzIuMTYuODQwLjEuMTEzODgzLjMuNDY0LjEwMDMuMTAxLjEyLjEwMDEnPC9hOnM+DQogICAgICAgICAgICA8L2E6cz4NCiAgICAgICAgIDwvYW5ub3RhdGlvbj4NCiAgICAgIDwvZGVmPg0KICAgICAgPGRlZiBsb2NhbElkPSIzNCIgbG9jYXRvcj0iMzk6MS0zOToxMDIiIG5hbWU9Ik91dHBhdGllbnQiIGlkPSJodHRwOi8vY3RzLm5sbS5uaWguZ292L2ZoaXIvVmFsdWVTZXQvMi4xNi44NDAuMS4xMTM4ODMuMy40NjQuMTAwMy4xMDEuMTIuMTA4NyIgYWNjZXNzTGV2ZWw9IlB1YmxpYyI+DQogICAgICAgICA8YW5ub3RhdGlvbiB4c2k6dHlwZT0iYTpBbm5vdGF0aW9uIj4NCiAgICAgICAgICAgIDxhOnMgcj0iMzQiPg0KICAgICAgICAgICAgICAgPGE6cz52YWx1ZXNldCAmcXVvdDtPdXRwYXRpZW50JnF1b3Q7OiAnaHR0cDovL2N0cy5ubG0ubmloLmdvdi9maGlyL1ZhbHVlU2V0LzIuMTYuODQwLjEuMTEzODgzLjMuNDY0LjEwMDMuMTAxLjEyLjEwODcnPC9hOnM+DQogICAgICAgICAgICA8L2E6cz4NCiAgICAgICAgIDwvYW5ub3RhdGlvbj4NCiAgICAgIDwvZGVmPg0KICAgICAgPGRlZiBsb2NhbElkPSIzNSIgbG9jYXRvcj0iNDA6MS00MDoxNTQiIG5hbWU9IlByZXZlbnRpdmUgQ2FyZSBTZXJ2aWNlcyAtIEVzdGFibGlzaGVkIE9mZmljZSBWaXNpdCwgMTggYW5kIFVwIiBpZD0iaHR0cDovL2N0cy5ubG0ubmloLmdvdi9maGlyL1ZhbHVlU2V0LzIuMTYuODQwLjEuMTEzODgzLjMuNDY0LjEwMDMuMTAxLjEyLjEwMjUiIGFjY2Vzc0xldmVsPSJQdWJsaWMiPg0KICAgICAgICAgPGFubm90YXRpb24geHNpOnR5cGU9ImE6QW5ub3RhdGlvbiI+DQogICAgICAgICAgICA8YTpzIHI9IjM1Ij4NCiAgICAgICAgICAgICAgIDxhOnM+dmFsdWVzZXQgJnF1b3Q7UHJldmVudGl2ZSBDYXJlIFNlcnZpY2VzIC0gRXN0YWJsaXNoZWQgT2ZmaWNlIFZpc2l0LCAxOCBhbmQgVXAmcXVvdDs6ICdodHRwOi8vY3RzLm5sbS5uaWguZ292L2ZoaXIvVmFsdWVTZXQvMi4xNi44NDAuMS4xMTM4ODMuMy40NjQuMTAwMy4xMDEuMTIuMTAyNSc8L2E6cz4NCiAgICAgICAgICAgIDwvYTpzPg0KICAgICAgICAgPC9hbm5vdGF0aW9uPg0KICAgICAgPC9kZWY+DQogICAgICA8ZGVmIGxvY2FsSWQ9IjM2IiBsb2NhdG9yPSI0MToxLTQxOjE0OCIgbmFtZT0iUHJldmVudGl2ZSBDYXJlIFNlcnZpY2VzLUluaXRpYWwgT2ZmaWNlIFZpc2l0LCAxOCBhbmQgVXAiIGlkPSJodHRwOi8vY3RzLm5sbS5uaWguZ292L2ZoaXIvVmFsdWVTZXQvMi4xNi44NDAuMS4xMTM4ODMuMy40NjQuMTAwMy4xMDEuMTIuMTAyMyIgYWNjZXNzTGV2ZWw9IlB1YmxpYyI+DQogICAgICAgICA8YW5ub3RhdGlvbiB4c2k6dHlwZT0iYTpBbm5vdGF0aW9uIj4NCiAgICAgICAgICAgIDxhOnMgcj0iMzYiPg0KICAgICAgICAgICAgICAgPGE6cz52YWx1ZXNldCAmcXVvdDtQcmV2ZW50aXZlIENhcmUgU2VydmljZXMtSW5pdGlhbCBPZmZpY2UgVmlzaXQsIDE4IGFuZCBVcCZxdW90OzogJ2h0dHA6Ly9jdHMubmxtLm5paC5nb3YvZmhpci9WYWx1ZVNldC8yLjE2Ljg0MC4xLjExMzg4My4zLjQ2NC4xMDAzLjEwMS4xMi4xMDIzJzwvYTpzPg0KICAgICAgICAgICAgPC9hOnM+DQogICAgICAgICA8L2Fubm90YXRpb24+DQogICAgICA8L2RlZj4NCiAgICAgIDxkZWYgbG9jYWxJZD0iMzciIGxvY2F0b3I9IjQyOjEtNDI6MTA3IiBuYW1lPSJUb3RhbCBDb2xlY3RvbXkiIGlkPSJodHRwOi8vY3RzLm5sbS5uaWguZ292L2ZoaXIvVmFsdWVTZXQvMi4xNi44NDAuMS4xMTM4ODMuMy40NjQuMTAwMy4xOTguMTIuMTAxOSIgYWNjZXNzTGV2ZWw9IlB1YmxpYyI+DQogICAgICAgICA8YW5ub3RhdGlvbiB4c2k6dHlwZT0iYTpBbm5vdGF0aW9uIj4NCiAgICAgICAgICAgIDxhOnMgcj0iMzciPg0KICAgICAgICAgICAgICAgPGE6cz52YWx1ZXNldCAmcXVvdDtUb3RhbCBDb2xlY3RvbXkmcXVvdDs6ICdodHRwOi8vY3RzLm5sbS5uaWguZ292L2ZoaXIvVmFsdWVTZXQvMi4xNi44NDAuMS4xMTM4ODMuMy40NjQuMTAwMy4xOTguMTIuMTAxOSc8L2E6cz4NCiAgICAgICAgICAgIDwvYTpzPg0KICAgICAgICAgPC9hbm5vdGF0aW9uPg0KICAgICAgPC9kZWY+DQogICA8L3ZhbHVlU2V0cz4NCiAgIDxjb2Rlcz4NCiAgICAgIDxkZWYgbG9jYWxJZD0iMzkiIGxvY2F0b3I9IjQ0OjEtNDQ6NjIiIG5hbWU9IkJpcnRoIGRhdGUiIGlkPSIyMTExMi04IiBkaXNwbGF5PSJCaXJ0aCBkYXRlIiBhY2Nlc3NMZXZlbD0iUHVibGljIj4NCiAgICAgICAgIDxhbm5vdGF0aW9uIHhzaTp0eXBlPSJhOkFubm90YXRpb24iPg0KICAgICAgICAgICAgPGE6cyByPSIzOSI+DQogICAgICAgICAgICAgICA8YTpzPmNvZGUgJnF1b3Q7QmlydGggZGF0ZSZxdW90OzogJzIxMTEyLTgnIGZyb20gPC9hOnM+DQogICAgICAgICAgICAgICA8YTpzIHI9IjM4Ij4NCiAgICAgICAgICAgICAgICAgIDxhOnM+JnF1b3Q7TE9JTkMmcXVvdDs8L2E6cz4NCiAgICAgICAgICAgICAgIDwvYTpzPg0KICAgICAgICAgICAgICAgPGE6cz4gZGlzcGxheSAnQmlydGggZGF0ZSc8L2E6cz4NCiAgICAgICAgICAgIDwvYTpzPg0KICAgICAgICAgPC9hbm5vdGF0aW9uPg0KICAgICAgICAgPGNvZGVTeXN0ZW0gbG9jYWxJZD0iMzgiIGxvY2F0b3I9IjQ0OjM1LTQ0OjQxIiBuYW1lPSJMT0lOQyIvPg0KICAgICAgPC9kZWY+DQogICA8L2NvZGVzPg0KICAgPHN0YXRlbWVudHM+DQogICAgICA8ZGVmIGxvY2F0b3I9IjQ5OjEtNDk6MTUiIG5hbWU9IlBhdGllbnQiIGNvbnRleHQ9IlBhdGllbnQiPg0KICAgICAgICAgPGV4cHJlc3Npb24geHNpOnR5cGU9IlNpbmdsZXRvbkZyb20iPg0KICAgICAgICAgICAgPG9wZXJhbmQgbG9jYXRvcj0iNDk6MS00OToxNSIgZGF0YVR5cGU9ImZoaXI6UGF0aWVudCIgdGVtcGxhdGVJZD0iaHR0cDovL2hsNy5vcmcvZmhpci9TdHJ1Y3R1cmVEZWZpbml0aW9uL1BhdGllbnQiIHhzaTp0eXBlPSJSZXRyaWV2ZSIvPg0KICAgICAgICAgPC9leHByZXNzaW9uPg0KICAgICAgPC9kZWY+DQogICAgICA8ZGVmIGxvY2FsSWQ9IjQ4IiBsb2NhdG9yPSI1MToxLTUyOjIxIiBuYW1lPSJTREUgRXRobmljaXR5IiBjb250ZXh0PSJQYXRpZW50IiBhY2Nlc3NMZXZlbD0iUHVibGljIj4NCiAgICAgICAgIDxhbm5vdGF0aW9uIHhzaTp0eXBlPSJhOkFubm90YXRpb24iPg0KICAgICAgICAgICAgPGE6cyByPSI0OCI+DQogICAgICAgICAgICAgICA8YTpzPmRlZmluZSAmcXVvdDtTREUgRXRobmljaXR5JnF1b3Q7OgogIDwvYTpzPg0KICAgICAgICAgICAgICAgPGE6cyByPSI0NyI+DQogICAgICAgICAgICAgICAgICA8YTpzIHI9IjQ2Ij4NCiAgICAgICAgICAgICAgICAgICAgIDxhOnM+U0RFPC9hOnM+DQogICAgICAgICAgICAgICAgICA8L2E6cz4NCiAgICAgICAgICAgICAgICAgIDxhOnM+LjwvYTpzPg0KICAgICAgICAgICAgICAgICAgPGE6cyByPSI0NyI+DQogICAgICAgICAgICAgICAgICAgICA8YTpzPiZxdW90O1NERSBFdGhuaWNpdHkmcXVvdDs8L2E6cz4NCiAgICAgICAgICAgICAgICAgIDwvYTpzPg0KICAgICAgICAgICAgICAgPC9hOnM+DQogICAgICAgICAgICA8L2E6cz4NCiAgICAgICAgIDwvYW5ub3RhdGlvbj4NCiAgICAgICAgIDxleHByZXNzaW9uIGxvY2FsSWQ9IjQ3IiBsb2NhdG9yPSI1MjozLTUyOjIxIiBuYW1lPSJTREUgRXRobmljaXR5IiBsaWJyYXJ5TmFtZT0iU0RFIiB4c2k6dHlwZT0iRXhwcmVzc2lvblJlZiIvPg0KICAgICAgPC9kZWY+DQogICAgICA8ZGVmIGxvY2FsSWQ9IjUxIiBsb2NhdG9yPSI1NDoxLTU1OjE3IiBuYW1lPSJTREUgUGF5ZXIiIGNvbnRleHQ9IlBhdGllbnQiIGFjY2Vzc0xldmVsPSJQdWJsaWMiPg0KICAgICAgICAgPGFubm90YXRpb24geHNpOnR5cGU9ImE6QW5ub3RhdGlvbiI+DQogICAgICAgICAgICA8YTpzIHI9IjUxIj4NCiAgICAgICAgICAgICAgIDxhOnM+ZGVmaW5lICZxdW90O1NERSBQYXllciZxdW90OzoKICA8L2E6cz4NCiAgICAgICAgICAgICAgIDxhOnMgcj0iNTAiPg0KICAgICAgICAgICAgICAgICAgPGE6cyByPSI0OSI+DQogICAgICAgICAgICAgICAgICAgICA8YTpzPlNERTwvYTpzPg0KICAgICAgICAgICAgICAgICAgPC9hOnM+DQogICAgICAgICAgICAgICAgICA8YTpzPi48L2E6cz4NCiAgICAgICAgICAgICAgICAgIDxhOnMgcj0iNTAiPg0KICAgICAgICAgICAgICAgICAgICAgPGE6cz4mcXVvdDtTREUgUGF5ZXImcXVvdDs8L2E6cz4NCiAgICAgICAgICAgICAgICAgIDwvYTpzPg0KICAgICAgICAgICAgICAgPC9hOnM+DQogICAgICAgICAgICA8L2E6cz4NCiAgICAgICAgIDwvYW5ub3RhdGlvbj4NCiAgICAgICAgIDxleHByZXNzaW9uIGxvY2FsSWQ9IjUwIiBsb2NhdG9yPSI1NTozLTU1OjE3IiBuYW1lPSJTREUgUGF5ZXIiIGxpYnJhcnlOYW1lPSJTREUiIHhzaTp0eXBlPSJFeHByZXNzaW9uUmVmIi8+DQogICAgICA8L2RlZj4NCiAgICAgIDxkZWYgbG9jYWxJZD0iNTQiIGxvY2F0b3I9IjU3OjEtNTg6MTYiIG5hbWU9IlNERSBSYWNlIiBjb250ZXh0PSJQYXRpZW50IiBhY2Nlc3NMZXZlbD0iUHVibGljIj4NCiAgICAgICAgIDxhbm5vdGF0aW9uIHhzaTp0eXBlPSJhOkFubm90YXRpb24iPg0KICAgICAgICAgICAgPGE6cyByPSI1NCI+DQogICAgICAgICAgICAgICA8YTpzPmRlZmluZSAmcXVvdDtTREUgUmFjZSZxdW90OzoKICA8L2E6cz4NCiAgICAgICAgICAgICAgIDxhOnMgcj0iNTMiPg0KICAgICAgICAgICAgICAgICAgPGE6cyByPSI1MiI+DQogICAgICAgICAgICAgICAgICAgICA8YTpzPlNERTwvYTpzPg0KICAgICAgICAgICAgICAgICAgPC9hOnM+DQogICAgICAgICAgICAgICAgICA8YTpzPi48L2E6cz4NCiAgICAgICAgICAgICAgICAgIDxhOnMgcj0iNTMiPg0KICAgICAgICAgICAgICAgICAgICAgPGE6cz4mcXVvdDtTREUgUmFjZSZxdW90OzwvYTpzPg0KICAgICAgICAgICAgICAgICAgPC9hOnM+DQogICAgICAgICAgICAgICA8L2E6cz4NCiAgICAgICAgICAgIDwvYTpzPg0KICAgICAgICAgPC9hbm5vdGF0aW9uPg0KICAgICAgICAgPGV4cHJlc3Npb24gbG9jYWxJZD0iNTMiIGxvY2F0b3I9IjU4OjMtNTg6MTYiIG5hbWU9IlNERSBSYWNlIiBsaWJyYXJ5TmFtZT0iU0RFIiB4c2k6dHlwZT0iRXhwcmVzc2lvblJlZiIvPg0KICAgICAgPC9kZWY+DQogICAgICA8ZGVmIGxvY2FsSWQ9IjU3IiBsb2NhdG9yPSI2MDoxLTYxOjE1IiBuYW1lPSJTREUgU2V4IiBjb250ZXh0PSJQYXRpZW50IiBhY2Nlc3NMZXZlbD0iUHVibGljIj4NCiAgICAgICAgIDxhbm5vdGF0aW9uIHhzaTp0eXBlPSJhOkFubm90YXRpb24iPg0KICAgICAgICAgICAgPGE6cyByPSI1NyI+DQogICAgICAgICAgICAgICA8YTpzPmRlZmluZSAmcXVvdDtTREUgU2V4JnF1b3Q7OgogIDwvYTpzPg0KICAgICAgICAgICAgICAgPGE6cyByPSI1NiI+DQogICAgICAgICAgICAgICAgICA8YTpzIHI9IjU1Ij4NCiAgICAgICAgICAgICAgICAgICAgIDxhOnM+U0RFPC9hOnM+DQogICAgICAgICAgICAgICAgICA8L2E6cz4NCiAgICAgICAgICAgICAgICAgIDxhOnM+LjwvYTpzPg0KICAgICAgICAgICAgICAgICAgPGE6cyByPSI1NiI+DQogICAgICAgICAgICAgICAgICAgICA8YTpzPiZxdW90O1NERSBTZXgmcXVvdDs8L2E6cz4NCiAgICAgICAgICAgICAgICAgIDwvYTpzPg0KICAgICAgICAgICAgICAgPC9hOnM+DQogICAgICAgICAgICA8L2E6cz4NCiAgICAgICAgIDwvYW5ub3RhdGlvbj4NCiAgICAgICAgIDxleHByZXNzaW9uIGxvY2FsSWQ9IjU2IiBsb2NhdG9yPSI2MTozLTYxOjE1IiBuYW1lPSJTREUgU2V4IiBsaWJyYXJ5TmFtZT0iU0RFIiB4c2k6dHlwZT0iRXhwcmVzc2lvblJlZiIvPg0KICAgICAgPC9kZWY+DQogICAgICA8ZGVmIGxvY2FsSWQ9IjY4IiBsb2NhdG9yPSI2MzoxLTY1OjYyIiBuYW1lPSJJbml0aWFsIFBvcHVsYXRpb24iIGNvbnRleHQ9IlBhdGllbnQiIGFjY2Vzc0xldmVsPSJQdWJsaWMiPg0KICAgICAgICAgPGFubm90YXRpb24geHNpOnR5cGU9ImE6QW5ub3RhdGlvbiI+DQogICAgICAgICAgICA8YTpzIHI9IjY4Ij4NCiAgICAgICAgICAgICAgIDxhOnM+ZGVmaW5lICZxdW90O0luaXRpYWwgUG9wdWxhdGlvbiZxdW90OzoKICA8L2E6cz4NCiAgICAgICAgICAgICAgIDxhOnMgcj0iNjciPg0KICAgICAgICAgICAgICAgICAgPGE6cyByPSI2MyI+DQogICAgICAgICAgICAgICAgICAgICA8YTpzIHI9IjYwIj4NCiAgICAgICAgICAgICAgICAgICAgICAgIDxhOnM+QWdlSW5ZZWFyc0F0KDwvYTpzPg0KICAgICAgICAgICAgICAgICAgICAgICAgPGE6cyByPSI1OSI+DQogICAgICAgICAgICAgICAgICAgICAgICAgICA8YTpzPnN0YXJ0IG9mIDwvYTpzPg0KICAgICAgICAgICAgICAgICAgICAgICAgICAgPGE6cyByPSI1OCI+DQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8YTpzPiZxdW90O01lYXN1cmVtZW50IFBlcmlvZCZxdW90OzwvYTpzPg0KICAgICAgICAgICAgICAgICAgICAgICAgICAgPC9hOnM+DQogICAgICAgICAgICAgICAgICAgICAgICA8L2E6cz4NCiAgICAgICAgICAgICAgICAgICAgICAgIDxhOnM+KTwvYTpzPg0KICAgICAgICAgICAgICAgICAgICAgPC9hOnM+DQogICAgICAgICAgICAgICAgICAgICA8YTpzIHI9IjYxIj4gYmV0d2VlbiA1MCBhbmQgNzU8L2E6cz4NCiAgICAgICAgICAgICAgICAgIDwvYTpzPg0KICAgICAgICAgICAgICAgICAgPGE6cz4KCSBhbmQgPC9hOnM+DQogICAgICAgICAgICAgICAgICA8YTpzIHI9IjY2Ij4NCiAgICAgICAgICAgICAgICAgICAgIDxhOnM+ZXhpc3RzIDwvYTpzPg0KICAgICAgICAgICAgICAgICAgICAgPGE6cyByPSI2NSI+DQogICAgICAgICAgICAgICAgICAgICAgICA8YTpzIHI9IjY0Ij4NCiAgICAgICAgICAgICAgICAgICAgICAgICAgIDxhOnM+QWR1bHRPdXRwYXRpZW50RW5jb3VudGVyczwvYTpzPg0KICAgICAgICAgICAgICAgICAgICAgICAgPC9hOnM+DQogICAgICAgICAgICAgICAgICAgICAgICA8YTpzPi48L2E6cz4NCiAgICAgICAgICAgICAgICAgICAgICAgIDxhOnMgcj0iNjUiPg0KICAgICAgICAgICAgICAgICAgICAgICAgICAgPGE6cz4mcXVvdDtRdWFsaWZ5aW5nIEVuY291bnRlcnMmcXVvdDs8L2E6cz4NCiAgICAgICAgICAgICAgICAgICAgICAgIDwvYTpzPg0KICAgICAgICAgICAgICAgICAgICAgPC9hOnM+DQogICAgICAgICAgICAgICAgICA8L2E6cz4NCiAgICAgICAgICAgICAgIDwvYTpzPg0KICAgICAgICAgICAgPC9hOnM+DQogICAgICAgICA8L2Fubm90YXRpb24+DQogICAgICAgICA8ZXhwcmVzc2lvbiBsb2NhbElkPSI2NyIgbG9jYXRvcj0iNjQ6My02NTo2MiIgeHNpOnR5cGU9IkFuZCI+DQogICAgICAgICAgICA8b3BlcmFuZCBsb2NhbElkPSI2MyIgbG9jYXRvcj0iNjQ6My02NDo2MyIgeHNpOnR5cGU9IkFuZCI+DQogICAgICAgICAgICAgICA8b3BlcmFuZCB4c2k6dHlwZT0iR3JlYXRlck9yRXF1YWwiPg0KICAgICAgICAgICAgICAgICAgPG9wZXJhbmQgbG9jYWxJZD0iNjAiIGxvY2F0b3I9IjY0OjMtNjQ6NDUiIHByZWNpc2lvbj0iWWVhciIgeHNpOnR5cGU9IkNhbGN1bGF0ZUFnZUF0Ij4NCiAgICAgICAgICAgICAgICAgICAgIDxvcGVyYW5kIHhzaTp0eXBlPSJUb0RhdGVUaW1lIj4NCiAgICAgICAgICAgICAgICAgICAgICAgIDxvcGVyYW5kIHBhdGg9ImJpcnRoRGF0ZS52YWx1ZSIgeHNpOnR5cGU9IlByb3BlcnR5Ij4NCiAgICAgICAgICAgICAgICAgICAgICAgICAgIDxzb3VyY2UgbmFtZT0iUGF0aWVudCIgeHNpOnR5cGU9IkV4cHJlc3Npb25SZWYiLz4NCiAgICAgICAgICAgICAgICAgICAgICAgIDwvb3BlcmFuZD4NCiAgICAgICAgICAgICAgICAgICAgIDwvb3BlcmFuZD4NCiAgICAgICAgICAgICAgICAgICAgIDxvcGVyYW5kIGxvY2FsSWQ9IjU5IiBsb2NhdG9yPSI2NDoxNi02NDo0NCIgeHNpOnR5cGU9IlN0YXJ0Ij4NCiAgICAgICAgICAgICAgICAgICAgICAgIDxvcGVyYW5kIGxvY2FsSWQ9IjU4IiBsb2NhdG9yPSI2NDoyNS02NDo0NCIgbmFtZT0iTWVhc3VyZW1lbnQgUGVyaW9kIiB4c2k6dHlwZT0iUGFyYW1ldGVyUmVmIi8+DQogICAgICAgICAgICAgICAgICAgICA8L29wZXJhbmQ+DQogICAgICAgICAgICAgICAgICA8L29wZXJhbmQ+DQogICAgICAgICAgICAgICAgICA8b3BlcmFuZCBsb2NhbElkPSI2MSIgbG9jYXRvcj0iNjQ6NTUtNjQ6NTYiIHZhbHVlVHlwZT0idDpJbnRlZ2VyIiB2YWx1ZT0iNTAiIHhzaTp0eXBlPSJMaXRlcmFsIi8+DQogICAgICAgICAgICAgICA8L29wZXJhbmQ+DQogICAgICAgICAgICAgICA8b3BlcmFuZCB4c2k6dHlwZT0iTGVzc09yRXF1YWwiPg0KICAgICAgICAgICAgICAgICAgPG9wZXJhbmQgbG9jYWxJZD0iNjAiIGxvY2F0b3I9IjY0OjMtNjQ6NDUiIHByZWNpc2lvbj0iWWVhciIgeHNpOnR5cGU9IkNhbGN1bGF0ZUFnZUF0Ij4NCiAgICAgICAgICAgICAgICAgICAgIDxvcGVyYW5kIHhzaTp0eXBlPSJUb0RhdGVUaW1lIj4NCiAgICAgICAgICAgICAgICAgICAgICAgIDxvcGVyYW5kIHBhdGg9ImJpcnRoRGF0ZS52YWx1ZSIgeHNpOnR5cGU9IlByb3BlcnR5Ij4NCiAgICAgICAgICAgICAgICAgICAgICAgICAgIDxzb3VyY2UgbmFtZT0iUGF0aWVudCIgeHNpOnR5cGU9IkV4cHJlc3Npb25SZWYiLz4NCiAgICAgICAgICAgICAgICAgICAgICAgIDwvb3BlcmFuZD4NCiAgICAgICAgICAgICAgICAgICAgIDwvb3BlcmFuZD4NCiAgICAgICAgICAgICAgICAgICAgIDxvcGVyYW5kIGxvY2FsSWQ9IjU5IiBsb2NhdG9yPSI2NDoxNi02NDo0NCIgeHNpOnR5cGU9IlN0YXJ0Ij4NCiAgICAgICAgICAgICAgICAgICAgICAgIDxvcGVyYW5kIGxvY2FsSWQ9IjU4IiBsb2NhdG9yPSI2NDoyNS02NDo0NCIgbmFtZT0iTWVhc3VyZW1lbnQgUGVyaW9kIiB4c2k6dHlwZT0iUGFyYW1ldGVyUmVmIi8+DQogICAgICAgICAgICAgICAgICAgICA8L29wZXJhbmQ+DQogICAgICAgICAgICAgICAgICA8L29wZXJhbmQ+DQogICAgICAgICAgICAgICAgICA8b3BlcmFuZCBsb2NhbElkPSI2MiIgbG9jYXRvcj0iNjQ6NjItNjQ6NjMiIHZhbHVlVHlwZT0idDpJbnRlZ2VyIiB2YWx1ZT0iNzUiIHhzaTp0eXBlPSJMaXRlcmFsIi8+DQogICAgICAgICAgICAgICA8L29wZXJhbmQ+DQogICAgICAgICAgICA8L29wZXJhbmQ+DQogICAgICAgICAgICA8b3BlcmFuZCBsb2NhbElkPSI2NiIgbG9jYXRvcj0iNjU6Ny02NTo2MiIgeHNpOnR5cGU9IkV4aXN0cyI+DQogICAgICAgICAgICAgICA8b3BlcmFuZCBsb2NhbElkPSI2NSIgbG9jYXRvcj0iNjU6MTQtNjU6NjIiIG5hbWU9IlF1YWxpZnlpbmcgRW5jb3VudGVycyIgbGlicmFyeU5hbWU9IkFkdWx0T3V0cGF0aWVudEVuY291bnRlcnMiIHhzaTp0eXBlPSJFeHByZXNzaW9uUmVmIi8+DQogICAgICAgICAgICA8L29wZXJhbmQ+DQogICAgICAgICA8L2V4cHJlc3Npb24+DQogICAgICA8L2RlZj4NCiAgICAgIDxkZWYgbG9jYWxJZD0iNzAiIGxvY2F0b3I9IjY3OjEtNjg6MjIiIG5hbWU9IkRlbm9taW5hdG9yIiBjb250ZXh0PSJQYXRpZW50IiBhY2Nlc3NMZXZlbD0iUHVibGljIj4NCiAgICAgICAgIDxhbm5vdGF0aW9uIHhzaTp0eXBlPSJhOkFubm90YXRpb24iPg0KICAgICAgICAgICAgPGE6cyByPSI3MCI+DQogICAgICAgICAgICAgICA8YTpzPmRlZmluZSAmcXVvdDtEZW5vbWluYXRvciZxdW90OzoKICA8L2E6cz4NCiAgICAgICAgICAgICAgIDxhOnMgcj0iNjkiPg0KICAgICAgICAgICAgICAgICAgPGE6cz4mcXVvdDtJbml0aWFsIFBvcHVsYXRpb24mcXVvdDs8L2E6cz4NCiAgICAgICAgICAgICAgIDwvYTpzPg0KICAgICAgICAgICAgPC9hOnM+DQogICAgICAgICA8L2Fubm90YXRpb24+DQogICAgICAgICA8ZXhwcmVzc2lvbiBsb2NhbElkPSI2OSIgbG9jYXRvcj0iNjg6My02ODoyMiIgbmFtZT0iSW5pdGlhbCBQb3B1bGF0aW9uIiB4c2k6dHlwZT0iRXhwcmVzc2lvblJlZiIvPg0KICAgICAgPC9kZWY+DQogICAgICA8ZGVmIGxvY2FsSWQ9Ijg3IiBsb2NhdG9yPSI3MDoxLTczOjE0OSIgbmFtZT0iRmxleGlibGUgU2lnbW9pZG9zY29weSBQZXJmb3JtZWQiIGNvbnRleHQ9IlBhdGllbnQiIGFjY2Vzc0xldmVsPSJQdWJsaWMiPg0KICAgICAgICAgPGFubm90YXRpb24geHNpOnR5cGU9ImE6QW5ub3RhdGlvbiI+DQogICAgICAgICAgICA8YTpzIHI9Ijg3Ij4NCiAgICAgICAgICAgICAgIDxhOnM+ZGVmaW5lICZxdW90O0ZsZXhpYmxlIFNpZ21vaWRvc2NvcHkgUGVyZm9ybWVkJnF1b3Q7OgogIDwvYTpzPg0KICAgICAgICAgICAgICAgPGE6cyByPSI4NiI+DQogICAgICAgICAgICAgICAgICA8YTpzPg0KICAgICAgICAgICAgICAgICAgICAgPGE6cyByPSI3MiI+DQogICAgICAgICAgICAgICAgICAgICAgICA8YTpzIHI9IjcxIj4NCiAgICAgICAgICAgICAgICAgICAgICAgICAgIDxhOnMgcj0iNzEiPg0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPGE6cz5bUHJvY2VkdXJlOiA8L2E6cz4NCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDxhOnM+DQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8YTpzPiZxdW90O0ZsZXhpYmxlIFNpZ21vaWRvc2NvcHkmcXVvdDs8L2E6cz4NCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDwvYTpzPg0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPGE6cz5dPC9hOnM+DQogICAgICAgICAgICAgICAgICAgICAgICAgICA8L2E6cz4NCiAgICAgICAgICAgICAgICAgICAgICAgIDwvYTpzPg0KICAgICAgICAgICAgICAgICAgICAgICAgPGE6cz4gRmxleGlibGVTaWdtb2lkb3Njb3B5PC9hOnM+DQogICAgICAgICAgICAgICAgICAgICA8L2E6cz4NCiAgICAgICAgICAgICAgICAgIDwvYTpzPg0KICAgICAgICAgICAgICAgICAgPGE6cz4KICAgICAgICAgICAgICAgICAgICAJCTwvYTpzPg0KICAgICAgICAgICAgICAgICAgPGE6cyByPSI4NSI+DQogICAgICAgICAgICAgICAgICAgICA8YTpzPndoZXJlIDwvYTpzPg0KICAgICAgICAgICAgICAgICAgICAgPGE6cyByPSI4NSI+DQogICAgICAgICAgICAgICAgICAgICAgICA8YTpzIHI9Ijc2Ij4NCiAgICAgICAgICAgICAgICAgICAgICAgICAgIDxhOnMgcj0iNzQiPg0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPGE6cyByPSI3MyI+DQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8YTpzPkZsZXhpYmxlU2lnbW9pZG9zY29weTwvYTpzPg0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPC9hOnM+DQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8YTpzPi48L2E6cz4NCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDxhOnMgcj0iNzQiPg0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPGE6cz5zdGF0dXM8L2E6cz4NCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDwvYTpzPg0KICAgICAgICAgICAgICAgICAgICAgICAgICAgPC9hOnM+DQogICAgICAgICAgICAgICAgICAgICAgICAgICA8YTpzPiA9IDwvYTpzPg0KICAgICAgICAgICAgICAgICAgICAgICAgICAgPGE6cyByPSI3NSI+DQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8YTpzPidjb21wbGV0ZWQnPC9hOnM+DQogICAgICAgICAgICAgICAgICAgICAgICAgICA8L2E6cz4NCiAgICAgICAgICAgICAgICAgICAgICAgIDwvYTpzPg0KICAgICAgICAgICAgICAgICAgICAgICAgPGE6cz4KICAgICAgICAgICAgICAgICAgICAJCQlhbmQgPC9hOnM+DQogICAgICAgICAgICAgICAgICAgICAgICA8YTpzIHI9Ijg0Ij4NCiAgICAgICAgICAgICAgICAgICAgICAgICAgIDxhOnMgcj0iODAiPg0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPGE6cyByPSI3NyI+DQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8YTpzPkdsb2JhbDwvYTpzPg0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPC9hOnM+DQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8YTpzPi48L2E6cz4NCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDxhOnMgcj0iODAiPg0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPGE6cz4mcXVvdDtOb3JtYWxpemUgSW50ZXJ2YWwmcXVvdDsoPC9hOnM+DQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8YTpzIHI9Ijc5Ij4NCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDxhOnMgcj0iNzgiPg0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPGE6cz5GbGV4aWJsZVNpZ21vaWRvc2NvcHk8L2E6cz4NCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDwvYTpzPg0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPGE6cz4uPC9hOnM+DQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8YTpzIHI9Ijc5Ij4NCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDxhOnM+cGVyZm9ybWVkPC9hOnM+DQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8L2E6cz4NCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDwvYTpzPg0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPGE6cz4pPC9hOnM+DQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8L2E6cz4NCiAgICAgICAgICAgICAgICAgICAgICAgICAgIDwvYTpzPg0KICAgICAgICAgICAgICAgICAgICAgICAgICAgPGE6cz4gPC9hOnM+DQogICAgICAgICAgICAgICAgICAgICAgICAgICA8YTpzIHI9Ijg0Ij4NCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDxhOnM+ZW5kcyA8L2E6cz4NCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDxhOnMgcj0iODMiPg0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPGE6cz41IHllYXJzPC9hOnM+DQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8L2E6cz4NCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDxhOnM+IG9yIGxlc3Mgb24gb3IgYmVmb3JlPC9hOnM+DQogICAgICAgICAgICAgICAgICAgICAgICAgICA8L2E6cz4NCiAgICAgICAgICAgICAgICAgICAgICAgICAgIDxhOnM+IDwvYTpzPg0KICAgICAgICAgICAgICAgICAgICAgICAgICAgPGE6cyByPSI4MiI+DQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8YTpzPmVuZCBvZiA8L2E6cz4NCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDxhOnMgcj0iODEiPg0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPGE6cz4mcXVvdDtNZWFzdXJlbWVudCBQZXJpb2QmcXVvdDs8L2E6cz4NCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDwvYTpzPg0KICAgICAgICAgICAgICAgICAgICAgICAgICAgPC9hOnM+DQogICAgICAgICAgICAgICAgICAgICAgICA8L2E6cz4NCiAgICAgICAgICAgICAgICAgICAgIDwvYTpzPg0KICAgICAgICAgICAgICAgICAgPC9hOnM+DQogICAgICAgICAgICAgICA8L2E6cz4NCiAgICAgICAgICAgIDwvYTpzPg0KICAgICAgICAgPC9hbm5vdGF0aW9uPg0KICAgICAgICAgPGV4cHJlc3Npb24gbG9jYWxJZD0iODYiIGxvY2F0b3I9IjcxOjMtNzM6MTQ5IiB4c2k6dHlwZT0iUXVlcnkiPg0KICAgICAgICAgICAgPHNvdXJjZSBsb2NhbElkPSI3MiIgbG9jYXRvcj0iNzE6My03MTo2MSIgYWxpYXM9IkZsZXhpYmxlU2lnbW9pZG9zY29weSI+DQogICAgICAgICAgICAgICA8ZXhwcmVzc2lvbiBsb2NhbElkPSI3MSIgbG9jYXRvcj0iNzE6My03MTozOSIgZGF0YVR5cGU9ImZoaXI6UHJvY2VkdXJlIiB0ZW1wbGF0ZUlkPSJodHRwOi8vaGw3Lm9yZy9maGlyL1N0cnVjdHVyZURlZmluaXRpb24vUHJvY2VkdXJlIiBjb2RlUHJvcGVydHk9ImNvZGUiIGNvZGVDb21wYXJhdG9yPSJpbiIgeHNpOnR5cGU9IlJldHJpZXZlIj4NCiAgICAgICAgICAgICAgICAgIDxjb2RlcyBsb2NhdG9yPSI3MToxNS03MTozOCIgbmFtZT0iRmxleGlibGUgU2lnbW9pZG9zY29weSIgeHNpOnR5cGU9IlZhbHVlU2V0UmVmIi8+DQogICAgICAgICAgICAgICA8L2V4cHJlc3Npb24+DQogICAgICAgICAgICA8L3NvdXJjZT4NCiAgICAgICAgICAgIDx3aGVyZSBsb2NhbElkPSI4NSIgbG9jYXRvcj0iNzI6MjMtNzM6MTQ5IiB4c2k6dHlwZT0iQW5kIj4NCiAgICAgICAgICAgICAgIDxvcGVyYW5kIGxvY2FsSWQ9Ijc2IiBsb2NhdG9yPSI3MjoyOS03Mjo3MCIgeHNpOnR5cGU9IkVxdWFsIj4NCiAgICAgICAgICAgICAgICAgIDxvcGVyYW5kIG5hbWU9IlRvU3RyaW5nIiBsaWJyYXJ5TmFtZT0iRkhJUkhlbHBlcnMiIHhzaTp0eXBlPSJGdW5jdGlvblJlZiI+DQogICAgICAgICAgICAgICAgICAgICA8b3BlcmFuZCBsb2NhbElkPSI3NCIgbG9jYXRvcj0iNzI6MjktNzI6NTYiIHBhdGg9InN0YXR1cyIgc2NvcGU9IkZsZXhpYmxlU2lnbW9pZG9zY29weSIgeHNpOnR5cGU9IlByb3BlcnR5Ii8+DQogICAgICAgICAgICAgICAgICA8L29wZXJhbmQ+DQogICAgICAgICAgICAgICAgICA8b3BlcmFuZCBsb2NhbElkPSI3NSIgbG9jYXRvcj0iNzI6NjAtNzI6NzAiIHZhbHVlVHlwZT0idDpTdHJpbmciIHZhbHVlPSJjb21wbGV0ZWQiIHhzaTp0eXBlPSJMaXRlcmFsIi8+DQogICAgICAgICAgICAgICA8L29wZXJhbmQ+DQogICAgICAgICAgICAgICA8b3BlcmFuZCBsb2NhbElkPSI4NCIgbG9jYXRvcj0iNzM6MjgtNzM6MTQ5IiB4c2k6dHlwZT0iQW5kIj4NCiAgICAgICAgICAgICAgICAgIDxvcGVyYW5kIGxvY2F0b3I9IjczOjk0LTczOjEwOCIgeHNpOnR5cGU9IkluIj4NCiAgICAgICAgICAgICAgICAgICAgIDxvcGVyYW5kIGxvY2F0b3I9IjczOjg5LTczOjkyIiB4c2k6dHlwZT0iRW5kIj4NCiAgICAgICAgICAgICAgICAgICAgICAgIDxvcGVyYW5kIGxvY2FsSWQ9IjgwIiBsb2NhdG9yPSI3MzoyOC03Mzo4NyIgbmFtZT0iTm9ybWFsaXplIEludGVydmFsIiBsaWJyYXJ5TmFtZT0iR2xvYmFsIiB4c2k6dHlwZT0iRnVuY3Rpb25SZWYiPg0KICAgICAgICAgICAgICAgICAgICAgICAgICAgPG9wZXJhbmQgbG9jYWxJZD0iNzkiIGxvY2F0b3I9IjczOjU2LTczOjg2IiBwYXRoPSJwZXJmb3JtZWQiIHNjb3BlPSJGbGV4aWJsZVNpZ21vaWRvc2NvcHkiIHhzaTp0eXBlPSJQcm9wZXJ0eSIvPg0KICAgICAgICAgICAgICAgICAgICAgICAgPC9vcGVyYW5kPg0KICAgICAgICAgICAgICAgICAgICAgPC9vcGVyYW5kPg0KICAgICAgICAgICAgICAgICAgICAgPG9wZXJhbmQgbG9jYXRvcj0iNzM6OTQtNzM6MTA4IiBsb3dDbG9zZWQ9InRydWUiIGhpZ2hDbG9zZWQ9InRydWUiIHhzaTp0eXBlPSJJbnRlcnZhbCI+DQogICAgICAgICAgICAgICAgICAgICAgICA8bG93IGxvY2F0b3I9IjczOjEyMy03MzoxNDkiIHhzaTp0eXBlPSJTdWJ0cmFjdCI+DQogICAgICAgICAgICAgICAgICAgICAgICAgICA8b3BlcmFuZCBsb2NhbElkPSI4MiIgbG9jYXRvcj0iNzM6MTIzLTczOjE0OSIgeHNpOnR5cGU9IkVuZCI+DQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8b3BlcmFuZCBsb2NhbElkPSI4MSIgbG9jYXRvcj0iNzM6MTMwLTczOjE0OSIgbmFtZT0iTWVhc3VyZW1lbnQgUGVyaW9kIiB4c2k6dHlwZT0iUGFyYW1ldGVyUmVmIi8+DQogICAgICAgICAgICAgICAgICAgICAgICAgICA8L29wZXJhbmQ+DQogICAgICAgICAgICAgICAgICAgICAgICAgICA8b3BlcmFuZCBsb2NhbElkPSI4MyIgbG9jYXRvcj0iNzM6OTQtNzM6MTAwIiB2YWx1ZT0iNSIgdW5pdD0ieWVhcnMiIHhzaTp0eXBlPSJRdWFudGl0eSIvPg0KICAgICAgICAgICAgICAgICAgICAgICAgPC9sb3c+DQogICAgICAgICAgICAgICAgICAgICAgICA8aGlnaCBsb2NhbElkPSI4MiIgbG9jYXRvcj0iNzM6MTIzLTczOjE0OSIgeHNpOnR5cGU9IkVuZCI+DQogICAgICAgICAgICAgICAgICAgICAgICAgICA8b3BlcmFuZCBsb2NhbElkPSI4MSIgbG9jYXRvcj0iNzM6MTMwLTczOjE0OSIgbmFtZT0iTWVhc3VyZW1lbnQgUGVyaW9kIiB4c2k6dHlwZT0iUGFyYW1ldGVyUmVmIi8+DQogICAgICAgICAgICAgICAgICAgICAgICA8L2hpZ2g+DQogICAgICAgICAgICAgICAgICAgICA8L29wZXJhbmQ+DQogICAgICAgICAgICAgICAgICA8L29wZXJhbmQ+DQogICAgICAgICAgICAgICAgICA8b3BlcmFuZCBsb2NhdG9yPSI3Mzo5NC03MzoxMDgiIHhzaTp0eXBlPSJOb3QiPg0KICAgICAgICAgICAgICAgICAgICAgPG9wZXJhbmQgbG9jYXRvcj0iNzM6OTQtNzM6MTA4IiB4c2k6dHlwZT0iSXNOdWxsIj4NCiAgICAgICAgICAgICAgICAgICAgICAgIDxvcGVyYW5kIGxvY2FsSWQ9IjgyIiBsb2NhdG9yPSI3MzoxMjMtNzM6MTQ5IiB4c2k6dHlwZT0iRW5kIj4NCiAgICAgICAgICAgICAgICAgICAgICAgICAgIDxvcGVyYW5kIGxvY2FsSWQ9IjgxIiBsb2NhdG9yPSI3MzoxMzAtNzM6MTQ5IiBuYW1lPSJNZWFzdXJlbWVudCBQZXJpb2QiIHhzaTp0eXBlPSJQYXJhbWV0ZXJSZWYiLz4NCiAgICAgICAgICAgICAgICAgICAgICAgIDwvb3BlcmFuZD4NCiAgICAgICAgICAgICAgICAgICAgIDwvb3BlcmFuZD4NCiAgICAgICAgICAgICAgICAgIDwvb3BlcmFuZD4NCiAgICAgICAgICAgICAgIDwvb3BlcmFuZD4NCiAgICAgICAgICAgIDwvd2hlcmU+DQogICAgICAgICA8L2V4cHJlc3Npb24+DQogICAgICA8L2RlZj4NCiAgICAgIDxkZWYgbG9jYWxJZD0iMTA0IiBsb2NhdG9yPSI3NToxLTc4OjE0MCIgbmFtZT0iQ1QgQ29sb25vZ3JhcGh5IFBlcmZvcm1lZCIgY29udGV4dD0iUGF0aWVudCIgYWNjZXNzTGV2ZWw9IlB1YmxpYyI+DQogICAgICAgICA8YW5ub3RhdGlvbiB4c2k6dHlwZT0iYTpBbm5vdGF0aW9uIj4NCiAgICAgICAgICAgIDxhOnMgcj0iMTA0Ij4NCiAgICAgICAgICAgICAgIDxhOnM+ZGVmaW5lICZxdW90O0NUIENvbG9ub2dyYXBoeSBQZXJmb3JtZWQmcXVvdDs6CiAgPC9hOnM+DQogICAgICAgICAgICAgICA8YTpzIHI9IjEwMyI+DQogICAgICAgICAgICAgICAgICA8YTpzPg0KICAgICAgICAgICAgICAgICAgICAgPGE6cyByPSI4OSI+DQogICAgICAgICAgICAgICAgICAgICAgICA8YTpzIHI9Ijg4Ij4NCiAgICAgICAgICAgICAgICAgICAgICAgICAgIDxhOnMgcj0iODgiPg0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPGE6cz5bUHJvY2VkdXJlOiA8L2E6cz4NCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDxhOnM+DQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8YTpzPiZxdW90O0NUIENvbG9ub2dyYXBoeSZxdW90OzwvYTpzPg0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPC9hOnM+DQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8YTpzPl08L2E6cz4NCiAgICAgICAgICAgICAgICAgICAgICAgICAgIDwvYTpzPg0KICAgICAgICAgICAgICAgICAgICAgICAgPC9hOnM+DQogICAgICAgICAgICAgICAgICAgICAgICA8YTpzPiBDb2xvbm9ncmFwaHk8L2E6cz4NCiAgICAgICAgICAgICAgICAgICAgIDwvYTpzPg0KICAgICAgICAgICAgICAgICAgPC9hOnM+DQogICAgICAgICAgICAgICAgICA8YTpzPgogICAgICAgICAgICAgICAgICAgIAkJPC9hOnM+DQogICAgICAgICAgICAgICAgICA8YTpzIHI9IjEwMiI+DQogICAgICAgICAgICAgICAgICAgICA8YTpzPndoZXJlIDwvYTpzPg0KICAgICAgICAgICAgICAgICAgICAgPGE6cyByPSIxMDIiPg0KICAgICAgICAgICAgICAgICAgICAgICAgPGE6cyByPSI5MyI+DQogICAgICAgICAgICAgICAgICAgICAgICAgICA8YTpzIHI9IjkxIj4NCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDxhOnMgcj0iOTAiPg0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPGE6cz5Db2xvbm9ncmFwaHk8L2E6cz4NCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDwvYTpzPg0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPGE6cz4uPC9hOnM+DQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8YTpzIHI9IjkxIj4NCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDxhOnM+c3RhdHVzPC9hOnM+DQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8L2E6cz4NCiAgICAgICAgICAgICAgICAgICAgICAgICAgIDwvYTpzPg0KICAgICAgICAgICAgICAgICAgICAgICAgICAgPGE6cz4gPSA8L2E6cz4NCiAgICAgICAgICAgICAgICAgICAgICAgICAgIDxhOnMgcj0iOTIiPg0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPGE6cz4nY29tcGxldGVkJzwvYTpzPg0KICAgICAgICAgICAgICAgICAgICAgICAgICAgPC9hOnM+DQogICAgICAgICAgICAgICAgICAgICAgICA8L2E6cz4NCiAgICAgICAgICAgICAgICAgICAgICAgIDxhOnM+CiAgICAgICAgICAgICAgICAgICAgCQkJYW5kIDwvYTpzPg0KICAgICAgICAgICAgICAgICAgICAgICAgPGE6cyByPSIxMDEiPg0KICAgICAgICAgICAgICAgICAgICAgICAgICAgPGE6cyByPSI5NyI+DQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8YTpzIHI9Ijk0Ij4NCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDxhOnM+R2xvYmFsPC9hOnM+DQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8L2E6cz4NCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDxhOnM+LjwvYTpzPg0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPGE6cyByPSI5NyI+DQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8YTpzPiZxdW90O05vcm1hbGl6ZSBJbnRlcnZhbCZxdW90Oyg8L2E6cz4NCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDxhOnMgcj0iOTYiPg0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPGE6cyByPSI5NSI+DQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8YTpzPkNvbG9ub2dyYXBoeTwvYTpzPg0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPC9hOnM+DQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8YTpzPi48L2E6cz4NCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDxhOnMgcj0iOTYiPg0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPGE6cz5wZXJmb3JtZWQ8L2E6cz4NCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDwvYTpzPg0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPC9hOnM+DQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8YTpzPik8L2E6cz4NCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDwvYTpzPg0KICAgICAgICAgICAgICAgICAgICAgICAgICAgPC9hOnM+DQogICAgICAgICAgICAgICAgICAgICAgICAgICA8YTpzPiA8L2E6cz4NCiAgICAgICAgICAgICAgICAgICAgICAgICAgIDxhOnMgcj0iMTAxIj4NCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDxhOnM+ZW5kcyA8L2E6cz4NCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDxhOnMgcj0iMTAwIj4NCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDxhOnM+NSB5ZWFyczwvYTpzPg0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPC9hOnM+DQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8YTpzPiBvciBsZXNzIG9uIG9yIGJlZm9yZTwvYTpzPg0KICAgICAgICAgICAgICAgICAgICAgICAgICAgPC9hOnM+DQogICAgICAgICAgICAgICAgICAgICAgICAgICA8YTpzPiA8L2E6cz4NCiAgICAgICAgICAgICAgICAgICAgICAgICAgIDxhOnMgcj0iOTkiPg0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPGE6cz5lbmQgb2YgPC9hOnM+DQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8YTpzIHI9Ijk4Ij4NCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDxhOnM+JnF1b3Q7TWVhc3VyZW1lbnQgUGVyaW9kJnF1b3Q7PC9hOnM+DQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8L2E6cz4NCiAgICAgICAgICAgICAgICAgICAgICAgICAgIDwvYTpzPg0KICAgICAgICAgICAgICAgICAgICAgICAgPC9hOnM+DQogICAgICAgICAgICAgICAgICAgICA8L2E6cz4NCiAgICAgICAgICAgICAgICAgIDwvYTpzPg0KICAgICAgICAgICAgICAgPC9hOnM+DQogICAgICAgICAgICA8L2E6cz4NCiAgICAgICAgIDwvYW5ub3RhdGlvbj4NCiAgICAgICAgIDxleHByZXNzaW9uIGxvY2FsSWQ9IjEwMyIgbG9jYXRvcj0iNzY6My03ODoxNDAiIHhzaTp0eXBlPSJRdWVyeSI+DQogICAgICAgICAgICA8c291cmNlIGxvY2FsSWQ9Ijg5IiBsb2NhdG9yPSI3NjozLTc2OjQ1IiBhbGlhcz0iQ29sb25vZ3JhcGh5Ij4NCiAgICAgICAgICAgICAgIDxleHByZXNzaW9uIGxvY2FsSWQ9Ijg4IiBsb2NhdG9yPSI3NjozLTc2OjMyIiBkYXRhVHlwZT0iZmhpcjpQcm9jZWR1cmUiIHRlbXBsYXRlSWQ9Imh0dHA6Ly9obDcub3JnL2ZoaXIvU3RydWN0dXJlRGVmaW5pdGlvbi9Qcm9jZWR1cmUiIGNvZGVQcm9wZXJ0eT0iY29kZSIgY29kZUNvbXBhcmF0b3I9ImluIiB4c2k6dHlwZT0iUmV0cmlldmUiPg0KICAgICAgICAgICAgICAgICAgPGNvZGVzIGxvY2F0b3I9Ijc2OjE1LTc2OjMxIiBuYW1lPSJDVCBDb2xvbm9ncmFwaHkiIHhzaTp0eXBlPSJWYWx1ZVNldFJlZiIvPg0KICAgICAgICAgICAgICAgPC9leHByZXNzaW9uPg0KICAgICAgICAgICAgPC9zb3VyY2U+DQogICAgICAgICAgICA8d2hlcmUgbG9jYWxJZD0iMTAyIiBsb2NhdG9yPSI3NzoyMy03ODoxNDAiIHhzaTp0eXBlPSJBbmQiPg0KICAgICAgICAgICAgICAgPG9wZXJhbmQgbG9jYWxJZD0iOTMiIGxvY2F0b3I9Ijc3OjI5LTc3OjYxIiB4c2k6dHlwZT0iRXF1YWwiPg0KICAgICAgICAgICAgICAgICAgPG9wZXJhbmQgbmFtZT0iVG9TdHJpbmciIGxpYnJhcnlOYW1lPSJGSElSSGVscGVycyIgeHNpOnR5cGU9IkZ1bmN0aW9uUmVmIj4NCiAgICAgICAgICAgICAgICAgICAgIDxvcGVyYW5kIGxvY2FsSWQ9IjkxIiBsb2NhdG9yPSI3NzoyOS03Nzo0NyIgcGF0aD0ic3RhdHVzIiBzY29wZT0iQ29sb25vZ3JhcGh5IiB4c2k6dHlwZT0iUHJvcGVydHkiLz4NCiAgICAgICAgICAgICAgICAgIDwvb3BlcmFuZD4NCiAgICAgICAgICAgICAgICAgIDxvcGVyYW5kIGxvY2FsSWQ9IjkyIiBsb2NhdG9yPSI3Nzo1MS03Nzo2MSIgdmFsdWVUeXBlPSJ0OlN0cmluZyIgdmFsdWU9ImNvbXBsZXRlZCIgeHNpOnR5cGU9IkxpdGVyYWwiLz4NCiAgICAgICAgICAgICAgIDwvb3BlcmFuZD4NCiAgICAgICAgICAgICAgIDxvcGVyYW5kIGxvY2FsSWQ9IjEwMSIgbG9jYXRvcj0iNzg6MjgtNzg6MTQwIiB4c2k6dHlwZT0iQW5kIj4NCiAgICAgICAgICAgICAgICAgIDxvcGVyYW5kIGxvY2F0b3I9Ijc4Ojg1LTc4Ojk5IiB4c2k6dHlwZT0iSW4iPg0KICAgICAgICAgICAgICAgICAgICAgPG9wZXJhbmQgbG9jYXRvcj0iNzg6ODAtNzg6ODMiIHhzaTp0eXBlPSJFbmQiPg0KICAgICAgICAgICAgICAgICAgICAgICAgPG9wZXJhbmQgbG9jYWxJZD0iOTciIGxvY2F0b3I9Ijc4OjI4LTc4Ojc4IiBuYW1lPSJOb3JtYWxpemUgSW50ZXJ2YWwiIGxpYnJhcnlOYW1lPSJHbG9iYWwiIHhzaTp0eXBlPSJGdW5jdGlvblJlZiI+DQogICAgICAgICAgICAgICAgICAgICAgICAgICA8b3BlcmFuZCBsb2NhbElkPSI5NiIgbG9jYXRvcj0iNzg6NTYtNzg6NzciIHBhdGg9InBlcmZvcm1lZCIgc2NvcGU9IkNvbG9ub2dyYXBoeSIgeHNpOnR5cGU9IlByb3BlcnR5Ii8+DQogICAgICAgICAgICAgICAgICAgICAgICA8L29wZXJhbmQ+DQogICAgICAgICAgICAgICAgICAgICA8L29wZXJhbmQ+DQogICAgICAgICAgICAgICAgICAgICA8b3BlcmFuZCBsb2NhdG9yPSI3ODo4NS03ODo5OSIgbG93Q2xvc2VkPSJ0cnVlIiBoaWdoQ2xvc2VkPSJ0cnVlIiB4c2k6dHlwZT0iSW50ZXJ2YWwiPg0KICAgICAgICAgICAgICAgICAgICAgICAgPGxvdyBsb2NhdG9yPSI3ODoxMTQtNzg6MTQwIiB4c2k6dHlwZT0iU3VidHJhY3QiPg0KICAgICAgICAgICAgICAgICAgICAgICAgICAgPG9wZXJhbmQgbG9jYWxJZD0iOTkiIGxvY2F0b3I9Ijc4OjExNC03ODoxNDAiIHhzaTp0eXBlPSJFbmQiPg0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPG9wZXJhbmQgbG9jYWxJZD0iOTgiIGxvY2F0b3I9Ijc4OjEyMS03ODoxNDAiIG5hbWU9Ik1lYXN1cmVtZW50IFBlcmlvZCIgeHNpOnR5cGU9IlBhcmFtZXRlclJlZiIvPg0KICAgICAgICAgICAgICAgICAgICAgICAgICAgPC9vcGVyYW5kPg0KICAgICAgICAgICAgICAgICAgICAgICAgICAgPG9wZXJhbmQgbG9jYWxJZD0iMTAwIiBsb2NhdG9yPSI3ODo4NS03ODo5MSIgdmFsdWU9IjUiIHVuaXQ9InllYXJzIiB4c2k6dHlwZT0iUXVhbnRpdHkiLz4NCiAgICAgICAgICAgICAgICAgICAgICAgIDwvbG93Pg0KICAgICAgICAgICAgICAgICAgICAgICAgPGhpZ2ggbG9jYWxJZD0iOTkiIGxvY2F0b3I9Ijc4OjExNC03ODoxNDAiIHhzaTp0eXBlPSJFbmQiPg0KICAgICAgICAgICAgICAgICAgICAgICAgICAgPG9wZXJhbmQgbG9jYWxJZD0iOTgiIGxvY2F0b3I9Ijc4OjEyMS03ODoxNDAiIG5hbWU9Ik1lYXN1cmVtZW50IFBlcmlvZCIgeHNpOnR5cGU9IlBhcmFtZXRlclJlZiIvPg0KICAgICAgICAgICAgICAgICAgICAgICAgPC9oaWdoPg0KICAgICAgICAgICAgICAgICAgICAgPC9vcGVyYW5kPg0KICAgICAgICAgICAgICAgICAgPC9vcGVyYW5kPg0KICAgICAgICAgICAgICAgICAgPG9wZXJhbmQgbG9jYXRvcj0iNzg6ODUtNzg6OTkiIHhzaTp0eXBlPSJOb3QiPg0KICAgICAgICAgICAgICAgICAgICAgPG9wZXJhbmQgbG9jYXRvcj0iNzg6ODUtNzg6OTkiIHhzaTp0eXBlPSJJc051bGwiPg0KICAgICAgICAgICAgICAgICAgICAgICAgPG9wZXJhbmQgbG9jYWxJZD0iOTkiIGxvY2F0b3I9Ijc4OjExNC03ODoxNDAiIHhzaTp0eXBlPSJFbmQiPg0KICAgICAgICAgICAgICAgICAgICAgICAgICAgPG9wZXJhbmQgbG9jYWxJZD0iOTgiIGxvY2F0b3I9Ijc4OjEyMS03ODoxNDAiIG5hbWU9Ik1lYXN1cmVtZW50IFBlcmlvZCIgeHNpOnR5cGU9IlBhcmFtZXRlclJlZiIvPg0KICAgICAgICAgICAgICAgICAgICAgICAgPC9vcGVyYW5kPg0KICAgICAgICAgICAgICAgICAgICAgPC9vcGVyYW5kPg0KICAgICAgICAgICAgICAgICAgPC9vcGVyYW5kPg0KICAgICAgICAgICAgICAgPC9vcGVyYW5kPg0KICAgICAgICAgICAgPC93aGVyZT4NCiAgICAgICAgIDwvZXhwcmVzc2lvbj4NCiAgICAgIDwvZGVmPg0KICAgICAgPGRlZiBsb2NhbElkPSIxMjAiIGxvY2F0b3I9IjgwOjEtODM6MTIzIiBuYW1lPSJUb3RhbCBDb2xlY3RvbXkgUGVyZm9ybWVkIiBjb250ZXh0PSJQYXRpZW50IiBhY2Nlc3NMZXZlbD0iUHVibGljIj4NCiAgICAgICAgIDxhbm5vdGF0aW9uIHhzaTp0eXBlPSJhOkFubm90YXRpb24iPg0KICAgICAgICAgICAgPGE6cyByPSIxMjAiPg0KICAgICAgICAgICAgICAgPGE6cz5kZWZpbmUgJnF1b3Q7VG90YWwgQ29sZWN0b215IFBlcmZvcm1lZCZxdW90OzoKICA8L2E6cz4NCiAgICAgICAgICAgICAgIDxhOnMgcj0iMTE5Ij4NCiAgICAgICAgICAgICAgICAgIDxhOnM+DQogICAgICAgICAgICAgICAgICAgICA8YTpzIHI9IjEwNiI+DQogICAgICAgICAgICAgICAgICAgICAgICA8YTpzIHI9IjEwNSI+DQogICAgICAgICAgICAgICAgICAgICAgICAgICA8YTpzIHI9IjEwNSI+DQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8YTpzPltQcm9jZWR1cmU6IDwvYTpzPg0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPGE6cz4NCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDxhOnM+JnF1b3Q7VG90YWwgQ29sZWN0b215JnF1b3Q7PC9hOnM+DQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8L2E6cz4NCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDxhOnM+XTwvYTpzPg0KICAgICAgICAgICAgICAgICAgICAgICAgICAgPC9hOnM+DQogICAgICAgICAgICAgICAgICAgICAgICA8L2E6cz4NCiAgICAgICAgICAgICAgICAgICAgICAgIDxhOnM+IENvbGVjdG9teTwvYTpzPg0KICAgICAgICAgICAgICAgICAgICAgPC9hOnM+DQogICAgICAgICAgICAgICAgICA8L2E6cz4NCiAgICAgICAgICAgICAgICAgIDxhOnM+CiAgICAgICAgICAgICAgICAgICAgCQk8L2E6cz4NCiAgICAgICAgICAgICAgICAgIDxhOnMgcj0iMTE4Ij4NCiAgICAgICAgICAgICAgICAgICAgIDxhOnM+d2hlcmUgPC9hOnM+DQogICAgICAgICAgICAgICAgICAgICA8YTpzIHI9IjExOCI+DQogICAgICAgICAgICAgICAgICAgICAgICA8YTpzIHI9IjExMCI+DQogICAgICAgICAgICAgICAgICAgICAgICAgICA8YTpzIHI9IjEwOCI+DQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8YTpzIHI9IjEwNyI+DQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8YTpzPkNvbGVjdG9teTwvYTpzPg0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPC9hOnM+DQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8YTpzPi48L2E6cz4NCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDxhOnMgcj0iMTA4Ij4NCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDxhOnM+c3RhdHVzPC9hOnM+DQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8L2E6cz4NCiAgICAgICAgICAgICAgICAgICAgICAgICAgIDwvYTpzPg0KICAgICAgICAgICAgICAgICAgICAgICAgICAgPGE6cz4gPSA8L2E6cz4NCiAgICAgICAgICAgICAgICAgICAgICAgICAgIDxhOnMgcj0iMTA5Ij4NCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDxhOnM+J2NvbXBsZXRlZCc8L2E6cz4NCiAgICAgICAgICAgICAgICAgICAgICAgICAgIDwvYTpzPg0KICAgICAgICAgICAgICAgICAgICAgICAgPC9hOnM+DQogICAgICAgICAgICAgICAgICAgICAgICA8YTpzPgogICAgICAgICAgICAgICAgICAgIAkJCWFuZCA8L2E6cz4NCiAgICAgICAgICAgICAgICAgICAgICAgIDxhOnMgcj0iMTE3Ij4NCiAgICAgICAgICAgICAgICAgICAgICAgICAgIDxhOnMgcj0iMTE0Ij4NCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDxhOnMgcj0iMTExIj4NCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDxhOnM+R2xvYmFsPC9hOnM+DQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8L2E6cz4NCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDxhOnM+LjwvYTpzPg0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPGE6cyByPSIxMTQiPg0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPGE6cz4mcXVvdDtOb3JtYWxpemUgSW50ZXJ2YWwmcXVvdDsoPC9hOnM+DQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8YTpzIHI9IjExMyI+DQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8YTpzIHI9IjExMiI+DQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8YTpzPkNvbGVjdG9teTwvYTpzPg0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPC9hOnM+DQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8YTpzPi48L2E6cz4NCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDxhOnMgcj0iMTEzIj4NCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDxhOnM+cGVyZm9ybWVkPC9hOnM+DQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8L2E6cz4NCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDwvYTpzPg0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPGE6cz4pPC9hOnM+DQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8L2E6cz4NCiAgICAgICAgICAgICAgICAgICAgICAgICAgIDwvYTpzPg0KICAgICAgICAgICAgICAgICAgICAgICAgICAgPGE6cyByPSIxMTciPiBzdGFydHMgb24gb3IgYmVmb3JlIDwvYTpzPg0KICAgICAgICAgICAgICAgICAgICAgICAgICAgPGE6cyByPSIxMTYiPg0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPGE6cz5lbmQgb2YgPC9hOnM+DQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8YTpzIHI9IjExNSI+DQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8YTpzPiZxdW90O01lYXN1cmVtZW50IFBlcmlvZCZxdW90OzwvYTpzPg0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPC9hOnM+DQogICAgICAgICAgICAgICAgICAgICAgICAgICA8L2E6cz4NCiAgICAgICAgICAgICAgICAgICAgICAgIDwvYTpzPg0KICAgICAgICAgICAgICAgICAgICAgPC9hOnM+DQogICAgICAgICAgICAgICAgICA8L2E6cz4NCiAgICAgICAgICAgICAgIDwvYTpzPg0KICAgICAgICAgICAgPC9hOnM+DQogICAgICAgICA8L2Fubm90YXRpb24+DQogICAgICAgICA8ZXhwcmVzc2lvbiBsb2NhbElkPSIxMTkiIGxvY2F0b3I9IjgxOjMtODM6MTIzIiB4c2k6dHlwZT0iUXVlcnkiPg0KICAgICAgICAgICAgPHNvdXJjZSBsb2NhbElkPSIxMDYiIGxvY2F0b3I9IjgxOjMtODE6NDIiIGFsaWFzPSJDb2xlY3RvbXkiPg0KICAgICAgICAgICAgICAgPGV4cHJlc3Npb24gbG9jYWxJZD0iMTA1IiBsb2NhdG9yPSI4MTozLTgxOjMyIiBkYXRhVHlwZT0iZmhpcjpQcm9jZWR1cmUiIHRlbXBsYXRlSWQ9Imh0dHA6Ly9obDcub3JnL2ZoaXIvU3RydWN0dXJlRGVmaW5pdGlvbi9Qcm9jZWR1cmUiIGNvZGVQcm9wZXJ0eT0iY29kZSIgY29kZUNvbXBhcmF0b3I9ImluIiB4c2k6dHlwZT0iUmV0cmlldmUiPg0KICAgICAgICAgICAgICAgICAgPGNvZGVzIGxvY2F0b3I9IjgxOjE1LTgxOjMxIiBuYW1lPSJUb3RhbCBDb2xlY3RvbXkiIHhzaTp0eXBlPSJWYWx1ZVNldFJlZiIvPg0KICAgICAgICAgICAgICAgPC9leHByZXNzaW9uPg0KICAgICAgICAgICAgPC9zb3VyY2U+DQogICAgICAgICAgICA8d2hlcmUgbG9jYWxJZD0iMTE4IiBsb2NhdG9yPSI4MjoyMy04MzoxMjMiIHhzaTp0eXBlPSJBbmQiPg0KICAgICAgICAgICAgICAgPG9wZXJhbmQgbG9jYWxJZD0iMTEwIiBsb2NhdG9yPSI4MjoyOS04Mjo1OCIgeHNpOnR5cGU9IkVxdWFsIj4NCiAgICAgICAgICAgICAgICAgIDxvcGVyYW5kIG5hbWU9IlRvU3RyaW5nIiBsaWJyYXJ5TmFtZT0iRkhJUkhlbHBlcnMiIHhzaTp0eXBlPSJGdW5jdGlvblJlZiI+DQogICAgICAgICAgICAgICAgICAgICA8b3BlcmFuZCBsb2NhbElkPSIxMDgiIGxvY2F0b3I9IjgyOjI5LTgyOjQ0IiBwYXRoPSJzdGF0dXMiIHNjb3BlPSJDb2xlY3RvbXkiIHhzaTp0eXBlPSJQcm9wZXJ0eSIvPg0KICAgICAgICAgICAgICAgICAgPC9vcGVyYW5kPg0KICAgICAgICAgICAgICAgICAgPG9wZXJhbmQgbG9jYWxJZD0iMTA5IiBsb2NhdG9yPSI4Mjo0OC04Mjo1OCIgdmFsdWVUeXBlPSJ0OlN0cmluZyIgdmFsdWU9ImNvbXBsZXRlZCIgeHNpOnR5cGU9IkxpdGVyYWwiLz4NCiAgICAgICAgICAgICAgIDwvb3BlcmFuZD4NCiAgICAgICAgICAgICAgIDxvcGVyYW5kIGxvY2FsSWQ9IjExNyIgbG9jYXRvcj0iODM6MjgtODM6MTIzIiB4c2k6dHlwZT0iU2FtZU9yQmVmb3JlIj4NCiAgICAgICAgICAgICAgICAgIDxvcGVyYW5kIGxvY2F0b3I9IjgzOjc3LTgzOjgyIiB4c2k6dHlwZT0iU3RhcnQiPg0KICAgICAgICAgICAgICAgICAgICAgPG9wZXJhbmQgbG9jYWxJZD0iMTE0IiBsb2NhdG9yPSI4MzoyOC04Mzo3NSIgbmFtZT0iTm9ybWFsaXplIEludGVydmFsIiBsaWJyYXJ5TmFtZT0iR2xvYmFsIiB4c2k6dHlwZT0iRnVuY3Rpb25SZWYiPg0KICAgICAgICAgICAgICAgICAgICAgICAgPG9wZXJhbmQgbG9jYWxJZD0iMTEzIiBsb2NhdG9yPSI4Mzo1Ni04Mzo3NCIgcGF0aD0icGVyZm9ybWVkIiBzY29wZT0iQ29sZWN0b215IiB4c2k6dHlwZT0iUHJvcGVydHkiLz4NCiAgICAgICAgICAgICAgICAgICAgIDwvb3BlcmFuZD4NCiAgICAgICAgICAgICAgICAgIDwvb3BlcmFuZD4NCiAgICAgICAgICAgICAgICAgIDxvcGVyYW5kIGxvY2FsSWQ9IjExNiIgbG9jYXRvcj0iODM6OTctODM6MTIzIiB4c2k6dHlwZT0iRW5kIj4NCiAgICAgICAgICAgICAgICAgICAgIDxvcGVyYW5kIGxvY2FsSWQ9IjExNSIgbG9jYXRvcj0iODM6MTA0LTgzOjEyMyIgbmFtZT0iTWVhc3VyZW1lbnQgUGVyaW9kIiB4c2k6dHlwZT0iUGFyYW1ldGVyUmVmIi8+DQogICAgICAgICAgICAgICAgICA8L29wZXJhbmQ+DQogICAgICAgICAgICAgICA8L29wZXJhbmQ+DQogICAgICAgICAgICA8L3doZXJlPg0KICAgICAgICAgPC9leHByZXNzaW9uPg0KICAgICAgPC9kZWY+DQogICAgICA8ZGVmIGxvY2FsSWQ9IjE0MyIgbG9jYXRvcj0iODU6MS04OToxMTgiIG5hbWU9IkZlY2FsIE9jY3VsdCBCbG9vZCBUZXN0IFBlcmZvcm1lZCIgY29udGV4dD0iUGF0aWVudCIgYWNjZXNzTGV2ZWw9IlB1YmxpYyI+DQogICAgICAgICA8YW5ub3RhdGlvbiB4c2k6dHlwZT0iYTpBbm5vdGF0aW9uIj4NCiAgICAgICAgICAgIDxhOnMgcj0iMTQzIj4NCiAgICAgICAgICAgICAgIDxhOnM+ZGVmaW5lICZxdW90O0ZlY2FsIE9jY3VsdCBCbG9vZCBUZXN0IFBlcmZvcm1lZCZxdW90OzoKICA8L2E6cz4NCiAgICAgICAgICAgICAgIDxhOnMgcj0iMTQyIj4NCiAgICAgICAgICAgICAgICAgIDxhOnM+DQogICAgICAgICAgICAgICAgICAgICA8YTpzIHI9IjEyMiI+DQogICAgICAgICAgICAgICAgICAgICAgICA8YTpzIHI9IjEyMSI+DQogICAgICAgICAgICAgICAgICAgICAgICAgICA8YTpzIHI9IjEyMSI+DQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8YTpzPltPYnNlcnZhdGlvbjogPC9hOnM+DQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8YTpzPg0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPGE6cz4mcXVvdDtGZWNhbCBPY2N1bHQgQmxvb2QgVGVzdCAoRk9CVCkmcXVvdDs8L2E6cz4NCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDwvYTpzPg0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPGE6cz5dPC9hOnM+DQogICAgICAgICAgICAgICAgICAgICAgICAgICA8L2E6cz4NCiAgICAgICAgICAgICAgICAgICAgICAgIDwvYTpzPg0KICAgICAgICAgICAgICAgICAgICAgICAgPGE6cz4gRmVjYWxPY2N1bHRSZXN1bHQ8L2E6cz4NCiAgICAgICAgICAgICAgICAgICAgIDwvYTpzPg0KICAgICAgICAgICAgICAgICAgPC9hOnM+DQogICAgICAgICAgICAgICAgICA8YTpzPgogICAgICAgICAgICAgICAgICAgIAkJPC9hOnM+DQogICAgICAgICAgICAgICAgICA8YTpzIHI9IjE0MSI+DQogICAgICAgICAgICAgICAgICAgICA8YTpzPndoZXJlIDwvYTpzPg0KICAgICAgICAgICAgICAgICAgICAgPGE6cyByPSIxNDEiPg0KICAgICAgICAgICAgICAgICAgICAgICAgPGE6cyByPSIxMzQiPg0KICAgICAgICAgICAgICAgICAgICAgICAgICAgPGE6cyByPSIxMzAiPg0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPGE6cyByPSIxMjQiPg0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPGE6cyByPSIxMjMiPg0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPGE6cz5GZWNhbE9jY3VsdFJlc3VsdDwvYTpzPg0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPC9hOnM+DQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8YTpzPi48L2E6cz4NCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDxhOnMgcj0iMTI0Ij4NCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDxhOnM+c3RhdHVzPC9hOnM+DQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8L2E6cz4NCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDwvYTpzPg0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPGE6cz4gaW4gPC9hOnM+DQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8YTpzIHI9IjEyOSI+DQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8YTpzPns8L2E6cz4NCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDxhOnMgcj0iMTI1Ij4NCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDxhOnM+J2ZpbmFsJzwvYTpzPg0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPC9hOnM+DQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8YTpzPiwgPC9hOnM+DQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8YTpzIHI9IjEyNiI+DQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8YTpzPidhbWVuZGVkJzwvYTpzPg0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPC9hOnM+DQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8YTpzPiwgPC9hOnM+DQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8YTpzIHI9IjEyNyI+DQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8YTpzPidjb3JyZWN0ZWQnPC9hOnM+DQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8L2E6cz4NCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDxhOnM+LCA8L2E6cz4NCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDxhOnMgcj0iMTI4Ij4NCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDxhOnM+J3ByZWxpbWluYXJ5JzwvYTpzPg0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPC9hOnM+DQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8YTpzPn08L2E6cz4NCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDwvYTpzPg0KICAgICAgICAgICAgICAgICAgICAgICAgICAgPC9hOnM+DQogICAgICAgICAgICAgICAgICAgICAgICAgICA8YTpzPgogICAgICAgICAgICAgICAgICAgIAkJCWFuZCA8L2E6cz4NCiAgICAgICAgICAgICAgICAgICAgICAgICAgIDxhOnMgcj0iMTMzIj4NCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDxhOnMgcj0iMTMyIj4NCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDxhOnMgcj0iMTMxIj4NCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDxhOnM+RmVjYWxPY2N1bHRSZXN1bHQ8L2E6cz4NCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDwvYTpzPg0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPGE6cz4uPC9hOnM+DQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8YTpzIHI9IjEzMiI+DQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8YTpzPnZhbHVlPC9hOnM+DQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8L2E6cz4NCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDwvYTpzPg0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPGE6cz4gaXMgbm90IG51bGw8L2E6cz4NCiAgICAgICAgICAgICAgICAgICAgICAgICAgIDwvYTpzPg0KICAgICAgICAgICAgICAgICAgICAgICAgPC9hOnM+DQogICAgICAgICAgICAgICAgICAgICAgICA8YTpzPgogICAgICAgICAgICAgICAgICAgIAkJCWFuZCA8L2E6cz4NCiAgICAgICAgICAgICAgICAgICAgICAgIDxhOnMgcj0iMTQwIj4NCiAgICAgICAgICAgICAgICAgICAgICAgICAgIDxhOnMgcj0iMTM4Ij4NCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDxhOnMgcj0iMTM1Ij4NCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDxhOnM+R2xvYmFsPC9hOnM+DQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8L2E6cz4NCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDxhOnM+LjwvYTpzPg0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPGE6cyByPSIxMzgiPg0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPGE6cz4mcXVvdDtOb3JtYWxpemUgSW50ZXJ2YWwmcXVvdDsoPC9hOnM+DQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8YTpzIHI9IjEzNyI+DQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8YTpzIHI9IjEzNiI+DQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8YTpzPkZlY2FsT2NjdWx0UmVzdWx0PC9hOnM+DQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8L2E6cz4NCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDxhOnM+LjwvYTpzPg0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPGE6cyByPSIxMzciPg0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPGE6cz5lZmZlY3RpdmU8L2E6cz4NCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDwvYTpzPg0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPC9hOnM+DQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8YTpzPik8L2E6cz4NCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDwvYTpzPg0KICAgICAgICAgICAgICAgICAgICAgICAgICAgPC9hOnM+DQogICAgICAgICAgICAgICAgICAgICAgICAgICA8YTpzIHI9IjE0MCI+IGR1cmluZyBkYXkgb2YgPC9hOnM+DQogICAgICAgICAgICAgICAgICAgICAgICAgICA8YTpzIHI9IjEzOSI+DQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8YTpzPiZxdW90O01lYXN1cmVtZW50IFBlcmlvZCZxdW90OzwvYTpzPg0KICAgICAgICAgICAgICAgICAgICAgICAgICAgPC9hOnM+DQogICAgICAgICAgICAgICAgICAgICAgICA8L2E6cz4NCiAgICAgICAgICAgICAgICAgICAgIDwvYTpzPg0KICAgICAgICAgICAgICAgICAgPC9hOnM+DQogICAgICAgICAgICAgICA8L2E6cz4NCiAgICAgICAgICAgIDwvYTpzPg0KICAgICAgICAgPC9hbm5vdGF0aW9uPg0KICAgICAgICAgPGV4cHJlc3Npb24gbG9jYWxJZD0iMTQyIiBsb2NhdG9yPSI4NjozLTg5OjExOCIgeHNpOnR5cGU9IlF1ZXJ5Ij4NCiAgICAgICAgICAgIDxzb3VyY2UgbG9jYWxJZD0iMTIyIiBsb2NhdG9yPSI4NjozLTg2OjY3IiBhbGlhcz0iRmVjYWxPY2N1bHRSZXN1bHQiPg0KICAgICAgICAgICAgICAgPGV4cHJlc3Npb24gbG9jYWxJZD0iMTIxIiBsb2NhdG9yPSI4NjozLTg2OjQ5IiBkYXRhVHlwZT0iZmhpcjpPYnNlcnZhdGlvbiIgdGVtcGxhdGVJZD0iaHR0cDovL2hsNy5vcmcvZmhpci9TdHJ1Y3R1cmVEZWZpbml0aW9uL09ic2VydmF0aW9uIiBjb2RlUHJvcGVydHk9ImNvZGUiIGNvZGVDb21wYXJhdG9yPSJpbiIgeHNpOnR5cGU9IlJldHJpZXZlIj4NCiAgICAgICAgICAgICAgICAgIDxjb2RlcyBsb2NhdG9yPSI4NjoxNy04Njo0OCIgbmFtZT0iRmVjYWwgT2NjdWx0IEJsb29kIFRlc3QgKEZPQlQpIiB4c2k6dHlwZT0iVmFsdWVTZXRSZWYiLz4NCiAgICAgICAgICAgICAgIDwvZXhwcmVzc2lvbj4NCiAgICAgICAgICAgIDwvc291cmNlPg0KICAgICAgICAgICAgPHdoZXJlIGxvY2FsSWQ9IjE0MSIgbG9jYXRvcj0iODc6MjMtODk6MTE4IiB4c2k6dHlwZT0iQW5kIj4NCiAgICAgICAgICAgICAgIDxvcGVyYW5kIGxvY2FsSWQ9IjEzNCIgbG9jYXRvcj0iODc6MjktODg6NjIiIHhzaTp0eXBlPSJBbmQiPg0KICAgICAgICAgICAgICAgICAgPG9wZXJhbmQgbG9jYWxJZD0iMTMwIiBsb2NhdG9yPSI4NzoyOS04NzoxMDQiIHhzaTp0eXBlPSJJbiI+DQogICAgICAgICAgICAgICAgICAgICA8b3BlcmFuZCBuYW1lPSJUb1N0cmluZyIgbGlicmFyeU5hbWU9IkZISVJIZWxwZXJzIiB4c2k6dHlwZT0iRnVuY3Rpb25SZWYiPg0KICAgICAgICAgICAgICAgICAgICAgICAgPG9wZXJhbmQgbG9jYWxJZD0iMTI0IiBsb2NhdG9yPSI4NzoyOS04Nzo1MiIgcGF0aD0ic3RhdHVzIiBzY29wZT0iRmVjYWxPY2N1bHRSZXN1bHQiIHhzaTp0eXBlPSJQcm9wZXJ0eSIvPg0KICAgICAgICAgICAgICAgICAgICAgPC9vcGVyYW5kPg0KICAgICAgICAgICAgICAgICAgICAgPG9wZXJhbmQgbG9jYWxJZD0iMTI5IiBsb2NhdG9yPSI4Nzo1Ny04NzoxMDQiIHhzaTp0eXBlPSJMaXN0Ij4NCiAgICAgICAgICAgICAgICAgICAgICAgIDxlbGVtZW50IGxvY2FsSWQ9IjEyNSIgbG9jYXRvcj0iODc6NTgtODc6NjQiIHZhbHVlVHlwZT0idDpTdHJpbmciIHZhbHVlPSJmaW5hbCIgeHNpOnR5cGU9IkxpdGVyYWwiLz4NCiAgICAgICAgICAgICAgICAgICAgICAgIDxlbGVtZW50IGxvY2FsSWQ9IjEyNiIgbG9jYXRvcj0iODc6NjctODc6NzUiIHZhbHVlVHlwZT0idDpTdHJpbmciIHZhbHVlPSJhbWVuZGVkIiB4c2k6dHlwZT0iTGl0ZXJhbCIvPg0KICAgICAgICAgICAgICAgICAgICAgICAgPGVsZW1lbnQgbG9jYWxJZD0iMTI3IiBsb2NhdG9yPSI4Nzo3OC04Nzo4OCIgdmFsdWVUeXBlPSJ0OlN0cmluZyIgdmFsdWU9ImNvcnJlY3RlZCIgeHNpOnR5cGU9IkxpdGVyYWwiLz4NCiAgICAgICAgICAgICAgICAgICAgICAgIDxlbGVtZW50IGxvY2FsSWQ9IjEyOCIgbG9jYXRvcj0iODc6OTEtODc6MTAzIiB2YWx1ZVR5cGU9InQ6U3RyaW5nIiB2YWx1ZT0icHJlbGltaW5hcnkiIHhzaTp0eXBlPSJMaXRlcmFsIi8+DQogICAgICAgICAgICAgICAgICAgICA8L29wZXJhbmQ+DQogICAgICAgICAgICAgICAgICA8L29wZXJhbmQ+DQogICAgICAgICAgICAgICAgICA8b3BlcmFuZCBsb2NhbElkPSIxMzMiIGxvY2F0b3I9Ijg4OjI4LTg4OjYyIiB4c2k6dHlwZT0iTm90Ij4NCiAgICAgICAgICAgICAgICAgICAgIDxvcGVyYW5kIGxvY2F0b3I9Ijg4OjI4LTg4OjYyIiB4c2k6dHlwZT0iSXNOdWxsIj4NCiAgICAgICAgICAgICAgICAgICAgICAgIDxvcGVyYW5kIGxvY2FsSWQ9IjEzMiIgbG9jYXRvcj0iODg6MjgtODg6NTAiIHBhdGg9InZhbHVlIiBzY29wZT0iRmVjYWxPY2N1bHRSZXN1bHQiIHhzaTp0eXBlPSJQcm9wZXJ0eSIvPg0KICAgICAgICAgICAgICAgICAgICAgPC9vcGVyYW5kPg0KICAgICAgICAgICAgICAgICAgPC9vcGVyYW5kPg0KICAgICAgICAgICAgICAgPC9vcGVyYW5kPg0KICAgICAgICAgICAgICAgPG9wZXJhbmQgbG9jYWxJZD0iMTQwIiBsb2NhdG9yPSI4OToyOC04OToxMTgiIHByZWNpc2lvbj0iRGF5IiB4c2k6dHlwZT0iSW5jbHVkZWRJbiI+DQogICAgICAgICAgICAgICAgICA8b3BlcmFuZCBsb2NhbElkPSIxMzgiIGxvY2F0b3I9Ijg5OjI4LTg5OjgzIiBuYW1lPSJOb3JtYWxpemUgSW50ZXJ2YWwiIGxpYnJhcnlOYW1lPSJHbG9iYWwiIHhzaTp0eXBlPSJGdW5jdGlvblJlZiI+DQogICAgICAgICAgICAgICAgICAgICA8b3BlcmFuZCBsb2NhbElkPSIxMzciIGxvY2F0b3I9Ijg5OjU2LTg5OjgyIiBwYXRoPSJlZmZlY3RpdmUiIHNjb3BlPSJGZWNhbE9jY3VsdFJlc3VsdCIgeHNpOnR5cGU9IlByb3BlcnR5Ii8+DQogICAgICAgICAgICAgICAgICA8L29wZXJhbmQ+DQogICAgICAgICAgICAgICAgICA8b3BlcmFuZCBsb2NhbElkPSIxMzkiIGxvY2F0b3I9Ijg5Ojk5LTg5OjExOCIgbmFtZT0iTWVhc3VyZW1lbnQgUGVyaW9kIiB4c2k6dHlwZT0iUGFyYW1ldGVyUmVmIi8+DQogICAgICAgICAgICAgICA8L29wZXJhbmQ+DQogICAgICAgICAgICA8L3doZXJlPg0KICAgICAgICAgPC9leHByZXNzaW9uPg0KICAgICAgPC9kZWY+DQogICAgICA8ZGVmIGxvY2FsSWQ9IjE2OCIgbG9jYXRvcj0iOTE6MS05NToxNDMiIG5hbWU9IkZlY2FsIEltbXVub2NoZW1pY2FsIFRlc3QgRE5BIiBjb250ZXh0PSJQYXRpZW50IiBhY2Nlc3NMZXZlbD0iUHVibGljIj4NCiAgICAgICAgIDxhbm5vdGF0aW9uIHhzaTp0eXBlPSJhOkFubm90YXRpb24iPg0KICAgICAgICAgICAgPGE6cyByPSIxNjgiPg0KICAgICAgICAgICAgICAgPGE6cz5kZWZpbmUgJnF1b3Q7RmVjYWwgSW1tdW5vY2hlbWljYWwgVGVzdCBETkEmcXVvdDs6CiAgPC9hOnM+DQogICAgICAgICAgICAgICA8YTpzIHI9IjE2NyI+DQogICAgICAgICAgICAgICAgICA8YTpzPg0KICAgICAgICAgICAgICAgICAgICAgPGE6cyByPSIxNDUiPg0KICAgICAgICAgICAgICAgICAgICAgICAgPGE6cyByPSIxNDQiPg0KICAgICAgICAgICAgICAgICAgICAgICAgICAgPGE6cyByPSIxNDQiPg0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPGE6cz5bT2JzZXJ2YXRpb246IDwvYTpzPg0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPGE6cz4NCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDxhOnM+JnF1b3Q7RklUIEROQSZxdW90OzwvYTpzPg0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPC9hOnM+DQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8YTpzPl08L2E6cz4NCiAgICAgICAgICAgICAgICAgICAgICAgICAgIDwvYTpzPg0KICAgICAgICAgICAgICAgICAgICAgICAgPC9hOnM+DQogICAgICAgICAgICAgICAgICAgICAgICA8YTpzPiBGaXRETkE8L2E6cz4NCiAgICAgICAgICAgICAgICAgICAgIDwvYTpzPg0KICAgICAgICAgICAgICAgICAgPC9hOnM+DQogICAgICAgICAgICAgICAgICA8YTpzPgogICAgICAgICAgICAgICAgICAgIAkJPC9hOnM+DQogICAgICAgICAgICAgICAgICA8YTpzIHI9IjE2NiI+DQogICAgICAgICAgICAgICAgICAgICA8YTpzPndoZXJlIDwvYTpzPg0KICAgICAgICAgICAgICAgICAgICAgPGE6cyByPSIxNjYiPg0KICAgICAgICAgICAgICAgICAgICAgICAgPGE6cyByPSIxNTciPg0KICAgICAgICAgICAgICAgICAgICAgICAgICAgPGE6cyByPSIxNTMiPg0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPGE6cyByPSIxNDciPg0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPGE6cyByPSIxNDYiPg0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPGE6cz5GaXRETkE8L2E6cz4NCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDwvYTpzPg0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPGE6cz4uPC9hOnM+DQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8YTpzIHI9IjE0NyI+DQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8YTpzPnN0YXR1czwvYTpzPg0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPC9hOnM+DQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8L2E6cz4NCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDxhOnM+IGluIDwvYTpzPg0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPGE6cyByPSIxNTIiPg0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPGE6cz57PC9hOnM+DQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8YTpzIHI9IjE0OCI+DQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8YTpzPidmaW5hbCc8L2E6cz4NCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDwvYTpzPg0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPGE6cz4sIDwvYTpzPg0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPGE6cyByPSIxNDkiPg0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPGE6cz4nYW1lbmRlZCc8L2E6cz4NCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDwvYTpzPg0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPGE6cz4sIDwvYTpzPg0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPGE6cyByPSIxNTAiPg0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPGE6cz4nY29ycmVjdGVkJzwvYTpzPg0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPC9hOnM+DQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8YTpzPiwgPC9hOnM+DQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8YTpzIHI9IjE1MSI+DQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8YTpzPidwcmVsaW1pbmFyeSc8L2E6cz4NCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDwvYTpzPg0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPGE6cz59PC9hOnM+DQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8L2E6cz4NCiAgICAgICAgICAgICAgICAgICAgICAgICAgIDwvYTpzPg0KICAgICAgICAgICAgICAgICAgICAgICAgICAgPGE6cz4KICAgICAgICAgICAgICAgICAgICAJCQlhbmQgPC9hOnM+DQogICAgICAgICAgICAgICAgICAgICAgICAgICA8YTpzIHI9IjE1NiI+DQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8YTpzIHI9IjE1NSI+DQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8YTpzIHI9IjE1NCI+DQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8YTpzPkZpdEROQTwvYTpzPg0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPC9hOnM+DQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8YTpzPi48L2E6cz4NCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDxhOnMgcj0iMTU1Ij4NCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDxhOnM+dmFsdWU8L2E6cz4NCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDwvYTpzPg0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPC9hOnM+DQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8YTpzPiBpcyBub3QgbnVsbDwvYTpzPg0KICAgICAgICAgICAgICAgICAgICAgICAgICAgPC9hOnM+DQogICAgICAgICAgICAgICAgICAgICAgICA8L2E6cz4NCiAgICAgICAgICAgICAgICAgICAgICAgIDxhOnM+CiAgICAgICAgICAgICAgICAgICAgCQkJYW5kIDwvYTpzPg0KICAgICAgICAgICAgICAgICAgICAgICAgPGE6cyByPSIxNjUiPg0KICAgICAgICAgICAgICAgICAgICAgICAgICAgPGE6cyByPSIxNjEiPg0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPGE6cyByPSIxNTgiPg0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPGE6cz5HbG9iYWw8L2E6cz4NCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDwvYTpzPg0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPGE6cz4uPC9hOnM+DQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8YTpzIHI9IjE2MSI+DQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8YTpzPiZxdW90O05vcm1hbGl6ZSBJbnRlcnZhbCZxdW90Oyg8L2E6cz4NCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDxhOnMgcj0iMTYwIj4NCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDxhOnMgcj0iMTU5Ij4NCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDxhOnM+Rml0RE5BPC9hOnM+DQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8L2E6cz4NCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDxhOnM+LjwvYTpzPg0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPGE6cyByPSIxNjAiPg0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPGE6cz5lZmZlY3RpdmU8L2E6cz4NCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDwvYTpzPg0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPC9hOnM+DQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8YTpzPik8L2E6cz4NCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDwvYTpzPg0KICAgICAgICAgICAgICAgICAgICAgICAgICAgPC9hOnM+DQogICAgICAgICAgICAgICAgICAgICAgICAgICA8YTpzPiA8L2E6cz4NCiAgICAgICAgICAgICAgICAgICAgICAgICAgIDxhOnMgcj0iMTY1Ij4NCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDxhOnM+b2NjdXJzIDwvYTpzPg0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPGE6cyByPSIxNjQiPg0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPGE6cz4zIHllYXJzPC9hOnM+DQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8L2E6cz4NCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDxhOnM+IG9yIGxlc3Mgb24gb3IgYmVmb3JlIGRheSBvZjwvYTpzPg0KICAgICAgICAgICAgICAgICAgICAgICAgICAgPC9hOnM+DQogICAgICAgICAgICAgICAgICAgICAgICAgICA8YTpzPiA8L2E6cz4NCiAgICAgICAgICAgICAgICAgICAgICAgICAgIDxhOnMgcj0iMTYzIj4NCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDxhOnM+ZW5kIG9mIDwvYTpzPg0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPGE6cyByPSIxNjIiPg0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPGE6cz4mcXVvdDtNZWFzdXJlbWVudCBQZXJpb2QmcXVvdDs8L2E6cz4NCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDwvYTpzPg0KICAgICAgICAgICAgICAgICAgICAgICAgICAgPC9hOnM+DQogICAgICAgICAgICAgICAgICAgICAgICA8L2E6cz4NCiAgICAgICAgICAgICAgICAgICAgIDwvYTpzPg0KICAgICAgICAgICAgICAgICAgPC9hOnM+DQogICAgICAgICAgICAgICA8L2E6cz4NCiAgICAgICAgICAgIDwvYTpzPg0KICAgICAgICAgPC9hbm5vdGF0aW9uPg0KICAgICAgICAgPGV4cHJlc3Npb24gbG9jYWxJZD0iMTY3IiBsb2NhdG9yPSI5MjozLTk1OjE0MyIgeHNpOnR5cGU9IlF1ZXJ5Ij4NCiAgICAgICAgICAgIDxzb3VyY2UgbG9jYWxJZD0iMTQ1IiBsb2NhdG9yPSI5MjozLTkyOjMzIiBhbGlhcz0iRml0RE5BIj4NCiAgICAgICAgICAgICAgIDxleHByZXNzaW9uIGxvY2FsSWQ9IjE0NCIgbG9jYXRvcj0iOTI6My05MjoyNiIgZGF0YVR5cGU9ImZoaXI6T2JzZXJ2YXRpb24iIHRlbXBsYXRlSWQ9Imh0dHA6Ly9obDcub3JnL2ZoaXIvU3RydWN0dXJlRGVmaW5pdGlvbi9PYnNlcnZhdGlvbiIgY29kZVByb3BlcnR5PSJjb2RlIiBjb2RlQ29tcGFyYXRvcj0iaW4iIHhzaTp0eXBlPSJSZXRyaWV2ZSI+DQogICAgICAgICAgICAgICAgICA8Y29kZXMgbG9jYXRvcj0iOTI6MTctOTI6MjUiIG5hbWU9IkZJVCBETkEiIHhzaTp0eXBlPSJWYWx1ZVNldFJlZiIvPg0KICAgICAgICAgICAgICAgPC9leHByZXNzaW9uPg0KICAgICAgICAgICAgPC9zb3VyY2U+DQogICAgICAgICAgICA8d2hlcmUgbG9jYWxJZD0iMTY2IiBsb2NhdG9yPSI5MzoyMy05NToxNDMiIHhzaTp0eXBlPSJBbmQiPg0KICAgICAgICAgICAgICAgPG9wZXJhbmQgbG9jYWxJZD0iMTU3IiBsb2NhdG9yPSI5MzoyOS05NDo1MSIgeHNpOnR5cGU9IkFuZCI+DQogICAgICAgICAgICAgICAgICA8b3BlcmFuZCBsb2NhbElkPSIxNTMiIGxvY2F0b3I9IjkzOjI5LTkzOjkzIiB4c2k6dHlwZT0iSW4iPg0KICAgICAgICAgICAgICAgICAgICAgPG9wZXJhbmQgbmFtZT0iVG9TdHJpbmciIGxpYnJhcnlOYW1lPSJGSElSSGVscGVycyIgeHNpOnR5cGU9IkZ1bmN0aW9uUmVmIj4NCiAgICAgICAgICAgICAgICAgICAgICAgIDxvcGVyYW5kIGxvY2FsSWQ9IjE0NyIgbG9jYXRvcj0iOTM6MjktOTM6NDEiIHBhdGg9InN0YXR1cyIgc2NvcGU9IkZpdEROQSIgeHNpOnR5cGU9IlByb3BlcnR5Ii8+DQogICAgICAgICAgICAgICAgICAgICA8L29wZXJhbmQ+DQogICAgICAgICAgICAgICAgICAgICA8b3BlcmFuZCBsb2NhbElkPSIxNTIiIGxvY2F0b3I9IjkzOjQ2LTkzOjkzIiB4c2k6dHlwZT0iTGlzdCI+DQogICAgICAgICAgICAgICAgICAgICAgICA8ZWxlbWVudCBsb2NhbElkPSIxNDgiIGxvY2F0b3I9IjkzOjQ3LTkzOjUzIiB2YWx1ZVR5cGU9InQ6U3RyaW5nIiB2YWx1ZT0iZmluYWwiIHhzaTp0eXBlPSJMaXRlcmFsIi8+DQogICAgICAgICAgICAgICAgICAgICAgICA8ZWxlbWVudCBsb2NhbElkPSIxNDkiIGxvY2F0b3I9IjkzOjU2LTkzOjY0IiB2YWx1ZVR5cGU9InQ6U3RyaW5nIiB2YWx1ZT0iYW1lbmRlZCIgeHNpOnR5cGU9IkxpdGVyYWwiLz4NCiAgICAgICAgICAgICAgICAgICAgICAgIDxlbGVtZW50IGxvY2FsSWQ9IjE1MCIgbG9jYXRvcj0iOTM6NjctOTM6NzciIHZhbHVlVHlwZT0idDpTdHJpbmciIHZhbHVlPSJjb3JyZWN0ZWQiIHhzaTp0eXBlPSJMaXRlcmFsIi8+DQogICAgICAgICAgICAgICAgICAgICAgICA8ZWxlbWVudCBsb2NhbElkPSIxNTEiIGxvY2F0b3I9IjkzOjgwLTkzOjkyIiB2YWx1ZVR5cGU9InQ6U3RyaW5nIiB2YWx1ZT0icHJlbGltaW5hcnkiIHhzaTp0eXBlPSJMaXRlcmFsIi8+DQogICAgICAgICAgICAgICAgICAgICA8L29wZXJhbmQ+DQogICAgICAgICAgICAgICAgICA8L29wZXJhbmQ+DQogICAgICAgICAgICAgICAgICA8b3BlcmFuZCBsb2NhbElkPSIxNTYiIGxvY2F0b3I9Ijk0OjI4LTk0OjUxIiB4c2k6dHlwZT0iTm90Ij4NCiAgICAgICAgICAgICAgICAgICAgIDxvcGVyYW5kIGxvY2F0b3I9Ijk0OjI4LTk0OjUxIiB4c2k6dHlwZT0iSXNOdWxsIj4NCiAgICAgICAgICAgICAgICAgICAgICAgIDxvcGVyYW5kIGxvY2FsSWQ9IjE1NSIgbG9jYXRvcj0iOTQ6MjgtOTQ6MzkiIHBhdGg9InZhbHVlIiBzY29wZT0iRml0RE5BIiB4c2k6dHlwZT0iUHJvcGVydHkiLz4NCiAgICAgICAgICAgICAgICAgICAgIDwvb3BlcmFuZD4NCiAgICAgICAgICAgICAgICAgIDwvb3BlcmFuZD4NCiAgICAgICAgICAgICAgIDwvb3BlcmFuZD4NCiAgICAgICAgICAgICAgIDxvcGVyYW5kIGxvY2FsSWQ9IjE2NSIgbG9jYXRvcj0iOTU6MjgtOTU6MTQzIiB4c2k6dHlwZT0iQW5kIj4NCiAgICAgICAgICAgICAgICAgIDxvcGVyYW5kIGxvY2F0b3I9Ijk1OjgxLTk1Ojk1IiBwcmVjaXNpb249IkRheSIgeHNpOnR5cGU9IkluIj4NCiAgICAgICAgICAgICAgICAgICAgIDxvcGVyYW5kIGxvY2F0b3I9Ijk1OjM1LTk1OjcyIiB4c2k6dHlwZT0iRW5kIj4NCiAgICAgICAgICAgICAgICAgICAgICAgIDxvcGVyYW5kIGxvY2FsSWQ9IjE2MSIgbG9jYXRvcj0iOTU6MjgtOTU6NzIiIG5hbWU9Ik5vcm1hbGl6ZSBJbnRlcnZhbCIgbGlicmFyeU5hbWU9Ikdsb2JhbCIgeHNpOnR5cGU9IkZ1bmN0aW9uUmVmIj4NCiAgICAgICAgICAgICAgICAgICAgICAgICAgIDxvcGVyYW5kIGxvY2FsSWQ9IjE2MCIgbG9jYXRvcj0iOTU6NTYtOTU6NzEiIHBhdGg9ImVmZmVjdGl2ZSIgc2NvcGU9IkZpdEROQSIgeHNpOnR5cGU9IlByb3BlcnR5Ii8+DQogICAgICAgICAgICAgICAgICAgICAgICA8L29wZXJhbmQ+DQogICAgICAgICAgICAgICAgICAgICA8L29wZXJhbmQ+DQogICAgICAgICAgICAgICAgICAgICA8b3BlcmFuZCBsb2NhdG9yPSI5NTo4MS05NTo5NSIgbG93Q2xvc2VkPSJ0cnVlIiBoaWdoQ2xvc2VkPSJ0cnVlIiB4c2k6dHlwZT0iSW50ZXJ2YWwiPg0KICAgICAgICAgICAgICAgICAgICAgICAgPGxvdyBsb2NhdG9yPSI5NToxMTctOTU6MTQzIiB4c2k6dHlwZT0iU3VidHJhY3QiPg0KICAgICAgICAgICAgICAgICAgICAgICAgICAgPG9wZXJhbmQgbG9jYWxJZD0iMTYzIiBsb2NhdG9yPSI5NToxMTctOTU6MTQzIiB4c2k6dHlwZT0iRW5kIj4NCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDxvcGVyYW5kIGxvY2FsSWQ9IjE2MiIgbG9jYXRvcj0iOTU6MTI0LTk1OjE0MyIgbmFtZT0iTWVhc3VyZW1lbnQgUGVyaW9kIiB4c2k6dHlwZT0iUGFyYW1ldGVyUmVmIi8+DQogICAgICAgICAgICAgICAgICAgICAgICAgICA8L29wZXJhbmQ+DQogICAgICAgICAgICAgICAgICAgICAgICAgICA8b3BlcmFuZCBsb2NhbElkPSIxNjQiIGxvY2F0b3I9Ijk1OjgxLTk1Ojg3IiB2YWx1ZT0iMyIgdW5pdD0ieWVhcnMiIHhzaTp0eXBlPSJRdWFudGl0eSIvPg0KICAgICAgICAgICAgICAgICAgICAgICAgPC9sb3c+DQogICAgICAgICAgICAgICAgICAgICAgICA8aGlnaCBsb2NhbElkPSIxNjMiIGxvY2F0b3I9Ijk1OjExNy05NToxNDMiIHhzaTp0eXBlPSJFbmQiPg0KICAgICAgICAgICAgICAgICAgICAgICAgICAgPG9wZXJhbmQgbG9jYWxJZD0iMTYyIiBsb2NhdG9yPSI5NToxMjQtOTU6MTQzIiBuYW1lPSJNZWFzdXJlbWVudCBQZXJpb2QiIHhzaTp0eXBlPSJQYXJhbWV0ZXJSZWYiLz4NCiAgICAgICAgICAgICAgICAgICAgICAgIDwvaGlnaD4NCiAgICAgICAgICAgICAgICAgICAgIDwvb3BlcmFuZD4NCiAgICAgICAgICAgICAgICAgIDwvb3BlcmFuZD4NCiAgICAgICAgICAgICAgICAgIDxvcGVyYW5kIGxvY2F0b3I9Ijk1OjgxLTk1Ojk1IiB4c2k6dHlwZT0iTm90Ij4NCiAgICAgICAgICAgICAgICAgICAgIDxvcGVyYW5kIGxvY2F0b3I9Ijk1OjgxLTk1Ojk1IiB4c2k6dHlwZT0iSXNOdWxsIj4NCiAgICAgICAgICAgICAgICAgICAgICAgIDxvcGVyYW5kIGxvY2FsSWQ9IjE2MyIgbG9jYXRvcj0iOTU6MTE3LTk1OjE0MyIgeHNpOnR5cGU9IkVuZCI+DQogICAgICAgICAgICAgICAgICAgICAgICAgICA8b3BlcmFuZCBsb2NhbElkPSIxNjIiIGxvY2F0b3I9Ijk1OjEyNC05NToxNDMiIG5hbWU9Ik1lYXN1cmVtZW50IFBlcmlvZCIgeHNpOnR5cGU9IlBhcmFtZXRlclJlZiIvPg0KICAgICAgICAgICAgICAgICAgICAgICAgPC9vcGVyYW5kPg0KICAgICAgICAgICAgICAgICAgICAgPC9vcGVyYW5kPg0KICAgICAgICAgICAgICAgICAgPC9vcGVyYW5kPg0KICAgICAgICAgICAgICAgPC9vcGVyYW5kPg0KICAgICAgICAgICAgPC93aGVyZT4NCiAgICAgICAgIDwvZXhwcmVzc2lvbj4NCiAgICAgIDwvZGVmPg0KICAgICAgPGRlZiBsb2NhbElkPSIxODUiIGxvY2F0b3I9Ijk3OjEtMTAwOjE0MSIgbmFtZT0iQ29sb25vc2NvcHkgUGVyZm9ybWVkIiBjb250ZXh0PSJQYXRpZW50IiBhY2Nlc3NMZXZlbD0iUHVibGljIj4NCiAgICAgICAgIDxhbm5vdGF0aW9uIHhzaTp0eXBlPSJhOkFubm90YXRpb24iPg0KICAgICAgICAgICAgPGE6cyByPSIxODUiPg0KICAgICAgICAgICAgICAgPGE6cz5kZWZpbmUgJnF1b3Q7Q29sb25vc2NvcHkgUGVyZm9ybWVkJnF1b3Q7OgogIDwvYTpzPg0KICAgICAgICAgICAgICAgPGE6cyByPSIxODQiPg0KICAgICAgICAgICAgICAgICAgPGE6cz4NCiAgICAgICAgICAgICAgICAgICAgIDxhOnMgcj0iMTcwIj4NCiAgICAgICAgICAgICAgICAgICAgICAgIDxhOnMgcj0iMTY5Ij4NCiAgICAgICAgICAgICAgICAgICAgICAgICAgIDxhOnMgcj0iMTY5Ij4NCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDxhOnM+W1Byb2NlZHVyZTogPC9hOnM+DQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8YTpzPg0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPGE6cz4mcXVvdDtDb2xvbm9zY29weSZxdW90OzwvYTpzPg0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPC9hOnM+DQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8YTpzPl08L2E6cz4NCiAgICAgICAgICAgICAgICAgICAgICAgICAgIDwvYTpzPg0KICAgICAgICAgICAgICAgICAgICAgICAgPC9hOnM+DQogICAgICAgICAgICAgICAgICAgICAgICA8YTpzPiBDb2xvbm9zY29weTwvYTpzPg0KICAgICAgICAgICAgICAgICAgICAgPC9hOnM+DQogICAgICAgICAgICAgICAgICA8L2E6cz4NCiAgICAgICAgICAgICAgICAgIDxhOnM+CiAgICAgICAgICAgICAgICAgICAgCQk8L2E6cz4NCiAgICAgICAgICAgICAgICAgIDxhOnMgcj0iMTgzIj4NCiAgICAgICAgICAgICAgICAgICAgIDxhOnM+d2hlcmUgPC9hOnM+DQogICAgICAgICAgICAgICAgICAgICA8YTpzIHI9IjE4MyI+DQogICAgICAgICAgICAgICAgICAgICAgICA8YTpzIHI9IjE3NCI+DQogICAgICAgICAgICAgICAgICAgICAgICAgICA8YTpzIHI9IjE3MiI+DQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8YTpzIHI9IjE3MSI+DQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8YTpzPkNvbG9ub3Njb3B5PC9hOnM+DQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8L2E6cz4NCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDxhOnM+LjwvYTpzPg0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPGE6cyByPSIxNzIiPg0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPGE6cz5zdGF0dXM8L2E6cz4NCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDwvYTpzPg0KICAgICAgICAgICAgICAgICAgICAgICAgICAgPC9hOnM+DQogICAgICAgICAgICAgICAgICAgICAgICAgICA8YTpzPiA9IDwvYTpzPg0KICAgICAgICAgICAgICAgICAgICAgICAgICAgPGE6cyByPSIxNzMiPg0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPGE6cz4nY29tcGxldGVkJzwvYTpzPg0KICAgICAgICAgICAgICAgICAgICAgICAgICAgPC9hOnM+DQogICAgICAgICAgICAgICAgICAgICAgICA8L2E6cz4NCiAgICAgICAgICAgICAgICAgICAgICAgIDxhOnM+CiAgICAgICAgICAgICAgICAgICAgCQkgIGFuZCA8L2E6cz4NCiAgICAgICAgICAgICAgICAgICAgICAgIDxhOnMgcj0iMTgyIj4NCiAgICAgICAgICAgICAgICAgICAgICAgICAgIDxhOnMgcj0iMTc4Ij4NCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDxhOnMgcj0iMTc1Ij4NCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDxhOnM+R2xvYmFsPC9hOnM+DQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8L2E6cz4NCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDxhOnM+LjwvYTpzPg0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPGE6cyByPSIxNzgiPg0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPGE6cz4mcXVvdDtOb3JtYWxpemUgSW50ZXJ2YWwmcXVvdDsoPC9hOnM+DQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8YTpzIHI9IjE3NyI+DQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8YTpzIHI9IjE3NiI+DQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8YTpzPkNvbG9ub3Njb3B5PC9hOnM+DQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8L2E6cz4NCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDxhOnM+LjwvYTpzPg0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPGE6cyByPSIxNzciPg0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPGE6cz5wZXJmb3JtZWQ8L2E6cz4NCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDwvYTpzPg0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPC9hOnM+DQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8YTpzPik8L2E6cz4NCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDwvYTpzPg0KICAgICAgICAgICAgICAgICAgICAgICAgICAgPC9hOnM+DQogICAgICAgICAgICAgICAgICAgICAgICAgICA8YTpzPiA8L2E6cz4NCiAgICAgICAgICAgICAgICAgICAgICAgICAgIDxhOnMgcj0iMTgyIj4NCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDxhOnM+ZW5kcyA8L2E6cz4NCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDxhOnMgcj0iMTgxIj4NCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDxhOnM+MTAgeWVhcnM8L2E6cz4NCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDwvYTpzPg0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPGE6cz4gb3IgbGVzcyBvbiBvciBiZWZvcmU8L2E6cz4NCiAgICAgICAgICAgICAgICAgICAgICAgICAgIDwvYTpzPg0KICAgICAgICAgICAgICAgICAgICAgICAgICAgPGE6cz4gPC9hOnM+DQogICAgICAgICAgICAgICAgICAgICAgICAgICA8YTpzIHI9IjE4MCI+DQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8YTpzPmVuZCBvZiA8L2E6cz4NCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDxhOnMgcj0iMTc5Ij4NCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDxhOnM+JnF1b3Q7TWVhc3VyZW1lbnQgUGVyaW9kJnF1b3Q7PC9hOnM+DQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8L2E6cz4NCiAgICAgICAgICAgICAgICAgICAgICAgICAgIDwvYTpzPg0KICAgICAgICAgICAgICAgICAgICAgICAgPC9hOnM+DQogICAgICAgICAgICAgICAgICAgICA8L2E6cz4NCiAgICAgICAgICAgICAgICAgIDwvYTpzPg0KICAgICAgICAgICAgICAgPC9hOnM+DQogICAgICAgICAgICA8L2E6cz4NCiAgICAgICAgIDwvYW5ub3RhdGlvbj4NCiAgICAgICAgIDxleHByZXNzaW9uIGxvY2FsSWQ9IjE4NCIgbG9jYXRvcj0iOTg6My0xMDA6MTQxIiB4c2k6dHlwZT0iUXVlcnkiPg0KICAgICAgICAgICAgPHNvdXJjZSBsb2NhbElkPSIxNzAiIGxvY2F0b3I9Ijk4OjMtOTg6NDAiIGFsaWFzPSJDb2xvbm9zY29weSI+DQogICAgICAgICAgICAgICA8ZXhwcmVzc2lvbiBsb2NhbElkPSIxNjkiIGxvY2F0b3I9Ijk4OjMtOTg6MjgiIGRhdGFUeXBlPSJmaGlyOlByb2NlZHVyZSIgdGVtcGxhdGVJZD0iaHR0cDovL2hsNy5vcmcvZmhpci9TdHJ1Y3R1cmVEZWZpbml0aW9uL1Byb2NlZHVyZSIgY29kZVByb3BlcnR5PSJjb2RlIiBjb2RlQ29tcGFyYXRvcj0iaW4iIHhzaTp0eXBlPSJSZXRyaWV2ZSI+DQogICAgICAgICAgICAgICAgICA8Y29kZXMgbG9jYXRvcj0iOTg6MTUtOTg6MjciIG5hbWU9IkNvbG9ub3Njb3B5IiB4c2k6dHlwZT0iVmFsdWVTZXRSZWYiLz4NCiAgICAgICAgICAgICAgIDwvZXhwcmVzc2lvbj4NCiAgICAgICAgICAgIDwvc291cmNlPg0KICAgICAgICAgICAgPHdoZXJlIGxvY2FsSWQ9IjE4MyIgbG9jYXRvcj0iOTk6MjMtMTAwOjE0MSIgeHNpOnR5cGU9IkFuZCI+DQogICAgICAgICAgICAgICA8b3BlcmFuZCBsb2NhbElkPSIxNzQiIGxvY2F0b3I9Ijk5OjI5LTk5OjYwIiB4c2k6dHlwZT0iRXF1YWwiPg0KICAgICAgICAgICAgICAgICAgPG9wZXJhbmQgbmFtZT0iVG9TdHJpbmciIGxpYnJhcnlOYW1lPSJGSElSSGVscGVycyIgeHNpOnR5cGU9IkZ1bmN0aW9uUmVmIj4NCiAgICAgICAgICAgICAgICAgICAgIDxvcGVyYW5kIGxvY2FsSWQ9IjE3MiIgbG9jYXRvcj0iOTk6MjktOTk6NDYiIHBhdGg9InN0YXR1cyIgc2NvcGU9IkNvbG9ub3Njb3B5IiB4c2k6dHlwZT0iUHJvcGVydHkiLz4NCiAgICAgICAgICAgICAgICAgIDwvb3BlcmFuZD4NCiAgICAgICAgICAgICAgICAgIDxvcGVyYW5kIGxvY2FsSWQ9IjE3MyIgbG9jYXRvcj0iOTk6NTAtOTk6NjAiIHZhbHVlVHlwZT0idDpTdHJpbmciIHZhbHVlPSJjb21wbGV0ZWQiIHhzaTp0eXBlPSJMaXRlcmFsIi8+DQogICAgICAgICAgICAgICA8L29wZXJhbmQ+DQogICAgICAgICAgICAgICA8b3BlcmFuZCBsb2NhbElkPSIxODIiIGxvY2F0b3I9IjEwMDoyOS0xMDA6MTQxIiB4c2k6dHlwZT0iQW5kIj4NCiAgICAgICAgICAgICAgICAgIDxvcGVyYW5kIGxvY2F0b3I9IjEwMDo4NS0xMDA6MTAwIiB4c2k6dHlwZT0iSW4iPg0KICAgICAgICAgICAgICAgICAgICAgPG9wZXJhbmQgbG9jYXRvcj0iMTAwOjgwLTEwMDo4MyIgeHNpOnR5cGU9IkVuZCI+DQogICAgICAgICAgICAgICAgICAgICAgICA8b3BlcmFuZCBsb2NhbElkPSIxNzgiIGxvY2F0b3I9IjEwMDoyOS0xMDA6NzgiIG5hbWU9Ik5vcm1hbGl6ZSBJbnRlcnZhbCIgbGlicmFyeU5hbWU9Ikdsb2JhbCIgeHNpOnR5cGU9IkZ1bmN0aW9uUmVmIj4NCiAgICAgICAgICAgICAgICAgICAgICAgICAgIDxvcGVyYW5kIGxvY2FsSWQ9IjE3NyIgbG9jYXRvcj0iMTAwOjU3LTEwMDo3NyIgcGF0aD0icGVyZm9ybWVkIiBzY29wZT0iQ29sb25vc2NvcHkiIHhzaTp0eXBlPSJQcm9wZXJ0eSIvPg0KICAgICAgICAgICAgICAgICAgICAgICAgPC9vcGVyYW5kPg0KICAgICAgICAgICAgICAgICAgICAgPC9vcGVyYW5kPg0KICAgICAgICAgICAgICAgICAgICAgPG9wZXJhbmQgbG9jYXRvcj0iMTAwOjg1LTEwMDoxMDAiIGxvd0Nsb3NlZD0idHJ1ZSIgaGlnaENsb3NlZD0idHJ1ZSIgeHNpOnR5cGU9IkludGVydmFsIj4NCiAgICAgICAgICAgICAgICAgICAgICAgIDxsb3cgbG9jYXRvcj0iMTAwOjExNS0xMDA6MTQxIiB4c2k6dHlwZT0iU3VidHJhY3QiPg0KICAgICAgICAgICAgICAgICAgICAgICAgICAgPG9wZXJhbmQgbG9jYWxJZD0iMTgwIiBsb2NhdG9yPSIxMDA6MTE1LTEwMDoxNDEiIHhzaTp0eXBlPSJFbmQiPg0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPG9wZXJhbmQgbG9jYWxJZD0iMTc5IiBsb2NhdG9yPSIxMDA6MTIyLTEwMDoxNDEiIG5hbWU9Ik1lYXN1cmVtZW50IFBlcmlvZCIgeHNpOnR5cGU9IlBhcmFtZXRlclJlZiIvPg0KICAgICAgICAgICAgICAgICAgICAgICAgICAgPC9vcGVyYW5kPg0KICAgICAgICAgICAgICAgICAgICAgICAgICAgPG9wZXJhbmQgbG9jYWxJZD0iMTgxIiBsb2NhdG9yPSIxMDA6ODUtMTAwOjkyIiB2YWx1ZT0iMTAiIHVuaXQ9InllYXJzIiB4c2k6dHlwZT0iUXVhbnRpdHkiLz4NCiAgICAgICAgICAgICAgICAgICAgICAgIDwvbG93Pg0KICAgICAgICAgICAgICAgICAgICAgICAgPGhpZ2ggbG9jYWxJZD0iMTgwIiBsb2NhdG9yPSIxMDA6MTE1LTEwMDoxNDEiIHhzaTp0eXBlPSJFbmQiPg0KICAgICAgICAgICAgICAgICAgICAgICAgICAgPG9wZXJhbmQgbG9jYWxJZD0iMTc5IiBsb2NhdG9yPSIxMDA6MTIyLTEwMDoxNDEiIG5hbWU9Ik1lYXN1cmVtZW50IFBlcmlvZCIgeHNpOnR5cGU9IlBhcmFtZXRlclJlZiIvPg0KICAgICAgICAgICAgICAgICAgICAgICAgPC9oaWdoPg0KICAgICAgICAgICAgICAgICAgICAgPC9vcGVyYW5kPg0KICAgICAgICAgICAgICAgICAgPC9vcGVyYW5kPg0KICAgICAgICAgICAgICAgICAgPG9wZXJhbmQgbG9jYXRvcj0iMTAwOjg1LTEwMDoxMDAiIHhzaTp0eXBlPSJOb3QiPg0KICAgICAgICAgICAgICAgICAgICAgPG9wZXJhbmQgbG9jYXRvcj0iMTAwOjg1LTEwMDoxMDAiIHhzaTp0eXBlPSJJc051bGwiPg0KICAgICAgICAgICAgICAgICAgICAgICAgPG9wZXJhbmQgbG9jYWxJZD0iMTgwIiBsb2NhdG9yPSIxMDA6MTE1LTEwMDoxNDEiIHhzaTp0eXBlPSJFbmQiPg0KICAgICAgICAgICAgICAgICAgICAgICAgICAgPG9wZXJhbmQgbG9jYWxJZD0iMTc5IiBsb2NhdG9yPSIxMDA6MTIyLTEwMDoxNDEiIG5hbWU9Ik1lYXN1cmVtZW50IFBlcmlvZCIgeHNpOnR5cGU9IlBhcmFtZXRlclJlZiIvPg0KICAgICAgICAgICAgICAgICAgICAgICAgPC9vcGVyYW5kPg0KICAgICAgICAgICAgICAgICAgICAgPC9vcGVyYW5kPg0KICAgICAgICAgICAgICAgICAgPC9vcGVyYW5kPg0KICAgICAgICAgICAgICAgPC9vcGVyYW5kPg0KICAgICAgICAgICAgPC93aGVyZT4NCiAgICAgICAgIDwvZXhwcmVzc2lvbj4NCiAgICAgIDwvZGVmPg0KICAgICAgPGRlZiBsb2NhbElkPSIyMDAiIGxvY2F0b3I9IjEwMjoxLTEwNzo1OSIgbmFtZT0iTnVtZXJhdG9yIiBjb250ZXh0PSJQYXRpZW50IiBhY2Nlc3NMZXZlbD0iUHVibGljIj4NCiAgICAgICAgIDxhbm5vdGF0aW9uIHhzaTp0eXBlPSJhOkFubm90YXRpb24iPg0KICAgICAgICAgICAgPGE6cyByPSIyMDAiPg0KICAgICAgICAgICAgICAgPGE6cz5kZWZpbmUgJnF1b3Q7TnVtZXJhdG9yJnF1b3Q7OgogIDwvYTpzPg0KICAgICAgICAgICAgICAgPGE6cyByPSIxOTkiPg0KICAgICAgICAgICAgICAgICAgPGE6cyByPSIxOTYiPg0KICAgICAgICAgICAgICAgICAgICAgPGE6cyByPSIxOTMiPg0KICAgICAgICAgICAgICAgICAgICAgICAgPGE6cyByPSIxOTAiPg0KICAgICAgICAgICAgICAgICAgICAgICAgICAgPGE6cyByPSIxODciPg0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPGE6cz5leGlzdHMgPC9hOnM+DQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8YTpzIHI9IjE4NiI+DQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8YTpzPiZxdW90O0NvbG9ub3Njb3B5IFBlcmZvcm1lZCZxdW90OzwvYTpzPg0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPC9hOnM+DQogICAgICAgICAgICAgICAgICAgICAgICAgICA8L2E6cz4NCiAgICAgICAgICAgICAgICAgICAgICAgICAgIDxhOnM+CiAgICAgICAgICAgICAgICAgICAgCQlvciA8L2E6cz4NCiAgICAgICAgICAgICAgICAgICAgICAgICAgIDxhOnMgcj0iMTg5Ij4NCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDxhOnM+ZXhpc3RzIDwvYTpzPg0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPGE6cyByPSIxODgiPg0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPGE6cz4mcXVvdDtGZWNhbCBPY2N1bHQgQmxvb2QgVGVzdCBQZXJmb3JtZWQmcXVvdDs8L2E6cz4NCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDwvYTpzPg0KICAgICAgICAgICAgICAgICAgICAgICAgICAgPC9hOnM+DQogICAgICAgICAgICAgICAgICAgICAgICA8L2E6cz4NCiAgICAgICAgICAgICAgICAgICAgICAgIDxhOnM+CiAgICAgICAgICAgICAgICAgICAgCQlvciA8L2E6cz4NCiAgICAgICAgICAgICAgICAgICAgICAgIDxhOnMgcj0iMTkyIj4NCiAgICAgICAgICAgICAgICAgICAgICAgICAgIDxhOnM+ZXhpc3RzIDwvYTpzPg0KICAgICAgICAgICAgICAgICAgICAgICAgICAgPGE6cyByPSIxOTEiPg0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPGE6cz4mcXVvdDtGbGV4aWJsZSBTaWdtb2lkb3Njb3B5IFBlcmZvcm1lZCZxdW90OzwvYTpzPg0KICAgICAgICAgICAgICAgICAgICAgICAgICAgPC9hOnM+DQogICAgICAgICAgICAgICAgICAgICAgICA8L2E6cz4NCiAgICAgICAgICAgICAgICAgICAgIDwvYTpzPg0KICAgICAgICAgICAgICAgICAgICAgPGE6cz4KICAgICAgICAgICAgICAgICAgICAJCW9yIDwvYTpzPg0KICAgICAgICAgICAgICAgICAgICAgPGE6cyByPSIxOTUiPg0KICAgICAgICAgICAgICAgICAgICAgICAgPGE6cz5leGlzdHMgPC9hOnM+DQogICAgICAgICAgICAgICAgICAgICAgICA8YTpzIHI9IjE5NCI+DQogICAgICAgICAgICAgICAgICAgICAgICAgICA8YTpzPiZxdW90O0ZlY2FsIEltbXVub2NoZW1pY2FsIFRlc3QgRE5BJnF1b3Q7PC9hOnM+DQogICAgICAgICAgICAgICAgICAgICAgICA8L2E6cz4NCiAgICAgICAgICAgICAgICAgICAgIDwvYTpzPg0KICAgICAgICAgICAgICAgICAgPC9hOnM+DQogICAgICAgICAgICAgICAgICA8YTpzPgogICAgICAgICAgICAgICAgICAgIAkJb3IgPC9hOnM+DQogICAgICAgICAgICAgICAgICA8YTpzIHI9IjE5OCI+DQogICAgICAgICAgICAgICAgICAgICA8YTpzPmV4aXN0cyA8L2E6cz4NCiAgICAgICAgICAgICAgICAgICAgIDxhOnMgcj0iMTk3Ij4NCiAgICAgICAgICAgICAgICAgICAgICAgIDxhOnM+JnF1b3Q7Q1QgQ29sb25vZ3JhcGh5IFBlcmZvcm1lZCZxdW90OzwvYTpzPg0KICAgICAgICAgICAgICAgICAgICAgPC9hOnM+DQogICAgICAgICAgICAgICAgICA8L2E6cz4NCiAgICAgICAgICAgICAgIDwvYTpzPg0KICAgICAgICAgICAgPC9hOnM+DQogICAgICAgICA8L2Fubm90YXRpb24+DQogICAgICAgICA8ZXhwcmVzc2lvbiBsb2NhbElkPSIxOTkiIGxvY2F0b3I9IjEwMzozLTEwNzo1OSIgeHNpOnR5cGU9Ik9yIj4NCiAgICAgICAgICAgIDxvcGVyYW5kIGxvY2FsSWQ9IjE5NiIgbG9jYXRvcj0iMTAzOjMtMTA2OjYzIiB4c2k6dHlwZT0iT3IiPg0KICAgICAgICAgICAgICAgPG9wZXJhbmQgbG9jYWxJZD0iMTkzIiBsb2NhdG9yPSIxMDM6My0xMDU6NjYiIHhzaTp0eXBlPSJPciI+DQogICAgICAgICAgICAgICAgICA8b3BlcmFuZCBsb2NhbElkPSIxOTAiIGxvY2F0b3I9IjEwMzozLTEwNDo2NyIgeHNpOnR5cGU9Ik9yIj4NCiAgICAgICAgICAgICAgICAgICAgIDxvcGVyYW5kIGxvY2FsSWQ9IjE4NyIgbG9jYXRvcj0iMTAzOjMtMTAzOjMyIiB4c2k6dHlwZT0iRXhpc3RzIj4NCiAgICAgICAgICAgICAgICAgICAgICAgIDxvcGVyYW5kIGxvY2FsSWQ9IjE4NiIgbG9jYXRvcj0iMTAzOjEwLTEwMzozMiIgbmFtZT0iQ29sb25vc2NvcHkgUGVyZm9ybWVkIiB4c2k6dHlwZT0iRXhwcmVzc2lvblJlZiIvPg0KICAgICAgICAgICAgICAgICAgICAgPC9vcGVyYW5kPg0KICAgICAgICAgICAgICAgICAgICAgPG9wZXJhbmQgbG9jYWxJZD0iMTg5IiBsb2NhdG9yPSIxMDQ6MjYtMTA0OjY3IiB4c2k6dHlwZT0iRXhpc3RzIj4NCiAgICAgICAgICAgICAgICAgICAgICAgIDxvcGVyYW5kIGxvY2FsSWQ9IjE4OCIgbG9jYXRvcj0iMTA0OjMzLTEwNDo2NyIgbmFtZT0iRmVjYWwgT2NjdWx0IEJsb29kIFRlc3QgUGVyZm9ybWVkIiB4c2k6dHlwZT0iRXhwcmVzc2lvblJlZiIvPg0KICAgICAgICAgICAgICAgICAgICAgPC9vcGVyYW5kPg0KICAgICAgICAgICAgICAgICAgPC9vcGVyYW5kPg0KICAgICAgICAgICAgICAgICAgPG9wZXJhbmQgbG9jYWxJZD0iMTkyIiBsb2NhdG9yPSIxMDU6MjYtMTA1OjY2IiB4c2k6dHlwZT0iRXhpc3RzIj4NCiAgICAgICAgICAgICAgICAgICAgIDxvcGVyYW5kIGxvY2FsSWQ9IjE5MSIgbG9jYXRvcj0iMTA1OjMzLTEwNTo2NiIgbmFtZT0iRmxleGlibGUgU2lnbW9pZG9zY29weSBQZXJmb3JtZWQiIHhzaTp0eXBlPSJFeHByZXNzaW9uUmVmIi8+DQogICAgICAgICAgICAgICAgICA8L29wZXJhbmQ+DQogICAgICAgICAgICAgICA8L29wZXJhbmQ+DQogICAgICAgICAgICAgICA8b3BlcmFuZCBsb2NhbElkPSIxOTUiIGxvY2F0b3I9IjEwNjoyNi0xMDY6NjMiIHhzaTp0eXBlPSJFeGlzdHMiPg0KICAgICAgICAgICAgICAgICAgPG9wZXJhbmQgbG9jYWxJZD0iMTk0IiBsb2NhdG9yPSIxMDY6MzMtMTA2OjYzIiBuYW1lPSJGZWNhbCBJbW11bm9jaGVtaWNhbCBUZXN0IEROQSIgeHNpOnR5cGU9IkV4cHJlc3Npb25SZWYiLz4NCiAgICAgICAgICAgICAgIDwvb3BlcmFuZD4NCiAgICAgICAgICAgIDwvb3BlcmFuZD4NCiAgICAgICAgICAgIDxvcGVyYW5kIGxvY2FsSWQ9IjE5OCIgbG9jYXRvcj0iMTA3OjI2LTEwNzo1OSIgeHNpOnR5cGU9IkV4aXN0cyI+DQogICAgICAgICAgICAgICA8b3BlcmFuZCBsb2NhbElkPSIxOTciIGxvY2F0b3I9IjEwNzozMy0xMDc6NTkiIG5hbWU9IkNUIENvbG9ub2dyYXBoeSBQZXJmb3JtZWQiIHhzaTp0eXBlPSJFeHByZXNzaW9uUmVmIi8+DQogICAgICAgICAgICA8L29wZXJhbmQ+DQogICAgICAgICA8L2V4cHJlc3Npb24+DQogICAgICA8L2RlZj4NCiAgICAgIDxkZWYgbG9jYWxJZD0iMjEwIiBsb2NhdG9yPSIxMDk6MS0xMTE6MTIwIiBuYW1lPSJNYWxpZ25hbnQgTmVvcGxhc20iIGNvbnRleHQ9IlBhdGllbnQiIGFjY2Vzc0xldmVsPSJQdWJsaWMiPg0KICAgICAgICAgPGFubm90YXRpb24geHNpOnR5cGU9ImE6QW5ub3RhdGlvbiI+DQogICAgICAgICAgICA8YTpzIHI9IjIxMCI+DQogICAgICAgICAgICAgICA8YTpzPmRlZmluZSAmcXVvdDtNYWxpZ25hbnQgTmVvcGxhc20mcXVvdDs6CiAgPC9hOnM+DQogICAgICAgICAgICAgICA8YTpzIHI9IjIwOSI+DQogICAgICAgICAgICAgICAgICA8YTpzPg0KICAgICAgICAgICAgICAgICAgICAgPGE6cyByPSIyMDIiPg0KICAgICAgICAgICAgICAgICAgICAgICAgPGE6cyByPSIyMDEiPg0KICAgICAgICAgICAgICAgICAgICAgICAgICAgPGE6cyByPSIyMDEiPg0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPGE6cz5bQ29uZGl0aW9uOiA8L2E6cz4NCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDxhOnM+DQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8YTpzPiZxdW90O01hbGlnbmFudCBOZW9wbGFzbSBvZiBDb2xvbiZxdW90OzwvYTpzPg0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPC9hOnM+DQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8YTpzPl08L2E6cz4NCiAgICAgICAgICAgICAgICAgICAgICAgICAgIDwvYTpzPg0KICAgICAgICAgICAgICAgICAgICAgICAgPC9hOnM+DQogICAgICAgICAgICAgICAgICAgICAgICA8YTpzPiBDb2xvcmVjdGFsQ2FuY2VyPC9hOnM+DQogICAgICAgICAgICAgICAgICAgICA8L2E6cz4NCiAgICAgICAgICAgICAgICAgIDwvYTpzPg0KICAgICAgICAgICAgICAgICAgPGE6cz4KICAgICAgICAgICAgICAgICAgICAJCTwvYTpzPg0KICAgICAgICAgICAgICAgICAgPGE6cyByPSIyMDgiPg0KICAgICAgICAgICAgICAgICAgICAgPGE6cz53aGVyZSA8L2E6cz4NCiAgICAgICAgICAgICAgICAgICAgIDxhOnMgcj0iMjA4Ij4NCiAgICAgICAgICAgICAgICAgICAgICAgIDxhOnMgcj0iMjA1Ij4NCiAgICAgICAgICAgICAgICAgICAgICAgICAgIDxhOnMgcj0iMjAzIj4NCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDxhOnM+R2xvYmFsPC9hOnM+DQogICAgICAgICAgICAgICAgICAgICAgICAgICA8L2E6cz4NCiAgICAgICAgICAgICAgICAgICAgICAgICAgIDxhOnM+LjwvYTpzPg0KICAgICAgICAgICAgICAgICAgICAgICAgICAgPGE6cyByPSIyMDUiPg0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPGE6cz4mcXVvdDtQcmV2YWxlbmNlIFBlcmlvZCZxdW90Oyg8L2E6cz4NCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDxhOnMgcj0iMjA0Ij4NCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDxhOnM+Q29sb3JlY3RhbENhbmNlcjwvYTpzPg0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPC9hOnM+DQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8YTpzPik8L2E6cz4NCiAgICAgICAgICAgICAgICAgICAgICAgICAgIDwvYTpzPg0KICAgICAgICAgICAgICAgICAgICAgICAgPC9hOnM+DQogICAgICAgICAgICAgICAgICAgICAgICA8YTpzIHI9IjIwOCI+IHN0YXJ0cyBvbiBvciBiZWZvcmUgPC9hOnM+DQogICAgICAgICAgICAgICAgICAgICAgICA8YTpzIHI9IjIwNyI+DQogICAgICAgICAgICAgICAgICAgICAgICAgICA8YTpzPmVuZCBvZiA8L2E6cz4NCiAgICAgICAgICAgICAgICAgICAgICAgICAgIDxhOnMgcj0iMjA2Ij4NCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDxhOnM+JnF1b3Q7TWVhc3VyZW1lbnQgUGVyaW9kJnF1b3Q7PC9hOnM+DQogICAgICAgICAgICAgICAgICAgICAgICAgICA8L2E6cz4NCiAgICAgICAgICAgICAgICAgICAgICAgIDwvYTpzPg0KICAgICAgICAgICAgICAgICAgICAgPC9hOnM+DQogICAgICAgICAgICAgICAgICA8L2E6cz4NCiAgICAgICAgICAgICAgIDwvYTpzPg0KICAgICAgICAgICAgPC9hOnM+DQogICAgICAgICA8L2Fubm90YXRpb24+DQogICAgICAgICA8ZXhwcmVzc2lvbiBsb2NhbElkPSIyMDkiIGxvY2F0b3I9IjExMDozLTExMToxMjAiIHhzaTp0eXBlPSJRdWVyeSI+DQogICAgICAgICAgICA8c291cmNlIGxvY2FsSWQ9IjIwMiIgbG9jYXRvcj0iMTEwOjMtMTEwOjYxIiBhbGlhcz0iQ29sb3JlY3RhbENhbmNlciI+DQogICAgICAgICAgICAgICA8ZXhwcmVzc2lvbiBsb2NhbElkPSIyMDEiIGxvY2F0b3I9IjExMDozLTExMDo0NCIgZGF0YVR5cGU9ImZoaXI6Q29uZGl0aW9uIiB0ZW1wbGF0ZUlkPSJodHRwOi8vaGw3Lm9yZy9maGlyL1N0cnVjdHVyZURlZmluaXRpb24vQ29uZGl0aW9uIiBjb2RlUHJvcGVydHk9ImNvZGUiIGNvZGVDb21wYXJhdG9yPSJpbiIgeHNpOnR5cGU9IlJldHJpZXZlIj4NCiAgICAgICAgICAgICAgICAgIDxjb2RlcyBsb2NhdG9yPSIxMTA6MTUtMTEwOjQzIiBuYW1lPSJNYWxpZ25hbnQgTmVvcGxhc20gb2YgQ29sb24iIHhzaTp0eXBlPSJWYWx1ZVNldFJlZiIvPg0KICAgICAgICAgICAgICAgPC9leHByZXNzaW9uPg0KICAgICAgICAgICAgPC9zb3VyY2U+DQogICAgICAgICAgICA8d2hlcmUgbG9jYWxJZD0iMjA4IiBsb2NhdG9yPSIxMTE6MjMtMTExOjEyMCIgeHNpOnR5cGU9IlNhbWVPckJlZm9yZSI+DQogICAgICAgICAgICAgICA8b3BlcmFuZCBsb2NhdG9yPSIxMTE6NzQtMTExOjc5IiB4c2k6dHlwZT0iU3RhcnQiPg0KICAgICAgICAgICAgICAgICAgPG9wZXJhbmQgbG9jYWxJZD0iMjA1IiBsb2NhdG9yPSIxMTE6MjktMTExOjcyIiBuYW1lPSJQcmV2YWxlbmNlIFBlcmlvZCIgbGlicmFyeU5hbWU9Ikdsb2JhbCIgeHNpOnR5cGU9IkZ1bmN0aW9uUmVmIj4NCiAgICAgICAgICAgICAgICAgICAgIDxvcGVyYW5kIGxvY2FsSWQ9IjIwNCIgbG9jYXRvcj0iMTExOjU2LTExMTo3MSIgbmFtZT0iQ29sb3JlY3RhbENhbmNlciIgeHNpOnR5cGU9IkFsaWFzUmVmIi8+DQogICAgICAgICAgICAgICAgICA8L29wZXJhbmQ+DQogICAgICAgICAgICAgICA8L29wZXJhbmQ+DQogICAgICAgICAgICAgICA8b3BlcmFuZCBsb2NhbElkPSIyMDciIGxvY2F0b3I9IjExMTo5NC0xMTE6MTIwIiB4c2k6dHlwZT0iRW5kIj4NCiAgICAgICAgICAgICAgICAgIDxvcGVyYW5kIGxvY2FsSWQ9IjIwNiIgbG9jYXRvcj0iMTExOjEwMS0xMTE6MTIwIiBuYW1lPSJNZWFzdXJlbWVudCBQZXJpb2QiIHhzaTp0eXBlPSJQYXJhbWV0ZXJSZWYiLz4NCiAgICAgICAgICAgICAgIDwvb3BlcmFuZD4NCiAgICAgICAgICAgIDwvd2hlcmU+DQogICAgICAgICA8L2V4cHJlc3Npb24+DQogICAgICA8L2RlZj4NCiAgICAgIDxkZWYgbG9jYWxJZD0iMjI1IiBsb2NhdG9yPSIxMTM6MS0xMTg6OTAiIG5hbWU9IkRlbm9taW5hdG9yIEV4Y2x1c2lvbiIgY29udGV4dD0iUGF0aWVudCIgYWNjZXNzTGV2ZWw9IlB1YmxpYyI+DQogICAgICAgICA8YW5ub3RhdGlvbiB4c2k6dHlwZT0iYTpBbm5vdGF0aW9uIj4NCiAgICAgICAgICAgIDxhOnMgcj0iMjI1Ij4NCiAgICAgICAgICAgICAgIDxhOnM+ZGVmaW5lICZxdW90O0Rlbm9taW5hdG9yIEV4Y2x1c2lvbiZxdW90OzoKICA8L2E6cz4NCiAgICAgICAgICAgICAgIDxhOnMgcj0iMjI0Ij4NCiAgICAgICAgICAgICAgICAgIDxhOnMgcj0iMjE4Ij4NCiAgICAgICAgICAgICAgICAgICAgIDxhOnMgcj0iMjE1Ij4NCiAgICAgICAgICAgICAgICAgICAgICAgIDxhOnMgcj0iMjEyIj4NCiAgICAgICAgICAgICAgICAgICAgICAgICAgIDxhOnMgcj0iMjExIj4NCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDxhOnM+SG9zcGljZTwvYTpzPg0KICAgICAgICAgICAgICAgICAgICAgICAgICAgPC9hOnM+DQogICAgICAgICAgICAgICAgICAgICAgICAgICA8YTpzPi48L2E6cz4NCiAgICAgICAgICAgICAgICAgICAgICAgICAgIDxhOnMgcj0iMjEyIj4NCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDxhOnM+JnF1b3Q7SGFzIEhvc3BpY2UmcXVvdDs8L2E6cz4NCiAgICAgICAgICAgICAgICAgICAgICAgICAgIDwvYTpzPg0KICAgICAgICAgICAgICAgICAgICAgICAgPC9hOnM+DQogICAgICAgICAgICAgICAgICAgICAgICA8YTpzPgogICAgICAgICAgICAgICAgICAgIAkJb3IgPC9hOnM+DQogICAgICAgICAgICAgICAgICAgICAgICA8YTpzIHI9IjIxNCI+DQogICAgICAgICAgICAgICAgICAgICAgICAgICA8YTpzPmV4aXN0cyA8L2E6cz4NCiAgICAgICAgICAgICAgICAgICAgICAgICAgIDxhOnMgcj0iMjEzIj4NCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDxhOnM+JnF1b3Q7TWFsaWduYW50IE5lb3BsYXNtJnF1b3Q7PC9hOnM+DQogICAgICAgICAgICAgICAgICAgICAgICAgICA8L2E6cz4NCiAgICAgICAgICAgICAgICAgICAgICAgIDwvYTpzPg0KICAgICAgICAgICAgICAgICAgICAgPC9hOnM+DQogICAgICAgICAgICAgICAgICAgICA8YTpzPgogICAgICAgICAgICAgICAgICAgIAkJb3IgPC9hOnM+DQogICAgICAgICAgICAgICAgICAgICA8YTpzIHI9IjIxNyI+DQogICAgICAgICAgICAgICAgICAgICAgICA8YTpzPmV4aXN0cyA8L2E6cz4NCiAgICAgICAgICAgICAgICAgICAgICAgIDxhOnMgcj0iMjE2Ij4NCiAgICAgICAgICAgICAgICAgICAgICAgICAgIDxhOnM+JnF1b3Q7VG90YWwgQ29sZWN0b215IFBlcmZvcm1lZCZxdW90OzwvYTpzPg0KICAgICAgICAgICAgICAgICAgICAgICAgPC9hOnM+DQogICAgICAgICAgICAgICAgICAgICA8L2E6cz4NCiAgICAgICAgICAgICAgICAgIDwvYTpzPg0KICAgICAgICAgICAgICAgICAgPGE6cz4KICAgICAgICAgICAgICAgICAgICAJCW9yIDwvYTpzPg0KICAgICAgICAgICAgICAgICAgPGE6cyByPSIyMjMiPg0KICAgICAgICAgICAgICAgICAgICAgPGE6cyByPSIyMjAiPg0KICAgICAgICAgICAgICAgICAgICAgICAgPGE6cyByPSIyMTkiPg0KICAgICAgICAgICAgICAgICAgICAgICAgICAgPGE6cz5GcmFpbHR5PC9hOnM+DQogICAgICAgICAgICAgICAgICAgICAgICA8L2E6cz4NCiAgICAgICAgICAgICAgICAgICAgICAgIDxhOnM+LiA8L2E6cz4NCiAgICAgICAgICAgICAgICAgICAgICAgIDxhOnMgcj0iMjIwIj4NCiAgICAgICAgICAgICAgICAgICAgICAgICAgIDxhOnM+JnF1b3Q7QWR2YW5jZWQgSWxsbmVzcyBhbmQgRnJhaWx0eSBFeGNsdXNpb24gTm90IEluY2x1ZGluZyBPdmVyIEFnZSA4MCZxdW90OzwvYTpzPg0KICAgICAgICAgICAgICAgICAgICAgICAgPC9hOnM+DQogICAgICAgICAgICAgICAgICAgICA8L2E6cz4NCiAgICAgICAgICAgICAgICAgICAgIDxhOnM+CiAgICAgICAgICAgICAgICAgICAgCQlhbmQgPC9hOnM+DQogICAgICAgICAgICAgICAgICAgICA8YTpzIHI9IjIyMiI+DQogICAgICAgICAgICAgICAgICAgICAgICA8YTpzIHI9IjIyMSI+DQogICAgICAgICAgICAgICAgICAgICAgICAgICA8YTpzPkZyYWlsdHk8L2E6cz4NCiAgICAgICAgICAgICAgICAgICAgICAgIDwvYTpzPg0KICAgICAgICAgICAgICAgICAgICAgICAgPGE6cz4uPC9hOnM+DQogICAgICAgICAgICAgICAgICAgICAgICA8YTpzIHI9IjIyMiI+DQogICAgICAgICAgICAgICAgICAgICAgICAgICA8YTpzPiZxdW90O0xvbmcgVGVybSBDYXJlIFBlcmlvZHMgTG9uZ2VyIFRoYW4gOTAgQ29uc2VjdXRpdmUgRGF5cyZxdW90OzwvYTpzPg0KICAgICAgICAgICAgICAgICAgICAgICAgPC9hOnM+DQogICAgICAgICAgICAgICAgICAgICA8L2E6cz4NCiAgICAgICAgICAgICAgICAgIDwvYTpzPg0KICAgICAgICAgICAgICAgPC9hOnM+DQogICAgICAgICAgICA8L2E6cz4NCiAgICAgICAgIDwvYW5ub3RhdGlvbj4NCiAgICAgICAgIDxleHByZXNzaW9uIGxvY2FsSWQ9IjIyNCIgbG9jYXRvcj0iMTE0OjMtMTE4OjkwIiB4c2k6dHlwZT0iT3IiPg0KICAgICAgICAgICAgPG9wZXJhbmQgbG9jYWxJZD0iMjE4IiBsb2NhdG9yPSIxMTQ6My0xMTY6NTkiIHhzaTp0eXBlPSJPciI+DQogICAgICAgICAgICAgICA8b3BlcmFuZCBsb2NhbElkPSIyMTUiIGxvY2F0b3I9IjExNDozLTExNTo1MiIgeHNpOnR5cGU9Ik9yIj4NCiAgICAgICAgICAgICAgICAgIDxvcGVyYW5kIGxvY2FsSWQ9IjIxMiIgbG9jYXRvcj0iMTE0OjMtMTE0OjIzIiBuYW1lPSJIYXMgSG9zcGljZSIgbGlicmFyeU5hbWU9Ikhvc3BpY2UiIHhzaTp0eXBlPSJFeHByZXNzaW9uUmVmIi8+DQogICAgICAgICAgICAgICAgICA8b3BlcmFuZCBsb2NhbElkPSIyMTQiIGxvY2F0b3I9IjExNToyNi0xMTU6NTIiIHhzaTp0eXBlPSJFeGlzdHMiPg0KICAgICAgICAgICAgICAgICAgICAgPG9wZXJhbmQgbG9jYWxJZD0iMjEzIiBsb2NhdG9yPSIxMTU6MzMtMTE1OjUyIiBuYW1lPSJNYWxpZ25hbnQgTmVvcGxhc20iIHhzaTp0eXBlPSJFeHByZXNzaW9uUmVmIi8+DQogICAgICAgICAgICAgICAgICA8L29wZXJhbmQ+DQogICAgICAgICAgICAgICA8L29wZXJhbmQ+DQogICAgICAgICAgICAgICA8b3BlcmFuZCBsb2NhbElkPSIyMTciIGxvY2F0b3I9IjExNjoyNi0xMTY6NTkiIHhzaTp0eXBlPSJFeGlzdHMiPg0KICAgICAgICAgICAgICAgICAgPG9wZXJhbmQgbG9jYWxJZD0iMjE2IiBsb2NhdG9yPSIxMTY6MzMtMTE2OjU5IiBuYW1lPSJUb3RhbCBDb2xlY3RvbXkgUGVyZm9ybWVkIiB4c2k6dHlwZT0iRXhwcmVzc2lvblJlZiIvPg0KICAgICAgICAgICAgICAgPC9vcGVyYW5kPg0KICAgICAgICAgICAgPC9vcGVyYW5kPg0KICAgICAgICAgICAgPG9wZXJhbmQgbG9jYWxJZD0iMjIzIiBsb2NhdG9yPSIxMTc6MjYtMTE4OjkwIiB4c2k6dHlwZT0iQW5kIj4NCiAgICAgICAgICAgICAgIDxvcGVyYW5kIGxvY2FsSWQ9IjIyMCIgbG9jYXRvcj0iMTE3OjI2LTExNzoxMDAiIG5hbWU9IkFkdmFuY2VkIElsbG5lc3MgYW5kIEZyYWlsdHkgRXhjbHVzaW9uIE5vdCBJbmNsdWRpbmcgT3ZlciBBZ2UgODAiIGxpYnJhcnlOYW1lPSJGcmFpbHR5IiB4c2k6dHlwZT0iRXhwcmVzc2lvblJlZiIvPg0KICAgICAgICAgICAgICAgPG9wZXJhbmQgbG9jYWxJZD0iMjIyIiBsb2NhdG9yPSIxMTg6MjctMTE4OjkwIiBuYW1lPSJMb25nIFRlcm0gQ2FyZSBQZXJpb2RzIExvbmdlciBUaGFuIDkwIENvbnNlY3V0aXZlIERheXMiIGxpYnJhcnlOYW1lPSJGcmFpbHR5IiB4c2k6dHlwZT0iRXhwcmVzc2lvblJlZiIvPg0KICAgICAgICAgICAgPC9vcGVyYW5kPg0KICAgICAgICAgPC9leHByZXNzaW9uPg0KICAgICAgPC9kZWY+DQogICA8L3N0YXRlbWVudHM+DQo8L2xpYnJhcnk+DQo=" + } + ] + }, + "request": { + "method": "PUT", + "url": "Library/ColorectalCancerScreeningsFHIR", + "ifNoneExist": "Library?url=http://ecqi.healthit.gov/ecqms/Library/ColorectalCancerScreeningsFHIR&version=0.0.001" + } + } + ]} diff --git a/hapi-fhir-storage-cr/src/test/resources/multiversion/EXM130-0.0.002-bundle.json b/hapi-fhir-storage-cr/src/test/resources/multiversion/EXM130-0.0.002-bundle.json new file mode 100644 index 00000000000..d3cba496293 --- /dev/null +++ b/hapi-fhir-storage-cr/src/test/resources/multiversion/EXM130-0.0.002-bundle.json @@ -0,0 +1,920 @@ +{ + "resourceType": "Bundle", + "id": "GeneratedBundle", + "type": "transaction", + "entry": [ + { + "resource": { + "resourceType": "Library", + "id": "ColorectalCancerScreeningsFHIR", + "meta": { + "profile": [ + "http://hl7.org/fhir/us/cqfmeasures/StructureDefinition/computable-library-cqfm" + ] + }, + "language": "en", + "extension": [ + { + "url": "http://hl7.org/fhir/us/cqfmeasures/StructureDefinition/cqfm-softwaresystem", + "valueReference": { + "reference": "Device/cqf-tooling" + } + } + ], + "url": "http://ecqi.healthit.gov/ecqms/Library/ColorectalCancerScreeningsFHIR", + "version": "0.0.002", + "name": "ColorectalCancerScreeningsFHIR", + "status": "active", + "experimental": false, + "type": { + "coding": [ + { + "system": "http://terminology.hl7.org/CodeSystem/library-type", + "code": "logic-library" + } + ] + }, + "date": "2021-04-23T15:32:03+00:00", + "publisher": "National Committee for Quality Assurance", + "description": "Colorectal Cancer ScreeningFHIR", + "relatedArtifact": [ + { + "type": "depends-on", + "display": "FHIR model information", + "resource": "http://fhir.org/guides/cqf/common/Library/FHIR-ModelInfo|4.0.1" + }, + { + "type": "depends-on", + "display": "Library FHIRHelpers", + "resource": "http://ecqi.healthit.gov/ecqms/Library/FHIRHelpers|4.0.001" + }, + { + "type": "depends-on", + "display": "Library SDE", + "resource": "http://ecqi.healthit.gov/ecqms/Library/SupplementalDataElementsFHIR4|2.0.000" + }, + { + "type": "depends-on", + "display": "Library Global", + "resource": "http://ecqi.healthit.gov/ecqms/Library/MATGlobalCommonFunctionsFHIR4|6.0.000" + }, + { + "type": "depends-on", + "display": "Library AdultOutpatientEncounters", + "resource": "http://ecqi.healthit.gov/ecqms/Library/AdultOutpatientEncountersFHIR4|2.0.000" + }, + { + "type": "depends-on", + "display": "Library Hospice", + "resource": "http://ecqi.healthit.gov/ecqms/Library/HospiceFHIR4|2.0.000" + }, + { + "type": "depends-on", + "display": "Library Frailty", + "resource": "http://ecqi.healthit.gov/ecqms/Library/AdvancedIllnessandFrailtyExclusionECQMFHIR4|5.12.000" + }, + { + "type": "depends-on", + "display": "Code system LOINC", + "resource": "http://loinc.org" + }, + { + "type": "depends-on", + "display": "Code system SNOMEDCT:2017-09", + "resource": "http://snomed.info/sct|http://snomed.info/sct/version/201709" + }, + { + "type": "depends-on", + "display": "Value set Acute Inpatient", + "resource": "http://cts.nlm.nih.gov/fhir/ValueSet/2.16.840.1.113883.3.464.1003.101.12.1083" + }, + { + "type": "depends-on", + "display": "Value set Advanced Illness", + "resource": "http://cts.nlm.nih.gov/fhir/ValueSet/2.16.840.1.113883.3.464.1003.110.12.1082" + }, + { + "type": "depends-on", + "display": "Value set Annual Wellness Visit", + "resource": "http://cts.nlm.nih.gov/fhir/ValueSet/2.16.840.1.113883.3.526.3.1240" + }, + { + "type": "depends-on", + "display": "Value set Care Services in Long-Term Residential Facility", + "resource": "http://cts.nlm.nih.gov/fhir/ValueSet/2.16.840.1.113883.3.464.1003.101.12.1014" + }, + { + "type": "depends-on", + "display": "Value set Colonoscopy", + "resource": "http://cts.nlm.nih.gov/fhir/ValueSet/2.16.840.1.113883.3.464.1003.108.12.1020" + }, + { + "type": "depends-on", + "display": "Value set CT Colonography", + "resource": "http://cts.nlm.nih.gov/fhir/ValueSet/2.16.840.1.113883.3.464.1003.108.12.1038" + }, + { + "type": "depends-on", + "display": "Value set Dementia Medications", + "resource": "http://cts.nlm.nih.gov/fhir/ValueSet/2.16.840.1.113883.3.464.1003.196.12.1510" + }, + { + "type": "depends-on", + "display": "Value set Discharged to Health Care Facility for Hospice Care", + "resource": "http://cts.nlm.nih.gov/fhir/ValueSet/2.16.840.1.113883.3.117.1.7.1.207" + }, + { + "type": "depends-on", + "display": "Value set Discharged to Home for Hospice Care", + "resource": "http://cts.nlm.nih.gov/fhir/ValueSet/2.16.840.1.113883.3.117.1.7.1.209" + }, + { + "type": "depends-on", + "display": "Value set ED", + "resource": "http://cts.nlm.nih.gov/fhir/ValueSet/2.16.840.1.113883.3.464.1003.101.12.1085" + }, + { + "type": "depends-on", + "display": "Value set Encounter Inpatient", + "resource": "http://cts.nlm.nih.gov/fhir/ValueSet/2.16.840.1.113883.3.666.5.307" + }, + { + "type": "depends-on", + "display": "Value set Fecal Occult Blood Test (FOBT)", + "resource": "http://cts.nlm.nih.gov/fhir/ValueSet/2.16.840.1.113883.3.464.1003.198.12.1011" + }, + { + "type": "depends-on", + "display": "Value set FIT DNA", + "resource": "http://cts.nlm.nih.gov/fhir/ValueSet/2.16.840.1.113883.3.464.1003.108.12.1039" + }, + { + "type": "depends-on", + "display": "Value set Flexible Sigmoidoscopy", + "resource": "http://cts.nlm.nih.gov/fhir/ValueSet/2.16.840.1.113883.3.464.1003.198.12.1010" + }, + { + "type": "depends-on", + "display": "Value set Frailty Device", + "resource": "http://cts.nlm.nih.gov/fhir/ValueSet/2.16.840.1.113883.3.464.1003.118.12.1300" + }, + { + "type": "depends-on", + "display": "Value set Frailty Diagnosis", + "resource": "http://cts.nlm.nih.gov/fhir/ValueSet/2.16.840.1.113883.3.464.1003.113.12.1074" + }, + { + "type": "depends-on", + "display": "Value set Frailty Encounter", + "resource": "http://cts.nlm.nih.gov/fhir/ValueSet/2.16.840.1.113883.3.464.1003.101.12.1088" + }, + { + "type": "depends-on", + "display": "Value set Frailty Symptom", + "resource": "http://cts.nlm.nih.gov/fhir/ValueSet/2.16.840.1.113883.3.464.1003.113.12.1075" + }, + { + "type": "depends-on", + "display": "Value set Home Healthcare Services", + "resource": "http://cts.nlm.nih.gov/fhir/ValueSet/2.16.840.1.113883.3.464.1003.101.12.1016" + }, + { + "type": "depends-on", + "display": "Value set Hospice care ambulatory", + "resource": "http://cts.nlm.nih.gov/fhir/ValueSet/2.16.840.1.113762.1.4.1108.15" + }, + { + "type": "depends-on", + "display": "Value set Malignant Neoplasm of Colon", + "resource": "http://cts.nlm.nih.gov/fhir/ValueSet/2.16.840.1.113883.3.464.1003.108.12.1001" + }, + { + "type": "depends-on", + "display": "Value set Nonacute Inpatient", + "resource": "http://cts.nlm.nih.gov/fhir/ValueSet/2.16.840.1.113883.3.464.1003.101.12.1084" + }, + { + "type": "depends-on", + "display": "Value set Nursing Facility Visit", + "resource": "http://cts.nlm.nih.gov/fhir/ValueSet/2.16.840.1.113883.3.464.1003.101.12.1012" + }, + { + "type": "depends-on", + "display": "Value set Observation", + "resource": "http://cts.nlm.nih.gov/fhir/ValueSet/2.16.840.1.113883.3.464.1003.101.12.1086" + }, + { + "type": "depends-on", + "display": "Value set Office Visit", + "resource": "http://cts.nlm.nih.gov/fhir/ValueSet/2.16.840.1.113883.3.464.1003.101.12.1001" + }, + { + "type": "depends-on", + "display": "Value set Outpatient", + "resource": "http://cts.nlm.nih.gov/fhir/ValueSet/2.16.840.1.113883.3.464.1003.101.12.1087" + }, + { + "type": "depends-on", + "display": "Value set Preventive Care Services - Established Office Visit, 18 and Up", + "resource": "http://cts.nlm.nih.gov/fhir/ValueSet/2.16.840.1.113883.3.464.1003.101.12.1025" + }, + { + "type": "depends-on", + "display": "Value set Preventive Care Services-Initial Office Visit, 18 and Up", + "resource": "http://cts.nlm.nih.gov/fhir/ValueSet/2.16.840.1.113883.3.464.1003.101.12.1023" + }, + { + "type": "depends-on", + "display": "Value set Total Colectomy", + "resource": "http://cts.nlm.nih.gov/fhir/ValueSet/2.16.840.1.113883.3.464.1003.198.12.1019" + }, + { + "type": "depends-on", + "display": "Value set Payer", + "resource": "http://cts.nlm.nih.gov/fhir/ValueSet/2.16.840.1.114222.4.11.3591" + } + ], + "parameter": [ + { + "name": "Measurement Period", + "use": "in", + "min": 0, + "max": "1", + "type": "Period" + }, + { + "name": "Patient", + "use": "out", + "min": 0, + "max": "1", + "type": "Patient" + }, + { + "name": "SDE Ethnicity", + "use": "out", + "min": 0, + "max": "*", + "type": "Coding" + }, + { + "name": "SDE Payer", + "use": "out", + "min": 0, + "max": "*", + "type": "Any" + }, + { + "name": "SDE Race", + "use": "out", + "min": 0, + "max": "*", + "type": "Coding" + }, + { + "name": "SDE Sex", + "use": "out", + "min": 0, + "max": "1", + "type": "Coding" + }, + { + "name": "Initial Population", + "use": "out", + "min": 0, + "max": "1", + "type": "boolean" + }, + { + "name": "Denominator", + "use": "out", + "min": 0, + "max": "1", + "type": "boolean" + }, + { + "name": "Flexible Sigmoidoscopy Performed", + "use": "out", + "min": 0, + "max": "*", + "type": "Procedure" + }, + { + "name": "CT Colonography Performed", + "use": "out", + "min": 0, + "max": "*", + "type": "Procedure" + }, + { + "name": "Total Colectomy Performed", + "use": "out", + "min": 0, + "max": "*", + "type": "Procedure" + }, + { + "name": "Fecal Occult Blood Test Performed", + "use": "out", + "min": 0, + "max": "*", + "type": "Observation" + }, + { + "name": "Fecal Immunochemical Test DNA", + "use": "out", + "min": 0, + "max": "*", + "type": "Observation" + }, + { + "name": "Colonoscopy Performed", + "use": "out", + "min": 0, + "max": "*", + "type": "Procedure" + }, + { + "name": "Numerator", + "use": "out", + "min": 0, + "max": "1", + "type": "boolean" + }, + { + "name": "Malignant Neoplasm", + "use": "out", + "min": 0, + "max": "*", + "type": "Condition" + }, + { + "name": "Denominator Exclusion", + "use": "out", + "min": 0, + "max": "1", + "type": "boolean" + } + ], + "dataRequirement": [ + { + "type": "Patient", + "profile": [ + "http://hl7.org/fhir/StructureDefinition/Patient" + ] + }, + { + "type": "Patient", + "profile": [ + "http://hl7.org/fhir/StructureDefinition/Patient" + ], + "mustSupport": [ + "extension", + "value", + "url" + ], + "codeFilter": [ + { + "path": "url", + "code": [ + { + "code": "http://hl7.org/fhir/us/core/StructureDefinition/us-core-ethnicity" + } + ] + } + ] + }, + { + "type": "Patient", + "profile": [ + "http://hl7.org/fhir/StructureDefinition/Patient" + ], + "mustSupport": [ + "extension", + "value", + "url" + ], + "codeFilter": [ + { + "path": "url", + "code": [ + { + "code": "http://hl7.org/fhir/us/core/StructureDefinition/us-core-race" + } + ] + } + ] + }, + { + "type": "Procedure", + "profile": [ + "http://hl7.org/fhir/StructureDefinition/Procedure" + ], + "mustSupport": [ + "code", + "performed", + "status" + ], + "codeFilter": [ + { + "path": "code", + "valueSet": "http://cts.nlm.nih.gov/fhir/ValueSet/2.16.840.1.113883.3.464.1003.198.12.1010" + }, + { + "path": "status", + "code": [ + { + "code": "completed" + } + ] + } + ] + }, + { + "type": "Procedure", + "profile": [ + "http://hl7.org/fhir/StructureDefinition/Procedure" + ], + "mustSupport": [ + "code", + "performed", + "status" + ], + "codeFilter": [ + { + "path": "code", + "valueSet": "http://cts.nlm.nih.gov/fhir/ValueSet/2.16.840.1.113883.3.464.1003.108.12.1038" + }, + { + "path": "status", + "code": [ + { + "code": "completed" + } + ] + } + ] + }, + { + "type": "Procedure", + "profile": [ + "http://hl7.org/fhir/StructureDefinition/Procedure" + ], + "mustSupport": [ + "code", + "performed", + "status" + ], + "codeFilter": [ + { + "path": "code", + "valueSet": "http://cts.nlm.nih.gov/fhir/ValueSet/2.16.840.1.113883.3.464.1003.198.12.1019" + }, + { + "path": "status", + "code": [ + { + "code": "completed" + } + ] + } + ] + }, + { + "type": "Procedure", + "profile": [ + "http://hl7.org/fhir/StructureDefinition/Procedure" + ], + "mustSupport": [ + "code", + "performed", + "status" + ], + "codeFilter": [ + { + "path": "code", + "valueSet": "http://cts.nlm.nih.gov/fhir/ValueSet/2.16.840.1.113883.3.464.1003.108.12.1020" + }, + { + "path": "status", + "code": [ + { + "code": "completed" + } + ] + } + ] + }, + { + "type": "Procedure", + "profile": [ + "http://hl7.org/fhir/StructureDefinition/Procedure" + ], + "mustSupport": [ + "code", + "performed", + "status" + ], + "codeFilter": [ + { + "path": "code", + "valueSet": "http://cts.nlm.nih.gov/fhir/ValueSet/2.16.840.1.113762.1.4.1108.15" + }, + { + "path": "status", + "code": [ + { + "code": "completed" + } + ] + }, + { + "path": "status", + "code": [ + { + "code": "completed" + } + ] + }, + { + "path": "status", + "code": [ + { + "code": "completed" + } + ] + }, + { + "path": "status", + "code": [ + { + "code": "completed" + } + ] + } + ] + }, + { + "type": "Observation", + "profile": [ + "http://hl7.org/fhir/StructureDefinition/Observation" + ], + "mustSupport": [ + "effective", + "code", + "value", + "status" + ], + "codeFilter": [ + { + "path": "code", + "valueSet": "http://cts.nlm.nih.gov/fhir/ValueSet/2.16.840.1.113883.3.464.1003.198.12.1011" + } + ] + }, + { + "type": "Observation", + "profile": [ + "http://hl7.org/fhir/StructureDefinition/Observation" + ], + "mustSupport": [ + "effective", + "code", + "value", + "status" + ], + "codeFilter": [ + { + "path": "code", + "valueSet": "http://cts.nlm.nih.gov/fhir/ValueSet/2.16.840.1.113883.3.464.1003.108.12.1039" + } + ] + }, + { + "type": "Observation", + "profile": [ + "http://hl7.org/fhir/StructureDefinition/Observation" + ], + "mustSupport": [ + "effective", + "code" + ], + "codeFilter": [ + { + "path": "code", + "valueSet": "http://cts.nlm.nih.gov/fhir/ValueSet/2.16.840.1.113883.3.464.1003.113.12.1075" + } + ] + }, + { + "type": "Condition", + "profile": [ + "http://hl7.org/fhir/StructureDefinition/Condition" + ], + "mustSupport": [ + "code" + ], + "codeFilter": [ + { + "path": "code", + "valueSet": "http://cts.nlm.nih.gov/fhir/ValueSet/2.16.840.1.113883.3.464.1003.108.12.1001" + } + ] + }, + { + "type": "Condition", + "profile": [ + "http://hl7.org/fhir/StructureDefinition/Condition" + ], + "mustSupport": [ + "code" + ], + "codeFilter": [ + { + "path": "code", + "valueSet": "http://cts.nlm.nih.gov/fhir/ValueSet/2.16.840.1.113883.3.464.1003.113.12.1074" + } + ] + }, + { + "type": "Encounter", + "profile": [ + "http://hl7.org/fhir/StructureDefinition/Encounter" + ], + "mustSupport": [ + "period", + "hospitalization", + "hospitalization.dischargeDisposition", + "type", + "status" + ], + "codeFilter": [ + { + "path": "type", + "valueSet": "http://cts.nlm.nih.gov/fhir/ValueSet/2.16.840.1.113883.3.666.5.307" + } + ], + "dateFilter": [ + { + "path": "period", + "_valueDateTime": { + "extension": [ + { + "url": "http://hl7.org/fhir/StructureDefinition/cqf-expression", + "valueExpression": { + "language": "text/cql-identifier", + "expression": "Measurement Period" + } + } + ] + } + }, + { + "path": "period", + "_valueDateTime": { + "extension": [ + { + "url": "http://hl7.org/fhir/StructureDefinition/cqf-expression", + "valueExpression": { + "language": "text/cql-identifier", + "expression": "Measurement Period" + } + } + ] + } + }, + { + "path": "period", + "_valueDateTime": { + "extension": [ + { + "url": "http://hl7.org/fhir/StructureDefinition/cqf-expression", + "valueExpression": { + "language": "text/cql-identifier", + "expression": "Measurement Period" + } + } + ] + } + }, + { + "path": "period", + "_valueDateTime": { + "extension": [ + { + "url": "http://hl7.org/fhir/StructureDefinition/cqf-expression", + "valueExpression": { + "language": "text/cql-identifier", + "expression": "Measurement Period" + } + } + ] + } + } + ] + }, + { + "type": "Encounter", + "profile": [ + "http://hl7.org/fhir/StructureDefinition/Encounter" + ], + "mustSupport": [ + "period", + "type" + ], + "codeFilter": [ + { + "path": "type", + "valueSet": "http://cts.nlm.nih.gov/fhir/ValueSet/2.16.840.1.113883.3.464.1003.101.12.1088" + } + ] + }, + { + "type": "ServiceRequest", + "profile": [ + "http://hl7.org/fhir/StructureDefinition/ServiceRequest" + ], + "mustSupport": [ + "code", + "authoredOn", + "intent" + ], + "codeFilter": [ + { + "path": "code", + "valueSet": "http://cts.nlm.nih.gov/fhir/ValueSet/2.16.840.1.113762.1.4.1108.15" + }, + { + "path": "intent", + "code": [ + { + "code": "order" + } + ] + }, + { + "path": "intent", + "code": [ + { + "code": "order" + } + ] + }, + { + "path": "intent", + "code": [ + { + "code": "order" + } + ] + }, + { + "path": "intent", + "code": [ + { + "code": "order" + } + ] + } + ], + "dateFilter": [ + { + "path": "authoredOn", + "_valueDateTime": { + "extension": [ + { + "url": "http://hl7.org/fhir/StructureDefinition/cqf-expression", + "valueExpression": { + "language": "text/cql-identifier", + "expression": "Measurement Period" + } + } + ] + } + }, + { + "path": "authoredOn", + "_valueDateTime": { + "extension": [ + { + "url": "http://hl7.org/fhir/StructureDefinition/cqf-expression", + "valueExpression": { + "language": "text/cql-identifier", + "expression": "Measurement Period" + } + } + ] + } + }, + { + "path": "authoredOn", + "_valueDateTime": { + "extension": [ + { + "url": "http://hl7.org/fhir/StructureDefinition/cqf-expression", + "valueExpression": { + "language": "text/cql-identifier", + "expression": "Measurement Period" + } + } + ] + } + }, + { + "path": "authoredOn", + "_valueDateTime": { + "extension": [ + { + "url": "http://hl7.org/fhir/StructureDefinition/cqf-expression", + "valueExpression": { + "language": "text/cql-identifier", + "expression": "Measurement Period" + } + } + ] + } + } + ] + }, + { + "type": "DeviceRequest", + "profile": [ + "http://hl7.org/fhir/StructureDefinition/DeviceRequest" + ], + "mustSupport": [ + "code", + "authoredOn" + ], + "codeFilter": [ + { + "path": "code", + "valueSet": "http://cts.nlm.nih.gov/fhir/ValueSet/2.16.840.1.113883.3.464.1003.118.12.1300" + } + ], + "dateFilter": [ + { + "path": "authoredOn", + "_valueDateTime": { + "extension": [ + { + "url": "http://hl7.org/fhir/StructureDefinition/cqf-expression", + "valueExpression": { + "language": "text/cql-identifier", + "expression": "Measurement Period" + } + } + ] + } + }, + { + "path": "authoredOn", + "_valueDateTime": { + "extension": [ + { + "url": "http://hl7.org/fhir/StructureDefinition/cqf-expression", + "valueExpression": { + "language": "text/cql-identifier", + "expression": "Measurement Period" + } + } + ] + } + }, + { + "path": "authoredOn", + "_valueDateTime": { + "extension": [ + { + "url": "http://hl7.org/fhir/StructureDefinition/cqf-expression", + "valueExpression": { + "language": "text/cql-identifier", + "expression": "Measurement Period" + } + } + ] + } + } + ] + } + ], + "content": [ + { + "contentType": "text/cql", + "data": "bGlicmFyeSBDb2xvcmVjdGFsQ2FuY2VyU2NyZWVuaW5nc0ZISVIgdmVyc2lvbiAnMC4wLjAwMScNCg0KdXNpbmcgRkhJUiB2ZXJzaW9uICc0LjAuMScNCg0KaW5jbHVkZSBGSElSSGVscGVycyB2ZXJzaW9uICc0LjAuMDAxJyBjYWxsZWQgRkhJUkhlbHBlcnMNCmluY2x1ZGUgU3VwcGxlbWVudGFsRGF0YUVsZW1lbnRzRkhJUjQgdmVyc2lvbiAnMi4wLjAwMCcgY2FsbGVkIFNERQ0KaW5jbHVkZSBNQVRHbG9iYWxDb21tb25GdW5jdGlvbnNGSElSNCB2ZXJzaW9uICc2LjAuMDAwJyBjYWxsZWQgR2xvYmFsDQppbmNsdWRlIEFkdWx0T3V0cGF0aWVudEVuY291bnRlcnNGSElSNCB2ZXJzaW9uICcyLjAuMDAwJyBjYWxsZWQgQWR1bHRPdXRwYXRpZW50RW5jb3VudGVycw0KaW5jbHVkZSBIb3NwaWNlRkhJUjQgdmVyc2lvbiAnMi4wLjAwMCcgY2FsbGVkIEhvc3BpY2UNCmluY2x1ZGUgQWR2YW5jZWRJbGxuZXNzYW5kRnJhaWx0eUV4Y2x1c2lvbkVDUU1GSElSNCB2ZXJzaW9uICc1LjEyLjAwMCcgY2FsbGVkIEZyYWlsdHkNCg0KY29kZXN5c3RlbSAiTE9JTkMiOiAnaHR0cDovL2xvaW5jLm9yZycNCg0KdmFsdWVzZXQgIkFjdXRlIElucGF0aWVudCI6ICdodHRwOi8vY3RzLm5sbS5uaWguZ292L2ZoaXIvVmFsdWVTZXQvMi4xNi44NDAuMS4xMTM4ODMuMy40NjQuMTAwMy4xMDEuMTIuMTA4MycNCnZhbHVlc2V0ICJBZHZhbmNlZCBJbGxuZXNzIjogJ2h0dHA6Ly9jdHMubmxtLm5paC5nb3YvZmhpci9WYWx1ZVNldC8yLjE2Ljg0MC4xLjExMzg4My4zLjQ2NC4xMDAzLjExMC4xMi4xMDgyJw0KdmFsdWVzZXQgIkFubnVhbCBXZWxsbmVzcyBWaXNpdCI6ICdodHRwOi8vY3RzLm5sbS5uaWguZ292L2ZoaXIvVmFsdWVTZXQvMi4xNi44NDAuMS4xMTM4ODMuMy41MjYuMy4xMjQwJw0KdmFsdWVzZXQgIkNhcmUgU2VydmljZXMgaW4gTG9uZy1UZXJtIFJlc2lkZW50aWFsIEZhY2lsaXR5IjogJ2h0dHA6Ly9jdHMubmxtLm5paC5nb3YvZmhpci9WYWx1ZVNldC8yLjE2Ljg0MC4xLjExMzg4My4zLjQ2NC4xMDAzLjEwMS4xMi4xMDE0Jw0KdmFsdWVzZXQgIkNvbG9ub3Njb3B5IjogJ2h0dHA6Ly9jdHMubmxtLm5paC5nb3YvZmhpci9WYWx1ZVNldC8yLjE2Ljg0MC4xLjExMzg4My4zLjQ2NC4xMDAzLjEwOC4xMi4xMDIwJw0KdmFsdWVzZXQgIkNUIENvbG9ub2dyYXBoeSI6ICdodHRwOi8vY3RzLm5sbS5uaWguZ292L2ZoaXIvVmFsdWVTZXQvMi4xNi44NDAuMS4xMTM4ODMuMy40NjQuMTAwMy4xMDguMTIuMTAzOCcNCnZhbHVlc2V0ICJEZW1lbnRpYSBNZWRpY2F0aW9ucyI6ICdodHRwOi8vY3RzLm5sbS5uaWguZ292L2ZoaXIvVmFsdWVTZXQvMi4xNi44NDAuMS4xMTM4ODMuMy40NjQuMTAwMy4xOTYuMTIuMTUxMCcNCnZhbHVlc2V0ICJEaXNjaGFyZ2VkIHRvIEhlYWx0aCBDYXJlIEZhY2lsaXR5IGZvciBIb3NwaWNlIENhcmUiOiAnaHR0cDovL2N0cy5ubG0ubmloLmdvdi9maGlyL1ZhbHVlU2V0LzIuMTYuODQwLjEuMTEzODgzLjMuMTE3LjEuNy4xLjIwNycNCnZhbHVlc2V0ICJEaXNjaGFyZ2VkIHRvIEhvbWUgZm9yIEhvc3BpY2UgQ2FyZSI6ICdodHRwOi8vY3RzLm5sbS5uaWguZ292L2ZoaXIvVmFsdWVTZXQvMi4xNi44NDAuMS4xMTM4ODMuMy4xMTcuMS43LjEuMjA5Jw0KdmFsdWVzZXQgIkVEIjogJ2h0dHA6Ly9jdHMubmxtLm5paC5nb3YvZmhpci9WYWx1ZVNldC8yLjE2Ljg0MC4xLjExMzg4My4zLjQ2NC4xMDAzLjEwMS4xMi4xMDg1Jw0KdmFsdWVzZXQgIkVuY291bnRlciBJbnBhdGllbnQiOiAnaHR0cDovL2N0cy5ubG0ubmloLmdvdi9maGlyL1ZhbHVlU2V0LzIuMTYuODQwLjEuMTEzODgzLjMuNjY2LjUuMzA3Jw0KdmFsdWVzZXQgIkZlY2FsIE9jY3VsdCBCbG9vZCBUZXN0IChGT0JUKSI6ICdodHRwOi8vY3RzLm5sbS5uaWguZ292L2ZoaXIvVmFsdWVTZXQvMi4xNi44NDAuMS4xMTM4ODMuMy40NjQuMTAwMy4xOTguMTIuMTAxMScNCnZhbHVlc2V0ICJGSVQgRE5BIjogJ2h0dHA6Ly9jdHMubmxtLm5paC5nb3YvZmhpci9WYWx1ZVNldC8yLjE2Ljg0MC4xLjExMzg4My4zLjQ2NC4xMDAzLjEwOC4xMi4xMDM5Jw0KdmFsdWVzZXQgIkZsZXhpYmxlIFNpZ21vaWRvc2NvcHkiOiAnaHR0cDovL2N0cy5ubG0ubmloLmdvdi9maGlyL1ZhbHVlU2V0LzIuMTYuODQwLjEuMTEzODgzLjMuNDY0LjEwMDMuMTk4LjEyLjEwMTAnDQp2YWx1ZXNldCAiRnJhaWx0eSBEZXZpY2UiOiAnaHR0cDovL2N0cy5ubG0ubmloLmdvdi9maGlyL1ZhbHVlU2V0LzIuMTYuODQwLjEuMTEzODgzLjMuNDY0LjEwMDMuMTE4LjEyLjEzMDAnDQp2YWx1ZXNldCAiRnJhaWx0eSBEaWFnbm9zaXMiOiAnaHR0cDovL2N0cy5ubG0ubmloLmdvdi9maGlyL1ZhbHVlU2V0LzIuMTYuODQwLjEuMTEzODgzLjMuNDY0LjEwMDMuMTEzLjEyLjEwNzQnDQp2YWx1ZXNldCAiRnJhaWx0eSBFbmNvdW50ZXIiOiAnaHR0cDovL2N0cy5ubG0ubmloLmdvdi9maGlyL1ZhbHVlU2V0LzIuMTYuODQwLjEuMTEzODgzLjMuNDY0LjEwMDMuMTAxLjEyLjEwODgnDQp2YWx1ZXNldCAiRnJhaWx0eSBTeW1wdG9tIjogJ2h0dHA6Ly9jdHMubmxtLm5paC5nb3YvZmhpci9WYWx1ZVNldC8yLjE2Ljg0MC4xLjExMzg4My4zLjQ2NC4xMDAzLjExMy4xMi4xMDc1Jw0KdmFsdWVzZXQgIkhvbWUgSGVhbHRoY2FyZSBTZXJ2aWNlcyI6ICdodHRwOi8vY3RzLm5sbS5uaWguZ292L2ZoaXIvVmFsdWVTZXQvMi4xNi44NDAuMS4xMTM4ODMuMy40NjQuMTAwMy4xMDEuMTIuMTAxNicNCnZhbHVlc2V0ICJIb3NwaWNlIGNhcmUgYW1idWxhdG9yeSI6ICdodHRwOi8vY3RzLm5sbS5uaWguZ292L2ZoaXIvVmFsdWVTZXQvMi4xNi44NDAuMS4xMTM3NjIuMS40LjExMDguMTUnDQp2YWx1ZXNldCAiTWFsaWduYW50IE5lb3BsYXNtIG9mIENvbG9uIjogJ2h0dHA6Ly9jdHMubmxtLm5paC5nb3YvZmhpci9WYWx1ZVNldC8yLjE2Ljg0MC4xLjExMzg4My4zLjQ2NC4xMDAzLjEwOC4xMi4xMDAxJw0KdmFsdWVzZXQgIk5vbmFjdXRlIElucGF0aWVudCI6ICdodHRwOi8vY3RzLm5sbS5uaWguZ292L2ZoaXIvVmFsdWVTZXQvMi4xNi44NDAuMS4xMTM4ODMuMy40NjQuMTAwMy4xMDEuMTIuMTA4NCcNCnZhbHVlc2V0ICJOdXJzaW5nIEZhY2lsaXR5IFZpc2l0IjogJ2h0dHA6Ly9jdHMubmxtLm5paC5nb3YvZmhpci9WYWx1ZVNldC8yLjE2Ljg0MC4xLjExMzg4My4zLjQ2NC4xMDAzLjEwMS4xMi4xMDEyJw0KdmFsdWVzZXQgIk9ic2VydmF0aW9uIjogJ2h0dHA6Ly9jdHMubmxtLm5paC5nb3YvZmhpci9WYWx1ZVNldC8yLjE2Ljg0MC4xLjExMzg4My4zLjQ2NC4xMDAzLjEwMS4xMi4xMDg2Jw0KdmFsdWVzZXQgIk9mZmljZSBWaXNpdCI6ICdodHRwOi8vY3RzLm5sbS5uaWguZ292L2ZoaXIvVmFsdWVTZXQvMi4xNi44NDAuMS4xMTM4ODMuMy40NjQuMTAwMy4xMDEuMTIuMTAwMScNCnZhbHVlc2V0ICJPdXRwYXRpZW50IjogJ2h0dHA6Ly9jdHMubmxtLm5paC5nb3YvZmhpci9WYWx1ZVNldC8yLjE2Ljg0MC4xLjExMzg4My4zLjQ2NC4xMDAzLjEwMS4xMi4xMDg3Jw0KdmFsdWVzZXQgIlByZXZlbnRpdmUgQ2FyZSBTZXJ2aWNlcyAtIEVzdGFibGlzaGVkIE9mZmljZSBWaXNpdCwgMTggYW5kIFVwIjogJ2h0dHA6Ly9jdHMubmxtLm5paC5nb3YvZmhpci9WYWx1ZVNldC8yLjE2Ljg0MC4xLjExMzg4My4zLjQ2NC4xMDAzLjEwMS4xMi4xMDI1Jw0KdmFsdWVzZXQgIlByZXZlbnRpdmUgQ2FyZSBTZXJ2aWNlcy1Jbml0aWFsIE9mZmljZSBWaXNpdCwgMTggYW5kIFVwIjogJ2h0dHA6Ly9jdHMubmxtLm5paC5nb3YvZmhpci9WYWx1ZVNldC8yLjE2Ljg0MC4xLjExMzg4My4zLjQ2NC4xMDAzLjEwMS4xMi4xMDIzJw0KdmFsdWVzZXQgIlRvdGFsIENvbGVjdG9teSI6ICdodHRwOi8vY3RzLm5sbS5uaWguZ292L2ZoaXIvVmFsdWVTZXQvMi4xNi44NDAuMS4xMTM4ODMuMy40NjQuMTAwMy4xOTguMTIuMTAxOScNCg0KY29kZSAiQmlydGggZGF0ZSI6ICcyMTExMi04JyBmcm9tICJMT0lOQyIgZGlzcGxheSAnQmlydGggZGF0ZScNCg0KcGFyYW1ldGVyICJNZWFzdXJlbWVudCBQZXJpb2QiIEludGVydmFsPERhdGVUaW1lPg0KICBkZWZhdWx0IEludGVydmFsW0AyMDE5LTAxLTAxVDAwOjAwOjAwLjAsIEAyMDIwLTAxLTAxVDAwOjAwOjAwLjApDQoNCmNvbnRleHQgUGF0aWVudA0KDQpkZWZpbmUgIlNERSBFdGhuaWNpdHkiOg0KICBTREUuIlNERSBFdGhuaWNpdHkiDQoNCmRlZmluZSAiU0RFIFBheWVyIjoNCiAgU0RFLiJTREUgUGF5ZXIiDQoNCmRlZmluZSAiU0RFIFJhY2UiOg0KICBTREUuIlNERSBSYWNlIg0KDQpkZWZpbmUgIlNERSBTZXgiOg0KICBTREUuIlNERSBTZXgiDQoNCmRlZmluZSAiSW5pdGlhbCBQb3B1bGF0aW9uIjoNCiAgQWdlSW5ZZWFyc0F0KHN0YXJ0IG9mICJNZWFzdXJlbWVudCBQZXJpb2QiKSBiZXR3ZWVuIDUwIGFuZCA3NQ0KCSBhbmQgZXhpc3RzIEFkdWx0T3V0cGF0aWVudEVuY291bnRlcnMuIlF1YWxpZnlpbmcgRW5jb3VudGVycyINCg0KZGVmaW5lICJEZW5vbWluYXRvciI6DQogICJJbml0aWFsIFBvcHVsYXRpb24iDQoNCmRlZmluZSAiRmxleGlibGUgU2lnbW9pZG9zY29weSBQZXJmb3JtZWQiOg0KICBbUHJvY2VkdXJlOiAiRmxleGlibGUgU2lnbW9pZG9zY29weSJdIEZsZXhpYmxlU2lnbW9pZG9zY29weQ0KICAgICAgICAgICAgICAgICAgICAJCXdoZXJlIEZsZXhpYmxlU2lnbW9pZG9zY29weS5zdGF0dXMgPSAnY29tcGxldGVkJw0KICAgICAgICAgICAgICAgICAgICAJCQlhbmQgR2xvYmFsLiJOb3JtYWxpemUgSW50ZXJ2YWwiKEZsZXhpYmxlU2lnbW9pZG9zY29weS5wZXJmb3JtZWQpIGVuZHMgNSB5ZWFycyBvciBsZXNzIG9uIG9yIGJlZm9yZSBlbmQgb2YgIk1lYXN1cmVtZW50IFBlcmlvZCINCg0KZGVmaW5lICJDVCBDb2xvbm9ncmFwaHkgUGVyZm9ybWVkIjoNCiAgW1Byb2NlZHVyZTogIkNUIENvbG9ub2dyYXBoeSJdIENvbG9ub2dyYXBoeQ0KICAgICAgICAgICAgICAgICAgICAJCXdoZXJlIENvbG9ub2dyYXBoeS5zdGF0dXMgPSAnY29tcGxldGVkJw0KICAgICAgICAgICAgICAgICAgICAJCQlhbmQgR2xvYmFsLiJOb3JtYWxpemUgSW50ZXJ2YWwiKENvbG9ub2dyYXBoeS5wZXJmb3JtZWQpIGVuZHMgNSB5ZWFycyBvciBsZXNzIG9uIG9yIGJlZm9yZSBlbmQgb2YgIk1lYXN1cmVtZW50IFBlcmlvZCINCg0KZGVmaW5lICJUb3RhbCBDb2xlY3RvbXkgUGVyZm9ybWVkIjoNCiAgW1Byb2NlZHVyZTogIlRvdGFsIENvbGVjdG9teSJdIENvbGVjdG9teQ0KICAgICAgICAgICAgICAgICAgICAJCXdoZXJlIENvbGVjdG9teS5zdGF0dXMgPSAnY29tcGxldGVkJw0KICAgICAgICAgICAgICAgICAgICAJCQlhbmQgR2xvYmFsLiJOb3JtYWxpemUgSW50ZXJ2YWwiKENvbGVjdG9teS5wZXJmb3JtZWQpIHN0YXJ0cyBvbiBvciBiZWZvcmUgZW5kIG9mICJNZWFzdXJlbWVudCBQZXJpb2QiDQoNCmRlZmluZSAiRmVjYWwgT2NjdWx0IEJsb29kIFRlc3QgUGVyZm9ybWVkIjoNCiAgW09ic2VydmF0aW9uOiAiRmVjYWwgT2NjdWx0IEJsb29kIFRlc3QgKEZPQlQpIl0gRmVjYWxPY2N1bHRSZXN1bHQNCiAgICAgICAgICAgICAgICAgICAgCQl3aGVyZSBGZWNhbE9jY3VsdFJlc3VsdC5zdGF0dXMgaW4geydmaW5hbCcsICdhbWVuZGVkJywgJ2NvcnJlY3RlZCcsICdwcmVsaW1pbmFyeSd9DQogICAgICAgICAgICAgICAgICAgIAkJCWFuZCBGZWNhbE9jY3VsdFJlc3VsdC52YWx1ZSBpcyBub3QgbnVsbA0KICAgICAgICAgICAgICAgICAgICAJCQlhbmQgR2xvYmFsLiJOb3JtYWxpemUgSW50ZXJ2YWwiKEZlY2FsT2NjdWx0UmVzdWx0LmVmZmVjdGl2ZSkgZHVyaW5nIGRheSBvZiAiTWVhc3VyZW1lbnQgUGVyaW9kIg0KDQpkZWZpbmUgIkZlY2FsIEltbXVub2NoZW1pY2FsIFRlc3QgRE5BIjoNCiAgW09ic2VydmF0aW9uOiAiRklUIEROQSJdIEZpdEROQQ0KICAgICAgICAgICAgICAgICAgICAJCXdoZXJlIEZpdEROQS5zdGF0dXMgaW4geydmaW5hbCcsICdhbWVuZGVkJywgJ2NvcnJlY3RlZCcsICdwcmVsaW1pbmFyeSd9DQogICAgICAgICAgICAgICAgICAgIAkJCWFuZCBGaXRETkEudmFsdWUgaXMgbm90IG51bGwNCiAgICAgICAgICAgICAgICAgICAgCQkJYW5kIEdsb2JhbC4iTm9ybWFsaXplIEludGVydmFsIihGaXRETkEuZWZmZWN0aXZlKSBvY2N1cnMgMyB5ZWFycyBvciBsZXNzIG9uIG9yIGJlZm9yZSBkYXkgb2YgZW5kIG9mICJNZWFzdXJlbWVudCBQZXJpb2QiDQoNCmRlZmluZSAiQ29sb25vc2NvcHkgUGVyZm9ybWVkIjoNCiAgW1Byb2NlZHVyZTogIkNvbG9ub3Njb3B5Il0gQ29sb25vc2NvcHkNCiAgICAgICAgICAgICAgICAgICAgCQl3aGVyZSBDb2xvbm9zY29weS5zdGF0dXMgPSAnY29tcGxldGVkJw0KICAgICAgICAgICAgICAgICAgICAJCSAgYW5kIEdsb2JhbC4iTm9ybWFsaXplIEludGVydmFsIihDb2xvbm9zY29weS5wZXJmb3JtZWQpIGVuZHMgMTAgeWVhcnMgb3IgbGVzcyBvbiBvciBiZWZvcmUgZW5kIG9mICJNZWFzdXJlbWVudCBQZXJpb2QiDQoNCmRlZmluZSAiTnVtZXJhdG9yIjoNCiAgZXhpc3RzICJDb2xvbm9zY29weSBQZXJmb3JtZWQiDQogICAgICAgICAgICAgICAgICAgIAkJb3IgZXhpc3RzICJGZWNhbCBPY2N1bHQgQmxvb2QgVGVzdCBQZXJmb3JtZWQiDQogICAgICAgICAgICAgICAgICAgIAkJb3IgZXhpc3RzICJGbGV4aWJsZSBTaWdtb2lkb3Njb3B5IFBlcmZvcm1lZCINCiAgICAgICAgICAgICAgICAgICAgCQlvciBleGlzdHMgIkZlY2FsIEltbXVub2NoZW1pY2FsIFRlc3QgRE5BIg0KICAgICAgICAgICAgICAgICAgICAJCW9yIGV4aXN0cyAiQ1QgQ29sb25vZ3JhcGh5IFBlcmZvcm1lZCINCg0KZGVmaW5lICJNYWxpZ25hbnQgTmVvcGxhc20iOg0KICBbQ29uZGl0aW9uOiAiTWFsaWduYW50IE5lb3BsYXNtIG9mIENvbG9uIl0gQ29sb3JlY3RhbENhbmNlcg0KICAgICAgICAgICAgICAgICAgICAJCXdoZXJlIEdsb2JhbC4iUHJldmFsZW5jZSBQZXJpb2QiKENvbG9yZWN0YWxDYW5jZXIpIHN0YXJ0cyBvbiBvciBiZWZvcmUgZW5kIG9mICJNZWFzdXJlbWVudCBQZXJpb2QiDQoNCmRlZmluZSAiRGVub21pbmF0b3IgRXhjbHVzaW9uIjoNCiAgSG9zcGljZS4iSGFzIEhvc3BpY2UiDQogICAgICAgICAgICAgICAgICAgIAkJb3IgZXhpc3RzICJNYWxpZ25hbnQgTmVvcGxhc20iDQogICAgICAgICAgICAgICAgICAgIAkJb3IgZXhpc3RzICJUb3RhbCBDb2xlY3RvbXkgUGVyZm9ybWVkIg0KICAgICAgICAgICAgICAgICAgICAJCW9yIEZyYWlsdHkuICJBZHZhbmNlZCBJbGxuZXNzIGFuZCBGcmFpbHR5IEV4Y2x1c2lvbiBOb3QgSW5jbHVkaW5nIE92ZXIgQWdlIDgwIg0KICAgICAgICAgICAgICAgICAgICAJCWFuZCBGcmFpbHR5LiJMb25nIFRlcm0gQ2FyZSBQZXJpb2RzIExvbmdlciBUaGFuIDkwIENvbnNlY3V0aXZlIERheXMiDQo=" + }, + { + "contentType": "application/elm+xml", + "data": "PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0iVVRGLTgiPz4NCjxsaWJyYXJ5IHhtbG5zPSJ1cm46aGw3LW9yZzplbG06cjEiIHhtbG5zOnQ9InVybjpobDctb3JnOmVsbS10eXBlczpyMSIgeG1sbnM6eHNpPSJodHRwOi8vd3d3LnczLm9yZy8yMDAxL1hNTFNjaGVtYS1pbnN0YW5jZSIgeG1sbnM6eHNkPSJodHRwOi8vd3d3LnczLm9yZy8yMDAxL1hNTFNjaGVtYSIgeG1sbnM6Zmhpcj0iaHR0cDovL2hsNy5vcmcvZmhpciIgeG1sbnM6cWRtNDM9InVybjpoZWFsdGhpdC1nb3Y6cWRtOnY0XzMiIHhtbG5zOnFkbTUzPSJ1cm46aGVhbHRoaXQtZ292OnFkbTp2NV8zIiB4bWxuczphPSJ1cm46aGw3LW9yZzpjcWwtYW5ub3RhdGlvbnM6cjEiPg0KICAgPGFubm90YXRpb24gdHJhbnNsYXRvclZlcnNpb249IjEuMyIgdHJhbnNsYXRvck9wdGlvbnM9IkVuYWJsZUFubm90YXRpb25zLEVuYWJsZUxvY2F0b3JzLERpc2FibGVMaXN0RGVtb3Rpb24sRGlzYWJsZUxpc3RQcm9tb3Rpb24iIHhzaTp0eXBlPSJhOkNxbFRvRWxtSW5mbyIvPg0KICAgPGFubm90YXRpb24geHNpOnR5cGU9ImE6QW5ub3RhdGlvbiI+DQogICAgICA8YTpzIHI9IjIyNSI+DQogICAgICAgICA8YTpzPmxpYnJhcnkgQ29sb3JlY3RhbENhbmNlclNjcmVlbmluZ3NGSElSIHZlcnNpb24gJzAuMC4wMDEnPC9hOnM+DQogICAgICA8L2E6cz4NCiAgIDwvYW5ub3RhdGlvbj4NCiAgIDxpZGVudGlmaWVyIGlkPSJDb2xvcmVjdGFsQ2FuY2VyU2NyZWVuaW5nc0ZISVIiIHN5c3RlbT0iaHR0cDovL2VjcWkuaGVhbHRoaXQuZ292L2VjcW1zIiB2ZXJzaW9uPSIwLjAuMDAxIi8+DQogICA8c2NoZW1hSWRlbnRpZmllciBpZD0idXJuOmhsNy1vcmc6ZWxtIiB2ZXJzaW9uPSJyMSIvPg0KICAgPHVzaW5ncz4NCiAgICAgIDxkZWYgbG9jYWxJZGVudGlmaWVyPSJTeXN0ZW0iIHVyaT0idXJuOmhsNy1vcmc6ZWxtLXR5cGVzOnIxIi8+DQogICAgICA8ZGVmIGxvY2FsSWQ9IjEiIGxvY2F0b3I9IjM6MS0zOjI2IiBsb2NhbElkZW50aWZpZXI9IkZISVIiIHVyaT0iaHR0cDovL2hsNy5vcmcvZmhpciIgdmVyc2lvbj0iNC4wLjEiPg0KICAgICAgICAgPGFubm90YXRpb24geHNpOnR5cGU9ImE6QW5ub3RhdGlvbiI+DQogICAgICAgICAgICA8YTpzIHI9IjEiPg0KICAgICAgICAgICAgICAgPGE6cz51c2luZyA8L2E6cz4NCiAgICAgICAgICAgICAgIDxhOnM+DQogICAgICAgICAgICAgICAgICA8YTpzPkZISVI8L2E6cz4NCiAgICAgICAgICAgICAgIDwvYTpzPg0KICAgICAgICAgICAgICAgPGE6cz4gdmVyc2lvbiAnNC4wLjEnPC9hOnM+DQogICAgICAgICAgICA8L2E6cz4NCiAgICAgICAgIDwvYW5ub3RhdGlvbj4NCiAgICAgIDwvZGVmPg0KICAgPC91c2luZ3M+DQogICA8aW5jbHVkZXM+DQogICAgICA8ZGVmIGxvY2FsSWQ9IjIiIGxvY2F0b3I9IjU6MS01OjU2IiBsb2NhbElkZW50aWZpZXI9IkZISVJIZWxwZXJzIiBwYXRoPSJodHRwOi8vZWNxaS5oZWFsdGhpdC5nb3YvZWNxbXMvRkhJUkhlbHBlcnMiIHZlcnNpb249IjQuMC4wMDEiPg0KICAgICAgICAgPGFubm90YXRpb24geHNpOnR5cGU9ImE6QW5ub3RhdGlvbiI+DQogICAgICAgICAgICA8YTpzIHI9IjIiPg0KICAgICAgICAgICAgICAgPGE6cz5pbmNsdWRlIDwvYTpzPg0KICAgICAgICAgICAgICAgPGE6cz4NCiAgICAgICAgICAgICAgICAgIDxhOnM+RkhJUkhlbHBlcnM8L2E6cz4NCiAgICAgICAgICAgICAgIDwvYTpzPg0KICAgICAgICAgICAgICAgPGE6cz4gdmVyc2lvbiAnNC4wLjAwMScgY2FsbGVkIEZISVJIZWxwZXJzPC9hOnM+DQogICAgICAgICAgICA8L2E6cz4NCiAgICAgICAgIDwvYW5ub3RhdGlvbj4NCiAgICAgIDwvZGVmPg0KICAgICAgPGRlZiBsb2NhbElkPSIzIiBsb2NhdG9yPSI2OjEtNjo2NiIgbG9jYWxJZGVudGlmaWVyPSJTREUiIHBhdGg9Imh0dHA6Ly9lY3FpLmhlYWx0aGl0Lmdvdi9lY3Ftcy9TdXBwbGVtZW50YWxEYXRhRWxlbWVudHNGSElSNCIgdmVyc2lvbj0iMi4wLjAwMCI+DQogICAgICAgICA8YW5ub3RhdGlvbiB4c2k6dHlwZT0iYTpBbm5vdGF0aW9uIj4NCiAgICAgICAgICAgIDxhOnMgcj0iMyI+DQogICAgICAgICAgICAgICA8YTpzPmluY2x1ZGUgPC9hOnM+DQogICAgICAgICAgICAgICA8YTpzPg0KICAgICAgICAgICAgICAgICAgPGE6cz5TdXBwbGVtZW50YWxEYXRhRWxlbWVudHNGSElSNDwvYTpzPg0KICAgICAgICAgICAgICAgPC9hOnM+DQogICAgICAgICAgICAgICA8YTpzPiB2ZXJzaW9uICcyLjAuMDAwJyBjYWxsZWQgU0RFPC9hOnM+DQogICAgICAgICAgICA8L2E6cz4NCiAgICAgICAgIDwvYW5ub3RhdGlvbj4NCiAgICAgIDwvZGVmPg0KICAgICAgPGRlZiBsb2NhbElkPSI0IiBsb2NhdG9yPSI3OjEtNzo2OSIgbG9jYWxJZGVudGlmaWVyPSJHbG9iYWwiIHBhdGg9Imh0dHA6Ly9lY3FpLmhlYWx0aGl0Lmdvdi9lY3Ftcy9NQVRHbG9iYWxDb21tb25GdW5jdGlvbnNGSElSNCIgdmVyc2lvbj0iNi4wLjAwMCI+DQogICAgICAgICA8YW5ub3RhdGlvbiB4c2k6dHlwZT0iYTpBbm5vdGF0aW9uIj4NCiAgICAgICAgICAgIDxhOnMgcj0iNCI+DQogICAgICAgICAgICAgICA8YTpzPmluY2x1ZGUgPC9hOnM+DQogICAgICAgICAgICAgICA8YTpzPg0KICAgICAgICAgICAgICAgICAgPGE6cz5NQVRHbG9iYWxDb21tb25GdW5jdGlvbnNGSElSNDwvYTpzPg0KICAgICAgICAgICAgICAgPC9hOnM+DQogICAgICAgICAgICAgICA8YTpzPiB2ZXJzaW9uICc2LjAuMDAwJyBjYWxsZWQgR2xvYmFsPC9hOnM+DQogICAgICAgICAgICA8L2E6cz4NCiAgICAgICAgIDwvYW5ub3RhdGlvbj4NCiAgICAgIDwvZGVmPg0KICAgICAgPGRlZiBsb2NhbElkPSI1IiBsb2NhdG9yPSI4OjEtODo4OSIgbG9jYWxJZGVudGlmaWVyPSJBZHVsdE91dHBhdGllbnRFbmNvdW50ZXJzIiBwYXRoPSJodHRwOi8vZWNxaS5oZWFsdGhpdC5nb3YvZWNxbXMvQWR1bHRPdXRwYXRpZW50RW5jb3VudGVyc0ZISVI0IiB2ZXJzaW9uPSIyLjAuMDAwIj4NCiAgICAgICAgIDxhbm5vdGF0aW9uIHhzaTp0eXBlPSJhOkFubm90YXRpb24iPg0KICAgICAgICAgICAgPGE6cyByPSI1Ij4NCiAgICAgICAgICAgICAgIDxhOnM+aW5jbHVkZSA8L2E6cz4NCiAgICAgICAgICAgICAgIDxhOnM+DQogICAgICAgICAgICAgICAgICA8YTpzPkFkdWx0T3V0cGF0aWVudEVuY291bnRlcnNGSElSNDwvYTpzPg0KICAgICAgICAgICAgICAgPC9hOnM+DQogICAgICAgICAgICAgICA8YTpzPiB2ZXJzaW9uICcyLjAuMDAwJyBjYWxsZWQgQWR1bHRPdXRwYXRpZW50RW5jb3VudGVyczwvYTpzPg0KICAgICAgICAgICAgPC9hOnM+DQogICAgICAgICA8L2Fubm90YXRpb24+DQogICAgICA8L2RlZj4NCiAgICAgIDxkZWYgbG9jYWxJZD0iNiIgbG9jYXRvcj0iOToxLTk6NTMiIGxvY2FsSWRlbnRpZmllcj0iSG9zcGljZSIgcGF0aD0iaHR0cDovL2VjcWkuaGVhbHRoaXQuZ292L2VjcW1zL0hvc3BpY2VGSElSNCIgdmVyc2lvbj0iMi4wLjAwMCI+DQogICAgICAgICA8YW5ub3RhdGlvbiB4c2k6dHlwZT0iYTpBbm5vdGF0aW9uIj4NCiAgICAgICAgICAgIDxhOnMgcj0iNiI+DQogICAgICAgICAgICAgICA8YTpzPmluY2x1ZGUgPC9hOnM+DQogICAgICAgICAgICAgICA8YTpzPg0KICAgICAgICAgICAgICAgICAgPGE6cz5Ib3NwaWNlRkhJUjQ8L2E6cz4NCiAgICAgICAgICAgICAgIDwvYTpzPg0KICAgICAgICAgICAgICAgPGE6cz4gdmVyc2lvbiAnMi4wLjAwMCcgY2FsbGVkIEhvc3BpY2U8L2E6cz4NCiAgICAgICAgICAgIDwvYTpzPg0KICAgICAgICAgPC9hbm5vdGF0aW9uPg0KICAgICAgPC9kZWY+DQogICAgICA8ZGVmIGxvY2FsSWQ9IjciIGxvY2F0b3I9IjEwOjEtMTA6ODUiIGxvY2FsSWRlbnRpZmllcj0iRnJhaWx0eSIgcGF0aD0iaHR0cDovL2VjcWkuaGVhbHRoaXQuZ292L2VjcW1zL0FkdmFuY2VkSWxsbmVzc2FuZEZyYWlsdHlFeGNsdXNpb25FQ1FNRkhJUjQiIHZlcnNpb249IjUuMTIuMDAwIj4NCiAgICAgICAgIDxhbm5vdGF0aW9uIHhzaTp0eXBlPSJhOkFubm90YXRpb24iPg0KICAgICAgICAgICAgPGE6cyByPSI3Ij4NCiAgICAgICAgICAgICAgIDxhOnM+aW5jbHVkZSA8L2E6cz4NCiAgICAgICAgICAgICAgIDxhOnM+DQogICAgICAgICAgICAgICAgICA8YTpzPkFkdmFuY2VkSWxsbmVzc2FuZEZyYWlsdHlFeGNsdXNpb25FQ1FNRkhJUjQ8L2E6cz4NCiAgICAgICAgICAgICAgIDwvYTpzPg0KICAgICAgICAgICAgICAgPGE6cz4gdmVyc2lvbiAnNS4xMi4wMDAnIGNhbGxlZCBGcmFpbHR5PC9hOnM+DQogICAgICAgICAgICA8L2E6cz4NCiAgICAgICAgIDwvYW5ub3RhdGlvbj4NCiAgICAgIDwvZGVmPg0KICAgPC9pbmNsdWRlcz4NCiAgIDxwYXJhbWV0ZXJzPg0KICAgICAgPGRlZiBsb2NhbElkPSI0NSIgbG9jYXRvcj0iNDY6MS00Nzo2NiIgbmFtZT0iTWVhc3VyZW1lbnQgUGVyaW9kIiBhY2Nlc3NMZXZlbD0iUHVibGljIj4NCiAgICAgICAgIDxhbm5vdGF0aW9uIHhzaTp0eXBlPSJhOkFubm90YXRpb24iPg0KICAgICAgICAgICAgPGE6cyByPSI0NSI+DQogICAgICAgICAgICAgICA8YTpzPnBhcmFtZXRlciAmcXVvdDtNZWFzdXJlbWVudCBQZXJpb2QmcXVvdDsgPC9hOnM+DQogICAgICAgICAgICAgICA8YTpzIHI9IjQ0Ij4NCiAgICAgICAgICAgICAgICAgIDxhOnM+SW50ZXJ2YWwmbHQ7PC9hOnM+DQogICAgICAgICAgICAgICAgICA8YTpzIHI9IjQzIj4NCiAgICAgICAgICAgICAgICAgICAgIDxhOnM+RGF0ZVRpbWU8L2E6cz4NCiAgICAgICAgICAgICAgICAgIDwvYTpzPg0KICAgICAgICAgICAgICAgICAgPGE6cz4+PC9hOnM+DQogICAgICAgICAgICAgICA8L2E6cz4NCiAgICAgICAgICAgICAgIDxhOnM+CiAgZGVmYXVsdCA8L2E6cz4NCiAgICAgICAgICAgICAgIDxhOnMgcj0iNDIiPg0KICAgICAgICAgICAgICAgICAgPGE6cyByPSI0MCI+SW50ZXJ2YWxbQDIwMTktMDEtMDFUMDA6MDA6MDAuMCwgQDIwMjAtMDEtMDFUMDA6MDA6MDAuMCk8L2E6cz4NCiAgICAgICAgICAgICAgIDwvYTpzPg0KICAgICAgICAgICAgPC9hOnM+DQogICAgICAgICA8L2Fubm90YXRpb24+DQogICAgICAgICA8ZGVmYXVsdCBsb2NhbElkPSI0MiIgbG9jYXRvcj0iNDc6MTEtNDc6NjYiIGxvd0Nsb3NlZD0idHJ1ZSIgaGlnaENsb3NlZD0iZmFsc2UiIHhzaTp0eXBlPSJJbnRlcnZhbCI+DQogICAgICAgICAgICA8bG93IGxvY2FsSWQ9IjQwIiBsb2NhdG9yPSI0NzoyMC00Nzo0MSIgeHNpOnR5cGU9IkRhdGVUaW1lIj4NCiAgICAgICAgICAgICAgIDx5ZWFyIHZhbHVlVHlwZT0idDpJbnRlZ2VyIiB2YWx1ZT0iMjAxOSIgeHNpOnR5cGU9IkxpdGVyYWwiLz4NCiAgICAgICAgICAgICAgIDxtb250aCB2YWx1ZVR5cGU9InQ6SW50ZWdlciIgdmFsdWU9IjEiIHhzaTp0eXBlPSJMaXRlcmFsIi8+DQogICAgICAgICAgICAgICA8ZGF5IHZhbHVlVHlwZT0idDpJbnRlZ2VyIiB2YWx1ZT0iMSIgeHNpOnR5cGU9IkxpdGVyYWwiLz4NCiAgICAgICAgICAgICAgIDxob3VyIHZhbHVlVHlwZT0idDpJbnRlZ2VyIiB2YWx1ZT0iMCIgeHNpOnR5cGU9IkxpdGVyYWwiLz4NCiAgICAgICAgICAgICAgIDxtaW51dGUgdmFsdWVUeXBlPSJ0OkludGVnZXIiIHZhbHVlPSIwIiB4c2k6dHlwZT0iTGl0ZXJhbCIvPg0KICAgICAgICAgICAgICAgPHNlY29uZCB2YWx1ZVR5cGU9InQ6SW50ZWdlciIgdmFsdWU9IjAiIHhzaTp0eXBlPSJMaXRlcmFsIi8+DQogICAgICAgICAgICAgICA8bWlsbGlzZWNvbmQgdmFsdWVUeXBlPSJ0OkludGVnZXIiIHZhbHVlPSIwIiB4c2k6dHlwZT0iTGl0ZXJhbCIvPg0KICAgICAgICAgICAgPC9sb3c+DQogICAgICAgICAgICA8aGlnaCBsb2NhbElkPSI0MSIgbG9jYXRvcj0iNDc6NDQtNDc6NjUiIHhzaTp0eXBlPSJEYXRlVGltZSI+DQogICAgICAgICAgICAgICA8eWVhciB2YWx1ZVR5cGU9InQ6SW50ZWdlciIgdmFsdWU9IjIwMjAiIHhzaTp0eXBlPSJMaXRlcmFsIi8+DQogICAgICAgICAgICAgICA8bW9udGggdmFsdWVUeXBlPSJ0OkludGVnZXIiIHZhbHVlPSIxIiB4c2k6dHlwZT0iTGl0ZXJhbCIvPg0KICAgICAgICAgICAgICAgPGRheSB2YWx1ZVR5cGU9InQ6SW50ZWdlciIgdmFsdWU9IjEiIHhzaTp0eXBlPSJMaXRlcmFsIi8+DQogICAgICAgICAgICAgICA8aG91ciB2YWx1ZVR5cGU9InQ6SW50ZWdlciIgdmFsdWU9IjAiIHhzaTp0eXBlPSJMaXRlcmFsIi8+DQogICAgICAgICAgICAgICA8bWludXRlIHZhbHVlVHlwZT0idDpJbnRlZ2VyIiB2YWx1ZT0iMCIgeHNpOnR5cGU9IkxpdGVyYWwiLz4NCiAgICAgICAgICAgICAgIDxzZWNvbmQgdmFsdWVUeXBlPSJ0OkludGVnZXIiIHZhbHVlPSIwIiB4c2k6dHlwZT0iTGl0ZXJhbCIvPg0KICAgICAgICAgICAgICAgPG1pbGxpc2Vjb25kIHZhbHVlVHlwZT0idDpJbnRlZ2VyIiB2YWx1ZT0iMCIgeHNpOnR5cGU9IkxpdGVyYWwiLz4NCiAgICAgICAgICAgIDwvaGlnaD4NCiAgICAgICAgIDwvZGVmYXVsdD4NCiAgICAgICAgIDxwYXJhbWV0ZXJUeXBlU3BlY2lmaWVyIGxvY2FsSWQ9IjQ0IiBsb2NhdG9yPSI0NjozMi00Njo0OSIgeHNpOnR5cGU9IkludGVydmFsVHlwZVNwZWNpZmllciI+DQogICAgICAgICAgICA8cG9pbnRUeXBlIGxvY2FsSWQ9IjQzIiBsb2NhdG9yPSI0Njo0MS00Njo0OCIgbmFtZT0idDpEYXRlVGltZSIgeHNpOnR5cGU9Ik5hbWVkVHlwZVNwZWNpZmllciIvPg0KICAgICAgICAgPC9wYXJhbWV0ZXJUeXBlU3BlY2lmaWVyPg0KICAgICAgPC9kZWY+DQogICA8L3BhcmFtZXRlcnM+DQogICA8Y29kZVN5c3RlbXM+DQogICAgICA8ZGVmIGxvY2FsSWQ9IjgiIGxvY2F0b3I9IjEyOjEtMTI6MzgiIG5hbWU9IkxPSU5DIiBpZD0iaHR0cDovL2xvaW5jLm9yZyIgYWNjZXNzTGV2ZWw9IlB1YmxpYyI+DQogICAgICAgICA8YW5ub3RhdGlvbiB4c2k6dHlwZT0iYTpBbm5vdGF0aW9uIj4NCiAgICAgICAgICAgIDxhOnMgcj0iOCI+DQogICAgICAgICAgICAgICA8YTpzPmNvZGVzeXN0ZW0gJnF1b3Q7TE9JTkMmcXVvdDs6ICdodHRwOi8vbG9pbmMub3JnJzwvYTpzPg0KICAgICAgICAgICAgPC9hOnM+DQogICAgICAgICA8L2Fubm90YXRpb24+DQogICAgICA8L2RlZj4NCiAgIDwvY29kZVN5c3RlbXM+DQogICA8dmFsdWVTZXRzPg0KICAgICAgPGRlZiBsb2NhbElkPSI5IiBsb2NhdG9yPSIxNDoxLTE0OjEwNyIgbmFtZT0iQWN1dGUgSW5wYXRpZW50IiBpZD0iaHR0cDovL2N0cy5ubG0ubmloLmdvdi9maGlyL1ZhbHVlU2V0LzIuMTYuODQwLjEuMTEzODgzLjMuNDY0LjEwMDMuMTAxLjEyLjEwODMiIGFjY2Vzc0xldmVsPSJQdWJsaWMiPg0KICAgICAgICAgPGFubm90YXRpb24geHNpOnR5cGU9ImE6QW5ub3RhdGlvbiI+DQogICAgICAgICAgICA8YTpzIHI9IjkiPg0KICAgICAgICAgICAgICAgPGE6cz52YWx1ZXNldCAmcXVvdDtBY3V0ZSBJbnBhdGllbnQmcXVvdDs6ICdodHRwOi8vY3RzLm5sbS5uaWguZ292L2ZoaXIvVmFsdWVTZXQvMi4xNi44NDAuMS4xMTM4ODMuMy40NjQuMTAwMy4xMDEuMTIuMTA4Myc8L2E6cz4NCiAgICAgICAgICAgIDwvYTpzPg0KICAgICAgICAgPC9hbm5vdGF0aW9uPg0KICAgICAgPC9kZWY+DQogICAgICA8ZGVmIGxvY2FsSWQ9IjEwIiBsb2NhdG9yPSIxNToxLTE1OjEwOCIgbmFtZT0iQWR2YW5jZWQgSWxsbmVzcyIgaWQ9Imh0dHA6Ly9jdHMubmxtLm5paC5nb3YvZmhpci9WYWx1ZVNldC8yLjE2Ljg0MC4xLjExMzg4My4zLjQ2NC4xMDAzLjExMC4xMi4xMDgyIiBhY2Nlc3NMZXZlbD0iUHVibGljIj4NCiAgICAgICAgIDxhbm5vdGF0aW9uIHhzaTp0eXBlPSJhOkFubm90YXRpb24iPg0KICAgICAgICAgICAgPGE6cyByPSIxMCI+DQogICAgICAgICAgICAgICA8YTpzPnZhbHVlc2V0ICZxdW90O0FkdmFuY2VkIElsbG5lc3MmcXVvdDs6ICdodHRwOi8vY3RzLm5sbS5uaWguZ292L2ZoaXIvVmFsdWVTZXQvMi4xNi44NDAuMS4xMTM4ODMuMy40NjQuMTAwMy4xMTAuMTIuMTA4Mic8L2E6cz4NCiAgICAgICAgICAgIDwvYTpzPg0KICAgICAgICAgPC9hbm5vdGF0aW9uPg0KICAgICAgPC9kZWY+DQogICAgICA8ZGVmIGxvY2FsSWQ9IjExIiBsb2NhdG9yPSIxNjoxLTE2OjEwMyIgbmFtZT0iQW5udWFsIFdlbGxuZXNzIFZpc2l0IiBpZD0iaHR0cDovL2N0cy5ubG0ubmloLmdvdi9maGlyL1ZhbHVlU2V0LzIuMTYuODQwLjEuMTEzODgzLjMuNTI2LjMuMTI0MCIgYWNjZXNzTGV2ZWw9IlB1YmxpYyI+DQogICAgICAgICA8YW5ub3RhdGlvbiB4c2k6dHlwZT0iYTpBbm5vdGF0aW9uIj4NCiAgICAgICAgICAgIDxhOnMgcj0iMTEiPg0KICAgICAgICAgICAgICAgPGE6cz52YWx1ZXNldCAmcXVvdDtBbm51YWwgV2VsbG5lc3MgVmlzaXQmcXVvdDs6ICdodHRwOi8vY3RzLm5sbS5uaWguZ292L2ZoaXIvVmFsdWVTZXQvMi4xNi44NDAuMS4xMTM4ODMuMy41MjYuMy4xMjQwJzwvYTpzPg0KICAgICAgICAgICAgPC9hOnM+DQogICAgICAgICA8L2Fubm90YXRpb24+DQogICAgICA8L2RlZj4NCiAgICAgIDxkZWYgbG9jYWxJZD0iMTIiIGxvY2F0b3I9IjE3OjEtMTc6MTM5IiBuYW1lPSJDYXJlIFNlcnZpY2VzIGluIExvbmctVGVybSBSZXNpZGVudGlhbCBGYWNpbGl0eSIgaWQ9Imh0dHA6Ly9jdHMubmxtLm5paC5nb3YvZmhpci9WYWx1ZVNldC8yLjE2Ljg0MC4xLjExMzg4My4zLjQ2NC4xMDAzLjEwMS4xMi4xMDE0IiBhY2Nlc3NMZXZlbD0iUHVibGljIj4NCiAgICAgICAgIDxhbm5vdGF0aW9uIHhzaTp0eXBlPSJhOkFubm90YXRpb24iPg0KICAgICAgICAgICAgPGE6cyByPSIxMiI+DQogICAgICAgICAgICAgICA8YTpzPnZhbHVlc2V0ICZxdW90O0NhcmUgU2VydmljZXMgaW4gTG9uZy1UZXJtIFJlc2lkZW50aWFsIEZhY2lsaXR5JnF1b3Q7OiAnaHR0cDovL2N0cy5ubG0ubmloLmdvdi9maGlyL1ZhbHVlU2V0LzIuMTYuODQwLjEuMTEzODgzLjMuNDY0LjEwMDMuMTAxLjEyLjEwMTQnPC9hOnM+DQogICAgICAgICAgICA8L2E6cz4NCiAgICAgICAgIDwvYW5ub3RhdGlvbj4NCiAgICAgIDwvZGVmPg0KICAgICAgPGRlZiBsb2NhbElkPSIxMyIgbG9jYXRvcj0iMTg6MS0xODoxMDMiIG5hbWU9IkNvbG9ub3Njb3B5IiBpZD0iaHR0cDovL2N0cy5ubG0ubmloLmdvdi9maGlyL1ZhbHVlU2V0LzIuMTYuODQwLjEuMTEzODgzLjMuNDY0LjEwMDMuMTA4LjEyLjEwMjAiIGFjY2Vzc0xldmVsPSJQdWJsaWMiPg0KICAgICAgICAgPGFubm90YXRpb24geHNpOnR5cGU9ImE6QW5ub3RhdGlvbiI+DQogICAgICAgICAgICA8YTpzIHI9IjEzIj4NCiAgICAgICAgICAgICAgIDxhOnM+dmFsdWVzZXQgJnF1b3Q7Q29sb25vc2NvcHkmcXVvdDs6ICdodHRwOi8vY3RzLm5sbS5uaWguZ292L2ZoaXIvVmFsdWVTZXQvMi4xNi44NDAuMS4xMTM4ODMuMy40NjQuMTAwMy4xMDguMTIuMTAyMCc8L2E6cz4NCiAgICAgICAgICAgIDwvYTpzPg0KICAgICAgICAgPC9hbm5vdGF0aW9uPg0KICAgICAgPC9kZWY+DQogICAgICA8ZGVmIGxvY2FsSWQ9IjE0IiBsb2NhdG9yPSIxOToxLTE5OjEwNyIgbmFtZT0iQ1QgQ29sb25vZ3JhcGh5IiBpZD0iaHR0cDovL2N0cy5ubG0ubmloLmdvdi9maGlyL1ZhbHVlU2V0LzIuMTYuODQwLjEuMTEzODgzLjMuNDY0LjEwMDMuMTA4LjEyLjEwMzgiIGFjY2Vzc0xldmVsPSJQdWJsaWMiPg0KICAgICAgICAgPGFubm90YXRpb24geHNpOnR5cGU9ImE6QW5ub3RhdGlvbiI+DQogICAgICAgICAgICA8YTpzIHI9IjE0Ij4NCiAgICAgICAgICAgICAgIDxhOnM+dmFsdWVzZXQgJnF1b3Q7Q1QgQ29sb25vZ3JhcGh5JnF1b3Q7OiAnaHR0cDovL2N0cy5ubG0ubmloLmdvdi9maGlyL1ZhbHVlU2V0LzIuMTYuODQwLjEuMTEzODgzLjMuNDY0LjEwMDMuMTA4LjEyLjEwMzgnPC9hOnM+DQogICAgICAgICAgICA8L2E6cz4NCiAgICAgICAgIDwvYW5ub3RhdGlvbj4NCiAgICAgIDwvZGVmPg0KICAgICAgPGRlZiBsb2NhbElkPSIxNSIgbG9jYXRvcj0iMjA6MS0yMDoxMTIiIG5hbWU9IkRlbWVudGlhIE1lZGljYXRpb25zIiBpZD0iaHR0cDovL2N0cy5ubG0ubmloLmdvdi9maGlyL1ZhbHVlU2V0LzIuMTYuODQwLjEuMTEzODgzLjMuNDY0LjEwMDMuMTk2LjEyLjE1MTAiIGFjY2Vzc0xldmVsPSJQdWJsaWMiPg0KICAgICAgICAgPGFubm90YXRpb24geHNpOnR5cGU9ImE6QW5ub3RhdGlvbiI+DQogICAgICAgICAgICA8YTpzIHI9IjE1Ij4NCiAgICAgICAgICAgICAgIDxhOnM+dmFsdWVzZXQgJnF1b3Q7RGVtZW50aWEgTWVkaWNhdGlvbnMmcXVvdDs6ICdodHRwOi8vY3RzLm5sbS5uaWguZ292L2ZoaXIvVmFsdWVTZXQvMi4xNi44NDAuMS4xMTM4ODMuMy40NjQuMTAwMy4xOTYuMTIuMTUxMCc8L2E6cz4NCiAgICAgICAgICAgIDwvYTpzPg0KICAgICAgICAgPC9hbm5vdGF0aW9uPg0KICAgICAgPC9kZWY+DQogICAgICA8ZGVmIGxvY2FsSWQ9IjE2IiBsb2NhdG9yPSIyMToxLTIxOjEzNiIgbmFtZT0iRGlzY2hhcmdlZCB0byBIZWFsdGggQ2FyZSBGYWNpbGl0eSBmb3IgSG9zcGljZSBDYXJlIiBpZD0iaHR0cDovL2N0cy5ubG0ubmloLmdvdi9maGlyL1ZhbHVlU2V0LzIuMTYuODQwLjEuMTEzODgzLjMuMTE3LjEuNy4xLjIwNyIgYWNjZXNzTGV2ZWw9IlB1YmxpYyI+DQogICAgICAgICA8YW5ub3RhdGlvbiB4c2k6dHlwZT0iYTpBbm5vdGF0aW9uIj4NCiAgICAgICAgICAgIDxhOnMgcj0iMTYiPg0KICAgICAgICAgICAgICAgPGE6cz52YWx1ZXNldCAmcXVvdDtEaXNjaGFyZ2VkIHRvIEhlYWx0aCBDYXJlIEZhY2lsaXR5IGZvciBIb3NwaWNlIENhcmUmcXVvdDs6ICdodHRwOi8vY3RzLm5sbS5uaWguZ292L2ZoaXIvVmFsdWVTZXQvMi4xNi44NDAuMS4xMTM4ODMuMy4xMTcuMS43LjEuMjA3JzwvYTpzPg0KICAgICAgICAgICAgPC9hOnM+DQogICAgICAgICA8L2Fubm90YXRpb24+DQogICAgICA8L2RlZj4NCiAgICAgIDxkZWYgbG9jYWxJZD0iMTciIGxvY2F0b3I9IjIyOjEtMjI6MTIwIiBuYW1lPSJEaXNjaGFyZ2VkIHRvIEhvbWUgZm9yIEhvc3BpY2UgQ2FyZSIgaWQ9Imh0dHA6Ly9jdHMubmxtLm5paC5nb3YvZmhpci9WYWx1ZVNldC8yLjE2Ljg0MC4xLjExMzg4My4zLjExNy4xLjcuMS4yMDkiIGFjY2Vzc0xldmVsPSJQdWJsaWMiPg0KICAgICAgICAgPGFubm90YXRpb24geHNpOnR5cGU9ImE6QW5ub3RhdGlvbiI+DQogICAgICAgICAgICA8YTpzIHI9IjE3Ij4NCiAgICAgICAgICAgICAgIDxhOnM+dmFsdWVzZXQgJnF1b3Q7RGlzY2hhcmdlZCB0byBIb21lIGZvciBIb3NwaWNlIENhcmUmcXVvdDs6ICdodHRwOi8vY3RzLm5sbS5uaWguZ292L2ZoaXIvVmFsdWVTZXQvMi4xNi44NDAuMS4xMTM4ODMuMy4xMTcuMS43LjEuMjA5JzwvYTpzPg0KICAgICAgICAgICAgPC9hOnM+DQogICAgICAgICA8L2Fubm90YXRpb24+DQogICAgICA8L2RlZj4NCiAgICAgIDxkZWYgbG9jYWxJZD0iMTgiIGxvY2F0b3I9IjIzOjEtMjM6OTQiIG5hbWU9IkVEIiBpZD0iaHR0cDovL2N0cy5ubG0ubmloLmdvdi9maGlyL1ZhbHVlU2V0LzIuMTYuODQwLjEuMTEzODgzLjMuNDY0LjEwMDMuMTAxLjEyLjEwODUiIGFjY2Vzc0xldmVsPSJQdWJsaWMiPg0KICAgICAgICAgPGFubm90YXRpb24geHNpOnR5cGU9ImE6QW5ub3RhdGlvbiI+DQogICAgICAgICAgICA8YTpzIHI9IjE4Ij4NCiAgICAgICAgICAgICAgIDxhOnM+dmFsdWVzZXQgJnF1b3Q7RUQmcXVvdDs6ICdodHRwOi8vY3RzLm5sbS5uaWguZ292L2ZoaXIvVmFsdWVTZXQvMi4xNi44NDAuMS4xMTM4ODMuMy40NjQuMTAwMy4xMDEuMTIuMTA4NSc8L2E6cz4NCiAgICAgICAgICAgIDwvYTpzPg0KICAgICAgICAgPC9hbm5vdGF0aW9uPg0KICAgICAgPC9kZWY+DQogICAgICA8ZGVmIGxvY2FsSWQ9IjE5IiBsb2NhdG9yPSIyNDoxLTI0OjEwMCIgbmFtZT0iRW5jb3VudGVyIElucGF0aWVudCIgaWQ9Imh0dHA6Ly9jdHMubmxtLm5paC5nb3YvZmhpci9WYWx1ZVNldC8yLjE2Ljg0MC4xLjExMzg4My4zLjY2Ni41LjMwNyIgYWNjZXNzTGV2ZWw9IlB1YmxpYyI+DQogICAgICAgICA8YW5ub3RhdGlvbiB4c2k6dHlwZT0iYTpBbm5vdGF0aW9uIj4NCiAgICAgICAgICAgIDxhOnMgcj0iMTkiPg0KICAgICAgICAgICAgICAgPGE6cz52YWx1ZXNldCAmcXVvdDtFbmNvdW50ZXIgSW5wYXRpZW50JnF1b3Q7OiAnaHR0cDovL2N0cy5ubG0ubmloLmdvdi9maGlyL1ZhbHVlU2V0LzIuMTYuODQwLjEuMTEzODgzLjMuNjY2LjUuMzA3JzwvYTpzPg0KICAgICAgICAgICAgPC9hOnM+DQogICAgICAgICA8L2Fubm90YXRpb24+DQogICAgICA8L2RlZj4NCiAgICAgIDxkZWYgbG9jYWxJZD0iMjAiIGxvY2F0b3I9IjI1OjEtMjU6MTIyIiBuYW1lPSJGZWNhbCBPY2N1bHQgQmxvb2QgVGVzdCAoRk9CVCkiIGlkPSJodHRwOi8vY3RzLm5sbS5uaWguZ292L2ZoaXIvVmFsdWVTZXQvMi4xNi44NDAuMS4xMTM4ODMuMy40NjQuMTAwMy4xOTguMTIuMTAxMSIgYWNjZXNzTGV2ZWw9IlB1YmxpYyI+DQogICAgICAgICA8YW5ub3RhdGlvbiB4c2k6dHlwZT0iYTpBbm5vdGF0aW9uIj4NCiAgICAgICAgICAgIDxhOnMgcj0iMjAiPg0KICAgICAgICAgICAgICAgPGE6cz52YWx1ZXNldCAmcXVvdDtGZWNhbCBPY2N1bHQgQmxvb2QgVGVzdCAoRk9CVCkmcXVvdDs6ICdodHRwOi8vY3RzLm5sbS5uaWguZ292L2ZoaXIvVmFsdWVTZXQvMi4xNi44NDAuMS4xMTM4ODMuMy40NjQuMTAwMy4xOTguMTIuMTAxMSc8L2E6cz4NCiAgICAgICAgICAgIDwvYTpzPg0KICAgICAgICAgPC9hbm5vdGF0aW9uPg0KICAgICAgPC9kZWY+DQogICAgICA8ZGVmIGxvY2FsSWQ9IjIxIiBsb2NhdG9yPSIyNjoxLTI2Ojk5IiBuYW1lPSJGSVQgRE5BIiBpZD0iaHR0cDovL2N0cy5ubG0ubmloLmdvdi9maGlyL1ZhbHVlU2V0LzIuMTYuODQwLjEuMTEzODgzLjMuNDY0LjEwMDMuMTA4LjEyLjEwMzkiIGFjY2Vzc0xldmVsPSJQdWJsaWMiPg0KICAgICAgICAgPGFubm90YXRpb24geHNpOnR5cGU9ImE6QW5ub3RhdGlvbiI+DQogICAgICAgICAgICA8YTpzIHI9IjIxIj4NCiAgICAgICAgICAgICAgIDxhOnM+dmFsdWVzZXQgJnF1b3Q7RklUIEROQSZxdW90OzogJ2h0dHA6Ly9jdHMubmxtLm5paC5nb3YvZmhpci9WYWx1ZVNldC8yLjE2Ljg0MC4xLjExMzg4My4zLjQ2NC4xMDAzLjEwOC4xMi4xMDM5JzwvYTpzPg0KICAgICAgICAgICAgPC9hOnM+DQogICAgICAgICA8L2Fubm90YXRpb24+DQogICAgICA8L2RlZj4NCiAgICAgIDxkZWYgbG9jYWxJZD0iMjIiIGxvY2F0b3I9IjI3OjEtMjc6MTE0IiBuYW1lPSJGbGV4aWJsZSBTaWdtb2lkb3Njb3B5IiBpZD0iaHR0cDovL2N0cy5ubG0ubmloLmdvdi9maGlyL1ZhbHVlU2V0LzIuMTYuODQwLjEuMTEzODgzLjMuNDY0LjEwMDMuMTk4LjEyLjEwMTAiIGFjY2Vzc0xldmVsPSJQdWJsaWMiPg0KICAgICAgICAgPGFubm90YXRpb24geHNpOnR5cGU9ImE6QW5ub3RhdGlvbiI+DQogICAgICAgICAgICA8YTpzIHI9IjIyIj4NCiAgICAgICAgICAgICAgIDxhOnM+dmFsdWVzZXQgJnF1b3Q7RmxleGlibGUgU2lnbW9pZG9zY29weSZxdW90OzogJ2h0dHA6Ly9jdHMubmxtLm5paC5nb3YvZmhpci9WYWx1ZVNldC8yLjE2Ljg0MC4xLjExMzg4My4zLjQ2NC4xMDAzLjE5OC4xMi4xMDEwJzwvYTpzPg0KICAgICAgICAgICAgPC9hOnM+DQogICAgICAgICA8L2Fubm90YXRpb24+DQogICAgICA8L2RlZj4NCiAgICAgIDxkZWYgbG9jYWxJZD0iMjMiIGxvY2F0b3I9IjI4OjEtMjg6MTA2IiBuYW1lPSJGcmFpbHR5IERldmljZSIgaWQ9Imh0dHA6Ly9jdHMubmxtLm5paC5nb3YvZmhpci9WYWx1ZVNldC8yLjE2Ljg0MC4xLjExMzg4My4zLjQ2NC4xMDAzLjExOC4xMi4xMzAwIiBhY2Nlc3NMZXZlbD0iUHVibGljIj4NCiAgICAgICAgIDxhbm5vdGF0aW9uIHhzaTp0eXBlPSJhOkFubm90YXRpb24iPg0KICAgICAgICAgICAgPGE6cyByPSIyMyI+DQogICAgICAgICAgICAgICA8YTpzPnZhbHVlc2V0ICZxdW90O0ZyYWlsdHkgRGV2aWNlJnF1b3Q7OiAnaHR0cDovL2N0cy5ubG0ubmloLmdvdi9maGlyL1ZhbHVlU2V0LzIuMTYuODQwLjEuMTEzODgzLjMuNDY0LjEwMDMuMTE4LjEyLjEzMDAnPC9hOnM+DQogICAgICAgICAgICA8L2E6cz4NCiAgICAgICAgIDwvYW5ub3RhdGlvbj4NCiAgICAgIDwvZGVmPg0KICAgICAgPGRlZiBsb2NhbElkPSIyNCIgbG9jYXRvcj0iMjk6MS0yOToxMDkiIG5hbWU9IkZyYWlsdHkgRGlhZ25vc2lzIiBpZD0iaHR0cDovL2N0cy5ubG0ubmloLmdvdi9maGlyL1ZhbHVlU2V0LzIuMTYuODQwLjEuMTEzODgzLjMuNDY0LjEwMDMuMTEzLjEyLjEwNzQiIGFjY2Vzc0xldmVsPSJQdWJsaWMiPg0KICAgICAgICAgPGFubm90YXRpb24geHNpOnR5cGU9ImE6QW5ub3RhdGlvbiI+DQogICAgICAgICAgICA8YTpzIHI9IjI0Ij4NCiAgICAgICAgICAgICAgIDxhOnM+dmFsdWVzZXQgJnF1b3Q7RnJhaWx0eSBEaWFnbm9zaXMmcXVvdDs6ICdodHRwOi8vY3RzLm5sbS5uaWguZ292L2ZoaXIvVmFsdWVTZXQvMi4xNi44NDAuMS4xMTM4ODMuMy40NjQuMTAwMy4xMTMuMTIuMTA3NCc8L2E6cz4NCiAgICAgICAgICAgIDwvYTpzPg0KICAgICAgICAgPC9hbm5vdGF0aW9uPg0KICAgICAgPC9kZWY+DQogICAgICA8ZGVmIGxvY2FsSWQ9IjI1IiBsb2NhdG9yPSIzMDoxLTMwOjEwOSIgbmFtZT0iRnJhaWx0eSBFbmNvdW50ZXIiIGlkPSJodHRwOi8vY3RzLm5sbS5uaWguZ292L2ZoaXIvVmFsdWVTZXQvMi4xNi44NDAuMS4xMTM4ODMuMy40NjQuMTAwMy4xMDEuMTIuMTA4OCIgYWNjZXNzTGV2ZWw9IlB1YmxpYyI+DQogICAgICAgICA8YW5ub3RhdGlvbiB4c2k6dHlwZT0iYTpBbm5vdGF0aW9uIj4NCiAgICAgICAgICAgIDxhOnMgcj0iMjUiPg0KICAgICAgICAgICAgICAgPGE6cz52YWx1ZXNldCAmcXVvdDtGcmFpbHR5IEVuY291bnRlciZxdW90OzogJ2h0dHA6Ly9jdHMubmxtLm5paC5nb3YvZmhpci9WYWx1ZVNldC8yLjE2Ljg0MC4xLjExMzg4My4zLjQ2NC4xMDAzLjEwMS4xMi4xMDg4JzwvYTpzPg0KICAgICAgICAgICAgPC9hOnM+DQogICAgICAgICA8L2Fubm90YXRpb24+DQogICAgICA8L2RlZj4NCiAgICAgIDxkZWYgbG9jYWxJZD0iMjYiIGxvY2F0b3I9IjMxOjEtMzE6MTA3IiBuYW1lPSJGcmFpbHR5IFN5bXB0b20iIGlkPSJodHRwOi8vY3RzLm5sbS5uaWguZ292L2ZoaXIvVmFsdWVTZXQvMi4xNi44NDAuMS4xMTM4ODMuMy40NjQuMTAwMy4xMTMuMTIuMTA3NSIgYWNjZXNzTGV2ZWw9IlB1YmxpYyI+DQogICAgICAgICA8YW5ub3RhdGlvbiB4c2k6dHlwZT0iYTpBbm5vdGF0aW9uIj4NCiAgICAgICAgICAgIDxhOnMgcj0iMjYiPg0KICAgICAgICAgICAgICAgPGE6cz52YWx1ZXNldCAmcXVvdDtGcmFpbHR5IFN5bXB0b20mcXVvdDs6ICdodHRwOi8vY3RzLm5sbS5uaWguZ292L2ZoaXIvVmFsdWVTZXQvMi4xNi44NDAuMS4xMTM4ODMuMy40NjQuMTAwMy4xMTMuMTIuMTA3NSc8L2E6cz4NCiAgICAgICAgICAgIDwvYTpzPg0KICAgICAgICAgPC9hbm5vdGF0aW9uPg0KICAgICAgPC9kZWY+DQogICAgICA8ZGVmIGxvY2FsSWQ9IjI3IiBsb2NhdG9yPSIzMjoxLTMyOjExNiIgbmFtZT0iSG9tZSBIZWFsdGhjYXJlIFNlcnZpY2VzIiBpZD0iaHR0cDovL2N0cy5ubG0ubmloLmdvdi9maGlyL1ZhbHVlU2V0LzIuMTYuODQwLjEuMTEzODgzLjMuNDY0LjEwMDMuMTAxLjEyLjEwMTYiIGFjY2Vzc0xldmVsPSJQdWJsaWMiPg0KICAgICAgICAgPGFubm90YXRpb24geHNpOnR5cGU9ImE6QW5ub3RhdGlvbiI+DQogICAgICAgICAgICA8YTpzIHI9IjI3Ij4NCiAgICAgICAgICAgICAgIDxhOnM+dmFsdWVzZXQgJnF1b3Q7SG9tZSBIZWFsdGhjYXJlIFNlcnZpY2VzJnF1b3Q7OiAnaHR0cDovL2N0cy5ubG0ubmloLmdvdi9maGlyL1ZhbHVlU2V0LzIuMTYuODQwLjEuMTEzODgzLjMuNDY0LjEwMDMuMTAxLjEyLjEwMTYnPC9hOnM+DQogICAgICAgICAgICA8L2E6cz4NCiAgICAgICAgIDwvYW5ub3RhdGlvbj4NCiAgICAgIDwvZGVmPg0KICAgICAgPGRlZiBsb2NhbElkPSIyOCIgbG9jYXRvcj0iMzM6MS0zMzoxMDQiIG5hbWU9Ikhvc3BpY2UgY2FyZSBhbWJ1bGF0b3J5IiBpZD0iaHR0cDovL2N0cy5ubG0ubmloLmdvdi9maGlyL1ZhbHVlU2V0LzIuMTYuODQwLjEuMTEzNzYyLjEuNC4xMTA4LjE1IiBhY2Nlc3NMZXZlbD0iUHVibGljIj4NCiAgICAgICAgIDxhbm5vdGF0aW9uIHhzaTp0eXBlPSJhOkFubm90YXRpb24iPg0KICAgICAgICAgICAgPGE6cyByPSIyOCI+DQogICAgICAgICAgICAgICA8YTpzPnZhbHVlc2V0ICZxdW90O0hvc3BpY2UgY2FyZSBhbWJ1bGF0b3J5JnF1b3Q7OiAnaHR0cDovL2N0cy5ubG0ubmloLmdvdi9maGlyL1ZhbHVlU2V0LzIuMTYuODQwLjEuMTEzNzYyLjEuNC4xMTA4LjE1JzwvYTpzPg0KICAgICAgICAgICAgPC9hOnM+DQogICAgICAgICA8L2Fubm90YXRpb24+DQogICAgICA8L2RlZj4NCiAgICAgIDxkZWYgbG9jYWxJZD0iMjkiIGxvY2F0b3I9IjM0OjEtMzQ6MTE5IiBuYW1lPSJNYWxpZ25hbnQgTmVvcGxhc20gb2YgQ29sb24iIGlkPSJodHRwOi8vY3RzLm5sbS5uaWguZ292L2ZoaXIvVmFsdWVTZXQvMi4xNi44NDAuMS4xMTM4ODMuMy40NjQuMTAwMy4xMDguMTIuMTAwMSIgYWNjZXNzTGV2ZWw9IlB1YmxpYyI+DQogICAgICAgICA8YW5ub3RhdGlvbiB4c2k6dHlwZT0iYTpBbm5vdGF0aW9uIj4NCiAgICAgICAgICAgIDxhOnMgcj0iMjkiPg0KICAgICAgICAgICAgICAgPGE6cz52YWx1ZXNldCAmcXVvdDtNYWxpZ25hbnQgTmVvcGxhc20gb2YgQ29sb24mcXVvdDs6ICdodHRwOi8vY3RzLm5sbS5uaWguZ292L2ZoaXIvVmFsdWVTZXQvMi4xNi44NDAuMS4xMTM4ODMuMy40NjQuMTAwMy4xMDguMTIuMTAwMSc8L2E6cz4NCiAgICAgICAgICAgIDwvYTpzPg0KICAgICAgICAgPC9hbm5vdGF0aW9uPg0KICAgICAgPC9kZWY+DQogICAgICA8ZGVmIGxvY2FsSWQ9IjMwIiBsb2NhdG9yPSIzNToxLTM1OjExMCIgbmFtZT0iTm9uYWN1dGUgSW5wYXRpZW50IiBpZD0iaHR0cDovL2N0cy5ubG0ubmloLmdvdi9maGlyL1ZhbHVlU2V0LzIuMTYuODQwLjEuMTEzODgzLjMuNDY0LjEwMDMuMTAxLjEyLjEwODQiIGFjY2Vzc0xldmVsPSJQdWJsaWMiPg0KICAgICAgICAgPGFubm90YXRpb24geHNpOnR5cGU9ImE6QW5ub3RhdGlvbiI+DQogICAgICAgICAgICA8YTpzIHI9IjMwIj4NCiAgICAgICAgICAgICAgIDxhOnM+dmFsdWVzZXQgJnF1b3Q7Tm9uYWN1dGUgSW5wYXRpZW50JnF1b3Q7OiAnaHR0cDovL2N0cy5ubG0ubmloLmdvdi9maGlyL1ZhbHVlU2V0LzIuMTYuODQwLjEuMTEzODgzLjMuNDY0LjEwMDMuMTAxLjEyLjEwODQnPC9hOnM+DQogICAgICAgICAgICA8L2E6cz4NCiAgICAgICAgIDwvYW5ub3RhdGlvbj4NCiAgICAgIDwvZGVmPg0KICAgICAgPGRlZiBsb2NhbElkPSIzMSIgbG9jYXRvcj0iMzY6MS0zNjoxMTQiIG5hbWU9Ik51cnNpbmcgRmFjaWxpdHkgVmlzaXQiIGlkPSJodHRwOi8vY3RzLm5sbS5uaWguZ292L2ZoaXIvVmFsdWVTZXQvMi4xNi44NDAuMS4xMTM4ODMuMy40NjQuMTAwMy4xMDEuMTIuMTAxMiIgYWNjZXNzTGV2ZWw9IlB1YmxpYyI+DQogICAgICAgICA8YW5ub3RhdGlvbiB4c2k6dHlwZT0iYTpBbm5vdGF0aW9uIj4NCiAgICAgICAgICAgIDxhOnMgcj0iMzEiPg0KICAgICAgICAgICAgICAgPGE6cz52YWx1ZXNldCAmcXVvdDtOdXJzaW5nIEZhY2lsaXR5IFZpc2l0JnF1b3Q7OiAnaHR0cDovL2N0cy5ubG0ubmloLmdvdi9maGlyL1ZhbHVlU2V0LzIuMTYuODQwLjEuMTEzODgzLjMuNDY0LjEwMDMuMTAxLjEyLjEwMTInPC9hOnM+DQogICAgICAgICAgICA8L2E6cz4NCiAgICAgICAgIDwvYW5ub3RhdGlvbj4NCiAgICAgIDwvZGVmPg0KICAgICAgPGRlZiBsb2NhbElkPSIzMiIgbG9jYXRvcj0iMzc6MS0zNzoxMDMiIG5hbWU9Ik9ic2VydmF0aW9uIiBpZD0iaHR0cDovL2N0cy5ubG0ubmloLmdvdi9maGlyL1ZhbHVlU2V0LzIuMTYuODQwLjEuMTEzODgzLjMuNDY0LjEwMDMuMTAxLjEyLjEwODYiIGFjY2Vzc0xldmVsPSJQdWJsaWMiPg0KICAgICAgICAgPGFubm90YXRpb24geHNpOnR5cGU9ImE6QW5ub3RhdGlvbiI+DQogICAgICAgICAgICA8YTpzIHI9IjMyIj4NCiAgICAgICAgICAgICAgIDxhOnM+dmFsdWVzZXQgJnF1b3Q7T2JzZXJ2YXRpb24mcXVvdDs6ICdodHRwOi8vY3RzLm5sbS5uaWguZ292L2ZoaXIvVmFsdWVTZXQvMi4xNi44NDAuMS4xMTM4ODMuMy40NjQuMTAwMy4xMDEuMTIuMTA4Nic8L2E6cz4NCiAgICAgICAgICAgIDwvYTpzPg0KICAgICAgICAgPC9hbm5vdGF0aW9uPg0KICAgICAgPC9kZWY+DQogICAgICA8ZGVmIGxvY2FsSWQ9IjMzIiBsb2NhdG9yPSIzODoxLTM4OjEwNCIgbmFtZT0iT2ZmaWNlIFZpc2l0IiBpZD0iaHR0cDovL2N0cy5ubG0ubmloLmdvdi9maGlyL1ZhbHVlU2V0LzIuMTYuODQwLjEuMTEzODgzLjMuNDY0LjEwMDMuMTAxLjEyLjEwMDEiIGFjY2Vzc0xldmVsPSJQdWJsaWMiPg0KICAgICAgICAgPGFubm90YXRpb24geHNpOnR5cGU9ImE6QW5ub3RhdGlvbiI+DQogICAgICAgICAgICA8YTpzIHI9IjMzIj4NCiAgICAgICAgICAgICAgIDxhOnM+dmFsdWVzZXQgJnF1b3Q7T2ZmaWNlIFZpc2l0JnF1b3Q7OiAnaHR0cDovL2N0cy5ubG0ubmloLmdvdi9maGlyL1ZhbHVlU2V0LzIuMTYuODQwLjEuMTEzODgzLjMuNDY0LjEwMDMuMTAxLjEyLjEwMDEnPC9hOnM+DQogICAgICAgICAgICA8L2E6cz4NCiAgICAgICAgIDwvYW5ub3RhdGlvbj4NCiAgICAgIDwvZGVmPg0KICAgICAgPGRlZiBsb2NhbElkPSIzNCIgbG9jYXRvcj0iMzk6MS0zOToxMDIiIG5hbWU9Ik91dHBhdGllbnQiIGlkPSJodHRwOi8vY3RzLm5sbS5uaWguZ292L2ZoaXIvVmFsdWVTZXQvMi4xNi44NDAuMS4xMTM4ODMuMy40NjQuMTAwMy4xMDEuMTIuMTA4NyIgYWNjZXNzTGV2ZWw9IlB1YmxpYyI+DQogICAgICAgICA8YW5ub3RhdGlvbiB4c2k6dHlwZT0iYTpBbm5vdGF0aW9uIj4NCiAgICAgICAgICAgIDxhOnMgcj0iMzQiPg0KICAgICAgICAgICAgICAgPGE6cz52YWx1ZXNldCAmcXVvdDtPdXRwYXRpZW50JnF1b3Q7OiAnaHR0cDovL2N0cy5ubG0ubmloLmdvdi9maGlyL1ZhbHVlU2V0LzIuMTYuODQwLjEuMTEzODgzLjMuNDY0LjEwMDMuMTAxLjEyLjEwODcnPC9hOnM+DQogICAgICAgICAgICA8L2E6cz4NCiAgICAgICAgIDwvYW5ub3RhdGlvbj4NCiAgICAgIDwvZGVmPg0KICAgICAgPGRlZiBsb2NhbElkPSIzNSIgbG9jYXRvcj0iNDA6MS00MDoxNTQiIG5hbWU9IlByZXZlbnRpdmUgQ2FyZSBTZXJ2aWNlcyAtIEVzdGFibGlzaGVkIE9mZmljZSBWaXNpdCwgMTggYW5kIFVwIiBpZD0iaHR0cDovL2N0cy5ubG0ubmloLmdvdi9maGlyL1ZhbHVlU2V0LzIuMTYuODQwLjEuMTEzODgzLjMuNDY0LjEwMDMuMTAxLjEyLjEwMjUiIGFjY2Vzc0xldmVsPSJQdWJsaWMiPg0KICAgICAgICAgPGFubm90YXRpb24geHNpOnR5cGU9ImE6QW5ub3RhdGlvbiI+DQogICAgICAgICAgICA8YTpzIHI9IjM1Ij4NCiAgICAgICAgICAgICAgIDxhOnM+dmFsdWVzZXQgJnF1b3Q7UHJldmVudGl2ZSBDYXJlIFNlcnZpY2VzIC0gRXN0YWJsaXNoZWQgT2ZmaWNlIFZpc2l0LCAxOCBhbmQgVXAmcXVvdDs6ICdodHRwOi8vY3RzLm5sbS5uaWguZ292L2ZoaXIvVmFsdWVTZXQvMi4xNi44NDAuMS4xMTM4ODMuMy40NjQuMTAwMy4xMDEuMTIuMTAyNSc8L2E6cz4NCiAgICAgICAgICAgIDwvYTpzPg0KICAgICAgICAgPC9hbm5vdGF0aW9uPg0KICAgICAgPC9kZWY+DQogICAgICA8ZGVmIGxvY2FsSWQ9IjM2IiBsb2NhdG9yPSI0MToxLTQxOjE0OCIgbmFtZT0iUHJldmVudGl2ZSBDYXJlIFNlcnZpY2VzLUluaXRpYWwgT2ZmaWNlIFZpc2l0LCAxOCBhbmQgVXAiIGlkPSJodHRwOi8vY3RzLm5sbS5uaWguZ292L2ZoaXIvVmFsdWVTZXQvMi4xNi44NDAuMS4xMTM4ODMuMy40NjQuMTAwMy4xMDEuMTIuMTAyMyIgYWNjZXNzTGV2ZWw9IlB1YmxpYyI+DQogICAgICAgICA8YW5ub3RhdGlvbiB4c2k6dHlwZT0iYTpBbm5vdGF0aW9uIj4NCiAgICAgICAgICAgIDxhOnMgcj0iMzYiPg0KICAgICAgICAgICAgICAgPGE6cz52YWx1ZXNldCAmcXVvdDtQcmV2ZW50aXZlIENhcmUgU2VydmljZXMtSW5pdGlhbCBPZmZpY2UgVmlzaXQsIDE4IGFuZCBVcCZxdW90OzogJ2h0dHA6Ly9jdHMubmxtLm5paC5nb3YvZmhpci9WYWx1ZVNldC8yLjE2Ljg0MC4xLjExMzg4My4zLjQ2NC4xMDAzLjEwMS4xMi4xMDIzJzwvYTpzPg0KICAgICAgICAgICAgPC9hOnM+DQogICAgICAgICA8L2Fubm90YXRpb24+DQogICAgICA8L2RlZj4NCiAgICAgIDxkZWYgbG9jYWxJZD0iMzciIGxvY2F0b3I9IjQyOjEtNDI6MTA3IiBuYW1lPSJUb3RhbCBDb2xlY3RvbXkiIGlkPSJodHRwOi8vY3RzLm5sbS5uaWguZ292L2ZoaXIvVmFsdWVTZXQvMi4xNi44NDAuMS4xMTM4ODMuMy40NjQuMTAwMy4xOTguMTIuMTAxOSIgYWNjZXNzTGV2ZWw9IlB1YmxpYyI+DQogICAgICAgICA8YW5ub3RhdGlvbiB4c2k6dHlwZT0iYTpBbm5vdGF0aW9uIj4NCiAgICAgICAgICAgIDxhOnMgcj0iMzciPg0KICAgICAgICAgICAgICAgPGE6cz52YWx1ZXNldCAmcXVvdDtUb3RhbCBDb2xlY3RvbXkmcXVvdDs6ICdodHRwOi8vY3RzLm5sbS5uaWguZ292L2ZoaXIvVmFsdWVTZXQvMi4xNi44NDAuMS4xMTM4ODMuMy40NjQuMTAwMy4xOTguMTIuMTAxOSc8L2E6cz4NCiAgICAgICAgICAgIDwvYTpzPg0KICAgICAgICAgPC9hbm5vdGF0aW9uPg0KICAgICAgPC9kZWY+DQogICA8L3ZhbHVlU2V0cz4NCiAgIDxjb2Rlcz4NCiAgICAgIDxkZWYgbG9jYWxJZD0iMzkiIGxvY2F0b3I9IjQ0OjEtNDQ6NjIiIG5hbWU9IkJpcnRoIGRhdGUiIGlkPSIyMTExMi04IiBkaXNwbGF5PSJCaXJ0aCBkYXRlIiBhY2Nlc3NMZXZlbD0iUHVibGljIj4NCiAgICAgICAgIDxhbm5vdGF0aW9uIHhzaTp0eXBlPSJhOkFubm90YXRpb24iPg0KICAgICAgICAgICAgPGE6cyByPSIzOSI+DQogICAgICAgICAgICAgICA8YTpzPmNvZGUgJnF1b3Q7QmlydGggZGF0ZSZxdW90OzogJzIxMTEyLTgnIGZyb20gPC9hOnM+DQogICAgICAgICAgICAgICA8YTpzIHI9IjM4Ij4NCiAgICAgICAgICAgICAgICAgIDxhOnM+JnF1b3Q7TE9JTkMmcXVvdDs8L2E6cz4NCiAgICAgICAgICAgICAgIDwvYTpzPg0KICAgICAgICAgICAgICAgPGE6cz4gZGlzcGxheSAnQmlydGggZGF0ZSc8L2E6cz4NCiAgICAgICAgICAgIDwvYTpzPg0KICAgICAgICAgPC9hbm5vdGF0aW9uPg0KICAgICAgICAgPGNvZGVTeXN0ZW0gbG9jYWxJZD0iMzgiIGxvY2F0b3I9IjQ0OjM1LTQ0OjQxIiBuYW1lPSJMT0lOQyIvPg0KICAgICAgPC9kZWY+DQogICA8L2NvZGVzPg0KICAgPHN0YXRlbWVudHM+DQogICAgICA8ZGVmIGxvY2F0b3I9IjQ5OjEtNDk6MTUiIG5hbWU9IlBhdGllbnQiIGNvbnRleHQ9IlBhdGllbnQiPg0KICAgICAgICAgPGV4cHJlc3Npb24geHNpOnR5cGU9IlNpbmdsZXRvbkZyb20iPg0KICAgICAgICAgICAgPG9wZXJhbmQgbG9jYXRvcj0iNDk6MS00OToxNSIgZGF0YVR5cGU9ImZoaXI6UGF0aWVudCIgdGVtcGxhdGVJZD0iaHR0cDovL2hsNy5vcmcvZmhpci9TdHJ1Y3R1cmVEZWZpbml0aW9uL1BhdGllbnQiIHhzaTp0eXBlPSJSZXRyaWV2ZSIvPg0KICAgICAgICAgPC9leHByZXNzaW9uPg0KICAgICAgPC9kZWY+DQogICAgICA8ZGVmIGxvY2FsSWQ9IjQ4IiBsb2NhdG9yPSI1MToxLTUyOjIxIiBuYW1lPSJTREUgRXRobmljaXR5IiBjb250ZXh0PSJQYXRpZW50IiBhY2Nlc3NMZXZlbD0iUHVibGljIj4NCiAgICAgICAgIDxhbm5vdGF0aW9uIHhzaTp0eXBlPSJhOkFubm90YXRpb24iPg0KICAgICAgICAgICAgPGE6cyByPSI0OCI+DQogICAgICAgICAgICAgICA8YTpzPmRlZmluZSAmcXVvdDtTREUgRXRobmljaXR5JnF1b3Q7OgogIDwvYTpzPg0KICAgICAgICAgICAgICAgPGE6cyByPSI0NyI+DQogICAgICAgICAgICAgICAgICA8YTpzIHI9IjQ2Ij4NCiAgICAgICAgICAgICAgICAgICAgIDxhOnM+U0RFPC9hOnM+DQogICAgICAgICAgICAgICAgICA8L2E6cz4NCiAgICAgICAgICAgICAgICAgIDxhOnM+LjwvYTpzPg0KICAgICAgICAgICAgICAgICAgPGE6cyByPSI0NyI+DQogICAgICAgICAgICAgICAgICAgICA8YTpzPiZxdW90O1NERSBFdGhuaWNpdHkmcXVvdDs8L2E6cz4NCiAgICAgICAgICAgICAgICAgIDwvYTpzPg0KICAgICAgICAgICAgICAgPC9hOnM+DQogICAgICAgICAgICA8L2E6cz4NCiAgICAgICAgIDwvYW5ub3RhdGlvbj4NCiAgICAgICAgIDxleHByZXNzaW9uIGxvY2FsSWQ9IjQ3IiBsb2NhdG9yPSI1MjozLTUyOjIxIiBuYW1lPSJTREUgRXRobmljaXR5IiBsaWJyYXJ5TmFtZT0iU0RFIiB4c2k6dHlwZT0iRXhwcmVzc2lvblJlZiIvPg0KICAgICAgPC9kZWY+DQogICAgICA8ZGVmIGxvY2FsSWQ9IjUxIiBsb2NhdG9yPSI1NDoxLTU1OjE3IiBuYW1lPSJTREUgUGF5ZXIiIGNvbnRleHQ9IlBhdGllbnQiIGFjY2Vzc0xldmVsPSJQdWJsaWMiPg0KICAgICAgICAgPGFubm90YXRpb24geHNpOnR5cGU9ImE6QW5ub3RhdGlvbiI+DQogICAgICAgICAgICA8YTpzIHI9IjUxIj4NCiAgICAgICAgICAgICAgIDxhOnM+ZGVmaW5lICZxdW90O1NERSBQYXllciZxdW90OzoKICA8L2E6cz4NCiAgICAgICAgICAgICAgIDxhOnMgcj0iNTAiPg0KICAgICAgICAgICAgICAgICAgPGE6cyByPSI0OSI+DQogICAgICAgICAgICAgICAgICAgICA8YTpzPlNERTwvYTpzPg0KICAgICAgICAgICAgICAgICAgPC9hOnM+DQogICAgICAgICAgICAgICAgICA8YTpzPi48L2E6cz4NCiAgICAgICAgICAgICAgICAgIDxhOnMgcj0iNTAiPg0KICAgICAgICAgICAgICAgICAgICAgPGE6cz4mcXVvdDtTREUgUGF5ZXImcXVvdDs8L2E6cz4NCiAgICAgICAgICAgICAgICAgIDwvYTpzPg0KICAgICAgICAgICAgICAgPC9hOnM+DQogICAgICAgICAgICA8L2E6cz4NCiAgICAgICAgIDwvYW5ub3RhdGlvbj4NCiAgICAgICAgIDxleHByZXNzaW9uIGxvY2FsSWQ9IjUwIiBsb2NhdG9yPSI1NTozLTU1OjE3IiBuYW1lPSJTREUgUGF5ZXIiIGxpYnJhcnlOYW1lPSJTREUiIHhzaTp0eXBlPSJFeHByZXNzaW9uUmVmIi8+DQogICAgICA8L2RlZj4NCiAgICAgIDxkZWYgbG9jYWxJZD0iNTQiIGxvY2F0b3I9IjU3OjEtNTg6MTYiIG5hbWU9IlNERSBSYWNlIiBjb250ZXh0PSJQYXRpZW50IiBhY2Nlc3NMZXZlbD0iUHVibGljIj4NCiAgICAgICAgIDxhbm5vdGF0aW9uIHhzaTp0eXBlPSJhOkFubm90YXRpb24iPg0KICAgICAgICAgICAgPGE6cyByPSI1NCI+DQogICAgICAgICAgICAgICA8YTpzPmRlZmluZSAmcXVvdDtTREUgUmFjZSZxdW90OzoKICA8L2E6cz4NCiAgICAgICAgICAgICAgIDxhOnMgcj0iNTMiPg0KICAgICAgICAgICAgICAgICAgPGE6cyByPSI1MiI+DQogICAgICAgICAgICAgICAgICAgICA8YTpzPlNERTwvYTpzPg0KICAgICAgICAgICAgICAgICAgPC9hOnM+DQogICAgICAgICAgICAgICAgICA8YTpzPi48L2E6cz4NCiAgICAgICAgICAgICAgICAgIDxhOnMgcj0iNTMiPg0KICAgICAgICAgICAgICAgICAgICAgPGE6cz4mcXVvdDtTREUgUmFjZSZxdW90OzwvYTpzPg0KICAgICAgICAgICAgICAgICAgPC9hOnM+DQogICAgICAgICAgICAgICA8L2E6cz4NCiAgICAgICAgICAgIDwvYTpzPg0KICAgICAgICAgPC9hbm5vdGF0aW9uPg0KICAgICAgICAgPGV4cHJlc3Npb24gbG9jYWxJZD0iNTMiIGxvY2F0b3I9IjU4OjMtNTg6MTYiIG5hbWU9IlNERSBSYWNlIiBsaWJyYXJ5TmFtZT0iU0RFIiB4c2k6dHlwZT0iRXhwcmVzc2lvblJlZiIvPg0KICAgICAgPC9kZWY+DQogICAgICA8ZGVmIGxvY2FsSWQ9IjU3IiBsb2NhdG9yPSI2MDoxLTYxOjE1IiBuYW1lPSJTREUgU2V4IiBjb250ZXh0PSJQYXRpZW50IiBhY2Nlc3NMZXZlbD0iUHVibGljIj4NCiAgICAgICAgIDxhbm5vdGF0aW9uIHhzaTp0eXBlPSJhOkFubm90YXRpb24iPg0KICAgICAgICAgICAgPGE6cyByPSI1NyI+DQogICAgICAgICAgICAgICA8YTpzPmRlZmluZSAmcXVvdDtTREUgU2V4JnF1b3Q7OgogIDwvYTpzPg0KICAgICAgICAgICAgICAgPGE6cyByPSI1NiI+DQogICAgICAgICAgICAgICAgICA8YTpzIHI9IjU1Ij4NCiAgICAgICAgICAgICAgICAgICAgIDxhOnM+U0RFPC9hOnM+DQogICAgICAgICAgICAgICAgICA8L2E6cz4NCiAgICAgICAgICAgICAgICAgIDxhOnM+LjwvYTpzPg0KICAgICAgICAgICAgICAgICAgPGE6cyByPSI1NiI+DQogICAgICAgICAgICAgICAgICAgICA8YTpzPiZxdW90O1NERSBTZXgmcXVvdDs8L2E6cz4NCiAgICAgICAgICAgICAgICAgIDwvYTpzPg0KICAgICAgICAgICAgICAgPC9hOnM+DQogICAgICAgICAgICA8L2E6cz4NCiAgICAgICAgIDwvYW5ub3RhdGlvbj4NCiAgICAgICAgIDxleHByZXNzaW9uIGxvY2FsSWQ9IjU2IiBsb2NhdG9yPSI2MTozLTYxOjE1IiBuYW1lPSJTREUgU2V4IiBsaWJyYXJ5TmFtZT0iU0RFIiB4c2k6dHlwZT0iRXhwcmVzc2lvblJlZiIvPg0KICAgICAgPC9kZWY+DQogICAgICA8ZGVmIGxvY2FsSWQ9IjY4IiBsb2NhdG9yPSI2MzoxLTY1OjYyIiBuYW1lPSJJbml0aWFsIFBvcHVsYXRpb24iIGNvbnRleHQ9IlBhdGllbnQiIGFjY2Vzc0xldmVsPSJQdWJsaWMiPg0KICAgICAgICAgPGFubm90YXRpb24geHNpOnR5cGU9ImE6QW5ub3RhdGlvbiI+DQogICAgICAgICAgICA8YTpzIHI9IjY4Ij4NCiAgICAgICAgICAgICAgIDxhOnM+ZGVmaW5lICZxdW90O0luaXRpYWwgUG9wdWxhdGlvbiZxdW90OzoKICA8L2E6cz4NCiAgICAgICAgICAgICAgIDxhOnMgcj0iNjciPg0KICAgICAgICAgICAgICAgICAgPGE6cyByPSI2MyI+DQogICAgICAgICAgICAgICAgICAgICA8YTpzIHI9IjYwIj4NCiAgICAgICAgICAgICAgICAgICAgICAgIDxhOnM+QWdlSW5ZZWFyc0F0KDwvYTpzPg0KICAgICAgICAgICAgICAgICAgICAgICAgPGE6cyByPSI1OSI+DQogICAgICAgICAgICAgICAgICAgICAgICAgICA8YTpzPnN0YXJ0IG9mIDwvYTpzPg0KICAgICAgICAgICAgICAgICAgICAgICAgICAgPGE6cyByPSI1OCI+DQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8YTpzPiZxdW90O01lYXN1cmVtZW50IFBlcmlvZCZxdW90OzwvYTpzPg0KICAgICAgICAgICAgICAgICAgICAgICAgICAgPC9hOnM+DQogICAgICAgICAgICAgICAgICAgICAgICA8L2E6cz4NCiAgICAgICAgICAgICAgICAgICAgICAgIDxhOnM+KTwvYTpzPg0KICAgICAgICAgICAgICAgICAgICAgPC9hOnM+DQogICAgICAgICAgICAgICAgICAgICA8YTpzIHI9IjYxIj4gYmV0d2VlbiA1MCBhbmQgNzU8L2E6cz4NCiAgICAgICAgICAgICAgICAgIDwvYTpzPg0KICAgICAgICAgICAgICAgICAgPGE6cz4KCSBhbmQgPC9hOnM+DQogICAgICAgICAgICAgICAgICA8YTpzIHI9IjY2Ij4NCiAgICAgICAgICAgICAgICAgICAgIDxhOnM+ZXhpc3RzIDwvYTpzPg0KICAgICAgICAgICAgICAgICAgICAgPGE6cyByPSI2NSI+DQogICAgICAgICAgICAgICAgICAgICAgICA8YTpzIHI9IjY0Ij4NCiAgICAgICAgICAgICAgICAgICAgICAgICAgIDxhOnM+QWR1bHRPdXRwYXRpZW50RW5jb3VudGVyczwvYTpzPg0KICAgICAgICAgICAgICAgICAgICAgICAgPC9hOnM+DQogICAgICAgICAgICAgICAgICAgICAgICA8YTpzPi48L2E6cz4NCiAgICAgICAgICAgICAgICAgICAgICAgIDxhOnMgcj0iNjUiPg0KICAgICAgICAgICAgICAgICAgICAgICAgICAgPGE6cz4mcXVvdDtRdWFsaWZ5aW5nIEVuY291bnRlcnMmcXVvdDs8L2E6cz4NCiAgICAgICAgICAgICAgICAgICAgICAgIDwvYTpzPg0KICAgICAgICAgICAgICAgICAgICAgPC9hOnM+DQogICAgICAgICAgICAgICAgICA8L2E6cz4NCiAgICAgICAgICAgICAgIDwvYTpzPg0KICAgICAgICAgICAgPC9hOnM+DQogICAgICAgICA8L2Fubm90YXRpb24+DQogICAgICAgICA8ZXhwcmVzc2lvbiBsb2NhbElkPSI2NyIgbG9jYXRvcj0iNjQ6My02NTo2MiIgeHNpOnR5cGU9IkFuZCI+DQogICAgICAgICAgICA8b3BlcmFuZCBsb2NhbElkPSI2MyIgbG9jYXRvcj0iNjQ6My02NDo2MyIgeHNpOnR5cGU9IkFuZCI+DQogICAgICAgICAgICAgICA8b3BlcmFuZCB4c2k6dHlwZT0iR3JlYXRlck9yRXF1YWwiPg0KICAgICAgICAgICAgICAgICAgPG9wZXJhbmQgbG9jYWxJZD0iNjAiIGxvY2F0b3I9IjY0OjMtNjQ6NDUiIHByZWNpc2lvbj0iWWVhciIgeHNpOnR5cGU9IkNhbGN1bGF0ZUFnZUF0Ij4NCiAgICAgICAgICAgICAgICAgICAgIDxvcGVyYW5kIHhzaTp0eXBlPSJUb0RhdGVUaW1lIj4NCiAgICAgICAgICAgICAgICAgICAgICAgIDxvcGVyYW5kIHBhdGg9ImJpcnRoRGF0ZS52YWx1ZSIgeHNpOnR5cGU9IlByb3BlcnR5Ij4NCiAgICAgICAgICAgICAgICAgICAgICAgICAgIDxzb3VyY2UgbmFtZT0iUGF0aWVudCIgeHNpOnR5cGU9IkV4cHJlc3Npb25SZWYiLz4NCiAgICAgICAgICAgICAgICAgICAgICAgIDwvb3BlcmFuZD4NCiAgICAgICAgICAgICAgICAgICAgIDwvb3BlcmFuZD4NCiAgICAgICAgICAgICAgICAgICAgIDxvcGVyYW5kIGxvY2FsSWQ9IjU5IiBsb2NhdG9yPSI2NDoxNi02NDo0NCIgeHNpOnR5cGU9IlN0YXJ0Ij4NCiAgICAgICAgICAgICAgICAgICAgICAgIDxvcGVyYW5kIGxvY2FsSWQ9IjU4IiBsb2NhdG9yPSI2NDoyNS02NDo0NCIgbmFtZT0iTWVhc3VyZW1lbnQgUGVyaW9kIiB4c2k6dHlwZT0iUGFyYW1ldGVyUmVmIi8+DQogICAgICAgICAgICAgICAgICAgICA8L29wZXJhbmQ+DQogICAgICAgICAgICAgICAgICA8L29wZXJhbmQ+DQogICAgICAgICAgICAgICAgICA8b3BlcmFuZCBsb2NhbElkPSI2MSIgbG9jYXRvcj0iNjQ6NTUtNjQ6NTYiIHZhbHVlVHlwZT0idDpJbnRlZ2VyIiB2YWx1ZT0iNTAiIHhzaTp0eXBlPSJMaXRlcmFsIi8+DQogICAgICAgICAgICAgICA8L29wZXJhbmQ+DQogICAgICAgICAgICAgICA8b3BlcmFuZCB4c2k6dHlwZT0iTGVzc09yRXF1YWwiPg0KICAgICAgICAgICAgICAgICAgPG9wZXJhbmQgbG9jYWxJZD0iNjAiIGxvY2F0b3I9IjY0OjMtNjQ6NDUiIHByZWNpc2lvbj0iWWVhciIgeHNpOnR5cGU9IkNhbGN1bGF0ZUFnZUF0Ij4NCiAgICAgICAgICAgICAgICAgICAgIDxvcGVyYW5kIHhzaTp0eXBlPSJUb0RhdGVUaW1lIj4NCiAgICAgICAgICAgICAgICAgICAgICAgIDxvcGVyYW5kIHBhdGg9ImJpcnRoRGF0ZS52YWx1ZSIgeHNpOnR5cGU9IlByb3BlcnR5Ij4NCiAgICAgICAgICAgICAgICAgICAgICAgICAgIDxzb3VyY2UgbmFtZT0iUGF0aWVudCIgeHNpOnR5cGU9IkV4cHJlc3Npb25SZWYiLz4NCiAgICAgICAgICAgICAgICAgICAgICAgIDwvb3BlcmFuZD4NCiAgICAgICAgICAgICAgICAgICAgIDwvb3BlcmFuZD4NCiAgICAgICAgICAgICAgICAgICAgIDxvcGVyYW5kIGxvY2FsSWQ9IjU5IiBsb2NhdG9yPSI2NDoxNi02NDo0NCIgeHNpOnR5cGU9IlN0YXJ0Ij4NCiAgICAgICAgICAgICAgICAgICAgICAgIDxvcGVyYW5kIGxvY2FsSWQ9IjU4IiBsb2NhdG9yPSI2NDoyNS02NDo0NCIgbmFtZT0iTWVhc3VyZW1lbnQgUGVyaW9kIiB4c2k6dHlwZT0iUGFyYW1ldGVyUmVmIi8+DQogICAgICAgICAgICAgICAgICAgICA8L29wZXJhbmQ+DQogICAgICAgICAgICAgICAgICA8L29wZXJhbmQ+DQogICAgICAgICAgICAgICAgICA8b3BlcmFuZCBsb2NhbElkPSI2MiIgbG9jYXRvcj0iNjQ6NjItNjQ6NjMiIHZhbHVlVHlwZT0idDpJbnRlZ2VyIiB2YWx1ZT0iNzUiIHhzaTp0eXBlPSJMaXRlcmFsIi8+DQogICAgICAgICAgICAgICA8L29wZXJhbmQ+DQogICAgICAgICAgICA8L29wZXJhbmQ+DQogICAgICAgICAgICA8b3BlcmFuZCBsb2NhbElkPSI2NiIgbG9jYXRvcj0iNjU6Ny02NTo2MiIgeHNpOnR5cGU9IkV4aXN0cyI+DQogICAgICAgICAgICAgICA8b3BlcmFuZCBsb2NhbElkPSI2NSIgbG9jYXRvcj0iNjU6MTQtNjU6NjIiIG5hbWU9IlF1YWxpZnlpbmcgRW5jb3VudGVycyIgbGlicmFyeU5hbWU9IkFkdWx0T3V0cGF0aWVudEVuY291bnRlcnMiIHhzaTp0eXBlPSJFeHByZXNzaW9uUmVmIi8+DQogICAgICAgICAgICA8L29wZXJhbmQ+DQogICAgICAgICA8L2V4cHJlc3Npb24+DQogICAgICA8L2RlZj4NCiAgICAgIDxkZWYgbG9jYWxJZD0iNzAiIGxvY2F0b3I9IjY3OjEtNjg6MjIiIG5hbWU9IkRlbm9taW5hdG9yIiBjb250ZXh0PSJQYXRpZW50IiBhY2Nlc3NMZXZlbD0iUHVibGljIj4NCiAgICAgICAgIDxhbm5vdGF0aW9uIHhzaTp0eXBlPSJhOkFubm90YXRpb24iPg0KICAgICAgICAgICAgPGE6cyByPSI3MCI+DQogICAgICAgICAgICAgICA8YTpzPmRlZmluZSAmcXVvdDtEZW5vbWluYXRvciZxdW90OzoKICA8L2E6cz4NCiAgICAgICAgICAgICAgIDxhOnMgcj0iNjkiPg0KICAgICAgICAgICAgICAgICAgPGE6cz4mcXVvdDtJbml0aWFsIFBvcHVsYXRpb24mcXVvdDs8L2E6cz4NCiAgICAgICAgICAgICAgIDwvYTpzPg0KICAgICAgICAgICAgPC9hOnM+DQogICAgICAgICA8L2Fubm90YXRpb24+DQogICAgICAgICA8ZXhwcmVzc2lvbiBsb2NhbElkPSI2OSIgbG9jYXRvcj0iNjg6My02ODoyMiIgbmFtZT0iSW5pdGlhbCBQb3B1bGF0aW9uIiB4c2k6dHlwZT0iRXhwcmVzc2lvblJlZiIvPg0KICAgICAgPC9kZWY+DQogICAgICA8ZGVmIGxvY2FsSWQ9Ijg3IiBsb2NhdG9yPSI3MDoxLTczOjE0OSIgbmFtZT0iRmxleGlibGUgU2lnbW9pZG9zY29weSBQZXJmb3JtZWQiIGNvbnRleHQ9IlBhdGllbnQiIGFjY2Vzc0xldmVsPSJQdWJsaWMiPg0KICAgICAgICAgPGFubm90YXRpb24geHNpOnR5cGU9ImE6QW5ub3RhdGlvbiI+DQogICAgICAgICAgICA8YTpzIHI9Ijg3Ij4NCiAgICAgICAgICAgICAgIDxhOnM+ZGVmaW5lICZxdW90O0ZsZXhpYmxlIFNpZ21vaWRvc2NvcHkgUGVyZm9ybWVkJnF1b3Q7OgogIDwvYTpzPg0KICAgICAgICAgICAgICAgPGE6cyByPSI4NiI+DQogICAgICAgICAgICAgICAgICA8YTpzPg0KICAgICAgICAgICAgICAgICAgICAgPGE6cyByPSI3MiI+DQogICAgICAgICAgICAgICAgICAgICAgICA8YTpzIHI9IjcxIj4NCiAgICAgICAgICAgICAgICAgICAgICAgICAgIDxhOnMgcj0iNzEiPg0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPGE6cz5bUHJvY2VkdXJlOiA8L2E6cz4NCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDxhOnM+DQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8YTpzPiZxdW90O0ZsZXhpYmxlIFNpZ21vaWRvc2NvcHkmcXVvdDs8L2E6cz4NCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDwvYTpzPg0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPGE6cz5dPC9hOnM+DQogICAgICAgICAgICAgICAgICAgICAgICAgICA8L2E6cz4NCiAgICAgICAgICAgICAgICAgICAgICAgIDwvYTpzPg0KICAgICAgICAgICAgICAgICAgICAgICAgPGE6cz4gRmxleGlibGVTaWdtb2lkb3Njb3B5PC9hOnM+DQogICAgICAgICAgICAgICAgICAgICA8L2E6cz4NCiAgICAgICAgICAgICAgICAgIDwvYTpzPg0KICAgICAgICAgICAgICAgICAgPGE6cz4KICAgICAgICAgICAgICAgICAgICAJCTwvYTpzPg0KICAgICAgICAgICAgICAgICAgPGE6cyByPSI4NSI+DQogICAgICAgICAgICAgICAgICAgICA8YTpzPndoZXJlIDwvYTpzPg0KICAgICAgICAgICAgICAgICAgICAgPGE6cyByPSI4NSI+DQogICAgICAgICAgICAgICAgICAgICAgICA8YTpzIHI9Ijc2Ij4NCiAgICAgICAgICAgICAgICAgICAgICAgICAgIDxhOnMgcj0iNzQiPg0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPGE6cyByPSI3MyI+DQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8YTpzPkZsZXhpYmxlU2lnbW9pZG9zY29weTwvYTpzPg0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPC9hOnM+DQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8YTpzPi48L2E6cz4NCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDxhOnMgcj0iNzQiPg0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPGE6cz5zdGF0dXM8L2E6cz4NCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDwvYTpzPg0KICAgICAgICAgICAgICAgICAgICAgICAgICAgPC9hOnM+DQogICAgICAgICAgICAgICAgICAgICAgICAgICA8YTpzPiA9IDwvYTpzPg0KICAgICAgICAgICAgICAgICAgICAgICAgICAgPGE6cyByPSI3NSI+DQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8YTpzPidjb21wbGV0ZWQnPC9hOnM+DQogICAgICAgICAgICAgICAgICAgICAgICAgICA8L2E6cz4NCiAgICAgICAgICAgICAgICAgICAgICAgIDwvYTpzPg0KICAgICAgICAgICAgICAgICAgICAgICAgPGE6cz4KICAgICAgICAgICAgICAgICAgICAJCQlhbmQgPC9hOnM+DQogICAgICAgICAgICAgICAgICAgICAgICA8YTpzIHI9Ijg0Ij4NCiAgICAgICAgICAgICAgICAgICAgICAgICAgIDxhOnMgcj0iODAiPg0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPGE6cyByPSI3NyI+DQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8YTpzPkdsb2JhbDwvYTpzPg0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPC9hOnM+DQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8YTpzPi48L2E6cz4NCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDxhOnMgcj0iODAiPg0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPGE6cz4mcXVvdDtOb3JtYWxpemUgSW50ZXJ2YWwmcXVvdDsoPC9hOnM+DQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8YTpzIHI9Ijc5Ij4NCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDxhOnMgcj0iNzgiPg0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPGE6cz5GbGV4aWJsZVNpZ21vaWRvc2NvcHk8L2E6cz4NCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDwvYTpzPg0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPGE6cz4uPC9hOnM+DQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8YTpzIHI9Ijc5Ij4NCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDxhOnM+cGVyZm9ybWVkPC9hOnM+DQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8L2E6cz4NCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDwvYTpzPg0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPGE6cz4pPC9hOnM+DQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8L2E6cz4NCiAgICAgICAgICAgICAgICAgICAgICAgICAgIDwvYTpzPg0KICAgICAgICAgICAgICAgICAgICAgICAgICAgPGE6cz4gPC9hOnM+DQogICAgICAgICAgICAgICAgICAgICAgICAgICA8YTpzIHI9Ijg0Ij4NCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDxhOnM+ZW5kcyA8L2E6cz4NCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDxhOnMgcj0iODMiPg0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPGE6cz41IHllYXJzPC9hOnM+DQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8L2E6cz4NCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDxhOnM+IG9yIGxlc3Mgb24gb3IgYmVmb3JlPC9hOnM+DQogICAgICAgICAgICAgICAgICAgICAgICAgICA8L2E6cz4NCiAgICAgICAgICAgICAgICAgICAgICAgICAgIDxhOnM+IDwvYTpzPg0KICAgICAgICAgICAgICAgICAgICAgICAgICAgPGE6cyByPSI4MiI+DQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8YTpzPmVuZCBvZiA8L2E6cz4NCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDxhOnMgcj0iODEiPg0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPGE6cz4mcXVvdDtNZWFzdXJlbWVudCBQZXJpb2QmcXVvdDs8L2E6cz4NCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDwvYTpzPg0KICAgICAgICAgICAgICAgICAgICAgICAgICAgPC9hOnM+DQogICAgICAgICAgICAgICAgICAgICAgICA8L2E6cz4NCiAgICAgICAgICAgICAgICAgICAgIDwvYTpzPg0KICAgICAgICAgICAgICAgICAgPC9hOnM+DQogICAgICAgICAgICAgICA8L2E6cz4NCiAgICAgICAgICAgIDwvYTpzPg0KICAgICAgICAgPC9hbm5vdGF0aW9uPg0KICAgICAgICAgPGV4cHJlc3Npb24gbG9jYWxJZD0iODYiIGxvY2F0b3I9IjcxOjMtNzM6MTQ5IiB4c2k6dHlwZT0iUXVlcnkiPg0KICAgICAgICAgICAgPHNvdXJjZSBsb2NhbElkPSI3MiIgbG9jYXRvcj0iNzE6My03MTo2MSIgYWxpYXM9IkZsZXhpYmxlU2lnbW9pZG9zY29weSI+DQogICAgICAgICAgICAgICA8ZXhwcmVzc2lvbiBsb2NhbElkPSI3MSIgbG9jYXRvcj0iNzE6My03MTozOSIgZGF0YVR5cGU9ImZoaXI6UHJvY2VkdXJlIiB0ZW1wbGF0ZUlkPSJodHRwOi8vaGw3Lm9yZy9maGlyL1N0cnVjdHVyZURlZmluaXRpb24vUHJvY2VkdXJlIiBjb2RlUHJvcGVydHk9ImNvZGUiIGNvZGVDb21wYXJhdG9yPSJpbiIgeHNpOnR5cGU9IlJldHJpZXZlIj4NCiAgICAgICAgICAgICAgICAgIDxjb2RlcyBsb2NhdG9yPSI3MToxNS03MTozOCIgbmFtZT0iRmxleGlibGUgU2lnbW9pZG9zY29weSIgeHNpOnR5cGU9IlZhbHVlU2V0UmVmIi8+DQogICAgICAgICAgICAgICA8L2V4cHJlc3Npb24+DQogICAgICAgICAgICA8L3NvdXJjZT4NCiAgICAgICAgICAgIDx3aGVyZSBsb2NhbElkPSI4NSIgbG9jYXRvcj0iNzI6MjMtNzM6MTQ5IiB4c2k6dHlwZT0iQW5kIj4NCiAgICAgICAgICAgICAgIDxvcGVyYW5kIGxvY2FsSWQ9Ijc2IiBsb2NhdG9yPSI3MjoyOS03Mjo3MCIgeHNpOnR5cGU9IkVxdWFsIj4NCiAgICAgICAgICAgICAgICAgIDxvcGVyYW5kIG5hbWU9IlRvU3RyaW5nIiBsaWJyYXJ5TmFtZT0iRkhJUkhlbHBlcnMiIHhzaTp0eXBlPSJGdW5jdGlvblJlZiI+DQogICAgICAgICAgICAgICAgICAgICA8b3BlcmFuZCBsb2NhbElkPSI3NCIgbG9jYXRvcj0iNzI6MjktNzI6NTYiIHBhdGg9InN0YXR1cyIgc2NvcGU9IkZsZXhpYmxlU2lnbW9pZG9zY29weSIgeHNpOnR5cGU9IlByb3BlcnR5Ii8+DQogICAgICAgICAgICAgICAgICA8L29wZXJhbmQ+DQogICAgICAgICAgICAgICAgICA8b3BlcmFuZCBsb2NhbElkPSI3NSIgbG9jYXRvcj0iNzI6NjAtNzI6NzAiIHZhbHVlVHlwZT0idDpTdHJpbmciIHZhbHVlPSJjb21wbGV0ZWQiIHhzaTp0eXBlPSJMaXRlcmFsIi8+DQogICAgICAgICAgICAgICA8L29wZXJhbmQ+DQogICAgICAgICAgICAgICA8b3BlcmFuZCBsb2NhbElkPSI4NCIgbG9jYXRvcj0iNzM6MjgtNzM6MTQ5IiB4c2k6dHlwZT0iQW5kIj4NCiAgICAgICAgICAgICAgICAgIDxvcGVyYW5kIGxvY2F0b3I9IjczOjk0LTczOjEwOCIgeHNpOnR5cGU9IkluIj4NCiAgICAgICAgICAgICAgICAgICAgIDxvcGVyYW5kIGxvY2F0b3I9IjczOjg5LTczOjkyIiB4c2k6dHlwZT0iRW5kIj4NCiAgICAgICAgICAgICAgICAgICAgICAgIDxvcGVyYW5kIGxvY2FsSWQ9IjgwIiBsb2NhdG9yPSI3MzoyOC03Mzo4NyIgbmFtZT0iTm9ybWFsaXplIEludGVydmFsIiBsaWJyYXJ5TmFtZT0iR2xvYmFsIiB4c2k6dHlwZT0iRnVuY3Rpb25SZWYiPg0KICAgICAgICAgICAgICAgICAgICAgICAgICAgPG9wZXJhbmQgbG9jYWxJZD0iNzkiIGxvY2F0b3I9IjczOjU2LTczOjg2IiBwYXRoPSJwZXJmb3JtZWQiIHNjb3BlPSJGbGV4aWJsZVNpZ21vaWRvc2NvcHkiIHhzaTp0eXBlPSJQcm9wZXJ0eSIvPg0KICAgICAgICAgICAgICAgICAgICAgICAgPC9vcGVyYW5kPg0KICAgICAgICAgICAgICAgICAgICAgPC9vcGVyYW5kPg0KICAgICAgICAgICAgICAgICAgICAgPG9wZXJhbmQgbG9jYXRvcj0iNzM6OTQtNzM6MTA4IiBsb3dDbG9zZWQ9InRydWUiIGhpZ2hDbG9zZWQ9InRydWUiIHhzaTp0eXBlPSJJbnRlcnZhbCI+DQogICAgICAgICAgICAgICAgICAgICAgICA8bG93IGxvY2F0b3I9IjczOjEyMy03MzoxNDkiIHhzaTp0eXBlPSJTdWJ0cmFjdCI+DQogICAgICAgICAgICAgICAgICAgICAgICAgICA8b3BlcmFuZCBsb2NhbElkPSI4MiIgbG9jYXRvcj0iNzM6MTIzLTczOjE0OSIgeHNpOnR5cGU9IkVuZCI+DQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8b3BlcmFuZCBsb2NhbElkPSI4MSIgbG9jYXRvcj0iNzM6MTMwLTczOjE0OSIgbmFtZT0iTWVhc3VyZW1lbnQgUGVyaW9kIiB4c2k6dHlwZT0iUGFyYW1ldGVyUmVmIi8+DQogICAgICAgICAgICAgICAgICAgICAgICAgICA8L29wZXJhbmQ+DQogICAgICAgICAgICAgICAgICAgICAgICAgICA8b3BlcmFuZCBsb2NhbElkPSI4MyIgbG9jYXRvcj0iNzM6OTQtNzM6MTAwIiB2YWx1ZT0iNSIgdW5pdD0ieWVhcnMiIHhzaTp0eXBlPSJRdWFudGl0eSIvPg0KICAgICAgICAgICAgICAgICAgICAgICAgPC9sb3c+DQogICAgICAgICAgICAgICAgICAgICAgICA8aGlnaCBsb2NhbElkPSI4MiIgbG9jYXRvcj0iNzM6MTIzLTczOjE0OSIgeHNpOnR5cGU9IkVuZCI+DQogICAgICAgICAgICAgICAgICAgICAgICAgICA8b3BlcmFuZCBsb2NhbElkPSI4MSIgbG9jYXRvcj0iNzM6MTMwLTczOjE0OSIgbmFtZT0iTWVhc3VyZW1lbnQgUGVyaW9kIiB4c2k6dHlwZT0iUGFyYW1ldGVyUmVmIi8+DQogICAgICAgICAgICAgICAgICAgICAgICA8L2hpZ2g+DQogICAgICAgICAgICAgICAgICAgICA8L29wZXJhbmQ+DQogICAgICAgICAgICAgICAgICA8L29wZXJhbmQ+DQogICAgICAgICAgICAgICAgICA8b3BlcmFuZCBsb2NhdG9yPSI3Mzo5NC03MzoxMDgiIHhzaTp0eXBlPSJOb3QiPg0KICAgICAgICAgICAgICAgICAgICAgPG9wZXJhbmQgbG9jYXRvcj0iNzM6OTQtNzM6MTA4IiB4c2k6dHlwZT0iSXNOdWxsIj4NCiAgICAgICAgICAgICAgICAgICAgICAgIDxvcGVyYW5kIGxvY2FsSWQ9IjgyIiBsb2NhdG9yPSI3MzoxMjMtNzM6MTQ5IiB4c2k6dHlwZT0iRW5kIj4NCiAgICAgICAgICAgICAgICAgICAgICAgICAgIDxvcGVyYW5kIGxvY2FsSWQ9IjgxIiBsb2NhdG9yPSI3MzoxMzAtNzM6MTQ5IiBuYW1lPSJNZWFzdXJlbWVudCBQZXJpb2QiIHhzaTp0eXBlPSJQYXJhbWV0ZXJSZWYiLz4NCiAgICAgICAgICAgICAgICAgICAgICAgIDwvb3BlcmFuZD4NCiAgICAgICAgICAgICAgICAgICAgIDwvb3BlcmFuZD4NCiAgICAgICAgICAgICAgICAgIDwvb3BlcmFuZD4NCiAgICAgICAgICAgICAgIDwvb3BlcmFuZD4NCiAgICAgICAgICAgIDwvd2hlcmU+DQogICAgICAgICA8L2V4cHJlc3Npb24+DQogICAgICA8L2RlZj4NCiAgICAgIDxkZWYgbG9jYWxJZD0iMTA0IiBsb2NhdG9yPSI3NToxLTc4OjE0MCIgbmFtZT0iQ1QgQ29sb25vZ3JhcGh5IFBlcmZvcm1lZCIgY29udGV4dD0iUGF0aWVudCIgYWNjZXNzTGV2ZWw9IlB1YmxpYyI+DQogICAgICAgICA8YW5ub3RhdGlvbiB4c2k6dHlwZT0iYTpBbm5vdGF0aW9uIj4NCiAgICAgICAgICAgIDxhOnMgcj0iMTA0Ij4NCiAgICAgICAgICAgICAgIDxhOnM+ZGVmaW5lICZxdW90O0NUIENvbG9ub2dyYXBoeSBQZXJmb3JtZWQmcXVvdDs6CiAgPC9hOnM+DQogICAgICAgICAgICAgICA8YTpzIHI9IjEwMyI+DQogICAgICAgICAgICAgICAgICA8YTpzPg0KICAgICAgICAgICAgICAgICAgICAgPGE6cyByPSI4OSI+DQogICAgICAgICAgICAgICAgICAgICAgICA8YTpzIHI9Ijg4Ij4NCiAgICAgICAgICAgICAgICAgICAgICAgICAgIDxhOnMgcj0iODgiPg0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPGE6cz5bUHJvY2VkdXJlOiA8L2E6cz4NCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDxhOnM+DQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8YTpzPiZxdW90O0NUIENvbG9ub2dyYXBoeSZxdW90OzwvYTpzPg0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPC9hOnM+DQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8YTpzPl08L2E6cz4NCiAgICAgICAgICAgICAgICAgICAgICAgICAgIDwvYTpzPg0KICAgICAgICAgICAgICAgICAgICAgICAgPC9hOnM+DQogICAgICAgICAgICAgICAgICAgICAgICA8YTpzPiBDb2xvbm9ncmFwaHk8L2E6cz4NCiAgICAgICAgICAgICAgICAgICAgIDwvYTpzPg0KICAgICAgICAgICAgICAgICAgPC9hOnM+DQogICAgICAgICAgICAgICAgICA8YTpzPgogICAgICAgICAgICAgICAgICAgIAkJPC9hOnM+DQogICAgICAgICAgICAgICAgICA8YTpzIHI9IjEwMiI+DQogICAgICAgICAgICAgICAgICAgICA8YTpzPndoZXJlIDwvYTpzPg0KICAgICAgICAgICAgICAgICAgICAgPGE6cyByPSIxMDIiPg0KICAgICAgICAgICAgICAgICAgICAgICAgPGE6cyByPSI5MyI+DQogICAgICAgICAgICAgICAgICAgICAgICAgICA8YTpzIHI9IjkxIj4NCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDxhOnMgcj0iOTAiPg0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPGE6cz5Db2xvbm9ncmFwaHk8L2E6cz4NCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDwvYTpzPg0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPGE6cz4uPC9hOnM+DQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8YTpzIHI9IjkxIj4NCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDxhOnM+c3RhdHVzPC9hOnM+DQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8L2E6cz4NCiAgICAgICAgICAgICAgICAgICAgICAgICAgIDwvYTpzPg0KICAgICAgICAgICAgICAgICAgICAgICAgICAgPGE6cz4gPSA8L2E6cz4NCiAgICAgICAgICAgICAgICAgICAgICAgICAgIDxhOnMgcj0iOTIiPg0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPGE6cz4nY29tcGxldGVkJzwvYTpzPg0KICAgICAgICAgICAgICAgICAgICAgICAgICAgPC9hOnM+DQogICAgICAgICAgICAgICAgICAgICAgICA8L2E6cz4NCiAgICAgICAgICAgICAgICAgICAgICAgIDxhOnM+CiAgICAgICAgICAgICAgICAgICAgCQkJYW5kIDwvYTpzPg0KICAgICAgICAgICAgICAgICAgICAgICAgPGE6cyByPSIxMDEiPg0KICAgICAgICAgICAgICAgICAgICAgICAgICAgPGE6cyByPSI5NyI+DQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8YTpzIHI9Ijk0Ij4NCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDxhOnM+R2xvYmFsPC9hOnM+DQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8L2E6cz4NCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDxhOnM+LjwvYTpzPg0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPGE6cyByPSI5NyI+DQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8YTpzPiZxdW90O05vcm1hbGl6ZSBJbnRlcnZhbCZxdW90Oyg8L2E6cz4NCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDxhOnMgcj0iOTYiPg0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPGE6cyByPSI5NSI+DQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8YTpzPkNvbG9ub2dyYXBoeTwvYTpzPg0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPC9hOnM+DQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8YTpzPi48L2E6cz4NCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDxhOnMgcj0iOTYiPg0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPGE6cz5wZXJmb3JtZWQ8L2E6cz4NCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDwvYTpzPg0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPC9hOnM+DQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8YTpzPik8L2E6cz4NCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDwvYTpzPg0KICAgICAgICAgICAgICAgICAgICAgICAgICAgPC9hOnM+DQogICAgICAgICAgICAgICAgICAgICAgICAgICA8YTpzPiA8L2E6cz4NCiAgICAgICAgICAgICAgICAgICAgICAgICAgIDxhOnMgcj0iMTAxIj4NCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDxhOnM+ZW5kcyA8L2E6cz4NCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDxhOnMgcj0iMTAwIj4NCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDxhOnM+NSB5ZWFyczwvYTpzPg0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPC9hOnM+DQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8YTpzPiBvciBsZXNzIG9uIG9yIGJlZm9yZTwvYTpzPg0KICAgICAgICAgICAgICAgICAgICAgICAgICAgPC9hOnM+DQogICAgICAgICAgICAgICAgICAgICAgICAgICA8YTpzPiA8L2E6cz4NCiAgICAgICAgICAgICAgICAgICAgICAgICAgIDxhOnMgcj0iOTkiPg0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPGE6cz5lbmQgb2YgPC9hOnM+DQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8YTpzIHI9Ijk4Ij4NCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDxhOnM+JnF1b3Q7TWVhc3VyZW1lbnQgUGVyaW9kJnF1b3Q7PC9hOnM+DQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8L2E6cz4NCiAgICAgICAgICAgICAgICAgICAgICAgICAgIDwvYTpzPg0KICAgICAgICAgICAgICAgICAgICAgICAgPC9hOnM+DQogICAgICAgICAgICAgICAgICAgICA8L2E6cz4NCiAgICAgICAgICAgICAgICAgIDwvYTpzPg0KICAgICAgICAgICAgICAgPC9hOnM+DQogICAgICAgICAgICA8L2E6cz4NCiAgICAgICAgIDwvYW5ub3RhdGlvbj4NCiAgICAgICAgIDxleHByZXNzaW9uIGxvY2FsSWQ9IjEwMyIgbG9jYXRvcj0iNzY6My03ODoxNDAiIHhzaTp0eXBlPSJRdWVyeSI+DQogICAgICAgICAgICA8c291cmNlIGxvY2FsSWQ9Ijg5IiBsb2NhdG9yPSI3NjozLTc2OjQ1IiBhbGlhcz0iQ29sb25vZ3JhcGh5Ij4NCiAgICAgICAgICAgICAgIDxleHByZXNzaW9uIGxvY2FsSWQ9Ijg4IiBsb2NhdG9yPSI3NjozLTc2OjMyIiBkYXRhVHlwZT0iZmhpcjpQcm9jZWR1cmUiIHRlbXBsYXRlSWQ9Imh0dHA6Ly9obDcub3JnL2ZoaXIvU3RydWN0dXJlRGVmaW5pdGlvbi9Qcm9jZWR1cmUiIGNvZGVQcm9wZXJ0eT0iY29kZSIgY29kZUNvbXBhcmF0b3I9ImluIiB4c2k6dHlwZT0iUmV0cmlldmUiPg0KICAgICAgICAgICAgICAgICAgPGNvZGVzIGxvY2F0b3I9Ijc2OjE1LTc2OjMxIiBuYW1lPSJDVCBDb2xvbm9ncmFwaHkiIHhzaTp0eXBlPSJWYWx1ZVNldFJlZiIvPg0KICAgICAgICAgICAgICAgPC9leHByZXNzaW9uPg0KICAgICAgICAgICAgPC9zb3VyY2U+DQogICAgICAgICAgICA8d2hlcmUgbG9jYWxJZD0iMTAyIiBsb2NhdG9yPSI3NzoyMy03ODoxNDAiIHhzaTp0eXBlPSJBbmQiPg0KICAgICAgICAgICAgICAgPG9wZXJhbmQgbG9jYWxJZD0iOTMiIGxvY2F0b3I9Ijc3OjI5LTc3OjYxIiB4c2k6dHlwZT0iRXF1YWwiPg0KICAgICAgICAgICAgICAgICAgPG9wZXJhbmQgbmFtZT0iVG9TdHJpbmciIGxpYnJhcnlOYW1lPSJGSElSSGVscGVycyIgeHNpOnR5cGU9IkZ1bmN0aW9uUmVmIj4NCiAgICAgICAgICAgICAgICAgICAgIDxvcGVyYW5kIGxvY2FsSWQ9IjkxIiBsb2NhdG9yPSI3NzoyOS03Nzo0NyIgcGF0aD0ic3RhdHVzIiBzY29wZT0iQ29sb25vZ3JhcGh5IiB4c2k6dHlwZT0iUHJvcGVydHkiLz4NCiAgICAgICAgICAgICAgICAgIDwvb3BlcmFuZD4NCiAgICAgICAgICAgICAgICAgIDxvcGVyYW5kIGxvY2FsSWQ9IjkyIiBsb2NhdG9yPSI3Nzo1MS03Nzo2MSIgdmFsdWVUeXBlPSJ0OlN0cmluZyIgdmFsdWU9ImNvbXBsZXRlZCIgeHNpOnR5cGU9IkxpdGVyYWwiLz4NCiAgICAgICAgICAgICAgIDwvb3BlcmFuZD4NCiAgICAgICAgICAgICAgIDxvcGVyYW5kIGxvY2FsSWQ9IjEwMSIgbG9jYXRvcj0iNzg6MjgtNzg6MTQwIiB4c2k6dHlwZT0iQW5kIj4NCiAgICAgICAgICAgICAgICAgIDxvcGVyYW5kIGxvY2F0b3I9Ijc4Ojg1LTc4Ojk5IiB4c2k6dHlwZT0iSW4iPg0KICAgICAgICAgICAgICAgICAgICAgPG9wZXJhbmQgbG9jYXRvcj0iNzg6ODAtNzg6ODMiIHhzaTp0eXBlPSJFbmQiPg0KICAgICAgICAgICAgICAgICAgICAgICAgPG9wZXJhbmQgbG9jYWxJZD0iOTciIGxvY2F0b3I9Ijc4OjI4LTc4Ojc4IiBuYW1lPSJOb3JtYWxpemUgSW50ZXJ2YWwiIGxpYnJhcnlOYW1lPSJHbG9iYWwiIHhzaTp0eXBlPSJGdW5jdGlvblJlZiI+DQogICAgICAgICAgICAgICAgICAgICAgICAgICA8b3BlcmFuZCBsb2NhbElkPSI5NiIgbG9jYXRvcj0iNzg6NTYtNzg6NzciIHBhdGg9InBlcmZvcm1lZCIgc2NvcGU9IkNvbG9ub2dyYXBoeSIgeHNpOnR5cGU9IlByb3BlcnR5Ii8+DQogICAgICAgICAgICAgICAgICAgICAgICA8L29wZXJhbmQ+DQogICAgICAgICAgICAgICAgICAgICA8L29wZXJhbmQ+DQogICAgICAgICAgICAgICAgICAgICA8b3BlcmFuZCBsb2NhdG9yPSI3ODo4NS03ODo5OSIgbG93Q2xvc2VkPSJ0cnVlIiBoaWdoQ2xvc2VkPSJ0cnVlIiB4c2k6dHlwZT0iSW50ZXJ2YWwiPg0KICAgICAgICAgICAgICAgICAgICAgICAgPGxvdyBsb2NhdG9yPSI3ODoxMTQtNzg6MTQwIiB4c2k6dHlwZT0iU3VidHJhY3QiPg0KICAgICAgICAgICAgICAgICAgICAgICAgICAgPG9wZXJhbmQgbG9jYWxJZD0iOTkiIGxvY2F0b3I9Ijc4OjExNC03ODoxNDAiIHhzaTp0eXBlPSJFbmQiPg0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPG9wZXJhbmQgbG9jYWxJZD0iOTgiIGxvY2F0b3I9Ijc4OjEyMS03ODoxNDAiIG5hbWU9Ik1lYXN1cmVtZW50IFBlcmlvZCIgeHNpOnR5cGU9IlBhcmFtZXRlclJlZiIvPg0KICAgICAgICAgICAgICAgICAgICAgICAgICAgPC9vcGVyYW5kPg0KICAgICAgICAgICAgICAgICAgICAgICAgICAgPG9wZXJhbmQgbG9jYWxJZD0iMTAwIiBsb2NhdG9yPSI3ODo4NS03ODo5MSIgdmFsdWU9IjUiIHVuaXQ9InllYXJzIiB4c2k6dHlwZT0iUXVhbnRpdHkiLz4NCiAgICAgICAgICAgICAgICAgICAgICAgIDwvbG93Pg0KICAgICAgICAgICAgICAgICAgICAgICAgPGhpZ2ggbG9jYWxJZD0iOTkiIGxvY2F0b3I9Ijc4OjExNC03ODoxNDAiIHhzaTp0eXBlPSJFbmQiPg0KICAgICAgICAgICAgICAgICAgICAgICAgICAgPG9wZXJhbmQgbG9jYWxJZD0iOTgiIGxvY2F0b3I9Ijc4OjEyMS03ODoxNDAiIG5hbWU9Ik1lYXN1cmVtZW50IFBlcmlvZCIgeHNpOnR5cGU9IlBhcmFtZXRlclJlZiIvPg0KICAgICAgICAgICAgICAgICAgICAgICAgPC9oaWdoPg0KICAgICAgICAgICAgICAgICAgICAgPC9vcGVyYW5kPg0KICAgICAgICAgICAgICAgICAgPC9vcGVyYW5kPg0KICAgICAgICAgICAgICAgICAgPG9wZXJhbmQgbG9jYXRvcj0iNzg6ODUtNzg6OTkiIHhzaTp0eXBlPSJOb3QiPg0KICAgICAgICAgICAgICAgICAgICAgPG9wZXJhbmQgbG9jYXRvcj0iNzg6ODUtNzg6OTkiIHhzaTp0eXBlPSJJc051bGwiPg0KICAgICAgICAgICAgICAgICAgICAgICAgPG9wZXJhbmQgbG9jYWxJZD0iOTkiIGxvY2F0b3I9Ijc4OjExNC03ODoxNDAiIHhzaTp0eXBlPSJFbmQiPg0KICAgICAgICAgICAgICAgICAgICAgICAgICAgPG9wZXJhbmQgbG9jYWxJZD0iOTgiIGxvY2F0b3I9Ijc4OjEyMS03ODoxNDAiIG5hbWU9Ik1lYXN1cmVtZW50IFBlcmlvZCIgeHNpOnR5cGU9IlBhcmFtZXRlclJlZiIvPg0KICAgICAgICAgICAgICAgICAgICAgICAgPC9vcGVyYW5kPg0KICAgICAgICAgICAgICAgICAgICAgPC9vcGVyYW5kPg0KICAgICAgICAgICAgICAgICAgPC9vcGVyYW5kPg0KICAgICAgICAgICAgICAgPC9vcGVyYW5kPg0KICAgICAgICAgICAgPC93aGVyZT4NCiAgICAgICAgIDwvZXhwcmVzc2lvbj4NCiAgICAgIDwvZGVmPg0KICAgICAgPGRlZiBsb2NhbElkPSIxMjAiIGxvY2F0b3I9IjgwOjEtODM6MTIzIiBuYW1lPSJUb3RhbCBDb2xlY3RvbXkgUGVyZm9ybWVkIiBjb250ZXh0PSJQYXRpZW50IiBhY2Nlc3NMZXZlbD0iUHVibGljIj4NCiAgICAgICAgIDxhbm5vdGF0aW9uIHhzaTp0eXBlPSJhOkFubm90YXRpb24iPg0KICAgICAgICAgICAgPGE6cyByPSIxMjAiPg0KICAgICAgICAgICAgICAgPGE6cz5kZWZpbmUgJnF1b3Q7VG90YWwgQ29sZWN0b215IFBlcmZvcm1lZCZxdW90OzoKICA8L2E6cz4NCiAgICAgICAgICAgICAgIDxhOnMgcj0iMTE5Ij4NCiAgICAgICAgICAgICAgICAgIDxhOnM+DQogICAgICAgICAgICAgICAgICAgICA8YTpzIHI9IjEwNiI+DQogICAgICAgICAgICAgICAgICAgICAgICA8YTpzIHI9IjEwNSI+DQogICAgICAgICAgICAgICAgICAgICAgICAgICA8YTpzIHI9IjEwNSI+DQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8YTpzPltQcm9jZWR1cmU6IDwvYTpzPg0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPGE6cz4NCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDxhOnM+JnF1b3Q7VG90YWwgQ29sZWN0b215JnF1b3Q7PC9hOnM+DQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8L2E6cz4NCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDxhOnM+XTwvYTpzPg0KICAgICAgICAgICAgICAgICAgICAgICAgICAgPC9hOnM+DQogICAgICAgICAgICAgICAgICAgICAgICA8L2E6cz4NCiAgICAgICAgICAgICAgICAgICAgICAgIDxhOnM+IENvbGVjdG9teTwvYTpzPg0KICAgICAgICAgICAgICAgICAgICAgPC9hOnM+DQogICAgICAgICAgICAgICAgICA8L2E6cz4NCiAgICAgICAgICAgICAgICAgIDxhOnM+CiAgICAgICAgICAgICAgICAgICAgCQk8L2E6cz4NCiAgICAgICAgICAgICAgICAgIDxhOnMgcj0iMTE4Ij4NCiAgICAgICAgICAgICAgICAgICAgIDxhOnM+d2hlcmUgPC9hOnM+DQogICAgICAgICAgICAgICAgICAgICA8YTpzIHI9IjExOCI+DQogICAgICAgICAgICAgICAgICAgICAgICA8YTpzIHI9IjExMCI+DQogICAgICAgICAgICAgICAgICAgICAgICAgICA8YTpzIHI9IjEwOCI+DQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8YTpzIHI9IjEwNyI+DQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8YTpzPkNvbGVjdG9teTwvYTpzPg0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPC9hOnM+DQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8YTpzPi48L2E6cz4NCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDxhOnMgcj0iMTA4Ij4NCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDxhOnM+c3RhdHVzPC9hOnM+DQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8L2E6cz4NCiAgICAgICAgICAgICAgICAgICAgICAgICAgIDwvYTpzPg0KICAgICAgICAgICAgICAgICAgICAgICAgICAgPGE6cz4gPSA8L2E6cz4NCiAgICAgICAgICAgICAgICAgICAgICAgICAgIDxhOnMgcj0iMTA5Ij4NCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDxhOnM+J2NvbXBsZXRlZCc8L2E6cz4NCiAgICAgICAgICAgICAgICAgICAgICAgICAgIDwvYTpzPg0KICAgICAgICAgICAgICAgICAgICAgICAgPC9hOnM+DQogICAgICAgICAgICAgICAgICAgICAgICA8YTpzPgogICAgICAgICAgICAgICAgICAgIAkJCWFuZCA8L2E6cz4NCiAgICAgICAgICAgICAgICAgICAgICAgIDxhOnMgcj0iMTE3Ij4NCiAgICAgICAgICAgICAgICAgICAgICAgICAgIDxhOnMgcj0iMTE0Ij4NCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDxhOnMgcj0iMTExIj4NCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDxhOnM+R2xvYmFsPC9hOnM+DQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8L2E6cz4NCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDxhOnM+LjwvYTpzPg0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPGE6cyByPSIxMTQiPg0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPGE6cz4mcXVvdDtOb3JtYWxpemUgSW50ZXJ2YWwmcXVvdDsoPC9hOnM+DQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8YTpzIHI9IjExMyI+DQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8YTpzIHI9IjExMiI+DQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8YTpzPkNvbGVjdG9teTwvYTpzPg0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPC9hOnM+DQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8YTpzPi48L2E6cz4NCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDxhOnMgcj0iMTEzIj4NCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDxhOnM+cGVyZm9ybWVkPC9hOnM+DQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8L2E6cz4NCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDwvYTpzPg0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPGE6cz4pPC9hOnM+DQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8L2E6cz4NCiAgICAgICAgICAgICAgICAgICAgICAgICAgIDwvYTpzPg0KICAgICAgICAgICAgICAgICAgICAgICAgICAgPGE6cyByPSIxMTciPiBzdGFydHMgb24gb3IgYmVmb3JlIDwvYTpzPg0KICAgICAgICAgICAgICAgICAgICAgICAgICAgPGE6cyByPSIxMTYiPg0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPGE6cz5lbmQgb2YgPC9hOnM+DQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8YTpzIHI9IjExNSI+DQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8YTpzPiZxdW90O01lYXN1cmVtZW50IFBlcmlvZCZxdW90OzwvYTpzPg0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPC9hOnM+DQogICAgICAgICAgICAgICAgICAgICAgICAgICA8L2E6cz4NCiAgICAgICAgICAgICAgICAgICAgICAgIDwvYTpzPg0KICAgICAgICAgICAgICAgICAgICAgPC9hOnM+DQogICAgICAgICAgICAgICAgICA8L2E6cz4NCiAgICAgICAgICAgICAgIDwvYTpzPg0KICAgICAgICAgICAgPC9hOnM+DQogICAgICAgICA8L2Fubm90YXRpb24+DQogICAgICAgICA8ZXhwcmVzc2lvbiBsb2NhbElkPSIxMTkiIGxvY2F0b3I9IjgxOjMtODM6MTIzIiB4c2k6dHlwZT0iUXVlcnkiPg0KICAgICAgICAgICAgPHNvdXJjZSBsb2NhbElkPSIxMDYiIGxvY2F0b3I9IjgxOjMtODE6NDIiIGFsaWFzPSJDb2xlY3RvbXkiPg0KICAgICAgICAgICAgICAgPGV4cHJlc3Npb24gbG9jYWxJZD0iMTA1IiBsb2NhdG9yPSI4MTozLTgxOjMyIiBkYXRhVHlwZT0iZmhpcjpQcm9jZWR1cmUiIHRlbXBsYXRlSWQ9Imh0dHA6Ly9obDcub3JnL2ZoaXIvU3RydWN0dXJlRGVmaW5pdGlvbi9Qcm9jZWR1cmUiIGNvZGVQcm9wZXJ0eT0iY29kZSIgY29kZUNvbXBhcmF0b3I9ImluIiB4c2k6dHlwZT0iUmV0cmlldmUiPg0KICAgICAgICAgICAgICAgICAgPGNvZGVzIGxvY2F0b3I9IjgxOjE1LTgxOjMxIiBuYW1lPSJUb3RhbCBDb2xlY3RvbXkiIHhzaTp0eXBlPSJWYWx1ZVNldFJlZiIvPg0KICAgICAgICAgICAgICAgPC9leHByZXNzaW9uPg0KICAgICAgICAgICAgPC9zb3VyY2U+DQogICAgICAgICAgICA8d2hlcmUgbG9jYWxJZD0iMTE4IiBsb2NhdG9yPSI4MjoyMy04MzoxMjMiIHhzaTp0eXBlPSJBbmQiPg0KICAgICAgICAgICAgICAgPG9wZXJhbmQgbG9jYWxJZD0iMTEwIiBsb2NhdG9yPSI4MjoyOS04Mjo1OCIgeHNpOnR5cGU9IkVxdWFsIj4NCiAgICAgICAgICAgICAgICAgIDxvcGVyYW5kIG5hbWU9IlRvU3RyaW5nIiBsaWJyYXJ5TmFtZT0iRkhJUkhlbHBlcnMiIHhzaTp0eXBlPSJGdW5jdGlvblJlZiI+DQogICAgICAgICAgICAgICAgICAgICA8b3BlcmFuZCBsb2NhbElkPSIxMDgiIGxvY2F0b3I9IjgyOjI5LTgyOjQ0IiBwYXRoPSJzdGF0dXMiIHNjb3BlPSJDb2xlY3RvbXkiIHhzaTp0eXBlPSJQcm9wZXJ0eSIvPg0KICAgICAgICAgICAgICAgICAgPC9vcGVyYW5kPg0KICAgICAgICAgICAgICAgICAgPG9wZXJhbmQgbG9jYWxJZD0iMTA5IiBsb2NhdG9yPSI4Mjo0OC04Mjo1OCIgdmFsdWVUeXBlPSJ0OlN0cmluZyIgdmFsdWU9ImNvbXBsZXRlZCIgeHNpOnR5cGU9IkxpdGVyYWwiLz4NCiAgICAgICAgICAgICAgIDwvb3BlcmFuZD4NCiAgICAgICAgICAgICAgIDxvcGVyYW5kIGxvY2FsSWQ9IjExNyIgbG9jYXRvcj0iODM6MjgtODM6MTIzIiB4c2k6dHlwZT0iU2FtZU9yQmVmb3JlIj4NCiAgICAgICAgICAgICAgICAgIDxvcGVyYW5kIGxvY2F0b3I9IjgzOjc3LTgzOjgyIiB4c2k6dHlwZT0iU3RhcnQiPg0KICAgICAgICAgICAgICAgICAgICAgPG9wZXJhbmQgbG9jYWxJZD0iMTE0IiBsb2NhdG9yPSI4MzoyOC04Mzo3NSIgbmFtZT0iTm9ybWFsaXplIEludGVydmFsIiBsaWJyYXJ5TmFtZT0iR2xvYmFsIiB4c2k6dHlwZT0iRnVuY3Rpb25SZWYiPg0KICAgICAgICAgICAgICAgICAgICAgICAgPG9wZXJhbmQgbG9jYWxJZD0iMTEzIiBsb2NhdG9yPSI4Mzo1Ni04Mzo3NCIgcGF0aD0icGVyZm9ybWVkIiBzY29wZT0iQ29sZWN0b215IiB4c2k6dHlwZT0iUHJvcGVydHkiLz4NCiAgICAgICAgICAgICAgICAgICAgIDwvb3BlcmFuZD4NCiAgICAgICAgICAgICAgICAgIDwvb3BlcmFuZD4NCiAgICAgICAgICAgICAgICAgIDxvcGVyYW5kIGxvY2FsSWQ9IjExNiIgbG9jYXRvcj0iODM6OTctODM6MTIzIiB4c2k6dHlwZT0iRW5kIj4NCiAgICAgICAgICAgICAgICAgICAgIDxvcGVyYW5kIGxvY2FsSWQ9IjExNSIgbG9jYXRvcj0iODM6MTA0LTgzOjEyMyIgbmFtZT0iTWVhc3VyZW1lbnQgUGVyaW9kIiB4c2k6dHlwZT0iUGFyYW1ldGVyUmVmIi8+DQogICAgICAgICAgICAgICAgICA8L29wZXJhbmQ+DQogICAgICAgICAgICAgICA8L29wZXJhbmQ+DQogICAgICAgICAgICA8L3doZXJlPg0KICAgICAgICAgPC9leHByZXNzaW9uPg0KICAgICAgPC9kZWY+DQogICAgICA8ZGVmIGxvY2FsSWQ9IjE0MyIgbG9jYXRvcj0iODU6MS04OToxMTgiIG5hbWU9IkZlY2FsIE9jY3VsdCBCbG9vZCBUZXN0IFBlcmZvcm1lZCIgY29udGV4dD0iUGF0aWVudCIgYWNjZXNzTGV2ZWw9IlB1YmxpYyI+DQogICAgICAgICA8YW5ub3RhdGlvbiB4c2k6dHlwZT0iYTpBbm5vdGF0aW9uIj4NCiAgICAgICAgICAgIDxhOnMgcj0iMTQzIj4NCiAgICAgICAgICAgICAgIDxhOnM+ZGVmaW5lICZxdW90O0ZlY2FsIE9jY3VsdCBCbG9vZCBUZXN0IFBlcmZvcm1lZCZxdW90OzoKICA8L2E6cz4NCiAgICAgICAgICAgICAgIDxhOnMgcj0iMTQyIj4NCiAgICAgICAgICAgICAgICAgIDxhOnM+DQogICAgICAgICAgICAgICAgICAgICA8YTpzIHI9IjEyMiI+DQogICAgICAgICAgICAgICAgICAgICAgICA8YTpzIHI9IjEyMSI+DQogICAgICAgICAgICAgICAgICAgICAgICAgICA8YTpzIHI9IjEyMSI+DQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8YTpzPltPYnNlcnZhdGlvbjogPC9hOnM+DQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8YTpzPg0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPGE6cz4mcXVvdDtGZWNhbCBPY2N1bHQgQmxvb2QgVGVzdCAoRk9CVCkmcXVvdDs8L2E6cz4NCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDwvYTpzPg0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPGE6cz5dPC9hOnM+DQogICAgICAgICAgICAgICAgICAgICAgICAgICA8L2E6cz4NCiAgICAgICAgICAgICAgICAgICAgICAgIDwvYTpzPg0KICAgICAgICAgICAgICAgICAgICAgICAgPGE6cz4gRmVjYWxPY2N1bHRSZXN1bHQ8L2E6cz4NCiAgICAgICAgICAgICAgICAgICAgIDwvYTpzPg0KICAgICAgICAgICAgICAgICAgPC9hOnM+DQogICAgICAgICAgICAgICAgICA8YTpzPgogICAgICAgICAgICAgICAgICAgIAkJPC9hOnM+DQogICAgICAgICAgICAgICAgICA8YTpzIHI9IjE0MSI+DQogICAgICAgICAgICAgICAgICAgICA8YTpzPndoZXJlIDwvYTpzPg0KICAgICAgICAgICAgICAgICAgICAgPGE6cyByPSIxNDEiPg0KICAgICAgICAgICAgICAgICAgICAgICAgPGE6cyByPSIxMzQiPg0KICAgICAgICAgICAgICAgICAgICAgICAgICAgPGE6cyByPSIxMzAiPg0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPGE6cyByPSIxMjQiPg0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPGE6cyByPSIxMjMiPg0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPGE6cz5GZWNhbE9jY3VsdFJlc3VsdDwvYTpzPg0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPC9hOnM+DQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8YTpzPi48L2E6cz4NCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDxhOnMgcj0iMTI0Ij4NCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDxhOnM+c3RhdHVzPC9hOnM+DQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8L2E6cz4NCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDwvYTpzPg0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPGE6cz4gaW4gPC9hOnM+DQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8YTpzIHI9IjEyOSI+DQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8YTpzPns8L2E6cz4NCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDxhOnMgcj0iMTI1Ij4NCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDxhOnM+J2ZpbmFsJzwvYTpzPg0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPC9hOnM+DQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8YTpzPiwgPC9hOnM+DQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8YTpzIHI9IjEyNiI+DQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8YTpzPidhbWVuZGVkJzwvYTpzPg0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPC9hOnM+DQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8YTpzPiwgPC9hOnM+DQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8YTpzIHI9IjEyNyI+DQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8YTpzPidjb3JyZWN0ZWQnPC9hOnM+DQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8L2E6cz4NCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDxhOnM+LCA8L2E6cz4NCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDxhOnMgcj0iMTI4Ij4NCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDxhOnM+J3ByZWxpbWluYXJ5JzwvYTpzPg0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPC9hOnM+DQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8YTpzPn08L2E6cz4NCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDwvYTpzPg0KICAgICAgICAgICAgICAgICAgICAgICAgICAgPC9hOnM+DQogICAgICAgICAgICAgICAgICAgICAgICAgICA8YTpzPgogICAgICAgICAgICAgICAgICAgIAkJCWFuZCA8L2E6cz4NCiAgICAgICAgICAgICAgICAgICAgICAgICAgIDxhOnMgcj0iMTMzIj4NCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDxhOnMgcj0iMTMyIj4NCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDxhOnMgcj0iMTMxIj4NCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDxhOnM+RmVjYWxPY2N1bHRSZXN1bHQ8L2E6cz4NCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDwvYTpzPg0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPGE6cz4uPC9hOnM+DQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8YTpzIHI9IjEzMiI+DQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8YTpzPnZhbHVlPC9hOnM+DQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8L2E6cz4NCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDwvYTpzPg0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPGE6cz4gaXMgbm90IG51bGw8L2E6cz4NCiAgICAgICAgICAgICAgICAgICAgICAgICAgIDwvYTpzPg0KICAgICAgICAgICAgICAgICAgICAgICAgPC9hOnM+DQogICAgICAgICAgICAgICAgICAgICAgICA8YTpzPgogICAgICAgICAgICAgICAgICAgIAkJCWFuZCA8L2E6cz4NCiAgICAgICAgICAgICAgICAgICAgICAgIDxhOnMgcj0iMTQwIj4NCiAgICAgICAgICAgICAgICAgICAgICAgICAgIDxhOnMgcj0iMTM4Ij4NCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDxhOnMgcj0iMTM1Ij4NCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDxhOnM+R2xvYmFsPC9hOnM+DQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8L2E6cz4NCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDxhOnM+LjwvYTpzPg0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPGE6cyByPSIxMzgiPg0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPGE6cz4mcXVvdDtOb3JtYWxpemUgSW50ZXJ2YWwmcXVvdDsoPC9hOnM+DQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8YTpzIHI9IjEzNyI+DQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8YTpzIHI9IjEzNiI+DQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8YTpzPkZlY2FsT2NjdWx0UmVzdWx0PC9hOnM+DQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8L2E6cz4NCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDxhOnM+LjwvYTpzPg0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPGE6cyByPSIxMzciPg0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPGE6cz5lZmZlY3RpdmU8L2E6cz4NCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDwvYTpzPg0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPC9hOnM+DQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8YTpzPik8L2E6cz4NCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDwvYTpzPg0KICAgICAgICAgICAgICAgICAgICAgICAgICAgPC9hOnM+DQogICAgICAgICAgICAgICAgICAgICAgICAgICA8YTpzIHI9IjE0MCI+IGR1cmluZyBkYXkgb2YgPC9hOnM+DQogICAgICAgICAgICAgICAgICAgICAgICAgICA8YTpzIHI9IjEzOSI+DQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8YTpzPiZxdW90O01lYXN1cmVtZW50IFBlcmlvZCZxdW90OzwvYTpzPg0KICAgICAgICAgICAgICAgICAgICAgICAgICAgPC9hOnM+DQogICAgICAgICAgICAgICAgICAgICAgICA8L2E6cz4NCiAgICAgICAgICAgICAgICAgICAgIDwvYTpzPg0KICAgICAgICAgICAgICAgICAgPC9hOnM+DQogICAgICAgICAgICAgICA8L2E6cz4NCiAgICAgICAgICAgIDwvYTpzPg0KICAgICAgICAgPC9hbm5vdGF0aW9uPg0KICAgICAgICAgPGV4cHJlc3Npb24gbG9jYWxJZD0iMTQyIiBsb2NhdG9yPSI4NjozLTg5OjExOCIgeHNpOnR5cGU9IlF1ZXJ5Ij4NCiAgICAgICAgICAgIDxzb3VyY2UgbG9jYWxJZD0iMTIyIiBsb2NhdG9yPSI4NjozLTg2OjY3IiBhbGlhcz0iRmVjYWxPY2N1bHRSZXN1bHQiPg0KICAgICAgICAgICAgICAgPGV4cHJlc3Npb24gbG9jYWxJZD0iMTIxIiBsb2NhdG9yPSI4NjozLTg2OjQ5IiBkYXRhVHlwZT0iZmhpcjpPYnNlcnZhdGlvbiIgdGVtcGxhdGVJZD0iaHR0cDovL2hsNy5vcmcvZmhpci9TdHJ1Y3R1cmVEZWZpbml0aW9uL09ic2VydmF0aW9uIiBjb2RlUHJvcGVydHk9ImNvZGUiIGNvZGVDb21wYXJhdG9yPSJpbiIgeHNpOnR5cGU9IlJldHJpZXZlIj4NCiAgICAgICAgICAgICAgICAgIDxjb2RlcyBsb2NhdG9yPSI4NjoxNy04Njo0OCIgbmFtZT0iRmVjYWwgT2NjdWx0IEJsb29kIFRlc3QgKEZPQlQpIiB4c2k6dHlwZT0iVmFsdWVTZXRSZWYiLz4NCiAgICAgICAgICAgICAgIDwvZXhwcmVzc2lvbj4NCiAgICAgICAgICAgIDwvc291cmNlPg0KICAgICAgICAgICAgPHdoZXJlIGxvY2FsSWQ9IjE0MSIgbG9jYXRvcj0iODc6MjMtODk6MTE4IiB4c2k6dHlwZT0iQW5kIj4NCiAgICAgICAgICAgICAgIDxvcGVyYW5kIGxvY2FsSWQ9IjEzNCIgbG9jYXRvcj0iODc6MjktODg6NjIiIHhzaTp0eXBlPSJBbmQiPg0KICAgICAgICAgICAgICAgICAgPG9wZXJhbmQgbG9jYWxJZD0iMTMwIiBsb2NhdG9yPSI4NzoyOS04NzoxMDQiIHhzaTp0eXBlPSJJbiI+DQogICAgICAgICAgICAgICAgICAgICA8b3BlcmFuZCBuYW1lPSJUb1N0cmluZyIgbGlicmFyeU5hbWU9IkZISVJIZWxwZXJzIiB4c2k6dHlwZT0iRnVuY3Rpb25SZWYiPg0KICAgICAgICAgICAgICAgICAgICAgICAgPG9wZXJhbmQgbG9jYWxJZD0iMTI0IiBsb2NhdG9yPSI4NzoyOS04Nzo1MiIgcGF0aD0ic3RhdHVzIiBzY29wZT0iRmVjYWxPY2N1bHRSZXN1bHQiIHhzaTp0eXBlPSJQcm9wZXJ0eSIvPg0KICAgICAgICAgICAgICAgICAgICAgPC9vcGVyYW5kPg0KICAgICAgICAgICAgICAgICAgICAgPG9wZXJhbmQgbG9jYWxJZD0iMTI5IiBsb2NhdG9yPSI4Nzo1Ny04NzoxMDQiIHhzaTp0eXBlPSJMaXN0Ij4NCiAgICAgICAgICAgICAgICAgICAgICAgIDxlbGVtZW50IGxvY2FsSWQ9IjEyNSIgbG9jYXRvcj0iODc6NTgtODc6NjQiIHZhbHVlVHlwZT0idDpTdHJpbmciIHZhbHVlPSJmaW5hbCIgeHNpOnR5cGU9IkxpdGVyYWwiLz4NCiAgICAgICAgICAgICAgICAgICAgICAgIDxlbGVtZW50IGxvY2FsSWQ9IjEyNiIgbG9jYXRvcj0iODc6NjctODc6NzUiIHZhbHVlVHlwZT0idDpTdHJpbmciIHZhbHVlPSJhbWVuZGVkIiB4c2k6dHlwZT0iTGl0ZXJhbCIvPg0KICAgICAgICAgICAgICAgICAgICAgICAgPGVsZW1lbnQgbG9jYWxJZD0iMTI3IiBsb2NhdG9yPSI4Nzo3OC04Nzo4OCIgdmFsdWVUeXBlPSJ0OlN0cmluZyIgdmFsdWU9ImNvcnJlY3RlZCIgeHNpOnR5cGU9IkxpdGVyYWwiLz4NCiAgICAgICAgICAgICAgICAgICAgICAgIDxlbGVtZW50IGxvY2FsSWQ9IjEyOCIgbG9jYXRvcj0iODc6OTEtODc6MTAzIiB2YWx1ZVR5cGU9InQ6U3RyaW5nIiB2YWx1ZT0icHJlbGltaW5hcnkiIHhzaTp0eXBlPSJMaXRlcmFsIi8+DQogICAgICAgICAgICAgICAgICAgICA8L29wZXJhbmQ+DQogICAgICAgICAgICAgICAgICA8L29wZXJhbmQ+DQogICAgICAgICAgICAgICAgICA8b3BlcmFuZCBsb2NhbElkPSIxMzMiIGxvY2F0b3I9Ijg4OjI4LTg4OjYyIiB4c2k6dHlwZT0iTm90Ij4NCiAgICAgICAgICAgICAgICAgICAgIDxvcGVyYW5kIGxvY2F0b3I9Ijg4OjI4LTg4OjYyIiB4c2k6dHlwZT0iSXNOdWxsIj4NCiAgICAgICAgICAgICAgICAgICAgICAgIDxvcGVyYW5kIGxvY2FsSWQ9IjEzMiIgbG9jYXRvcj0iODg6MjgtODg6NTAiIHBhdGg9InZhbHVlIiBzY29wZT0iRmVjYWxPY2N1bHRSZXN1bHQiIHhzaTp0eXBlPSJQcm9wZXJ0eSIvPg0KICAgICAgICAgICAgICAgICAgICAgPC9vcGVyYW5kPg0KICAgICAgICAgICAgICAgICAgPC9vcGVyYW5kPg0KICAgICAgICAgICAgICAgPC9vcGVyYW5kPg0KICAgICAgICAgICAgICAgPG9wZXJhbmQgbG9jYWxJZD0iMTQwIiBsb2NhdG9yPSI4OToyOC04OToxMTgiIHByZWNpc2lvbj0iRGF5IiB4c2k6dHlwZT0iSW5jbHVkZWRJbiI+DQogICAgICAgICAgICAgICAgICA8b3BlcmFuZCBsb2NhbElkPSIxMzgiIGxvY2F0b3I9Ijg5OjI4LTg5OjgzIiBuYW1lPSJOb3JtYWxpemUgSW50ZXJ2YWwiIGxpYnJhcnlOYW1lPSJHbG9iYWwiIHhzaTp0eXBlPSJGdW5jdGlvblJlZiI+DQogICAgICAgICAgICAgICAgICAgICA8b3BlcmFuZCBsb2NhbElkPSIxMzciIGxvY2F0b3I9Ijg5OjU2LTg5OjgyIiBwYXRoPSJlZmZlY3RpdmUiIHNjb3BlPSJGZWNhbE9jY3VsdFJlc3VsdCIgeHNpOnR5cGU9IlByb3BlcnR5Ii8+DQogICAgICAgICAgICAgICAgICA8L29wZXJhbmQ+DQogICAgICAgICAgICAgICAgICA8b3BlcmFuZCBsb2NhbElkPSIxMzkiIGxvY2F0b3I9Ijg5Ojk5LTg5OjExOCIgbmFtZT0iTWVhc3VyZW1lbnQgUGVyaW9kIiB4c2k6dHlwZT0iUGFyYW1ldGVyUmVmIi8+DQogICAgICAgICAgICAgICA8L29wZXJhbmQ+DQogICAgICAgICAgICA8L3doZXJlPg0KICAgICAgICAgPC9leHByZXNzaW9uPg0KICAgICAgPC9kZWY+DQogICAgICA8ZGVmIGxvY2FsSWQ9IjE2OCIgbG9jYXRvcj0iOTE6MS05NToxNDMiIG5hbWU9IkZlY2FsIEltbXVub2NoZW1pY2FsIFRlc3QgRE5BIiBjb250ZXh0PSJQYXRpZW50IiBhY2Nlc3NMZXZlbD0iUHVibGljIj4NCiAgICAgICAgIDxhbm5vdGF0aW9uIHhzaTp0eXBlPSJhOkFubm90YXRpb24iPg0KICAgICAgICAgICAgPGE6cyByPSIxNjgiPg0KICAgICAgICAgICAgICAgPGE6cz5kZWZpbmUgJnF1b3Q7RmVjYWwgSW1tdW5vY2hlbWljYWwgVGVzdCBETkEmcXVvdDs6CiAgPC9hOnM+DQogICAgICAgICAgICAgICA8YTpzIHI9IjE2NyI+DQogICAgICAgICAgICAgICAgICA8YTpzPg0KICAgICAgICAgICAgICAgICAgICAgPGE6cyByPSIxNDUiPg0KICAgICAgICAgICAgICAgICAgICAgICAgPGE6cyByPSIxNDQiPg0KICAgICAgICAgICAgICAgICAgICAgICAgICAgPGE6cyByPSIxNDQiPg0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPGE6cz5bT2JzZXJ2YXRpb246IDwvYTpzPg0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPGE6cz4NCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDxhOnM+JnF1b3Q7RklUIEROQSZxdW90OzwvYTpzPg0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPC9hOnM+DQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8YTpzPl08L2E6cz4NCiAgICAgICAgICAgICAgICAgICAgICAgICAgIDwvYTpzPg0KICAgICAgICAgICAgICAgICAgICAgICAgPC9hOnM+DQogICAgICAgICAgICAgICAgICAgICAgICA8YTpzPiBGaXRETkE8L2E6cz4NCiAgICAgICAgICAgICAgICAgICAgIDwvYTpzPg0KICAgICAgICAgICAgICAgICAgPC9hOnM+DQogICAgICAgICAgICAgICAgICA8YTpzPgogICAgICAgICAgICAgICAgICAgIAkJPC9hOnM+DQogICAgICAgICAgICAgICAgICA8YTpzIHI9IjE2NiI+DQogICAgICAgICAgICAgICAgICAgICA8YTpzPndoZXJlIDwvYTpzPg0KICAgICAgICAgICAgICAgICAgICAgPGE6cyByPSIxNjYiPg0KICAgICAgICAgICAgICAgICAgICAgICAgPGE6cyByPSIxNTciPg0KICAgICAgICAgICAgICAgICAgICAgICAgICAgPGE6cyByPSIxNTMiPg0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPGE6cyByPSIxNDciPg0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPGE6cyByPSIxNDYiPg0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPGE6cz5GaXRETkE8L2E6cz4NCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDwvYTpzPg0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPGE6cz4uPC9hOnM+DQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8YTpzIHI9IjE0NyI+DQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8YTpzPnN0YXR1czwvYTpzPg0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPC9hOnM+DQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8L2E6cz4NCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDxhOnM+IGluIDwvYTpzPg0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPGE6cyByPSIxNTIiPg0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPGE6cz57PC9hOnM+DQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8YTpzIHI9IjE0OCI+DQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8YTpzPidmaW5hbCc8L2E6cz4NCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDwvYTpzPg0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPGE6cz4sIDwvYTpzPg0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPGE6cyByPSIxNDkiPg0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPGE6cz4nYW1lbmRlZCc8L2E6cz4NCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDwvYTpzPg0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPGE6cz4sIDwvYTpzPg0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPGE6cyByPSIxNTAiPg0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPGE6cz4nY29ycmVjdGVkJzwvYTpzPg0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPC9hOnM+DQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8YTpzPiwgPC9hOnM+DQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8YTpzIHI9IjE1MSI+DQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8YTpzPidwcmVsaW1pbmFyeSc8L2E6cz4NCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDwvYTpzPg0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPGE6cz59PC9hOnM+DQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8L2E6cz4NCiAgICAgICAgICAgICAgICAgICAgICAgICAgIDwvYTpzPg0KICAgICAgICAgICAgICAgICAgICAgICAgICAgPGE6cz4KICAgICAgICAgICAgICAgICAgICAJCQlhbmQgPC9hOnM+DQogICAgICAgICAgICAgICAgICAgICAgICAgICA8YTpzIHI9IjE1NiI+DQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8YTpzIHI9IjE1NSI+DQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8YTpzIHI9IjE1NCI+DQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8YTpzPkZpdEROQTwvYTpzPg0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPC9hOnM+DQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8YTpzPi48L2E6cz4NCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDxhOnMgcj0iMTU1Ij4NCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDxhOnM+dmFsdWU8L2E6cz4NCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDwvYTpzPg0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPC9hOnM+DQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8YTpzPiBpcyBub3QgbnVsbDwvYTpzPg0KICAgICAgICAgICAgICAgICAgICAgICAgICAgPC9hOnM+DQogICAgICAgICAgICAgICAgICAgICAgICA8L2E6cz4NCiAgICAgICAgICAgICAgICAgICAgICAgIDxhOnM+CiAgICAgICAgICAgICAgICAgICAgCQkJYW5kIDwvYTpzPg0KICAgICAgICAgICAgICAgICAgICAgICAgPGE6cyByPSIxNjUiPg0KICAgICAgICAgICAgICAgICAgICAgICAgICAgPGE6cyByPSIxNjEiPg0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPGE6cyByPSIxNTgiPg0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPGE6cz5HbG9iYWw8L2E6cz4NCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDwvYTpzPg0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPGE6cz4uPC9hOnM+DQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8YTpzIHI9IjE2MSI+DQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8YTpzPiZxdW90O05vcm1hbGl6ZSBJbnRlcnZhbCZxdW90Oyg8L2E6cz4NCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDxhOnMgcj0iMTYwIj4NCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDxhOnMgcj0iMTU5Ij4NCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDxhOnM+Rml0RE5BPC9hOnM+DQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8L2E6cz4NCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDxhOnM+LjwvYTpzPg0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPGE6cyByPSIxNjAiPg0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPGE6cz5lZmZlY3RpdmU8L2E6cz4NCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDwvYTpzPg0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPC9hOnM+DQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8YTpzPik8L2E6cz4NCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDwvYTpzPg0KICAgICAgICAgICAgICAgICAgICAgICAgICAgPC9hOnM+DQogICAgICAgICAgICAgICAgICAgICAgICAgICA8YTpzPiA8L2E6cz4NCiAgICAgICAgICAgICAgICAgICAgICAgICAgIDxhOnMgcj0iMTY1Ij4NCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDxhOnM+b2NjdXJzIDwvYTpzPg0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPGE6cyByPSIxNjQiPg0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPGE6cz4zIHllYXJzPC9hOnM+DQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8L2E6cz4NCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDxhOnM+IG9yIGxlc3Mgb24gb3IgYmVmb3JlIGRheSBvZjwvYTpzPg0KICAgICAgICAgICAgICAgICAgICAgICAgICAgPC9hOnM+DQogICAgICAgICAgICAgICAgICAgICAgICAgICA8YTpzPiA8L2E6cz4NCiAgICAgICAgICAgICAgICAgICAgICAgICAgIDxhOnMgcj0iMTYzIj4NCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDxhOnM+ZW5kIG9mIDwvYTpzPg0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPGE6cyByPSIxNjIiPg0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPGE6cz4mcXVvdDtNZWFzdXJlbWVudCBQZXJpb2QmcXVvdDs8L2E6cz4NCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDwvYTpzPg0KICAgICAgICAgICAgICAgICAgICAgICAgICAgPC9hOnM+DQogICAgICAgICAgICAgICAgICAgICAgICA8L2E6cz4NCiAgICAgICAgICAgICAgICAgICAgIDwvYTpzPg0KICAgICAgICAgICAgICAgICAgPC9hOnM+DQogICAgICAgICAgICAgICA8L2E6cz4NCiAgICAgICAgICAgIDwvYTpzPg0KICAgICAgICAgPC9hbm5vdGF0aW9uPg0KICAgICAgICAgPGV4cHJlc3Npb24gbG9jYWxJZD0iMTY3IiBsb2NhdG9yPSI5MjozLTk1OjE0MyIgeHNpOnR5cGU9IlF1ZXJ5Ij4NCiAgICAgICAgICAgIDxzb3VyY2UgbG9jYWxJZD0iMTQ1IiBsb2NhdG9yPSI5MjozLTkyOjMzIiBhbGlhcz0iRml0RE5BIj4NCiAgICAgICAgICAgICAgIDxleHByZXNzaW9uIGxvY2FsSWQ9IjE0NCIgbG9jYXRvcj0iOTI6My05MjoyNiIgZGF0YVR5cGU9ImZoaXI6T2JzZXJ2YXRpb24iIHRlbXBsYXRlSWQ9Imh0dHA6Ly9obDcub3JnL2ZoaXIvU3RydWN0dXJlRGVmaW5pdGlvbi9PYnNlcnZhdGlvbiIgY29kZVByb3BlcnR5PSJjb2RlIiBjb2RlQ29tcGFyYXRvcj0iaW4iIHhzaTp0eXBlPSJSZXRyaWV2ZSI+DQogICAgICAgICAgICAgICAgICA8Y29kZXMgbG9jYXRvcj0iOTI6MTctOTI6MjUiIG5hbWU9IkZJVCBETkEiIHhzaTp0eXBlPSJWYWx1ZVNldFJlZiIvPg0KICAgICAgICAgICAgICAgPC9leHByZXNzaW9uPg0KICAgICAgICAgICAgPC9zb3VyY2U+DQogICAgICAgICAgICA8d2hlcmUgbG9jYWxJZD0iMTY2IiBsb2NhdG9yPSI5MzoyMy05NToxNDMiIHhzaTp0eXBlPSJBbmQiPg0KICAgICAgICAgICAgICAgPG9wZXJhbmQgbG9jYWxJZD0iMTU3IiBsb2NhdG9yPSI5MzoyOS05NDo1MSIgeHNpOnR5cGU9IkFuZCI+DQogICAgICAgICAgICAgICAgICA8b3BlcmFuZCBsb2NhbElkPSIxNTMiIGxvY2F0b3I9IjkzOjI5LTkzOjkzIiB4c2k6dHlwZT0iSW4iPg0KICAgICAgICAgICAgICAgICAgICAgPG9wZXJhbmQgbmFtZT0iVG9TdHJpbmciIGxpYnJhcnlOYW1lPSJGSElSSGVscGVycyIgeHNpOnR5cGU9IkZ1bmN0aW9uUmVmIj4NCiAgICAgICAgICAgICAgICAgICAgICAgIDxvcGVyYW5kIGxvY2FsSWQ9IjE0NyIgbG9jYXRvcj0iOTM6MjktOTM6NDEiIHBhdGg9InN0YXR1cyIgc2NvcGU9IkZpdEROQSIgeHNpOnR5cGU9IlByb3BlcnR5Ii8+DQogICAgICAgICAgICAgICAgICAgICA8L29wZXJhbmQ+DQogICAgICAgICAgICAgICAgICAgICA8b3BlcmFuZCBsb2NhbElkPSIxNTIiIGxvY2F0b3I9IjkzOjQ2LTkzOjkzIiB4c2k6dHlwZT0iTGlzdCI+DQogICAgICAgICAgICAgICAgICAgICAgICA8ZWxlbWVudCBsb2NhbElkPSIxNDgiIGxvY2F0b3I9IjkzOjQ3LTkzOjUzIiB2YWx1ZVR5cGU9InQ6U3RyaW5nIiB2YWx1ZT0iZmluYWwiIHhzaTp0eXBlPSJMaXRlcmFsIi8+DQogICAgICAgICAgICAgICAgICAgICAgICA8ZWxlbWVudCBsb2NhbElkPSIxNDkiIGxvY2F0b3I9IjkzOjU2LTkzOjY0IiB2YWx1ZVR5cGU9InQ6U3RyaW5nIiB2YWx1ZT0iYW1lbmRlZCIgeHNpOnR5cGU9IkxpdGVyYWwiLz4NCiAgICAgICAgICAgICAgICAgICAgICAgIDxlbGVtZW50IGxvY2FsSWQ9IjE1MCIgbG9jYXRvcj0iOTM6NjctOTM6NzciIHZhbHVlVHlwZT0idDpTdHJpbmciIHZhbHVlPSJjb3JyZWN0ZWQiIHhzaTp0eXBlPSJMaXRlcmFsIi8+DQogICAgICAgICAgICAgICAgICAgICAgICA8ZWxlbWVudCBsb2NhbElkPSIxNTEiIGxvY2F0b3I9IjkzOjgwLTkzOjkyIiB2YWx1ZVR5cGU9InQ6U3RyaW5nIiB2YWx1ZT0icHJlbGltaW5hcnkiIHhzaTp0eXBlPSJMaXRlcmFsIi8+DQogICAgICAgICAgICAgICAgICAgICA8L29wZXJhbmQ+DQogICAgICAgICAgICAgICAgICA8L29wZXJhbmQ+DQogICAgICAgICAgICAgICAgICA8b3BlcmFuZCBsb2NhbElkPSIxNTYiIGxvY2F0b3I9Ijk0OjI4LTk0OjUxIiB4c2k6dHlwZT0iTm90Ij4NCiAgICAgICAgICAgICAgICAgICAgIDxvcGVyYW5kIGxvY2F0b3I9Ijk0OjI4LTk0OjUxIiB4c2k6dHlwZT0iSXNOdWxsIj4NCiAgICAgICAgICAgICAgICAgICAgICAgIDxvcGVyYW5kIGxvY2FsSWQ9IjE1NSIgbG9jYXRvcj0iOTQ6MjgtOTQ6MzkiIHBhdGg9InZhbHVlIiBzY29wZT0iRml0RE5BIiB4c2k6dHlwZT0iUHJvcGVydHkiLz4NCiAgICAgICAgICAgICAgICAgICAgIDwvb3BlcmFuZD4NCiAgICAgICAgICAgICAgICAgIDwvb3BlcmFuZD4NCiAgICAgICAgICAgICAgIDwvb3BlcmFuZD4NCiAgICAgICAgICAgICAgIDxvcGVyYW5kIGxvY2FsSWQ9IjE2NSIgbG9jYXRvcj0iOTU6MjgtOTU6MTQzIiB4c2k6dHlwZT0iQW5kIj4NCiAgICAgICAgICAgICAgICAgIDxvcGVyYW5kIGxvY2F0b3I9Ijk1OjgxLTk1Ojk1IiBwcmVjaXNpb249IkRheSIgeHNpOnR5cGU9IkluIj4NCiAgICAgICAgICAgICAgICAgICAgIDxvcGVyYW5kIGxvY2F0b3I9Ijk1OjM1LTk1OjcyIiB4c2k6dHlwZT0iRW5kIj4NCiAgICAgICAgICAgICAgICAgICAgICAgIDxvcGVyYW5kIGxvY2FsSWQ9IjE2MSIgbG9jYXRvcj0iOTU6MjgtOTU6NzIiIG5hbWU9Ik5vcm1hbGl6ZSBJbnRlcnZhbCIgbGlicmFyeU5hbWU9Ikdsb2JhbCIgeHNpOnR5cGU9IkZ1bmN0aW9uUmVmIj4NCiAgICAgICAgICAgICAgICAgICAgICAgICAgIDxvcGVyYW5kIGxvY2FsSWQ9IjE2MCIgbG9jYXRvcj0iOTU6NTYtOTU6NzEiIHBhdGg9ImVmZmVjdGl2ZSIgc2NvcGU9IkZpdEROQSIgeHNpOnR5cGU9IlByb3BlcnR5Ii8+DQogICAgICAgICAgICAgICAgICAgICAgICA8L29wZXJhbmQ+DQogICAgICAgICAgICAgICAgICAgICA8L29wZXJhbmQ+DQogICAgICAgICAgICAgICAgICAgICA8b3BlcmFuZCBsb2NhdG9yPSI5NTo4MS05NTo5NSIgbG93Q2xvc2VkPSJ0cnVlIiBoaWdoQ2xvc2VkPSJ0cnVlIiB4c2k6dHlwZT0iSW50ZXJ2YWwiPg0KICAgICAgICAgICAgICAgICAgICAgICAgPGxvdyBsb2NhdG9yPSI5NToxMTctOTU6MTQzIiB4c2k6dHlwZT0iU3VidHJhY3QiPg0KICAgICAgICAgICAgICAgICAgICAgICAgICAgPG9wZXJhbmQgbG9jYWxJZD0iMTYzIiBsb2NhdG9yPSI5NToxMTctOTU6MTQzIiB4c2k6dHlwZT0iRW5kIj4NCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDxvcGVyYW5kIGxvY2FsSWQ9IjE2MiIgbG9jYXRvcj0iOTU6MTI0LTk1OjE0MyIgbmFtZT0iTWVhc3VyZW1lbnQgUGVyaW9kIiB4c2k6dHlwZT0iUGFyYW1ldGVyUmVmIi8+DQogICAgICAgICAgICAgICAgICAgICAgICAgICA8L29wZXJhbmQ+DQogICAgICAgICAgICAgICAgICAgICAgICAgICA8b3BlcmFuZCBsb2NhbElkPSIxNjQiIGxvY2F0b3I9Ijk1OjgxLTk1Ojg3IiB2YWx1ZT0iMyIgdW5pdD0ieWVhcnMiIHhzaTp0eXBlPSJRdWFudGl0eSIvPg0KICAgICAgICAgICAgICAgICAgICAgICAgPC9sb3c+DQogICAgICAgICAgICAgICAgICAgICAgICA8aGlnaCBsb2NhbElkPSIxNjMiIGxvY2F0b3I9Ijk1OjExNy05NToxNDMiIHhzaTp0eXBlPSJFbmQiPg0KICAgICAgICAgICAgICAgICAgICAgICAgICAgPG9wZXJhbmQgbG9jYWxJZD0iMTYyIiBsb2NhdG9yPSI5NToxMjQtOTU6MTQzIiBuYW1lPSJNZWFzdXJlbWVudCBQZXJpb2QiIHhzaTp0eXBlPSJQYXJhbWV0ZXJSZWYiLz4NCiAgICAgICAgICAgICAgICAgICAgICAgIDwvaGlnaD4NCiAgICAgICAgICAgICAgICAgICAgIDwvb3BlcmFuZD4NCiAgICAgICAgICAgICAgICAgIDwvb3BlcmFuZD4NCiAgICAgICAgICAgICAgICAgIDxvcGVyYW5kIGxvY2F0b3I9Ijk1OjgxLTk1Ojk1IiB4c2k6dHlwZT0iTm90Ij4NCiAgICAgICAgICAgICAgICAgICAgIDxvcGVyYW5kIGxvY2F0b3I9Ijk1OjgxLTk1Ojk1IiB4c2k6dHlwZT0iSXNOdWxsIj4NCiAgICAgICAgICAgICAgICAgICAgICAgIDxvcGVyYW5kIGxvY2FsSWQ9IjE2MyIgbG9jYXRvcj0iOTU6MTE3LTk1OjE0MyIgeHNpOnR5cGU9IkVuZCI+DQogICAgICAgICAgICAgICAgICAgICAgICAgICA8b3BlcmFuZCBsb2NhbElkPSIxNjIiIGxvY2F0b3I9Ijk1OjEyNC05NToxNDMiIG5hbWU9Ik1lYXN1cmVtZW50IFBlcmlvZCIgeHNpOnR5cGU9IlBhcmFtZXRlclJlZiIvPg0KICAgICAgICAgICAgICAgICAgICAgICAgPC9vcGVyYW5kPg0KICAgICAgICAgICAgICAgICAgICAgPC9vcGVyYW5kPg0KICAgICAgICAgICAgICAgICAgPC9vcGVyYW5kPg0KICAgICAgICAgICAgICAgPC9vcGVyYW5kPg0KICAgICAgICAgICAgPC93aGVyZT4NCiAgICAgICAgIDwvZXhwcmVzc2lvbj4NCiAgICAgIDwvZGVmPg0KICAgICAgPGRlZiBsb2NhbElkPSIxODUiIGxvY2F0b3I9Ijk3OjEtMTAwOjE0MSIgbmFtZT0iQ29sb25vc2NvcHkgUGVyZm9ybWVkIiBjb250ZXh0PSJQYXRpZW50IiBhY2Nlc3NMZXZlbD0iUHVibGljIj4NCiAgICAgICAgIDxhbm5vdGF0aW9uIHhzaTp0eXBlPSJhOkFubm90YXRpb24iPg0KICAgICAgICAgICAgPGE6cyByPSIxODUiPg0KICAgICAgICAgICAgICAgPGE6cz5kZWZpbmUgJnF1b3Q7Q29sb25vc2NvcHkgUGVyZm9ybWVkJnF1b3Q7OgogIDwvYTpzPg0KICAgICAgICAgICAgICAgPGE6cyByPSIxODQiPg0KICAgICAgICAgICAgICAgICAgPGE6cz4NCiAgICAgICAgICAgICAgICAgICAgIDxhOnMgcj0iMTcwIj4NCiAgICAgICAgICAgICAgICAgICAgICAgIDxhOnMgcj0iMTY5Ij4NCiAgICAgICAgICAgICAgICAgICAgICAgICAgIDxhOnMgcj0iMTY5Ij4NCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDxhOnM+W1Byb2NlZHVyZTogPC9hOnM+DQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8YTpzPg0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPGE6cz4mcXVvdDtDb2xvbm9zY29weSZxdW90OzwvYTpzPg0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPC9hOnM+DQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8YTpzPl08L2E6cz4NCiAgICAgICAgICAgICAgICAgICAgICAgICAgIDwvYTpzPg0KICAgICAgICAgICAgICAgICAgICAgICAgPC9hOnM+DQogICAgICAgICAgICAgICAgICAgICAgICA8YTpzPiBDb2xvbm9zY29weTwvYTpzPg0KICAgICAgICAgICAgICAgICAgICAgPC9hOnM+DQogICAgICAgICAgICAgICAgICA8L2E6cz4NCiAgICAgICAgICAgICAgICAgIDxhOnM+CiAgICAgICAgICAgICAgICAgICAgCQk8L2E6cz4NCiAgICAgICAgICAgICAgICAgIDxhOnMgcj0iMTgzIj4NCiAgICAgICAgICAgICAgICAgICAgIDxhOnM+d2hlcmUgPC9hOnM+DQogICAgICAgICAgICAgICAgICAgICA8YTpzIHI9IjE4MyI+DQogICAgICAgICAgICAgICAgICAgICAgICA8YTpzIHI9IjE3NCI+DQogICAgICAgICAgICAgICAgICAgICAgICAgICA8YTpzIHI9IjE3MiI+DQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8YTpzIHI9IjE3MSI+DQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8YTpzPkNvbG9ub3Njb3B5PC9hOnM+DQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8L2E6cz4NCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDxhOnM+LjwvYTpzPg0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPGE6cyByPSIxNzIiPg0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPGE6cz5zdGF0dXM8L2E6cz4NCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDwvYTpzPg0KICAgICAgICAgICAgICAgICAgICAgICAgICAgPC9hOnM+DQogICAgICAgICAgICAgICAgICAgICAgICAgICA8YTpzPiA9IDwvYTpzPg0KICAgICAgICAgICAgICAgICAgICAgICAgICAgPGE6cyByPSIxNzMiPg0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPGE6cz4nY29tcGxldGVkJzwvYTpzPg0KICAgICAgICAgICAgICAgICAgICAgICAgICAgPC9hOnM+DQogICAgICAgICAgICAgICAgICAgICAgICA8L2E6cz4NCiAgICAgICAgICAgICAgICAgICAgICAgIDxhOnM+CiAgICAgICAgICAgICAgICAgICAgCQkgIGFuZCA8L2E6cz4NCiAgICAgICAgICAgICAgICAgICAgICAgIDxhOnMgcj0iMTgyIj4NCiAgICAgICAgICAgICAgICAgICAgICAgICAgIDxhOnMgcj0iMTc4Ij4NCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDxhOnMgcj0iMTc1Ij4NCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDxhOnM+R2xvYmFsPC9hOnM+DQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8L2E6cz4NCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDxhOnM+LjwvYTpzPg0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPGE6cyByPSIxNzgiPg0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPGE6cz4mcXVvdDtOb3JtYWxpemUgSW50ZXJ2YWwmcXVvdDsoPC9hOnM+DQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8YTpzIHI9IjE3NyI+DQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8YTpzIHI9IjE3NiI+DQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8YTpzPkNvbG9ub3Njb3B5PC9hOnM+DQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8L2E6cz4NCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDxhOnM+LjwvYTpzPg0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPGE6cyByPSIxNzciPg0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPGE6cz5wZXJmb3JtZWQ8L2E6cz4NCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDwvYTpzPg0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPC9hOnM+DQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8YTpzPik8L2E6cz4NCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDwvYTpzPg0KICAgICAgICAgICAgICAgICAgICAgICAgICAgPC9hOnM+DQogICAgICAgICAgICAgICAgICAgICAgICAgICA8YTpzPiA8L2E6cz4NCiAgICAgICAgICAgICAgICAgICAgICAgICAgIDxhOnMgcj0iMTgyIj4NCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDxhOnM+ZW5kcyA8L2E6cz4NCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDxhOnMgcj0iMTgxIj4NCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDxhOnM+MTAgeWVhcnM8L2E6cz4NCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDwvYTpzPg0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPGE6cz4gb3IgbGVzcyBvbiBvciBiZWZvcmU8L2E6cz4NCiAgICAgICAgICAgICAgICAgICAgICAgICAgIDwvYTpzPg0KICAgICAgICAgICAgICAgICAgICAgICAgICAgPGE6cz4gPC9hOnM+DQogICAgICAgICAgICAgICAgICAgICAgICAgICA8YTpzIHI9IjE4MCI+DQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8YTpzPmVuZCBvZiA8L2E6cz4NCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDxhOnMgcj0iMTc5Ij4NCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDxhOnM+JnF1b3Q7TWVhc3VyZW1lbnQgUGVyaW9kJnF1b3Q7PC9hOnM+DQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8L2E6cz4NCiAgICAgICAgICAgICAgICAgICAgICAgICAgIDwvYTpzPg0KICAgICAgICAgICAgICAgICAgICAgICAgPC9hOnM+DQogICAgICAgICAgICAgICAgICAgICA8L2E6cz4NCiAgICAgICAgICAgICAgICAgIDwvYTpzPg0KICAgICAgICAgICAgICAgPC9hOnM+DQogICAgICAgICAgICA8L2E6cz4NCiAgICAgICAgIDwvYW5ub3RhdGlvbj4NCiAgICAgICAgIDxleHByZXNzaW9uIGxvY2FsSWQ9IjE4NCIgbG9jYXRvcj0iOTg6My0xMDA6MTQxIiB4c2k6dHlwZT0iUXVlcnkiPg0KICAgICAgICAgICAgPHNvdXJjZSBsb2NhbElkPSIxNzAiIGxvY2F0b3I9Ijk4OjMtOTg6NDAiIGFsaWFzPSJDb2xvbm9zY29weSI+DQogICAgICAgICAgICAgICA8ZXhwcmVzc2lvbiBsb2NhbElkPSIxNjkiIGxvY2F0b3I9Ijk4OjMtOTg6MjgiIGRhdGFUeXBlPSJmaGlyOlByb2NlZHVyZSIgdGVtcGxhdGVJZD0iaHR0cDovL2hsNy5vcmcvZmhpci9TdHJ1Y3R1cmVEZWZpbml0aW9uL1Byb2NlZHVyZSIgY29kZVByb3BlcnR5PSJjb2RlIiBjb2RlQ29tcGFyYXRvcj0iaW4iIHhzaTp0eXBlPSJSZXRyaWV2ZSI+DQogICAgICAgICAgICAgICAgICA8Y29kZXMgbG9jYXRvcj0iOTg6MTUtOTg6MjciIG5hbWU9IkNvbG9ub3Njb3B5IiB4c2k6dHlwZT0iVmFsdWVTZXRSZWYiLz4NCiAgICAgICAgICAgICAgIDwvZXhwcmVzc2lvbj4NCiAgICAgICAgICAgIDwvc291cmNlPg0KICAgICAgICAgICAgPHdoZXJlIGxvY2FsSWQ9IjE4MyIgbG9jYXRvcj0iOTk6MjMtMTAwOjE0MSIgeHNpOnR5cGU9IkFuZCI+DQogICAgICAgICAgICAgICA8b3BlcmFuZCBsb2NhbElkPSIxNzQiIGxvY2F0b3I9Ijk5OjI5LTk5OjYwIiB4c2k6dHlwZT0iRXF1YWwiPg0KICAgICAgICAgICAgICAgICAgPG9wZXJhbmQgbmFtZT0iVG9TdHJpbmciIGxpYnJhcnlOYW1lPSJGSElSSGVscGVycyIgeHNpOnR5cGU9IkZ1bmN0aW9uUmVmIj4NCiAgICAgICAgICAgICAgICAgICAgIDxvcGVyYW5kIGxvY2FsSWQ9IjE3MiIgbG9jYXRvcj0iOTk6MjktOTk6NDYiIHBhdGg9InN0YXR1cyIgc2NvcGU9IkNvbG9ub3Njb3B5IiB4c2k6dHlwZT0iUHJvcGVydHkiLz4NCiAgICAgICAgICAgICAgICAgIDwvb3BlcmFuZD4NCiAgICAgICAgICAgICAgICAgIDxvcGVyYW5kIGxvY2FsSWQ9IjE3MyIgbG9jYXRvcj0iOTk6NTAtOTk6NjAiIHZhbHVlVHlwZT0idDpTdHJpbmciIHZhbHVlPSJjb21wbGV0ZWQiIHhzaTp0eXBlPSJMaXRlcmFsIi8+DQogICAgICAgICAgICAgICA8L29wZXJhbmQ+DQogICAgICAgICAgICAgICA8b3BlcmFuZCBsb2NhbElkPSIxODIiIGxvY2F0b3I9IjEwMDoyOS0xMDA6MTQxIiB4c2k6dHlwZT0iQW5kIj4NCiAgICAgICAgICAgICAgICAgIDxvcGVyYW5kIGxvY2F0b3I9IjEwMDo4NS0xMDA6MTAwIiB4c2k6dHlwZT0iSW4iPg0KICAgICAgICAgICAgICAgICAgICAgPG9wZXJhbmQgbG9jYXRvcj0iMTAwOjgwLTEwMDo4MyIgeHNpOnR5cGU9IkVuZCI+DQogICAgICAgICAgICAgICAgICAgICAgICA8b3BlcmFuZCBsb2NhbElkPSIxNzgiIGxvY2F0b3I9IjEwMDoyOS0xMDA6NzgiIG5hbWU9Ik5vcm1hbGl6ZSBJbnRlcnZhbCIgbGlicmFyeU5hbWU9Ikdsb2JhbCIgeHNpOnR5cGU9IkZ1bmN0aW9uUmVmIj4NCiAgICAgICAgICAgICAgICAgICAgICAgICAgIDxvcGVyYW5kIGxvY2FsSWQ9IjE3NyIgbG9jYXRvcj0iMTAwOjU3LTEwMDo3NyIgcGF0aD0icGVyZm9ybWVkIiBzY29wZT0iQ29sb25vc2NvcHkiIHhzaTp0eXBlPSJQcm9wZXJ0eSIvPg0KICAgICAgICAgICAgICAgICAgICAgICAgPC9vcGVyYW5kPg0KICAgICAgICAgICAgICAgICAgICAgPC9vcGVyYW5kPg0KICAgICAgICAgICAgICAgICAgICAgPG9wZXJhbmQgbG9jYXRvcj0iMTAwOjg1LTEwMDoxMDAiIGxvd0Nsb3NlZD0idHJ1ZSIgaGlnaENsb3NlZD0idHJ1ZSIgeHNpOnR5cGU9IkludGVydmFsIj4NCiAgICAgICAgICAgICAgICAgICAgICAgIDxsb3cgbG9jYXRvcj0iMTAwOjExNS0xMDA6MTQxIiB4c2k6dHlwZT0iU3VidHJhY3QiPg0KICAgICAgICAgICAgICAgICAgICAgICAgICAgPG9wZXJhbmQgbG9jYWxJZD0iMTgwIiBsb2NhdG9yPSIxMDA6MTE1LTEwMDoxNDEiIHhzaTp0eXBlPSJFbmQiPg0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPG9wZXJhbmQgbG9jYWxJZD0iMTc5IiBsb2NhdG9yPSIxMDA6MTIyLTEwMDoxNDEiIG5hbWU9Ik1lYXN1cmVtZW50IFBlcmlvZCIgeHNpOnR5cGU9IlBhcmFtZXRlclJlZiIvPg0KICAgICAgICAgICAgICAgICAgICAgICAgICAgPC9vcGVyYW5kPg0KICAgICAgICAgICAgICAgICAgICAgICAgICAgPG9wZXJhbmQgbG9jYWxJZD0iMTgxIiBsb2NhdG9yPSIxMDA6ODUtMTAwOjkyIiB2YWx1ZT0iMTAiIHVuaXQ9InllYXJzIiB4c2k6dHlwZT0iUXVhbnRpdHkiLz4NCiAgICAgICAgICAgICAgICAgICAgICAgIDwvbG93Pg0KICAgICAgICAgICAgICAgICAgICAgICAgPGhpZ2ggbG9jYWxJZD0iMTgwIiBsb2NhdG9yPSIxMDA6MTE1LTEwMDoxNDEiIHhzaTp0eXBlPSJFbmQiPg0KICAgICAgICAgICAgICAgICAgICAgICAgICAgPG9wZXJhbmQgbG9jYWxJZD0iMTc5IiBsb2NhdG9yPSIxMDA6MTIyLTEwMDoxNDEiIG5hbWU9Ik1lYXN1cmVtZW50IFBlcmlvZCIgeHNpOnR5cGU9IlBhcmFtZXRlclJlZiIvPg0KICAgICAgICAgICAgICAgICAgICAgICAgPC9oaWdoPg0KICAgICAgICAgICAgICAgICAgICAgPC9vcGVyYW5kPg0KICAgICAgICAgICAgICAgICAgPC9vcGVyYW5kPg0KICAgICAgICAgICAgICAgICAgPG9wZXJhbmQgbG9jYXRvcj0iMTAwOjg1LTEwMDoxMDAiIHhzaTp0eXBlPSJOb3QiPg0KICAgICAgICAgICAgICAgICAgICAgPG9wZXJhbmQgbG9jYXRvcj0iMTAwOjg1LTEwMDoxMDAiIHhzaTp0eXBlPSJJc051bGwiPg0KICAgICAgICAgICAgICAgICAgICAgICAgPG9wZXJhbmQgbG9jYWxJZD0iMTgwIiBsb2NhdG9yPSIxMDA6MTE1LTEwMDoxNDEiIHhzaTp0eXBlPSJFbmQiPg0KICAgICAgICAgICAgICAgICAgICAgICAgICAgPG9wZXJhbmQgbG9jYWxJZD0iMTc5IiBsb2NhdG9yPSIxMDA6MTIyLTEwMDoxNDEiIG5hbWU9Ik1lYXN1cmVtZW50IFBlcmlvZCIgeHNpOnR5cGU9IlBhcmFtZXRlclJlZiIvPg0KICAgICAgICAgICAgICAgICAgICAgICAgPC9vcGVyYW5kPg0KICAgICAgICAgICAgICAgICAgICAgPC9vcGVyYW5kPg0KICAgICAgICAgICAgICAgICAgPC9vcGVyYW5kPg0KICAgICAgICAgICAgICAgPC9vcGVyYW5kPg0KICAgICAgICAgICAgPC93aGVyZT4NCiAgICAgICAgIDwvZXhwcmVzc2lvbj4NCiAgICAgIDwvZGVmPg0KICAgICAgPGRlZiBsb2NhbElkPSIyMDAiIGxvY2F0b3I9IjEwMjoxLTEwNzo1OSIgbmFtZT0iTnVtZXJhdG9yIiBjb250ZXh0PSJQYXRpZW50IiBhY2Nlc3NMZXZlbD0iUHVibGljIj4NCiAgICAgICAgIDxhbm5vdGF0aW9uIHhzaTp0eXBlPSJhOkFubm90YXRpb24iPg0KICAgICAgICAgICAgPGE6cyByPSIyMDAiPg0KICAgICAgICAgICAgICAgPGE6cz5kZWZpbmUgJnF1b3Q7TnVtZXJhdG9yJnF1b3Q7OgogIDwvYTpzPg0KICAgICAgICAgICAgICAgPGE6cyByPSIxOTkiPg0KICAgICAgICAgICAgICAgICAgPGE6cyByPSIxOTYiPg0KICAgICAgICAgICAgICAgICAgICAgPGE6cyByPSIxOTMiPg0KICAgICAgICAgICAgICAgICAgICAgICAgPGE6cyByPSIxOTAiPg0KICAgICAgICAgICAgICAgICAgICAgICAgICAgPGE6cyByPSIxODciPg0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPGE6cz5leGlzdHMgPC9hOnM+DQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8YTpzIHI9IjE4NiI+DQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8YTpzPiZxdW90O0NvbG9ub3Njb3B5IFBlcmZvcm1lZCZxdW90OzwvYTpzPg0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPC9hOnM+DQogICAgICAgICAgICAgICAgICAgICAgICAgICA8L2E6cz4NCiAgICAgICAgICAgICAgICAgICAgICAgICAgIDxhOnM+CiAgICAgICAgICAgICAgICAgICAgCQlvciA8L2E6cz4NCiAgICAgICAgICAgICAgICAgICAgICAgICAgIDxhOnMgcj0iMTg5Ij4NCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDxhOnM+ZXhpc3RzIDwvYTpzPg0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPGE6cyByPSIxODgiPg0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPGE6cz4mcXVvdDtGZWNhbCBPY2N1bHQgQmxvb2QgVGVzdCBQZXJmb3JtZWQmcXVvdDs8L2E6cz4NCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDwvYTpzPg0KICAgICAgICAgICAgICAgICAgICAgICAgICAgPC9hOnM+DQogICAgICAgICAgICAgICAgICAgICAgICA8L2E6cz4NCiAgICAgICAgICAgICAgICAgICAgICAgIDxhOnM+CiAgICAgICAgICAgICAgICAgICAgCQlvciA8L2E6cz4NCiAgICAgICAgICAgICAgICAgICAgICAgIDxhOnMgcj0iMTkyIj4NCiAgICAgICAgICAgICAgICAgICAgICAgICAgIDxhOnM+ZXhpc3RzIDwvYTpzPg0KICAgICAgICAgICAgICAgICAgICAgICAgICAgPGE6cyByPSIxOTEiPg0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPGE6cz4mcXVvdDtGbGV4aWJsZSBTaWdtb2lkb3Njb3B5IFBlcmZvcm1lZCZxdW90OzwvYTpzPg0KICAgICAgICAgICAgICAgICAgICAgICAgICAgPC9hOnM+DQogICAgICAgICAgICAgICAgICAgICAgICA8L2E6cz4NCiAgICAgICAgICAgICAgICAgICAgIDwvYTpzPg0KICAgICAgICAgICAgICAgICAgICAgPGE6cz4KICAgICAgICAgICAgICAgICAgICAJCW9yIDwvYTpzPg0KICAgICAgICAgICAgICAgICAgICAgPGE6cyByPSIxOTUiPg0KICAgICAgICAgICAgICAgICAgICAgICAgPGE6cz5leGlzdHMgPC9hOnM+DQogICAgICAgICAgICAgICAgICAgICAgICA8YTpzIHI9IjE5NCI+DQogICAgICAgICAgICAgICAgICAgICAgICAgICA8YTpzPiZxdW90O0ZlY2FsIEltbXVub2NoZW1pY2FsIFRlc3QgRE5BJnF1b3Q7PC9hOnM+DQogICAgICAgICAgICAgICAgICAgICAgICA8L2E6cz4NCiAgICAgICAgICAgICAgICAgICAgIDwvYTpzPg0KICAgICAgICAgICAgICAgICAgPC9hOnM+DQogICAgICAgICAgICAgICAgICA8YTpzPgogICAgICAgICAgICAgICAgICAgIAkJb3IgPC9hOnM+DQogICAgICAgICAgICAgICAgICA8YTpzIHI9IjE5OCI+DQogICAgICAgICAgICAgICAgICAgICA8YTpzPmV4aXN0cyA8L2E6cz4NCiAgICAgICAgICAgICAgICAgICAgIDxhOnMgcj0iMTk3Ij4NCiAgICAgICAgICAgICAgICAgICAgICAgIDxhOnM+JnF1b3Q7Q1QgQ29sb25vZ3JhcGh5IFBlcmZvcm1lZCZxdW90OzwvYTpzPg0KICAgICAgICAgICAgICAgICAgICAgPC9hOnM+DQogICAgICAgICAgICAgICAgICA8L2E6cz4NCiAgICAgICAgICAgICAgIDwvYTpzPg0KICAgICAgICAgICAgPC9hOnM+DQogICAgICAgICA8L2Fubm90YXRpb24+DQogICAgICAgICA8ZXhwcmVzc2lvbiBsb2NhbElkPSIxOTkiIGxvY2F0b3I9IjEwMzozLTEwNzo1OSIgeHNpOnR5cGU9Ik9yIj4NCiAgICAgICAgICAgIDxvcGVyYW5kIGxvY2FsSWQ9IjE5NiIgbG9jYXRvcj0iMTAzOjMtMTA2OjYzIiB4c2k6dHlwZT0iT3IiPg0KICAgICAgICAgICAgICAgPG9wZXJhbmQgbG9jYWxJZD0iMTkzIiBsb2NhdG9yPSIxMDM6My0xMDU6NjYiIHhzaTp0eXBlPSJPciI+DQogICAgICAgICAgICAgICAgICA8b3BlcmFuZCBsb2NhbElkPSIxOTAiIGxvY2F0b3I9IjEwMzozLTEwNDo2NyIgeHNpOnR5cGU9Ik9yIj4NCiAgICAgICAgICAgICAgICAgICAgIDxvcGVyYW5kIGxvY2FsSWQ9IjE4NyIgbG9jYXRvcj0iMTAzOjMtMTAzOjMyIiB4c2k6dHlwZT0iRXhpc3RzIj4NCiAgICAgICAgICAgICAgICAgICAgICAgIDxvcGVyYW5kIGxvY2FsSWQ9IjE4NiIgbG9jYXRvcj0iMTAzOjEwLTEwMzozMiIgbmFtZT0iQ29sb25vc2NvcHkgUGVyZm9ybWVkIiB4c2k6dHlwZT0iRXhwcmVzc2lvblJlZiIvPg0KICAgICAgICAgICAgICAgICAgICAgPC9vcGVyYW5kPg0KICAgICAgICAgICAgICAgICAgICAgPG9wZXJhbmQgbG9jYWxJZD0iMTg5IiBsb2NhdG9yPSIxMDQ6MjYtMTA0OjY3IiB4c2k6dHlwZT0iRXhpc3RzIj4NCiAgICAgICAgICAgICAgICAgICAgICAgIDxvcGVyYW5kIGxvY2FsSWQ9IjE4OCIgbG9jYXRvcj0iMTA0OjMzLTEwNDo2NyIgbmFtZT0iRmVjYWwgT2NjdWx0IEJsb29kIFRlc3QgUGVyZm9ybWVkIiB4c2k6dHlwZT0iRXhwcmVzc2lvblJlZiIvPg0KICAgICAgICAgICAgICAgICAgICAgPC9vcGVyYW5kPg0KICAgICAgICAgICAgICAgICAgPC9vcGVyYW5kPg0KICAgICAgICAgICAgICAgICAgPG9wZXJhbmQgbG9jYWxJZD0iMTkyIiBsb2NhdG9yPSIxMDU6MjYtMTA1OjY2IiB4c2k6dHlwZT0iRXhpc3RzIj4NCiAgICAgICAgICAgICAgICAgICAgIDxvcGVyYW5kIGxvY2FsSWQ9IjE5MSIgbG9jYXRvcj0iMTA1OjMzLTEwNTo2NiIgbmFtZT0iRmxleGlibGUgU2lnbW9pZG9zY29weSBQZXJmb3JtZWQiIHhzaTp0eXBlPSJFeHByZXNzaW9uUmVmIi8+DQogICAgICAgICAgICAgICAgICA8L29wZXJhbmQ+DQogICAgICAgICAgICAgICA8L29wZXJhbmQ+DQogICAgICAgICAgICAgICA8b3BlcmFuZCBsb2NhbElkPSIxOTUiIGxvY2F0b3I9IjEwNjoyNi0xMDY6NjMiIHhzaTp0eXBlPSJFeGlzdHMiPg0KICAgICAgICAgICAgICAgICAgPG9wZXJhbmQgbG9jYWxJZD0iMTk0IiBsb2NhdG9yPSIxMDY6MzMtMTA2OjYzIiBuYW1lPSJGZWNhbCBJbW11bm9jaGVtaWNhbCBUZXN0IEROQSIgeHNpOnR5cGU9IkV4cHJlc3Npb25SZWYiLz4NCiAgICAgICAgICAgICAgIDwvb3BlcmFuZD4NCiAgICAgICAgICAgIDwvb3BlcmFuZD4NCiAgICAgICAgICAgIDxvcGVyYW5kIGxvY2FsSWQ9IjE5OCIgbG9jYXRvcj0iMTA3OjI2LTEwNzo1OSIgeHNpOnR5cGU9IkV4aXN0cyI+DQogICAgICAgICAgICAgICA8b3BlcmFuZCBsb2NhbElkPSIxOTciIGxvY2F0b3I9IjEwNzozMy0xMDc6NTkiIG5hbWU9IkNUIENvbG9ub2dyYXBoeSBQZXJmb3JtZWQiIHhzaTp0eXBlPSJFeHByZXNzaW9uUmVmIi8+DQogICAgICAgICAgICA8L29wZXJhbmQ+DQogICAgICAgICA8L2V4cHJlc3Npb24+DQogICAgICA8L2RlZj4NCiAgICAgIDxkZWYgbG9jYWxJZD0iMjEwIiBsb2NhdG9yPSIxMDk6MS0xMTE6MTIwIiBuYW1lPSJNYWxpZ25hbnQgTmVvcGxhc20iIGNvbnRleHQ9IlBhdGllbnQiIGFjY2Vzc0xldmVsPSJQdWJsaWMiPg0KICAgICAgICAgPGFubm90YXRpb24geHNpOnR5cGU9ImE6QW5ub3RhdGlvbiI+DQogICAgICAgICAgICA8YTpzIHI9IjIxMCI+DQogICAgICAgICAgICAgICA8YTpzPmRlZmluZSAmcXVvdDtNYWxpZ25hbnQgTmVvcGxhc20mcXVvdDs6CiAgPC9hOnM+DQogICAgICAgICAgICAgICA8YTpzIHI9IjIwOSI+DQogICAgICAgICAgICAgICAgICA8YTpzPg0KICAgICAgICAgICAgICAgICAgICAgPGE6cyByPSIyMDIiPg0KICAgICAgICAgICAgICAgICAgICAgICAgPGE6cyByPSIyMDEiPg0KICAgICAgICAgICAgICAgICAgICAgICAgICAgPGE6cyByPSIyMDEiPg0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPGE6cz5bQ29uZGl0aW9uOiA8L2E6cz4NCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDxhOnM+DQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8YTpzPiZxdW90O01hbGlnbmFudCBOZW9wbGFzbSBvZiBDb2xvbiZxdW90OzwvYTpzPg0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPC9hOnM+DQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8YTpzPl08L2E6cz4NCiAgICAgICAgICAgICAgICAgICAgICAgICAgIDwvYTpzPg0KICAgICAgICAgICAgICAgICAgICAgICAgPC9hOnM+DQogICAgICAgICAgICAgICAgICAgICAgICA8YTpzPiBDb2xvcmVjdGFsQ2FuY2VyPC9hOnM+DQogICAgICAgICAgICAgICAgICAgICA8L2E6cz4NCiAgICAgICAgICAgICAgICAgIDwvYTpzPg0KICAgICAgICAgICAgICAgICAgPGE6cz4KICAgICAgICAgICAgICAgICAgICAJCTwvYTpzPg0KICAgICAgICAgICAgICAgICAgPGE6cyByPSIyMDgiPg0KICAgICAgICAgICAgICAgICAgICAgPGE6cz53aGVyZSA8L2E6cz4NCiAgICAgICAgICAgICAgICAgICAgIDxhOnMgcj0iMjA4Ij4NCiAgICAgICAgICAgICAgICAgICAgICAgIDxhOnMgcj0iMjA1Ij4NCiAgICAgICAgICAgICAgICAgICAgICAgICAgIDxhOnMgcj0iMjAzIj4NCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDxhOnM+R2xvYmFsPC9hOnM+DQogICAgICAgICAgICAgICAgICAgICAgICAgICA8L2E6cz4NCiAgICAgICAgICAgICAgICAgICAgICAgICAgIDxhOnM+LjwvYTpzPg0KICAgICAgICAgICAgICAgICAgICAgICAgICAgPGE6cyByPSIyMDUiPg0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPGE6cz4mcXVvdDtQcmV2YWxlbmNlIFBlcmlvZCZxdW90Oyg8L2E6cz4NCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDxhOnMgcj0iMjA0Ij4NCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDxhOnM+Q29sb3JlY3RhbENhbmNlcjwvYTpzPg0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPC9hOnM+DQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8YTpzPik8L2E6cz4NCiAgICAgICAgICAgICAgICAgICAgICAgICAgIDwvYTpzPg0KICAgICAgICAgICAgICAgICAgICAgICAgPC9hOnM+DQogICAgICAgICAgICAgICAgICAgICAgICA8YTpzIHI9IjIwOCI+IHN0YXJ0cyBvbiBvciBiZWZvcmUgPC9hOnM+DQogICAgICAgICAgICAgICAgICAgICAgICA8YTpzIHI9IjIwNyI+DQogICAgICAgICAgICAgICAgICAgICAgICAgICA8YTpzPmVuZCBvZiA8L2E6cz4NCiAgICAgICAgICAgICAgICAgICAgICAgICAgIDxhOnMgcj0iMjA2Ij4NCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDxhOnM+JnF1b3Q7TWVhc3VyZW1lbnQgUGVyaW9kJnF1b3Q7PC9hOnM+DQogICAgICAgICAgICAgICAgICAgICAgICAgICA8L2E6cz4NCiAgICAgICAgICAgICAgICAgICAgICAgIDwvYTpzPg0KICAgICAgICAgICAgICAgICAgICAgPC9hOnM+DQogICAgICAgICAgICAgICAgICA8L2E6cz4NCiAgICAgICAgICAgICAgIDwvYTpzPg0KICAgICAgICAgICAgPC9hOnM+DQogICAgICAgICA8L2Fubm90YXRpb24+DQogICAgICAgICA8ZXhwcmVzc2lvbiBsb2NhbElkPSIyMDkiIGxvY2F0b3I9IjExMDozLTExMToxMjAiIHhzaTp0eXBlPSJRdWVyeSI+DQogICAgICAgICAgICA8c291cmNlIGxvY2FsSWQ9IjIwMiIgbG9jYXRvcj0iMTEwOjMtMTEwOjYxIiBhbGlhcz0iQ29sb3JlY3RhbENhbmNlciI+DQogICAgICAgICAgICAgICA8ZXhwcmVzc2lvbiBsb2NhbElkPSIyMDEiIGxvY2F0b3I9IjExMDozLTExMDo0NCIgZGF0YVR5cGU9ImZoaXI6Q29uZGl0aW9uIiB0ZW1wbGF0ZUlkPSJodHRwOi8vaGw3Lm9yZy9maGlyL1N0cnVjdHVyZURlZmluaXRpb24vQ29uZGl0aW9uIiBjb2RlUHJvcGVydHk9ImNvZGUiIGNvZGVDb21wYXJhdG9yPSJpbiIgeHNpOnR5cGU9IlJldHJpZXZlIj4NCiAgICAgICAgICAgICAgICAgIDxjb2RlcyBsb2NhdG9yPSIxMTA6MTUtMTEwOjQzIiBuYW1lPSJNYWxpZ25hbnQgTmVvcGxhc20gb2YgQ29sb24iIHhzaTp0eXBlPSJWYWx1ZVNldFJlZiIvPg0KICAgICAgICAgICAgICAgPC9leHByZXNzaW9uPg0KICAgICAgICAgICAgPC9zb3VyY2U+DQogICAgICAgICAgICA8d2hlcmUgbG9jYWxJZD0iMjA4IiBsb2NhdG9yPSIxMTE6MjMtMTExOjEyMCIgeHNpOnR5cGU9IlNhbWVPckJlZm9yZSI+DQogICAgICAgICAgICAgICA8b3BlcmFuZCBsb2NhdG9yPSIxMTE6NzQtMTExOjc5IiB4c2k6dHlwZT0iU3RhcnQiPg0KICAgICAgICAgICAgICAgICAgPG9wZXJhbmQgbG9jYWxJZD0iMjA1IiBsb2NhdG9yPSIxMTE6MjktMTExOjcyIiBuYW1lPSJQcmV2YWxlbmNlIFBlcmlvZCIgbGlicmFyeU5hbWU9Ikdsb2JhbCIgeHNpOnR5cGU9IkZ1bmN0aW9uUmVmIj4NCiAgICAgICAgICAgICAgICAgICAgIDxvcGVyYW5kIGxvY2FsSWQ9IjIwNCIgbG9jYXRvcj0iMTExOjU2LTExMTo3MSIgbmFtZT0iQ29sb3JlY3RhbENhbmNlciIgeHNpOnR5cGU9IkFsaWFzUmVmIi8+DQogICAgICAgICAgICAgICAgICA8L29wZXJhbmQ+DQogICAgICAgICAgICAgICA8L29wZXJhbmQ+DQogICAgICAgICAgICAgICA8b3BlcmFuZCBsb2NhbElkPSIyMDciIGxvY2F0b3I9IjExMTo5NC0xMTE6MTIwIiB4c2k6dHlwZT0iRW5kIj4NCiAgICAgICAgICAgICAgICAgIDxvcGVyYW5kIGxvY2FsSWQ9IjIwNiIgbG9jYXRvcj0iMTExOjEwMS0xMTE6MTIwIiBuYW1lPSJNZWFzdXJlbWVudCBQZXJpb2QiIHhzaTp0eXBlPSJQYXJhbWV0ZXJSZWYiLz4NCiAgICAgICAgICAgICAgIDwvb3BlcmFuZD4NCiAgICAgICAgICAgIDwvd2hlcmU+DQogICAgICAgICA8L2V4cHJlc3Npb24+DQogICAgICA8L2RlZj4NCiAgICAgIDxkZWYgbG9jYWxJZD0iMjI1IiBsb2NhdG9yPSIxMTM6MS0xMTg6OTAiIG5hbWU9IkRlbm9taW5hdG9yIEV4Y2x1c2lvbiIgY29udGV4dD0iUGF0aWVudCIgYWNjZXNzTGV2ZWw9IlB1YmxpYyI+DQogICAgICAgICA8YW5ub3RhdGlvbiB4c2k6dHlwZT0iYTpBbm5vdGF0aW9uIj4NCiAgICAgICAgICAgIDxhOnMgcj0iMjI1Ij4NCiAgICAgICAgICAgICAgIDxhOnM+ZGVmaW5lICZxdW90O0Rlbm9taW5hdG9yIEV4Y2x1c2lvbiZxdW90OzoKICA8L2E6cz4NCiAgICAgICAgICAgICAgIDxhOnMgcj0iMjI0Ij4NCiAgICAgICAgICAgICAgICAgIDxhOnMgcj0iMjE4Ij4NCiAgICAgICAgICAgICAgICAgICAgIDxhOnMgcj0iMjE1Ij4NCiAgICAgICAgICAgICAgICAgICAgICAgIDxhOnMgcj0iMjEyIj4NCiAgICAgICAgICAgICAgICAgICAgICAgICAgIDxhOnMgcj0iMjExIj4NCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDxhOnM+SG9zcGljZTwvYTpzPg0KICAgICAgICAgICAgICAgICAgICAgICAgICAgPC9hOnM+DQogICAgICAgICAgICAgICAgICAgICAgICAgICA8YTpzPi48L2E6cz4NCiAgICAgICAgICAgICAgICAgICAgICAgICAgIDxhOnMgcj0iMjEyIj4NCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDxhOnM+JnF1b3Q7SGFzIEhvc3BpY2UmcXVvdDs8L2E6cz4NCiAgICAgICAgICAgICAgICAgICAgICAgICAgIDwvYTpzPg0KICAgICAgICAgICAgICAgICAgICAgICAgPC9hOnM+DQogICAgICAgICAgICAgICAgICAgICAgICA8YTpzPgogICAgICAgICAgICAgICAgICAgIAkJb3IgPC9hOnM+DQogICAgICAgICAgICAgICAgICAgICAgICA8YTpzIHI9IjIxNCI+DQogICAgICAgICAgICAgICAgICAgICAgICAgICA8YTpzPmV4aXN0cyA8L2E6cz4NCiAgICAgICAgICAgICAgICAgICAgICAgICAgIDxhOnMgcj0iMjEzIj4NCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDxhOnM+JnF1b3Q7TWFsaWduYW50IE5lb3BsYXNtJnF1b3Q7PC9hOnM+DQogICAgICAgICAgICAgICAgICAgICAgICAgICA8L2E6cz4NCiAgICAgICAgICAgICAgICAgICAgICAgIDwvYTpzPg0KICAgICAgICAgICAgICAgICAgICAgPC9hOnM+DQogICAgICAgICAgICAgICAgICAgICA8YTpzPgogICAgICAgICAgICAgICAgICAgIAkJb3IgPC9hOnM+DQogICAgICAgICAgICAgICAgICAgICA8YTpzIHI9IjIxNyI+DQogICAgICAgICAgICAgICAgICAgICAgICA8YTpzPmV4aXN0cyA8L2E6cz4NCiAgICAgICAgICAgICAgICAgICAgICAgIDxhOnMgcj0iMjE2Ij4NCiAgICAgICAgICAgICAgICAgICAgICAgICAgIDxhOnM+JnF1b3Q7VG90YWwgQ29sZWN0b215IFBlcmZvcm1lZCZxdW90OzwvYTpzPg0KICAgICAgICAgICAgICAgICAgICAgICAgPC9hOnM+DQogICAgICAgICAgICAgICAgICAgICA8L2E6cz4NCiAgICAgICAgICAgICAgICAgIDwvYTpzPg0KICAgICAgICAgICAgICAgICAgPGE6cz4KICAgICAgICAgICAgICAgICAgICAJCW9yIDwvYTpzPg0KICAgICAgICAgICAgICAgICAgPGE6cyByPSIyMjMiPg0KICAgICAgICAgICAgICAgICAgICAgPGE6cyByPSIyMjAiPg0KICAgICAgICAgICAgICAgICAgICAgICAgPGE6cyByPSIyMTkiPg0KICAgICAgICAgICAgICAgICAgICAgICAgICAgPGE6cz5GcmFpbHR5PC9hOnM+DQogICAgICAgICAgICAgICAgICAgICAgICA8L2E6cz4NCiAgICAgICAgICAgICAgICAgICAgICAgIDxhOnM+LiA8L2E6cz4NCiAgICAgICAgICAgICAgICAgICAgICAgIDxhOnMgcj0iMjIwIj4NCiAgICAgICAgICAgICAgICAgICAgICAgICAgIDxhOnM+JnF1b3Q7QWR2YW5jZWQgSWxsbmVzcyBhbmQgRnJhaWx0eSBFeGNsdXNpb24gTm90IEluY2x1ZGluZyBPdmVyIEFnZSA4MCZxdW90OzwvYTpzPg0KICAgICAgICAgICAgICAgICAgICAgICAgPC9hOnM+DQogICAgICAgICAgICAgICAgICAgICA8L2E6cz4NCiAgICAgICAgICAgICAgICAgICAgIDxhOnM+CiAgICAgICAgICAgICAgICAgICAgCQlhbmQgPC9hOnM+DQogICAgICAgICAgICAgICAgICAgICA8YTpzIHI9IjIyMiI+DQogICAgICAgICAgICAgICAgICAgICAgICA8YTpzIHI9IjIyMSI+DQogICAgICAgICAgICAgICAgICAgICAgICAgICA8YTpzPkZyYWlsdHk8L2E6cz4NCiAgICAgICAgICAgICAgICAgICAgICAgIDwvYTpzPg0KICAgICAgICAgICAgICAgICAgICAgICAgPGE6cz4uPC9hOnM+DQogICAgICAgICAgICAgICAgICAgICAgICA8YTpzIHI9IjIyMiI+DQogICAgICAgICAgICAgICAgICAgICAgICAgICA8YTpzPiZxdW90O0xvbmcgVGVybSBDYXJlIFBlcmlvZHMgTG9uZ2VyIFRoYW4gOTAgQ29uc2VjdXRpdmUgRGF5cyZxdW90OzwvYTpzPg0KICAgICAgICAgICAgICAgICAgICAgICAgPC9hOnM+DQogICAgICAgICAgICAgICAgICAgICA8L2E6cz4NCiAgICAgICAgICAgICAgICAgIDwvYTpzPg0KICAgICAgICAgICAgICAgPC9hOnM+DQogICAgICAgICAgICA8L2E6cz4NCiAgICAgICAgIDwvYW5ub3RhdGlvbj4NCiAgICAgICAgIDxleHByZXNzaW9uIGxvY2FsSWQ9IjIyNCIgbG9jYXRvcj0iMTE0OjMtMTE4OjkwIiB4c2k6dHlwZT0iT3IiPg0KICAgICAgICAgICAgPG9wZXJhbmQgbG9jYWxJZD0iMjE4IiBsb2NhdG9yPSIxMTQ6My0xMTY6NTkiIHhzaTp0eXBlPSJPciI+DQogICAgICAgICAgICAgICA8b3BlcmFuZCBsb2NhbElkPSIyMTUiIGxvY2F0b3I9IjExNDozLTExNTo1MiIgeHNpOnR5cGU9Ik9yIj4NCiAgICAgICAgICAgICAgICAgIDxvcGVyYW5kIGxvY2FsSWQ9IjIxMiIgbG9jYXRvcj0iMTE0OjMtMTE0OjIzIiBuYW1lPSJIYXMgSG9zcGljZSIgbGlicmFyeU5hbWU9Ikhvc3BpY2UiIHhzaTp0eXBlPSJFeHByZXNzaW9uUmVmIi8+DQogICAgICAgICAgICAgICAgICA8b3BlcmFuZCBsb2NhbElkPSIyMTQiIGxvY2F0b3I9IjExNToyNi0xMTU6NTIiIHhzaTp0eXBlPSJFeGlzdHMiPg0KICAgICAgICAgICAgICAgICAgICAgPG9wZXJhbmQgbG9jYWxJZD0iMjEzIiBsb2NhdG9yPSIxMTU6MzMtMTE1OjUyIiBuYW1lPSJNYWxpZ25hbnQgTmVvcGxhc20iIHhzaTp0eXBlPSJFeHByZXNzaW9uUmVmIi8+DQogICAgICAgICAgICAgICAgICA8L29wZXJhbmQ+DQogICAgICAgICAgICAgICA8L29wZXJhbmQ+DQogICAgICAgICAgICAgICA8b3BlcmFuZCBsb2NhbElkPSIyMTciIGxvY2F0b3I9IjExNjoyNi0xMTY6NTkiIHhzaTp0eXBlPSJFeGlzdHMiPg0KICAgICAgICAgICAgICAgICAgPG9wZXJhbmQgbG9jYWxJZD0iMjE2IiBsb2NhdG9yPSIxMTY6MzMtMTE2OjU5IiBuYW1lPSJUb3RhbCBDb2xlY3RvbXkgUGVyZm9ybWVkIiB4c2k6dHlwZT0iRXhwcmVzc2lvblJlZiIvPg0KICAgICAgICAgICAgICAgPC9vcGVyYW5kPg0KICAgICAgICAgICAgPC9vcGVyYW5kPg0KICAgICAgICAgICAgPG9wZXJhbmQgbG9jYWxJZD0iMjIzIiBsb2NhdG9yPSIxMTc6MjYtMTE4OjkwIiB4c2k6dHlwZT0iQW5kIj4NCiAgICAgICAgICAgICAgIDxvcGVyYW5kIGxvY2FsSWQ9IjIyMCIgbG9jYXRvcj0iMTE3OjI2LTExNzoxMDAiIG5hbWU9IkFkdmFuY2VkIElsbG5lc3MgYW5kIEZyYWlsdHkgRXhjbHVzaW9uIE5vdCBJbmNsdWRpbmcgT3ZlciBBZ2UgODAiIGxpYnJhcnlOYW1lPSJGcmFpbHR5IiB4c2k6dHlwZT0iRXhwcmVzc2lvblJlZiIvPg0KICAgICAgICAgICAgICAgPG9wZXJhbmQgbG9jYWxJZD0iMjIyIiBsb2NhdG9yPSIxMTg6MjctMTE4OjkwIiBuYW1lPSJMb25nIFRlcm0gQ2FyZSBQZXJpb2RzIExvbmdlciBUaGFuIDkwIENvbnNlY3V0aXZlIERheXMiIGxpYnJhcnlOYW1lPSJGcmFpbHR5IiB4c2k6dHlwZT0iRXhwcmVzc2lvblJlZiIvPg0KICAgICAgICAgICAgPC9vcGVyYW5kPg0KICAgICAgICAgPC9leHByZXNzaW9uPg0KICAgICAgPC9kZWY+DQogICA8L3N0YXRlbWVudHM+DQo8L2xpYnJhcnk+DQo=" + }, + { + "contentType": "application/elm+json", + "data": "ew0KICAgImxpYnJhcnkiIDogew0KICAgICAgImFubm90YXRpb24iIDogWyB7DQogICAgICAgICAidHJhbnNsYXRvclZlcnNpb24iIDogIjEuMyIsDQogICAgICAgICAidHJhbnNsYXRvck9wdGlvbnMiIDogIkVuYWJsZUFubm90YXRpb25zLEVuYWJsZUxvY2F0b3JzLERpc2FibGVMaXN0RGVtb3Rpb24sRGlzYWJsZUxpc3RQcm9tb3Rpb24iLA0KICAgICAgICAgInR5cGUiIDogIkNxbFRvRWxtSW5mbyINCiAgICAgIH0sIHsNCiAgICAgICAgICJ0eXBlIiA6ICJBbm5vdGF0aW9uIiwNCiAgICAgICAgICJzIiA6IHsNCiAgICAgICAgICAgICJyIiA6ICIyMjUiLA0KICAgICAgICAgICAgInMiIDogWyB7DQogICAgICAgICAgICAgICAidmFsdWUiIDogWyAiIiwibGlicmFyeSBDb2xvcmVjdGFsQ2FuY2VyU2NyZWVuaW5nc0ZISVIgdmVyc2lvbiAnMC4wLjAwMSciIF0NCiAgICAgICAgICAgIH0gXQ0KICAgICAgICAgfQ0KICAgICAgfSBdLA0KICAgICAgImlkZW50aWZpZXIiIDogew0KICAgICAgICAgImlkIiA6ICJDb2xvcmVjdGFsQ2FuY2VyU2NyZWVuaW5nc0ZISVIiLA0KICAgICAgICAgInN5c3RlbSIgOiAiaHR0cDovL2VjcWkuaGVhbHRoaXQuZ292L2VjcW1zIiwNCiAgICAgICAgICJ2ZXJzaW9uIiA6ICIwLjAuMDAxIg0KICAgICAgfSwNCiAgICAgICJzY2hlbWFJZGVudGlmaWVyIiA6IHsNCiAgICAgICAgICJpZCIgOiAidXJuOmhsNy1vcmc6ZWxtIiwNCiAgICAgICAgICJ2ZXJzaW9uIiA6ICJyMSINCiAgICAgIH0sDQogICAgICAidXNpbmdzIiA6IHsNCiAgICAgICAgICJkZWYiIDogWyB7DQogICAgICAgICAgICAibG9jYWxJZGVudGlmaWVyIiA6ICJTeXN0ZW0iLA0KICAgICAgICAgICAgInVyaSIgOiAidXJuOmhsNy1vcmc6ZWxtLXR5cGVzOnIxIg0KICAgICAgICAgfSwgew0KICAgICAgICAgICAgImxvY2FsSWQiIDogIjEiLA0KICAgICAgICAgICAgImxvY2F0b3IiIDogIjM6MS0zOjI2IiwNCiAgICAgICAgICAgICJsb2NhbElkZW50aWZpZXIiIDogIkZISVIiLA0KICAgICAgICAgICAgInVyaSIgOiAiaHR0cDovL2hsNy5vcmcvZmhpciIsDQogICAgICAgICAgICAidmVyc2lvbiIgOiAiNC4wLjEiLA0KICAgICAgICAgICAgImFubm90YXRpb24iIDogWyB7DQogICAgICAgICAgICAgICAidHlwZSIgOiAiQW5ub3RhdGlvbiIsDQogICAgICAgICAgICAgICAicyIgOiB7DQogICAgICAgICAgICAgICAgICAiciIgOiAiMSIsDQogICAgICAgICAgICAgICAgICAicyIgOiBbIHsNCiAgICAgICAgICAgICAgICAgICAgICJ2YWx1ZSIgOiBbICIiLCJ1c2luZyAiIF0NCiAgICAgICAgICAgICAgICAgIH0sIHsNCiAgICAgICAgICAgICAgICAgICAgICJzIiA6IFsgew0KICAgICAgICAgICAgICAgICAgICAgICAgInZhbHVlIiA6IFsgIkZISVIiIF0NCiAgICAgICAgICAgICAgICAgICAgIH0gXQ0KICAgICAgICAgICAgICAgICAgfSwgew0KICAgICAgICAgICAgICAgICAgICAgInZhbHVlIiA6IFsgIiB2ZXJzaW9uICIsIic0LjAuMSciIF0NCiAgICAgICAgICAgICAgICAgIH0gXQ0KICAgICAgICAgICAgICAgfQ0KICAgICAgICAgICAgfSBdDQogICAgICAgICB9IF0NCiAgICAgIH0sDQogICAgICAiaW5jbHVkZXMiIDogew0KICAgICAgICAgImRlZiIgOiBbIHsNCiAgICAgICAgICAgICJsb2NhbElkIiA6ICIyIiwNCiAgICAgICAgICAgICJsb2NhdG9yIiA6ICI1OjEtNTo1NiIsDQogICAgICAgICAgICAibG9jYWxJZGVudGlmaWVyIiA6ICJGSElSSGVscGVycyIsDQogICAgICAgICAgICAicGF0aCIgOiAiaHR0cDovL2VjcWkuaGVhbHRoaXQuZ292L2VjcW1zL0ZISVJIZWxwZXJzIiwNCiAgICAgICAgICAgICJ2ZXJzaW9uIiA6ICI0LjAuMDAxIiwNCiAgICAgICAgICAgICJhbm5vdGF0aW9uIiA6IFsgew0KICAgICAgICAgICAgICAgInR5cGUiIDogIkFubm90YXRpb24iLA0KICAgICAgICAgICAgICAgInMiIDogew0KICAgICAgICAgICAgICAgICAgInIiIDogIjIiLA0KICAgICAgICAgICAgICAgICAgInMiIDogWyB7DQogICAgICAgICAgICAgICAgICAgICAidmFsdWUiIDogWyAiIiwiaW5jbHVkZSAiIF0NCiAgICAgICAgICAgICAgICAgIH0sIHsNCiAgICAgICAgICAgICAgICAgICAgICJzIiA6IFsgew0KICAgICAgICAgICAgICAgICAgICAgICAgInZhbHVlIiA6IFsgIkZISVJIZWxwZXJzIiBdDQogICAgICAgICAgICAgICAgICAgICB9IF0NCiAgICAgICAgICAgICAgICAgIH0sIHsNCiAgICAgICAgICAgICAgICAgICAgICJ2YWx1ZSIgOiBbICIgdmVyc2lvbiAiLCInNC4wLjAwMSciLCIgY2FsbGVkICIsIkZISVJIZWxwZXJzIiBdDQogICAgICAgICAgICAgICAgICB9IF0NCiAgICAgICAgICAgICAgIH0NCiAgICAgICAgICAgIH0gXQ0KICAgICAgICAgfSwgew0KICAgICAgICAgICAgImxvY2FsSWQiIDogIjMiLA0KICAgICAgICAgICAgImxvY2F0b3IiIDogIjY6MS02OjY2IiwNCiAgICAgICAgICAgICJsb2NhbElkZW50aWZpZXIiIDogIlNERSIsDQogICAgICAgICAgICAicGF0aCIgOiAiaHR0cDovL2VjcWkuaGVhbHRoaXQuZ292L2VjcW1zL1N1cHBsZW1lbnRhbERhdGFFbGVtZW50c0ZISVI0IiwNCiAgICAgICAgICAgICJ2ZXJzaW9uIiA6ICIyLjAuMDAwIiwNCiAgICAgICAgICAgICJhbm5vdGF0aW9uIiA6IFsgew0KICAgICAgICAgICAgICAgInR5cGUiIDogIkFubm90YXRpb24iLA0KICAgICAgICAgICAgICAgInMiIDogew0KICAgICAgICAgICAgICAgICAgInIiIDogIjMiLA0KICAgICAgICAgICAgICAgICAgInMiIDogWyB7DQogICAgICAgICAgICAgICAgICAgICAidmFsdWUiIDogWyAiIiwiaW5jbHVkZSAiIF0NCiAgICAgICAgICAgICAgICAgIH0sIHsNCiAgICAgICAgICAgICAgICAgICAgICJzIiA6IFsgew0KICAgICAgICAgICAgICAgICAgICAgICAgInZhbHVlIiA6IFsgIlN1cHBsZW1lbnRhbERhdGFFbGVtZW50c0ZISVI0IiBdDQogICAgICAgICAgICAgICAgICAgICB9IF0NCiAgICAgICAgICAgICAgICAgIH0sIHsNCiAgICAgICAgICAgICAgICAgICAgICJ2YWx1ZSIgOiBbICIgdmVyc2lvbiAiLCInMi4wLjAwMCciLCIgY2FsbGVkICIsIlNERSIgXQ0KICAgICAgICAgICAgICAgICAgfSBdDQogICAgICAgICAgICAgICB9DQogICAgICAgICAgICB9IF0NCiAgICAgICAgIH0sIHsNCiAgICAgICAgICAgICJsb2NhbElkIiA6ICI0IiwNCiAgICAgICAgICAgICJsb2NhdG9yIiA6ICI3OjEtNzo2OSIsDQogICAgICAgICAgICAibG9jYWxJZGVudGlmaWVyIiA6ICJHbG9iYWwiLA0KICAgICAgICAgICAgInBhdGgiIDogImh0dHA6Ly9lY3FpLmhlYWx0aGl0Lmdvdi9lY3Ftcy9NQVRHbG9iYWxDb21tb25GdW5jdGlvbnNGSElSNCIsDQogICAgICAgICAgICAidmVyc2lvbiIgOiAiNi4wLjAwMCIsDQogICAgICAgICAgICAiYW5ub3RhdGlvbiIgOiBbIHsNCiAgICAgICAgICAgICAgICJ0eXBlIiA6ICJBbm5vdGF0aW9uIiwNCiAgICAgICAgICAgICAgICJzIiA6IHsNCiAgICAgICAgICAgICAgICAgICJyIiA6ICI0IiwNCiAgICAgICAgICAgICAgICAgICJzIiA6IFsgew0KICAgICAgICAgICAgICAgICAgICAgInZhbHVlIiA6IFsgIiIsImluY2x1ZGUgIiBdDQogICAgICAgICAgICAgICAgICB9LCB7DQogICAgICAgICAgICAgICAgICAgICAicyIgOiBbIHsNCiAgICAgICAgICAgICAgICAgICAgICAgICJ2YWx1ZSIgOiBbICJNQVRHbG9iYWxDb21tb25GdW5jdGlvbnNGSElSNCIgXQ0KICAgICAgICAgICAgICAgICAgICAgfSBdDQogICAgICAgICAgICAgICAgICB9LCB7DQogICAgICAgICAgICAgICAgICAgICAidmFsdWUiIDogWyAiIHZlcnNpb24gIiwiJzYuMC4wMDAnIiwiIGNhbGxlZCAiLCJHbG9iYWwiIF0NCiAgICAgICAgICAgICAgICAgIH0gXQ0KICAgICAgICAgICAgICAgfQ0KICAgICAgICAgICAgfSBdDQogICAgICAgICB9LCB7DQogICAgICAgICAgICAibG9jYWxJZCIgOiAiNSIsDQogICAgICAgICAgICAibG9jYXRvciIgOiAiODoxLTg6ODkiLA0KICAgICAgICAgICAgImxvY2FsSWRlbnRpZmllciIgOiAiQWR1bHRPdXRwYXRpZW50RW5jb3VudGVycyIsDQogICAgICAgICAgICAicGF0aCIgOiAiaHR0cDovL2VjcWkuaGVhbHRoaXQuZ292L2VjcW1zL0FkdWx0T3V0cGF0aWVudEVuY291bnRlcnNGSElSNCIsDQogICAgICAgICAgICAidmVyc2lvbiIgOiAiMi4wLjAwMCIsDQogICAgICAgICAgICAiYW5ub3RhdGlvbiIgOiBbIHsNCiAgICAgICAgICAgICAgICJ0eXBlIiA6ICJBbm5vdGF0aW9uIiwNCiAgICAgICAgICAgICAgICJzIiA6IHsNCiAgICAgICAgICAgICAgICAgICJyIiA6ICI1IiwNCiAgICAgICAgICAgICAgICAgICJzIiA6IFsgew0KICAgICAgICAgICAgICAgICAgICAgInZhbHVlIiA6IFsgIiIsImluY2x1ZGUgIiBdDQogICAgICAgICAgICAgICAgICB9LCB7DQogICAgICAgICAgICAgICAgICAgICAicyIgOiBbIHsNCiAgICAgICAgICAgICAgICAgICAgICAgICJ2YWx1ZSIgOiBbICJBZHVsdE91dHBhdGllbnRFbmNvdW50ZXJzRkhJUjQiIF0NCiAgICAgICAgICAgICAgICAgICAgIH0gXQ0KICAgICAgICAgICAgICAgICAgfSwgew0KICAgICAgICAgICAgICAgICAgICAgInZhbHVlIiA6IFsgIiB2ZXJzaW9uICIsIicyLjAuMDAwJyIsIiBjYWxsZWQgIiwiQWR1bHRPdXRwYXRpZW50RW5jb3VudGVycyIgXQ0KICAgICAgICAgICAgICAgICAgfSBdDQogICAgICAgICAgICAgICB9DQogICAgICAgICAgICB9IF0NCiAgICAgICAgIH0sIHsNCiAgICAgICAgICAgICJsb2NhbElkIiA6ICI2IiwNCiAgICAgICAgICAgICJsb2NhdG9yIiA6ICI5OjEtOTo1MyIsDQogICAgICAgICAgICAibG9jYWxJZGVudGlmaWVyIiA6ICJIb3NwaWNlIiwNCiAgICAgICAgICAgICJwYXRoIiA6ICJodHRwOi8vZWNxaS5oZWFsdGhpdC5nb3YvZWNxbXMvSG9zcGljZUZISVI0IiwNCiAgICAgICAgICAgICJ2ZXJzaW9uIiA6ICIyLjAuMDAwIiwNCiAgICAgICAgICAgICJhbm5vdGF0aW9uIiA6IFsgew0KICAgICAgICAgICAgICAgInR5cGUiIDogIkFubm90YXRpb24iLA0KICAgICAgICAgICAgICAgInMiIDogew0KICAgICAgICAgICAgICAgICAgInIiIDogIjYiLA0KICAgICAgICAgICAgICAgICAgInMiIDogWyB7DQogICAgICAgICAgICAgICAgICAgICAidmFsdWUiIDogWyAiIiwiaW5jbHVkZSAiIF0NCiAgICAgICAgICAgICAgICAgIH0sIHsNCiAgICAgICAgICAgICAgICAgICAgICJzIiA6IFsgew0KICAgICAgICAgICAgICAgICAgICAgICAgInZhbHVlIiA6IFsgIkhvc3BpY2VGSElSNCIgXQ0KICAgICAgICAgICAgICAgICAgICAgfSBdDQogICAgICAgICAgICAgICAgICB9LCB7DQogICAgICAgICAgICAgICAgICAgICAidmFsdWUiIDogWyAiIHZlcnNpb24gIiwiJzIuMC4wMDAnIiwiIGNhbGxlZCAiLCJIb3NwaWNlIiBdDQogICAgICAgICAgICAgICAgICB9IF0NCiAgICAgICAgICAgICAgIH0NCiAgICAgICAgICAgIH0gXQ0KICAgICAgICAgfSwgew0KICAgICAgICAgICAgImxvY2FsSWQiIDogIjciLA0KICAgICAgICAgICAgImxvY2F0b3IiIDogIjEwOjEtMTA6ODUiLA0KICAgICAgICAgICAgImxvY2FsSWRlbnRpZmllciIgOiAiRnJhaWx0eSIsDQogICAgICAgICAgICAicGF0aCIgOiAiaHR0cDovL2VjcWkuaGVhbHRoaXQuZ292L2VjcW1zL0FkdmFuY2VkSWxsbmVzc2FuZEZyYWlsdHlFeGNsdXNpb25FQ1FNRkhJUjQiLA0KICAgICAgICAgICAgInZlcnNpb24iIDogIjUuMTIuMDAwIiwNCiAgICAgICAgICAgICJhbm5vdGF0aW9uIiA6IFsgew0KICAgICAgICAgICAgICAgInR5cGUiIDogIkFubm90YXRpb24iLA0KICAgICAgICAgICAgICAgInMiIDogew0KICAgICAgICAgICAgICAgICAgInIiIDogIjciLA0KICAgICAgICAgICAgICAgICAgInMiIDogWyB7DQogICAgICAgICAgICAgICAgICAgICAidmFsdWUiIDogWyAiIiwiaW5jbHVkZSAiIF0NCiAgICAgICAgICAgICAgICAgIH0sIHsNCiAgICAgICAgICAgICAgICAgICAgICJzIiA6IFsgew0KICAgICAgICAgICAgICAgICAgICAgICAgInZhbHVlIiA6IFsgIkFkdmFuY2VkSWxsbmVzc2FuZEZyYWlsdHlFeGNsdXNpb25FQ1FNRkhJUjQiIF0NCiAgICAgICAgICAgICAgICAgICAgIH0gXQ0KICAgICAgICAgICAgICAgICAgfSwgew0KICAgICAgICAgICAgICAgICAgICAgInZhbHVlIiA6IFsgIiB2ZXJzaW9uICIsIic1LjEyLjAwMCciLCIgY2FsbGVkICIsIkZyYWlsdHkiIF0NCiAgICAgICAgICAgICAgICAgIH0gXQ0KICAgICAgICAgICAgICAgfQ0KICAgICAgICAgICAgfSBdDQogICAgICAgICB9IF0NCiAgICAgIH0sDQogICAgICAicGFyYW1ldGVycyIgOiB7DQogICAgICAgICAiZGVmIiA6IFsgew0KICAgICAgICAgICAgImxvY2FsSWQiIDogIjQ1IiwNCiAgICAgICAgICAgICJsb2NhdG9yIiA6ICI0NjoxLTQ3OjY2IiwNCiAgICAgICAgICAgICJuYW1lIiA6ICJNZWFzdXJlbWVudCBQZXJpb2QiLA0KICAgICAgICAgICAgImFjY2Vzc0xldmVsIiA6ICJQdWJsaWMiLA0KICAgICAgICAgICAgImFubm90YXRpb24iIDogWyB7DQogICAgICAgICAgICAgICAidHlwZSIgOiAiQW5ub3RhdGlvbiIsDQogICAgICAgICAgICAgICAicyIgOiB7DQogICAgICAgICAgICAgICAgICAiciIgOiAiNDUiLA0KICAgICAgICAgICAgICAgICAgInMiIDogWyB7DQogICAgICAgICAgICAgICAgICAgICAidmFsdWUiIDogWyAiIiwicGFyYW1ldGVyICIsIlwiTWVhc3VyZW1lbnQgUGVyaW9kXCIiLCIgIiBdDQogICAgICAgICAgICAgICAgICB9LCB7DQogICAgICAgICAgICAgICAgICAgICAiciIgOiAiNDQiLA0KICAgICAgICAgICAgICAgICAgICAgInMiIDogWyB7DQogICAgICAgICAgICAgICAgICAgICAgICAidmFsdWUiIDogWyAiSW50ZXJ2YWw8IiBdDQogICAgICAgICAgICAgICAgICAgICB9LCB7DQogICAgICAgICAgICAgICAgICAgICAgICAiciIgOiAiNDMiLA0KICAgICAgICAgICAgICAgICAgICAgICAgInMiIDogWyB7DQogICAgICAgICAgICAgICAgICAgICAgICAgICAidmFsdWUiIDogWyAiRGF0ZVRpbWUiIF0NCiAgICAgICAgICAgICAgICAgICAgICAgIH0gXQ0KICAgICAgICAgICAgICAgICAgICAgfSwgew0KICAgICAgICAgICAgICAgICAgICAgICAgInZhbHVlIiA6IFsgIj4iIF0NCiAgICAgICAgICAgICAgICAgICAgIH0gXQ0KICAgICAgICAgICAgICAgICAgfSwgew0KICAgICAgICAgICAgICAgICAgICAgInZhbHVlIiA6IFsgIlxuICBkZWZhdWx0ICIgXQ0KICAgICAgICAgICAgICAgICAgfSwgew0KICAgICAgICAgICAgICAgICAgICAgInIiIDogIjQyIiwNCiAgICAgICAgICAgICAgICAgICAgICJzIiA6IFsgew0KICAgICAgICAgICAgICAgICAgICAgICAgInIiIDogIjQwIiwNCiAgICAgICAgICAgICAgICAgICAgICAgICJ2YWx1ZSIgOiBbICJJbnRlcnZhbFsiLCJAMjAxOS0wMS0wMVQwMDowMDowMC4wIiwiLCAiLCJAMjAyMC0wMS0wMVQwMDowMDowMC4wIiwiKSIgXQ0KICAgICAgICAgICAgICAgICAgICAgfSBdDQogICAgICAgICAgICAgICAgICB9IF0NCiAgICAgICAgICAgICAgIH0NCiAgICAgICAgICAgIH0gXSwNCiAgICAgICAgICAgICJkZWZhdWx0IiA6IHsNCiAgICAgICAgICAgICAgICJsb2NhbElkIiA6ICI0MiIsDQogICAgICAgICAgICAgICAibG9jYXRvciIgOiAiNDc6MTEtNDc6NjYiLA0KICAgICAgICAgICAgICAgImxvd0Nsb3NlZCIgOiB0cnVlLA0KICAgICAgICAgICAgICAgImhpZ2hDbG9zZWQiIDogZmFsc2UsDQogICAgICAgICAgICAgICAidHlwZSIgOiAiSW50ZXJ2YWwiLA0KICAgICAgICAgICAgICAgImxvdyIgOiB7DQogICAgICAgICAgICAgICAgICAibG9jYWxJZCIgOiAiNDAiLA0KICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjQ3OjIwLTQ3OjQxIiwNCiAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJEYXRlVGltZSIsDQogICAgICAgICAgICAgICAgICAieWVhciIgOiB7DQogICAgICAgICAgICAgICAgICAgICAidmFsdWVUeXBlIiA6ICJ7dXJuOmhsNy1vcmc6ZWxtLXR5cGVzOnIxfUludGVnZXIiLA0KICAgICAgICAgICAgICAgICAgICAgInZhbHVlIiA6ICIyMDE5IiwNCiAgICAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJMaXRlcmFsIg0KICAgICAgICAgICAgICAgICAgfSwNCiAgICAgICAgICAgICAgICAgICJtb250aCIgOiB7DQogICAgICAgICAgICAgICAgICAgICAidmFsdWVUeXBlIiA6ICJ7dXJuOmhsNy1vcmc6ZWxtLXR5cGVzOnIxfUludGVnZXIiLA0KICAgICAgICAgICAgICAgICAgICAgInZhbHVlIiA6ICIxIiwNCiAgICAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJMaXRlcmFsIg0KICAgICAgICAgICAgICAgICAgfSwNCiAgICAgICAgICAgICAgICAgICJkYXkiIDogew0KICAgICAgICAgICAgICAgICAgICAgInZhbHVlVHlwZSIgOiAie3VybjpobDctb3JnOmVsbS10eXBlczpyMX1JbnRlZ2VyIiwNCiAgICAgICAgICAgICAgICAgICAgICJ2YWx1ZSIgOiAiMSIsDQogICAgICAgICAgICAgICAgICAgICAidHlwZSIgOiAiTGl0ZXJhbCINCiAgICAgICAgICAgICAgICAgIH0sDQogICAgICAgICAgICAgICAgICAiaG91ciIgOiB7DQogICAgICAgICAgICAgICAgICAgICAidmFsdWVUeXBlIiA6ICJ7dXJuOmhsNy1vcmc6ZWxtLXR5cGVzOnIxfUludGVnZXIiLA0KICAgICAgICAgICAgICAgICAgICAgInZhbHVlIiA6ICIwIiwNCiAgICAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJMaXRlcmFsIg0KICAgICAgICAgICAgICAgICAgfSwNCiAgICAgICAgICAgICAgICAgICJtaW51dGUiIDogew0KICAgICAgICAgICAgICAgICAgICAgInZhbHVlVHlwZSIgOiAie3VybjpobDctb3JnOmVsbS10eXBlczpyMX1JbnRlZ2VyIiwNCiAgICAgICAgICAgICAgICAgICAgICJ2YWx1ZSIgOiAiMCIsDQogICAgICAgICAgICAgICAgICAgICAidHlwZSIgOiAiTGl0ZXJhbCINCiAgICAgICAgICAgICAgICAgIH0sDQogICAgICAgICAgICAgICAgICAic2Vjb25kIiA6IHsNCiAgICAgICAgICAgICAgICAgICAgICJ2YWx1ZVR5cGUiIDogInt1cm46aGw3LW9yZzplbG0tdHlwZXM6cjF9SW50ZWdlciIsDQogICAgICAgICAgICAgICAgICAgICAidmFsdWUiIDogIjAiLA0KICAgICAgICAgICAgICAgICAgICAgInR5cGUiIDogIkxpdGVyYWwiDQogICAgICAgICAgICAgICAgICB9LA0KICAgICAgICAgICAgICAgICAgIm1pbGxpc2Vjb25kIiA6IHsNCiAgICAgICAgICAgICAgICAgICAgICJ2YWx1ZVR5cGUiIDogInt1cm46aGw3LW9yZzplbG0tdHlwZXM6cjF9SW50ZWdlciIsDQogICAgICAgICAgICAgICAgICAgICAidmFsdWUiIDogIjAiLA0KICAgICAgICAgICAgICAgICAgICAgInR5cGUiIDogIkxpdGVyYWwiDQogICAgICAgICAgICAgICAgICB9DQogICAgICAgICAgICAgICB9LA0KICAgICAgICAgICAgICAgImhpZ2giIDogew0KICAgICAgICAgICAgICAgICAgImxvY2FsSWQiIDogIjQxIiwNCiAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICI0Nzo0NC00Nzo2NSIsDQogICAgICAgICAgICAgICAgICAidHlwZSIgOiAiRGF0ZVRpbWUiLA0KICAgICAgICAgICAgICAgICAgInllYXIiIDogew0KICAgICAgICAgICAgICAgICAgICAgInZhbHVlVHlwZSIgOiAie3VybjpobDctb3JnOmVsbS10eXBlczpyMX1JbnRlZ2VyIiwNCiAgICAgICAgICAgICAgICAgICAgICJ2YWx1ZSIgOiAiMjAyMCIsDQogICAgICAgICAgICAgICAgICAgICAidHlwZSIgOiAiTGl0ZXJhbCINCiAgICAgICAgICAgICAgICAgIH0sDQogICAgICAgICAgICAgICAgICAibW9udGgiIDogew0KICAgICAgICAgICAgICAgICAgICAgInZhbHVlVHlwZSIgOiAie3VybjpobDctb3JnOmVsbS10eXBlczpyMX1JbnRlZ2VyIiwNCiAgICAgICAgICAgICAgICAgICAgICJ2YWx1ZSIgOiAiMSIsDQogICAgICAgICAgICAgICAgICAgICAidHlwZSIgOiAiTGl0ZXJhbCINCiAgICAgICAgICAgICAgICAgIH0sDQogICAgICAgICAgICAgICAgICAiZGF5IiA6IHsNCiAgICAgICAgICAgICAgICAgICAgICJ2YWx1ZVR5cGUiIDogInt1cm46aGw3LW9yZzplbG0tdHlwZXM6cjF9SW50ZWdlciIsDQogICAgICAgICAgICAgICAgICAgICAidmFsdWUiIDogIjEiLA0KICAgICAgICAgICAgICAgICAgICAgInR5cGUiIDogIkxpdGVyYWwiDQogICAgICAgICAgICAgICAgICB9LA0KICAgICAgICAgICAgICAgICAgImhvdXIiIDogew0KICAgICAgICAgICAgICAgICAgICAgInZhbHVlVHlwZSIgOiAie3VybjpobDctb3JnOmVsbS10eXBlczpyMX1JbnRlZ2VyIiwNCiAgICAgICAgICAgICAgICAgICAgICJ2YWx1ZSIgOiAiMCIsDQogICAgICAgICAgICAgICAgICAgICAidHlwZSIgOiAiTGl0ZXJhbCINCiAgICAgICAgICAgICAgICAgIH0sDQogICAgICAgICAgICAgICAgICAibWludXRlIiA6IHsNCiAgICAgICAgICAgICAgICAgICAgICJ2YWx1ZVR5cGUiIDogInt1cm46aGw3LW9yZzplbG0tdHlwZXM6cjF9SW50ZWdlciIsDQogICAgICAgICAgICAgICAgICAgICAidmFsdWUiIDogIjAiLA0KICAgICAgICAgICAgICAgICAgICAgInR5cGUiIDogIkxpdGVyYWwiDQogICAgICAgICAgICAgICAgICB9LA0KICAgICAgICAgICAgICAgICAgInNlY29uZCIgOiB7DQogICAgICAgICAgICAgICAgICAgICAidmFsdWVUeXBlIiA6ICJ7dXJuOmhsNy1vcmc6ZWxtLXR5cGVzOnIxfUludGVnZXIiLA0KICAgICAgICAgICAgICAgICAgICAgInZhbHVlIiA6ICIwIiwNCiAgICAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJMaXRlcmFsIg0KICAgICAgICAgICAgICAgICAgfSwNCiAgICAgICAgICAgICAgICAgICJtaWxsaXNlY29uZCIgOiB7DQogICAgICAgICAgICAgICAgICAgICAidmFsdWVUeXBlIiA6ICJ7dXJuOmhsNy1vcmc6ZWxtLXR5cGVzOnIxfUludGVnZXIiLA0KICAgICAgICAgICAgICAgICAgICAgInZhbHVlIiA6ICIwIiwNCiAgICAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJMaXRlcmFsIg0KICAgICAgICAgICAgICAgICAgfQ0KICAgICAgICAgICAgICAgfQ0KICAgICAgICAgICAgfSwNCiAgICAgICAgICAgICJwYXJhbWV0ZXJUeXBlU3BlY2lmaWVyIiA6IHsNCiAgICAgICAgICAgICAgICJsb2NhbElkIiA6ICI0NCIsDQogICAgICAgICAgICAgICAibG9jYXRvciIgOiAiNDY6MzItNDY6NDkiLA0KICAgICAgICAgICAgICAgInR5cGUiIDogIkludGVydmFsVHlwZVNwZWNpZmllciIsDQogICAgICAgICAgICAgICAicG9pbnRUeXBlIiA6IHsNCiAgICAgICAgICAgICAgICAgICJsb2NhbElkIiA6ICI0MyIsDQogICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiNDY6NDEtNDY6NDgiLA0KICAgICAgICAgICAgICAgICAgIm5hbWUiIDogInt1cm46aGw3LW9yZzplbG0tdHlwZXM6cjF9RGF0ZVRpbWUiLA0KICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk5hbWVkVHlwZVNwZWNpZmllciINCiAgICAgICAgICAgICAgIH0NCiAgICAgICAgICAgIH0NCiAgICAgICAgIH0gXQ0KICAgICAgfSwNCiAgICAgICJjb2RlU3lzdGVtcyIgOiB7DQogICAgICAgICAiZGVmIiA6IFsgew0KICAgICAgICAgICAgImxvY2FsSWQiIDogIjgiLA0KICAgICAgICAgICAgImxvY2F0b3IiIDogIjEyOjEtMTI6MzgiLA0KICAgICAgICAgICAgIm5hbWUiIDogIkxPSU5DIiwNCiAgICAgICAgICAgICJpZCIgOiAiaHR0cDovL2xvaW5jLm9yZyIsDQogICAgICAgICAgICAiYWNjZXNzTGV2ZWwiIDogIlB1YmxpYyIsDQogICAgICAgICAgICAiYW5ub3RhdGlvbiIgOiBbIHsNCiAgICAgICAgICAgICAgICJ0eXBlIiA6ICJBbm5vdGF0aW9uIiwNCiAgICAgICAgICAgICAgICJzIiA6IHsNCiAgICAgICAgICAgICAgICAgICJyIiA6ICI4IiwNCiAgICAgICAgICAgICAgICAgICJzIiA6IFsgew0KICAgICAgICAgICAgICAgICAgICAgInZhbHVlIiA6IFsgIiIsImNvZGVzeXN0ZW0gIiwiXCJMT0lOQ1wiIiwiOiAiLCInaHR0cDovL2xvaW5jLm9yZyciIF0NCiAgICAgICAgICAgICAgICAgIH0gXQ0KICAgICAgICAgICAgICAgfQ0KICAgICAgICAgICAgfSBdDQogICAgICAgICB9IF0NCiAgICAgIH0sDQogICAgICAidmFsdWVTZXRzIiA6IHsNCiAgICAgICAgICJkZWYiIDogWyB7DQogICAgICAgICAgICAibG9jYWxJZCIgOiAiOSIsDQogICAgICAgICAgICAibG9jYXRvciIgOiAiMTQ6MS0xNDoxMDciLA0KICAgICAgICAgICAgIm5hbWUiIDogIkFjdXRlIElucGF0aWVudCIsDQogICAgICAgICAgICAiaWQiIDogImh0dHA6Ly9jdHMubmxtLm5paC5nb3YvZmhpci9WYWx1ZVNldC8yLjE2Ljg0MC4xLjExMzg4My4zLjQ2NC4xMDAzLjEwMS4xMi4xMDgzIiwNCiAgICAgICAgICAgICJhY2Nlc3NMZXZlbCIgOiAiUHVibGljIiwNCiAgICAgICAgICAgICJhbm5vdGF0aW9uIiA6IFsgew0KICAgICAgICAgICAgICAgInR5cGUiIDogIkFubm90YXRpb24iLA0KICAgICAgICAgICAgICAgInMiIDogew0KICAgICAgICAgICAgICAgICAgInIiIDogIjkiLA0KICAgICAgICAgICAgICAgICAgInMiIDogWyB7DQogICAgICAgICAgICAgICAgICAgICAidmFsdWUiIDogWyAiIiwidmFsdWVzZXQgIiwiXCJBY3V0ZSBJbnBhdGllbnRcIiIsIjogIiwiJ2h0dHA6Ly9jdHMubmxtLm5paC5nb3YvZmhpci9WYWx1ZVNldC8yLjE2Ljg0MC4xLjExMzg4My4zLjQ2NC4xMDAzLjEwMS4xMi4xMDgzJyIgXQ0KICAgICAgICAgICAgICAgICAgfSBdDQogICAgICAgICAgICAgICB9DQogICAgICAgICAgICB9IF0NCiAgICAgICAgIH0sIHsNCiAgICAgICAgICAgICJsb2NhbElkIiA6ICIxMCIsDQogICAgICAgICAgICAibG9jYXRvciIgOiAiMTU6MS0xNToxMDgiLA0KICAgICAgICAgICAgIm5hbWUiIDogIkFkdmFuY2VkIElsbG5lc3MiLA0KICAgICAgICAgICAgImlkIiA6ICJodHRwOi8vY3RzLm5sbS5uaWguZ292L2ZoaXIvVmFsdWVTZXQvMi4xNi44NDAuMS4xMTM4ODMuMy40NjQuMTAwMy4xMTAuMTIuMTA4MiIsDQogICAgICAgICAgICAiYWNjZXNzTGV2ZWwiIDogIlB1YmxpYyIsDQogICAgICAgICAgICAiYW5ub3RhdGlvbiIgOiBbIHsNCiAgICAgICAgICAgICAgICJ0eXBlIiA6ICJBbm5vdGF0aW9uIiwNCiAgICAgICAgICAgICAgICJzIiA6IHsNCiAgICAgICAgICAgICAgICAgICJyIiA6ICIxMCIsDQogICAgICAgICAgICAgICAgICAicyIgOiBbIHsNCiAgICAgICAgICAgICAgICAgICAgICJ2YWx1ZSIgOiBbICIiLCJ2YWx1ZXNldCAiLCJcIkFkdmFuY2VkIElsbG5lc3NcIiIsIjogIiwiJ2h0dHA6Ly9jdHMubmxtLm5paC5nb3YvZmhpci9WYWx1ZVNldC8yLjE2Ljg0MC4xLjExMzg4My4zLjQ2NC4xMDAzLjExMC4xMi4xMDgyJyIgXQ0KICAgICAgICAgICAgICAgICAgfSBdDQogICAgICAgICAgICAgICB9DQogICAgICAgICAgICB9IF0NCiAgICAgICAgIH0sIHsNCiAgICAgICAgICAgICJsb2NhbElkIiA6ICIxMSIsDQogICAgICAgICAgICAibG9jYXRvciIgOiAiMTY6MS0xNjoxMDMiLA0KICAgICAgICAgICAgIm5hbWUiIDogIkFubnVhbCBXZWxsbmVzcyBWaXNpdCIsDQogICAgICAgICAgICAiaWQiIDogImh0dHA6Ly9jdHMubmxtLm5paC5nb3YvZmhpci9WYWx1ZVNldC8yLjE2Ljg0MC4xLjExMzg4My4zLjUyNi4zLjEyNDAiLA0KICAgICAgICAgICAgImFjY2Vzc0xldmVsIiA6ICJQdWJsaWMiLA0KICAgICAgICAgICAgImFubm90YXRpb24iIDogWyB7DQogICAgICAgICAgICAgICAidHlwZSIgOiAiQW5ub3RhdGlvbiIsDQogICAgICAgICAgICAgICAicyIgOiB7DQogICAgICAgICAgICAgICAgICAiciIgOiAiMTEiLA0KICAgICAgICAgICAgICAgICAgInMiIDogWyB7DQogICAgICAgICAgICAgICAgICAgICAidmFsdWUiIDogWyAiIiwidmFsdWVzZXQgIiwiXCJBbm51YWwgV2VsbG5lc3MgVmlzaXRcIiIsIjogIiwiJ2h0dHA6Ly9jdHMubmxtLm5paC5nb3YvZmhpci9WYWx1ZVNldC8yLjE2Ljg0MC4xLjExMzg4My4zLjUyNi4zLjEyNDAnIiBdDQogICAgICAgICAgICAgICAgICB9IF0NCiAgICAgICAgICAgICAgIH0NCiAgICAgICAgICAgIH0gXQ0KICAgICAgICAgfSwgew0KICAgICAgICAgICAgImxvY2FsSWQiIDogIjEyIiwNCiAgICAgICAgICAgICJsb2NhdG9yIiA6ICIxNzoxLTE3OjEzOSIsDQogICAgICAgICAgICAibmFtZSIgOiAiQ2FyZSBTZXJ2aWNlcyBpbiBMb25nLVRlcm0gUmVzaWRlbnRpYWwgRmFjaWxpdHkiLA0KICAgICAgICAgICAgImlkIiA6ICJodHRwOi8vY3RzLm5sbS5uaWguZ292L2ZoaXIvVmFsdWVTZXQvMi4xNi44NDAuMS4xMTM4ODMuMy40NjQuMTAwMy4xMDEuMTIuMTAxNCIsDQogICAgICAgICAgICAiYWNjZXNzTGV2ZWwiIDogIlB1YmxpYyIsDQogICAgICAgICAgICAiYW5ub3RhdGlvbiIgOiBbIHsNCiAgICAgICAgICAgICAgICJ0eXBlIiA6ICJBbm5vdGF0aW9uIiwNCiAgICAgICAgICAgICAgICJzIiA6IHsNCiAgICAgICAgICAgICAgICAgICJyIiA6ICIxMiIsDQogICAgICAgICAgICAgICAgICAicyIgOiBbIHsNCiAgICAgICAgICAgICAgICAgICAgICJ2YWx1ZSIgOiBbICIiLCJ2YWx1ZXNldCAiLCJcIkNhcmUgU2VydmljZXMgaW4gTG9uZy1UZXJtIFJlc2lkZW50aWFsIEZhY2lsaXR5XCIiLCI6ICIsIidodHRwOi8vY3RzLm5sbS5uaWguZ292L2ZoaXIvVmFsdWVTZXQvMi4xNi44NDAuMS4xMTM4ODMuMy40NjQuMTAwMy4xMDEuMTIuMTAxNCciIF0NCiAgICAgICAgICAgICAgICAgIH0gXQ0KICAgICAgICAgICAgICAgfQ0KICAgICAgICAgICAgfSBdDQogICAgICAgICB9LCB7DQogICAgICAgICAgICAibG9jYWxJZCIgOiAiMTMiLA0KICAgICAgICAgICAgImxvY2F0b3IiIDogIjE4OjEtMTg6MTAzIiwNCiAgICAgICAgICAgICJuYW1lIiA6ICJDb2xvbm9zY29weSIsDQogICAgICAgICAgICAiaWQiIDogImh0dHA6Ly9jdHMubmxtLm5paC5nb3YvZmhpci9WYWx1ZVNldC8yLjE2Ljg0MC4xLjExMzg4My4zLjQ2NC4xMDAzLjEwOC4xMi4xMDIwIiwNCiAgICAgICAgICAgICJhY2Nlc3NMZXZlbCIgOiAiUHVibGljIiwNCiAgICAgICAgICAgICJhbm5vdGF0aW9uIiA6IFsgew0KICAgICAgICAgICAgICAgInR5cGUiIDogIkFubm90YXRpb24iLA0KICAgICAgICAgICAgICAgInMiIDogew0KICAgICAgICAgICAgICAgICAgInIiIDogIjEzIiwNCiAgICAgICAgICAgICAgICAgICJzIiA6IFsgew0KICAgICAgICAgICAgICAgICAgICAgInZhbHVlIiA6IFsgIiIsInZhbHVlc2V0ICIsIlwiQ29sb25vc2NvcHlcIiIsIjogIiwiJ2h0dHA6Ly9jdHMubmxtLm5paC5nb3YvZmhpci9WYWx1ZVNldC8yLjE2Ljg0MC4xLjExMzg4My4zLjQ2NC4xMDAzLjEwOC4xMi4xMDIwJyIgXQ0KICAgICAgICAgICAgICAgICAgfSBdDQogICAgICAgICAgICAgICB9DQogICAgICAgICAgICB9IF0NCiAgICAgICAgIH0sIHsNCiAgICAgICAgICAgICJsb2NhbElkIiA6ICIxNCIsDQogICAgICAgICAgICAibG9jYXRvciIgOiAiMTk6MS0xOToxMDciLA0KICAgICAgICAgICAgIm5hbWUiIDogIkNUIENvbG9ub2dyYXBoeSIsDQogICAgICAgICAgICAiaWQiIDogImh0dHA6Ly9jdHMubmxtLm5paC5nb3YvZmhpci9WYWx1ZVNldC8yLjE2Ljg0MC4xLjExMzg4My4zLjQ2NC4xMDAzLjEwOC4xMi4xMDM4IiwNCiAgICAgICAgICAgICJhY2Nlc3NMZXZlbCIgOiAiUHVibGljIiwNCiAgICAgICAgICAgICJhbm5vdGF0aW9uIiA6IFsgew0KICAgICAgICAgICAgICAgInR5cGUiIDogIkFubm90YXRpb24iLA0KICAgICAgICAgICAgICAgInMiIDogew0KICAgICAgICAgICAgICAgICAgInIiIDogIjE0IiwNCiAgICAgICAgICAgICAgICAgICJzIiA6IFsgew0KICAgICAgICAgICAgICAgICAgICAgInZhbHVlIiA6IFsgIiIsInZhbHVlc2V0ICIsIlwiQ1QgQ29sb25vZ3JhcGh5XCIiLCI6ICIsIidodHRwOi8vY3RzLm5sbS5uaWguZ292L2ZoaXIvVmFsdWVTZXQvMi4xNi44NDAuMS4xMTM4ODMuMy40NjQuMTAwMy4xMDguMTIuMTAzOCciIF0NCiAgICAgICAgICAgICAgICAgIH0gXQ0KICAgICAgICAgICAgICAgfQ0KICAgICAgICAgICAgfSBdDQogICAgICAgICB9LCB7DQogICAgICAgICAgICAibG9jYWxJZCIgOiAiMTUiLA0KICAgICAgICAgICAgImxvY2F0b3IiIDogIjIwOjEtMjA6MTEyIiwNCiAgICAgICAgICAgICJuYW1lIiA6ICJEZW1lbnRpYSBNZWRpY2F0aW9ucyIsDQogICAgICAgICAgICAiaWQiIDogImh0dHA6Ly9jdHMubmxtLm5paC5nb3YvZmhpci9WYWx1ZVNldC8yLjE2Ljg0MC4xLjExMzg4My4zLjQ2NC4xMDAzLjE5Ni4xMi4xNTEwIiwNCiAgICAgICAgICAgICJhY2Nlc3NMZXZlbCIgOiAiUHVibGljIiwNCiAgICAgICAgICAgICJhbm5vdGF0aW9uIiA6IFsgew0KICAgICAgICAgICAgICAgInR5cGUiIDogIkFubm90YXRpb24iLA0KICAgICAgICAgICAgICAgInMiIDogew0KICAgICAgICAgICAgICAgICAgInIiIDogIjE1IiwNCiAgICAgICAgICAgICAgICAgICJzIiA6IFsgew0KICAgICAgICAgICAgICAgICAgICAgInZhbHVlIiA6IFsgIiIsInZhbHVlc2V0ICIsIlwiRGVtZW50aWEgTWVkaWNhdGlvbnNcIiIsIjogIiwiJ2h0dHA6Ly9jdHMubmxtLm5paC5nb3YvZmhpci9WYWx1ZVNldC8yLjE2Ljg0MC4xLjExMzg4My4zLjQ2NC4xMDAzLjE5Ni4xMi4xNTEwJyIgXQ0KICAgICAgICAgICAgICAgICAgfSBdDQogICAgICAgICAgICAgICB9DQogICAgICAgICAgICB9IF0NCiAgICAgICAgIH0sIHsNCiAgICAgICAgICAgICJsb2NhbElkIiA6ICIxNiIsDQogICAgICAgICAgICAibG9jYXRvciIgOiAiMjE6MS0yMToxMzYiLA0KICAgICAgICAgICAgIm5hbWUiIDogIkRpc2NoYXJnZWQgdG8gSGVhbHRoIENhcmUgRmFjaWxpdHkgZm9yIEhvc3BpY2UgQ2FyZSIsDQogICAgICAgICAgICAiaWQiIDogImh0dHA6Ly9jdHMubmxtLm5paC5nb3YvZmhpci9WYWx1ZVNldC8yLjE2Ljg0MC4xLjExMzg4My4zLjExNy4xLjcuMS4yMDciLA0KICAgICAgICAgICAgImFjY2Vzc0xldmVsIiA6ICJQdWJsaWMiLA0KICAgICAgICAgICAgImFubm90YXRpb24iIDogWyB7DQogICAgICAgICAgICAgICAidHlwZSIgOiAiQW5ub3RhdGlvbiIsDQogICAgICAgICAgICAgICAicyIgOiB7DQogICAgICAgICAgICAgICAgICAiciIgOiAiMTYiLA0KICAgICAgICAgICAgICAgICAgInMiIDogWyB7DQogICAgICAgICAgICAgICAgICAgICAidmFsdWUiIDogWyAiIiwidmFsdWVzZXQgIiwiXCJEaXNjaGFyZ2VkIHRvIEhlYWx0aCBDYXJlIEZhY2lsaXR5IGZvciBIb3NwaWNlIENhcmVcIiIsIjogIiwiJ2h0dHA6Ly9jdHMubmxtLm5paC5nb3YvZmhpci9WYWx1ZVNldC8yLjE2Ljg0MC4xLjExMzg4My4zLjExNy4xLjcuMS4yMDcnIiBdDQogICAgICAgICAgICAgICAgICB9IF0NCiAgICAgICAgICAgICAgIH0NCiAgICAgICAgICAgIH0gXQ0KICAgICAgICAgfSwgew0KICAgICAgICAgICAgImxvY2FsSWQiIDogIjE3IiwNCiAgICAgICAgICAgICJsb2NhdG9yIiA6ICIyMjoxLTIyOjEyMCIsDQogICAgICAgICAgICAibmFtZSIgOiAiRGlzY2hhcmdlZCB0byBIb21lIGZvciBIb3NwaWNlIENhcmUiLA0KICAgICAgICAgICAgImlkIiA6ICJodHRwOi8vY3RzLm5sbS5uaWguZ292L2ZoaXIvVmFsdWVTZXQvMi4xNi44NDAuMS4xMTM4ODMuMy4xMTcuMS43LjEuMjA5IiwNCiAgICAgICAgICAgICJhY2Nlc3NMZXZlbCIgOiAiUHVibGljIiwNCiAgICAgICAgICAgICJhbm5vdGF0aW9uIiA6IFsgew0KICAgICAgICAgICAgICAgInR5cGUiIDogIkFubm90YXRpb24iLA0KICAgICAgICAgICAgICAgInMiIDogew0KICAgICAgICAgICAgICAgICAgInIiIDogIjE3IiwNCiAgICAgICAgICAgICAgICAgICJzIiA6IFsgew0KICAgICAgICAgICAgICAgICAgICAgInZhbHVlIiA6IFsgIiIsInZhbHVlc2V0ICIsIlwiRGlzY2hhcmdlZCB0byBIb21lIGZvciBIb3NwaWNlIENhcmVcIiIsIjogIiwiJ2h0dHA6Ly9jdHMubmxtLm5paC5nb3YvZmhpci9WYWx1ZVNldC8yLjE2Ljg0MC4xLjExMzg4My4zLjExNy4xLjcuMS4yMDknIiBdDQogICAgICAgICAgICAgICAgICB9IF0NCiAgICAgICAgICAgICAgIH0NCiAgICAgICAgICAgIH0gXQ0KICAgICAgICAgfSwgew0KICAgICAgICAgICAgImxvY2FsSWQiIDogIjE4IiwNCiAgICAgICAgICAgICJsb2NhdG9yIiA6ICIyMzoxLTIzOjk0IiwNCiAgICAgICAgICAgICJuYW1lIiA6ICJFRCIsDQogICAgICAgICAgICAiaWQiIDogImh0dHA6Ly9jdHMubmxtLm5paC5nb3YvZmhpci9WYWx1ZVNldC8yLjE2Ljg0MC4xLjExMzg4My4zLjQ2NC4xMDAzLjEwMS4xMi4xMDg1IiwNCiAgICAgICAgICAgICJhY2Nlc3NMZXZlbCIgOiAiUHVibGljIiwNCiAgICAgICAgICAgICJhbm5vdGF0aW9uIiA6IFsgew0KICAgICAgICAgICAgICAgInR5cGUiIDogIkFubm90YXRpb24iLA0KICAgICAgICAgICAgICAgInMiIDogew0KICAgICAgICAgICAgICAgICAgInIiIDogIjE4IiwNCiAgICAgICAgICAgICAgICAgICJzIiA6IFsgew0KICAgICAgICAgICAgICAgICAgICAgInZhbHVlIiA6IFsgIiIsInZhbHVlc2V0ICIsIlwiRURcIiIsIjogIiwiJ2h0dHA6Ly9jdHMubmxtLm5paC5nb3YvZmhpci9WYWx1ZVNldC8yLjE2Ljg0MC4xLjExMzg4My4zLjQ2NC4xMDAzLjEwMS4xMi4xMDg1JyIgXQ0KICAgICAgICAgICAgICAgICAgfSBdDQogICAgICAgICAgICAgICB9DQogICAgICAgICAgICB9IF0NCiAgICAgICAgIH0sIHsNCiAgICAgICAgICAgICJsb2NhbElkIiA6ICIxOSIsDQogICAgICAgICAgICAibG9jYXRvciIgOiAiMjQ6MS0yNDoxMDAiLA0KICAgICAgICAgICAgIm5hbWUiIDogIkVuY291bnRlciBJbnBhdGllbnQiLA0KICAgICAgICAgICAgImlkIiA6ICJodHRwOi8vY3RzLm5sbS5uaWguZ292L2ZoaXIvVmFsdWVTZXQvMi4xNi44NDAuMS4xMTM4ODMuMy42NjYuNS4zMDciLA0KICAgICAgICAgICAgImFjY2Vzc0xldmVsIiA6ICJQdWJsaWMiLA0KICAgICAgICAgICAgImFubm90YXRpb24iIDogWyB7DQogICAgICAgICAgICAgICAidHlwZSIgOiAiQW5ub3RhdGlvbiIsDQogICAgICAgICAgICAgICAicyIgOiB7DQogICAgICAgICAgICAgICAgICAiciIgOiAiMTkiLA0KICAgICAgICAgICAgICAgICAgInMiIDogWyB7DQogICAgICAgICAgICAgICAgICAgICAidmFsdWUiIDogWyAiIiwidmFsdWVzZXQgIiwiXCJFbmNvdW50ZXIgSW5wYXRpZW50XCIiLCI6ICIsIidodHRwOi8vY3RzLm5sbS5uaWguZ292L2ZoaXIvVmFsdWVTZXQvMi4xNi44NDAuMS4xMTM4ODMuMy42NjYuNS4zMDcnIiBdDQogICAgICAgICAgICAgICAgICB9IF0NCiAgICAgICAgICAgICAgIH0NCiAgICAgICAgICAgIH0gXQ0KICAgICAgICAgfSwgew0KICAgICAgICAgICAgImxvY2FsSWQiIDogIjIwIiwNCiAgICAgICAgICAgICJsb2NhdG9yIiA6ICIyNToxLTI1OjEyMiIsDQogICAgICAgICAgICAibmFtZSIgOiAiRmVjYWwgT2NjdWx0IEJsb29kIFRlc3QgKEZPQlQpIiwNCiAgICAgICAgICAgICJpZCIgOiAiaHR0cDovL2N0cy5ubG0ubmloLmdvdi9maGlyL1ZhbHVlU2V0LzIuMTYuODQwLjEuMTEzODgzLjMuNDY0LjEwMDMuMTk4LjEyLjEwMTEiLA0KICAgICAgICAgICAgImFjY2Vzc0xldmVsIiA6ICJQdWJsaWMiLA0KICAgICAgICAgICAgImFubm90YXRpb24iIDogWyB7DQogICAgICAgICAgICAgICAidHlwZSIgOiAiQW5ub3RhdGlvbiIsDQogICAgICAgICAgICAgICAicyIgOiB7DQogICAgICAgICAgICAgICAgICAiciIgOiAiMjAiLA0KICAgICAgICAgICAgICAgICAgInMiIDogWyB7DQogICAgICAgICAgICAgICAgICAgICAidmFsdWUiIDogWyAiIiwidmFsdWVzZXQgIiwiXCJGZWNhbCBPY2N1bHQgQmxvb2QgVGVzdCAoRk9CVClcIiIsIjogIiwiJ2h0dHA6Ly9jdHMubmxtLm5paC5nb3YvZmhpci9WYWx1ZVNldC8yLjE2Ljg0MC4xLjExMzg4My4zLjQ2NC4xMDAzLjE5OC4xMi4xMDExJyIgXQ0KICAgICAgICAgICAgICAgICAgfSBdDQogICAgICAgICAgICAgICB9DQogICAgICAgICAgICB9IF0NCiAgICAgICAgIH0sIHsNCiAgICAgICAgICAgICJsb2NhbElkIiA6ICIyMSIsDQogICAgICAgICAgICAibG9jYXRvciIgOiAiMjY6MS0yNjo5OSIsDQogICAgICAgICAgICAibmFtZSIgOiAiRklUIEROQSIsDQogICAgICAgICAgICAiaWQiIDogImh0dHA6Ly9jdHMubmxtLm5paC5nb3YvZmhpci9WYWx1ZVNldC8yLjE2Ljg0MC4xLjExMzg4My4zLjQ2NC4xMDAzLjEwOC4xMi4xMDM5IiwNCiAgICAgICAgICAgICJhY2Nlc3NMZXZlbCIgOiAiUHVibGljIiwNCiAgICAgICAgICAgICJhbm5vdGF0aW9uIiA6IFsgew0KICAgICAgICAgICAgICAgInR5cGUiIDogIkFubm90YXRpb24iLA0KICAgICAgICAgICAgICAgInMiIDogew0KICAgICAgICAgICAgICAgICAgInIiIDogIjIxIiwNCiAgICAgICAgICAgICAgICAgICJzIiA6IFsgew0KICAgICAgICAgICAgICAgICAgICAgInZhbHVlIiA6IFsgIiIsInZhbHVlc2V0ICIsIlwiRklUIEROQVwiIiwiOiAiLCInaHR0cDovL2N0cy5ubG0ubmloLmdvdi9maGlyL1ZhbHVlU2V0LzIuMTYuODQwLjEuMTEzODgzLjMuNDY0LjEwMDMuMTA4LjEyLjEwMzknIiBdDQogICAgICAgICAgICAgICAgICB9IF0NCiAgICAgICAgICAgICAgIH0NCiAgICAgICAgICAgIH0gXQ0KICAgICAgICAgfSwgew0KICAgICAgICAgICAgImxvY2FsSWQiIDogIjIyIiwNCiAgICAgICAgICAgICJsb2NhdG9yIiA6ICIyNzoxLTI3OjExNCIsDQogICAgICAgICAgICAibmFtZSIgOiAiRmxleGlibGUgU2lnbW9pZG9zY29weSIsDQogICAgICAgICAgICAiaWQiIDogImh0dHA6Ly9jdHMubmxtLm5paC5nb3YvZmhpci9WYWx1ZVNldC8yLjE2Ljg0MC4xLjExMzg4My4zLjQ2NC4xMDAzLjE5OC4xMi4xMDEwIiwNCiAgICAgICAgICAgICJhY2Nlc3NMZXZlbCIgOiAiUHVibGljIiwNCiAgICAgICAgICAgICJhbm5vdGF0aW9uIiA6IFsgew0KICAgICAgICAgICAgICAgInR5cGUiIDogIkFubm90YXRpb24iLA0KICAgICAgICAgICAgICAgInMiIDogew0KICAgICAgICAgICAgICAgICAgInIiIDogIjIyIiwNCiAgICAgICAgICAgICAgICAgICJzIiA6IFsgew0KICAgICAgICAgICAgICAgICAgICAgInZhbHVlIiA6IFsgIiIsInZhbHVlc2V0ICIsIlwiRmxleGlibGUgU2lnbW9pZG9zY29weVwiIiwiOiAiLCInaHR0cDovL2N0cy5ubG0ubmloLmdvdi9maGlyL1ZhbHVlU2V0LzIuMTYuODQwLjEuMTEzODgzLjMuNDY0LjEwMDMuMTk4LjEyLjEwMTAnIiBdDQogICAgICAgICAgICAgICAgICB9IF0NCiAgICAgICAgICAgICAgIH0NCiAgICAgICAgICAgIH0gXQ0KICAgICAgICAgfSwgew0KICAgICAgICAgICAgImxvY2FsSWQiIDogIjIzIiwNCiAgICAgICAgICAgICJsb2NhdG9yIiA6ICIyODoxLTI4OjEwNiIsDQogICAgICAgICAgICAibmFtZSIgOiAiRnJhaWx0eSBEZXZpY2UiLA0KICAgICAgICAgICAgImlkIiA6ICJodHRwOi8vY3RzLm5sbS5uaWguZ292L2ZoaXIvVmFsdWVTZXQvMi4xNi44NDAuMS4xMTM4ODMuMy40NjQuMTAwMy4xMTguMTIuMTMwMCIsDQogICAgICAgICAgICAiYWNjZXNzTGV2ZWwiIDogIlB1YmxpYyIsDQogICAgICAgICAgICAiYW5ub3RhdGlvbiIgOiBbIHsNCiAgICAgICAgICAgICAgICJ0eXBlIiA6ICJBbm5vdGF0aW9uIiwNCiAgICAgICAgICAgICAgICJzIiA6IHsNCiAgICAgICAgICAgICAgICAgICJyIiA6ICIyMyIsDQogICAgICAgICAgICAgICAgICAicyIgOiBbIHsNCiAgICAgICAgICAgICAgICAgICAgICJ2YWx1ZSIgOiBbICIiLCJ2YWx1ZXNldCAiLCJcIkZyYWlsdHkgRGV2aWNlXCIiLCI6ICIsIidodHRwOi8vY3RzLm5sbS5uaWguZ292L2ZoaXIvVmFsdWVTZXQvMi4xNi44NDAuMS4xMTM4ODMuMy40NjQuMTAwMy4xMTguMTIuMTMwMCciIF0NCiAgICAgICAgICAgICAgICAgIH0gXQ0KICAgICAgICAgICAgICAgfQ0KICAgICAgICAgICAgfSBdDQogICAgICAgICB9LCB7DQogICAgICAgICAgICAibG9jYWxJZCIgOiAiMjQiLA0KICAgICAgICAgICAgImxvY2F0b3IiIDogIjI5OjEtMjk6MTA5IiwNCiAgICAgICAgICAgICJuYW1lIiA6ICJGcmFpbHR5IERpYWdub3NpcyIsDQogICAgICAgICAgICAiaWQiIDogImh0dHA6Ly9jdHMubmxtLm5paC5nb3YvZmhpci9WYWx1ZVNldC8yLjE2Ljg0MC4xLjExMzg4My4zLjQ2NC4xMDAzLjExMy4xMi4xMDc0IiwNCiAgICAgICAgICAgICJhY2Nlc3NMZXZlbCIgOiAiUHVibGljIiwNCiAgICAgICAgICAgICJhbm5vdGF0aW9uIiA6IFsgew0KICAgICAgICAgICAgICAgInR5cGUiIDogIkFubm90YXRpb24iLA0KICAgICAgICAgICAgICAgInMiIDogew0KICAgICAgICAgICAgICAgICAgInIiIDogIjI0IiwNCiAgICAgICAgICAgICAgICAgICJzIiA6IFsgew0KICAgICAgICAgICAgICAgICAgICAgInZhbHVlIiA6IFsgIiIsInZhbHVlc2V0ICIsIlwiRnJhaWx0eSBEaWFnbm9zaXNcIiIsIjogIiwiJ2h0dHA6Ly9jdHMubmxtLm5paC5nb3YvZmhpci9WYWx1ZVNldC8yLjE2Ljg0MC4xLjExMzg4My4zLjQ2NC4xMDAzLjExMy4xMi4xMDc0JyIgXQ0KICAgICAgICAgICAgICAgICAgfSBdDQogICAgICAgICAgICAgICB9DQogICAgICAgICAgICB9IF0NCiAgICAgICAgIH0sIHsNCiAgICAgICAgICAgICJsb2NhbElkIiA6ICIyNSIsDQogICAgICAgICAgICAibG9jYXRvciIgOiAiMzA6MS0zMDoxMDkiLA0KICAgICAgICAgICAgIm5hbWUiIDogIkZyYWlsdHkgRW5jb3VudGVyIiwNCiAgICAgICAgICAgICJpZCIgOiAiaHR0cDovL2N0cy5ubG0ubmloLmdvdi9maGlyL1ZhbHVlU2V0LzIuMTYuODQwLjEuMTEzODgzLjMuNDY0LjEwMDMuMTAxLjEyLjEwODgiLA0KICAgICAgICAgICAgImFjY2Vzc0xldmVsIiA6ICJQdWJsaWMiLA0KICAgICAgICAgICAgImFubm90YXRpb24iIDogWyB7DQogICAgICAgICAgICAgICAidHlwZSIgOiAiQW5ub3RhdGlvbiIsDQogICAgICAgICAgICAgICAicyIgOiB7DQogICAgICAgICAgICAgICAgICAiciIgOiAiMjUiLA0KICAgICAgICAgICAgICAgICAgInMiIDogWyB7DQogICAgICAgICAgICAgICAgICAgICAidmFsdWUiIDogWyAiIiwidmFsdWVzZXQgIiwiXCJGcmFpbHR5IEVuY291bnRlclwiIiwiOiAiLCInaHR0cDovL2N0cy5ubG0ubmloLmdvdi9maGlyL1ZhbHVlU2V0LzIuMTYuODQwLjEuMTEzODgzLjMuNDY0LjEwMDMuMTAxLjEyLjEwODgnIiBdDQogICAgICAgICAgICAgICAgICB9IF0NCiAgICAgICAgICAgICAgIH0NCiAgICAgICAgICAgIH0gXQ0KICAgICAgICAgfSwgew0KICAgICAgICAgICAgImxvY2FsSWQiIDogIjI2IiwNCiAgICAgICAgICAgICJsb2NhdG9yIiA6ICIzMToxLTMxOjEwNyIsDQogICAgICAgICAgICAibmFtZSIgOiAiRnJhaWx0eSBTeW1wdG9tIiwNCiAgICAgICAgICAgICJpZCIgOiAiaHR0cDovL2N0cy5ubG0ubmloLmdvdi9maGlyL1ZhbHVlU2V0LzIuMTYuODQwLjEuMTEzODgzLjMuNDY0LjEwMDMuMTEzLjEyLjEwNzUiLA0KICAgICAgICAgICAgImFjY2Vzc0xldmVsIiA6ICJQdWJsaWMiLA0KICAgICAgICAgICAgImFubm90YXRpb24iIDogWyB7DQogICAgICAgICAgICAgICAidHlwZSIgOiAiQW5ub3RhdGlvbiIsDQogICAgICAgICAgICAgICAicyIgOiB7DQogICAgICAgICAgICAgICAgICAiciIgOiAiMjYiLA0KICAgICAgICAgICAgICAgICAgInMiIDogWyB7DQogICAgICAgICAgICAgICAgICAgICAidmFsdWUiIDogWyAiIiwidmFsdWVzZXQgIiwiXCJGcmFpbHR5IFN5bXB0b21cIiIsIjogIiwiJ2h0dHA6Ly9jdHMubmxtLm5paC5nb3YvZmhpci9WYWx1ZVNldC8yLjE2Ljg0MC4xLjExMzg4My4zLjQ2NC4xMDAzLjExMy4xMi4xMDc1JyIgXQ0KICAgICAgICAgICAgICAgICAgfSBdDQogICAgICAgICAgICAgICB9DQogICAgICAgICAgICB9IF0NCiAgICAgICAgIH0sIHsNCiAgICAgICAgICAgICJsb2NhbElkIiA6ICIyNyIsDQogICAgICAgICAgICAibG9jYXRvciIgOiAiMzI6MS0zMjoxMTYiLA0KICAgICAgICAgICAgIm5hbWUiIDogIkhvbWUgSGVhbHRoY2FyZSBTZXJ2aWNlcyIsDQogICAgICAgICAgICAiaWQiIDogImh0dHA6Ly9jdHMubmxtLm5paC5nb3YvZmhpci9WYWx1ZVNldC8yLjE2Ljg0MC4xLjExMzg4My4zLjQ2NC4xMDAzLjEwMS4xMi4xMDE2IiwNCiAgICAgICAgICAgICJhY2Nlc3NMZXZlbCIgOiAiUHVibGljIiwNCiAgICAgICAgICAgICJhbm5vdGF0aW9uIiA6IFsgew0KICAgICAgICAgICAgICAgInR5cGUiIDogIkFubm90YXRpb24iLA0KICAgICAgICAgICAgICAgInMiIDogew0KICAgICAgICAgICAgICAgICAgInIiIDogIjI3IiwNCiAgICAgICAgICAgICAgICAgICJzIiA6IFsgew0KICAgICAgICAgICAgICAgICAgICAgInZhbHVlIiA6IFsgIiIsInZhbHVlc2V0ICIsIlwiSG9tZSBIZWFsdGhjYXJlIFNlcnZpY2VzXCIiLCI6ICIsIidodHRwOi8vY3RzLm5sbS5uaWguZ292L2ZoaXIvVmFsdWVTZXQvMi4xNi44NDAuMS4xMTM4ODMuMy40NjQuMTAwMy4xMDEuMTIuMTAxNiciIF0NCiAgICAgICAgICAgICAgICAgIH0gXQ0KICAgICAgICAgICAgICAgfQ0KICAgICAgICAgICAgfSBdDQogICAgICAgICB9LCB7DQogICAgICAgICAgICAibG9jYWxJZCIgOiAiMjgiLA0KICAgICAgICAgICAgImxvY2F0b3IiIDogIjMzOjEtMzM6MTA0IiwNCiAgICAgICAgICAgICJuYW1lIiA6ICJIb3NwaWNlIGNhcmUgYW1idWxhdG9yeSIsDQogICAgICAgICAgICAiaWQiIDogImh0dHA6Ly9jdHMubmxtLm5paC5nb3YvZmhpci9WYWx1ZVNldC8yLjE2Ljg0MC4xLjExMzc2Mi4xLjQuMTEwOC4xNSIsDQogICAgICAgICAgICAiYWNjZXNzTGV2ZWwiIDogIlB1YmxpYyIsDQogICAgICAgICAgICAiYW5ub3RhdGlvbiIgOiBbIHsNCiAgICAgICAgICAgICAgICJ0eXBlIiA6ICJBbm5vdGF0aW9uIiwNCiAgICAgICAgICAgICAgICJzIiA6IHsNCiAgICAgICAgICAgICAgICAgICJyIiA6ICIyOCIsDQogICAgICAgICAgICAgICAgICAicyIgOiBbIHsNCiAgICAgICAgICAgICAgICAgICAgICJ2YWx1ZSIgOiBbICIiLCJ2YWx1ZXNldCAiLCJcIkhvc3BpY2UgY2FyZSBhbWJ1bGF0b3J5XCIiLCI6ICIsIidodHRwOi8vY3RzLm5sbS5uaWguZ292L2ZoaXIvVmFsdWVTZXQvMi4xNi44NDAuMS4xMTM3NjIuMS40LjExMDguMTUnIiBdDQogICAgICAgICAgICAgICAgICB9IF0NCiAgICAgICAgICAgICAgIH0NCiAgICAgICAgICAgIH0gXQ0KICAgICAgICAgfSwgew0KICAgICAgICAgICAgImxvY2FsSWQiIDogIjI5IiwNCiAgICAgICAgICAgICJsb2NhdG9yIiA6ICIzNDoxLTM0OjExOSIsDQogICAgICAgICAgICAibmFtZSIgOiAiTWFsaWduYW50IE5lb3BsYXNtIG9mIENvbG9uIiwNCiAgICAgICAgICAgICJpZCIgOiAiaHR0cDovL2N0cy5ubG0ubmloLmdvdi9maGlyL1ZhbHVlU2V0LzIuMTYuODQwLjEuMTEzODgzLjMuNDY0LjEwMDMuMTA4LjEyLjEwMDEiLA0KICAgICAgICAgICAgImFjY2Vzc0xldmVsIiA6ICJQdWJsaWMiLA0KICAgICAgICAgICAgImFubm90YXRpb24iIDogWyB7DQogICAgICAgICAgICAgICAidHlwZSIgOiAiQW5ub3RhdGlvbiIsDQogICAgICAgICAgICAgICAicyIgOiB7DQogICAgICAgICAgICAgICAgICAiciIgOiAiMjkiLA0KICAgICAgICAgICAgICAgICAgInMiIDogWyB7DQogICAgICAgICAgICAgICAgICAgICAidmFsdWUiIDogWyAiIiwidmFsdWVzZXQgIiwiXCJNYWxpZ25hbnQgTmVvcGxhc20gb2YgQ29sb25cIiIsIjogIiwiJ2h0dHA6Ly9jdHMubmxtLm5paC5nb3YvZmhpci9WYWx1ZVNldC8yLjE2Ljg0MC4xLjExMzg4My4zLjQ2NC4xMDAzLjEwOC4xMi4xMDAxJyIgXQ0KICAgICAgICAgICAgICAgICAgfSBdDQogICAgICAgICAgICAgICB9DQogICAgICAgICAgICB9IF0NCiAgICAgICAgIH0sIHsNCiAgICAgICAgICAgICJsb2NhbElkIiA6ICIzMCIsDQogICAgICAgICAgICAibG9jYXRvciIgOiAiMzU6MS0zNToxMTAiLA0KICAgICAgICAgICAgIm5hbWUiIDogIk5vbmFjdXRlIElucGF0aWVudCIsDQogICAgICAgICAgICAiaWQiIDogImh0dHA6Ly9jdHMubmxtLm5paC5nb3YvZmhpci9WYWx1ZVNldC8yLjE2Ljg0MC4xLjExMzg4My4zLjQ2NC4xMDAzLjEwMS4xMi4xMDg0IiwNCiAgICAgICAgICAgICJhY2Nlc3NMZXZlbCIgOiAiUHVibGljIiwNCiAgICAgICAgICAgICJhbm5vdGF0aW9uIiA6IFsgew0KICAgICAgICAgICAgICAgInR5cGUiIDogIkFubm90YXRpb24iLA0KICAgICAgICAgICAgICAgInMiIDogew0KICAgICAgICAgICAgICAgICAgInIiIDogIjMwIiwNCiAgICAgICAgICAgICAgICAgICJzIiA6IFsgew0KICAgICAgICAgICAgICAgICAgICAgInZhbHVlIiA6IFsgIiIsInZhbHVlc2V0ICIsIlwiTm9uYWN1dGUgSW5wYXRpZW50XCIiLCI6ICIsIidodHRwOi8vY3RzLm5sbS5uaWguZ292L2ZoaXIvVmFsdWVTZXQvMi4xNi44NDAuMS4xMTM4ODMuMy40NjQuMTAwMy4xMDEuMTIuMTA4NCciIF0NCiAgICAgICAgICAgICAgICAgIH0gXQ0KICAgICAgICAgICAgICAgfQ0KICAgICAgICAgICAgfSBdDQogICAgICAgICB9LCB7DQogICAgICAgICAgICAibG9jYWxJZCIgOiAiMzEiLA0KICAgICAgICAgICAgImxvY2F0b3IiIDogIjM2OjEtMzY6MTE0IiwNCiAgICAgICAgICAgICJuYW1lIiA6ICJOdXJzaW5nIEZhY2lsaXR5IFZpc2l0IiwNCiAgICAgICAgICAgICJpZCIgOiAiaHR0cDovL2N0cy5ubG0ubmloLmdvdi9maGlyL1ZhbHVlU2V0LzIuMTYuODQwLjEuMTEzODgzLjMuNDY0LjEwMDMuMTAxLjEyLjEwMTIiLA0KICAgICAgICAgICAgImFjY2Vzc0xldmVsIiA6ICJQdWJsaWMiLA0KICAgICAgICAgICAgImFubm90YXRpb24iIDogWyB7DQogICAgICAgICAgICAgICAidHlwZSIgOiAiQW5ub3RhdGlvbiIsDQogICAgICAgICAgICAgICAicyIgOiB7DQogICAgICAgICAgICAgICAgICAiciIgOiAiMzEiLA0KICAgICAgICAgICAgICAgICAgInMiIDogWyB7DQogICAgICAgICAgICAgICAgICAgICAidmFsdWUiIDogWyAiIiwidmFsdWVzZXQgIiwiXCJOdXJzaW5nIEZhY2lsaXR5IFZpc2l0XCIiLCI6ICIsIidodHRwOi8vY3RzLm5sbS5uaWguZ292L2ZoaXIvVmFsdWVTZXQvMi4xNi44NDAuMS4xMTM4ODMuMy40NjQuMTAwMy4xMDEuMTIuMTAxMiciIF0NCiAgICAgICAgICAgICAgICAgIH0gXQ0KICAgICAgICAgICAgICAgfQ0KICAgICAgICAgICAgfSBdDQogICAgICAgICB9LCB7DQogICAgICAgICAgICAibG9jYWxJZCIgOiAiMzIiLA0KICAgICAgICAgICAgImxvY2F0b3IiIDogIjM3OjEtMzc6MTAzIiwNCiAgICAgICAgICAgICJuYW1lIiA6ICJPYnNlcnZhdGlvbiIsDQogICAgICAgICAgICAiaWQiIDogImh0dHA6Ly9jdHMubmxtLm5paC5nb3YvZmhpci9WYWx1ZVNldC8yLjE2Ljg0MC4xLjExMzg4My4zLjQ2NC4xMDAzLjEwMS4xMi4xMDg2IiwNCiAgICAgICAgICAgICJhY2Nlc3NMZXZlbCIgOiAiUHVibGljIiwNCiAgICAgICAgICAgICJhbm5vdGF0aW9uIiA6IFsgew0KICAgICAgICAgICAgICAgInR5cGUiIDogIkFubm90YXRpb24iLA0KICAgICAgICAgICAgICAgInMiIDogew0KICAgICAgICAgICAgICAgICAgInIiIDogIjMyIiwNCiAgICAgICAgICAgICAgICAgICJzIiA6IFsgew0KICAgICAgICAgICAgICAgICAgICAgInZhbHVlIiA6IFsgIiIsInZhbHVlc2V0ICIsIlwiT2JzZXJ2YXRpb25cIiIsIjogIiwiJ2h0dHA6Ly9jdHMubmxtLm5paC5nb3YvZmhpci9WYWx1ZVNldC8yLjE2Ljg0MC4xLjExMzg4My4zLjQ2NC4xMDAzLjEwMS4xMi4xMDg2JyIgXQ0KICAgICAgICAgICAgICAgICAgfSBdDQogICAgICAgICAgICAgICB9DQogICAgICAgICAgICB9IF0NCiAgICAgICAgIH0sIHsNCiAgICAgICAgICAgICJsb2NhbElkIiA6ICIzMyIsDQogICAgICAgICAgICAibG9jYXRvciIgOiAiMzg6MS0zODoxMDQiLA0KICAgICAgICAgICAgIm5hbWUiIDogIk9mZmljZSBWaXNpdCIsDQogICAgICAgICAgICAiaWQiIDogImh0dHA6Ly9jdHMubmxtLm5paC5nb3YvZmhpci9WYWx1ZVNldC8yLjE2Ljg0MC4xLjExMzg4My4zLjQ2NC4xMDAzLjEwMS4xMi4xMDAxIiwNCiAgICAgICAgICAgICJhY2Nlc3NMZXZlbCIgOiAiUHVibGljIiwNCiAgICAgICAgICAgICJhbm5vdGF0aW9uIiA6IFsgew0KICAgICAgICAgICAgICAgInR5cGUiIDogIkFubm90YXRpb24iLA0KICAgICAgICAgICAgICAgInMiIDogew0KICAgICAgICAgICAgICAgICAgInIiIDogIjMzIiwNCiAgICAgICAgICAgICAgICAgICJzIiA6IFsgew0KICAgICAgICAgICAgICAgICAgICAgInZhbHVlIiA6IFsgIiIsInZhbHVlc2V0ICIsIlwiT2ZmaWNlIFZpc2l0XCIiLCI6ICIsIidodHRwOi8vY3RzLm5sbS5uaWguZ292L2ZoaXIvVmFsdWVTZXQvMi4xNi44NDAuMS4xMTM4ODMuMy40NjQuMTAwMy4xMDEuMTIuMTAwMSciIF0NCiAgICAgICAgICAgICAgICAgIH0gXQ0KICAgICAgICAgICAgICAgfQ0KICAgICAgICAgICAgfSBdDQogICAgICAgICB9LCB7DQogICAgICAgICAgICAibG9jYWxJZCIgOiAiMzQiLA0KICAgICAgICAgICAgImxvY2F0b3IiIDogIjM5OjEtMzk6MTAyIiwNCiAgICAgICAgICAgICJuYW1lIiA6ICJPdXRwYXRpZW50IiwNCiAgICAgICAgICAgICJpZCIgOiAiaHR0cDovL2N0cy5ubG0ubmloLmdvdi9maGlyL1ZhbHVlU2V0LzIuMTYuODQwLjEuMTEzODgzLjMuNDY0LjEwMDMuMTAxLjEyLjEwODciLA0KICAgICAgICAgICAgImFjY2Vzc0xldmVsIiA6ICJQdWJsaWMiLA0KICAgICAgICAgICAgImFubm90YXRpb24iIDogWyB7DQogICAgICAgICAgICAgICAidHlwZSIgOiAiQW5ub3RhdGlvbiIsDQogICAgICAgICAgICAgICAicyIgOiB7DQogICAgICAgICAgICAgICAgICAiciIgOiAiMzQiLA0KICAgICAgICAgICAgICAgICAgInMiIDogWyB7DQogICAgICAgICAgICAgICAgICAgICAidmFsdWUiIDogWyAiIiwidmFsdWVzZXQgIiwiXCJPdXRwYXRpZW50XCIiLCI6ICIsIidodHRwOi8vY3RzLm5sbS5uaWguZ292L2ZoaXIvVmFsdWVTZXQvMi4xNi44NDAuMS4xMTM4ODMuMy40NjQuMTAwMy4xMDEuMTIuMTA4NyciIF0NCiAgICAgICAgICAgICAgICAgIH0gXQ0KICAgICAgICAgICAgICAgfQ0KICAgICAgICAgICAgfSBdDQogICAgICAgICB9LCB7DQogICAgICAgICAgICAibG9jYWxJZCIgOiAiMzUiLA0KICAgICAgICAgICAgImxvY2F0b3IiIDogIjQwOjEtNDA6MTU0IiwNCiAgICAgICAgICAgICJuYW1lIiA6ICJQcmV2ZW50aXZlIENhcmUgU2VydmljZXMgLSBFc3RhYmxpc2hlZCBPZmZpY2UgVmlzaXQsIDE4IGFuZCBVcCIsDQogICAgICAgICAgICAiaWQiIDogImh0dHA6Ly9jdHMubmxtLm5paC5nb3YvZmhpci9WYWx1ZVNldC8yLjE2Ljg0MC4xLjExMzg4My4zLjQ2NC4xMDAzLjEwMS4xMi4xMDI1IiwNCiAgICAgICAgICAgICJhY2Nlc3NMZXZlbCIgOiAiUHVibGljIiwNCiAgICAgICAgICAgICJhbm5vdGF0aW9uIiA6IFsgew0KICAgICAgICAgICAgICAgInR5cGUiIDogIkFubm90YXRpb24iLA0KICAgICAgICAgICAgICAgInMiIDogew0KICAgICAgICAgICAgICAgICAgInIiIDogIjM1IiwNCiAgICAgICAgICAgICAgICAgICJzIiA6IFsgew0KICAgICAgICAgICAgICAgICAgICAgInZhbHVlIiA6IFsgIiIsInZhbHVlc2V0ICIsIlwiUHJldmVudGl2ZSBDYXJlIFNlcnZpY2VzIC0gRXN0YWJsaXNoZWQgT2ZmaWNlIFZpc2l0LCAxOCBhbmQgVXBcIiIsIjogIiwiJ2h0dHA6Ly9jdHMubmxtLm5paC5nb3YvZmhpci9WYWx1ZVNldC8yLjE2Ljg0MC4xLjExMzg4My4zLjQ2NC4xMDAzLjEwMS4xMi4xMDI1JyIgXQ0KICAgICAgICAgICAgICAgICAgfSBdDQogICAgICAgICAgICAgICB9DQogICAgICAgICAgICB9IF0NCiAgICAgICAgIH0sIHsNCiAgICAgICAgICAgICJsb2NhbElkIiA6ICIzNiIsDQogICAgICAgICAgICAibG9jYXRvciIgOiAiNDE6MS00MToxNDgiLA0KICAgICAgICAgICAgIm5hbWUiIDogIlByZXZlbnRpdmUgQ2FyZSBTZXJ2aWNlcy1Jbml0aWFsIE9mZmljZSBWaXNpdCwgMTggYW5kIFVwIiwNCiAgICAgICAgICAgICJpZCIgOiAiaHR0cDovL2N0cy5ubG0ubmloLmdvdi9maGlyL1ZhbHVlU2V0LzIuMTYuODQwLjEuMTEzODgzLjMuNDY0LjEwMDMuMTAxLjEyLjEwMjMiLA0KICAgICAgICAgICAgImFjY2Vzc0xldmVsIiA6ICJQdWJsaWMiLA0KICAgICAgICAgICAgImFubm90YXRpb24iIDogWyB7DQogICAgICAgICAgICAgICAidHlwZSIgOiAiQW5ub3RhdGlvbiIsDQogICAgICAgICAgICAgICAicyIgOiB7DQogICAgICAgICAgICAgICAgICAiciIgOiAiMzYiLA0KICAgICAgICAgICAgICAgICAgInMiIDogWyB7DQogICAgICAgICAgICAgICAgICAgICAidmFsdWUiIDogWyAiIiwidmFsdWVzZXQgIiwiXCJQcmV2ZW50aXZlIENhcmUgU2VydmljZXMtSW5pdGlhbCBPZmZpY2UgVmlzaXQsIDE4IGFuZCBVcFwiIiwiOiAiLCInaHR0cDovL2N0cy5ubG0ubmloLmdvdi9maGlyL1ZhbHVlU2V0LzIuMTYuODQwLjEuMTEzODgzLjMuNDY0LjEwMDMuMTAxLjEyLjEwMjMnIiBdDQogICAgICAgICAgICAgICAgICB9IF0NCiAgICAgICAgICAgICAgIH0NCiAgICAgICAgICAgIH0gXQ0KICAgICAgICAgfSwgew0KICAgICAgICAgICAgImxvY2FsSWQiIDogIjM3IiwNCiAgICAgICAgICAgICJsb2NhdG9yIiA6ICI0MjoxLTQyOjEwNyIsDQogICAgICAgICAgICAibmFtZSIgOiAiVG90YWwgQ29sZWN0b215IiwNCiAgICAgICAgICAgICJpZCIgOiAiaHR0cDovL2N0cy5ubG0ubmloLmdvdi9maGlyL1ZhbHVlU2V0LzIuMTYuODQwLjEuMTEzODgzLjMuNDY0LjEwMDMuMTk4LjEyLjEwMTkiLA0KICAgICAgICAgICAgImFjY2Vzc0xldmVsIiA6ICJQdWJsaWMiLA0KICAgICAgICAgICAgImFubm90YXRpb24iIDogWyB7DQogICAgICAgICAgICAgICAidHlwZSIgOiAiQW5ub3RhdGlvbiIsDQogICAgICAgICAgICAgICAicyIgOiB7DQogICAgICAgICAgICAgICAgICAiciIgOiAiMzciLA0KICAgICAgICAgICAgICAgICAgInMiIDogWyB7DQogICAgICAgICAgICAgICAgICAgICAidmFsdWUiIDogWyAiIiwidmFsdWVzZXQgIiwiXCJUb3RhbCBDb2xlY3RvbXlcIiIsIjogIiwiJ2h0dHA6Ly9jdHMubmxtLm5paC5nb3YvZmhpci9WYWx1ZVNldC8yLjE2Ljg0MC4xLjExMzg4My4zLjQ2NC4xMDAzLjE5OC4xMi4xMDE5JyIgXQ0KICAgICAgICAgICAgICAgICAgfSBdDQogICAgICAgICAgICAgICB9DQogICAgICAgICAgICB9IF0NCiAgICAgICAgIH0gXQ0KICAgICAgfSwNCiAgICAgICJjb2RlcyIgOiB7DQogICAgICAgICAiZGVmIiA6IFsgew0KICAgICAgICAgICAgImxvY2FsSWQiIDogIjM5IiwNCiAgICAgICAgICAgICJsb2NhdG9yIiA6ICI0NDoxLTQ0OjYyIiwNCiAgICAgICAgICAgICJuYW1lIiA6ICJCaXJ0aCBkYXRlIiwNCiAgICAgICAgICAgICJpZCIgOiAiMjExMTItOCIsDQogICAgICAgICAgICAiZGlzcGxheSIgOiAiQmlydGggZGF0ZSIsDQogICAgICAgICAgICAiYWNjZXNzTGV2ZWwiIDogIlB1YmxpYyIsDQogICAgICAgICAgICAiYW5ub3RhdGlvbiIgOiBbIHsNCiAgICAgICAgICAgICAgICJ0eXBlIiA6ICJBbm5vdGF0aW9uIiwNCiAgICAgICAgICAgICAgICJzIiA6IHsNCiAgICAgICAgICAgICAgICAgICJyIiA6ICIzOSIsDQogICAgICAgICAgICAgICAgICAicyIgOiBbIHsNCiAgICAgICAgICAgICAgICAgICAgICJ2YWx1ZSIgOiBbICIiLCJjb2RlICIsIlwiQmlydGggZGF0ZVwiIiwiOiAiLCInMjExMTItOCciLCIgZnJvbSAiIF0NCiAgICAgICAgICAgICAgICAgIH0sIHsNCiAgICAgICAgICAgICAgICAgICAgICJyIiA6ICIzOCIsDQogICAgICAgICAgICAgICAgICAgICAicyIgOiBbIHsNCiAgICAgICAgICAgICAgICAgICAgICAgICJ2YWx1ZSIgOiBbICJcIkxPSU5DXCIiIF0NCiAgICAgICAgICAgICAgICAgICAgIH0gXQ0KICAgICAgICAgICAgICAgICAgfSwgew0KICAgICAgICAgICAgICAgICAgICAgInZhbHVlIiA6IFsgIiBkaXNwbGF5ICIsIidCaXJ0aCBkYXRlJyIgXQ0KICAgICAgICAgICAgICAgICAgfSBdDQogICAgICAgICAgICAgICB9DQogICAgICAgICAgICB9IF0sDQogICAgICAgICAgICAiY29kZVN5c3RlbSIgOiB7DQogICAgICAgICAgICAgICAibG9jYWxJZCIgOiAiMzgiLA0KICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjQ0OjM1LTQ0OjQxIiwNCiAgICAgICAgICAgICAgICJuYW1lIiA6ICJMT0lOQyINCiAgICAgICAgICAgIH0NCiAgICAgICAgIH0gXQ0KICAgICAgfSwNCiAgICAgICJzdGF0ZW1lbnRzIiA6IHsNCiAgICAgICAgICJkZWYiIDogWyB7DQogICAgICAgICAgICAibG9jYXRvciIgOiAiNDk6MS00OToxNSIsDQogICAgICAgICAgICAibmFtZSIgOiAiUGF0aWVudCIsDQogICAgICAgICAgICAiY29udGV4dCIgOiAiUGF0aWVudCIsDQogICAgICAgICAgICAiZXhwcmVzc2lvbiIgOiB7DQogICAgICAgICAgICAgICAidHlwZSIgOiAiU2luZ2xldG9uRnJvbSIsDQogICAgICAgICAgICAgICAib3BlcmFuZCIgOiB7DQogICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiNDk6MS00OToxNSIsDQogICAgICAgICAgICAgICAgICAiZGF0YVR5cGUiIDogIntodHRwOi8vaGw3Lm9yZy9maGlyfVBhdGllbnQiLA0KICAgICAgICAgICAgICAgICAgInRlbXBsYXRlSWQiIDogImh0dHA6Ly9obDcub3JnL2ZoaXIvU3RydWN0dXJlRGVmaW5pdGlvbi9QYXRpZW50IiwNCiAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJSZXRyaWV2ZSINCiAgICAgICAgICAgICAgIH0NCiAgICAgICAgICAgIH0NCiAgICAgICAgIH0sIHsNCiAgICAgICAgICAgICJsb2NhbElkIiA6ICI0OCIsDQogICAgICAgICAgICAibG9jYXRvciIgOiAiNTE6MS01MjoyMSIsDQogICAgICAgICAgICAibmFtZSIgOiAiU0RFIEV0aG5pY2l0eSIsDQogICAgICAgICAgICAiY29udGV4dCIgOiAiUGF0aWVudCIsDQogICAgICAgICAgICAiYWNjZXNzTGV2ZWwiIDogIlB1YmxpYyIsDQogICAgICAgICAgICAiYW5ub3RhdGlvbiIgOiBbIHsNCiAgICAgICAgICAgICAgICJ0eXBlIiA6ICJBbm5vdGF0aW9uIiwNCiAgICAgICAgICAgICAgICJzIiA6IHsNCiAgICAgICAgICAgICAgICAgICJyIiA6ICI0OCIsDQogICAgICAgICAgICAgICAgICAicyIgOiBbIHsNCiAgICAgICAgICAgICAgICAgICAgICJ2YWx1ZSIgOiBbICIiLCJkZWZpbmUgIiwiXCJTREUgRXRobmljaXR5XCIiLCI6XG4gICIgXQ0KICAgICAgICAgICAgICAgICAgfSwgew0KICAgICAgICAgICAgICAgICAgICAgInIiIDogIjQ3IiwNCiAgICAgICAgICAgICAgICAgICAgICJzIiA6IFsgew0KICAgICAgICAgICAgICAgICAgICAgICAgInIiIDogIjQ2IiwNCiAgICAgICAgICAgICAgICAgICAgICAgICJzIiA6IFsgew0KICAgICAgICAgICAgICAgICAgICAgICAgICAgInZhbHVlIiA6IFsgIlNERSIgXQ0KICAgICAgICAgICAgICAgICAgICAgICAgfSBdDQogICAgICAgICAgICAgICAgICAgICB9LCB7DQogICAgICAgICAgICAgICAgICAgICAgICAidmFsdWUiIDogWyAiLiIgXQ0KICAgICAgICAgICAgICAgICAgICAgfSwgew0KICAgICAgICAgICAgICAgICAgICAgICAgInIiIDogIjQ3IiwNCiAgICAgICAgICAgICAgICAgICAgICAgICJzIiA6IFsgew0KICAgICAgICAgICAgICAgICAgICAgICAgICAgInZhbHVlIiA6IFsgIlwiU0RFIEV0aG5pY2l0eVwiIiBdDQogICAgICAgICAgICAgICAgICAgICAgICB9IF0NCiAgICAgICAgICAgICAgICAgICAgIH0gXQ0KICAgICAgICAgICAgICAgICAgfSBdDQogICAgICAgICAgICAgICB9DQogICAgICAgICAgICB9IF0sDQogICAgICAgICAgICAiZXhwcmVzc2lvbiIgOiB7DQogICAgICAgICAgICAgICAibG9jYWxJZCIgOiAiNDciLA0KICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjUyOjMtNTI6MjEiLA0KICAgICAgICAgICAgICAgIm5hbWUiIDogIlNERSBFdGhuaWNpdHkiLA0KICAgICAgICAgICAgICAgImxpYnJhcnlOYW1lIiA6ICJTREUiLA0KICAgICAgICAgICAgICAgInR5cGUiIDogIkV4cHJlc3Npb25SZWYiDQogICAgICAgICAgICB9DQogICAgICAgICB9LCB7DQogICAgICAgICAgICAibG9jYWxJZCIgOiAiNTEiLA0KICAgICAgICAgICAgImxvY2F0b3IiIDogIjU0OjEtNTU6MTciLA0KICAgICAgICAgICAgIm5hbWUiIDogIlNERSBQYXllciIsDQogICAgICAgICAgICAiY29udGV4dCIgOiAiUGF0aWVudCIsDQogICAgICAgICAgICAiYWNjZXNzTGV2ZWwiIDogIlB1YmxpYyIsDQogICAgICAgICAgICAiYW5ub3RhdGlvbiIgOiBbIHsNCiAgICAgICAgICAgICAgICJ0eXBlIiA6ICJBbm5vdGF0aW9uIiwNCiAgICAgICAgICAgICAgICJzIiA6IHsNCiAgICAgICAgICAgICAgICAgICJyIiA6ICI1MSIsDQogICAgICAgICAgICAgICAgICAicyIgOiBbIHsNCiAgICAgICAgICAgICAgICAgICAgICJ2YWx1ZSIgOiBbICIiLCJkZWZpbmUgIiwiXCJTREUgUGF5ZXJcIiIsIjpcbiAgIiBdDQogICAgICAgICAgICAgICAgICB9LCB7DQogICAgICAgICAgICAgICAgICAgICAiciIgOiAiNTAiLA0KICAgICAgICAgICAgICAgICAgICAgInMiIDogWyB7DQogICAgICAgICAgICAgICAgICAgICAgICAiciIgOiAiNDkiLA0KICAgICAgICAgICAgICAgICAgICAgICAgInMiIDogWyB7DQogICAgICAgICAgICAgICAgICAgICAgICAgICAidmFsdWUiIDogWyAiU0RFIiBdDQogICAgICAgICAgICAgICAgICAgICAgICB9IF0NCiAgICAgICAgICAgICAgICAgICAgIH0sIHsNCiAgICAgICAgICAgICAgICAgICAgICAgICJ2YWx1ZSIgOiBbICIuIiBdDQogICAgICAgICAgICAgICAgICAgICB9LCB7DQogICAgICAgICAgICAgICAgICAgICAgICAiciIgOiAiNTAiLA0KICAgICAgICAgICAgICAgICAgICAgICAgInMiIDogWyB7DQogICAgICAgICAgICAgICAgICAgICAgICAgICAidmFsdWUiIDogWyAiXCJTREUgUGF5ZXJcIiIgXQ0KICAgICAgICAgICAgICAgICAgICAgICAgfSBdDQogICAgICAgICAgICAgICAgICAgICB9IF0NCiAgICAgICAgICAgICAgICAgIH0gXQ0KICAgICAgICAgICAgICAgfQ0KICAgICAgICAgICAgfSBdLA0KICAgICAgICAgICAgImV4cHJlc3Npb24iIDogew0KICAgICAgICAgICAgICAgImxvY2FsSWQiIDogIjUwIiwNCiAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICI1NTozLTU1OjE3IiwNCiAgICAgICAgICAgICAgICJuYW1lIiA6ICJTREUgUGF5ZXIiLA0KICAgICAgICAgICAgICAgImxpYnJhcnlOYW1lIiA6ICJTREUiLA0KICAgICAgICAgICAgICAgInR5cGUiIDogIkV4cHJlc3Npb25SZWYiDQogICAgICAgICAgICB9DQogICAgICAgICB9LCB7DQogICAgICAgICAgICAibG9jYWxJZCIgOiAiNTQiLA0KICAgICAgICAgICAgImxvY2F0b3IiIDogIjU3OjEtNTg6MTYiLA0KICAgICAgICAgICAgIm5hbWUiIDogIlNERSBSYWNlIiwNCiAgICAgICAgICAgICJjb250ZXh0IiA6ICJQYXRpZW50IiwNCiAgICAgICAgICAgICJhY2Nlc3NMZXZlbCIgOiAiUHVibGljIiwNCiAgICAgICAgICAgICJhbm5vdGF0aW9uIiA6IFsgew0KICAgICAgICAgICAgICAgInR5cGUiIDogIkFubm90YXRpb24iLA0KICAgICAgICAgICAgICAgInMiIDogew0KICAgICAgICAgICAgICAgICAgInIiIDogIjU0IiwNCiAgICAgICAgICAgICAgICAgICJzIiA6IFsgew0KICAgICAgICAgICAgICAgICAgICAgInZhbHVlIiA6IFsgIiIsImRlZmluZSAiLCJcIlNERSBSYWNlXCIiLCI6XG4gICIgXQ0KICAgICAgICAgICAgICAgICAgfSwgew0KICAgICAgICAgICAgICAgICAgICAgInIiIDogIjUzIiwNCiAgICAgICAgICAgICAgICAgICAgICJzIiA6IFsgew0KICAgICAgICAgICAgICAgICAgICAgICAgInIiIDogIjUyIiwNCiAgICAgICAgICAgICAgICAgICAgICAgICJzIiA6IFsgew0KICAgICAgICAgICAgICAgICAgICAgICAgICAgInZhbHVlIiA6IFsgIlNERSIgXQ0KICAgICAgICAgICAgICAgICAgICAgICAgfSBdDQogICAgICAgICAgICAgICAgICAgICB9LCB7DQogICAgICAgICAgICAgICAgICAgICAgICAidmFsdWUiIDogWyAiLiIgXQ0KICAgICAgICAgICAgICAgICAgICAgfSwgew0KICAgICAgICAgICAgICAgICAgICAgICAgInIiIDogIjUzIiwNCiAgICAgICAgICAgICAgICAgICAgICAgICJzIiA6IFsgew0KICAgICAgICAgICAgICAgICAgICAgICAgICAgInZhbHVlIiA6IFsgIlwiU0RFIFJhY2VcIiIgXQ0KICAgICAgICAgICAgICAgICAgICAgICAgfSBdDQogICAgICAgICAgICAgICAgICAgICB9IF0NCiAgICAgICAgICAgICAgICAgIH0gXQ0KICAgICAgICAgICAgICAgfQ0KICAgICAgICAgICAgfSBdLA0KICAgICAgICAgICAgImV4cHJlc3Npb24iIDogew0KICAgICAgICAgICAgICAgImxvY2FsSWQiIDogIjUzIiwNCiAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICI1ODozLTU4OjE2IiwNCiAgICAgICAgICAgICAgICJuYW1lIiA6ICJTREUgUmFjZSIsDQogICAgICAgICAgICAgICAibGlicmFyeU5hbWUiIDogIlNERSIsDQogICAgICAgICAgICAgICAidHlwZSIgOiAiRXhwcmVzc2lvblJlZiINCiAgICAgICAgICAgIH0NCiAgICAgICAgIH0sIHsNCiAgICAgICAgICAgICJsb2NhbElkIiA6ICI1NyIsDQogICAgICAgICAgICAibG9jYXRvciIgOiAiNjA6MS02MToxNSIsDQogICAgICAgICAgICAibmFtZSIgOiAiU0RFIFNleCIsDQogICAgICAgICAgICAiY29udGV4dCIgOiAiUGF0aWVudCIsDQogICAgICAgICAgICAiYWNjZXNzTGV2ZWwiIDogIlB1YmxpYyIsDQogICAgICAgICAgICAiYW5ub3RhdGlvbiIgOiBbIHsNCiAgICAgICAgICAgICAgICJ0eXBlIiA6ICJBbm5vdGF0aW9uIiwNCiAgICAgICAgICAgICAgICJzIiA6IHsNCiAgICAgICAgICAgICAgICAgICJyIiA6ICI1NyIsDQogICAgICAgICAgICAgICAgICAicyIgOiBbIHsNCiAgICAgICAgICAgICAgICAgICAgICJ2YWx1ZSIgOiBbICIiLCJkZWZpbmUgIiwiXCJTREUgU2V4XCIiLCI6XG4gICIgXQ0KICAgICAgICAgICAgICAgICAgfSwgew0KICAgICAgICAgICAgICAgICAgICAgInIiIDogIjU2IiwNCiAgICAgICAgICAgICAgICAgICAgICJzIiA6IFsgew0KICAgICAgICAgICAgICAgICAgICAgICAgInIiIDogIjU1IiwNCiAgICAgICAgICAgICAgICAgICAgICAgICJzIiA6IFsgew0KICAgICAgICAgICAgICAgICAgICAgICAgICAgInZhbHVlIiA6IFsgIlNERSIgXQ0KICAgICAgICAgICAgICAgICAgICAgICAgfSBdDQogICAgICAgICAgICAgICAgICAgICB9LCB7DQogICAgICAgICAgICAgICAgICAgICAgICAidmFsdWUiIDogWyAiLiIgXQ0KICAgICAgICAgICAgICAgICAgICAgfSwgew0KICAgICAgICAgICAgICAgICAgICAgICAgInIiIDogIjU2IiwNCiAgICAgICAgICAgICAgICAgICAgICAgICJzIiA6IFsgew0KICAgICAgICAgICAgICAgICAgICAgICAgICAgInZhbHVlIiA6IFsgIlwiU0RFIFNleFwiIiBdDQogICAgICAgICAgICAgICAgICAgICAgICB9IF0NCiAgICAgICAgICAgICAgICAgICAgIH0gXQ0KICAgICAgICAgICAgICAgICAgfSBdDQogICAgICAgICAgICAgICB9DQogICAgICAgICAgICB9IF0sDQogICAgICAgICAgICAiZXhwcmVzc2lvbiIgOiB7DQogICAgICAgICAgICAgICAibG9jYWxJZCIgOiAiNTYiLA0KICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjYxOjMtNjE6MTUiLA0KICAgICAgICAgICAgICAgIm5hbWUiIDogIlNERSBTZXgiLA0KICAgICAgICAgICAgICAgImxpYnJhcnlOYW1lIiA6ICJTREUiLA0KICAgICAgICAgICAgICAgInR5cGUiIDogIkV4cHJlc3Npb25SZWYiDQogICAgICAgICAgICB9DQogICAgICAgICB9LCB7DQogICAgICAgICAgICAibG9jYWxJZCIgOiAiNjgiLA0KICAgICAgICAgICAgImxvY2F0b3IiIDogIjYzOjEtNjU6NjIiLA0KICAgICAgICAgICAgIm5hbWUiIDogIkluaXRpYWwgUG9wdWxhdGlvbiIsDQogICAgICAgICAgICAiY29udGV4dCIgOiAiUGF0aWVudCIsDQogICAgICAgICAgICAiYWNjZXNzTGV2ZWwiIDogIlB1YmxpYyIsDQogICAgICAgICAgICAiYW5ub3RhdGlvbiIgOiBbIHsNCiAgICAgICAgICAgICAgICJ0eXBlIiA6ICJBbm5vdGF0aW9uIiwNCiAgICAgICAgICAgICAgICJzIiA6IHsNCiAgICAgICAgICAgICAgICAgICJyIiA6ICI2OCIsDQogICAgICAgICAgICAgICAgICAicyIgOiBbIHsNCiAgICAgICAgICAgICAgICAgICAgICJ2YWx1ZSIgOiBbICIiLCJkZWZpbmUgIiwiXCJJbml0aWFsIFBvcHVsYXRpb25cIiIsIjpcbiAgIiBdDQogICAgICAgICAgICAgICAgICB9LCB7DQogICAgICAgICAgICAgICAgICAgICAiciIgOiAiNjciLA0KICAgICAgICAgICAgICAgICAgICAgInMiIDogWyB7DQogICAgICAgICAgICAgICAgICAgICAgICAiciIgOiAiNjMiLA0KICAgICAgICAgICAgICAgICAgICAgICAgInMiIDogWyB7DQogICAgICAgICAgICAgICAgICAgICAgICAgICAiciIgOiAiNjAiLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgInMiIDogWyB7DQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAidmFsdWUiIDogWyAiQWdlSW5ZZWFyc0F0IiwiKCIgXQ0KICAgICAgICAgICAgICAgICAgICAgICAgICAgfSwgew0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInIiIDogIjU5IiwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJzIiA6IFsgew0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInZhbHVlIiA6IFsgInN0YXJ0IG9mICIgXQ0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfSwgew0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInIiIDogIjU4IiwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJzIiA6IFsgew0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInZhbHVlIiA6IFsgIlwiTWVhc3VyZW1lbnQgUGVyaW9kXCIiIF0NCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIH0gXQ0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfSBdDQogICAgICAgICAgICAgICAgICAgICAgICAgICB9LCB7DQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAidmFsdWUiIDogWyAiKSIgXQ0KICAgICAgICAgICAgICAgICAgICAgICAgICAgfSBdDQogICAgICAgICAgICAgICAgICAgICAgICB9LCB7DQogICAgICAgICAgICAgICAgICAgICAgICAgICAiciIgOiAiNjEiLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgInZhbHVlIiA6IFsgIiBiZXR3ZWVuICIsIjUwIiwiIGFuZCAiLCI3NSIgXQ0KICAgICAgICAgICAgICAgICAgICAgICAgfSBdDQogICAgICAgICAgICAgICAgICAgICB9LCB7DQogICAgICAgICAgICAgICAgICAgICAgICAidmFsdWUiIDogWyAiXG5cdCBhbmQgIiBdDQogICAgICAgICAgICAgICAgICAgICB9LCB7DQogICAgICAgICAgICAgICAgICAgICAgICAiciIgOiAiNjYiLA0KICAgICAgICAgICAgICAgICAgICAgICAgInMiIDogWyB7DQogICAgICAgICAgICAgICAgICAgICAgICAgICAidmFsdWUiIDogWyAiZXhpc3RzICIgXQ0KICAgICAgICAgICAgICAgICAgICAgICAgfSwgew0KICAgICAgICAgICAgICAgICAgICAgICAgICAgInIiIDogIjY1IiwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICJzIiA6IFsgew0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInIiIDogIjY0IiwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJzIiA6IFsgew0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInZhbHVlIiA6IFsgIkFkdWx0T3V0cGF0aWVudEVuY291bnRlcnMiIF0NCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIH0gXQ0KICAgICAgICAgICAgICAgICAgICAgICAgICAgfSwgew0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInZhbHVlIiA6IFsgIi4iIF0NCiAgICAgICAgICAgICAgICAgICAgICAgICAgIH0sIHsNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJyIiA6ICI2NSIsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAicyIgOiBbIHsNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJ2YWx1ZSIgOiBbICJcIlF1YWxpZnlpbmcgRW5jb3VudGVyc1wiIiBdDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICB9IF0NCiAgICAgICAgICAgICAgICAgICAgICAgICAgIH0gXQ0KICAgICAgICAgICAgICAgICAgICAgICAgfSBdDQogICAgICAgICAgICAgICAgICAgICB9IF0NCiAgICAgICAgICAgICAgICAgIH0gXQ0KICAgICAgICAgICAgICAgfQ0KICAgICAgICAgICAgfSBdLA0KICAgICAgICAgICAgImV4cHJlc3Npb24iIDogew0KICAgICAgICAgICAgICAgImxvY2FsSWQiIDogIjY3IiwNCiAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICI2NDozLTY1OjYyIiwNCiAgICAgICAgICAgICAgICJ0eXBlIiA6ICJBbmQiLA0KICAgICAgICAgICAgICAgIm9wZXJhbmQiIDogWyB7DQogICAgICAgICAgICAgICAgICAibG9jYWxJZCIgOiAiNjMiLA0KICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjY0OjMtNjQ6NjMiLA0KICAgICAgICAgICAgICAgICAgInR5cGUiIDogIkFuZCIsDQogICAgICAgICAgICAgICAgICAib3BlcmFuZCIgOiBbIHsNCiAgICAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJHcmVhdGVyT3JFcXVhbCIsDQogICAgICAgICAgICAgICAgICAgICAib3BlcmFuZCIgOiBbIHsNCiAgICAgICAgICAgICAgICAgICAgICAgICJsb2NhbElkIiA6ICI2MCIsDQogICAgICAgICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiNjQ6My02NDo0NSIsDQogICAgICAgICAgICAgICAgICAgICAgICAicHJlY2lzaW9uIiA6ICJZZWFyIiwNCiAgICAgICAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJDYWxjdWxhdGVBZ2VBdCIsDQogICAgICAgICAgICAgICAgICAgICAgICAib3BlcmFuZCIgOiBbIHsNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJUb0RhdGVUaW1lIiwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICJvcGVyYW5kIiA6IHsNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJwYXRoIiA6ICJiaXJ0aERhdGUudmFsdWUiLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInR5cGUiIDogIlByb3BlcnR5IiwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJzb3VyY2UiIDogew0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIm5hbWUiIDogIlBhdGllbnQiLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInR5cGUiIDogIkV4cHJlc3Npb25SZWYiDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICB9DQogICAgICAgICAgICAgICAgICAgICAgICAgICB9DQogICAgICAgICAgICAgICAgICAgICAgICB9LCB7DQogICAgICAgICAgICAgICAgICAgICAgICAgICAibG9jYWxJZCIgOiAiNTkiLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjY0OjE2LTY0OjQ0IiwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJTdGFydCIsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAib3BlcmFuZCIgOiB7DQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAibG9jYWxJZCIgOiAiNTgiLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjY0OjI1LTY0OjQ0IiwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJNZWFzdXJlbWVudCBQZXJpb2QiLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInR5cGUiIDogIlBhcmFtZXRlclJlZiINCiAgICAgICAgICAgICAgICAgICAgICAgICAgIH0NCiAgICAgICAgICAgICAgICAgICAgICAgIH0gXQ0KICAgICAgICAgICAgICAgICAgICAgfSwgew0KICAgICAgICAgICAgICAgICAgICAgICAgImxvY2FsSWQiIDogIjYxIiwNCiAgICAgICAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICI2NDo1NS02NDo1NiIsDQogICAgICAgICAgICAgICAgICAgICAgICAidmFsdWVUeXBlIiA6ICJ7dXJuOmhsNy1vcmc6ZWxtLXR5cGVzOnIxfUludGVnZXIiLA0KICAgICAgICAgICAgICAgICAgICAgICAgInZhbHVlIiA6ICI1MCIsDQogICAgICAgICAgICAgICAgICAgICAgICAidHlwZSIgOiAiTGl0ZXJhbCINCiAgICAgICAgICAgICAgICAgICAgIH0gXQ0KICAgICAgICAgICAgICAgICAgfSwgew0KICAgICAgICAgICAgICAgICAgICAgInR5cGUiIDogIkxlc3NPckVxdWFsIiwNCiAgICAgICAgICAgICAgICAgICAgICJvcGVyYW5kIiA6IFsgew0KICAgICAgICAgICAgICAgICAgICAgICAgImxvY2FsSWQiIDogIjYwIiwNCiAgICAgICAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICI2NDozLTY0OjQ1IiwNCiAgICAgICAgICAgICAgICAgICAgICAgICJwcmVjaXNpb24iIDogIlllYXIiLA0KICAgICAgICAgICAgICAgICAgICAgICAgInR5cGUiIDogIkNhbGN1bGF0ZUFnZUF0IiwNCiAgICAgICAgICAgICAgICAgICAgICAgICJvcGVyYW5kIiA6IFsgew0KICAgICAgICAgICAgICAgICAgICAgICAgICAgInR5cGUiIDogIlRvRGF0ZVRpbWUiLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgIm9wZXJhbmQiIDogew0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInBhdGgiIDogImJpcnRoRGF0ZS52YWx1ZSIsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAidHlwZSIgOiAiUHJvcGVydHkiLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInNvdXJjZSIgOiB7DQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAibmFtZSIgOiAiUGF0aWVudCIsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAidHlwZSIgOiAiRXhwcmVzc2lvblJlZiINCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIH0NCiAgICAgICAgICAgICAgICAgICAgICAgICAgIH0NCiAgICAgICAgICAgICAgICAgICAgICAgIH0sIHsNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICJsb2NhbElkIiA6ICI1OSIsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiNjQ6MTYtNjQ6NDQiLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgInR5cGUiIDogIlN0YXJ0IiwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICJvcGVyYW5kIiA6IHsNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJsb2NhbElkIiA6ICI1OCIsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiNjQ6MjUtNjQ6NDQiLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIm5hbWUiIDogIk1lYXN1cmVtZW50IFBlcmlvZCIsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAidHlwZSIgOiAiUGFyYW1ldGVyUmVmIg0KICAgICAgICAgICAgICAgICAgICAgICAgICAgfQ0KICAgICAgICAgICAgICAgICAgICAgICAgfSBdDQogICAgICAgICAgICAgICAgICAgICB9LCB7DQogICAgICAgICAgICAgICAgICAgICAgICAibG9jYWxJZCIgOiAiNjIiLA0KICAgICAgICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjY0OjYyLTY0OjYzIiwNCiAgICAgICAgICAgICAgICAgICAgICAgICJ2YWx1ZVR5cGUiIDogInt1cm46aGw3LW9yZzplbG0tdHlwZXM6cjF9SW50ZWdlciIsDQogICAgICAgICAgICAgICAgICAgICAgICAidmFsdWUiIDogIjc1IiwNCiAgICAgICAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJMaXRlcmFsIg0KICAgICAgICAgICAgICAgICAgICAgfSBdDQogICAgICAgICAgICAgICAgICB9IF0NCiAgICAgICAgICAgICAgIH0sIHsNCiAgICAgICAgICAgICAgICAgICJsb2NhbElkIiA6ICI2NiIsDQogICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiNjU6Ny02NTo2MiIsDQogICAgICAgICAgICAgICAgICAidHlwZSIgOiAiRXhpc3RzIiwNCiAgICAgICAgICAgICAgICAgICJvcGVyYW5kIiA6IHsNCiAgICAgICAgICAgICAgICAgICAgICJsb2NhbElkIiA6ICI2NSIsDQogICAgICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiNjU6MTQtNjU6NjIiLA0KICAgICAgICAgICAgICAgICAgICAgIm5hbWUiIDogIlF1YWxpZnlpbmcgRW5jb3VudGVycyIsDQogICAgICAgICAgICAgICAgICAgICAibGlicmFyeU5hbWUiIDogIkFkdWx0T3V0cGF0aWVudEVuY291bnRlcnMiLA0KICAgICAgICAgICAgICAgICAgICAgInR5cGUiIDogIkV4cHJlc3Npb25SZWYiDQogICAgICAgICAgICAgICAgICB9DQogICAgICAgICAgICAgICB9IF0NCiAgICAgICAgICAgIH0NCiAgICAgICAgIH0sIHsNCiAgICAgICAgICAgICJsb2NhbElkIiA6ICI3MCIsDQogICAgICAgICAgICAibG9jYXRvciIgOiAiNjc6MS02ODoyMiIsDQogICAgICAgICAgICAibmFtZSIgOiAiRGVub21pbmF0b3IiLA0KICAgICAgICAgICAgImNvbnRleHQiIDogIlBhdGllbnQiLA0KICAgICAgICAgICAgImFjY2Vzc0xldmVsIiA6ICJQdWJsaWMiLA0KICAgICAgICAgICAgImFubm90YXRpb24iIDogWyB7DQogICAgICAgICAgICAgICAidHlwZSIgOiAiQW5ub3RhdGlvbiIsDQogICAgICAgICAgICAgICAicyIgOiB7DQogICAgICAgICAgICAgICAgICAiciIgOiAiNzAiLA0KICAgICAgICAgICAgICAgICAgInMiIDogWyB7DQogICAgICAgICAgICAgICAgICAgICAidmFsdWUiIDogWyAiIiwiZGVmaW5lICIsIlwiRGVub21pbmF0b3JcIiIsIjpcbiAgIiBdDQogICAgICAgICAgICAgICAgICB9LCB7DQogICAgICAgICAgICAgICAgICAgICAiciIgOiAiNjkiLA0KICAgICAgICAgICAgICAgICAgICAgInMiIDogWyB7DQogICAgICAgICAgICAgICAgICAgICAgICAidmFsdWUiIDogWyAiXCJJbml0aWFsIFBvcHVsYXRpb25cIiIgXQ0KICAgICAgICAgICAgICAgICAgICAgfSBdDQogICAgICAgICAgICAgICAgICB9IF0NCiAgICAgICAgICAgICAgIH0NCiAgICAgICAgICAgIH0gXSwNCiAgICAgICAgICAgICJleHByZXNzaW9uIiA6IHsNCiAgICAgICAgICAgICAgICJsb2NhbElkIiA6ICI2OSIsDQogICAgICAgICAgICAgICAibG9jYXRvciIgOiAiNjg6My02ODoyMiIsDQogICAgICAgICAgICAgICAibmFtZSIgOiAiSW5pdGlhbCBQb3B1bGF0aW9uIiwNCiAgICAgICAgICAgICAgICJ0eXBlIiA6ICJFeHByZXNzaW9uUmVmIg0KICAgICAgICAgICAgfQ0KICAgICAgICAgfSwgew0KICAgICAgICAgICAgImxvY2FsSWQiIDogIjg3IiwNCiAgICAgICAgICAgICJsb2NhdG9yIiA6ICI3MDoxLTczOjE0OSIsDQogICAgICAgICAgICAibmFtZSIgOiAiRmxleGlibGUgU2lnbW9pZG9zY29weSBQZXJmb3JtZWQiLA0KICAgICAgICAgICAgImNvbnRleHQiIDogIlBhdGllbnQiLA0KICAgICAgICAgICAgImFjY2Vzc0xldmVsIiA6ICJQdWJsaWMiLA0KICAgICAgICAgICAgImFubm90YXRpb24iIDogWyB7DQogICAgICAgICAgICAgICAidHlwZSIgOiAiQW5ub3RhdGlvbiIsDQogICAgICAgICAgICAgICAicyIgOiB7DQogICAgICAgICAgICAgICAgICAiciIgOiAiODciLA0KICAgICAgICAgICAgICAgICAgInMiIDogWyB7DQogICAgICAgICAgICAgICAgICAgICAidmFsdWUiIDogWyAiIiwiZGVmaW5lICIsIlwiRmxleGlibGUgU2lnbW9pZG9zY29weSBQZXJmb3JtZWRcIiIsIjpcbiAgIiBdDQogICAgICAgICAgICAgICAgICB9LCB7DQogICAgICAgICAgICAgICAgICAgICAiciIgOiAiODYiLA0KICAgICAgICAgICAgICAgICAgICAgInMiIDogWyB7DQogICAgICAgICAgICAgICAgICAgICAgICAicyIgOiBbIHsNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICJyIiA6ICI3MiIsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAicyIgOiBbIHsNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJyIiA6ICI3MSIsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAicyIgOiBbIHsNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJyIiA6ICI3MSIsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAicyIgOiBbIHsNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJ2YWx1ZSIgOiBbICJbIiwiUHJvY2VkdXJlIiwiOiAiIF0NCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIH0sIHsNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJzIiA6IFsgew0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInZhbHVlIiA6IFsgIlwiRmxleGlibGUgU2lnbW9pZG9zY29weVwiIiBdDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB9IF0NCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIH0sIHsNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJ2YWx1ZSIgOiBbICJdIiBdDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB9IF0NCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIH0gXQ0KICAgICAgICAgICAgICAgICAgICAgICAgICAgfSwgew0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInZhbHVlIiA6IFsgIiAiLCJGbGV4aWJsZVNpZ21vaWRvc2NvcHkiIF0NCiAgICAgICAgICAgICAgICAgICAgICAgICAgIH0gXQ0KICAgICAgICAgICAgICAgICAgICAgICAgfSBdDQogICAgICAgICAgICAgICAgICAgICB9LCB7DQogICAgICAgICAgICAgICAgICAgICAgICAidmFsdWUiIDogWyAiXG4gICAgICAgICAgICAgICAgICAgIFx0XHQiIF0NCiAgICAgICAgICAgICAgICAgICAgIH0sIHsNCiAgICAgICAgICAgICAgICAgICAgICAgICJyIiA6ICI4NSIsDQogICAgICAgICAgICAgICAgICAgICAgICAicyIgOiBbIHsNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICJ2YWx1ZSIgOiBbICJ3aGVyZSAiIF0NCiAgICAgICAgICAgICAgICAgICAgICAgIH0sIHsNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICJyIiA6ICI4NSIsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAicyIgOiBbIHsNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJyIiA6ICI3NiIsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAicyIgOiBbIHsNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJyIiA6ICI3NCIsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAicyIgOiBbIHsNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJyIiA6ICI3MyIsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAicyIgOiBbIHsNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJ2YWx1ZSIgOiBbICJGbGV4aWJsZVNpZ21vaWRvc2NvcHkiIF0NCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIH0gXQ0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfSwgew0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInZhbHVlIiA6IFsgIi4iIF0NCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIH0sIHsNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJyIiA6ICI3NCIsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAicyIgOiBbIHsNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJ2YWx1ZSIgOiBbICJzdGF0dXMiIF0NCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIH0gXQ0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfSBdDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICB9LCB7DQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAidmFsdWUiIDogWyAiICIsIj0iLCIgIiBdDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICB9LCB7DQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAiciIgOiAiNzUiLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInMiIDogWyB7DQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAidmFsdWUiIDogWyAiJ2NvbXBsZXRlZCciIF0NCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIH0gXQ0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfSBdDQogICAgICAgICAgICAgICAgICAgICAgICAgICB9LCB7DQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAidmFsdWUiIDogWyAiXG4gICAgICAgICAgICAgICAgICAgIFx0XHRcdGFuZCAiIF0NCiAgICAgICAgICAgICAgICAgICAgICAgICAgIH0sIHsNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJyIiA6ICI4NCIsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAicyIgOiBbIHsNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJyIiA6ICI4MCIsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAicyIgOiBbIHsNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJyIiA6ICI3NyIsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAicyIgOiBbIHsNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJ2YWx1ZSIgOiBbICJHbG9iYWwiIF0NCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIH0gXQ0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfSwgew0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInZhbHVlIiA6IFsgIi4iIF0NCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIH0sIHsNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJyIiA6ICI4MCIsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAicyIgOiBbIHsNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJ2YWx1ZSIgOiBbICJcIk5vcm1hbGl6ZSBJbnRlcnZhbFwiIiwiKCIgXQ0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfSwgew0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInIiIDogIjc5IiwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJzIiA6IFsgew0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInIiIDogIjc4IiwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJzIiA6IFsgew0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInZhbHVlIiA6IFsgIkZsZXhpYmxlU2lnbW9pZG9zY29weSIgXQ0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfSBdDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB9LCB7DQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAidmFsdWUiIDogWyAiLiIgXQ0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfSwgew0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInIiIDogIjc5IiwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJzIiA6IFsgew0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInZhbHVlIiA6IFsgInBlcmZvcm1lZCIgXQ0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfSBdDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB9IF0NCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIH0sIHsNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJ2YWx1ZSIgOiBbICIpIiBdDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB9IF0NCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIH0gXQ0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfSwgew0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInZhbHVlIiA6IFsgIiAiIF0NCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIH0sIHsNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJyIiA6ICI4NCIsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAicyIgOiBbIHsNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJ2YWx1ZSIgOiBbICJlbmRzICIgXQ0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfSwgew0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInIiIDogIjgzIiwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJzIiA6IFsgew0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInZhbHVlIiA6IFsgIjUgIiwieWVhcnMiIF0NCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIH0gXQ0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfSwgew0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInZhbHVlIiA6IFsgIiBvciBsZXNzIG9uIG9yIGJlZm9yZSIgXQ0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfSBdDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICB9LCB7DQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAidmFsdWUiIDogWyAiICIgXQ0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfSwgew0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInIiIDogIjgyIiwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJzIiA6IFsgew0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInZhbHVlIiA6IFsgImVuZCBvZiAiIF0NCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIH0sIHsNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJyIiA6ICI4MSIsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAicyIgOiBbIHsNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJ2YWx1ZSIgOiBbICJcIk1lYXN1cmVtZW50IFBlcmlvZFwiIiBdDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB9IF0NCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIH0gXQ0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfSBdDQogICAgICAgICAgICAgICAgICAgICAgICAgICB9IF0NCiAgICAgICAgICAgICAgICAgICAgICAgIH0gXQ0KICAgICAgICAgICAgICAgICAgICAgfSBdDQogICAgICAgICAgICAgICAgICB9IF0NCiAgICAgICAgICAgICAgIH0NCiAgICAgICAgICAgIH0gXSwNCiAgICAgICAgICAgICJleHByZXNzaW9uIiA6IHsNCiAgICAgICAgICAgICAgICJsb2NhbElkIiA6ICI4NiIsDQogICAgICAgICAgICAgICAibG9jYXRvciIgOiAiNzE6My03MzoxNDkiLA0KICAgICAgICAgICAgICAgInR5cGUiIDogIlF1ZXJ5IiwNCiAgICAgICAgICAgICAgICJzb3VyY2UiIDogWyB7DQogICAgICAgICAgICAgICAgICAibG9jYWxJZCIgOiAiNzIiLA0KICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjcxOjMtNzE6NjEiLA0KICAgICAgICAgICAgICAgICAgImFsaWFzIiA6ICJGbGV4aWJsZVNpZ21vaWRvc2NvcHkiLA0KICAgICAgICAgICAgICAgICAgImV4cHJlc3Npb24iIDogew0KICAgICAgICAgICAgICAgICAgICAgImxvY2FsSWQiIDogIjcxIiwNCiAgICAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICI3MTozLTcxOjM5IiwNCiAgICAgICAgICAgICAgICAgICAgICJkYXRhVHlwZSIgOiAie2h0dHA6Ly9obDcub3JnL2ZoaXJ9UHJvY2VkdXJlIiwNCiAgICAgICAgICAgICAgICAgICAgICJ0ZW1wbGF0ZUlkIiA6ICJodHRwOi8vaGw3Lm9yZy9maGlyL1N0cnVjdHVyZURlZmluaXRpb24vUHJvY2VkdXJlIiwNCiAgICAgICAgICAgICAgICAgICAgICJjb2RlUHJvcGVydHkiIDogImNvZGUiLA0KICAgICAgICAgICAgICAgICAgICAgImNvZGVDb21wYXJhdG9yIiA6ICJpbiIsDQogICAgICAgICAgICAgICAgICAgICAidHlwZSIgOiAiUmV0cmlldmUiLA0KICAgICAgICAgICAgICAgICAgICAgImNvZGVzIiA6IHsNCiAgICAgICAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICI3MToxNS03MTozOCIsDQogICAgICAgICAgICAgICAgICAgICAgICAibmFtZSIgOiAiRmxleGlibGUgU2lnbW9pZG9zY29weSIsDQogICAgICAgICAgICAgICAgICAgICAgICAidHlwZSIgOiAiVmFsdWVTZXRSZWYiDQogICAgICAgICAgICAgICAgICAgICB9DQogICAgICAgICAgICAgICAgICB9DQogICAgICAgICAgICAgICB9IF0sDQogICAgICAgICAgICAgICAicmVsYXRpb25zaGlwIiA6IFsgXSwNCiAgICAgICAgICAgICAgICJ3aGVyZSIgOiB7DQogICAgICAgICAgICAgICAgICAibG9jYWxJZCIgOiAiODUiLA0KICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjcyOjIzLTczOjE0OSIsDQogICAgICAgICAgICAgICAgICAidHlwZSIgOiAiQW5kIiwNCiAgICAgICAgICAgICAgICAgICJvcGVyYW5kIiA6IFsgew0KICAgICAgICAgICAgICAgICAgICAgImxvY2FsSWQiIDogIjc2IiwNCiAgICAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICI3MjoyOS03Mjo3MCIsDQogICAgICAgICAgICAgICAgICAgICAidHlwZSIgOiAiRXF1YWwiLA0KICAgICAgICAgICAgICAgICAgICAgIm9wZXJhbmQiIDogWyB7DQogICAgICAgICAgICAgICAgICAgICAgICAibmFtZSIgOiAiVG9TdHJpbmciLA0KICAgICAgICAgICAgICAgICAgICAgICAgImxpYnJhcnlOYW1lIiA6ICJGSElSSGVscGVycyIsDQogICAgICAgICAgICAgICAgICAgICAgICAidHlwZSIgOiAiRnVuY3Rpb25SZWYiLA0KICAgICAgICAgICAgICAgICAgICAgICAgIm9wZXJhbmQiIDogWyB7DQogICAgICAgICAgICAgICAgICAgICAgICAgICAibG9jYWxJZCIgOiAiNzQiLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjcyOjI5LTcyOjU2IiwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICJwYXRoIiA6ICJzdGF0dXMiLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgInNjb3BlIiA6ICJGbGV4aWJsZVNpZ21vaWRvc2NvcHkiLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgInR5cGUiIDogIlByb3BlcnR5Ig0KICAgICAgICAgICAgICAgICAgICAgICAgfSBdDQogICAgICAgICAgICAgICAgICAgICB9LCB7DQogICAgICAgICAgICAgICAgICAgICAgICAibG9jYWxJZCIgOiAiNzUiLA0KICAgICAgICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjcyOjYwLTcyOjcwIiwNCiAgICAgICAgICAgICAgICAgICAgICAgICJ2YWx1ZVR5cGUiIDogInt1cm46aGw3LW9yZzplbG0tdHlwZXM6cjF9U3RyaW5nIiwNCiAgICAgICAgICAgICAgICAgICAgICAgICJ2YWx1ZSIgOiAiY29tcGxldGVkIiwNCiAgICAgICAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJMaXRlcmFsIg0KICAgICAgICAgICAgICAgICAgICAgfSBdDQogICAgICAgICAgICAgICAgICB9LCB7DQogICAgICAgICAgICAgICAgICAgICAibG9jYWxJZCIgOiAiODQiLA0KICAgICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjczOjI4LTczOjE0OSIsDQogICAgICAgICAgICAgICAgICAgICAidHlwZSIgOiAiQW5kIiwNCiAgICAgICAgICAgICAgICAgICAgICJvcGVyYW5kIiA6IFsgew0KICAgICAgICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjczOjk0LTczOjEwOCIsDQogICAgICAgICAgICAgICAgICAgICAgICAidHlwZSIgOiAiSW4iLA0KICAgICAgICAgICAgICAgICAgICAgICAgIm9wZXJhbmQiIDogWyB7DQogICAgICAgICAgICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiNzM6ODktNzM6OTIiLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgInR5cGUiIDogIkVuZCIsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAib3BlcmFuZCIgOiB7DQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAibG9jYWxJZCIgOiAiODAiLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjczOjI4LTczOjg3IiwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJOb3JtYWxpemUgSW50ZXJ2YWwiLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgImxpYnJhcnlOYW1lIiA6ICJHbG9iYWwiLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInR5cGUiIDogIkZ1bmN0aW9uUmVmIiwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJvcGVyYW5kIiA6IFsgew0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgImxvY2FsSWQiIDogIjc5IiwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICI3Mzo1Ni03Mzo4NiIsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAicGF0aCIgOiAicGVyZm9ybWVkIiwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJzY29wZSIgOiAiRmxleGlibGVTaWdtb2lkb3Njb3B5IiwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJQcm9wZXJ0eSINCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIH0gXQ0KICAgICAgICAgICAgICAgICAgICAgICAgICAgfQ0KICAgICAgICAgICAgICAgICAgICAgICAgfSwgew0KICAgICAgICAgICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjczOjk0LTczOjEwOCIsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAibG93Q2xvc2VkIiA6IHRydWUsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAiaGlnaENsb3NlZCIgOiB0cnVlLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgInR5cGUiIDogIkludGVydmFsIiwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICJsb3ciIDogew0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjczOjEyMy03MzoxNDkiLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInR5cGUiIDogIlN1YnRyYWN0IiwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJvcGVyYW5kIiA6IFsgew0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgImxvY2FsSWQiIDogIjgyIiwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICI3MzoxMjMtNzM6MTQ5IiwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJFbmQiLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIm9wZXJhbmQiIDogew0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgImxvY2FsSWQiIDogIjgxIiwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICI3MzoxMzAtNzM6MTQ5IiwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJNZWFzdXJlbWVudCBQZXJpb2QiLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInR5cGUiIDogIlBhcmFtZXRlclJlZiINCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIH0NCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIH0sIHsNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJsb2NhbElkIiA6ICI4MyIsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiNzM6OTQtNzM6MTAwIiwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJ2YWx1ZSIgOiA1LA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInVuaXQiIDogInllYXJzIiwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJRdWFudGl0eSINCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIH0gXQ0KICAgICAgICAgICAgICAgICAgICAgICAgICAgfSwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICJoaWdoIiA6IHsNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJsb2NhbElkIiA6ICI4MiIsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiNzM6MTIzLTczOjE0OSIsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAidHlwZSIgOiAiRW5kIiwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJvcGVyYW5kIiA6IHsNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJsb2NhbElkIiA6ICI4MSIsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiNzM6MTMwLTczOjE0OSIsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAibmFtZSIgOiAiTWVhc3VyZW1lbnQgUGVyaW9kIiwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJQYXJhbWV0ZXJSZWYiDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICB9DQogICAgICAgICAgICAgICAgICAgICAgICAgICB9DQogICAgICAgICAgICAgICAgICAgICAgICB9IF0NCiAgICAgICAgICAgICAgICAgICAgIH0sIHsNCiAgICAgICAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICI3Mzo5NC03MzoxMDgiLA0KICAgICAgICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk5vdCIsDQogICAgICAgICAgICAgICAgICAgICAgICAib3BlcmFuZCIgOiB7DQogICAgICAgICAgICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiNzM6OTQtNzM6MTA4IiwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJJc051bGwiLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgIm9wZXJhbmQiIDogew0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgImxvY2FsSWQiIDogIjgyIiwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICI3MzoxMjMtNzM6MTQ5IiwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJFbmQiLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIm9wZXJhbmQiIDogew0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgImxvY2FsSWQiIDogIjgxIiwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICI3MzoxMzAtNzM6MTQ5IiwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJNZWFzdXJlbWVudCBQZXJpb2QiLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInR5cGUiIDogIlBhcmFtZXRlclJlZiINCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIH0NCiAgICAgICAgICAgICAgICAgICAgICAgICAgIH0NCiAgICAgICAgICAgICAgICAgICAgICAgIH0NCiAgICAgICAgICAgICAgICAgICAgIH0gXQ0KICAgICAgICAgICAgICAgICAgfSBdDQogICAgICAgICAgICAgICB9DQogICAgICAgICAgICB9DQogICAgICAgICB9LCB7DQogICAgICAgICAgICAibG9jYWxJZCIgOiAiMTA0IiwNCiAgICAgICAgICAgICJsb2NhdG9yIiA6ICI3NToxLTc4OjE0MCIsDQogICAgICAgICAgICAibmFtZSIgOiAiQ1QgQ29sb25vZ3JhcGh5IFBlcmZvcm1lZCIsDQogICAgICAgICAgICAiY29udGV4dCIgOiAiUGF0aWVudCIsDQogICAgICAgICAgICAiYWNjZXNzTGV2ZWwiIDogIlB1YmxpYyIsDQogICAgICAgICAgICAiYW5ub3RhdGlvbiIgOiBbIHsNCiAgICAgICAgICAgICAgICJ0eXBlIiA6ICJBbm5vdGF0aW9uIiwNCiAgICAgICAgICAgICAgICJzIiA6IHsNCiAgICAgICAgICAgICAgICAgICJyIiA6ICIxMDQiLA0KICAgICAgICAgICAgICAgICAgInMiIDogWyB7DQogICAgICAgICAgICAgICAgICAgICAidmFsdWUiIDogWyAiIiwiZGVmaW5lICIsIlwiQ1QgQ29sb25vZ3JhcGh5IFBlcmZvcm1lZFwiIiwiOlxuICAiIF0NCiAgICAgICAgICAgICAgICAgIH0sIHsNCiAgICAgICAgICAgICAgICAgICAgICJyIiA6ICIxMDMiLA0KICAgICAgICAgICAgICAgICAgICAgInMiIDogWyB7DQogICAgICAgICAgICAgICAgICAgICAgICAicyIgOiBbIHsNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICJyIiA6ICI4OSIsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAicyIgOiBbIHsNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJyIiA6ICI4OCIsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAicyIgOiBbIHsNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJyIiA6ICI4OCIsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAicyIgOiBbIHsNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJ2YWx1ZSIgOiBbICJbIiwiUHJvY2VkdXJlIiwiOiAiIF0NCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIH0sIHsNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJzIiA6IFsgew0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInZhbHVlIiA6IFsgIlwiQ1QgQ29sb25vZ3JhcGh5XCIiIF0NCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIH0gXQ0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfSwgew0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInZhbHVlIiA6IFsgIl0iIF0NCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIH0gXQ0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfSBdDQogICAgICAgICAgICAgICAgICAgICAgICAgICB9LCB7DQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAidmFsdWUiIDogWyAiICIsIkNvbG9ub2dyYXBoeSIgXQ0KICAgICAgICAgICAgICAgICAgICAgICAgICAgfSBdDQogICAgICAgICAgICAgICAgICAgICAgICB9IF0NCiAgICAgICAgICAgICAgICAgICAgIH0sIHsNCiAgICAgICAgICAgICAgICAgICAgICAgICJ2YWx1ZSIgOiBbICJcbiAgICAgICAgICAgICAgICAgICAgXHRcdCIgXQ0KICAgICAgICAgICAgICAgICAgICAgfSwgew0KICAgICAgICAgICAgICAgICAgICAgICAgInIiIDogIjEwMiIsDQogICAgICAgICAgICAgICAgICAgICAgICAicyIgOiBbIHsNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICJ2YWx1ZSIgOiBbICJ3aGVyZSAiIF0NCiAgICAgICAgICAgICAgICAgICAgICAgIH0sIHsNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICJyIiA6ICIxMDIiLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgInMiIDogWyB7DQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAiciIgOiAiOTMiLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInMiIDogWyB7DQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAiciIgOiAiOTEiLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInMiIDogWyB7DQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAiciIgOiAiOTAiLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInMiIDogWyB7DQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAidmFsdWUiIDogWyAiQ29sb25vZ3JhcGh5IiBdDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB9IF0NCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIH0sIHsNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJ2YWx1ZSIgOiBbICIuIiBdDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB9LCB7DQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAiciIgOiAiOTEiLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInMiIDogWyB7DQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAidmFsdWUiIDogWyAic3RhdHVzIiBdDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB9IF0NCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIH0gXQ0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfSwgew0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInZhbHVlIiA6IFsgIiAiLCI9IiwiICIgXQ0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfSwgew0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInIiIDogIjkyIiwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJzIiA6IFsgew0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInZhbHVlIiA6IFsgIidjb21wbGV0ZWQnIiBdDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB9IF0NCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIH0gXQ0KICAgICAgICAgICAgICAgICAgICAgICAgICAgfSwgew0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInZhbHVlIiA6IFsgIlxuICAgICAgICAgICAgICAgICAgICBcdFx0XHRhbmQgIiBdDQogICAgICAgICAgICAgICAgICAgICAgICAgICB9LCB7DQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAiciIgOiAiMTAxIiwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJzIiA6IFsgew0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInIiIDogIjk3IiwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJzIiA6IFsgew0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInIiIDogIjk0IiwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJzIiA6IFsgew0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInZhbHVlIiA6IFsgIkdsb2JhbCIgXQ0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfSBdDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB9LCB7DQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAidmFsdWUiIDogWyAiLiIgXQ0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfSwgew0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInIiIDogIjk3IiwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJzIiA6IFsgew0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInZhbHVlIiA6IFsgIlwiTm9ybWFsaXplIEludGVydmFsXCIiLCIoIiBdDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB9LCB7DQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAiciIgOiAiOTYiLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInMiIDogWyB7DQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAiciIgOiAiOTUiLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInMiIDogWyB7DQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAidmFsdWUiIDogWyAiQ29sb25vZ3JhcGh5IiBdDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB9IF0NCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIH0sIHsNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJ2YWx1ZSIgOiBbICIuIiBdDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB9LCB7DQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAiciIgOiAiOTYiLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInMiIDogWyB7DQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAidmFsdWUiIDogWyAicGVyZm9ybWVkIiBdDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB9IF0NCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIH0gXQ0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfSwgew0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInZhbHVlIiA6IFsgIikiIF0NCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIH0gXQ0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfSBdDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICB9LCB7DQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAidmFsdWUiIDogWyAiICIgXQ0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfSwgew0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInIiIDogIjEwMSIsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAicyIgOiBbIHsNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJ2YWx1ZSIgOiBbICJlbmRzICIgXQ0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfSwgew0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInIiIDogIjEwMCIsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAicyIgOiBbIHsNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJ2YWx1ZSIgOiBbICI1ICIsInllYXJzIiBdDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB9IF0NCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIH0sIHsNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJ2YWx1ZSIgOiBbICIgb3IgbGVzcyBvbiBvciBiZWZvcmUiIF0NCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIH0gXQ0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfSwgew0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInZhbHVlIiA6IFsgIiAiIF0NCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIH0sIHsNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJyIiA6ICI5OSIsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAicyIgOiBbIHsNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJ2YWx1ZSIgOiBbICJlbmQgb2YgIiBdDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB9LCB7DQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAiciIgOiAiOTgiLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInMiIDogWyB7DQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAidmFsdWUiIDogWyAiXCJNZWFzdXJlbWVudCBQZXJpb2RcIiIgXQ0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfSBdDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB9IF0NCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIH0gXQ0KICAgICAgICAgICAgICAgICAgICAgICAgICAgfSBdDQogICAgICAgICAgICAgICAgICAgICAgICB9IF0NCiAgICAgICAgICAgICAgICAgICAgIH0gXQ0KICAgICAgICAgICAgICAgICAgfSBdDQogICAgICAgICAgICAgICB9DQogICAgICAgICAgICB9IF0sDQogICAgICAgICAgICAiZXhwcmVzc2lvbiIgOiB7DQogICAgICAgICAgICAgICAibG9jYWxJZCIgOiAiMTAzIiwNCiAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICI3NjozLTc4OjE0MCIsDQogICAgICAgICAgICAgICAidHlwZSIgOiAiUXVlcnkiLA0KICAgICAgICAgICAgICAgInNvdXJjZSIgOiBbIHsNCiAgICAgICAgICAgICAgICAgICJsb2NhbElkIiA6ICI4OSIsDQogICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiNzY6My03Njo0NSIsDQogICAgICAgICAgICAgICAgICAiYWxpYXMiIDogIkNvbG9ub2dyYXBoeSIsDQogICAgICAgICAgICAgICAgICAiZXhwcmVzc2lvbiIgOiB7DQogICAgICAgICAgICAgICAgICAgICAibG9jYWxJZCIgOiAiODgiLA0KICAgICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjc2OjMtNzY6MzIiLA0KICAgICAgICAgICAgICAgICAgICAgImRhdGFUeXBlIiA6ICJ7aHR0cDovL2hsNy5vcmcvZmhpcn1Qcm9jZWR1cmUiLA0KICAgICAgICAgICAgICAgICAgICAgInRlbXBsYXRlSWQiIDogImh0dHA6Ly9obDcub3JnL2ZoaXIvU3RydWN0dXJlRGVmaW5pdGlvbi9Qcm9jZWR1cmUiLA0KICAgICAgICAgICAgICAgICAgICAgImNvZGVQcm9wZXJ0eSIgOiAiY29kZSIsDQogICAgICAgICAgICAgICAgICAgICAiY29kZUNvbXBhcmF0b3IiIDogImluIiwNCiAgICAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJSZXRyaWV2ZSIsDQogICAgICAgICAgICAgICAgICAgICAiY29kZXMiIDogew0KICAgICAgICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjc2OjE1LTc2OjMxIiwNCiAgICAgICAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJDVCBDb2xvbm9ncmFwaHkiLA0KICAgICAgICAgICAgICAgICAgICAgICAgInR5cGUiIDogIlZhbHVlU2V0UmVmIg0KICAgICAgICAgICAgICAgICAgICAgfQ0KICAgICAgICAgICAgICAgICAgfQ0KICAgICAgICAgICAgICAgfSBdLA0KICAgICAgICAgICAgICAgInJlbGF0aW9uc2hpcCIgOiBbIF0sDQogICAgICAgICAgICAgICAid2hlcmUiIDogew0KICAgICAgICAgICAgICAgICAgImxvY2FsSWQiIDogIjEwMiIsDQogICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiNzc6MjMtNzg6MTQwIiwNCiAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJBbmQiLA0KICAgICAgICAgICAgICAgICAgIm9wZXJhbmQiIDogWyB7DQogICAgICAgICAgICAgICAgICAgICAibG9jYWxJZCIgOiAiOTMiLA0KICAgICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjc3OjI5LTc3OjYxIiwNCiAgICAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJFcXVhbCIsDQogICAgICAgICAgICAgICAgICAgICAib3BlcmFuZCIgOiBbIHsNCiAgICAgICAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJUb1N0cmluZyIsDQogICAgICAgICAgICAgICAgICAgICAgICAibGlicmFyeU5hbWUiIDogIkZISVJIZWxwZXJzIiwNCiAgICAgICAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJGdW5jdGlvblJlZiIsDQogICAgICAgICAgICAgICAgICAgICAgICAib3BlcmFuZCIgOiBbIHsNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICJsb2NhbElkIiA6ICI5MSIsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiNzc6MjktNzc6NDciLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgInBhdGgiIDogInN0YXR1cyIsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAic2NvcGUiIDogIkNvbG9ub2dyYXBoeSIsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAidHlwZSIgOiAiUHJvcGVydHkiDQogICAgICAgICAgICAgICAgICAgICAgICB9IF0NCiAgICAgICAgICAgICAgICAgICAgIH0sIHsNCiAgICAgICAgICAgICAgICAgICAgICAgICJsb2NhbElkIiA6ICI5MiIsDQogICAgICAgICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiNzc6NTEtNzc6NjEiLA0KICAgICAgICAgICAgICAgICAgICAgICAgInZhbHVlVHlwZSIgOiAie3VybjpobDctb3JnOmVsbS10eXBlczpyMX1TdHJpbmciLA0KICAgICAgICAgICAgICAgICAgICAgICAgInZhbHVlIiA6ICJjb21wbGV0ZWQiLA0KICAgICAgICAgICAgICAgICAgICAgICAgInR5cGUiIDogIkxpdGVyYWwiDQogICAgICAgICAgICAgICAgICAgICB9IF0NCiAgICAgICAgICAgICAgICAgIH0sIHsNCiAgICAgICAgICAgICAgICAgICAgICJsb2NhbElkIiA6ICIxMDEiLA0KICAgICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjc4OjI4LTc4OjE0MCIsDQogICAgICAgICAgICAgICAgICAgICAidHlwZSIgOiAiQW5kIiwNCiAgICAgICAgICAgICAgICAgICAgICJvcGVyYW5kIiA6IFsgew0KICAgICAgICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjc4Ojg1LTc4Ojk5IiwNCiAgICAgICAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJJbiIsDQogICAgICAgICAgICAgICAgICAgICAgICAib3BlcmFuZCIgOiBbIHsNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICI3ODo4MC03ODo4MyIsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAidHlwZSIgOiAiRW5kIiwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICJvcGVyYW5kIiA6IHsNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJsb2NhbElkIiA6ICI5NyIsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiNzg6MjgtNzg6NzgiLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIm5hbWUiIDogIk5vcm1hbGl6ZSBJbnRlcnZhbCIsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAibGlicmFyeU5hbWUiIDogIkdsb2JhbCIsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAidHlwZSIgOiAiRnVuY3Rpb25SZWYiLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIm9wZXJhbmQiIDogWyB7DQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAibG9jYWxJZCIgOiAiOTYiLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjc4OjU2LTc4Ojc3IiwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJwYXRoIiA6ICJwZXJmb3JtZWQiLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInNjb3BlIiA6ICJDb2xvbm9ncmFwaHkiLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInR5cGUiIDogIlByb3BlcnR5Ig0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfSBdDQogICAgICAgICAgICAgICAgICAgICAgICAgICB9DQogICAgICAgICAgICAgICAgICAgICAgICB9LCB7DQogICAgICAgICAgICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiNzg6ODUtNzg6OTkiLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgImxvd0Nsb3NlZCIgOiB0cnVlLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgImhpZ2hDbG9zZWQiIDogdHJ1ZSwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJJbnRlcnZhbCIsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAibG93IiA6IHsNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICI3ODoxMTQtNzg6MTQwIiwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJTdWJ0cmFjdCIsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAib3BlcmFuZCIgOiBbIHsNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJsb2NhbElkIiA6ICI5OSIsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiNzg6MTE0LTc4OjE0MCIsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAidHlwZSIgOiAiRW5kIiwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJvcGVyYW5kIiA6IHsNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJsb2NhbElkIiA6ICI5OCIsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiNzg6MTIxLTc4OjE0MCIsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAibmFtZSIgOiAiTWVhc3VyZW1lbnQgUGVyaW9kIiwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJQYXJhbWV0ZXJSZWYiDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB9DQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICB9LCB7DQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAibG9jYWxJZCIgOiAiMTAwIiwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICI3ODo4NS03ODo5MSIsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAidmFsdWUiIDogNSwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJ1bml0IiA6ICJ5ZWFycyIsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAidHlwZSIgOiAiUXVhbnRpdHkiDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICB9IF0NCiAgICAgICAgICAgICAgICAgICAgICAgICAgIH0sDQogICAgICAgICAgICAgICAgICAgICAgICAgICAiaGlnaCIgOiB7DQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAibG9jYWxJZCIgOiAiOTkiLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjc4OjExNC03ODoxNDAiLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInR5cGUiIDogIkVuZCIsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAib3BlcmFuZCIgOiB7DQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAibG9jYWxJZCIgOiAiOTgiLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjc4OjEyMS03ODoxNDAiLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIm5hbWUiIDogIk1lYXN1cmVtZW50IFBlcmlvZCIsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAidHlwZSIgOiAiUGFyYW1ldGVyUmVmIg0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfQ0KICAgICAgICAgICAgICAgICAgICAgICAgICAgfQ0KICAgICAgICAgICAgICAgICAgICAgICAgfSBdDQogICAgICAgICAgICAgICAgICAgICB9LCB7DQogICAgICAgICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiNzg6ODUtNzg6OTkiLA0KICAgICAgICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk5vdCIsDQogICAgICAgICAgICAgICAgICAgICAgICAib3BlcmFuZCIgOiB7DQogICAgICAgICAgICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiNzg6ODUtNzg6OTkiLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgInR5cGUiIDogIklzTnVsbCIsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAib3BlcmFuZCIgOiB7DQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAibG9jYWxJZCIgOiAiOTkiLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjc4OjExNC03ODoxNDAiLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInR5cGUiIDogIkVuZCIsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAib3BlcmFuZCIgOiB7DQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAibG9jYWxJZCIgOiAiOTgiLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjc4OjEyMS03ODoxNDAiLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIm5hbWUiIDogIk1lYXN1cmVtZW50IFBlcmlvZCIsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAidHlwZSIgOiAiUGFyYW1ldGVyUmVmIg0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfQ0KICAgICAgICAgICAgICAgICAgICAgICAgICAgfQ0KICAgICAgICAgICAgICAgICAgICAgICAgfQ0KICAgICAgICAgICAgICAgICAgICAgfSBdDQogICAgICAgICAgICAgICAgICB9IF0NCiAgICAgICAgICAgICAgIH0NCiAgICAgICAgICAgIH0NCiAgICAgICAgIH0sIHsNCiAgICAgICAgICAgICJsb2NhbElkIiA6ICIxMjAiLA0KICAgICAgICAgICAgImxvY2F0b3IiIDogIjgwOjEtODM6MTIzIiwNCiAgICAgICAgICAgICJuYW1lIiA6ICJUb3RhbCBDb2xlY3RvbXkgUGVyZm9ybWVkIiwNCiAgICAgICAgICAgICJjb250ZXh0IiA6ICJQYXRpZW50IiwNCiAgICAgICAgICAgICJhY2Nlc3NMZXZlbCIgOiAiUHVibGljIiwNCiAgICAgICAgICAgICJhbm5vdGF0aW9uIiA6IFsgew0KICAgICAgICAgICAgICAgInR5cGUiIDogIkFubm90YXRpb24iLA0KICAgICAgICAgICAgICAgInMiIDogew0KICAgICAgICAgICAgICAgICAgInIiIDogIjEyMCIsDQogICAgICAgICAgICAgICAgICAicyIgOiBbIHsNCiAgICAgICAgICAgICAgICAgICAgICJ2YWx1ZSIgOiBbICIiLCJkZWZpbmUgIiwiXCJUb3RhbCBDb2xlY3RvbXkgUGVyZm9ybWVkXCIiLCI6XG4gICIgXQ0KICAgICAgICAgICAgICAgICAgfSwgew0KICAgICAgICAgICAgICAgICAgICAgInIiIDogIjExOSIsDQogICAgICAgICAgICAgICAgICAgICAicyIgOiBbIHsNCiAgICAgICAgICAgICAgICAgICAgICAgICJzIiA6IFsgew0KICAgICAgICAgICAgICAgICAgICAgICAgICAgInIiIDogIjEwNiIsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAicyIgOiBbIHsNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJyIiA6ICIxMDUiLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInMiIDogWyB7DQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAiciIgOiAiMTA1IiwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJzIiA6IFsgew0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInZhbHVlIiA6IFsgIlsiLCJQcm9jZWR1cmUiLCI6ICIgXQ0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfSwgew0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInMiIDogWyB7DQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAidmFsdWUiIDogWyAiXCJUb3RhbCBDb2xlY3RvbXlcIiIgXQ0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfSBdDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB9LCB7DQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAidmFsdWUiIDogWyAiXSIgXQ0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfSBdDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICB9IF0NCiAgICAgICAgICAgICAgICAgICAgICAgICAgIH0sIHsNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJ2YWx1ZSIgOiBbICIgIiwiQ29sZWN0b215IiBdDQogICAgICAgICAgICAgICAgICAgICAgICAgICB9IF0NCiAgICAgICAgICAgICAgICAgICAgICAgIH0gXQ0KICAgICAgICAgICAgICAgICAgICAgfSwgew0KICAgICAgICAgICAgICAgICAgICAgICAgInZhbHVlIiA6IFsgIlxuICAgICAgICAgICAgICAgICAgICBcdFx0IiBdDQogICAgICAgICAgICAgICAgICAgICB9LCB7DQogICAgICAgICAgICAgICAgICAgICAgICAiciIgOiAiMTE4IiwNCiAgICAgICAgICAgICAgICAgICAgICAgICJzIiA6IFsgew0KICAgICAgICAgICAgICAgICAgICAgICAgICAgInZhbHVlIiA6IFsgIndoZXJlICIgXQ0KICAgICAgICAgICAgICAgICAgICAgICAgfSwgew0KICAgICAgICAgICAgICAgICAgICAgICAgICAgInIiIDogIjExOCIsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAicyIgOiBbIHsNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJyIiA6ICIxMTAiLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInMiIDogWyB7DQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAiciIgOiAiMTA4IiwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJzIiA6IFsgew0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInIiIDogIjEwNyIsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAicyIgOiBbIHsNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJ2YWx1ZSIgOiBbICJDb2xlY3RvbXkiIF0NCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIH0gXQ0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfSwgew0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInZhbHVlIiA6IFsgIi4iIF0NCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIH0sIHsNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJyIiA6ICIxMDgiLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInMiIDogWyB7DQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAidmFsdWUiIDogWyAic3RhdHVzIiBdDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB9IF0NCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIH0gXQ0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfSwgew0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInZhbHVlIiA6IFsgIiAiLCI9IiwiICIgXQ0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfSwgew0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInIiIDogIjEwOSIsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAicyIgOiBbIHsNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJ2YWx1ZSIgOiBbICInY29tcGxldGVkJyIgXQ0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfSBdDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICB9IF0NCiAgICAgICAgICAgICAgICAgICAgICAgICAgIH0sIHsNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJ2YWx1ZSIgOiBbICJcbiAgICAgICAgICAgICAgICAgICAgXHRcdFx0YW5kICIgXQ0KICAgICAgICAgICAgICAgICAgICAgICAgICAgfSwgew0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInIiIDogIjExNyIsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAicyIgOiBbIHsNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJyIiA6ICIxMTQiLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInMiIDogWyB7DQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAiciIgOiAiMTExIiwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJzIiA6IFsgew0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInZhbHVlIiA6IFsgIkdsb2JhbCIgXQ0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfSBdDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB9LCB7DQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAidmFsdWUiIDogWyAiLiIgXQ0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfSwgew0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInIiIDogIjExNCIsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAicyIgOiBbIHsNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJ2YWx1ZSIgOiBbICJcIk5vcm1hbGl6ZSBJbnRlcnZhbFwiIiwiKCIgXQ0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfSwgew0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInIiIDogIjExMyIsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAicyIgOiBbIHsNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJyIiA6ICIxMTIiLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInMiIDogWyB7DQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAidmFsdWUiIDogWyAiQ29sZWN0b215IiBdDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB9IF0NCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIH0sIHsNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJ2YWx1ZSIgOiBbICIuIiBdDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB9LCB7DQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAiciIgOiAiMTEzIiwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJzIiA6IFsgew0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInZhbHVlIiA6IFsgInBlcmZvcm1lZCIgXQ0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfSBdDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB9IF0NCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIH0sIHsNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJ2YWx1ZSIgOiBbICIpIiBdDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB9IF0NCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIH0gXQ0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfSwgew0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInIiIDogIjExNyIsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAidmFsdWUiIDogWyAiICIsInN0YXJ0cyBvbiBvciBiZWZvcmUiLCIgIiBdDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICB9LCB7DQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAiciIgOiAiMTE2IiwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJzIiA6IFsgew0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInZhbHVlIiA6IFsgImVuZCBvZiAiIF0NCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIH0sIHsNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJyIiA6ICIxMTUiLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInMiIDogWyB7DQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAidmFsdWUiIDogWyAiXCJNZWFzdXJlbWVudCBQZXJpb2RcIiIgXQ0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfSBdDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB9IF0NCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIH0gXQ0KICAgICAgICAgICAgICAgICAgICAgICAgICAgfSBdDQogICAgICAgICAgICAgICAgICAgICAgICB9IF0NCiAgICAgICAgICAgICAgICAgICAgIH0gXQ0KICAgICAgICAgICAgICAgICAgfSBdDQogICAgICAgICAgICAgICB9DQogICAgICAgICAgICB9IF0sDQogICAgICAgICAgICAiZXhwcmVzc2lvbiIgOiB7DQogICAgICAgICAgICAgICAibG9jYWxJZCIgOiAiMTE5IiwNCiAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICI4MTozLTgzOjEyMyIsDQogICAgICAgICAgICAgICAidHlwZSIgOiAiUXVlcnkiLA0KICAgICAgICAgICAgICAgInNvdXJjZSIgOiBbIHsNCiAgICAgICAgICAgICAgICAgICJsb2NhbElkIiA6ICIxMDYiLA0KICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjgxOjMtODE6NDIiLA0KICAgICAgICAgICAgICAgICAgImFsaWFzIiA6ICJDb2xlY3RvbXkiLA0KICAgICAgICAgICAgICAgICAgImV4cHJlc3Npb24iIDogew0KICAgICAgICAgICAgICAgICAgICAgImxvY2FsSWQiIDogIjEwNSIsDQogICAgICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiODE6My04MTozMiIsDQogICAgICAgICAgICAgICAgICAgICAiZGF0YVR5cGUiIDogIntodHRwOi8vaGw3Lm9yZy9maGlyfVByb2NlZHVyZSIsDQogICAgICAgICAgICAgICAgICAgICAidGVtcGxhdGVJZCIgOiAiaHR0cDovL2hsNy5vcmcvZmhpci9TdHJ1Y3R1cmVEZWZpbml0aW9uL1Byb2NlZHVyZSIsDQogICAgICAgICAgICAgICAgICAgICAiY29kZVByb3BlcnR5IiA6ICJjb2RlIiwNCiAgICAgICAgICAgICAgICAgICAgICJjb2RlQ29tcGFyYXRvciIgOiAiaW4iLA0KICAgICAgICAgICAgICAgICAgICAgInR5cGUiIDogIlJldHJpZXZlIiwNCiAgICAgICAgICAgICAgICAgICAgICJjb2RlcyIgOiB7DQogICAgICAgICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiODE6MTUtODE6MzEiLA0KICAgICAgICAgICAgICAgICAgICAgICAgIm5hbWUiIDogIlRvdGFsIENvbGVjdG9teSIsDQogICAgICAgICAgICAgICAgICAgICAgICAidHlwZSIgOiAiVmFsdWVTZXRSZWYiDQogICAgICAgICAgICAgICAgICAgICB9DQogICAgICAgICAgICAgICAgICB9DQogICAgICAgICAgICAgICB9IF0sDQogICAgICAgICAgICAgICAicmVsYXRpb25zaGlwIiA6IFsgXSwNCiAgICAgICAgICAgICAgICJ3aGVyZSIgOiB7DQogICAgICAgICAgICAgICAgICAibG9jYWxJZCIgOiAiMTE4IiwNCiAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICI4MjoyMy04MzoxMjMiLA0KICAgICAgICAgICAgICAgICAgInR5cGUiIDogIkFuZCIsDQogICAgICAgICAgICAgICAgICAib3BlcmFuZCIgOiBbIHsNCiAgICAgICAgICAgICAgICAgICAgICJsb2NhbElkIiA6ICIxMTAiLA0KICAgICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjgyOjI5LTgyOjU4IiwNCiAgICAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJFcXVhbCIsDQogICAgICAgICAgICAgICAgICAgICAib3BlcmFuZCIgOiBbIHsNCiAgICAgICAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJUb1N0cmluZyIsDQogICAgICAgICAgICAgICAgICAgICAgICAibGlicmFyeU5hbWUiIDogIkZISVJIZWxwZXJzIiwNCiAgICAgICAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJGdW5jdGlvblJlZiIsDQogICAgICAgICAgICAgICAgICAgICAgICAib3BlcmFuZCIgOiBbIHsNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICJsb2NhbElkIiA6ICIxMDgiLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjgyOjI5LTgyOjQ0IiwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICJwYXRoIiA6ICJzdGF0dXMiLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgInNjb3BlIiA6ICJDb2xlY3RvbXkiLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgInR5cGUiIDogIlByb3BlcnR5Ig0KICAgICAgICAgICAgICAgICAgICAgICAgfSBdDQogICAgICAgICAgICAgICAgICAgICB9LCB7DQogICAgICAgICAgICAgICAgICAgICAgICAibG9jYWxJZCIgOiAiMTA5IiwNCiAgICAgICAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICI4Mjo0OC04Mjo1OCIsDQogICAgICAgICAgICAgICAgICAgICAgICAidmFsdWVUeXBlIiA6ICJ7dXJuOmhsNy1vcmc6ZWxtLXR5cGVzOnIxfVN0cmluZyIsDQogICAgICAgICAgICAgICAgICAgICAgICAidmFsdWUiIDogImNvbXBsZXRlZCIsDQogICAgICAgICAgICAgICAgICAgICAgICAidHlwZSIgOiAiTGl0ZXJhbCINCiAgICAgICAgICAgICAgICAgICAgIH0gXQ0KICAgICAgICAgICAgICAgICAgfSwgew0KICAgICAgICAgICAgICAgICAgICAgImxvY2FsSWQiIDogIjExNyIsDQogICAgICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiODM6MjgtODM6MTIzIiwNCiAgICAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJTYW1lT3JCZWZvcmUiLA0KICAgICAgICAgICAgICAgICAgICAgIm9wZXJhbmQiIDogWyB7DQogICAgICAgICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiODM6NzctODM6ODIiLA0KICAgICAgICAgICAgICAgICAgICAgICAgInR5cGUiIDogIlN0YXJ0IiwNCiAgICAgICAgICAgICAgICAgICAgICAgICJvcGVyYW5kIiA6IHsNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICJsb2NhbElkIiA6ICIxMTQiLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjgzOjI4LTgzOjc1IiwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJOb3JtYWxpemUgSW50ZXJ2YWwiLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgImxpYnJhcnlOYW1lIiA6ICJHbG9iYWwiLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgInR5cGUiIDogIkZ1bmN0aW9uUmVmIiwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICJvcGVyYW5kIiA6IFsgew0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgImxvY2FsSWQiIDogIjExMyIsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiODM6NTYtODM6NzQiLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInBhdGgiIDogInBlcmZvcm1lZCIsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAic2NvcGUiIDogIkNvbGVjdG9teSIsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAidHlwZSIgOiAiUHJvcGVydHkiDQogICAgICAgICAgICAgICAgICAgICAgICAgICB9IF0NCiAgICAgICAgICAgICAgICAgICAgICAgIH0NCiAgICAgICAgICAgICAgICAgICAgIH0sIHsNCiAgICAgICAgICAgICAgICAgICAgICAgICJsb2NhbElkIiA6ICIxMTYiLA0KICAgICAgICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjgzOjk3LTgzOjEyMyIsDQogICAgICAgICAgICAgICAgICAgICAgICAidHlwZSIgOiAiRW5kIiwNCiAgICAgICAgICAgICAgICAgICAgICAgICJvcGVyYW5kIiA6IHsNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICJsb2NhbElkIiA6ICIxMTUiLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjgzOjEwNC04MzoxMjMiLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgIm5hbWUiIDogIk1lYXN1cmVtZW50IFBlcmlvZCIsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAidHlwZSIgOiAiUGFyYW1ldGVyUmVmIg0KICAgICAgICAgICAgICAgICAgICAgICAgfQ0KICAgICAgICAgICAgICAgICAgICAgfSBdDQogICAgICAgICAgICAgICAgICB9IF0NCiAgICAgICAgICAgICAgIH0NCiAgICAgICAgICAgIH0NCiAgICAgICAgIH0sIHsNCiAgICAgICAgICAgICJsb2NhbElkIiA6ICIxNDMiLA0KICAgICAgICAgICAgImxvY2F0b3IiIDogIjg1OjEtODk6MTE4IiwNCiAgICAgICAgICAgICJuYW1lIiA6ICJGZWNhbCBPY2N1bHQgQmxvb2QgVGVzdCBQZXJmb3JtZWQiLA0KICAgICAgICAgICAgImNvbnRleHQiIDogIlBhdGllbnQiLA0KICAgICAgICAgICAgImFjY2Vzc0xldmVsIiA6ICJQdWJsaWMiLA0KICAgICAgICAgICAgImFubm90YXRpb24iIDogWyB7DQogICAgICAgICAgICAgICAidHlwZSIgOiAiQW5ub3RhdGlvbiIsDQogICAgICAgICAgICAgICAicyIgOiB7DQogICAgICAgICAgICAgICAgICAiciIgOiAiMTQzIiwNCiAgICAgICAgICAgICAgICAgICJzIiA6IFsgew0KICAgICAgICAgICAgICAgICAgICAgInZhbHVlIiA6IFsgIiIsImRlZmluZSAiLCJcIkZlY2FsIE9jY3VsdCBCbG9vZCBUZXN0IFBlcmZvcm1lZFwiIiwiOlxuICAiIF0NCiAgICAgICAgICAgICAgICAgIH0sIHsNCiAgICAgICAgICAgICAgICAgICAgICJyIiA6ICIxNDIiLA0KICAgICAgICAgICAgICAgICAgICAgInMiIDogWyB7DQogICAgICAgICAgICAgICAgICAgICAgICAicyIgOiBbIHsNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICJyIiA6ICIxMjIiLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgInMiIDogWyB7DQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAiciIgOiAiMTIxIiwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJzIiA6IFsgew0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInIiIDogIjEyMSIsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAicyIgOiBbIHsNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJ2YWx1ZSIgOiBbICJbIiwiT2JzZXJ2YXRpb24iLCI6ICIgXQ0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfSwgew0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInMiIDogWyB7DQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAidmFsdWUiIDogWyAiXCJGZWNhbCBPY2N1bHQgQmxvb2QgVGVzdCAoRk9CVClcIiIgXQ0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfSBdDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB9LCB7DQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAidmFsdWUiIDogWyAiXSIgXQ0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfSBdDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICB9IF0NCiAgICAgICAgICAgICAgICAgICAgICAgICAgIH0sIHsNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJ2YWx1ZSIgOiBbICIgIiwiRmVjYWxPY2N1bHRSZXN1bHQiIF0NCiAgICAgICAgICAgICAgICAgICAgICAgICAgIH0gXQ0KICAgICAgICAgICAgICAgICAgICAgICAgfSBdDQogICAgICAgICAgICAgICAgICAgICB9LCB7DQogICAgICAgICAgICAgICAgICAgICAgICAidmFsdWUiIDogWyAiXG4gICAgICAgICAgICAgICAgICAgIFx0XHQiIF0NCiAgICAgICAgICAgICAgICAgICAgIH0sIHsNCiAgICAgICAgICAgICAgICAgICAgICAgICJyIiA6ICIxNDEiLA0KICAgICAgICAgICAgICAgICAgICAgICAgInMiIDogWyB7DQogICAgICAgICAgICAgICAgICAgICAgICAgICAidmFsdWUiIDogWyAid2hlcmUgIiBdDQogICAgICAgICAgICAgICAgICAgICAgICB9LCB7DQogICAgICAgICAgICAgICAgICAgICAgICAgICAiciIgOiAiMTQxIiwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICJzIiA6IFsgew0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInIiIDogIjEzNCIsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAicyIgOiBbIHsNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJyIiA6ICIxMzAiLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInMiIDogWyB7DQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAiciIgOiAiMTI0IiwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJzIiA6IFsgew0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInIiIDogIjEyMyIsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAicyIgOiBbIHsNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJ2YWx1ZSIgOiBbICJGZWNhbE9jY3VsdFJlc3VsdCIgXQ0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfSBdDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB9LCB7DQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAidmFsdWUiIDogWyAiLiIgXQ0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfSwgew0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInIiIDogIjEyNCIsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAicyIgOiBbIHsNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJ2YWx1ZSIgOiBbICJzdGF0dXMiIF0NCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIH0gXQ0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfSBdDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB9LCB7DQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAidmFsdWUiIDogWyAiIGluICIgXQ0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfSwgew0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInIiIDogIjEyOSIsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAicyIgOiBbIHsNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJ2YWx1ZSIgOiBbICJ7IiBdDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB9LCB7DQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAiciIgOiAiMTI1IiwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJzIiA6IFsgew0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInZhbHVlIiA6IFsgIidmaW5hbCciIF0NCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIH0gXQ0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfSwgew0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInZhbHVlIiA6IFsgIiwgIiBdDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB9LCB7DQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAiciIgOiAiMTI2IiwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJzIiA6IFsgew0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInZhbHVlIiA6IFsgIidhbWVuZGVkJyIgXQ0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfSBdDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB9LCB7DQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAidmFsdWUiIDogWyAiLCAiIF0NCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIH0sIHsNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJyIiA6ICIxMjciLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInMiIDogWyB7DQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAidmFsdWUiIDogWyAiJ2NvcnJlY3RlZCciIF0NCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIH0gXQ0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfSwgew0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInZhbHVlIiA6IFsgIiwgIiBdDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB9LCB7DQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAiciIgOiAiMTI4IiwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJzIiA6IFsgew0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInZhbHVlIiA6IFsgIidwcmVsaW1pbmFyeSciIF0NCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIH0gXQ0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfSwgew0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInZhbHVlIiA6IFsgIn0iIF0NCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIH0gXQ0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfSBdDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICB9LCB7DQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAidmFsdWUiIDogWyAiXG4gICAgICAgICAgICAgICAgICAgIFx0XHRcdGFuZCAiIF0NCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIH0sIHsNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJyIiA6ICIxMzMiLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInMiIDogWyB7DQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAiciIgOiAiMTMyIiwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJzIiA6IFsgew0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInIiIDogIjEzMSIsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAicyIgOiBbIHsNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJ2YWx1ZSIgOiBbICJGZWNhbE9jY3VsdFJlc3VsdCIgXQ0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfSBdDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB9LCB7DQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAidmFsdWUiIDogWyAiLiIgXQ0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfSwgew0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInIiIDogIjEzMiIsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAicyIgOiBbIHsNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJ2YWx1ZSIgOiBbICJ2YWx1ZSIgXQ0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfSBdDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB9IF0NCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIH0sIHsNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJ2YWx1ZSIgOiBbICIgaXMgbm90IG51bGwiIF0NCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIH0gXQ0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfSBdDQogICAgICAgICAgICAgICAgICAgICAgICAgICB9LCB7DQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAidmFsdWUiIDogWyAiXG4gICAgICAgICAgICAgICAgICAgIFx0XHRcdGFuZCAiIF0NCiAgICAgICAgICAgICAgICAgICAgICAgICAgIH0sIHsNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJyIiA6ICIxNDAiLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInMiIDogWyB7DQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAiciIgOiAiMTM4IiwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJzIiA6IFsgew0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInIiIDogIjEzNSIsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAicyIgOiBbIHsNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJ2YWx1ZSIgOiBbICJHbG9iYWwiIF0NCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIH0gXQ0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfSwgew0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInZhbHVlIiA6IFsgIi4iIF0NCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIH0sIHsNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJyIiA6ICIxMzgiLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInMiIDogWyB7DQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAidmFsdWUiIDogWyAiXCJOb3JtYWxpemUgSW50ZXJ2YWxcIiIsIigiIF0NCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIH0sIHsNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJyIiA6ICIxMzciLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInMiIDogWyB7DQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAiciIgOiAiMTM2IiwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJzIiA6IFsgew0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInZhbHVlIiA6IFsgIkZlY2FsT2NjdWx0UmVzdWx0IiBdDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB9IF0NCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIH0sIHsNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJ2YWx1ZSIgOiBbICIuIiBdDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB9LCB7DQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAiciIgOiAiMTM3IiwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJzIiA6IFsgew0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInZhbHVlIiA6IFsgImVmZmVjdGl2ZSIgXQ0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfSBdDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB9IF0NCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIH0sIHsNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJ2YWx1ZSIgOiBbICIpIiBdDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB9IF0NCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIH0gXQ0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfSwgew0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInIiIDogIjE0MCIsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAidmFsdWUiIDogWyAiICIsImR1cmluZyBkYXkgb2YiLCIgIiBdDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICB9LCB7DQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAiciIgOiAiMTM5IiwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJzIiA6IFsgew0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInZhbHVlIiA6IFsgIlwiTWVhc3VyZW1lbnQgUGVyaW9kXCIiIF0NCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIH0gXQ0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfSBdDQogICAgICAgICAgICAgICAgICAgICAgICAgICB9IF0NCiAgICAgICAgICAgICAgICAgICAgICAgIH0gXQ0KICAgICAgICAgICAgICAgICAgICAgfSBdDQogICAgICAgICAgICAgICAgICB9IF0NCiAgICAgICAgICAgICAgIH0NCiAgICAgICAgICAgIH0gXSwNCiAgICAgICAgICAgICJleHByZXNzaW9uIiA6IHsNCiAgICAgICAgICAgICAgICJsb2NhbElkIiA6ICIxNDIiLA0KICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjg2OjMtODk6MTE4IiwNCiAgICAgICAgICAgICAgICJ0eXBlIiA6ICJRdWVyeSIsDQogICAgICAgICAgICAgICAic291cmNlIiA6IFsgew0KICAgICAgICAgICAgICAgICAgImxvY2FsSWQiIDogIjEyMiIsDQogICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiODY6My04Njo2NyIsDQogICAgICAgICAgICAgICAgICAiYWxpYXMiIDogIkZlY2FsT2NjdWx0UmVzdWx0IiwNCiAgICAgICAgICAgICAgICAgICJleHByZXNzaW9uIiA6IHsNCiAgICAgICAgICAgICAgICAgICAgICJsb2NhbElkIiA6ICIxMjEiLA0KICAgICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjg2OjMtODY6NDkiLA0KICAgICAgICAgICAgICAgICAgICAgImRhdGFUeXBlIiA6ICJ7aHR0cDovL2hsNy5vcmcvZmhpcn1PYnNlcnZhdGlvbiIsDQogICAgICAgICAgICAgICAgICAgICAidGVtcGxhdGVJZCIgOiAiaHR0cDovL2hsNy5vcmcvZmhpci9TdHJ1Y3R1cmVEZWZpbml0aW9uL09ic2VydmF0aW9uIiwNCiAgICAgICAgICAgICAgICAgICAgICJjb2RlUHJvcGVydHkiIDogImNvZGUiLA0KICAgICAgICAgICAgICAgICAgICAgImNvZGVDb21wYXJhdG9yIiA6ICJpbiIsDQogICAgICAgICAgICAgICAgICAgICAidHlwZSIgOiAiUmV0cmlldmUiLA0KICAgICAgICAgICAgICAgICAgICAgImNvZGVzIiA6IHsNCiAgICAgICAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICI4NjoxNy04Njo0OCIsDQogICAgICAgICAgICAgICAgICAgICAgICAibmFtZSIgOiAiRmVjYWwgT2NjdWx0IEJsb29kIFRlc3QgKEZPQlQpIiwNCiAgICAgICAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJWYWx1ZVNldFJlZiINCiAgICAgICAgICAgICAgICAgICAgIH0NCiAgICAgICAgICAgICAgICAgIH0NCiAgICAgICAgICAgICAgIH0gXSwNCiAgICAgICAgICAgICAgICJyZWxhdGlvbnNoaXAiIDogWyBdLA0KICAgICAgICAgICAgICAgIndoZXJlIiA6IHsNCiAgICAgICAgICAgICAgICAgICJsb2NhbElkIiA6ICIxNDEiLA0KICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjg3OjIzLTg5OjExOCIsDQogICAgICAgICAgICAgICAgICAidHlwZSIgOiAiQW5kIiwNCiAgICAgICAgICAgICAgICAgICJvcGVyYW5kIiA6IFsgew0KICAgICAgICAgICAgICAgICAgICAgImxvY2FsSWQiIDogIjEzNCIsDQogICAgICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiODc6MjktODg6NjIiLA0KICAgICAgICAgICAgICAgICAgICAgInR5cGUiIDogIkFuZCIsDQogICAgICAgICAgICAgICAgICAgICAib3BlcmFuZCIgOiBbIHsNCiAgICAgICAgICAgICAgICAgICAgICAgICJsb2NhbElkIiA6ICIxMzAiLA0KICAgICAgICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjg3OjI5LTg3OjEwNCIsDQogICAgICAgICAgICAgICAgICAgICAgICAidHlwZSIgOiAiSW4iLA0KICAgICAgICAgICAgICAgICAgICAgICAgIm9wZXJhbmQiIDogWyB7DQogICAgICAgICAgICAgICAgICAgICAgICAgICAibmFtZSIgOiAiVG9TdHJpbmciLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgImxpYnJhcnlOYW1lIiA6ICJGSElSSGVscGVycyIsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAidHlwZSIgOiAiRnVuY3Rpb25SZWYiLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgIm9wZXJhbmQiIDogWyB7DQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAibG9jYWxJZCIgOiAiMTI0IiwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICI4NzoyOS04Nzo1MiIsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAicGF0aCIgOiAic3RhdHVzIiwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJzY29wZSIgOiAiRmVjYWxPY2N1bHRSZXN1bHQiLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInR5cGUiIDogIlByb3BlcnR5Ig0KICAgICAgICAgICAgICAgICAgICAgICAgICAgfSBdDQogICAgICAgICAgICAgICAgICAgICAgICB9LCB7DQogICAgICAgICAgICAgICAgICAgICAgICAgICAibG9jYWxJZCIgOiAiMTI5IiwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICI4Nzo1Ny04NzoxMDQiLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgInR5cGUiIDogIkxpc3QiLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgImVsZW1lbnQiIDogWyB7DQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAibG9jYWxJZCIgOiAiMTI1IiwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICI4Nzo1OC04Nzo2NCIsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAidmFsdWVUeXBlIiA6ICJ7dXJuOmhsNy1vcmc6ZWxtLXR5cGVzOnIxfVN0cmluZyIsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAidmFsdWUiIDogImZpbmFsIiwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJMaXRlcmFsIg0KICAgICAgICAgICAgICAgICAgICAgICAgICAgfSwgew0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgImxvY2FsSWQiIDogIjEyNiIsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiODc6NjctODc6NzUiLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInZhbHVlVHlwZSIgOiAie3VybjpobDctb3JnOmVsbS10eXBlczpyMX1TdHJpbmciLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInZhbHVlIiA6ICJhbWVuZGVkIiwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJMaXRlcmFsIg0KICAgICAgICAgICAgICAgICAgICAgICAgICAgfSwgew0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgImxvY2FsSWQiIDogIjEyNyIsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiODc6NzgtODc6ODgiLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInZhbHVlVHlwZSIgOiAie3VybjpobDctb3JnOmVsbS10eXBlczpyMX1TdHJpbmciLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInZhbHVlIiA6ICJjb3JyZWN0ZWQiLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInR5cGUiIDogIkxpdGVyYWwiDQogICAgICAgICAgICAgICAgICAgICAgICAgICB9LCB7DQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAibG9jYWxJZCIgOiAiMTI4IiwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICI4Nzo5MS04NzoxMDMiLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInZhbHVlVHlwZSIgOiAie3VybjpobDctb3JnOmVsbS10eXBlczpyMX1TdHJpbmciLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInZhbHVlIiA6ICJwcmVsaW1pbmFyeSIsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAidHlwZSIgOiAiTGl0ZXJhbCINCiAgICAgICAgICAgICAgICAgICAgICAgICAgIH0gXQ0KICAgICAgICAgICAgICAgICAgICAgICAgfSBdDQogICAgICAgICAgICAgICAgICAgICB9LCB7DQogICAgICAgICAgICAgICAgICAgICAgICAibG9jYWxJZCIgOiAiMTMzIiwNCiAgICAgICAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICI4ODoyOC04ODo2MiIsDQogICAgICAgICAgICAgICAgICAgICAgICAidHlwZSIgOiAiTm90IiwNCiAgICAgICAgICAgICAgICAgICAgICAgICJvcGVyYW5kIiA6IHsNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICI4ODoyOC04ODo2MiIsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAidHlwZSIgOiAiSXNOdWxsIiwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICJvcGVyYW5kIiA6IHsNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJsb2NhbElkIiA6ICIxMzIiLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjg4OjI4LTg4OjUwIiwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJwYXRoIiA6ICJ2YWx1ZSIsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAic2NvcGUiIDogIkZlY2FsT2NjdWx0UmVzdWx0IiwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJQcm9wZXJ0eSINCiAgICAgICAgICAgICAgICAgICAgICAgICAgIH0NCiAgICAgICAgICAgICAgICAgICAgICAgIH0NCiAgICAgICAgICAgICAgICAgICAgIH0gXQ0KICAgICAgICAgICAgICAgICAgfSwgew0KICAgICAgICAgICAgICAgICAgICAgImxvY2FsSWQiIDogIjE0MCIsDQogICAgICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiODk6MjgtODk6MTE4IiwNCiAgICAgICAgICAgICAgICAgICAgICJwcmVjaXNpb24iIDogIkRheSIsDQogICAgICAgICAgICAgICAgICAgICAidHlwZSIgOiAiSW5jbHVkZWRJbiIsDQogICAgICAgICAgICAgICAgICAgICAib3BlcmFuZCIgOiBbIHsNCiAgICAgICAgICAgICAgICAgICAgICAgICJsb2NhbElkIiA6ICIxMzgiLA0KICAgICAgICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjg5OjI4LTg5OjgzIiwNCiAgICAgICAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJOb3JtYWxpemUgSW50ZXJ2YWwiLA0KICAgICAgICAgICAgICAgICAgICAgICAgImxpYnJhcnlOYW1lIiA6ICJHbG9iYWwiLA0KICAgICAgICAgICAgICAgICAgICAgICAgInR5cGUiIDogIkZ1bmN0aW9uUmVmIiwNCiAgICAgICAgICAgICAgICAgICAgICAgICJvcGVyYW5kIiA6IFsgew0KICAgICAgICAgICAgICAgICAgICAgICAgICAgImxvY2FsSWQiIDogIjEzNyIsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiODk6NTYtODk6ODIiLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgInBhdGgiIDogImVmZmVjdGl2ZSIsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAic2NvcGUiIDogIkZlY2FsT2NjdWx0UmVzdWx0IiwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJQcm9wZXJ0eSINCiAgICAgICAgICAgICAgICAgICAgICAgIH0gXQ0KICAgICAgICAgICAgICAgICAgICAgfSwgew0KICAgICAgICAgICAgICAgICAgICAgICAgImxvY2FsSWQiIDogIjEzOSIsDQogICAgICAgICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiODk6OTktODk6MTE4IiwNCiAgICAgICAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJNZWFzdXJlbWVudCBQZXJpb2QiLA0KICAgICAgICAgICAgICAgICAgICAgICAgInR5cGUiIDogIlBhcmFtZXRlclJlZiINCiAgICAgICAgICAgICAgICAgICAgIH0gXQ0KICAgICAgICAgICAgICAgICAgfSBdDQogICAgICAgICAgICAgICB9DQogICAgICAgICAgICB9DQogICAgICAgICB9LCB7DQogICAgICAgICAgICAibG9jYWxJZCIgOiAiMTY4IiwNCiAgICAgICAgICAgICJsb2NhdG9yIiA6ICI5MToxLTk1OjE0MyIsDQogICAgICAgICAgICAibmFtZSIgOiAiRmVjYWwgSW1tdW5vY2hlbWljYWwgVGVzdCBETkEiLA0KICAgICAgICAgICAgImNvbnRleHQiIDogIlBhdGllbnQiLA0KICAgICAgICAgICAgImFjY2Vzc0xldmVsIiA6ICJQdWJsaWMiLA0KICAgICAgICAgICAgImFubm90YXRpb24iIDogWyB7DQogICAgICAgICAgICAgICAidHlwZSIgOiAiQW5ub3RhdGlvbiIsDQogICAgICAgICAgICAgICAicyIgOiB7DQogICAgICAgICAgICAgICAgICAiciIgOiAiMTY4IiwNCiAgICAgICAgICAgICAgICAgICJzIiA6IFsgew0KICAgICAgICAgICAgICAgICAgICAgInZhbHVlIiA6IFsgIiIsImRlZmluZSAiLCJcIkZlY2FsIEltbXVub2NoZW1pY2FsIFRlc3QgRE5BXCIiLCI6XG4gICIgXQ0KICAgICAgICAgICAgICAgICAgfSwgew0KICAgICAgICAgICAgICAgICAgICAgInIiIDogIjE2NyIsDQogICAgICAgICAgICAgICAgICAgICAicyIgOiBbIHsNCiAgICAgICAgICAgICAgICAgICAgICAgICJzIiA6IFsgew0KICAgICAgICAgICAgICAgICAgICAgICAgICAgInIiIDogIjE0NSIsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAicyIgOiBbIHsNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJyIiA6ICIxNDQiLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInMiIDogWyB7DQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAiciIgOiAiMTQ0IiwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJzIiA6IFsgew0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInZhbHVlIiA6IFsgIlsiLCJPYnNlcnZhdGlvbiIsIjogIiBdDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB9LCB7DQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAicyIgOiBbIHsNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJ2YWx1ZSIgOiBbICJcIkZJVCBETkFcIiIgXQ0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfSBdDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB9LCB7DQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAidmFsdWUiIDogWyAiXSIgXQ0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfSBdDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICB9IF0NCiAgICAgICAgICAgICAgICAgICAgICAgICAgIH0sIHsNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJ2YWx1ZSIgOiBbICIgIiwiRml0RE5BIiBdDQogICAgICAgICAgICAgICAgICAgICAgICAgICB9IF0NCiAgICAgICAgICAgICAgICAgICAgICAgIH0gXQ0KICAgICAgICAgICAgICAgICAgICAgfSwgew0KICAgICAgICAgICAgICAgICAgICAgICAgInZhbHVlIiA6IFsgIlxuICAgICAgICAgICAgICAgICAgICBcdFx0IiBdDQogICAgICAgICAgICAgICAgICAgICB9LCB7DQogICAgICAgICAgICAgICAgICAgICAgICAiciIgOiAiMTY2IiwNCiAgICAgICAgICAgICAgICAgICAgICAgICJzIiA6IFsgew0KICAgICAgICAgICAgICAgICAgICAgICAgICAgInZhbHVlIiA6IFsgIndoZXJlICIgXQ0KICAgICAgICAgICAgICAgICAgICAgICAgfSwgew0KICAgICAgICAgICAgICAgICAgICAgICAgICAgInIiIDogIjE2NiIsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAicyIgOiBbIHsNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJyIiA6ICIxNTciLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInMiIDogWyB7DQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAiciIgOiAiMTUzIiwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJzIiA6IFsgew0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInIiIDogIjE0NyIsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAicyIgOiBbIHsNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJyIiA6ICIxNDYiLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInMiIDogWyB7DQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAidmFsdWUiIDogWyAiRml0RE5BIiBdDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB9IF0NCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIH0sIHsNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJ2YWx1ZSIgOiBbICIuIiBdDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB9LCB7DQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAiciIgOiAiMTQ3IiwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJzIiA6IFsgew0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInZhbHVlIiA6IFsgInN0YXR1cyIgXQ0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfSBdDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB9IF0NCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIH0sIHsNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJ2YWx1ZSIgOiBbICIgaW4gIiBdDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB9LCB7DQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAiciIgOiAiMTUyIiwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJzIiA6IFsgew0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInZhbHVlIiA6IFsgInsiIF0NCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIH0sIHsNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJyIiA6ICIxNDgiLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInMiIDogWyB7DQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAidmFsdWUiIDogWyAiJ2ZpbmFsJyIgXQ0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfSBdDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB9LCB7DQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAidmFsdWUiIDogWyAiLCAiIF0NCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIH0sIHsNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJyIiA6ICIxNDkiLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInMiIDogWyB7DQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAidmFsdWUiIDogWyAiJ2FtZW5kZWQnIiBdDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB9IF0NCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIH0sIHsNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJ2YWx1ZSIgOiBbICIsICIgXQ0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfSwgew0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInIiIDogIjE1MCIsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAicyIgOiBbIHsNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJ2YWx1ZSIgOiBbICInY29ycmVjdGVkJyIgXQ0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfSBdDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB9LCB7DQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAidmFsdWUiIDogWyAiLCAiIF0NCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIH0sIHsNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJyIiA6ICIxNTEiLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInMiIDogWyB7DQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAidmFsdWUiIDogWyAiJ3ByZWxpbWluYXJ5JyIgXQ0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfSBdDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB9LCB7DQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAidmFsdWUiIDogWyAifSIgXQ0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfSBdDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB9IF0NCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIH0sIHsNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJ2YWx1ZSIgOiBbICJcbiAgICAgICAgICAgICAgICAgICAgXHRcdFx0YW5kICIgXQ0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfSwgew0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInIiIDogIjE1NiIsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAicyIgOiBbIHsNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJyIiA6ICIxNTUiLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInMiIDogWyB7DQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAiciIgOiAiMTU0IiwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJzIiA6IFsgew0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInZhbHVlIiA6IFsgIkZpdEROQSIgXQ0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfSBdDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB9LCB7DQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAidmFsdWUiIDogWyAiLiIgXQ0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfSwgew0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInIiIDogIjE1NSIsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAicyIgOiBbIHsNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJ2YWx1ZSIgOiBbICJ2YWx1ZSIgXQ0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfSBdDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB9IF0NCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIH0sIHsNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJ2YWx1ZSIgOiBbICIgaXMgbm90IG51bGwiIF0NCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIH0gXQ0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfSBdDQogICAgICAgICAgICAgICAgICAgICAgICAgICB9LCB7DQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAidmFsdWUiIDogWyAiXG4gICAgICAgICAgICAgICAgICAgIFx0XHRcdGFuZCAiIF0NCiAgICAgICAgICAgICAgICAgICAgICAgICAgIH0sIHsNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJyIiA6ICIxNjUiLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInMiIDogWyB7DQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAiciIgOiAiMTYxIiwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJzIiA6IFsgew0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInIiIDogIjE1OCIsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAicyIgOiBbIHsNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJ2YWx1ZSIgOiBbICJHbG9iYWwiIF0NCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIH0gXQ0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfSwgew0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInZhbHVlIiA6IFsgIi4iIF0NCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIH0sIHsNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJyIiA6ICIxNjEiLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInMiIDogWyB7DQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAidmFsdWUiIDogWyAiXCJOb3JtYWxpemUgSW50ZXJ2YWxcIiIsIigiIF0NCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIH0sIHsNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJyIiA6ICIxNjAiLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInMiIDogWyB7DQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAiciIgOiAiMTU5IiwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJzIiA6IFsgew0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInZhbHVlIiA6IFsgIkZpdEROQSIgXQ0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfSBdDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB9LCB7DQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAidmFsdWUiIDogWyAiLiIgXQ0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfSwgew0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInIiIDogIjE2MCIsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAicyIgOiBbIHsNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJ2YWx1ZSIgOiBbICJlZmZlY3RpdmUiIF0NCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIH0gXQ0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfSBdDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB9LCB7DQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAidmFsdWUiIDogWyAiKSIgXQ0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfSBdDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB9IF0NCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIH0sIHsNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJ2YWx1ZSIgOiBbICIgIiBdDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICB9LCB7DQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAiciIgOiAiMTY1IiwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJzIiA6IFsgew0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInZhbHVlIiA6IFsgIm9jY3VycyAiIF0NCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIH0sIHsNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJyIiA6ICIxNjQiLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInMiIDogWyB7DQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAidmFsdWUiIDogWyAiMyAiLCJ5ZWFycyIgXQ0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfSBdDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB9LCB7DQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAidmFsdWUiIDogWyAiIG9yIGxlc3Mgb24gb3IgYmVmb3JlIGRheSBvZiIgXQ0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfSBdDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICB9LCB7DQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAidmFsdWUiIDogWyAiICIgXQ0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfSwgew0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInIiIDogIjE2MyIsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAicyIgOiBbIHsNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJ2YWx1ZSIgOiBbICJlbmQgb2YgIiBdDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB9LCB7DQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAiciIgOiAiMTYyIiwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJzIiA6IFsgew0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInZhbHVlIiA6IFsgIlwiTWVhc3VyZW1lbnQgUGVyaW9kXCIiIF0NCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIH0gXQ0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfSBdDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICB9IF0NCiAgICAgICAgICAgICAgICAgICAgICAgICAgIH0gXQ0KICAgICAgICAgICAgICAgICAgICAgICAgfSBdDQogICAgICAgICAgICAgICAgICAgICB9IF0NCiAgICAgICAgICAgICAgICAgIH0gXQ0KICAgICAgICAgICAgICAgfQ0KICAgICAgICAgICAgfSBdLA0KICAgICAgICAgICAgImV4cHJlc3Npb24iIDogew0KICAgICAgICAgICAgICAgImxvY2FsSWQiIDogIjE2NyIsDQogICAgICAgICAgICAgICAibG9jYXRvciIgOiAiOTI6My05NToxNDMiLA0KICAgICAgICAgICAgICAgInR5cGUiIDogIlF1ZXJ5IiwNCiAgICAgICAgICAgICAgICJzb3VyY2UiIDogWyB7DQogICAgICAgICAgICAgICAgICAibG9jYWxJZCIgOiAiMTQ1IiwNCiAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICI5MjozLTkyOjMzIiwNCiAgICAgICAgICAgICAgICAgICJhbGlhcyIgOiAiRml0RE5BIiwNCiAgICAgICAgICAgICAgICAgICJleHByZXNzaW9uIiA6IHsNCiAgICAgICAgICAgICAgICAgICAgICJsb2NhbElkIiA6ICIxNDQiLA0KICAgICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjkyOjMtOTI6MjYiLA0KICAgICAgICAgICAgICAgICAgICAgImRhdGFUeXBlIiA6ICJ7aHR0cDovL2hsNy5vcmcvZmhpcn1PYnNlcnZhdGlvbiIsDQogICAgICAgICAgICAgICAgICAgICAidGVtcGxhdGVJZCIgOiAiaHR0cDovL2hsNy5vcmcvZmhpci9TdHJ1Y3R1cmVEZWZpbml0aW9uL09ic2VydmF0aW9uIiwNCiAgICAgICAgICAgICAgICAgICAgICJjb2RlUHJvcGVydHkiIDogImNvZGUiLA0KICAgICAgICAgICAgICAgICAgICAgImNvZGVDb21wYXJhdG9yIiA6ICJpbiIsDQogICAgICAgICAgICAgICAgICAgICAidHlwZSIgOiAiUmV0cmlldmUiLA0KICAgICAgICAgICAgICAgICAgICAgImNvZGVzIiA6IHsNCiAgICAgICAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICI5MjoxNy05MjoyNSIsDQogICAgICAgICAgICAgICAgICAgICAgICAibmFtZSIgOiAiRklUIEROQSIsDQogICAgICAgICAgICAgICAgICAgICAgICAidHlwZSIgOiAiVmFsdWVTZXRSZWYiDQogICAgICAgICAgICAgICAgICAgICB9DQogICAgICAgICAgICAgICAgICB9DQogICAgICAgICAgICAgICB9IF0sDQogICAgICAgICAgICAgICAicmVsYXRpb25zaGlwIiA6IFsgXSwNCiAgICAgICAgICAgICAgICJ3aGVyZSIgOiB7DQogICAgICAgICAgICAgICAgICAibG9jYWxJZCIgOiAiMTY2IiwNCiAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICI5MzoyMy05NToxNDMiLA0KICAgICAgICAgICAgICAgICAgInR5cGUiIDogIkFuZCIsDQogICAgICAgICAgICAgICAgICAib3BlcmFuZCIgOiBbIHsNCiAgICAgICAgICAgICAgICAgICAgICJsb2NhbElkIiA6ICIxNTciLA0KICAgICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjkzOjI5LTk0OjUxIiwNCiAgICAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJBbmQiLA0KICAgICAgICAgICAgICAgICAgICAgIm9wZXJhbmQiIDogWyB7DQogICAgICAgICAgICAgICAgICAgICAgICAibG9jYWxJZCIgOiAiMTUzIiwNCiAgICAgICAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICI5MzoyOS05Mzo5MyIsDQogICAgICAgICAgICAgICAgICAgICAgICAidHlwZSIgOiAiSW4iLA0KICAgICAgICAgICAgICAgICAgICAgICAgIm9wZXJhbmQiIDogWyB7DQogICAgICAgICAgICAgICAgICAgICAgICAgICAibmFtZSIgOiAiVG9TdHJpbmciLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgImxpYnJhcnlOYW1lIiA6ICJGSElSSGVscGVycyIsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAidHlwZSIgOiAiRnVuY3Rpb25SZWYiLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgIm9wZXJhbmQiIDogWyB7DQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAibG9jYWxJZCIgOiAiMTQ3IiwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICI5MzoyOS05Mzo0MSIsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAicGF0aCIgOiAic3RhdHVzIiwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJzY29wZSIgOiAiRml0RE5BIiwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJQcm9wZXJ0eSINCiAgICAgICAgICAgICAgICAgICAgICAgICAgIH0gXQ0KICAgICAgICAgICAgICAgICAgICAgICAgfSwgew0KICAgICAgICAgICAgICAgICAgICAgICAgICAgImxvY2FsSWQiIDogIjE1MiIsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiOTM6NDYtOTM6OTMiLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgInR5cGUiIDogIkxpc3QiLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgImVsZW1lbnQiIDogWyB7DQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAibG9jYWxJZCIgOiAiMTQ4IiwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICI5Mzo0Ny05Mzo1MyIsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAidmFsdWVUeXBlIiA6ICJ7dXJuOmhsNy1vcmc6ZWxtLXR5cGVzOnIxfVN0cmluZyIsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAidmFsdWUiIDogImZpbmFsIiwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJMaXRlcmFsIg0KICAgICAgICAgICAgICAgICAgICAgICAgICAgfSwgew0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgImxvY2FsSWQiIDogIjE0OSIsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiOTM6NTYtOTM6NjQiLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInZhbHVlVHlwZSIgOiAie3VybjpobDctb3JnOmVsbS10eXBlczpyMX1TdHJpbmciLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInZhbHVlIiA6ICJhbWVuZGVkIiwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJMaXRlcmFsIg0KICAgICAgICAgICAgICAgICAgICAgICAgICAgfSwgew0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgImxvY2FsSWQiIDogIjE1MCIsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiOTM6NjctOTM6NzciLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInZhbHVlVHlwZSIgOiAie3VybjpobDctb3JnOmVsbS10eXBlczpyMX1TdHJpbmciLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInZhbHVlIiA6ICJjb3JyZWN0ZWQiLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInR5cGUiIDogIkxpdGVyYWwiDQogICAgICAgICAgICAgICAgICAgICAgICAgICB9LCB7DQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAibG9jYWxJZCIgOiAiMTUxIiwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICI5Mzo4MC05Mzo5MiIsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAidmFsdWVUeXBlIiA6ICJ7dXJuOmhsNy1vcmc6ZWxtLXR5cGVzOnIxfVN0cmluZyIsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAidmFsdWUiIDogInByZWxpbWluYXJ5IiwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJMaXRlcmFsIg0KICAgICAgICAgICAgICAgICAgICAgICAgICAgfSBdDQogICAgICAgICAgICAgICAgICAgICAgICB9IF0NCiAgICAgICAgICAgICAgICAgICAgIH0sIHsNCiAgICAgICAgICAgICAgICAgICAgICAgICJsb2NhbElkIiA6ICIxNTYiLA0KICAgICAgICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjk0OjI4LTk0OjUxIiwNCiAgICAgICAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJOb3QiLA0KICAgICAgICAgICAgICAgICAgICAgICAgIm9wZXJhbmQiIDogew0KICAgICAgICAgICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjk0OjI4LTk0OjUxIiwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJJc051bGwiLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgIm9wZXJhbmQiIDogew0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgImxvY2FsSWQiIDogIjE1NSIsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiOTQ6MjgtOTQ6MzkiLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInBhdGgiIDogInZhbHVlIiwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJzY29wZSIgOiAiRml0RE5BIiwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJQcm9wZXJ0eSINCiAgICAgICAgICAgICAgICAgICAgICAgICAgIH0NCiAgICAgICAgICAgICAgICAgICAgICAgIH0NCiAgICAgICAgICAgICAgICAgICAgIH0gXQ0KICAgICAgICAgICAgICAgICAgfSwgew0KICAgICAgICAgICAgICAgICAgICAgImxvY2FsSWQiIDogIjE2NSIsDQogICAgICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiOTU6MjgtOTU6MTQzIiwNCiAgICAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJBbmQiLA0KICAgICAgICAgICAgICAgICAgICAgIm9wZXJhbmQiIDogWyB7DQogICAgICAgICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiOTU6ODEtOTU6OTUiLA0KICAgICAgICAgICAgICAgICAgICAgICAgInByZWNpc2lvbiIgOiAiRGF5IiwNCiAgICAgICAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJJbiIsDQogICAgICAgICAgICAgICAgICAgICAgICAib3BlcmFuZCIgOiBbIHsNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICI5NTozNS05NTo3MiIsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAidHlwZSIgOiAiRW5kIiwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICJvcGVyYW5kIiA6IHsNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJsb2NhbElkIiA6ICIxNjEiLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjk1OjI4LTk1OjcyIiwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJOb3JtYWxpemUgSW50ZXJ2YWwiLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgImxpYnJhcnlOYW1lIiA6ICJHbG9iYWwiLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInR5cGUiIDogIkZ1bmN0aW9uUmVmIiwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJvcGVyYW5kIiA6IFsgew0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgImxvY2FsSWQiIDogIjE2MCIsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiOTU6NTYtOTU6NzEiLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInBhdGgiIDogImVmZmVjdGl2ZSIsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAic2NvcGUiIDogIkZpdEROQSIsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAidHlwZSIgOiAiUHJvcGVydHkiDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICB9IF0NCiAgICAgICAgICAgICAgICAgICAgICAgICAgIH0NCiAgICAgICAgICAgICAgICAgICAgICAgIH0sIHsNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICI5NTo4MS05NTo5NSIsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAibG93Q2xvc2VkIiA6IHRydWUsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAiaGlnaENsb3NlZCIgOiB0cnVlLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgInR5cGUiIDogIkludGVydmFsIiwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICJsb3ciIDogew0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjk1OjExNy05NToxNDMiLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInR5cGUiIDogIlN1YnRyYWN0IiwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJvcGVyYW5kIiA6IFsgew0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgImxvY2FsSWQiIDogIjE2MyIsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiOTU6MTE3LTk1OjE0MyIsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAidHlwZSIgOiAiRW5kIiwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJvcGVyYW5kIiA6IHsNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJsb2NhbElkIiA6ICIxNjIiLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjk1OjEyNC05NToxNDMiLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIm5hbWUiIDogIk1lYXN1cmVtZW50IFBlcmlvZCIsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAidHlwZSIgOiAiUGFyYW1ldGVyUmVmIg0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfQ0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfSwgew0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgImxvY2FsSWQiIDogIjE2NCIsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiOTU6ODEtOTU6ODciLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInZhbHVlIiA6IDMsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAidW5pdCIgOiAieWVhcnMiLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInR5cGUiIDogIlF1YW50aXR5Ig0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfSBdDQogICAgICAgICAgICAgICAgICAgICAgICAgICB9LA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgImhpZ2giIDogew0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgImxvY2FsSWQiIDogIjE2MyIsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiOTU6MTE3LTk1OjE0MyIsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAidHlwZSIgOiAiRW5kIiwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJvcGVyYW5kIiA6IHsNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJsb2NhbElkIiA6ICIxNjIiLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjk1OjEyNC05NToxNDMiLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIm5hbWUiIDogIk1lYXN1cmVtZW50IFBlcmlvZCIsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAidHlwZSIgOiAiUGFyYW1ldGVyUmVmIg0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfQ0KICAgICAgICAgICAgICAgICAgICAgICAgICAgfQ0KICAgICAgICAgICAgICAgICAgICAgICAgfSBdDQogICAgICAgICAgICAgICAgICAgICB9LCB7DQogICAgICAgICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiOTU6ODEtOTU6OTUiLA0KICAgICAgICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk5vdCIsDQogICAgICAgICAgICAgICAgICAgICAgICAib3BlcmFuZCIgOiB7DQogICAgICAgICAgICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiOTU6ODEtOTU6OTUiLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgInR5cGUiIDogIklzTnVsbCIsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAib3BlcmFuZCIgOiB7DQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAibG9jYWxJZCIgOiAiMTYzIiwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICI5NToxMTctOTU6MTQzIiwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJFbmQiLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIm9wZXJhbmQiIDogew0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgImxvY2FsSWQiIDogIjE2MiIsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiOTU6MTI0LTk1OjE0MyIsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAibmFtZSIgOiAiTWVhc3VyZW1lbnQgUGVyaW9kIiwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJQYXJhbWV0ZXJSZWYiDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICB9DQogICAgICAgICAgICAgICAgICAgICAgICAgICB9DQogICAgICAgICAgICAgICAgICAgICAgICB9DQogICAgICAgICAgICAgICAgICAgICB9IF0NCiAgICAgICAgICAgICAgICAgIH0gXQ0KICAgICAgICAgICAgICAgfQ0KICAgICAgICAgICAgfQ0KICAgICAgICAgfSwgew0KICAgICAgICAgICAgImxvY2FsSWQiIDogIjE4NSIsDQogICAgICAgICAgICAibG9jYXRvciIgOiAiOTc6MS0xMDA6MTQxIiwNCiAgICAgICAgICAgICJuYW1lIiA6ICJDb2xvbm9zY29weSBQZXJmb3JtZWQiLA0KICAgICAgICAgICAgImNvbnRleHQiIDogIlBhdGllbnQiLA0KICAgICAgICAgICAgImFjY2Vzc0xldmVsIiA6ICJQdWJsaWMiLA0KICAgICAgICAgICAgImFubm90YXRpb24iIDogWyB7DQogICAgICAgICAgICAgICAidHlwZSIgOiAiQW5ub3RhdGlvbiIsDQogICAgICAgICAgICAgICAicyIgOiB7DQogICAgICAgICAgICAgICAgICAiciIgOiAiMTg1IiwNCiAgICAgICAgICAgICAgICAgICJzIiA6IFsgew0KICAgICAgICAgICAgICAgICAgICAgInZhbHVlIiA6IFsgIiIsImRlZmluZSAiLCJcIkNvbG9ub3Njb3B5IFBlcmZvcm1lZFwiIiwiOlxuICAiIF0NCiAgICAgICAgICAgICAgICAgIH0sIHsNCiAgICAgICAgICAgICAgICAgICAgICJyIiA6ICIxODQiLA0KICAgICAgICAgICAgICAgICAgICAgInMiIDogWyB7DQogICAgICAgICAgICAgICAgICAgICAgICAicyIgOiBbIHsNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICJyIiA6ICIxNzAiLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgInMiIDogWyB7DQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAiciIgOiAiMTY5IiwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJzIiA6IFsgew0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInIiIDogIjE2OSIsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAicyIgOiBbIHsNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJ2YWx1ZSIgOiBbICJbIiwiUHJvY2VkdXJlIiwiOiAiIF0NCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIH0sIHsNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJzIiA6IFsgew0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInZhbHVlIiA6IFsgIlwiQ29sb25vc2NvcHlcIiIgXQ0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfSBdDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB9LCB7DQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAidmFsdWUiIDogWyAiXSIgXQ0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfSBdDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICB9IF0NCiAgICAgICAgICAgICAgICAgICAgICAgICAgIH0sIHsNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJ2YWx1ZSIgOiBbICIgIiwiQ29sb25vc2NvcHkiIF0NCiAgICAgICAgICAgICAgICAgICAgICAgICAgIH0gXQ0KICAgICAgICAgICAgICAgICAgICAgICAgfSBdDQogICAgICAgICAgICAgICAgICAgICB9LCB7DQogICAgICAgICAgICAgICAgICAgICAgICAidmFsdWUiIDogWyAiXG4gICAgICAgICAgICAgICAgICAgIFx0XHQiIF0NCiAgICAgICAgICAgICAgICAgICAgIH0sIHsNCiAgICAgICAgICAgICAgICAgICAgICAgICJyIiA6ICIxODMiLA0KICAgICAgICAgICAgICAgICAgICAgICAgInMiIDogWyB7DQogICAgICAgICAgICAgICAgICAgICAgICAgICAidmFsdWUiIDogWyAid2hlcmUgIiBdDQogICAgICAgICAgICAgICAgICAgICAgICB9LCB7DQogICAgICAgICAgICAgICAgICAgICAgICAgICAiciIgOiAiMTgzIiwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICJzIiA6IFsgew0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInIiIDogIjE3NCIsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAicyIgOiBbIHsNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJyIiA6ICIxNzIiLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInMiIDogWyB7DQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAiciIgOiAiMTcxIiwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJzIiA6IFsgew0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInZhbHVlIiA6IFsgIkNvbG9ub3Njb3B5IiBdDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB9IF0NCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIH0sIHsNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJ2YWx1ZSIgOiBbICIuIiBdDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB9LCB7DQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAiciIgOiAiMTcyIiwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJzIiA6IFsgew0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInZhbHVlIiA6IFsgInN0YXR1cyIgXQ0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfSBdDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB9IF0NCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIH0sIHsNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJ2YWx1ZSIgOiBbICIgIiwiPSIsIiAiIF0NCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIH0sIHsNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJyIiA6ICIxNzMiLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInMiIDogWyB7DQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAidmFsdWUiIDogWyAiJ2NvbXBsZXRlZCciIF0NCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIH0gXQ0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfSBdDQogICAgICAgICAgICAgICAgICAgICAgICAgICB9LCB7DQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAidmFsdWUiIDogWyAiXG4gICAgICAgICAgICAgICAgICAgIFx0XHQgIGFuZCAiIF0NCiAgICAgICAgICAgICAgICAgICAgICAgICAgIH0sIHsNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJyIiA6ICIxODIiLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInMiIDogWyB7DQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAiciIgOiAiMTc4IiwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJzIiA6IFsgew0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInIiIDogIjE3NSIsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAicyIgOiBbIHsNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJ2YWx1ZSIgOiBbICJHbG9iYWwiIF0NCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIH0gXQ0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfSwgew0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInZhbHVlIiA6IFsgIi4iIF0NCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIH0sIHsNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJyIiA6ICIxNzgiLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInMiIDogWyB7DQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAidmFsdWUiIDogWyAiXCJOb3JtYWxpemUgSW50ZXJ2YWxcIiIsIigiIF0NCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIH0sIHsNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJyIiA6ICIxNzciLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInMiIDogWyB7DQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAiciIgOiAiMTc2IiwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJzIiA6IFsgew0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInZhbHVlIiA6IFsgIkNvbG9ub3Njb3B5IiBdDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB9IF0NCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIH0sIHsNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJ2YWx1ZSIgOiBbICIuIiBdDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB9LCB7DQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAiciIgOiAiMTc3IiwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJzIiA6IFsgew0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInZhbHVlIiA6IFsgInBlcmZvcm1lZCIgXQ0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfSBdDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB9IF0NCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIH0sIHsNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJ2YWx1ZSIgOiBbICIpIiBdDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB9IF0NCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIH0gXQ0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfSwgew0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInZhbHVlIiA6IFsgIiAiIF0NCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIH0sIHsNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJyIiA6ICIxODIiLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInMiIDogWyB7DQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAidmFsdWUiIDogWyAiZW5kcyAiIF0NCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIH0sIHsNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJyIiA6ICIxODEiLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInMiIDogWyB7DQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAidmFsdWUiIDogWyAiMTAgIiwieWVhcnMiIF0NCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIH0gXQ0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfSwgew0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInZhbHVlIiA6IFsgIiBvciBsZXNzIG9uIG9yIGJlZm9yZSIgXQ0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfSBdDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICB9LCB7DQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAidmFsdWUiIDogWyAiICIgXQ0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfSwgew0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInIiIDogIjE4MCIsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAicyIgOiBbIHsNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJ2YWx1ZSIgOiBbICJlbmQgb2YgIiBdDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB9LCB7DQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAiciIgOiAiMTc5IiwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJzIiA6IFsgew0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInZhbHVlIiA6IFsgIlwiTWVhc3VyZW1lbnQgUGVyaW9kXCIiIF0NCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIH0gXQ0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfSBdDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICB9IF0NCiAgICAgICAgICAgICAgICAgICAgICAgICAgIH0gXQ0KICAgICAgICAgICAgICAgICAgICAgICAgfSBdDQogICAgICAgICAgICAgICAgICAgICB9IF0NCiAgICAgICAgICAgICAgICAgIH0gXQ0KICAgICAgICAgICAgICAgfQ0KICAgICAgICAgICAgfSBdLA0KICAgICAgICAgICAgImV4cHJlc3Npb24iIDogew0KICAgICAgICAgICAgICAgImxvY2FsSWQiIDogIjE4NCIsDQogICAgICAgICAgICAgICAibG9jYXRvciIgOiAiOTg6My0xMDA6MTQxIiwNCiAgICAgICAgICAgICAgICJ0eXBlIiA6ICJRdWVyeSIsDQogICAgICAgICAgICAgICAic291cmNlIiA6IFsgew0KICAgICAgICAgICAgICAgICAgImxvY2FsSWQiIDogIjE3MCIsDQogICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiOTg6My05ODo0MCIsDQogICAgICAgICAgICAgICAgICAiYWxpYXMiIDogIkNvbG9ub3Njb3B5IiwNCiAgICAgICAgICAgICAgICAgICJleHByZXNzaW9uIiA6IHsNCiAgICAgICAgICAgICAgICAgICAgICJsb2NhbElkIiA6ICIxNjkiLA0KICAgICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjk4OjMtOTg6MjgiLA0KICAgICAgICAgICAgICAgICAgICAgImRhdGFUeXBlIiA6ICJ7aHR0cDovL2hsNy5vcmcvZmhpcn1Qcm9jZWR1cmUiLA0KICAgICAgICAgICAgICAgICAgICAgInRlbXBsYXRlSWQiIDogImh0dHA6Ly9obDcub3JnL2ZoaXIvU3RydWN0dXJlRGVmaW5pdGlvbi9Qcm9jZWR1cmUiLA0KICAgICAgICAgICAgICAgICAgICAgImNvZGVQcm9wZXJ0eSIgOiAiY29kZSIsDQogICAgICAgICAgICAgICAgICAgICAiY29kZUNvbXBhcmF0b3IiIDogImluIiwNCiAgICAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJSZXRyaWV2ZSIsDQogICAgICAgICAgICAgICAgICAgICAiY29kZXMiIDogew0KICAgICAgICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjk4OjE1LTk4OjI3IiwNCiAgICAgICAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJDb2xvbm9zY29weSIsDQogICAgICAgICAgICAgICAgICAgICAgICAidHlwZSIgOiAiVmFsdWVTZXRSZWYiDQogICAgICAgICAgICAgICAgICAgICB9DQogICAgICAgICAgICAgICAgICB9DQogICAgICAgICAgICAgICB9IF0sDQogICAgICAgICAgICAgICAicmVsYXRpb25zaGlwIiA6IFsgXSwNCiAgICAgICAgICAgICAgICJ3aGVyZSIgOiB7DQogICAgICAgICAgICAgICAgICAibG9jYWxJZCIgOiAiMTgzIiwNCiAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICI5OToyMy0xMDA6MTQxIiwNCiAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJBbmQiLA0KICAgICAgICAgICAgICAgICAgIm9wZXJhbmQiIDogWyB7DQogICAgICAgICAgICAgICAgICAgICAibG9jYWxJZCIgOiAiMTc0IiwNCiAgICAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICI5OToyOS05OTo2MCIsDQogICAgICAgICAgICAgICAgICAgICAidHlwZSIgOiAiRXF1YWwiLA0KICAgICAgICAgICAgICAgICAgICAgIm9wZXJhbmQiIDogWyB7DQogICAgICAgICAgICAgICAgICAgICAgICAibmFtZSIgOiAiVG9TdHJpbmciLA0KICAgICAgICAgICAgICAgICAgICAgICAgImxpYnJhcnlOYW1lIiA6ICJGSElSSGVscGVycyIsDQogICAgICAgICAgICAgICAgICAgICAgICAidHlwZSIgOiAiRnVuY3Rpb25SZWYiLA0KICAgICAgICAgICAgICAgICAgICAgICAgIm9wZXJhbmQiIDogWyB7DQogICAgICAgICAgICAgICAgICAgICAgICAgICAibG9jYWxJZCIgOiAiMTcyIiwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICI5OToyOS05OTo0NiIsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAicGF0aCIgOiAic3RhdHVzIiwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICJzY29wZSIgOiAiQ29sb25vc2NvcHkiLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgInR5cGUiIDogIlByb3BlcnR5Ig0KICAgICAgICAgICAgICAgICAgICAgICAgfSBdDQogICAgICAgICAgICAgICAgICAgICB9LCB7DQogICAgICAgICAgICAgICAgICAgICAgICAibG9jYWxJZCIgOiAiMTczIiwNCiAgICAgICAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICI5OTo1MC05OTo2MCIsDQogICAgICAgICAgICAgICAgICAgICAgICAidmFsdWVUeXBlIiA6ICJ7dXJuOmhsNy1vcmc6ZWxtLXR5cGVzOnIxfVN0cmluZyIsDQogICAgICAgICAgICAgICAgICAgICAgICAidmFsdWUiIDogImNvbXBsZXRlZCIsDQogICAgICAgICAgICAgICAgICAgICAgICAidHlwZSIgOiAiTGl0ZXJhbCINCiAgICAgICAgICAgICAgICAgICAgIH0gXQ0KICAgICAgICAgICAgICAgICAgfSwgew0KICAgICAgICAgICAgICAgICAgICAgImxvY2FsSWQiIDogIjE4MiIsDQogICAgICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMTAwOjI5LTEwMDoxNDEiLA0KICAgICAgICAgICAgICAgICAgICAgInR5cGUiIDogIkFuZCIsDQogICAgICAgICAgICAgICAgICAgICAib3BlcmFuZCIgOiBbIHsNCiAgICAgICAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIxMDA6ODUtMTAwOjEwMCIsDQogICAgICAgICAgICAgICAgICAgICAgICAidHlwZSIgOiAiSW4iLA0KICAgICAgICAgICAgICAgICAgICAgICAgIm9wZXJhbmQiIDogWyB7DQogICAgICAgICAgICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMTAwOjgwLTEwMDo4MyIsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAidHlwZSIgOiAiRW5kIiwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICJvcGVyYW5kIiA6IHsNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJsb2NhbElkIiA6ICIxNzgiLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjEwMDoyOS0xMDA6NzgiLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIm5hbWUiIDogIk5vcm1hbGl6ZSBJbnRlcnZhbCIsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAibGlicmFyeU5hbWUiIDogIkdsb2JhbCIsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAidHlwZSIgOiAiRnVuY3Rpb25SZWYiLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIm9wZXJhbmQiIDogWyB7DQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAibG9jYWxJZCIgOiAiMTc3IiwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIxMDA6NTctMTAwOjc3IiwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJwYXRoIiA6ICJwZXJmb3JtZWQiLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInNjb3BlIiA6ICJDb2xvbm9zY29weSIsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAidHlwZSIgOiAiUHJvcGVydHkiDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICB9IF0NCiAgICAgICAgICAgICAgICAgICAgICAgICAgIH0NCiAgICAgICAgICAgICAgICAgICAgICAgIH0sIHsNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIxMDA6ODUtMTAwOjEwMCIsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAibG93Q2xvc2VkIiA6IHRydWUsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAiaGlnaENsb3NlZCIgOiB0cnVlLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgInR5cGUiIDogIkludGVydmFsIiwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICJsb3ciIDogew0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjEwMDoxMTUtMTAwOjE0MSIsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAidHlwZSIgOiAiU3VidHJhY3QiLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIm9wZXJhbmQiIDogWyB7DQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAibG9jYWxJZCIgOiAiMTgwIiwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIxMDA6MTE1LTEwMDoxNDEiLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInR5cGUiIDogIkVuZCIsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAib3BlcmFuZCIgOiB7DQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAibG9jYWxJZCIgOiAiMTc5IiwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIxMDA6MTIyLTEwMDoxNDEiLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIm5hbWUiIDogIk1lYXN1cmVtZW50IFBlcmlvZCIsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAidHlwZSIgOiAiUGFyYW1ldGVyUmVmIg0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfQ0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfSwgew0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgImxvY2FsSWQiIDogIjE4MSIsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMTAwOjg1LTEwMDo5MiIsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAidmFsdWUiIDogMTAsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAidW5pdCIgOiAieWVhcnMiLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInR5cGUiIDogIlF1YW50aXR5Ig0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfSBdDQogICAgICAgICAgICAgICAgICAgICAgICAgICB9LA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgImhpZ2giIDogew0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgImxvY2FsSWQiIDogIjE4MCIsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMTAwOjExNS0xMDA6MTQxIiwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJFbmQiLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIm9wZXJhbmQiIDogew0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgImxvY2FsSWQiIDogIjE3OSIsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMTAwOjEyMi0xMDA6MTQxIiwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJNZWFzdXJlbWVudCBQZXJpb2QiLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInR5cGUiIDogIlBhcmFtZXRlclJlZiINCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIH0NCiAgICAgICAgICAgICAgICAgICAgICAgICAgIH0NCiAgICAgICAgICAgICAgICAgICAgICAgIH0gXQ0KICAgICAgICAgICAgICAgICAgICAgfSwgew0KICAgICAgICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjEwMDo4NS0xMDA6MTAwIiwNCiAgICAgICAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJOb3QiLA0KICAgICAgICAgICAgICAgICAgICAgICAgIm9wZXJhbmQiIDogew0KICAgICAgICAgICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjEwMDo4NS0xMDA6MTAwIiwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJJc051bGwiLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgIm9wZXJhbmQiIDogew0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgImxvY2FsSWQiIDogIjE4MCIsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMTAwOjExNS0xMDA6MTQxIiwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJFbmQiLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIm9wZXJhbmQiIDogew0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgImxvY2FsSWQiIDogIjE3OSIsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMTAwOjEyMi0xMDA6MTQxIiwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJNZWFzdXJlbWVudCBQZXJpb2QiLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInR5cGUiIDogIlBhcmFtZXRlclJlZiINCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIH0NCiAgICAgICAgICAgICAgICAgICAgICAgICAgIH0NCiAgICAgICAgICAgICAgICAgICAgICAgIH0NCiAgICAgICAgICAgICAgICAgICAgIH0gXQ0KICAgICAgICAgICAgICAgICAgfSBdDQogICAgICAgICAgICAgICB9DQogICAgICAgICAgICB9DQogICAgICAgICB9LCB7DQogICAgICAgICAgICAibG9jYWxJZCIgOiAiMjAwIiwNCiAgICAgICAgICAgICJsb2NhdG9yIiA6ICIxMDI6MS0xMDc6NTkiLA0KICAgICAgICAgICAgIm5hbWUiIDogIk51bWVyYXRvciIsDQogICAgICAgICAgICAiY29udGV4dCIgOiAiUGF0aWVudCIsDQogICAgICAgICAgICAiYWNjZXNzTGV2ZWwiIDogIlB1YmxpYyIsDQogICAgICAgICAgICAiYW5ub3RhdGlvbiIgOiBbIHsNCiAgICAgICAgICAgICAgICJ0eXBlIiA6ICJBbm5vdGF0aW9uIiwNCiAgICAgICAgICAgICAgICJzIiA6IHsNCiAgICAgICAgICAgICAgICAgICJyIiA6ICIyMDAiLA0KICAgICAgICAgICAgICAgICAgInMiIDogWyB7DQogICAgICAgICAgICAgICAgICAgICAidmFsdWUiIDogWyAiIiwiZGVmaW5lICIsIlwiTnVtZXJhdG9yXCIiLCI6XG4gICIgXQ0KICAgICAgICAgICAgICAgICAgfSwgew0KICAgICAgICAgICAgICAgICAgICAgInIiIDogIjE5OSIsDQogICAgICAgICAgICAgICAgICAgICAicyIgOiBbIHsNCiAgICAgICAgICAgICAgICAgICAgICAgICJyIiA6ICIxOTYiLA0KICAgICAgICAgICAgICAgICAgICAgICAgInMiIDogWyB7DQogICAgICAgICAgICAgICAgICAgICAgICAgICAiciIgOiAiMTkzIiwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICJzIiA6IFsgew0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInIiIDogIjE5MCIsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAicyIgOiBbIHsNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJyIiA6ICIxODciLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInMiIDogWyB7DQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAidmFsdWUiIDogWyAiZXhpc3RzICIgXQ0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfSwgew0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInIiIDogIjE4NiIsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAicyIgOiBbIHsNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJ2YWx1ZSIgOiBbICJcIkNvbG9ub3Njb3B5IFBlcmZvcm1lZFwiIiBdDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB9IF0NCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIH0gXQ0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfSwgew0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInZhbHVlIiA6IFsgIlxuICAgICAgICAgICAgICAgICAgICBcdFx0b3IgIiBdDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICB9LCB7DQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAiciIgOiAiMTg5IiwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJzIiA6IFsgew0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInZhbHVlIiA6IFsgImV4aXN0cyAiIF0NCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIH0sIHsNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJyIiA6ICIxODgiLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInMiIDogWyB7DQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAidmFsdWUiIDogWyAiXCJGZWNhbCBPY2N1bHQgQmxvb2QgVGVzdCBQZXJmb3JtZWRcIiIgXQ0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfSBdDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB9IF0NCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIH0gXQ0KICAgICAgICAgICAgICAgICAgICAgICAgICAgfSwgew0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInZhbHVlIiA6IFsgIlxuICAgICAgICAgICAgICAgICAgICBcdFx0b3IgIiBdDQogICAgICAgICAgICAgICAgICAgICAgICAgICB9LCB7DQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAiciIgOiAiMTkyIiwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJzIiA6IFsgew0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInZhbHVlIiA6IFsgImV4aXN0cyAiIF0NCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIH0sIHsNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJyIiA6ICIxOTEiLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInMiIDogWyB7DQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAidmFsdWUiIDogWyAiXCJGbGV4aWJsZSBTaWdtb2lkb3Njb3B5IFBlcmZvcm1lZFwiIiBdDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB9IF0NCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIH0gXQ0KICAgICAgICAgICAgICAgICAgICAgICAgICAgfSBdDQogICAgICAgICAgICAgICAgICAgICAgICB9LCB7DQogICAgICAgICAgICAgICAgICAgICAgICAgICAidmFsdWUiIDogWyAiXG4gICAgICAgICAgICAgICAgICAgIFx0XHRvciAiIF0NCiAgICAgICAgICAgICAgICAgICAgICAgIH0sIHsNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICJyIiA6ICIxOTUiLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgInMiIDogWyB7DQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAidmFsdWUiIDogWyAiZXhpc3RzICIgXQ0KICAgICAgICAgICAgICAgICAgICAgICAgICAgfSwgew0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInIiIDogIjE5NCIsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAicyIgOiBbIHsNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJ2YWx1ZSIgOiBbICJcIkZlY2FsIEltbXVub2NoZW1pY2FsIFRlc3QgRE5BXCIiIF0NCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIH0gXQ0KICAgICAgICAgICAgICAgICAgICAgICAgICAgfSBdDQogICAgICAgICAgICAgICAgICAgICAgICB9IF0NCiAgICAgICAgICAgICAgICAgICAgIH0sIHsNCiAgICAgICAgICAgICAgICAgICAgICAgICJ2YWx1ZSIgOiBbICJcbiAgICAgICAgICAgICAgICAgICAgXHRcdG9yICIgXQ0KICAgICAgICAgICAgICAgICAgICAgfSwgew0KICAgICAgICAgICAgICAgICAgICAgICAgInIiIDogIjE5OCIsDQogICAgICAgICAgICAgICAgICAgICAgICAicyIgOiBbIHsNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICJ2YWx1ZSIgOiBbICJleGlzdHMgIiBdDQogICAgICAgICAgICAgICAgICAgICAgICB9LCB7DQogICAgICAgICAgICAgICAgICAgICAgICAgICAiciIgOiAiMTk3IiwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICJzIiA6IFsgew0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInZhbHVlIiA6IFsgIlwiQ1QgQ29sb25vZ3JhcGh5IFBlcmZvcm1lZFwiIiBdDQogICAgICAgICAgICAgICAgICAgICAgICAgICB9IF0NCiAgICAgICAgICAgICAgICAgICAgICAgIH0gXQ0KICAgICAgICAgICAgICAgICAgICAgfSBdDQogICAgICAgICAgICAgICAgICB9IF0NCiAgICAgICAgICAgICAgIH0NCiAgICAgICAgICAgIH0gXSwNCiAgICAgICAgICAgICJleHByZXNzaW9uIiA6IHsNCiAgICAgICAgICAgICAgICJsb2NhbElkIiA6ICIxOTkiLA0KICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjEwMzozLTEwNzo1OSIsDQogICAgICAgICAgICAgICAidHlwZSIgOiAiT3IiLA0KICAgICAgICAgICAgICAgIm9wZXJhbmQiIDogWyB7DQogICAgICAgICAgICAgICAgICAibG9jYWxJZCIgOiAiMTk2IiwNCiAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIxMDM6My0xMDY6NjMiLA0KICAgICAgICAgICAgICAgICAgInR5cGUiIDogIk9yIiwNCiAgICAgICAgICAgICAgICAgICJvcGVyYW5kIiA6IFsgew0KICAgICAgICAgICAgICAgICAgICAgImxvY2FsSWQiIDogIjE5MyIsDQogICAgICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMTAzOjMtMTA1OjY2IiwNCiAgICAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJPciIsDQogICAgICAgICAgICAgICAgICAgICAib3BlcmFuZCIgOiBbIHsNCiAgICAgICAgICAgICAgICAgICAgICAgICJsb2NhbElkIiA6ICIxOTAiLA0KICAgICAgICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjEwMzozLTEwNDo2NyIsDQogICAgICAgICAgICAgICAgICAgICAgICAidHlwZSIgOiAiT3IiLA0KICAgICAgICAgICAgICAgICAgICAgICAgIm9wZXJhbmQiIDogWyB7DQogICAgICAgICAgICAgICAgICAgICAgICAgICAibG9jYWxJZCIgOiAiMTg3IiwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIxMDM6My0xMDM6MzIiLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgInR5cGUiIDogIkV4aXN0cyIsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAib3BlcmFuZCIgOiB7DQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAibG9jYWxJZCIgOiAiMTg2IiwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIxMDM6MTAtMTAzOjMyIiwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJDb2xvbm9zY29weSBQZXJmb3JtZWQiLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInR5cGUiIDogIkV4cHJlc3Npb25SZWYiDQogICAgICAgICAgICAgICAgICAgICAgICAgICB9DQogICAgICAgICAgICAgICAgICAgICAgICB9LCB7DQogICAgICAgICAgICAgICAgICAgICAgICAgICAibG9jYWxJZCIgOiAiMTg5IiwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIxMDQ6MjYtMTA0OjY3IiwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJFeGlzdHMiLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgIm9wZXJhbmQiIDogew0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgImxvY2FsSWQiIDogIjE4OCIsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMTA0OjMzLTEwNDo2NyIsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAibmFtZSIgOiAiRmVjYWwgT2NjdWx0IEJsb29kIFRlc3QgUGVyZm9ybWVkIiwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJFeHByZXNzaW9uUmVmIg0KICAgICAgICAgICAgICAgICAgICAgICAgICAgfQ0KICAgICAgICAgICAgICAgICAgICAgICAgfSBdDQogICAgICAgICAgICAgICAgICAgICB9LCB7DQogICAgICAgICAgICAgICAgICAgICAgICAibG9jYWxJZCIgOiAiMTkyIiwNCiAgICAgICAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIxMDU6MjYtMTA1OjY2IiwNCiAgICAgICAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJFeGlzdHMiLA0KICAgICAgICAgICAgICAgICAgICAgICAgIm9wZXJhbmQiIDogew0KICAgICAgICAgICAgICAgICAgICAgICAgICAgImxvY2FsSWQiIDogIjE5MSIsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMTA1OjMzLTEwNTo2NiIsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAibmFtZSIgOiAiRmxleGlibGUgU2lnbW9pZG9zY29weSBQZXJmb3JtZWQiLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgInR5cGUiIDogIkV4cHJlc3Npb25SZWYiDQogICAgICAgICAgICAgICAgICAgICAgICB9DQogICAgICAgICAgICAgICAgICAgICB9IF0NCiAgICAgICAgICAgICAgICAgIH0sIHsNCiAgICAgICAgICAgICAgICAgICAgICJsb2NhbElkIiA6ICIxOTUiLA0KICAgICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjEwNjoyNi0xMDY6NjMiLA0KICAgICAgICAgICAgICAgICAgICAgInR5cGUiIDogIkV4aXN0cyIsDQogICAgICAgICAgICAgICAgICAgICAib3BlcmFuZCIgOiB7DQogICAgICAgICAgICAgICAgICAgICAgICAibG9jYWxJZCIgOiAiMTk0IiwNCiAgICAgICAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIxMDY6MzMtMTA2OjYzIiwNCiAgICAgICAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJGZWNhbCBJbW11bm9jaGVtaWNhbCBUZXN0IEROQSIsDQogICAgICAgICAgICAgICAgICAgICAgICAidHlwZSIgOiAiRXhwcmVzc2lvblJlZiINCiAgICAgICAgICAgICAgICAgICAgIH0NCiAgICAgICAgICAgICAgICAgIH0gXQ0KICAgICAgICAgICAgICAgfSwgew0KICAgICAgICAgICAgICAgICAgImxvY2FsSWQiIDogIjE5OCIsDQogICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMTA3OjI2LTEwNzo1OSIsDQogICAgICAgICAgICAgICAgICAidHlwZSIgOiAiRXhpc3RzIiwNCiAgICAgICAgICAgICAgICAgICJvcGVyYW5kIiA6IHsNCiAgICAgICAgICAgICAgICAgICAgICJsb2NhbElkIiA6ICIxOTciLA0KICAgICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjEwNzozMy0xMDc6NTkiLA0KICAgICAgICAgICAgICAgICAgICAgIm5hbWUiIDogIkNUIENvbG9ub2dyYXBoeSBQZXJmb3JtZWQiLA0KICAgICAgICAgICAgICAgICAgICAgInR5cGUiIDogIkV4cHJlc3Npb25SZWYiDQogICAgICAgICAgICAgICAgICB9DQogICAgICAgICAgICAgICB9IF0NCiAgICAgICAgICAgIH0NCiAgICAgICAgIH0sIHsNCiAgICAgICAgICAgICJsb2NhbElkIiA6ICIyMTAiLA0KICAgICAgICAgICAgImxvY2F0b3IiIDogIjEwOToxLTExMToxMjAiLA0KICAgICAgICAgICAgIm5hbWUiIDogIk1hbGlnbmFudCBOZW9wbGFzbSIsDQogICAgICAgICAgICAiY29udGV4dCIgOiAiUGF0aWVudCIsDQogICAgICAgICAgICAiYWNjZXNzTGV2ZWwiIDogIlB1YmxpYyIsDQogICAgICAgICAgICAiYW5ub3RhdGlvbiIgOiBbIHsNCiAgICAgICAgICAgICAgICJ0eXBlIiA6ICJBbm5vdGF0aW9uIiwNCiAgICAgICAgICAgICAgICJzIiA6IHsNCiAgICAgICAgICAgICAgICAgICJyIiA6ICIyMTAiLA0KICAgICAgICAgICAgICAgICAgInMiIDogWyB7DQogICAgICAgICAgICAgICAgICAgICAidmFsdWUiIDogWyAiIiwiZGVmaW5lICIsIlwiTWFsaWduYW50IE5lb3BsYXNtXCIiLCI6XG4gICIgXQ0KICAgICAgICAgICAgICAgICAgfSwgew0KICAgICAgICAgICAgICAgICAgICAgInIiIDogIjIwOSIsDQogICAgICAgICAgICAgICAgICAgICAicyIgOiBbIHsNCiAgICAgICAgICAgICAgICAgICAgICAgICJzIiA6IFsgew0KICAgICAgICAgICAgICAgICAgICAgICAgICAgInIiIDogIjIwMiIsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAicyIgOiBbIHsNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJyIiA6ICIyMDEiLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInMiIDogWyB7DQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAiciIgOiAiMjAxIiwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJzIiA6IFsgew0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInZhbHVlIiA6IFsgIlsiLCJDb25kaXRpb24iLCI6ICIgXQ0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfSwgew0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInMiIDogWyB7DQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAidmFsdWUiIDogWyAiXCJNYWxpZ25hbnQgTmVvcGxhc20gb2YgQ29sb25cIiIgXQ0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfSBdDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB9LCB7DQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAidmFsdWUiIDogWyAiXSIgXQ0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfSBdDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICB9IF0NCiAgICAgICAgICAgICAgICAgICAgICAgICAgIH0sIHsNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJ2YWx1ZSIgOiBbICIgIiwiQ29sb3JlY3RhbENhbmNlciIgXQ0KICAgICAgICAgICAgICAgICAgICAgICAgICAgfSBdDQogICAgICAgICAgICAgICAgICAgICAgICB9IF0NCiAgICAgICAgICAgICAgICAgICAgIH0sIHsNCiAgICAgICAgICAgICAgICAgICAgICAgICJ2YWx1ZSIgOiBbICJcbiAgICAgICAgICAgICAgICAgICAgXHRcdCIgXQ0KICAgICAgICAgICAgICAgICAgICAgfSwgew0KICAgICAgICAgICAgICAgICAgICAgICAgInIiIDogIjIwOCIsDQogICAgICAgICAgICAgICAgICAgICAgICAicyIgOiBbIHsNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICJ2YWx1ZSIgOiBbICJ3aGVyZSAiIF0NCiAgICAgICAgICAgICAgICAgICAgICAgIH0sIHsNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICJyIiA6ICIyMDgiLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgInMiIDogWyB7DQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAiciIgOiAiMjA1IiwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJzIiA6IFsgew0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInIiIDogIjIwMyIsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAicyIgOiBbIHsNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJ2YWx1ZSIgOiBbICJHbG9iYWwiIF0NCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIH0gXQ0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfSwgew0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInZhbHVlIiA6IFsgIi4iIF0NCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIH0sIHsNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJyIiA6ICIyMDUiLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInMiIDogWyB7DQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAidmFsdWUiIDogWyAiXCJQcmV2YWxlbmNlIFBlcmlvZFwiIiwiKCIgXQ0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfSwgew0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInIiIDogIjIwNCIsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAicyIgOiBbIHsNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJ2YWx1ZSIgOiBbICJDb2xvcmVjdGFsQ2FuY2VyIiBdDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB9IF0NCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIH0sIHsNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJ2YWx1ZSIgOiBbICIpIiBdDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB9IF0NCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIH0gXQ0KICAgICAgICAgICAgICAgICAgICAgICAgICAgfSwgew0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInIiIDogIjIwOCIsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAidmFsdWUiIDogWyAiICIsInN0YXJ0cyBvbiBvciBiZWZvcmUiLCIgIiBdDQogICAgICAgICAgICAgICAgICAgICAgICAgICB9LCB7DQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAiciIgOiAiMjA3IiwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJzIiA6IFsgew0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInZhbHVlIiA6IFsgImVuZCBvZiAiIF0NCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIH0sIHsNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJyIiA6ICIyMDYiLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInMiIDogWyB7DQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAidmFsdWUiIDogWyAiXCJNZWFzdXJlbWVudCBQZXJpb2RcIiIgXQ0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfSBdDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICB9IF0NCiAgICAgICAgICAgICAgICAgICAgICAgICAgIH0gXQ0KICAgICAgICAgICAgICAgICAgICAgICAgfSBdDQogICAgICAgICAgICAgICAgICAgICB9IF0NCiAgICAgICAgICAgICAgICAgIH0gXQ0KICAgICAgICAgICAgICAgfQ0KICAgICAgICAgICAgfSBdLA0KICAgICAgICAgICAgImV4cHJlc3Npb24iIDogew0KICAgICAgICAgICAgICAgImxvY2FsSWQiIDogIjIwOSIsDQogICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMTEwOjMtMTExOjEyMCIsDQogICAgICAgICAgICAgICAidHlwZSIgOiAiUXVlcnkiLA0KICAgICAgICAgICAgICAgInNvdXJjZSIgOiBbIHsNCiAgICAgICAgICAgICAgICAgICJsb2NhbElkIiA6ICIyMDIiLA0KICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjExMDozLTExMDo2MSIsDQogICAgICAgICAgICAgICAgICAiYWxpYXMiIDogIkNvbG9yZWN0YWxDYW5jZXIiLA0KICAgICAgICAgICAgICAgICAgImV4cHJlc3Npb24iIDogew0KICAgICAgICAgICAgICAgICAgICAgImxvY2FsSWQiIDogIjIwMSIsDQogICAgICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMTEwOjMtMTEwOjQ0IiwNCiAgICAgICAgICAgICAgICAgICAgICJkYXRhVHlwZSIgOiAie2h0dHA6Ly9obDcub3JnL2ZoaXJ9Q29uZGl0aW9uIiwNCiAgICAgICAgICAgICAgICAgICAgICJ0ZW1wbGF0ZUlkIiA6ICJodHRwOi8vaGw3Lm9yZy9maGlyL1N0cnVjdHVyZURlZmluaXRpb24vQ29uZGl0aW9uIiwNCiAgICAgICAgICAgICAgICAgICAgICJjb2RlUHJvcGVydHkiIDogImNvZGUiLA0KICAgICAgICAgICAgICAgICAgICAgImNvZGVDb21wYXJhdG9yIiA6ICJpbiIsDQogICAgICAgICAgICAgICAgICAgICAidHlwZSIgOiAiUmV0cmlldmUiLA0KICAgICAgICAgICAgICAgICAgICAgImNvZGVzIiA6IHsNCiAgICAgICAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIxMTA6MTUtMTEwOjQzIiwNCiAgICAgICAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJNYWxpZ25hbnQgTmVvcGxhc20gb2YgQ29sb24iLA0KICAgICAgICAgICAgICAgICAgICAgICAgInR5cGUiIDogIlZhbHVlU2V0UmVmIg0KICAgICAgICAgICAgICAgICAgICAgfQ0KICAgICAgICAgICAgICAgICAgfQ0KICAgICAgICAgICAgICAgfSBdLA0KICAgICAgICAgICAgICAgInJlbGF0aW9uc2hpcCIgOiBbIF0sDQogICAgICAgICAgICAgICAid2hlcmUiIDogew0KICAgICAgICAgICAgICAgICAgImxvY2FsSWQiIDogIjIwOCIsDQogICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMTExOjIzLTExMToxMjAiLA0KICAgICAgICAgICAgICAgICAgInR5cGUiIDogIlNhbWVPckJlZm9yZSIsDQogICAgICAgICAgICAgICAgICAib3BlcmFuZCIgOiBbIHsNCiAgICAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIxMTE6NzQtMTExOjc5IiwNCiAgICAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJTdGFydCIsDQogICAgICAgICAgICAgICAgICAgICAib3BlcmFuZCIgOiB7DQogICAgICAgICAgICAgICAgICAgICAgICAibG9jYWxJZCIgOiAiMjA1IiwNCiAgICAgICAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIxMTE6MjktMTExOjcyIiwNCiAgICAgICAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJQcmV2YWxlbmNlIFBlcmlvZCIsDQogICAgICAgICAgICAgICAgICAgICAgICAibGlicmFyeU5hbWUiIDogIkdsb2JhbCIsDQogICAgICAgICAgICAgICAgICAgICAgICAidHlwZSIgOiAiRnVuY3Rpb25SZWYiLA0KICAgICAgICAgICAgICAgICAgICAgICAgIm9wZXJhbmQiIDogWyB7DQogICAgICAgICAgICAgICAgICAgICAgICAgICAibG9jYWxJZCIgOiAiMjA0IiwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIxMTE6NTYtMTExOjcxIiwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJDb2xvcmVjdGFsQ2FuY2VyIiwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJBbGlhc1JlZiINCiAgICAgICAgICAgICAgICAgICAgICAgIH0gXQ0KICAgICAgICAgICAgICAgICAgICAgfQ0KICAgICAgICAgICAgICAgICAgfSwgew0KICAgICAgICAgICAgICAgICAgICAgImxvY2FsSWQiIDogIjIwNyIsDQogICAgICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMTExOjk0LTExMToxMjAiLA0KICAgICAgICAgICAgICAgICAgICAgInR5cGUiIDogIkVuZCIsDQogICAgICAgICAgICAgICAgICAgICAib3BlcmFuZCIgOiB7DQogICAgICAgICAgICAgICAgICAgICAgICAibG9jYWxJZCIgOiAiMjA2IiwNCiAgICAgICAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIxMTE6MTAxLTExMToxMjAiLA0KICAgICAgICAgICAgICAgICAgICAgICAgIm5hbWUiIDogIk1lYXN1cmVtZW50IFBlcmlvZCIsDQogICAgICAgICAgICAgICAgICAgICAgICAidHlwZSIgOiAiUGFyYW1ldGVyUmVmIg0KICAgICAgICAgICAgICAgICAgICAgfQ0KICAgICAgICAgICAgICAgICAgfSBdDQogICAgICAgICAgICAgICB9DQogICAgICAgICAgICB9DQogICAgICAgICB9LCB7DQogICAgICAgICAgICAibG9jYWxJZCIgOiAiMjI1IiwNCiAgICAgICAgICAgICJsb2NhdG9yIiA6ICIxMTM6MS0xMTg6OTAiLA0KICAgICAgICAgICAgIm5hbWUiIDogIkRlbm9taW5hdG9yIEV4Y2x1c2lvbiIsDQogICAgICAgICAgICAiY29udGV4dCIgOiAiUGF0aWVudCIsDQogICAgICAgICAgICAiYWNjZXNzTGV2ZWwiIDogIlB1YmxpYyIsDQogICAgICAgICAgICAiYW5ub3RhdGlvbiIgOiBbIHsNCiAgICAgICAgICAgICAgICJ0eXBlIiA6ICJBbm5vdGF0aW9uIiwNCiAgICAgICAgICAgICAgICJzIiA6IHsNCiAgICAgICAgICAgICAgICAgICJyIiA6ICIyMjUiLA0KICAgICAgICAgICAgICAgICAgInMiIDogWyB7DQogICAgICAgICAgICAgICAgICAgICAidmFsdWUiIDogWyAiIiwiZGVmaW5lICIsIlwiRGVub21pbmF0b3IgRXhjbHVzaW9uXCIiLCI6XG4gICIgXQ0KICAgICAgICAgICAgICAgICAgfSwgew0KICAgICAgICAgICAgICAgICAgICAgInIiIDogIjIyNCIsDQogICAgICAgICAgICAgICAgICAgICAicyIgOiBbIHsNCiAgICAgICAgICAgICAgICAgICAgICAgICJyIiA6ICIyMTgiLA0KICAgICAgICAgICAgICAgICAgICAgICAgInMiIDogWyB7DQogICAgICAgICAgICAgICAgICAgICAgICAgICAiciIgOiAiMjE1IiwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICJzIiA6IFsgew0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInIiIDogIjIxMiIsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAicyIgOiBbIHsNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJyIiA6ICIyMTEiLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInMiIDogWyB7DQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAidmFsdWUiIDogWyAiSG9zcGljZSIgXQ0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfSBdDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICB9LCB7DQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAidmFsdWUiIDogWyAiLiIgXQ0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfSwgew0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInIiIDogIjIxMiIsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAicyIgOiBbIHsNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJ2YWx1ZSIgOiBbICJcIkhhcyBIb3NwaWNlXCIiIF0NCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIH0gXQ0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfSBdDQogICAgICAgICAgICAgICAgICAgICAgICAgICB9LCB7DQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAidmFsdWUiIDogWyAiXG4gICAgICAgICAgICAgICAgICAgIFx0XHRvciAiIF0NCiAgICAgICAgICAgICAgICAgICAgICAgICAgIH0sIHsNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJyIiA6ICIyMTQiLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInMiIDogWyB7DQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAidmFsdWUiIDogWyAiZXhpc3RzICIgXQ0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfSwgew0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInIiIDogIjIxMyIsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAicyIgOiBbIHsNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJ2YWx1ZSIgOiBbICJcIk1hbGlnbmFudCBOZW9wbGFzbVwiIiBdDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB9IF0NCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIH0gXQ0KICAgICAgICAgICAgICAgICAgICAgICAgICAgfSBdDQogICAgICAgICAgICAgICAgICAgICAgICB9LCB7DQogICAgICAgICAgICAgICAgICAgICAgICAgICAidmFsdWUiIDogWyAiXG4gICAgICAgICAgICAgICAgICAgIFx0XHRvciAiIF0NCiAgICAgICAgICAgICAgICAgICAgICAgIH0sIHsNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICJyIiA6ICIyMTciLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgInMiIDogWyB7DQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAidmFsdWUiIDogWyAiZXhpc3RzICIgXQ0KICAgICAgICAgICAgICAgICAgICAgICAgICAgfSwgew0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInIiIDogIjIxNiIsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAicyIgOiBbIHsNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJ2YWx1ZSIgOiBbICJcIlRvdGFsIENvbGVjdG9teSBQZXJmb3JtZWRcIiIgXQ0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfSBdDQogICAgICAgICAgICAgICAgICAgICAgICAgICB9IF0NCiAgICAgICAgICAgICAgICAgICAgICAgIH0gXQ0KICAgICAgICAgICAgICAgICAgICAgfSwgew0KICAgICAgICAgICAgICAgICAgICAgICAgInZhbHVlIiA6IFsgIlxuICAgICAgICAgICAgICAgICAgICBcdFx0b3IgIiBdDQogICAgICAgICAgICAgICAgICAgICB9LCB7DQogICAgICAgICAgICAgICAgICAgICAgICAiciIgOiAiMjIzIiwNCiAgICAgICAgICAgICAgICAgICAgICAgICJzIiA6IFsgew0KICAgICAgICAgICAgICAgICAgICAgICAgICAgInIiIDogIjIyMCIsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAicyIgOiBbIHsNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJyIiA6ICIyMTkiLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInMiIDogWyB7DQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAidmFsdWUiIDogWyAiRnJhaWx0eSIgXQ0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfSBdDQogICAgICAgICAgICAgICAgICAgICAgICAgICB9LCB7DQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAidmFsdWUiIDogWyAiLiAiIF0NCiAgICAgICAgICAgICAgICAgICAgICAgICAgIH0sIHsNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJyIiA6ICIyMjAiLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInMiIDogWyB7DQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAidmFsdWUiIDogWyAiXCJBZHZhbmNlZCBJbGxuZXNzIGFuZCBGcmFpbHR5IEV4Y2x1c2lvbiBOb3QgSW5jbHVkaW5nIE92ZXIgQWdlIDgwXCIiIF0NCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIH0gXQ0KICAgICAgICAgICAgICAgICAgICAgICAgICAgfSBdDQogICAgICAgICAgICAgICAgICAgICAgICB9LCB7DQogICAgICAgICAgICAgICAgICAgICAgICAgICAidmFsdWUiIDogWyAiXG4gICAgICAgICAgICAgICAgICAgIFx0XHRhbmQgIiBdDQogICAgICAgICAgICAgICAgICAgICAgICB9LCB7DQogICAgICAgICAgICAgICAgICAgICAgICAgICAiciIgOiAiMjIyIiwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICJzIiA6IFsgew0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInIiIDogIjIyMSIsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAicyIgOiBbIHsNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJ2YWx1ZSIgOiBbICJGcmFpbHR5IiBdDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICB9IF0NCiAgICAgICAgICAgICAgICAgICAgICAgICAgIH0sIHsNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJ2YWx1ZSIgOiBbICIuIiBdDQogICAgICAgICAgICAgICAgICAgICAgICAgICB9LCB7DQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAiciIgOiAiMjIyIiwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJzIiA6IFsgew0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInZhbHVlIiA6IFsgIlwiTG9uZyBUZXJtIENhcmUgUGVyaW9kcyBMb25nZXIgVGhhbiA5MCBDb25zZWN1dGl2ZSBEYXlzXCIiIF0NCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIH0gXQ0KICAgICAgICAgICAgICAgICAgICAgICAgICAgfSBdDQogICAgICAgICAgICAgICAgICAgICAgICB9IF0NCiAgICAgICAgICAgICAgICAgICAgIH0gXQ0KICAgICAgICAgICAgICAgICAgfSBdDQogICAgICAgICAgICAgICB9DQogICAgICAgICAgICB9IF0sDQogICAgICAgICAgICAiZXhwcmVzc2lvbiIgOiB7DQogICAgICAgICAgICAgICAibG9jYWxJZCIgOiAiMjI0IiwNCiAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIxMTQ6My0xMTg6OTAiLA0KICAgICAgICAgICAgICAgInR5cGUiIDogIk9yIiwNCiAgICAgICAgICAgICAgICJvcGVyYW5kIiA6IFsgew0KICAgICAgICAgICAgICAgICAgImxvY2FsSWQiIDogIjIxOCIsDQogICAgICAgICAgICAgICAgICAibG9jYXRvciIgOiAiMTE0OjMtMTE2OjU5IiwNCiAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJPciIsDQogICAgICAgICAgICAgICAgICAib3BlcmFuZCIgOiBbIHsNCiAgICAgICAgICAgICAgICAgICAgICJsb2NhbElkIiA6ICIyMTUiLA0KICAgICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjExNDozLTExNTo1MiIsDQogICAgICAgICAgICAgICAgICAgICAidHlwZSIgOiAiT3IiLA0KICAgICAgICAgICAgICAgICAgICAgIm9wZXJhbmQiIDogWyB7DQogICAgICAgICAgICAgICAgICAgICAgICAibG9jYWxJZCIgOiAiMjEyIiwNCiAgICAgICAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIxMTQ6My0xMTQ6MjMiLA0KICAgICAgICAgICAgICAgICAgICAgICAgIm5hbWUiIDogIkhhcyBIb3NwaWNlIiwNCiAgICAgICAgICAgICAgICAgICAgICAgICJsaWJyYXJ5TmFtZSIgOiAiSG9zcGljZSIsDQogICAgICAgICAgICAgICAgICAgICAgICAidHlwZSIgOiAiRXhwcmVzc2lvblJlZiINCiAgICAgICAgICAgICAgICAgICAgIH0sIHsNCiAgICAgICAgICAgICAgICAgICAgICAgICJsb2NhbElkIiA6ICIyMTQiLA0KICAgICAgICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjExNToyNi0xMTU6NTIiLA0KICAgICAgICAgICAgICAgICAgICAgICAgInR5cGUiIDogIkV4aXN0cyIsDQogICAgICAgICAgICAgICAgICAgICAgICAib3BlcmFuZCIgOiB7DQogICAgICAgICAgICAgICAgICAgICAgICAgICAibG9jYWxJZCIgOiAiMjEzIiwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIxMTU6MzMtMTE1OjUyIiwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJNYWxpZ25hbnQgTmVvcGxhc20iLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgInR5cGUiIDogIkV4cHJlc3Npb25SZWYiDQogICAgICAgICAgICAgICAgICAgICAgICB9DQogICAgICAgICAgICAgICAgICAgICB9IF0NCiAgICAgICAgICAgICAgICAgIH0sIHsNCiAgICAgICAgICAgICAgICAgICAgICJsb2NhbElkIiA6ICIyMTciLA0KICAgICAgICAgICAgICAgICAgICAgImxvY2F0b3IiIDogIjExNjoyNi0xMTY6NTkiLA0KICAgICAgICAgICAgICAgICAgICAgInR5cGUiIDogIkV4aXN0cyIsDQogICAgICAgICAgICAgICAgICAgICAib3BlcmFuZCIgOiB7DQogICAgICAgICAgICAgICAgICAgICAgICAibG9jYWxJZCIgOiAiMjE2IiwNCiAgICAgICAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIxMTY6MzMtMTE2OjU5IiwNCiAgICAgICAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJUb3RhbCBDb2xlY3RvbXkgUGVyZm9ybWVkIiwNCiAgICAgICAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJFeHByZXNzaW9uUmVmIg0KICAgICAgICAgICAgICAgICAgICAgfQ0KICAgICAgICAgICAgICAgICAgfSBdDQogICAgICAgICAgICAgICB9LCB7DQogICAgICAgICAgICAgICAgICAibG9jYWxJZCIgOiAiMjIzIiwNCiAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIxMTc6MjYtMTE4OjkwIiwNCiAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJBbmQiLA0KICAgICAgICAgICAgICAgICAgIm9wZXJhbmQiIDogWyB7DQogICAgICAgICAgICAgICAgICAgICAibG9jYWxJZCIgOiAiMjIwIiwNCiAgICAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIxMTc6MjYtMTE3OjEwMCIsDQogICAgICAgICAgICAgICAgICAgICAibmFtZSIgOiAiQWR2YW5jZWQgSWxsbmVzcyBhbmQgRnJhaWx0eSBFeGNsdXNpb24gTm90IEluY2x1ZGluZyBPdmVyIEFnZSA4MCIsDQogICAgICAgICAgICAgICAgICAgICAibGlicmFyeU5hbWUiIDogIkZyYWlsdHkiLA0KICAgICAgICAgICAgICAgICAgICAgInR5cGUiIDogIkV4cHJlc3Npb25SZWYiDQogICAgICAgICAgICAgICAgICB9LCB7DQogICAgICAgICAgICAgICAgICAgICAibG9jYWxJZCIgOiAiMjIyIiwNCiAgICAgICAgICAgICAgICAgICAgICJsb2NhdG9yIiA6ICIxMTg6MjctMTE4OjkwIiwNCiAgICAgICAgICAgICAgICAgICAgICJuYW1lIiA6ICJMb25nIFRlcm0gQ2FyZSBQZXJpb2RzIExvbmdlciBUaGFuIDkwIENvbnNlY3V0aXZlIERheXMiLA0KICAgICAgICAgICAgICAgICAgICAgImxpYnJhcnlOYW1lIiA6ICJGcmFpbHR5IiwNCiAgICAgICAgICAgICAgICAgICAgICJ0eXBlIiA6ICJFeHByZXNzaW9uUmVmIg0KICAgICAgICAgICAgICAgICAgfSBdDQogICAgICAgICAgICAgICB9IF0NCiAgICAgICAgICAgIH0NCiAgICAgICAgIH0gXQ0KICAgICAgfQ0KICAgfQ0KfQ==" + } + ] + }, + "request": { + "method": "PUT", + "url": "Library/ColorectalCancerScreeningsFHIR", + "ifNoneExist": "Library?url=http://ecqi.healthit.gov/ecqms/Library/ColorectalCancerScreeningsFHIR&version=0.0.002" + } + } + ]} diff --git a/hapi-fhir-storage-cr/src/test/resources/multiversion/valueset-version-bundle.json b/hapi-fhir-storage-cr/src/test/resources/multiversion/valueset-version-bundle.json new file mode 100644 index 00000000000..81b2d0d7316 --- /dev/null +++ b/hapi-fhir-storage-cr/src/test/resources/multiversion/valueset-version-bundle.json @@ -0,0 +1,3780 @@ +{ + "resourceType": "Bundle", + "id": "GeneratedBundle1", + "type": "transaction", + "entry": [ + { + "resource": { + "resourceType": "ValueSet", + "id": "2.16.840.1.113883.3.464.1003.101.12.1001", + "url": "http://cts.nlm.nih.gov/fhir/ValueSet/2.16.840.1.113883.3.464.1003.101.12.1001", + "identifier": [ + { + "system": "urn:ietf:rfc:3986", + "value": "2.16.840.1.113883.3.464.1003.101.12.1001" + } + ], + "version": "20180311", + "name": "OfficeVisit", + "title": "Office Visit", + "status": "active", + "experimental": false, + "publisher": "NLM", + "expansion": { + "identifier": "20200507", + "timestamp": "2021-01-14T20:59:46-07:00", + "contains": [ + { + "system": "http://www.ama-assn.org/go/cpt", + "version": "2020", + "code": "99201", + "display": "Office or other outpatient visit for the evaluation and management of a new patient, which requires these 3 key components: A problem focused history; A problem focused examination; Straightforward medical decision making. Counseling and/or coordination of care with other physicians, other qualified health care professionals, or agencies are provided consistent with the nature of the problem(s) and the patient's and/or family's needs. Usually, the presenting problem(s) are self limited or minor. Typically, 10 minutes are spent face-to-face with the patient and/or family." + }, + { + "system": "http://www.ama-assn.org/go/cpt", + "version": "2020", + "code": "99202", + "display": "Office or other outpatient visit for the evaluation and management of a new patient, which requires these 3 key components: An expanded problem focused history; An expanded problem focused examination; Straightforward medical decision making. Counseling and/or coordination of care with other physicians, other qualified health care professionals, or agencies are provided consistent with the nature of the problem(s) and the patient's and/or family's needs. Usually, the presenting problem(s) are of low to moderate severity. Typically, 20 minutes are spent face-to-face with the patient and/or family." + }, + { + "system": "http://www.ama-assn.org/go/cpt", + "version": "2020", + "code": "99203", + "display": "Office or other outpatient visit for the evaluation and management of a new patient, which requires these 3 key components: A detailed history; A detailed examination; Medical decision making of low complexity. Counseling and/or coordination of care with other physicians, other qualified health care professionals, or agencies are provided consistent with the nature of the problem(s) and the patient's and/or family's needs. Usually, the presenting problem(s) are of moderate severity. Typically, 30 minutes are spent face-to-face with the patient and/or family." + }, + { + "system": "http://www.ama-assn.org/go/cpt", + "version": "2020", + "code": "99204", + "display": "Office or other outpatient visit for the evaluation and management of a new patient, which requires these 3 key components: A comprehensive history; A comprehensive examination; Medical decision making of moderate complexity. Counseling and/or coordination of care with other physicians, other qualified health care professionals, or agencies are provided consistent with the nature of the problem(s) and the patient's and/or family's needs. Usually, the presenting problem(s) are of moderate to high severity. Typically, 45 minutes are spent face-to-face with the patient and/or family." + }, + { + "system": "http://www.ama-assn.org/go/cpt", + "version": "2020", + "code": "99205", + "display": "Office or other outpatient visit for the evaluation and management of a new patient, which requires these 3 key components: A comprehensive history; A comprehensive examination; Medical decision making of high complexity. Counseling and/or coordination of care with other physicians, other qualified health care professionals, or agencies are provided consistent with the nature of the problem(s) and the patient's and/or family's needs. Usually, the presenting problem(s) are of moderate to high severity. Typically, 60 minutes are spent face-to-face with the patient and/or family." + }, + { + "system": "http://www.ama-assn.org/go/cpt", + "version": "2020", + "code": "99212", + "display": "Office or other outpatient visit for the evaluation and management of an established patient, which requires at least 2 of these 3 key components: A problem focused history; A problem focused examination; Straightforward medical decision making. Counseling and/or coordination of care with other physicians, other qualified health care professionals, or agencies are provided consistent with the nature of the problem(s) and the patient's and/or family's needs. Usually, the presenting problem(s) are self limited or minor. Typically, 10 minutes are spent face-to-face with the patient and/or family." + }, + { + "system": "http://www.ama-assn.org/go/cpt", + "version": "2020", + "code": "99213", + "display": "Office or other outpatient visit for the evaluation and management of an established patient, which requires at least 2 of these 3 key components: An expanded problem focused history; An expanded problem focused examination; Medical decision making of low complexity. Counseling and coordination of care with other physicians, other qualified health care professionals, or agencies are provided consistent with the nature of the problem(s) and the patient's and/or family's needs. Usually, the presenting problem(s) are of low to moderate severity. Typically, 15 minutes are spent face-to-face with the patient and/or family." + }, + { + "system": "http://www.ama-assn.org/go/cpt", + "version": "2020", + "code": "99214", + "display": "Office or other outpatient visit for the evaluation and management of an established patient, which requires at least 2 of these 3 key components: A detailed history; A detailed examination; Medical decision making of moderate complexity. Counseling and/or coordination of care with other physicians, other qualified health care professionals, or agencies are provided consistent with the nature of the problem(s) and the patient's and/or family's needs. Usually, the presenting problem(s) are of moderate to high severity. Typically, 25 minutes are spent face-to-face with the patient and/or family." + }, + { + "system": "http://www.ama-assn.org/go/cpt", + "version": "2020", + "code": "99215", + "display": "Office or other outpatient visit for the evaluation and management of an established patient, which requires at least 2 of these 3 key components: A comprehensive history; A comprehensive examination; Medical decision making of high complexity. Counseling and/or coordination of care with other physicians, other qualified health care professionals, or agencies are provided consistent with the nature of the problem(s) and the patient's and/or family's needs. Usually, the presenting problem(s) are of moderate to high severity. Typically, 40 minutes are spent face-to-face with the patient and/or family." + }, + { + "system": "http://snomed.info/sct", + "version": "2018-03", + "code": "30346009", + "display": "Evaluation and management of established outpatient in office or other outpatient facility (procedure)" + }, + { + "system": "http://snomed.info/sct", + "version": "2018-03", + "code": "37894004", + "display": "Evaluation and management of new outpatient in office or other outpatient facility (procedure)" + }, + { + "system": "http://snomed.info/sct", + "version": "2019-09", + "code": "185463005", + "display": "Visit out of hours (procedure)" + }, + { + "system": "http://snomed.info/sct", + "version": "2019-09", + "code": "185464004", + "display": "Out of hours visit - not night visit (procedure)" + }, + { + "system": "http://snomed.info/sct", + "version": "2019-09", + "code": "185465003", + "display": "Weekend visit (procedure)" + }, + { + "system": "http://snomed.info/sct", + "version": "2019-09", + "code": "3391000175108", + "display": "Office visit for pediatric care and assessment (procedure)" + }, + { + "system": "http://snomed.info/sct", + "version": "2019-09", + "code": "439740005", + "display": "Postoperative follow-up visit (procedure)" + }, + { + "system": "http://www.ama-assn.org/go/cpt", + "version": "2020", + "code": "99201", + "display": "Office or other outpatient visit for the evaluation and management of a new patient, which requires these 3 key components: A problem focused history; A problem focused examination; Straightforward medical decision making. Counseling and/or coordination of care with other physicians, other qualified health care professionals, or agencies are provided consistent with the nature of the problem(s) and the patient's and/or family's needs. Usually, the presenting problem(s) are self limited or minor. Typically, 10 minutes are spent face-to-face with the patient and/or family." + }, + { + "system": "http://www.ama-assn.org/go/cpt", + "version": "2020", + "code": "99202", + "display": "Office or other outpatient visit for the evaluation and management of a new patient, which requires these 3 key components: An expanded problem focused history; An expanded problem focused examination; Straightforward medical decision making. Counseling and/or coordination of care with other physicians, other qualified health care professionals, or agencies are provided consistent with the nature of the problem(s) and the patient's and/or family's needs. Usually, the presenting problem(s) are of low to moderate severity. Typically, 20 minutes are spent face-to-face with the patient and/or family." + }, + { + "system": "http://www.ama-assn.org/go/cpt", + "version": "2020", + "code": "99203", + "display": "Office or other outpatient visit for the evaluation and management of a new patient, which requires these 3 key components: A detailed history; A detailed examination; Medical decision making of low complexity. Counseling and/or coordination of care with other physicians, other qualified health care professionals, or agencies are provided consistent with the nature of the problem(s) and the patient's and/or family's needs. Usually, the presenting problem(s) are of moderate severity. Typically, 30 minutes are spent face-to-face with the patient and/or family." + }, + { + "system": "http://www.ama-assn.org/go/cpt", + "version": "2020", + "code": "99204", + "display": "Office or other outpatient visit for the evaluation and management of a new patient, which requires these 3 key components: A comprehensive history; A comprehensive examination; Medical decision making of moderate complexity. Counseling and/or coordination of care with other physicians, other qualified health care professionals, or agencies are provided consistent with the nature of the problem(s) and the patient's and/or family's needs. Usually, the presenting problem(s) are of moderate to high severity. Typically, 45 minutes are spent face-to-face with the patient and/or family." + }, + { + "system": "http://www.ama-assn.org/go/cpt", + "version": "2020", + "code": "99205", + "display": "Office or other outpatient visit for the evaluation and management of a new patient, which requires these 3 key components: A comprehensive history; A comprehensive examination; Medical decision making of high complexity. Counseling and/or coordination of care with other physicians, other qualified health care professionals, or agencies are provided consistent with the nature of the problem(s) and the patient's and/or family's needs. Usually, the presenting problem(s) are of moderate to high severity. Typically, 60 minutes are spent face-to-face with the patient and/or family." + }, + { + "system": "http://www.ama-assn.org/go/cpt", + "version": "2020", + "code": "99212", + "display": "Office or other outpatient visit for the evaluation and management of an established patient, which requires at least 2 of these 3 key components: A problem focused history; A problem focused examination; Straightforward medical decision making. Counseling and/or coordination of care with other physicians, other qualified health care professionals, or agencies are provided consistent with the nature of the problem(s) and the patient's and/or family's needs. Usually, the presenting problem(s) are self limited or minor. Typically, 10 minutes are spent face-to-face with the patient and/or family." + }, + { + "system": "http://www.ama-assn.org/go/cpt", + "version": "2020", + "code": "99213", + "display": "Office or other outpatient visit for the evaluation and management of an established patient, which requires at least 2 of these 3 key components: An expanded problem focused history; An expanded problem focused examination; Medical decision making of low complexity. Counseling and coordination of care with other physicians, other qualified health care professionals, or agencies are provided consistent with the nature of the problem(s) and the patient's and/or family's needs. Usually, the presenting problem(s) are of low to moderate severity. Typically, 15 minutes are spent face-to-face with the patient and/or family." + }, + { + "system": "http://www.ama-assn.org/go/cpt", + "version": "2020", + "code": "99214", + "display": "Office or other outpatient visit for the evaluation and management of an established patient, which requires at least 2 of these 3 key components: A detailed history; A detailed examination; Medical decision making of moderate complexity. Counseling and/or coordination of care with other physicians, other qualified health care professionals, or agencies are provided consistent with the nature of the problem(s) and the patient's and/or family's needs. Usually, the presenting problem(s) are of moderate to high severity. Typically, 25 minutes are spent face-to-face with the patient and/or family." + }, + { + "system": "http://www.ama-assn.org/go/cpt", + "version": "2020", + "code": "99215", + "display": "Office or other outpatient visit for the evaluation and management of an established patient, which requires at least 2 of these 3 key components: A comprehensive history; A comprehensive examination; Medical decision making of high complexity. Counseling and/or coordination of care with other physicians, other qualified health care professionals, or agencies are provided consistent with the nature of the problem(s) and the patient's and/or family's needs. Usually, the presenting problem(s) are of moderate to high severity. Typically, 40 minutes are spent face-to-face with the patient and/or family." + }, + { + "system": "http://snomed.info/sct", + "version": "2018-03", + "code": "30346009", + "display": "Evaluation and management of established outpatient in office or other outpatient facility (procedure)" + }, + { + "system": "http://snomed.info/sct", + "version": "2018-03", + "code": "37894004", + "display": "Evaluation and management of new outpatient in office or other outpatient facility (procedure)" + }, + { + "system": "http://snomed.info/sct", + "version": "2019-09", + "code": "185463005", + "display": "Visit out of hours (procedure)" + }, + { + "system": "http://snomed.info/sct", + "version": "2019-09", + "code": "185464004", + "display": "Out of hours visit - not night visit (procedure)" + }, + { + "system": "http://snomed.info/sct", + "version": "2019-09", + "code": "185465003", + "display": "Weekend visit (procedure)" + }, + { + "system": "http://snomed.info/sct", + "version": "2019-09", + "code": "3391000175108", + "display": "Office visit for pediatric care and assessment (procedure)" + }, + { + "system": "http://snomed.info/sct", + "version": "2019-09", + "code": "439740005", + "display": "Postoperative follow-up visit (procedure)" + }, + { + "system": "http://www.ama-assn.org/go/cpt", + "version": "2020", + "code": "99201", + "display": "Office or other outpatient visit for the evaluation and management of a new patient, which requires these 3 key components: A problem focused history; A problem focused examination; Straightforward medical decision making. Counseling and/or coordination of care with other physicians, other qualified health care professionals, or agencies are provided consistent with the nature of the problem(s) and the patient's and/or family's needs. Usually, the presenting problem(s) are self limited or minor. Typically, 10 minutes are spent face-to-face with the patient and/or family." + }, + { + "system": "http://www.ama-assn.org/go/cpt", + "version": "2020", + "code": "99202", + "display": "Office or other outpatient visit for the evaluation and management of a new patient, which requires these 3 key components: An expanded problem focused history; An expanded problem focused examination; Straightforward medical decision making. Counseling and/or coordination of care with other physicians, other qualified health care professionals, or agencies are provided consistent with the nature of the problem(s) and the patient's and/or family's needs. Usually, the presenting problem(s) are of low to moderate severity. Typically, 20 minutes are spent face-to-face with the patient and/or family." + }, + { + "system": "http://www.ama-assn.org/go/cpt", + "version": "2020", + "code": "99203", + "display": "Office or other outpatient visit for the evaluation and management of a new patient, which requires these 3 key components: A detailed history; A detailed examination; Medical decision making of low complexity. Counseling and/or coordination of care with other physicians, other qualified health care professionals, or agencies are provided consistent with the nature of the problem(s) and the patient's and/or family's needs. Usually, the presenting problem(s) are of moderate severity. Typically, 30 minutes are spent face-to-face with the patient and/or family." + }, + { + "system": "http://www.ama-assn.org/go/cpt", + "version": "2020", + "code": "99204", + "display": "Office or other outpatient visit for the evaluation and management of a new patient, which requires these 3 key components: A comprehensive history; A comprehensive examination; Medical decision making of moderate complexity. Counseling and/or coordination of care with other physicians, other qualified health care professionals, or agencies are provided consistent with the nature of the problem(s) and the patient's and/or family's needs. Usually, the presenting problem(s) are of moderate to high severity. Typically, 45 minutes are spent face-to-face with the patient and/or family." + }, + { + "system": "http://www.ama-assn.org/go/cpt", + "version": "2020", + "code": "99205", + "display": "Office or other outpatient visit for the evaluation and management of a new patient, which requires these 3 key components: A comprehensive history; A comprehensive examination; Medical decision making of high complexity. Counseling and/or coordination of care with other physicians, other qualified health care professionals, or agencies are provided consistent with the nature of the problem(s) and the patient's and/or family's needs. Usually, the presenting problem(s) are of moderate to high severity. Typically, 60 minutes are spent face-to-face with the patient and/or family." + }, + { + "system": "http://www.ama-assn.org/go/cpt", + "version": "2020", + "code": "99212", + "display": "Office or other outpatient visit for the evaluation and management of an established patient, which requires at least 2 of these 3 key components: A problem focused history; A problem focused examination; Straightforward medical decision making. Counseling and/or coordination of care with other physicians, other qualified health care professionals, or agencies are provided consistent with the nature of the problem(s) and the patient's and/or family's needs. Usually, the presenting problem(s) are self limited or minor. Typically, 10 minutes are spent face-to-face with the patient and/or family." + }, + { + "system": "http://www.ama-assn.org/go/cpt", + "version": "2020", + "code": "99213", + "display": "Office or other outpatient visit for the evaluation and management of an established patient, which requires at least 2 of these 3 key components: An expanded problem focused history; An expanded problem focused examination; Medical decision making of low complexity. Counseling and coordination of care with other physicians, other qualified health care professionals, or agencies are provided consistent with the nature of the problem(s) and the patient's and/or family's needs. Usually, the presenting problem(s) are of low to moderate severity. Typically, 15 minutes are spent face-to-face with the patient and/or family." + }, + { + "system": "http://www.ama-assn.org/go/cpt", + "version": "2020", + "code": "99214", + "display": "Office or other outpatient visit for the evaluation and management of an established patient, which requires at least 2 of these 3 key components: A detailed history; A detailed examination; Medical decision making of moderate complexity. Counseling and/or coordination of care with other physicians, other qualified health care professionals, or agencies are provided consistent with the nature of the problem(s) and the patient's and/or family's needs. Usually, the presenting problem(s) are of moderate to high severity. Typically, 25 minutes are spent face-to-face with the patient and/or family." + }, + { + "system": "http://www.ama-assn.org/go/cpt", + "version": "2020", + "code": "99215", + "display": "Office or other outpatient visit for the evaluation and management of an established patient, which requires at least 2 of these 3 key components: A comprehensive history; A comprehensive examination; Medical decision making of high complexity. Counseling and/or coordination of care with other physicians, other qualified health care professionals, or agencies are provided consistent with the nature of the problem(s) and the patient's and/or family's needs. Usually, the presenting problem(s) are of moderate to high severity. Typically, 40 minutes are spent face-to-face with the patient and/or family." + }, + { + "system": "http://snomed.info/sct", + "version": "2018-03", + "code": "30346009", + "display": "Evaluation and management of established outpatient in office or other outpatient facility (procedure)" + }, + { + "system": "http://snomed.info/sct", + "version": "2018-03", + "code": "37894004", + "display": "Evaluation and management of new outpatient in office or other outpatient facility (procedure)" + }, + { + "system": "http://snomed.info/sct", + "version": "2019-09", + "code": "185463005", + "display": "Visit out of hours (procedure)" + }, + { + "system": "http://snomed.info/sct", + "version": "2019-09", + "code": "185464004", + "display": "Out of hours visit - not night visit (procedure)" + }, + { + "system": "http://snomed.info/sct", + "version": "2019-09", + "code": "185465003", + "display": "Weekend visit (procedure)" + }, + { + "system": "http://snomed.info/sct", + "version": "2019-09", + "code": "3391000175108", + "display": "Office visit for pediatric care and assessment (procedure)" + }, + { + "system": "http://snomed.info/sct", + "version": "2019-09", + "code": "439740005", + "display": "Postoperative follow-up visit (procedure)" + }, + { + "system": "http://www.ama-assn.org/go/cpt", + "version": "2020", + "code": "99201", + "display": "Office or other outpatient visit for the evaluation and management of a new patient, which requires these 3 key components: A problem focused history; A problem focused examination; Straightforward medical decision making. Counseling and/or coordination of care with other physicians, other qualified health care professionals, or agencies are provided consistent with the nature of the problem(s) and the patient's and/or family's needs. Usually, the presenting problem(s) are self limited or minor. Typically, 10 minutes are spent face-to-face with the patient and/or family." + }, + { + "system": "http://www.ama-assn.org/go/cpt", + "version": "2020", + "code": "99202", + "display": "Office or other outpatient visit for the evaluation and management of a new patient, which requires these 3 key components: An expanded problem focused history; An expanded problem focused examination; Straightforward medical decision making. Counseling and/or coordination of care with other physicians, other qualified health care professionals, or agencies are provided consistent with the nature of the problem(s) and the patient's and/or family's needs. Usually, the presenting problem(s) are of low to moderate severity. Typically, 20 minutes are spent face-to-face with the patient and/or family." + }, + { + "system": "http://www.ama-assn.org/go/cpt", + "version": "2020", + "code": "99203", + "display": "Office or other outpatient visit for the evaluation and management of a new patient, which requires these 3 key components: A detailed history; A detailed examination; Medical decision making of low complexity. Counseling and/or coordination of care with other physicians, other qualified health care professionals, or agencies are provided consistent with the nature of the problem(s) and the patient's and/or family's needs. Usually, the presenting problem(s) are of moderate severity. Typically, 30 minutes are spent face-to-face with the patient and/or family." + }, + { + "system": "http://www.ama-assn.org/go/cpt", + "version": "2020", + "code": "99204", + "display": "Office or other outpatient visit for the evaluation and management of a new patient, which requires these 3 key components: A comprehensive history; A comprehensive examination; Medical decision making of moderate complexity. Counseling and/or coordination of care with other physicians, other qualified health care professionals, or agencies are provided consistent with the nature of the problem(s) and the patient's and/or family's needs. Usually, the presenting problem(s) are of moderate to high severity. Typically, 45 minutes are spent face-to-face with the patient and/or family." + }, + { + "system": "http://www.ama-assn.org/go/cpt", + "version": "2020", + "code": "99205", + "display": "Office or other outpatient visit for the evaluation and management of a new patient, which requires these 3 key components: A comprehensive history; A comprehensive examination; Medical decision making of high complexity. Counseling and/or coordination of care with other physicians, other qualified health care professionals, or agencies are provided consistent with the nature of the problem(s) and the patient's and/or family's needs. Usually, the presenting problem(s) are of moderate to high severity. Typically, 60 minutes are spent face-to-face with the patient and/or family." + }, + { + "system": "http://www.ama-assn.org/go/cpt", + "version": "2020", + "code": "99212", + "display": "Office or other outpatient visit for the evaluation and management of an established patient, which requires at least 2 of these 3 key components: A problem focused history; A problem focused examination; Straightforward medical decision making. Counseling and/or coordination of care with other physicians, other qualified health care professionals, or agencies are provided consistent with the nature of the problem(s) and the patient's and/or family's needs. Usually, the presenting problem(s) are self limited or minor. Typically, 10 minutes are spent face-to-face with the patient and/or family." + }, + { + "system": "http://www.ama-assn.org/go/cpt", + "version": "2020", + "code": "99213", + "display": "Office or other outpatient visit for the evaluation and management of an established patient, which requires at least 2 of these 3 key components: An expanded problem focused history; An expanded problem focused examination; Medical decision making of low complexity. Counseling and coordination of care with other physicians, other qualified health care professionals, or agencies are provided consistent with the nature of the problem(s) and the patient's and/or family's needs. Usually, the presenting problem(s) are of low to moderate severity. Typically, 15 minutes are spent face-to-face with the patient and/or family." + }, + { + "system": "http://www.ama-assn.org/go/cpt", + "version": "2020", + "code": "99214", + "display": "Office or other outpatient visit for the evaluation and management of an established patient, which requires at least 2 of these 3 key components: A detailed history; A detailed examination; Medical decision making of moderate complexity. Counseling and/or coordination of care with other physicians, other qualified health care professionals, or agencies are provided consistent with the nature of the problem(s) and the patient's and/or family's needs. Usually, the presenting problem(s) are of moderate to high severity. Typically, 25 minutes are spent face-to-face with the patient and/or family." + }, + { + "system": "http://www.ama-assn.org/go/cpt", + "version": "2020", + "code": "99215", + "display": "Office or other outpatient visit for the evaluation and management of an established patient, which requires at least 2 of these 3 key components: A comprehensive history; A comprehensive examination; Medical decision making of high complexity. Counseling and/or coordination of care with other physicians, other qualified health care professionals, or agencies are provided consistent with the nature of the problem(s) and the patient's and/or family's needs. Usually, the presenting problem(s) are of moderate to high severity. Typically, 40 minutes are spent face-to-face with the patient and/or family." + }, + { + "system": "http://snomed.info/sct", + "version": "2018-03", + "code": "30346009", + "display": "Evaluation and management of established outpatient in office or other outpatient facility (procedure)" + }, + { + "system": "http://snomed.info/sct", + "version": "2018-03", + "code": "37894004", + "display": "Evaluation and management of new outpatient in office or other outpatient facility (procedure)" + }, + { + "system": "http://snomed.info/sct", + "version": "2019-09", + "code": "185463005", + "display": "Visit out of hours (procedure)" + }, + { + "system": "http://snomed.info/sct", + "version": "2019-09", + "code": "185464004", + "display": "Out of hours visit - not night visit (procedure)" + }, + { + "system": "http://snomed.info/sct", + "version": "2019-09", + "code": "185465003", + "display": "Weekend visit (procedure)" + }, + { + "system": "http://snomed.info/sct", + "version": "2019-09", + "code": "3391000175108", + "display": "Office visit for pediatric care and assessment (procedure)" + }, + { + "system": "http://snomed.info/sct", + "version": "2019-09", + "code": "439740005", + "display": "Postoperative follow-up visit (procedure)" + }, + { + "system": "http://www.ama-assn.org/go/cpt", + "version": "2020", + "code": "99201", + "display": "Office or other outpatient visit for the evaluation and management of a new patient, which requires these 3 key components: A problem focused history; A problem focused examination; Straightforward medical decision making. Counseling and/or coordination of care with other physicians, other qualified health care professionals, or agencies are provided consistent with the nature of the problem(s) and the patient's and/or family's needs. Usually, the presenting problem(s) are self limited or minor. Typically, 10 minutes are spent face-to-face with the patient and/or family." + }, + { + "system": "http://www.ama-assn.org/go/cpt", + "version": "2020", + "code": "99202", + "display": "Office or other outpatient visit for the evaluation and management of a new patient, which requires these 3 key components: An expanded problem focused history; An expanded problem focused examination; Straightforward medical decision making. Counseling and/or coordination of care with other physicians, other qualified health care professionals, or agencies are provided consistent with the nature of the problem(s) and the patient's and/or family's needs. Usually, the presenting problem(s) are of low to moderate severity. Typically, 20 minutes are spent face-to-face with the patient and/or family." + }, + { + "system": "http://www.ama-assn.org/go/cpt", + "version": "2020", + "code": "99203", + "display": "Office or other outpatient visit for the evaluation and management of a new patient, which requires these 3 key components: A detailed history; A detailed examination; Medical decision making of low complexity. Counseling and/or coordination of care with other physicians, other qualified health care professionals, or agencies are provided consistent with the nature of the problem(s) and the patient's and/or family's needs. Usually, the presenting problem(s) are of moderate severity. Typically, 30 minutes are spent face-to-face with the patient and/or family." + }, + { + "system": "http://www.ama-assn.org/go/cpt", + "version": "2020", + "code": "99204", + "display": "Office or other outpatient visit for the evaluation and management of a new patient, which requires these 3 key components: A comprehensive history; A comprehensive examination; Medical decision making of moderate complexity. Counseling and/or coordination of care with other physicians, other qualified health care professionals, or agencies are provided consistent with the nature of the problem(s) and the patient's and/or family's needs. Usually, the presenting problem(s) are of moderate to high severity. Typically, 45 minutes are spent face-to-face with the patient and/or family." + }, + { + "system": "http://www.ama-assn.org/go/cpt", + "version": "2020", + "code": "99205", + "display": "Office or other outpatient visit for the evaluation and management of a new patient, which requires these 3 key components: A comprehensive history; A comprehensive examination; Medical decision making of high complexity. Counseling and/or coordination of care with other physicians, other qualified health care professionals, or agencies are provided consistent with the nature of the problem(s) and the patient's and/or family's needs. Usually, the presenting problem(s) are of moderate to high severity. Typically, 60 minutes are spent face-to-face with the patient and/or family." + }, + { + "system": "http://www.ama-assn.org/go/cpt", + "version": "2020", + "code": "99212", + "display": "Office or other outpatient visit for the evaluation and management of an established patient, which requires at least 2 of these 3 key components: A problem focused history; A problem focused examination; Straightforward medical decision making. Counseling and/or coordination of care with other physicians, other qualified health care professionals, or agencies are provided consistent with the nature of the problem(s) and the patient's and/or family's needs. Usually, the presenting problem(s) are self limited or minor. Typically, 10 minutes are spent face-to-face with the patient and/or family." + }, + { + "system": "http://www.ama-assn.org/go/cpt", + "version": "2020", + "code": "99213", + "display": "Office or other outpatient visit for the evaluation and management of an established patient, which requires at least 2 of these 3 key components: An expanded problem focused history; An expanded problem focused examination; Medical decision making of low complexity. Counseling and coordination of care with other physicians, other qualified health care professionals, or agencies are provided consistent with the nature of the problem(s) and the patient's and/or family's needs. Usually, the presenting problem(s) are of low to moderate severity. Typically, 15 minutes are spent face-to-face with the patient and/or family." + }, + { + "system": "http://www.ama-assn.org/go/cpt", + "version": "2020", + "code": "99214", + "display": "Office or other outpatient visit for the evaluation and management of an established patient, which requires at least 2 of these 3 key components: A detailed history; A detailed examination; Medical decision making of moderate complexity. Counseling and/or coordination of care with other physicians, other qualified health care professionals, or agencies are provided consistent with the nature of the problem(s) and the patient's and/or family's needs. Usually, the presenting problem(s) are of moderate to high severity. Typically, 25 minutes are spent face-to-face with the patient and/or family." + }, + { + "system": "http://www.ama-assn.org/go/cpt", + "version": "2020", + "code": "99215", + "display": "Office or other outpatient visit for the evaluation and management of an established patient, which requires at least 2 of these 3 key components: A comprehensive history; A comprehensive examination; Medical decision making of high complexity. Counseling and/or coordination of care with other physicians, other qualified health care professionals, or agencies are provided consistent with the nature of the problem(s) and the patient's and/or family's needs. Usually, the presenting problem(s) are of moderate to high severity. Typically, 40 minutes are spent face-to-face with the patient and/or family." + }, + { + "system": "http://snomed.info/sct", + "version": "2018-03", + "code": "30346009", + "display": "Evaluation and management of established outpatient in office or other outpatient facility (procedure)" + }, + { + "system": "http://snomed.info/sct", + "version": "2018-03", + "code": "37894004", + "display": "Evaluation and management of new outpatient in office or other outpatient facility (procedure)" + }, + { + "system": "http://snomed.info/sct", + "version": "2019-09", + "code": "185463005", + "display": "Visit out of hours (procedure)" + }, + { + "system": "http://snomed.info/sct", + "version": "2019-09", + "code": "185464004", + "display": "Out of hours visit - not night visit (procedure)" + }, + { + "system": "http://snomed.info/sct", + "version": "2019-09", + "code": "185465003", + "display": "Weekend visit (procedure)" + }, + { + "system": "http://snomed.info/sct", + "version": "2019-09", + "code": "3391000175108", + "display": "Office visit for pediatric care and assessment (procedure)" + }, + { + "system": "http://snomed.info/sct", + "version": "2019-09", + "code": "439740005", + "display": "Postoperative follow-up visit (procedure)" + }, + { + "system": "http://www.ama-assn.org/go/cpt", + "version": "2020", + "code": "99201", + "display": "Office or other outpatient visit for the evaluation and management of a new patient, which requires these 3 key components: A problem focused history; A problem focused examination; Straightforward medical decision making. Counseling and/or coordination of care with other physicians, other qualified health care professionals, or agencies are provided consistent with the nature of the problem(s) and the patient's and/or family's needs. Usually, the presenting problem(s) are self limited or minor. Typically, 10 minutes are spent face-to-face with the patient and/or family." + }, + { + "system": "http://www.ama-assn.org/go/cpt", + "version": "2020", + "code": "99202", + "display": "Office or other outpatient visit for the evaluation and management of a new patient, which requires these 3 key components: An expanded problem focused history; An expanded problem focused examination; Straightforward medical decision making. Counseling and/or coordination of care with other physicians, other qualified health care professionals, or agencies are provided consistent with the nature of the problem(s) and the patient's and/or family's needs. Usually, the presenting problem(s) are of low to moderate severity. Typically, 20 minutes are spent face-to-face with the patient and/or family." + }, + { + "system": "http://www.ama-assn.org/go/cpt", + "version": "2020", + "code": "99203", + "display": "Office or other outpatient visit for the evaluation and management of a new patient, which requires these 3 key components: A detailed history; A detailed examination; Medical decision making of low complexity. Counseling and/or coordination of care with other physicians, other qualified health care professionals, or agencies are provided consistent with the nature of the problem(s) and the patient's and/or family's needs. Usually, the presenting problem(s) are of moderate severity. Typically, 30 minutes are spent face-to-face with the patient and/or family." + }, + { + "system": "http://www.ama-assn.org/go/cpt", + "version": "2020", + "code": "99204", + "display": "Office or other outpatient visit for the evaluation and management of a new patient, which requires these 3 key components: A comprehensive history; A comprehensive examination; Medical decision making of moderate complexity. Counseling and/or coordination of care with other physicians, other qualified health care professionals, or agencies are provided consistent with the nature of the problem(s) and the patient's and/or family's needs. Usually, the presenting problem(s) are of moderate to high severity. Typically, 45 minutes are spent face-to-face with the patient and/or family." + }, + { + "system": "http://www.ama-assn.org/go/cpt", + "version": "2020", + "code": "99205", + "display": "Office or other outpatient visit for the evaluation and management of a new patient, which requires these 3 key components: A comprehensive history; A comprehensive examination; Medical decision making of high complexity. Counseling and/or coordination of care with other physicians, other qualified health care professionals, or agencies are provided consistent with the nature of the problem(s) and the patient's and/or family's needs. Usually, the presenting problem(s) are of moderate to high severity. Typically, 60 minutes are spent face-to-face with the patient and/or family." + }, + { + "system": "http://www.ama-assn.org/go/cpt", + "version": "2020", + "code": "99212", + "display": "Office or other outpatient visit for the evaluation and management of an established patient, which requires at least 2 of these 3 key components: A problem focused history; A problem focused examination; Straightforward medical decision making. Counseling and/or coordination of care with other physicians, other qualified health care professionals, or agencies are provided consistent with the nature of the problem(s) and the patient's and/or family's needs. Usually, the presenting problem(s) are self limited or minor. Typically, 10 minutes are spent face-to-face with the patient and/or family." + }, + { + "system": "http://www.ama-assn.org/go/cpt", + "version": "2020", + "code": "99213", + "display": "Office or other outpatient visit for the evaluation and management of an established patient, which requires at least 2 of these 3 key components: An expanded problem focused history; An expanded problem focused examination; Medical decision making of low complexity. Counseling and coordination of care with other physicians, other qualified health care professionals, or agencies are provided consistent with the nature of the problem(s) and the patient's and/or family's needs. Usually, the presenting problem(s) are of low to moderate severity. Typically, 15 minutes are spent face-to-face with the patient and/or family." + }, + { + "system": "http://www.ama-assn.org/go/cpt", + "version": "2020", + "code": "99214", + "display": "Office or other outpatient visit for the evaluation and management of an established patient, which requires at least 2 of these 3 key components: A detailed history; A detailed examination; Medical decision making of moderate complexity. Counseling and/or coordination of care with other physicians, other qualified health care professionals, or agencies are provided consistent with the nature of the problem(s) and the patient's and/or family's needs. Usually, the presenting problem(s) are of moderate to high severity. Typically, 25 minutes are spent face-to-face with the patient and/or family." + }, + { + "system": "http://www.ama-assn.org/go/cpt", + "version": "2020", + "code": "99215", + "display": "Office or other outpatient visit for the evaluation and management of an established patient, which requires at least 2 of these 3 key components: A comprehensive history; A comprehensive examination; Medical decision making of high complexity. Counseling and/or coordination of care with other physicians, other qualified health care professionals, or agencies are provided consistent with the nature of the problem(s) and the patient's and/or family's needs. Usually, the presenting problem(s) are of moderate to high severity. Typically, 40 minutes are spent face-to-face with the patient and/or family." + }, + { + "system": "http://snomed.info/sct", + "version": "2018-03", + "code": "30346009", + "display": "Evaluation and management of established outpatient in office or other outpatient facility (procedure)" + }, + { + "system": "http://snomed.info/sct", + "version": "2018-03", + "code": "37894004", + "display": "Evaluation and management of new outpatient in office or other outpatient facility (procedure)" + }, + { + "system": "http://snomed.info/sct", + "version": "2019-09", + "code": "185463005", + "display": "Visit out of hours (procedure)" + }, + { + "system": "http://snomed.info/sct", + "version": "2019-09", + "code": "185464004", + "display": "Out of hours visit - not night visit (procedure)" + }, + { + "system": "http://snomed.info/sct", + "version": "2019-09", + "code": "185465003", + "display": "Weekend visit (procedure)" + }, + { + "system": "http://snomed.info/sct", + "version": "2019-09", + "code": "3391000175108", + "display": "Office visit for pediatric care and assessment (procedure)" + }, + { + "system": "http://snomed.info/sct", + "version": "2019-09", + "code": "439740005", + "display": "Postoperative follow-up visit (procedure)" + }, + { + "system": "http://www.ama-assn.org/go/cpt", + "version": "2020", + "code": "99201", + "display": "Office or other outpatient visit for the evaluation and management of a new patient, which requires these 3 key components: A problem focused history; A problem focused examination; Straightforward medical decision making. Counseling and/or coordination of care with other physicians, other qualified health care professionals, or agencies are provided consistent with the nature of the problem(s) and the patient's and/or family's needs. Usually, the presenting problem(s) are self limited or minor. Typically, 10 minutes are spent face-to-face with the patient and/or family." + }, + { + "system": "http://www.ama-assn.org/go/cpt", + "version": "2020", + "code": "99202", + "display": "Office or other outpatient visit for the evaluation and management of a new patient, which requires these 3 key components: An expanded problem focused history; An expanded problem focused examination; Straightforward medical decision making. Counseling and/or coordination of care with other physicians, other qualified health care professionals, or agencies are provided consistent with the nature of the problem(s) and the patient's and/or family's needs. Usually, the presenting problem(s) are of low to moderate severity. Typically, 20 minutes are spent face-to-face with the patient and/or family." + }, + { + "system": "http://www.ama-assn.org/go/cpt", + "version": "2020", + "code": "99203", + "display": "Office or other outpatient visit for the evaluation and management of a new patient, which requires these 3 key components: A detailed history; A detailed examination; Medical decision making of low complexity. Counseling and/or coordination of care with other physicians, other qualified health care professionals, or agencies are provided consistent with the nature of the problem(s) and the patient's and/or family's needs. Usually, the presenting problem(s) are of moderate severity. Typically, 30 minutes are spent face-to-face with the patient and/or family." + }, + { + "system": "http://www.ama-assn.org/go/cpt", + "version": "2020", + "code": "99204", + "display": "Office or other outpatient visit for the evaluation and management of a new patient, which requires these 3 key components: A comprehensive history; A comprehensive examination; Medical decision making of moderate complexity. Counseling and/or coordination of care with other physicians, other qualified health care professionals, or agencies are provided consistent with the nature of the problem(s) and the patient's and/or family's needs. Usually, the presenting problem(s) are of moderate to high severity. Typically, 45 minutes are spent face-to-face with the patient and/or family." + }, + { + "system": "http://www.ama-assn.org/go/cpt", + "version": "2020", + "code": "99205", + "display": "Office or other outpatient visit for the evaluation and management of a new patient, which requires these 3 key components: A comprehensive history; A comprehensive examination; Medical decision making of high complexity. Counseling and/or coordination of care with other physicians, other qualified health care professionals, or agencies are provided consistent with the nature of the problem(s) and the patient's and/or family's needs. Usually, the presenting problem(s) are of moderate to high severity. Typically, 60 minutes are spent face-to-face with the patient and/or family." + }, + { + "system": "http://www.ama-assn.org/go/cpt", + "version": "2020", + "code": "99212", + "display": "Office or other outpatient visit for the evaluation and management of an established patient, which requires at least 2 of these 3 key components: A problem focused history; A problem focused examination; Straightforward medical decision making. Counseling and/or coordination of care with other physicians, other qualified health care professionals, or agencies are provided consistent with the nature of the problem(s) and the patient's and/or family's needs. Usually, the presenting problem(s) are self limited or minor. Typically, 10 minutes are spent face-to-face with the patient and/or family." + }, + { + "system": "http://www.ama-assn.org/go/cpt", + "version": "2020", + "code": "99213", + "display": "Office or other outpatient visit for the evaluation and management of an established patient, which requires at least 2 of these 3 key components: An expanded problem focused history; An expanded problem focused examination; Medical decision making of low complexity. Counseling and coordination of care with other physicians, other qualified health care professionals, or agencies are provided consistent with the nature of the problem(s) and the patient's and/or family's needs. Usually, the presenting problem(s) are of low to moderate severity. Typically, 15 minutes are spent face-to-face with the patient and/or family." + }, + { + "system": "http://www.ama-assn.org/go/cpt", + "version": "2020", + "code": "99214", + "display": "Office or other outpatient visit for the evaluation and management of an established patient, which requires at least 2 of these 3 key components: A detailed history; A detailed examination; Medical decision making of moderate complexity. Counseling and/or coordination of care with other physicians, other qualified health care professionals, or agencies are provided consistent with the nature of the problem(s) and the patient's and/or family's needs. Usually, the presenting problem(s) are of moderate to high severity. Typically, 25 minutes are spent face-to-face with the patient and/or family." + }, + { + "system": "http://www.ama-assn.org/go/cpt", + "version": "2020", + "code": "99215", + "display": "Office or other outpatient visit for the evaluation and management of an established patient, which requires at least 2 of these 3 key components: A comprehensive history; A comprehensive examination; Medical decision making of high complexity. Counseling and/or coordination of care with other physicians, other qualified health care professionals, or agencies are provided consistent with the nature of the problem(s) and the patient's and/or family's needs. Usually, the presenting problem(s) are of moderate to high severity. Typically, 40 minutes are spent face-to-face with the patient and/or family." + }, + { + "system": "http://snomed.info/sct", + "version": "2018-03", + "code": "30346009", + "display": "Evaluation and management of established outpatient in office or other outpatient facility (procedure)" + }, + { + "system": "http://snomed.info/sct", + "version": "2018-03", + "code": "37894004", + "display": "Evaluation and management of new outpatient in office or other outpatient facility (procedure)" + }, + { + "system": "http://snomed.info/sct", + "version": "2019-09", + "code": "185463005", + "display": "Visit out of hours (procedure)" + }, + { + "system": "http://snomed.info/sct", + "version": "2019-09", + "code": "185464004", + "display": "Out of hours visit - not night visit (procedure)" + }, + { + "system": "http://snomed.info/sct", + "version": "2019-09", + "code": "185465003", + "display": "Weekend visit (procedure)" + }, + { + "system": "http://snomed.info/sct", + "version": "2019-09", + "code": "3391000175108", + "display": "Office visit for pediatric care and assessment (procedure)" + }, + { + "system": "http://snomed.info/sct", + "version": "2019-09", + "code": "439740005", + "display": "Postoperative follow-up visit (procedure)" + }, + { + "system": "http://www.ama-assn.org/go/cpt", + "version": "2020", + "code": "99201", + "display": "Office or other outpatient visit for the evaluation and management of a new patient, which requires these 3 key components: A problem focused history; A problem focused examination; Straightforward medical decision making. Counseling and/or coordination of care with other physicians, other qualified health care professionals, or agencies are provided consistent with the nature of the problem(s) and the patient's and/or family's needs. Usually, the presenting problem(s) are self limited or minor. Typically, 10 minutes are spent face-to-face with the patient and/or family." + }, + { + "system": "http://www.ama-assn.org/go/cpt", + "version": "2020", + "code": "99202", + "display": "Office or other outpatient visit for the evaluation and management of a new patient, which requires these 3 key components: An expanded problem focused history; An expanded problem focused examination; Straightforward medical decision making. Counseling and/or coordination of care with other physicians, other qualified health care professionals, or agencies are provided consistent with the nature of the problem(s) and the patient's and/or family's needs. Usually, the presenting problem(s) are of low to moderate severity. Typically, 20 minutes are spent face-to-face with the patient and/or family." + }, + { + "system": "http://www.ama-assn.org/go/cpt", + "version": "2020", + "code": "99203", + "display": "Office or other outpatient visit for the evaluation and management of a new patient, which requires these 3 key components: A detailed history; A detailed examination; Medical decision making of low complexity. Counseling and/or coordination of care with other physicians, other qualified health care professionals, or agencies are provided consistent with the nature of the problem(s) and the patient's and/or family's needs. Usually, the presenting problem(s) are of moderate severity. Typically, 30 minutes are spent face-to-face with the patient and/or family." + }, + { + "system": "http://www.ama-assn.org/go/cpt", + "version": "2020", + "code": "99204", + "display": "Office or other outpatient visit for the evaluation and management of a new patient, which requires these 3 key components: A comprehensive history; A comprehensive examination; Medical decision making of moderate complexity. Counseling and/or coordination of care with other physicians, other qualified health care professionals, or agencies are provided consistent with the nature of the problem(s) and the patient's and/or family's needs. Usually, the presenting problem(s) are of moderate to high severity. Typically, 45 minutes are spent face-to-face with the patient and/or family." + }, + { + "system": "http://www.ama-assn.org/go/cpt", + "version": "2020", + "code": "99205", + "display": "Office or other outpatient visit for the evaluation and management of a new patient, which requires these 3 key components: A comprehensive history; A comprehensive examination; Medical decision making of high complexity. Counseling and/or coordination of care with other physicians, other qualified health care professionals, or agencies are provided consistent with the nature of the problem(s) and the patient's and/or family's needs. Usually, the presenting problem(s) are of moderate to high severity. Typically, 60 minutes are spent face-to-face with the patient and/or family." + }, + { + "system": "http://www.ama-assn.org/go/cpt", + "version": "2020", + "code": "99212", + "display": "Office or other outpatient visit for the evaluation and management of an established patient, which requires at least 2 of these 3 key components: A problem focused history; A problem focused examination; Straightforward medical decision making. Counseling and/or coordination of care with other physicians, other qualified health care professionals, or agencies are provided consistent with the nature of the problem(s) and the patient's and/or family's needs. Usually, the presenting problem(s) are self limited or minor. Typically, 10 minutes are spent face-to-face with the patient and/or family." + }, + { + "system": "http://www.ama-assn.org/go/cpt", + "version": "2020", + "code": "99213", + "display": "Office or other outpatient visit for the evaluation and management of an established patient, which requires at least 2 of these 3 key components: An expanded problem focused history; An expanded problem focused examination; Medical decision making of low complexity. Counseling and coordination of care with other physicians, other qualified health care professionals, or agencies are provided consistent with the nature of the problem(s) and the patient's and/or family's needs. Usually, the presenting problem(s) are of low to moderate severity. Typically, 15 minutes are spent face-to-face with the patient and/or family." + }, + { + "system": "http://www.ama-assn.org/go/cpt", + "version": "2020", + "code": "99214", + "display": "Office or other outpatient visit for the evaluation and management of an established patient, which requires at least 2 of these 3 key components: A detailed history; A detailed examination; Medical decision making of moderate complexity. Counseling and/or coordination of care with other physicians, other qualified health care professionals, or agencies are provided consistent with the nature of the problem(s) and the patient's and/or family's needs. Usually, the presenting problem(s) are of moderate to high severity. Typically, 25 minutes are spent face-to-face with the patient and/or family." + }, + { + "system": "http://www.ama-assn.org/go/cpt", + "version": "2020", + "code": "99215", + "display": "Office or other outpatient visit for the evaluation and management of an established patient, which requires at least 2 of these 3 key components: A comprehensive history; A comprehensive examination; Medical decision making of high complexity. Counseling and/or coordination of care with other physicians, other qualified health care professionals, or agencies are provided consistent with the nature of the problem(s) and the patient's and/or family's needs. Usually, the presenting problem(s) are of moderate to high severity. Typically, 40 minutes are spent face-to-face with the patient and/or family." + }, + { + "system": "http://snomed.info/sct", + "version": "2018-03", + "code": "30346009", + "display": "Evaluation and management of established outpatient in office or other outpatient facility (procedure)" + }, + { + "system": "http://snomed.info/sct", + "version": "2018-03", + "code": "37894004", + "display": "Evaluation and management of new outpatient in office or other outpatient facility (procedure)" + }, + { + "system": "http://snomed.info/sct", + "version": "2019-09", + "code": "185463005", + "display": "Visit out of hours (procedure)" + }, + { + "system": "http://snomed.info/sct", + "version": "2019-09", + "code": "185464004", + "display": "Out of hours visit - not night visit (procedure)" + }, + { + "system": "http://snomed.info/sct", + "version": "2019-09", + "code": "185465003", + "display": "Weekend visit (procedure)" + }, + { + "system": "http://snomed.info/sct", + "version": "2019-09", + "code": "3391000175108", + "display": "Office visit for pediatric care and assessment (procedure)" + }, + { + "system": "http://snomed.info/sct", + "version": "2019-09", + "code": "439740005", + "display": "Postoperative follow-up visit (procedure)" + }, + { + "system": "http://www.ama-assn.org/go/cpt", + "version": "2020", + "code": "99201", + "display": "Office or other outpatient visit for the evaluation and management of a new patient, which requires these 3 key components: A problem focused history; A problem focused examination; Straightforward medical decision making. Counseling and/or coordination of care with other physicians, other qualified health care professionals, or agencies are provided consistent with the nature of the problem(s) and the patient's and/or family's needs. Usually, the presenting problem(s) are self limited or minor. Typically, 10 minutes are spent face-to-face with the patient and/or family." + }, + { + "system": "http://www.ama-assn.org/go/cpt", + "version": "2020", + "code": "99202", + "display": "Office or other outpatient visit for the evaluation and management of a new patient, which requires these 3 key components: An expanded problem focused history; An expanded problem focused examination; Straightforward medical decision making. Counseling and/or coordination of care with other physicians, other qualified health care professionals, or agencies are provided consistent with the nature of the problem(s) and the patient's and/or family's needs. Usually, the presenting problem(s) are of low to moderate severity. Typically, 20 minutes are spent face-to-face with the patient and/or family." + }, + { + "system": "http://www.ama-assn.org/go/cpt", + "version": "2020", + "code": "99203", + "display": "Office or other outpatient visit for the evaluation and management of a new patient, which requires these 3 key components: A detailed history; A detailed examination; Medical decision making of low complexity. Counseling and/or coordination of care with other physicians, other qualified health care professionals, or agencies are provided consistent with the nature of the problem(s) and the patient's and/or family's needs. Usually, the presenting problem(s) are of moderate severity. Typically, 30 minutes are spent face-to-face with the patient and/or family." + }, + { + "system": "http://www.ama-assn.org/go/cpt", + "version": "2020", + "code": "99204", + "display": "Office or other outpatient visit for the evaluation and management of a new patient, which requires these 3 key components: A comprehensive history; A comprehensive examination; Medical decision making of moderate complexity. Counseling and/or coordination of care with other physicians, other qualified health care professionals, or agencies are provided consistent with the nature of the problem(s) and the patient's and/or family's needs. Usually, the presenting problem(s) are of moderate to high severity. Typically, 45 minutes are spent face-to-face with the patient and/or family." + }, + { + "system": "http://www.ama-assn.org/go/cpt", + "version": "2020", + "code": "99205", + "display": "Office or other outpatient visit for the evaluation and management of a new patient, which requires these 3 key components: A comprehensive history; A comprehensive examination; Medical decision making of high complexity. Counseling and/or coordination of care with other physicians, other qualified health care professionals, or agencies are provided consistent with the nature of the problem(s) and the patient's and/or family's needs. Usually, the presenting problem(s) are of moderate to high severity. Typically, 60 minutes are spent face-to-face with the patient and/or family." + }, + { + "system": "http://www.ama-assn.org/go/cpt", + "version": "2020", + "code": "99212", + "display": "Office or other outpatient visit for the evaluation and management of an established patient, which requires at least 2 of these 3 key components: A problem focused history; A problem focused examination; Straightforward medical decision making. Counseling and/or coordination of care with other physicians, other qualified health care professionals, or agencies are provided consistent with the nature of the problem(s) and the patient's and/or family's needs. Usually, the presenting problem(s) are self limited or minor. Typically, 10 minutes are spent face-to-face with the patient and/or family." + }, + { + "system": "http://www.ama-assn.org/go/cpt", + "version": "2020", + "code": "99213", + "display": "Office or other outpatient visit for the evaluation and management of an established patient, which requires at least 2 of these 3 key components: An expanded problem focused history; An expanded problem focused examination; Medical decision making of low complexity. Counseling and coordination of care with other physicians, other qualified health care professionals, or agencies are provided consistent with the nature of the problem(s) and the patient's and/or family's needs. Usually, the presenting problem(s) are of low to moderate severity. Typically, 15 minutes are spent face-to-face with the patient and/or family." + }, + { + "system": "http://www.ama-assn.org/go/cpt", + "version": "2020", + "code": "99214", + "display": "Office or other outpatient visit for the evaluation and management of an established patient, which requires at least 2 of these 3 key components: A detailed history; A detailed examination; Medical decision making of moderate complexity. Counseling and/or coordination of care with other physicians, other qualified health care professionals, or agencies are provided consistent with the nature of the problem(s) and the patient's and/or family's needs. Usually, the presenting problem(s) are of moderate to high severity. Typically, 25 minutes are spent face-to-face with the patient and/or family." + }, + { + "system": "http://www.ama-assn.org/go/cpt", + "version": "2020", + "code": "99215", + "display": "Office or other outpatient visit for the evaluation and management of an established patient, which requires at least 2 of these 3 key components: A comprehensive history; A comprehensive examination; Medical decision making of high complexity. Counseling and/or coordination of care with other physicians, other qualified health care professionals, or agencies are provided consistent with the nature of the problem(s) and the patient's and/or family's needs. Usually, the presenting problem(s) are of moderate to high severity. Typically, 40 minutes are spent face-to-face with the patient and/or family." + }, + { + "system": "http://snomed.info/sct", + "version": "2018-03", + "code": "30346009", + "display": "Evaluation and management of established outpatient in office or other outpatient facility (procedure)" + }, + { + "system": "http://snomed.info/sct", + "version": "2018-03", + "code": "37894004", + "display": "Evaluation and management of new outpatient in office or other outpatient facility (procedure)" + }, + { + "system": "http://snomed.info/sct", + "version": "2019-09", + "code": "185463005", + "display": "Visit out of hours (procedure)" + }, + { + "system": "http://snomed.info/sct", + "version": "2019-09", + "code": "185464004", + "display": "Out of hours visit - not night visit (procedure)" + }, + { + "system": "http://snomed.info/sct", + "version": "2019-09", + "code": "185465003", + "display": "Weekend visit (procedure)" + }, + { + "system": "http://snomed.info/sct", + "version": "2019-09", + "code": "3391000175108", + "display": "Office visit for pediatric care and assessment (procedure)" + }, + { + "system": "http://snomed.info/sct", + "version": "2019-09", + "code": "439740005", + "display": "Postoperative follow-up visit (procedure)" + }, + { + "system": "http://www.ama-assn.org/go/cpt", + "version": "2020", + "code": "99201", + "display": "Office or other outpatient visit for the evaluation and management of a new patient, which requires these 3 key components: A problem focused history; A problem focused examination; Straightforward medical decision making. Counseling and/or coordination of care with other physicians, other qualified health care professionals, or agencies are provided consistent with the nature of the problem(s) and the patient's and/or family's needs. Usually, the presenting problem(s) are self limited or minor. Typically, 10 minutes are spent face-to-face with the patient and/or family." + }, + { + "system": "http://www.ama-assn.org/go/cpt", + "version": "2020", + "code": "99202", + "display": "Office or other outpatient visit for the evaluation and management of a new patient, which requires these 3 key components: An expanded problem focused history; An expanded problem focused examination; Straightforward medical decision making. Counseling and/or coordination of care with other physicians, other qualified health care professionals, or agencies are provided consistent with the nature of the problem(s) and the patient's and/or family's needs. Usually, the presenting problem(s) are of low to moderate severity. Typically, 20 minutes are spent face-to-face with the patient and/or family." + }, + { + "system": "http://www.ama-assn.org/go/cpt", + "version": "2020", + "code": "99203", + "display": "Office or other outpatient visit for the evaluation and management of a new patient, which requires these 3 key components: A detailed history; A detailed examination; Medical decision making of low complexity. Counseling and/or coordination of care with other physicians, other qualified health care professionals, or agencies are provided consistent with the nature of the problem(s) and the patient's and/or family's needs. Usually, the presenting problem(s) are of moderate severity. Typically, 30 minutes are spent face-to-face with the patient and/or family." + }, + { + "system": "http://www.ama-assn.org/go/cpt", + "version": "2020", + "code": "99204", + "display": "Office or other outpatient visit for the evaluation and management of a new patient, which requires these 3 key components: A comprehensive history; A comprehensive examination; Medical decision making of moderate complexity. Counseling and/or coordination of care with other physicians, other qualified health care professionals, or agencies are provided consistent with the nature of the problem(s) and the patient's and/or family's needs. Usually, the presenting problem(s) are of moderate to high severity. Typically, 45 minutes are spent face-to-face with the patient and/or family." + }, + { + "system": "http://www.ama-assn.org/go/cpt", + "version": "2020", + "code": "99205", + "display": "Office or other outpatient visit for the evaluation and management of a new patient, which requires these 3 key components: A comprehensive history; A comprehensive examination; Medical decision making of high complexity. Counseling and/or coordination of care with other physicians, other qualified health care professionals, or agencies are provided consistent with the nature of the problem(s) and the patient's and/or family's needs. Usually, the presenting problem(s) are of moderate to high severity. Typically, 60 minutes are spent face-to-face with the patient and/or family." + }, + { + "system": "http://www.ama-assn.org/go/cpt", + "version": "2020", + "code": "99212", + "display": "Office or other outpatient visit for the evaluation and management of an established patient, which requires at least 2 of these 3 key components: A problem focused history; A problem focused examination; Straightforward medical decision making. Counseling and/or coordination of care with other physicians, other qualified health care professionals, or agencies are provided consistent with the nature of the problem(s) and the patient's and/or family's needs. Usually, the presenting problem(s) are self limited or minor. Typically, 10 minutes are spent face-to-face with the patient and/or family." + }, + { + "system": "http://www.ama-assn.org/go/cpt", + "version": "2020", + "code": "99213", + "display": "Office or other outpatient visit for the evaluation and management of an established patient, which requires at least 2 of these 3 key components: An expanded problem focused history; An expanded problem focused examination; Medical decision making of low complexity. Counseling and coordination of care with other physicians, other qualified health care professionals, or agencies are provided consistent with the nature of the problem(s) and the patient's and/or family's needs. Usually, the presenting problem(s) are of low to moderate severity. Typically, 15 minutes are spent face-to-face with the patient and/or family." + }, + { + "system": "http://www.ama-assn.org/go/cpt", + "version": "2020", + "code": "99214", + "display": "Office or other outpatient visit for the evaluation and management of an established patient, which requires at least 2 of these 3 key components: A detailed history; A detailed examination; Medical decision making of moderate complexity. Counseling and/or coordination of care with other physicians, other qualified health care professionals, or agencies are provided consistent with the nature of the problem(s) and the patient's and/or family's needs. Usually, the presenting problem(s) are of moderate to high severity. Typically, 25 minutes are spent face-to-face with the patient and/or family." + }, + { + "system": "http://www.ama-assn.org/go/cpt", + "version": "2020", + "code": "99215", + "display": "Office or other outpatient visit for the evaluation and management of an established patient, which requires at least 2 of these 3 key components: A comprehensive history; A comprehensive examination; Medical decision making of high complexity. Counseling and/or coordination of care with other physicians, other qualified health care professionals, or agencies are provided consistent with the nature of the problem(s) and the patient's and/or family's needs. Usually, the presenting problem(s) are of moderate to high severity. Typically, 40 minutes are spent face-to-face with the patient and/or family." + }, + { + "system": "http://snomed.info/sct", + "version": "2018-03", + "code": "30346009", + "display": "Evaluation and management of established outpatient in office or other outpatient facility (procedure)" + }, + { + "system": "http://snomed.info/sct", + "version": "2018-03", + "code": "37894004", + "display": "Evaluation and management of new outpatient in office or other outpatient facility (procedure)" + }, + { + "system": "http://snomed.info/sct", + "version": "2019-09", + "code": "185463005", + "display": "Visit out of hours (procedure)" + }, + { + "system": "http://snomed.info/sct", + "version": "2019-09", + "code": "185464004", + "display": "Out of hours visit - not night visit (procedure)" + }, + { + "system": "http://snomed.info/sct", + "version": "2019-09", + "code": "185465003", + "display": "Weekend visit (procedure)" + }, + { + "system": "http://snomed.info/sct", + "version": "2019-09", + "code": "3391000175108", + "display": "Office visit for pediatric care and assessment (procedure)" + }, + { + "system": "http://snomed.info/sct", + "version": "2019-09", + "code": "439740005", + "display": "Postoperative follow-up visit (procedure)" + }, + { + "system": "http://www.ama-assn.org/go/cpt", + "version": "2020", + "code": "99201", + "display": "Office or other outpatient visit for the evaluation and management of a new patient, which requires these 3 key components: A problem focused history; A problem focused examination; Straightforward medical decision making. Counseling and/or coordination of care with other physicians, other qualified health care professionals, or agencies are provided consistent with the nature of the problem(s) and the patient's and/or family's needs. Usually, the presenting problem(s) are self limited or minor. Typically, 10 minutes are spent face-to-face with the patient and/or family." + }, + { + "system": "http://www.ama-assn.org/go/cpt", + "version": "2020", + "code": "99202", + "display": "Office or other outpatient visit for the evaluation and management of a new patient, which requires these 3 key components: An expanded problem focused history; An expanded problem focused examination; Straightforward medical decision making. Counseling and/or coordination of care with other physicians, other qualified health care professionals, or agencies are provided consistent with the nature of the problem(s) and the patient's and/or family's needs. Usually, the presenting problem(s) are of low to moderate severity. Typically, 20 minutes are spent face-to-face with the patient and/or family." + }, + { + "system": "http://www.ama-assn.org/go/cpt", + "version": "2020", + "code": "99203", + "display": "Office or other outpatient visit for the evaluation and management of a new patient, which requires these 3 key components: A detailed history; A detailed examination; Medical decision making of low complexity. Counseling and/or coordination of care with other physicians, other qualified health care professionals, or agencies are provided consistent with the nature of the problem(s) and the patient's and/or family's needs. Usually, the presenting problem(s) are of moderate severity. Typically, 30 minutes are spent face-to-face with the patient and/or family." + }, + { + "system": "http://www.ama-assn.org/go/cpt", + "version": "2020", + "code": "99204", + "display": "Office or other outpatient visit for the evaluation and management of a new patient, which requires these 3 key components: A comprehensive history; A comprehensive examination; Medical decision making of moderate complexity. Counseling and/or coordination of care with other physicians, other qualified health care professionals, or agencies are provided consistent with the nature of the problem(s) and the patient's and/or family's needs. Usually, the presenting problem(s) are of moderate to high severity. Typically, 45 minutes are spent face-to-face with the patient and/or family." + }, + { + "system": "http://www.ama-assn.org/go/cpt", + "version": "2020", + "code": "99205", + "display": "Office or other outpatient visit for the evaluation and management of a new patient, which requires these 3 key components: A comprehensive history; A comprehensive examination; Medical decision making of high complexity. Counseling and/or coordination of care with other physicians, other qualified health care professionals, or agencies are provided consistent with the nature of the problem(s) and the patient's and/or family's needs. Usually, the presenting problem(s) are of moderate to high severity. Typically, 60 minutes are spent face-to-face with the patient and/or family." + }, + { + "system": "http://www.ama-assn.org/go/cpt", + "version": "2020", + "code": "99212", + "display": "Office or other outpatient visit for the evaluation and management of an established patient, which requires at least 2 of these 3 key components: A problem focused history; A problem focused examination; Straightforward medical decision making. Counseling and/or coordination of care with other physicians, other qualified health care professionals, or agencies are provided consistent with the nature of the problem(s) and the patient's and/or family's needs. Usually, the presenting problem(s) are self limited or minor. Typically, 10 minutes are spent face-to-face with the patient and/or family." + }, + { + "system": "http://www.ama-assn.org/go/cpt", + "version": "2020", + "code": "99213", + "display": "Office or other outpatient visit for the evaluation and management of an established patient, which requires at least 2 of these 3 key components: An expanded problem focused history; An expanded problem focused examination; Medical decision making of low complexity. Counseling and coordination of care with other physicians, other qualified health care professionals, or agencies are provided consistent with the nature of the problem(s) and the patient's and/or family's needs. Usually, the presenting problem(s) are of low to moderate severity. Typically, 15 minutes are spent face-to-face with the patient and/or family." + }, + { + "system": "http://www.ama-assn.org/go/cpt", + "version": "2020", + "code": "99214", + "display": "Office or other outpatient visit for the evaluation and management of an established patient, which requires at least 2 of these 3 key components: A detailed history; A detailed examination; Medical decision making of moderate complexity. Counseling and/or coordination of care with other physicians, other qualified health care professionals, or agencies are provided consistent with the nature of the problem(s) and the patient's and/or family's needs. Usually, the presenting problem(s) are of moderate to high severity. Typically, 25 minutes are spent face-to-face with the patient and/or family." + }, + { + "system": "http://www.ama-assn.org/go/cpt", + "version": "2020", + "code": "99215", + "display": "Office or other outpatient visit for the evaluation and management of an established patient, which requires at least 2 of these 3 key components: A comprehensive history; A comprehensive examination; Medical decision making of high complexity. Counseling and/or coordination of care with other physicians, other qualified health care professionals, or agencies are provided consistent with the nature of the problem(s) and the patient's and/or family's needs. Usually, the presenting problem(s) are of moderate to high severity. Typically, 40 minutes are spent face-to-face with the patient and/or family." + }, + { + "system": "http://snomed.info/sct", + "version": "2018-03", + "code": "30346009", + "display": "Evaluation and management of established outpatient in office or other outpatient facility (procedure)" + }, + { + "system": "http://snomed.info/sct", + "version": "2018-03", + "code": "37894004", + "display": "Evaluation and management of new outpatient in office or other outpatient facility (procedure)" + }, + { + "system": "http://snomed.info/sct", + "version": "2019-09", + "code": "185463005", + "display": "Visit out of hours (procedure)" + }, + { + "system": "http://snomed.info/sct", + "version": "2019-09", + "code": "185464004", + "display": "Out of hours visit - not night visit (procedure)" + }, + { + "system": "http://snomed.info/sct", + "version": "2019-09", + "code": "185465003", + "display": "Weekend visit (procedure)" + }, + { + "system": "http://snomed.info/sct", + "version": "2019-09", + "code": "3391000175108", + "display": "Office visit for pediatric care and assessment (procedure)" + }, + { + "system": "http://snomed.info/sct", + "version": "2019-09", + "code": "439740005", + "display": "Postoperative follow-up visit (procedure)" + }, + { + "system": "http://www.ama-assn.org/go/cpt", + "version": "2020", + "code": "99201", + "display": "Office or other outpatient visit for the evaluation and management of a new patient, which requires these 3 key components: A problem focused history; A problem focused examination; Straightforward medical decision making. Counseling and/or coordination of care with other physicians, other qualified health care professionals, or agencies are provided consistent with the nature of the problem(s) and the patient's and/or family's needs. Usually, the presenting problem(s) are self limited or minor. Typically, 10 minutes are spent face-to-face with the patient and/or family." + }, + { + "system": "http://www.ama-assn.org/go/cpt", + "version": "2020", + "code": "99202", + "display": "Office or other outpatient visit for the evaluation and management of a new patient, which requires these 3 key components: An expanded problem focused history; An expanded problem focused examination; Straightforward medical decision making. Counseling and/or coordination of care with other physicians, other qualified health care professionals, or agencies are provided consistent with the nature of the problem(s) and the patient's and/or family's needs. Usually, the presenting problem(s) are of low to moderate severity. Typically, 20 minutes are spent face-to-face with the patient and/or family." + }, + { + "system": "http://www.ama-assn.org/go/cpt", + "version": "2020", + "code": "99203", + "display": "Office or other outpatient visit for the evaluation and management of a new patient, which requires these 3 key components: A detailed history; A detailed examination; Medical decision making of low complexity. Counseling and/or coordination of care with other physicians, other qualified health care professionals, or agencies are provided consistent with the nature of the problem(s) and the patient's and/or family's needs. Usually, the presenting problem(s) are of moderate severity. Typically, 30 minutes are spent face-to-face with the patient and/or family." + }, + { + "system": "http://www.ama-assn.org/go/cpt", + "version": "2020", + "code": "99204", + "display": "Office or other outpatient visit for the evaluation and management of a new patient, which requires these 3 key components: A comprehensive history; A comprehensive examination; Medical decision making of moderate complexity. Counseling and/or coordination of care with other physicians, other qualified health care professionals, or agencies are provided consistent with the nature of the problem(s) and the patient's and/or family's needs. Usually, the presenting problem(s) are of moderate to high severity. Typically, 45 minutes are spent face-to-face with the patient and/or family." + }, + { + "system": "http://www.ama-assn.org/go/cpt", + "version": "2020", + "code": "99205", + "display": "Office or other outpatient visit for the evaluation and management of a new patient, which requires these 3 key components: A comprehensive history; A comprehensive examination; Medical decision making of high complexity. Counseling and/or coordination of care with other physicians, other qualified health care professionals, or agencies are provided consistent with the nature of the problem(s) and the patient's and/or family's needs. Usually, the presenting problem(s) are of moderate to high severity. Typically, 60 minutes are spent face-to-face with the patient and/or family." + }, + { + "system": "http://www.ama-assn.org/go/cpt", + "version": "2020", + "code": "99212", + "display": "Office or other outpatient visit for the evaluation and management of an established patient, which requires at least 2 of these 3 key components: A problem focused history; A problem focused examination; Straightforward medical decision making. Counseling and/or coordination of care with other physicians, other qualified health care professionals, or agencies are provided consistent with the nature of the problem(s) and the patient's and/or family's needs. Usually, the presenting problem(s) are self limited or minor. Typically, 10 minutes are spent face-to-face with the patient and/or family." + }, + { + "system": "http://www.ama-assn.org/go/cpt", + "version": "2020", + "code": "99213", + "display": "Office or other outpatient visit for the evaluation and management of an established patient, which requires at least 2 of these 3 key components: An expanded problem focused history; An expanded problem focused examination; Medical decision making of low complexity. Counseling and coordination of care with other physicians, other qualified health care professionals, or agencies are provided consistent with the nature of the problem(s) and the patient's and/or family's needs. Usually, the presenting problem(s) are of low to moderate severity. Typically, 15 minutes are spent face-to-face with the patient and/or family." + }, + { + "system": "http://www.ama-assn.org/go/cpt", + "version": "2020", + "code": "99214", + "display": "Office or other outpatient visit for the evaluation and management of an established patient, which requires at least 2 of these 3 key components: A detailed history; A detailed examination; Medical decision making of moderate complexity. Counseling and/or coordination of care with other physicians, other qualified health care professionals, or agencies are provided consistent with the nature of the problem(s) and the patient's and/or family's needs. Usually, the presenting problem(s) are of moderate to high severity. Typically, 25 minutes are spent face-to-face with the patient and/or family." + }, + { + "system": "http://www.ama-assn.org/go/cpt", + "version": "2020", + "code": "99215", + "display": "Office or other outpatient visit for the evaluation and management of an established patient, which requires at least 2 of these 3 key components: A comprehensive history; A comprehensive examination; Medical decision making of high complexity. Counseling and/or coordination of care with other physicians, other qualified health care professionals, or agencies are provided consistent with the nature of the problem(s) and the patient's and/or family's needs. Usually, the presenting problem(s) are of moderate to high severity. Typically, 40 minutes are spent face-to-face with the patient and/or family." + }, + { + "system": "http://snomed.info/sct", + "version": "2018-03", + "code": "30346009", + "display": "Evaluation and management of established outpatient in office or other outpatient facility (procedure)" + }, + { + "system": "http://snomed.info/sct", + "version": "2018-03", + "code": "37894004", + "display": "Evaluation and management of new outpatient in office or other outpatient facility (procedure)" + }, + { + "system": "http://snomed.info/sct", + "version": "2019-09", + "code": "185463005", + "display": "Visit out of hours (procedure)" + }, + { + "system": "http://snomed.info/sct", + "version": "2019-09", + "code": "185464004", + "display": "Out of hours visit - not night visit (procedure)" + }, + { + "system": "http://snomed.info/sct", + "version": "2019-09", + "code": "185465003", + "display": "Weekend visit (procedure)" + }, + { + "system": "http://snomed.info/sct", + "version": "2019-09", + "code": "3391000175108", + "display": "Office visit for pediatric care and assessment (procedure)" + }, + { + "system": "http://snomed.info/sct", + "version": "2019-09", + "code": "439740005", + "display": "Postoperative follow-up visit (procedure)" + }, + { + "system": "http://www.ama-assn.org/go/cpt", + "version": "2020", + "code": "99201", + "display": "Office or other outpatient visit for the evaluation and management of a new patient, which requires these 3 key components: A problem focused history; A problem focused examination; Straightforward medical decision making. Counseling and/or coordination of care with other physicians, other qualified health care professionals, or agencies are provided consistent with the nature of the problem(s) and the patient's and/or family's needs. Usually, the presenting problem(s) are self limited or minor. Typically, 10 minutes are spent face-to-face with the patient and/or family." + }, + { + "system": "http://www.ama-assn.org/go/cpt", + "version": "2020", + "code": "99202", + "display": "Office or other outpatient visit for the evaluation and management of a new patient, which requires these 3 key components: An expanded problem focused history; An expanded problem focused examination; Straightforward medical decision making. Counseling and/or coordination of care with other physicians, other qualified health care professionals, or agencies are provided consistent with the nature of the problem(s) and the patient's and/or family's needs. Usually, the presenting problem(s) are of low to moderate severity. Typically, 20 minutes are spent face-to-face with the patient and/or family." + }, + { + "system": "http://www.ama-assn.org/go/cpt", + "version": "2020", + "code": "99203", + "display": "Office or other outpatient visit for the evaluation and management of a new patient, which requires these 3 key components: A detailed history; A detailed examination; Medical decision making of low complexity. Counseling and/or coordination of care with other physicians, other qualified health care professionals, or agencies are provided consistent with the nature of the problem(s) and the patient's and/or family's needs. Usually, the presenting problem(s) are of moderate severity. Typically, 30 minutes are spent face-to-face with the patient and/or family." + }, + { + "system": "http://www.ama-assn.org/go/cpt", + "version": "2020", + "code": "99204", + "display": "Office or other outpatient visit for the evaluation and management of a new patient, which requires these 3 key components: A comprehensive history; A comprehensive examination; Medical decision making of moderate complexity. Counseling and/or coordination of care with other physicians, other qualified health care professionals, or agencies are provided consistent with the nature of the problem(s) and the patient's and/or family's needs. Usually, the presenting problem(s) are of moderate to high severity. Typically, 45 minutes are spent face-to-face with the patient and/or family." + }, + { + "system": "http://www.ama-assn.org/go/cpt", + "version": "2020", + "code": "99205", + "display": "Office or other outpatient visit for the evaluation and management of a new patient, which requires these 3 key components: A comprehensive history; A comprehensive examination; Medical decision making of high complexity. Counseling and/or coordination of care with other physicians, other qualified health care professionals, or agencies are provided consistent with the nature of the problem(s) and the patient's and/or family's needs. Usually, the presenting problem(s) are of moderate to high severity. Typically, 60 minutes are spent face-to-face with the patient and/or family." + }, + { + "system": "http://www.ama-assn.org/go/cpt", + "version": "2020", + "code": "99212", + "display": "Office or other outpatient visit for the evaluation and management of an established patient, which requires at least 2 of these 3 key components: A problem focused history; A problem focused examination; Straightforward medical decision making. Counseling and/or coordination of care with other physicians, other qualified health care professionals, or agencies are provided consistent with the nature of the problem(s) and the patient's and/or family's needs. Usually, the presenting problem(s) are self limited or minor. Typically, 10 minutes are spent face-to-face with the patient and/or family." + }, + { + "system": "http://www.ama-assn.org/go/cpt", + "version": "2020", + "code": "99213", + "display": "Office or other outpatient visit for the evaluation and management of an established patient, which requires at least 2 of these 3 key components: An expanded problem focused history; An expanded problem focused examination; Medical decision making of low complexity. Counseling and coordination of care with other physicians, other qualified health care professionals, or agencies are provided consistent with the nature of the problem(s) and the patient's and/or family's needs. Usually, the presenting problem(s) are of low to moderate severity. Typically, 15 minutes are spent face-to-face with the patient and/or family." + }, + { + "system": "http://www.ama-assn.org/go/cpt", + "version": "2020", + "code": "99214", + "display": "Office or other outpatient visit for the evaluation and management of an established patient, which requires at least 2 of these 3 key components: A detailed history; A detailed examination; Medical decision making of moderate complexity. Counseling and/or coordination of care with other physicians, other qualified health care professionals, or agencies are provided consistent with the nature of the problem(s) and the patient's and/or family's needs. Usually, the presenting problem(s) are of moderate to high severity. Typically, 25 minutes are spent face-to-face with the patient and/or family." + }, + { + "system": "http://www.ama-assn.org/go/cpt", + "version": "2020", + "code": "99215", + "display": "Office or other outpatient visit for the evaluation and management of an established patient, which requires at least 2 of these 3 key components: A comprehensive history; A comprehensive examination; Medical decision making of high complexity. Counseling and/or coordination of care with other physicians, other qualified health care professionals, or agencies are provided consistent with the nature of the problem(s) and the patient's and/or family's needs. Usually, the presenting problem(s) are of moderate to high severity. Typically, 40 minutes are spent face-to-face with the patient and/or family." + }, + { + "system": "http://snomed.info/sct", + "version": "2018-03", + "code": "30346009", + "display": "Evaluation and management of established outpatient in office or other outpatient facility (procedure)" + }, + { + "system": "http://snomed.info/sct", + "version": "2018-03", + "code": "37894004", + "display": "Evaluation and management of new outpatient in office or other outpatient facility (procedure)" + }, + { + "system": "http://snomed.info/sct", + "version": "2019-09", + "code": "185463005", + "display": "Visit out of hours (procedure)" + }, + { + "system": "http://snomed.info/sct", + "version": "2019-09", + "code": "185464004", + "display": "Out of hours visit - not night visit (procedure)" + }, + { + "system": "http://snomed.info/sct", + "version": "2019-09", + "code": "185465003", + "display": "Weekend visit (procedure)" + }, + { + "system": "http://snomed.info/sct", + "version": "2019-09", + "code": "3391000175108", + "display": "Office visit for pediatric care and assessment (procedure)" + }, + { + "system": "http://snomed.info/sct", + "version": "2019-09", + "code": "439740005", + "display": "Postoperative follow-up visit (procedure)" + }, + { + "system": "http://www.ama-assn.org/go/cpt", + "version": "2020", + "code": "99201", + "display": "Office or other outpatient visit for the evaluation and management of a new patient, which requires these 3 key components: A problem focused history; A problem focused examination; Straightforward medical decision making. Counseling and/or coordination of care with other physicians, other qualified health care professionals, or agencies are provided consistent with the nature of the problem(s) and the patient's and/or family's needs. Usually, the presenting problem(s) are self limited or minor. Typically, 10 minutes are spent face-to-face with the patient and/or family." + }, + { + "system": "http://www.ama-assn.org/go/cpt", + "version": "2020", + "code": "99202", + "display": "Office or other outpatient visit for the evaluation and management of a new patient, which requires these 3 key components: An expanded problem focused history; An expanded problem focused examination; Straightforward medical decision making. Counseling and/or coordination of care with other physicians, other qualified health care professionals, or agencies are provided consistent with the nature of the problem(s) and the patient's and/or family's needs. Usually, the presenting problem(s) are of low to moderate severity. Typically, 20 minutes are spent face-to-face with the patient and/or family." + }, + { + "system": "http://www.ama-assn.org/go/cpt", + "version": "2020", + "code": "99203", + "display": "Office or other outpatient visit for the evaluation and management of a new patient, which requires these 3 key components: A detailed history; A detailed examination; Medical decision making of low complexity. Counseling and/or coordination of care with other physicians, other qualified health care professionals, or agencies are provided consistent with the nature of the problem(s) and the patient's and/or family's needs. Usually, the presenting problem(s) are of moderate severity. Typically, 30 minutes are spent face-to-face with the patient and/or family." + }, + { + "system": "http://www.ama-assn.org/go/cpt", + "version": "2020", + "code": "99204", + "display": "Office or other outpatient visit for the evaluation and management of a new patient, which requires these 3 key components: A comprehensive history; A comprehensive examination; Medical decision making of moderate complexity. Counseling and/or coordination of care with other physicians, other qualified health care professionals, or agencies are provided consistent with the nature of the problem(s) and the patient's and/or family's needs. Usually, the presenting problem(s) are of moderate to high severity. Typically, 45 minutes are spent face-to-face with the patient and/or family." + }, + { + "system": "http://www.ama-assn.org/go/cpt", + "version": "2020", + "code": "99205", + "display": "Office or other outpatient visit for the evaluation and management of a new patient, which requires these 3 key components: A comprehensive history; A comprehensive examination; Medical decision making of high complexity. Counseling and/or coordination of care with other physicians, other qualified health care professionals, or agencies are provided consistent with the nature of the problem(s) and the patient's and/or family's needs. Usually, the presenting problem(s) are of moderate to high severity. Typically, 60 minutes are spent face-to-face with the patient and/or family." + }, + { + "system": "http://www.ama-assn.org/go/cpt", + "version": "2020", + "code": "99212", + "display": "Office or other outpatient visit for the evaluation and management of an established patient, which requires at least 2 of these 3 key components: A problem focused history; A problem focused examination; Straightforward medical decision making. Counseling and/or coordination of care with other physicians, other qualified health care professionals, or agencies are provided consistent with the nature of the problem(s) and the patient's and/or family's needs. Usually, the presenting problem(s) are self limited or minor. Typically, 10 minutes are spent face-to-face with the patient and/or family." + }, + { + "system": "http://www.ama-assn.org/go/cpt", + "version": "2020", + "code": "99213", + "display": "Office or other outpatient visit for the evaluation and management of an established patient, which requires at least 2 of these 3 key components: An expanded problem focused history; An expanded problem focused examination; Medical decision making of low complexity. Counseling and coordination of care with other physicians, other qualified health care professionals, or agencies are provided consistent with the nature of the problem(s) and the patient's and/or family's needs. Usually, the presenting problem(s) are of low to moderate severity. Typically, 15 minutes are spent face-to-face with the patient and/or family." + }, + { + "system": "http://www.ama-assn.org/go/cpt", + "version": "2020", + "code": "99214", + "display": "Office or other outpatient visit for the evaluation and management of an established patient, which requires at least 2 of these 3 key components: A detailed history; A detailed examination; Medical decision making of moderate complexity. Counseling and/or coordination of care with other physicians, other qualified health care professionals, or agencies are provided consistent with the nature of the problem(s) and the patient's and/or family's needs. Usually, the presenting problem(s) are of moderate to high severity. Typically, 25 minutes are spent face-to-face with the patient and/or family." + }, + { + "system": "http://www.ama-assn.org/go/cpt", + "version": "2020", + "code": "99215", + "display": "Office or other outpatient visit for the evaluation and management of an established patient, which requires at least 2 of these 3 key components: A comprehensive history; A comprehensive examination; Medical decision making of high complexity. Counseling and/or coordination of care with other physicians, other qualified health care professionals, or agencies are provided consistent with the nature of the problem(s) and the patient's and/or family's needs. Usually, the presenting problem(s) are of moderate to high severity. Typically, 40 minutes are spent face-to-face with the patient and/or family." + }, + { + "system": "http://snomed.info/sct", + "version": "2018-03", + "code": "30346009", + "display": "Evaluation and management of established outpatient in office or other outpatient facility (procedure)" + }, + { + "system": "http://snomed.info/sct", + "version": "2018-03", + "code": "37894004", + "display": "Evaluation and management of new outpatient in office or other outpatient facility (procedure)" + }, + { + "system": "http://snomed.info/sct", + "version": "2019-09", + "code": "185463005", + "display": "Visit out of hours (procedure)" + }, + { + "system": "http://snomed.info/sct", + "version": "2019-09", + "code": "185464004", + "display": "Out of hours visit - not night visit (procedure)" + }, + { + "system": "http://snomed.info/sct", + "version": "2019-09", + "code": "185465003", + "display": "Weekend visit (procedure)" + }, + { + "system": "http://snomed.info/sct", + "version": "2019-09", + "code": "3391000175108", + "display": "Office visit for pediatric care and assessment (procedure)" + }, + { + "system": "http://snomed.info/sct", + "version": "2019-09", + "code": "439740005", + "display": "Postoperative follow-up visit (procedure)" + }, + { + "system": "http://www.ama-assn.org/go/cpt", + "version": "2020", + "code": "99201", + "display": "Office or other outpatient visit for the evaluation and management of a new patient, which requires these 3 key components: A problem focused history; A problem focused examination; Straightforward medical decision making. Counseling and/or coordination of care with other physicians, other qualified health care professionals, or agencies are provided consistent with the nature of the problem(s) and the patient's and/or family's needs. Usually, the presenting problem(s) are self limited or minor. Typically, 10 minutes are spent face-to-face with the patient and/or family." + }, + { + "system": "http://www.ama-assn.org/go/cpt", + "version": "2020", + "code": "99202", + "display": "Office or other outpatient visit for the evaluation and management of a new patient, which requires these 3 key components: An expanded problem focused history; An expanded problem focused examination; Straightforward medical decision making. Counseling and/or coordination of care with other physicians, other qualified health care professionals, or agencies are provided consistent with the nature of the problem(s) and the patient's and/or family's needs. Usually, the presenting problem(s) are of low to moderate severity. Typically, 20 minutes are spent face-to-face with the patient and/or family." + }, + { + "system": "http://www.ama-assn.org/go/cpt", + "version": "2020", + "code": "99203", + "display": "Office or other outpatient visit for the evaluation and management of a new patient, which requires these 3 key components: A detailed history; A detailed examination; Medical decision making of low complexity. Counseling and/or coordination of care with other physicians, other qualified health care professionals, or agencies are provided consistent with the nature of the problem(s) and the patient's and/or family's needs. Usually, the presenting problem(s) are of moderate severity. Typically, 30 minutes are spent face-to-face with the patient and/or family." + }, + { + "system": "http://www.ama-assn.org/go/cpt", + "version": "2020", + "code": "99204", + "display": "Office or other outpatient visit for the evaluation and management of a new patient, which requires these 3 key components: A comprehensive history; A comprehensive examination; Medical decision making of moderate complexity. Counseling and/or coordination of care with other physicians, other qualified health care professionals, or agencies are provided consistent with the nature of the problem(s) and the patient's and/or family's needs. Usually, the presenting problem(s) are of moderate to high severity. Typically, 45 minutes are spent face-to-face with the patient and/or family." + }, + { + "system": "http://www.ama-assn.org/go/cpt", + "version": "2020", + "code": "99205", + "display": "Office or other outpatient visit for the evaluation and management of a new patient, which requires these 3 key components: A comprehensive history; A comprehensive examination; Medical decision making of high complexity. Counseling and/or coordination of care with other physicians, other qualified health care professionals, or agencies are provided consistent with the nature of the problem(s) and the patient's and/or family's needs. Usually, the presenting problem(s) are of moderate to high severity. Typically, 60 minutes are spent face-to-face with the patient and/or family." + }, + { + "system": "http://www.ama-assn.org/go/cpt", + "version": "2020", + "code": "99212", + "display": "Office or other outpatient visit for the evaluation and management of an established patient, which requires at least 2 of these 3 key components: A problem focused history; A problem focused examination; Straightforward medical decision making. Counseling and/or coordination of care with other physicians, other qualified health care professionals, or agencies are provided consistent with the nature of the problem(s) and the patient's and/or family's needs. Usually, the presenting problem(s) are self limited or minor. Typically, 10 minutes are spent face-to-face with the patient and/or family." + }, + { + "system": "http://www.ama-assn.org/go/cpt", + "version": "2020", + "code": "99213", + "display": "Office or other outpatient visit for the evaluation and management of an established patient, which requires at least 2 of these 3 key components: An expanded problem focused history; An expanded problem focused examination; Medical decision making of low complexity. Counseling and coordination of care with other physicians, other qualified health care professionals, or agencies are provided consistent with the nature of the problem(s) and the patient's and/or family's needs. Usually, the presenting problem(s) are of low to moderate severity. Typically, 15 minutes are spent face-to-face with the patient and/or family." + }, + { + "system": "http://www.ama-assn.org/go/cpt", + "version": "2020", + "code": "99214", + "display": "Office or other outpatient visit for the evaluation and management of an established patient, which requires at least 2 of these 3 key components: A detailed history; A detailed examination; Medical decision making of moderate complexity. Counseling and/or coordination of care with other physicians, other qualified health care professionals, or agencies are provided consistent with the nature of the problem(s) and the patient's and/or family's needs. Usually, the presenting problem(s) are of moderate to high severity. Typically, 25 minutes are spent face-to-face with the patient and/or family." + }, + { + "system": "http://www.ama-assn.org/go/cpt", + "version": "2020", + "code": "99215", + "display": "Office or other outpatient visit for the evaluation and management of an established patient, which requires at least 2 of these 3 key components: A comprehensive history; A comprehensive examination; Medical decision making of high complexity. Counseling and/or coordination of care with other physicians, other qualified health care professionals, or agencies are provided consistent with the nature of the problem(s) and the patient's and/or family's needs. Usually, the presenting problem(s) are of moderate to high severity. Typically, 40 minutes are spent face-to-face with the patient and/or family." + }, + { + "system": "http://snomed.info/sct", + "version": "2018-03", + "code": "30346009", + "display": "Evaluation and management of established outpatient in office or other outpatient facility (procedure)" + }, + { + "system": "http://snomed.info/sct", + "version": "2018-03", + "code": "37894004", + "display": "Evaluation and management of new outpatient in office or other outpatient facility (procedure)" + }, + { + "system": "http://snomed.info/sct", + "version": "2019-09", + "code": "185463005", + "display": "Visit out of hours (procedure)" + }, + { + "system": "http://snomed.info/sct", + "version": "2019-09", + "code": "185464004", + "display": "Out of hours visit - not night visit (procedure)" + }, + { + "system": "http://snomed.info/sct", + "version": "2019-09", + "code": "185465003", + "display": "Weekend visit (procedure)" + }, + { + "system": "http://snomed.info/sct", + "version": "2019-09", + "code": "3391000175108", + "display": "Office visit for pediatric care and assessment (procedure)" + }, + { + "system": "http://snomed.info/sct", + "version": "2019-09", + "code": "439740005", + "display": "Postoperative follow-up visit (procedure)" + }, + { + "system": "http://www.ama-assn.org/go/cpt", + "version": "2020", + "code": "99201", + "display": "Office or other outpatient visit for the evaluation and management of a new patient, which requires these 3 key components: A problem focused history; A problem focused examination; Straightforward medical decision making. Counseling and/or coordination of care with other physicians, other qualified health care professionals, or agencies are provided consistent with the nature of the problem(s) and the patient's and/or family's needs. Usually, the presenting problem(s) are self limited or minor. Typically, 10 minutes are spent face-to-face with the patient and/or family." + }, + { + "system": "http://www.ama-assn.org/go/cpt", + "version": "2020", + "code": "99202", + "display": "Office or other outpatient visit for the evaluation and management of a new patient, which requires these 3 key components: An expanded problem focused history; An expanded problem focused examination; Straightforward medical decision making. Counseling and/or coordination of care with other physicians, other qualified health care professionals, or agencies are provided consistent with the nature of the problem(s) and the patient's and/or family's needs. Usually, the presenting problem(s) are of low to moderate severity. Typically, 20 minutes are spent face-to-face with the patient and/or family." + }, + { + "system": "http://www.ama-assn.org/go/cpt", + "version": "2020", + "code": "99203", + "display": "Office or other outpatient visit for the evaluation and management of a new patient, which requires these 3 key components: A detailed history; A detailed examination; Medical decision making of low complexity. Counseling and/or coordination of care with other physicians, other qualified health care professionals, or agencies are provided consistent with the nature of the problem(s) and the patient's and/or family's needs. Usually, the presenting problem(s) are of moderate severity. Typically, 30 minutes are spent face-to-face with the patient and/or family." + }, + { + "system": "http://www.ama-assn.org/go/cpt", + "version": "2020", + "code": "99204", + "display": "Office or other outpatient visit for the evaluation and management of a new patient, which requires these 3 key components: A comprehensive history; A comprehensive examination; Medical decision making of moderate complexity. Counseling and/or coordination of care with other physicians, other qualified health care professionals, or agencies are provided consistent with the nature of the problem(s) and the patient's and/or family's needs. Usually, the presenting problem(s) are of moderate to high severity. Typically, 45 minutes are spent face-to-face with the patient and/or family." + }, + { + "system": "http://www.ama-assn.org/go/cpt", + "version": "2020", + "code": "99205", + "display": "Office or other outpatient visit for the evaluation and management of a new patient, which requires these 3 key components: A comprehensive history; A comprehensive examination; Medical decision making of high complexity. Counseling and/or coordination of care with other physicians, other qualified health care professionals, or agencies are provided consistent with the nature of the problem(s) and the patient's and/or family's needs. Usually, the presenting problem(s) are of moderate to high severity. Typically, 60 minutes are spent face-to-face with the patient and/or family." + }, + { + "system": "http://www.ama-assn.org/go/cpt", + "version": "2020", + "code": "99212", + "display": "Office or other outpatient visit for the evaluation and management of an established patient, which requires at least 2 of these 3 key components: A problem focused history; A problem focused examination; Straightforward medical decision making. Counseling and/or coordination of care with other physicians, other qualified health care professionals, or agencies are provided consistent with the nature of the problem(s) and the patient's and/or family's needs. Usually, the presenting problem(s) are self limited or minor. Typically, 10 minutes are spent face-to-face with the patient and/or family." + }, + { + "system": "http://www.ama-assn.org/go/cpt", + "version": "2020", + "code": "99213", + "display": "Office or other outpatient visit for the evaluation and management of an established patient, which requires at least 2 of these 3 key components: An expanded problem focused history; An expanded problem focused examination; Medical decision making of low complexity. Counseling and coordination of care with other physicians, other qualified health care professionals, or agencies are provided consistent with the nature of the problem(s) and the patient's and/or family's needs. Usually, the presenting problem(s) are of low to moderate severity. Typically, 15 minutes are spent face-to-face with the patient and/or family." + }, + { + "system": "http://www.ama-assn.org/go/cpt", + "version": "2020", + "code": "99214", + "display": "Office or other outpatient visit for the evaluation and management of an established patient, which requires at least 2 of these 3 key components: A detailed history; A detailed examination; Medical decision making of moderate complexity. Counseling and/or coordination of care with other physicians, other qualified health care professionals, or agencies are provided consistent with the nature of the problem(s) and the patient's and/or family's needs. Usually, the presenting problem(s) are of moderate to high severity. Typically, 25 minutes are spent face-to-face with the patient and/or family." + }, + { + "system": "http://www.ama-assn.org/go/cpt", + "version": "2020", + "code": "99215", + "display": "Office or other outpatient visit for the evaluation and management of an established patient, which requires at least 2 of these 3 key components: A comprehensive history; A comprehensive examination; Medical decision making of high complexity. Counseling and/or coordination of care with other physicians, other qualified health care professionals, or agencies are provided consistent with the nature of the problem(s) and the patient's and/or family's needs. Usually, the presenting problem(s) are of moderate to high severity. Typically, 40 minutes are spent face-to-face with the patient and/or family." + }, + { + "system": "http://snomed.info/sct", + "version": "2018-03", + "code": "30346009", + "display": "Evaluation and management of established outpatient in office or other outpatient facility (procedure)" + }, + { + "system": "http://snomed.info/sct", + "version": "2018-03", + "code": "37894004", + "display": "Evaluation and management of new outpatient in office or other outpatient facility (procedure)" + }, + { + "system": "http://snomed.info/sct", + "version": "2019-09", + "code": "185463005", + "display": "Visit out of hours (procedure)" + }, + { + "system": "http://snomed.info/sct", + "version": "2019-09", + "code": "185464004", + "display": "Out of hours visit - not night visit (procedure)" + }, + { + "system": "http://snomed.info/sct", + "version": "2019-09", + "code": "185465003", + "display": "Weekend visit (procedure)" + }, + { + "system": "http://snomed.info/sct", + "version": "2019-09", + "code": "3391000175108", + "display": "Office visit for pediatric care and assessment (procedure)" + }, + { + "system": "http://snomed.info/sct", + "version": "2019-09", + "code": "439740005", + "display": "Postoperative follow-up visit (procedure)" + }, + { + "system": "http://www.ama-assn.org/go/cpt", + "version": "2020", + "code": "99201", + "display": "Office or other outpatient visit for the evaluation and management of a new patient, which requires these 3 key components: A problem focused history; A problem focused examination; Straightforward medical decision making. Counseling and/or coordination of care with other physicians, other qualified health care professionals, or agencies are provided consistent with the nature of the problem(s) and the patient's and/or family's needs. Usually, the presenting problem(s) are self limited or minor. Typically, 10 minutes are spent face-to-face with the patient and/or family." + }, + { + "system": "http://www.ama-assn.org/go/cpt", + "version": "2020", + "code": "99202", + "display": "Office or other outpatient visit for the evaluation and management of a new patient, which requires these 3 key components: An expanded problem focused history; An expanded problem focused examination; Straightforward medical decision making. Counseling and/or coordination of care with other physicians, other qualified health care professionals, or agencies are provided consistent with the nature of the problem(s) and the patient's and/or family's needs. Usually, the presenting problem(s) are of low to moderate severity. Typically, 20 minutes are spent face-to-face with the patient and/or family." + }, + { + "system": "http://www.ama-assn.org/go/cpt", + "version": "2020", + "code": "99203", + "display": "Office or other outpatient visit for the evaluation and management of a new patient, which requires these 3 key components: A detailed history; A detailed examination; Medical decision making of low complexity. Counseling and/or coordination of care with other physicians, other qualified health care professionals, or agencies are provided consistent with the nature of the problem(s) and the patient's and/or family's needs. Usually, the presenting problem(s) are of moderate severity. Typically, 30 minutes are spent face-to-face with the patient and/or family." + }, + { + "system": "http://www.ama-assn.org/go/cpt", + "version": "2020", + "code": "99204", + "display": "Office or other outpatient visit for the evaluation and management of a new patient, which requires these 3 key components: A comprehensive history; A comprehensive examination; Medical decision making of moderate complexity. Counseling and/or coordination of care with other physicians, other qualified health care professionals, or agencies are provided consistent with the nature of the problem(s) and the patient's and/or family's needs. Usually, the presenting problem(s) are of moderate to high severity. Typically, 45 minutes are spent face-to-face with the patient and/or family." + }, + { + "system": "http://www.ama-assn.org/go/cpt", + "version": "2020", + "code": "99205", + "display": "Office or other outpatient visit for the evaluation and management of a new patient, which requires these 3 key components: A comprehensive history; A comprehensive examination; Medical decision making of high complexity. Counseling and/or coordination of care with other physicians, other qualified health care professionals, or agencies are provided consistent with the nature of the problem(s) and the patient's and/or family's needs. Usually, the presenting problem(s) are of moderate to high severity. Typically, 60 minutes are spent face-to-face with the patient and/or family." + }, + { + "system": "http://www.ama-assn.org/go/cpt", + "version": "2020", + "code": "99212", + "display": "Office or other outpatient visit for the evaluation and management of an established patient, which requires at least 2 of these 3 key components: A problem focused history; A problem focused examination; Straightforward medical decision making. Counseling and/or coordination of care with other physicians, other qualified health care professionals, or agencies are provided consistent with the nature of the problem(s) and the patient's and/or family's needs. Usually, the presenting problem(s) are self limited or minor. Typically, 10 minutes are spent face-to-face with the patient and/or family." + }, + { + "system": "http://www.ama-assn.org/go/cpt", + "version": "2020", + "code": "99213", + "display": "Office or other outpatient visit for the evaluation and management of an established patient, which requires at least 2 of these 3 key components: An expanded problem focused history; An expanded problem focused examination; Medical decision making of low complexity. Counseling and coordination of care with other physicians, other qualified health care professionals, or agencies are provided consistent with the nature of the problem(s) and the patient's and/or family's needs. Usually, the presenting problem(s) are of low to moderate severity. Typically, 15 minutes are spent face-to-face with the patient and/or family." + }, + { + "system": "http://www.ama-assn.org/go/cpt", + "version": "2020", + "code": "99214", + "display": "Office or other outpatient visit for the evaluation and management of an established patient, which requires at least 2 of these 3 key components: A detailed history; A detailed examination; Medical decision making of moderate complexity. Counseling and/or coordination of care with other physicians, other qualified health care professionals, or agencies are provided consistent with the nature of the problem(s) and the patient's and/or family's needs. Usually, the presenting problem(s) are of moderate to high severity. Typically, 25 minutes are spent face-to-face with the patient and/or family." + }, + { + "system": "http://www.ama-assn.org/go/cpt", + "version": "2020", + "code": "99215", + "display": "Office or other outpatient visit for the evaluation and management of an established patient, which requires at least 2 of these 3 key components: A comprehensive history; A comprehensive examination; Medical decision making of high complexity. Counseling and/or coordination of care with other physicians, other qualified health care professionals, or agencies are provided consistent with the nature of the problem(s) and the patient's and/or family's needs. Usually, the presenting problem(s) are of moderate to high severity. Typically, 40 minutes are spent face-to-face with the patient and/or family." + }, + { + "system": "http://snomed.info/sct", + "version": "2018-03", + "code": "30346009", + "display": "Evaluation and management of established outpatient in office or other outpatient facility (procedure)" + }, + { + "system": "http://snomed.info/sct", + "version": "2018-03", + "code": "37894004", + "display": "Evaluation and management of new outpatient in office or other outpatient facility (procedure)" + }, + { + "system": "http://snomed.info/sct", + "version": "2019-09", + "code": "185463005", + "display": "Visit out of hours (procedure)" + }, + { + "system": "http://snomed.info/sct", + "version": "2019-09", + "code": "185464004", + "display": "Out of hours visit - not night visit (procedure)" + }, + { + "system": "http://snomed.info/sct", + "version": "2019-09", + "code": "185465003", + "display": "Weekend visit (procedure)" + }, + { + "system": "http://snomed.info/sct", + "version": "2019-09", + "code": "3391000175108", + "display": "Office visit for pediatric care and assessment (procedure)" + }, + { + "system": "http://snomed.info/sct", + "version": "2019-09", + "code": "439740005", + "display": "Postoperative follow-up visit (procedure)" + }, + { + "system": "http://www.ama-assn.org/go/cpt", + "version": "2020", + "code": "99201", + "display": "Office or other outpatient visit for the evaluation and management of a new patient, which requires these 3 key components: A problem focused history; A problem focused examination; Straightforward medical decision making. Counseling and/or coordination of care with other physicians, other qualified health care professionals, or agencies are provided consistent with the nature of the problem(s) and the patient's and/or family's needs. Usually, the presenting problem(s) are self limited or minor. Typically, 10 minutes are spent face-to-face with the patient and/or family." + }, + { + "system": "http://www.ama-assn.org/go/cpt", + "version": "2020", + "code": "99202", + "display": "Office or other outpatient visit for the evaluation and management of a new patient, which requires these 3 key components: An expanded problem focused history; An expanded problem focused examination; Straightforward medical decision making. Counseling and/or coordination of care with other physicians, other qualified health care professionals, or agencies are provided consistent with the nature of the problem(s) and the patient's and/or family's needs. Usually, the presenting problem(s) are of low to moderate severity. Typically, 20 minutes are spent face-to-face with the patient and/or family." + }, + { + "system": "http://www.ama-assn.org/go/cpt", + "version": "2020", + "code": "99203", + "display": "Office or other outpatient visit for the evaluation and management of a new patient, which requires these 3 key components: A detailed history; A detailed examination; Medical decision making of low complexity. Counseling and/or coordination of care with other physicians, other qualified health care professionals, or agencies are provided consistent with the nature of the problem(s) and the patient's and/or family's needs. Usually, the presenting problem(s) are of moderate severity. Typically, 30 minutes are spent face-to-face with the patient and/or family." + }, + { + "system": "http://www.ama-assn.org/go/cpt", + "version": "2020", + "code": "99204", + "display": "Office or other outpatient visit for the evaluation and management of a new patient, which requires these 3 key components: A comprehensive history; A comprehensive examination; Medical decision making of moderate complexity. Counseling and/or coordination of care with other physicians, other qualified health care professionals, or agencies are provided consistent with the nature of the problem(s) and the patient's and/or family's needs. Usually, the presenting problem(s) are of moderate to high severity. Typically, 45 minutes are spent face-to-face with the patient and/or family." + }, + { + "system": "http://www.ama-assn.org/go/cpt", + "version": "2020", + "code": "99205", + "display": "Office or other outpatient visit for the evaluation and management of a new patient, which requires these 3 key components: A comprehensive history; A comprehensive examination; Medical decision making of high complexity. Counseling and/or coordination of care with other physicians, other qualified health care professionals, or agencies are provided consistent with the nature of the problem(s) and the patient's and/or family's needs. Usually, the presenting problem(s) are of moderate to high severity. Typically, 60 minutes are spent face-to-face with the patient and/or family." + }, + { + "system": "http://www.ama-assn.org/go/cpt", + "version": "2020", + "code": "99212", + "display": "Office or other outpatient visit for the evaluation and management of an established patient, which requires at least 2 of these 3 key components: A problem focused history; A problem focused examination; Straightforward medical decision making. Counseling and/or coordination of care with other physicians, other qualified health care professionals, or agencies are provided consistent with the nature of the problem(s) and the patient's and/or family's needs. Usually, the presenting problem(s) are self limited or minor. Typically, 10 minutes are spent face-to-face with the patient and/or family." + }, + { + "system": "http://www.ama-assn.org/go/cpt", + "version": "2020", + "code": "99213", + "display": "Office or other outpatient visit for the evaluation and management of an established patient, which requires at least 2 of these 3 key components: An expanded problem focused history; An expanded problem focused examination; Medical decision making of low complexity. Counseling and coordination of care with other physicians, other qualified health care professionals, or agencies are provided consistent with the nature of the problem(s) and the patient's and/or family's needs. Usually, the presenting problem(s) are of low to moderate severity. Typically, 15 minutes are spent face-to-face with the patient and/or family." + }, + { + "system": "http://www.ama-assn.org/go/cpt", + "version": "2020", + "code": "99214", + "display": "Office or other outpatient visit for the evaluation and management of an established patient, which requires at least 2 of these 3 key components: A detailed history; A detailed examination; Medical decision making of moderate complexity. Counseling and/or coordination of care with other physicians, other qualified health care professionals, or agencies are provided consistent with the nature of the problem(s) and the patient's and/or family's needs. Usually, the presenting problem(s) are of moderate to high severity. Typically, 25 minutes are spent face-to-face with the patient and/or family." + }, + { + "system": "http://www.ama-assn.org/go/cpt", + "version": "2020", + "code": "99215", + "display": "Office or other outpatient visit for the evaluation and management of an established patient, which requires at least 2 of these 3 key components: A comprehensive history; A comprehensive examination; Medical decision making of high complexity. Counseling and/or coordination of care with other physicians, other qualified health care professionals, or agencies are provided consistent with the nature of the problem(s) and the patient's and/or family's needs. Usually, the presenting problem(s) are of moderate to high severity. Typically, 40 minutes are spent face-to-face with the patient and/or family." + }, + { + "system": "http://snomed.info/sct", + "version": "2018-03", + "code": "30346009", + "display": "Evaluation and management of established outpatient in office or other outpatient facility (procedure)" + }, + { + "system": "http://snomed.info/sct", + "version": "2018-03", + "code": "37894004", + "display": "Evaluation and management of new outpatient in office or other outpatient facility (procedure)" + }, + { + "system": "http://snomed.info/sct", + "version": "2019-09", + "code": "185463005", + "display": "Visit out of hours (procedure)" + }, + { + "system": "http://snomed.info/sct", + "version": "2019-09", + "code": "185464004", + "display": "Out of hours visit - not night visit (procedure)" + }, + { + "system": "http://snomed.info/sct", + "version": "2019-09", + "code": "185465003", + "display": "Weekend visit (procedure)" + }, + { + "system": "http://snomed.info/sct", + "version": "2019-09", + "code": "3391000175108", + "display": "Office visit for pediatric care and assessment (procedure)" + }, + { + "system": "http://snomed.info/sct", + "version": "2019-09", + "code": "439740005", + "display": "Postoperative follow-up visit (procedure)" + }, + { + "system": "http://www.ama-assn.org/go/cpt", + "version": "2020", + "code": "99201", + "display": "Office or other outpatient visit for the evaluation and management of a new patient, which requires these 3 key components: A problem focused history; A problem focused examination; Straightforward medical decision making. Counseling and/or coordination of care with other physicians, other qualified health care professionals, or agencies are provided consistent with the nature of the problem(s) and the patient's and/or family's needs. Usually, the presenting problem(s) are self limited or minor. Typically, 10 minutes are spent face-to-face with the patient and/or family." + }, + { + "system": "http://www.ama-assn.org/go/cpt", + "version": "2020", + "code": "99202", + "display": "Office or other outpatient visit for the evaluation and management of a new patient, which requires these 3 key components: An expanded problem focused history; An expanded problem focused examination; Straightforward medical decision making. Counseling and/or coordination of care with other physicians, other qualified health care professionals, or agencies are provided consistent with the nature of the problem(s) and the patient's and/or family's needs. Usually, the presenting problem(s) are of low to moderate severity. Typically, 20 minutes are spent face-to-face with the patient and/or family." + }, + { + "system": "http://www.ama-assn.org/go/cpt", + "version": "2020", + "code": "99203", + "display": "Office or other outpatient visit for the evaluation and management of a new patient, which requires these 3 key components: A detailed history; A detailed examination; Medical decision making of low complexity. Counseling and/or coordination of care with other physicians, other qualified health care professionals, or agencies are provided consistent with the nature of the problem(s) and the patient's and/or family's needs. Usually, the presenting problem(s) are of moderate severity. Typically, 30 minutes are spent face-to-face with the patient and/or family." + }, + { + "system": "http://www.ama-assn.org/go/cpt", + "version": "2020", + "code": "99204", + "display": "Office or other outpatient visit for the evaluation and management of a new patient, which requires these 3 key components: A comprehensive history; A comprehensive examination; Medical decision making of moderate complexity. Counseling and/or coordination of care with other physicians, other qualified health care professionals, or agencies are provided consistent with the nature of the problem(s) and the patient's and/or family's needs. Usually, the presenting problem(s) are of moderate to high severity. Typically, 45 minutes are spent face-to-face with the patient and/or family." + }, + { + "system": "http://www.ama-assn.org/go/cpt", + "version": "2020", + "code": "99205", + "display": "Office or other outpatient visit for the evaluation and management of a new patient, which requires these 3 key components: A comprehensive history; A comprehensive examination; Medical decision making of high complexity. Counseling and/or coordination of care with other physicians, other qualified health care professionals, or agencies are provided consistent with the nature of the problem(s) and the patient's and/or family's needs. Usually, the presenting problem(s) are of moderate to high severity. Typically, 60 minutes are spent face-to-face with the patient and/or family." + }, + { + "system": "http://www.ama-assn.org/go/cpt", + "version": "2020", + "code": "99212", + "display": "Office or other outpatient visit for the evaluation and management of an established patient, which requires at least 2 of these 3 key components: A problem focused history; A problem focused examination; Straightforward medical decision making. Counseling and/or coordination of care with other physicians, other qualified health care professionals, or agencies are provided consistent with the nature of the problem(s) and the patient's and/or family's needs. Usually, the presenting problem(s) are self limited or minor. Typically, 10 minutes are spent face-to-face with the patient and/or family." + }, + { + "system": "http://www.ama-assn.org/go/cpt", + "version": "2020", + "code": "99213", + "display": "Office or other outpatient visit for the evaluation and management of an established patient, which requires at least 2 of these 3 key components: An expanded problem focused history; An expanded problem focused examination; Medical decision making of low complexity. Counseling and coordination of care with other physicians, other qualified health care professionals, or agencies are provided consistent with the nature of the problem(s) and the patient's and/or family's needs. Usually, the presenting problem(s) are of low to moderate severity. Typically, 15 minutes are spent face-to-face with the patient and/or family." + }, + { + "system": "http://www.ama-assn.org/go/cpt", + "version": "2020", + "code": "99214", + "display": "Office or other outpatient visit for the evaluation and management of an established patient, which requires at least 2 of these 3 key components: A detailed history; A detailed examination; Medical decision making of moderate complexity. Counseling and/or coordination of care with other physicians, other qualified health care professionals, or agencies are provided consistent with the nature of the problem(s) and the patient's and/or family's needs. Usually, the presenting problem(s) are of moderate to high severity. Typically, 25 minutes are spent face-to-face with the patient and/or family." + }, + { + "system": "http://www.ama-assn.org/go/cpt", + "version": "2020", + "code": "99215", + "display": "Office or other outpatient visit for the evaluation and management of an established patient, which requires at least 2 of these 3 key components: A comprehensive history; A comprehensive examination; Medical decision making of high complexity. Counseling and/or coordination of care with other physicians, other qualified health care professionals, or agencies are provided consistent with the nature of the problem(s) and the patient's and/or family's needs. Usually, the presenting problem(s) are of moderate to high severity. Typically, 40 minutes are spent face-to-face with the patient and/or family." + }, + { + "system": "http://snomed.info/sct", + "version": "2018-03", + "code": "30346009", + "display": "Evaluation and management of established outpatient in office or other outpatient facility (procedure)" + }, + { + "system": "http://snomed.info/sct", + "version": "2018-03", + "code": "37894004", + "display": "Evaluation and management of new outpatient in office or other outpatient facility (procedure)" + }, + { + "system": "http://snomed.info/sct", + "version": "2019-09", + "code": "185463005", + "display": "Visit out of hours (procedure)" + }, + { + "system": "http://snomed.info/sct", + "version": "2019-09", + "code": "185464004", + "display": "Out of hours visit - not night visit (procedure)" + }, + { + "system": "http://snomed.info/sct", + "version": "2019-09", + "code": "185465003", + "display": "Weekend visit (procedure)" + }, + { + "system": "http://snomed.info/sct", + "version": "2019-09", + "code": "3391000175108", + "display": "Office visit for pediatric care and assessment (procedure)" + }, + { + "system": "http://snomed.info/sct", + "version": "2019-09", + "code": "439740005", + "display": "Postoperative follow-up visit (procedure)" + }, + { + "system": "http://www.ama-assn.org/go/cpt", + "version": "2020", + "code": "99201", + "display": "Office or other outpatient visit for the evaluation and management of a new patient, which requires these 3 key components: A problem focused history; A problem focused examination; Straightforward medical decision making. Counseling and/or coordination of care with other physicians, other qualified health care professionals, or agencies are provided consistent with the nature of the problem(s) and the patient's and/or family's needs. Usually, the presenting problem(s) are self limited or minor. Typically, 10 minutes are spent face-to-face with the patient and/or family." + }, + { + "system": "http://www.ama-assn.org/go/cpt", + "version": "2020", + "code": "99202", + "display": "Office or other outpatient visit for the evaluation and management of a new patient, which requires these 3 key components: An expanded problem focused history; An expanded problem focused examination; Straightforward medical decision making. Counseling and/or coordination of care with other physicians, other qualified health care professionals, or agencies are provided consistent with the nature of the problem(s) and the patient's and/or family's needs. Usually, the presenting problem(s) are of low to moderate severity. Typically, 20 minutes are spent face-to-face with the patient and/or family." + }, + { + "system": "http://www.ama-assn.org/go/cpt", + "version": "2020", + "code": "99203", + "display": "Office or other outpatient visit for the evaluation and management of a new patient, which requires these 3 key components: A detailed history; A detailed examination; Medical decision making of low complexity. Counseling and/or coordination of care with other physicians, other qualified health care professionals, or agencies are provided consistent with the nature of the problem(s) and the patient's and/or family's needs. Usually, the presenting problem(s) are of moderate severity. Typically, 30 minutes are spent face-to-face with the patient and/or family." + }, + { + "system": "http://www.ama-assn.org/go/cpt", + "version": "2020", + "code": "99204", + "display": "Office or other outpatient visit for the evaluation and management of a new patient, which requires these 3 key components: A comprehensive history; A comprehensive examination; Medical decision making of moderate complexity. Counseling and/or coordination of care with other physicians, other qualified health care professionals, or agencies are provided consistent with the nature of the problem(s) and the patient's and/or family's needs. Usually, the presenting problem(s) are of moderate to high severity. Typically, 45 minutes are spent face-to-face with the patient and/or family." + }, + { + "system": "http://www.ama-assn.org/go/cpt", + "version": "2020", + "code": "99205", + "display": "Office or other outpatient visit for the evaluation and management of a new patient, which requires these 3 key components: A comprehensive history; A comprehensive examination; Medical decision making of high complexity. Counseling and/or coordination of care with other physicians, other qualified health care professionals, or agencies are provided consistent with the nature of the problem(s) and the patient's and/or family's needs. Usually, the presenting problem(s) are of moderate to high severity. Typically, 60 minutes are spent face-to-face with the patient and/or family." + }, + { + "system": "http://www.ama-assn.org/go/cpt", + "version": "2020", + "code": "99212", + "display": "Office or other outpatient visit for the evaluation and management of an established patient, which requires at least 2 of these 3 key components: A problem focused history; A problem focused examination; Straightforward medical decision making. Counseling and/or coordination of care with other physicians, other qualified health care professionals, or agencies are provided consistent with the nature of the problem(s) and the patient's and/or family's needs. Usually, the presenting problem(s) are self limited or minor. Typically, 10 minutes are spent face-to-face with the patient and/or family." + }, + { + "system": "http://www.ama-assn.org/go/cpt", + "version": "2020", + "code": "99213", + "display": "Office or other outpatient visit for the evaluation and management of an established patient, which requires at least 2 of these 3 key components: An expanded problem focused history; An expanded problem focused examination; Medical decision making of low complexity. Counseling and coordination of care with other physicians, other qualified health care professionals, or agencies are provided consistent with the nature of the problem(s) and the patient's and/or family's needs. Usually, the presenting problem(s) are of low to moderate severity. Typically, 15 minutes are spent face-to-face with the patient and/or family." + }, + { + "system": "http://www.ama-assn.org/go/cpt", + "version": "2020", + "code": "99214", + "display": "Office or other outpatient visit for the evaluation and management of an established patient, which requires at least 2 of these 3 key components: A detailed history; A detailed examination; Medical decision making of moderate complexity. Counseling and/or coordination of care with other physicians, other qualified health care professionals, or agencies are provided consistent with the nature of the problem(s) and the patient's and/or family's needs. Usually, the presenting problem(s) are of moderate to high severity. Typically, 25 minutes are spent face-to-face with the patient and/or family." + }, + { + "system": "http://www.ama-assn.org/go/cpt", + "version": "2020", + "code": "99215", + "display": "Office or other outpatient visit for the evaluation and management of an established patient, which requires at least 2 of these 3 key components: A comprehensive history; A comprehensive examination; Medical decision making of high complexity. Counseling and/or coordination of care with other physicians, other qualified health care professionals, or agencies are provided consistent with the nature of the problem(s) and the patient's and/or family's needs. Usually, the presenting problem(s) are of moderate to high severity. Typically, 40 minutes are spent face-to-face with the patient and/or family." + }, + { + "system": "http://snomed.info/sct", + "version": "2018-03", + "code": "30346009", + "display": "Evaluation and management of established outpatient in office or other outpatient facility (procedure)" + }, + { + "system": "http://snomed.info/sct", + "version": "2018-03", + "code": "37894004", + "display": "Evaluation and management of new outpatient in office or other outpatient facility (procedure)" + }, + { + "system": "http://snomed.info/sct", + "version": "2019-09", + "code": "185463005", + "display": "Visit out of hours (procedure)" + }, + { + "system": "http://snomed.info/sct", + "version": "2019-09", + "code": "185464004", + "display": "Out of hours visit - not night visit (procedure)" + }, + { + "system": "http://snomed.info/sct", + "version": "2019-09", + "code": "185465003", + "display": "Weekend visit (procedure)" + }, + { + "system": "http://snomed.info/sct", + "version": "2019-09", + "code": "3391000175108", + "display": "Office visit for pediatric care and assessment (procedure)" + }, + { + "system": "http://snomed.info/sct", + "version": "2019-09", + "code": "439740005", + "display": "Postoperative follow-up visit (procedure)" + }, + { + "system": "http://www.ama-assn.org/go/cpt", + "version": "2020", + "code": "99201", + "display": "Office or other outpatient visit for the evaluation and management of a new patient, which requires these 3 key components: A problem focused history; A problem focused examination; Straightforward medical decision making. Counseling and/or coordination of care with other physicians, other qualified health care professionals, or agencies are provided consistent with the nature of the problem(s) and the patient's and/or family's needs. Usually, the presenting problem(s) are self limited or minor. Typically, 10 minutes are spent face-to-face with the patient and/or family." + }, + { + "system": "http://www.ama-assn.org/go/cpt", + "version": "2020", + "code": "99202", + "display": "Office or other outpatient visit for the evaluation and management of a new patient, which requires these 3 key components: An expanded problem focused history; An expanded problem focused examination; Straightforward medical decision making. Counseling and/or coordination of care with other physicians, other qualified health care professionals, or agencies are provided consistent with the nature of the problem(s) and the patient's and/or family's needs. Usually, the presenting problem(s) are of low to moderate severity. Typically, 20 minutes are spent face-to-face with the patient and/or family." + }, + { + "system": "http://www.ama-assn.org/go/cpt", + "version": "2020", + "code": "99203", + "display": "Office or other outpatient visit for the evaluation and management of a new patient, which requires these 3 key components: A detailed history; A detailed examination; Medical decision making of low complexity. Counseling and/or coordination of care with other physicians, other qualified health care professionals, or agencies are provided consistent with the nature of the problem(s) and the patient's and/or family's needs. Usually, the presenting problem(s) are of moderate severity. Typically, 30 minutes are spent face-to-face with the patient and/or family." + }, + { + "system": "http://www.ama-assn.org/go/cpt", + "version": "2020", + "code": "99204", + "display": "Office or other outpatient visit for the evaluation and management of a new patient, which requires these 3 key components: A comprehensive history; A comprehensive examination; Medical decision making of moderate complexity. Counseling and/or coordination of care with other physicians, other qualified health care professionals, or agencies are provided consistent with the nature of the problem(s) and the patient's and/or family's needs. Usually, the presenting problem(s) are of moderate to high severity. Typically, 45 minutes are spent face-to-face with the patient and/or family." + }, + { + "system": "http://www.ama-assn.org/go/cpt", + "version": "2020", + "code": "99205", + "display": "Office or other outpatient visit for the evaluation and management of a new patient, which requires these 3 key components: A comprehensive history; A comprehensive examination; Medical decision making of high complexity. Counseling and/or coordination of care with other physicians, other qualified health care professionals, or agencies are provided consistent with the nature of the problem(s) and the patient's and/or family's needs. Usually, the presenting problem(s) are of moderate to high severity. Typically, 60 minutes are spent face-to-face with the patient and/or family." + }, + { + "system": "http://www.ama-assn.org/go/cpt", + "version": "2020", + "code": "99212", + "display": "Office or other outpatient visit for the evaluation and management of an established patient, which requires at least 2 of these 3 key components: A problem focused history; A problem focused examination; Straightforward medical decision making. Counseling and/or coordination of care with other physicians, other qualified health care professionals, or agencies are provided consistent with the nature of the problem(s) and the patient's and/or family's needs. Usually, the presenting problem(s) are self limited or minor. Typically, 10 minutes are spent face-to-face with the patient and/or family." + }, + { + "system": "http://www.ama-assn.org/go/cpt", + "version": "2020", + "code": "99213", + "display": "Office or other outpatient visit for the evaluation and management of an established patient, which requires at least 2 of these 3 key components: An expanded problem focused history; An expanded problem focused examination; Medical decision making of low complexity. Counseling and coordination of care with other physicians, other qualified health care professionals, or agencies are provided consistent with the nature of the problem(s) and the patient's and/or family's needs. Usually, the presenting problem(s) are of low to moderate severity. Typically, 15 minutes are spent face-to-face with the patient and/or family." + }, + { + "system": "http://www.ama-assn.org/go/cpt", + "version": "2020", + "code": "99214", + "display": "Office or other outpatient visit for the evaluation and management of an established patient, which requires at least 2 of these 3 key components: A detailed history; A detailed examination; Medical decision making of moderate complexity. Counseling and/or coordination of care with other physicians, other qualified health care professionals, or agencies are provided consistent with the nature of the problem(s) and the patient's and/or family's needs. Usually, the presenting problem(s) are of moderate to high severity. Typically, 25 minutes are spent face-to-face with the patient and/or family." + }, + { + "system": "http://www.ama-assn.org/go/cpt", + "version": "2020", + "code": "99215", + "display": "Office or other outpatient visit for the evaluation and management of an established patient, which requires at least 2 of these 3 key components: A comprehensive history; A comprehensive examination; Medical decision making of high complexity. Counseling and/or coordination of care with other physicians, other qualified health care professionals, or agencies are provided consistent with the nature of the problem(s) and the patient's and/or family's needs. Usually, the presenting problem(s) are of moderate to high severity. Typically, 40 minutes are spent face-to-face with the patient and/or family." + }, + { + "system": "http://snomed.info/sct", + "version": "2018-03", + "code": "30346009", + "display": "Evaluation and management of established outpatient in office or other outpatient facility (procedure)" + }, + { + "system": "http://snomed.info/sct", + "version": "2018-03", + "code": "37894004", + "display": "Evaluation and management of new outpatient in office or other outpatient facility (procedure)" + }, + { + "system": "http://snomed.info/sct", + "version": "2019-09", + "code": "185463005", + "display": "Visit out of hours (procedure)" + }, + { + "system": "http://snomed.info/sct", + "version": "2019-09", + "code": "185464004", + "display": "Out of hours visit - not night visit (procedure)" + }, + { + "system": "http://snomed.info/sct", + "version": "2019-09", + "code": "185465003", + "display": "Weekend visit (procedure)" + }, + { + "system": "http://snomed.info/sct", + "version": "2019-09", + "code": "3391000175108", + "display": "Office visit for pediatric care and assessment (procedure)" + }, + { + "system": "http://snomed.info/sct", + "version": "2019-09", + "code": "439740005", + "display": "Postoperative follow-up visit (procedure)" + }, + { + "system": "http://www.ama-assn.org/go/cpt", + "version": "2020", + "code": "99201", + "display": "Office or other outpatient visit for the evaluation and management of a new patient, which requires these 3 key components: A problem focused history; A problem focused examination; Straightforward medical decision making. Counseling and/or coordination of care with other physicians, other qualified health care professionals, or agencies are provided consistent with the nature of the problem(s) and the patient's and/or family's needs. Usually, the presenting problem(s) are self limited or minor. Typically, 10 minutes are spent face-to-face with the patient and/or family." + }, + { + "system": "http://www.ama-assn.org/go/cpt", + "version": "2020", + "code": "99202", + "display": "Office or other outpatient visit for the evaluation and management of a new patient, which requires these 3 key components: An expanded problem focused history; An expanded problem focused examination; Straightforward medical decision making. Counseling and/or coordination of care with other physicians, other qualified health care professionals, or agencies are provided consistent with the nature of the problem(s) and the patient's and/or family's needs. Usually, the presenting problem(s) are of low to moderate severity. Typically, 20 minutes are spent face-to-face with the patient and/or family." + }, + { + "system": "http://www.ama-assn.org/go/cpt", + "version": "2020", + "code": "99203", + "display": "Office or other outpatient visit for the evaluation and management of a new patient, which requires these 3 key components: A detailed history; A detailed examination; Medical decision making of low complexity. Counseling and/or coordination of care with other physicians, other qualified health care professionals, or agencies are provided consistent with the nature of the problem(s) and the patient's and/or family's needs. Usually, the presenting problem(s) are of moderate severity. Typically, 30 minutes are spent face-to-face with the patient and/or family." + }, + { + "system": "http://www.ama-assn.org/go/cpt", + "version": "2020", + "code": "99204", + "display": "Office or other outpatient visit for the evaluation and management of a new patient, which requires these 3 key components: A comprehensive history; A comprehensive examination; Medical decision making of moderate complexity. Counseling and/or coordination of care with other physicians, other qualified health care professionals, or agencies are provided consistent with the nature of the problem(s) and the patient's and/or family's needs. Usually, the presenting problem(s) are of moderate to high severity. Typically, 45 minutes are spent face-to-face with the patient and/or family." + }, + { + "system": "http://www.ama-assn.org/go/cpt", + "version": "2020", + "code": "99205", + "display": "Office or other outpatient visit for the evaluation and management of a new patient, which requires these 3 key components: A comprehensive history; A comprehensive examination; Medical decision making of high complexity. Counseling and/or coordination of care with other physicians, other qualified health care professionals, or agencies are provided consistent with the nature of the problem(s) and the patient's and/or family's needs. Usually, the presenting problem(s) are of moderate to high severity. Typically, 60 minutes are spent face-to-face with the patient and/or family." + }, + { + "system": "http://www.ama-assn.org/go/cpt", + "version": "2020", + "code": "99212", + "display": "Office or other outpatient visit for the evaluation and management of an established patient, which requires at least 2 of these 3 key components: A problem focused history; A problem focused examination; Straightforward medical decision making. Counseling and/or coordination of care with other physicians, other qualified health care professionals, or agencies are provided consistent with the nature of the problem(s) and the patient's and/or family's needs. Usually, the presenting problem(s) are self limited or minor. Typically, 10 minutes are spent face-to-face with the patient and/or family." + }, + { + "system": "http://www.ama-assn.org/go/cpt", + "version": "2020", + "code": "99213", + "display": "Office or other outpatient visit for the evaluation and management of an established patient, which requires at least 2 of these 3 key components: An expanded problem focused history; An expanded problem focused examination; Medical decision making of low complexity. Counseling and coordination of care with other physicians, other qualified health care professionals, or agencies are provided consistent with the nature of the problem(s) and the patient's and/or family's needs. Usually, the presenting problem(s) are of low to moderate severity. Typically, 15 minutes are spent face-to-face with the patient and/or family." + }, + { + "system": "http://www.ama-assn.org/go/cpt", + "version": "2020", + "code": "99214", + "display": "Office or other outpatient visit for the evaluation and management of an established patient, which requires at least 2 of these 3 key components: A detailed history; A detailed examination; Medical decision making of moderate complexity. Counseling and/or coordination of care with other physicians, other qualified health care professionals, or agencies are provided consistent with the nature of the problem(s) and the patient's and/or family's needs. Usually, the presenting problem(s) are of moderate to high severity. Typically, 25 minutes are spent face-to-face with the patient and/or family." + }, + { + "system": "http://www.ama-assn.org/go/cpt", + "version": "2020", + "code": "99215", + "display": "Office or other outpatient visit for the evaluation and management of an established patient, which requires at least 2 of these 3 key components: A comprehensive history; A comprehensive examination; Medical decision making of high complexity. Counseling and/or coordination of care with other physicians, other qualified health care professionals, or agencies are provided consistent with the nature of the problem(s) and the patient's and/or family's needs. Usually, the presenting problem(s) are of moderate to high severity. Typically, 40 minutes are spent face-to-face with the patient and/or family." + }, + { + "system": "http://snomed.info/sct", + "version": "2018-03", + "code": "30346009", + "display": "Evaluation and management of established outpatient in office or other outpatient facility (procedure)" + }, + { + "system": "http://snomed.info/sct", + "version": "2018-03", + "code": "37894004", + "display": "Evaluation and management of new outpatient in office or other outpatient facility (procedure)" + }, + { + "system": "http://snomed.info/sct", + "version": "2019-09", + "code": "185463005", + "display": "Visit out of hours (procedure)" + }, + { + "system": "http://snomed.info/sct", + "version": "2019-09", + "code": "185464004", + "display": "Out of hours visit - not night visit (procedure)" + }, + { + "system": "http://snomed.info/sct", + "version": "2019-09", + "code": "185465003", + "display": "Weekend visit (procedure)" + }, + { + "system": "http://snomed.info/sct", + "version": "2019-09", + "code": "3391000175108", + "display": "Office visit for pediatric care and assessment (procedure)" + }, + { + "system": "http://snomed.info/sct", + "version": "2019-09", + "code": "439740005", + "display": "Postoperative follow-up visit (procedure)" + }, + { + "system": "http://www.ama-assn.org/go/cpt", + "version": "2020", + "code": "99201", + "display": "Office or other outpatient visit for the evaluation and management of a new patient, which requires these 3 key components: A problem focused history; A problem focused examination; Straightforward medical decision making. Counseling and/or coordination of care with other physicians, other qualified health care professionals, or agencies are provided consistent with the nature of the problem(s) and the patient's and/or family's needs. Usually, the presenting problem(s) are self limited or minor. Typically, 10 minutes are spent face-to-face with the patient and/or family." + }, + { + "system": "http://www.ama-assn.org/go/cpt", + "version": "2020", + "code": "99202", + "display": "Office or other outpatient visit for the evaluation and management of a new patient, which requires these 3 key components: An expanded problem focused history; An expanded problem focused examination; Straightforward medical decision making. Counseling and/or coordination of care with other physicians, other qualified health care professionals, or agencies are provided consistent with the nature of the problem(s) and the patient's and/or family's needs. Usually, the presenting problem(s) are of low to moderate severity. Typically, 20 minutes are spent face-to-face with the patient and/or family." + }, + { + "system": "http://www.ama-assn.org/go/cpt", + "version": "2020", + "code": "99203", + "display": "Office or other outpatient visit for the evaluation and management of a new patient, which requires these 3 key components: A detailed history; A detailed examination; Medical decision making of low complexity. Counseling and/or coordination of care with other physicians, other qualified health care professionals, or agencies are provided consistent with the nature of the problem(s) and the patient's and/or family's needs. Usually, the presenting problem(s) are of moderate severity. Typically, 30 minutes are spent face-to-face with the patient and/or family." + }, + { + "system": "http://www.ama-assn.org/go/cpt", + "version": "2020", + "code": "99204", + "display": "Office or other outpatient visit for the evaluation and management of a new patient, which requires these 3 key components: A comprehensive history; A comprehensive examination; Medical decision making of moderate complexity. Counseling and/or coordination of care with other physicians, other qualified health care professionals, or agencies are provided consistent with the nature of the problem(s) and the patient's and/or family's needs. Usually, the presenting problem(s) are of moderate to high severity. Typically, 45 minutes are spent face-to-face with the patient and/or family." + }, + { + "system": "http://www.ama-assn.org/go/cpt", + "version": "2020", + "code": "99205", + "display": "Office or other outpatient visit for the evaluation and management of a new patient, which requires these 3 key components: A comprehensive history; A comprehensive examination; Medical decision making of high complexity. Counseling and/or coordination of care with other physicians, other qualified health care professionals, or agencies are provided consistent with the nature of the problem(s) and the patient's and/or family's needs. Usually, the presenting problem(s) are of moderate to high severity. Typically, 60 minutes are spent face-to-face with the patient and/or family." + }, + { + "system": "http://www.ama-assn.org/go/cpt", + "version": "2020", + "code": "99212", + "display": "Office or other outpatient visit for the evaluation and management of an established patient, which requires at least 2 of these 3 key components: A problem focused history; A problem focused examination; Straightforward medical decision making. Counseling and/or coordination of care with other physicians, other qualified health care professionals, or agencies are provided consistent with the nature of the problem(s) and the patient's and/or family's needs. Usually, the presenting problem(s) are self limited or minor. Typically, 10 minutes are spent face-to-face with the patient and/or family." + }, + { + "system": "http://www.ama-assn.org/go/cpt", + "version": "2020", + "code": "99213", + "display": "Office or other outpatient visit for the evaluation and management of an established patient, which requires at least 2 of these 3 key components: An expanded problem focused history; An expanded problem focused examination; Medical decision making of low complexity. Counseling and coordination of care with other physicians, other qualified health care professionals, or agencies are provided consistent with the nature of the problem(s) and the patient's and/or family's needs. Usually, the presenting problem(s) are of low to moderate severity. Typically, 15 minutes are spent face-to-face with the patient and/or family." + }, + { + "system": "http://www.ama-assn.org/go/cpt", + "version": "2020", + "code": "99214", + "display": "Office or other outpatient visit for the evaluation and management of an established patient, which requires at least 2 of these 3 key components: A detailed history; A detailed examination; Medical decision making of moderate complexity. Counseling and/or coordination of care with other physicians, other qualified health care professionals, or agencies are provided consistent with the nature of the problem(s) and the patient's and/or family's needs. Usually, the presenting problem(s) are of moderate to high severity. Typically, 25 minutes are spent face-to-face with the patient and/or family." + }, + { + "system": "http://www.ama-assn.org/go/cpt", + "version": "2020", + "code": "99215", + "display": "Office or other outpatient visit for the evaluation and management of an established patient, which requires at least 2 of these 3 key components: A comprehensive history; A comprehensive examination; Medical decision making of high complexity. Counseling and/or coordination of care with other physicians, other qualified health care professionals, or agencies are provided consistent with the nature of the problem(s) and the patient's and/or family's needs. Usually, the presenting problem(s) are of moderate to high severity. Typically, 40 minutes are spent face-to-face with the patient and/or family." + }, + { + "system": "http://snomed.info/sct", + "version": "2018-03", + "code": "30346009", + "display": "Evaluation and management of established outpatient in office or other outpatient facility (procedure)" + }, + { + "system": "http://snomed.info/sct", + "version": "2018-03", + "code": "37894004", + "display": "Evaluation and management of new outpatient in office or other outpatient facility (procedure)" + }, + { + "system": "http://snomed.info/sct", + "version": "2019-09", + "code": "185463005", + "display": "Visit out of hours (procedure)" + }, + { + "system": "http://snomed.info/sct", + "version": "2019-09", + "code": "185464004", + "display": "Out of hours visit - not night visit (procedure)" + }, + { + "system": "http://snomed.info/sct", + "version": "2019-09", + "code": "185465003", + "display": "Weekend visit (procedure)" + }, + { + "system": "http://snomed.info/sct", + "version": "2019-09", + "code": "3391000175108", + "display": "Office visit for pediatric care and assessment (procedure)" + }, + { + "system": "http://snomed.info/sct", + "version": "2019-09", + "code": "439740005", + "display": "Postoperative follow-up visit (procedure)" + }, + { + "system": "http://www.ama-assn.org/go/cpt", + "version": "2020", + "code": "99201", + "display": "Office or other outpatient visit for the evaluation and management of a new patient, which requires these 3 key components: A problem focused history; A problem focused examination; Straightforward medical decision making. Counseling and/or coordination of care with other physicians, other qualified health care professionals, or agencies are provided consistent with the nature of the problem(s) and the patient's and/or family's needs. Usually, the presenting problem(s) are self limited or minor. Typically, 10 minutes are spent face-to-face with the patient and/or family." + }, + { + "system": "http://www.ama-assn.org/go/cpt", + "version": "2020", + "code": "99202", + "display": "Office or other outpatient visit for the evaluation and management of a new patient, which requires these 3 key components: An expanded problem focused history; An expanded problem focused examination; Straightforward medical decision making. Counseling and/or coordination of care with other physicians, other qualified health care professionals, or agencies are provided consistent with the nature of the problem(s) and the patient's and/or family's needs. Usually, the presenting problem(s) are of low to moderate severity. Typically, 20 minutes are spent face-to-face with the patient and/or family." + }, + { + "system": "http://www.ama-assn.org/go/cpt", + "version": "2020", + "code": "99203", + "display": "Office or other outpatient visit for the evaluation and management of a new patient, which requires these 3 key components: A detailed history; A detailed examination; Medical decision making of low complexity. Counseling and/or coordination of care with other physicians, other qualified health care professionals, or agencies are provided consistent with the nature of the problem(s) and the patient's and/or family's needs. Usually, the presenting problem(s) are of moderate severity. Typically, 30 minutes are spent face-to-face with the patient and/or family." + }, + { + "system": "http://www.ama-assn.org/go/cpt", + "version": "2020", + "code": "99204", + "display": "Office or other outpatient visit for the evaluation and management of a new patient, which requires these 3 key components: A comprehensive history; A comprehensive examination; Medical decision making of moderate complexity. Counseling and/or coordination of care with other physicians, other qualified health care professionals, or agencies are provided consistent with the nature of the problem(s) and the patient's and/or family's needs. Usually, the presenting problem(s) are of moderate to high severity. Typically, 45 minutes are spent face-to-face with the patient and/or family." + }, + { + "system": "http://www.ama-assn.org/go/cpt", + "version": "2020", + "code": "99205", + "display": "Office or other outpatient visit for the evaluation and management of a new patient, which requires these 3 key components: A comprehensive history; A comprehensive examination; Medical decision making of high complexity. Counseling and/or coordination of care with other physicians, other qualified health care professionals, or agencies are provided consistent with the nature of the problem(s) and the patient's and/or family's needs. Usually, the presenting problem(s) are of moderate to high severity. Typically, 60 minutes are spent face-to-face with the patient and/or family." + }, + { + "system": "http://www.ama-assn.org/go/cpt", + "version": "2020", + "code": "99212", + "display": "Office or other outpatient visit for the evaluation and management of an established patient, which requires at least 2 of these 3 key components: A problem focused history; A problem focused examination; Straightforward medical decision making. Counseling and/or coordination of care with other physicians, other qualified health care professionals, or agencies are provided consistent with the nature of the problem(s) and the patient's and/or family's needs. Usually, the presenting problem(s) are self limited or minor. Typically, 10 minutes are spent face-to-face with the patient and/or family." + }, + { + "system": "http://www.ama-assn.org/go/cpt", + "version": "2020", + "code": "99213", + "display": "Office or other outpatient visit for the evaluation and management of an established patient, which requires at least 2 of these 3 key components: An expanded problem focused history; An expanded problem focused examination; Medical decision making of low complexity. Counseling and coordination of care with other physicians, other qualified health care professionals, or agencies are provided consistent with the nature of the problem(s) and the patient's and/or family's needs. Usually, the presenting problem(s) are of low to moderate severity. Typically, 15 minutes are spent face-to-face with the patient and/or family." + }, + { + "system": "http://www.ama-assn.org/go/cpt", + "version": "2020", + "code": "99214", + "display": "Office or other outpatient visit for the evaluation and management of an established patient, which requires at least 2 of these 3 key components: A detailed history; A detailed examination; Medical decision making of moderate complexity. Counseling and/or coordination of care with other physicians, other qualified health care professionals, or agencies are provided consistent with the nature of the problem(s) and the patient's and/or family's needs. Usually, the presenting problem(s) are of moderate to high severity. Typically, 25 minutes are spent face-to-face with the patient and/or family." + }, + { + "system": "http://www.ama-assn.org/go/cpt", + "version": "2020", + "code": "99215", + "display": "Office or other outpatient visit for the evaluation and management of an established patient, which requires at least 2 of these 3 key components: A comprehensive history; A comprehensive examination; Medical decision making of high complexity. Counseling and/or coordination of care with other physicians, other qualified health care professionals, or agencies are provided consistent with the nature of the problem(s) and the patient's and/or family's needs. Usually, the presenting problem(s) are of moderate to high severity. Typically, 40 minutes are spent face-to-face with the patient and/or family." + }, + { + "system": "http://snomed.info/sct", + "version": "2018-03", + "code": "30346009", + "display": "Evaluation and management of established outpatient in office or other outpatient facility (procedure)" + }, + { + "system": "http://snomed.info/sct", + "version": "2018-03", + "code": "37894004", + "display": "Evaluation and management of new outpatient in office or other outpatient facility (procedure)" + }, + { + "system": "http://snomed.info/sct", + "version": "2019-09", + "code": "185463005", + "display": "Visit out of hours (procedure)" + }, + { + "system": "http://snomed.info/sct", + "version": "2019-09", + "code": "185464004", + "display": "Out of hours visit - not night visit (procedure)" + }, + { + "system": "http://snomed.info/sct", + "version": "2019-09", + "code": "185465003", + "display": "Weekend visit (procedure)" + }, + { + "system": "http://snomed.info/sct", + "version": "2019-09", + "code": "3391000175108", + "display": "Office visit for pediatric care and assessment (procedure)" + }, + { + "system": "http://snomed.info/sct", + "version": "2019-09", + "code": "439740005", + "display": "Postoperative follow-up visit (procedure)" + }, + { + "system": "http://www.ama-assn.org/go/cpt", + "version": "2020", + "code": "99201", + "display": "Office or other outpatient visit for the evaluation and management of a new patient, which requires these 3 key components: A problem focused history; A problem focused examination; Straightforward medical decision making. Counseling and/or coordination of care with other physicians, other qualified health care professionals, or agencies are provided consistent with the nature of the problem(s) and the patient's and/or family's needs. Usually, the presenting problem(s) are self limited or minor. Typically, 10 minutes are spent face-to-face with the patient and/or family." + }, + { + "system": "http://www.ama-assn.org/go/cpt", + "version": "2020", + "code": "99202", + "display": "Office or other outpatient visit for the evaluation and management of a new patient, which requires these 3 key components: An expanded problem focused history; An expanded problem focused examination; Straightforward medical decision making. Counseling and/or coordination of care with other physicians, other qualified health care professionals, or agencies are provided consistent with the nature of the problem(s) and the patient's and/or family's needs. Usually, the presenting problem(s) are of low to moderate severity. Typically, 20 minutes are spent face-to-face with the patient and/or family." + }, + { + "system": "http://www.ama-assn.org/go/cpt", + "version": "2020", + "code": "99203", + "display": "Office or other outpatient visit for the evaluation and management of a new patient, which requires these 3 key components: A detailed history; A detailed examination; Medical decision making of low complexity. Counseling and/or coordination of care with other physicians, other qualified health care professionals, or agencies are provided consistent with the nature of the problem(s) and the patient's and/or family's needs. Usually, the presenting problem(s) are of moderate severity. Typically, 30 minutes are spent face-to-face with the patient and/or family." + }, + { + "system": "http://www.ama-assn.org/go/cpt", + "version": "2020", + "code": "99204", + "display": "Office or other outpatient visit for the evaluation and management of a new patient, which requires these 3 key components: A comprehensive history; A comprehensive examination; Medical decision making of moderate complexity. Counseling and/or coordination of care with other physicians, other qualified health care professionals, or agencies are provided consistent with the nature of the problem(s) and the patient's and/or family's needs. Usually, the presenting problem(s) are of moderate to high severity. Typically, 45 minutes are spent face-to-face with the patient and/or family." + }, + { + "system": "http://www.ama-assn.org/go/cpt", + "version": "2020", + "code": "99205", + "display": "Office or other outpatient visit for the evaluation and management of a new patient, which requires these 3 key components: A comprehensive history; A comprehensive examination; Medical decision making of high complexity. Counseling and/or coordination of care with other physicians, other qualified health care professionals, or agencies are provided consistent with the nature of the problem(s) and the patient's and/or family's needs. Usually, the presenting problem(s) are of moderate to high severity. Typically, 60 minutes are spent face-to-face with the patient and/or family." + }, + { + "system": "http://www.ama-assn.org/go/cpt", + "version": "2020", + "code": "99212", + "display": "Office or other outpatient visit for the evaluation and management of an established patient, which requires at least 2 of these 3 key components: A problem focused history; A problem focused examination; Straightforward medical decision making. Counseling and/or coordination of care with other physicians, other qualified health care professionals, or agencies are provided consistent with the nature of the problem(s) and the patient's and/or family's needs. Usually, the presenting problem(s) are self limited or minor. Typically, 10 minutes are spent face-to-face with the patient and/or family." + }, + { + "system": "http://www.ama-assn.org/go/cpt", + "version": "2020", + "code": "99213", + "display": "Office or other outpatient visit for the evaluation and management of an established patient, which requires at least 2 of these 3 key components: An expanded problem focused history; An expanded problem focused examination; Medical decision making of low complexity. Counseling and coordination of care with other physicians, other qualified health care professionals, or agencies are provided consistent with the nature of the problem(s) and the patient's and/or family's needs. Usually, the presenting problem(s) are of low to moderate severity. Typically, 15 minutes are spent face-to-face with the patient and/or family." + }, + { + "system": "http://www.ama-assn.org/go/cpt", + "version": "2020", + "code": "99214", + "display": "Office or other outpatient visit for the evaluation and management of an established patient, which requires at least 2 of these 3 key components: A detailed history; A detailed examination; Medical decision making of moderate complexity. Counseling and/or coordination of care with other physicians, other qualified health care professionals, or agencies are provided consistent with the nature of the problem(s) and the patient's and/or family's needs. Usually, the presenting problem(s) are of moderate to high severity. Typically, 25 minutes are spent face-to-face with the patient and/or family." + }, + { + "system": "http://www.ama-assn.org/go/cpt", + "version": "2020", + "code": "99215", + "display": "Office or other outpatient visit for the evaluation and management of an established patient, which requires at least 2 of these 3 key components: A comprehensive history; A comprehensive examination; Medical decision making of high complexity. Counseling and/or coordination of care with other physicians, other qualified health care professionals, or agencies are provided consistent with the nature of the problem(s) and the patient's and/or family's needs. Usually, the presenting problem(s) are of moderate to high severity. Typically, 40 minutes are spent face-to-face with the patient and/or family." + }, + { + "system": "http://snomed.info/sct", + "version": "2018-03", + "code": "30346009", + "display": "Evaluation and management of established outpatient in office or other outpatient facility (procedure)" + }, + { + "system": "http://snomed.info/sct", + "version": "2018-03", + "code": "37894004", + "display": "Evaluation and management of new outpatient in office or other outpatient facility (procedure)" + }, + { + "system": "http://snomed.info/sct", + "version": "2019-09", + "code": "185463005", + "display": "Visit out of hours (procedure)" + }, + { + "system": "http://snomed.info/sct", + "version": "2019-09", + "code": "185464004", + "display": "Out of hours visit - not night visit (procedure)" + }, + { + "system": "http://snomed.info/sct", + "version": "2019-09", + "code": "185465003", + "display": "Weekend visit (procedure)" + }, + { + "system": "http://snomed.info/sct", + "version": "2019-09", + "code": "3391000175108", + "display": "Office visit for pediatric care and assessment (procedure)" + }, + { + "system": "http://snomed.info/sct", + "version": "2019-09", + "code": "439740005", + "display": "Postoperative follow-up visit (procedure)" + }, + { + "system": "http://www.ama-assn.org/go/cpt", + "version": "2020", + "code": "99201", + "display": "Office or other outpatient visit for the evaluation and management of a new patient, which requires these 3 key components: A problem focused history; A problem focused examination; Straightforward medical decision making. Counseling and/or coordination of care with other physicians, other qualified health care professionals, or agencies are provided consistent with the nature of the problem(s) and the patient's and/or family's needs. Usually, the presenting problem(s) are self limited or minor. Typically, 10 minutes are spent face-to-face with the patient and/or family." + }, + { + "system": "http://www.ama-assn.org/go/cpt", + "version": "2020", + "code": "99202", + "display": "Office or other outpatient visit for the evaluation and management of a new patient, which requires these 3 key components: An expanded problem focused history; An expanded problem focused examination; Straightforward medical decision making. Counseling and/or coordination of care with other physicians, other qualified health care professionals, or agencies are provided consistent with the nature of the problem(s) and the patient's and/or family's needs. Usually, the presenting problem(s) are of low to moderate severity. Typically, 20 minutes are spent face-to-face with the patient and/or family." + }, + { + "system": "http://www.ama-assn.org/go/cpt", + "version": "2020", + "code": "99203", + "display": "Office or other outpatient visit for the evaluation and management of a new patient, which requires these 3 key components: A detailed history; A detailed examination; Medical decision making of low complexity. Counseling and/or coordination of care with other physicians, other qualified health care professionals, or agencies are provided consistent with the nature of the problem(s) and the patient's and/or family's needs. Usually, the presenting problem(s) are of moderate severity. Typically, 30 minutes are spent face-to-face with the patient and/or family." + }, + { + "system": "http://www.ama-assn.org/go/cpt", + "version": "2020", + "code": "99204", + "display": "Office or other outpatient visit for the evaluation and management of a new patient, which requires these 3 key components: A comprehensive history; A comprehensive examination; Medical decision making of moderate complexity. Counseling and/or coordination of care with other physicians, other qualified health care professionals, or agencies are provided consistent with the nature of the problem(s) and the patient's and/or family's needs. Usually, the presenting problem(s) are of moderate to high severity. Typically, 45 minutes are spent face-to-face with the patient and/or family." + }, + { + "system": "http://www.ama-assn.org/go/cpt", + "version": "2020", + "code": "99205", + "display": "Office or other outpatient visit for the evaluation and management of a new patient, which requires these 3 key components: A comprehensive history; A comprehensive examination; Medical decision making of high complexity. Counseling and/or coordination of care with other physicians, other qualified health care professionals, or agencies are provided consistent with the nature of the problem(s) and the patient's and/or family's needs. Usually, the presenting problem(s) are of moderate to high severity. Typically, 60 minutes are spent face-to-face with the patient and/or family." + }, + { + "system": "http://www.ama-assn.org/go/cpt", + "version": "2020", + "code": "99212", + "display": "Office or other outpatient visit for the evaluation and management of an established patient, which requires at least 2 of these 3 key components: A problem focused history; A problem focused examination; Straightforward medical decision making. Counseling and/or coordination of care with other physicians, other qualified health care professionals, or agencies are provided consistent with the nature of the problem(s) and the patient's and/or family's needs. Usually, the presenting problem(s) are self limited or minor. Typically, 10 minutes are spent face-to-face with the patient and/or family." + }, + { + "system": "http://www.ama-assn.org/go/cpt", + "version": "2020", + "code": "99213", + "display": "Office or other outpatient visit for the evaluation and management of an established patient, which requires at least 2 of these 3 key components: An expanded problem focused history; An expanded problem focused examination; Medical decision making of low complexity. Counseling and coordination of care with other physicians, other qualified health care professionals, or agencies are provided consistent with the nature of the problem(s) and the patient's and/or family's needs. Usually, the presenting problem(s) are of low to moderate severity. Typically, 15 minutes are spent face-to-face with the patient and/or family." + }, + { + "system": "http://www.ama-assn.org/go/cpt", + "version": "2020", + "code": "99214", + "display": "Office or other outpatient visit for the evaluation and management of an established patient, which requires at least 2 of these 3 key components: A detailed history; A detailed examination; Medical decision making of moderate complexity. Counseling and/or coordination of care with other physicians, other qualified health care professionals, or agencies are provided consistent with the nature of the problem(s) and the patient's and/or family's needs. Usually, the presenting problem(s) are of moderate to high severity. Typically, 25 minutes are spent face-to-face with the patient and/or family." + }, + { + "system": "http://www.ama-assn.org/go/cpt", + "version": "2020", + "code": "99215", + "display": "Office or other outpatient visit for the evaluation and management of an established patient, which requires at least 2 of these 3 key components: A comprehensive history; A comprehensive examination; Medical decision making of high complexity. Counseling and/or coordination of care with other physicians, other qualified health care professionals, or agencies are provided consistent with the nature of the problem(s) and the patient's and/or family's needs. Usually, the presenting problem(s) are of moderate to high severity. Typically, 40 minutes are spent face-to-face with the patient and/or family." + }, + { + "system": "http://snomed.info/sct", + "version": "2018-03", + "code": "30346009", + "display": "Evaluation and management of established outpatient in office or other outpatient facility (procedure)" + }, + { + "system": "http://snomed.info/sct", + "version": "2018-03", + "code": "37894004", + "display": "Evaluation and management of new outpatient in office or other outpatient facility (procedure)" + }, + { + "system": "http://snomed.info/sct", + "version": "2019-09", + "code": "185463005", + "display": "Visit out of hours (procedure)" + }, + { + "system": "http://snomed.info/sct", + "version": "2019-09", + "code": "185464004", + "display": "Out of hours visit - not night visit (procedure)" + }, + { + "system": "http://snomed.info/sct", + "version": "2019-09", + "code": "185465003", + "display": "Weekend visit (procedure)" + }, + { + "system": "http://snomed.info/sct", + "version": "2019-09", + "code": "3391000175108", + "display": "Office visit for pediatric care and assessment (procedure)" + }, + { + "system": "http://snomed.info/sct", + "version": "2019-09", + "code": "439740005", + "display": "Postoperative follow-up visit (procedure)" + }, + { + "system": "http://www.ama-assn.org/go/cpt", + "version": "2020", + "code": "99201", + "display": "Office or other outpatient visit for the evaluation and management of a new patient, which requires these 3 key components: A problem focused history; A problem focused examination; Straightforward medical decision making. Counseling and/or coordination of care with other physicians, other qualified health care professionals, or agencies are provided consistent with the nature of the problem(s) and the patient's and/or family's needs. Usually, the presenting problem(s) are self limited or minor. Typically, 10 minutes are spent face-to-face with the patient and/or family." + }, + { + "system": "http://www.ama-assn.org/go/cpt", + "version": "2020", + "code": "99202", + "display": "Office or other outpatient visit for the evaluation and management of a new patient, which requires these 3 key components: An expanded problem focused history; An expanded problem focused examination; Straightforward medical decision making. Counseling and/or coordination of care with other physicians, other qualified health care professionals, or agencies are provided consistent with the nature of the problem(s) and the patient's and/or family's needs. Usually, the presenting problem(s) are of low to moderate severity. Typically, 20 minutes are spent face-to-face with the patient and/or family." + }, + { + "system": "http://www.ama-assn.org/go/cpt", + "version": "2020", + "code": "99203", + "display": "Office or other outpatient visit for the evaluation and management of a new patient, which requires these 3 key components: A detailed history; A detailed examination; Medical decision making of low complexity. Counseling and/or coordination of care with other physicians, other qualified health care professionals, or agencies are provided consistent with the nature of the problem(s) and the patient's and/or family's needs. Usually, the presenting problem(s) are of moderate severity. Typically, 30 minutes are spent face-to-face with the patient and/or family." + }, + { + "system": "http://www.ama-assn.org/go/cpt", + "version": "2020", + "code": "99204", + "display": "Office or other outpatient visit for the evaluation and management of a new patient, which requires these 3 key components: A comprehensive history; A comprehensive examination; Medical decision making of moderate complexity. Counseling and/or coordination of care with other physicians, other qualified health care professionals, or agencies are provided consistent with the nature of the problem(s) and the patient's and/or family's needs. Usually, the presenting problem(s) are of moderate to high severity. Typically, 45 minutes are spent face-to-face with the patient and/or family." + }, + { + "system": "http://www.ama-assn.org/go/cpt", + "version": "2020", + "code": "99205", + "display": "Office or other outpatient visit for the evaluation and management of a new patient, which requires these 3 key components: A comprehensive history; A comprehensive examination; Medical decision making of high complexity. Counseling and/or coordination of care with other physicians, other qualified health care professionals, or agencies are provided consistent with the nature of the problem(s) and the patient's and/or family's needs. Usually, the presenting problem(s) are of moderate to high severity. Typically, 60 minutes are spent face-to-face with the patient and/or family." + }, + { + "system": "http://www.ama-assn.org/go/cpt", + "version": "2020", + "code": "99212", + "display": "Office or other outpatient visit for the evaluation and management of an established patient, which requires at least 2 of these 3 key components: A problem focused history; A problem focused examination; Straightforward medical decision making. Counseling and/or coordination of care with other physicians, other qualified health care professionals, or agencies are provided consistent with the nature of the problem(s) and the patient's and/or family's needs. Usually, the presenting problem(s) are self limited or minor. Typically, 10 minutes are spent face-to-face with the patient and/or family." + }, + { + "system": "http://www.ama-assn.org/go/cpt", + "version": "2020", + "code": "99213", + "display": "Office or other outpatient visit for the evaluation and management of an established patient, which requires at least 2 of these 3 key components: An expanded problem focused history; An expanded problem focused examination; Medical decision making of low complexity. Counseling and coordination of care with other physicians, other qualified health care professionals, or agencies are provided consistent with the nature of the problem(s) and the patient's and/or family's needs. Usually, the presenting problem(s) are of low to moderate severity. Typically, 15 minutes are spent face-to-face with the patient and/or family." + }, + { + "system": "http://www.ama-assn.org/go/cpt", + "version": "2020", + "code": "99214", + "display": "Office or other outpatient visit for the evaluation and management of an established patient, which requires at least 2 of these 3 key components: A detailed history; A detailed examination; Medical decision making of moderate complexity. Counseling and/or coordination of care with other physicians, other qualified health care professionals, or agencies are provided consistent with the nature of the problem(s) and the patient's and/or family's needs. Usually, the presenting problem(s) are of moderate to high severity. Typically, 25 minutes are spent face-to-face with the patient and/or family." + }, + { + "system": "http://www.ama-assn.org/go/cpt", + "version": "2020", + "code": "99215", + "display": "Office or other outpatient visit for the evaluation and management of an established patient, which requires at least 2 of these 3 key components: A comprehensive history; A comprehensive examination; Medical decision making of high complexity. Counseling and/or coordination of care with other physicians, other qualified health care professionals, or agencies are provided consistent with the nature of the problem(s) and the patient's and/or family's needs. Usually, the presenting problem(s) are of moderate to high severity. Typically, 40 minutes are spent face-to-face with the patient and/or family." + }, + { + "system": "http://snomed.info/sct", + "version": "2018-03", + "code": "30346009", + "display": "Evaluation and management of established outpatient in office or other outpatient facility (procedure)" + }, + { + "system": "http://snomed.info/sct", + "version": "2018-03", + "code": "37894004", + "display": "Evaluation and management of new outpatient in office or other outpatient facility (procedure)" + }, + { + "system": "http://snomed.info/sct", + "version": "2019-09", + "code": "185463005", + "display": "Visit out of hours (procedure)" + }, + { + "system": "http://snomed.info/sct", + "version": "2019-09", + "code": "185464004", + "display": "Out of hours visit - not night visit (procedure)" + }, + { + "system": "http://snomed.info/sct", + "version": "2019-09", + "code": "185465003", + "display": "Weekend visit (procedure)" + }, + { + "system": "http://snomed.info/sct", + "version": "2019-09", + "code": "3391000175108", + "display": "Office visit for pediatric care and assessment (procedure)" + }, + { + "system": "http://snomed.info/sct", + "version": "2019-09", + "code": "439740005", + "display": "Postoperative follow-up visit (procedure)" + }, + { + "system": "http://www.ama-assn.org/go/cpt", + "version": "2020", + "code": "99201", + "display": "Office or other outpatient visit for the evaluation and management of a new patient, which requires these 3 key components: A problem focused history; A problem focused examination; Straightforward medical decision making. Counseling and/or coordination of care with other physicians, other qualified health care professionals, or agencies are provided consistent with the nature of the problem(s) and the patient's and/or family's needs. Usually, the presenting problem(s) are self limited or minor. Typically, 10 minutes are spent face-to-face with the patient and/or family." + }, + { + "system": "http://www.ama-assn.org/go/cpt", + "version": "2020", + "code": "99202", + "display": "Office or other outpatient visit for the evaluation and management of a new patient, which requires these 3 key components: An expanded problem focused history; An expanded problem focused examination; Straightforward medical decision making. Counseling and/or coordination of care with other physicians, other qualified health care professionals, or agencies are provided consistent with the nature of the problem(s) and the patient's and/or family's needs. Usually, the presenting problem(s) are of low to moderate severity. Typically, 20 minutes are spent face-to-face with the patient and/or family." + }, + { + "system": "http://www.ama-assn.org/go/cpt", + "version": "2020", + "code": "99203", + "display": "Office or other outpatient visit for the evaluation and management of a new patient, which requires these 3 key components: A detailed history; A detailed examination; Medical decision making of low complexity. Counseling and/or coordination of care with other physicians, other qualified health care professionals, or agencies are provided consistent with the nature of the problem(s) and the patient's and/or family's needs. Usually, the presenting problem(s) are of moderate severity. Typically, 30 minutes are spent face-to-face with the patient and/or family." + }, + { + "system": "http://www.ama-assn.org/go/cpt", + "version": "2020", + "code": "99204", + "display": "Office or other outpatient visit for the evaluation and management of a new patient, which requires these 3 key components: A comprehensive history; A comprehensive examination; Medical decision making of moderate complexity. Counseling and/or coordination of care with other physicians, other qualified health care professionals, or agencies are provided consistent with the nature of the problem(s) and the patient's and/or family's needs. Usually, the presenting problem(s) are of moderate to high severity. Typically, 45 minutes are spent face-to-face with the patient and/or family." + }, + { + "system": "http://www.ama-assn.org/go/cpt", + "version": "2020", + "code": "99205", + "display": "Office or other outpatient visit for the evaluation and management of a new patient, which requires these 3 key components: A comprehensive history; A comprehensive examination; Medical decision making of high complexity. Counseling and/or coordination of care with other physicians, other qualified health care professionals, or agencies are provided consistent with the nature of the problem(s) and the patient's and/or family's needs. Usually, the presenting problem(s) are of moderate to high severity. Typically, 60 minutes are spent face-to-face with the patient and/or family." + }, + { + "system": "http://www.ama-assn.org/go/cpt", + "version": "2020", + "code": "99212", + "display": "Office or other outpatient visit for the evaluation and management of an established patient, which requires at least 2 of these 3 key components: A problem focused history; A problem focused examination; Straightforward medical decision making. Counseling and/or coordination of care with other physicians, other qualified health care professionals, or agencies are provided consistent with the nature of the problem(s) and the patient's and/or family's needs. Usually, the presenting problem(s) are self limited or minor. Typically, 10 minutes are spent face-to-face with the patient and/or family." + }, + { + "system": "http://www.ama-assn.org/go/cpt", + "version": "2020", + "code": "99213", + "display": "Office or other outpatient visit for the evaluation and management of an established patient, which requires at least 2 of these 3 key components: An expanded problem focused history; An expanded problem focused examination; Medical decision making of low complexity. Counseling and coordination of care with other physicians, other qualified health care professionals, or agencies are provided consistent with the nature of the problem(s) and the patient's and/or family's needs. Usually, the presenting problem(s) are of low to moderate severity. Typically, 15 minutes are spent face-to-face with the patient and/or family." + }, + { + "system": "http://www.ama-assn.org/go/cpt", + "version": "2020", + "code": "99214", + "display": "Office or other outpatient visit for the evaluation and management of an established patient, which requires at least 2 of these 3 key components: A detailed history; A detailed examination; Medical decision making of moderate complexity. Counseling and/or coordination of care with other physicians, other qualified health care professionals, or agencies are provided consistent with the nature of the problem(s) and the patient's and/or family's needs. Usually, the presenting problem(s) are of moderate to high severity. Typically, 25 minutes are spent face-to-face with the patient and/or family." + }, + { + "system": "http://www.ama-assn.org/go/cpt", + "version": "2020", + "code": "99215", + "display": "Office or other outpatient visit for the evaluation and management of an established patient, which requires at least 2 of these 3 key components: A comprehensive history; A comprehensive examination; Medical decision making of high complexity. Counseling and/or coordination of care with other physicians, other qualified health care professionals, or agencies are provided consistent with the nature of the problem(s) and the patient's and/or family's needs. Usually, the presenting problem(s) are of moderate to high severity. Typically, 40 minutes are spent face-to-face with the patient and/or family." + }, + { + "system": "http://snomed.info/sct", + "version": "2018-03", + "code": "30346009", + "display": "Evaluation and management of established outpatient in office or other outpatient facility (procedure)" + }, + { + "system": "http://snomed.info/sct", + "version": "2018-03", + "code": "37894004", + "display": "Evaluation and management of new outpatient in office or other outpatient facility (procedure)" + }, + { + "system": "http://snomed.info/sct", + "version": "2019-09", + "code": "185463005", + "display": "Visit out of hours (procedure)" + }, + { + "system": "http://snomed.info/sct", + "version": "2019-09", + "code": "185464004", + "display": "Out of hours visit - not night visit (procedure)" + }, + { + "system": "http://snomed.info/sct", + "version": "2019-09", + "code": "185465003", + "display": "Weekend visit (procedure)" + }, + { + "system": "http://snomed.info/sct", + "version": "2019-09", + "code": "3391000175108", + "display": "Office visit for pediatric care and assessment (procedure)" + }, + { + "system": "http://snomed.info/sct", + "version": "2019-09", + "code": "439740005", + "display": "Postoperative follow-up visit (procedure)" + }, + { + "system": "http://www.ama-assn.org/go/cpt", + "version": "2020", + "code": "99201", + "display": "Office or other outpatient visit for the evaluation and management of a new patient, which requires these 3 key components: A problem focused history; A problem focused examination; Straightforward medical decision making. Counseling and/or coordination of care with other physicians, other qualified health care professionals, or agencies are provided consistent with the nature of the problem(s) and the patient's and/or family's needs. Usually, the presenting problem(s) are self limited or minor. Typically, 10 minutes are spent face-to-face with the patient and/or family." + }, + { + "system": "http://www.ama-assn.org/go/cpt", + "version": "2020", + "code": "99202", + "display": "Office or other outpatient visit for the evaluation and management of a new patient, which requires these 3 key components: An expanded problem focused history; An expanded problem focused examination; Straightforward medical decision making. Counseling and/or coordination of care with other physicians, other qualified health care professionals, or agencies are provided consistent with the nature of the problem(s) and the patient's and/or family's needs. Usually, the presenting problem(s) are of low to moderate severity. Typically, 20 minutes are spent face-to-face with the patient and/or family." + }, + { + "system": "http://www.ama-assn.org/go/cpt", + "version": "2020", + "code": "99203", + "display": "Office or other outpatient visit for the evaluation and management of a new patient, which requires these 3 key components: A detailed history; A detailed examination; Medical decision making of low complexity. Counseling and/or coordination of care with other physicians, other qualified health care professionals, or agencies are provided consistent with the nature of the problem(s) and the patient's and/or family's needs. Usually, the presenting problem(s) are of moderate severity. Typically, 30 minutes are spent face-to-face with the patient and/or family." + }, + { + "system": "http://www.ama-assn.org/go/cpt", + "version": "2020", + "code": "99204", + "display": "Office or other outpatient visit for the evaluation and management of a new patient, which requires these 3 key components: A comprehensive history; A comprehensive examination; Medical decision making of moderate complexity. Counseling and/or coordination of care with other physicians, other qualified health care professionals, or agencies are provided consistent with the nature of the problem(s) and the patient's and/or family's needs. Usually, the presenting problem(s) are of moderate to high severity. Typically, 45 minutes are spent face-to-face with the patient and/or family." + }, + { + "system": "http://www.ama-assn.org/go/cpt", + "version": "2020", + "code": "99205", + "display": "Office or other outpatient visit for the evaluation and management of a new patient, which requires these 3 key components: A comprehensive history; A comprehensive examination; Medical decision making of high complexity. Counseling and/or coordination of care with other physicians, other qualified health care professionals, or agencies are provided consistent with the nature of the problem(s) and the patient's and/or family's needs. Usually, the presenting problem(s) are of moderate to high severity. Typically, 60 minutes are spent face-to-face with the patient and/or family." + }, + { + "system": "http://www.ama-assn.org/go/cpt", + "version": "2020", + "code": "99212", + "display": "Office or other outpatient visit for the evaluation and management of an established patient, which requires at least 2 of these 3 key components: A problem focused history; A problem focused examination; Straightforward medical decision making. Counseling and/or coordination of care with other physicians, other qualified health care professionals, or agencies are provided consistent with the nature of the problem(s) and the patient's and/or family's needs. Usually, the presenting problem(s) are self limited or minor. Typically, 10 minutes are spent face-to-face with the patient and/or family." + }, + { + "system": "http://www.ama-assn.org/go/cpt", + "version": "2020", + "code": "99213", + "display": "Office or other outpatient visit for the evaluation and management of an established patient, which requires at least 2 of these 3 key components: An expanded problem focused history; An expanded problem focused examination; Medical decision making of low complexity. Counseling and coordination of care with other physicians, other qualified health care professionals, or agencies are provided consistent with the nature of the problem(s) and the patient's and/or family's needs. Usually, the presenting problem(s) are of low to moderate severity. Typically, 15 minutes are spent face-to-face with the patient and/or family." + }, + { + "system": "http://www.ama-assn.org/go/cpt", + "version": "2020", + "code": "99214", + "display": "Office or other outpatient visit for the evaluation and management of an established patient, which requires at least 2 of these 3 key components: A detailed history; A detailed examination; Medical decision making of moderate complexity. Counseling and/or coordination of care with other physicians, other qualified health care professionals, or agencies are provided consistent with the nature of the problem(s) and the patient's and/or family's needs. Usually, the presenting problem(s) are of moderate to high severity. Typically, 25 minutes are spent face-to-face with the patient and/or family." + }, + { + "system": "http://www.ama-assn.org/go/cpt", + "version": "2020", + "code": "99215", + "display": "Office or other outpatient visit for the evaluation and management of an established patient, which requires at least 2 of these 3 key components: A comprehensive history; A comprehensive examination; Medical decision making of high complexity. Counseling and/or coordination of care with other physicians, other qualified health care professionals, or agencies are provided consistent with the nature of the problem(s) and the patient's and/or family's needs. Usually, the presenting problem(s) are of moderate to high severity. Typically, 40 minutes are spent face-to-face with the patient and/or family." + }, + { + "system": "http://snomed.info/sct", + "version": "2018-03", + "code": "30346009", + "display": "Evaluation and management of established outpatient in office or other outpatient facility (procedure)" + }, + { + "system": "http://snomed.info/sct", + "version": "2018-03", + "code": "37894004", + "display": "Evaluation and management of new outpatient in office or other outpatient facility (procedure)" + }, + { + "system": "http://snomed.info/sct", + "version": "2019-09", + "code": "185463005", + "display": "Visit out of hours (procedure)" + }, + { + "system": "http://snomed.info/sct", + "version": "2019-09", + "code": "185464004", + "display": "Out of hours visit - not night visit (procedure)" + }, + { + "system": "http://snomed.info/sct", + "version": "2019-09", + "code": "185465003", + "display": "Weekend visit (procedure)" + }, + { + "system": "http://snomed.info/sct", + "version": "2019-09", + "code": "3391000175108", + "display": "Office visit for pediatric care and assessment (procedure)" + }, + { + "system": "http://snomed.info/sct", + "version": "2019-09", + "code": "439740005", + "display": "Postoperative follow-up visit (procedure)" + }, + { + "system": "http://www.ama-assn.org/go/cpt", + "version": "2020", + "code": "99201", + "display": "Office or other outpatient visit for the evaluation and management of a new patient, which requires these 3 key components: A problem focused history; A problem focused examination; Straightforward medical decision making. Counseling and/or coordination of care with other physicians, other qualified health care professionals, or agencies are provided consistent with the nature of the problem(s) and the patient's and/or family's needs. Usually, the presenting problem(s) are self limited or minor. Typically, 10 minutes are spent face-to-face with the patient and/or family." + }, + { + "system": "http://www.ama-assn.org/go/cpt", + "version": "2020", + "code": "99202", + "display": "Office or other outpatient visit for the evaluation and management of a new patient, which requires these 3 key components: An expanded problem focused history; An expanded problem focused examination; Straightforward medical decision making. Counseling and/or coordination of care with other physicians, other qualified health care professionals, or agencies are provided consistent with the nature of the problem(s) and the patient's and/or family's needs. Usually, the presenting problem(s) are of low to moderate severity. Typically, 20 minutes are spent face-to-face with the patient and/or family." + }, + { + "system": "http://www.ama-assn.org/go/cpt", + "version": "2020", + "code": "99203", + "display": "Office or other outpatient visit for the evaluation and management of a new patient, which requires these 3 key components: A detailed history; A detailed examination; Medical decision making of low complexity. Counseling and/or coordination of care with other physicians, other qualified health care professionals, or agencies are provided consistent with the nature of the problem(s) and the patient's and/or family's needs. Usually, the presenting problem(s) are of moderate severity. Typically, 30 minutes are spent face-to-face with the patient and/or family." + }, + { + "system": "http://www.ama-assn.org/go/cpt", + "version": "2020", + "code": "99204", + "display": "Office or other outpatient visit for the evaluation and management of a new patient, which requires these 3 key components: A comprehensive history; A comprehensive examination; Medical decision making of moderate complexity. Counseling and/or coordination of care with other physicians, other qualified health care professionals, or agencies are provided consistent with the nature of the problem(s) and the patient's and/or family's needs. Usually, the presenting problem(s) are of moderate to high severity. Typically, 45 minutes are spent face-to-face with the patient and/or family." + }, + { + "system": "http://www.ama-assn.org/go/cpt", + "version": "2020", + "code": "99205", + "display": "Office or other outpatient visit for the evaluation and management of a new patient, which requires these 3 key components: A comprehensive history; A comprehensive examination; Medical decision making of high complexity. Counseling and/or coordination of care with other physicians, other qualified health care professionals, or agencies are provided consistent with the nature of the problem(s) and the patient's and/or family's needs. Usually, the presenting problem(s) are of moderate to high severity. Typically, 60 minutes are spent face-to-face with the patient and/or family." + }, + { + "system": "http://www.ama-assn.org/go/cpt", + "version": "2020", + "code": "99212", + "display": "Office or other outpatient visit for the evaluation and management of an established patient, which requires at least 2 of these 3 key components: A problem focused history; A problem focused examination; Straightforward medical decision making. Counseling and/or coordination of care with other physicians, other qualified health care professionals, or agencies are provided consistent with the nature of the problem(s) and the patient's and/or family's needs. Usually, the presenting problem(s) are self limited or minor. Typically, 10 minutes are spent face-to-face with the patient and/or family." + }, + { + "system": "http://www.ama-assn.org/go/cpt", + "version": "2020", + "code": "99213", + "display": "Office or other outpatient visit for the evaluation and management of an established patient, which requires at least 2 of these 3 key components: An expanded problem focused history; An expanded problem focused examination; Medical decision making of low complexity. Counseling and coordination of care with other physicians, other qualified health care professionals, or agencies are provided consistent with the nature of the problem(s) and the patient's and/or family's needs. Usually, the presenting problem(s) are of low to moderate severity. Typically, 15 minutes are spent face-to-face with the patient and/or family." + }, + { + "system": "http://www.ama-assn.org/go/cpt", + "version": "2020", + "code": "99214", + "display": "Office or other outpatient visit for the evaluation and management of an established patient, which requires at least 2 of these 3 key components: A detailed history; A detailed examination; Medical decision making of moderate complexity. Counseling and/or coordination of care with other physicians, other qualified health care professionals, or agencies are provided consistent with the nature of the problem(s) and the patient's and/or family's needs. Usually, the presenting problem(s) are of moderate to high severity. Typically, 25 minutes are spent face-to-face with the patient and/or family." + }, + { + "system": "http://www.ama-assn.org/go/cpt", + "version": "2020", + "code": "99215", + "display": "Office or other outpatient visit for the evaluation and management of an established patient, which requires at least 2 of these 3 key components: A comprehensive history; A comprehensive examination; Medical decision making of high complexity. Counseling and/or coordination of care with other physicians, other qualified health care professionals, or agencies are provided consistent with the nature of the problem(s) and the patient's and/or family's needs. Usually, the presenting problem(s) are of moderate to high severity. Typically, 40 minutes are spent face-to-face with the patient and/or family." + }, + { + "system": "http://snomed.info/sct", + "version": "2018-03", + "code": "30346009", + "display": "Evaluation and management of established outpatient in office or other outpatient facility (procedure)" + }, + { + "system": "http://snomed.info/sct", + "version": "2018-03", + "code": "37894004", + "display": "Evaluation and management of new outpatient in office or other outpatient facility (procedure)" + }, + { + "system": "http://snomed.info/sct", + "version": "2019-09", + "code": "185463005", + "display": "Visit out of hours (procedure)" + }, + { + "system": "http://snomed.info/sct", + "version": "2019-09", + "code": "185464004", + "display": "Out of hours visit - not night visit (procedure)" + }, + { + "system": "http://snomed.info/sct", + "version": "2019-09", + "code": "185465003", + "display": "Weekend visit (procedure)" + }, + { + "system": "http://snomed.info/sct", + "version": "2019-09", + "code": "3391000175108", + "display": "Office visit for pediatric care and assessment (procedure)" + }, + { + "system": "http://snomed.info/sct", + "version": "2019-09", + "code": "439740005", + "display": "Postoperative follow-up visit (procedure)" + }, + { + "system": "http://www.ama-assn.org/go/cpt", + "version": "2020", + "code": "99201", + "display": "Office or other outpatient visit for the evaluation and management of a new patient, which requires these 3 key components: A problem focused history; A problem focused examination; Straightforward medical decision making. Counseling and/or coordination of care with other physicians, other qualified health care professionals, or agencies are provided consistent with the nature of the problem(s) and the patient's and/or family's needs. Usually, the presenting problem(s) are self limited or minor. Typically, 10 minutes are spent face-to-face with the patient and/or family." + }, + { + "system": "http://www.ama-assn.org/go/cpt", + "version": "2020", + "code": "99202", + "display": "Office or other outpatient visit for the evaluation and management of a new patient, which requires these 3 key components: An expanded problem focused history; An expanded problem focused examination; Straightforward medical decision making. Counseling and/or coordination of care with other physicians, other qualified health care professionals, or agencies are provided consistent with the nature of the problem(s) and the patient's and/or family's needs. Usually, the presenting problem(s) are of low to moderate severity. Typically, 20 minutes are spent face-to-face with the patient and/or family." + }, + { + "system": "http://www.ama-assn.org/go/cpt", + "version": "2020", + "code": "99203", + "display": "Office or other outpatient visit for the evaluation and management of a new patient, which requires these 3 key components: A detailed history; A detailed examination; Medical decision making of low complexity. Counseling and/or coordination of care with other physicians, other qualified health care professionals, or agencies are provided consistent with the nature of the problem(s) and the patient's and/or family's needs. Usually, the presenting problem(s) are of moderate severity. Typically, 30 minutes are spent face-to-face with the patient and/or family." + }, + { + "system": "http://www.ama-assn.org/go/cpt", + "version": "2020", + "code": "99204", + "display": "Office or other outpatient visit for the evaluation and management of a new patient, which requires these 3 key components: A comprehensive history; A comprehensive examination; Medical decision making of moderate complexity. Counseling and/or coordination of care with other physicians, other qualified health care professionals, or agencies are provided consistent with the nature of the problem(s) and the patient's and/or family's needs. Usually, the presenting problem(s) are of moderate to high severity. Typically, 45 minutes are spent face-to-face with the patient and/or family." + }, + { + "system": "http://www.ama-assn.org/go/cpt", + "version": "2020", + "code": "99205", + "display": "Office or other outpatient visit for the evaluation and management of a new patient, which requires these 3 key components: A comprehensive history; A comprehensive examination; Medical decision making of high complexity. Counseling and/or coordination of care with other physicians, other qualified health care professionals, or agencies are provided consistent with the nature of the problem(s) and the patient's and/or family's needs. Usually, the presenting problem(s) are of moderate to high severity. Typically, 60 minutes are spent face-to-face with the patient and/or family." + }, + { + "system": "http://www.ama-assn.org/go/cpt", + "version": "2020", + "code": "99212", + "display": "Office or other outpatient visit for the evaluation and management of an established patient, which requires at least 2 of these 3 key components: A problem focused history; A problem focused examination; Straightforward medical decision making. Counseling and/or coordination of care with other physicians, other qualified health care professionals, or agencies are provided consistent with the nature of the problem(s) and the patient's and/or family's needs. Usually, the presenting problem(s) are self limited or minor. Typically, 10 minutes are spent face-to-face with the patient and/or family." + }, + { + "system": "http://www.ama-assn.org/go/cpt", + "version": "2020", + "code": "99213", + "display": "Office or other outpatient visit for the evaluation and management of an established patient, which requires at least 2 of these 3 key components: An expanded problem focused history; An expanded problem focused examination; Medical decision making of low complexity. Counseling and coordination of care with other physicians, other qualified health care professionals, or agencies are provided consistent with the nature of the problem(s) and the patient's and/or family's needs. Usually, the presenting problem(s) are of low to moderate severity. Typically, 15 minutes are spent face-to-face with the patient and/or family." + }, + { + "system": "http://www.ama-assn.org/go/cpt", + "version": "2020", + "code": "99214", + "display": "Office or other outpatient visit for the evaluation and management of an established patient, which requires at least 2 of these 3 key components: A detailed history; A detailed examination; Medical decision making of moderate complexity. Counseling and/or coordination of care with other physicians, other qualified health care professionals, or agencies are provided consistent with the nature of the problem(s) and the patient's and/or family's needs. Usually, the presenting problem(s) are of moderate to high severity. Typically, 25 minutes are spent face-to-face with the patient and/or family." + }, + { + "system": "http://www.ama-assn.org/go/cpt", + "version": "2020", + "code": "99215", + "display": "Office or other outpatient visit for the evaluation and management of an established patient, which requires at least 2 of these 3 key components: A comprehensive history; A comprehensive examination; Medical decision making of high complexity. Counseling and/or coordination of care with other physicians, other qualified health care professionals, or agencies are provided consistent with the nature of the problem(s) and the patient's and/or family's needs. Usually, the presenting problem(s) are of moderate to high severity. Typically, 40 minutes are spent face-to-face with the patient and/or family." + }, + { + "system": "http://snomed.info/sct", + "version": "2018-03", + "code": "30346009", + "display": "Evaluation and management of established outpatient in office or other outpatient facility (procedure)" + }, + { + "system": "http://snomed.info/sct", + "version": "2018-03", + "code": "37894004", + "display": "Evaluation and management of new outpatient in office or other outpatient facility (procedure)" + }, + { + "system": "http://snomed.info/sct", + "version": "2019-09", + "code": "185463005", + "display": "Visit out of hours (procedure)" + }, + { + "system": "http://snomed.info/sct", + "version": "2019-09", + "code": "185464004", + "display": "Out of hours visit - not night visit (procedure)" + }, + { + "system": "http://snomed.info/sct", + "version": "2019-09", + "code": "185465003", + "display": "Weekend visit (procedure)" + }, + { + "system": "http://snomed.info/sct", + "version": "2019-09", + "code": "3391000175108", + "display": "Office visit for pediatric care and assessment (procedure)" + }, + { + "system": "http://snomed.info/sct", + "version": "2019-09", + "code": "439740005", + "display": "Postoperative follow-up visit (procedure)" + }, + { + "system": "http://www.ama-assn.org/go/cpt", + "version": "2020", + "code": "99201", + "display": "Office or other outpatient visit for the evaluation and management of a new patient, which requires these 3 key components: A problem focused history; A problem focused examination; Straightforward medical decision making. Counseling and/or coordination of care with other physicians, other qualified health care professionals, or agencies are provided consistent with the nature of the problem(s) and the patient's and/or family's needs. Usually, the presenting problem(s) are self limited or minor. Typically, 10 minutes are spent face-to-face with the patient and/or family." + }, + { + "system": "http://www.ama-assn.org/go/cpt", + "version": "2020", + "code": "99202", + "display": "Office or other outpatient visit for the evaluation and management of a new patient, which requires these 3 key components: An expanded problem focused history; An expanded problem focused examination; Straightforward medical decision making. Counseling and/or coordination of care with other physicians, other qualified health care professionals, or agencies are provided consistent with the nature of the problem(s) and the patient's and/or family's needs. Usually, the presenting problem(s) are of low to moderate severity. Typically, 20 minutes are spent face-to-face with the patient and/or family." + }, + { + "system": "http://www.ama-assn.org/go/cpt", + "version": "2020", + "code": "99203", + "display": "Office or other outpatient visit for the evaluation and management of a new patient, which requires these 3 key components: A detailed history; A detailed examination; Medical decision making of low complexity. Counseling and/or coordination of care with other physicians, other qualified health care professionals, or agencies are provided consistent with the nature of the problem(s) and the patient's and/or family's needs. Usually, the presenting problem(s) are of moderate severity. Typically, 30 minutes are spent face-to-face with the patient and/or family." + }, + { + "system": "http://www.ama-assn.org/go/cpt", + "version": "2020", + "code": "99204", + "display": "Office or other outpatient visit for the evaluation and management of a new patient, which requires these 3 key components: A comprehensive history; A comprehensive examination; Medical decision making of moderate complexity. Counseling and/or coordination of care with other physicians, other qualified health care professionals, or agencies are provided consistent with the nature of the problem(s) and the patient's and/or family's needs. Usually, the presenting problem(s) are of moderate to high severity. Typically, 45 minutes are spent face-to-face with the patient and/or family." + }, + { + "system": "http://www.ama-assn.org/go/cpt", + "version": "2020", + "code": "99205", + "display": "Office or other outpatient visit for the evaluation and management of a new patient, which requires these 3 key components: A comprehensive history; A comprehensive examination; Medical decision making of high complexity. Counseling and/or coordination of care with other physicians, other qualified health care professionals, or agencies are provided consistent with the nature of the problem(s) and the patient's and/or family's needs. Usually, the presenting problem(s) are of moderate to high severity. Typically, 60 minutes are spent face-to-face with the patient and/or family." + }, + { + "system": "http://www.ama-assn.org/go/cpt", + "version": "2020", + "code": "99212", + "display": "Office or other outpatient visit for the evaluation and management of an established patient, which requires at least 2 of these 3 key components: A problem focused history; A problem focused examination; Straightforward medical decision making. Counseling and/or coordination of care with other physicians, other qualified health care professionals, or agencies are provided consistent with the nature of the problem(s) and the patient's and/or family's needs. Usually, the presenting problem(s) are self limited or minor. Typically, 10 minutes are spent face-to-face with the patient and/or family." + }, + { + "system": "http://www.ama-assn.org/go/cpt", + "version": "2020", + "code": "99213", + "display": "Office or other outpatient visit for the evaluation and management of an established patient, which requires at least 2 of these 3 key components: An expanded problem focused history; An expanded problem focused examination; Medical decision making of low complexity. Counseling and coordination of care with other physicians, other qualified health care professionals, or agencies are provided consistent with the nature of the problem(s) and the patient's and/or family's needs. Usually, the presenting problem(s) are of low to moderate severity. Typically, 15 minutes are spent face-to-face with the patient and/or family." + }, + { + "system": "http://www.ama-assn.org/go/cpt", + "version": "2020", + "code": "99214", + "display": "Office or other outpatient visit for the evaluation and management of an established patient, which requires at least 2 of these 3 key components: A detailed history; A detailed examination; Medical decision making of moderate complexity. Counseling and/or coordination of care with other physicians, other qualified health care professionals, or agencies are provided consistent with the nature of the problem(s) and the patient's and/or family's needs. Usually, the presenting problem(s) are of moderate to high severity. Typically, 25 minutes are spent face-to-face with the patient and/or family." + }, + { + "system": "http://www.ama-assn.org/go/cpt", + "version": "2020", + "code": "99215", + "display": "Office or other outpatient visit for the evaluation and management of an established patient, which requires at least 2 of these 3 key components: A comprehensive history; A comprehensive examination; Medical decision making of high complexity. Counseling and/or coordination of care with other physicians, other qualified health care professionals, or agencies are provided consistent with the nature of the problem(s) and the patient's and/or family's needs. Usually, the presenting problem(s) are of moderate to high severity. Typically, 40 minutes are spent face-to-face with the patient and/or family." + }, + { + "system": "http://snomed.info/sct", + "version": "2018-03", + "code": "30346009", + "display": "Evaluation and management of established outpatient in office or other outpatient facility (procedure)" + }, + { + "system": "http://snomed.info/sct", + "version": "2018-03", + "code": "37894004", + "display": "Evaluation and management of new outpatient in office or other outpatient facility (procedure)" + }, + { + "system": "http://snomed.info/sct", + "version": "2019-09", + "code": "185463005", + "display": "Visit out of hours (procedure)" + }, + { + "system": "http://snomed.info/sct", + "version": "2019-09", + "code": "185464004", + "display": "Out of hours visit - not night visit (procedure)" + }, + { + "system": "http://snomed.info/sct", + "version": "2019-09", + "code": "185465003", + "display": "Weekend visit (procedure)" + }, + { + "system": "http://snomed.info/sct", + "version": "2019-09", + "code": "3391000175108", + "display": "Office visit for pediatric care and assessment (procedure)" + }, + { + "system": "http://snomed.info/sct", + "version": "2019-09", + "code": "439740005", + "display": "Postoperative follow-up visit (procedure)" + }, + { + "system": "http://www.ama-assn.org/go/cpt", + "version": "2020", + "code": "99201", + "display": "Office or other outpatient visit for the evaluation and management of a new patient, which requires these 3 key components: A problem focused history; A problem focused examination; Straightforward medical decision making. Counseling and/or coordination of care with other physicians, other qualified health care professionals, or agencies are provided consistent with the nature of the problem(s) and the patient's and/or family's needs. Usually, the presenting problem(s) are self limited or minor. Typically, 10 minutes are spent face-to-face with the patient and/or family." + }, + { + "system": "http://www.ama-assn.org/go/cpt", + "version": "2020", + "code": "99202", + "display": "Office or other outpatient visit for the evaluation and management of a new patient, which requires these 3 key components: An expanded problem focused history; An expanded problem focused examination; Straightforward medical decision making. Counseling and/or coordination of care with other physicians, other qualified health care professionals, or agencies are provided consistent with the nature of the problem(s) and the patient's and/or family's needs. Usually, the presenting problem(s) are of low to moderate severity. Typically, 20 minutes are spent face-to-face with the patient and/or family." + }, + { + "system": "http://www.ama-assn.org/go/cpt", + "version": "2020", + "code": "99203", + "display": "Office or other outpatient visit for the evaluation and management of a new patient, which requires these 3 key components: A detailed history; A detailed examination; Medical decision making of low complexity. Counseling and/or coordination of care with other physicians, other qualified health care professionals, or agencies are provided consistent with the nature of the problem(s) and the patient's and/or family's needs. Usually, the presenting problem(s) are of moderate severity. Typically, 30 minutes are spent face-to-face with the patient and/or family." + }, + { + "system": "http://www.ama-assn.org/go/cpt", + "version": "2020", + "code": "99204", + "display": "Office or other outpatient visit for the evaluation and management of a new patient, which requires these 3 key components: A comprehensive history; A comprehensive examination; Medical decision making of moderate complexity. Counseling and/or coordination of care with other physicians, other qualified health care professionals, or agencies are provided consistent with the nature of the problem(s) and the patient's and/or family's needs. Usually, the presenting problem(s) are of moderate to high severity. Typically, 45 minutes are spent face-to-face with the patient and/or family." + }, + { + "system": "http://www.ama-assn.org/go/cpt", + "version": "2020", + "code": "99205", + "display": "Office or other outpatient visit for the evaluation and management of a new patient, which requires these 3 key components: A comprehensive history; A comprehensive examination; Medical decision making of high complexity. Counseling and/or coordination of care with other physicians, other qualified health care professionals, or agencies are provided consistent with the nature of the problem(s) and the patient's and/or family's needs. Usually, the presenting problem(s) are of moderate to high severity. Typically, 60 minutes are spent face-to-face with the patient and/or family." + }, + { + "system": "http://www.ama-assn.org/go/cpt", + "version": "2020", + "code": "99212", + "display": "Office or other outpatient visit for the evaluation and management of an established patient, which requires at least 2 of these 3 key components: A problem focused history; A problem focused examination; Straightforward medical decision making. Counseling and/or coordination of care with other physicians, other qualified health care professionals, or agencies are provided consistent with the nature of the problem(s) and the patient's and/or family's needs. Usually, the presenting problem(s) are self limited or minor. Typically, 10 minutes are spent face-to-face with the patient and/or family." + }, + { + "system": "http://www.ama-assn.org/go/cpt", + "version": "2020", + "code": "99213", + "display": "Office or other outpatient visit for the evaluation and management of an established patient, which requires at least 2 of these 3 key components: An expanded problem focused history; An expanded problem focused examination; Medical decision making of low complexity. Counseling and coordination of care with other physicians, other qualified health care professionals, or agencies are provided consistent with the nature of the problem(s) and the patient's and/or family's needs. Usually, the presenting problem(s) are of low to moderate severity. Typically, 15 minutes are spent face-to-face with the patient and/or family." + }, + { + "system": "http://www.ama-assn.org/go/cpt", + "version": "2020", + "code": "99214", + "display": "Office or other outpatient visit for the evaluation and management of an established patient, which requires at least 2 of these 3 key components: A detailed history; A detailed examination; Medical decision making of moderate complexity. Counseling and/or coordination of care with other physicians, other qualified health care professionals, or agencies are provided consistent with the nature of the problem(s) and the patient's and/or family's needs. Usually, the presenting problem(s) are of moderate to high severity. Typically, 25 minutes are spent face-to-face with the patient and/or family." + }, + { + "system": "http://www.ama-assn.org/go/cpt", + "version": "2020", + "code": "99215", + "display": "Office or other outpatient visit for the evaluation and management of an established patient, which requires at least 2 of these 3 key components: A comprehensive history; A comprehensive examination; Medical decision making of high complexity. Counseling and/or coordination of care with other physicians, other qualified health care professionals, or agencies are provided consistent with the nature of the problem(s) and the patient's and/or family's needs. Usually, the presenting problem(s) are of moderate to high severity. Typically, 40 minutes are spent face-to-face with the patient and/or family." + }, + { + "system": "http://snomed.info/sct", + "version": "2018-03", + "code": "30346009", + "display": "Evaluation and management of established outpatient in office or other outpatient facility (procedure)" + }, + { + "system": "http://snomed.info/sct", + "version": "2018-03", + "code": "37894004", + "display": "Evaluation and management of new outpatient in office or other outpatient facility (procedure)" + }, + { + "system": "http://snomed.info/sct", + "version": "2019-09", + "code": "185463005", + "display": "Visit out of hours (procedure)" + }, + { + "system": "http://snomed.info/sct", + "version": "2019-09", + "code": "185464004", + "display": "Out of hours visit - not night visit (procedure)" + }, + { + "system": "http://snomed.info/sct", + "version": "2019-09", + "code": "185465003", + "display": "Weekend visit (procedure)" + }, + { + "system": "http://snomed.info/sct", + "version": "2019-09", + "code": "3391000175108", + "display": "Office visit for pediatric care and assessment (procedure)" + }, + { + "system": "http://snomed.info/sct", + "version": "2019-09", + "code": "439740005", + "display": "Postoperative follow-up visit (procedure)" + }, + { + "system": "http://www.ama-assn.org/go/cpt", + "version": "2020", + "code": "99201", + "display": "Office or other outpatient visit for the evaluation and management of a new patient, which requires these 3 key components: A problem focused history; A problem focused examination; Straightforward medical decision making. Counseling and/or coordination of care with other physicians, other qualified health care professionals, or agencies are provided consistent with the nature of the problem(s) and the patient's and/or family's needs. Usually, the presenting problem(s) are self limited or minor. Typically, 10 minutes are spent face-to-face with the patient and/or family." + }, + { + "system": "http://www.ama-assn.org/go/cpt", + "version": "2020", + "code": "99202", + "display": "Office or other outpatient visit for the evaluation and management of a new patient, which requires these 3 key components: An expanded problem focused history; An expanded problem focused examination; Straightforward medical decision making. Counseling and/or coordination of care with other physicians, other qualified health care professionals, or agencies are provided consistent with the nature of the problem(s) and the patient's and/or family's needs. Usually, the presenting problem(s) are of low to moderate severity. Typically, 20 minutes are spent face-to-face with the patient and/or family." + }, + { + "system": "http://www.ama-assn.org/go/cpt", + "version": "2020", + "code": "99203", + "display": "Office or other outpatient visit for the evaluation and management of a new patient, which requires these 3 key components: A detailed history; A detailed examination; Medical decision making of low complexity. Counseling and/or coordination of care with other physicians, other qualified health care professionals, or agencies are provided consistent with the nature of the problem(s) and the patient's and/or family's needs. Usually, the presenting problem(s) are of moderate severity. Typically, 30 minutes are spent face-to-face with the patient and/or family." + }, + { + "system": "http://www.ama-assn.org/go/cpt", + "version": "2020", + "code": "99204", + "display": "Office or other outpatient visit for the evaluation and management of a new patient, which requires these 3 key components: A comprehensive history; A comprehensive examination; Medical decision making of moderate complexity. Counseling and/or coordination of care with other physicians, other qualified health care professionals, or agencies are provided consistent with the nature of the problem(s) and the patient's and/or family's needs. Usually, the presenting problem(s) are of moderate to high severity. Typically, 45 minutes are spent face-to-face with the patient and/or family." + }, + { + "system": "http://www.ama-assn.org/go/cpt", + "version": "2020", + "code": "99205", + "display": "Office or other outpatient visit for the evaluation and management of a new patient, which requires these 3 key components: A comprehensive history; A comprehensive examination; Medical decision making of high complexity. Counseling and/or coordination of care with other physicians, other qualified health care professionals, or agencies are provided consistent with the nature of the problem(s) and the patient's and/or family's needs. Usually, the presenting problem(s) are of moderate to high severity. Typically, 60 minutes are spent face-to-face with the patient and/or family." + }, + { + "system": "http://www.ama-assn.org/go/cpt", + "version": "2020", + "code": "99212", + "display": "Office or other outpatient visit for the evaluation and management of an established patient, which requires at least 2 of these 3 key components: A problem focused history; A problem focused examination; Straightforward medical decision making. Counseling and/or coordination of care with other physicians, other qualified health care professionals, or agencies are provided consistent with the nature of the problem(s) and the patient's and/or family's needs. Usually, the presenting problem(s) are self limited or minor. Typically, 10 minutes are spent face-to-face with the patient and/or family." + }, + { + "system": "http://www.ama-assn.org/go/cpt", + "version": "2020", + "code": "99213", + "display": "Office or other outpatient visit for the evaluation and management of an established patient, which requires at least 2 of these 3 key components: An expanded problem focused history; An expanded problem focused examination; Medical decision making of low complexity. Counseling and coordination of care with other physicians, other qualified health care professionals, or agencies are provided consistent with the nature of the problem(s) and the patient's and/or family's needs. Usually, the presenting problem(s) are of low to moderate severity. Typically, 15 minutes are spent face-to-face with the patient and/or family." + }, + { + "system": "http://www.ama-assn.org/go/cpt", + "version": "2020", + "code": "99214", + "display": "Office or other outpatient visit for the evaluation and management of an established patient, which requires at least 2 of these 3 key components: A detailed history; A detailed examination; Medical decision making of moderate complexity. Counseling and/or coordination of care with other physicians, other qualified health care professionals, or agencies are provided consistent with the nature of the problem(s) and the patient's and/or family's needs. Usually, the presenting problem(s) are of moderate to high severity. Typically, 25 minutes are spent face-to-face with the patient and/or family." + }, + { + "system": "http://www.ama-assn.org/go/cpt", + "version": "2020", + "code": "99215", + "display": "Office or other outpatient visit for the evaluation and management of an established patient, which requires at least 2 of these 3 key components: A comprehensive history; A comprehensive examination; Medical decision making of high complexity. Counseling and/or coordination of care with other physicians, other qualified health care professionals, or agencies are provided consistent with the nature of the problem(s) and the patient's and/or family's needs. Usually, the presenting problem(s) are of moderate to high severity. Typically, 40 minutes are spent face-to-face with the patient and/or family." + }, + { + "system": "http://snomed.info/sct", + "version": "2018-03", + "code": "30346009", + "display": "Evaluation and management of established outpatient in office or other outpatient facility (procedure)" + }, + { + "system": "http://snomed.info/sct", + "version": "2018-03", + "code": "37894004", + "display": "Evaluation and management of new outpatient in office or other outpatient facility (procedure)" + }, + { + "system": "http://snomed.info/sct", + "version": "2019-09", + "code": "185463005", + "display": "Visit out of hours (procedure)" + }, + { + "system": "http://snomed.info/sct", + "version": "2019-09", + "code": "185464004", + "display": "Out of hours visit - not night visit (procedure)" + }, + { + "system": "http://snomed.info/sct", + "version": "2019-09", + "code": "185465003", + "display": "Weekend visit (procedure)" + }, + { + "system": "http://snomed.info/sct", + "version": "2019-09", + "code": "3391000175108", + "display": "Office visit for pediatric care and assessment (procedure)" + }, + { + "system": "http://snomed.info/sct", + "version": "2019-09", + "code": "439740005", + "display": "Postoperative follow-up visit (procedure)" + }, + { + "system": "http://www.ama-assn.org/go/cpt", + "version": "2020", + "code": "99201", + "display": "Office or other outpatient visit for the evaluation and management of a new patient, which requires these 3 key components: A problem focused history; A problem focused examination; Straightforward medical decision making. Counseling and/or coordination of care with other physicians, other qualified health care professionals, or agencies are provided consistent with the nature of the problem(s) and the patient's and/or family's needs. Usually, the presenting problem(s) are self limited or minor. Typically, 10 minutes are spent face-to-face with the patient and/or family." + }, + { + "system": "http://www.ama-assn.org/go/cpt", + "version": "2020", + "code": "99202", + "display": "Office or other outpatient visit for the evaluation and management of a new patient, which requires these 3 key components: An expanded problem focused history; An expanded problem focused examination; Straightforward medical decision making. Counseling and/or coordination of care with other physicians, other qualified health care professionals, or agencies are provided consistent with the nature of the problem(s) and the patient's and/or family's needs. Usually, the presenting problem(s) are of low to moderate severity. Typically, 20 minutes are spent face-to-face with the patient and/or family." + }, + { + "system": "http://www.ama-assn.org/go/cpt", + "version": "2020", + "code": "99203", + "display": "Office or other outpatient visit for the evaluation and management of a new patient, which requires these 3 key components: A detailed history; A detailed examination; Medical decision making of low complexity. Counseling and/or coordination of care with other physicians, other qualified health care professionals, or agencies are provided consistent with the nature of the problem(s) and the patient's and/or family's needs. Usually, the presenting problem(s) are of moderate severity. Typically, 30 minutes are spent face-to-face with the patient and/or family." + }, + { + "system": "http://www.ama-assn.org/go/cpt", + "version": "2020", + "code": "99204", + "display": "Office or other outpatient visit for the evaluation and management of a new patient, which requires these 3 key components: A comprehensive history; A comprehensive examination; Medical decision making of moderate complexity. Counseling and/or coordination of care with other physicians, other qualified health care professionals, or agencies are provided consistent with the nature of the problem(s) and the patient's and/or family's needs. Usually, the presenting problem(s) are of moderate to high severity. Typically, 45 minutes are spent face-to-face with the patient and/or family." + }, + { + "system": "http://www.ama-assn.org/go/cpt", + "version": "2020", + "code": "99205", + "display": "Office or other outpatient visit for the evaluation and management of a new patient, which requires these 3 key components: A comprehensive history; A comprehensive examination; Medical decision making of high complexity. Counseling and/or coordination of care with other physicians, other qualified health care professionals, or agencies are provided consistent with the nature of the problem(s) and the patient's and/or family's needs. Usually, the presenting problem(s) are of moderate to high severity. Typically, 60 minutes are spent face-to-face with the patient and/or family." + }, + { + "system": "http://www.ama-assn.org/go/cpt", + "version": "2020", + "code": "99212", + "display": "Office or other outpatient visit for the evaluation and management of an established patient, which requires at least 2 of these 3 key components: A problem focused history; A problem focused examination; Straightforward medical decision making. Counseling and/or coordination of care with other physicians, other qualified health care professionals, or agencies are provided consistent with the nature of the problem(s) and the patient's and/or family's needs. Usually, the presenting problem(s) are self limited or minor. Typically, 10 minutes are spent face-to-face with the patient and/or family." + }, + { + "system": "http://www.ama-assn.org/go/cpt", + "version": "2020", + "code": "99213", + "display": "Office or other outpatient visit for the evaluation and management of an established patient, which requires at least 2 of these 3 key components: An expanded problem focused history; An expanded problem focused examination; Medical decision making of low complexity. Counseling and coordination of care with other physicians, other qualified health care professionals, or agencies are provided consistent with the nature of the problem(s) and the patient's and/or family's needs. Usually, the presenting problem(s) are of low to moderate severity. Typically, 15 minutes are spent face-to-face with the patient and/or family." + }, + { + "system": "http://www.ama-assn.org/go/cpt", + "version": "2020", + "code": "99214", + "display": "Office or other outpatient visit for the evaluation and management of an established patient, which requires at least 2 of these 3 key components: A detailed history; A detailed examination; Medical decision making of moderate complexity. Counseling and/or coordination of care with other physicians, other qualified health care professionals, or agencies are provided consistent with the nature of the problem(s) and the patient's and/or family's needs. Usually, the presenting problem(s) are of moderate to high severity. Typically, 25 minutes are spent face-to-face with the patient and/or family." + }, + { + "system": "http://www.ama-assn.org/go/cpt", + "version": "2020", + "code": "99215", + "display": "Office or other outpatient visit for the evaluation and management of an established patient, which requires at least 2 of these 3 key components: A comprehensive history; A comprehensive examination; Medical decision making of high complexity. Counseling and/or coordination of care with other physicians, other qualified health care professionals, or agencies are provided consistent with the nature of the problem(s) and the patient's and/or family's needs. Usually, the presenting problem(s) are of moderate to high severity. Typically, 40 minutes are spent face-to-face with the patient and/or family." + }, + { + "system": "http://snomed.info/sct", + "version": "2018-03", + "code": "30346009", + "display": "Evaluation and management of established outpatient in office or other outpatient facility (procedure)" + }, + { + "system": "http://snomed.info/sct", + "version": "2018-03", + "code": "37894004", + "display": "Evaluation and management of new outpatient in office or other outpatient facility (procedure)" + }, + { + "system": "http://snomed.info/sct", + "version": "2019-09", + "code": "185463005", + "display": "Visit out of hours (procedure)" + }, + { + "system": "http://snomed.info/sct", + "version": "2019-09", + "code": "185464004", + "display": "Out of hours visit - not night visit (procedure)" + }, + { + "system": "http://snomed.info/sct", + "version": "2019-09", + "code": "185465003", + "display": "Weekend visit (procedure)" + }, + { + "system": "http://snomed.info/sct", + "version": "2019-09", + "code": "3391000175108", + "display": "Office visit for pediatric care and assessment (procedure)" + }, + { + "system": "http://snomed.info/sct", + "version": "2019-09", + "code": "439740005", + "display": "Postoperative follow-up visit (procedure)" + }, + { + "system": "http://www.ama-assn.org/go/cpt", + "version": "2020", + "code": "99201", + "display": "Office or other outpatient visit for the evaluation and management of a new patient, which requires these 3 key components: A problem focused history; A problem focused examination; Straightforward medical decision making. Counseling and/or coordination of care with other physicians, other qualified health care professionals, or agencies are provided consistent with the nature of the problem(s) and the patient's and/or family's needs. Usually, the presenting problem(s) are self limited or minor. Typically, 10 minutes are spent face-to-face with the patient and/or family." + }, + { + "system": "http://www.ama-assn.org/go/cpt", + "version": "2020", + "code": "99202", + "display": "Office or other outpatient visit for the evaluation and management of a new patient, which requires these 3 key components: An expanded problem focused history; An expanded problem focused examination; Straightforward medical decision making. Counseling and/or coordination of care with other physicians, other qualified health care professionals, or agencies are provided consistent with the nature of the problem(s) and the patient's and/or family's needs. Usually, the presenting problem(s) are of low to moderate severity. Typically, 20 minutes are spent face-to-face with the patient and/or family." + }, + { + "system": "http://www.ama-assn.org/go/cpt", + "version": "2020", + "code": "99203", + "display": "Office or other outpatient visit for the evaluation and management of a new patient, which requires these 3 key components: A detailed history; A detailed examination; Medical decision making of low complexity. Counseling and/or coordination of care with other physicians, other qualified health care professionals, or agencies are provided consistent with the nature of the problem(s) and the patient's and/or family's needs. Usually, the presenting problem(s) are of moderate severity. Typically, 30 minutes are spent face-to-face with the patient and/or family." + }, + { + "system": "http://www.ama-assn.org/go/cpt", + "version": "2020", + "code": "99204", + "display": "Office or other outpatient visit for the evaluation and management of a new patient, which requires these 3 key components: A comprehensive history; A comprehensive examination; Medical decision making of moderate complexity. Counseling and/or coordination of care with other physicians, other qualified health care professionals, or agencies are provided consistent with the nature of the problem(s) and the patient's and/or family's needs. Usually, the presenting problem(s) are of moderate to high severity. Typically, 45 minutes are spent face-to-face with the patient and/or family." + }, + { + "system": "http://www.ama-assn.org/go/cpt", + "version": "2020", + "code": "99205", + "display": "Office or other outpatient visit for the evaluation and management of a new patient, which requires these 3 key components: A comprehensive history; A comprehensive examination; Medical decision making of high complexity. Counseling and/or coordination of care with other physicians, other qualified health care professionals, or agencies are provided consistent with the nature of the problem(s) and the patient's and/or family's needs. Usually, the presenting problem(s) are of moderate to high severity. Typically, 60 minutes are spent face-to-face with the patient and/or family." + }, + { + "system": "http://www.ama-assn.org/go/cpt", + "version": "2020", + "code": "99212", + "display": "Office or other outpatient visit for the evaluation and management of an established patient, which requires at least 2 of these 3 key components: A problem focused history; A problem focused examination; Straightforward medical decision making. Counseling and/or coordination of care with other physicians, other qualified health care professionals, or agencies are provided consistent with the nature of the problem(s) and the patient's and/or family's needs. Usually, the presenting problem(s) are self limited or minor. Typically, 10 minutes are spent face-to-face with the patient and/or family." + }, + { + "system": "http://www.ama-assn.org/go/cpt", + "version": "2020", + "code": "99213", + "display": "Office or other outpatient visit for the evaluation and management of an established patient, which requires at least 2 of these 3 key components: An expanded problem focused history; An expanded problem focused examination; Medical decision making of low complexity. Counseling and coordination of care with other physicians, other qualified health care professionals, or agencies are provided consistent with the nature of the problem(s) and the patient's and/or family's needs. Usually, the presenting problem(s) are of low to moderate severity. Typically, 15 minutes are spent face-to-face with the patient and/or family." + }, + { + "system": "http://www.ama-assn.org/go/cpt", + "version": "2020", + "code": "99214", + "display": "Office or other outpatient visit for the evaluation and management of an established patient, which requires at least 2 of these 3 key components: A detailed history; A detailed examination; Medical decision making of moderate complexity. Counseling and/or coordination of care with other physicians, other qualified health care professionals, or agencies are provided consistent with the nature of the problem(s) and the patient's and/or family's needs. Usually, the presenting problem(s) are of moderate to high severity. Typically, 25 minutes are spent face-to-face with the patient and/or family." + }, + { + "system": "http://www.ama-assn.org/go/cpt", + "version": "2020", + "code": "99215", + "display": "Office or other outpatient visit for the evaluation and management of an established patient, which requires at least 2 of these 3 key components: A comprehensive history; A comprehensive examination; Medical decision making of high complexity. Counseling and/or coordination of care with other physicians, other qualified health care professionals, or agencies are provided consistent with the nature of the problem(s) and the patient's and/or family's needs. Usually, the presenting problem(s) are of moderate to high severity. Typically, 40 minutes are spent face-to-face with the patient and/or family." + }, + { + "system": "http://snomed.info/sct", + "version": "2018-03", + "code": "30346009", + "display": "Evaluation and management of established outpatient in office or other outpatient facility (procedure)" + }, + { + "system": "http://snomed.info/sct", + "version": "2018-03", + "code": "37894004", + "display": "Evaluation and management of new outpatient in office or other outpatient facility (procedure)" + }, + { + "system": "http://snomed.info/sct", + "version": "2019-09", + "code": "185463005", + "display": "Visit out of hours (procedure)" + }, + { + "system": "http://snomed.info/sct", + "version": "2019-09", + "code": "185464004", + "display": "Out of hours visit - not night visit (procedure)" + }, + { + "system": "http://snomed.info/sct", + "version": "2019-09", + "code": "185465003", + "display": "Weekend visit (procedure)" + }, + { + "system": "http://snomed.info/sct", + "version": "2019-09", + "code": "3391000175108", + "display": "Office visit for pediatric care and assessment (procedure)" + }, + { + "system": "http://snomed.info/sct", + "version": "2019-09", + "code": "439740005", + "display": "Postoperative follow-up visit (procedure)" + }, + { + "system": "http://www.ama-assn.org/go/cpt", + "version": "2020", + "code": "99201", + "display": "Office or other outpatient visit for the evaluation and management of a new patient, which requires these 3 key components: A problem focused history; A problem focused examination; Straightforward medical decision making. Counseling and/or coordination of care with other physicians, other qualified health care professionals, or agencies are provided consistent with the nature of the problem(s) and the patient's and/or family's needs. Usually, the presenting problem(s) are self limited or minor. Typically, 10 minutes are spent face-to-face with the patient and/or family." + }, + { + "system": "http://www.ama-assn.org/go/cpt", + "version": "2020", + "code": "99202", + "display": "Office or other outpatient visit for the evaluation and management of a new patient, which requires these 3 key components: An expanded problem focused history; An expanded problem focused examination; Straightforward medical decision making. Counseling and/or coordination of care with other physicians, other qualified health care professionals, or agencies are provided consistent with the nature of the problem(s) and the patient's and/or family's needs. Usually, the presenting problem(s) are of low to moderate severity. Typically, 20 minutes are spent face-to-face with the patient and/or family." + }, + { + "system": "http://www.ama-assn.org/go/cpt", + "version": "2020", + "code": "99203", + "display": "Office or other outpatient visit for the evaluation and management of a new patient, which requires these 3 key components: A detailed history; A detailed examination; Medical decision making of low complexity. Counseling and/or coordination of care with other physicians, other qualified health care professionals, or agencies are provided consistent with the nature of the problem(s) and the patient's and/or family's needs. Usually, the presenting problem(s) are of moderate severity. Typically, 30 minutes are spent face-to-face with the patient and/or family." + }, + { + "system": "http://www.ama-assn.org/go/cpt", + "version": "2020", + "code": "99204", + "display": "Office or other outpatient visit for the evaluation and management of a new patient, which requires these 3 key components: A comprehensive history; A comprehensive examination; Medical decision making of moderate complexity. Counseling and/or coordination of care with other physicians, other qualified health care professionals, or agencies are provided consistent with the nature of the problem(s) and the patient's and/or family's needs. Usually, the presenting problem(s) are of moderate to high severity. Typically, 45 minutes are spent face-to-face with the patient and/or family." + }, + { + "system": "http://www.ama-assn.org/go/cpt", + "version": "2020", + "code": "99205", + "display": "Office or other outpatient visit for the evaluation and management of a new patient, which requires these 3 key components: A comprehensive history; A comprehensive examination; Medical decision making of high complexity. Counseling and/or coordination of care with other physicians, other qualified health care professionals, or agencies are provided consistent with the nature of the problem(s) and the patient's and/or family's needs. Usually, the presenting problem(s) are of moderate to high severity. Typically, 60 minutes are spent face-to-face with the patient and/or family." + }, + { + "system": "http://www.ama-assn.org/go/cpt", + "version": "2020", + "code": "99212", + "display": "Office or other outpatient visit for the evaluation and management of an established patient, which requires at least 2 of these 3 key components: A problem focused history; A problem focused examination; Straightforward medical decision making. Counseling and/or coordination of care with other physicians, other qualified health care professionals, or agencies are provided consistent with the nature of the problem(s) and the patient's and/or family's needs. Usually, the presenting problem(s) are self limited or minor. Typically, 10 minutes are spent face-to-face with the patient and/or family." + }, + { + "system": "http://www.ama-assn.org/go/cpt", + "version": "2020", + "code": "99213", + "display": "Office or other outpatient visit for the evaluation and management of an established patient, which requires at least 2 of these 3 key components: An expanded problem focused history; An expanded problem focused examination; Medical decision making of low complexity. Counseling and coordination of care with other physicians, other qualified health care professionals, or agencies are provided consistent with the nature of the problem(s) and the patient's and/or family's needs. Usually, the presenting problem(s) are of low to moderate severity. Typically, 15 minutes are spent face-to-face with the patient and/or family." + }, + { + "system": "http://www.ama-assn.org/go/cpt", + "version": "2020", + "code": "99214", + "display": "Office or other outpatient visit for the evaluation and management of an established patient, which requires at least 2 of these 3 key components: A detailed history; A detailed examination; Medical decision making of moderate complexity. Counseling and/or coordination of care with other physicians, other qualified health care professionals, or agencies are provided consistent with the nature of the problem(s) and the patient's and/or family's needs. Usually, the presenting problem(s) are of moderate to high severity. Typically, 25 minutes are spent face-to-face with the patient and/or family." + }, + { + "system": "http://www.ama-assn.org/go/cpt", + "version": "2020", + "code": "99215", + "display": "Office or other outpatient visit for the evaluation and management of an established patient, which requires at least 2 of these 3 key components: A comprehensive history; A comprehensive examination; Medical decision making of high complexity. Counseling and/or coordination of care with other physicians, other qualified health care professionals, or agencies are provided consistent with the nature of the problem(s) and the patient's and/or family's needs. Usually, the presenting problem(s) are of moderate to high severity. Typically, 40 minutes are spent face-to-face with the patient and/or family." + }, + { + "system": "http://snomed.info/sct", + "version": "2018-03", + "code": "30346009", + "display": "Evaluation and management of established outpatient in office or other outpatient facility (procedure)" + }, + { + "system": "http://snomed.info/sct", + "version": "2018-03", + "code": "37894004", + "display": "Evaluation and management of new outpatient in office or other outpatient facility (procedure)" + }, + { + "system": "http://snomed.info/sct", + "version": "2019-09", + "code": "185463005", + "display": "Visit out of hours (procedure)" + }, + { + "system": "http://snomed.info/sct", + "version": "2019-09", + "code": "185464004", + "display": "Out of hours visit - not night visit (procedure)" + }, + { + "system": "http://snomed.info/sct", + "version": "2019-09", + "code": "185465003", + "display": "Weekend visit (procedure)" + }, + { + "system": "http://snomed.info/sct", + "version": "2019-09", + "code": "3391000175108", + "display": "Office visit for pediatric care and assessment (procedure)" + }, + { + "system": "http://snomed.info/sct", + "version": "2019-09", + "code": "439740005", + "display": "Postoperative follow-up visit (procedure)" + }, + { + "system": "http://www.ama-assn.org/go/cpt", + "version": "2020", + "code": "99201", + "display": "Office or other outpatient visit for the evaluation and management of a new patient, which requires these 3 key components: A problem focused history; A problem focused examination; Straightforward medical decision making. Counseling and/or coordination of care with other physicians, other qualified health care professionals, or agencies are provided consistent with the nature of the problem(s) and the patient's and/or family's needs. Usually, the presenting problem(s) are self limited or minor. Typically, 10 minutes are spent face-to-face with the patient and/or family." + }, + { + "system": "http://www.ama-assn.org/go/cpt", + "version": "2020", + "code": "99202", + "display": "Office or other outpatient visit for the evaluation and management of a new patient, which requires these 3 key components: An expanded problem focused history; An expanded problem focused examination; Straightforward medical decision making. Counseling and/or coordination of care with other physicians, other qualified health care professionals, or agencies are provided consistent with the nature of the problem(s) and the patient's and/or family's needs. Usually, the presenting problem(s) are of low to moderate severity. Typically, 20 minutes are spent face-to-face with the patient and/or family." + }, + { + "system": "http://www.ama-assn.org/go/cpt", + "version": "2020", + "code": "99203", + "display": "Office or other outpatient visit for the evaluation and management of a new patient, which requires these 3 key components: A detailed history; A detailed examination; Medical decision making of low complexity. Counseling and/or coordination of care with other physicians, other qualified health care professionals, or agencies are provided consistent with the nature of the problem(s) and the patient's and/or family's needs. Usually, the presenting problem(s) are of moderate severity. Typically, 30 minutes are spent face-to-face with the patient and/or family." + }, + { + "system": "http://www.ama-assn.org/go/cpt", + "version": "2020", + "code": "99204", + "display": "Office or other outpatient visit for the evaluation and management of a new patient, which requires these 3 key components: A comprehensive history; A comprehensive examination; Medical decision making of moderate complexity. Counseling and/or coordination of care with other physicians, other qualified health care professionals, or agencies are provided consistent with the nature of the problem(s) and the patient's and/or family's needs. Usually, the presenting problem(s) are of moderate to high severity. Typically, 45 minutes are spent face-to-face with the patient and/or family." + }, + { + "system": "http://www.ama-assn.org/go/cpt", + "version": "2020", + "code": "99205", + "display": "Office or other outpatient visit for the evaluation and management of a new patient, which requires these 3 key components: A comprehensive history; A comprehensive examination; Medical decision making of high complexity. Counseling and/or coordination of care with other physicians, other qualified health care professionals, or agencies are provided consistent with the nature of the problem(s) and the patient's and/or family's needs. Usually, the presenting problem(s) are of moderate to high severity. Typically, 60 minutes are spent face-to-face with the patient and/or family." + }, + { + "system": "http://www.ama-assn.org/go/cpt", + "version": "2020", + "code": "99212", + "display": "Office or other outpatient visit for the evaluation and management of an established patient, which requires at least 2 of these 3 key components: A problem focused history; A problem focused examination; Straightforward medical decision making. Counseling and/or coordination of care with other physicians, other qualified health care professionals, or agencies are provided consistent with the nature of the problem(s) and the patient's and/or family's needs. Usually, the presenting problem(s) are self limited or minor. Typically, 10 minutes are spent face-to-face with the patient and/or family." + }, + { + "system": "http://www.ama-assn.org/go/cpt", + "version": "2020", + "code": "99213", + "display": "Office or other outpatient visit for the evaluation and management of an established patient, which requires at least 2 of these 3 key components: An expanded problem focused history; An expanded problem focused examination; Medical decision making of low complexity. Counseling and coordination of care with other physicians, other qualified health care professionals, or agencies are provided consistent with the nature of the problem(s) and the patient's and/or family's needs. Usually, the presenting problem(s) are of low to moderate severity. Typically, 15 minutes are spent face-to-face with the patient and/or family." + }, + { + "system": "http://www.ama-assn.org/go/cpt", + "version": "2020", + "code": "99214", + "display": "Office or other outpatient visit for the evaluation and management of an established patient, which requires at least 2 of these 3 key components: A detailed history; A detailed examination; Medical decision making of moderate complexity. Counseling and/or coordination of care with other physicians, other qualified health care professionals, or agencies are provided consistent with the nature of the problem(s) and the patient's and/or family's needs. Usually, the presenting problem(s) are of moderate to high severity. Typically, 25 minutes are spent face-to-face with the patient and/or family." + }, + { + "system": "http://www.ama-assn.org/go/cpt", + "version": "2020", + "code": "99215", + "display": "Office or other outpatient visit for the evaluation and management of an established patient, which requires at least 2 of these 3 key components: A comprehensive history; A comprehensive examination; Medical decision making of high complexity. Counseling and/or coordination of care with other physicians, other qualified health care professionals, or agencies are provided consistent with the nature of the problem(s) and the patient's and/or family's needs. Usually, the presenting problem(s) are of moderate to high severity. Typically, 40 minutes are spent face-to-face with the patient and/or family." + }, + { + "system": "http://snomed.info/sct", + "version": "2018-03", + "code": "30346009", + "display": "Evaluation and management of established outpatient in office or other outpatient facility (procedure)" + }, + { + "system": "http://snomed.info/sct", + "version": "2018-03", + "code": "37894004", + "display": "Evaluation and management of new outpatient in office or other outpatient facility (procedure)" + }, + { + "system": "http://snomed.info/sct", + "version": "2019-09", + "code": "185463005", + "display": "Visit out of hours (procedure)" + }, + { + "system": "http://snomed.info/sct", + "version": "2019-09", + "code": "185464004", + "display": "Out of hours visit - not night visit (procedure)" + }, + { + "system": "http://snomed.info/sct", + "version": "2019-09", + "code": "185465003", + "display": "Weekend visit (procedure)" + }, + { + "system": "http://snomed.info/sct", + "version": "2019-09", + "code": "3391000175108", + "display": "Office visit for pediatric care and assessment (procedure)" + }, + { + "system": "http://snomed.info/sct", + "version": "2019-09", + "code": "439740005", + "display": "Postoperative follow-up visit (procedure)" + }, + { + "system": "http://www.ama-assn.org/go/cpt", + "version": "2020", + "code": "99201", + "display": "Office or other outpatient visit for the evaluation and management of a new patient, which requires these 3 key components: A problem focused history; A problem focused examination; Straightforward medical decision making. Counseling and/or coordination of care with other physicians, other qualified health care professionals, or agencies are provided consistent with the nature of the problem(s) and the patient's and/or family's needs. Usually, the presenting problem(s) are self limited or minor. Typically, 10 minutes are spent face-to-face with the patient and/or family." + }, + { + "system": "http://www.ama-assn.org/go/cpt", + "version": "2020", + "code": "99202", + "display": "Office or other outpatient visit for the evaluation and management of a new patient, which requires these 3 key components: An expanded problem focused history; An expanded problem focused examination; Straightforward medical decision making. Counseling and/or coordination of care with other physicians, other qualified health care professionals, or agencies are provided consistent with the nature of the problem(s) and the patient's and/or family's needs. Usually, the presenting problem(s) are of low to moderate severity. Typically, 20 minutes are spent face-to-face with the patient and/or family." + }, + { + "system": "http://www.ama-assn.org/go/cpt", + "version": "2020", + "code": "99203", + "display": "Office or other outpatient visit for the evaluation and management of a new patient, which requires these 3 key components: A detailed history; A detailed examination; Medical decision making of low complexity. Counseling and/or coordination of care with other physicians, other qualified health care professionals, or agencies are provided consistent with the nature of the problem(s) and the patient's and/or family's needs. Usually, the presenting problem(s) are of moderate severity. Typically, 30 minutes are spent face-to-face with the patient and/or family." + }, + { + "system": "http://www.ama-assn.org/go/cpt", + "version": "2020", + "code": "99204", + "display": "Office or other outpatient visit for the evaluation and management of a new patient, which requires these 3 key components: A comprehensive history; A comprehensive examination; Medical decision making of moderate complexity. Counseling and/or coordination of care with other physicians, other qualified health care professionals, or agencies are provided consistent with the nature of the problem(s) and the patient's and/or family's needs. Usually, the presenting problem(s) are of moderate to high severity. Typically, 45 minutes are spent face-to-face with the patient and/or family." + }, + { + "system": "http://www.ama-assn.org/go/cpt", + "version": "2020", + "code": "99205", + "display": "Office or other outpatient visit for the evaluation and management of a new patient, which requires these 3 key components: A comprehensive history; A comprehensive examination; Medical decision making of high complexity. Counseling and/or coordination of care with other physicians, other qualified health care professionals, or agencies are provided consistent with the nature of the problem(s) and the patient's and/or family's needs. Usually, the presenting problem(s) are of moderate to high severity. Typically, 60 minutes are spent face-to-face with the patient and/or family." + }, + { + "system": "http://www.ama-assn.org/go/cpt", + "version": "2020", + "code": "99212", + "display": "Office or other outpatient visit for the evaluation and management of an established patient, which requires at least 2 of these 3 key components: A problem focused history; A problem focused examination; Straightforward medical decision making. Counseling and/or coordination of care with other physicians, other qualified health care professionals, or agencies are provided consistent with the nature of the problem(s) and the patient's and/or family's needs. Usually, the presenting problem(s) are self limited or minor. Typically, 10 minutes are spent face-to-face with the patient and/or family." + }, + { + "system": "http://www.ama-assn.org/go/cpt", + "version": "2020", + "code": "99213", + "display": "Office or other outpatient visit for the evaluation and management of an established patient, which requires at least 2 of these 3 key components: An expanded problem focused history; An expanded problem focused examination; Medical decision making of low complexity. Counseling and coordination of care with other physicians, other qualified health care professionals, or agencies are provided consistent with the nature of the problem(s) and the patient's and/or family's needs. Usually, the presenting problem(s) are of low to moderate severity. Typically, 15 minutes are spent face-to-face with the patient and/or family." + }, + { + "system": "http://www.ama-assn.org/go/cpt", + "version": "2020", + "code": "99214", + "display": "Office or other outpatient visit for the evaluation and management of an established patient, which requires at least 2 of these 3 key components: A detailed history; A detailed examination; Medical decision making of moderate complexity. Counseling and/or coordination of care with other physicians, other qualified health care professionals, or agencies are provided consistent with the nature of the problem(s) and the patient's and/or family's needs. Usually, the presenting problem(s) are of moderate to high severity. Typically, 25 minutes are spent face-to-face with the patient and/or family." + }, + { + "system": "http://www.ama-assn.org/go/cpt", + "version": "2020", + "code": "99215", + "display": "Office or other outpatient visit for the evaluation and management of an established patient, which requires at least 2 of these 3 key components: A comprehensive history; A comprehensive examination; Medical decision making of high complexity. Counseling and/or coordination of care with other physicians, other qualified health care professionals, or agencies are provided consistent with the nature of the problem(s) and the patient's and/or family's needs. Usually, the presenting problem(s) are of moderate to high severity. Typically, 40 minutes are spent face-to-face with the patient and/or family." + }, + { + "system": "http://snomed.info/sct", + "version": "2018-03", + "code": "30346009", + "display": "Evaluation and management of established outpatient in office or other outpatient facility (procedure)" + }, + { + "system": "http://snomed.info/sct", + "version": "2018-03", + "code": "37894004", + "display": "Evaluation and management of new outpatient in office or other outpatient facility (procedure)" + }, + { + "system": "http://snomed.info/sct", + "version": "2019-09", + "code": "185463005", + "display": "Visit out of hours (procedure)" + }, + { + "system": "http://snomed.info/sct", + "version": "2019-09", + "code": "185464004", + "display": "Out of hours visit - not night visit (procedure)" + }, + { + "system": "http://snomed.info/sct", + "version": "2019-09", + "code": "185465003", + "display": "Weekend visit (procedure)" + }, + { + "system": "http://snomed.info/sct", + "version": "2019-09", + "code": "3391000175108", + "display": "Office visit for pediatric care and assessment (procedure)" + }, + { + "system": "http://snomed.info/sct", + "version": "2019-09", + "code": "439740005", + "display": "Postoperative follow-up visit (procedure)" + } + ] + } + }, + "request": { + "method": "PUT", + "url": "ValueSet/2.16.840.1.113883.3.464.1003.101.12.1001", + "ifNoneExist": "ValueSet?url=http://cts.nlm.nih.gov/fhir/ValueSet/2.16.840.1.113883.3.464.1003.101.12.1001&version=20180311" + } + } + ]} diff --git a/hapi-fhir-storage-mdm/pom.xml b/hapi-fhir-storage-mdm/pom.xml index 0158106fa8b..6612210e842 100644 --- a/hapi-fhir-storage-mdm/pom.xml +++ b/hapi-fhir-storage-mdm/pom.xml @@ -5,7 +5,7 @@ ca.uhn.hapi.fhir hapi-deployable-pom - 6.9.7-SNAPSHOT + 6.11.7-SNAPSHOT ../hapi-deployable-pom/pom.xml diff --git a/hapi-fhir-storage-mdm/src/main/java/ca/uhn/fhir/jpa/mdm/interceptor/MdmSubmitterInterceptorLoader.java b/hapi-fhir-storage-mdm/src/main/java/ca/uhn/fhir/jpa/mdm/interceptor/MdmSubmitterInterceptorLoader.java index 1e5cb18bcfd..eac3ab35686 100644 --- a/hapi-fhir-storage-mdm/src/main/java/ca/uhn/fhir/jpa/mdm/interceptor/MdmSubmitterInterceptorLoader.java +++ b/hapi-fhir-storage-mdm/src/main/java/ca/uhn/fhir/jpa/mdm/interceptor/MdmSubmitterInterceptorLoader.java @@ -27,12 +27,11 @@ import ca.uhn.fhir.mdm.api.IMdmSettings; import ca.uhn.fhir.mdm.interceptor.IMdmStorageInterceptor; import ca.uhn.fhir.mdm.interceptor.MdmSearchExpandingInterceptor; import ca.uhn.fhir.mdm.log.Logs; +import jakarta.annotation.PostConstruct; import org.hl7.fhir.dstu2.model.Subscription; import org.slf4j.Logger; import org.springframework.beans.factory.annotation.Autowired; -import javax.annotation.PostConstruct; - public class MdmSubmitterInterceptorLoader { private static final Logger ourLog = Logs.getMdmTroubleshootingLog(); diff --git a/hapi-fhir-storage-mdm/src/main/java/ca/uhn/fhir/mdm/batch2/LoadGoldenIdsStep.java b/hapi-fhir-storage-mdm/src/main/java/ca/uhn/fhir/mdm/batch2/LoadGoldenIdsStep.java index 60d4bb98ade..df8f3e78d48 100644 --- a/hapi-fhir-storage-mdm/src/main/java/ca/uhn/fhir/mdm/batch2/LoadGoldenIdsStep.java +++ b/hapi-fhir-storage-mdm/src/main/java/ca/uhn/fhir/mdm/batch2/LoadGoldenIdsStep.java @@ -29,8 +29,7 @@ import ca.uhn.fhir.batch2.jobs.step.IIdChunkProducer; import ca.uhn.fhir.batch2.jobs.step.ResourceIdListStep; import ca.uhn.fhir.jpa.api.svc.IGoldenResourceSearchSvc; import ca.uhn.fhir.mdm.batch2.clear.MdmClearJobParameters; - -import javax.annotation.Nonnull; +import jakarta.annotation.Nonnull; public class LoadGoldenIdsStep implements IJobStepWorker { diff --git a/hapi-fhir-storage-mdm/src/main/java/ca/uhn/fhir/mdm/batch2/MdmChunkRangeJson.java b/hapi-fhir-storage-mdm/src/main/java/ca/uhn/fhir/mdm/batch2/MdmChunkRangeJson.java index 54a89cd63b9..bc6678ac135 100644 --- a/hapi-fhir-storage-mdm/src/main/java/ca/uhn/fhir/mdm/batch2/MdmChunkRangeJson.java +++ b/hapi-fhir-storage-mdm/src/main/java/ca/uhn/fhir/mdm/batch2/MdmChunkRangeJson.java @@ -21,9 +21,8 @@ package ca.uhn.fhir.mdm.batch2; import ca.uhn.fhir.batch2.jobs.chunk.ChunkRangeJson; import com.fasterxml.jackson.annotation.JsonProperty; - -import javax.annotation.Nonnull; -import javax.annotation.Nullable; +import jakarta.annotation.Nonnull; +import jakarta.annotation.Nullable; public class MdmChunkRangeJson extends ChunkRangeJson { @Nonnull diff --git a/hapi-fhir-storage-mdm/src/main/java/ca/uhn/fhir/mdm/batch2/MdmGenerateRangeChunksStep.java b/hapi-fhir-storage-mdm/src/main/java/ca/uhn/fhir/mdm/batch2/MdmGenerateRangeChunksStep.java index 9b4b2002e63..12245841e40 100644 --- a/hapi-fhir-storage-mdm/src/main/java/ca/uhn/fhir/mdm/batch2/MdmGenerateRangeChunksStep.java +++ b/hapi-fhir-storage-mdm/src/main/java/ca/uhn/fhir/mdm/batch2/MdmGenerateRangeChunksStep.java @@ -27,11 +27,11 @@ import ca.uhn.fhir.batch2.api.StepExecutionDetails; import ca.uhn.fhir.batch2.api.VoidModel; import ca.uhn.fhir.batch2.util.Batch2Constants; import ca.uhn.fhir.mdm.batch2.clear.MdmClearJobParameters; +import jakarta.annotation.Nonnull; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import java.util.Date; -import javax.annotation.Nonnull; public class MdmGenerateRangeChunksStep implements IFirstJobStepWorker { private static final Logger ourLog = LoggerFactory.getLogger(MdmGenerateRangeChunksStep.class); diff --git a/hapi-fhir-storage-mdm/src/main/java/ca/uhn/fhir/mdm/batch2/MdmIdChunkProducer.java b/hapi-fhir-storage-mdm/src/main/java/ca/uhn/fhir/mdm/batch2/MdmIdChunkProducer.java index 8411e7177a8..90ad9e236cf 100644 --- a/hapi-fhir-storage-mdm/src/main/java/ca/uhn/fhir/mdm/batch2/MdmIdChunkProducer.java +++ b/hapi-fhir-storage-mdm/src/main/java/ca/uhn/fhir/mdm/batch2/MdmIdChunkProducer.java @@ -21,13 +21,13 @@ package ca.uhn.fhir.mdm.batch2; import ca.uhn.fhir.batch2.jobs.step.IIdChunkProducer; import ca.uhn.fhir.interceptor.model.RequestPartitionId; -import ca.uhn.fhir.jpa.api.pid.IResourcePidList; +import ca.uhn.fhir.jpa.api.pid.IResourcePidStream; import ca.uhn.fhir.jpa.api.svc.IGoldenResourceSearchSvc; +import jakarta.annotation.Nullable; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import java.util.Date; -import javax.annotation.Nonnull; public class MdmIdChunkProducer implements IIdChunkProducer { private static final Logger ourLog = LoggerFactory.getLogger(MdmIdChunkProducer.class); @@ -38,21 +38,17 @@ public class MdmIdChunkProducer implements IIdChunkProducer { } @Override - public IResourcePidList fetchResourceIdsPage( - Date theNextStart, - Date theEnd, - @Nonnull Integer thePageSize, - RequestPartitionId theRequestPartitionId, - MdmChunkRangeJson theData) { + public IResourcePidStream fetchResourceIdStream( + Date theStart, Date theEnd, @Nullable RequestPartitionId theRequestPartitionId, MdmChunkRangeJson theData) { String resourceType = theData.getResourceType(); ourLog.info( "Fetching golden resource ID chunk for resource type {} - Range {} - {}", resourceType, - theNextStart, + theStart, theEnd); - return myGoldenResourceSearchSvc.fetchGoldenResourceIdsPage( - theNextStart, theEnd, thePageSize, theRequestPartitionId, resourceType); + return myGoldenResourceSearchSvc.fetchGoldenResourceIdStream( + theStart, theEnd, theRequestPartitionId, resourceType); } } diff --git a/hapi-fhir-storage-mdm/src/main/java/ca/uhn/fhir/mdm/batch2/clear/MdmClearJobParameters.java b/hapi-fhir-storage-mdm/src/main/java/ca/uhn/fhir/mdm/batch2/clear/MdmClearJobParameters.java index fed4b7d3615..40330274b9f 100644 --- a/hapi-fhir-storage-mdm/src/main/java/ca/uhn/fhir/mdm/batch2/clear/MdmClearJobParameters.java +++ b/hapi-fhir-storage-mdm/src/main/java/ca/uhn/fhir/mdm/batch2/clear/MdmClearJobParameters.java @@ -21,12 +21,12 @@ package ca.uhn.fhir.mdm.batch2.clear; import ca.uhn.fhir.batch2.jobs.parameters.PartitionedJobParameters; import com.fasterxml.jackson.annotation.JsonProperty; +import jakarta.annotation.Nonnull; +import jakarta.validation.constraints.Pattern; import org.apache.commons.lang3.Validate; import java.util.ArrayList; import java.util.List; -import javax.annotation.Nonnull; -import javax.validation.constraints.Pattern; public class MdmClearJobParameters extends PartitionedJobParameters { @JsonProperty("resourceType") diff --git a/hapi-fhir-storage-mdm/src/main/java/ca/uhn/fhir/mdm/batch2/clear/MdmClearJobParametersValidator.java b/hapi-fhir-storage-mdm/src/main/java/ca/uhn/fhir/mdm/batch2/clear/MdmClearJobParametersValidator.java index 2c805ea87c4..da562aa8084 100644 --- a/hapi-fhir-storage-mdm/src/main/java/ca/uhn/fhir/mdm/batch2/clear/MdmClearJobParametersValidator.java +++ b/hapi-fhir-storage-mdm/src/main/java/ca/uhn/fhir/mdm/batch2/clear/MdmClearJobParametersValidator.java @@ -23,12 +23,12 @@ import ca.uhn.fhir.batch2.api.IJobParametersValidator; import ca.uhn.fhir.jpa.api.dao.DaoRegistry; import ca.uhn.fhir.mdm.api.IMdmSettings; import ca.uhn.fhir.rest.api.server.RequestDetails; +import jakarta.annotation.Nonnull; +import jakarta.annotation.Nullable; import java.util.ArrayList; import java.util.Collections; import java.util.List; -import javax.annotation.Nonnull; -import javax.annotation.Nullable; public class MdmClearJobParametersValidator implements IJobParametersValidator { diff --git a/hapi-fhir-storage-mdm/src/main/java/ca/uhn/fhir/mdm/batch2/clear/MdmClearStep.java b/hapi-fhir-storage-mdm/src/main/java/ca/uhn/fhir/mdm/batch2/clear/MdmClearStep.java index cc8d020a9f7..d6b4c16783e 100644 --- a/hapi-fhir-storage-mdm/src/main/java/ca/uhn/fhir/mdm/batch2/clear/MdmClearStep.java +++ b/hapi-fhir-storage-mdm/src/main/java/ca/uhn/fhir/mdm/batch2/clear/MdmClearStep.java @@ -38,6 +38,7 @@ import ca.uhn.fhir.rest.api.server.storage.IResourcePersistentId; import ca.uhn.fhir.rest.api.server.storage.TransactionDetails; import ca.uhn.fhir.util.StopWatch; import com.google.common.annotations.VisibleForTesting; +import jakarta.annotation.Nonnull; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; @@ -46,7 +47,6 @@ import org.springframework.transaction.support.TransactionCallback; import java.util.List; import java.util.concurrent.TimeUnit; -import javax.annotation.Nonnull; @SuppressWarnings("rawtypes") public class MdmClearStep implements IJobStepWorker { diff --git a/hapi-fhir-storage-mdm/src/main/java/ca/uhn/fhir/mdm/batch2/submit/MdmInflateAndSubmitResourcesStep.java b/hapi-fhir-storage-mdm/src/main/java/ca/uhn/fhir/mdm/batch2/submit/MdmInflateAndSubmitResourcesStep.java index 67e7e9f3d2a..386881adb57 100644 --- a/hapi-fhir-storage-mdm/src/main/java/ca/uhn/fhir/mdm/batch2/submit/MdmInflateAndSubmitResourcesStep.java +++ b/hapi-fhir-storage-mdm/src/main/java/ca/uhn/fhir/mdm/batch2/submit/MdmInflateAndSubmitResourcesStep.java @@ -34,13 +34,13 @@ import ca.uhn.fhir.rest.api.server.storage.IResourcePersistentId; import ca.uhn.fhir.rest.server.exceptions.ResourceNotFoundException; import ca.uhn.fhir.rest.server.interceptor.ResponseTerminologyTranslationSvc; import ca.uhn.fhir.util.Logs; +import jakarta.annotation.Nonnull; import org.hl7.fhir.instance.model.api.IBaseResource; import org.slf4j.Logger; import org.springframework.beans.factory.annotation.Autowired; import java.util.ArrayList; import java.util.List; -import javax.annotation.Nonnull; public class MdmInflateAndSubmitResourcesStep implements IJobStepWorker { diff --git a/hapi-fhir-storage-mdm/src/main/java/ca/uhn/fhir/mdm/batch2/submit/MdmSubmitJobParametersValidator.java b/hapi-fhir-storage-mdm/src/main/java/ca/uhn/fhir/mdm/batch2/submit/MdmSubmitJobParametersValidator.java index d67d3f03f87..b6db1d8a375 100644 --- a/hapi-fhir-storage-mdm/src/main/java/ca/uhn/fhir/mdm/batch2/submit/MdmSubmitJobParametersValidator.java +++ b/hapi-fhir-storage-mdm/src/main/java/ca/uhn/fhir/mdm/batch2/submit/MdmSubmitJobParametersValidator.java @@ -27,10 +27,10 @@ import ca.uhn.fhir.jpa.searchparam.MatchUrlService; import ca.uhn.fhir.mdm.api.IMdmSettings; import ca.uhn.fhir.rest.api.server.RequestDetails; import ca.uhn.fhir.rest.server.exceptions.InvalidRequestException; +import jakarta.annotation.Nonnull; import java.util.ArrayList; import java.util.List; -import javax.annotation.Nonnull; public class MdmSubmitJobParametersValidator implements IJobParametersValidator { diff --git a/hapi-fhir-storage-test-utilities/pom.xml b/hapi-fhir-storage-test-utilities/pom.xml index db577ff2395..d58ac7db8c2 100644 --- a/hapi-fhir-storage-test-utilities/pom.xml +++ b/hapi-fhir-storage-test-utilities/pom.xml @@ -5,7 +5,7 @@ ca.uhn.hapi.fhir hapi-deployable-pom - 6.9.7-SNAPSHOT + 6.11.7-SNAPSHOT ../hapi-deployable-pom/pom.xml diff --git a/hapi-fhir-storage-test-utilities/src/main/java/ca/uhn/fhir/storage/test/DaoTestDataBuilder.java b/hapi-fhir-storage-test-utilities/src/main/java/ca/uhn/fhir/storage/test/DaoTestDataBuilder.java index 625addbd379..012a2856ca9 100644 --- a/hapi-fhir-storage-test-utilities/src/main/java/ca/uhn/fhir/storage/test/DaoTestDataBuilder.java +++ b/hapi-fhir-storage-test-utilities/src/main/java/ca/uhn/fhir/storage/test/DaoTestDataBuilder.java @@ -39,7 +39,8 @@ import org.springframework.context.annotation.Configuration; /** * Implements ITestDataBuilder via a live DaoRegistry. - * + * Note: this implements {@link AfterEachCallback} and will delete any resources created when registered + * via {@link org.junit.jupiter.api.extension.RegisterExtension}. * Add the inner {@link Config} to your spring context to inject this. * For convenience, you can still implement ITestDataBuilder on your test class, and delegate the missing methods to this bean. */ @@ -75,7 +76,9 @@ public class DaoTestDataBuilder implements ITestDataBuilder.WithSupport, ITestDa //noinspection rawtypes IFhirResourceDao dao = myDaoRegistry.getResourceDao(theResource.getClass()); //noinspection unchecked - return dao.update(theResource, mySrd).getId().toUnqualifiedVersionless(); + IIdType id = dao.update(theResource, mySrd).getId().toUnqualifiedVersionless(); + myIds.put(theResource.fhirType(), id); + return id; } @Override diff --git a/hapi-fhir-storage-test-utilities/src/test/java/ca/uhn/fhir/rest/server/messaging/json/BaseJsonMessageTest.java b/hapi-fhir-storage-test-utilities/src/test/java/ca/uhn/fhir/rest/server/messaging/json/BaseJsonMessageTest.java index 43f2b0801cd..1154568b04a 100644 --- a/hapi-fhir-storage-test-utilities/src/test/java/ca/uhn/fhir/rest/server/messaging/json/BaseJsonMessageTest.java +++ b/hapi-fhir-storage-test-utilities/src/test/java/ca/uhn/fhir/rest/server/messaging/json/BaseJsonMessageTest.java @@ -13,7 +13,7 @@ import org.hl7.fhir.instance.model.api.IBaseResource; import org.hl7.fhir.r4.model.Patient; import org.junit.jupiter.api.Test; -import javax.annotation.Nonnull; +import jakarta.annotation.Nonnull; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertNull; diff --git a/hapi-fhir-storage/pom.xml b/hapi-fhir-storage/pom.xml index db2d2d1c0cb..c4059235dcf 100644 --- a/hapi-fhir-storage/pom.xml +++ b/hapi-fhir-storage/pom.xml @@ -5,7 +5,7 @@ ca.uhn.hapi.fhir hapi-deployable-pom - 6.9.7-SNAPSHOT + 6.11.7-SNAPSHOT ../hapi-deployable-pom/pom.xml @@ -85,7 +85,7 @@ org.hibernate.search - hibernate-search-mapper-orm + hibernate-search-mapper-orm-orm6 org.hibernate.search @@ -132,8 +132,8 @@ - javax.servlet - javax.servlet-api + jakarta.servlet + jakarta.servlet-api provided @@ -160,6 +160,12 @@ org.springframework.data spring-data-commons + + ca.uhn.hapi.fhir + hapi-fhir-caching-caffeine + ${project.version} + test + diff --git a/hapi-fhir-storage/src/main/java/ca/uhn/fhir/cache/BaseResourceCacheSynchronizer.java b/hapi-fhir-storage/src/main/java/ca/uhn/fhir/cache/BaseResourceCacheSynchronizer.java index dc05cb761ee..6073c8dc222 100644 --- a/hapi-fhir-storage/src/main/java/ca/uhn/fhir/cache/BaseResourceCacheSynchronizer.java +++ b/hapi-fhir-storage/src/main/java/ca/uhn/fhir/cache/BaseResourceCacheSynchronizer.java @@ -32,6 +32,9 @@ import ca.uhn.fhir.rest.api.server.SystemRequestDetails; import ca.uhn.fhir.rest.server.util.ISearchParamRegistry; import ca.uhn.fhir.subscription.SubscriptionConstants; import com.google.common.annotations.VisibleForTesting; +import jakarta.annotation.Nonnull; +import jakarta.annotation.PostConstruct; +import jakarta.annotation.PreDestroy; import org.apache.commons.lang3.time.DateUtils; import org.hl7.fhir.instance.model.api.IBaseResource; import org.hl7.fhir.instance.model.api.IIdType; @@ -46,9 +49,6 @@ import java.util.Collection; import java.util.List; import java.util.concurrent.Semaphore; import java.util.stream.Collectors; -import javax.annotation.Nonnull; -import javax.annotation.PostConstruct; -import javax.annotation.PreDestroy; public abstract class BaseResourceCacheSynchronizer implements IResourceChangeListener { private static final Logger ourLog = LoggerFactory.getLogger(BaseResourceCacheSynchronizer.class); diff --git a/hapi-fhir-storage/src/main/java/ca/uhn/fhir/jpa/api/config/JpaStorageSettings.java b/hapi-fhir-storage/src/main/java/ca/uhn/fhir/jpa/api/config/JpaStorageSettings.java index 23f7451be70..44f05007c05 100644 --- a/hapi-fhir-storage/src/main/java/ca/uhn/fhir/jpa/api/config/JpaStorageSettings.java +++ b/hapi-fhir-storage/src/main/java/ca/uhn/fhir/jpa/api/config/JpaStorageSettings.java @@ -29,6 +29,8 @@ import ca.uhn.fhir.system.HapiSystemProperties; import ca.uhn.fhir.util.HapiExtensions; import ca.uhn.fhir.validation.FhirValidator; import com.google.common.collect.Sets; +import jakarta.annotation.Nonnull; +import jakarta.annotation.Nullable; import org.apache.commons.io.FileUtils; import org.apache.commons.lang3.StringUtils; import org.apache.commons.lang3.Validate; @@ -43,8 +45,6 @@ import java.util.Collections; import java.util.List; import java.util.Set; import java.util.TreeSet; -import javax.annotation.Nonnull; -import javax.annotation.Nullable; @SuppressWarnings("JavadocLinkAsPlainText") public class JpaStorageSettings extends StorageSettings { @@ -267,14 +267,6 @@ public class JpaStorageSettings extends StorageSettings { * @since 5.6.0 */ private boolean myAdvancedHSearchIndexing = false; - /** - * If set to a positive number, any resources with a character length at or below the given number - * of characters will be stored inline in the HFJ_RES_VER table instead of using a - * separate LOB column. - * - * @since 5.7.0 - */ - private int myInlineResourceTextBelowSize = 0; /** * @since 5.7.0 @@ -381,25 +373,21 @@ public class JpaStorageSettings extends StorageSettings { } /** - * If set to a positive number, any resources with a character length at or below the given number - * of characters will be stored inline in the HFJ_RES_VER table instead of using a - * separate LOB column. - * * @since 5.7.0 + * @deprecated This setting no longer does anything as of HAPI FHIR 7.0.0 */ + @Deprecated public int getInlineResourceTextBelowSize() { - return myInlineResourceTextBelowSize; + return 0; } /** - * If set to a positive number, any resources with a character length at or below the given number - * of characters will be stored inline in the HFJ_RES_VER table instead of using a - * separate LOB column. - * * @since 5.7.0 + * @deprecated This setting no longer does anything as of HAPI FHIR 7.0.0 */ + @Deprecated public void setInlineResourceTextBelowSize(int theInlineResourceTextBelowSize) { - myInlineResourceTextBelowSize = theInlineResourceTextBelowSize; + // ignored } /** diff --git a/hapi-fhir-storage/src/main/java/ca/uhn/fhir/jpa/api/dao/DaoRegistry.java b/hapi-fhir-storage/src/main/java/ca/uhn/fhir/jpa/api/dao/DaoRegistry.java index 82bbd2cfe7c..c6285b9c183 100644 --- a/hapi-fhir-storage/src/main/java/ca/uhn/fhir/jpa/api/dao/DaoRegistry.java +++ b/hapi-fhir-storage/src/main/java/ca/uhn/fhir/jpa/api/dao/DaoRegistry.java @@ -25,6 +25,7 @@ import ca.uhn.fhir.i18n.Msg; import ca.uhn.fhir.jpa.api.IDaoRegistry; import ca.uhn.fhir.model.dstu2.valueset.ResourceTypeEnum; import ca.uhn.fhir.rest.server.exceptions.InvalidRequestException; +import jakarta.annotation.Nullable; import org.apache.commons.lang3.Validate; import org.hl7.fhir.instance.model.api.IBaseResource; import org.springframework.beans.BeansException; @@ -41,7 +42,6 @@ import java.util.List; import java.util.Map; import java.util.Set; import java.util.stream.Collectors; -import javax.annotation.Nullable; public class DaoRegistry implements ApplicationContextAware, IDaoRegistry { private ApplicationContext myAppCtx; diff --git a/hapi-fhir-storage/src/main/java/ca/uhn/fhir/jpa/api/dao/IFhirResourceDao.java b/hapi-fhir-storage/src/main/java/ca/uhn/fhir/jpa/api/dao/IFhirResourceDao.java index 6f08db9be85..53ad3feebf7 100644 --- a/hapi-fhir-storage/src/main/java/ca/uhn/fhir/jpa/api/dao/IFhirResourceDao.java +++ b/hapi-fhir-storage/src/main/java/ca/uhn/fhir/jpa/api/dao/IFhirResourceDao.java @@ -42,6 +42,9 @@ import ca.uhn.fhir.rest.param.HistorySearchDateRangeParam; import ca.uhn.fhir.rest.server.exceptions.InvalidRequestException; import ca.uhn.fhir.rest.server.exceptions.ResourceGoneException; import ca.uhn.fhir.rest.server.exceptions.ResourceNotFoundException; +import jakarta.annotation.Nonnull; +import jakarta.annotation.Nullable; +import jakarta.servlet.http.HttpServletResponse; import org.hl7.fhir.instance.model.api.IBaseMetaType; import org.hl7.fhir.instance.model.api.IBaseParameters; import org.hl7.fhir.instance.model.api.IBaseResource; @@ -51,9 +54,7 @@ import java.util.Collection; import java.util.Date; import java.util.List; import java.util.Map; -import javax.annotation.Nonnull; -import javax.annotation.Nullable; -import javax.servlet.http.HttpServletResponse; +import java.util.stream.Stream; /** * Note that this interface is not considered a stable interface. While it is possible to build applications @@ -326,18 +327,29 @@ public interface IFhirResourceDao extends IDao { /** * @deprecated Use {@link #search(SearchParameterMap, RequestDetails)} instead + * @throws InvalidRequestException If a SearchParameter is not known to the server */ - IBundleProvider search(SearchParameterMap theParams); + IBundleProvider search(SearchParameterMap theParams) throws InvalidRequestException; - IBundleProvider search(SearchParameterMap theParams, RequestDetails theRequestDetails); + /** + * * + * @throws InvalidRequestException If a SearchParameter is not known to the server + */ + IBundleProvider search(SearchParameterMap theParams, RequestDetails theRequestDetails) + throws InvalidRequestException; + /** + * * + * @throws InvalidRequestException If a SearchParameter is not known to the server + */ IBundleProvider search( - SearchParameterMap theParams, RequestDetails theRequestDetails, HttpServletResponse theServletResponse); + SearchParameterMap theParams, RequestDetails theRequestDetails, HttpServletResponse theServletResponse) + throws InvalidRequestException; /** * Search for IDs for processing a match URLs, etc. */ - default List searchForIds( + default List searchForIds( SearchParameterMap theParams, RequestDetails theRequest) { return searchForIds(theParams, theRequest, null); } @@ -349,13 +361,29 @@ public interface IFhirResourceDao extends IDao { * create/update, this is the resource being searched for * @since 5.5.0 */ - default List searchForIds( + default List searchForIds( SearchParameterMap theParams, RequestDetails theRequest, @Nullable IBaseResource theConditionalOperationTargetOrNull) { return searchForIds(theParams, theRequest); } + /** + * Search results matching theParams. + * The Stream MUST be called within a transaction because the stream wraps an open query ResultSet. + * The Stream MUST be closed to avoid leaking resources. + * @param theParams the search + * @param theRequest for partition target info + * @return a Stream than MUST only be used within the calling transaction. + */ + default > Stream searchForIdStream( + SearchParameterMap theParams, + RequestDetails theRequest, + @Nullable IBaseResource theConditionalOperationTargetOrNull) { + List iResourcePersistentIds = searchForIds(theParams, theRequest); + return iResourcePersistentIds.stream(); + } + /** * Takes a map of incoming raw search parameters and translates/parses them into * appropriate {@link IQueryParameterType} instances of the appropriate type diff --git a/hapi-fhir-storage/src/main/java/ca/uhn/fhir/jpa/api/dao/IFhirResourceDaoCodeSystem.java b/hapi-fhir-storage/src/main/java/ca/uhn/fhir/jpa/api/dao/IFhirResourceDaoCodeSystem.java index bf323ac8226..813739ac0be 100644 --- a/hapi-fhir-storage/src/main/java/ca/uhn/fhir/jpa/api/dao/IFhirResourceDaoCodeSystem.java +++ b/hapi-fhir-storage/src/main/java/ca/uhn/fhir/jpa/api/dao/IFhirResourceDaoCodeSystem.java @@ -23,6 +23,7 @@ import ca.uhn.fhir.context.FhirContext; import ca.uhn.fhir.context.support.IValidationSupport; import ca.uhn.fhir.rest.api.server.RequestDetails; import ca.uhn.fhir.util.ParametersUtil; +import jakarta.annotation.Nonnull; import org.hl7.fhir.instance.model.api.IBaseCoding; import org.hl7.fhir.instance.model.api.IBaseDatatype; import org.hl7.fhir.instance.model.api.IBaseParameters; @@ -32,8 +33,8 @@ import org.hl7.fhir.instance.model.api.IPrimitiveType; import org.hl7.fhir.r4.model.codesystems.ConceptSubsumptionOutcome; import org.springframework.transaction.annotation.Transactional; +import java.util.Collection; import java.util.List; -import javax.annotation.Nonnull; public interface IFhirResourceDaoCodeSystem extends IFhirResourceDao { @@ -55,6 +56,15 @@ public interface IFhirResourceDaoCodeSystem extends IFh IPrimitiveType theDisplayLanguage, RequestDetails theRequestDetails); + @Nonnull + IValidationSupport.LookupCodeResult lookupCode( + IPrimitiveType theCode, + IPrimitiveType theSystem, + IBaseCoding theCoding, + IPrimitiveType theDisplayLanguage, + Collection> thePropertyNames, + RequestDetails theRequestDetails); + SubsumesResult subsumes( IPrimitiveType theCodeA, IPrimitiveType theCodeB, diff --git a/hapi-fhir-storage/src/main/java/ca/uhn/fhir/jpa/api/dao/IFhirResourceDaoComposition.java b/hapi-fhir-storage/src/main/java/ca/uhn/fhir/jpa/api/dao/IFhirResourceDaoComposition.java index a269de9bec5..5fd5cadaadc 100644 --- a/hapi-fhir-storage/src/main/java/ca/uhn/fhir/jpa/api/dao/IFhirResourceDaoComposition.java +++ b/hapi-fhir-storage/src/main/java/ca/uhn/fhir/jpa/api/dao/IFhirResourceDaoComposition.java @@ -23,12 +23,11 @@ import ca.uhn.fhir.rest.api.SortSpec; import ca.uhn.fhir.rest.api.server.IBundleProvider; import ca.uhn.fhir.rest.api.server.RequestDetails; import ca.uhn.fhir.rest.param.DateRangeParam; +import jakarta.servlet.http.HttpServletRequest; import org.hl7.fhir.instance.model.api.IBaseResource; import org.hl7.fhir.instance.model.api.IIdType; import org.hl7.fhir.instance.model.api.IPrimitiveType; -import javax.servlet.http.HttpServletRequest; - public interface IFhirResourceDaoComposition extends IFhirResourceDao { IBundleProvider getDocumentForComposition( diff --git a/hapi-fhir-storage/src/main/java/ca/uhn/fhir/jpa/api/dao/IFhirResourceDaoEncounter.java b/hapi-fhir-storage/src/main/java/ca/uhn/fhir/jpa/api/dao/IFhirResourceDaoEncounter.java index f97b47dacad..06b3d846a2c 100644 --- a/hapi-fhir-storage/src/main/java/ca/uhn/fhir/jpa/api/dao/IFhirResourceDaoEncounter.java +++ b/hapi-fhir-storage/src/main/java/ca/uhn/fhir/jpa/api/dao/IFhirResourceDaoEncounter.java @@ -22,12 +22,11 @@ package ca.uhn.fhir.jpa.api.dao; import ca.uhn.fhir.rest.api.SortSpec; import ca.uhn.fhir.rest.api.server.IBundleProvider; import ca.uhn.fhir.rest.param.DateRangeParam; +import jakarta.servlet.http.HttpServletRequest; import org.hl7.fhir.instance.model.api.IBaseResource; import org.hl7.fhir.instance.model.api.IIdType; import org.hl7.fhir.instance.model.api.IPrimitiveType; -import javax.servlet.http.HttpServletRequest; - public interface IFhirResourceDaoEncounter extends IFhirResourceDao { IBundleProvider encounterInstanceEverything( diff --git a/hapi-fhir-storage/src/main/java/ca/uhn/fhir/jpa/api/dao/IFhirResourceDaoObservation.java b/hapi-fhir-storage/src/main/java/ca/uhn/fhir/jpa/api/dao/IFhirResourceDaoObservation.java index 82fbbf9135d..5c7049c59cd 100644 --- a/hapi-fhir-storage/src/main/java/ca/uhn/fhir/jpa/api/dao/IFhirResourceDaoObservation.java +++ b/hapi-fhir-storage/src/main/java/ca/uhn/fhir/jpa/api/dao/IFhirResourceDaoObservation.java @@ -22,10 +22,9 @@ package ca.uhn.fhir.jpa.api.dao; import ca.uhn.fhir.jpa.searchparam.SearchParameterMap; import ca.uhn.fhir.rest.api.server.IBundleProvider; import ca.uhn.fhir.rest.api.server.RequestDetails; +import jakarta.servlet.http.HttpServletResponse; import org.hl7.fhir.instance.model.api.IBaseResource; -import javax.servlet.http.HttpServletResponse; - public interface IFhirResourceDaoObservation extends IFhirResourceDao { /** diff --git a/hapi-fhir-storage/src/main/java/ca/uhn/fhir/jpa/api/dao/IFhirResourceDaoPatient.java b/hapi-fhir-storage/src/main/java/ca/uhn/fhir/jpa/api/dao/IFhirResourceDaoPatient.java index 1a14ce9ad5a..c4021290fee 100644 --- a/hapi-fhir-storage/src/main/java/ca/uhn/fhir/jpa/api/dao/IFhirResourceDaoPatient.java +++ b/hapi-fhir-storage/src/main/java/ca/uhn/fhir/jpa/api/dao/IFhirResourceDaoPatient.java @@ -22,11 +22,10 @@ package ca.uhn.fhir.jpa.api.dao; import ca.uhn.fhir.rest.api.server.IBundleProvider; import ca.uhn.fhir.rest.api.server.RequestDetails; import ca.uhn.fhir.rest.param.TokenOrListParam; +import jakarta.servlet.http.HttpServletRequest; import org.hl7.fhir.instance.model.api.IBaseResource; import org.hl7.fhir.instance.model.api.IIdType; -import javax.servlet.http.HttpServletRequest; - public interface IFhirResourceDaoPatient extends IFhirResourceDao { IBundleProvider patientInstanceEverything( diff --git a/hapi-fhir-storage/src/main/java/ca/uhn/fhir/jpa/api/dao/IFhirSystemDao.java b/hapi-fhir-storage/src/main/java/ca/uhn/fhir/jpa/api/dao/IFhirSystemDao.java index 9c4b2713ba1..cf63693824a 100644 --- a/hapi-fhir-storage/src/main/java/ca/uhn/fhir/jpa/api/dao/IFhirSystemDao.java +++ b/hapi-fhir-storage/src/main/java/ca/uhn/fhir/jpa/api/dao/IFhirSystemDao.java @@ -25,13 +25,13 @@ import ca.uhn.fhir.jpa.api.model.ExpungeOutcome; import ca.uhn.fhir.rest.api.server.IBundleProvider; import ca.uhn.fhir.rest.api.server.RequestDetails; import ca.uhn.fhir.rest.api.server.storage.IResourcePersistentId; +import jakarta.annotation.Nullable; import org.hl7.fhir.instance.model.api.IBaseBundle; import org.springframework.transaction.annotation.Transactional; import java.util.Date; import java.util.List; import java.util.Map; -import javax.annotation.Nullable; /** * Note that this interface is not considered a stable interface. While it is possible to build applications diff --git a/hapi-fhir-storage/src/main/java/ca/uhn/fhir/jpa/api/model/DeleteConflictList.java b/hapi-fhir-storage/src/main/java/ca/uhn/fhir/jpa/api/model/DeleteConflictList.java index 825a4cc69e7..b5de2225856 100644 --- a/hapi-fhir-storage/src/main/java/ca/uhn/fhir/jpa/api/model/DeleteConflictList.java +++ b/hapi-fhir-storage/src/main/java/ca/uhn/fhir/jpa/api/model/DeleteConflictList.java @@ -114,7 +114,7 @@ public class DeleteConflictList implements Iterable { @Override public void remove() { - Assert.isTrue(myLastOperationWasNext); + Assert.isTrue(myLastOperationWasNext, "myLastOperationWasNext is not true"); myNextIndex--; myList.remove(myNextIndex); myLastOperationWasNext = false; diff --git a/hapi-fhir-storage/src/main/java/ca/uhn/fhir/jpa/api/pid/AutoClosingStreamTemplate.java b/hapi-fhir-storage/src/main/java/ca/uhn/fhir/jpa/api/pid/AutoClosingStreamTemplate.java new file mode 100644 index 00000000000..659dcc5521c --- /dev/null +++ b/hapi-fhir-storage/src/main/java/ca/uhn/fhir/jpa/api/pid/AutoClosingStreamTemplate.java @@ -0,0 +1,46 @@ +/*- + * #%L + * HAPI FHIR Storage api + * %% + * Copyright (C) 2014 - 2023 Smile CDR, Inc. + * %% + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * #L% + */ +package ca.uhn.fhir.jpa.api.pid; + +import jakarta.annotation.Nonnull; +import jakarta.annotation.Nullable; + +import java.util.function.Function; +import java.util.function.Supplier; +import java.util.stream.Stream; + +/** + * Template for wrapping access to stream supplier in a try-with-resources block. + */ +class AutoClosingStreamTemplate implements StreamTemplate { + private final Supplier> myStreamQuery; + + AutoClosingStreamTemplate(Supplier> theStreamQuery) { + myStreamQuery = theStreamQuery; + } + + @Nullable + @Override + public R call(@Nonnull Function, R> theCallback) { + try (Stream stream = myStreamQuery.get()) { + return theCallback.apply(stream); + } + } +} diff --git a/hapi-fhir-storage/src/main/java/ca/uhn/fhir/jpa/api/pid/BaseResourcePidList.java b/hapi-fhir-storage/src/main/java/ca/uhn/fhir/jpa/api/pid/BaseResourcePidList.java index 79489a0a295..da279a6b903 100644 --- a/hapi-fhir-storage/src/main/java/ca/uhn/fhir/jpa/api/pid/BaseResourcePidList.java +++ b/hapi-fhir-storage/src/main/java/ca/uhn/fhir/jpa/api/pid/BaseResourcePidList.java @@ -21,10 +21,14 @@ package ca.uhn.fhir.jpa.api.pid; import ca.uhn.fhir.interceptor.model.RequestPartitionId; import ca.uhn.fhir.rest.api.server.storage.IResourcePersistentId; +import jakarta.annotation.Nonnull; +import jakarta.annotation.Nullable; -import java.util.*; -import javax.annotation.Nonnull; -import javax.annotation.Nullable; +import java.util.ArrayList; +import java.util.Collection; +import java.util.Collections; +import java.util.Date; +import java.util.List; public abstract class BaseResourcePidList implements IResourcePidList { @@ -36,7 +40,9 @@ public abstract class BaseResourcePidList implements IResourcePidList { private final RequestPartitionId myRequestPartitionId; BaseResourcePidList( - Collection theIds, Date theLastDate, RequestPartitionId theRequestPartitionId) { + Collection theIds, + Date theLastDate, + RequestPartitionId theRequestPartitionId) { myIds.addAll(theIds); myLastDate = theLastDate; myRequestPartitionId = theRequestPartitionId; diff --git a/hapi-fhir-storage/src/main/java/ca/uhn/fhir/jpa/api/pid/EmptyResourcePidList.java b/hapi-fhir-storage/src/main/java/ca/uhn/fhir/jpa/api/pid/EmptyResourcePidList.java index 2881a0d0d78..f06f2c11a68 100644 --- a/hapi-fhir-storage/src/main/java/ca/uhn/fhir/jpa/api/pid/EmptyResourcePidList.java +++ b/hapi-fhir-storage/src/main/java/ca/uhn/fhir/jpa/api/pid/EmptyResourcePidList.java @@ -22,11 +22,11 @@ package ca.uhn.fhir.jpa.api.pid; import ca.uhn.fhir.i18n.Msg; import ca.uhn.fhir.interceptor.model.RequestPartitionId; import ca.uhn.fhir.rest.api.server.storage.IResourcePersistentId; +import jakarta.annotation.Nonnull; import java.util.Collections; import java.util.Date; import java.util.List; -import javax.annotation.Nonnull; /** * An empty resource pid list diff --git a/hapi-fhir-storage/src/main/java/ca/uhn/fhir/jpa/api/pid/HomogeneousResourcePidList.java b/hapi-fhir-storage/src/main/java/ca/uhn/fhir/jpa/api/pid/HomogeneousResourcePidList.java index 5e3a857c37e..bc5b06a2f8d 100644 --- a/hapi-fhir-storage/src/main/java/ca/uhn/fhir/jpa/api/pid/HomogeneousResourcePidList.java +++ b/hapi-fhir-storage/src/main/java/ca/uhn/fhir/jpa/api/pid/HomogeneousResourcePidList.java @@ -21,10 +21,10 @@ package ca.uhn.fhir.jpa.api.pid; import ca.uhn.fhir.interceptor.model.RequestPartitionId; import ca.uhn.fhir.rest.api.server.storage.IResourcePersistentId; +import jakarta.annotation.Nonnull; import java.util.Collection; import java.util.Date; -import javax.annotation.Nonnull; /** * A resource pid list where all pids have the same resource type diff --git a/hapi-fhir-storage/src/main/java/ca/uhn/fhir/jpa/api/pid/IResourcePidList.java b/hapi-fhir-storage/src/main/java/ca/uhn/fhir/jpa/api/pid/IResourcePidList.java index d57a4e3c20d..4583165c9af 100644 --- a/hapi-fhir-storage/src/main/java/ca/uhn/fhir/jpa/api/pid/IResourcePidList.java +++ b/hapi-fhir-storage/src/main/java/ca/uhn/fhir/jpa/api/pid/IResourcePidList.java @@ -21,10 +21,10 @@ package ca.uhn.fhir.jpa.api.pid; import ca.uhn.fhir.interceptor.model.RequestPartitionId; import ca.uhn.fhir.rest.api.server.storage.IResourcePersistentId; +import jakarta.annotation.Nonnull; import java.util.Date; import java.util.List; -import javax.annotation.Nonnull; /** * List of IResourcePersistentId along with a resource type each id diff --git a/hapi-fhir-storage/src/main/java/ca/uhn/fhir/jpa/api/pid/IResourcePidStream.java b/hapi-fhir-storage/src/main/java/ca/uhn/fhir/jpa/api/pid/IResourcePidStream.java new file mode 100644 index 00000000000..9cc2926c308 --- /dev/null +++ b/hapi-fhir-storage/src/main/java/ca/uhn/fhir/jpa/api/pid/IResourcePidStream.java @@ -0,0 +1,45 @@ +/*- + * #%L + * HAPI FHIR Storage api + * %% + * Copyright (C) 2014 - 2023 Smile CDR, Inc. + * %% + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * #L% + */ +package ca.uhn.fhir.jpa.api.pid; + +import ca.uhn.fhir.interceptor.model.RequestPartitionId; + +import java.util.function.Consumer; +import java.util.function.Function; +import java.util.stream.Stream; + +/** + * Wrapper for a query result stream. + */ +public interface IResourcePidStream { + T visitStream(Function, T> theCallback); + + default void visitStreamNoResult(Consumer> theCallback) { + visitStream(theStream -> { + theCallback.accept(theStream); + return null; + }); + } + + /** + * The partition info for the query. + */ + RequestPartitionId getRequestPartitionId(); +} diff --git a/hapi-fhir-storage/src/main/java/ca/uhn/fhir/jpa/api/pid/ListWrappingPidStream.java b/hapi-fhir-storage/src/main/java/ca/uhn/fhir/jpa/api/pid/ListWrappingPidStream.java new file mode 100644 index 00000000000..6bfa5bfdd51 --- /dev/null +++ b/hapi-fhir-storage/src/main/java/ca/uhn/fhir/jpa/api/pid/ListWrappingPidStream.java @@ -0,0 +1,47 @@ +/*- + * #%L + * HAPI FHIR Storage api + * %% + * Copyright (C) 2014 - 2023 Smile CDR, Inc. + * %% + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * #L% + */ +package ca.uhn.fhir.jpa.api.pid; + +import ca.uhn.fhir.interceptor.model.RequestPartitionId; + +import java.util.function.Function; +import java.util.stream.Stream; + +public class ListWrappingPidStream implements IResourcePidStream { + private final IResourcePidList myList; + + public ListWrappingPidStream(IResourcePidList theList) { + myList = theList; + } + + public Stream getTypedResourcePidStream() { + return myList.getTypedResourcePids().stream(); + } + + @Override + public T visitStream(Function, T> theCallback) { + return theCallback.apply(getTypedResourcePidStream()); + } + + @Override + public RequestPartitionId getRequestPartitionId() { + return myList.getRequestPartitionId(); + } +} diff --git a/hapi-fhir-storage/src/main/java/ca/uhn/fhir/jpa/api/pid/MixedResourcePidList.java b/hapi-fhir-storage/src/main/java/ca/uhn/fhir/jpa/api/pid/MixedResourcePidList.java index f14e52cad4c..f8397be79c7 100644 --- a/hapi-fhir-storage/src/main/java/ca/uhn/fhir/jpa/api/pid/MixedResourcePidList.java +++ b/hapi-fhir-storage/src/main/java/ca/uhn/fhir/jpa/api/pid/MixedResourcePidList.java @@ -21,11 +21,11 @@ package ca.uhn.fhir.jpa.api.pid; import ca.uhn.fhir.interceptor.model.RequestPartitionId; import ca.uhn.fhir.rest.api.server.storage.IResourcePersistentId; +import jakarta.annotation.Nonnull; import java.util.Collection; import java.util.Date; import java.util.List; -import javax.annotation.Nonnull; /** * A resource pid list where the pids can have different resource types @@ -36,7 +36,7 @@ public class MixedResourcePidList extends BaseResourcePidList { public MixedResourcePidList( List theResourceTypes, - Collection theIds, + Collection theIds, Date theLastDate, RequestPartitionId theRequestPartitionId) { super(theIds, theLastDate, theRequestPartitionId); diff --git a/hapi-fhir-storage/src/main/java/ca/uhn/fhir/jpa/api/pid/StreamTemplate.java b/hapi-fhir-storage/src/main/java/ca/uhn/fhir/jpa/api/pid/StreamTemplate.java new file mode 100644 index 00000000000..4f1f72c4240 --- /dev/null +++ b/hapi-fhir-storage/src/main/java/ca/uhn/fhir/jpa/api/pid/StreamTemplate.java @@ -0,0 +1,60 @@ +/*- + * #%L + * HAPI FHIR Storage api + * %% + * Copyright (C) 2014 - 2023 Smile CDR, Inc. + * %% + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * #L% + */ +package ca.uhn.fhir.jpa.api.pid; + +import jakarta.annotation.Nonnull; +import jakarta.annotation.Nullable; +import org.springframework.transaction.support.TransactionOperations; + +import java.util.function.Function; +import java.util.function.Supplier; +import java.util.stream.Stream; + +/** + * A template for stream queries, like JDBCTemplate and friends. + * + * We need to wrap access to the stream with a tx-span, a try-with-resources block, and RequestDetails. + * @param The stream content type + */ +public interface StreamTemplate { + @Nullable + R call(@Nonnull Function, R> theCallback); + + /** + * Wrap this template with a transaction boundary. + * Our dao Stream methods require an active Hibernate session for the duration of the Stream. + * This advice uses a tx boundary to ensure that active session. + * + * @param theTxBuilder the transaction and partition settings + * @return the wrapped template + */ + default StreamTemplate withTransactionAdvice(TransactionOperations theTxBuilder) { + return new TransactionWrappingStreamTemplate<>(theTxBuilder, this); + } + + /** + * Wrap the supplied stream as a StreamTemplate in a try-with-resources block to ensure it is closed. + * @param theStreamQuery the query to run + * @return a template that will always close the Stream on exit. + */ + static StreamTemplate fromSupplier(Supplier> theStreamQuery) { + return new AutoClosingStreamTemplate<>(theStreamQuery); + } +} diff --git a/hapi-fhir-storage/src/main/java/ca/uhn/fhir/jpa/api/pid/TransactionWrappingStreamTemplate.java b/hapi-fhir-storage/src/main/java/ca/uhn/fhir/jpa/api/pid/TransactionWrappingStreamTemplate.java new file mode 100644 index 00000000000..2b4d97cae6f --- /dev/null +++ b/hapi-fhir-storage/src/main/java/ca/uhn/fhir/jpa/api/pid/TransactionWrappingStreamTemplate.java @@ -0,0 +1,52 @@ +/*- + * #%L + * HAPI FHIR Storage api + * %% + * Copyright (C) 2014 - 2023 Smile CDR, Inc. + * %% + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * #L% + */ +package ca.uhn.fhir.jpa.api.pid; + +import jakarta.annotation.Nonnull; +import jakarta.annotation.Nullable; +import org.springframework.transaction.support.TransactionOperations; + +import java.util.function.Function; +import java.util.stream.Stream; + +/** + * Wrap a StreamTemplate with transaction advice. + * We can't cary open ResultSets past a transaction boundary. + * This wraps a Stream producer with tx advice so the connection is still open. + */ +class TransactionWrappingStreamTemplate implements StreamTemplate { + @Nonnull + final TransactionOperations myTransaction; + + @Nonnull + final StreamTemplate myWrappedStreamTemplate; + + TransactionWrappingStreamTemplate( + @Nonnull TransactionOperations theTransaction, @Nonnull StreamTemplate theWrappedStreamTemplate) { + myTransaction = theTransaction; + this.myWrappedStreamTemplate = theWrappedStreamTemplate; + } + + @Nullable + @Override + public R call(@Nonnull Function, R> theCallback) { + return myTransaction.execute(unusedTxStatus -> myWrappedStreamTemplate.call(theCallback)); + } +} diff --git a/hapi-fhir-storage/src/main/java/ca/uhn/fhir/jpa/api/pid/TypedResourceStream.java b/hapi-fhir-storage/src/main/java/ca/uhn/fhir/jpa/api/pid/TypedResourceStream.java new file mode 100644 index 00000000000..c0ad8f92912 --- /dev/null +++ b/hapi-fhir-storage/src/main/java/ca/uhn/fhir/jpa/api/pid/TypedResourceStream.java @@ -0,0 +1,47 @@ +/*- + * #%L + * HAPI FHIR Storage api + * %% + * Copyright (C) 2014 - 2023 Smile CDR, Inc. + * %% + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * #L% + */ +package ca.uhn.fhir.jpa.api.pid; + +import ca.uhn.fhir.interceptor.model.RequestPartitionId; + +import java.util.function.Function; +import java.util.stream.Stream; + +public class TypedResourceStream implements IResourcePidStream { + + private final RequestPartitionId myRequestPartitionId; + private final StreamTemplate myStreamSupplier; + + public TypedResourceStream( + RequestPartitionId theRequestPartitionId, StreamTemplate theStreamSupplier) { + myRequestPartitionId = theRequestPartitionId; + myStreamSupplier = theStreamSupplier; + } + + @Override + public T visitStream(Function, T> theCallback) { + return myStreamSupplier.call(theCallback); + } + + @Override + public RequestPartitionId getRequestPartitionId() { + return myRequestPartitionId; + } +} diff --git a/hapi-fhir-storage/src/main/java/ca/uhn/fhir/jpa/api/svc/IBatch2DaoSvc.java b/hapi-fhir-storage/src/main/java/ca/uhn/fhir/jpa/api/svc/IBatch2DaoSvc.java index d6759d53bde..96c125849d5 100644 --- a/hapi-fhir-storage/src/main/java/ca/uhn/fhir/jpa/api/svc/IBatch2DaoSvc.java +++ b/hapi-fhir-storage/src/main/java/ca/uhn/fhir/jpa/api/svc/IBatch2DaoSvc.java @@ -22,10 +22,12 @@ package ca.uhn.fhir.jpa.api.svc; import ca.uhn.fhir.i18n.Msg; import ca.uhn.fhir.interceptor.model.RequestPartitionId; import ca.uhn.fhir.jpa.api.pid.IResourcePidList; +import ca.uhn.fhir.jpa.api.pid.IResourcePidStream; +import ca.uhn.fhir.jpa.api.pid.ListWrappingPidStream; +import jakarta.annotation.Nonnull; +import jakarta.annotation.Nullable; import java.util.Date; -import javax.annotation.Nonnull; -import javax.annotation.Nullable; public interface IBatch2DaoSvc { @@ -68,4 +70,10 @@ public interface IBatch2DaoSvc { @Nullable String theUrl) { return fetchResourceIdsPage(theStart, theEnd, theRequestPartitionId, theUrl); } + + default IResourcePidStream fetchResourceIdStream( + Date theStart, Date theEnd, RequestPartitionId theTargetPartitionId, String theUrl) { + return new ListWrappingPidStream(fetchResourceIdsPage( + theStart, theEnd, 20000 /* ResourceIdListStep.DEFAULT_PAGE_SIZE */, theTargetPartitionId, theUrl)); + } } diff --git a/hapi-fhir-storage/src/main/java/ca/uhn/fhir/jpa/api/svc/IGoldenResourceSearchSvc.java b/hapi-fhir-storage/src/main/java/ca/uhn/fhir/jpa/api/svc/IGoldenResourceSearchSvc.java index 94548ae03d8..9085343eae6 100644 --- a/hapi-fhir-storage/src/main/java/ca/uhn/fhir/jpa/api/svc/IGoldenResourceSearchSvc.java +++ b/hapi-fhir-storage/src/main/java/ca/uhn/fhir/jpa/api/svc/IGoldenResourceSearchSvc.java @@ -20,27 +20,25 @@ package ca.uhn.fhir.jpa.api.svc; import ca.uhn.fhir.interceptor.model.RequestPartitionId; -import ca.uhn.fhir.jpa.api.pid.IResourcePidList; +import ca.uhn.fhir.jpa.api.pid.IResourcePidStream; +import jakarta.annotation.Nonnull; +import jakarta.annotation.Nullable; import java.util.Date; -import javax.annotation.Nonnull; -import javax.annotation.Nullable; public interface IGoldenResourceSearchSvc { /** - * Fetches a page of resource IDs for golden resources of the given type. The page size is up to the discretion of the implementation. + * Fetches a cursor of resource IDs for golden resources of the given type. * * @param theStart The start of the date range, must be inclusive. * @param theEnd The end of the date range, should be exclusive. - * @param thePageSize The number of golden resources to request at a time. * @param theRequestPartitionId The request partition ID (may be null on nonpartitioned systems) * @param theResourceType the type of resource. */ - IResourcePidList fetchGoldenResourceIdsPage( + IResourcePidStream fetchGoldenResourceIdStream( Date theStart, Date theEnd, - @Nonnull Integer thePageSize, @Nullable RequestPartitionId theRequestPartitionId, - @Nullable String theResourceType); + @Nonnull String theResourceType); } diff --git a/hapi-fhir-storage/src/main/java/ca/uhn/fhir/jpa/api/svc/IIdHelperService.java b/hapi-fhir-storage/src/main/java/ca/uhn/fhir/jpa/api/svc/IIdHelperService.java index 7d48096cb60..a0dc27012d0 100644 --- a/hapi-fhir-storage/src/main/java/ca/uhn/fhir/jpa/api/svc/IIdHelperService.java +++ b/hapi-fhir-storage/src/main/java/ca/uhn/fhir/jpa/api/svc/IIdHelperService.java @@ -26,6 +26,8 @@ import ca.uhn.fhir.jpa.api.model.PersistentIdToForcedIdMap; import ca.uhn.fhir.jpa.model.cross.IResourceLookup; import ca.uhn.fhir.rest.api.server.storage.IResourcePersistentId; import ca.uhn.fhir.rest.server.exceptions.ResourceNotFoundException; +import jakarta.annotation.Nonnull; +import jakarta.annotation.Nullable; import org.hl7.fhir.instance.model.api.IAnyResource; import org.hl7.fhir.instance.model.api.IBaseResource; import org.hl7.fhir.instance.model.api.IIdType; @@ -35,8 +37,6 @@ import java.util.List; import java.util.Map; import java.util.Optional; import java.util.Set; -import javax.annotation.Nonnull; -import javax.annotation.Nullable; /** * This interface is used to translate between {@link IResourcePersistentId} diff --git a/hapi-fhir-storage/src/main/java/ca/uhn/fhir/jpa/api/svc/ISearchCoordinatorSvc.java b/hapi-fhir-storage/src/main/java/ca/uhn/fhir/jpa/api/svc/ISearchCoordinatorSvc.java index a15b0876289..2a4c5c7a87c 100644 --- a/hapi-fhir-storage/src/main/java/ca/uhn/fhir/jpa/api/svc/ISearchCoordinatorSvc.java +++ b/hapi-fhir-storage/src/main/java/ca/uhn/fhir/jpa/api/svc/ISearchCoordinatorSvc.java @@ -26,10 +26,10 @@ import ca.uhn.fhir.rest.api.CacheControlDirective; import ca.uhn.fhir.rest.api.server.IBundleProvider; import ca.uhn.fhir.rest.api.server.RequestDetails; import ca.uhn.fhir.rest.api.server.storage.IResourcePersistentId; +import jakarta.annotation.Nullable; import java.util.List; import java.util.Optional; -import javax.annotation.Nullable; public interface ISearchCoordinatorSvc { diff --git a/hapi-fhir-storage/src/main/java/ca/uhn/fhir/jpa/batch/models/Batch2BaseJobParameters.java b/hapi-fhir-storage/src/main/java/ca/uhn/fhir/jpa/batch/models/Batch2BaseJobParameters.java index 686823ef83a..c419eaaaf2d 100644 --- a/hapi-fhir-storage/src/main/java/ca/uhn/fhir/jpa/batch/models/Batch2BaseJobParameters.java +++ b/hapi-fhir-storage/src/main/java/ca/uhn/fhir/jpa/batch/models/Batch2BaseJobParameters.java @@ -19,7 +19,7 @@ */ package ca.uhn.fhir.jpa.batch.models; -import javax.annotation.Nonnull; +import jakarta.annotation.Nonnull; /** * Base parameters for StartJob as well as other requests diff --git a/hapi-fhir-storage/src/main/java/ca/uhn/fhir/jpa/binary/api/IBinaryStorageSvc.java b/hapi-fhir-storage/src/main/java/ca/uhn/fhir/jpa/binary/api/IBinaryStorageSvc.java index 399f2bc588e..85833cbc92e 100644 --- a/hapi-fhir-storage/src/main/java/ca/uhn/fhir/jpa/binary/api/IBinaryStorageSvc.java +++ b/hapi-fhir-storage/src/main/java/ca/uhn/fhir/jpa/binary/api/IBinaryStorageSvc.java @@ -21,13 +21,13 @@ package ca.uhn.fhir.jpa.binary.api; import ca.uhn.fhir.rest.api.server.RequestDetails; import ca.uhn.fhir.rest.server.servlet.ServletRequestDetails; +import jakarta.annotation.Nonnull; import org.hl7.fhir.instance.model.api.IBaseBinary; import org.hl7.fhir.instance.model.api.IIdType; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; -import javax.annotation.Nonnull; public interface IBinaryStorageSvc { diff --git a/hapi-fhir-storage/src/main/java/ca/uhn/fhir/jpa/binary/api/StoredDetails.java b/hapi-fhir-storage/src/main/java/ca/uhn/fhir/jpa/binary/api/StoredDetails.java index bbca43085e0..489d0d93a68 100644 --- a/hapi-fhir-storage/src/main/java/ca/uhn/fhir/jpa/binary/api/StoredDetails.java +++ b/hapi-fhir-storage/src/main/java/ca/uhn/fhir/jpa/binary/api/StoredDetails.java @@ -26,10 +26,10 @@ import com.fasterxml.jackson.annotation.JsonProperty; import com.fasterxml.jackson.databind.annotation.JsonDeserialize; import com.fasterxml.jackson.databind.annotation.JsonSerialize; import com.google.common.hash.HashingInputStream; +import jakarta.annotation.Nonnull; import org.apache.commons.lang3.builder.ToStringBuilder; import java.util.Date; -import javax.annotation.Nonnull; public class StoredDetails implements IModelJson { diff --git a/hapi-fhir-storage/src/main/java/ca/uhn/fhir/jpa/binary/interceptor/BinaryStorageInterceptor.java b/hapi-fhir-storage/src/main/java/ca/uhn/fhir/jpa/binary/interceptor/BinaryStorageInterceptor.java index 0ff58ec7cf3..be86acd3c83 100644 --- a/hapi-fhir-storage/src/main/java/ca/uhn/fhir/jpa/binary/interceptor/BinaryStorageInterceptor.java +++ b/hapi-fhir-storage/src/main/java/ca/uhn/fhir/jpa/binary/interceptor/BinaryStorageInterceptor.java @@ -43,6 +43,7 @@ import ca.uhn.fhir.rest.server.servlet.ServletRequestDetails; import ca.uhn.fhir.rest.server.util.CompositeInterceptorBroadcaster; import ca.uhn.fhir.util.HapiExtensions; import ca.uhn.fhir.util.IModelVisitor2; +import jakarta.annotation.Nonnull; import org.apache.commons.io.FileUtils; import org.apache.commons.lang3.StringUtils; import org.hl7.fhir.instance.model.api.IBase; @@ -66,7 +67,6 @@ import java.util.Optional; import java.util.Set; import java.util.concurrent.atomic.AtomicInteger; import java.util.stream.Collectors; -import javax.annotation.Nonnull; import static ca.uhn.fhir.util.HapiExtensions.EXT_EXTERNALIZED_BINARY_ID; import static org.apache.commons.lang3.StringUtils.isNotBlank; @@ -251,7 +251,7 @@ public class BinaryStorageInterceptor> { } if (myBinaryStorageSvc.isValidBlobId(newBlobId)) { List deferredBinaryTargets = - getOrCreateDeferredBinaryStorageMap(theTransactionDetails); + getOrCreateDeferredBinaryStorageList(theResource); DeferredBinaryTarget newDeferredBinaryTarget = new DeferredBinaryTarget(newBlobId, nextTarget, data); deferredBinaryTargets.add(newDeferredBinaryTarget); @@ -289,21 +289,29 @@ public class BinaryStorageInterceptor> { } @Nonnull - private List getOrCreateDeferredBinaryStorageMap(TransactionDetails theTransactionDetails) { - return theTransactionDetails.getOrCreateUserData(getDeferredListKey(), ArrayList::new); + @SuppressWarnings("unchecked") + private List getOrCreateDeferredBinaryStorageList(IBaseResource theResource) { + Object deferredBinaryTargetList = theResource.getUserData(getDeferredListKey()); + if (deferredBinaryTargetList == null) { + deferredBinaryTargetList = new ArrayList<>(); + theResource.setUserData(getDeferredListKey(), deferredBinaryTargetList); + } + return (List) deferredBinaryTargetList; } + @SuppressWarnings("unchecked") @Hook(Pointcut.STORAGE_PRECOMMIT_RESOURCE_CREATED) public void storeLargeBinariesBeforeCreatePersistence( - TransactionDetails theTransactionDetails, IBaseResource theResource, Pointcut thePoincut) + TransactionDetails theTransactionDetails, IBaseResource theResource, Pointcut thePointcut) throws IOException { - if (theTransactionDetails == null) { + if (theResource == null) { return; } - List deferredBinaryTargets = theTransactionDetails.getUserData(getDeferredListKey()); - if (deferredBinaryTargets != null) { + Object deferredBinaryTargetList = theResource.getUserData(getDeferredListKey()); + + if (deferredBinaryTargetList != null) { IIdType resourceId = theResource.getIdElement(); - for (DeferredBinaryTarget next : deferredBinaryTargets) { + for (DeferredBinaryTarget next : (List) deferredBinaryTargetList) { String blobId = next.getBlobId(); IBinaryTarget target = next.getBinaryTarget(); InputStream dataStream = next.getDataStream(); diff --git a/hapi-fhir-storage/src/main/java/ca/uhn/fhir/jpa/binary/provider/BinaryAccessProvider.java b/hapi-fhir-storage/src/main/java/ca/uhn/fhir/jpa/binary/provider/BinaryAccessProvider.java index 52cce62c41a..d189484a72a 100644 --- a/hapi-fhir-storage/src/main/java/ca/uhn/fhir/jpa/binary/provider/BinaryAccessProvider.java +++ b/hapi-fhir-storage/src/main/java/ca/uhn/fhir/jpa/binary/provider/BinaryAccessProvider.java @@ -42,6 +42,9 @@ import ca.uhn.fhir.util.BinaryUtil; import ca.uhn.fhir.util.DateUtils; import ca.uhn.fhir.util.HapiExtensions; import com.google.common.annotations.VisibleForTesting; +import jakarta.annotation.Nonnull; +import jakarta.servlet.http.HttpServletRequest; +import jakarta.servlet.http.HttpServletResponse; import org.apache.commons.lang3.StringUtils; import org.apache.commons.lang3.Validate; import org.hl7.fhir.instance.model.api.IBase; @@ -59,9 +62,6 @@ import org.springframework.beans.factory.annotation.Autowired; import java.io.ByteArrayInputStream; import java.io.IOException; import java.util.Optional; -import javax.annotation.Nonnull; -import javax.servlet.http.HttpServletRequest; -import javax.servlet.http.HttpServletResponse; import static ca.uhn.fhir.util.UrlUtil.sanitizeUrlPart; import static org.apache.commons.lang3.StringUtils.isBlank; diff --git a/hapi-fhir-storage/src/main/java/ca/uhn/fhir/jpa/binary/svc/BaseBinaryStorageSvcImpl.java b/hapi-fhir-storage/src/main/java/ca/uhn/fhir/jpa/binary/svc/BaseBinaryStorageSvcImpl.java index 4d1cb164ee2..72e44bb7959 100644 --- a/hapi-fhir-storage/src/main/java/ca/uhn/fhir/jpa/binary/svc/BaseBinaryStorageSvcImpl.java +++ b/hapi-fhir-storage/src/main/java/ca/uhn/fhir/jpa/binary/svc/BaseBinaryStorageSvcImpl.java @@ -37,6 +37,8 @@ import com.google.common.hash.HashFunction; import com.google.common.hash.Hashing; import com.google.common.hash.HashingInputStream; import com.google.common.io.ByteStreams; +import jakarta.annotation.Nonnull; +import jakarta.annotation.Nullable; import org.apache.commons.io.input.CountingInputStream; import org.apache.commons.lang3.StringUtils; import org.apache.commons.lang3.Validate; @@ -50,8 +52,6 @@ import org.springframework.beans.factory.annotation.Autowired; import java.io.IOException; import java.io.InputStream; import java.util.Optional; -import javax.annotation.Nonnull; -import javax.annotation.Nullable; import static org.apache.commons.lang3.StringUtils.isNotBlank; diff --git a/hapi-fhir-storage/src/main/java/ca/uhn/fhir/jpa/binary/svc/NullBinaryStorageSvcImpl.java b/hapi-fhir-storage/src/main/java/ca/uhn/fhir/jpa/binary/svc/NullBinaryStorageSvcImpl.java index 3509be83924..0334c467815 100644 --- a/hapi-fhir-storage/src/main/java/ca/uhn/fhir/jpa/binary/svc/NullBinaryStorageSvcImpl.java +++ b/hapi-fhir-storage/src/main/java/ca/uhn/fhir/jpa/binary/svc/NullBinaryStorageSvcImpl.java @@ -23,12 +23,12 @@ import ca.uhn.fhir.i18n.Msg; import ca.uhn.fhir.jpa.binary.api.IBinaryStorageSvc; import ca.uhn.fhir.jpa.binary.api.StoredDetails; import ca.uhn.fhir.rest.api.server.RequestDetails; +import jakarta.annotation.Nonnull; import org.hl7.fhir.instance.model.api.IBaseBinary; import org.hl7.fhir.instance.model.api.IIdType; import java.io.InputStream; import java.io.OutputStream; -import javax.annotation.Nonnull; public class NullBinaryStorageSvcImpl implements IBinaryStorageSvc { diff --git a/hapi-fhir-storage/src/main/java/ca/uhn/fhir/jpa/binstore/FilesystemBinaryStorageSvcImpl.java b/hapi-fhir-storage/src/main/java/ca/uhn/fhir/jpa/binstore/FilesystemBinaryStorageSvcImpl.java index 3d1ed467240..0fef2b6735c 100644 --- a/hapi-fhir-storage/src/main/java/ca/uhn/fhir/jpa/binstore/FilesystemBinaryStorageSvcImpl.java +++ b/hapi-fhir-storage/src/main/java/ca/uhn/fhir/jpa/binstore/FilesystemBinaryStorageSvcImpl.java @@ -30,6 +30,8 @@ import com.fasterxml.jackson.databind.ObjectMapper; import com.fasterxml.jackson.databind.SerializationFeature; import com.google.common.base.Charsets; import com.google.common.hash.HashingInputStream; +import jakarta.annotation.Nonnull; +import jakarta.annotation.Nullable; import org.apache.commons.io.FileUtils; import org.apache.commons.io.IOUtils; import org.apache.commons.io.input.CountingInputStream; @@ -50,8 +52,6 @@ import java.io.InputStreamReader; import java.io.OutputStream; import java.io.Reader; import java.util.Date; -import javax.annotation.Nonnull; -import javax.annotation.Nullable; public class FilesystemBinaryStorageSvcImpl extends BaseBinaryStorageSvcImpl { diff --git a/hapi-fhir-storage/src/main/java/ca/uhn/fhir/jpa/binstore/MemoryBinaryStorageSvcImpl.java b/hapi-fhir-storage/src/main/java/ca/uhn/fhir/jpa/binstore/MemoryBinaryStorageSvcImpl.java index a5f6dd3d4af..e742db5221b 100644 --- a/hapi-fhir-storage/src/main/java/ca/uhn/fhir/jpa/binstore/MemoryBinaryStorageSvcImpl.java +++ b/hapi-fhir-storage/src/main/java/ca/uhn/fhir/jpa/binstore/MemoryBinaryStorageSvcImpl.java @@ -24,6 +24,7 @@ import ca.uhn.fhir.jpa.binary.api.StoredDetails; import ca.uhn.fhir.jpa.binary.svc.BaseBinaryStorageSvcImpl; import ca.uhn.fhir.rest.api.server.RequestDetails; import com.google.common.hash.HashingInputStream; +import jakarta.annotation.Nonnull; import org.apache.commons.io.IOUtils; import org.apache.commons.io.input.CountingInputStream; import org.hl7.fhir.instance.model.api.IIdType; @@ -33,7 +34,6 @@ import java.io.InputStream; import java.io.OutputStream; import java.util.Date; import java.util.concurrent.ConcurrentHashMap; -import javax.annotation.Nonnull; /** * Purely in-memory implementation of binary storage service. This is really diff --git a/hapi-fhir-storage/src/main/java/ca/uhn/fhir/jpa/bulk/imprt/api/IBulkDataImportSvc.java b/hapi-fhir-storage/src/main/java/ca/uhn/fhir/jpa/bulk/imprt/api/IBulkDataImportSvc.java index d7f2d6e14b8..aa7969a4505 100644 --- a/hapi-fhir-storage/src/main/java/ca/uhn/fhir/jpa/bulk/imprt/api/IBulkDataImportSvc.java +++ b/hapi-fhir-storage/src/main/java/ca/uhn/fhir/jpa/bulk/imprt/api/IBulkDataImportSvc.java @@ -23,10 +23,10 @@ import ca.uhn.fhir.jpa.bulk.imprt.model.ActivateJobResult; import ca.uhn.fhir.jpa.bulk.imprt.model.BulkImportJobFileJson; import ca.uhn.fhir.jpa.bulk.imprt.model.BulkImportJobJson; import ca.uhn.fhir.jpa.bulk.imprt.model.BulkImportJobStatusEnum; +import jakarta.annotation.Nonnull; import java.util.Date; import java.util.List; -import javax.annotation.Nonnull; public interface IBulkDataImportSvc { diff --git a/hapi-fhir-storage/src/main/java/ca/uhn/fhir/jpa/bulk/imprt/model/ActivateJobResult.java b/hapi-fhir-storage/src/main/java/ca/uhn/fhir/jpa/bulk/imprt/model/ActivateJobResult.java index da558d26693..171c222215e 100644 --- a/hapi-fhir-storage/src/main/java/ca/uhn/fhir/jpa/bulk/imprt/model/ActivateJobResult.java +++ b/hapi-fhir-storage/src/main/java/ca/uhn/fhir/jpa/bulk/imprt/model/ActivateJobResult.java @@ -19,7 +19,7 @@ */ package ca.uhn.fhir.jpa.bulk.imprt.model; -import javax.annotation.Nullable; +import jakarta.annotation.Nullable; public class ActivateJobResult { diff --git a/hapi-fhir-storage/src/main/java/ca/uhn/fhir/jpa/dao/BaseStorageDao.java b/hapi-fhir-storage/src/main/java/ca/uhn/fhir/jpa/dao/BaseStorageDao.java index 4c3e398bff4..6642f2e339e 100644 --- a/hapi-fhir-storage/src/main/java/ca/uhn/fhir/jpa/dao/BaseStorageDao.java +++ b/hapi-fhir-storage/src/main/java/ca/uhn/fhir/jpa/dao/BaseStorageDao.java @@ -65,6 +65,8 @@ import ca.uhn.fhir.util.ResourceReferenceInfo; import ca.uhn.fhir.util.StopWatch; import ca.uhn.fhir.util.UrlUtil; import com.google.common.annotations.VisibleForTesting; +import jakarta.annotation.Nonnull; +import jakarta.annotation.Nullable; import org.hl7.fhir.instance.model.api.IBaseBundle; import org.hl7.fhir.instance.model.api.IBaseOperationOutcome; import org.hl7.fhir.instance.model.api.IBaseReference; @@ -84,8 +86,6 @@ import java.util.List; import java.util.Map; import java.util.Set; import java.util.function.Supplier; -import javax.annotation.Nonnull; -import javax.annotation.Nullable; import static org.apache.commons.lang3.StringUtils.defaultString; import static org.apache.commons.lang3.StringUtils.isNotBlank; diff --git a/hapi-fhir-storage/src/main/java/ca/uhn/fhir/jpa/dao/BaseStorageResourceDao.java b/hapi-fhir-storage/src/main/java/ca/uhn/fhir/jpa/dao/BaseStorageResourceDao.java index 46d2c2fb925..e782e1c9789 100644 --- a/hapi-fhir-storage/src/main/java/ca/uhn/fhir/jpa/dao/BaseStorageResourceDao.java +++ b/hapi-fhir-storage/src/main/java/ca/uhn/fhir/jpa/dao/BaseStorageResourceDao.java @@ -44,6 +44,7 @@ import ca.uhn.fhir.rest.server.exceptions.PreconditionFailedException; import ca.uhn.fhir.rest.server.exceptions.ResourceNotFoundException; import ca.uhn.fhir.rest.server.exceptions.ResourceVersionConflictException; import ca.uhn.fhir.rest.server.exceptions.UnprocessableEntityException; +import jakarta.annotation.Nonnull; import org.hl7.fhir.instance.model.api.IBaseParameters; import org.hl7.fhir.instance.model.api.IBaseResource; import org.hl7.fhir.instance.model.api.IIdType; @@ -53,7 +54,6 @@ import org.springframework.transaction.support.TransactionSynchronizationManager import java.util.Collections; import java.util.List; import java.util.Set; -import javax.annotation.Nonnull; import static org.apache.commons.lang3.StringUtils.isNotBlank; diff --git a/hapi-fhir-storage/src/main/java/ca/uhn/fhir/jpa/dao/BaseTransactionProcessor.java b/hapi-fhir-storage/src/main/java/ca/uhn/fhir/jpa/dao/BaseTransactionProcessor.java index 3604cd2cd8d..1db5396a8ac 100644 --- a/hapi-fhir-storage/src/main/java/ca/uhn/fhir/jpa/dao/BaseTransactionProcessor.java +++ b/hapi-fhir-storage/src/main/java/ca/uhn/fhir/jpa/dao/BaseTransactionProcessor.java @@ -84,6 +84,7 @@ import ca.uhn.fhir.util.UrlUtil; import com.google.common.annotations.VisibleForTesting; import com.google.common.collect.ArrayListMultimap; import com.google.common.collect.ListMultimap; +import jakarta.annotation.Nonnull; import org.apache.commons.lang3.StringUtils; import org.apache.commons.lang3.Validate; import org.hl7.fhir.dstu3.model.Bundle; @@ -124,7 +125,6 @@ import java.util.concurrent.CountDownLatch; import java.util.concurrent.TimeUnit; import java.util.regex.Pattern; import java.util.stream.Collectors; -import javax.annotation.Nonnull; import static ca.uhn.fhir.util.StringUtil.toUtf8String; import static java.util.Objects.isNull; diff --git a/hapi-fhir-storage/src/main/java/ca/uhn/fhir/jpa/dao/ISearchBuilder.java b/hapi-fhir-storage/src/main/java/ca/uhn/fhir/jpa/dao/ISearchBuilder.java index 42b1a668c2d..0d1b741ba25 100644 --- a/hapi-fhir-storage/src/main/java/ca/uhn/fhir/jpa/dao/ISearchBuilder.java +++ b/hapi-fhir-storage/src/main/java/ca/uhn/fhir/jpa/dao/ISearchBuilder.java @@ -28,15 +28,15 @@ import ca.uhn.fhir.model.api.Include; import ca.uhn.fhir.rest.api.server.RequestDetails; import ca.uhn.fhir.rest.api.server.storage.IResourcePersistentId; import ca.uhn.fhir.rest.param.DateRangeParam; +import jakarta.annotation.Nonnull; +import jakarta.persistence.EntityManager; import org.hl7.fhir.instance.model.api.IBaseResource; import java.util.Collection; import java.util.List; import java.util.Set; -import javax.annotation.Nonnull; -import javax.persistence.EntityManager; -public interface ISearchBuilder { +public interface ISearchBuilder> { String SEARCH_BUILDER_BEAN_NAME = "SearchBuilder"; IResultIterator createQuery( diff --git a/hapi-fhir-storage/src/main/java/ca/uhn/fhir/jpa/dao/MatchResourceUrlService.java b/hapi-fhir-storage/src/main/java/ca/uhn/fhir/jpa/dao/MatchResourceUrlService.java index c04eadb174c..91c711bc7d6 100644 --- a/hapi-fhir-storage/src/main/java/ca/uhn/fhir/jpa/dao/MatchResourceUrlService.java +++ b/hapi-fhir-storage/src/main/java/ca/uhn/fhir/jpa/dao/MatchResourceUrlService.java @@ -43,6 +43,7 @@ import ca.uhn.fhir.rest.server.exceptions.InvalidRequestException; import ca.uhn.fhir.rest.server.servlet.ServletRequestDetails; import ca.uhn.fhir.rest.server.util.CompositeInterceptorBroadcaster; import ca.uhn.fhir.util.StopWatch; +import jakarta.annotation.Nullable; import org.apache.commons.lang3.Validate; import org.hl7.fhir.instance.model.api.IBaseResource; import org.springframework.beans.factory.annotation.Autowired; @@ -56,7 +57,6 @@ import java.util.Map; import java.util.Objects; import java.util.Set; import java.util.stream.Collectors; -import javax.annotation.Nullable; @Service public class MatchResourceUrlService { diff --git a/hapi-fhir-storage/src/main/java/ca/uhn/fhir/jpa/dao/expunge/ExpungeOperation.java b/hapi-fhir-storage/src/main/java/ca/uhn/fhir/jpa/dao/expunge/ExpungeOperation.java index 0a8ceb54240..9af4034046f 100644 --- a/hapi-fhir-storage/src/main/java/ca/uhn/fhir/jpa/dao/expunge/ExpungeOperation.java +++ b/hapi-fhir-storage/src/main/java/ca/uhn/fhir/jpa/dao/expunge/ExpungeOperation.java @@ -25,6 +25,7 @@ import ca.uhn.fhir.jpa.api.model.ExpungeOutcome; import ca.uhn.fhir.jpa.dao.tx.HapiTransactionService; import ca.uhn.fhir.rest.api.server.RequestDetails; import ca.uhn.fhir.rest.api.server.storage.IResourcePersistentId; +import com.google.common.annotations.VisibleForTesting; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; @@ -43,7 +44,7 @@ public class ExpungeOperation implements Callable { public static final String THREAD_PREFIX = "expunge"; @Autowired - private IResourceExpungeService myExpungeDaoService; + private IResourceExpungeService myResourceExpungeService; @Autowired private JpaStorageSettings myStorageSettings; @@ -101,17 +102,14 @@ public class ExpungeOperation implements Callable { } private List findHistoricalVersionsOfDeletedResources() { - List retVal = myExpungeDaoService.findHistoricalVersionsOfDeletedResources( - myResourceName, myResourceId, myRemainingCount.get()); + List retVal = getPartitionAwareSupplier() + .supplyInPartitionedContext(() -> myResourceExpungeService.findHistoricalVersionsOfDeletedResources( + myResourceName, myResourceId, myRemainingCount.get())); + ourLog.debug("Found {} historical versions", retVal.size()); return retVal; } - private List findHistoricalVersionsOfNonDeletedResources() { - return myExpungeDaoService.findHistoricalVersionsOfNonDeletedResources( - myResourceName, myResourceId, myRemainingCount.get()); - } - private boolean expungeLimitReached() { boolean expungeLimitReached = myRemainingCount.get() <= 0; if (expungeLimitReached) { @@ -121,15 +119,21 @@ public class ExpungeOperation implements Callable { } private void expungeOldVersions() { - List historicalIds = findHistoricalVersionsOfNonDeletedResources(); + List historicalIds = getPartitionAwareSupplier() + .supplyInPartitionedContext(() -> myResourceExpungeService.findHistoricalVersionsOfNonDeletedResources( + myResourceName, myResourceId, myRemainingCount.get())); getPartitionRunner() .runInPartitionedThreads( historicalIds, - partition -> myExpungeDaoService.expungeHistoricalVersions( + partition -> myResourceExpungeService.expungeHistoricalVersions( myRequestDetails, partition, myRemainingCount)); } + private PartitionAwareSupplier getPartitionAwareSupplier() { + return new PartitionAwareSupplier(myTxService, myRequestDetails); + } + private PartitionRunner getPartitionRunner() { return new PartitionRunner( PROCESS_NAME, @@ -144,7 +148,7 @@ public class ExpungeOperation implements Callable { getPartitionRunner() .runInPartitionedThreads( theResourceIds, - partition -> myExpungeDaoService.expungeCurrentVersionOfResources( + partition -> myResourceExpungeService.expungeCurrentVersionOfResources( myRequestDetails, partition, myRemainingCount)); } @@ -152,11 +156,26 @@ public class ExpungeOperation implements Callable { getPartitionRunner() .runInPartitionedThreads( theResourceIds, - partition -> myExpungeDaoService.expungeHistoricalVersionsOfIds( + partition -> myResourceExpungeService.expungeHistoricalVersionsOfIds( myRequestDetails, partition, myRemainingCount)); } private ExpungeOutcome expungeOutcome() { return new ExpungeOutcome().setDeletedCount(myExpungeOptions.getLimit() - myRemainingCount.get()); } + + @VisibleForTesting + public void setHapiTransactionServiceForTesting(HapiTransactionService theHapiTransactionService) { + myTxService = theHapiTransactionService; + } + + @VisibleForTesting + public void setStorageSettingsForTesting(JpaStorageSettings theStorageSettings) { + myStorageSettings = theStorageSettings; + } + + @VisibleForTesting + public void setExpungeDaoServiceForTesting(IResourceExpungeService theIResourceExpungeService) { + myResourceExpungeService = theIResourceExpungeService; + } } diff --git a/hapi-fhir-storage/src/main/java/ca/uhn/fhir/jpa/dao/expunge/IExpungeEverythingService.java b/hapi-fhir-storage/src/main/java/ca/uhn/fhir/jpa/dao/expunge/IExpungeEverythingService.java index c2e75e14c03..cbab0273c79 100644 --- a/hapi-fhir-storage/src/main/java/ca/uhn/fhir/jpa/dao/expunge/IExpungeEverythingService.java +++ b/hapi-fhir-storage/src/main/java/ca/uhn/fhir/jpa/dao/expunge/IExpungeEverythingService.java @@ -20,8 +20,7 @@ package ca.uhn.fhir.jpa.dao.expunge; import ca.uhn.fhir.rest.api.server.RequestDetails; - -import javax.annotation.Nullable; +import jakarta.annotation.Nullable; public interface IExpungeEverythingService { void expungeEverything(@Nullable RequestDetails theRequest); diff --git a/hapi-fhir-storage/src/main/java/ca/uhn/fhir/jpa/dao/expunge/PartitionAwareSupplier.java b/hapi-fhir-storage/src/main/java/ca/uhn/fhir/jpa/dao/expunge/PartitionAwareSupplier.java new file mode 100644 index 00000000000..933ae17e118 --- /dev/null +++ b/hapi-fhir-storage/src/main/java/ca/uhn/fhir/jpa/dao/expunge/PartitionAwareSupplier.java @@ -0,0 +1,46 @@ +/*- + * #%L + * HAPI FHIR Storage api + * %% + * Copyright (C) 2014 - 2023 Smile CDR, Inc. + * %% + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * #L% + */ +package ca.uhn.fhir.jpa.dao.expunge; + +import ca.uhn.fhir.jpa.dao.tx.HapiTransactionService; +import ca.uhn.fhir.rest.api.server.RequestDetails; +import jakarta.annotation.Nonnull; + +import java.util.function.Supplier; + +/** + * Utility class wrapping a supplier in a transaction with the purpose of performing the supply operation with a + * partitioned aware context. + */ +public class PartitionAwareSupplier { + private final HapiTransactionService myTransactionService; + private final RequestDetails myRequestDetails; + + @Nonnull + public PartitionAwareSupplier(HapiTransactionService theTxService, RequestDetails theRequestDetails) { + myTransactionService = theTxService; + myRequestDetails = theRequestDetails; + } + + @Nonnull + public T supplyInPartitionedContext(Supplier theResourcePersistentIdSupplier) { + return myTransactionService.withRequest(myRequestDetails).execute(tx -> theResourcePersistentIdSupplier.get()); + } +} diff --git a/hapi-fhir-storage/src/main/java/ca/uhn/fhir/jpa/dao/expunge/PartitionRunner.java b/hapi-fhir-storage/src/main/java/ca/uhn/fhir/jpa/dao/expunge/PartitionRunner.java index f729babddd3..90dc98c1f0e 100644 --- a/hapi-fhir-storage/src/main/java/ca/uhn/fhir/jpa/dao/expunge/PartitionRunner.java +++ b/hapi-fhir-storage/src/main/java/ca/uhn/fhir/jpa/dao/expunge/PartitionRunner.java @@ -27,6 +27,7 @@ import ca.uhn.fhir.rest.server.exceptions.InternalErrorException; import ca.uhn.fhir.rest.server.exceptions.PreconditionFailedException; import ca.uhn.fhir.util.StopWatch; import com.google.common.collect.Lists; +import jakarta.annotation.Nullable; import org.apache.commons.lang3.concurrent.BasicThreadFactory; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -44,7 +45,6 @@ import java.util.concurrent.ThreadPoolExecutor; import java.util.concurrent.TimeUnit; import java.util.function.Consumer; import java.util.stream.Collectors; -import javax.annotation.Nullable; public class PartitionRunner { private static final Logger ourLog = LoggerFactory.getLogger(PartitionRunner.class); diff --git a/hapi-fhir-storage/src/main/java/ca/uhn/fhir/jpa/dao/index/DaoResourceLinkResolver.java b/hapi-fhir-storage/src/main/java/ca/uhn/fhir/jpa/dao/index/DaoResourceLinkResolver.java index a2df3b9a415..302c3a1f20c 100644 --- a/hapi-fhir-storage/src/main/java/ca/uhn/fhir/jpa/dao/index/DaoResourceLinkResolver.java +++ b/hapi-fhir-storage/src/main/java/ca/uhn/fhir/jpa/dao/index/DaoResourceLinkResolver.java @@ -46,6 +46,8 @@ import ca.uhn.fhir.rest.server.util.ISearchParamRegistry; import ca.uhn.fhir.util.CanonicalIdentifier; import ca.uhn.fhir.util.HapiExtensions; import ca.uhn.fhir.util.TerserUtil; +import jakarta.annotation.Nonnull; +import jakarta.annotation.Nullable; import org.apache.http.NameValuePair; import org.apache.http.client.utils.URLEncodedUtils; import org.hl7.fhir.instance.model.api.IBase; @@ -61,8 +63,6 @@ import java.nio.charset.StandardCharsets; import java.util.Date; import java.util.List; import java.util.Optional; -import javax.annotation.Nonnull; -import javax.annotation.Nullable; public class DaoResourceLinkResolver implements IResourceLinkResolver { private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(DaoResourceLinkResolver.class); diff --git a/hapi-fhir-storage/src/main/java/ca/uhn/fhir/jpa/dao/tx/HapiTransactionService.java b/hapi-fhir-storage/src/main/java/ca/uhn/fhir/jpa/dao/tx/HapiTransactionService.java index a54a370a982..990ef103309 100644 --- a/hapi-fhir-storage/src/main/java/ca/uhn/fhir/jpa/dao/tx/HapiTransactionService.java +++ b/hapi-fhir-storage/src/main/java/ca/uhn/fhir/jpa/dao/tx/HapiTransactionService.java @@ -38,6 +38,8 @@ import ca.uhn.fhir.rest.server.util.CompositeInterceptorBroadcaster; import ca.uhn.fhir.util.ICallable; import ca.uhn.fhir.util.TestUtil; import com.google.common.annotations.VisibleForTesting; +import jakarta.annotation.Nonnull; +import jakarta.annotation.Nullable; import org.apache.commons.lang3.Validate; import org.apache.commons.lang3.exception.ExceptionUtils; import org.hibernate.exception.ConstraintViolationException; @@ -53,13 +55,12 @@ import org.springframework.transaction.annotation.Isolation; import org.springframework.transaction.annotation.Propagation; import org.springframework.transaction.support.TransactionCallback; import org.springframework.transaction.support.TransactionCallbackWithoutResult; +import org.springframework.transaction.support.TransactionOperations; import org.springframework.transaction.support.TransactionSynchronizationManager; import org.springframework.transaction.support.TransactionTemplate; import java.util.Objects; import java.util.concurrent.Callable; -import javax.annotation.Nonnull; -import javax.annotation.Nullable; /** * @see IHapiTransactionService for an explanation of this class @@ -72,6 +73,7 @@ public class HapiTransactionService implements IHapiTransactionService { HapiTransactionService.class.getName() + "_EXISTING_SEARCH_PARAMS"; private static final Logger ourLog = LoggerFactory.getLogger(HapiTransactionService.class); private static final ThreadLocal ourRequestPartitionThreadLocal = new ThreadLocal<>(); + private static final ThreadLocal ourExistingTransaction = new ThreadLocal<>(); @Autowired protected IInterceptorBroadcaster myInterceptorBroadcaster; @@ -219,6 +221,11 @@ public class HapiTransactionService implements IHapiTransactionService { myTransactionManager = theTransactionManager; } + @VisibleForTesting + public void setPartitionSettingsForUnitTest(PartitionSettings thePartitionSettings) { + myPartitionSettings = thePartitionSettings; + } + @Nullable protected T doExecute(ExecutionBuilder theExecutionBuilder, TransactionCallback theCallback) { final RequestPartitionId requestPartitionId; @@ -236,8 +243,9 @@ public class HapiTransactionService implements IHapiTransactionService { ourRequestPartitionThreadLocal.set(requestPartitionId); } - if (Objects.equals(previousRequestPartitionId, requestPartitionId)) { - if (canReuseExistingTransaction(theExecutionBuilder)) { + if (!myPartitionSettings.isPartitioningEnabled() + || Objects.equals(previousRequestPartitionId, requestPartitionId)) { + if (ourExistingTransaction.get() == this && canReuseExistingTransaction(theExecutionBuilder)) { /* * If we're already in an active transaction, and it's for the right partition, * and it's not a read-only transaction, we don't need to open a new transaction @@ -245,12 +253,22 @@ public class HapiTransactionService implements IHapiTransactionService { */ return executeInExistingTransaction(theCallback); } - } else if (myTransactionPropagationWhenChangingPartitions == Propagation.REQUIRES_NEW) { - return executeInNewTransactionForPartitionChange( - theExecutionBuilder, theCallback, requestPartitionId, previousRequestPartitionId); } - return doExecuteInTransaction(theExecutionBuilder, theCallback, requestPartitionId, previousRequestPartitionId); + HapiTransactionService previousExistingTransaction = ourExistingTransaction.get(); + try { + ourExistingTransaction.set(this); + + if (myTransactionPropagationWhenChangingPartitions == Propagation.REQUIRES_NEW) { + return executeInNewTransactionForPartitionChange( + theExecutionBuilder, theCallback, requestPartitionId, previousRequestPartitionId); + } else { + return doExecuteInTransaction( + theExecutionBuilder, theCallback, requestPartitionId, previousRequestPartitionId); + } + } finally { + ourExistingTransaction.set(previousExistingTransaction); + } } @Nullable @@ -402,7 +420,7 @@ public class HapiTransactionService implements IHapiTransactionService { } } - protected class ExecutionBuilder implements IExecutionBuilder { + protected class ExecutionBuilder implements IExecutionBuilder, TransactionOperations { private final RequestDetails myRequestDetails; private Isolation myIsolation; @@ -473,7 +491,7 @@ public class HapiTransactionService implements IHapiTransactionService { } @Override - public T execute(TransactionCallback callback) { + public T execute(@Nonnull TransactionCallback callback) { assert callback != null; return doExecute(this, callback); @@ -483,6 +501,15 @@ public class HapiTransactionService implements IHapiTransactionService { public RequestPartitionId getRequestPartitionIdForTesting() { return myRequestPartitionId; } + + @VisibleForTesting + public RequestDetails getRequestDetailsForTesting() { + return myRequestDetails; + } + + public Propagation getPropagation() { + return myPropagation; + } } /** @@ -509,7 +536,8 @@ public class HapiTransactionService implements IHapiTransactionService { } @Nullable - private static T executeInExistingTransaction(TransactionCallback theCallback) { + private static T executeInExistingTransaction(@Nonnull TransactionCallback theCallback) { + // TODO we could probably track the TransactionStatus we need as a thread local like we do our partition id. return theCallback.doInTransaction(null); } diff --git a/hapi-fhir-storage/src/main/java/ca/uhn/fhir/jpa/dao/tx/IHapiTransactionService.java b/hapi-fhir-storage/src/main/java/ca/uhn/fhir/jpa/dao/tx/IHapiTransactionService.java index 6a3ca18f3b3..2b56a725169 100644 --- a/hapi-fhir-storage/src/main/java/ca/uhn/fhir/jpa/dao/tx/IHapiTransactionService.java +++ b/hapi-fhir-storage/src/main/java/ca/uhn/fhir/jpa/dao/tx/IHapiTransactionService.java @@ -23,13 +23,14 @@ import ca.uhn.fhir.interceptor.model.RequestPartitionId; import ca.uhn.fhir.rest.api.server.RequestDetails; import ca.uhn.fhir.rest.api.server.storage.TransactionDetails; import ca.uhn.fhir.util.ICallable; +import jakarta.annotation.Nonnull; +import jakarta.annotation.Nullable; import org.springframework.transaction.annotation.Isolation; import org.springframework.transaction.annotation.Propagation; import org.springframework.transaction.support.TransactionCallback; +import org.springframework.transaction.support.TransactionOperations; import java.util.concurrent.Callable; -import javax.annotation.Nonnull; -import javax.annotation.Nullable; /** * This class is used to execute code within the context of a database transaction, @@ -69,10 +70,17 @@ public interface IHapiTransactionService { return withSystemRequest().withRequestPartitionId(theRequestPartitionId); } + /** + * Convenience for TX working with non-partitioned entities. + */ + default IExecutionBuilder withSystemRequestOnDefaultPartition() { + return withSystemRequestOnPartition(RequestPartitionId.defaultPartition()); + } + /** * @deprecated It is highly recommended to use {@link #withRequest(RequestDetails)} instead of this method, for increased visibility. */ - @Deprecated + @Deprecated(since = "6.10") T withRequest( @Nullable RequestDetails theRequestDetails, @Nullable TransactionDetails theTransactionDetails, @@ -80,7 +88,7 @@ public interface IHapiTransactionService { @Nonnull Isolation theIsolation, @Nonnull ICallable theCallback); - interface IExecutionBuilder { + interface IExecutionBuilder extends TransactionOperations { IExecutionBuilder withIsolation(Isolation theIsolation); @@ -98,6 +106,6 @@ public interface IHapiTransactionService { T execute(Callable theTask); - T execute(TransactionCallback callback); + T execute(@Nonnull TransactionCallback callback); } } diff --git a/hapi-fhir-storage/src/main/java/ca/uhn/fhir/jpa/dao/tx/NonTransactionalHapiTransactionService.java b/hapi-fhir-storage/src/main/java/ca/uhn/fhir/jpa/dao/tx/NonTransactionalHapiTransactionService.java index f2b9209ba5b..d4fdaec4ae9 100644 --- a/hapi-fhir-storage/src/main/java/ca/uhn/fhir/jpa/dao/tx/NonTransactionalHapiTransactionService.java +++ b/hapi-fhir-storage/src/main/java/ca/uhn/fhir/jpa/dao/tx/NonTransactionalHapiTransactionService.java @@ -19,11 +19,10 @@ */ package ca.uhn.fhir.jpa.dao.tx; +import jakarta.annotation.Nullable; import org.springframework.transaction.support.SimpleTransactionStatus; import org.springframework.transaction.support.TransactionCallback; -import javax.annotation.Nullable; - /** * A transaction service implementation that does not actually * wrap any transactions. This is mostly intended for tests but diff --git a/hapi-fhir-storage/src/main/java/ca/uhn/fhir/jpa/graphql/GraphQLProvider.java b/hapi-fhir-storage/src/main/java/ca/uhn/fhir/jpa/graphql/GraphQLProvider.java index 4136e40f846..13145488c87 100644 --- a/hapi-fhir-storage/src/main/java/ca/uhn/fhir/jpa/graphql/GraphQLProvider.java +++ b/hapi-fhir-storage/src/main/java/ca/uhn/fhir/jpa/graphql/GraphQLProvider.java @@ -38,6 +38,8 @@ import ca.uhn.fhir.rest.server.exceptions.BaseServerResponseException; import ca.uhn.fhir.rest.server.exceptions.InvalidRequestException; import ca.uhn.fhir.rest.server.exceptions.UnclassifiedServerFailureException; import ca.uhn.fhir.rest.server.servlet.ServletRequestDetails; +import jakarta.annotation.Nonnull; +import jakarta.annotation.Nullable; import org.apache.commons.lang3.ObjectUtils; import org.apache.commons.lang3.Validate; import org.hl7.fhir.instance.model.api.IBaseResource; @@ -51,8 +53,6 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; import java.util.function.Supplier; -import javax.annotation.Nonnull; -import javax.annotation.Nullable; public class GraphQLProvider { private static final Logger ourLog = LoggerFactory.getLogger(GraphQLProvider.class); diff --git a/hapi-fhir-storage/src/main/java/ca/uhn/fhir/jpa/interceptor/PatientIdPartitionInterceptor.java b/hapi-fhir-storage/src/main/java/ca/uhn/fhir/jpa/interceptor/PatientIdPartitionInterceptor.java index 4036aa79c03..049b462b1c6 100644 --- a/hapi-fhir-storage/src/main/java/ca/uhn/fhir/jpa/interceptor/PatientIdPartitionInterceptor.java +++ b/hapi-fhir-storage/src/main/java/ca/uhn/fhir/jpa/interceptor/PatientIdPartitionInterceptor.java @@ -37,6 +37,7 @@ import ca.uhn.fhir.rest.api.RestSearchParameterTypeEnum; import ca.uhn.fhir.rest.api.server.RequestDetails; import ca.uhn.fhir.rest.param.ReferenceParam; import ca.uhn.fhir.rest.server.exceptions.MethodNotAllowedException; +import jakarta.annotation.Nonnull; import org.apache.commons.lang3.StringUtils; import org.hl7.fhir.instance.model.api.IBaseReference; import org.hl7.fhir.instance.model.api.IBaseResource; @@ -47,7 +48,6 @@ import java.util.ArrayList; import java.util.Arrays; import java.util.List; import java.util.stream.Collectors; -import javax.annotation.Nonnull; import static org.apache.commons.lang3.StringUtils.isBlank; import static org.apache.commons.lang3.StringUtils.isNotBlank; diff --git a/hapi-fhir-storage/src/main/java/ca/uhn/fhir/jpa/interceptor/validation/BaseTypedRule.java b/hapi-fhir-storage/src/main/java/ca/uhn/fhir/jpa/interceptor/validation/BaseTypedRule.java index 2cbd1830440..54b4eb9dc51 100644 --- a/hapi-fhir-storage/src/main/java/ca/uhn/fhir/jpa/interceptor/validation/BaseTypedRule.java +++ b/hapi-fhir-storage/src/main/java/ca/uhn/fhir/jpa/interceptor/validation/BaseTypedRule.java @@ -20,10 +20,9 @@ package ca.uhn.fhir.jpa.interceptor.validation; import ca.uhn.fhir.context.FhirContext; +import jakarta.annotation.Nonnull; import org.apache.commons.lang3.Validate; -import javax.annotation.Nonnull; - abstract class BaseTypedRule implements IRepositoryValidatingRule { private final String myResourceType; diff --git a/hapi-fhir-storage/src/main/java/ca/uhn/fhir/jpa/interceptor/validation/IRepositoryValidatingRule.java b/hapi-fhir-storage/src/main/java/ca/uhn/fhir/jpa/interceptor/validation/IRepositoryValidatingRule.java index 4bbeb1af078..7914dee69b9 100644 --- a/hapi-fhir-storage/src/main/java/ca/uhn/fhir/jpa/interceptor/validation/IRepositoryValidatingRule.java +++ b/hapi-fhir-storage/src/main/java/ca/uhn/fhir/jpa/interceptor/validation/IRepositoryValidatingRule.java @@ -20,12 +20,11 @@ package ca.uhn.fhir.jpa.interceptor.validation; import ca.uhn.fhir.rest.api.server.RequestDetails; +import jakarta.annotation.Nonnull; import org.apache.commons.lang3.Validate; import org.hl7.fhir.instance.model.api.IBaseOperationOutcome; import org.hl7.fhir.instance.model.api.IBaseResource; -import javax.annotation.Nonnull; - /** * This is an internal API for HAPI FHIR. It is subject to change without warning. */ diff --git a/hapi-fhir-storage/src/main/java/ca/uhn/fhir/jpa/interceptor/validation/RepositoryValidatingInterceptor.java b/hapi-fhir-storage/src/main/java/ca/uhn/fhir/jpa/interceptor/validation/RepositoryValidatingInterceptor.java index f59ab1bc605..f20cc492826 100644 --- a/hapi-fhir-storage/src/main/java/ca/uhn/fhir/jpa/interceptor/validation/RepositoryValidatingInterceptor.java +++ b/hapi-fhir-storage/src/main/java/ca/uhn/fhir/jpa/interceptor/validation/RepositoryValidatingInterceptor.java @@ -30,6 +30,7 @@ import ca.uhn.fhir.util.ExtensionUtil; import ca.uhn.fhir.util.OperationOutcomeUtil; import com.google.common.collect.ArrayListMultimap; import com.google.common.collect.Multimap; +import jakarta.annotation.Nonnull; import org.apache.commons.lang3.Validate; import org.hl7.fhir.instance.model.api.IBaseResource; import org.slf4j.Logger; @@ -38,7 +39,6 @@ import org.slf4j.LoggerFactory; import java.util.Collection; import java.util.List; import java.util.stream.Collectors; -import javax.annotation.Nonnull; import static ca.uhn.fhir.util.HapiExtensions.EXT_RESOURCE_PLACEHOLDER; diff --git a/hapi-fhir-storage/src/main/java/ca/uhn/fhir/jpa/interceptor/validation/RepositoryValidatingRuleBuilder.java b/hapi-fhir-storage/src/main/java/ca/uhn/fhir/jpa/interceptor/validation/RepositoryValidatingRuleBuilder.java index a7b8671e295..f924c942624 100644 --- a/hapi-fhir-storage/src/main/java/ca/uhn/fhir/jpa/interceptor/validation/RepositoryValidatingRuleBuilder.java +++ b/hapi-fhir-storage/src/main/java/ca/uhn/fhir/jpa/interceptor/validation/RepositoryValidatingRuleBuilder.java @@ -26,6 +26,7 @@ import ca.uhn.fhir.jpa.validation.ValidatorPolicyAdvisor; import ca.uhn.fhir.jpa.validation.ValidatorResourceFetcher; import ca.uhn.fhir.rest.server.interceptor.ValidationResultEnrichingInterceptor; import ca.uhn.fhir.validation.ResultSeverityEnum; +import jakarta.annotation.Nonnull; import org.apache.commons.lang3.Validate; import org.apache.commons.text.WordUtils; import org.hl7.fhir.r5.utils.validation.constants.BestPracticeWarningLevel; @@ -35,7 +36,6 @@ import java.util.ArrayList; import java.util.Arrays; import java.util.Collection; import java.util.List; -import javax.annotation.Nonnull; import static com.google.common.base.Ascii.toLowerCase; import static org.apache.commons.lang3.StringUtils.isNotBlank; diff --git a/hapi-fhir-storage/src/main/java/ca/uhn/fhir/jpa/interceptor/validation/RequireValidationRule.java b/hapi-fhir-storage/src/main/java/ca/uhn/fhir/jpa/interceptor/validation/RequireValidationRule.java index eab7f12bbe8..fcb95ee733f 100644 --- a/hapi-fhir-storage/src/main/java/ca/uhn/fhir/jpa/interceptor/validation/RequireValidationRule.java +++ b/hapi-fhir-storage/src/main/java/ca/uhn/fhir/jpa/interceptor/validation/RequireValidationRule.java @@ -31,6 +31,7 @@ import ca.uhn.fhir.validation.FhirValidator; import ca.uhn.fhir.validation.ResultSeverityEnum; import ca.uhn.fhir.validation.SingleValidationMessage; import ca.uhn.fhir.validation.ValidationResult; +import jakarta.annotation.Nonnull; import org.apache.commons.lang3.Validate; import org.apache.commons.lang3.builder.ToStringBuilder; import org.apache.commons.lang3.builder.ToStringStyle; @@ -41,7 +42,6 @@ import org.hl7.fhir.r5.utils.validation.constants.BestPracticeWarningLevel; import java.util.ArrayList; import java.util.Collections; import java.util.List; -import javax.annotation.Nonnull; class RequireValidationRule extends BaseTypedRule { private final FhirInstanceValidator myValidator; diff --git a/hapi-fhir-storage/src/main/java/ca/uhn/fhir/jpa/interceptor/validation/RuleDisallowProfile.java b/hapi-fhir-storage/src/main/java/ca/uhn/fhir/jpa/interceptor/validation/RuleDisallowProfile.java index 5ae5a036563..174f8c29117 100644 --- a/hapi-fhir-storage/src/main/java/ca/uhn/fhir/jpa/interceptor/validation/RuleDisallowProfile.java +++ b/hapi-fhir-storage/src/main/java/ca/uhn/fhir/jpa/interceptor/validation/RuleDisallowProfile.java @@ -22,6 +22,7 @@ package ca.uhn.fhir.jpa.interceptor.validation; import ca.uhn.fhir.context.FhirContext; import ca.uhn.fhir.rest.api.server.RequestDetails; import ca.uhn.fhir.util.UrlUtil; +import jakarta.annotation.Nonnull; import org.apache.commons.lang3.Validate; import org.apache.commons.lang3.builder.ToStringBuilder; import org.apache.commons.lang3.builder.ToStringStyle; @@ -30,7 +31,6 @@ import org.hl7.fhir.instance.model.api.IPrimitiveType; import java.util.HashSet; import java.util.Set; -import javax.annotation.Nonnull; class RuleDisallowProfile extends BaseTypedRule { private final Set myProfileUrls; diff --git a/hapi-fhir-storage/src/main/java/ca/uhn/fhir/jpa/interceptor/validation/RuleRequireProfileDeclaration.java b/hapi-fhir-storage/src/main/java/ca/uhn/fhir/jpa/interceptor/validation/RuleRequireProfileDeclaration.java index 55f51b8ec4d..e2d9d64128c 100644 --- a/hapi-fhir-storage/src/main/java/ca/uhn/fhir/jpa/interceptor/validation/RuleRequireProfileDeclaration.java +++ b/hapi-fhir-storage/src/main/java/ca/uhn/fhir/jpa/interceptor/validation/RuleRequireProfileDeclaration.java @@ -21,13 +21,13 @@ package ca.uhn.fhir.jpa.interceptor.validation; import ca.uhn.fhir.context.FhirContext; import ca.uhn.fhir.rest.api.server.RequestDetails; +import jakarta.annotation.Nonnull; import org.apache.commons.lang3.builder.ToStringBuilder; import org.apache.commons.lang3.builder.ToStringStyle; import org.hl7.fhir.instance.model.api.IBaseResource; import java.util.Collection; import java.util.Optional; -import javax.annotation.Nonnull; class RuleRequireProfileDeclaration extends BaseTypedRule { private final Collection myProfileOptions; diff --git a/hapi-fhir-storage/src/main/java/ca/uhn/fhir/jpa/model/search/SearchBuilderLoadIncludesParameters.java b/hapi-fhir-storage/src/main/java/ca/uhn/fhir/jpa/model/search/SearchBuilderLoadIncludesParameters.java index f199bbfe9be..558c7a4648d 100644 --- a/hapi-fhir-storage/src/main/java/ca/uhn/fhir/jpa/model/search/SearchBuilderLoadIncludesParameters.java +++ b/hapi-fhir-storage/src/main/java/ca/uhn/fhir/jpa/model/search/SearchBuilderLoadIncludesParameters.java @@ -24,11 +24,11 @@ import ca.uhn.fhir.model.api.Include; import ca.uhn.fhir.rest.api.server.RequestDetails; import ca.uhn.fhir.rest.api.server.storage.IResourcePersistentId; import ca.uhn.fhir.rest.param.DateRangeParam; +import jakarta.persistence.EntityManager; import java.util.ArrayList; import java.util.Collection; import java.util.List; -import javax.persistence.EntityManager; public class SearchBuilderLoadIncludesParameters { diff --git a/hapi-fhir-storage/src/main/java/ca/uhn/fhir/jpa/model/search/SearchRuntimeDetails.java b/hapi-fhir-storage/src/main/java/ca/uhn/fhir/jpa/model/search/SearchRuntimeDetails.java index e5b925da3bd..f00b69ec5f1 100644 --- a/hapi-fhir-storage/src/main/java/ca/uhn/fhir/jpa/model/search/SearchRuntimeDetails.java +++ b/hapi-fhir-storage/src/main/java/ca/uhn/fhir/jpa/model/search/SearchRuntimeDetails.java @@ -21,8 +21,7 @@ package ca.uhn.fhir.jpa.model.search; import ca.uhn.fhir.rest.api.server.RequestDetails; import ca.uhn.fhir.util.StopWatch; - -import javax.annotation.Nullable; +import jakarta.annotation.Nullable; /** * This class contains a runtime in-memory description of a search operation, diff --git a/hapi-fhir-storage/src/main/java/ca/uhn/fhir/jpa/partition/BaseRequestPartitionHelperSvc.java b/hapi-fhir-storage/src/main/java/ca/uhn/fhir/jpa/partition/BaseRequestPartitionHelperSvc.java index b532571ab52..e4832dec545 100644 --- a/hapi-fhir-storage/src/main/java/ca/uhn/fhir/jpa/partition/BaseRequestPartitionHelperSvc.java +++ b/hapi-fhir-storage/src/main/java/ca/uhn/fhir/jpa/partition/BaseRequestPartitionHelperSvc.java @@ -34,6 +34,8 @@ import ca.uhn.fhir.rest.api.server.SystemRequestDetails; import ca.uhn.fhir.rest.server.exceptions.InternalErrorException; import ca.uhn.fhir.rest.server.exceptions.UnprocessableEntityException; import ca.uhn.fhir.rest.server.servlet.ServletRequestDetails; +import jakarta.annotation.Nonnull; +import jakarta.annotation.Nullable; import org.apache.commons.lang3.StringUtils; import org.hl7.fhir.instance.model.api.IBaseResource; import org.springframework.beans.factory.annotation.Autowired; @@ -42,8 +44,6 @@ import java.util.HashSet; import java.util.List; import java.util.Set; import java.util.stream.Collectors; -import javax.annotation.Nonnull; -import javax.annotation.Nullable; import static ca.uhn.fhir.rest.server.util.CompositeInterceptorBroadcaster.doCallHooks; import static ca.uhn.fhir.rest.server.util.CompositeInterceptorBroadcaster.doCallHooksAndReturnObject; diff --git a/hapi-fhir-storage/src/main/java/ca/uhn/fhir/jpa/patch/FhirPatch.java b/hapi-fhir-storage/src/main/java/ca/uhn/fhir/jpa/patch/FhirPatch.java index 310e856e7c2..2786221f34e 100644 --- a/hapi-fhir-storage/src/main/java/ca/uhn/fhir/jpa/patch/FhirPatch.java +++ b/hapi-fhir-storage/src/main/java/ca/uhn/fhir/jpa/patch/FhirPatch.java @@ -28,6 +28,8 @@ import ca.uhn.fhir.parser.path.EncodeContextPath; import ca.uhn.fhir.rest.server.exceptions.InvalidRequestException; import ca.uhn.fhir.util.IModelVisitor2; import ca.uhn.fhir.util.ParametersUtil; +import jakarta.annotation.Nonnull; +import jakarta.annotation.Nullable; import org.apache.commons.lang3.Validate; import org.hl7.fhir.instance.model.api.IBase; import org.hl7.fhir.instance.model.api.IBaseEnumeration; @@ -45,8 +47,6 @@ import java.util.List; import java.util.Objects; import java.util.Optional; import java.util.Set; -import javax.annotation.Nonnull; -import javax.annotation.Nullable; import static org.apache.commons.lang3.StringUtils.defaultString; import static org.apache.commons.lang3.StringUtils.isNotBlank; diff --git a/hapi-fhir-storage/src/main/java/ca/uhn/fhir/jpa/provider/BaseJpaProvider.java b/hapi-fhir-storage/src/main/java/ca/uhn/fhir/jpa/provider/BaseJpaProvider.java index 7a017937c4e..03819a3dd79 100644 --- a/hapi-fhir-storage/src/main/java/ca/uhn/fhir/jpa/provider/BaseJpaProvider.java +++ b/hapi-fhir-storage/src/main/java/ca/uhn/fhir/jpa/provider/BaseJpaProvider.java @@ -29,6 +29,7 @@ import ca.uhn.fhir.rest.param.DateRangeParam; import ca.uhn.fhir.rest.server.exceptions.InvalidRequestException; import ca.uhn.fhir.rest.server.servlet.ServletRequestDetails; import ca.uhn.fhir.util.ParametersUtil; +import jakarta.servlet.http.HttpServletRequest; import org.apache.commons.lang3.StringUtils; import org.hl7.fhir.instance.model.api.IBaseParameters; import org.hl7.fhir.instance.model.api.IPrimitiveType; @@ -39,7 +40,6 @@ import java.util.Date; import java.util.Enumeration; import java.util.Set; import java.util.TreeSet; -import javax.servlet.http.HttpServletRequest; public abstract class BaseJpaProvider { public static final String REMOTE_ADDR = "req.remoteAddr"; diff --git a/hapi-fhir-storage/src/main/java/ca/uhn/fhir/jpa/provider/BaseJpaResourceProvider.java b/hapi-fhir-storage/src/main/java/ca/uhn/fhir/jpa/provider/BaseJpaResourceProvider.java index 8c2a8b35ff3..11b3dee3043 100644 --- a/hapi-fhir-storage/src/main/java/ca/uhn/fhir/jpa/provider/BaseJpaResourceProvider.java +++ b/hapi-fhir-storage/src/main/java/ca/uhn/fhir/jpa/provider/BaseJpaResourceProvider.java @@ -54,15 +54,14 @@ import ca.uhn.fhir.rest.server.exceptions.InvalidRequestException; import ca.uhn.fhir.rest.server.provider.ProviderConstants; import ca.uhn.fhir.util.CoverageIgnore; import ca.uhn.fhir.util.ParametersUtil; +import jakarta.servlet.http.HttpServletRequest; import org.hl7.fhir.instance.model.api.IBaseMetaType; import org.hl7.fhir.instance.model.api.IBaseParameters; import org.hl7.fhir.instance.model.api.IBaseResource; import org.hl7.fhir.instance.model.api.IIdType; import org.hl7.fhir.instance.model.api.IPrimitiveType; -import org.springframework.beans.factory.annotation.Required; import java.util.Date; -import javax.servlet.http.HttpServletRequest; import static ca.uhn.fhir.jpa.model.util.JpaConstants.OPERATION_META_ADD; import static ca.uhn.fhir.jpa.model.util.JpaConstants.OPERATION_META_DELETE; @@ -107,7 +106,6 @@ public abstract class BaseJpaResourceProvider extends B return myDao; } - @Required public void setDao(IFhirResourceDao theDao) { myDao = theDao; } diff --git a/hapi-fhir-storage/src/main/java/ca/uhn/fhir/jpa/provider/BaseStorageSystemProvider.java b/hapi-fhir-storage/src/main/java/ca/uhn/fhir/jpa/provider/BaseStorageSystemProvider.java index 3ad030ff555..2a984c2f90d 100644 --- a/hapi-fhir-storage/src/main/java/ca/uhn/fhir/jpa/provider/BaseStorageSystemProvider.java +++ b/hapi-fhir-storage/src/main/java/ca/uhn/fhir/jpa/provider/BaseStorageSystemProvider.java @@ -29,7 +29,6 @@ import ca.uhn.fhir.rest.api.server.RequestDetails; import ca.uhn.fhir.rest.server.provider.ProviderConstants; import org.hl7.fhir.instance.model.api.IBaseParameters; import org.hl7.fhir.instance.model.api.IPrimitiveType; -import org.springframework.beans.factory.annotation.Required; public abstract class BaseStorageSystemProvider extends BaseJpaProvider { protected IFhirSystemDao myDao; @@ -74,7 +73,6 @@ public abstract class BaseStorageSystemProvider extends BaseJpaProvider { return myDao; } - @Required public void setDao(IFhirSystemDao theDao) { myDao = theDao; } diff --git a/hapi-fhir-storage/src/main/java/ca/uhn/fhir/jpa/searchparam/submit/interceptor/SearchParamValidatingInterceptor.java b/hapi-fhir-storage/src/main/java/ca/uhn/fhir/jpa/searchparam/submit/interceptor/SearchParamValidatingInterceptor.java index 98c377fa6bd..b955cb18c7d 100644 --- a/hapi-fhir-storage/src/main/java/ca/uhn/fhir/jpa/searchparam/submit/interceptor/SearchParamValidatingInterceptor.java +++ b/hapi-fhir-storage/src/main/java/ca/uhn/fhir/jpa/searchparam/submit/interceptor/SearchParamValidatingInterceptor.java @@ -37,6 +37,7 @@ import ca.uhn.fhir.rest.param.TokenOrListParam; import ca.uhn.fhir.rest.param.TokenParam; import ca.uhn.fhir.rest.server.exceptions.UnprocessableEntityException; import ca.uhn.fhir.util.HapiExtensions; +import jakarta.annotation.Nullable; import org.hl7.fhir.instance.model.api.IBaseExtension; import org.hl7.fhir.instance.model.api.IBaseResource; import org.springframework.beans.factory.annotation.Autowired; @@ -45,7 +46,6 @@ import java.util.HashSet; import java.util.List; import java.util.Set; import java.util.stream.Collectors; -import javax.annotation.Nullable; import static org.apache.commons.collections4.CollectionUtils.isNotEmpty; import static org.apache.commons.lang3.StringUtils.isBlank; diff --git a/hapi-fhir-storage/src/main/java/ca/uhn/fhir/jpa/subscription/channel/api/PayloadTooLargeException.java b/hapi-fhir-storage/src/main/java/ca/uhn/fhir/jpa/subscription/channel/api/PayloadTooLargeException.java new file mode 100644 index 00000000000..daac9c18948 --- /dev/null +++ b/hapi-fhir-storage/src/main/java/ca/uhn/fhir/jpa/subscription/channel/api/PayloadTooLargeException.java @@ -0,0 +1,35 @@ +/*- + * #%L + * HAPI FHIR Storage api + * %% + * Copyright (C) 2014 - 2023 Smile CDR, Inc. + * %% + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * #L% + */ +package ca.uhn.fhir.jpa.subscription.channel.api; + +/** + * This exception represents the message payload exceeded the maximum message size of the broker. Used as a wrapper of + * similar exceptions specific to different message brokers, e.g. kafka.common.errors.RecordTooLargeException. + */ +public class PayloadTooLargeException extends RuntimeException { + + public PayloadTooLargeException(String theMessage) { + super(theMessage); + } + + public PayloadTooLargeException(String theMessage, Throwable theThrowable) { + super(theMessage, theThrowable); + } +} diff --git a/hapi-fhir-storage/src/main/java/ca/uhn/fhir/jpa/subscription/channel/impl/LinkedBlockingChannel.java b/hapi-fhir-storage/src/main/java/ca/uhn/fhir/jpa/subscription/channel/impl/LinkedBlockingChannel.java index c42fea383c7..a28c63f5935 100644 --- a/hapi-fhir-storage/src/main/java/ca/uhn/fhir/jpa/subscription/channel/impl/LinkedBlockingChannel.java +++ b/hapi-fhir-storage/src/main/java/ca/uhn/fhir/jpa/subscription/channel/impl/LinkedBlockingChannel.java @@ -21,6 +21,7 @@ package ca.uhn.fhir.jpa.subscription.channel.impl; import ca.uhn.fhir.jpa.subscription.channel.api.IChannelProducer; import ca.uhn.fhir.jpa.subscription.channel.api.IChannelReceiver; +import jakarta.annotation.Nonnull; import org.springframework.messaging.MessageHandler; import org.springframework.messaging.support.ExecutorSubscribableChannel; @@ -28,7 +29,6 @@ import java.util.ArrayList; import java.util.Optional; import java.util.concurrent.Executor; import java.util.function.Supplier; -import javax.annotation.Nonnull; import static org.apache.commons.lang3.ObjectUtils.defaultIfNull; diff --git a/hapi-fhir-storage/src/main/java/ca/uhn/fhir/jpa/subscription/channel/impl/LinkedBlockingChannelFactory.java b/hapi-fhir-storage/src/main/java/ca/uhn/fhir/jpa/subscription/channel/impl/LinkedBlockingChannelFactory.java index 13bd3aede1f..0b9a1518b40 100644 --- a/hapi-fhir-storage/src/main/java/ca/uhn/fhir/jpa/subscription/channel/impl/LinkedBlockingChannelFactory.java +++ b/hapi-fhir-storage/src/main/java/ca/uhn/fhir/jpa/subscription/channel/impl/LinkedBlockingChannelFactory.java @@ -28,13 +28,13 @@ import ca.uhn.fhir.jpa.subscription.channel.api.IChannelSettings; import ca.uhn.fhir.jpa.subscription.channel.subscription.IChannelNamer; import ca.uhn.fhir.subscription.SubscriptionConstants; import ca.uhn.fhir.util.ThreadPoolUtil; +import jakarta.annotation.Nonnull; +import jakarta.annotation.PreDestroy; import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor; import java.util.Collections; import java.util.HashMap; import java.util.Map; -import javax.annotation.Nonnull; -import javax.annotation.PreDestroy; public class LinkedBlockingChannelFactory implements IChannelFactory { diff --git a/hapi-fhir-storage/src/main/java/ca/uhn/fhir/jpa/subscription/channel/impl/RetryingMessageHandlerWrapper.java b/hapi-fhir-storage/src/main/java/ca/uhn/fhir/jpa/subscription/channel/impl/RetryingMessageHandlerWrapper.java index 6c88ae84f2f..b5a88222bbb 100644 --- a/hapi-fhir-storage/src/main/java/ca/uhn/fhir/jpa/subscription/channel/impl/RetryingMessageHandlerWrapper.java +++ b/hapi-fhir-storage/src/main/java/ca/uhn/fhir/jpa/subscription/channel/impl/RetryingMessageHandlerWrapper.java @@ -20,6 +20,7 @@ package ca.uhn.fhir.jpa.subscription.channel.impl; import ca.uhn.fhir.util.BaseUnrecoverableRuntimeException; +import jakarta.annotation.Nonnull; import org.apache.commons.lang3.exception.ExceptionUtils; import org.apache.commons.lang3.time.DateUtils; import org.slf4j.Logger; @@ -36,8 +37,6 @@ import org.springframework.retry.policy.TimeoutRetryPolicy; import org.springframework.retry.support.RetryTemplate; import org.springframework.transaction.CannotCreateTransactionException; -import javax.annotation.Nonnull; - class RetryingMessageHandlerWrapper implements MessageHandler { private static final Logger ourLog = LoggerFactory.getLogger(RetryingMessageHandlerWrapper.class); private final MessageHandler myWrap; diff --git a/hapi-fhir-storage/src/main/java/ca/uhn/fhir/jpa/subscription/match/registry/SubscriptionCanonicalizer.java b/hapi-fhir-storage/src/main/java/ca/uhn/fhir/jpa/subscription/match/registry/SubscriptionCanonicalizer.java index 79470188949..7801d9a7993 100644 --- a/hapi-fhir-storage/src/main/java/ca/uhn/fhir/jpa/subscription/match/registry/SubscriptionCanonicalizer.java +++ b/hapi-fhir-storage/src/main/java/ca/uhn/fhir/jpa/subscription/match/registry/SubscriptionCanonicalizer.java @@ -38,6 +38,8 @@ import ca.uhn.fhir.rest.server.exceptions.PreconditionFailedException; import ca.uhn.fhir.subscription.SubscriptionConstants; import ca.uhn.fhir.util.HapiExtensions; import ca.uhn.fhir.util.SubscriptionUtil; +import jakarta.annotation.Nonnull; +import jakarta.annotation.Nullable; import org.hl7.fhir.exceptions.FHIRException; import org.hl7.fhir.instance.model.api.IBaseHasExtensions; import org.hl7.fhir.instance.model.api.IBaseMetaType; @@ -56,10 +58,9 @@ import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.stream.Collectors; -import javax.annotation.Nonnull; -import javax.annotation.Nullable; import static ca.uhn.fhir.util.HapiExtensions.EX_SEND_DELETE_MESSAGES; +import static java.util.Objects.nonNull; import static java.util.stream.Collectors.mapping; import static java.util.stream.Collectors.toList; @@ -305,8 +306,6 @@ public class SubscriptionCanonicalizer { CanonicalTopicSubscription topicSubscription = retVal.getTopicSubscription(); topicSubscription.setTopic(getCriteria(theSubscription)); - // WIP STR5 support other content types - topicSubscription.setContent(org.hl7.fhir.r5.model.Subscription.SubscriptionPayloadContent.FULLRESOURCE); retVal.setEndpointUrl(channel.getEndpoint()); retVal.setChannelType(getChannelType(subscription)); @@ -320,31 +319,37 @@ public class SubscriptionCanonicalizer { } if (channel.hasExtension(SubscriptionConstants.SUBSCRIPTION_TOPIC_CHANNEL_HEARTBEAT_PERIOD_URL)) { - org.hl7.fhir.r4.model.Extension timeoutExtension = channel.getExtensionByUrl( + org.hl7.fhir.r4.model.Extension channelHeartbeatPeriotUrlExtension = channel.getExtensionByUrl( SubscriptionConstants.SUBSCRIPTION_TOPIC_CHANNEL_HEARTBEAT_PERIOD_URL); - topicSubscription.setHeartbeatPeriod( - Integer.valueOf(timeoutExtension.getValue().primitiveValue())); + topicSubscription.setHeartbeatPeriod(Integer.valueOf( + channelHeartbeatPeriotUrlExtension.getValue().primitiveValue())); } if (channel.hasExtension(SubscriptionConstants.SUBSCRIPTION_TOPIC_CHANNEL_TIMEOUT_URL)) { - org.hl7.fhir.r4.model.Extension timeoutExtension = + org.hl7.fhir.r4.model.Extension channelTimeoutUrlExtension = channel.getExtensionByUrl(SubscriptionConstants.SUBSCRIPTION_TOPIC_CHANNEL_TIMEOUT_URL); topicSubscription.setTimeout( - Integer.valueOf(timeoutExtension.getValue().primitiveValue())); + Integer.valueOf(channelTimeoutUrlExtension.getValue().primitiveValue())); } if (channel.hasExtension(SubscriptionConstants.SUBSCRIPTION_TOPIC_CHANNEL_MAX_COUNT)) { - org.hl7.fhir.r4.model.Extension timeoutExtension = + org.hl7.fhir.r4.model.Extension channelMaxCountExtension = channel.getExtensionByUrl(SubscriptionConstants.SUBSCRIPTION_TOPIC_CHANNEL_MAX_COUNT); topicSubscription.setMaxCount( - Integer.valueOf(timeoutExtension.getValue().primitiveValue())); - } - if (channel.getPayloadElement() - .hasExtension(SubscriptionConstants.SUBSCRIPTION_TOPIC_CHANNEL_PAYLOAD_CONTENT)) { - org.hl7.fhir.r4.model.Extension timeoutExtension = channel.getPayloadElement() - .getExtensionByUrl(SubscriptionConstants.SUBSCRIPTION_TOPIC_CHANNEL_PAYLOAD_CONTENT); - topicSubscription.setContent(org.hl7.fhir.r5.model.Subscription.SubscriptionPayloadContent.fromCode( - timeoutExtension.getValue().primitiveValue())); + Integer.valueOf(channelMaxCountExtension.getValue().primitiveValue())); } + // setting full-resource PayloadContent if backport-payload-content is not provided + org.hl7.fhir.r5.model.Subscription.SubscriptionPayloadContent payloadContent = + org.hl7.fhir.r5.model.Subscription.SubscriptionPayloadContent.FULLRESOURCE; + + org.hl7.fhir.r4.model.Extension channelPayloadContentExtension = channel.getPayloadElement() + .getExtensionByUrl(SubscriptionConstants.SUBSCRIPTION_TOPIC_CHANNEL_PAYLOAD_CONTENT); + + if (nonNull(channelPayloadContentExtension)) { + payloadContent = org.hl7.fhir.r5.model.Subscription.SubscriptionPayloadContent.fromCode( + channelPayloadContentExtension.getValue().primitiveValue()); + } + + topicSubscription.setContent(payloadContent); } else { retVal.setCriteriaString(getCriteria(theSubscription)); retVal.setEndpointUrl(channel.getEndpoint()); @@ -423,13 +428,25 @@ public class SubscriptionCanonicalizer { } if (retVal.isTopicSubscription()) { - retVal.getTopicSubscription().setTopic(getCriteria(theSubscription)); + CanonicalTopicSubscription topicSubscription = retVal.getTopicSubscription(); + topicSubscription.setTopic(getCriteria(theSubscription)); - // WIP STR5 support other content types - retVal.getTopicSubscription() - .setContent(org.hl7.fhir.r5.model.Subscription.SubscriptionPayloadContent.FULLRESOURCE); retVal.setEndpointUrl(channel.getEndpoint()); retVal.setChannelType(getChannelType(subscription)); + + // setting full-resource PayloadContent if backport-payload-content is not provided + org.hl7.fhir.r5.model.Subscription.SubscriptionPayloadContent payloadContent = + org.hl7.fhir.r5.model.Subscription.SubscriptionPayloadContent.FULLRESOURCE; + + org.hl7.fhir.r4b.model.Extension channelPayloadContentExtension = channel.getPayloadElement() + .getExtensionByUrl(SubscriptionConstants.SUBSCRIPTION_TOPIC_CHANNEL_PAYLOAD_CONTENT); + + if (nonNull(channelPayloadContentExtension)) { + payloadContent = org.hl7.fhir.r5.model.Subscription.SubscriptionPayloadContent.fromCode( + channelPayloadContentExtension.getValue().primitiveValue()); + } + + topicSubscription.setContent(payloadContent); } else { retVal.setCriteriaString(getCriteria(theSubscription)); retVal.setEndpointUrl(channel.getEndpoint()); diff --git a/hapi-fhir-storage/src/main/java/ca/uhn/fhir/jpa/subscription/model/CanonicalSubscription.java b/hapi-fhir-storage/src/main/java/ca/uhn/fhir/jpa/subscription/model/CanonicalSubscription.java index 5dce9dae0ef..50462d02c04 100644 --- a/hapi-fhir-storage/src/main/java/ca/uhn/fhir/jpa/subscription/model/CanonicalSubscription.java +++ b/hapi-fhir-storage/src/main/java/ca/uhn/fhir/jpa/subscription/model/CanonicalSubscription.java @@ -22,6 +22,8 @@ package ca.uhn.fhir.jpa.subscription.model; import ca.uhn.fhir.context.FhirContext; import ca.uhn.fhir.model.api.IModelJson; import com.fasterxml.jackson.annotation.JsonProperty; +import jakarta.annotation.Nonnull; +import jakarta.annotation.Nullable; import org.apache.commons.lang3.builder.EqualsBuilder; import org.apache.commons.lang3.builder.HashCodeBuilder; import org.apache.commons.lang3.builder.ToStringBuilder; @@ -36,8 +38,6 @@ import java.util.Collections; import java.util.HashMap; import java.util.List; import java.util.Map; -import javax.annotation.Nonnull; -import javax.annotation.Nullable; import static org.apache.commons.lang3.StringUtils.isNotBlank; diff --git a/hapi-fhir-storage/src/main/java/ca/uhn/fhir/jpa/subscription/model/CanonicalSubscriptionChannelType.java b/hapi-fhir-storage/src/main/java/ca/uhn/fhir/jpa/subscription/model/CanonicalSubscriptionChannelType.java index 58b0f725a2d..d200b09e2df 100644 --- a/hapi-fhir-storage/src/main/java/ca/uhn/fhir/jpa/subscription/model/CanonicalSubscriptionChannelType.java +++ b/hapi-fhir-storage/src/main/java/ca/uhn/fhir/jpa/subscription/model/CanonicalSubscriptionChannelType.java @@ -20,12 +20,11 @@ package ca.uhn.fhir.jpa.subscription.model; import ca.uhn.fhir.i18n.Msg; +import jakarta.annotation.Nonnull; +import jakarta.annotation.Nullable; import org.hl7.fhir.dstu2.model.Subscription; import org.hl7.fhir.exceptions.FHIRException; -import javax.annotation.Nonnull; -import javax.annotation.Nullable; - import static org.apache.commons.lang3.StringUtils.isBlank; public enum CanonicalSubscriptionChannelType { diff --git a/hapi-fhir-storage/src/main/java/ca/uhn/fhir/jpa/subscription/model/ResourceDeliveryJsonMessage.java b/hapi-fhir-storage/src/main/java/ca/uhn/fhir/jpa/subscription/model/ResourceDeliveryJsonMessage.java index 4f1482fafd7..3c3c3f79ab8 100644 --- a/hapi-fhir-storage/src/main/java/ca/uhn/fhir/jpa/subscription/model/ResourceDeliveryJsonMessage.java +++ b/hapi-fhir-storage/src/main/java/ca/uhn/fhir/jpa/subscription/model/ResourceDeliveryJsonMessage.java @@ -23,10 +23,9 @@ import ca.uhn.fhir.rest.server.messaging.json.BaseJsonMessage; import com.fasterxml.jackson.annotation.JsonProperty; import com.fasterxml.jackson.core.JsonProcessingException; import com.fasterxml.jackson.databind.ObjectMapper; +import jakarta.annotation.Nullable; import org.apache.commons.lang3.builder.ToStringBuilder; -import javax.annotation.Nullable; - public class ResourceDeliveryJsonMessage extends BaseJsonMessage { private static final ObjectMapper ourObjectMapper = new ObjectMapper().registerModule(new com.fasterxml.jackson.datatype.jsr310.JavaTimeModule()); diff --git a/hapi-fhir-storage/src/main/java/ca/uhn/fhir/jpa/subscription/model/ResourceDeliveryMessage.java b/hapi-fhir-storage/src/main/java/ca/uhn/fhir/jpa/subscription/model/ResourceDeliveryMessage.java index 9cd638d2600..6e9d4cd0505 100644 --- a/hapi-fhir-storage/src/main/java/ca/uhn/fhir/jpa/subscription/model/ResourceDeliveryMessage.java +++ b/hapi-fhir-storage/src/main/java/ca/uhn/fhir/jpa/subscription/model/ResourceDeliveryMessage.java @@ -27,13 +27,12 @@ import ca.uhn.fhir.rest.server.messaging.BaseResourceMessage; import ca.uhn.fhir.rest.server.messaging.IResourceMessage; import com.fasterxml.jackson.annotation.JsonIgnore; import com.fasterxml.jackson.annotation.JsonProperty; +import jakarta.annotation.Nullable; import org.apache.commons.lang3.StringUtils; import org.apache.commons.lang3.builder.ToStringBuilder; import org.hl7.fhir.instance.model.api.IBaseResource; import org.hl7.fhir.instance.model.api.IIdType; -import javax.annotation.Nullable; - import static org.apache.commons.lang3.StringUtils.isNotBlank; @SuppressWarnings("WeakerAccess") @@ -108,6 +107,10 @@ public class ResourceDeliveryMessage extends BaseResourceMessage implements IRes myPayloadId = thePayload.getIdElement().toUnqualifiedVersionless().getValue(); } + public void setPayloadToNull() { + myPayloadString = null; + } + @Override public String getPayloadId() { return myPayloadId; diff --git a/hapi-fhir-storage/src/main/java/ca/uhn/fhir/jpa/subscription/model/ResourceModifiedJsonMessage.java b/hapi-fhir-storage/src/main/java/ca/uhn/fhir/jpa/subscription/model/ResourceModifiedJsonMessage.java index 14ebe68293a..9a103d41d08 100644 --- a/hapi-fhir-storage/src/main/java/ca/uhn/fhir/jpa/subscription/model/ResourceModifiedJsonMessage.java +++ b/hapi-fhir-storage/src/main/java/ca/uhn/fhir/jpa/subscription/model/ResourceModifiedJsonMessage.java @@ -21,10 +21,9 @@ package ca.uhn.fhir.jpa.subscription.model; import ca.uhn.fhir.rest.server.messaging.json.BaseJsonMessage; import com.fasterxml.jackson.annotation.JsonProperty; +import jakarta.annotation.Nullable; import org.apache.commons.lang3.builder.ToStringBuilder; -import javax.annotation.Nullable; - public class ResourceModifiedJsonMessage extends BaseJsonMessage { @JsonProperty("payload") diff --git a/hapi-fhir-storage/src/main/java/ca/uhn/fhir/jpa/subscription/model/ResourceModifiedMessage.java b/hapi-fhir-storage/src/main/java/ca/uhn/fhir/jpa/subscription/model/ResourceModifiedMessage.java index ab4fe6c7de9..e493035f80c 100644 --- a/hapi-fhir-storage/src/main/java/ca/uhn/fhir/jpa/subscription/model/ResourceModifiedMessage.java +++ b/hapi-fhir-storage/src/main/java/ca/uhn/fhir/jpa/subscription/model/ResourceModifiedMessage.java @@ -26,6 +26,7 @@ import ca.uhn.fhir.rest.server.messaging.BaseResourceModifiedMessage; import com.fasterxml.jackson.annotation.JsonProperty; import org.apache.commons.lang3.builder.ToStringBuilder; import org.hl7.fhir.instance.model.api.IBaseResource; +import org.hl7.fhir.instance.model.api.IIdType; /** * Most of this class has been moved to ResourceModifiedMessage in the hapi-fhir-server project, for a reusable channel ResourceModifiedMessage @@ -47,6 +48,11 @@ public class ResourceModifiedMessage extends BaseResourceModifiedMessage { super(); } + public ResourceModifiedMessage(IIdType theIdType, OperationTypeEnum theOperationType) { + super(theIdType, theOperationType); + setPartitionId(RequestPartitionId.defaultPartition()); + } + public ResourceModifiedMessage( FhirContext theFhirContext, IBaseResource theResource, OperationTypeEnum theOperationType) { super(theFhirContext, theResource, theOperationType); @@ -79,6 +85,10 @@ public class ResourceModifiedMessage extends BaseResourceModifiedMessage { mySubscriptionId = theSubscriptionId; } + public void setPayloadToNull() { + myPayload = null; + } + @Override public String toString() { return new ToStringBuilder(this) diff --git a/hapi-fhir-storage/src/main/java/ca/uhn/fhir/jpa/subscription/triggering/ISubscriptionTriggeringSvc.java b/hapi-fhir-storage/src/main/java/ca/uhn/fhir/jpa/subscription/triggering/ISubscriptionTriggeringSvc.java index aaac3a2ce35..6740ffddeef 100644 --- a/hapi-fhir-storage/src/main/java/ca/uhn/fhir/jpa/subscription/triggering/ISubscriptionTriggeringSvc.java +++ b/hapi-fhir-storage/src/main/java/ca/uhn/fhir/jpa/subscription/triggering/ISubscriptionTriggeringSvc.java @@ -19,12 +19,12 @@ */ package ca.uhn.fhir.jpa.subscription.triggering; +import jakarta.annotation.Nullable; import org.hl7.fhir.instance.model.api.IBaseParameters; import org.hl7.fhir.instance.model.api.IIdType; import org.hl7.fhir.instance.model.api.IPrimitiveType; import java.util.List; -import javax.annotation.Nullable; public interface ISubscriptionTriggeringSvc { diff --git a/hapi-fhir-storage/src/main/java/ca/uhn/fhir/jpa/util/BaseCaptureQueriesListener.java b/hapi-fhir-storage/src/main/java/ca/uhn/fhir/jpa/util/BaseCaptureQueriesListener.java index 6ec1911b70c..ba825fcbf52 100644 --- a/hapi-fhir-storage/src/main/java/ca/uhn/fhir/jpa/util/BaseCaptureQueriesListener.java +++ b/hapi-fhir-storage/src/main/java/ca/uhn/fhir/jpa/util/BaseCaptureQueriesListener.java @@ -21,6 +21,7 @@ package ca.uhn.fhir.jpa.util; import ca.uhn.fhir.interceptor.model.RequestPartitionId; import ca.uhn.fhir.jpa.dao.tx.HapiTransactionService; +import jakarta.annotation.Nullable; import net.ttddyy.dsproxy.ExecutionInfo; import net.ttddyy.dsproxy.QueryInfo; import net.ttddyy.dsproxy.listener.MethodExecutionContext; @@ -32,7 +33,6 @@ import java.util.List; import java.util.Queue; import java.util.concurrent.atomic.AtomicInteger; import java.util.stream.Collectors; -import javax.annotation.Nullable; import static org.apache.commons.lang3.StringUtils.trim; diff --git a/hapi-fhir-storage/src/main/java/ca/uhn/fhir/jpa/util/CircularQueueCaptureQueriesListener.java b/hapi-fhir-storage/src/main/java/ca/uhn/fhir/jpa/util/CircularQueueCaptureQueriesListener.java index 3251fc242f0..294f2f0ca86 100644 --- a/hapi-fhir-storage/src/main/java/ca/uhn/fhir/jpa/util/CircularQueueCaptureQueriesListener.java +++ b/hapi-fhir-storage/src/main/java/ca/uhn/fhir/jpa/util/CircularQueueCaptureQueriesListener.java @@ -21,6 +21,7 @@ package ca.uhn.fhir.jpa.util; import ca.uhn.fhir.util.StopWatch; import com.google.common.collect.Queues; +import jakarta.annotation.Nonnull; import net.ttddyy.dsproxy.support.ProxyDataSourceBuilder; import org.apache.commons.collections4.queue.CircularFifoQueue; import org.hl7.fhir.r4.model.InstantType; @@ -32,12 +33,12 @@ import java.util.Arrays; import java.util.Collections; import java.util.Date; import java.util.List; +import java.util.Locale; import java.util.Queue; import java.util.concurrent.atomic.AtomicInteger; import java.util.function.Predicate; import java.util.stream.Collectors; import java.util.stream.Stream; -import javax.annotation.Nonnull; /** * This is a query listener designed to be plugged into a {@link ProxyDataSourceBuilder proxy DataSource}. @@ -50,12 +51,17 @@ import javax.annotation.Nonnull; */ public class CircularQueueCaptureQueriesListener extends BaseCaptureQueriesListener { + public static final Predicate DEFAULT_SELECT_INCLUSION_CRITERIA = + t -> t.toLowerCase(Locale.US).startsWith("select"); private static final int CAPACITY = 1000; private static final Logger ourLog = LoggerFactory.getLogger(CircularQueueCaptureQueriesListener.class); private Queue myQueries; private AtomicInteger myCommitCounter; private AtomicInteger myRollbackCounter; + @Nonnull + private Predicate mySelectQueryInclusionCriteria = DEFAULT_SELECT_INCLUSION_CRITERIA; + /** * Constructor */ @@ -63,6 +69,16 @@ public class CircularQueueCaptureQueriesListener extends BaseCaptureQueriesListe startCollecting(); } + /** + * Sets an alternate inclusion criteria for select queries. This can be used to add + * additional criteria beyond the default value of {@link #DEFAULT_SELECT_INCLUSION_CRITERIA}. + */ + public CircularQueueCaptureQueriesListener setSelectQueryInclusionCriteria( + @Nonnull Predicate theSelectQueryInclusionCriteria) { + mySelectQueryInclusionCriteria = theSelectQueryInclusionCriteria; + return this; + } + @Override protected Queue provideQueryList() { return myQueries; @@ -133,6 +149,22 @@ public class CircularQueueCaptureQueriesListener extends BaseCaptureQueriesListe return getQueriesStartingWith(theStart, null); } + private List getQueriesMatching(Predicate thePredicate, String theThreadName) { + return getCapturedQueries().stream() + .filter(t -> theThreadName == null || t.getThreadName().equals(theThreadName)) + .filter(t -> thePredicate.test(t.getSql(false, false))) + .collect(Collectors.toList()); + } + + private List getQueriesMatching(Predicate thePredicate) { + return getQueriesMatching(thePredicate, null); + } + + private List getQueriesForCurrentThreadMatching(Predicate thePredicate) { + String threadName = Thread.currentThread().getName(); + return getQueriesMatching(thePredicate, threadName); + } + public int getCommitCount() { return myCommitCounter.get(); } @@ -145,7 +177,7 @@ public class CircularQueueCaptureQueriesListener extends BaseCaptureQueriesListe * Returns all SELECT queries executed on the current thread - Index 0 is oldest */ public List getSelectQueries() { - return getQueriesStartingWith("select"); + return getQueriesMatching(mySelectQueryInclusionCriteria); } /** @@ -173,7 +205,7 @@ public class CircularQueueCaptureQueriesListener extends BaseCaptureQueriesListe * Returns all SELECT queries executed on the current thread - Index 0 is oldest */ public List getSelectQueriesForCurrentThread() { - return getQueriesForCurrentThreadStartingWith("select"); + return getQueriesForCurrentThreadMatching(mySelectQueryInclusionCriteria); } /** @@ -208,40 +240,31 @@ public class CircularQueueCaptureQueriesListener extends BaseCaptureQueriesListe * Log all captured UPDATE queries */ public String logUpdateQueriesForCurrentThread() { - List queries = getUpdateQueriesForCurrentThread().stream() - .map(CircularQueueCaptureQueriesListener::formatQueryAsSql) - .collect(Collectors.toList()); - String joined = String.join("\n", queries); + List queries = getUpdateQueriesForCurrentThread(); + List queriesStrings = renderQueriesForLogging(true, true, queries); + String joined = String.join("\n", queriesStrings); ourLog.info("Update Queries:\n{}", joined); return joined; } /** * Log all captured SELECT queries - * - * @return */ public String logSelectQueriesForCurrentThread(int... theIndexes) { - List queries = getSelectQueriesForCurrentThread().stream() - .map(CircularQueueCaptureQueriesListener::formatQueryAsSql) - .collect(Collectors.toList()); + List queries = getSelectQueriesForCurrentThread(); + List queriesStrings = renderQueriesForLogging(true, true, queries); List newList = new ArrayList<>(); if (theIndexes != null && theIndexes.length > 0) { - for (int i = 0; i < theIndexes.length; i++) { - int index = theIndexes[i]; - newList.add("[" + index + "] " + queries.get(index)); - } - } else { - for (int i = 0; i < queries.size(); i++) { - newList.add("[" + i + "] " + queries.get(i)); + for (int index : theIndexes) { + newList.add(queriesStrings.get(index)); } + queriesStrings = newList; } - queries = newList; - String queriesAsString = String.join("\n", queries); - ourLog.info("Select Queries:\n{}", queriesAsString); - return queriesAsString; + String joined = String.join("\n", queriesStrings); + ourLog.info("Select Queries:\n{}", joined); + return joined; } /** @@ -256,20 +279,32 @@ public class CircularQueueCaptureQueriesListener extends BaseCaptureQueriesListe */ public List logSelectQueries(boolean theInlineParams, boolean theFormatSql) { List queries = getSelectQueries(); - List queriesStrings = queries.stream() - .map(t -> CircularQueueCaptureQueriesListener.formatQueryAsSql(t, theInlineParams, theFormatSql)) - .collect(Collectors.toList()); + List queriesStrings = renderQueriesForLogging(theInlineParams, theFormatSql, queries); ourLog.info("Select Queries:\n{}", String.join("\n", queriesStrings)); return queries; } + @Nonnull + private static List renderQueriesForLogging( + boolean theInlineParams, boolean theFormatSql, List queries) { + List queriesStrings = new ArrayList<>(); + for (int i = 0; i < queries.size(); i++) { + SqlQuery query = queries.get(i); + String remderedString = "[" + i + "] " + + CircularQueueCaptureQueriesListener.formatQueryAsSql(query, theInlineParams, theFormatSql); + queriesStrings.add(remderedString); + } + return queriesStrings; + } + /** * Log first captured SELECT query */ public void logFirstSelectQueryForCurrentThread() { + boolean inlineParams = true; String firstSelectQuery = getSelectQueriesForCurrentThread().stream() .findFirst() - .map(CircularQueueCaptureQueriesListener::formatQueryAsSql) + .map(t -> CircularQueueCaptureQueriesListener.formatQueryAsSql(t, inlineParams, inlineParams)) .orElse("NONE FOUND"); ourLog.info("First select SqlQuery:\n{}", firstSelectQuery); } @@ -278,10 +313,9 @@ public class CircularQueueCaptureQueriesListener extends BaseCaptureQueriesListe * Log all captured INSERT queries */ public String logInsertQueriesForCurrentThread() { - List queries = getInsertQueriesForCurrentThread().stream() - .map(CircularQueueCaptureQueriesListener::formatQueryAsSql) - .collect(Collectors.toList()); - String queriesAsString = String.join("\n", queries); + List queries = getInsertQueriesForCurrentThread(); + List queriesStrings = renderQueriesForLogging(true, true, queries); + String queriesAsString = String.join("\n", queriesStrings); ourLog.info("Insert Queries:\n{}", queriesAsString); return queriesAsString; } @@ -290,20 +324,18 @@ public class CircularQueueCaptureQueriesListener extends BaseCaptureQueriesListe * Log all captured queries */ public void logAllQueriesForCurrentThread() { - List queries = getAllQueriesForCurrentThread().stream() - .map(CircularQueueCaptureQueriesListener::formatQueryAsSql) - .collect(Collectors.toList()); - ourLog.info("Queries:\n{}", String.join("\n", queries)); + List queries = getAllQueriesForCurrentThread(); + List queriesStrings = renderQueriesForLogging(true, true, queries); + ourLog.info("Queries:\n{}", String.join("\n", queriesStrings)); } /** * Log all captured queries */ public void logAllQueries() { - List queries = getCapturedQueries().stream() - .map(CircularQueueCaptureQueriesListener::formatQueryAsSql) - .collect(Collectors.toList()); - ourLog.info("Queries:\n{}", String.join("\n", queries)); + List queries = getCapturedQueries(); + List queriesStrings = renderQueriesForLogging(true, true, queries); + ourLog.info("Queries:\n{}", String.join("\n", queriesStrings)); } /** @@ -317,10 +349,12 @@ public class CircularQueueCaptureQueriesListener extends BaseCaptureQueriesListe * Log all captured INSERT queries */ public int logInsertQueries(Predicate theInclusionPredicate) { - List insertQueries = getInsertQueries(); - List queries = insertQueries.stream() + List insertQueries = getInsertQueries().stream() .filter(t -> theInclusionPredicate == null || theInclusionPredicate.test(t)) - .map(CircularQueueCaptureQueriesListener::formatQueryAsSql) + .collect(Collectors.toList()); + boolean inlineParams = true; + List queries = insertQueries.stream() + .map(t -> CircularQueueCaptureQueriesListener.formatQueryAsSql(t, inlineParams, inlineParams)) .collect(Collectors.toList()); ourLog.info("Insert Queries:\n{}", String.join("\n", queries)); @@ -331,23 +365,20 @@ public class CircularQueueCaptureQueriesListener extends BaseCaptureQueriesListe * Log all captured INSERT queries */ public int logUpdateQueries() { - List updateQueries = getUpdateQueries(); - List queries = updateQueries.stream() - .map(CircularQueueCaptureQueriesListener::formatQueryAsSql) - .collect(Collectors.toList()); - ourLog.info("Update Queries:\n{}", String.join("\n", queries)); + List queries = getUpdateQueries(); + List queriesStrings = renderQueriesForLogging(true, true, queries); + ourLog.info("Update Queries:\n{}", String.join("\n", queriesStrings)); - return countQueries(updateQueries); + return countQueries(queries); } /** * Log all captured DELETE queries */ public String logDeleteQueriesForCurrentThread() { - List queries = getDeleteQueriesForCurrentThread().stream() - .map(CircularQueueCaptureQueriesListener::formatQueryAsSql) - .collect(Collectors.toList()); - String joined = String.join("\n", queries); + List queries = getDeleteQueriesForCurrentThread(); + List queriesStrings = renderQueriesForLogging(true, true, queries); + String joined = String.join("\n", queriesStrings); ourLog.info("Delete Queries:\n{}", joined); return joined; } @@ -356,13 +387,11 @@ public class CircularQueueCaptureQueriesListener extends BaseCaptureQueriesListe * Log all captured DELETE queries */ public int logDeleteQueries() { - List deleteQueries = getDeleteQueries(); - List queries = deleteQueries.stream() - .map(CircularQueueCaptureQueriesListener::formatQueryAsSql) - .collect(Collectors.toList()); - ourLog.info("Delete Queries:\n{}", String.join("\n", queries)); + List queries = getDeleteQueries(); + List queriesStrings = renderQueriesForLogging(true, true, queries); + ourLog.info("Delete Queries:\n{}", String.join("\n", queriesStrings)); - return countQueries(deleteQueries); + return countQueries(queries); } public int countSelectQueries() { diff --git a/hapi-fhir-storage/src/main/java/ca/uhn/fhir/jpa/util/MemoryCacheService.java b/hapi-fhir-storage/src/main/java/ca/uhn/fhir/jpa/util/MemoryCacheService.java index 6cf8ce63220..105a59a7bc3 100644 --- a/hapi-fhir-storage/src/main/java/ca/uhn/fhir/jpa/util/MemoryCacheService.java +++ b/hapi-fhir-storage/src/main/java/ca/uhn/fhir/jpa/util/MemoryCacheService.java @@ -24,6 +24,7 @@ import ca.uhn.fhir.jpa.api.model.TranslationQuery; import ca.uhn.fhir.jpa.model.entity.TagTypeEnum; import ca.uhn.fhir.sl.cache.Cache; import ca.uhn.fhir.sl.cache.CacheFactory; +import jakarta.annotation.Nonnull; import org.apache.commons.lang3.builder.EqualsBuilder; import org.apache.commons.lang3.builder.HashCodeBuilder; import org.springframework.transaction.support.TransactionSynchronization; @@ -33,7 +34,6 @@ import java.util.Collection; import java.util.EnumMap; import java.util.Map; import java.util.function.Function; -import javax.annotation.Nonnull; import static java.util.concurrent.TimeUnit.MINUTES; import static java.util.concurrent.TimeUnit.SECONDS; diff --git a/hapi-fhir-storage/src/main/java/ca/uhn/fhir/jpa/util/SqlQuery.java b/hapi-fhir-storage/src/main/java/ca/uhn/fhir/jpa/util/SqlQuery.java index 1717acdcbf7..efb4161dcca 100644 --- a/hapi-fhir-storage/src/main/java/ca/uhn/fhir/jpa/util/SqlQuery.java +++ b/hapi-fhir-storage/src/main/java/ca/uhn/fhir/jpa/util/SqlQuery.java @@ -23,6 +23,8 @@ import ca.uhn.fhir.interceptor.model.RequestPartitionId; import ca.uhn.fhir.util.UrlUtil; import org.apache.commons.lang3.Validate; import org.hibernate.engine.jdbc.internal.BasicFormatterImpl; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; import java.util.ArrayList; import java.util.Collections; @@ -31,6 +33,7 @@ import java.util.List; import static org.apache.commons.lang3.StringUtils.trim; public class SqlQuery { + private static final Logger ourLog = LoggerFactory.getLogger(SqlQuery.class); private final String myThreadName = Thread.currentThread().getName(); private final String mySql; private final List myParams; diff --git a/hapi-fhir-storage/src/main/java/ca/uhn/fhir/jpa/validation/ValidationSettings.java b/hapi-fhir-storage/src/main/java/ca/uhn/fhir/jpa/validation/ValidationSettings.java index 9f0fef49289..39620b45478 100644 --- a/hapi-fhir-storage/src/main/java/ca/uhn/fhir/jpa/validation/ValidationSettings.java +++ b/hapi-fhir-storage/src/main/java/ca/uhn/fhir/jpa/validation/ValidationSettings.java @@ -19,11 +19,10 @@ */ package ca.uhn.fhir.jpa.validation; +import jakarta.annotation.Nonnull; import org.hl7.fhir.r5.utils.validation.constants.ReferenceValidationPolicy; import org.thymeleaf.util.Validate; -import javax.annotation.Nonnull; - public class ValidationSettings { private ReferenceValidationPolicy myLocalReferenceValidationDefaultPolicy = ReferenceValidationPolicy.IGNORE; diff --git a/hapi-fhir-storage/src/main/java/ca/uhn/fhir/jpa/validation/ValidatorResourceFetcher.java b/hapi-fhir-storage/src/main/java/ca/uhn/fhir/jpa/validation/ValidatorResourceFetcher.java index d7fc7b40ab7..f988532989a 100644 --- a/hapi-fhir-storage/src/main/java/ca/uhn/fhir/jpa/validation/ValidatorResourceFetcher.java +++ b/hapi-fhir-storage/src/main/java/ca/uhn/fhir/jpa/validation/ValidatorResourceFetcher.java @@ -24,12 +24,14 @@ import ca.uhn.fhir.context.support.IValidationSupport; import ca.uhn.fhir.i18n.Msg; import ca.uhn.fhir.jpa.api.dao.DaoRegistry; import ca.uhn.fhir.jpa.api.dao.IFhirResourceDao; +import ca.uhn.fhir.jpa.searchparam.SearchParameterMap; import ca.uhn.fhir.rest.api.server.RequestDetails; +import ca.uhn.fhir.rest.param.TokenParam; +import ca.uhn.fhir.rest.param.UriParam; +import ca.uhn.fhir.rest.server.exceptions.InvalidRequestException; import ca.uhn.fhir.rest.server.exceptions.ResourceNotFoundException; import org.hl7.fhir.common.hapi.validation.validator.VersionSpecificWorkerContextWrapper; -import org.hl7.fhir.exceptions.DefinitionException; import org.hl7.fhir.exceptions.FHIRException; -import org.hl7.fhir.exceptions.FHIRFormatError; import org.hl7.fhir.instance.model.api.IBaseResource; import org.hl7.fhir.r4.model.IdType; import org.hl7.fhir.r5.elementmodel.Element; @@ -37,12 +39,11 @@ import org.hl7.fhir.r5.elementmodel.JsonParser; import org.hl7.fhir.r5.model.CanonicalResource; import org.hl7.fhir.r5.utils.validation.IResourceValidator; import org.hl7.fhir.r5.utils.validation.IValidatorResourceFetcher; +import org.hl7.fhir.utilities.CanonicalPair; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import java.io.IOException; -import java.net.MalformedURLException; -import java.net.URISyntaxException; +import java.util.List; import java.util.Locale; public class ValidatorResourceFetcher implements IValidatorResourceFetcher { @@ -50,22 +51,19 @@ public class ValidatorResourceFetcher implements IValidatorResourceFetcher { private static final Logger ourLog = LoggerFactory.getLogger(ValidatorResourceFetcher.class); private final FhirContext myFhirContext; - private final IValidationSupport myValidationSupport; private final DaoRegistry myDaoRegistry; private final VersionSpecificWorkerContextWrapper myVersionSpecificContextWrapper; public ValidatorResourceFetcher( FhirContext theFhirContext, IValidationSupport theValidationSupport, DaoRegistry theDaoRegistry) { myFhirContext = theFhirContext; - myValidationSupport = theValidationSupport; myDaoRegistry = theDaoRegistry; myVersionSpecificContextWrapper = - VersionSpecificWorkerContextWrapper.newVersionSpecificWorkerContextWrapper(myValidationSupport); + VersionSpecificWorkerContextWrapper.newVersionSpecificWorkerContextWrapper(theValidationSupport); } @Override - public Element fetch(IResourceValidator iResourceValidator, Object appContext, String theUrl) - throws FHIRFormatError, DefinitionException, FHIRException, IOException { + public Element fetch(IResourceValidator iResourceValidator, Object appContext, String theUrl) throws FHIRException { IdType id = new IdType(theUrl); String resourceType = id.getResourceType(); IFhirResourceDao dao = myDaoRegistry.getResourceDao(resourceType); @@ -74,9 +72,13 @@ public class ValidatorResourceFetcher implements IValidatorResourceFetcher { target = dao.read(id, (RequestDetails) appContext); } catch (ResourceNotFoundException e) { ourLog.info("Failed to resolve local reference: {}", theUrl); - return null; + try { + target = fetchByUrl(theUrl, dao, (RequestDetails) appContext); + } catch (ResourceNotFoundException e2) { + ourLog.info("Failed to find resource by URL: {}", theUrl); + return null; + } } - try { return new JsonParser(myVersionSpecificContextWrapper) .parse(myFhirContext.newJsonParser().encodeResourceToString(target), resourceType); @@ -85,15 +87,40 @@ public class ValidatorResourceFetcher implements IValidatorResourceFetcher { } } + private IBaseResource fetchByUrl(String url, IFhirResourceDao dao, RequestDetails requestDetails) + throws ResourceNotFoundException { + CanonicalPair pair = new CanonicalPair(url); + SearchParameterMap searchParameterMap = new SearchParameterMap(); + searchParameterMap.add("url", new UriParam(pair.getUrl())); + String version = pair.getVersion(); + if (version != null && !version.isEmpty()) { + searchParameterMap.add("version", new TokenParam(version)); + } + List results = null; + try { + results = dao.search(searchParameterMap, requestDetails).getAllResources(); + } catch (InvalidRequestException e) { + ourLog.info("Resource does not support 'url' or 'version' Search Parameters"); + } + if (results != null && results.size() > 0) { + if (results.size() > 1) { + ourLog.warn( + String.format("Multiple results found for URL '%s', only the first will be considered.", url)); + } + return results.get(0); + } else { + throw new ResourceNotFoundException(Msg.code(2444) + "Failed to find resource by URL: " + url); + } + } + @Override public boolean resolveURL( - IResourceValidator iResourceValidator, Object o, String s, String s1, String s2, boolean isCanonical) - throws IOException, FHIRException { + IResourceValidator iResourceValidator, Object o, String s, String s1, String s2, boolean isCanonical) { return true; } @Override - public byte[] fetchRaw(IResourceValidator iResourceValidator, String s) throws MalformedURLException, IOException { + public byte[] fetchRaw(IResourceValidator iResourceValidator, String s) throws UnsupportedOperationException { throw new UnsupportedOperationException(Msg.code(577)); } @@ -104,8 +131,7 @@ public class ValidatorResourceFetcher implements IValidatorResourceFetcher { } @Override - public CanonicalResource fetchCanonicalResource(IResourceValidator iResourceValidator, String s) - throws URISyntaxException { + public CanonicalResource fetchCanonicalResource(IResourceValidator iResourceValidator, String s) { return null; } diff --git a/hapi-fhir-storage/src/main/java/ca/uhn/fhir/storage/interceptor/balp/AsyncMemoryQueueBackedFhirClientBalpSink.java b/hapi-fhir-storage/src/main/java/ca/uhn/fhir/storage/interceptor/balp/AsyncMemoryQueueBackedFhirClientBalpSink.java index 4a39746796c..be6ca1a6130 100644 --- a/hapi-fhir-storage/src/main/java/ca/uhn/fhir/storage/interceptor/balp/AsyncMemoryQueueBackedFhirClientBalpSink.java +++ b/hapi-fhir-storage/src/main/java/ca/uhn/fhir/storage/interceptor/balp/AsyncMemoryQueueBackedFhirClientBalpSink.java @@ -24,6 +24,9 @@ import ca.uhn.fhir.rest.client.api.IGenericClient; import ca.uhn.fhir.rest.server.exceptions.BaseServerResponseException; import ca.uhn.fhir.util.BundleBuilder; import ca.uhn.fhir.util.ThreadPoolUtil; +import jakarta.annotation.Nonnull; +import jakarta.annotation.Nullable; +import jakarta.annotation.PreDestroy; import org.hl7.fhir.instance.model.api.IBaseBundle; import org.hl7.fhir.instance.model.api.IBaseResource; import org.slf4j.Logger; @@ -33,9 +36,6 @@ import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor; import java.util.ArrayList; import java.util.List; import java.util.concurrent.atomic.AtomicLong; -import javax.annotation.Nonnull; -import javax.annotation.Nullable; -import javax.annotation.PreDestroy; /** * This implementation of the {@link IBalpAuditEventSink} transmits audit events to diff --git a/hapi-fhir-storage/src/main/java/ca/uhn/fhir/storage/interceptor/balp/BalpAuditCaptureInterceptor.java b/hapi-fhir-storage/src/main/java/ca/uhn/fhir/storage/interceptor/balp/BalpAuditCaptureInterceptor.java index d7ae427a476..305d7cdb246 100644 --- a/hapi-fhir-storage/src/main/java/ca/uhn/fhir/storage/interceptor/balp/BalpAuditCaptureInterceptor.java +++ b/hapi-fhir-storage/src/main/java/ca/uhn/fhir/storage/interceptor/balp/BalpAuditCaptureInterceptor.java @@ -29,16 +29,13 @@ import ca.uhn.fhir.rest.api.server.IPreResourceShowDetails; import ca.uhn.fhir.rest.server.servlet.ServletRequestDetails; import ca.uhn.fhir.util.FhirTerser; import ca.uhn.fhir.util.UrlUtil; +import jakarta.annotation.Nonnull; import org.apache.commons.lang3.Validate; import org.hl7.fhir.instance.model.api.IBaseResource; import org.hl7.fhir.r4.model.AuditEvent; import java.nio.charset.StandardCharsets; import java.util.*; -import javax.annotation.Nonnull; -import javax.servlet.http.HttpServletRequest; - -import static org.apache.commons.lang3.StringUtils.isNotBlank; /** * The IHE Basic Audit Logging Pattern (BALP) interceptor can be used to autopmatically generate @@ -319,7 +316,7 @@ public class BalpAuditCaptureInterceptor { auditEvent.setOutcome(AuditEvent.AuditEventOutcome._0); auditEvent.setRecorded(new Date()); - auditEvent.getSource().getObserver().setDisplay(theRequestDetails.getServerBaseForRequest()); + auditEvent.getSource().getObserver().setDisplay(theRequestDetails.getFhirServerBase()); AuditEvent.AuditEventAgentComponent clientAgent = auditEvent.addAgent(); clientAgent.setWho(myContextServices.getAgentClientWho(theRequestDetails)); @@ -333,8 +330,8 @@ public class BalpAuditCaptureInterceptor { AuditEvent.AuditEventAgentComponent serverAgent = auditEvent.addAgent(); serverAgent.getType().addCoding(theProfile.getAgentServerTypeCoding()); - serverAgent.getWho().setDisplay(theRequestDetails.getServerBaseForRequest()); - serverAgent.getNetwork().setAddress(theRequestDetails.getServerBaseForRequest()); + serverAgent.getWho().setDisplay(theRequestDetails.getFhirServerBase()); + serverAgent.getNetwork().setAddress(theRequestDetails.getFhirServerBase()); serverAgent.setRequestor(false); AuditEvent.AuditEventAgentComponent userAgent = auditEvent.addAgent(); @@ -374,19 +371,14 @@ public class BalpAuditCaptureInterceptor { // Description StringBuilder description = new StringBuilder(); - HttpServletRequest servletRequest = theRequestDetails.getServletRequest(); - description.append(servletRequest.getMethod()); + description.append(theRequestDetails.getRequestType().name()); description.append(" "); - description.append(servletRequest.getRequestURI()); - if (isNotBlank(servletRequest.getQueryString())) { - description.append("?"); - description.append(servletRequest.getQueryString()); - } + description.append(theRequestDetails.getCompleteUrl()); queryEntity.setDescription(description.toString()); // Query String StringBuilder queryString = new StringBuilder(); - queryString.append(theRequestDetails.getServerBaseForRequest()); + queryString.append(theRequestDetails.getFhirServerBase()); queryString.append("/"); queryString.append(theRequestDetails.getRequestPath()); boolean first = true; diff --git a/hapi-fhir-storage/src/main/java/ca/uhn/fhir/storage/interceptor/balp/FhirClientBalpSink.java b/hapi-fhir-storage/src/main/java/ca/uhn/fhir/storage/interceptor/balp/FhirClientBalpSink.java index 23911345825..6ed047bc6bd 100644 --- a/hapi-fhir-storage/src/main/java/ca/uhn/fhir/storage/interceptor/balp/FhirClientBalpSink.java +++ b/hapi-fhir-storage/src/main/java/ca/uhn/fhir/storage/interceptor/balp/FhirClientBalpSink.java @@ -22,13 +22,13 @@ package ca.uhn.fhir.storage.interceptor.balp; import ca.uhn.fhir.context.FhirContext; import ca.uhn.fhir.rest.client.api.IGenericClient; import ca.uhn.hapi.converters.canonical.VersionCanonicalizer; +import jakarta.annotation.Nonnull; +import jakarta.annotation.Nullable; import org.apache.commons.lang3.Validate; import org.hl7.fhir.instance.model.api.IBaseResource; import org.hl7.fhir.r4.model.AuditEvent; import java.util.List; -import javax.annotation.Nonnull; -import javax.annotation.Nullable; public class FhirClientBalpSink implements IBalpAuditEventSink { diff --git a/hapi-fhir-storage/src/main/java/ca/uhn/fhir/storage/interceptor/balp/IBalpAuditContextServices.java b/hapi-fhir-storage/src/main/java/ca/uhn/fhir/storage/interceptor/balp/IBalpAuditContextServices.java index 412991d6d08..b4a4e8863ca 100644 --- a/hapi-fhir-storage/src/main/java/ca/uhn/fhir/storage/interceptor/balp/IBalpAuditContextServices.java +++ b/hapi-fhir-storage/src/main/java/ca/uhn/fhir/storage/interceptor/balp/IBalpAuditContextServices.java @@ -21,13 +21,12 @@ package ca.uhn.fhir.storage.interceptor.balp; import ca.uhn.fhir.rest.api.server.RequestDetails; import ca.uhn.fhir.rest.server.servlet.ServletRequestDetails; +import jakarta.annotation.Nonnull; import org.hl7.fhir.instance.model.api.IBaseResource; import org.hl7.fhir.instance.model.api.IIdType; import org.hl7.fhir.r4.model.AuditEvent; import org.hl7.fhir.r4.model.Reference; -import javax.annotation.Nonnull; - /** * This interface is intended to be implemented in order to supply implementation * strategy details to the {@link BalpAuditCaptureInterceptor}. @@ -95,7 +94,7 @@ public interface IBalpAuditContextServices { @Nonnull RequestDetails theRequestDetails, @Nonnull IBaseResource theResource, @Nonnull IIdType theResourceId) { - String serverBaseUrl = theRequestDetails.getServerBaseForRequest(); + String serverBaseUrl = theRequestDetails.getFhirServerBase(); String resourceName = theResourceId.getResourceType(); return theResourceId.withServerBase(serverBaseUrl, resourceName).getValue(); } diff --git a/hapi-fhir-storage/src/main/java/ca/uhn/fhir/subscription/api/IResourceModifiedMessagePersistenceSvc.java b/hapi-fhir-storage/src/main/java/ca/uhn/fhir/subscription/api/IResourceModifiedMessagePersistenceSvc.java index 68aad03a48c..5f54b04e544 100644 --- a/hapi-fhir-storage/src/main/java/ca/uhn/fhir/subscription/api/IResourceModifiedMessagePersistenceSvc.java +++ b/hapi-fhir-storage/src/main/java/ca/uhn/fhir/subscription/api/IResourceModifiedMessagePersistenceSvc.java @@ -25,6 +25,7 @@ import ca.uhn.fhir.jpa.model.entity.IPersistedResourceModifiedMessagePK; import ca.uhn.fhir.jpa.subscription.model.ResourceModifiedMessage; import java.util.List; +import java.util.Optional; /** * An implementer of this interface will provide {@link ResourceModifiedMessage} persistence services. @@ -61,10 +62,29 @@ public interface IResourceModifiedMessagePersistenceSvc { /** * Restore a resourceModifiedMessage to its pre persistence representation. * - * @param thePersistedResourceModifiedMessage The message needing restoration. + * @param theResourceModifiedMessage The message needing restoration. * @return The resourceModifiedMessage in its pre persistence form. */ - ResourceModifiedMessage inflatePersistedResourceModifiedMessage( + ResourceModifiedMessage inflatePersistedResourceModifiedMessage(ResourceModifiedMessage theResourceModifiedMessage); + + /** + * Restore a resourceModifiedMessage to its pre persistence representation or null if the resource does not exist. + * + * @param theResourceModifiedMessage + * @return An Optional containing The resourceModifiedMessage in its pre persistence form or null when the resource + * does not exist + */ + Optional inflatePersistedResourceModifiedMessageOrNull( + ResourceModifiedMessage theResourceModifiedMessage); + + /** + * Create a ResourceModifiedMessage without its pre persistence representation, i.e. without the resource body in + * payload + * + * @param thePersistedResourceModifiedMessage The message needing creation + * @return The resourceModifiedMessage without its pre persistence form + */ + ResourceModifiedMessage createResourceModifiedMessageFromEntityWithoutInflation( IPersistedResourceModifiedMessage thePersistedResourceModifiedMessage); /** diff --git a/hapi-fhir-storage/src/main/java/ca/uhn/fhir/util/ThreadPoolUtil.java b/hapi-fhir-storage/src/main/java/ca/uhn/fhir/util/ThreadPoolUtil.java index f049eac59a0..c6d5f3dfb57 100644 --- a/hapi-fhir-storage/src/main/java/ca/uhn/fhir/util/ThreadPoolUtil.java +++ b/hapi-fhir-storage/src/main/java/ca/uhn/fhir/util/ThreadPoolUtil.java @@ -20,12 +20,11 @@ package ca.uhn.fhir.util; import ca.uhn.fhir.jpa.search.reindex.BlockPolicy; +import jakarta.annotation.Nonnull; import org.apache.commons.lang3.Validate; import org.springframework.core.task.TaskDecorator; import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor; -import javax.annotation.Nonnull; - public final class ThreadPoolUtil { private ThreadPoolUtil() {} diff --git a/hapi-fhir-storage/src/test/java/ca/uhn/fhir/jpa/api/pid/AutoClosingStreamTemplateTest.java b/hapi-fhir-storage/src/test/java/ca/uhn/fhir/jpa/api/pid/AutoClosingStreamTemplateTest.java new file mode 100644 index 00000000000..3161decc75d --- /dev/null +++ b/hapi-fhir-storage/src/test/java/ca/uhn/fhir/jpa/api/pid/AutoClosingStreamTemplateTest.java @@ -0,0 +1,65 @@ +package ca.uhn.fhir.jpa.api.pid; + +import org.junit.jupiter.api.Test; + +import java.util.concurrent.atomic.AtomicBoolean; +import java.util.stream.Stream; + +import static org.junit.jupiter.api.Assertions.*; + +class AutoClosingStreamTemplateTest { + + @Test + void templatePassesStreamToCallback() { + // given + Stream concreteStream = Stream.of("one", "two"); + StreamTemplate streamTemplate = StreamTemplate.fromSupplier(() -> concreteStream); + + // when + streamTemplate.call(s -> { + assertSame(concreteStream, s); + return 0; + }); + } + + @Test + void templateClosesStreamOnExit() { + // given + AtomicBoolean wasClosed = new AtomicBoolean(false); + Stream concreteStream = Stream.of("one", "two") + .onClose(()->wasClosed.set(true)); + StreamTemplate streamTemplate = StreamTemplate.fromSupplier(() -> concreteStream); + + // when + streamTemplate.call(s -> { + // don't touch the stream; + return 0; + }); + + assertTrue(wasClosed.get(), "stream was closed"); + + } + + + @Test + void templateClosesStreamOnException() { + // given + AtomicBoolean wasClosed = new AtomicBoolean(false); + Stream concreteStream = Stream.of("one", "two") + .onClose(()->wasClosed.set(true)); + StreamTemplate streamTemplate = StreamTemplate.fromSupplier(() -> concreteStream); + + // when + try { + streamTemplate.call(s -> { + throw new RuntimeException("something failed"); + }); + } catch (RuntimeException e) { + // expected; + } + + assertTrue(wasClosed.get(), "stream was closed"); + + } + +} diff --git a/hapi-fhir-storage/src/test/java/ca/uhn/fhir/jpa/interceptor/validation/SearchParameterValidatingInterceptorTest.java b/hapi-fhir-storage/src/test/java/ca/uhn/fhir/jpa/interceptor/validation/SearchParameterValidatingInterceptorTest.java index 073a28d3e7a..398efb827b7 100644 --- a/hapi-fhir-storage/src/test/java/ca/uhn/fhir/jpa/interceptor/validation/SearchParameterValidatingInterceptorTest.java +++ b/hapi-fhir-storage/src/test/java/ca/uhn/fhir/jpa/interceptor/validation/SearchParameterValidatingInterceptorTest.java @@ -24,7 +24,7 @@ import org.junit.jupiter.api.extension.ExtendWith; import org.mockito.Mock; import org.mockito.junit.jupiter.MockitoExtension; -import javax.annotation.Nonnull; +import jakarta.annotation.Nonnull; import java.util.List; import java.util.Set; import java.util.concurrent.atomic.AtomicLong; diff --git a/hapi-fhir-storage/src/test/java/ca/uhn/fhir/jpa/subscription/channel/impl/LinkedBlockingChannelFactoryTest.java b/hapi-fhir-storage/src/test/java/ca/uhn/fhir/jpa/subscription/channel/impl/LinkedBlockingChannelFactoryTest.java index feef2646add..c23d19b10ed 100644 --- a/hapi-fhir-storage/src/test/java/ca/uhn/fhir/jpa/subscription/channel/impl/LinkedBlockingChannelFactoryTest.java +++ b/hapi-fhir-storage/src/test/java/ca/uhn/fhir/jpa/subscription/channel/impl/LinkedBlockingChannelFactoryTest.java @@ -12,7 +12,7 @@ import org.slf4j.LoggerFactory; import org.springframework.messaging.Message; import org.springframework.messaging.MessageHeaders; -import javax.annotation.Nonnull; +import jakarta.annotation.Nonnull; import java.util.ArrayList; import java.util.List; import java.util.concurrent.CountDownLatch; diff --git a/hapi-fhir-storage/src/test/java/ca/uhn/fhir/jpa/subscription/match/registry/SubscriptionCanonicalizerTest.java b/hapi-fhir-storage/src/test/java/ca/uhn/fhir/jpa/subscription/match/registry/SubscriptionCanonicalizerTest.java index b48e2ca5198..1c34a8e7e73 100644 --- a/hapi-fhir-storage/src/test/java/ca/uhn/fhir/jpa/subscription/match/registry/SubscriptionCanonicalizerTest.java +++ b/hapi-fhir-storage/src/test/java/ca/uhn/fhir/jpa/subscription/match/registry/SubscriptionCanonicalizerTest.java @@ -6,6 +6,7 @@ import ca.uhn.fhir.jpa.subscription.model.CanonicalSubscriptionChannelType; import ca.uhn.fhir.jpa.subscription.model.CanonicalTopicSubscriptionFilter; import ca.uhn.fhir.model.api.ExtensionDt; import ca.uhn.fhir.model.primitive.BooleanDt; +import ca.uhn.fhir.subscription.SubscriptionConstants; import ca.uhn.fhir.subscription.SubscriptionTestDataHelper; import org.hl7.fhir.r4.model.BooleanType; import org.hl7.fhir.r4.model.Extension; @@ -14,6 +15,8 @@ import org.hl7.fhir.r5.model.Coding; import org.hl7.fhir.r5.model.Enumerations; import org.jetbrains.annotations.NotNull; import org.junit.jupiter.api.Test; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.ValueSource; import static ca.uhn.fhir.rest.api.Constants.CT_FHIR_JSON_NEW; import static ca.uhn.fhir.util.HapiExtensions.EX_SEND_DELETE_MESSAGES; @@ -83,16 +86,12 @@ class SubscriptionCanonicalizerTest { assertTrue(canonicalize.getSendDeleteMessages()); } - @Test - public void testR5() { - // setup - SubscriptionCanonicalizer r5Canonicalizer = new SubscriptionCanonicalizer(FhirContext.forR5Cached()); + private org.hl7.fhir.r5.model.Subscription buildR5Subscription(org.hl7.fhir.r5.model.Subscription.SubscriptionPayloadContent thePayloadContent) { org.hl7.fhir.r5.model.Subscription subscription = new org.hl7.fhir.r5.model.Subscription(); subscription.setStatus(Enumerations.SubscriptionStatusCodes.ACTIVE); subscription.setContentType(CT_FHIR_JSON_NEW); - // WIP STR5 support different content types - subscription.setContent(org.hl7.fhir.r5.model.Subscription.SubscriptionPayloadContent.FULLRESOURCE); + subscription.setContent(thePayloadContent); subscription.setEndpoint("http://foo"); subscription.setTopic(SubscriptionTestDataHelper.TEST_TOPIC); Coding channelType = new Coding().setSystem("http://terminology.hl7.org/CodeSystem/subscription-channel-type").setCode("rest-hook"); @@ -102,13 +101,25 @@ class SubscriptionCanonicalizerTest { subscription.setHeartbeatPeriod(123); subscription.setMaxCount(456); + return subscription; + } + + @ParameterizedTest + @ValueSource(strings = {"full-resource", "id-only", "empty"}) + public void testR5Canonicalize_returnsCorrectCanonicalSubscription(String thePayloadContent) { + // setup + SubscriptionCanonicalizer r5Canonicalizer = new SubscriptionCanonicalizer(FhirContext.forR5Cached()); + org.hl7.fhir.r5.model.Subscription.SubscriptionPayloadContent payloadContent = + org.hl7.fhir.r5.model.Subscription.SubscriptionPayloadContent.fromCode(thePayloadContent); + org.hl7.fhir.r5.model.Subscription subscription = buildR5Subscription(payloadContent); + // execute CanonicalSubscription canonical = r5Canonicalizer.canonicalize(subscription); // verify assertEquals(Subscription.SubscriptionStatus.ACTIVE, canonical.getStatus()); assertEquals(CT_FHIR_JSON_NEW, canonical.getContentType()); - assertEquals(org.hl7.fhir.r5.model.Subscription.SubscriptionPayloadContent.FULLRESOURCE, canonical.getContent()); + assertEquals(payloadContent, canonical.getContent()); assertEquals("http://foo", canonical.getEndpointUrl()); assertEquals(SubscriptionTestDataHelper.TEST_TOPIC, canonical.getTopic()); assertEquals(CanonicalSubscriptionChannelType.RESTHOOK, canonical.getChannelType()); @@ -131,37 +142,72 @@ class SubscriptionCanonicalizerTest { assertEquals(456, canonical.getMaxCount()); } - @Test - void testR4Backport() { + @ParameterizedTest + @ValueSource(strings = {"full-resource", "id-only", "empty"}) + void testR4BCanonicalize_returnsCorrectCanonicalSubscription(String thePayloadContent) { + // Example drawn from http://build.fhir.org/ig/HL7/fhir-subscription-backport-ig/Subscription-subscription-zulip.json.html + + // setup + SubscriptionCanonicalizer r4bCanonicalizer = new SubscriptionCanonicalizer(FhirContext.forR4BCached()); + org.hl7.fhir.r4b.model.Subscription subscription = buildR4BSubscription(thePayloadContent); + + // execute + CanonicalSubscription canonical = r4bCanonicalizer.canonicalize(subscription); + + // verify + assertEquals(Subscription.SubscriptionStatus.ACTIVE, canonical.getStatus()); + verifyStandardSubscriptionParameters(canonical); + verifyChannelParameters(canonical, thePayloadContent); + } + + private org.hl7.fhir.r4b.model.Subscription buildR4BSubscription(String thePayloadContent) { + org.hl7.fhir.r4b.model.Subscription subscription = new org.hl7.fhir.r4b.model.Subscription(); + + subscription.setId("testId"); + subscription.getMeta().addTag("http://a", "b", "c"); + subscription.getMeta().addTag("http://d", "e", "f"); + subscription.setStatus(org.hl7.fhir.r4b.model.Enumerations.SubscriptionStatus.ACTIVE); + subscription.getChannel().setPayload(CT_FHIR_JSON_NEW); + subscription.getChannel().setType(org.hl7.fhir.r4b.model.Subscription.SubscriptionChannelType.RESTHOOK); + subscription.getChannel().setEndpoint(SubscriptionTestDataHelper.TEST_ENDPOINT); + + subscription.getMeta().addProfile(SubscriptionConstants.SUBSCRIPTION_TOPIC_PROFILE_URL); + subscription.setCriteria(SubscriptionTestDataHelper.TEST_TOPIC); + + subscription.getChannel().setPayload(CT_FHIR_JSON_NEW); + subscription.getChannel().addHeader(SubscriptionTestDataHelper.TEST_HEADER1); + subscription.getChannel().addHeader(SubscriptionTestDataHelper.TEST_HEADER2); + subscription.setStatus(org.hl7.fhir.r4b.model.Enumerations.SubscriptionStatus.ACTIVE); + + subscription + .getChannel() + .getPayloadElement() + .addExtension( + SubscriptionConstants.SUBSCRIPTION_TOPIC_CHANNEL_PAYLOAD_CONTENT, + new org.hl7.fhir.r4b.model.CodeType(thePayloadContent)); + + return subscription; + } + + @ParameterizedTest + @ValueSource(strings = {"full-resource", "id-only", "empty"}) + void testR4canonicalize_withBackPortedSubscription_returnsCorrectCanonicalSubscription(String thePayloadContent) { // Example drawn from http://build.fhir.org/ig/HL7/fhir-subscription-backport-ig/Subscription-subscription-zulip.json.html // setup SubscriptionCanonicalizer r4Canonicalizer = new SubscriptionCanonicalizer(FhirContext.forR4Cached()); // execute - - CanonicalSubscription canonical = r4Canonicalizer.canonicalize(SubscriptionTestDataHelper.buildR4TopicSubscription()); + Subscription subscription = SubscriptionTestDataHelper.buildR4TopicSubscriptionWithContent(thePayloadContent); + CanonicalSubscription canonical = r4Canonicalizer.canonicalize(subscription); // verify // Standard R4 stuff - assertEquals(2, canonical.getTags().size()); - assertEquals("b", canonical.getTags().get("http://a")); - assertEquals("e", canonical.getTags().get("http://d")); - assertEquals("testId", canonical.getIdPart()); - assertEquals("testId", canonical.getIdElementString()); - assertEquals(SubscriptionTestDataHelper.TEST_ENDPOINT, canonical.getEndpointUrl()); - assertEquals(CT_FHIR_JSON_NEW, canonical.getContentType()); - assertThat(canonical.getHeaders(), hasSize(2)); - assertEquals(SubscriptionTestDataHelper.TEST_HEADER1, canonical.getHeaders().get(0)); - assertEquals(SubscriptionTestDataHelper.TEST_HEADER2, canonical.getHeaders().get(1)); + verifyStandardSubscriptionParameters(canonical); assertEquals(Subscription.SubscriptionStatus.ACTIVE, canonical.getStatus()); + verifyChannelParameters(canonical, thePayloadContent); - assertEquals(CT_FHIR_JSON_NEW, canonical.getContentType()); - assertEquals(org.hl7.fhir.r5.model.Subscription.SubscriptionPayloadContent.FULLRESOURCE, canonical.getContent()); - assertEquals(SubscriptionTestDataHelper.TEST_ENDPOINT, canonical.getEndpointUrl()); - assertEquals(SubscriptionTestDataHelper.TEST_TOPIC, canonical.getTopic()); - assertEquals(CanonicalSubscriptionChannelType.RESTHOOK, canonical.getChannelType()); assertThat(canonical.getFilters(), hasSize(2)); CanonicalTopicSubscriptionFilter filter1 = canonical.getFilters().get(0); @@ -183,6 +229,26 @@ class SubscriptionCanonicalizerTest { assertEquals(20, canonical.getMaxCount()); } + private void verifyChannelParameters(CanonicalSubscription theCanonicalSubscriptions, String thePayloadContent) { + assertThat(theCanonicalSubscriptions.getHeaders(), hasSize(2)); + assertEquals(SubscriptionTestDataHelper.TEST_HEADER1, theCanonicalSubscriptions.getHeaders().get(0)); + assertEquals(SubscriptionTestDataHelper.TEST_HEADER2, theCanonicalSubscriptions.getHeaders().get(1)); + + assertEquals(CT_FHIR_JSON_NEW, theCanonicalSubscriptions.getContentType()); + assertEquals(thePayloadContent, theCanonicalSubscriptions.getContent().toCode()); + assertEquals(SubscriptionTestDataHelper.TEST_ENDPOINT, theCanonicalSubscriptions.getEndpointUrl()); + assertEquals(SubscriptionTestDataHelper.TEST_TOPIC, theCanonicalSubscriptions.getTopic()); + assertEquals(CanonicalSubscriptionChannelType.RESTHOOK, theCanonicalSubscriptions.getChannelType()); + } + + private void verifyStandardSubscriptionParameters(CanonicalSubscription theCanonicalSubscription) { + assertEquals(2, theCanonicalSubscription.getTags().size()); + assertEquals("b", theCanonicalSubscription.getTags().get("http://a")); + assertEquals("e", theCanonicalSubscription.getTags().get("http://d")); + assertEquals("testId", theCanonicalSubscription.getIdPart()); + assertEquals("testId", theCanonicalSubscription.getIdElementString()); + } + @NotNull private static org.hl7.fhir.r5.model.Subscription.SubscriptionFilterByComponent buildFilter(String theResourceType, String theParam, String theValue) { org.hl7.fhir.r5.model.Subscription.SubscriptionFilterByComponent filter = new org.hl7.fhir.r5.model.Subscription.SubscriptionFilterByComponent(); diff --git a/hapi-fhir-storage/src/test/java/ca/uhn/fhir/jpa/validation/ValidatorResourceFetcherTest.java b/hapi-fhir-storage/src/test/java/ca/uhn/fhir/jpa/validation/ValidatorResourceFetcherTest.java new file mode 100644 index 00000000000..9009f6137fa --- /dev/null +++ b/hapi-fhir-storage/src/test/java/ca/uhn/fhir/jpa/validation/ValidatorResourceFetcherTest.java @@ -0,0 +1,65 @@ +package ca.uhn.fhir.jpa.validation; + +import ca.uhn.fhir.context.FhirContext; +import ca.uhn.fhir.context.support.DefaultProfileValidationSupport; +import ca.uhn.fhir.jpa.api.dao.DaoRegistry; +import ca.uhn.fhir.jpa.api.dao.IFhirResourceDao; +import ca.uhn.fhir.rest.api.server.RequestDetails; +import ca.uhn.fhir.rest.api.server.SystemRequestDetails; +import ca.uhn.fhir.rest.server.SimpleBundleProvider; +import ca.uhn.fhir.rest.server.exceptions.ResourceNotFoundException; +import ca.uhn.fhir.test.BaseTest; +import ca.uhn.fhir.util.ClasspathUtil; +import org.hl7.fhir.common.hapi.validation.validator.FhirInstanceValidator; +import org.hl7.fhir.common.hapi.validation.validator.VersionSpecificWorkerContextWrapper; +import org.hl7.fhir.instance.model.api.IBaseResource; +import org.hl7.fhir.r5.elementmodel.Element; +import org.hl7.fhir.r5.utils.XVerExtensionManager; +import org.hl7.fhir.validation.instance.InstanceValidator; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; + +import java.util.List; + +import static org.junit.jupiter.api.Assertions.assertNotNull; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.Mockito.doReturn; +import static org.mockito.Mockito.doThrow; +import static org.mockito.Mockito.mock; + + +public class ValidatorResourceFetcherTest extends BaseTest { + private static final FhirContext ourCtx = FhirContext.forR4(); + private static final DefaultProfileValidationSupport myDefaultValidationSupport = new DefaultProfileValidationSupport(ourCtx); + private static ValidatorResourceFetcher fetcher; + private static DaoRegistry mockDaoRegistry; + private static IFhirResourceDao mockResourceDao; + + @SuppressWarnings("unchecked") + @BeforeEach + public void before() { + mockDaoRegistry = mock(DaoRegistry.class); + mockResourceDao = mock(IFhirResourceDao.class); + fetcher = new ValidatorResourceFetcher(ourCtx, myDefaultValidationSupport, mockDaoRegistry); + } + + @Test + public void checkFetchByUrl() { + // setup mocks + String resource = ClasspathUtil.loadResource("/q_jon_with_url_version.json"); + doReturn(mockResourceDao).when(mockDaoRegistry).getResourceDao("Questionnaire"); + doThrow(new ResourceNotFoundException("Not Found")).when(mockResourceDao).read(any(),any()); + doReturn(new SimpleBundleProvider(List.of( + ourCtx.newJsonParser().parseResource(resource) + ))).when(mockResourceDao).search(any(),any()); + VersionSpecificWorkerContextWrapper wrappedWorkerContext = VersionSpecificWorkerContextWrapper.newVersionSpecificWorkerContextWrapper(myDefaultValidationSupport); + InstanceValidator v = new InstanceValidator( + wrappedWorkerContext, + new FhirInstanceValidator.NullEvaluationContext(), + new XVerExtensionManager(null)); + RequestDetails r = new SystemRequestDetails(); + // test + Element returnedResource = fetcher.fetch(v, r,"http://www.test-url-for-questionnaire.com/Questionnaire/test-id|1.0.0"); + assertNotNull(returnedResource); + } +} diff --git a/hapi-fhir-storage/src/test/java/ca/uhn/fhir/rest/server/method/ResponseBundleBuilderTest.java b/hapi-fhir-storage/src/test/java/ca/uhn/fhir/rest/server/method/ResponseBundleBuilderTest.java index 9c2ecc222d6..4150a04d9e7 100644 --- a/hapi-fhir-storage/src/test/java/ca/uhn/fhir/rest/server/method/ResponseBundleBuilderTest.java +++ b/hapi-fhir-storage/src/test/java/ca/uhn/fhir/rest/server/method/ResponseBundleBuilderTest.java @@ -26,7 +26,7 @@ import org.mockito.junit.jupiter.MockitoExtension; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import javax.annotation.Nonnull; +import jakarta.annotation.Nonnull; import java.util.ArrayList; import java.util.Collections; import java.util.List; diff --git a/hapi-fhir-storage/src/test/java/ca/uhn/fhir/storage/interceptor/balp/BalpAuditCaptureInterceptorTest.java b/hapi-fhir-storage/src/test/java/ca/uhn/fhir/storage/interceptor/balp/BalpAuditCaptureInterceptorTest.java index 141d3f72b89..d08642b77c7 100644 --- a/hapi-fhir-storage/src/test/java/ca/uhn/fhir/storage/interceptor/balp/BalpAuditCaptureInterceptorTest.java +++ b/hapi-fhir-storage/src/test/java/ca/uhn/fhir/storage/interceptor/balp/BalpAuditCaptureInterceptorTest.java @@ -31,12 +31,17 @@ import org.mockito.Captor; import org.mockito.Mock; import org.mockito.junit.jupiter.MockitoExtension; -import javax.annotation.Nonnull; +import jakarta.annotation.Nonnull; import java.io.IOException; import java.nio.charset.StandardCharsets; +import java.util.ArrayList; import java.util.Arrays; import java.util.List; import java.util.Set; +import java.util.concurrent.ExecutionException; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; +import java.util.concurrent.Future; import java.util.stream.Collectors; import static ca.uhn.fhir.storage.interceptor.balp.BalpConstants.*; @@ -547,7 +552,7 @@ public class BalpAuditCaptureInterceptorTest implements ITestDataBuilder { assertEquals(AuditEvent.AuditEventOutcome._0, auditEvent.getOutcome()); assertHasPatientEntities(auditEvent, "Patient/P1"); assertQuery(auditEvent, ourServer.getBaseUrl() + "/Observation?subject=Patient%2FP1"); - assertQueryDescription(auditEvent, "GET /Observation?subject=Patient%2FP1"); + assertQueryDescription(auditEvent, "GET " + ourServer.getBaseUrl() + "/Observation?subject=Patient%2FP1"); } @Test @@ -584,11 +589,11 @@ public class BalpAuditCaptureInterceptorTest implements ITestDataBuilder { assertEquals(AuditEvent.AuditEventOutcome._0, auditEvent.getOutcome()); assertHasPatientEntities(auditEvent); assertQuery(auditEvent, ourServer.getBaseUrl() + "/CodeSystem"); - assertQueryDescription(auditEvent, "GET /CodeSystem"); + assertQueryDescription(auditEvent, "GET " + ourServer.getBaseUrl() + "/CodeSystem"); } @Test - public void testSearch_ResponseIncludesSinglePatientCompartment_LoadPageTwo() { + public void testSearch_ResponseIncludesSinglePatientCompartment_LoadPageTwo() throws ExecutionException, InterruptedException { // Setup create10Observations("Patient/P1"); @@ -624,7 +629,7 @@ public class BalpAuditCaptureInterceptorTest implements ITestDataBuilder { assertEquals(AuditEvent.AuditEventOutcome._0, auditEvent.getOutcome()); assertHasPatientEntities(auditEvent, "Patient/P1"); assertQuery(auditEvent, ourServer.getBaseUrl() + "/Observation?_count=5&subject=Patient%2FP1"); - assertQueryDescription(auditEvent, "GET /Observation?subject=Patient%2FP1&_count=5"); + assertQueryDescription(auditEvent, "GET " + ourServer.getBaseUrl() + "/Observation?subject=Patient%2FP1&_count=5"); auditEvent = myAuditEventCaptor.getAllValues().get(1); ourLog.info("Audit Event: {}", ourCtx.newJsonParser().setPrettyPrint(true).encodeResourceToString(auditEvent)); @@ -669,7 +674,7 @@ public class BalpAuditCaptureInterceptorTest implements ITestDataBuilder { assertEquals(AuditEvent.AuditEventOutcome._0, auditEvent.getOutcome()); assertHasPatientEntities(auditEvent, "Patient/P1"); assertQuery(auditEvent, ourServer.getBaseUrl() + "/Observation/_search?subject=Patient%2FP1"); - assertQueryDescription(auditEvent, "POST /Observation/_search"); + assertQueryDescription(auditEvent, "POST " + ourServer.getBaseUrl() + "/Observation/_search"); } @Test @@ -704,7 +709,7 @@ public class BalpAuditCaptureInterceptorTest implements ITestDataBuilder { assertEquals(AuditEvent.AuditEventOutcome._0, auditEvent.getOutcome()); assertHasPatientEntities(auditEvent, "Patient/P1"); assertQuery(auditEvent, ourServer.getBaseUrl() + "/Observation/_search?subject=Patient%2FP1"); - assertQueryDescription(auditEvent, "GET /Observation/_search?subject=Patient%2FP1"); + assertQueryDescription(auditEvent, "GET " + ourServer.getBaseUrl() + "/Observation/_search?subject=Patient%2FP1"); } @Test diff --git a/hapi-fhir-storage/src/test/resources/q_jon_with_url_version.json b/hapi-fhir-storage/src/test/resources/q_jon_with_url_version.json new file mode 100644 index 00000000000..7ca33e5e290 --- /dev/null +++ b/hapi-fhir-storage/src/test/resources/q_jon_with_url_version.json @@ -0,0 +1,1388 @@ +{ + "resourceType": "Questionnaire", + "status": "draft", + "date": "2015-10-29", + "publisher": "Cancer Care Ontario", + "id": "test-id", + "telecom": [ + { + "system": "email", + "value": "jon.zammit@cancercare.on.ca" + } + ], + "title": "CT Lung for Cancer Staging Template - DRAFT (Short Version)", + "url": "http://www.test-url-for-questionnaire.com/Questionnaire/test-id", + "version": "1.0.0", + "item": [ + { + "text": "Patient with high suspicion of cancer as per the PEBC document (EBS #24-2) or radiological/laboratory tests suggesting cancer. Excluding: patients with synchronous lung primary, previous diagnosis of lung cancer, lung cancer surgery or therapy. New single lung primary only.", + "type": "display" + }, + { + "linkId": "root", + "type": "group", + "required": true, + "item": [ + { + "linkId": "g1", + "concept": [ + { + "system": "http://loinc.org", + "code": "55752-0", + "display": "Clinical Information" + } + ], + "text": "CLINICAL INFORMATION", + "type": "group", + "item": [ + { + "extension": [ + { + "url": "http://hl7.org/fhir/StructureDefinition/questionnaire-label", + "valueString": "1.1" + } + ], + "linkId": "1.1", + "text": "Patient Clinical Information", + "type": "text" + }, + { + "extension": [ + { + "url": "http://hl7.org/fhir/StructureDefinition/questionnaire-label", + "valueString": "1.2" + } + ], + "linkId": "1.2", + "text": "Previous Examination (Date and Modality)", + "type": "text" + } + ] + }, + { + "linkId": "g2", + "text": "IMAGING PROCEDURE DESCRIPTION", + "type": "group", + "item": [ + { + "extension": [ + { + "url": "http://hl7.org/fhir/StructureDefinition/questionnaire-label", + "valueString": "2.1" + } + ], + "linkId": "2.1", + "text": "Overall Image Quality:", + "type": "choice", + "option": [ + { + "valueCoding": { + "code": "2.1a", + "display": "Adequate" + } + }, + { + "valueCoding": { + "code": "2.1b", + "display": "Suboptimal" + } + }, + { + "valueCoding": { + "code": "2.1c", + "display": "Non-diagnostic" + } + } + ] + }, + { + "extension": [ + { + "url": "http://hl7.org/fhir/StructureDefinition/questionnaire-label", + "valueString": "2.2" + } + ], + "linkId": "2.2", + "text": "Intravenous Contrast Used?", + "type": "boolean" + }, + { + "extension": [ + { + "url": "http://hl7.org/fhir/StructureDefinition/questionnaire-label", + "valueString": "2.3" + } + ], + "linkId": "2.3", + "text": "Additional Comments", + "type": "text" + } + ] + }, + { + "linkId": "g3", + "text": "FINDINGS", + "type": "group", + "item": [ + { + "extension": [ + { + "url": "http://hl7.org/fhir/StructureDefinition/questionnaire-label", + "valueString": "3" + } + ], + "linkId": "g3.0", + "text": "T Category", + "type": "group", + "item": [ + { + "extension": [ + { + "url": "http://hl7.org/fhir/StructureDefinition/questionnaire-label", + "valueString": "3.1" + } + ], + "linkId": "g3.1", + "text": "Location of Main Nodule/Mass (Primary tumor, or Reference tumor)", + "type": "group", + "item": [ + { + "extension": [ + { + "url": "http://hl7.org/fhir/StructureDefinition/questionnaire-label", + "valueString": "3.1.1" + } + ], + "linkId": "q3.1.1", + "text": "Location of main nodule/mass:", + "type": "choice", + "option": [ + { + "valueCoding": { + "code": "3.1.1a", + "display": "Peripheral" + } + }, + { + "valueCoding": { + "code": "3.1.1b", + "display": "Central*" + } + }, + { + "valueCoding": { + "code": "3.1.1c", + "display": "Both*" + } + } + ], + "item": [ + { + "extension": [ + { + "url": "http://hl7.org/fhir/StructureDefinition/questionnaire-enableWhen", + "extension": [ + { + "url": "#question", + "valueString": "q3.1.1" + }, + { + "url": "#answer", + "valueCoding": { + "code": "3.1.1b" + } + } + ] + }, + { + "url": "http://hl7.org/fhir/StructureDefinition/questionnaire-enableWhen", + "extension": [ + { + "url": "#question", + "valueString": "q3.1.1" + }, + { + "url": "#answer", + "valueCoding": { + "code": "3.1.1c" + } + } + ] + } + ], + "linkId": "g3.1.1i", + "type": "group", + "item": [ + { + "extension": [ + { + "url": "http://hl7.org/fhir/StructureDefinition/questionnaire-label", + "valueString": "i)" + } + ], + "linkId": "q3.1.1i", + "text": "What is the distance of the nodule/mass to the carina?", + "type": "integer", + "item": [ + { + "extension": [ + { + "url": "http://hl7.org/fhir/StructureDefinition/questionnaire-itemControl", + "valueCodeableConcept": { + "coding": [ + { + "system": "http://hl7.org/fhir/ValueSet/questionnaire-item-control", + "code": "unit" + } + ] + } + } + ], + "text": "mm", + "type": "display" + } + ] + }, + { + "linkId": "3.1.1i.image", + "text": "image", + "type": "string" + }, + { + "linkId": "3.1.1i.series", + "text": "series", + "type": "string" + } + ] + } + ] + }, + { + "extension": [ + { + "url": "http://hl7.org/fhir/StructureDefinition/questionnaire-label", + "valueString": "3.1.2" + } + ], + "linkId": "3.1.2", + "text": "State the lobe(s) and segment(s) where the nodule/mass is located", + "type": "text" + } + ] + }, + { + "extension": [ + { + "url": "http://hl7.org/fhir/StructureDefinition/questionnaire-label", + "valueString": "3.2" + } + ], + "linkId": "g3.2", + "text": "Size and characteristics of main nodule/mass", + "type": "group", + "item": [ + { + "extension": [ + { + "url": "http://hl7.org/fhir/StructureDefinition/questionnaire-label", + "valueString": "3.2.1" + } + ], + "linkId": "3.2.1", + "text": "Size of the nodule/mass:", + "type": "choice", + "option": [ + { + "valueCoding": { + "code": "3.2.1a", + "display": "Solid nodule/mass" + } + }, + { + "valueCoding": { + "code": "3.2.1b", + "display": "Part-solid nodule/mass" + } + }, + { + "valueCoding": { + "code": "3.2.1c", + "display": "Pure Ground glass" + } + } + ], + "item": [ + { + "linkId": "g3.2.1", + "type": "group", + "item": [ + { + "extension": [ + { + "url": "http://hl7.org/fhir/StructureDefinition/questionnaire-enableWhen", + "extension": [ + { + "url": "#question", + "valueString": "3.2.1" + }, + { + "url": "#answer", + "valueCoding": { + "code": "3.2.1a" + } + } + ] + } + ], + "linkId": "g3.2.1a", + "type": "group", + "item": [ + { + "linkId": "3.2.1a", + "text": "largest dimension:", + "type": "integer", + "item": [ + { + "extension": [ + { + "url": "http://hl7.org/fhir/StructureDefinition/questionnaire-itemControl", + "valueCodeableConcept": { + "coding": [ + { + "system": "http://hl7.org/fhir/ValueSet/questionnaire-item-control", + "code": "unit" + } + ] + } + } + ], + "text": "mm", + "type": "display" + } + ] + }, + { + "linkId": "3.2.1a.image", + "text": "image", + "type": "string" + }, + { + "linkId": "3.2.1a.series", + "text": "series", + "type": "string" + } + ] + } + ] + } + ] + }, + { + "extension": [ + { + "url": "http://hl7.org/fhir/StructureDefinition/questionnaire-label", + "valueString": "3.2.2" + } + ], + "linkId": "3.2.2", + "text": "Plane in which the mass was measured:", + "type": "choice", + "option": [ + { + "code": "3.2.2a", + "display": "Axial" + }, + { + "code": "3.2.2b", + "display": "Coronal" + }, + { + "code": "3.2.2c", + "display": "Sagittal" + } + ] + }, + { + "extension": [ + { + "url": "http://hl7.org/fhir/StructureDefinition/questionnaire-label", + "valueString": "3.2.3" + } + ], + "linkId": "3.2.3", + "text": "Other characteristics of the main nodule/mass:", + "type": "text" + } + ] + }, + { + "extension": [ + { + "url": "http://hl7.org/fhir/StructureDefinition/questionnaire-label", + "valueString": "3.3" + } + ], + "linkId": "g3.3", + "text": "Structures directly involved", + "type": "group", + "item": [ + { + "extension": [ + { + "url": "http://hl7.org/fhir/StructureDefinition/questionnaire-label", + "valueString": "3.3.1" + } + ], + "linkId": "3.3.1", + "text": "State if there is bronchial involvement:", + "type": "choice", + "option": [ + { + "code": "3.3.1a", + "display": "*Yes" + }, + { + "code": "3.3.1b", + "display": "No" + }, + { + "code": "3.3.1c", + "display": "Uncertain" + } + ], + "item": [ + { + "extension": [ + { + "url": "http://hl7.org/fhir/StructureDefinition/questionnaire-displayCategory", + "valueCodeableConcept": { + "coding": [ + { + "system": "http://hl7.org/fhir/ValueSet/questionnaire-display-category", + "code": "instructions" + } + ] + } + } + ], + "text": "*If yes, answer i and ii.", + "type": "display" + }, + { + "linkId": "g3.3.1", + "type": "group", + "item": [ + { + "extension": [ + { + "url": "http://hl7.org/fhir/StructureDefinition/questionnaire-enableWhen", + "extension": [ + { + "url": "#question", + "valueString": "3.3.1" + }, + { + "url": "#answer", + "valueCoding": { + "code": "3.3.1a" + } + } + ] + } + ], + "linkId": "g3.3.1.yes", + "type": "group", + "item": [ + { + "extension": [ + { + "url": "http://hl7.org/fhir/StructureDefinition/questionnaire-label", + "valueString": "i)" + } + ], + "linkId": "3.3.1i", + "text": "Type of involvement:", + "type": "text" + }, + { + "extension": [ + { + "url": "http://hl7.org/fhir/StructureDefinition/questionnaire-label", + "valueString": "ii)" + } + ], + "linkId": "3.3.1ii", + "text": "State if there is endobronchial involvement:", + "type": "choice", + "option": [ + { + "code": "3.3.1.iia", + "display": "*Yes" + }, + { + "code": "3.3.1.iib", + "display": "No" + }, + { + "code": "3.3.1.iic", + "display": "Uncertain" + } + ] + } + ] + }, + { + "extension": [ + { + "url": "http://hl7.org/fhir/StructureDefinition/questionnaire-enableWhen", + "extension": [ + { + "url": "#question", + "valueString": "3.3.1ii" + }, + { + "url": "#answer", + "valueCoding": { + "code": "3.3.1.iia" + } + } + ] + } + ], + "linkId": "3.3.1iic", + "text": "Describe:", + "type": "text" + } + ] + } + ] + }, + { + "extension": [ + { + "url": "http://hl7.org/fhir/StructureDefinition/questionnaire-label", + "valueString": "3.3.2" + } + ], + "linkId": "3.3.2", + "text": "Is there direct involvement of any other anatomical structures?", + "type": "boolean", + "item": [ + { + "extension": [ + { + "url": "http://hl7.org/fhir/StructureDefinition/questionnaire-displayCategory", + "valueCodeableConcept": { + "coding": [ + { + "system": "http://hl7.org/fhir/ValueSet/questionnaire-display-category", + "code": "instructions" + } + ] + } + } + ], + "text": "*If yes, indicate and describe all ipsilateral anatomical structures involved by the mass. If no, go to 3.3.3.", + "type": "display" + }, + { + "linkId": "g3.3.2.yes", + "type": "group", + "item": [ + { + "extension": [ + { + "url": "http://hl7.org/fhir/StructureDefinition/questionnaire-enableWhen", + "extension": [ + { + "url": "#question", + "valueString": "3.3.2" + }, + { + "url": "#answer", + "valueBoolean": true + } + ] + } + ], + "linkId": "3.3.2.Yes", + "text": "Indicate and describe all ipsilateral anatomical structures involved by the mass:", + "type": "choice", + "repeats": true, + "option": [ + { + "code": "3.3.2.Yes.a", + "display": "Pleura" + }, + { + "code": "3.3.2.Yes.b", + "display": "Brachial Plexus" + }, + { + "code": "3.3.2.Yes.c", + "display": "Diaphragm" + }, + { + "code": "3.3.2.Yes.d", + "display": "Mediastinal fat" + }, + { + "code": "3.3.2.Yes.e", + "display": "Aorta" + }, + { + "code": "3.3.2.Yes.f", + "display": "Pulmonary Vessel" + }, + { + "code": "3.3.2.Yes.g", + "display": "Pericardium or Heart" + }, + { + "code": "3.3.2.Yes.h", + "display": "Mediastinal Vessels (including SVC)" + }, + { + "code": "3.3.2.Yes.i", + "display": "Trachea/carina" + }, + { + "code": "3.3.2.Yes.j", + "display": "Esophagus" + }, + { + "code": "3.3.2.Yes.k", + "display": "Trachea esophageal groove" + }, + { + "code": "3.3.2.Yes.l", + "display": "Vertebral Body" + }, + { + "code": "3.3.2.Yes.m", + "display": "Chest wall and Ribs" + }, + { + "code": "3.3.2.Yes.n", + "display": "Other structures" + } + ], + "item": [ + { + "extension": [ + { + "url": "http://hl7.org/fhir/StructureDefinition/questionnaire-enableWhen", + "extension": [ + { + "url": "#question", + "valueString": "3.3.2.Yes" + }, + { + "url": "#answer", + "valueCoding": { + "code": "3.3.2.Yes.a" + } + } + ] + } + ], + "linkId": "3.3.2.Yes.a.text", + "text": "Pleura, description:", + "type": "string" + } + ] + } + ] + } + ] + }, + { + "extension": [ + { + "url": "http://hl7.org/fhir/StructureDefinition/questionnaire-label", + "valueString": "3.3.3" + } + ], + "linkId": "3.3.3", + "text": "Are there additional suspicious pulmonary nodules?", + "type": "boolean", + "item": [ + { + "extension": [ + { + "url": "http://hl7.org/fhir/StructureDefinition/questionnaire-displayCategory", + "valueCodeableConcept": { + "coding": [ + { + "system": "http://hl7.org/fhir/ValueSet/questionnaire-display-category", + "code": "instructions" + } + ] + } + } + ], + "text": "*If yes, indicate and describe pulmonary nodules which apply. If no, go to 3.3.4.", + "type": "display" + }, + { + "linkId": "g3.3.3.yes", + "type": "group", + "item": [ + { + "extension": [ + { + "url": "http://hl7.org/fhir/StructureDefinition/questionnaire-enableWhen", + "extension": [ + { + "url": "#question", + "valueString": "3.3.3" + }, + { + "url": "#answer", + "valueBoolean": true + } + ] + } + ], + "linkId": "3.3.3.Yes", + "text": "Indicate and describe pulmonary nodules which apply:", + "type": "choice", + "option": [ + { + "code": "3.3.3.Yes.a", + "display": "In the same lobe" + }, + { + "code": "3.3.3.Yes.b", + "display": "In a different lobe, same lung" + }, + { + "code": "3.3.3.Yes.c", + "display": "In the opposite lung (M1a)" + } + ], + "item": [ + { + "extension": [ + { + "url": "http://hl7.org/fhir/StructureDefinition/questionnaire-enableWhen", + "extension": [ + { + "url": "#question", + "valueString": "3.3.3.Yes" + }, + { + "url": "#answer", + "valueCoding": { + "code": "3.3.3.Yes.a" + } + } + ] + } + ], + "linkId": "3.3.3.Yes.a.text", + "text": "In the same lobe, description:", + "type": "string" + } + ] + } + ] + } + ] + }, + { + "extension": [ + { + "url": "http://hl7.org/fhir/StructureDefinition/questionnaire-label", + "valueString": "3.3.4" + } + ], + "linkId": "3.3.4", + "text": "Other notable intrathoracic findings (eg lymphangitis carcinomatosis):", + "type": "text" + } + ] + } + ] + }, + { + "extension": [ + { + "url": "http://hl7.org/fhir/StructureDefinition/questionnaire-label", + "valueString": "4" + } + ], + "linkId": "4.0", + "text": "N Category", + "type": "group", + "item": [ + { + "extension": [ + { + "url": "http://hl7.org/fhir/StructureDefinition/questionnaire-label", + "valueString": "4.1" + } + ], + "linkId": "4.1", + "text": "Are there enlarged lymph nodes?", + "type": "boolean", + "item": [ + { + "extension": [ + { + "url": "http://hl7.org/fhir/StructureDefinition/questionnaire-displayCategory", + "valueCodeableConcept": { + "coding": [ + { + "system": "http://hl7.org/fhir/ValueSet/questionnaire-display-category", + "code": "instructions" + } + ] + } + } + ], + "text": "*If yes, indicate and describe the nodes which apply. If no, go to 4.2.", + "type": "display" + }, + { + "linkId": "g4.1.yes", + "type": "group", + "item": [ + { + "extension": [ + { + "url": "http://hl7.org/fhir/StructureDefinition/questionnaire-enableWhen", + "extension": [ + { + "url": "#question", + "valueString": "4.1" + }, + { + "url": "#answer", + "valueBoolean": true + } + ] + } + ], + "linkId": "4.1.yes", + "text": "Nodes and Descriptions:", + "type": "choice", + "repeats": true, + "option": [ + { + "code": "4.1.yes.a", + "display": "1 Low Cervical, supraclavicular, and sternal notch nodes" + }, + { + "code": "4.1.yes.b", + "display": "2R Upper Paratracheal (right)" + }, + { + "code": "4.1.yes.c", + "display": "2L Upper paratracheal (left)" + }, + { + "code": "4.1.yes.d", + "display": "3a Pre-vascular" + }, + { + "code": "4.1.yes.e", + "display": "3p Retrotracheal" + }, + { + "code": "4.1.yes.f", + "display": "4R Lower paratracheal (right)" + }, + { + "code": "4.1.yes.g", + "display": "4L Upper paratracheal (left)" + }, + { + "code": "4.1.yes.h", + "display": "5 Subaortic" + }, + { + "code": "4.1.yes.i", + "display": "6 Para-aortic (ascending aorta or phrenic)" + }, + { + "code": "4.1.yes.j", + "display": "7 Subcarinal" + }, + { + "code": "4.1.yes.k", + "display": "8 Paraesophageal (below carina)" + }, + { + "code": "4.1.yes.l", + "display": "9 Pulmonary ligament" + }, + { + "code": "4.1.yes.m", + "display": "10 Hilar" + }, + { + "code": "4.1.yes.n", + "display": "11 Interlobar" + }, + { + "code": "4.1.yes.o", + "display": "12 Lobar" + }, + { + "code": "4.1.yes.p", + "display": "13 Segmental" + }, + { + "code": "4.1.yes.q", + "display": "14 Subsegmental" + }, + { + "code": "4.1.yes.r", + "display": "Other Nodes (axilla, sub-diaphragmatic)" + } + ], + "item": [ + { + "extension": [ + { + "url": "http://hl7.org/fhir/StructureDefinition/questionnaire-enableWhen", + "extension": [ + { + "url": "#question", + "valueString": "4.1.yes" + }, + { + "url": "#answer", + "valueCoding": { + "code": "4.1.yes.a" + } + } + ] + } + ], + "linkId": "4.1.yes.description", + "text": "Description of node:", + "type": "string" + } + ] + } + ] + } + ] + }, + { + "extension": [ + { + "url": "http://hl7.org/fhir/StructureDefinition/questionnaire-label", + "valueString": "4.2" + } + ], + "linkId": "4.2", + "text": "Other notable findings:", + "type": "text" + } + ] + }, + { + "extension": [ + { + "url": "http://hl7.org/fhir/StructureDefinition/questionnaire-label", + "valueString": "5" + } + ], + "linkId": "5.0", + "text": "M Category (Suspicious Extrathoracic Findings (M1b))", + "type": "group", + "item": [ + { + "extension": [ + { + "url": "http://hl7.org/fhir/StructureDefinition/questionnaire-label", + "valueString": "5.1" + } + ], + "linkId": "5.1", + "text": "Are there suspicious extrathoracic findings?", + "type": "boolean", + "item": [ + { + "extension": [ + { + "url": "http://hl7.org/fhir/StructureDefinition/questionnaire-displayCategory", + "valueCodeableConcept": { + "coding": [ + { + "system": "http://hl7.org/fhir/ValueSet/questionnaire-display-category", + "code": "instructions" + } + ] + } + } + ], + "text": "*If yes, indicate and describe the structures below which apply. If no, go to 6.", + "type": "display" + }, + { + "linkId": "g5.1.yes", + "type": "group", + "item": [ + { + "extension": [ + { + "url": "http://hl7.org/fhir/StructureDefinition/questionnaire-enableWhen", + "extension": [ + { + "url": "#question", + "valueString": "5.1" + }, + { + "url": "#answer", + "valueBoolean": true + } + ] + } + ], + "linkId": "5.1.yes", + "text": "Applicable Structures and Descriptions:", + "type": "choice", + "repeats": true, + "option": [ + { + "code": "5.1.yes.a", + "display": "Adrenals" + }, + { + "code": "5.1.yes.b", + "display": "Liver" + }, + { + "code": "5.1.yes.c", + "display": "Bone" + }, + { + "code": "5.1.yes.d", + "display": "Other" + } + ], + "item": [ + { + "extension": [ + { + "url": "http://hl7.org/fhir/StructureDefinition/questionnaire-enableWhen", + "extension": [ + { + "url": "#question", + "valueString": "5.1.yes" + }, + { + "url": "#answer", + "valueCoding": { + "code": "5.1.yes.d" + } + } + ] + } + ], + "linkId": "5.1.yes.d.description", + "text": "Description of structures:", + "type": "string" + } + ] + } + ] + } + ] + } + ] + }, + { + "extension": [ + { + "url": "http://hl7.org/fhir/StructureDefinition/questionnaire-label", + "valueString": "6" + } + ], + "linkId": "6.0", + "text": "Additional Findings", + "type": "group", + "item": [ + { + "extension": [ + { + "url": "http://hl7.org/fhir/StructureDefinition/questionnaire-label", + "valueString": "6.1" + } + ], + "linkId": "6.1", + "text": "Are there additional findings?", + "type": "boolean", + "item": [ + { + "extension": [ + { + "url": "http://hl7.org/fhir/StructureDefinition/questionnaire-displayCategory", + "valueCodeableConcept": { + "coding": [ + { + "system": "http://hl7.org/fhir/ValueSet/questionnaire-display-category", + "code": "instructions" + } + ] + } + } + ], + "text": "*If yes, indicate and describe the findings below which apply. If no, go to Impressions.", + "type": "display" + } + ] + }, + { + "extension": [ + { + "url": "http://hl7.org/fhir/StructureDefinition/questionnaire-label", + "valueString": "6.2" + }, + { + "url": "http://hl7.org/fhir/StructureDefinition/questionnaire-enableWhen", + "extension": [ + { + "url": "#question", + "valueString": "6.1" + }, + { + "url": "#answer", + "valueBoolean": true + } + ] + } + ], + "linkId": "6.2", + "text": "Findings and Descriptions:", + "type": "open-choice", + "repeats": true, + "option": [ + { + "code": "6.2a", + "display": "Emphysema" + }, + { + "code": "6.2b", + "display": "Fibrosis" + }, + { + "code": "6.2c", + "display": "Coronary artery classification" + }, + { + "code": "6.2d", + "display": "Asbestos related pleural disease" + }, + { + "code": "6.2e", + "display": "Interstitial lung disease" + }, + { + "code": "6.2f", + "display": "Atherosclerosis" + }, + { + "code": "6.2g", + "display": "Pulmonary Embolism" + } + ], + "item": [ + { + "extension": [ + { + "url": "http://hl7.org/fhir/StructureDefinition/questionnaire-enableWhen", + "extension": [ + { + "url": "#question", + "valueString": "6.2" + }, + { + "url": "#answer", + "valueCoding": { + "code": "6.2a" + } + } + ] + } + ], + "linkId": "6.2a", + "text": "Pulmonary, description:", + "type": "string" + } + ] + } + ] + } + ] + }, + { + "linkId": "g7", + "text": "IMPRESSIONS", + "type": "group", + "item": [ + { + "linkId": "g7.1", + "type": "group", + "item": [ + { + "extension": [ + { + "url": "http://hl7.org/fhir/StructureDefinition/questionnaire-label", + "valueString": "7.1" + } + ], + "linkId": "7.1", + "text": "Impression/Summary:", + "type": "text" + } + ] + }, + { + "extension": [ + { + "url": "http://hl7.org/fhir/StructureDefinition/questionnaire-label", + "valueString": "7.2" + } + ], + "linkId": "g7.2", + "text": "Radiologic Staging (TNM Version – 7th edition)", + "type": "group", + "item": [ + { + "linkId": "g7.20", + "text": "If this is a biopsy proven carcinoma, the preliminary radiologic stage is:", + "type": "group", + "item": [ + { + "extension": [ + { + "url": "http://hl7.org/fhir/StructureDefinition/questionnaire-label", + "valueString": "i)" + }, + { + "url": "http://hl7.org/fhir/StructureDefinition/questionnaire-questionControl", + "valueCodeableConcept": { + "coding": [ + { + "code": "radio-button", + "display": "Radio Button" + } + ] + } + }, + { + "url": "http://hl7.org/fhir/StructureDefinition/questionnaire-choiceOrientation", + "valueCode": "horizontal" + } + ], + "linkId": "7.2i", + "text": "Primary Tumour (T):", + "type": "choice", + "option": [ + { + "code": "T1a", + "display": "T1a" + }, + { + "code": "T1b", + "display": "T1b" + }, + { + "code": "T2", + "display": "T2" + }, + { + "code": "T2a", + "display": "T2a" + }, + { + "code": "T2b", + "display": "T2b" + }, + { + "code": "T3", + "display": "T3" + }, + { + "code": "T4", + "display": "T4" + } + ] + }, + { + "extension": [ + { + "url": "http://hl7.org/fhir/StructureDefinition/questionnaire-label", + "valueString": "ii)" + } + ], + "linkId": "7.2ii", + "text": "Regional Lymph Nodes (N):", + "type": "choice", + "option": [ + { + "code": "NX", + "display": "NX" + }, + { + "code": "N0", + "display": "N0" + }, + { + "code": "N1", + "display": "N1" + }, + { + "code": "N2", + "display": "N2" + }, + { + "code": "N3", + "display": "N3" + } + ] + }, + { + "extension": [ + { + "url": "http://hl7.org/fhir/StructureDefinition/questionnaire-label", + "valueString": "iii)" + } + ], + "linkId": "7.2iii", + "text": "Distant Metastasis (M):", + "type": "choice", + "option": [ + { + "code": "M0", + "display": "M0" + }, + { + "code": "M1", + "display": "M1" + }, + { + "code": "M1a", + "display": "M1a" + }, + { + "code": "M1b", + "display": "M1b" + } + ] + } + ] + } + ] + } + ] + } + ] + } + ] +} diff --git a/hapi-fhir-structures-dstu2.1/pom.xml b/hapi-fhir-structures-dstu2.1/pom.xml index 4551585acde..dac3392fc7e 100644 --- a/hapi-fhir-structures-dstu2.1/pom.xml +++ b/hapi-fhir-structures-dstu2.1/pom.xml @@ -5,7 +5,7 @@ ca.uhn.hapi.fhir hapi-deployable-pom - 6.9.7-SNAPSHOT + 6.11.7-SNAPSHOT ../hapi-deployable-pom/pom.xml @@ -38,6 +38,13 @@ + + jakarta.servlet + jakarta.servlet-api + provided + true + + @@ -65,17 +72,6 @@ true - - - javax.servlet - servlet-api - 2.5 - provided - - commons-codec commons-codec @@ -97,36 +93,6 @@ xmlunit-core test - - org.eclipse.jetty - jetty-servlets - test - - - org.eclipse.jetty - jetty-servlet - test - - - org.eclipse.jetty - jetty-server - test - - - org.eclipse.jetty - jetty-util - test - - - org.eclipse.jetty - jetty-webapp - test - - - org.eclipse.jetty - jetty-http - test - ch.qos.logback logback-classic @@ -210,23 +176,18 @@ - com.helger - ph-schematron + com.helger.schematron + ph-schematron-api test - com.helger - ph-commons + com.helger.schematron + ph-schematron-xslt test - javax.activation - javax.activation-api - test - - - javax.xml.bind - jaxb-api + jakarta.activation + jakarta.activation-api test @@ -263,13 +224,6 @@ true - - org.apache.maven.plugins - maven-compiler-plugin - - true - - org.apache.felix maven-bundle-plugin @@ -284,7 +238,7 @@ --> - javax.servlet*;resolution:=optional, + jakarta.servlet*;resolution:=optional, * diff --git a/hapi-fhir-structures-dstu2.1/src/main/java/org/hl7/fhir/dstu2016may/hapi/rest/server/Dstu2_1BundleFactory.java b/hapi-fhir-structures-dstu2.1/src/main/java/org/hl7/fhir/dstu2016may/hapi/rest/server/Dstu2_1BundleFactory.java index be6e75ff6b7..8eaf848f8f0 100644 --- a/hapi-fhir-structures-dstu2.1/src/main/java/org/hl7/fhir/dstu2016may/hapi/rest/server/Dstu2_1BundleFactory.java +++ b/hapi-fhir-structures-dstu2.1/src/main/java/org/hl7/fhir/dstu2016may/hapi/rest/server/Dstu2_1BundleFactory.java @@ -30,6 +30,7 @@ import ca.uhn.fhir.rest.api.BundleLinks; import ca.uhn.fhir.rest.api.Constants; import ca.uhn.fhir.rest.api.IVersionSpecificBundleFactory; import ca.uhn.fhir.util.ResourceReferenceInfo; +import jakarta.annotation.Nonnull; import org.hl7.fhir.dstu2016may.model.Bundle; import org.hl7.fhir.dstu2016may.model.Bundle.BundleEntryComponent; import org.hl7.fhir.dstu2016may.model.Bundle.BundleLinkComponent; @@ -48,7 +49,6 @@ import java.util.HashSet; import java.util.List; import java.util.Set; import java.util.UUID; -import javax.annotation.Nonnull; import static org.apache.commons.lang3.StringUtils.isNotBlank; diff --git a/hapi-fhir-structures-dstu2.1/src/main/java/org/hl7/fhir/dstu2016may/hapi/rest/server/ServerConformanceProvider.java b/hapi-fhir-structures-dstu2.1/src/main/java/org/hl7/fhir/dstu2016may/hapi/rest/server/ServerConformanceProvider.java index 59f3614d32f..f58805ed7c6 100644 --- a/hapi-fhir-structures-dstu2.1/src/main/java/org/hl7/fhir/dstu2016may/hapi/rest/server/ServerConformanceProvider.java +++ b/hapi-fhir-structures-dstu2.1/src/main/java/org/hl7/fhir/dstu2016may/hapi/rest/server/ServerConformanceProvider.java @@ -36,22 +36,22 @@ import ca.uhn.fhir.rest.server.method.SearchParameter; import ca.uhn.fhir.rest.server.util.BaseServerCapabilityStatementProvider; import ca.uhn.fhir.rest.server.*; import ca.uhn.fhir.rest.server.method.*; +import jakarta.servlet.ServletContext; +import jakarta.servlet.http.HttpServletRequest; import org.apache.commons.lang3.StringUtils; import org.hl7.fhir.dstu2016may.model.Enumerations.ConformanceResourceStatus; -import org.hl7.fhir.dstu2016may.model.Enumerations.ResourceType; import org.hl7.fhir.dstu2016may.model.OperationDefinition.OperationDefinitionParameterComponent; +import org.hl7.fhir.dstu2016may.model.OperationDefinition.OperationKind; import org.hl7.fhir.dstu2016may.model.OperationDefinition.OperationParameterUse; import org.hl7.fhir.dstu2016may.model.*; import org.hl7.fhir.dstu2016may.model.Conformance.*; -import org.hl7.fhir.dstu2016may.model.OperationDefinition.OperationKind; +import org.hl7.fhir.dstu2016may.model.Enumerations.ResourceType; import org.hl7.fhir.exceptions.FHIRException; import org.hl7.fhir.instance.model.api.IBaseResource; import org.hl7.fhir.instance.model.api.IPrimitiveType; import java.util.Map.Entry; import java.util.*; -import javax.servlet.ServletContext; -import javax.servlet.http.HttpServletRequest; import static org.apache.commons.lang3.StringUtils.isBlank; import static org.apache.commons.lang3.StringUtils.isNotBlank; diff --git a/hapi-fhir-structures-dstu2.1/src/test/java/ca/uhn/fhir/rest/server/CreateBinaryDstu2_1Test.java b/hapi-fhir-structures-dstu2.1/src/test/java/ca/uhn/fhir/rest/server/CreateBinaryDstu2_1Test.java deleted file mode 100644 index d8b81a61e16..00000000000 --- a/hapi-fhir-structures-dstu2.1/src/test/java/ca/uhn/fhir/rest/server/CreateBinaryDstu2_1Test.java +++ /dev/null @@ -1,172 +0,0 @@ -package ca.uhn.fhir.rest.server; - -import ca.uhn.fhir.context.FhirContext; -import ca.uhn.fhir.rest.annotation.Create; -import ca.uhn.fhir.rest.annotation.ResourceParam; -import ca.uhn.fhir.rest.api.Constants; -import ca.uhn.fhir.rest.api.MethodOutcome; -import ca.uhn.fhir.test.utilities.JettyUtil; -import ca.uhn.fhir.util.TestUtil; -import org.apache.commons.io.IOUtils; -import org.apache.http.client.methods.CloseableHttpResponse; -import org.apache.http.client.methods.HttpPost; -import org.apache.http.entity.ByteArrayEntity; -import org.apache.http.entity.StringEntity; -import org.apache.http.impl.client.CloseableHttpClient; -import org.apache.http.impl.client.HttpClientBuilder; -import org.apache.http.impl.conn.PoolingHttpClientConnectionManager; -import org.eclipse.jetty.server.Server; -import org.eclipse.jetty.servlet.ServletHandler; -import org.eclipse.jetty.servlet.ServletHolder; -import org.hl7.fhir.dstu2016may.model.Binary; -import org.hl7.fhir.dstu2016may.model.IdType; -import org.hl7.fhir.dstu2016may.model.Patient; -import org.hl7.fhir.instance.model.api.IBaseResource; -import org.junit.jupiter.api.AfterAll; -import org.junit.jupiter.api.BeforeAll; -import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.Test; - -import java.util.concurrent.TimeUnit; - -import static org.junit.jupiter.api.Assertions.assertArrayEquals; -import static org.junit.jupiter.api.Assertions.assertEquals; -import static org.junit.jupiter.api.Assertions.assertNull; - -public class CreateBinaryDstu2_1Test { - private static CloseableHttpClient ourClient; - private static FhirContext ourCtx = FhirContext.forDstu2_1(); - private static Binary ourLastBinary; - private static byte[] ourLastBinaryBytes; - private static String ourLastBinaryString; - private static int ourPort; - private static Server ourServer; - - @BeforeEach - public void before() { - ourLastBinary = null; - ourLastBinaryBytes = null; - ourLastBinaryString = null; - } - - @Test - public void testRawBytesBinaryContentType() throws Exception { - HttpPost post = new HttpPost("http://localhost:" + ourPort + "/Binary"); - post.setEntity(new ByteArrayEntity(new byte[] { 0, 1, 2, 3, 4 })); - post.addHeader("Content-Type", "application/foo"); - CloseableHttpResponse status = ourClient.execute(post); - try { - assertEquals("application/foo", ourLastBinary.getContentType()); - assertArrayEquals(new byte[] { 0, 1, 2, 3, 4 }, ourLastBinary.getContent()); - assertArrayEquals(new byte[] { 0, 1, 2, 3, 4 }, ourLastBinaryBytes); - } finally { - IOUtils.closeQuietly(status); - } - } - - /** - * Technically the client shouldn't be doing it this way, but we'll be accepting - */ - @Test - public void testRawBytesFhirContentType() throws Exception { - - Binary b = new Binary(); - b.setContentType("application/foo"); - b.setContent(new byte[] { 0, 1, 2, 3, 4 }); - String encoded = ourCtx.newJsonParser().encodeResourceToString(b); - - HttpPost post = new HttpPost("http://localhost:" + ourPort + "/Binary"); - post.setEntity(new StringEntity(encoded)); - post.addHeader("Content-Type", Constants.CT_FHIR_JSON); - CloseableHttpResponse status = ourClient.execute(post); - try { - assertEquals("application/foo", ourLastBinary.getContentType()); - assertArrayEquals(new byte[] { 0, 1, 2, 3, 4 }, ourLastBinary.getContent()); - } finally { - IOUtils.closeQuietly(status); - } - } - - @Test - public void testRawBytesFhirContentTypeContainingFhir() throws Exception { - - Patient p = new Patient(); - p.getText().setDivAsString("A PATIENT"); - - Binary b = new Binary(); - b.setContentType("application/xml+fhir"); - b.setContent(ourCtx.newXmlParser().encodeResourceToString(p).getBytes("UTF-8")); - String encoded = ourCtx.newJsonParser().encodeResourceToString(b); - - HttpPost post = new HttpPost("http://localhost:" + ourPort + "/Binary"); - post.setEntity(new StringEntity(encoded)); - post.addHeader("Content-Type", Constants.CT_FHIR_JSON); - CloseableHttpResponse status = ourClient.execute(post); - try { - assertEquals("application/xml+fhir", ourLastBinary.getContentType()); - assertArrayEquals(b.getContent(), ourLastBinary.getContent()); - assertEquals(encoded, ourLastBinaryString); - assertArrayEquals(encoded.getBytes("UTF-8"), ourLastBinaryBytes); - } finally { - IOUtils.closeQuietly(status); - } - } - - @Test - public void testRawBytesNoContentType() throws Exception { - HttpPost post = new HttpPost("http://localhost:" + ourPort + "/Binary"); - post.setEntity(new ByteArrayEntity(new byte[] { 0, 1, 2, 3, 4 })); - CloseableHttpResponse status = ourClient.execute(post); - try { - assertNull(ourLastBinary.getContentType()); - assertArrayEquals(new byte[] { 0, 1, 2, 3, 4 }, ourLastBinary.getContent()); - } finally { - IOUtils.closeQuietly(status); - } - } - - @AfterAll - public static void afterClassClearContext() throws Exception { - JettyUtil.closeServer(ourServer); - TestUtil.randomizeLocaleAndTimezone(); - } - - @BeforeAll - public static void beforeClass() throws Exception { - ourServer = new Server(0); - - BinaryProvider binaryProvider = new BinaryProvider(); - - ServletHandler proxyHandler = new ServletHandler(); - RestfulServer servlet = new RestfulServer(ourCtx); - servlet.setResourceProviders(binaryProvider); - ServletHolder servletHolder = new ServletHolder(servlet); - proxyHandler.addServletWithMapping(servletHolder, "/*"); - ourServer.setHandler(proxyHandler); - JettyUtil.startServer(ourServer); - ourPort = JettyUtil.getPortForStartedServer(ourServer); - - PoolingHttpClientConnectionManager connectionManager = new PoolingHttpClientConnectionManager(5000, TimeUnit.MILLISECONDS); - HttpClientBuilder builder = HttpClientBuilder.create(); - builder.setConnectionManager(connectionManager); - ourClient = builder.build(); - } - - public static class BinaryProvider implements IResourceProvider { - - @Create() - public MethodOutcome createBinary(@ResourceParam Binary theBinary, @ResourceParam String theBinaryString, @ResourceParam byte[] theBinaryBytes) { - ourLastBinary = theBinary; - ourLastBinaryString = theBinaryString; - ourLastBinaryBytes = theBinaryBytes; - return new MethodOutcome(new IdType("Binary/001/_history/002")); - } - - @Override - public Class getResourceType() { - return Binary.class; - } - - } - -} diff --git a/hapi-fhir-structures-dstu2.1/src/test/java/ca/uhn/fhir/rest/server/CreateDstu2_1Test.java b/hapi-fhir-structures-dstu2.1/src/test/java/ca/uhn/fhir/rest/server/CreateDstu2_1Test.java deleted file mode 100644 index 8b6f5cafa8f..00000000000 --- a/hapi-fhir-structures-dstu2.1/src/test/java/ca/uhn/fhir/rest/server/CreateDstu2_1Test.java +++ /dev/null @@ -1,283 +0,0 @@ -package ca.uhn.fhir.rest.server; - -import ca.uhn.fhir.context.FhirContext; -import ca.uhn.fhir.i18n.Msg; -import ca.uhn.fhir.rest.annotation.Create; -import ca.uhn.fhir.rest.annotation.IdParam; -import ca.uhn.fhir.rest.annotation.Read; -import ca.uhn.fhir.rest.annotation.ResourceParam; -import ca.uhn.fhir.rest.annotation.Search; -import ca.uhn.fhir.rest.api.Constants; -import ca.uhn.fhir.rest.api.MethodOutcome; -import ca.uhn.fhir.rest.client.MyPatientWithExtensions; -import ca.uhn.fhir.test.utilities.JettyUtil; -import ca.uhn.fhir.util.TestUtil; -import org.apache.commons.io.IOUtils; -import org.apache.http.HttpResponse; -import org.apache.http.client.methods.HttpGet; -import org.apache.http.client.methods.HttpPost; -import org.apache.http.entity.ContentType; -import org.apache.http.entity.StringEntity; -import org.apache.http.impl.client.CloseableHttpClient; -import org.apache.http.impl.client.HttpClientBuilder; -import org.apache.http.impl.conn.PoolingHttpClientConnectionManager; -import org.eclipse.jetty.server.Server; -import org.eclipse.jetty.servlet.ServletHandler; -import org.eclipse.jetty.servlet.ServletHolder; -import org.hl7.fhir.dstu2016may.model.DateType; -import org.hl7.fhir.dstu2016may.model.IdType; -import org.hl7.fhir.dstu2016may.model.OperationOutcome; -import org.hl7.fhir.dstu2016may.model.OperationOutcome.OperationOutcomeIssueComponent; -import org.hl7.fhir.dstu2016may.model.Patient; -import org.hl7.fhir.instance.model.api.IBaseOperationOutcome; -import org.hl7.fhir.instance.model.api.IBaseResource; -import org.junit.jupiter.api.AfterAll; -import org.junit.jupiter.api.BeforeAll; -import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.Test; - -import java.nio.charset.StandardCharsets; -import java.util.ArrayList; -import java.util.List; -import java.util.concurrent.TimeUnit; - -import static org.hamcrest.MatcherAssert.assertThat; -import static org.hamcrest.Matchers.containsString; -import static org.hamcrest.Matchers.not; -import static org.hamcrest.Matchers.stringContainsInOrder; -import static org.junit.jupiter.api.Assertions.assertEquals; - -public class CreateDstu2_1Test { - private static CloseableHttpClient ourClient; - - private static FhirContext ourCtx = FhirContext.forDstu2_1(); - private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(CreateDstu2_1Test.class); - private static int ourPort; - private static Server ourServer; - public static IBaseOperationOutcome ourReturnOo; - - @BeforeEach - public void before() { - ourReturnOo = null; - } - - /** - * #472 - */ - @Test - public void testCreateReturnsLocationHeader() throws Exception { - - HttpPost httpPost = new HttpPost("http://localhost:" + ourPort + "/Patient"); - httpPost.setEntity(new StringEntity("{\"resourceType\":\"Patient\", \"status\":\"active\"}", ContentType.parse(Constants.CT_FHIR_JSON+"; charset=utf-8"))); - HttpResponse status = ourClient.execute(httpPost); - - String responseContent = IOUtils.toString(status.getEntity().getContent(), StandardCharsets.UTF_8); - IOUtils.closeQuietly(status.getEntity().getContent()); - - ourLog.info("Response was:\n{}", responseContent); - - assertEquals(201, status.getStatusLine().getStatusCode()); - - assertEquals(1, status.getHeaders("Location").length); - assertEquals(1, status.getHeaders("Content-Location").length); - assertEquals("http://localhost:" + ourPort + "/Patient/1", status.getFirstHeader("Location").getValue()); - - } - - @Test - public void testCreateReturnsRepresentation() throws Exception { - ourReturnOo = new OperationOutcome().addIssue(new OperationOutcomeIssueComponent().setDiagnostics("DIAG")); - - HttpPost httpPost = new HttpPost("http://localhost:" + ourPort + "/Patient"); - httpPost.setEntity(new StringEntity("{\"resourceType\":\"Patient\", \"gender\":\"male\"}", ContentType.parse(Constants.CT_FHIR_JSON+"; charset=utf-8"))); - HttpResponse status = ourClient.execute(httpPost); - String expectedContent = "{\"resourceType\":\"Patient\",\"id\":\"1\",\"gender\":\"male\"}"; - - String responseContent = IOUtils.toString(status.getEntity().getContent(), StandardCharsets.UTF_8); - IOUtils.closeQuietly(status.getEntity().getContent()); - - ourLog.info("Response was:\n{}", responseContent); - - assertEquals(201, status.getStatusLine().getStatusCode()); - assertEquals(expectedContent, responseContent); - - } - - /** - * #342 - */ - @Test - public void testCreateWithInvalidContent() throws Exception { - - HttpPost httpPost = new HttpPost("http://localhost:" + ourPort + "/Patient"); - httpPost.setEntity(new StringEntity("FOO", ContentType.parse("application/xml+fhir; charset=utf-8"))); - HttpResponse status = ourClient.execute(httpPost); - - String responseContent = IOUtils.toString(status.getEntity().getContent(), StandardCharsets.UTF_8); - IOUtils.closeQuietly(status.getEntity().getContent()); - - ourLog.info("Response was:\n{}", responseContent); - - assertEquals(400, status.getStatusLine().getStatusCode()); - - assertThat(responseContent, containsString("", - "", - "", - "", - "", - "", - "", - "", - "")); - //@formatter:on - - assertThat(responseContent, not(containsString("http://hl7.org/fhir/"))); - } - - @AfterAll - public static void afterClassClearContext() throws Exception { - JettyUtil.closeServer(ourServer); - TestUtil.randomizeLocaleAndTimezone(); - } - - @BeforeAll - public static void beforeClass() throws Exception { - ourServer = new Server(0); - - PatientProvider patientProvider = new PatientProvider(); - - ServletHandler proxyHandler = new ServletHandler(); - RestfulServer servlet = new RestfulServer(ourCtx); - - servlet.setResourceProviders(patientProvider); - ServletHolder servletHolder = new ServletHolder(servlet); - proxyHandler.addServletWithMapping(servletHolder, "/*"); - ourServer.setHandler(proxyHandler); - JettyUtil.startServer(ourServer); - ourPort = JettyUtil.getPortForStartedServer(ourServer); - - PoolingHttpClientConnectionManager connectionManager = new PoolingHttpClientConnectionManager(5000, TimeUnit.MILLISECONDS); - HttpClientBuilder builder = HttpClientBuilder.create(); - builder.setConnectionManager(connectionManager); - ourClient = builder.build(); - - } - - public static class PatientProvider implements IResourceProvider { - - @Create() - public MethodOutcome create(@ResourceParam Patient theIdParam) { - theIdParam.setId("1"); - return new MethodOutcome(new IdType("Patient", "1"), true).setOperationOutcome(ourReturnOo) - .setResource(theIdParam); - } - - @Override - public Class getResourceType() { - return Patient.class; - } - - @Read() - public MyPatientWithExtensions read(@IdParam IdType theIdParam) { - MyPatientWithExtensions p0 = new MyPatientWithExtensions(); - p0.setId(theIdParam); - p0.setDateExt(new DateType("2011-01-01")); - return p0; - } - - @Search - public List search() { - ArrayList retVal = new ArrayList(); - - MyPatientWithExtensions p0 = new MyPatientWithExtensions(); - p0.setId(new IdType("Patient/0")); - p0.setDateExt(new DateType("2011-01-01")); - retVal.add(p0); - - Patient p1 = new Patient(); - p1.setId(new IdType("Patient/1")); - p1.addName().addFamily("The Family"); - retVal.add(p1); - - return retVal; - } - - } - -} diff --git a/hapi-fhir-structures-dstu2.1/src/test/java/ca/uhn/fhir/rest/server/CustomTypeServerDstu2_1.java b/hapi-fhir-structures-dstu2.1/src/test/java/ca/uhn/fhir/rest/server/CustomTypeServerDstu2_1.java index bc39778fe31..6ba6d4cf304 100644 --- a/hapi-fhir-structures-dstu2.1/src/test/java/ca/uhn/fhir/rest/server/CustomTypeServerDstu2_1.java +++ b/hapi-fhir-structures-dstu2.1/src/test/java/ca/uhn/fhir/rest/server/CustomTypeServerDstu2_1.java @@ -1,6 +1,7 @@ package ca.uhn.fhir.rest.server; import ca.uhn.fhir.context.FhirContext; +import ca.uhn.fhir.i18n.Msg; import ca.uhn.fhir.model.api.IResource; import ca.uhn.fhir.model.primitive.StringDt; import ca.uhn.fhir.rest.annotation.ConditionalUrlParam; @@ -11,46 +12,43 @@ import ca.uhn.fhir.rest.annotation.ResourceParam; import ca.uhn.fhir.rest.annotation.Search; import ca.uhn.fhir.rest.api.Constants; import ca.uhn.fhir.rest.api.MethodOutcome; -import ca.uhn.fhir.test.utilities.JettyUtil; +import ca.uhn.fhir.test.utilities.HttpClientExtension; +import ca.uhn.fhir.test.utilities.server.RestfulServerExtension; import ca.uhn.fhir.util.TestUtil; import org.apache.commons.io.IOUtils; import org.apache.http.HttpResponse; import org.apache.http.client.methods.HttpPost; import org.apache.http.entity.ContentType; import org.apache.http.entity.StringEntity; -import org.apache.http.impl.client.CloseableHttpClient; -import org.apache.http.impl.client.HttpClientBuilder; -import org.apache.http.impl.conn.PoolingHttpClientConnectionManager; -import org.eclipse.jetty.server.Server; -import org.eclipse.jetty.servlet.ServletHandler; -import org.eclipse.jetty.servlet.ServletHolder; import org.hl7.fhir.dstu2016may.model.IdType; import org.hl7.fhir.dstu2016may.model.OperationOutcome; import org.hl7.fhir.dstu2016may.model.Patient; import org.junit.jupiter.api.AfterAll; -import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.RegisterExtension; import java.nio.charset.StandardCharsets; import java.util.ArrayList; import java.util.List; -import java.util.concurrent.TimeUnit; import static org.junit.jupiter.api.Assertions.assertEquals; public class CustomTypeServerDstu2_1 { - private static CloseableHttpClient ourClient; - - private static FhirContext ourCtx = FhirContext.forDstu2_1(); + private static final FhirContext ourCtx = FhirContext.forDstu2_1(); private static String ourLastConditionalUrl; private static IdType ourLastId; private static IdType ourLastIdParam; private static boolean ourLastRequestWasSearch; private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(CustomTypeServerDstu2_1.class); - private static int ourPort; - private static Server ourServer; + @RegisterExtension + public static final RestfulServerExtension ourServer = new RestfulServerExtension(ourCtx) + .registerProvider(new PatientProvider()); + + @RegisterExtension + public static final HttpClientExtension ourClient = new HttpClientExtension(); + @BeforeEach public void before() { ourLastId = null; @@ -67,7 +65,7 @@ public class CustomTypeServerDstu2_1 { patient.setId("2"); patient.addIdentifier().setValue("002"); - HttpPost httpPost = new HttpPost("http://localhost:" + ourPort + "/Patient"); + HttpPost httpPost = new HttpPost(ourServer.getBaseUrl() + "/Patient"); // httpPost.addHeader(Constants.HEADER_IF_NONE_EXIST, "Patient?identifier=system%7C001"); httpPost.setEntity(new StringEntity(ourCtx.newXmlParser().encodeResourceToString(patient), ContentType.create(Constants.CT_FHIR_XML, "UTF-8"))); @@ -87,7 +85,7 @@ public class CustomTypeServerDstu2_1 { Patient patient = new Patient(); patient.addIdentifier().setValue("002"); - HttpPost httpPost = new HttpPost("http://localhost:" + ourPort + "/Patient/2"); + HttpPost httpPost = new HttpPost(ourServer.getBaseUrl() + "/Patient/2"); // httpPost.addHeader(Constants.HEADER_IF_NONE_EXIST, "Patient?identifier=system%7C001"); httpPost.setEntity(new StringEntity(ourCtx.newXmlParser().encodeResourceToString(patient), ContentType.create(Constants.CT_FHIR_XML, "UTF-8"))); @@ -100,7 +98,7 @@ public class CustomTypeServerDstu2_1 { assertEquals(400, status.getStatusLine().getStatusCode()); OperationOutcome oo = ourCtx.newXmlParser().parseResource(OperationOutcome.class, responseContent); - assertEquals("Can not create resource with ID \"2\", ID must not be supplied on a create (POST) operation (use an HTTP PUT / update operation if you wish to supply an ID)", oo.getIssue().get(0).getDiagnostics()); + assertEquals(Msg.code(365) + "Can not create resource with ID \"2\", ID must not be supplied on a create (POST) operation (use an HTTP PUT / update operation if you wish to supply an ID)", oo.getIssue().get(0).getDiagnostics()); } @Test @@ -109,7 +107,7 @@ public class CustomTypeServerDstu2_1 { Patient patient = new Patient(); patient.addIdentifier().setValue("002"); - HttpPost httpPost = new HttpPost("http://localhost:" + ourPort + "/Patient/2"); + HttpPost httpPost = new HttpPost(ourServer.getBaseUrl() + "/Patient/2"); httpPost.addHeader(Constants.HEADER_IF_NONE_EXIST, "Patient?identifier=system%7C001"); httpPost.setEntity(new StringEntity(ourCtx.newXmlParser().encodeResourceToString(patient), ContentType.create(Constants.CT_FHIR_XML, "UTF-8"))); @@ -122,38 +120,15 @@ public class CustomTypeServerDstu2_1 { assertEquals(400, status.getStatusLine().getStatusCode()); OperationOutcome oo = ourCtx.newXmlParser().parseResource(OperationOutcome.class, responseContent); - assertEquals("Can not create resource with ID \"2\", ID must not be supplied on a create (POST) operation (use an HTTP PUT / update operation if you wish to supply an ID)", oo.getIssue().get(0).getDiagnostics()); + assertEquals(Msg.code(365) + "Can not create resource with ID \"2\", ID must not be supplied on a create (POST) operation (use an HTTP PUT / update operation if you wish to supply an ID)", oo.getIssue().get(0).getDiagnostics()); } @AfterAll public static void afterClassClearContext() throws Exception { - JettyUtil.closeServer(ourServer); TestUtil.randomizeLocaleAndTimezone(); } - @BeforeAll - public static void beforeClass() throws Exception { - ourServer = new Server(0); - - PatientProvider patientProvider = new PatientProvider(); - - ServletHandler proxyHandler = new ServletHandler(); - RestfulServer servlet = new RestfulServer(ourCtx); - servlet.setResourceProviders(patientProvider); - ServletHolder servletHolder = new ServletHolder(servlet); - proxyHandler.addServletWithMapping(servletHolder, "/*"); - ourServer.setHandler(proxyHandler); - JettyUtil.startServer(ourServer); - ourPort = JettyUtil.getPortForStartedServer(ourServer); - - PoolingHttpClientConnectionManager connectionManager = new PoolingHttpClientConnectionManager(5000, TimeUnit.MILLISECONDS); - HttpClientBuilder builder = HttpClientBuilder.create(); - builder.setConnectionManager(connectionManager); - ourClient = builder.build(); - - } - public static class PatientProvider implements IResourceProvider { @Create() diff --git a/hapi-fhir-structures-dstu2.1/src/test/java/ca/uhn/fhir/rest/server/DeleteConditionalDstu2_1Test.java b/hapi-fhir-structures-dstu2.1/src/test/java/ca/uhn/fhir/rest/server/DeleteConditionalDstu2_1Test.java deleted file mode 100644 index ad7fe63c5a3..00000000000 --- a/hapi-fhir-structures-dstu2.1/src/test/java/ca/uhn/fhir/rest/server/DeleteConditionalDstu2_1Test.java +++ /dev/null @@ -1,132 +0,0 @@ -package ca.uhn.fhir.rest.server; - -import ca.uhn.fhir.context.FhirContext; -import ca.uhn.fhir.rest.annotation.ConditionalUrlParam; -import ca.uhn.fhir.rest.annotation.Delete; -import ca.uhn.fhir.rest.annotation.IdParam; -import ca.uhn.fhir.rest.api.MethodOutcome; -import ca.uhn.fhir.rest.client.api.IGenericClient; -import ca.uhn.fhir.rest.client.interceptor.LoggingInterceptor; -import ca.uhn.fhir.test.utilities.JettyUtil; -import ca.uhn.fhir.util.TestUtil; -import org.apache.http.impl.client.CloseableHttpClient; -import org.apache.http.impl.client.HttpClientBuilder; -import org.apache.http.impl.conn.PoolingHttpClientConnectionManager; -import org.eclipse.jetty.server.Server; -import org.eclipse.jetty.servlet.ServletHandler; -import org.eclipse.jetty.servlet.ServletHolder; -import org.hl7.fhir.dstu2016may.model.IdType; -import org.hl7.fhir.dstu2016may.model.Patient; -import org.hl7.fhir.instance.model.api.IBaseResource; -import org.junit.jupiter.api.AfterAll; -import org.junit.jupiter.api.BeforeAll; -import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.Test; - -import java.util.concurrent.TimeUnit; - -import static org.junit.jupiter.api.Assertions.assertEquals; -import static org.junit.jupiter.api.Assertions.assertTrue; - -public class DeleteConditionalDstu2_1Test { - private static CloseableHttpClient ourClient; - private static FhirContext ourCtx = FhirContext.forDstu2_1(); - private static IGenericClient ourHapiClient; - private static String ourLastConditionalUrl; - private static IdType ourLastIdParam; - private static boolean ourLastRequestWasDelete; - private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(DeleteConditionalDstu2_1Test.class); - private static int ourPort; - private static Server ourServer; - - - @BeforeEach - public void before() { - ourLastConditionalUrl = null; - ourLastIdParam = null; - ourLastRequestWasDelete = false; - } - - - - @Test - public void testSearchStillWorks() throws Exception { - - Patient patient = new Patient(); - patient.addIdentifier().setValue("002"); - -// HttpGet httpGet = new HttpGet("http://localhost:" + ourPort + "/Patient?_pretty=true"); -// -// HttpResponse status = ourClient.execute(httpGet); -// -// String responseContent = IOUtils.toString(status.getEntity().getContent()); -// IOUtils.closeQuietly(status.getEntity().getContent()); -// -// ourLog.info("Response was:\n{}", responseContent); - - //@formatter:off - ourHapiClient - .delete() - .resourceConditionalByType(Patient.class) - .where(Patient.IDENTIFIER.exactly().systemAndIdentifier("SOMESYS","SOMEID")) - .execute(); - //@formatter:on - - assertTrue(ourLastRequestWasDelete); - assertEquals(null, ourLastIdParam); - assertEquals("Patient?identifier=SOMESYS%7CSOMEID", ourLastConditionalUrl); - - } - - - - @AfterAll - public static void afterClassClearContext() throws Exception { - JettyUtil.closeServer(ourServer); - TestUtil.randomizeLocaleAndTimezone(); - } - - - @BeforeAll - public static void beforeClass() throws Exception { - ourServer = new Server(0); - - PatientProvider patientProvider = new PatientProvider(); - - ServletHandler proxyHandler = new ServletHandler(); - RestfulServer servlet = new RestfulServer(ourCtx); - servlet.setResourceProviders(patientProvider); - ServletHolder servletHolder = new ServletHolder(servlet); - proxyHandler.addServletWithMapping(servletHolder, "/*"); - ourServer.setHandler(proxyHandler); - JettyUtil.startServer(ourServer); - ourPort = JettyUtil.getPortForStartedServer(ourServer); - - PoolingHttpClientConnectionManager connectionManager = new PoolingHttpClientConnectionManager(5000, TimeUnit.MILLISECONDS); - HttpClientBuilder builder = HttpClientBuilder.create(); - builder.setConnectionManager(connectionManager); - ourClient = builder.build(); - - ourCtx.getRestfulClientFactory().setSocketTimeout(500 * 1000); - ourHapiClient = ourCtx.newRestfulGenericClient("http://localhost:" + ourPort + "/"); - ourHapiClient.registerInterceptor(new LoggingInterceptor()); - } - - public static class PatientProvider implements IResourceProvider { - - @Delete() - public MethodOutcome deletePatient(@IdParam IdType theIdParam, @ConditionalUrlParam String theConditional) { - ourLastRequestWasDelete = true; - ourLastConditionalUrl = theConditional; - ourLastIdParam = theIdParam; - return new MethodOutcome(new IdType("Patient/001/_history/002")); - } - - @Override - public Class getResourceType() { - return Patient.class; - } - - } - -} diff --git a/hapi-fhir-structures-dstu2.1/src/test/java/ca/uhn/fhir/rest/server/FormatParameterDstu2_1Test.java b/hapi-fhir-structures-dstu2.1/src/test/java/ca/uhn/fhir/rest/server/FormatParameterDstu2_1Test.java deleted file mode 100644 index e5c7478ec9d..00000000000 --- a/hapi-fhir-structures-dstu2.1/src/test/java/ca/uhn/fhir/rest/server/FormatParameterDstu2_1Test.java +++ /dev/null @@ -1,255 +0,0 @@ -package ca.uhn.fhir.rest.server; - -import ca.uhn.fhir.context.FhirContext; -import ca.uhn.fhir.model.api.annotation.ResourceDef; -import ca.uhn.fhir.rest.annotation.IdParam; -import ca.uhn.fhir.rest.annotation.Read; -import ca.uhn.fhir.rest.api.EncodingEnum; -import ca.uhn.fhir.test.utilities.JettyUtil; -import ca.uhn.fhir.util.TestUtil; -import org.apache.commons.io.IOUtils; -import org.apache.http.client.methods.CloseableHttpResponse; -import org.apache.http.client.methods.HttpGet; -import org.apache.http.impl.client.CloseableHttpClient; -import org.apache.http.impl.client.HttpClientBuilder; -import org.apache.http.impl.conn.PoolingHttpClientConnectionManager; -import org.eclipse.jetty.server.Server; -import org.eclipse.jetty.servlet.ServletHandler; -import org.eclipse.jetty.servlet.ServletHolder; -import org.hl7.fhir.dstu2016may.model.IdType; -import org.hl7.fhir.dstu2016may.model.Patient; -import org.junit.jupiter.api.AfterAll; -import org.junit.jupiter.api.BeforeAll; -import org.junit.jupiter.api.Test; - -import java.util.concurrent.TimeUnit; - -import static org.junit.jupiter.api.Assertions.assertEquals; - -public class FormatParameterDstu2_1Test { - - private static final String VALUE_XML = ""; - private static final String VALUE_JSON = "{\"resourceType\":\"Patient\",\"id\":\"p1ReadId\",\"meta\":{\"profile\":[\"http://foo_profile\"]},\"identifier\":[{\"value\":\"p1ReadValue\"}]}"; - private static CloseableHttpClient ourClient; - private static FhirContext ourCtx = FhirContext.forDstu2_1(); - private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(FormatParameterDstu2_1Test.class); - private static int ourPort; - private static Server ourServer; - private static RestfulServer ourServlet; - - /** - * See #346 - */ - @Test - public void testFormatXml() throws Exception { - ourServlet.setDefaultResponseEncoding(EncodingEnum.JSON); - - HttpGet httpGet = new HttpGet("http://localhost:" + ourPort + "/Patient/123?_format=xml"); - CloseableHttpResponse status = ourClient.execute(httpGet); - try { - String responseContent = IOUtils.toString(status.getEntity().getContent()); - ourLog.info(responseContent); - - assertEquals(200, status.getStatusLine().getStatusCode()); - assertEquals(VALUE_XML, responseContent); - } finally { - IOUtils.closeQuietly(status); - } - } - - /** - * See #346 - */ - @Test - public void testFormatApplicationXml() throws Exception { - ourServlet.setDefaultResponseEncoding(EncodingEnum.JSON); - - HttpGet httpGet = new HttpGet("http://localhost:" + ourPort + "/Patient/123?_format=application/xml"); - CloseableHttpResponse status = ourClient.execute(httpGet); - try { - String responseContent = IOUtils.toString(status.getEntity().getContent()); - ourLog.info(responseContent); - - assertEquals(200, status.getStatusLine().getStatusCode()); - assertEquals(VALUE_XML, responseContent); - } finally { - IOUtils.closeQuietly(status); - } - } - - /** - * See #346 - */ - @Test - public void testFormatApplicationXmlFhir() throws Exception { - ourServlet.setDefaultResponseEncoding(EncodingEnum.JSON); - - HttpGet httpGet = new HttpGet("http://localhost:" + ourPort + "/Patient/123?_format=application/xml%2Bfhir"); - CloseableHttpResponse status = ourClient.execute(httpGet); - try { - String responseContent = IOUtils.toString(status.getEntity().getContent()); - ourLog.info(responseContent); - - assertEquals(200, status.getStatusLine().getStatusCode()); - assertEquals(VALUE_XML, responseContent); - } finally { - IOUtils.closeQuietly(status); - } - } - - /** - * See #346 - */ - @Test - public void testFormatApplicationXmlFhirUnescaped() throws Exception { - ourServlet.setDefaultResponseEncoding(EncodingEnum.JSON); - - // The plus isn't escaped here, and it should be.. but we'll be lenient - HttpGet httpGet = new HttpGet("http://localhost:" + ourPort + "/Patient/123?_format=application/xml+fhir"); - CloseableHttpResponse status = ourClient.execute(httpGet); - try { - String responseContent = IOUtils.toString(status.getEntity().getContent()); - ourLog.info(responseContent); - - assertEquals(200, status.getStatusLine().getStatusCode()); - assertEquals(VALUE_XML, responseContent); - } finally { - IOUtils.closeQuietly(status); - } - } - - /** - * See #346 - */ - @Test - public void testFormatJson() throws Exception { - ourServlet.setDefaultResponseEncoding(EncodingEnum.XML); - - HttpGet httpGet = new HttpGet("http://localhost:" + ourPort + "/Patient/123?_format=json"); - CloseableHttpResponse status = ourClient.execute(httpGet); - try { - String responseContent = IOUtils.toString(status.getEntity().getContent()); - ourLog.info(responseContent); - - assertEquals(200, status.getStatusLine().getStatusCode()); - assertEquals(VALUE_JSON, responseContent); - } finally { - IOUtils.closeQuietly(status); - } - } - - /** - * See #346 - */ - @Test - public void testFormatApplicationJson() throws Exception { - ourServlet.setDefaultResponseEncoding(EncodingEnum.XML); - - HttpGet httpGet = new HttpGet("http://localhost:" + ourPort + "/Patient/123?_format=application/json"); - CloseableHttpResponse status = ourClient.execute(httpGet); - try { - String responseContent = IOUtils.toString(status.getEntity().getContent()); - ourLog.info(responseContent); - - assertEquals(200, status.getStatusLine().getStatusCode()); - assertEquals(VALUE_JSON, responseContent); - } finally { - IOUtils.closeQuietly(status); - } - } - - /** - * See #346 - */ - @Test - public void testFormatApplicationJsonFhir() throws Exception { - ourServlet.setDefaultResponseEncoding(EncodingEnum.XML); - - HttpGet httpGet = new HttpGet("http://localhost:" + ourPort + "/Patient/123?_format=application/json%2Bfhir"); - CloseableHttpResponse status = ourClient.execute(httpGet); - try { - String responseContent = IOUtils.toString(status.getEntity().getContent()); - ourLog.info(responseContent); - - assertEquals(200, status.getStatusLine().getStatusCode()); - assertEquals(VALUE_JSON, responseContent); - } finally { - IOUtils.closeQuietly(status); - } - } - - /** - * See #346 - */ - @Test - public void testFormatApplicationJsonFhirUnescaped() throws Exception { - ourServlet.setDefaultResponseEncoding(EncodingEnum.XML); - - // The plus isn't escaped here, and it should be.. but we'll be lenient - HttpGet httpGet = new HttpGet("http://localhost:" + ourPort + "/Patient/123?_format=application/json+fhir"); - CloseableHttpResponse status = ourClient.execute(httpGet); - try { - String responseContent = IOUtils.toString(status.getEntity().getContent()); - ourLog.info(responseContent); - - assertEquals(200, status.getStatusLine().getStatusCode()); - assertEquals(VALUE_JSON, responseContent); - } finally { - IOUtils.closeQuietly(status); - } - } - - @AfterAll - public static void afterClassClearContext() throws Exception { - JettyUtil.closeServer(ourServer); - TestUtil.randomizeLocaleAndTimezone(); - } - - @BeforeAll - public static void beforeClass() throws Exception { - ourServer = new Server(0); - - DummyPatientResourceProvider patientProvider = new DummyPatientResourceProvider(); - - ServletHandler proxyHandler = new ServletHandler(); - ourServlet = new RestfulServer(ourCtx); - ourServlet.setFhirContext(ourCtx); - ourServlet.setResourceProviders(patientProvider); - ServletHolder servletHolder = new ServletHolder(ourServlet); - proxyHandler.addServletWithMapping(servletHolder, "/*"); - ourServer.setHandler(proxyHandler); - JettyUtil.startServer(ourServer); - ourPort = JettyUtil.getPortForStartedServer(ourServer); - - PoolingHttpClientConnectionManager connectionManager = new PoolingHttpClientConnectionManager(5000, TimeUnit.MILLISECONDS); - HttpClientBuilder builder = HttpClientBuilder.create(); - builder.setConnectionManager(connectionManager); - ourClient = builder.build(); - - } - - public static class DummyPatientResourceProvider implements IResourceProvider { - - @Override - public Class getResourceType() { - return Patient.class; - } - - @Read(version = true) - public Patient read(@IdParam IdType theId) { - Patient p1 = new MyPatient(); - p1.setId("p1ReadId"); - p1.addIdentifier().setValue("p1ReadValue"); - return p1; - } - - } - - @ResourceDef(name = "Patient", profile = "http://foo_profile") - public static class MyPatient extends Patient { - - private static final long serialVersionUID = 1L; - - } - -} diff --git a/hapi-fhir-structures-dstu2.1/src/test/java/ca/uhn/fhir/rest/server/MetadataConformanceDstu2_1Test.java b/hapi-fhir-structures-dstu2.1/src/test/java/ca/uhn/fhir/rest/server/MetadataConformanceDstu2_1Test.java deleted file mode 100644 index b2d8553b8f2..00000000000 --- a/hapi-fhir-structures-dstu2.1/src/test/java/ca/uhn/fhir/rest/server/MetadataConformanceDstu2_1Test.java +++ /dev/null @@ -1,191 +0,0 @@ -package ca.uhn.fhir.rest.server; - -import ca.uhn.fhir.context.FhirContext; -import ca.uhn.fhir.rest.annotation.OptionalParam; -import ca.uhn.fhir.rest.annotation.ResourceParam; -import ca.uhn.fhir.rest.annotation.Search; -import ca.uhn.fhir.rest.annotation.Validate; -import ca.uhn.fhir.rest.api.EncodingEnum; -import ca.uhn.fhir.rest.api.MethodOutcome; -import ca.uhn.fhir.rest.param.StringParam; -import ca.uhn.fhir.test.utilities.JettyUtil; -import ca.uhn.fhir.util.TestUtil; -import ca.uhn.fhir.util.VersionUtil; -import org.apache.commons.io.IOUtils; -import org.apache.http.client.methods.CloseableHttpResponse; -import org.apache.http.client.methods.HttpGet; -import org.apache.http.client.methods.HttpPost; -import org.apache.http.client.methods.HttpRequestBase; -import org.apache.http.impl.client.CloseableHttpClient; -import org.apache.http.impl.client.HttpClientBuilder; -import org.apache.http.impl.conn.PoolingHttpClientConnectionManager; -import org.eclipse.jetty.server.Server; -import org.eclipse.jetty.servlet.ServletHandler; -import org.eclipse.jetty.servlet.ServletHolder; -import org.hl7.fhir.dstu2016may.hapi.rest.server.ServerConformanceProvider; -import org.hl7.fhir.dstu2016may.model.Patient; -import org.junit.jupiter.api.AfterAll; -import org.junit.jupiter.api.BeforeAll; -import org.junit.jupiter.api.Test; - -import java.nio.charset.StandardCharsets; -import java.util.List; -import java.util.concurrent.TimeUnit; - -import static org.hamcrest.MatcherAssert.assertThat; -import static org.hamcrest.Matchers.containsString; -import static org.hamcrest.Matchers.not; -import static org.hamcrest.Matchers.stringContainsInOrder; -import static org.junit.jupiter.api.Assertions.assertEquals; - -@SuppressWarnings("deprecation") -public class MetadataConformanceDstu2_1Test { - - private static CloseableHttpClient ourClient; - private static FhirContext ourCtx = FhirContext.forDstu2_1(); - private static int ourPort; - private static Server ourServer; - private static RestfulServer ourServlet; - private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(MetadataConformanceDstu2_1Test.class); - - - @Test - public void testSummary() throws Exception { - String output; - - // With - HttpRequestBase httpPost = new HttpGet("http://localhost:" + ourPort + "/metadata?_summary=true&_pretty=true"); - CloseableHttpResponse status = ourClient.execute(httpPost); - try { - output = IOUtils.toString(status.getEntity().getContent(), StandardCharsets.UTF_8); - assertEquals(200, status.getStatusLine().getStatusCode()); - ourLog.info(output); - assertThat(output, containsString("", "SUBSETTED", "")); - assertThat(output, not(stringContainsInOrder("searchParam"))); - } finally { - IOUtils.closeQuietly(status.getEntity().getContent()); - } - - // Without - httpPost = new HttpGet("http://localhost:" + ourPort + "/metadata?_pretty=true"); - status = ourClient.execute(httpPost); - try { - output = IOUtils.toString(status.getEntity().getContent(), StandardCharsets.UTF_8); - assertEquals(200, status.getStatusLine().getStatusCode()); - ourLog.info(output); - assertThat(output, containsString("", "SUBSETTED", ""))); - assertThat(output, stringContainsInOrder("searchParam")); - } finally { - IOUtils.closeQuietly(status.getEntity().getContent()); - } - } - - @Test - public void testElements() throws Exception { - String output; - - HttpRequestBase httpPost = new HttpGet("http://localhost:" + ourPort + "/metadata?_elements=fhirVersion&_pretty=true"); - CloseableHttpResponse status = ourClient.execute(httpPost); - try { - output = IOUtils.toString(status.getEntity().getContent(), StandardCharsets.UTF_8); - assertEquals(200, status.getStatusLine().getStatusCode()); - ourLog.info(output); - assertThat(output, containsString("", "SUBSETTED", "")); - } finally { - IOUtils.closeQuietly(status.getEntity().getContent()); - } - } - - @Test - public void testHttpMethods() throws Exception { - String output; - - HttpRequestBase httpPost = new HttpGet("http://localhost:" + ourPort + "/metadata"); - CloseableHttpResponse status = ourClient.execute(httpPost); - try { - output = IOUtils.toString(status.getEntity().getContent(), StandardCharsets.UTF_8); - assertEquals(200, status.getStatusLine().getStatusCode()); - assertThat(output, containsString(" getResourceType() { - return Patient.class; - } - - @Search - public List search(@OptionalParam(name="foo") StringParam theFoo) { - throw new UnsupportedOperationException(); - } - - @Validate() - public MethodOutcome validate(@ResourceParam Patient theResource) { - return new MethodOutcome(); - } - } - -} diff --git a/hapi-fhir-structures-dstu2.1/src/test/java/ca/uhn/fhir/rest/server/OperationServerDstu2_1Test.java b/hapi-fhir-structures-dstu2.1/src/test/java/ca/uhn/fhir/rest/server/OperationServerDstu2_1Test.java deleted file mode 100644 index 2075b04e46e..00000000000 --- a/hapi-fhir-structures-dstu2.1/src/test/java/ca/uhn/fhir/rest/server/OperationServerDstu2_1Test.java +++ /dev/null @@ -1,786 +0,0 @@ -package ca.uhn.fhir.rest.server; - -import ca.uhn.fhir.context.FhirContext; -import ca.uhn.fhir.rest.annotation.IdParam; -import ca.uhn.fhir.rest.annotation.Operation; -import ca.uhn.fhir.rest.annotation.OperationParam; -import ca.uhn.fhir.rest.annotation.Read; -import ca.uhn.fhir.rest.api.Constants; -import ca.uhn.fhir.rest.api.EncodingEnum; -import ca.uhn.fhir.rest.api.server.IBundleProvider; -import ca.uhn.fhir.rest.client.api.IGenericClient; -import ca.uhn.fhir.rest.client.interceptor.LoggingInterceptor; -import ca.uhn.fhir.test.utilities.JettyUtil; -import ca.uhn.fhir.util.TestUtil; -import org.apache.commons.io.IOUtils; -import org.apache.http.HttpResponse; -import org.apache.http.client.methods.CloseableHttpResponse; -import org.apache.http.client.methods.HttpGet; -import org.apache.http.client.methods.HttpPost; -import org.apache.http.entity.ContentType; -import org.apache.http.entity.StringEntity; -import org.apache.http.impl.client.CloseableHttpClient; -import org.apache.http.impl.client.HttpClientBuilder; -import org.apache.http.impl.conn.PoolingHttpClientConnectionManager; -import org.eclipse.jetty.server.Server; -import org.eclipse.jetty.servlet.ServletHandler; -import org.eclipse.jetty.servlet.ServletHolder; -import org.hl7.fhir.dstu2016may.model.Bundle; -import org.hl7.fhir.dstu2016may.model.Conformance; -import org.hl7.fhir.dstu2016may.model.Conformance.ConformanceRestOperationComponent; -import org.hl7.fhir.dstu2016may.model.DateTimeType; -import org.hl7.fhir.dstu2016may.model.IdType; -import org.hl7.fhir.dstu2016may.model.IntegerType; -import org.hl7.fhir.dstu2016may.model.Money; -import org.hl7.fhir.dstu2016may.model.OperationDefinition; -import org.hl7.fhir.dstu2016may.model.OperationDefinition.OperationParameterUse; -import org.hl7.fhir.dstu2016may.model.Parameters; -import org.hl7.fhir.dstu2016may.model.Patient; -import org.hl7.fhir.dstu2016may.model.StringType; -import org.hl7.fhir.dstu2016may.model.UnsignedIntType; -import org.hl7.fhir.instance.model.api.IBaseResource; -import org.junit.jupiter.api.AfterAll; -import org.junit.jupiter.api.BeforeAll; -import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.Test; - -import java.nio.charset.StandardCharsets; -import java.util.ArrayList; -import java.util.List; -import java.util.concurrent.TimeUnit; - -import static org.hamcrest.MatcherAssert.assertThat; -import static org.hamcrest.Matchers.containsInRelativeOrder; -import static org.hamcrest.Matchers.containsString; -import static org.hamcrest.Matchers.greaterThan; -import static org.hamcrest.Matchers.startsWith; -import static org.junit.jupiter.api.Assertions.assertEquals; -import static org.junit.jupiter.api.Assertions.assertNull; - -public class OperationServerDstu2_1Test { - private static CloseableHttpClient ourClient; - private static FhirContext ourCtx; - - private static IdType ourLastId; - private static String ourLastMethod; - private static StringType ourLastParam1; - private static Patient ourLastParam2; - private static List ourLastParam3; - private static Money ourLastParamMoney1; - private static UnsignedIntType ourLastParamUnsignedInt1; - private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(OperationServerDstu2_1Test.class); - private static int ourPort; - private static Server ourServer; - private IGenericClient myFhirClient; - - @BeforeEach - public void before() { - ourLastParam1 = null; - ourLastParam2 = null; - ourLastParam3 = null; - ourLastParamUnsignedInt1 = null; - ourLastParamMoney1 = null; - ourLastId = null; - ourLastMethod = ""; - - myFhirClient = ourCtx.newRestfulGenericClient("http://localhost:" + ourPort); - } - - - @Test - public void testConformance() { - LoggingInterceptor loggingInterceptor = new LoggingInterceptor(); - loggingInterceptor.setLogResponseBody(true); - myFhirClient.registerInterceptor(loggingInterceptor); - - Conformance p = myFhirClient.fetchConformance().ofType(Conformance.class).prettyPrint().execute(); - ourLog.debug(ourCtx.newXmlParser().setPrettyPrint(true).encodeResourceToString(p)); - - List ops = p.getRest().get(0).getOperation(); - assertThat(ops.size(), greaterThan(1)); - - List opNames = toOpNames(ops); - assertThat(opNames, containsInRelativeOrder("OP_TYPE")); - -// OperationDefinition def = (OperationDefinition) ops.get(opNames.indexOf("OP_TYPE")).getDefinition().getResource(); - OperationDefinition def = myFhirClient.read().resource(OperationDefinition.class).withId(ops.get(opNames.indexOf("OP_TYPE")).getDefinition().getReferenceElement()).execute(); - assertEquals("OP_TYPE", def.getCode()); - } - - /** - * See #380 - */ - @Test - public void testOperationDefinition() { - OperationDefinition def = myFhirClient.read().resource(OperationDefinition.class).withId("OperationDefinition/Patient-t-OP_TYPE").execute(); - - ourLog.debug(ourCtx.newXmlParser().setPrettyPrint(true).encodeResourceToString(def)); - -// @OperationParam(name="PARAM1") StringType theParam1, -// @OperationParam(name="PARAM2") Patient theParam2, -// @OperationParam(name="PARAM3", min=2, max=5) List theParam3, -// @OperationParam(name="PARAM4", min=1) List theParam4, - - assertEquals(4, def.getParameter().size()); - assertEquals("PARAM1", def.getParameter().get(0).getName()); - assertEquals(OperationParameterUse.IN, def.getParameter().get(0).getUse()); - assertEquals(0, def.getParameter().get(0).getMin()); - assertEquals("1", def.getParameter().get(0).getMax()); - - assertEquals("PARAM2", def.getParameter().get(1).getName()); - assertEquals(OperationParameterUse.IN, def.getParameter().get(1).getUse()); - assertEquals(0, def.getParameter().get(1).getMin()); - assertEquals("1", def.getParameter().get(1).getMax()); - - assertEquals("PARAM3", def.getParameter().get(2).getName()); - assertEquals(OperationParameterUse.IN, def.getParameter().get(2).getUse()); - assertEquals(2, def.getParameter().get(2).getMin()); - assertEquals("5", def.getParameter().get(2).getMax()); - - assertEquals("PARAM4", def.getParameter().get(3).getName()); - assertEquals(OperationParameterUse.IN, def.getParameter().get(3).getUse()); - assertEquals(1, def.getParameter().get(3).getMin()); - assertEquals("*", def.getParameter().get(3).getMax()); - - } - - private List toOpNames(List theOps) { - ArrayList retVal = new ArrayList(); - for (ConformanceRestOperationComponent next : theOps) { - retVal.add(next.getName()); - } - return retVal; - } - - @Test - public void testInstanceEverythingGet() throws Exception { - - // Try with a GET - HttpGet httpGet = new HttpGet("http://localhost:" + ourPort + "/Patient/123/$everything"); - CloseableHttpResponse status = ourClient.execute(httpGet); - - assertEquals(200, status.getStatusLine().getStatusCode()); - String response = IOUtils.toString(status.getEntity().getContent(), StandardCharsets.UTF_8); - IOUtils.closeQuietly(status.getEntity().getContent()); - - assertEquals("instance $everything", ourLastMethod); - assertThat(response, startsWith(" getResourceType() { - return Patient.class; - } - - //@formatter:off - @Operation(name="$OP_INSTANCE") - public Parameters opInstance( - @IdParam IdType theId, - @OperationParam(name="PARAM1") StringType theParam1, - @OperationParam(name="PARAM2") Patient theParam2 - ) { - //@formatter:on - - ourLastMethod = "$OP_INSTANCE"; - ourLastId = theId; - ourLastParam1 = theParam1; - ourLastParam2 = theParam2; - - Parameters retVal = new Parameters(); - retVal.addParameter().setName("RET1").setValue(new StringType("RETVAL1")); - return retVal; - } - - //@formatter:off - @Operation(name="$OP_INSTANCE_OR_TYPE") - public Parameters opInstanceOrType( - @IdParam(optional=true) IdType theId, - @OperationParam(name="PARAM1") StringType theParam1, - @OperationParam(name="PARAM2") Patient theParam2 - ) { - //@formatter:on - - ourLastMethod = "$OP_INSTANCE_OR_TYPE"; - ourLastId = theId; - ourLastParam1 = theParam1; - ourLastParam2 = theParam2; - - Parameters retVal = new Parameters(); - retVal.addParameter().setName("RET1").setValue(new StringType("RETVAL1")); - return retVal; - } - - //@formatter:off - @Operation(name="$OP_PROFILE_DT2", idempotent=true) - public Bundle opProfileType( - @OperationParam(name="PARAM1") Money theParam1 - ) { - //@formatter:on - - ourLastMethod = "$OP_PROFILE_DT2"; - ourLastParamMoney1 = theParam1; - - Bundle retVal = new Bundle(); - retVal.addEntry().getResponse().setStatus("100"); - return retVal; - } - - //@formatter:off - @Operation(name="$OP_PROFILE_DT", idempotent=true) - public Bundle opProfileType( - @OperationParam(name="PARAM1") UnsignedIntType theParam1 - ) { - //@formatter:on - - ourLastMethod = "$OP_PROFILE_DT"; - ourLastParamUnsignedInt1 = theParam1; - - Bundle retVal = new Bundle(); - retVal.addEntry().getResponse().setStatus("100"); - return retVal; - } - - //@formatter:off - @SuppressWarnings("unused") - @Operation(name="$OP_TYPE", idempotent=true) - public Parameters opType( - @OperationParam(name="PARAM1") StringType theParam1, - @OperationParam(name="PARAM2") Patient theParam2, - @OperationParam(name="PARAM3", min=2, max=5) List theParam3, - @OperationParam(name="PARAM4", min=1) List theParam4 - ) { - //@formatter:on - - ourLastMethod = "$OP_TYPE"; - ourLastParam1 = theParam1; - ourLastParam2 = theParam2; - - Parameters retVal = new Parameters(); - retVal.addParameter().setName("RET1").setValue(new StringType("RETVAL1")); - return retVal; - } - - //@formatter:off - @Operation(name="$OP_TYPE_ONLY_STRING", idempotent=true) - public Parameters opTypeOnlyString( - @OperationParam(name="PARAM1") StringType theParam1 - ) { - //@formatter:on - - ourLastMethod = "$OP_TYPE"; - ourLastParam1 = theParam1; - - Parameters retVal = new Parameters(); - retVal.addParameter().setName("RET1").setValue(new StringType("RETVAL1")); - return retVal; - } - - //@formatter:off - @Operation(name="$OP_TYPE_RET_BUNDLE") - public Bundle opTypeRetBundle( - @OperationParam(name="PARAM1") StringType theParam1, - @OperationParam(name="PARAM2") Patient theParam2 - ) { - //@formatter:on - - ourLastMethod = "$OP_TYPE_RET_BUNDLE"; - ourLastParam1 = theParam1; - ourLastParam2 = theParam2; - - Bundle retVal = new Bundle(); - retVal.addEntry().getResponse().setStatus("100"); - return retVal; - } - - @Operation(name = "$everything", idempotent=true) - public Bundle patientEverything(@IdParam IdType thePatientId) { - ourLastMethod = "instance $everything"; - ourLastId = thePatientId; - return new Bundle(); - } - - /** - * Just to make sure this method doesn't "steal" calls - */ - @Read - public Patient read(@IdParam IdType theId) { - ourLastMethod = "read"; - Patient retVal = new Patient(); - retVal.setId(theId); - return retVal; - } - - } - - public static class PlainProvider { - - //@formatter:off - @Operation(name="$OP_INSTANCE_BUNDLE_PROVIDER", idempotent=true) - public IBundleProvider opInstanceReturnsBundleProvider() { - ourLastMethod = "$OP_INSTANCE_BUNDLE_PROVIDER"; - - List resources = new ArrayList<>(); - for (int i =0; i < 100;i++) { - Patient p = new Patient(); - p.setId("Patient/" + i); - p.addName().addFamily("Patient " + i); - resources.add(p); - } - - return new SimpleBundleProvider(resources); - } - - //@formatter:off - @Operation(name="$OP_SERVER") - public Parameters opServer( - @OperationParam(name="PARAM1") StringType theParam1, - @OperationParam(name="PARAM2") Patient theParam2 - ) { - //@formatter:on - - ourLastMethod = "$OP_SERVER"; - ourLastParam1 = theParam1; - ourLastParam2 = theParam2; - - Parameters retVal = new Parameters(); - retVal.addParameter().setName("RET1").setValue(new StringType("RETVAL1")); - return retVal; - } - - //@formatter:off - @Operation(name="$OP_SERVER_LIST_PARAM") - public Parameters opServerListParam( - @OperationParam(name="PARAM2") Patient theParam2, - @OperationParam(name="PARAM3") List theParam3 - ) { - //@formatter:on - - ourLastMethod = "$OP_SERVER_LIST_PARAM"; - ourLastParam2 = theParam2; - ourLastParam3 = theParam3; - - Parameters retVal = new Parameters(); - retVal.addParameter().setName("RET1").setValue(new StringType("RETVAL1")); - return retVal; - } - - } - -} diff --git a/hapi-fhir-structures-dstu2.1/src/test/java/ca/uhn/fhir/rest/server/OperationServerWithSearchParamTypesDstu2_1Test.java b/hapi-fhir-structures-dstu2.1/src/test/java/ca/uhn/fhir/rest/server/OperationServerWithSearchParamTypesDstu2_1Test.java deleted file mode 100644 index d8054c0c07e..00000000000 --- a/hapi-fhir-structures-dstu2.1/src/test/java/ca/uhn/fhir/rest/server/OperationServerWithSearchParamTypesDstu2_1Test.java +++ /dev/null @@ -1,514 +0,0 @@ -package ca.uhn.fhir.rest.server; - -import ca.uhn.fhir.context.FhirContext; -import ca.uhn.fhir.rest.annotation.Operation; -import ca.uhn.fhir.rest.annotation.OperationParam; -import ca.uhn.fhir.rest.api.Constants; -import ca.uhn.fhir.rest.api.server.RequestDetails; -import ca.uhn.fhir.rest.param.StringAndListParam; -import ca.uhn.fhir.rest.param.StringOrListParam; -import ca.uhn.fhir.rest.param.StringParam; -import ca.uhn.fhir.rest.param.TokenAndListParam; -import ca.uhn.fhir.rest.param.TokenOrListParam; -import ca.uhn.fhir.rest.param.TokenParam; -import ca.uhn.fhir.rest.param.TokenParamModifier; -import ca.uhn.fhir.rest.server.servlet.ServletRequestDetails; -import ca.uhn.fhir.test.utilities.JettyUtil; -import ca.uhn.fhir.util.TestUtil; -import ca.uhn.fhir.util.UrlUtil; -import org.apache.commons.io.IOUtils; -import org.apache.http.HttpResponse; -import org.apache.http.client.methods.HttpGet; -import org.apache.http.client.methods.HttpPost; -import org.apache.http.entity.ContentType; -import org.apache.http.entity.StringEntity; -import org.apache.http.impl.client.CloseableHttpClient; -import org.apache.http.impl.client.HttpClientBuilder; -import org.apache.http.impl.conn.PoolingHttpClientConnectionManager; -import org.eclipse.jetty.server.Server; -import org.eclipse.jetty.servlet.ServletHandler; -import org.eclipse.jetty.servlet.ServletHolder; -import org.hl7.fhir.dstu2016may.hapi.rest.server.ServerConformanceProvider; -import org.hl7.fhir.dstu2016may.model.Conformance; -import org.hl7.fhir.dstu2016may.model.IdType; -import org.hl7.fhir.dstu2016may.model.OperationDefinition; -import org.hl7.fhir.dstu2016may.model.Parameters; -import org.hl7.fhir.dstu2016may.model.Patient; -import org.hl7.fhir.dstu2016may.model.StringType; -import org.hl7.fhir.instance.model.api.IBaseResource; -import org.junit.jupiter.api.AfterAll; -import org.junit.jupiter.api.BeforeAll; -import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.Test; - -import javax.servlet.ServletConfig; -import javax.servlet.http.HttpServletRequest; -import java.util.Collections; -import java.util.List; -import java.util.concurrent.TimeUnit; - -import static org.hamcrest.MatcherAssert.assertThat; -import static org.hamcrest.Matchers.stringContainsInOrder; -import static org.junit.jupiter.api.Assertions.assertEquals; -import static org.junit.jupiter.api.Assertions.assertTrue; -import static org.mockito.Mockito.mock; -import static org.mockito.Mockito.when; - -public class OperationServerWithSearchParamTypesDstu2_1Test { - private static CloseableHttpClient ourClient; - private static FhirContext ourCtx; - - private static String ourLastMethod; - private static List ourLastParamValStr; - private static List ourLastParamValTok; - private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(OperationServerWithSearchParamTypesDstu2_1Test.class); - private static int ourPort; - private static Server ourServer; - @BeforeEach - public void before() { - ourLastMethod = ""; - ourLastParamValStr = null; - ourLastParamValTok = null; - } - - private HttpServletRequest createHttpServletRequest() { - HttpServletRequest req = mock(HttpServletRequest.class); - when(req.getRequestURI()).thenReturn("/FhirStorm/fhir/Patient/_search"); - when(req.getServletPath()).thenReturn("/fhir"); - when(req.getRequestURL()).thenReturn(new StringBuffer().append("http://fhirstorm.dyndns.org:8080/FhirStorm/fhir/Patient/_search")); - when(req.getContextPath()).thenReturn("/FhirStorm"); - return req; - } - - private ServletConfig createServletConfig() { - ServletConfig sc = mock(ServletConfig.class); - when(sc.getServletContext()).thenReturn(null); - return sc; - } - - @Test - public void testAndListWithParameters() throws Exception { - Parameters p = new Parameters(); - p.addParameter().setName("valstr").setValue(new StringType("VALSTR1A,VALSTR1B")); - p.addParameter().setName("valstr").setValue(new StringType("VALSTR2A,VALSTR2B")); - p.addParameter().setName("valtok").setValue(new StringType("VALTOK1A|VALTOK1B")); - p.addParameter().setName("valtok").setValue(new StringType("VALTOK2A|VALTOK2B")); - String inParamsStr = ourCtx.newXmlParser().encodeResourceToString(p); - - HttpPost httpPost = new HttpPost("http://localhost:" + ourPort + "/Patient/$andlist"); - httpPost.setEntity(new StringEntity(inParamsStr, ContentType.create(Constants.CT_FHIR_XML, "UTF-8"))); - HttpResponse status = ourClient.execute(httpPost); - - assertEquals(200, status.getStatusLine().getStatusCode()); - String response = IOUtils.toString(status.getEntity().getContent()); - ourLog.info(response); - IOUtils.closeQuietly(status.getEntity().getContent()); - - assertEquals(2, ourLastParamValStr.size()); - assertEquals(2, ourLastParamValStr.get(0).getValuesAsQueryTokens().size()); - assertEquals("VALSTR1A", ourLastParamValStr.get(0).getValuesAsQueryTokens().get(0).getValue()); - assertEquals("VALSTR1B", ourLastParamValStr.get(0).getValuesAsQueryTokens().get(1).getValue()); - assertEquals("VALSTR2A", ourLastParamValStr.get(1).getValuesAsQueryTokens().get(0).getValue()); - assertEquals("VALSTR2B", ourLastParamValStr.get(1).getValuesAsQueryTokens().get(1).getValue()); - assertEquals(2, ourLastParamValTok.size()); - assertEquals(1, ourLastParamValTok.get(0).getValuesAsQueryTokens().size()); - assertEquals("VALTOK1A", ourLastParamValTok.get(0).getValuesAsQueryTokens().get(0).getSystem()); - assertEquals("VALTOK1B", ourLastParamValTok.get(0).getValuesAsQueryTokens().get(0).getValue()); - assertEquals("VALTOK2A", ourLastParamValTok.get(1).getValuesAsQueryTokens().get(0).getSystem()); - assertEquals("VALTOK2B", ourLastParamValTok.get(1).getValuesAsQueryTokens().get(0).getValue()); - assertEquals("type $orlist", ourLastMethod); - } - - @Test - public void testEscapedOperationName() throws Exception { - HttpGet httpGet = new HttpGet("http://localhost:" + ourPort + "/Patient/%24andlist?valstr=VALSTR1A,VALSTR1B&valstr=VALSTR2A,VALSTR2B&valtok=" + UrlUtil.escapeUrlParam("VALTOK1A|VALTOK1B") + "&valtok=" + UrlUtil.escapeUrlParam("VALTOK2A|VALTOK2B")); - HttpResponse status = ourClient.execute(httpGet); - - assertEquals(200, status.getStatusLine().getStatusCode()); - String response = IOUtils.toString(status.getEntity().getContent()); - ourLog.info(response); - IOUtils.closeQuietly(status.getEntity().getContent()); - - assertEquals(2, ourLastParamValStr.size()); - } - - @Test - public void testAndListWithUrl() throws Exception { - HttpGet httpGet = new HttpGet("http://localhost:" + ourPort + "/Patient/$andlist?valstr=VALSTR1A,VALSTR1B&valstr=VALSTR2A,VALSTR2B&valtok=" + UrlUtil.escapeUrlParam("VALTOK1A|VALTOK1B") + "&valtok=" + UrlUtil.escapeUrlParam("VALTOK2A|VALTOK2B")); - HttpResponse status = ourClient.execute(httpGet); - - assertEquals(200, status.getStatusLine().getStatusCode()); - String response = IOUtils.toString(status.getEntity().getContent()); - ourLog.info(response); - IOUtils.closeQuietly(status.getEntity().getContent()); - - assertEquals(2, ourLastParamValStr.size()); - assertEquals(2, ourLastParamValStr.get(0).getValuesAsQueryTokens().size()); - assertEquals("VALSTR1A", ourLastParamValStr.get(0).getValuesAsQueryTokens().get(0).getValue()); - assertEquals("VALSTR1B", ourLastParamValStr.get(0).getValuesAsQueryTokens().get(1).getValue()); - assertEquals("VALSTR2A", ourLastParamValStr.get(1).getValuesAsQueryTokens().get(0).getValue()); - assertEquals("VALSTR2B", ourLastParamValStr.get(1).getValuesAsQueryTokens().get(1).getValue()); - assertEquals(2, ourLastParamValTok.size()); - assertEquals(1, ourLastParamValTok.get(0).getValuesAsQueryTokens().size()); - assertEquals("VALTOK1A", ourLastParamValTok.get(0).getValuesAsQueryTokens().get(0).getSystem()); - assertEquals("VALTOK1B", ourLastParamValTok.get(0).getValuesAsQueryTokens().get(0).getValue()); - assertEquals("VALTOK2A", ourLastParamValTok.get(1).getValuesAsQueryTokens().get(0).getSystem()); - assertEquals("VALTOK2B", ourLastParamValTok.get(1).getValuesAsQueryTokens().get(0).getValue()); - assertEquals("type $orlist", ourLastMethod); - } - - @Test - public void testGenerateConformance() throws Exception { - RestfulServer rs = new RestfulServer(ourCtx); - rs.setProviders(new PatientProvider()); - - ServerConformanceProvider sc = new ServerConformanceProvider(rs); - rs.setServerConformanceProvider(sc); - - rs.init(createServletConfig()); - - Conformance conformance = sc.getServerConformance(createHttpServletRequest(), createRequestDetails(rs)); - - String conf = ourCtx.newXmlParser().setPrettyPrint(true).encodeResourceToString(conformance); - ourLog.info(conf); - //@formatter:off - assertThat(conf, stringContainsInOrder( - "", - "", - "", - "" - )); - assertThat(conf, stringContainsInOrder( - "", - "", - "" - )); - assertThat(conf, stringContainsInOrder( - "", - "", - "" - )); - //@formatter:on - - /* - * Check the operation definitions themselves - */ - OperationDefinition andListDef = sc.readOperationDefinition(new IdType("OperationDefinition/Patient-t-andlist"), createRequestDetails(rs)); - String def = ourCtx.newXmlParser().setPrettyPrint(true).encodeResourceToString(andListDef); - ourLog.info(def); - //@formatter:off - assertThat(def, stringContainsInOrder( - "", - "", - "", - "", - "", - "", - "", - "" - )); - //@formatter:on - - andListDef = sc.readOperationDefinition(new IdType("OperationDefinition/Patient-t-andlist-withnomax"), createRequestDetails(rs)); - def = ourCtx.newXmlParser().setPrettyPrint(true).encodeResourceToString(andListDef); - ourLog.info(def); - //@formatter:off - assertThat(def, stringContainsInOrder( - "", - "", - "", - "", - "", - "", - "", - "" - )); - //@formatter:on - - OperationDefinition orListDef = sc.readOperationDefinition(new IdType("OperationDefinition/Patient-t-orlist"), createRequestDetails(rs)); - def = ourCtx.newXmlParser().setPrettyPrint(true).encodeResourceToString(orListDef); - ourLog.info(def); - //@formatter:off - assertThat(def, stringContainsInOrder( - "", - "", - "", - "", - "", - "", - "", - "" - )); - //@formatter:on - - orListDef = sc.readOperationDefinition(new IdType("OperationDefinition/Patient-t-orlist-withnomax"), createRequestDetails(rs)); - def = ourCtx.newXmlParser().setPrettyPrint(true).encodeResourceToString(orListDef); - ourLog.info(def); - //@formatter:off - assertThat(def, stringContainsInOrder( - "", - "", - "", - "", - "", - "", - "", - "" - )); - //@formatter:on - - } - - @Test - public void testNonRepeatingWithParams() throws Exception { - Parameters p = new Parameters(); - p.addParameter().setName("valstr").setValue(new StringType("VALSTR")); - p.addParameter().setName("valtok").setValue(new StringType("VALTOKA|VALTOKB")); - String inParamsStr = ourCtx.newXmlParser().encodeResourceToString(p); - - HttpPost httpPost = new HttpPost("http://localhost:" + ourPort + "/Patient/$nonrepeating"); - httpPost.setEntity(new StringEntity(inParamsStr, ContentType.create(Constants.CT_FHIR_XML, "UTF-8"))); - HttpResponse status = ourClient.execute(httpPost); - - assertEquals(200, status.getStatusLine().getStatusCode()); - String response = IOUtils.toString(status.getEntity().getContent()); - ourLog.info(response); - IOUtils.closeQuietly(status.getEntity().getContent()); - - assertEquals(1, ourLastParamValStr.size()); - assertEquals(1, ourLastParamValStr.get(0).getValuesAsQueryTokens().size()); - assertEquals("VALSTR", ourLastParamValStr.get(0).getValuesAsQueryTokens().get(0).getValue()); - assertEquals(1, ourLastParamValTok.size()); - assertEquals(1, ourLastParamValTok.get(0).getValuesAsQueryTokens().size()); - assertEquals("VALTOKA", ourLastParamValTok.get(0).getValuesAsQueryTokens().get(0).getSystem()); - assertEquals("VALTOKB", ourLastParamValTok.get(0).getValuesAsQueryTokens().get(0).getValue()); - assertEquals("type $nonrepeating", ourLastMethod); - } - @Test - public void testNonRepeatingWithUrl() throws Exception { - HttpGet httpGet = new HttpGet("http://localhost:" + ourPort + "/Patient/$nonrepeating?valstr=VALSTR&valtok=" + UrlUtil.escapeUrlParam("VALTOKA|VALTOKB")); - HttpResponse status = ourClient.execute(httpGet); - - assertEquals(200, status.getStatusLine().getStatusCode()); - String response = IOUtils.toString(status.getEntity().getContent()); - ourLog.info(response); - IOUtils.closeQuietly(status.getEntity().getContent()); - - assertEquals(1, ourLastParamValStr.size()); - assertEquals(1, ourLastParamValStr.get(0).getValuesAsQueryTokens().size()); - assertEquals("VALSTR", ourLastParamValStr.get(0).getValuesAsQueryTokens().get(0).getValue()); - assertEquals(1, ourLastParamValTok.size()); - assertEquals(1, ourLastParamValTok.get(0).getValuesAsQueryTokens().size()); - assertEquals("VALTOKA", ourLastParamValTok.get(0).getValuesAsQueryTokens().get(0).getSystem()); - assertEquals("VALTOKB", ourLastParamValTok.get(0).getValuesAsQueryTokens().get(0).getValue()); - assertEquals("type $nonrepeating", ourLastMethod); - } - - @Test - public void testNonRepeatingWithUrlQualified() throws Exception { - HttpGet httpGet = new HttpGet("http://localhost:" + ourPort + "/Patient/$nonrepeating?valstr:exact=VALSTR&valtok:not=" + UrlUtil.escapeUrlParam("VALTOKA|VALTOKB")); - HttpResponse status = ourClient.execute(httpGet); - - assertEquals(200, status.getStatusLine().getStatusCode()); - String response = IOUtils.toString(status.getEntity().getContent()); - ourLog.info(response); - IOUtils.closeQuietly(status.getEntity().getContent()); - - assertEquals(1, ourLastParamValStr.size()); - assertEquals(1, ourLastParamValStr.get(0).getValuesAsQueryTokens().size()); - assertEquals("VALSTR", ourLastParamValStr.get(0).getValuesAsQueryTokens().get(0).getValue()); - assertTrue(ourLastParamValStr.get(0).getValuesAsQueryTokens().get(0).isExact()); - assertEquals(1, ourLastParamValTok.size()); - assertEquals(1, ourLastParamValTok.get(0).getValuesAsQueryTokens().size()); - assertEquals("VALTOKA", ourLastParamValTok.get(0).getValuesAsQueryTokens().get(0).getSystem()); - assertEquals("VALTOKB", ourLastParamValTok.get(0).getValuesAsQueryTokens().get(0).getValue()); - assertEquals(TokenParamModifier.NOT, ourLastParamValTok.get(0).getValuesAsQueryTokens().get(0).getModifier()); - assertEquals("type $nonrepeating", ourLastMethod); - } - - @Test - public void testOrListWithParameters() throws Exception { - Parameters p = new Parameters(); - p.addParameter().setName("valstr").setValue(new StringType("VALSTR1A,VALSTR1B")); - p.addParameter().setName("valstr").setValue(new StringType("VALSTR2A,VALSTR2B")); - p.addParameter().setName("valtok").setValue(new StringType("VALTOK1A|VALTOK1B")); - p.addParameter().setName("valtok").setValue(new StringType("VALTOK2A|VALTOK2B")); - String inParamsStr = ourCtx.newXmlParser().encodeResourceToString(p); - - HttpPost httpPost = new HttpPost("http://localhost:" + ourPort + "/Patient/$orlist"); - httpPost.setEntity(new StringEntity(inParamsStr, ContentType.create(Constants.CT_FHIR_XML, "UTF-8"))); - HttpResponse status = ourClient.execute(httpPost); - - assertEquals(200, status.getStatusLine().getStatusCode()); - String response = IOUtils.toString(status.getEntity().getContent()); - ourLog.info(response); - IOUtils.closeQuietly(status.getEntity().getContent()); - - assertEquals(2, ourLastParamValStr.size()); - assertEquals(2, ourLastParamValStr.get(0).getValuesAsQueryTokens().size()); - assertEquals("VALSTR1A", ourLastParamValStr.get(0).getValuesAsQueryTokens().get(0).getValue()); - assertEquals("VALSTR1B", ourLastParamValStr.get(0).getValuesAsQueryTokens().get(1).getValue()); - assertEquals("VALSTR2A", ourLastParamValStr.get(1).getValuesAsQueryTokens().get(0).getValue()); - assertEquals("VALSTR2B", ourLastParamValStr.get(1).getValuesAsQueryTokens().get(1).getValue()); - assertEquals(2, ourLastParamValTok.size()); - assertEquals(1, ourLastParamValTok.get(0).getValuesAsQueryTokens().size()); - assertEquals("VALTOK1A", ourLastParamValTok.get(0).getValuesAsQueryTokens().get(0).getSystem()); - assertEquals("VALTOK1B", ourLastParamValTok.get(0).getValuesAsQueryTokens().get(0).getValue()); - assertEquals("VALTOK2A", ourLastParamValTok.get(1).getValuesAsQueryTokens().get(0).getSystem()); - assertEquals("VALTOK2B", ourLastParamValTok.get(1).getValuesAsQueryTokens().get(0).getValue()); - assertEquals("type $orlist", ourLastMethod); - } - - @Test - public void testOrListWithUrl() throws Exception { - HttpGet httpGet = new HttpGet("http://localhost:" + ourPort + "/Patient/$orlist?valstr=VALSTR1A,VALSTR1B&valstr=VALSTR2A,VALSTR2B&valtok=" + UrlUtil.escapeUrlParam("VALTOK1A|VALTOK1B") + "&valtok=" + UrlUtil.escapeUrlParam("VALTOK2A|VALTOK2B")); - HttpResponse status = ourClient.execute(httpGet); - - assertEquals(200, status.getStatusLine().getStatusCode()); - String response = IOUtils.toString(status.getEntity().getContent()); - ourLog.info(response); - IOUtils.closeQuietly(status.getEntity().getContent()); - - assertEquals(2, ourLastParamValStr.size()); - assertEquals(2, ourLastParamValStr.get(0).getValuesAsQueryTokens().size()); - assertEquals("VALSTR1A", ourLastParamValStr.get(0).getValuesAsQueryTokens().get(0).getValue()); - assertEquals("VALSTR1B", ourLastParamValStr.get(0).getValuesAsQueryTokens().get(1).getValue()); - assertEquals("VALSTR2A", ourLastParamValStr.get(1).getValuesAsQueryTokens().get(0).getValue()); - assertEquals("VALSTR2B", ourLastParamValStr.get(1).getValuesAsQueryTokens().get(1).getValue()); - assertEquals(2, ourLastParamValTok.size()); - assertEquals(1, ourLastParamValTok.get(0).getValuesAsQueryTokens().size()); - assertEquals("VALTOK1A", ourLastParamValTok.get(0).getValuesAsQueryTokens().get(0).getSystem()); - assertEquals("VALTOK1B", ourLastParamValTok.get(0).getValuesAsQueryTokens().get(0).getValue()); - assertEquals("VALTOK2A", ourLastParamValTok.get(1).getValuesAsQueryTokens().get(0).getSystem()); - assertEquals("VALTOK2B", ourLastParamValTok.get(1).getValuesAsQueryTokens().get(0).getValue()); - assertEquals("type $orlist", ourLastMethod); - } - - @AfterAll - public static void afterClassClearContext() throws Exception { - JettyUtil.closeServer(ourServer); - TestUtil.randomizeLocaleAndTimezone(); - } - @BeforeAll - public static void beforeClass() throws Exception { - ourCtx = FhirContext.forDstu2_1(); - ourServer = new Server(0); - - ServletHandler proxyHandler = new ServletHandler(); - RestfulServer servlet = new RestfulServer(ourCtx); - - servlet.setPagingProvider(new FifoMemoryPagingProvider(10).setDefaultPageSize(2)); - - servlet.setFhirContext(ourCtx); - servlet.setResourceProviders(new PatientProvider()); - ServletHolder servletHolder = new ServletHolder(servlet); - proxyHandler.addServletWithMapping(servletHolder, "/*"); - ourServer.setHandler(proxyHandler); - JettyUtil.startServer(ourServer); - ourPort = JettyUtil.getPortForStartedServer(ourServer); - - PoolingHttpClientConnectionManager connectionManager = new PoolingHttpClientConnectionManager(5000, TimeUnit.MILLISECONDS); - HttpClientBuilder builder = HttpClientBuilder.create(); - builder.setConnectionManager(connectionManager); - ourClient = builder.build(); - - } - - public static class PatientProvider implements IResourceProvider { - - - @Operation(name = "$andlist", idempotent = true) - public Parameters andlist( - //@formatter:off - @OperationParam(name="valstr", max=10) StringAndListParam theValStr, - @OperationParam(name="valtok", max=10) TokenAndListParam theValTok - //@formatter:on - ) { - ourLastMethod = "type $orlist"; - ourLastParamValStr = theValStr.getValuesAsQueryTokens(); - ourLastParamValTok = theValTok.getValuesAsQueryTokens(); - - return createEmptyParams(); - } - - @Operation(name = "$andlist-withnomax", idempotent = true) - public Parameters andlistWithNoMax( - //@formatter:off - @OperationParam(name="valstr") StringAndListParam theValStr, - @OperationParam(name="valtok") TokenAndListParam theValTok - //@formatter:on - ) { - ourLastMethod = "type $orlist"; - ourLastParamValStr = theValStr.getValuesAsQueryTokens(); - ourLastParamValTok = theValTok.getValuesAsQueryTokens(); - - return createEmptyParams(); - } - - /** - * Just so we have something to return - */ - private Parameters createEmptyParams() { - Parameters retVal = new Parameters(); - retVal.setId("100"); - return retVal; - } - - @Override - public Class getResourceType() { - return Patient.class; - } - - @Operation(name = "$nonrepeating", idempotent = true) - public Parameters nonrepeating( - //@formatter:off - @OperationParam(name="valstr") StringParam theValStr, - @OperationParam(name="valtok") TokenParam theValTok - //@formatter:on - ) { - ourLastMethod = "type $nonrepeating"; - ourLastParamValStr = Collections.singletonList(new StringOrListParam().add(theValStr)); - ourLastParamValTok = Collections.singletonList(new TokenOrListParam().add(theValTok)); - - return createEmptyParams(); - } - - @Operation(name = "$orlist", idempotent = true) - public Parameters orlist( - //@formatter:off - @OperationParam(name="valstr", max=10) List theValStr, - @OperationParam(name="valtok", max=10) List theValTok - //@formatter:on - ) { - ourLastMethod = "type $orlist"; - ourLastParamValStr = theValStr; - ourLastParamValTok = theValTok; - - return createEmptyParams(); - } - - @Operation(name = "$orlist-withnomax", idempotent = true) - public Parameters orlistWithNoMax( - //@formatter:off - @OperationParam(name="valstr") List theValStr, - @OperationParam(name="valtok") List theValTok - //@formatter:on - ) { - ourLastMethod = "type $orlist"; - ourLastParamValStr = theValStr; - ourLastParamValTok = theValTok; - - return createEmptyParams(); - } - - } - - - private RequestDetails createRequestDetails(RestfulServer theServer) { - ServletRequestDetails retVal = new ServletRequestDetails(); - retVal.setServer(theServer); - return retVal; - } - -} - diff --git a/hapi-fhir-structures-dstu2.1/src/test/java/ca/uhn/fhir/rest/server/PatchDstu2_1Test.java b/hapi-fhir-structures-dstu2.1/src/test/java/ca/uhn/fhir/rest/server/PatchDstu2_1Test.java deleted file mode 100644 index 970b61397e7..00000000000 --- a/hapi-fhir-structures-dstu2.1/src/test/java/ca/uhn/fhir/rest/server/PatchDstu2_1Test.java +++ /dev/null @@ -1,188 +0,0 @@ -package ca.uhn.fhir.rest.server; - -import ca.uhn.fhir.context.FhirContext; -import ca.uhn.fhir.i18n.Msg; -import ca.uhn.fhir.rest.annotation.IdParam; -import ca.uhn.fhir.rest.annotation.Patch; -import ca.uhn.fhir.rest.annotation.ResourceParam; -import ca.uhn.fhir.rest.api.Constants; -import ca.uhn.fhir.rest.api.EncodingEnum; -import ca.uhn.fhir.rest.api.PatchTypeEnum; -import ca.uhn.fhir.test.utilities.JettyUtil; -import ca.uhn.fhir.util.TestUtil; -import org.apache.commons.io.IOUtils; -import org.apache.http.client.methods.CloseableHttpResponse; -import org.apache.http.client.methods.HttpPatch; -import org.apache.http.entity.ContentType; -import org.apache.http.entity.StringEntity; -import org.apache.http.impl.client.CloseableHttpClient; -import org.apache.http.impl.client.HttpClientBuilder; -import org.apache.http.impl.conn.PoolingHttpClientConnectionManager; -import org.eclipse.jetty.server.Server; -import org.eclipse.jetty.servlet.ServletHandler; -import org.eclipse.jetty.servlet.ServletHolder; -import org.hl7.fhir.dstu2016may.model.IdType; -import org.hl7.fhir.dstu2016may.model.OperationOutcome; -import org.hl7.fhir.dstu2016may.model.Patient; -import org.hl7.fhir.instance.model.api.IBaseResource; -import org.junit.jupiter.api.AfterAll; -import org.junit.jupiter.api.BeforeAll; -import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.Test; - -import java.nio.charset.StandardCharsets; -import java.util.concurrent.TimeUnit; - -import static org.junit.jupiter.api.Assertions.assertEquals; - -public class PatchDstu2_1Test { - - private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(PatchDstu2_1Test.class); - private static CloseableHttpClient ourClient; - private static FhirContext ourCtx = FhirContext.forDstu2_1(); - private static int ourPort; - private static Server ourServer; - private static String ourLastMethod; - private static PatchTypeEnum ourLastPatchType; - private static String ourLastBody; - private static IdType ourLastId; - - @BeforeEach - public void before() { - ourLastMethod = null; - ourLastBody = null; - ourLastId = null; - } - - @Test - public void testPatchValidJson() throws Exception { - String requestContents = "[ { \"op\": \"add\", \"path\": \"/a/b/c\", \"value\": [ \"foo\", \"bar\" ] } ]"; - HttpPatch httpPatch = new HttpPatch("http://localhost:" + ourPort + "/Patient/123"); - httpPatch.setEntity(new StringEntity(requestContents, ContentType.parse(Constants.CT_JSON_PATCH))); - httpPatch.addHeader(Constants.HEADER_PREFER, Constants.HEADER_PREFER_RETURN + '=' + Constants.HEADER_PREFER_RETURN_OPERATION_OUTCOME); - try (CloseableHttpResponse status = ourClient.execute(httpPatch)) { - String responseContent = IOUtils.toString(status.getEntity().getContent(), StandardCharsets.UTF_8); - ourLog.info(responseContent); - assertEquals(200, status.getStatusLine().getStatusCode()); - assertEquals("
    OK
    ", responseContent); - } - - assertEquals("patientPatch", ourLastMethod); - assertEquals("Patient/123", ourLastId.getValue()); - assertEquals(requestContents, ourLastBody); - assertEquals(PatchTypeEnum.JSON_PATCH, ourLastPatchType); - } - - @Test - public void testPatchValidXml() throws Exception { - String requestContents = ""; - HttpPatch httpPatch = new HttpPatch("http://localhost:" + ourPort + "/Patient/123"); - httpPatch.addHeader(Constants.HEADER_PREFER, Constants.HEADER_PREFER_RETURN + "=" + Constants.HEADER_PREFER_RETURN_OPERATION_OUTCOME); - httpPatch.setEntity(new StringEntity(requestContents, ContentType.parse(Constants.CT_XML_PATCH))); - - try (CloseableHttpResponse status = ourClient.execute(httpPatch)) { - String responseContent = IOUtils.toString(status.getEntity().getContent(), StandardCharsets.UTF_8); - ourLog.info(responseContent); - assertEquals(200, status.getStatusLine().getStatusCode()); - assertEquals("
    OK
    ", responseContent); - } - - assertEquals("patientPatch", ourLastMethod); - assertEquals("Patient/123", ourLastId.getValue()); - assertEquals(requestContents, ourLastBody); - assertEquals(PatchTypeEnum.XML_PATCH, ourLastPatchType); - } - - @Test - public void testPatchValidJsonWithCharset() throws Exception { - String requestContents = "[ { \"op\": \"add\", \"path\": \"/a/b/c\", \"value\": [ \"foo\", \"bar\" ] } ]"; - HttpPatch httpPatch = new HttpPatch("http://localhost:" + ourPort + "/Patient/123"); - httpPatch.setEntity(new StringEntity(requestContents, ContentType.parse(Constants.CT_JSON_PATCH + Constants.CHARSET_UTF8_CTSUFFIX))); - CloseableHttpResponse status = ourClient.execute(httpPatch); - - try { - String responseContent = IOUtils.toString(status.getEntity().getContent(), StandardCharsets.UTF_8); - ourLog.info(responseContent); - assertEquals(200, status.getStatusLine().getStatusCode()); - } finally { - IOUtils.closeQuietly(status.getEntity().getContent()); - } - - assertEquals("patientPatch", ourLastMethod); - assertEquals("Patient/123", ourLastId.getValue()); - assertEquals(requestContents, ourLastBody); - } - - @Test - public void testPatchInvalidMimeType() throws Exception { - String requestContents = "[ { \"op\": \"add\", \"path\": \"/a/b/c\", \"value\": [ \"foo\", \"bar\" ] } ]"; - HttpPatch httpPatch = new HttpPatch("http://localhost:" + ourPort + "/Patient/123"); - httpPatch.setEntity(new StringEntity(requestContents, ContentType.parse("text/plain; charset=UTF-8"))); - CloseableHttpResponse status = ourClient.execute(httpPatch); - - try { - String responseContent = IOUtils.toString(status.getEntity().getContent(), StandardCharsets.UTF_8); - ourLog.info(responseContent); - assertEquals(400, status.getStatusLine().getStatusCode()); - assertEquals("", responseContent); - } finally { - IOUtils.closeQuietly(status.getEntity().getContent()); - } - - } - - public static class DummyPatientResourceProvider implements IResourceProvider { - - - @Override - public Class getResourceType() { - return Patient.class; - } - - //@formatter:off - @Patch - public OperationOutcome patientPatch(@IdParam IdType theId, PatchTypeEnum thePatchType, @ResourceParam String theBody) { - ourLastMethod = "patientPatch"; - ourLastBody = theBody; - ourLastId = theId; - ourLastPatchType = thePatchType; - OperationOutcome retVal = new OperationOutcome(); - retVal.getText().setDivAsString("
    OK
    "); - return retVal; - } - //@formatter:on - - } - - @AfterAll - public static void afterClassClearContext() throws Exception { - JettyUtil.closeServer(ourServer); - TestUtil.randomizeLocaleAndTimezone(); - } - - @BeforeAll - public static void beforeClass() throws Exception { - ourServer = new Server(0); - - DummyPatientResourceProvider patientProvider = new DummyPatientResourceProvider(); - - ServletHandler proxyHandler = new ServletHandler(); - RestfulServer servlet = new RestfulServer(ourCtx); - servlet.setPagingProvider(new FifoMemoryPagingProvider(10)); - servlet.setDefaultResponseEncoding(EncodingEnum.XML); - servlet.setResourceProviders(patientProvider); - - ServletHolder servletHolder = new ServletHolder(servlet); - proxyHandler.addServletWithMapping(servletHolder, "/*"); - ourServer.setHandler(proxyHandler); - JettyUtil.startServer(ourServer); - ourPort = JettyUtil.getPortForStartedServer(ourServer); - - PoolingHttpClientConnectionManager connectionManager = new PoolingHttpClientConnectionManager(5000, TimeUnit.MILLISECONDS); - HttpClientBuilder builder = HttpClientBuilder.create(); - builder.setConnectionManager(connectionManager); - ourClient = builder.build(); - - } - -} diff --git a/hapi-fhir-structures-dstu2.1/src/test/java/ca/uhn/fhir/rest/server/ReadDstu2_1Test.java b/hapi-fhir-structures-dstu2.1/src/test/java/ca/uhn/fhir/rest/server/ReadDstu2_1Test.java deleted file mode 100644 index ef1892e907c..00000000000 --- a/hapi-fhir-structures-dstu2.1/src/test/java/ca/uhn/fhir/rest/server/ReadDstu2_1Test.java +++ /dev/null @@ -1,122 +0,0 @@ -package ca.uhn.fhir.rest.server; - -import ca.uhn.fhir.context.FhirContext; -import ca.uhn.fhir.rest.annotation.IdParam; -import ca.uhn.fhir.rest.annotation.Read; -import ca.uhn.fhir.rest.api.Constants; -import ca.uhn.fhir.rest.client.MyPatientWithExtensions; -import ca.uhn.fhir.test.utilities.JettyUtil; -import ca.uhn.fhir.util.TestUtil; -import org.apache.commons.io.IOUtils; -import org.apache.http.HttpResponse; -import org.apache.http.client.methods.HttpGet; -import org.apache.http.impl.client.CloseableHttpClient; -import org.apache.http.impl.client.HttpClientBuilder; -import org.apache.http.impl.conn.PoolingHttpClientConnectionManager; -import org.eclipse.jetty.server.Server; -import org.eclipse.jetty.servlet.ServletHandler; -import org.eclipse.jetty.servlet.ServletHolder; -import org.hl7.fhir.dstu2016may.model.DateType; -import org.hl7.fhir.dstu2016may.model.IdType; -import org.hl7.fhir.dstu2016may.model.Patient; -import org.junit.jupiter.api.AfterAll; -import org.junit.jupiter.api.BeforeAll; -import org.junit.jupiter.api.Test; - -import java.nio.charset.StandardCharsets; -import java.util.concurrent.TimeUnit; - -import static org.hamcrest.MatcherAssert.assertThat; -import static org.hamcrest.Matchers.stringContainsInOrder; -import static org.junit.jupiter.api.Assertions.assertEquals; - -public class ReadDstu2_1Test { - private static CloseableHttpClient ourClient; - - private static FhirContext ourCtx = FhirContext.forDstu2_1(); - private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(ReadDstu2_1Test.class); - private static int ourPort; - private static Server ourServer; - - - @Test - public void testRead() throws Exception { - - HttpGet httpGet = new HttpGet("http://localhost:" + ourPort + "/Patient/2?_format=xml&_pretty=true"); - HttpResponse status = ourClient.execute(httpGet); - - String responseContent = IOUtils.toString(status.getEntity().getContent(), StandardCharsets.UTF_8); - IOUtils.closeQuietly(status.getEntity().getContent()); - - ourLog.info("Response was:\n{}", responseContent); - - assertEquals(200, status.getStatusLine().getStatusCode()); - assertEquals(null, status.getFirstHeader(Constants.HEADER_LOCATION)); - assertEquals("http://localhost:" + ourPort + "/Patient/2/_history/2", status.getFirstHeader(Constants.HEADER_CONTENT_LOCATION).getValue()); - - //@formatter:off - assertThat(responseContent, stringContainsInOrder( - "", - "", - "", - "", - "", - "", - "", - "", - "")); - //@formatter:on - } - - - @AfterAll - public static void afterClassClearContext() throws Exception { - JettyUtil.closeServer(ourServer); - TestUtil.randomizeLocaleAndTimezone(); - } - - @BeforeAll - public static void beforeClass() throws Exception { - ourServer = new Server(0); - - PatientProvider patientProvider = new PatientProvider(); - - ServletHandler proxyHandler = new ServletHandler(); - RestfulServer servlet = new RestfulServer(ourCtx); - - servlet.setResourceProviders(patientProvider); - ServletHolder servletHolder = new ServletHolder(servlet); - proxyHandler.addServletWithMapping(servletHolder, "/*"); - ourServer.setHandler(proxyHandler); - JettyUtil.startServer(ourServer); - ourPort = JettyUtil.getPortForStartedServer(ourServer); - - PoolingHttpClientConnectionManager connectionManager = new PoolingHttpClientConnectionManager(5000, TimeUnit.MILLISECONDS); - HttpClientBuilder builder = HttpClientBuilder.create(); - builder.setConnectionManager(connectionManager); - ourClient = builder.build(); - - } - - public static class PatientProvider implements IResourceProvider { - - @Override - public Class getResourceType() { - return Patient.class; - } - - @Read(version=true) - public MyPatientWithExtensions read(@IdParam IdType theIdParam) { - MyPatientWithExtensions p0 = new MyPatientWithExtensions(); - p0.setId(theIdParam); - if (theIdParam.hasVersionIdPart() == false) { - p0.setIdElement(p0.getIdElement().withVersion("2")); - } - p0.setDateExt(new DateType("2011-01-01")); - return p0; - } - - - } - -} diff --git a/hapi-fhir-structures-dstu2.1/src/test/java/ca/uhn/fhir/rest/server/SearchCountParamDstu2_1Test.java b/hapi-fhir-structures-dstu2.1/src/test/java/ca/uhn/fhir/rest/server/SearchCountParamDstu2_1Test.java deleted file mode 100644 index 126f01451ae..00000000000 --- a/hapi-fhir-structures-dstu2.1/src/test/java/ca/uhn/fhir/rest/server/SearchCountParamDstu2_1Test.java +++ /dev/null @@ -1,184 +0,0 @@ -package ca.uhn.fhir.rest.server; - -import ca.uhn.fhir.context.FhirContext; -import ca.uhn.fhir.rest.annotation.Count; -import ca.uhn.fhir.rest.annotation.OptionalParam; -import ca.uhn.fhir.rest.annotation.Search; -import ca.uhn.fhir.rest.api.EncodingEnum; -import ca.uhn.fhir.rest.param.TokenParam; -import ca.uhn.fhir.test.utilities.JettyUtil; -import ca.uhn.fhir.util.TestUtil; -import org.apache.commons.io.IOUtils; -import org.apache.http.client.methods.CloseableHttpResponse; -import org.apache.http.client.methods.HttpGet; -import org.apache.http.impl.client.CloseableHttpClient; -import org.apache.http.impl.client.HttpClientBuilder; -import org.apache.http.impl.conn.PoolingHttpClientConnectionManager; -import org.eclipse.jetty.server.Server; -import org.eclipse.jetty.servlet.ServletHandler; -import org.eclipse.jetty.servlet.ServletHolder; -import org.hl7.fhir.dstu2016may.model.HumanName; -import org.hl7.fhir.dstu2016may.model.Patient; -import org.hl7.fhir.instance.model.api.IBaseResource; -import org.junit.jupiter.api.AfterAll; -import org.junit.jupiter.api.BeforeAll; -import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.Test; - -import java.util.ArrayList; -import java.util.List; -import java.util.concurrent.TimeUnit; - -import static org.hamcrest.MatcherAssert.assertThat; -import static org.hamcrest.Matchers.stringContainsInOrder; -import static org.junit.jupiter.api.Assertions.assertEquals; - -public class SearchCountParamDstu2_1Test { - - private static CloseableHttpClient ourClient; - private static FhirContext ourCtx = FhirContext.forDstu2_1(); - private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(SearchCountParamDstu2_1Test.class); - private static int ourPort; - private static Server ourServer; - private static String ourLastMethod; - private static Integer ourLastParam; - - @BeforeEach - public void before() { - ourLastMethod = null; - ourLastParam = null; - } - - @Test - public void testSearch() throws Exception { - HttpGet httpGet = new HttpGet("http://localhost:" + ourPort + "/Patient?_count=2"); - CloseableHttpResponse status = ourClient.execute(httpGet); - try { - String responseContent = IOUtils.toString(status.getEntity().getContent()); - ourLog.info(responseContent); - assertEquals(200, status.getStatusLine().getStatusCode()); - assertEquals("search", ourLastMethod); - assertEquals(new Integer(2), ourLastParam); - - //@formatter:off - assertThat(responseContent, stringContainsInOrder( - "", - "", - "", - "", - "", - "", - "", - "")); - //@formatter:on - - } finally { - IOUtils.closeQuietly(status.getEntity().getContent()); - } - - } - - /** - * See #372 - */ - @Test - public void testSearchWithNoCountParam() throws Exception { - HttpGet httpGet = new HttpGet("http://localhost:" + ourPort + "/Patient?_query=searchWithNoCountParam&_count=2"); - CloseableHttpResponse status = ourClient.execute(httpGet); - try { - String responseContent = IOUtils.toString(status.getEntity().getContent()); - ourLog.info(responseContent); - assertEquals(200, status.getStatusLine().getStatusCode()); - assertEquals("searchWithNoCountParam", ourLastMethod); - assertEquals(null, ourLastParam); - - //@formatter:off - assertThat(responseContent, stringContainsInOrder( - "", - "", - "", - "", - "", - "", - "", - "")); - //@formatter:on - - } finally { - IOUtils.closeQuietly(status.getEntity().getContent()); - } - - } - - @AfterAll - public static void afterClassClearContext() throws Exception { - JettyUtil.closeServer(ourServer); - TestUtil.randomizeLocaleAndTimezone(); - } - - @BeforeAll - public static void beforeClass() throws Exception { - ourServer = new Server(0); - - DummyPatientResourceProvider patientProvider = new DummyPatientResourceProvider(); - - ServletHandler proxyHandler = new ServletHandler(); - RestfulServer servlet = new RestfulServer(ourCtx); - servlet.setPagingProvider(new FifoMemoryPagingProvider(10)); - servlet.setDefaultResponseEncoding(EncodingEnum.XML); - servlet.setResourceProviders(patientProvider); - - ServletHolder servletHolder = new ServletHolder(servlet); - proxyHandler.addServletWithMapping(servletHolder, "/*"); - ourServer.setHandler(proxyHandler); - JettyUtil.startServer(ourServer); - ourPort = JettyUtil.getPortForStartedServer(ourServer); - - PoolingHttpClientConnectionManager connectionManager = new PoolingHttpClientConnectionManager(5000, TimeUnit.MILLISECONDS); - HttpClientBuilder builder = HttpClientBuilder.create(); - builder.setConnectionManager(connectionManager); - ourClient = builder.build(); - - } - - public static class DummyPatientResourceProvider implements IResourceProvider { - - @Override - public Class getResourceType() { - return Patient.class; - } - - //@formatter:off - @SuppressWarnings("rawtypes") - @Search() - public List search( - @OptionalParam(name=Patient.SP_IDENTIFIER) TokenParam theIdentifier, - @Count() Integer theParam - ) { - ourLastMethod = "search"; - ourLastParam = theParam; - ArrayList retVal = new ArrayList(); - for (int i = 1; i < 100; i++) { - retVal.add((Patient) new Patient().addName(new HumanName().addFamily("FAMILY")).setId("" + i)); - } - return retVal; - } - //@formatter:on - - //@formatter:off - @SuppressWarnings("rawtypes") - @Search(queryName="searchWithNoCountParam") - public List searchWithNoCountParam() { - ourLastMethod = "searchWithNoCountParam"; - ourLastParam = null; - ArrayList retVal = new ArrayList(); - for (int i = 1; i < 100; i++) { - retVal.add((Patient) new Patient().addName(new HumanName().addFamily("FAMILY")).setId("" + i)); - } - return retVal; - } - //@formatter:on - - } - -} diff --git a/hapi-fhir-structures-dstu2.1/src/test/java/ca/uhn/fhir/rest/server/SearchDstu2_1Test.java b/hapi-fhir-structures-dstu2.1/src/test/java/ca/uhn/fhir/rest/server/SearchDstu2_1Test.java deleted file mode 100644 index dea3760cf4e..00000000000 --- a/hapi-fhir-structures-dstu2.1/src/test/java/ca/uhn/fhir/rest/server/SearchDstu2_1Test.java +++ /dev/null @@ -1,142 +0,0 @@ -package ca.uhn.fhir.rest.server; - -import ca.uhn.fhir.context.FhirContext; -import ca.uhn.fhir.i18n.Msg; -import ca.uhn.fhir.rest.annotation.OptionalParam; -import ca.uhn.fhir.rest.annotation.Search; -import ca.uhn.fhir.rest.api.EncodingEnum; -import ca.uhn.fhir.rest.param.TokenAndListParam; -import ca.uhn.fhir.test.utilities.JettyUtil; -import ca.uhn.fhir.util.TestUtil; -import org.apache.commons.io.IOUtils; -import org.apache.http.client.methods.CloseableHttpResponse; -import org.apache.http.client.methods.HttpGet; -import org.apache.http.impl.client.CloseableHttpClient; -import org.apache.http.impl.client.HttpClientBuilder; -import org.apache.http.impl.conn.PoolingHttpClientConnectionManager; -import org.eclipse.jetty.server.Server; -import org.eclipse.jetty.servlet.ServletHandler; -import org.eclipse.jetty.servlet.ServletHolder; -import org.hl7.fhir.dstu2016may.model.HumanName; -import org.hl7.fhir.dstu2016may.model.OperationOutcome; -import org.hl7.fhir.dstu2016may.model.Patient; -import org.hl7.fhir.instance.model.api.IBaseResource; -import org.junit.jupiter.api.AfterAll; -import org.junit.jupiter.api.BeforeAll; -import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.Test; - -import java.nio.charset.StandardCharsets; -import java.util.ArrayList; -import java.util.List; -import java.util.concurrent.TimeUnit; - -import static org.junit.jupiter.api.Assertions.assertEquals; - -public class SearchDstu2_1Test { - - private static CloseableHttpClient ourClient; - private static FhirContext ourCtx = FhirContext.forDstu2_1(); - private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(SearchDstu2_1Test.class); - private static int ourPort; - private static Server ourServer; - private static String ourLastMethod; - private static TokenAndListParam ourIdentifiers; - - @BeforeEach - public void before() { - ourLastMethod = null; - ourIdentifiers = null; - } - - @Test - public void testSearchNormal() throws Exception { - HttpGet httpGet = new HttpGet("http://localhost:" + ourPort + "/Patient?identifier=foo%7Cbar"); - CloseableHttpResponse status = ourClient.execute(httpGet); - try { - String responseContent = IOUtils.toString(status.getEntity().getContent(), StandardCharsets.UTF_8); - ourLog.info(responseContent); - assertEquals(200, status.getStatusLine().getStatusCode()); - - assertEquals("search", ourLastMethod); - - assertEquals("foo", ourIdentifiers.getValuesAsQueryTokens().get(0).getValuesAsQueryTokens().get(0).getSystem()); - assertEquals("bar", ourIdentifiers.getValuesAsQueryTokens().get(0).getValuesAsQueryTokens().get(0).getValue()); - } finally { - IOUtils.closeQuietly(status.getEntity().getContent()); - } - - } - - @Test - public void testSearchWithInvalidChain() throws Exception { - HttpGet httpGet = new HttpGet("http://localhost:" + ourPort + "/Patient?identifier.chain=foo%7Cbar"); - CloseableHttpResponse status = ourClient.execute(httpGet); - try { - String responseContent = IOUtils.toString(status.getEntity().getContent(), StandardCharsets.UTF_8); - ourLog.info(responseContent); - assertEquals(400, status.getStatusLine().getStatusCode()); - - OperationOutcome oo = (OperationOutcome) ourCtx.newXmlParser().parseResource(responseContent); - assertEquals(Msg.code(1935) + "Invalid search parameter \"identifier.chain\". Parameter contains a chain (.chain) and chains are not supported for this parameter (chaining is only allowed on reference parameters)", oo.getIssue().get(0).getDiagnostics()); - } finally { - IOUtils.closeQuietly(status.getEntity().getContent()); - } - - } - - @AfterAll - public static void afterClassClearContext() throws Exception { - JettyUtil.closeServer(ourServer); - TestUtil.randomizeLocaleAndTimezone(); - } - - @BeforeAll - public static void beforeClass() throws Exception { - ourServer = new Server(0); - - DummyPatientResourceProvider patientProvider = new DummyPatientResourceProvider(); - - ServletHandler proxyHandler = new ServletHandler(); - RestfulServer servlet = new RestfulServer(ourCtx); - servlet.setPagingProvider(new FifoMemoryPagingProvider(10)); - servlet.setDefaultResponseEncoding(EncodingEnum.XML); - servlet.setResourceProviders(patientProvider); - - ServletHolder servletHolder = new ServletHolder(servlet); - proxyHandler.addServletWithMapping(servletHolder, "/*"); - ourServer.setHandler(proxyHandler); - JettyUtil.startServer(ourServer); - ourPort = JettyUtil.getPortForStartedServer(ourServer); - - PoolingHttpClientConnectionManager connectionManager = new PoolingHttpClientConnectionManager(5000, TimeUnit.MILLISECONDS); - HttpClientBuilder builder = HttpClientBuilder.create(); - builder.setConnectionManager(connectionManager); - ourClient = builder.build(); - - } - - public static class DummyPatientResourceProvider implements IResourceProvider { - - @Override - public Class getResourceType() { - return Patient.class; - } - - //@formatter:off - @SuppressWarnings("rawtypes") - @Search() - public List search( - @OptionalParam(name=Patient.SP_IDENTIFIER) TokenAndListParam theIdentifiers - ) { - ourLastMethod = "search"; - ourIdentifiers = theIdentifiers; - ArrayList retVal = new ArrayList(); - retVal.add((Patient) new Patient().addName(new HumanName().addFamily("FAMILY")).setId("1")); - return retVal; - } - //@formatter:on - - } - -} diff --git a/hapi-fhir-structures-dstu2.1/src/test/java/ca/uhn/fhir/rest/server/SearchHasParamDstu2_1Test.java b/hapi-fhir-structures-dstu2.1/src/test/java/ca/uhn/fhir/rest/server/SearchHasParamDstu2_1Test.java deleted file mode 100644 index fcd300cee41..00000000000 --- a/hapi-fhir-structures-dstu2.1/src/test/java/ca/uhn/fhir/rest/server/SearchHasParamDstu2_1Test.java +++ /dev/null @@ -1,121 +0,0 @@ -package ca.uhn.fhir.rest.server; - -import ca.uhn.fhir.context.FhirContext; -import ca.uhn.fhir.rest.annotation.OptionalParam; -import ca.uhn.fhir.rest.annotation.Search; -import ca.uhn.fhir.rest.param.HasAndListParam; -import ca.uhn.fhir.rest.param.HasParam; -import ca.uhn.fhir.rest.param.TokenParam; -import ca.uhn.fhir.test.utilities.JettyUtil; -import ca.uhn.fhir.util.TestUtil; -import org.apache.commons.io.IOUtils; -import org.apache.http.HttpResponse; -import org.apache.http.client.methods.HttpGet; -import org.apache.http.impl.client.CloseableHttpClient; -import org.apache.http.impl.client.HttpClientBuilder; -import org.apache.http.impl.conn.PoolingHttpClientConnectionManager; -import org.eclipse.jetty.server.Server; -import org.eclipse.jetty.servlet.ServletHandler; -import org.eclipse.jetty.servlet.ServletHolder; -import org.hl7.fhir.dstu2016may.model.HumanName; -import org.hl7.fhir.dstu2016may.model.Patient; -import org.hl7.fhir.instance.model.api.IBaseResource; -import org.junit.jupiter.api.AfterAll; -import org.junit.jupiter.api.BeforeAll; -import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.Test; - -import java.util.ArrayList; -import java.util.List; -import java.util.concurrent.TimeUnit; - -import static org.junit.jupiter.api.Assertions.assertEquals; - -public class SearchHasParamDstu2_1Test { - - private static CloseableHttpClient ourClient; - private static FhirContext ourCtx = FhirContext.forDstu2_1(); - private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(SearchHasParamDstu2_1Test.class); - private static int ourPort; - private static Server ourServer; - private static String ourLastMethod; - private static HasAndListParam ourLastParam; - - @BeforeEach - public void before() { - ourLastMethod = null; - ourLastParam = null; - } - - @Test - public void testSearch() throws Exception { - HttpGet httpGet = new HttpGet("http://localhost:" + ourPort + "/Patient?_has:Encounter:patient:type=SURG"); - HttpResponse status = ourClient.execute(httpGet); - String responseContent = IOUtils.toString(status.getEntity().getContent()); - IOUtils.closeQuietly(status.getEntity().getContent()); - ourLog.info(responseContent); - assertEquals(200, status.getStatusLine().getStatusCode()); - assertEquals("search", ourLastMethod); - - HasParam param = ourLastParam.getValuesAsQueryTokens().get(0).getValuesAsQueryTokens().get(0); - assertEquals("Encounter", param.getTargetResourceType()); - assertEquals("patient", param.getReferenceFieldName()); - assertEquals("type", param.getParameterName()); - assertEquals("SURG", param.getParameterValue()); - } - - @AfterAll - public static void afterClassClearContext() throws Exception { - JettyUtil.closeServer(ourServer); - TestUtil.randomizeLocaleAndTimezone(); - } - - @BeforeAll - public static void beforeClass() throws Exception { - ourServer = new Server(0); - - DummyPatientResourceProvider patientProvider = new DummyPatientResourceProvider(); - - ServletHandler proxyHandler = new ServletHandler(); - RestfulServer servlet = new RestfulServer(ourCtx); - servlet.setPagingProvider(new FifoMemoryPagingProvider(10)); - - servlet.setResourceProviders(patientProvider); - ServletHolder servletHolder = new ServletHolder(servlet); - proxyHandler.addServletWithMapping(servletHolder, "/*"); - ourServer.setHandler(proxyHandler); - JettyUtil.startServer(ourServer); - ourPort = JettyUtil.getPortForStartedServer(ourServer); - - PoolingHttpClientConnectionManager connectionManager = new PoolingHttpClientConnectionManager(5000, TimeUnit.MILLISECONDS); - HttpClientBuilder builder = HttpClientBuilder.create(); - builder.setConnectionManager(connectionManager); - ourClient = builder.build(); - - } - - public static class DummyPatientResourceProvider implements IResourceProvider { - - @Override - public Class getResourceType() { - return Patient.class; - } - - //@formatter:off - @SuppressWarnings("rawtypes") - @Search() - public List search( - @OptionalParam(name=Patient.SP_IDENTIFIER) TokenParam theIdentifier, - @OptionalParam(name="_has") HasAndListParam theParam - ) { - ourLastMethod = "search"; - ourLastParam = theParam; - ArrayList retVal = new ArrayList(); - retVal.add((Patient) new Patient().addName(new HumanName().addFamily("FAMILY")).setId("1")); - return retVal; - } - //@formatter:on - - } - -} diff --git a/hapi-fhir-structures-dstu2.1/src/test/java/ca/uhn/fhir/rest/server/SearchPostDstu2_1Test.java b/hapi-fhir-structures-dstu2.1/src/test/java/ca/uhn/fhir/rest/server/SearchPostDstu2_1Test.java deleted file mode 100644 index 7ab78bee8b9..00000000000 --- a/hapi-fhir-structures-dstu2.1/src/test/java/ca/uhn/fhir/rest/server/SearchPostDstu2_1Test.java +++ /dev/null @@ -1,260 +0,0 @@ -package ca.uhn.fhir.rest.server; - -import ca.uhn.fhir.context.FhirContext; -import ca.uhn.fhir.rest.annotation.OptionalParam; -import ca.uhn.fhir.rest.annotation.Search; -import ca.uhn.fhir.rest.annotation.Sort; -import ca.uhn.fhir.rest.api.Constants; -import ca.uhn.fhir.rest.api.EncodingEnum; -import ca.uhn.fhir.rest.api.SortSpec; -import ca.uhn.fhir.rest.param.StringAndListParam; -import ca.uhn.fhir.rest.server.interceptor.InterceptorAdapter; -import ca.uhn.fhir.test.utilities.JettyUtil; -import ca.uhn.fhir.util.TestUtil; -import com.google.common.collect.Lists; -import org.apache.commons.io.IOUtils; -import org.apache.http.NameValuePair; -import org.apache.http.client.entity.UrlEncodedFormEntity; -import org.apache.http.client.methods.CloseableHttpResponse; -import org.apache.http.client.methods.HttpPost; -import org.apache.http.impl.client.CloseableHttpClient; -import org.apache.http.impl.client.HttpClientBuilder; -import org.apache.http.impl.conn.PoolingHttpClientConnectionManager; -import org.apache.http.message.BasicNameValuePair; -import org.eclipse.jetty.server.Server; -import org.eclipse.jetty.servlet.ServletHandler; -import org.eclipse.jetty.servlet.ServletHolder; -import org.hl7.fhir.dstu2016may.model.HumanName; -import org.hl7.fhir.dstu2016may.model.Patient; -import org.hl7.fhir.instance.model.api.IBaseResource; -import org.junit.jupiter.api.AfterAll; -import org.junit.jupiter.api.BeforeAll; -import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.Test; - -import javax.servlet.http.HttpServletRequest; -import javax.servlet.http.HttpServletResponse; -import java.nio.charset.StandardCharsets; -import java.util.ArrayList; -import java.util.List; -import java.util.concurrent.TimeUnit; - -import static org.junit.jupiter.api.Assertions.assertEquals; - -public class SearchPostDstu2_1Test { - - public class ParamLoggingInterceptor extends InterceptorAdapter { - - @Override - public boolean incomingRequestPreProcessed(HttpServletRequest theRequest, HttpServletResponse theResponse) { - ourLog.info("Params: {}", theRequest.getParameterMap()); - return true; - } - - - } - - private static CloseableHttpClient ourClient; - private static FhirContext ourCtx = FhirContext.forDstu2_1(); - private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(SearchPostDstu2_1Test.class); - private static int ourPort; - private static Server ourServer; - private static String ourLastMethod; - private static SortSpec ourLastSortSpec; - private static StringAndListParam ourLastName; - private static RestfulServer ourServlet; - - @BeforeEach - public void before() { - ourLastMethod = null; - ourLastSortSpec = null; - ourLastName = null; - - ourServlet.getInterceptorService().unregisterAllInterceptors(); - } - - /** - * See #411 - */ - @Test - public void testSearchWithMixedParamsNoInterceptorsYesParams() throws Exception { - HttpPost httpPost = new HttpPost("http://localhost:" + ourPort + "/Patient/_search?_format="+Constants.CT_FHIR_JSON); - httpPost.addHeader("Cache-Control","no-cache"); - List parameters = Lists.newArrayList(); - parameters.add(new BasicNameValuePair("name", "Smith")); - httpPost.setEntity(new UrlEncodedFormEntity(parameters)); - - ourLog.info("Outgoing post: {}", httpPost); - - CloseableHttpResponse status = ourClient.execute(httpPost); - try { - String responseContent = IOUtils.toString(status.getEntity().getContent(), StandardCharsets.UTF_8); - ourLog.info(responseContent); - assertEquals(200, status.getStatusLine().getStatusCode()); - - assertEquals("search", ourLastMethod); - assertEquals(null, ourLastSortSpec); - assertEquals(1, ourLastName.getValuesAsQueryTokens().size()); - assertEquals(1, ourLastName.getValuesAsQueryTokens().get(0).getValuesAsQueryTokens().size()); - assertEquals("Smith", ourLastName.getValuesAsQueryTokens().get(0).getValuesAsQueryTokens().get(0).getValue()); - assertEquals(Constants.CT_FHIR_JSON, status.getEntity().getContentType().getValue().replaceAll(";.*", "")); - } finally { - IOUtils.closeQuietly(status.getEntity().getContent()); - } - - } - - /** - * See #411 - */ - @Test - public void testSearchWithMixedParamsNoInterceptorsNoParams() throws Exception { - HttpPost httpPost = new HttpPost("http://localhost:" + ourPort + "/Patient/_search"); - httpPost.addHeader("Cache-Control","no-cache"); - List parameters = Lists.newArrayList(); - parameters.add(new BasicNameValuePair("name", "Smith")); - httpPost.setEntity(new UrlEncodedFormEntity(parameters)); - - ourLog.info("Outgoing post: {}", httpPost); - - CloseableHttpResponse status = ourClient.execute(httpPost); - try { - String responseContent = IOUtils.toString(status.getEntity().getContent(), StandardCharsets.UTF_8); - ourLog.info(responseContent); - assertEquals(200, status.getStatusLine().getStatusCode()); - - assertEquals("search", ourLastMethod); - assertEquals(null, ourLastSortSpec); - assertEquals(1, ourLastName.getValuesAsQueryTokens().size()); - assertEquals(1, ourLastName.getValuesAsQueryTokens().get(0).getValuesAsQueryTokens().size()); - assertEquals("Smith", ourLastName.getValuesAsQueryTokens().get(0).getValuesAsQueryTokens().get(0).getValue()); - assertEquals(Constants.CT_FHIR_XML, status.getEntity().getContentType().getValue().replaceAll(";.*", "")); - } finally { - IOUtils.closeQuietly(status.getEntity().getContent()); - } - - } - - /** - * See #411 - */ - @Test - public void testSearchWithMixedParamsYesInterceptorsYesParams() throws Exception { - ourServlet.registerInterceptor(new ParamLoggingInterceptor()); - - HttpPost httpPost = new HttpPost("http://localhost:" + ourPort + "/Patient/_search?_format="+Constants.CT_FHIR_JSON); - httpPost.addHeader("Cache-Control","no-cache"); - List parameters = Lists.newArrayList(); - parameters.add(new BasicNameValuePair("name", "Smith")); - httpPost.setEntity(new UrlEncodedFormEntity(parameters)); - - ourLog.info("Outgoing post: {}", httpPost); - - CloseableHttpResponse status = ourClient.execute(httpPost); - try { - String responseContent = IOUtils.toString(status.getEntity().getContent(), StandardCharsets.UTF_8); - ourLog.info(responseContent); - assertEquals(200, status.getStatusLine().getStatusCode()); - - assertEquals("search", ourLastMethod); - assertEquals(null, ourLastSortSpec); - assertEquals(1, ourLastName.getValuesAsQueryTokens().size()); - assertEquals(1, ourLastName.getValuesAsQueryTokens().get(0).getValuesAsQueryTokens().size()); - assertEquals("Smith", ourLastName.getValuesAsQueryTokens().get(0).getValuesAsQueryTokens().get(0).getValue()); - assertEquals(Constants.CT_FHIR_JSON, status.getEntity().getContentType().getValue().replaceAll(";.*", "")); - } finally { - IOUtils.closeQuietly(status.getEntity().getContent()); - } - - } - - /** - * See #411 - */ - @Test - public void testSearchWithMixedParamsYesInterceptorsNoParams() throws Exception { - ourServlet.registerInterceptor(new ParamLoggingInterceptor()); - - HttpPost httpPost = new HttpPost("http://localhost:" + ourPort + "/Patient/_search"); - httpPost.addHeader("Cache-Control","no-cache"); - List parameters = Lists.newArrayList(); - parameters.add(new BasicNameValuePair("name", "Smith")); - httpPost.setEntity(new UrlEncodedFormEntity(parameters)); - - ourLog.info("Outgoing post: {}", httpPost); - - CloseableHttpResponse status = ourClient.execute(httpPost); - try { - String responseContent = IOUtils.toString(status.getEntity().getContent(), StandardCharsets.UTF_8); - ourLog.info(responseContent); - assertEquals(200, status.getStatusLine().getStatusCode()); - - assertEquals("search", ourLastMethod); - assertEquals(null, ourLastSortSpec); - assertEquals(1, ourLastName.getValuesAsQueryTokens().size()); - assertEquals(1, ourLastName.getValuesAsQueryTokens().get(0).getValuesAsQueryTokens().size()); - assertEquals("Smith", ourLastName.getValuesAsQueryTokens().get(0).getValuesAsQueryTokens().get(0).getValue()); - assertEquals(Constants.CT_FHIR_XML, status.getEntity().getContentType().getValue().replaceAll(";.*", "")); - } finally { - IOUtils.closeQuietly(status.getEntity().getContent()); - } - - } - - @AfterAll - public static void afterClassClearContext() throws Exception { - JettyUtil.closeServer(ourServer); - TestUtil.randomizeLocaleAndTimezone(); - } - - @BeforeAll - public static void beforeClass() throws Exception { - ourServer = new Server(0); - - DummyPatientResourceProvider patientProvider = new DummyPatientResourceProvider(); - - ServletHandler proxyHandler = new ServletHandler(); - ourServlet = new RestfulServer(ourCtx); - ourServlet.setDefaultResponseEncoding(EncodingEnum.XML); - ourServlet.setPagingProvider(new FifoMemoryPagingProvider(10)); - - ourServlet.setResourceProviders(patientProvider); - ServletHolder servletHolder = new ServletHolder(ourServlet); - proxyHandler.addServletWithMapping(servletHolder, "/*"); - ourServer.setHandler(proxyHandler); - JettyUtil.startServer(ourServer); - ourPort = JettyUtil.getPortForStartedServer(ourServer); - - PoolingHttpClientConnectionManager connectionManager = new PoolingHttpClientConnectionManager(5000, TimeUnit.MILLISECONDS); - HttpClientBuilder builder = HttpClientBuilder.create(); - builder.setConnectionManager(connectionManager); - ourClient = builder.build(); - - } - - public static class DummyPatientResourceProvider implements IResourceProvider { - - @Override - public Class getResourceType() { - return Patient.class; - } - - //@formatter:off - @SuppressWarnings("rawtypes") - @Search() - public List search( - @Sort SortSpec theSortSpec, - @OptionalParam(name=Patient.SP_NAME) StringAndListParam theName - ) { - ourLastMethod = "search"; - ourLastSortSpec = theSortSpec; - ourLastName = theName; - ArrayList retVal = new ArrayList<>(); - retVal.add((Patient) new Patient().addName(new HumanName().addFamily("FAMILY")).setId("foo")); - return retVal; - } - //@formatter:on - - } - -} diff --git a/hapi-fhir-structures-dstu2.1/src/test/java/ca/uhn/fhir/rest/server/SearchSortDstu2_1Test.java b/hapi-fhir-structures-dstu2.1/src/test/java/ca/uhn/fhir/rest/server/SearchSortDstu2_1Test.java deleted file mode 100644 index 2abec3a4fb3..00000000000 --- a/hapi-fhir-structures-dstu2.1/src/test/java/ca/uhn/fhir/rest/server/SearchSortDstu2_1Test.java +++ /dev/null @@ -1,132 +0,0 @@ -package ca.uhn.fhir.rest.server; - -import ca.uhn.fhir.context.FhirContext; -import ca.uhn.fhir.rest.annotation.Search; -import ca.uhn.fhir.rest.annotation.Sort; -import ca.uhn.fhir.rest.api.SortOrderEnum; -import ca.uhn.fhir.rest.api.SortSpec; -import ca.uhn.fhir.test.utilities.JettyUtil; -import ca.uhn.fhir.util.TestUtil; -import org.apache.commons.io.IOUtils; -import org.apache.http.client.methods.CloseableHttpResponse; -import org.apache.http.client.methods.HttpGet; -import org.apache.http.impl.client.CloseableHttpClient; -import org.apache.http.impl.client.HttpClientBuilder; -import org.apache.http.impl.conn.PoolingHttpClientConnectionManager; -import org.eclipse.jetty.server.Server; -import org.eclipse.jetty.servlet.ServletHandler; -import org.eclipse.jetty.servlet.ServletHolder; -import org.hl7.fhir.dstu2016may.model.HumanName; -import org.hl7.fhir.dstu2016may.model.Patient; -import org.hl7.fhir.instance.model.api.IBaseResource; -import org.junit.jupiter.api.AfterAll; -import org.junit.jupiter.api.BeforeAll; -import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.Test; - -import java.util.ArrayList; -import java.util.List; -import java.util.concurrent.TimeUnit; - -import static org.junit.jupiter.api.Assertions.assertEquals; - -public class SearchSortDstu2_1Test { - - private static CloseableHttpClient ourClient; - private static FhirContext ourCtx = FhirContext.forDstu2_1(); - private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(SearchSortDstu2_1Test.class); - private static int ourPort; - private static Server ourServer; - private static String ourLastMethod; - private static SortSpec ourLastSortSpec; - - @BeforeEach - public void before() { - ourLastMethod = null; - ourLastSortSpec = null; - } - - @Test - public void testSearch() throws Exception { - HttpGet httpGet = new HttpGet("http://localhost:" + ourPort + "/Patient?_sort=param1,-param2,param3,-param4"); - CloseableHttpResponse status = ourClient.execute(httpGet); - try { - String responseContent = IOUtils.toString(status.getEntity().getContent()); - ourLog.info(responseContent); - assertEquals(200, status.getStatusLine().getStatusCode()); - assertEquals("search", ourLastMethod); - - assertEquals("param1", ourLastSortSpec.getParamName()); - assertEquals(SortOrderEnum.ASC, ourLastSortSpec.getOrder()); - - assertEquals("param2", ourLastSortSpec.getChain().getParamName()); - assertEquals(SortOrderEnum.DESC, ourLastSortSpec.getChain().getOrder()); - - assertEquals("param3", ourLastSortSpec.getChain().getChain().getParamName()); - assertEquals(SortOrderEnum.ASC, ourLastSortSpec.getChain().getChain().getOrder()); - - assertEquals("param4", ourLastSortSpec.getChain().getChain().getChain().getParamName()); - assertEquals(SortOrderEnum.DESC, ourLastSortSpec.getChain().getChain().getChain().getOrder()); - - } finally { - IOUtils.closeQuietly(status.getEntity().getContent()); - } - - } - - @AfterAll - public static void afterClassClearContext() throws Exception { - JettyUtil.closeServer(ourServer); - TestUtil.randomizeLocaleAndTimezone(); - } - - @BeforeAll - public static void beforeClass() throws Exception { - ourServer = new Server(0); - - DummyPatientResourceProvider patientProvider = new DummyPatientResourceProvider(); - - ServletHandler proxyHandler = new ServletHandler(); - RestfulServer servlet = new RestfulServer(ourCtx); - servlet.setPagingProvider(new FifoMemoryPagingProvider(10)); - - servlet.setResourceProviders(patientProvider); - ServletHolder servletHolder = new ServletHolder(servlet); - proxyHandler.addServletWithMapping(servletHolder, "/*"); - ourServer.setHandler(proxyHandler); - JettyUtil.startServer(ourServer); - ourPort = JettyUtil.getPortForStartedServer(ourServer); - - PoolingHttpClientConnectionManager connectionManager = new PoolingHttpClientConnectionManager(5000, TimeUnit.MILLISECONDS); - HttpClientBuilder builder = HttpClientBuilder.create(); - builder.setConnectionManager(connectionManager); - ourClient = builder.build(); - - } - - public static class DummyPatientResourceProvider implements IResourceProvider { - - @Override - public Class getResourceType() { - return Patient.class; - } - - //@formatter:off - @SuppressWarnings("rawtypes") - @Search() - public List search( - @Sort SortSpec theSortSpec - ) { - ourLastMethod = "search"; - ourLastSortSpec = theSortSpec; - ArrayList retVal = new ArrayList(); - for (int i = 1; i < 100; i++) { - retVal.add((Patient) new Patient().addName(new HumanName().addFamily("FAMILY")).setId("" + i)); - } - return retVal; - } - //@formatter:on - - } - -} diff --git a/hapi-fhir-structures-dstu2.1/src/test/java/ca/uhn/fhir/rest/server/SearchWithGenericListDstu2_1Test.java b/hapi-fhir-structures-dstu2.1/src/test/java/ca/uhn/fhir/rest/server/SearchWithGenericListDstu2_1Test.java index e565e08a5f0..3f80fe2b250 100644 --- a/hapi-fhir-structures-dstu2.1/src/test/java/ca/uhn/fhir/rest/server/SearchWithGenericListDstu2_1Test.java +++ b/hapi-fhir-structures-dstu2.1/src/test/java/ca/uhn/fhir/rest/server/SearchWithGenericListDstu2_1Test.java @@ -5,28 +5,22 @@ import ca.uhn.fhir.rest.annotation.RequiredParam; import ca.uhn.fhir.rest.annotation.Search; import ca.uhn.fhir.rest.api.EncodingEnum; import ca.uhn.fhir.rest.param.TokenParam; -import ca.uhn.fhir.test.utilities.JettyUtil; +import ca.uhn.fhir.test.utilities.HttpClientExtension; +import ca.uhn.fhir.test.utilities.server.RestfulServerExtension; import ca.uhn.fhir.util.TestUtil; import org.apache.commons.io.IOUtils; import org.apache.http.HttpResponse; import org.apache.http.client.methods.HttpGet; -import org.apache.http.impl.client.CloseableHttpClient; -import org.apache.http.impl.client.HttpClientBuilder; -import org.apache.http.impl.conn.PoolingHttpClientConnectionManager; -import org.eclipse.jetty.server.Server; -import org.eclipse.jetty.servlet.ServletHandler; -import org.eclipse.jetty.servlet.ServletHolder; import org.hl7.fhir.dstu2016may.model.HumanName; import org.hl7.fhir.dstu2016may.model.Patient; import org.hl7.fhir.instance.model.api.IBaseResource; import org.junit.jupiter.api.AfterAll; -import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.RegisterExtension; import java.util.ArrayList; import java.util.List; -import java.util.concurrent.TimeUnit; import static org.hamcrest.MatcherAssert.assertThat; import static org.hamcrest.Matchers.containsString; @@ -34,13 +28,18 @@ import static org.junit.jupiter.api.Assertions.assertEquals; public class SearchWithGenericListDstu2_1Test { - private static CloseableHttpClient ourClient; - private static FhirContext ourCtx = FhirContext.forDstu2_1(); + private static final FhirContext ourCtx = FhirContext.forDstu2_1(); private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(SearchWithGenericListDstu2_1Test.class); - private static int ourPort; - private static Server ourServer; private static String ourLastMethod; + @RegisterExtension + public static final RestfulServerExtension ourServer = new RestfulServerExtension(ourCtx) + .registerProvider(new DummyPatientResourceProvider()) + .setDefaultResponseEncoding(EncodingEnum.XML); + + @RegisterExtension + public static final HttpClientExtension ourClient = new HttpClientExtension(); + @BeforeEach public void before() { ourLastMethod = null; @@ -51,7 +50,7 @@ public class SearchWithGenericListDstu2_1Test { */ @Test public void testSearch() throws Exception { - HttpGet httpGet = new HttpGet("http://localhost:" + ourPort + "/Patient?identifier=foo&_pretty=true"); + HttpGet httpGet = new HttpGet(ourServer.getBaseUrl() + "/Patient?identifier=foo&_pretty=true"); HttpResponse status = ourClient.execute(httpGet); String responseContent = IOUtils.toString(status.getEntity().getContent()); IOUtils.closeQuietly(status.getEntity().getContent()); @@ -59,43 +58,17 @@ public class SearchWithGenericListDstu2_1Test { assertEquals(200, status.getStatusLine().getStatusCode()); assertEquals("searchByIdentifier", ourLastMethod); assertThat(responseContent, containsString("")); + assertThat(responseContent, containsString("")); } @AfterAll public static void afterClassClearContext() throws Exception { - JettyUtil.closeServer(ourServer); TestUtil.randomizeLocaleAndTimezone(); } - @BeforeAll - public static void beforeClass() throws Exception { - ourServer = new Server(0); - - DummyPatientResourceProvider patientProvider = new DummyPatientResourceProvider(); - - ServletHandler proxyHandler = new ServletHandler(); - RestfulServer servlet = new RestfulServer(ourCtx); - servlet.setPagingProvider(new FifoMemoryPagingProvider(10)); - servlet.setDefaultResponseEncoding(EncodingEnum.XML); - servlet.setResourceProviders(patientProvider); - - ServletHolder servletHolder = new ServletHolder(servlet); - proxyHandler.addServletWithMapping(servletHolder, "/*"); - ourServer.setHandler(proxyHandler); - JettyUtil.startServer(ourServer); - ourPort = JettyUtil.getPortForStartedServer(ourServer); - - PoolingHttpClientConnectionManager connectionManager = new PoolingHttpClientConnectionManager(5000, TimeUnit.MILLISECONDS); - HttpClientBuilder builder = HttpClientBuilder.create(); - builder.setConnectionManager(connectionManager); - ourClient = builder.build(); - - } - public static class DummyPatientResourceProvider implements IResourceProvider { @Override @@ -109,7 +82,7 @@ public class SearchWithGenericListDstu2_1Test { public List searchByIdentifier( @RequiredParam(name=Patient.SP_IDENTIFIER) TokenParam theIdentifier) { ourLastMethod = "searchByIdentifier"; - ArrayList retVal = new ArrayList(); + ArrayList retVal = new ArrayList<>(); retVal.add((Patient) new Patient().addName(new HumanName().addFamily("FAMILY")).setId("1")); return retVal; } diff --git a/hapi-fhir-structures-dstu2.1/src/test/java/ca/uhn/fhir/rest/server/SearchWithIncludesDstu2_1Test.java b/hapi-fhir-structures-dstu2.1/src/test/java/ca/uhn/fhir/rest/server/SearchWithIncludesDstu2_1Test.java deleted file mode 100644 index 97b88e3bcad..00000000000 --- a/hapi-fhir-structures-dstu2.1/src/test/java/ca/uhn/fhir/rest/server/SearchWithIncludesDstu2_1Test.java +++ /dev/null @@ -1,135 +0,0 @@ -package ca.uhn.fhir.rest.server; - -import ca.uhn.fhir.context.FhirContext; -import ca.uhn.fhir.model.api.Include; -import ca.uhn.fhir.rest.annotation.IncludeParam; -import ca.uhn.fhir.rest.annotation.Search; -import ca.uhn.fhir.rest.api.EncodingEnum; -import ca.uhn.fhir.test.utilities.JettyUtil; -import ca.uhn.fhir.util.TestUtil; -import org.apache.commons.io.IOUtils; -import org.apache.http.HttpResponse; -import org.apache.http.client.methods.HttpGet; -import org.apache.http.impl.client.CloseableHttpClient; -import org.apache.http.impl.client.HttpClientBuilder; -import org.apache.http.impl.conn.PoolingHttpClientConnectionManager; -import org.eclipse.jetty.server.Server; -import org.eclipse.jetty.servlet.ServletHandler; -import org.eclipse.jetty.servlet.ServletHolder; -import org.hl7.fhir.dstu2016may.model.Organization; -import org.hl7.fhir.dstu2016may.model.Patient; -import org.hl7.fhir.instance.model.api.IBaseResource; -import org.junit.jupiter.api.AfterAll; -import org.junit.jupiter.api.BeforeAll; -import org.junit.jupiter.api.Test; - -import java.nio.charset.StandardCharsets; -import java.util.ArrayList; -import java.util.List; -import java.util.Set; -import java.util.concurrent.TimeUnit; - -import static org.hamcrest.MatcherAssert.assertThat; -import static org.hamcrest.Matchers.containsString; -import static org.junit.jupiter.api.Assertions.assertEquals; - -public class SearchWithIncludesDstu2_1Test { - - private static CloseableHttpClient ourClient; - private static FhirContext ourCtx = FhirContext.forDstu2_1(); - private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(SearchWithIncludesDstu2_1Test.class); - private static int ourPort; - private static Server ourServer; - - @Test - public void testSearchIncludesReferences() throws Exception { - HttpGet httpGet = new HttpGet("http://localhost:" + ourPort + "/Patient?_pretty=true&_include=Patient:organization&_include=Organization:" + Organization.SP_PARTOF); - HttpResponse status = ourClient.execute(httpGet); - String responseContent = IOUtils.toString(status.getEntity().getContent(), StandardCharsets.UTF_8); - IOUtils.closeQuietly(status.getEntity().getContent()); - ourLog.info(responseContent); - assertEquals(200, status.getStatusLine().getStatusCode()); - - // Response should include both the patient, and the organization that was referred to - // by a linked resource - - assertThat(responseContent, containsString("")); - assertThat(responseContent, containsString("")); - assertThat(responseContent, containsString("")); - assertThat(responseContent, containsString("")); - - } - - - @AfterAll - public static void afterClassClearContext() throws Exception { - JettyUtil.closeServer(ourServer); - TestUtil.randomizeLocaleAndTimezone(); - } - - - - @BeforeAll - public static void beforeClass() throws Exception { - ourServer = new Server(0); - - DummyPatientResourceProvider patientProvider = new DummyPatientResourceProvider(); - - ServletHandler proxyHandler = new ServletHandler(); - RestfulServer servlet = new RestfulServer(ourCtx); - servlet.setPagingProvider(new FifoMemoryPagingProvider(10)); - servlet.setDefaultResponseEncoding(EncodingEnum.XML); - servlet.setResourceProviders(patientProvider); - - ServletHolder servletHolder = new ServletHolder(servlet); - proxyHandler.addServletWithMapping(servletHolder, "/*"); - ourServer.setHandler(proxyHandler); - JettyUtil.startServer(ourServer); - ourPort = JettyUtil.getPortForStartedServer(ourServer); - - PoolingHttpClientConnectionManager connectionManager = new PoolingHttpClientConnectionManager(5000, TimeUnit.MILLISECONDS); - HttpClientBuilder builder = HttpClientBuilder.create(); - builder.setConnectionManager(connectionManager); - ourClient = builder.build(); - - } - - public static class DummyPatientResourceProvider implements IResourceProvider { - - @Override - public Class getResourceType() { - return Patient.class; - } - - //@formatter:off - @Search() - public List search(@IncludeParam() Set theIncludes) { - ourLog.info("Includes: {}", theIncludes); - - // Grandparent has ID, other orgs don't - Organization gp = new Organization(); - gp.setId("Organization/GP"); - gp.setName("grandparent"); - - Organization parent = new Organization(); - parent.setName("parent"); - parent.getPartOf().setResource(gp); - - Organization child = new Organization(); - child.setName("child"); - child.getPartOf().setResource(parent); - - Patient patient = new Patient(); - patient.setId("Patient/FOO"); - patient.getManagingOrganization().setResource(child); - - ArrayList retVal = new ArrayList(); - retVal.add(patient); - return retVal; - } - //@formatter:on - - - } - -} diff --git a/hapi-fhir-structures-dstu2.1/src/test/java/ca/uhn/fhir/rest/server/SearchWithServerAddressStrategyDstu2_1Test.java b/hapi-fhir-structures-dstu2.1/src/test/java/ca/uhn/fhir/rest/server/SearchWithServerAddressStrategyDstu2_1Test.java deleted file mode 100644 index d486934fa09..00000000000 --- a/hapi-fhir-structures-dstu2.1/src/test/java/ca/uhn/fhir/rest/server/SearchWithServerAddressStrategyDstu2_1Test.java +++ /dev/null @@ -1,172 +0,0 @@ -package ca.uhn.fhir.rest.server; - -import ca.uhn.fhir.context.FhirContext; -import ca.uhn.fhir.rest.annotation.Search; -import ca.uhn.fhir.rest.api.EncodingEnum; -import ca.uhn.fhir.test.utilities.JettyUtil; -import ca.uhn.fhir.util.TestUtil; -import org.apache.commons.io.IOUtils; -import org.apache.http.HttpResponse; -import org.apache.http.client.methods.HttpGet; -import org.apache.http.impl.client.CloseableHttpClient; -import org.apache.http.impl.client.HttpClientBuilder; -import org.apache.http.impl.conn.PoolingHttpClientConnectionManager; -import org.eclipse.jetty.server.Server; -import org.eclipse.jetty.servlet.ServletHandler; -import org.eclipse.jetty.servlet.ServletHolder; -import org.hl7.fhir.dstu2016may.model.HumanName; -import org.hl7.fhir.dstu2016may.model.Patient; -import org.hl7.fhir.instance.model.api.IBaseResource; -import org.junit.jupiter.api.AfterAll; -import org.junit.jupiter.api.BeforeAll; -import org.junit.jupiter.api.Test; - -import java.util.ArrayList; -import java.util.List; -import java.util.concurrent.TimeUnit; - -import static org.hamcrest.MatcherAssert.assertThat; -import static org.hamcrest.Matchers.containsString; -import static org.junit.jupiter.api.Assertions.assertEquals; - -public class SearchWithServerAddressStrategyDstu2_1Test { - - private static CloseableHttpClient ourClient; - private static FhirContext ourCtx = FhirContext.forDstu2_1(); - private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(SearchWithServerAddressStrategyDstu2_1Test.class); - private static int ourPort; - private static Server ourServer; - private static RestfulServer ourServlet; - - @Test - public void testIncomingRequestAddressStrategy() throws Exception { - ourServlet.setServerAddressStrategy(new IncomingRequestAddressStrategy()); - - HttpGet httpGet = new HttpGet("http://localhost:" + ourPort + "/Patient"); - HttpResponse status = ourClient.execute(httpGet); - String responseContent = IOUtils.toString(status.getEntity().getContent()); - IOUtils.closeQuietly(status.getEntity().getContent()); - ourLog.info(responseContent); - assertEquals(200, status.getStatusLine().getStatusCode()); - assertThat(responseContent, containsString("")); - } - - @Test - public void testApacheProxyAddressStrategy() throws Exception { - - ourServlet.setServerAddressStrategy(ApacheProxyAddressStrategy.forHttp()); - HttpGet httpGet = new HttpGet("http://localhost:" + ourPort + "/Patient"); - HttpResponse status = ourClient.execute(httpGet); - String responseContent = IOUtils.toString(status.getEntity().getContent()); - IOUtils.closeQuietly(status.getEntity().getContent()); - ourLog.info(responseContent); - assertEquals(200, status.getStatusLine().getStatusCode()); - assertThat(responseContent, containsString("")); - - ourServlet.setServerAddressStrategy(new ApacheProxyAddressStrategy(false)); - httpGet = new HttpGet("http://localhost:" + ourPort + "/Patient"); - httpGet.addHeader("x-forwarded-host", "foo.com"); - status = ourClient.execute(httpGet); - responseContent = IOUtils.toString(status.getEntity().getContent()); - IOUtils.closeQuietly(status.getEntity().getContent()); - ourLog.info(responseContent); - assertEquals(200, status.getStatusLine().getStatusCode()); - assertThat(responseContent, containsString("")); - - ourServlet.setServerAddressStrategy(ApacheProxyAddressStrategy.forHttps()); - httpGet = new HttpGet("http://localhost:" + ourPort + "/Patient"); - httpGet.addHeader("x-forwarded-host", "foo.com"); - status = ourClient.execute(httpGet); - responseContent = IOUtils.toString(status.getEntity().getContent()); - IOUtils.closeQuietly(status.getEntity().getContent()); - ourLog.info(responseContent); - assertEquals(200, status.getStatusLine().getStatusCode()); - assertThat(responseContent, containsString("")); - - ourServlet.setServerAddressStrategy(new ApacheProxyAddressStrategy(false)); - httpGet = new HttpGet("http://localhost:" + ourPort + "/Patient"); - httpGet.addHeader("x-forwarded-host", "foo.com"); - httpGet.addHeader("x-forwarded-proto", "https"); - status = ourClient.execute(httpGet); - responseContent = IOUtils.toString(status.getEntity().getContent()); - IOUtils.closeQuietly(status.getEntity().getContent()); - ourLog.info(responseContent); - assertEquals(200, status.getStatusLine().getStatusCode()); - assertThat(responseContent, containsString("")); - - } - - @Test - public void testHardcodedAddressStrategy() throws Exception { - ourServlet.setServerAddressStrategy(new HardcodedServerAddressStrategy("http://example.com/fhir/base")); - - HttpGet httpGet = new HttpGet("http://localhost:" + ourPort + "/Patient"); - HttpResponse status = ourClient.execute(httpGet); - String responseContent = IOUtils.toString(status.getEntity().getContent()); - IOUtils.closeQuietly(status.getEntity().getContent()); - ourLog.info(responseContent); - assertEquals(200, status.getStatusLine().getStatusCode()); - assertThat(responseContent, containsString("")); - } - - - @AfterAll - public static void afterClassClearContext() throws Exception { - JettyUtil.closeServer(ourServer); - TestUtil.randomizeLocaleAndTimezone(); - } - - - - @BeforeAll - public static void beforeClass() throws Exception { - ourServer = new Server(0); - - DummyPatientResourceProvider patientProvider = new DummyPatientResourceProvider(); - - ServletHandler proxyHandler = new ServletHandler(); - ourServlet = new RestfulServer(ourCtx); - ourServlet.setPagingProvider(new FifoMemoryPagingProvider(10)); - ourServlet.setDefaultResponseEncoding(EncodingEnum.XML); - ourServlet.setResourceProviders(patientProvider); - - ServletHolder servletHolder = new ServletHolder(ourServlet); - proxyHandler.addServletWithMapping(servletHolder, "/*"); - ourServer.setHandler(proxyHandler); - JettyUtil.startServer(ourServer); - ourPort = JettyUtil.getPortForStartedServer(ourServer); - - PoolingHttpClientConnectionManager connectionManager = new PoolingHttpClientConnectionManager(5000, TimeUnit.MILLISECONDS); - HttpClientBuilder builder = HttpClientBuilder.create(); - builder.setConnectionManager(connectionManager); - ourClient = builder.build(); - - } - - public static class DummyPatientResourceProvider implements IResourceProvider { - - @Override - public Class getResourceType() { - return Patient.class; - } - - //@formatter:off - @Search() - public List searchByIdentifier() { - ArrayList retVal = new ArrayList(); - retVal.add((Patient) new Patient().addName(new HumanName().addGiven("FAMILY")).setId("1")); - retVal.add((Patient) new Patient().addName(new HumanName().addGiven("FAMILY")).setId("2")); - return retVal; - } - //@formatter:on - - - } - -} diff --git a/hapi-fhir-structures-dstu2.1/src/test/java/ca/uhn/fhir/rest/server/ServerExceptionDstu2_1Test.java b/hapi-fhir-structures-dstu2.1/src/test/java/ca/uhn/fhir/rest/server/ServerExceptionDstu2_1Test.java deleted file mode 100644 index ec0f45e8320..00000000000 --- a/hapi-fhir-structures-dstu2.1/src/test/java/ca/uhn/fhir/rest/server/ServerExceptionDstu2_1Test.java +++ /dev/null @@ -1,137 +0,0 @@ -package ca.uhn.fhir.rest.server; - -import ca.uhn.fhir.context.FhirContext; -import ca.uhn.fhir.rest.annotation.Search; -import ca.uhn.fhir.rest.server.exceptions.AuthenticationException; -import ca.uhn.fhir.rest.server.exceptions.BaseServerResponseException; -import ca.uhn.fhir.rest.server.exceptions.ResourceNotFoundException; -import ca.uhn.fhir.test.utilities.JettyUtil; -import ca.uhn.fhir.util.TestUtil; -import org.apache.commons.io.IOUtils; -import org.apache.http.client.methods.CloseableHttpResponse; -import org.apache.http.client.methods.HttpGet; -import org.apache.http.impl.client.CloseableHttpClient; -import org.apache.http.impl.client.HttpClientBuilder; -import org.apache.http.impl.conn.PoolingHttpClientConnectionManager; -import org.eclipse.jetty.server.Server; -import org.eclipse.jetty.servlet.ServletHandler; -import org.eclipse.jetty.servlet.ServletHolder; -import org.hl7.fhir.dstu2016may.model.OperationOutcome; -import org.hl7.fhir.dstu2016may.model.OperationOutcome.IssueType; -import org.hl7.fhir.dstu2016may.model.Patient; -import org.hl7.fhir.instance.model.api.IBaseResource; -import org.junit.jupiter.api.AfterAll; -import org.junit.jupiter.api.BeforeAll; -import org.junit.jupiter.api.Test; - -import java.nio.charset.StandardCharsets; -import java.util.List; -import java.util.concurrent.TimeUnit; - -import static org.hamcrest.MatcherAssert.assertThat; -import static org.hamcrest.Matchers.containsString; -import static org.junit.jupiter.api.Assertions.assertEquals; - -public class ServerExceptionDstu2_1Test { - - private static CloseableHttpClient ourClient; - private static FhirContext ourCtx = FhirContext.forDstu2_1(); - private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(ServerExceptionDstu2_1Test.class); - private static int ourPort; - private static Server ourServer; - public static BaseServerResponseException ourException; - - @Test - public void testAddHeadersNotFound() throws Exception { - - OperationOutcome operationOutcome = new OperationOutcome(); - operationOutcome.addIssue().setCode(IssueType.BUSINESSRULE); - - ourException = new ResourceNotFoundException("SOME MESSAGE"); - ourException.addResponseHeader("X-Foo", "BAR BAR"); - - - HttpGet httpGet = new HttpGet("http://localhost:" + ourPort + "/Patient"); - CloseableHttpResponse status = ourClient.execute(httpGet); - try { - String responseContent = IOUtils.toString(status.getEntity().getContent(), StandardCharsets.UTF_8); - ourLog.info(status.getStatusLine().toString()); - ourLog.info(responseContent); - - assertEquals(404, status.getStatusLine().getStatusCode()); - assertEquals("BAR BAR", status.getFirstHeader("X-Foo").getValue()); - assertThat(status.getFirstHeader("X-Powered-By").getValue(), containsString("HAPI FHIR")); - } finally { - IOUtils.closeQuietly(status.getEntity().getContent()); - } - - } - - @Test - public void testAuthorize() throws Exception { - - OperationOutcome operationOutcome = new OperationOutcome(); - operationOutcome.addIssue().setCode(IssueType.BUSINESSRULE); - - ourException = new AuthenticationException().addAuthenticateHeaderForRealm("REALM"); - - HttpGet httpGet = new HttpGet("http://localhost:" + ourPort + "/Patient"); - CloseableHttpResponse status = ourClient.execute(httpGet); - try { - String responseContent = IOUtils.toString(status.getEntity().getContent(), StandardCharsets.UTF_8); - ourLog.info(status.getStatusLine().toString()); - ourLog.info(responseContent); - - assertEquals(401, status.getStatusLine().getStatusCode()); - assertEquals("Basic realm=\"REALM\"", status.getFirstHeader("WWW-Authenticate").getValue()); - } finally { - IOUtils.closeQuietly(status.getEntity().getContent()); - } - - } - - @AfterAll - public static void afterClassClearContext() throws Exception { - JettyUtil.closeServer(ourServer); - TestUtil.randomizeLocaleAndTimezone(); - } - - @BeforeAll - public static void beforeClass() throws Exception { - ourServer = new Server(0); - - DummyPatientResourceProvider patientProvider = new DummyPatientResourceProvider(); - - ServletHandler proxyHandler = new ServletHandler(); - RestfulServer servlet = new RestfulServer(ourCtx); - servlet.setPagingProvider(new FifoMemoryPagingProvider(10)); - - servlet.setResourceProviders(patientProvider); - ServletHolder servletHolder = new ServletHolder(servlet); - proxyHandler.addServletWithMapping(servletHolder, "/*"); - ourServer.setHandler(proxyHandler); - JettyUtil.startServer(ourServer); - ourPort = JettyUtil.getPortForStartedServer(ourServer); - - PoolingHttpClientConnectionManager connectionManager = new PoolingHttpClientConnectionManager(5000, TimeUnit.MILLISECONDS); - HttpClientBuilder builder = HttpClientBuilder.create(); - builder.setConnectionManager(connectionManager); - ourClient = builder.build(); - - } - - public static class DummyPatientResourceProvider implements IResourceProvider { - - @Override - public Class getResourceType() { - return Patient.class; - } - - @Search() - public List search() { - throw ourException; - } - - } - -} diff --git a/hapi-fhir-structures-dstu2.1/src/test/java/ca/uhn/fhir/rest/server/ServerMimetypeDstu2_1Test.java b/hapi-fhir-structures-dstu2.1/src/test/java/ca/uhn/fhir/rest/server/ServerMimetypeDstu2_1Test.java deleted file mode 100644 index 08854c9b191..00000000000 --- a/hapi-fhir-structures-dstu2.1/src/test/java/ca/uhn/fhir/rest/server/ServerMimetypeDstu2_1Test.java +++ /dev/null @@ -1,422 +0,0 @@ -package ca.uhn.fhir.rest.server; - -import ca.uhn.fhir.context.FhirContext; -import ca.uhn.fhir.rest.annotation.Create; -import ca.uhn.fhir.rest.annotation.IdParam; -import ca.uhn.fhir.rest.annotation.Read; -import ca.uhn.fhir.rest.annotation.ResourceParam; -import ca.uhn.fhir.rest.annotation.Search; -import ca.uhn.fhir.rest.api.Constants; -import ca.uhn.fhir.rest.api.EncodingEnum; -import ca.uhn.fhir.rest.api.MethodOutcome; -import ca.uhn.fhir.rest.client.MyPatientWithExtensions; -import ca.uhn.fhir.test.utilities.JettyUtil; -import ca.uhn.fhir.util.TestUtil; -import org.apache.commons.io.IOUtils; -import org.apache.http.HttpResponse; -import org.apache.http.client.methods.CloseableHttpResponse; -import org.apache.http.client.methods.HttpGet; -import org.apache.http.client.methods.HttpPost; -import org.apache.http.client.methods.HttpRequestBase; -import org.apache.http.client.methods.HttpTrace; -import org.apache.http.entity.ContentType; -import org.apache.http.entity.StringEntity; -import org.apache.http.impl.client.CloseableHttpClient; -import org.apache.http.impl.client.HttpClientBuilder; -import org.apache.http.impl.conn.PoolingHttpClientConnectionManager; -import org.eclipse.jetty.server.Server; -import org.eclipse.jetty.servlet.ServletHandler; -import org.eclipse.jetty.servlet.ServletHolder; -import org.hl7.fhir.dstu2016may.model.CodeType; -import org.hl7.fhir.dstu2016may.model.Conformance; -import org.hl7.fhir.dstu2016may.model.DateType; -import org.hl7.fhir.dstu2016may.model.IdType; -import org.hl7.fhir.dstu2016may.model.OperationOutcome; -import org.hl7.fhir.dstu2016may.model.Patient; -import org.hl7.fhir.instance.model.api.IBaseResource; -import org.junit.jupiter.api.AfterAll; -import org.junit.jupiter.api.BeforeAll; -import org.junit.jupiter.api.Test; - -import java.net.URI; -import java.nio.charset.StandardCharsets; -import java.util.ArrayList; -import java.util.List; -import java.util.concurrent.TimeUnit; - -import static org.hamcrest.MatcherAssert.assertThat; -import static org.hamcrest.Matchers.contains; -import static org.hamcrest.Matchers.containsString; -import static org.hamcrest.Matchers.not; -import static org.junit.jupiter.api.Assertions.assertEquals; - -public class ServerMimetypeDstu2_1Test { - private static CloseableHttpClient ourClient; - - private static FhirContext ourCtx = FhirContext.forDstu2_1(); - private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(ServerMimetypeDstu2_1Test.class); - private static int ourPort; - private static Server ourServer; - - @Test - public void testConformanceMetadataUsesNewMimetypes() throws Exception { - HttpGet httpGet = new HttpGet("http://localhost:" + ourPort + "/metadata"); - CloseableHttpResponse status = ourClient.execute(httpGet); - try { - String content = IOUtils.toString(status.getEntity().getContent(), StandardCharsets.UTF_8); - ourLog.info(content); - Conformance conf = ourCtx.newXmlParser().parseResource(Conformance.class, content); - List strings = toStrings(conf.getFormat()); - assertThat(strings, contains(Constants.CT_FHIR_XML, Constants.CT_FHIR_JSON)); - } finally { - status.close(); - } - } - - - - private List toStrings(List theFormat) { - ArrayList retVal = new ArrayList<>(); - for (CodeType next : theFormat) { - retVal.add(next.asStringValue()); - } - return retVal; - } - - - - @Test - public void testCreateWithXmlLegacyNoAcceptHeader() throws Exception { - Patient p = new Patient(); - p.addName().addFamily("FAMILY"); - String enc = ourCtx.newXmlParser().encodeResourceToString(p); - String expectedResponseContent = ""; - - HttpPost httpPost = new HttpPost("http://localhost:" + ourPort + "/Patient"); - httpPost.setEntity(new StringEntity(enc, ContentType.parse(Constants.CT_FHIR_XML + "; charset=utf-8"))); - HttpResponse status = ourClient.execute(httpPost); - - String responseContent = IOUtils.toString(status.getEntity().getContent(), StandardCharsets.UTF_8); - IOUtils.closeQuietly(status.getEntity().getContent()); - - ourLog.info("Response was:\n{}", responseContent); - - assertEquals(201, status.getStatusLine().getStatusCode()); - assertEquals(Constants.CT_FHIR_XML, status.getFirstHeader("content-type").getValue().replaceAll(";.*", "")); - assertEquals(expectedResponseContent, responseContent); - } - - @Test - public void testHttpTraceNotEnabled() throws Exception { - HttpTrace req = new HttpTrace("http://localhost:" + ourPort + "/Patient"); - CloseableHttpResponse status = ourClient.execute(req); - try { - ourLog.info(status.toString()); - assertEquals(400, status.getStatusLine().getStatusCode()); - } finally { - IOUtils.closeQuietly(status.getEntity().getContent()); - } - } - - @Test - public void testHttpTrackNotEnabled() throws Exception { - HttpRequestBase req = new HttpRequestBase() { - @Override - public String getMethod() { - return "TRACK"; - }}; - req.setURI(new URI("http://localhost:" + ourPort + "/Patient")); - - CloseableHttpResponse status = ourClient.execute(req); - try { - ourLog.info(status.toString()); - assertEquals(400, status.getStatusLine().getStatusCode()); - } finally { - IOUtils.closeQuietly(status.getEntity().getContent()); - } - } - - @Test - public void testCreateWithXmlNewNoAcceptHeader() throws Exception { - Patient p = new Patient(); - p.addName().addFamily("FAMILY"); - String enc = ourCtx.newXmlParser().encodeResourceToString(p); - String expectedResponseContent = ""; - - HttpPost httpPost = new HttpPost("http://localhost:" + ourPort + "/Patient"); - httpPost.setEntity(new StringEntity(enc, ContentType.parse(Constants.CT_FHIR_XML+ "; charset=utf-8"))); - HttpResponse status = ourClient.execute(httpPost); - - String responseContent = IOUtils.toString(status.getEntity().getContent(), StandardCharsets.UTF_8); - IOUtils.closeQuietly(status.getEntity().getContent()); - - ourLog.info("Response was:\n{}", responseContent); - - assertEquals(201, status.getStatusLine().getStatusCode()); - assertEquals(Constants.CT_FHIR_XML, status.getFirstHeader("content-type").getValue().replaceAll(";.*", "")); - assertEquals(expectedResponseContent, responseContent); - } - - @Test - public void testCreateWithXmlNewWithAcceptHeaderReturnsOperationOutcome() throws Exception { - Patient p = new Patient(); - p.addName().addFamily("FAMILY"); - String enc = ourCtx.newXmlParser().encodeResourceToString(p); - String expectedResponseContent = ""; - - HttpPost httpPost = new HttpPost("http://localhost:" + ourPort + "/Patient"); - httpPost.setEntity(new StringEntity(enc, ContentType.parse(Constants.CT_FHIR_XML + "; charset=utf-8"))); - httpPost.addHeader(Constants.HEADER_ACCEPT, Constants.CT_FHIR_XML); - httpPost.addHeader(Constants.HEADER_PREFER, Constants.HEADER_PREFER_RETURN + "=" + Constants.HEADER_PREFER_RETURN_OPERATION_OUTCOME); - HttpResponse status = ourClient.execute(httpPost); - - String responseContent = IOUtils.toString(status.getEntity().getContent(), StandardCharsets.UTF_8); - IOUtils.closeQuietly(status.getEntity().getContent()); - - ourLog.info("Response was:\n{}", responseContent); - - assertEquals(201, status.getStatusLine().getStatusCode()); - assertEquals(Constants.CT_FHIR_XML, status.getFirstHeader("content-type").getValue().replaceAll(";.*", "")); - assertEquals(expectedResponseContent, responseContent); - } - - @Test - public void testCreateWithJsonLegacyNoAcceptHeader() throws Exception { - Patient p = new Patient(); - p.addName().addFamily("FAMILY"); - String enc = ourCtx.newJsonParser().encodeResourceToString(p); - String expectedResponseContent = "{\"resourceType\":\"Patient\",\"id\":\"1\",\"meta\":{\"versionId\":\"1\"},\"name\":[{\"family\":[\"FAMILY\"]}]}"; - - HttpPost httpPost = new HttpPost("http://localhost:" + ourPort + "/Patient"); - httpPost.setEntity(new StringEntity(enc, ContentType.parse(Constants.CT_FHIR_JSON + "; charset=utf-8"))); - HttpResponse status = ourClient.execute(httpPost); - - String responseContent = IOUtils.toString(status.getEntity().getContent(), StandardCharsets.UTF_8); - IOUtils.closeQuietly(status.getEntity().getContent()); - - ourLog.info("Response was:\n{}", responseContent); - - assertEquals(201, status.getStatusLine().getStatusCode()); - assertEquals(Constants.CT_FHIR_JSON, status.getFirstHeader("content-type").getValue().replaceAll(";.*", "")); - assertEquals(expectedResponseContent, responseContent); - } - - @Test - public void testCreateWithJsonNewNoAcceptHeaderReturnsOperationOutcome() throws Exception { - Patient p = new Patient(); - p.addName().addFamily("FAMILY"); - String enc = ourCtx.newJsonParser().encodeResourceToString(p); - String expectedResponseContent = "{\"resourceType\":\"OperationOutcome\",\"issue\":[{\"diagnostics\":\"FAMILY\"}]}"; - - HttpPost httpPost = new HttpPost("http://localhost:" + ourPort + "/Patient"); - httpPost.setEntity(new StringEntity(enc, ContentType.parse(Constants.CT_FHIR_JSON+ "; charset=utf-8"))); - httpPost.addHeader(Constants.HEADER_PREFER, Constants.HEADER_PREFER_RETURN + "=" + Constants.HEADER_PREFER_RETURN_OPERATION_OUTCOME); - HttpResponse status = ourClient.execute(httpPost); - - String responseContent = IOUtils.toString(status.getEntity().getContent(), StandardCharsets.UTF_8); - IOUtils.closeQuietly(status.getEntity().getContent()); - - ourLog.info("Response was:\n{}", responseContent); - - assertEquals(201, status.getStatusLine().getStatusCode()); - assertEquals(Constants.CT_FHIR_JSON, status.getFirstHeader("content-type").getValue().replaceAll(";.*", "")); - assertEquals(expectedResponseContent, responseContent); - } - - @Test - public void testCreateWithJsonNewWithAcceptHeader() throws Exception { - Patient p = new Patient(); - p.addName().addFamily("FAMILY"); - String enc = ourCtx.newJsonParser().encodeResourceToString(p); - String expectedResponseContent = "{\"resourceType\":\"Patient\",\"id\":\"1\",\"meta\":{\"versionId\":\"1\"},\"name\":[{\"family\":[\"FAMILY\"]}]}"; - - HttpPost httpPost = new HttpPost("http://localhost:" + ourPort + "/Patient"); - httpPost.setEntity(new StringEntity(enc, ContentType.parse(Constants.CT_FHIR_JSON + "; charset=utf-8"))); - httpPost.addHeader(Constants.HEADER_ACCEPT, Constants.CT_FHIR_JSON); - HttpResponse status = ourClient.execute(httpPost); - - String responseContent = IOUtils.toString(status.getEntity().getContent(), StandardCharsets.UTF_8); - IOUtils.closeQuietly(status.getEntity().getContent()); - - ourLog.info("Response was:\n{}", responseContent); - - assertEquals(201, status.getStatusLine().getStatusCode()); - assertEquals(Constants.CT_FHIR_JSON, status.getFirstHeader("content-type").getValue().replaceAll(";.*", "")); - assertEquals(expectedResponseContent, responseContent); - } - - @Test - public void testSearchWithFormatXmlSimple() throws Exception { - - HttpGet httpGet = new HttpGet("http://localhost:" + ourPort + "/Patient?_format=xml"); - HttpResponse status = ourClient.execute(httpGet); - - String responseContent = IOUtils.toString(status.getEntity().getContent(), StandardCharsets.UTF_8); - IOUtils.closeQuietly(status.getEntity().getContent()); - - ourLog.info("Response was:\n{}", responseContent); - - assertEquals(200, status.getStatusLine().getStatusCode()); - assertThat(responseContent, containsString("")); - assertThat(responseContent, not(containsString("http://hl7.org/fhir/"))); - assertEquals(Constants.CT_FHIR_XML, status.getFirstHeader("content-type").getValue().replaceAll(";.*", "")); - } - - @Test - public void testSearchWithFormatXmlLegacy() throws Exception { - - HttpGet httpGet = new HttpGet("http://localhost:" + ourPort + "/Patient?_format=" + Constants.CT_FHIR_XML); - HttpResponse status = ourClient.execute(httpGet); - - String responseContent = IOUtils.toString(status.getEntity().getContent(), StandardCharsets.UTF_8); - IOUtils.closeQuietly(status.getEntity().getContent()); - - ourLog.info("Response was:\n{}", responseContent); - - assertEquals(200, status.getStatusLine().getStatusCode()); - assertThat(responseContent, containsString("")); - assertThat(responseContent, not(containsString("http://hl7.org/fhir/"))); - assertEquals(Constants.CT_FHIR_XML, status.getFirstHeader("content-type").getValue().replaceAll(";.*", "")); - } - - @Test - public void testSearchWithFormatXmlNew() throws Exception { - - HttpGet httpGet = new HttpGet("http://localhost:" + ourPort + "/Patient?_format=" + Constants.CT_FHIR_XML); - HttpResponse status = ourClient.execute(httpGet); - - String responseContent = IOUtils.toString(status.getEntity().getContent(), StandardCharsets.UTF_8); - IOUtils.closeQuietly(status.getEntity().getContent()); - - ourLog.info("Response was:\n{}", responseContent); - - assertEquals(200, status.getStatusLine().getStatusCode()); - assertThat(responseContent, containsString("")); - assertThat(responseContent, not(containsString("http://hl7.org/fhir/"))); - assertEquals(Constants.CT_FHIR_XML, status.getFirstHeader("content-type").getValue().replaceAll(";.*", "")); - } - - - - @Test - public void testSearchWithFormatJsonSimple() throws Exception { - - HttpGet httpGet = new HttpGet("http://localhost:" + ourPort + "/Patient?_format=json"); - HttpResponse status = ourClient.execute(httpGet); - - String responseContent = IOUtils.toString(status.getEntity().getContent(), StandardCharsets.UTF_8); - IOUtils.closeQuietly(status.getEntity().getContent()); - - ourLog.info("Response was:\n{}", responseContent); - - assertEquals(200, status.getStatusLine().getStatusCode()); - assertThat(responseContent, containsString("\"resourceType\"")); - assertEquals(Constants.CT_FHIR_JSON, status.getFirstHeader("content-type").getValue().replaceAll(";.*", "")); - } - - @Test - public void testSearchWithFormatJsonLegacy() throws Exception { - - HttpGet httpGet = new HttpGet("http://localhost:" + ourPort + "/Patient?_format=" + Constants.CT_FHIR_JSON); - HttpResponse status = ourClient.execute(httpGet); - - String responseContent = IOUtils.toString(status.getEntity().getContent(), StandardCharsets.UTF_8); - IOUtils.closeQuietly(status.getEntity().getContent()); - - ourLog.info("Response was:\n{}", responseContent); - - assertEquals(200, status.getStatusLine().getStatusCode()); - assertThat(responseContent, containsString("\"resourceType\"")); - assertEquals(Constants.CT_FHIR_JSON, status.getFirstHeader("content-type").getValue().replaceAll(";.*", "")); - } - - @Test - public void testSearchWithFormatJsonNew() throws Exception { - - HttpGet httpGet = new HttpGet("http://localhost:" + ourPort + "/Patient?_format=" + Constants.CT_FHIR_JSON); - HttpResponse status = ourClient.execute(httpGet); - - String responseContent = IOUtils.toString(status.getEntity().getContent(), StandardCharsets.UTF_8); - IOUtils.closeQuietly(status.getEntity().getContent()); - - ourLog.info("Response was:\n{}", responseContent); - - assertEquals(200, status.getStatusLine().getStatusCode()); - assertThat(responseContent, containsString("\"resourceType\"")); - assertEquals(Constants.CT_FHIR_JSON, status.getFirstHeader("content-type").getValue().replaceAll(";.*", "")); - } - - @AfterAll - public static void afterClassClearContext() throws Exception { - JettyUtil.closeServer(ourServer); - TestUtil.randomizeLocaleAndTimezone(); - } - - @BeforeAll - public static void beforeClass() throws Exception { - ourServer = new Server(0); - - PatientProvider patientProvider = new PatientProvider(); - - ServletHandler proxyHandler = new ServletHandler(); - RestfulServer servlet = new RestfulServer(ourCtx); - servlet.setDefaultResponseEncoding(EncodingEnum.XML); - servlet.setResourceProviders(patientProvider); - - ServletHolder servletHolder = new ServletHolder(servlet); - proxyHandler.addServletWithMapping(servletHolder, "/*"); - ourServer.setHandler(proxyHandler); - JettyUtil.startServer(ourServer); - ourPort = JettyUtil.getPortForStartedServer(ourServer); - - PoolingHttpClientConnectionManager connectionManager = new PoolingHttpClientConnectionManager(5000, TimeUnit.MILLISECONDS); - HttpClientBuilder builder = HttpClientBuilder.create(); - builder.setConnectionManager(connectionManager); - ourClient = builder.build(); - - } - - public static class PatientProvider implements IResourceProvider { - - @Create() - public MethodOutcome create(@ResourceParam Patient theIdParam) { - OperationOutcome oo = new OperationOutcome(); - oo.addIssue().setDiagnostics(theIdParam.getName().get(0).getFamilyAsSingleString()); - theIdParam.setId("1"); - theIdParam.getMeta().setVersionId("1"); - return new MethodOutcome(new IdType("Patient", "1"), true).setOperationOutcome(oo).setResource(theIdParam); - } - - @Override - public Class getResourceType() { - return Patient.class; - } - - @Read() - public MyPatientWithExtensions read(@IdParam IdType theIdParam) { - MyPatientWithExtensions p0 = new MyPatientWithExtensions(); - p0.setId(theIdParam); - p0.setDateExt(new DateType("2011-01-01")); - return p0; - } - - @Search - public List search() { - ArrayList retVal = new ArrayList<>(); - - MyPatientWithExtensions p0 = new MyPatientWithExtensions(); - p0.setId(new IdType("Patient/0")); - p0.setDateExt(new DateType("2011-01-01")); - retVal.add(p0); - - Patient p1 = new Patient(); - p1.setId(new IdType("Patient/1")); - p1.addName().addFamily("The Family"); - retVal.add(p1); - - return retVal; - } - - } - -} diff --git a/hapi-fhir-structures-dstu2.1/src/test/java/ca/uhn/fhir/rest/server/ServerUsingOldTypesDstu2_1Test.java b/hapi-fhir-structures-dstu2.1/src/test/java/ca/uhn/fhir/rest/server/ServerUsingOldTypesDstu2_1Test.java index afe0e9b97e0..7a4c5855cee 100644 --- a/hapi-fhir-structures-dstu2.1/src/test/java/ca/uhn/fhir/rest/server/ServerUsingOldTypesDstu2_1Test.java +++ b/hapi-fhir-structures-dstu2.1/src/test/java/ca/uhn/fhir/rest/server/ServerUsingOldTypesDstu2_1Test.java @@ -14,7 +14,7 @@ import org.hl7.fhir.instance.model.api.IBaseResource; import org.junit.jupiter.api.AfterAll; import org.junit.jupiter.api.Test; -import javax.servlet.ServletException; +import jakarta.servlet.ServletException; import static org.hamcrest.MatcherAssert.assertThat; import static org.junit.jupiter.api.Assertions.fail; diff --git a/hapi-fhir-structures-dstu2.1/src/test/java/ca/uhn/fhir/rest/server/UnclassifiedServerExceptionDstu2_1Test.java b/hapi-fhir-structures-dstu2.1/src/test/java/ca/uhn/fhir/rest/server/UnclassifiedServerExceptionDstu2_1Test.java deleted file mode 100644 index 20b571725b5..00000000000 --- a/hapi-fhir-structures-dstu2.1/src/test/java/ca/uhn/fhir/rest/server/UnclassifiedServerExceptionDstu2_1Test.java +++ /dev/null @@ -1,108 +0,0 @@ -package ca.uhn.fhir.rest.server; - -import ca.uhn.fhir.context.FhirContext; -import ca.uhn.fhir.rest.annotation.Search; -import ca.uhn.fhir.rest.server.exceptions.UnclassifiedServerFailureException; -import ca.uhn.fhir.test.utilities.JettyUtil; -import ca.uhn.fhir.util.TestUtil; -import org.apache.commons.io.IOUtils; -import org.apache.http.client.methods.CloseableHttpResponse; -import org.apache.http.client.methods.HttpGet; -import org.apache.http.impl.client.CloseableHttpClient; -import org.apache.http.impl.client.HttpClientBuilder; -import org.apache.http.impl.conn.PoolingHttpClientConnectionManager; -import org.eclipse.jetty.server.Server; -import org.eclipse.jetty.servlet.ServletHandler; -import org.eclipse.jetty.servlet.ServletHolder; -import org.hl7.fhir.dstu2016may.model.OperationOutcome; -import org.hl7.fhir.dstu2016may.model.OperationOutcome.IssueType; -import org.hl7.fhir.dstu2016may.model.Patient; -import org.hl7.fhir.instance.model.api.IBaseResource; -import org.junit.jupiter.api.AfterAll; -import org.junit.jupiter.api.BeforeAll; -import org.junit.jupiter.api.Test; - -import java.nio.charset.StandardCharsets; -import java.util.List; -import java.util.concurrent.TimeUnit; - -import static org.hamcrest.MatcherAssert.assertThat; -import static org.hamcrest.Matchers.stringContainsInOrder; -import static org.junit.jupiter.api.Assertions.assertEquals; - -public class UnclassifiedServerExceptionDstu2_1Test { - - private static CloseableHttpClient ourClient; - private static FhirContext ourCtx = FhirContext.forDstu2_1(); - private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(UnclassifiedServerExceptionDstu2_1Test.class); - private static int ourPort; - private static Server ourServer; - public static UnclassifiedServerFailureException ourException; - - @Test - public void testSearch() throws Exception { - - OperationOutcome operationOutcome = new OperationOutcome(); - operationOutcome.addIssue().setCode(IssueType.BUSINESSRULE); - ourException = new UnclassifiedServerFailureException(477, "SOME MESSAGE", operationOutcome); - - HttpGet httpGet = new HttpGet("http://localhost:" + ourPort + "/Patient"); - CloseableHttpResponse status = ourClient.execute(httpGet); - try { - String responseContent = IOUtils.toString(status.getEntity().getContent(), StandardCharsets.UTF_8); - ourLog.info(status.getStatusLine().toString()); - ourLog.info(responseContent); - assertEquals(477, status.getStatusLine().getStatusCode()); - //assertEquals("SOME MESSAGE", status.getStatusLine().getReasonPhrase()); - assertThat(responseContent, stringContainsInOrder("business-rule")); - } finally { - IOUtils.closeQuietly(status.getEntity().getContent()); - } - - } - - @AfterAll - public static void afterClassClearContext() throws Exception { - JettyUtil.closeServer(ourServer); - TestUtil.randomizeLocaleAndTimezone(); - } - - @BeforeAll - public static void beforeClass() throws Exception { - ourServer = new Server(0); - - DummyPatientResourceProvider patientProvider = new DummyPatientResourceProvider(); - - ServletHandler proxyHandler = new ServletHandler(); - RestfulServer servlet = new RestfulServer(ourCtx); - servlet.setPagingProvider(new FifoMemoryPagingProvider(10)); - - servlet.setResourceProviders(patientProvider); - ServletHolder servletHolder = new ServletHolder(servlet); - proxyHandler.addServletWithMapping(servletHolder, "/*"); - ourServer.setHandler(proxyHandler); - JettyUtil.startServer(ourServer); - ourPort = JettyUtil.getPortForStartedServer(ourServer); - - PoolingHttpClientConnectionManager connectionManager = new PoolingHttpClientConnectionManager(5000, TimeUnit.MILLISECONDS); - HttpClientBuilder builder = HttpClientBuilder.create(); - builder.setConnectionManager(connectionManager); - ourClient = builder.build(); - - } - - public static class DummyPatientResourceProvider implements IResourceProvider { - - @Override - public Class getResourceType() { - return Patient.class; - } - - @Search() - public List search() { - throw ourException; - } - - } - -} diff --git a/hapi-fhir-structures-dstu2.1/src/test/java/ca/uhn/fhir/rest/server/UpdateDstu2_1Test.java b/hapi-fhir-structures-dstu2.1/src/test/java/ca/uhn/fhir/rest/server/UpdateDstu2_1Test.java deleted file mode 100644 index 6dbba1ac3d9..00000000000 --- a/hapi-fhir-structures-dstu2.1/src/test/java/ca/uhn/fhir/rest/server/UpdateDstu2_1Test.java +++ /dev/null @@ -1,195 +0,0 @@ -package ca.uhn.fhir.rest.server; - -import ca.uhn.fhir.context.FhirContext; -import ca.uhn.fhir.i18n.Msg; -import ca.uhn.fhir.rest.annotation.ConditionalUrlParam; -import ca.uhn.fhir.rest.annotation.IdParam; -import ca.uhn.fhir.rest.annotation.ResourceParam; -import ca.uhn.fhir.rest.annotation.Update; -import ca.uhn.fhir.rest.api.Constants; -import ca.uhn.fhir.rest.api.MethodOutcome; -import ca.uhn.fhir.test.utilities.JettyUtil; -import ca.uhn.fhir.util.TestUtil; -import org.apache.commons.io.IOUtils; -import org.apache.http.HttpResponse; -import org.apache.http.client.methods.CloseableHttpResponse; -import org.apache.http.client.methods.HttpPut; -import org.apache.http.entity.ContentType; -import org.apache.http.entity.StringEntity; -import org.apache.http.impl.client.CloseableHttpClient; -import org.apache.http.impl.client.HttpClientBuilder; -import org.apache.http.impl.conn.PoolingHttpClientConnectionManager; -import org.eclipse.jetty.server.Server; -import org.eclipse.jetty.servlet.ServletHandler; -import org.eclipse.jetty.servlet.ServletHolder; -import org.hl7.fhir.dstu2016may.model.IdType; -import org.hl7.fhir.dstu2016may.model.OperationOutcome; -import org.hl7.fhir.dstu2016may.model.Patient; -import org.junit.jupiter.api.AfterAll; -import org.junit.jupiter.api.BeforeAll; -import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.Test; - -import java.nio.charset.StandardCharsets; -import java.util.concurrent.TimeUnit; - -import static org.hamcrest.MatcherAssert.assertThat; -import static org.hamcrest.Matchers.containsString; -import static org.junit.jupiter.api.Assertions.assertEquals; -import static org.junit.jupiter.api.Assertions.assertNull; - -public class UpdateDstu2_1Test { - private static CloseableHttpClient ourClient; - private static String ourConditionalUrl; - private static FhirContext ourCtx = FhirContext.forDstu2_1(); - private static IdType ourId; - private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(UpdateDstu2_1Test.class); - private static int ourPort; - private static Server ourServer; - - @BeforeEach - public void before() { - ourConditionalUrl = null; - ourId = null; - } - - @Test - public void testUpdateConditional() throws Exception { - - Patient patient = new Patient(); - patient.setId("001"); - patient.addIdentifier().setValue("002"); - - HttpPut httpPost = new HttpPut("http://localhost:" + ourPort + "/Patient?_id=001"); - httpPost.setEntity(new StringEntity(ourCtx.newXmlParser().encodeResourceToString(patient), ContentType.create(Constants.CT_FHIR_XML, "UTF-8"))); - - CloseableHttpResponse status = ourClient.execute(httpPost); - try { - String responseContent = IOUtils.toString(status.getEntity().getContent(), StandardCharsets.UTF_8); - ourLog.info("Response was:\n{}", responseContent); - assertEquals(200, status.getStatusLine().getStatusCode()); - - assertEquals("Patient?_id=001",ourConditionalUrl); - assertEquals(null, ourId); - } finally { - IOUtils.closeQuietly(status.getEntity().getContent()); - } - - } - - @Test - public void testUpdateMissingIdInBody() throws Exception { - - Patient patient = new Patient(); - patient.addIdentifier().setValue("002"); - - HttpPut httpPost = new HttpPut("http://localhost:" + ourPort + "/Patient/001"); - httpPost.setEntity(new StringEntity(ourCtx.newXmlParser().encodeResourceToString(patient), ContentType.create(Constants.CT_FHIR_XML, "UTF-8"))); - - HttpResponse status = ourClient.execute(httpPost); - - String responseContent = IOUtils.toString(status.getEntity().getContent(), StandardCharsets.UTF_8); - IOUtils.closeQuietly(status.getEntity().getContent()); - - ourLog.info("Response was:\n{}", responseContent); - - assertEquals(400, status.getStatusLine().getStatusCode()); - - OperationOutcome oo = ourCtx.newXmlParser().parseResource(OperationOutcome.class, responseContent); - assertEquals(Msg.code(419) + "Can not update resource, resource body must contain an ID element for update (PUT) operation", oo.getIssue().get(0).getDiagnostics()); - } - - @Test - public void testUpdateNormal() throws Exception { - - Patient patient = new Patient(); - patient.setId("001"); - patient.addIdentifier().setValue("002"); - - HttpPut httpPost = new HttpPut("http://localhost:" + ourPort + "/Patient/001"); - httpPost.setEntity(new StringEntity(ourCtx.newXmlParser().encodeResourceToString(patient), ContentType.create(Constants.CT_FHIR_XML, "UTF-8"))); - - CloseableHttpResponse status = ourClient.execute(httpPost); - try { - String responseContent = IOUtils.toString(status.getEntity().getContent(), StandardCharsets.UTF_8); - ourLog.info("Response was:\n{}", responseContent); - assertEquals(200, status.getStatusLine().getStatusCode()); - - assertNull(ourConditionalUrl); - assertEquals("Patient/001", ourId.getValue()); - } finally { - IOUtils.closeQuietly(status.getEntity().getContent()); - } - - } - - @Test - public void testUpdateWrongIdInBody() throws Exception { - - Patient patient = new Patient(); - patient.setId("Patient/3/_history/4"); - patient.addIdentifier().setValue("002"); - - HttpPut httpPost = new HttpPut("http://localhost:" + ourPort + "/Patient/1/_history/2"); - httpPost.setEntity(new StringEntity(ourCtx.newXmlParser().encodeResourceToString(patient), ContentType.create(Constants.CT_FHIR_XML, "UTF-8"))); - - HttpResponse status = ourClient.execute(httpPost); - - String responseContent = IOUtils.toString(status.getEntity().getContent(), StandardCharsets.UTF_8); - IOUtils.closeQuietly(status.getEntity().getContent()); - - ourLog.info("Response was:\n{}", responseContent); - - assertEquals(400, status.getStatusLine().getStatusCode()); - assertThat(responseContent, containsString("Resource body ID of "3" does not match")); - } - - @AfterAll - public static void afterClassClearContext() throws Exception { - JettyUtil.closeServer(ourServer); - TestUtil.randomizeLocaleAndTimezone(); - } - - @BeforeAll - public static void beforeClass() throws Exception { - ourServer = new Server(0); - - ServletHandler proxyHandler = new ServletHandler(); - RestfulServer servlet = new RestfulServer(ourCtx); - servlet.setResourceProviders(new PatientProvider()); - ServletHolder servletHolder = new ServletHolder(servlet); - proxyHandler.addServletWithMapping(servletHolder, "/*"); - ourServer.setHandler(proxyHandler); - JettyUtil.startServer(ourServer); - ourPort = JettyUtil.getPortForStartedServer(ourServer); - - PoolingHttpClientConnectionManager connectionManager = new PoolingHttpClientConnectionManager(5000, TimeUnit.MILLISECONDS); - HttpClientBuilder builder = HttpClientBuilder.create(); - builder.setConnectionManager(connectionManager); - ourClient = builder.build(); - - } - - public static class PatientProvider implements IResourceProvider { - - @Override - public Class getResourceType() { - return Patient.class; - } - - @Update() - public MethodOutcome updatePatient(@IdParam IdType theId, @ResourceParam Patient thePatient, @ConditionalUrlParam String theConditionalUrl) { - ourId = theId; - ourConditionalUrl = theConditionalUrl; - IdType id = theId != null ? theId.withVersion(thePatient.getIdentifier().get(0).getValue()) : new IdType("Patient/1"); - OperationOutcome oo = new OperationOutcome(); - oo.addIssue().setDiagnostics("OODETAILS"); - if (id.getValueAsString().contains("CREATE")) { - return new MethodOutcome(id, oo, true); - } - - return new MethodOutcome(id, oo); - } - - } -} diff --git a/hapi-fhir-structures-dstu2.1/src/test/java/ca/uhn/fhir/rest/server/ValidateDstu2_1Test.java b/hapi-fhir-structures-dstu2.1/src/test/java/ca/uhn/fhir/rest/server/ValidateDstu2_1Test.java deleted file mode 100644 index 9f928939725..00000000000 --- a/hapi-fhir-structures-dstu2.1/src/test/java/ca/uhn/fhir/rest/server/ValidateDstu2_1Test.java +++ /dev/null @@ -1,327 +0,0 @@ -package ca.uhn.fhir.rest.server; - -import ca.uhn.fhir.context.FhirContext; -import ca.uhn.fhir.rest.annotation.IdParam; -import ca.uhn.fhir.rest.annotation.ResourceParam; -import ca.uhn.fhir.rest.annotation.Validate; -import ca.uhn.fhir.rest.api.Constants; -import ca.uhn.fhir.rest.api.EncodingEnum; -import ca.uhn.fhir.rest.api.MethodOutcome; -import ca.uhn.fhir.rest.api.ValidationModeEnum; -import ca.uhn.fhir.test.utilities.JettyUtil; -import ca.uhn.fhir.util.TestUtil; -import org.apache.commons.io.IOUtils; -import org.apache.http.HttpResponse; -import org.apache.http.client.methods.HttpGet; -import org.apache.http.client.methods.HttpPost; -import org.apache.http.entity.ContentType; -import org.apache.http.entity.StringEntity; -import org.apache.http.impl.client.CloseableHttpClient; -import org.apache.http.impl.client.HttpClientBuilder; -import org.apache.http.impl.conn.PoolingHttpClientConnectionManager; -import org.eclipse.jetty.server.Server; -import org.eclipse.jetty.servlet.ServletHandler; -import org.eclipse.jetty.servlet.ServletHolder; -import org.hl7.fhir.dstu2016may.model.CodeType; -import org.hl7.fhir.dstu2016may.model.IdType; -import org.hl7.fhir.dstu2016may.model.OperationOutcome; -import org.hl7.fhir.dstu2016may.model.Organization; -import org.hl7.fhir.dstu2016may.model.Parameters; -import org.hl7.fhir.dstu2016may.model.Patient; -import org.hl7.fhir.dstu2016may.model.StringType; -import org.junit.jupiter.api.AfterAll; -import org.junit.jupiter.api.BeforeAll; -import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.Test; - -import java.util.concurrent.TimeUnit; - -import static org.hamcrest.MatcherAssert.assertThat; -import static org.hamcrest.Matchers.stringContainsInOrder; -import static org.junit.jupiter.api.Assertions.assertEquals; - -public class ValidateDstu2_1Test { - private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(ValidateDstu2_1Test.class); - public static Patient ourLastPatient; - private static CloseableHttpClient ourClient; - private static FhirContext ourCtx = FhirContext.forDstu2_1(); - private static EncodingEnum ourLastEncoding; - private static IdType ourLastId; - private static ValidationModeEnum ourLastMode; - private static String ourLastProfile; - private static String ourLastResourceBody; - private static OperationOutcome ourOutcomeToReturn; - private static int ourPort; - - private static Server ourServer; - - @BeforeEach() - public void before() { - ourLastResourceBody = null; - ourLastEncoding = null; - ourOutcomeToReturn = null; - ourLastMode = null; - ourLastProfile = null; - } - - @Test - public void testValidate() throws Exception { - - Patient patient = new Patient(); - patient.addIdentifier().setValue("001"); - patient.addIdentifier().setValue("002"); - - Parameters params = new Parameters(); - params.addParameter().setName("resource").setResource(patient); - - HttpPost httpPost = new HttpPost("http://localhost:" + ourPort + "/Patient/$validate"); - httpPost.setEntity(new StringEntity(ourCtx.newXmlParser().encodeResourceToString(params), ContentType.create(Constants.CT_FHIR_XML, "UTF-8"))); - - HttpResponse status = ourClient.execute(httpPost); - String resp = IOUtils.toString(status.getEntity().getContent()); - IOUtils.closeQuietly(status.getEntity().getContent()); - - assertEquals(200, status.getStatusLine().getStatusCode()); - - assertThat(resp, stringContainsInOrder(" getResourceType() { - return Organization.class; - } - - @Validate() - public MethodOutcome validate(@ResourceParam String theResourceBody, @ResourceParam EncodingEnum theEncoding) { - ourLastResourceBody = theResourceBody; - ourLastEncoding = theEncoding; - - return new MethodOutcome(new IdType("001")); - } - - } - - public static class PatientProvider implements IResourceProvider { - - @Override - public Class getResourceType() { - return Patient.class; - } - - @Validate() - public MethodOutcome validatePatient(@ResourceParam Patient thePatient, @IdParam(optional = true) IdType theId, @Validate.Mode ValidationModeEnum theMode, @Validate.Profile String theProfile) { - - ourLastPatient = thePatient; - ourLastId = theId; - IdType id; - if (thePatient != null) { - id = new IdType(thePatient.getIdentifier().get(0).getValue()); - if (thePatient.getIdElement().isEmpty() == false) { - id = thePatient.getIdElement(); - } - } else { - id = new IdType("1"); - } - ourLastMode = theMode; - ourLastProfile = theProfile; - - MethodOutcome outcome = new MethodOutcome(id.withVersion("002")); - outcome.setOperationOutcome(ourOutcomeToReturn); - return outcome; - } - - } - - @AfterAll - public static void afterClassClearContext() throws Exception { - JettyUtil.closeServer(ourServer); - TestUtil.randomizeLocaleAndTimezone(); - } - - @BeforeAll - public static void beforeClass() throws Exception { - ourServer = new Server(0); - - PatientProvider patientProvider = new PatientProvider(); - - ServletHandler proxyHandler = new ServletHandler(); - RestfulServer servlet = new RestfulServer(ourCtx); - servlet.setResourceProviders(patientProvider, new OrganizationProvider()); - servlet.setDefaultResponseEncoding(EncodingEnum.XML); - - ServletHolder servletHolder = new ServletHolder(servlet); - proxyHandler.addServletWithMapping(servletHolder, "/*"); - ourServer.setHandler(proxyHandler); - JettyUtil.startServer(ourServer); - ourPort = JettyUtil.getPortForStartedServer(ourServer); - - PoolingHttpClientConnectionManager connectionManager = new PoolingHttpClientConnectionManager(5000, TimeUnit.MILLISECONDS); - HttpClientBuilder builder = HttpClientBuilder.create(); - builder.setConnectionManager(connectionManager); - ourClient = builder.build(); - - } - -} diff --git a/hapi-fhir-structures-dstu2/pom.xml b/hapi-fhir-structures-dstu2/pom.xml index c1e21dc8588..4532fb82ccf 100644 --- a/hapi-fhir-structures-dstu2/pom.xml +++ b/hapi-fhir-structures-dstu2/pom.xml @@ -4,7 +4,7 @@ ca.uhn.hapi.fhir hapi-deployable-pom - 6.9.7-SNAPSHOT + 6.11.7-SNAPSHOT ../hapi-deployable-pom/pom.xml @@ -49,8 +49,8 @@
    - javax.servlet - javax.servlet-api + jakarta.servlet + jakarta.servlet-api provided @@ -62,36 +62,6 @@ xmlunit-core test
    - - org.eclipse.jetty - jetty-servlets - test - - - org.eclipse.jetty - jetty-server - test - - - org.eclipse.jetty - jetty-servlet - test - - - org.eclipse.jetty - jetty-util - test - - - org.eclipse.jetty - jetty-webapp - test - - - org.eclipse.jetty - jetty-http - test - ch.qos.logback logback-classic @@ -112,18 +82,18 @@ - com.helger - ph-schematron + com.helger.schematron + ph-schematron-api test - javax.activation - javax.activation-api + com.helger.schematron + ph-schematron-xslt test - javax.xml.bind - jaxb-api + jakarta.activation + jakarta.activation-api test @@ -372,7 +342,7 @@ --> - javax.servlet*;resolution:=optional, + jakarta.servlet*;resolution:=optional, * diff --git a/hapi-fhir-structures-dstu2/src/main/java/ca/uhn/fhir/rest/server/provider/dstu2/Dstu2BundleFactory.java b/hapi-fhir-structures-dstu2/src/main/java/ca/uhn/fhir/rest/server/provider/dstu2/Dstu2BundleFactory.java index a64ae6236fa..3dfa2f4ccda 100644 --- a/hapi-fhir-structures-dstu2/src/main/java/ca/uhn/fhir/rest/server/provider/dstu2/Dstu2BundleFactory.java +++ b/hapi-fhir-structures-dstu2/src/main/java/ca/uhn/fhir/rest/server/provider/dstu2/Dstu2BundleFactory.java @@ -37,6 +37,7 @@ import ca.uhn.fhir.rest.api.BundleLinks; import ca.uhn.fhir.rest.api.Constants; import ca.uhn.fhir.rest.api.IVersionSpecificBundleFactory; import ca.uhn.fhir.util.ResourceReferenceInfo; +import jakarta.annotation.Nonnull; import org.hl7.fhir.instance.model.api.IBaseResource; import org.hl7.fhir.instance.model.api.IPrimitiveType; @@ -46,7 +47,6 @@ import java.util.HashSet; import java.util.List; import java.util.Set; import java.util.UUID; -import javax.annotation.Nonnull; import static org.apache.commons.lang3.StringUtils.isNotBlank; diff --git a/hapi-fhir-structures-dstu2/src/main/java/ca/uhn/fhir/rest/server/provider/dstu2/ServerConformanceProvider.java b/hapi-fhir-structures-dstu2/src/main/java/ca/uhn/fhir/rest/server/provider/dstu2/ServerConformanceProvider.java index bedc07b6338..413c733a15d 100644 --- a/hapi-fhir-structures-dstu2/src/main/java/ca/uhn/fhir/rest/server/provider/dstu2/ServerConformanceProvider.java +++ b/hapi-fhir-structures-dstu2/src/main/java/ca/uhn/fhir/rest/server/provider/dstu2/ServerConformanceProvider.java @@ -45,14 +45,14 @@ import ca.uhn.fhir.rest.server.method.OperationMethodBinding.ReturnType; import ca.uhn.fhir.rest.server.*; import ca.uhn.fhir.rest.server.method.*; import ca.uhn.fhir.rest.server.util.BaseServerCapabilityStatementProvider; +import jakarta.servlet.ServletContext; +import jakarta.servlet.http.HttpServletRequest; import org.apache.commons.lang3.StringUtils; import org.hl7.fhir.instance.model.api.IBaseResource; import org.hl7.fhir.instance.model.api.IPrimitiveType; import java.util.Map.Entry; import java.util.*; -import javax.servlet.ServletContext; -import javax.servlet.http.HttpServletRequest; import static org.apache.commons.lang3.StringUtils.isBlank; import static org.apache.commons.lang3.StringUtils.isNotBlank; diff --git a/hapi-fhir-structures-dstu2/src/test/java/ca/uhn/fhir/narrative/DefaultThymeleafNarrativeGeneratorDstu2Test.java b/hapi-fhir-structures-dstu2/src/test/java/ca/uhn/fhir/narrative/DefaultThymeleafNarrativeGeneratorDstu2Test.java index e809ef4a6bc..aab91a5c8c4 100644 --- a/hapi-fhir-structures-dstu2/src/test/java/ca/uhn/fhir/narrative/DefaultThymeleafNarrativeGeneratorDstu2Test.java +++ b/hapi-fhir-structures-dstu2/src/test/java/ca/uhn/fhir/narrative/DefaultThymeleafNarrativeGeneratorDstu2Test.java @@ -88,7 +88,7 @@ public class DefaultThymeleafNarrativeGeneratorDstu2Test { String parse = "\n" + " \n" + " \n" + - " \n" + + " \n" + " \n" + " \n" + " \n" + diff --git a/hapi-fhir-structures-dstu2/src/test/java/ca/uhn/fhir/rest/client/GenericClientDstu2Test.java b/hapi-fhir-structures-dstu2/src/test/java/ca/uhn/fhir/rest/client/GenericClientDstu2Test.java index 159424fbb0e..542b490ee4c 100644 --- a/hapi-fhir-structures-dstu2/src/test/java/ca/uhn/fhir/rest/client/GenericClientDstu2Test.java +++ b/hapi-fhir-structures-dstu2/src/test/java/ca/uhn/fhir/rest/client/GenericClientDstu2Test.java @@ -72,7 +72,7 @@ import org.mockito.internal.stubbing.defaultanswers.ReturnsDeepStubs; import org.mockito.invocation.InvocationOnMock; import org.mockito.stubbing.Answer; -import javax.annotation.Nonnull; +import jakarta.annotation.Nonnull; import java.io.IOException; import java.io.InputStream; import java.io.StringReader; diff --git a/hapi-fhir-structures-dstu2/src/test/java/ca/uhn/fhir/rest/client/apache/ApacheClientIntegrationTest.java b/hapi-fhir-structures-dstu2/src/test/java/ca/uhn/fhir/rest/client/apache/ApacheClientIntegrationTest.java index b4a6be35be7..24f1cbd4f44 100644 --- a/hapi-fhir-structures-dstu2/src/test/java/ca/uhn/fhir/rest/client/apache/ApacheClientIntegrationTest.java +++ b/hapi-fhir-structures-dstu2/src/test/java/ca/uhn/fhir/rest/client/apache/ApacheClientIntegrationTest.java @@ -5,21 +5,19 @@ import ca.uhn.fhir.model.dstu2.resource.Bundle; import ca.uhn.fhir.model.dstu2.resource.Patient; import ca.uhn.fhir.rest.annotation.OptionalParam; import ca.uhn.fhir.rest.annotation.Search; -import ca.uhn.fhir.rest.client.api.IGenericClient; +import ca.uhn.fhir.rest.api.EncodingEnum; import ca.uhn.fhir.rest.param.StringParam; +import ca.uhn.fhir.rest.server.FifoMemoryPagingProvider; import ca.uhn.fhir.rest.server.IResourceProvider; -import ca.uhn.fhir.rest.server.RestfulServer; import ca.uhn.fhir.rest.server.interceptor.VerboseLoggingInterceptor; -import ca.uhn.fhir.test.utilities.JettyUtil; +import ca.uhn.fhir.test.utilities.HttpClientExtension; +import ca.uhn.fhir.test.utilities.server.RestfulServerExtension; import ca.uhn.fhir.util.TestUtil; -import org.eclipse.jetty.server.Server; -import org.eclipse.jetty.servlet.ServletHandler; -import org.eclipse.jetty.servlet.ServletHolder; import org.hl7.fhir.instance.model.api.IBaseResource; import org.junit.jupiter.api.AfterAll; -import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.RegisterExtension; import java.util.ArrayList; import java.util.List; @@ -28,12 +26,20 @@ import static org.junit.jupiter.api.Assertions.assertEquals; public class ApacheClientIntegrationTest { - private static FhirContext ourCtx = FhirContext.forDstu2(); + private static final FhirContext ourCtx = FhirContext.forDstu2Cached(); private static String ourLastMethod; private static StringParam ourLastName; - private static int ourPort; - private static Server ourServer; - private static String ourBase; + + @RegisterExtension + public static final RestfulServerExtension ourServer = new RestfulServerExtension(ourCtx) + .setDefaultResponseEncoding(EncodingEnum.XML) + .registerProvider(new DummyPatientResourceProvider()) + .registerInterceptor(new VerboseLoggingInterceptor()) + .withPagingProvider(new FifoMemoryPagingProvider(100)) + .setDefaultPrettyPrint(false); + + @RegisterExtension + public static final HttpClientExtension ourClient = new HttpClientExtension(); @BeforeEach public void before() { @@ -43,11 +49,8 @@ public class ApacheClientIntegrationTest { @Test - public void testSearchWithParam() throws Exception { - - IGenericClient client = ourCtx.newRestfulGenericClient(ourBase); - - Bundle response = client.search().forResource(Patient.class).where(Patient.NAME.matches().value("FOO")).returnBundle(Bundle.class).execute(); + public void testSearchWithParam() { + Bundle response = ourServer.getFhirClient().search().forResource(Patient.class).where(Patient.NAME.matches().value("FOO")).returnBundle(Bundle.class).execute(); assertEquals("search", ourLastMethod); assertEquals("FOO", ourLastName.getValue()); assertEquals(1, response.getEntry().size()); @@ -56,29 +59,9 @@ public class ApacheClientIntegrationTest { @AfterAll public static void afterClassClearContext() throws Exception { - JettyUtil.closeServer(ourServer); TestUtil.randomizeLocaleAndTimezone(); } - @BeforeAll - public static void beforeClass() throws Exception { - ourServer = new Server(0); - - ServletHandler proxyHandler = new ServletHandler(); - RestfulServer servlet = new RestfulServer(ourCtx); - - servlet.registerInterceptor(new VerboseLoggingInterceptor()); - - servlet.setResourceProviders(new DummyPatientResourceProvider()); - ServletHolder servletHolder = new ServletHolder(servlet); - proxyHandler.addServletWithMapping(servletHolder, "/*"); - ourServer.setHandler(proxyHandler); - JettyUtil.startServer(ourServer); - ourPort = JettyUtil.getPortForStartedServer(ourServer); - - ourBase = "http://localhost:" + ourPort; - } - public static class DummyPatientResourceProvider implements IResourceProvider { //@formatter:off @@ -87,7 +70,7 @@ public class ApacheClientIntegrationTest { ourLastMethod = "search"; ourLastName = theName; - List retVal = new ArrayList(); + List retVal = new ArrayList<>(); Patient patient = new Patient(); patient.setId("123"); patient.addName().addGiven("GIVEN"); diff --git a/hapi-fhir-structures-dstu2/src/test/java/ca/uhn/fhir/rest/server/BinaryDstu2Test.java b/hapi-fhir-structures-dstu2/src/test/java/ca/uhn/fhir/rest/server/BinaryDstu2Test.java index 4724e1c3e55..f96792ca743 100644 --- a/hapi-fhir-structures-dstu2/src/test/java/ca/uhn/fhir/rest/server/BinaryDstu2Test.java +++ b/hapi-fhir-structures-dstu2/src/test/java/ca/uhn/fhir/rest/server/BinaryDstu2Test.java @@ -13,7 +13,9 @@ import ca.uhn.fhir.rest.annotation.Search; import ca.uhn.fhir.rest.api.Constants; import ca.uhn.fhir.rest.api.EncodingEnum; import ca.uhn.fhir.rest.api.MethodOutcome; +import ca.uhn.fhir.test.utilities.HttpClientExtension; import ca.uhn.fhir.test.utilities.JettyUtil; +import ca.uhn.fhir.test.utilities.server.RestfulServerExtension; import ca.uhn.fhir.util.TestUtil; import org.apache.commons.io.IOUtils; import org.apache.http.client.config.RequestConfig; @@ -27,12 +29,13 @@ import org.apache.http.impl.client.CloseableHttpClient; import org.apache.http.impl.client.HttpClientBuilder; import org.apache.http.impl.conn.PoolingHttpClientConnectionManager; import org.eclipse.jetty.server.Server; -import org.eclipse.jetty.servlet.ServletHandler; -import org.eclipse.jetty.servlet.ServletHolder; +import org.eclipse.jetty.ee10.servlet.ServletHandler; +import org.eclipse.jetty.ee10.servlet.ServletHolder; import org.junit.jupiter.api.AfterAll; import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.RegisterExtension; import java.io.IOException; import java.nio.charset.StandardCharsets; @@ -52,12 +55,18 @@ import static org.junit.jupiter.api.Assertions.assertNull; public class BinaryDstu2Test { private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(BinaryDstu2Test.class); - private static CloseableHttpClient ourClient; - private static FhirContext ourCtx = FhirContext.forDstu2(); + private static final FhirContext ourCtx = FhirContext.forDstu2Cached(); private static Binary ourLast; - private static int ourPort; - private static Server ourServer; + @RegisterExtension + public static final RestfulServerExtension ourServer = new RestfulServerExtension(ourCtx) + .setDefaultResponseEncoding(EncodingEnum.XML) + .registerProvider(new ResourceProvider()) + .withPagingProvider(new FifoMemoryPagingProvider(100)) + .setDefaultPrettyPrint(false); + + @RegisterExtension + public static final HttpClientExtension ourClient = new HttpClientExtension(); @BeforeEach public void before() { @@ -66,7 +75,7 @@ public class BinaryDstu2Test { @Test public void testReadWithExplicitTypeXml() throws Exception { - HttpGet httpGet = new HttpGet("http://localhost:" + ourPort + "/Binary/foo?_format=xml"); + HttpGet httpGet = new HttpGet(ourServer.getBaseUrl() + "/Binary/foo?_format=xml"); try (CloseableHttpResponse response = ourClient.execute(httpGet)) { String responseContent = IOUtils.toString(response.getEntity().getContent(), "UTF-8"); IOUtils.closeQuietly(response.getEntity().getContent()); @@ -84,7 +93,7 @@ public class BinaryDstu2Test { @Test public void testReadWithExplicitTypeJson() throws Exception { - HttpGet httpGet = new HttpGet("http://localhost:" + ourPort + "/Binary/foo?_format=json"); + HttpGet httpGet = new HttpGet(ourServer.getBaseUrl() + "/Binary/foo?_format=json"); try (CloseableHttpResponse response = ourClient.execute(httpGet)) { String responseContent = IOUtils.toString(response.getEntity().getContent(), "UTF-8"); IOUtils.closeQuietly(response.getEntity().getContent()); @@ -103,7 +112,7 @@ public class BinaryDstu2Test { // posts Binary directly @Test public void testPostBinary() throws Exception { - HttpPost http = new HttpPost("http://localhost:" + ourPort + "/Binary"); + HttpPost http = new HttpPost(ourServer.getBaseUrl() + "/Binary"); http.setEntity(new ByteArrayEntity(new byte[]{1, 2, 3, 4}, ContentType.create("foo/bar", "UTF-8"))); try (CloseableHttpResponse response = ourClient.execute(http)) { @@ -122,7 +131,7 @@ public class BinaryDstu2Test { res.setContentType("text/plain"); String stringContent = ourCtx.newJsonParser().encodeResourceToString(res); - HttpPost http = new HttpPost("http://localhost:" + ourPort + "/Binary"); + HttpPost http = new HttpPost(ourServer.getBaseUrl() + "/Binary"); http.setEntity(new StringEntity(stringContent, ContentType.create(Constants.CT_FHIR_JSON, "UTF-8"))); try (CloseableHttpResponse response = ourClient.execute(http)) { @@ -134,14 +143,14 @@ public class BinaryDstu2Test { @Test public void testBinaryReadAcceptMissing() throws Exception { - HttpGet http = new HttpGet("http://localhost:" + ourPort + "/Binary/foo"); + HttpGet http = new HttpGet(ourServer.getBaseUrl() + "/Binary/foo"); binaryRead(http); } @Test public void testBinaryReadAcceptBrowser() throws Exception { - HttpGet http = new HttpGet("http://localhost:" + ourPort + "/Binary/foo"); + HttpGet http = new HttpGet(ourServer.getBaseUrl() + "/Binary/foo"); http.addHeader("User-Agent", "Mozilla/5.0 (Windows NT 6.1; WOW64; rv:40.0) Gecko/20100101 Firefox/40.1"); http.addHeader("Accept", "text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8"); @@ -161,7 +170,7 @@ public class BinaryDstu2Test { @Test public void testBinaryReadAcceptFhirJson() throws Exception { - HttpGet http = new HttpGet("http://localhost:" + ourPort + "/Binary/foo"); + HttpGet http = new HttpGet(ourServer.getBaseUrl() + "/Binary/foo"); http.addHeader("User-Agent", "Mozilla/5.0 (Windows NT 6.1; WOW64; rv:40.0) Gecko/20100101 Firefox/40.1"); http.addHeader("Accept", Constants.CT_FHIR_JSON); @@ -177,7 +186,7 @@ public class BinaryDstu2Test { @Test public void testSearchJson() throws Exception { - HttpGet http = new HttpGet("http://localhost:" + ourPort + "/Binary?_pretty=true&_format=json"); + HttpGet http = new HttpGet(ourServer.getBaseUrl() + "/Binary?_pretty=true&_format=json"); try (CloseableHttpResponse response = ourClient.execute(http)) { String responseContent = IOUtils.toString(response.getEntity().getContent(), StandardCharsets.UTF_8); IOUtils.closeQuietly(response.getEntity().getContent()); @@ -196,7 +205,7 @@ public class BinaryDstu2Test { @Test public void testSearchXml() throws Exception { - HttpGet http = new HttpGet("http://localhost:" + ourPort + "/Binary?_pretty=true"); + HttpGet http = new HttpGet(ourServer.getBaseUrl() + "/Binary?_pretty=true"); try (CloseableHttpResponse response = ourClient.execute(http)) { String responseContent = IOUtils.toString(response.getEntity().getContent(), StandardCharsets.UTF_8); IOUtils.closeQuietly(response.getEntity().getContent()); @@ -247,38 +256,7 @@ public class BinaryDstu2Test { @AfterAll public static void afterClassClearContext() throws Exception { - JettyUtil.closeServer(ourServer); TestUtil.randomizeLocaleAndTimezone(); } - @BeforeAll - public static void beforeClass() throws Exception { - ourServer = new Server(0); - - ResourceProvider binaryProvider = new ResourceProvider(); - - ServletHandler proxyHandler = new ServletHandler(); - RestfulServer servlet = new RestfulServer(ourCtx); - servlet.setResourceProviders(binaryProvider); - servlet.setDefaultResponseEncoding(EncodingEnum.XML); - - ServletHolder servletHolder = new ServletHolder(servlet); - proxyHandler.addServletWithMapping(servletHolder, "/*"); - ourServer.setHandler(proxyHandler); - JettyUtil.startServer(ourServer); - ourPort = JettyUtil.getPortForStartedServer(ourServer); - - PoolingHttpClientConnectionManager connectionManager = new PoolingHttpClientConnectionManager(5000, TimeUnit.MILLISECONDS); - HttpClientBuilder builder = HttpClientBuilder.create(); - - int timeout = 60; - RequestConfig config = RequestConfig.custom() - .setConnectTimeout(timeout * 1000) - .setConnectionRequestTimeout(timeout * 1000) - .setSocketTimeout(timeout * 1000).build(); - - builder.setConnectionManager(connectionManager); - ourClient = builder.setDefaultRequestConfig(config).build(); - - } } diff --git a/hapi-fhir-structures-dstu2/src/test/java/ca/uhn/fhir/rest/server/BundleTypeInResponseTest.java b/hapi-fhir-structures-dstu2/src/test/java/ca/uhn/fhir/rest/server/BundleTypeInResponseTest.java index d0608fb2839..dd6bb801858 100644 --- a/hapi-fhir-structures-dstu2/src/test/java/ca/uhn/fhir/rest/server/BundleTypeInResponseTest.java +++ b/hapi-fhir-structures-dstu2/src/test/java/ca/uhn/fhir/rest/server/BundleTypeInResponseTest.java @@ -8,7 +8,9 @@ import ca.uhn.fhir.model.dstu2.valueset.BundleTypeEnum; import ca.uhn.fhir.narrative.DefaultThymeleafNarrativeGenerator; import ca.uhn.fhir.rest.annotation.Search; import ca.uhn.fhir.rest.api.EncodingEnum; +import ca.uhn.fhir.test.utilities.HttpClientExtension; import ca.uhn.fhir.test.utilities.JettyUtil; +import ca.uhn.fhir.test.utilities.server.RestfulServerExtension; import ca.uhn.fhir.util.TestUtil; import org.apache.commons.io.IOUtils; import org.apache.http.HttpResponse; @@ -17,11 +19,12 @@ import org.apache.http.impl.client.CloseableHttpClient; import org.apache.http.impl.client.HttpClientBuilder; import org.apache.http.impl.conn.PoolingHttpClientConnectionManager; import org.eclipse.jetty.server.Server; -import org.eclipse.jetty.servlet.ServletHandler; -import org.eclipse.jetty.servlet.ServletHolder; +import org.eclipse.jetty.ee10.servlet.ServletHandler; +import org.eclipse.jetty.ee10.servlet.ServletHolder; import org.junit.jupiter.api.AfterAll; import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.RegisterExtension; import java.util.ArrayList; import java.util.List; @@ -34,16 +37,22 @@ import static org.junit.jupiter.api.Assertions.assertEquals; */ public class BundleTypeInResponseTest { - private static CloseableHttpClient ourClient; - private static FhirContext ourCtx = FhirContext.forDstu2(); + private static final FhirContext ourCtx = FhirContext.forDstu2Cached(); private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(BundleTypeInResponseTest.class); - private static int ourPort; - private static Server ourServer; + @RegisterExtension + public static final RestfulServerExtension ourServer = new RestfulServerExtension(ourCtx) + .setDefaultResponseEncoding(EncodingEnum.XML) + .registerProvider(new DummyPatientResourceProvider()) + .withPagingProvider(new FifoMemoryPagingProvider(100)) + .setDefaultPrettyPrint(false); + + @RegisterExtension + public static final HttpClientExtension ourClient = new HttpClientExtension(); @Test public void testSearch() throws Exception { - HttpGet httpGet = new HttpGet("http://localhost:" + ourPort + "/Patient"); + HttpGet httpGet = new HttpGet(ourServer.getBaseUrl() + "/Patient"); HttpResponse status = ourClient.execute(httpGet); String responseContent = IOUtils.toString(status.getEntity().getContent()); IOUtils.closeQuietly(status.getEntity().getContent()); @@ -60,35 +69,9 @@ public class BundleTypeInResponseTest { @AfterAll public static void afterClassClearContext() throws Exception { - JettyUtil.closeServer(ourServer); TestUtil.randomizeLocaleAndTimezone(); } - @BeforeAll - public static void beforeClass() throws Exception { - ourServer = new Server(0); - - DummyPatientResourceProvider patientProvider = new DummyPatientResourceProvider(); - - ServletHandler proxyHandler = new ServletHandler(); - RestfulServer servlet = new RestfulServer(ourCtx); - servlet.getFhirContext().setNarrativeGenerator(new DefaultThymeleafNarrativeGenerator()); - servlet.setResourceProviders(patientProvider); - servlet.setDefaultResponseEncoding(EncodingEnum.XML); - - ServletHolder servletHolder = new ServletHolder(servlet); - proxyHandler.addServletWithMapping(servletHolder, "/*"); - ourServer.setHandler(proxyHandler); - JettyUtil.startServer(ourServer); - ourPort = JettyUtil.getPortForStartedServer(ourServer); - - PoolingHttpClientConnectionManager connectionManager = new PoolingHttpClientConnectionManager(5000, TimeUnit.MILLISECONDS); - HttpClientBuilder builder = HttpClientBuilder.create(); - builder.setConnectionManager(connectionManager); - ourClient = builder.build(); - - } - public static class DummyPatientResourceProvider implements IResourceProvider { @Search diff --git a/hapi-fhir-structures-dstu2/src/test/java/ca/uhn/fhir/rest/server/CompartmentDstu2Test.java b/hapi-fhir-structures-dstu2/src/test/java/ca/uhn/fhir/rest/server/CompartmentDstu2Test.java index 96d6cc85f09..1e07325b03c 100644 --- a/hapi-fhir-structures-dstu2/src/test/java/ca/uhn/fhir/rest/server/CompartmentDstu2Test.java +++ b/hapi-fhir-structures-dstu2/src/test/java/ca/uhn/fhir/rest/server/CompartmentDstu2Test.java @@ -10,7 +10,9 @@ import ca.uhn.fhir.rest.annotation.IdParam; import ca.uhn.fhir.rest.annotation.Read; import ca.uhn.fhir.rest.annotation.Search; import ca.uhn.fhir.rest.api.EncodingEnum; +import ca.uhn.fhir.test.utilities.HttpClientExtension; import ca.uhn.fhir.test.utilities.JettyUtil; +import ca.uhn.fhir.test.utilities.server.RestfulServerExtension; import ca.uhn.fhir.util.TestUtil; import org.apache.commons.io.IOUtils; import org.apache.http.HttpResponse; @@ -19,12 +21,13 @@ import org.apache.http.impl.client.CloseableHttpClient; import org.apache.http.impl.client.HttpClientBuilder; import org.apache.http.impl.conn.PoolingHttpClientConnectionManager; import org.eclipse.jetty.server.Server; -import org.eclipse.jetty.servlet.ServletHandler; -import org.eclipse.jetty.servlet.ServletHolder; +import org.eclipse.jetty.ee10.servlet.ServletHandler; +import org.eclipse.jetty.ee10.servlet.ServletHolder; import org.junit.jupiter.api.AfterAll; import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.RegisterExtension; import java.util.ArrayList; import java.util.List; @@ -39,15 +42,21 @@ import static org.junit.jupiter.api.Assertions.assertEquals; * Created by dsotnikov on 2/25/2014. */ public class CompartmentDstu2Test { - private static CloseableHttpClient ourClient; - private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(CompartmentDstu2Test.class); - private static int ourPort; - private static Server ourServer; private static String ourLastMethod; - private static FhirContext ourCtx = FhirContext.forDstu2(); + private static final FhirContext ourCtx = FhirContext.forDstu2Cached(); private static IdDt ourLastId; + @RegisterExtension + public static final RestfulServerExtension ourServer = new RestfulServerExtension(ourCtx) + .setDefaultResponseEncoding(EncodingEnum.XML) + .registerProvider(new TempPatientResourceProvider()) + .withPagingProvider(new FifoMemoryPagingProvider(100)) + .setDefaultPrettyPrint(false); + + @RegisterExtension + public static final HttpClientExtension ourClient = new HttpClientExtension(); + @AfterAll public static void afterClassClearContext() { TestUtil.randomizeLocaleAndTimezone(); @@ -63,9 +72,7 @@ public class CompartmentDstu2Test { @Test public void testReadFirst() throws Exception { - init(new TempPatientResourceProvider()); - - HttpGet httpGet = new HttpGet("http://localhost:" + ourPort + "/Patient/123"); + HttpGet httpGet = new HttpGet(ourServer.getBaseUrl() + "/Patient/123"); HttpResponse status = ourClient.execute(httpGet); String responseContent = IOUtils.toString(status.getEntity().getContent()); IOUtils.closeQuietly(status.getEntity().getContent()); @@ -78,9 +85,7 @@ public class CompartmentDstu2Test { @Test public void testCompartmentSecond() throws Exception { - init(new TempPatientResourceProvider()); - - HttpGet httpGet = new HttpGet("http://localhost:" + ourPort + "/Patient/123/Encounter"); + HttpGet httpGet = new HttpGet(ourServer.getBaseUrl() + "/Patient/123/Encounter"); HttpResponse status = ourClient.execute(httpGet); String responseContent = IOUtils.toString(status.getEntity().getContent()); IOUtils.closeQuietly(status.getEntity().getContent()); @@ -94,9 +99,7 @@ public class CompartmentDstu2Test { @Test public void testCompartmentSecond2() throws Exception { - init(new TempPatientResourceProvider()); - - HttpGet httpGet = new HttpGet("http://localhost:" + ourPort + "/Patient/123/Observation"); + HttpGet httpGet = new HttpGet(ourServer.getBaseUrl() + "/Patient/123/Observation"); HttpResponse status = ourClient.execute(httpGet); String responseContent = IOUtils.toString(status.getEntity().getContent()); IOUtils.closeQuietly(status.getEntity().getContent()); @@ -108,33 +111,6 @@ public class CompartmentDstu2Test { assertThat(responseContent, containsString(" getResourceType() { @@ -178,4 +154,5 @@ public class CompartmentDstu2Test { } + } diff --git a/hapi-fhir-structures-dstu2/src/test/java/ca/uhn/fhir/rest/server/CreateConditionalTest.java b/hapi-fhir-structures-dstu2/src/test/java/ca/uhn/fhir/rest/server/CreateConditionalTest.java index 60e487dbf19..96be8d7097c 100644 --- a/hapi-fhir-structures-dstu2/src/test/java/ca/uhn/fhir/rest/server/CreateConditionalTest.java +++ b/hapi-fhir-structures-dstu2/src/test/java/ca/uhn/fhir/rest/server/CreateConditionalTest.java @@ -12,8 +12,11 @@ import ca.uhn.fhir.rest.annotation.OptionalParam; import ca.uhn.fhir.rest.annotation.ResourceParam; import ca.uhn.fhir.rest.annotation.Search; import ca.uhn.fhir.rest.api.Constants; +import ca.uhn.fhir.rest.api.EncodingEnum; import ca.uhn.fhir.rest.api.MethodOutcome; +import ca.uhn.fhir.test.utilities.HttpClientExtension; import ca.uhn.fhir.test.utilities.JettyUtil; +import ca.uhn.fhir.test.utilities.server.RestfulServerExtension; import ca.uhn.fhir.util.TestUtil; import org.apache.commons.io.IOUtils; import org.apache.http.HttpResponse; @@ -25,12 +28,13 @@ import org.apache.http.impl.client.CloseableHttpClient; import org.apache.http.impl.client.HttpClientBuilder; import org.apache.http.impl.conn.PoolingHttpClientConnectionManager; import org.eclipse.jetty.server.Server; -import org.eclipse.jetty.servlet.ServletHandler; -import org.eclipse.jetty.servlet.ServletHolder; +import org.eclipse.jetty.ee10.servlet.ServletHandler; +import org.eclipse.jetty.ee10.servlet.ServletHolder; import org.junit.jupiter.api.AfterAll; import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.RegisterExtension; import java.util.ArrayList; import java.util.List; @@ -40,21 +44,24 @@ import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertNull; import static org.junit.jupiter.api.Assertions.assertTrue; -/** - * Created by dsotnikov on 2/25/2014. - */ public class CreateConditionalTest { - private static CloseableHttpClient ourClient; - - private static FhirContext ourCtx = FhirContext.forDstu2(); + + private static final FhirContext ourCtx = FhirContext.forDstu2Cached(); private static String ourLastConditionalUrl; private static IdDt ourLastId; private static IdDt ourLastIdParam; private static boolean ourLastRequestWasSearch; private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(CreateConditionalTest.class); - private static int ourPort; - private static Server ourServer; - + + @RegisterExtension + public static final RestfulServerExtension ourServer = new RestfulServerExtension(ourCtx) + .setDefaultResponseEncoding(EncodingEnum.XML) + .registerProvider(new PatientProvider()) + .withPagingProvider(new FifoMemoryPagingProvider(100)) + .setDefaultPrettyPrint(false); + + @RegisterExtension + public static final HttpClientExtension ourClient = new HttpClientExtension(); @BeforeEach public void before() { @@ -72,7 +79,7 @@ public class CreateConditionalTest { Patient patient = new Patient(); patient.addIdentifier().setValue("002"); - HttpPost httpPost = new HttpPost("http://localhost:" + ourPort + "/Patient"); + HttpPost httpPost = new HttpPost(ourServer.getBaseUrl() + "/Patient"); httpPost.addHeader(Constants.HEADER_IF_NONE_EXIST, "Patient?identifier=system%7C001"); httpPost.setEntity(new StringEntity(ourCtx.newXmlParser().encodeResourceToString(patient), ContentType.create(Constants.CT_FHIR_XML, "UTF-8"))); @@ -84,8 +91,8 @@ public class CreateConditionalTest { ourLog.info("Response was:\n{}", responseContent); assertEquals(201, status.getStatusLine().getStatusCode()); - assertEquals("http://localhost:" + ourPort + "/Patient/001/_history/002", status.getFirstHeader("location").getValue()); - assertEquals("http://localhost:" + ourPort + "/Patient/001/_history/002", status.getFirstHeader("content-location").getValue()); + assertEquals(ourServer.getBaseUrl() + "/Patient/001/_history/002", status.getFirstHeader("location").getValue()); + assertEquals(ourServer.getBaseUrl() + "/Patient/001/_history/002", status.getFirstHeader("content-location").getValue()); assertNull(ourLastId.getValue()); assertNull(ourLastIdParam); @@ -99,7 +106,7 @@ public class CreateConditionalTest { Patient patient = new Patient(); patient.addIdentifier().setValue("002"); - HttpPost httpPost = new HttpPost("http://localhost:" + ourPort + "/Patient?_format=true&_pretty=true"); + HttpPost httpPost = new HttpPost(ourServer.getBaseUrl() + "/Patient?_format=true&_pretty=true"); httpPost.setEntity(new StringEntity(ourCtx.newXmlParser().encodeResourceToString(patient), ContentType.create(Constants.CT_FHIR_XML, "UTF-8"))); HttpResponse status = ourClient.execute(httpPost); @@ -110,8 +117,8 @@ public class CreateConditionalTest { ourLog.info("Response was:\n{}", responseContent); assertEquals(201, status.getStatusLine().getStatusCode()); - assertEquals("http://localhost:" + ourPort + "/Patient/001/_history/002", status.getFirstHeader("location").getValue()); - assertEquals("http://localhost:" + ourPort + "/Patient/001/_history/002", status.getFirstHeader("content-location").getValue()); + assertEquals(ourServer.getBaseUrl() + "/Patient/001/_history/002", status.getFirstHeader("location").getValue()); + assertEquals(ourServer.getBaseUrl() + "/Patient/001/_history/002", status.getFirstHeader("content-location").getValue()); assertEquals(null, ourLastId.toUnqualified().getValue()); assertEquals(null, ourLastIdParam); @@ -125,7 +132,7 @@ public class CreateConditionalTest { Patient patient = new Patient(); patient.addIdentifier().setValue("002"); - HttpGet httpGet = new HttpGet("http://localhost:" + ourPort + "/Patient?_pretty=true"); + HttpGet httpGet = new HttpGet(ourServer.getBaseUrl() + "/Patient?_pretty=true"); HttpResponse status = ourClient.execute(httpGet); @@ -144,33 +151,10 @@ public class CreateConditionalTest { @AfterAll public static void afterClassClearContext() throws Exception { - JettyUtil.closeServer(ourServer); TestUtil.randomizeLocaleAndTimezone(); } - @BeforeAll - public static void beforeClass() throws Exception { - ourServer = new Server(0); - - PatientProvider patientProvider = new PatientProvider(); - - ServletHandler proxyHandler = new ServletHandler(); - RestfulServer servlet = new RestfulServer(ourCtx); - servlet.setResourceProviders(patientProvider); - ServletHolder servletHolder = new ServletHolder(servlet); - proxyHandler.addServletWithMapping(servletHolder, "/*"); - ourServer.setHandler(proxyHandler); - JettyUtil.startServer(ourServer); - ourPort = JettyUtil.getPortForStartedServer(ourServer); - - PoolingHttpClientConnectionManager connectionManager = new PoolingHttpClientConnectionManager(5000, TimeUnit.MILLISECONDS); - HttpClientBuilder builder = HttpClientBuilder.create(); - builder.setConnectionManager(connectionManager); - ourClient = builder.build(); - - } - public static class PatientProvider implements IResourceProvider { @Create() diff --git a/hapi-fhir-structures-dstu2/src/test/java/ca/uhn/fhir/rest/server/DeleteDstu2Test.java b/hapi-fhir-structures-dstu2/src/test/java/ca/uhn/fhir/rest/server/DeleteDstu2Test.java index 13c9a918b6f..20104a2fa79 100644 --- a/hapi-fhir-structures-dstu2/src/test/java/ca/uhn/fhir/rest/server/DeleteDstu2Test.java +++ b/hapi-fhir-structures-dstu2/src/test/java/ca/uhn/fhir/rest/server/DeleteDstu2Test.java @@ -8,8 +8,11 @@ import ca.uhn.fhir.rest.annotation.ConditionalUrlParam; import ca.uhn.fhir.rest.annotation.Delete; import ca.uhn.fhir.rest.annotation.IdParam; import ca.uhn.fhir.rest.api.Constants; +import ca.uhn.fhir.rest.api.EncodingEnum; import ca.uhn.fhir.rest.api.MethodOutcome; +import ca.uhn.fhir.test.utilities.HttpClientExtension; import ca.uhn.fhir.test.utilities.JettyUtil; +import ca.uhn.fhir.test.utilities.server.RestfulServerExtension; import ca.uhn.fhir.util.TestUtil; import org.apache.http.HttpResponse; import org.apache.http.client.methods.HttpDelete; @@ -17,12 +20,13 @@ import org.apache.http.impl.client.CloseableHttpClient; import org.apache.http.impl.client.HttpClientBuilder; import org.apache.http.impl.conn.PoolingHttpClientConnectionManager; import org.eclipse.jetty.server.Server; -import org.eclipse.jetty.servlet.ServletHandler; -import org.eclipse.jetty.servlet.ServletHolder; +import org.eclipse.jetty.ee10.servlet.ServletHandler; +import org.eclipse.jetty.ee10.servlet.ServletHolder; import org.junit.jupiter.api.AfterAll; import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.RegisterExtension; import java.util.concurrent.TimeUnit; @@ -30,14 +34,21 @@ import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertNull; public class DeleteDstu2Test { - private static CloseableHttpClient ourClient; - private static FhirContext ourCtx = FhirContext.forDstu2(); + private static final FhirContext ourCtx = FhirContext.forDstu2Cached(); private static boolean ourInvoked; private static String ourLastConditionalUrl; private static IdDt ourLastIdParam; - private static int ourPort; - private static Server ourServer; - + + @RegisterExtension + public static final RestfulServerExtension ourServer = new RestfulServerExtension(ourCtx) + .setDefaultResponseEncoding(EncodingEnum.XML) + .registerProvider(new PatientProvider()) + .withPagingProvider(new FifoMemoryPagingProvider(100)) + .setDefaultPrettyPrint(false); + + @RegisterExtension + public static final HttpClientExtension ourClient = new HttpClientExtension(); + @BeforeEach public void before() { @@ -53,7 +64,7 @@ public class DeleteDstu2Test { Patient patient = new Patient(); patient.addIdentifier().setValue("002"); - HttpDelete httpPost = new HttpDelete("http://localhost:" + ourPort + "/Patient?identifier=system%7C001"); + HttpDelete httpPost = new HttpDelete(ourServer.getBaseUrl() + "/Patient?identifier=system%7C001"); HttpResponse status = ourClient.execute(httpPost); @@ -68,7 +79,7 @@ public class DeleteDstu2Test { Patient patient = new Patient(); patient.addIdentifier().setValue("002"); - HttpDelete httpPost = new HttpDelete("http://localhost:" + ourPort + "/Patient/2"); + HttpDelete httpPost = new HttpDelete(ourServer.getBaseUrl() + "/Patient/2"); HttpResponse status = ourClient.execute(httpPost); @@ -82,33 +93,10 @@ public class DeleteDstu2Test { @AfterAll public static void afterClassClearContext() throws Exception { - JettyUtil.closeServer(ourServer); TestUtil.randomizeLocaleAndTimezone(); } - @BeforeAll - public static void beforeClass() throws Exception { - ourServer = new Server(0); - - PatientProvider patientProvider = new PatientProvider(); - - ServletHandler proxyHandler = new ServletHandler(); - RestfulServer servlet = new RestfulServer(ourCtx); - servlet.setResourceProviders(patientProvider); - ServletHolder servletHolder = new ServletHolder(servlet); - proxyHandler.addServletWithMapping(servletHolder, "/*"); - ourServer.setHandler(proxyHandler); - JettyUtil.startServer(ourServer); - ourPort = JettyUtil.getPortForStartedServer(ourServer); - - PoolingHttpClientConnectionManager connectionManager = new PoolingHttpClientConnectionManager(5000, TimeUnit.MILLISECONDS); - HttpClientBuilder builder = HttpClientBuilder.create(); - builder.setConnectionManager(connectionManager); - ourClient = builder.build(); - - } - public static class PatientProvider implements IResourceProvider { @Delete() diff --git a/hapi-fhir-structures-dstu2/src/test/java/ca/uhn/fhir/rest/server/ETagServerTest.java b/hapi-fhir-structures-dstu2/src/test/java/ca/uhn/fhir/rest/server/ETagServerTest.java index 73dccb78629..7e6188e0eec 100644 --- a/hapi-fhir-structures-dstu2/src/test/java/ca/uhn/fhir/rest/server/ETagServerTest.java +++ b/hapi-fhir-structures-dstu2/src/test/java/ca/uhn/fhir/rest/server/ETagServerTest.java @@ -15,7 +15,8 @@ import ca.uhn.fhir.rest.api.Constants; import ca.uhn.fhir.rest.api.EncodingEnum; import ca.uhn.fhir.rest.api.MethodOutcome; import ca.uhn.fhir.rest.server.exceptions.PreconditionFailedException; -import ca.uhn.fhir.test.utilities.JettyUtil; +import ca.uhn.fhir.test.utilities.HttpClientExtension; +import ca.uhn.fhir.test.utilities.server.RestfulServerExtension; import ca.uhn.fhir.util.TestUtil; import org.apache.commons.io.IOUtils; import org.apache.http.Header; @@ -25,35 +26,33 @@ import org.apache.http.client.methods.HttpGet; import org.apache.http.client.methods.HttpPut; import org.apache.http.entity.ContentType; import org.apache.http.entity.StringEntity; -import org.apache.http.impl.client.CloseableHttpClient; -import org.apache.http.impl.client.HttpClientBuilder; -import org.apache.http.impl.conn.PoolingHttpClientConnectionManager; -import org.eclipse.jetty.server.Server; -import org.eclipse.jetty.servlet.ServletHandler; -import org.eclipse.jetty.servlet.ServletHolder; import org.junit.jupiter.api.AfterAll; -import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.RegisterExtension; import java.io.IOException; import java.util.Date; -import java.util.concurrent.TimeUnit; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertNotNull; public class ETagServerTest { - private static CloseableHttpClient ourClient; - private static PoolingHttpClientConnectionManager ourConnectionManager; - private static FhirContext ourCtx; + private static final FhirContext ourCtx = FhirContext.forDstu2Cached(); private static IdDt ourLastId; private static Date ourLastModifiedDate; - private static int ourPort; - private static Server ourServer; + @RegisterExtension + public static final RestfulServerExtension ourServer = new RestfulServerExtension(ourCtx) + .setDefaultResponseEncoding(EncodingEnum.XML) + .registerProvider(new PatientProvider()) + .withPagingProvider(new FifoMemoryPagingProvider(100)) + .setDefaultPrettyPrint(false); + + @RegisterExtension + public static final HttpClientExtension ourClient = new HttpClientExtension(); @BeforeEach @@ -65,7 +64,7 @@ public class ETagServerTest { public void testAutomaticNotModified() throws Exception { ourLastModifiedDate = new InstantDt("2012-11-25T02:34:45.2222Z").getValue(); - HttpGet httpGet = new HttpGet("http://localhost:" + ourPort + "/Patient/2"); + HttpGet httpGet = new HttpGet(ourServer.getBaseUrl() + "/Patient/2"); httpGet.addHeader(Constants.HEADER_IF_NONE_MATCH, "\"222\""); HttpResponse status = ourClient.execute(httpGet); try { @@ -82,7 +81,7 @@ public class ETagServerTest { public void testETagHeader() throws Exception { ourLastModifiedDate = new InstantDt("2012-11-25T02:34:45.2222Z").getValue(); - HttpGet httpGet = new HttpGet("http://localhost:" + ourPort + "/Patient/2/_history/3"); + HttpGet httpGet = new HttpGet(ourServer.getBaseUrl() + "/Patient/2/_history/3"); HttpResponse status = ourClient.execute(httpGet); try { String responseContent = IOUtils.toString(status.getEntity().getContent()); @@ -104,7 +103,7 @@ public class ETagServerTest { public void testLastModifiedHeader() throws Exception { ourLastModifiedDate = new InstantDt("2012-11-25T02:34:45.222Z").getValue(); - HttpGet httpGet = new HttpGet("http://localhost:" + ourPort + "/Patient/2/_history/3"); + HttpGet httpGet = new HttpGet(ourServer.getBaseUrl() + "/Patient/2/_history/3"); HttpResponse status = ourClient.execute(httpGet); try { String responseContent = IOUtils.toString(status.getEntity().getContent()); @@ -132,7 +131,7 @@ public class ETagServerTest { String resBody = ourCtx.newXmlParser().encodeResourceToString(p); HttpPut http; - http = new HttpPut("http://localhost:" + ourPort + "/Patient/2"); + http = new HttpPut(ourServer.getBaseUrl() + "/Patient/2"); http.setEntity(new StringEntity(resBody, ContentType.create(Constants.CT_FHIR_XML, "UTF-8"))); http.addHeader(Constants.HEADER_IF_MATCH, "\"221\""); CloseableHttpResponse status = ourClient.execute(http); @@ -154,7 +153,7 @@ public class ETagServerTest { String resBody = ourCtx.newXmlParser().encodeResourceToString(p); HttpPut http; - http = new HttpPut("http://localhost:" + ourPort + "/Patient/2"); + http = new HttpPut(ourServer.getBaseUrl() + "/Patient/2"); http.setEntity(new StringEntity(resBody, ContentType.create(Constants.CT_FHIR_XML, "UTF-8"))); http.addHeader(Constants.HEADER_IF_MATCH, "\"222\""); CloseableHttpResponse status = ourClient.execute(http); @@ -176,7 +175,7 @@ public class ETagServerTest { String resBody = ourCtx.newXmlParser().encodeResourceToString(p); HttpPut http; - http = new HttpPut("http://localhost:" + ourPort + "/Patient/2"); + http = new HttpPut(ourServer.getBaseUrl() + "/Patient/2"); http.setEntity(new StringEntity(resBody, ContentType.create(Constants.CT_FHIR_XML, "UTF-8"))); HttpResponse status = ourClient.execute(http); try { @@ -191,34 +190,9 @@ public class ETagServerTest { @AfterAll public static void afterClassClearContext() throws Exception { - JettyUtil.closeServer(ourServer); TestUtil.randomizeLocaleAndTimezone(); } - @BeforeAll - public static void beforeClass() throws Exception { - ourServer = new Server(0); - - PatientProvider patientProvider = new PatientProvider(); - - ServletHandler proxyHandler = new ServletHandler(); - RestfulServer servlet = new RestfulServer(FhirContext.forDstu2()); - ourCtx = servlet.getFhirContext(); - servlet.setResourceProviders(patientProvider); - servlet.setDefaultResponseEncoding(EncodingEnum.XML); - ServletHolder servletHolder = new ServletHolder(servlet); - proxyHandler.addServletWithMapping(servletHolder, "/*"); - ourServer.setHandler(proxyHandler); - JettyUtil.startServer(ourServer); - ourPort = JettyUtil.getPortForStartedServer(ourServer); - - ourConnectionManager = new PoolingHttpClientConnectionManager(50000, TimeUnit.MILLISECONDS); - HttpClientBuilder builder = HttpClientBuilder.create(); - builder.setConnectionManager(ourConnectionManager); - ourClient = builder.build(); - - } - public static class PatientProvider implements IResourceProvider { @Read(version = true) diff --git a/hapi-fhir-structures-dstu2/src/test/java/ca/uhn/fhir/rest/server/IncludeAndRevincludeParameterTest.java b/hapi-fhir-structures-dstu2/src/test/java/ca/uhn/fhir/rest/server/IncludeAndRevincludeParameterTest.java index 9627fcc509f..cd3aa5a55ad 100644 --- a/hapi-fhir-structures-dstu2/src/test/java/ca/uhn/fhir/rest/server/IncludeAndRevincludeParameterTest.java +++ b/hapi-fhir-structures-dstu2/src/test/java/ca/uhn/fhir/rest/server/IncludeAndRevincludeParameterTest.java @@ -7,7 +7,10 @@ import ca.uhn.fhir.model.api.Include; import ca.uhn.fhir.model.dstu2.resource.Patient; import ca.uhn.fhir.rest.annotation.IncludeParam; import ca.uhn.fhir.rest.annotation.Search; +import ca.uhn.fhir.rest.api.EncodingEnum; +import ca.uhn.fhir.test.utilities.HttpClientExtension; import ca.uhn.fhir.test.utilities.JettyUtil; +import ca.uhn.fhir.test.utilities.server.RestfulServerExtension; import ca.uhn.fhir.util.TestUtil; import org.apache.commons.io.IOUtils; import org.apache.http.HttpResponse; @@ -16,12 +19,13 @@ import org.apache.http.impl.client.CloseableHttpClient; import org.apache.http.impl.client.HttpClientBuilder; import org.apache.http.impl.conn.PoolingHttpClientConnectionManager; import org.eclipse.jetty.server.Server; -import org.eclipse.jetty.servlet.ServletHandler; -import org.eclipse.jetty.servlet.ServletHolder; +import org.eclipse.jetty.ee10.servlet.ServletHandler; +import org.eclipse.jetty.ee10.servlet.ServletHolder; import org.junit.jupiter.api.AfterAll; import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.RegisterExtension; import java.util.ArrayList; import java.util.List; @@ -38,13 +42,20 @@ import static org.junit.jupiter.api.Assertions.assertEquals; */ public class IncludeAndRevincludeParameterTest { - private static CloseableHttpClient ourClient; - private static FhirContext ourCtx; + private static final FhirContext ourCtx = FhirContext.forDstu2Cached(); private static Set ourIncludes; private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(IncludeAndRevincludeParameterTest.class); - private static int ourPort; private static Set ourReverseIncludes; - private static Server ourServer; + + @RegisterExtension + public static final RestfulServerExtension ourServer = new RestfulServerExtension(ourCtx) + .setDefaultResponseEncoding(EncodingEnum.XML) + .registerProvider(new DummyPatientResourceProvider()) + .withPagingProvider(new FifoMemoryPagingProvider(100)) + .setDefaultPrettyPrint(false); + + @RegisterExtension + public static final HttpClientExtension ourClient = new HttpClientExtension(); @BeforeEach public void before() { @@ -55,7 +66,7 @@ public class IncludeAndRevincludeParameterTest { @Test public void testNoIncludes() throws Exception { - HttpGet httpGet = new HttpGet("http://localhost:" + ourPort + "/Patient?_query=normalInclude"); + HttpGet httpGet = new HttpGet(ourServer.getBaseUrl() + "/Patient?_query=normalInclude"); HttpResponse status = ourClient.execute(httpGet); IOUtils.closeQuietly(status.getEntity().getContent()); @@ -67,7 +78,7 @@ public class IncludeAndRevincludeParameterTest { @Test public void testWithBoth() throws Exception { - HttpGet httpGet = new HttpGet("http://localhost:" + ourPort + "/Patient?_query=normalInclude&_include=A.a&_include=B.b&_revinclude=C.c&_revinclude=D.d"); + HttpGet httpGet = new HttpGet(ourServer.getBaseUrl() + "/Patient?_query=normalInclude&_include=A.a&_include=B.b&_revinclude=C.c&_revinclude=D.d"); HttpResponse status = ourClient.execute(httpGet); IOUtils.closeQuietly(status.getEntity().getContent()); @@ -82,33 +93,9 @@ public class IncludeAndRevincludeParameterTest { @AfterAll public static void afterClassClearContext() throws Exception { - JettyUtil.closeServer(ourServer); TestUtil.randomizeLocaleAndTimezone(); } - - @BeforeAll - public static void beforeClass() throws Exception { - - ourCtx = FhirContext.forDstu2(); - ourServer = new Server(ourPort); - - ServletHandler proxyHandler = new ServletHandler(); - RestfulServer servlet = new RestfulServer(ourCtx); - servlet.setResourceProviders(new DummyPatientResourceProvider()); - servlet.setBundleInclusionRule(BundleInclusionRule.BASED_ON_RESOURCE_PRESENCE); - ServletHolder servletHolder = new ServletHolder(servlet); - proxyHandler.addServletWithMapping(servletHolder, "/*"); - ourServer.setHandler(proxyHandler); - JettyUtil.startServer(ourServer); - ourPort = JettyUtil.getPortForStartedServer(ourServer); - - PoolingHttpClientConnectionManager connectionManager = new PoolingHttpClientConnectionManager(5000, TimeUnit.MILLISECONDS); - HttpClientBuilder builder = HttpClientBuilder.create(); - builder.setConnectionManager(connectionManager); - ourClient = builder.build(); - - } - + public static class DummyPatientResourceProvider implements IResourceProvider { @Override diff --git a/hapi-fhir-structures-dstu2/src/test/java/ca/uhn/fhir/rest/server/IncludeDstu2Test.java b/hapi-fhir-structures-dstu2/src/test/java/ca/uhn/fhir/rest/server/IncludeDstu2Test.java index 03f226af1f9..cf19e290f8c 100644 --- a/hapi-fhir-structures-dstu2/src/test/java/ca/uhn/fhir/rest/server/IncludeDstu2Test.java +++ b/hapi-fhir-structures-dstu2/src/test/java/ca/uhn/fhir/rest/server/IncludeDstu2Test.java @@ -21,7 +21,9 @@ import ca.uhn.fhir.rest.annotation.IncludeParam; import ca.uhn.fhir.rest.annotation.RequiredParam; import ca.uhn.fhir.rest.annotation.Search; import ca.uhn.fhir.rest.api.EncodingEnum; +import ca.uhn.fhir.test.utilities.HttpClientExtension; import ca.uhn.fhir.test.utilities.JettyUtil; +import ca.uhn.fhir.test.utilities.server.RestfulServerExtension; import ca.uhn.fhir.util.BundleUtil; import ca.uhn.fhir.util.ElementUtil; import ca.uhn.fhir.util.TestUtil; @@ -32,12 +34,13 @@ import org.apache.http.impl.client.CloseableHttpClient; import org.apache.http.impl.client.HttpClientBuilder; import org.apache.http.impl.conn.PoolingHttpClientConnectionManager; import org.eclipse.jetty.server.Server; -import org.eclipse.jetty.servlet.ServletHandler; -import org.eclipse.jetty.servlet.ServletHolder; +import org.eclipse.jetty.ee10.servlet.ServletHandler; +import org.eclipse.jetty.ee10.servlet.ServletHolder; import org.junit.jupiter.api.AfterAll; import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.Disabled; import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.RegisterExtension; import java.util.ArrayList; import java.util.Arrays; @@ -54,14 +57,23 @@ import static org.junit.jupiter.api.Assertions.assertEquals; public class IncludeDstu2Test { private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(IncludeDstu2Test.class); - private static CloseableHttpClient ourClient; - private static FhirContext ourCtx; - private static int ourPort; - private static Server ourServer; + private static final FhirContext ourCtx = FhirContext.forDstu2Cached(); + + @RegisterExtension + public static final RestfulServerExtension ourServer = new RestfulServerExtension(ourCtx) + .setDefaultResponseEncoding(EncodingEnum.XML) + .registerProvider(new DummyPatientResourceProvider()) + .registerProvider(new DummyDiagnosticReportResourceProvider()) + .withPagingProvider(new FifoMemoryPagingProvider(100)) + .withServer(s->s.setBundleInclusionRule(BundleInclusionRule.BASED_ON_RESOURCE_PRESENCE)) + .setDefaultPrettyPrint(false); + + @RegisterExtension + public static final HttpClientExtension ourClient = new HttpClientExtension(); @Test public void testBadInclude() throws Exception { - HttpGet httpGet = new HttpGet("http://localhost:" + ourPort + "/Patient?name=Hello&_include=foo&_include=baz"); + HttpGet httpGet = new HttpGet(ourServer.getBaseUrl() + "/Patient?name=Hello&_include=foo&_include=baz"); HttpResponse status = ourClient.execute(httpGet); assertEquals(400, status.getStatusLine().getStatusCode()); } @@ -69,7 +81,7 @@ public class IncludeDstu2Test { @Test public void testIIncludedResourcesNonContained() throws Exception { - HttpGet httpGet = new HttpGet("http://localhost:" + ourPort + "/Patient?_query=normalInclude&_pretty=true"); + HttpGet httpGet = new HttpGet(ourServer.getBaseUrl() + "/Patient?_query=normalInclude&_pretty=true"); HttpResponse status = ourClient.execute(httpGet); String responseContent = IOUtils.toString(status.getEntity().getContent()); IOUtils.closeQuietly(status.getEntity().getContent()); @@ -96,7 +108,7 @@ public class IncludeDstu2Test { @Test public void testIIncludedResourcesNonContainedInDeclaredExtension() throws Exception { - HttpGet httpGet = new HttpGet("http://localhost:" + ourPort + "/Patient?_query=declaredExtInclude&_pretty=true"); + HttpGet httpGet = new HttpGet(ourServer.getBaseUrl() + "/Patient?_query=declaredExtInclude&_pretty=true"); HttpResponse status = ourClient.execute(httpGet); String responseContent = IOUtils.toString(status.getEntity().getContent()); IOUtils.closeQuietly(status.getEntity().getContent()); @@ -124,7 +136,7 @@ public class IncludeDstu2Test { @Test public void testIIncludedResourcesNonContainedInExtension() throws Exception { - HttpGet httpGet = new HttpGet("http://localhost:" + ourPort + "/Patient?_query=extInclude&_pretty=true"); + HttpGet httpGet = new HttpGet(ourServer.getBaseUrl() + "/Patient?_query=extInclude&_pretty=true"); HttpResponse status = ourClient.execute(httpGet); String responseContent = IOUtils.toString(status.getEntity().getContent()); IOUtils.closeQuietly(status.getEntity().getContent()); @@ -150,7 +162,7 @@ public class IncludeDstu2Test { @Test public void testIIncludedResourcesNonContainedInExtensionJson() throws Exception { - HttpGet httpGet = new HttpGet("http://localhost:" + ourPort + "/Patient?_query=extInclude&_pretty=true&_format=json"); + HttpGet httpGet = new HttpGet(ourServer.getBaseUrl() + "/Patient?_query=extInclude&_pretty=true&_format=json"); HttpResponse status = ourClient.execute(httpGet); String responseContent = IOUtils.toString(status.getEntity().getContent()); IOUtils.closeQuietly(status.getEntity().getContent()); @@ -177,7 +189,7 @@ public class IncludeDstu2Test { @Test @Disabled public void testMixedContainedAndNonContained() throws Exception { - HttpGet httpGet = new HttpGet("http://localhost:" + ourPort + "/DiagnosticReport?_query=stitchedInclude&_pretty=true"); + HttpGet httpGet = new HttpGet(ourServer.getBaseUrl() + "/DiagnosticReport?_query=stitchedInclude&_pretty=true"); HttpResponse status = ourClient.execute(httpGet); String responseContent = IOUtils.toString(status.getEntity().getContent()); IOUtils.closeQuietly(status.getEntity().getContent()); @@ -191,7 +203,7 @@ public class IncludeDstu2Test { @Test public void testNoIncludes() throws Exception { - HttpGet httpGet = new HttpGet("http://localhost:" + ourPort + "/Patient?name=Hello"); + HttpGet httpGet = new HttpGet(ourServer.getBaseUrl() + "/Patient?name=Hello"); HttpResponse status = ourClient.execute(httpGet); String responseContent = IOUtils.toString(status.getEntity().getContent()); IOUtils.closeQuietly(status.getEntity().getContent()); @@ -207,7 +219,7 @@ public class IncludeDstu2Test { @Test public void testOneIncludeJson() throws Exception { - HttpGet httpGet = new HttpGet("http://localhost:" + ourPort + "/Patient?name=Hello&_include=foo&_format=json"); + HttpGet httpGet = new HttpGet(ourServer.getBaseUrl() + "/Patient?name=Hello&_include=foo&_format=json"); HttpResponse status = ourClient.execute(httpGet); String responseContent = IOUtils.toString(status.getEntity().getContent()); IOUtils.closeQuietly(status.getEntity().getContent()); @@ -227,7 +239,7 @@ public class IncludeDstu2Test { @Test public void testOneIncludeXml() throws Exception { - HttpGet httpGet = new HttpGet("http://localhost:" + ourPort + "/Patient?name=Hello&_include=foo"); + HttpGet httpGet = new HttpGet(ourServer.getBaseUrl() + "/Patient?name=Hello&_include=foo"); HttpResponse status = ourClient.execute(httpGet); String responseContent = IOUtils.toString(status.getEntity().getContent()); IOUtils.closeQuietly(status.getEntity().getContent()); @@ -247,7 +259,7 @@ public class IncludeDstu2Test { @Test public void testTwoInclude() throws Exception { - HttpGet httpGet = new HttpGet("http://localhost:" + ourPort + "/Patient?name=Hello&_include=foo&_include=bar&_pretty=true"); + HttpGet httpGet = new HttpGet(ourServer.getBaseUrl() + "/Patient?name=Hello&_include=foo&_include=bar&_pretty=true"); HttpResponse status = ourClient.execute(httpGet); String responseContent = IOUtils.toString(status.getEntity().getContent()); IOUtils.closeQuietly(status.getEntity().getContent()); @@ -451,34 +463,8 @@ public class IncludeDstu2Test { @AfterAll public static void afterClassClearContext() throws Exception { - JettyUtil.closeServer(ourServer); TestUtil.randomizeLocaleAndTimezone(); } - @BeforeAll - public static void beforeClass() throws Exception { - - ourCtx = FhirContext.forDstu2(); - ourServer = new Server(0); - - DummyPatientResourceProvider patientProvider = new DummyPatientResourceProvider(); - - ServletHandler proxyHandler = new ServletHandler(); - RestfulServer servlet = new RestfulServer(ourCtx); - servlet.setResourceProviders(patientProvider, new DummyDiagnosticReportResourceProvider()); - servlet.setBundleInclusionRule(BundleInclusionRule.BASED_ON_RESOURCE_PRESENCE); - servlet.setDefaultResponseEncoding(EncodingEnum.XML); - ServletHolder servletHolder = new ServletHolder(servlet); - proxyHandler.addServletWithMapping(servletHolder, "/*"); - ourServer.setHandler(proxyHandler); - JettyUtil.startServer(ourServer); - ourPort = JettyUtil.getPortForStartedServer(ourServer); - - PoolingHttpClientConnectionManager connectionManager = new PoolingHttpClientConnectionManager(5000, TimeUnit.MILLISECONDS); - HttpClientBuilder builder = HttpClientBuilder.create(); - builder.setConnectionManager(connectionManager); - ourClient = builder.build(); - - } } diff --git a/hapi-fhir-structures-dstu2/src/test/java/ca/uhn/fhir/rest/server/OperationDuplicateServerDstu2Test.java b/hapi-fhir-structures-dstu2/src/test/java/ca/uhn/fhir/rest/server/OperationDuplicateServerDstu2Test.java index caeac0d1e9b..c8534a9bdc4 100644 --- a/hapi-fhir-structures-dstu2/src/test/java/ca/uhn/fhir/rest/server/OperationDuplicateServerDstu2Test.java +++ b/hapi-fhir-structures-dstu2/src/test/java/ca/uhn/fhir/rest/server/OperationDuplicateServerDstu2Test.java @@ -10,7 +10,9 @@ import ca.uhn.fhir.model.primitive.StringDt; import ca.uhn.fhir.rest.annotation.Operation; import ca.uhn.fhir.rest.annotation.OperationParam; import ca.uhn.fhir.rest.api.EncodingEnum; +import ca.uhn.fhir.test.utilities.HttpClientExtension; import ca.uhn.fhir.test.utilities.JettyUtil; +import ca.uhn.fhir.test.utilities.server.RestfulServerExtension; import ca.uhn.fhir.util.TestUtil; import org.apache.commons.io.IOUtils; import org.apache.http.HttpResponse; @@ -19,29 +21,39 @@ import org.apache.http.impl.client.CloseableHttpClient; import org.apache.http.impl.client.HttpClientBuilder; import org.apache.http.impl.conn.PoolingHttpClientConnectionManager; import org.eclipse.jetty.server.Server; -import org.eclipse.jetty.servlet.ServletHandler; -import org.eclipse.jetty.servlet.ServletHolder; +import org.eclipse.jetty.ee10.servlet.ServletHandler; +import org.eclipse.jetty.ee10.servlet.ServletHolder; import org.hl7.fhir.instance.model.api.IBaseResource; import org.junit.jupiter.api.AfterAll; import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.RegisterExtension; import java.util.concurrent.TimeUnit; import static org.junit.jupiter.api.Assertions.assertEquals; public class OperationDuplicateServerDstu2Test { - private static CloseableHttpClient ourClient; - private static FhirContext ourCtx; + private static final FhirContext ourCtx = FhirContext.forDstu2Cached(); private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(OperationDuplicateServerDstu2Test.class); - private static int ourPort; - private static Server ourServer; + + @RegisterExtension + public static final RestfulServerExtension ourServer = new RestfulServerExtension(ourCtx) + .setDefaultResponseEncoding(EncodingEnum.XML) + .registerProvider(new PatientProvider()) + .registerProvider(new OrganizationProvider()) + .registerProvider(new PlainProvider()) + .withPagingProvider(new FifoMemoryPagingProvider(10).setDefaultPageSize(2)) + .setDefaultPrettyPrint(false); + + @RegisterExtension + public static final HttpClientExtension ourClient = new HttpClientExtension(); @Test public void testOperationsAreCollapsed() throws Exception { // Metadata { - HttpGet httpGet = new HttpGet("http://localhost:" + ourPort + "/metadata?_pretty=true"); + HttpGet httpGet = new HttpGet(ourServer.getBaseUrl() + "/metadata?_pretty=true"); HttpResponse status = ourClient.execute(httpGet); assertEquals(200, status.getStatusLine().getStatusCode()); @@ -57,7 +69,7 @@ public class OperationDuplicateServerDstu2Test { // OperationDefinition { - HttpGet httpGet = new HttpGet("http://localhost:" + ourPort + "/OperationDefinition/OrganizationPatient-ts-myoperation?_pretty=true"); + HttpGet httpGet = new HttpGet(ourServer.getBaseUrl() + "/OperationDefinition/OrganizationPatient-ts-myoperation?_pretty=true"); HttpResponse status = ourClient.execute(httpGet); assertEquals(200, status.getStatusLine().getStatusCode()); @@ -78,37 +90,9 @@ public class OperationDuplicateServerDstu2Test { @AfterAll public static void afterClassClearContext() throws Exception { - JettyUtil.closeServer(ourServer); TestUtil.randomizeLocaleAndTimezone(); } - @BeforeAll - public static void beforeClass() throws Exception { - ourCtx = FhirContext.forDstu2(); - ourServer = new Server(0); - - ServletHandler proxyHandler = new ServletHandler(); - RestfulServer servlet = new RestfulServer(ourCtx); - - servlet.setPagingProvider(new FifoMemoryPagingProvider(10).setDefaultPageSize(2)); - - servlet.setDefaultResponseEncoding(EncodingEnum.XML); - servlet.setFhirContext(ourCtx); - servlet.setResourceProviders(new PatientProvider(), new OrganizationProvider()); - servlet.setPlainProviders(new PlainProvider()); - ServletHolder servletHolder = new ServletHolder(servlet); - proxyHandler.addServletWithMapping(servletHolder, "/*"); - ourServer.setHandler(proxyHandler); - JettyUtil.startServer(ourServer); - ourPort = JettyUtil.getPortForStartedServer(ourServer); - - PoolingHttpClientConnectionManager connectionManager = new PoolingHttpClientConnectionManager(5000, TimeUnit.MILLISECONDS); - HttpClientBuilder builder = HttpClientBuilder.create(); - builder.setConnectionManager(connectionManager); - ourClient = builder.build(); - - } - public static class BaseProvider { @Operation(name = "$myoperation", idempotent = true) diff --git a/hapi-fhir-structures-dstu2/src/test/java/ca/uhn/fhir/rest/server/OperationServerDstu2Test.java b/hapi-fhir-structures-dstu2/src/test/java/ca/uhn/fhir/rest/server/OperationServerDstu2Test.java index 388db6332fe..36236934379 100644 --- a/hapi-fhir-structures-dstu2/src/test/java/ca/uhn/fhir/rest/server/OperationServerDstu2Test.java +++ b/hapi-fhir-structures-dstu2/src/test/java/ca/uhn/fhir/rest/server/OperationServerDstu2Test.java @@ -21,9 +21,8 @@ import ca.uhn.fhir.rest.annotation.Read; import ca.uhn.fhir.rest.api.Constants; import ca.uhn.fhir.rest.api.EncodingEnum; import ca.uhn.fhir.rest.api.server.IBundleProvider; -import ca.uhn.fhir.rest.client.api.IGenericClient; -import ca.uhn.fhir.rest.client.interceptor.LoggingInterceptor; -import ca.uhn.fhir.test.utilities.JettyUtil; +import ca.uhn.fhir.test.utilities.HttpClientExtension; +import ca.uhn.fhir.test.utilities.server.RestfulServerExtension; import ca.uhn.fhir.util.TestUtil; import org.apache.commons.io.IOUtils; import org.apache.http.HttpResponse; @@ -32,21 +31,14 @@ import org.apache.http.client.methods.HttpGet; import org.apache.http.client.methods.HttpPost; import org.apache.http.entity.ContentType; import org.apache.http.entity.StringEntity; -import org.apache.http.impl.client.CloseableHttpClient; -import org.apache.http.impl.client.HttpClientBuilder; -import org.apache.http.impl.conn.PoolingHttpClientConnectionManager; -import org.eclipse.jetty.server.Server; -import org.eclipse.jetty.servlet.ServletHandler; -import org.eclipse.jetty.servlet.ServletHolder; import org.hl7.fhir.instance.model.api.IBaseResource; import org.junit.jupiter.api.AfterAll; -import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.RegisterExtension; import java.util.ArrayList; import java.util.List; -import java.util.concurrent.TimeUnit; import static org.hamcrest.MatcherAssert.assertThat; import static org.hamcrest.Matchers.blankOrNullString; @@ -59,8 +51,7 @@ import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertNull; public class OperationServerDstu2Test { - private static CloseableHttpClient ourClient; - private static FhirContext ourCtx; + private static final FhirContext ourCtx = FhirContext.forDstu2Cached(); private static IdDt ourLastId; private static String ourLastMethod; @@ -70,9 +61,17 @@ public class OperationServerDstu2Test { private static MoneyDt ourLastParamMoney1; private static UnsignedIntDt ourLastParamUnsignedInt1; private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(OperationServerDstu2Test.class); - private static int ourPort; - private static Server ourServer; - private IGenericClient myFhirClient; + + @RegisterExtension + public static final RestfulServerExtension ourServer = new RestfulServerExtension(ourCtx) + .setDefaultResponseEncoding(EncodingEnum.XML) + .registerProvider(new PatientProvider()) + .registerProvider(new PlainProvider()) + .withPagingProvider(new FifoMemoryPagingProvider(10).setDefaultPageSize(2)) + .setDefaultPrettyPrint(false); + + @RegisterExtension + public static final HttpClientExtension ourClient = new HttpClientExtension(); @BeforeEach public void before() { @@ -83,23 +82,17 @@ public class OperationServerDstu2Test { ourLastParamMoney1 = null; ourLastId = null; ourLastMethod = ""; - - myFhirClient = ourCtx.newRestfulGenericClient("http://localhost:" + ourPort); } @Test public void testConformance() throws Exception { - LoggingInterceptor loggingInterceptor = new LoggingInterceptor(); - loggingInterceptor.setLogResponseBody(true); - myFhirClient.registerInterceptor(loggingInterceptor); - - Conformance p = myFhirClient.fetchConformance().ofType(Conformance.class).prettyPrint().execute(); + Conformance p = ourServer.getFhirClient().fetchConformance().ofType(Conformance.class).prettyPrint().execute(); List ops = p.getRest().get(0).getOperation(); assertThat(ops.size(), greaterThan(1)); assertNull(ops.get(0).getDefinition().getReference().getBaseUrl()); assertThat(ops.get(0).getDefinition().getReference().getValue(), startsWith("OperationDefinition/")); - OperationDefinition def = myFhirClient.read().resource(OperationDefinition.class).withId(ops.get(0).getDefinition().getReference()).execute(); + OperationDefinition def = ourServer.getFhirClient().read().resource(OperationDefinition.class).withId(ops.get(0).getDefinition().getReference()).execute(); assertThat(def.getCode(), not(blankOrNullString())); List opNames = toOpNames(ops); @@ -113,7 +106,7 @@ public class OperationServerDstu2Test { */ @Test public void testOperationDefinition() { - OperationDefinition def = myFhirClient.read().resource(OperationDefinition.class).withId("OperationDefinition/Patient-t-OP_TYPE").execute(); + OperationDefinition def = ourServer.getFhirClient().read().resource(OperationDefinition.class).withId("OperationDefinition/Patient-t-OP_TYPE").execute(); ourLog.debug(ourCtx.newXmlParser().setPrettyPrint(true).encodeResourceToString(def)); @@ -158,7 +151,7 @@ public class OperationServerDstu2Test { public void testInstanceEverythingGet() throws Exception { // Try with a GET - HttpGet httpGet = new HttpGet("http://localhost:" + ourPort + "/Patient/123/$everything"); + HttpGet httpGet = new HttpGet(ourServer.getBaseUrl() + "/Patient/123/$everything"); CloseableHttpResponse status = ourClient.execute(httpGet); assertEquals(200, status.getStatusLine().getStatusCode()); @@ -173,7 +166,7 @@ public class OperationServerDstu2Test { @Test public void testInstanceEverythingHapiClient() throws Exception { - Parameters p = ourCtx.newRestfulGenericClient("http://localhost:" + ourPort).operation().onInstance(new IdDt("Patient/123")).named("$everything").withParameters(new Parameters()).execute(); + Parameters p = ourCtx.newRestfulGenericClient(ourServer.getBaseUrl()).operation().onInstance(new IdDt("Patient/123")).named("$everything").withParameters(new Parameters()).execute(); Bundle b = (Bundle) p.getParameterFirstRep().getResource(); assertEquals("instance $everything", ourLastMethod); @@ -186,7 +179,7 @@ public class OperationServerDstu2Test { String inParamsStr = ourCtx.newXmlParser().encodeResourceToString(new Parameters()); // Try with a POST - HttpPost httpPost = new HttpPost("http://localhost:" + ourPort + "/Patient/123/$everything"); + HttpPost httpPost = new HttpPost(ourServer.getBaseUrl() + "/Patient/123/$everything"); httpPost.setEntity(new StringEntity(inParamsStr, ContentType.create(Constants.CT_FHIR_XML, "UTF-8"))); HttpResponse status = ourClient.execute(httpPost); @@ -202,7 +195,7 @@ public class OperationServerDstu2Test { @Test public void testOperationCantUseGetIfItIsntIdempotent() throws Exception { - HttpGet httpPost = new HttpGet("http://localhost:" + ourPort + "/Patient/123/$OP_INSTANCE"); + HttpGet httpPost = new HttpGet(ourServer.getBaseUrl() + "/Patient/123/$OP_INSTANCE"); HttpResponse status = ourClient.execute(httpPost); assertEquals(Constants.STATUS_HTTP_405_METHOD_NOT_ALLOWED, status.getStatusLine().getStatusCode()); @@ -219,7 +212,7 @@ public class OperationServerDstu2Test { p.addParameter().setName("PARAM1").setValue(new IntegerDt(123)); String inParamsStr = ourCtx.newXmlParser().encodeResourceToString(p); - HttpPost httpPost = new HttpPost("http://localhost:" + ourPort + "/Patient/123/$OP_INSTANCE"); + HttpPost httpPost = new HttpPost(ourServer.getBaseUrl() + "/Patient/123/$OP_INSTANCE"); httpPost.setEntity(new StringEntity(inParamsStr, ContentType.create(Constants.CT_FHIR_XML, "UTF-8"))); CloseableHttpResponse status = ourClient.execute(httpPost); try { @@ -238,7 +231,7 @@ public class OperationServerDstu2Test { p.addParameter().setName("PARAM2").setResource(new Patient().setActive(true)); String inParamsStr = ourCtx.newXmlParser().encodeResourceToString(p); - HttpPost httpPost = new HttpPost("http://localhost:" + ourPort + "/Patient/123/$OP_INSTANCE"); + HttpPost httpPost = new HttpPost(ourServer.getBaseUrl() + "/Patient/123/$OP_INSTANCE"); httpPost.setEntity(new StringEntity(inParamsStr, ContentType.create(Constants.CT_FHIR_XML, "UTF-8"))); HttpResponse status = ourClient.execute(httpPost); @@ -258,7 +251,7 @@ public class OperationServerDstu2Test { * Against type should fail */ - httpPost = new HttpPost("http://localhost:" + ourPort + "/Patient/$OP_INSTANCE"); + httpPost = new HttpPost(ourServer.getBaseUrl() + "/Patient/$OP_INSTANCE"); httpPost.setEntity(new StringEntity(inParamsStr, ContentType.create(Constants.CT_FHIR_XML, "UTF-8"))); status = ourClient.execute(httpPost); @@ -276,7 +269,7 @@ public class OperationServerDstu2Test { p.addParameter().setName("PARAM2").setResource(new Patient().setActive(true)); String inParamsStr = ourCtx.newXmlParser().encodeResourceToString(p); - HttpPost httpPost = new HttpPost("http://localhost:" + ourPort + "/Patient/123/$OP_INSTANCE_OR_TYPE"); + HttpPost httpPost = new HttpPost(ourServer.getBaseUrl() + "/Patient/123/$OP_INSTANCE_OR_TYPE"); httpPost.setEntity(new StringEntity(inParamsStr, ContentType.create(Constants.CT_FHIR_XML, "UTF-8"))); HttpResponse status = ourClient.execute(httpPost); @@ -301,7 +294,7 @@ public class OperationServerDstu2Test { p.addParameter().setName("PARAM2").setResource(new Patient().setActive(true)); String inParamsStr = ourCtx.newXmlParser().encodeResourceToString(p); - HttpPost httpPost = new HttpPost("http://localhost:" + ourPort + "/Patient/$OP_INSTANCE_OR_TYPE"); + HttpPost httpPost = new HttpPost(ourServer.getBaseUrl() + "/Patient/$OP_INSTANCE_OR_TYPE"); httpPost.setEntity(new StringEntity(inParamsStr, ContentType.create(Constants.CT_FHIR_XML, "UTF-8"))); CloseableHttpResponse status = ourClient.execute(httpPost); @@ -325,7 +318,7 @@ public class OperationServerDstu2Test { p.addParameter().setName("PARAM2").setResource(new Patient().setActive(true)); String inParamsStr = ourCtx.newXmlParser().encodeResourceToString(p); - HttpPost httpPost = new HttpPost("http://localhost:" + ourPort + "/$OP_SERVER"); + HttpPost httpPost = new HttpPost(ourServer.getBaseUrl() + "/$OP_SERVER"); httpPost.setEntity(new StringEntity(inParamsStr, ContentType.create(Constants.CT_FHIR_XML, "UTF-8"))); HttpResponse status = ourClient.execute(httpPost); @@ -348,7 +341,7 @@ public class OperationServerDstu2Test { p.addParameter().setName("PARAM2").setResource(new Patient().setActive(true)); String inParamsStr = ourCtx.newXmlParser().encodeResourceToString(p); - HttpPost httpPost = new HttpPost("http://localhost:" + ourPort + "/Patient/$OP_TYPE"); + HttpPost httpPost = new HttpPost(ourServer.getBaseUrl() + "/Patient/$OP_TYPE"); httpPost.setEntity(new StringEntity(inParamsStr, ContentType.create(Constants.CT_FHIR_XML, "UTF-8"))); HttpResponse status = ourClient.execute(httpPost); @@ -371,7 +364,7 @@ public class OperationServerDstu2Test { p.addParameter().setName("PARAM2").setResource(new Patient().setActive(true)); String inParamsStr = ourCtx.newXmlParser().encodeResourceToString(p); - HttpPost httpPost = new HttpPost("http://localhost:" + ourPort + "/Patient/$OP_TYPE_RET_BUNDLE"); + HttpPost httpPost = new HttpPost(ourServer.getBaseUrl() + "/Patient/$OP_TYPE_RET_BUNDLE"); httpPost.setEntity(new StringEntity(inParamsStr, ContentType.create(Constants.CT_FHIR_XML, "UTF-8"))); HttpResponse status = ourClient.execute(httpPost); @@ -389,7 +382,7 @@ public class OperationServerDstu2Test { @Test public void testOperationWithBundleProviderResponse() throws Exception { - HttpGet httpPost = new HttpGet("http://localhost:" + ourPort + "/$OP_INSTANCE_BUNDLE_PROVIDER?_pretty=true"); + HttpGet httpPost = new HttpGet(ourServer.getBaseUrl() + "/$OP_INSTANCE_BUNDLE_PROVIDER?_pretty=true"); HttpResponse status = ourClient.execute(httpPost); assertEquals(200, status.getStatusLine().getStatusCode()); @@ -402,7 +395,7 @@ public class OperationServerDstu2Test { @Test public void testOperationWithGetUsingParams() throws Exception { - HttpGet httpGet = new HttpGet("http://localhost:" + ourPort + "/Patient/$OP_TYPE?PARAM1=PARAM1val"); + HttpGet httpGet = new HttpGet(ourServer.getBaseUrl() + "/Patient/$OP_TYPE?PARAM1=PARAM1val"); HttpResponse status = ourClient.execute(httpGet); assertEquals(200, status.getStatusLine().getStatusCode()); @@ -419,7 +412,7 @@ public class OperationServerDstu2Test { @Test public void testOperationWithGetUsingParamsFailsWithNonPrimitive() throws Exception { - HttpGet httpGet = new HttpGet("http://localhost:" + ourPort + "/Patient/$OP_TYPE?PARAM1=PARAM1val&PARAM2=foo"); + HttpGet httpGet = new HttpGet(ourServer.getBaseUrl() + "/Patient/$OP_TYPE?PARAM1=PARAM1val&PARAM2=foo"); HttpResponse status = ourClient.execute(httpGet); assertEquals(405, status.getStatusLine().getStatusCode()); @@ -438,7 +431,7 @@ public class OperationServerDstu2Test { p.addParameter().setName("PARAM3").setValue(new StringDt("PARAM3val2")); String inParamsStr = ourCtx.newXmlParser().encodeResourceToString(p); - HttpPost httpPost = new HttpPost("http://localhost:" + ourPort + "/$OP_SERVER_LIST_PARAM"); + HttpPost httpPost = new HttpPost(ourServer.getBaseUrl() + "/$OP_SERVER_LIST_PARAM"); httpPost.setEntity(new StringEntity(inParamsStr, ContentType.create(Constants.CT_FHIR_XML, "UTF-8"))); HttpResponse status = ourClient.execute(httpPost); @@ -463,7 +456,7 @@ public class OperationServerDstu2Test { p.addParameter().setName("PARAM1").setValue(new IntegerDt("123")); String inParamsStr = ourCtx.newXmlParser().encodeResourceToString(p); - HttpPost httpPost = new HttpPost("http://localhost:" + ourPort + "/Patient/$OP_PROFILE_DT"); + HttpPost httpPost = new HttpPost(ourServer.getBaseUrl() + "/Patient/$OP_PROFILE_DT"); httpPost.setEntity(new StringEntity(inParamsStr, ContentType.create(Constants.CT_FHIR_XML, "UTF-8"))); HttpResponse status = ourClient.execute(httpPost); @@ -484,7 +477,7 @@ public class OperationServerDstu2Test { p.addParameter().setName("PARAM1").setValue(money); String inParamsStr = ourCtx.newXmlParser().encodeResourceToString(p); - HttpPost httpPost = new HttpPost("http://localhost:" + ourPort + "/Patient/$OP_PROFILE_DT2"); + HttpPost httpPost = new HttpPost(ourServer.getBaseUrl() + "/Patient/$OP_PROFILE_DT2"); httpPost.setEntity(new StringEntity(inParamsStr, ContentType.create(Constants.CT_FHIR_XML, "UTF-8"))); HttpResponse status = ourClient.execute(httpPost); @@ -499,7 +492,7 @@ public class OperationServerDstu2Test { @Test public void testOperationWithProfileDatatypeUrl() throws Exception { - HttpGet httpPost = new HttpGet("http://localhost:" + ourPort + "/Patient/$OP_PROFILE_DT?PARAM1=123"); + HttpGet httpPost = new HttpGet(ourServer.getBaseUrl() + "/Patient/$OP_PROFILE_DT?PARAM1=123"); HttpResponse status = ourClient.execute(httpPost); assertEquals(200, status.getStatusLine().getStatusCode()); @@ -516,7 +509,7 @@ public class OperationServerDstu2Test { p.addParameter().setName("PARAM2").setResource(new Patient().setActive(true)); String inParamsStr = ourCtx.newXmlParser().encodeResourceToString(p); - HttpPost httpPost = new HttpPost("http://localhost:" + ourPort + "/Patient/$OP_TYPE"); + HttpPost httpPost = new HttpPost(ourServer.getBaseUrl() + "/Patient/$OP_TYPE"); httpPost.setEntity(new StringEntity(inParamsStr, ContentType.create(Constants.CT_FHIR_XML, "UTF-8"))); HttpResponse status = ourClient.execute(httpPost); @@ -532,7 +525,7 @@ public class OperationServerDstu2Test { @Test public void testReadWithOperations() throws Exception { - HttpGet httpGet = new HttpGet("http://localhost:" + ourPort + "/Patient/123"); + HttpGet httpGet = new HttpGet(ourServer.getBaseUrl() + "/Patient/123"); HttpResponse status = ourClient.execute(httpGet); assertEquals(200, status.getStatusLine().getStatusCode()); @@ -543,38 +536,9 @@ public class OperationServerDstu2Test { @AfterAll public static void afterClassClearContext() throws Exception { - JettyUtil.closeServer(ourServer); TestUtil.randomizeLocaleAndTimezone(); } - @BeforeAll - public static void beforeClass() throws Exception { - ourCtx = FhirContext.forDstu2(); - ourServer = new Server(0); - - ServletHandler proxyHandler = new ServletHandler(); - RestfulServer servlet = new RestfulServer(ourCtx); - - servlet.setPagingProvider(new FifoMemoryPagingProvider(10).setDefaultPageSize(2)); - - servlet.setFhirContext(ourCtx); - servlet.setResourceProviders(new PatientProvider()); - servlet.setPlainProviders(new PlainProvider()); - servlet.setDefaultResponseEncoding(EncodingEnum.XML); - - ServletHolder servletHolder = new ServletHolder(servlet); - proxyHandler.addServletWithMapping(servletHolder, "/*"); - ourServer.setHandler(proxyHandler); - JettyUtil.startServer(ourServer); - ourPort = JettyUtil.getPortForStartedServer(ourServer); - - PoolingHttpClientConnectionManager connectionManager = new PoolingHttpClientConnectionManager(5000, TimeUnit.MILLISECONDS); - HttpClientBuilder builder = HttpClientBuilder.create(); - builder.setConnectionManager(connectionManager); - ourClient = builder.build(); - - } - public static class PatientProvider implements IResourceProvider { @Override diff --git a/hapi-fhir-structures-dstu2/src/test/java/ca/uhn/fhir/rest/server/OperationServerWithSearchParamTypesDstu2Test.java b/hapi-fhir-structures-dstu2/src/test/java/ca/uhn/fhir/rest/server/OperationServerWithSearchParamTypesDstu2Test.java index 441964a2290..34c99f7cfec 100644 --- a/hapi-fhir-structures-dstu2/src/test/java/ca/uhn/fhir/rest/server/OperationServerWithSearchParamTypesDstu2Test.java +++ b/hapi-fhir-structures-dstu2/src/test/java/ca/uhn/fhir/rest/server/OperationServerWithSearchParamTypesDstu2Test.java @@ -10,6 +10,7 @@ import ca.uhn.fhir.model.primitive.StringDt; import ca.uhn.fhir.rest.annotation.Operation; import ca.uhn.fhir.rest.annotation.OperationParam; import ca.uhn.fhir.rest.api.Constants; +import ca.uhn.fhir.rest.api.EncodingEnum; import ca.uhn.fhir.rest.api.server.RequestDetails; import ca.uhn.fhir.rest.param.StringAndListParam; import ca.uhn.fhir.rest.param.StringOrListParam; @@ -20,7 +21,9 @@ import ca.uhn.fhir.rest.param.TokenParam; import ca.uhn.fhir.rest.param.TokenParamModifier; import ca.uhn.fhir.rest.server.provider.dstu2.ServerConformanceProvider; import ca.uhn.fhir.rest.server.servlet.ServletRequestDetails; +import ca.uhn.fhir.test.utilities.HttpClientExtension; import ca.uhn.fhir.test.utilities.JettyUtil; +import ca.uhn.fhir.test.utilities.server.RestfulServerExtension; import ca.uhn.fhir.util.TestUtil; import ca.uhn.fhir.util.UrlUtil; import org.apache.commons.io.IOUtils; @@ -33,16 +36,18 @@ import org.apache.http.impl.client.CloseableHttpClient; import org.apache.http.impl.client.HttpClientBuilder; import org.apache.http.impl.conn.PoolingHttpClientConnectionManager; import org.eclipse.jetty.server.Server; -import org.eclipse.jetty.servlet.ServletHandler; -import org.eclipse.jetty.servlet.ServletHolder; +import org.eclipse.jetty.ee10.servlet.ServletHandler; +import org.eclipse.jetty.ee10.servlet.ServletHolder; import org.hl7.fhir.instance.model.api.IBaseResource; import org.junit.jupiter.api.AfterAll; import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; -import javax.servlet.ServletConfig; -import javax.servlet.http.HttpServletRequest; +import jakarta.servlet.ServletConfig; +import jakarta.servlet.http.HttpServletRequest; +import org.junit.jupiter.api.extension.RegisterExtension; + import java.util.Collections; import java.util.List; import java.util.concurrent.TimeUnit; @@ -55,15 +60,22 @@ import static org.mockito.Mockito.mock; import static org.mockito.Mockito.when; public class OperationServerWithSearchParamTypesDstu2Test { - private static CloseableHttpClient ourClient; - private static FhirContext ourCtx; + private static final FhirContext ourCtx = FhirContext.forDstu2Cached(); private static String ourLastMethod; private static List ourLastParamValStr; private static List ourLastParamValTok; private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(OperationServerWithSearchParamTypesDstu2Test.class); - private static int ourPort; - private static Server ourServer; + + @RegisterExtension + public static final RestfulServerExtension ourServer = new RestfulServerExtension(ourCtx) + .setDefaultResponseEncoding(EncodingEnum.XML) + .registerProvider(new PatientProvider()) + .withPagingProvider(new FifoMemoryPagingProvider(10).setDefaultPageSize(2)) + .setDefaultPrettyPrint(false); + + @RegisterExtension + public static final HttpClientExtension ourClient = new HttpClientExtension(); @BeforeEach public void before() { @@ -97,7 +109,7 @@ public class OperationServerWithSearchParamTypesDstu2Test { p.addParameter().setName("valtok").setValue(new StringDt("VALTOK2A|VALTOK2B")); String inParamsStr = ourCtx.newXmlParser().encodeResourceToString(p); - HttpPost httpPost = new HttpPost("http://localhost:" + ourPort + "/Patient/$andlist"); + HttpPost httpPost = new HttpPost(ourServer.getBaseUrl() + "/Patient/$andlist"); httpPost.setEntity(new StringEntity(inParamsStr, ContentType.create(Constants.CT_FHIR_XML, "UTF-8"))); HttpResponse status = ourClient.execute(httpPost); @@ -123,7 +135,7 @@ public class OperationServerWithSearchParamTypesDstu2Test { @Test public void testAndListWithUrl() throws Exception { - HttpGet httpGet = new HttpGet("http://localhost:" + ourPort + "/Patient/$andlist?valstr=VALSTR1A,VALSTR1B&valstr=VALSTR2A,VALSTR2B&valtok=" + UrlUtil.escapeUrlParam("VALTOK1A|VALTOK1B") + "&valtok=" + HttpGet httpGet = new HttpGet(ourServer.getBaseUrl() + "/Patient/$andlist?valstr=VALSTR1A,VALSTR1B&valstr=VALSTR2A,VALSTR2B&valtok=" + UrlUtil.escapeUrlParam("VALTOK1A|VALTOK1B") + "&valtok=" + UrlUtil.escapeUrlParam("VALTOK2A|VALTOK2B")); HttpResponse status = ourClient.execute(httpGet); @@ -206,7 +218,7 @@ public class OperationServerWithSearchParamTypesDstu2Test { p.addParameter().setName("valtok").setValue(new StringDt("VALTOKA|VALTOKB")); String inParamsStr = ourCtx.newXmlParser().encodeResourceToString(p); - HttpPost httpPost = new HttpPost("http://localhost:" + ourPort + "/Patient/$nonrepeating"); + HttpPost httpPost = new HttpPost(ourServer.getBaseUrl() + "/Patient/$nonrepeating"); httpPost.setEntity(new StringEntity(inParamsStr, ContentType.create(Constants.CT_FHIR_XML, "UTF-8"))); HttpResponse status = ourClient.execute(httpPost); @@ -227,7 +239,7 @@ public class OperationServerWithSearchParamTypesDstu2Test { @Test public void testNonRepeatingWithUrl() throws Exception { - HttpGet httpGet = new HttpGet("http://localhost:" + ourPort + "/Patient/$nonrepeating?valstr=VALSTR&valtok=" + UrlUtil.escapeUrlParam("VALTOKA|VALTOKB")); + HttpGet httpGet = new HttpGet(ourServer.getBaseUrl() + "/Patient/$nonrepeating?valstr=VALSTR&valtok=" + UrlUtil.escapeUrlParam("VALTOKA|VALTOKB")); HttpResponse status = ourClient.execute(httpGet); assertEquals(200, status.getStatusLine().getStatusCode()); @@ -247,7 +259,7 @@ public class OperationServerWithSearchParamTypesDstu2Test { @Test public void testNonRepeatingWithUrlQualified() throws Exception { - HttpGet httpGet = new HttpGet("http://localhost:" + ourPort + "/Patient/$nonrepeating?valstr:exact=VALSTR&valtok:not=" + UrlUtil.escapeUrlParam("VALTOKA|VALTOKB")); + HttpGet httpGet = new HttpGet(ourServer.getBaseUrl() + "/Patient/$nonrepeating?valstr:exact=VALSTR&valtok:not=" + UrlUtil.escapeUrlParam("VALTOKA|VALTOKB")); HttpResponse status = ourClient.execute(httpGet); assertEquals(200, status.getStatusLine().getStatusCode()); @@ -276,7 +288,7 @@ public class OperationServerWithSearchParamTypesDstu2Test { p.addParameter().setName("valtok").setValue(new StringDt("VALTOK2A|VALTOK2B")); String inParamsStr = ourCtx.newXmlParser().encodeResourceToString(p); - HttpPost httpPost = new HttpPost("http://localhost:" + ourPort + "/Patient/$orlist"); + HttpPost httpPost = new HttpPost(ourServer.getBaseUrl() + "/Patient/$orlist"); httpPost.setEntity(new StringEntity(inParamsStr, ContentType.create(Constants.CT_FHIR_XML, "UTF-8"))); HttpResponse status = ourClient.execute(httpPost); @@ -302,7 +314,7 @@ public class OperationServerWithSearchParamTypesDstu2Test { @Test public void testOrListWithUrl() throws Exception { - HttpGet httpGet = new HttpGet("http://localhost:" + ourPort + "/Patient/$orlist?valstr=VALSTR1A,VALSTR1B&valstr=VALSTR2A,VALSTR2B&valtok=" + UrlUtil.escapeUrlParam("VALTOK1A|VALTOK1B") + "&valtok=" + HttpGet httpGet = new HttpGet(ourServer.getBaseUrl() + "/Patient/$orlist?valstr=VALSTR1A,VALSTR1B&valstr=VALSTR2A,VALSTR2B&valtok=" + UrlUtil.escapeUrlParam("VALTOK1A|VALTOK1B") + "&valtok=" + UrlUtil.escapeUrlParam("VALTOK2A|VALTOK2B")); HttpResponse status = ourClient.execute(httpGet); @@ -328,35 +340,9 @@ public class OperationServerWithSearchParamTypesDstu2Test { @AfterAll public static void afterClassClearContext() throws Exception { - JettyUtil.closeServer(ourServer); TestUtil.randomizeLocaleAndTimezone(); } - @BeforeAll - public static void beforeClass() throws Exception { - ourCtx = FhirContext.forDstu2(); - ourServer = new Server(0); - - ServletHandler proxyHandler = new ServletHandler(); - RestfulServer servlet = new RestfulServer(ourCtx); - - servlet.setPagingProvider(new FifoMemoryPagingProvider(10).setDefaultPageSize(2)); - - servlet.setFhirContext(ourCtx); - servlet.setResourceProviders(new PatientProvider()); - ServletHolder servletHolder = new ServletHolder(servlet); - proxyHandler.addServletWithMapping(servletHolder, "/*"); - ourServer.setHandler(proxyHandler); - JettyUtil.startServer(ourServer); - ourPort = JettyUtil.getPortForStartedServer(ourServer); - - PoolingHttpClientConnectionManager connectionManager = new PoolingHttpClientConnectionManager(5000, TimeUnit.MILLISECONDS); - HttpClientBuilder builder = HttpClientBuilder.create(); - builder.setConnectionManager(connectionManager); - ourClient = builder.build(); - - } - public static class PatientProvider implements IResourceProvider { @Operation(name = "$andlist", idempotent = true) diff --git a/hapi-fhir-structures-dstu2/src/test/java/ca/uhn/fhir/rest/server/PatientResourceProvider.java b/hapi-fhir-structures-dstu2/src/test/java/ca/uhn/fhir/rest/server/PatientResourceProvider.java index 07079f0e3e2..e41f47492a1 100644 --- a/hapi-fhir-structures-dstu2/src/test/java/ca/uhn/fhir/rest/server/PatientResourceProvider.java +++ b/hapi-fhir-structures-dstu2/src/test/java/ca/uhn/fhir/rest/server/PatientResourceProvider.java @@ -27,7 +27,7 @@ public class PatientResourceProvider implements IResourceProvider //@formatter:off @Search() public IBundleProvider search( - javax.servlet.http.HttpServletRequest theServletRequest, + jakarta.servlet.http.HttpServletRequest theServletRequest, @Description(shortDefinition="The resource identity") @OptionalParam(name="_id") diff --git a/hapi-fhir-structures-dstu2/src/test/java/ca/uhn/fhir/rest/server/ReadDstu2Test.java b/hapi-fhir-structures-dstu2/src/test/java/ca/uhn/fhir/rest/server/ReadDstu2Test.java index 300dacfa4d3..74fda07024c 100644 --- a/hapi-fhir-structures-dstu2/src/test/java/ca/uhn/fhir/rest/server/ReadDstu2Test.java +++ b/hapi-fhir-structures-dstu2/src/test/java/ca/uhn/fhir/rest/server/ReadDstu2Test.java @@ -12,7 +12,9 @@ import ca.uhn.fhir.rest.annotation.IdParam; import ca.uhn.fhir.rest.annotation.Read; import ca.uhn.fhir.rest.api.Constants; import ca.uhn.fhir.rest.api.EncodingEnum; +import ca.uhn.fhir.test.utilities.HttpClientExtension; import ca.uhn.fhir.test.utilities.JettyUtil; +import ca.uhn.fhir.test.utilities.server.RestfulServerExtension; import ca.uhn.fhir.util.DateUtils; import ca.uhn.fhir.util.TestUtil; import org.apache.commons.io.IOUtils; @@ -23,12 +25,13 @@ import org.apache.http.impl.client.CloseableHttpClient; import org.apache.http.impl.client.HttpClientBuilder; import org.apache.http.impl.conn.PoolingHttpClientConnectionManager; import org.eclipse.jetty.server.Server; -import org.eclipse.jetty.servlet.ServletHandler; -import org.eclipse.jetty.servlet.ServletHolder; +import org.eclipse.jetty.ee10.servlet.ServletHandler; +import org.eclipse.jetty.ee10.servlet.ServletHolder; import org.junit.jupiter.api.AfterAll; import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.RegisterExtension; import java.util.ArrayList; import java.util.List; @@ -41,13 +44,20 @@ import static org.junit.jupiter.api.Assertions.assertEquals; public class ReadDstu2Test { private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(ReadDstu2Test.class); - private static CloseableHttpClient ourClient; - private static FhirContext ourCtx = FhirContext.forDstu2(); + private static final FhirContext ourCtx = FhirContext.forDstu2Cached(); private static boolean ourInitializeProfileList; private static IdDt ourLastId; - private static int ourPort; - private static Server ourServer; - private static RestfulServer ourServlet; + + + @RegisterExtension + public static final RestfulServerExtension ourServer = new RestfulServerExtension(ourCtx) + .setDefaultResponseEncoding(EncodingEnum.XML) + .registerProvider(new DummyPatientResourceProvider()) + .withPagingProvider(new FifoMemoryPagingProvider(100)) + .setDefaultPrettyPrint(false); + + @RegisterExtension + public static final HttpClientExtension ourClient = new HttpClientExtension(); @BeforeEach public void before() { @@ -65,7 +75,7 @@ public class ReadDstu2Test { // Fixture was last modified at 2012-01-01T12:12:12Z // thus it has changed before the later time of 2012-01-01T13:00:00Z // so we expect a 304 - httpGet = new HttpGet("http://localhost:" + ourPort + "/Patient/2"); + httpGet = new HttpGet(ourServer.getBaseUrl() + "/Patient/2"); httpGet.addHeader(Constants.HEADER_IF_MODIFIED_SINCE, DateUtils.formatDate(new InstantDt("2012-01-01T13:00:00Z").getValue())); status = ourClient.execute(httpGet); try { @@ -77,7 +87,7 @@ public class ReadDstu2Test { // Fixture was last modified at 2012-01-01T12:12:12Z // thus it has changed at the same time of 2012-01-01T12:12:12Z // so we expect a 304 - httpGet = new HttpGet("http://localhost:" + ourPort + "/Patient/2"); + httpGet = new HttpGet(ourServer.getBaseUrl() + "/Patient/2"); httpGet.addHeader(Constants.HEADER_IF_MODIFIED_SINCE, DateUtils.formatDate(new InstantDt("2012-01-01T12:12:12Z").getValue())); status = ourClient.execute(httpGet); try { @@ -89,7 +99,7 @@ public class ReadDstu2Test { // Fixture was last modified at 2012-01-01T12:12:12Z // thus it has changed after the earlier time of 2012-01-01T10:00:00Z // so we expect a 200 - httpGet = new HttpGet("http://localhost:" + ourPort + "/Patient/2"); + httpGet = new HttpGet(ourServer.getBaseUrl() + "/Patient/2"); httpGet.addHeader(Constants.HEADER_IF_MODIFIED_SINCE, DateUtils.formatDate(new InstantDt("2012-01-01T10:00:00Z").getValue())); status = ourClient.execute(httpGet); try { @@ -107,7 +117,7 @@ public class ReadDstu2Test { public void testAddProfile() throws Exception { ourCtx.setAddProfileTagWhenEncoding(AddProfileTagEnum.ONLY_FOR_CUSTOM); - HttpGet httpGet = new HttpGet("http://localhost:" + ourPort + "/Patient/123?_format=xml"); + HttpGet httpGet = new HttpGet(ourServer.getBaseUrl() + "/Patient/123?_format=xml"); HttpResponse status = ourClient.execute(httpGet); String responseContent = IOUtils.toString(status.getEntity().getContent(), Constants.CHARSET_UTF8); IOUtils.closeQuietly(status.getEntity().getContent()); @@ -130,7 +140,7 @@ public class ReadDstu2Test { ourInitializeProfileList = true; ourCtx.setAddProfileTagWhenEncoding(AddProfileTagEnum.ONLY_FOR_CUSTOM); - HttpGet httpGet = new HttpGet("http://localhost:" + ourPort + "/Patient/123&_format=xml"); + HttpGet httpGet = new HttpGet(ourServer.getBaseUrl() + "/Patient/123&_format=xml"); HttpResponse status = ourClient.execute(httpGet); String responseContent = IOUtils.toString(status.getEntity().getContent(), Constants.CHARSET_UTF8); IOUtils.closeQuietly(status.getEntity().getContent()); @@ -150,7 +160,7 @@ public class ReadDstu2Test { public void testReadJson() throws Exception { ourCtx.setAddProfileTagWhenEncoding(AddProfileTagEnum.ONLY_FOR_CUSTOM); - HttpGet httpGet = new HttpGet("http://localhost:" + ourPort + "/Patient/123?_format=json"); + HttpGet httpGet = new HttpGet(ourServer.getBaseUrl() + "/Patient/123?_format=json"); HttpResponse status = ourClient.execute(httpGet); String responseContent = IOUtils.toString(status.getEntity().getContent(), Constants.CHARSET_UTF8); ourLog.info(responseContent); @@ -167,7 +177,7 @@ public class ReadDstu2Test { */ @Test public void testReadXml() throws Exception { - HttpGet httpGet = new HttpGet("http://localhost:" + ourPort + "/Patient/123&_format=xml"); + HttpGet httpGet = new HttpGet(ourServer.getBaseUrl() + "/Patient/123&_format=xml"); HttpResponse status = ourClient.execute(httpGet); String responseContent = IOUtils.toString(status.getEntity().getContent(), Constants.CHARSET_UTF8); IOUtils.closeQuietly(status.getEntity().getContent()); @@ -183,7 +193,7 @@ public class ReadDstu2Test { public void testVread() throws Exception { ourCtx.setAddProfileTagWhenEncoding(AddProfileTagEnum.ONLY_FOR_CUSTOM); - HttpGet httpGet = new HttpGet("http://localhost:" + ourPort + "/Patient/123/_history/1"); + HttpGet httpGet = new HttpGet(ourServer.getBaseUrl() + "/Patient/123/_history/1"); HttpResponse status = ourClient.execute(httpGet); String responseContent = IOUtils.toString(status.getEntity().getContent(), Constants.CHARSET_UTF8); IOUtils.closeQuietly(status.getEntity().getContent()); @@ -234,33 +244,7 @@ public class ReadDstu2Test { @AfterAll public static void afterClassClearContext() throws Exception { - JettyUtil.closeServer(ourServer); TestUtil.randomizeLocaleAndTimezone(); } - @BeforeAll - public static void beforeClass() throws Exception { - ourServer = new Server(0); - - DummyPatientResourceProvider patientProvider = new DummyPatientResourceProvider(); - - ServletHandler proxyHandler = new ServletHandler(); - ourServlet = new RestfulServer(ourCtx); - ourServlet.setFhirContext(ourCtx); - ourServlet.setResourceProviders(patientProvider); - ourServlet.setDefaultResponseEncoding(EncodingEnum.XML); - - ServletHolder servletHolder = new ServletHolder(ourServlet); - proxyHandler.addServletWithMapping(servletHolder, "/*"); - ourServer.setHandler(proxyHandler); - JettyUtil.startServer(ourServer); - ourPort = JettyUtil.getPortForStartedServer(ourServer); - - PoolingHttpClientConnectionManager connectionManager = new PoolingHttpClientConnectionManager(5000, TimeUnit.MILLISECONDS); - HttpClientBuilder builder = HttpClientBuilder.create(); - builder.setConnectionManager(connectionManager); - ourClient = builder.build(); - - } - } diff --git a/hapi-fhir-structures-dstu2/src/test/java/ca/uhn/fhir/rest/server/SearchBundleProviderWithNoSizeDstu2Test.java b/hapi-fhir-structures-dstu2/src/test/java/ca/uhn/fhir/rest/server/SearchBundleProviderWithNoSizeDstu2Test.java index 8e330e82ddc..8186316569b 100644 --- a/hapi-fhir-structures-dstu2/src/test/java/ca/uhn/fhir/rest/server/SearchBundleProviderWithNoSizeDstu2Test.java +++ b/hapi-fhir-structures-dstu2/src/test/java/ca/uhn/fhir/rest/server/SearchBundleProviderWithNoSizeDstu2Test.java @@ -11,8 +11,8 @@ import org.apache.http.impl.client.CloseableHttpClient; import org.apache.http.impl.client.HttpClientBuilder; import org.apache.http.impl.conn.PoolingHttpClientConnectionManager; import org.eclipse.jetty.server.Server; -import org.eclipse.jetty.servlet.ServletHandler; -import org.eclipse.jetty.servlet.ServletHolder; +import org.eclipse.jetty.ee10.servlet.ServletHandler; +import org.eclipse.jetty.ee10.servlet.ServletHolder; import org.hl7.fhir.instance.model.api.IBaseResource; import org.junit.jupiter.api.AfterAll; import org.junit.jupiter.api.BeforeAll; diff --git a/hapi-fhir-structures-dstu2/src/test/java/ca/uhn/fhir/rest/server/SearchCountParamDstu2Test.java b/hapi-fhir-structures-dstu2/src/test/java/ca/uhn/fhir/rest/server/SearchCountParamDstu2Test.java index e8a575a9833..f9167179b46 100644 --- a/hapi-fhir-structures-dstu2/src/test/java/ca/uhn/fhir/rest/server/SearchCountParamDstu2Test.java +++ b/hapi-fhir-structures-dstu2/src/test/java/ca/uhn/fhir/rest/server/SearchCountParamDstu2Test.java @@ -8,7 +8,9 @@ import ca.uhn.fhir.rest.annotation.OptionalParam; import ca.uhn.fhir.rest.annotation.Search; import ca.uhn.fhir.rest.api.EncodingEnum; import ca.uhn.fhir.rest.param.TokenParam; +import ca.uhn.fhir.test.utilities.HttpClientExtension; import ca.uhn.fhir.test.utilities.JettyUtil; +import ca.uhn.fhir.test.utilities.server.RestfulServerExtension; import ca.uhn.fhir.util.TestUtil; import org.apache.commons.io.IOUtils; import org.apache.http.client.methods.CloseableHttpResponse; @@ -17,13 +19,14 @@ import org.apache.http.impl.client.CloseableHttpClient; import org.apache.http.impl.client.HttpClientBuilder; import org.apache.http.impl.conn.PoolingHttpClientConnectionManager; import org.eclipse.jetty.server.Server; -import org.eclipse.jetty.servlet.ServletHandler; -import org.eclipse.jetty.servlet.ServletHolder; +import org.eclipse.jetty.ee10.servlet.ServletHandler; +import org.eclipse.jetty.ee10.servlet.ServletHolder; import org.hl7.fhir.instance.model.api.IBaseResource; import org.junit.jupiter.api.AfterAll; import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.RegisterExtension; import java.util.ArrayList; import java.util.List; @@ -35,14 +38,21 @@ import static org.junit.jupiter.api.Assertions.assertEquals; public class SearchCountParamDstu2Test { - private static CloseableHttpClient ourClient; - private static FhirContext ourCtx = FhirContext.forDstu2(); + private static final FhirContext ourCtx = FhirContext.forDstu2Cached(); private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(SearchCountParamDstu2Test.class); - private static int ourPort; - private static Server ourServer; private static String ourLastMethod; private static Integer ourLastParam; + @RegisterExtension + public static final RestfulServerExtension ourServer = new RestfulServerExtension(ourCtx) + .setDefaultResponseEncoding(EncodingEnum.XML) + .registerProvider(new DummyPatientResourceProvider()) + .withPagingProvider(new FifoMemoryPagingProvider(100)) + .setDefaultPrettyPrint(false); + + @RegisterExtension + public static final HttpClientExtension ourClient = new HttpClientExtension(); + @BeforeEach public void before() { ourLastMethod = null; @@ -51,23 +61,23 @@ public class SearchCountParamDstu2Test { @Test public void testSearch() throws Exception { - HttpGet httpGet = new HttpGet("http://localhost:" + ourPort + "/Patient?_count=2"); + HttpGet httpGet = new HttpGet(ourServer.getBaseUrl() + "/Patient?_count=2"); CloseableHttpResponse status = ourClient.execute(httpGet); try { String responseContent = IOUtils.toString(status.getEntity().getContent()); ourLog.info(responseContent); assertEquals(200, status.getStatusLine().getStatusCode()); assertEquals("search", ourLastMethod); - assertEquals(new Integer(2), ourLastParam); + assertEquals(Integer.valueOf(2), ourLastParam); assertThat(responseContent, stringContainsInOrder( "", "", - "", + "", "", "", "", - "", + "", "")); } finally { @@ -81,7 +91,7 @@ public class SearchCountParamDstu2Test { */ @Test public void testSearchWithNoCountParam() throws Exception { - HttpGet httpGet = new HttpGet("http://localhost:" + ourPort + "/Patient?_query=searchWithNoCountParam&_count=2"); + HttpGet httpGet = new HttpGet(ourServer.getBaseUrl() + "/Patient?_query=searchWithNoCountParam&_count=2"); CloseableHttpResponse status = ourClient.execute(httpGet); try { String responseContent = IOUtils.toString(status.getEntity().getContent()); @@ -94,11 +104,11 @@ public class SearchCountParamDstu2Test { assertThat(responseContent, stringContainsInOrder( "", "", - "", + "", "", "", "", - "", + "", "")); //@formatter:on @@ -110,35 +120,9 @@ public class SearchCountParamDstu2Test { @AfterAll public static void afterClassClearContext() throws Exception { - JettyUtil.closeServer(ourServer); TestUtil.randomizeLocaleAndTimezone(); } - @BeforeAll - public static void beforeClass() throws Exception { - ourServer = new Server(0); - - DummyPatientResourceProvider patientProvider = new DummyPatientResourceProvider(); - - ServletHandler proxyHandler = new ServletHandler(); - RestfulServer servlet = new RestfulServer(ourCtx); - servlet.setPagingProvider(new FifoMemoryPagingProvider(10)); - servlet.setDefaultResponseEncoding(EncodingEnum.XML); - servlet.setResourceProviders(patientProvider); - - ServletHolder servletHolder = new ServletHolder(servlet); - proxyHandler.addServletWithMapping(servletHolder, "/*"); - ourServer.setHandler(proxyHandler); - JettyUtil.startServer(ourServer); - ourPort = JettyUtil.getPortForStartedServer(ourServer); - - PoolingHttpClientConnectionManager connectionManager = new PoolingHttpClientConnectionManager(5000, TimeUnit.MILLISECONDS); - HttpClientBuilder builder = HttpClientBuilder.create(); - builder.setConnectionManager(connectionManager); - ourClient = builder.build(); - - } - public static class DummyPatientResourceProvider implements IResourceProvider { @Override diff --git a/hapi-fhir-structures-dstu2/src/test/java/ca/uhn/fhir/rest/server/SearchDstu2Test.java b/hapi-fhir-structures-dstu2/src/test/java/ca/uhn/fhir/rest/server/SearchDstu2Test.java index d28dbf5505b..e4a9c1fbfe3 100644 --- a/hapi-fhir-structures-dstu2/src/test/java/ca/uhn/fhir/rest/server/SearchDstu2Test.java +++ b/hapi-fhir-structures-dstu2/src/test/java/ca/uhn/fhir/rest/server/SearchDstu2Test.java @@ -22,7 +22,9 @@ import ca.uhn.fhir.rest.param.ParamPrefixEnum; import ca.uhn.fhir.rest.param.QuantityParam; import ca.uhn.fhir.rest.param.ReferenceParam; import ca.uhn.fhir.rest.param.StringParam; +import ca.uhn.fhir.test.utilities.HttpClientExtension; import ca.uhn.fhir.test.utilities.JettyUtil; +import ca.uhn.fhir.test.utilities.server.RestfulServerExtension; import ca.uhn.fhir.util.TestUtil; import com.google.common.base.Charsets; import org.apache.commons.io.IOUtils; @@ -40,15 +42,16 @@ import org.apache.http.impl.client.HttpClientBuilder; import org.apache.http.impl.conn.PoolingHttpClientConnectionManager; import org.apache.http.message.BasicNameValuePair; import org.eclipse.jetty.server.Server; -import org.eclipse.jetty.servlet.ServletHandler; -import org.eclipse.jetty.servlet.ServletHolder; +import org.eclipse.jetty.ee10.servlet.ServletHandler; +import org.eclipse.jetty.ee10.servlet.ServletHolder; import org.hl7.fhir.instance.model.api.IBaseResource; import org.junit.jupiter.api.AfterAll; import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.RegisterExtension; -import javax.annotation.Nonnull; +import jakarta.annotation.Nonnull; import java.nio.charset.Charset; import java.util.ArrayList; import java.util.Collections; @@ -66,17 +69,24 @@ import static org.junit.jupiter.api.Assertions.assertNull; public class SearchDstu2Test { - private static CloseableHttpClient ourClient; - private static FhirContext ourCtx = FhirContext.forDstu2(); + private static final FhirContext ourCtx = FhirContext.forDstu2Cached(); private static DateAndListParam ourLastDateAndList; private static String ourLastMethod; private static QuantityParam ourLastQuantity; private static ReferenceParam ourLastRef; private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(SearchDstu2Test.class); - private static int ourPort; private static InstantDt ourReturnPublished; - private static Server ourServer; - private static RestfulServer ourServlet; + + @RegisterExtension + public static final RestfulServerExtension ourServer = new RestfulServerExtension(ourCtx) + .setDefaultResponseEncoding(EncodingEnum.XML) + .registerProvider(new DummyPatientResourceProvider()) + .registerProvider(new DummyPatientResourceNoIdProvider()) + .withPagingProvider(new FifoMemoryPagingProvider(100)) + .setDefaultPrettyPrint(false); + + @RegisterExtension + public static final HttpClientExtension ourClient = new HttpClientExtension(); @BeforeEach public void before() { @@ -84,13 +94,13 @@ public class SearchDstu2Test { ourLastDateAndList = null; ourLastRef = null; ourLastQuantity = null; - ourServlet.setIgnoreServerParsedRequestParameters(true); + ourServer.getRestfulServer().setIgnoreServerParsedRequestParameters(true); } @Test public void testSearchWithInvalidPostUrl() throws Exception { // should end with _search - HttpPost filePost = new HttpPost("http://localhost:" + ourPort + "/Patient?name=Central"); + HttpPost filePost = new HttpPost(ourServer.getBaseUrl() + "/Patient?name=Central"); // add parameters to the post method List parameters = new ArrayList(); @@ -110,7 +120,7 @@ public class SearchDstu2Test { @Test public void testEncodeConvertsReferencesToRelative() throws Exception { - HttpGet httpGet = new HttpGet("http://localhost:" + ourPort + "/Patient?_query=searchWithRef"); + HttpGet httpGet = new HttpGet(ourServer.getBaseUrl() + "/Patient?_query=searchWithRef"); HttpResponse status = ourClient.execute(httpGet); String responseContent = IOUtils.toString(status.getEntity().getContent()); IOUtils.closeQuietly(status.getEntity().getContent()); @@ -127,7 +137,7 @@ public class SearchDstu2Test { @Test public void testEncodeConvertsReferencesToRelativeJson() throws Exception { - HttpGet httpGet = new HttpGet("http://localhost:" + ourPort + "/Patient?_query=searchWithRef&_format=json"); + HttpGet httpGet = new HttpGet(ourServer.getBaseUrl() + "/Patient?_query=searchWithRef&_format=json"); HttpResponse status = ourClient.execute(httpGet); String responseContent = IOUtils.toString(status.getEntity().getContent()); IOUtils.closeQuietly(status.getEntity().getContent()); @@ -146,7 +156,7 @@ public class SearchDstu2Test { public void testResultBundleHasUpdateTime() throws Exception { ourReturnPublished = new InstantDt("2011-02-03T11:22:33Z"); - HttpGet httpGet = new HttpGet("http://localhost:" + ourPort + "/Patient?_query=searchWithBundleProvider&_pretty=true"); + HttpGet httpGet = new HttpGet(ourServer.getBaseUrl() + "/Patient?_query=searchWithBundleProvider&_pretty=true"); HttpResponse status = ourClient.execute(httpGet); String responseContent = IOUtils.toString(status.getEntity().getContent()); IOUtils.closeQuietly(status.getEntity().getContent()); @@ -157,7 +167,7 @@ public class SearchDstu2Test { @Test public void testResultBundleHasUuid() throws Exception { - HttpGet httpGet = new HttpGet("http://localhost:" + ourPort + "/Patient?_query=searchWithRef"); + HttpGet httpGet = new HttpGet(ourServer.getBaseUrl() + "/Patient?_query=searchWithRef"); HttpResponse status = ourClient.execute(httpGet); String responseContent = IOUtils.toString(status.getEntity().getContent()); IOUtils.closeQuietly(status.getEntity().getContent()); @@ -169,7 +179,7 @@ public class SearchDstu2Test { @Test public void testSearchBlacklist01Failing() throws Exception { - HttpGet httpGet = new HttpGet("http://localhost:" + ourPort + "/Patient?_query=searchBlacklist01&ref.black1=value"); + HttpGet httpGet = new HttpGet(ourServer.getBaseUrl() + "/Patient?_query=searchBlacklist01&ref.black1=value"); HttpResponse status = ourClient.execute(httpGet); String responseContent = IOUtils.toString(status.getEntity().getContent(), Charsets.UTF_8); IOUtils.closeQuietly(status.getEntity().getContent()); @@ -179,7 +189,7 @@ public class SearchDstu2Test { @Test public void testSearchBlacklist01Passing() throws Exception { - HttpGet httpGet = new HttpGet("http://localhost:" + ourPort + "/Patient?_query=searchBlacklist01&ref.white1=value"); + HttpGet httpGet = new HttpGet(ourServer.getBaseUrl() + "/Patient?_query=searchBlacklist01&ref.white1=value"); HttpResponse status = ourClient.execute(httpGet); String responseContent = IOUtils.toString(status.getEntity().getContent(), Charsets.UTF_8); IOUtils.closeQuietly(status.getEntity().getContent()); @@ -190,7 +200,7 @@ public class SearchDstu2Test { @Test public void testSearchByPost() throws Exception { - HttpPost httpGet = new HttpPost("http://localhost:" + ourPort + "/Patient/_search"); + HttpPost httpGet = new HttpPost(ourServer.getBaseUrl() + "/Patient/_search"); StringEntity entity = new StringEntity("searchDateAndList=2001,2002&searchDateAndList=2003,2004", ContentType.APPLICATION_FORM_URLENCODED); httpGet.setEntity(entity); @@ -209,7 +219,7 @@ public class SearchDstu2Test { @Test public void testSearchMethodReturnsNull() throws Exception { - HttpGet httpGet = new HttpGet("http://localhost:" + ourPort + "/Patient?_query=searchReturnNull"); + HttpGet httpGet = new HttpGet(ourServer.getBaseUrl() + "/Patient?_query=searchReturnNull"); HttpResponse status = ourClient.execute(httpGet); String responseContent = IOUtils.toString(status.getEntity().getContent()); @@ -221,7 +231,7 @@ public class SearchDstu2Test { @Test public void testSearchByPostWithBodyAndUrlParams() throws Exception { - HttpPost httpGet = new HttpPost("http://localhost:" + ourPort + "/Patient/_search?_format=json"); + HttpPost httpGet = new HttpPost(ourServer.getBaseUrl() + "/Patient/_search?_format=json"); StringEntity entity = new StringEntity("searchDateAndList=2001,2002&searchDateAndList=2003,2004", ContentType.APPLICATION_FORM_URLENCODED); httpGet.setEntity(entity); @@ -241,9 +251,9 @@ public class SearchDstu2Test { @Test public void testSearchByPostWithBodyAndUrlParamsNoManual() throws Exception { - ourServlet.setIgnoreServerParsedRequestParameters(false); + ourServer.getRestfulServer().setIgnoreServerParsedRequestParameters(false); - HttpPost httpGet = new HttpPost("http://localhost:" + ourPort + "/Patient/_search?_format=json"); + HttpPost httpGet = new HttpPost(ourServer.getBaseUrl() + "/Patient/_search?_format=json"); StringEntity entity = new StringEntity("searchDateAndList=2001,2002&searchDateAndList=2003,2004", ContentType.APPLICATION_FORM_URLENCODED); httpGet.setEntity(entity); @@ -263,7 +273,7 @@ public class SearchDstu2Test { @Test public void testSearchByPut() throws Exception { - HttpPut httpGet = new HttpPut("http://localhost:" + ourPort + "/Patient/_search"); + HttpPut httpGet = new HttpPut(ourServer.getBaseUrl() + "/Patient/_search"); StringEntity entity = new StringEntity("searchDateAndList=2001,2002&searchDateAndList=2003,2004", ContentType.APPLICATION_FORM_URLENCODED); httpGet.setEntity(entity); @@ -276,7 +286,7 @@ public class SearchDstu2Test { @Test public void testSearchDateAndList() throws Exception { - HttpGet httpGet = new HttpGet("http://localhost:" + ourPort + "/Patient?searchDateAndList=2001,2002&searchDateAndList=2003,2004"); + HttpGet httpGet = new HttpGet(ourServer.getBaseUrl() + "/Patient?searchDateAndList=2001,2002&searchDateAndList=2003,2004"); HttpResponse status = ourClient.execute(httpGet); String responseContent = IOUtils.toString(status.getEntity().getContent()); IOUtils.closeQuietly(status.getEntity().getContent()); @@ -296,7 +306,7 @@ public class SearchDstu2Test { public void testSearchPagesAllHaveCorrectBundleType() throws Exception { Bundle resp; { - HttpGet httpGet = new HttpGet("http://localhost:" + ourPort + "/Patient?searchHugeResults=yes&_count=10&_pretty=true"); + HttpGet httpGet = new HttpGet(ourServer.getBaseUrl() + "/Patient?searchHugeResults=yes&_count=10&_pretty=true"); HttpResponse status = ourClient.execute(httpGet); String responseContent = IOUtils.toString(status.getEntity().getContent()); IOUtils.closeQuietly(status.getEntity().getContent()); @@ -341,7 +351,7 @@ public class SearchDstu2Test { */ @Test public void testSearchQuantityMissingTrue() throws Exception { - HttpGet httpGet = new HttpGet("http://localhost:" + ourPort + "/Patient?quantity:missing=true"); + HttpGet httpGet = new HttpGet(ourServer.getBaseUrl() + "/Patient?quantity:missing=true"); HttpResponse status = ourClient.execute(httpGet); String responseContent = IOUtils.toString(status.getEntity().getContent()); IOUtils.closeQuietly(status.getEntity().getContent()); @@ -355,7 +365,7 @@ public class SearchDstu2Test { */ @Test public void testSearchQuantityValue() throws Exception { - HttpGet httpGet = new HttpGet("http://localhost:" + ourPort + "/Patient?quantity=gt100"); + HttpGet httpGet = new HttpGet(ourServer.getBaseUrl() + "/Patient?quantity=gt100"); HttpResponse status = ourClient.execute(httpGet); String responseContent = IOUtils.toString(status.getEntity().getContent()); IOUtils.closeQuietly(status.getEntity().getContent()); @@ -368,7 +378,7 @@ public class SearchDstu2Test { @Test public void testSearchReferenceParams01() throws Exception { - HttpGet httpGet = new HttpGet("http://localhost:" + ourPort + "/Patient?_query=searchNoList&ref=123"); + HttpGet httpGet = new HttpGet(ourServer.getBaseUrl() + "/Patient?_query=searchNoList&ref=123"); HttpResponse status = ourClient.execute(httpGet); String responseContent = IOUtils.toString(status.getEntity().getContent()); IOUtils.closeQuietly(status.getEntity().getContent()); @@ -381,7 +391,7 @@ public class SearchDstu2Test { @Test public void testSearchReferenceParams02() throws Exception { - HttpGet httpGet = new HttpGet("http://localhost:" + ourPort + "/Patient?_query=searchNoList&ref=Patient/123"); + HttpGet httpGet = new HttpGet(ourServer.getBaseUrl() + "/Patient?_query=searchNoList&ref=Patient/123"); HttpResponse status = ourClient.execute(httpGet); String responseContent = IOUtils.toString(status.getEntity().getContent()); IOUtils.closeQuietly(status.getEntity().getContent()); @@ -394,7 +404,7 @@ public class SearchDstu2Test { @Test public void testSearchReferenceParams03() throws Exception { - HttpGet httpGet = new HttpGet("http://localhost:" + ourPort + "/Patient?_query=searchNoList&ref:Patient=Patient/123"); + HttpGet httpGet = new HttpGet(ourServer.getBaseUrl() + "/Patient?_query=searchNoList&ref:Patient=Patient/123"); HttpResponse status = ourClient.execute(httpGet); String responseContent = IOUtils.toString(status.getEntity().getContent()); IOUtils.closeQuietly(status.getEntity().getContent()); @@ -407,7 +417,7 @@ public class SearchDstu2Test { @Test public void testSearchReferenceParams04() throws Exception { - HttpGet httpGet = new HttpGet("http://localhost:" + ourPort + "/Patient?_query=searchNoList&ref:Patient=123"); + HttpGet httpGet = new HttpGet(ourServer.getBaseUrl() + "/Patient?_query=searchNoList&ref:Patient=123"); HttpResponse status = ourClient.execute(httpGet); String responseContent = IOUtils.toString(status.getEntity().getContent()); IOUtils.closeQuietly(status.getEntity().getContent()); @@ -423,7 +433,7 @@ public class SearchDstu2Test { */ @Test public void testSearchByIdExact() throws Exception { - HttpGet httpGet = new HttpGet("http://localhost:" + ourPort + "/Patient?_id:exact=aaa&reference=value"); + HttpGet httpGet = new HttpGet(ourServer.getBaseUrl() + "/Patient?_id:exact=aaa&reference=value"); HttpResponse status = ourClient.execute(httpGet); String responseContent = IOUtils.toString(status.getEntity().getContent(), Charset.defaultCharset()); IOUtils.closeQuietly(status.getEntity().getContent()); @@ -435,7 +445,7 @@ public class SearchDstu2Test { @Test public void testSearchByQualifiedIdQualifiedString() throws Exception { - HttpGet httpGet = new HttpGet("http://localhost:" + ourPort + "/Patient?_id:exact=aaa&stringParam:exact=value"); + HttpGet httpGet = new HttpGet(ourServer.getBaseUrl() + "/Patient?_id:exact=aaa&stringParam:exact=value"); HttpResponse status = ourClient.execute(httpGet); String responseContent = IOUtils.toString(status.getEntity().getContent(), Charset.defaultCharset()); IOUtils.closeQuietly(status.getEntity().getContent()); @@ -447,7 +457,7 @@ public class SearchDstu2Test { @Test public void testSearchByQualifiedString() throws Exception { - HttpGet httpGet = new HttpGet("http://localhost:" + ourPort + "/Patient?_id=aaa&stringParam:exact=value"); + HttpGet httpGet = new HttpGet(ourServer.getBaseUrl() + "/Patient?_id=aaa&stringParam:exact=value"); HttpResponse status = ourClient.execute(httpGet); String responseContent = IOUtils.toString(status.getEntity().getContent(), Charset.defaultCharset()); IOUtils.closeQuietly(status.getEntity().getContent()); @@ -459,7 +469,7 @@ public class SearchDstu2Test { @Test public void testSearchByQualifiedIdString() throws Exception { - HttpGet httpGet = new HttpGet("http://localhost:" + ourPort + "/Patient?_id:exact=aaa&stringParam=value"); + HttpGet httpGet = new HttpGet(ourServer.getBaseUrl() + "/Patient?_id:exact=aaa&stringParam=value"); HttpResponse status = ourClient.execute(httpGet); String responseContent = IOUtils.toString(status.getEntity().getContent(), Charset.defaultCharset()); IOUtils.closeQuietly(status.getEntity().getContent()); @@ -471,7 +481,7 @@ public class SearchDstu2Test { @Test public void testSearchByIdString() throws Exception { - HttpGet httpGet = new HttpGet("http://localhost:" + ourPort + "/Patient?_id=aaa&stringParam=value"); + HttpGet httpGet = new HttpGet(ourServer.getBaseUrl() + "/Patient?_id=aaa&stringParam=value"); HttpResponse status = ourClient.execute(httpGet); String responseContent = IOUtils.toString(status.getEntity().getContent(), Charset.defaultCharset()); IOUtils.closeQuietly(status.getEntity().getContent()); @@ -484,7 +494,7 @@ public class SearchDstu2Test { @Test public void testSearchWhitelist01Failing() throws Exception { - HttpGet httpGet = new HttpGet("http://localhost:" + ourPort + "/Patient?_query=searchWhitelist01&ref=value"); + HttpGet httpGet = new HttpGet(ourServer.getBaseUrl() + "/Patient?_query=searchWhitelist01&ref=value"); HttpResponse status = ourClient.execute(httpGet); String responseContent = IOUtils.toString(status.getEntity().getContent()); IOUtils.closeQuietly(status.getEntity().getContent()); @@ -494,7 +504,7 @@ public class SearchDstu2Test { @Test public void testSearchWhitelist01Passing() throws Exception { - HttpGet httpGet = new HttpGet("http://localhost:" + ourPort + "/Patient?_query=searchWhitelist01&ref.white1=value"); + HttpGet httpGet = new HttpGet(ourServer.getBaseUrl() + "/Patient?_query=searchWhitelist01&ref.white1=value"); HttpResponse status = ourClient.execute(httpGet); String responseContent = IOUtils.toString(status.getEntity().getContent()); IOUtils.closeQuietly(status.getEntity().getContent()); @@ -505,36 +515,9 @@ public class SearchDstu2Test { @AfterAll public static void afterClassClearContext() throws Exception { - JettyUtil.closeServer(ourServer); TestUtil.randomizeLocaleAndTimezone(); } - @BeforeAll - public static void beforeClass() throws Exception { - ourServer = new Server(0); - - DummyPatientResourceProvider patientProvider = new DummyPatientResourceProvider(); - DummyPatientResourceNoIdProvider patientResourceNoIdProviderProvider = new DummyPatientResourceNoIdProvider(); - - ServletHandler proxyHandler = new ServletHandler(); - ourServlet = new RestfulServer(ourCtx); - ourServlet.setPagingProvider(new FifoMemoryPagingProvider(10)); - ourServlet.setDefaultResponseEncoding(EncodingEnum.XML); - ourServlet.setResourceProviders(patientResourceNoIdProviderProvider, patientProvider); - - ServletHolder servletHolder = new ServletHolder(ourServlet); - proxyHandler.addServletWithMapping(servletHolder, "/*"); - ourServer.setHandler(proxyHandler); - JettyUtil.startServer(ourServer); - ourPort = JettyUtil.getPortForStartedServer(ourServer); - - PoolingHttpClientConnectionManager connectionManager = new PoolingHttpClientConnectionManager(5000, TimeUnit.MILLISECONDS); - HttpClientBuilder builder = HttpClientBuilder.create(); - builder.setConnectionManager(connectionManager); - ourClient = builder.build(); - - } - public static class DummyPatientResourceNoIdProvider implements IResourceProvider { @Override @@ -698,7 +681,7 @@ public class SearchDstu2Test { public Patient searchWithRef() { Patient patient = new Patient(); patient.setId("Patient/1/_history/1"); - patient.getManagingOrganization().setReference("http://localhost:" + ourPort + "/Organization/555/_history/666"); + patient.getManagingOrganization().setReference(ourServer.getBaseUrl() + "/Organization/555/_history/666"); return patient; } diff --git a/hapi-fhir-structures-dstu2/src/test/java/ca/uhn/fhir/rest/server/SearchReturningProfiledResourceDstu2Test.java b/hapi-fhir-structures-dstu2/src/test/java/ca/uhn/fhir/rest/server/SearchReturningProfiledResourceDstu2Test.java index c04e524443f..52f0bb07be2 100644 --- a/hapi-fhir-structures-dstu2/src/test/java/ca/uhn/fhir/rest/server/SearchReturningProfiledResourceDstu2Test.java +++ b/hapi-fhir-structures-dstu2/src/test/java/ca/uhn/fhir/rest/server/SearchReturningProfiledResourceDstu2Test.java @@ -1,6 +1,7 @@ package ca.uhn.fhir.rest.server; import ca.uhn.fhir.context.FhirContext; +import ca.uhn.fhir.context.api.AddProfileTagEnum; import ca.uhn.fhir.model.api.ResourceMetadataKeyEnum; import ca.uhn.fhir.model.api.annotation.Description; import ca.uhn.fhir.model.dstu2.resource.Bundle; @@ -14,7 +15,9 @@ import ca.uhn.fhir.rest.client.api.IGenericClient; import ca.uhn.fhir.rest.param.DateRangeParam; import ca.uhn.fhir.rest.param.StringParam; import ca.uhn.fhir.rest.server.interceptor.ResponseHighlighterInterceptor; +import ca.uhn.fhir.test.utilities.HttpClientExtension; import ca.uhn.fhir.test.utilities.JettyUtil; +import ca.uhn.fhir.test.utilities.server.RestfulServerExtension; import ca.uhn.fhir.util.TestUtil; import ca.uhn.fhir.validation.PatientProfileDstu2; import org.apache.commons.io.IOUtils; @@ -24,12 +27,15 @@ import org.apache.http.impl.client.CloseableHttpClient; import org.apache.http.impl.client.HttpClientBuilder; import org.apache.http.impl.conn.PoolingHttpClientConnectionManager; import org.eclipse.jetty.server.Server; -import org.eclipse.jetty.servlet.ServletHandler; -import org.eclipse.jetty.servlet.ServletHolder; +import org.eclipse.jetty.ee10.servlet.ServletHandler; +import org.eclipse.jetty.ee10.servlet.ServletHolder; import org.hl7.fhir.instance.model.api.IBaseResource; import org.junit.jupiter.api.AfterAll; +import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.RegisterExtension; import java.util.ArrayList; import java.util.Collections; @@ -43,16 +49,34 @@ import static org.junit.jupiter.api.Assertions.assertEquals; public class SearchReturningProfiledResourceDstu2Test { - private static CloseableHttpClient ourClient; - private static FhirContext ourCtx = FhirContext.forDstu2(); + private static final FhirContext ourCtx = FhirContext.forDstu2Cached(); private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(SearchReturningProfiledResourceDstu2Test.class); - private static int ourPort; - private static Server ourServer; + @RegisterExtension + public static final RestfulServerExtension ourServer = new RestfulServerExtension(ourCtx) + .setDefaultResponseEncoding(EncodingEnum.XML) + .registerProvider(new DummyPatientResourceProvider()) + .registerInterceptor(new ResponseHighlighterInterceptor()) + .withPagingProvider(new FifoMemoryPagingProvider(100)) + .setDefaultPrettyPrint(false); + + @RegisterExtension + public static final HttpClientExtension ourClient = new HttpClientExtension(); + + @BeforeEach + public void before() { + ourCtx.setAddProfileTagWhenEncoding(AddProfileTagEnum.ONLY_FOR_CUSTOM); + } + + @AfterEach + public void after() { + ourCtx.setDefaultTypeForProfile("http://ahr.copa.inso.tuwien.ac.at/StructureDefinition/Patient", null); + } + + @Test public void testClientTypedRequest() throws Exception { - ourCtx = FhirContext.forDstu2(); - IGenericClient client = ourCtx.newRestfulGenericClient("http://localhost:" + ourPort + "/"); + IGenericClient client = ourCtx.newRestfulGenericClient(ourServer.getBaseUrl() + "/"); Bundle bundle = client.search().forResource(PatientProfileDstu2.class).returnBundle(Bundle.class).execute(); assertEquals(PatientProfileDstu2.class, bundle.getEntry().get(0).getResource().getClass()); @@ -60,9 +84,8 @@ public class SearchReturningProfiledResourceDstu2Test { @Test public void testClientUntypedRequestWithHint() throws Exception { - ourCtx = FhirContext.forDstu2(); ourCtx.setDefaultTypeForProfile("http://ahr.copa.inso.tuwien.ac.at/StructureDefinition/Patient", PatientProfileDstu2.class); - IGenericClient client = ourCtx.newRestfulGenericClient("http://localhost:" + ourPort + "/"); + IGenericClient client = ourCtx.newRestfulGenericClient(ourServer.getBaseUrl() + "/"); Bundle bundle = client.search().forResource(Patient.class).returnBundle(Bundle.class).execute(); assertEquals(PatientProfileDstu2.class, bundle.getEntry().get(0).getResource().getClass()); @@ -70,8 +93,7 @@ public class SearchReturningProfiledResourceDstu2Test { @Test public void testClientUntypedRequestWithoutHint() throws Exception { - ourCtx = FhirContext.forDstu2(); - IGenericClient client = ourCtx.newRestfulGenericClient("http://localhost:" + ourPort + "/"); + IGenericClient client = ourCtx.newRestfulGenericClient(ourServer.getBaseUrl() + "/"); Bundle bundle = client.search().forResource(Patient.class).returnBundle(Bundle.class).execute(); assertEquals(Patient.class, bundle.getEntry().get(0).getResource().getClass()); @@ -79,7 +101,7 @@ public class SearchReturningProfiledResourceDstu2Test { @Test public void testProfilesGetAdded() throws Exception { - HttpGet httpGet = new HttpGet("http://localhost:" + ourPort + "/Patient?_pretty=true"); + HttpGet httpGet = new HttpGet(ourServer.getBaseUrl() + "/Patient?_pretty=true"); HttpResponse status = ourClient.execute(httpGet); String responseContent = IOUtils.toString(status.getEntity().getContent()); IOUtils.closeQuietly(status.getEntity().getContent()); @@ -93,7 +115,7 @@ public class SearchReturningProfiledResourceDstu2Test { @Test public void testProfilesGetAddedHtml() throws Exception { - HttpGet httpGet = new HttpGet("http://localhost:" + ourPort + "/Patient?_format=html"); + HttpGet httpGet = new HttpGet(ourServer.getBaseUrl() + "/Patient?_format=html"); HttpResponse status = ourClient.execute(httpGet); String responseContent = IOUtils.toString(status.getEntity().getContent()); IOUtils.closeQuietly(status.getEntity().getContent()); @@ -107,34 +129,9 @@ public class SearchReturningProfiledResourceDstu2Test { @AfterAll public static void afterClassClearContext() throws Exception { - JettyUtil.closeServer(ourServer); TestUtil.randomizeLocaleAndTimezone(); } - @BeforeAll - public static void beforeClass() throws Exception { - ourServer = new Server(0); - - ServletHandler proxyHandler = new ServletHandler(); - RestfulServer servlet = new RestfulServer(ourCtx); - - servlet.registerInterceptor(new ResponseHighlighterInterceptor()); - servlet.setDefaultResponseEncoding(EncodingEnum.XML); - servlet.setResourceProviders(new DummyPatientResourceProvider()); - - ServletHolder servletHolder = new ServletHolder(servlet); - proxyHandler.addServletWithMapping(servletHolder, "/*"); - ourServer.setHandler(proxyHandler); - JettyUtil.startServer(ourServer); - ourPort = JettyUtil.getPortForStartedServer(ourServer); - - PoolingHttpClientConnectionManager connectionManager = new PoolingHttpClientConnectionManager(5000, TimeUnit.MILLISECONDS); - HttpClientBuilder builder = HttpClientBuilder.create(); - builder.setConnectionManager(connectionManager); - ourClient = builder.build(); - - } - public static class DummyPatientResourceProvider implements IResourceProvider { @Override diff --git a/hapi-fhir-structures-dstu2/src/test/java/ca/uhn/fhir/rest/server/SearchWithDstu2BundleTest.java b/hapi-fhir-structures-dstu2/src/test/java/ca/uhn/fhir/rest/server/SearchWithDstu2BundleTest.java index f609c3583a1..9a5b91e2228 100644 --- a/hapi-fhir-structures-dstu2/src/test/java/ca/uhn/fhir/rest/server/SearchWithDstu2BundleTest.java +++ b/hapi-fhir-structures-dstu2/src/test/java/ca/uhn/fhir/rest/server/SearchWithDstu2BundleTest.java @@ -5,7 +5,10 @@ import ca.uhn.fhir.model.api.IResource; import ca.uhn.fhir.model.dstu2.resource.Bundle; import ca.uhn.fhir.model.dstu2.resource.Patient; import ca.uhn.fhir.rest.annotation.Search; +import ca.uhn.fhir.rest.api.EncodingEnum; +import ca.uhn.fhir.test.utilities.HttpClientExtension; import ca.uhn.fhir.test.utilities.JettyUtil; +import ca.uhn.fhir.test.utilities.server.RestfulServerExtension; import ca.uhn.fhir.util.TestUtil; import org.apache.commons.io.IOUtils; import org.apache.http.HttpResponse; @@ -14,11 +17,12 @@ import org.apache.http.impl.client.CloseableHttpClient; import org.apache.http.impl.client.HttpClientBuilder; import org.apache.http.impl.conn.PoolingHttpClientConnectionManager; import org.eclipse.jetty.server.Server; -import org.eclipse.jetty.servlet.ServletHandler; -import org.eclipse.jetty.servlet.ServletHolder; +import org.eclipse.jetty.ee10.servlet.ServletHandler; +import org.eclipse.jetty.ee10.servlet.ServletHolder; import org.junit.jupiter.api.AfterAll; import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.RegisterExtension; import java.util.concurrent.TimeUnit; @@ -28,15 +32,22 @@ import static org.junit.jupiter.api.Assertions.assertEquals; public class SearchWithDstu2BundleTest { - private static CloseableHttpClient ourClient; - private static FhirContext ourCtx = FhirContext.forDstu2(); + private static final FhirContext ourCtx = FhirContext.forDstu2Cached(); private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(SearchWithDstu2BundleTest.class); - private static int ourPort; - private static Server ourServer; + + @RegisterExtension + public static final RestfulServerExtension ourServer = new RestfulServerExtension(ourCtx) + .setDefaultResponseEncoding(EncodingEnum.XML) + .registerProvider(new DummyPatientResourceProvider()) + .withPagingProvider(new FifoMemoryPagingProvider(100)) + .setDefaultPrettyPrint(false); + + @RegisterExtension + public static final HttpClientExtension ourClient = new HttpClientExtension(); @Test public void testSearch() throws Exception { - HttpGet httpGet = new HttpGet("http://localhost:" + ourPort + "/Patient?_format=xml&_pretty=true"); + HttpGet httpGet = new HttpGet(ourServer.getBaseUrl() + "/Patient?_format=xml&_pretty=true"); HttpResponse status = ourClient.execute(httpGet); String responseContent = IOUtils.toString(status.getEntity().getContent()); IOUtils.closeQuietly(status.getEntity().getContent()); @@ -52,7 +63,7 @@ public class SearchWithDstu2BundleTest { "", "" , "", - "", + "", "" , "" , "" , @@ -65,33 +76,9 @@ public class SearchWithDstu2BundleTest { @AfterAll public static void afterClassClearContext() throws Exception { - JettyUtil.closeServer(ourServer); TestUtil.randomizeLocaleAndTimezone(); } - @BeforeAll - public static void beforeClass() throws Exception { - ourServer = new Server(0); - - DummyPatientResourceProvider patientProvider = new DummyPatientResourceProvider(); - - ServletHandler proxyHandler = new ServletHandler(); - RestfulServer servlet = new RestfulServer(ourCtx); - servlet.setFhirContext(ourCtx); - servlet.setResourceProviders(patientProvider); - ServletHolder servletHolder = new ServletHolder(servlet); - proxyHandler.addServletWithMapping(servletHolder, "/*"); - ourServer.setHandler(proxyHandler); - JettyUtil.startServer(ourServer); - ourPort = JettyUtil.getPortForStartedServer(ourServer); - - PoolingHttpClientConnectionManager connectionManager = new PoolingHttpClientConnectionManager(5000000, TimeUnit.MILLISECONDS); - HttpClientBuilder builder = HttpClientBuilder.create(); - builder.setConnectionManager(connectionManager); - ourClient = builder.build(); - - } - /** * Created by dsotnikov on 2/25/2014. */ diff --git a/hapi-fhir-structures-dstu2/src/test/java/ca/uhn/fhir/rest/server/SearchWithGenericListDstu2Test.java b/hapi-fhir-structures-dstu2/src/test/java/ca/uhn/fhir/rest/server/SearchWithGenericListDstu2Test.java index 1cd0e34a250..8cc362ad2bd 100644 --- a/hapi-fhir-structures-dstu2/src/test/java/ca/uhn/fhir/rest/server/SearchWithGenericListDstu2Test.java +++ b/hapi-fhir-structures-dstu2/src/test/java/ca/uhn/fhir/rest/server/SearchWithGenericListDstu2Test.java @@ -8,25 +8,19 @@ import ca.uhn.fhir.rest.annotation.RequiredParam; import ca.uhn.fhir.rest.annotation.Search; import ca.uhn.fhir.rest.api.EncodingEnum; import ca.uhn.fhir.rest.param.TokenParam; -import ca.uhn.fhir.test.utilities.JettyUtil; +import ca.uhn.fhir.test.utilities.HttpClientExtension; +import ca.uhn.fhir.test.utilities.server.RestfulServerExtension; import ca.uhn.fhir.util.TestUtil; import org.apache.commons.io.IOUtils; import org.apache.http.HttpResponse; import org.apache.http.client.methods.HttpGet; -import org.apache.http.impl.client.CloseableHttpClient; -import org.apache.http.impl.client.HttpClientBuilder; -import org.apache.http.impl.conn.PoolingHttpClientConnectionManager; -import org.eclipse.jetty.server.Server; -import org.eclipse.jetty.servlet.ServletHandler; -import org.eclipse.jetty.servlet.ServletHolder; import org.junit.jupiter.api.AfterAll; -import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.RegisterExtension; import java.util.ArrayList; import java.util.List; -import java.util.concurrent.TimeUnit; import static org.hamcrest.MatcherAssert.assertThat; import static org.hamcrest.Matchers.containsString; @@ -34,12 +28,19 @@ import static org.junit.jupiter.api.Assertions.assertEquals; public class SearchWithGenericListDstu2Test { - private static CloseableHttpClient ourClient; - private static FhirContext ourCtx = FhirContext.forDstu2(); + private static final FhirContext ourCtx = FhirContext.forDstu2Cached(); private static String ourLastMethod; private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(SearchWithGenericListDstu2Test.class); - private static int ourPort; - private static Server ourServer; + + @RegisterExtension + public static final RestfulServerExtension ourServer = new RestfulServerExtension(ourCtx) + .setDefaultResponseEncoding(EncodingEnum.XML) + .registerProvider(new DummyPatientResourceProvider()) + .withPagingProvider(new FifoMemoryPagingProvider(100)) + .setDefaultPrettyPrint(false); + + @RegisterExtension + public static final HttpClientExtension ourClient = new HttpClientExtension(); @BeforeEach public void before() { @@ -51,7 +52,7 @@ public class SearchWithGenericListDstu2Test { */ @Test public void testSearch() throws Exception { - HttpGet httpGet = new HttpGet("http://localhost:" + ourPort + "/Patient?identifier=foo&_pretty=true"); + HttpGet httpGet = new HttpGet(ourServer.getBaseUrl() + "/Patient?identifier=foo&_pretty=true"); HttpResponse status = ourClient.execute(httpGet); String responseContent = IOUtils.toString(status.getEntity().getContent()); IOUtils.closeQuietly(status.getEntity().getContent()); @@ -59,42 +60,16 @@ public class SearchWithGenericListDstu2Test { assertEquals(200, status.getStatusLine().getStatusCode()); assertEquals("searchByIdentifier", ourLastMethod); assertThat(responseContent, containsString("")); + assertThat(responseContent, containsString("")); } @AfterAll public static void afterClassClearContext() throws Exception { - JettyUtil.closeServer(ourServer); TestUtil.randomizeLocaleAndTimezone(); } - @BeforeAll - public static void beforeClass() throws Exception { - ourServer = new Server(0); - - DummyPatientResourceProvider patientProvider = new DummyPatientResourceProvider(); - - ServletHandler proxyHandler = new ServletHandler(); - RestfulServer servlet = new RestfulServer(ourCtx); - servlet.setPagingProvider(new FifoMemoryPagingProvider(10)); - servlet.setDefaultResponseEncoding(EncodingEnum.XML); - servlet.setResourceProviders(patientProvider); - - ServletHolder servletHolder = new ServletHolder(servlet); - proxyHandler.addServletWithMapping(servletHolder, "/*"); - ourServer.setHandler(proxyHandler); - JettyUtil.startServer(ourServer); - ourPort = JettyUtil.getPortForStartedServer(ourServer); - - PoolingHttpClientConnectionManager connectionManager = new PoolingHttpClientConnectionManager(5000, TimeUnit.MILLISECONDS); - HttpClientBuilder builder = HttpClientBuilder.create(); - builder.setConnectionManager(connectionManager); - ourClient = builder.build(); - - } - public static class DummyPatientResourceProvider implements IResourceProvider { @Override diff --git a/hapi-fhir-structures-dstu2/src/test/java/ca/uhn/fhir/rest/server/ServerConformanceProviderDstu2Test.java b/hapi-fhir-structures-dstu2/src/test/java/ca/uhn/fhir/rest/server/ServerConformanceProviderDstu2Test.java index 4e034ac93dc..2bb11ab7f97 100644 --- a/hapi-fhir-structures-dstu2/src/test/java/ca/uhn/fhir/rest/server/ServerConformanceProviderDstu2Test.java +++ b/hapi-fhir-structures-dstu2/src/test/java/ca/uhn/fhir/rest/server/ServerConformanceProviderDstu2Test.java @@ -60,9 +60,9 @@ import org.hl7.fhir.instance.model.api.IBaseResource; import org.junit.jupiter.api.AfterAll; import org.junit.jupiter.api.Test; -import javax.servlet.ServletConfig; -import javax.servlet.ServletException; -import javax.servlet.http.HttpServletRequest; +import jakarta.servlet.ServletConfig; +import jakarta.servlet.ServletException; +import jakarta.servlet.http.HttpServletRequest; import java.util.ArrayList; import java.util.Collection; import java.util.HashSet; @@ -731,7 +731,7 @@ public class ServerConformanceProviderDstu2Test { public static class MultiTypeEncounterProvider implements IResourceProvider { @Operation(name = "someOp") - public IBundleProvider everything(javax.servlet.http.HttpServletRequest theServletRequest, @IdParam IdDt theId, + public IBundleProvider everything(jakarta.servlet.http.HttpServletRequest theServletRequest, @IdParam IdDt theId, @OperationParam(name = "someOpParam1") DateDt theStart, @OperationParam(name = "someOpParam2") Encounter theEnd) { return null; } @@ -742,7 +742,7 @@ public class ServerConformanceProviderDstu2Test { } @Validate - public IBundleProvider validate(javax.servlet.http.HttpServletRequest theServletRequest, @IdParam IdDt theId, @ResourceParam Encounter thePatient) { + public IBundleProvider validate(jakarta.servlet.http.HttpServletRequest theServletRequest, @IdParam IdDt theId, @ResourceParam Encounter thePatient) { return null; } @@ -751,7 +751,7 @@ public class ServerConformanceProviderDstu2Test { public static class MultiTypePatientProvider implements IResourceProvider { @Operation(name = "someOp") - public IBundleProvider everything(javax.servlet.http.HttpServletRequest theServletRequest, @IdParam IdDt theId, + public IBundleProvider everything(jakarta.servlet.http.HttpServletRequest theServletRequest, @IdParam IdDt theId, @OperationParam(name = "someOpParam1") DateDt theStart, @OperationParam(name = "someOpParam2") Patient theEnd) { return null; } @@ -762,7 +762,7 @@ public class ServerConformanceProviderDstu2Test { } @Validate - public IBundleProvider validate(javax.servlet.http.HttpServletRequest theServletRequest, @IdParam IdDt theId, @ResourceParam Patient thePatient) { + public IBundleProvider validate(jakarta.servlet.http.HttpServletRequest theServletRequest, @IdParam IdDt theId, @ResourceParam Patient thePatient) { return null; } @@ -795,7 +795,7 @@ public class ServerConformanceProviderDstu2Test { public static class PlainProviderWithExtendedOperationOnNoType { @Operation(name = "plain", idempotent = true, returnParameters = {@OperationParam(min = 1, max = 2, name = "out1", type = StringDt.class)}) - public IBundleProvider everything(javax.servlet.http.HttpServletRequest theServletRequest, @IdParam ca.uhn.fhir.model.primitive.IdDt theId, @OperationParam(name = "start") DateDt theStart, @OperationParam(name = "end") DateDt theEnd) { + public IBundleProvider everything(jakarta.servlet.http.HttpServletRequest theServletRequest, @IdParam ca.uhn.fhir.model.primitive.IdDt theId, @OperationParam(name = "start") DateDt theStart, @OperationParam(name = "end") DateDt theEnd) { return null; } @@ -804,7 +804,7 @@ public class ServerConformanceProviderDstu2Test { public static class ProviderWithExtendedOperationReturningBundle implements IResourceProvider { @Operation(name = "everything", idempotent = true) - public IBundleProvider everything(javax.servlet.http.HttpServletRequest theServletRequest, @IdParam ca.uhn.fhir.model.primitive.IdDt theId, @OperationParam(name = "start") DateDt theStart, @OperationParam(name = "end") DateDt theEnd) { + public IBundleProvider everything(jakarta.servlet.http.HttpServletRequest theServletRequest, @IdParam ca.uhn.fhir.model.primitive.IdDt theId, @OperationParam(name = "start") DateDt theStart, @OperationParam(name = "end") DateDt theEnd) { return null; } diff --git a/hapi-fhir-structures-dstu2/src/test/java/ca/uhn/fhir/rest/server/ServerFeaturesDstu2Test.java b/hapi-fhir-structures-dstu2/src/test/java/ca/uhn/fhir/rest/server/ServerFeaturesDstu2Test.java index 03e45b6bafe..1492d1f9810 100644 --- a/hapi-fhir-structures-dstu2/src/test/java/ca/uhn/fhir/rest/server/ServerFeaturesDstu2Test.java +++ b/hapi-fhir-structures-dstu2/src/test/java/ca/uhn/fhir/rest/server/ServerFeaturesDstu2Test.java @@ -7,7 +7,9 @@ import ca.uhn.fhir.model.primitive.IdDt; import ca.uhn.fhir.rest.annotation.IdParam; import ca.uhn.fhir.rest.annotation.Read; import ca.uhn.fhir.rest.api.EncodingEnum; +import ca.uhn.fhir.test.utilities.HttpClientExtension; import ca.uhn.fhir.test.utilities.JettyUtil; +import ca.uhn.fhir.test.utilities.server.RestfulServerExtension; import ca.uhn.fhir.util.TestUtil; import org.apache.commons.io.IOUtils; import org.apache.http.HttpResponse; @@ -18,11 +20,12 @@ import org.apache.http.impl.client.CloseableHttpClient; import org.apache.http.impl.client.HttpClientBuilder; import org.apache.http.impl.conn.PoolingHttpClientConnectionManager; import org.eclipse.jetty.server.Server; -import org.eclipse.jetty.servlet.ServletHandler; -import org.eclipse.jetty.servlet.ServletHolder; +import org.eclipse.jetty.ee10.servlet.ServletHandler; +import org.eclipse.jetty.ee10.servlet.ServletHolder; import org.junit.jupiter.api.AfterAll; import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.RegisterExtension; import java.nio.charset.StandardCharsets; import java.util.ArrayList; @@ -38,17 +41,22 @@ import static org.junit.jupiter.api.Assertions.assertEquals; */ public class ServerFeaturesDstu2Test { - private static CloseableHttpClient ourClient; - private static FhirContext ourCtx = FhirContext.forDstu2(); + private static final FhirContext ourCtx = FhirContext.forDstu2Cached(); private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(ServerFeaturesDstu2Test.class); - private static int ourPort; - private static Server ourServer; - private static RestfulServer ourServlet; + @RegisterExtension + public static final RestfulServerExtension ourServer = new RestfulServerExtension(ourCtx) + .setDefaultResponseEncoding(EncodingEnum.XML) + .registerProvider(new DummyPatientResourceProvider()) + .withPagingProvider(new FifoMemoryPagingProvider(100)) + .setDefaultPrettyPrint(false); + + @RegisterExtension + public static final HttpClientExtension ourClient = new HttpClientExtension(); @Test public void testOptions() throws Exception { - HttpOptions httpGet = new HttpOptions("http://localhost:" + ourPort + ""); + HttpOptions httpGet = new HttpOptions(ourServer.getBaseUrl() + ""); HttpResponse status = ourClient.execute(httpGet); String responseContent = IOUtils.toString(status.getEntity().getContent(), StandardCharsets.UTF_8); IOUtils.closeQuietly(status.getEntity().getContent()); @@ -60,7 +68,7 @@ public class ServerFeaturesDstu2Test { * Now with a leading / */ - httpGet = new HttpOptions("http://localhost:" + ourPort + "/"); + httpGet = new HttpOptions(ourServer.getBaseUrl() + "/"); status = ourClient.execute(httpGet); responseContent = IOUtils.toString(status.getEntity().getContent(), StandardCharsets.UTF_8); IOUtils.closeQuietly(status.getEntity().getContent()); @@ -76,7 +84,7 @@ public class ServerFeaturesDstu2Test { */ @Test public void testOptionsForNonBasePath1() throws Exception { - HttpOptions httpGet = new HttpOptions("http://localhost:" + ourPort + "/Foo"); + HttpOptions httpGet = new HttpOptions(ourServer.getBaseUrl() + "/Foo"); HttpResponse status = ourClient.execute(httpGet); String responseContent = IOUtils.toString(status.getEntity().getContent(), StandardCharsets.UTF_8); IOUtils.closeQuietly(status.getEntity().getContent()); @@ -90,7 +98,7 @@ public class ServerFeaturesDstu2Test { */ @Test public void testOptionsForNonBasePath2() throws Exception { - HttpOptions httpGet = new HttpOptions("http://localhost:" + ourPort + "/Patient/1"); + HttpOptions httpGet = new HttpOptions(ourServer.getBaseUrl() + "/Patient/1"); HttpResponse status = ourClient.execute(httpGet); String responseContent = IOUtils.toString(status.getEntity().getContent(), StandardCharsets.UTF_8); IOUtils.closeQuietly(status.getEntity().getContent()); @@ -104,7 +112,7 @@ public class ServerFeaturesDstu2Test { */ @Test public void testOptionsForNonBasePath3() throws Exception { - HttpOptions httpGet = new HttpOptions("http://localhost:" + ourPort + "/metadata"); + HttpOptions httpGet = new HttpOptions(ourServer.getBaseUrl() + "/metadata"); HttpResponse status = ourClient.execute(httpGet); String responseContent = IOUtils.toString(status.getEntity().getContent(), StandardCharsets.UTF_8); IOUtils.closeQuietly(status.getEntity().getContent()); @@ -115,7 +123,7 @@ public class ServerFeaturesDstu2Test { @Test public void testOptionsJson() throws Exception { - HttpOptions httpGet = new HttpOptions("http://localhost:" + ourPort + "?_format=json"); + HttpOptions httpGet = new HttpOptions(ourServer.getBaseUrl() + "?_format=json"); HttpResponse status = ourClient.execute(httpGet); String responseContent = IOUtils.toString(status.getEntity().getContent(), StandardCharsets.UTF_8); IOUtils.closeQuietly(status.getEntity().getContent()); @@ -126,7 +134,7 @@ public class ServerFeaturesDstu2Test { @Test public void testHeadJson() throws Exception { - HttpHead httpGet = new HttpHead("http://localhost:" + ourPort + "/Patient/123"); + HttpHead httpGet = new HttpHead(ourServer.getBaseUrl() + "/Patient/123"); HttpResponse status = ourClient.execute(httpGet); assertEquals(null, status.getEntity()); @@ -138,60 +146,45 @@ public class ServerFeaturesDstu2Test { @Test public void testRegisterAndUnregisterResourceProviders() throws Exception { - HttpGet httpGet = new HttpGet("http://localhost:" + ourPort + "/Patient/1"); + HttpGet httpGet = new HttpGet(ourServer.getBaseUrl() + "/Patient/1"); HttpResponse status = ourClient.execute(httpGet); String responseContent = IOUtils.toString(status.getEntity().getContent(), StandardCharsets.UTF_8); IOUtils.closeQuietly(status.getEntity().getContent()); assertEquals(200, status.getStatusLine().getStatusCode()); assertThat(responseContent, containsString("PRP1")); - Collection providers = new ArrayList(ourServlet.getResourceProviders()); - for (IResourceProvider provider : providers) { - ourServlet.unregisterProvider(provider); + Collection originalProviders = new ArrayList<>(ourServer.getRestfulServer().getResourceProviders()); + DummyPatientResourceProvider2 newProvider = new DummyPatientResourceProvider2(); + try { + + // Replace provider + for (IResourceProvider provider : originalProviders) { + ourServer.getRestfulServer().unregisterProvider(provider); + } + ourServer.getRestfulServer().registerProvider(newProvider); + + httpGet = new HttpGet(ourServer.getBaseUrl() + "/Patient/1"); + status = ourClient.execute(httpGet); + responseContent = IOUtils.toString(status.getEntity().getContent(), StandardCharsets.UTF_8); + IOUtils.closeQuietly(status.getEntity().getContent()); + assertEquals(200, status.getStatusLine().getStatusCode()); + assertThat(responseContent, containsString("PRP2")); + + } finally { + + // Restore providers + ourServer.getRestfulServer().unregisterProvider(newProvider); + originalProviders.forEach(p->ourServer.getRestfulServer().registerProvider(p)); + } - - ourServlet.registerProvider(new DummyPatientResourceProvider2()); - - httpGet = new HttpGet("http://localhost:" + ourPort + "/Patient/1"); - status = ourClient.execute(httpGet); - responseContent = IOUtils.toString(status.getEntity().getContent(), StandardCharsets.UTF_8); - IOUtils.closeQuietly(status.getEntity().getContent()); - assertEquals(200, status.getStatusLine().getStatusCode()); - assertThat(responseContent, containsString("PRP2")); - } @AfterAll public static void afterClassClearContext() throws Exception { - JettyUtil.closeServer(ourServer); TestUtil.randomizeLocaleAndTimezone(); } - @BeforeAll - public static void beforeClass() throws Exception { - ourServer = new Server(0); - - DummyPatientResourceProvider patientProvider = new DummyPatientResourceProvider(); - - ServletHandler proxyHandler = new ServletHandler(); - ourServlet = new RestfulServer(ourCtx); - ourServlet.setFhirContext(ourCtx); - ourServlet.setResourceProviders(patientProvider); - ourServlet.setDefaultResponseEncoding(EncodingEnum.XML); - - ServletHolder servletHolder = new ServletHolder(ourServlet); - proxyHandler.addServletWithMapping(servletHolder, "/*"); - ourServer.setHandler(proxyHandler); - JettyUtil.startServer(ourServer); - ourPort = JettyUtil.getPortForStartedServer(ourServer); - - PoolingHttpClientConnectionManager connectionManager = new PoolingHttpClientConnectionManager(5000, TimeUnit.MILLISECONDS); - HttpClientBuilder builder = HttpClientBuilder.create(); - builder.setConnectionManager(connectionManager); - ourClient = builder.build(); - - } public static class DummyPatientResourceProvider implements IResourceProvider { diff --git a/hapi-fhir-structures-dstu2/src/test/java/ca/uhn/fhir/rest/server/ServerSearchDstu2Test.java b/hapi-fhir-structures-dstu2/src/test/java/ca/uhn/fhir/rest/server/ServerSearchDstu2Test.java index 4aa3af0638f..57f4072b97a 100644 --- a/hapi-fhir-structures-dstu2/src/test/java/ca/uhn/fhir/rest/server/ServerSearchDstu2Test.java +++ b/hapi-fhir-structures-dstu2/src/test/java/ca/uhn/fhir/rest/server/ServerSearchDstu2Test.java @@ -4,42 +4,44 @@ import ca.uhn.fhir.context.FhirContext; import ca.uhn.fhir.model.dstu2.resource.Patient; import ca.uhn.fhir.rest.annotation.RequiredParam; import ca.uhn.fhir.rest.annotation.Search; +import ca.uhn.fhir.rest.api.EncodingEnum; import ca.uhn.fhir.rest.param.ReferenceParam; import ca.uhn.fhir.rest.param.StringParam; -import ca.uhn.fhir.test.utilities.JettyUtil; +import ca.uhn.fhir.test.utilities.HttpClientExtension; +import ca.uhn.fhir.test.utilities.server.RestfulServerExtension; import ca.uhn.fhir.util.TestUtil; import ca.uhn.fhir.util.UrlUtil; import org.apache.commons.io.IOUtils; import org.apache.http.HttpResponse; import org.apache.http.client.methods.HttpGet; -import org.apache.http.impl.client.CloseableHttpClient; -import org.apache.http.impl.client.HttpClientBuilder; -import org.apache.http.impl.conn.PoolingHttpClientConnectionManager; -import org.eclipse.jetty.server.Server; -import org.eclipse.jetty.servlet.ServletHandler; -import org.eclipse.jetty.servlet.ServletHolder; import org.hl7.fhir.instance.model.api.IBaseResource; import org.junit.jupiter.api.AfterAll; -import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.RegisterExtension; import java.util.ArrayList; import java.util.List; -import java.util.concurrent.TimeUnit; import static org.junit.jupiter.api.Assertions.assertEquals; public class ServerSearchDstu2Test { - private static CloseableHttpClient ourClient; - private static FhirContext ourCtx = FhirContext.forDstu2(); + private static final FhirContext ourCtx = FhirContext.forDstu2Cached(); private static String ourLastMethod; private static StringParam ourLastRef; private static ReferenceParam ourLastRef2; private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(ServerSearchDstu2Test.class); - private static int ourPort; - private static Server ourServer; + + @RegisterExtension + public static final RestfulServerExtension ourServer = new RestfulServerExtension(ourCtx) + .setDefaultResponseEncoding(EncodingEnum.XML) + .registerProvider(new DummyPatientResourceProvider()) + .withPagingProvider(new FifoMemoryPagingProvider(100)) + .setDefaultPrettyPrint(false); + + @RegisterExtension + public static final HttpClientExtension ourClient = new HttpClientExtension(); @BeforeEach public void before() { @@ -50,7 +52,7 @@ public class ServerSearchDstu2Test { @Test public void testReferenceParamMissingFalse() throws Exception { - HttpGet httpGet = new HttpGet("http://localhost:" + ourPort + "/?param3:missing=false"); + HttpGet httpGet = new HttpGet(ourServer.getBaseUrl() + "/?param3:missing=false"); HttpResponse status = ourClient.execute(httpGet); String responseContent = IOUtils.toString(status.getEntity().getContent()); IOUtils.closeQuietly(status.getEntity().getContent()); @@ -61,7 +63,7 @@ public class ServerSearchDstu2Test { @Test public void testReferenceParamMissingTrue() throws Exception { - HttpGet httpGet = new HttpGet("http://localhost:" + ourPort + "/?param3:missing=true"); + HttpGet httpGet = new HttpGet(ourServer.getBaseUrl() + "/?param3:missing=true"); HttpResponse status = ourClient.execute(httpGet); String responseContent = IOUtils.toString(status.getEntity().getContent()); IOUtils.closeQuietly(status.getEntity().getContent()); @@ -72,7 +74,7 @@ public class ServerSearchDstu2Test { @Test public void testSearchParam1() throws Exception { - HttpGet httpGet = new HttpGet("http://localhost:" + ourPort + "/?param1=param1value"); + HttpGet httpGet = new HttpGet(ourServer.getBaseUrl() + "/?param1=param1value"); HttpResponse status = ourClient.execute(httpGet); String responseContent = IOUtils.toString(status.getEntity().getContent()); IOUtils.closeQuietly(status.getEntity().getContent()); @@ -83,7 +85,7 @@ public class ServerSearchDstu2Test { @Test public void testSearchParam2() throws Exception { - HttpGet httpGet = new HttpGet("http://localhost:" + ourPort + "/?param2=param2value&foo=bar"); + HttpGet httpGet = new HttpGet(ourServer.getBaseUrl() + "/?param2=param2value&foo=bar"); HttpResponse status = ourClient.execute(httpGet); String responseContent = IOUtils.toString(status.getEntity().getContent()); IOUtils.closeQuietly(status.getEntity().getContent()); @@ -94,7 +96,7 @@ public class ServerSearchDstu2Test { @Test public void testSearchParamWithSpace() throws Exception { - HttpGet httpGet = new HttpGet("http://localhost:" + ourPort + "/?param2=param+value&foo=bar"); + HttpGet httpGet = new HttpGet(ourServer.getBaseUrl() + "/?param2=param+value&foo=bar"); HttpResponse status = ourClient.execute(httpGet); String responseContent = IOUtils.toString(status.getEntity().getContent()); IOUtils.closeQuietly(status.getEntity().getContent()); @@ -105,7 +107,7 @@ public class ServerSearchDstu2Test { @Test public void testSearchWithEncodedValue() throws Exception { - HttpGet httpGet = new HttpGet("http://localhost:" + ourPort + "/?param1=" + UrlUtil.escapeUrlParam("Jernelöv")); + HttpGet httpGet = new HttpGet(ourServer.getBaseUrl() + "/?param1=" + UrlUtil.escapeUrlParam("Jernelöv")); HttpResponse status = ourClient.execute(httpGet); String responseContent = IOUtils.toString(status.getEntity().getContent()); IOUtils.closeQuietly(status.getEntity().getContent()); @@ -116,7 +118,7 @@ public class ServerSearchDstu2Test { @Test public void testUnknownSearchParam() throws Exception { - HttpGet httpGet = new HttpGet("http://localhost:" + ourPort + "/?foo=bar"); + HttpGet httpGet = new HttpGet(ourServer.getBaseUrl() + "/?foo=bar"); HttpResponse status = ourClient.execute(httpGet); String responseContent = IOUtils.toString(status.getEntity().getContent()); IOUtils.closeQuietly(status.getEntity().getContent()); @@ -126,33 +128,9 @@ public class ServerSearchDstu2Test { @AfterAll public static void afterClassClearContext() throws Exception { - JettyUtil.closeServer(ourServer); TestUtil.randomizeLocaleAndTimezone(); } - @BeforeAll - public static void beforeClass() throws Exception { - ourServer = new Server(0); - - DummyPatientResourceProvider patientProvider = new DummyPatientResourceProvider(); - - ServletHandler proxyHandler = new ServletHandler(); - RestfulServer servlet = new RestfulServer(ourCtx); - - servlet.setPlainProviders(patientProvider); - ServletHolder servletHolder = new ServletHolder(servlet); - proxyHandler.addServletWithMapping(servletHolder, "/*"); - ourServer.setHandler(proxyHandler); - JettyUtil.startServer(ourServer); - ourPort = JettyUtil.getPortForStartedServer(ourServer); - - PoolingHttpClientConnectionManager connectionManager = new PoolingHttpClientConnectionManager(5000, TimeUnit.MILLISECONDS); - HttpClientBuilder builder = HttpClientBuilder.create(); - builder.setConnectionManager(connectionManager); - ourClient = builder.build(); - - } - public static class DummyPatientResourceProvider { @Search(allowUnknownParams=true) diff --git a/hapi-fhir-structures-dstu2/src/test/java/ca/uhn/fhir/rest/server/SummaryParamDstu2Test.java b/hapi-fhir-structures-dstu2/src/test/java/ca/uhn/fhir/rest/server/SummaryParamDstu2Test.java index bf6cfdb4da7..a1e4e2bac42 100644 --- a/hapi-fhir-structures-dstu2/src/test/java/ca/uhn/fhir/rest/server/SummaryParamDstu2Test.java +++ b/hapi-fhir-structures-dstu2/src/test/java/ca/uhn/fhir/rest/server/SummaryParamDstu2Test.java @@ -13,7 +13,9 @@ import ca.uhn.fhir.rest.annotation.Search; import ca.uhn.fhir.rest.api.Constants; import ca.uhn.fhir.rest.api.EncodingEnum; import ca.uhn.fhir.rest.api.SummaryEnum; +import ca.uhn.fhir.test.utilities.HttpClientExtension; import ca.uhn.fhir.test.utilities.JettyUtil; +import ca.uhn.fhir.test.utilities.server.RestfulServerExtension; import ca.uhn.fhir.util.TestUtil; import org.apache.commons.io.IOUtils; import org.apache.http.HttpResponse; @@ -22,13 +24,14 @@ import org.apache.http.impl.client.CloseableHttpClient; import org.apache.http.impl.client.HttpClientBuilder; import org.apache.http.impl.conn.PoolingHttpClientConnectionManager; import org.eclipse.jetty.server.Server; -import org.eclipse.jetty.servlet.ServletHandler; -import org.eclipse.jetty.servlet.ServletHolder; +import org.eclipse.jetty.ee10.servlet.ServletHandler; +import org.eclipse.jetty.ee10.servlet.ServletHolder; import org.hl7.fhir.instance.model.api.IBaseResource; import org.junit.jupiter.api.AfterAll; import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.RegisterExtension; import java.util.Arrays; import java.util.List; @@ -44,13 +47,20 @@ import static org.junit.jupiter.api.Assertions.assertEquals; public class SummaryParamDstu2Test { private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(SummaryParamDstu2Test.class); - private static CloseableHttpClient ourClient; - private static FhirContext ourCtx = FhirContext.forDstu2(); + private static final FhirContext ourCtx = FhirContext.forDstu2Cached(); private static SummaryEnum ourLastSummary; private static List ourLastSummaryList; - private static int ourPort; - private static Server ourServer; + @RegisterExtension + public static final RestfulServerExtension ourServer = new RestfulServerExtension(ourCtx) + .setDefaultResponseEncoding(EncodingEnum.XML) + .registerProvider(new DummyPatientResourceProvider()) + .registerProvider(new DummyMedicationOrderProvider()) + .withPagingProvider(new FifoMemoryPagingProvider(100)) + .setDefaultPrettyPrint(false); + + @RegisterExtension + public static final HttpClientExtension ourClient = new HttpClientExtension(); @BeforeEach public void before() { @@ -60,7 +70,7 @@ public class SummaryParamDstu2Test { @Test public void testReadSummaryData() throws Exception { - HttpGet httpGet = new HttpGet("http://localhost:" + ourPort + "/Patient/1?_summary=" + SummaryEnum.DATA.getCode()); + HttpGet httpGet = new HttpGet(ourServer.getBaseUrl() + "/Patient/1?_summary=" + SummaryEnum.DATA.getCode()); HttpResponse status = ourClient.execute(httpGet); String responseContent = IOUtils.toString(status.getEntity().getContent()); IOUtils.closeQuietly(status.getEntity().getContent()); @@ -79,7 +89,7 @@ public class SummaryParamDstu2Test { @Test public void testReadSummaryText() throws Exception { - HttpGet httpGet = new HttpGet("http://localhost:" + ourPort + "/Patient/1?_summary=" + SummaryEnum.TEXT.getCode()); + HttpGet httpGet = new HttpGet(ourServer.getBaseUrl() + "/Patient/1?_summary=" + SummaryEnum.TEXT.getCode()); HttpResponse status = ourClient.execute(httpGet); String responseContent = IOUtils.toString(status.getEntity().getContent()); IOUtils.closeQuietly(status.getEntity().getContent()); @@ -96,7 +106,7 @@ public class SummaryParamDstu2Test { @Test public void testReadSummaryTextWithMandatory() throws Exception { - HttpGet httpGet = new HttpGet("http://localhost:" + ourPort + "/MedicationOrder/1?_summary=" + SummaryEnum.TEXT.getCode()); + HttpGet httpGet = new HttpGet(ourServer.getBaseUrl() + "/MedicationOrder/1?_summary=" + SummaryEnum.TEXT.getCode()); HttpResponse status = ourClient.execute(httpGet); String responseContent = IOUtils.toString(status.getEntity().getContent()); IOUtils.closeQuietly(status.getEntity().getContent()); @@ -113,7 +123,7 @@ public class SummaryParamDstu2Test { @Test public void testReadSummaryTrue() throws Exception { - HttpGet httpGet = new HttpGet("http://localhost:" + ourPort + "/Patient/1?_summary=" + SummaryEnum.TRUE.getCode()); + HttpGet httpGet = new HttpGet(ourServer.getBaseUrl() + "/Patient/1?_summary=" + SummaryEnum.TRUE.getCode()); HttpResponse status = ourClient.execute(httpGet); String responseContent = IOUtils.toString(status.getEntity().getContent()); IOUtils.closeQuietly(status.getEntity().getContent()); @@ -131,7 +141,7 @@ public class SummaryParamDstu2Test { @Test public void testSearchSummaryCount() throws Exception { - HttpGet httpGet = new HttpGet("http://localhost:" + ourPort + "/Patient?_pretty=true&_summary=" + SummaryEnum.COUNT.getCode()); + HttpGet httpGet = new HttpGet(ourServer.getBaseUrl() + "/Patient?_pretty=true&_summary=" + SummaryEnum.COUNT.getCode()); HttpResponse status = ourClient.execute(httpGet); String responseContent = IOUtils.toString(status.getEntity().getContent()); IOUtils.closeQuietly(status.getEntity().getContent()); @@ -148,7 +158,7 @@ public class SummaryParamDstu2Test { @Test public void testSearchSummaryData() throws Exception { - HttpGet httpGet = new HttpGet("http://localhost:" + ourPort + "/Patient?_summary=" + SummaryEnum.DATA.getCode()); + HttpGet httpGet = new HttpGet(ourServer.getBaseUrl() + "/Patient?_summary=" + SummaryEnum.DATA.getCode()); HttpResponse status = ourClient.execute(httpGet); String responseContent = IOUtils.toString(status.getEntity().getContent()); IOUtils.closeQuietly(status.getEntity().getContent()); @@ -164,7 +174,7 @@ public class SummaryParamDstu2Test { @Test public void testSearchSummaryFalse() throws Exception { - HttpGet httpGet = new HttpGet("http://localhost:" + ourPort + "/Patient?_summary=false"); + HttpGet httpGet = new HttpGet(ourServer.getBaseUrl() + "/Patient?_summary=false"); HttpResponse status = ourClient.execute(httpGet); String responseContent = IOUtils.toString(status.getEntity().getContent()); IOUtils.closeQuietly(status.getEntity().getContent()); @@ -180,7 +190,7 @@ public class SummaryParamDstu2Test { @Test public void testSearchSummaryText() throws Exception { - HttpGet httpGet = new HttpGet("http://localhost:" + ourPort + "/Patient?_summary=" + SummaryEnum.TEXT.getCode()); + HttpGet httpGet = new HttpGet(ourServer.getBaseUrl() + "/Patient?_summary=" + SummaryEnum.TEXT.getCode()); HttpResponse status = ourClient.execute(httpGet); String responseContent = IOUtils.toString(status.getEntity().getContent()); IOUtils.closeQuietly(status.getEntity().getContent()); @@ -197,7 +207,7 @@ public class SummaryParamDstu2Test { @Test public void testSearchSummaryTextWithMandatory() throws Exception { - HttpGet httpGet = new HttpGet("http://localhost:" + ourPort + "/MedicationOrder?_summary=" + SummaryEnum.TEXT.getCode() + "&_pretty=true"); + HttpGet httpGet = new HttpGet(ourServer.getBaseUrl() + "/MedicationOrder?_summary=" + SummaryEnum.TEXT.getCode() + "&_pretty=true"); HttpResponse status = ourClient.execute(httpGet); String responseContent = IOUtils.toString(status.getEntity().getContent()); IOUtils.closeQuietly(status.getEntity().getContent()); @@ -213,7 +223,7 @@ public class SummaryParamDstu2Test { @Test public void testSearchSummaryTextMulti() throws Exception { - HttpGet httpGet = new HttpGet("http://localhost:" + ourPort + "/Patient?_query=multi&_summary=" + SummaryEnum.TEXT.getCode()); + HttpGet httpGet = new HttpGet(ourServer.getBaseUrl() + "/Patient?_query=multi&_summary=" + SummaryEnum.TEXT.getCode()); HttpResponse status = ourClient.execute(httpGet); String responseContent = IOUtils.toString(status.getEntity().getContent()); IOUtils.closeQuietly(status.getEntity().getContent()); @@ -230,7 +240,7 @@ public class SummaryParamDstu2Test { @Test public void testSearchSummaryTrue() throws Exception { - HttpGet httpGet = new HttpGet("http://localhost:" + ourPort + "/Patient?_summary=" + SummaryEnum.TRUE.getCode()); + HttpGet httpGet = new HttpGet(ourServer.getBaseUrl() + "/Patient?_summary=" + SummaryEnum.TRUE.getCode()); HttpResponse status = ourClient.execute(httpGet); String responseContent = IOUtils.toString(status.getEntity().getContent()); IOUtils.closeQuietly(status.getEntity().getContent()); @@ -246,7 +256,7 @@ public class SummaryParamDstu2Test { @Test public void testSearchSummaryWithTextAndOthers() throws Exception { - HttpGet httpGet = new HttpGet("http://localhost:" + ourPort + "/Patient?_summary=text&_summary=data"); + HttpGet httpGet = new HttpGet(ourServer.getBaseUrl() + "/Patient?_summary=text&_summary=data"); HttpResponse status = ourClient.execute(httpGet); String responseContent = IOUtils.toString(status.getEntity().getContent()); IOUtils.closeQuietly(status.getEntity().getContent()); @@ -280,7 +290,7 @@ public class SummaryParamDstu2Test { } - public static class DummyPatientResourceProvider implements IResourceProvider { + private static class DummyPatientResourceProvider implements IResourceProvider { @Override public Class getResourceType() { @@ -324,31 +334,8 @@ public class SummaryParamDstu2Test { @AfterAll public static void afterClassClearContext() throws Exception { - JettyUtil.closeServer(ourServer); TestUtil.randomizeLocaleAndTimezone(); } - @BeforeAll - public static void beforeClass() throws Exception { - ourServer = new Server(0); - - ServletHandler proxyHandler = new ServletHandler(); - - RestfulServer servlet = new RestfulServer(ourCtx); - servlet.setResourceProviders(new DummyPatientResourceProvider(), new DummyMedicationOrderProvider()); - servlet.setDefaultResponseEncoding(EncodingEnum.XML); - - ServletHolder servletHolder = new ServletHolder(servlet); - proxyHandler.addServletWithMapping(servletHolder, "/*"); - ourServer.setHandler(proxyHandler); - JettyUtil.startServer(ourServer); - ourPort = JettyUtil.getPortForStartedServer(ourServer); - - PoolingHttpClientConnectionManager connectionManager = new PoolingHttpClientConnectionManager(5000, TimeUnit.MILLISECONDS); - HttpClientBuilder builder = HttpClientBuilder.create(); - builder.setConnectionManager(connectionManager); - ourClient = builder.build(); - - } } diff --git a/hapi-fhir-structures-dstu2/src/test/java/ca/uhn/fhir/rest/server/TransactionWithBundleResourceParamTest.java b/hapi-fhir-structures-dstu2/src/test/java/ca/uhn/fhir/rest/server/TransactionWithBundleResourceParamTest.java index f0930d2f5ef..c0ccc0f1191 100644 --- a/hapi-fhir-structures-dstu2/src/test/java/ca/uhn/fhir/rest/server/TransactionWithBundleResourceParamTest.java +++ b/hapi-fhir-structures-dstu2/src/test/java/ca/uhn/fhir/rest/server/TransactionWithBundleResourceParamTest.java @@ -26,7 +26,7 @@ import org.apache.http.impl.client.CloseableHttpClient; import org.apache.http.impl.client.HttpClientBuilder; import org.apache.http.impl.conn.PoolingHttpClientConnectionManager; import org.eclipse.jetty.server.Server; -import org.eclipse.jetty.servlet.ServletHolder; +import org.eclipse.jetty.ee10.servlet.ServletHolder; import org.junit.jupiter.api.AfterAll; import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.BeforeEach; @@ -232,7 +232,7 @@ public class TransactionWithBundleResourceParamTest { RestfulServer server = new RestfulServer(ourCtx); server.setProviders(patientProvider); - org.eclipse.jetty.servlet.ServletContextHandler proxyHandler = new org.eclipse.jetty.servlet.ServletContextHandler(); + org.eclipse.jetty.ee10.servlet.ServletContextHandler proxyHandler = new org.eclipse.jetty.ee10.servlet.ServletContextHandler(); proxyHandler.setContextPath("/"); ServletHolder handler = new ServletHolder(); diff --git a/hapi-fhir-structures-dstu2/src/test/java/ca/uhn/fhir/rest/server/TransactionWithVersionlessBundleResourceParamTest.java b/hapi-fhir-structures-dstu2/src/test/java/ca/uhn/fhir/rest/server/TransactionWithVersionlessBundleResourceParamTest.java index ce1263d5831..771c77140c7 100644 --- a/hapi-fhir-structures-dstu2/src/test/java/ca/uhn/fhir/rest/server/TransactionWithVersionlessBundleResourceParamTest.java +++ b/hapi-fhir-structures-dstu2/src/test/java/ca/uhn/fhir/rest/server/TransactionWithVersionlessBundleResourceParamTest.java @@ -22,7 +22,7 @@ import org.apache.http.impl.client.CloseableHttpClient; import org.apache.http.impl.client.HttpClientBuilder; import org.apache.http.impl.conn.PoolingHttpClientConnectionManager; import org.eclipse.jetty.server.Server; -import org.eclipse.jetty.servlet.ServletHolder; +import org.eclipse.jetty.ee10.servlet.ServletHolder; import org.hl7.fhir.instance.model.api.IBaseBundle; import org.junit.jupiter.api.AfterAll; import org.junit.jupiter.api.BeforeAll; @@ -96,7 +96,7 @@ public class TransactionWithVersionlessBundleResourceParamTest { RestfulServer server = new RestfulServer(ourCtx); server.setProviders(patientProvider); - org.eclipse.jetty.servlet.ServletContextHandler proxyHandler = new org.eclipse.jetty.servlet.ServletContextHandler(); + org.eclipse.jetty.ee10.servlet.ServletContextHandler proxyHandler = new org.eclipse.jetty.ee10.servlet.ServletContextHandler(); proxyHandler.setContextPath("/"); ServletHolder handler = new ServletHolder(); diff --git a/hapi-fhir-structures-dstu2/src/test/java/ca/uhn/fhir/rest/server/UpdateDstu2Test.java b/hapi-fhir-structures-dstu2/src/test/java/ca/uhn/fhir/rest/server/UpdateDstu2Test.java index 3db6f7cd78e..29fc3ad5ead 100644 --- a/hapi-fhir-structures-dstu2/src/test/java/ca/uhn/fhir/rest/server/UpdateDstu2Test.java +++ b/hapi-fhir-structures-dstu2/src/test/java/ca/uhn/fhir/rest/server/UpdateDstu2Test.java @@ -14,8 +14,11 @@ import ca.uhn.fhir.rest.annotation.ResourceParam; import ca.uhn.fhir.rest.annotation.Search; import ca.uhn.fhir.rest.annotation.Update; import ca.uhn.fhir.rest.api.Constants; +import ca.uhn.fhir.rest.api.EncodingEnum; import ca.uhn.fhir.rest.api.MethodOutcome; +import ca.uhn.fhir.test.utilities.HttpClientExtension; import ca.uhn.fhir.test.utilities.JettyUtil; +import ca.uhn.fhir.test.utilities.server.RestfulServerExtension; import ca.uhn.fhir.util.TestUtil; import org.apache.commons.io.IOUtils; import org.apache.http.HttpResponse; @@ -27,12 +30,13 @@ import org.apache.http.impl.client.CloseableHttpClient; import org.apache.http.impl.client.HttpClientBuilder; import org.apache.http.impl.conn.PoolingHttpClientConnectionManager; import org.eclipse.jetty.server.Server; -import org.eclipse.jetty.servlet.ServletHandler; -import org.eclipse.jetty.servlet.ServletHolder; +import org.eclipse.jetty.ee10.servlet.ServletHandler; +import org.eclipse.jetty.ee10.servlet.ServletHolder; import org.junit.jupiter.api.AfterAll; import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.RegisterExtension; import java.util.ArrayList; import java.util.List; @@ -43,17 +47,24 @@ import static org.junit.jupiter.api.Assertions.assertNull; import static org.junit.jupiter.api.Assertions.assertTrue; public class UpdateDstu2Test { - private static CloseableHttpClient ourClient; - private static FhirContext ourCtx = FhirContext.forDstu2(); + private static final FhirContext ourCtx = FhirContext.forDstu2Cached(); private static String ourLastConditionalUrl; private static IdDt ourLastId; private static IdDt ourLastIdParam; private static boolean ourLastRequestWasSearch; private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(UpdateDstu2Test.class); - private static int ourPort; - private static Server ourServer; private static InstantDt ourSetLastUpdated; - + + @RegisterExtension + private RestfulServerExtension ourServer = new RestfulServerExtension(ourCtx) + .setDefaultResponseEncoding(EncodingEnum.XML) + .registerProvider(new PatientProvider()) + .withPagingProvider(new FifoMemoryPagingProvider(100)) + .setDefaultPrettyPrint(false); + + @RegisterExtension + private HttpClientExtension ourClient = new HttpClientExtension(); + @BeforeEach public void before() { @@ -71,7 +82,7 @@ public class UpdateDstu2Test { Patient patient = new Patient(); patient.addIdentifier().setValue("002"); - HttpGet httpGet = new HttpGet("http://localhost:" + ourPort + "/Patient?_pretty=true"); + HttpGet httpGet = new HttpGet(ourServer.getBaseUrl() + "/Patient?_pretty=true"); HttpResponse status = ourClient.execute(httpGet); @@ -93,7 +104,7 @@ public class UpdateDstu2Test { Patient patient = new Patient(); patient.addIdentifier().setValue("002"); - HttpPut httpPost = new HttpPut("http://localhost:" + ourPort + "/Patient?identifier=system%7C001"); + HttpPut httpPost = new HttpPut(ourServer.getBaseUrl() + "/Patient?identifier=system%7C001"); httpPost.setEntity(new StringEntity(ourCtx.newXmlParser().encodeResourceToString(patient), ContentType.create(Constants.CT_FHIR_XML, "UTF-8"))); HttpResponse status = ourClient.execute(httpPost); @@ -105,7 +116,7 @@ public class UpdateDstu2Test { assertEquals(200, status.getStatusLine().getStatusCode()); assertEquals(null, status.getFirstHeader("location")); - assertEquals("http://localhost:" + ourPort + "/Patient/001/_history/002", status.getFirstHeader("content-location").getValue()); + assertEquals(ourServer.getBaseUrl() + "/Patient/001/_history/002", status.getFirstHeader("content-location").getValue()); assertNull(ourLastId.getValue()); assertNull(ourLastIdParam); @@ -121,7 +132,7 @@ public class UpdateDstu2Test { patient.setId("2"); patient.addIdentifier().setValue("002"); - HttpPut httpPost = new HttpPut("http://localhost:" + ourPort + "/Patient/2"); + HttpPut httpPost = new HttpPut(ourServer.getBaseUrl() + "/Patient/2"); httpPost.setEntity(new StringEntity(ourCtx.newXmlParser().encodeResourceToString(patient), ContentType.create(Constants.CT_FHIR_XML, "UTF-8"))); HttpResponse status = ourClient.execute(httpPost); @@ -133,7 +144,7 @@ public class UpdateDstu2Test { assertEquals(200, status.getStatusLine().getStatusCode()); assertEquals(null, status.getFirstHeader("location")); - assertEquals("http://localhost:" + ourPort + "/Patient/001/_history/002", status.getFirstHeader("content-location").getValue()); + assertEquals(ourServer.getBaseUrl() + "/Patient/001/_history/002", status.getFirstHeader("content-location").getValue()); assertEquals("Patient/2", ourLastId.toUnqualified().getValue()); assertEquals("Patient/2", ourLastIdParam.toUnqualified().getValue()); @@ -144,32 +155,10 @@ public class UpdateDstu2Test { @AfterAll public static void afterClassClearContext() throws Exception { - JettyUtil.closeServer(ourServer); TestUtil.randomizeLocaleAndTimezone(); } - @BeforeAll - public static void beforeClass() throws Exception { - ourServer = new Server(0); - - PatientProvider patientProvider = new PatientProvider(); - - ServletHandler proxyHandler = new ServletHandler(); - RestfulServer servlet = new RestfulServer(ourCtx); - servlet.setResourceProviders(patientProvider); - ServletHolder servletHolder = new ServletHolder(servlet); - proxyHandler.addServletWithMapping(servletHolder, "/*"); - ourServer.setHandler(proxyHandler); - JettyUtil.startServer(ourServer); - ourPort = JettyUtil.getPortForStartedServer(ourServer); - - PoolingHttpClientConnectionManager connectionManager = new PoolingHttpClientConnectionManager(5000, TimeUnit.MILLISECONDS); - HttpClientBuilder builder = HttpClientBuilder.create(); - builder.setConnectionManager(connectionManager); - ourClient = builder.build(); - - } public static class PatientProvider implements IResourceProvider { diff --git a/hapi-fhir-structures-dstu2/src/test/java/ca/uhn/fhir/rest/server/ValidateDstu2Test.java b/hapi-fhir-structures-dstu2/src/test/java/ca/uhn/fhir/rest/server/ValidateDstu2Test.java index d3738f289e0..ac9791d0550 100644 --- a/hapi-fhir-structures-dstu2/src/test/java/ca/uhn/fhir/rest/server/ValidateDstu2Test.java +++ b/hapi-fhir-structures-dstu2/src/test/java/ca/uhn/fhir/rest/server/ValidateDstu2Test.java @@ -15,7 +15,9 @@ import ca.uhn.fhir.rest.api.Constants; import ca.uhn.fhir.rest.api.EncodingEnum; import ca.uhn.fhir.rest.api.MethodOutcome; import ca.uhn.fhir.rest.api.ValidationModeEnum; +import ca.uhn.fhir.test.utilities.HttpClientExtension; import ca.uhn.fhir.test.utilities.JettyUtil; +import ca.uhn.fhir.test.utilities.server.RestfulServerExtension; import ca.uhn.fhir.util.TestUtil; import org.apache.commons.io.IOUtils; import org.apache.http.HttpResponse; @@ -26,12 +28,13 @@ import org.apache.http.impl.client.CloseableHttpClient; import org.apache.http.impl.client.HttpClientBuilder; import org.apache.http.impl.conn.PoolingHttpClientConnectionManager; import org.eclipse.jetty.server.Server; -import org.eclipse.jetty.servlet.ServletHandler; -import org.eclipse.jetty.servlet.ServletHolder; +import org.eclipse.jetty.ee10.servlet.ServletHandler; +import org.eclipse.jetty.ee10.servlet.ServletHolder; import org.junit.jupiter.api.AfterAll; import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.RegisterExtension; import java.util.concurrent.TimeUnit; @@ -40,15 +43,23 @@ import static org.hamcrest.Matchers.stringContainsInOrder; import static org.junit.jupiter.api.Assertions.assertEquals; public class ValidateDstu2Test { - private static CloseableHttpClient ourClient; - private static FhirContext ourCtx = FhirContext.forDstu2(); + private static final FhirContext ourCtx = FhirContext.forDstu2Cached(); private static EncodingEnum ourLastEncoding; private static ValidationModeEnum ourLastMode; private static String ourLastProfile; private static String ourLastResourceBody; private static BaseOperationOutcome ourOutcomeToReturn; - private static int ourPort; - private static Server ourServer; + + @RegisterExtension + public static final RestfulServerExtension ourServer = new RestfulServerExtension(ourCtx) + .setDefaultResponseEncoding(EncodingEnum.XML) + .registerProvider(new PatientProvider()) + .registerProvider(new OrganizationProvider()) + .withPagingProvider(new FifoMemoryPagingProvider(100)) + .setDefaultPrettyPrint(false); + + @RegisterExtension + public static final HttpClientExtension ourClient = new HttpClientExtension(); @BeforeEach public void before() { @@ -70,7 +81,7 @@ public class ValidateDstu2Test { Parameters params = new Parameters(); params.addParameter().setName("resource").setResource(patient); - HttpPost httpPost = new HttpPost("http://localhost:" + ourPort + "/Patient/$validate"); + HttpPost httpPost = new HttpPost(ourServer.getBaseUrl() + "/Patient/$validate"); httpPost.setEntity(new StringEntity(ourCtx.newXmlParser().encodeResourceToString(params), ContentType.create(Constants.CT_FHIR_XML, "UTF-8"))); HttpResponse status = ourClient.execute(httpPost); @@ -92,7 +103,7 @@ public class ValidateDstu2Test { Parameters params = new Parameters(); params.addParameter().setName("resource").setResource(org); - HttpPost httpPost = new HttpPost("http://localhost:" + ourPort + "/Organization/$validate"); + HttpPost httpPost = new HttpPost(ourServer.getBaseUrl() + "/Organization/$validate"); httpPost.setEntity(new StringEntity(ourCtx.newJsonParser().encodeResourceToString(params), ContentType.create(Constants.CT_FHIR_JSON, "UTF-8"))); HttpResponse status = ourClient.execute(httpPost); @@ -115,7 +126,7 @@ public class ValidateDstu2Test { params.addParameter().setName("profile").setValue(new StringDt("http://foo")); params.addParameter().setName("mode").setValue(new StringDt(ValidationModeEnum.CREATE.getCode())); - HttpPost httpPost = new HttpPost("http://localhost:" + ourPort + "/Patient/$validate"); + HttpPost httpPost = new HttpPost(ourServer.getBaseUrl() + "/Patient/$validate"); httpPost.setEntity(new StringEntity(ourCtx.newXmlParser().encodeResourceToString(params), ContentType.create(Constants.CT_FHIR_XML, "UTF-8"))); HttpResponse status = ourClient.execute(httpPost); @@ -142,7 +153,7 @@ public class ValidateDstu2Test { Parameters params = new Parameters(); params.addParameter().setName("resource").setResource(patient); - HttpPost httpPost = new HttpPost("http://localhost:" + ourPort + "/Patient/$validate"); + HttpPost httpPost = new HttpPost(ourServer.getBaseUrl() + "/Patient/$validate"); httpPost.setEntity(new StringEntity(ourCtx.newXmlParser().encodeResourceToString(params), ContentType.create(Constants.CT_FHIR_XML, "UTF-8"))); HttpResponse status = ourClient.execute(httpPost); @@ -156,32 +167,9 @@ public class ValidateDstu2Test { @AfterAll public static void afterClassClearContext() throws Exception { - JettyUtil.closeServer(ourServer); TestUtil.randomizeLocaleAndTimezone(); } - @BeforeAll - public static void beforeClass() throws Exception { - ourServer = new Server(0); - - PatientProvider patientProvider = new PatientProvider(); - - ServletHandler proxyHandler = new ServletHandler(); - RestfulServer servlet = new RestfulServer(ourCtx); - servlet.setResourceProviders(patientProvider, new OrganizationProvider()); - ServletHolder servletHolder = new ServletHolder(servlet); - proxyHandler.addServletWithMapping(servletHolder, "/*"); - ourServer.setHandler(proxyHandler); - JettyUtil.startServer(ourServer); - ourPort = JettyUtil.getPortForStartedServer(ourServer); - - PoolingHttpClientConnectionManager connectionManager = new PoolingHttpClientConnectionManager(5000, TimeUnit.MILLISECONDS); - HttpClientBuilder builder = HttpClientBuilder.create(); - builder.setConnectionManager(connectionManager); - ourClient = builder.build(); - - } - public static class OrganizationProvider implements IResourceProvider { @Override diff --git a/hapi-fhir-structures-dstu2/src/test/java/ca/uhn/fhir/rest/server/interceptor/InterceptorUserDataMapDstu2Test.java b/hapi-fhir-structures-dstu2/src/test/java/ca/uhn/fhir/rest/server/interceptor/InterceptorUserDataMapDstu2Test.java index 8209c5c1ee5..7aacbea52d6 100644 --- a/hapi-fhir-structures-dstu2/src/test/java/ca/uhn/fhir/rest/server/interceptor/InterceptorUserDataMapDstu2Test.java +++ b/hapi-fhir-structures-dstu2/src/test/java/ca/uhn/fhir/rest/server/interceptor/InterceptorUserDataMapDstu2Test.java @@ -19,12 +19,17 @@ import ca.uhn.fhir.rest.annotation.OperationParam; import ca.uhn.fhir.rest.annotation.Read; import ca.uhn.fhir.rest.annotation.RequiredParam; import ca.uhn.fhir.rest.annotation.Search; +import ca.uhn.fhir.rest.api.EncodingEnum; import ca.uhn.fhir.rest.api.server.RequestDetails; +import ca.uhn.fhir.rest.server.FifoMemoryPagingProvider; import ca.uhn.fhir.rest.server.IResourceProvider; import ca.uhn.fhir.rest.server.RestfulServer; +import ca.uhn.fhir.rest.server.SummaryParamDstu2Test; import ca.uhn.fhir.rest.server.exceptions.InvalidRequestException; import ca.uhn.fhir.rest.server.servlet.ServletRequestDetails; +import ca.uhn.fhir.test.utilities.HttpClientExtension; import ca.uhn.fhir.test.utilities.JettyUtil; +import ca.uhn.fhir.test.utilities.server.RestfulServerExtension; import ca.uhn.fhir.util.TestUtil; import org.apache.commons.io.IOUtils; import org.apache.http.client.methods.CloseableHttpResponse; @@ -33,12 +38,13 @@ import org.apache.http.impl.client.CloseableHttpClient; import org.apache.http.impl.client.HttpClientBuilder; import org.apache.http.impl.conn.PoolingHttpClientConnectionManager; import org.eclipse.jetty.server.Server; -import org.eclipse.jetty.servlet.ServletHandler; -import org.eclipse.jetty.servlet.ServletHolder; +import org.eclipse.jetty.ee10.servlet.ServletHandler; +import org.eclipse.jetty.ee10.servlet.ServletHolder; import org.junit.jupiter.api.AfterAll; import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.RegisterExtension; import java.nio.charset.StandardCharsets; import java.util.Collections; @@ -60,20 +66,27 @@ import static org.junit.jupiter.api.Assertions.assertSame; public class InterceptorUserDataMapDstu2Test { private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(InterceptorUserDataMapDstu2Test.class); - private static CloseableHttpClient ourClient; - private static FhirContext ourCtx = FhirContext.forDstu2(); - private static int ourPort; - private static Server ourServer; - private static RestfulServer servlet; + private static final FhirContext ourCtx = FhirContext.forDstu2Cached(); private final Object myKey = "KEY"; private final Object myValue = "VALUE"; private Map myMap; private Set myMapCheckMethods; + @RegisterExtension + public static final RestfulServerExtension ourServer = new RestfulServerExtension(ourCtx) + .setDefaultResponseEncoding(EncodingEnum.JSON) + .registerProvider(new DummyPatientResourceProvider()) + .registerProvider(new PlainProvider()) + .withPagingProvider(new FifoMemoryPagingProvider(100)) + .setDefaultPrettyPrint(false); + + @RegisterExtension + public static final HttpClientExtension ourClient = new HttpClientExtension(); + @BeforeEach public void before() { - servlet.getInterceptorService().unregisterAllInterceptors(); - servlet.getInterceptorService().registerInterceptor(new MyInterceptor()); + ourServer.getInterceptorService().unregisterAllInterceptors(); + ourServer.getInterceptorService().registerInterceptor(new MyInterceptor()); } @@ -87,7 +100,7 @@ public class InterceptorUserDataMapDstu2Test { @Test public void testException() throws Exception { - HttpGet httpGet = new HttpGet("http://localhost:" + ourPort + "/Patient?_id=foo"); + HttpGet httpGet = new HttpGet(ourServer.getBaseUrl() + "/Patient?_id=foo"); try (CloseableHttpResponse status = ourClient.execute(httpGet)) { assertEquals(400, status.getStatusLine().getStatusCode()); } @@ -98,7 +111,7 @@ public class InterceptorUserDataMapDstu2Test { @Test public void testRead() throws Exception { - HttpGet httpGet = new HttpGet("http://localhost:" + ourPort + "/Patient/1"); + HttpGet httpGet = new HttpGet(ourServer.getBaseUrl() + "/Patient/1"); try (CloseableHttpResponse status = ourClient.execute(httpGet)) { @@ -269,31 +282,7 @@ public class InterceptorUserDataMapDstu2Test { @AfterAll public static void afterClassClearContext() throws Exception { - JettyUtil.closeServer(ourServer); TestUtil.randomizeLocaleAndTimezone(); } - @BeforeAll - public static void beforeClass() throws Exception { - ourServer = new Server(0); - - ServletHandler proxyHandler = new ServletHandler(); - servlet = new RestfulServer(ourCtx); - - servlet.setResourceProviders(new DummyPatientResourceProvider()); - servlet.setPlainProviders(new PlainProvider()); - - ServletHolder servletHolder = new ServletHolder(servlet); - proxyHandler.addServletWithMapping(servletHolder, "/*"); - ourServer.setHandler(proxyHandler); - JettyUtil.startServer(ourServer); - ourPort = JettyUtil.getPortForStartedServer(ourServer); - - PoolingHttpClientConnectionManager connectionManager = new PoolingHttpClientConnectionManager(5000, TimeUnit.MILLISECONDS); - HttpClientBuilder builder = HttpClientBuilder.create(); - builder.setConnectionManager(connectionManager); - ourClient = builder.build(); - - } - } diff --git a/hapi-fhir-structures-dstu2/src/test/java/ca/uhn/fhir/rest/server/interceptor/LoggingInterceptorDstu2Test.java b/hapi-fhir-structures-dstu2/src/test/java/ca/uhn/fhir/rest/server/interceptor/LoggingInterceptorDstu2Test.java index 41fdd43c6bd..9a64c21a654 100644 --- a/hapi-fhir-structures-dstu2/src/test/java/ca/uhn/fhir/rest/server/interceptor/LoggingInterceptorDstu2Test.java +++ b/hapi-fhir-structures-dstu2/src/test/java/ca/uhn/fhir/rest/server/interceptor/LoggingInterceptorDstu2Test.java @@ -19,11 +19,16 @@ import ca.uhn.fhir.rest.annotation.RequiredParam; import ca.uhn.fhir.rest.annotation.ResourceParam; import ca.uhn.fhir.rest.annotation.Search; import ca.uhn.fhir.rest.api.Constants; +import ca.uhn.fhir.rest.api.EncodingEnum; import ca.uhn.fhir.rest.api.MethodOutcome; +import ca.uhn.fhir.rest.server.FifoMemoryPagingProvider; import ca.uhn.fhir.rest.server.IResourceProvider; import ca.uhn.fhir.rest.server.RestfulServer; +import ca.uhn.fhir.rest.server.SummaryParamDstu2Test; import ca.uhn.fhir.rest.server.exceptions.InvalidRequestException; +import ca.uhn.fhir.test.utilities.HttpClientExtension; import ca.uhn.fhir.test.utilities.JettyUtil; +import ca.uhn.fhir.test.utilities.server.RestfulServerExtension; import ca.uhn.fhir.util.TestUtil; import org.apache.commons.io.IOUtils; import org.apache.http.HttpResponse; @@ -35,13 +40,14 @@ import org.apache.http.impl.client.CloseableHttpClient; import org.apache.http.impl.client.HttpClientBuilder; import org.apache.http.impl.conn.PoolingHttpClientConnectionManager; import org.eclipse.jetty.server.Server; -import org.eclipse.jetty.servlet.ServletHandler; -import org.eclipse.jetty.servlet.ServletHolder; +import org.eclipse.jetty.ee10.servlet.ServletHandler; +import org.eclipse.jetty.ee10.servlet.ServletHolder; import org.hamcrest.core.StringContains; import org.junit.jupiter.api.AfterAll; import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.RegisterExtension; import org.mockito.ArgumentCaptor; import org.slf4j.Logger; @@ -62,17 +68,24 @@ import static org.mockito.Mockito.verify; public class LoggingInterceptorDstu2Test { - private static CloseableHttpClient ourClient; - private static FhirContext ourCtx = FhirContext.forDstu2(); - private static int ourPort; - private static Server ourServer; - private static RestfulServer servlet; + private static final FhirContext ourCtx = FhirContext.forDstu2Cached(); private static int ourDelayMs; private static Exception ourThrowException; + @RegisterExtension + public static final RestfulServerExtension ourServer = new RestfulServerExtension(ourCtx) + .setDefaultResponseEncoding(EncodingEnum.XML) + .registerProvider(new DummyPatientResourceProvider()) + .registerProvider(new PlainProvider()) + .withPagingProvider(new FifoMemoryPagingProvider(100)) + .setDefaultPrettyPrint(false); + + @RegisterExtension + public static final HttpClientExtension ourClient = new HttpClientExtension(); + @BeforeEach public void before() { - servlet.getInterceptorService().unregisterAllInterceptors(); + ourServer.getInterceptorService().unregisterAllInterceptors(); ourThrowException = null; ourDelayMs=0; } @@ -86,30 +99,30 @@ public class LoggingInterceptorDstu2Test { interceptor.setErrorMessageFormat("ERROR - ${requestVerb} ${requestUrl}"); assertEquals("ERROR - ${requestVerb} ${requestUrl}", interceptor.getErrorMessageFormat()); - servlet.getInterceptorService().registerInterceptor(interceptor); + ourServer.getInterceptorService().registerInterceptor(interceptor); Logger logger = mock(Logger.class); interceptor.setLogger(logger); - HttpGet httpGet = new HttpGet("http://localhost:" + ourPort + "/Patient/EX"); + HttpGet httpGet = new HttpGet(ourServer.getBaseUrl() + "/Patient/EX"); HttpResponse status = ourClient.execute(httpGet); IOUtils.closeQuietly(status.getEntity().getContent()); ArgumentCaptor captor = ArgumentCaptor.forClass(String.class); verify(logger, timeout(1000).times(1)).info(captor.capture()); - assertThat(captor.getAllValues().get(0), StringContains.containsString("ERROR - GET http://localhost:" + ourPort + "/Patient/EX")); + assertThat(captor.getAllValues().get(0), StringContains.containsString("ERROR - GET " + ourServer.getBaseUrl() + "/Patient/EX")); } @Test public void testMetadata() throws Exception { LoggingInterceptor interceptor = new LoggingInterceptor(); - servlet.getInterceptorService().registerInterceptor(interceptor); + ourServer.getInterceptorService().registerInterceptor(interceptor); Logger logger = mock(Logger.class); interceptor.setLogger(logger); - HttpGet httpGet = new HttpGet("http://localhost:" + ourPort + "/metadata"); + HttpGet httpGet = new HttpGet(ourServer.getBaseUrl() + "/metadata"); HttpResponse status = ourClient.execute(httpGet); IOUtils.closeQuietly(status.getEntity().getContent()); @@ -125,12 +138,12 @@ public class LoggingInterceptorDstu2Test { LoggingInterceptor interceptor = new LoggingInterceptor(); interceptor.setMessageFormat("${operationType} - ${operationName} - ${idOrResourceName}"); - servlet.getInterceptorService().registerInterceptor(interceptor); + ourServer.getInterceptorService().registerInterceptor(interceptor); Logger logger = mock(Logger.class); interceptor.setLogger(logger); - HttpGet httpGet = new HttpGet("http://localhost:" + ourPort + "/Patient/123/$everything"); + HttpGet httpGet = new HttpGet(ourServer.getBaseUrl() + "/Patient/123/$everything"); HttpResponse status = ourClient.execute(httpGet); IOUtils.closeQuietly(status.getEntity().getContent()); @@ -147,12 +160,12 @@ public class LoggingInterceptorDstu2Test { LoggingInterceptor interceptor = new LoggingInterceptor(); interceptor.setMessageFormat("${operationType} - ${operationName} - ${idOrResourceName} - ${requestBodyFhir}"); - servlet.getInterceptorService().registerInterceptor(interceptor); + ourServer.getInterceptorService().registerInterceptor(interceptor); Logger logger = mock(Logger.class); interceptor.setLogger(logger); - HttpGet httpGet = new HttpGet("http://localhost:" + ourPort + "/Patient/1"); + HttpGet httpGet = new HttpGet(ourServer.getBaseUrl() + "/Patient/1"); HttpResponse status = ourClient.execute(httpGet); IOUtils.closeQuietly(status.getEntity().getContent()); @@ -167,12 +180,12 @@ public class LoggingInterceptorDstu2Test { LoggingInterceptor interceptor = new LoggingInterceptor(); interceptor.setMessageFormat("${requestId}"); - servlet.getInterceptorService().registerInterceptor(interceptor); + ourServer.getInterceptorService().registerInterceptor(interceptor); Logger logger = mock(Logger.class); interceptor.setLogger(logger); - HttpGet httpGet = new HttpGet("http://localhost:" + ourPort + "/Patient/1"); + HttpGet httpGet = new HttpGet(ourServer.getBaseUrl() + "/Patient/1"); HttpResponse status = ourClient.execute(httpGet); IOUtils.closeQuietly(status.getEntity().getContent()); @@ -187,12 +200,12 @@ public class LoggingInterceptorDstu2Test { LoggingInterceptor interceptor = new LoggingInterceptor(); interceptor.setMessageFormat("${operationType} - ${processingTimeMillis}"); - servlet.getInterceptorService().registerInterceptor(interceptor); + ourServer.getInterceptorService().registerInterceptor(interceptor); Logger logger = mock(Logger.class); interceptor.setLogger(logger); - HttpGet httpGet = new HttpGet("http://localhost:" + ourPort + "/Patient/1"); + HttpGet httpGet = new HttpGet(ourServer.getBaseUrl() + "/Patient/1"); HttpResponse status = ourClient.execute(httpGet); IOUtils.closeQuietly(status.getEntity().getContent()); @@ -209,12 +222,12 @@ public class LoggingInterceptorDstu2Test { LoggingInterceptor interceptor = new LoggingInterceptor(); interceptor.setMessageFormat("${processingTimeMillis}"); - servlet.getInterceptorService().registerInterceptor(interceptor); + ourServer.getInterceptorService().registerInterceptor(interceptor); Logger logger = mock(Logger.class); interceptor.setLogger(logger); - HttpGet httpGet = new HttpGet("http://localhost:" + ourPort + "/Patient/1"); + HttpGet httpGet = new HttpGet(ourServer.getBaseUrl() + "/Patient/1"); HttpResponse status = ourClient.execute(httpGet); IOUtils.closeQuietly(status.getEntity().getContent()); @@ -231,12 +244,12 @@ public class LoggingInterceptorDstu2Test { LoggingInterceptor interceptor = new LoggingInterceptor(); interceptor.setMessageFormat("${operationType} - ${operationName} - ${idOrResourceName} - ${requestBodyFhir}"); - servlet.getInterceptorService().registerInterceptor(interceptor); + ourServer.getInterceptorService().registerInterceptor(interceptor); Logger logger = mock(Logger.class); interceptor.setLogger(logger); - HttpGet httpGet = new HttpGet("http://localhost:" + ourPort + "/Patient/1"); + HttpGet httpGet = new HttpGet(ourServer.getBaseUrl() + "/Patient/1"); httpGet.addHeader(Constants.HEADER_CONTENT_TYPE, Constants.CT_FHIR_XML); HttpResponse status = ourClient.execute(httpGet); @@ -252,7 +265,7 @@ public class LoggingInterceptorDstu2Test { LoggingInterceptor interceptor = new LoggingInterceptor(); interceptor.setMessageFormat("${operationType} - ${operationName} - ${idOrResourceName} - ${requestBodyFhir}"); - servlet.getInterceptorService().registerInterceptor(interceptor); + ourServer.getInterceptorService().registerInterceptor(interceptor); Logger logger = mock(Logger.class); interceptor.setLogger(logger); @@ -261,7 +274,7 @@ public class LoggingInterceptorDstu2Test { p.addIdentifier().setValue("VAL"); String input = ourCtx.newXmlParser().encodeResourceToString(p); - HttpPost httpPost = new HttpPost("http://localhost:" + ourPort + "/Patient"); + HttpPost httpPost = new HttpPost(ourServer.getBaseUrl() + "/Patient"); httpPost.setEntity(new StringEntity(input, ContentType.parse(Constants.CT_FHIR_XML + ";charset=utf-8"))); HttpResponse status = ourClient.execute(httpPost); @@ -280,7 +293,7 @@ public class LoggingInterceptorDstu2Test { LoggingInterceptor interceptor = new LoggingInterceptor(); interceptor.setMessageFormat("${operationType} - ${operationName} - ${idOrResourceName} - ${requestBodyFhir}"); interceptor.setErrorMessageFormat("ERROR - ${operationType} - ${operationName} - ${idOrResourceName} - ${requestBodyFhir}"); - servlet.getInterceptorService().registerInterceptor(interceptor); + ourServer.getInterceptorService().registerInterceptor(interceptor); Logger logger = mock(Logger.class); interceptor.setLogger(logger); @@ -291,7 +304,7 @@ public class LoggingInterceptorDstu2Test { ourThrowException = new NullPointerException("FOO"); - HttpPost httpPost = new HttpPost("http://localhost:" + ourPort + "/Patient"); + HttpPost httpPost = new HttpPost(ourServer.getBaseUrl() + "/Patient"); httpPost.setEntity(new StringEntity(input, ContentType.parse(Constants.CT_FHIR_XML + ";charset=utf-8"))); HttpResponse status = ourClient.execute(httpPost); @@ -309,12 +322,12 @@ public class LoggingInterceptorDstu2Test { LoggingInterceptor interceptor = new LoggingInterceptor(); interceptor.setMessageFormat("${operationType} - ${operationName} - ${idOrResourceName}"); - servlet.getInterceptorService().registerInterceptor(interceptor); + ourServer.getInterceptorService().registerInterceptor(interceptor); Logger logger = mock(Logger.class); interceptor.setLogger(logger); - HttpGet httpGet = new HttpGet("http://localhost:" + ourPort + "/$everything"); + HttpGet httpGet = new HttpGet(ourServer.getBaseUrl() + "/$everything"); HttpResponse status = ourClient.execute(httpGet); IOUtils.closeQuietly(status.getEntity().getContent()); @@ -330,12 +343,12 @@ public class LoggingInterceptorDstu2Test { LoggingInterceptor interceptor = new LoggingInterceptor(); interceptor.setMessageFormat("${operationType} - ${operationName} - ${idOrResourceName}"); - servlet.getInterceptorService().registerInterceptor(interceptor); + ourServer.getInterceptorService().registerInterceptor(interceptor); Logger logger = mock(Logger.class); interceptor.setLogger(logger); - HttpGet httpGet = new HttpGet("http://localhost:" + ourPort + "/Patient/$everything"); + HttpGet httpGet = new HttpGet(ourServer.getBaseUrl() + "/Patient/$everything"); HttpResponse status = ourClient.execute(httpGet); IOUtils.closeQuietly(status.getEntity().getContent()); @@ -350,12 +363,12 @@ public class LoggingInterceptorDstu2Test { public void testRead() throws Exception { LoggingInterceptor interceptor = new LoggingInterceptor(); - servlet.getInterceptorService().registerInterceptor(interceptor); + ourServer.getInterceptorService().registerInterceptor(interceptor); Logger logger = mock(Logger.class); interceptor.setLogger(logger); - HttpGet httpGet = new HttpGet("http://localhost:" + ourPort + "/Patient/1"); + HttpGet httpGet = new HttpGet(ourServer.getBaseUrl() + "/Patient/1"); HttpResponse status = ourClient.execute(httpGet); IOUtils.closeQuietly(status.getEntity().getContent()); @@ -371,12 +384,12 @@ public class LoggingInterceptorDstu2Test { LoggingInterceptor interceptor = new LoggingInterceptor(); interceptor.setMessageFormat("${operationType} - ${idOrResourceName} - ${requestParameters}"); - servlet.getInterceptorService().registerInterceptor(interceptor); + ourServer.getInterceptorService().registerInterceptor(interceptor); Logger logger = mock(Logger.class); interceptor.setLogger(logger); - HttpGet httpGet = new HttpGet("http://localhost:" + ourPort + "/Patient?_id=1"); + HttpGet httpGet = new HttpGet(ourServer.getBaseUrl() + "/Patient?_id=1"); HttpResponse status = ourClient.execute(httpGet); IOUtils.closeQuietly(status.getEntity().getContent()); @@ -389,33 +402,9 @@ public class LoggingInterceptorDstu2Test { @AfterAll public static void afterClassClearContext() throws Exception { - JettyUtil.closeServer(ourServer); TestUtil.randomizeLocaleAndTimezone(); } - @BeforeAll - public static void beforeClass() throws Exception { - ourServer = new Server(0); - - ServletHandler proxyHandler = new ServletHandler(); - servlet = new RestfulServer(ourCtx); - - servlet.setResourceProviders(new DummyPatientResourceProvider()); - servlet.setPlainProviders(new PlainProvider()); - - ServletHolder servletHolder = new ServletHolder(servlet); - proxyHandler.addServletWithMapping(servletHolder, "/*"); - ourServer.setHandler(proxyHandler); - JettyUtil.startServer(ourServer); - ourPort = JettyUtil.getPortForStartedServer(ourServer); - - PoolingHttpClientConnectionManager connectionManager = new PoolingHttpClientConnectionManager(5000, TimeUnit.MILLISECONDS); - HttpClientBuilder builder = HttpClientBuilder.create(); - builder.setConnectionManager(connectionManager); - ourClient = builder.build(); - - } - /** * Created by dsotnikov on 2/25/2014. */ @@ -436,7 +425,7 @@ public class LoggingInterceptorDstu2Test { } public Map getIdToPatient() { - Map idToPatient = new HashMap(); + Map idToPatient = new HashMap<>(); { Patient patient = createPatient1(); idToPatient.put("1", patient); @@ -538,4 +527,5 @@ public class LoggingInterceptorDstu2Test { } + } diff --git a/hapi-fhir-structures-dstu2/src/test/java/ca/uhn/fhir/rest/server/interceptor/ServerActionInterceptorTest.java b/hapi-fhir-structures-dstu2/src/test/java/ca/uhn/fhir/rest/server/interceptor/ServerActionInterceptorTest.java index 48b2924cf7f..1fe3ded49cf 100644 --- a/hapi-fhir-structures-dstu2/src/test/java/ca/uhn/fhir/rest/server/interceptor/ServerActionInterceptorTest.java +++ b/hapi-fhir-structures-dstu2/src/test/java/ca/uhn/fhir/rest/server/interceptor/ServerActionInterceptorTest.java @@ -1,7 +1,6 @@ package ca.uhn.fhir.rest.server.interceptor; import ca.uhn.fhir.context.FhirContext; -import ca.uhn.fhir.context.api.BundleInclusionRule; import ca.uhn.fhir.model.dstu2.resource.Observation; import ca.uhn.fhir.model.dstu2.resource.Patient; import ca.uhn.fhir.model.primitive.IdDt; @@ -16,61 +15,79 @@ import ca.uhn.fhir.rest.api.MethodOutcome; import ca.uhn.fhir.rest.api.RestOperationTypeEnum; import ca.uhn.fhir.rest.api.server.RequestDetails; import ca.uhn.fhir.rest.api.server.ResponseDetails; -import ca.uhn.fhir.rest.client.api.IGenericClient; -import ca.uhn.fhir.rest.client.api.ServerValidationModeEnum; +import ca.uhn.fhir.rest.server.FifoMemoryPagingProvider; import ca.uhn.fhir.rest.server.IResourceProvider; -import ca.uhn.fhir.rest.server.RestfulServer; -import ca.uhn.fhir.test.utilities.JettyUtil; +import ca.uhn.fhir.test.utilities.HttpClientExtension; +import ca.uhn.fhir.test.utilities.server.RestfulServerExtension; import ca.uhn.fhir.util.TestUtil; import org.apache.commons.io.IOUtils; import org.apache.http.client.methods.CloseableHttpResponse; import org.apache.http.client.methods.HttpGet; -import org.apache.http.impl.client.CloseableHttpClient; -import org.apache.http.impl.client.HttpClientBuilder; -import org.apache.http.impl.conn.PoolingHttpClientConnectionManager; -import org.eclipse.jetty.server.Server; -import org.eclipse.jetty.servlet.ServletHandler; -import org.eclipse.jetty.servlet.ServletHolder; import org.hl7.fhir.instance.model.api.IBaseResource; import org.junit.jupiter.api.AfterAll; -import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.RegisterExtension; import org.mockito.ArgumentCaptor; -import javax.servlet.http.HttpServletRequest; -import javax.servlet.http.HttpServletResponse; +import jakarta.servlet.http.HttpServletRequest; +import jakarta.servlet.http.HttpServletResponse; import java.util.Collections; import java.util.List; -import java.util.concurrent.TimeUnit; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.eq; import static org.mockito.Mockito.mock; -import static org.mockito.Mockito.reset; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; public class ServerActionInterceptorTest { - private static CloseableHttpClient ourClient; - private static FhirContext ourCtx = FhirContext.forDstu2(); - private static int ourPort; - private static Server ourServer; - private static IServerInterceptor ourInterceptor; - private static IGenericClient ourFhirClient; + private static final FhirContext ourCtx = FhirContext.forDstu2Cached(); + private IServerInterceptor myInterceptor; + + @RegisterExtension + public static final RestfulServerExtension ourServer = new RestfulServerExtension(ourCtx) + .setDefaultResponseEncoding(EncodingEnum.XML) + .registerInterceptor(new ResponseHighlighterInterceptor()) + .registerProvider(new DummyPatientResourceProvider()) + .registerProvider(new DummyObservationResourceProvider()) + .registerProvider(new PlainProvider()) + .withPagingProvider(new FifoMemoryPagingProvider(100)) + .setDefaultPrettyPrint(false); + + @RegisterExtension + public static final HttpClientExtension ourClient = new HttpClientExtension(); + + @BeforeEach + public void before() { + myInterceptor = mock(InterceptorAdapter.class); + ourServer.registerInterceptor(myInterceptor); + + when(myInterceptor.incomingRequestPreProcessed(any(HttpServletRequest.class), any(HttpServletResponse.class))).thenReturn(true); + when(myInterceptor.incomingRequestPostProcessed(any(RequestDetails.class), any(HttpServletRequest.class), any(HttpServletResponse.class))).thenReturn(true); + when(myInterceptor.outgoingResponse(any(RequestDetails.class), any(HttpServletRequest.class), any(HttpServletResponse.class))).thenReturn(true); + when(myInterceptor.outgoingResponse(any(RequestDetails.class), any(IBaseResource.class), any(HttpServletRequest.class), any(HttpServletResponse.class))).thenReturn(true); + when(myInterceptor.outgoingResponse(any(RequestDetails.class), any(ResponseDetails.class), any(HttpServletRequest.class), any(HttpServletResponse.class))).thenReturn(true); + } + + @AfterEach + public void after() { + ourServer.unregisterInterceptor(myInterceptor); + } @Test public void testRead() throws Exception { - HttpGet httpGet = new HttpGet("http://localhost:" + ourPort + "/Patient/123"); + HttpGet httpGet = new HttpGet(ourServer.getBaseUrl() + "/Patient/123"); CloseableHttpResponse status = ourClient.execute(httpGet); IOUtils.closeQuietly(status.getEntity().getContent()); assertEquals(200, status.getStatusLine().getStatusCode()); ArgumentCaptor detailsCapt = ArgumentCaptor.forClass(RequestDetails.class); - verify(ourInterceptor).incomingRequestPreHandled(eq(RestOperationTypeEnum.READ), detailsCapt.capture()); + verify(myInterceptor).incomingRequestPreHandled(eq(RestOperationTypeEnum.READ), detailsCapt.capture()); RequestDetails details = detailsCapt.getValue(); assertEquals("Patient/123", details.getId().getValue()); @@ -78,14 +95,14 @@ public class ServerActionInterceptorTest { @Test public void testVRead() throws Exception { - HttpGet httpGet = new HttpGet("http://localhost:" + ourPort + "/Patient/123/_history/456"); + HttpGet httpGet = new HttpGet(ourServer.getBaseUrl() + "/Patient/123/_history/456"); CloseableHttpResponse status = ourClient.execute(httpGet); IOUtils.closeQuietly(status.getEntity().getContent()); assertEquals(200, status.getStatusLine().getStatusCode()); ArgumentCaptor detailsCapt = ArgumentCaptor.forClass(RequestDetails.class); - verify(ourInterceptor).incomingRequestPreHandled(eq(RestOperationTypeEnum.VREAD), detailsCapt.capture()); + verify(myInterceptor).incomingRequestPreHandled(eq(RestOperationTypeEnum.VREAD), detailsCapt.capture()); RequestDetails details = detailsCapt.getValue(); assertEquals("Patient/123/_history/456", details.getId().getValue()); @@ -95,10 +112,10 @@ public class ServerActionInterceptorTest { public void testCreate() throws Exception { Patient patient = new Patient(); patient.addName().addFamily("FAMILY"); - ourFhirClient.create().resource(patient).execute(); + ourServer.getFhirClient().create().resource(patient).execute(); ArgumentCaptor detailsCapt = ArgumentCaptor.forClass(RequestDetails.class); - verify(ourInterceptor).incomingRequestPreHandled(eq(RestOperationTypeEnum.CREATE), detailsCapt.capture()); + verify(myInterceptor).incomingRequestPreHandled(eq(RestOperationTypeEnum.CREATE), detailsCapt.capture()); RequestDetails details = detailsCapt.getValue(); assertEquals("Patient", details.getResourceName()); @@ -110,10 +127,10 @@ public class ServerActionInterceptorTest { public void testCreateWhereMethodHasNoResourceParam() throws Exception { Observation observation = new Observation(); observation.getCode().setText("OBSCODE"); - ourFhirClient.create().resource(observation).execute(); + ourServer.getFhirClient().create().resource(observation).execute(); ArgumentCaptor detailsCapt = ArgumentCaptor.forClass(RequestDetails.class); - verify(ourInterceptor).incomingRequestPreHandled(eq(RestOperationTypeEnum.CREATE), detailsCapt.capture()); + verify(myInterceptor).incomingRequestPreHandled(eq(RestOperationTypeEnum.CREATE), detailsCapt.capture()); RequestDetails details = detailsCapt.getValue(); assertEquals("Observation", details.getResourceName()); @@ -126,10 +143,10 @@ public class ServerActionInterceptorTest { Patient patient = new Patient(); patient.addName().addFamily("FAMILY"); patient.setId("Patient/123"); - ourFhirClient.update().resource(patient).execute(); + ourServer.getFhirClient().update().resource(patient).execute(); ArgumentCaptor detailsCapt = ArgumentCaptor.forClass(RequestDetails.class); - verify(ourInterceptor).incomingRequestPreHandled(eq(RestOperationTypeEnum.UPDATE), detailsCapt.capture()); + verify(myInterceptor).incomingRequestPreHandled(eq(RestOperationTypeEnum.UPDATE), detailsCapt.capture()); RequestDetails details = detailsCapt.getValue(); assertEquals("Patient", details.getResourceName()); @@ -141,91 +158,48 @@ public class ServerActionInterceptorTest { @Test public void testHistorySystem() throws Exception { - HttpGet httpGet = new HttpGet("http://localhost:" + ourPort + "/_history"); + HttpGet httpGet = new HttpGet(ourServer.getBaseUrl() + "/_history"); CloseableHttpResponse status = ourClient.execute(httpGet); IOUtils.closeQuietly(status.getEntity().getContent()); assertEquals(200, status.getStatusLine().getStatusCode()); ArgumentCaptor detailsCapt = ArgumentCaptor.forClass(RequestDetails.class); - verify(ourInterceptor).incomingRequestPreHandled(eq(RestOperationTypeEnum.HISTORY_SYSTEM), detailsCapt.capture()); + verify(myInterceptor).incomingRequestPreHandled(eq(RestOperationTypeEnum.HISTORY_SYSTEM), detailsCapt.capture()); } @Test public void testHistoryType() throws Exception { - HttpGet httpGet = new HttpGet("http://localhost:" + ourPort + "/Patient/_history"); + HttpGet httpGet = new HttpGet(ourServer.getBaseUrl() + "/Patient/_history"); CloseableHttpResponse status = ourClient.execute(httpGet); IOUtils.closeQuietly(status.getEntity().getContent()); assertEquals(200, status.getStatusLine().getStatusCode()); ArgumentCaptor detailsCapt = ArgumentCaptor.forClass(RequestDetails.class); - verify(ourInterceptor).incomingRequestPreHandled(eq(RestOperationTypeEnum.HISTORY_TYPE), detailsCapt.capture()); + verify(myInterceptor).incomingRequestPreHandled(eq(RestOperationTypeEnum.HISTORY_TYPE), detailsCapt.capture()); assertEquals("Patient", detailsCapt.getValue().getResourceName()); } @Test public void testHistoryInstance() throws Exception { - HttpGet httpGet = new HttpGet("http://localhost:" + ourPort + "/Patient/123/_history"); + HttpGet httpGet = new HttpGet(ourServer.getBaseUrl() + "/Patient/123/_history"); CloseableHttpResponse status = ourClient.execute(httpGet); IOUtils.closeQuietly(status.getEntity().getContent()); assertEquals(200, status.getStatusLine().getStatusCode()); ArgumentCaptor detailsCapt = ArgumentCaptor.forClass(RequestDetails.class); - verify(ourInterceptor).incomingRequestPreHandled(eq(RestOperationTypeEnum.HISTORY_INSTANCE), detailsCapt.capture()); + verify(myInterceptor).incomingRequestPreHandled(eq(RestOperationTypeEnum.HISTORY_INSTANCE), detailsCapt.capture()); assertEquals("Patient", detailsCapt.getValue().getResourceName()); assertEquals("Patient/123", detailsCapt.getValue().getId().getValue()); } - @BeforeEach - public void before() { - reset(ourInterceptor); - - when(ourInterceptor.incomingRequestPreProcessed(any(HttpServletRequest.class), any(HttpServletResponse.class))).thenReturn(true); - when(ourInterceptor.incomingRequestPostProcessed(any(RequestDetails.class), any(HttpServletRequest.class), any(HttpServletResponse.class))).thenReturn(true); - when(ourInterceptor.outgoingResponse(any(RequestDetails.class), any(HttpServletRequest.class), any(HttpServletResponse.class))).thenReturn(true); - when(ourInterceptor.outgoingResponse(any(RequestDetails.class), any(IBaseResource.class), any(HttpServletRequest.class), any(HttpServletResponse.class))).thenReturn(true); - when(ourInterceptor.outgoingResponse(any(RequestDetails.class), any(ResponseDetails.class), any(HttpServletRequest.class), any(HttpServletResponse.class))).thenReturn(true); - } - @AfterAll public static void afterClassClearContext() throws Exception { - JettyUtil.closeServer(ourServer); TestUtil.randomizeLocaleAndTimezone(); } - @BeforeAll - public static void beforeClass() throws Exception { - ourServer = new Server(0); - - ServletHandler proxyHandler = new ServletHandler(); - RestfulServer servlet = new RestfulServer(ourCtx); - servlet.registerInterceptor(new ResponseHighlighterInterceptor()); - servlet.setResourceProviders(new DummyPatientResourceProvider(), new DummyObservationResourceProvider()); - servlet.setPlainProviders(new PlainProvider()); - servlet.setBundleInclusionRule(BundleInclusionRule.BASED_ON_RESOURCE_PRESENCE); - servlet.setDefaultResponseEncoding(EncodingEnum.XML); - - ServletHolder servletHolder = new ServletHolder(servlet); - proxyHandler.addServletWithMapping(servletHolder, "/*"); - ourServer.setHandler(proxyHandler); - JettyUtil.startServer(ourServer); - ourPort = JettyUtil.getPortForStartedServer(ourServer); - - PoolingHttpClientConnectionManager connectionManager = new PoolingHttpClientConnectionManager(5000, TimeUnit.MILLISECONDS); - HttpClientBuilder builder = HttpClientBuilder.create(); - builder.setConnectionManager(connectionManager); - ourClient = builder.build(); - - ourInterceptor = mock(InterceptorAdapter.class); - servlet.registerInterceptor(ourInterceptor); - - ourCtx.getRestfulClientFactory().setSocketTimeout(240 * 1000); - ourCtx.getRestfulClientFactory().setServerValidationMode(ServerValidationModeEnum.NEVER); - ourFhirClient = ourCtx.newRestfulGenericClient("http://localhost:" + ourPort); - - } public static class PlainProvider { diff --git a/hapi-fhir-structures-dstu2/src/test/java/ca/uhn/fhir/util/FhirTerserDstu2Test.java b/hapi-fhir-structures-dstu2/src/test/java/ca/uhn/fhir/util/FhirTerserDstu2Test.java index b7e149e007f..64bff5e51fa 100644 --- a/hapi-fhir-structures-dstu2/src/test/java/ca/uhn/fhir/util/FhirTerserDstu2Test.java +++ b/hapi-fhir-structures-dstu2/src/test/java/ca/uhn/fhir/util/FhirTerserDstu2Test.java @@ -863,17 +863,19 @@ public class FhirTerserDstu2Test { /** * See http://stackoverflow.com/questions/182636/how-to-determine-the-class-of-a-generic-type */ + @SuppressWarnings({"UnnecessaryLocalVariable", "unchecked"}) private static Class> getListClass(Class theClass) { - return new ClassGetter>() { - }.get(); + Class listClass = List.class; + return listClass; } /** * See http://stackoverflow.com/questions/182636/how-to-determine-the-class-of-a-generic-type */ + @SuppressWarnings({"UnnecessaryLocalVariable", "unchecked"}) private static Class>> getListClass2() { - return new ClassGetter>>() { - }.get(); + Class listClass = List.class; + return listClass; } /** diff --git a/hapi-fhir-structures-dstu3/pom.xml b/hapi-fhir-structures-dstu3/pom.xml index 631fe4f5b37..f4cfda0fd24 100644 --- a/hapi-fhir-structures-dstu3/pom.xml +++ b/hapi-fhir-structures-dstu3/pom.xml @@ -5,7 +5,7 @@ ca.uhn.hapi.fhir hapi-deployable-pom - 6.9.7-SNAPSHOT + 6.11.7-SNAPSHOT ../hapi-deployable-pom/pom.xml @@ -155,9 +155,9 @@ as much as possible. See #283. --> - javax.servlet - javax.servlet-api - 3.1.0 + jakarta.servlet + jakarta.servlet-api + 6.0.0 true @@ -200,36 +200,6 @@ xmlunit-core test - - org.eclipse.jetty - jetty-servlets - test - - - org.eclipse.jetty - jetty-servlet - test - - - org.eclipse.jetty - jetty-server - test - - - org.eclipse.jetty - jetty-util - test - - - org.eclipse.jetty - jetty-webapp - test - - - org.eclipse.jetty - jetty-http - test - ch.qos.logback logback-classic @@ -241,7 +211,7 @@ thymeleaf test - + ca.uhn.hapi.fhir hapi-fhir-test-utilities ${project.version} @@ -250,18 +220,18 @@ - com.helger - ph-schematron + com.helger.schematron + ph-schematron-api test - com.helger - ph-commons + com.helger.schematron + ph-schematron-xslt test - javax.activation - javax.activation-api + jakarta.activation + jakarta.activation-api test diff --git a/hapi-fhir-structures-dstu3/src/main/java/org/hl7/fhir/dstu3/hapi/fluentpath/FhirPathDstu3.java b/hapi-fhir-structures-dstu3/src/main/java/org/hl7/fhir/dstu3/hapi/fluentpath/FhirPathDstu3.java index 076481df481..e7dfd0d1c9b 100644 --- a/hapi-fhir-structures-dstu3/src/main/java/org/hl7/fhir/dstu3/hapi/fluentpath/FhirPathDstu3.java +++ b/hapi-fhir-structures-dstu3/src/main/java/org/hl7/fhir/dstu3/hapi/fluentpath/FhirPathDstu3.java @@ -6,6 +6,7 @@ import ca.uhn.fhir.fhirpath.FhirPathExecutionException; import ca.uhn.fhir.fhirpath.IFhirPath; import ca.uhn.fhir.fhirpath.IFhirPathEvaluationContext; import ca.uhn.fhir.i18n.Msg; +import jakarta.annotation.Nonnull; import org.hl7.fhir.dstu3.hapi.ctx.HapiWorkerContext; import org.hl7.fhir.dstu3.model.Base; import org.hl7.fhir.dstu3.model.ExpressionNode; @@ -19,7 +20,6 @@ import org.hl7.fhir.instance.model.api.IBase; import java.util.List; import java.util.Optional; -import javax.annotation.Nonnull; public class FhirPathDstu3 implements IFhirPath { diff --git a/hapi-fhir-structures-dstu3/src/main/java/org/hl7/fhir/dstu3/hapi/rest/server/Dstu3BundleFactory.java b/hapi-fhir-structures-dstu3/src/main/java/org/hl7/fhir/dstu3/hapi/rest/server/Dstu3BundleFactory.java index 6608644db94..af435eefe5f 100644 --- a/hapi-fhir-structures-dstu3/src/main/java/org/hl7/fhir/dstu3/hapi/rest/server/Dstu3BundleFactory.java +++ b/hapi-fhir-structures-dstu3/src/main/java/org/hl7/fhir/dstu3/hapi/rest/server/Dstu3BundleFactory.java @@ -31,6 +31,7 @@ import ca.uhn.fhir.rest.api.Constants; import ca.uhn.fhir.rest.api.IVersionSpecificBundleFactory; import ca.uhn.fhir.rest.server.RestfulServerUtils; import ca.uhn.fhir.util.ResourceReferenceInfo; +import jakarta.annotation.Nonnull; import org.hl7.fhir.dstu3.model.Bundle; import org.hl7.fhir.dstu3.model.Bundle.BundleEntryComponent; import org.hl7.fhir.dstu3.model.Bundle.BundleLinkComponent; @@ -49,7 +50,6 @@ import java.util.HashSet; import java.util.List; import java.util.Set; import java.util.UUID; -import javax.annotation.Nonnull; import static org.apache.commons.lang3.StringUtils.isNotBlank; diff --git a/hapi-fhir-structures-dstu3/src/main/java/org/hl7/fhir/dstu3/hapi/rest/server/ServerCapabilityStatementProvider.java b/hapi-fhir-structures-dstu3/src/main/java/org/hl7/fhir/dstu3/hapi/rest/server/ServerCapabilityStatementProvider.java index eaf2a98c582..ca200ea7982 100644 --- a/hapi-fhir-structures-dstu3/src/main/java/org/hl7/fhir/dstu3/hapi/rest/server/ServerCapabilityStatementProvider.java +++ b/hapi-fhir-structures-dstu3/src/main/java/org/hl7/fhir/dstu3/hapi/rest/server/ServerCapabilityStatementProvider.java @@ -30,27 +30,59 @@ import ca.uhn.fhir.rest.annotation.Metadata; import ca.uhn.fhir.rest.annotation.Read; import ca.uhn.fhir.rest.api.Constants; import ca.uhn.fhir.rest.api.server.RequestDetails; +import ca.uhn.fhir.rest.server.Bindings; +import ca.uhn.fhir.rest.server.IServerConformanceProvider; +import ca.uhn.fhir.rest.server.ResourceBinding; +import ca.uhn.fhir.rest.server.RestfulServer; +import ca.uhn.fhir.rest.server.RestfulServerConfiguration; import ca.uhn.fhir.rest.server.exceptions.ResourceNotFoundException; +import ca.uhn.fhir.rest.server.method.BaseMethodBinding; +import ca.uhn.fhir.rest.server.method.IParameter; +import ca.uhn.fhir.rest.server.method.OperationMethodBinding; import ca.uhn.fhir.rest.server.method.OperationMethodBinding.ReturnType; +import ca.uhn.fhir.rest.server.method.OperationParameter; +import ca.uhn.fhir.rest.server.method.SearchMethodBinding; import ca.uhn.fhir.rest.server.method.SearchParameter; +import ca.uhn.fhir.rest.server.provider.ProviderConstants; import ca.uhn.fhir.rest.server.util.BaseServerCapabilityStatementProvider; -import ca.uhn.fhir.rest.server.*; -import ca.uhn.fhir.rest.server.method.*; +import jakarta.servlet.ServletContext; +import jakarta.servlet.http.HttpServletRequest; import org.apache.commons.lang3.StringUtils; +import org.hl7.fhir.dstu3.model.CapabilityStatement; +import org.hl7.fhir.dstu3.model.CapabilityStatement.CapabilityStatementKind; +import org.hl7.fhir.dstu3.model.CapabilityStatement.CapabilityStatementRestComponent; +import org.hl7.fhir.dstu3.model.CapabilityStatement.CapabilityStatementRestResourceComponent; +import org.hl7.fhir.dstu3.model.CapabilityStatement.CapabilityStatementRestResourceSearchParamComponent; +import org.hl7.fhir.dstu3.model.CapabilityStatement.ConditionalDeleteStatus; +import org.hl7.fhir.dstu3.model.CapabilityStatement.ResourceInteractionComponent; +import org.hl7.fhir.dstu3.model.CapabilityStatement.RestfulCapabilityMode; +import org.hl7.fhir.dstu3.model.CapabilityStatement.SystemRestfulInteraction; +import org.hl7.fhir.dstu3.model.CapabilityStatement.TypeRestfulInteraction; +import org.hl7.fhir.dstu3.model.CapabilityStatement.UnknownContentCode; +import org.hl7.fhir.dstu3.model.DateTimeType; import org.hl7.fhir.dstu3.model.Enumerations.PublicationStatus; +import org.hl7.fhir.dstu3.model.IdType; +import org.hl7.fhir.dstu3.model.OperationDefinition; import org.hl7.fhir.dstu3.model.OperationDefinition.OperationDefinitionParameterComponent; import org.hl7.fhir.dstu3.model.OperationDefinition.OperationKind; -import org.hl7.fhir.dstu3.model.*; -import org.hl7.fhir.dstu3.model.CapabilityStatement.*; import org.hl7.fhir.dstu3.model.OperationDefinition.OperationParameterUse; +import org.hl7.fhir.dstu3.model.Reference; +import org.hl7.fhir.dstu3.model.ResourceType; import org.hl7.fhir.exceptions.FHIRException; import org.hl7.fhir.instance.model.api.IBaseResource; import org.hl7.fhir.instance.model.api.IPrimitiveType; +import java.util.ArrayList; +import java.util.Collections; +import java.util.Comparator; +import java.util.Date; +import java.util.HashSet; +import java.util.List; +import java.util.Map; import java.util.Map.Entry; -import java.util.*; -import javax.servlet.ServletContext; -import javax.servlet.http.HttpServletRequest; +import java.util.Set; +import java.util.TreeMap; +import java.util.TreeSet; import static org.apache.commons.lang3.StringUtils.isBlank; import static org.apache.commons.lang3.StringUtils.isNotBlank; @@ -384,6 +416,8 @@ public class ServerCapabilityStatementProvider extends BaseServerCapabilityState } } + maybeAddBulkDataDeclarationToConformingToIg(retVal, serverConfiguration.getServerBindings()); + return retVal; } @@ -682,4 +716,17 @@ public class ServerCapabilityStatementProvider extends BaseServerCapabilityState } }); } + + private void maybeAddBulkDataDeclarationToConformingToIg( + CapabilityStatement theCapabilityStatement, List theServerBindings) { + boolean bulkExportEnabled = theServerBindings.stream() + .filter(OperationMethodBinding.class::isInstance) + .map(OperationMethodBinding.class::cast) + .map(OperationMethodBinding::getName) + .anyMatch(ProviderConstants.OPERATION_EXPORT::equals); + + if (bulkExportEnabled) { + theCapabilityStatement.addInstantiates(Constants.BULK_DATA_ACCESS_IG_URL); + } + } } diff --git a/hapi-fhir-structures-dstu3/src/test/java/ca/uhn/fhir/context/ContextScanningDstu3Test.java b/hapi-fhir-structures-dstu3/src/test/java/ca/uhn/fhir/context/ContextScanningDstu3Test.java index 5dd67c5faec..8e25e3754a8 100644 --- a/hapi-fhir-structures-dstu3/src/test/java/ca/uhn/fhir/context/ContextScanningDstu3Test.java +++ b/hapi-fhir-structures-dstu3/src/test/java/ca/uhn/fhir/context/ContextScanningDstu3Test.java @@ -2,14 +2,18 @@ package ca.uhn.fhir.context; import ca.uhn.fhir.rest.annotation.IdParam; import ca.uhn.fhir.rest.annotation.Read; +import ca.uhn.fhir.rest.api.EncodingEnum; import ca.uhn.fhir.rest.client.api.IGenericClient; +import ca.uhn.fhir.rest.server.FifoMemoryPagingProvider; import ca.uhn.fhir.rest.server.IResourceProvider; import ca.uhn.fhir.rest.server.RestfulServer; +import ca.uhn.fhir.test.utilities.HttpClientExtension; import ca.uhn.fhir.test.utilities.JettyUtil; +import ca.uhn.fhir.test.utilities.server.RestfulServerExtension; import ca.uhn.fhir.util.TestUtil; import org.eclipse.jetty.server.Server; -import org.eclipse.jetty.servlet.ServletHandler; -import org.eclipse.jetty.servlet.ServletHolder; +import org.eclipse.jetty.ee10.servlet.ServletHandler; +import org.eclipse.jetty.ee10.servlet.ServletHolder; import org.hl7.fhir.dstu3.model.IdType; import org.hl7.fhir.dstu3.model.Observation; import org.hl7.fhir.dstu3.model.Observation.ObservationStatus; @@ -19,6 +23,7 @@ import org.hl7.fhir.exceptions.FHIRFormatError; import org.junit.jupiter.api.AfterAll; import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.RegisterExtension; import java.util.TreeSet; @@ -29,10 +34,18 @@ import static org.junit.jupiter.api.Assertions.assertFalse; import static org.junit.jupiter.api.Assertions.assertTrue; public class ContextScanningDstu3Test { - private static FhirContext ourCtx = FhirContext.forDstu3(); + private static final FhirContext ourCtx = FhirContext.forDstu3Cached(); private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(ContextScanningDstu3Test.class); - private static int ourPort; - private static Server ourServer; + + @RegisterExtension + private RestfulServerExtension ourServer = new RestfulServerExtension(ourCtx) + .setDefaultResponseEncoding(EncodingEnum.XML) + .registerProvider(new PatientProvider()) + .registerProvider(new ObservationProvider()) + .setDefaultPrettyPrint(false); + + @RegisterExtension + private HttpClientExtension ourClient = new HttpClientExtension(); @Test public void testContextDoesntScanUnneccesaryTypes() { @@ -44,7 +57,7 @@ public class ContextScanningDstu3Test { ourLog.info("Have {} element definitions: {}", ctx.getElementDefinitions().size(), elementDefs); assertThat(resDefs, not(containsInRelativeOrder("Observation"))); - IGenericClient client = ctx.newRestfulGenericClient("http://localhost:" + ourPort); + IGenericClient client = ctx.newRestfulGenericClient(ourServer.getBaseUrl()); client.read().resource(Patient.class).withId("1").execute(); resDefs = scannedResourceNames(ctx); @@ -101,7 +114,7 @@ public class ContextScanningDstu3Test { BaseRuntimeElementCompositeDefinition compositeDef = (BaseRuntimeElementCompositeDefinition) ctx.getElementDefinition("identifier"); assertFalse(compositeDef.isSealed()); - IGenericClient client = ctx.newRestfulGenericClient("http://localhost:" + ourPort); + IGenericClient client = ctx.newRestfulGenericClient(ourServer.getBaseUrl()); client.read().resource(Patient.class).withId("1").execute(); resDefs = scannedResourceNames(ctx); @@ -141,24 +154,9 @@ public class ContextScanningDstu3Test { @AfterAll public static void afterClassClearContext() throws Exception { - JettyUtil.closeServer(ourServer); TestUtil.randomizeLocaleAndTimezone(); } - @BeforeAll - public static void beforeClass() throws Exception { - ourServer = new Server(0); - - ServletHandler proxyHandler = new ServletHandler(); - RestfulServer servlet = new RestfulServer(ourCtx); - servlet.setResourceProviders(new PatientProvider(), new ObservationProvider()); - ServletHolder servletHolder = new ServletHolder(servlet); - proxyHandler.addServletWithMapping(servletHolder, "/*"); - ourServer.setHandler(proxyHandler); - JettyUtil.startServer(ourServer); - ourPort = JettyUtil.getPortForStartedServer(ourServer); - - } public static class PatientProvider implements IResourceProvider { diff --git a/hapi-fhir-structures-dstu3/src/test/java/ca/uhn/fhir/narrative/DefaultThymeleafNarrativeGeneratorDstu3Test.java b/hapi-fhir-structures-dstu3/src/test/java/ca/uhn/fhir/narrative/DefaultThymeleafNarrativeGeneratorDstu3Test.java index 421a0ce66a0..64e51dcf21c 100644 --- a/hapi-fhir-structures-dstu3/src/test/java/ca/uhn/fhir/narrative/DefaultThymeleafNarrativeGeneratorDstu3Test.java +++ b/hapi-fhir-structures-dstu3/src/test/java/ca/uhn/fhir/narrative/DefaultThymeleafNarrativeGeneratorDstu3Test.java @@ -143,7 +143,7 @@ public class DefaultThymeleafNarrativeGeneratorDstu3Test { String parse = "\n" + " \n" + " \n" + - " \n" + + " \n" + " \n" + " \n" + " \n" + diff --git a/hapi-fhir-structures-dstu3/src/test/java/ca/uhn/fhir/narrative2/ThymeleafNarrativeGeneratorTest.java b/hapi-fhir-structures-dstu3/src/test/java/ca/uhn/fhir/narrative2/ThymeleafNarrativeGeneratorTest.java index c19baf9d3c6..03eca5a2315 100644 --- a/hapi-fhir-structures-dstu3/src/test/java/ca/uhn/fhir/narrative2/ThymeleafNarrativeGeneratorTest.java +++ b/hapi-fhir-structures-dstu3/src/test/java/ca/uhn/fhir/narrative2/ThymeleafNarrativeGeneratorTest.java @@ -20,8 +20,8 @@ import org.junit.jupiter.api.Test; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import javax.annotation.Nonnull; -import javax.annotation.Nullable; +import jakarta.annotation.Nonnull; +import jakarta.annotation.Nullable; import java.io.IOException; import static org.hamcrest.CoreMatchers.containsString; diff --git a/hapi-fhir-structures-dstu3/src/test/java/ca/uhn/fhir/parser/JsonParserDstu3Test.java b/hapi-fhir-structures-dstu3/src/test/java/ca/uhn/fhir/parser/JsonParserDstu3Test.java index bcff5de5fb3..cfa190aa1db 100644 --- a/hapi-fhir-structures-dstu3/src/test/java/ca/uhn/fhir/parser/JsonParserDstu3Test.java +++ b/hapi-fhir-structures-dstu3/src/test/java/ca/uhn/fhir/parser/JsonParserDstu3Test.java @@ -9,6 +9,7 @@ import ca.uhn.fhir.parser.XmlParserDstu3Test.TestPatientFor327; import ca.uhn.fhir.parser.json.BaseJsonLikeValue.ScalarType; import ca.uhn.fhir.parser.json.BaseJsonLikeValue.ValueType; import ca.uhn.fhir.util.ClasspathUtil; +import ca.uhn.fhir.util.JsonUtil; import ca.uhn.fhir.util.TestUtil; import ca.uhn.fhir.validation.FhirValidator; import ca.uhn.fhir.validation.ValidationResult; @@ -157,7 +158,7 @@ public class JsonParserDstu3Test { fail(); } catch (DataFormatException e) { assertEquals(Msg.code(1861) + "Failed to parse JSON encoded FHIR content: Unexpected character ('=' (code 61)): was expecting a colon to separate field name and value\n" + - " at [Source: UNKNOWN; line: 4, column: 18]", e.getMessage()); + " at [line: 4, column: 18]", e.getMessage()); } } @@ -2324,8 +2325,10 @@ public class JsonParserDstu3Test { ourCtx.newJsonParser().parseResource(Bundle.class, bundle); fail(); } catch (DataFormatException e) { - assertEquals(Msg.code(1861) + "Failed to parse JSON encoded FHIR content: Unexpected close marker '}': expected ']' (for root starting at [Source: UNKNOWN; line: 1])\n" + - " at [Source: UNKNOWN; line: 4, column: 3]", e.getMessage()); + // I'm hoping at some point we can get rid of the REDACTED message entirely. + // Request filed with Jackson: https://github.com/FasterXML/jackson-core/issues/1158 + assertEquals(Msg.code(1861) + "Failed to parse JSON encoded FHIR content: Unexpected close marker '}': expected ']' (for root starting at [line: 1])\n" + + " at [line: 4, column: 3]", e.getMessage()); } } diff --git a/hapi-fhir-structures-dstu3/src/test/java/ca/uhn/fhir/rest/server/CreateBinaryDstu3Test.java b/hapi-fhir-structures-dstu3/src/test/java/ca/uhn/fhir/rest/server/CreateBinaryDstu3Test.java index 2cf05c1c35e..933df506788 100644 --- a/hapi-fhir-structures-dstu3/src/test/java/ca/uhn/fhir/rest/server/CreateBinaryDstu3Test.java +++ b/hapi-fhir-structures-dstu3/src/test/java/ca/uhn/fhir/rest/server/CreateBinaryDstu3Test.java @@ -4,8 +4,11 @@ import ca.uhn.fhir.context.FhirContext; import ca.uhn.fhir.rest.annotation.Create; import ca.uhn.fhir.rest.annotation.ResourceParam; import ca.uhn.fhir.rest.api.Constants; +import ca.uhn.fhir.rest.api.EncodingEnum; import ca.uhn.fhir.rest.api.MethodOutcome; +import ca.uhn.fhir.test.utilities.HttpClientExtension; import ca.uhn.fhir.test.utilities.JettyUtil; +import ca.uhn.fhir.test.utilities.server.RestfulServerExtension; import ca.uhn.fhir.util.TestUtil; import org.apache.commons.io.IOUtils; import org.apache.http.client.methods.CloseableHttpResponse; @@ -16,8 +19,8 @@ import org.apache.http.impl.client.CloseableHttpClient; import org.apache.http.impl.client.HttpClientBuilder; import org.apache.http.impl.conn.PoolingHttpClientConnectionManager; import org.eclipse.jetty.server.Server; -import org.eclipse.jetty.servlet.ServletHandler; -import org.eclipse.jetty.servlet.ServletHolder; +import org.eclipse.jetty.ee10.servlet.ServletHandler; +import org.eclipse.jetty.ee10.servlet.ServletHolder; import org.hl7.fhir.dstu3.model.Binary; import org.hl7.fhir.dstu3.model.IdType; import org.hl7.fhir.dstu3.model.Patient; @@ -26,6 +29,7 @@ import org.junit.jupiter.api.AfterAll; import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.RegisterExtension; import java.util.concurrent.TimeUnit; @@ -34,13 +38,20 @@ import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertNull; public class CreateBinaryDstu3Test { - private static CloseableHttpClient ourClient; - private static FhirContext ourCtx = FhirContext.forDstu3(); + private static final FhirContext ourCtx = FhirContext.forDstu3Cached(); private static Binary ourLastBinary; private static byte[] ourLastBinaryBytes; private static String ourLastBinaryString; - private static int ourPort; - private static Server ourServer; + + @RegisterExtension + private RestfulServerExtension ourServer = new RestfulServerExtension(ourCtx) + .setDefaultResponseEncoding(EncodingEnum.XML) + .registerProvider(new BinaryProvider()) + .withPagingProvider(new FifoMemoryPagingProvider(100)) + .setDefaultPrettyPrint(false); + + @RegisterExtension + private HttpClientExtension ourClient = new HttpClientExtension(); @BeforeEach public void before() { @@ -51,7 +62,7 @@ public class CreateBinaryDstu3Test { @Test public void testRawBytesBinaryContentType() throws Exception { - HttpPost post = new HttpPost("http://localhost:" + ourPort + "/Binary"); + HttpPost post = new HttpPost(ourServer.getBaseUrl() + "/Binary"); post.setEntity(new ByteArrayEntity(new byte[] { 0, 1, 2, 3, 4 })); post.addHeader("Content-Type", "application/foo"); CloseableHttpResponse status = ourClient.execute(post); @@ -75,7 +86,7 @@ public class CreateBinaryDstu3Test { b.setContent(new byte[] { 0, 1, 2, 3, 4 }); String encoded = ourCtx.newJsonParser().encodeResourceToString(b); - HttpPost post = new HttpPost("http://localhost:" + ourPort + "/Binary"); + HttpPost post = new HttpPost(ourServer.getBaseUrl() + "/Binary"); post.setEntity(new StringEntity(encoded)); post.addHeader("Content-Type", Constants.CT_FHIR_JSON); CloseableHttpResponse status = ourClient.execute(post); @@ -98,7 +109,7 @@ public class CreateBinaryDstu3Test { b.setContent(ourCtx.newXmlParser().encodeResourceToString(p).getBytes("UTF-8")); String encoded = ourCtx.newJsonParser().encodeResourceToString(b); - HttpPost post = new HttpPost("http://localhost:" + ourPort + "/Binary"); + HttpPost post = new HttpPost(ourServer.getBaseUrl() + "/Binary"); post.setEntity(new StringEntity(encoded)); post.addHeader("Content-Type", Constants.CT_FHIR_JSON); CloseableHttpResponse status = ourClient.execute(post); @@ -114,7 +125,7 @@ public class CreateBinaryDstu3Test { @Test public void testRawBytesNoContentType() throws Exception { - HttpPost post = new HttpPost("http://localhost:" + ourPort + "/Binary"); + HttpPost post = new HttpPost(ourServer.getBaseUrl() + "/Binary"); post.setEntity(new ByteArrayEntity(new byte[] { 0, 1, 2, 3, 4 })); CloseableHttpResponse status = ourClient.execute(post); try { @@ -127,31 +138,9 @@ public class CreateBinaryDstu3Test { @AfterAll public static void afterClassClearContext() throws Exception { - JettyUtil.closeServer(ourServer); TestUtil.randomizeLocaleAndTimezone(); } - @BeforeAll - public static void beforeClass() throws Exception { - ourServer = new Server(0); - - BinaryProvider binaryProvider = new BinaryProvider(); - - ServletHandler proxyHandler = new ServletHandler(); - RestfulServer servlet = new RestfulServer(ourCtx); - servlet.setResourceProviders(binaryProvider); - ServletHolder servletHolder = new ServletHolder(servlet); - proxyHandler.addServletWithMapping(servletHolder, "/*"); - ourServer.setHandler(proxyHandler); - JettyUtil.startServer(ourServer); - ourPort = JettyUtil.getPortForStartedServer(ourServer); - - PoolingHttpClientConnectionManager connectionManager = new PoolingHttpClientConnectionManager(5000, TimeUnit.MILLISECONDS); - HttpClientBuilder builder = HttpClientBuilder.create(); - builder.setConnectionManager(connectionManager); - ourClient = builder.build(); - } - public static class BinaryProvider implements IResourceProvider { @Create() diff --git a/hapi-fhir-structures-dstu3/src/test/java/ca/uhn/fhir/rest/server/CreateDstu3Test.java b/hapi-fhir-structures-dstu3/src/test/java/ca/uhn/fhir/rest/server/CreateDstu3Test.java index b1691ef0ad2..bffb7aa199b 100644 --- a/hapi-fhir-structures-dstu3/src/test/java/ca/uhn/fhir/rest/server/CreateDstu3Test.java +++ b/hapi-fhir-structures-dstu3/src/test/java/ca/uhn/fhir/rest/server/CreateDstu3Test.java @@ -7,9 +7,12 @@ import ca.uhn.fhir.rest.annotation.IdParam; import ca.uhn.fhir.rest.annotation.Read; import ca.uhn.fhir.rest.annotation.ResourceParam; import ca.uhn.fhir.rest.annotation.Search; +import ca.uhn.fhir.rest.api.EncodingEnum; import ca.uhn.fhir.rest.api.MethodOutcome; import ca.uhn.fhir.rest.client.MyPatientWithExtensions; +import ca.uhn.fhir.test.utilities.HttpClientExtension; import ca.uhn.fhir.test.utilities.JettyUtil; +import ca.uhn.fhir.test.utilities.server.RestfulServerExtension; import ca.uhn.fhir.util.TestUtil; import org.apache.commons.io.IOUtils; import org.apache.http.HttpResponse; @@ -21,8 +24,8 @@ import org.apache.http.impl.client.CloseableHttpClient; import org.apache.http.impl.client.HttpClientBuilder; import org.apache.http.impl.conn.PoolingHttpClientConnectionManager; import org.eclipse.jetty.server.Server; -import org.eclipse.jetty.servlet.ServletHandler; -import org.eclipse.jetty.servlet.ServletHolder; +import org.eclipse.jetty.ee10.servlet.ServletHandler; +import org.eclipse.jetty.ee10.servlet.ServletHolder; import org.hl7.fhir.dstu3.model.DateType; import org.hl7.fhir.dstu3.model.IdType; import org.hl7.fhir.dstu3.model.OperationOutcome; @@ -34,6 +37,7 @@ import org.junit.jupiter.api.AfterAll; import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.RegisterExtension; import java.nio.charset.StandardCharsets; import java.util.ArrayList; @@ -47,14 +51,21 @@ import static org.hamcrest.Matchers.stringContainsInOrder; import static org.junit.jupiter.api.Assertions.assertEquals; public class CreateDstu3Test { - private static CloseableHttpClient ourClient; - private static FhirContext ourCtx = FhirContext.forDstu3(); + private static final FhirContext ourCtx = FhirContext.forDstu3(); private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(CreateDstu3Test.class); - private static int ourPort; - private static Server ourServer; public static IBaseOperationOutcome ourReturnOo; + @RegisterExtension + private RestfulServerExtension ourServer = new RestfulServerExtension(ourCtx) + .setDefaultResponseEncoding(EncodingEnum.XML) + .registerProvider(new PatientProvider()) + .withPagingProvider(new FifoMemoryPagingProvider(100)) + .setDefaultPrettyPrint(false); + + @RegisterExtension + private HttpClientExtension ourClient = new HttpClientExtension(); + @BeforeEach public void before() { ourReturnOo = null; @@ -66,7 +77,7 @@ public class CreateDstu3Test { @Test public void testCreateReturnsLocationHeader() throws Exception { - HttpPost httpPost = new HttpPost("http://localhost:" + ourPort + "/Patient"); + HttpPost httpPost = new HttpPost(ourServer.getBaseUrl() + "/Patient"); httpPost.setEntity(new StringEntity("{\"resourceType\":\"Patient\", \"status\":\"active\"}", ContentType.parse("application/fhir+json; charset=utf-8"))); HttpResponse status = ourClient.execute(httpPost); @@ -79,7 +90,7 @@ public class CreateDstu3Test { assertEquals(1, status.getHeaders("Location").length); assertEquals(1, status.getHeaders("Content-Location").length); - assertEquals("http://localhost:" + ourPort + "/Patient/1", status.getFirstHeader("Location").getValue()); + assertEquals(ourServer.getBaseUrl() + "/Patient/1", status.getFirstHeader("Location").getValue()); } @@ -88,7 +99,7 @@ public class CreateDstu3Test { ourReturnOo = new OperationOutcome().addIssue(new OperationOutcomeIssueComponent().setDiagnostics("DIAG")); String expectedResponseContent = "{\"resourceType\":\"Patient\",\"id\":\"1\",\"meta\":{\"versionId\":\"1\"},\"gender\":\"male\"}"; - HttpPost httpPost = new HttpPost("http://localhost:" + ourPort + "/Patient"); + HttpPost httpPost = new HttpPost(ourServer.getBaseUrl() + "/Patient"); httpPost.setEntity(new StringEntity("{\"resourceType\":\"Patient\", \"gender\":\"male\"}", ContentType.parse("application/fhir+json; charset=utf-8"))); HttpResponse status = ourClient.execute(httpPost); @@ -108,7 +119,7 @@ public class CreateDstu3Test { @Test public void testCreateWithInvalidContent() throws Exception { - HttpPost httpPost = new HttpPost("http://localhost:" + ourPort + "/Patient"); + HttpPost httpPost = new HttpPost(ourServer.getBaseUrl() + "/Patient"); httpPost.setEntity(new StringEntity("FOO", ContentType.parse("application/xml+fhir; charset=utf-8"))); HttpResponse status = ourClient.execute(httpPost); @@ -127,7 +138,7 @@ public class CreateDstu3Test { @Test public void testCreateWithIncorrectContent1() throws Exception { - HttpPost httpPost = new HttpPost("http://localhost:" + ourPort + "/Patient"); + HttpPost httpPost = new HttpPost(ourServer.getBaseUrl() + "/Patient"); httpPost.setEntity(new StringEntity("{\"foo\":\"bar\"}", ContentType.parse("application/xml+fhir; charset=utf-8"))); HttpResponse status = ourClient.execute(httpPost); @@ -146,7 +157,7 @@ public class CreateDstu3Test { @Test public void testCreateWithIncorrectContent2() throws Exception { - HttpPost httpPost = new HttpPost("http://localhost:" + ourPort + "/Patient"); + HttpPost httpPost = new HttpPost(ourServer.getBaseUrl() + "/Patient"); httpPost.setEntity(new StringEntity("{\"foo\":\"bar\"}", ContentType.parse("application/fhir+xml; charset=utf-8"))); HttpResponse status = ourClient.execute(httpPost); @@ -165,7 +176,7 @@ public class CreateDstu3Test { @Test public void testCreateWithIncorrectContent3() throws Exception { - HttpPost httpPost = new HttpPost("http://localhost:" + ourPort + "/Patient"); + HttpPost httpPost = new HttpPost(ourServer.getBaseUrl() + "/Patient"); httpPost.setEntity(new StringEntity("{\"foo\":\"bar\"}", ContentType.parse("application/fhir+json; charset=utf-8"))); HttpResponse status = ourClient.execute(httpPost); @@ -183,7 +194,7 @@ public class CreateDstu3Test { @Test public void testSearch() throws Exception { - HttpGet httpGet = new HttpGet("http://localhost:" + ourPort + "/Patient?_format=xml&_pretty=true"); + HttpGet httpGet = new HttpGet(ourServer.getBaseUrl() + "/Patient?_format=xml&_pretty=true"); HttpResponse status = ourClient.execute(httpGet); String responseContent = IOUtils.toString(status.getEntity().getContent(), StandardCharsets.UTF_8); @@ -211,34 +222,10 @@ public class CreateDstu3Test { @AfterAll public static void afterClassClearContext() throws Exception { - JettyUtil.closeServer(ourServer); TestUtil.randomizeLocaleAndTimezone(); } - @BeforeAll - public static void beforeClass() throws Exception { - ourServer = new Server(0); - - PatientProvider patientProvider = new PatientProvider(); - - ServletHandler proxyHandler = new ServletHandler(); - RestfulServer servlet = new RestfulServer(ourCtx); - - servlet.setResourceProviders(patientProvider); - ServletHolder servletHolder = new ServletHolder(servlet); - proxyHandler.addServletWithMapping(servletHolder, "/*"); - ourServer.setHandler(proxyHandler); - JettyUtil.startServer(ourServer); - ourPort = JettyUtil.getPortForStartedServer(ourServer); - - PoolingHttpClientConnectionManager connectionManager = new PoolingHttpClientConnectionManager(5000, TimeUnit.MILLISECONDS); - HttpClientBuilder builder = HttpClientBuilder.create(); - builder.setConnectionManager(connectionManager); - ourClient = builder.build(); - - } - - public static class PatientProvider implements IResourceProvider { + private static class PatientProvider implements IResourceProvider { @Create() public MethodOutcome create(@ResourceParam Patient theIdParam) { @@ -263,7 +250,7 @@ public class CreateDstu3Test { @Search public List search() { - ArrayList retVal = new ArrayList(); + ArrayList retVal = new ArrayList<>(); MyPatientWithExtensions p0 = new MyPatientWithExtensions(); p0.setId(new IdType("Patient/0")); diff --git a/hapi-fhir-structures-dstu3/src/test/java/ca/uhn/fhir/rest/server/CustomTypeServerDstu3.java b/hapi-fhir-structures-dstu3/src/test/java/ca/uhn/fhir/rest/server/CustomTypeServerDstu3.java index ab4e3d9c0bb..9bae98c5f28 100644 --- a/hapi-fhir-structures-dstu3/src/test/java/ca/uhn/fhir/rest/server/CustomTypeServerDstu3.java +++ b/hapi-fhir-structures-dstu3/src/test/java/ca/uhn/fhir/rest/server/CustomTypeServerDstu3.java @@ -10,8 +10,11 @@ import ca.uhn.fhir.rest.annotation.OptionalParam; import ca.uhn.fhir.rest.annotation.ResourceParam; import ca.uhn.fhir.rest.annotation.Search; import ca.uhn.fhir.rest.api.Constants; +import ca.uhn.fhir.rest.api.EncodingEnum; import ca.uhn.fhir.rest.api.MethodOutcome; +import ca.uhn.fhir.test.utilities.HttpClientExtension; import ca.uhn.fhir.test.utilities.JettyUtil; +import ca.uhn.fhir.test.utilities.server.RestfulServerExtension; import ca.uhn.fhir.util.TestUtil; import org.apache.commons.io.IOUtils; import org.apache.http.HttpResponse; @@ -22,8 +25,8 @@ import org.apache.http.impl.client.CloseableHttpClient; import org.apache.http.impl.client.HttpClientBuilder; import org.apache.http.impl.conn.PoolingHttpClientConnectionManager; import org.eclipse.jetty.server.Server; -import org.eclipse.jetty.servlet.ServletHandler; -import org.eclipse.jetty.servlet.ServletHolder; +import org.eclipse.jetty.ee10.servlet.ServletHandler; +import org.eclipse.jetty.ee10.servlet.ServletHolder; import org.hl7.fhir.dstu3.model.IdType; import org.hl7.fhir.dstu3.model.OperationOutcome; import org.hl7.fhir.dstu3.model.Patient; @@ -31,6 +34,7 @@ import org.junit.jupiter.api.AfterAll; import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.RegisterExtension; import java.nio.charset.StandardCharsets; import java.util.ArrayList; @@ -40,16 +44,23 @@ import java.util.concurrent.TimeUnit; import static org.junit.jupiter.api.Assertions.assertEquals; public class CustomTypeServerDstu3 { - private static CloseableHttpClient ourClient; - private static FhirContext ourCtx = FhirContext.forDstu3(); + private static final FhirContext ourCtx = FhirContext.forDstu3Cached(); private static String ourLastConditionalUrl; private static IdType ourLastId; private static IdType ourLastIdParam; private static boolean ourLastRequestWasSearch; private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(CustomTypeServerDstu3.class); - private static int ourPort; - private static Server ourServer; + + @RegisterExtension + private RestfulServerExtension ourServer = new RestfulServerExtension(ourCtx) + .setDefaultResponseEncoding(EncodingEnum.XML) + .registerProvider(new PatientProvider()) + .withPagingProvider(new FifoMemoryPagingProvider(100)) + .setDefaultPrettyPrint(false); + + @RegisterExtension + private HttpClientExtension ourClient = new HttpClientExtension(); @BeforeEach public void before() { @@ -67,7 +78,7 @@ public class CustomTypeServerDstu3 { patient.setId("2"); patient.addIdentifier().setValue("002"); - HttpPost httpPost = new HttpPost("http://localhost:" + ourPort + "/Patient"); + HttpPost httpPost = new HttpPost(ourServer.getBaseUrl() + "/Patient"); // httpPost.addHeader(Constants.HEADER_IF_NONE_EXIST, "Patient?identifier=system%7C001"); httpPost.setEntity(new StringEntity(ourCtx.newXmlParser().encodeResourceToString(patient), ContentType.create(Constants.CT_FHIR_XML, "UTF-8"))); @@ -87,7 +98,7 @@ public class CustomTypeServerDstu3 { Patient patient = new Patient(); patient.addIdentifier().setValue("002"); - HttpPost httpPost = new HttpPost("http://localhost:" + ourPort + "/Patient/2"); + HttpPost httpPost = new HttpPost(ourServer.getBaseUrl() + "/Patient/2"); // httpPost.addHeader(Constants.HEADER_IF_NONE_EXIST, "Patient?identifier=system%7C001"); httpPost.setEntity(new StringEntity(ourCtx.newXmlParser().encodeResourceToString(patient), ContentType.create(Constants.CT_FHIR_XML, "UTF-8"))); @@ -109,7 +120,7 @@ public class CustomTypeServerDstu3 { Patient patient = new Patient(); patient.addIdentifier().setValue("002"); - HttpPost httpPost = new HttpPost("http://localhost:" + ourPort + "/Patient/2"); + HttpPost httpPost = new HttpPost(ourServer.getBaseUrl() + "/Patient/2"); httpPost.addHeader(Constants.HEADER_IF_NONE_EXIST, "Patient?identifier=system%7C001"); httpPost.setEntity(new StringEntity(ourCtx.newXmlParser().encodeResourceToString(patient), ContentType.create(Constants.CT_FHIR_XML, "UTF-8"))); @@ -128,32 +139,9 @@ public class CustomTypeServerDstu3 { @AfterAll public static void afterClassClearContext() throws Exception { - JettyUtil.closeServer(ourServer); TestUtil.randomizeLocaleAndTimezone(); } - @BeforeAll - public static void beforeClass() throws Exception { - ourServer = new Server(0); - - PatientProvider patientProvider = new PatientProvider(); - - ServletHandler proxyHandler = new ServletHandler(); - RestfulServer servlet = new RestfulServer(ourCtx); - servlet.setResourceProviders(patientProvider); - ServletHolder servletHolder = new ServletHolder(servlet); - proxyHandler.addServletWithMapping(servletHolder, "/*"); - ourServer.setHandler(proxyHandler); - JettyUtil.startServer(ourServer); - ourPort = JettyUtil.getPortForStartedServer(ourServer); - - PoolingHttpClientConnectionManager connectionManager = new PoolingHttpClientConnectionManager(5000, TimeUnit.MILLISECONDS); - HttpClientBuilder builder = HttpClientBuilder.create(); - builder.setConnectionManager(connectionManager); - ourClient = builder.build(); - - } - public static class PatientProvider implements IResourceProvider { @Create() diff --git a/hapi-fhir-structures-dstu3/src/test/java/ca/uhn/fhir/rest/server/DeleteConditionalDstu3Test.java b/hapi-fhir-structures-dstu3/src/test/java/ca/uhn/fhir/rest/server/DeleteConditionalDstu3Test.java index 26fc80d2b57..12b738269b5 100644 --- a/hapi-fhir-structures-dstu3/src/test/java/ca/uhn/fhir/rest/server/DeleteConditionalDstu3Test.java +++ b/hapi-fhir-structures-dstu3/src/test/java/ca/uhn/fhir/rest/server/DeleteConditionalDstu3Test.java @@ -4,17 +4,20 @@ import ca.uhn.fhir.context.FhirContext; import ca.uhn.fhir.rest.annotation.ConditionalUrlParam; import ca.uhn.fhir.rest.annotation.Delete; import ca.uhn.fhir.rest.annotation.IdParam; +import ca.uhn.fhir.rest.api.EncodingEnum; import ca.uhn.fhir.rest.api.MethodOutcome; import ca.uhn.fhir.rest.client.api.IGenericClient; import ca.uhn.fhir.rest.client.interceptor.LoggingInterceptor; +import ca.uhn.fhir.test.utilities.HttpClientExtension; import ca.uhn.fhir.test.utilities.JettyUtil; +import ca.uhn.fhir.test.utilities.server.RestfulServerExtension; import ca.uhn.fhir.util.TestUtil; import org.apache.http.impl.client.CloseableHttpClient; import org.apache.http.impl.client.HttpClientBuilder; import org.apache.http.impl.conn.PoolingHttpClientConnectionManager; import org.eclipse.jetty.server.Server; -import org.eclipse.jetty.servlet.ServletHandler; -import org.eclipse.jetty.servlet.ServletHolder; +import org.eclipse.jetty.ee10.servlet.ServletHandler; +import org.eclipse.jetty.ee10.servlet.ServletHolder; import org.hl7.fhir.dstu3.model.IdType; import org.hl7.fhir.dstu3.model.Patient; import org.hl7.fhir.instance.model.api.IBaseResource; @@ -22,6 +25,7 @@ import org.junit.jupiter.api.AfterAll; import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.RegisterExtension; import java.util.concurrent.TimeUnit; @@ -29,16 +33,23 @@ import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertTrue; public class DeleteConditionalDstu3Test { - private static CloseableHttpClient ourClient; - private static FhirContext ourCtx = FhirContext.forDstu3(); + private static final FhirContext ourCtx = FhirContext.forDstu3Cached(); private static IGenericClient ourHapiClient; private static String ourLastConditionalUrl; private static IdType ourLastIdParam; private static boolean ourLastRequestWasDelete; private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(DeleteConditionalDstu3Test.class); - private static int ourPort; - private static Server ourServer; - + + @RegisterExtension + private RestfulServerExtension ourServer = new RestfulServerExtension(ourCtx) + .setDefaultResponseEncoding(EncodingEnum.XML) + .registerProvider(new PatientProvider()) + .withPagingProvider(new FifoMemoryPagingProvider(100)) + .setDefaultPrettyPrint(false); + + @RegisterExtension + private HttpClientExtension ourClient = new HttpClientExtension(); + @BeforeEach public void before() { @@ -55,23 +66,13 @@ public class DeleteConditionalDstu3Test { Patient patient = new Patient(); patient.addIdentifier().setValue("002"); -// HttpGet httpGet = new HttpGet("http://localhost:" + ourPort + "/Patient?_pretty=true"); -// -// HttpResponse status = ourClient.execute(httpGet); -// -// String responseContent = IOUtils.toString(status.getEntity().getContent()); -// IOUtils.closeQuietly(status.getEntity().getContent()); -// -// ourLog.info("Response was:\n{}", responseContent); - - //@formatter:off - ourHapiClient + ourServer + .getFhirClient() .delete() .resourceConditionalByType(Patient.class) .where(Patient.IDENTIFIER.exactly().systemAndIdentifier("SOMESYS","SOMEID")) .execute(); - //@formatter:on - + assertTrue(ourLastRequestWasDelete); assertEquals(null, ourLastIdParam); assertEquals("Patient?identifier=SOMESYS%7CSOMEID", ourLastConditionalUrl); @@ -82,36 +83,10 @@ public class DeleteConditionalDstu3Test { @AfterAll public static void afterClassClearContext() throws Exception { - JettyUtil.closeServer(ourServer); TestUtil.randomizeLocaleAndTimezone(); } - @BeforeAll - public static void beforeClass() throws Exception { - ourServer = new Server(0); - - PatientProvider patientProvider = new PatientProvider(); - - ServletHandler proxyHandler = new ServletHandler(); - RestfulServer servlet = new RestfulServer(ourCtx); - servlet.setResourceProviders(patientProvider); - ServletHolder servletHolder = new ServletHolder(servlet); - proxyHandler.addServletWithMapping(servletHolder, "/*"); - ourServer.setHandler(proxyHandler); - JettyUtil.startServer(ourServer); - ourPort = JettyUtil.getPortForStartedServer(ourServer); - - PoolingHttpClientConnectionManager connectionManager = new PoolingHttpClientConnectionManager(5000, TimeUnit.MILLISECONDS); - HttpClientBuilder builder = HttpClientBuilder.create(); - builder.setConnectionManager(connectionManager); - ourClient = builder.build(); - - ourCtx.getRestfulClientFactory().setSocketTimeout(500 * 1000); - ourHapiClient = ourCtx.newRestfulGenericClient("http://localhost:" + ourPort + "/"); - ourHapiClient.registerInterceptor(new LoggingInterceptor()); - } - public static class PatientProvider implements IResourceProvider { @Delete() diff --git a/hapi-fhir-structures-dstu3/src/test/java/ca/uhn/fhir/rest/server/FormatParameterDstu3Test.java b/hapi-fhir-structures-dstu3/src/test/java/ca/uhn/fhir/rest/server/FormatParameterDstu3Test.java index f9fb9b0cfb1..78ada9acf20 100644 --- a/hapi-fhir-structures-dstu3/src/test/java/ca/uhn/fhir/rest/server/FormatParameterDstu3Test.java +++ b/hapi-fhir-structures-dstu3/src/test/java/ca/uhn/fhir/rest/server/FormatParameterDstu3Test.java @@ -5,24 +5,17 @@ import ca.uhn.fhir.model.api.annotation.ResourceDef; import ca.uhn.fhir.rest.annotation.IdParam; import ca.uhn.fhir.rest.annotation.Read; import ca.uhn.fhir.rest.api.EncodingEnum; -import ca.uhn.fhir.test.utilities.JettyUtil; +import ca.uhn.fhir.test.utilities.HttpClientExtension; +import ca.uhn.fhir.test.utilities.server.RestfulServerExtension; import ca.uhn.fhir.util.TestUtil; import org.apache.commons.io.IOUtils; import org.apache.http.client.methods.CloseableHttpResponse; import org.apache.http.client.methods.HttpGet; -import org.apache.http.impl.client.CloseableHttpClient; -import org.apache.http.impl.client.HttpClientBuilder; -import org.apache.http.impl.conn.PoolingHttpClientConnectionManager; -import org.eclipse.jetty.server.Server; -import org.eclipse.jetty.servlet.ServletHandler; -import org.eclipse.jetty.servlet.ServletHolder; import org.hl7.fhir.dstu3.model.IdType; import org.hl7.fhir.dstu3.model.Patient; import org.junit.jupiter.api.AfterAll; -import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.Test; - -import java.util.concurrent.TimeUnit; +import org.junit.jupiter.api.extension.RegisterExtension; import static org.junit.jupiter.api.Assertions.assertEquals; @@ -30,21 +23,26 @@ public class FormatParameterDstu3Test { private static final String VALUE_XML = ""; private static final String VALUE_JSON = "{\"resourceType\":\"Patient\",\"id\":\"p1ReadId\",\"meta\":{\"profile\":[\"http://foo_profile\"]},\"identifier\":[{\"value\":\"p1ReadValue\"}]}"; - private static CloseableHttpClient ourClient; - private static FhirContext ourCtx = FhirContext.forDstu3(); + private static final FhirContext ourCtx = FhirContext.forDstu3Cached(); private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(FormatParameterDstu3Test.class); - private static int ourPort; - private static Server ourServer; - private static RestfulServer ourServlet; + + @RegisterExtension + private RestfulServerExtension ourServer = new RestfulServerExtension(ourCtx) + .registerProvider(new DummyPatientResourceProvider()) + .withPagingProvider(new FifoMemoryPagingProvider(100)) + .setDefaultPrettyPrint(false); + + @RegisterExtension + private HttpClientExtension ourClient = new HttpClientExtension(); /** * See #346 */ @Test public void testFormatXml() throws Exception { - ourServlet.setDefaultResponseEncoding(EncodingEnum.JSON); + ourServer.setDefaultResponseEncoding(EncodingEnum.JSON); - HttpGet httpGet = new HttpGet("http://localhost:" + ourPort + "/Patient/123?_format=xml"); + HttpGet httpGet = new HttpGet(ourServer.getBaseUrl() + "/Patient/123?_format=xml"); CloseableHttpResponse status = ourClient.execute(httpGet); try { String responseContent = IOUtils.toString(status.getEntity().getContent()); @@ -62,9 +60,9 @@ public class FormatParameterDstu3Test { */ @Test public void testFormatApplicationXml() throws Exception { - ourServlet.setDefaultResponseEncoding(EncodingEnum.JSON); + ourServer.setDefaultResponseEncoding(EncodingEnum.JSON); - HttpGet httpGet = new HttpGet("http://localhost:" + ourPort + "/Patient/123?_format=application/xml"); + HttpGet httpGet = new HttpGet(ourServer.getBaseUrl() + "/Patient/123?_format=application/xml"); CloseableHttpResponse status = ourClient.execute(httpGet); try { String responseContent = IOUtils.toString(status.getEntity().getContent()); @@ -82,9 +80,9 @@ public class FormatParameterDstu3Test { */ @Test public void testFormatApplicationXmlFhir() throws Exception { - ourServlet.setDefaultResponseEncoding(EncodingEnum.JSON); + ourServer.setDefaultResponseEncoding(EncodingEnum.JSON); - HttpGet httpGet = new HttpGet("http://localhost:" + ourPort + "/Patient/123?_format=application/xml%2Bfhir"); + HttpGet httpGet = new HttpGet(ourServer.getBaseUrl() + "/Patient/123?_format=application/xml%2Bfhir"); CloseableHttpResponse status = ourClient.execute(httpGet); try { String responseContent = IOUtils.toString(status.getEntity().getContent()); @@ -102,10 +100,10 @@ public class FormatParameterDstu3Test { */ @Test public void testFormatApplicationXmlFhirUnescaped() throws Exception { - ourServlet.setDefaultResponseEncoding(EncodingEnum.JSON); + ourServer.setDefaultResponseEncoding(EncodingEnum.JSON); // The plus isn't escaped here, and it should be.. but we'll be lenient - HttpGet httpGet = new HttpGet("http://localhost:" + ourPort + "/Patient/123?_format=application/xml+fhir"); + HttpGet httpGet = new HttpGet(ourServer.getBaseUrl() + "/Patient/123?_format=application/xml+fhir"); CloseableHttpResponse status = ourClient.execute(httpGet); try { String responseContent = IOUtils.toString(status.getEntity().getContent()); @@ -123,9 +121,9 @@ public class FormatParameterDstu3Test { */ @Test public void testFormatJson() throws Exception { - ourServlet.setDefaultResponseEncoding(EncodingEnum.XML); + ourServer.setDefaultResponseEncoding(EncodingEnum.XML); - HttpGet httpGet = new HttpGet("http://localhost:" + ourPort + "/Patient/123?_format=json"); + HttpGet httpGet = new HttpGet(ourServer.getBaseUrl() + "/Patient/123?_format=json"); CloseableHttpResponse status = ourClient.execute(httpGet); try { String responseContent = IOUtils.toString(status.getEntity().getContent()); @@ -143,9 +141,9 @@ public class FormatParameterDstu3Test { */ @Test public void testFormatApplicationJson() throws Exception { - ourServlet.setDefaultResponseEncoding(EncodingEnum.XML); + ourServer.setDefaultResponseEncoding(EncodingEnum.XML); - HttpGet httpGet = new HttpGet("http://localhost:" + ourPort + "/Patient/123?_format=application/json"); + HttpGet httpGet = new HttpGet(ourServer.getBaseUrl() + "/Patient/123?_format=application/json"); CloseableHttpResponse status = ourClient.execute(httpGet); try { String responseContent = IOUtils.toString(status.getEntity().getContent()); @@ -163,9 +161,9 @@ public class FormatParameterDstu3Test { */ @Test public void testFormatApplicationJsonFhir() throws Exception { - ourServlet.setDefaultResponseEncoding(EncodingEnum.XML); + ourServer.setDefaultResponseEncoding(EncodingEnum.XML); - HttpGet httpGet = new HttpGet("http://localhost:" + ourPort + "/Patient/123?_format=application/json%2Bfhir"); + HttpGet httpGet = new HttpGet(ourServer.getBaseUrl() + "/Patient/123?_format=application/json%2Bfhir"); CloseableHttpResponse status = ourClient.execute(httpGet); try { String responseContent = IOUtils.toString(status.getEntity().getContent()); @@ -183,10 +181,10 @@ public class FormatParameterDstu3Test { */ @Test public void testFormatApplicationJsonFhirUnescaped() throws Exception { - ourServlet.setDefaultResponseEncoding(EncodingEnum.XML); + ourServer.setDefaultResponseEncoding(EncodingEnum.XML); // The plus isn't escaped here, and it should be.. but we'll be lenient - HttpGet httpGet = new HttpGet("http://localhost:" + ourPort + "/Patient/123?_format=application/json+fhir"); + HttpGet httpGet = new HttpGet(ourServer.getBaseUrl() + "/Patient/123?_format=application/json+fhir"); CloseableHttpResponse status = ourClient.execute(httpGet); try { String responseContent = IOUtils.toString(status.getEntity().getContent()); @@ -201,34 +199,10 @@ public class FormatParameterDstu3Test { @AfterAll public static void afterClassClearContext() throws Exception { - JettyUtil.closeServer(ourServer); TestUtil.randomizeLocaleAndTimezone(); } - @BeforeAll - public static void beforeClass() throws Exception { - ourServer = new Server(0); - - DummyPatientResourceProvider patientProvider = new DummyPatientResourceProvider(); - - ServletHandler proxyHandler = new ServletHandler(); - ourServlet = new RestfulServer(ourCtx); - ourServlet.setFhirContext(ourCtx); - ourServlet.setResourceProviders(patientProvider); - ServletHolder servletHolder = new ServletHolder(ourServlet); - proxyHandler.addServletWithMapping(servletHolder, "/*"); - ourServer.setHandler(proxyHandler); - JettyUtil.startServer(ourServer); - ourPort = JettyUtil.getPortForStartedServer(ourServer); - - PoolingHttpClientConnectionManager connectionManager = new PoolingHttpClientConnectionManager(5000, TimeUnit.MILLISECONDS); - HttpClientBuilder builder = HttpClientBuilder.create(); - builder.setConnectionManager(connectionManager); - ourClient = builder.build(); - - } - - public static class DummyPatientResourceProvider implements IResourceProvider { + private static class DummyPatientResourceProvider implements IResourceProvider { @Override public Class getResourceType() { diff --git a/hapi-fhir-structures-dstu3/src/test/java/ca/uhn/fhir/rest/server/InterceptorDstu3Test.java b/hapi-fhir-structures-dstu3/src/test/java/ca/uhn/fhir/rest/server/InterceptorDstu3Test.java index 1f636645ada..f720e5851b7 100644 --- a/hapi-fhir-structures-dstu3/src/test/java/ca/uhn/fhir/rest/server/InterceptorDstu3Test.java +++ b/hapi-fhir-structures-dstu3/src/test/java/ca/uhn/fhir/rest/server/InterceptorDstu3Test.java @@ -21,7 +21,9 @@ import ca.uhn.fhir.rest.server.interceptor.IServerInterceptor; import ca.uhn.fhir.rest.server.interceptor.InterceptorAdapter; import ca.uhn.fhir.rest.server.interceptor.ServerOperationInterceptorAdapter; import ca.uhn.fhir.rest.server.servlet.ServletRequestDetails; +import ca.uhn.fhir.test.utilities.HttpClientExtension; import ca.uhn.fhir.test.utilities.JettyUtil; +import ca.uhn.fhir.test.utilities.server.RestfulServerExtension; import ca.uhn.fhir.util.TestUtil; import org.apache.commons.io.IOUtils; import org.apache.http.client.methods.CloseableHttpResponse; @@ -33,8 +35,8 @@ import org.apache.http.impl.client.CloseableHttpClient; import org.apache.http.impl.client.HttpClientBuilder; import org.apache.http.impl.conn.PoolingHttpClientConnectionManager; import org.eclipse.jetty.server.Server; -import org.eclipse.jetty.servlet.ServletHandler; -import org.eclipse.jetty.servlet.ServletHolder; +import org.eclipse.jetty.ee10.servlet.ServletHandler; +import org.eclipse.jetty.ee10.servlet.ServletHolder; import org.hl7.fhir.dstu3.model.IdType; import org.hl7.fhir.dstu3.model.IntegerType; import org.hl7.fhir.dstu3.model.OperationOutcome; @@ -46,11 +48,12 @@ import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.RegisterExtension; import org.mockito.ArgumentCaptor; import org.mockito.InOrder; -import javax.servlet.http.HttpServletRequest; -import javax.servlet.http.HttpServletResponse; +import jakarta.servlet.http.HttpServletRequest; +import jakarta.servlet.http.HttpServletResponse; import java.io.IOException; import java.nio.charset.StandardCharsets; import java.util.concurrent.TimeUnit; @@ -73,18 +76,23 @@ import static org.mockito.Mockito.when; public class InterceptorDstu3Test { - private static CloseableHttpClient ourClient; - private static FhirContext ourCtx = FhirContext.forDstu3(); - private static int ourPort; - private static Server ourServer; - private static RestfulServer ourServlet; + private static final FhirContext ourCtx = FhirContext.forDstu3Cached(); private static Patient ourLastPatient; private IServerInterceptor myInterceptor1; private IServerInterceptor myInterceptor2; + @RegisterExtension + private RestfulServerExtension ourServer = new RestfulServerExtension(ourCtx) + .registerProvider(new DummyPatientResourceProvider()) + .withPagingProvider(new FifoMemoryPagingProvider(100)) + .setDefaultPrettyPrint(false); + + @RegisterExtension + private HttpClientExtension ourClient = new HttpClientExtension(); + @AfterEach public void after() { - ourServlet.getInterceptorService().unregisterAllInterceptors(); + ourServer.getInterceptorService().unregisterAllInterceptors(); } @BeforeEach @@ -115,20 +123,20 @@ public class InterceptorDstu3Test { resource.set(requestDetails.getResource()); }; - ourServlet.getInterceptorService().registerAnonymousInterceptor(Pointcut.SERVER_INCOMING_REQUEST_PRE_HANDLED, interceptor); + ourServer.getInterceptorService().registerAnonymousInterceptor(Pointcut.SERVER_INCOMING_REQUEST_PRE_HANDLED, interceptor); try { Parameters p = new Parameters(); p.addParameter().setName("limit").setValue(new IntegerType(123)); String input = ourCtx.newJsonParser().encodeResourceToString(p); - HttpPost post = new HttpPost("http://localhost:" + ourPort + "/Patient/$postOperation"); + HttpPost post = new HttpPost(ourServer.getBaseUrl() + "/Patient/$postOperation"); post.setEntity(new StringEntity(input, ContentType.create("application/fhir+json", Constants.CHARSET_UTF8))); try (CloseableHttpResponse status = ourClient.execute(post)) { assertEquals(200, status.getStatusLine().getStatusCode()); IOUtils.toString(status.getEntity().getContent(), StandardCharsets.UTF_8); } } finally { - ourServlet.unregisterInterceptor(interceptor); + ourServer.unregisterInterceptor(interceptor); } assertNotNull(resource.get()); @@ -148,10 +156,10 @@ public class InterceptorDstu3Test { return true; } }; - ourServlet.registerInterceptor(interceptor); + ourServer.registerInterceptor(interceptor); try { - HttpGet get = new HttpGet("http://localhost:" + ourPort + "/Patient/1"); + HttpGet get = new HttpGet(ourServer.getBaseUrl() + "/Patient/1"); try (CloseableHttpResponse status = ourClient.execute(get)) { String response = IOUtils.toString(status.getEntity().getContent(), Constants.CHARSET_UTF8); assertThat(response, containsString("NAME1")); @@ -160,13 +168,13 @@ public class InterceptorDstu3Test { } } finally { - ourServlet.unregisterInterceptor(interceptor); + ourServer.unregisterInterceptor(interceptor); } } @Test public void testResourceResponseIncluded() throws Exception { - ourServlet.setInterceptors(myInterceptor1, myInterceptor2); + ourServer.getRestfulServer().setInterceptors(myInterceptor1, myInterceptor2); when(myInterceptor1.incomingRequestPreProcessed(nullable(HttpServletRequest.class), nullable(HttpServletResponse.class))).thenReturn(true); when(myInterceptor1.incomingRequestPostProcessed(nullable(ServletRequestDetails.class), nullable(HttpServletRequest.class), nullable(HttpServletResponse.class))).thenReturn(true); @@ -192,7 +200,7 @@ public class InterceptorDstu3Test { String input = createInput(); - HttpPost httpPost = new HttpPost("http://localhost:" + ourPort + "/Patient/$validate"); + HttpPost httpPost = new HttpPost(ourServer.getBaseUrl() + "/Patient/$validate"); httpPost.setEntity(new StringEntity(input, ContentType.create(Constants.CT_FHIR_JSON, "UTF-8"))); try (CloseableHttpResponse status = ourClient.execute(httpPost)) { IOUtils.toString(status.getEntity().getContent(), StandardCharsets.UTF_8); @@ -214,7 +222,7 @@ public class InterceptorDstu3Test { @Test public void testExceptionInProcessingCompletedNormally() throws Exception { - ourServlet.setInterceptors(myInterceptor1); + ourServer.getRestfulServer().setInterceptors(myInterceptor1); when(myInterceptor1.incomingRequestPreProcessed(nullable(HttpServletRequest.class), nullable(HttpServletResponse.class))).thenReturn(true); when(myInterceptor1.incomingRequestPostProcessed(nullable(ServletRequestDetails.class), nullable(HttpServletRequest.class), nullable(HttpServletResponse.class))).thenReturn(true); @@ -226,7 +234,7 @@ public class InterceptorDstu3Test { String input = createInput(); - HttpPost httpPost = new HttpPost("http://localhost:" + ourPort + "/Patient"); + HttpPost httpPost = new HttpPost(ourServer.getBaseUrl() + "/Patient"); httpPost.setEntity(new StringEntity(input, ContentType.create(Constants.CT_FHIR_JSON, "UTF-8"))); try (CloseableHttpResponse status = ourClient.execute(httpPost)) { IOUtils.toString(status.getEntity().getContent(), StandardCharsets.UTF_8); @@ -236,7 +244,7 @@ public class InterceptorDstu3Test { @Test public void testResponseWithNothing() throws Exception { - ourServlet.setInterceptors(myInterceptor1); + ourServer.getRestfulServer().setInterceptors(myInterceptor1); when(myInterceptor1.incomingRequestPreProcessed(nullable(HttpServletRequest.class), nullable(HttpServletResponse.class))).thenReturn(true); when(myInterceptor1.incomingRequestPostProcessed(nullable(ServletRequestDetails.class), nullable(HttpServletRequest.class), nullable(HttpServletResponse.class))).thenReturn(true); @@ -248,7 +256,7 @@ public class InterceptorDstu3Test { String input = createInput(); - HttpPost httpPost = new HttpPost("http://localhost:" + ourPort + "/Patient"); + HttpPost httpPost = new HttpPost(ourServer.getBaseUrl() + "/Patient"); httpPost.setEntity(new StringEntity(input, ContentType.create(Constants.CT_FHIR_JSON, "UTF-8"))); try (CloseableHttpResponse status = ourClient.execute(httpPost)) { assertEquals(201, status.getStatusLine().getStatusCode()); @@ -272,7 +280,7 @@ public class InterceptorDstu3Test { @Test public void testResponseWithOperationOutcome() throws Exception { - ourServlet.setInterceptors(myInterceptor1); + ourServer.getRestfulServer().setInterceptors(myInterceptor1); when(myInterceptor1.incomingRequestPreProcessed(nullable(HttpServletRequest.class), nullable(HttpServletResponse.class))).thenReturn(true); when(myInterceptor1.incomingRequestPostProcessed(nullable(ServletRequestDetails.class), nullable(HttpServletRequest.class), nullable(HttpServletResponse.class))).thenReturn(true); @@ -284,7 +292,7 @@ public class InterceptorDstu3Test { String input = createInput(); - HttpPost httpPost = new HttpPost("http://localhost:" + ourPort + "/Patient/$validate"); + HttpPost httpPost = new HttpPost(ourServer.getBaseUrl() + "/Patient/$validate"); httpPost.setEntity(new StringEntity(input, ContentType.create(Constants.CT_FHIR_JSON, "UTF-8"))); try (CloseableHttpResponse status = ourClient.execute(httpPost)) { IOUtils.toString(status.getEntity().getContent(), StandardCharsets.UTF_8); @@ -315,33 +323,9 @@ public class InterceptorDstu3Test { @AfterAll public static void afterClassClearContext() throws Exception { - JettyUtil.closeServer(ourServer); TestUtil.randomizeLocaleAndTimezone(); } - @BeforeAll - public static void beforeClass() throws Exception { - ourServer = new Server(0); - - DummyPatientResourceProvider patientProvider = new DummyPatientResourceProvider(); - - ServletHandler proxyHandler = new ServletHandler(); - ourServlet = new RestfulServer(ourCtx); - ourServlet.setResourceProviders(patientProvider); - ourServlet.setDefaultResponseEncoding(EncodingEnum.XML); - ServletHolder servletHolder = new ServletHolder(ourServlet); - proxyHandler.addServletWithMapping(servletHolder, "/*"); - ourServer.setHandler(proxyHandler); - JettyUtil.startServer(ourServer); - ourPort = JettyUtil.getPortForStartedServer(ourServer); - - PoolingHttpClientConnectionManager connectionManager = new PoolingHttpClientConnectionManager(5000, TimeUnit.MILLISECONDS); - HttpClientBuilder builder = HttpClientBuilder.create(); - builder.setConnectionManager(connectionManager); - ourClient = builder.build(); - - } - public static class DummyPatientResourceProvider implements IResourceProvider { @Create() diff --git a/hapi-fhir-structures-dstu3/src/test/java/ca/uhn/fhir/rest/server/MetadataCapabilityStatementDstu3Test.java b/hapi-fhir-structures-dstu3/src/test/java/ca/uhn/fhir/rest/server/MetadataCapabilityStatementDstu3Test.java index 62f7c77ac8a..df9e31cd42c 100644 --- a/hapi-fhir-structures-dstu3/src/test/java/ca/uhn/fhir/rest/server/MetadataCapabilityStatementDstu3Test.java +++ b/hapi-fhir-structures-dstu3/src/test/java/ca/uhn/fhir/rest/server/MetadataCapabilityStatementDstu3Test.java @@ -10,7 +10,9 @@ import ca.uhn.fhir.rest.api.EncodingEnum; import ca.uhn.fhir.rest.api.MethodOutcome; import ca.uhn.fhir.rest.param.StringParam; import ca.uhn.fhir.system.HapiSystemProperties; +import ca.uhn.fhir.test.utilities.HttpClientExtension; import ca.uhn.fhir.test.utilities.JettyUtil; +import ca.uhn.fhir.test.utilities.server.RestfulServerExtension; import ca.uhn.fhir.util.TestUtil; import ca.uhn.fhir.util.VersionUtil; import org.apache.commons.io.IOUtils; @@ -22,8 +24,8 @@ import org.apache.http.impl.client.CloseableHttpClient; import org.apache.http.impl.client.HttpClientBuilder; import org.apache.http.impl.conn.PoolingHttpClientConnectionManager; import org.eclipse.jetty.server.Server; -import org.eclipse.jetty.servlet.ServletHandler; -import org.eclipse.jetty.servlet.ServletHolder; +import org.eclipse.jetty.ee10.servlet.ServletHandler; +import org.eclipse.jetty.ee10.servlet.ServletHolder; import org.hl7.fhir.dstu3.hapi.rest.server.ServerCapabilityStatementProvider; import org.hl7.fhir.dstu3.model.CapabilityStatement; import org.hl7.fhir.dstu3.model.Patient; @@ -31,6 +33,7 @@ import org.junit.jupiter.api.AfterAll; import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.RegisterExtension; import java.nio.charset.StandardCharsets; import java.util.List; @@ -45,11 +48,18 @@ import static org.junit.jupiter.api.Assertions.assertEquals; public class MetadataCapabilityStatementDstu3Test { private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(MetadataCapabilityStatementDstu3Test.class); - private static CloseableHttpClient ourClient; - private static FhirContext ourCtx = FhirContext.forDstu3(); - private static int ourPort; - private static Server ourServer; - private static RestfulServer ourServlet; + private static final FhirContext ourCtx = FhirContext.forDstu3Cached(); + + @RegisterExtension + private RestfulServerExtension ourServer = new RestfulServerExtension(ourCtx) + .setDefaultResponseEncoding(EncodingEnum.XML) + .registerProvider(new DummyPatientResourceProvider()) + .withPagingProvider(new FifoMemoryPagingProvider(100)) + .setDefaultPrettyPrint(false) + .withServer(s->s.setServerConformanceProvider(new ServerCapabilityStatementProvider(s).setCache(false))); + + @RegisterExtension + private HttpClientExtension ourClient = new HttpClientExtension(); static { HapiSystemProperties.enableTestMode(); @@ -57,14 +67,14 @@ public class MetadataCapabilityStatementDstu3Test { @AfterEach public void after() { - ourServlet.setServerAddressStrategy(new IncomingRequestAddressStrategy()); + ourServer.setServerAddressStrategy(new IncomingRequestAddressStrategy()); } @Test public void testElements() throws Exception { String output; - HttpRequestBase httpPost = new HttpGet("http://localhost:" + ourPort + "/metadata?_elements=fhirVersion&_pretty=true"); + HttpRequestBase httpPost = new HttpGet(ourServer.getBaseUrl() + "/metadata?_elements=fhirVersion&_pretty=true"); CloseableHttpResponse status = ourClient.execute(httpPost); try { output = IOUtils.toString(status.getEntity().getContent(), StandardCharsets.UTF_8); @@ -81,7 +91,7 @@ public class MetadataCapabilityStatementDstu3Test { public void testHttpMethods() throws Exception { String output; - HttpRequestBase httpPost = new HttpGet("http://localhost:" + ourPort + "/metadata"); + HttpRequestBase httpPost = new HttpGet(ourServer.getBaseUrl() + "/metadata"); CloseableHttpResponse status = ourClient.execute(httpPost); try { output = IOUtils.toString(status.getEntity().getContent(), StandardCharsets.UTF_8); @@ -94,7 +104,7 @@ public class MetadataCapabilityStatementDstu3Test { } try { - httpPost = new HttpPost("http://localhost:" + ourPort + "/metadata"); + httpPost = new HttpPost(ourServer.getBaseUrl() + "/metadata"); status = ourClient.execute(httpPost); output = IOUtils.toString(status.getEntity().getContent(), StandardCharsets.UTF_8); assertEquals(405, status.getStatusLine().getStatusCode()); @@ -110,7 +120,7 @@ public class MetadataCapabilityStatementDstu3Test { * would be interpreted as a read on ID "metadata" */ try { - httpPost = new HttpGet("http://localhost:" + ourPort + "/Patient/metadata"); + httpPost = new HttpGet(ourServer.getBaseUrl() + "/Patient/metadata"); status = ourClient.execute(httpPost); output = IOUtils.toString(status.getEntity().getContent(), StandardCharsets.UTF_8); assertEquals(400, status.getStatusLine().getStatusCode()); @@ -123,7 +133,7 @@ public class MetadataCapabilityStatementDstu3Test { public void testResponseContainsBaseUrl() throws Exception { String output; - HttpRequestBase httpPost = new HttpGet("http://localhost:" + ourPort + "/metadata?_format=json"); + HttpRequestBase httpPost = new HttpGet(ourServer.getBaseUrl() + "/metadata?_format=json"); CloseableHttpResponse status = ourClient.execute(httpPost); try { output = IOUtils.toString(status.getEntity().getContent(), StandardCharsets.UTF_8); @@ -131,7 +141,7 @@ public class MetadataCapabilityStatementDstu3Test { ourLog.info(output); CapabilityStatement cs = ourCtx.newJsonParser().parseResource(CapabilityStatement.class, output); - assertEquals("http://localhost:" + ourPort + "/", cs.getImplementation().getUrl()); + assertEquals(ourServer.getBaseUrl() + "/", cs.getImplementation().getUrl()); } finally { IOUtils.closeQuietly(status.getEntity().getContent()); } @@ -139,11 +149,11 @@ public class MetadataCapabilityStatementDstu3Test { @Test public void testResponseContainsBaseUrlFixed() throws Exception { - ourServlet.setServerAddressStrategy(new HardcodedServerAddressStrategy("http://foo/bar")); + ourServer.setServerAddressStrategy(new HardcodedServerAddressStrategy("http://foo/bar")); String output; - HttpRequestBase httpPost = new HttpGet("http://localhost:" + ourPort + "/metadata?_format=json"); + HttpRequestBase httpPost = new HttpGet(ourServer.getBaseUrl() + "/metadata?_format=json"); CloseableHttpResponse status = ourClient.execute(httpPost); try { output = IOUtils.toString(status.getEntity().getContent(), StandardCharsets.UTF_8); @@ -162,7 +172,7 @@ public class MetadataCapabilityStatementDstu3Test { String output; // With - HttpRequestBase httpPost = new HttpGet("http://localhost:" + ourPort + "/metadata?_summary=true&_pretty=true"); + HttpRequestBase httpPost = new HttpGet(ourServer.getBaseUrl() + "/metadata?_summary=true&_pretty=true"); CloseableHttpResponse status = ourClient.execute(httpPost); try { output = IOUtils.toString(status.getEntity().getContent(), StandardCharsets.UTF_8); @@ -176,7 +186,7 @@ public class MetadataCapabilityStatementDstu3Test { } // Without - httpPost = new HttpGet("http://localhost:" + ourPort + "/metadata?_pretty=true"); + httpPost = new HttpGet(ourServer.getBaseUrl() + "/metadata?_pretty=true"); status = ourClient.execute(httpPost); try { output = IOUtils.toString(status.getEntity().getContent(), StandardCharsets.UTF_8); @@ -192,36 +202,9 @@ public class MetadataCapabilityStatementDstu3Test { @AfterAll public static void afterClassClearContext() throws Exception { - JettyUtil.closeServer(ourServer); TestUtil.randomizeLocaleAndTimezone(); } - @BeforeAll - public static void beforeClass() throws Exception { - ourServer = new Server(0); - - DummyPatientResourceProvider patientProvider = new DummyPatientResourceProvider(); - - ServletHandler proxyHandler = new ServletHandler(); - ourServlet = new RestfulServer(ourCtx); - ourServlet.setResourceProviders(patientProvider); - ourServlet.setDefaultResponseEncoding(EncodingEnum.XML); - - ourServlet.setServerConformanceProvider(new ServerCapabilityStatementProvider(ourServlet).setCache(false)); - - ServletHolder servletHolder = new ServletHolder(ourServlet); - proxyHandler.addServletWithMapping(servletHolder, "/*"); - ourServer.setHandler(proxyHandler); - JettyUtil.startServer(ourServer); - ourPort = JettyUtil.getPortForStartedServer(ourServer); - - PoolingHttpClientConnectionManager connectionManager = new PoolingHttpClientConnectionManager(5000, TimeUnit.MILLISECONDS); - HttpClientBuilder builder = HttpClientBuilder.create(); - builder.setConnectionManager(connectionManager); - ourClient = builder.build(); - - } - @SuppressWarnings("unused") public static class DummyPatientResourceProvider implements IResourceProvider { diff --git a/hapi-fhir-structures-dstu3/src/test/java/ca/uhn/fhir/rest/server/MetadataConformanceDstu3Test.java b/hapi-fhir-structures-dstu3/src/test/java/ca/uhn/fhir/rest/server/MetadataConformanceDstu3Test.java index 3a3203833da..60bfcf009e9 100644 --- a/hapi-fhir-structures-dstu3/src/test/java/ca/uhn/fhir/rest/server/MetadataConformanceDstu3Test.java +++ b/hapi-fhir-structures-dstu3/src/test/java/ca/uhn/fhir/rest/server/MetadataConformanceDstu3Test.java @@ -9,7 +9,8 @@ import ca.uhn.fhir.rest.annotation.Validate; import ca.uhn.fhir.rest.api.EncodingEnum; import ca.uhn.fhir.rest.api.MethodOutcome; import ca.uhn.fhir.rest.param.StringParam; -import ca.uhn.fhir.test.utilities.JettyUtil; +import ca.uhn.fhir.test.utilities.HttpClientExtension; +import ca.uhn.fhir.test.utilities.server.RestfulServerExtension; import ca.uhn.fhir.util.TestUtil; import ca.uhn.fhir.util.VersionUtil; import org.apache.commons.io.IOUtils; @@ -18,21 +19,13 @@ import org.apache.http.client.methods.HttpGet; import org.apache.http.client.methods.HttpOptions; import org.apache.http.client.methods.HttpPost; import org.apache.http.client.methods.HttpRequestBase; -import org.apache.http.impl.client.CloseableHttpClient; -import org.apache.http.impl.client.HttpClientBuilder; -import org.apache.http.impl.conn.PoolingHttpClientConnectionManager; -import org.eclipse.jetty.server.Server; -import org.eclipse.jetty.servlet.ServletHandler; -import org.eclipse.jetty.servlet.ServletHolder; -import org.hl7.fhir.dstu3.hapi.rest.server.ServerCapabilityStatementProvider; import org.hl7.fhir.dstu3.model.Patient; import org.junit.jupiter.api.AfterAll; -import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.RegisterExtension; import java.nio.charset.StandardCharsets; import java.util.List; -import java.util.concurrent.TimeUnit; import static org.hamcrest.MatcherAssert.assertThat; import static org.hamcrest.Matchers.containsString; @@ -43,18 +36,24 @@ import static org.junit.jupiter.api.Assertions.assertEquals; public class MetadataConformanceDstu3Test { private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(MetadataConformanceDstu3Test.class); - private static CloseableHttpClient ourClient; - private static FhirContext ourCtx = FhirContext.forDstu3(); - private static int ourPort; - private static Server ourServer; - private static RestfulServer ourServlet; + private static final FhirContext ourCtx = FhirContext.forDstu3Cached(); + + @RegisterExtension + private RestfulServerExtension ourServer = new RestfulServerExtension(ourCtx) + .setDefaultResponseEncoding(EncodingEnum.XML) + .registerProvider(new DummyPatientResourceProvider()) + .withPagingProvider(new FifoMemoryPagingProvider(100)) + .setDefaultPrettyPrint(false); + + @RegisterExtension + private HttpClientExtension ourClient = new HttpClientExtension(); @Test public void testSummary() throws Exception { String output; // With - HttpRequestBase httpPost = new HttpGet("http://localhost:" + ourPort + "/metadata?_summary=true&_pretty=true"); + HttpRequestBase httpPost = new HttpGet(ourServer.getBaseUrl() + "/metadata?_summary=true&_pretty=true"); CloseableHttpResponse status = ourClient.execute(httpPost); try { output = IOUtils.toString(status.getEntity().getContent(), StandardCharsets.UTF_8); @@ -68,7 +67,7 @@ public class MetadataConformanceDstu3Test { } // Without - httpPost = new HttpGet("http://localhost:" + ourPort + "/metadata?_pretty=true"); + httpPost = new HttpGet(ourServer.getBaseUrl() + "/metadata?_pretty=true"); status = ourClient.execute(httpPost); try { output = IOUtils.toString(status.getEntity().getContent(), StandardCharsets.UTF_8); @@ -86,7 +85,7 @@ public class MetadataConformanceDstu3Test { public void testElements() throws Exception { String output; - HttpRequestBase httpPost = new HttpGet("http://localhost:" + ourPort + "/metadata?_elements=fhirVersion&_pretty=true"); + HttpRequestBase httpPost = new HttpGet(ourServer.getBaseUrl() + "/metadata?_elements=fhirVersion&_pretty=true"); CloseableHttpResponse status = ourClient.execute(httpPost); try { output = IOUtils.toString(status.getEntity().getContent(), StandardCharsets.UTF_8); @@ -103,7 +102,7 @@ public class MetadataConformanceDstu3Test { public void testHttpMethods() throws Exception { String output; - HttpRequestBase httpOperation = new HttpGet("http://localhost:" + ourPort + "/metadata"); + HttpRequestBase httpOperation = new HttpGet(ourServer.getBaseUrl() + "/metadata"); try (CloseableHttpResponse status = ourClient.execute(httpOperation)) { output = IOUtils.toString(status.getEntity().getContent(), StandardCharsets.UTF_8); assertEquals(200, status.getStatusLine().getStatusCode()); @@ -112,7 +111,7 @@ public class MetadataConformanceDstu3Test { assertThat(status.getFirstHeader("X-Powered-By").getValue(), containsString("REST Server (FHIR Server; FHIR " + ourCtx.getVersion().getVersion().getFhirVersionString() + "/" + ourCtx.getVersion().getVersion().name() + ")")); } - httpOperation = new HttpOptions("http://localhost:" + ourPort); + httpOperation = new HttpOptions(ourServer.getBaseUrl()); try (CloseableHttpResponse status = ourClient.execute(httpOperation)) { output = IOUtils.toString(status.getEntity().getContent(), StandardCharsets.UTF_8); assertEquals(200, status.getStatusLine().getStatusCode()); @@ -121,7 +120,7 @@ public class MetadataConformanceDstu3Test { assertThat(status.getFirstHeader("X-Powered-By").getValue(), containsString("REST Server (FHIR Server; FHIR " + ourCtx.getVersion().getVersion().getFhirVersionString() + "/" + ourCtx.getVersion().getVersion().name() + ")")); } - httpOperation = new HttpPost("http://localhost:" + ourPort + "/metadata"); + httpOperation = new HttpPost(ourServer.getBaseUrl() + "/metadata"); try (CloseableHttpResponse status = ourClient.execute(httpOperation)) { output = IOUtils.toString(status.getEntity().getContent(), StandardCharsets.UTF_8); assertEquals(405, status.getStatusLine().getStatusCode()); @@ -132,7 +131,7 @@ public class MetadataConformanceDstu3Test { * There is no @read on the RP below, so this should fail. Otherwise it * would be interpreted as a read on ID "metadata" */ - httpOperation = new HttpGet("http://localhost:" + ourPort + "/Patient/metadata"); + httpOperation = new HttpGet(ourServer.getBaseUrl() + "/Patient/metadata"); try (CloseableHttpResponse status = ourClient.execute(httpOperation)) { assertEquals(400, status.getStatusLine().getStatusCode()); } @@ -159,32 +158,7 @@ public class MetadataConformanceDstu3Test { @AfterAll public static void afterClassClearContext() throws Exception { - JettyUtil.closeServer(ourServer); TestUtil.randomizeLocaleAndTimezone(); } - @BeforeAll - public static void beforeClass() throws Exception { - ourServer = new Server(0); - - DummyPatientResourceProvider patientProvider = new DummyPatientResourceProvider(); - - ServletHandler proxyHandler = new ServletHandler(); - ourServlet = new RestfulServer(ourCtx); - ourServlet.setServerConformanceProvider(new ServerCapabilityStatementProvider(ourServlet)); - ourServlet.setResourceProviders(patientProvider); - ourServlet.setDefaultResponseEncoding(EncodingEnum.XML); - ServletHolder servletHolder = new ServletHolder(ourServlet); - proxyHandler.addServletWithMapping(servletHolder, "/*"); - ourServer.setHandler(proxyHandler); - JettyUtil.startServer(ourServer); - ourPort = JettyUtil.getPortForStartedServer(ourServer); - - PoolingHttpClientConnectionManager connectionManager = new PoolingHttpClientConnectionManager(5000, TimeUnit.MILLISECONDS); - HttpClientBuilder builder = HttpClientBuilder.create(); - builder.setConnectionManager(connectionManager); - ourClient = builder.build(); - - } - } diff --git a/hapi-fhir-structures-dstu3/src/test/java/ca/uhn/fhir/rest/server/OperationServerDstu3Test.java b/hapi-fhir-structures-dstu3/src/test/java/ca/uhn/fhir/rest/server/OperationServerDstu3Test.java index a4f1f28041c..f8acf9eaa86 100644 --- a/hapi-fhir-structures-dstu3/src/test/java/ca/uhn/fhir/rest/server/OperationServerDstu3Test.java +++ b/hapi-fhir-structures-dstu3/src/test/java/ca/uhn/fhir/rest/server/OperationServerDstu3Test.java @@ -10,7 +10,9 @@ import ca.uhn.fhir.rest.api.EncodingEnum; import ca.uhn.fhir.rest.api.server.IBundleProvider; import ca.uhn.fhir.rest.client.api.IGenericClient; import ca.uhn.fhir.rest.client.interceptor.LoggingInterceptor; +import ca.uhn.fhir.test.utilities.HttpClientExtension; import ca.uhn.fhir.test.utilities.JettyUtil; +import ca.uhn.fhir.test.utilities.server.RestfulServerExtension; import ca.uhn.fhir.util.TestUtil; import org.apache.commons.io.IOUtils; import org.apache.http.HttpResponse; @@ -23,8 +25,8 @@ import org.apache.http.impl.client.CloseableHttpClient; import org.apache.http.impl.client.HttpClientBuilder; import org.apache.http.impl.conn.PoolingHttpClientConnectionManager; import org.eclipse.jetty.server.Server; -import org.eclipse.jetty.servlet.ServletHandler; -import org.eclipse.jetty.servlet.ServletHolder; +import org.eclipse.jetty.ee10.servlet.ServletHandler; +import org.eclipse.jetty.ee10.servlet.ServletHolder; import org.hl7.fhir.dstu3.model.Bundle; import org.hl7.fhir.dstu3.model.CapabilityStatement; import org.hl7.fhir.dstu3.model.CapabilityStatement.CapabilityStatementRestOperationComponent; @@ -42,6 +44,7 @@ import org.junit.jupiter.api.AfterAll; import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.RegisterExtension; import java.nio.charset.StandardCharsets; import java.util.ArrayList; @@ -57,8 +60,7 @@ import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertNull; public class OperationServerDstu3Test { - private static CloseableHttpClient ourClient; - private static FhirContext ourCtx; + private static final FhirContext ourCtx = FhirContext.forDstu3Cached(); private static IdType ourLastId; private static String ourLastMethod; @@ -68,10 +70,19 @@ public class OperationServerDstu3Test { private static Money ourLastParamMoney1; private static UnsignedIntType ourLastParamUnsignedInt1; private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(OperationServerDstu3Test.class); - private static int ourPort; - private static Server ourServer; + @RegisterExtension + private RestfulServerExtension ourServer = new RestfulServerExtension(ourCtx) + .setDefaultResponseEncoding(EncodingEnum.XML) + .registerProvider(new PatientProvider()) + .registerProvider(new PlainProvider()) + .withPagingProvider(new FifoMemoryPagingProvider(10).setDefaultPageSize(2)) + .setDefaultPrettyPrint(false); + + @RegisterExtension + private HttpClientExtension ourClient = new HttpClientExtension(); private IGenericClient myFhirClient; + @BeforeEach public void before() { ourLastParam1 = null; @@ -82,7 +93,7 @@ public class OperationServerDstu3Test { ourLastId = null; ourLastMethod = ""; - myFhirClient = ourCtx.newRestfulGenericClient("http://localhost:" + ourPort); + myFhirClient = ourServer.getFhirClient(); } @@ -155,7 +166,7 @@ public class OperationServerDstu3Test { public void testInstanceEverythingGet() throws Exception { // Try with a GET - HttpGet httpGet = new HttpGet("http://localhost:" + ourPort + "/Patient/123/$everything"); + HttpGet httpGet = new HttpGet(ourServer.getBaseUrl() + "/Patient/123/$everything"); CloseableHttpResponse status = ourClient.execute(httpGet); assertEquals(200, status.getStatusLine().getStatusCode()); @@ -170,7 +181,7 @@ public class OperationServerDstu3Test { @Test public void testInstanceEverythingHapiClient() throws Exception { - ourCtx.newRestfulGenericClient("http://localhost:" + ourPort).operation().onInstance(new IdType("Patient/123")).named("$everything").withParameters(new Parameters()).execute(); + ourCtx.newRestfulGenericClient(ourServer.getBaseUrl()).operation().onInstance(new IdType("Patient/123")).named("$everything").withParameters(new Parameters()).execute(); assertEquals("instance $everything", ourLastMethod); assertEquals("Patient/123", ourLastId.toUnqualifiedVersionless().getValue()); @@ -183,7 +194,7 @@ public class OperationServerDstu3Test { String inParamsStr = ourCtx.newXmlParser().encodeResourceToString(new Parameters()); // Try with a POST - HttpPost httpPost = new HttpPost("http://localhost:" + ourPort + "/Patient/123/$everything"); + HttpPost httpPost = new HttpPost(ourServer.getBaseUrl() + "/Patient/123/$everything"); httpPost.setEntity(new StringEntity(inParamsStr, ContentType.create(Constants.CT_FHIR_XML, "UTF-8"))); HttpResponse status = ourClient.execute(httpPost); @@ -199,7 +210,7 @@ public class OperationServerDstu3Test { @Test public void testOperationCantUseGetIfItIsntIdempotent() throws Exception { - HttpGet httpPost = new HttpGet("http://localhost:" + ourPort + "/Patient/123/$OP_INSTANCE"); + HttpGet httpPost = new HttpGet(ourServer.getBaseUrl() + "/Patient/123/$OP_INSTANCE"); HttpResponse status = ourClient.execute(httpPost); assertEquals(Constants.STATUS_HTTP_405_METHOD_NOT_ALLOWED, status.getStatusLine().getStatusCode()); @@ -216,7 +227,7 @@ public class OperationServerDstu3Test { p.addParameter().setName("PARAM1").setValue(new IntegerType(123)); String inParamsStr = ourCtx.newXmlParser().encodeResourceToString(p); - HttpPost httpPost = new HttpPost("http://localhost:" + ourPort + "/Patient/123/$OP_INSTANCE"); + HttpPost httpPost = new HttpPost(ourServer.getBaseUrl() + "/Patient/123/$OP_INSTANCE"); httpPost.setEntity(new StringEntity(inParamsStr, ContentType.create(Constants.CT_FHIR_XML, "UTF-8"))); CloseableHttpResponse status = ourClient.execute(httpPost); try { @@ -235,7 +246,7 @@ public class OperationServerDstu3Test { p.addParameter().setName("PARAM2").setResource(new Patient().setActive(true)); String inParamsStr = ourCtx.newXmlParser().encodeResourceToString(p); - HttpPost httpPost = new HttpPost("http://localhost:" + ourPort + "/Patient/123/$OP_INSTANCE"); + HttpPost httpPost = new HttpPost(ourServer.getBaseUrl() + "/Patient/123/$OP_INSTANCE"); httpPost.setEntity(new StringEntity(inParamsStr, ContentType.create(Constants.CT_FHIR_XML, "UTF-8"))); HttpResponse status = ourClient.execute(httpPost); @@ -255,7 +266,7 @@ public class OperationServerDstu3Test { * Against type should fail */ - httpPost = new HttpPost("http://localhost:" + ourPort + "/Patient/$OP_INSTANCE"); + httpPost = new HttpPost(ourServer.getBaseUrl() + "/Patient/$OP_INSTANCE"); httpPost.setEntity(new StringEntity(inParamsStr, ContentType.create(Constants.CT_FHIR_XML, "UTF-8"))); status = ourClient.execute(httpPost); @@ -273,7 +284,7 @@ public class OperationServerDstu3Test { p.addParameter().setName("PARAM2").setResource(new Patient().setActive(true)); String inParamsStr = ourCtx.newXmlParser().encodeResourceToString(p); - HttpPost httpPost = new HttpPost("http://localhost:" + ourPort + "/Patient/123/$OP_INSTANCE_OR_TYPE"); + HttpPost httpPost = new HttpPost(ourServer.getBaseUrl() + "/Patient/123/$OP_INSTANCE_OR_TYPE"); httpPost.setEntity(new StringEntity(inParamsStr, ContentType.create(Constants.CT_FHIR_XML, "UTF-8"))); HttpResponse status = ourClient.execute(httpPost); @@ -298,7 +309,7 @@ public class OperationServerDstu3Test { p.addParameter().setName("PARAM2").setResource(new Patient().setActive(true)); String inParamsStr = ourCtx.newXmlParser().encodeResourceToString(p); - HttpPost httpPost = new HttpPost("http://localhost:" + ourPort + "/Patient/$OP_INSTANCE_OR_TYPE"); + HttpPost httpPost = new HttpPost(ourServer.getBaseUrl() + "/Patient/$OP_INSTANCE_OR_TYPE"); httpPost.setEntity(new StringEntity(inParamsStr, ContentType.create(Constants.CT_FHIR_XML, "UTF-8"))); CloseableHttpResponse status = ourClient.execute(httpPost); @@ -322,7 +333,7 @@ public class OperationServerDstu3Test { p.addParameter().setName("PARAM2").setResource(new Patient().setActive(true)); String inParamsStr = ourCtx.newXmlParser().encodeResourceToString(p); - HttpPost httpPost = new HttpPost("http://localhost:" + ourPort + "/$OP_SERVER"); + HttpPost httpPost = new HttpPost(ourServer.getBaseUrl() + "/$OP_SERVER"); httpPost.setEntity(new StringEntity(inParamsStr, ContentType.create(Constants.CT_FHIR_XML, "UTF-8"))); HttpResponse status = ourClient.execute(httpPost); @@ -345,7 +356,7 @@ public class OperationServerDstu3Test { p.addParameter().setName("PARAM2").setResource(new Patient().setActive(true)); String inParamsStr = ourCtx.newXmlParser().encodeResourceToString(p); - HttpPost httpPost = new HttpPost("http://localhost:" + ourPort + "/Patient/$OP_TYPE"); + HttpPost httpPost = new HttpPost(ourServer.getBaseUrl() + "/Patient/$OP_TYPE"); httpPost.setEntity(new StringEntity(inParamsStr, ContentType.create(Constants.CT_FHIR_XML, "UTF-8"))); HttpResponse status = ourClient.execute(httpPost); @@ -368,7 +379,7 @@ public class OperationServerDstu3Test { p.addParameter().setName("PARAM2").setResource(new Patient().setActive(true)); String inParamsStr = ourCtx.newXmlParser().encodeResourceToString(p); - HttpPost httpPost = new HttpPost("http://localhost:" + ourPort + "/Patient/$OP_TYPE_RET_BUNDLE"); + HttpPost httpPost = new HttpPost(ourServer.getBaseUrl() + "/Patient/$OP_TYPE_RET_BUNDLE"); httpPost.setEntity(new StringEntity(inParamsStr, ContentType.create(Constants.CT_FHIR_XML, "UTF-8"))); HttpResponse status = ourClient.execute(httpPost); @@ -386,7 +397,7 @@ public class OperationServerDstu3Test { @Test public void testOperationWithBundleProviderResponse() throws Exception { - HttpGet httpPost = new HttpGet("http://localhost:" + ourPort + "/$OP_INSTANCE_BUNDLE_PROVIDER?_pretty=true"); + HttpGet httpPost = new HttpGet(ourServer.getBaseUrl() + "/$OP_INSTANCE_BUNDLE_PROVIDER?_pretty=true"); HttpResponse status = ourClient.execute(httpPost); assertEquals(200, status.getStatusLine().getStatusCode()); @@ -399,7 +410,7 @@ public class OperationServerDstu3Test { @Test public void testOperationWithGetUsingParams() throws Exception { - HttpGet httpGet = new HttpGet("http://localhost:" + ourPort + "/Patient/$OP_TYPE?PARAM1=PARAM1val"); + HttpGet httpGet = new HttpGet(ourServer.getBaseUrl() + "/Patient/$OP_TYPE?PARAM1=PARAM1val"); HttpResponse status = ourClient.execute(httpGet); assertEquals(200, status.getStatusLine().getStatusCode()); @@ -416,7 +427,7 @@ public class OperationServerDstu3Test { @Test public void testOperationWithGetUsingParamsFailsWithNonPrimitive() throws Exception { - HttpGet httpGet = new HttpGet("http://localhost:" + ourPort + "/Patient/$OP_TYPE?PARAM1=PARAM1val&PARAM2=foo"); + HttpGet httpGet = new HttpGet(ourServer.getBaseUrl() + "/Patient/$OP_TYPE?PARAM1=PARAM1val&PARAM2=foo"); HttpResponse status = ourClient.execute(httpGet); assertEquals(405, status.getStatusLine().getStatusCode()); @@ -436,7 +447,7 @@ public class OperationServerDstu3Test { p.addParameter().setName("PARAM3").setValue(new StringType("PARAM3val2")); String inParamsStr = ourCtx.newXmlParser().encodeResourceToString(p); - HttpPost httpPost = new HttpPost("http://localhost:" + ourPort + "/$OP_SERVER_LIST_PARAM"); + HttpPost httpPost = new HttpPost(ourServer.getBaseUrl() + "/$OP_SERVER_LIST_PARAM"); httpPost.setEntity(new StringEntity(inParamsStr, ContentType.create(Constants.CT_FHIR_XML, "UTF-8"))); HttpResponse status = ourClient.execute(httpPost); @@ -461,7 +472,7 @@ public class OperationServerDstu3Test { p.addParameter().setName("PARAM1").setValue(new IntegerType("123")); String inParamsStr = ourCtx.newXmlParser().encodeResourceToString(p); - HttpPost httpPost = new HttpPost("http://localhost:" + ourPort + "/Patient/$OP_PROFILE_DT"); + HttpPost httpPost = new HttpPost(ourServer.getBaseUrl() + "/Patient/$OP_PROFILE_DT"); httpPost.setEntity(new StringEntity(inParamsStr, ContentType.create(Constants.CT_FHIR_XML, "UTF-8"))); HttpResponse status = ourClient.execute(httpPost); @@ -482,7 +493,7 @@ public class OperationServerDstu3Test { p.addParameter().setName("PARAM1").setValue(money); String inParamsStr = ourCtx.newXmlParser().encodeResourceToString(p); - HttpPost httpPost = new HttpPost("http://localhost:" + ourPort + "/Patient/$OP_PROFILE_DT2"); + HttpPost httpPost = new HttpPost(ourServer.getBaseUrl() + "/Patient/$OP_PROFILE_DT2"); httpPost.setEntity(new StringEntity(inParamsStr, ContentType.create(Constants.CT_FHIR_XML, "UTF-8"))); HttpResponse status = ourClient.execute(httpPost); @@ -497,7 +508,7 @@ public class OperationServerDstu3Test { @Test public void testOperationWithProfileDatatypeUrl() throws Exception { - HttpGet httpPost = new HttpGet("http://localhost:" + ourPort + "/Patient/$OP_PROFILE_DT?PARAM1=123"); + HttpGet httpPost = new HttpGet(ourServer.getBaseUrl() + "/Patient/$OP_PROFILE_DT?PARAM1=123"); HttpResponse status = ourClient.execute(httpPost); assertEquals(200, status.getStatusLine().getStatusCode()); @@ -514,7 +525,7 @@ public class OperationServerDstu3Test { p.addParameter().setName("PARAM2").setResource(new Patient().setActive(true)); String inParamsStr = ourCtx.newXmlParser().encodeResourceToString(p); - HttpPost httpPost = new HttpPost("http://localhost:" + ourPort + "/Patient/$OP_TYPE"); + HttpPost httpPost = new HttpPost(ourServer.getBaseUrl() + "/Patient/$OP_TYPE"); httpPost.setEntity(new StringEntity(inParamsStr, ContentType.create(Constants.CT_FHIR_XML, "UTF-8"))); HttpResponse status = ourClient.execute(httpPost); @@ -530,7 +541,7 @@ public class OperationServerDstu3Test { @Test public void testReadWithOperations() throws Exception { - HttpGet httpGet = new HttpGet("http://localhost:" + ourPort + "/Patient/123"); + HttpGet httpGet = new HttpGet(ourServer.getBaseUrl() + "/Patient/123"); HttpResponse status = ourClient.execute(httpGet); assertEquals(200, status.getStatusLine().getStatusCode()); @@ -542,38 +553,9 @@ public class OperationServerDstu3Test { @AfterAll public static void afterClassClearContext() throws Exception { - JettyUtil.closeServer(ourServer); TestUtil.randomizeLocaleAndTimezone(); } - @BeforeAll - public static void beforeClass() throws Exception { - ourCtx = FhirContext.forDstu3(); - ourServer = new Server(0); - - ServletHandler proxyHandler = new ServletHandler(); - RestfulServer servlet = new RestfulServer(ourCtx); - servlet.setDefaultResponseEncoding(EncodingEnum.XML); - - servlet.setPagingProvider(new FifoMemoryPagingProvider(10).setDefaultPageSize(2)); - - servlet.setFhirContext(ourCtx); - servlet.setResourceProviders(new PatientProvider()); - servlet.registerProvider(new PlainProvider()); - ServletHolder servletHolder = new ServletHolder(servlet); - proxyHandler.addServletWithMapping(servletHolder, "/*"); - ourServer.setHandler(proxyHandler); - JettyUtil.startServer(ourServer); - ourPort = JettyUtil.getPortForStartedServer(ourServer); - - PoolingHttpClientConnectionManager connectionManager = new PoolingHttpClientConnectionManager(5000, TimeUnit.MILLISECONDS); - HttpClientBuilder builder = HttpClientBuilder.create(); - builder.setConnectionManager(connectionManager); - ourClient = builder.build(); - - } - - public static class PatientProvider implements IResourceProvider { @Override diff --git a/hapi-fhir-structures-dstu3/src/test/java/ca/uhn/fhir/rest/server/OperationServerWithSearchParamTypesDstu3Test.java b/hapi-fhir-structures-dstu3/src/test/java/ca/uhn/fhir/rest/server/OperationServerWithSearchParamTypesDstu3Test.java index 76335fec772..3036e082ac3 100644 --- a/hapi-fhir-structures-dstu3/src/test/java/ca/uhn/fhir/rest/server/OperationServerWithSearchParamTypesDstu3Test.java +++ b/hapi-fhir-structures-dstu3/src/test/java/ca/uhn/fhir/rest/server/OperationServerWithSearchParamTypesDstu3Test.java @@ -4,6 +4,7 @@ import ca.uhn.fhir.context.FhirContext; import ca.uhn.fhir.rest.annotation.Operation; import ca.uhn.fhir.rest.annotation.OperationParam; import ca.uhn.fhir.rest.api.Constants; +import ca.uhn.fhir.rest.api.EncodingEnum; import ca.uhn.fhir.rest.api.server.RequestDetails; import ca.uhn.fhir.rest.param.StringAndListParam; import ca.uhn.fhir.rest.param.StringOrListParam; @@ -13,7 +14,9 @@ import ca.uhn.fhir.rest.param.TokenOrListParam; import ca.uhn.fhir.rest.param.TokenParam; import ca.uhn.fhir.rest.param.TokenParamModifier; import ca.uhn.fhir.rest.server.servlet.ServletRequestDetails; +import ca.uhn.fhir.test.utilities.HttpClientExtension; import ca.uhn.fhir.test.utilities.JettyUtil; +import ca.uhn.fhir.test.utilities.server.RestfulServerExtension; import ca.uhn.fhir.util.TestUtil; import ca.uhn.fhir.util.UrlUtil; import org.apache.commons.io.IOUtils; @@ -26,8 +29,8 @@ import org.apache.http.impl.client.CloseableHttpClient; import org.apache.http.impl.client.HttpClientBuilder; import org.apache.http.impl.conn.PoolingHttpClientConnectionManager; import org.eclipse.jetty.server.Server; -import org.eclipse.jetty.servlet.ServletHandler; -import org.eclipse.jetty.servlet.ServletHolder; +import org.eclipse.jetty.ee10.servlet.ServletHandler; +import org.eclipse.jetty.ee10.servlet.ServletHolder; import org.hl7.fhir.dstu3.hapi.rest.server.ServerCapabilityStatementProvider; import org.hl7.fhir.dstu3.model.CapabilityStatement; import org.hl7.fhir.dstu3.model.IdType; @@ -41,8 +44,10 @@ import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; -import javax.servlet.ServletConfig; -import javax.servlet.http.HttpServletRequest; +import jakarta.servlet.ServletConfig; +import jakarta.servlet.http.HttpServletRequest; +import org.junit.jupiter.api.extension.RegisterExtension; + import java.util.Collections; import java.util.List; import java.util.concurrent.TimeUnit; @@ -55,15 +60,24 @@ import static org.mockito.Mockito.mock; import static org.mockito.Mockito.when; public class OperationServerWithSearchParamTypesDstu3Test { - private static CloseableHttpClient ourClient; - private static FhirContext ourCtx; + private static final FhirContext ourCtx = FhirContext.forDstu3Cached(); private static String ourLastMethod; private static List ourLastParamValStr; private static List ourLastParamValTok; private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(OperationServerWithSearchParamTypesDstu3Test.class); - private static int ourPort; - private static Server ourServer; + + + @RegisterExtension + private RestfulServerExtension ourServer = new RestfulServerExtension(ourCtx) + .setDefaultResponseEncoding(EncodingEnum.XML) + .registerProvider(new PatientProvider()) + .withPagingProvider(new FifoMemoryPagingProvider(10).setDefaultPageSize(2)) + .setDefaultPrettyPrint(false); + + @RegisterExtension + private HttpClientExtension ourClient = new HttpClientExtension(); + @BeforeEach public void before() { ourLastMethod = ""; @@ -95,7 +109,7 @@ public class OperationServerWithSearchParamTypesDstu3Test { p.addParameter().setName("valtok").setValue(new StringType("VALTOK2A|VALTOK2B")); String inParamsStr = ourCtx.newXmlParser().encodeResourceToString(p); - HttpPost httpPost = new HttpPost("http://localhost:" + ourPort + "/Patient/$andlist"); + HttpPost httpPost = new HttpPost(ourServer.getBaseUrl() + "/Patient/$andlist"); httpPost.setEntity(new StringEntity(inParamsStr, ContentType.create(Constants.CT_FHIR_XML, "UTF-8"))); HttpResponse status = ourClient.execute(httpPost); @@ -121,7 +135,7 @@ public class OperationServerWithSearchParamTypesDstu3Test { @Test public void testEscapedOperationName() throws Exception { - HttpGet httpGet = new HttpGet("http://localhost:" + ourPort + "/Patient/%24andlist?valstr=VALSTR1A,VALSTR1B&valstr=VALSTR2A,VALSTR2B&valtok=" + UrlUtil.escapeUrlParam("VALTOK1A|VALTOK1B") + "&valtok=" + UrlUtil.escapeUrlParam("VALTOK2A|VALTOK2B")); + HttpGet httpGet = new HttpGet(ourServer.getBaseUrl() + "/Patient/%24andlist?valstr=VALSTR1A,VALSTR1B&valstr=VALSTR2A,VALSTR2B&valtok=" + UrlUtil.escapeUrlParam("VALTOK1A|VALTOK1B") + "&valtok=" + UrlUtil.escapeUrlParam("VALTOK2A|VALTOK2B")); HttpResponse status = ourClient.execute(httpGet); assertEquals(200, status.getStatusLine().getStatusCode()); @@ -134,7 +148,7 @@ public class OperationServerWithSearchParamTypesDstu3Test { @Test public void testAndListWithUrl() throws Exception { - HttpGet httpGet = new HttpGet("http://localhost:" + ourPort + "/Patient/$andlist?valstr=VALSTR1A,VALSTR1B&valstr=VALSTR2A,VALSTR2B&valtok=" + UrlUtil.escapeUrlParam("VALTOK1A|VALTOK1B") + "&valtok=" + UrlUtil.escapeUrlParam("VALTOK2A|VALTOK2B")); + HttpGet httpGet = new HttpGet(ourServer.getBaseUrl() + "/Patient/$andlist?valstr=VALSTR1A,VALSTR1B&valstr=VALSTR2A,VALSTR2B&valtok=" + UrlUtil.escapeUrlParam("VALTOK1A|VALTOK1B") + "&valtok=" + UrlUtil.escapeUrlParam("VALTOK2A|VALTOK2B")); HttpResponse status = ourClient.execute(httpGet); assertEquals(200, status.getStatusLine().getStatusCode()); @@ -266,7 +280,7 @@ public class OperationServerWithSearchParamTypesDstu3Test { p.addParameter().setName("valtok").setValue(new StringType("VALTOKA|VALTOKB")); String inParamsStr = ourCtx.newXmlParser().encodeResourceToString(p); - HttpPost httpPost = new HttpPost("http://localhost:" + ourPort + "/Patient/$nonrepeating"); + HttpPost httpPost = new HttpPost(ourServer.getBaseUrl() + "/Patient/$nonrepeating"); httpPost.setEntity(new StringEntity(inParamsStr, ContentType.create(Constants.CT_FHIR_XML, "UTF-8"))); HttpResponse status = ourClient.execute(httpPost); @@ -286,7 +300,7 @@ public class OperationServerWithSearchParamTypesDstu3Test { } @Test public void testNonRepeatingWithUrl() throws Exception { - HttpGet httpGet = new HttpGet("http://localhost:" + ourPort + "/Patient/$nonrepeating?valstr=VALSTR&valtok=" + UrlUtil.escapeUrlParam("VALTOKA|VALTOKB")); + HttpGet httpGet = new HttpGet(ourServer.getBaseUrl() + "/Patient/$nonrepeating?valstr=VALSTR&valtok=" + UrlUtil.escapeUrlParam("VALTOKA|VALTOKB")); HttpResponse status = ourClient.execute(httpGet); assertEquals(200, status.getStatusLine().getStatusCode()); @@ -306,7 +320,7 @@ public class OperationServerWithSearchParamTypesDstu3Test { @Test public void testNonRepeatingWithUrlQualified() throws Exception { - HttpGet httpGet = new HttpGet("http://localhost:" + ourPort + "/Patient/$nonrepeating?valstr:exact=VALSTR&valtok:not=" + UrlUtil.escapeUrlParam("VALTOKA|VALTOKB")); + HttpGet httpGet = new HttpGet(ourServer.getBaseUrl() + "/Patient/$nonrepeating?valstr:exact=VALSTR&valtok:not=" + UrlUtil.escapeUrlParam("VALTOKA|VALTOKB")); HttpResponse status = ourClient.execute(httpGet); assertEquals(200, status.getStatusLine().getStatusCode()); @@ -335,7 +349,7 @@ public class OperationServerWithSearchParamTypesDstu3Test { p.addParameter().setName("valtok").setValue(new StringType("VALTOK2A|VALTOK2B")); String inParamsStr = ourCtx.newXmlParser().encodeResourceToString(p); - HttpPost httpPost = new HttpPost("http://localhost:" + ourPort + "/Patient/$orlist"); + HttpPost httpPost = new HttpPost(ourServer.getBaseUrl() + "/Patient/$orlist"); httpPost.setEntity(new StringEntity(inParamsStr, ContentType.create(Constants.CT_FHIR_XML, "UTF-8"))); HttpResponse status = ourClient.execute(httpPost); @@ -361,7 +375,7 @@ public class OperationServerWithSearchParamTypesDstu3Test { @Test public void testOrListWithUrl() throws Exception { - HttpGet httpGet = new HttpGet("http://localhost:" + ourPort + "/Patient/$orlist?valstr=VALSTR1A,VALSTR1B&valstr=VALSTR2A,VALSTR2B&valtok=" + UrlUtil.escapeUrlParam("VALTOK1A|VALTOK1B") + "&valtok=" + UrlUtil.escapeUrlParam("VALTOK2A|VALTOK2B")); + HttpGet httpGet = new HttpGet(ourServer.getBaseUrl() + "/Patient/$orlist?valstr=VALSTR1A,VALSTR1B&valstr=VALSTR2A,VALSTR2B&valtok=" + UrlUtil.escapeUrlParam("VALTOK1A|VALTOK1B") + "&valtok=" + UrlUtil.escapeUrlParam("VALTOK2A|VALTOK2B")); HttpResponse status = ourClient.execute(httpGet); assertEquals(200, status.getStatusLine().getStatusCode()); @@ -386,33 +400,9 @@ public class OperationServerWithSearchParamTypesDstu3Test { @AfterAll public static void afterClassClearContext() throws Exception { - JettyUtil.closeServer(ourServer); TestUtil.randomizeLocaleAndTimezone(); } - @BeforeAll - public static void beforeClass() throws Exception { - ourCtx = FhirContext.forDstu3(); - ourServer = new Server(0); - ServletHandler proxyHandler = new ServletHandler(); - RestfulServer servlet = new RestfulServer(ourCtx); - - servlet.setPagingProvider(new FifoMemoryPagingProvider(10).setDefaultPageSize(2)); - - servlet.setFhirContext(ourCtx); - servlet.setResourceProviders(new PatientProvider()); - ServletHolder servletHolder = new ServletHolder(servlet); - proxyHandler.addServletWithMapping(servletHolder, "/*"); - ourServer.setHandler(proxyHandler); - JettyUtil.startServer(ourServer); - ourPort = JettyUtil.getPortForStartedServer(ourServer); - - PoolingHttpClientConnectionManager connectionManager = new PoolingHttpClientConnectionManager(5000, TimeUnit.MILLISECONDS); - HttpClientBuilder builder = HttpClientBuilder.create(); - builder.setConnectionManager(connectionManager); - ourClient = builder.build(); - - } public static class PatientProvider implements IResourceProvider { diff --git a/hapi-fhir-structures-dstu3/src/test/java/ca/uhn/fhir/rest/server/PatchServerDstu3Test.java b/hapi-fhir-structures-dstu3/src/test/java/ca/uhn/fhir/rest/server/PatchServerDstu3Test.java index e82099319a4..d4b9b59e3de 100644 --- a/hapi-fhir-structures-dstu3/src/test/java/ca/uhn/fhir/rest/server/PatchServerDstu3Test.java +++ b/hapi-fhir-structures-dstu3/src/test/java/ca/uhn/fhir/rest/server/PatchServerDstu3Test.java @@ -9,46 +9,47 @@ import ca.uhn.fhir.rest.annotation.ResourceParam; import ca.uhn.fhir.rest.api.Constants; import ca.uhn.fhir.rest.api.EncodingEnum; import ca.uhn.fhir.rest.api.PatchTypeEnum; -import ca.uhn.fhir.test.utilities.JettyUtil; +import ca.uhn.fhir.test.utilities.HttpClientExtension; +import ca.uhn.fhir.test.utilities.server.RestfulServerExtension; import ca.uhn.fhir.util.TestUtil; import org.apache.commons.io.IOUtils; import org.apache.http.client.methods.CloseableHttpResponse; import org.apache.http.client.methods.HttpPatch; import org.apache.http.entity.ContentType; import org.apache.http.entity.StringEntity; -import org.apache.http.impl.client.CloseableHttpClient; -import org.apache.http.impl.client.HttpClientBuilder; -import org.apache.http.impl.conn.PoolingHttpClientConnectionManager; -import org.eclipse.jetty.server.Server; -import org.eclipse.jetty.servlet.ServletHandler; -import org.eclipse.jetty.servlet.ServletHolder; import org.hl7.fhir.dstu3.model.IdType; import org.hl7.fhir.dstu3.model.OperationOutcome; import org.hl7.fhir.dstu3.model.Patient; import org.hl7.fhir.instance.model.api.IBaseResource; import org.junit.jupiter.api.AfterAll; -import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.RegisterExtension; import java.nio.charset.StandardCharsets; -import java.util.concurrent.TimeUnit; import static org.junit.jupiter.api.Assertions.assertEquals; public class PatchServerDstu3Test { private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(PatchServerDstu3Test.class); - private static CloseableHttpClient ourClient; - private static FhirContext ourCtx = FhirContext.forDstu3(); - private static int ourPort; - private static Server ourServer; + private static final FhirContext ourCtx = FhirContext.forDstu3Cached(); private static String ourLastMethod; private static PatchTypeEnum ourLastPatchType; private static String ourLastBody; private static IdType ourLastId; private static String ourLastConditional; + @RegisterExtension + private RestfulServerExtension ourServer = new RestfulServerExtension(ourCtx) + .setDefaultResponseEncoding(EncodingEnum.XML) + .registerProvider(new DummyPatientResourceProvider()) + .withPagingProvider(new FifoMemoryPagingProvider(100)) + .setDefaultPrettyPrint(false); + + @RegisterExtension + private HttpClientExtension ourClient = new HttpClientExtension(); + @BeforeEach public void before() { ourLastMethod = null; @@ -60,7 +61,7 @@ public class PatchServerDstu3Test { @Test public void testPatchValidJson() throws Exception { String requestContents = "[ { \"op\": \"add\", \"path\": \"/a/b/c\", \"value\": [ \"foo\", \"bar\" ] } ]"; - HttpPatch httpPatch = new HttpPatch("http://localhost:" + ourPort + "/Patient/123"); + HttpPatch httpPatch = new HttpPatch(ourServer.getBaseUrl() + "/Patient/123"); httpPatch.addHeader(Constants.HEADER_PREFER, Constants.HEADER_PREFER_RETURN + "=" + Constants.HEADER_PREFER_RETURN_OPERATION_OUTCOME); httpPatch.setEntity(new StringEntity(requestContents, ContentType.parse(Constants.CT_JSON_PATCH))); CloseableHttpResponse status = ourClient.execute(httpPatch); @@ -83,7 +84,7 @@ public class PatchServerDstu3Test { @Test public void testPatchUsingConditional() throws Exception { String requestContents = "[ { \"op\": \"add\", \"path\": \"/a/b/c\", \"value\": [ \"foo\", \"bar\" ] } ]"; - HttpPatch httpPatch = new HttpPatch("http://localhost:" + ourPort + "/Patient?_id=123"); + HttpPatch httpPatch = new HttpPatch(ourServer.getBaseUrl() + "/Patient?_id=123"); httpPatch.addHeader(Constants.HEADER_PREFER, Constants.HEADER_PREFER_RETURN + "=" + Constants.HEADER_PREFER_RETURN_OPERATION_OUTCOME); httpPatch.setEntity(new StringEntity(requestContents, ContentType.parse(Constants.CT_JSON_PATCH))); CloseableHttpResponse status = ourClient.execute(httpPatch); @@ -107,7 +108,7 @@ public class PatchServerDstu3Test { @Test public void testPatchValidXml() throws Exception { String requestContents = ""; - HttpPatch httpPatch = new HttpPatch("http://localhost:" + ourPort + "/Patient/123"); + HttpPatch httpPatch = new HttpPatch(ourServer.getBaseUrl() + "/Patient/123"); httpPatch.addHeader(Constants.HEADER_PREFER, Constants.HEADER_PREFER_RETURN + "=" + Constants.HEADER_PREFER_RETURN_OPERATION_OUTCOME); httpPatch.setEntity(new StringEntity(requestContents, ContentType.parse(Constants.CT_XML_PATCH))); CloseableHttpResponse status = ourClient.execute(httpPatch); @@ -130,7 +131,7 @@ public class PatchServerDstu3Test { @Test public void testPatchValidJsonWithCharset() throws Exception { String requestContents = "[ { \"op\": \"add\", \"path\": \"/a/b/c\", \"value\": [ \"foo\", \"bar\" ] } ]"; - HttpPatch httpPatch = new HttpPatch("http://localhost:" + ourPort + "/Patient/123"); + HttpPatch httpPatch = new HttpPatch(ourServer.getBaseUrl() + "/Patient/123"); httpPatch.setEntity(new StringEntity(requestContents, ContentType.parse(Constants.CT_JSON_PATCH + Constants.CHARSET_UTF8_CTSUFFIX))); CloseableHttpResponse status = ourClient.execute(httpPatch); @@ -150,7 +151,7 @@ public class PatchServerDstu3Test { @Test public void testPatchInvalidMimeType() throws Exception { String requestContents = "[ { \"op\": \"add\", \"path\": \"/a/b/c\", \"value\": [ \"foo\", \"bar\" ] } ]"; - HttpPatch httpPatch = new HttpPatch("http://localhost:" + ourPort + "/Patient/123"); + HttpPatch httpPatch = new HttpPatch(ourServer.getBaseUrl() + "/Patient/123"); httpPatch.setEntity(new StringEntity(requestContents, ContentType.parse("text/plain; charset=UTF-8"))); CloseableHttpResponse status = ourClient.execute(httpPatch); @@ -193,33 +194,7 @@ public class PatchServerDstu3Test { @AfterAll public static void afterClassClearContext() throws Exception { - JettyUtil.closeServer(ourServer); TestUtil.randomizeLocaleAndTimezone(); } - @BeforeAll - public static void beforeClass() throws Exception { - ourServer = new Server(0); - - DummyPatientResourceProvider patientProvider = new DummyPatientResourceProvider(); - - ServletHandler proxyHandler = new ServletHandler(); - RestfulServer servlet = new RestfulServer(ourCtx); - servlet.setPagingProvider(new FifoMemoryPagingProvider(10)); - servlet.setResourceProviders(patientProvider); - servlet.setDefaultResponseEncoding(EncodingEnum.XML); - - ServletHolder servletHolder = new ServletHolder(servlet); - proxyHandler.addServletWithMapping(servletHolder, "/*"); - ourServer.setHandler(proxyHandler); - JettyUtil.startServer(ourServer); - ourPort = JettyUtil.getPortForStartedServer(ourServer); - - PoolingHttpClientConnectionManager connectionManager = new PoolingHttpClientConnectionManager(5000, TimeUnit.MILLISECONDS); - HttpClientBuilder builder = HttpClientBuilder.create(); - builder.setConnectionManager(connectionManager); - ourClient = builder.build(); - - } - } diff --git a/hapi-fhir-structures-dstu3/src/test/java/ca/uhn/fhir/rest/server/SearchBundleProviderWithNoSizeDstu3Test.java b/hapi-fhir-structures-dstu3/src/test/java/ca/uhn/fhir/rest/server/SearchBundleProviderWithNoSizeDstu3Test.java index 3675cb90c9b..9817849504b 100644 --- a/hapi-fhir-structures-dstu3/src/test/java/ca/uhn/fhir/rest/server/SearchBundleProviderWithNoSizeDstu3Test.java +++ b/hapi-fhir-structures-dstu3/src/test/java/ca/uhn/fhir/rest/server/SearchBundleProviderWithNoSizeDstu3Test.java @@ -10,8 +10,8 @@ import org.apache.http.impl.client.CloseableHttpClient; import org.apache.http.impl.client.HttpClientBuilder; import org.apache.http.impl.conn.PoolingHttpClientConnectionManager; import org.eclipse.jetty.server.Server; -import org.eclipse.jetty.servlet.ServletHandler; -import org.eclipse.jetty.servlet.ServletHolder; +import org.eclipse.jetty.ee10.servlet.ServletHandler; +import org.eclipse.jetty.ee10.servlet.ServletHolder; import org.hl7.fhir.dstu3.model.Patient; import org.hl7.fhir.instance.model.api.IBaseResource; import org.junit.jupiter.api.AfterAll; diff --git a/hapi-fhir-structures-dstu3/src/test/java/ca/uhn/fhir/rest/server/SearchCountParamDstu3Test.java b/hapi-fhir-structures-dstu3/src/test/java/ca/uhn/fhir/rest/server/SearchCountParamDstu3Test.java index b3edc466493..5f8bfa1a944 100644 --- a/hapi-fhir-structures-dstu3/src/test/java/ca/uhn/fhir/rest/server/SearchCountParamDstu3Test.java +++ b/hapi-fhir-structures-dstu3/src/test/java/ca/uhn/fhir/rest/server/SearchCountParamDstu3Test.java @@ -6,7 +6,9 @@ import ca.uhn.fhir.rest.annotation.OptionalParam; import ca.uhn.fhir.rest.annotation.Search; import ca.uhn.fhir.rest.api.EncodingEnum; import ca.uhn.fhir.rest.param.TokenParam; +import ca.uhn.fhir.test.utilities.HttpClientExtension; import ca.uhn.fhir.test.utilities.JettyUtil; +import ca.uhn.fhir.test.utilities.server.RestfulServerExtension; import ca.uhn.fhir.util.TestUtil; import org.apache.commons.io.IOUtils; import org.apache.http.client.methods.CloseableHttpResponse; @@ -15,8 +17,8 @@ import org.apache.http.impl.client.CloseableHttpClient; import org.apache.http.impl.client.HttpClientBuilder; import org.apache.http.impl.conn.PoolingHttpClientConnectionManager; import org.eclipse.jetty.server.Server; -import org.eclipse.jetty.servlet.ServletHandler; -import org.eclipse.jetty.servlet.ServletHolder; +import org.eclipse.jetty.ee10.servlet.ServletHandler; +import org.eclipse.jetty.ee10.servlet.ServletHolder; import org.hl7.fhir.dstu3.model.HumanName; import org.hl7.fhir.dstu3.model.Patient; import org.hl7.fhir.instance.model.api.IBaseResource; @@ -24,6 +26,7 @@ import org.junit.jupiter.api.AfterAll; import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.RegisterExtension; import java.util.ArrayList; import java.util.List; @@ -38,13 +41,20 @@ import static org.junit.jupiter.api.Assertions.assertEquals; public class SearchCountParamDstu3Test { private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(SearchCountParamDstu3Test.class); - private static CloseableHttpClient ourClient; - private static FhirContext ourCtx = FhirContext.forDstu3(); - private static int ourPort; - private static Server ourServer; + private static final FhirContext ourCtx = FhirContext.forDstu3Cached(); private static String ourLastMethod; private static Integer ourLastParam; + @RegisterExtension + private RestfulServerExtension ourServer = new RestfulServerExtension(ourCtx) + .registerProvider(new DummyPatientResourceProvider()) + .setDefaultResponseEncoding(EncodingEnum.XML) + .withPagingProvider(new FifoMemoryPagingProvider(100)) + .setDefaultPrettyPrint(false); + + @RegisterExtension + private HttpClientExtension ourClient = new HttpClientExtension(); + @BeforeEach public void before() { ourLastMethod = null; @@ -53,7 +63,7 @@ public class SearchCountParamDstu3Test { @Test public void testSearch() throws Exception { - HttpGet httpGet = new HttpGet("http://localhost:" + ourPort + "/Patient?_count=2"); + HttpGet httpGet = new HttpGet(ourServer.getBaseUrl() + "/Patient?_count=2"); try (CloseableHttpResponse status = ourClient.execute(httpGet)) { String responseContent = IOUtils.toString(status.getEntity().getContent()); @@ -65,11 +75,11 @@ public class SearchCountParamDstu3Test { assertThat(responseContent, stringContainsInOrder( "", "", - "", + "", "", "", "", - "", + "", "")); } @@ -79,7 +89,7 @@ public class SearchCountParamDstu3Test { @Test public void testSearchCount0() throws Exception { - HttpGet httpGet = new HttpGet("http://localhost:" + ourPort + "/Patient?_count=0&_pretty=true"); + HttpGet httpGet = new HttpGet(ourServer.getBaseUrl() + "/Patient?_count=0&_pretty=true"); try (CloseableHttpResponse status = ourClient.execute(httpGet)) { String responseContent = IOUtils.toString(status.getEntity().getContent()); @@ -103,7 +113,7 @@ public class SearchCountParamDstu3Test { */ @Test public void testSearchWithNoCountParam() throws Exception { - HttpGet httpGet = new HttpGet("http://localhost:" + ourPort + "/Patient?_query=searchWithNoCountParam&_count=2"); + HttpGet httpGet = new HttpGet(ourServer.getBaseUrl() + "/Patient?_query=searchWithNoCountParam&_count=2"); CloseableHttpResponse status = ourClient.execute(httpGet); try { String responseContent = IOUtils.toString(status.getEntity().getContent()); @@ -115,11 +125,11 @@ public class SearchCountParamDstu3Test { assertThat(responseContent, stringContainsInOrder( "", "", - "", + "", "", "", "", - "", + "", "")); } finally { @@ -170,33 +180,7 @@ public class SearchCountParamDstu3Test { @AfterAll public static void afterClassClearContext() throws Exception { - JettyUtil.closeServer(ourServer); TestUtil.randomizeLocaleAndTimezone(); } - @BeforeAll - public static void beforeClass() throws Exception { - ourServer = new Server(0); - - DummyPatientResourceProvider patientProvider = new DummyPatientResourceProvider(); - - ServletHandler proxyHandler = new ServletHandler(); - RestfulServer servlet = new RestfulServer(ourCtx); - servlet.setPagingProvider(new FifoMemoryPagingProvider(10)); - servlet.setResourceProviders(patientProvider); - servlet.setDefaultResponseEncoding(EncodingEnum.XML); - - ServletHolder servletHolder = new ServletHolder(servlet); - proxyHandler.addServletWithMapping(servletHolder, "/*"); - ourServer.setHandler(proxyHandler); - JettyUtil.startServer(ourServer); - ourPort = JettyUtil.getPortForStartedServer(ourServer); - - PoolingHttpClientConnectionManager connectionManager = new PoolingHttpClientConnectionManager(5000, TimeUnit.MILLISECONDS); - HttpClientBuilder builder = HttpClientBuilder.create(); - builder.setConnectionManager(connectionManager); - ourClient = builder.build(); - - } - } diff --git a/hapi-fhir-structures-dstu3/src/test/java/ca/uhn/fhir/rest/server/SearchDefaultMethodDstu3Test.java b/hapi-fhir-structures-dstu3/src/test/java/ca/uhn/fhir/rest/server/SearchDefaultMethodDstu3Test.java index ceb424dfcb4..cf4d62d6701 100644 --- a/hapi-fhir-structures-dstu3/src/test/java/ca/uhn/fhir/rest/server/SearchDefaultMethodDstu3Test.java +++ b/hapi-fhir-structures-dstu3/src/test/java/ca/uhn/fhir/rest/server/SearchDefaultMethodDstu3Test.java @@ -4,8 +4,11 @@ import ca.uhn.fhir.context.FhirContext; import ca.uhn.fhir.rest.annotation.OptionalParam; import ca.uhn.fhir.rest.annotation.RawParam; import ca.uhn.fhir.rest.annotation.Search; +import ca.uhn.fhir.rest.api.EncodingEnum; import ca.uhn.fhir.rest.param.StringAndListParam; +import ca.uhn.fhir.test.utilities.HttpClientExtension; import ca.uhn.fhir.test.utilities.JettyUtil; +import ca.uhn.fhir.test.utilities.server.RestfulServerExtension; import ca.uhn.fhir.util.TestUtil; import org.apache.commons.io.IOUtils; import org.apache.http.client.methods.CloseableHttpResponse; @@ -14,8 +17,8 @@ import org.apache.http.impl.client.CloseableHttpClient; import org.apache.http.impl.client.HttpClientBuilder; import org.apache.http.impl.conn.PoolingHttpClientConnectionManager; import org.eclipse.jetty.server.Server; -import org.eclipse.jetty.servlet.ServletHandler; -import org.eclipse.jetty.servlet.ServletHolder; +import org.eclipse.jetty.ee10.servlet.ServletHandler; +import org.eclipse.jetty.ee10.servlet.ServletHolder; import org.hl7.fhir.dstu3.model.HumanName; import org.hl7.fhir.dstu3.model.Patient; import org.hl7.fhir.instance.model.api.IBaseResource; @@ -23,6 +26,7 @@ import org.junit.jupiter.api.AfterAll; import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.RegisterExtension; import java.nio.charset.StandardCharsets; import java.util.ArrayList; @@ -37,15 +41,24 @@ import static org.junit.jupiter.api.Assertions.assertEquals; public class SearchDefaultMethodDstu3Test { - private static CloseableHttpClient ourClient; - private static FhirContext ourCtx = FhirContext.forDstu3(); + private static final FhirContext ourCtx = FhirContext.forDstu3Cached(); private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(SearchDefaultMethodDstu3Test.class); private static int ourPort; - private static Server ourServer; private static String ourLastMethod; private static StringAndListParam ourLastParam1; private static StringAndListParam ourLastParam2; + + @RegisterExtension + public RestfulServerExtension ourServer = new RestfulServerExtension(ourCtx) + .setDefaultResponseEncoding(EncodingEnum.XML) + .registerProvider(new DummyPatientResourceProvider()) + .withPagingProvider(new FifoMemoryPagingProvider(100)) + .setDefaultPrettyPrint(false); + + @RegisterExtension + public HttpClientExtension ourClient = new HttpClientExtension(); + @BeforeEach public void before() { ourLastMethod = null; @@ -56,7 +69,7 @@ public class SearchDefaultMethodDstu3Test { @Test public void testSearchNoParams() throws Exception { - HttpGet httpGet = new HttpGet("http://localhost:" + ourPort + "/Patient"); + HttpGet httpGet = new HttpGet(ourServer.getBaseUrl() + "/Patient"); CloseableHttpResponse status = ourClient.execute(httpGet); try { String responseContent = IOUtils.toString(status.getEntity().getContent(), StandardCharsets.UTF_8); @@ -76,7 +89,7 @@ public class SearchDefaultMethodDstu3Test { @Test public void testSearchOneOptionalParam() throws Exception { - HttpGet httpGet = new HttpGet("http://localhost:" + ourPort + "/Patient?param1=val1"); + HttpGet httpGet = new HttpGet(ourServer.getBaseUrl() + "/Patient?param1=val1"); CloseableHttpResponse status = ourClient.execute(httpGet); try { String responseContent = IOUtils.toString(status.getEntity().getContent(), StandardCharsets.UTF_8); @@ -97,7 +110,7 @@ public class SearchDefaultMethodDstu3Test { @Test public void testSearchTwoOptionalParams() throws Exception { - HttpGet httpGet = new HttpGet("http://localhost:" + ourPort + "/Patient?param1=val1¶m2=val2"); + HttpGet httpGet = new HttpGet(ourServer.getBaseUrl() + "/Patient?param1=val1¶m2=val2"); CloseableHttpResponse status = ourClient.execute(httpGet); try { String responseContent = IOUtils.toString(status.getEntity().getContent(), StandardCharsets.UTF_8); @@ -122,7 +135,7 @@ public class SearchDefaultMethodDstu3Test { @Test public void testSearchTwoOptionalParamsAndExtraParam() throws Exception { - HttpGet httpGet = new HttpGet("http://localhost:" + ourPort + "/Patient?param1=val1¶m2=val2¶m3=val3&_pretty=true"); + HttpGet httpGet = new HttpGet(ourServer.getBaseUrl() + "/Patient?param1=val1¶m2=val2¶m3=val3&_pretty=true"); CloseableHttpResponse status = ourClient.execute(httpGet); try { String responseContent = IOUtils.toString(status.getEntity().getContent(), StandardCharsets.UTF_8); @@ -151,7 +164,7 @@ public class SearchDefaultMethodDstu3Test { @Test public void testSearchTwoOptionalParamsWithQualifierAndExtraParam() throws Exception { - HttpGet httpGet = new HttpGet("http://localhost:" + ourPort + "/Patient?param1=val1¶m2=val2¶m2:exact=val2e¶m3=val3&_pretty=true"); + HttpGet httpGet = new HttpGet(ourServer.getBaseUrl() + "/Patient?param1=val1¶m2=val2¶m2:exact=val2e¶m3=val3&_pretty=true"); CloseableHttpResponse status = ourClient.execute(httpGet); try { String responseContent = IOUtils.toString(status.getEntity().getContent(), StandardCharsets.UTF_8); @@ -181,34 +194,9 @@ public class SearchDefaultMethodDstu3Test { @AfterAll public static void afterClassClearContext() throws Exception { - JettyUtil.closeServer(ourServer); TestUtil.randomizeLocaleAndTimezone(); } - @BeforeAll - public static void beforeClass() throws Exception { - ourServer = new Server(0); - - DummyPatientResourceProvider patientProvider = new DummyPatientResourceProvider(); - - ServletHandler proxyHandler = new ServletHandler(); - RestfulServer servlet = new RestfulServer(ourCtx); - servlet.setPagingProvider(new FifoMemoryPagingProvider(10)); - - servlet.setResourceProviders(patientProvider); - ServletHolder servletHolder = new ServletHolder(servlet); - proxyHandler.addServletWithMapping(servletHolder, "/*"); - ourServer.setHandler(proxyHandler); - JettyUtil.startServer(ourServer); - ourPort = JettyUtil.getPortForStartedServer(ourServer); - - PoolingHttpClientConnectionManager connectionManager = new PoolingHttpClientConnectionManager(5000, TimeUnit.MILLISECONDS); - HttpClientBuilder builder = HttpClientBuilder.create(); - builder.setConnectionManager(connectionManager); - ourClient = builder.build(); - - } - private static Map> ourLastAdditionalParams; public static class DummyPatientResourceProvider implements IResourceProvider { diff --git a/hapi-fhir-structures-dstu3/src/test/java/ca/uhn/fhir/rest/server/SearchDstu3Test.java b/hapi-fhir-structures-dstu3/src/test/java/ca/uhn/fhir/rest/server/SearchDstu3Test.java index 9ec3e18c12f..2a5ba4c17b7 100644 --- a/hapi-fhir-structures-dstu3/src/test/java/ca/uhn/fhir/rest/server/SearchDstu3Test.java +++ b/hapi-fhir-structures-dstu3/src/test/java/ca/uhn/fhir/rest/server/SearchDstu3Test.java @@ -12,34 +12,28 @@ import ca.uhn.fhir.rest.client.interceptor.LoggingInterceptor; import ca.uhn.fhir.rest.gclient.StringClientParam; import ca.uhn.fhir.rest.param.TokenAndListParam; import ca.uhn.fhir.rest.server.exceptions.InvalidRequestException; -import ca.uhn.fhir.test.utilities.JettyUtil; +import ca.uhn.fhir.test.utilities.HttpClientExtension; +import ca.uhn.fhir.test.utilities.server.RestfulServerExtension; import ca.uhn.fhir.util.TestUtil; import ca.uhn.fhir.util.UrlUtil; import org.apache.commons.io.IOUtils; import org.apache.http.client.ClientProtocolException; import org.apache.http.client.methods.CloseableHttpResponse; import org.apache.http.client.methods.HttpGet; -import org.apache.http.impl.client.CloseableHttpClient; -import org.apache.http.impl.client.HttpClientBuilder; -import org.apache.http.impl.conn.PoolingHttpClientConnectionManager; -import org.eclipse.jetty.server.Server; -import org.eclipse.jetty.servlet.ServletHandler; -import org.eclipse.jetty.servlet.ServletHolder; import org.hl7.fhir.dstu3.model.Bundle; import org.hl7.fhir.dstu3.model.HumanName; import org.hl7.fhir.dstu3.model.OperationOutcome; import org.hl7.fhir.dstu3.model.Patient; import org.hl7.fhir.instance.model.api.IBaseResource; import org.junit.jupiter.api.AfterAll; -import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.RegisterExtension; import java.io.IOException; import java.nio.charset.StandardCharsets; import java.util.ArrayList; import java.util.List; -import java.util.concurrent.TimeUnit; import static org.hamcrest.MatcherAssert.assertThat; import static org.hamcrest.Matchers.containsString; @@ -50,14 +44,19 @@ import static org.junit.jupiter.api.Assertions.fail; public class SearchDstu3Test { - private static CloseableHttpClient ourClient; - private static FhirContext ourCtx = FhirContext.forDstu3(); + private static final FhirContext ourCtx = FhirContext.forDstu3Cached(); private static TokenAndListParam ourIdentifiers; private static String ourLastMethod; private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(SearchDstu3Test.class); - private static int ourPort; - private static Server ourServer; + @RegisterExtension + private RestfulServerExtension ourServer = new RestfulServerExtension(ourCtx) + .registerProvider(new DummyPatientResourceProvider()) + .withPagingProvider(new FifoMemoryPagingProvider(100)) + .setDefaultPrettyPrint(false); + + @RegisterExtension + private HttpClientExtension ourClient = new HttpClientExtension(); @BeforeEach public void before() { @@ -67,7 +66,7 @@ public class SearchDstu3Test { @Test public void testSearchNormal() throws Exception { - HttpGet httpGet = new HttpGet("http://localhost:" + ourPort + "/Patient?identifier=foo%7Cbar"); + HttpGet httpGet = new HttpGet(ourServer.getBaseUrl() + "/Patient?identifier=foo%7Cbar"); CloseableHttpResponse status = ourClient.execute(httpGet); try { String responseContent = IOUtils.toString(status.getEntity().getContent(), StandardCharsets.UTF_8); @@ -86,7 +85,7 @@ public class SearchDstu3Test { @Test public void testSearchWithInvalidChain() throws Exception { - HttpGet httpGet = new HttpGet("http://localhost:" + ourPort + "/Patient?identifier.chain=foo%7Cbar"); + HttpGet httpGet = new HttpGet(ourServer.getBaseUrl() + "/Patient?identifier.chain=foo%7Cbar"); CloseableHttpResponse status = ourClient.execute(httpGet); try { String responseContent = IOUtils.toString(status.getEntity().getContent(), StandardCharsets.UTF_8); @@ -111,7 +110,7 @@ public class SearchDstu3Test { Bundle bundle; // Initial search - httpGet = new HttpGet("http://localhost:" + ourPort + "/Patient?identifier=foo%7Cbar&_format=json"); + httpGet = new HttpGet(ourServer.getBaseUrl() + "/Patient?identifier=foo%7Cbar&_format=json"); bundle = executeAndReturnLinkNext(httpGet, EncodingEnum.JSON); linkNext = bundle.getLink(Constants.LINK_NEXT).getUrl(); assertThat(linkNext, containsString("_format=json")); @@ -143,7 +142,7 @@ public class SearchDstu3Test { Bundle bundle; // Initial search - httpGet = new HttpGet("http://localhost:" + ourPort + "/Patient?identifier=foo%7Cbar&_format=" + Constants.CT_FHIR_JSON_NEW); + httpGet = new HttpGet(ourServer.getBaseUrl() + "/Patient?identifier=foo%7Cbar&_format=" + Constants.CT_FHIR_JSON_NEW); bundle = executeAndReturnLinkNext(httpGet, EncodingEnum.JSON); linkNext = bundle.getLink(Constants.LINK_NEXT).getUrl(); assertThat(linkNext, containsString("_format=" + UrlUtil.escapeUrlParam(Constants.CT_FHIR_JSON_NEW))); @@ -175,7 +174,7 @@ public class SearchDstu3Test { Bundle bundle; // Initial search - httpGet = new HttpGet("http://localhost:" + ourPort + "/Patient?identifier=foo%7Cbar&_format=xml"); + httpGet = new HttpGet(ourServer.getBaseUrl() + "/Patient?identifier=foo%7Cbar&_format=xml"); bundle = executeAndReturnLinkNext(httpGet, EncodingEnum.XML); linkNext = bundle.getLink(Constants.LINK_NEXT).getUrl(); assertThat(linkNext, containsString("_format=xml")); @@ -207,7 +206,7 @@ public class SearchDstu3Test { Bundle bundle; // Initial search - httpGet = new HttpGet("http://localhost:" + ourPort + "/Patient?identifier=foo%7Cbar"); + httpGet = new HttpGet(ourServer.getBaseUrl() + "/Patient?identifier=foo%7Cbar"); bundle = executeAndReturnLinkNext(httpGet, EncodingEnum.JSON); linkNext = bundle.getLink(Constants.LINK_NEXT).getUrl(); assertThat(linkNext, not(containsString("_format"))); @@ -239,7 +238,7 @@ public class SearchDstu3Test { Bundle bundle; // Initial search - httpGet = new HttpGet("http://localhost:" + ourPort + "/Patient?identifier=foo%7Cbar"); + httpGet = new HttpGet(ourServer.getBaseUrl() + "/Patient?identifier=foo%7Cbar"); httpGet.addHeader(Constants.HEADER_ACCEPT, "text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8"); bundle = executeAndReturnLinkNext(httpGet, EncodingEnum.XML); linkNext = bundle.getLink(Constants.LINK_NEXT).getUrl(); @@ -290,7 +289,7 @@ public class SearchDstu3Test { @Test public void testSearchWithPostAndInvalidParameters() throws Exception { - IGenericClient client = ourCtx.newRestfulGenericClient("http://localhost:" + ourPort); + IGenericClient client = ourCtx.newRestfulGenericClient(ourServer.getBaseUrl()); LoggingInterceptor interceptor = new LoggingInterceptor(); interceptor.setLogRequestSummary(true); interceptor.setLogRequestBody(true); @@ -320,35 +319,9 @@ public class SearchDstu3Test { @AfterAll public static void afterClassClearContext() throws Exception { - JettyUtil.closeServer(ourServer); TestUtil.randomizeLocaleAndTimezone(); } - @BeforeAll - public static void beforeClass() throws Exception { - ourServer = new Server(0); - - DummyPatientResourceProvider patientProvider = new DummyPatientResourceProvider(); - - ServletHandler proxyHandler = new ServletHandler(); - RestfulServer servlet = new RestfulServer(ourCtx); - servlet.setDefaultResponseEncoding(EncodingEnum.JSON); - servlet.setPagingProvider(new FifoMemoryPagingProvider(10)); - - servlet.setResourceProviders(patientProvider); - ServletHolder servletHolder = new ServletHolder(servlet); - proxyHandler.addServletWithMapping(servletHolder, "/*"); - ourServer.setHandler(proxyHandler); - JettyUtil.startServer(ourServer); - ourPort = JettyUtil.getPortForStartedServer(ourServer); - - PoolingHttpClientConnectionManager connectionManager = new PoolingHttpClientConnectionManager(5000, TimeUnit.MILLISECONDS); - HttpClientBuilder builder = HttpClientBuilder.create(); - builder.setConnectionManager(connectionManager); - ourClient = builder.build(); - - } - public static class DummyPatientResourceProvider implements IResourceProvider { @Override @@ -362,13 +335,13 @@ public class SearchDstu3Test { @RequiredParam(name = Patient.SP_IDENTIFIER) TokenAndListParam theIdentifiers) { ourLastMethod = "search"; ourIdentifiers = theIdentifiers; - ArrayList retVal = new ArrayList(); + ArrayList retVal = new ArrayList<>(); for (int i = 0; i < 200; i++) { Patient patient = new Patient(); patient.addName(new HumanName().setFamily("FAMILY")); patient.getIdElement().setValue("Patient/" + i); - retVal.add((Patient) patient); + retVal.add(patient); } return retVal; } diff --git a/hapi-fhir-structures-dstu3/src/test/java/ca/uhn/fhir/rest/server/SearchHasParamDstu3Test.java b/hapi-fhir-structures-dstu3/src/test/java/ca/uhn/fhir/rest/server/SearchHasParamDstu3Test.java index 17af852a1cf..7346bc7a7f5 100644 --- a/hapi-fhir-structures-dstu3/src/test/java/ca/uhn/fhir/rest/server/SearchHasParamDstu3Test.java +++ b/hapi-fhir-structures-dstu3/src/test/java/ca/uhn/fhir/rest/server/SearchHasParamDstu3Test.java @@ -6,7 +6,9 @@ import ca.uhn.fhir.rest.annotation.Search; import ca.uhn.fhir.rest.param.HasAndListParam; import ca.uhn.fhir.rest.param.HasParam; import ca.uhn.fhir.rest.param.TokenParam; +import ca.uhn.fhir.test.utilities.HttpClientExtension; import ca.uhn.fhir.test.utilities.JettyUtil; +import ca.uhn.fhir.test.utilities.server.RestfulServerExtension; import ca.uhn.fhir.util.TestUtil; import org.apache.commons.io.IOUtils; import org.apache.http.HttpResponse; @@ -15,8 +17,8 @@ import org.apache.http.impl.client.CloseableHttpClient; import org.apache.http.impl.client.HttpClientBuilder; import org.apache.http.impl.conn.PoolingHttpClientConnectionManager; import org.eclipse.jetty.server.Server; -import org.eclipse.jetty.servlet.ServletHandler; -import org.eclipse.jetty.servlet.ServletHolder; +import org.eclipse.jetty.ee10.servlet.ServletHandler; +import org.eclipse.jetty.ee10.servlet.ServletHolder; import org.hl7.fhir.dstu3.model.HumanName; import org.hl7.fhir.dstu3.model.Patient; import org.hl7.fhir.instance.model.api.IBaseResource; @@ -24,6 +26,7 @@ import org.junit.jupiter.api.AfterAll; import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.RegisterExtension; import java.util.ArrayList; import java.util.List; @@ -33,14 +36,20 @@ import static org.junit.jupiter.api.Assertions.assertEquals; public class SearchHasParamDstu3Test { - private static CloseableHttpClient ourClient; - private static FhirContext ourCtx = FhirContext.forDstu3(); + private static final FhirContext ourCtx = FhirContext.forDstu3Cached(); private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(SearchHasParamDstu3Test.class); - private static int ourPort; - private static Server ourServer; private static String ourLastMethod; private static HasAndListParam ourLastParam; + @RegisterExtension + private RestfulServerExtension ourServer = new RestfulServerExtension(ourCtx) + .registerProvider(new DummyPatientResourceProvider()) + .withPagingProvider(new FifoMemoryPagingProvider(100)) + .setDefaultPrettyPrint(false); + + @RegisterExtension + private HttpClientExtension ourClient = new HttpClientExtension(); + @BeforeEach public void before() { ourLastMethod = null; @@ -49,7 +58,7 @@ public class SearchHasParamDstu3Test { @Test public void testSearch() throws Exception { - HttpGet httpGet = new HttpGet("http://localhost:" + ourPort + "/Patient?_has:Encounter:patient:type=SURG"); + HttpGet httpGet = new HttpGet(ourServer.getBaseUrl() + "/Patient?_has:Encounter:patient:type=SURG"); HttpResponse status = ourClient.execute(httpGet); String responseContent = IOUtils.toString(status.getEntity().getContent()); IOUtils.closeQuietly(status.getEntity().getContent()); @@ -66,34 +75,9 @@ public class SearchHasParamDstu3Test { @AfterAll public static void afterClassClearContext() throws Exception { - JettyUtil.closeServer(ourServer); TestUtil.randomizeLocaleAndTimezone(); } - @BeforeAll - public static void beforeClass() throws Exception { - ourServer = new Server(0); - - DummyPatientResourceProvider patientProvider = new DummyPatientResourceProvider(); - - ServletHandler proxyHandler = new ServletHandler(); - RestfulServer servlet = new RestfulServer(ourCtx); - servlet.setPagingProvider(new FifoMemoryPagingProvider(10)); - - servlet.setResourceProviders(patientProvider); - ServletHolder servletHolder = new ServletHolder(servlet); - proxyHandler.addServletWithMapping(servletHolder, "/*"); - ourServer.setHandler(proxyHandler); - JettyUtil.startServer(ourServer); - ourPort = JettyUtil.getPortForStartedServer(ourServer); - - PoolingHttpClientConnectionManager connectionManager = new PoolingHttpClientConnectionManager(5000, TimeUnit.MILLISECONDS); - HttpClientBuilder builder = HttpClientBuilder.create(); - builder.setConnectionManager(connectionManager); - ourClient = builder.build(); - - } - public static class DummyPatientResourceProvider implements IResourceProvider { @Override diff --git a/hapi-fhir-structures-dstu3/src/test/java/ca/uhn/fhir/rest/server/SearchPostDstu3Test.java b/hapi-fhir-structures-dstu3/src/test/java/ca/uhn/fhir/rest/server/SearchPostDstu3Test.java index 323e3f2276b..154910aec81 100644 --- a/hapi-fhir-structures-dstu3/src/test/java/ca/uhn/fhir/rest/server/SearchPostDstu3Test.java +++ b/hapi-fhir-structures-dstu3/src/test/java/ca/uhn/fhir/rest/server/SearchPostDstu3Test.java @@ -9,7 +9,9 @@ import ca.uhn.fhir.rest.api.EncodingEnum; import ca.uhn.fhir.rest.api.SortSpec; import ca.uhn.fhir.rest.param.StringAndListParam; import ca.uhn.fhir.rest.server.interceptor.InterceptorAdapter; +import ca.uhn.fhir.test.utilities.HttpClientExtension; import ca.uhn.fhir.test.utilities.JettyUtil; +import ca.uhn.fhir.test.utilities.server.RestfulServerExtension; import ca.uhn.fhir.util.TestUtil; import com.google.common.collect.Lists; import org.apache.commons.io.IOUtils; @@ -22,8 +24,8 @@ import org.apache.http.impl.client.HttpClientBuilder; import org.apache.http.impl.conn.PoolingHttpClientConnectionManager; import org.apache.http.message.BasicNameValuePair; import org.eclipse.jetty.server.Server; -import org.eclipse.jetty.servlet.ServletHandler; -import org.eclipse.jetty.servlet.ServletHolder; +import org.eclipse.jetty.ee10.servlet.ServletHandler; +import org.eclipse.jetty.ee10.servlet.ServletHolder; import org.hl7.fhir.dstu3.model.HumanName; import org.hl7.fhir.dstu3.model.Patient; import org.hl7.fhir.instance.model.api.IBaseResource; @@ -32,8 +34,10 @@ import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; -import javax.servlet.http.HttpServletRequest; -import javax.servlet.http.HttpServletResponse; +import jakarta.servlet.http.HttpServletRequest; +import jakarta.servlet.http.HttpServletResponse; +import org.junit.jupiter.api.extension.RegisterExtension; + import java.nio.charset.StandardCharsets; import java.util.ArrayList; import java.util.List; @@ -54,22 +58,28 @@ public class SearchPostDstu3Test { } - private static CloseableHttpClient ourClient; - private static FhirContext ourCtx = FhirContext.forDstu3(); + private static final FhirContext ourCtx = FhirContext.forDstu3Cached(); private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(SearchPostDstu3Test.class); - private static int ourPort; - private static Server ourServer; private static String ourLastMethod; private static SortSpec ourLastSortSpec; private static StringAndListParam ourLastName; - private static RestfulServer ourServlet; + + @RegisterExtension + private RestfulServerExtension ourServer = new RestfulServerExtension(ourCtx) + .setDefaultResponseEncoding(EncodingEnum.XML) + .registerProvider(new DummyPatientResourceProvider()) + .withPagingProvider(new FifoMemoryPagingProvider(100)) + .setDefaultPrettyPrint(false); + + @RegisterExtension + private HttpClientExtension ourClient = new HttpClientExtension(); @BeforeEach public void before() { ourLastMethod = null; ourLastSortSpec = null; ourLastName = null; - ourServlet.getInterceptorService().unregisterAllInterceptors(); + ourServer.getInterceptorService().unregisterAllInterceptors(); } /** @@ -77,7 +87,7 @@ public class SearchPostDstu3Test { */ @Test public void testSearchWithMixedParamsNoInterceptorsYesParams() throws Exception { - HttpPost httpPost = new HttpPost("http://localhost:" + ourPort + "/Patient/_search?_format=application/fhir+json"); + HttpPost httpPost = new HttpPost(ourServer.getBaseUrl() + "/Patient/_search?_format=application/fhir+json"); httpPost.addHeader("Cache-Control","no-cache"); List parameters = Lists.newArrayList(); parameters.add(new BasicNameValuePair("name", "Smith")); @@ -108,7 +118,7 @@ public class SearchPostDstu3Test { */ @Test public void testSearchWithMixedParamsNoInterceptorsNoParams() throws Exception { - HttpPost httpPost = new HttpPost("http://localhost:" + ourPort + "/Patient/_search"); + HttpPost httpPost = new HttpPost(ourServer.getBaseUrl() + "/Patient/_search"); httpPost.addHeader("Cache-Control","no-cache"); List parameters = Lists.newArrayList(); parameters.add(new BasicNameValuePair("name", "Smith")); @@ -139,9 +149,9 @@ public class SearchPostDstu3Test { */ @Test public void testSearchWithMixedParamsYesInterceptorsYesParams() throws Exception { - ourServlet.registerInterceptor(new ParamLoggingInterceptor()); + ourServer.registerInterceptor(new ParamLoggingInterceptor()); - HttpPost httpPost = new HttpPost("http://localhost:" + ourPort + "/Patient/_search?_format=application/fhir+json"); + HttpPost httpPost = new HttpPost(ourServer.getBaseUrl() + "/Patient/_search?_format=application/fhir+json"); httpPost.addHeader("Cache-Control","no-cache"); List parameters = Lists.newArrayList(); parameters.add(new BasicNameValuePair("name", "Smith")); @@ -172,9 +182,9 @@ public class SearchPostDstu3Test { */ @Test public void testSearchWithMixedParamsYesInterceptorsNoParams() throws Exception { - ourServlet.registerInterceptor(new ParamLoggingInterceptor()); + ourServer.registerInterceptor(new ParamLoggingInterceptor()); - HttpPost httpPost = new HttpPost("http://localhost:" + ourPort + "/Patient/_search"); + HttpPost httpPost = new HttpPost(ourServer.getBaseUrl() + "/Patient/_search"); httpPost.addHeader("Cache-Control","no-cache"); List parameters = Lists.newArrayList(); parameters.add(new BasicNameValuePair("name", "Smith")); @@ -202,35 +212,9 @@ public class SearchPostDstu3Test { @AfterAll public static void afterClassClearContext() throws Exception { - JettyUtil.closeServer(ourServer); TestUtil.randomizeLocaleAndTimezone(); } - @BeforeAll - public static void beforeClass() throws Exception { - ourServer = new Server(0); - - DummyPatientResourceProvider patientProvider = new DummyPatientResourceProvider(); - - ServletHandler proxyHandler = new ServletHandler(); - ourServlet = new RestfulServer(ourCtx); - ourServlet.setDefaultResponseEncoding(EncodingEnum.XML); - ourServlet.setPagingProvider(new FifoMemoryPagingProvider(10)); - - ourServlet.setResourceProviders(patientProvider); - ServletHolder servletHolder = new ServletHolder(ourServlet); - proxyHandler.addServletWithMapping(servletHolder, "/*"); - ourServer.setHandler(proxyHandler); - JettyUtil.startServer(ourServer); - ourPort = JettyUtil.getPortForStartedServer(ourServer); - - PoolingHttpClientConnectionManager connectionManager = new PoolingHttpClientConnectionManager(5000, TimeUnit.MILLISECONDS); - HttpClientBuilder builder = HttpClientBuilder.create(); - builder.setConnectionManager(connectionManager); - ourClient = builder.build(); - - } - public static class DummyPatientResourceProvider implements IResourceProvider { @Override diff --git a/hapi-fhir-structures-dstu3/src/test/java/ca/uhn/fhir/rest/server/SearchSortDstu3Test.java b/hapi-fhir-structures-dstu3/src/test/java/ca/uhn/fhir/rest/server/SearchSortDstu3Test.java index e225ad2feb0..ec1d974ec0f 100644 --- a/hapi-fhir-structures-dstu3/src/test/java/ca/uhn/fhir/rest/server/SearchSortDstu3Test.java +++ b/hapi-fhir-structures-dstu3/src/test/java/ca/uhn/fhir/rest/server/SearchSortDstu3Test.java @@ -5,7 +5,9 @@ import ca.uhn.fhir.rest.annotation.Search; import ca.uhn.fhir.rest.annotation.Sort; import ca.uhn.fhir.rest.api.SortOrderEnum; import ca.uhn.fhir.rest.api.SortSpec; +import ca.uhn.fhir.test.utilities.HttpClientExtension; import ca.uhn.fhir.test.utilities.JettyUtil; +import ca.uhn.fhir.test.utilities.server.RestfulServerExtension; import ca.uhn.fhir.util.TestUtil; import org.apache.commons.io.IOUtils; import org.apache.http.client.methods.CloseableHttpResponse; @@ -14,8 +16,8 @@ import org.apache.http.impl.client.CloseableHttpClient; import org.apache.http.impl.client.HttpClientBuilder; import org.apache.http.impl.conn.PoolingHttpClientConnectionManager; import org.eclipse.jetty.server.Server; -import org.eclipse.jetty.servlet.ServletHandler; -import org.eclipse.jetty.servlet.ServletHolder; +import org.eclipse.jetty.ee10.servlet.ServletHandler; +import org.eclipse.jetty.ee10.servlet.ServletHolder; import org.hl7.fhir.dstu3.model.HumanName; import org.hl7.fhir.dstu3.model.Patient; import org.hl7.fhir.instance.model.api.IBaseResource; @@ -23,6 +25,7 @@ import org.junit.jupiter.api.AfterAll; import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.RegisterExtension; import java.util.ArrayList; import java.util.List; @@ -32,14 +35,20 @@ import static org.junit.jupiter.api.Assertions.assertEquals; public class SearchSortDstu3Test { - private static CloseableHttpClient ourClient; - private static FhirContext ourCtx = FhirContext.forDstu3(); + private static final FhirContext ourCtx = FhirContext.forDstu3Cached(); private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(SearchSortDstu3Test.class); - private static int ourPort; - private static Server ourServer; private static String ourLastMethod; private static SortSpec ourLastSortSpec; + @RegisterExtension + private RestfulServerExtension ourServer = new RestfulServerExtension(ourCtx) + .registerProvider(new DummyPatientResourceProvider()) + .withPagingProvider(new FifoMemoryPagingProvider(100)) + .setDefaultPrettyPrint(false); + + @RegisterExtension + private HttpClientExtension ourClient = new HttpClientExtension(); + @BeforeEach public void before() { ourLastMethod = null; @@ -48,7 +57,7 @@ public class SearchSortDstu3Test { @Test public void testSearch() throws Exception { - HttpGet httpGet = new HttpGet("http://localhost:" + ourPort + "/Patient?_sort=param1,-param2,param3,-param4"); + HttpGet httpGet = new HttpGet(ourServer.getBaseUrl() + "/Patient?_sort=param1,-param2,param3,-param4"); CloseableHttpResponse status = ourClient.execute(httpGet); try { String responseContent = IOUtils.toString(status.getEntity().getContent()); @@ -76,34 +85,9 @@ public class SearchSortDstu3Test { @AfterAll public static void afterClassClearContext() throws Exception { - JettyUtil.closeServer(ourServer); TestUtil.randomizeLocaleAndTimezone(); } - @BeforeAll - public static void beforeClass() throws Exception { - ourServer = new Server(0); - - DummyPatientResourceProvider patientProvider = new DummyPatientResourceProvider(); - - ServletHandler proxyHandler = new ServletHandler(); - RestfulServer servlet = new RestfulServer(ourCtx); - servlet.setPagingProvider(new FifoMemoryPagingProvider(10)); - - servlet.setResourceProviders(patientProvider); - ServletHolder servletHolder = new ServletHolder(servlet); - proxyHandler.addServletWithMapping(servletHolder, "/*"); - ourServer.setHandler(proxyHandler); - JettyUtil.startServer(ourServer); - ourPort = JettyUtil.getPortForStartedServer(ourServer); - - PoolingHttpClientConnectionManager connectionManager = new PoolingHttpClientConnectionManager(5000, TimeUnit.MILLISECONDS); - HttpClientBuilder builder = HttpClientBuilder.create(); - builder.setConnectionManager(connectionManager); - ourClient = builder.build(); - - } - public static class DummyPatientResourceProvider implements IResourceProvider { @Override diff --git a/hapi-fhir-structures-dstu3/src/test/java/ca/uhn/fhir/rest/server/SearchWithGenericListDstu3Test.java b/hapi-fhir-structures-dstu3/src/test/java/ca/uhn/fhir/rest/server/SearchWithGenericListDstu3Test.java index 96875620297..ed73903c2be 100644 --- a/hapi-fhir-structures-dstu3/src/test/java/ca/uhn/fhir/rest/server/SearchWithGenericListDstu3Test.java +++ b/hapi-fhir-structures-dstu3/src/test/java/ca/uhn/fhir/rest/server/SearchWithGenericListDstu3Test.java @@ -5,28 +5,22 @@ import ca.uhn.fhir.rest.annotation.RequiredParam; import ca.uhn.fhir.rest.annotation.Search; import ca.uhn.fhir.rest.api.EncodingEnum; import ca.uhn.fhir.rest.param.TokenParam; -import ca.uhn.fhir.test.utilities.JettyUtil; +import ca.uhn.fhir.test.utilities.HttpClientExtension; +import ca.uhn.fhir.test.utilities.server.RestfulServerExtension; import ca.uhn.fhir.util.TestUtil; import org.apache.commons.io.IOUtils; import org.apache.http.HttpResponse; import org.apache.http.client.methods.HttpGet; -import org.apache.http.impl.client.CloseableHttpClient; -import org.apache.http.impl.client.HttpClientBuilder; -import org.apache.http.impl.conn.PoolingHttpClientConnectionManager; -import org.eclipse.jetty.server.Server; -import org.eclipse.jetty.servlet.ServletHandler; -import org.eclipse.jetty.servlet.ServletHolder; import org.hl7.fhir.dstu3.model.HumanName; import org.hl7.fhir.dstu3.model.Patient; import org.hl7.fhir.instance.model.api.IBaseResource; import org.junit.jupiter.api.AfterAll; -import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.RegisterExtension; import java.util.ArrayList; import java.util.List; -import java.util.concurrent.TimeUnit; import static org.hamcrest.MatcherAssert.assertThat; import static org.hamcrest.Matchers.containsString; @@ -34,13 +28,20 @@ import static org.junit.jupiter.api.Assertions.assertEquals; public class SearchWithGenericListDstu3Test { - private static CloseableHttpClient ourClient; - private static FhirContext ourCtx = FhirContext.forDstu3(); + private static final FhirContext ourCtx = FhirContext.forDstu3Cached(); private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(SearchWithGenericListDstu3Test.class); - private static int ourPort; - private static Server ourServer; private static String ourLastMethod; + @RegisterExtension + private RestfulServerExtension ourServer = new RestfulServerExtension(ourCtx) + .setDefaultResponseEncoding(EncodingEnum.XML) + .registerProvider(new DummyPatientResourceProvider()) + .withPagingProvider(new FifoMemoryPagingProvider(100)) + .setDefaultPrettyPrint(false); + + @RegisterExtension + private HttpClientExtension ourClient = new HttpClientExtension(); + @BeforeEach public void before() { ourLastMethod = null; @@ -51,7 +52,7 @@ public class SearchWithGenericListDstu3Test { */ @Test public void testSearch() throws Exception { - HttpGet httpGet = new HttpGet("http://localhost:" + ourPort + "/Patient?identifier=foo&_pretty=true"); + HttpGet httpGet = new HttpGet(ourServer.getBaseUrl() + "/Patient?identifier=foo&_pretty=true"); HttpResponse status = ourClient.execute(httpGet); String responseContent = IOUtils.toString(status.getEntity().getContent()); IOUtils.closeQuietly(status.getEntity().getContent()); @@ -59,42 +60,17 @@ public class SearchWithGenericListDstu3Test { assertEquals(200, status.getStatusLine().getStatusCode()); assertEquals("searchByIdentifier", ourLastMethod); assertThat(responseContent, containsString("")); + assertThat(responseContent, containsString("")); } @AfterAll public static void afterClassClearContext() throws Exception { - JettyUtil.closeServer(ourServer); TestUtil.randomizeLocaleAndTimezone(); } - @BeforeAll - public static void beforeClass() throws Exception { - ourServer = new Server(0); - - DummyPatientResourceProvider patientProvider = new DummyPatientResourceProvider(); - - ServletHandler proxyHandler = new ServletHandler(); - RestfulServer servlet = new RestfulServer(ourCtx); - servlet.setPagingProvider(new FifoMemoryPagingProvider(10)); - servlet.setResourceProviders(patientProvider); - servlet.setDefaultResponseEncoding(EncodingEnum.XML); - - ServletHolder servletHolder = new ServletHolder(servlet); - proxyHandler.addServletWithMapping(servletHolder, "/*"); - ourServer.setHandler(proxyHandler); - JettyUtil.startServer(ourServer); - ourPort = JettyUtil.getPortForStartedServer(ourServer); - - PoolingHttpClientConnectionManager connectionManager = new PoolingHttpClientConnectionManager(5000, TimeUnit.MILLISECONDS); - HttpClientBuilder builder = HttpClientBuilder.create(); - builder.setConnectionManager(connectionManager); - ourClient = builder.build(); - - } public static class DummyPatientResourceProvider implements IResourceProvider { @@ -109,7 +85,7 @@ public class SearchWithGenericListDstu3Test { public List searchByIdentifier( @RequiredParam(name=Patient.SP_IDENTIFIER) TokenParam theIdentifier) { ourLastMethod = "searchByIdentifier"; - ArrayList retVal = new ArrayList(); + ArrayList retVal = new ArrayList<>(); retVal.add((Patient) new Patient().addName(new HumanName().setFamily("FAMILY")).setId("1")); return retVal; } diff --git a/hapi-fhir-structures-dstu3/src/test/java/ca/uhn/fhir/rest/server/SearchWithIncludesDstu3Test.java b/hapi-fhir-structures-dstu3/src/test/java/ca/uhn/fhir/rest/server/SearchWithIncludesDstu3Test.java index 9a7d1db840b..555df4c1d6e 100644 --- a/hapi-fhir-structures-dstu3/src/test/java/ca/uhn/fhir/rest/server/SearchWithIncludesDstu3Test.java +++ b/hapi-fhir-structures-dstu3/src/test/java/ca/uhn/fhir/rest/server/SearchWithIncludesDstu3Test.java @@ -5,7 +5,9 @@ import ca.uhn.fhir.model.api.Include; import ca.uhn.fhir.rest.annotation.IncludeParam; import ca.uhn.fhir.rest.annotation.Search; import ca.uhn.fhir.rest.api.EncodingEnum; +import ca.uhn.fhir.test.utilities.HttpClientExtension; import ca.uhn.fhir.test.utilities.JettyUtil; +import ca.uhn.fhir.test.utilities.server.RestfulServerExtension; import ca.uhn.fhir.util.TestUtil; import org.apache.commons.io.IOUtils; import org.apache.http.HttpResponse; @@ -14,14 +16,15 @@ import org.apache.http.impl.client.CloseableHttpClient; import org.apache.http.impl.client.HttpClientBuilder; import org.apache.http.impl.conn.PoolingHttpClientConnectionManager; import org.eclipse.jetty.server.Server; -import org.eclipse.jetty.servlet.ServletHandler; -import org.eclipse.jetty.servlet.ServletHolder; +import org.eclipse.jetty.ee10.servlet.ServletHandler; +import org.eclipse.jetty.ee10.servlet.ServletHolder; import org.hl7.fhir.dstu3.model.Organization; import org.hl7.fhir.dstu3.model.Patient; import org.hl7.fhir.instance.model.api.IBaseResource; import org.junit.jupiter.api.AfterAll; import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.RegisterExtension; import java.nio.charset.StandardCharsets; import java.util.ArrayList; @@ -35,15 +38,22 @@ import static org.junit.jupiter.api.Assertions.assertEquals; public class SearchWithIncludesDstu3Test { - private static CloseableHttpClient ourClient; - private static FhirContext ourCtx = FhirContext.forDstu3(); + private static final FhirContext ourCtx = FhirContext.forDstu3Cached(); private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(SearchWithIncludesDstu3Test.class); - private static int ourPort; - private static Server ourServer; + + @RegisterExtension + private RestfulServerExtension ourServer = new RestfulServerExtension(ourCtx) + .setDefaultResponseEncoding(EncodingEnum.XML) + .registerProvider(new DummyPatientResourceProvider()) + .withPagingProvider(new FifoMemoryPagingProvider(100)) + .setDefaultPrettyPrint(false); + + @RegisterExtension + private HttpClientExtension ourClient = new HttpClientExtension(); @Test public void testSearchIncludesReferences() throws Exception { - HttpGet httpGet = new HttpGet("http://localhost:" + ourPort + "/Patient?_pretty=true&_include=Patient:organization&_include=Organization:" + Organization.SP_PARTOF); + HttpGet httpGet = new HttpGet(ourServer.getBaseUrl() + "/Patient?_pretty=true&_include=Patient:organization&_include=Organization:" + Organization.SP_PARTOF); HttpResponse status = ourClient.execute(httpGet); String responseContent = IOUtils.toString(status.getEntity().getContent(), StandardCharsets.UTF_8); IOUtils.closeQuietly(status.getEntity().getContent()); @@ -63,37 +73,11 @@ public class SearchWithIncludesDstu3Test { @AfterAll public static void afterClassClearContext() throws Exception { - JettyUtil.closeServer(ourServer); TestUtil.randomizeLocaleAndTimezone(); } - @BeforeAll - public static void beforeClass() throws Exception { - ourServer = new Server(0); - - DummyPatientResourceProvider patientProvider = new DummyPatientResourceProvider(); - - ServletHandler proxyHandler = new ServletHandler(); - RestfulServer servlet = new RestfulServer(ourCtx); - servlet.setPagingProvider(new FifoMemoryPagingProvider(10)); - servlet.setResourceProviders(patientProvider); - servlet.setDefaultResponseEncoding(EncodingEnum.XML); - - ServletHolder servletHolder = new ServletHolder(servlet); - proxyHandler.addServletWithMapping(servletHolder, "/*"); - ourServer.setHandler(proxyHandler); - JettyUtil.startServer(ourServer); - ourPort = JettyUtil.getPortForStartedServer(ourServer); - - PoolingHttpClientConnectionManager connectionManager = new PoolingHttpClientConnectionManager(5000, TimeUnit.MILLISECONDS); - HttpClientBuilder builder = HttpClientBuilder.create(); - builder.setConnectionManager(connectionManager); - ourClient = builder.build(); - - } - public static class DummyPatientResourceProvider implements IResourceProvider { @Override diff --git a/hapi-fhir-structures-dstu3/src/test/java/ca/uhn/fhir/rest/server/SearchWithServerAddressStrategyDstu3Test.java b/hapi-fhir-structures-dstu3/src/test/java/ca/uhn/fhir/rest/server/SearchWithServerAddressStrategyDstu3Test.java index 10ae6e87550..86b175194a2 100644 --- a/hapi-fhir-structures-dstu3/src/test/java/ca/uhn/fhir/rest/server/SearchWithServerAddressStrategyDstu3Test.java +++ b/hapi-fhir-structures-dstu3/src/test/java/ca/uhn/fhir/rest/server/SearchWithServerAddressStrategyDstu3Test.java @@ -3,27 +3,21 @@ package ca.uhn.fhir.rest.server; import ca.uhn.fhir.context.FhirContext; import ca.uhn.fhir.rest.annotation.Search; import ca.uhn.fhir.rest.api.EncodingEnum; -import ca.uhn.fhir.test.utilities.JettyUtil; +import ca.uhn.fhir.test.utilities.HttpClientExtension; +import ca.uhn.fhir.test.utilities.server.RestfulServerExtension; import ca.uhn.fhir.util.TestUtil; import org.apache.commons.io.IOUtils; import org.apache.http.HttpResponse; import org.apache.http.client.methods.HttpGet; -import org.apache.http.impl.client.CloseableHttpClient; -import org.apache.http.impl.client.HttpClientBuilder; -import org.apache.http.impl.conn.PoolingHttpClientConnectionManager; -import org.eclipse.jetty.server.Server; -import org.eclipse.jetty.servlet.ServletHandler; -import org.eclipse.jetty.servlet.ServletHolder; import org.hl7.fhir.dstu3.model.HumanName; import org.hl7.fhir.dstu3.model.Patient; import org.hl7.fhir.instance.model.api.IBaseResource; import org.junit.jupiter.api.AfterAll; -import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.RegisterExtension; import java.util.ArrayList; import java.util.List; -import java.util.concurrent.TimeUnit; import static org.hamcrest.MatcherAssert.assertThat; import static org.hamcrest.Matchers.containsString; @@ -31,42 +25,48 @@ import static org.junit.jupiter.api.Assertions.assertEquals; public class SearchWithServerAddressStrategyDstu3Test { - private static CloseableHttpClient ourClient; - private static FhirContext ourCtx = FhirContext.forDstu3(); + private static final FhirContext ourCtx = FhirContext.forDstu3Cached(); private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(SearchWithServerAddressStrategyDstu3Test.class); - private static int ourPort; - private static Server ourServer; - private static RestfulServer ourServlet; + + @RegisterExtension + private RestfulServerExtension ourServer = new RestfulServerExtension(ourCtx) + .registerProvider(new DummyPatientResourceProvider()) + .setDefaultResponseEncoding(EncodingEnum.XML) + .withPagingProvider(new FifoMemoryPagingProvider(100)) + .setDefaultPrettyPrint(false); + + @RegisterExtension + private HttpClientExtension ourClient = new HttpClientExtension(); @Test public void testIncomingRequestAddressStrategy() throws Exception { - ourServlet.setServerAddressStrategy(new IncomingRequestAddressStrategy()); + ourServer.setServerAddressStrategy(new IncomingRequestAddressStrategy()); - HttpGet httpGet = new HttpGet("http://localhost:" + ourPort + "/Patient"); + HttpGet httpGet = new HttpGet(ourServer.getBaseUrl() + "/Patient"); HttpResponse status = ourClient.execute(httpGet); String responseContent = IOUtils.toString(status.getEntity().getContent()); IOUtils.closeQuietly(status.getEntity().getContent()); ourLog.info(responseContent); assertEquals(200, status.getStatusLine().getStatusCode()); assertThat(responseContent, containsString("")); + assertThat(responseContent, containsString("")); } @Test public void testApacheProxyAddressStrategy() throws Exception { - ourServlet.setServerAddressStrategy(ApacheProxyAddressStrategy.forHttp()); - HttpGet httpGet = new HttpGet("http://localhost:" + ourPort + "/Patient"); + ourServer.setServerAddressStrategy(ApacheProxyAddressStrategy.forHttp()); + HttpGet httpGet = new HttpGet(ourServer.getBaseUrl() + "/Patient"); HttpResponse status = ourClient.execute(httpGet); String responseContent = IOUtils.toString(status.getEntity().getContent()); IOUtils.closeQuietly(status.getEntity().getContent()); ourLog.info(responseContent); assertEquals(200, status.getStatusLine().getStatusCode()); assertThat(responseContent, containsString("")); + assertThat(responseContent, containsString("")); - ourServlet.setServerAddressStrategy(new ApacheProxyAddressStrategy(false)); - httpGet = new HttpGet("http://localhost:" + ourPort + "/Patient"); + ourServer.setServerAddressStrategy(new ApacheProxyAddressStrategy(false)); + httpGet = new HttpGet(ourServer.getBaseUrl() + "/Patient"); httpGet.addHeader("x-forwarded-host", "foo.com"); status = ourClient.execute(httpGet); responseContent = IOUtils.toString(status.getEntity().getContent()); @@ -76,8 +76,8 @@ public class SearchWithServerAddressStrategyDstu3Test { assertThat(responseContent, containsString("")); - ourServlet.setServerAddressStrategy(ApacheProxyAddressStrategy.forHttps()); - httpGet = new HttpGet("http://localhost:" + ourPort + "/Patient"); + ourServer.setServerAddressStrategy(ApacheProxyAddressStrategy.forHttps()); + httpGet = new HttpGet(ourServer.getBaseUrl() + "/Patient"); httpGet.addHeader("x-forwarded-host", "foo.com"); status = ourClient.execute(httpGet); responseContent = IOUtils.toString(status.getEntity().getContent()); @@ -87,8 +87,8 @@ public class SearchWithServerAddressStrategyDstu3Test { assertThat(responseContent, containsString("")); - ourServlet.setServerAddressStrategy(new ApacheProxyAddressStrategy(false)); - httpGet = new HttpGet("http://localhost:" + ourPort + "/Patient"); + ourServer.setServerAddressStrategy(new ApacheProxyAddressStrategy(false)); + httpGet = new HttpGet(ourServer.getBaseUrl() + "/Patient"); httpGet.addHeader("x-forwarded-host", "foo.com"); httpGet.addHeader("x-forwarded-proto", "https"); status = ourClient.execute(httpGet); @@ -103,9 +103,9 @@ public class SearchWithServerAddressStrategyDstu3Test { @Test public void testHardcodedAddressStrategy() throws Exception { - ourServlet.setServerAddressStrategy(new HardcodedServerAddressStrategy("http://example.com/fhir/base")); + ourServer.setServerAddressStrategy(new HardcodedServerAddressStrategy("http://example.com/fhir/base")); - HttpGet httpGet = new HttpGet("http://localhost:" + ourPort + "/Patient"); + HttpGet httpGet = new HttpGet(ourServer.getBaseUrl() + "/Patient"); HttpResponse status = ourClient.execute(httpGet); String responseContent = IOUtils.toString(status.getEntity().getContent()); IOUtils.closeQuietly(status.getEntity().getContent()); @@ -118,37 +118,10 @@ public class SearchWithServerAddressStrategyDstu3Test { @AfterAll public static void afterClassClearContext() throws Exception { - JettyUtil.closeServer(ourServer); TestUtil.randomizeLocaleAndTimezone(); } - - @BeforeAll - public static void beforeClass() throws Exception { - ourServer = new Server(0); - - DummyPatientResourceProvider patientProvider = new DummyPatientResourceProvider(); - - ServletHandler proxyHandler = new ServletHandler(); - ourServlet = new RestfulServer(ourCtx); - ourServlet.setPagingProvider(new FifoMemoryPagingProvider(10)); - ourServlet.setResourceProviders(patientProvider); - ourServlet.setDefaultResponseEncoding(EncodingEnum.XML); - - ServletHolder servletHolder = new ServletHolder(ourServlet); - proxyHandler.addServletWithMapping(servletHolder, "/*"); - ourServer.setHandler(proxyHandler); - JettyUtil.startServer(ourServer); - ourPort = JettyUtil.getPortForStartedServer(ourServer); - - PoolingHttpClientConnectionManager connectionManager = new PoolingHttpClientConnectionManager(5000, TimeUnit.MILLISECONDS); - HttpClientBuilder builder = HttpClientBuilder.create(); - builder.setConnectionManager(connectionManager); - ourClient = builder.build(); - - } - public static class DummyPatientResourceProvider implements IResourceProvider { @Override @@ -159,7 +132,7 @@ public class SearchWithServerAddressStrategyDstu3Test { //@formatter:off @Search() public List searchByIdentifier() { - ArrayList retVal = new ArrayList(); + ArrayList retVal = new ArrayList<>(); retVal.add((Patient) new Patient().addName(new HumanName().addGiven("FAMILY")).setId("1")); retVal.add((Patient) new Patient().addName(new HumanName().addGiven("FAMILY")).setId("2")); return retVal; diff --git a/hapi-fhir-structures-dstu3/src/test/java/ca/uhn/fhir/rest/server/ServerExceptionDstu3Test.java b/hapi-fhir-structures-dstu3/src/test/java/ca/uhn/fhir/rest/server/ServerExceptionDstu3Test.java index 2b329c2b91d..0bf146ae48a 100644 --- a/hapi-fhir-structures-dstu3/src/test/java/ca/uhn/fhir/rest/server/ServerExceptionDstu3Test.java +++ b/hapi-fhir-structures-dstu3/src/test/java/ca/uhn/fhir/rest/server/ServerExceptionDstu3Test.java @@ -9,11 +9,14 @@ import ca.uhn.fhir.interceptor.api.Pointcut; import ca.uhn.fhir.rest.annotation.Create; import ca.uhn.fhir.rest.annotation.ResourceParam; import ca.uhn.fhir.rest.annotation.Search; +import ca.uhn.fhir.rest.api.EncodingEnum; import ca.uhn.fhir.rest.api.MethodOutcome; import ca.uhn.fhir.rest.server.exceptions.AuthenticationException; import ca.uhn.fhir.rest.server.exceptions.InternalErrorException; import ca.uhn.fhir.rest.server.exceptions.ResourceNotFoundException; +import ca.uhn.fhir.test.utilities.HttpClientExtension; import ca.uhn.fhir.test.utilities.JettyUtil; +import ca.uhn.fhir.test.utilities.server.RestfulServerExtension; import ca.uhn.fhir.util.TestUtil; import com.google.common.base.Charsets; import org.apache.commons.io.IOUtils; @@ -25,8 +28,8 @@ import org.apache.http.impl.client.CloseableHttpClient; import org.apache.http.impl.client.HttpClientBuilder; import org.apache.http.impl.conn.PoolingHttpClientConnectionManager; import org.eclipse.jetty.server.Server; -import org.eclipse.jetty.servlet.ServletHandler; -import org.eclipse.jetty.servlet.ServletHolder; +import org.eclipse.jetty.ee10.servlet.ServletHandler; +import org.eclipse.jetty.ee10.servlet.ServletHolder; import org.hl7.fhir.dstu3.model.IdType; import org.hl7.fhir.dstu3.model.OperationOutcome; import org.hl7.fhir.dstu3.model.OperationOutcome.IssueType; @@ -36,6 +39,7 @@ import org.junit.jupiter.api.AfterAll; import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.RegisterExtension; import java.io.IOException; import java.nio.charset.StandardCharsets; @@ -51,11 +55,17 @@ public class ServerExceptionDstu3Test { private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(ServerExceptionDstu3Test.class); public static Exception ourException; - private static CloseableHttpClient ourClient; - private static FhirContext ourCtx = FhirContext.forDstu3(); - private static int ourPort; - private static Server ourServer; - private static RestfulServer ourServlet; + private static final FhirContext ourCtx = FhirContext.forDstu3Cached(); + + @RegisterExtension + private RestfulServerExtension ourServer = new RestfulServerExtension(ourCtx) + .setDefaultResponseEncoding(EncodingEnum.XML) + .registerProvider(new DummyPatientResourceProvider()) + .withPagingProvider(new FifoMemoryPagingProvider(100)) + .setDefaultPrettyPrint(false); + + @RegisterExtension + private HttpClientExtension ourClient = new HttpClientExtension(); @AfterEach public void after() { @@ -72,7 +82,7 @@ public class ServerExceptionDstu3Test { .addResponseHeader("X-Foo", "BAR BAR"); - HttpGet httpGet = new HttpGet("http://localhost:" + ourPort + "/Patient"); + HttpGet httpGet = new HttpGet(ourServer.getBaseUrl() + "/Patient"); CloseableHttpResponse status = ourClient.execute(httpGet); try { String responseContent = IOUtils.toString(status.getEntity().getContent(), StandardCharsets.UTF_8); @@ -100,7 +110,7 @@ public class ServerExceptionDstu3Test { ourException = new InternalErrorException("Error", operationOutcome); - HttpGet httpGet = new HttpGet("http://localhost:" + ourPort + "/Patient?_format=json"); + HttpGet httpGet = new HttpGet(ourServer.getBaseUrl() + "/Patient?_format=json"); try (CloseableHttpResponse status = ourClient.execute(httpGet)) { byte[] responseContentBytes = IOUtils.toByteArray(status.getEntity().getContent()); String responseContent = new String(responseContentBytes, Charsets.UTF_8); @@ -116,7 +126,7 @@ public class ServerExceptionDstu3Test { ourException = new NullPointerException("Hello"); - HttpGet httpGet = new HttpGet("http://localhost:" + ourPort + "/Patient?_format=json"); + HttpGet httpGet = new HttpGet(ourServer.getBaseUrl() + "/Patient?_format=json"); try (CloseableHttpResponse status = ourClient.execute(httpGet)) { assertEquals(500, status.getStatusLine().getStatusCode()); byte[] responseContentBytes = IOUtils.toByteArray(status.getEntity().getContent()); @@ -133,7 +143,7 @@ public class ServerExceptionDstu3Test { ourException = new IOException("Hello"); - HttpGet httpGet = new HttpGet("http://localhost:" + ourPort + "/Patient?_format=json"); + HttpGet httpGet = new HttpGet(ourServer.getBaseUrl() + "/Patient?_format=json"); try (CloseableHttpResponse status = ourClient.execute(httpGet)) { assertEquals(500, status.getStatusLine().getStatusCode()); byte[] responseContentBytes = IOUtils.toByteArray(status.getEntity().getContent()); @@ -148,14 +158,14 @@ public class ServerExceptionDstu3Test { @Test public void testInterceptorThrowsNonHapiUncheckedExceptionHandledCleanly() throws Exception { - ourServlet.getInterceptorService().registerAnonymousInterceptor(Pointcut.SERVER_INCOMING_REQUEST_PRE_HANDLED, new IAnonymousInterceptor() { + ourServer.getInterceptorService().registerAnonymousInterceptor(Pointcut.SERVER_INCOMING_REQUEST_PRE_HANDLED, new IAnonymousInterceptor() { @Override public void invoke(IPointcut thePointcut, HookParams theArgs) { throw new NullPointerException("Hello"); } }); - HttpGet httpGet = new HttpGet("http://localhost:" + ourPort + "/Patient?_format=json"); + HttpGet httpGet = new HttpGet(ourServer.getBaseUrl() + "/Patient?_format=json"); try (CloseableHttpResponse status = ourClient.execute(httpGet)) { assertEquals(500, status.getStatusLine().getStatusCode()); byte[] responseContentBytes = IOUtils.toByteArray(status.getEntity().getContent()); @@ -165,7 +175,7 @@ public class ServerExceptionDstu3Test { assertThat(responseContent, containsString("\"diagnostics\":\"Hello\"")); } - ourServlet.getInterceptorService().unregisterAllInterceptors(); + ourServer.getInterceptorService().unregisterAllInterceptors(); } @@ -173,7 +183,7 @@ public class ServerExceptionDstu3Test { @Test public void testPostWithNoBody() throws IOException { - HttpPost httpPost = new HttpPost("http://localhost:" + ourPort + "/Patient"); + HttpPost httpPost = new HttpPost(ourServer.getBaseUrl() + "/Patient"); try (CloseableHttpResponse status = ourClient.execute(httpPost)) { String responseContent = IOUtils.toString(status.getEntity().getContent(), StandardCharsets.UTF_8); ourLog.info(status.getStatusLine().toString()); @@ -194,7 +204,7 @@ public class ServerExceptionDstu3Test { ourException = new AuthenticationException().addAuthenticateHeaderForRealm("REALM"); - HttpGet httpGet = new HttpGet("http://localhost:" + ourPort + "/Patient"); + HttpGet httpGet = new HttpGet(ourServer.getBaseUrl() + "/Patient"); try (CloseableHttpResponse status = ourClient.execute(httpGet)) { String responseContent = IOUtils.toString(status.getEntity().getContent(), StandardCharsets.UTF_8); ourLog.info(status.getStatusLine().toString()); @@ -231,32 +241,8 @@ public class ServerExceptionDstu3Test { @AfterAll public static void afterClassClearContext() throws Exception { - JettyUtil.closeServer(ourServer); TestUtil.randomizeLocaleAndTimezone(); } - @BeforeAll - public static void beforeClass() throws Exception { - ourServer = new Server(0); - - DummyPatientResourceProvider patientProvider = new DummyPatientResourceProvider(); - - ServletHandler proxyHandler = new ServletHandler(); - ourServlet = new RestfulServer(ourCtx); - ourServlet.setPagingProvider(new FifoMemoryPagingProvider(10)); - - ourServlet.setResourceProviders(patientProvider); - ServletHolder servletHolder = new ServletHolder(ourServlet); - proxyHandler.addServletWithMapping(servletHolder, "/*"); - ourServer.setHandler(proxyHandler); - JettyUtil.startServer(ourServer); - ourPort = JettyUtil.getPortForStartedServer(ourServer); - - PoolingHttpClientConnectionManager connectionManager = new PoolingHttpClientConnectionManager(5000, TimeUnit.MILLISECONDS); - HttpClientBuilder builder = HttpClientBuilder.create(); - builder.setConnectionManager(connectionManager); - ourClient = builder.build(); - - } } diff --git a/hapi-fhir-structures-dstu3/src/test/java/ca/uhn/fhir/rest/server/ServerMimetypeDstu3Test.java b/hapi-fhir-structures-dstu3/src/test/java/ca/uhn/fhir/rest/server/ServerMimetypeDstu3Test.java index 258ecf21f3e..cdef78525ce 100644 --- a/hapi-fhir-structures-dstu3/src/test/java/ca/uhn/fhir/rest/server/ServerMimetypeDstu3Test.java +++ b/hapi-fhir-structures-dstu3/src/test/java/ca/uhn/fhir/rest/server/ServerMimetypeDstu3Test.java @@ -10,7 +10,9 @@ import ca.uhn.fhir.rest.api.Constants; import ca.uhn.fhir.rest.api.EncodingEnum; import ca.uhn.fhir.rest.api.MethodOutcome; import ca.uhn.fhir.rest.client.MyPatientWithExtensions; +import ca.uhn.fhir.test.utilities.HttpClientExtension; import ca.uhn.fhir.test.utilities.JettyUtil; +import ca.uhn.fhir.test.utilities.server.RestfulServerExtension; import ca.uhn.fhir.util.TestUtil; import org.apache.commons.io.IOUtils; import org.apache.http.HttpResponse; @@ -25,8 +27,8 @@ import org.apache.http.impl.client.CloseableHttpClient; import org.apache.http.impl.client.HttpClientBuilder; import org.apache.http.impl.conn.PoolingHttpClientConnectionManager; import org.eclipse.jetty.server.Server; -import org.eclipse.jetty.servlet.ServletHandler; -import org.eclipse.jetty.servlet.ServletHolder; +import org.eclipse.jetty.ee10.servlet.ServletHandler; +import org.eclipse.jetty.ee10.servlet.ServletHolder; import org.hl7.fhir.dstu3.model.CapabilityStatement; import org.hl7.fhir.dstu3.model.CodeType; import org.hl7.fhir.dstu3.model.DateType; @@ -37,6 +39,7 @@ import org.hl7.fhir.instance.model.api.IBaseResource; import org.junit.jupiter.api.AfterAll; import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.RegisterExtension; import java.net.URI; import java.nio.charset.StandardCharsets; @@ -51,16 +54,22 @@ import static org.hamcrest.Matchers.not; import static org.junit.jupiter.api.Assertions.assertEquals; public class ServerMimetypeDstu3Test { - private static CloseableHttpClient ourClient; - private static FhirContext ourCtx = FhirContext.forDstu3(); + private static final FhirContext ourCtx = FhirContext.forDstu3Cached(); private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(ServerMimetypeDstu3Test.class); - private static int ourPort; - private static Server ourServer; + @RegisterExtension + private RestfulServerExtension ourServer = new RestfulServerExtension(ourCtx) + .registerProvider(new PatientProvider()) + .withPagingProvider(new FifoMemoryPagingProvider(100)) + .setDefaultResponseEncoding(EncodingEnum.XML) + .setDefaultPrettyPrint(false); + + @RegisterExtension + private HttpClientExtension ourClient = new HttpClientExtension(); @Test public void testConformanceMetadataUsesNewMimetypes() throws Exception { - HttpGet httpGet = new HttpGet("http://localhost:" + ourPort + "/metadata"); + HttpGet httpGet = new HttpGet(ourServer.getBaseUrl() + "/metadata"); CloseableHttpResponse status = ourClient.execute(httpGet); try { String content = IOUtils.toString(status.getEntity().getContent(), StandardCharsets.UTF_8); @@ -91,7 +100,7 @@ public class ServerMimetypeDstu3Test { String enc = ourCtx.newXmlParser().encodeResourceToString(p); String expectedResponseContent = ""; - HttpPost httpPost = new HttpPost("http://localhost:" + ourPort + "/Patient"); + HttpPost httpPost = new HttpPost(ourServer.getBaseUrl() + "/Patient"); httpPost.setEntity(new StringEntity(enc, ContentType.parse(Constants.CT_FHIR_XML + "; charset=utf-8"))); HttpResponse status = ourClient.execute(httpPost); @@ -107,7 +116,7 @@ public class ServerMimetypeDstu3Test { @Test public void testHttpTraceNotEnabled() throws Exception { - HttpTrace req = new HttpTrace("http://localhost:" + ourPort + "/Patient"); + HttpTrace req = new HttpTrace(ourServer.getBaseUrl() + "/Patient"); CloseableHttpResponse status = ourClient.execute(req); try { ourLog.info(status.toString()); @@ -124,7 +133,7 @@ public class ServerMimetypeDstu3Test { public String getMethod() { return "TRACK"; }}; - req.setURI(new URI("http://localhost:" + ourPort + "/Patient")); + req.setURI(new URI(ourServer.getBaseUrl() + "/Patient")); CloseableHttpResponse status = ourClient.execute(req); try { @@ -142,7 +151,7 @@ public class ServerMimetypeDstu3Test { String enc = ourCtx.newXmlParser().encodeResourceToString(p); String expectedResponseContent = ""; - HttpPost httpPost = new HttpPost("http://localhost:" + ourPort + "/Patient"); + HttpPost httpPost = new HttpPost(ourServer.getBaseUrl() + "/Patient"); httpPost.setEntity(new StringEntity(enc, ContentType.parse(Constants.CT_FHIR_XML_NEW + "; charset=utf-8"))); httpPost.addHeader(Constants.HEADER_PREFER, Constants.HEADER_PREFER_RETURN + "=" + Constants.HEADER_PREFER_RETURN_OPERATION_OUTCOME); HttpResponse status = ourClient.execute(httpPost); @@ -164,7 +173,7 @@ public class ServerMimetypeDstu3Test { String enc = ourCtx.newXmlParser().encodeResourceToString(p); String expectedResponseContent = ""; - HttpPost httpPost = new HttpPost("http://localhost:" + ourPort + "/Patient"); + HttpPost httpPost = new HttpPost(ourServer.getBaseUrl() + "/Patient"); httpPost.setEntity(new StringEntity(enc, ContentType.parse(Constants.CT_FHIR_XML + "; charset=utf-8"))); httpPost.addHeader(Constants.HEADER_ACCEPT, Constants.CT_FHIR_XML_NEW); HttpResponse status = ourClient.execute(httpPost); @@ -186,7 +195,7 @@ public class ServerMimetypeDstu3Test { String enc = ourCtx.newJsonParser().encodeResourceToString(p); String expectedResponseContent = "{\"resourceType\":\"Patient\",\"id\":\"1\",\"meta\":{\"versionId\":\"1\"},\"name\":[{\"family\":\"FAMILY\"}]}"; - HttpPost httpPost = new HttpPost("http://localhost:" + ourPort + "/Patient"); + HttpPost httpPost = new HttpPost(ourServer.getBaseUrl() + "/Patient"); httpPost.setEntity(new StringEntity(enc, ContentType.parse(Constants.CT_FHIR_JSON + "; charset=utf-8"))); HttpResponse status = ourClient.execute(httpPost); @@ -207,7 +216,7 @@ public class ServerMimetypeDstu3Test { String enc = ourCtx.newJsonParser().encodeResourceToString(p); String expectedResponseContent = "{\"resourceType\":\"Patient\",\"id\":\"1\",\"meta\":{\"versionId\":\"1\"},\"name\":[{\"family\":\"FAMILY\"}]}"; - HttpPost httpPost = new HttpPost("http://localhost:" + ourPort + "/Patient"); + HttpPost httpPost = new HttpPost(ourServer.getBaseUrl() + "/Patient"); httpPost.setEntity(new StringEntity(enc, ContentType.parse(Constants.CT_FHIR_JSON_NEW + "; charset=utf-8"))); HttpResponse status = ourClient.execute(httpPost); @@ -228,7 +237,7 @@ public class ServerMimetypeDstu3Test { String enc = ourCtx.newJsonParser().encodeResourceToString(p); String expectedResponseContent = "{\"resourceType\":\"OperationOutcome\",\"issue\":[{\"diagnostics\":\"FAMILY\"}]}"; - HttpPost httpPost = new HttpPost("http://localhost:" + ourPort + "/Patient"); + HttpPost httpPost = new HttpPost(ourServer.getBaseUrl() + "/Patient"); httpPost.setEntity(new StringEntity(enc, ContentType.parse(Constants.CT_FHIR_JSON + "; charset=utf-8"))); httpPost.addHeader(Constants.HEADER_ACCEPT, Constants.CT_FHIR_JSON_NEW); httpPost.addHeader(Constants.HEADER_PREFER, Constants.HEADER_PREFER_RETURN + "=" + Constants.HEADER_PREFER_RETURN_OPERATION_OUTCOME); @@ -247,7 +256,7 @@ public class ServerMimetypeDstu3Test { @Test public void testSearchWithFormatXmlSimple() throws Exception { - HttpGet httpGet = new HttpGet("http://localhost:" + ourPort + "/Patient?_format=xml"); + HttpGet httpGet = new HttpGet(ourServer.getBaseUrl() + "/Patient?_format=xml"); HttpResponse status = ourClient.execute(httpGet); String responseContent = IOUtils.toString(status.getEntity().getContent(), StandardCharsets.UTF_8); @@ -264,7 +273,7 @@ public class ServerMimetypeDstu3Test { @Test public void testSearchWithFormatXmlLegacy() throws Exception { - HttpGet httpGet = new HttpGet("http://localhost:" + ourPort + "/Patient?_format=" + Constants.CT_FHIR_XML); + HttpGet httpGet = new HttpGet(ourServer.getBaseUrl() + "/Patient?_format=" + Constants.CT_FHIR_XML); HttpResponse status = ourClient.execute(httpGet); String responseContent = IOUtils.toString(status.getEntity().getContent(), StandardCharsets.UTF_8); @@ -281,7 +290,7 @@ public class ServerMimetypeDstu3Test { @Test public void testSearchWithFormatXmlNew() throws Exception { - HttpGet httpGet = new HttpGet("http://localhost:" + ourPort + "/Patient?_format=" + Constants.CT_FHIR_XML_NEW); + HttpGet httpGet = new HttpGet(ourServer.getBaseUrl() + "/Patient?_format=" + Constants.CT_FHIR_XML_NEW); HttpResponse status = ourClient.execute(httpGet); String responseContent = IOUtils.toString(status.getEntity().getContent(), StandardCharsets.UTF_8); @@ -300,7 +309,7 @@ public class ServerMimetypeDstu3Test { @Test public void testSearchWithFormatJsonSimple() throws Exception { - HttpGet httpGet = new HttpGet("http://localhost:" + ourPort + "/Patient?_format=json"); + HttpGet httpGet = new HttpGet(ourServer.getBaseUrl() + "/Patient?_format=json"); HttpResponse status = ourClient.execute(httpGet); String responseContent = IOUtils.toString(status.getEntity().getContent(), StandardCharsets.UTF_8); @@ -316,7 +325,7 @@ public class ServerMimetypeDstu3Test { @Test public void testSearchWithFormatJsonLegacy() throws Exception { - HttpGet httpGet = new HttpGet("http://localhost:" + ourPort + "/Patient?_format=" + Constants.CT_FHIR_JSON); + HttpGet httpGet = new HttpGet(ourServer.getBaseUrl() + "/Patient?_format=" + Constants.CT_FHIR_JSON); HttpResponse status = ourClient.execute(httpGet); String responseContent = IOUtils.toString(status.getEntity().getContent(), StandardCharsets.UTF_8); @@ -332,7 +341,7 @@ public class ServerMimetypeDstu3Test { @Test public void testSearchWithFormatJsonNew() throws Exception { - HttpGet httpGet = new HttpGet("http://localhost:" + ourPort + "/Patient?_format=" + Constants.CT_FHIR_JSON_NEW); + HttpGet httpGet = new HttpGet(ourServer.getBaseUrl() + "/Patient?_format=" + Constants.CT_FHIR_JSON_NEW); HttpResponse status = ourClient.execute(httpGet); String responseContent = IOUtils.toString(status.getEntity().getContent(), StandardCharsets.UTF_8); @@ -347,33 +356,9 @@ public class ServerMimetypeDstu3Test { @AfterAll public static void afterClassClearContext() throws Exception { - JettyUtil.closeServer(ourServer); TestUtil.randomizeLocaleAndTimezone(); } - - @BeforeAll - public static void beforeClass() throws Exception { - ourServer = new Server(0); - - PatientProvider patientProvider = new PatientProvider(); - - ServletHandler proxyHandler = new ServletHandler(); - RestfulServer servlet = new RestfulServer(ourCtx); - servlet.setResourceProviders(patientProvider); - servlet.setDefaultResponseEncoding(EncodingEnum.XML); - - ServletHolder servletHolder = new ServletHolder(servlet); - proxyHandler.addServletWithMapping(servletHolder, "/*"); - ourServer.setHandler(proxyHandler); - JettyUtil.startServer(ourServer); - ourPort = JettyUtil.getPortForStartedServer(ourServer); - - PoolingHttpClientConnectionManager connectionManager = new PoolingHttpClientConnectionManager(5000, TimeUnit.MILLISECONDS); - HttpClientBuilder builder = HttpClientBuilder.create(); - builder.setConnectionManager(connectionManager); - ourClient = builder.build(); - - } + public static class PatientProvider implements IResourceProvider { diff --git a/hapi-fhir-structures-dstu3/src/test/java/ca/uhn/fhir/rest/server/ServerUsingOldTypesDstu3Test.java b/hapi-fhir-structures-dstu3/src/test/java/ca/uhn/fhir/rest/server/ServerUsingOldTypesDstu3Test.java index 45e3a4ecb9b..6cd10c180cc 100644 --- a/hapi-fhir-structures-dstu3/src/test/java/ca/uhn/fhir/rest/server/ServerUsingOldTypesDstu3Test.java +++ b/hapi-fhir-structures-dstu3/src/test/java/ca/uhn/fhir/rest/server/ServerUsingOldTypesDstu3Test.java @@ -14,7 +14,7 @@ import org.hl7.fhir.instance.model.api.IBaseResource; import org.junit.jupiter.api.AfterAll; import org.junit.jupiter.api.Test; -import javax.servlet.ServletException; +import jakarta.servlet.ServletException; import static org.hamcrest.MatcherAssert.assertThat; import static org.junit.jupiter.api.Assertions.fail; diff --git a/hapi-fhir-structures-dstu3/src/test/java/ca/uhn/fhir/rest/server/UnclassifiedServerExceptionDstu3Test.java b/hapi-fhir-structures-dstu3/src/test/java/ca/uhn/fhir/rest/server/UnclassifiedServerExceptionDstu3Test.java index 4e03d07bf6e..a2ca5a8680d 100644 --- a/hapi-fhir-structures-dstu3/src/test/java/ca/uhn/fhir/rest/server/UnclassifiedServerExceptionDstu3Test.java +++ b/hapi-fhir-structures-dstu3/src/test/java/ca/uhn/fhir/rest/server/UnclassifiedServerExceptionDstu3Test.java @@ -2,8 +2,11 @@ package ca.uhn.fhir.rest.server; import ca.uhn.fhir.context.FhirContext; import ca.uhn.fhir.rest.annotation.Search; +import ca.uhn.fhir.rest.api.EncodingEnum; import ca.uhn.fhir.rest.server.exceptions.UnclassifiedServerFailureException; +import ca.uhn.fhir.test.utilities.HttpClientExtension; import ca.uhn.fhir.test.utilities.JettyUtil; +import ca.uhn.fhir.test.utilities.server.RestfulServerExtension; import ca.uhn.fhir.util.TestUtil; import org.apache.commons.io.IOUtils; import org.apache.http.client.methods.CloseableHttpResponse; @@ -12,8 +15,8 @@ import org.apache.http.impl.client.CloseableHttpClient; import org.apache.http.impl.client.HttpClientBuilder; import org.apache.http.impl.conn.PoolingHttpClientConnectionManager; import org.eclipse.jetty.server.Server; -import org.eclipse.jetty.servlet.ServletHandler; -import org.eclipse.jetty.servlet.ServletHolder; +import org.eclipse.jetty.ee10.servlet.ServletHandler; +import org.eclipse.jetty.ee10.servlet.ServletHolder; import org.hl7.fhir.dstu3.model.OperationOutcome; import org.hl7.fhir.dstu3.model.OperationOutcome.IssueType; import org.hl7.fhir.dstu3.model.Patient; @@ -21,6 +24,7 @@ import org.hl7.fhir.instance.model.api.IBaseResource; import org.junit.jupiter.api.AfterAll; import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.RegisterExtension; import java.nio.charset.StandardCharsets; import java.util.List; @@ -32,13 +36,20 @@ import static org.junit.jupiter.api.Assertions.assertEquals; public class UnclassifiedServerExceptionDstu3Test { - private static CloseableHttpClient ourClient; - private static FhirContext ourCtx = FhirContext.forDstu3(); + private static final FhirContext ourCtx = FhirContext.forDstu3Cached(); private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(UnclassifiedServerExceptionDstu3Test.class); - private static int ourPort; - private static Server ourServer; public static UnclassifiedServerFailureException ourException; + @RegisterExtension + private RestfulServerExtension ourServer = new RestfulServerExtension(ourCtx) + .setDefaultResponseEncoding(EncodingEnum.XML) + .registerProvider(new DummyPatientResourceProvider()) + .withPagingProvider(new FifoMemoryPagingProvider(10)) + .setDefaultPrettyPrint(false); + + @RegisterExtension + private HttpClientExtension ourClient = new HttpClientExtension(); + @Test public void testSearch() throws Exception { @@ -46,7 +57,7 @@ public class UnclassifiedServerExceptionDstu3Test { operationOutcome.addIssue().setCode(IssueType.BUSINESSRULE); ourException = new UnclassifiedServerFailureException(477, "SOME MESSAGE", operationOutcome); - HttpGet httpGet = new HttpGet("http://localhost:" + ourPort + "/Patient"); + HttpGet httpGet = new HttpGet(ourServer.getBaseUrl() + "/Patient"); CloseableHttpResponse status = ourClient.execute(httpGet); try { String responseContent = IOUtils.toString(status.getEntity().getContent(), StandardCharsets.UTF_8); @@ -63,33 +74,9 @@ public class UnclassifiedServerExceptionDstu3Test { @AfterAll public static void afterClassClearContext() throws Exception { - JettyUtil.closeServer(ourServer); TestUtil.randomizeLocaleAndTimezone(); } - @BeforeAll - public static void beforeClass() throws Exception { - ourServer = new Server(0); - - DummyPatientResourceProvider patientProvider = new DummyPatientResourceProvider(); - - ServletHandler proxyHandler = new ServletHandler(); - RestfulServer servlet = new RestfulServer(ourCtx); - servlet.setPagingProvider(new FifoMemoryPagingProvider(10)); - - servlet.setResourceProviders(patientProvider); - ServletHolder servletHolder = new ServletHolder(servlet); - proxyHandler.addServletWithMapping(servletHolder, "/*"); - ourServer.setHandler(proxyHandler); - JettyUtil.startServer(ourServer); - ourPort = JettyUtil.getPortForStartedServer(ourServer); - - PoolingHttpClientConnectionManager connectionManager = new PoolingHttpClientConnectionManager(5000, TimeUnit.MILLISECONDS); - HttpClientBuilder builder = HttpClientBuilder.create(); - builder.setConnectionManager(connectionManager); - ourClient = builder.build(); - - } public static class DummyPatientResourceProvider implements IResourceProvider { diff --git a/hapi-fhir-structures-dstu3/src/test/java/ca/uhn/fhir/rest/server/ValidateDstu3Test.java b/hapi-fhir-structures-dstu3/src/test/java/ca/uhn/fhir/rest/server/ValidateDstu3Test.java index c18f4175cee..844069431a2 100644 --- a/hapi-fhir-structures-dstu3/src/test/java/ca/uhn/fhir/rest/server/ValidateDstu3Test.java +++ b/hapi-fhir-structures-dstu3/src/test/java/ca/uhn/fhir/rest/server/ValidateDstu3Test.java @@ -8,7 +8,9 @@ import ca.uhn.fhir.rest.api.Constants; import ca.uhn.fhir.rest.api.EncodingEnum; import ca.uhn.fhir.rest.api.MethodOutcome; import ca.uhn.fhir.rest.api.ValidationModeEnum; +import ca.uhn.fhir.test.utilities.HttpClientExtension; import ca.uhn.fhir.test.utilities.JettyUtil; +import ca.uhn.fhir.test.utilities.server.RestfulServerExtension; import ca.uhn.fhir.util.TestUtil; import org.apache.commons.io.IOUtils; import org.apache.http.HttpResponse; @@ -20,8 +22,8 @@ import org.apache.http.impl.client.CloseableHttpClient; import org.apache.http.impl.client.HttpClientBuilder; import org.apache.http.impl.conn.PoolingHttpClientConnectionManager; import org.eclipse.jetty.server.Server; -import org.eclipse.jetty.servlet.ServletHandler; -import org.eclipse.jetty.servlet.ServletHolder; +import org.eclipse.jetty.ee10.servlet.ServletHandler; +import org.eclipse.jetty.ee10.servlet.ServletHolder; import org.hl7.fhir.dstu3.model.CodeType; import org.hl7.fhir.dstu3.model.IdType; import org.hl7.fhir.dstu3.model.OperationOutcome; @@ -33,6 +35,7 @@ import org.junit.jupiter.api.AfterAll; import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.RegisterExtension; import java.util.concurrent.TimeUnit; @@ -41,8 +44,7 @@ import static org.hamcrest.Matchers.stringContainsInOrder; import static org.junit.jupiter.api.Assertions.assertEquals; public class ValidateDstu3Test { - private static CloseableHttpClient ourClient; - private static FhirContext ourCtx = FhirContext.forDstu3(); + private static final FhirContext ourCtx = FhirContext.forDstu3Cached(); private static EncodingEnum ourLastEncoding; private static IdType ourLastId; private static ValidationModeEnum ourLastMode; @@ -51,9 +53,17 @@ public class ValidateDstu3Test { private static String ourLastResourceBody; private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(ValidateDstu3Test.class); private static OperationOutcome ourOutcomeToReturn; - private static int ourPort; - private static Server ourServer; + @RegisterExtension + private RestfulServerExtension ourServer = new RestfulServerExtension(ourCtx) + .setDefaultResponseEncoding(EncodingEnum.XML) + .registerProvider(new PatientProvider()) + .registerProvider(new OrganizationProvider()) + .withPagingProvider(new FifoMemoryPagingProvider(100)) + .setDefaultPrettyPrint(false); + + @RegisterExtension + private HttpClientExtension ourClient = new HttpClientExtension(); @BeforeEach() public void before() { @@ -74,7 +84,7 @@ public class ValidateDstu3Test { Parameters params = new Parameters(); params.addParameter().setName("resource").setResource(patient); - HttpPost httpPost = new HttpPost("http://localhost:" + ourPort + "/Patient/$validate"); + HttpPost httpPost = new HttpPost(ourServer.getBaseUrl() + "/Patient/$validate"); httpPost.setEntity(new StringEntity(ourCtx.newXmlParser().encodeResourceToString(params), ContentType.create(Constants.CT_FHIR_XML, "UTF-8"))); HttpResponse status = ourClient.execute(httpPost); @@ -97,7 +107,7 @@ public class ValidateDstu3Test { params.addParameter().setName("resource").setResource(patient); params.addParameter().setName("mode").setValue(new CodeType(" ")); - HttpPost httpPost = new HttpPost("http://localhost:" + ourPort + "/Patient/$validate"); + HttpPost httpPost = new HttpPost(ourServer.getBaseUrl() + "/Patient/$validate"); httpPost.setEntity(new StringEntity(ourCtx.newXmlParser().encodeResourceToString(params), ContentType.create(Constants.CT_FHIR_XML, "UTF-8"))); HttpResponse status = ourClient.execute(httpPost); @@ -118,7 +128,7 @@ public class ValidateDstu3Test { params.addParameter().setName("resource").setResource(patient); params.addParameter().setName("mode").setValue(new CodeType("AAA")); - HttpPost httpPost = new HttpPost("http://localhost:" + ourPort + "/Patient/$validate"); + HttpPost httpPost = new HttpPost(ourServer.getBaseUrl() + "/Patient/$validate"); httpPost.setEntity(new StringEntity(ourCtx.newXmlParser().encodeResourceToString(params), ContentType.create(Constants.CT_FHIR_XML, "UTF-8"))); HttpResponse status = ourClient.execute(httpPost); @@ -140,7 +150,7 @@ public class ValidateDstu3Test { Parameters params = new Parameters(); params.addParameter().setName("mode").setValue(new CodeType("create")); - HttpPost httpPost = new HttpPost("http://localhost:" + ourPort + "/Patient/$validate"); + HttpPost httpPost = new HttpPost(ourServer.getBaseUrl() + "/Patient/$validate"); httpPost.setEntity(new StringEntity(ourCtx.newXmlParser().encodeResourceToString(params), ContentType.create(Constants.CT_FHIR_XML, "UTF-8"))); HttpResponse status = ourClient.execute(httpPost); @@ -156,7 +166,7 @@ public class ValidateDstu3Test { ourOutcomeToReturn = new OperationOutcome(); ourOutcomeToReturn.addIssue().setDiagnostics("FOOBAR"); - HttpGet httpGet = new HttpGet("http://localhost:" + ourPort + "/Patient/123/$validate"); + HttpGet httpGet = new HttpGet(ourServer.getBaseUrl() + "/Patient/123/$validate"); HttpResponse status = ourClient.execute(httpGet); String resp = IOUtils.toString(status.getEntity().getContent()); @@ -180,7 +190,7 @@ public class ValidateDstu3Test { Parameters params = new Parameters(); params.addParameter().setName("resource").setResource(org); - HttpPost httpPost = new HttpPost("http://localhost:" + ourPort + "/Organization/$validate"); + HttpPost httpPost = new HttpPost(ourServer.getBaseUrl() + "/Organization/$validate"); httpPost.setEntity(new StringEntity(ourCtx.newJsonParser().encodeResourceToString(params), ContentType.create(Constants.CT_FHIR_JSON, "UTF-8"))); HttpResponse status = ourClient.execute(httpPost); @@ -203,7 +213,7 @@ public class ValidateDstu3Test { params.addParameter().setName("profile").setValue(new StringType("http://foo")); params.addParameter().setName("mode").setValue(new StringType(ValidationModeEnum.CREATE.getCode())); - HttpPost httpPost = new HttpPost("http://localhost:" + ourPort + "/Patient/$validate"); + HttpPost httpPost = new HttpPost(ourServer.getBaseUrl() + "/Patient/$validate"); httpPost.setEntity(new StringEntity(ourCtx.newXmlParser().encodeResourceToString(params), ContentType.create(Constants.CT_FHIR_XML, "UTF-8"))); HttpResponse status = ourClient.execute(httpPost); @@ -231,7 +241,7 @@ public class ValidateDstu3Test { Parameters params = new Parameters(); params.addParameter().setName("resource").setResource(patient); - HttpPost httpPost = new HttpPost("http://localhost:" + ourPort + "/Patient/$validate"); + HttpPost httpPost = new HttpPost(ourServer.getBaseUrl() + "/Patient/$validate"); httpPost.setEntity(new StringEntity(ourCtx.newXmlParser().encodeResourceToString(params), ContentType.create(Constants.CT_FHIR_XML, "UTF-8"))); HttpResponse status = ourClient.execute(httpPost); @@ -245,32 +255,9 @@ public class ValidateDstu3Test { @AfterAll public static void afterClassClearContext() throws Exception { - JettyUtil.closeServer(ourServer); TestUtil.randomizeLocaleAndTimezone(); } - @BeforeAll - public static void beforeClass() throws Exception { - ourServer = new Server(0); - - PatientProvider patientProvider = new PatientProvider(); - - ServletHandler proxyHandler = new ServletHandler(); - RestfulServer servlet = new RestfulServer(ourCtx); - servlet.setResourceProviders(patientProvider, new OrganizationProvider()); - servlet.setDefaultResponseEncoding(EncodingEnum.XML); - ServletHolder servletHolder = new ServletHolder(servlet); - proxyHandler.addServletWithMapping(servletHolder, "/*"); - ourServer.setHandler(proxyHandler); - JettyUtil.startServer(ourServer); - ourPort = JettyUtil.getPortForStartedServer(ourServer); - - PoolingHttpClientConnectionManager connectionManager = new PoolingHttpClientConnectionManager(5000, TimeUnit.MILLISECONDS); - HttpClientBuilder builder = HttpClientBuilder.create(); - builder.setConnectionManager(connectionManager); - ourClient = builder.build(); - - } public static class OrganizationProvider implements IResourceProvider { diff --git a/hapi-fhir-structures-dstu3/src/test/java/ca/uhn/fhir/rest/server/interceptor/BanUnsupprtedHttpMethodsInterceptorDstu3Test.java b/hapi-fhir-structures-dstu3/src/test/java/ca/uhn/fhir/rest/server/interceptor/BanUnsupprtedHttpMethodsInterceptorDstu3Test.java index a4296c69645..d5dcec14b98 100644 --- a/hapi-fhir-structures-dstu3/src/test/java/ca/uhn/fhir/rest/server/interceptor/BanUnsupprtedHttpMethodsInterceptorDstu3Test.java +++ b/hapi-fhir-structures-dstu3/src/test/java/ca/uhn/fhir/rest/server/interceptor/BanUnsupprtedHttpMethodsInterceptorDstu3Test.java @@ -3,10 +3,15 @@ package ca.uhn.fhir.rest.server.interceptor; import ca.uhn.fhir.context.FhirContext; import ca.uhn.fhir.rest.annotation.IdParam; import ca.uhn.fhir.rest.annotation.Read; +import ca.uhn.fhir.rest.api.EncodingEnum; +import ca.uhn.fhir.rest.server.CreateDstu3Test; +import ca.uhn.fhir.rest.server.FifoMemoryPagingProvider; import ca.uhn.fhir.rest.server.IResourceProvider; import ca.uhn.fhir.rest.server.RestfulServer; import ca.uhn.fhir.rest.server.exceptions.InvalidRequestException; +import ca.uhn.fhir.test.utilities.HttpClientExtension; import ca.uhn.fhir.test.utilities.JettyUtil; +import ca.uhn.fhir.test.utilities.server.RestfulServerExtension; import ca.uhn.fhir.util.TestUtil; import org.apache.commons.io.IOUtils; import org.apache.http.HttpResponse; @@ -19,13 +24,14 @@ import org.apache.http.impl.client.CloseableHttpClient; import org.apache.http.impl.client.HttpClientBuilder; import org.apache.http.impl.conn.PoolingHttpClientConnectionManager; import org.eclipse.jetty.server.Server; -import org.eclipse.jetty.servlet.ServletHandler; -import org.eclipse.jetty.servlet.ServletHolder; +import org.eclipse.jetty.ee10.servlet.ServletHandler; +import org.eclipse.jetty.ee10.servlet.ServletHolder; import org.hl7.fhir.dstu3.model.IdType; import org.hl7.fhir.dstu3.model.Patient; import org.junit.jupiter.api.AfterAll; import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.RegisterExtension; import java.net.URI; import java.util.HashMap; @@ -38,17 +44,22 @@ import static org.junit.jupiter.api.Assertions.assertEquals; public class BanUnsupprtedHttpMethodsInterceptorDstu3Test { - private static CloseableHttpClient ourClient; - private static FhirContext ourCtx = FhirContext.forDstu3(); + private static final FhirContext ourCtx = FhirContext.forDstu3Cached(); private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(BanUnsupprtedHttpMethodsInterceptorDstu3Test.class); - private static int ourPort; - private static Server ourServer; + @RegisterExtension + private RestfulServerExtension ourServer = new RestfulServerExtension(ourCtx) + .setDefaultResponseEncoding(EncodingEnum.XML) + .registerProvider(new DummyPatientResourceProvider()) + .registerInterceptor(new BanUnsupportedHttpMethodsInterceptor()) + .withPagingProvider(new FifoMemoryPagingProvider(100)) + .setDefaultPrettyPrint(false); - private static RestfulServer servlet; + @RegisterExtension + private HttpClientExtension ourClient = new HttpClientExtension(); @Test public void testHttpTraceNotEnabled() throws Exception { - HttpTrace req = new HttpTrace("http://localhost:" + ourPort + "/Patient"); + HttpTrace req = new HttpTrace(ourServer.getBaseUrl() + "/Patient"); CloseableHttpResponse status = ourClient.execute(req); try { ourLog.info(status.toString()); @@ -60,7 +71,7 @@ public class BanUnsupprtedHttpMethodsInterceptorDstu3Test { @Test public void testHeadJsonWithInvalidPatient() throws Exception { - HttpHead httpGet = new HttpHead("http://localhost:" + ourPort + "/Patient/123"); + HttpHead httpGet = new HttpHead(ourServer.getBaseUrl() + "/Patient/123"); HttpResponse status = ourClient.execute(httpGet); assertEquals(null, status.getEntity()); ourLog.info(status.toString()); @@ -71,7 +82,7 @@ public class BanUnsupprtedHttpMethodsInterceptorDstu3Test { @Test public void testHeadJsonWithValidPatient() throws Exception { - HttpHead httpGet = new HttpHead("http://localhost:" + ourPort + "/Patient/1"); + HttpHead httpGet = new HttpHead(ourServer.getBaseUrl() + "/Patient/1"); HttpResponse status = ourClient.execute(httpGet); assertEquals(null, status.getEntity()); ourLog.info(status.toString()); @@ -88,7 +99,7 @@ public class BanUnsupprtedHttpMethodsInterceptorDstu3Test { return "TRACK"; } }; - req.setURI(new URI("http://localhost:" + ourPort + "/Patient")); + req.setURI(new URI(ourServer.getBaseUrl() + "/Patient")); CloseableHttpResponse status = ourClient.execute(req); try { @@ -107,7 +118,7 @@ public class BanUnsupprtedHttpMethodsInterceptorDstu3Test { return "FOO"; } }; - req.setURI(new URI("http://localhost:" + ourPort + "/Patient")); + req.setURI(new URI(ourServer.getBaseUrl() + "/Patient")); CloseableHttpResponse status = ourClient.execute(req); try { @@ -121,7 +132,7 @@ public class BanUnsupprtedHttpMethodsInterceptorDstu3Test { @Test public void testRead() throws Exception { - HttpGet httpGet = new HttpGet("http://localhost:" + ourPort + "/Patient/1"); + HttpGet httpGet = new HttpGet(ourServer.getBaseUrl() + "/Patient/1"); HttpResponse status = ourClient.execute(httpGet); IOUtils.closeQuietly(status.getEntity().getContent()); @@ -130,33 +141,9 @@ public class BanUnsupprtedHttpMethodsInterceptorDstu3Test { @AfterAll public static void afterClassClearContext() throws Exception { - JettyUtil.closeServer(ourServer); TestUtil.randomizeLocaleAndTimezone(); } - @BeforeAll - public static void beforeClass() throws Exception { - ourServer = new Server(0); - - ServletHandler proxyHandler = new ServletHandler(); - servlet = new RestfulServer(ourCtx); - - servlet.setResourceProviders(new DummyPatientResourceProvider()); - servlet.registerInterceptor(new BanUnsupportedHttpMethodsInterceptor()); - - ServletHolder servletHolder = new ServletHolder(servlet); - proxyHandler.addServletWithMapping(servletHolder, "/*"); - ourServer.setHandler(proxyHandler); - JettyUtil.startServer(ourServer); - ourPort = JettyUtil.getPortForStartedServer(ourServer); - - PoolingHttpClientConnectionManager connectionManager = new PoolingHttpClientConnectionManager(5000, TimeUnit.MILLISECONDS); - HttpClientBuilder builder = HttpClientBuilder.create(); - builder.setConnectionManager(connectionManager); - ourClient = builder.build(); - - } - public static class DummyPatientResourceProvider implements IResourceProvider { private Patient createPatient1() { diff --git a/hapi-fhir-structures-dstu3/src/test/java/ca/uhn/fhir/rest/server/interceptor/CorsInterceptorDstu3Test.java b/hapi-fhir-structures-dstu3/src/test/java/ca/uhn/fhir/rest/server/interceptor/CorsInterceptorDstu3Test.java index 5098740f1b5..6255287bcfc 100644 --- a/hapi-fhir-structures-dstu3/src/test/java/ca/uhn/fhir/rest/server/interceptor/CorsInterceptorDstu3Test.java +++ b/hapi-fhir-structures-dstu3/src/test/java/ca/uhn/fhir/rest/server/interceptor/CorsInterceptorDstu3Test.java @@ -28,8 +28,8 @@ import org.apache.http.impl.client.HttpClientBuilder; import org.apache.http.impl.conn.PoolingHttpClientConnectionManager; import org.eclipse.jetty.server.Server; import org.eclipse.jetty.server.handler.ContextHandlerCollection; -import org.eclipse.jetty.servlet.ServletContextHandler; -import org.eclipse.jetty.servlet.ServletHolder; +import org.eclipse.jetty.ee10.servlet.ServletContextHandler; +import org.eclipse.jetty.ee10.servlet.ServletHolder; import org.hl7.fhir.dstu3.model.Bundle; import org.hl7.fhir.dstu3.model.Enumerations.AdministrativeGender; import org.hl7.fhir.dstu3.model.HumanName; diff --git a/hapi-fhir-structures-dstu3/src/test/java/ca/uhn/fhir/rest/server/interceptor/auth/AuthorizationInterceptorDstu3Test.java b/hapi-fhir-structures-dstu3/src/test/java/ca/uhn/fhir/rest/server/interceptor/auth/AuthorizationInterceptorDstu3Test.java index 6d3a2777a34..25fa2bb8d1d 100644 --- a/hapi-fhir-structures-dstu3/src/test/java/ca/uhn/fhir/rest/server/interceptor/auth/AuthorizationInterceptorDstu3Test.java +++ b/hapi-fhir-structures-dstu3/src/test/java/ca/uhn/fhir/rest/server/interceptor/auth/AuthorizationInterceptorDstu3Test.java @@ -19,7 +19,9 @@ import ca.uhn.fhir.rest.api.server.RequestDetails; import ca.uhn.fhir.rest.server.FifoMemoryPagingProvider; import ca.uhn.fhir.rest.server.RestfulServer; import ca.uhn.fhir.rest.server.servlet.ServletRequestDetails; +import ca.uhn.fhir.test.utilities.HttpClientExtension; import ca.uhn.fhir.test.utilities.JettyUtil; +import ca.uhn.fhir.test.utilities.server.RestfulServerExtension; import ca.uhn.fhir.util.TestUtil; import org.apache.commons.io.IOUtils; import org.apache.http.HttpEntity; @@ -31,9 +33,10 @@ import org.apache.http.entity.StringEntity; import org.apache.http.impl.client.CloseableHttpClient; import org.apache.http.impl.client.HttpClientBuilder; import org.apache.http.impl.conn.PoolingHttpClientConnectionManager; +import org.eclipse.jetty.ee10.servlet.ServletContextHandler; import org.eclipse.jetty.server.Server; -import org.eclipse.jetty.servlet.ServletHandler; -import org.eclipse.jetty.servlet.ServletHolder; +import org.eclipse.jetty.ee10.servlet.ServletHandler; +import org.eclipse.jetty.ee10.servlet.ServletHolder; import org.hl7.fhir.dstu3.model.Binary; import org.hl7.fhir.dstu3.model.Bundle; import org.hl7.fhir.dstu3.model.CarePlan; @@ -52,6 +55,7 @@ import org.junit.jupiter.api.AfterAll; import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.RegisterExtension; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -66,27 +70,25 @@ import static org.junit.jupiter.api.Assertions.assertEquals; public class AuthorizationInterceptorDstu3Test { - private static final String ERR403 = "{\"resourceType\":\"OperationOutcome\",\"issue\":[{\"severity\":\"error\",\"code\":\"processing\",\"diagnostics\":\"Access denied by default policy (no applicable rules)\"}]}"; - private static final Logger ourLog = LoggerFactory.getLogger(AuthorizationInterceptorDstu3Test.class); - private static CloseableHttpClient ourClient; - private static String ourConditionalCreateId; - private static FhirContext ourCtx = FhirContext.forDstu3(); - private static boolean ourHitMethod; - private static int ourPort; + private static final FhirContext ourCtx = FhirContext.forDstu3Cached(); private static List ourReturn; private static List ourDeleted; - private static Server ourServer; - private static RestfulServer ourServlet; + + @RegisterExtension + private RestfulServerExtension ourServer = new RestfulServerExtension(ourCtx) + .registerProvider(new PlainProvider()) + .withPagingProvider(new FifoMemoryPagingProvider(100)); + + @RegisterExtension + private HttpClientExtension ourClient = new HttpClientExtension(); @BeforeEach public void before() { ourCtx.setAddProfileTagWhenEncoding(AddProfileTagEnum.NEVER); - ourServlet.getInterceptorService().unregisterAllInterceptors(); - ourServlet.setTenantIdentificationStrategy(null); + ourServer.getInterceptorService().unregisterAllInterceptors(); + ourServer.getRestfulServer().setTenantIdentificationStrategy(null); ourReturn = null; ourDeleted = null; - ourHitMethod = false; - ourConditionalCreateId = "1123"; } private Resource createCarePlan(Integer theId, String theSubjectId) { @@ -176,7 +178,7 @@ public class AuthorizationInterceptorDstu3Test { @Test public void testTransactionWithPatch() throws IOException { - ourServlet.registerInterceptor(new AuthorizationInterceptor(PolicyEnum.DENY) { + ourServer.registerInterceptor(new AuthorizationInterceptor(PolicyEnum.DENY) { @Override public List buildRuleList(RequestDetails theRequestDetails) { return new RuleBuilder() @@ -207,7 +209,7 @@ public class AuthorizationInterceptorDstu3Test { responseBundle.setType(Bundle.BundleType.TRANSACTION); ourReturn = Collections.singletonList(responseBundle); - HttpPost httpPost = new HttpPost("http://localhost:" + ourPort + "/"); + HttpPost httpPost = new HttpPost(ourServer.getBaseUrl()); httpPost.setEntity(createFhirResourceEntity(requestBundle)); CloseableHttpResponse status = ourClient.execute(httpPost); String resp = extractResponseAndClose(status); @@ -220,31 +222,26 @@ public class AuthorizationInterceptorDstu3Test { @History() public List history() { - ourHitMethod = true; return (ourReturn); } @Operation(name = "opName", idempotent = true) public Parameters operation() { - ourHitMethod = true; return (Parameters) new Parameters().setId("1"); } @Operation(name = "process-message", idempotent = true) public Parameters processMessage(@OperationParam(name = "content") Bundle theInput) { - ourHitMethod = true; return (Parameters) new Parameters().setId("1"); } @GraphQL public String processGraphQlRequest(ServletRequestDetails theRequestDetails, @IdParam IIdType theId, @GraphQLQueryUrl String theQuery) { - ourHitMethod = true; return "{'foo':'bar'}"; } @Transaction() public Bundle search(ServletRequestDetails theRequestDetails, IInterceptorBroadcaster theInterceptorBroadcaster, @TransactionParam Bundle theInput) { - ourHitMethod = true; if (ourDeleted != null) { for (IBaseResource next : ourDeleted) { HookParams params = new HookParams() @@ -261,34 +258,8 @@ public class AuthorizationInterceptorDstu3Test { @AfterAll public static void afterClassClearContext() throws Exception { - JettyUtil.closeServer(ourServer); TestUtil.randomizeLocaleAndTimezone(); } - @BeforeAll - public static void beforeClass() throws Exception { - ourServer = new Server(0); - - PlainProvider plainProvider = new PlainProvider(); - - ServletHandler proxyHandler = new ServletHandler(); - ourServlet = new RestfulServer(ourCtx); - ourServlet.setFhirContext(ourCtx); - ourServlet.registerProvider(plainProvider); - ourServlet.setPagingProvider(new FifoMemoryPagingProvider(100)); - ourServlet.setDefaultResponseEncoding(EncodingEnum.JSON); - ServletHolder servletHolder = new ServletHolder(ourServlet); - proxyHandler.addServletWithMapping(servletHolder, "/*"); - ourServer.setHandler(proxyHandler); - JettyUtil.startServer(ourServer); - ourPort = JettyUtil.getPortForStartedServer(ourServer); - - PoolingHttpClientConnectionManager connectionManager = new PoolingHttpClientConnectionManager(5000, TimeUnit.MILLISECONDS); - HttpClientBuilder builder = HttpClientBuilder.create(); - builder.setConnectionManager(connectionManager); - ourClient = builder.build(); - - } - } diff --git a/hapi-fhir-structures-dstu3/src/test/java/org/hl7/fhir/dstu3/hapi/rest/server/PatientResourceProvider.java b/hapi-fhir-structures-dstu3/src/test/java/org/hl7/fhir/dstu3/hapi/rest/server/PatientResourceProvider.java index 08c67a17007..7cefdb1a833 100644 --- a/hapi-fhir-structures-dstu3/src/test/java/org/hl7/fhir/dstu3/hapi/rest/server/PatientResourceProvider.java +++ b/hapi-fhir-structures-dstu3/src/test/java/org/hl7/fhir/dstu3/hapi/rest/server/PatientResourceProvider.java @@ -27,7 +27,7 @@ public class PatientResourceProvider implements IResourceProvider @Search() public IBundleProvider search( - javax.servlet.http.HttpServletRequest theServletRequest, + jakarta.servlet.http.HttpServletRequest theServletRequest, @Description(shortDefinition="The resource identity") @OptionalParam(name="_id") diff --git a/hapi-fhir-structures-dstu3/src/test/java/org/hl7/fhir/dstu3/hapi/rest/server/ServerCapabilityStatementProviderDstu3Test.java b/hapi-fhir-structures-dstu3/src/test/java/org/hl7/fhir/dstu3/hapi/rest/server/ServerCapabilityStatementProviderDstu3Test.java index 6942d049006..a88043940cf 100644 --- a/hapi-fhir-structures-dstu3/src/test/java/org/hl7/fhir/dstu3/hapi/rest/server/ServerCapabilityStatementProviderDstu3Test.java +++ b/hapi-fhir-structures-dstu3/src/test/java/org/hl7/fhir/dstu3/hapi/rest/server/ServerCapabilityStatementProviderDstu3Test.java @@ -32,6 +32,7 @@ import ca.uhn.fhir.rest.param.ReferenceParam; import ca.uhn.fhir.rest.param.StringParam; import ca.uhn.fhir.rest.param.TokenOrListParam; import ca.uhn.fhir.rest.param.TokenParam; +import ca.uhn.fhir.rest.server.HardcodedServerAddressStrategy; import ca.uhn.fhir.rest.server.IResourceProvider; import ca.uhn.fhir.rest.server.ResourceBinding; import ca.uhn.fhir.rest.server.RestfulServer; @@ -40,6 +41,8 @@ import ca.uhn.fhir.rest.server.method.BaseMethodBinding; import ca.uhn.fhir.rest.server.method.IParameter; import ca.uhn.fhir.rest.server.method.SearchMethodBinding; import ca.uhn.fhir.rest.server.method.SearchParameter; +import ca.uhn.fhir.rest.server.provider.BulkDataExportProvider; +import ca.uhn.fhir.rest.server.provider.ProviderConstants; import ca.uhn.fhir.rest.server.servlet.ServletRequestDetails; import ca.uhn.fhir.util.TestUtil; import ca.uhn.fhir.validation.FhirValidator; @@ -71,9 +74,9 @@ import org.junit.jupiter.api.AfterAll; import org.junit.jupiter.api.Disabled; import org.junit.jupiter.api.Test; -import javax.servlet.ServletConfig; -import javax.servlet.ServletException; -import javax.servlet.http.HttpServletRequest; +import jakarta.servlet.ServletConfig; +import jakarta.servlet.ServletException; +import jakarta.servlet.http.HttpServletRequest; import java.util.ArrayList; import java.util.Collection; import java.util.HashSet; @@ -81,6 +84,7 @@ import java.util.List; import java.util.Set; import java.util.stream.Collectors; +import static org.hamcrest.CoreMatchers.equalTo; import static org.hamcrest.MatcherAssert.assertThat; import static org.hamcrest.Matchers.containsInAnyOrder; import static org.hamcrest.Matchers.containsString; @@ -846,6 +850,24 @@ public class ServerCapabilityStatementProviderDstu3Test { assertThat(patientResource.getProfile().getReference(), containsString(PATIENT_SUB)); } + @Test + public void testMethodGetServerConformance_whenServerSupportsExportOperation_willIncludeInstantiatesElement() throws Exception { + // given + RestfulServer rs = new RestfulServer(ourCtx); + rs.setProviders(new BulkDataExportProvider()); + rs.setServerAddressStrategy(new HardcodedServerAddressStrategy("http://localhost/baseR3")); + ServerCapabilityStatementProvider sc = new ServerCapabilityStatementProvider(); + rs.setServerConformanceProvider(sc); + + // when + rs.init(createServletConfig()); + CapabilityStatement conformance = sc.getServerConformance(createHttpServletRequest(), createRequestDetails(rs)); + + // then + String instantiatesFirstRepValue = conformance.getInstantiates().get(0).getValue(); + assertThat(instantiatesFirstRepValue, equalTo(Constants.BULK_DATA_ACCESS_IG_URL)); + } + private List toOperationIdParts(List theOperation) { ArrayList retVal = Lists.newArrayList(); for (CapabilityStatementRestOperationComponent next : theOperation) { @@ -948,7 +970,7 @@ public class ServerCapabilityStatementProviderDstu3Test { public static class MultiTypeEncounterProvider implements IResourceProvider { @Operation(name = "someOp") - public IBundleProvider everything(javax.servlet.http.HttpServletRequest theServletRequest, @IdParam IdType theId, + public IBundleProvider everything(jakarta.servlet.http.HttpServletRequest theServletRequest, @IdParam IdType theId, @OperationParam(name = "someOpParam1") DateType theStart, @OperationParam(name = "someOpParam2") Encounter theEnd) { return null; } @@ -959,7 +981,7 @@ public class ServerCapabilityStatementProviderDstu3Test { } @Validate - public IBundleProvider validate(javax.servlet.http.HttpServletRequest theServletRequest, @IdParam IdType theId, @ResourceParam Encounter thePatient) { + public IBundleProvider validate(jakarta.servlet.http.HttpServletRequest theServletRequest, @IdParam IdType theId, @ResourceParam Encounter thePatient) { return null; } @@ -969,7 +991,7 @@ public class ServerCapabilityStatementProviderDstu3Test { public static class MultiTypePatientProvider implements IResourceProvider { @Operation(name = "someOp") - public IBundleProvider everything(javax.servlet.http.HttpServletRequest theServletRequest, @IdParam IdType theId, + public IBundleProvider everything(jakarta.servlet.http.HttpServletRequest theServletRequest, @IdParam IdType theId, @OperationParam(name = "someOpParam1") DateType theStart, @OperationParam(name = "someOpParam2") Patient theEnd) { return null; } @@ -980,7 +1002,7 @@ public class ServerCapabilityStatementProviderDstu3Test { } @Validate - public IBundleProvider validate(javax.servlet.http.HttpServletRequest theServletRequest, @IdParam IdType theId, @ResourceParam Patient thePatient) { + public IBundleProvider validate(jakarta.servlet.http.HttpServletRequest theServletRequest, @IdParam IdType theId, @ResourceParam Patient thePatient) { return null; } @@ -1015,7 +1037,7 @@ public class ServerCapabilityStatementProviderDstu3Test { public static class PlainProviderWithExtendedOperationOnNoType { @Operation(name = "plain", idempotent = true, returnParameters = { @OperationParam(min = 1, max = 2, name = "out1", type = StringType.class) }) - public IBundleProvider everything(javax.servlet.http.HttpServletRequest theServletRequest, @IdParam IdType theId, @OperationParam(name = "start") DateType theStart, + public IBundleProvider everything(jakarta.servlet.http.HttpServletRequest theServletRequest, @IdParam IdType theId, @OperationParam(name = "start") DateType theStart, @OperationParam(name = "end") DateType theEnd) { return null; } @@ -1026,7 +1048,7 @@ public class ServerCapabilityStatementProviderDstu3Test { public static class ProviderWithExtendedOperationReturningBundle implements IResourceProvider { @Operation(name = "everything", idempotent = true) - public IBundleProvider everything(javax.servlet.http.HttpServletRequest theServletRequest, @IdParam IdType theId, @OperationParam(name = "start") DateType theStart, + public IBundleProvider everything(jakarta.servlet.http.HttpServletRequest theServletRequest, @IdParam IdType theId, @OperationParam(name = "start") DateType theStart, @OperationParam(name = "end") DateType theEnd) { return null; } diff --git a/hapi-fhir-structures-hl7org-dstu2/pom.xml b/hapi-fhir-structures-hl7org-dstu2/pom.xml index 8fc2cd71f11..91c113e8e84 100644 --- a/hapi-fhir-structures-hl7org-dstu2/pom.xml +++ b/hapi-fhir-structures-hl7org-dstu2/pom.xml @@ -5,7 +5,7 @@ ca.uhn.hapi.fhir hapi-deployable-pom - 6.9.7-SNAPSHOT + 6.11.7-SNAPSHOT ../hapi-deployable-pom/pom.xml @@ -32,6 +32,13 @@ ${fhir_core_version} + + jakarta.servlet + jakarta.servlet-api + provided + true + + @@ -59,17 +66,6 @@ true - - - javax.servlet - servlet-api - 2.5 - true - - org.ogce xpp3 @@ -103,36 +99,6 @@ xmlunit-core test - - org.eclipse.jetty - jetty-servlets - test - - - org.eclipse.jetty - jetty-servlet - test - - - org.eclipse.jetty - jetty-server - test - - - org.eclipse.jetty - jetty-util - test - - - org.eclipse.jetty - jetty-webapp - test - - - org.eclipse.jetty - jetty-http - test - ch.qos.logback logback-classic @@ -153,23 +119,18 @@ - com.helger - ph-schematron + com.helger.schematron + ph-schematron-api test - com.helger - ph-commons + com.helger.schematron + ph-schematron-xslt test - javax.activation - javax.activation-api - test - - - javax.xml.bind - jaxb-api + jakarta.activation + jakarta.activation-api test @@ -274,13 +235,6 @@ true - - org.apache.maven.plugins - maven-compiler-plugin - - true - - org.apache.felix maven-bundle-plugin @@ -295,7 +249,7 @@ --> - javax.servlet*;resolution:=optional, + jakarta.servlet*;resolution:=optional, * diff --git a/hapi-fhir-structures-hl7org-dstu2/src/main/java/ca/uhn/fhir/rest/server/provider/dstu2hl7org/Dstu2Hl7OrgBundleFactory.java b/hapi-fhir-structures-hl7org-dstu2/src/main/java/ca/uhn/fhir/rest/server/provider/dstu2hl7org/Dstu2Hl7OrgBundleFactory.java index 172f3bc3162..90b50bea114 100644 --- a/hapi-fhir-structures-hl7org-dstu2/src/main/java/ca/uhn/fhir/rest/server/provider/dstu2hl7org/Dstu2Hl7OrgBundleFactory.java +++ b/hapi-fhir-structures-hl7org-dstu2/src/main/java/ca/uhn/fhir/rest/server/provider/dstu2hl7org/Dstu2Hl7OrgBundleFactory.java @@ -31,6 +31,7 @@ import ca.uhn.fhir.rest.api.Constants; import ca.uhn.fhir.rest.api.IVersionSpecificBundleFactory; import ca.uhn.fhir.rest.server.RestfulServerUtils; import ca.uhn.fhir.util.ResourceReferenceInfo; +import jakarta.annotation.Nonnull; import org.hl7.fhir.dstu2.model.Bundle; import org.hl7.fhir.dstu2.model.Bundle.BundleEntryComponent; import org.hl7.fhir.dstu2.model.Bundle.BundleLinkComponent; @@ -50,7 +51,6 @@ import java.util.HashSet; import java.util.List; import java.util.Set; import java.util.UUID; -import javax.annotation.Nonnull; import static org.apache.commons.lang3.StringUtils.isBlank; import static org.apache.commons.lang3.StringUtils.isNotBlank; diff --git a/hapi-fhir-structures-hl7org-dstu2/src/main/java/org/hl7/fhir/dstu2/hapi/rest/server/ServerConformanceProvider.java b/hapi-fhir-structures-hl7org-dstu2/src/main/java/org/hl7/fhir/dstu2/hapi/rest/server/ServerConformanceProvider.java index 4a6aaae718a..49315d13b8d 100644 --- a/hapi-fhir-structures-hl7org-dstu2/src/main/java/org/hl7/fhir/dstu2/hapi/rest/server/ServerConformanceProvider.java +++ b/hapi-fhir-structures-hl7org-dstu2/src/main/java/org/hl7/fhir/dstu2/hapi/rest/server/ServerConformanceProvider.java @@ -43,6 +43,8 @@ import ca.uhn.fhir.rest.server.method.OperationParameter; import ca.uhn.fhir.rest.server.method.SearchMethodBinding; import ca.uhn.fhir.rest.server.method.SearchParameter; import ca.uhn.fhir.rest.server.util.BaseServerCapabilityStatementProvider; +import jakarta.servlet.ServletContext; +import jakarta.servlet.http.HttpServletRequest; import org.apache.commons.lang3.StringUtils; import org.hl7.fhir.dstu2.model.Conformance; import org.hl7.fhir.dstu2.model.Conformance.*; @@ -58,8 +60,6 @@ import org.hl7.fhir.instance.model.api.IPrimitiveType; import java.util.Map.Entry; import java.util.*; -import javax.servlet.ServletContext; -import javax.servlet.http.HttpServletRequest; import static org.apache.commons.lang3.StringUtils.isNotBlank; diff --git a/hapi-fhir-structures-hl7org-dstu2/src/test/java/ca/uhn/fhir/rest/server/BinaryHl7OrgDstu2Test.java b/hapi-fhir-structures-hl7org-dstu2/src/test/java/ca/uhn/fhir/rest/server/BinaryHl7OrgDstu2Test.java index b2bf88b0eb1..fe89591728f 100644 --- a/hapi-fhir-structures-hl7org-dstu2/src/test/java/ca/uhn/fhir/rest/server/BinaryHl7OrgDstu2Test.java +++ b/hapi-fhir-structures-hl7org-dstu2/src/test/java/ca/uhn/fhir/rest/server/BinaryHl7OrgDstu2Test.java @@ -9,7 +9,10 @@ import ca.uhn.fhir.rest.annotation.Search; import ca.uhn.fhir.rest.api.Constants; import ca.uhn.fhir.rest.api.EncodingEnum; import ca.uhn.fhir.rest.api.MethodOutcome; +import ca.uhn.fhir.test.utilities.HttpClientExtension; import ca.uhn.fhir.test.utilities.JettyUtil; +import ca.uhn.fhir.test.utilities.server.RestfulServerExtension; +import ca.uhn.fhir.util.TestUtil; import org.apache.commons.io.IOUtils; import org.apache.http.HttpResponse; import org.apache.http.client.methods.HttpGet; @@ -20,8 +23,8 @@ import org.apache.http.impl.client.CloseableHttpClient; import org.apache.http.impl.client.HttpClientBuilder; import org.apache.http.impl.conn.PoolingHttpClientConnectionManager; import org.eclipse.jetty.server.Server; -import org.eclipse.jetty.servlet.ServletHandler; -import org.eclipse.jetty.servlet.ServletHolder; +import org.eclipse.jetty.ee10.servlet.ServletHandler; +import org.eclipse.jetty.ee10.servlet.ServletHolder; import org.hl7.fhir.dstu2.model.Binary; import org.hl7.fhir.dstu2.model.IdType; import org.hl7.fhir.instance.model.api.IBaseResource; @@ -29,6 +32,7 @@ import org.junit.jupiter.api.AfterAll; import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.RegisterExtension; import java.util.Collections; import java.util.List; @@ -45,12 +49,18 @@ import static org.junit.jupiter.api.Assertions.assertEquals; public class BinaryHl7OrgDstu2Test { private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(BinaryHl7OrgDstu2Test.class); - private static CloseableHttpClient ourClient; - private static FhirContext ourCtx = FhirContext.forDstu2Hl7Org(); + private static final FhirContext ourCtx = FhirContext.forDstu2Hl7OrgCached(); private static Binary ourLast; - private static int ourPort; - private static Server ourServer; + @RegisterExtension + public static RestfulServerExtension ourServer = new RestfulServerExtension(ourCtx) + .registerProvider(new ResourceProvider()) + .withPagingProvider(new FifoMemoryPagingProvider(100)) + .setDefaultResponseEncoding(EncodingEnum.XML) + .setDefaultPrettyPrint(false); + + @RegisterExtension + public static HttpClientExtension ourClient = new HttpClientExtension(); @BeforeEach public void before() { @@ -59,7 +69,7 @@ public class BinaryHl7OrgDstu2Test { @Test public void testReadWithExplicitTypeXml() throws Exception { - HttpGet httpGet = new HttpGet("http://localhost:" + ourPort + "/Binary/foo?_format=xml"); + HttpGet httpGet = new HttpGet(ourServer.getBaseUrl() + "/Binary/foo?_format=xml"); HttpResponse status = ourClient.execute(httpGet); String responseContent = IOUtils.toString(status.getEntity().getContent(), "UTF-8"); IOUtils.closeQuietly(status.getEntity().getContent()); @@ -76,7 +86,7 @@ public class BinaryHl7OrgDstu2Test { @Test public void testReadWithExplicitTypeJson() throws Exception { - HttpGet httpGet = new HttpGet("http://localhost:" + ourPort + "/Binary/foo?_format=json"); + HttpGet httpGet = new HttpGet(ourServer.getBaseUrl() + "/Binary/foo?_format=json"); HttpResponse status = ourClient.execute(httpGet); String responseContent = IOUtils.toString(status.getEntity().getContent(), "UTF-8"); IOUtils.closeQuietly(status.getEntity().getContent()); @@ -94,7 +104,7 @@ public class BinaryHl7OrgDstu2Test { @Test public void testCreate() throws Exception { - HttpPost http = new HttpPost("http://localhost:" + ourPort + "/Binary"); + HttpPost http = new HttpPost(ourServer.getBaseUrl() + "/Binary"); http.setEntity(new ByteArrayEntity(new byte[]{1, 2, 3, 4}, ContentType.create("foo/bar", "UTF-8"))); HttpResponse status = ourClient.execute(http); @@ -107,7 +117,7 @@ public class BinaryHl7OrgDstu2Test { @Test public void testRead() throws Exception { - HttpGet httpGet = new HttpGet("http://localhost:" + ourPort + "/Binary/foo"); + HttpGet httpGet = new HttpGet(ourServer.getBaseUrl() + "/Binary/foo"); HttpResponse status = ourClient.execute(httpGet); byte[] responseContent = IOUtils.toByteArray(status.getEntity().getContent()); IOUtils.closeQuietly(status.getEntity().getContent()); @@ -119,7 +129,7 @@ public class BinaryHl7OrgDstu2Test { @Test public void testSearchJson() throws Exception { - HttpGet httpGet = new HttpGet("http://localhost:" + ourPort + "/Binary?_pretty=true&_format=json"); + HttpGet httpGet = new HttpGet(ourServer.getBaseUrl() + "/Binary?_pretty=true&_format=json"); HttpResponse status = ourClient.execute(httpGet); String responseContent = IOUtils.toString(status.getEntity().getContent()); IOUtils.closeQuietly(status.getEntity().getContent()); @@ -137,7 +147,7 @@ public class BinaryHl7OrgDstu2Test { @Test public void testSearchXml() throws Exception { - HttpGet httpGet = new HttpGet("http://localhost:" + ourPort + "/Binary?_pretty=true"); + HttpGet httpGet = new HttpGet(ourServer.getBaseUrl() + "/Binary?_pretty=true"); HttpResponse status = ourClient.execute(httpGet); String responseContent = IOUtils.toString(status.getEntity().getContent()); IOUtils.closeQuietly(status.getEntity().getContent()); @@ -191,30 +201,7 @@ public class BinaryHl7OrgDstu2Test { @AfterAll public static void afterClass() throws Exception { - JettyUtil.closeServer(ourServer); - } - - @BeforeAll - public static void beforeClass() throws Exception { - ourServer = new Server(0); - - ResourceProvider patientProvider = new ResourceProvider(); - - ServletHandler proxyHandler = new ServletHandler(); - RestfulServer servlet = new RestfulServer(ourCtx); - servlet.setResourceProviders(patientProvider); - servlet.setDefaultResponseEncoding(EncodingEnum.XML); - ServletHolder servletHolder = new ServletHolder(servlet); - proxyHandler.addServletWithMapping(servletHolder, "/*"); - ourServer.setHandler(proxyHandler); - JettyUtil.startServer(ourServer); - ourPort = JettyUtil.getPortForStartedServer(ourServer); - - PoolingHttpClientConnectionManager connectionManager = new PoolingHttpClientConnectionManager(5000, TimeUnit.MILLISECONDS); - HttpClientBuilder builder = HttpClientBuilder.create(); - builder.setConnectionManager(connectionManager); - ourClient = builder.build(); - + TestUtil.randomizeLocaleAndTimezone(); } } diff --git a/hapi-fhir-structures-hl7org-dstu2/src/test/java/ca/uhn/fhir/rest/server/BundleTypeInResponseHl7OrgTest.java b/hapi-fhir-structures-hl7org-dstu2/src/test/java/ca/uhn/fhir/rest/server/BundleTypeInResponseHl7OrgTest.java index 734e011c748..12341610839 100644 --- a/hapi-fhir-structures-hl7org-dstu2/src/test/java/ca/uhn/fhir/rest/server/BundleTypeInResponseHl7OrgTest.java +++ b/hapi-fhir-structures-hl7org-dstu2/src/test/java/ca/uhn/fhir/rest/server/BundleTypeInResponseHl7OrgTest.java @@ -4,7 +4,10 @@ import ca.uhn.fhir.context.FhirContext; import ca.uhn.fhir.narrative.DefaultThymeleafNarrativeGenerator; import ca.uhn.fhir.rest.annotation.Search; import ca.uhn.fhir.rest.api.EncodingEnum; +import ca.uhn.fhir.test.utilities.HttpClientExtension; import ca.uhn.fhir.test.utilities.JettyUtil; +import ca.uhn.fhir.test.utilities.server.RestfulServerExtension; +import ca.uhn.fhir.util.TestUtil; import org.apache.commons.io.IOUtils; import org.apache.http.HttpResponse; import org.apache.http.client.methods.HttpGet; @@ -12,13 +15,14 @@ import org.apache.http.impl.client.CloseableHttpClient; import org.apache.http.impl.client.HttpClientBuilder; import org.apache.http.impl.conn.PoolingHttpClientConnectionManager; import org.eclipse.jetty.server.Server; -import org.eclipse.jetty.servlet.ServletHandler; -import org.eclipse.jetty.servlet.ServletHolder; +import org.eclipse.jetty.ee10.servlet.ServletHandler; +import org.eclipse.jetty.ee10.servlet.ServletHolder; import org.hl7.fhir.dstu2.model.Bundle.BundleType; import org.hl7.fhir.dstu2.model.Patient; import org.junit.jupiter.api.AfterAll; import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.RegisterExtension; import java.util.ArrayList; import java.util.List; @@ -29,14 +33,21 @@ import static org.junit.jupiter.api.Assertions.assertEquals; public class BundleTypeInResponseHl7OrgTest { private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(BundleTypeInResponseHl7OrgTest.class); - private static CloseableHttpClient ourClient; - private static FhirContext ourCtx = FhirContext.forDstu2Hl7Org(); - private static int ourPort; - private static Server ourServer; + private static final FhirContext ourCtx = FhirContext.forDstu2Hl7OrgCached(); + + @RegisterExtension + public static RestfulServerExtension ourServer = new RestfulServerExtension(ourCtx) + .registerProvider(new DummyPatientResourceProvider()) + .withPagingProvider(new FifoMemoryPagingProvider(100)) + .setDefaultResponseEncoding(EncodingEnum.XML) + .setDefaultPrettyPrint(false); + + @RegisterExtension + public static HttpClientExtension ourClient = new HttpClientExtension(); @Test public void testSearch() throws Exception { - HttpGet httpGet = new HttpGet("http://localhost:" + ourPort + "/Patient"); + HttpGet httpGet = new HttpGet(ourServer.getBaseUrl() + "/Patient"); HttpResponse status = ourClient.execute(httpGet); String responseContent = IOUtils.toString(status.getEntity().getContent()); IOUtils.closeQuietly(status.getEntity().getContent()); @@ -71,32 +82,7 @@ public class BundleTypeInResponseHl7OrgTest { @AfterAll public static void afterClass() throws Exception { - JettyUtil.closeServer(ourServer); - } - - @BeforeAll - public static void beforeClass() throws Exception { - ourServer = new Server(0); - - DummyPatientResourceProvider patientProvider = new DummyPatientResourceProvider(); - - ServletHandler proxyHandler = new ServletHandler(); - RestfulServer servlet = new RestfulServer(ourCtx); - servlet.getFhirContext().setNarrativeGenerator(new DefaultThymeleafNarrativeGenerator()); - servlet.setResourceProviders(patientProvider); - servlet.setDefaultResponseEncoding(EncodingEnum.XML); - - ServletHolder servletHolder = new ServletHolder(servlet); - proxyHandler.addServletWithMapping(servletHolder, "/*"); - ourServer.setHandler(proxyHandler); - JettyUtil.startServer(ourServer); - ourPort = JettyUtil.getPortForStartedServer(ourServer); - - PoolingHttpClientConnectionManager connectionManager = new PoolingHttpClientConnectionManager(5000, TimeUnit.MILLISECONDS); - HttpClientBuilder builder = HttpClientBuilder.create(); - builder.setConnectionManager(connectionManager); - ourClient = builder.build(); - + TestUtil.randomizeLocaleAndTimezone(); } } diff --git a/hapi-fhir-structures-hl7org-dstu2/src/test/java/ca/uhn/fhir/rest/server/CreateConditionalHl7OrgTest.java b/hapi-fhir-structures-hl7org-dstu2/src/test/java/ca/uhn/fhir/rest/server/CreateConditionalHl7OrgTest.java index d14a0e629c9..38103382829 100644 --- a/hapi-fhir-structures-hl7org-dstu2/src/test/java/ca/uhn/fhir/rest/server/CreateConditionalHl7OrgTest.java +++ b/hapi-fhir-structures-hl7org-dstu2/src/test/java/ca/uhn/fhir/rest/server/CreateConditionalHl7OrgTest.java @@ -10,8 +10,12 @@ import ca.uhn.fhir.rest.annotation.OptionalParam; import ca.uhn.fhir.rest.annotation.ResourceParam; import ca.uhn.fhir.rest.annotation.Search; import ca.uhn.fhir.rest.api.Constants; +import ca.uhn.fhir.rest.api.EncodingEnum; import ca.uhn.fhir.rest.api.MethodOutcome; +import ca.uhn.fhir.test.utilities.HttpClientExtension; import ca.uhn.fhir.test.utilities.JettyUtil; +import ca.uhn.fhir.test.utilities.server.RestfulServerExtension; +import ca.uhn.fhir.util.TestUtil; import org.apache.commons.io.IOUtils; import org.apache.http.HttpResponse; import org.apache.http.client.methods.HttpGet; @@ -22,14 +26,15 @@ import org.apache.http.impl.client.CloseableHttpClient; import org.apache.http.impl.client.HttpClientBuilder; import org.apache.http.impl.conn.PoolingHttpClientConnectionManager; import org.eclipse.jetty.server.Server; -import org.eclipse.jetty.servlet.ServletHandler; -import org.eclipse.jetty.servlet.ServletHolder; +import org.eclipse.jetty.ee10.servlet.ServletHandler; +import org.eclipse.jetty.ee10.servlet.ServletHolder; import org.hl7.fhir.dstu2.model.IdType; import org.hl7.fhir.dstu2.model.Patient; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.AfterAll; import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.extension.RegisterExtension; import java.util.ArrayList; import java.util.List; @@ -43,18 +48,23 @@ import static org.junit.jupiter.api.Assertions.assertTrue; * Created by dsotnikov on 2/25/2014. */ public class CreateConditionalHl7OrgTest { - private static CloseableHttpClient ourClient; - private static String ourLastConditionalUrl; private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(CreateConditionalHl7OrgTest.class); - private static int ourPort; - private static Server ourServer; private static IdType ourLastId; private static IdType ourLastIdParam; private static boolean ourLastRequestWasSearch; - private static FhirContext ourCtx = FhirContext.forDstu2Hl7Org(); - - + private static final FhirContext ourCtx = FhirContext.forDstu2Hl7OrgCached(); + + @RegisterExtension + public static RestfulServerExtension ourServer = new RestfulServerExtension(ourCtx) + .registerProvider(new PatientProvider()) + .withPagingProvider(new FifoMemoryPagingProvider(100)) + .setDefaultResponseEncoding(EncodingEnum.JSON) + .setDefaultPrettyPrint(false); + + @RegisterExtension + public static HttpClientExtension ourClient = new HttpClientExtension(); + @BeforeEach public void before() { @@ -70,7 +80,7 @@ public class CreateConditionalHl7OrgTest { Patient patient = new Patient(); patient.addIdentifier().setValue("002"); - HttpPost httpPost = new HttpPost("http://localhost:" + ourPort + "/Patient"); + HttpPost httpPost = new HttpPost(ourServer.getBaseUrl() + "/Patient"); httpPost.addHeader(Constants.HEADER_IF_NONE_EXIST, "Patient?identifier=system%7C001"); httpPost.setEntity(new StringEntity(ourCtx.newXmlParser().encodeResourceToString(patient), ContentType.create(Constants.CT_FHIR_XML, "UTF-8"))); @@ -82,8 +92,8 @@ public class CreateConditionalHl7OrgTest { ourLog.info("Response was:\n{}", responseContent); assertEquals(201, status.getStatusLine().getStatusCode()); - assertEquals("http://localhost:" + ourPort + "/Patient/001/_history/002", status.getFirstHeader("location").getValue()); - assertEquals("http://localhost:" + ourPort + "/Patient/001/_history/002", status.getFirstHeader("content-location").getValue()); + assertEquals(ourServer.getBaseUrl() + "/Patient/001/_history/002", status.getFirstHeader("location").getValue()); + assertEquals(ourServer.getBaseUrl() + "/Patient/001/_history/002", status.getFirstHeader("content-location").getValue()); assertNull(ourLastId.getValue()); assertNull(ourLastIdParam); @@ -97,7 +107,7 @@ public class CreateConditionalHl7OrgTest { Patient patient = new Patient(); patient.addIdentifier().setValue("002"); - HttpPost httpPost = new HttpPost("http://localhost:" + ourPort + "/Patient?_format=true&_pretty=true"); + HttpPost httpPost = new HttpPost(ourServer.getBaseUrl() + "/Patient?_format=true&_pretty=true"); httpPost.setEntity(new StringEntity(ourCtx.newXmlParser().encodeResourceToString(patient), ContentType.create(Constants.CT_FHIR_XML, "UTF-8"))); HttpResponse status = ourClient.execute(httpPost); @@ -108,8 +118,8 @@ public class CreateConditionalHl7OrgTest { ourLog.info("Response was:\n{}", responseContent); assertEquals(201, status.getStatusLine().getStatusCode()); - assertEquals("http://localhost:" + ourPort + "/Patient/001/_history/002", status.getFirstHeader("location").getValue()); - assertEquals("http://localhost:" + ourPort + "/Patient/001/_history/002", status.getFirstHeader("content-location").getValue()); + assertEquals(ourServer.getBaseUrl() + "/Patient/001/_history/002", status.getFirstHeader("location").getValue()); + assertEquals(ourServer.getBaseUrl() + "/Patient/001/_history/002", status.getFirstHeader("content-location").getValue()); assertNull(ourLastId.getValue()); assertNull(ourLastIdParam); @@ -123,7 +133,7 @@ public class CreateConditionalHl7OrgTest { Patient patient = new Patient(); patient.addIdentifier().setValue("002"); - HttpGet httpGet = new HttpGet("http://localhost:" + ourPort + "/Patient?_pretty=true"); + HttpGet httpGet = new HttpGet(ourServer.getBaseUrl() + "/Patient?_pretty=true"); HttpResponse status = ourClient.execute(httpGet); @@ -142,32 +152,10 @@ public class CreateConditionalHl7OrgTest { @AfterAll public static void afterClass() throws Exception { - JettyUtil.closeServer(ourServer); + TestUtil.randomizeLocaleAndTimezone(); } - @BeforeAll - public static void beforeClass() throws Exception { - ourServer = new Server(0); - - PatientProvider patientProvider = new PatientProvider(); - - ServletHandler proxyHandler = new ServletHandler(); - RestfulServer servlet = new RestfulServer(ourCtx); - servlet.setResourceProviders(patientProvider); - ServletHolder servletHolder = new ServletHolder(servlet); - proxyHandler.addServletWithMapping(servletHolder, "/*"); - ourServer.setHandler(proxyHandler); - JettyUtil.startServer(ourServer); - ourPort = JettyUtil.getPortForStartedServer(ourServer); - - PoolingHttpClientConnectionManager connectionManager = new PoolingHttpClientConnectionManager(5000, TimeUnit.MILLISECONDS); - HttpClientBuilder builder = HttpClientBuilder.create(); - builder.setConnectionManager(connectionManager); - ourClient = builder.build(); - - } - public static class PatientProvider implements IResourceProvider { @Override diff --git a/hapi-fhir-structures-hl7org-dstu2/src/test/java/ca/uhn/fhir/rest/server/DeleteConditionalHl7OrgTest.java b/hapi-fhir-structures-hl7org-dstu2/src/test/java/ca/uhn/fhir/rest/server/DeleteConditionalHl7OrgTest.java index 1722aa1034b..6ca5d14e60a 100644 --- a/hapi-fhir-structures-hl7org-dstu2/src/test/java/ca/uhn/fhir/rest/server/DeleteConditionalHl7OrgTest.java +++ b/hapi-fhir-structures-hl7org-dstu2/src/test/java/ca/uhn/fhir/rest/server/DeleteConditionalHl7OrgTest.java @@ -4,22 +4,27 @@ import ca.uhn.fhir.context.FhirContext; import ca.uhn.fhir.rest.annotation.ConditionalUrlParam; import ca.uhn.fhir.rest.annotation.Delete; import ca.uhn.fhir.rest.annotation.IdParam; +import ca.uhn.fhir.rest.api.EncodingEnum; import ca.uhn.fhir.rest.api.MethodOutcome; +import ca.uhn.fhir.test.utilities.HttpClientExtension; import ca.uhn.fhir.test.utilities.JettyUtil; +import ca.uhn.fhir.test.utilities.server.RestfulServerExtension; +import ca.uhn.fhir.util.TestUtil; import org.apache.http.HttpResponse; import org.apache.http.client.methods.HttpDelete; import org.apache.http.impl.client.CloseableHttpClient; import org.apache.http.impl.client.HttpClientBuilder; import org.apache.http.impl.conn.PoolingHttpClientConnectionManager; import org.eclipse.jetty.server.Server; -import org.eclipse.jetty.servlet.ServletHandler; -import org.eclipse.jetty.servlet.ServletHolder; +import org.eclipse.jetty.ee10.servlet.ServletHandler; +import org.eclipse.jetty.ee10.servlet.ServletHolder; import org.hl7.fhir.dstu2.model.IdType; import org.hl7.fhir.dstu2.model.Patient; import org.junit.jupiter.api.AfterAll; import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.RegisterExtension; import java.util.concurrent.TimeUnit; @@ -30,16 +35,23 @@ import static org.junit.jupiter.api.Assertions.assertNull; * Created by dsotnikov on 2/25/2014. */ public class DeleteConditionalHl7OrgTest { - private static CloseableHttpClient ourClient; private static String ourLastConditionalUrl; - private static int ourPort; - private static FhirContext ourCtx = FhirContext.forDstu2Hl7Org(); - private static Server ourServer; + private static final FhirContext ourCtx = FhirContext.forDstu2Hl7OrgCached(); private static IdType ourLastIdParam; - - - - @BeforeEach + + + @RegisterExtension + public static RestfulServerExtension ourServer = new RestfulServerExtension(ourCtx) + .registerProvider(new PatientProvider()) + .withPagingProvider(new FifoMemoryPagingProvider(100)) + .setDefaultResponseEncoding(EncodingEnum.JSON) + .setDefaultPrettyPrint(false); + + @RegisterExtension + public static HttpClientExtension ourClient = new HttpClientExtension(); + + + @BeforeEach public void before() { ourLastConditionalUrl = null; ourLastIdParam = null; @@ -50,7 +62,7 @@ public class DeleteConditionalHl7OrgTest { Patient patient = new Patient(); patient.addIdentifier().setValue("002"); - HttpDelete httpPost = new HttpDelete("http://localhost:" + ourPort + "/Patient?identifier=system%7C001"); + HttpDelete httpPost = new HttpDelete(ourServer.getBaseUrl() + "/Patient?identifier=system%7C001"); HttpResponse status = ourClient.execute(httpPost); @@ -66,7 +78,7 @@ public class DeleteConditionalHl7OrgTest { Patient patient = new Patient(); patient.addIdentifier().setValue("002"); - HttpDelete httpPost = new HttpDelete("http://localhost:" + ourPort + "/Patient/2"); + HttpDelete httpPost = new HttpDelete(ourServer.getBaseUrl() + "/Patient/2"); HttpResponse status = ourClient.execute(httpPost); @@ -78,32 +90,10 @@ public class DeleteConditionalHl7OrgTest { @AfterAll public static void afterClass() throws Exception { - JettyUtil.closeServer(ourServer); + TestUtil.randomizeLocaleAndTimezone(); } - @BeforeAll - public static void beforeClass() throws Exception { - ourServer = new Server(0); - - PatientProvider patientProvider = new PatientProvider(); - - ServletHandler proxyHandler = new ServletHandler(); - RestfulServer servlet = new RestfulServer(ourCtx); - servlet.setResourceProviders(patientProvider); - ServletHolder servletHolder = new ServletHolder(servlet); - proxyHandler.addServletWithMapping(servletHolder, "/*"); - ourServer.setHandler(proxyHandler); - JettyUtil.startServer(ourServer); - ourPort = JettyUtil.getPortForStartedServer(ourServer); - - PoolingHttpClientConnectionManager connectionManager = new PoolingHttpClientConnectionManager(5000, TimeUnit.MILLISECONDS); - HttpClientBuilder builder = HttpClientBuilder.create(); - builder.setConnectionManager(connectionManager); - ourClient = builder.build(); - - } - public static class PatientProvider implements IResourceProvider { @Override diff --git a/hapi-fhir-structures-hl7org-dstu2/src/test/java/ca/uhn/fhir/rest/server/IncludeAndRevincludeParameterHl7OrgTest.java b/hapi-fhir-structures-hl7org-dstu2/src/test/java/ca/uhn/fhir/rest/server/IncludeAndRevincludeParameterHl7OrgTest.java index 6528c42fb52..2cf3d0a2737 100644 --- a/hapi-fhir-structures-hl7org-dstu2/src/test/java/ca/uhn/fhir/rest/server/IncludeAndRevincludeParameterHl7OrgTest.java +++ b/hapi-fhir-structures-hl7org-dstu2/src/test/java/ca/uhn/fhir/rest/server/IncludeAndRevincludeParameterHl7OrgTest.java @@ -5,7 +5,11 @@ import ca.uhn.fhir.context.api.BundleInclusionRule; import ca.uhn.fhir.model.api.Include; import ca.uhn.fhir.rest.annotation.IncludeParam; import ca.uhn.fhir.rest.annotation.Search; +import ca.uhn.fhir.rest.api.EncodingEnum; +import ca.uhn.fhir.test.utilities.HttpClientExtension; import ca.uhn.fhir.test.utilities.JettyUtil; +import ca.uhn.fhir.test.utilities.server.RestfulServerExtension; +import ca.uhn.fhir.util.TestUtil; import org.apache.commons.io.IOUtils; import org.apache.http.HttpResponse; import org.apache.http.client.methods.HttpGet; @@ -13,13 +17,14 @@ import org.apache.http.impl.client.CloseableHttpClient; import org.apache.http.impl.client.HttpClientBuilder; import org.apache.http.impl.conn.PoolingHttpClientConnectionManager; import org.eclipse.jetty.server.Server; -import org.eclipse.jetty.servlet.ServletHandler; -import org.eclipse.jetty.servlet.ServletHolder; +import org.eclipse.jetty.ee10.servlet.ServletHandler; +import org.eclipse.jetty.ee10.servlet.ServletHolder; import org.hl7.fhir.dstu2.model.Patient; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.AfterAll; import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.extension.RegisterExtension; import java.util.ArrayList; import java.util.List; @@ -33,14 +38,22 @@ import static org.junit.jupiter.api.Assertions.assertEquals; public class IncludeAndRevincludeParameterHl7OrgTest { - private static CloseableHttpClient ourClient; - private static int ourPort; - private static Server ourServer; - private static FhirContext ourCtx; + private static final FhirContext ourCtx = FhirContext.forDstu2Hl7OrgCached(); private static Set ourIncludes; private static Set ourReverseIncludes; - @BeforeEach + @RegisterExtension + public static RestfulServerExtension ourServer = new RestfulServerExtension(ourCtx) + .registerProvider(new DummyPatientResourceProvider()) + .withPagingProvider(new FifoMemoryPagingProvider(100)) + .setDefaultResponseEncoding(EncodingEnum.JSON) + .withServer(s->s.setBundleInclusionRule(BundleInclusionRule.BASED_ON_RESOURCE_PRESENCE)) + .setDefaultPrettyPrint(false); + + @RegisterExtension + public static HttpClientExtension ourClient = new HttpClientExtension(); + + @BeforeEach public void before() { ourIncludes = null; ourReverseIncludes = null; @@ -48,7 +61,7 @@ public class IncludeAndRevincludeParameterHl7OrgTest { @Test public void testNoIncludes() throws Exception { - HttpGet httpGet = new HttpGet("http://localhost:" + ourPort + "/Patient?_query=normalInclude"); + HttpGet httpGet = new HttpGet(ourServer.getBaseUrl() + "/Patient?_query=normalInclude"); HttpResponse status = ourClient.execute(httpGet); IOUtils.closeQuietly(status.getEntity().getContent()); @@ -60,7 +73,7 @@ public class IncludeAndRevincludeParameterHl7OrgTest { @Test public void testWithBoth() throws Exception { - HttpGet httpGet = new HttpGet("http://localhost:" + ourPort + "/Patient?_query=normalInclude&_include=A.a&_include=B.b&_revinclude=C.c&_revinclude=D.d"); + HttpGet httpGet = new HttpGet(ourServer.getBaseUrl() + "/Patient?_query=normalInclude&_include=A.a&_include=B.b&_revinclude=C.c&_revinclude=D.d"); HttpResponse status = ourClient.execute(httpGet); IOUtils.closeQuietly(status.getEntity().getContent()); @@ -74,30 +87,7 @@ public class IncludeAndRevincludeParameterHl7OrgTest { @AfterAll public static void afterClass() throws Exception { - JettyUtil.closeServer(ourServer); - } - - @BeforeAll - public static void beforeClass() throws Exception { - - ourCtx = FhirContext.forDstu2Hl7Org(); - ourServer = new Server(0); - - ServletHandler proxyHandler = new ServletHandler(); - RestfulServer servlet = new RestfulServer(ourCtx); - servlet.setResourceProviders(new DummyPatientResourceProvider()); - servlet.setBundleInclusionRule(BundleInclusionRule.BASED_ON_RESOURCE_PRESENCE); - ServletHolder servletHolder = new ServletHolder(servlet); - proxyHandler.addServletWithMapping(servletHolder, "/*"); - ourServer.setHandler(proxyHandler); - JettyUtil.startServer(ourServer); - ourPort = JettyUtil.getPortForStartedServer(ourServer); - - PoolingHttpClientConnectionManager connectionManager = new PoolingHttpClientConnectionManager(5000, TimeUnit.MILLISECONDS); - HttpClientBuilder builder = HttpClientBuilder.create(); - builder.setConnectionManager(connectionManager); - ourClient = builder.build(); - + TestUtil.randomizeLocaleAndTimezone(); } public static class DummyPatientResourceProvider implements IResourceProvider { diff --git a/hapi-fhir-structures-hl7org-dstu2/src/test/java/ca/uhn/fhir/rest/server/IncludeHl7OrgDstu2Test.java b/hapi-fhir-structures-hl7org-dstu2/src/test/java/ca/uhn/fhir/rest/server/IncludeHl7OrgDstu2Test.java index 39899c6b19c..362cf19d653 100644 --- a/hapi-fhir-structures-hl7org-dstu2/src/test/java/ca/uhn/fhir/rest/server/IncludeHl7OrgDstu2Test.java +++ b/hapi-fhir-structures-hl7org-dstu2/src/test/java/ca/uhn/fhir/rest/server/IncludeHl7OrgDstu2Test.java @@ -11,17 +11,13 @@ import ca.uhn.fhir.rest.annotation.IncludeParam; import ca.uhn.fhir.rest.annotation.RequiredParam; import ca.uhn.fhir.rest.annotation.Search; import ca.uhn.fhir.rest.api.EncodingEnum; -import ca.uhn.fhir.test.utilities.JettyUtil; +import ca.uhn.fhir.test.utilities.HttpClientExtension; +import ca.uhn.fhir.test.utilities.server.RestfulServerExtension; import ca.uhn.fhir.util.ElementUtil; +import ca.uhn.fhir.util.TestUtil; import org.apache.commons.io.IOUtils; import org.apache.http.HttpResponse; import org.apache.http.client.methods.HttpGet; -import org.apache.http.impl.client.CloseableHttpClient; -import org.apache.http.impl.client.HttpClientBuilder; -import org.apache.http.impl.conn.PoolingHttpClientConnectionManager; -import org.eclipse.jetty.server.Server; -import org.eclipse.jetty.servlet.ServletHandler; -import org.eclipse.jetty.servlet.ServletHolder; import org.hl7.fhir.dstu2.model.Bundle; import org.hl7.fhir.dstu2.model.Bundle.SearchEntryMode; import org.hl7.fhir.dstu2.model.DiagnosticReport; @@ -31,9 +27,9 @@ import org.hl7.fhir.dstu2.model.Organization; import org.hl7.fhir.dstu2.model.Patient; import org.hl7.fhir.dstu2.model.Practitioner; import org.hl7.fhir.dstu2.model.Reference; -import org.junit.jupiter.api.Test; import org.junit.jupiter.api.AfterAll; -import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.RegisterExtension; import java.util.ArrayList; import java.util.Arrays; @@ -41,7 +37,6 @@ import java.util.Collections; import java.util.HashSet; import java.util.List; import java.util.Set; -import java.util.concurrent.TimeUnit; import static org.hamcrest.MatcherAssert.assertThat; import static org.hamcrest.Matchers.containsInAnyOrder; @@ -50,14 +45,22 @@ import static org.junit.jupiter.api.Assertions.assertEquals; public class IncludeHl7OrgDstu2Test { private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(IncludeHl7OrgDstu2Test.class); - private static CloseableHttpClient ourClient; - private static int ourPort; - private static Server ourServer; - private static FhirContext ourCtx; + private static final FhirContext ourCtx = FhirContext.forDstu2Hl7OrgCached(); + + @RegisterExtension + public static RestfulServerExtension ourServer = new RestfulServerExtension(ourCtx) + .registerProvider(new DummyPatientResourceProvider()) + .withPagingProvider(new FifoMemoryPagingProvider(100)) + .setDefaultResponseEncoding(EncodingEnum.XML) + .setDefaultPrettyPrint(false) + .withServer(s->s.setBundleInclusionRule(BundleInclusionRule.BASED_ON_RESOURCE_PRESENCE)); + + @RegisterExtension + public static HttpClientExtension ourClient = new HttpClientExtension(); @Test public void testNoIncludes() throws Exception { - HttpGet httpGet = new HttpGet("http://localhost:" + ourPort + "/Patient?name=Hello"); + HttpGet httpGet = new HttpGet(ourServer.getBaseUrl() + "/Patient?name=Hello"); HttpResponse status = ourClient.execute(httpGet); String responseContent = IOUtils.toString(status.getEntity().getContent()); IOUtils.closeQuietly(status.getEntity().getContent()); @@ -73,7 +76,7 @@ public class IncludeHl7OrgDstu2Test { @Test public void testOneIncludeXml() throws Exception { - HttpGet httpGet = new HttpGet("http://localhost:" + ourPort + "/Patient?name=Hello&_include=foo"); + HttpGet httpGet = new HttpGet(ourServer.getBaseUrl() + "/Patient?name=Hello&_include=foo"); HttpResponse status = ourClient.execute(httpGet); String responseContent = IOUtils.toString(status.getEntity().getContent()); IOUtils.closeQuietly(status.getEntity().getContent()); @@ -93,7 +96,7 @@ public class IncludeHl7OrgDstu2Test { @Test public void testOneIncludeJson() throws Exception { - HttpGet httpGet = new HttpGet("http://localhost:" + ourPort + "/Patient?name=Hello&_include=foo&_format=json"); + HttpGet httpGet = new HttpGet(ourServer.getBaseUrl() + "/Patient?name=Hello&_include=foo&_format=json"); HttpResponse status = ourClient.execute(httpGet); String responseContent = IOUtils.toString(status.getEntity().getContent()); IOUtils.closeQuietly(status.getEntity().getContent()); @@ -113,7 +116,7 @@ public class IncludeHl7OrgDstu2Test { @Test public void testIIncludedResourcesNonContained() throws Exception { - HttpGet httpGet = new HttpGet("http://localhost:" + ourPort + "/Patient?_query=normalInclude&_pretty=true"); + HttpGet httpGet = new HttpGet(ourServer.getBaseUrl() + "/Patient?_query=normalInclude&_pretty=true"); HttpResponse status = ourClient.execute(httpGet); String responseContent = IOUtils.toString(status.getEntity().getContent()); IOUtils.closeQuietly(status.getEntity().getContent()); @@ -139,7 +142,7 @@ public class IncludeHl7OrgDstu2Test { @Test public void testIIncludedResourcesNonContainedInExtension() throws Exception { - HttpGet httpGet = new HttpGet("http://localhost:" + ourPort + "/Patient?_query=extInclude&_pretty=true"); + HttpGet httpGet = new HttpGet(ourServer.getBaseUrl() + "/Patient?_query=extInclude&_pretty=true"); HttpResponse status = ourClient.execute(httpGet); String responseContent = IOUtils.toString(status.getEntity().getContent()); IOUtils.closeQuietly(status.getEntity().getContent()); @@ -165,7 +168,7 @@ public class IncludeHl7OrgDstu2Test { @Test public void testIIncludedResourcesNonContainedInExtensionJson() throws Exception { - HttpGet httpGet = new HttpGet("http://localhost:" + ourPort + "/Patient?_query=extInclude&_pretty=true&_format=json"); + HttpGet httpGet = new HttpGet(ourServer.getBaseUrl() + "/Patient?_query=extInclude&_pretty=true&_format=json"); HttpResponse status = ourClient.execute(httpGet); String responseContent = IOUtils.toString(status.getEntity().getContent()); IOUtils.closeQuietly(status.getEntity().getContent()); @@ -191,7 +194,7 @@ public class IncludeHl7OrgDstu2Test { @Test public void testIIncludedResourcesNonContainedInDeclaredExtension() throws Exception { - HttpGet httpGet = new HttpGet("http://localhost:" + ourPort + "/Patient?_query=declaredExtInclude&_pretty=true"); + HttpGet httpGet = new HttpGet(ourServer.getBaseUrl() + "/Patient?_query=declaredExtInclude&_pretty=true"); HttpResponse status = ourClient.execute(httpGet); String responseContent = IOUtils.toString(status.getEntity().getContent()); IOUtils.closeQuietly(status.getEntity().getContent()); @@ -219,7 +222,7 @@ public class IncludeHl7OrgDstu2Test { @Test public void testTwoInclude() throws Exception { - HttpGet httpGet = new HttpGet("http://localhost:" + ourPort + "/Patient?name=Hello&_include=foo&_include=bar&_pretty=true"); + HttpGet httpGet = new HttpGet(ourServer.getBaseUrl() + "/Patient?name=Hello&_include=foo&_include=bar&_pretty=true"); HttpResponse status = ourClient.execute(httpGet); String responseContent = IOUtils.toString(status.getEntity().getContent()); IOUtils.closeQuietly(status.getEntity().getContent()); @@ -243,7 +246,7 @@ public class IncludeHl7OrgDstu2Test { @Test public void testBadInclude() throws Exception { - HttpGet httpGet = new HttpGet("http://localhost:" + ourPort + "/Patient?name=Hello&_include=foo&_include=baz"); + HttpGet httpGet = new HttpGet(ourServer.getBaseUrl() + "/Patient?name=Hello&_include=foo&_include=baz"); HttpResponse status = ourClient.execute(httpGet); assertEquals(400, status.getStatusLine().getStatusCode()); } @@ -435,33 +438,7 @@ public class IncludeHl7OrgDstu2Test { @AfterAll public static void afterClass() throws Exception { - JettyUtil.closeServer(ourServer); - } - - @BeforeAll - public static void beforeClass() throws Exception { - - ourCtx = FhirContext.forDstu2Hl7Org(); - ourServer = new Server(0); - - DummyPatientResourceProvider patientProvider = new DummyPatientResourceProvider(); - - ServletHandler proxyHandler = new ServletHandler(); - RestfulServer servlet = new RestfulServer(ourCtx); - servlet.setResourceProviders(patientProvider, new DummyDiagnosticReportResourceProvider()); - servlet.setBundleInclusionRule(BundleInclusionRule.BASED_ON_RESOURCE_PRESENCE); - servlet.setDefaultResponseEncoding(EncodingEnum.XML); - ServletHolder servletHolder = new ServletHolder(servlet); - proxyHandler.addServletWithMapping(servletHolder, "/*"); - ourServer.setHandler(proxyHandler); - JettyUtil.startServer(ourServer); - ourPort = JettyUtil.getPortForStartedServer(ourServer); - - PoolingHttpClientConnectionManager connectionManager = new PoolingHttpClientConnectionManager(5000, TimeUnit.MILLISECONDS); - HttpClientBuilder builder = HttpClientBuilder.create(); - builder.setConnectionManager(connectionManager); - ourClient = builder.build(); - + TestUtil.randomizeLocaleAndTimezone(); } } diff --git a/hapi-fhir-structures-hl7org-dstu2/src/test/java/ca/uhn/fhir/rest/server/OperationDuplicateServerHl7OrgDstu2Test.java b/hapi-fhir-structures-hl7org-dstu2/src/test/java/ca/uhn/fhir/rest/server/OperationDuplicateServerHl7OrgDstu2Test.java index 885e0427105..4a7f6762ef5 100644 --- a/hapi-fhir-structures-hl7org-dstu2/src/test/java/ca/uhn/fhir/rest/server/OperationDuplicateServerHl7OrgDstu2Test.java +++ b/hapi-fhir-structures-hl7org-dstu2/src/test/java/ca/uhn/fhir/rest/server/OperationDuplicateServerHl7OrgDstu2Test.java @@ -5,7 +5,10 @@ import ca.uhn.fhir.rest.annotation.Operation; import ca.uhn.fhir.rest.annotation.OperationParam; import ca.uhn.fhir.rest.api.EncodingEnum; import ca.uhn.fhir.system.HapiSystemProperties; +import ca.uhn.fhir.test.utilities.HttpClientExtension; import ca.uhn.fhir.test.utilities.JettyUtil; +import ca.uhn.fhir.test.utilities.server.RestfulServerExtension; +import ca.uhn.fhir.util.TestUtil; import org.apache.commons.io.IOUtils; import org.apache.http.HttpResponse; import org.apache.http.client.methods.HttpGet; @@ -13,8 +16,8 @@ import org.apache.http.impl.client.CloseableHttpClient; import org.apache.http.impl.client.HttpClientBuilder; import org.apache.http.impl.conn.PoolingHttpClientConnectionManager; import org.eclipse.jetty.server.Server; -import org.eclipse.jetty.servlet.ServletHandler; -import org.eclipse.jetty.servlet.ServletHolder; +import org.eclipse.jetty.ee10.servlet.ServletHandler; +import org.eclipse.jetty.ee10.servlet.ServletHolder; import org.hl7.fhir.dstu2.model.Conformance; import org.hl7.fhir.dstu2.model.OperationDefinition; import org.hl7.fhir.dstu2.model.Organization; @@ -25,6 +28,7 @@ import org.hl7.fhir.instance.model.api.IBaseResource; import org.junit.jupiter.api.AfterAll; import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.RegisterExtension; import java.util.concurrent.TimeUnit; @@ -32,20 +36,29 @@ import static org.junit.jupiter.api.Assertions.assertEquals; public class OperationDuplicateServerHl7OrgDstu2Test { private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(OperationDuplicateServerHl7OrgDstu2Test.class); - private static CloseableHttpClient ourClient; - private static FhirContext ourCtx; - private static int ourPort; - private static Server ourServer; + private static final FhirContext ourCtx = FhirContext.forDstu2Hl7OrgCached(); static { HapiSystemProperties.enableTestMode(); } + @RegisterExtension + public static RestfulServerExtension ourServer = new RestfulServerExtension(ourCtx) + .registerProvider(new PatientProvider()) + .registerProvider(new OrganizationProvider()) + .registerProvider(new PlainProvider()) + .withPagingProvider(new FifoMemoryPagingProvider(100)) + .setDefaultResponseEncoding(EncodingEnum.XML) + .setDefaultPrettyPrint(false); + + @RegisterExtension + public static HttpClientExtension ourClient = new HttpClientExtension(); + @Test public void testOperationsAreCollapsed() throws Exception { // Metadata { - HttpGet httpGet = new HttpGet("http://localhost:" + ourPort + "/metadata?_pretty=true"); + HttpGet httpGet = new HttpGet(ourServer.getBaseUrl() + "/metadata?_pretty=true"); HttpResponse status = ourClient.execute(httpGet); assertEquals(200, status.getStatusLine().getStatusCode()); @@ -64,7 +77,7 @@ public class OperationDuplicateServerHl7OrgDstu2Test { // OperationDefinition { - HttpGet httpGet = new HttpGet("http://localhost:" + ourPort + "/OperationDefinition/OrganizationPatient-ts-myoperation?_pretty=true"); + HttpGet httpGet = new HttpGet(ourServer.getBaseUrl() + "/OperationDefinition/OrganizationPatient-ts-myoperation?_pretty=true"); HttpResponse status = ourClient.execute(httpGet); assertEquals(200, status.getStatusLine().getStatusCode()); @@ -118,34 +131,7 @@ public class OperationDuplicateServerHl7OrgDstu2Test { @AfterAll public static void afterClass() throws Exception { - JettyUtil.closeServer(ourServer); - } - - @BeforeAll - public static void beforeClass() throws Exception { - ourCtx = FhirContext.forDstu2Hl7Org(); - ourServer = new Server(0); - - ServletHandler proxyHandler = new ServletHandler(); - - RestfulServer servlet = new RestfulServer(ourCtx); - servlet.setPagingProvider(new FifoMemoryPagingProvider(10).setDefaultPageSize(2)); - servlet.setFhirContext(ourCtx); - servlet.setResourceProviders(new PatientProvider(), new OrganizationProvider()); - servlet.setPlainProviders(new PlainProvider()); - servlet.setDefaultResponseEncoding(EncodingEnum.XML); - - ServletHolder servletHolder = new ServletHolder(servlet); - proxyHandler.addServletWithMapping(servletHolder, "/*"); - ourServer.setHandler(proxyHandler); - JettyUtil.startServer(ourServer); - ourPort = JettyUtil.getPortForStartedServer(ourServer); - - PoolingHttpClientConnectionManager connectionManager = new PoolingHttpClientConnectionManager(5000, TimeUnit.MILLISECONDS); - HttpClientBuilder builder = HttpClientBuilder.create(); - builder.setConnectionManager(connectionManager); - ourClient = builder.build(); - + TestUtil.randomizeLocaleAndTimezone(); } } diff --git a/hapi-fhir-structures-hl7org-dstu2/src/test/java/ca/uhn/fhir/rest/server/OperationServerHl7OrgTest.java b/hapi-fhir-structures-hl7org-dstu2/src/test/java/ca/uhn/fhir/rest/server/OperationServerHl7OrgTest.java index 43577d3d6b3..adc247aa472 100644 --- a/hapi-fhir-structures-hl7org-dstu2/src/test/java/ca/uhn/fhir/rest/server/OperationServerHl7OrgTest.java +++ b/hapi-fhir-structures-hl7org-dstu2/src/test/java/ca/uhn/fhir/rest/server/OperationServerHl7OrgTest.java @@ -8,7 +8,10 @@ import ca.uhn.fhir.rest.annotation.Read; import ca.uhn.fhir.rest.api.Constants; import ca.uhn.fhir.rest.api.EncodingEnum; import ca.uhn.fhir.rest.api.server.IBundleProvider; +import ca.uhn.fhir.test.utilities.HttpClientExtension; import ca.uhn.fhir.test.utilities.JettyUtil; +import ca.uhn.fhir.test.utilities.server.RestfulServerExtension; +import ca.uhn.fhir.util.TestUtil; import org.apache.commons.io.IOUtils; import org.apache.http.HttpResponse; import org.apache.http.client.methods.CloseableHttpResponse; @@ -20,8 +23,8 @@ import org.apache.http.impl.client.CloseableHttpClient; import org.apache.http.impl.client.HttpClientBuilder; import org.apache.http.impl.conn.PoolingHttpClientConnectionManager; import org.eclipse.jetty.server.Server; -import org.eclipse.jetty.servlet.ServletHandler; -import org.eclipse.jetty.servlet.ServletHolder; +import org.eclipse.jetty.ee10.servlet.ServletHandler; +import org.eclipse.jetty.ee10.servlet.ServletHolder; import org.hl7.fhir.dstu2.model.Bundle; import org.hl7.fhir.dstu2.model.IdType; import org.hl7.fhir.dstu2.model.IntegerType; @@ -33,6 +36,7 @@ import org.junit.jupiter.api.AfterAll; import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.RegisterExtension; import java.util.ArrayList; import java.util.List; @@ -47,16 +51,24 @@ import static org.junit.jupiter.api.Assertions.assertNull; public class OperationServerHl7OrgTest { private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(OperationServerHl7OrgTest.class); - private static CloseableHttpClient ourClient; - private static FhirContext ourCtx; + private static final FhirContext ourCtx = FhirContext.forDstu2Hl7OrgCached(); private static StringType ourLastParam1; private static Patient ourLastParam2; - private static int ourPort; private static IdType ourLastId; - private static Server ourServer; private static String ourLastMethod; private static List ourLastParam3; + @RegisterExtension + public static RestfulServerExtension ourServer = new RestfulServerExtension(ourCtx) + .registerProvider(new PatientProvider()) + .registerProvider(new PlainProvider()) + .withPagingProvider(new FifoMemoryPagingProvider(10).setDefaultPageSize(2)) + .setDefaultResponseEncoding(EncodingEnum.XML) + .setDefaultPrettyPrint(false); + + @RegisterExtension + public static HttpClientExtension ourClient = new HttpClientExtension(); + @BeforeEach public void before() { ourLastParam1 = null; @@ -73,7 +85,7 @@ public class OperationServerHl7OrgTest { p.addParameter().setName("PARAM2").setResource(new Patient().setActive(true)); String inParamsStr = ourCtx.newXmlParser().encodeResourceToString(p); - HttpPost httpPost = new HttpPost("http://localhost:" + ourPort + "/Patient/$OP_TYPE"); + HttpPost httpPost = new HttpPost(ourServer.getBaseUrl() + "/Patient/$OP_TYPE"); httpPost.setEntity(new StringEntity(inParamsStr, ContentType.create(Constants.CT_FHIR_XML, "UTF-8"))); HttpResponse status = ourClient.execute(httpPost); @@ -91,7 +103,7 @@ public class OperationServerHl7OrgTest { @Test public void testOperationWithGetUsingParams() throws Exception { - HttpGet httpGet = new HttpGet("http://localhost:" + ourPort + "/Patient/$OP_TYPE?PARAM1=PARAM1val"); + HttpGet httpGet = new HttpGet(ourServer.getBaseUrl() + "/Patient/$OP_TYPE?PARAM1=PARAM1val"); HttpResponse status = ourClient.execute(httpGet); assertEquals(200, status.getStatusLine().getStatusCode()); @@ -108,7 +120,7 @@ public class OperationServerHl7OrgTest { @Test public void testOperationWithGetUsingParamsFailsWithNonPrimitive() throws Exception { - HttpGet httpGet = new HttpGet("http://localhost:" + ourPort + "/Patient/$OP_TYPE?PARAM1=PARAM1val&PARAM2=foo"); + HttpGet httpGet = new HttpGet(ourServer.getBaseUrl() + "/Patient/$OP_TYPE?PARAM1=PARAM1val&PARAM2=foo"); HttpResponse status = ourClient.execute(httpGet); assertEquals(405, status.getStatusLine().getStatusCode()); @@ -126,7 +138,7 @@ public class OperationServerHl7OrgTest { p.addParameter().setName("PARAM2").setResource(new Patient().setActive(true)); String inParamsStr = ourCtx.newXmlParser().encodeResourceToString(p); - HttpPost httpPost = new HttpPost("http://localhost:" + ourPort + "/Patient/$OP_TYPE_RET_BUNDLE"); + HttpPost httpPost = new HttpPost(ourServer.getBaseUrl() + "/Patient/$OP_TYPE_RET_BUNDLE"); httpPost.setEntity(new StringEntity(inParamsStr, ContentType.create(Constants.CT_FHIR_XML, "UTF-8"))); HttpResponse status = ourClient.execute(httpPost); @@ -149,7 +161,7 @@ public class OperationServerHl7OrgTest { p.addParameter().setName("PARAM2").setResource(new Patient().setActive(true)); String inParamsStr = ourCtx.newXmlParser().encodeResourceToString(p); - HttpPost httpPost = new HttpPost("http://localhost:" + ourPort + "/$OP_SERVER"); + HttpPost httpPost = new HttpPost(ourServer.getBaseUrl() + "/$OP_SERVER"); httpPost.setEntity(new StringEntity(inParamsStr, ContentType.create(Constants.CT_FHIR_XML, "UTF-8"))); HttpResponse status = ourClient.execute(httpPost); @@ -167,7 +179,7 @@ public class OperationServerHl7OrgTest { @Test public void testOperationWithBundleProviderResponse() throws Exception { - HttpGet httpPost = new HttpGet("http://localhost:" + ourPort + "/$OP_INSTANCE_BUNDLE_PROVIDER?_pretty=true"); + HttpGet httpPost = new HttpGet(ourServer.getBaseUrl() + "/$OP_INSTANCE_BUNDLE_PROVIDER?_pretty=true"); HttpResponse status = ourClient.execute(httpPost); assertEquals(200, status.getStatusLine().getStatusCode()); @@ -187,7 +199,7 @@ public class OperationServerHl7OrgTest { p.addParameter().setName("PARAM3").setValue(new StringType("PARAM3val2")); String inParamsStr = ourCtx.newXmlParser().encodeResourceToString(p); - HttpPost httpPost = new HttpPost("http://localhost:" + ourPort + "/$OP_SERVER_LIST_PARAM"); + HttpPost httpPost = new HttpPost(ourServer.getBaseUrl() + "/$OP_SERVER_LIST_PARAM"); httpPost.setEntity(new StringEntity(inParamsStr, ContentType.create(Constants.CT_FHIR_XML, "UTF-8"))); HttpResponse status = ourClient.execute(httpPost); @@ -213,7 +225,7 @@ public class OperationServerHl7OrgTest { p.addParameter().setName("PARAM2").setResource(new Patient().setActive(true)); String inParamsStr = ourCtx.newXmlParser().encodeResourceToString(p); - HttpPost httpPost = new HttpPost("http://localhost:" + ourPort + "/Patient/123/$OP_INSTANCE"); + HttpPost httpPost = new HttpPost(ourServer.getBaseUrl() + "/Patient/123/$OP_INSTANCE"); httpPost.setEntity(new StringEntity(inParamsStr, ContentType.create(Constants.CT_FHIR_XML, "UTF-8"))); HttpResponse status = ourClient.execute(httpPost); @@ -232,7 +244,7 @@ public class OperationServerHl7OrgTest { @Test public void testOperationCantUseGetIfItIsntIdempotent() throws Exception { - HttpGet httpPost = new HttpGet("http://localhost:" + ourPort + "/Patient/123/$OP_INSTANCE"); + HttpGet httpPost = new HttpGet(ourServer.getBaseUrl() + "/Patient/123/$OP_INSTANCE"); HttpResponse status = ourClient.execute(httpPost); assertEquals(Constants.STATUS_HTTP_405_METHOD_NOT_ALLOWED, status.getStatusLine().getStatusCode()); @@ -250,7 +262,7 @@ public class OperationServerHl7OrgTest { p.addParameter().setName("PARAM2").setResource(new Patient().setActive(true)); String inParamsStr = ourCtx.newXmlParser().encodeResourceToString(p); - HttpPost httpPost = new HttpPost("http://localhost:" + ourPort + "/Patient/$OP_TYPE"); + HttpPost httpPost = new HttpPost(ourServer.getBaseUrl() + "/Patient/$OP_TYPE"); httpPost.setEntity(new StringEntity(inParamsStr, ContentType.create(Constants.CT_FHIR_XML, "UTF-8"))); HttpResponse status = ourClient.execute(httpPost); @@ -266,7 +278,7 @@ public class OperationServerHl7OrgTest { @Test public void testReadWithOperations() throws Exception { - HttpGet httpGet = new HttpGet("http://localhost:" + ourPort + "/Patient/123"); + HttpGet httpGet = new HttpGet(ourServer.getBaseUrl() + "/Patient/123"); HttpResponse status = ourClient.execute(httpGet); assertEquals(200, status.getStatusLine().getStatusCode()); @@ -280,7 +292,7 @@ public class OperationServerHl7OrgTest { String inParamsStr = ourCtx.newXmlParser().encodeResourceToString(new Parameters()); // Try with a POST - HttpPost httpPost = new HttpPost("http://localhost:" + ourPort + "/Patient/123/$everything"); + HttpPost httpPost = new HttpPost(ourServer.getBaseUrl() + "/Patient/123/$everything"); httpPost.setEntity(new StringEntity(inParamsStr, ContentType.create(Constants.CT_FHIR_XML, "UTF-8"))); HttpResponse status = ourClient.execute(httpPost); @@ -296,7 +308,7 @@ public class OperationServerHl7OrgTest { @Test public void testInstanceEverythingHapiClient() throws Exception { - Parameters p = ourCtx.newRestfulGenericClient("http://localhost:" + ourPort).operation().onInstance(new IdType("Patient/123")).named("$everything").withParameters(new Parameters()).execute(); + Parameters p = ourCtx.newRestfulGenericClient(ourServer.getBaseUrl()).operation().onInstance(new IdType("Patient/123")).named("$everything").withParameters(new Parameters()).execute(); Bundle b = (Bundle) p.getParameter().get(0).getResource(); assertNotNull(b); @@ -309,7 +321,7 @@ public class OperationServerHl7OrgTest { public void testInstanceEverythingGet() throws Exception { // Try with a GET - HttpGet httpGet = new HttpGet("http://localhost:" + ourPort + "/Patient/123/$everything"); + HttpGet httpGet = new HttpGet(ourServer.getBaseUrl() + "/Patient/123/$everything"); CloseableHttpResponse status = ourClient.execute(httpGet); assertEquals(200, status.getStatusLine().getStatusCode()); @@ -473,34 +485,8 @@ public class OperationServerHl7OrgTest { @AfterAll public static void afterClass() throws Exception { - JettyUtil.closeServer(ourServer); + TestUtil.randomizeLocaleAndTimezone(); } - @BeforeAll - public static void beforeClass() throws Exception { - ourCtx = FhirContext.forDstu2Hl7Org(); - ourServer = new Server(0); - - ServletHandler proxyHandler = new ServletHandler(); - RestfulServer servlet = new RestfulServer(ourCtx); - - servlet.setPagingProvider(new FifoMemoryPagingProvider(10).setDefaultPageSize(2)); - servlet.setFhirContext(ourCtx); - servlet.setResourceProviders(new PatientProvider()); - servlet.setPlainProviders(new PlainProvider()); - servlet.setDefaultResponseEncoding(EncodingEnum.XML); - - ServletHolder servletHolder = new ServletHolder(servlet); - proxyHandler.addServletWithMapping(servletHolder, "/*"); - ourServer.setHandler(proxyHandler); - JettyUtil.startServer(ourServer); - ourPort = JettyUtil.getPortForStartedServer(ourServer); - - PoolingHttpClientConnectionManager connectionManager = new PoolingHttpClientConnectionManager(5000, TimeUnit.MILLISECONDS); - HttpClientBuilder builder = HttpClientBuilder.create(); - builder.setConnectionManager(connectionManager); - ourClient = builder.build(); - - } } diff --git a/hapi-fhir-structures-hl7org-dstu2/src/test/java/ca/uhn/fhir/rest/server/PreferHl7OrgDstu2Test.java b/hapi-fhir-structures-hl7org-dstu2/src/test/java/ca/uhn/fhir/rest/server/PreferHl7OrgDstu2Test.java index 57c3ad93858..92be0bbb272 100644 --- a/hapi-fhir-structures-hl7org-dstu2/src/test/java/ca/uhn/fhir/rest/server/PreferHl7OrgDstu2Test.java +++ b/hapi-fhir-structures-hl7org-dstu2/src/test/java/ca/uhn/fhir/rest/server/PreferHl7OrgDstu2Test.java @@ -6,8 +6,12 @@ import ca.uhn.fhir.rest.annotation.IdParam; import ca.uhn.fhir.rest.annotation.ResourceParam; import ca.uhn.fhir.rest.annotation.Update; import ca.uhn.fhir.rest.api.Constants; +import ca.uhn.fhir.rest.api.EncodingEnum; import ca.uhn.fhir.rest.api.MethodOutcome; +import ca.uhn.fhir.test.utilities.HttpClientExtension; import ca.uhn.fhir.test.utilities.JettyUtil; +import ca.uhn.fhir.test.utilities.server.RestfulServerExtension; +import ca.uhn.fhir.util.TestUtil; import org.apache.commons.io.IOUtils; import org.apache.http.HttpResponse; import org.apache.http.client.methods.HttpPost; @@ -17,13 +21,14 @@ import org.apache.http.impl.client.CloseableHttpClient; import org.apache.http.impl.client.HttpClientBuilder; import org.apache.http.impl.conn.PoolingHttpClientConnectionManager; import org.eclipse.jetty.server.Server; -import org.eclipse.jetty.servlet.ServletHandler; -import org.eclipse.jetty.servlet.ServletHolder; +import org.eclipse.jetty.ee10.servlet.ServletHandler; +import org.eclipse.jetty.ee10.servlet.ServletHolder; import org.hl7.fhir.dstu2.model.IdType; import org.hl7.fhir.dstu2.model.Patient; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.AfterAll; import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.extension.RegisterExtension; import java.util.concurrent.TimeUnit; @@ -33,21 +38,28 @@ import static org.junit.jupiter.api.Assertions.assertEquals; * Created by dsotnikov on 2/25/2014. */ public class PreferHl7OrgDstu2Test { - private static CloseableHttpClient ourClient; - + private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(PreferHl7OrgDstu2Test.class); - private static int ourPort; - private static Server ourServer; - private static FhirContext ourCtx = FhirContext.forDstu2Hl7Org(); - - - @Test + private static final FhirContext ourCtx = FhirContext.forDstu2Hl7OrgCached(); + + @RegisterExtension + public static RestfulServerExtension ourServer = new RestfulServerExtension(ourCtx) + .registerProvider(new PatientProvider()) + .withPagingProvider(new FifoMemoryPagingProvider(100)) + .setDefaultResponseEncoding(EncodingEnum.JSON) + .setDefaultPrettyPrint(false); + + @RegisterExtension + public static HttpClientExtension ourClient = new HttpClientExtension(); + + + @Test public void testCreateWithNoPrefer() throws Exception { Patient patient = new Patient(); patient.addIdentifier().setValue("002"); - HttpPost httpPost = new HttpPost("http://localhost:" + ourPort + "/Patient"); + HttpPost httpPost = new HttpPost(ourServer.getBaseUrl() + "/Patient"); httpPost.setEntity(new StringEntity(ourCtx.newXmlParser().encodeResourceToString(patient), ContentType.create(Constants.CT_FHIR_XML, "UTF-8"))); HttpResponse status = ourClient.execute(httpPost); @@ -58,8 +70,8 @@ public class PreferHl7OrgDstu2Test { ourLog.info("Response was:\n{}", responseContent); assertEquals(201, status.getStatusLine().getStatusCode()); - assertEquals("http://localhost:" + ourPort + "/Patient/001/_history/002", status.getFirstHeader("location").getValue()); - assertEquals("http://localhost:" + ourPort + "/Patient/001/_history/002", status.getFirstHeader("content-location").getValue()); + assertEquals(ourServer.getBaseUrl() + "/Patient/001/_history/002", status.getFirstHeader("location").getValue()); + assertEquals(ourServer.getBaseUrl() + "/Patient/001/_history/002", status.getFirstHeader("content-location").getValue()); } @@ -68,32 +80,10 @@ public class PreferHl7OrgDstu2Test { @AfterAll public static void afterClass() throws Exception { - JettyUtil.closeServer(ourServer); + TestUtil.randomizeLocaleAndTimezone(); } - @BeforeAll - public static void beforeClass() throws Exception { - ourServer = new Server(0); - - PatientProvider patientProvider = new PatientProvider(); - - ServletHandler proxyHandler = new ServletHandler(); - RestfulServer servlet = new RestfulServer(ourCtx); - servlet.setResourceProviders(patientProvider); - ServletHolder servletHolder = new ServletHolder(servlet); - proxyHandler.addServletWithMapping(servletHolder, "/*"); - ourServer.setHandler(proxyHandler); - JettyUtil.startServer(ourServer); - ourPort = JettyUtil.getPortForStartedServer(ourServer); - - PoolingHttpClientConnectionManager connectionManager = new PoolingHttpClientConnectionManager(5000, TimeUnit.MILLISECONDS); - HttpClientBuilder builder = HttpClientBuilder.create(); - builder.setConnectionManager(connectionManager); - ourClient = builder.build(); - - } - public static class PatientProvider implements IResourceProvider { @Override diff --git a/hapi-fhir-structures-hl7org-dstu2/src/test/java/ca/uhn/fhir/rest/server/ReadHl7OrgDstu2Test.java b/hapi-fhir-structures-hl7org-dstu2/src/test/java/ca/uhn/fhir/rest/server/ReadHl7OrgDstu2Test.java index 1a13e271050..890df560551 100644 --- a/hapi-fhir-structures-hl7org-dstu2/src/test/java/ca/uhn/fhir/rest/server/ReadHl7OrgDstu2Test.java +++ b/hapi-fhir-structures-hl7org-dstu2/src/test/java/ca/uhn/fhir/rest/server/ReadHl7OrgDstu2Test.java @@ -3,22 +3,17 @@ package ca.uhn.fhir.rest.server; import ca.uhn.fhir.context.FhirContext; import ca.uhn.fhir.rest.annotation.IdParam; import ca.uhn.fhir.rest.annotation.Read; -import ca.uhn.fhir.test.utilities.JettyUtil; +import ca.uhn.fhir.rest.api.EncodingEnum; +import ca.uhn.fhir.test.utilities.HttpClientExtension; +import ca.uhn.fhir.test.utilities.server.RestfulServerExtension; +import ca.uhn.fhir.util.TestUtil; import org.apache.commons.io.IOUtils; import org.apache.http.HttpResponse; import org.apache.http.client.methods.HttpGet; -import org.apache.http.impl.client.CloseableHttpClient; -import org.apache.http.impl.client.HttpClientBuilder; -import org.apache.http.impl.conn.PoolingHttpClientConnectionManager; -import org.eclipse.jetty.server.Server; -import org.eclipse.jetty.servlet.ServletHandler; -import org.eclipse.jetty.servlet.ServletHolder; import org.hl7.fhir.dstu2.model.Patient; import org.junit.jupiter.api.AfterAll; -import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.Test; - -import java.util.concurrent.TimeUnit; +import org.junit.jupiter.api.extension.RegisterExtension; import static org.hamcrest.MatcherAssert.assertThat; import static org.hamcrest.Matchers.containsString; @@ -26,17 +21,24 @@ import static org.junit.jupiter.api.Assertions.assertEquals; public class ReadHl7OrgDstu2Test { - private static CloseableHttpClient ourClient; - private static int ourPort; - private static Server ourServer; - private static FhirContext ourCtx = FhirContext.forDstu2Hl7Org(); - - /** + private static final FhirContext ourCtx = FhirContext.forDstu2Hl7OrgCached(); + + @RegisterExtension + public static RestfulServerExtension ourServer = new RestfulServerExtension(ourCtx) + .registerProvider(new DummyPatientResourceProvider()) + .withPagingProvider(new FifoMemoryPagingProvider(100)) + .setDefaultResponseEncoding(EncodingEnum.JSON) + .setDefaultPrettyPrint(false); + + @RegisterExtension + public static HttpClientExtension ourClient = new HttpClientExtension(); + + /** * In DSTU2+ the resource ID appears in the resource body */ @Test public void testReadXml() throws Exception { - HttpGet httpGet = new HttpGet("http://localhost:" + ourPort + "/Patient/123&_format=xml"); + HttpGet httpGet = new HttpGet(ourServer.getBaseUrl() + "/Patient/123&_format=xml"); HttpResponse status = ourClient.execute(httpGet); String responseContent = IOUtils.toString(status.getEntity().getContent()); IOUtils.closeQuietly(status.getEntity().getContent()); @@ -51,7 +53,7 @@ public class ReadHl7OrgDstu2Test { */ @Test public void testReadJson() throws Exception { - HttpGet httpGet = new HttpGet("http://localhost:" + ourPort + "/Patient/123&_format=json"); + HttpGet httpGet = new HttpGet(ourServer.getBaseUrl() + "/Patient/123&_format=json"); HttpResponse status = ourClient.execute(httpGet); String responseContent = IOUtils.toString(status.getEntity().getContent()); IOUtils.closeQuietly(status.getEntity().getContent()); @@ -63,30 +65,7 @@ public class ReadHl7OrgDstu2Test { @AfterAll public static void afterClass() throws Exception { - JettyUtil.closeServer(ourServer); - } - - @BeforeAll - public static void beforeClass() throws Exception { - ourServer = new Server(0); - - DummyPatientResourceProvider patientProvider = new DummyPatientResourceProvider(); - - ServletHandler proxyHandler = new ServletHandler(); - RestfulServer servlet = new RestfulServer(ourCtx); - servlet.setFhirContext(ourCtx); - servlet.setResourceProviders(patientProvider); - ServletHolder servletHolder = new ServletHolder(servlet); - proxyHandler.addServletWithMapping(servletHolder, "/*"); - ourServer.setHandler(proxyHandler); - JettyUtil.startServer(ourServer); - ourPort = JettyUtil.getPortForStartedServer(ourServer); - - PoolingHttpClientConnectionManager connectionManager = new PoolingHttpClientConnectionManager(5000, TimeUnit.MILLISECONDS); - HttpClientBuilder builder = HttpClientBuilder.create(); - builder.setConnectionManager(connectionManager); - ourClient = builder.build(); - + TestUtil.randomizeLocaleAndTimezone(); } /** diff --git a/hapi-fhir-structures-hl7org-dstu2/src/test/java/ca/uhn/fhir/rest/server/SearchHl7OrgDstu2Test.java b/hapi-fhir-structures-hl7org-dstu2/src/test/java/ca/uhn/fhir/rest/server/SearchHl7OrgDstu2Test.java index a2a9ddf740b..7b034d381b6 100644 --- a/hapi-fhir-structures-hl7org-dstu2/src/test/java/ca/uhn/fhir/rest/server/SearchHl7OrgDstu2Test.java +++ b/hapi-fhir-structures-hl7org-dstu2/src/test/java/ca/uhn/fhir/rest/server/SearchHl7OrgDstu2Test.java @@ -6,7 +6,10 @@ import ca.uhn.fhir.rest.annotation.Search; import ca.uhn.fhir.rest.api.Constants; import ca.uhn.fhir.rest.api.EncodingEnum; import ca.uhn.fhir.rest.api.server.IBundleProvider; +import ca.uhn.fhir.test.utilities.HttpClientExtension; import ca.uhn.fhir.test.utilities.JettyUtil; +import ca.uhn.fhir.test.utilities.server.RestfulServerExtension; +import ca.uhn.fhir.util.TestUtil; import org.apache.commons.io.IOUtils; import org.apache.http.HttpResponse; import org.apache.http.client.methods.HttpGet; @@ -14,16 +17,17 @@ import org.apache.http.impl.client.CloseableHttpClient; import org.apache.http.impl.client.HttpClientBuilder; import org.apache.http.impl.conn.PoolingHttpClientConnectionManager; import org.eclipse.jetty.server.Server; -import org.eclipse.jetty.servlet.ServletHandler; -import org.eclipse.jetty.servlet.ServletHolder; +import org.eclipse.jetty.ee10.servlet.ServletHandler; +import org.eclipse.jetty.ee10.servlet.ServletHolder; import org.hl7.fhir.dstu2.model.Bundle; import org.hl7.fhir.dstu2.model.Patient; import org.hl7.fhir.instance.model.api.IBaseResource; import org.junit.jupiter.api.AfterAll; import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.RegisterExtension; -import javax.annotation.Nonnull; +import jakarta.annotation.Nonnull; import java.util.List; import java.util.concurrent.TimeUnit; @@ -38,17 +42,22 @@ import static org.junit.jupiter.api.Assertions.assertNull; public class SearchHl7OrgDstu2Test { private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(SearchHl7OrgDstu2Test.class); - private static CloseableHttpClient ourClient; - private static FhirContext ourCtx = FhirContext.forDstu2Hl7Org(); - private static int ourPort; - + private static final FhirContext ourCtx = FhirContext.forDstu2Hl7OrgCached(); private static InstantDt ourReturnPublished; - private static Server ourServer; + @RegisterExtension + public static RestfulServerExtension ourServer = new RestfulServerExtension(ourCtx) + .registerProvider(new DummyPatientResourceProvider()) + .withPagingProvider(new FifoMemoryPagingProvider(100)) + .setDefaultResponseEncoding(EncodingEnum.XML) + .setDefaultPrettyPrint(false); + + @RegisterExtension + public static HttpClientExtension ourClient = new HttpClientExtension(); @Test public void testEncodeConvertsReferencesToRelative() throws Exception { - HttpGet httpGet = new HttpGet("http://localhost:" + ourPort + "/Patient?_query=searchWithRef"); + HttpGet httpGet = new HttpGet(ourServer.getBaseUrl() + "/Patient?_query=searchWithRef"); HttpResponse status = ourClient.execute(httpGet); String responseContent = IOUtils.toString(status.getEntity().getContent()); IOUtils.closeQuietly(status.getEntity().getContent()); @@ -65,7 +74,7 @@ public class SearchHl7OrgDstu2Test { @Test public void testEncodeConvertsReferencesToRelativeJson() throws Exception { - HttpGet httpGet = new HttpGet("http://localhost:" + ourPort + "/Patient?_query=searchWithRef&_format=json"); + HttpGet httpGet = new HttpGet(ourServer.getBaseUrl() + "/Patient?_query=searchWithRef&_format=json"); HttpResponse status = ourClient.execute(httpGet); String responseContent = IOUtils.toString(status.getEntity().getContent()); IOUtils.closeQuietly(status.getEntity().getContent()); @@ -82,7 +91,7 @@ public class SearchHl7OrgDstu2Test { @Test public void testResultBundleHasUuid() throws Exception { - HttpGet httpGet = new HttpGet("http://localhost:" + ourPort + "/Patient?_query=searchWithRef"); + HttpGet httpGet = new HttpGet(ourServer.getBaseUrl() + "/Patient?_query=searchWithRef"); HttpResponse status = ourClient.execute(httpGet); String responseContent = IOUtils.toString(status.getEntity().getContent()); IOUtils.closeQuietly(status.getEntity().getContent()); @@ -97,7 +106,7 @@ public class SearchHl7OrgDstu2Test { ourReturnPublished = new InstantDt("2011-02-03T11:22:33Z"); assertEquals(ourReturnPublished.getValueAsString(), "2011-02-03T11:22:33Z"); - HttpGet httpGet = new HttpGet("http://localhost:" + ourPort + "/Patient?_query=searchWithBundleProvider&_pretty=true"); + HttpGet httpGet = new HttpGet(ourServer.getBaseUrl() + "/Patient?_query=searchWithBundleProvider&_pretty=true"); HttpResponse status = ourClient.execute(httpGet); String responseContent = IOUtils.toString(status.getEntity().getContent()); IOUtils.closeQuietly(status.getEntity().getContent()); @@ -153,7 +162,7 @@ public class SearchHl7OrgDstu2Test { public Patient searchWithRef() { Patient patient = new Patient(); patient.setId("Patient/1/_history/1"); - patient.getManagingOrganization().setReference("http://localhost:" + ourPort + "/Organization/555/_history/666"); + patient.getManagingOrganization().setReference(ourServer.getBaseUrl() + "/Organization/555/_history/666"); return patient; } @@ -161,31 +170,7 @@ public class SearchHl7OrgDstu2Test { @AfterAll public static void afterClass() throws Exception { - JettyUtil.closeServer(ourServer); - } - - @BeforeAll - public static void beforeClass() throws Exception { - ourServer = new Server(0); - - DummyPatientResourceProvider patientProvider = new DummyPatientResourceProvider(); - - ServletHandler proxyHandler = new ServletHandler(); - RestfulServer servlet = new RestfulServer(ourCtx); - servlet.setResourceProviders(patientProvider); - servlet.setDefaultResponseEncoding(EncodingEnum.XML); - - ServletHolder servletHolder = new ServletHolder(servlet); - proxyHandler.addServletWithMapping(servletHolder, "/*"); - ourServer.setHandler(proxyHandler); - JettyUtil.startServer(ourServer); - ourPort = JettyUtil.getPortForStartedServer(ourServer); - - PoolingHttpClientConnectionManager connectionManager = new PoolingHttpClientConnectionManager(5000, TimeUnit.MILLISECONDS); - HttpClientBuilder builder = HttpClientBuilder.create(); - builder.setConnectionManager(connectionManager); - ourClient = builder.build(); - + TestUtil.randomizeLocaleAndTimezone(); } } diff --git a/hapi-fhir-structures-hl7org-dstu2/src/test/java/ca/uhn/fhir/rest/server/SearchWithGenericListHl7OrgDstu2Test.java b/hapi-fhir-structures-hl7org-dstu2/src/test/java/ca/uhn/fhir/rest/server/SearchWithGenericListHl7OrgDstu2Test.java index af3acf25d3d..a288b8217c0 100644 --- a/hapi-fhir-structures-hl7org-dstu2/src/test/java/ca/uhn/fhir/rest/server/SearchWithGenericListHl7OrgDstu2Test.java +++ b/hapi-fhir-structures-hl7org-dstu2/src/test/java/ca/uhn/fhir/rest/server/SearchWithGenericListHl7OrgDstu2Test.java @@ -5,28 +5,22 @@ import ca.uhn.fhir.rest.annotation.RequiredParam; import ca.uhn.fhir.rest.annotation.Search; import ca.uhn.fhir.rest.api.EncodingEnum; import ca.uhn.fhir.rest.param.TokenParam; -import ca.uhn.fhir.test.utilities.JettyUtil; +import ca.uhn.fhir.test.utilities.HttpClientExtension; +import ca.uhn.fhir.test.utilities.server.RestfulServerExtension; import ca.uhn.fhir.util.TestUtil; import org.apache.commons.io.IOUtils; import org.apache.http.HttpResponse; import org.apache.http.client.methods.HttpGet; -import org.apache.http.impl.client.CloseableHttpClient; -import org.apache.http.impl.client.HttpClientBuilder; -import org.apache.http.impl.conn.PoolingHttpClientConnectionManager; -import org.eclipse.jetty.server.Server; -import org.eclipse.jetty.servlet.ServletHandler; -import org.eclipse.jetty.servlet.ServletHolder; import org.hl7.fhir.dstu2.model.HumanName; import org.hl7.fhir.dstu2.model.Patient; import org.hl7.fhir.instance.model.api.IBaseResource; import org.junit.jupiter.api.AfterAll; -import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.RegisterExtension; import java.util.ArrayList; import java.util.List; -import java.util.concurrent.TimeUnit; import static org.hamcrest.MatcherAssert.assertThat; import static org.hamcrest.Matchers.containsString; @@ -35,12 +29,19 @@ import static org.junit.jupiter.api.Assertions.assertEquals; public class SearchWithGenericListHl7OrgDstu2Test { private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(SearchWithGenericListHl7OrgDstu2Test.class); - private static CloseableHttpClient ourClient; - private static FhirContext ourCtx = FhirContext.forDstu2Hl7Org(); - private static int ourPort; - private static Server ourServer; + private static final FhirContext ourCtx = FhirContext.forDstu2Hl7OrgCached(); private static String ourLastMethod; + @RegisterExtension + public static RestfulServerExtension ourServer = new RestfulServerExtension(ourCtx) + .registerProvider(new DummyPatientResourceProvider()) + .withPagingProvider(new FifoMemoryPagingProvider(100)) + .setDefaultResponseEncoding(EncodingEnum.XML) + .setDefaultPrettyPrint(false); + + @RegisterExtension + public static HttpClientExtension ourClient = new HttpClientExtension(); + @BeforeEach public void before() { ourLastMethod = null; @@ -51,7 +52,7 @@ public class SearchWithGenericListHl7OrgDstu2Test { */ @Test public void testSearch() throws Exception { - HttpGet httpGet = new HttpGet("http://localhost:" + ourPort + "/Patient?identifier=foo&_pretty=true"); + HttpGet httpGet = new HttpGet(ourServer.getBaseUrl() + "/Patient?identifier=foo&_pretty=true"); HttpResponse status = ourClient.execute(httpGet); String responseContent = IOUtils.toString(status.getEntity().getContent()); IOUtils.closeQuietly(status.getEntity().getContent()); @@ -59,7 +60,7 @@ public class SearchWithGenericListHl7OrgDstu2Test { assertEquals(200, status.getStatusLine().getStatusCode()); assertEquals("searchByIdentifier", ourLastMethod); assertThat(responseContent, containsString("")); + assertThat(responseContent, containsString("")); } public static class DummyPatientResourceProvider implements IResourceProvider { @@ -86,33 +87,7 @@ public class SearchWithGenericListHl7OrgDstu2Test { @AfterAll public static void afterClassClearContext() throws Exception { - JettyUtil.closeServer(ourServer); TestUtil.randomizeLocaleAndTimezone(); } - @BeforeAll - public static void beforeClass() throws Exception { - ourServer = new Server(0); - - DummyPatientResourceProvider patientProvider = new DummyPatientResourceProvider(); - - ServletHandler proxyHandler = new ServletHandler(); - RestfulServer servlet = new RestfulServer(ourCtx); - servlet.setPagingProvider(new FifoMemoryPagingProvider(10)); - servlet.setDefaultResponseEncoding(EncodingEnum.XML); - servlet.setResourceProviders(patientProvider); - - ServletHolder servletHolder = new ServletHolder(servlet); - proxyHandler.addServletWithMapping(servletHolder, "/*"); - ourServer.setHandler(proxyHandler); - JettyUtil.startServer(ourServer); - ourPort = JettyUtil.getPortForStartedServer(ourServer); - - PoolingHttpClientConnectionManager connectionManager = new PoolingHttpClientConnectionManager(5000, TimeUnit.MILLISECONDS); - HttpClientBuilder builder = HttpClientBuilder.create(); - builder.setConnectionManager(connectionManager); - ourClient = builder.build(); - - } - } diff --git a/hapi-fhir-structures-hl7org-dstu2/src/test/java/ca/uhn/fhir/rest/server/SearchWithHl7OrgDstu2BundleTest.java b/hapi-fhir-structures-hl7org-dstu2/src/test/java/ca/uhn/fhir/rest/server/SearchWithHl7OrgDstu2BundleTest.java index 009b4656a8b..5269847861a 100644 --- a/hapi-fhir-structures-hl7org-dstu2/src/test/java/ca/uhn/fhir/rest/server/SearchWithHl7OrgDstu2BundleTest.java +++ b/hapi-fhir-structures-hl7org-dstu2/src/test/java/ca/uhn/fhir/rest/server/SearchWithHl7OrgDstu2BundleTest.java @@ -2,23 +2,18 @@ package ca.uhn.fhir.rest.server; import ca.uhn.fhir.context.FhirContext; import ca.uhn.fhir.rest.annotation.Search; -import ca.uhn.fhir.test.utilities.JettyUtil; +import ca.uhn.fhir.rest.api.EncodingEnum; +import ca.uhn.fhir.test.utilities.HttpClientExtension; +import ca.uhn.fhir.test.utilities.server.RestfulServerExtension; +import ca.uhn.fhir.util.TestUtil; import org.apache.commons.io.IOUtils; import org.apache.http.HttpResponse; import org.apache.http.client.methods.HttpGet; -import org.apache.http.impl.client.CloseableHttpClient; -import org.apache.http.impl.client.HttpClientBuilder; -import org.apache.http.impl.conn.PoolingHttpClientConnectionManager; -import org.eclipse.jetty.server.Server; -import org.eclipse.jetty.servlet.ServletHandler; -import org.eclipse.jetty.servlet.ServletHolder; import org.hl7.fhir.dstu2.model.Bundle; import org.hl7.fhir.dstu2.model.Patient; import org.junit.jupiter.api.AfterAll; -import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.Test; - -import java.util.concurrent.TimeUnit; +import org.junit.jupiter.api.extension.RegisterExtension; import static org.hamcrest.MatcherAssert.assertThat; import static org.hamcrest.Matchers.stringContainsInOrder; @@ -26,15 +21,22 @@ import static org.junit.jupiter.api.Assertions.assertEquals; public class SearchWithHl7OrgDstu2BundleTest { - private static CloseableHttpClient ourClient; - private static int ourPort; - private static Server ourServer; - private static FhirContext ourCtx = FhirContext.forDstu2Hl7Org(); + private static final FhirContext ourCtx = FhirContext.forDstu2Hl7OrgCached(); private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(SearchWithHl7OrgDstu2BundleTest.class); - @Test + @RegisterExtension + public static RestfulServerExtension ourServer = new RestfulServerExtension(ourCtx) + .registerProvider(new DummyPatientResourceProvider()) + .withPagingProvider(new FifoMemoryPagingProvider(100)) + .setDefaultResponseEncoding(EncodingEnum.JSON) + .setDefaultPrettyPrint(false); + + @RegisterExtension + public static HttpClientExtension ourClient = new HttpClientExtension(); + + @Test public void testSearch() throws Exception { - HttpGet httpGet = new HttpGet("http://localhost:" + ourPort + "/Patient?_format=xml&_pretty=true"); + HttpGet httpGet = new HttpGet(ourServer.getBaseUrl() + "/Patient?_format=xml&_pretty=true"); HttpResponse status = ourClient.execute(httpGet); String responseContent = IOUtils.toString(status.getEntity().getContent()); IOUtils.closeQuietly(status.getEntity().getContent()); @@ -50,10 +52,10 @@ public class SearchWithHl7OrgDstu2BundleTest { "", "" , "", - "", + "", "" , "" , - //"" , + //"" , "" , "")); // @formatter:off @@ -62,30 +64,7 @@ public class SearchWithHl7OrgDstu2BundleTest { @AfterAll public static void afterClass() throws Exception { - JettyUtil.closeServer(ourServer); - } - - @BeforeAll - public static void beforeClass() throws Exception { - ourServer = new Server(0); - - DummyPatientResourceProvider patientProvider = new DummyPatientResourceProvider(); - - ServletHandler proxyHandler = new ServletHandler(); - RestfulServer servlet = new RestfulServer(ourCtx); - servlet.setFhirContext(ourCtx); - servlet.setResourceProviders(patientProvider); - ServletHolder servletHolder = new ServletHolder(servlet); - proxyHandler.addServletWithMapping(servletHolder, "/*"); - ourServer.setHandler(proxyHandler); - JettyUtil.startServer(ourServer); - ourPort = JettyUtil.getPortForStartedServer(ourServer); - - PoolingHttpClientConnectionManager connectionManager = new PoolingHttpClientConnectionManager(5000000, TimeUnit.MILLISECONDS); - HttpClientBuilder builder = HttpClientBuilder.create(); - builder.setConnectionManager(connectionManager); - ourClient = builder.build(); - + TestUtil.randomizeLocaleAndTimezone(); } /** diff --git a/hapi-fhir-structures-hl7org-dstu2/src/test/java/ca/uhn/fhir/rest/server/ServerConformanceProviderHl7OrgDstu2Test.java b/hapi-fhir-structures-hl7org-dstu2/src/test/java/ca/uhn/fhir/rest/server/ServerConformanceProviderHl7OrgDstu2Test.java index a2b44b07ae7..2f202dc09ff 100644 --- a/hapi-fhir-structures-hl7org-dstu2/src/test/java/ca/uhn/fhir/rest/server/ServerConformanceProviderHl7OrgDstu2Test.java +++ b/hapi-fhir-structures-hl7org-dstu2/src/test/java/ca/uhn/fhir/rest/server/ServerConformanceProviderHl7OrgDstu2Test.java @@ -45,8 +45,8 @@ import org.hl7.fhir.dstu2.model.StringType; import org.hl7.fhir.instance.model.api.IBaseResource; import org.junit.jupiter.api.Test; -import javax.servlet.ServletConfig; -import javax.servlet.http.HttpServletRequest; +import jakarta.servlet.ServletConfig; +import jakarta.servlet.http.HttpServletRequest; import java.util.Collection; import java.util.List; import java.util.Set; @@ -492,7 +492,7 @@ public class ServerConformanceProviderHl7OrgDstu2Test { @Operation(name = "plain", idempotent = true, returnParameters= { @OperationParam(min=1, max=2, name="out1", type=StringType.class) }) - public IBundleProvider everything(javax.servlet.http.HttpServletRequest theServletRequest, @IdParam IdType theId, @OperationParam(name = "start") DateType theStart, @OperationParam(name = "end") DateType theEnd) { + public IBundleProvider everything(jakarta.servlet.http.HttpServletRequest theServletRequest, @IdParam IdType theId, @OperationParam(name = "start") DateType theStart, @OperationParam(name = "end") DateType theEnd) { return null; } @@ -501,7 +501,7 @@ public class ServerConformanceProviderHl7OrgDstu2Test { public static class ProviderWithExtendedOperationReturningBundle implements IResourceProvider { @Operation(name = "everything", idempotent = true) - public IBundleProvider everything(javax.servlet.http.HttpServletRequest theServletRequest, @IdParam IdType theId, @OperationParam(name = "start") DateType theStart, @OperationParam(name = "end") DateType theEnd) { + public IBundleProvider everything(jakarta.servlet.http.HttpServletRequest theServletRequest, @IdParam IdType theId, @OperationParam(name = "start") DateType theStart, @OperationParam(name = "end") DateType theEnd) { return null; } diff --git a/hapi-fhir-structures-hl7org-dstu2/src/test/java/ca/uhn/fhir/rest/server/TransactionWithBundleResourceParamHl7OrgDstu2Test.java b/hapi-fhir-structures-hl7org-dstu2/src/test/java/ca/uhn/fhir/rest/server/TransactionWithBundleResourceParamHl7OrgDstu2Test.java index f101a33165c..89ab146034d 100644 --- a/hapi-fhir-structures-hl7org-dstu2/src/test/java/ca/uhn/fhir/rest/server/TransactionWithBundleResourceParamHl7OrgDstu2Test.java +++ b/hapi-fhir-structures-hl7org-dstu2/src/test/java/ca/uhn/fhir/rest/server/TransactionWithBundleResourceParamHl7OrgDstu2Test.java @@ -16,7 +16,7 @@ import org.apache.http.impl.client.CloseableHttpClient; import org.apache.http.impl.client.HttpClientBuilder; import org.apache.http.impl.conn.PoolingHttpClientConnectionManager; import org.eclipse.jetty.server.Server; -import org.eclipse.jetty.servlet.ServletHolder; +import org.eclipse.jetty.ee10.servlet.ServletHolder; import org.hl7.fhir.dstu2.model.Bundle; import org.hl7.fhir.dstu2.model.Bundle.BundleEntryComponent; import org.hl7.fhir.dstu2.model.Bundle.HTTPVerb; @@ -231,7 +231,7 @@ public class TransactionWithBundleResourceParamHl7OrgDstu2Test { RestfulServer server = new RestfulServer(ourCtx); server.setProviders(patientProvider); - org.eclipse.jetty.servlet.ServletContextHandler proxyHandler = new org.eclipse.jetty.servlet.ServletContextHandler(); + org.eclipse.jetty.ee10.servlet.ServletContextHandler proxyHandler = new org.eclipse.jetty.ee10.servlet.ServletContextHandler(); proxyHandler.setContextPath("/"); ServletHolder handler = new ServletHolder(); diff --git a/hapi-fhir-structures-hl7org-dstu2/src/test/java/ca/uhn/fhir/rest/server/UpdateConditionalHl7OrgDstu2Test.java b/hapi-fhir-structures-hl7org-dstu2/src/test/java/ca/uhn/fhir/rest/server/UpdateConditionalHl7OrgDstu2Test.java index 2c33117196d..f78b2581532 100644 --- a/hapi-fhir-structures-hl7org-dstu2/src/test/java/ca/uhn/fhir/rest/server/UpdateConditionalHl7OrgDstu2Test.java +++ b/hapi-fhir-structures-hl7org-dstu2/src/test/java/ca/uhn/fhir/rest/server/UpdateConditionalHl7OrgDstu2Test.java @@ -10,8 +10,12 @@ import ca.uhn.fhir.rest.annotation.ResourceParam; import ca.uhn.fhir.rest.annotation.Search; import ca.uhn.fhir.rest.annotation.Update; import ca.uhn.fhir.rest.api.Constants; +import ca.uhn.fhir.rest.api.EncodingEnum; import ca.uhn.fhir.rest.api.MethodOutcome; +import ca.uhn.fhir.test.utilities.HttpClientExtension; import ca.uhn.fhir.test.utilities.JettyUtil; +import ca.uhn.fhir.test.utilities.server.RestfulServerExtension; +import ca.uhn.fhir.util.TestUtil; import org.apache.commons.io.IOUtils; import org.apache.http.HttpResponse; import org.apache.http.client.methods.HttpGet; @@ -22,14 +26,15 @@ import org.apache.http.impl.client.CloseableHttpClient; import org.apache.http.impl.client.HttpClientBuilder; import org.apache.http.impl.conn.PoolingHttpClientConnectionManager; import org.eclipse.jetty.server.Server; -import org.eclipse.jetty.servlet.ServletHandler; -import org.eclipse.jetty.servlet.ServletHolder; +import org.eclipse.jetty.ee10.servlet.ServletHandler; +import org.eclipse.jetty.ee10.servlet.ServletHolder; import org.hl7.fhir.dstu2.model.IdType; import org.hl7.fhir.dstu2.model.Patient; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.AfterAll; import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.extension.RegisterExtension; import java.util.ArrayList; import java.util.List; @@ -40,19 +45,26 @@ import static org.junit.jupiter.api.Assertions.assertNull; import static org.junit.jupiter.api.Assertions.assertTrue; public class UpdateConditionalHl7OrgDstu2Test { - private static CloseableHttpClient ourClient; private static String ourLastConditionalUrl; private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(UpdateConditionalHl7OrgDstu2Test.class); - private static int ourPort; - private static FhirContext ourCtx = FhirContext.forDstu2Hl7Org(); - private static Server ourServer; + private static final FhirContext ourCtx = FhirContext.forDstu2Hl7OrgCached(); private static String ourLastId; private static IdType ourLastIdParam; private static boolean ourLastRequestWasSearch; - - - - @BeforeEach + + + @RegisterExtension + public static RestfulServerExtension ourServer = new RestfulServerExtension(ourCtx) + .registerProvider(new PatientProvider()) + .withPagingProvider(new FifoMemoryPagingProvider(100)) + .setDefaultResponseEncoding(EncodingEnum.JSON) + .setDefaultPrettyPrint(false); + + @RegisterExtension + public static HttpClientExtension ourClient = new HttpClientExtension(); + + + @BeforeEach public void before() { ourLastId = null; ourLastConditionalUrl = null; @@ -66,7 +78,7 @@ public class UpdateConditionalHl7OrgDstu2Test { Patient patient = new Patient(); patient.addIdentifier().setValue("002"); - HttpPut httpPost = new HttpPut("http://localhost:" + ourPort + "/Patient?identifier=system%7C001"); + HttpPut httpPost = new HttpPut(ourServer.getBaseUrl() + "/Patient?identifier=system%7C001"); httpPost.setEntity(new StringEntity(ourCtx.newXmlParser().encodeResourceToString(patient), ContentType.create(Constants.CT_FHIR_XML, "UTF-8"))); HttpResponse status = ourClient.execute(httpPost); @@ -78,7 +90,7 @@ public class UpdateConditionalHl7OrgDstu2Test { assertEquals(200, status.getStatusLine().getStatusCode()); assertEquals(null, status.getFirstHeader("location")); - assertEquals("http://localhost:" + ourPort + "/Patient/001/_history/002", status.getFirstHeader("content-location").getValue()); + assertEquals(ourServer.getBaseUrl() + "/Patient/001/_history/002", status.getFirstHeader("content-location").getValue()); assertNull(ourLastId); assertNull(ourLastIdParam); @@ -93,7 +105,7 @@ public class UpdateConditionalHl7OrgDstu2Test { patient.setId("2"); patient.addIdentifier().setValue("002"); - HttpPut httpPost = new HttpPut("http://localhost:" + ourPort + "/Patient/2"); + HttpPut httpPost = new HttpPut(ourServer.getBaseUrl() + "/Patient/2"); httpPost.setEntity(new StringEntity(ourCtx.newXmlParser().encodeResourceToString(patient), ContentType.create(Constants.CT_FHIR_XML, "UTF-8"))); HttpResponse status = ourClient.execute(httpPost); @@ -105,7 +117,7 @@ public class UpdateConditionalHl7OrgDstu2Test { assertEquals(200, status.getStatusLine().getStatusCode()); assertEquals(null, status.getFirstHeader("location")); - assertEquals("http://localhost:" + ourPort + "/Patient/001/_history/002", status.getFirstHeader("content-location").getValue()); + assertEquals(ourServer.getBaseUrl() + "/Patient/001/_history/002", status.getFirstHeader("content-location").getValue()); assertEquals("Patient/2", new IdType(ourLastId).toUnqualified().getValue()); assertEquals("Patient/2", ourLastIdParam.toUnqualified().getValue()); @@ -119,7 +131,7 @@ public class UpdateConditionalHl7OrgDstu2Test { Patient patient = new Patient(); patient.addIdentifier().setValue("002"); - HttpGet httpGet = new HttpGet("http://localhost:" + ourPort + "/Patient?_pretty=true"); + HttpGet httpGet = new HttpGet(ourServer.getBaseUrl() + "/Patient?_pretty=true"); HttpResponse status = ourClient.execute(httpGet); @@ -138,32 +150,10 @@ public class UpdateConditionalHl7OrgDstu2Test { @AfterAll public static void afterClass() throws Exception { - JettyUtil.closeServer(ourServer); + TestUtil.randomizeLocaleAndTimezone(); } - @BeforeAll - public static void beforeClass() throws Exception { - ourServer = new Server(0); - - PatientProvider patientProvider = new PatientProvider(); - - ServletHandler proxyHandler = new ServletHandler(); - RestfulServer servlet = new RestfulServer(ourCtx); - servlet.setResourceProviders(patientProvider); - ServletHolder servletHolder = new ServletHolder(servlet); - proxyHandler.addServletWithMapping(servletHolder, "/*"); - ourServer.setHandler(proxyHandler); - JettyUtil.startServer(ourServer); - ourPort = JettyUtil.getPortForStartedServer(ourServer); - - PoolingHttpClientConnectionManager connectionManager = new PoolingHttpClientConnectionManager(5000, TimeUnit.MILLISECONDS); - HttpClientBuilder builder = HttpClientBuilder.create(); - builder.setConnectionManager(connectionManager); - ourClient = builder.build(); - - } - public static class PatientProvider implements IResourceProvider { @Override diff --git a/hapi-fhir-structures-hl7org-dstu2/src/test/java/ca/uhn/fhir/rest/server/ValidateHl7OrgDstu2Test.java b/hapi-fhir-structures-hl7org-dstu2/src/test/java/ca/uhn/fhir/rest/server/ValidateHl7OrgDstu2Test.java index 268ef8c772d..6994f042f11 100644 --- a/hapi-fhir-structures-hl7org-dstu2/src/test/java/ca/uhn/fhir/rest/server/ValidateHl7OrgDstu2Test.java +++ b/hapi-fhir-structures-hl7org-dstu2/src/test/java/ca/uhn/fhir/rest/server/ValidateHl7OrgDstu2Test.java @@ -8,7 +8,10 @@ import ca.uhn.fhir.rest.api.Constants; import ca.uhn.fhir.rest.api.EncodingEnum; import ca.uhn.fhir.rest.api.MethodOutcome; import ca.uhn.fhir.rest.api.ValidationModeEnum; +import ca.uhn.fhir.test.utilities.HttpClientExtension; import ca.uhn.fhir.test.utilities.JettyUtil; +import ca.uhn.fhir.test.utilities.server.RestfulServerExtension; +import ca.uhn.fhir.util.TestUtil; import org.apache.commons.io.IOUtils; import org.apache.http.HttpResponse; import org.apache.http.client.methods.HttpPost; @@ -18,8 +21,8 @@ import org.apache.http.impl.client.CloseableHttpClient; import org.apache.http.impl.client.HttpClientBuilder; import org.apache.http.impl.conn.PoolingHttpClientConnectionManager; import org.eclipse.jetty.server.Server; -import org.eclipse.jetty.servlet.ServletHandler; -import org.eclipse.jetty.servlet.ServletHolder; +import org.eclipse.jetty.ee10.servlet.ServletHandler; +import org.eclipse.jetty.ee10.servlet.ServletHolder; import org.hl7.fhir.dstu2.model.IdType; import org.hl7.fhir.dstu2.model.OperationOutcome; import org.hl7.fhir.dstu2.model.Organization; @@ -30,6 +33,7 @@ import org.junit.jupiter.api.Test; import org.junit.jupiter.api.AfterAll; import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.extension.RegisterExtension; import java.util.concurrent.TimeUnit; @@ -41,17 +45,25 @@ import static org.junit.jupiter.api.Assertions.assertEquals; * Created by dsotnikov on 2/25/2014. */ public class ValidateHl7OrgDstu2Test { - private static CloseableHttpClient ourClient; private static EncodingEnum ourLastEncoding; private static String ourLastResourceBody; - private static int ourPort; - private static Server ourServer; private static OperationOutcome ourOutcomeToReturn; private static ValidationModeEnum ourLastMode; private static String ourLastProfile; - private static FhirContext ourCtx = FhirContext.forDstu2Hl7Org(); - - @BeforeEach() + private static final FhirContext ourCtx = FhirContext.forDstu2Hl7OrgCached(); + + @RegisterExtension + public static RestfulServerExtension ourServer = new RestfulServerExtension(ourCtx) + .registerProvider(new PatientProvider()) + .registerProvider(new OrganizationProvider()) + .withPagingProvider(new FifoMemoryPagingProvider(100)) + .setDefaultResponseEncoding(EncodingEnum.JSON) + .setDefaultPrettyPrint(false); + + @RegisterExtension + public static HttpClientExtension ourClient = new HttpClientExtension(); + + @BeforeEach() public void before() { ourLastResourceBody = null; ourLastEncoding = null; @@ -72,7 +84,7 @@ public class ValidateHl7OrgDstu2Test { params.addParameter().setName("profile").setValue(new StringType("http://foo")); params.addParameter().setName("mode").setValue(new StringType(ValidationModeEnum.CREATE.getCode())); - HttpPost httpPost = new HttpPost("http://localhost:" + ourPort + "/Patient/$validate"); + HttpPost httpPost = new HttpPost(ourServer.getBaseUrl() + "/Patient/$validate"); httpPost.setEntity(new StringEntity(ourCtx.newXmlParser().encodeResourceToString(params), ContentType.create(Constants.CT_FHIR_XML, "UTF-8"))); HttpResponse status = ourClient.execute(httpPost); @@ -96,7 +108,7 @@ public class ValidateHl7OrgDstu2Test { Parameters params = new Parameters(); params.addParameter().setName("resource").setResource(patient); - HttpPost httpPost = new HttpPost("http://localhost:" + ourPort + "/Patient/$validate"); + HttpPost httpPost = new HttpPost(ourServer.getBaseUrl() + "/Patient/$validate"); httpPost.setEntity(new StringEntity(ourCtx.newXmlParser().encodeResourceToString(params), ContentType.create(Constants.CT_FHIR_XML, "UTF-8"))); HttpResponse status = ourClient.execute(httpPost); @@ -121,7 +133,7 @@ public class ValidateHl7OrgDstu2Test { Parameters params = new Parameters(); params.addParameter().setName("resource").setResource(patient); - HttpPost httpPost = new HttpPost("http://localhost:" + ourPort + "/Patient/$validate"); + HttpPost httpPost = new HttpPost(ourServer.getBaseUrl() + "/Patient/$validate"); httpPost.setEntity(new StringEntity(ourCtx.newXmlParser().encodeResourceToString(params), ContentType.create(Constants.CT_FHIR_XML, "UTF-8"))); HttpResponse status = ourClient.execute(httpPost); @@ -143,7 +155,7 @@ public class ValidateHl7OrgDstu2Test { Parameters params = new Parameters(); params.addParameter().setName("resource").setResource(org); - HttpPost httpPost = new HttpPost("http://localhost:" + ourPort + "/Organization/$validate"); + HttpPost httpPost = new HttpPost(ourServer.getBaseUrl() + "/Organization/$validate"); httpPost.setEntity(new StringEntity(ourCtx.newJsonParser().encodeResourceToString(params), ContentType.create(Constants.CT_FHIR_JSON, "UTF-8"))); HttpResponse status = ourClient.execute(httpPost); @@ -156,30 +168,9 @@ public class ValidateHl7OrgDstu2Test { @AfterAll public static void afterClass() throws Exception { - JettyUtil.closeServer(ourServer); + TestUtil.randomizeLocaleAndTimezone(); } - @BeforeAll - public static void beforeClass() throws Exception { - ourServer = new Server(0); - - PatientProvider patientProvider = new PatientProvider(); - - ServletHandler proxyHandler = new ServletHandler(); - RestfulServer servlet = new RestfulServer(ourCtx); - servlet.setResourceProviders(patientProvider, new OrganizationProvider()); - ServletHolder servletHolder = new ServletHolder(servlet); - proxyHandler.addServletWithMapping(servletHolder, "/*"); - ourServer.setHandler(proxyHandler); - JettyUtil.startServer(ourServer); - ourPort = JettyUtil.getPortForStartedServer(ourServer); - - PoolingHttpClientConnectionManager connectionManager = new PoolingHttpClientConnectionManager(5000, TimeUnit.MILLISECONDS); - HttpClientBuilder builder = HttpClientBuilder.create(); - builder.setConnectionManager(connectionManager); - ourClient = builder.build(); - - } public static class PatientProvider implements IResourceProvider { diff --git a/hapi-fhir-structures-r4/pom.xml b/hapi-fhir-structures-r4/pom.xml index 59b166ee958..f2d1ae5e412 100644 --- a/hapi-fhir-structures-r4/pom.xml +++ b/hapi-fhir-structures-r4/pom.xml @@ -5,7 +5,7 @@ ca.uhn.hapi.fhir hapi-deployable-pom - 6.9.7-SNAPSHOT + 6.11.7-SNAPSHOT ../hapi-deployable-pom/pom.xml @@ -106,8 +106,8 @@ true - javax.servlet - javax.servlet-api + jakarta.servlet + jakarta.servlet-api true @@ -140,6 +140,11 @@ + + com.helger.commons + ph-collection + test + @@ -191,36 +196,6 @@ xmlunit-core test - - org.eclipse.jetty - jetty-servlets - test - - - org.eclipse.jetty - jetty-servlet - test - - - org.eclipse.jetty - jetty-server - test - - - org.eclipse.jetty - jetty-util - test - - - org.eclipse.jetty - jetty-webapp - test - - - org.eclipse.jetty - jetty-http - test - ch.qos.logback logback-classic @@ -244,34 +219,6 @@ test - - - com.helger - ph-schematron - test - - - com.helger - ph-commons - test - - - javax.activation - javax.activation-api - test - - - javax.xml.bind - jaxb-api - test - - - org.glassfish.jaxb - jaxb-runtime - test - - - net.sf.json-lib @@ -356,13 +303,6 @@ true - - org.apache.maven.plugins - maven-compiler-plugin - - true - - org.apache.felix maven-bundle-plugin diff --git a/hapi-fhir-structures-r4/src/main/java/org/hl7/fhir/r4/hapi/fluentpath/FhirPathR4.java b/hapi-fhir-structures-r4/src/main/java/org/hl7/fhir/r4/hapi/fluentpath/FhirPathR4.java index 9b867892b7f..5c492e158e3 100644 --- a/hapi-fhir-structures-r4/src/main/java/org/hl7/fhir/r4/hapi/fluentpath/FhirPathR4.java +++ b/hapi-fhir-structures-r4/src/main/java/org/hl7/fhir/r4/hapi/fluentpath/FhirPathR4.java @@ -6,6 +6,7 @@ import ca.uhn.fhir.fhirpath.FhirPathExecutionException; import ca.uhn.fhir.fhirpath.IFhirPath; import ca.uhn.fhir.fhirpath.IFhirPathEvaluationContext; import ca.uhn.fhir.i18n.Msg; +import jakarta.annotation.Nonnull; import org.hl7.fhir.exceptions.FHIRException; import org.hl7.fhir.exceptions.PathEngineException; import org.hl7.fhir.instance.model.api.IBase; @@ -20,7 +21,6 @@ import org.hl7.fhir.r4.utils.FHIRPathUtilityClasses.FunctionDetails; import java.util.List; import java.util.Optional; -import javax.annotation.Nonnull; public class FhirPathR4 implements IFhirPath { diff --git a/hapi-fhir-structures-r4/src/main/java/org/hl7/fhir/r4/hapi/rest/server/R4BundleFactory.java b/hapi-fhir-structures-r4/src/main/java/org/hl7/fhir/r4/hapi/rest/server/R4BundleFactory.java index 58a179e211c..73cd64314b1 100644 --- a/hapi-fhir-structures-r4/src/main/java/org/hl7/fhir/r4/hapi/rest/server/R4BundleFactory.java +++ b/hapi-fhir-structures-r4/src/main/java/org/hl7/fhir/r4/hapi/rest/server/R4BundleFactory.java @@ -31,6 +31,8 @@ import ca.uhn.fhir.rest.api.Constants; import ca.uhn.fhir.rest.api.IVersionSpecificBundleFactory; import ca.uhn.fhir.rest.server.RestfulServerUtils; import ca.uhn.fhir.util.ResourceReferenceInfo; +import jakarta.annotation.Nonnull; +import jakarta.annotation.Nullable; import org.hl7.fhir.instance.model.api.IAnyResource; import org.hl7.fhir.instance.model.api.IBaseResource; import org.hl7.fhir.instance.model.api.IIdType; @@ -49,8 +51,6 @@ import java.util.HashSet; import java.util.List; import java.util.Set; import java.util.UUID; -import javax.annotation.Nonnull; -import javax.annotation.Nullable; import static org.apache.commons.lang3.StringUtils.isNotBlank; diff --git a/hapi-fhir-structures-r4/src/main/java/org/hl7/fhir/r4/hapi/rest/server/helper/BatchHelperR4.java b/hapi-fhir-structures-r4/src/main/java/org/hl7/fhir/r4/hapi/rest/server/helper/BatchHelperR4.java index ff3ce1796c3..1affa8a03d0 100644 --- a/hapi-fhir-structures-r4/src/main/java/org/hl7/fhir/r4/hapi/rest/server/helper/BatchHelperR4.java +++ b/hapi-fhir-structures-r4/src/main/java/org/hl7/fhir/r4/hapi/rest/server/helper/BatchHelperR4.java @@ -1,12 +1,11 @@ package org.hl7.fhir.r4.hapi.rest.server.helper; import ca.uhn.fhir.rest.server.provider.ProviderConstants; +import jakarta.annotation.Nonnull; import org.hl7.fhir.r4.model.DecimalType; import org.hl7.fhir.r4.model.Parameters; import org.hl7.fhir.r4.model.StringType; -import javax.annotation.Nonnull; - public class BatchHelperR4 { @Nonnull diff --git a/hapi-fhir-structures-r4/src/test/java/ca/uhn/fhir/narrative/CustomThymeleafNarrativeGeneratorR4Test.java b/hapi-fhir-structures-r4/src/test/java/ca/uhn/fhir/narrative/CustomThymeleafNarrativeGeneratorR4Test.java index 012552f7896..f7e9bd5797d 100644 --- a/hapi-fhir-structures-r4/src/test/java/ca/uhn/fhir/narrative/CustomThymeleafNarrativeGeneratorR4Test.java +++ b/hapi-fhir-structures-r4/src/test/java/ca/uhn/fhir/narrative/CustomThymeleafNarrativeGeneratorR4Test.java @@ -15,9 +15,6 @@ public class CustomThymeleafNarrativeGeneratorR4Test { private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(CustomThymeleafNarrativeGeneratorR4Test.class); - /** - * Don't use cached here since we modify the context - */ private final FhirContext myCtx = FhirContext.forR4Cached(); @AfterEach @@ -30,7 +27,6 @@ public class CustomThymeleafNarrativeGeneratorR4Test { */ @Test public void testStandardType() { - CustomThymeleafNarrativeGenerator gen = new CustomThymeleafNarrativeGenerator("classpath:narrative/standardtypes_r4.properties"); myCtx.setNarrativeGenerator(gen); @@ -51,6 +47,7 @@ public class CustomThymeleafNarrativeGeneratorR4Test { @Test public void testCustomType() { + myCtx.setNarrativeGenerator(null); CustomPatient patient = new CustomPatient(); patient.setActive(true); diff --git a/hapi-fhir-structures-r4/src/test/java/ca/uhn/fhir/narrative/DefaultThymeleafNarrativeGeneratorR4Test.java b/hapi-fhir-structures-r4/src/test/java/ca/uhn/fhir/narrative/DefaultThymeleafNarrativeGeneratorR4Test.java index 6a88ca6c9e8..f78bc4d32a3 100644 --- a/hapi-fhir-structures-r4/src/test/java/ca/uhn/fhir/narrative/DefaultThymeleafNarrativeGeneratorR4Test.java +++ b/hapi-fhir-structures-r4/src/test/java/ca/uhn/fhir/narrative/DefaultThymeleafNarrativeGeneratorR4Test.java @@ -91,7 +91,7 @@ public class DefaultThymeleafNarrativeGeneratorR4Test { String parse = "\n" + " \n" + " \n" + - " \n" + + " \n" + " \n" + " \n" + " \n" + diff --git a/hapi-fhir-structures-r4/src/test/java/ca/uhn/fhir/parser/CustomTypeR4Test.java b/hapi-fhir-structures-r4/src/test/java/ca/uhn/fhir/parser/CustomTypeR4Test.java index 18215244fdf..76ce4a8f3c6 100644 --- a/hapi-fhir-structures-r4/src/test/java/ca/uhn/fhir/parser/CustomTypeR4Test.java +++ b/hapi-fhir-structures-r4/src/test/java/ca/uhn/fhir/parser/CustomTypeR4Test.java @@ -28,7 +28,7 @@ import org.springframework.beans.factory.config.ConfigurableListableBeanFactory; import org.springframework.context.ConfigurableApplicationContext; import org.springframework.context.annotation.AnnotationConfigApplicationContext; -import javax.annotation.Nonnull; +import jakarta.annotation.Nonnull; import java.util.ArrayList; import java.util.Collections; import java.util.List; diff --git a/hapi-fhir-structures-r4/src/test/java/ca/uhn/fhir/parser/RDFParserR4Test.java b/hapi-fhir-structures-r4/src/test/java/ca/uhn/fhir/parser/RDFParserR4Test.java index 0d20454ddae..ea370b70b12 100644 --- a/hapi-fhir-structures-r4/src/test/java/ca/uhn/fhir/parser/RDFParserR4Test.java +++ b/hapi-fhir-structures-r4/src/test/java/ca/uhn/fhir/parser/RDFParserR4Test.java @@ -55,9 +55,9 @@ public class RDFParserR4Test { @prefix xsd: . - rdf:type fhir:Patient ; - fhir:Patient.active [ fhir:value true ] ; - fhir:Resource.id [ fhir:value "123" ] ; + rdf:type fhir:Patient; + fhir:Patient.active [ fhir:value true ]; + fhir:Resource.id [ fhir:value "123" ]; fhir:nodeRole fhir:treeRoot . """; diff --git a/hapi-fhir-structures-r4/src/test/java/ca/uhn/fhir/parser/RDFParserTest.java b/hapi-fhir-structures-r4/src/test/java/ca/uhn/fhir/parser/RDFParserTest.java index a27fddac70c..7ddda19896a 100644 --- a/hapi-fhir-structures-r4/src/test/java/ca/uhn/fhir/parser/RDFParserTest.java +++ b/hapi-fhir-structures-r4/src/test/java/ca/uhn/fhir/parser/RDFParserTest.java @@ -38,6 +38,8 @@ import org.hl7.fhir.instance.model.api.IBaseResource; import org.hl7.fhir.r4.model.Base; import org.junit.jupiter.api.AfterAll; import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.parallel.Execution; +import org.junit.jupiter.api.parallel.ExecutionMode; import org.junit.jupiter.params.ParameterizedTest; import org.junit.jupiter.params.provider.MethodSource; import org.springframework.core.io.Resource; @@ -88,6 +90,7 @@ public class RDFParserTest extends BaseTest { */ @ParameterizedTest @MethodSource("getInputFiles") + @Execution(ExecutionMode.CONCURRENT) public void testRDFRoundTrip(String referenceFilePath) throws IOException { String referenceFileName = referenceFilePath.substring(referenceFilePath.lastIndexOf("/")+1); IBaseResource referenceResource = parseJson(new FileInputStream(referenceFilePath)); diff --git a/hapi-fhir-structures-r4/src/test/java/ca/uhn/fhir/rest/client/ClientHeadersR4Test.java b/hapi-fhir-structures-r4/src/test/java/ca/uhn/fhir/rest/client/ClientHeadersR4Test.java index e14028ac170..7d5a51910f1 100644 --- a/hapi-fhir-structures-r4/src/test/java/ca/uhn/fhir/rest/client/ClientHeadersR4Test.java +++ b/hapi-fhir-structures-r4/src/test/java/ca/uhn/fhir/rest/client/ClientHeadersR4Test.java @@ -9,17 +9,17 @@ import ca.uhn.fhir.rest.client.api.ServerValidationModeEnum; import ca.uhn.fhir.test.utilities.JettyUtil; import ca.uhn.fhir.util.TestUtil; import org.eclipse.jetty.server.Server; -import org.eclipse.jetty.servlet.ServletContextHandler; -import org.eclipse.jetty.servlet.ServletHolder; +import org.eclipse.jetty.ee10.servlet.ServletContextHandler; +import org.eclipse.jetty.ee10.servlet.ServletHolder; import org.hl7.fhir.r4.model.Patient; import org.junit.jupiter.api.AfterAll; import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; -import javax.servlet.http.HttpServlet; -import javax.servlet.http.HttpServletRequest; -import javax.servlet.http.HttpServletResponse; +import jakarta.servlet.http.HttpServlet; +import jakarta.servlet.http.HttpServletRequest; +import jakarta.servlet.http.HttpServletResponse; import java.io.IOException; import java.util.ArrayList; import java.util.Enumeration; diff --git a/hapi-fhir-structures-r4/src/test/java/ca/uhn/fhir/rest/client/ClientIntegrationTest.java b/hapi-fhir-structures-r4/src/test/java/ca/uhn/fhir/rest/client/ClientIntegrationTest.java index fe78ecb8004..026af8d9059 100644 --- a/hapi-fhir-structures-r4/src/test/java/ca/uhn/fhir/rest/client/ClientIntegrationTest.java +++ b/hapi-fhir-structures-r4/src/test/java/ca/uhn/fhir/rest/client/ClientIntegrationTest.java @@ -4,69 +4,66 @@ import ca.uhn.fhir.context.FhirContext; import ca.uhn.fhir.model.primitive.StringDt; import ca.uhn.fhir.rest.annotation.RequiredParam; import ca.uhn.fhir.rest.annotation.Search; +import ca.uhn.fhir.rest.api.EncodingEnum; +import ca.uhn.fhir.rest.client.apache.ApacheRestfulClientFactory; import ca.uhn.fhir.rest.client.api.IBasicClient; import ca.uhn.fhir.rest.client.impl.HttpBasicAuthInterceptor; +import ca.uhn.fhir.rest.server.FifoMemoryPagingProvider; import ca.uhn.fhir.rest.server.IResourceProvider; import ca.uhn.fhir.rest.server.RestfulServer; +import ca.uhn.fhir.rest.server.interceptor.ExceptionInterceptorMethodTest; +import ca.uhn.fhir.test.utilities.HttpClientExtension; import ca.uhn.fhir.test.utilities.JettyUtil; +import ca.uhn.fhir.test.utilities.server.RestfulServerExtension; import ca.uhn.fhir.util.TestUtil; import org.apache.commons.lang3.Validate; import org.apache.http.impl.client.CloseableHttpClient; import org.apache.http.impl.client.HttpClientBuilder; import org.eclipse.jetty.server.Server; -import org.eclipse.jetty.servlet.ServletHandler; -import org.eclipse.jetty.servlet.ServletHolder; +import org.eclipse.jetty.ee10.servlet.ServletHandler; +import org.eclipse.jetty.ee10.servlet.ServletHolder; import org.hl7.fhir.r4.model.Patient; import org.junit.jupiter.api.AfterAll; import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; -import javax.servlet.http.HttpServletRequest; -import javax.servlet.http.HttpServletResponse; +import jakarta.servlet.http.HttpServletRequest; +import jakarta.servlet.http.HttpServletResponse; +import org.junit.jupiter.api.extension.RegisterExtension; + import java.util.Collections; import java.util.List; import static org.junit.jupiter.api.Assertions.assertEquals; public class ClientIntegrationTest { - private Server myServer; - private MyPatientResourceProvider myPatientProvider; - private static FhirContext ourCtx = FhirContext.forR4(); + private MyPatientResourceProvider myPatientProvider = new MyPatientResourceProvider(); + private static final FhirContext ourCtx = FhirContext.forR4Cached(); - @BeforeEach - public void before() { - myServer = new Server(0); + @RegisterExtension + public RestfulServerExtension ourServer = new RestfulServerExtension(ourCtx) + .registerProvider(myPatientProvider) + .withPagingProvider(new FifoMemoryPagingProvider(100)) + .setDefaultResponseEncoding(EncodingEnum.XML); - myPatientProvider = new MyPatientResourceProvider(); - - ServletHandler proxyHandler = new ServletHandler(); - RestfulServer servlet = new RestfulServer(ourCtx); - servlet.setResourceProviders(myPatientProvider); - ServletHolder servletHolder = new ServletHolder(servlet); - proxyHandler.addServletWithMapping(servletHolder, "/*"); - myServer.setHandler(proxyHandler); - - } + @RegisterExtension + private HttpClientExtension ourClient = new HttpClientExtension(); @SuppressWarnings("deprecation") @Test public void testClientSecurity() throws Exception { - JettyUtil.startServer(myServer); - int myPort = JettyUtil.getPortForStartedServer(myServer); - - FhirContext ctx = FhirContext.forR4(); - HttpClientBuilder builder = HttpClientBuilder.create(); // PoolingHttpClientConnectionManager connectionManager = new PoolingHttpClientConnectionManager(5000, TimeUnit.MILLISECONDS); // builder.setConnectionManager(connectionManager); builder.addInterceptorFirst(new HttpBasicAuthInterceptor("foobar", "boobear")); CloseableHttpClient httpClient = builder.build(); - ctx.getRestfulClientFactory().setHttpClient(httpClient); - PatientClient client = ctx.newRestfulClient(PatientClient.class, "http://localhost:" + myPort + "/"); + ourCtx.getRestfulClientFactory().setHttpClient(httpClient); + + PatientClient client = ourCtx.newRestfulClient(PatientClient.class, ourServer.getBaseUrl() + "/"); List actualPatients = client.searchForPatients(new StringDt("AAAABBBB")); assertEquals(1, actualPatients.size()); @@ -77,7 +74,7 @@ public class ClientIntegrationTest { @AfterEach public void after() throws Exception { - JettyUtil.closeServer(myServer); + ourCtx.setRestfulClientFactory(new ApacheRestfulClientFactory(ourCtx)); } public static class MyPatientResourceProvider implements IResourceProvider { diff --git a/hapi-fhir-structures-r4/src/test/java/ca/uhn/fhir/rest/client/CompressOutgoingContentInterceptorTest.java b/hapi-fhir-structures-r4/src/test/java/ca/uhn/fhir/rest/client/CompressOutgoingContentInterceptorTest.java index e24883f5696..31bf708965e 100644 --- a/hapi-fhir-structures-r4/src/test/java/ca/uhn/fhir/rest/client/CompressOutgoingContentInterceptorTest.java +++ b/hapi-fhir-structures-r4/src/test/java/ca/uhn/fhir/rest/client/CompressOutgoingContentInterceptorTest.java @@ -5,51 +5,48 @@ import ca.uhn.fhir.model.primitive.IdDt; import ca.uhn.fhir.rest.annotation.Create; import ca.uhn.fhir.rest.annotation.ResourceParam; import ca.uhn.fhir.rest.api.Constants; +import ca.uhn.fhir.rest.api.EncodingEnum; import ca.uhn.fhir.rest.api.MethodOutcome; import ca.uhn.fhir.rest.client.apache.GZipContentInterceptor; import ca.uhn.fhir.rest.client.api.IGenericClient; import ca.uhn.fhir.rest.server.FifoMemoryPagingProvider; import ca.uhn.fhir.rest.server.IResourceProvider; -import ca.uhn.fhir.rest.server.RestfulServer; -import ca.uhn.fhir.test.utilities.JettyUtil; +import ca.uhn.fhir.test.utilities.HttpClientExtension; +import ca.uhn.fhir.test.utilities.server.RestfulServerExtension; import ca.uhn.fhir.util.TestUtil; -import org.eclipse.jetty.server.Server; -import org.eclipse.jetty.servlet.ServletHandler; -import org.eclipse.jetty.servlet.ServletHolder; +import jakarta.servlet.http.HttpServletRequest; import org.hl7.fhir.r4.model.Patient; import org.junit.jupiter.api.AfterAll; -import org.junit.jupiter.api.BeforeAll; -import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; - -import javax.servlet.http.HttpServletRequest; +import org.junit.jupiter.api.extension.RegisterExtension; import static org.junit.jupiter.api.Assertions.assertEquals; public class CompressOutgoingContentInterceptorTest { - private static IGenericClient ourClient; - private static FhirContext ourCtx = FhirContext.forR4(); + private static final FhirContext ourCtx = FhirContext.forR4Cached(); private static Patient ourLastPatient; private static String ourLastReq; private static String ourLastResponseEncoding; - private static int ourPort; - private static Server ourServer; - @BeforeEach - public void before() { - ourClient = ourCtx.newRestfulGenericClient("http://localhost:" + ourPort); - } + @RegisterExtension + public RestfulServerExtension ourServer = new RestfulServerExtension(ourCtx) + .registerProvider(new DummyPatientResourceProvider()) + .withPagingProvider(new FifoMemoryPagingProvider(10)) + .setDefaultResponseEncoding(EncodingEnum.XML); + @RegisterExtension + private HttpClientExtension ourClient = new HttpClientExtension(); @Test public void testCreate() { - ourClient.registerInterceptor(new GZipContentInterceptor()); + IGenericClient client = ourServer.getFhirClient(); + client.registerInterceptor(new GZipContentInterceptor()); Patient p = new Patient(); p.addName().setFamily("FAMILY"); - ourClient.create().resource(p).execute(); + client.create().resource(p).execute(); assertEquals("FAMILY", p.getName().get(0).getFamily()); assertEquals("gzip", ourLastReq); @@ -59,28 +56,9 @@ public class CompressOutgoingContentInterceptorTest { @AfterAll public static void afterClassClearContext() throws Exception { - JettyUtil.closeServer(ourServer); TestUtil.randomizeLocaleAndTimezone(); } - @BeforeAll - public static void beforeClass() throws Exception { - ourServer = new Server(0); - - DummyPatientResourceProvider patientProvider = new DummyPatientResourceProvider(); - - ServletHandler proxyHandler = new ServletHandler(); - RestfulServer servlet = new RestfulServer(ourCtx); - servlet.setPagingProvider(new FifoMemoryPagingProvider(10)); - - servlet.setResourceProviders(patientProvider); - ServletHolder servletHolder = new ServletHolder(servlet); - proxyHandler.addServletWithMapping(servletHolder, "/*"); - ourServer.setHandler(proxyHandler); - JettyUtil.startServer(ourServer); - ourPort = JettyUtil.getPortForStartedServer(ourServer); - } - public static class DummyPatientResourceProvider implements IResourceProvider { @Create diff --git a/hapi-fhir-structures-r4/src/test/java/ca/uhn/fhir/rest/client/HttpProxyTest.java b/hapi-fhir-structures-r4/src/test/java/ca/uhn/fhir/rest/client/HttpProxyTest.java index e04b623afe2..9231bbe7184 100644 --- a/hapi-fhir-structures-r4/src/test/java/ca/uhn/fhir/rest/client/HttpProxyTest.java +++ b/hapi-fhir-structures-r4/src/test/java/ca/uhn/fhir/rest/client/HttpProxyTest.java @@ -11,17 +11,17 @@ import ca.uhn.fhir.util.TestUtil; import org.apache.commons.collections.EnumerationUtils; import org.eclipse.jetty.server.Server; import org.eclipse.jetty.server.handler.ContextHandlerCollection; -import org.eclipse.jetty.servlet.ServletContextHandler; -import org.eclipse.jetty.servlet.ServletHolder; +import org.eclipse.jetty.ee10.servlet.ServletContextHandler; +import org.eclipse.jetty.ee10.servlet.ServletHolder; import org.hl7.fhir.r4.model.IdType; import org.hl7.fhir.r4.model.Patient; import org.junit.jupiter.api.AfterAll; import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.Test; -import javax.servlet.ServletException; -import javax.servlet.http.HttpServletRequest; -import javax.servlet.http.HttpServletResponse; +import jakarta.servlet.ServletException; +import jakarta.servlet.http.HttpServletRequest; +import jakarta.servlet.http.HttpServletResponse; import java.io.IOException; import static org.junit.jupiter.api.Assertions.assertEquals; diff --git a/hapi-fhir-structures-r4/src/test/java/ca/uhn/fhir/rest/client/LoggingInterceptorTest.java b/hapi-fhir-structures-r4/src/test/java/ca/uhn/fhir/rest/client/LoggingInterceptorTest.java index 2f4109fb05e..93c67d6a6f9 100644 --- a/hapi-fhir-structures-r4/src/test/java/ca/uhn/fhir/rest/client/LoggingInterceptorTest.java +++ b/hapi-fhir-structures-r4/src/test/java/ca/uhn/fhir/rest/client/LoggingInterceptorTest.java @@ -5,11 +5,16 @@ import ca.uhn.fhir.rest.annotation.IdParam; import ca.uhn.fhir.rest.annotation.Read; import ca.uhn.fhir.rest.api.EncodingEnum; import ca.uhn.fhir.rest.client.api.IGenericClient; +import ca.uhn.fhir.rest.client.api.IRestfulClientFactory; import ca.uhn.fhir.rest.client.api.ServerValidationModeEnum; import ca.uhn.fhir.rest.client.interceptor.LoggingInterceptor; +import ca.uhn.fhir.rest.server.FifoMemoryPagingProvider; import ca.uhn.fhir.rest.server.IResourceProvider; import ca.uhn.fhir.rest.server.RestfulServer; +import ca.uhn.fhir.rest.server.interceptor.ExceptionInterceptorMethodTest; +import ca.uhn.fhir.test.utilities.HttpClientExtension; import ca.uhn.fhir.test.utilities.JettyUtil; +import ca.uhn.fhir.test.utilities.server.RestfulServerExtension; import ca.uhn.fhir.util.TestUtil; import ch.qos.logback.classic.Logger; import ch.qos.logback.classic.LoggerContext; @@ -17,8 +22,8 @@ import ch.qos.logback.classic.joran.JoranConfigurator; import ch.qos.logback.classic.spi.ILoggingEvent; import ch.qos.logback.core.Appender; import org.eclipse.jetty.server.Server; -import org.eclipse.jetty.servlet.ServletHandler; -import org.eclipse.jetty.servlet.ServletHolder; +import org.eclipse.jetty.ee10.servlet.ServletHandler; +import org.eclipse.jetty.ee10.servlet.ServletHolder; import org.hl7.fhir.r4.model.IdType; import org.hl7.fhir.r4.model.Patient; import org.junit.jupiter.api.AfterAll; @@ -26,6 +31,7 @@ import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.RegisterExtension; import org.mockito.ArgumentMatcher; import org.slf4j.LoggerFactory; @@ -42,11 +48,18 @@ import static org.mockito.Mockito.when; public class LoggingInterceptorTest { private static final FhirContext ourCtx = FhirContext.forR4Cached(); - private static int ourPort; - private static Server ourServer; private Logger myLoggerRoot; private Appender myMockAppender; + @RegisterExtension + public RestfulServerExtension ourServer = new RestfulServerExtension(ourCtx) + .registerProvider(new DummyProvider()) + .withPagingProvider(new FifoMemoryPagingProvider(100)) + .setDefaultResponseEncoding(EncodingEnum.XML); + + @RegisterExtension + private HttpClientExtension ourClient = new HttpClientExtension(); + @AfterEach public void after() { myLoggerRoot.detachAppender(myMockAppender); @@ -71,7 +84,7 @@ public class LoggingInterceptorTest { @Test public void testLoggerNonVerbose() { System.out.println("Starting testLogger"); - IGenericClient client = ourCtx.newRestfulGenericClient("http://localhost:" + ourPort); + IGenericClient client = ourCtx.newRestfulGenericClient(ourServer.getBaseUrl()); ourCtx.getRestfulClientFactory().setServerValidationMode(ServerValidationModeEnum.NEVER); LoggingInterceptor interceptor = new LoggingInterceptor(false); @@ -87,7 +100,7 @@ public class LoggingInterceptorTest { System.out.println("** Got Message: " + formattedMessage); System.out.flush(); return - formattedMessage.contains("Client request: GET http://localhost:" + ourPort + "/Patient/1 HTTP/1.1") || + formattedMessage.contains("Client request: GET " + ourServer.getBaseUrl() + "/Patient/1 HTTP/1.1") || formattedMessage.contains("Client response: HTTP 200 OK (Patient/1/_history/1) in "); } })); @@ -96,7 +109,7 @@ public class LoggingInterceptorTest { @Test public void testLoggerVerbose() { System.out.println("Starting testLogger"); - IGenericClient client = ourCtx.newRestfulGenericClient("http://localhost:" + ourPort); + IGenericClient client = ourCtx.newRestfulGenericClient(ourServer.getBaseUrl()); ourCtx.getRestfulClientFactory().setServerValidationMode(ServerValidationModeEnum.NEVER); LoggingInterceptor interceptor = new LoggingInterceptor(true); @@ -132,7 +145,7 @@ public class LoggingInterceptorTest { @AfterAll public static void afterClassClearContext() throws Exception { - JettyUtil.closeServer(ourServer); + ourCtx.getRestfulClientFactory().setServerValidationMode(IRestfulClientFactory.DEFAULT_SERVER_VALIDATION_MODE); TestUtil.randomizeLocaleAndTimezone(); } @@ -147,20 +160,6 @@ public class LoggingInterceptorTest { context.reset(); configurator.doConfigure(conf); - ourServer = new Server(0); - - DummyProvider patientProvider = new DummyProvider(); - - ServletHandler proxyHandler = new ServletHandler(); - RestfulServer servlet = new RestfulServer(ourCtx); - servlet.setDefaultResponseEncoding(EncodingEnum.XML); - servlet.setResourceProviders(patientProvider); - ServletHolder servletHolder = new ServletHolder(servlet); - proxyHandler.addServletWithMapping(servletHolder, "/*"); - ourServer.setHandler(proxyHandler); - JettyUtil.startServer(ourServer); - ourPort = JettyUtil.getPortForStartedServer(ourServer); - ourCtx.getRestfulClientFactory().setServerValidationMode(ServerValidationModeEnum.NEVER); } diff --git a/hapi-fhir-structures-r4/src/test/java/ca/uhn/fhir/rest/param/DateRangeParamR4Test.java b/hapi-fhir-structures-r4/src/test/java/ca/uhn/fhir/rest/param/DateRangeParamR4Test.java index aedeb3c7f84..2a6b5016d3e 100644 --- a/hapi-fhir-structures-r4/src/test/java/ca/uhn/fhir/rest/param/DateRangeParamR4Test.java +++ b/hapi-fhir-structures-r4/src/test/java/ca/uhn/fhir/rest/param/DateRangeParamR4Test.java @@ -4,32 +4,26 @@ import ca.uhn.fhir.context.FhirContext; import ca.uhn.fhir.model.api.TemporalPrecisionEnum; import ca.uhn.fhir.model.primitive.DateTimeDt; import ca.uhn.fhir.model.primitive.InstantDt; -import ca.uhn.fhir.narrative.DefaultThymeleafNarrativeGenerator; import ca.uhn.fhir.rest.annotation.RequiredParam; import ca.uhn.fhir.rest.annotation.Search; +import ca.uhn.fhir.rest.api.EncodingEnum; import ca.uhn.fhir.rest.api.QualifiedParamList; +import ca.uhn.fhir.rest.server.FifoMemoryPagingProvider; import ca.uhn.fhir.rest.server.IResourceProvider; -import ca.uhn.fhir.rest.server.RestfulServer; import ca.uhn.fhir.rest.server.exceptions.InvalidRequestException; -import ca.uhn.fhir.test.utilities.JettyUtil; +import ca.uhn.fhir.test.utilities.HttpClientExtension; +import ca.uhn.fhir.test.utilities.server.RestfulServerExtension; import ca.uhn.fhir.util.TestUtil; import com.google.common.base.Charsets; import org.apache.commons.io.IOUtils; import org.apache.commons.lang3.time.DateUtils; import org.apache.http.client.methods.CloseableHttpResponse; import org.apache.http.client.methods.HttpGet; -import org.apache.http.impl.client.CloseableHttpClient; -import org.apache.http.impl.client.HttpClientBuilder; -import org.apache.http.impl.conn.PoolingHttpClientConnectionManager; -import org.apache.jena.base.Sys; -import org.eclipse.jetty.server.Server; -import org.eclipse.jetty.servlet.ServletHandler; -import org.eclipse.jetty.servlet.ServletHolder; import org.hl7.fhir.r4.model.Patient; import org.junit.jupiter.api.AfterAll; -import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.RegisterExtension; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -41,7 +35,6 @@ import java.util.ArrayList; import java.util.Date; import java.util.List; import java.util.TimeZone; -import java.util.concurrent.TimeUnit; import static ca.uhn.fhir.rest.param.ParamPrefixEnum.EQUAL; import static ca.uhn.fhir.rest.param.ParamPrefixEnum.GREATERTHAN_OR_EQUALS; @@ -59,12 +52,8 @@ public class DateRangeParamR4Test { private static final SimpleDateFormat ourFmtLowerForTime; private static final SimpleDateFormat ourFmtUpperForTime; private static final Logger ourLog = LoggerFactory.getLogger(DateRangeParamR4Test.class); - private static CloseableHttpClient ourClient; - private static FhirContext ourCtx = FhirContext.forR4Cached(); + private static final FhirContext ourCtx = FhirContext.forR4Cached(); private static DateRangeParam ourLastDateRange; - private static int ourPort; - private static Server ourServer; - private static String ourBaseUrl; static { ourFmtLower = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SSSS"); @@ -77,6 +66,17 @@ public class DateRangeParamR4Test { ourFmtUpperForTime = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SSSS"); } + @RegisterExtension + private RestfulServerExtension ourServer = new RestfulServerExtension(ourCtx) + .setDefaultResponseEncoding(EncodingEnum.XML) + .registerProvider(new DummyPatientResourceProvider()) + .withPagingProvider(new FifoMemoryPagingProvider(100)) + .setDefaultPrettyPrint(false); + + @RegisterExtension + private HttpClientExtension ourClient = new HttpClientExtension(); + + @BeforeEach public void before() { ourLastDateRange = null; @@ -84,7 +84,7 @@ public class DateRangeParamR4Test { @Test public void testSearchForMultipleUnqualifiedDate() throws Exception { - String baseUrl = "http://localhost:" + ourPort + "/Patient?" + Patient.SP_BIRTHDATE + "="; + String baseUrl = ourServer.getBaseUrl() + "/Patient?" + Patient.SP_BIRTHDATE + "="; HttpGet httpGet = new HttpGet(baseUrl + "2012-01-01&" + Patient.SP_BIRTHDATE + "=2012-02-03"); CloseableHttpResponse status = ourClient.execute(httpGet); consumeResponse(status); @@ -102,7 +102,7 @@ public class DateRangeParamR4Test { @Test public void testSearchForOneUnqualifiedDate() throws Exception { - HttpGet httpGet = new HttpGet(ourBaseUrl + "?birthdate=2012-01-01"); + HttpGet httpGet = new HttpGet(ourServer.getBaseUrl() + "/Patient?birthdate=2012-01-01"); CloseableHttpResponse status = ourClient.execute(httpGet); consumeResponse(status); assertEquals(200, status.getStatusLine().getStatusCode()); @@ -118,7 +118,7 @@ public class DateRangeParamR4Test { @Test public void testSearchForOneQualifiedDateEq() throws Exception { - HttpGet httpGet = new HttpGet(ourBaseUrl + "?birthdate=eq2012-01-01"); + HttpGet httpGet = new HttpGet(ourServer.getBaseUrl() + "/Patient?birthdate=eq2012-01-01"); CloseableHttpResponse status = ourClient.execute(httpGet); consumeResponse(status); assertEquals(200, status.getStatusLine().getStatusCode()); @@ -134,7 +134,7 @@ public class DateRangeParamR4Test { @Test public void testSearchForOneQualifiedDateGt() throws Exception { - HttpGet httpGet = new HttpGet(ourBaseUrl + "?birthdate=gt2012-01-01"); + HttpGet httpGet = new HttpGet(ourServer.getBaseUrl() + "/Patient?birthdate=gt2012-01-01"); CloseableHttpResponse status = ourClient.execute(httpGet); consumeResponse(status); assertEquals(200, status.getStatusLine().getStatusCode()); @@ -150,7 +150,7 @@ public class DateRangeParamR4Test { @Test public void testSearchForOneQualifiedDateLt() throws Exception { - HttpGet httpGet = new HttpGet(ourBaseUrl + "?birthdate=lt2012-01-01"); + HttpGet httpGet = new HttpGet(ourServer.getBaseUrl() + "/Patient?birthdate=lt2012-01-01"); CloseableHttpResponse status = ourClient.execute(httpGet); consumeResponse(status); assertEquals(200, status.getStatusLine().getStatusCode()); @@ -166,7 +166,7 @@ public class DateRangeParamR4Test { @Test public void testSearchForOneQualifiedDateGe() throws Exception { - HttpGet httpGet = new HttpGet(ourBaseUrl + "?birthdate=ge2012-01-01"); + HttpGet httpGet = new HttpGet(ourServer.getBaseUrl() + "/Patient?birthdate=ge2012-01-01"); CloseableHttpResponse status = ourClient.execute(httpGet); consumeResponse(status); assertEquals(200, status.getStatusLine().getStatusCode()); @@ -182,7 +182,7 @@ public class DateRangeParamR4Test { @Test public void testSearchForOneQualifiedDateLe() throws Exception { - HttpGet httpGet = new HttpGet(ourBaseUrl + "?birthdate=le2012-01-01"); + HttpGet httpGet = new HttpGet(ourServer.getBaseUrl() + "/Patient?birthdate=le2012-01-01"); CloseableHttpResponse status = ourClient.execute(httpGet); consumeResponse(status); assertEquals(200, status.getStatusLine().getStatusCode()); @@ -198,7 +198,7 @@ public class DateRangeParamR4Test { @Test public void testSearchForOneQualifiedDateNe() throws Exception { - HttpGet httpGet = new HttpGet(ourBaseUrl + "?birthdate=ne2012-01-01"); + HttpGet httpGet = new HttpGet(ourServer.getBaseUrl() + "/Patient?birthdate=ne2012-01-01"); CloseableHttpResponse status = ourClient.execute(httpGet); consumeResponse(status); assertEquals(200, status.getStatusLine().getStatusCode()); @@ -212,7 +212,7 @@ public class DateRangeParamR4Test { @Test public void testRangeWithDatePrecision() throws Exception { - HttpGet httpGet = new HttpGet(ourBaseUrl + "?birthdate=gt2012-01-01&birthdate=lt2012-01-03"); + HttpGet httpGet = new HttpGet(ourServer.getBaseUrl() + "/Patient?birthdate=gt2012-01-01&birthdate=lt2012-01-03"); CloseableHttpResponse status = ourClient.execute(httpGet); consumeResponse(status); assertEquals(200, status.getStatusLine().getStatusCode()); @@ -412,7 +412,7 @@ public class DateRangeParamR4Test { assertEquals(new DateRangeParam(null, upperBound), new DateRangeParam(null, new DateParam(LESSTHAN_OR_EQUALS, upperBound))); } - public static class DummyPatientResourceProvider implements IResourceProvider { + private static class DummyPatientResourceProvider implements IResourceProvider { @Override public Class getResourceType() { @@ -466,33 +466,7 @@ public class DateRangeParamR4Test { @AfterAll public static void afterClassClearContext() throws Exception { - JettyUtil.closeServer(ourServer); TestUtil.randomizeLocaleAndTimezone(); } - @BeforeAll - public static void beforeClass() throws Exception { - ourServer = new Server(0); - - DummyPatientResourceProvider patientProvider = new DummyPatientResourceProvider(); - - ServletHandler proxyHandler = new ServletHandler(); - RestfulServer servlet = new RestfulServer(ourCtx); - servlet.getFhirContext().setNarrativeGenerator(new DefaultThymeleafNarrativeGenerator()); - - servlet.setResourceProviders(patientProvider); - ServletHolder servletHolder = new ServletHolder(servlet); - proxyHandler.addServletWithMapping(servletHolder, "/*"); - ourServer.setHandler(proxyHandler); - JettyUtil.startServer(ourServer); - ourPort = JettyUtil.getPortForStartedServer(ourServer); - - PoolingHttpClientConnectionManager connectionManager = new PoolingHttpClientConnectionManager(5000, TimeUnit.MILLISECONDS); - HttpClientBuilder builder = HttpClientBuilder.create(); - builder.setConnectionManager(connectionManager); - ourClient = builder.build(); - - ourBaseUrl = "http://localhost:" + ourPort + "/Patient"; - } - } diff --git a/hapi-fhir-structures-r4/src/test/java/ca/uhn/fhir/rest/server/BaseR4ServerTest.java b/hapi-fhir-structures-r4/src/test/java/ca/uhn/fhir/rest/server/BaseR4ServerTest.java index a1bc70a2618..3f73d339ed0 100644 --- a/hapi-fhir-structures-r4/src/test/java/ca/uhn/fhir/rest/server/BaseR4ServerTest.java +++ b/hapi-fhir-structures-r4/src/test/java/ca/uhn/fhir/rest/server/BaseR4ServerTest.java @@ -2,43 +2,42 @@ package ca.uhn.fhir.rest.server; import ca.uhn.fhir.context.FhirContext; import ca.uhn.fhir.context.api.BundleInclusionRule; +import ca.uhn.fhir.rest.annotation.Operation; import ca.uhn.fhir.rest.api.EncodingEnum; import ca.uhn.fhir.rest.client.api.IGenericClient; import ca.uhn.fhir.rest.client.api.ServerValidationModeEnum; +import ca.uhn.fhir.test.utilities.HttpClientExtension; import ca.uhn.fhir.test.utilities.JettyUtil; +import ca.uhn.fhir.test.utilities.server.RestfulServerExtension; import org.eclipse.jetty.server.Server; -import org.eclipse.jetty.servlet.ServletHandler; -import org.eclipse.jetty.servlet.ServletHolder; +import org.eclipse.jetty.ee10.servlet.ServletHandler; +import org.eclipse.jetty.ee10.servlet.ServletHolder; import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.extension.RegisterExtension; public class BaseR4ServerTest { - protected FhirContext myCtx = FhirContext.forR4Cached(); - private Server myServer; - protected IGenericClient myClient; - protected String myBaseUrl; + private static final FhirContext ourCtx = FhirContext.forR4Cached(); - @AfterEach - public void after() throws Exception { - JettyUtil.closeServer(myServer); - } + @RegisterExtension + public RestfulServerExtension ourServer = new RestfulServerExtension(ourCtx) + .registerProvider(new PlaceholderProvider()) + .withPagingProvider(new FifoMemoryPagingProvider(100)) + .setDefaultResponseEncoding(EncodingEnum.XML) + .withServer(s->s.setBundleInclusionRule(BundleInclusionRule.BASED_ON_RESOURCE_PRESENCE)); + + @RegisterExtension + public HttpClientExtension ourClient = new HttpClientExtension(); protected void startServer(Object theProvider) throws Exception { - RestfulServer servlet = new RestfulServer(myCtx); - servlet.registerProvider(theProvider); - ServletHandler proxyHandler = new ServletHandler(); - servlet.setDefaultResponseEncoding(EncodingEnum.XML); - servlet.setBundleInclusionRule(BundleInclusionRule.BASED_ON_RESOURCE_PRESENCE); - ServletHolder servletHolder = new ServletHolder(servlet); - proxyHandler.addServletWithMapping(servletHolder, "/*"); - - myServer = new Server(0); - myServer.setHandler(proxyHandler); - JettyUtil.startServer(myServer); - int port = JettyUtil.getPortForStartedServer(myServer); - - myBaseUrl = "http://localhost:" + port; - myCtx.getRestfulClientFactory().setServerValidationMode(ServerValidationModeEnum.NEVER); - myClient = myCtx.newRestfulGenericClient(myBaseUrl); + ourServer.getRestfulServer().registerProvider(theProvider); } + private static class PlaceholderProvider { + + @Operation(name = "placeholderOperation") + public void placeholderOperation() { + // nothing + } + + } } diff --git a/hapi-fhir-structures-r4/src/test/java/ca/uhn/fhir/rest/server/BinaryServerR4Test.java b/hapi-fhir-structures-r4/src/test/java/ca/uhn/fhir/rest/server/BinaryServerR4Test.java index c507eb82aca..13f98b36727 100644 --- a/hapi-fhir-structures-r4/src/test/java/ca/uhn/fhir/rest/server/BinaryServerR4Test.java +++ b/hapi-fhir-structures-r4/src/test/java/ca/uhn/fhir/rest/server/BinaryServerR4Test.java @@ -7,8 +7,10 @@ import ca.uhn.fhir.rest.annotation.Read; import ca.uhn.fhir.rest.annotation.ResourceParam; import ca.uhn.fhir.rest.annotation.Update; import ca.uhn.fhir.rest.api.Constants; +import ca.uhn.fhir.rest.api.EncodingEnum; import ca.uhn.fhir.rest.api.MethodOutcome; -import ca.uhn.fhir.test.utilities.JettyUtil; +import ca.uhn.fhir.test.utilities.HttpClientExtension; +import ca.uhn.fhir.test.utilities.server.RestfulServerExtension; import ca.uhn.fhir.util.TestUtil; import com.google.common.base.Charsets; import org.apache.commons.io.IOUtils; @@ -18,39 +20,38 @@ import org.apache.http.client.methods.HttpPost; import org.apache.http.client.methods.HttpPut; import org.apache.http.entity.ByteArrayEntity; import org.apache.http.entity.StringEntity; -import org.apache.http.impl.client.CloseableHttpClient; -import org.apache.http.impl.client.HttpClientBuilder; -import org.apache.http.impl.conn.PoolingHttpClientConnectionManager; -import org.eclipse.jetty.server.Server; -import org.eclipse.jetty.servlet.ServletHandler; -import org.eclipse.jetty.servlet.ServletHolder; import org.hl7.fhir.instance.model.api.IBaseResource; import org.hl7.fhir.r4.model.Binary; import org.hl7.fhir.r4.model.IdType; import org.hl7.fhir.r4.model.Patient; import org.hl7.fhir.r4.model.Reference; import org.junit.jupiter.api.AfterAll; -import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; - -import java.util.concurrent.TimeUnit; +import org.junit.jupiter.api.extension.RegisterExtension; import static org.junit.jupiter.api.Assertions.assertArrayEquals; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertNull; -public class BinaryServerR4Test { - private static CloseableHttpClient ourClient; - private static FhirContext ourCtx = FhirContext.forR4(); +public class BinaryServerR4Test { + private static final FhirContext ourCtx = FhirContext.forR4Cached(); private static Binary ourLastBinary; private static byte[] ourLastBinaryBytes; private static String ourLastBinaryString; - private static int ourPort; - private static Server ourServer; private static IdType ourLastId; private static Binary ourNextBinary; + @RegisterExtension + public static final RestfulServerExtension ourServer = new RestfulServerExtension(ourCtx) + .registerProvider(new BinaryProvider()) + .withPagingProvider(new FifoMemoryPagingProvider(100)) + .setDefaultResponseEncoding(EncodingEnum.XML) + .setDefaultPrettyPrint(false); + + @RegisterExtension + public static final HttpClientExtension ourClient = new HttpClientExtension(); + @BeforeEach public void before() { ourLastBinary = null; @@ -69,7 +70,7 @@ public class BinaryServerR4Test { ourNextBinary.setSecurityContext(new Reference("Patient/1")); ourNextBinary.setContentType("application/foo"); - HttpGet get = new HttpGet("http://localhost:" + ourPort + "/Binary/A"); + HttpGet get = new HttpGet(ourServer.getBaseUrl() + "/Binary/A"); get.addHeader("Content-Type", "application/foo"); CloseableHttpResponse status = ourClient.execute(get); try { @@ -77,7 +78,7 @@ public class BinaryServerR4Test { assertEquals("application/foo", status.getEntity().getContentType().getValue()); assertEquals("Patient/1", status.getFirstHeader(Constants.HEADER_X_SECURITY_CONTEXT).getValue()); assertEquals("W/\"222\"", status.getFirstHeader(Constants.HEADER_ETAG).getValue()); - assertEquals("http://localhost:" + ourPort + "/Binary/A/_history/222", status.getFirstHeader(Constants.HEADER_CONTENT_LOCATION).getValue()); + assertEquals(ourServer.getBaseUrl() + "/Binary/A/_history/222", status.getFirstHeader(Constants.HEADER_CONTENT_LOCATION).getValue()); assertEquals(null, status.getFirstHeader(Constants.HEADER_LOCATION)); byte[] content = IOUtils.toByteArray(status.getEntity().getContent()); @@ -97,7 +98,7 @@ public class BinaryServerR4Test { ourNextBinary.setSecurityContext(new Reference("Patient/1")); ourNextBinary.setContentType("application/foo"); - HttpGet get = new HttpGet("http://localhost:" + ourPort + "/Binary/A"); + HttpGet get = new HttpGet(ourServer.getBaseUrl() + "/Binary/A"); get.addHeader("Content-Type", "application/foo"); get.addHeader("Accept", Constants.CT_FHIR_JSON); CloseableHttpResponse status = ourClient.execute(get); @@ -106,7 +107,7 @@ public class BinaryServerR4Test { assertEquals("application/json+fhir;charset=utf-8", status.getEntity().getContentType().getValue()); assertEquals("Patient/1", status.getFirstHeader(Constants.HEADER_X_SECURITY_CONTEXT).getValue()); assertEquals("W/\"222\"", status.getFirstHeader(Constants.HEADER_ETAG).getValue()); - assertEquals("http://localhost:" + ourPort + "/Binary/A/_history/222", status.getFirstHeader(Constants.HEADER_CONTENT_LOCATION).getValue()); + assertEquals(ourServer.getBaseUrl() + "/Binary/A/_history/222", status.getFirstHeader(Constants.HEADER_CONTENT_LOCATION).getValue()); assertEquals(null, status.getFirstHeader(Constants.HEADER_LOCATION)); String content = IOUtils.toString(status.getEntity().getContent(), Charsets.UTF_8); @@ -118,7 +119,7 @@ public class BinaryServerR4Test { @Test public void testPostBinaryWithSecurityContext() throws Exception { - HttpPost post = new HttpPost("http://localhost:" + ourPort + "/Binary"); + HttpPost post = new HttpPost(ourServer.getBaseUrl() + "/Binary"); post.setEntity(new ByteArrayEntity(new byte[]{0, 1, 2, 3, 4})); post.addHeader("Content-Type", "application/foo"); post.addHeader(Constants.HEADER_X_SECURITY_CONTEXT, "Encounter/2"); @@ -136,7 +137,7 @@ public class BinaryServerR4Test { @Test public void testPostRawBytesBinaryContentType() throws Exception { - HttpPost post = new HttpPost("http://localhost:" + ourPort + "/Binary"); + HttpPost post = new HttpPost(ourServer.getBaseUrl() + "/Binary"); post.setEntity(new ByteArrayEntity(new byte[]{0, 1, 2, 3, 4})); post.addHeader("Content-Type", "application/foo"); CloseableHttpResponse status = ourClient.execute(post); @@ -161,7 +162,7 @@ public class BinaryServerR4Test { b.setContent(new byte[]{0, 1, 2, 3, 4}); String encoded = ourCtx.newJsonParser().encodeResourceToString(b); - HttpPost post = new HttpPost("http://localhost:" + ourPort + "/Binary"); + HttpPost post = new HttpPost(ourServer.getBaseUrl() + "/Binary"); post.setEntity(new StringEntity(encoded)); post.addHeader("Content-Type", Constants.CT_FHIR_JSON); CloseableHttpResponse status = ourClient.execute(post); @@ -184,7 +185,7 @@ public class BinaryServerR4Test { b.setContent(ourCtx.newXmlParser().encodeResourceToString(p).getBytes("UTF-8")); String encoded = ourCtx.newJsonParser().encodeResourceToString(b); - HttpPost post = new HttpPost("http://localhost:" + ourPort + "/Binary"); + HttpPost post = new HttpPost(ourServer.getBaseUrl() + "/Binary"); post.setEntity(new StringEntity(encoded)); post.addHeader("Content-Type", Constants.CT_FHIR_JSON); CloseableHttpResponse status = ourClient.execute(post); @@ -200,7 +201,7 @@ public class BinaryServerR4Test { @Test public void testPostRawBytesNoContentType() throws Exception { - HttpPost post = new HttpPost("http://localhost:" + ourPort + "/Binary"); + HttpPost post = new HttpPost(ourServer.getBaseUrl() + "/Binary"); post.setEntity(new ByteArrayEntity(new byte[]{0, 1, 2, 3, 4})); CloseableHttpResponse status = ourClient.execute(post); try { @@ -213,7 +214,7 @@ public class BinaryServerR4Test { @Test public void testPutBinaryWithSecurityContext() throws Exception { - HttpPut post = new HttpPut("http://localhost:" + ourPort + "/Binary/A"); + HttpPut post = new HttpPut(ourServer.getBaseUrl() + "/Binary/A"); post.setEntity(new ByteArrayEntity(new byte[]{0, 1, 2, 3, 4})); post.addHeader("Content-Type", "application/foo"); post.addHeader(Constants.HEADER_X_SECURITY_CONTEXT, "Encounter/2"); @@ -232,31 +233,9 @@ public class BinaryServerR4Test { @AfterAll public static void afterClassClearContext() throws Exception { - JettyUtil.closeServer(ourServer); TestUtil.randomizeLocaleAndTimezone(); } - @BeforeAll - public static void beforeClass() throws Exception { - ourServer = new Server(0); - - BinaryProvider binaryProvider = new BinaryProvider(); - - ServletHandler proxyHandler = new ServletHandler(); - RestfulServer servlet = new RestfulServer(ourCtx); - servlet.setResourceProviders(binaryProvider); - ServletHolder servletHolder = new ServletHolder(servlet); - proxyHandler.addServletWithMapping(servletHolder, "/*"); - ourServer.setHandler(proxyHandler); - JettyUtil.startServer(ourServer); - ourPort = JettyUtil.getPortForStartedServer(ourServer); - - PoolingHttpClientConnectionManager connectionManager = new PoolingHttpClientConnectionManager(5000, TimeUnit.MILLISECONDS); - HttpClientBuilder builder = HttpClientBuilder.create(); - builder.setConnectionManager(connectionManager); - ourClient = builder.build(); - } - public static class BinaryProvider implements IResourceProvider { @Create() public MethodOutcome createBinary(@ResourceParam Binary theBinary, @ResourceParam String theBinaryString, @ResourceParam byte[] theBinaryBytes) { diff --git a/hapi-fhir-structures-r4/src/test/java/ca/uhn/fhir/rest/server/CapabilityStatementCacheR4Test.java b/hapi-fhir-structures-r4/src/test/java/ca/uhn/fhir/rest/server/CapabilityStatementCacheR4Test.java index d10efd46300..0e82c283a81 100644 --- a/hapi-fhir-structures-r4/src/test/java/ca/uhn/fhir/rest/server/CapabilityStatementCacheR4Test.java +++ b/hapi-fhir-structures-r4/src/test/java/ca/uhn/fhir/rest/server/CapabilityStatementCacheR4Test.java @@ -13,7 +13,7 @@ import org.hl7.fhir.r4.model.Patient; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.RegisterExtension; -import javax.servlet.http.HttpServletRequest; +import jakarta.servlet.http.HttpServletRequest; import java.util.List; import java.util.stream.Collectors; diff --git a/hapi-fhir-structures-r4/src/test/java/ca/uhn/fhir/rest/server/CreateR4Test.java b/hapi-fhir-structures-r4/src/test/java/ca/uhn/fhir/rest/server/CreateR4Test.java index d9a7957056c..9cf95713f2d 100644 --- a/hapi-fhir-structures-r4/src/test/java/ca/uhn/fhir/rest/server/CreateR4Test.java +++ b/hapi-fhir-structures-r4/src/test/java/ca/uhn/fhir/rest/server/CreateR4Test.java @@ -8,10 +8,13 @@ import ca.uhn.fhir.rest.annotation.Read; import ca.uhn.fhir.rest.annotation.ResourceParam; import ca.uhn.fhir.rest.annotation.Search; import ca.uhn.fhir.rest.api.Constants; +import ca.uhn.fhir.rest.api.EncodingEnum; import ca.uhn.fhir.rest.api.MethodOutcome; import ca.uhn.fhir.rest.api.PreferReturnEnum; import ca.uhn.fhir.rest.client.MyPatientWithExtensions; +import ca.uhn.fhir.test.utilities.HttpClientExtension; import ca.uhn.fhir.test.utilities.JettyUtil; +import ca.uhn.fhir.test.utilities.server.RestfulServerExtension; import ca.uhn.fhir.util.TestUtil; import org.apache.commons.io.IOUtils; import org.apache.http.HttpResponse; @@ -24,8 +27,8 @@ import org.apache.http.impl.client.CloseableHttpClient; import org.apache.http.impl.client.HttpClientBuilder; import org.apache.http.impl.conn.PoolingHttpClientConnectionManager; import org.eclipse.jetty.server.Server; -import org.eclipse.jetty.servlet.ServletHandler; -import org.eclipse.jetty.servlet.ServletHolder; +import org.eclipse.jetty.ee10.servlet.ServletHandler; +import org.eclipse.jetty.ee10.servlet.ServletHolder; import org.hl7.fhir.instance.model.api.IBaseResource; import org.hl7.fhir.r4.model.DateType; import org.hl7.fhir.r4.model.IdType; @@ -37,6 +40,7 @@ import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.RegisterExtension; import java.nio.charset.StandardCharsets; import java.util.ArrayList; @@ -54,17 +58,21 @@ import static org.junit.jupiter.api.Assertions.assertNull; public class CreateR4Test { private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(CreateR4Test.class); public static OperationOutcome ourReturnOo; - private static CloseableHttpClient ourClient; - private static FhirContext ourCtx = FhirContext.forR4(); - private static int ourPort; - private static Server ourServer; - private static RestfulServer ourServlet; + private static final FhirContext ourCtx = FhirContext.forR4Cached(); - @AfterEach - public void after() { - ourServlet.setDefaultPreferReturn(RestfulServer.DEFAULT_PREFER_RETURN); - } + @RegisterExtension + public RestfulServerExtension ourServer = new RestfulServerExtension(ourCtx) + .registerProvider(new PatientProviderCreate()) + .registerProvider(new PatientProviderRead()) + .registerProvider(new PatientProviderSearch()) + .withPagingProvider(new FifoMemoryPagingProvider(100)) + .setDefaultResponseEncoding(EncodingEnum.JSON) + .withServer(s->s.setDefaultPreferReturn(RestfulServer.DEFAULT_PREFER_RETURN)) + .setDefaultPrettyPrint(false); + @RegisterExtension + private HttpClientExtension ourClient = new HttpClientExtension(); + @BeforeEach public void before() { ourReturnOo = null; @@ -73,7 +81,7 @@ public class CreateR4Test { @Test public void testCreateIgnoresIdInResourceBody() throws Exception { - HttpPost httpPost = new HttpPost("http://localhost:" + ourPort + "/Patient"); + HttpPost httpPost = new HttpPost(ourServer.getBaseUrl() + "/Patient"); httpPost.setEntity(new StringEntity("{\"resourceType\":\"Patient\", \"id\":\"999\", \"status\":\"active\"}", ContentType.parse("application/fhir+json; charset=utf-8"))); HttpResponse status = ourClient.execute(httpPost); @@ -86,14 +94,14 @@ public class CreateR4Test { assertEquals(1, status.getHeaders("Location").length); assertEquals(1, status.getHeaders("Content-Location").length); - assertEquals("http://localhost:" + ourPort + "/Patient/1", status.getFirstHeader("Location").getValue()); + assertEquals(ourServer.getBaseUrl() + "/Patient/1", status.getFirstHeader("Location").getValue()); } @Test public void testCreateFailsIfNoContentTypeProvided() throws Exception { - HttpPost httpPost = new HttpPost("http://localhost:" + ourPort + "/Patient"); + HttpPost httpPost = new HttpPost(ourServer.getBaseUrl() + "/Patient"); httpPost.setEntity(new StringEntity("{\"resourceType\":\"Patient\", \"id\":\"999\", \"status\":\"active\"}", (ContentType) null)); try (CloseableHttpResponse status = ourClient.execute(httpPost)) { @@ -113,7 +121,7 @@ public class CreateR4Test { @Test public void testCreateReturnsLocationHeader() throws Exception { - HttpPost httpPost = new HttpPost("http://localhost:" + ourPort + "/Patient"); + HttpPost httpPost = new HttpPost(ourServer.getBaseUrl() + "/Patient"); httpPost.setEntity(new StringEntity("{\"resourceType\":\"Patient\", \"status\":\"active\"}", ContentType.parse("application/fhir+json; charset=utf-8"))); HttpResponse status = ourClient.execute(httpPost); @@ -126,7 +134,7 @@ public class CreateR4Test { assertEquals(1, status.getHeaders("Location").length); assertEquals(1, status.getHeaders("Content-Location").length); - assertEquals("http://localhost:" + ourPort + "/Patient/1", status.getFirstHeader("Location").getValue()); + assertEquals(ourServer.getBaseUrl() + "/Patient/1", status.getFirstHeader("Location").getValue()); } @@ -134,7 +142,7 @@ public class CreateR4Test { public void testCreateReturnsOperationOutcome() throws Exception { ourReturnOo = new OperationOutcome().addIssue(new OperationOutcomeIssueComponent().setDiagnostics("DIAG")); - HttpPost httpPost = new HttpPost("http://localhost:" + ourPort + "/Patient"); + HttpPost httpPost = new HttpPost(ourServer.getBaseUrl() + "/Patient"); httpPost.setEntity(new StringEntity("{\"resourceType\":\"Patient\", \"status\":\"active\"}", ContentType.parse("application/fhir+json; charset=utf-8"))); httpPost.addHeader(Constants.HEADER_PREFER, Constants.HEADER_PREFER_RETURN + "=" + Constants.HEADER_PREFER_RETURN_OPERATION_OUTCOME); HttpResponse status = ourClient.execute(httpPost); @@ -154,7 +162,7 @@ public class CreateR4Test { ourReturnOo = new OperationOutcome().addIssue(new OperationOutcomeIssueComponent().setDiagnostics("DIAG")); String expectedResponseContent = "{\"resourceType\":\"Patient\",\"id\":\"1\",\"meta\":{\"versionId\":\"1\"},\"gender\":\"male\"}"; - HttpPost httpPost = new HttpPost("http://localhost:" + ourPort + "/Patient"); + HttpPost httpPost = new HttpPost(ourServer.getBaseUrl() + "/Patient"); httpPost.setEntity(new StringEntity("{\"resourceType\":\"Patient\", \"gender\":\"male\"}", ContentType.parse("application/fhir+json; charset=utf-8"))); HttpResponse status = ourClient.execute(httpPost); @@ -170,7 +178,7 @@ public class CreateR4Test { @Test public void testCreateWithIncorrectContent1() throws Exception { - HttpPost httpPost = new HttpPost("http://localhost:" + ourPort + "/Patient"); + HttpPost httpPost = new HttpPost(ourServer.getBaseUrl() + "/Patient"); httpPost.setEntity(new StringEntity("{\"foo\":\"bar\"}", ContentType.parse("application/xml+fhir; charset=utf-8"))); HttpResponse status = ourClient.execute(httpPost); @@ -189,7 +197,7 @@ public class CreateR4Test { @Test public void testCreateWithIncorrectContent2() throws Exception { - HttpPost httpPost = new HttpPost("http://localhost:" + ourPort + "/Patient"); + HttpPost httpPost = new HttpPost(ourServer.getBaseUrl() + "/Patient"); httpPost.setEntity(new StringEntity("{\"foo\":\"bar\"}", ContentType.parse("application/fhir+xml; charset=utf-8"))); HttpResponse status = ourClient.execute(httpPost); @@ -208,7 +216,7 @@ public class CreateR4Test { @Test public void testCreateWithIncorrectContent3() throws Exception { - HttpPost httpPost = new HttpPost("http://localhost:" + ourPort + "/Patient"); + HttpPost httpPost = new HttpPost(ourServer.getBaseUrl() + "/Patient"); httpPost.setEntity(new StringEntity("{\"foo\":\"bar\"}", ContentType.parse("application/fhir+json; charset=utf-8"))); HttpResponse status = ourClient.execute(httpPost); @@ -229,7 +237,7 @@ public class CreateR4Test { @Test public void testCreateWithInvalidContent() throws Exception { - HttpPost httpPost = new HttpPost("http://localhost:" + ourPort + "/Patient"); + HttpPost httpPost = new HttpPost(ourServer.getBaseUrl() + "/Patient"); httpPost.setEntity(new StringEntity("FOO", ContentType.parse("application/xml+fhir; charset=utf-8"))); HttpResponse status = ourClient.execute(httpPost); @@ -256,7 +264,7 @@ public class CreateR4Test { String body = ourCtx.newJsonParser().encodeResourceToString(p); HttpPost httpPost; - httpPost = new HttpPost("http://localhost:" + ourPort + "/Patient"); + httpPost = new HttpPost(ourServer.getBaseUrl() + "/Patient"); httpPost.setEntity(new StringEntity(body, ContentType.parse("application/fhir+json; charset=utf-8"))); try (CloseableHttpResponse status = ourClient.execute(httpPost)) { @@ -279,9 +287,9 @@ public class CreateR4Test { p.setActive(true); String body = ourCtx.newJsonParser().encodeResourceToString(p); - HttpPost httpPost = new HttpPost("http://localhost:" + ourPort + "/Patient"); + HttpPost httpPost = new HttpPost(ourServer.getBaseUrl() + "/Patient"); httpPost.setEntity(new StringEntity(body, ContentType.parse("application/fhir+json; charset=utf-8"))); - ourServlet.setDefaultPreferReturn(PreferReturnEnum.OPERATION_OUTCOME); + ourServer.getRestfulServer().setDefaultPreferReturn(PreferReturnEnum.OPERATION_OUTCOME); try (CloseableHttpResponse status = ourClient.execute(httpPost)) { assertEquals(201, status.getStatusLine().getStatusCode()); @@ -304,9 +312,9 @@ public class CreateR4Test { p.setActive(true); String body = ourCtx.newJsonParser().encodeResourceToString(p); - HttpPost httpPost = new HttpPost("http://localhost:" + ourPort + "/Patient"); + HttpPost httpPost = new HttpPost(ourServer.getBaseUrl() + "/Patient"); httpPost.setEntity(new StringEntity(body, ContentType.parse("application/fhir+json; charset=utf-8"))); - ourServlet.setDefaultPreferReturn(PreferReturnEnum.MINIMAL); + ourServer.getRestfulServer().setDefaultPreferReturn(PreferReturnEnum.MINIMAL); try (CloseableHttpResponse status = ourClient.execute(httpPost)) { assertEquals(201, status.getStatusLine().getStatusCode()); @@ -321,7 +329,7 @@ public class CreateR4Test { @Test public void testSearch() throws Exception { - HttpGet httpGet = new HttpGet("http://localhost:" + ourPort + "/Patient?_format=xml&_pretty=true"); + HttpGet httpGet = new HttpGet(ourServer.getBaseUrl() + "/Patient?_format=xml&_pretty=true"); HttpResponse status = ourClient.execute(httpGet); String responseContent = IOUtils.toString(status.getEntity().getContent(), StandardCharsets.UTF_8); @@ -389,7 +397,7 @@ public class CreateR4Test { @Search public List search() { - ArrayList retVal = new ArrayList(); + ArrayList retVal = new ArrayList<>(); MyPatientWithExtensions p0 = new MyPatientWithExtensions(); p0.setId(new IdType("Patient/0")); @@ -408,33 +416,7 @@ public class CreateR4Test { @AfterAll public static void afterClassClearContext() throws Exception { - JettyUtil.closeServer(ourServer); TestUtil.randomizeLocaleAndTimezone(); } - @BeforeAll - public static void beforeClass() throws Exception { - ourServer = new Server(0); - - PatientProviderCreate patientProviderCreate = new PatientProviderCreate(); - PatientProviderRead patientProviderRead = new PatientProviderRead(); - PatientProviderSearch patientProviderSearch = new PatientProviderSearch(); - - ServletHandler proxyHandler = new ServletHandler(); - ourServlet = new RestfulServer(ourCtx); - - ourServlet.setResourceProviders(patientProviderCreate, patientProviderRead, patientProviderSearch); - ServletHolder servletHolder = new ServletHolder(ourServlet); - proxyHandler.addServletWithMapping(servletHolder, "/*"); - ourServer.setHandler(proxyHandler); - JettyUtil.startServer(ourServer); - ourPort = JettyUtil.getPortForStartedServer(ourServer); - - PoolingHttpClientConnectionManager connectionManager = new PoolingHttpClientConnectionManager(5000, TimeUnit.MILLISECONDS); - HttpClientBuilder builder = HttpClientBuilder.create(); - builder.setConnectionManager(connectionManager); - ourClient = builder.build(); - - } - } diff --git a/hapi-fhir-structures-r4/src/test/java/ca/uhn/fhir/rest/server/DeleteConditionalR4Test.java b/hapi-fhir-structures-r4/src/test/java/ca/uhn/fhir/rest/server/DeleteConditionalR4Test.java index edbbb0b25be..6c26e113fe2 100644 --- a/hapi-fhir-structures-r4/src/test/java/ca/uhn/fhir/rest/server/DeleteConditionalR4Test.java +++ b/hapi-fhir-structures-r4/src/test/java/ca/uhn/fhir/rest/server/DeleteConditionalR4Test.java @@ -4,17 +4,20 @@ import ca.uhn.fhir.context.FhirContext; import ca.uhn.fhir.rest.annotation.ConditionalUrlParam; import ca.uhn.fhir.rest.annotation.Delete; import ca.uhn.fhir.rest.annotation.IdParam; +import ca.uhn.fhir.rest.api.EncodingEnum; import ca.uhn.fhir.rest.api.MethodOutcome; import ca.uhn.fhir.rest.client.api.IGenericClient; import ca.uhn.fhir.rest.client.interceptor.LoggingInterceptor; +import ca.uhn.fhir.test.utilities.HttpClientExtension; import ca.uhn.fhir.test.utilities.JettyUtil; +import ca.uhn.fhir.test.utilities.server.RestfulServerExtension; import ca.uhn.fhir.util.TestUtil; import org.apache.http.impl.client.CloseableHttpClient; import org.apache.http.impl.client.HttpClientBuilder; import org.apache.http.impl.conn.PoolingHttpClientConnectionManager; import org.eclipse.jetty.server.Server; -import org.eclipse.jetty.servlet.ServletHandler; -import org.eclipse.jetty.servlet.ServletHolder; +import org.eclipse.jetty.ee10.servlet.ServletHandler; +import org.eclipse.jetty.ee10.servlet.ServletHolder; import org.hl7.fhir.instance.model.api.IBaseResource; import org.hl7.fhir.r4.model.IdType; import org.hl7.fhir.r4.model.Patient; @@ -22,6 +25,7 @@ import org.junit.jupiter.api.AfterAll; import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.RegisterExtension; import java.util.concurrent.TimeUnit; @@ -30,16 +34,21 @@ import static org.junit.jupiter.api.Assertions.assertTrue; public class DeleteConditionalR4Test { private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(DeleteConditionalR4Test.class); - private static CloseableHttpClient ourClient; - private static FhirContext ourCtx = FhirContext.forR4(); - private static IGenericClient ourHapiClient; + private static final FhirContext ourCtx = FhirContext.forR4Cached(); private static String ourLastConditionalUrl; private static IdType ourLastIdParam; private static boolean ourLastRequestWasDelete; - private static int ourPort; - private static Server ourServer; + @RegisterExtension + public RestfulServerExtension ourServer = new RestfulServerExtension(ourCtx) + .registerProvider(new PatientProvider()) + .withPagingProvider(new FifoMemoryPagingProvider(100)) + .setDefaultResponseEncoding(EncodingEnum.XML); + + @RegisterExtension + private HttpClientExtension ourClient = new HttpClientExtension(); + @BeforeEach public void before() { ourLastConditionalUrl = null; @@ -54,7 +63,8 @@ public class DeleteConditionalR4Test { Patient patient = new Patient(); patient.addIdentifier().setValue("002"); - ourHapiClient + ourServer + .getFhirClient() .delete() .resourceConditionalByType(Patient.class) .where(Patient.IDENTIFIER.exactly().systemAndIdentifier("SOMESYS", "SOMEID")).execute(); @@ -84,33 +94,7 @@ public class DeleteConditionalR4Test { @AfterAll public static void afterClassClearContext() throws Exception { - JettyUtil.closeServer(ourServer); TestUtil.randomizeLocaleAndTimezone(); } - @BeforeAll - public static void beforeClass() throws Exception { - ourServer = new Server(0); - - PatientProvider patientProvider = new PatientProvider(); - - ServletHandler proxyHandler = new ServletHandler(); - RestfulServer servlet = new RestfulServer(ourCtx); - servlet.setResourceProviders(patientProvider); - ServletHolder servletHolder = new ServletHolder(servlet); - proxyHandler.addServletWithMapping(servletHolder, "/*"); - ourServer.setHandler(proxyHandler); - JettyUtil.startServer(ourServer); - ourPort = JettyUtil.getPortForStartedServer(ourServer); - - PoolingHttpClientConnectionManager connectionManager = new PoolingHttpClientConnectionManager(5000, TimeUnit.MILLISECONDS); - HttpClientBuilder builder = HttpClientBuilder.create(); - builder.setConnectionManager(connectionManager); - ourClient = builder.build(); - - ourCtx.getRestfulClientFactory().setSocketTimeout(500 * 1000); - ourHapiClient = ourCtx.newRestfulGenericClient("http://localhost:" + ourPort + "/"); - ourHapiClient.registerInterceptor(new LoggingInterceptor()); - } - } diff --git a/hapi-fhir-structures-r4/src/test/java/ca/uhn/fhir/rest/server/ETagServerR4Test.java b/hapi-fhir-structures-r4/src/test/java/ca/uhn/fhir/rest/server/ETagServerR4Test.java index d4f2aea8004..c4c867f8f10 100644 --- a/hapi-fhir-structures-r4/src/test/java/ca/uhn/fhir/rest/server/ETagServerR4Test.java +++ b/hapi-fhir-structures-r4/src/test/java/ca/uhn/fhir/rest/server/ETagServerR4Test.java @@ -10,7 +10,9 @@ import ca.uhn.fhir.rest.api.Constants; import ca.uhn.fhir.rest.api.EncodingEnum; import ca.uhn.fhir.rest.api.MethodOutcome; import ca.uhn.fhir.rest.server.exceptions.PreconditionFailedException; -import ca.uhn.fhir.test.utilities.JettyUtil; +import ca.uhn.fhir.test.utilities.HttpClientExtension; +import ca.uhn.fhir.test.utilities.server.RestfulServerExtension; +import ca.uhn.fhir.util.TestUtil; import com.google.common.base.Charsets; import org.apache.commons.io.IOUtils; import org.apache.http.Header; @@ -20,39 +22,37 @@ import org.apache.http.client.methods.HttpGet; import org.apache.http.client.methods.HttpPut; import org.apache.http.entity.ContentType; import org.apache.http.entity.StringEntity; -import org.apache.http.impl.client.CloseableHttpClient; -import org.apache.http.impl.client.HttpClientBuilder; -import org.apache.http.impl.conn.PoolingHttpClientConnectionManager; -import org.eclipse.jetty.server.Server; -import org.eclipse.jetty.servlet.ServletHandler; -import org.eclipse.jetty.servlet.ServletHolder; import org.hl7.fhir.r4.model.IdType; import org.hl7.fhir.r4.model.Identifier; import org.hl7.fhir.r4.model.Patient; import org.junit.jupiter.api.AfterAll; -import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.RegisterExtension; import java.util.Date; -import java.util.concurrent.TimeUnit; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertNotNull; public class ETagServerR4Test { - private static CloseableHttpClient ourClient; - private static FhirContext ourCtx = FhirContext.forR4(); + private static final FhirContext ourCtx = FhirContext.forR4Cached(); private static Date ourLastModifiedDate; - private static int ourPort; - private static Server ourServer; - private static PoolingHttpClientConnectionManager ourConnectionManager; private static IdType ourLastId; private static boolean ourPutVersionInPatientId; private static boolean ourPutVersionInPatientMeta; - @BeforeEach + @RegisterExtension + public RestfulServerExtension ourServer = new RestfulServerExtension(ourCtx) + .registerProvider(new PatientProvider()) + .withPagingProvider(new FifoMemoryPagingProvider(100)) + .setDefaultResponseEncoding(EncodingEnum.XML); + + @RegisterExtension + private HttpClientExtension ourClient = new HttpClientExtension(); + + @BeforeEach public void before() { ourLastId = null; ourPutVersionInPatientId = true; @@ -74,7 +74,7 @@ public class ETagServerR4Test { private void doTestAutomaticNotModified() throws Exception { ourLastModifiedDate = new InstantDt("2012-11-25T02:34:45.2222Z").getValue(); - HttpGet httpGet = new HttpGet("http://localhost:" + ourPort + "/Patient/2"); + HttpGet httpGet = new HttpGet(ourServer.getBaseUrl() + "/Patient/2"); httpGet.addHeader(Constants.HEADER_IF_NONE_MATCH, "\"222\""); HttpResponse status = ourClient.execute(httpGet); assertEquals(Constants.STATUS_HTTP_304_NOT_MODIFIED, status.getStatusLine().getStatusCode()); @@ -84,7 +84,7 @@ public class ETagServerR4Test { public void testETagHeader() throws Exception { ourLastModifiedDate = new InstantDt("2012-11-25T02:34:45.2222Z").getValue(); - HttpGet httpGet = new HttpGet("http://localhost:" + ourPort + "/Patient/2/_history/3"); + HttpGet httpGet = new HttpGet(ourServer.getBaseUrl() + "/Patient/2/_history/3"); HttpResponse status = ourClient.execute(httpGet); String responseContent = IOUtils.toString(status.getEntity().getContent(), Charsets.UTF_8); IOUtils.closeQuietly(status.getEntity().getContent()); @@ -105,7 +105,7 @@ public class ETagServerR4Test { ourPutVersionInPatientId = false; ourLastModifiedDate = null; - HttpGet httpGet = new HttpGet("http://localhost:" + ourPort + "/Patient/2/_history/3"); + HttpGet httpGet = new HttpGet(ourServer.getBaseUrl() + "/Patient/2/_history/3"); HttpResponse status = ourClient.execute(httpGet); IOUtils.closeQuietly(status.getEntity().getContent()); @@ -120,7 +120,7 @@ public class ETagServerR4Test { public void testLastModifiedHeader() throws Exception { ourLastModifiedDate = new InstantDt("2012-11-25T02:34:45.2222Z").getValue(); - HttpGet httpGet = new HttpGet("http://localhost:" + ourPort + "/Patient/2/_history/3"); + HttpGet httpGet = new HttpGet(ourServer.getBaseUrl() + "/Patient/2/_history/3"); HttpResponse status = ourClient.execute(httpGet); String responseContent = IOUtils.toString(status.getEntity().getContent(), Charsets.UTF_8); IOUtils.closeQuietly(status.getEntity().getContent()); @@ -143,7 +143,7 @@ public class ETagServerR4Test { String resBody = ourCtx.newXmlParser().encodeResourceToString(p); HttpPut http; - http = new HttpPut("http://localhost:" + ourPort + "/Patient/2"); + http = new HttpPut(ourServer.getBaseUrl() + "/Patient/2"); http.setEntity(new StringEntity(resBody, ContentType.create(Constants.CT_FHIR_XML, "UTF-8"))); http.addHeader(Constants.HEADER_IF_MATCH, "\"221\""); CloseableHttpResponse status = ourClient.execute(http); @@ -161,7 +161,7 @@ public class ETagServerR4Test { String resBody = ourCtx.newXmlParser().encodeResourceToString(p); HttpPut http; - http = new HttpPut("http://localhost:" + ourPort + "/Patient/2"); + http = new HttpPut(ourServer.getBaseUrl() + "/Patient/2"); http.setEntity(new StringEntity(resBody, ContentType.create(Constants.CT_FHIR_XML, "UTF-8"))); http.addHeader(Constants.HEADER_IF_MATCH, "\"222\""); CloseableHttpResponse status = ourClient.execute(http); @@ -178,7 +178,7 @@ public class ETagServerR4Test { String resBody = ourCtx.newXmlParser().encodeResourceToString(p); HttpPut http; - http = new HttpPut("http://localhost:" + ourPort + "/Patient/2"); + http = new HttpPut(ourServer.getBaseUrl() + "/Patient/2"); http.setEntity(new StringEntity(resBody, ContentType.create(Constants.CT_FHIR_XML, "UTF-8"))); HttpResponse status = ourClient.execute(http); IOUtils.closeQuietly(status.getEntity().getContent()); @@ -187,32 +187,10 @@ public class ETagServerR4Test { } @AfterAll - public static void afterClass() throws Exception { - JettyUtil.closeServer(ourServer); + public static void afterClass() { + TestUtil.randomizeLocaleAndTimezone(); } - @BeforeAll - public static void beforeClass() throws Exception { - ourServer = new Server(0); - - PatientProvider patientProvider = new PatientProvider(); - - ServletHandler proxyHandler = new ServletHandler(); - RestfulServer servlet = new RestfulServer(ourCtx); - servlet.setDefaultResponseEncoding(EncodingEnum.XML); - servlet.setResourceProviders(patientProvider); - ServletHolder servletHolder = new ServletHolder(servlet); - proxyHandler.addServletWithMapping(servletHolder, "/*"); - ourServer.setHandler(proxyHandler); - JettyUtil.startServer(ourServer); - ourPort = JettyUtil.getPortForStartedServer(ourServer); - - ourConnectionManager = new PoolingHttpClientConnectionManager(50000, TimeUnit.MILLISECONDS); - HttpClientBuilder builder = HttpClientBuilder.create(); - builder.setConnectionManager(ourConnectionManager); - ourClient = builder.build(); - - } public static class PatientProvider implements IResourceProvider { diff --git a/hapi-fhir-structures-r4/src/test/java/ca/uhn/fhir/rest/server/ElementsParamR4Test.java b/hapi-fhir-structures-r4/src/test/java/ca/uhn/fhir/rest/server/ElementsParamR4Test.java index b4a6e2b015a..9f888145b1e 100644 --- a/hapi-fhir-structures-r4/src/test/java/ca/uhn/fhir/rest/server/ElementsParamR4Test.java +++ b/hapi-fhir-structures-r4/src/test/java/ca/uhn/fhir/rest/server/ElementsParamR4Test.java @@ -8,7 +8,10 @@ import ca.uhn.fhir.rest.annotation.IncludeParam; import ca.uhn.fhir.rest.annotation.Read; import ca.uhn.fhir.rest.annotation.Search; import ca.uhn.fhir.rest.api.EncodingEnum; +import ca.uhn.fhir.rest.server.interceptor.ExceptionInterceptorMethodTest; +import ca.uhn.fhir.test.utilities.HttpClientExtension; import ca.uhn.fhir.test.utilities.JettyUtil; +import ca.uhn.fhir.test.utilities.server.RestfulServerExtension; import ca.uhn.fhir.util.TestUtil; import com.google.common.base.Charsets; import org.apache.commons.io.IOUtils; @@ -18,8 +21,8 @@ import org.apache.http.impl.client.CloseableHttpClient; import org.apache.http.impl.client.HttpClientBuilder; import org.apache.http.impl.conn.PoolingHttpClientConnectionManager; import org.eclipse.jetty.server.Server; -import org.eclipse.jetty.servlet.ServletHandler; -import org.eclipse.jetty.servlet.ServletHolder; +import org.eclipse.jetty.ee10.servlet.ServletHandler; +import org.eclipse.jetty.ee10.servlet.ServletHolder; import org.hl7.fhir.instance.model.api.IBaseResource; import org.hl7.fhir.r4.model.Bundle; import org.hl7.fhir.r4.model.DiagnosticReport; @@ -35,6 +38,7 @@ import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Disabled; import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.RegisterExtension; import java.io.IOException; import java.util.Collection; @@ -51,27 +55,34 @@ import static org.junit.jupiter.api.Assertions.assertEquals; public class ElementsParamR4Test { private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(ElementsParamR4Test.class); - private static CloseableHttpClient ourClient; - private static FhirContext ourCtx = FhirContext.forR4(); + private static final FhirContext ourCtx = FhirContext.forR4Cached(); private static Set ourLastElements; - private static int ourPort; - private static Server ourServer; private static Procedure ourNextProcedure; - private static RestfulServer ourServlet; private static Observation ourNextObservation; + @RegisterExtension + public RestfulServerExtension ourServer = new RestfulServerExtension(ourCtx) + .registerProvider(new DummyPatientResourceProvider()) + .registerProvider(new DummyProcedureResourceProvider()) + .registerProvider(new DummyObservationResourceProvider()) + .withPagingProvider(new FifoMemoryPagingProvider(100)) + .setDefaultResponseEncoding(EncodingEnum.XML); + + @RegisterExtension + private HttpClientExtension ourClient = new HttpClientExtension(); + @BeforeEach public void before() { ourLastElements = null; ourNextProcedure = null; - ourServlet.setElementsSupport(new RestfulServer().getElementsSupport()); + ourServer.getRestfulServer().setElementsSupport(new RestfulServer().getElementsSupport()); } @Test public void testElementsOnChoiceWithGenericName() throws IOException { createObservationWithQuantity(); verifyXmlAndJson( - "http://localhost:" + ourPort + "/Observation?_elements=value,status", + ourServer.getBaseUrl() + "/Observation?_elements=value,status", bundle -> { Observation obs = (Observation) bundle.getEntry().get(0).getResource(); assertEquals("SUBSETTED", obs.getMeta().getTag().get(0).getCode()); @@ -85,7 +96,7 @@ public class ElementsParamR4Test { public void testElementsOnChoiceWithSpecificName() throws IOException { createObservationWithQuantity(); verifyXmlAndJson( - "http://localhost:" + ourPort + "/Observation?_elements=valueQuantity,status", + ourServer.getBaseUrl() + "/Observation?_elements=valueQuantity,status", bundle -> { Observation obs = (Observation) bundle.getEntry().get(0).getResource(); assertEquals("SUBSETTED", obs.getMeta().getTag().get(0).getCode()); @@ -100,7 +111,7 @@ public class ElementsParamR4Test { public void testElementsOnChoiceWithSpecificNameNotMatching() throws IOException { createObservationWithQuantity(); verifyXmlAndJson( - "http://localhost:" + ourPort + "/Observation?_elements=valueString,status", + ourServer.getBaseUrl() + "/Observation?_elements=valueString,status", bundle -> { Observation obs = (Observation) bundle.getEntry().get(0).getResource(); assertEquals("SUBSETTED", obs.getMeta().getTag().get(0).getCode()); @@ -113,7 +124,7 @@ public class ElementsParamR4Test { public void testExcludeResources() throws IOException { createProcedureWithLongChain(); verifyXmlAndJson( - "http://localhost:" + ourPort + "/Procedure?_include=*&_elements:exclude=Procedure,DiagnosticReport,*.meta", + ourServer.getBaseUrl() + "/Procedure?_include=*&_elements:exclude=Procedure,DiagnosticReport,*.meta", bundle -> { assertEquals(null, bundle.getEntry().get(0).getResource()); assertEquals(null, bundle.getEntry().get(1).getResource()); @@ -133,7 +144,7 @@ public class ElementsParamR4Test { HttpGet httpGet; encodingEnum = EncodingEnum.JSON; - httpGet = new HttpGet(("http://localhost:" + ourPort + "/Procedure?_include=*&_elements=DiagnosticReport:foo") + "&_pretty=true&_format=" + encodingEnum.getFormatContentType()); + httpGet = new HttpGet((ourServer.getBaseUrl() + "/Procedure?_include=*&_elements=DiagnosticReport:foo") + "&_pretty=true&_format=" + encodingEnum.getFormatContentType()); try (CloseableHttpResponse status = ourClient.execute(httpGet)) { String responseContent = IOUtils.toString(status.getEntity().getContent(), Charsets.UTF_8); ourLog.info(responseContent); @@ -156,7 +167,7 @@ public class ElementsParamR4Test { @Test public void testReadSummaryData() throws Exception { verifyXmlAndJson( - "http://localhost:" + ourPort + "/Patient/1?_elements=name,maritalStatus", + ourServer.getBaseUrl() + "/Patient/1?_elements=name,maritalStatus", Patient.class, patient -> { String responseContent = ourCtx.newXmlParser().encodeResourceToString(patient); @@ -173,7 +184,7 @@ public class ElementsParamR4Test { @Test public void testReadSummaryTrue() throws Exception { verifyXmlAndJson( - "http://localhost:" + ourPort + "/Patient/1?_elements=name", + ourServer.getBaseUrl() + "/Patient/1?_elements=name", Patient.class, patient -> { String responseContent = ourCtx.newXmlParser().encodeResourceToString(patient); @@ -188,7 +199,7 @@ public class ElementsParamR4Test { @Test public void testSearchSummaryData() throws Exception { verifyXmlAndJson( - "http://localhost:" + ourPort + "/Patient?_elements=name,maritalStatus", + ourServer.getBaseUrl() + "/Patient?_elements=name,maritalStatus", bundle -> { assertEquals("1", bundle.getTotalElement().getValueAsString()); String responseContent = ourCtx.newXmlParser().encodeResourceToString(bundle.getEntry().get(0).getResource()); @@ -204,7 +215,7 @@ public class ElementsParamR4Test { @Test public void testSearchSummaryText() throws Exception { verifyXmlAndJson( - "http://localhost:" + ourPort + "/Patient?_elements=text&_pretty=true", + ourServer.getBaseUrl() + "/Patient?_elements=text&_pretty=true", bundle -> { assertEquals("1", bundle.getTotalElement().getValueAsString()); String responseContent = ourCtx.newXmlParser().encodeResourceToString(bundle.getEntry().get(0).getResource()); @@ -224,7 +235,7 @@ public class ElementsParamR4Test { public void testStandardElementsFilter() throws IOException { createProcedureWithLongChain(); verifyXmlAndJson( - "http://localhost:" + ourPort + "/Procedure?_include=*&_elements=reasonCode,status", + ourServer.getBaseUrl() + "/Procedure?_include=*&_elements=reasonCode,status", bundle -> { Procedure procedure = (Procedure) bundle.getEntry().get(0).getResource(); assertEquals("SUBSETTED", procedure.getMeta().getTag().get(0).getCode()); @@ -246,7 +257,7 @@ public class ElementsParamR4Test { public void testMultiResourceElementsFilter() throws IOException { createProcedureWithLongChain(); verifyXmlAndJson( - "http://localhost:" + ourPort + "/Procedure?_include=*&_elements=Procedure.reasonCode,Observation.status,Observation.subject,Observation.value", + ourServer.getBaseUrl() + "/Procedure?_include=*&_elements=Procedure.reasonCode,Observation.status,Observation.subject,Observation.value", bundle -> { Procedure procedure = (Procedure) bundle.getEntry().get(0).getResource(); assertEquals("SUBSETTED", procedure.getMeta().getTag().get(0).getCode()); @@ -272,7 +283,7 @@ public class ElementsParamR4Test { .setUrl("http://quantity") .setValue(Quantity.fromUcum("1.1", "mg")); verifyXmlAndJson( - "http://localhost:" + ourPort + "/Procedure?_elements=Procedure.extension", + ourServer.getBaseUrl() + "/Procedure?_elements=Procedure.extension", bundle -> { Procedure procedure = (Procedure) bundle.getEntry().get(0).getResource(); assertEquals("SUBSETTED", procedure.getMeta().getTag().get(0).getCode()); @@ -282,7 +293,7 @@ public class ElementsParamR4Test { }); verifyXmlAndJson( - "http://localhost:" + ourPort + "/Procedure?_elements=Procedure.extension.value", + ourServer.getBaseUrl() + "/Procedure?_elements=Procedure.extension.value", bundle -> { Procedure procedure = (Procedure) bundle.getEntry().get(0).getResource(); assertEquals("SUBSETTED", procedure.getMeta().getTag().get(0).getCode()); @@ -292,7 +303,7 @@ public class ElementsParamR4Test { }); verifyXmlAndJson( - "http://localhost:" + ourPort + "/Procedure?_elements=Procedure.extension.value.value", + ourServer.getBaseUrl() + "/Procedure?_elements=Procedure.extension.value.value", bundle -> { Procedure procedure = (Procedure) bundle.getEntry().get(0).getResource(); assertEquals("SUBSETTED", procedure.getMeta().getTag().get(0).getCode()); @@ -302,7 +313,7 @@ public class ElementsParamR4Test { }); verifyXmlAndJson( - "http://localhost:" + ourPort + "/Procedure?_elements=Procedure.reason", + ourServer.getBaseUrl() + "/Procedure?_elements=Procedure.reason", bundle -> { Procedure procedure = (Procedure) bundle.getEntry().get(0).getResource(); assertEquals("SUBSETTED", procedure.getMeta().getTag().get(0).getCode()); @@ -314,7 +325,7 @@ public class ElementsParamR4Test { public void testMultiResourceElementsFilterWithMetadataExcluded() throws IOException { createProcedureWithLongChain(); verifyXmlAndJson( - "http://localhost:" + ourPort + "/Procedure?_include=*&_elements=Procedure.reasonCode,Observation.status,Observation.subject,Observation.value&_elements:exclude=*.meta", + ourServer.getBaseUrl() + "/Procedure?_include=*&_elements=Procedure.reasonCode,Observation.status,Observation.subject,Observation.value&_elements:exclude=*.meta", bundle -> { Procedure procedure = (Procedure) bundle.getEntry().get(0).getResource(); assertEquals(true, procedure.getMeta().isEmpty()); @@ -340,7 +351,7 @@ public class ElementsParamR4Test { public void testMultiResourceElementsFilterDoesntAffectFocalResource() throws IOException { createProcedureWithLongChain(); verifyXmlAndJson( - "http://localhost:" + ourPort + "/Procedure?_include=*&_elements=Observation.subject", + ourServer.getBaseUrl() + "/Procedure?_include=*&_elements=Observation.subject", bundle -> { Procedure procedure = (Procedure) bundle.getEntry().get(0).getResource(); assertEquals(true, procedure.getMeta().isEmpty()); @@ -362,10 +373,10 @@ public class ElementsParamR4Test { @Test public void testMultiResourceElementsFilterWithMetadataExcludedStandardMode() throws IOException { - ourServlet.setElementsSupport(ElementsSupportEnum.STANDARD); + ourServer.getRestfulServer().setElementsSupport(ElementsSupportEnum.STANDARD); createProcedureWithLongChain(); verifyXmlAndJson( - "http://localhost:" + ourPort + "/Procedure?_include=*&_elements=Procedure.reasonCode,Observation.status,Observation.subject,Observation.value&_elements:exclude=*.meta", + ourServer.getBaseUrl() + "/Procedure?_include=*&_elements=Procedure.reasonCode,Observation.status,Observation.subject,Observation.value&_elements:exclude=*.meta", bundle -> { Procedure procedure = (Procedure) bundle.getEntry().get(0).getResource(); assertEquals(true, procedure.getMeta().isEmpty()); @@ -388,7 +399,7 @@ public class ElementsParamR4Test { public void testElementsFilterWithComplexPath() throws IOException { createProcedureWithLongChain(); verifyXmlAndJson( - "http://localhost:" + ourPort + "/Procedure?_elements=Procedure.reasonCode.coding.code", + ourServer.getBaseUrl() + "/Procedure?_elements=Procedure.reasonCode.coding.code", bundle -> { Procedure procedure = (Procedure) bundle.getEntry().get(0).getResource(); assertEquals("SUBSETTED", procedure.getMeta().getTag().get(0).getCode()); @@ -507,32 +518,8 @@ public class ElementsParamR4Test { @AfterAll public static void afterClassClearContext() throws Exception { - JettyUtil.closeServer(ourServer); TestUtil.randomizeLocaleAndTimezone(); } - @BeforeAll - public static void beforeClass() throws Exception { - ourServer = new Server(0); - - ServletHandler proxyHandler = new ServletHandler(); - ourServlet = new RestfulServer(ourCtx); - - ourServlet.registerProvider(new DummyPatientResourceProvider()); - ourServlet.registerProvider(new DummyProcedureResourceProvider()); - ourServlet.registerProvider(new DummyObservationResourceProvider()); - - ServletHolder servletHolder = new ServletHolder(ourServlet); - proxyHandler.addServletWithMapping(servletHolder, "/*"); - ourServer.setHandler(proxyHandler); - JettyUtil.startServer(ourServer); - ourPort = JettyUtil.getPortForStartedServer(ourServer); - - PoolingHttpClientConnectionManager connectionManager = new PoolingHttpClientConnectionManager(5000, TimeUnit.MILLISECONDS); - HttpClientBuilder builder = HttpClientBuilder.create(); - builder.setConnectionManager(connectionManager); - ourClient = builder.build(); - - } } diff --git a/hapi-fhir-structures-r4/src/test/java/ca/uhn/fhir/rest/server/HistoryR4Test.java b/hapi-fhir-structures-r4/src/test/java/ca/uhn/fhir/rest/server/HistoryR4Test.java index 6764e39cee8..d208c883aa8 100644 --- a/hapi-fhir-structures-r4/src/test/java/ca/uhn/fhir/rest/server/HistoryR4Test.java +++ b/hapi-fhir-structures-r4/src/test/java/ca/uhn/fhir/rest/server/HistoryR4Test.java @@ -9,7 +9,9 @@ import ca.uhn.fhir.rest.annotation.Since; import ca.uhn.fhir.rest.api.EncodingEnum; import ca.uhn.fhir.rest.param.DateRangeParam; import ca.uhn.fhir.rest.param.ParamPrefixEnum; +import ca.uhn.fhir.test.utilities.HttpClientExtension; import ca.uhn.fhir.test.utilities.JettyUtil; +import ca.uhn.fhir.test.utilities.server.RestfulServerExtension; import ca.uhn.fhir.util.TestUtil; import com.google.common.base.Charsets; import org.apache.commons.io.IOUtils; @@ -19,8 +21,8 @@ import org.apache.http.impl.client.CloseableHttpClient; import org.apache.http.impl.client.HttpClientBuilder; import org.apache.http.impl.conn.PoolingHttpClientConnectionManager; import org.eclipse.jetty.server.Server; -import org.eclipse.jetty.servlet.ServletHandler; -import org.eclipse.jetty.servlet.ServletHolder; +import org.eclipse.jetty.ee10.servlet.ServletHandler; +import org.eclipse.jetty.ee10.servlet.ServletHolder; import org.hl7.fhir.instance.model.api.IPrimitiveType; import org.hl7.fhir.r4.model.Bundle; import org.hl7.fhir.r4.model.DateTimeType; @@ -32,6 +34,7 @@ import org.junit.jupiter.api.AfterAll; import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.RegisterExtension; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -47,15 +50,22 @@ import static org.junit.jupiter.api.Assertions.assertTrue; public class HistoryR4Test { private static final Logger ourLog = LoggerFactory.getLogger(HistoryR4Test.class); - private static CloseableHttpClient ourClient; - private static FhirContext ourCtx = FhirContext.forR4(); + private static final FhirContext ourCtx = FhirContext.forR4Cached(); private static DateRangeParam ourLastAt; private static InstantType ourLastSince; private static IPrimitiveType ourLastSince2; private static IPrimitiveType ourLastSince3; private static IPrimitiveType ourLastSince4; - private static int ourPort; - private static Server ourServer; + + @RegisterExtension + public RestfulServerExtension ourServer = new RestfulServerExtension(ourCtx) + .registerProvider(new DummyPlainProvider()) + .registerProvider(new DummyResourceProvider()) + .withPagingProvider(new FifoMemoryPagingProvider(100)) + .setDefaultResponseEncoding(EncodingEnum.XML); + + @RegisterExtension + private HttpClientExtension ourClient = new HttpClientExtension(); @BeforeEach public void before() { @@ -69,7 +79,7 @@ public class HistoryR4Test { @Test public void testAt() throws Exception { { - HttpGet httpGet = new HttpGet("http://localhost:" + ourPort + "/_history?_at=gt2001&_at=lt2005"); + HttpGet httpGet = new HttpGet(ourServer.getBaseUrl() + "/_history?_at=gt2001&_at=lt2005"); try (CloseableHttpResponse status = ourClient.execute(httpGet)) { String responseContent = IOUtils.toString(status.getEntity().getContent(), Charsets.UTF_8); ourLog.info(responseContent); @@ -86,7 +96,7 @@ public class HistoryR4Test { @Test public void testInstanceHistory() throws Exception { { - HttpGet httpGet = new HttpGet("http://localhost:" + ourPort + "/Patient/123/_history?_pretty=true"); + HttpGet httpGet = new HttpGet(ourServer.getBaseUrl() + "/Patient/123/_history?_pretty=true"); String responseContent; try (CloseableHttpResponse status = ourClient.execute(httpGet)) { responseContent = IOUtils.toString(status.getEntity().getContent(), Charsets.UTF_8); @@ -96,8 +106,8 @@ public class HistoryR4Test { Bundle bundle = ourCtx.newXmlParser().parseResource(Bundle.class, responseContent); assertEquals(2, bundle.getEntry().size()); - assertEquals("http://localhost:" + ourPort + "/Patient/ih1/_history/1", bundle.getEntry().get(0).getResource().getId()); - assertEquals("http://localhost:" + ourPort + "/Patient/ih1/_history/2", bundle.getEntry().get(1).getResource().getId()); + assertEquals(ourServer.getBaseUrl() + "/Patient/ih1/_history/1", bundle.getEntry().get(0).getResource().getId()); + assertEquals(ourServer.getBaseUrl() + "/Patient/ih1/_history/2", bundle.getEntry().get(1).getResource().getId()); } } @@ -105,7 +115,7 @@ public class HistoryR4Test { @Test public void testServerHistory() throws Exception { { - HttpGet httpGet = new HttpGet("http://localhost:" + ourPort + "/_history"); + HttpGet httpGet = new HttpGet(ourServer.getBaseUrl() + "/_history"); String responseContent; try (CloseableHttpResponse status = ourClient.execute(httpGet)) { responseContent = IOUtils.toString(status.getEntity().getContent(), Charsets.UTF_8); @@ -115,8 +125,8 @@ public class HistoryR4Test { Bundle bundle = ourCtx.newXmlParser().parseResource(Bundle.class, responseContent); assertEquals(2, bundle.getEntry().size()); - assertEquals("http://localhost:" + ourPort + "/Patient/h1/_history/1", bundle.getEntry().get(0).getResource().getId()); - assertEquals("http://localhost:" + ourPort + "/Patient/h1/_history/2", bundle.getEntry().get(1).getResource().getId()); + assertEquals(ourServer.getBaseUrl() + "/Patient/h1/_history/1", bundle.getEntry().get(0).getResource().getId()); + assertEquals(ourServer.getBaseUrl() + "/Patient/h1/_history/2", bundle.getEntry().get(1).getResource().getId()); } } @@ -124,7 +134,7 @@ public class HistoryR4Test { @Test public void testSince() throws Exception { { - HttpGet httpGet = new HttpGet("http://localhost:" + ourPort + "/_history?_since=2005"); + HttpGet httpGet = new HttpGet(ourServer.getBaseUrl() + "/_history?_since=2005"); String responseContent; try (CloseableHttpResponse status = ourClient.execute(httpGet)) { responseContent = IOUtils.toString(status.getEntity().getContent(), Charsets.UTF_8); @@ -146,7 +156,7 @@ public class HistoryR4Test { @Test public void testTypeHistory() throws Exception { { - HttpGet httpGet = new HttpGet("http://localhost:" + ourPort + "/Patient/_history"); + HttpGet httpGet = new HttpGet(ourServer.getBaseUrl() + "/Patient/_history"); String responseContent; try (CloseableHttpResponse status = ourClient.execute(httpGet)) { responseContent = IOUtils.toString(status.getEntity().getContent(), Charsets.UTF_8); @@ -158,8 +168,8 @@ public class HistoryR4Test { Bundle bundle = ourCtx.newXmlParser().parseResource(Bundle.class, responseContent); assertEquals(2, bundle.getEntry().size()); - assertEquals("http://localhost:" + ourPort + "/Patient/th1/_history/1", bundle.getEntry().get(0).getResource().getId()); - assertEquals("http://localhost:" + ourPort + "/Patient/th1/_history/2", bundle.getEntry().get(1).getResource().getId()); + assertEquals(ourServer.getBaseUrl() + "/Patient/th1/_history/1", bundle.getEntry().get(0).getResource().getId()); + assertEquals(ourServer.getBaseUrl() + "/Patient/th1/_history/2", bundle.getEntry().get(1).getResource().getId()); } } @@ -170,7 +180,7 @@ public class HistoryR4Test { @Test public void testVread() throws Exception { { - HttpGet httpGet = new HttpGet("http://localhost:" + ourPort + "/Patient/123/_history/456"); + HttpGet httpGet = new HttpGet(ourServer.getBaseUrl() + "/Patient/123/_history/456"); String responseContent; try (CloseableHttpResponse status = ourClient.execute(httpGet)) { responseContent = IOUtils.toString(status.getEntity().getContent(), Charsets.UTF_8); @@ -185,34 +195,9 @@ public class HistoryR4Test { @AfterAll public static void afterClassClearContext() throws Exception { - JettyUtil.closeServer(ourServer); TestUtil.randomizeLocaleAndTimezone(); } - @BeforeAll - public static void beforeClass() throws Exception { - ourServer = new Server(0); - - DummyPlainProvider plainProvider = new DummyPlainProvider(); - DummyResourceProvider patientProvider = new DummyResourceProvider(); - - ServletHandler proxyHandler = new ServletHandler(); - RestfulServer servlet = new RestfulServer(ourCtx); - servlet.setDefaultResponseEncoding(EncodingEnum.XML); - servlet.registerProviders(plainProvider, patientProvider); - ServletHolder servletHolder = new ServletHolder(servlet); - proxyHandler.addServletWithMapping(servletHolder, "/*"); - ourServer.setHandler(proxyHandler); - JettyUtil.startServer(ourServer); - ourPort = JettyUtil.getPortForStartedServer(ourServer); - - PoolingHttpClientConnectionManager connectionManager = new PoolingHttpClientConnectionManager(5000, TimeUnit.MILLISECONDS); - HttpClientBuilder builder = HttpClientBuilder.create(); - builder.setConnectionManager(connectionManager); - ourClient = builder.build(); - - } - public static class DummyPlainProvider { @History diff --git a/hapi-fhir-structures-r4/src/test/java/ca/uhn/fhir/rest/server/IncludeTest.java b/hapi-fhir-structures-r4/src/test/java/ca/uhn/fhir/rest/server/IncludeTest.java index 247c425c19a..06143b2dafe 100644 --- a/hapi-fhir-structures-r4/src/test/java/ca/uhn/fhir/rest/server/IncludeTest.java +++ b/hapi-fhir-structures-r4/src/test/java/ca/uhn/fhir/rest/server/IncludeTest.java @@ -12,7 +12,9 @@ import ca.uhn.fhir.rest.annotation.RequiredParam; import ca.uhn.fhir.rest.annotation.Search; import ca.uhn.fhir.rest.api.Constants; import ca.uhn.fhir.rest.api.EncodingEnum; +import ca.uhn.fhir.test.utilities.HttpClientExtension; import ca.uhn.fhir.test.utilities.JettyUtil; +import ca.uhn.fhir.test.utilities.server.RestfulServerExtension; import ca.uhn.fhir.util.BundleUtil; import ca.uhn.fhir.util.ElementUtil; import ca.uhn.fhir.util.TestUtil; @@ -23,8 +25,8 @@ import org.apache.http.impl.client.CloseableHttpClient; import org.apache.http.impl.client.HttpClientBuilder; import org.apache.http.impl.conn.PoolingHttpClientConnectionManager; import org.eclipse.jetty.server.Server; -import org.eclipse.jetty.servlet.ServletHandler; -import org.eclipse.jetty.servlet.ServletHolder; +import org.eclipse.jetty.ee10.servlet.ServletHandler; +import org.eclipse.jetty.ee10.servlet.ServletHolder; import org.hl7.fhir.r4.model.Bundle; import org.hl7.fhir.r4.model.DiagnosticReport; import org.hl7.fhir.r4.model.Observation; @@ -35,6 +37,7 @@ import org.hl7.fhir.r4.model.Reference; import org.junit.jupiter.api.AfterAll; import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.RegisterExtension; import java.util.ArrayList; import java.util.Arrays; @@ -53,14 +56,22 @@ import static org.junit.jupiter.api.Assertions.fail; public class IncludeTest { private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(IncludeTest.class); - private static CloseableHttpClient ourClient; - private static FhirContext ourCtx; - private static int ourPort; - private static Server ourServer; + private static final FhirContext ourCtx = FhirContext.forR4Cached(); + + @RegisterExtension + public RestfulServerExtension ourServer = new RestfulServerExtension(ourCtx) + .registerProvider(new DummyPatientResourceProvider()) + .registerProvider(new DummyDiagnosticReportResourceProvider()) + .withPagingProvider(new FifoMemoryPagingProvider(100)) + .setDefaultResponseEncoding(EncodingEnum.XML) + .withServer(s->s.setBundleInclusionRule(BundleInclusionRule.BASED_ON_RESOURCE_PRESENCE)); + + @RegisterExtension + private HttpClientExtension ourClient = new HttpClientExtension(); @Test public void testBadInclude() throws Exception { - HttpGet httpGet = new HttpGet("http://localhost:" + ourPort + "/Patient?name=Hello&_include=foo&_include=baz"); + HttpGet httpGet = new HttpGet(ourServer.getBaseUrl() + "/Patient?name=Hello&_include=foo&_include=baz"); try (CloseableHttpResponse status = ourClient.execute(httpGet)) { assertEquals(400, status.getStatusLine().getStatusCode()); String responseContent = IOUtils.toString(status.getEntity().getContent()); @@ -72,7 +83,7 @@ public class IncludeTest { @Test public void testIIncludedResourcesNonContained() throws Exception { - HttpGet httpGet = new HttpGet("http://localhost:" + ourPort + "/Patient?_query=normalInclude&_pretty=true"); + HttpGet httpGet = new HttpGet(ourServer.getBaseUrl() + "/Patient?_query=normalInclude&_pretty=true"); try (CloseableHttpResponse status = ourClient.execute(httpGet)) { String responseContent = IOUtils.toString(status.getEntity().getContent()); @@ -98,7 +109,7 @@ public class IncludeTest { @Test public void testIIncludedResourcesNonContainedInDeclaredExtension() throws Exception { - HttpGet httpGet = new HttpGet("http://localhost:" + ourPort + "/Patient?_query=declaredExtInclude&_pretty=true"); + HttpGet httpGet = new HttpGet(ourServer.getBaseUrl() + "/Patient?_query=declaredExtInclude&_pretty=true"); try (CloseableHttpResponse status = ourClient.execute(httpGet)) { String responseContent = IOUtils.toString(status.getEntity().getContent()); @@ -124,7 +135,7 @@ public class IncludeTest { @Test public void testIIncludedResourcesNonContainedInExtension() throws Exception { - HttpGet httpGet = new HttpGet("http://localhost:" + ourPort + "/Patient?_query=extInclude&_pretty=true"); + HttpGet httpGet = new HttpGet(ourServer.getBaseUrl() + "/Patient?_query=extInclude&_pretty=true"); try (CloseableHttpResponse status = ourClient.execute(httpGet)) { String responseContent = IOUtils.toString(status.getEntity().getContent()); @@ -149,7 +160,7 @@ public class IncludeTest { @Test public void testIIncludedResourcesNonContainedInExtensionJson() throws Exception { - HttpGet httpGet = new HttpGet("http://localhost:" + ourPort + "/Patient?_query=extInclude&_pretty=true&_format=json"); + HttpGet httpGet = new HttpGet(ourServer.getBaseUrl() + "/Patient?_query=extInclude&_pretty=true&_format=json"); try (CloseableHttpResponse status = ourClient.execute(httpGet)) { String responseContent = IOUtils.toString(status.getEntity().getContent()); @@ -205,7 +216,7 @@ public class IncludeTest { @Test public void testNoIncludes() throws Exception { - HttpGet httpGet = new HttpGet("http://localhost:" + ourPort + "/Patient?name=Hello"); + HttpGet httpGet = new HttpGet(ourServer.getBaseUrl() + "/Patient?name=Hello"); try (CloseableHttpResponse status = ourClient.execute(httpGet)) { String responseContent = IOUtils.toString(status.getEntity().getContent()); @@ -221,7 +232,7 @@ public class IncludeTest { @Test public void testOneInclude() throws Exception { - HttpGet httpGet = new HttpGet("http://localhost:" + ourPort + "/Patient?name=Hello&_include=foo"); + HttpGet httpGet = new HttpGet(ourServer.getBaseUrl() + "/Patient?name=Hello&_include=foo"); try (CloseableHttpResponse status = ourClient.execute(httpGet)) { String responseContent = IOUtils.toString(status.getEntity().getContent()); @@ -238,7 +249,7 @@ public class IncludeTest { @Test public void testOneIncludeIterate() throws Exception { - HttpGet httpGet = new HttpGet("http://localhost:" + ourPort + "/Patient?name=Hello&" + Constants.PARAM_INCLUDE_ITERATE + "=foo"); + HttpGet httpGet = new HttpGet(ourServer.getBaseUrl() + "/Patient?name=Hello&" + Constants.PARAM_INCLUDE_ITERATE + "=foo"); try (CloseableHttpResponse status = ourClient.execute(httpGet)) { String responseContent = IOUtils.toString(status.getEntity().getContent()); @@ -255,7 +266,7 @@ public class IncludeTest { @Test public void testTwoInclude() throws Exception { - HttpGet httpGet = new HttpGet("http://localhost:" + ourPort + "/Patient?name=Hello&_include=foo&_include=bar"); + HttpGet httpGet = new HttpGet(ourServer.getBaseUrl() + "/Patient?name=Hello&_include=foo&_include=bar"); try (CloseableHttpResponse status = ourClient.execute(httpGet)) { String responseContent = IOUtils.toString(status.getEntity().getContent()); @@ -277,7 +288,7 @@ public class IncludeTest { @Test public void testStringInclude() throws Exception { - HttpGet httpGet = new HttpGet("http://localhost:" + ourPort + "/Patient?_query=stringInclude&_include=foo"); + HttpGet httpGet = new HttpGet(ourServer.getBaseUrl() + "/Patient?_query=stringInclude&_include=foo"); try (CloseableHttpResponse status = ourClient.execute(httpGet)) { String responseContent = IOUtils.toString(status.getEntity().getContent()); @@ -478,34 +489,7 @@ public class IncludeTest { @AfterAll public static void afterClassClearContext() throws Exception { - JettyUtil.closeServer(ourServer); TestUtil.randomizeLocaleAndTimezone(); } - - @BeforeAll - public static void beforeClass() throws Exception { - - ourCtx = FhirContext.forR4(); - ourServer = new Server(0); - - DummyPatientResourceProvider patientProvider = new DummyPatientResourceProvider(); - - ServletHandler proxyHandler = new ServletHandler(); - RestfulServer servlet = new RestfulServer(ourCtx); - servlet.setDefaultResponseEncoding(EncodingEnum.XML); - servlet.setBundleInclusionRule(BundleInclusionRule.BASED_ON_RESOURCE_PRESENCE); - servlet.setResourceProviders(patientProvider, new DummyDiagnosticReportResourceProvider()); - ServletHolder servletHolder = new ServletHolder(servlet); - proxyHandler.addServletWithMapping(servletHolder, "/*"); - ourServer.setHandler(proxyHandler); - JettyUtil.startServer(ourServer); - ourPort = JettyUtil.getPortForStartedServer(ourServer); - - PoolingHttpClientConnectionManager connectionManager = new PoolingHttpClientConnectionManager(5000, TimeUnit.MILLISECONDS); - HttpClientBuilder builder = HttpClientBuilder.create(); - builder.setConnectionManager(connectionManager); - ourClient = builder.build(); - - } } diff --git a/hapi-fhir-structures-r4/src/test/java/ca/uhn/fhir/rest/server/LastNProviderTest.java b/hapi-fhir-structures-r4/src/test/java/ca/uhn/fhir/rest/server/LastNProviderTest.java index 0a52c202083..899f97e383e 100644 --- a/hapi-fhir-structures-r4/src/test/java/ca/uhn/fhir/rest/server/LastNProviderTest.java +++ b/hapi-fhir-structures-r4/src/test/java/ca/uhn/fhir/rest/server/LastNProviderTest.java @@ -40,9 +40,10 @@ public class LastNProviderTest extends BaseR4ServerTest { MyProvider provider = new MyProvider(); startServer(provider); - Bundle response = myClient + Bundle response = ourServer + .getFhirClient() .search() - .byUrl(myBaseUrl + "/Observation/$lastn?subject=Patient/123&category=http://terminology.hl7.org/CodeSystem/observation-category|laboratory,http://terminology.hl7.org/CodeSystem/observation-category|vital-signs&code=http://loinc.org|1111-1,http://loinc.org|2222-2&max=15") + .byUrl(ourServer.getBaseUrl() + "/Observation/$lastn?subject=Patient/123&category=http://terminology.hl7.org/CodeSystem/observation-category|laboratory,http://terminology.hl7.org/CodeSystem/observation-category|vital-signs&code=http://loinc.org|1111-1,http://loinc.org|2222-2&max=15") .returnBundle(Bundle.class) .execute(); assertEquals("abc123", response.getIdElement().getIdPart()); diff --git a/hapi-fhir-structures-r4/src/test/java/ca/uhn/fhir/rest/server/MultitenancyR4Test.java b/hapi-fhir-structures-r4/src/test/java/ca/uhn/fhir/rest/server/MultitenancyR4Test.java index b73b66cdf3e..07aad245a88 100644 --- a/hapi-fhir-structures-r4/src/test/java/ca/uhn/fhir/rest/server/MultitenancyR4Test.java +++ b/hapi-fhir-structures-r4/src/test/java/ca/uhn/fhir/rest/server/MultitenancyR4Test.java @@ -8,30 +8,24 @@ import ca.uhn.fhir.rest.api.EncodingEnum; import ca.uhn.fhir.rest.api.server.RequestDetails; import ca.uhn.fhir.rest.param.TokenAndListParam; import ca.uhn.fhir.rest.server.tenant.UrlBaseTenantIdentificationStrategy; -import ca.uhn.fhir.test.utilities.JettyUtil; +import ca.uhn.fhir.test.utilities.HttpClientExtension; +import ca.uhn.fhir.test.utilities.server.RestfulServerExtension; import ca.uhn.fhir.util.TestUtil; import org.apache.commons.io.IOUtils; import org.apache.http.client.methods.CloseableHttpResponse; import org.apache.http.client.methods.HttpGet; -import org.apache.http.impl.client.CloseableHttpClient; -import org.apache.http.impl.client.HttpClientBuilder; -import org.apache.http.impl.conn.PoolingHttpClientConnectionManager; -import org.eclipse.jetty.server.Server; -import org.eclipse.jetty.servlet.ServletHandler; -import org.eclipse.jetty.servlet.ServletHolder; import org.hl7.fhir.instance.model.api.IBaseResource; import org.hl7.fhir.r4.model.Bundle; import org.hl7.fhir.r4.model.HumanName; import org.hl7.fhir.r4.model.Patient; import org.junit.jupiter.api.AfterAll; -import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.RegisterExtension; import java.nio.charset.StandardCharsets; import java.util.ArrayList; import java.util.List; -import java.util.concurrent.TimeUnit; import static org.hamcrest.MatcherAssert.assertThat; import static org.hamcrest.Matchers.containsString; @@ -41,14 +35,22 @@ import static org.junit.jupiter.api.Assertions.assertEquals; public class MultitenancyR4Test { private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(MultitenancyR4Test.class); - private static CloseableHttpClient ourClient; - private static FhirContext ourCtx = FhirContext.forR4(); + private static final FhirContext ourCtx = FhirContext.forR4Cached(); private static TokenAndListParam ourIdentifiers; private static String ourLastMethod; - private static int ourPort; - private static Server ourServer; private static String ourLastTenantId; + @RegisterExtension + public RestfulServerExtension ourServer = new RestfulServerExtension(ourCtx) + .registerProvider(new DummyPatientResourceProvider()) + .withPagingProvider(new FifoMemoryPagingProvider(100)) + .setDefaultResponseEncoding(EncodingEnum.JSON) + .withServer(s -> s.setTenantIdentificationStrategy(new UrlBaseTenantIdentificationStrategy())) + .setDefaultPrettyPrint(false); + + @RegisterExtension + private HttpClientExtension ourClient = new HttpClientExtension(); + @BeforeEach public void before() { ourLastMethod = null; @@ -58,24 +60,8 @@ public class MultitenancyR4Test { @Test public void testUrlBaseStrategy() throws Exception { - ourServer = new Server(0); - DummyPatientResourceProvider patientProvider = new DummyPatientResourceProvider(); - - ServletHandler proxyHandler = new ServletHandler(); - RestfulServer servlet = new RestfulServer(ourCtx); - servlet.setDefaultResponseEncoding(EncodingEnum.JSON); - servlet.setPagingProvider(new FifoMemoryPagingProvider(10)); - servlet.setTenantIdentificationStrategy(new UrlBaseTenantIdentificationStrategy()); - - servlet.setResourceProviders(patientProvider); - ServletHolder servletHolder = new ServletHolder(servlet); - proxyHandler.addServletWithMapping(servletHolder, "/*"); - ourServer.setHandler(proxyHandler); - JettyUtil.startServer(ourServer); - ourPort = JettyUtil.getPortForStartedServer(ourServer); - - HttpGet httpGet = new HttpGet("http://localhost:" + ourPort + "/TENANT2/Patient?identifier=foo%7Cbar"); + HttpGet httpGet = new HttpGet(ourServer.getBaseUrl() + "/TENANT2/Patient?identifier=foo%7Cbar"); CloseableHttpResponse status = ourClient.execute(httpGet); try { String responseContent = IOUtils.toString(status.getEntity().getContent(), StandardCharsets.UTF_8); @@ -88,22 +74,22 @@ public class MultitenancyR4Test { Bundle resp = ourCtx.newJsonParser().parseResource(Bundle.class, responseContent); ourLog.debug(ourCtx.newXmlParser().setPrettyPrint(true).encodeResourceToString(resp)); - assertEquals("http://localhost:" + ourPort + "/TENANT2/Patient?identifier=foo%7Cbar", resp.getLink("self").getUrl()); - assertEquals("http://localhost:" + ourPort + "/TENANT2/Patient/0", resp.getEntry().get(0).getFullUrl()); - assertEquals("http://localhost:"+ourPort+"/TENANT2/Patient/0", resp.getEntry().get(0).getResource().getId()); - assertThat(resp.getLink("next").getUrl(), startsWith("http://localhost:"+ourPort+"/TENANT2?_getpages=")); + assertEquals(ourServer.getBaseUrl() + "/TENANT2/Patient?identifier=foo%7Cbar", resp.getLink("self").getUrl()); + assertEquals(ourServer.getBaseUrl() + "/TENANT2/Patient/0", resp.getEntry().get(0).getFullUrl()); + assertEquals(ourServer.getBaseUrl() + "/TENANT2/Patient/0", resp.getEntry().get(0).getResource().getId()); + assertThat(resp.getLink("next").getUrl(), startsWith(ourServer.getBaseUrl() + "/TENANT2?_getpages=")); } finally { IOUtils.closeQuietly(status.getEntity().getContent()); } // GET the root - httpGet = new HttpGet("http://localhost:" + ourPort + "/"); + httpGet = new HttpGet(ourServer.getBaseUrl() + "/"); status = ourClient.execute(httpGet); try { String responseContent = IOUtils.toString(status.getEntity().getContent(), StandardCharsets.UTF_8); assertEquals(400, status.getStatusLine().getStatusCode()); - assertThat(responseContent, containsString("\"diagnostics\":\""+ Msg.code(307) + "This is the base URL of a multitenant FHIR server. Unable to handle this request, as it does not contain a tenant ID.\"")); + assertThat(responseContent, containsString("\"diagnostics\":\"" + Msg.code(307) + "This is the base URL of a multitenant FHIR server. Unable to handle this request, as it does not contain a tenant ID.\"")); } finally { IOUtils.closeQuietly(status.getEntity().getContent()); } @@ -111,20 +97,11 @@ public class MultitenancyR4Test { @AfterAll public static void afterClassClearContext() throws Exception { - JettyUtil.closeServer(ourServer); TestUtil.randomizeLocaleAndTimezone(); } - @BeforeAll - public static void beforeClass() { - PoolingHttpClientConnectionManager connectionManager = new PoolingHttpClientConnectionManager(5000, TimeUnit.MILLISECONDS); - HttpClientBuilder builder = HttpClientBuilder.create(); - builder.setConnectionManager(connectionManager); - ourClient = builder.build(); - } - - public static class DummyPatientResourceProvider implements IResourceProvider { + private static class DummyPatientResourceProvider implements IResourceProvider { @Override @@ -135,8 +112,8 @@ public class MultitenancyR4Test { @SuppressWarnings("rawtypes") @Search() public List search( - RequestDetails theRequestDetails, - @RequiredParam(name = Patient.SP_IDENTIFIER) TokenAndListParam theIdentifiers) { + RequestDetails theRequestDetails, + @RequiredParam(name = Patient.SP_IDENTIFIER) TokenAndListParam theIdentifiers) { ourLastMethod = "search"; ourIdentifiers = theIdentifiers; ourLastTenantId = theRequestDetails.getTenantId(); diff --git a/hapi-fhir-structures-r4/src/test/java/ca/uhn/fhir/rest/server/OperationGenericServer2R4Test.java b/hapi-fhir-structures-r4/src/test/java/ca/uhn/fhir/rest/server/OperationGenericServer2R4Test.java index 125754b1f4a..faeb24863a6 100644 --- a/hapi-fhir-structures-r4/src/test/java/ca/uhn/fhir/rest/server/OperationGenericServer2R4Test.java +++ b/hapi-fhir-structures-r4/src/test/java/ca/uhn/fhir/rest/server/OperationGenericServer2R4Test.java @@ -9,7 +9,11 @@ import ca.uhn.fhir.rest.annotation.OperationParam; import ca.uhn.fhir.rest.annotation.ResourceParam; import ca.uhn.fhir.rest.api.Constants; import ca.uhn.fhir.rest.api.EncodingEnum; +import ca.uhn.fhir.rest.server.exceptions.InternalErrorException; +import ca.uhn.fhir.rest.server.provider.HashMapResourceProvider; +import ca.uhn.fhir.test.utilities.HttpClientExtension; import ca.uhn.fhir.test.utilities.JettyUtil; +import ca.uhn.fhir.test.utilities.server.RestfulServerExtension; import ca.uhn.fhir.util.TestUtil; import org.apache.commons.io.IOUtils; import org.apache.http.client.methods.CloseableHttpResponse; @@ -21,14 +25,15 @@ import org.apache.http.impl.client.CloseableHttpClient; import org.apache.http.impl.client.HttpClientBuilder; import org.apache.http.impl.conn.PoolingHttpClientConnectionManager; import org.eclipse.jetty.server.Server; -import org.eclipse.jetty.servlet.ServletHandler; -import org.eclipse.jetty.servlet.ServletHolder; +import org.eclipse.jetty.ee10.servlet.ServletHandler; +import org.eclipse.jetty.ee10.servlet.ServletHolder; import org.hl7.fhir.instance.model.api.IBaseResource; import org.hl7.fhir.instance.model.api.ICompositeType; import org.hl7.fhir.instance.model.api.IPrimitiveType; import org.hl7.fhir.r4.model.CodeType; import org.hl7.fhir.r4.model.Coding; import org.hl7.fhir.r4.model.IdType; +import org.hl7.fhir.r4.model.MolecularSequence; import org.hl7.fhir.r4.model.Parameters; import org.hl7.fhir.r4.model.Patient; import org.hl7.fhir.r4.model.StringType; @@ -39,7 +44,9 @@ import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; -import javax.servlet.ServletException; +import jakarta.servlet.ServletException; +import org.junit.jupiter.api.extension.RegisterExtension; + import java.nio.charset.StandardCharsets; import java.util.List; import java.util.concurrent.TimeUnit; @@ -51,15 +58,23 @@ import static org.junit.jupiter.api.Assertions.fail; public class OperationGenericServer2R4Test { private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(OperationGenericServer2R4Test.class); - private static CloseableHttpClient ourClient; - private static FhirContext ourCtx; + private static final FhirContext ourCtx = FhirContext.forR4Cached(); private static IdType ourLastId; private static Object ourLastParam1; private static Object ourLastParam2; private static Object ourLastParam3; private static Parameters ourLastResourceParam; - private int myPort; - private Server myServer; + + @RegisterExtension + public RestfulServerExtension ourServer = new RestfulServerExtension(ourCtx) + .registerProvider(new HashMapResourceProvider<>(ourCtx, MolecularSequence.class)) + .withPagingProvider(new FifoMemoryPagingProvider(10).setDefaultPageSize(2)) + .setDefaultResponseEncoding(EncodingEnum.JSON) + .setDefaultPrettyPrint(false); + + @RegisterExtension + private HttpClientExtension ourClient = new HttpClientExtension(); + @BeforeEach public void before() { @@ -103,14 +118,14 @@ public class OperationGenericServer2R4Test { } PatientProvider provider = new PatientProvider(); - startServer(provider); + ourServer.registerProvider(provider); Parameters p = new Parameters(); p.addParameter().setName("PARAM1").setValue(new CodeType("PARAM1val")); p.addParameter().setName("PARAM2").setValue(new Coding("sys", "val", "dis")); String inParamsStr = ourCtx.newXmlParser().encodeResourceToString(p); - HttpPost httpPost = new HttpPost("http://localhost:" + myPort + "/Patient/123/$OP_INSTANCE"); + HttpPost httpPost = new HttpPost(ourServer.getBaseUrl() + "/Patient/123/$OP_INSTANCE"); httpPost.setEntity(new StringEntity(inParamsStr, ContentType.create(Constants.CT_FHIR_XML, "UTF-8"))); try (CloseableHttpResponse status = ourClient.execute(httpPost)) { assertEquals(200, status.getStatusLine().getStatusCode()); @@ -159,14 +174,14 @@ public class OperationGenericServer2R4Test { } PatientProvider provider = new PatientProvider(); - startServer(provider); + ourServer.registerProvider(provider); Parameters p = new Parameters(); p.addParameter().setName("PARAM1").setValue(new CodeType("PARAM1val")); p.addParameter().setName("PARAM1").setValue(new CodeType("PARAM1val2")); String inParamsStr = ourCtx.newXmlParser().encodeResourceToString(p); - HttpPost httpPost = new HttpPost("http://localhost:" + myPort + "/Patient/123/$OP_INSTANCE"); + HttpPost httpPost = new HttpPost(ourServer.getBaseUrl() + "/Patient/123/$OP_INSTANCE"); httpPost.setEntity(new StringEntity(inParamsStr, ContentType.create(Constants.CT_FHIR_XML, "UTF-8"))); try (CloseableHttpResponse status = ourClient.execute(httpPost)) { assertEquals(200, status.getStatusLine().getStatusCode()); @@ -218,14 +233,14 @@ public class OperationGenericServer2R4Test { } PatientProvider provider = new PatientProvider(); - startServer(provider); + ourServer.registerProvider(provider); Parameters p = new Parameters(); p.addParameter().setName("PARAM1").setValue(new UriType("PARAM1val")); p.addParameter().setName("PARAM2").setValue(new StringType("PARAM2val")); String inParamsStr = ourCtx.newXmlParser().encodeResourceToString(p); - HttpPost httpPost = new HttpPost("http://localhost:" + myPort + "/Patient/123/$OP_INSTANCE"); + HttpPost httpPost = new HttpPost(ourServer.getBaseUrl() + "/Patient/123/$OP_INSTANCE"); httpPost.setEntity(new StringEntity(inParamsStr, ContentType.create(Constants.CT_FHIR_XML, "UTF-8"))); try (CloseableHttpResponse status = ourClient.execute(httpPost)) { assertEquals(200, status.getStatusLine().getStatusCode()); @@ -264,11 +279,11 @@ public class OperationGenericServer2R4Test { try { PatientProvider provider = new PatientProvider(); - startServer(provider); + ourServer.registerProvider(provider); fail(); - } catch (ServletException e) { + } catch (ConfigurationException e) { ConfigurationException ce = (ConfigurationException) e.getCause(); - assertThat(ce.getMessage(), containsString("Failure scanning class PatientProvider: " + Msg.code(405) + "Non assignable parameter typeName=\"code\" specified on method public org.hl7.fhir.r4.model.Parameters ca.uhn.fhir.rest.server.OperationGenericServer2R4Test")); + assertThat(ce.getMessage(), containsString(Msg.code(405) + "Non assignable parameter typeName=\"code\" specified on method public org.hl7.fhir.r4.model.Parameters ca.uhn.fhir.rest.server.OperationGenericServer2R4Test")); } } @@ -295,9 +310,9 @@ public class OperationGenericServer2R4Test { } PlainProvider provider = new PlainProvider(); - startServer(provider); + ourServer.registerProvider(provider); - HttpGet httpPost = new HttpGet("http://localhost:" + myPort + "/Patient/123/$OP_INSTANCE"); + HttpGet httpPost = new HttpGet(ourServer.getBaseUrl() + "/Patient/123/$OP_INSTANCE"); try (CloseableHttpResponse status = ourClient.execute(httpPost)) { String response = IOUtils.toString(status.getEntity().getContent(), StandardCharsets.UTF_8); ourLog.info(response); @@ -324,37 +339,19 @@ public class OperationGenericServer2R4Test { PlainProvider provider = new PlainProvider(); try { - startServer(provider); + ourServer.registerProvider(provider); fail(); - } catch (ServletException e) { - Throwable cause = e.getRootCause(); - assertEquals(Msg.code(288) + "Failure scanning class PlainProvider: " + Msg.code(423) + "Failed to bind method public org.hl7.fhir.r4.model.Parameters ca.uhn.fhir.rest.server.OperationGenericServer2R4Test$2PlainProvider.opInstance() - " + Msg.code(1684) + "Unknown resource name \"FOO\" (this name is not known in FHIR version \"R4\")", cause.getMessage()); + } catch (ConfigurationException e) { + Throwable cause = e.getCause(); + assertEquals(Msg.code(423) + "Failed to bind method public org.hl7.fhir.r4.model.Parameters ca.uhn.fhir.rest.server.OperationGenericServer2R4Test$2PlainProvider.opInstance() - " + Msg.code(1684) + "Unknown resource name \"FOO\" (this name is not known in FHIR version \"R4\")", cause.getMessage()); } } - private void startServer(Object theProvider) throws Exception { - myServer = new Server(0); - - ServletHandler proxyHandler = new ServletHandler(); - RestfulServer servlet = new RestfulServer(ourCtx); - servlet.setDefaultResponseEncoding(EncodingEnum.XML); - - servlet.setPagingProvider(new FifoMemoryPagingProvider(10).setDefaultPageSize(2)); - - servlet.setFhirContext(ourCtx); - servlet.registerProvider(theProvider); - ServletHolder servletHolder = new ServletHolder(servlet); - proxyHandler.addServletWithMapping(servletHolder, "/*"); - myServer.setHandler(proxyHandler); - JettyUtil.startServer(myServer); - myPort = JettyUtil.getPortForStartedServer(myServer); - } - @AfterEach public void after() throws Exception { - JettyUtil.closeServer(myServer); + TestUtil.randomizeLocaleAndTimezone(); } @AfterAll @@ -362,15 +359,4 @@ public class OperationGenericServer2R4Test { TestUtil.randomizeLocaleAndTimezone(); } - @BeforeAll - public static void beforeClass() { - ourCtx = FhirContext.forR4(); - - PoolingHttpClientConnectionManager connectionManager = new PoolingHttpClientConnectionManager(5000, TimeUnit.MILLISECONDS); - HttpClientBuilder builder = HttpClientBuilder.create(); - builder.setConnectionManager(connectionManager); - ourClient = builder.build(); - - } - } diff --git a/hapi-fhir-structures-r4/src/test/java/ca/uhn/fhir/rest/server/OperationGenericServerR4Test.java b/hapi-fhir-structures-r4/src/test/java/ca/uhn/fhir/rest/server/OperationGenericServerR4Test.java index 999e7ffc1ae..56e17ae4a0f 100644 --- a/hapi-fhir-structures-r4/src/test/java/ca/uhn/fhir/rest/server/OperationGenericServerR4Test.java +++ b/hapi-fhir-structures-r4/src/test/java/ca/uhn/fhir/rest/server/OperationGenericServerR4Test.java @@ -8,7 +8,9 @@ import ca.uhn.fhir.rest.annotation.ResourceParam; import ca.uhn.fhir.rest.annotation.Search; import ca.uhn.fhir.rest.api.Constants; import ca.uhn.fhir.rest.api.EncodingEnum; +import ca.uhn.fhir.test.utilities.HttpClientExtension; import ca.uhn.fhir.test.utilities.JettyUtil; +import ca.uhn.fhir.test.utilities.server.RestfulServerExtension; import ca.uhn.fhir.util.TestUtil; import org.apache.commons.io.IOUtils; import org.apache.http.client.methods.CloseableHttpResponse; @@ -20,8 +22,8 @@ import org.apache.http.impl.client.CloseableHttpClient; import org.apache.http.impl.client.HttpClientBuilder; import org.apache.http.impl.conn.PoolingHttpClientConnectionManager; import org.eclipse.jetty.server.Server; -import org.eclipse.jetty.servlet.ServletHandler; -import org.eclipse.jetty.servlet.ServletHolder; +import org.eclipse.jetty.ee10.servlet.ServletHandler; +import org.eclipse.jetty.ee10.servlet.ServletHolder; import org.hl7.fhir.instance.model.api.IBaseResource; import org.hl7.fhir.r4.model.IdType; import org.hl7.fhir.r4.model.Parameters; @@ -31,6 +33,7 @@ import org.junit.jupiter.api.AfterAll; import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.RegisterExtension; import java.nio.charset.StandardCharsets; import java.util.ArrayList; @@ -42,16 +45,23 @@ import static org.junit.jupiter.api.Assertions.assertNull; public class OperationGenericServerR4Test { private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(OperationGenericServerR4Test.class); - private static CloseableHttpClient ourClient; - private static FhirContext ourCtx; + private static final FhirContext ourCtx = FhirContext.forR4Cached(); private static IdType ourLastId; private static String ourLastMethod; private static StringType ourLastParam1; private static Patient ourLastParam2; - private static int ourPort; - private static Server ourServer; private static Parameters ourLastResourceParam; + @RegisterExtension + public RestfulServerExtension ourServer = new RestfulServerExtension(ourCtx) + .registerProvider(new PatientProvider()) + .registerProvider(new PlainProvider()) + .withPagingProvider(new FifoMemoryPagingProvider(10).setDefaultPageSize(2)) + .setDefaultResponseEncoding(EncodingEnum.XML); + + @RegisterExtension + private HttpClientExtension ourClient = new HttpClientExtension(); + @BeforeEach public void before() { ourLastParam1 = null; @@ -69,7 +79,7 @@ public class OperationGenericServerR4Test { p.addParameter().setName("PARAM2").setResource(new Patient().setActive(true)); String inParamsStr = ourCtx.newXmlParser().encodeResourceToString(p); - HttpPost httpPost = new HttpPost("http://localhost:" + ourPort + "/Patient/123/$OP_INSTANCE"); + HttpPost httpPost = new HttpPost(ourServer.getBaseUrl() + "/Patient/123/$OP_INSTANCE"); httpPost.setEntity(new StringEntity(inParamsStr, ContentType.create(Constants.CT_FHIR_XML, "UTF-8"))); CloseableHttpResponse status = ourClient.execute(httpPost); try { @@ -99,7 +109,7 @@ public class OperationGenericServerR4Test { p.addParameter().setName("PARAM2").setResource(new Patient().setActive(true)); String inParamsStr = ourCtx.newXmlParser().encodeResourceToString(p); - HttpPost httpPost = new HttpPost("http://localhost:" + ourPort + "/$OP_SERVER"); + HttpPost httpPost = new HttpPost(ourServer.getBaseUrl() + "/$OP_SERVER"); httpPost.setEntity(new StringEntity(inParamsStr, ContentType.create(Constants.CT_FHIR_XML, "UTF-8"))); CloseableHttpResponse status = ourClient.execute(httpPost); try { @@ -126,7 +136,7 @@ public class OperationGenericServerR4Test { p.addParameter().setName("PARAM2").setResource(new Patient().setActive(true)); String inParamsStr = ourCtx.newXmlParser().encodeResourceToString(p); - HttpPost httpPost = new HttpPost("http://localhost:" + ourPort + "/Patient/$OP_TYPE"); + HttpPost httpPost = new HttpPost(ourServer.getBaseUrl() + "/Patient/$OP_TYPE"); httpPost.setEntity(new StringEntity(inParamsStr, ContentType.create(Constants.CT_FHIR_XML, "UTF-8"))); CloseableHttpResponse status = ourClient.execute(httpPost); try { @@ -150,7 +160,7 @@ public class OperationGenericServerR4Test { @Test public void testOperationWithGetUsingParams() throws Exception { - HttpGet httpGet = new HttpGet("http://localhost:" + ourPort + "/Patient/$OP_TYPE?PARAM1=PARAM1val"); + HttpGet httpGet = new HttpGet(ourServer.getBaseUrl() + "/Patient/$OP_TYPE?PARAM1=PARAM1val"); CloseableHttpResponse status = ourClient.execute(httpGet); try { String response = IOUtils.toString(status.getEntity().getContent(), StandardCharsets.UTF_8); @@ -174,7 +184,7 @@ public class OperationGenericServerR4Test { @Test public void testSearchGetsClassifiedAppropriately() throws Exception { - HttpGet httpPost = new HttpGet("http://localhost:" + ourPort + "/Patient"); + HttpGet httpPost = new HttpGet(ourServer.getBaseUrl() + "/Patient"); CloseableHttpResponse status = ourClient.execute(httpPost); try { assertEquals(200, status.getStatusLine().getStatusCode()); @@ -275,35 +285,8 @@ public class OperationGenericServerR4Test { @AfterAll public static void afterClassClearContext() throws Exception { - JettyUtil.closeServer(ourServer); TestUtil.randomizeLocaleAndTimezone(); } - @BeforeAll - public static void beforeClass() throws Exception { - ourCtx = FhirContext.forR4(); - ourServer = new Server(0); - - ServletHandler proxyHandler = new ServletHandler(); - RestfulServer servlet = new RestfulServer(ourCtx); - servlet.setDefaultResponseEncoding(EncodingEnum.XML); - - servlet.setPagingProvider(new FifoMemoryPagingProvider(10).setDefaultPageSize(2)); - - servlet.setFhirContext(ourCtx); - servlet.setResourceProviders(new PatientProvider()); - servlet.setPlainProviders(new PlainProvider()); - ServletHolder servletHolder = new ServletHolder(servlet); - proxyHandler.addServletWithMapping(servletHolder, "/*"); - ourServer.setHandler(proxyHandler); - JettyUtil.startServer(ourServer); - ourPort = JettyUtil.getPortForStartedServer(ourServer); - - PoolingHttpClientConnectionManager connectionManager = new PoolingHttpClientConnectionManager(5000, TimeUnit.MILLISECONDS); - HttpClientBuilder builder = HttpClientBuilder.create(); - builder.setConnectionManager(connectionManager); - ourClient = builder.build(); - - } } diff --git a/hapi-fhir-structures-r4/src/test/java/ca/uhn/fhir/rest/server/OperationServerInvalidDefinitionTest.java b/hapi-fhir-structures-r4/src/test/java/ca/uhn/fhir/rest/server/OperationServerInvalidDefinitionTest.java index ed0c6ed9405..9e41be33e59 100644 --- a/hapi-fhir-structures-r4/src/test/java/ca/uhn/fhir/rest/server/OperationServerInvalidDefinitionTest.java +++ b/hapi-fhir-structures-r4/src/test/java/ca/uhn/fhir/rest/server/OperationServerInvalidDefinitionTest.java @@ -5,11 +5,10 @@ import ca.uhn.fhir.context.FhirContext; import ca.uhn.fhir.model.primitive.StringDt; import ca.uhn.fhir.rest.annotation.Operation; import ca.uhn.fhir.rest.annotation.OperationParam; -import ca.uhn.fhir.rest.api.Constants; import ca.uhn.fhir.rest.server.provider.ProviderConstants; import ca.uhn.fhir.test.utilities.server.RestfulServerExtension; +import org.hl7.fhir.instance.model.api.IBaseResource; import org.hl7.fhir.r4.model.Parameters; -import org.hl7.fhir.r4.model.Patient; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.RegisterExtension; @@ -26,11 +25,11 @@ public class OperationServerInvalidDefinitionTest { class OperationProvider { - @Operation(name = ProviderConstants.OPERATION_MEMBER_MATCH, idempotent = false, returnParameters = { - @OperationParam(name = "MemberIdentifier", type = StringDt.class) + @Operation(name = ProviderConstants.MDM_MATCH, idempotent = false, returnParameters = { + @OperationParam(name = "matchResult", type = StringDt.class) }) - public Parameters patientMemberMatch( - @OperationParam(name = Constants.PARAM_MEMBER_PATIENT, min = 1, max = 1) Patient theMemberPatient + public Parameters mdmMatch( + @OperationParam(name = ProviderConstants.MDM_MATCH_RESOURCE, min = 1, max = 1) IBaseResource theResource ) { return null; } @@ -41,7 +40,7 @@ public class OperationServerInvalidDefinitionTest { myRestfulServerExtension.registerProvider(new OperationProvider()); fail(); } catch (ConfigurationException e) { - assertEquals("HAPI-0288: Failure scanning class OperationProvider: HAPI-0360: Incorrect use of type StringDt as return type for method when theContext is for version R4 in method: public org.hl7.fhir.r4.model.Parameters ca.uhn.fhir.rest.server.OperationServerInvalidDefinitionTest$1OperationProvider.patientMemberMatch(org.hl7.fhir.r4.model.Patient)", e.getMessage()); + assertEquals("HAPI-0288: Failure scanning class OperationProvider: HAPI-0360: Incorrect use of type StringDt as return type for method when theContext is for version R4 in method: public org.hl7.fhir.r4.model.Parameters ca.uhn.fhir.rest.server.OperationServerInvalidDefinitionTest$1OperationProvider.mdmMatch(org.hl7.fhir.instance.model.api.IBaseResource)", e.getMessage()); } } diff --git a/hapi-fhir-structures-r4/src/test/java/ca/uhn/fhir/rest/server/OperationServerR4Test.java b/hapi-fhir-structures-r4/src/test/java/ca/uhn/fhir/rest/server/OperationServerR4Test.java index 3215597a114..20744e7ca0f 100644 --- a/hapi-fhir-structures-r4/src/test/java/ca/uhn/fhir/rest/server/OperationServerR4Test.java +++ b/hapi-fhir-structures-r4/src/test/java/ca/uhn/fhir/rest/server/OperationServerR4Test.java @@ -36,8 +36,8 @@ import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.RegisterExtension; -import javax.servlet.http.HttpServletRequest; -import javax.servlet.http.HttpServletResponse; +import jakarta.servlet.http.HttpServletRequest; +import jakarta.servlet.http.HttpServletResponse; import java.io.IOException; import java.nio.charset.StandardCharsets; import java.util.ArrayList; diff --git a/hapi-fhir-structures-r4/src/test/java/ca/uhn/fhir/rest/server/PagingTest.java b/hapi-fhir-structures-r4/src/test/java/ca/uhn/fhir/rest/server/PagingTest.java index aed0f118745..f3b35670e85 100644 --- a/hapi-fhir-structures-r4/src/test/java/ca/uhn/fhir/rest/server/PagingTest.java +++ b/hapi-fhir-structures-r4/src/test/java/ca/uhn/fhir/rest/server/PagingTest.java @@ -49,7 +49,7 @@ import static org.mockito.Mockito.when; */ public class PagingTest { - private FhirContext ourContext = FhirContext.forR4(); + private FhirContext ourContext = FhirContext.forR4Cached(); @RegisterExtension public RestfulServerExtension myServerExtension = new RestfulServerExtension(ourContext); diff --git a/hapi-fhir-structures-r4/src/test/java/ca/uhn/fhir/rest/server/PagingUsingNamedPagesR4Test.java b/hapi-fhir-structures-r4/src/test/java/ca/uhn/fhir/rest/server/PagingUsingNamedPagesR4Test.java index 6a05c7f0ff2..ce11063307e 100644 --- a/hapi-fhir-structures-r4/src/test/java/ca/uhn/fhir/rest/server/PagingUsingNamedPagesR4Test.java +++ b/hapi-fhir-structures-r4/src/test/java/ca/uhn/fhir/rest/server/PagingUsingNamedPagesR4Test.java @@ -5,7 +5,10 @@ import ca.uhn.fhir.rest.annotation.Search; import ca.uhn.fhir.rest.api.Constants; import ca.uhn.fhir.rest.api.EncodingEnum; import ca.uhn.fhir.rest.api.server.IBundleProvider; +import ca.uhn.fhir.rest.server.interceptor.ExceptionInterceptorMethodTest; +import ca.uhn.fhir.test.utilities.HttpClientExtension; import ca.uhn.fhir.test.utilities.JettyUtil; +import ca.uhn.fhir.test.utilities.server.RestfulServerExtension; import ca.uhn.fhir.util.TestUtil; import ca.uhn.fhir.util.UrlUtil; import org.apache.commons.io.IOUtils; @@ -17,8 +20,8 @@ import org.apache.http.impl.client.CloseableHttpClient; import org.apache.http.impl.client.HttpClientBuilder; import org.apache.http.impl.conn.PoolingHttpClientConnectionManager; import org.eclipse.jetty.server.Server; -import org.eclipse.jetty.servlet.ServletHandler; -import org.eclipse.jetty.servlet.ServletHolder; +import org.eclipse.jetty.ee10.servlet.ServletHandler; +import org.eclipse.jetty.ee10.servlet.ServletHolder; import org.hl7.fhir.instance.model.api.IBaseResource; import org.hl7.fhir.r4.model.Bundle; import org.hl7.fhir.r4.model.Patient; @@ -26,6 +29,7 @@ import org.junit.jupiter.api.AfterAll; import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.RegisterExtension; import java.io.IOException; import java.nio.charset.StandardCharsets; @@ -47,20 +51,25 @@ import static org.mockito.Mockito.when; public class PagingUsingNamedPagesR4Test { private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(PagingUsingNamedPagesR4Test.class); - private static CloseableHttpClient ourClient; - private static FhirContext ourCtx = FhirContext.forR4(); - private static int ourPort; - - private static Server ourServer; - private static RestfulServer servlet; + private static final FhirContext ourCtx = FhirContext.forR4Cached(); private static IBundleProvider ourNextBundleProvider; private IPagingProvider myPagingProvider; + @RegisterExtension + public RestfulServerExtension ourServer = new RestfulServerExtension(ourCtx) + .registerProvider(new DummyPatientResourceProvider()) + .withPagingProvider(new FifoMemoryPagingProvider(100)) + .setDefaultResponseEncoding(EncodingEnum.JSON) + .setDefaultPrettyPrint(false); + + @RegisterExtension + private HttpClientExtension ourClient = new HttpClientExtension(); + @BeforeEach public void before() { myPagingProvider = mock(IPagingProvider.class); when(myPagingProvider.canStoreSearchResults()).thenReturn(true); - servlet.setPagingProvider(myPagingProvider); + ourServer.getRestfulServer().setPagingProvider(myPagingProvider); ourNextBundleProvider = null; } @@ -118,32 +127,32 @@ public class PagingUsingNamedPagesR4Test { Bundle bundle; // Initial search - httpGet = new HttpGet("http://localhost:" + ourPort + "/Patient?_format=xml"); + httpGet = new HttpGet(ourServer.getBaseUrl() + "/Patient?_format=xml"); bundle = executeAndReturnBundle(httpGet, EncodingEnum.XML); linkSelf = bundle.getLink(Constants.LINK_SELF).getUrl(); - assertEquals("http://localhost:" + ourPort + "/Patient?_format=xml", linkSelf); + assertEquals(ourServer.getBaseUrl() + "/Patient?_format=xml", linkSelf); linkNext = bundle.getLink(Constants.LINK_NEXT).getUrl(); - assertEquals("http://localhost:" + ourPort + "?_getpages=SEARCHID0&_pageId=PAGEID1&_format=xml&_bundletype=searchset", linkNext); + assertEquals(ourServer.getBaseUrl() + "?_getpages=SEARCHID0&_pageId=PAGEID1&_format=xml&_bundletype=searchset", linkNext); assertNull(bundle.getLink(Constants.LINK_PREVIOUS)); // Fetch the next page httpGet = new HttpGet(linkNext); bundle = executeAndReturnBundle(httpGet, EncodingEnum.XML); linkSelf = bundle.getLink(Constants.LINK_SELF).getUrl(); - assertEquals("http://localhost:" + ourPort + "?_getpages=SEARCHID0&_pageId=PAGEID1&_format=xml&_bundletype=searchset", linkSelf); + assertEquals(ourServer.getBaseUrl() + "?_getpages=SEARCHID0&_pageId=PAGEID1&_format=xml&_bundletype=searchset", linkSelf); linkNext = bundle.getLink(Constants.LINK_NEXT).getUrl(); - assertEquals("http://localhost:" + ourPort + "?_getpages=SEARCHID0&_pageId=PAGEID2&_format=xml&_bundletype=searchset", linkNext); + assertEquals(ourServer.getBaseUrl() + "?_getpages=SEARCHID0&_pageId=PAGEID2&_format=xml&_bundletype=searchset", linkNext); linkPrev = bundle.getLink(Constants.LINK_PREVIOUS).getUrl(); - assertEquals("http://localhost:" + ourPort + "?_getpages=SEARCHID0&_pageId=PAGEID0&_format=xml&_bundletype=searchset", linkPrev); + assertEquals(ourServer.getBaseUrl() + "?_getpages=SEARCHID0&_pageId=PAGEID0&_format=xml&_bundletype=searchset", linkPrev); // Fetch the next page httpGet = new HttpGet(linkNext); bundle = executeAndReturnBundle(httpGet, EncodingEnum.XML); linkSelf = bundle.getLink(Constants.LINK_SELF).getUrl(); - assertEquals("http://localhost:" + ourPort + "?_getpages=SEARCHID0&_pageId=PAGEID2&_format=xml&_bundletype=searchset", linkSelf); + assertEquals(ourServer.getBaseUrl() + "?_getpages=SEARCHID0&_pageId=PAGEID2&_format=xml&_bundletype=searchset", linkSelf); assertNull(bundle.getLink(Constants.LINK_NEXT)); linkPrev = bundle.getLink(Constants.LINK_PREVIOUS).getUrl(); - assertEquals("http://localhost:" + ourPort + "?_getpages=SEARCHID0&_pageId=PAGEID1&_format=xml&_bundletype=searchset", linkPrev); + assertEquals(ourServer.getBaseUrl() + "?_getpages=SEARCHID0&_pageId=PAGEID1&_format=xml&_bundletype=searchset", linkPrev); } @Test @@ -153,7 +162,7 @@ public class PagingUsingNamedPagesR4Test { when(myPagingProvider.retrieveResultList(any(), nullable(String.class), nullable(String.class))).thenReturn(null); // With ID - HttpGet httpGet = new HttpGet("http://localhost:" + ourPort + "?_getpages=SEARCHID0&_pageId=PAGEID0&_format=xml&_bundletype=FOO" + UrlUtil.escapeUrlParam("\"")); + HttpGet httpGet = new HttpGet(ourServer.getBaseUrl() + "?_getpages=SEARCHID0&_pageId=PAGEID0&_format=xml&_bundletype=FOO" + UrlUtil.escapeUrlParam("\"")); try (CloseableHttpResponse status = ourClient.execute(httpGet)) { String responseContent = IOUtils.toString(status.getEntity().getContent(), StandardCharsets.UTF_8); ourLog.info(responseContent); @@ -162,7 +171,7 @@ public class PagingUsingNamedPagesR4Test { } // Without ID - httpGet = new HttpGet("http://localhost:" + ourPort + "?_getpages=SEARCHID0&_format=xml&_bundletype=FOO" + UrlUtil.escapeUrlParam("\"")); + httpGet = new HttpGet(ourServer.getBaseUrl() + "?_getpages=SEARCHID0&_format=xml&_bundletype=FOO" + UrlUtil.escapeUrlParam("\"")); try (CloseableHttpResponse status = ourClient.execute(httpGet)) { String responseContent = IOUtils.toString(status.getEntity().getContent(), StandardCharsets.UTF_8); ourLog.info(responseContent); @@ -181,7 +190,7 @@ public class PagingUsingNamedPagesR4Test { when(myPagingProvider.retrieveResultList(any(), eq("SEARCHID0"), eq("PAGEID0"))).thenReturn(provider0); // Initial search - HttpGet httpGet = new HttpGet("http://localhost:" + ourPort + "?_getpages=SEARCHID0&_pageId=PAGEID0&_format=xml&_bundletype=FOO" + UrlUtil.escapeUrlParam("\"")); + HttpGet httpGet = new HttpGet(ourServer.getBaseUrl() + "?_getpages=SEARCHID0&_pageId=PAGEID0&_format=xml&_bundletype=FOO" + UrlUtil.escapeUrlParam("\"")); try (CloseableHttpResponse status = ourClient.execute(httpGet)) { String responseContent = IOUtils.toString(status.getEntity().getContent(), StandardCharsets.UTF_8); ourLog.info(responseContent); @@ -197,37 +206,10 @@ public class PagingUsingNamedPagesR4Test { @AfterAll public static void afterClassClearContext() throws Exception { - JettyUtil.closeServer(ourServer); TestUtil.randomizeLocaleAndTimezone(); } - @BeforeAll - public static void beforeClass() throws Exception { - ourServer = new Server(0); - - DummyPatientResourceProvider patientProvider = new DummyPatientResourceProvider(); - - ServletHandler proxyHandler = new ServletHandler(); - servlet = new RestfulServer(ourCtx); - servlet.setDefaultResponseEncoding(EncodingEnum.JSON); - servlet.setPagingProvider(new FifoMemoryPagingProvider(10)); - - servlet.setResourceProviders(patientProvider); - ServletHolder servletHolder = new ServletHolder(servlet); - proxyHandler.addServletWithMapping(servletHolder, "/*"); - ourServer.setHandler(proxyHandler); - JettyUtil.startServer(ourServer); - ourPort = JettyUtil.getPortForStartedServer(ourServer); - - PoolingHttpClientConnectionManager connectionManager = new PoolingHttpClientConnectionManager(5000, TimeUnit.MILLISECONDS); - HttpClientBuilder builder = HttpClientBuilder.create(); - builder.setConnectionManager(connectionManager); - builder.setDefaultSocketConfig(SocketConfig.custom().setSoTimeout(600000).build()); - ourClient = builder.build(); - - } - - public static class DummyPatientResourceProvider implements IResourceProvider { + private static class DummyPatientResourceProvider implements IResourceProvider { @Override diff --git a/hapi-fhir-structures-r4/src/test/java/ca/uhn/fhir/rest/server/PatientResourceProvider.java b/hapi-fhir-structures-r4/src/test/java/ca/uhn/fhir/rest/server/PatientResourceProvider.java index dd278c04bc9..80c9a1f6484 100644 --- a/hapi-fhir-structures-r4/src/test/java/ca/uhn/fhir/rest/server/PatientResourceProvider.java +++ b/hapi-fhir-structures-r4/src/test/java/ca/uhn/fhir/rest/server/PatientResourceProvider.java @@ -29,7 +29,7 @@ public class PatientResourceProvider implements IResourceProvider @Search() public IBundleProvider search( - javax.servlet.http.HttpServletRequest theServletRequest, + jakarta.servlet.http.HttpServletRequest theServletRequest, @Description(shortDefinition="The resource identity") @OptionalParam(name="_id") diff --git a/hapi-fhir-structures-r4/src/test/java/ca/uhn/fhir/rest/server/PlainProviderR4Test.java b/hapi-fhir-structures-r4/src/test/java/ca/uhn/fhir/rest/server/PlainProviderR4Test.java index 1a11ebd4cfd..72599912728 100644 --- a/hapi-fhir-structures-r4/src/test/java/ca/uhn/fhir/rest/server/PlainProviderR4Test.java +++ b/hapi-fhir-structures-r4/src/test/java/ca/uhn/fhir/rest/server/PlainProviderR4Test.java @@ -10,7 +10,10 @@ import ca.uhn.fhir.rest.annotation.Search; import ca.uhn.fhir.rest.annotation.Since; import ca.uhn.fhir.rest.api.EncodingEnum; import ca.uhn.fhir.rest.param.TokenParam; +import ca.uhn.fhir.rest.server.provider.HashMapResourceProvider; +import ca.uhn.fhir.test.utilities.HttpClientExtension; import ca.uhn.fhir.test.utilities.JettyUtil; +import ca.uhn.fhir.test.utilities.server.RestfulServerExtension; import ca.uhn.fhir.util.TestUtil; import org.apache.commons.io.IOUtils; import org.apache.http.HttpResponse; @@ -20,8 +23,8 @@ import org.apache.http.impl.client.CloseableHttpClient; import org.apache.http.impl.client.HttpClientBuilder; import org.apache.http.impl.conn.PoolingHttpClientConnectionManager; import org.eclipse.jetty.server.Server; -import org.eclipse.jetty.servlet.ServletHandler; -import org.eclipse.jetty.servlet.ServletHolder; +import org.eclipse.jetty.ee10.servlet.ServletHandler; +import org.eclipse.jetty.ee10.servlet.ServletHolder; import org.hamcrest.core.IsEqual; import org.hamcrest.core.StringStartsWith; import org.hl7.fhir.instance.model.api.IBaseResource; @@ -31,6 +34,7 @@ import org.hl7.fhir.r4.model.IdType; import org.hl7.fhir.r4.model.Identifier; import org.hl7.fhir.r4.model.InstantType; import org.hl7.fhir.r4.model.IntegerType; +import org.hl7.fhir.r4.model.Observation; import org.hl7.fhir.r4.model.Organization; import org.hl7.fhir.r4.model.Patient; import org.hl7.fhir.r4.model.Resource; @@ -38,6 +42,7 @@ import org.junit.jupiter.api.AfterAll; import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.RegisterExtension; import java.util.ArrayList; import java.util.HashMap; @@ -51,47 +56,26 @@ import static org.junit.jupiter.api.Assertions.assertNull; public class PlainProviderR4Test { - private static FhirContext ourCtx = FhirContext.forR4(); + private static final FhirContext ourCtx = FhirContext.forR4Cached(); private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(PlainProviderR4Test.class); - private CloseableHttpClient myClient; - private int myPort; - private RestfulServer myRestfulServer; - private Server myServer; - @AfterEach - public void after() throws Exception { - JettyUtil.closeServer(myServer); - } + @RegisterExtension + public RestfulServerExtension ourServer = new RestfulServerExtension(ourCtx) + .registerProvider(new HashMapResourceProvider<>(ourCtx, Observation.class)) + .withPagingProvider(new FifoMemoryPagingProvider(100)) + .setDefaultResponseEncoding(EncodingEnum.XML) + .withServletPath("/fhir/context/*"); - @BeforeEach - public void before() throws Exception { - myServer = new Server(0); - - ServletHandler proxyHandler = new ServletHandler(); - ServletHolder servletHolder = new ServletHolder(); - myRestfulServer = new RestfulServer(ourCtx); - myRestfulServer.setDefaultResponseEncoding(EncodingEnum.XML); - servletHolder.setServlet(myRestfulServer); - proxyHandler.addServletWithMapping(servletHolder, "/fhir/context/*"); - myServer.setHandler(proxyHandler); - - PoolingHttpClientConnectionManager connectionManager = new PoolingHttpClientConnectionManager(5000, TimeUnit.MILLISECONDS); - HttpClientBuilder builder = HttpClientBuilder.create(); - - builder.setConnectionManager(connectionManager); - myClient = builder.build(); - - } + @RegisterExtension + private HttpClientExtension ourClient = new HttpClientExtension(); @Test public void testGlobalHistory() throws Exception { GlobalHistoryProvider provider = new GlobalHistoryProvider(); - myRestfulServer.setProviders(provider); - JettyUtil.startServer(myServer); - myPort = JettyUtil.getPortForStartedServer(myServer); + ourServer.registerProvider(provider); - String baseUri = "http://localhost:" + myPort + "/fhir/context"; - HttpResponse status = myClient.execute(new HttpGet(baseUri + "/_history?_since=2012-01-02T00%3A01%3A02&_count=12")); + String baseUri = ourServer.getBaseUrl(); + HttpResponse status = ourClient.execute(new HttpGet(baseUri + "/_history?_since=2012-01-02T00%3A01%3A02&_count=12")); String responseContent = IOUtils.toString(status.getEntity().getContent()); IOUtils.closeQuietly(status.getEntity().getContent()); @@ -103,7 +87,7 @@ public class PlainProviderR4Test { assertThat(provider.myLastSince.getValueAsString(), StringStartsWith.startsWith("2012-01-02T00:01:02")); assertThat(provider.myLastCount.getValueAsString(), IsEqual.equalTo("12")); - status = myClient.execute(new HttpGet(baseUri + "/_history?&_count=12")); + status = ourClient.execute(new HttpGet(baseUri + "/_history?&_count=12")); responseContent = IOUtils.toString(status.getEntity().getContent()); IOUtils.closeQuietly(status.getEntity().getContent()); assertEquals(200, status.getStatusLine().getStatusCode()); @@ -112,7 +96,7 @@ public class PlainProviderR4Test { assertNull(provider.myLastSince); assertThat(provider.myLastCount.getValueAsString(), IsEqual.equalTo("12")); - status =myClient.execute(new HttpGet(baseUri + "/_history?_since=2012-01-02T00%3A01%3A02")); + status =ourClient.execute(new HttpGet(baseUri + "/_history?_since=2012-01-02T00%3A01%3A02")); responseContent = IOUtils.toString(status.getEntity().getContent()); IOUtils.closeQuietly(status.getEntity().getContent()); assertEquals(200, status.getStatusLine().getStatusCode()); @@ -125,12 +109,10 @@ public class PlainProviderR4Test { @Test public void testGlobalHistoryNoParams() throws Exception { GlobalHistoryProvider provider = new GlobalHistoryProvider(); - myRestfulServer.setProviders(provider); - JettyUtil.startServer(myServer); - myPort = JettyUtil.getPortForStartedServer(myServer); + ourServer.registerProvider(provider); - String baseUri = "http://localhost:" + myPort + "/fhir/context"; - CloseableHttpResponse status = myClient.execute(new HttpGet(baseUri + "/_history")); + String baseUri = ourServer.getBaseUrl(); + CloseableHttpResponse status = ourClient.execute(new HttpGet(baseUri + "/_history")); String responseContent = IOUtils.toString(status.getEntity().getContent()); IOUtils.closeQuietly(status.getEntity().getContent()); assertEquals(200, status.getStatusLine().getStatusCode()); @@ -143,14 +125,12 @@ public class PlainProviderR4Test { @Test public void testSearchByParamIdentifier() throws Exception { - myRestfulServer.setProviders(new SearchProvider()); - JettyUtil.startServer(myServer); - myPort = JettyUtil.getPortForStartedServer(myServer); + ourServer.registerProvider(new SearchProvider()); - String baseUri = "http://localhost:" + myPort + "/fhir/context"; + String baseUri = ourServer.getBaseUrl(); String uri = baseUri + "/Patient?identifier=urn:hapitest:mrns%7C00001"; HttpGet httpGet = new HttpGet(uri); - try (CloseableHttpResponse status = myClient.execute(httpGet)) { + try (CloseableHttpResponse status = ourClient.execute(httpGet)) { String responseContent = IOUtils.toString(status.getEntity().getContent()); IOUtils.closeQuietly(status.getEntity().getContent()); diff --git a/hapi-fhir-structures-r4/src/test/java/ca/uhn/fhir/rest/server/PreferTest.java b/hapi-fhir-structures-r4/src/test/java/ca/uhn/fhir/rest/server/PreferTest.java index 45891b4d569..a412d83006a 100644 --- a/hapi-fhir-structures-r4/src/test/java/ca/uhn/fhir/rest/server/PreferTest.java +++ b/hapi-fhir-structures-r4/src/test/java/ca/uhn/fhir/rest/server/PreferTest.java @@ -7,8 +7,11 @@ import ca.uhn.fhir.rest.annotation.IdParam; import ca.uhn.fhir.rest.annotation.ResourceParam; import ca.uhn.fhir.rest.annotation.Update; import ca.uhn.fhir.rest.api.Constants; +import ca.uhn.fhir.rest.api.EncodingEnum; import ca.uhn.fhir.rest.api.MethodOutcome; +import ca.uhn.fhir.test.utilities.HttpClientExtension; import ca.uhn.fhir.test.utilities.JettyUtil; +import ca.uhn.fhir.test.utilities.server.RestfulServerExtension; import ca.uhn.fhir.util.TestUtil; import org.apache.commons.io.IOUtils; import org.apache.http.HttpResponse; @@ -19,8 +22,8 @@ import org.apache.http.impl.client.CloseableHttpClient; import org.apache.http.impl.client.HttpClientBuilder; import org.apache.http.impl.conn.PoolingHttpClientConnectionManager; import org.eclipse.jetty.server.Server; -import org.eclipse.jetty.servlet.ServletHandler; -import org.eclipse.jetty.servlet.ServletHolder; +import org.eclipse.jetty.ee10.servlet.ServletHandler; +import org.eclipse.jetty.ee10.servlet.ServletHolder; import org.hl7.fhir.instance.model.api.IBaseOperationOutcome; import org.hl7.fhir.instance.model.api.IBaseResource; import org.hl7.fhir.r4.model.IdType; @@ -30,6 +33,7 @@ import org.junit.jupiter.api.AfterAll; import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.RegisterExtension; import java.util.concurrent.TimeUnit; @@ -41,14 +45,21 @@ import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertNull; public class PreferTest { - private static CloseableHttpClient ourClient; - private static FhirContext ourCtx = FhirContext.forR4(); + private static final FhirContext ourCtx = FhirContext.forR4Cached(); private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(PreferTest.class); - private static int ourPort; - private static Server ourServer; private static IBaseOperationOutcome ourReturnOperationOutcome; + @RegisterExtension + public RestfulServerExtension ourServer = new RestfulServerExtension(ourCtx) + .registerProvider(new PatientProvider()) + .withPagingProvider(new FifoMemoryPagingProvider(100)) + .setDefaultResponseEncoding(EncodingEnum.XML) + .setDefaultPrettyPrint(false); + + @RegisterExtension + private HttpClientExtension ourClient = new HttpClientExtension(); + @BeforeEach public void before() { ourReturnOperationOutcome = null; @@ -64,7 +75,7 @@ public class PreferTest { oo.addIssue().setDiagnostics("DIAG"); ourReturnOperationOutcome = oo; - HttpPost httpPost = new HttpPost("http://localhost:" + ourPort + "/Patient"); + HttpPost httpPost = new HttpPost(ourServer.getBaseUrl() + "/Patient"); httpPost.addHeader(Constants.HEADER_PREFER, Constants.HEADER_PREFER_RETURN + "=" + Constants.HEADER_PREFER_RETURN_MINIMAL); httpPost.setEntity(new StringEntity(ourCtx.newXmlParser().encodeResourceToString(patient), ContentType.create(Constants.CT_FHIR_XML, "UTF-8"))); @@ -79,8 +90,8 @@ public class PreferTest { assertThat(responseContent, is(emptyOrNullString())); // assertThat(status.getFirstHeader(Constants.HEADER_CONTENT_TYPE).getValue(), not(containsString("fhir"))); assertNull(status.getFirstHeader(Constants.HEADER_CONTENT_TYPE)); - assertEquals("http://localhost:" + ourPort + "/Patient/001/_history/002", status.getFirstHeader("location").getValue()); - assertEquals("http://localhost:" + ourPort + "/Patient/001/_history/002", status.getFirstHeader("content-location").getValue()); + assertEquals(ourServer.getBaseUrl() + "/Patient/001/_history/002", status.getFirstHeader("location").getValue()); + assertEquals(ourServer.getBaseUrl() + "/Patient/001/_history/002", status.getFirstHeader("content-location").getValue()); } @@ -94,7 +105,7 @@ public class PreferTest { Patient patient = new Patient(); patient.addIdentifier().setValue("002"); - HttpPost httpPost = new HttpPost("http://localhost:" + ourPort + "/Patient"); + HttpPost httpPost = new HttpPost(ourServer.getBaseUrl() + "/Patient"); httpPost.addHeader(Constants.HEADER_PREFER, Constants.HEADER_PREFER_RETURN + "=" + Constants.HEADER_PREFER_RETURN_OPERATION_OUTCOME); httpPost.setEntity(new StringEntity(ourCtx.newXmlParser().encodeResourceToString(patient), ContentType.create(Constants.CT_FHIR_XML, "UTF-8"))); @@ -108,8 +119,8 @@ public class PreferTest { assertEquals(Constants.STATUS_HTTP_201_CREATED, status.getStatusLine().getStatusCode()); assertThat(responseContent, containsString("DIAG")); assertEquals("application/xml+fhir;charset=utf-8", status.getFirstHeader(Constants.HEADER_CONTENT_TYPE).getValue().toLowerCase().replace(" ", "")); - assertEquals("http://localhost:" + ourPort + "/Patient/001/_history/002", status.getFirstHeader("location").getValue()); - assertEquals("http://localhost:" + ourPort + "/Patient/001/_history/002", status.getFirstHeader("content-location").getValue()); + assertEquals(ourServer.getBaseUrl() + "/Patient/001/_history/002", status.getFirstHeader("location").getValue()); + assertEquals(ourServer.getBaseUrl() + "/Patient/001/_history/002", status.getFirstHeader("content-location").getValue()); } @@ -119,7 +130,7 @@ public class PreferTest { Patient patient = new Patient(); patient.addIdentifier().setValue("002"); - HttpPost httpPost = new HttpPost("http://localhost:" + ourPort + "/Patient"); + HttpPost httpPost = new HttpPost(ourServer.getBaseUrl() + "/Patient"); httpPost.addHeader(Constants.HEADER_PREFER, Constants.HEADER_PREFER_RETURN + "=" + Constants.HEADER_PREFER_RETURN_REPRESENTATION); httpPost.setEntity(new StringEntity(ourCtx.newXmlParser().encodeResourceToString(patient), ContentType.create(Constants.CT_FHIR_XML, "UTF-8"))); @@ -133,8 +144,8 @@ public class PreferTest { assertEquals(Constants.STATUS_HTTP_201_CREATED, status.getStatusLine().getStatusCode()); assertThat(status.getFirstHeader(Constants.HEADER_CONTENT_TYPE).getValue(), containsString(Constants.CT_FHIR_XML)); assertEquals("", responseContent); - assertEquals("http://localhost:" + ourPort + "/Patient/001/_history/002", status.getFirstHeader("location").getValue()); - assertEquals("http://localhost:" + ourPort + "/Patient/001/_history/002", status.getFirstHeader("content-location").getValue()); + assertEquals(ourServer.getBaseUrl() + "/Patient/001/_history/002", status.getFirstHeader("location").getValue()); + assertEquals(ourServer.getBaseUrl() + "/Patient/001/_history/002", status.getFirstHeader("content-location").getValue()); } @@ -144,7 +155,7 @@ public class PreferTest { Patient patient = new Patient(); patient.addIdentifier().setValue("002"); - HttpPost httpPost = new HttpPost("http://localhost:" + ourPort + "/Patient"); + HttpPost httpPost = new HttpPost(ourServer.getBaseUrl() + "/Patient"); httpPost.setEntity(new StringEntity(ourCtx.newXmlParser().encodeResourceToString(patient), ContentType.create(Constants.CT_FHIR_XML, "UTF-8"))); HttpResponse status = ourClient.execute(httpPost); @@ -155,38 +166,16 @@ public class PreferTest { ourLog.info("Response was:\n{}", responseContent); assertEquals(201, status.getStatusLine().getStatusCode()); - assertEquals("http://localhost:" + ourPort + "/Patient/001/_history/002", status.getFirstHeader("location").getValue()); - assertEquals("http://localhost:" + ourPort + "/Patient/001/_history/002", status.getFirstHeader("content-location").getValue()); + assertEquals(ourServer.getBaseUrl() + "/Patient/001/_history/002", status.getFirstHeader("location").getValue()); + assertEquals(ourServer.getBaseUrl() + "/Patient/001/_history/002", status.getFirstHeader("content-location").getValue()); } @AfterAll public static void afterClassClearContext() throws Exception { - JettyUtil.closeServer(ourServer); TestUtil.randomizeLocaleAndTimezone(); } - @BeforeAll - public static void beforeClass() throws Exception { - ourServer = new Server(0); - - PatientProvider patientProvider = new PatientProvider(); - - ServletHandler proxyHandler = new ServletHandler(); - RestfulServer servlet = new RestfulServer(ourCtx); - servlet.setResourceProviders(patientProvider); - ServletHolder servletHolder = new ServletHolder(servlet); - proxyHandler.addServletWithMapping(servletHolder, "/*"); - ourServer.setHandler(proxyHandler); - JettyUtil.startServer(ourServer); - ourPort = JettyUtil.getPortForStartedServer(ourServer); - - PoolingHttpClientConnectionManager connectionManager = new PoolingHttpClientConnectionManager(5000, TimeUnit.MILLISECONDS); - HttpClientBuilder builder = HttpClientBuilder.create(); - builder.setConnectionManager(connectionManager); - ourClient = builder.build(); - - } public static class PatientProvider implements IResourceProvider { diff --git a/hapi-fhir-structures-r4/src/test/java/ca/uhn/fhir/rest/server/ResponseCodeModifyingResourceProviderTest.java b/hapi-fhir-structures-r4/src/test/java/ca/uhn/fhir/rest/server/ResponseCodeModifyingResourceProviderTest.java index c4526ff9ee6..06be8295137 100644 --- a/hapi-fhir-structures-r4/src/test/java/ca/uhn/fhir/rest/server/ResponseCodeModifyingResourceProviderTest.java +++ b/hapi-fhir-structures-r4/src/test/java/ca/uhn/fhir/rest/server/ResponseCodeModifyingResourceProviderTest.java @@ -15,7 +15,7 @@ import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.RegisterExtension; -import javax.servlet.ServletRequest; +import jakarta.servlet.ServletRequest; import static ca.uhn.fhir.rest.api.Constants.STATUS_HTTP_202_ACCEPTED; import static org.junit.jupiter.api.Assertions.assertEquals; diff --git a/hapi-fhir-structures-r4/src/test/java/ca/uhn/fhir/rest/server/RestfulServerTest.java b/hapi-fhir-structures-r4/src/test/java/ca/uhn/fhir/rest/server/RestfulServerTest.java index 7624f4c23d1..d7fdff76d8d 100644 --- a/hapi-fhir-structures-r4/src/test/java/ca/uhn/fhir/rest/server/RestfulServerTest.java +++ b/hapi-fhir-structures-r4/src/test/java/ca/uhn/fhir/rest/server/RestfulServerTest.java @@ -23,8 +23,8 @@ import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.ExtendWith; import org.mockito.junit.jupiter.MockitoExtension; -import javax.servlet.ServletException; -import javax.servlet.http.HttpServletRequest; +import jakarta.servlet.ServletException; +import jakarta.servlet.http.HttpServletRequest; import java.io.Serializable; import java.util.List; diff --git a/hapi-fhir-structures-r4/src/test/java/ca/uhn/fhir/rest/server/SearchBundleProviderWithNoSizeR4Test.java b/hapi-fhir-structures-r4/src/test/java/ca/uhn/fhir/rest/server/SearchBundleProviderWithNoSizeR4Test.java index 9034759aa55..77defe6e41c 100644 --- a/hapi-fhir-structures-r4/src/test/java/ca/uhn/fhir/rest/server/SearchBundleProviderWithNoSizeR4Test.java +++ b/hapi-fhir-structures-r4/src/test/java/ca/uhn/fhir/rest/server/SearchBundleProviderWithNoSizeR4Test.java @@ -2,10 +2,14 @@ package ca.uhn.fhir.rest.server; import ca.uhn.fhir.context.FhirContext; import ca.uhn.fhir.rest.annotation.Search; +import ca.uhn.fhir.rest.api.EncodingEnum; import ca.uhn.fhir.rest.api.server.IBundleProvider; import ca.uhn.fhir.rest.param.TokenAndListParam; +import ca.uhn.fhir.rest.server.interceptor.ExceptionInterceptorMethodTest; import ca.uhn.fhir.rest.server.method.ResponsePage; +import ca.uhn.fhir.test.utilities.HttpClientExtension; import ca.uhn.fhir.test.utilities.JettyUtil; +import ca.uhn.fhir.test.utilities.server.RestfulServerExtension; import ca.uhn.fhir.util.TestUtil; import com.google.common.collect.Lists; import org.apache.commons.io.IOUtils; @@ -15,8 +19,8 @@ import org.apache.http.impl.client.CloseableHttpClient; import org.apache.http.impl.client.HttpClientBuilder; import org.apache.http.impl.conn.PoolingHttpClientConnectionManager; import org.eclipse.jetty.server.Server; -import org.eclipse.jetty.servlet.ServletHandler; -import org.eclipse.jetty.servlet.ServletHolder; +import org.eclipse.jetty.ee10.servlet.ServletHandler; +import org.eclipse.jetty.ee10.servlet.ServletHolder; import org.hl7.fhir.instance.model.api.IBaseResource; import org.hl7.fhir.r4.model.Bundle; import org.hl7.fhir.r4.model.Bundle.BundleLinkComponent; @@ -25,6 +29,7 @@ import org.junit.jupiter.api.AfterAll; import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.RegisterExtension; import org.mockito.invocation.InvocationOnMock; import org.mockito.stubbing.Answer; @@ -42,15 +47,20 @@ import static org.mockito.Mockito.when; public class SearchBundleProviderWithNoSizeR4Test { - private static CloseableHttpClient ourClient; - private static FhirContext ourCtx = FhirContext.forR4(); + private static final FhirContext ourCtx = FhirContext.forR4Cached(); private static TokenAndListParam ourIdentifiers; private static IBundleProvider ourLastBundleProvider; private static String ourLastMethod; private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(SearchBundleProviderWithNoSizeR4Test.class); - private static int ourPort; - private static Server ourServer; + @RegisterExtension + public RestfulServerExtension ourServer = new RestfulServerExtension(ourCtx) + .registerProvider(new DummyPatientResourceProvider()) + .withPagingProvider(new FifoMemoryPagingProvider(100)) + .setDefaultResponseEncoding(EncodingEnum.XML); + + @RegisterExtension + private HttpClientExtension ourClient = new HttpClientExtension(); @BeforeEach public void before() { @@ -86,7 +96,7 @@ public class SearchBundleProviderWithNoSizeR4Test { BundleLinkComponent linkNext; try { - httpGet = new HttpGet("http://localhost:" + ourPort + "/Patient?_format=json"); + httpGet = new HttpGet(ourServer.getBaseUrl() + "/Patient?_format=json"); status = ourClient.execute(httpGet); String responseContent = IOUtils.toString(status.getEntity().getContent(), StandardCharsets.UTF_8); ourLog.info(responseContent); @@ -146,33 +156,9 @@ public class SearchBundleProviderWithNoSizeR4Test { @AfterAll public static void afterClassClearContext() throws Exception { - JettyUtil.closeServer(ourServer); TestUtil.randomizeLocaleAndTimezone(); } - @BeforeAll - public static void beforeClass() throws Exception { - ourServer = new Server(0); - - DummyPatientResourceProvider patientProvider = new DummyPatientResourceProvider(); - - ServletHandler proxyHandler = new ServletHandler(); - RestfulServer servlet = new RestfulServer(ourCtx); - servlet.setPagingProvider(new FifoMemoryPagingProvider(10)); - - servlet.setResourceProviders(patientProvider); - ServletHolder servletHolder = new ServletHolder(servlet); - proxyHandler.addServletWithMapping(servletHolder, "/*"); - ourServer.setHandler(proxyHandler); - JettyUtil.startServer(ourServer); - ourPort = JettyUtil.getPortForStartedServer(ourServer); - - PoolingHttpClientConnectionManager connectionManager = new PoolingHttpClientConnectionManager(5000, TimeUnit.MILLISECONDS); - HttpClientBuilder builder = HttpClientBuilder.create(); - builder.setConnectionManager(connectionManager); - ourClient = builder.build(); - - } public static class DummyPatientResourceProvider implements IResourceProvider { diff --git a/hapi-fhir-structures-r4/src/test/java/ca/uhn/fhir/rest/server/SearchHasParamR4Test.java b/hapi-fhir-structures-r4/src/test/java/ca/uhn/fhir/rest/server/SearchHasParamR4Test.java index 3235d7342e4..31eeb8243fd 100644 --- a/hapi-fhir-structures-r4/src/test/java/ca/uhn/fhir/rest/server/SearchHasParamR4Test.java +++ b/hapi-fhir-structures-r4/src/test/java/ca/uhn/fhir/rest/server/SearchHasParamR4Test.java @@ -3,10 +3,13 @@ package ca.uhn.fhir.rest.server; import ca.uhn.fhir.context.FhirContext; import ca.uhn.fhir.rest.annotation.OptionalParam; import ca.uhn.fhir.rest.annotation.Search; +import ca.uhn.fhir.rest.api.EncodingEnum; import ca.uhn.fhir.rest.param.HasAndListParam; import ca.uhn.fhir.rest.param.HasParam; import ca.uhn.fhir.rest.param.TokenParam; +import ca.uhn.fhir.test.utilities.HttpClientExtension; import ca.uhn.fhir.test.utilities.JettyUtil; +import ca.uhn.fhir.test.utilities.server.RestfulServerExtension; import ca.uhn.fhir.util.TestUtil; import com.google.common.base.Charsets; import org.apache.commons.io.IOUtils; @@ -16,8 +19,8 @@ import org.apache.http.impl.client.CloseableHttpClient; import org.apache.http.impl.client.HttpClientBuilder; import org.apache.http.impl.conn.PoolingHttpClientConnectionManager; import org.eclipse.jetty.server.Server; -import org.eclipse.jetty.servlet.ServletHandler; -import org.eclipse.jetty.servlet.ServletHolder; +import org.eclipse.jetty.ee10.servlet.ServletHandler; +import org.eclipse.jetty.ee10.servlet.ServletHolder; import org.hl7.fhir.instance.model.api.IBaseResource; import org.hl7.fhir.r4.model.HumanName; import org.hl7.fhir.r4.model.Patient; @@ -25,6 +28,7 @@ import org.junit.jupiter.api.AfterAll; import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.RegisterExtension; import java.util.ArrayList; import java.util.List; @@ -34,14 +38,20 @@ import static org.junit.jupiter.api.Assertions.assertEquals; public class SearchHasParamR4Test { - private static CloseableHttpClient ourClient; - private static FhirContext ourCtx = FhirContext.forR4(); + private static final FhirContext ourCtx = FhirContext.forR4Cached(); private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(SearchHasParamR4Test.class); - private static int ourPort; - private static Server ourServer; private static String ourLastMethod; private static HasAndListParam ourLastParam; + @RegisterExtension + public RestfulServerExtension ourServer = new RestfulServerExtension(ourCtx) + .registerProvider(new DummyPatientResourceProvider()) + .withPagingProvider(new FifoMemoryPagingProvider(10)) + .setDefaultResponseEncoding(EncodingEnum.XML); + + @RegisterExtension + private HttpClientExtension ourClient = new HttpClientExtension(); + @BeforeEach public void before() { ourLastMethod = null; @@ -50,7 +60,7 @@ public class SearchHasParamR4Test { @Test public void testSearch() throws Exception { - HttpGet httpGet = new HttpGet("http://localhost:" + ourPort + "/Patient?_has:Encounter:patient:type=SURG"); + HttpGet httpGet = new HttpGet(ourServer.getBaseUrl() + "/Patient?_has:Encounter:patient:type=SURG"); HttpResponse status = ourClient.execute(httpGet); String responseContent = IOUtils.toString(status.getEntity().getContent(), Charsets.UTF_8); IOUtils.closeQuietly(status.getEntity().getContent()); @@ -67,34 +77,9 @@ public class SearchHasParamR4Test { @AfterAll public static void afterClassClearContext() throws Exception { - JettyUtil.closeServer(ourServer); TestUtil.randomizeLocaleAndTimezone(); } - @BeforeAll - public static void beforeClass() throws Exception { - ourServer = new Server(0); - - DummyPatientResourceProvider patientProvider = new DummyPatientResourceProvider(); - - ServletHandler proxyHandler = new ServletHandler(); - RestfulServer servlet = new RestfulServer(ourCtx); - servlet.setPagingProvider(new FifoMemoryPagingProvider(10)); - - servlet.setResourceProviders(patientProvider); - ServletHolder servletHolder = new ServletHolder(servlet); - proxyHandler.addServletWithMapping(servletHolder, "/*"); - ourServer.setHandler(proxyHandler); - JettyUtil.startServer(ourServer); - ourPort = JettyUtil.getPortForStartedServer(ourServer); - - PoolingHttpClientConnectionManager connectionManager = new PoolingHttpClientConnectionManager(5000, TimeUnit.MILLISECONDS); - HttpClientBuilder builder = HttpClientBuilder.create(); - builder.setConnectionManager(connectionManager); - ourClient = builder.build(); - - } - public static class DummyPatientResourceProvider implements IResourceProvider { @Override diff --git a/hapi-fhir-structures-r4/src/test/java/ca/uhn/fhir/rest/server/SearchR4Test.java b/hapi-fhir-structures-r4/src/test/java/ca/uhn/fhir/rest/server/SearchR4Test.java index 198285c87e5..4bf2de0a10e 100644 --- a/hapi-fhir-structures-r4/src/test/java/ca/uhn/fhir/rest/server/SearchR4Test.java +++ b/hapi-fhir-structures-r4/src/test/java/ca/uhn/fhir/rest/server/SearchR4Test.java @@ -38,6 +38,7 @@ import org.hl7.fhir.r4.model.OperationOutcome; import org.hl7.fhir.r4.model.Patient; import org.hl7.fhir.r4.model.Reference; import org.junit.jupiter.api.AfterAll; +import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; @@ -81,6 +82,11 @@ public class SearchR4Test { myCtx.setNarrativeGenerator(new DefaultThymeleafNarrativeGenerator()); } + @AfterEach + public void after() { + myCtx.setNarrativeGenerator(null); + } + private Bundle executeSearchAndValidateHasLinkNext(HttpGet httpGet, EncodingEnum theExpectEncoding) throws IOException { Bundle bundle = executeSearch(httpGet, theExpectEncoding); String linkNext = bundle.getLink(Constants.LINK_NEXT).getUrl(); diff --git a/hapi-fhir-structures-r4/src/test/java/ca/uhn/fhir/rest/server/SearchSearchServerR4Test.java b/hapi-fhir-structures-r4/src/test/java/ca/uhn/fhir/rest/server/SearchSearchServerR4Test.java index 2698f569afb..7f670af8b28 100644 --- a/hapi-fhir-structures-r4/src/test/java/ca/uhn/fhir/rest/server/SearchSearchServerR4Test.java +++ b/hapi-fhir-structures-r4/src/test/java/ca/uhn/fhir/rest/server/SearchSearchServerR4Test.java @@ -20,7 +20,9 @@ import ca.uhn.fhir.rest.param.StringOrListParam; import ca.uhn.fhir.rest.param.StringParam; import ca.uhn.fhir.rest.param.TokenOrListParam; import ca.uhn.fhir.rest.param.TokenParam; +import ca.uhn.fhir.test.utilities.HttpClientExtension; import ca.uhn.fhir.test.utilities.JettyUtil; +import ca.uhn.fhir.test.utilities.server.RestfulServerExtension; import ca.uhn.fhir.util.BundleUtil; import ca.uhn.fhir.util.TestUtil; import ca.uhn.fhir.util.UrlUtil; @@ -38,17 +40,19 @@ import org.apache.http.impl.client.HttpClientBuilder; import org.apache.http.impl.conn.PoolingHttpClientConnectionManager; import org.apache.http.message.BasicNameValuePair; import org.eclipse.jetty.server.Server; -import org.eclipse.jetty.servlet.ServletHandler; -import org.eclipse.jetty.servlet.ServletHolder; +import org.eclipse.jetty.ee10.servlet.ServletHandler; +import org.eclipse.jetty.ee10.servlet.ServletHolder; import org.hl7.fhir.r4.model.BaseResource; import org.hl7.fhir.r4.model.Bundle; import org.hl7.fhir.r4.model.IdType; import org.hl7.fhir.r4.model.Observation; import org.hl7.fhir.r4.model.Patient; import org.junit.jupiter.api.AfterAll; +import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.RegisterExtension; import java.nio.charset.Charset; import java.util.ArrayList; @@ -72,27 +76,39 @@ import static org.junit.jupiter.api.Assertions.assertTrue; public class SearchSearchServerR4Test { - private static CloseableHttpClient ourClient; - private static final FhirContext ourCtx = FhirContext.forR4(); + private static final FhirContext ourCtx = FhirContext.forR4Cached(); private static IServerAddressStrategy ourDefaultAddressStrategy; private static StringAndListParam ourLastAndList; private static Set ourLastIncludes; private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(SearchSearchServerR4Test.class); - private static int ourPort; - private static Server ourServer; - private static RestfulServer ourServlet; - @BeforeEach + @RegisterExtension + public static RestfulServerExtension ourServer = new RestfulServerExtension(ourCtx) + .registerProvider(new DummyPatientResourceProvider()) + .registerProvider(new DummyObservationResourceProvider()) + .withPagingProvider(new FifoMemoryPagingProvider(10).setDefaultPageSize(10)) + .setDefaultResponseEncoding(EncodingEnum.XML); + + @RegisterExtension + public static HttpClientExtension ourClient = new HttpClientExtension(); + + @BeforeEach public void before() { - ourServlet.setServerAddressStrategy(ourDefaultAddressStrategy); - ourLastIncludes = null; + ourServer.setServerAddressStrategy(new IncomingRequestAddressStrategy()); + ourLastIncludes = null; ourLastAndList = null; + ourCtx.setNarrativeGenerator(new DefaultThymeleafNarrativeGenerator()); + } + + @AfterEach + public void after() { + ourCtx.setNarrativeGenerator(null); } @Test public void testEncodeConvertsReferencesToRelative() throws Exception { - HttpGet httpGet = new HttpGet("http://localhost:" + ourPort + "/Patient?_query=searchWithRef"); + HttpGet httpGet = new HttpGet(ourServer.getBaseUrl() + "/Patient?_query=searchWithRef"); HttpResponse status = ourClient.execute(httpGet); String responseContent = IOUtils.toString(status.getEntity().getContent(), Charset.defaultCharset()); IOUtils.closeQuietly(status.getEntity().getContent()); @@ -110,7 +126,7 @@ public class SearchSearchServerR4Test { @Test public void testGetPagesWithPost() throws Exception { - HttpPost httpPost = new HttpPost("http://localhost:" + ourPort); + HttpPost httpPost = new HttpPost(ourServer.getBaseUrl()); List parameters = Collections.singletonList(new BasicNameValuePair("_getpages", "AAA")); httpPost.setEntity(new UrlEncodedFormEntity(parameters)); @@ -124,7 +140,7 @@ public class SearchSearchServerR4Test { @Test public void testOmitEmptyOptionalParam() throws Exception { - HttpGet httpGet = new HttpGet("http://localhost:" + ourPort + "/Patient?_id="); + HttpGet httpGet = new HttpGet(ourServer.getBaseUrl() + "/Patient?_id="); HttpResponse status = ourClient.execute(httpGet); String responseContent = IOUtils.toString(status.getEntity().getContent(), Charset.defaultCharset()); IOUtils.closeQuietly(status.getEntity().getContent()); @@ -139,8 +155,7 @@ public class SearchSearchServerR4Test { @Test public void testParseEscapedValues() throws Exception { - String b = "http://localhost:" + - ourPort + + String b = ourServer.getBaseUrl() + "/Patient?" + escapeUrlParam("findPatientWithAndList") + '=' + escapeUrlParam("NE\\,NE,NE\\,NE") + '&' + escapeUrlParam("findPatientWithAndList") + '=' + escapeUrlParam("NE\\\\NE") + '&' + @@ -169,7 +184,7 @@ public class SearchSearchServerR4Test { @Test public void testReturnLinks() throws Exception { - HttpGet httpGet = new HttpGet("http://localhost:" + ourPort + "/Patient?_query=findWithLinks"); + HttpGet httpGet = new HttpGet(ourServer.getBaseUrl() + "/Patient?_query=findWithLinks"); CloseableHttpResponse status = ourClient.execute(httpGet); String responseContent = IOUtils.toString(status.getEntity().getContent(), Charset.defaultCharset()); @@ -188,9 +203,9 @@ public class SearchSearchServerR4Test { */ @Test public void testReturnLinksWithAddressStrategy() throws Exception { - ourServlet.setServerAddressStrategy(new HardcodedServerAddressStrategy("https://blah.com/base")); + ourServer.setServerAddressStrategy(new HardcodedServerAddressStrategy("https://blah.com/base")); - HttpGet httpGet = new HttpGet("http://localhost:" + ourPort + "/Patient?_query=findWithLinks"); + HttpGet httpGet = new HttpGet(ourServer.getBaseUrl() + "/Patient?_query=findWithLinks"); CloseableHttpResponse status = ourClient.execute(httpGet); String responseContent = IOUtils.toString(status.getEntity().getContent(), Charset.defaultCharset()); @@ -214,7 +229,7 @@ public class SearchSearchServerR4Test { * Load the second page */ String urlPart = linkNext.substring(linkNext.indexOf('?')); - String link = "http://localhost:" + ourPort + urlPart; + String link = ourServer.getBaseUrl() + urlPart; httpGet = new HttpGet(link); status = ourClient.execute(httpGet); @@ -235,7 +250,7 @@ public class SearchSearchServerR4Test { @Test public void testSearchById() throws Exception { - HttpGet httpGet = new HttpGet("http://localhost:" + ourPort + "/Patient?_id=aaa"); + HttpGet httpGet = new HttpGet(ourServer.getBaseUrl() + "/Patient?_id=aaa"); HttpResponse status = ourClient.execute(httpGet); String responseContent = IOUtils.toString(status.getEntity().getContent(), Charset.defaultCharset()); IOUtils.closeQuietly(status.getEntity().getContent()); @@ -249,7 +264,7 @@ public class SearchSearchServerR4Test { @Test public void testSearchByIdUsingClient() { - IGenericClient client = ourCtx.newRestfulGenericClient("http://localhost:" + ourPort); + IGenericClient client = ourCtx.newRestfulGenericClient(ourServer.getBaseUrl()); Bundle bundle = client .search() @@ -265,7 +280,7 @@ public class SearchSearchServerR4Test { @Test public void testSearchByPost() throws Exception { - HttpPost filePost = new HttpPost("http://localhost:" + ourPort + "/Patient/_search"); + HttpPost filePost = new HttpPost(ourServer.getBaseUrl() + "/Patient/_search"); // add parameters to the post method List parameters = new ArrayList<>(); @@ -290,7 +305,7 @@ public class SearchSearchServerR4Test { */ @Test public void testSearchByPostWithInvalidPostUrl() throws Exception { - HttpPost filePost = new HttpPost("http://localhost:" + ourPort + "/Patient?name=Central"); // should end with + HttpPost filePost = new HttpPost(ourServer.getBaseUrl() + "/Patient?name=Central"); // should end with // _search // add parameters to the post method @@ -314,7 +329,7 @@ public class SearchSearchServerR4Test { */ @Test public void testSearchByPostWithMissingContentType() throws Exception { - HttpPost filePost = new HttpPost("http://localhost:" + ourPort + "/Patient?name=Central"); // should end with + HttpPost filePost = new HttpPost(ourServer.getBaseUrl() + "/Patient?name=Central"); // should end with // _search HttpEntity sendentity = new ByteArrayEntity(new byte[] { 1, 2, 3, 4 }); @@ -333,7 +348,7 @@ public class SearchSearchServerR4Test { */ @Test public void testSearchByPostWithParamsInBodyAndUrl() throws Exception { - HttpPost filePost = new HttpPost("http://localhost:" + ourPort + "/Patient/_search?name=Central"); + HttpPost filePost = new HttpPost(ourServer.getBaseUrl() + "/Patient/_search?name=Central"); // add parameters to the post method List parameters = new ArrayList<>(); @@ -359,7 +374,7 @@ public class SearchSearchServerR4Test { @Test public void testSearchCompartment() throws Exception { - HttpGet httpGet = new HttpGet("http://localhost:" + ourPort + "/Patient/123/fooCompartment"); + HttpGet httpGet = new HttpGet(ourServer.getBaseUrl() + "/Patient/123/fooCompartment"); HttpResponse status = ourClient.execute(httpGet); String responseContent = IOUtils.toString(status.getEntity().getContent(), Charset.defaultCharset()); ourLog.info(responseContent); @@ -375,7 +390,7 @@ public class SearchSearchServerR4Test { @Test public void testSearchGetWithUnderscoreSearch() throws Exception { - HttpGet httpGet = new HttpGet("http://localhost:" + ourPort + "/Observation/_search?subject%3APatient=100&name=3141-9%2C8302-2%2C8287-5%2C39156-5"); + HttpGet httpGet = new HttpGet(ourServer.getBaseUrl() + "/Observation/_search?subject%3APatient=100&name=3141-9%2C8302-2%2C8287-5%2C39156-5"); HttpResponse status = ourClient.execute(httpGet); String responseContent = IOUtils.toString(status.getEntity().getContent(), Charset.defaultCharset()); @@ -395,7 +410,7 @@ public class SearchSearchServerR4Test { @Test public void testSearchIncludesParametersIncludes() throws Exception { - HttpGet httpGet = new HttpGet("http://localhost:" + ourPort + "/Patient?_query=searchIncludes&_include=foo&_include:recurse=bar"); + HttpGet httpGet = new HttpGet(ourServer.getBaseUrl() + "/Patient?_query=searchIncludes&_include=foo&_include:recurse=bar"); CloseableHttpResponse status = ourClient.execute(httpGet); IOUtils.toString(status.getEntity().getContent(), Charset.defaultCharset()); @@ -408,7 +423,7 @@ public class SearchSearchServerR4Test { @Test public void testSearchIncludesParametersIncludesList() throws Exception { - HttpGet httpGet = new HttpGet("http://localhost:" + ourPort + "/Patient?_query=searchIncludesList&_include=foo&_include:recurse=bar"); + HttpGet httpGet = new HttpGet(ourServer.getBaseUrl() + "/Patient?_query=searchIncludesList&_include=foo&_include:recurse=bar"); CloseableHttpResponse status = ourClient.execute(httpGet); IOUtils.toString(status.getEntity().getContent(), Charset.defaultCharset()); @@ -421,7 +436,7 @@ public class SearchSearchServerR4Test { @Test public void testSearchIncludesParametersNone() throws Exception { - HttpGet httpGet = new HttpGet("http://localhost:" + ourPort + "/Patient?_query=searchIncludes"); + HttpGet httpGet = new HttpGet(ourServer.getBaseUrl() + "/Patient?_query=searchIncludes"); CloseableHttpResponse status = ourClient.execute(httpGet); IOUtils.toString(status.getEntity().getContent(), Charset.defaultCharset()); @@ -433,7 +448,7 @@ public class SearchSearchServerR4Test { @Test public void testSearchWithOrList() throws Exception { - HttpGet httpGet = new HttpGet("http://localhost:" + ourPort + "/Patient?findPatientWithOrList=aaa,bbb"); + HttpGet httpGet = new HttpGet(ourServer.getBaseUrl() + "/Patient?findPatientWithOrList=aaa,bbb"); HttpResponse status = ourClient.execute(httpGet); String responseContent = IOUtils.toString(status.getEntity().getContent(), Charset.defaultCharset()); IOUtils.closeQuietly(status.getEntity().getContent()); @@ -449,7 +464,7 @@ public class SearchSearchServerR4Test { @Test public void testSearchWithTokenParameter() throws Exception { String token = UrlUtil.escapeUrlParam("http://www.dmix.gov/vista/2957|301"); - HttpGet httpGet = new HttpGet("http://localhost:" + ourPort + "/Patient?tokenParam=" + token); + HttpGet httpGet = new HttpGet(ourServer.getBaseUrl() + "/Patient?tokenParam=" + token); HttpResponse status = ourClient.execute(httpGet); String responseContent = IOUtils.toString(status.getEntity().getContent(), Charset.defaultCharset()); IOUtils.closeQuietly(status.getEntity().getContent()); @@ -464,7 +479,7 @@ public class SearchSearchServerR4Test { @Test public void testSpecificallyNamedQueryGetsPrecedence() throws Exception { - HttpGet httpGet = new HttpGet("http://localhost:" + ourPort + "/Patient?AAA=123"); + HttpGet httpGet = new HttpGet(ourServer.getBaseUrl() + "/Patient?AAA=123"); HttpResponse status = ourClient.execute(httpGet); String responseContent = IOUtils.toString(status.getEntity().getContent(), Charset.defaultCharset()); @@ -478,7 +493,7 @@ public class SearchSearchServerR4Test { // Now the named query - httpGet = new HttpGet("http://localhost:" + ourPort + "/Patient?_query=findPatientByAAA&AAA=123"); + httpGet = new HttpGet(ourServer.getBaseUrl() + "/Patient?_query=findPatientByAAA&AAA=123"); status = ourClient.execute(httpGet); responseContent = IOUtils.toString(status.getEntity().getContent(), Charset.defaultCharset()); @@ -493,37 +508,9 @@ public class SearchSearchServerR4Test { @AfterAll public static void afterClassClearContext() throws Exception { - JettyUtil.closeServer(ourServer); TestUtil.randomizeLocaleAndTimezone(); } - @BeforeAll - public static void beforeClass() throws Exception { - ourServer = new Server(0); - - DummyPatientResourceProvider patientProvider = new DummyPatientResourceProvider(); - - ServletHandler proxyHandler = new ServletHandler(); - ourServlet = new RestfulServer(ourCtx); - ourServlet.getFhirContext().setNarrativeGenerator(new DefaultThymeleafNarrativeGenerator()); - ourServlet.setDefaultResponseEncoding(EncodingEnum.XML); - ourServlet.setPagingProvider(new FifoMemoryPagingProvider(10).setDefaultPageSize(10)); - - ourServlet.setResourceProviders(patientProvider, new DummyObservationResourceProvider()); - ServletHolder servletHolder = new ServletHolder(ourServlet); - proxyHandler.addServletWithMapping(servletHolder, "/*"); - ourServer.setHandler(proxyHandler); - JettyUtil.startServer(ourServer); - ourPort = JettyUtil.getPortForStartedServer(ourServer); - - PoolingHttpClientConnectionManager connectionManager = new PoolingHttpClientConnectionManager(5000, TimeUnit.MILLISECONDS); - HttpClientBuilder builder = HttpClientBuilder.create(); - builder.setConnectionManager(connectionManager); - ourClient = builder.build(); - - ourDefaultAddressStrategy = ourServlet.getServerAddressStrategy(); - } - public static class DummyObservationResourceProvider implements IResourceProvider { @Override @@ -675,7 +662,7 @@ public class SearchSearchServerR4Test { public Patient searchWithRef() { Patient patient = new Patient(); patient.setId("Patient/1/_history/1"); - patient.getManagingOrganization().setReference("http://localhost:" + ourPort + "/Organization/555/_history/666"); + patient.getManagingOrganization().setReference(ourServer.getBaseUrl() + "/Organization/555/_history/666"); return patient; } diff --git a/hapi-fhir-structures-r4/src/test/java/ca/uhn/fhir/rest/server/SearchSortR4Test.java b/hapi-fhir-structures-r4/src/test/java/ca/uhn/fhir/rest/server/SearchSortR4Test.java index de38b5e2900..a770721dd91 100644 --- a/hapi-fhir-structures-r4/src/test/java/ca/uhn/fhir/rest/server/SearchSortR4Test.java +++ b/hapi-fhir-structures-r4/src/test/java/ca/uhn/fhir/rest/server/SearchSortR4Test.java @@ -3,9 +3,12 @@ package ca.uhn.fhir.rest.server; import ca.uhn.fhir.context.FhirContext; import ca.uhn.fhir.rest.annotation.Search; import ca.uhn.fhir.rest.annotation.Sort; +import ca.uhn.fhir.rest.api.EncodingEnum; import ca.uhn.fhir.rest.api.SortOrderEnum; import ca.uhn.fhir.rest.api.SortSpec; +import ca.uhn.fhir.test.utilities.HttpClientExtension; import ca.uhn.fhir.test.utilities.JettyUtil; +import ca.uhn.fhir.test.utilities.server.RestfulServerExtension; import ca.uhn.fhir.util.TestUtil; import org.apache.commons.io.IOUtils; import org.apache.http.client.methods.CloseableHttpResponse; @@ -14,8 +17,8 @@ import org.apache.http.impl.client.CloseableHttpClient; import org.apache.http.impl.client.HttpClientBuilder; import org.apache.http.impl.conn.PoolingHttpClientConnectionManager; import org.eclipse.jetty.server.Server; -import org.eclipse.jetty.servlet.ServletHandler; -import org.eclipse.jetty.servlet.ServletHolder; +import org.eclipse.jetty.ee10.servlet.ServletHandler; +import org.eclipse.jetty.ee10.servlet.ServletHolder; import org.hl7.fhir.instance.model.api.IBaseResource; import org.hl7.fhir.r4.model.HumanName; import org.hl7.fhir.r4.model.Patient; @@ -23,6 +26,7 @@ import org.junit.jupiter.api.AfterAll; import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.RegisterExtension; import java.util.ArrayList; import java.util.List; @@ -32,14 +36,21 @@ import static org.junit.jupiter.api.Assertions.assertEquals; public class SearchSortR4Test { - private static CloseableHttpClient ourClient; - private static FhirContext ourCtx = FhirContext.forR4(); + private static final FhirContext ourCtx = FhirContext.forR4Cached(); private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(SearchSortR4Test.class); - private static int ourPort; - private static Server ourServer; private static String ourLastMethod; private static SortSpec ourLastSortSpec; + @RegisterExtension + public RestfulServerExtension ourServer = new RestfulServerExtension(ourCtx) + .registerProvider(new DummyPatientResourceProvider()) + .withPagingProvider(new FifoMemoryPagingProvider(100)) + .setDefaultResponseEncoding(EncodingEnum.JSON) + .setDefaultPrettyPrint(false); + + @RegisterExtension + private HttpClientExtension ourClient = new HttpClientExtension(); + @BeforeEach public void before() { ourLastMethod = null; @@ -48,7 +59,7 @@ public class SearchSortR4Test { @Test public void testSearch() throws Exception { - HttpGet httpGet = new HttpGet("http://localhost:" + ourPort + "/Patient?_sort=param1,-param2,param3,-param4"); + HttpGet httpGet = new HttpGet(ourServer.getBaseUrl() + "/Patient?_sort=param1,-param2,param3,-param4"); CloseableHttpResponse status = ourClient.execute(httpGet); try { String responseContent = IOUtils.toString(status.getEntity().getContent()); @@ -76,35 +87,10 @@ public class SearchSortR4Test { @AfterAll public static void afterClassClearContext() throws Exception { - JettyUtil.closeServer(ourServer); TestUtil.randomizeLocaleAndTimezone(); } - @BeforeAll - public static void beforeClass() throws Exception { - ourServer = new Server(0); - - DummyPatientResourceProvider patientProvider = new DummyPatientResourceProvider(); - - ServletHandler proxyHandler = new ServletHandler(); - RestfulServer servlet = new RestfulServer(ourCtx); - servlet.setPagingProvider(new FifoMemoryPagingProvider(10)); - - servlet.setResourceProviders(patientProvider); - ServletHolder servletHolder = new ServletHolder(servlet); - proxyHandler.addServletWithMapping(servletHolder, "/*"); - ourServer.setHandler(proxyHandler); - JettyUtil.startServer(ourServer); - ourPort = JettyUtil.getPortForStartedServer(ourServer); - - PoolingHttpClientConnectionManager connectionManager = new PoolingHttpClientConnectionManager(5000, TimeUnit.MILLISECONDS); - HttpClientBuilder builder = HttpClientBuilder.create(); - builder.setConnectionManager(connectionManager); - ourClient = builder.build(); - - } - - public static class DummyPatientResourceProvider implements IResourceProvider { + private static class DummyPatientResourceProvider implements IResourceProvider { @Override public Class getResourceType() { diff --git a/hapi-fhir-structures-r4/src/test/java/ca/uhn/fhir/rest/server/ServerConcurrencyTest.java b/hapi-fhir-structures-r4/src/test/java/ca/uhn/fhir/rest/server/ServerConcurrencyTest.java index 45ed11d0b3e..950f625b55a 100644 --- a/hapi-fhir-structures-r4/src/test/java/ca/uhn/fhir/rest/server/ServerConcurrencyTest.java +++ b/hapi-fhir-structures-r4/src/test/java/ca/uhn/fhir/rest/server/ServerConcurrencyTest.java @@ -23,11 +23,11 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.util.Assert; -import javax.annotation.Nonnull; -import javax.servlet.ReadListener; -import javax.servlet.ServletInputStream; -import javax.servlet.http.HttpServletRequest; -import javax.servlet.http.HttpServletResponse; +import jakarta.annotation.Nonnull; +import jakarta.servlet.ReadListener; +import jakarta.servlet.ServletInputStream; +import jakarta.servlet.http.HttpServletRequest; +import jakarta.servlet.http.HttpServletResponse; import java.io.ByteArrayInputStream; import java.io.EOFException; import java.io.IOException; diff --git a/hapi-fhir-structures-r4/src/test/java/ca/uhn/fhir/rest/server/ServerInvalidDefinitionR4Test.java b/hapi-fhir-structures-r4/src/test/java/ca/uhn/fhir/rest/server/ServerInvalidDefinitionR4Test.java index e76dbb0c678..3c095277e1b 100644 --- a/hapi-fhir-structures-r4/src/test/java/ca/uhn/fhir/rest/server/ServerInvalidDefinitionR4Test.java +++ b/hapi-fhir-structures-r4/src/test/java/ca/uhn/fhir/rest/server/ServerInvalidDefinitionR4Test.java @@ -1,5 +1,6 @@ package ca.uhn.fhir.rest.server; +import ca.uhn.fhir.context.ConfigurationException; import ca.uhn.fhir.context.FhirContext; import ca.uhn.fhir.i18n.Msg; import ca.uhn.fhir.rest.annotation.ConditionalUrlParam; @@ -16,6 +17,7 @@ import ca.uhn.fhir.rest.api.server.IBundleProvider; import ca.uhn.fhir.rest.param.TokenParam; import ca.uhn.fhir.test.utilities.server.MockServletUtil; import com.google.common.collect.Lists; +import jakarta.servlet.ServletException; import org.hamcrest.core.StringContains; import org.hl7.fhir.instance.model.api.IBaseResource; import org.hl7.fhir.r4.model.DateType; @@ -24,8 +26,7 @@ import org.hl7.fhir.r4.model.Patient; import org.hl7.fhir.r4.model.StringType; import org.junit.jupiter.api.Test; -import javax.servlet.ServletException; -import javax.servlet.http.HttpServletRequest; +import jakarta.servlet.http.HttpServletRequest; import java.util.List; import static org.hamcrest.MatcherAssert.assertThat; @@ -39,7 +40,7 @@ public class ServerInvalidDefinitionR4Test extends BaseR4ServerTest { try { startServer(new UpdateWithWrongConditionalUrlType()); fail(); - } catch (ServletException e) { + } catch (ConfigurationException e) { assertThat(e.getCause().toString(), StringContains.containsString("ConfigurationException")); assertThat(e.getCause().toString(), StringContains.containsString( "Parameters annotated with @ConditionalUrlParam must be of type String, found incorrect parameter in method \"public ca.uhn.fhir.rest.api.MethodOutcome ca.uhn.fhir.rest.server.ServerInvalidDefinitionR4Test$UpdateWithWrongConditionalUrlType.update(ca.uhn.fhir.rest.param.TokenParam,org.hl7.fhir.r4.model.Patient)")); @@ -51,7 +52,7 @@ public class ServerInvalidDefinitionR4Test extends BaseR4ServerTest { try { startServer(new UpdateWithWrongResourceType()); fail(); - } catch (ServletException e) { + } catch (ConfigurationException e) { assertThat(e.getCause().toString(), StringContains.containsString("ConfigurationException")); assertThat(e.getCause().toString(), StringContains .containsString("Method 'update' is annotated with @ResourceParam but has a type that is not an implementation of org.hl7.fhir.instance.model.api.IBaseResource or String or byte[]")); @@ -63,7 +64,7 @@ public class ServerInvalidDefinitionR4Test extends BaseR4ServerTest { try { startServer(new ValidateWithWrongModeType()); fail(); - } catch (ServletException e) { + } catch (ConfigurationException e) { assertThat(e.getCause().toString(), StringContains.containsString("ConfigurationException")); assertThat(e.getCause().toString(), StringContains.containsString("Parameter annotated with @Validate.Mode must be of type ca.uhn.fhir.rest.api.ValidationModeEnum")); } @@ -74,7 +75,7 @@ public class ServerInvalidDefinitionR4Test extends BaseR4ServerTest { try { startServer(new ValidateWithWrongProfileType()); fail(); - } catch (ServletException e) { + } catch (ConfigurationException e) { assertThat(e.getCause().toString(), StringContains.containsString("ConfigurationException")); assertThat(e.getCause().toString(), StringContains.containsString("Parameter annotated with @Validate.Profile must be of type java.lang.String")); } @@ -94,8 +95,8 @@ public class ServerInvalidDefinitionR4Test extends BaseR4ServerTest { try { startServer(new MyProvider()); fail(); - } catch (ServletException e) { - assertThat(e.getCause().toString(), StringContains.containsString(Msg.code(288) + "Failure scanning class MyProvider: "+ Msg.code(421) + "Illegal method parameter annotation @OptionalParam on method: public ca.uhn.fhir.rest.api.MethodOutcome ca.uhn.fhir.rest.server.ServerInvalidDefinitionR4Test$1MyProvider.update(org.hl7.fhir.r4.model.StringType)")); + } catch (ConfigurationException e) { + assertThat(e.toString(), StringContains.containsString(Msg.code(288) + "Failure scanning class MyProvider: "+ Msg.code(421) + "Illegal method parameter annotation @OptionalParam on method: public ca.uhn.fhir.rest.api.MethodOutcome ca.uhn.fhir.rest.server.ServerInvalidDefinitionR4Test$1MyProvider.update(org.hl7.fhir.r4.model.StringType)")); } } @@ -121,8 +122,8 @@ public class ServerInvalidDefinitionR4Test extends BaseR4ServerTest { try { startServer(provider); fail(); - } catch (ServletException e) { - assertEquals(Msg.code(288) + "Failure scanning class MyProvider: "+ Msg.code(404) + "@OperationParam detected on method that is not annotated with @Operation: public java.util.List ca.uhn.fhir.rest.server.ServerInvalidDefinitionR4Test$2MyProvider.search(org.hl7.fhir.r4.model.StringType,org.hl7.fhir.r4.model.StringType)", e.getCause().getMessage()); + } catch (ConfigurationException e) { + assertEquals(Msg.code(288) + "Failure scanning class MyProvider: "+ Msg.code(404) + "@OperationParam detected on method that is not annotated with @Operation: public java.util.List ca.uhn.fhir.rest.server.ServerInvalidDefinitionR4Test$2MyProvider.search(org.hl7.fhir.r4.model.StringType,org.hl7.fhir.r4.model.StringType)", e.getMessage()); } } diff --git a/hapi-fhir-structures-r4/src/test/java/ca/uhn/fhir/rest/server/ServerMethodSelectionR4Test.java b/hapi-fhir-structures-r4/src/test/java/ca/uhn/fhir/rest/server/ServerMethodSelectionR4Test.java index 25194f468b3..ac5491144fc 100644 --- a/hapi-fhir-structures-r4/src/test/java/ca/uhn/fhir/rest/server/ServerMethodSelectionR4Test.java +++ b/hapi-fhir-structures-r4/src/test/java/ca/uhn/fhir/rest/server/ServerMethodSelectionR4Test.java @@ -1,16 +1,21 @@ package ca.uhn.fhir.rest.server; +import ca.uhn.fhir.context.FhirContext; import ca.uhn.fhir.model.api.Include; import ca.uhn.fhir.rest.annotation.IncludeParam; import ca.uhn.fhir.rest.annotation.OptionalParam; import ca.uhn.fhir.rest.annotation.Search; +import ca.uhn.fhir.rest.api.EncodingEnum; import ca.uhn.fhir.rest.server.exceptions.InvalidRequestException; +import ca.uhn.fhir.test.utilities.HttpClientExtension; +import ca.uhn.fhir.test.utilities.server.RestfulServerExtension; import com.google.common.collect.Lists; import org.hl7.fhir.instance.model.api.IBaseResource; import org.hl7.fhir.r4.model.Bundle; import org.hl7.fhir.r4.model.Patient; import org.hl7.fhir.r4.model.StringType; import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.RegisterExtension; import java.util.List; import java.util.Set; @@ -21,7 +26,6 @@ import static org.junit.jupiter.api.Assertions.*; public class ServerMethodSelectionR4Test extends BaseR4ServerTest { - /** * Server method with no _include * Client request with _include @@ -42,7 +46,8 @@ public class ServerMethodSelectionR4Test extends BaseR4ServerTest { startServer(provider); try { - myClient + ourServer + .getFhirClient() .search() .forResource(Patient.class) .where(Patient.NAME.matches().value("foo")) @@ -73,7 +78,8 @@ public class ServerMethodSelectionR4Test extends BaseR4ServerTest { startServer(provider); - Bundle results = myClient + Bundle results = ourServer + .getFhirClient() .search() .forResource(Patient.class) .where(Patient.NAME.matches().value("foo")) @@ -103,7 +109,8 @@ public class ServerMethodSelectionR4Test extends BaseR4ServerTest { startServer(provider); try { - myClient + ourServer + .getFhirClient() .search() .forResource(Patient.class) .where(Patient.NAME.matches().value("foo")) @@ -134,7 +141,8 @@ public class ServerMethodSelectionR4Test extends BaseR4ServerTest { startServer(provider); - Bundle results = myClient + Bundle results = ourServer + .getFhirClient() .search() .forResource(Patient.class) .where(Patient.NAME.matches().value("foo")) diff --git a/hapi-fhir-structures-r4/src/test/java/ca/uhn/fhir/rest/server/ServerMimetypeR4Test.java b/hapi-fhir-structures-r4/src/test/java/ca/uhn/fhir/rest/server/ServerMimetypeR4Test.java index 6544b49186c..c4a6de6f8e7 100644 --- a/hapi-fhir-structures-r4/src/test/java/ca/uhn/fhir/rest/server/ServerMimetypeR4Test.java +++ b/hapi-fhir-structures-r4/src/test/java/ca/uhn/fhir/rest/server/ServerMimetypeR4Test.java @@ -10,7 +10,8 @@ import ca.uhn.fhir.rest.api.Constants; import ca.uhn.fhir.rest.api.EncodingEnum; import ca.uhn.fhir.rest.api.MethodOutcome; import ca.uhn.fhir.rest.client.MyPatientWithExtensions; -import ca.uhn.fhir.test.utilities.JettyUtil; +import ca.uhn.fhir.test.utilities.HttpClientExtension; +import ca.uhn.fhir.test.utilities.server.RestfulServerExtension; import ca.uhn.fhir.util.TestUtil; import org.apache.commons.io.IOUtils; import org.apache.http.HttpResponse; @@ -21,12 +22,6 @@ import org.apache.http.client.methods.HttpRequestBase; import org.apache.http.client.methods.HttpTrace; import org.apache.http.entity.ContentType; import org.apache.http.entity.StringEntity; -import org.apache.http.impl.client.CloseableHttpClient; -import org.apache.http.impl.client.HttpClientBuilder; -import org.apache.http.impl.conn.PoolingHttpClientConnectionManager; -import org.eclipse.jetty.server.Server; -import org.eclipse.jetty.servlet.ServletHandler; -import org.eclipse.jetty.servlet.ServletHolder; import org.hl7.fhir.instance.model.api.IBaseResource; import org.hl7.fhir.r4.model.CapabilityStatement; import org.hl7.fhir.r4.model.CodeType; @@ -35,16 +30,14 @@ import org.hl7.fhir.r4.model.IdType; import org.hl7.fhir.r4.model.OperationOutcome; import org.hl7.fhir.r4.model.Patient; import org.junit.jupiter.api.AfterAll; -import org.junit.jupiter.api.BeforeAll; -import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.RegisterExtension; import java.io.IOException; import java.net.URI; import java.nio.charset.StandardCharsets; import java.util.ArrayList; import java.util.List; -import java.util.concurrent.TimeUnit; import static org.hamcrest.MatcherAssert.assertThat; import static org.hamcrest.Matchers.containsString; @@ -54,19 +47,20 @@ import static org.junit.jupiter.api.Assertions.assertEquals; public class ServerMimetypeR4Test { private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(ServerMimetypeR4Test.class); - private static CloseableHttpClient ourClient; - private static FhirContext ourCtx = FhirContext.forR4(); - private static int ourPort; - private static Server ourServer; - private static RestfulServer ourServlet; + private static final FhirContext ourCtx = FhirContext.forR4Cached(); - @BeforeEach - public void before() { - ourServlet.setDefaultResponseEncoding(EncodingEnum.XML); - } + @RegisterExtension + public RestfulServerExtension ourServer = new RestfulServerExtension(ourCtx) + .registerProvider(new PatientProvider()) + .withPagingProvider(new FifoMemoryPagingProvider(100)) + .setDefaultResponseEncoding(EncodingEnum.XML) + .setDefaultPrettyPrint(false); + + @RegisterExtension + private HttpClientExtension ourClient = new HttpClientExtension(); private String readAndReturnContentType(String theAccept) throws IOException { - HttpGet httpGet = new HttpGet("http://localhost:" + ourPort + "/Patient"); + HttpGet httpGet = new HttpGet(ourServer.getBaseUrl() + "/Patient"); if (theAccept != null) { httpGet.addHeader(Constants.HEADER_ACCEPT, theAccept); } @@ -79,7 +73,7 @@ public class ServerMimetypeR4Test { @Test public void testConformanceMetadataUsesNewMimetypes() throws Exception { - HttpGet httpGet = new HttpGet("http://localhost:" + ourPort + "/metadata"); + HttpGet httpGet = new HttpGet(ourServer.getBaseUrl() + "/metadata"); CloseableHttpResponse status = ourClient.execute(httpGet); try { String content = IOUtils.toString(status.getEntity().getContent(), StandardCharsets.UTF_8); @@ -99,7 +93,7 @@ public class ServerMimetypeR4Test { String enc = ourCtx.newJsonParser().encodeResourceToString(p); String expectedResponseContent = "{\"resourceType\":\"Patient\",\"id\":\"1\",\"meta\":{\"versionId\":\"1\"},\"name\":[{\"family\":\"FAMILY\"}]}"; - HttpPost httpPost = new HttpPost("http://localhost:" + ourPort + "/Patient"); + HttpPost httpPost = new HttpPost(ourServer.getBaseUrl() + "/Patient"); httpPost.setEntity(new StringEntity(enc, ContentType.parse(Constants.CT_FHIR_JSON + "; charset=utf-8"))); HttpResponse status = ourClient.execute(httpPost); @@ -120,7 +114,7 @@ public class ServerMimetypeR4Test { String enc = ourCtx.newJsonParser().encodeResourceToString(p); String expectedResponseContent = "{\"resourceType\":\"OperationOutcome\",\"issue\":[{\"diagnostics\":\"FAMILY\"}]}"; - HttpPost httpPost = new HttpPost("http://localhost:" + ourPort + "/Patient"); + HttpPost httpPost = new HttpPost(ourServer.getBaseUrl() + "/Patient"); httpPost.setEntity(new StringEntity(enc, ContentType.parse(Constants.CT_FHIR_JSON_NEW + "; charset=utf-8"))); httpPost.addHeader(Constants.HEADER_PREFER, Constants.HEADER_PREFER_RETURN + "=" + Constants.HEADER_PREFER_RETURN_OPERATION_OUTCOME); HttpResponse status = ourClient.execute(httpPost); @@ -142,7 +136,7 @@ public class ServerMimetypeR4Test { String enc = ourCtx.newJsonParser().encodeResourceToString(p); String expectedResponseContent = "{\"resourceType\":\"Patient\",\"id\":\"1\",\"meta\":{\"versionId\":\"1\"},\"name\":[{\"family\":\"FAMILY\"}]}"; - HttpPost httpPost = new HttpPost("http://localhost:" + ourPort + "/Patient"); + HttpPost httpPost = new HttpPost(ourServer.getBaseUrl() + "/Patient"); httpPost.setEntity(new StringEntity(enc, ContentType.parse(Constants.CT_FHIR_JSON + "; charset=utf-8"))); httpPost.addHeader(Constants.HEADER_ACCEPT, Constants.CT_FHIR_JSON_NEW); HttpResponse status = ourClient.execute(httpPost); @@ -164,7 +158,7 @@ public class ServerMimetypeR4Test { String enc = ourCtx.newXmlParser().encodeResourceToString(p); String expectedResponseContent = ""; - HttpPost httpPost = new HttpPost("http://localhost:" + ourPort + "/Patient"); + HttpPost httpPost = new HttpPost(ourServer.getBaseUrl() + "/Patient"); httpPost.setEntity(new StringEntity(enc, ContentType.parse(Constants.CT_FHIR_XML + "; charset=utf-8"))); httpPost.addHeader(Constants.HEADER_PREFER, Constants.HEADER_PREFER_RETURN + "=" + Constants.HEADER_PREFER_RETURN_OPERATION_OUTCOME); HttpResponse status = ourClient.execute(httpPost); @@ -186,7 +180,7 @@ public class ServerMimetypeR4Test { String enc = ourCtx.newXmlParser().encodeResourceToString(p); String expectedResponseContent = ""; - HttpPost httpPost = new HttpPost("http://localhost:" + ourPort + "/Patient"); + HttpPost httpPost = new HttpPost(ourServer.getBaseUrl() + "/Patient"); httpPost.setEntity(new StringEntity(enc, ContentType.parse(Constants.CT_FHIR_XML_NEW + "; charset=utf-8"))); HttpResponse status = ourClient.execute(httpPost); @@ -207,7 +201,7 @@ public class ServerMimetypeR4Test { String enc = ourCtx.newXmlParser().encodeResourceToString(p); String expectedResponseContent = ""; - HttpPost httpPost = new HttpPost("http://localhost:" + ourPort + "/Patient"); + HttpPost httpPost = new HttpPost(ourServer.getBaseUrl() + "/Patient"); httpPost.setEntity(new StringEntity(enc, ContentType.parse(Constants.CT_FHIR_XML + "; charset=utf-8"))); httpPost.addHeader(Constants.HEADER_ACCEPT, Constants.CT_FHIR_XML_NEW); HttpResponse status = ourClient.execute(httpPost); @@ -224,7 +218,7 @@ public class ServerMimetypeR4Test { @Test public void testHttpTraceNotEnabled() throws Exception { - HttpTrace req = new HttpTrace("http://localhost:" + ourPort + "/Patient"); + HttpTrace req = new HttpTrace(ourServer.getBaseUrl() + "/Patient"); CloseableHttpResponse status = ourClient.execute(req); try { ourLog.info(status.toString()); @@ -242,7 +236,7 @@ public class ServerMimetypeR4Test { return "TRACK"; } }; - req.setURI(new URI("http://localhost:" + ourPort + "/Patient")); + req.setURI(new URI(ourServer.getBaseUrl() + "/Patient")); CloseableHttpResponse status = ourClient.execute(req); try { @@ -258,7 +252,7 @@ public class ServerMimetypeR4Test { */ @Test public void testResponseContentTypesJson() throws IOException { - ourServlet.setDefaultResponseEncoding(EncodingEnum.XML); + ourServer.setDefaultResponseEncoding(EncodingEnum.XML); // None given assertEquals("application/fhir+xml", readAndReturnContentType(null)); @@ -281,7 +275,7 @@ public class ServerMimetypeR4Test { */ @Test public void testResponseContentTypesXml() throws IOException { - ourServlet.setDefaultResponseEncoding(EncodingEnum.JSON); + ourServer.setDefaultResponseEncoding(EncodingEnum.JSON); // None given assertEquals("application/fhir+json", readAndReturnContentType(null)); @@ -302,7 +296,7 @@ public class ServerMimetypeR4Test { @Test public void testSearchWithFormatJsonLegacy() throws Exception { - HttpGet httpGet = new HttpGet("http://localhost:" + ourPort + "/Patient?_format=" + Constants.CT_FHIR_JSON); + HttpGet httpGet = new HttpGet(ourServer.getBaseUrl() + "/Patient?_format=" + Constants.CT_FHIR_JSON); HttpResponse status = ourClient.execute(httpGet); String responseContent = IOUtils.toString(status.getEntity().getContent(), StandardCharsets.UTF_8); @@ -318,7 +312,7 @@ public class ServerMimetypeR4Test { @Test public void testSearchWithFormatJsonNew() throws Exception { - HttpGet httpGet = new HttpGet("http://localhost:" + ourPort + "/Patient?_format=" + Constants.CT_FHIR_JSON_NEW); + HttpGet httpGet = new HttpGet(ourServer.getBaseUrl() + "/Patient?_format=" + Constants.CT_FHIR_JSON_NEW); try (CloseableHttpResponse status = ourClient.execute(httpGet)) { String responseContent = IOUtils.toString(status.getEntity().getContent(), StandardCharsets.UTF_8); @@ -334,7 +328,7 @@ public class ServerMimetypeR4Test { @Test public void testSearchWithFormatJsonSimple() throws Exception { - HttpGet httpGet = new HttpGet("http://localhost:" + ourPort + "/Patient?_format=json"); + HttpGet httpGet = new HttpGet(ourServer.getBaseUrl() + "/Patient?_format=json"); HttpResponse status = ourClient.execute(httpGet); String responseContent = IOUtils.toString(status.getEntity().getContent(), StandardCharsets.UTF_8); @@ -350,7 +344,7 @@ public class ServerMimetypeR4Test { @Test public void testSearchWithFormatXmlLegacy() throws Exception { - HttpGet httpGet = new HttpGet("http://localhost:" + ourPort + "/Patient?_format=" + Constants.CT_FHIR_XML); + HttpGet httpGet = new HttpGet(ourServer.getBaseUrl() + "/Patient?_format=" + Constants.CT_FHIR_XML); HttpResponse status = ourClient.execute(httpGet); String responseContent = IOUtils.toString(status.getEntity().getContent(), StandardCharsets.UTF_8); @@ -367,7 +361,7 @@ public class ServerMimetypeR4Test { @Test public void testSearchWithFormatXmlNew() throws Exception { - HttpGet httpGet = new HttpGet("http://localhost:" + ourPort + "/Patient?_format=" + Constants.CT_FHIR_XML_NEW); + HttpGet httpGet = new HttpGet(ourServer.getBaseUrl() + "/Patient?_format=" + Constants.CT_FHIR_XML_NEW); HttpResponse status = ourClient.execute(httpGet); String responseContent = IOUtils.toString(status.getEntity().getContent(), StandardCharsets.UTF_8); @@ -384,7 +378,7 @@ public class ServerMimetypeR4Test { @Test public void testSearchWithFormatXmlSimple() throws Exception { - HttpGet httpGet = new HttpGet("http://localhost:" + ourPort + "/Patient?_format=xml"); + HttpGet httpGet = new HttpGet(ourServer.getBaseUrl() + "/Patient?_format=xml"); HttpResponse status = ourClient.execute(httpGet); String responseContent = IOUtils.toString(status.getEntity().getContent(), StandardCharsets.UTF_8); @@ -399,7 +393,7 @@ public class ServerMimetypeR4Test { } private List toStrings(List theFormat) { - ArrayList retVal = new ArrayList(); + ArrayList retVal = new ArrayList<>(); for (CodeType next : theFormat) { retVal.add(next.asStringValue()); } @@ -434,7 +428,7 @@ public class ServerMimetypeR4Test { @Search public List search() { - ArrayList retVal = new ArrayList(); + ArrayList retVal = new ArrayList<>(); MyPatientWithExtensions p0 = new MyPatientWithExtensions(); p0.setId(new IdType("Patient/0")); @@ -453,31 +447,7 @@ public class ServerMimetypeR4Test { @AfterAll public static void afterClassClearContext() throws Exception { - JettyUtil.closeServer(ourServer); TestUtil.randomizeLocaleAndTimezone(); } - @BeforeAll - public static void beforeClass() throws Exception { - ourServer = new Server(0); - - PatientProvider patientProvider = new PatientProvider(); - - ServletHandler proxyHandler = new ServletHandler(); - ourServlet = new RestfulServer(ourCtx); - - ourServlet.setResourceProviders(patientProvider); - ServletHolder servletHolder = new ServletHolder(ourServlet); - proxyHandler.addServletWithMapping(servletHolder, "/*"); - ourServer.setHandler(proxyHandler); - JettyUtil.startServer(ourServer); - ourPort = JettyUtil.getPortForStartedServer(ourServer); - - PoolingHttpClientConnectionManager connectionManager = new PoolingHttpClientConnectionManager(5000, TimeUnit.MILLISECONDS); - HttpClientBuilder builder = HttpClientBuilder.create(); - builder.setConnectionManager(connectionManager); - ourClient = builder.build(); - - } - } diff --git a/hapi-fhir-structures-r4/src/test/java/ca/uhn/fhir/rest/server/SummaryParamR4Test.java b/hapi-fhir-structures-r4/src/test/java/ca/uhn/fhir/rest/server/SummaryParamR4Test.java index 155a8c89825..8f93d6d1f68 100644 --- a/hapi-fhir-structures-r4/src/test/java/ca/uhn/fhir/rest/server/SummaryParamR4Test.java +++ b/hapi-fhir-structures-r4/src/test/java/ca/uhn/fhir/rest/server/SummaryParamR4Test.java @@ -7,7 +7,9 @@ import ca.uhn.fhir.rest.annotation.Search; import ca.uhn.fhir.rest.api.Constants; import ca.uhn.fhir.rest.api.EncodingEnum; import ca.uhn.fhir.rest.api.SummaryEnum; +import ca.uhn.fhir.test.utilities.HttpClientExtension; import ca.uhn.fhir.test.utilities.JettyUtil; +import ca.uhn.fhir.test.utilities.server.RestfulServerExtension; import ca.uhn.fhir.util.TestUtil; import com.google.common.base.Charsets; import org.apache.commons.io.IOUtils; @@ -17,8 +19,8 @@ import org.apache.http.impl.client.CloseableHttpClient; import org.apache.http.impl.client.HttpClientBuilder; import org.apache.http.impl.conn.PoolingHttpClientConnectionManager; import org.eclipse.jetty.server.Server; -import org.eclipse.jetty.servlet.ServletHandler; -import org.eclipse.jetty.servlet.ServletHolder; +import org.eclipse.jetty.ee10.servlet.ServletHandler; +import org.eclipse.jetty.ee10.servlet.ServletHolder; import org.hl7.fhir.instance.model.api.IBaseResource; import org.hl7.fhir.r4.model.Bundle; import org.hl7.fhir.r4.model.IdType; @@ -29,6 +31,7 @@ import org.junit.jupiter.api.AfterAll; import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.RegisterExtension; import java.io.IOException; import java.util.Collections; @@ -46,13 +49,19 @@ import static org.junit.jupiter.api.Assertions.assertEquals; public class SummaryParamR4Test { private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(SummaryParamR4Test.class); - private static CloseableHttpClient ourClient; - private static FhirContext ourCtx = FhirContext.forR4(); + private static final FhirContext ourCtx = FhirContext.forR4Cached(); private static SummaryEnum ourLastSummary; private static List ourLastSummaryList; - private static int ourPort; - private static Server ourServer; + @RegisterExtension + public RestfulServerExtension ourServer = new RestfulServerExtension(ourCtx) + .registerProvider(new DummyPatientResourceProvider()) + .registerProvider(new DummyMedicationRequestProvider()) + .withPagingProvider(new FifoMemoryPagingProvider(100)) + .setDefaultResponseEncoding(EncodingEnum.XML); + + @RegisterExtension + private HttpClientExtension ourClient = new HttpClientExtension(); @BeforeEach public void before() { @@ -63,7 +72,7 @@ public class SummaryParamR4Test { @Test public void testReadSummaryData() throws Exception { verifyXmlAndJson( - "http://localhost:" + ourPort + "/Patient/1?_summary=" + SummaryEnum.DATA.getCode(), + ourServer.getBaseUrl() + "/Patient/1?_summary=" + SummaryEnum.DATA.getCode(), Patient.class, patient -> { String responseContent = ourCtx.newXmlParser().encodeResourceToString(patient); @@ -80,7 +89,7 @@ public class SummaryParamR4Test { @Test public void testReadSummaryText() throws Exception { - HttpGet httpGet = new HttpGet("http://localhost:" + ourPort + "/Patient/1?_summary=" + SummaryEnum.TEXT.getCode()); + HttpGet httpGet = new HttpGet(ourServer.getBaseUrl() + "/Patient/1?_summary=" + SummaryEnum.TEXT.getCode()); try (CloseableHttpResponse status = ourClient.execute(httpGet)) { String responseContent = IOUtils.toString(status.getEntity().getContent(), Charsets.UTF_8); ourLog.info(responseContent); @@ -97,7 +106,7 @@ public class SummaryParamR4Test { @Test public void testReadSummaryTextWithMandatory() throws Exception { - HttpGet httpGet = new HttpGet("http://localhost:" + ourPort + "/MedicationRequest/1?_summary=" + SummaryEnum.TEXT.getCode()); + HttpGet httpGet = new HttpGet(ourServer.getBaseUrl() + "/MedicationRequest/1?_summary=" + SummaryEnum.TEXT.getCode()); try (CloseableHttpResponse status = ourClient.execute(httpGet)) { String responseContent = IOUtils.toString(status.getEntity().getContent(), Charsets.UTF_8); ourLog.info(responseContent); @@ -114,7 +123,7 @@ public class SummaryParamR4Test { @Test public void testReadSummaryTrue() throws Exception { - String url = "http://localhost:" + ourPort + "/Patient/1?_summary=" + SummaryEnum.TRUE.getCode(); + String url = ourServer.getBaseUrl() + "/Patient/1?_summary=" + SummaryEnum.TRUE.getCode(); verifyXmlAndJson( url, Patient.class, @@ -133,7 +142,7 @@ public class SummaryParamR4Test { @Test public void testSearchSummaryCount() throws Exception { - String url = "http://localhost:" + ourPort + "/Patient?_pretty=true&_summary=" + SummaryEnum.COUNT.getCode(); + String url = ourServer.getBaseUrl() + "/Patient?_pretty=true&_summary=" + SummaryEnum.COUNT.getCode(); verifyXmlAndJson( url, bundle -> { @@ -150,7 +159,7 @@ public class SummaryParamR4Test { @Test public void testSearchSummaryCountAndData() throws Exception { - String url = "http://localhost:" + ourPort + "/Patient?_pretty=true&_summary=" + SummaryEnum.COUNT.getCode() + "," + SummaryEnum.DATA.getCode(); + String url = ourServer.getBaseUrl() + "/Patient?_pretty=true&_summary=" + SummaryEnum.COUNT.getCode() + "," + SummaryEnum.DATA.getCode(); verifyXmlAndJson( url, bundle -> { @@ -166,7 +175,7 @@ public class SummaryParamR4Test { @Test public void testSearchSummaryData() throws Exception { - String url = "http://localhost:" + ourPort + "/Patient?_summary=" + SummaryEnum.DATA.getCode(); + String url = ourServer.getBaseUrl() + "/Patient?_summary=" + SummaryEnum.DATA.getCode(); verifyXmlAndJson( url, bundle -> { @@ -183,7 +192,7 @@ public class SummaryParamR4Test { @Test public void testSearchSummaryFalse() throws Exception { - String url = "http://localhost:" + ourPort + "/Patient?_summary=false"; + String url = ourServer.getBaseUrl() + "/Patient?_summary=false"; verifyXmlAndJson( url, bundle -> { @@ -199,7 +208,7 @@ public class SummaryParamR4Test { @Test public void testSearchSummaryText() throws Exception { - String url = "http://localhost:" + ourPort + "/Patient?_summary=" + SummaryEnum.TEXT.getCode(); + String url = ourServer.getBaseUrl() + "/Patient?_summary=" + SummaryEnum.TEXT.getCode(); verifyXmlAndJson( url, bundle -> { @@ -216,7 +225,7 @@ public class SummaryParamR4Test { @Test public void testSearchSummaryTextWithMandatory() throws Exception { - String url = "http://localhost:" + ourPort + "/MedicationRequest?_summary=" + SummaryEnum.TEXT.getCode() + "&_pretty=true"; + String url = ourServer.getBaseUrl() + "/MedicationRequest?_summary=" + SummaryEnum.TEXT.getCode() + "&_pretty=true"; verifyXmlAndJson( url, bundle -> { @@ -234,7 +243,7 @@ public class SummaryParamR4Test { @Test public void testSearchSummaryTextMulti() throws Exception { - String url = "http://localhost:" + ourPort + "/Patient?_query=multi&_summary=" + SummaryEnum.TEXT.getCode(); + String url = ourServer.getBaseUrl() + "/Patient?_query=multi&_summary=" + SummaryEnum.TEXT.getCode(); verifyXmlAndJson( url, bundle -> { @@ -251,7 +260,7 @@ public class SummaryParamR4Test { @Test public void testSearchSummaryTrue() throws Exception { - String url = "http://localhost:" + ourPort + "/Patient?_summary=" + SummaryEnum.TRUE.getCode(); + String url = ourServer.getBaseUrl() + "/Patient?_summary=" + SummaryEnum.TRUE.getCode(); verifyXmlAndJson( url, bundle -> { @@ -268,7 +277,7 @@ public class SummaryParamR4Test { @Test public void testSearchSummaryWithTextAndOthers() throws Exception { - String url = "http://localhost:" + ourPort + "/Patient?_summary=text&_summary=data"; + String url = ourServer.getBaseUrl() + "/Patient?_summary=text&_summary=data"; try (CloseableHttpResponse status = ourClient.execute(new HttpGet(url))) { String responseContent = IOUtils.toString(status.getEntity().getContent(), Charsets.UTF_8); ourLog.info(responseContent); @@ -373,29 +382,7 @@ public class SummaryParamR4Test { @AfterAll public static void afterClassClearContext() throws Exception { - JettyUtil.closeServer(ourServer); TestUtil.randomizeLocaleAndTimezone(); } - @BeforeAll - public static void beforeClass() throws Exception { - ourServer = new Server(0); - - ServletHandler proxyHandler = new ServletHandler(); - RestfulServer servlet = new RestfulServer(ourCtx); - - servlet.setResourceProviders(new DummyPatientResourceProvider(), new DummyMedicationRequestProvider()); - ServletHolder servletHolder = new ServletHolder(servlet); - proxyHandler.addServletWithMapping(servletHolder, "/*"); - ourServer.setHandler(proxyHandler); - JettyUtil.startServer(ourServer); - ourPort = JettyUtil.getPortForStartedServer(ourServer); - - PoolingHttpClientConnectionManager connectionManager = new PoolingHttpClientConnectionManager(5000, TimeUnit.MILLISECONDS); - HttpClientBuilder builder = HttpClientBuilder.create(); - builder.setConnectionManager(connectionManager); - ourClient = builder.build(); - - } - } diff --git a/hapi-fhir-structures-r4/src/test/java/ca/uhn/fhir/rest/server/interceptor/ConsentInterceptorTest.java b/hapi-fhir-structures-r4/src/test/java/ca/uhn/fhir/rest/server/interceptor/ConsentInterceptorTest.java index a27b8ae4134..237e72a218c 100644 --- a/hapi-fhir-structures-r4/src/test/java/ca/uhn/fhir/rest/server/interceptor/ConsentInterceptorTest.java +++ b/hapi-fhir-structures-r4/src/test/java/ca/uhn/fhir/rest/server/interceptor/ConsentInterceptorTest.java @@ -18,8 +18,8 @@ import ca.uhn.fhir.rest.server.interceptor.consent.ConsentOutcome; import ca.uhn.fhir.rest.server.interceptor.consent.IConsentService; import ca.uhn.fhir.rest.server.provider.HashMapResourceProvider; import ca.uhn.fhir.rest.server.servlet.ServletRequestDetails; +import ca.uhn.fhir.rest.server.util.ICachedSearchDetails; import ca.uhn.fhir.test.utilities.HttpClientExtension; -import ca.uhn.fhir.test.utilities.LoggingExtension; import ca.uhn.fhir.test.utilities.server.RestfulServerExtension; import com.google.common.base.Charsets; import com.helger.commons.collection.iterate.EmptyEnumeration; @@ -36,6 +36,7 @@ import org.hl7.fhir.r4.model.Parameters; import org.hl7.fhir.r4.model.Patient; import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Nested; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.ExtendWith; import org.junit.jupiter.api.extension.RegisterExtension; @@ -46,10 +47,11 @@ import org.mockito.Mock; import org.mockito.junit.jupiter.MockitoExtension; import org.springframework.util.Assert; -import javax.servlet.ReadListener; -import javax.servlet.ServletInputStream; -import javax.servlet.http.HttpServletRequest; -import javax.servlet.http.HttpServletResponse; +import jakarta.servlet.ReadListener; +import jakarta.servlet.ServletInputStream; +import jakarta.servlet.http.HttpServletRequest; +import jakarta.servlet.http.HttpServletResponse; +import java.io.EOFException; import java.io.IOException; import java.io.InputStream; import java.io.PrintWriter; @@ -61,8 +63,11 @@ import static org.hamcrest.CoreMatchers.containsString; import static org.hamcrest.MatcherAssert.assertThat; import static org.hamcrest.Matchers.not; import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertFalse; import static org.junit.jupiter.api.Assertions.assertNull; +import static org.junit.jupiter.api.Assertions.assertTrue; import static org.mockito.ArgumentMatchers.any; +import static org.mockito.Mockito.never; import static org.mockito.Mockito.reset; import static org.mockito.Mockito.timeout; import static org.mockito.Mockito.verify; @@ -75,15 +80,13 @@ public class ConsentInterceptorTest { private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(ConsentInterceptorTest.class); @RegisterExtension private final HttpClientExtension myClient = new HttpClientExtension(); - @RegisterExtension - private final LoggingExtension myLoggingExtension = new LoggingExtension(); private static final FhirContext ourCtx = FhirContext.forR4Cached(); private int myPort; private static final DummyPatientResourceProvider ourPatientProvider = new DummyPatientResourceProvider(ourCtx); private static final DummySystemProvider ourSystemProvider = new DummySystemProvider(); @RegisterExtension - private static final RestfulServerExtension ourServer = new RestfulServerExtension(ourCtx) + static final RestfulServerExtension ourServer = new RestfulServerExtension(ourCtx) .registerProvider(ourPatientProvider) .registerProvider(ourSystemProvider) .withPagingProvider(new FifoMemoryPagingProvider(10)); @@ -357,9 +360,7 @@ public class ConsentInterceptorTest { when(myConsentSvc.startOperation(any(), any())).thenReturn(ConsentOutcome.PROCEED); when(myConsentSvc.canSeeResource(any(RequestDetails.class), any(IBaseResource.class), any())).thenAnswer(t-> ConsentOutcome.PROCEED); - when(myConsentSvc.willSeeResource(any(RequestDetails.class), any(IBaseResource.class), any())).thenAnswer(t-> { - return ConsentOutcome.REJECT; - }); + when(myConsentSvc.willSeeResource(any(RequestDetails.class), any(IBaseResource.class), any())).thenAnswer(t-> ConsentOutcome.REJECT); HttpGet httpGet = new HttpGet("http://localhost:" + myPort + "/Patient"); @@ -861,7 +862,7 @@ public class ConsentInterceptorTest { @Test - public void testNoServicesRegistered() throws IOException { + public void testNoServicesRegistered() { myInterceptor.unregisterConsentService(myConsentSvc); Patient patientA = new Patient(); @@ -886,6 +887,52 @@ public class ConsentInterceptorTest { assertEquals(2, response.getTotal()); } + @Nested class CacheUsage { + @Mock ICachedSearchDetails myCachedSearchDetails; + ServletRequestDetails myRequestDetails = new ServletRequestDetails(); + + @Test + void testAuthorizedRequestsMayBeCachedAndUseCache() { + when(myConsentSvc.startOperation(any(), any())).thenReturn(ConsentOutcome.AUTHORIZED); + myInterceptor.interceptPreHandled(myRequestDetails); + + assertTrue(myInterceptor.interceptPreCheckForCachedSearch(myRequestDetails), "AUTHORIZED requests can use cache"); + + myInterceptor.interceptPreSearchRegistered(myRequestDetails, myCachedSearchDetails); + verify(myCachedSearchDetails, never()).setCannotBeReused(); + } + + @Test + void testCanSeeResourceFilteredRequestsMayNotBeCachedNorUseCache() { + when(myConsentSvc.startOperation(any(), any())).thenReturn(ConsentOutcome.PROCEED); + when(myConsentSvc.startOperation(any(), any())).thenReturn(ConsentOutcome.PROCEED); + when(myConsentSvc.shouldProcessCanSeeResource(any(), any())).thenReturn(true); + when(myConsentSvc2.startOperation(any(), any())).thenReturn(ConsentOutcome.PROCEED); + when(myConsentSvc2.shouldProcessCanSeeResource(any(), any())).thenReturn(false); + myInterceptor.registerConsentService(myConsentSvc2); + myInterceptor.interceptPreHandled(myRequestDetails); + + assertFalse(myInterceptor.interceptPreCheckForCachedSearch(myRequestDetails), "PROCEED requests can not use cache"); + + myInterceptor.interceptPreSearchRegistered(myRequestDetails, myCachedSearchDetails); + verify(myCachedSearchDetails).setCannotBeReused(); + } + + @Test + void testRequestsWithNoCanSeeFilteringMayBeCachedAndUseCache() { + when(myConsentSvc.startOperation(any(), any())).thenReturn(ConsentOutcome.PROCEED); + when(myConsentSvc.shouldProcessCanSeeResource(any(), any())).thenReturn(false); + myInterceptor.interceptPreHandled(myRequestDetails); + + assertTrue(myInterceptor.interceptPreCheckForCachedSearch(myRequestDetails), "PROCEED requests that promise not to filter can not use cache"); + + myInterceptor.interceptPreSearchRegistered(myRequestDetails, myCachedSearchDetails); + verify(myCachedSearchDetails, never()).setCannotBeReused(); + } + } + + + public static class DummyPatientResourceProvider extends HashMapResourceProvider { public DummyPatientResourceProvider(FhirContext theFhirContext) { diff --git a/hapi-fhir-structures-r4/src/test/java/ca/uhn/fhir/rest/server/interceptor/ExceptionHandlingInterceptorTest.java b/hapi-fhir-structures-r4/src/test/java/ca/uhn/fhir/rest/server/interceptor/ExceptionHandlingInterceptorTest.java index 06362fa443d..e133f302a34 100644 --- a/hapi-fhir-structures-r4/src/test/java/ca/uhn/fhir/rest/server/interceptor/ExceptionHandlingInterceptorTest.java +++ b/hapi-fhir-structures-r4/src/test/java/ca/uhn/fhir/rest/server/interceptor/ExceptionHandlingInterceptorTest.java @@ -27,8 +27,8 @@ import org.apache.http.impl.client.CloseableHttpClient; import org.apache.http.impl.client.HttpClientBuilder; import org.apache.http.impl.conn.PoolingHttpClientConnectionManager; import org.eclipse.jetty.server.Server; -import org.eclipse.jetty.servlet.ServletHandler; -import org.eclipse.jetty.servlet.ServletHolder; +import org.eclipse.jetty.ee10.servlet.ServletHandler; +import org.eclipse.jetty.ee10.servlet.ServletHolder; import org.hamcrest.core.StringContains; import org.hl7.fhir.r4.model.IdType; import org.hl7.fhir.r4.model.OperationOutcome; diff --git a/hapi-fhir-structures-r4/src/test/java/ca/uhn/fhir/rest/server/interceptor/ExceptionInterceptorMethodTest.java b/hapi-fhir-structures-r4/src/test/java/ca/uhn/fhir/rest/server/interceptor/ExceptionInterceptorMethodTest.java index 1b7d6b7f344..aa128750d69 100644 --- a/hapi-fhir-structures-r4/src/test/java/ca/uhn/fhir/rest/server/interceptor/ExceptionInterceptorMethodTest.java +++ b/hapi-fhir-structures-r4/src/test/java/ca/uhn/fhir/rest/server/interceptor/ExceptionInterceptorMethodTest.java @@ -2,12 +2,16 @@ package ca.uhn.fhir.rest.server.interceptor; import ca.uhn.fhir.context.FhirContext; import ca.uhn.fhir.rest.annotation.Search; +import ca.uhn.fhir.rest.api.EncodingEnum; import ca.uhn.fhir.rest.api.server.RequestDetails; +import ca.uhn.fhir.rest.server.FifoMemoryPagingProvider; import ca.uhn.fhir.rest.server.IResourceProvider; import ca.uhn.fhir.rest.server.RestfulServer; import ca.uhn.fhir.rest.server.exceptions.BaseServerResponseException; import ca.uhn.fhir.rest.server.exceptions.UnprocessableEntityException; +import ca.uhn.fhir.test.utilities.HttpClientExtension; import ca.uhn.fhir.test.utilities.JettyUtil; +import ca.uhn.fhir.test.utilities.server.RestfulServerExtension; import ca.uhn.fhir.util.TestUtil; import com.google.common.base.Charsets; import org.apache.commons.io.IOUtils; @@ -17,18 +21,19 @@ import org.apache.http.impl.client.CloseableHttpClient; import org.apache.http.impl.client.HttpClientBuilder; import org.apache.http.impl.conn.PoolingHttpClientConnectionManager; import org.eclipse.jetty.server.Server; -import org.eclipse.jetty.servlet.ServletHandler; -import org.eclipse.jetty.servlet.ServletHolder; +import org.eclipse.jetty.ee10.servlet.ServletHandler; +import org.eclipse.jetty.ee10.servlet.ServletHolder; import org.hl7.fhir.r4.model.Patient; import org.junit.jupiter.api.AfterAll; import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.RegisterExtension; import org.mockito.ArgumentCaptor; -import javax.servlet.http.HttpServletRequest; -import javax.servlet.http.HttpServletResponse; +import jakarta.servlet.http.HttpServletRequest; +import jakarta.servlet.http.HttpServletResponse; import java.util.List; import java.util.concurrent.TimeUnit; @@ -41,23 +46,28 @@ import static org.mockito.Mockito.when; public class ExceptionInterceptorMethodTest { - private static CloseableHttpClient ourClient; - private static FhirContext ourCtx = FhirContext.forR4(); + private static final FhirContext ourCtx = FhirContext.forR4Cached(); private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(ExceptionInterceptorMethodTest.class); - private static int ourPort; - private static Server ourServer; - private static RestfulServer servlet; private IServerInterceptor myInterceptor; + @RegisterExtension + public RestfulServerExtension ourServer = new RestfulServerExtension(ourCtx) + .registerProvider(new DummyPatientResourceProvider()) + .withPagingProvider(new FifoMemoryPagingProvider(100)) + .setDefaultResponseEncoding(EncodingEnum.XML); + + @RegisterExtension + private HttpClientExtension ourClient = new HttpClientExtension(); + @BeforeEach public void before() { myInterceptor = mock(IServerInterceptor.class); - servlet.getInterceptorService().registerInterceptor(myInterceptor); + ourServer.getInterceptorService().registerInterceptor(myInterceptor); } @AfterEach public void after() { - servlet.getInterceptorService().unregisterInterceptor(myInterceptor); + ourServer.getInterceptorService().unregisterInterceptor(myInterceptor); } @Test @@ -67,7 +77,7 @@ public class ExceptionInterceptorMethodTest { when(myInterceptor.incomingRequestPostProcessed(any(RequestDetails.class), any(HttpServletRequest.class), any(HttpServletResponse.class))).thenReturn(true); when(myInterceptor.handleException(any(RequestDetails.class), any(BaseServerResponseException.class), any(HttpServletRequest.class), any(HttpServletResponse.class))).thenReturn(true); - HttpGet httpGet = new HttpGet("http://localhost:" + ourPort + "/Patient?_query=throwUnprocessableEntityException"); + HttpGet httpGet = new HttpGet(ourServer.getBaseUrl() + "/Patient?_query=throwUnprocessableEntityException"); try (CloseableHttpResponse status = ourClient.execute(httpGet)) { ourLog.info(IOUtils.toString(status.getEntity().getContent(), Charsets.UTF_8)); assertEquals(422, status.getStatusLine().getStatusCode()); @@ -94,7 +104,7 @@ public class ExceptionInterceptorMethodTest { return false; }); - HttpGet httpGet = new HttpGet("http://localhost:" + ourPort + "/Patient?_query=throwUnprocessableEntityException"); + HttpGet httpGet = new HttpGet(ourServer.getBaseUrl() + "/Patient?_query=throwUnprocessableEntityException"); try (CloseableHttpResponse status = ourClient.execute(httpGet)) { String responseContent = IOUtils.toString(status.getEntity().getContent(), Charsets.UTF_8); ourLog.info(responseContent); @@ -106,33 +116,12 @@ public class ExceptionInterceptorMethodTest { @AfterAll public static void afterClassClearContext() throws Exception { - JettyUtil.closeServer(ourServer); TestUtil.randomizeLocaleAndTimezone(); } - @BeforeAll - public static void beforeClass() throws Exception { - ourServer = new Server(0); + - DummyPatientResourceProvider patientProvider = new DummyPatientResourceProvider(); - - ServletHandler proxyHandler = new ServletHandler(); - servlet = new RestfulServer(ourCtx); - servlet.setResourceProviders(patientProvider); - ServletHolder servletHolder = new ServletHolder(servlet); - proxyHandler.addServletWithMapping(servletHolder, "/*"); - ourServer.setHandler(proxyHandler); - JettyUtil.startServer(ourServer); - ourPort = JettyUtil.getPortForStartedServer(ourServer); - - PoolingHttpClientConnectionManager connectionManager = new PoolingHttpClientConnectionManager(5000, TimeUnit.MILLISECONDS); - HttpClientBuilder builder = HttpClientBuilder.create(); - builder.setConnectionManager(connectionManager); - ourClient = builder.build(); - - } - - public static class DummyPatientResourceProvider implements IResourceProvider { + private static class DummyPatientResourceProvider implements IResourceProvider { @Override public Class getResourceType() { diff --git a/hapi-fhir-structures-r4/src/test/java/ca/uhn/fhir/rest/server/interceptor/InjectionAttackTest.java b/hapi-fhir-structures-r4/src/test/java/ca/uhn/fhir/rest/server/interceptor/InjectionAttackTest.java index 220ec958e59..73ccf4884bc 100644 --- a/hapi-fhir-structures-r4/src/test/java/ca/uhn/fhir/rest/server/interceptor/InjectionAttackTest.java +++ b/hapi-fhir-structures-r4/src/test/java/ca/uhn/fhir/rest/server/interceptor/InjectionAttackTest.java @@ -6,31 +6,26 @@ import ca.uhn.fhir.rest.annotation.OptionalParam; import ca.uhn.fhir.rest.annotation.Read; import ca.uhn.fhir.rest.annotation.Search; import ca.uhn.fhir.rest.api.Constants; +import ca.uhn.fhir.rest.api.EncodingEnum; import ca.uhn.fhir.rest.param.TokenParam; +import ca.uhn.fhir.rest.server.FifoMemoryPagingProvider; import ca.uhn.fhir.rest.server.IResourceProvider; -import ca.uhn.fhir.rest.server.RestfulServer; -import ca.uhn.fhir.test.utilities.JettyUtil; +import ca.uhn.fhir.test.utilities.HttpClientExtension; +import ca.uhn.fhir.test.utilities.server.RestfulServerExtension; import ca.uhn.fhir.util.TestUtil; import ca.uhn.fhir.util.UrlUtil; import com.google.common.base.Charsets; import org.apache.commons.io.IOUtils; import org.apache.http.client.methods.CloseableHttpResponse; import org.apache.http.client.methods.HttpGet; -import org.apache.http.impl.client.CloseableHttpClient; -import org.apache.http.impl.client.HttpClientBuilder; -import org.apache.http.impl.conn.PoolingHttpClientConnectionManager; -import org.eclipse.jetty.server.Server; -import org.eclipse.jetty.servlet.ServletHandler; -import org.eclipse.jetty.servlet.ServletHolder; import org.hl7.fhir.r4.model.IdType; import org.hl7.fhir.r4.model.Patient; import org.junit.jupiter.api.AfterAll; -import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.RegisterExtension; import java.util.ArrayList; import java.util.List; -import java.util.concurrent.TimeUnit; import static org.hamcrest.MatcherAssert.assertThat; import static org.hamcrest.Matchers.containsString; @@ -39,17 +34,21 @@ import static org.junit.jupiter.api.Assertions.assertEquals; public class InjectionAttackTest { private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(InjectionAttackTest.class); - private static CloseableHttpClient ourClient; - private static FhirContext ourCtx = FhirContext.forR4(); - private static int ourPort; - private static Server ourServer; - private static RestfulServer ourServlet; + private static final FhirContext ourCtx = FhirContext.forR4Cached(); + + @RegisterExtension + public RestfulServerExtension ourServer = new RestfulServerExtension(ourCtx) + .registerProvider(new DummyPatientResourceProvider()) + .withPagingProvider(new FifoMemoryPagingProvider(100)) + .registerInterceptor(new ResponseHighlighterInterceptor()) + .setDefaultResponseEncoding(EncodingEnum.JSON); + + @RegisterExtension + private HttpClientExtension ourClient = new HttpClientExtension(); @Test public void testPreventHtmlInjectionViaInvalidContentType() throws Exception { - String requestUrl = "http://localhost:" + - ourPort + - "/Patient/123"; + String requestUrl = ourServer.getBaseUrl() + "/Patient/123"; // XML HTML HttpGet httpGet = new HttpGet(requestUrl); @@ -65,8 +64,7 @@ public class InjectionAttackTest { @Test public void testPreventHtmlInjectionViaInvalidParameterName() throws Exception { - String requestUrl = "http://localhost:" + - ourPort + + String requestUrl = ourServer.getBaseUrl() + "/Patient?a" + UrlUtil.escapeUrlParam("